@oxyhq/services 9.0.0 → 10.0.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.
Files changed (63) hide show
  1. package/lib/commonjs/ui/components/OxyProvider.js +2 -2
  2. package/lib/commonjs/ui/components/OxyProvider.js.map +1 -1
  3. package/lib/commonjs/ui/components/SignInModal.js +14 -3
  4. package/lib/commonjs/ui/components/SignInModal.js.map +1 -1
  5. package/lib/commonjs/ui/context/OxyContext.js +30 -18
  6. package/lib/commonjs/ui/context/OxyContext.js.map +1 -1
  7. package/lib/commonjs/ui/hooks/useWebSSO.js +7 -8
  8. package/lib/commonjs/ui/hooks/useWebSSO.js.map +1 -1
  9. package/lib/commonjs/ui/screens/OxyAuthScreen.js +14 -3
  10. package/lib/commonjs/ui/screens/OxyAuthScreen.js.map +1 -1
  11. package/lib/commonjs/utils/silentGuardKey.js +54 -0
  12. package/lib/commonjs/utils/silentGuardKey.js.map +1 -0
  13. package/lib/module/ui/components/OxyProvider.js +2 -2
  14. package/lib/module/ui/components/OxyProvider.js.map +1 -1
  15. package/lib/module/ui/components/SignInModal.js +14 -3
  16. package/lib/module/ui/components/SignInModal.js.map +1 -1
  17. package/lib/module/ui/context/OxyContext.js +30 -18
  18. package/lib/module/ui/context/OxyContext.js.map +1 -1
  19. package/lib/module/ui/hooks/useWebSSO.js +7 -8
  20. package/lib/module/ui/hooks/useWebSSO.js.map +1 -1
  21. package/lib/module/ui/screens/OxyAuthScreen.js +14 -3
  22. package/lib/module/ui/screens/OxyAuthScreen.js.map +1 -1
  23. package/lib/module/utils/silentGuardKey.js +49 -0
  24. package/lib/module/utils/silentGuardKey.js.map +1 -0
  25. package/lib/typescript/commonjs/ui/components/SignInModal.d.ts.map +1 -1
  26. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts +12 -8
  27. package/lib/typescript/commonjs/ui/context/OxyContext.d.ts.map +1 -1
  28. package/lib/typescript/commonjs/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  29. package/lib/typescript/commonjs/ui/hooks/useWebSSO.d.ts.map +1 -1
  30. package/lib/typescript/commonjs/ui/screens/OxyAuthScreen.d.ts.map +1 -1
  31. package/lib/typescript/commonjs/ui/types/navigation.d.ts +8 -6
  32. package/lib/typescript/commonjs/ui/types/navigation.d.ts.map +1 -1
  33. package/lib/typescript/commonjs/utils/silentGuardKey.d.ts +31 -0
  34. package/lib/typescript/commonjs/utils/silentGuardKey.d.ts.map +1 -0
  35. package/lib/typescript/module/ui/components/SignInModal.d.ts.map +1 -1
  36. package/lib/typescript/module/ui/context/OxyContext.d.ts +12 -8
  37. package/lib/typescript/module/ui/context/OxyContext.d.ts.map +1 -1
  38. package/lib/typescript/module/ui/hooks/mutations/useAccountMutations.d.ts.map +1 -1
  39. package/lib/typescript/module/ui/hooks/useWebSSO.d.ts.map +1 -1
  40. package/lib/typescript/module/ui/screens/OxyAuthScreen.d.ts.map +1 -1
  41. package/lib/typescript/module/ui/types/navigation.d.ts +8 -6
  42. package/lib/typescript/module/ui/types/navigation.d.ts.map +1 -1
  43. package/lib/typescript/module/utils/silentGuardKey.d.ts +31 -0
  44. package/lib/typescript/module/utils/silentGuardKey.d.ts.map +1 -0
  45. package/package.json +2 -2
  46. package/src/ui/components/OxyProvider.tsx +2 -2
  47. package/src/ui/components/SignInModal.tsx +14 -3
  48. package/src/ui/context/OxyContext.tsx +42 -29
  49. package/src/ui/hooks/useWebSSO.ts +7 -8
  50. package/src/ui/screens/OxyAuthScreen.tsx +14 -3
  51. package/src/ui/types/navigation.ts +8 -6
  52. package/src/utils/__tests__/silentGuardKey.test.ts +82 -0
  53. package/src/utils/silentGuardKey.ts +46 -0
  54. package/lib/commonjs/ui/utils/appName.js +0 -62
  55. package/lib/commonjs/ui/utils/appName.js.map +0 -1
  56. package/lib/module/ui/utils/appName.js +0 -59
  57. package/lib/module/ui/utils/appName.js.map +0 -1
  58. package/lib/typescript/commonjs/ui/utils/appName.d.ts +0 -22
  59. package/lib/typescript/commonjs/ui/utils/appName.d.ts.map +0 -1
  60. package/lib/typescript/module/ui/utils/appName.d.ts +0 -22
  61. package/lib/typescript/module/ui/utils/appName.d.ts.map +0 -1
  62. package/src/ui/utils/__tests__/appName.test.ts +0 -52
  63. package/src/ui/utils/appName.ts +0 -62
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Shared, pure helpers for building the `origin|baseURL` signature used as the
3
+ * module-level run-once guard key for cold-boot silent-SSO probes
4
+ * (`silentColdBootKey` in `OxyContext`, `ssoSignature` in `useWebSSO`).
5
+ *
6
+ * NATIVE SAFETY (the bug this fixes): React Native aliases a global `window`
7
+ * (it points at the JS global object), so `typeof window !== 'undefined'` is
8
+ * `true` on native — but `window.location` is `undefined`. Reading
9
+ * `window.location.origin` after only a `typeof window` check therefore throws
10
+ * `TypeError: Cannot read property 'origin' of undefined` on native. Because
11
+ * the key is built UNCONDITIONALLY at the top of the cold-boot path (before its
12
+ * try/catch), that throw escaped session restore entirely and broke
13
+ * cross-session restore on native. Both prior copies of the guard had the same
14
+ * insufficient `typeof window` check and were prone to drift, so the read is
15
+ * consolidated here behind a guard that also verifies `window.location`.
16
+ */
17
+
18
+ /**
19
+ * Read `window.location.origin` safely on every platform.
20
+ *
21
+ * Returns the browser origin on web, and the sentinel `'no-origin'` anywhere
22
+ * `window.location` is absent (React Native, SSR/Node). Never throws.
23
+ */
24
+ export function safeWindowOrigin(): string {
25
+ if (typeof window !== 'undefined' && typeof window.location !== 'undefined') {
26
+ return window.location.origin;
27
+ }
28
+ return 'no-origin';
29
+ }
30
+
31
+ /**
32
+ * Build the stable `origin|baseURL` signature for the silent-SSO run-once
33
+ * guard. Two providers pointed at the same API from the same origin share one
34
+ * attempt. `getBaseURL` is invoked defensively (it may be absent or throw on a
35
+ * partially-initialised client); any failure degrades to an empty baseURL.
36
+ */
37
+ export function buildSilentGuardKey(getBaseURL?: () => string | undefined): string {
38
+ const origin = safeWindowOrigin();
39
+ let baseURL = '';
40
+ try {
41
+ baseURL = getBaseURL?.() ?? '';
42
+ } catch {
43
+ baseURL = '';
44
+ }
45
+ return `${origin}|${baseURL}`;
46
+ }
@@ -1,62 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.resolveAppDisplayName = resolveAppDisplayName;
7
- var _reactNative = require("react-native");
8
- /**
9
- * The `storageKeyPrefix` default applied by `OxyContextProvider`. When the
10
- * consumer never overrides it, the prefix carries no app-identity signal and
11
- * must NOT be used to derive a display name (it would surface "Oxy_session").
12
- */
13
- const DEFAULT_STORAGE_KEY_PREFIX = 'oxy_session';
14
-
15
- /**
16
- * Capitalize the first character of a non-empty string. Used to turn a lower
17
- * case `storageKeyPrefix` (e.g. `"mention"`) into a presentable label
18
- * (`"Mention"`). Pure; leaves the remainder untouched so multi-word or already
19
- * capitalized values are preserved.
20
- */
21
- function capitalizeFirst(value) {
22
- return value.charAt(0).toUpperCase() + value.slice(1);
23
- }
24
-
25
- /**
26
- * Resolve a human-readable application display name for the consent / sign-in
27
- * UI shown by the central Oxy auth experience (e.g. "Mention wants to access
28
- * your Oxy account"). This is sent as the `appId` field on
29
- * `POST /auth/session/create` and rendered verbatim by the auth consent page.
30
- *
31
- * Resolution order (first non-empty wins):
32
- * 1. An explicit `appName` declared by the consumer on `OxyProvider`.
33
- * 2. The capitalized `storageKeyPrefix` — but only when the consumer actually
34
- * overrode the default. Apps already pass a brand-shaped prefix
35
- * (`"mention"`, `"homiio"`, …) so this gives most apps a correct name with
36
- * zero extra config.
37
- * 3. On web only, a meaningful `document.title` (trimmed). This rescues
38
- * zero-config web apps that set a page title but no prefix.
39
- * 4. `Platform.OS` as the terminal fallback. On web this yields the historical
40
- * `"web"` value — now reached ONLY when an app supplies neither an explicit
41
- * name, a custom prefix, nor a document title.
42
- *
43
- * The result is never empty.
44
- */
45
- function resolveAppDisplayName(appName, storageKeyPrefix) {
46
- const explicit = appName?.trim();
47
- if (explicit) {
48
- return explicit;
49
- }
50
- const prefix = storageKeyPrefix?.trim();
51
- if (prefix && prefix !== DEFAULT_STORAGE_KEY_PREFIX) {
52
- return capitalizeFirst(prefix);
53
- }
54
- if (_reactNative.Platform.OS === 'web' && typeof document !== 'undefined') {
55
- const title = document.title?.trim();
56
- if (title) {
57
- return title;
58
- }
59
- }
60
- return _reactNative.Platform.OS;
61
- }
62
- //# sourceMappingURL=appName.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["_reactNative","require","DEFAULT_STORAGE_KEY_PREFIX","capitalizeFirst","value","charAt","toUpperCase","slice","resolveAppDisplayName","appName","storageKeyPrefix","explicit","trim","prefix","Platform","OS","document","title"],"sourceRoot":"../../../../src","sources":["ui/utils/appName.ts"],"mappings":";;;;;;AAAA,IAAAA,YAAA,GAAAC,OAAA;AAEA;AACA;AACA;AACA;AACA;AACA,MAAMC,0BAA0B,GAAG,aAAa;;AAEhD;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,eAAeA,CAACC,KAAa,EAAU;EAC9C,OAAOA,KAAK,CAACC,MAAM,CAAC,CAAC,CAAC,CAACC,WAAW,CAAC,CAAC,GAAGF,KAAK,CAACG,KAAK,CAAC,CAAC,CAAC;AACvD;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACO,SAASC,qBAAqBA,CACnCC,OAA2B,EAC3BC,gBAAoC,EAC5B;EACR,MAAMC,QAAQ,GAAGF,OAAO,EAAEG,IAAI,CAAC,CAAC;EAChC,IAAID,QAAQ,EAAE;IACZ,OAAOA,QAAQ;EACjB;EAEA,MAAME,MAAM,GAAGH,gBAAgB,EAAEE,IAAI,CAAC,CAAC;EACvC,IAAIC,MAAM,IAAIA,MAAM,KAAKX,0BAA0B,EAAE;IACnD,OAAOC,eAAe,CAACU,MAAM,CAAC;EAChC;EAEA,IAAIC,qBAAQ,CAACC,EAAE,KAAK,KAAK,IAAI,OAAOC,QAAQ,KAAK,WAAW,EAAE;IAC5D,MAAMC,KAAK,GAAGD,QAAQ,CAACC,KAAK,EAAEL,IAAI,CAAC,CAAC;IACpC,IAAIK,KAAK,EAAE;MACT,OAAOA,KAAK;IACd;EACF;EAEA,OAAOH,qBAAQ,CAACC,EAAE;AACpB","ignoreList":[]}
@@ -1,59 +0,0 @@
1
- "use strict";
2
-
3
- import { Platform } from 'react-native';
4
-
5
- /**
6
- * The `storageKeyPrefix` default applied by `OxyContextProvider`. When the
7
- * consumer never overrides it, the prefix carries no app-identity signal and
8
- * must NOT be used to derive a display name (it would surface "Oxy_session").
9
- */
10
- const DEFAULT_STORAGE_KEY_PREFIX = 'oxy_session';
11
-
12
- /**
13
- * Capitalize the first character of a non-empty string. Used to turn a lower
14
- * case `storageKeyPrefix` (e.g. `"mention"`) into a presentable label
15
- * (`"Mention"`). Pure; leaves the remainder untouched so multi-word or already
16
- * capitalized values are preserved.
17
- */
18
- function capitalizeFirst(value) {
19
- return value.charAt(0).toUpperCase() + value.slice(1);
20
- }
21
-
22
- /**
23
- * Resolve a human-readable application display name for the consent / sign-in
24
- * UI shown by the central Oxy auth experience (e.g. "Mention wants to access
25
- * your Oxy account"). This is sent as the `appId` field on
26
- * `POST /auth/session/create` and rendered verbatim by the auth consent page.
27
- *
28
- * Resolution order (first non-empty wins):
29
- * 1. An explicit `appName` declared by the consumer on `OxyProvider`.
30
- * 2. The capitalized `storageKeyPrefix` — but only when the consumer actually
31
- * overrode the default. Apps already pass a brand-shaped prefix
32
- * (`"mention"`, `"homiio"`, …) so this gives most apps a correct name with
33
- * zero extra config.
34
- * 3. On web only, a meaningful `document.title` (trimmed). This rescues
35
- * zero-config web apps that set a page title but no prefix.
36
- * 4. `Platform.OS` as the terminal fallback. On web this yields the historical
37
- * `"web"` value — now reached ONLY when an app supplies neither an explicit
38
- * name, a custom prefix, nor a document title.
39
- *
40
- * The result is never empty.
41
- */
42
- export function resolveAppDisplayName(appName, storageKeyPrefix) {
43
- const explicit = appName?.trim();
44
- if (explicit) {
45
- return explicit;
46
- }
47
- const prefix = storageKeyPrefix?.trim();
48
- if (prefix && prefix !== DEFAULT_STORAGE_KEY_PREFIX) {
49
- return capitalizeFirst(prefix);
50
- }
51
- if (Platform.OS === 'web' && typeof document !== 'undefined') {
52
- const title = document.title?.trim();
53
- if (title) {
54
- return title;
55
- }
56
- }
57
- return Platform.OS;
58
- }
59
- //# sourceMappingURL=appName.js.map
@@ -1 +0,0 @@
1
- {"version":3,"names":["Platform","DEFAULT_STORAGE_KEY_PREFIX","capitalizeFirst","value","charAt","toUpperCase","slice","resolveAppDisplayName","appName","storageKeyPrefix","explicit","trim","prefix","OS","document","title"],"sourceRoot":"../../../../src","sources":["ui/utils/appName.ts"],"mappings":";;AAAA,SAASA,QAAQ,QAAQ,cAAc;;AAEvC;AACA;AACA;AACA;AACA;AACA,MAAMC,0BAA0B,GAAG,aAAa;;AAEhD;AACA;AACA;AACA;AACA;AACA;AACA,SAASC,eAAeA,CAACC,KAAa,EAAU;EAC9C,OAAOA,KAAK,CAACC,MAAM,CAAC,CAAC,CAAC,CAACC,WAAW,CAAC,CAAC,GAAGF,KAAK,CAACG,KAAK,CAAC,CAAC,CAAC;AACvD;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,qBAAqBA,CACnCC,OAA2B,EAC3BC,gBAAoC,EAC5B;EACR,MAAMC,QAAQ,GAAGF,OAAO,EAAEG,IAAI,CAAC,CAAC;EAChC,IAAID,QAAQ,EAAE;IACZ,OAAOA,QAAQ;EACjB;EAEA,MAAME,MAAM,GAAGH,gBAAgB,EAAEE,IAAI,CAAC,CAAC;EACvC,IAAIC,MAAM,IAAIA,MAAM,KAAKX,0BAA0B,EAAE;IACnD,OAAOC,eAAe,CAACU,MAAM,CAAC;EAChC;EAEA,IAAIZ,QAAQ,CAACa,EAAE,KAAK,KAAK,IAAI,OAAOC,QAAQ,KAAK,WAAW,EAAE;IAC5D,MAAMC,KAAK,GAAGD,QAAQ,CAACC,KAAK,EAAEJ,IAAI,CAAC,CAAC;IACpC,IAAII,KAAK,EAAE;MACT,OAAOA,KAAK;IACd;EACF;EAEA,OAAOf,QAAQ,CAACa,EAAE;AACpB","ignoreList":[]}
@@ -1,22 +0,0 @@
1
- /**
2
- * Resolve a human-readable application display name for the consent / sign-in
3
- * UI shown by the central Oxy auth experience (e.g. "Mention wants to access
4
- * your Oxy account"). This is sent as the `appId` field on
5
- * `POST /auth/session/create` and rendered verbatim by the auth consent page.
6
- *
7
- * Resolution order (first non-empty wins):
8
- * 1. An explicit `appName` declared by the consumer on `OxyProvider`.
9
- * 2. The capitalized `storageKeyPrefix` — but only when the consumer actually
10
- * overrode the default. Apps already pass a brand-shaped prefix
11
- * (`"mention"`, `"homiio"`, …) so this gives most apps a correct name with
12
- * zero extra config.
13
- * 3. On web only, a meaningful `document.title` (trimmed). This rescues
14
- * zero-config web apps that set a page title but no prefix.
15
- * 4. `Platform.OS` as the terminal fallback. On web this yields the historical
16
- * `"web"` value — now reached ONLY when an app supplies neither an explicit
17
- * name, a custom prefix, nor a document title.
18
- *
19
- * The result is never empty.
20
- */
21
- export declare function resolveAppDisplayName(appName: string | undefined, storageKeyPrefix: string | undefined): string;
22
- //# sourceMappingURL=appName.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"appName.d.ts","sourceRoot":"","sources":["../../../../../src/ui/utils/appName.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,gBAAgB,EAAE,MAAM,GAAG,SAAS,GACnC,MAAM,CAmBR"}
@@ -1,22 +0,0 @@
1
- /**
2
- * Resolve a human-readable application display name for the consent / sign-in
3
- * UI shown by the central Oxy auth experience (e.g. "Mention wants to access
4
- * your Oxy account"). This is sent as the `appId` field on
5
- * `POST /auth/session/create` and rendered verbatim by the auth consent page.
6
- *
7
- * Resolution order (first non-empty wins):
8
- * 1. An explicit `appName` declared by the consumer on `OxyProvider`.
9
- * 2. The capitalized `storageKeyPrefix` — but only when the consumer actually
10
- * overrode the default. Apps already pass a brand-shaped prefix
11
- * (`"mention"`, `"homiio"`, …) so this gives most apps a correct name with
12
- * zero extra config.
13
- * 3. On web only, a meaningful `document.title` (trimmed). This rescues
14
- * zero-config web apps that set a page title but no prefix.
15
- * 4. `Platform.OS` as the terminal fallback. On web this yields the historical
16
- * `"web"` value — now reached ONLY when an app supplies neither an explicit
17
- * name, a custom prefix, nor a document title.
18
- *
19
- * The result is never empty.
20
- */
21
- export declare function resolveAppDisplayName(appName: string | undefined, storageKeyPrefix: string | undefined): string;
22
- //# sourceMappingURL=appName.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"appName.d.ts","sourceRoot":"","sources":["../../../../../src/ui/utils/appName.ts"],"names":[],"mappings":"AAmBA;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,gBAAgB,EAAE,MAAM,GAAG,SAAS,GACnC,MAAM,CAmBR"}
@@ -1,52 +0,0 @@
1
- import { resolveAppDisplayName } from '../appName';
2
-
3
- // The shared react-native mock pins `Platform.OS` to 'web', which is exactly
4
- // the platform on which the historical "web wants to access your Oxy account"
5
- // regression occurred. These tests assert the resolution order that prevents it.
6
-
7
- describe('resolveAppDisplayName', () => {
8
- const originalTitle = typeof document !== 'undefined' ? document.title : '';
9
-
10
- afterEach(() => {
11
- if (typeof document !== 'undefined') {
12
- document.title = originalTitle;
13
- }
14
- });
15
-
16
- it('prefers an explicit appName, trimmed', () => {
17
- expect(resolveAppDisplayName(' Mention ', 'oxy_session')).toBe('Mention');
18
- });
19
-
20
- it('explicit appName wins over a custom storageKeyPrefix', () => {
21
- expect(resolveAppDisplayName('Mention', 'homiio')).toBe('Mention');
22
- });
23
-
24
- it('capitalizes a custom storageKeyPrefix when no appName is given', () => {
25
- expect(resolveAppDisplayName(undefined, 'mention')).toBe('Mention');
26
- });
27
-
28
- it('ignores the default storageKeyPrefix (never surfaces "Oxy_session")', () => {
29
- if (typeof document !== 'undefined') {
30
- document.title = '';
31
- }
32
- expect(resolveAppDisplayName(undefined, 'oxy_session')).toBe('web');
33
- });
34
-
35
- it('falls back to document.title on web when no name or custom prefix is set', () => {
36
- if (typeof document !== 'undefined') {
37
- document.title = 'Homiio';
38
- }
39
- expect(resolveAppDisplayName(undefined, 'oxy_session')).toBe('Homiio');
40
- });
41
-
42
- it('falls back to the platform only when nothing else is available', () => {
43
- if (typeof document !== 'undefined') {
44
- document.title = '';
45
- }
46
- expect(resolveAppDisplayName(undefined, undefined)).toBe('web');
47
- });
48
-
49
- it('treats a whitespace-only appName as absent', () => {
50
- expect(resolveAppDisplayName(' ', 'mention')).toBe('Mention');
51
- });
52
- });
@@ -1,62 +0,0 @@
1
- import { Platform } from 'react-native';
2
-
3
- /**
4
- * The `storageKeyPrefix` default applied by `OxyContextProvider`. When the
5
- * consumer never overrides it, the prefix carries no app-identity signal and
6
- * must NOT be used to derive a display name (it would surface "Oxy_session").
7
- */
8
- const DEFAULT_STORAGE_KEY_PREFIX = 'oxy_session';
9
-
10
- /**
11
- * Capitalize the first character of a non-empty string. Used to turn a lower
12
- * case `storageKeyPrefix` (e.g. `"mention"`) into a presentable label
13
- * (`"Mention"`). Pure; leaves the remainder untouched so multi-word or already
14
- * capitalized values are preserved.
15
- */
16
- function capitalizeFirst(value: string): string {
17
- return value.charAt(0).toUpperCase() + value.slice(1);
18
- }
19
-
20
- /**
21
- * Resolve a human-readable application display name for the consent / sign-in
22
- * UI shown by the central Oxy auth experience (e.g. "Mention wants to access
23
- * your Oxy account"). This is sent as the `appId` field on
24
- * `POST /auth/session/create` and rendered verbatim by the auth consent page.
25
- *
26
- * Resolution order (first non-empty wins):
27
- * 1. An explicit `appName` declared by the consumer on `OxyProvider`.
28
- * 2. The capitalized `storageKeyPrefix` — but only when the consumer actually
29
- * overrode the default. Apps already pass a brand-shaped prefix
30
- * (`"mention"`, `"homiio"`, …) so this gives most apps a correct name with
31
- * zero extra config.
32
- * 3. On web only, a meaningful `document.title` (trimmed). This rescues
33
- * zero-config web apps that set a page title but no prefix.
34
- * 4. `Platform.OS` as the terminal fallback. On web this yields the historical
35
- * `"web"` value — now reached ONLY when an app supplies neither an explicit
36
- * name, a custom prefix, nor a document title.
37
- *
38
- * The result is never empty.
39
- */
40
- export function resolveAppDisplayName(
41
- appName: string | undefined,
42
- storageKeyPrefix: string | undefined,
43
- ): string {
44
- const explicit = appName?.trim();
45
- if (explicit) {
46
- return explicit;
47
- }
48
-
49
- const prefix = storageKeyPrefix?.trim();
50
- if (prefix && prefix !== DEFAULT_STORAGE_KEY_PREFIX) {
51
- return capitalizeFirst(prefix);
52
- }
53
-
54
- if (Platform.OS === 'web' && typeof document !== 'undefined') {
55
- const title = document.title?.trim();
56
- if (title) {
57
- return title;
58
- }
59
- }
60
-
61
- return Platform.OS;
62
- }