diff --git a/.gitignore b/.gitignore index a07b6f04..5b4c5f35 100644 --- a/.gitignore +++ b/.gitignore @@ -145,3 +145,5 @@ dist .svelte-kit # End of https://www.toptal.com/developers/gitignore/api/node + +*.xcconfig \ No newline at end of file diff --git a/apps/branch.json b/apps/branch.json new file mode 100644 index 00000000..086fc924 --- /dev/null +++ b/apps/branch.json @@ -0,0 +1,3 @@ +{ + "deferInitForPluginRuntime": true +} diff --git a/apps/mobile/.gitignore b/apps/mobile/.gitignore index 7087b69d..07cb0512 100644 --- a/apps/mobile/.gitignore +++ b/apps/mobile/.gitignore @@ -23,4 +23,6 @@ expo-env.d.ts *.keystore -gradle.properties \ No newline at end of file +gradle.properties + +ios/Config.xcconfig \ No newline at end of file diff --git a/apps/mobile/README.md b/apps/mobile/README.md index cd4feb8a..abdad948 100644 --- a/apps/mobile/README.md +++ b/apps/mobile/README.md @@ -48,3 +48,5 @@ Join our community of developers creating universal apps. - [Expo on GitHub](https://github.com/expo/expo): View our open source platform and contribute. - [Discord community](https://chat.expo.dev): Chat with Expo users and ask questions. + +npx uri-scheme open 'layer://?utm_source=google&utm_medium=cpc&utm_campaign=spring_sale' --ios diff --git a/apps/mobile/app.config.ts b/apps/mobile/app.config.ts index d118f188..f1cd5311 100644 --- a/apps/mobile/app.config.ts +++ b/apps/mobile/app.config.ts @@ -1,4 +1,7 @@ import { ConfigContext } from "expo/config"; +import * as dotenv from "dotenv"; + +dotenv.config(); export default ({ config }: ConfigContext): ConfigContext["config"] => { return { @@ -8,11 +11,28 @@ export default ({ config }: ConfigContext): ConfigContext["config"] => { config: { ...(config?.ios?.config ?? {}), usesNonExemptEncryption: false, + branch: { + apiKey: "key_live_itjP2IzCCpusGpEaJZoejcicxrdQXPCx", + }, + }, + associatedDomains: [ + "applinks:app.layerapp.io", + "applinks:layer-five.vercel.app", + "applinks:1xdd5-alternate.app.link", + ], + }, + android: { + config: { + branch: { + apiKey: "key_live_itjP2IzCCpusGpEaJZoejcicxrdQXPCx", + }, }, }, plugins: [ "expo-router", "expo-asset", + ["@config-plugins/react-native-branch"], + [ "expo-font", { diff --git a/apps/mobile/app/(tabs)/analysis.tsx b/apps/mobile/app/(tabs)/analysis.tsx index 5fca3faf..b778f068 100644 --- a/apps/mobile/app/(tabs)/analysis.tsx +++ b/apps/mobile/app/(tabs)/analysis.tsx @@ -7,6 +7,7 @@ export default function LoginPage() { diff --git a/apps/mobile/app/(tabs)/goals.tsx b/apps/mobile/app/(tabs)/goals.tsx index 16f04090..f33c066a 100644 --- a/apps/mobile/app/(tabs)/goals.tsx +++ b/apps/mobile/app/(tabs)/goals.tsx @@ -7,6 +7,7 @@ export default function LoginPage() { diff --git a/apps/mobile/app/(tabs)/index.tsx b/apps/mobile/app/(tabs)/index.tsx index 9c304204..6c71e6d9 100644 --- a/apps/mobile/app/(tabs)/index.tsx +++ b/apps/mobile/app/(tabs)/index.tsx @@ -7,6 +7,7 @@ export default function LoginPage() { diff --git a/apps/mobile/app/+html.tsx b/apps/mobile/app/+html.tsx new file mode 100644 index 00000000..c8576732 --- /dev/null +++ b/apps/mobile/app/+html.tsx @@ -0,0 +1,17 @@ +import { PropsWithChildren } from "react"; + +export default function Root({ children }: PropsWithChildren) { + return ( + + + + + + + + {/* Other head elements... */} + + {children} + + ); +} diff --git a/apps/mobile/app/_layout.tsx b/apps/mobile/app/_layout.tsx index d693ecb5..d7d89bbd 100644 --- a/apps/mobile/app/_layout.tsx +++ b/apps/mobile/app/_layout.tsx @@ -16,14 +16,88 @@ import { GestureHandlerRootView } from "react-native-gesture-handler"; import { SuspenseProvider } from "@/provider/suspense-provider"; import { PermissionsAndroid, Platform } from "react-native"; import { PERMISSIONS } from "react-native-permissions"; +import * as Linking from "expo-linking"; +import branch from "react-native-branch"; +import { Mixpanel } from "mixpanel-react-native"; + +const trackAutomaticEvents = false; +const useNative = false; +const mixpanel = new Mixpanel( + process.env.EXPO_PUBLIC_MIXPANEL_TOKEN, + trackAutomaticEvents, + useNative +); + +mixpanel.init(); + +interface BranchEventParams { + $og_title: string; + "+referrer": string; + $marketing_title: string; + "+referring_browser": string; + "+is_first_session": boolean; + /** + * utm_source + */ + "~channel": string; + "~referring_link": string; + "+match_type": string; + "~id": string; + "+click_timestamp": number; + "+match_guaranteed": boolean; + "+clicked_branch_link": boolean; + /** + * utm_medium + */ + "~feature": "natural"; + $deeplink_path: string; + "~marketing": true; + /** + * utm_campaign + */ + "~campaign": "none"; +} export default function App() { const colorScheme = useColorScheme(); useEffect(() => { + async function checkInitialReferringParams() { + const [f, l] = await Promise.all([ + branch.getFirstReferringParams(), + branch.getLatestReferringParams(), + ]); + if (l) { + mixpanel.track("Last Branch Event ", l); + } + } + function subscribeBranch() { + const unsubscribe = branch.subscribe({ + onOpenComplete(event) { + if (event.params) { + const params = event.params as unknown as BranchEventParams; + const args = { + utm_source: params["~channel"], + utm_medium: params["~feature"], + utm_campaign: params["~campaign"], + ...params, + }; + mixpanel.track("Referer Event ", args); + } + }, + }); + + return unsubscribe; + } + + checkInitialReferringParams(); + const unsubscribe = subscribeBranch(); if (!!process.env.EXPO_PUBLIC_KAKAO_NATIVE_APP_KEY) { initializeKakaoSDK(process.env.EXPO_PUBLIC_KAKAO_NATIVE_APP_KEY); } + return () => { + unsubscribe(); + }; }, []); useEffect(() => { @@ -51,6 +125,11 @@ export default function App() { } }; + const url = Linking.useURL(); + if (url) { + const { hostname, path, queryParams, scheme } = Linking.parse(url); + } + return ( diff --git a/apps/mobile/app/space/index.tsx b/apps/mobile/app/space/index.tsx new file mode 100644 index 00000000..810eef04 --- /dev/null +++ b/apps/mobile/app/space/index.tsx @@ -0,0 +1,5 @@ +import { Redirect } from "expo-router"; + +export default function Page() { + return ; +} diff --git a/apps/mobile/env.d.ts b/apps/mobile/env.d.ts index f718a452..2e7286ef 100644 --- a/apps/mobile/env.d.ts +++ b/apps/mobile/env.d.ts @@ -1,5 +1,8 @@ -declare module "@env" { - export const EXPO_PUBLIC_KAKAO_NATIVE_APP_KEY: string; - export const EXPO_USE_METRO_WORKSPACE_ROOT: string; - export const EXPO_PUBLIC_WEBVIEW_URI: string; +declare namespace NodeJS { + interface ProcessEnv { + readonly EXPO_PUBLIC_KAKAO_NATIVE_APP_KEY: string; + readonly EXPO_USE_METRO_WORKSPACE_ROOT: string; + readonly EXPO_PUBLIC_WEBVIEW_URI: string; + readonly EXPO_PUBLIC_MIXPANEL_TOKEN: string; + } } diff --git a/apps/mobile/ios/.gitignore b/apps/mobile/ios/.gitignore index 8beb3443..96966f69 100644 --- a/apps/mobile/ios/.gitignore +++ b/apps/mobile/ios/.gitignore @@ -22,9 +22,11 @@ DerivedData *.xcuserstate project.xcworkspace .xcode.env.local +Config.xcconfig # Bundle artifacts *.jsbundle # CocoaPods /Pods/ + diff --git a/apps/mobile/ios/Podfile.lock b/apps/mobile/ios/Podfile.lock index 7eabefa2..18fad9b4 100644 --- a/apps/mobile/ios/Podfile.lock +++ b/apps/mobile/ios/Podfile.lock @@ -1,11 +1,16 @@ PODS: - Alamofire (5.9.1) - boost (1.83.0) + - BranchSDK (3.4.4) - DoubleConversion (1.1.6) - EXConstants (16.0.2): - ExpoModulesCore - Expo (51.0.32): - ExpoModulesCore + - ExpoAdapterBranch (8.0.0): + - ExpoModulesCore + - React-Core + - react-native-branch - ExpoAsset (10.0.10): - ExpoModulesCore - ExpoFileSystem (17.0.1): @@ -117,6 +122,12 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - Mixpanel-swift (4.2.7): + - Mixpanel-swift/Complete (= 4.2.7) + - Mixpanel-swift/Complete (4.2.7) + - MixpanelReactNative (3.0.5): + - Mixpanel-swift (= 4.2.7) + - React-Core - RCT-Folly (2024.01.01.00): - boost - DoubleConversion @@ -1066,6 +1077,13 @@ PODS: - ReactCommon/turbomodule/bridging - ReactCommon/turbomodule/core - Yoga + - react-native-branch (6.2.2): + - BranchSDK (= 3.4.4) + - React-Core + - react-native-config (1.5.3): + - react-native-config/App (= 1.5.3) + - react-native-config/App (1.5.3): + - React-Core - react-native-safe-area-context (4.10.5): - React-Core - react-native-webview (13.8.6): @@ -1318,6 +1336,8 @@ PODS: - React-logger (= 0.74.5) - React-perflogger (= 0.74.5) - React-utils (= 0.74.5) + - RNCAsyncStorage (2.0.0): + - React-Core - RNCKakaoCore (2.2.6): - DoubleConversion - glog @@ -1488,6 +1508,7 @@ DEPENDENCIES: - DoubleConversion (from `../node_modules/react-native/third-party-podspecs/DoubleConversion.podspec`) - "EXConstants (from `../../../node_modules/.pnpm/expo-constants@16.0.2_expo@51.0.32_@babel+core@7.24.5_@babel+preset-env@7.25.3_@babel+core@7.24.5__encoding@0.1.13_/node_modules/expo-constants/ios`)" - "Expo (from `../../../node_modules/.pnpm/expo@51.0.32_@babel+core@7.24.5_@babel+preset-env@7.25.3_@babel+core@7.24.5__encoding@0.1.13/node_modules/expo`)" + - "ExpoAdapterBranch (from `../../../node_modules/.pnpm/@config-plugins+react-native-branch@8.0.0_expo@51.0.32_@babel+core@7.24.5_@babel+preset-env@7_luqyqhuc2pxykg4onuuckfmd2a/node_modules/@config-plugins/react-native-branch/ios`)" - "ExpoAsset (from `../../../node_modules/.pnpm/expo-asset@10.0.10_expo@51.0.32_@babel+core@7.24.5_@babel+preset-env@7.25.3_@babel+core@7.24.5__encoding@0.1.13_/node_modules/expo-asset/ios`)" - "ExpoFileSystem (from `../../../node_modules/.pnpm/expo-file-system@17.0.1_expo@51.0.32_@babel+core@7.24.5_@babel+preset-env@7.25.3_@babel+core@7.24.5__encoding@0.1.13_/node_modules/expo-file-system/ios`)" - "ExpoFont (from `../../../node_modules/.pnpm/expo-font@12.0.10_expo@51.0.32_@babel+core@7.24.5_@babel+preset-env@7.25.3_@babel+core@7.24.5__encoding@0.1.13_/node_modules/expo-font/ios`)" @@ -1502,6 +1523,7 @@ DEPENDENCIES: - glog (from `../node_modules/react-native/third-party-podspecs/glog.podspec`) - hermes-engine (from `../node_modules/react-native/sdks/hermes-engine/hermes-engine.podspec`) - lottie-react-native (from `../node_modules/lottie-react-native`) + - MixpanelReactNative (from `../node_modules/mixpanel-react-native`) - RCT-Folly (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCT-Folly/Fabric (from `../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec`) - RCTDeprecation (from `../node_modules/react-native/ReactApple/Libraries/RCTFoundation/RCTDeprecation`) @@ -1529,6 +1551,8 @@ DEPENDENCIES: - React-logger (from `../node_modules/react-native/ReactCommon/logger`) - React-Mapbuffer (from `../node_modules/react-native/ReactCommon`) - "react-native-blur (from `../node_modules/@react-native-community/blur`)" + - react-native-branch (from `../node_modules/react-native-branch`) + - react-native-config (from `../node_modules/react-native-config`) - react-native-safe-area-context (from `../node_modules/react-native-safe-area-context`) - react-native-webview (from `../node_modules/react-native-webview`) - React-nativeconfig (from `../node_modules/react-native/ReactCommon`) @@ -1554,6 +1578,7 @@ DEPENDENCIES: - React-runtimescheduler (from `../node_modules/react-native/ReactCommon/react/renderer/runtimescheduler`) - React-utils (from `../node_modules/react-native/ReactCommon/react/utils`) - ReactCommon/turbomodule/core (from `../node_modules/react-native/ReactCommon`) + - "RNCAsyncStorage (from `../node_modules/@react-native-async-storage/async-storage`)" - "RNCKakaoCore (from `../node_modules/@react-native-kakao/core`)" - "RNCKakaoShare (from `../node_modules/@react-native-kakao/share`)" - "RNCKakaoSocial (from `../node_modules/@react-native-kakao/social`)" @@ -1568,6 +1593,7 @@ DEPENDENCIES: SPEC REPOS: trunk: - Alamofire + - BranchSDK - KakaoSDKAuth - KakaoSDKCommon - KakaoSDKFriend @@ -1577,6 +1603,7 @@ SPEC REPOS: - KakaoSDKTemplate - KakaoSDKUser - lottie-ios + - Mixpanel-swift - SocketRocket EXTERNAL SOURCES: @@ -1588,6 +1615,8 @@ EXTERNAL SOURCES: :path: "../../../node_modules/.pnpm/expo-constants@16.0.2_expo@51.0.32_@babel+core@7.24.5_@babel+preset-env@7.25.3_@babel+core@7.24.5__encoding@0.1.13_/node_modules/expo-constants/ios" Expo: :path: "../../../node_modules/.pnpm/expo@51.0.32_@babel+core@7.24.5_@babel+preset-env@7.25.3_@babel+core@7.24.5__encoding@0.1.13/node_modules/expo" + ExpoAdapterBranch: + :path: "../../../node_modules/.pnpm/@config-plugins+react-native-branch@8.0.0_expo@51.0.32_@babel+core@7.24.5_@babel+preset-env@7_luqyqhuc2pxykg4onuuckfmd2a/node_modules/@config-plugins/react-native-branch/ios" ExpoAsset: :path: "../../../node_modules/.pnpm/expo-asset@10.0.10_expo@51.0.32_@babel+core@7.24.5_@babel+preset-env@7.25.3_@babel+core@7.24.5__encoding@0.1.13_/node_modules/expo-asset/ios" ExpoFileSystem: @@ -1617,6 +1646,8 @@ EXTERNAL SOURCES: :tag: hermes-2024-06-28-RNv0.74.3-7bda0c267e76d11b68a585f84cfdd65000babf85 lottie-react-native: :path: "../node_modules/lottie-react-native" + MixpanelReactNative: + :path: "../node_modules/mixpanel-react-native" RCT-Folly: :podspec: "../node_modules/react-native/third-party-podspecs/RCT-Folly.podspec" RCTDeprecation: @@ -1667,6 +1698,10 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon" react-native-blur: :path: "../node_modules/@react-native-community/blur" + react-native-branch: + :path: "../node_modules/react-native-branch" + react-native-config: + :path: "../node_modules/react-native-config" react-native-safe-area-context: :path: "../node_modules/react-native-safe-area-context" react-native-webview: @@ -1717,6 +1752,8 @@ EXTERNAL SOURCES: :path: "../node_modules/react-native/ReactCommon/react/utils" ReactCommon: :path: "../node_modules/react-native/ReactCommon" + RNCAsyncStorage: + :path: "../node_modules/@react-native-async-storage/async-storage" RNCKakaoCore: :path: "../node_modules/@react-native-kakao/core" RNCKakaoShare: @@ -1741,9 +1778,11 @@ EXTERNAL SOURCES: SPEC CHECKSUMS: Alamofire: f36a35757af4587d8e4f4bfa223ad10be2422b8c boost: d3f49c53809116a5d38da093a8aa78bf551aed09 + BranchSDK: 28bec34fb99c53ab8e86b7bf69cc55a4505fe0b3 DoubleConversion: 76ab83afb40bddeeee456813d9c04f67f78771b5 EXConstants: 409690fbfd5afea964e5e9d6c4eb2c2b59222c59 Expo: 33132a667698a3259a4e6c0af1b4936388e5fa33 + ExpoAdapterBranch: 0a777cc421574cbb56d35f372e975bc1e625bb48 ExpoAsset: 323700f291684f110fb55f0d4022a3362ea9f875 ExpoFileSystem: 80bfe850b1f9922c16905822ecbf97acd711dc51 ExpoFont: 00756e6c796d8f7ee8d211e29c8b619e75cbf238 @@ -1767,6 +1806,8 @@ SPEC CHECKSUMS: KakaoSDKUser: 043bcd7e91454ebf3bf64f150c430e6f65f0a08d lottie-ios: e047b1d2e6239b787cc5e9755b988869cf190494 lottie-react-native: 4279da8b681e89c29a2adb9f99985d6cf372d49d + Mixpanel-swift: 7f5b749dbda2708033d67ff3d7a91710468d4a38 + MixpanelReactNative: 00d0aa5ebf291f389a87d9583419c4d4e51c5941 RCT-Folly: 02617c592a293bd6d418e0a88ff4ee1f88329b47 RCTDeprecation: 3afceddffa65aee666dafd6f0116f1d975db1584 RCTRequired: ec1239bc9d8bf63e10fb92bd8b26171a9258e0c1 @@ -1792,6 +1833,8 @@ SPEC CHECKSUMS: React-logger: 257858bd55f3a4e1bc0cf07ddc8fb9faba6f8c7c React-Mapbuffer: 6c1cacdbf40b531f549eba249e531a7d0bfd8e7f react-native-blur: 30d91a67da86a4d4d924b0c7c36f6e01479a246b + react-native-branch: 1a3a65b8902fcb988c110a60b0dcb56595db7077 + react-native-config: 8f7283449bbb048902f4e764affbbf24504454af react-native-safe-area-context: a240ad4b683349e48b1d51fed1611138d1bdad97 react-native-webview: 05bae3a03a1e4f59568dfc05286c0ebf8954106c React-nativeconfig: ba9a2e54e2f0882cf7882698825052793ed4c851 @@ -1817,6 +1860,7 @@ SPEC CHECKSUMS: React-runtimescheduler: cfbe85c3510c541ec6dc815c7729b41304b67961 React-utils: f242eb7e7889419d979ca0e1c02ccc0ea6e43b29 ReactCommon: f7da14a8827b72704169a48c929bcde802698361 + RNCAsyncStorage: d35c79ffba52c1013013e16b1fc295aec2feabb6 RNCKakaoCore: 271121ac80392d3ebe06782c5bcab4879cb3ea65 RNCKakaoShare: 370949ae8f3ee35c3935b05188bbe827a334195c RNCKakaoSocial: 9c03b03e30c25b3a33d8ca2595ae7053bdba5b4d diff --git a/apps/mobile/ios/layer.xcodeproj/project.pbxproj b/apps/mobile/ios/layer.xcodeproj/project.pbxproj index fb004783..5c99646b 100644 --- a/apps/mobile/ios/layer.xcodeproj/project.pbxproj +++ b/apps/mobile/ios/layer.xcodeproj/project.pbxproj @@ -32,6 +32,7 @@ 6C2E3173556A471DD304B334 /* Pods-layer.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-layer.debug.xcconfig"; path = "Target Support Files/Pods-layer/Pods-layer.debug.xcconfig"; sourceTree = ""; }; 77203D4CF0F24E99942E94DB /* layer-Bridging-Header.h */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 4; includeInIndex = 0; lastKnownFileType = sourcecode.c.h; name = "layer-Bridging-Header.h"; path = "layer/layer-Bridging-Header.h"; sourceTree = ""; }; 7A4D352CD337FB3A3BF06240 /* Pods-layer.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-layer.release.xcconfig"; path = "Target Support Files/Pods-layer/Pods-layer.release.xcconfig"; sourceTree = ""; }; + 7FA4F0C42C8F366A0016D928 /* Config.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; 8F46C79D23705C174FDE920B /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xml; name = PrivacyInfo.xcprivacy; path = layer/PrivacyInfo.xcprivacy; sourceTree = ""; }; AA286B85B6C04FC6940260E9 /* SplashScreen.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = SplashScreen.storyboard; path = layer/SplashScreen.storyboard; sourceTree = ""; }; B56B8C86D6B14A489FBBA64E /* PretendardVariable.ttf */ = {isa = PBXFileReference; explicitFileType = undefined; fileEncoding = 9; includeInIndex = 0; lastKnownFileType = unknown; name = PretendardVariable.ttf; path = ../assets/fonts/PretendardVariable.ttf; sourceTree = ""; }; @@ -91,6 +92,7 @@ 83CBB9F61A601CBA00E9B192 = { isa = PBXGroup; children = ( + 7FA4F0C42C8F366A0016D928 /* Config.xcconfig */, 13B07FAE1A68108700A75B9A /* layer */, 832341AE1AAA6A7D00B99B32 /* Libraries */, 83CBBA001A601CBA00E9B192 /* Products */, @@ -275,12 +277,15 @@ inputPaths = ( "${PODS_ROOT}/Target Support Files/Pods-layer/Pods-layer-resources.sh", "${PODS_CONFIGURATION_BUILD_DIR}/Alamofire/Alamofire.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/BranchSDK/BranchSDK.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/EXConstants.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/EXConstants/ExpoConstants_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/ExpoFileSystem/ExpoFileSystem_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/ExpoSystemUI/ExpoSystemUI_privacy.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/KakaoSDKCommon/KakaoSDKCommon.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/KakaoSDKFriendCore/KakaoSDKFriendResources.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/Mixpanel-swift/Mixpanel.bundle", + "${PODS_CONFIGURATION_BUILD_DIR}/RNCAsyncStorage/RNCAsyncStorage_resources.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/React-Core/RCTI18nStrings.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/lottie-ios/LottiePrivacyInfo.bundle", "${PODS_CONFIGURATION_BUILD_DIR}/lottie-react-native/Lottie_React_Native_Privacy.bundle", @@ -288,12 +293,15 @@ name = "[CP] Copy Pods Resources"; outputPaths = ( "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Alamofire.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/BranchSDK.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/EXConstants.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoConstants_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoFileSystem_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/ExpoSystemUI_privacy.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/KakaoSDKCommon.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/KakaoSDKFriendResources.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Mixpanel.bundle", + "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RNCAsyncStorage_resources.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/RCTI18nStrings.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/LottiePrivacyInfo.bundle", "${TARGET_BUILD_DIR}/${UNLOCALIZED_RESOURCES_FOLDER_PATH}/Lottie_React_Native_Privacy.bundle", @@ -439,6 +447,7 @@ }; 83CBBA201A601CBA00E9B192 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 7FA4F0C42C8F366A0016D928 /* Config.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CC = ""; @@ -508,6 +517,7 @@ }; 83CBBA211A601CBA00E9B192 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 7FA4F0C42C8F366A0016D928 /* Config.xcconfig */; buildSettings = { ALWAYS_SEARCH_USER_PATHS = NO; CC = ""; diff --git a/apps/mobile/ios/layer.xcodeproj/xcshareddata/xcschemes/layer.xcscheme b/apps/mobile/ios/layer.xcodeproj/xcshareddata/xcschemes/layer.xcscheme index 68715239..9c6a54db 100644 --- a/apps/mobile/ios/layer.xcodeproj/xcshareddata/xcschemes/layer.xcscheme +++ b/apps/mobile/ios/layer.xcodeproj/xcshareddata/xcschemes/layer.xcscheme @@ -1,10 +1,28 @@ + version = "1.7"> + + + + + + + + + + + + + + #import #import @@ -12,7 +13,10 @@ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:( // You can add your custom initial props in the dictionary below. // They will be passed down to the ViewController used by React Native. self.initialProps = @{}; - + // Uncomment this line to use the test key instead of the live one. + // [RNBranch useTestInstance]; + [RNBranch initSessionWithLaunchOptions:launchOptions isReferrable:YES]; + // NSURL *jsCodeLocation; return [super application:application didFinishLaunchingWithOptions:launchOptions]; } @@ -32,11 +36,14 @@ - (NSURL *)bundleURL // Linking API - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { + [RNBranch application:application openURL:url options:options]; return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options]; } // Universal Links - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { + [RNBranch continueUserActivity:userActivity]; + BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result; } diff --git a/apps/mobile/ios/layer/Info.plist b/apps/mobile/ios/layer/Info.plist index 4857c31d..f589fcd2 100644 --- a/apps/mobile/ios/layer/Info.plist +++ b/apps/mobile/ios/layer/Info.plist @@ -25,6 +25,10 @@ CFBundleURLTypes + CFBundleTypeRole + Editor + CFBundleURLName + layer CFBundleURLSchemes layer @@ -51,6 +55,12 @@ $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route + UIAppFonts + + Helvetica-Ultra.ttf + Helvetica.ttf + PretendardVariable.ttf + UILaunchStoryboardName SplashScreen UIRequiredDeviceCapabilities @@ -69,11 +79,17 @@ Automatic UIViewControllerBasedStatusBarAppearance - UIAppFonts + branch_key + + live + key_live_$(BRANCH_KEY) + + branch_universal_link_domains - Helvetica-Ultra.ttf - Helvetica.ttf - PretendardVariable.ttf + app.layerapp.io + layerapp.io + layer-five.vercel.app + 1xdd5-alternate.app.link diff --git a/apps/mobile/ios/layer/PrivacyInfo.xcprivacy b/apps/mobile/ios/layer/PrivacyInfo.xcprivacy index 765aa13a..a4232a97 100644 --- a/apps/mobile/ios/layer/PrivacyInfo.xcprivacy +++ b/apps/mobile/ios/layer/PrivacyInfo.xcprivacy @@ -14,20 +14,21 @@ NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryUserDefaults + NSPrivacyAccessedAPICategoryFileTimestamp NSPrivacyAccessedAPITypeReasons - CA92.1 + C617.1 + 0A2A.1 + 3B52.1 NSPrivacyAccessedAPIType - NSPrivacyAccessedAPICategoryFileTimestamp + NSPrivacyAccessedAPICategoryUserDefaults NSPrivacyAccessedAPITypeReasons - 0A2A.1 - 3B52.1 - C617.1 + CA92.1 + 1C8F.1 diff --git a/apps/mobile/ios/layer/layer.entitlements b/apps/mobile/ios/layer/layer.entitlements index f683276c..ce132560 100644 --- a/apps/mobile/ios/layer/layer.entitlements +++ b/apps/mobile/ios/layer/layer.entitlements @@ -1,5 +1,13 @@ - - \ No newline at end of file + + com.apple.developer.associated-domains + + applinks:app.layerapp.io + applinks:layerapp.io + applinks:layer-five.vercel.app + applinks:1xdd5-alternate.app.link + + + diff --git a/apps/mobile/package.json b/apps/mobile/package.json index 829e88d2..6bc58a8e 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -18,9 +18,11 @@ "preset": "jest-expo" }, "dependencies": { + "@config-plugins/react-native-branch": "^8.0.0", "@expo/config": "^9.0.3", "@expo/vector-icons": "^14.0.2", "@layer/shared": "workspace:^", + "@react-native-async-storage/async-storage": "^2.0.0", "@react-native-community/blur": "^4.4.1", "@react-native-kakao/core": "^2.2.6", "@react-native-kakao/share": "^2.2.6", @@ -43,9 +45,12 @@ "expo-system-ui": "~3.0.7", "expo-web-browser": "~13.0.3", "lottie-react-native": "^6.7.2", + "mixpanel-react-native": "^3.0.5", "react": "18.2.0", "react-dom": "18.2.0", "react-native": "0.74.5", + "react-native-branch": "^6.2.2", + "react-native-config": "^1.5.3", "react-native-dotenv": "^3.4.11", "react-native-gesture-handler": "~2.16.1", "react-native-permissions": "^4.1.5", @@ -65,6 +70,7 @@ "@types/react": "~18.2.45", "@types/react-test-renderer": "^18.0.7", "babel-preset-expo": "^11.0.14", + "dotenv": "^16.4.5", "jest": "^29.2.1", "jest-expo": "~51.0.3", "react-native-svg-transformer": "^1.5.0", diff --git a/apps/mobile/public/.well-known/apple-app-site-association b/apps/mobile/public/.well-known/apple-app-site-association new file mode 100644 index 00000000..b4a47bf9 --- /dev/null +++ b/apps/mobile/public/.well-known/apple-app-site-association @@ -0,0 +1,28 @@ + +{ + "applinks": { + "details": [ + { + "appIDs": [ + "3XU5VHUK72.com.team-kasukabe.layer" + ], + "components": [ + { + "/": "*", + "comment": "Matches all routes" + } + ] + } + ] + }, + "activitycontinuation": { + "apps": [ + "3XU5VHUK72.com.team-kasukabe.layer" + ] + }, + "webcredentials": { + "apps": [ + "3XU5VHUK72.com.team-kasukabe.layer" + ] + } +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d4b60cb3..2a3e3801 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -18,6 +18,9 @@ importers: apps/mobile: dependencies: + '@config-plugins/react-native-branch': + specifier: ^8.0.0 + version: 8.0.0(expo@51.0.32(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(encoding@0.1.13)) '@expo/config': specifier: ^9.0.3 version: 9.0.3 @@ -27,6 +30,9 @@ importers: '@layer/shared': specifier: workspace:^ version: link:../../packages/shared + '@react-native-async-storage/async-storage': + specifier: ^2.0.0 + version: 2.0.0(react-native@0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0)) '@react-native-community/blur': specifier: ^4.4.1 version: 4.4.1(react-native@0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0))(react@18.2.0) @@ -93,6 +99,9 @@ importers: lottie-react-native: specifier: ^6.7.2 version: 6.7.2(react-native@0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0))(react@18.2.0) + mixpanel-react-native: + specifier: ^3.0.5 + version: 3.0.5(react-native@0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0)) react: specifier: 18.2.0 version: 18.2.0 @@ -102,6 +111,12 @@ importers: react-native: specifier: 0.74.5 version: 0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0) + react-native-branch: + specifier: ^6.2.2 + version: 6.2.2(react-native@0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0)) + react-native-config: + specifier: ^1.5.3 + version: 1.5.3 react-native-dotenv: specifier: ^3.4.11 version: 3.4.11(@babel/runtime@7.25.6) @@ -154,6 +169,9 @@ importers: babel-preset-expo: specifier: ^11.0.14 version: 11.0.14(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5)) + dotenv: + specifier: ^16.4.5 + version: 16.4.5 jest: specifier: ^29.2.1 version: 29.7.0(@types/node@20.12.12)(babel-plugin-macros@3.1.0) @@ -1181,6 +1199,11 @@ packages: '@cfcs/core@0.1.0': resolution: {integrity: sha512-kvYX0RpL45XTHJ5sW7teNbKeAa7pK3nNqaJPoFfZDPTIBJOkTtRD3QhkBG+O3Hu69a8xeMoPvF6y/RtJ6JUOdA==} + '@config-plugins/react-native-branch@8.0.0': + resolution: {integrity: sha512-lPKpnLx2HZ82FdDQXfSOggmLfrV0sArze9B8Ns2XvuuNPbmJa4+ixyxIBGe2vs9HTj7NBd3e12SOZ4ZG/BUlWw==} + peerDependencies: + expo: ^51 + '@egjs/agent@2.4.3': resolution: {integrity: sha512-XvksSENe8wPeFlEVouvrOhKdx8HMniJ3by7sro2uPF3M6QqWwjzVcmvwoPtdjiX8O1lfRoLhQMp1a7NGlVTdIA==} @@ -1427,7 +1450,7 @@ packages: '@expo/bunyan@4.0.1': resolution: {integrity: sha512-+Lla7nYSiHZirgK+U/uYzsLv/X+HaJienbD5AKX1UQZHYfWaP+9uuQluRB4GrEVWF0GZ7vEVp/jzaOT9k/SQlg==} - engines: {'0': node >=0.10.0} + engines: {node: '>=0.10.0'} '@expo/cli@0.18.29': resolution: {integrity: sha512-X810C48Ss+67RdZU39YEO1khNYo1RmjouRV+vVe0QhMoTe8R6OA3t+XYEdwaNbJ5p/DJN7szfHfNmX2glpC7xg==} @@ -1934,6 +1957,16 @@ packages: peerDependencies: react: ^16.8 || ^17.0 || ^18.0 + '@react-native-async-storage/async-storage@1.24.0': + resolution: {integrity: sha512-W4/vbwUOYOjco0x3toB8QCr7EjIP6nE9G7o8PMguvvjYT5Awg09lyV4enACRx4s++PPulBiBSjL0KTFx2u0Z/g==} + peerDependencies: + react-native: ^0.0.0-0 || >=0.60 <1.0 + + '@react-native-async-storage/async-storage@2.0.0': + resolution: {integrity: sha512-af6H9JjfL6G/PktBfUivvexoiFKQTJGQCtSWxMdivLzNIY94mu9DdiY0JqCSg/LyPCLGKhHPUlRQhNvpu3/KVA==} + peerDependencies: + react-native: ^0.0.0-0 || >=0.65 <1.0 + '@react-native-community/blur@4.4.1': resolution: {integrity: sha512-XBSsRiYxE/MOEln2ayunShfJtWztHwUxLFcSL20o+HNNRnuUDv+GXkF6FmM2zE8ZUfrnhQ/zeTqvnuDPGw6O8A==} peerDependencies: @@ -5074,6 +5107,10 @@ packages: resolution: {integrity: sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==} engines: {node: '>=0.10.0'} + is-plain-obj@2.1.0: + resolution: {integrity: sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==} + engines: {node: '>=8'} + is-plain-object@2.0.4: resolution: {integrity: sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==} engines: {node: '>=0.10.0'} @@ -5490,7 +5527,6 @@ packages: resolution: {integrity: sha512-Quz3MvAwHxVYNXsOByL7xI5EB2WYOeFswqaHIA3qOK3isRWTxiplBEocmmru6XmxDB2L7jDNYtYA4FyimoAFEw==} engines: {node: '>=8.17.0'} hasBin: true - bundledDependencies: [] jsonfile@4.0.0: resolution: {integrity: sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==} @@ -5779,6 +5815,10 @@ packages: merge-descriptors@1.0.1: resolution: {integrity: sha512-cCi6g3/Zr1iqQi6ySbseM1Xvooa98N0w31jzUYrXPX2xqObmFGHJ0tQ5u74H3mVh7wLouTseZyYIq39g8cNp1w==} + merge-options@3.0.4: + resolution: {integrity: sha512-2Sug1+knBjkaMsMgf1ctR1Ujx+Ayku4EdJN4Z+C2+JzoeF7A3OZ9KM2GY0CpQS51NR61LTurMJrRKPhSs3ZRTQ==} + engines: {node: '>=10'} + merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} @@ -5961,6 +6001,9 @@ packages: mixpanel-browser@2.55.1: resolution: {integrity: sha512-NSEPdFSJxoR1OCKWKHbtqd3BeH1c9NjXbEt0tN5TgBEO1nSDji6niU9n4MopAXOP0POET9spjpQKxZtLZKTJwA==} + mixpanel-react-native@3.0.5: + resolution: {integrity: sha512-y6rh5AXy0xZI3kQvwmjt70tAzzPvCkx/6kywIfitI3UTTgbV/wuPeRHkAOxaM8C0jZbiCFtEhE09vGSN4ZutgQ==} + mkdirp@0.5.6: resolution: {integrity: sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==} hasBin: true @@ -6713,6 +6756,19 @@ packages: '@types/react': ^16.8.0 || ^17.0.0 || ^18.0.0 react: ^16.8.0 || ^17.0.0 || ^18.0.0 + react-native-branch@6.2.2: + resolution: {integrity: sha512-zXc90/IoZKR0RBEhpjLKHnm2rSrZdAuEdMnmGg0tC63n11MUhkHEB78KKnC1ooQV9DBjdyhkhg25D1tA7xdprw==} + peerDependencies: + react-native: '>= 0.60' + + react-native-config@1.5.3: + resolution: {integrity: sha512-3D05Abgk5DfDw9w258EzXvX5AkU7eqj3u9H0H0L4gUga4nYg/zuupcrpGbpF4QeXBcJ84jjs6g8JaEP6VBT7Pg==} + peerDependencies: + react-native-windows: '>=0.61' + peerDependenciesMeta: + react-native-windows: + optional: true + react-native-dotenv@3.4.11: resolution: {integrity: sha512-6vnIE+WHABSeHCaYP6l3O1BOEhWxKH6nHAdV7n/wKn/sciZ64zPPp2NUdEUf1m7g4uuzlLbjgr+6uDt89q2DOg==} peerDependencies: @@ -7960,6 +8016,11 @@ packages: resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==} hasBin: true + uuid@3.3.2: + resolution: {integrity: sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==} + deprecated: Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details. + hasBin: true + uuid@7.0.3: resolution: {integrity: sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==} hasBin: true @@ -9321,6 +9382,10 @@ snapshots: dependencies: '@egjs/component': 3.0.5 + '@config-plugins/react-native-branch@8.0.0(expo@51.0.32(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(encoding@0.1.13))': + dependencies: + expo: 51.0.32(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(encoding@0.1.13) + '@egjs/agent@2.4.3': {} '@egjs/axes@3.9.0': @@ -10563,6 +10628,16 @@ snapshots: '@radix-ui/react-compose-refs': 1.0.0(react@18.2.0) react: 18.2.0 + '@react-native-async-storage/async-storage@1.24.0(react-native@0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0))': + dependencies: + merge-options: 3.0.4 + react-native: 0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0) + + '@react-native-async-storage/async-storage@2.0.0(react-native@0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0))': + dependencies: + merge-options: 3.0.4 + react-native: 0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0) + '@react-native-community/blur@4.4.1(react-native@0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0))(react@18.2.0)': dependencies: react: 18.2.0 @@ -14323,6 +14398,8 @@ snapshots: is-plain-obj@1.1.0: {} + is-plain-obj@2.1.0: {} + is-plain-object@2.0.4: dependencies: isobject: 3.0.1 @@ -15374,6 +15451,10 @@ snapshots: merge-descriptors@1.0.1: {} + merge-options@3.0.4: + dependencies: + is-plain-obj: 2.1.0 + merge-stream@2.0.0: {} merge2@1.4.1: {} @@ -15662,6 +15743,13 @@ snapshots: dependencies: rrweb: 2.0.0-alpha.13 + mixpanel-react-native@3.0.5(react-native@0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0)): + dependencies: + '@react-native-async-storage/async-storage': 1.24.0(react-native@0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0)) + uuid: 3.3.2 + transitivePeerDependencies: + - react-native + mkdirp@0.5.6: dependencies: minimist: 1.2.8 @@ -16492,6 +16580,12 @@ snapshots: react: 18.2.0 react-base16-styling: 0.9.1 + react-native-branch@6.2.2(react-native@0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0)): + dependencies: + react-native: 0.74.5(@babel/core@7.24.5)(@babel/preset-env@7.25.3(@babel/core@7.24.5))(@types/react@18.2.79)(encoding@0.1.13)(react@18.2.0) + + react-native-config@1.5.3: {} + react-native-dotenv@3.4.11(@babel/runtime@7.25.6): dependencies: '@babel/runtime': 7.25.6 @@ -17852,6 +17946,8 @@ snapshots: uuid@10.0.0: {} + uuid@3.3.2: {} + uuid@7.0.3: {} uuid@8.3.2: {}