@oxy.so/core 1.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.
- package/LICENSE +202 -0
- package/NOTICE +16 -0
- package/README.md +221 -0
- package/dist/cjs/.tsbuildinfo +1 -0
- package/dist/cjs/HttpService.js +1270 -0
- package/dist/cjs/OxyServices.base.js +390 -0
- package/dist/cjs/OxyServices.errors.js +95 -0
- package/dist/cjs/OxyServices.js +72 -0
- package/dist/cjs/boot/sessionColdBoot.js +395 -0
- package/dist/cjs/constants/version.js +16 -0
- package/dist/cjs/crypto/aead.js +79 -0
- package/dist/cjs/crypto/ecdh.js +35 -0
- package/dist/cjs/crypto/identityMarker.js +255 -0
- package/dist/cjs/crypto/kdf.js +39 -0
- package/dist/cjs/crypto/keyManager.js +2234 -0
- package/dist/cjs/crypto/polyfill.js +131 -0
- package/dist/cjs/crypto/recoveryPhrase.js +242 -0
- package/dist/cjs/crypto/signatureService.js +273 -0
- package/dist/cjs/i18n/accountCategoryLabels.js +44 -0
- package/dist/cjs/i18n/accountRoleLabels.js +27 -0
- package/dist/cjs/i18n/index.js +90 -0
- package/dist/cjs/i18n/locales/ar-SA.json +282 -0
- package/dist/cjs/i18n/locales/ca-ES.json +282 -0
- package/dist/cjs/i18n/locales/de-DE.json +282 -0
- package/dist/cjs/i18n/locales/en-US.json +2161 -0
- package/dist/cjs/i18n/locales/es-ES.json +2161 -0
- package/dist/cjs/i18n/locales/fr-FR.json +282 -0
- package/dist/cjs/i18n/locales/it-IT.json +282 -0
- package/dist/cjs/i18n/locales/ja-JP.json +282 -0
- package/dist/cjs/i18n/locales/ko-KR.json +282 -0
- package/dist/cjs/i18n/locales/locales/ar-SA.json +282 -0
- package/dist/cjs/i18n/locales/locales/ca-ES.json +282 -0
- package/dist/cjs/i18n/locales/locales/de-DE.json +282 -0
- package/dist/cjs/i18n/locales/locales/en-US.json +2161 -0
- package/dist/cjs/i18n/locales/locales/es-ES.json +2161 -0
- package/dist/cjs/i18n/locales/locales/fr-FR.json +282 -0
- package/dist/cjs/i18n/locales/locales/it-IT.json +282 -0
- package/dist/cjs/i18n/locales/locales/ja-JP.json +282 -0
- package/dist/cjs/i18n/locales/locales/ko-KR.json +282 -0
- package/dist/cjs/i18n/locales/locales/pt-PT.json +282 -0
- package/dist/cjs/i18n/locales/locales/zh-CN.json +282 -0
- package/dist/cjs/i18n/locales/pt-PT.json +282 -0
- package/dist/cjs/i18n/locales/zh-CN.json +282 -0
- package/dist/cjs/i18n/reputationCategoryLabels.js +20 -0
- package/dist/cjs/i18n/trustTierLabels.js +19 -0
- package/dist/cjs/index.js +498 -0
- package/dist/cjs/inference/OxyInferenceClient.js +544 -0
- package/dist/cjs/logger/index.js +149 -0
- package/dist/cjs/mixins/OxyServices.accounts.js +555 -0
- package/dist/cjs/mixins/OxyServices.analytics.js +49 -0
- package/dist/cjs/mixins/OxyServices.appData.js +108 -0
- package/dist/cjs/mixins/OxyServices.assets.js +764 -0
- package/dist/cjs/mixins/OxyServices.auth.js +1254 -0
- package/dist/cjs/mixins/OxyServices.chains.js +73 -0
- package/dist/cjs/mixins/OxyServices.civic.js +611 -0
- package/dist/cjs/mixins/OxyServices.connectedApps.js +78 -0
- package/dist/cjs/mixins/OxyServices.contacts.js +50 -0
- package/dist/cjs/mixins/OxyServices.deviceBoot.js +191 -0
- package/dist/cjs/mixins/OxyServices.deviceTransfer.js +314 -0
- package/dist/cjs/mixins/OxyServices.devices.js +116 -0
- package/dist/cjs/mixins/OxyServices.features.js +335 -0
- package/dist/cjs/mixins/OxyServices.followGraph.js +221 -0
- package/dist/cjs/mixins/OxyServices.identity.js +461 -0
- package/dist/cjs/mixins/OxyServices.identityBackup.js +172 -0
- package/dist/cjs/mixins/OxyServices.inference.js +59 -0
- package/dist/cjs/mixins/OxyServices.language.js +125 -0
- package/dist/cjs/mixins/OxyServices.links.js +72 -0
- package/dist/cjs/mixins/OxyServices.location.js +43 -0
- package/dist/cjs/mixins/OxyServices.nodes.js +175 -0
- package/dist/cjs/mixins/OxyServices.notifications.js +63 -0
- package/dist/cjs/mixins/OxyServices.payment.js +158 -0
- package/dist/cjs/mixins/OxyServices.privacy.js +199 -0
- package/dist/cjs/mixins/OxyServices.reputation.js +289 -0
- package/dist/cjs/mixins/OxyServices.security.js +89 -0
- package/dist/cjs/mixins/OxyServices.store.js +266 -0
- package/dist/cjs/mixins/OxyServices.topics.js +137 -0
- package/dist/cjs/mixins/OxyServices.user.js +902 -0
- package/dist/cjs/mixins/OxyServices.utility.js +1098 -0
- package/dist/cjs/mixins/index.js +135 -0
- package/dist/cjs/mixins/mixinHelpers.js +53 -0
- package/dist/cjs/models/Topic.js +16 -0
- package/dist/cjs/models/interfaces.js +20 -0
- package/dist/cjs/models/session.js +2 -0
- package/dist/cjs/server/auth.js +167 -0
- package/dist/cjs/server/capabilityTicket.js +192 -0
- package/dist/cjs/server/cors.js +225 -0
- package/dist/cjs/server/index.js +97 -0
- package/dist/cjs/server/rateLimit.js +207 -0
- package/dist/cjs/server/safeFetch.js +458 -0
- package/dist/cjs/server/securityHeaders.js +377 -0
- package/dist/cjs/server/userInvalidation.js +156 -0
- package/dist/cjs/server/verifySecret.js +50 -0
- package/dist/cjs/session/SessionClient.js +865 -0
- package/dist/cjs/session/accountDelegationAccess.js +48 -0
- package/dist/cjs/session/accountDialogController.js +1131 -0
- package/dist/cjs/session/accountSwitchTargets.js +81 -0
- package/dist/cjs/session/authStateStore.js +404 -0
- package/dist/cjs/session/createSessionClient.js +32 -0
- package/dist/cjs/session/deviceDirectory.js +143 -0
- package/dist/cjs/session/deviceSwitcherRows.js +76 -0
- package/dist/cjs/session/identityPin.js +256 -0
- package/dist/cjs/session/identitySession.js +135 -0
- package/dist/cjs/session/projectSessionState.js +121 -0
- package/dist/cjs/session/refresh.js +398 -0
- package/dist/cjs/session/sessionClientHost.js +34 -0
- package/dist/cjs/session/sharedDeviceCredential.js +247 -0
- package/dist/cjs/session/socketLoader.js +55 -0
- package/dist/cjs/shared/utils/colorUtils.js +153 -0
- package/dist/cjs/shared/utils/errorUtils.js +183 -0
- package/dist/cjs/shared/utils/index.js +49 -0
- package/dist/cjs/shared/utils/networkUtils.js +183 -0
- package/dist/cjs/shared/utils/themeUtils.js +106 -0
- package/dist/cjs/utils/accountCacheSweep.js +80 -0
- package/dist/cjs/utils/accountUtils.js +140 -0
- package/dist/cjs/utils/activityId.js +6 -0
- package/dist/cjs/utils/apiUtils.js +91 -0
- package/dist/cjs/utils/asyncUtils.js +222 -0
- package/dist/cjs/utils/authHelpers.js +120 -0
- package/dist/cjs/utils/authWebUrl.js +19 -0
- package/dist/cjs/utils/avatarUtils.js +29 -0
- package/dist/cjs/utils/cache.js +235 -0
- package/dist/cjs/utils/cacheKey.js +85 -0
- package/dist/cjs/utils/coldBoot.js +122 -0
- package/dist/cjs/utils/commonsApproval.js +31 -0
- package/dist/cjs/utils/commonsDelivery.js +59 -0
- package/dist/cjs/utils/deviceManager.js +174 -0
- package/dist/cjs/utils/displayNamePolicyRanges.generated.js +60 -0
- package/dist/cjs/utils/edgeRegion.js +6 -0
- package/dist/cjs/utils/errorUtils.js +243 -0
- package/dist/cjs/utils/identityCacheSweep.js +97 -0
- package/dist/cjs/utils/languageUtils.js +224 -0
- package/dist/cjs/utils/oauthPkce.js +311 -0
- package/dist/cjs/utils/officialOrigins.js +64 -0
- package/dist/cjs/utils/oxyServiceEnvironment.js +19 -0
- package/dist/cjs/utils/platform.js +119 -0
- package/dist/cjs/utils/profileLinks.js +63 -0
- package/dist/cjs/utils/redactUrl.js +29 -0
- package/dist/cjs/utils/registrableApex.js +45 -0
- package/dist/cjs/utils/requestUtils.js +209 -0
- package/dist/cjs/utils/sessionUtils.js +181 -0
- package/dist/cjs/utils/textNormalization.js +158 -0
- package/dist/cjs/utils/userHandle.js +37 -0
- package/dist/cjs/utils/userIdentity.js +50 -0
- package/dist/cjs/utils/validationUtils.js +370 -0
- package/dist/cjs/utils/webauthnOrigin.js +51 -0
- package/dist/esm/.tsbuildinfo +1 -0
- package/dist/esm/HttpService.js +1266 -0
- package/dist/esm/OxyServices.base.js +386 -0
- package/dist/esm/OxyServices.errors.js +88 -0
- package/dist/esm/OxyServices.js +66 -0
- package/dist/esm/boot/sessionColdBoot.js +392 -0
- package/dist/esm/constants/version.js +13 -0
- package/dist/esm/crypto/aead.js +74 -0
- package/dist/esm/crypto/ecdh.js +32 -0
- package/dist/esm/crypto/identityMarker.js +248 -0
- package/dist/esm/crypto/kdf.js +36 -0
- package/dist/esm/crypto/keyManager.js +2227 -0
- package/dist/esm/crypto/polyfill.js +129 -0
- package/dist/esm/crypto/recoveryPhrase.js +238 -0
- package/dist/esm/crypto/signatureService.js +269 -0
- package/dist/esm/i18n/accountCategoryLabels.js +37 -0
- package/dist/esm/i18n/accountRoleLabels.js +20 -0
- package/dist/esm/i18n/index.js +83 -0
- package/dist/esm/i18n/locales/ar-SA.json +282 -0
- package/dist/esm/i18n/locales/ca-ES.json +282 -0
- package/dist/esm/i18n/locales/de-DE.json +282 -0
- package/dist/esm/i18n/locales/en-US.json +2161 -0
- package/dist/esm/i18n/locales/es-ES.json +2161 -0
- package/dist/esm/i18n/locales/fr-FR.json +282 -0
- package/dist/esm/i18n/locales/it-IT.json +282 -0
- package/dist/esm/i18n/locales/ja-JP.json +282 -0
- package/dist/esm/i18n/locales/ko-KR.json +282 -0
- package/dist/esm/i18n/locales/locales/ar-SA.json +282 -0
- package/dist/esm/i18n/locales/locales/ca-ES.json +282 -0
- package/dist/esm/i18n/locales/locales/de-DE.json +282 -0
- package/dist/esm/i18n/locales/locales/en-US.json +2161 -0
- package/dist/esm/i18n/locales/locales/es-ES.json +2161 -0
- package/dist/esm/i18n/locales/locales/fr-FR.json +282 -0
- package/dist/esm/i18n/locales/locales/it-IT.json +282 -0
- package/dist/esm/i18n/locales/locales/ja-JP.json +282 -0
- package/dist/esm/i18n/locales/locales/ko-KR.json +282 -0
- package/dist/esm/i18n/locales/locales/pt-PT.json +282 -0
- package/dist/esm/i18n/locales/locales/zh-CN.json +282 -0
- package/dist/esm/i18n/locales/pt-PT.json +282 -0
- package/dist/esm/i18n/locales/zh-CN.json +282 -0
- package/dist/esm/i18n/reputationCategoryLabels.js +13 -0
- package/dist/esm/i18n/trustTierLabels.js +12 -0
- package/dist/esm/index.js +254 -0
- package/dist/esm/inference/OxyInferenceClient.js +538 -0
- package/dist/esm/logger/index.js +140 -0
- package/dist/esm/mixins/OxyServices.accounts.js +543 -0
- package/dist/esm/mixins/OxyServices.analytics.js +46 -0
- package/dist/esm/mixins/OxyServices.appData.js +103 -0
- package/dist/esm/mixins/OxyServices.assets.js +761 -0
- package/dist/esm/mixins/OxyServices.auth.js +1247 -0
- package/dist/esm/mixins/OxyServices.chains.js +70 -0
- package/dist/esm/mixins/OxyServices.civic.js +605 -0
- package/dist/esm/mixins/OxyServices.connectedApps.js +75 -0
- package/dist/esm/mixins/OxyServices.contacts.js +47 -0
- package/dist/esm/mixins/OxyServices.deviceBoot.js +186 -0
- package/dist/esm/mixins/OxyServices.deviceTransfer.js +311 -0
- package/dist/esm/mixins/OxyServices.devices.js +113 -0
- package/dist/esm/mixins/OxyServices.features.js +332 -0
- package/dist/esm/mixins/OxyServices.followGraph.js +218 -0
- package/dist/esm/mixins/OxyServices.identity.js +457 -0
- package/dist/esm/mixins/OxyServices.identityBackup.js +169 -0
- package/dist/esm/mixins/OxyServices.inference.js +56 -0
- package/dist/esm/mixins/OxyServices.language.js +122 -0
- package/dist/esm/mixins/OxyServices.links.js +69 -0
- package/dist/esm/mixins/OxyServices.location.js +40 -0
- package/dist/esm/mixins/OxyServices.nodes.js +172 -0
- package/dist/esm/mixins/OxyServices.notifications.js +60 -0
- package/dist/esm/mixins/OxyServices.payment.js +155 -0
- package/dist/esm/mixins/OxyServices.privacy.js +196 -0
- package/dist/esm/mixins/OxyServices.reputation.js +286 -0
- package/dist/esm/mixins/OxyServices.security.js +86 -0
- package/dist/esm/mixins/OxyServices.store.js +263 -0
- package/dist/esm/mixins/OxyServices.topics.js +134 -0
- package/dist/esm/mixins/OxyServices.user.js +899 -0
- package/dist/esm/mixins/OxyServices.utility.js +1095 -0
- package/dist/esm/mixins/index.js +132 -0
- package/dist/esm/mixins/mixinHelpers.js +48 -0
- package/dist/esm/models/Topic.js +13 -0
- package/dist/esm/models/interfaces.js +17 -0
- package/dist/esm/models/session.js +1 -0
- package/dist/esm/server/auth.js +154 -0
- package/dist/esm/server/capabilityTicket.js +183 -0
- package/dist/esm/server/cors.js +220 -0
- package/dist/esm/server/index.js +47 -0
- package/dist/esm/server/rateLimit.js +201 -0
- package/dist/esm/server/safeFetch.js +447 -0
- package/dist/esm/server/securityHeaders.js +364 -0
- package/dist/esm/server/userInvalidation.js +152 -0
- package/dist/esm/server/verifySecret.js +47 -0
- package/dist/esm/session/SessionClient.js +861 -0
- package/dist/esm/session/accountDelegationAccess.js +45 -0
- package/dist/esm/session/accountDialogController.js +1126 -0
- package/dist/esm/session/accountSwitchTargets.js +77 -0
- package/dist/esm/session/authStateStore.js +398 -0
- package/dist/esm/session/createSessionClient.js +29 -0
- package/dist/esm/session/deviceDirectory.js +135 -0
- package/dist/esm/session/deviceSwitcherRows.js +72 -0
- package/dist/esm/session/identityPin.js +249 -0
- package/dist/esm/session/identitySession.js +131 -0
- package/dist/esm/session/projectSessionState.js +114 -0
- package/dist/esm/session/refresh.js +390 -0
- package/dist/esm/session/sessionClientHost.js +31 -0
- package/dist/esm/session/sharedDeviceCredential.js +239 -0
- package/dist/esm/session/socketLoader.js +19 -0
- package/dist/esm/shared/utils/colorUtils.js +143 -0
- package/dist/esm/shared/utils/errorUtils.js +170 -0
- package/dist/esm/shared/utils/index.js +15 -0
- package/dist/esm/shared/utils/networkUtils.js +173 -0
- package/dist/esm/shared/utils/themeUtils.js +98 -0
- package/dist/esm/utils/accountCacheSweep.js +75 -0
- package/dist/esm/utils/accountUtils.js +131 -0
- package/dist/esm/utils/activityId.js +2 -0
- package/dist/esm/utils/apiUtils.js +84 -0
- package/dist/esm/utils/asyncUtils.js +207 -0
- package/dist/esm/utils/authHelpers.js +111 -0
- package/dist/esm/utils/authWebUrl.js +16 -0
- package/dist/esm/utils/avatarUtils.js +26 -0
- package/dist/esm/utils/cache.js +227 -0
- package/dist/esm/utils/cacheKey.js +80 -0
- package/dist/esm/utils/coldBoot.js +119 -0
- package/dist/esm/utils/commonsApproval.js +27 -0
- package/dist/esm/utils/commonsDelivery.js +54 -0
- package/dist/esm/utils/deviceManager.js +170 -0
- package/dist/esm/utils/displayNamePolicyRanges.generated.js +57 -0
- package/dist/esm/utils/edgeRegion.js +2 -0
- package/dist/esm/utils/errorUtils.js +232 -0
- package/dist/esm/utils/identityCacheSweep.js +92 -0
- package/dist/esm/utils/languageUtils.js +212 -0
- package/dist/esm/utils/oauthPkce.js +297 -0
- package/dist/esm/utils/officialOrigins.js +59 -0
- package/dist/esm/utils/oxyServiceEnvironment.js +16 -0
- package/dist/esm/utils/platform.js +110 -0
- package/dist/esm/utils/profileLinks.js +60 -0
- package/dist/esm/utils/redactUrl.js +26 -0
- package/dist/esm/utils/registrableApex.js +42 -0
- package/dist/esm/utils/requestUtils.js +203 -0
- package/dist/esm/utils/sessionUtils.js +171 -0
- package/dist/esm/utils/textNormalization.js +154 -0
- package/dist/esm/utils/userHandle.js +33 -0
- package/dist/esm/utils/userIdentity.js +45 -0
- package/dist/esm/utils/validationUtils.js +350 -0
- package/dist/esm/utils/webauthnOrigin.js +48 -0
- package/dist/types/.tsbuildinfo +1 -0
- package/dist/types/HttpService.d.ts +413 -0
- package/dist/types/OxyServices.base.d.ts +187 -0
- package/dist/types/OxyServices.d.ts +139 -0
- package/dist/types/OxyServices.errors.d.ts +84 -0
- package/dist/types/boot/sessionColdBoot.d.ts +140 -0
- package/dist/types/constants/version.d.ts +13 -0
- package/dist/types/crypto/aead.d.ts +56 -0
- package/dist/types/crypto/ecdh.d.ts +29 -0
- package/dist/types/crypto/identityMarker.d.ts +94 -0
- package/dist/types/crypto/kdf.d.ts +25 -0
- package/dist/types/crypto/keyManager.d.ts +582 -0
- package/dist/types/crypto/polyfill.d.ts +25 -0
- package/dist/types/crypto/recoveryPhrase.d.ts +173 -0
- package/dist/types/crypto/signatureService.d.ts +136 -0
- package/dist/types/i18n/accountCategoryLabels.d.ts +34 -0
- package/dist/types/i18n/accountRoleLabels.d.ts +10 -0
- package/dist/types/i18n/index.d.ts +3 -0
- package/dist/types/i18n/reputationCategoryLabels.d.ts +10 -0
- package/dist/types/i18n/trustTierLabels.d.ts +9 -0
- package/dist/types/index.d.ts +145 -0
- package/dist/types/inference/OxyInferenceClient.d.ts +342 -0
- package/dist/types/logger/index.d.ts +104 -0
- package/dist/types/mixins/OxyServices.accounts.d.ts +868 -0
- package/dist/types/mixins/OxyServices.analytics.d.ts +72 -0
- package/dist/types/mixins/OxyServices.appData.d.ts +108 -0
- package/dist/types/mixins/OxyServices.assets.d.ts +329 -0
- package/dist/types/mixins/OxyServices.auth.d.ts +908 -0
- package/dist/types/mixins/OxyServices.chains.d.ts +156 -0
- package/dist/types/mixins/OxyServices.civic.d.ts +510 -0
- package/dist/types/mixins/OxyServices.connectedApps.d.ts +169 -0
- package/dist/types/mixins/OxyServices.contacts.d.ts +100 -0
- package/dist/types/mixins/OxyServices.deviceBoot.d.ts +185 -0
- package/dist/types/mixins/OxyServices.deviceTransfer.d.ts +149 -0
- package/dist/types/mixins/OxyServices.devices.d.ts +103 -0
- package/dist/types/mixins/OxyServices.features.d.ts +250 -0
- package/dist/types/mixins/OxyServices.followGraph.d.ts +223 -0
- package/dist/types/mixins/OxyServices.identity.d.ts +351 -0
- package/dist/types/mixins/OxyServices.identityBackup.d.ts +129 -0
- package/dist/types/mixins/OxyServices.inference.d.ts +95 -0
- package/dist/types/mixins/OxyServices.language.d.ts +87 -0
- package/dist/types/mixins/OxyServices.links.d.ts +104 -0
- package/dist/types/mixins/OxyServices.location.d.ts +70 -0
- package/dist/types/mixins/OxyServices.nodes.d.ts +198 -0
- package/dist/types/mixins/OxyServices.notifications.d.ts +143 -0
- package/dist/types/mixins/OxyServices.payment.d.ts +117 -0
- package/dist/types/mixins/OxyServices.privacy.d.ts +146 -0
- package/dist/types/mixins/OxyServices.reputation.d.ts +202 -0
- package/dist/types/mixins/OxyServices.security.d.ts +84 -0
- package/dist/types/mixins/OxyServices.store.d.ts +334 -0
- package/dist/types/mixins/OxyServices.topics.d.ts +110 -0
- package/dist/types/mixins/OxyServices.user.d.ts +558 -0
- package/dist/types/mixins/OxyServices.utility.d.ts +393 -0
- package/dist/types/mixins/index.d.ts +87 -0
- package/dist/types/mixins/mixinHelpers.d.ts +31 -0
- package/dist/types/models/Topic.d.ts +42 -0
- package/dist/types/models/interfaces.d.ts +675 -0
- package/dist/types/models/session.d.ts +51 -0
- package/dist/types/server/auth.d.ts +130 -0
- package/dist/types/server/capabilityTicket.d.ts +41 -0
- package/dist/types/server/cors.d.ts +98 -0
- package/dist/types/server/index.d.ts +35 -0
- package/dist/types/server/rateLimit.d.ts +40 -0
- package/dist/types/server/safeFetch.d.ts +137 -0
- package/dist/types/server/securityHeaders.d.ts +221 -0
- package/dist/types/server/userInvalidation.d.ts +114 -0
- package/dist/types/server/verifySecret.d.ts +29 -0
- package/dist/types/session/SessionClient.d.ts +418 -0
- package/dist/types/session/accountDelegationAccess.d.ts +40 -0
- package/dist/types/session/accountDialogController.d.ts +648 -0
- package/dist/types/session/accountSwitchTargets.d.ts +70 -0
- package/dist/types/session/authStateStore.d.ts +153 -0
- package/dist/types/session/createSessionClient.d.ts +30 -0
- package/dist/types/session/deviceDirectory.d.ts +182 -0
- package/dist/types/session/deviceSwitcherRows.d.ts +92 -0
- package/dist/types/session/identityPin.d.ts +87 -0
- package/dist/types/session/identitySession.d.ts +105 -0
- package/dist/types/session/projectSessionState.d.ts +74 -0
- package/dist/types/session/refresh.d.ts +179 -0
- package/dist/types/session/sessionClientHost.d.ts +18 -0
- package/dist/types/session/sharedDeviceCredential.d.ts +202 -0
- package/dist/types/session/socketLoader.d.ts +11 -0
- package/dist/types/shared/utils/colorUtils.d.ts +104 -0
- package/dist/types/shared/utils/errorUtils.d.ts +97 -0
- package/dist/types/shared/utils/index.d.ts +13 -0
- package/dist/types/shared/utils/networkUtils.d.ts +139 -0
- package/dist/types/shared/utils/themeUtils.d.ts +90 -0
- package/dist/types/utils/accountCacheSweep.d.ts +75 -0
- package/dist/types/utils/accountUtils.d.ts +100 -0
- package/dist/types/utils/activityId.d.ts +2 -0
- package/dist/types/utils/apiUtils.d.ts +95 -0
- package/dist/types/utils/asyncUtils.d.ts +62 -0
- package/dist/types/utils/authHelpers.d.ts +58 -0
- package/dist/types/utils/authWebUrl.d.ts +16 -0
- package/dist/types/utils/avatarUtils.d.ts +16 -0
- package/dist/types/utils/cache.d.ts +130 -0
- package/dist/types/utils/cacheKey.d.ts +66 -0
- package/dist/types/utils/coldBoot.d.ts +131 -0
- package/dist/types/utils/commonsApproval.d.ts +13 -0
- package/dist/types/utils/commonsDelivery.d.ts +105 -0
- package/dist/types/utils/deviceManager.d.ts +65 -0
- package/dist/types/utils/displayNamePolicyRanges.generated.d.ts +57 -0
- package/dist/types/utils/edgeRegion.d.ts +2 -0
- package/dist/types/utils/errorUtils.d.ts +125 -0
- package/dist/types/utils/identityCacheSweep.d.ts +80 -0
- package/dist/types/utils/languageUtils.d.ts +100 -0
- package/dist/types/utils/oauthPkce.d.ts +157 -0
- package/dist/types/utils/officialOrigins.d.ts +13 -0
- package/dist/types/utils/oxyServiceEnvironment.d.ts +17 -0
- package/dist/types/utils/platform.d.ts +49 -0
- package/dist/types/utils/profileLinks.d.ts +44 -0
- package/dist/types/utils/redactUrl.d.ts +17 -0
- package/dist/types/utils/registrableApex.d.ts +27 -0
- package/dist/types/utils/requestUtils.d.ts +123 -0
- package/dist/types/utils/sessionUtils.d.ts +54 -0
- package/dist/types/utils/textNormalization.d.ts +103 -0
- package/dist/types/utils/userHandle.d.ts +26 -0
- package/dist/types/utils/userIdentity.d.ts +21 -0
- package/dist/types/utils/validationUtils.d.ts +268 -0
- package/dist/types/utils/webauthnOrigin.d.ts +32 -0
- package/package.json +168 -0
- package/src/HttpService.ts +1553 -0
- package/src/OxyServices.base.ts +478 -0
- package/src/OxyServices.errors.ts +122 -0
- package/src/OxyServices.ts +172 -0
- package/src/__tests__/authSocket.test.ts +96 -0
- package/src/__tests__/httpServiceActivityId.test.ts +49 -0
- package/src/__tests__/httpServiceAuthSelfAwait.test.ts +108 -0
- package/src/__tests__/httpServiceCache.test.ts +318 -0
- package/src/__tests__/httpServiceCsrf.test.ts +235 -0
- package/src/__tests__/httpServiceFormEncoded.test.ts +142 -0
- package/src/__tests__/httpServiceQueueDeadlock.test.ts +128 -0
- package/src/__tests__/httpServiceUnwrapEnvelope.test.ts +115 -0
- package/src/__tests__/inSessionRefresh.test.ts +260 -0
- package/src/__tests__/linkedClient.test.ts +370 -0
- package/src/__tests__/parseHttpErrorBody.test.ts +116 -0
- package/src/__tests__/serverValueImportsDeclared.test.ts +120 -0
- package/src/__tests__/userIdentity.test.ts +143 -0
- package/src/boot/__tests__/sessionColdBoot.identity.test.ts +393 -0
- package/src/boot/__tests__/sessionColdBoot.sharedDevice.test.ts +325 -0
- package/src/boot/__tests__/sessionColdBoot.test.ts +570 -0
- package/src/boot/sessionColdBoot.ts +529 -0
- package/src/constants/version.ts +15 -0
- package/src/crypto/__tests__/backupMaterial.test.ts +86 -0
- package/src/crypto/__tests__/cryptoPrimitives.test.ts +226 -0
- package/src/crypto/__tests__/identityMocks.ts +125 -0
- package/src/crypto/__tests__/keyManager.atomicity.test.ts +272 -0
- package/src/crypto/__tests__/keyManager.cacheSafety.test.ts +175 -0
- package/src/crypto/__tests__/keyManager.identityStatus.test.ts +217 -0
- package/src/crypto/__tests__/keyManager.recoveryLadder.test.ts +179 -0
- package/src/crypto/__tests__/keyManager.recoveryMnemonic.test.ts +138 -0
- package/src/crypto/__tests__/keyManager.storageMigration.test.ts +227 -0
- package/src/crypto/__tests__/keyManager.test.ts +329 -0
- package/src/crypto/__tests__/polyfill.test.ts +109 -0
- package/src/crypto/__tests__/recoveryPhrase.test.ts +61 -0
- package/src/crypto/__tests__/scopedSeed.test.ts +126 -0
- package/src/crypto/__tests__/signChallengeShared.test.ts +63 -0
- package/src/crypto/__tests__/signedRecord.test.ts +230 -0
- package/src/crypto/aead.ts +97 -0
- package/src/crypto/ecdh.ts +37 -0
- package/src/crypto/identityMarker.ts +291 -0
- package/src/crypto/kdf.ts +43 -0
- package/src/crypto/keyManager.ts +2607 -0
- package/src/crypto/polyfill.ts +143 -0
- package/src/crypto/recoveryPhrase.ts +337 -0
- package/src/crypto/signatureService.ts +350 -0
- package/src/i18n/__tests__/accountCategoryLabels.test.ts +62 -0
- package/src/i18n/__tests__/accountRoleLabels.test.ts +54 -0
- package/src/i18n/__tests__/reputationCategoryLabels.test.ts +56 -0
- package/src/i18n/__tests__/trustTierLabels.test.ts +47 -0
- package/src/i18n/accountCategoryLabels.ts +44 -0
- package/src/i18n/accountRoleLabels.ts +26 -0
- package/src/i18n/index.ts +88 -0
- package/src/i18n/locales/ar-SA.json +282 -0
- package/src/i18n/locales/ca-ES.json +282 -0
- package/src/i18n/locales/de-DE.json +282 -0
- package/src/i18n/locales/en-US.json +2161 -0
- package/src/i18n/locales/es-ES.json +2161 -0
- package/src/i18n/locales/fr-FR.json +282 -0
- package/src/i18n/locales/it-IT.json +282 -0
- package/src/i18n/locales/ja-JP.json +282 -0
- package/src/i18n/locales/ko-KR.json +282 -0
- package/src/i18n/locales/pt-PT.json +282 -0
- package/src/i18n/locales/zh-CN.json +282 -0
- package/src/i18n/reputationCategoryLabels.ts +20 -0
- package/src/i18n/trustTierLabels.ts +18 -0
- package/src/index.ts +881 -0
- package/src/inference/OxyInferenceClient.ts +888 -0
- package/src/inference/__tests__/OxyInferenceClient.test.ts +834 -0
- package/src/logger/__tests__/logger.test.ts +207 -0
- package/src/logger/index.ts +217 -0
- package/src/mixins/OxyServices.accounts.ts +1330 -0
- package/src/mixins/OxyServices.analytics.ts +53 -0
- package/src/mixins/OxyServices.appData.ts +158 -0
- package/src/mixins/OxyServices.assets.ts +861 -0
- package/src/mixins/OxyServices.auth.ts +1858 -0
- package/src/mixins/OxyServices.chains.ts +134 -0
- package/src/mixins/OxyServices.civic.ts +945 -0
- package/src/mixins/OxyServices.connectedApps.ts +174 -0
- package/src/mixins/OxyServices.contacts.ts +73 -0
- package/src/mixins/OxyServices.deviceBoot.ts +210 -0
- package/src/mixins/OxyServices.deviceTransfer.ts +393 -0
- package/src/mixins/OxyServices.devices.ts +120 -0
- package/src/mixins/OxyServices.features.ts +454 -0
- package/src/mixins/OxyServices.followGraph.ts +293 -0
- package/src/mixins/OxyServices.identity.ts +705 -0
- package/src/mixins/OxyServices.identityBackup.ts +250 -0
- package/src/mixins/OxyServices.inference.ts +57 -0
- package/src/mixins/OxyServices.language.ts +144 -0
- package/src/mixins/OxyServices.links.ts +107 -0
- package/src/mixins/OxyServices.location.ts +46 -0
- package/src/mixins/OxyServices.nodes.ts +337 -0
- package/src/mixins/OxyServices.notifications.ts +142 -0
- package/src/mixins/OxyServices.payment.ts +163 -0
- package/src/mixins/OxyServices.privacy.ts +219 -0
- package/src/mixins/OxyServices.reputation.ts +436 -0
- package/src/mixins/OxyServices.security.ts +110 -0
- package/src/mixins/OxyServices.store.ts +585 -0
- package/src/mixins/OxyServices.topics.ts +169 -0
- package/src/mixins/OxyServices.user.ts +1137 -0
- package/src/mixins/OxyServices.utility.ts +1370 -0
- package/src/mixins/__tests__/OxyServices.civic.test.ts +1095 -0
- package/src/mixins/__tests__/OxyServices.deviceBoot.test.ts +124 -0
- package/src/mixins/__tests__/OxyServices.deviceTransfer.test.ts +271 -0
- package/src/mixins/__tests__/OxyServices.identity.test.ts +376 -0
- package/src/mixins/__tests__/OxyServices.links.test.ts +154 -0
- package/src/mixins/__tests__/OxyServices.nodes.test.ts +341 -0
- package/src/mixins/__tests__/OxyServices.rotateKey.test.ts +276 -0
- package/src/mixins/__tests__/OxyServices.serviceAssetMetadata.test.ts +192 -0
- package/src/mixins/__tests__/OxyServices.serviceAssetMetadataBySha.test.ts +135 -0
- package/src/mixins/__tests__/accounts.test.ts +727 -0
- package/src/mixins/__tests__/appData.test.ts +203 -0
- package/src/mixins/__tests__/assetCredentials.test.ts +47 -0
- package/src/mixins/__tests__/assetUpload.test.ts +191 -0
- package/src/mixins/__tests__/backgroundCredential.httpIntegration.test.ts +135 -0
- package/src/mixins/__tests__/chains.test.ts +113 -0
- package/src/mixins/__tests__/commonsSignIn.test.ts +1144 -0
- package/src/mixins/__tests__/connectedApps.test.ts +123 -0
- package/src/mixins/__tests__/deviceTokenMintPinned.test.ts +113 -0
- package/src/mixins/__tests__/discoveryErrorHandling.test.ts +266 -0
- package/src/mixins/__tests__/followCacheInvalidation.test.ts +173 -0
- package/src/mixins/__tests__/followGraph.test.ts +147 -0
- package/src/mixins/__tests__/followGraphPagination.test.ts +271 -0
- package/src/mixins/__tests__/getFileDownloadUrl.test.ts +346 -0
- package/src/mixins/__tests__/getFollowStatuses.test.ts +95 -0
- package/src/mixins/__tests__/getUsersByIds.test.ts +149 -0
- package/src/mixins/__tests__/identityBackup.test.ts +315 -0
- package/src/mixins/__tests__/identityWriteCacheInvalidation.test.ts +407 -0
- package/src/mixins/__tests__/inferenceFactory.test.ts +58 -0
- package/src/mixins/__tests__/onTokensChanged.test.ts +129 -0
- package/src/mixins/__tests__/preSessionSkipAuth.test.ts +284 -0
- package/src/mixins/__tests__/privacyCacheInvalidation.test.ts +155 -0
- package/src/mixins/__tests__/reputation.test.ts +522 -0
- package/src/mixins/__tests__/serviceAuth.test.ts +832 -0
- package/src/mixins/__tests__/serviceTokenJwks.test.ts +208 -0
- package/src/mixins/__tests__/store.test.ts +304 -0
- package/src/mixins/__tests__/topics.test.ts +156 -0
- package/src/mixins/__tests__/userReadCacheBypass.test.ts +129 -0
- package/src/mixins/__tests__/userTokenAuth.test.ts +746 -0
- package/src/mixins/__tests__/verifyChallenge.test.ts +130 -0
- package/src/mixins/__tests__/verifyServiceActingAs.test.ts +167 -0
- package/src/mixins/__tests__/webauthnAuth.test.ts +205 -0
- package/src/mixins/index.ts +201 -0
- package/src/mixins/mixinHelpers.ts +69 -0
- package/src/models/Topic.ts +46 -0
- package/src/models/interfaces.ts +786 -0
- package/src/models/session.ts +55 -0
- package/src/server/__tests__/auth.test.ts +78 -0
- package/src/server/__tests__/capabilityTicket.test.ts +111 -0
- package/src/server/__tests__/cors.socket.test.ts +225 -0
- package/src/server/__tests__/cors.test.ts +158 -0
- package/src/server/__tests__/rateLimit.test.ts +257 -0
- package/src/server/__tests__/safeFetch.test.ts +232 -0
- package/src/server/__tests__/securityHeaders.test.ts +348 -0
- package/src/server/__tests__/serviceTokenAttribution.test.ts +396 -0
- package/src/server/__tests__/userInvalidation.test.ts +203 -0
- package/src/server/__tests__/verifySecret.test.ts +40 -0
- package/src/server/auth.ts +271 -0
- package/src/server/capabilityTicket.ts +258 -0
- package/src/server/cors.ts +277 -0
- package/src/server/index.ts +136 -0
- package/src/server/rateLimit.ts +313 -0
- package/src/server/safeFetch.ts +586 -0
- package/src/server/securityHeaders.ts +467 -0
- package/src/server/userInvalidation.ts +199 -0
- package/src/server/verifySecret.ts +52 -0
- package/src/session/SessionClient.ts +1001 -0
- package/src/session/__tests__/SessionClient.additive.test.ts +153 -0
- package/src/session/__tests__/SessionClient.broadcastChannel.test.ts +114 -0
- package/src/session/__tests__/SessionClient.diagnostics.test.ts +91 -0
- package/src/session/__tests__/SessionClient.directory.test.ts +688 -0
- package/src/session/__tests__/SessionClient.httpIntegration.test.ts +65 -0
- package/src/session/__tests__/SessionClient.identityPin.test.ts +175 -0
- package/src/session/__tests__/SessionClient.rest.test.ts +119 -0
- package/src/session/__tests__/SessionClient.serverEvents.test.ts +72 -0
- package/src/session/__tests__/SessionClient.socket.test.ts +202 -0
- package/src/session/__tests__/SessionClient.socketFactory.test.ts +81 -0
- package/src/session/__tests__/SessionClient.state.test.ts +78 -0
- package/src/session/__tests__/SessionClient.switchTokenOrder.test.ts +170 -0
- package/src/session/__tests__/accountDelegationAccess.test.ts +104 -0
- package/src/session/__tests__/accountDialogController.test.ts +1969 -0
- package/src/session/__tests__/accountDialogShape.test.ts +118 -0
- package/src/session/__tests__/accountSwitchTargets.test.ts +137 -0
- package/src/session/__tests__/authStateStore.test.ts +451 -0
- package/src/session/__tests__/deviceDirectory.test.ts +422 -0
- package/src/session/__tests__/deviceSwitcherRows.test.ts +223 -0
- package/src/session/__tests__/identityPin.test.ts +131 -0
- package/src/session/__tests__/identitySession.test.ts +194 -0
- package/src/session/__tests__/projectSessionState.test.ts +105 -0
- package/src/session/__tests__/refresh.identityPin.test.ts +307 -0
- package/src/session/__tests__/refresh.test.ts +443 -0
- package/src/session/__tests__/sessionIntegration.test.ts +209 -0
- package/src/session/__tests__/sharedDeviceCredential.test.ts +300 -0
- package/src/session/accountDelegationAccess.ts +72 -0
- package/src/session/accountDialogController.ts +1494 -0
- package/src/session/accountSwitchTargets.ts +93 -0
- package/src/session/authStateStore.ts +502 -0
- package/src/session/createSessionClient.ts +39 -0
- package/src/session/deviceDirectory.ts +269 -0
- package/src/session/deviceSwitcherRows.ts +145 -0
- package/src/session/identityPin.ts +310 -0
- package/src/session/identitySession.ts +206 -0
- package/src/session/projectSessionState.ts +137 -0
- package/src/session/refresh.ts +517 -0
- package/src/session/sessionClientHost.ts +39 -0
- package/src/session/sharedDeviceCredential.ts +349 -0
- package/src/session/socketLoader.ts +30 -0
- package/src/shared/utils/colorUtils.ts +155 -0
- package/src/shared/utils/errorUtils.ts +181 -0
- package/src/shared/utils/index.ts +59 -0
- package/src/shared/utils/networkUtils.ts +248 -0
- package/src/shared/utils/themeUtils.ts +115 -0
- package/src/types/buffer.d.ts +97 -0
- package/src/types/expo-crypto.d.ts +16 -0
- package/src/types/expo-secure-store.d.ts +17 -0
- package/src/utils/__tests__/accountUtils.test.ts +151 -0
- package/src/utils/__tests__/activityId.test.ts +45 -0
- package/src/utils/__tests__/asyncUtils.test.ts +187 -0
- package/src/utils/__tests__/authWebUrl.test.ts +15 -0
- package/src/utils/__tests__/cacheKey.test.ts +0 -0
- package/src/utils/__tests__/coldBoot.test.ts +496 -0
- package/src/utils/__tests__/commonsApproval.test.ts +60 -0
- package/src/utils/__tests__/identityCacheSweep.test.ts +151 -0
- package/src/utils/__tests__/languageUtils.test.ts +219 -0
- package/src/utils/__tests__/oauthPkce.test.ts +272 -0
- package/src/utils/__tests__/officialOrigins.test.ts +31 -0
- package/src/utils/__tests__/oxyServiceEnvironment.test.ts +7 -0
- package/src/utils/__tests__/profileLinks.test.ts +180 -0
- package/src/utils/__tests__/redactUrl.test.ts +33 -0
- package/src/utils/__tests__/registrableApex.test.ts +62 -0
- package/src/utils/__tests__/textNormalization.test.ts +205 -0
- package/src/utils/__tests__/validationUtils.test.ts +703 -0
- package/src/utils/__tests__/webauthnOrigin.test.ts +83 -0
- package/src/utils/accountCacheSweep.ts +93 -0
- package/src/utils/accountUtils.ts +194 -0
- package/src/utils/activityId.ts +2 -0
- package/src/utils/apiUtils.ts +137 -0
- package/src/utils/asyncUtils.ts +280 -0
- package/src/utils/authHelpers.ts +148 -0
- package/src/utils/authWebUrl.ts +17 -0
- package/src/utils/avatarUtils.ts +39 -0
- package/src/utils/cache.ts +273 -0
- package/src/utils/cacheKey.ts +93 -0
- package/src/utils/coldBoot.ts +218 -0
- package/src/utils/commonsApproval.ts +39 -0
- package/src/utils/commonsDelivery.ts +132 -0
- package/src/utils/deviceManager.ts +203 -0
- package/src/utils/displayNamePolicyRanges.generated.ts +69 -0
- package/src/utils/edgeRegion.ts +2 -0
- package/src/utils/errorUtils.ts +352 -0
- package/src/utils/identityCacheSweep.ts +104 -0
- package/src/utils/languageUtils.ts +256 -0
- package/src/utils/oauthPkce.ts +392 -0
- package/src/utils/officialOrigins.ts +62 -0
- package/src/utils/oxyServiceEnvironment.ts +17 -0
- package/src/utils/platform.ts +139 -0
- package/src/utils/profileLinks.ts +91 -0
- package/src/utils/redactUrl.ts +28 -0
- package/src/utils/registrableApex.ts +41 -0
- package/src/utils/requestUtils.ts +237 -0
- package/src/utils/sessionUtils.ts +206 -0
- package/src/utils/textNormalization.ts +162 -0
- package/src/utils/userHandle.ts +49 -0
- package/src/utils/userIdentity.ts +60 -0
- package/src/utils/validationUtils.ts +390 -0
- package/src/utils/webauthnOrigin.ts +52 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The shared native DeviceSession credential — one device, one session, many apps.
|
|
3
|
+
*
|
|
4
|
+
* ## What this is, and what it deliberately is NOT
|
|
5
|
+
*
|
|
6
|
+
* Two different secrets can make a native Oxy app boot signed in, and conflating
|
|
7
|
+
* them is the bug this module exists to end:
|
|
8
|
+
*
|
|
9
|
+
* Commons private identity key → identity and signed approval ONLY. It is
|
|
10
|
+
* self-custody, irreplaceable, and must never
|
|
11
|
+
* become the general app session transport.
|
|
12
|
+
* Shared DeviceSession credential → THIS module. An ordinary `deviceId` +
|
|
13
|
+
* `deviceSecret` pair that restores ordinary
|
|
14
|
+
* official apps, follows the device's active
|
|
15
|
+
* context, and is individually rotatable and
|
|
16
|
+
* revocable server-side.
|
|
17
|
+
*
|
|
18
|
+
* An ordinary app needs the second one. Handing it the first — which is what the
|
|
19
|
+
* `shared-key-signin` lane does today — gives every sibling app the ability to
|
|
20
|
+
* sign as the user's cryptographic identity to obtain something as mundane as a
|
|
21
|
+
* session. That lane stays as a recovery/compatibility path; this one supersedes
|
|
22
|
+
* it for the ordinary case.
|
|
23
|
+
*
|
|
24
|
+
* ## Why sharing one credential is safe against the server
|
|
25
|
+
*
|
|
26
|
+
* `POST /session/device/token` does NOT rotate: the response echoes the presented
|
|
27
|
+
* secret back as `nextDeviceSecret` precisely so several first-party apps sharing
|
|
28
|
+
* one `DeviceSession` can refresh concurrently without invalidating one another.
|
|
29
|
+
* So N apps holding one credential is a supported server state, not a race — and
|
|
30
|
+
* because they then share ONE `DeviceSession`, they automatically share its
|
|
31
|
+
* `activeContextId` and its token-free `session_state` broadcasts.
|
|
32
|
+
*
|
|
33
|
+
* ## The one safety rule everything here is built around
|
|
34
|
+
*
|
|
35
|
+
* A read that FAILED and a slot that is EMPTY must never be the same value. The
|
|
36
|
+
* empty answer authorises writes (seed the slot); the failed answer must
|
|
37
|
+
* authorise nothing. {@link SharedDeviceCredentialRead} keeps them apart at the
|
|
38
|
+
* type level, and every decision below fails closed on anything that is not a
|
|
39
|
+
* positive `absent`/`present`.
|
|
40
|
+
*
|
|
41
|
+
* Platform-agnostic: the actual keychain / keystore access is injected as a
|
|
42
|
+
* {@link SharedDeviceCredentialStore} by `@oxy.so/services`. ESM-safe, no
|
|
43
|
+
* `require()`, no react/react-native/expo imports.
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
import { logger } from '../logger';
|
|
47
|
+
import type { AuthStateStore, PersistedAuthState } from './authStateStore';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* The zero-cookie device credential, as shared between apps. Exactly the pair
|
|
51
|
+
* `POST /session/device/token` takes — nothing else travels through the shared
|
|
52
|
+
* slot: no access token, no account id, no user id, no identity key.
|
|
53
|
+
*/
|
|
54
|
+
export interface SharedDeviceCredential {
|
|
55
|
+
deviceId: string;
|
|
56
|
+
deviceSecret: string;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The outcome of reading the shared slot. FOUR states, and the distinction
|
|
61
|
+
* between the last two is load-bearing:
|
|
62
|
+
*
|
|
63
|
+
* - `present` — a well-formed credential was read.
|
|
64
|
+
* - `absent` — the read SUCCEEDED and the slot is empty. The only state that
|
|
65
|
+
* may authorise seeding the slot.
|
|
66
|
+
* - `unavailable` — the read failed (keychain locked, keystore unreadable, the
|
|
67
|
+
* bridge returned something unrecognisable). Authorises
|
|
68
|
+
* nothing: not adoption, and above all not a write.
|
|
69
|
+
* - `unsupported` — this build has no shared slot at all (web, or a native app
|
|
70
|
+
* without the module linked). Not an error; simply means the
|
|
71
|
+
* app keeps its own per-app credential.
|
|
72
|
+
*/
|
|
73
|
+
export type SharedDeviceCredentialRead =
|
|
74
|
+
| { state: 'present'; credential: SharedDeviceCredential }
|
|
75
|
+
| { state: 'absent' }
|
|
76
|
+
| { state: 'unavailable'; cause: unknown }
|
|
77
|
+
| { state: 'unsupported' };
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The platform seam. `@oxy.so/services` implements this over the iOS Keychain
|
|
81
|
+
* Access Group (a dedicated `keychainService`) or the Android signature-protected
|
|
82
|
+
* `OxyDeviceSession` broker.
|
|
83
|
+
*/
|
|
84
|
+
export interface SharedDeviceCredentialStore {
|
|
85
|
+
/** Never throws — a failure is reported as `unavailable`, never as `absent`. */
|
|
86
|
+
read(): Promise<SharedDeviceCredentialRead>;
|
|
87
|
+
/**
|
|
88
|
+
* Publish the credential. Resolves `true` only when a read-back confirmed the
|
|
89
|
+
* exact bytes landed; `false` on any failure. Never throws.
|
|
90
|
+
*/
|
|
91
|
+
publish(credential: SharedDeviceCredential): Promise<boolean>;
|
|
92
|
+
/** Drop this app's copy of the shared credential. Never throws. */
|
|
93
|
+
clear(): Promise<void>;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/** Why {@link decideSharedDeviceJoin} declined to adopt the shared credential. */
|
|
97
|
+
export type SharedDeviceJoinSkipReason =
|
|
98
|
+
| 'shared-unsupported'
|
|
99
|
+
| 'shared-unreadable'
|
|
100
|
+
| 'shared-empty'
|
|
101
|
+
| 'local-credential-present';
|
|
102
|
+
|
|
103
|
+
/** What a booting app should do with the shared slot it just read. */
|
|
104
|
+
export type SharedDeviceJoinDecision =
|
|
105
|
+
| { action: 'adopt'; credential: SharedDeviceCredential }
|
|
106
|
+
| { action: 'skip'; reason: SharedDeviceJoinSkipReason };
|
|
107
|
+
|
|
108
|
+
/** Why {@link decideSharedDevicePublish} declined to write the shared slot. */
|
|
109
|
+
export type SharedDevicePublishSkipReason =
|
|
110
|
+
| 'shared-unsupported'
|
|
111
|
+
| 'shared-unreadable'
|
|
112
|
+
| 'already-current'
|
|
113
|
+
| 'owned-by-another-device';
|
|
114
|
+
|
|
115
|
+
/** What an app that just PROVED a credential should do with the shared slot. */
|
|
116
|
+
export type SharedDevicePublishDecision =
|
|
117
|
+
| { action: 'publish' }
|
|
118
|
+
| { action: 'skip'; reason: SharedDevicePublishSkipReason };
|
|
119
|
+
|
|
120
|
+
/** The usable `{deviceId, deviceSecret}` pair in a persisted state, or null. */
|
|
121
|
+
export function readLocalDeviceCredential(
|
|
122
|
+
state: PersistedAuthState | null,
|
|
123
|
+
): SharedDeviceCredential | null {
|
|
124
|
+
if (!state?.deviceId || !state.deviceSecret) {
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
return { deviceId: state.deviceId, deviceSecret: state.deviceSecret };
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Narrow an UNTRUSTED bridge payload into a {@link SharedDeviceCredentialRead}.
|
|
132
|
+
*
|
|
133
|
+
* Anything unrecognised resolves to `unavailable`, never `absent`. A native
|
|
134
|
+
* module returning a shape this build does not understand (an older app in the
|
|
135
|
+
* signing group, a partially-applied upgrade) means we do not KNOW whether the
|
|
136
|
+
* device has a shared session — and "do not know" must never authorise a write
|
|
137
|
+
* that would overwrite one.
|
|
138
|
+
*/
|
|
139
|
+
export function normalizeSharedDeviceSessionRead(raw: unknown): SharedDeviceCredentialRead {
|
|
140
|
+
if (!raw || typeof raw !== 'object') {
|
|
141
|
+
return { state: 'unavailable', cause: new Error('shared device session bridge returned a non-object') };
|
|
142
|
+
}
|
|
143
|
+
const payload = raw as Record<string, unknown>;
|
|
144
|
+
if (payload.status === 'absent') {
|
|
145
|
+
return { state: 'absent' };
|
|
146
|
+
}
|
|
147
|
+
if (payload.status === 'present') {
|
|
148
|
+
const deviceId = payload.deviceId;
|
|
149
|
+
const deviceSecret = payload.deviceSecret;
|
|
150
|
+
if (
|
|
151
|
+
typeof deviceId === 'string' &&
|
|
152
|
+
deviceId.length > 0 &&
|
|
153
|
+
typeof deviceSecret === 'string' &&
|
|
154
|
+
deviceSecret.length > 0
|
|
155
|
+
) {
|
|
156
|
+
return { state: 'present', credential: { deviceId, deviceSecret } };
|
|
157
|
+
}
|
|
158
|
+
// A `present` verdict whose payload is incomplete is a broken slot, not an
|
|
159
|
+
// empty one. Reporting `absent` here would let the next successful sign-in
|
|
160
|
+
// overwrite whatever is really in there.
|
|
161
|
+
return {
|
|
162
|
+
state: 'unavailable',
|
|
163
|
+
cause: new Error('shared device session bridge reported `present` with an incomplete credential'),
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
if (payload.status === 'unavailable') {
|
|
167
|
+
const reason = typeof payload.reason === 'string' ? payload.reason : 'unknown';
|
|
168
|
+
return { state: 'unavailable', cause: new Error(`shared device session slot unavailable: ${reason}`) };
|
|
169
|
+
}
|
|
170
|
+
return {
|
|
171
|
+
state: 'unavailable',
|
|
172
|
+
cause: new Error(`shared device session bridge returned an unrecognised status: ${String(payload.status)}`),
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/**
|
|
177
|
+
* Should this app adopt the shared credential? Pure.
|
|
178
|
+
*
|
|
179
|
+
* Adoption happens in exactly ONE case: the shared slot holds a credential and
|
|
180
|
+
* this app has none of its own. That single rule delivers the product
|
|
181
|
+
* requirement — a newly installed official app joins the device's existing
|
|
182
|
+
* session without another QR — while making the two failure modes that matter
|
|
183
|
+
* unreachable:
|
|
184
|
+
*
|
|
185
|
+
* - An app that is already signed in is NEVER moved onto another credential, so
|
|
186
|
+
* "no user is signed out merely because one app updates first" holds by
|
|
187
|
+
* construction, in both upgrade directions.
|
|
188
|
+
* - A failed read never looks like an empty slot, so a locked keychain resolves
|
|
189
|
+
* to "keep what I have" rather than "this is a fresh device".
|
|
190
|
+
*
|
|
191
|
+
* The cost is stated plainly: two apps that each already own a DIFFERENT device
|
|
192
|
+
* session stay on their own until one of them loses its credential. Converging
|
|
193
|
+
* them would mean signing one of them out or a server-side device merge, and
|
|
194
|
+
* neither is something a boot path may do silently.
|
|
195
|
+
*/
|
|
196
|
+
export function decideSharedDeviceJoin(
|
|
197
|
+
local: PersistedAuthState | null,
|
|
198
|
+
shared: SharedDeviceCredentialRead,
|
|
199
|
+
): SharedDeviceJoinDecision {
|
|
200
|
+
if (readLocalDeviceCredential(local) !== null) {
|
|
201
|
+
return { action: 'skip', reason: 'local-credential-present' };
|
|
202
|
+
}
|
|
203
|
+
switch (shared.state) {
|
|
204
|
+
case 'present':
|
|
205
|
+
return { action: 'adopt', credential: shared.credential };
|
|
206
|
+
case 'absent':
|
|
207
|
+
return { action: 'skip', reason: 'shared-empty' };
|
|
208
|
+
case 'unavailable':
|
|
209
|
+
return { action: 'skip', reason: 'shared-unreadable' };
|
|
210
|
+
case 'unsupported':
|
|
211
|
+
return { action: 'skip', reason: 'shared-unsupported' };
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Should this app write the credential it just proved into the shared slot? Pure.
|
|
217
|
+
*
|
|
218
|
+
* `proven` means the server accepted it moments ago — a successful sign-in or a
|
|
219
|
+
* successful mint. Only a proven credential is ever published, so the slot can
|
|
220
|
+
* never be seeded with something no app could use.
|
|
221
|
+
*
|
|
222
|
+
* A slot already held by a DIFFERENT `deviceId` is left alone. Overwriting it
|
|
223
|
+
* would silently migrate every other app on this device onto our session at their
|
|
224
|
+
* next cold boot — a real, user-visible change of who they are signed in as, and
|
|
225
|
+
* not something a background persist may decide.
|
|
226
|
+
*/
|
|
227
|
+
export function decideSharedDevicePublish(
|
|
228
|
+
proven: SharedDeviceCredential,
|
|
229
|
+
shared: SharedDeviceCredentialRead,
|
|
230
|
+
): SharedDevicePublishDecision {
|
|
231
|
+
switch (shared.state) {
|
|
232
|
+
case 'unsupported':
|
|
233
|
+
return { action: 'skip', reason: 'shared-unsupported' };
|
|
234
|
+
case 'unavailable':
|
|
235
|
+
return { action: 'skip', reason: 'shared-unreadable' };
|
|
236
|
+
case 'absent':
|
|
237
|
+
return { action: 'publish' };
|
|
238
|
+
case 'present': {
|
|
239
|
+
if (shared.credential.deviceId !== proven.deviceId) {
|
|
240
|
+
return { action: 'skip', reason: 'owned-by-another-device' };
|
|
241
|
+
}
|
|
242
|
+
if (shared.credential.deviceSecret === proven.deviceSecret) {
|
|
243
|
+
return { action: 'skip', reason: 'already-current' };
|
|
244
|
+
}
|
|
245
|
+
// Same device, newer secret. Sign-in rotates the secret (the mint does
|
|
246
|
+
// not), so the just-proven one is the credential a fresh install should
|
|
247
|
+
// join with.
|
|
248
|
+
return { action: 'publish' };
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** The result of {@link publishProvenDeviceCredential}, for logs and tests. */
|
|
254
|
+
export type SharedDevicePublishOutcome =
|
|
255
|
+
| { status: 'published' }
|
|
256
|
+
| { status: 'publish-failed' }
|
|
257
|
+
| { status: 'skipped'; reason: SharedDevicePublishSkipReason };
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Read the shared slot, apply {@link decideSharedDevicePublish}, and write when
|
|
261
|
+
* it says so. Best-effort by contract: the caller's own durable credential is
|
|
262
|
+
* already persisted, so a failure here only means a future install will have to
|
|
263
|
+
* sign in interactively.
|
|
264
|
+
*/
|
|
265
|
+
export async function publishProvenDeviceCredential(deps: {
|
|
266
|
+
shared: SharedDeviceCredentialStore;
|
|
267
|
+
credential: SharedDeviceCredential;
|
|
268
|
+
}): Promise<SharedDevicePublishOutcome> {
|
|
269
|
+
const read = await deps.shared.read();
|
|
270
|
+
const decision = decideSharedDevicePublish(deps.credential, read);
|
|
271
|
+
if (decision.action === 'skip') {
|
|
272
|
+
if (decision.reason === 'shared-unreadable') {
|
|
273
|
+
logger.debug(
|
|
274
|
+
'shared device credential slot unreadable — not publishing (an unreadable slot is never an empty one)',
|
|
275
|
+
{ component: 'sharedDeviceCredential', method: 'publishProvenDeviceCredential' },
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
return { status: 'skipped', reason: decision.reason };
|
|
279
|
+
}
|
|
280
|
+
const published = await deps.shared.publish(deps.credential);
|
|
281
|
+
return published ? { status: 'published' } : { status: 'publish-failed' };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Wrap a platform {@link AuthStateStore} so that every durable credential it
|
|
286
|
+
* persists is ALSO mirrored into the shared slot.
|
|
287
|
+
*
|
|
288
|
+
* Writes mirror automatically; reads do NOT adopt. That split is deliberate:
|
|
289
|
+
*
|
|
290
|
+
* - Mirroring on write is the right place because `save()` is where a proven
|
|
291
|
+
* credential lands, on every lane there is — interactive sign-in, the cold
|
|
292
|
+
* boot mint, the refresh scheduler, the 401 re-mint, shared-key recovery. One
|
|
293
|
+
* seam, no lane left out, and no new call site to forget.
|
|
294
|
+
* - Adopting on read would hide a change of WHO THIS APP IS SIGNED IN AS inside
|
|
295
|
+
* a storage primitive, and would run on every `load()`. Adoption is an
|
|
296
|
+
* explicit, once-per-boot cold-boot step instead (`shared-device-adopt`).
|
|
297
|
+
*
|
|
298
|
+
* `clear()` deliberately does NOT clear the shared slot. This app signing out is
|
|
299
|
+
* not authority over the device-wide join point: other apps may still be signed
|
|
300
|
+
* in on that same credential, and once the server session is really gone the
|
|
301
|
+
* credential mints `no_active_session` for everyone anyway.
|
|
302
|
+
*/
|
|
303
|
+
export function createSharedMirroringAuthStateStore(deps: {
|
|
304
|
+
local: AuthStateStore;
|
|
305
|
+
shared: SharedDeviceCredentialStore;
|
|
306
|
+
}): AuthStateStore {
|
|
307
|
+
const { local, shared } = deps;
|
|
308
|
+
// Process-local memo of the credential we last observed the shared slot to
|
|
309
|
+
// hold. Purely an optimization to keep the refresh scheduler from re-reading
|
|
310
|
+
// the keychain every mint; a slot wiped out from under us is re-seeded on the
|
|
311
|
+
// next launch rather than mid-process.
|
|
312
|
+
let mirrored: SharedDeviceCredential | null = null;
|
|
313
|
+
|
|
314
|
+
return {
|
|
315
|
+
load: () => local.load(),
|
|
316
|
+
clear: () => local.clear(),
|
|
317
|
+
save: async (state) => {
|
|
318
|
+
// The durable local write is the contract this store owes its caller —
|
|
319
|
+
// run it first and report ITS result, unchanged. The mirror is additive.
|
|
320
|
+
const durablePersisted = await local.save(state);
|
|
321
|
+
const credential = readLocalDeviceCredential(state);
|
|
322
|
+
if (!credential) {
|
|
323
|
+
return durablePersisted;
|
|
324
|
+
}
|
|
325
|
+
if (
|
|
326
|
+
mirrored !== null &&
|
|
327
|
+
mirrored.deviceId === credential.deviceId &&
|
|
328
|
+
mirrored.deviceSecret === credential.deviceSecret
|
|
329
|
+
) {
|
|
330
|
+
return durablePersisted;
|
|
331
|
+
}
|
|
332
|
+
try {
|
|
333
|
+
const outcome = await publishProvenDeviceCredential({ shared, credential });
|
|
334
|
+
if (outcome.status === 'published' || (outcome.status === 'skipped' && outcome.reason === 'already-current')) {
|
|
335
|
+
mirrored = credential;
|
|
336
|
+
}
|
|
337
|
+
} catch (error) {
|
|
338
|
+
// A store implementation is contractually non-throwing, but a mirror
|
|
339
|
+
// failure must never take down the durable write that already succeeded.
|
|
340
|
+
logger.debug(
|
|
341
|
+
'mirroring the device credential into the shared slot threw — the local credential is unaffected',
|
|
342
|
+
{ component: 'sharedDeviceCredential', method: 'save' },
|
|
343
|
+
error,
|
|
344
|
+
);
|
|
345
|
+
}
|
|
346
|
+
return durablePersisted;
|
|
347
|
+
},
|
|
348
|
+
};
|
|
349
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { logger } from '../logger';
|
|
2
|
+
|
|
3
|
+
export interface MinimalSocket {
|
|
4
|
+
connected: boolean;
|
|
5
|
+
on(event: string, handler: (...args: unknown[]) => void): void;
|
|
6
|
+
off(event: string, handler?: (...args: unknown[]) => void): void;
|
|
7
|
+
/** Client→server emit (e.g. joining the `/auth-session` room for a QR flow). */
|
|
8
|
+
emit(event: string, ...args: unknown[]): void;
|
|
9
|
+
connect(): void;
|
|
10
|
+
disconnect(): void;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export type SocketIOFactory = (uri: string, opts?: Record<string, unknown>) => MinimalSocket;
|
|
14
|
+
|
|
15
|
+
let cachedFactory: SocketIOFactory | null = null;
|
|
16
|
+
let loadAttempted = false;
|
|
17
|
+
|
|
18
|
+
export async function getSocketIO(): Promise<SocketIOFactory | null> {
|
|
19
|
+
if (cachedFactory) return cachedFactory;
|
|
20
|
+
if (loadAttempted) return null;
|
|
21
|
+
loadAttempted = true;
|
|
22
|
+
try {
|
|
23
|
+
const mod = (await import('socket.io-client')) as { io?: SocketIOFactory; default?: SocketIOFactory };
|
|
24
|
+
cachedFactory = mod.io ?? mod.default ?? null;
|
|
25
|
+
return cachedFactory;
|
|
26
|
+
} catch (error) {
|
|
27
|
+
logger.warn('[SessionClient] socket.io-client import failed; realtime session sync disabled', { component: 'SessionClient' }, error);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Color Utility Functions
|
|
3
|
+
*
|
|
4
|
+
* Consolidated color manipulation utilities used across the OxyServices ecosystem.
|
|
5
|
+
* These functions work in any JavaScript environment (browser, Node.js, React Native).
|
|
6
|
+
*
|
|
7
|
+
* @module shared/utils/colorUtils
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Darkens a hex color by a specified factor.
|
|
12
|
+
*
|
|
13
|
+
* @param color - Hex color string (with or without #)
|
|
14
|
+
* @param factor - Amount to darken (0-1). Default: 0.6
|
|
15
|
+
* @returns Darkened hex color string with # prefix
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* darkenColor('#FF0000', 0.5); // Returns a darker red
|
|
20
|
+
* darkenColor('FF0000', 0.3); // Also works without #
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
export const darkenColor = (color: string, factor = 0.6): string => {
|
|
24
|
+
const hex = color.replace('#', '');
|
|
25
|
+
|
|
26
|
+
const r = Number.parseInt(hex.substring(0, 2), 16);
|
|
27
|
+
const g = Number.parseInt(hex.substring(2, 4), 16);
|
|
28
|
+
const b = Number.parseInt(hex.substring(4, 6), 16);
|
|
29
|
+
|
|
30
|
+
const newR = Math.max(0, Math.round(r * (1 - factor)));
|
|
31
|
+
const newG = Math.max(0, Math.round(g * (1 - factor)));
|
|
32
|
+
const newB = Math.max(0, Math.round(b * (1 - factor)));
|
|
33
|
+
|
|
34
|
+
return `#${newR.toString(16).padStart(2, '0')}${newG.toString(16).padStart(2, '0')}${newB.toString(16).padStart(2, '0')}`;
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Lightens a hex color by a specified factor.
|
|
39
|
+
*
|
|
40
|
+
* @param color - Hex color string (with or without #)
|
|
41
|
+
* @param factor - Amount to lighten (0-1). Default: 0.3
|
|
42
|
+
* @returns Lightened hex color string with # prefix
|
|
43
|
+
*
|
|
44
|
+
* @example
|
|
45
|
+
* ```ts
|
|
46
|
+
* lightenColor('#0000FF', 0.5); // Returns a lighter blue
|
|
47
|
+
* ```
|
|
48
|
+
*/
|
|
49
|
+
export const lightenColor = (color: string, factor = 0.3): string => {
|
|
50
|
+
const hex = color.replace('#', '');
|
|
51
|
+
|
|
52
|
+
const r = Number.parseInt(hex.substring(0, 2), 16);
|
|
53
|
+
const g = Number.parseInt(hex.substring(2, 4), 16);
|
|
54
|
+
const b = Number.parseInt(hex.substring(4, 6), 16);
|
|
55
|
+
|
|
56
|
+
const newR = Math.min(255, Math.round(r + (255 - r) * factor));
|
|
57
|
+
const newG = Math.min(255, Math.round(g + (255 - g) * factor));
|
|
58
|
+
const newB = Math.min(255, Math.round(b + (255 - b) * factor));
|
|
59
|
+
|
|
60
|
+
return `#${newR.toString(16).padStart(2, '0')}${newG.toString(16).padStart(2, '0')}${newB.toString(16).padStart(2, '0')}`;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Converts a hex color to RGB values.
|
|
65
|
+
*
|
|
66
|
+
* @param hex - Hex color string (with or without #)
|
|
67
|
+
* @returns Object with r, g, b values (0-255)
|
|
68
|
+
*
|
|
69
|
+
* @example
|
|
70
|
+
* ```ts
|
|
71
|
+
* hexToRgb('#FF5733'); // { r: 255, g: 87, b: 51 }
|
|
72
|
+
* ```
|
|
73
|
+
*/
|
|
74
|
+
export const hexToRgb = (hex: string): { r: number; g: number; b: number } | null => {
|
|
75
|
+
const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
|
|
76
|
+
return result
|
|
77
|
+
? {
|
|
78
|
+
r: Number.parseInt(result[1], 16),
|
|
79
|
+
g: Number.parseInt(result[2], 16),
|
|
80
|
+
b: Number.parseInt(result[3], 16),
|
|
81
|
+
}
|
|
82
|
+
: null;
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Converts RGB values to a hex color string.
|
|
87
|
+
*
|
|
88
|
+
* @param r - Red value (0-255)
|
|
89
|
+
* @param g - Green value (0-255)
|
|
90
|
+
* @param b - Blue value (0-255)
|
|
91
|
+
* @returns Hex color string with # prefix
|
|
92
|
+
*
|
|
93
|
+
* @example
|
|
94
|
+
* ```ts
|
|
95
|
+
* rgbToHex(255, 87, 51); // '#ff5733'
|
|
96
|
+
* ```
|
|
97
|
+
*/
|
|
98
|
+
export const rgbToHex = (r: number, g: number, b: number): string => {
|
|
99
|
+
return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Adjusts the opacity of a hex color, returning an rgba string.
|
|
104
|
+
*
|
|
105
|
+
* @param hex - Hex color string
|
|
106
|
+
* @param opacity - Opacity value (0-1)
|
|
107
|
+
* @returns RGBA color string
|
|
108
|
+
*
|
|
109
|
+
* @example
|
|
110
|
+
* ```ts
|
|
111
|
+
* withOpacity('#FF0000', 0.5); // 'rgba(255, 0, 0, 0.5)'
|
|
112
|
+
* ```
|
|
113
|
+
*/
|
|
114
|
+
export const withOpacity = (hex: string, opacity: number): string => {
|
|
115
|
+
const rgb = hexToRgb(hex);
|
|
116
|
+
if (!rgb) return hex;
|
|
117
|
+
return `rgba(${rgb.r}, ${rgb.g}, ${rgb.b}, ${opacity})`;
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Checks if a color is considered "light" (for determining text contrast).
|
|
122
|
+
*
|
|
123
|
+
* @param hex - Hex color string
|
|
124
|
+
* @returns true if the color is light, false if dark
|
|
125
|
+
*
|
|
126
|
+
* @example
|
|
127
|
+
* ```ts
|
|
128
|
+
* isLightColor('#FFFFFF'); // true
|
|
129
|
+
* isLightColor('#000000'); // false
|
|
130
|
+
* ```
|
|
131
|
+
*/
|
|
132
|
+
export const isLightColor = (hex: string): boolean => {
|
|
133
|
+
const rgb = hexToRgb(hex);
|
|
134
|
+
if (!rgb) return true;
|
|
135
|
+
|
|
136
|
+
// Using relative luminance formula
|
|
137
|
+
const luminance = (0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b) / 255;
|
|
138
|
+
return luminance > 0.5;
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Gets a contrasting text color (black or white) for a given background color.
|
|
143
|
+
*
|
|
144
|
+
* @param backgroundColor - Hex color string of the background
|
|
145
|
+
* @returns '#000000' for light backgrounds, '#ffffff' for dark backgrounds
|
|
146
|
+
*
|
|
147
|
+
* @example
|
|
148
|
+
* ```ts
|
|
149
|
+
* getContrastTextColor('#FFFF00'); // '#000000' (black text on yellow)
|
|
150
|
+
* getContrastTextColor('#000080'); // '#ffffff' (white text on navy)
|
|
151
|
+
* ```
|
|
152
|
+
*/
|
|
153
|
+
export const getContrastTextColor = (backgroundColor: string): string => {
|
|
154
|
+
return isLightColor(backgroundColor) ? '#000000' : '#ffffff';
|
|
155
|
+
};
|