@oxyhq/core 7.1.1 → 8.1.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 (93) hide show
  1. package/README.md +48 -24
  2. package/dist/cjs/.tsbuildinfo +1 -1
  3. package/dist/cjs/HttpService.js +6 -6
  4. package/dist/cjs/boot/coldBootV2.js +97 -2
  5. package/dist/cjs/boot/deviceBootReturn.js +15 -0
  6. package/dist/cjs/i18n/locales/en-US.json +44 -1
  7. package/dist/cjs/i18n/locales/es-ES.json +44 -1
  8. package/dist/cjs/i18n/locales/locales/en-US.json +45 -2
  9. package/dist/cjs/i18n/locales/locales/es-ES.json +45 -2
  10. package/dist/cjs/index.js +19 -16
  11. package/dist/cjs/mixins/OxyServices.deviceBoot.js +28 -0
  12. package/dist/cjs/server/index.js +1 -7
  13. package/dist/cjs/session/accountDialogController.js +1 -1
  14. package/dist/cjs/session/accountProjection.js +1 -1
  15. package/dist/cjs/session/authStateStore.js +6 -0
  16. package/dist/cjs/session/projectSessionState.js +1 -1
  17. package/dist/cjs/session/refresh.js +9 -0
  18. package/dist/cjs/session/sessionClientHost.js +1 -2
  19. package/dist/cjs/utils/accountUtils.js +1 -1
  20. package/dist/cjs/utils/oauthPkce.js +142 -0
  21. package/dist/cjs/utils/platform.js +1 -1
  22. package/dist/esm/.tsbuildinfo +1 -1
  23. package/dist/esm/HttpService.js +6 -6
  24. package/dist/esm/boot/coldBootV2.js +97 -2
  25. package/dist/esm/boot/deviceBootReturn.js +15 -0
  26. package/dist/esm/i18n/locales/en-US.json +44 -1
  27. package/dist/esm/i18n/locales/es-ES.json +44 -1
  28. package/dist/esm/i18n/locales/locales/en-US.json +45 -2
  29. package/dist/esm/i18n/locales/locales/es-ES.json +45 -2
  30. package/dist/esm/index.js +11 -13
  31. package/dist/esm/mixins/OxyServices.deviceBoot.js +29 -1
  32. package/dist/esm/server/index.js +0 -5
  33. package/dist/esm/session/accountDialogController.js +1 -1
  34. package/dist/esm/session/accountProjection.js +1 -1
  35. package/dist/esm/session/authStateStore.js +6 -0
  36. package/dist/esm/session/projectSessionState.js +1 -1
  37. package/dist/esm/session/refresh.js +9 -0
  38. package/dist/esm/session/sessionClientHost.js +1 -2
  39. package/dist/esm/utils/accountUtils.js +1 -1
  40. package/dist/esm/utils/oauthPkce.js +135 -0
  41. package/dist/esm/utils/platform.js +1 -1
  42. package/dist/types/.tsbuildinfo +1 -1
  43. package/dist/types/HttpService.d.ts +1 -1
  44. package/dist/types/index.d.ts +3 -2
  45. package/dist/types/mixins/OxyServices.accounts.d.ts +13 -3
  46. package/dist/types/mixins/OxyServices.connectedApps.d.ts +4 -0
  47. package/dist/types/mixins/OxyServices.deviceBoot.d.ts +17 -1
  48. package/dist/types/mixins/OxyServices.devices.d.ts +3 -2
  49. package/dist/types/models/interfaces.d.ts +4 -4
  50. package/dist/types/server/index.d.ts +0 -1
  51. package/dist/types/session/accountDialogController.d.ts +1 -1
  52. package/dist/types/session/accountProjection.d.ts +1 -1
  53. package/dist/types/session/authStateStore.d.ts +20 -0
  54. package/dist/types/session/projectSessionState.d.ts +1 -1
  55. package/dist/types/session/refresh.d.ts +4 -8
  56. package/dist/types/session/sessionClientHost.d.ts +1 -2
  57. package/dist/types/utils/accountUtils.d.ts +1 -1
  58. package/dist/types/utils/oauthPkce.d.ts +74 -0
  59. package/dist/types/utils/platform.d.ts +1 -1
  60. package/package.json +3 -3
  61. package/src/HttpService.ts +6 -6
  62. package/src/boot/__tests__/coldBootV2.test.ts +215 -1
  63. package/src/boot/__tests__/deviceBootReturn.test.ts +32 -0
  64. package/src/boot/coldBootV2.ts +117 -2
  65. package/src/boot/deviceBootReturn.ts +15 -0
  66. package/src/i18n/locales/en-US.json +45 -2
  67. package/src/i18n/locales/es-ES.json +45 -2
  68. package/src/index.ts +23 -16
  69. package/src/mixins/OxyServices.accounts.ts +12 -0
  70. package/src/mixins/OxyServices.connectedApps.ts +4 -0
  71. package/src/mixins/OxyServices.deviceBoot.ts +38 -0
  72. package/src/mixins/OxyServices.devices.ts +6 -5
  73. package/src/mixins/__tests__/OxyServices.deviceBoot.test.ts +44 -1
  74. package/src/mixins/__tests__/accounts.test.ts +1 -1
  75. package/src/models/interfaces.ts +7 -5
  76. package/src/server/index.ts +0 -6
  77. package/src/session/__tests__/authStateStore.test.ts +27 -0
  78. package/src/session/__tests__/refresh.test.ts +14 -0
  79. package/src/session/accountDialogController.ts +1 -1
  80. package/src/session/accountProjection.ts +1 -1
  81. package/src/session/authStateStore.ts +26 -0
  82. package/src/session/projectSessionState.ts +1 -1
  83. package/src/session/refresh.ts +13 -8
  84. package/src/session/sessionClientHost.ts +1 -2
  85. package/src/utils/__tests__/coldBoot.test.ts +55 -65
  86. package/src/utils/__tests__/oauthPkce.test.ts +154 -0
  87. package/src/utils/accountUtils.ts +1 -1
  88. package/src/utils/oauthPkce.ts +189 -0
  89. package/src/utils/platform.ts +1 -1
  90. package/dist/cjs/utils/ssoBounce.js +0 -24
  91. package/dist/esm/utils/ssoBounce.js +0 -21
  92. package/dist/types/utils/ssoBounce.d.ts +0 -21
  93. package/src/utils/ssoBounce.ts +0 -22
package/README.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  OxyHQ SDK Foundation. Platform-agnostic core library that works in Node.js, browser, and React Native environments. No React dependency.
4
4
 
5
- **Current published version: 3.4.16**
6
-
7
5
  ## Installation
8
6
 
9
7
  ```bash
@@ -13,7 +11,8 @@ bun add @oxyhq/core
13
11
  ## Contents
14
12
 
15
13
  - **OxyServices API client** — all API methods for interacting with OxyHQ services
16
- - **AuthManager, CrossDomainAuth** — authentication and cross-domain session handling
14
+ - **Device-first session engine** — `SessionClient` (`src/session/`), `runSessionColdBoot`, and the device-session mixin that back `OxyProvider` in `@oxyhq/services`
15
+ - **OAuth helpers** — `generatePkcePair`, `generateOAuthState`, `buildOAuthAuthorizeUrl` for third-party "Sign in with Oxy" (see [docs/auth/integration-guide.md](../../docs/auth/integration-guide.md))
17
16
  - **Crypto** — KeyManager, SignatureService, RecoveryPhraseService
18
17
  - **Models and types** — User, ApiError, ClientSession, and more
19
18
  - **i18n** — translate function and locale files
@@ -21,31 +20,61 @@ bun add @oxyhq/core
21
20
  - **Platform detection utilities**
22
21
  - **Device management**
23
22
  - **Linked clients** for app backends that need the active Oxy bearer token
24
- - **User identity contracts and handle normalization** so SDK user payloads expose required `id`/`displayName` fields and apps build local/federated profile handles consistently
23
+ - **User identity contracts and handle normalization** so apps render display names and build local/federated profile handles consistently
25
24
  - **Server middleware** for Express request identity and per-user rate limiting
26
25
 
27
26
  ## Exports
28
27
 
29
28
  The package exposes two public entry points:
30
29
 
31
- - `@oxyhq/core` — main entry (API client, auth, crypto, models, shared utilities, i18n, platform, device)
32
- - `@oxyhq/core/server` — Express-only helpers (`createOxyRateLimit`, `createOxyAuthMiddleware`, `requireOxyAuth`, `getOxyUserId`, `getRequiredOxyUserId`, and request types)
30
+ - `@oxyhq/core` — main entry (API client, session, crypto, models, shared utilities, i18n, platform, device)
31
+ - `@oxyhq/core/server` — Express-only helpers (`createOxyRateLimit`, `createOxyAuthMiddleware`, `requireOxyAuth`, `getOxyUserId`, `getRequiredOxyUserId`, `createOxyCors`, `safeFetch`, `verifySecret`, and request types)
33
32
 
34
- All client/runtime symbols (including `KeyManager`, `SignatureService`, `RecoveryPhraseService`, and the shared color / theme / error / network / debug helpers) are re-exported from the package root. Server-only Express helpers live under `@oxyhq/core/server` so React Native and browser bundles never import Express.
33
+ All client/runtime symbols (including `SessionClient`, `KeyManager`, `SignatureService`, `RecoveryPhraseService`, and the shared color / theme / error / network / debug helpers) are re-exported from the package root. Server-only Express helpers live under `@oxyhq/core/server` so React Native and browser bundles never import Express.
35
34
 
36
35
  ## Usage
37
36
 
38
37
  ```ts
39
- import { OxyServices, oxyClient, KeyManager, SignatureService } from '@oxyhq/core';
38
+ import { OxyServices, oxyClient, KeyManager } from '@oxyhq/core';
40
39
  import type { User, ApiError } from '@oxyhq/core';
41
40
 
42
41
  // Get user
43
42
  const user = await oxyClient.getUserById('123');
44
43
 
45
- // Crypto
46
- const keyManager = new KeyManager();
44
+ // Crypto (KeyManager methods are static)
45
+ const hasIdentity = await KeyManager.hasIdentity();
47
46
  ```
48
47
 
48
+ ## Device-First Sessions
49
+
50
+ The session authority is the server-side `DeviceSession` (one document per device: signed-in accounts + active account + revision). `@oxyhq/core` owns the whole client side of that contract:
51
+
52
+ - **`SessionClient`** (`src/session/`) — reads `GET /session/device/state`, mutates via `POST /session/device/{add,switch,signout}`, and applies `session_state` socket pushes (room `device:<deviceId>`, token-free payload) so every app on the same device stays in sync.
53
+ - **`runSessionColdBoot`** (`src/boot/coldBootV2.ts`) — the ordered, short-circuit cold-boot runner used by `OxyProvider`. It restores silently from device state or resolves to logged-out; it NEVER auto-redirects to a login page.
54
+ - **Boot handoff** — `GET /auth/device/bootstrap` → `#oxy_boot` fragment → `POST /auth/device/exchange` (single-use, origin-bound code) lets a first visit on a sibling origin adopt the existing device session.
55
+
56
+ The current transport is a durable first-party `oxy_device` cookie (`Domain=.oxy.so`, opaque secret — never the deviceId) plus a persisted rotating refresh-token family. This transport is frozen by decision; a cookie-free `deviceSecret` mint is a pending design goal, not current behavior. Full contract: [docs/auth/device-session.md](../../docs/auth/device-session.md).
57
+
58
+ Consumers never build session restore themselves — mount `OxyProvider` from `@oxyhq/services` with a registered `clientId`.
59
+
60
+ ## OAuth Helpers (third party)
61
+
62
+ Third-party apps sign users in with standard OAuth 2.0 Authorization Code + PKCE against `auth.oxy.so`:
63
+
64
+ ```ts
65
+ import { generatePkcePair, generateOAuthState, buildOAuthAuthorizeUrl } from '@oxyhq/core';
66
+
67
+ const [pkce, state] = await Promise.all([generatePkcePair(), generateOAuthState()]);
68
+ const url = buildOAuthAuthorizeUrl({
69
+ clientId: 'oxy_dk_…',
70
+ redirectUri: 'https://merchant.example/auth/callback',
71
+ codeChallenge: pkce.codeChallenge,
72
+ state,
73
+ });
74
+ ```
75
+
76
+ `OxySignInButton` in `@oxyhq/services` uses these internally when the resolved Application is `third_party`. See [docs/auth/integration-guide.md](../../docs/auth/integration-guide.md).
77
+
49
78
  ## User Identity And Handles
50
79
 
51
80
  SDK user payloads may arrive with either `id` or Mongo-style `_id`; normalize
@@ -58,10 +87,10 @@ const id = getNormalizedUserId(user);
58
87
  const normalizedUser = normalizeUserIdentity(user);
59
88
  ```
60
89
 
61
- `User.displayName` is a required API contract. The API composes it server-side
62
- from the structured name when present, otherwise from the username/server
63
- fallback. UI consumers should render `displayName` directly instead of rebuilding
64
- names from `name.first`, `name.last`, `name.full`, or `username`.
90
+ `User.name.displayName` is **optional** federated or unresolved actors routinely
91
+ omit it. Render it directly when present; when absent, fall back to the
92
+ normalized handle (`displayName ?? handle`). Never rebuild names from
93
+ `name.first`, `name.last`, `name.full`, or `username`.
65
94
 
66
95
  For profile display/routing, use `getNormalizedUserHandle()`. It strips a
67
96
  leading `@`, preserves an existing `user@instance` handle, and appends
@@ -93,7 +122,7 @@ Linked clients send the current Oxy bearer token for authenticated requests.
93
122
  State-changing bearer requests do not fetch app-local CSRF tokens; cookie-only
94
123
  writes still use CSRF.
95
124
 
96
- **GET response caching is OFF by default for linked clients** (since 3.9.0). The
125
+ **GET response caching is OFF by default for linked clients.** The
97
126
  SDK's per-instance GET cache is only safe on the canonical `OxyServices` client,
98
127
  where every mutation (`updateProfile`, `followUser`, `blockUser`, …) busts the
99
128
  matching cached GET. A linked client targets the consuming app's own backend,
@@ -145,7 +174,9 @@ using backend-specific fields.
145
174
  bun run build
146
175
  ```
147
176
 
148
- Compiles with TypeScript, producing CJS, ESM, and type declaration outputs.
177
+ Compiles with TypeScript, producing CJS, ESM, and type declaration outputs. The
178
+ ESM build must never contain `require()` calls — use `await import()` for
179
+ optional/platform-specific modules.
149
180
 
150
181
  ## KeyManager Safety
151
182
 
@@ -153,13 +184,6 @@ Compiles with TypeScript, producing CJS, ESM, and type declaration outputs.
153
184
  - `restoreIdentityFromBackup()` treats keychain-read exceptions as transient — never clobbers a healthy-but-locked primary. Rejects mismatched backups (dual mismatch guards).
154
185
  - `deleteIdentity(skipBackup=false, force=false, userConfirmed=false)` — `force=true` also deletes the backup slot.
155
186
 
156
- ## FedCM (`OxyServices.fedcm.ts`)
157
-
158
- - Use W3C-spec `mode` enum: `'active'` / `'passive'`. Do NOT use legacy `'button'` / `'widget'` (Chrome throws TypeError).
159
- - Client sends `'active'` first, transparently retries with legacy value for Chrome 125–131 backwards compat.
160
- - Token exchange requires a server-minted nonce from `POST /fedcm/nonce` — local UUID nonces are rejected.
161
- - **Silent SSO guard is NOT here**: a module-level singleton in core was tried and reverted — it re-evaluates in the Metro web bundle so the guard did not hold. The guard lives in the consumer hooks (`useWebSSO` in `@oxyhq/services` and `@oxyhq/auth`) and in `WebOxyProvider`. Do NOT move it back into a core module-level singleton.
162
-
163
187
  ## `verifyChallenge` Token Planting
164
188
 
165
- `OxyServices.verifyChallenge()` calls `setTokens(accessToken, refreshToken ?? '')` internally before returning. Callers do not need to plant tokens manually after `verifyChallenge` — the SDK handles it.
189
+ `OxyServices.verifyChallenge()` plants the freshly-minted access token internally before returning. Callers do not need to plant tokens manually after `verifyChallenge` — the SDK handles it.