Compare commits

...
Author SHA1 Message Date
Jonas DanielsGitHubgithub-actions[bot] <github-actions[bot]@users.noreply.github.com>GitHub Action
0a9fc269ff Version Packages (#2246)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: GitHub Action <[email protected]>
2024-01-31 16:59:43 -08:00
iketwandGitHub 2e3cc7cdd4 Adds support for RN 0.73 and removes magic (#2225) 2024-02-01 00:34:28 +00:00
3ba15366be [Analytics] Unify client analytics (#2224)
Co-authored-by: ikethirdweb <[email protected]>
2024-01-31 23:46:44 +00:00
771bd1e1b2 chore(chains): update chains (#2245)
Co-authored-by: GitHub Action <[email protected]>
2024-01-31 01:05:57 -08:00
68 changed files with 1692 additions and 1308 deletions
+7
View File
@@ -1,5 +1,12 @@
# @thirdweb-dev/auth
## 4.1.28
### Patch Changes
- Updated dependencies [[`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a)]:
- @thirdweb-dev/wallets@2.4.6
## 4.1.27
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/auth",
"version": "4.1.27",
"version": "4.1.28",
"main": "dist/thirdweb-dev-auth.cjs.js",
"module": "dist/thirdweb-dev-auth.esm.js",
"browser": {
+6
View File
@@ -1,5 +1,11 @@
# @thirdweb-dev/chains
## 0.1.68
### Patch Changes
- [#2245](https://github.com/thirdweb-dev/js/pull/2245) [`771bd1e1`](https://github.com/thirdweb-dev/js/commit/771bd1e1b280681992195103c22aafa1fb98f7e0) Thanks [@github-actions](https://github.com/apps/github-actions)! - Synced Chains Package
## 0.1.67
### Patch Changes
+31
View File
@@ -0,0 +1,31 @@
import type { Chain } from "../src/types";
export default {
"chain": "Avalanche",
"chainId": 5042,
"explorers": [],
"faucets": [],
"features": [],
"icon": {
"url": "https://images.ctfassets.net/9bazykntljf6/62CceHSYsRS4D9fgDSkLRB/877cb8f26954e1743ff535fd7fdaf78f/avacloud-placeholder.svg",
"width": 256,
"height": 256,
"format": "svg"
},
"infoURL": "https://avacloud.io",
"name": "OMOCHI",
"nativeCurrency": {
"name": "OMOCHI Token",
"symbol": "OMCH",
"decimals": 18
},
"networkId": 5042,
"redFlags": [],
"rpc": [
"https://omochi.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://5042.rpc.thirdweb.com/${THIRDWEB_API_KEY}",
"https://subnets.avax.network/omochi/testnet/rpc"
],
"shortName": "OMOCHI",
"slug": "omochi",
"testnet": true
} as const satisfies Chain;
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/chains",
"version": "0.1.67",
"version": "0.1.68",
"main": "dist/thirdweb-dev-chains.cjs.js",
"module": "dist/thirdweb-dev-chains.esm.js",
"exports": {
File diff suppressed because one or more lines are too long
+11
View File
@@ -1,5 +1,16 @@
# thirdweb
## 0.13.36
### Patch Changes
- Updated dependencies [[`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a), [`771bd1e1`](https://github.com/thirdweb-dev/js/commit/771bd1e1b280681992195103c22aafa1fb98f7e0)]:
- @thirdweb-dev/storage@2.0.9
- @thirdweb-dev/wallets@2.4.6
- @thirdweb-dev/sdk@4.0.31
- @thirdweb-dev/chains@0.1.68
- @thirdweb-dev/auth@4.1.28
## 0.13.35
### Patch Changes
+1 -1
View File
@@ -1,7 +1,7 @@
{
"name": "thirdweb",
"main": "dist/cli/index.js",
"version": "0.13.35",
"version": "0.13.36",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/cli",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
+11
View File
@@ -1,5 +1,16 @@
# @thirdweb-dev/react-core
## 4.4.3
### Patch Changes
- Updated dependencies [[`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a), [`771bd1e1`](https://github.com/thirdweb-dev/js/commit/771bd1e1b280681992195103c22aafa1fb98f7e0)]:
- @thirdweb-dev/storage@2.0.9
- @thirdweb-dev/wallets@2.4.6
- @thirdweb-dev/sdk@4.0.31
- @thirdweb-dev/chains@0.1.68
- @thirdweb-dev/auth@4.1.28
## 4.4.2
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-core",
"version": "4.4.2",
"version": "4.4.3",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-core",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
Binary file not shown.
@@ -1,5 +1,11 @@
# @thirdweb-dev/react-native-compat
## 0.7.0
### Minor Changes
- [#2225](https://github.com/thirdweb-dev/js/pull/2225) [`2e3cc7cd`](https://github.com/thirdweb-dev/js/commit/2e3cc7cdd4017df8a5b00ebdd6e37334432e0b82) Thanks [@iketw](https://github.com/iketw)! - Adds support for RN 0.73
## 0.6.5
## 0.6.4
+2 -2
View File
@@ -1,7 +1,7 @@
{
"name": "@thirdweb-dev/react-native-compat",
"description": "Shims for Thirdweb in React Native Projects",
"version": "0.6.5",
"version": "0.7.0",
"author": "thirdweb eng <[email protected]>",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native-compat",
"license": "Apache-2.0",
@@ -22,7 +22,7 @@
},
"dependencies": {
"@ethersproject/shims": "^5.7.0",
"@walletconnect/react-native-compat": "^2.8.6",
"@walletconnect/react-native-compat": "2.9.2",
"base-64": "^1.0.0",
"big-integer": "^1.6.51",
"react-native-get-random-values": "^1.10.0",
+19
View File
@@ -1,5 +1,24 @@
# @thirdweb-dev/react-native
## 0.7.0
### Minor Changes
- [#2225](https://github.com/thirdweb-dev/js/pull/2225) [`2e3cc7cd`](https://github.com/thirdweb-dev/js/commit/2e3cc7cdd4017df8a5b00ebdd6e37334432e0b82) Thanks [@iketw](https://github.com/iketw)! - Adds support for RN 0.73
### Patch Changes
- [#2224](https://github.com/thirdweb-dev/js/pull/2224) [`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - Adds correct reporing headers
- [#2224](https://github.com/thirdweb-dev/js/pull/2224) [`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - Adds global sdk vars
- Updated dependencies [[`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a), [`771bd1e1`](https://github.com/thirdweb-dev/js/commit/771bd1e1b280681992195103c22aafa1fb98f7e0)]:
- @thirdweb-dev/storage@2.0.9
- @thirdweb-dev/wallets@2.4.6
- @thirdweb-dev/sdk@4.0.31
- @thirdweb-dev/chains@0.1.68
- @thirdweb-dev/react-core@4.4.3
## 0.6.5
### Patch Changes
+5 -13
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react-native",
"version": "0.6.5",
"version": "0.7.0",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react-native",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -27,9 +27,7 @@
"dependencies": {
"@aws-sdk/client-lambda": "3.350.0",
"@aws-sdk/credential-providers": "3.350.0",
"@coinbase/wallet-mobile-sdk": "1.0.10",
"@magic-sdk/provider": "17.2.0",
"@magic-sdk/react-native-bare": "^18.5.0",
"@coinbase/wallet-mobile-sdk": "1.0.13",
"@paperxyz/embedded-wallet-service-sdk": "^1.2.4",
"@shopify/restyle": "^2.4.2",
"@tanstack/react-query": "^4.33.0",
@@ -41,7 +39,7 @@
"abitype": "^0.2.5",
"aws-amplify": "^5.3.8",
"expo-application": "^5.3.0",
"expo-clipboard": "^4.1.2",
"expo-clipboard": "^4.5.0",
"expo-document-picker": "^11.2.2",
"expo-file-system": "^15.2.2",
"expo-secure-store": "^12.1.1",
@@ -77,9 +75,6 @@
"react": "^18.2.0",
"react-native": "^0.71.11",
"react-native-camera": "^4.2.1",
"react-native-device-info": "10.6.0",
"react-native-safe-area-context": "4.7.4",
"react-native-webview": "12.1.0",
"rimraf": "^3.0.2",
"typedoc-gen": "workspace:*",
"typescript": "^5.3.3"
@@ -88,16 +83,13 @@
"amazon-cognito-identity-js": "^6.3.3",
"react-native-aes-gcm-crypto": "^0.2.2",
"react-native-camera": "^4.2.1",
"react-native-device-info": "10.6.0",
"react-native-inappbrowser-reborn": "^3.7.0",
"react-native-quick-base64": "^2.0.7",
"react-native-quick-crypto": "^0.6.1",
"react-native-safe-area-context": "4.7.4",
"react-native-webview": "12.1.0"
"react-native-quick-crypto": "^0.6.1"
},
"peerDependencies": {
"ethers": ">=5.5.1 <6",
"expo": ">=47 <50",
"expo": ">=47",
"react": ">=18.0.0",
"react-native": ">=0.71.0",
"react-native-mmkv": ">=2.11.0"
@@ -5,8 +5,7 @@ import {
TW_UPLOAD_SERVER_URL,
} from "@thirdweb-dev/storage";
import { IpfsUploaderOptions, UploadDataValue } from "./types";
import { appBundleId, packageVersion } from "../../evm/utils/version";
import { BUNDLE_ID_HEADER } from "../../evm/constants/headers";
import { getAnalyticsHeaders, setAnalyticsHeaders } from "./utils";
const METADATA_NAME = "Storage React Native SDK";
@@ -38,9 +37,6 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
keyvalues: { ...options?.metadata },
};
const { version, name: packageName } = packageVersion;
const platform = "react-native";
if ("uri" in data[0] && "type" in data[0] && "name" in data[0]) {
// then it's an array of files
return new Promise(async (resolve, reject) => {
@@ -140,14 +136,11 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
});
xhr.open("POST", `${TW_UPLOAD_SERVER_URL}/ipfs/upload`);
xhr.setRequestHeader(BUNDLE_ID_HEADER, appBundleId || ""); // only empty on web
if (this.clientId) {
xhr.setRequestHeader("x-client-id", this.clientId);
}
xhr.setRequestHeader("x-sdk-version", version);
xhr.setRequestHeader("x-sdk-name", packageName);
xhr.setRequestHeader("x-sdk-platform", platform);
setAnalyticsHeaders(xhr);
xhr.send(form);
});
@@ -165,12 +158,9 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
{
method: "POST",
headers: {
[BUNDLE_ID_HEADER]: appBundleId || "", // only empty on web
...(this.clientId ? { "x-client-id": this.clientId } : {}),
"Content-Type": "application/json",
"x-sdk-version": version,
"x-sdk-name": packageName,
"x-sdk-platform": platform,
...getAnalyticsHeaders(),
},
body: fetchBody,
},
@@ -1,4 +1,6 @@
import { DEFAULT_GATEWAY_URLS, GatewayUrls } from "@thirdweb-dev/storage";
import { Platform } from "react-native";
import { appBundleId, packageVersion } from "../../evm/utils/version";
/**
* @internal
@@ -32,3 +34,51 @@ export function prepareGatewayUrls(
return allGatewayUrls;
}
export function setAnalyticsHeaders(xhr: XMLHttpRequest) {
const globals = getAnalyticsGlobals();
xhr.setRequestHeader("x-sdk-version", globals.x_sdk_version);
xhr.setRequestHeader("x-sdk-os", globals.x_sdk_os);
xhr.setRequestHeader("x-sdk-name", globals.x_sdk_name);
xhr.setRequestHeader("x-sdk-platform", globals.x_sdk_platform);
xhr.setRequestHeader("x-bundle-id", globals.app_bundle_id);
}
export function getAnalyticsHeaders() {
const globals = getAnalyticsGlobals();
return {
"x-sdk-version": globals.x_sdk_version,
"x-sdk-os": globals.x_sdk_os,
"x-sdk-name": globals.x_sdk_name,
"x-sdk-platform": globals.x_sdk_platform,
"x-bundle-id": globals.app_bundle_id,
};
}
export function getAnalyticsGlobals() {
if (typeof globalThis === "undefined") {
return {
x_sdk_name: packageVersion.name,
x_sdk_platform: "mobile",
x_sdk_version: packageVersion.version,
x_sdk_os: Platform.OS,
app_bundle_id: appBundleId,
};
}
if ((globalThis as any).X_SDK_NAME === undefined) {
(globalThis as any).X_SDK_NAME = packageVersion.name;
(globalThis as any).X_SDK_PLATFORM = "mobile";
(globalThis as any).X_SDK_VERSION = packageVersion.version;
(globalThis as any).X_SDK_OS = Platform.OS;
(globalThis as any).APP_BUNDLE_ID = appBundleId;
}
return {
x_sdk_name: (globalThis as any).X_SDK_NAME,
x_sdk_platform: (globalThis as any).X_SDK_PLATFORM,
x_sdk_version: (globalThis as any).X_SDK_VERSION,
x_sdk_os: (globalThis as any).X_SDK_OS,
app_bundle_id: (globalThis as any).APP_BUNDLE_ID,
};
}
@@ -49,15 +49,12 @@ export function ChooseWallet({
const guestWallet = wallets.find((w) => w.id === walletIds.localWallet);
const emailWallet = wallets.find(
(w) =>
w.id === walletIds.magicLink ||
(w.id === walletIds.embeddedWallet && w.selectUI),
(w) => w.id === walletIds.embeddedWallet && w.selectUI,
);
const walletsToDisplay = useMemo(() => {
const filteredWallets = wallets.filter(
(wallet) =>
wallet.id !== walletIds.magicLink &&
wallet.id !== walletIds.embeddedWallet &&
wallet.id !== walletIds.localWallet,
);
@@ -1 +0,0 @@
export const BUNDLE_ID_HEADER = "x-bundle-id";
-4
View File
@@ -15,10 +15,6 @@ export { TrustWallet, trustWallet } from "./wallets/wallets/trust-wallet";
export { smartWallet } from "./wallets/wallets/smart-wallet";
export { localWallet } from "./wallets/wallets/local-wallet";
export { LocalWallet } from "./wallets/wallets/LocalWallet";
export { magicWallet } from "./wallets/wallets/magic-link";
export { MagicWallet } from "./wallets/wallets/MagicLink";
export { MagicLink } from "./wallets/wallets/MagicLink";
export { magicLink } from "./wallets/wallets/magic-link";
export { EmbeddedWallet } from "./wallets/wallets/embedded/EmbeddedWallet";
export { embeddedWallet } from "./wallets/wallets/embedded/embedded-wallet";
export * from "./wallets/wallets/wallet-connect/WalletConnectBase";
@@ -1,61 +1,27 @@
import { SmartWallet, walletIds } from "@thirdweb-dev/wallets";
import {
createContext,
useCallback,
useContext,
useEffect,
useState,
} from "react";
import { SmartWallet } from "@thirdweb-dev/wallets";
import { createContext, useContext, useState } from "react";
import { useWalletConnectListener } from "../wallets/hooks/useWalletConnectListener";
import { MagicLink } from "../wallets/wallets/MagicLink";
import { useWalletContext } from "@thirdweb-dev/react-core";
type DappContextType = {
smartWallet?: SmartWallet;
setSmartWallet?: (value?: SmartWallet) => void;
magicLink?: MagicLink;
setMagicLink?: (value?: MagicLink) => void;
};
const DappContext = createContext<DappContextType>({});
export const DappContextProvider = (props: React.PropsWithChildren) => {
const [smartWallet, setSmartWallet] = useState<SmartWallet | undefined>();
const [magicLink, setMagicLink] = useState<MagicLink | undefined>();
const createdWalletInstance = useWalletContext().createdWalletInstance;
useEffect(() => {
if (!createdWalletInstance) {
return;
}
if (createdWalletInstance.walletId === walletIds.magicLink) {
setMagicLink(createdWalletInstance as MagicLink);
}
}, [createdWalletInstance, setMagicLink]);
useWalletConnectListener();
const magicSDK = useCallback(() => {
if (magicLink) {
const magic = magicLink.getMagicSDK();
return <magic.Relayer />;
}
return null;
}, [magicLink]);
return (
<DappContext.Provider
value={{
smartWallet,
setSmartWallet,
magicLink: magicLink,
setMagicLink: setMagicLink,
}}
>
{props.children}
{magicSDK()}
</DappContext.Provider>
);
};
@@ -69,12 +35,3 @@ export const useSmartWallet = () => {
return [context.smartWallet, context.setSmartWallet] as const;
};
export const useMagicLink = () => {
const context = useContext(DappContext);
return {
magicLink: context.magicLink,
setMagicLink: context.setMagicLink,
} as const;
};
@@ -4,7 +4,7 @@ import {
ThirdwebProviderCoreProps,
WalletConfig,
} from "@thirdweb-dev/react-core";
import { PropsWithChildren, useMemo } from "react";
import { PropsWithChildren } from "react";
import type { Chain, defaultChains } from "@thirdweb-dev/chains";
import { SecureStorage } from "../../core/SecureStorage";
import { useCoinbaseWalletListener } from "../wallets/hooks/useCoinbaseWalletListener";
@@ -13,10 +13,10 @@ import { DappContextProvider } from "./context-provider";
import { UIContextProvider } from "./ui-context-provider";
import { MainModal } from "../components/MainModal";
import { ThemeProvider, ThemeType } from "../styles/ThemeProvider";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { walletIds } from "@thirdweb-dev/wallets";
import { ThirdwebStorage } from "../../core/storage/storage";
import type { Locale } from "../i18n/types";
import { getAnalyticsGlobals } from "../../core/storage/utils";
export interface ThirdwebProviderProps<TChains extends Chain[]>
extends Omit<
@@ -148,6 +148,8 @@ export const ThirdwebProvider = <TChains extends Chain[] = DefaultChains>(
...restProps
} = props;
getAnalyticsGlobals();
const coinbaseWalletObj = supportedWallets.find(
(w) => w.id === walletIds.coinbase,
);
@@ -158,11 +160,6 @@ export const ThirdwebProvider = <TChains extends Chain[] = DefaultChains>(
coinbaseWalletObj?.config?.callbackURL,
);
const hasMagicConfig = useMemo(
() => !!supportedWallets.find((wc) => wc.id === walletIds.magicLink),
[supportedWallets],
);
return (
<ThirdwebProviderCore
supportedWallets={supportedWallets}
@@ -188,19 +185,10 @@ export const ThirdwebProvider = <TChains extends Chain[] = DefaultChains>(
>
<ThemeProvider theme={theme ? theme : "dark"}>
<UIContextProvider locale={locale}>
{hasMagicConfig ? (
<SafeAreaProvider>
<DappContextProvider>
{children}
<MainModal />
</DappContextProvider>
</SafeAreaProvider>
) : (
<DappContextProvider>
{children}
<MainModal />
</DappContextProvider>
)}
<DappContextProvider>
{children}
<MainModal />
</DappContextProvider>
</UIContextProvider>
</ThemeProvider>
</ThirdwebProviderCore>
@@ -1,7 +0,0 @@
export function isGlobalThisPresent() {
return typeof globalThis !== "undefined";
}
export function isAppBundleIdPresentInGlobal() {
return isGlobalThisPresent() && "APP_BUNDLE_ID" in globalThis;
}
@@ -41,12 +41,14 @@ import {
} from "../types";
import { InAppBrowser } from "react-native-inappbrowser-reborn";
import { createErrorMessage } from "./helpers/errors";
import {
appBundleId,
reactNativePackageVersion,
} from "../../../../utils/version";
import { BUNDLE_ID_HEADER } from "../../../../constants/headers";
import { ANALYTICS } from "./helpers/analytics";
import { getAnalyticsHeaders } from "../../../../../core/storage/utils";
const HEADERS = {
[EWS_VERSION_HEADER]: (globalThis as any).X_SDK_VERSION,
[THIRDWEB_SESSION_NONCE_HEADER]: ANALYTICS.nonce,
...getAnalyticsHeaders(),
};
export async function sendVerificationEmail(options: {
email: string;
@@ -196,9 +198,7 @@ export async function socialLogin(oauthOptions: OauthOption, clientId: string) {
const resp = await fetch(headlessLoginLinkWithParams, {
headers: {
[EWS_VERSION_HEADER]: reactNativePackageVersion,
[BUNDLE_ID_HEADER]: appBundleId,
[THIRDWEB_SESSION_NONCE_HEADER]: ANALYTICS.nonce,
...HEADERS,
},
});
@@ -279,9 +279,7 @@ export async function customJwt(authOptions: AuthOptions, clientId: string) {
method: "POST",
headers: {
"Content-Type": "application/json",
[EWS_VERSION_HEADER]: reactNativePackageVersion,
[BUNDLE_ID_HEADER]: appBundleId,
[THIRDWEB_SESSION_NONCE_HEADER]: ANALYTICS.nonce,
...HEADERS,
},
body: JSON.stringify({
jwt: jwt,
@@ -329,9 +327,7 @@ export async function authEndpoint(
method: "POST",
headers: {
"Content-Type": "application/json",
[EWS_VERSION_HEADER]: reactNativePackageVersion,
[BUNDLE_ID_HEADER]: appBundleId,
[THIRDWEB_SESSION_NONCE_HEADER]: ANALYTICS.nonce,
...HEADERS,
},
body: JSON.stringify({
payload: payload,
@@ -20,20 +20,17 @@ import {
VerifiedTokenResponse,
} from "../../../types";
import { createErrorMessage } from "../errors";
import {
appBundleId,
reactNativePackageVersion,
} from "../../../../../../utils/version";
import { BUNDLE_ID_HEADER } from "../../../../../../constants/headers";
import { ANALYTICS } from "../analytics";
import { getAnalyticsHeaders } from "../../../../../../../core/storage/utils";
const EMBEDDED_WALLET_TOKEN_HEADER = "embedded-wallet-token";
const PAPER_CLIENT_ID_HEADER = "x-thirdweb-client-id";
const HEADERS = {
"Content-Type": "application/json",
[BUNDLE_ID_HEADER]: appBundleId,
[EWS_VERSION_HEADER]: reactNativePackageVersion,
[EWS_VERSION_HEADER]: (globalThis as any).X_SDK_VERSION,
[THIRDWEB_SESSION_NONCE_HEADER]: ANALYTICS.nonce,
...getAnalyticsHeaders(),
};
export const verifyClientId = async (clientId: string) => {
@@ -68,19 +65,15 @@ export const authFetchEmbeddedWalletUser = async (
Authorization: `Bearer ${EMBEDDED_WALLET_TOKEN_HEADER}:${
authTokenClient || ""
}`,
[BUNDLE_ID_HEADER]: appBundleId,
[PAPER_CLIENT_ID_HEADER]: clientId,
[EWS_VERSION_HEADER]: reactNativePackageVersion,
[THIRDWEB_SESSION_NONCE_HEADER]: ANALYTICS.nonce,
...HEADERS,
}
: {
Authorization: `Bearer ${EMBEDDED_WALLET_TOKEN_HEADER}:${
authTokenClient || ""
}`,
[BUNDLE_ID_HEADER]: appBundleId,
[PAPER_CLIENT_ID_HEADER]: clientId,
[EWS_VERSION_HEADER]: reactNativePackageVersion,
[THIRDWEB_SESSION_NONCE_HEADER]: ANALYTICS.nonce,
...HEADERS,
};
return fetch(url, params);
};
@@ -1,198 +0,0 @@
import {
Magic,
MagicSDKAdditionalConfiguration,
} from "@magic-sdk/react-native-bare";
import { Chain, Connector, normalizeChainId } from "@thirdweb-dev/wallets";
import { Signer, ethers, providers } from "ethers";
import { defaultChains } from "@thirdweb-dev/chains";
import { MagicConnectorOptions } from "./types";
export class MagicConnector extends Connector<MagicConnectorOptions> {
magicSDK: Magic;
magicOptions: MagicConnectorOptions;
provider: any | undefined;
chains: Chain[];
magicSdkConfiguration: MagicSDKAdditionalConfiguration | undefined;
connectedChainId: number | undefined;
constructor(options: MagicConnectorOptions) {
super();
this.magicOptions = options;
this.chains = options.chains ? options.chains : defaultChains;
this.magicSdkConfiguration = options.magicSdkConfiguration;
this.magicSDK = this.initializeMagicSDK({ chainId: options?.chainId || 0 });
}
async connect(options: MagicConnectorOptions): Promise<string> {
if (!this.magicOptions.apiKey) {
throw new Error("Magic API Key is not provided.");
}
// if (options.chainId) {
// this.initializeMagicSDK({ chainId: options.chainId });
// }
await this.getProvider();
this.setupListeners();
this.emit("message", { type: "connecting" });
// Check if there is a user logged in
const isAuthenticated = await this.isConnected();
// Check if we have a chainId, in case of error just assign 0 for legacy
let chainId: number;
try {
chainId = await this.getChainId();
} catch (e) {
chainId = 0;
}
this.connectedChainId = chainId;
// if there is a user logged in, return the user
if (isAuthenticated) {
return await this.getAddress();
}
const magic = this.getMagicSDK();
// LOGIN WITH MAGIC LINK WITH EMAIL
if (options.email) {
await magic.auth.loginWithEmailOTP({
email: options.email,
});
}
// LOGIN WITH MAGIC LINK WITH PHONE NUMBER
if (options.phoneNumber) {
await magic.auth.loginWithSMS({
phoneNumber: options.phoneNumber,
});
}
const signer = await this.getSigner();
let address = await signer.getAddress();
if (!address.startsWith("0x")) {
address = `0x${address}`;
}
return address;
}
async disconnect(): Promise<void> {
const magic = this.getMagicSDK();
await magic.user.logout();
}
async getAddress(): Promise<string> {
const signer = await this.getSigner();
const account = await signer.getAddress();
if (account.startsWith("0x")) {
return account;
}
return `0x${account}`;
}
async getSigner(): Promise<Signer> {
const provider = new ethers.providers.Web3Provider(
(await this.getProvider()) as any, // TODO: fix type mismatch
);
const signer = await provider.getSigner();
return signer;
}
getProvider(): Promise<providers.Provider> {
if (this.provider) {
return this.provider;
}
const magic = this.getMagicSDK();
this.provider = magic.rpcProvider;
return this.provider;
}
switchChain(chainId: number): Promise<void> {
const chain = this.chains.find((c) => c.chainId === chainId);
if (!chain) {
throw new Error("Chain not found");
}
if (this.connectedChainId !== chainId) {
this.initializeMagicSDK({ chainId });
}
return Promise.resolve();
}
isConnected(): Promise<boolean> {
const magic = this.getMagicSDK();
try {
return magic.user.isLoggedIn();
} catch (e) {
return Promise.resolve(false);
}
}
async setupListeners(): Promise<void> {
this.provider?.on("accountsChanged", () => {
throw new Error("Accounts changed not implemented");
});
this.provider?.on("chainChanged", this.onChainChanged);
this.provider?.on("disconnect", this.onDisconnect);
return Promise.resolve();
}
updateChains(chains: Chain[]): void {
this.chains = chains;
}
// my methods
onDisconnect(): void {
this.emit("disconnect");
}
getMagicSDK() {
if (!this.magicSDK) {
return this.initializeMagicSDK();
}
return this.magicSDK;
}
initializeMagicSDK({ chainId }: { chainId?: number } = {}) {
if (chainId) {
const chain = this.chains.find((c) => c.chainId === chainId);
if (chain) {
this.magicSdkConfiguration = this.magicSdkConfiguration || {};
this.magicSdkConfiguration.network = {
rpcUrl: chain.rpc[0],
chainId: chain.chainId,
};
}
}
this.magicSDK = new Magic(
this.magicOptions.apiKey,
this.magicSdkConfiguration,
);
this.provider = this.magicSDK.rpcProvider;
return this.magicSDK;
}
async getChainId(): Promise<number> {
const networkOptions = this.magicSdkConfiguration?.network;
if (typeof networkOptions === "object") {
const chainID = networkOptions.chainId;
if (chainID) {
return normalizeChainId(chainID);
}
}
throw new Error("Chain ID is not defined");
}
protected isChainUnsupported(chainId: number) {
return !this.chains.some((x) => x.chainId === chainId);
}
protected onChainChanged(chainId: string | number): void {
const id = normalizeChainId(chainId);
const unsupported = this.isChainUnsupported(id);
this.emit("change", { chain: { id, unsupported } });
}
}
@@ -1,17 +0,0 @@
import { MagicSDKAdditionalConfiguration } from "@magic-sdk/react-native-bare";
import { Chain } from "@thirdweb-dev/chains";
import { WalletOptions } from "@thirdweb-dev/wallets";
export interface MagicConnectorOptions {
apiKey: string;
magicSdkConfiguration?: MagicSDKAdditionalConfiguration;
chains?: Chain[];
chainId?: number;
email?: string;
phoneNumber?: string;
}
export type MagicLinkOptions = Omit<
WalletOptions<MagicConnectorOptions>,
"clientId"
>;
@@ -1,83 +0,0 @@
import { AbstractClientWallet, walletIds } from "@thirdweb-dev/wallets";
import {
MagicConnectorOptions,
MagicLinkOptions,
} from "../connectors/magic/types";
import type { MagicConnector as MagicConnectorType } from "../connectors/magic/magic-connector";
import { MagicConnector } from "../connectors/magic/magic-connector";
/**
* @deprecated We have deprecated magicLink in favor of our embeddedWallet which adds support for more sign in methods.
* To learn more, please see:
*
* Checkout the [Release Notes](https://portal.thirdweb.com/wallets/embedded-wallet/overview) and [Documentation](https://portal.thirdweb.com/react-native/v0/wallets/embedded-wallet) for more information.
*/
export class MagicLink extends AbstractClientWallet<
MagicLinkOptions,
MagicConnectorOptions
> {
connector?: MagicConnectorType;
options: MagicLinkOptions;
static meta = {
iconURL:
"ipfs://QmUMBFZGXxBpgDmZzZAHhbcCL5nYvZnVaYLTajsNjLcxMU/1-Icon_Magic_Color.svg",
name: "Magic Link",
};
static id = walletIds.magicLink;
constructor(options: MagicLinkOptions) {
super(walletIds.magicLink, options);
this.options = options;
this.initializeConnector();
}
async getConnector(): Promise<MagicConnectorType> {
if (!this.connector) {
return await this.initializeConnector();
}
return this.connector;
}
// my methods
initializeConnector() {
this.connector = new MagicConnector({
...this.options,
chainId: this.options.chainId,
chains: this.chains,
});
return this.connector;
}
getMagicSDK() {
return (this.connector as MagicConnector)?.getMagicSDK();
}
async autoConnect(options?: MagicConnectorOptions) {
this.connector?.initializeMagicSDK(options);
const magic = this.getMagicSDK();
if (await magic.user.isLoggedIn()) {
return super.autoConnect(options);
} else {
throw new Error("Magic user is not logged in");
}
}
async disconnect() {
const magic = this.getMagicSDK();
await magic.user.logout();
return super.disconnect();
}
}
/**
* @deprecated Use `MagicLink` instead
*
* Renamed for consistency with our React package
*/
export class MagicWallet extends MagicLink {}
@@ -1,192 +0,0 @@
import { walletIds } from "@thirdweb-dev/wallets";
import {
ConnectUIProps,
SelectUIProps,
WalletConfig,
WalletOptions,
useCreateWalletInstance,
useSetConnectedWallet,
useSetConnectionStatus,
useWalletContext,
} from "@thirdweb-dev/react-core";
import { MagicLinkOptions } from "../connectors/magic/types";
import { MagicLink } from "./MagicLink";
import { useEffect, useRef } from "react";
import Box from "../../components/base/Box";
import { TextInput } from "../../components/base/TextInput";
import Text from "../../components/base/Text";
import { useMagicLink } from "../../providers/context-provider";
import BaseButton from "../../components/base/BaseButton";
import React from "react";
import { ConnectingWallet } from "../../components/ConnectWalletFlow/ConnectingWallet/ConnectingWallet";
import { useGlobalTheme, useLocale } from "../../providers/ui-context-provider";
/**
*
* @param magicLinkOptions - Options available to create a magic link wallet
* @returns A WalletConfig<MagicLink> object
*
* @deprecated We have deprecated magicLink in favor of our embeddedWallet which adds support for more sign in methods.
* To learn more, please see:
*
* Check out [Release Note](https://portal.thirdweb.com/wallets/embedded-wallet/overview) and [Documentation](https://portal.thirdweb.com/react-native/v0/wallets/embedded-wallet) for more details.
*/
export const magicLink = (
magicLinkOptions: MagicLinkOptions & { recommended?: boolean },
): WalletConfig<MagicLink> => {
return {
id: walletIds.magicLink,
meta: MagicLink.meta,
create: (options: WalletOptions) => {
return new MagicLink({
...options,
...magicLinkOptions,
chainId: options.chain.chainId,
});
},
/**
* UI for connecting wallet
*/
connectUI(props) {
return (
<MagicConnectionUI {...props} magicLinkOptions={magicLinkOptions} />
);
},
/**
* UI for selecting wallet - this UI is rendered in the wallet selection screen
*/
selectUI: MagicSelectionUI,
isInstalled: () => {
return true;
},
recommended: magicLinkOptions?.recommended,
};
};
const MagicSelectionUI: React.FC<SelectUIProps<MagicLink>> = (props) => {
const l = useLocale();
const theme = useGlobalTheme();
const [email, setEmail] = React.useState("");
const onContinuePress = () => {
if (!email) {
return;
}
props.onSelect(email);
};
return (
<Box paddingHorizontal="xl" mt="lg">
<TextInput
textInputProps={{
placeholder: l.embedded_wallet.enter_your_email,
placeholderTextColor: theme.colors.textSecondary,
onChangeText: (text: string) => {
setEmail(text);
},
style: {
fontSize: 14,
color: theme.colors.textPrimary,
fontFamily: theme.textVariants.defaults.fontFamily,
lineHeight: 16,
padding: 0,
},
}}
containerProps={{
paddingHorizontal: "sm",
paddingVertical: "sm",
}}
/>
<BaseButton
mt="md"
paddingVertical="md"
borderRadius="lg"
borderWidth={1}
borderColor="border"
backgroundColor="accentButtonColor"
onPress={onContinuePress}
>
<Text
variant="bodySmall"
color="accentButtonTextColor"
fontWeight="700"
>
{l.common.continue}
</Text>
</BaseButton>
</Box>
);
};
const MagicConnectionUI: React.FC<
ConnectUIProps<MagicLink> & { magicLinkOptions: MagicLinkOptions }
> = ({ selectionData, walletConfig, connected, goBack, magicLinkOptions }) => {
const createWalletInstance = useCreateWalletInstance();
const setConnectedWallet = useSetConnectedWallet();
const chainToConnect = useWalletContext().chainToConnect;
const setConnectionStatus = useSetConnectionStatus();
const { magicLink: ctxMagicLink, setMagicLink } = useMagicLink();
useEffect(() => {
const inst = createWalletInstance(walletConfig);
setMagicLink?.(inst);
}, [createWalletInstance, setMagicLink, walletConfig]);
const connectPrompted = useRef(false);
useEffect(() => {
if (connectPrompted.current || !ctxMagicLink) {
return;
}
connectPrompted.current = true;
const isEmail = (selectionData as string).includes("@");
(async () => {
// close();
try {
setConnectionStatus("connecting");
const connectParams = {
chainId: chainToConnect?.chainId,
...magicLinkOptions,
...(isEmail
? { email: selectionData }
: { phoneNumber: selectionData }),
};
connected();
await ctxMagicLink.connect(connectParams);
await ctxMagicLink.getMagicSDK().user.getMetadata();
setConnectedWallet(ctxMagicLink, connectParams);
setConnectionStatus("connected");
} catch (e) {
setConnectionStatus("disconnected");
console.error("Error connecting to magic", e);
}
})();
}, [
selectionData,
walletConfig,
connected,
ctxMagicLink,
setConnectedWallet,
setConnectionStatus,
chainToConnect?.chainId,
magicLinkOptions,
]);
return (
<ConnectingWallet
subHeaderText=""
wallet={walletConfig}
onClose={connected}
onBackPress={goBack}
/>
);
};
/**
* @deprecated Use `magicLink()` instead
*
* Renamed for consistency with our React package
*/
export const magicWallet = magicLink;
-7
View File
@@ -1,8 +1 @@
import { isGlobalThisPresent } from "./evm/utils/global";
import { appBundleId } from "./evm/utils/version";
export * from "./evm";
if (isGlobalThisPresent()) {
(globalThis as any).APP_BUNDLE_ID = appBundleId;
}
Binary file not shown.
+14
View File
@@ -1,5 +1,19 @@
# @thirdweb-dev/react
## 4.4.3
### Patch Changes
- [#2224](https://github.com/thirdweb-dev/js/pull/2224) [`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - Adds correct reporing headers
- [#2224](https://github.com/thirdweb-dev/js/pull/2224) [`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - Adds global sdk vars
- Updated dependencies [[`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a), [`771bd1e1`](https://github.com/thirdweb-dev/js/commit/771bd1e1b280681992195103c22aafa1fb98f7e0)]:
- @thirdweb-dev/wallets@2.4.6
- @thirdweb-dev/sdk@4.0.31
- @thirdweb-dev/chains@0.1.68
- @thirdweb-dev/react-core@4.4.3
## 4.4.2
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/react",
"version": "4.4.2",
"version": "4.4.3",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/react",
"author": "thirdweb eng <[email protected]>",
"license": "Apache-2.0",
@@ -16,6 +16,7 @@ import { en } from "../locales/en";
import { ThirdwebLocaleContext } from "./locale-provider";
import { walletIds } from "@thirdweb-dev/wallets";
import { ThirdwebLocale } from "../locales/types";
import { getAnalyticsGlobals } from "../utils/headers";
export interface ThirdwebProviderProps<TChains extends Chain[]>
extends Omit<
@@ -479,6 +480,8 @@ export const ThirdwebProvider = <TChains extends Chain[] = DefaultChains>(
...restProps
} = props;
getAnalyticsGlobals();
const wallets: WalletConfig[] = supportedWallets || defaultWallets;
const theme = _theme || "dark";
+30
View File
@@ -0,0 +1,30 @@
import packageJson from "../../../package.json";
import { detectOS } from "./isMobile";
export function getAnalyticsGlobals() {
if (typeof globalThis === "undefined") {
return {
x_sdk_name: packageJson.name,
x_sdk_platform: "browser",
x_sdk_version: packageJson.version,
x_sdk_os: detectOS(),
app_bundle_id: undefined,
};
}
if ((globalThis as any).X_SDK_NAME === undefined) {
(globalThis as any).X_SDK_NAME = packageJson.name;
(globalThis as any).X_SDK_PLATFORM = "browser";
(globalThis as any).X_SDK_VERSION = packageJson.version;
(globalThis as any).X_SDK_OS = detectOS();
(globalThis as any).APP_BUNDLE_ID = undefined;
}
return {
x_sdk_name: (globalThis as any).X_SDK_NAME,
x_sdk_platform: (globalThis as any).X_SDK_PLATFORM,
x_sdk_version: (globalThis as any).X_SDK_VERSION,
x_sdk_os: (globalThis as any).X_SDK_OS,
app_bundle_id: (globalThis as any).APP_BUNDLE_ID,
};
}
+1 -1
View File
@@ -41,7 +41,7 @@ export function isIOS(): boolean {
/**
* @internal
*/
function detectOS() {
export function detectOS() {
const env = detectEnv();
return env?.os ? env.os : undefined;
}
Binary file not shown.
+10
View File
@@ -1,5 +1,15 @@
# @thirdweb-dev/sdk
## 4.0.31
### Patch Changes
- [#2224](https://github.com/thirdweb-dev/js/pull/2224) [`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - Adds correct reporing headers
- Updated dependencies [[`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a), [`771bd1e1`](https://github.com/thirdweb-dev/js/commit/771bd1e1b280681992195103c22aafa1fb98f7e0)]:
- @thirdweb-dev/storage@2.0.9
- @thirdweb-dev/chains@0.1.68
## 4.0.30
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/sdk",
"version": "4.0.30",
"version": "4.0.31",
"description": "The main thirdweb SDK.",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/sdk",
"license": "Apache-2.0",
@@ -0,0 +1,338 @@
/**
* @internal
*
* The code below comes from the package https://github.com/DamonOehlman/detect-browser
*/
export type DetectedInfoType =
| "browser"
| "node"
| "bot-device"
| "bot"
| "react-native";
interface DetectedInfo<
T extends DetectedInfoType,
N extends string,
O,
V = null,
> {
readonly type: T;
readonly name: N;
readonly version: V;
readonly os: O;
}
export class BrowserInfo
implements DetectedInfo<"browser", Browser, OperatingSystem | null, string>
{
public readonly type = "browser";
constructor(
public readonly name: Browser,
public readonly version: string,
public readonly os: OperatingSystem | null,
) {}
}
export class NodeInfo
implements DetectedInfo<"node", "node", NodeJS.Platform, string>
{
public readonly type = "node";
public readonly name = "node";
public readonly os: NodeJS.Platform = process.platform;
constructor(public readonly version: string) {}
}
export class SearchBotDeviceInfo
implements
DetectedInfo<"bot-device", Browser, OperatingSystem | null, string>
{
public readonly type = "bot-device";
constructor(
public readonly name: Browser,
public readonly version: string,
public readonly os: OperatingSystem | null,
public readonly bot: string,
) {}
}
export class BotInfo implements DetectedInfo<"bot", "bot", null, null> {
public readonly type = "bot";
public readonly bot = true; // NOTE: deprecated test name instead
public readonly name = "bot";
public readonly version: null = null;
public readonly os: null = null;
}
export class ReactNativeInfo
implements DetectedInfo<"react-native", "react-native", null, null>
{
public readonly type = "react-native";
public readonly name = "react-native";
public readonly version: null = null;
public readonly os: null = null;
}
export type Browser =
| "aol"
| "edge"
| "edge-ios"
| "yandexbrowser"
| "kakaotalk"
| "samsung"
| "silk"
| "miui"
| "beaker"
| "edge-chromium"
| "chrome"
| "chromium-webview"
| "phantomjs"
| "crios"
| "firefox"
| "fxios"
| "opera-mini"
| "opera"
| "pie"
| "netfront"
| "ie"
| "bb10"
| "android"
| "ios"
| "safari"
| "facebook"
| "instagram"
| "ios-webview"
| "curl"
| "searchbot";
export type OperatingSystem =
| "iOS"
| "Android OS"
| "BlackBerry OS"
| "Windows Mobile"
| "Amazon OS"
| "Windows 3.11"
| "Windows 95"
| "Windows 98"
| "Windows 2000"
| "Windows XP"
| "Windows Server 2003"
| "Windows Vista"
| "Windows 7"
| "Windows 8"
| "Windows 8.1"
| "Windows 10"
| "Windows ME"
| "Windows CE"
| "Open BSD"
| "Sun OS"
| "Linux"
| "Mac OS"
| "QNX"
| "BeOS"
| "OS/2"
| "Chrome OS";
type UserAgentRule = [Browser, RegExp];
type UserAgentMatch = [Browser, RegExpExecArray] | false;
type OperatingSystemRule = [OperatingSystem, RegExp];
// tslint:disable-next-line:max-line-length
const SEARCHBOX_UA_REGEX =
/alexa|bot|crawl(er|ing)|facebookexternalhit|feedburner|google web preview|nagios|postrank|pingdom|slurp|spider|yahoo!|yandex/;
const SEARCHBOT_OS_REGEX =
/(nuhk|curl|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask\ Jeeves\/Teoma|ia_archiver)/;
const REQUIRED_VERSION_PARTS = 3;
const userAgentRules: UserAgentRule[] = [
["aol", /AOLShield\/([0-9\._]+)/],
["edge", /Edge\/([0-9\._]+)/],
["edge-ios", /EdgiOS\/([0-9\._]+)/],
["yandexbrowser", /YaBrowser\/([0-9\._]+)/],
["kakaotalk", /KAKAOTALK\s([0-9\.]+)/],
["samsung", /SamsungBrowser\/([0-9\.]+)/],
["silk", /\bSilk\/([0-9._-]+)\b/],
["miui", /MiuiBrowser\/([0-9\.]+)$/],
["beaker", /BeakerBrowser\/([0-9\.]+)/],
["edge-chromium", /EdgA?\/([0-9\.]+)/],
[
"chromium-webview",
/(?!Chrom.*OPR)wv\).*Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/,
],
["chrome", /(?!Chrom.*OPR)Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/],
["phantomjs", /PhantomJS\/([0-9\.]+)(:?\s|$)/],
["crios", /CriOS\/([0-9\.]+)(:?\s|$)/],
["firefox", /Firefox\/([0-9\.]+)(?:\s|$)/],
["fxios", /FxiOS\/([0-9\.]+)/],
["opera-mini", /Opera Mini.*Version\/([0-9\.]+)/],
["opera", /Opera\/([0-9\.]+)(?:\s|$)/],
["opera", /OPR\/([0-9\.]+)(:?\s|$)/],
["pie", /^Microsoft Pocket Internet Explorer\/(\d+\.\d+)$/],
[
"pie",
/^Mozilla\/\d\.\d+\s\(compatible;\s(?:MSP?IE|MSInternet Explorer) (\d+\.\d+);.*Windows CE.*\)$/,
],
["netfront", /^Mozilla\/\d\.\d+.*NetFront\/(\d.\d)/],
["ie", /Trident\/7\.0.*rv\:([0-9\.]+).*\).*Gecko$/],
["ie", /MSIE\s([0-9\.]+);.*Trident\/[4-7].0/],
["ie", /MSIE\s(7\.0)/],
["bb10", /BB10;\sTouch.*Version\/([0-9\.]+)/],
["android", /Android\s([0-9\.]+)/],
["ios", /Version\/([0-9\._]+).*Mobile.*Safari.*/],
["safari", /Version\/([0-9\._]+).*Safari/],
["facebook", /FB[AS]V\/([0-9\.]+)/],
["instagram", /Instagram\s([0-9\.]+)/],
["ios-webview", /AppleWebKit\/([0-9\.]+).*Mobile/],
["ios-webview", /AppleWebKit\/([0-9\.]+).*Gecko\)$/],
["curl", /^curl\/([0-9\.]+)$/],
["searchbot", SEARCHBOX_UA_REGEX],
];
const operatingSystemRules: OperatingSystemRule[] = [
["iOS", /iP(hone|od|ad)/],
["Android OS", /Android/],
["BlackBerry OS", /BlackBerry|BB10/],
["Windows Mobile", /IEMobile/],
["Amazon OS", /Kindle/],
["Windows 3.11", /Win16/],
["Windows 95", /(Windows 95)|(Win95)|(Windows_95)/],
["Windows 98", /(Windows 98)|(Win98)/],
["Windows 2000", /(Windows NT 5.0)|(Windows 2000)/],
["Windows XP", /(Windows NT 5.1)|(Windows XP)/],
["Windows Server 2003", /(Windows NT 5.2)/],
["Windows Vista", /(Windows NT 6.0)/],
["Windows 7", /(Windows NT 6.1)/],
["Windows 8", /(Windows NT 6.2)/],
["Windows 8.1", /(Windows NT 6.3)/],
["Windows 10", /(Windows NT 10.0)/],
["Windows ME", /Windows ME/],
["Windows CE", /Windows CE|WinCE|Microsoft Pocket Internet Explorer/],
["Open BSD", /OpenBSD/],
["Sun OS", /SunOS/],
["Chrome OS", /CrOS/],
["Linux", /(Linux)|(X11)/],
["Mac OS", /(Mac_PowerPC)|(Macintosh)/],
["QNX", /QNX/],
["BeOS", /BeOS/],
["OS/2", /OS\/2/],
];
export function detect(
userAgent?: string,
):
| BrowserInfo
| SearchBotDeviceInfo
| BotInfo
| NodeInfo
| ReactNativeInfo
| null {
if (!!userAgent) {
return parseUserAgent(userAgent);
}
if (
typeof document === "undefined" &&
typeof navigator !== "undefined" &&
navigator.product === "ReactNative"
) {
return new ReactNativeInfo();
}
if (typeof navigator !== "undefined") {
return parseUserAgent(navigator.userAgent);
}
return getNodeVersion();
}
function matchUserAgent(ua: string): UserAgentMatch {
// opted for using reduce here rather than Array#first with a regex.test call
// this is primarily because using the reduce we only perform the regex
// execution once rather than once for the test and for the exec again below
// probably something that needs to be benchmarked though
return (
ua !== "" &&
userAgentRules.reduce<UserAgentMatch>(
(matched: UserAgentMatch, [browser, regex]) => {
if (matched) {
return matched;
}
const uaMatch = regex.exec(ua);
return !!uaMatch && [browser, uaMatch];
},
false,
)
);
}
export function browserName(ua: string): Browser | null {
const data = matchUserAgent(ua);
return data ? data[0] : null;
}
export function parseUserAgent(
ua: string,
): BrowserInfo | SearchBotDeviceInfo | BotInfo | null {
const matchedRule: UserAgentMatch = matchUserAgent(ua);
if (!matchedRule) {
return null;
}
const [name, match] = matchedRule;
if (name === "searchbot") {
return new BotInfo();
}
// Do not use RegExp for split operation as some browser do not support it (See: http://blog.stevenlevithan.com/archives/cross-browser-split)
let versionParts =
match[1] && match[1].split(".").join("_").split("_").slice(0, 3);
if (versionParts) {
if (versionParts.length < REQUIRED_VERSION_PARTS) {
versionParts = [
...versionParts,
...createVersionParts(REQUIRED_VERSION_PARTS - versionParts.length),
];
}
} else {
versionParts = [];
}
const version = versionParts.join(".");
const os = detectOS(ua);
const searchBotMatch = SEARCHBOT_OS_REGEX.exec(ua);
if (searchBotMatch && searchBotMatch[1]) {
return new SearchBotDeviceInfo(name, version, os, searchBotMatch[1]);
}
return new BrowserInfo(name, version, os);
}
export function detectOS(ua: string): OperatingSystem | null {
for (let ii = 0, count = operatingSystemRules.length; ii < count; ii++) {
const result = operatingSystemRules[ii];
if (!result) {
continue;
}
const [os, regex] = result;
const match = regex.exec(ua);
if (match) {
return os;
}
}
return null;
}
export function getNodeVersion(): NodeInfo | null {
const isNode = typeof process !== "undefined" && process.version;
return isNode ? new NodeInfo(process.version.slice(1)) : null;
}
function createVersionParts(count: number): string[] {
const output = [];
for (let ii = 0; ii < count; ii++) {
output.push("0");
}
return output;
}
+68
View File
@@ -0,0 +1,68 @@
import pkg from "../../../package.json";
import { getOperatingSystem } from "./os";
export function setAnalyticsHeaders(headers: Record<string, string>) {
const globals = getAnalyticsGlobals();
headers["x-sdk-version"] = globals.x_sdk_version;
headers["x-sdk-name"] = globals.x_sdk_name;
headers["x-sdk-platform"] = globals.x_sdk_platform;
headers["x-sdk-os"] = globals.x_sdk_os;
}
export function setAnalyticsHeadersForXhr(xhr: XMLHttpRequest) {
const globals = getAnalyticsGlobals();
xhr.setRequestHeader("x-sdk-version", globals.x_sdk_version);
xhr.setRequestHeader("x-sdk-os", globals.x_sdk_os);
xhr.setRequestHeader("x-sdk-name", globals.x_sdk_name);
xhr.setRequestHeader("x-sdk-platform", globals.x_sdk_platform);
xhr.setRequestHeader("x-bundle-id", globals.app_bundle_id);
}
export function getAnalyticsHeaders() {
const globals = getAnalyticsGlobals();
return {
"x-sdk-version": globals.x_sdk_version,
"x-sdk-os": globals.x_sdk_os,
"x-sdk-name": globals.x_sdk_name,
"x-sdk-platform": globals.x_sdk_platform,
"x-bundle-id": globals.app_bundle_id,
};
}
export function getAnalyticsGlobals() {
if (typeof globalThis === "undefined") {
return {
x_sdk_name: pkg.name,
x_sdk_platform: getPlatform(),
x_sdk_version: pkg.version,
x_sdk_os: getOperatingSystem(),
app_bundle_id: undefined,
};
}
if ((globalThis as any).X_SDK_NAME === undefined) {
(globalThis as any).X_SDK_NAME = pkg.name;
(globalThis as any).X_SDK_PLATFORM = getPlatform();
(globalThis as any).X_SDK_VERSION = pkg.version;
(globalThis as any).X_SDK_OS = getOperatingSystem();
(globalThis as any).APP_BUNDLE_ID = undefined;
}
return {
x_sdk_name: (globalThis as any).X_SDK_NAME,
x_sdk_platform: (globalThis as any).X_SDK_PLATFORM,
x_sdk_version: (globalThis as any).X_SDK_VERSION,
x_sdk_os: (globalThis as any).X_SDK_OS,
app_bundle_id: (globalThis as any).APP_BUNDLE_ID || "", // if react, this will be empty
};
}
export function getPlatform() {
return typeof navigator !== "undefined" && navigator.product === "ReactNative"
? "mobile"
: typeof window !== "undefined"
? "browser"
: "node";
}
+13
View File
@@ -0,0 +1,13 @@
import { detectOS } from "./detect-browser";
export function getOperatingSystem() {
if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
return "";
} else if (typeof window !== "undefined") {
const userAgent = navigator.userAgent;
return detectOS(userAgent) || "";
} else {
return process.platform;
}
}
@@ -14,6 +14,7 @@ import { constructAbiFromBytecode } from "./feature-detection/getAllDetectedFeat
import { SDKOptions } from "../schema/sdk-options";
import { Polygon } from "@thirdweb-dev/chains";
import { createLruCache } from "./utils";
import { getAnalyticsHeaders } from "../../core/utils/headers";
const CONTRACT_RESOLVER_BASE_URL = "https://contract.thirdweb.com/metadata";
@@ -68,6 +69,11 @@ export async function fetchContractMetadataFromAddress(
try {
const response = await fetch(
`${CONTRACT_RESOLVER_BASE_URL}/${chainId}/${address}`,
{
headers: {
...getAnalyticsHeaders(),
},
},
);
if (response.ok) {
const resolvedData = await response.json();
+2 -3
View File
@@ -474,9 +474,8 @@ async function fetchDeployBytecodeFromPublishedContractMetadata(
getChainProvider("polygon", {}),
) as ContractPublisher;
const publishedMetadataUri = await contract.getPublishedUriFromCompilerUri(
compilerMetaUri,
);
const publishedMetadataUri =
await contract.getPublishedUriFromCompilerUri(compilerMetaUri);
if (publishedMetadataUri.length === 0) {
throw Error(
`Could not resolve published metadata URI from ${compilerMetaUri}`,
+6 -14
View File
@@ -8,9 +8,8 @@ import { getValidChainRPCs } from "@thirdweb-dev/chains";
import type { Chain } from "@thirdweb-dev/chains";
import { providers } from "ethers";
import type { Signer } from "ethers";
import pkg from "../../../package.json";
import { isBrowser } from "@thirdweb-dev/storage";
import { sha256HexSync } from "@thirdweb-dev/crypto";
import { setAnalyticsHeaders } from "../../core/utils/headers";
/**
* @internal
@@ -252,6 +251,8 @@ export function getProviderFromRpcUrl(
headers["x-authorize-wallet"] = "true";
}
setAnalyticsHeaders(headers);
const bundleId =
typeof globalThis !== "undefined" && "APP_BUNDLE_ID" in globalThis
? ((globalThis as any).APP_BUNDLE_ID as string)
@@ -259,16 +260,6 @@ export function getProviderFromRpcUrl(
if (!rpcUrl.includes("bundleId")) {
rpcUrl = rpcUrl + (bundleId ? `?bundleId=${bundleId}` : "");
}
headers["x-sdk-version"] = pkg.version;
headers["x-sdk-name"] = pkg.name;
headers["x-sdk-platform"] = bundleId
? "react-native"
: isBrowser()
? (window as any).bridge !== undefined
? "webGL"
: "browser"
: "node";
}
const match = rpcUrl.match(/^(ws|http)s?:/i);
// Try the JSON batch provider if available
@@ -284,7 +275,7 @@ export function getProviderFromRpcUrl(
if (existingProvider) {
return existingProvider;
}
// TODO: remove below `skipFetchSetup` logic when ethers.js v6 support arrives
let _skipFetchSetup = false;
if (
@@ -292,7 +283,8 @@ export function getProviderFromRpcUrl(
"TW_SKIP_FETCH_SETUP" in globalThis &&
typeof (globalThis as any).TW_SKIP_FETCH_SETUP === "boolean"
) {
_skipFetchSetup = (globalThis as any).TW_SKIP_FETCH_SETUP as boolean;
_skipFetchSetup = (globalThis as any)
.TW_SKIP_FETCH_SETUP as boolean;
}
// Otherwise, create a new provider on the specific network
+10 -15
View File
@@ -592,9 +592,8 @@ export class ThirdwebSDK extends RPCConnectionHandler {
} catch (e) {
// fallback to
// try resolving the contract type (legacy contracts)
const resolvedContractType = await this.resolveContractType(
resolvedAddress,
);
const resolvedContractType =
await this.resolveContractType(resolvedAddress);
if (resolvedContractType && resolvedContractType !== "custom") {
// otherwise if it's a prebuilt contract we can just use the contract type
const contractAbi = await PREBUILT_CONTRACTS_MAP[
@@ -721,9 +720,8 @@ export class ThirdwebSDK extends RPCConnectionHandler {
walletAddress: AddressOrEns,
chains: Chain[] = defaultChains,
): Promise<ContractWithMetadata[]> {
const contracts = await this.multiChainRegistry.getContractAddresses(
walletAddress,
);
const contracts =
await this.multiChainRegistry.getContractAddresses(walletAddress);
const chainMap = chains.reduce(
(acc, chain) => {
@@ -1019,9 +1017,8 @@ export class ContractDeployer extends RPCConnectionHandler {
metadata: NFTContractDeployMetadata,
options?: DeployOptions,
): Promise<DeployTransaction> => {
const parsedMetadata = await LoyaltyCardContractDeploy.parseAsync(
metadata,
);
const parsedMetadata =
await LoyaltyCardContractDeploy.parseAsync(metadata);
const contractURI = await this.storage.upload(parsedMetadata);
const trustedForwarders: string[] = [];
@@ -1078,9 +1075,8 @@ export class ContractDeployer extends RPCConnectionHandler {
metadata: OpenEditionContractDeployMetadata,
options?: DeployOptions,
): Promise<DeployTransaction> => {
const parsedMetadata = await DropErc721ContractSchema.deploy.parseAsync(
metadata,
);
const parsedMetadata =
await DropErc721ContractSchema.deploy.parseAsync(metadata);
const contractURI = await this.storage.upload(parsedMetadata);
const trustedForwarders: string[] = [];
@@ -1938,9 +1934,8 @@ export class ContractDeployer extends RPCConnectionHandler {
?.factoryDeploymentData?.customFactoryInput?.customFactoryAddresses[
chainId
] as AddressOrEns;
const resolvedCustomFactoryAddress = await resolveAddress(
customFactoryAddress,
);
const resolvedCustomFactoryAddress =
await resolveAddress(customFactoryAddress);
invariant(
resolvedCustomFactoryAddress,
Binary file not shown.
+6
View File
@@ -1,5 +1,11 @@
# @thirdweb-dev/storage
## 2.0.9
### Patch Changes
- [#2224](https://github.com/thirdweb-dev/js/pull/2224) [`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - Adds correct reporing headers
## 2.0.8
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/storage",
"version": "2.0.8",
"version": "2.0.9",
"main": "dist/thirdweb-dev-storage.cjs.js",
"module": "dist/thirdweb-dev-storage.esm.js",
"exports": {
@@ -1,12 +1,12 @@
import { isTwGatewayUrl } from "../../common/urls";
import { isBrowser, replaceSchemeWithGatewayUrl } from "../../common/utils";
import { replaceSchemeWithGatewayUrl } from "../../common/utils";
import {
GatewayUrls,
IStorageDownloader,
IpfsDownloaderOptions,
SingleDownloadOptions,
} from "../../types";
import pkg from "../../../package.json";
import { getAnalyticsGlobals, setAnalyticsHeaders } from "../../utils/headers";
/**
* Default downloader used - handles downloading from all schemes specified in the gateway URLs configuration.
@@ -86,10 +86,7 @@ export class StorageDownloader implements IStorageDownloader {
let headers: HeadersInit = {};
if (isTwGatewayUrl(resolvedUri)) {
const bundleId =
typeof globalThis !== "undefined" && "APP_BUNDLE_ID" in globalThis
? ((globalThis as any).APP_BUNDLE_ID as string)
: undefined;
const bundleId = getAnalyticsGlobals().app_bundle_id;
if (this.secretKey) {
headers = { "x-secret-key": this.secretKey };
} else if (this.clientId) {
@@ -126,15 +123,7 @@ export class StorageDownloader implements IStorageDownloader {
headers["x-authorize-wallet"] = "true";
}
headers["x-sdk-version"] = pkg.version;
headers["x-sdk-name"] = pkg.name;
headers["x-sdk-platform"] = bundleId
? "react-native"
: isBrowser()
? (window as any).bridge !== undefined
? "webGL"
: "browser"
: "node";
setAnalyticsHeaders(headers);
}
if (isTooManyRequests(resolvedUri)) {
@@ -12,7 +12,10 @@ import {
IStorageUploader,
} from "../../types";
import FormData from "form-data";
import pkg from "../../../package.json";
import {
setAnalyticsHeaders,
setAnalyticsHeadersForXhr,
} from "../../utils/headers";
/**
* Default uploader used - handles uploading arbitrary data to IPFS
@@ -274,26 +277,7 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
xhr.setRequestHeader("x-client-id", this.clientId);
}
const bundleId =
typeof globalThis !== "undefined" && "APP_BUNDLE_ID" in globalThis
? ((globalThis as any).APP_BUNDLE_ID as string)
: undefined;
if (bundleId) {
xhr.setRequestHeader("x-bundle-id", bundleId);
}
xhr.setRequestHeader("x-sdk-version", pkg.version);
xhr.setRequestHeader("x-sdk-name", pkg.name);
xhr.setRequestHeader(
"x-sdk-platform",
bundleId
? "react-native"
: isBrowser()
? (window as any).bridge !== undefined
? "webGL"
: "browser"
: "node",
);
setAnalyticsHeadersForXhr(xhr);
// if we have a authorization token on global context then add that to the headers, this is for the dashboard.
if (
@@ -341,11 +325,6 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
headers["x-client-id"] = this.clientId;
}
// if we have a bundle id on global context then add that to the headers
if (typeof globalThis !== "undefined" && "APP_BUNDLE_ID" in globalThis) {
headers["x-bundle-id"] = (globalThis as any).APP_BUNDLE_ID as string;
}
// if we have a authorization token on global context then add that to the headers, this is for the dashboard.
if (
typeof globalThis !== "undefined" &&
@@ -369,6 +348,8 @@ export class IpfsUploader implements IStorageUploader<IpfsUploadBatchOptions> {
headers["x-authorize-wallet"] = "true";
}
setAnalyticsHeaders(headers);
const res = await fetch(`${this.uploadServerUrl}/ipfs/upload`, {
method: "POST",
headers: {
@@ -0,0 +1,338 @@
/**
* @internal
*
* The code below comes from the package https://github.com/DamonOehlman/detect-browser
*/
export type DetectedInfoType =
| "browser"
| "node"
| "bot-device"
| "bot"
| "react-native";
interface DetectedInfo<
T extends DetectedInfoType,
N extends string,
O,
V = null,
> {
readonly type: T;
readonly name: N;
readonly version: V;
readonly os: O;
}
export class BrowserInfo
implements DetectedInfo<"browser", Browser, OperatingSystem | null, string>
{
public readonly type = "browser";
constructor(
public readonly name: Browser,
public readonly version: string,
public readonly os: OperatingSystem | null,
) {}
}
export class NodeInfo
implements DetectedInfo<"node", "node", NodeJS.Platform, string>
{
public readonly type = "node";
public readonly name = "node";
public readonly os: NodeJS.Platform = process.platform;
constructor(public readonly version: string) {}
}
export class SearchBotDeviceInfo
implements
DetectedInfo<"bot-device", Browser, OperatingSystem | null, string>
{
public readonly type = "bot-device";
constructor(
public readonly name: Browser,
public readonly version: string,
public readonly os: OperatingSystem | null,
public readonly bot: string,
) {}
}
export class BotInfo implements DetectedInfo<"bot", "bot", null, null> {
public readonly type = "bot";
public readonly bot = true; // NOTE: deprecated test name instead
public readonly name = "bot";
public readonly version: null = null;
public readonly os: null = null;
}
export class ReactNativeInfo
implements DetectedInfo<"react-native", "react-native", null, null>
{
public readonly type = "react-native";
public readonly name = "react-native";
public readonly version: null = null;
public readonly os: null = null;
}
export type Browser =
| "aol"
| "edge"
| "edge-ios"
| "yandexbrowser"
| "kakaotalk"
| "samsung"
| "silk"
| "miui"
| "beaker"
| "edge-chromium"
| "chrome"
| "chromium-webview"
| "phantomjs"
| "crios"
| "firefox"
| "fxios"
| "opera-mini"
| "opera"
| "pie"
| "netfront"
| "ie"
| "bb10"
| "android"
| "ios"
| "safari"
| "facebook"
| "instagram"
| "ios-webview"
| "curl"
| "searchbot";
export type OperatingSystem =
| "iOS"
| "Android OS"
| "BlackBerry OS"
| "Windows Mobile"
| "Amazon OS"
| "Windows 3.11"
| "Windows 95"
| "Windows 98"
| "Windows 2000"
| "Windows XP"
| "Windows Server 2003"
| "Windows Vista"
| "Windows 7"
| "Windows 8"
| "Windows 8.1"
| "Windows 10"
| "Windows ME"
| "Windows CE"
| "Open BSD"
| "Sun OS"
| "Linux"
| "Mac OS"
| "QNX"
| "BeOS"
| "OS/2"
| "Chrome OS";
type UserAgentRule = [Browser, RegExp];
type UserAgentMatch = [Browser, RegExpExecArray] | false;
type OperatingSystemRule = [OperatingSystem, RegExp];
// tslint:disable-next-line:max-line-length
const SEARCHBOX_UA_REGEX =
/alexa|bot|crawl(er|ing)|facebookexternalhit|feedburner|google web preview|nagios|postrank|pingdom|slurp|spider|yahoo!|yandex/;
const SEARCHBOT_OS_REGEX =
/(nuhk|curl|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask\ Jeeves\/Teoma|ia_archiver)/;
const REQUIRED_VERSION_PARTS = 3;
const userAgentRules: UserAgentRule[] = [
["aol", /AOLShield\/([0-9\._]+)/],
["edge", /Edge\/([0-9\._]+)/],
["edge-ios", /EdgiOS\/([0-9\._]+)/],
["yandexbrowser", /YaBrowser\/([0-9\._]+)/],
["kakaotalk", /KAKAOTALK\s([0-9\.]+)/],
["samsung", /SamsungBrowser\/([0-9\.]+)/],
["silk", /\bSilk\/([0-9._-]+)\b/],
["miui", /MiuiBrowser\/([0-9\.]+)$/],
["beaker", /BeakerBrowser\/([0-9\.]+)/],
["edge-chromium", /EdgA?\/([0-9\.]+)/],
[
"chromium-webview",
/(?!Chrom.*OPR)wv\).*Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/,
],
["chrome", /(?!Chrom.*OPR)Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/],
["phantomjs", /PhantomJS\/([0-9\.]+)(:?\s|$)/],
["crios", /CriOS\/([0-9\.]+)(:?\s|$)/],
["firefox", /Firefox\/([0-9\.]+)(?:\s|$)/],
["fxios", /FxiOS\/([0-9\.]+)/],
["opera-mini", /Opera Mini.*Version\/([0-9\.]+)/],
["opera", /Opera\/([0-9\.]+)(?:\s|$)/],
["opera", /OPR\/([0-9\.]+)(:?\s|$)/],
["pie", /^Microsoft Pocket Internet Explorer\/(\d+\.\d+)$/],
[
"pie",
/^Mozilla\/\d\.\d+\s\(compatible;\s(?:MSP?IE|MSInternet Explorer) (\d+\.\d+);.*Windows CE.*\)$/,
],
["netfront", /^Mozilla\/\d\.\d+.*NetFront\/(\d.\d)/],
["ie", /Trident\/7\.0.*rv\:([0-9\.]+).*\).*Gecko$/],
["ie", /MSIE\s([0-9\.]+);.*Trident\/[4-7].0/],
["ie", /MSIE\s(7\.0)/],
["bb10", /BB10;\sTouch.*Version\/([0-9\.]+)/],
["android", /Android\s([0-9\.]+)/],
["ios", /Version\/([0-9\._]+).*Mobile.*Safari.*/],
["safari", /Version\/([0-9\._]+).*Safari/],
["facebook", /FB[AS]V\/([0-9\.]+)/],
["instagram", /Instagram\s([0-9\.]+)/],
["ios-webview", /AppleWebKit\/([0-9\.]+).*Mobile/],
["ios-webview", /AppleWebKit\/([0-9\.]+).*Gecko\)$/],
["curl", /^curl\/([0-9\.]+)$/],
["searchbot", SEARCHBOX_UA_REGEX],
];
const operatingSystemRules: OperatingSystemRule[] = [
["iOS", /iP(hone|od|ad)/],
["Android OS", /Android/],
["BlackBerry OS", /BlackBerry|BB10/],
["Windows Mobile", /IEMobile/],
["Amazon OS", /Kindle/],
["Windows 3.11", /Win16/],
["Windows 95", /(Windows 95)|(Win95)|(Windows_95)/],
["Windows 98", /(Windows 98)|(Win98)/],
["Windows 2000", /(Windows NT 5.0)|(Windows 2000)/],
["Windows XP", /(Windows NT 5.1)|(Windows XP)/],
["Windows Server 2003", /(Windows NT 5.2)/],
["Windows Vista", /(Windows NT 6.0)/],
["Windows 7", /(Windows NT 6.1)/],
["Windows 8", /(Windows NT 6.2)/],
["Windows 8.1", /(Windows NT 6.3)/],
["Windows 10", /(Windows NT 10.0)/],
["Windows ME", /Windows ME/],
["Windows CE", /Windows CE|WinCE|Microsoft Pocket Internet Explorer/],
["Open BSD", /OpenBSD/],
["Sun OS", /SunOS/],
["Chrome OS", /CrOS/],
["Linux", /(Linux)|(X11)/],
["Mac OS", /(Mac_PowerPC)|(Macintosh)/],
["QNX", /QNX/],
["BeOS", /BeOS/],
["OS/2", /OS\/2/],
];
export function detect(
userAgent?: string,
):
| BrowserInfo
| SearchBotDeviceInfo
| BotInfo
| NodeInfo
| ReactNativeInfo
| null {
if (!!userAgent) {
return parseUserAgent(userAgent);
}
if (
typeof document === "undefined" &&
typeof navigator !== "undefined" &&
navigator.product === "ReactNative"
) {
return new ReactNativeInfo();
}
if (typeof navigator !== "undefined") {
return parseUserAgent(navigator.userAgent);
}
return getNodeVersion();
}
function matchUserAgent(ua: string): UserAgentMatch {
// opted for using reduce here rather than Array#first with a regex.test call
// this is primarily because using the reduce we only perform the regex
// execution once rather than once for the test and for the exec again below
// probably something that needs to be benchmarked though
return (
ua !== "" &&
userAgentRules.reduce<UserAgentMatch>(
(matched: UserAgentMatch, [browser, regex]) => {
if (matched) {
return matched;
}
const uaMatch = regex.exec(ua);
return !!uaMatch && [browser, uaMatch];
},
false,
)
);
}
export function browserName(ua: string): Browser | null {
const data = matchUserAgent(ua);
return data ? data[0] : null;
}
export function parseUserAgent(
ua: string,
): BrowserInfo | SearchBotDeviceInfo | BotInfo | null {
const matchedRule: UserAgentMatch = matchUserAgent(ua);
if (!matchedRule) {
return null;
}
const [name, match] = matchedRule;
if (name === "searchbot") {
return new BotInfo();
}
// Do not use RegExp for split operation as some browser do not support it (See: http://blog.stevenlevithan.com/archives/cross-browser-split)
let versionParts =
match[1] && match[1].split(".").join("_").split("_").slice(0, 3);
if (versionParts) {
if (versionParts.length < REQUIRED_VERSION_PARTS) {
versionParts = [
...versionParts,
...createVersionParts(REQUIRED_VERSION_PARTS - versionParts.length),
];
}
} else {
versionParts = [];
}
const version = versionParts.join(".");
const os = detectOS(ua);
const searchBotMatch = SEARCHBOT_OS_REGEX.exec(ua);
if (searchBotMatch && searchBotMatch[1]) {
return new SearchBotDeviceInfo(name, version, os, searchBotMatch[1]);
}
return new BrowserInfo(name, version, os);
}
export function detectOS(ua: string): OperatingSystem | null {
for (let ii = 0, count = operatingSystemRules.length; ii < count; ii++) {
const result = operatingSystemRules[ii];
if (!result) {
continue;
}
const [os, regex] = result;
const match = regex.exec(ua);
if (match) {
return os;
}
}
return null;
}
export function getNodeVersion(): NodeInfo | null {
const isNode = typeof process !== "undefined" && process.version;
return isNode ? new NodeInfo(process.version.slice(1)) : null;
}
function createVersionParts(count: number): string[] {
const output = [];
for (let ii = 0; ii < count; ii++) {
output.push("0");
}
return output;
}
+68
View File
@@ -0,0 +1,68 @@
import pkg from "../../package.json";
import { getOperatingSystem } from "./os";
export function setAnalyticsHeaders(headers: Record<string, string>) {
const globals = getAnalyticsGlobals();
headers["x-sdk-version"] = globals.x_sdk_version;
headers["x-sdk-name"] = globals.x_sdk_name;
headers["x-sdk-platform"] = globals.x_sdk_platform;
headers["x-sdk-os"] = globals.x_sdk_os;
}
export function setAnalyticsHeadersForXhr(xhr: XMLHttpRequest) {
const globals = getAnalyticsGlobals();
xhr.setRequestHeader("x-sdk-version", globals.x_sdk_version);
xhr.setRequestHeader("x-sdk-os", globals.x_sdk_os);
xhr.setRequestHeader("x-sdk-name", globals.x_sdk_name);
xhr.setRequestHeader("x-sdk-platform", globals.x_sdk_platform);
xhr.setRequestHeader("x-bundle-id", globals.app_bundle_id);
}
export function getAnalyticsHeaders() {
const globals = getAnalyticsGlobals();
return {
"x-sdk-version": globals.x_sdk_version,
"x-sdk-os": globals.x_sdk_os,
"x-sdk-name": globals.x_sdk_name,
"x-sdk-platform": globals.x_sdk_platform,
"x-bundle-id": globals.app_bundle_id,
};
}
export function getAnalyticsGlobals() {
if (typeof globalThis === "undefined") {
return {
x_sdk_name: pkg.name,
x_sdk_platform: getPlatform(),
x_sdk_version: pkg.version,
x_sdk_os: getOperatingSystem(),
app_bundle_id: undefined,
};
}
if ((globalThis as any).X_SDK_NAME === undefined) {
(globalThis as any).X_SDK_NAME = pkg.name;
(globalThis as any).X_SDK_PLATFORM = getPlatform();
(globalThis as any).X_SDK_VERSION = pkg.version;
(globalThis as any).X_SDK_OS = getOperatingSystem();
(globalThis as any).APP_BUNDLE_ID = undefined;
}
return {
x_sdk_name: (globalThis as any).X_SDK_NAME,
x_sdk_platform: (globalThis as any).X_SDK_PLATFORM,
x_sdk_version: (globalThis as any).X_SDK_VERSION,
x_sdk_os: (globalThis as any).X_SDK_OS,
app_bundle_id: (globalThis as any).APP_BUNDLE_ID || "", // if react, this will be empty
};
}
export function getPlatform() {
return typeof navigator !== "undefined" && navigator.product === "ReactNative"
? "mobile"
: typeof window !== "undefined"
? "browser"
: "node";
}
+13
View File
@@ -0,0 +1,13 @@
import { detectOS } from "./detect-browser";
export function getOperatingSystem() {
if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
return "";
} else if (typeof window !== "undefined") {
const userAgent = navigator.userAgent;
return detectOS(userAgent) || "";
} else {
return process.platform;
}
}
Binary file not shown.
+15
View File
@@ -1,5 +1,20 @@
# @thirdweb-dev/unity-js-bridge
## 0.3.11
### Patch Changes
- [#2224](https://github.com/thirdweb-dev/js/pull/2224) [`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - Adds correct reporing headers
- [#2224](https://github.com/thirdweb-dev/js/pull/2224) [`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - Adds global sdk vars
- Updated dependencies [[`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a), [`771bd1e1`](https://github.com/thirdweb-dev/js/commit/771bd1e1b280681992195103c22aafa1fb98f7e0)]:
- @thirdweb-dev/storage@2.0.9
- @thirdweb-dev/wallets@2.4.6
- @thirdweb-dev/sdk@4.0.31
- @thirdweb-dev/chains@0.1.68
- @thirdweb-dev/auth@4.1.28
## 0.3.10
### Patch Changes
+2 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/unity-js-bridge",
"version": "0.3.10",
"version": "0.3.11",
"main": "dist/thirdweb-unity-bridge.js",
"repository": "https://github.com/thirdweb-dev/js/tree/main/packages/unity-js-bridge",
"license": "Apache-2.0",
@@ -18,6 +18,7 @@
"@thirdweb-dev/sdk": "workspace:*",
"@thirdweb-dev/storage": "workspace:*",
"@thirdweb-dev/wallets": "workspace:*",
"detect-browser": "^5.3.0",
"ethers": "^5.7.2"
},
"devDependencies": {
@@ -25,6 +25,7 @@ import {
getChainByChainId,
} from "@thirdweb-dev/chains";
import type { ContractInterface, Signer } from "ethers";
import { detect } from "detect-browser";
declare global {
interface Window {
@@ -132,6 +133,19 @@ class ThirdwebBridge implements TWBridge {
}
public initialize(chain: ChainIdOrName, options: string) {
if (typeof globalThis !== "undefined") {
let browser;
try {
browser = detect();
} catch {
console.warn("Failed to detect browser");
browser = undefined;
}
(globalThis as any).X_SDK_NAME = "UnitySDK_WebGL";
(globalThis as any).X_SDK_PLATFORM = "unity";
(globalThis as any).X_SDK_VERSION = "4.6.0";
(globalThis as any).X_SDK_OS = browser?.os ?? "unknown";
}
this.initializedChain = chain;
console.debug("thirdwebSDK initialization:", chain, options);
const sdkOptions = JSON.parse(options);
@@ -741,8 +755,7 @@ class ThirdwebBridge implements TWBridge {
return JSON.stringify({ result: result }, bigNumberReplacer);
}
public async smartWalletGetAllActiveSigners(){
public async smartWalletGetAllActiveSigners() {
if (!this.activeWallet) {
throw new Error("No wallet connected");
}
+10
View File
@@ -1,5 +1,15 @@
# @thirdweb-dev/wallets
## 2.4.6
### Patch Changes
- [#2224](https://github.com/thirdweb-dev/js/pull/2224) [`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a) Thanks [@0xFirekeeper](https://github.com/0xFirekeeper)! - Adds correct reporing headers
- Updated dependencies [[`3ba15366`](https://github.com/thirdweb-dev/js/commit/3ba15366be8b57e577b871c44e386978dc9d4a8a), [`771bd1e1`](https://github.com/thirdweb-dev/js/commit/771bd1e1b280681992195103c22aafa1fb98f7e0)]:
- @thirdweb-dev/sdk@4.0.31
- @thirdweb-dev/chains@0.1.68
## 2.4.5
### Patch Changes
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@thirdweb-dev/wallets",
"version": "2.4.5",
"version": "2.4.6",
"main": "dist/thirdweb-dev-wallets.cjs.js",
"module": "dist/thirdweb-dev-wallets.esm.js",
"browser": {
@@ -1,13 +1,10 @@
import { providers, utils } from "ethers";
import { UserOperationStruct } from "@account-abstraction/contracts";
import { isTwUrl } from "../../../utils/url";
import pkg from "../../../../../package.json";
import { hexlifyUserOp } from "./utils";
import { setAnalyticsHeaders } from "../../../utils/headers";
export const DEBUG = false; // TODO set as public flag
function isBrowser() {
return typeof window !== "undefined";
}
export class HttpRpcClient {
private readonly userOpJsonRpcProvider: providers.JsonRpcProvider;
@@ -69,15 +66,7 @@ export class HttpRpcClient {
headers["x-authorize-wallet"] = "true";
}
headers["x-sdk-version"] = pkg.version;
headers["x-sdk-name"] = pkg.name;
headers["x-sdk-platform"] = bundleId
? "react-native"
: isBrowser()
? (window as any).bridge !== undefined
? "webGL"
: "browser"
: "node";
setAnalyticsHeaders(headers);
}
this.userOpJsonRpcProvider = new providers.JsonRpcProvider(
@@ -3,6 +3,7 @@ import { hexlifyUserOp } from "./utils";
import { isTwUrl } from "../../../utils/url";
import { PaymasterAPI, PaymasterResult } from "../types";
import { DEBUG } from "./http-rpc-client";
import { setAnalyticsHeaders } from "../../../utils/headers";
export const SIG_SIZE = 65;
export const DUMMY_PAYMASTER_AND_DATA =
@@ -75,6 +76,8 @@ class VerifyingPaymasterAPI extends PaymasterAPI {
}`;
headers["x-authorize-wallet"] = "true";
}
setAnalyticsHeaders(headers);
}
// Ask the paymaster to sign the transaction and return a valid paymasterAndData value.
@@ -1,4 +1,5 @@
import { isWalletAnalyticsEnabled } from "./setWalletAnaltyicsEnabled";
import { getAnalyticsHeaders } from "./headers";
const ANALYTICS_ENDPOINT = "https://c.thirdweb.com/event";
@@ -25,6 +26,7 @@ export function track(args: {
headers: {
"Content-Type": "application/json",
"x-client-id": clientId,
...getAnalyticsHeaders(),
},
body: JSON.stringify(body),
});
+75
View File
@@ -0,0 +1,75 @@
import pkg from "../../../package.json";
import { getOperatingSystem } from "./os/os";
// const pkg: {
// version: string;
// name: string;
// // this is on purpose because we can't import package.json as a module as it is outside rootDir
// // eslint-disable-next-line @typescript-eslint/no-var-requires, better-tree-shaking/no-top-level-side-effects
// } = require("../../../package.json");
export function setAnalyticsHeaders(headers: Record<string, string>) {
const globals = getAnalyticsGlobals();
headers["x-sdk-version"] = globals.x_sdk_version;
headers["x-sdk-name"] = globals.x_sdk_name;
headers["x-sdk-platform"] = globals.x_sdk_platform;
headers["x-sdk-os"] = globals.x_sdk_os;
}
export function setAnalyticsHeadersForXhr(xhr: XMLHttpRequest) {
const globals = getAnalyticsGlobals();
xhr.setRequestHeader("x-sdk-version", globals.x_sdk_version);
xhr.setRequestHeader("x-sdk-os", globals.x_sdk_os);
xhr.setRequestHeader("x-sdk-name", globals.x_sdk_name);
xhr.setRequestHeader("x-sdk-platform", globals.x_sdk_platform);
xhr.setRequestHeader("x-bundle-id", globals.app_bundle_id);
}
export function getAnalyticsHeaders() {
const globals = getAnalyticsGlobals();
return {
"x-sdk-version": globals.x_sdk_version,
"x-sdk-os": globals.x_sdk_os,
"x-sdk-name": globals.x_sdk_name,
"x-sdk-platform": globals.x_sdk_platform,
"x-bundle-id": globals.app_bundle_id,
};
}
export function getAnalyticsGlobals() {
if (typeof globalThis === "undefined") {
return {
x_sdk_name: pkg.name,
x_sdk_platform: getPlatform(),
x_sdk_version: pkg.version,
x_sdk_os: getOperatingSystem(),
app_bundle_id: undefined,
};
}
if ((globalThis as any).X_SDK_NAME === undefined) {
(globalThis as any).X_SDK_NAME = pkg.name;
(globalThis as any).X_SDK_PLATFORM = getPlatform();
(globalThis as any).X_SDK_VERSION = pkg.version;
(globalThis as any).X_SDK_OS = getOperatingSystem();
(globalThis as any).APP_BUNDLE_ID = undefined;
}
return {
x_sdk_name: (globalThis as any).X_SDK_NAME,
x_sdk_platform: (globalThis as any).X_SDK_PLATFORM,
x_sdk_version: (globalThis as any).X_SDK_VERSION,
x_sdk_os: (globalThis as any).X_SDK_OS,
app_bundle_id: (globalThis as any).APP_BUNDLE_ID || "", // if not RN, this will be empty
};
}
export function getPlatform() {
return typeof navigator !== "undefined" && navigator.product === "ReactNative"
? "mobile"
: typeof window !== "undefined"
? "browser"
: "node";
}
@@ -0,0 +1,338 @@
/**
* @internal
*
* The code below comes from the package https://github.com/DamonOehlman/detect-browser
*/
export type DetectedInfoType =
| "browser"
| "node"
| "bot-device"
| "bot"
| "react-native";
interface DetectedInfo<
T extends DetectedInfoType,
N extends string,
O,
V = null,
> {
readonly type: T;
readonly name: N;
readonly version: V;
readonly os: O;
}
export class BrowserInfo
implements DetectedInfo<"browser", Browser, OperatingSystem | null, string>
{
public readonly type = "browser";
constructor(
public readonly name: Browser,
public readonly version: string,
public readonly os: OperatingSystem | null,
) {}
}
export class NodeInfo
implements DetectedInfo<"node", "node", NodeJS.Platform, string>
{
public readonly type = "node";
public readonly name = "node";
public readonly os: NodeJS.Platform = process.platform;
constructor(public readonly version: string) {}
}
export class SearchBotDeviceInfo
implements
DetectedInfo<"bot-device", Browser, OperatingSystem | null, string>
{
public readonly type = "bot-device";
constructor(
public readonly name: Browser,
public readonly version: string,
public readonly os: OperatingSystem | null,
public readonly bot: string,
) {}
}
export class BotInfo implements DetectedInfo<"bot", "bot", null, null> {
public readonly type = "bot";
public readonly bot = true; // NOTE: deprecated test name instead
public readonly name = "bot";
public readonly version: null = null;
public readonly os: null = null;
}
export class ReactNativeInfo
implements DetectedInfo<"react-native", "react-native", null, null>
{
public readonly type = "react-native";
public readonly name = "react-native";
public readonly version: null = null;
public readonly os: null = null;
}
export type Browser =
| "aol"
| "edge"
| "edge-ios"
| "yandexbrowser"
| "kakaotalk"
| "samsung"
| "silk"
| "miui"
| "beaker"
| "edge-chromium"
| "chrome"
| "chromium-webview"
| "phantomjs"
| "crios"
| "firefox"
| "fxios"
| "opera-mini"
| "opera"
| "pie"
| "netfront"
| "ie"
| "bb10"
| "android"
| "ios"
| "safari"
| "facebook"
| "instagram"
| "ios-webview"
| "curl"
| "searchbot";
export type OperatingSystem =
| "iOS"
| "Android OS"
| "BlackBerry OS"
| "Windows Mobile"
| "Amazon OS"
| "Windows 3.11"
| "Windows 95"
| "Windows 98"
| "Windows 2000"
| "Windows XP"
| "Windows Server 2003"
| "Windows Vista"
| "Windows 7"
| "Windows 8"
| "Windows 8.1"
| "Windows 10"
| "Windows ME"
| "Windows CE"
| "Open BSD"
| "Sun OS"
| "Linux"
| "Mac OS"
| "QNX"
| "BeOS"
| "OS/2"
| "Chrome OS";
type UserAgentRule = [Browser, RegExp];
type UserAgentMatch = [Browser, RegExpExecArray] | false;
type OperatingSystemRule = [OperatingSystem, RegExp];
// tslint:disable-next-line:max-line-length
const SEARCHBOX_UA_REGEX =
/alexa|bot|crawl(er|ing)|facebookexternalhit|feedburner|google web preview|nagios|postrank|pingdom|slurp|spider|yahoo!|yandex/;
const SEARCHBOT_OS_REGEX =
/(nuhk|curl|Googlebot|Yammybot|Openbot|Slurp|MSNBot|Ask\ Jeeves\/Teoma|ia_archiver)/;
const REQUIRED_VERSION_PARTS = 3;
const userAgentRules: UserAgentRule[] = [
["aol", /AOLShield\/([0-9\._]+)/],
["edge", /Edge\/([0-9\._]+)/],
["edge-ios", /EdgiOS\/([0-9\._]+)/],
["yandexbrowser", /YaBrowser\/([0-9\._]+)/],
["kakaotalk", /KAKAOTALK\s([0-9\.]+)/],
["samsung", /SamsungBrowser\/([0-9\.]+)/],
["silk", /\bSilk\/([0-9._-]+)\b/],
["miui", /MiuiBrowser\/([0-9\.]+)$/],
["beaker", /BeakerBrowser\/([0-9\.]+)/],
["edge-chromium", /EdgA?\/([0-9\.]+)/],
[
"chromium-webview",
/(?!Chrom.*OPR)wv\).*Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/,
],
["chrome", /(?!Chrom.*OPR)Chrom(?:e|ium)\/([0-9\.]+)(:?\s|$)/],
["phantomjs", /PhantomJS\/([0-9\.]+)(:?\s|$)/],
["crios", /CriOS\/([0-9\.]+)(:?\s|$)/],
["firefox", /Firefox\/([0-9\.]+)(?:\s|$)/],
["fxios", /FxiOS\/([0-9\.]+)/],
["opera-mini", /Opera Mini.*Version\/([0-9\.]+)/],
["opera", /Opera\/([0-9\.]+)(?:\s|$)/],
["opera", /OPR\/([0-9\.]+)(:?\s|$)/],
["pie", /^Microsoft Pocket Internet Explorer\/(\d+\.\d+)$/],
[
"pie",
/^Mozilla\/\d\.\d+\s\(compatible;\s(?:MSP?IE|MSInternet Explorer) (\d+\.\d+);.*Windows CE.*\)$/,
],
["netfront", /^Mozilla\/\d\.\d+.*NetFront\/(\d.\d)/],
["ie", /Trident\/7\.0.*rv\:([0-9\.]+).*\).*Gecko$/],
["ie", /MSIE\s([0-9\.]+);.*Trident\/[4-7].0/],
["ie", /MSIE\s(7\.0)/],
["bb10", /BB10;\sTouch.*Version\/([0-9\.]+)/],
["android", /Android\s([0-9\.]+)/],
["ios", /Version\/([0-9\._]+).*Mobile.*Safari.*/],
["safari", /Version\/([0-9\._]+).*Safari/],
["facebook", /FB[AS]V\/([0-9\.]+)/],
["instagram", /Instagram\s([0-9\.]+)/],
["ios-webview", /AppleWebKit\/([0-9\.]+).*Mobile/],
["ios-webview", /AppleWebKit\/([0-9\.]+).*Gecko\)$/],
["curl", /^curl\/([0-9\.]+)$/],
["searchbot", SEARCHBOX_UA_REGEX],
];
const operatingSystemRules: OperatingSystemRule[] = [
["iOS", /iP(hone|od|ad)/],
["Android OS", /Android/],
["BlackBerry OS", /BlackBerry|BB10/],
["Windows Mobile", /IEMobile/],
["Amazon OS", /Kindle/],
["Windows 3.11", /Win16/],
["Windows 95", /(Windows 95)|(Win95)|(Windows_95)/],
["Windows 98", /(Windows 98)|(Win98)/],
["Windows 2000", /(Windows NT 5.0)|(Windows 2000)/],
["Windows XP", /(Windows NT 5.1)|(Windows XP)/],
["Windows Server 2003", /(Windows NT 5.2)/],
["Windows Vista", /(Windows NT 6.0)/],
["Windows 7", /(Windows NT 6.1)/],
["Windows 8", /(Windows NT 6.2)/],
["Windows 8.1", /(Windows NT 6.3)/],
["Windows 10", /(Windows NT 10.0)/],
["Windows ME", /Windows ME/],
["Windows CE", /Windows CE|WinCE|Microsoft Pocket Internet Explorer/],
["Open BSD", /OpenBSD/],
["Sun OS", /SunOS/],
["Chrome OS", /CrOS/],
["Linux", /(Linux)|(X11)/],
["Mac OS", /(Mac_PowerPC)|(Macintosh)/],
["QNX", /QNX/],
["BeOS", /BeOS/],
["OS/2", /OS\/2/],
];
export function detect(
userAgent?: string,
):
| BrowserInfo
| SearchBotDeviceInfo
| BotInfo
| NodeInfo
| ReactNativeInfo
| null {
if (!!userAgent) {
return parseUserAgent(userAgent);
}
if (
typeof document === "undefined" &&
typeof navigator !== "undefined" &&
navigator.product === "ReactNative"
) {
return new ReactNativeInfo();
}
if (typeof navigator !== "undefined") {
return parseUserAgent(navigator.userAgent);
}
return getNodeVersion();
}
function matchUserAgent(ua: string): UserAgentMatch {
// opted for using reduce here rather than Array#first with a regex.test call
// this is primarily because using the reduce we only perform the regex
// execution once rather than once for the test and for the exec again below
// probably something that needs to be benchmarked though
return (
ua !== "" &&
userAgentRules.reduce<UserAgentMatch>(
(matched: UserAgentMatch, [browser, regex]) => {
if (matched) {
return matched;
}
const uaMatch = regex.exec(ua);
return !!uaMatch && [browser, uaMatch];
},
false,
)
);
}
export function browserName(ua: string): Browser | null {
const data = matchUserAgent(ua);
return data ? data[0] : null;
}
export function parseUserAgent(
ua: string,
): BrowserInfo | SearchBotDeviceInfo | BotInfo | null {
const matchedRule: UserAgentMatch = matchUserAgent(ua);
if (!matchedRule) {
return null;
}
const [name, match] = matchedRule;
if (name === "searchbot") {
return new BotInfo();
}
// Do not use RegExp for split operation as some browser do not support it (See: http://blog.stevenlevithan.com/archives/cross-browser-split)
let versionParts =
match[1] && match[1].split(".").join("_").split("_").slice(0, 3);
if (versionParts) {
if (versionParts.length < REQUIRED_VERSION_PARTS) {
versionParts = [
...versionParts,
...createVersionParts(REQUIRED_VERSION_PARTS - versionParts.length),
];
}
} else {
versionParts = [];
}
const version = versionParts.join(".");
const os = detectOS(ua);
const searchBotMatch = SEARCHBOT_OS_REGEX.exec(ua);
if (searchBotMatch && searchBotMatch[1]) {
return new SearchBotDeviceInfo(name, version, os, searchBotMatch[1]);
}
return new BrowserInfo(name, version, os);
}
export function detectOS(ua: string): OperatingSystem | null {
for (let ii = 0, count = operatingSystemRules.length; ii < count; ii++) {
const result = operatingSystemRules[ii];
if (!result) {
continue;
}
const [os, regex] = result;
const match = regex.exec(ua);
if (match) {
return os;
}
}
return null;
}
export function getNodeVersion(): NodeInfo | null {
const isNode = typeof process !== "undefined" && process.version;
return isNode ? new NodeInfo(process.version.slice(1)) : null;
}
function createVersionParts(count: number): string[] {
const output = [];
for (let ii = 0; ii < count; ii++) {
output.push("0");
}
return output;
}
+13
View File
@@ -0,0 +1,13 @@
import { detectOS } from "./detect-browser";
export function getOperatingSystem() {
if (typeof navigator !== "undefined" && navigator.product === "ReactNative") {
return "";
} else if (typeof window !== "undefined") {
const userAgent = navigator.userAgent;
return detectOS(userAgent) || "";
} else {
return process.platform;
}
}
Binary file not shown.
+89 -576
View File
File diff suppressed because it is too large Load Diff