@oxy.so/services 12.12.0 → 12.14.0
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/lib/commonjs/ui/boot/runProviderColdBoot.js +188 -88
- package/lib/commonjs/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/commonjs/ui/components/OxyProvider.js +2 -1
- package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
- package/lib/commonjs/ui/context/OxyContext.js +116 -3
- package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
- package/lib/commonjs/ui/hooks/queries/useSupervision.js +45 -9
- package/lib/commonjs/ui/hooks/queries/useSupervision.js.map +1 -1
- package/lib/commonjs/ui/session/cachedBoot.js +95 -0
- package/lib/commonjs/ui/session/cachedBoot.js.map +1 -0
- package/lib/module/ui/boot/runProviderColdBoot.js +188 -88
- package/lib/module/ui/boot/runProviderColdBoot.js.map +1 -1
- package/lib/module/ui/components/OxyProvider.js +2 -1
- package/lib/module/ui/components/OxyProvider.js.map +1 -1
- package/lib/module/ui/context/OxyContext.js +117 -4
- package/lib/module/ui/context/OxyContext.js.map +1 -1
- package/lib/module/ui/hooks/queries/useSupervision.js +45 -10
- package/lib/module/ui/hooks/queries/useSupervision.js.map +1 -1
- package/lib/module/ui/session/cachedBoot.js +92 -0
- package/lib/module/ui/session/cachedBoot.js.map +1 -0
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts +24 -0
- package/lib/typescript/commonjs/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useSupervision.d.ts +22 -1
- package/lib/typescript/commonjs/ui/hooks/queries/useSupervision.d.ts.map +1 -1
- package/lib/typescript/commonjs/ui/session/cachedBoot.d.ts +48 -0
- package/lib/typescript/commonjs/ui/session/cachedBoot.d.ts.map +1 -0
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts +24 -0
- package/lib/typescript/module/ui/boot/runProviderColdBoot.d.ts.map +1 -1
- package/lib/typescript/module/ui/components/OxyProvider.d.ts.map +1 -1
- package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
- package/lib/typescript/module/ui/hooks/queries/useSupervision.d.ts +22 -1
- package/lib/typescript/module/ui/hooks/queries/useSupervision.d.ts.map +1 -1
- package/lib/typescript/module/ui/session/cachedBoot.d.ts +48 -0
- package/lib/typescript/module/ui/session/cachedBoot.d.ts.map +1 -0
- package/package.json +7 -8
- package/src/ui/boot/__tests__/cachedColdBoot.test.ts +187 -0
- package/src/ui/boot/runProviderColdBoot.ts +229 -95
- package/src/ui/components/OxyProvider.tsx +2 -0
- package/src/ui/context/OxyContext.tsx +146 -3
- package/src/ui/hooks/queries/__tests__/useSupervision.test.tsx +73 -0
- package/src/ui/hooks/queries/useSupervision.ts +43 -9
- package/src/ui/session/__tests__/cachedBoot.test.ts +32 -0
- package/src/ui/session/cachedBoot.ts +139 -0
|
@@ -45,10 +45,34 @@ export interface RunProviderColdBootOptions {
|
|
|
45
45
|
}) => Promise<void>;
|
|
46
46
|
markAuthResolved: () => void;
|
|
47
47
|
setTokenReady: (ready: boolean) => void;
|
|
48
|
+
/**
|
|
49
|
+
* Show the session saved on this device before any network: the account from
|
|
50
|
+
* its cached profile, auth resolved. Resolves `true` when it did, `false` when
|
|
51
|
+
* the device holds nothing a first paint can use (then the boot runs as a
|
|
52
|
+
* first boot). Never consulted in `sessionMode: 'identity'` or with an OAuth
|
|
53
|
+
* return on the URL.
|
|
54
|
+
*/
|
|
55
|
+
paintCachedSession?: () => Promise<boolean>;
|
|
56
|
+
/**
|
|
57
|
+
* The server ended the painted session: the registry now classifies the app
|
|
58
|
+
* as OAuth-only, or the mint answered this holder `invalid_device_secret` /
|
|
59
|
+
* `no_active_session` and no recovery lane restored one. Sign out now.
|
|
60
|
+
*/
|
|
61
|
+
onCachedSessionRejected?: () => Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* The painted session could not be checked yet (offline, the registry or the
|
|
64
|
+
* mint unreachable). It stays on screen; call `retry` later to ask again.
|
|
65
|
+
*/
|
|
66
|
+
onCachedSessionUnconfirmed?: (retry: () => Promise<void>) => void;
|
|
48
67
|
}
|
|
49
68
|
/**
|
|
50
69
|
* Device-first cold boot for `@oxy.so/services` providers.
|
|
51
70
|
*
|
|
71
|
+
* A returning session saved on this device (account mode, the app last
|
|
72
|
+
* classified `device`, its profile cached) paints first, with no network, and
|
|
73
|
+
* is then checked in the background — see `paintCachedSession`. Otherwise, a
|
|
74
|
+
* first boot:
|
|
75
|
+
*
|
|
52
76
|
* Ordered pipeline:
|
|
53
77
|
* 1. Complete an OAuth authorization-code return already on the URL (web)
|
|
54
78
|
* 2. `runSessionColdBoot` — device-secret mint (+ the native Commons-proof lane,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runProviderColdBoot.d.ts","sourceRoot":"","sources":["../../../../../src/ui/boot/runProviderColdBoot.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAM1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA4B,CAAC;AAwB9D,iFAAiF;AACjF,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAEhD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,6BAA6B,QAAS,CAAC;AA0CpD,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,aAAa,EAAE,aAAa,CAAC;IAC7B,8EAA8E;IAC9E,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAC;IACvD,0EAA0E;IAC1E,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,0BAA0B,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,aAAa,EAAE,CACb,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,KAC5C,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"runProviderColdBoot.d.ts","sourceRoot":"","sources":["../../../../../src/ui/boot/runProviderColdBoot.ts"],"names":[],"mappings":"AACA,OAAO,EAAwB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AACtE,OAAO,EAEL,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,WAAW,EACjB,MAAM,sBAAsB,CAAC;AAC9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAM1D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,+BAA4B,CAAC;AAwB9D,iFAAiF;AACjF,eAAO,MAAM,2BAA2B,OAAO,CAAC;AAEhD;;;;;;;;;;GAUG;AACH,eAAO,MAAM,6BAA6B,QAAS,CAAC;AA0CpD,MAAM,WAAW,0BAA0B;IACzC,WAAW,EAAE,WAAW,CAAC;IACzB,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;;OAMG;IACH,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,kEAAkE;IAClE,QAAQ,CAAC,EAAE,eAAe,CAAC;IAC3B,aAAa,EAAE,aAAa,CAAC;IAC7B,8EAA8E;IAC9E,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,QAAQ,GAAG,OAAO,CAAC,CAAC;IACvD,0EAA0E;IAC1E,YAAY,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,GAAG,OAAO,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClE,gBAAgB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,eAAe,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,0BAA0B,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAChD,aAAa,EAAE,CACb,KAAK,EAAE,WAAW,EAClB,OAAO,EAAE;QAAE,QAAQ,EAAE,OAAO,CAAC;QAAC,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,KAC5C,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB,gBAAgB,EAAE,MAAM,IAAI,CAAC;IAC7B,aAAa,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACxC;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,MAAM,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5C;;;;OAIG;IACH,uBAAuB,CAAC,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9C;;;OAGG;IACH,0BAA0B,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,KAAK,IAAI,CAAC;CACnE;AA8LD;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,wBAAsB,mBAAmB,CAAC,IAAI,EAAE,0BAA0B,GAAG,OAAO,CAAC,IAAI,CAAC,CA0FzF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OxyProvider.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/OxyProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAwC,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAGtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"OxyProvider.d.ts","sourceRoot":"","sources":["../../../../../src/ui/components/OxyProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAwC,KAAK,EAAE,EAAE,MAAM,OAAO,CAAC;AAGtE,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAqB,CAAC;AAuC5D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,QAAA,MAAM,WAAW,EAAE,EAAE,CAAC,gBAAgB,CA0NrC,CAAC;AAEF,eAAe,WAAW,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"OxyContext.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/OxyContext.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"OxyContext.d.ts","sourceRoot":"","sources":["../../../../../src/ui/context/OxyContext.tsx"],"names":[],"mappings":"AAUA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAoG/B,OAAO,KAAK,EAAmB,uBAAuB,EAAe,MAAM,sBAAmB,CAAC;AAQ/F,YAAY,EAAE,YAAY,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,sBAAmB,CAAC;AAOhG,OAAO,EACL,iBAAiB,EACjB,MAAM,EACN,cAAc,EACd,uBAAuB,GACxB,MAAM,wBAAqB,CAAC;AAI7B;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,EAAE,KAAK,CAAC,EAAE,CAAC,uBAAuB,CAutDhE,CAAC"}
|
|
@@ -2,7 +2,21 @@ export declare const supervisionQueryKeys: {
|
|
|
2
2
|
all: readonly ["oxy", "safety", "supervision"];
|
|
3
3
|
account: (id: string, sessionId: string) => readonly ["oxy", "safety", "supervision", string, string];
|
|
4
4
|
};
|
|
5
|
-
/**
|
|
5
|
+
/**
|
|
6
|
+
* The signed-in personal account's supervision (parental-control) state.
|
|
7
|
+
*
|
|
8
|
+
* Policy: the SDK keeps the session's supervision state always current — it is
|
|
9
|
+
* fetched when the session is established, refetched when the app returns to
|
|
10
|
+
* the foreground, and refetched when the session socket signals
|
|
11
|
+
* `supervision_changed` (`SupervisionStateObserver`, mounted by `OxyProvider`).
|
|
12
|
+
* Screens read it from the cache without waiting for the network: no observer
|
|
13
|
+
* forces its own refetch on mount. `data` is undefined only while the session's
|
|
14
|
+
* first fetch is pending, when the query is disabled (signed out, or not a
|
|
15
|
+
* personal account) and after a failed fetch.
|
|
16
|
+
*
|
|
17
|
+
* Advisory UI state only. The server keeps enforcing supervision on every
|
|
18
|
+
* content request; never treat this value as authorization.
|
|
19
|
+
*/
|
|
6
20
|
export declare function useSupervision(): {
|
|
7
21
|
data: {
|
|
8
22
|
self: {
|
|
@@ -472,4 +486,11 @@ export declare function useSupervision(): {
|
|
|
472
486
|
}, Error>>;
|
|
473
487
|
fetchStatus: import("@tanstack/react-query").FetchStatus;
|
|
474
488
|
};
|
|
489
|
+
/**
|
|
490
|
+
* Keeps the session's supervision state current for every `useSupervision()`
|
|
491
|
+
* reader: its observer fetches once the session is established and on each
|
|
492
|
+
* return to the foreground, and the `supervision_changed` socket signal
|
|
493
|
+
* refetches it. Rendered once by `OxyProvider`.
|
|
494
|
+
*/
|
|
495
|
+
export declare function SupervisionStateObserver(): null;
|
|
475
496
|
//# sourceMappingURL=useSupervision.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useSupervision.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/hooks/queries/useSupervision.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"useSupervision.d.ts","sourceRoot":"","sources":["../../../../../../src/ui/hooks/queries/useSupervision.ts"],"names":[],"mappings":"AAKA,eAAO,MAAM,oBAAoB;;kBAEjB,MAAM,aAAa,MAAM;CAExC,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiB7B;AAED;;;;;GAKG;AACH,wBAAgB,wBAAwB,SAOvC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a returning session needs for its first paint, kept on this device.
|
|
3
|
+
*
|
|
4
|
+
* A cold boot used to wait on two round trips before it showed anything: the
|
|
5
|
+
* registry's classification of this app and the profile of the signed-in
|
|
6
|
+
* account. With a session saved on the device, the app now paints at once from
|
|
7
|
+
* what the last run left here, and the boot verifies both in the background
|
|
8
|
+
* (`runProviderColdBoot`). This module is that cache:
|
|
9
|
+
*
|
|
10
|
+
* - the registry's last verdict on this app (`device` or `oauth`), tied to the
|
|
11
|
+
* `clientId` it was given for;
|
|
12
|
+
* - the last account profile the runtime held, tied to that account's id.
|
|
13
|
+
*
|
|
14
|
+
* Both live in the provider's ordinary storage (AsyncStorage on native,
|
|
15
|
+
* `localStorage` on web), next to the per-account query cache, which already
|
|
16
|
+
* holds the same profile under the SDK's `users` queries. Neither is a
|
|
17
|
+
* credential: the device secret and the access token stay in the auth store.
|
|
18
|
+
*
|
|
19
|
+
* A cached session is offered only when it matches the auth store: the profile
|
|
20
|
+
* must belong to the account the persisted session is for, so a switch or a
|
|
21
|
+
* sign-out on a previous run can never paint somebody else.
|
|
22
|
+
*/
|
|
23
|
+
import type { User } from '@oxy.so/core';
|
|
24
|
+
import { type AuthStateStore } from '@oxy.so/core/session';
|
|
25
|
+
import type { StorageInterface } from '../utils/storageHelpers.js';
|
|
26
|
+
export type SessionLane = 'device' | 'oauth';
|
|
27
|
+
/** A persisted session the app can show before the network answers. */
|
|
28
|
+
export interface CachedSession {
|
|
29
|
+
sessionId: string;
|
|
30
|
+
userId: string;
|
|
31
|
+
deviceId: string;
|
|
32
|
+
/** The account profile the last run held for {@link userId}. */
|
|
33
|
+
user: User;
|
|
34
|
+
/** The persisted access token, when it is still valid past the refresh lead. */
|
|
35
|
+
warmToken: string | null;
|
|
36
|
+
}
|
|
37
|
+
export interface CachedBootStore {
|
|
38
|
+
saveLane(clientId: string, lane: SessionLane): Promise<void>;
|
|
39
|
+
saveAccount(user: User): Promise<void>;
|
|
40
|
+
clearAccount(): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* The cached session for this `clientId`, or `null` when anything a first
|
|
43
|
+
* paint needs is missing or does not match the auth store.
|
|
44
|
+
*/
|
|
45
|
+
readSession(clientId: string, authStore: AuthStateStore): Promise<CachedSession | null>;
|
|
46
|
+
}
|
|
47
|
+
export declare function createCachedBootStore(storage: StorageInterface, prefix: string, now?: () => number): CachedBootStore;
|
|
48
|
+
//# sourceMappingURL=cachedBoot.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cachedBoot.d.ts","sourceRoot":"","sources":["../../../../../src/ui/session/cachedBoot.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AACH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAyB,KAAK,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAClF,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAAyB,CAAC;AAEhE,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE7C,uEAAuE;AACvE,MAAM,WAAW,aAAa;IAC5B,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,IAAI,EAAE,IAAI,CAAC;IACX,gFAAgF;IAChF,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;CAC1B;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC7D,WAAW,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC,YAAY,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAC9B;;;OAGG;IACH,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAAC;CACzF;AAuBD,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,gBAAgB,EACzB,MAAM,EAAE,MAAM,EACd,GAAG,GAAE,MAAM,MAAiB,GAC3B,eAAe,CA+DjB"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oxy.so/services",
|
|
3
|
-
"version": "12.
|
|
3
|
+
"version": "12.14.0",
|
|
4
4
|
"description": "OxyHQ Expo/React Native SDK — UI components, screens, and native features",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -147,11 +147,10 @@
|
|
|
147
147
|
"copy-assets": "copyfiles -u 1 \"src/assets/**/*\" lib/commonjs/assets && copyfiles -u 1 \"src/assets/**/*\" lib/module/assets",
|
|
148
148
|
"copy-dts": "copyfiles -u 1 \"src/**/*.d.ts\" lib/typescript",
|
|
149
149
|
"delete-debug-view": "rm -rf ./lib/commonjs/components/bottomSheetDebugView ./lib/module/components/bottomSheetDebugView ./lib/typescript/components/bottomSheetDebugView || true",
|
|
150
|
-
"delete-dts.js": "find ./lib/commonjs -name '*.d.js*' -delete && find ./lib/module -name '*.d.js*' -delete"
|
|
151
|
-
"prepublishOnly": "node ../../scripts/assert-bun-publish.mjs && bun run typescript && bun run test && bun run build"
|
|
150
|
+
"delete-dts.js": "find ./lib/commonjs -name '*.d.js*' -delete && find ./lib/module -name '*.d.js*' -delete"
|
|
152
151
|
},
|
|
153
152
|
"dependencies": {
|
|
154
|
-
"@oxy.so/contracts": "^5.
|
|
153
|
+
"@oxy.so/contracts": "^5.8.0",
|
|
155
154
|
"color": "^5.0.3",
|
|
156
155
|
"@peable.to/sdk": "0.4.1"
|
|
157
156
|
},
|
|
@@ -160,7 +159,7 @@
|
|
|
160
159
|
"@commitlint/config-conventional": "^21.2.0",
|
|
161
160
|
"@expo/app-integrity": "~58.0.2",
|
|
162
161
|
"@expo/vector-icons": "^15.0.3",
|
|
163
|
-
"@oxy.so/core": "6.
|
|
162
|
+
"@oxy.so/core": "6.17.0",
|
|
164
163
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
165
164
|
"@react-native-community/netinfo": "^12.0.1",
|
|
166
165
|
"@react-native/babel-preset": "^0.88.0",
|
|
@@ -175,7 +174,7 @@
|
|
|
175
174
|
"@types/jest": "^30.0.0",
|
|
176
175
|
"@types/node": "^20.19.43",
|
|
177
176
|
"@types/react": "*",
|
|
178
|
-
"@oxy.so/bloom": "
|
|
177
|
+
"@oxy.so/bloom": "15.18.0",
|
|
179
178
|
"copyfiles": "^2.4.1",
|
|
180
179
|
"expo-clipboard": "~58.0.3",
|
|
181
180
|
"expo-constants": "~58.0.12",
|
|
@@ -210,12 +209,12 @@
|
|
|
210
209
|
"expo-video": "~58.0.11"
|
|
211
210
|
},
|
|
212
211
|
"//@oxy.so/bloom": "Bloom 11.1.0 adds the shared grouped sections, usage card, subscription plans, frame appearance and overlay navigation APIs used by this SDK. First-party Bloom peers remain open above this tested minimum.",
|
|
213
|
-
"//@oxy.so/core": "Core 6.13.0 supplies the `accounts.settings.*` strings (picture, categories) the account settings speak in every language; 6.12.0 supplies `fileManagement.contentLost` and the `lost` file status a lost file is listed with; 6.11.0 supplies the `follow.errors.*` strings the follow error codes are worded with; 6.4.0 the `follow.*` strings the follow buttons read for every verb and language; 6.1.0 the `oxy.auth.email.{startChange,change,remove}` and `oxy.auth.password.remove` the sign-in & security panels call. Keep one Core instance per app; raise this floor with the root catalogue when new runtime APIs are required.",
|
|
212
|
+
"//@oxy.so/core": "Core 6.16.0 supplies `requireServerVerdict` and `onHolderRejected`, with which the cold boot checks a session it painted from the cache; 6.13.0 supplies the `accounts.settings.*` strings (picture, categories) the account settings speak in every language; 6.12.0 supplies `fileManagement.contentLost` and the `lost` file status a lost file is listed with; 6.11.0 supplies the `follow.errors.*` strings the follow error codes are worded with; 6.4.0 the `follow.*` strings the follow buttons read for every verb and language; 6.1.0 the `oxy.auth.email.{startChange,change,remove}` and `oxy.auth.password.remove` the sign-in & security panels call. Keep one Core instance per app; raise this floor with the root catalogue when new runtime APIs are required.",
|
|
214
213
|
"peerDependencies": {
|
|
215
214
|
"@expo/app-integrity": ">=56.0.0 <59.0.0",
|
|
216
215
|
"@expo/vector-icons": "^15.0.3",
|
|
217
216
|
"@oxy.so/bloom": ">=11.1.0",
|
|
218
|
-
"@oxy.so/core": "^6.
|
|
217
|
+
"@oxy.so/core": "^6.16.0",
|
|
219
218
|
"@react-native-async-storage/async-storage": "^2.0.0",
|
|
220
219
|
"@react-native-community/netinfo": ">=11.4.1 <13.0.0",
|
|
221
220
|
"@tanstack/query-async-storage-persister": "^5.101",
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `runProviderColdBoot` with a session saved on the device: it paints from the
|
|
3
|
+
* cache before any network answers, then checks the classification and the
|
|
4
|
+
* session in the background. A first boot (nothing cached) is unchanged.
|
|
5
|
+
*/
|
|
6
|
+
import type { AuthStateStore, PersistedAuthState, SessionClient } from '@oxy.so/core/session';
|
|
7
|
+
|
|
8
|
+
jest.mock('@oxy.so/core/session', () => {
|
|
9
|
+
const actual = jest.requireActual('@oxy.so/core/session');
|
|
10
|
+
return { __esModule: true, ...actual, runSessionColdBoot: jest.fn() };
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
import { runSessionColdBoot } from '@oxy.so/core/session';
|
|
14
|
+
import { runProviderColdBoot, type RunProviderColdBootOptions } from '../runProviderColdBoot';
|
|
15
|
+
|
|
16
|
+
const mockRunSessionColdBoot = runSessionColdBoot as jest.Mock;
|
|
17
|
+
type BootOptions = Parameters<typeof runSessionColdBoot>[0];
|
|
18
|
+
|
|
19
|
+
const OFFICIAL = {
|
|
20
|
+
id: 'app',
|
|
21
|
+
name: 'App',
|
|
22
|
+
type: 'first_party',
|
|
23
|
+
isOfficial: true,
|
|
24
|
+
isInternal: false,
|
|
25
|
+
scopes: [],
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function credentialStore(): AuthStateStore {
|
|
29
|
+
const state: PersistedAuthState = {
|
|
30
|
+
sessionId: 's1',
|
|
31
|
+
userId: 'u1',
|
|
32
|
+
deviceId: 'd1',
|
|
33
|
+
deviceSecret: 'secret',
|
|
34
|
+
};
|
|
35
|
+
return {
|
|
36
|
+
load: jest.fn(async () => state),
|
|
37
|
+
save: jest.fn(async () => true),
|
|
38
|
+
clear: jest.fn(async () => undefined),
|
|
39
|
+
} as unknown as AuthStateStore;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function makeOptions(paints: boolean): RunProviderColdBootOptions & {
|
|
43
|
+
getPublic: jest.Mock;
|
|
44
|
+
release: (app: unknown) => void;
|
|
45
|
+
fail: (error: Error) => void;
|
|
46
|
+
} {
|
|
47
|
+
let release: (app: unknown) => void = () => undefined;
|
|
48
|
+
let fail: (error: Error) => void = () => undefined;
|
|
49
|
+
const getPublic = jest.fn(
|
|
50
|
+
() =>
|
|
51
|
+
new Promise((resolve, reject) => {
|
|
52
|
+
release = resolve;
|
|
53
|
+
fail = reject;
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
const markAuthResolved = jest.fn();
|
|
57
|
+
return {
|
|
58
|
+
oxyServices: { apps: { getPublic } } as unknown as RunProviderColdBootOptions['oxyServices'],
|
|
59
|
+
authStore: credentialStore(),
|
|
60
|
+
clientId: 'oxy_dk_test',
|
|
61
|
+
sessionClient: { start: jest.fn(async () => undefined) } as unknown as SessionClient,
|
|
62
|
+
syncDeviceCredentialToHost: jest.fn(async () => undefined),
|
|
63
|
+
commitSession: jest.fn(async () => undefined),
|
|
64
|
+
markAuthResolved,
|
|
65
|
+
setTokenReady: jest.fn(),
|
|
66
|
+
paintCachedSession: jest.fn(async () => {
|
|
67
|
+
if (paints) markAuthResolved();
|
|
68
|
+
return paints;
|
|
69
|
+
}),
|
|
70
|
+
onCachedSessionRejected: jest.fn(async () => undefined),
|
|
71
|
+
onCachedSessionUnconfirmed: jest.fn(),
|
|
72
|
+
getPublic,
|
|
73
|
+
release: (app) => release(app),
|
|
74
|
+
fail: (error) => fail(error),
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const tick = () => new Promise((resolve) => setTimeout(resolve, 0));
|
|
79
|
+
|
|
80
|
+
beforeEach(() => {
|
|
81
|
+
mockRunSessionColdBoot.mockReset();
|
|
82
|
+
window.history.replaceState(null, '', '/');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('paints a saved session before the registry or the mint answers, then mints for a verdict', async () => {
|
|
86
|
+
mockRunSessionColdBoot.mockImplementation(async (opts: BootOptions) => {
|
|
87
|
+
const session = { sessionId: 's1', userId: 'u1', accessToken: 'minted', via: 'mint' };
|
|
88
|
+
await opts.onSession?.(session);
|
|
89
|
+
return { kind: 'session', via: session.via, session };
|
|
90
|
+
});
|
|
91
|
+
const options = makeOptions(true);
|
|
92
|
+
|
|
93
|
+
const boot = runProviderColdBoot(options);
|
|
94
|
+
await tick();
|
|
95
|
+
|
|
96
|
+
expect(options.markAuthResolved).toHaveBeenCalled();
|
|
97
|
+
expect(mockRunSessionColdBoot).not.toHaveBeenCalled();
|
|
98
|
+
|
|
99
|
+
options.release(OFFICIAL);
|
|
100
|
+
await boot;
|
|
101
|
+
|
|
102
|
+
expect(mockRunSessionColdBoot).toHaveBeenCalledWith(
|
|
103
|
+
expect.objectContaining({ requireServerVerdict: true }),
|
|
104
|
+
);
|
|
105
|
+
expect(options.commitSession).toHaveBeenCalledWith(
|
|
106
|
+
{ sessionId: 's1', userId: 'u1', accessToken: 'minted', deviceState: undefined },
|
|
107
|
+
{ activate: false },
|
|
108
|
+
);
|
|
109
|
+
expect(options.onCachedSessionRejected).not.toHaveBeenCalled();
|
|
110
|
+
expect(options.onCachedSessionUnconfirmed).not.toHaveBeenCalled();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it.each([
|
|
114
|
+
['the server ends the holder', OFFICIAL, true],
|
|
115
|
+
[
|
|
116
|
+
'the registry now classifies the app OAuth-only',
|
|
117
|
+
{ ...OFFICIAL, type: 'third_party', isOfficial: false },
|
|
118
|
+
false,
|
|
119
|
+
],
|
|
120
|
+
])('signs a painted session out when %s', async (_case, app, mints) => {
|
|
121
|
+
mockRunSessionColdBoot.mockImplementation(async (opts: BootOptions) => {
|
|
122
|
+
opts.onHolderRejected?.();
|
|
123
|
+
await opts.onSignedOut?.('no_session');
|
|
124
|
+
return { kind: 'unauthenticated' };
|
|
125
|
+
});
|
|
126
|
+
const options = makeOptions(true);
|
|
127
|
+
|
|
128
|
+
const boot = runProviderColdBoot(options);
|
|
129
|
+
await tick();
|
|
130
|
+
options.release(app);
|
|
131
|
+
await boot;
|
|
132
|
+
|
|
133
|
+
expect(mockRunSessionColdBoot).toHaveBeenCalledTimes(mints ? 1 : 0);
|
|
134
|
+
expect(options.onCachedSessionRejected).toHaveBeenCalledTimes(1);
|
|
135
|
+
expect(options.onCachedSessionUnconfirmed).not.toHaveBeenCalled();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('keeps a painted session the server could not be asked about, and asks again', async () => {
|
|
139
|
+
mockRunSessionColdBoot.mockImplementationOnce(async (opts: BootOptions) => {
|
|
140
|
+
await opts.onSignedOut?.('no_session');
|
|
141
|
+
return { kind: 'unauthenticated' };
|
|
142
|
+
});
|
|
143
|
+
const options = makeOptions(true);
|
|
144
|
+
|
|
145
|
+
const boot = runProviderColdBoot(options);
|
|
146
|
+
await tick();
|
|
147
|
+
options.fail(new Error('offline'));
|
|
148
|
+
await boot;
|
|
149
|
+
|
|
150
|
+
expect(options.onCachedSessionRejected).not.toHaveBeenCalled();
|
|
151
|
+
const retry = (options.onCachedSessionUnconfirmed as jest.Mock).mock.calls[0][0];
|
|
152
|
+
mockRunSessionColdBoot.mockImplementationOnce(async (opts: BootOptions) => {
|
|
153
|
+
const session = { sessionId: 's1', userId: 'u1', accessToken: 'minted', via: 'mint' };
|
|
154
|
+
await opts.onSession?.(session);
|
|
155
|
+
return { kind: 'session', via: session.via, session };
|
|
156
|
+
});
|
|
157
|
+
const retried = retry();
|
|
158
|
+
await tick();
|
|
159
|
+
options.release(OFFICIAL);
|
|
160
|
+
await retried;
|
|
161
|
+
|
|
162
|
+
expect(options.commitSession).toHaveBeenCalledTimes(1);
|
|
163
|
+
expect(options.onCachedSessionUnconfirmed).toHaveBeenCalledTimes(1);
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
it('a first boot with nothing cached still waits for the verdict and the handoff', async () => {
|
|
167
|
+
mockRunSessionColdBoot.mockImplementation(async (opts: BootOptions) => {
|
|
168
|
+
await opts.onSignedOut?.('no_session');
|
|
169
|
+
return { kind: 'unauthenticated' };
|
|
170
|
+
});
|
|
171
|
+
const options = makeOptions(false);
|
|
172
|
+
|
|
173
|
+
const boot = runProviderColdBoot(options);
|
|
174
|
+
await tick();
|
|
175
|
+
|
|
176
|
+
expect(options.markAuthResolved).not.toHaveBeenCalled();
|
|
177
|
+
expect(mockRunSessionColdBoot).not.toHaveBeenCalled();
|
|
178
|
+
|
|
179
|
+
options.release(OFFICIAL);
|
|
180
|
+
await boot;
|
|
181
|
+
|
|
182
|
+
expect(mockRunSessionColdBoot).toHaveBeenCalledWith(
|
|
183
|
+
expect.objectContaining({ requireServerVerdict: false }),
|
|
184
|
+
);
|
|
185
|
+
expect(options.markAuthResolved).toHaveBeenCalled();
|
|
186
|
+
expect(options.onCachedSessionRejected).not.toHaveBeenCalled();
|
|
187
|
+
});
|