@oxy.so/core 4.5.0-oxy-one.1 → 4.6.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 (283) hide show
  1. package/README.md +1 -1
  2. package/dist/cjs/HttpService.js +63 -19
  3. package/dist/cjs/api/accounts.js +10 -3
  4. package/dist/cjs/api/agency.js +3 -1
  5. package/dist/cjs/api/apps.js +15 -4
  6. package/dist/cjs/api/assets.js +39 -11
  7. package/dist/cjs/api/auth.js +28 -8
  8. package/dist/cjs/api/billing.js +24 -7
  9. package/dist/cjs/api/civic.js +14 -4
  10. package/dist/cjs/api/follows.js +9 -2
  11. package/dist/cjs/api/identity.js +31 -9
  12. package/dist/cjs/api/nodes.js +3 -1
  13. package/dist/cjs/api/privacy.js +8 -2
  14. package/dist/cjs/api/session.js +15 -4
  15. package/dist/cjs/api/store.js +15 -5
  16. package/dist/cjs/api/topics.js +10 -3
  17. package/dist/cjs/api/users.js +16 -4
  18. package/dist/cjs/boot/sessionColdBoot.js +40 -13
  19. package/dist/cjs/constants/version.js +6 -6
  20. package/dist/cjs/crypto/identityMarker.js +3 -1
  21. package/dist/cjs/crypto/identityProof.js +6 -1
  22. package/dist/cjs/crypto/keyManager.js +87 -32
  23. package/dist/cjs/crypto/recoveryPhrase.js +2 -2
  24. package/dist/cjs/crypto/signatureService.js +3 -4
  25. package/dist/cjs/i18n/index.js +16 -3
  26. package/dist/cjs/i18n/locales/ar-SA.js +366 -366
  27. package/dist/cjs/i18n/locales/ca-ES.js +366 -366
  28. package/dist/cjs/i18n/locales/de-DE.js +366 -366
  29. package/dist/cjs/i18n/locales/en-US.js +1994 -1994
  30. package/dist/cjs/i18n/locales/es-ES.js +1994 -1994
  31. package/dist/cjs/i18n/locales/fr-FR.js +366 -366
  32. package/dist/cjs/i18n/locales/it-IT.js +366 -366
  33. package/dist/cjs/i18n/locales/ja-JP.js +366 -366
  34. package/dist/cjs/i18n/locales/ko-KR.js +366 -366
  35. package/dist/cjs/i18n/locales/pt-PT.js +366 -366
  36. package/dist/cjs/i18n/locales/zh-CN.js +366 -366
  37. package/dist/cjs/inference/OxyInferenceClient.js +12 -18
  38. package/dist/cjs/models/interfaces.js +11 -11
  39. package/dist/cjs/server/OxyServer.js +86 -28
  40. package/dist/cjs/server/agentAccount.js +6 -2
  41. package/dist/cjs/server/aliaMachineCredential.js +39 -14
  42. package/dist/cjs/server/auth.js +5 -4
  43. package/dist/cjs/server/capabilityTicket.js +5 -5
  44. package/dist/cjs/server/index.js +2 -1
  45. package/dist/cjs/server/liveCapabilityTicket.js +4 -1
  46. package/dist/cjs/server/middleware.js +61 -42
  47. package/dist/cjs/server/namespaces.js +28 -7
  48. package/dist/cjs/server/requesterAssertion.js +36 -30
  49. package/dist/cjs/server/serviceTokenLifetime.js +7 -4
  50. package/dist/cjs/server/traffic.js +53 -14
  51. package/dist/cjs/server/trafficNodeHttp.js +33 -8
  52. package/dist/cjs/server/workloadIdentity.client.js +4 -0
  53. package/dist/cjs/server/workloadIdentity.js +28 -1
  54. package/dist/cjs/session/SessionClient.js +54 -17
  55. package/dist/cjs/session/accountDelegationAccess.js +4 -6
  56. package/dist/cjs/session/accountDialogController.js +1 -1
  57. package/dist/cjs/session/authStateStore.js +18 -9
  58. package/dist/cjs/session/identityPin.js +6 -2
  59. package/dist/cjs/session/refresh.js +81 -23
  60. package/dist/cjs/session/sharedDeviceCredential.js +28 -15
  61. package/dist/cjs/shared/utils/errorUtils.js +1 -1
  62. package/dist/cjs/shared/utils/networkUtils.js +1 -1
  63. package/dist/cjs/shared/utils/themeUtils.js +1 -1
  64. package/dist/cjs/utils/accountCacheSweep.js +3 -1
  65. package/dist/cjs/utils/accountUtils.js +1 -3
  66. package/dist/cjs/utils/asyncUtils.js +1 -1
  67. package/dist/cjs/utils/authHelpers.js +2 -1
  68. package/dist/cjs/utils/avatarUtils.js +4 -2
  69. package/dist/cjs/utils/cache.js +1 -1
  70. package/dist/cjs/utils/cacheKey.js +3 -1
  71. package/dist/cjs/utils/deviceManager.js +4 -4
  72. package/dist/cjs/utils/errorUtils.js +6 -4
  73. package/dist/cjs/utils/languageUtils.js +404 -57
  74. package/dist/cjs/utils/profileLinks.js +1 -3
  75. package/dist/cjs/utils/requestUtils.js +7 -1
  76. package/dist/cjs/utils/userIdentity.js +1 -1
  77. package/dist/cjs/utils/validationUtils.js +5 -6
  78. package/dist/esm/HttpService.js +64 -20
  79. package/dist/esm/api/accounts.js +11 -4
  80. package/dist/esm/api/agency.js +3 -1
  81. package/dist/esm/api/apps.js +15 -4
  82. package/dist/esm/api/assets.js +39 -11
  83. package/dist/esm/api/auth.js +28 -8
  84. package/dist/esm/api/billing.js +25 -8
  85. package/dist/esm/api/civic.js +14 -4
  86. package/dist/esm/api/follows.js +9 -2
  87. package/dist/esm/api/identity.js +31 -9
  88. package/dist/esm/api/nodes.js +3 -1
  89. package/dist/esm/api/privacy.js +8 -2
  90. package/dist/esm/api/session.js +15 -4
  91. package/dist/esm/api/store.js +15 -5
  92. package/dist/esm/api/topics.js +10 -3
  93. package/dist/esm/api/users.js +16 -4
  94. package/dist/esm/boot/sessionColdBoot.js +41 -14
  95. package/dist/esm/constants/version.js +6 -6
  96. package/dist/esm/crypto/identityMarker.js +3 -1
  97. package/dist/esm/crypto/identityProof.js +6 -1
  98. package/dist/esm/crypto/internal.js +1 -1
  99. package/dist/esm/crypto/keyManager.js +87 -32
  100. package/dist/esm/crypto/recoveryPhrase.js +2 -2
  101. package/dist/esm/crypto/signatureService.js +3 -4
  102. package/dist/esm/i18n/index.js +16 -3
  103. package/dist/esm/i18n/locales/ar-SA.js +366 -366
  104. package/dist/esm/i18n/locales/ca-ES.js +366 -366
  105. package/dist/esm/i18n/locales/de-DE.js +366 -366
  106. package/dist/esm/i18n/locales/en-US.js +1994 -1994
  107. package/dist/esm/i18n/locales/es-ES.js +1994 -1994
  108. package/dist/esm/i18n/locales/fr-FR.js +366 -366
  109. package/dist/esm/i18n/locales/it-IT.js +366 -366
  110. package/dist/esm/i18n/locales/ja-JP.js +366 -366
  111. package/dist/esm/i18n/locales/ko-KR.js +366 -366
  112. package/dist/esm/i18n/locales/pt-PT.js +366 -366
  113. package/dist/esm/i18n/locales/zh-CN.js +366 -366
  114. package/dist/esm/index.js +1 -1
  115. package/dist/esm/inference/OxyInferenceClient.js +13 -19
  116. package/dist/esm/models/interfaces.js +11 -11
  117. package/dist/esm/server/OxyServer.js +87 -29
  118. package/dist/esm/server/agentAccount.js +6 -2
  119. package/dist/esm/server/aliaMachineCredential.js +39 -14
  120. package/dist/esm/server/auth.js +6 -5
  121. package/dist/esm/server/capabilityTicket.js +6 -6
  122. package/dist/esm/server/index.js +5 -5
  123. package/dist/esm/server/liveCapabilityTicket.js +6 -3
  124. package/dist/esm/server/middleware.js +62 -43
  125. package/dist/esm/server/namespaces.js +31 -10
  126. package/dist/esm/server/requesterAssertion.js +36 -30
  127. package/dist/esm/server/serviceTokenLifetime.js +7 -4
  128. package/dist/esm/server/traffic.js +55 -16
  129. package/dist/esm/server/trafficNodeHttp.js +34 -9
  130. package/dist/esm/server/userInvalidation.js +1 -1
  131. package/dist/esm/server/workloadIdentity.client.js +3 -0
  132. package/dist/esm/server/workloadIdentity.js +27 -1
  133. package/dist/esm/session/SessionClient.js +54 -17
  134. package/dist/esm/session/accountDelegationAccess.js +5 -7
  135. package/dist/esm/session/accountDialogController.js +1 -1
  136. package/dist/esm/session/authStateStore.js +18 -9
  137. package/dist/esm/session/identityPin.js +6 -2
  138. package/dist/esm/session/refresh.js +82 -24
  139. package/dist/esm/session/sharedDeviceCredential.js +28 -15
  140. package/dist/esm/shared/utils/errorUtils.js +1 -1
  141. package/dist/esm/shared/utils/networkUtils.js +1 -1
  142. package/dist/esm/shared/utils/themeUtils.js +1 -1
  143. package/dist/esm/utils/accountCacheSweep.js +3 -1
  144. package/dist/esm/utils/accountUtils.js +1 -3
  145. package/dist/esm/utils/asyncUtils.js +1 -1
  146. package/dist/esm/utils/authHelpers.js +2 -1
  147. package/dist/esm/utils/avatarUtils.js +4 -2
  148. package/dist/esm/utils/cache.js +1 -1
  149. package/dist/esm/utils/cacheKey.js +3 -1
  150. package/dist/esm/utils/deviceManager.js +4 -4
  151. package/dist/esm/utils/errorUtils.js +6 -4
  152. package/dist/esm/utils/languageUtils.js +404 -57
  153. package/dist/esm/utils/profileLinks.js +1 -3
  154. package/dist/esm/utils/requestUtils.js +7 -1
  155. package/dist/esm/utils/userIdentity.js +1 -1
  156. package/dist/esm/utils/validationUtils.js +5 -6
  157. package/dist/types/HttpService.d.ts +1 -1
  158. package/dist/types/api/accounts.d.ts +16 -3
  159. package/dist/types/api/apps.d.ts +4 -4
  160. package/dist/types/crypto/internal.d.ts +1 -1
  161. package/dist/types/index.d.ts +4 -4
  162. package/dist/types/models/interfaces.d.ts +9 -1
  163. package/dist/types/server/index.d.ts +6 -6
  164. package/dist/types/server/workloadIdentity.client.d.ts +5 -1
  165. package/dist/types/server/workloadIdentity.d.ts +26 -0
  166. package/dist/types/session/accountDelegationAccess.d.ts +1 -1
  167. package/dist/types/session/index.d.ts +1 -1
  168. package/dist/types/utils/userIdentity.d.ts +1 -1
  169. package/dist/types/utils/validationUtils.d.ts +4 -5
  170. package/package.json +3 -4
  171. package/src/HttpService.ts +183 -53
  172. package/src/OxyServices.errors.ts +7 -3
  173. package/src/OxyServices.ts +6 -2
  174. package/src/api/accounts.ts +96 -28
  175. package/src/api/agency.ts +24 -9
  176. package/src/api/appData.ts +12 -2
  177. package/src/api/apps.ts +81 -31
  178. package/src/api/assets.ts +113 -43
  179. package/src/api/auth.ts +145 -77
  180. package/src/api/billing.ts +112 -65
  181. package/src/api/civic.ts +61 -19
  182. package/src/api/contacts.ts +4 -1
  183. package/src/api/devices.ts +54 -21
  184. package/src/api/follows.ts +88 -31
  185. package/src/api/identity.ts +101 -33
  186. package/src/api/linkedAccounts.ts +26 -8
  187. package/src/api/nodes.ts +18 -6
  188. package/src/api/notifications.ts +26 -6
  189. package/src/api/privacy.ts +42 -8
  190. package/src/api/reputation.ts +18 -7
  191. package/src/api/session.ts +23 -5
  192. package/src/api/store.ts +81 -31
  193. package/src/api/topics.ts +18 -6
  194. package/src/api/users.ts +98 -35
  195. package/src/boot/sessionColdBoot.ts +60 -19
  196. package/src/civic/payloads.ts +1 -4
  197. package/src/client/context.ts +6 -1
  198. package/src/constants/version.ts +7 -6
  199. package/src/crypto/aead.ts +1 -5
  200. package/src/crypto/ecdh.ts +1 -4
  201. package/src/crypto/identityDerivations.ts +5 -1
  202. package/src/crypto/identityMarker.ts +3 -1
  203. package/src/crypto/identityProof.ts +6 -1
  204. package/src/crypto/index.ts +11 -11
  205. package/src/crypto/internal.ts +6 -1
  206. package/src/crypto/keyManager.ts +348 -98
  207. package/src/crypto/recoveryPhrase.ts +14 -9
  208. package/src/crypto/registrationPow.ts +1 -1
  209. package/src/crypto/signatureService.ts +14 -6
  210. package/src/i18n/accountCategoryLabels.ts +1 -4
  211. package/src/i18n/accountRoleLabels.ts +1 -4
  212. package/src/i18n/index.ts +25 -7
  213. package/src/i18n/locales/ar-SA.ts +375 -366
  214. package/src/i18n/locales/ca-ES.ts +382 -366
  215. package/src/i18n/locales/de-DE.ts +387 -366
  216. package/src/i18n/locales/en-US.ts +2034 -1994
  217. package/src/i18n/locales/es-ES.ts +2043 -1994
  218. package/src/i18n/locales/fr-FR.ts +387 -366
  219. package/src/i18n/locales/it-IT.ts +380 -366
  220. package/src/i18n/locales/ja-JP.ts +380 -366
  221. package/src/i18n/locales/ko-KR.ts +377 -366
  222. package/src/i18n/locales/pt-PT.ts +383 -366
  223. package/src/i18n/locales/zh-CN.ts +373 -366
  224. package/src/i18n/trustTierLabels.ts +1 -4
  225. package/src/index.ts +371 -349
  226. package/src/inference/OxyInferenceClient.ts +784 -780
  227. package/src/models/interfaces.ts +39 -27
  228. package/src/server/OxyServer.ts +227 -68
  229. package/src/server/agentAccount.ts +8 -2
  230. package/src/server/aliaMachineCredential.ts +57 -16
  231. package/src/server/auth.ts +9 -5
  232. package/src/server/capabilityTicket.ts +30 -21
  233. package/src/server/index.ts +29 -6
  234. package/src/server/liveCapabilityTicket.ts +31 -9
  235. package/src/server/middleware.ts +212 -121
  236. package/src/server/namespaces.ts +247 -94
  237. package/src/server/rateLimit.ts +2 -4
  238. package/src/server/requesterAssertion.ts +93 -54
  239. package/src/server/serviceTokenLifetime.ts +9 -4
  240. package/src/server/traffic.ts +117 -23
  241. package/src/server/trafficNodeHttp.ts +61 -14
  242. package/src/server/userInvalidation.ts +1 -4
  243. package/src/server/workloadIdentity.client.ts +12 -1
  244. package/src/server/workloadIdentity.ts +52 -5
  245. package/src/session/SessionClient.ts +170 -49
  246. package/src/session/accountDelegationAccess.ts +24 -31
  247. package/src/session/accountDialogController.ts +59 -14
  248. package/src/session/accountSwitchTargets.ts +10 -15
  249. package/src/session/authStateStore.ts +121 -81
  250. package/src/session/deviceDirectory.ts +1 -3
  251. package/src/session/identityPin.ts +10 -11
  252. package/src/session/identitySession.ts +3 -1
  253. package/src/session/index.ts +87 -82
  254. package/src/session/refresh.ts +138 -44
  255. package/src/session/sessionClientHost.ts +1 -3
  256. package/src/session/sharedDeviceCredential.ts +35 -14
  257. package/src/session/socketLoader.ts +9 -2
  258. package/src/shared/utils/errorUtils.ts +5 -2
  259. package/src/shared/utils/networkUtils.ts +5 -8
  260. package/src/shared/utils/themeUtils.ts +2 -6
  261. package/src/types/buffer.d.ts +66 -21
  262. package/src/types/expo-secure-store.d.ts +5 -1
  263. package/src/utils/accountCacheSweep.ts +4 -5
  264. package/src/utils/accountUtils.ts +107 -108
  265. package/src/utils/apiUtils.ts +3 -3
  266. package/src/utils/asyncUtils.ts +2 -2
  267. package/src/utils/authHelpers.ts +10 -5
  268. package/src/utils/avatarUtils.ts +11 -6
  269. package/src/utils/cache.ts +9 -10
  270. package/src/utils/cacheKey.ts +5 -1
  271. package/src/utils/coldBoot.ts +1 -3
  272. package/src/utils/commonsDelivery.ts +1 -4
  273. package/src/utils/deviceManager.ts +15 -14
  274. package/src/utils/errorUtils.ts +68 -86
  275. package/src/utils/languageUtils.ts +407 -58
  276. package/src/utils/oauthPkce.ts +1 -4
  277. package/src/utils/platform.ts +0 -1
  278. package/src/utils/profileLinks.ts +2 -5
  279. package/src/utils/requestUtils.ts +18 -14
  280. package/src/utils/sessionUtils.ts +34 -33
  281. package/src/utils/userHandle.ts +3 -1
  282. package/src/utils/userIdentity.ts +1 -1
  283. package/src/utils/validationUtils.ts +11 -9
@@ -807,10 +807,18 @@ export class AccountDialogController {
807
807
  // unexpected — surface it, and keep whatever directory is already held so
808
808
  // an outage degrades rather than blanks the switcher.
809
809
  if (extractErrorStatus(error) === 401) {
810
- logger.debug('[AccountDialogController] directory unauthorized (signed out)', { component: 'AccountDialogController' }, error);
810
+ logger.debug(
811
+ '[AccountDialogController] directory unauthorized (signed out)',
812
+ { component: 'AccountDialogController' },
813
+ error,
814
+ );
811
815
  } else {
812
816
  failure = errorMessage(error);
813
- logger.warn('[AccountDialogController] directory refresh failed', { component: 'AccountDialogController' }, error);
817
+ logger.warn(
818
+ '[AccountDialogController] directory refresh failed',
819
+ { component: 'AccountDialogController' },
820
+ error,
821
+ );
814
822
  }
815
823
  }
816
824
  // Superseded by a newer refresh: that read owns `error` and `loading`, so an
@@ -1041,7 +1049,11 @@ export class AccountDialogController {
1041
1049
  if (!this.isCurrentAttempt(attempt)) return;
1042
1050
  // The identity mint failed — log and fall through to the QR handoff rather
1043
1051
  // than dead-ending the sign-in.
1044
- logger.warn('[AccountDialogController] signInWithCommonsIdentity failed', { component: 'AccountDialogController' }, error);
1052
+ logger.warn(
1053
+ '[AccountDialogController] signInWithCommonsIdentity failed',
1054
+ { component: 'AccountDialogController' },
1055
+ error,
1056
+ );
1045
1057
  }
1046
1058
  await this.startDeviceFlowSession(attempt, { deliver: true });
1047
1059
  }
@@ -1127,7 +1139,10 @@ export class AccountDialogController {
1127
1139
  ): Promise<CommonsSignInHandle | null> {
1128
1140
  if (!this.isCurrentAttempt(attempt)) return null;
1129
1141
  if (!this.clientId) {
1130
- this.failSignIn('not-configured', 'This app is not configured for sign-in (missing clientId).');
1142
+ this.failSignIn(
1143
+ 'not-configured',
1144
+ 'This app is not configured for sign-in (missing clientId).',
1145
+ );
1131
1146
  return null;
1132
1147
  }
1133
1148
  this.setSignIn({ ...IDLE_SIGN_IN_FACTS, phase: 'starting' });
@@ -1235,7 +1250,10 @@ export class AccountDialogController {
1235
1250
  * surfaced as an error: there is nothing the user did wrong and nothing for
1236
1251
  * them to fix.
1237
1252
  */
1238
- private async deliverToKnownCommons(authorizeCode: string, commonsAvailable: boolean): Promise<number> {
1253
+ private async deliverToKnownCommons(
1254
+ authorizeCode: string,
1255
+ commonsAvailable: boolean,
1256
+ ): Promise<number> {
1239
1257
  // Route 1 (mobile with a verified local Commons link) reaches the identity
1240
1258
  // on this very device — pushing as well would notify a second surface for a
1241
1259
  // request the user is about to confirm here.
@@ -1397,12 +1415,16 @@ export class AccountDialogController {
1397
1415
  // recording it before the terminal branches means a poll that also
1398
1416
  // carries the approval still leaves an honest trail behind it.
1399
1417
  this.recordDeliveryProgress(status.pushSentAt, status.openedAt);
1400
- const purpose = status.purpose === 'oauth_authorization' ? 'oauth_authorization' : 'device_sign_in';
1418
+ const purpose =
1419
+ status.purpose === 'oauth_authorization' ? 'oauth_authorization' : 'device_sign_in';
1401
1420
  if (status.authorized && purpose === 'oauth_authorization') {
1402
1421
  // OAuth-bound sessions mint no sessionId on approval — they finalize
1403
1422
  // into an authorization code. The account dialog only starts device
1404
1423
  // sign-in today; stop rather than poll until expiry.
1405
- this.failSignIn('unsupported-flow', 'This sign-in flow cannot be completed here. Use the app\'s OAuth sign-in instead.');
1424
+ this.failSignIn(
1425
+ 'unsupported-flow',
1426
+ "This sign-in flow cannot be completed here. Use the app's OAuth sign-in instead.",
1427
+ );
1406
1428
  return;
1407
1429
  }
1408
1430
  if (status.authorized && status.sessionId) {
@@ -1420,7 +1442,11 @@ export class AccountDialogController {
1420
1442
  }
1421
1443
  } catch (error) {
1422
1444
  // Transient poll error — the next tick retries. Logged, never thrown.
1423
- logger.debug('[AccountDialogController] poll error (will retry)', { component: 'AccountDialogController' }, error);
1445
+ logger.debug(
1446
+ '[AccountDialogController] poll error (will retry)',
1447
+ { component: 'AccountDialogController' },
1448
+ error,
1449
+ );
1424
1450
  }
1425
1451
  if (this.signInToken === sessionToken) {
1426
1452
  this.scheduleNextPoll(sessionToken);
@@ -1445,7 +1471,11 @@ export class AccountDialogController {
1445
1471
  this.patchSignIn({ pushSentAt: nextPushSentAt, openedAt: nextOpenedAt });
1446
1472
  }
1447
1473
 
1448
- private async claimAndComplete(attempt: number, sessionId: string, sessionToken: string): Promise<void> {
1474
+ private async claimAndComplete(
1475
+ attempt: number,
1476
+ sessionId: string,
1477
+ sessionToken: string,
1478
+ ): Promise<void> {
1449
1479
  this.patchSignIn({ phase: 'authorized' });
1450
1480
  let claimed: {
1451
1481
  accessToken: string;
@@ -1466,7 +1496,10 @@ export class AccountDialogController {
1466
1496
  }
1467
1497
  if (!this.isCurrentAttempt(attempt)) return;
1468
1498
  if (!claimed?.accessToken || !claimed.user) {
1469
- this.failSignIn('claim-failed', 'Authorization succeeded but the session could not be claimed. Please try again.');
1499
+ this.failSignIn(
1500
+ 'claim-failed',
1501
+ 'Authorization succeeded but the session could not be claimed. Please try again.',
1502
+ );
1470
1503
  return;
1471
1504
  }
1472
1505
  // `SessionLoginResponse.user` is the minimal session-carried shape; the claim
@@ -1541,7 +1574,7 @@ export class AccountDialogController {
1541
1574
  this.signInToken = null;
1542
1575
  this.clearPollTimer();
1543
1576
  this.closeAuthSessionSocket();
1544
- // Terminal SUCCESS, not idle: the surface gets one honest frame to show
1577
+ // Terminal SUCCESS, not idle: the surface gets one honest frame to show
1545
1578
  // "Identity confirmed" before it closes. Cleared on the next view change.
1546
1579
  this.signIn = this.stampSignIn(COMPLETED_SIGN_IN_FACTS);
1547
1580
  this.view = 'accounts';
@@ -1617,7 +1650,11 @@ export class AccountDialogController {
1617
1650
  });
1618
1651
  } catch (error) {
1619
1652
  // Socket unavailable — the fallback poll still completes the flow.
1620
- logger.debug('[AccountDialogController] auth-session socket create failed (poll fallback)', { component: 'AccountDialogController' }, error);
1653
+ logger.debug(
1654
+ '[AccountDialogController] auth-session socket create failed (poll fallback)',
1655
+ { component: 'AccountDialogController' },
1656
+ error,
1657
+ );
1621
1658
  return;
1622
1659
  }
1623
1660
  const join = (): void => {
@@ -1625,7 +1662,11 @@ export class AccountDialogController {
1625
1662
  try {
1626
1663
  socket.emit('join', sessionToken);
1627
1664
  } catch (error) {
1628
- logger.debug('[AccountDialogController] auth-session join failed', { component: 'AccountDialogController' }, error);
1665
+ logger.debug(
1666
+ '[AccountDialogController] auth-session join failed',
1667
+ { component: 'AccountDialogController' },
1668
+ error,
1669
+ );
1629
1670
  }
1630
1671
  };
1631
1672
  socket.on('connect', join);
@@ -1648,7 +1689,11 @@ export class AccountDialogController {
1648
1689
  socket.off('connect');
1649
1690
  socket.disconnect();
1650
1691
  } catch (error) {
1651
- logger.debug('[AccountDialogController] auth-session socket close failed', { component: 'AccountDialogController' }, error);
1692
+ logger.debug(
1693
+ '[AccountDialogController] auth-session socket close failed',
1694
+ { component: 'AccountDialogController' },
1695
+ error,
1696
+ );
1652
1697
  }
1653
1698
  }
1654
1699
 
@@ -17,11 +17,7 @@
17
17
  */
18
18
 
19
19
  import { isOperatorSwitchTargetKind } from '@oxy.so/contracts';
20
- import type {
21
- AccountRelationship,
22
- AccountKind,
23
- AccountMember,
24
- } from '../api/accounts';
20
+ import type { AccountRelationship, AccountKind, AccountMember } from '../api/accounts';
25
21
 
26
22
  /**
27
23
  * Whether the caller can BECOME this account — the one question every account
@@ -55,9 +51,10 @@ import type {
55
51
  * Takes a structural subset rather than a whole {@link AccountNode} so a caller
56
52
  * holding an already-projected row can ask it too.
57
53
  */
58
- export function isSwitchTargetAccount(
59
- node: { kind?: AccountKind | null; relationship?: AccountRelationship },
60
- ): boolean {
54
+ export function isSwitchTargetAccount(node: {
55
+ kind?: AccountKind | null;
56
+ relationship?: AccountRelationship;
57
+ }): boolean {
61
58
  return node.relationship === 'self' || isOperatorSwitchTargetKind(node.kind);
62
59
  }
63
60
 
@@ -72,13 +69,11 @@ export function isSwitchTargetAccount(
72
69
  * API always resolves effective permissions for owned accounts, but test
73
70
  * fixtures and stale rows may omit the membership blob.
74
71
  */
75
- export function canSwitchIntoAccount(
76
- node: {
77
- kind?: AccountKind | null;
78
- relationship?: AccountRelationship;
79
- callerMembership?: AccountMember | null;
80
- },
81
- ): boolean {
72
+ export function canSwitchIntoAccount(node: {
73
+ kind?: AccountKind | null;
74
+ relationship?: AccountRelationship;
75
+ callerMembership?: AccountMember | null;
76
+ }): boolean {
82
77
  if (node.relationship === 'self') {
83
78
  return true;
84
79
  }
@@ -89,11 +89,14 @@ export interface AuthStateStore {
89
89
  clear(): Promise<void>;
90
90
  /** Durable local intent only: never authority or a mutation of an identity key. */
91
91
  isAutomaticIdentitySignInSuppressed?(): Promise<boolean>;
92
- setAutomaticIdentitySignInSuppressed?(suppressed: boolean, guard?: {
93
- /** Checked inside the native store queue, before mutating logout intent. */
94
- expectedState: PersistedAuthState;
95
- isCurrent: () => boolean;
96
- }): Promise<boolean>;
92
+ setAutomaticIdentitySignInSuppressed?(
93
+ suppressed: boolean,
94
+ guard?: {
95
+ /** Checked inside the native store queue, before mutating logout intent. */
96
+ expectedState: PersistedAuthState;
97
+ isCurrent: () => boolean;
98
+ },
99
+ ): Promise<boolean>;
97
100
  }
98
101
 
99
102
  export interface AuthStateWriteGuard {
@@ -101,11 +104,19 @@ export interface AuthStateWriteGuard {
101
104
  isCurrent: () => boolean;
102
105
  }
103
106
 
104
- export function matchesAuthState(actual: PersistedAuthState | null, expected: PersistedAuthState | null): boolean {
107
+ export function matchesAuthState(
108
+ actual: PersistedAuthState | null,
109
+ expected: PersistedAuthState | null,
110
+ ): boolean {
105
111
  if (!actual || !expected) return actual === expected;
106
- return actual.deviceId === expected.deviceId && actual.deviceSecret === expected.deviceSecret &&
107
- actual.sessionId === expected.sessionId && actual.userId === expected.userId &&
108
- actual.accessToken === expected.accessToken && actual.expiresAt === expected.expiresAt;
112
+ return (
113
+ actual.deviceId === expected.deviceId &&
114
+ actual.deviceSecret === expected.deviceSecret &&
115
+ actual.sessionId === expected.sessionId &&
116
+ actual.userId === expected.userId &&
117
+ actual.accessToken === expected.accessToken &&
118
+ actual.expiresAt === expected.expiresAt
119
+ );
109
120
  }
110
121
 
111
122
  /**
@@ -136,7 +147,8 @@ export interface NativeKeyValueStorage {
136
147
  * so upgrading users are not signed out; the next `save()` splits them apart.
137
148
  */
138
149
  export const AUTH_STATE_STORAGE_KEY = 'oxy.auth.v1';
139
- export const AUTOMATIC_IDENTITY_SIGNIN_SUPPRESSED_KEY = 'oxy.auth.automatic-identity-signin-suppressed.v1';
150
+ export const AUTOMATIC_IDENTITY_SIGNIN_SUPPRESSED_KEY =
151
+ 'oxy.auth.automatic-identity-signin-suppressed.v1';
140
152
 
141
153
  /**
142
154
  * Versioned BEST-EFFORT warm-token storage key. Holds the short-lived
@@ -277,7 +289,10 @@ function serializeWarmToken(state: PersistedAuthState): string | null {
277
289
  * present; when the warm key is ABSENT the token fields fall back to whatever
278
290
  * the durable blob carried — the pre-split combined `oxy.auth.v1` blob (BACK-COMPAT).
279
291
  */
280
- function composeState(durableRaw: string | null, warmRaw: string | null): PersistedAuthState | null {
292
+ function composeState(
293
+ durableRaw: string | null,
294
+ warmRaw: string | null,
295
+ ): PersistedAuthState | null {
281
296
  const state = deserialize(durableRaw);
282
297
  if (!state) {
283
298
  return null;
@@ -459,13 +474,17 @@ export function createNativeAuthStateStore(storage: NativeKeyValueStorage): Auth
459
474
  let pending: Promise<void> = Promise.resolve();
460
475
  const serialize = <T>(operation: () => Promise<T>): Promise<T> => {
461
476
  const result = pending.then(operation);
462
- pending = result.then(() => undefined, () => undefined);
477
+ pending = result.then(
478
+ () => undefined,
479
+ () => undefined,
480
+ );
463
481
  return result;
464
482
  };
465
483
  const readCurrent = async (): Promise<PersistedAuthState | null> => {
466
484
  if (sessionMirror !== undefined) return sessionMirror;
467
485
  const [durable, warm] = await Promise.all([
468
- storage.getItem(AUTH_STATE_STORAGE_KEY), storage.getItem(AUTH_STATE_TOKEN_STORAGE_KEY),
486
+ storage.getItem(AUTH_STATE_STORAGE_KEY),
487
+ storage.getItem(AUTH_STATE_TOKEN_STORAGE_KEY),
469
488
  ]);
470
489
  return composeState(durable, warm);
471
490
  };
@@ -513,75 +532,96 @@ export function createNativeAuthStateStore(storage: NativeKeyValueStorage): Auth
513
532
  return durablePersisted;
514
533
  };
515
534
  return {
516
- isAutomaticIdentitySignInSuppressed: () => serialize(async () => {
517
- if (suppressedMirror !== undefined) return suppressedMirror;
518
- try {
519
- return (await storage.getItem(AUTOMATIC_IDENTITY_SIGNIN_SUPPRESSED_KEY)) !== null;
520
- } catch {
521
- return true; // Unknown storage must not start a new key sign-in.
522
- }
523
- }),
524
- setAutomaticIdentitySignInSuppressed: (suppressed, guard) => serialize(async () => {
525
- if (guard) {
526
- let current = sessionMirror;
527
- if (current === undefined) {
528
- try {
529
- const [durable, warm] = await Promise.all([
530
- storage.getItem(AUTH_STATE_STORAGE_KEY), storage.getItem(AUTH_STATE_TOKEN_STORAGE_KEY),
531
- ]);
532
- current = composeState(durable, warm);
533
- } catch { suppressedMirror = true; return false; }
535
+ isAutomaticIdentitySignInSuppressed: () =>
536
+ serialize(async () => {
537
+ if (suppressedMirror !== undefined) return suppressedMirror;
538
+ try {
539
+ return (await storage.getItem(AUTOMATIC_IDENTITY_SIGNIN_SUPPRESSED_KEY)) !== null;
540
+ } catch {
541
+ return true; // Unknown storage must not start a new key sign-in.
534
542
  }
535
- const expected = guard.expectedState;
536
- if (!guard.isCurrent() || !current ||
537
- current.deviceId !== expected.deviceId || current.deviceSecret !== expected.deviceSecret ||
538
- current.sessionId !== expected.sessionId || current.userId !== expected.userId ||
539
- current.accessToken !== expected.accessToken || current.expiresAt !== expected.expiresAt) return false;
540
- }
541
- suppressedMirror = suppressed;
542
- try {
543
- if (suppressed) await storage.setItem(AUTOMATIC_IDENTITY_SIGNIN_SUPPRESSED_KEY, '1');
544
- else await storage.removeItem(AUTOMATIC_IDENTITY_SIGNIN_SUPPRESSED_KEY);
545
- const actual = await storage.getItem(AUTOMATIC_IDENTITY_SIGNIN_SUPPRESSED_KEY);
546
- const verified = suppressed ? actual === '1' : actual === null;
547
- if (!verified) suppressedMirror = true;
548
- return verified;
549
- } catch { suppressedMirror = true; return false; }
550
- }),
551
- load: () => serialize(async () => {
552
- if (sessionMirror !== undefined) {
553
- return sessionMirror;
554
- }
555
- try {
556
- const [durableRaw, warmRaw] = await Promise.all([
557
- storage.getItem(AUTH_STATE_STORAGE_KEY),
558
- storage.getItem(AUTH_STATE_TOKEN_STORAGE_KEY),
559
- ]);
560
- return composeState(durableRaw, warmRaw);
561
- } catch {
562
- return null;
563
- }
564
- }),
543
+ }),
544
+ setAutomaticIdentitySignInSuppressed: (suppressed, guard) =>
545
+ serialize(async () => {
546
+ if (guard) {
547
+ let current = sessionMirror;
548
+ if (current === undefined) {
549
+ try {
550
+ const [durable, warm] = await Promise.all([
551
+ storage.getItem(AUTH_STATE_STORAGE_KEY),
552
+ storage.getItem(AUTH_STATE_TOKEN_STORAGE_KEY),
553
+ ]);
554
+ current = composeState(durable, warm);
555
+ } catch {
556
+ suppressedMirror = true;
557
+ return false;
558
+ }
559
+ }
560
+ const expected = guard.expectedState;
561
+ if (
562
+ !guard.isCurrent() ||
563
+ !current ||
564
+ current.deviceId !== expected.deviceId ||
565
+ current.deviceSecret !== expected.deviceSecret ||
566
+ current.sessionId !== expected.sessionId ||
567
+ current.userId !== expected.userId ||
568
+ current.accessToken !== expected.accessToken ||
569
+ current.expiresAt !== expected.expiresAt
570
+ )
571
+ return false;
572
+ }
573
+ suppressedMirror = suppressed;
574
+ try {
575
+ if (suppressed) await storage.setItem(AUTOMATIC_IDENTITY_SIGNIN_SUPPRESSED_KEY, '1');
576
+ else await storage.removeItem(AUTOMATIC_IDENTITY_SIGNIN_SUPPRESSED_KEY);
577
+ const actual = await storage.getItem(AUTOMATIC_IDENTITY_SIGNIN_SUPPRESSED_KEY);
578
+ const verified = suppressed ? actual === '1' : actual === null;
579
+ if (!verified) suppressedMirror = true;
580
+ return verified;
581
+ } catch {
582
+ suppressedMirror = true;
583
+ return false;
584
+ }
585
+ }),
586
+ load: () =>
587
+ serialize(async () => {
588
+ if (sessionMirror !== undefined) {
589
+ return sessionMirror;
590
+ }
591
+ try {
592
+ const [durableRaw, warmRaw] = await Promise.all([
593
+ storage.getItem(AUTH_STATE_STORAGE_KEY),
594
+ storage.getItem(AUTH_STATE_TOKEN_STORAGE_KEY),
595
+ ]);
596
+ return composeState(durableRaw, warmRaw);
597
+ } catch {
598
+ return null;
599
+ }
600
+ }),
565
601
  save: (state) => serialize(() => persistState(state)),
566
- saveIfCurrent: (state, guard) => serialize(async () => {
567
- try {
568
- const current = await readCurrent();
569
- if (!guard.isCurrent() || !matchesAuthState(current, guard.expectedState)) return false;
570
- } catch { return false; }
571
- return persistState(state);
572
- }),
573
- clear: () => serialize(async () => {
574
- sessionMirror = null;
575
- try {
576
- await storage.removeItem(AUTH_STATE_STORAGE_KEY);
577
- } catch {
578
- // Non-fatal.
579
- }
580
- try {
581
- await storage.removeItem(AUTH_STATE_TOKEN_STORAGE_KEY);
582
- } catch {
583
- // Non-fatal.
584
- }
585
- }),
602
+ saveIfCurrent: (state, guard) =>
603
+ serialize(async () => {
604
+ try {
605
+ const current = await readCurrent();
606
+ if (!guard.isCurrent() || !matchesAuthState(current, guard.expectedState)) return false;
607
+ } catch {
608
+ return false;
609
+ }
610
+ return persistState(state);
611
+ }),
612
+ clear: () =>
613
+ serialize(async () => {
614
+ sessionMirror = null;
615
+ try {
616
+ await storage.removeItem(AUTH_STATE_STORAGE_KEY);
617
+ } catch {
618
+ // Non-fatal.
619
+ }
620
+ try {
621
+ await storage.removeItem(AUTH_STATE_TOKEN_STORAGE_KEY);
622
+ } catch {
623
+ // Non-fatal.
624
+ }
625
+ }),
586
626
  };
587
627
  }
@@ -179,9 +179,7 @@ export interface DevicePrincipalGroup {
179
179
  * and rendering them with nothing under them is how "sign out of this person"
180
180
  * stays reachable. Silently omitting them would strand the row.
181
181
  */
182
- export function projectDevicePrincipals(
183
- directory: DeviceDirectory | null,
184
- ): DevicePrincipalGroup[] {
182
+ export function projectDevicePrincipals(directory: DeviceDirectory | null): DevicePrincipalGroup[] {
185
183
  if (directory === null) {
186
184
  return [];
187
185
  }
@@ -135,7 +135,10 @@ function serialize(pin: IdentityPin): string | null {
135
135
  * definitive "no identity here" and therefore never a match — the caller clears
136
136
  * the pin rather than binding to an account whose key is gone.
137
137
  */
138
- export function identityPinMatches(pin: IdentityPin | null, localPublicKey: string | null): boolean {
138
+ export function identityPinMatches(
139
+ pin: IdentityPin | null,
140
+ localPublicKey: string | null,
141
+ ): boolean {
139
142
  if (!pin || !localPublicKey) {
140
143
  return false;
141
144
  }
@@ -208,11 +211,9 @@ export function createWebIdentityPinStore(): IdentityPinStore {
208
211
  save: async (pin) => {
209
212
  const json = serialize(pin);
210
213
  if (!json) {
211
- logger.error(
212
- '[identityPin] refusing to persist a malformed identity pin',
213
- undefined,
214
- { component: 'identityPin' },
215
- );
214
+ logger.error('[identityPin] refusing to persist a malformed identity pin', undefined, {
215
+ component: 'identityPin',
216
+ });
216
217
  return false;
217
218
  }
218
219
  sessionMirror = { publicKey: pin.publicKey.toLowerCase(), accountId: pin.accountId };
@@ -268,11 +269,9 @@ export function createNativeIdentityPinStore(storage: NativeKeyValueStorage): Id
268
269
  save: async (pin) => {
269
270
  const json = serialize(pin);
270
271
  if (!json) {
271
- logger.error(
272
- '[identityPin] refusing to persist a malformed identity pin',
273
- undefined,
274
- { component: 'identityPin' },
275
- );
272
+ logger.error('[identityPin] refusing to persist a malformed identity pin', undefined, {
273
+ component: 'identityPin',
274
+ });
276
275
  return false;
277
276
  }
278
277
  sessionMirror = { publicKey: pin.publicKey.toLowerCase(), accountId: pin.accountId };
@@ -159,7 +159,9 @@ export async function establishIdentitySession(args: {
159
159
  // The signer disagrees with the key we resolved a challenge for — the
160
160
  // identity changed mid-flight, or a custom signer was misconfigured. Refuse:
161
161
  // verifying under a different key would bind this client to another account.
162
- throw new Error('Identity sign-in aborted: the signing key does not match the device identity key');
162
+ throw new Error(
163
+ 'Identity sign-in aborted: the signing key does not match the device identity key',
164
+ );
163
165
  }
164
166
 
165
167
  // `signed.challenge` carries the SIGNATURE (mirrors `signChallenge`).