@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,370 @@
|
|
|
1
|
+
import { OxyServices } from '../OxyServices';
|
|
2
|
+
|
|
3
|
+
interface FetchCall {
|
|
4
|
+
url: string;
|
|
5
|
+
init: RequestInit | undefined;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
function createServices(): OxyServices {
|
|
9
|
+
return new OxyServices({ baseURL: 'https://api.oxy.so' });
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
function createJwt(payload: Record<string, unknown>): string {
|
|
13
|
+
const encode = (value: unknown): string => Buffer.from(JSON.stringify(value)).toString('base64url');
|
|
14
|
+
return `${encode({ alg: 'HS256', typ: 'JWT' })}.${encode(payload)}.signature`;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function jsonResponse(data: unknown): Response {
|
|
18
|
+
return new Response(JSON.stringify({ data }), {
|
|
19
|
+
status: 200,
|
|
20
|
+
headers: { 'content-type': 'application/json' },
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function readHeaders(init: RequestInit | undefined): Record<string, string> {
|
|
25
|
+
const headers = init?.headers;
|
|
26
|
+
if (!headers) {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
if (headers instanceof Headers) {
|
|
30
|
+
return Object.fromEntries(headers.entries());
|
|
31
|
+
}
|
|
32
|
+
if (Array.isArray(headers)) {
|
|
33
|
+
return Object.fromEntries(headers);
|
|
34
|
+
}
|
|
35
|
+
return headers as Record<string, string>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe('OxyServices.createLinkedClient', () => {
|
|
39
|
+
const originalFetch = globalThis.fetch;
|
|
40
|
+
|
|
41
|
+
afterEach(() => {
|
|
42
|
+
globalThis.fetch = originalFetch;
|
|
43
|
+
jest.restoreAllMocks();
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('mirrors token changes from the session owner', () => {
|
|
47
|
+
const oxy = createServices();
|
|
48
|
+
const linked = oxy.createLinkedClient({ baseURL: 'https://api.syra.fm' });
|
|
49
|
+
|
|
50
|
+
expect(linked.client.getAccessToken()).toBeNull();
|
|
51
|
+
|
|
52
|
+
oxy.setTokens('access_1');
|
|
53
|
+
expect(linked.client.getAccessToken()).toBe('access_1');
|
|
54
|
+
|
|
55
|
+
oxy.setTokens('access_2');
|
|
56
|
+
expect(linked.client.getAccessToken()).toBe('access_2');
|
|
57
|
+
|
|
58
|
+
oxy.clearTokens();
|
|
59
|
+
expect(linked.client.getAccessToken()).toBeNull();
|
|
60
|
+
|
|
61
|
+
linked.dispose();
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('copies the current token when created after sign-in', () => {
|
|
65
|
+
const oxy = createServices();
|
|
66
|
+
oxy.setTokens('existing_access');
|
|
67
|
+
|
|
68
|
+
const linked = oxy.createLinkedClient({ baseURL: 'https://api.syra.fm' });
|
|
69
|
+
|
|
70
|
+
expect(linked.client.getAccessToken()).toBe('existing_access');
|
|
71
|
+
|
|
72
|
+
linked.dispose();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
it('delegates token refresh to the session owner', async () => {
|
|
76
|
+
const oxy = createServices();
|
|
77
|
+
const linked = oxy.createLinkedClient({ baseURL: 'https://api.syra.fm' });
|
|
78
|
+
|
|
79
|
+
oxy.getClient().setAuthRefreshHandler(async () => 'refreshed_access');
|
|
80
|
+
|
|
81
|
+
const refreshed = await linked.client.refreshAccessToken('preflight');
|
|
82
|
+
|
|
83
|
+
expect(refreshed).toBe('refreshed_access');
|
|
84
|
+
expect(oxy.getAccessToken()).toBe('refreshed_access');
|
|
85
|
+
expect(linked.client.getAccessToken()).toBe('refreshed_access');
|
|
86
|
+
|
|
87
|
+
linked.dispose();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
it('returns an unread authenticated streaming response', async () => {
|
|
91
|
+
const calls: FetchCall[] = [];
|
|
92
|
+
globalThis.fetch = async (input, init) => {
|
|
93
|
+
calls.push({ url: String(input), init });
|
|
94
|
+
return new Response('data: {"delta":"hello"}\n\n', {
|
|
95
|
+
status: 200,
|
|
96
|
+
headers: { 'content-type': 'text/event-stream' },
|
|
97
|
+
});
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
const oxy = createServices();
|
|
101
|
+
const accessToken = createJwt({
|
|
102
|
+
userId: 'user_1',
|
|
103
|
+
exp: Math.floor(Date.now() / 1000) + 3600,
|
|
104
|
+
});
|
|
105
|
+
oxy.setTokens(accessToken);
|
|
106
|
+
const linked = oxy.createLinkedClient({ baseURL: 'https://api.alia.onl/' });
|
|
107
|
+
|
|
108
|
+
const response = await linked.client.requestAuthenticatedResponse({
|
|
109
|
+
method: 'POST',
|
|
110
|
+
url: '/alia/chat',
|
|
111
|
+
body: JSON.stringify({ message: 'hello' }),
|
|
112
|
+
headers: {
|
|
113
|
+
Authorization: 'Bearer caller_supplied_token',
|
|
114
|
+
'Content-Type': 'application/json',
|
|
115
|
+
},
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
expect(response.bodyUsed).toBe(false);
|
|
119
|
+
expect(await response.text()).toBe('data: {"delta":"hello"}\n\n');
|
|
120
|
+
expect(calls).toHaveLength(1);
|
|
121
|
+
expect(calls[0]?.url).toBe('https://api.alia.onl/alia/chat');
|
|
122
|
+
expect(readHeaders(calls[0]?.init).authorization).toBe(`Bearer ${accessToken}`);
|
|
123
|
+
|
|
124
|
+
linked.dispose();
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
it('refreshes once after a streaming response 401 and keeps the final body unread', async () => {
|
|
128
|
+
const calls: FetchCall[] = [];
|
|
129
|
+
globalThis.fetch = async (input, init) => {
|
|
130
|
+
calls.push({ url: String(input), init });
|
|
131
|
+
if (calls.length === 1) {
|
|
132
|
+
return new Response('expired', { status: 401 });
|
|
133
|
+
}
|
|
134
|
+
return new Response('data: done\n\n', {
|
|
135
|
+
status: 200,
|
|
136
|
+
headers: { 'content-type': 'text/event-stream' },
|
|
137
|
+
});
|
|
138
|
+
};
|
|
139
|
+
|
|
140
|
+
const oxy = createServices();
|
|
141
|
+
const oldToken = createJwt({
|
|
142
|
+
userId: 'user_1',
|
|
143
|
+
exp: Math.floor(Date.now() / 1000) + 3600,
|
|
144
|
+
});
|
|
145
|
+
const refreshedToken = createJwt({
|
|
146
|
+
userId: 'user_1',
|
|
147
|
+
exp: Math.floor(Date.now() / 1000) + 7200,
|
|
148
|
+
});
|
|
149
|
+
oxy.setTokens(oldToken);
|
|
150
|
+
oxy.getClient().setAuthRefreshHandler(async () => refreshedToken);
|
|
151
|
+
const linked = oxy.createLinkedClient({ baseURL: 'https://api.alia.onl' });
|
|
152
|
+
|
|
153
|
+
const response = await linked.client.requestAuthenticatedResponse({
|
|
154
|
+
method: 'POST',
|
|
155
|
+
url: '/alia/chat',
|
|
156
|
+
body: '{}',
|
|
157
|
+
headers: { 'Content-Type': 'application/json' },
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
expect(calls).toHaveLength(2);
|
|
161
|
+
expect(readHeaders(calls[0]?.init).authorization).toBe(`Bearer ${oldToken}`);
|
|
162
|
+
expect(readHeaders(calls[1]?.init).authorization).toBe(`Bearer ${refreshedToken}`);
|
|
163
|
+
expect(oxy.getAccessToken()).toBe(refreshedToken);
|
|
164
|
+
expect(linked.client.getAccessToken()).toBe(refreshedToken);
|
|
165
|
+
expect(response.bodyUsed).toBe(false);
|
|
166
|
+
expect(await response.text()).toBe('data: done\n\n');
|
|
167
|
+
|
|
168
|
+
linked.dispose();
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
it('rejects an authenticated streaming request before fetch without a session', async () => {
|
|
172
|
+
const fetchMock = jest.fn();
|
|
173
|
+
globalThis.fetch = fetchMock as unknown as typeof globalThis.fetch;
|
|
174
|
+
const oxy = createServices();
|
|
175
|
+
const linked = oxy.createLinkedClient({ baseURL: 'https://api.alia.onl' });
|
|
176
|
+
|
|
177
|
+
await expect(linked.client.requestAuthenticatedResponse({
|
|
178
|
+
method: 'POST',
|
|
179
|
+
url: '/alia/chat',
|
|
180
|
+
body: '{}',
|
|
181
|
+
})).rejects.toMatchObject({
|
|
182
|
+
code: 'AUTH_REQUIRED',
|
|
183
|
+
status: 401,
|
|
184
|
+
});
|
|
185
|
+
expect(fetchMock).not.toHaveBeenCalled();
|
|
186
|
+
|
|
187
|
+
linked.dispose();
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
it('keeps the session owner intact when a linked response 401 cannot refresh', async () => {
|
|
191
|
+
const oxy = createServices();
|
|
192
|
+
oxy.setTokens('stale_access');
|
|
193
|
+
const linked = oxy.createLinkedClient({ baseURL: 'https://api.syra.fm' });
|
|
194
|
+
|
|
195
|
+
const refreshed = await linked.client.refreshAccessToken('response-401');
|
|
196
|
+
|
|
197
|
+
expect(refreshed).toBeNull();
|
|
198
|
+
expect(oxy.getAccessToken()).toBe('stale_access');
|
|
199
|
+
expect(linked.client.getAccessToken()).toBe('stale_access');
|
|
200
|
+
|
|
201
|
+
linked.dispose();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
it('resynchronizes from the session owner after a linked app 401 clears the local token', async () => {
|
|
205
|
+
const calls: FetchCall[] = [];
|
|
206
|
+
let queueWriteAttempts = 0;
|
|
207
|
+
globalThis.fetch = async (input, init) => {
|
|
208
|
+
const url = String(input);
|
|
209
|
+
calls.push({ url, init });
|
|
210
|
+
|
|
211
|
+
if (url.endsWith('/csrf-token')) {
|
|
212
|
+
return new Response(JSON.stringify({ csrfToken: 'csrf_1' }), {
|
|
213
|
+
status: 200,
|
|
214
|
+
headers: { 'content-type': 'application/json' },
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (url.endsWith('/api/queue/current')) {
|
|
219
|
+
queueWriteAttempts += 1;
|
|
220
|
+
if (queueWriteAttempts === 1) {
|
|
221
|
+
return new Response(JSON.stringify({ error: 'MISSING_TOKEN' }), {
|
|
222
|
+
status: 401,
|
|
223
|
+
statusText: 'Unauthorized',
|
|
224
|
+
headers: { 'content-type': 'application/json' },
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return jsonResponse({ ok: true });
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const oxy = createServices();
|
|
233
|
+
const accessToken = createJwt({
|
|
234
|
+
userId: 'user_1',
|
|
235
|
+
exp: Math.floor(Date.now() / 1000) + 3600,
|
|
236
|
+
});
|
|
237
|
+
oxy.setTokens(accessToken);
|
|
238
|
+
oxy.getClient().setAuthRefreshHandler(async () => null);
|
|
239
|
+
const linked = oxy.createLinkedClient({ baseURL: 'https://api.syra.fm' });
|
|
240
|
+
|
|
241
|
+
await expect(linked.client.put('/api/queue/current', { trackId: 'track_1' }, { retry: false })).rejects.toMatchObject({
|
|
242
|
+
message: 'MISSING_TOKEN',
|
|
243
|
+
status: 401,
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
expect(oxy.getAccessToken()).toBe(accessToken);
|
|
247
|
+
expect(linked.client.getAccessToken()).toBeNull();
|
|
248
|
+
|
|
249
|
+
await linked.client.put('/api/queue/current', { trackId: 'track_2' });
|
|
250
|
+
|
|
251
|
+
expect(calls.map((call) => call.url)).toEqual([
|
|
252
|
+
'https://api.syra.fm/api/queue/current',
|
|
253
|
+
'https://api.syra.fm/api/queue/current',
|
|
254
|
+
]);
|
|
255
|
+
expect(linked.client.getAccessToken()).toBe(accessToken);
|
|
256
|
+
|
|
257
|
+
const firstHeaders = readHeaders(calls[0]?.init);
|
|
258
|
+
expect(firstHeaders.Authorization).toBe(`Bearer ${accessToken}`);
|
|
259
|
+
expect(firstHeaders['X-CSRF-Token']).toBeUndefined();
|
|
260
|
+
|
|
261
|
+
const secondHeaders = readHeaders(calls[1]?.init);
|
|
262
|
+
expect(secondHeaders.Authorization).toBe(`Bearer ${accessToken}`);
|
|
263
|
+
expect(secondHeaders['X-CSRF-Token']).toBeUndefined();
|
|
264
|
+
|
|
265
|
+
linked.dispose();
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
it('keeps the session owner intact when linked preflight refresh cannot refresh', async () => {
|
|
269
|
+
const oxy = createServices();
|
|
270
|
+
oxy.setTokens('existing_access');
|
|
271
|
+
const linked = oxy.createLinkedClient({ baseURL: 'https://api.syra.fm' });
|
|
272
|
+
|
|
273
|
+
const refreshed = await linked.client.refreshAccessToken('preflight');
|
|
274
|
+
|
|
275
|
+
expect(refreshed).toBeNull();
|
|
276
|
+
expect(oxy.getAccessToken()).toBe('existing_access');
|
|
277
|
+
expect(linked.client.getAccessToken()).toBe('existing_access');
|
|
278
|
+
|
|
279
|
+
linked.dispose();
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('stops mirroring after dispose', () => {
|
|
283
|
+
const oxy = createServices();
|
|
284
|
+
const linked = oxy.createLinkedClient({ baseURL: 'https://api.syra.fm' });
|
|
285
|
+
|
|
286
|
+
oxy.setTokens('before_dispose');
|
|
287
|
+
expect(linked.client.getAccessToken()).toBe('before_dispose');
|
|
288
|
+
|
|
289
|
+
linked.dispose();
|
|
290
|
+
oxy.setTokens('after_dispose');
|
|
291
|
+
|
|
292
|
+
expect(linked.client.getAccessToken()).toBeNull();
|
|
293
|
+
});
|
|
294
|
+
|
|
295
|
+
it('joins linked base URLs with relative paths that omit the leading slash', async () => {
|
|
296
|
+
const fetchMock = jest.fn(async () => jsonResponse({ ok: true }));
|
|
297
|
+
globalThis.fetch = fetchMock as unknown as typeof globalThis.fetch;
|
|
298
|
+
|
|
299
|
+
const oxy = createServices();
|
|
300
|
+
const linked = oxy.createLinkedClient({ baseURL: 'https://api.mention.earth' });
|
|
301
|
+
|
|
302
|
+
await linked.client.get('profile/settings/me');
|
|
303
|
+
|
|
304
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
305
|
+
expect(String(fetchMock.mock.calls[0]?.[0])).toBe('https://api.mention.earth/profile/settings/me');
|
|
306
|
+
|
|
307
|
+
linked.dispose();
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* GET response caching is OFF by default for linked clients: the SDK cannot
|
|
312
|
+
* invalidate the consumer backend's resources, so a cached GET there would
|
|
313
|
+
* serve stale data after the app mutates its own data. The consumer's own
|
|
314
|
+
* layer (React Query / stores) owns caching. An explicit `enableCache: true`
|
|
315
|
+
* opts back in.
|
|
316
|
+
*/
|
|
317
|
+
describe('linked client GET cache default', () => {
|
|
318
|
+
it('does NOT cache GETs by default — every read hits the network', async () => {
|
|
319
|
+
const fetchMock = jest.fn();
|
|
320
|
+
globalThis.fetch = fetchMock as unknown as typeof globalThis.fetch;
|
|
321
|
+
|
|
322
|
+
const oxy = createServices();
|
|
323
|
+
const accessToken = createJwt({
|
|
324
|
+
userId: 'user_1',
|
|
325
|
+
exp: Math.floor(Date.now() / 1000) + 3600,
|
|
326
|
+
});
|
|
327
|
+
oxy.setTokens(accessToken);
|
|
328
|
+
const linked = oxy.createLinkedClient({ baseURL: 'https://api.mention.earth' });
|
|
329
|
+
|
|
330
|
+
// Two identical GETs with cache:true — both MUST hit the network because
|
|
331
|
+
// the linked client's cache is disabled by default.
|
|
332
|
+
fetchMock.mockResolvedValueOnce(jsonResponse({ v: 1 }));
|
|
333
|
+
const first = await linked.client.get<{ v: number }>('/feed/mtn', { cache: true });
|
|
334
|
+
fetchMock.mockResolvedValueOnce(jsonResponse({ v: 2 }));
|
|
335
|
+
const second = await linked.client.get<{ v: number }>('/feed/mtn', { cache: true });
|
|
336
|
+
|
|
337
|
+
expect(first.v).toBe(1);
|
|
338
|
+
expect(second.v).toBe(2);
|
|
339
|
+
expect(fetchMock).toHaveBeenCalledTimes(2);
|
|
340
|
+
|
|
341
|
+
linked.dispose();
|
|
342
|
+
});
|
|
343
|
+
|
|
344
|
+
it('caches GETs when the caller explicitly opts in with enableCache: true', async () => {
|
|
345
|
+
const fetchMock = jest.fn();
|
|
346
|
+
globalThis.fetch = fetchMock as unknown as typeof globalThis.fetch;
|
|
347
|
+
|
|
348
|
+
const oxy = createServices();
|
|
349
|
+
const accessToken = createJwt({
|
|
350
|
+
userId: 'user_1',
|
|
351
|
+
exp: Math.floor(Date.now() / 1000) + 3600,
|
|
352
|
+
});
|
|
353
|
+
oxy.setTokens(accessToken);
|
|
354
|
+
const linked = oxy.createLinkedClient({
|
|
355
|
+
baseURL: 'https://api.mention.earth',
|
|
356
|
+
enableCache: true,
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
// Second identical GET is a warm cache hit — only one network call.
|
|
360
|
+
fetchMock.mockResolvedValueOnce(jsonResponse({ v: 1 }));
|
|
361
|
+
const first = await linked.client.get<{ v: number }>('/feed/mtn', { cache: true });
|
|
362
|
+
const second = await linked.client.get<{ v: number }>('/feed/mtn', { cache: true });
|
|
363
|
+
|
|
364
|
+
expect(first).toEqual(second);
|
|
365
|
+
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
366
|
+
|
|
367
|
+
linked.dispose();
|
|
368
|
+
});
|
|
369
|
+
});
|
|
370
|
+
});
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { isHttpRequestError, parseHttpErrorBody } from '../utils/errorUtils';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* `parseHttpErrorBody` is the ONE place the SDK decides what a failed request's
|
|
5
|
+
* message, code and details are, across every error envelope the Oxy ecosystem
|
|
6
|
+
* emits. The bug it exists to prevent is silent: a nested
|
|
7
|
+
* `{ error: { code, message } }` assigned straight to `new Error(...)` yields
|
|
8
|
+
* the literal string `"[object Object]"`, which reads as a rendering bug in
|
|
9
|
+
* whichever app surfaces it, arbitrarily far from here.
|
|
10
|
+
*
|
|
11
|
+
* Each case below pins one envelope AND the discrimination it depends on — in
|
|
12
|
+
* particular, whether the top-level `error` string is a machine CODE or human
|
|
13
|
+
* prose is decided solely by whether a sibling human field is present, so both
|
|
14
|
+
* sides of that fork need a fixture or the rule is untested.
|
|
15
|
+
*/
|
|
16
|
+
describe('parseHttpErrorBody', () => {
|
|
17
|
+
it('reads the nested { error: { code, message, details } } envelope', () => {
|
|
18
|
+
expect(
|
|
19
|
+
parseHttpErrorBody({
|
|
20
|
+
error: { code: 'case_conflict', message: 'A case already exists', details: { caseId: 'c_1' } },
|
|
21
|
+
}),
|
|
22
|
+
).toEqual({
|
|
23
|
+
message: 'A case already exists',
|
|
24
|
+
code: 'case_conflict',
|
|
25
|
+
details: { caseId: 'c_1' },
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("reads oxy-api's { error: '<CODE>', message } shape — error IS the code", () => {
|
|
30
|
+
expect(parseHttpErrorBody({ error: 'VALIDATION_ERROR', message: 'username is taken' })).toEqual({
|
|
31
|
+
message: 'username is taken',
|
|
32
|
+
code: 'VALIDATION_ERROR',
|
|
33
|
+
details: undefined,
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
it("reads RFC 6749 { error: '<CODE>', error_description } from the OAuth endpoints", () => {
|
|
38
|
+
// The OAuth token/userinfo endpoints are the one part of the API that
|
|
39
|
+
// does not use the `{ error, message }` envelope. Without this arm the
|
|
40
|
+
// human text is dropped and the raw code is shown to the user.
|
|
41
|
+
expect(
|
|
42
|
+
parseHttpErrorBody({ error: 'invalid_grant', error_description: 'Authorization code expired' }),
|
|
43
|
+
).toEqual({
|
|
44
|
+
message: 'Authorization code expired',
|
|
45
|
+
code: 'invalid_grant',
|
|
46
|
+
details: undefined,
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
it("treats a LONE { error: '<text>' } as the message, never as a code", () => {
|
|
51
|
+
// The discriminator is the ABSENCE of a sibling human field. Promoting a
|
|
52
|
+
// bare string to `code` would put prose where callers switch on codes,
|
|
53
|
+
// so this case and the two above have to disagree.
|
|
54
|
+
expect(parseHttpErrorBody({ error: 'Something went wrong' })).toEqual({
|
|
55
|
+
message: 'Something went wrong',
|
|
56
|
+
code: undefined,
|
|
57
|
+
details: undefined,
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('reads the flat { message, code } shape (CSRF rejections)', () => {
|
|
62
|
+
expect(parseHttpErrorBody({ message: 'Invalid CSRF token', code: 'CSRF_TOKEN_INVALID' })).toEqual({
|
|
63
|
+
message: 'Invalid CSRF token',
|
|
64
|
+
code: 'CSRF_TOKEN_INVALID',
|
|
65
|
+
details: undefined,
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('ignores whitespace-only strings rather than reporting them as a message', () => {
|
|
70
|
+
// A blank message is worse than none: it replaces the status fallback
|
|
71
|
+
// with an empty error surface the user cannot act on.
|
|
72
|
+
expect(parseHttpErrorBody({ message: ' ', error: '' })).toEqual({
|
|
73
|
+
message: undefined,
|
|
74
|
+
code: undefined,
|
|
75
|
+
details: undefined,
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it('ignores a non-object `details` instead of passing it through', () => {
|
|
80
|
+
expect(parseHttpErrorBody({ message: 'nope', details: 'not-an-object' }).details).toBeUndefined();
|
|
81
|
+
expect(parseHttpErrorBody({ message: 'nope', details: ['a'] }).details).toBeUndefined();
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it.each([null, undefined, [], 'a string', 42, true])('returns {} for the non-object body %p', (body) => {
|
|
85
|
+
expect(parseHttpErrorBody(body)).toEqual({});
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('returns {} for an object carrying none of the known fields', () => {
|
|
89
|
+
expect(parseHttpErrorBody({ unrelated: 'field' })).toEqual({
|
|
90
|
+
message: undefined,
|
|
91
|
+
code: undefined,
|
|
92
|
+
details: undefined,
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
describe('isHttpRequestError', () => {
|
|
98
|
+
it('accepts an Error carrying a numeric status', () => {
|
|
99
|
+
const error = Object.assign(new Error('boom'), { status: 409 });
|
|
100
|
+
expect(isHttpRequestError(error)).toBe(true);
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('rejects a plain ApiError object — those are objects, not Errors', () => {
|
|
104
|
+
// The distinction is load-bearing: an ApiError has to go through
|
|
105
|
+
// `handleHttpError` first, and a truthy answer here would let a caller
|
|
106
|
+
// read `.stack`/`.name` off something that has neither.
|
|
107
|
+
expect(isHttpRequestError({ message: 'boom', status: 409, code: 'CONFLICT' })).toBe(false);
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
it('rejects an Error whose status is a non-number', () => {
|
|
111
|
+
// `'409'` is the shape a hand-built error most plausibly carries, and
|
|
112
|
+
// the narrowing promises callers a number they can compare.
|
|
113
|
+
expect(isHttpRequestError(Object.assign(new Error('boom'), { status: '409' }))).toBe(false);
|
|
114
|
+
expect(isHttpRequestError(new Error('boom'))).toBe(false);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Every package `@oxy.so/core/server` VALUE-imports must be a real dependency.
|
|
3
|
+
*
|
|
4
|
+
* ## The failure this exists for, which happened
|
|
5
|
+
*
|
|
6
|
+
* `helmet` was declared an OPTIONAL peer dependency. `src/server/securityHeaders.ts`
|
|
7
|
+
* imports it at the top level, and the server barrel re-exports that module — so
|
|
8
|
+
* importing *anything* from `@oxy.so/core/server` loads helmet, and a consumer
|
|
9
|
+
* that believed the "optional" marking did not install it.
|
|
10
|
+
*
|
|
11
|
+
* The result was a backend that could not boot: `Cannot find package 'helmet'`.
|
|
12
|
+
* `tsc` passed clean the whole time, because types resolve from a peer whether
|
|
13
|
+
* or not the runtime can find the module. It was found by a test suite failing
|
|
14
|
+
* to resolve 29 of 63 files while every typecheck was green — which is a very
|
|
15
|
+
* expensive way to learn it.
|
|
16
|
+
*
|
|
17
|
+
* ## Why value imports and not all imports
|
|
18
|
+
*
|
|
19
|
+
* `express` is imported by eight files here and every one of them is
|
|
20
|
+
* `import type`. Those vanish at build time, so an optional peer is a truthful
|
|
21
|
+
* declaration for it: a consumer of the client entry never needs express
|
|
22
|
+
* installed. The distinction this test draws is exactly the one that decides
|
|
23
|
+
* whether a missing package is a warning or a crash.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
27
|
+
import path from 'node:path';
|
|
28
|
+
|
|
29
|
+
const packageRoot = path.resolve(__dirname, '../..');
|
|
30
|
+
const serverDir = path.join(packageRoot, 'src', 'server');
|
|
31
|
+
|
|
32
|
+
const manifest = JSON.parse(readFileSync(path.join(packageRoot, 'package.json'), 'utf8')) as {
|
|
33
|
+
dependencies?: Record<string, string>;
|
|
34
|
+
peerDependencies?: Record<string, string>;
|
|
35
|
+
peerDependenciesMeta?: Record<string, { optional?: boolean }>;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const dependencies = new Set(Object.keys(manifest.dependencies ?? {}));
|
|
39
|
+
const requiredPeers = new Set(
|
|
40
|
+
Object.keys(manifest.peerDependencies ?? {}).filter(
|
|
41
|
+
(name) => manifest.peerDependenciesMeta?.[name]?.optional !== true
|
|
42
|
+
)
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Bare specifiers imported for their VALUE — `import x from 'y'`,
|
|
47
|
+
* `import { a } from 'y'`, `import 'y'` — but never `import type`.
|
|
48
|
+
*/
|
|
49
|
+
function valueImports(source: string): string[] {
|
|
50
|
+
const found: string[] = [];
|
|
51
|
+
// Strip comments first: prose in a doc comment can otherwise read as code.
|
|
52
|
+
const code = source.replace(/\/\*[\s\S]*?\*\//g, ' ').replace(/(^|[^:])\/\/[^\n]*/g, '$1 ');
|
|
53
|
+
for (const match of code.matchAll(/import\s+(?!type\b)([^;']*?)from\s*'([^']+)'/g)) {
|
|
54
|
+
// `import { type A, type B } from 'x'` is still type-only in effect.
|
|
55
|
+
const clause = match[1];
|
|
56
|
+
const specifier = match[2];
|
|
57
|
+
if (specifier.startsWith('.') || specifier.startsWith('node:')) continue;
|
|
58
|
+
const names = clause.replace(/[{}]/g, '').split(',').map((n) => n.trim()).filter(Boolean);
|
|
59
|
+
if (names.length > 0 && names.every((n) => n.startsWith('type '))) continue;
|
|
60
|
+
found.push(specifier);
|
|
61
|
+
}
|
|
62
|
+
for (const match of code.matchAll(/import\s*'([^']+)'/g)) {
|
|
63
|
+
if (!match[1].startsWith('.') && !match[1].startsWith('node:')) found.push(match[1]);
|
|
64
|
+
}
|
|
65
|
+
return found;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function packageNameOf(specifier: string): string {
|
|
69
|
+
const parts = specifier.split('/');
|
|
70
|
+
return specifier.startsWith('@') ? `${parts[0]}/${parts[1]}` : parts[0];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const files = readdirSync(serverDir).filter((f) => f.endsWith('.ts') && !f.endsWith('.d.ts'));
|
|
74
|
+
|
|
75
|
+
describe('@oxy.so/core/server value imports are installable', () => {
|
|
76
|
+
it('reads a non-trivial number of server modules', () => {
|
|
77
|
+
// Vacuity floor: a traversal that found nothing would satisfy every
|
|
78
|
+
// assertion below by having nothing to check.
|
|
79
|
+
expect(files.length).toBeGreaterThan(5);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('declares every value-imported package as a dependency or a REQUIRED peer', () => {
|
|
83
|
+
const undeclared: string[] = [];
|
|
84
|
+
for (const file of files) {
|
|
85
|
+
const source = readFileSync(path.join(serverDir, file), 'utf8');
|
|
86
|
+
for (const specifier of valueImports(source)) {
|
|
87
|
+
const name = packageNameOf(specifier);
|
|
88
|
+
if (dependencies.has(name) || requiredPeers.has(name)) continue;
|
|
89
|
+
undeclared.push(`${file} → ${name}`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// An OPTIONAL peer is not enough here and that is the whole point: the
|
|
94
|
+
// module is loaded the moment anything is imported from the server barrel,
|
|
95
|
+
// so "optional" describes a package the consumer cannot actually omit.
|
|
96
|
+
expect(undeclared.sort()).toEqual([]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('keeps helmet installable rather than optional', () => {
|
|
100
|
+
// The specific regression, asserted by name so a failure says what broke
|
|
101
|
+
// rather than only showing it inside a list.
|
|
102
|
+
expect(dependencies.has('helmet')).toBe(true);
|
|
103
|
+
expect(manifest.peerDependenciesMeta?.helmet?.optional).toBeUndefined();
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
it('keeps express-rate-limit installable rather than a peer', () => {
|
|
107
|
+
// Same class as helmet: `rateLimit.ts` value-imports it and the server
|
|
108
|
+
// barrel re-exports that module, so a missing install crashes at boot.
|
|
109
|
+
expect(dependencies.has('express-rate-limit')).toBe(true);
|
|
110
|
+
expect(manifest.peerDependencies?.['express-rate-limit']).toBeUndefined();
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('leaves express as an optional peer, because it is type-only here', () => {
|
|
114
|
+
// The counter-example that stops this test from being read as "declare
|
|
115
|
+
// everything": type imports vanish at build time, so an optional peer is a
|
|
116
|
+
// truthful declaration for express and making it required would nag every
|
|
117
|
+
// React Native consumer about a package they never load.
|
|
118
|
+
expect(manifest.peerDependenciesMeta?.express?.optional).toBe(true);
|
|
119
|
+
});
|
|
120
|
+
});
|