From 563438a4c9a231874470a91b104708b2936f02dc Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 6 Feb 2024 14:48:32 -0800 Subject: [PATCH 01/35] Add necessary dependencies --- gradle/libs.versions.toml | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 795510bce..83de5f2b3 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -56,6 +56,20 @@ room = "2.6.1" secrets = "2.0.1" truth = "1.1.5" turbine = "1.0.0" +agp = "8.2.0" +android-compileSdk = "34" +android-minSdk = "24" +android-targetSdk = "34" +androidx-activityCompose = "1.8.2" +androidx-appcompat = "1.6.1" +androidx-constraintlayout = "2.1.4" +androidx-core-ktx = "1.12.0" +androidx-espresso-core = "3.5.1" +androidx-material = "1.11.0" +androidx-test-junit = "1.1.5" +compose = "1.6.0" +compose-plugin = "1.6.0-alpha01" +junit = "4.13.2" [libraries] accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanist" } @@ -133,6 +147,18 @@ room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } truth = { group = "com.google.truth", name = "truth", version.ref = "truth" } turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine" } +kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } +kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } +junit = { group = "junit", name = "junit", version.ref = "junit" } +androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-ktx" } +androidx-test-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-junit" } +androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidx-espresso-core" } +androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" } +androidx-material = { group = "com.google.android.material", name = "material", version.ref = "androidx-material" } +androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraintlayout" } +androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" } +compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" } +compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" } # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } @@ -161,6 +187,10 @@ protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" } room = { id = "androidx.room", version.ref = "room" } secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } +androidApplication = { id = "com.android.application", version.ref = "agp" } +androidLibrary = { id = "com.android.library", version.ref = "agp" } +jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } +kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } # Plugins defined by this project nowinandroid-android-application = { id = "nowinandroid.android.application", version = "unspecified" } From 14678d5c25a6f6c46c6548adcfbd6e481aad3d48 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 6 Feb 2024 14:53:50 -0800 Subject: [PATCH 02/35] Add iosApp folder --- iosApp/Configuration/Config.xcconfig | 3 + iosApp/iosApp.xcodeproj/project.pbxproj | 383 ++++++++++++++++++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 14 + .../AppIcon.appiconset/app-icon-1024.png | Bin 0 -> 67285 bytes iosApp/iosApp/Assets.xcassets/Contents.json | 6 + iosApp/iosApp/ContentView.swift | 21 + iosApp/iosApp/Info.plist | 50 +++ .../Preview Assets.xcassets/Contents.json | 6 + iosApp/iosApp/iOSApp.swift | 10 + 10 files changed, 504 insertions(+) create mode 100644 iosApp/Configuration/Config.xcconfig create mode 100644 iosApp/iosApp.xcodeproj/project.pbxproj create mode 100644 iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png create mode 100644 iosApp/iosApp/Assets.xcassets/Contents.json create mode 100644 iosApp/iosApp/ContentView.swift create mode 100644 iosApp/iosApp/Info.plist create mode 100644 iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json create mode 100644 iosApp/iosApp/iOSApp.swift diff --git a/iosApp/Configuration/Config.xcconfig b/iosApp/Configuration/Config.xcconfig new file mode 100644 index 000000000..f75f4bd06 --- /dev/null +++ b/iosApp/Configuration/Config.xcconfig @@ -0,0 +1,3 @@ +TEAM_ID= +BUNDLE_ID=com.google.samples.apps.nowinandroid +APP_NAME=nowinandroid \ No newline at end of file diff --git a/iosApp/iosApp.xcodeproj/project.pbxproj b/iosApp/iosApp.xcodeproj/project.pbxproj new file mode 100644 index 000000000..66c59c017 --- /dev/null +++ b/iosApp/iosApp.xcodeproj/project.pbxproj @@ -0,0 +1,383 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557BA273AAA24004C7B11 /* Assets.xcassets */; }; + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */; }; + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2152FB032600AC8F00CF470E /* iOSApp.swift */; }; + 7555FF83242A565900829871 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7555FF82242A565900829871 /* ContentView.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + 058557BA273AAA24004C7B11 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + 2152FB032600AC8F00CF470E /* iOSApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iOSApp.swift; sourceTree = ""; }; + 7555FF7B242A565900829871 /* .app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = .app; sourceTree = BUILT_PRODUCTS_DIR; }; + 7555FF82242A565900829871 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 7555FF8C242A565B00829871 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + AB3632DC29227652001CCB65 /* Config.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Config.xcconfig; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXGroup section */ + 058557D7273AAEEB004C7B11 /* Preview Content */ = { + isa = PBXGroup; + children = ( + 058557D8273AAEEB004C7B11 /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; + 42799AB246E5F90AF97AA0EF /* Frameworks */ = { + isa = PBXGroup; + children = ( + ); + name = Frameworks; + sourceTree = ""; + }; + 7555FF72242A565900829871 = { + isa = PBXGroup; + children = ( + AB1DB47929225F7C00F7AF9C /* Configuration */, + 7555FF7D242A565900829871 /* iosApp */, + 7555FF7C242A565900829871 /* Products */, + 42799AB246E5F90AF97AA0EF /* Frameworks */, + ); + sourceTree = ""; + }; + 7555FF7C242A565900829871 /* Products */ = { + isa = PBXGroup; + children = ( + 7555FF7B242A565900829871 /* .app */, + ); + name = Products; + sourceTree = ""; + }; + 7555FF7D242A565900829871 /* iosApp */ = { + isa = PBXGroup; + children = ( + 058557BA273AAA24004C7B11 /* Assets.xcassets */, + 7555FF82242A565900829871 /* ContentView.swift */, + 7555FF8C242A565B00829871 /* Info.plist */, + 2152FB032600AC8F00CF470E /* iOSApp.swift */, + 058557D7273AAEEB004C7B11 /* Preview Content */, + ); + path = iosApp; + sourceTree = ""; + }; + AB1DB47929225F7C00F7AF9C /* Configuration */ = { + isa = PBXGroup; + children = ( + AB3632DC29227652001CCB65 /* Config.xcconfig */, + ); + path = Configuration; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 7555FF7A242A565900829871 /* iosApp */ = { + isa = PBXNativeTarget; + buildConfigurationList = 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */; + buildPhases = ( + F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */, + 7555FF77242A565900829871 /* Sources */, + 7555FF79242A565900829871 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = iosApp; + productName = iosApp; + productReference = 7555FF7B242A565900829871 /* .app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 7555FF73242A565900829871 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1130; + LastUpgradeCheck = 1130; + ORGANIZATIONNAME = orgName; + TargetAttributes = { + 7555FF7A242A565900829871 = { + CreatedOnToolsVersion = 11.3.1; + }; + }; + }; + buildConfigurationList = 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 7555FF72242A565900829871; + productRefGroup = 7555FF7C242A565900829871 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 7555FF7A242A565900829871 /* iosApp */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 7555FF79242A565900829871 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 058557D9273AAEEB004C7B11 /* Preview Assets.xcassets in Resources */, + 058557BB273AAA24004C7B11 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + F36B1CEB2AD83DDC00CB74D5 /* Compile Kotlin Framework */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Compile Kotlin Framework"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [ \"YES\" = \"$OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED\" ]; then\n echo \"Skipping Gradle build task invocation due to OVERRIDE_KOTLIN_BUILD_IDE_SUPPORTED environment variable set to \\\"YES\\\"\"\n exit 0\nfi\ncd \"$SRCROOT/..\"\n./gradlew :composeApp:embedAndSignAppleFrameworkForXcode\n"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 7555FF77242A565900829871 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 2152FB042600AC8F00CF470E /* iOSApp.swift in Sources */, + 7555FF83242A565900829871 /* ContentView.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + 7555FFA3242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 7555FFA4242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = AB3632DC29227652001CCB65 /* Config.xcconfig */; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 7555FFA6242A565B00829871 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + composeApp, + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 7555FFA7242A565B00829871 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CODE_SIGN_IDENTITY = "Apple Development"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_ASSET_PATHS = "\"iosApp/Preview Content\""; + DEVELOPMENT_TEAM = "${TEAM_ID}"; + ENABLE_PREVIEWS = YES; + FRAMEWORK_SEARCH_PATHS = ( + "$(SRCROOT)/../shared/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)\n$(SRCROOT)/../composeApp/build/xcode-frameworks/$(CONFIGURATION)/$(SDK_NAME)", + ); + INFOPLIST_FILE = iosApp/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 14.1; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + OTHER_LDFLAGS = ( + "$(inherited)", + "-framework", + composeApp, + ); + PRODUCT_BUNDLE_IDENTIFIER = "${BUNDLE_ID}${TEAM_ID}"; + PRODUCT_NAME = "${APP_NAME}"; + PROVISIONING_PROFILE_SPECIFIER = ""; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 7555FF76242A565900829871 /* Build configuration list for PBXProject "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA3242A565B00829871 /* Debug */, + 7555FFA4242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 7555FFA5242A565B00829871 /* Build configuration list for PBXNativeTarget "iosApp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 7555FFA6242A565B00829871 /* Debug */, + 7555FFA7242A565B00829871 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 7555FF73242A565900829871 /* Project object */; +} diff --git a/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json b/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 000000000..ee7e3ca03 --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 000000000..8edf56e7a --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,14 @@ +{ + "images" : [ + { + "filename" : "app-icon-1024.png", + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png b/iosApp/iosApp/Assets.xcassets/AppIcon.appiconset/app-icon-1024.png new file mode 100644 index 0000000000000000000000000000000000000000..53fc536fb9ac5c1dbb27c7e1da13db3760070a11 GIT binary patch literal 67285 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 BXNCX( literal 0 HcmV?d00001 diff --git a/iosApp/iosApp/Assets.xcassets/Contents.json b/iosApp/iosApp/Assets.xcassets/Contents.json new file mode 100644 index 000000000..4aa7c5350 --- /dev/null +++ b/iosApp/iosApp/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/ContentView.swift b/iosApp/iosApp/ContentView.swift new file mode 100644 index 000000000..3cd5c325b --- /dev/null +++ b/iosApp/iosApp/ContentView.swift @@ -0,0 +1,21 @@ +import UIKit +import SwiftUI +import ComposeApp + +struct ComposeView: UIViewControllerRepresentable { + func makeUIViewController(context: Context) -> UIViewController { + MainViewControllerKt.MainViewController() + } + + func updateUIViewController(_ uiViewController: UIViewController, context: Context) {} +} + +struct ContentView: View { + var body: some View { + ComposeView() + .ignoresSafeArea(.keyboard) // Compose has own keyboard handler + } +} + + + diff --git a/iosApp/iosApp/Info.plist b/iosApp/iosApp/Info.plist new file mode 100644 index 000000000..412e37812 --- /dev/null +++ b/iosApp/iosApp/Info.plist @@ -0,0 +1,50 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + CADisableMinimumFrameDurationOnPhone + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + + UILaunchScreen + + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json b/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 000000000..4aa7c5350 --- /dev/null +++ b/iosApp/iosApp/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} \ No newline at end of file diff --git a/iosApp/iosApp/iOSApp.swift b/iosApp/iosApp/iOSApp.swift new file mode 100644 index 000000000..0648e8602 --- /dev/null +++ b/iosApp/iosApp/iOSApp.swift @@ -0,0 +1,10 @@ +import SwiftUI + +@main +struct iOSApp: App { + var body: some Scene { + WindowGroup { + ContentView() + } + } +} \ No newline at end of file From d3314c20287cbb781e3f608b2a7211a7c76dbe1c Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 7 Feb 2024 13:08:26 -0800 Subject: [PATCH 03/35] Add plugin declaration in the build.gradle.kts --- build.gradle.kts | 2 ++ gradle/libs.versions.toml | 5 ----- 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/build.gradle.kts b/build.gradle.kts index 6fe9b829e..1f329a53e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -46,4 +46,6 @@ plugins { alias(libs.plugins.roborazzi) apply false alias(libs.plugins.secrets) apply false alias(libs.plugins.room) apply false + alias(libs.plugins.jetbrainsCompose) apply false + alias(libs.plugins.kotlinMultiplatform) apply false } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 83de5f2b3..db9ca3ed0 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -150,13 +150,10 @@ turbine = { group = "app.cash.turbine", name = "turbine", version.ref = "turbine kotlin-test = { module = "org.jetbrains.kotlin:kotlin-test", version.ref = "kotlin" } kotlin-test-junit = { module = "org.jetbrains.kotlin:kotlin-test-junit", version.ref = "kotlin" } junit = { group = "junit", name = "junit", version.ref = "junit" } -androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "androidx-core-ktx" } androidx-test-junit = { group = "androidx.test.ext", name = "junit", version.ref = "androidx-test-junit" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "androidx-espresso-core" } -androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "androidx-appcompat" } androidx-material = { group = "com.google.android.material", name = "material", version.ref = "androidx-material" } androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraintlayout" } -androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidx-activityCompose" } compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" } compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" } @@ -187,8 +184,6 @@ protobuf = { id = "com.google.protobuf", version.ref = "protobufPlugin" } roborazzi = { id = "io.github.takahirom.roborazzi", version.ref = "roborazzi" } room = { id = "androidx.room", version.ref = "room" } secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } -androidApplication = { id = "com.android.application", version.ref = "agp" } -androidLibrary = { id = "com.android.library", version.ref = "agp" } jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } From dcdc7f0c71df012e5e8a793f3c35d184b62939c2 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 7 Feb 2024 16:27:20 -0800 Subject: [PATCH 04/35] Add KMP convention plugin --- build-logic/convention/build.gradle.kts | 4 + .../main/kotlin/KmpLibraryConventionPlugin.kt | 32 ++++++++ .../apps/nowinandroid/KotlinMultiplatform.kt | 77 +++++++++++++++++++ gradle/libs.versions.toml | 1 + 4 files changed, 114 insertions(+) create mode 100644 build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt create mode 100644 build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index aa0e615ad..2c84a8003 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -110,5 +110,9 @@ gradlePlugin { id = "nowinandroid.jvm.library" implementationClass = "JvmLibraryConventionPlugin" } + register("kmpLibrary") { + id = "nowinandroid.kmp.library" + implementationClass = "KmpLibraryConventionPlugin" + } } } diff --git a/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt new file mode 100644 index 000000000..ed9a20f47 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt @@ -0,0 +1,32 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.google.samples.apps.nowinandroid.configureKotlinMultiplatform +import org.gradle.api.Plugin +import org.gradle.api.Project + +/** + * A plugin that applies the Kotlin Multiplatform plugin and configures it for the project. + * https://github.com/cashapp/sqldelight/blob/master/buildLogic/multiplatform-convention/src/main/kotlin/app/cash/sqldelight/multiplatform/MultiplatformConventions.kt + */ +class KmpLibraryConventionPlugin: Plugin { + override fun apply(target: Project) { + with(target) { + plugins.apply("org.jetbrains.kotlin.multiplatform") + configureKotlinMultiplatform() + } + } +} diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt new file mode 100644 index 000000000..cd5ca7545 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt @@ -0,0 +1,77 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid + +import org.gradle.api.Project +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.dsl.kotlinExtension +import org.jetbrains.kotlin.konan.target.HostManager + +internal fun Project.configureKotlinMultiplatform() { + (project.kotlinExtension as KotlinMultiplatformExtension).apply { + jvm() + + js { + browser { + testTask { + useKarma { + useChromeHeadless() + } + } + } + compilations.configureEach { + kotlinOptions { + moduleKind = "umd" + } + } + } + + // tier 1 + linuxX64() + macosX64() + macosArm64() + iosSimulatorArm64() + iosX64() + + // tier 2 + linuxArm64() + watchosSimulatorArm64() + watchosX64() + watchosArm32() + watchosArm64() + tvosSimulatorArm64() + tvosX64() + tvosArm64() + iosArm64() + + // tier 3 + androidNativeArm32() + androidNativeArm64() + androidNativeX86() + androidNativeX64() + mingwX64() + watchosDeviceArm64() + + // linking fails for the linux test build if not built on a linux host + // ensure the tests and linking for them is only done on linux hosts + project.tasks.named("linuxX64Test") { enabled = HostManager.hostIsLinux } + project.tasks.named("linkDebugTestLinuxX64") { enabled = HostManager.hostIsLinux } + + project.tasks.named("mingwX64Test") { enabled = HostManager.hostIsMingw } + project.tasks.named("linkDebugTestMingwX64") { enabled = HostManager.hostIsMingw } + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index db9ca3ed0..117fefa4c 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -202,3 +202,4 @@ nowinandroid-android-lint = { id = "nowinandroid.android.lint", version = "unspe nowinandroid-android-room = { id = "nowinandroid.android.room", version = "unspecified" } nowinandroid-android-test = { id = "nowinandroid.android.test", version = "unspecified" } nowinandroid-jvm-library = { id = "nowinandroid.jvm.library", version = "unspecified" } +nowinandroid-kmp-library = { id = "nowinandroid.kmp.library", version = "unspecified" } From 218d820b6c049ad3cf618b4e6864605fa2898bbf Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 8 Feb 2024 13:18:32 -0800 Subject: [PATCH 05/35] Create commonMain for the model module --- .../apps/nowinandroid/KotlinMultiplatform.kt | 4 ++-- core/model/build.gradle.kts | 15 +++++++++++---- .../core/model/data/DarkThemeConfig.kt | 0 .../core/model/data/FollowableTopic.kt | 0 .../nowinandroid/core/model/data/NewsResource.kt | 0 .../nowinandroid/core/model/data/SearchResult.kt | 0 .../nowinandroid/core/model/data/ThemeBrand.kt | 0 .../apps/nowinandroid/core/model/data/Topic.kt | 0 .../apps/nowinandroid/core/model/data/UserData.kt | 0 .../core/model/data/UserNewsResource.kt | 0 .../core/model/data/UserSearchResult.kt | 0 11 files changed, 13 insertions(+), 6 deletions(-) rename core/model/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/model/data/DarkThemeConfig.kt (100%) rename core/model/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/model/data/FollowableTopic.kt (100%) rename core/model/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/model/data/NewsResource.kt (100%) rename core/model/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/model/data/SearchResult.kt (100%) rename core/model/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/model/data/ThemeBrand.kt (100%) rename core/model/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/model/data/Topic.kt (100%) rename core/model/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserData.kt (100%) rename core/model/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserNewsResource.kt (100%) rename core/model/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserSearchResult.kt (100%) diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt index cd5ca7545..225588b99 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt @@ -17,12 +17,12 @@ package com.google.samples.apps.nowinandroid import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension -import org.jetbrains.kotlin.gradle.dsl.kotlinExtension import org.jetbrains.kotlin.konan.target.HostManager internal fun Project.configureKotlinMultiplatform() { - (project.kotlinExtension as KotlinMultiplatformExtension).apply { + extensions.configure { jvm() js { diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts index 5d6cabfdf..d7844ecc1 100644 --- a/core/model/build.gradle.kts +++ b/core/model/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,9 +15,16 @@ */ plugins { - alias(libs.plugins.nowinandroid.jvm.library) + alias(libs.plugins.nowinandroid.kmp.library) } -dependencies { - api(libs.kotlinx.datetime) +kotlin { + sourceSets { + commonMain.dependencies { + api(libs.kotlinx.datetime) + } + commonTest.dependencies { + implementation(libs.kotlin.test) + } + } } diff --git a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/DarkThemeConfig.kt b/core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/DarkThemeConfig.kt similarity index 100% rename from core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/DarkThemeConfig.kt rename to core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/DarkThemeConfig.kt diff --git a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/FollowableTopic.kt b/core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/FollowableTopic.kt similarity index 100% rename from core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/FollowableTopic.kt rename to core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/FollowableTopic.kt diff --git a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/NewsResource.kt b/core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/NewsResource.kt similarity index 100% rename from core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/NewsResource.kt rename to core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/NewsResource.kt diff --git a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/SearchResult.kt b/core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/SearchResult.kt similarity index 100% rename from core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/SearchResult.kt rename to core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/SearchResult.kt diff --git a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/ThemeBrand.kt b/core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/ThemeBrand.kt similarity index 100% rename from core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/ThemeBrand.kt rename to core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/ThemeBrand.kt diff --git a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/Topic.kt b/core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/Topic.kt similarity index 100% rename from core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/Topic.kt rename to core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/Topic.kt diff --git a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserData.kt b/core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserData.kt similarity index 100% rename from core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserData.kt rename to core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserData.kt diff --git a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserNewsResource.kt b/core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserNewsResource.kt similarity index 100% rename from core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserNewsResource.kt rename to core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserNewsResource.kt diff --git a/core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserSearchResult.kt b/core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserSearchResult.kt similarity index 100% rename from core/model/src/main/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserSearchResult.kt rename to core/model/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/model/data/UserSearchResult.kt From 85325f126758c0a7e2aa712acb89966517c522d1 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 8 Feb 2024 14:17:30 -0800 Subject: [PATCH 06/35] Fix compilation errors --- .../apps/nowinandroid/KotlinMultiplatform.kt | 22 +- kotlin-js-store/yarn.lock | 1994 +++++++++++++++++ settings.gradle.kts | 5 +- 3 files changed, 2011 insertions(+), 10 deletions(-) create mode 100644 kotlin-js-store/yarn.lock diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt index 225588b99..80ac2dbdd 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt @@ -58,20 +58,24 @@ internal fun Project.configureKotlinMultiplatform() { tvosArm64() iosArm64() - // tier 3 - androidNativeArm32() - androidNativeArm64() - androidNativeX86() - androidNativeX64() - mingwX64() - watchosDeviceArm64() + // fix :core:model:androidNativeArm32Main: Could not resolve org.jetbrains.kotlinx:kotlinx-datetime +// // tier 3 +// androidNativeArm32() +// androidNativeArm64() +// androidNativeX86() +// androidNativeX64() +// mingwX64() +// watchosDeviceArm64() // linking fails for the linux test build if not built on a linux host // ensure the tests and linking for them is only done on linux hosts project.tasks.named("linuxX64Test") { enabled = HostManager.hostIsLinux } project.tasks.named("linkDebugTestLinuxX64") { enabled = HostManager.hostIsLinux } - project.tasks.named("mingwX64Test") { enabled = HostManager.hostIsMingw } - project.tasks.named("linkDebugTestMingwX64") { enabled = HostManager.hostIsMingw } + // Fixes Cannot locate tasks that match ':core:model:testClasses' as task 'testClasses' + // not found in project ':core:model'. Some candidates are: 'jsTestClasses', 'jvmTestClasses'. + project.tasks.create("testClasses") { + dependsOn("allTests") + } } } diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock new file mode 100644 index 000000000..e7d5142ea --- /dev/null +++ b/kotlin-js-store/yarn.lock @@ -0,0 +1,1994 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@colors/colors@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" + integrity sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ== + +"@discoveryjs/json-ext@^0.5.0": + version "0.5.7" + resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" + integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== + +"@jridgewell/gen-mapping@^0.3.0": + version "0.3.3" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.3.tgz#7e02e6eb5df901aaedb08514203b096614024098" + integrity sha512-HLhSWOLRi875zjjMG/r+Nv0oCW8umGb0BgEhyX3dDX3egwZtB8PqLnjz3yedt8R5StBrzcg4aBpnh8UA9D1BoQ== + dependencies: + "@jridgewell/set-array" "^1.0.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.1" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz#c08679063f279615a3326583ba3a90d1d82cc721" + integrity sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA== + +"@jridgewell/set-array@^1.0.1": + version "1.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.1.2.tgz#7c6cf998d6d20b914c0a55a91ae928ff25965e72" + integrity sha512-xnkseuNADM0gt2bs+BvhO0p78Mk762YnZdsuzFV018NoG1Sj1SCQvpSqa7XUaTam5vAGasABV9qXASMKnFMwMw== + +"@jridgewell/source-map@^0.3.3": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.5.tgz#a3bb4d5c6825aab0d281268f47f6ad5853431e91" + integrity sha512-UTYAUj/wviwdsMfzoSJspJxbkH5o1snzwX0//0ENX1u/55kkZZkcTZP6u9bwKGkv+dkk9at4m1Cpt0uY80kcpQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.0" + "@jridgewell/trace-mapping" "^0.3.9" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.4.15" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz#d7c6e6755c78567a951e04ab52ef0fd26de59f32" + integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg== + +"@jridgewell/trace-mapping@^0.3.20", "@jridgewell/trace-mapping@^0.3.9": + version "0.3.22" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.22.tgz#72a621e5de59f5f1ef792d0793a82ee20f645e4c" + integrity sha512-Wf963MzWtA2sjrNt+g18IAln9lKnlRp+K2eH4jjIoF1wYeq3aMREpG09xhlhdzS0EjwU7qmUJYangWa+151vZw== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@js-joda/core@3.2.0": + version "3.2.0" + resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" + integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== + +"@socket.io/component-emitter@~3.1.0": + version "3.1.0" + resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" + integrity sha512-+9jVqKhRSpsc591z5vX+X5Yyw+he/HCB4iQ/RYxw35CEPaY1gnsNE43nf9n9AaYjAQrTiI/mOwKUKdUs9vf7Xg== + +"@types/cookie@^0.4.1": + version "0.4.1" + resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d" + integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q== + +"@types/cors@^2.8.12": + version "2.8.17" + resolved "https://registry.yarnpkg.com/@types/cors/-/cors-2.8.17.tgz#5d718a5e494a8166f569d986794e49c48b216b2b" + integrity sha512-8CGDvrBj1zgo2qE+oS3pOCyYNqCPryMWY2bGfwA0dcfopWGgxs+78df0Rs3rc9THP4JkOhLsAa+15VdpAqkcUA== + dependencies: + "@types/node" "*" + +"@types/eslint-scope@^3.7.3": + version "3.7.7" + resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.7.tgz#3108bd5f18b0cdb277c867b3dd449c9ed7079ac5" + integrity sha512-MzMFlSLBqNF2gcHWO0G1vP/YQyfvrxZ0bF+u7mzUdZ1/xK4A4sru+nraZz5i3iEIk1l1uyicaDVTB4QbbEkAYg== + dependencies: + "@types/eslint" "*" + "@types/estree" "*" + +"@types/eslint@*": + version "8.56.2" + resolved "https://registry.yarnpkg.com/@types/eslint/-/eslint-8.56.2.tgz#1c72a9b794aa26a8b94ad26d5b9aa51c8a6384bb" + integrity sha512-uQDwm1wFHmbBbCZCqAlq6Do9LYwByNZHWzXppSnay9SuwJ+VRbjkbLABer54kcPnMSlG6Fdiy2yaFXm/z9Z5gw== + dependencies: + "@types/estree" "*" + "@types/json-schema" "*" + +"@types/estree@*", "@types/estree@^1.0.0": + version "1.0.5" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4" + integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw== + +"@types/json-schema@*", "@types/json-schema@^7.0.8": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +"@types/node@*", "@types/node@>=10.0.0": + version "20.11.17" + resolved "https://registry.yarnpkg.com/@types/node/-/node-20.11.17.tgz#cdd642d0e62ef3a861f88ddbc2b61e32578a9292" + integrity sha512-QmgQZGWu1Yw9TDyAP9ZzpFJKynYNeOvwMJmaxABfieQoVoiVOS6MN1WSpqpRcbeA5+RW82kraAVxCCJg+780Qw== + dependencies: + undici-types "~5.26.4" + +"@webassemblyjs/ast@1.11.6", "@webassemblyjs/ast@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.6.tgz#db046555d3c413f8966ca50a95176a0e2c642e24" + integrity sha512-IN1xI7PwOvLPgjcf180gC1bqn3q/QaOCwYUahIOhbYUu8KA/3tw2RT/T0Gidi1l7Hhj5D/INhJxiICObqpMu4Q== + dependencies: + "@webassemblyjs/helper-numbers" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + +"@webassemblyjs/floating-point-hex-parser@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/floating-point-hex-parser/-/floating-point-hex-parser-1.11.6.tgz#dacbcb95aff135c8260f77fa3b4c5fea600a6431" + integrity sha512-ejAj9hfRJ2XMsNHk/v6Fu2dGS+i4UaXBXGemOfQ/JfQ6mdQg/WXtwleQRLLS4OvfDhv8rYnVwH27YJLMyYsxhw== + +"@webassemblyjs/helper-api-error@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-api-error/-/helper-api-error-1.11.6.tgz#6132f68c4acd59dcd141c44b18cbebbd9f2fa768" + integrity sha512-o0YkoP4pVu4rN8aTJgAyj9hC2Sv5UlkzCHhxqWj8butaLvnpdc2jOwh4ewE6CX0txSfLn/UYaV/pheS2Txg//Q== + +"@webassemblyjs/helper-buffer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-buffer/-/helper-buffer-1.11.6.tgz#b66d73c43e296fd5e88006f18524feb0f2c7c093" + integrity sha512-z3nFzdcp1mb8nEOFFk8DrYLpHvhKC3grJD2ardfKOzmbmJvEf/tPIqCY+sNcwZIY8ZD7IkB2l7/pqhUhqm7hLA== + +"@webassemblyjs/helper-numbers@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-numbers/-/helper-numbers-1.11.6.tgz#cbce5e7e0c1bd32cf4905ae444ef64cea919f1b5" + integrity sha512-vUIhZ8LZoIWHBohiEObxVm6hwP034jwmc9kuq5GdHZH0wiLVLIPcMCdpJzG4C11cHoQ25TFIQj9kaVADVX7N3g== + dependencies: + "@webassemblyjs/floating-point-hex-parser" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webassemblyjs/helper-wasm-bytecode@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-bytecode/-/helper-wasm-bytecode-1.11.6.tgz#bb2ebdb3b83aa26d9baad4c46d4315283acd51e9" + integrity sha512-sFFHKwcmBprO9e7Icf0+gddyWYDViL8bpPjJJl0WHxCdETktXdmtWLGVzoHbqUcY4Be1LkNfwTmXOJUFZYSJdA== + +"@webassemblyjs/helper-wasm-section@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/helper-wasm-section/-/helper-wasm-section-1.11.6.tgz#ff97f3863c55ee7f580fd5c41a381e9def4aa577" + integrity sha512-LPpZbSOwTpEC2cgn4hTydySy1Ke+XEu+ETXuoyvuyezHO3Kjdu90KK95Sh9xTbmjrCsUwvWwCOQQNta37VrS9g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + +"@webassemblyjs/ieee754@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/ieee754/-/ieee754-1.11.6.tgz#bb665c91d0b14fffceb0e38298c329af043c6e3a" + integrity sha512-LM4p2csPNvbij6U1f19v6WR56QZ8JcHg3QIJTlSwzFcmx6WSORicYj6I63f9yU1kEUtrpG+kjkiIAkevHpDXrg== + dependencies: + "@xtuc/ieee754" "^1.2.0" + +"@webassemblyjs/leb128@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/leb128/-/leb128-1.11.6.tgz#70e60e5e82f9ac81118bc25381a0b283893240d7" + integrity sha512-m7a0FhE67DQXgouf1tbN5XQcdWoNgaAuoULHIfGFIEVKA6tu/edls6XnIlkmS6FrXAquJRPni3ZZKjw6FSPjPQ== + dependencies: + "@xtuc/long" "4.2.2" + +"@webassemblyjs/utf8@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/utf8/-/utf8-1.11.6.tgz#90f8bc34c561595fe156603be7253cdbcd0fab5a" + integrity sha512-vtXf2wTQ3+up9Zsg8sa2yWiQpzSsMyXj0qViVP6xKGCUT8p8YJ6HqI7l5eCnWx1T/FYdsv07HQs2wTFbbof/RA== + +"@webassemblyjs/wasm-edit@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-edit/-/wasm-edit-1.11.6.tgz#c72fa8220524c9b416249f3d94c2958dfe70ceab" + integrity sha512-Ybn2I6fnfIGuCR+Faaz7YcvtBKxvoLV3Lebn1tM4o/IAJzmi9AWYIPWpyBfU8cC+JxAO57bk4+zdsTjJR+VTOw== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/helper-wasm-section" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-opt" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + "@webassemblyjs/wast-printer" "1.11.6" + +"@webassemblyjs/wasm-gen@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-gen/-/wasm-gen-1.11.6.tgz#fb5283e0e8b4551cc4e9c3c0d7184a65faf7c268" + integrity sha512-3XOqkZP/y6B4F0PBAXvI1/bky7GryoogUtfwExeP/v7Nzwo1QLcq5oQmpKlftZLbT+ERUOAZVQjuNVak6UXjPA== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wasm-opt@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-opt/-/wasm-opt-1.11.6.tgz#d9a22d651248422ca498b09aa3232a81041487c2" + integrity sha512-cOrKuLRE7PCe6AsOVl7WasYf3wbSo4CeOk6PkrjS7g57MFfVUF9u6ysQBBODX0LdgSvQqRiGz3CXvIDKcPNy4g== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-buffer" "1.11.6" + "@webassemblyjs/wasm-gen" "1.11.6" + "@webassemblyjs/wasm-parser" "1.11.6" + +"@webassemblyjs/wasm-parser@1.11.6", "@webassemblyjs/wasm-parser@^1.11.5": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wasm-parser/-/wasm-parser-1.11.6.tgz#bb85378c527df824004812bbdb784eea539174a1" + integrity sha512-6ZwPeGzMJM3Dqp3hCsLgESxBGtT/OeCvCZ4TA1JUPYgmhAx38tTPR9JaKy0S5H3evQpO/h2uWs2j6Yc/fjkpTQ== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@webassemblyjs/helper-api-error" "1.11.6" + "@webassemblyjs/helper-wasm-bytecode" "1.11.6" + "@webassemblyjs/ieee754" "1.11.6" + "@webassemblyjs/leb128" "1.11.6" + "@webassemblyjs/utf8" "1.11.6" + +"@webassemblyjs/wast-printer@1.11.6": + version "1.11.6" + resolved "https://registry.yarnpkg.com/@webassemblyjs/wast-printer/-/wast-printer-1.11.6.tgz#a7bf8dd7e362aeb1668ff43f35cb849f188eff20" + integrity sha512-JM7AhRcE+yW2GWYaKeHL5vt4xqee5N2WcezptmgyhNS+ScggqcT1OtXykhAb13Sn5Yas0j2uv9tHgrjwvzAP4A== + dependencies: + "@webassemblyjs/ast" "1.11.6" + "@xtuc/long" "4.2.2" + +"@webpack-cli/configtest@^2.1.0": + version "2.1.1" + resolved "https://registry.yarnpkg.com/@webpack-cli/configtest/-/configtest-2.1.1.tgz#3b2f852e91dac6e3b85fb2a314fb8bef46d94646" + integrity sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw== + +"@webpack-cli/info@^2.0.1": + version "2.0.2" + resolved "https://registry.yarnpkg.com/@webpack-cli/info/-/info-2.0.2.tgz#cc3fbf22efeb88ff62310cf885c5b09f44ae0fdd" + integrity sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A== + +"@webpack-cli/serve@^2.0.3": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@webpack-cli/serve/-/serve-2.0.5.tgz#325db42395cd49fe6c14057f9a900e427df8810e" + integrity sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ== + +"@xtuc/ieee754@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@xtuc/ieee754/-/ieee754-1.2.0.tgz#eef014a3145ae477a1cbc00cd1e552336dceb790" + integrity sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA== + +"@xtuc/long@4.2.2": + version "4.2.2" + resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" + integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== + +abab@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.6.tgz#41b80f2c871d19686216b82309231cfd3cb3d291" + integrity sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA== + +accepts@~1.3.4: + version "1.3.8" + resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" + integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== + dependencies: + mime-types "~2.1.34" + negotiator "0.6.3" + +acorn-import-assertions@^1.7.6: + version "1.9.0" + resolved "https://registry.yarnpkg.com/acorn-import-assertions/-/acorn-import-assertions-1.9.0.tgz#507276249d684797c84e0734ef84860334cfb1ac" + integrity sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA== + +acorn@^8.7.1, acorn@^8.8.2: + version "8.11.3" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.3.tgz#71e0b14e13a4ec160724b38fb7b0f233b1b81d7a" + integrity sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg== + +ajv-keywords@^3.5.2: + version "3.5.2" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.5.2.tgz#31f29da5ab6e00d1c2d329acf7b5929614d5014d" + integrity sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ== + +ajv@^6.12.5: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-colors@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" + integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== + +ansi-regex@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" + integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ== + +ansi-styles@^4.0.0, ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +anymatch@~3.1.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e" + integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw== + dependencies: + normalize-path "^3.0.0" + picomatch "^2.0.4" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +base64id@2.0.0, base64id@~2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/base64id/-/base64id-2.0.0.tgz#2770ac6bc47d312af97a8bf9a634342e0cd25cb6" + integrity sha512-lGe34o6EHj9y3Kts9R4ZYs/Gr+6N7MCaMlIFA3F1R2O5/m7K06AxfSeO5530PEERE6/WyEg3lsuyw4GHlPZHog== + +binary-extensions@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" + integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== + +body-parser@^1.19.0: + version "1.20.2" + resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.2.tgz#6feb0e21c4724d06de7ff38da36dad4f57a747fd" + integrity sha512-ml9pReCu3M61kGlqoTm2umSXTlRTuGTx0bfYj+uIUKKYycG5NtSbeetV3faSU6R7ajOPw0g/J1PvK4qNy7s5bA== + dependencies: + bytes "3.1.2" + content-type "~1.0.5" + debug "2.6.9" + depd "2.0.0" + destroy "1.2.0" + http-errors "2.0.0" + iconv-lite "0.4.24" + on-finished "2.4.1" + qs "6.11.0" + raw-body "2.5.2" + type-is "~1.6.18" + unpipe "1.0.0" + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +brace-expansion@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.0.1.tgz#1edc459e0f0c548486ecf9fc99f2221364b9a0ae" + integrity sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA== + dependencies: + balanced-match "^1.0.0" + +braces@^3.0.2, braces@~3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" + integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== + dependencies: + fill-range "^7.0.1" + +browser-stdout@1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" + integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== + +browserslist@^4.14.5: + version "4.22.3" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.22.3.tgz#299d11b7e947a6b843981392721169e27d60c5a6" + integrity sha512-UAp55yfwNv0klWNapjs/ktHoguxuQNGnOzxYmfnXIS+8AsRDZkSDxg7R1AX3GKzn078SBI5dzwzj/Yx0Or0e3A== + dependencies: + caniuse-lite "^1.0.30001580" + electron-to-chromium "^1.4.648" + node-releases "^2.0.14" + update-browserslist-db "^1.0.13" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +bytes@3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.2.tgz#8b0beeb98605adf1b128fa4386403c009e0221a5" + integrity sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg== + +call-bind@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.6.tgz#6c46675fc7a5e9de82d75a233d586c8b7ac0d931" + integrity sha512-Mj50FLHtlsoVfRfnHaZvyrooHcrlceNZdL/QBvJJVd9Ta55qCQK0gs4ss2oZDeV9zFCs6ewzYgVE5yfVmfFpVg== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.3" + set-function-length "^1.2.0" + +camelcase@^6.0.0: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + +caniuse-lite@^1.0.30001580: + version "1.0.30001585" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001585.tgz#0b4e848d84919c783b2a41c13f7de8ce96744401" + integrity sha512-yr2BWR1yLXQ8fMpdS/4ZZXpseBgE7o4g41x3a6AJOqZuOi+iE/WdJYAuZ6Y95i4Ohd2Y+9MzIWRR+uGABH4s3Q== + +chalk@^4.1.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +chokidar@3.5.3: + version "3.5.3" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" + integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chokidar@^3.5.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.6.0.tgz#197c6cc669ef2a8dc5e7b4d97ee4e092c3eb0d5b" + integrity sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw== + dependencies: + anymatch "~3.1.2" + braces "~3.0.2" + glob-parent "~5.1.2" + is-binary-path "~2.1.0" + is-glob "~4.0.1" + normalize-path "~3.0.0" + readdirp "~3.6.0" + optionalDependencies: + fsevents "~2.3.2" + +chrome-trace-event@^1.0.2: + version "1.0.3" + resolved "https://registry.yarnpkg.com/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz#1015eced4741e15d06664a957dbbf50d041e26ac" + integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg== + +cliui@^7.0.2: + version "7.0.4" + resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" + integrity sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ== + dependencies: + string-width "^4.2.0" + strip-ansi "^6.0.0" + wrap-ansi "^7.0.0" + +clone-deep@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-4.0.1.tgz#c19fd9bdbbf85942b4fd979c84dcf7d5f07c2387" + integrity sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ== + dependencies: + is-plain-object "^2.0.4" + kind-of "^6.0.2" + shallow-clone "^3.0.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +colorette@^2.0.14: + version "2.0.20" + resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a" + integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w== + +commander@^10.0.1: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +connect@^3.7.0: + version "3.7.0" + resolved "https://registry.yarnpkg.com/connect/-/connect-3.7.0.tgz#5d49348910caa5e07a01800b030d0c35f20484f8" + integrity sha512-ZqRXc+tZukToSNmh5C2iWMSoV3X1YUcPbqEM4DkEG5tNQXrQUZCNVGGv3IuicnkMtPfGf3Xtp8WCXs295iQ1pQ== + dependencies: + debug "2.6.9" + finalhandler "1.1.2" + parseurl "~1.3.3" + utils-merge "1.0.1" + +content-type@~1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918" + integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA== + +cookie@~0.4.1: + version "0.4.2" + resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" + integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== + +cors@~2.8.5: + version "2.8.5" + resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" + integrity sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g== + dependencies: + object-assign "^4" + vary "^1" + +cross-spawn@^7.0.3: + version "7.0.3" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" + integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +custom-event@~1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/custom-event/-/custom-event-1.0.1.tgz#5d02a46850adf1b4a317946a3928fccb5bfd0425" + integrity sha512-GAj5FOq0Hd+RsCGVJxZuKaIDXDf3h6GQoNEjFgbLLI/trgtavwUbSnZ5pVfg27DVCaWjIohryS0JFwIJyT2cMg== + +date-format@^4.0.14: + version "4.0.14" + resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" + integrity sha512-39BOQLs9ZjKh0/patS9nrT8wc3ioX3/eA/zgbKNopnF2wCqJEoxywwwElATYvRsXdnOxA/OQeQoFZ3rFjVajhg== + +debug@2.6.9: + version "2.6.9" + resolved "https://registry.yarnpkg.com/debug/-/debug-2.6.9.tgz#5d128515df134ff327e90a4c93f4e077a536341f" + integrity sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA== + dependencies: + ms "2.0.0" + +debug@4.3.4, debug@^4.3.4, debug@~4.3.1, debug@~4.3.2: + version "4.3.4" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" + integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== + dependencies: + ms "2.1.2" + +decamelize@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-4.0.0.tgz#aa472d7bf660eb15f3494efd531cab7f2a709837" + integrity sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ== + +define-data-property@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.2.tgz#f3c33b4f0102360cd7c0f5f28700f5678510b63a" + integrity sha512-SRtsSqsDbgpJBbW3pABMCOt6rQyeM8s8RiyeSN8jYG8sYmt/kGJejbydttUsnDs1tadr19tvhT4ShwMyoqAm4g== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.2" + gopd "^1.0.1" + has-property-descriptors "^1.0.1" + +depd@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" + integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== + +destroy@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.2.0.tgz#4803735509ad8be552934c67df614f94e66fa015" + integrity sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg== + +di@^0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/di/-/di-0.0.1.tgz#806649326ceaa7caa3306d75d985ea2748ba913c" + integrity sha512-uJaamHkagcZtHPqCIHZxnFrXlunQXgBOsZSUOWwFw31QJCAbyTBoHMW75YOTur5ZNx8pIeAKgf6GWIgaqqiLhA== + +diff@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" + integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== + +dom-serialize@^2.2.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" + integrity sha512-Yra4DbvoW7/Z6LBN560ZwXMjoNOSAN2wRsKFGc4iBeso+mpIA6qj1vfdf9HpMaKAqG6wXTy+1SYEzmNpKXOSsQ== + dependencies: + custom-event "~1.0.0" + ent "~2.2.0" + extend "^3.0.0" + void-elements "^2.0.0" + +ee-first@1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" + integrity sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow== + +electron-to-chromium@^1.4.648: + version "1.4.662" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.662.tgz#0e014d31687210312c5f601bc9edcae2fa1663ef" + integrity sha512-gfl1XVWTQmPHhqEG0kN77SpUxaqPpMb9r83PT4gvKhg7P3irSxru3lW85RxvK1uI1j2CAcTWPjG/HbE0IP/Rtg== + +emoji-regex@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37" + integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A== + +encodeurl@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" + integrity sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w== + +engine.io-parser@~5.2.1: + version "5.2.2" + resolved "https://registry.yarnpkg.com/engine.io-parser/-/engine.io-parser-5.2.2.tgz#37b48e2d23116919a3453738c5720455e64e1c49" + integrity sha512-RcyUFKA93/CXH20l4SoVvzZfrSDMOTUS3bWVpTt2FuFP+XYrL8i8oonHP7WInRyVHXh0n/ORtoeiE1os+8qkSw== + +engine.io@~6.5.2: + version "6.5.4" + resolved "https://registry.yarnpkg.com/engine.io/-/engine.io-6.5.4.tgz#6822debf324e781add2254e912f8568508850cdc" + integrity sha512-KdVSDKhVKyOi+r5uEabrDLZw2qXStVvCsEB/LN3mw4WFi6Gx50jTyuxYVCwAAC0U46FdnzP/ScKRBTXb/NiEOg== + dependencies: + "@types/cookie" "^0.4.1" + "@types/cors" "^2.8.12" + "@types/node" ">=10.0.0" + accepts "~1.3.4" + base64id "2.0.0" + cookie "~0.4.1" + cors "~2.8.5" + debug "~4.3.1" + engine.io-parser "~5.2.1" + ws "~8.11.0" + +enhanced-resolve@^5.13.0: + version "5.15.0" + resolved "https://registry.yarnpkg.com/enhanced-resolve/-/enhanced-resolve-5.15.0.tgz#1af946c7d93603eb88e9896cee4904dc012e9c35" + integrity sha512-LXYT42KJ7lpIKECr2mAXIaMldcNCh/7E0KBKOu4KSfkHmP+mZmSs+8V5gBAqisWBy0OO4W5Oyys0GO1Y8KtdKg== + dependencies: + graceful-fs "^4.2.4" + tapable "^2.2.0" + +ent@~2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d" + integrity sha512-GHrMyVZQWvTIdDtpiEXdHZnFQKzeO09apj8Cbl4pKWy4i0Oprcq17usfDt5aO63swf0JOeMWjWQE/LzgSRuWpA== + +envinfo@^7.7.3: + version "7.11.1" + resolved "https://registry.yarnpkg.com/envinfo/-/envinfo-7.11.1.tgz#2ffef77591057081b0129a8fd8cf6118da1b94e1" + integrity sha512-8PiZgZNIB4q/Lw4AhOvAfB/ityHAd2bli3lESSWmWSzSsl5dKpy5N1d1Rfkd2teq/g9xN90lc6o98DOjMeYHpg== + +es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-module-lexer@^1.2.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/es-module-lexer/-/es-module-lexer-1.4.1.tgz#41ea21b43908fe6a287ffcbe4300f790555331f5" + integrity sha512-cXLGjP0c4T3flZJKQSuziYoq7MlT+rnvfZjfp7h+I7K9BNX54kP9nyWvdbwjQ4u1iWbOL4u96fgeZLToQlZC7w== + +escalade@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.1.2.tgz#54076e9ab29ea5bf3d8f1ed62acffbb88272df27" + integrity sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA== + +escape-html@~1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" + integrity sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow== + +escape-string-regexp@4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@5.1.1: + version "5.1.1" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.1.1.tgz#e786e59a66cb92b3f6c1fb0d508aab174848f48c" + integrity sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw== + dependencies: + esrecurse "^4.3.0" + estraverse "^4.1.1" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^4.1.1: + version "4.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-4.3.0.tgz#398ad3f3c5a24948be7725e83d11a7de28cdbd1d" + integrity sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw== + +estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +eventemitter3@^4.0.0: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + +events@^3.2.0: + version "3.3.0" + resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" + integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q== + +extend@^3.0.0: + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +fast-deep-equal@^3.1.1: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fastest-levenshtein@^1.0.12: + version "1.0.16" + resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" + integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== + +fill-range@^7.0.1: + version "7.0.1" + resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" + integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== + dependencies: + to-regex-range "^5.0.1" + +finalhandler@1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/finalhandler/-/finalhandler-1.1.2.tgz#b7e7d000ffd11938d0fdb053506f6ebabe9f587d" + integrity sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA== + dependencies: + debug "2.6.9" + encodeurl "~1.0.2" + escape-html "~1.0.3" + on-finished "~2.3.0" + parseurl "~1.3.3" + statuses "~1.5.0" + unpipe "~1.0.0" + +find-up@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +find-up@^4.0.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-4.1.0.tgz#97afe7d6cdc0bc5928584b7c8d7b16e8a9aa5d19" + integrity sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw== + dependencies: + locate-path "^5.0.0" + path-exists "^4.0.0" + +flat@^5.0.2: + version "5.0.2" + resolved "https://registry.yarnpkg.com/flat/-/flat-5.0.2.tgz#8ca6fe332069ffa9d324c327198c598259ceb241" + integrity sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ== + +flatted@^3.2.7: + version "3.2.9" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.2.9.tgz#7eb4c67ca1ba34232ca9d2d93e9886e611ad7daf" + integrity sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ== + +follow-redirects@^1.0.0: + version "1.15.5" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.5.tgz#54d4d6d062c0fa7d9d17feb008461550e3ba8020" + integrity sha512-vSFWUON1B+yAw1VN4xMfxgn5fTUiaOzAJCKBwIIgT/+7CuGy9+r+5gITvP62j3RmaD5Ph65UaERdOSRGUzZtgw== + +format-util@^1.0.5: + version "1.0.5" + resolved "https://registry.yarnpkg.com/format-util/-/format-util-1.0.5.tgz#1ffb450c8a03e7bccffe40643180918cc297d271" + integrity sha512-varLbTj0e0yVyRpqQhuWV+8hlePAgaoFRhNFj50BNjEIrw1/DphHSObtqwskVCPWNgzwPoQrZAbfa/SBiicNeg== + +fs-extra@^8.1.0: + version "8.1.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0" + integrity sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^4.0.0" + universalify "^0.1.0" + +fs.realpath@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f" + integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw== + +fsevents@~2.3.2: + version "2.3.3" + resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6" + integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw== + +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + +get-caller-file@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" + integrity sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg== + +get-intrinsic@^1.1.3, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + +glob-parent@~5.1.2: + version "5.1.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" + integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== + dependencies: + is-glob "^4.0.1" + +glob-to-regexp@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" + integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw== + +glob@7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" + integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.0.4" + once "^1.3.0" + path-is-absolute "^1.0.0" + +glob@^7.1.3, glob@^7.1.7: + version "7.2.3" + resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" + integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== + dependencies: + fs.realpath "^1.0.0" + inflight "^1.0.4" + inherits "2" + minimatch "^3.1.1" + once "^1.3.0" + path-is-absolute "^1.0.0" + +gopd@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" + integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA== + dependencies: + get-intrinsic "^1.1.3" + +graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.10, graceful-fs@^4.2.4, graceful-fs@^4.2.6, graceful-fs@^4.2.9: + version "4.2.11" + resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.11.tgz#4183e4e8bf08bb6e05bbb2f7d2e0c8f712ca40e3" + integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +has-property-descriptors@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340" + integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg== + dependencies: + get-intrinsic "^1.2.2" + +has-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" + integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== + +has-symbols@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" + integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== + +hasown@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c" + integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA== + dependencies: + function-bind "^1.1.2" + +he@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" + integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== + +http-errors@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-2.0.0.tgz#b7774a1486ef73cf7667ac9ae0858c012c57b9d3" + integrity sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ== + dependencies: + depd "2.0.0" + inherits "2.0.4" + setprototypeof "1.2.0" + statuses "2.0.1" + toidentifier "1.0.1" + +http-proxy@^1.18.1: + version "1.18.1" + resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549" + integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ== + dependencies: + eventemitter3 "^4.0.0" + follow-redirects "^1.0.0" + requires-port "^1.0.0" + +iconv-lite@0.4.24: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + +iconv-lite@^0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501" + integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw== + dependencies: + safer-buffer ">= 2.1.2 < 3.0.0" + +import-local@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" + integrity sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg== + dependencies: + pkg-dir "^4.2.0" + resolve-cwd "^3.0.0" + +inflight@^1.0.4: + version "1.0.6" + resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9" + integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA== + dependencies: + once "^1.3.0" + wrappy "1" + +inherits@2, inherits@2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" + integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== + +interpret@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/interpret/-/interpret-3.1.1.tgz#5be0ceed67ca79c6c4bc5cf0d7ee843dcea110c4" + integrity sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ== + +is-binary-path@~2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-binary-path/-/is-binary-path-2.1.0.tgz#ea1f7f3b80f064236e83470f86c09c254fb45b09" + integrity sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw== + dependencies: + binary-extensions "^2.0.0" + +is-core-module@^2.13.0: + version "2.13.1" + resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384" + integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw== + dependencies: + hasown "^2.0.0" + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-fullwidth-code-point@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" + integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== + +is-glob@^4.0.1, is-glob@~4.0.1: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +is-number@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" + integrity sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng== + +is-plain-obj@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/is-plain-obj/-/is-plain-obj-2.1.0.tgz#45e42e37fccf1f40da8e5f76ee21515840c09287" + integrity sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA== + +is-plain-object@^2.0.4: + version "2.0.4" + resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== + dependencies: + isobject "^3.0.1" + +is-unicode-supported@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz#3f26c76a809593b52bfa2ecb5710ed2779b522a7" + integrity sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw== + +isbinaryfile@^4.0.8: + version "4.0.10" + resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-4.0.10.tgz#0c5b5e30c2557a2f06febd37b7322946aaee42b3" + integrity sha512-iHrqe5shvBUcFbmZq9zOQHBoeOhZJu6RQGrDpBgenUm/Am+F3JM2MgQj+rK3Z601fzrL5gLZWtAPH2OBaSVcyw== + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +isobject@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg== + +jest-worker@^27.4.5: + version "27.5.1" + resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-27.5.1.tgz#8d146f0900e8973b106b6f73cc1e9a8cb86f8db0" + integrity sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg== + dependencies: + "@types/node" "*" + merge-stream "^2.0.0" + supports-color "^8.0.0" + +js-yaml@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-parse-even-better-errors@^2.3.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz#7c47805a94319928e05777405dc12e1f7a4ee02d" + integrity sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +jsonfile@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-4.0.0.tgz#8771aae0799b64076b76640fca058f9c10e33ecb" + integrity sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg== + optionalDependencies: + graceful-fs "^4.1.6" + +karma-chrome-launcher@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/karma-chrome-launcher/-/karma-chrome-launcher-3.2.0.tgz#eb9c95024f2d6dfbb3748d3415ac9b381906b9a9" + integrity sha512-rE9RkUPI7I9mAxByQWkGJFXfFD6lE4gC5nPuZdobf/QdTEJI6EU4yIay/cfU/xV4ZxlM5JiTv7zWYgA64NpS5Q== + dependencies: + which "^1.2.1" + +karma-mocha@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/karma-mocha/-/karma-mocha-2.0.1.tgz#4b0254a18dfee71bdbe6188d9a6861bf86b0cd7d" + integrity sha512-Tzd5HBjm8his2OA4bouAsATYEpZrp9vC7z5E5j4C5Of5Rrs1jY67RAwXNcVmd/Bnk1wgvQRou0zGVLey44G4tQ== + dependencies: + minimist "^1.2.3" + +karma-sourcemap-loader@0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/karma-sourcemap-loader/-/karma-sourcemap-loader-0.4.0.tgz#b01d73f8f688f533bcc8f5d273d43458e13b5488" + integrity sha512-xCRL3/pmhAYF3I6qOrcn0uhbQevitc2DERMPH82FMnG+4WReoGcGFZb1pURf2a5apyrOHRdvD+O6K7NljqKHyA== + dependencies: + graceful-fs "^4.2.10" + +karma-webpack@5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/karma-webpack/-/karma-webpack-5.0.0.tgz#2a2c7b80163fe7ffd1010f83f5507f95ef39f840" + integrity sha512-+54i/cd3/piZuP3dr54+NcFeKOPnys5QeM1IY+0SPASwrtHsliXUiCL50iW+K9WWA7RvamC4macvvQ86l3KtaA== + dependencies: + glob "^7.1.3" + minimatch "^3.0.4" + webpack-merge "^4.1.5" + +karma@6.4.2: + version "6.4.2" + resolved "https://registry.yarnpkg.com/karma/-/karma-6.4.2.tgz#a983f874cee6f35990c4b2dcc3d274653714de8e" + integrity sha512-C6SU/53LB31BEgRg+omznBEMY4SjHU3ricV6zBcAe1EeILKkeScr+fZXtaI5WyDbkVowJxxAI6h73NcFPmXolQ== + dependencies: + "@colors/colors" "1.5.0" + body-parser "^1.19.0" + braces "^3.0.2" + chokidar "^3.5.1" + connect "^3.7.0" + di "^0.0.1" + dom-serialize "^2.2.1" + glob "^7.1.7" + graceful-fs "^4.2.6" + http-proxy "^1.18.1" + isbinaryfile "^4.0.8" + lodash "^4.17.21" + log4js "^6.4.1" + mime "^2.5.2" + minimatch "^3.0.4" + mkdirp "^0.5.5" + qjobs "^1.2.0" + range-parser "^1.2.1" + rimraf "^3.0.2" + socket.io "^4.4.1" + source-map "^0.6.1" + tmp "^0.2.1" + ua-parser-js "^0.7.30" + yargs "^16.1.1" + +kind-of@^6.0.2: + version "6.0.3" + resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-6.0.3.tgz#07c05034a6c349fa06e24fa35aa76db4580ce4dd" + integrity sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw== + +loader-runner@^4.2.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" + integrity sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg== + +locate-path@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-5.0.0.tgz#1afba396afd676a6d42504d0a67a3a7eb9f62aa0" + integrity sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g== + dependencies: + p-locate "^4.1.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash@^4.17.15, lodash@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" + integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== + +log-symbols@4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" + integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== + dependencies: + chalk "^4.1.0" + is-unicode-supported "^0.1.0" + +log4js@^6.4.1: + version "6.9.1" + resolved "https://registry.yarnpkg.com/log4js/-/log4js-6.9.1.tgz#aba5a3ff4e7872ae34f8b4c533706753709e38b6" + integrity sha512-1somDdy9sChrr9/f4UlzhdaGfDR2c/SaD2a4T7qEkG4jTS57/B3qmnjLYePwQ8cqWnUHZI0iAKxMBpCZICiZ2g== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + flatted "^3.2.7" + rfdc "^1.3.0" + streamroller "^3.1.5" + +media-typer@0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/media-typer/-/media-typer-0.3.0.tgz#8710d7af0aa626f8fffa1ce00168545263255748" + integrity sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ== + +merge-stream@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" + integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.27, mime-types@~2.1.24, mime-types@~2.1.34: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +mime@^2.5.2: + version "2.6.0" + resolved "https://registry.yarnpkg.com/mime/-/mime-2.6.0.tgz#a2a682a95cd4d0cb1d6257e28f83da7e35800367" + integrity sha512-USPkMeET31rOMiarsBNIHZKLGgvKc/LrjofAnBlOttf5ajRvqiRA8QsenbcooctK6d6Ts6aqZXBA+XbkKthiQg== + +minimatch@5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b" + integrity sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g== + dependencies: + brace-expansion "^2.0.1" + +minimatch@^3.0.4, minimatch@^3.1.1: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +minimist@^1.2.3, minimist@^1.2.6: + version "1.2.8" + resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" + integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== + +mkdirp@^0.5.5: + version "0.5.6" + resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6" + integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw== + dependencies: + minimist "^1.2.6" + +mocha@10.2.0: + version "10.2.0" + resolved "https://registry.yarnpkg.com/mocha/-/mocha-10.2.0.tgz#1fd4a7c32ba5ac372e03a17eef435bd00e5c68b8" + integrity sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg== + dependencies: + ansi-colors "4.1.1" + browser-stdout "1.3.1" + chokidar "3.5.3" + debug "4.3.4" + diff "5.0.0" + escape-string-regexp "4.0.0" + find-up "5.0.0" + glob "7.2.0" + he "1.2.0" + js-yaml "4.1.0" + log-symbols "4.1.0" + minimatch "5.0.1" + ms "2.1.3" + nanoid "3.3.3" + serialize-javascript "6.0.0" + strip-json-comments "3.1.1" + supports-color "8.1.1" + workerpool "6.2.1" + yargs "16.2.0" + yargs-parser "20.2.4" + yargs-unparser "2.0.0" + +ms@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" + integrity sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A== + +ms@2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009" + integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== + +ms@2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +nanoid@3.3.3: + version "3.3.3" + resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25" + integrity sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w== + +negotiator@0.6.3: + version "0.6.3" + resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.3.tgz#58e323a72fedc0d6f9cd4d31fe49f51479590ccd" + integrity sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg== + +neo-async@^2.6.2: + version "2.6.2" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.2.tgz#b4aafb93e3aeb2d8174ca53cf163ab7d7308305f" + integrity sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw== + +node-releases@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.14.tgz#2ffb053bceb8b2be8495ece1ab6ce600c4461b0b" + integrity sha512-y10wOWt8yZpqXmOgRo77WaHEmhYQYGNA6y421PKsKYWEK8aW+cqAphborZDhqfyKrbZEN92CN1X2KbafY2s7Yw== + +normalize-path@^3.0.0, normalize-path@~3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/normalize-path/-/normalize-path-3.0.0.tgz#0dcd69ff23a1c9b11fd0978316644a0388216a65" + integrity sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA== + +object-assign@^4: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + +on-finished@2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" + integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== + dependencies: + ee-first "1.1.1" + +on-finished@~2.3.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" + integrity sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww== + dependencies: + ee-first "1.1.1" + +once@^1.3.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" + integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w== + dependencies: + wrappy "1" + +p-limit@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-2.3.0.tgz#3dd33c647a214fdfffd835933eb086da0dc21db1" + integrity sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w== + dependencies: + p-try "^2.0.0" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-4.1.0.tgz#a3428bb7088b3a60292f66919278b7c297ad4f07" + integrity sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A== + dependencies: + p-limit "^2.2.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +p-try@^2.0.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/p-try/-/p-try-2.2.0.tgz#cb2868540e313d61de58fafbe35ce9004d5540e6" + integrity sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ== + +parseurl@~1.3.3: + version "1.3.3" + resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" + integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-is-absolute@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" + integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +path-parse@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" + integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== + +picocolors@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" + integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== + +picomatch@^2.0.4, picomatch@^2.2.1: + version "2.3.1" + resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" + integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== + +pkg-dir@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/pkg-dir/-/pkg-dir-4.2.0.tgz#f099133df7ede422e81d1d8448270eeb3e4261f3" + integrity sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ== + dependencies: + find-up "^4.0.0" + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +qjobs@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/qjobs/-/qjobs-1.2.0.tgz#c45e9c61800bd087ef88d7e256423bdd49e5d071" + integrity sha512-8YOJEHtxpySA3fFDyCRxA+UUV+fA+rTWnuWvylOK/NCjhY+b4ocCtmu8TtsWb+mYeU+GCHf/S66KZF/AsteKHg== + +qs@6.11.0: + version "6.11.0" + resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.0.tgz#fd0d963446f7a65e1367e01abd85429453f0c37a" + integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q== + dependencies: + side-channel "^1.0.4" + +randombytes@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" + integrity sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ== + dependencies: + safe-buffer "^5.1.0" + +range-parser@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/range-parser/-/range-parser-1.2.1.tgz#3cf37023d199e1c24d1a55b84800c2f3e6468031" + integrity sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg== + +raw-body@2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a" + integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA== + dependencies: + bytes "3.1.2" + http-errors "2.0.0" + iconv-lite "0.4.24" + unpipe "1.0.0" + +readdirp@~3.6.0: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-3.6.0.tgz#74a370bd857116e245b29cc97340cd431a02a6c7" + integrity sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA== + dependencies: + picomatch "^2.2.1" + +rechoir@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/rechoir/-/rechoir-0.8.0.tgz#49f866e0d32146142da3ad8f0eff352b3215ff22" + integrity sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ== + dependencies: + resolve "^1.20.0" + +require-directory@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" + integrity sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q== + +requires-port@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff" + integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ== + +resolve-cwd@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/resolve-cwd/-/resolve-cwd-3.0.0.tgz#0f0075f1bb2544766cf73ba6a6e2adfebcb13f2d" + integrity sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg== + dependencies: + resolve-from "^5.0.0" + +resolve-from@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" + integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== + +resolve@^1.20.0: + version "1.22.8" + resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.22.8.tgz#b6c87a9f2aa06dfab52e3d70ac8cde321fa5a48d" + integrity sha512-oKWePCxqpd6FlLvGV1VU0x7bkPmmCNolxzjMf4NczoDnQcIWrAF+cPtZn5i6n+RfD2d9i0tzpKnG6Yk168yIyw== + dependencies: + is-core-module "^2.13.0" + path-parse "^1.0.7" + supports-preserve-symlinks-flag "^1.0.0" + +rfdc@^1.3.0: + version "1.3.1" + resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.1.tgz#2b6d4df52dffe8bb346992a10ea9451f24373a8f" + integrity sha512-r5a3l5HzYlIC68TpmYKlxWjmOP6wiPJ1vWv2HeLhNsRZMrCkxeqxiHlQ21oXmQ4F3SiryXBHhAD7JZqvOJjFmg== + +rimraf@^3.0.0, rimraf@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a" + integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA== + dependencies: + glob "^7.1.3" + +safe-buffer@^5.1.0: + version "5.2.1" + resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" + integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== + +"safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0": + version "2.1.2" + resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a" + integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg== + +schema-utils@^3.1.1, schema-utils@^3.1.2: + version "3.3.0" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-3.3.0.tgz#f50a88877c3c01652a15b622ae9e9795df7a60fe" + integrity sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg== + dependencies: + "@types/json-schema" "^7.0.8" + ajv "^6.12.5" + ajv-keywords "^3.5.2" + +serialize-javascript@6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.0.tgz#efae5d88f45d7924141da8b5c3a7a7e663fefeb8" + integrity sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag== + dependencies: + randombytes "^2.1.0" + +serialize-javascript@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" + integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== + dependencies: + randombytes "^2.1.0" + +set-function-length@^1.2.0: + version "1.2.1" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.1.tgz#47cc5945f2c771e2cf261c6737cf9684a2a5e425" + integrity sha512-j4t6ccc+VsKwYHso+kElc5neZpjtq9EnRICFZtWyBsLojhmeF/ZBd/elqm22WJh/BziDe/SBiOeAt0m2mfLD0g== + dependencies: + define-data-property "^1.1.2" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.1" + +setprototypeof@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/setprototypeof/-/setprototypeof-1.2.0.tgz#66c9a24a73f9fc28cbe66b09fed3d33dcaf1b424" + integrity sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw== + +shallow-clone@^3.0.0: + version "3.0.1" + resolved "https://registry.yarnpkg.com/shallow-clone/-/shallow-clone-3.0.1.tgz#8f2981ad92531f55035b01fb230769a40e02efa3" + integrity sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA== + dependencies: + kind-of "^6.0.2" + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +side-channel@^1.0.4: + version "1.0.5" + resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.5.tgz#9a84546599b48909fb6af1211708d23b1946221b" + integrity sha512-QcgiIWV4WV7qWExbN5llt6frQB/lBven9pqliLXfGPB+K9ZYXxDozp0wLkHS24kWCm+6YXH/f0HhnObZnZOBnQ== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + object-inspect "^1.13.1" + +socket.io-adapter@~2.5.2: + version "2.5.2" + resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12" + integrity sha512-87C3LO/NOMc+eMcpcxUBebGjkpMDkNBS9tf7KJqcDsmL936EChtVva71Dw2q4tQcuVC+hAUy4an2NO/sYXmwRA== + dependencies: + ws "~8.11.0" + +socket.io-parser@~4.2.4: + version "4.2.4" + resolved "https://registry.yarnpkg.com/socket.io-parser/-/socket.io-parser-4.2.4.tgz#c806966cf7270601e47469ddeec30fbdfda44c83" + integrity sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew== + dependencies: + "@socket.io/component-emitter" "~3.1.0" + debug "~4.3.1" + +socket.io@^4.4.1: + version "4.7.4" + resolved "https://registry.yarnpkg.com/socket.io/-/socket.io-4.7.4.tgz#2401a2d7101e4bdc64da80b140d5d8b6a8c7738b" + integrity sha512-DcotgfP1Zg9iP/dH9zvAQcWrE0TtbMVwXmlV4T4mqsvY+gw+LqUGPfx2AoVyRk0FLME+GQhufDMyacFmw7ksqw== + dependencies: + accepts "~1.3.4" + base64id "~2.0.0" + cors "~2.8.5" + debug "~4.3.2" + engine.io "~6.5.2" + socket.io-adapter "~2.5.2" + socket.io-parser "~4.2.4" + +source-map-js@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c" + integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw== + +source-map-loader@4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/source-map-loader/-/source-map-loader-4.0.1.tgz#72f00d05f5d1f90f80974eda781cbd7107c125f2" + integrity sha512-oqXpzDIByKONVY8g1NUPOTQhe0UTU5bWUl32GSkqK2LjJj0HmwTMVKxcUip0RgAYhY1mqgOxjbQM48a0mmeNfA== + dependencies: + abab "^2.0.6" + iconv-lite "^0.6.3" + source-map-js "^1.0.2" + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@^0.6.1: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +statuses@2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" + integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== + +statuses@~1.5.0: + version "1.5.0" + resolved "https://registry.yarnpkg.com/statuses/-/statuses-1.5.0.tgz#161c7dac177659fd9811f43771fa99381478628c" + integrity sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA== + +streamroller@^3.1.5: + version "3.1.5" + resolved "https://registry.yarnpkg.com/streamroller/-/streamroller-3.1.5.tgz#1263182329a45def1ffaef58d31b15d13d2ee7ff" + integrity sha512-KFxaM7XT+irxvdqSP1LGLgNWbYN7ay5owZ3r/8t77p+EtSUAfUgtl7be3xtqtOmGUl9K9YPO2ca8133RlTjvKw== + dependencies: + date-format "^4.0.14" + debug "^4.3.4" + fs-extra "^8.1.0" + +string-width@^4.1.0, string-width@^4.2.0: + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-json-comments@3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@8.1.1, supports-color@^8.0.0: + version "8.1.1" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-8.1.1.tgz#cd6fc17e28500cff56c1b86c0a7fd4a54a73005c" + integrity sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q== + dependencies: + has-flag "^4.0.0" + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +supports-preserve-symlinks-flag@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz#6eda4bd344a3c94aea376d4cc31bc77311039e09" + integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w== + +tapable@^2.1.1, tapable@^2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/tapable/-/tapable-2.2.1.tgz#1967a73ef4060a82f12ab96af86d52fdb76eeca0" + integrity sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ== + +terser-webpack-plugin@^5.3.7: + version "5.3.10" + resolved "https://registry.yarnpkg.com/terser-webpack-plugin/-/terser-webpack-plugin-5.3.10.tgz#904f4c9193c6fd2a03f693a2150c62a92f40d199" + integrity sha512-BKFPWlPDndPs+NGGCr1U59t0XScL5317Y0UReNrHaw9/FwhPENlq6bfgs+4yPfyP51vqC1bQ4rp1EfXW5ZSH9w== + dependencies: + "@jridgewell/trace-mapping" "^0.3.20" + jest-worker "^27.4.5" + schema-utils "^3.1.1" + serialize-javascript "^6.0.1" + terser "^5.26.0" + +terser@^5.26.0: + version "5.27.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.27.0.tgz#70108689d9ab25fef61c4e93e808e9fd092bf20c" + integrity sha512-bi1HRwVRskAjheeYl291n3JC4GgO/Ty4z1nVs5AAsmonJulGxpSektecnNedrwK9C7vpvVtcX3cw00VSLt7U2A== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +tmp@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.2.1.tgz#8457fc3037dcf4719c251367a1af6500ee1ccf14" + integrity sha512-76SUhtfqR2Ijn+xllcI5P1oyannHNHByD80W1q447gU3mp9G9PSpGdWmjUOHRDPiHYacIk66W7ubDTuPF3BEtQ== + dependencies: + rimraf "^3.0.0" + +to-regex-range@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" + integrity sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ== + dependencies: + is-number "^7.0.0" + +toidentifier@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" + integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== + +type-is@~1.6.18: + version "1.6.18" + resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" + integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== + dependencies: + media-typer "0.3.0" + mime-types "~2.1.24" + +typescript@5.0.4: + version "5.0.4" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.0.4.tgz#b217fd20119bd61a94d4011274e0ab369058da3b" + integrity sha512-cW9T5W9xY37cc+jfEnaUvX91foxtHkza3Nw3wkoF4sSlKn0MONdkdEndig/qPBWXNkmplh3NzayQzCiHM4/hqw== + +ua-parser-js@^0.7.30: + version "0.7.37" + resolved "https://registry.yarnpkg.com/ua-parser-js/-/ua-parser-js-0.7.37.tgz#e464e66dac2d33a7a1251d7d7a99d6157ec27832" + integrity sha512-xV8kqRKM+jhMvcHWUKthV9fNebIzrNy//2O9ZwWcfiBFR5f25XVZPLlEajk/sf3Ra15V92isyQqnIEXRDaZWEA== + +undici-types@~5.26.4: + version "5.26.5" + resolved "https://registry.yarnpkg.com/undici-types/-/undici-types-5.26.5.tgz#bcd539893d00b56e964fd2657a4866b221a65617" + integrity sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA== + +universalify@^0.1.0: + version "0.1.2" + resolved "https://registry.yarnpkg.com/universalify/-/universalify-0.1.2.tgz#b646f69be3942dabcecc9d6639c80dc105efaa66" + integrity sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg== + +unpipe@1.0.0, unpipe@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/unpipe/-/unpipe-1.0.0.tgz#b2bf4ee8514aae6165b4817829d21b2ef49904ec" + integrity sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ== + +update-browserslist-db@^1.0.13: + version "1.0.13" + resolved "https://registry.yarnpkg.com/update-browserslist-db/-/update-browserslist-db-1.0.13.tgz#3c5e4f5c083661bd38ef64b6328c26ed6c8248c4" + integrity sha512-xebP81SNcPuNpPP3uzeW1NYXxI3rxyJzF3pD6sH4jE7o/IX+WtSpwnVU+qIsDPyk0d3hmFQ7mjqc6AtV604hbg== + dependencies: + escalade "^3.1.1" + picocolors "^1.0.0" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +utils-merge@1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713" + integrity sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA== + +vary@^1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" + integrity sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg== + +void-elements@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/void-elements/-/void-elements-2.0.1.tgz#c066afb582bb1cb4128d60ea92392e94d5e9dbec" + integrity sha512-qZKX4RnBzH2ugr8Lxa7x+0V6XD9Sb/ouARtiasEQCHB1EVU4NXtmHsDDrx1dO4ne5fc3J6EW05BP1Dl0z0iung== + +watchpack@^2.4.0: + version "2.4.0" + resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-2.4.0.tgz#fa33032374962c78113f93c7f2fb4c54c9862a5d" + integrity sha512-Lcvm7MGST/4fup+ifyKi2hjyIAwcdI4HRgtvTpIUxBRhB+RFtUh8XtDOxUfctVCnhVi+QQj49i91OyvzkJl6cg== + dependencies: + glob-to-regexp "^0.4.1" + graceful-fs "^4.1.2" + +webpack-cli@5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/webpack-cli/-/webpack-cli-5.1.0.tgz#abc4b1f44b50250f2632d8b8b536cfe2f6257891" + integrity sha512-a7KRJnCxejFoDpYTOwzm5o21ZXMaNqtRlvS183XzGDUPRdVEzJNImcQokqYZ8BNTnk9DkKiuWxw75+DCCoZ26w== + dependencies: + "@discoveryjs/json-ext" "^0.5.0" + "@webpack-cli/configtest" "^2.1.0" + "@webpack-cli/info" "^2.0.1" + "@webpack-cli/serve" "^2.0.3" + colorette "^2.0.14" + commander "^10.0.1" + cross-spawn "^7.0.3" + envinfo "^7.7.3" + fastest-levenshtein "^1.0.12" + import-local "^3.0.2" + interpret "^3.1.1" + rechoir "^0.8.0" + webpack-merge "^5.7.3" + +webpack-merge@^4.1.5: + version "4.2.2" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-4.2.2.tgz#a27c52ea783d1398afd2087f547d7b9d2f43634d" + integrity sha512-TUE1UGoTX2Cd42j3krGYqObZbOD+xF7u28WB7tfUordytSjbWTIjK/8V0amkBfTYN4/pB/GIDlJZZ657BGG19g== + dependencies: + lodash "^4.17.15" + +webpack-merge@^5.7.3: + version "5.10.0" + resolved "https://registry.yarnpkg.com/webpack-merge/-/webpack-merge-5.10.0.tgz#a3ad5d773241e9c682803abf628d4cd62b8a4177" + integrity sha512-+4zXKdx7UnO+1jaN4l2lHVD+mFvnlZQP/6ljaJVb4SZiwIKeUnrT5l0gkT8z+n4hKpC+jpOv6O9R+gLtag7pSA== + dependencies: + clone-deep "^4.0.1" + flat "^5.0.2" + wildcard "^2.0.0" + +webpack-sources@^3.2.3: + version "3.2.3" + resolved "https://registry.yarnpkg.com/webpack-sources/-/webpack-sources-3.2.3.tgz#2d4daab8451fd4b240cc27055ff6a0c2ccea0cde" + integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== + +webpack@5.82.0: + version "5.82.0" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-5.82.0.tgz#3c0d074dec79401db026b4ba0fb23d6333f88e7d" + integrity sha512-iGNA2fHhnDcV1bONdUu554eZx+XeldsaeQ8T67H6KKHl2nUSwX8Zm7cmzOA46ox/X1ARxf7Bjv8wQ/HsB5fxBg== + dependencies: + "@types/eslint-scope" "^3.7.3" + "@types/estree" "^1.0.0" + "@webassemblyjs/ast" "^1.11.5" + "@webassemblyjs/wasm-edit" "^1.11.5" + "@webassemblyjs/wasm-parser" "^1.11.5" + acorn "^8.7.1" + acorn-import-assertions "^1.7.6" + browserslist "^4.14.5" + chrome-trace-event "^1.0.2" + enhanced-resolve "^5.13.0" + es-module-lexer "^1.2.1" + eslint-scope "5.1.1" + events "^3.2.0" + glob-to-regexp "^0.4.1" + graceful-fs "^4.2.9" + json-parse-even-better-errors "^2.3.1" + loader-runner "^4.2.0" + mime-types "^2.1.27" + neo-async "^2.6.2" + schema-utils "^3.1.2" + tapable "^2.1.1" + terser-webpack-plugin "^5.3.7" + watchpack "^2.4.0" + webpack-sources "^3.2.3" + +which@^1.2.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" + integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== + dependencies: + isexe "^2.0.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +wildcard@^2.0.0: + version "2.0.1" + resolved "https://registry.yarnpkg.com/wildcard/-/wildcard-2.0.1.tgz#5ab10d02487198954836b6349f74fff961e10f67" + integrity sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ== + +workerpool@6.2.1: + version "6.2.1" + resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" + integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== + +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + +wrappy@1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f" + integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ== + +ws@~8.11.0: + version "8.11.0" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" + integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== + +y18n@^5.0.5: + version "5.0.8" + resolved "https://registry.yarnpkg.com/y18n/-/y18n-5.0.8.tgz#7f4934d0f7ca8c56f95314939ddcd2dd91ce1d55" + integrity sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA== + +yargs-parser@20.2.4: + version "20.2.4" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.4.tgz#b42890f14566796f85ae8e3a25290d205f154a54" + integrity sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA== + +yargs-parser@^20.2.2: + version "20.2.9" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee" + integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w== + +yargs-unparser@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/yargs-unparser/-/yargs-unparser-2.0.0.tgz#f131f9226911ae5d9ad38c432fe809366c2325eb" + integrity sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA== + dependencies: + camelcase "^6.0.0" + decamelize "^4.0.0" + flat "^5.0.2" + is-plain-obj "^2.1.0" + +yargs@16.2.0, yargs@^16.1.1: + version "16.2.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66" + integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw== + dependencies: + cliui "^7.0.2" + escalade "^3.1.1" + get-caller-file "^2.0.5" + require-directory "^2.1.1" + string-width "^4.2.0" + y18n "^5.0.5" + yargs-parser "^20.2.2" + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== diff --git a/settings.gradle.kts b/settings.gradle.kts index fa043c955..ad8030dca 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -24,7 +24,10 @@ pluginManagement { } dependencyResolutionManagement { - repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) + // https://youtrack.jetbrains.com/issue/KT-55620 + // Issue: Could not determine the dependencies of task ':kotlinNodeJsSetup'. + // Fix target versions: Kotlin 2.0.0-Beta1 +// repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) repositories { google() mavenCentral() From 429dabc14b722f95a117f51ab40799881f52e6a2 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Mon, 12 Feb 2024 10:47:10 -0800 Subject: [PATCH 07/35] Add convention for Multiplatform Android --- .../KmpAndroidLibraryConventionPlugin.kt | 27 +++++++++++++++++++ .../main/kotlin/KmpLibraryConventionPlugin.kt | 4 --- .../apps/nowinandroid/KotlinMultiplatform.kt | 11 ++++++++ 3 files changed, 38 insertions(+), 4 deletions(-) create mode 100644 build-logic/convention/src/main/kotlin/KmpAndroidLibraryConventionPlugin.kt diff --git a/build-logic/convention/src/main/kotlin/KmpAndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KmpAndroidLibraryConventionPlugin.kt new file mode 100644 index 000000000..e35d844ef --- /dev/null +++ b/build-logic/convention/src/main/kotlin/KmpAndroidLibraryConventionPlugin.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.google.samples.apps.nowinandroid.configureAndroidKotlinMultiplatform +import org.gradle.api.Plugin +import org.gradle.api.Project +class KmpAndroidLibraryConventionPlugin: Plugin { + override fun apply(target: Project) { + with(target) { + plugins.apply("org.jetbrains.kotlin.multiplatform") + configureAndroidKotlinMultiplatform() + } + } +} diff --git a/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt index ed9a20f47..d2a73858c 100644 --- a/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt @@ -18,10 +18,6 @@ import com.google.samples.apps.nowinandroid.configureKotlinMultiplatform import org.gradle.api.Plugin import org.gradle.api.Project -/** - * A plugin that applies the Kotlin Multiplatform plugin and configures it for the project. - * https://github.com/cashapp/sqldelight/blob/master/buildLogic/multiplatform-convention/src/main/kotlin/app/cash/sqldelight/multiplatform/MultiplatformConventions.kt - */ class KmpLibraryConventionPlugin: Plugin { override fun apply(target: Project) { with(target) { diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt index 80ac2dbdd..355e705b5 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt @@ -21,6 +21,10 @@ import org.gradle.kotlin.dsl.configure import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.konan.target.HostManager +/** + * A plugin that applies the Kotlin Multiplatform plugin and configures it for the project. + * https://github.com/cashapp/sqldelight/blob/master/buildLogic/multiplatform-convention/src/main/kotlin/app/cash/sqldelight/multiplatform/MultiplatformConventions.kt + */ internal fun Project.configureKotlinMultiplatform() { extensions.configure { jvm() @@ -79,3 +83,10 @@ internal fun Project.configureKotlinMultiplatform() { } } } + +internal fun Project.configureAndroidKotlinMultiplatform() { + extensions.configure { + androidTarget() + } + configureKotlinMultiplatform() +} From bdf201762efae3c5bb520035e53b031179cbd34c Mon Sep 17 00:00:00 2001 From: lihenggui Date: Mon, 12 Feb 2024 11:26:46 -0800 Subject: [PATCH 08/35] Add sqldelight support and move source to the commonMain folder --- build-logic/convention/build.gradle.kts | 4 ++ .../KmpAndroidLibraryConventionPlugin.kt | 27 ------------ .../main/kotlin/KmpLibraryConventionPlugin.kt | 15 +++++++ .../main/kotlin/SqlDelightConventionPlugin.kt | 39 +++++++++++++++++ .../apps/nowinandroid/KotlinMultiplatform.kt | 8 +--- build.gradle.kts | 1 + core/database/build.gradle.kts | 43 ++++++++++++++----- .../{main => commonMain}/AndroidManifest.xml | 0 .../nowinandroid/core/database/DaosModule.kt | 0 .../core/database/DatabaseMigrations.kt | 0 .../core/database/DatabaseModule.kt | 0 .../nowinandroid/core/database/NiaDatabase.kt | 0 .../core/database/dao/NewsResourceDao.kt | 0 .../core/database/dao/NewsResourceFtsDao.kt | 0 .../core/database/dao/RecentSearchQueryDao.kt | 0 .../core/database/dao/TopicDao.kt | 0 .../core/database/dao/TopicFtsDao.kt | 0 .../core/database/model/NewsResourceEntity.kt | 0 .../database/model/NewsResourceFtsEntity.kt | 0 .../model/NewsResourceTopicCrossRef.kt | 0 .../database/model/PopulatedNewsResource.kt | 0 .../database/model/RecentSearchQueryEntity.kt | 0 .../core/database/model/TopicEntity.kt | 0 .../core/database/model/TopicFtsEntity.kt | 0 .../core/database/util/InstantConverter.kt | 0 core/model/build.gradle.kts | 4 ++ gradle/libs.versions.toml | 5 +++ 27 files changed, 101 insertions(+), 45 deletions(-) delete mode 100644 build-logic/convention/src/main/kotlin/KmpAndroidLibraryConventionPlugin.kt create mode 100644 build-logic/convention/src/main/kotlin/SqlDelightConventionPlugin.kt rename core/database/src/{main => commonMain}/AndroidManifest.xml (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/RecentSearchQueryDao.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicDao.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicFtsDao.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceFtsEntity.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceTopicCrossRef.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/model/RecentSearchQueryEntity.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicEntity.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicFtsEntity.kt (100%) rename core/database/src/{main => commonMain}/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt (100%) diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index 2c84a8003..308846d13 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -114,5 +114,9 @@ gradlePlugin { id = "nowinandroid.kmp.library" implementationClass = "KmpLibraryConventionPlugin" } + register("kmpAndroidLibrary") { + id = "nowinandroid.kmp.android.library" + implementationClass = "KmpAndroidLibraryConventionPlugin" + } } } diff --git a/build-logic/convention/src/main/kotlin/KmpAndroidLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KmpAndroidLibraryConventionPlugin.kt deleted file mode 100644 index e35d844ef..000000000 --- a/build-logic/convention/src/main/kotlin/KmpAndroidLibraryConventionPlugin.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -import com.google.samples.apps.nowinandroid.configureAndroidKotlinMultiplatform -import org.gradle.api.Plugin -import org.gradle.api.Project -class KmpAndroidLibraryConventionPlugin: Plugin { - override fun apply(target: Project) { - with(target) { - plugins.apply("org.jetbrains.kotlin.multiplatform") - configureAndroidKotlinMultiplatform() - } - } -} diff --git a/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt index d2a73858c..7710ff73a 100644 --- a/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt +++ b/build-logic/convention/src/main/kotlin/KmpLibraryConventionPlugin.kt @@ -14,15 +14,30 @@ * limitations under the License. */ +import com.android.build.gradle.LibraryExtension +import com.google.samples.apps.nowinandroid.configureFlavors +import com.google.samples.apps.nowinandroid.configureGradleManagedDevices +import com.google.samples.apps.nowinandroid.configureKotlinAndroid import com.google.samples.apps.nowinandroid.configureKotlinMultiplatform import org.gradle.api.Plugin import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure class KmpLibraryConventionPlugin: Plugin { override fun apply(target: Project) { with(target) { + plugins.apply("com.android.library") plugins.apply("org.jetbrains.kotlin.multiplatform") configureKotlinMultiplatform() + extensions.configure { + configureKotlinAndroid(this) + defaultConfig.targetSdk = 34 + configureFlavors(this) + configureGradleManagedDevices(this) + // The resource prefix is derived from the module name, + // so resources inside ":core:module1" must be prefixed with "core_module1_" + resourcePrefix = path.split("""\W""".toRegex()).drop(1).distinct().joinToString(separator = "_").lowercase() + "_" + } } } } diff --git a/build-logic/convention/src/main/kotlin/SqlDelightConventionPlugin.kt b/build-logic/convention/src/main/kotlin/SqlDelightConventionPlugin.kt new file mode 100644 index 000000000..5d2c1b888 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/SqlDelightConventionPlugin.kt @@ -0,0 +1,39 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.android.build.api.variant.LibraryAndroidComponentsExtension +import com.android.build.gradle.LibraryExtension +import com.google.samples.apps.nowinandroid.configureFlavors +import com.google.samples.apps.nowinandroid.configureGradleManagedDevices +import com.google.samples.apps.nowinandroid.configureKotlinAndroid +import com.google.samples.apps.nowinandroid.configurePrintApksTask +import com.google.samples.apps.nowinandroid.disableUnnecessaryAndroidTests +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.dependencies +import org.gradle.kotlin.dsl.kotlin +import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension + +class SqlDelightConventionPlugin: Plugin { + override fun apply(target: Project) { + with(target) { + pluginManager.apply("app.cash.sqldelight") + extensions.configure { + } + } + } +} \ No newline at end of file diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt index 355e705b5..2fc1957d9 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt @@ -28,6 +28,7 @@ import org.jetbrains.kotlin.konan.target.HostManager internal fun Project.configureKotlinMultiplatform() { extensions.configure { jvm() + androidTarget() js { browser { @@ -83,10 +84,3 @@ internal fun Project.configureKotlinMultiplatform() { } } } - -internal fun Project.configureAndroidKotlinMultiplatform() { - extensions.configure { - androidTarget() - } - configureKotlinMultiplatform() -} diff --git a/build.gradle.kts b/build.gradle.kts index 1f329a53e..094a9eb3d 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -48,4 +48,5 @@ plugins { alias(libs.plugins.room) apply false alias(libs.plugins.jetbrainsCompose) apply false alias(libs.plugins.kotlinMultiplatform) apply false + alias(libs.plugins.sqldelight.gradle.plugin) apply false } diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 4a6bcb66a..c663cd270 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,10 +15,8 @@ */ plugins { - alias(libs.plugins.nowinandroid.android.library) - alias(libs.plugins.nowinandroid.android.library.jacoco) - alias(libs.plugins.nowinandroid.android.hilt) - alias(libs.plugins.nowinandroid.android.room) + alias(libs.plugins.nowinandroid.kmp.library) + alias(libs.plugins.sqldelight.gradle.plugin) } android { @@ -29,10 +27,33 @@ android { namespace = "com.google.samples.apps.nowinandroid.core.database" } -dependencies { - api(projects.core.model) - - implementation(libs.kotlinx.datetime) - - androidTestImplementation(projects.core.testing) +kotlin { + sourceSets { + val commonMain by getting { + dependencies { + api(projects.core.model) + implementation(libs.kotlinx.datetime) + } + } + val androidMain by getting { + dependencies { + implementation(libs.sqldelight.android.driver) + } + } + val nativeMain by getting { + dependencies { + implementation(libs.sqldelight.native.driver) + } + } + val jvmMain by getting { + dependencies { + implementation(libs.sqldelight.sqlite.driver) + } + } + val commonTest by getting { + dependencies { + implementation(libs.kotlin.test) + } + } + } } diff --git a/core/database/src/main/AndroidManifest.xml b/core/database/src/commonMain/AndroidManifest.xml similarity index 100% rename from core/database/src/main/AndroidManifest.xml rename to core/database/src/commonMain/AndroidManifest.xml diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/RecentSearchQueryDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/RecentSearchQueryDao.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/RecentSearchQueryDao.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/RecentSearchQueryDao.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicDao.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicDao.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicDao.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicFtsDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicFtsDao.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicFtsDao.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicFtsDao.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceFtsEntity.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceFtsEntity.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceFtsEntity.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceFtsEntity.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceTopicCrossRef.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceTopicCrossRef.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceTopicCrossRef.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceTopicCrossRef.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/RecentSearchQueryEntity.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/RecentSearchQueryEntity.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/RecentSearchQueryEntity.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/RecentSearchQueryEntity.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicEntity.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicEntity.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicEntity.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicEntity.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicFtsEntity.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicFtsEntity.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicFtsEntity.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicFtsEntity.kt diff --git a/core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt similarity index 100% rename from core/database/src/main/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt diff --git a/core/model/build.gradle.kts b/core/model/build.gradle.kts index d7844ecc1..0cba90bc7 100644 --- a/core/model/build.gradle.kts +++ b/core/model/build.gradle.kts @@ -18,6 +18,10 @@ plugins { alias(libs.plugins.nowinandroid.kmp.library) } +android { + namespace = "com.google.samples.apps.nowinandroid.core.model" +} + kotlin { sourceSets { commonMain.dependencies { diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 117fefa4c..816da6a51 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -70,6 +70,7 @@ androidx-test-junit = "1.1.5" compose = "1.6.0" compose-plugin = "1.6.0-alpha01" junit = "4.13.2" +sqldelight = "2.0.1" [libraries] accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanist" } @@ -156,6 +157,9 @@ androidx-material = { group = "com.google.android.material", name = "material", androidx-constraintlayout = { group = "androidx.constraintlayout", name = "constraintlayout", version.ref = "androidx-constraintlayout" } compose-ui-tooling = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" } compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" } +sqldelight-android-driver = { group = "app.cash.sqldelight", name = "android-driver", version.ref = "sqldelight" } +sqldelight-native-driver = { group = "app.cash.sqldelight", name = "native-driver", version.ref = "sqldelight" } +sqldelight-sqlite-driver = { group = "app.cash.sqldelight", name = "sqlite-driver", version.ref = "sqldelight" } # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } @@ -186,6 +190,7 @@ room = { id = "androidx.room", version.ref = "room" } secrets = { id = "com.google.android.libraries.mapsplatform.secrets-gradle-plugin", version.ref = "secrets" } jetbrainsCompose = { id = "org.jetbrains.compose", version.ref = "compose-plugin" } kotlinMultiplatform = { id = "org.jetbrains.kotlin.multiplatform", version.ref = "kotlin" } +sqldelight-gradle-plugin = { id = "app.cash.sqldelight", version.ref = "sqldelight" } # Plugins defined by this project nowinandroid-android-application = { id = "nowinandroid.android.application", version = "unspecified" } From 0712a7d26a76a723d3622449a7d45d54e3fe21d4 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Mon, 12 Feb 2024 13:32:35 -0800 Subject: [PATCH 09/35] Add SqlDelight drivers --- .../apps/nowinandroid/KotlinMultiplatform.kt | 26 +++++--- core/database/build.gradle.kts | 29 +++++++++ .../core/database/DriverFactory.kt} | 32 ++++------ .../nowinandroid/core/database/DaosModule.kt | 9 +-- .../core/database/DatabaseMigrations.kt | 63 ------------------- .../core/database/DriverFactory.kt | 27 ++++++++ .../nowinandroid/core/database/NiaDatabase.kt | 41 ------------ .../core/database/DriverFactory.kt | 35 +++++++++++ .../core/database/DriverFactory.kt | 31 +++++++++ .../core/database/DriverFactory.kt | 31 +++++++++ gradle/libs.versions.toml | 6 ++ 11 files changed, 190 insertions(+), 140 deletions(-) rename core/database/src/{commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt => androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt} (52%) delete mode 100644 core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt create mode 100644 core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt create mode 100644 core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt create mode 100644 core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt create mode 100644 core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt index 2fc1957d9..544f440a4 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt @@ -18,6 +18,7 @@ package com.google.samples.apps.nowinandroid import org.gradle.api.Project import org.gradle.kotlin.dsl.configure +import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension import org.jetbrains.kotlin.konan.target.HostManager @@ -27,6 +28,10 @@ import org.jetbrains.kotlin.konan.target.HostManager */ internal fun Project.configureKotlinMultiplatform() { extensions.configure { + // Enable native group by default + // https://kotlinlang.org/docs/whatsnew1820.html#new-approach-to-source-set-hierarchy + applyDefaultHierarchyTemplate() + jvm() androidTarget() @@ -52,18 +57,19 @@ internal fun Project.configureKotlinMultiplatform() { iosSimulatorArm64() iosX64() - // tier 2 - linuxArm64() - watchosSimulatorArm64() - watchosX64() - watchosArm32() - watchosArm64() - tvosSimulatorArm64() - tvosX64() - tvosArm64() +// Fix :core:database:linuxArm64Main: Could not resolve me.tatarka.inject:kotlin-inject-runtime:0.6.3. +// // tier 2 +// linuxArm64() +// watchosSimulatorArm64() +// watchosX64() +// watchosArm32() +// watchosArm64() +// tvosSimulatorArm64() +// tvosX64() +// tvosArm64() iosArm64() - // fix :core:model:androidNativeArm32Main: Could not resolve org.jetbrains.kotlinx:kotlinx-datetime +// fix :core:model:androidNativeArm32Main: Could not resolve org.jetbrains.kotlinx:kotlinx-datetime // // tier 3 // androidNativeArm32() // androidNativeArm64() diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index c663cd270..e0fb8a977 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -17,6 +17,7 @@ plugins { alias(libs.plugins.nowinandroid.kmp.library) alias(libs.plugins.sqldelight.gradle.plugin) + alias(libs.plugins.ksp) } android { @@ -33,6 +34,8 @@ kotlin { dependencies { api(projects.core.model) implementation(libs.kotlinx.datetime) + implementation(libs.kotlinInject.runtime) + implementation(libs.sqldelight.coroutines.extensions) } } val androidMain by getting { @@ -50,6 +53,14 @@ kotlin { implementation(libs.sqldelight.sqlite.driver) } } + val jsMain by getting { + dependencies { + implementation(libs.sqldelight.sqljs.driver) + implementation(libs.sqldelight.webworker.driver) + implementation(npm("sql.js", "1.6.2")) + implementation(devNpm("copy-webpack-plugin", "9.1.0")) + } + } val commonTest by getting { dependencies { implementation(libs.kotlin.test) @@ -57,3 +68,21 @@ kotlin { } } } + +sqldelight { + databases { + create("NiaDatabase") { + packageName.set("com.google.samples.apps.nowinandroid.core.database") + generateAsync.set(true) + } + } +} + +dependencies { + // KSP will eventually have better multiplatform support and we'll be able to simply have + // `ksp libs.kotlinInject.compiler` in the dependencies block of each source set + // https://github.com/google/ksp/pull/1021 + add("kspIosX64", libs.kotlinInject.compiler) + add("kspIosArm64", libs.kotlinInject.compiler) + add("kspIosSimulatorArm64", libs.kotlinInject.compiler) +} diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt b/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt similarity index 52% rename from core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt rename to core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt index c6e33f284..92b754383 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt +++ b/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,24 +17,16 @@ package com.google.samples.apps.nowinandroid.core.database import android.content.Context -import androidx.room.Room -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.android.qualifiers.ApplicationContext -import dagger.hilt.components.SingletonComponent -import javax.inject.Singleton +import app.cash.sqldelight.async.coroutines.synchronous +import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.db.SqlSchema +import app.cash.sqldelight.driver.android.AndroidSqliteDriver -@Module -@InstallIn(SingletonComponent::class) -internal object DatabaseModule { - @Provides - @Singleton - fun providesNiaDatabase( - @ApplicationContext context: Context, - ): NiaDatabase = Room.databaseBuilder( - context, - NiaDatabase::class.java, - "nia-database", - ).build() +actual class DriverFactory(private val context: Context) { + actual suspend fun provideDbDriver( + schema: SqlSchema>, + ): SqlDriver { + return AndroidSqliteDriver(schema.synchronous(), context, "nia-database.db") + } } diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt index afbbf42e6..b9c0a947a 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt @@ -21,13 +21,10 @@ import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceFtsDao import com.google.samples.apps.nowinandroid.core.database.dao.RecentSearchQueryDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao -import dagger.Module -import dagger.Provides -import dagger.hilt.InstallIn -import dagger.hilt.components.SingletonComponent +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides -@Module -@InstallIn(SingletonComponent::class) +@Component internal object DaosModule { @Provides fun providesTopicsDao( diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt deleted file mode 100644 index 4e396c9e2..000000000 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseMigrations.kt +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.core.database - -import androidx.room.DeleteColumn -import androidx.room.DeleteTable -import androidx.room.RenameColumn -import androidx.room.migration.AutoMigrationSpec - -/** - * Automatic schema migrations sometimes require extra instructions to perform the migration, for - * example, when a column is renamed. These extra instructions are placed here by creating a class - * using the following naming convention `SchemaXtoY` where X is the schema version you're migrating - * from and Y is the schema version you're migrating to. The class should implement - * `AutoMigrationSpec`. - */ -internal object DatabaseMigrations { - - @RenameColumn( - tableName = "topics", - fromColumnName = "description", - toColumnName = "shortDescription", - ) - class Schema2to3 : AutoMigrationSpec - - @DeleteColumn( - tableName = "news_resources", - columnName = "episode_id", - ) - @DeleteTable.Entries( - DeleteTable( - tableName = "episodes_authors", - ), - DeleteTable( - tableName = "episodes", - ), - ) - class Schema10to11 : AutoMigrationSpec - - @DeleteTable.Entries( - DeleteTable( - tableName = "news_resources_authors", - ), - DeleteTable( - tableName = "authors", - ), - ) - class Schema11to12 : AutoMigrationSpec -} diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt new file mode 100644 index 000000000..6d6166b52 --- /dev/null +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt @@ -0,0 +1,27 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.database + +import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.db.SqlSchema + +expect class DriverFactory { + suspend fun provideDbDriver( + schema: SqlSchema>, + ): SqlDriver +} diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt index 87fd82af1..7979ffbbf 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt @@ -16,53 +16,12 @@ package com.google.samples.apps.nowinandroid.core.database -import androidx.room.AutoMigration -import androidx.room.Database -import androidx.room.RoomDatabase -import androidx.room.TypeConverters import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceFtsDao import com.google.samples.apps.nowinandroid.core.database.dao.RecentSearchQueryDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao -import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity -import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceFtsEntity -import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef -import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity -import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity -import com.google.samples.apps.nowinandroid.core.database.model.TopicFtsEntity -import com.google.samples.apps.nowinandroid.core.database.util.InstantConverter -@Database( - entities = [ - NewsResourceEntity::class, - NewsResourceTopicCrossRef::class, - NewsResourceFtsEntity::class, - TopicEntity::class, - TopicFtsEntity::class, - RecentSearchQueryEntity::class, - ], - version = 14, - autoMigrations = [ - AutoMigration(from = 1, to = 2), - AutoMigration(from = 2, to = 3, spec = DatabaseMigrations.Schema2to3::class), - AutoMigration(from = 3, to = 4), - AutoMigration(from = 4, to = 5), - AutoMigration(from = 5, to = 6), - AutoMigration(from = 6, to = 7), - AutoMigration(from = 7, to = 8), - AutoMigration(from = 8, to = 9), - AutoMigration(from = 9, to = 10), - AutoMigration(from = 10, to = 11, spec = DatabaseMigrations.Schema10to11::class), - AutoMigration(from = 11, to = 12, spec = DatabaseMigrations.Schema11to12::class), - AutoMigration(from = 12, to = 13), - AutoMigration(from = 13, to = 14), - ], - exportSchema = true, -) -@TypeConverters( - InstantConverter::class, -) internal abstract class NiaDatabase : RoomDatabase() { abstract fun topicDao(): TopicDao abstract fun newsResourceDao(): NewsResourceDao diff --git a/core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt b/core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt new file mode 100644 index 000000000..12e025f06 --- /dev/null +++ b/core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt @@ -0,0 +1,35 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.database + +import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.db.SqlSchema +import app.cash.sqldelight.driver.worker.WebWorkerDriver +import org.w3c.dom.Worker + +actual class DriverFactory { + actual suspend fun provideDbDriver( + schema: SqlSchema>, + ): SqlDriver { + return WebWorkerDriver( + Worker( + js("""new URL("@cashapp/sqldelight-sqljs-worker/sqljs.worker.js", import.meta.url)"""), + ), + ).also { schema.create(it).await() } + } +} diff --git a/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt b/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt new file mode 100644 index 000000000..a5be2e3c4 --- /dev/null +++ b/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.database + +import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.db.SqlSchema +import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver + +actual class DriverFactory { + actual suspend fun provideDbDriver( + schema: SqlSchema>, + ): SqlDriver { + return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) + .also { schema.create(it).await() } + } +} diff --git a/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt b/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt new file mode 100644 index 000000000..ffe918cbc --- /dev/null +++ b/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.database + +import app.cash.sqldelight.async.coroutines.synchronous +import app.cash.sqldelight.db.QueryResult +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.db.SqlSchema +import app.cash.sqldelight.driver.native.NativeSqliteDriver + +actual class DriverFactory { + actual suspend fun provideDbDriver( + schema: SqlSchema>, + ): SqlDriver { + return NativeSqliteDriver(schema.synchronous(), "nia-database.db") + } +} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 816da6a51..d2f2ceed7 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -71,6 +71,7 @@ compose = "1.6.0" compose-plugin = "1.6.0-alpha01" junit = "4.13.2" sqldelight = "2.0.1" +kotlinInject = '0.6.3' [libraries] accompanist-permissions = { group = "com.google.accompanist", name = "accompanist-permissions", version.ref = "accompanist" } @@ -160,6 +161,11 @@ compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" sqldelight-android-driver = { group = "app.cash.sqldelight", name = "android-driver", version.ref = "sqldelight" } sqldelight-native-driver = { group = "app.cash.sqldelight", name = "native-driver", version.ref = "sqldelight" } sqldelight-sqlite-driver = { group = "app.cash.sqldelight", name = "sqlite-driver", version.ref = "sqldelight" } +sqldelight-sqljs-driver = { group = "app.cash.sqldelight", name = "sqljs-driver", version.ref = "sqldelight" } +sqldelight-webworker-driver = { group = "app.cash.sqldelight", name = "web-worker-driver", version.ref = "sqldelight" } +sqldelight-coroutines-extensions = { group = "app.cash.sqldelight", name = "coroutines-extensions", version.ref = "sqldelight" } +kotlinInject-compiler = { module = 'me.tatarka.inject:kotlin-inject-compiler-ksp', version.ref = 'kotlinInject' } +kotlinInject-runtime = { module = 'me.tatarka.inject:kotlin-inject-runtime', version.ref = 'kotlinInject' } # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } From 1d4792f16c88655970c76dd3ee149b4cddef9fa0 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Mon, 12 Feb 2024 15:31:16 -0800 Subject: [PATCH 10/35] WIP: Migrate table to SqlDelight --- core/database/build.gradle.kts | 1 + .../core/database/NewsResourceEntity.sq | 68 +++++++++++++++++++ gradle/libs.versions.toml | 1 + 3 files changed, 70 insertions(+) create mode 100644 core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceEntity.sq diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index e0fb8a977..837e471f5 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -36,6 +36,7 @@ kotlin { implementation(libs.kotlinx.datetime) implementation(libs.kotlinInject.runtime) implementation(libs.sqldelight.coroutines.extensions) + implementation(libs.sqldelight.primitive.adapters) } } val androidMain by getting { diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceEntity.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceEntity.sq new file mode 100644 index 000000000..4676cbccf --- /dev/null +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceEntity.sq @@ -0,0 +1,68 @@ +CREATE TABLE news_resources ( + id TEXT NOT NULL PRIMARY KEY, + title TEXT NOT NULL, + content TEXT NOT NULL, + url TEXT NOT NULL, + header_image_url TEXT, + publish_date INTEGER NOT NULL, + type TEXT NOT NULL +); + +getNewsResources: +SELECT * FROM news_resources +WHERE + CASE WHEN :useFilterNewsIds + THEN id IN (:filterNewsIds) + ELSE 1 + END + AND + CASE WHEN :useFilterTopicIds + THEN id IN + ( + SELECT news_resource_id FROM news_resources_topics + WHERE topic_id IN (:filterTopicIds) + ) + ELSE 1 + END +ORDER BY publish_date DESC; + +getNewsResourceIds: +SELECT id FROM news_resources +WHERE + CASE WHEN :useFilterNewsIds + THEN id IN (:filterNewsIds) + ELSE 1 + END + AND + CASE WHEN :useFilterTopicIds + THEN id IN + ( + SELECT news_resource_id FROM news_resources_topics + WHERE topic_id IN (:filterTopicIds) + ) + ELSE 1 + END +ORDER BY publish_date DESC; + +insertOrIgnoreNewsResources: +INSERT OR IGNORE INTO news_resources (id, title, content, url, header_image_url, publish_date, type) +VALUES (?, ?, ?, ?, ?, ?, ?); + +upsertNewsResources: +INSERT INTO news_resources (id, title, content, url, header_image_url, publish_date, type) +VALUES (?, ?, ?, ?, ?, ?, ?) +ON CONFLICT(id) DO UPDATE SET +title = excluded.title, +content = excluded.content, +url = excluded.url, +header_image_url = excluded.header_image_url, +publish_date = excluded.publish_date, +type = excluded.type; + +insertOrIgnoreTopicCrossRefEntities: +INSERT OR IGNORE INTO news_resources_topics (news_resource_id, topic_id) +VALUES (?, ?); + +deleteNewsResources: +DELETE FROM news_resources +WHERE id IN (:ids); diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index d2f2ceed7..e8fc00767 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -164,6 +164,7 @@ sqldelight-sqlite-driver = { group = "app.cash.sqldelight", name = "sqlite-drive sqldelight-sqljs-driver = { group = "app.cash.sqldelight", name = "sqljs-driver", version.ref = "sqldelight" } sqldelight-webworker-driver = { group = "app.cash.sqldelight", name = "web-worker-driver", version.ref = "sqldelight" } sqldelight-coroutines-extensions = { group = "app.cash.sqldelight", name = "coroutines-extensions", version.ref = "sqldelight" } +sqldelight-primitive-adapters = { group = "app.cash.sqldelight", name = "primitive-adapters", version.ref = "sqldelight" } kotlinInject-compiler = { module = 'me.tatarka.inject:kotlin-inject-compiler-ksp', version.ref = 'kotlinInject' } kotlinInject-runtime = { module = 'me.tatarka.inject:kotlin-inject-runtime', version.ref = 'kotlinInject' } From 704dd5bab6652c66daa95fba530bdbbd0f0e530e Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 13 Feb 2024 14:11:54 -0800 Subject: [PATCH 11/35] Add NewsResourceFtsEntity.sq --- .../core/database/NewsResourceFtsEntity.sq | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFtsEntity.sq diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFtsEntity.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFtsEntity.sq new file mode 100644 index 000000000..9f39a5128 --- /dev/null +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFtsEntity.sq @@ -0,0 +1,19 @@ +CREATE VIRTUAL TABLE news_resources_fts +USING FTS4( + tokenizer = porter, + content = news_resources, + news_resource_id TEXT NOT NULL, + title TEXT NOT NULL, + content TEXT NOT NULL, +); +-- Triggers to keep the FTS index up to date. +CREATE TRIGGER news_resources_ai AFTER INSERT ON news_resources BEGIN + INSERT INTO news_resources_fts (rowid, news_resource_id, title, content) VALUES (new.rowid, new.id, new.title, new.content); +END; +CREATE TRIGGER news_resources_ad AFTER DELETE ON news_resources BEGIN + INSERT INTO news_resources_fts (news_resources_fts, rowid, news_resource_id, title, content) VALUES ('delete', old.rowid, old.id, old.title, old.content); +END; +CREATE TRIGGER news_resources_au AFTER UPDATE ON news_resources BEGIN + INSERT INTO news_resources_fts (news_resources_fts, rowid, news_resource_id, title, content) VALUES ('delete', old.rowid, old.id, old.title, old.content); + INSERT INTO news_resources_fts (rowid, news_resource_id, title, content) VALUES (new.rowid, new.id, new.title, new.content); +END; From 68ca44707615bc15d9105b6cf6376d063a96ba6a Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 13 Feb 2024 14:17:51 -0800 Subject: [PATCH 12/35] Add NewsResourceTopicCrossRef.sq --- .../core/database/NewsResourceTopicCrossRef.sq | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopicCrossRef.sq diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopicCrossRef.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopicCrossRef.sq new file mode 100644 index 000000000..c76510666 --- /dev/null +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopicCrossRef.sq @@ -0,0 +1,10 @@ +CREATE TABLE news_resources_topics ( + news_resource_id TEXT NOT NULL, + topic_id TEXT NOT NULL, + PRIMARY KEY (news_resource_id, topic_id), + FOREIGN KEY (news_resource_id) REFERENCES news_resources(id) ON DELETE CASCADE, + FOREIGN KEY (topic_id) REFERENCES TopicEntity(id) ON DELETE CASCADE +); + +CREATE INDEX idx_news_resource_id ON news_resources_topics(news_resource_id); +CREATE INDEX idx_topic_id ON news_resources_topics(topic_id); From 2434f16495737c692c02296f1f61bc0aeb1030a9 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 13 Feb 2024 14:23:14 -0800 Subject: [PATCH 13/35] Add Topics and TopicsFts --- .../database/NewsResourceTopicCrossRef.sq | 2 +- .../apps/nowinandroid/core/database/Topics.sq | 8 ++++++ .../nowinandroid/core/database/TopicsFts.sq | 27 +++++++++++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) create mode 100644 core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq create mode 100644 core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopicCrossRef.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopicCrossRef.sq index c76510666..dcaa050f8 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopicCrossRef.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopicCrossRef.sq @@ -3,7 +3,7 @@ CREATE TABLE news_resources_topics ( topic_id TEXT NOT NULL, PRIMARY KEY (news_resource_id, topic_id), FOREIGN KEY (news_resource_id) REFERENCES news_resources(id) ON DELETE CASCADE, - FOREIGN KEY (topic_id) REFERENCES TopicEntity(id) ON DELETE CASCADE + FOREIGN KEY (topic_id) REFERENCES topics(id) ON DELETE CASCADE ); CREATE INDEX idx_news_resource_id ON news_resources_topics(news_resource_id); diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq new file mode 100644 index 000000000..e746808d2 --- /dev/null +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq @@ -0,0 +1,8 @@ +CREATE TABLE topics ( + id TEXT PRIMARY KEY NOT NULL, + name TEXT NOT NULL, + short_description TEXT NOT NULL, + long_description TEXT NOT NULL DEFAULT '', + url TEXT NOT NULL DEFAULT '', + image_url TEXT NOT NULL DEFAULT '' +); diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq new file mode 100644 index 000000000..8aebfa569 --- /dev/null +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq @@ -0,0 +1,27 @@ +CREATE VIRTUAL TABLE IF NOT EXISTS topics_fts +USING FTS4( + topic_id TEXT, + name TEXT, + short_description TEXT, + long_description TEXT +); + +CREATE TRIGGER IF NOT EXISTS topics_ai AFTER INSERT ON topics +BEGIN + INSERT INTO topics_fts (topic_id, name, short_description, long_description) + VALUES (new.id, new.name, new.short_description, new.long_description); +END; + +CREATE TRIGGER IF NOT EXISTS topics_ad AFTER DELETE ON topics +BEGIN + DELETE FROM topics_fts WHERE topic_id = old.id; +END; + +CREATE TRIGGER IF NOT EXISTS topics_au AFTER UPDATE ON topics +BEGIN + UPDATE topics_fts SET + name = new.name, + short_description = new.short_description, + long_description = new.long_description + WHERE topic_id = new.id; +END; From 68ea9a6a606e14d86d5fca3c34ea966d318c88bd Mon Sep 17 00:00:00 2001 From: lihenggui Date: Tue, 13 Feb 2024 15:42:31 -0800 Subject: [PATCH 14/35] Rename table file name --- core/database/build.gradle.kts | 1 + .../core/database/{NewsResourceEntity.sq => NewsResources.sq} | 0 .../database/{NewsResourceFtsEntity.sq => NewsResourcesFts.sq} | 0 ...ewsResourceTopicCrossRef.sq => NewsResourcesTopicCrossRef.sq} | 0 4 files changed, 1 insertion(+) rename core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/{NewsResourceEntity.sq => NewsResources.sq} (100%) rename core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/{NewsResourceFtsEntity.sq => NewsResourcesFts.sq} (100%) rename core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/{NewsResourceTopicCrossRef.sq => NewsResourcesTopicCrossRef.sq} (100%) diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 837e471f5..c8739b3b5 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -75,6 +75,7 @@ sqldelight { create("NiaDatabase") { packageName.set("com.google.samples.apps.nowinandroid.core.database") generateAsync.set(true) + dialect("sqlite:3.25") } } } diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceEntity.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResources.sq similarity index 100% rename from core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceEntity.sq rename to core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResources.sq diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFtsEntity.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesFts.sq similarity index 100% rename from core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFtsEntity.sq rename to core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesFts.sq diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopicCrossRef.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesTopicCrossRef.sq similarity index 100% rename from core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopicCrossRef.sq rename to core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesTopicCrossRef.sq From 502f5646adc6117d6778d6f4c4eb462ab10960a3 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 14 Feb 2024 10:56:42 -0800 Subject: [PATCH 15/35] Add recent search query --- .../apps/nowinandroid/core/database/RecentSearchQuery.sq | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq new file mode 100644 index 000000000..9ad8dae8a --- /dev/null +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq @@ -0,0 +1,5 @@ +CREATE TABLE recent_search_queries ( + id INTEGER PRIMARY KEY NOT NULL , + query TEXT NOT NULL, + queried_date INTEGER DEFAULT CURRENT_TIMESTAMP +); From 69a24c7e20fc373d2bfc72c38a08aa1ca2f9eab4 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 14 Feb 2024 10:58:43 -0800 Subject: [PATCH 16/35] Add NewsResourcesTopics --- .../nowinandroid/core/database/NewsResourceTopics.sq | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopics.sq diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopics.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopics.sq new file mode 100644 index 000000000..55aaa6b7f --- /dev/null +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopics.sq @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS news_resources_topics ( + news_resource_id TEXT NOT NULL, + topic_id TEXT NOT NULL, + PRIMARY KEY(news_resource_id, topic_id), + FOREIGN KEY(news_resource_id) REFERENCES news_resources(id) ON DELETE CASCADE, + FOREIGN KEY(topic_id) REFERENCES topics(id) ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS index_news_resources_topics_news_resource_id ON news_resources_topics(news_resource_id); + +CREATE INDEX IF NOT EXISTS index_news_resources_topics_topic_id ON news_resources_topics(topic_id); From 9810692d68c1712a85eb7fe9ce4369d0bf771383 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 14 Feb 2024 10:58:54 -0800 Subject: [PATCH 17/35] Suppress errors in sq file --- core/database/build.gradle.kts | 1 - .../nowinandroid/core/database/NewsResources.sq | 16 ++++++++-------- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index c8739b3b5..837e471f5 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -75,7 +75,6 @@ sqldelight { create("NiaDatabase") { packageName.set("com.google.samples.apps.nowinandroid.core.database") generateAsync.set(true) - dialect("sqlite:3.25") } } } diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResources.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResources.sq index 4676cbccf..51d6a52d3 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResources.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResources.sq @@ -50,14 +50,14 @@ VALUES (?, ?, ?, ?, ?, ?, ?); upsertNewsResources: INSERT INTO news_resources (id, title, content, url, header_image_url, publish_date, type) -VALUES (?, ?, ?, ?, ?, ?, ?) -ON CONFLICT(id) DO UPDATE SET -title = excluded.title, -content = excluded.content, -url = excluded.url, -header_image_url = excluded.header_image_url, -publish_date = excluded.publish_date, -type = excluded.type; +VALUES (?, ?, ?, ?, ?, ?, ?); +-- ON CONFLICT(id) DO UPDATE SET +-- title = excluded.title, +-- content = excluded.content, +-- url = excluded.url, +-- header_image_url = excluded.header_image_url, +-- publish_date = excluded.publish_date, +-- type = excluded.type; insertOrIgnoreTopicCrossRefEntities: INSERT OR IGNORE INTO news_resources_topics (news_resource_id, topic_id) From 8fc89e89655a59385ac7c51fd4eb9d22499b3a67 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 14 Feb 2024 11:17:08 -0800 Subject: [PATCH 18/35] Add AUTOINCREMENT in id --- .../apps/nowinandroid/core/database/RecentSearchQuery.sq | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq index 9ad8dae8a..68583e814 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq @@ -1,5 +1,5 @@ CREATE TABLE recent_search_queries ( - id INTEGER PRIMARY KEY NOT NULL , + id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, query TEXT NOT NULL, queried_date INTEGER DEFAULT CURRENT_TIMESTAMP ); From bbc9a537dc2208ad488ca3d243f199dd6cbe23ce Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 14 Feb 2024 11:54:19 -0800 Subject: [PATCH 19/35] Correct sql scripts and use SQLite 3.38 dialect --- core/database/build.gradle.kts | 3 +-- .../nowinandroid/core/database/NewsResources.sq | 16 ++++++++-------- .../core/database/NewsResourcesTopicCrossRef.sq | 3 ++- gradle/libs.versions.toml | 1 - 4 files changed, 11 insertions(+), 12 deletions(-) diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 837e471f5..5a746f6db 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -56,9 +56,7 @@ kotlin { } val jsMain by getting { dependencies { - implementation(libs.sqldelight.sqljs.driver) implementation(libs.sqldelight.webworker.driver) - implementation(npm("sql.js", "1.6.2")) implementation(devNpm("copy-webpack-plugin", "9.1.0")) } } @@ -75,6 +73,7 @@ sqldelight { create("NiaDatabase") { packageName.set("com.google.samples.apps.nowinandroid.core.database") generateAsync.set(true) + dialect("app.cash.sqldelight:sqlite-3-38-dialect:2.0.1") } } } diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResources.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResources.sq index 51d6a52d3..4676cbccf 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResources.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResources.sq @@ -50,14 +50,14 @@ VALUES (?, ?, ?, ?, ?, ?, ?); upsertNewsResources: INSERT INTO news_resources (id, title, content, url, header_image_url, publish_date, type) -VALUES (?, ?, ?, ?, ?, ?, ?); --- ON CONFLICT(id) DO UPDATE SET --- title = excluded.title, --- content = excluded.content, --- url = excluded.url, --- header_image_url = excluded.header_image_url, --- publish_date = excluded.publish_date, --- type = excluded.type; +VALUES (?, ?, ?, ?, ?, ?, ?) +ON CONFLICT(id) DO UPDATE SET +title = excluded.title, +content = excluded.content, +url = excluded.url, +header_image_url = excluded.header_image_url, +publish_date = excluded.publish_date, +type = excluded.type; insertOrIgnoreTopicCrossRefEntities: INSERT OR IGNORE INTO news_resources_topics (news_resource_id, topic_id) diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesTopicCrossRef.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesTopicCrossRef.sq index dcaa050f8..432a87ede 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesTopicCrossRef.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesTopicCrossRef.sq @@ -1,4 +1,4 @@ -CREATE TABLE news_resources_topics ( +CREATE TABLE IF NOT EXISTS news_resources_topics ( news_resource_id TEXT NOT NULL, topic_id TEXT NOT NULL, PRIMARY KEY (news_resource_id, topic_id), @@ -7,4 +7,5 @@ CREATE TABLE news_resources_topics ( ); CREATE INDEX idx_news_resource_id ON news_resources_topics(news_resource_id); + CREATE INDEX idx_topic_id ON news_resources_topics(topic_id); diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index e8fc00767..7e6b1cc3d 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -161,7 +161,6 @@ compose-ui-tooling-preview = { module = "androidx.compose.ui:ui-tooling-preview" sqldelight-android-driver = { group = "app.cash.sqldelight", name = "android-driver", version.ref = "sqldelight" } sqldelight-native-driver = { group = "app.cash.sqldelight", name = "native-driver", version.ref = "sqldelight" } sqldelight-sqlite-driver = { group = "app.cash.sqldelight", name = "sqlite-driver", version.ref = "sqldelight" } -sqldelight-sqljs-driver = { group = "app.cash.sqldelight", name = "sqljs-driver", version.ref = "sqldelight" } sqldelight-webworker-driver = { group = "app.cash.sqldelight", name = "web-worker-driver", version.ref = "sqldelight" } sqldelight-coroutines-extensions = { group = "app.cash.sqldelight", name = "coroutines-extensions", version.ref = "sqldelight" } sqldelight-primitive-adapters = { group = "app.cash.sqldelight", name = "primitive-adapters", version.ref = "sqldelight" } From 9bf5033c05963e427a5f1728ad6784c3081b4697 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 14 Feb 2024 15:16:51 -0800 Subject: [PATCH 20/35] Add functions in the sq file --- .../core/database/NewsResourcesFts.sq | 9 +++++++ .../core/database/RecentSearchQuery.sq | 9 +++++++ .../apps/nowinandroid/core/database/Topics.sq | 26 +++++++++++++++++++ .../nowinandroid/core/database/TopicsFts.sq | 14 ++++++++++ 4 files changed, 58 insertions(+) diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesFts.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesFts.sq index 9f39a5128..dcadc3ea9 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesFts.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesFts.sq @@ -17,3 +17,12 @@ CREATE TRIGGER news_resources_au AFTER UPDATE ON news_resources BEGIN INSERT INTO news_resources_fts (news_resources_fts, rowid, news_resource_id, title, content) VALUES ('delete', old.rowid, old.id, old.title, old.content); INSERT INTO news_resources_fts (rowid, news_resource_id, title, content) VALUES (new.rowid, new.id, new.title, new.content); END; + +insertAll: +INSERT INTO news_resources_fts (news_resource_id, title, content) SELECT id, title, content FROM news_resources; + +searchAllNewsResources: +SELECT news_resource_id FROM news_resources_fts WHERE news_resources_fts MATCH :query; + +getCount: +SELECT count(*) FROM news_resources_fts; diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq index 68583e814..13d2310a4 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq @@ -3,3 +3,12 @@ CREATE TABLE recent_search_queries ( query TEXT NOT NULL, queried_date INTEGER DEFAULT CURRENT_TIMESTAMP ); + +getRecentSearchQueryEntities: +SELECT * FROM recent_search_queries ORDER BY queried_date DESC LIMIT :limit; + +insertOrReplaceRecentSearchQuery: +INSERT OR REPLACE INTO recent_search_queries (query) VALUES (:query); + +clearRecentSearchQueries: +DELETE FROM recent_search_queries; diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq index e746808d2..a1fe7c4c4 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq @@ -6,3 +6,29 @@ CREATE TABLE topics ( url TEXT NOT NULL DEFAULT '', image_url TEXT NOT NULL DEFAULT '' ); + +getTopicEntity: +SELECT * FROM topics WHERE id = :topicId; + +getTopicEntities: +SELECT * FROM topics; + +getOneOffTopicEntities: +SELECT * FROM topics; + +insertOrIgnoreTopics: +INSERT OR IGNORE INTO topics(id, name, short_description, long_description, url, image_url) +VALUES (?, ?, ?, ?, ?, ?); + +upsertTopics: +INSERT INTO topics(id, name, short_description, long_description, url, image_url) +VALUES (?, ?, ?, ?, ?, ?) +ON CONFLICT(id) DO UPDATE SET +name = excluded.name, +short_description = excluded.short_description, +long_description = excluded.long_description, +url = excluded.url, +image_url = excluded.image_url; + +deleteTopics: +DELETE FROM topics WHERE id IN (:ids); diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq index 8aebfa569..b1b0ef819 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq @@ -25,3 +25,17 @@ BEGIN long_description = new.long_description WHERE topic_id = new.id; END; + +insertAll: +INSERT INTO topics_fts (topic_id, name, short_description, long_description) +VALUES (?, ?, ?, ?) +ON CONFLICT(topic_id) DO UPDATE SET +name = excluded.name, +short_description = excluded.short_description, +long_description = excluded.long_description; + +searchAllTopics: +SELECT topic_id FROM topics_fts WHERE topics_fts MATCH :query; + +getCount: +SELECT count(*) FROM topics_fts; From 90bf87b2937d828cc7615e843fdcce4589865cfd Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 14 Feb 2024 15:27:11 -0800 Subject: [PATCH 21/35] Use singular form in the sq file --- .../{NewsResources.sq => NewsResource.sq} | 12 ++++---- .../core/database/NewsResourceFts.sq | 28 +++++++++++++++++++ .../core/database/NewsResourceTopic.sq | 11 ++++++++ .../database/NewsResourceTopicCrossRef.sq | 11 ++++++++ .../core/database/NewsResourceTopics.sq | 11 -------- .../core/database/NewsResourcesFts.sq | 28 ------------------- .../database/NewsResourcesTopicCrossRef.sq | 11 -------- .../core/database/RecentSearchQuery.sq | 8 +++--- .../apps/nowinandroid/core/database/Topics.sq | 14 +++++----- .../nowinandroid/core/database/TopicsFts.sq | 20 ++++++------- 10 files changed, 77 insertions(+), 77 deletions(-) rename core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/{NewsResources.sq => NewsResource.sq} (82%) create mode 100644 core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq create mode 100644 core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopic.sq create mode 100644 core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopicCrossRef.sq delete mode 100644 core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopics.sq delete mode 100644 core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesFts.sq delete mode 100644 core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesTopicCrossRef.sq diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResources.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResource.sq similarity index 82% rename from core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResources.sq rename to core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResource.sq index 4676cbccf..a3c897183 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResources.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResource.sq @@ -1,4 +1,4 @@ -CREATE TABLE news_resources ( +CREATE TABLE news_resource ( id TEXT NOT NULL PRIMARY KEY, title TEXT NOT NULL, content TEXT NOT NULL, @@ -9,7 +9,7 @@ CREATE TABLE news_resources ( ); getNewsResources: -SELECT * FROM news_resources +SELECT * FROM news_resource WHERE CASE WHEN :useFilterNewsIds THEN id IN (:filterNewsIds) @@ -27,7 +27,7 @@ WHERE ORDER BY publish_date DESC; getNewsResourceIds: -SELECT id FROM news_resources +SELECT id FROM news_resource WHERE CASE WHEN :useFilterNewsIds THEN id IN (:filterNewsIds) @@ -45,11 +45,11 @@ WHERE ORDER BY publish_date DESC; insertOrIgnoreNewsResources: -INSERT OR IGNORE INTO news_resources (id, title, content, url, header_image_url, publish_date, type) +INSERT OR IGNORE INTO news_resource (id, title, content, url, header_image_url, publish_date, type) VALUES (?, ?, ?, ?, ?, ?, ?); upsertNewsResources: -INSERT INTO news_resources (id, title, content, url, header_image_url, publish_date, type) +INSERT INTO news_resource (id, title, content, url, header_image_url, publish_date, type) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET title = excluded.title, @@ -64,5 +64,5 @@ INSERT OR IGNORE INTO news_resources_topics (news_resource_id, topic_id) VALUES (?, ?); deleteNewsResources: -DELETE FROM news_resources +DELETE FROM news_resource WHERE id IN (:ids); diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq new file mode 100644 index 000000000..04349f386 --- /dev/null +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq @@ -0,0 +1,28 @@ +CREATE VIRTUAL TABLE news_resource_fts +USING FTS4( + tokenizer = porter, + content = news_resources, + news_resource_id TEXT NOT NULL, + title TEXT NOT NULL, + content TEXT NOT NULL, +); +-- Triggers to keep the FTS index up to date. +CREATE TRIGGER news_resource_ai AFTER INSERT ON news_resource BEGIN + INSERT INTO news_resource_fts (rowid, news_resource_id, title, content) VALUES (new.rowid, new.id, new.title, new.content); +END; +CREATE TRIGGER news_resource_ad AFTER DELETE ON news_resource BEGIN + INSERT INTO news_resource_fts (news_resource_fts, rowid, news_resource_id, title, content) VALUES ('delete', old.rowid, old.id, old.title, old.content); +END; +CREATE TRIGGER news_resource_au AFTER UPDATE ON news_resource BEGIN + INSERT INTO news_resource_fts (news_resource_fts, rowid, news_resource_id, title, content) VALUES ('delete', old.rowid, old.id, old.title, old.content); + INSERT INTO news_resource_fts (rowid, news_resource_id, title, content) VALUES (new.rowid, new.id, new.title, new.content); +END; + +insertAll: +INSERT INTO news_resource_fts (news_resource_id, title, content) SELECT id, title, content FROM news_resource; + +searchAllNewsResources: +SELECT news_resource_id FROM news_resource_fts WHERE news_resource_fts MATCH :query; + +getCount: +SELECT count(*) FROM news_resource_fts; diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopic.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopic.sq new file mode 100644 index 000000000..3158ad403 --- /dev/null +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopic.sq @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS news_resource_topic ( + news_resource_id TEXT NOT NULL, + topic_id TEXT NOT NULL, + PRIMARY KEY(news_resource_id, topic_id), + FOREIGN KEY(news_resource_id) REFERENCES news_resource(id) ON DELETE CASCADE, + FOREIGN KEY(topic_id) REFERENCES topic(id) ON DELETE CASCADE +); + +CREATE INDEX IF NOT EXISTS index_news_resource_topic_news_resource_id ON news_resource_topic(news_resource_id); + +CREATE INDEX IF NOT EXISTS index_news_resource_topic_topic_id ON news_resource_topic(topic_id); diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopicCrossRef.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopicCrossRef.sq new file mode 100644 index 000000000..2100a8a3c --- /dev/null +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopicCrossRef.sq @@ -0,0 +1,11 @@ +CREATE TABLE IF NOT EXISTS news_resources_topics ( + news_resource_id TEXT NOT NULL, + topic_id TEXT NOT NULL, + PRIMARY KEY (news_resource_id, topic_id), + FOREIGN KEY (news_resource_id) REFERENCES news_resource(id) ON DELETE CASCADE, + FOREIGN KEY (topic_id) REFERENCES topic(id) ON DELETE CASCADE +); + +CREATE INDEX idx_news_resource_id ON news_resource_topic(news_resource_id); + +CREATE INDEX idx_topic_id ON news_resource_topic(topic_id); diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopics.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopics.sq deleted file mode 100644 index 55aaa6b7f..000000000 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceTopics.sq +++ /dev/null @@ -1,11 +0,0 @@ -CREATE TABLE IF NOT EXISTS news_resources_topics ( - news_resource_id TEXT NOT NULL, - topic_id TEXT NOT NULL, - PRIMARY KEY(news_resource_id, topic_id), - FOREIGN KEY(news_resource_id) REFERENCES news_resources(id) ON DELETE CASCADE, - FOREIGN KEY(topic_id) REFERENCES topics(id) ON DELETE CASCADE -); - -CREATE INDEX IF NOT EXISTS index_news_resources_topics_news_resource_id ON news_resources_topics(news_resource_id); - -CREATE INDEX IF NOT EXISTS index_news_resources_topics_topic_id ON news_resources_topics(topic_id); diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesFts.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesFts.sq deleted file mode 100644 index dcadc3ea9..000000000 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesFts.sq +++ /dev/null @@ -1,28 +0,0 @@ -CREATE VIRTUAL TABLE news_resources_fts -USING FTS4( - tokenizer = porter, - content = news_resources, - news_resource_id TEXT NOT NULL, - title TEXT NOT NULL, - content TEXT NOT NULL, -); --- Triggers to keep the FTS index up to date. -CREATE TRIGGER news_resources_ai AFTER INSERT ON news_resources BEGIN - INSERT INTO news_resources_fts (rowid, news_resource_id, title, content) VALUES (new.rowid, new.id, new.title, new.content); -END; -CREATE TRIGGER news_resources_ad AFTER DELETE ON news_resources BEGIN - INSERT INTO news_resources_fts (news_resources_fts, rowid, news_resource_id, title, content) VALUES ('delete', old.rowid, old.id, old.title, old.content); -END; -CREATE TRIGGER news_resources_au AFTER UPDATE ON news_resources BEGIN - INSERT INTO news_resources_fts (news_resources_fts, rowid, news_resource_id, title, content) VALUES ('delete', old.rowid, old.id, old.title, old.content); - INSERT INTO news_resources_fts (rowid, news_resource_id, title, content) VALUES (new.rowid, new.id, new.title, new.content); -END; - -insertAll: -INSERT INTO news_resources_fts (news_resource_id, title, content) SELECT id, title, content FROM news_resources; - -searchAllNewsResources: -SELECT news_resource_id FROM news_resources_fts WHERE news_resources_fts MATCH :query; - -getCount: -SELECT count(*) FROM news_resources_fts; diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesTopicCrossRef.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesTopicCrossRef.sq deleted file mode 100644 index 432a87ede..000000000 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourcesTopicCrossRef.sq +++ /dev/null @@ -1,11 +0,0 @@ -CREATE TABLE IF NOT EXISTS news_resources_topics ( - news_resource_id TEXT NOT NULL, - topic_id TEXT NOT NULL, - PRIMARY KEY (news_resource_id, topic_id), - FOREIGN KEY (news_resource_id) REFERENCES news_resources(id) ON DELETE CASCADE, - FOREIGN KEY (topic_id) REFERENCES topics(id) ON DELETE CASCADE -); - -CREATE INDEX idx_news_resource_id ON news_resources_topics(news_resource_id); - -CREATE INDEX idx_topic_id ON news_resources_topics(topic_id); diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq index 13d2310a4..d1dc9557e 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq @@ -1,14 +1,14 @@ -CREATE TABLE recent_search_queries ( +CREATE TABLE recent_search_query ( id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, query TEXT NOT NULL, queried_date INTEGER DEFAULT CURRENT_TIMESTAMP ); getRecentSearchQueryEntities: -SELECT * FROM recent_search_queries ORDER BY queried_date DESC LIMIT :limit; +SELECT * FROM recent_search_query ORDER BY queried_date DESC LIMIT :limit; insertOrReplaceRecentSearchQuery: -INSERT OR REPLACE INTO recent_search_queries (query) VALUES (:query); +INSERT OR REPLACE INTO recent_search_query (query) VALUES (:query); clearRecentSearchQueries: -DELETE FROM recent_search_queries; +DELETE FROM recent_search_query; diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq index a1fe7c4c4..7aa803a34 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq @@ -1,4 +1,4 @@ -CREATE TABLE topics ( +CREATE TABLE topic ( id TEXT PRIMARY KEY NOT NULL, name TEXT NOT NULL, short_description TEXT NOT NULL, @@ -8,20 +8,20 @@ CREATE TABLE topics ( ); getTopicEntity: -SELECT * FROM topics WHERE id = :topicId; +SELECT * FROM topic WHERE id = :topicId; getTopicEntities: -SELECT * FROM topics; +SELECT * FROM topic; getOneOffTopicEntities: -SELECT * FROM topics; +SELECT * FROM topic; insertOrIgnoreTopics: -INSERT OR IGNORE INTO topics(id, name, short_description, long_description, url, image_url) +INSERT OR IGNORE INTO topic(id, name, short_description, long_description, url, image_url) VALUES (?, ?, ?, ?, ?, ?); upsertTopics: -INSERT INTO topics(id, name, short_description, long_description, url, image_url) +INSERT INTO topic(id, name, short_description, long_description, url, image_url) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET name = excluded.name, @@ -31,4 +31,4 @@ url = excluded.url, image_url = excluded.image_url; deleteTopics: -DELETE FROM topics WHERE id IN (:ids); +DELETE FROM topic WHERE id IN (:ids); diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq index b1b0ef819..4315078b1 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq @@ -1,4 +1,4 @@ -CREATE VIRTUAL TABLE IF NOT EXISTS topics_fts +CREATE VIRTUAL TABLE IF NOT EXISTS topic_fts USING FTS4( topic_id TEXT, name TEXT, @@ -6,20 +6,20 @@ USING FTS4( long_description TEXT ); -CREATE TRIGGER IF NOT EXISTS topics_ai AFTER INSERT ON topics +CREATE TRIGGER IF NOT EXISTS topic_ai AFTER INSERT ON topic BEGIN - INSERT INTO topics_fts (topic_id, name, short_description, long_description) + INSERT INTO topic_fts (topic_id, name, short_description, long_description) VALUES (new.id, new.name, new.short_description, new.long_description); END; -CREATE TRIGGER IF NOT EXISTS topics_ad AFTER DELETE ON topics +CREATE TRIGGER IF NOT EXISTS topic_ad AFTER DELETE ON topic BEGIN - DELETE FROM topics_fts WHERE topic_id = old.id; + DELETE FROM topic_fts WHERE topic_id = old.id; END; -CREATE TRIGGER IF NOT EXISTS topics_au AFTER UPDATE ON topics +CREATE TRIGGER IF NOT EXISTS topic_au AFTER UPDATE ON topic BEGIN - UPDATE topics_fts SET + UPDATE topic_fts SET name = new.name, short_description = new.short_description, long_description = new.long_description @@ -27,7 +27,7 @@ BEGIN END; insertAll: -INSERT INTO topics_fts (topic_id, name, short_description, long_description) +INSERT INTO topic_fts (topic_id, name, short_description, long_description) VALUES (?, ?, ?, ?) ON CONFLICT(topic_id) DO UPDATE SET name = excluded.name, @@ -35,7 +35,7 @@ short_description = excluded.short_description, long_description = excluded.long_description; searchAllTopics: -SELECT topic_id FROM topics_fts WHERE topics_fts MATCH :query; +SELECT topic_id FROM topic_fts WHERE topic_fts MATCH :query; getCount: -SELECT count(*) FROM topics_fts; +SELECT count(*) FROM topic_fts; From d56f934e1212a8308c0e489e084485234ca484de Mon Sep 17 00:00:00 2001 From: lihenggui Date: Wed, 14 Feb 2024 17:14:15 -0800 Subject: [PATCH 22/35] WIP: Implement DAOs --- .../nowinandroid/core/database/NiaDatabase.kt | 31 ---- .../core/database/dao/NewsResourceDao.kt | 143 ++++++++++-------- .../core/database/dao/NewsResourceFtsDao.kt | 36 +++-- .../core/database/model/TopicEntity.kt | 10 -- .../core/database/NewsResource.sq | 14 +- .../core/database/NewsResourceFts.sq | 6 +- 6 files changed, 112 insertions(+), 128 deletions(-) delete mode 100644 core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt deleted file mode 100644 index 7979ffbbf..000000000 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/NiaDatabase.kt +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.google.samples.apps.nowinandroid.core.database - -import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao -import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceFtsDao -import com.google.samples.apps.nowinandroid.core.database.dao.RecentSearchQueryDao -import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao -import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao - -internal abstract class NiaDatabase : RoomDatabase() { - abstract fun topicDao(): TopicDao - abstract fun newsResourceDao(): NewsResourceDao - abstract fun topicFtsDao(): TopicFtsDao - abstract fun newsResourceFtsDao(): NewsResourceFtsDao - abstract fun recentSearchQueryDao(): RecentSearchQueryDao -} diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt index 0ad1e4f7d..a49e44ca3 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDao.kt @@ -16,111 +16,124 @@ package com.google.samples.apps.nowinandroid.core.database.dao -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import androidx.room.Transaction -import androidx.room.Upsert +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import com.google.samples.apps.nowinandroid.core.database.NiaDatabase import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef import com.google.samples.apps.nowinandroid.core.database.model.PopulatedNewsResource import com.google.samples.apps.nowinandroid.core.model.data.NewsResource +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow +import kotlinx.datetime.Instant /** * DAO for [NewsResource] and [NewsResourceEntity] access */ -@Dao -interface NewsResourceDao { +class NewsResourceDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) { + private val query = db.newsResourceQueries /** * Fetches news resources that match the query parameters */ - @Transaction - @Query( - value = """ - SELECT * FROM news_resources - WHERE - CASE WHEN :useFilterNewsIds - THEN id IN (:filterNewsIds) - ELSE 1 - END - AND - CASE WHEN :useFilterTopicIds - THEN id IN - ( - SELECT news_resource_id FROM news_resources_topics - WHERE topic_id IN (:filterTopicIds) - ) - ELSE 1 - END - ORDER BY publish_date DESC - """, - ) fun getNewsResources( useFilterTopicIds: Boolean = false, filterTopicIds: Set = emptySet(), useFilterNewsIds: Boolean = false, filterNewsIds: Set = emptySet(), - ): Flow> + ): Flow> { + return query.getNewsResources( + useFilterTopicIds = useFilterTopicIds, + filterTopicIds = filterTopicIds, + useFilterNewsIds = useFilterNewsIds, + filterNewsIds = filterNewsIds, + ) { id, title, content, url, headerImageUrl, publishDate, type -> + PopulatedNewsResource( + entity = NewsResourceEntity( + id = id, + title = title, + content = content, + url = url, + headerImageUrl = headerImageUrl, + publishDate = Instant.fromEpochMilliseconds(publishDate), + type = type, + ), + // TODO Dealing with NewsResources <-> Topics relationship + topics = emptyList(), + ) + } + .asFlow() + .mapToList(dispatcher) + } /** * Fetches ids of news resources that match the query parameters */ - @Transaction - @Query( - value = """ - SELECT id FROM news_resources - WHERE - CASE WHEN :useFilterNewsIds - THEN id IN (:filterNewsIds) - ELSE 1 - END - AND - CASE WHEN :useFilterTopicIds - THEN id IN - ( - SELECT news_resource_id FROM news_resources_topics - WHERE topic_id IN (:filterTopicIds) - ) - ELSE 1 - END - ORDER BY publish_date DESC - """, - ) fun getNewsResourceIds( useFilterTopicIds: Boolean = false, filterTopicIds: Set = emptySet(), useFilterNewsIds: Boolean = false, filterNewsIds: Set = emptySet(), - ): Flow> + ): Flow> { + return query.getNewsResourceIds( + useFilterTopicIds = useFilterTopicIds, + filterTopicIds = filterTopicIds, + useFilterNewsIds = useFilterNewsIds, + filterNewsIds = filterNewsIds, + ) + .asFlow() + .mapToList(dispatcher) + } /** * Inserts [entities] into the db if they don't exist, and ignores those that do */ - @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnoreNewsResources(entities: List): List + suspend fun insertOrIgnoreNewsResources(entities: List): List { + entities.forEach { + query.insertOrIgnoreNewsResource( + id = it.id, + title = it.title, + content = it.content, + url = it.url, + header_image_url = it.headerImageUrl, + publish_date = it.publishDate.toEpochMilliseconds(), + type = it.type, + ) + } + // TODO Return the inserted ids + return entities.mapNotNull { + it.id.toLongOrNull() + } + } /** * Inserts or updates [newsResourceEntities] in the db under the specified primary keys */ - @Upsert - suspend fun upsertNewsResources(newsResourceEntities: List) + suspend fun upsertNewsResources(newsResourceEntities: List) { + newsResourceEntities.forEach { + query.upsertNewsResource( + id = it.id, + title = it.title, + content = it.content, + url = it.url, + header_image_url = it.headerImageUrl, + publish_date = it.publishDate.toEpochMilliseconds(), + type = it.type, + ) + } + } - @Insert(onConflict = OnConflictStrategy.IGNORE) suspend fun insertOrIgnoreTopicCrossRefEntities( newsResourceTopicCrossReferences: List, - ) + ) { + // TODO Consider removing cross references +// query.insertOrIgnoreNewsResourceTopicCrossRefs(newsResourceTopicCrossReferences) + } /** * Deletes rows in the db matching the specified [ids] */ - @Query( - value = """ - DELETE FROM news_resources - WHERE id in (:ids) - """, - ) - suspend fun deleteNewsResources(ids: List) + suspend fun deleteNewsResources(ids: List) { + query.deleteNewsResources(ids) + } } diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt index 86cc5529e..0834446e3 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt @@ -16,24 +16,36 @@ package com.google.samples.apps.nowinandroid.core.database.dao -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import app.cash.sqldelight.coroutines.mapToOneNotNull +import com.google.samples.apps.nowinandroid.core.database.NiaDatabase import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceFtsEntity +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow /** * DAO for [NewsResourceFtsEntity] access. */ -@Dao -interface NewsResourceFtsDao { - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertAll(newsResources: List) +class NewsResourceFtsDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) { + private val query = db.newsResourceFtsQueries + suspend fun insertAll(newsResources: List) { + newsResources.forEach { + query.insert( + news_resource_id = it.newsResourceId, + title = it.title, + content = it.content, + ) + } + } - @Query("SELECT newsResourceId FROM newsResourcesFts WHERE newsResourcesFts MATCH :query") - fun searchAllNewsResources(query: String): Flow> + fun searchAllNewsResources(query: String): Flow> { + return query.searchAllNewsResources(query) + } - @Query("SELECT count(*) FROM newsResourcesFts") - fun getCount(): Flow + fun getCount(): Flow { + return query.getCount() + .asFlow() + .mapToOneNotNull(dispatcher) + } } diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicEntity.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicEntity.kt index 043d110cd..f18279987 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicEntity.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicEntity.kt @@ -16,28 +16,18 @@ package com.google.samples.apps.nowinandroid.core.database.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey import com.google.samples.apps.nowinandroid.core.model.data.Topic /** * Defines a topic a user may follow. * It has a many to many relationship with [NewsResourceEntity] */ -@Entity( - tableName = "topics", -) data class TopicEntity( - @PrimaryKey val id: String, val name: String, val shortDescription: String, - @ColumnInfo(defaultValue = "") val longDescription: String, - @ColumnInfo(defaultValue = "") val url: String, - @ColumnInfo(defaultValue = "") val imageUrl: String, ) diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResource.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResource.sq index a3c897183..85cef42cc 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResource.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResource.sq @@ -12,7 +12,7 @@ getNewsResources: SELECT * FROM news_resource WHERE CASE WHEN :useFilterNewsIds - THEN id IN (:filterNewsIds) + THEN id IN :filterNewsIds ELSE 1 END AND @@ -20,7 +20,7 @@ WHERE THEN id IN ( SELECT news_resource_id FROM news_resources_topics - WHERE topic_id IN (:filterTopicIds) + WHERE topic_id IN :filterTopicIds ) ELSE 1 END @@ -30,7 +30,7 @@ getNewsResourceIds: SELECT id FROM news_resource WHERE CASE WHEN :useFilterNewsIds - THEN id IN (:filterNewsIds) + THEN id IN :filterNewsIds ELSE 1 END AND @@ -38,17 +38,17 @@ WHERE THEN id IN ( SELECT news_resource_id FROM news_resources_topics - WHERE topic_id IN (:filterTopicIds) + WHERE topic_id IN :filterTopicIds ) ELSE 1 END ORDER BY publish_date DESC; -insertOrIgnoreNewsResources: +insertOrIgnoreNewsResource: INSERT OR IGNORE INTO news_resource (id, title, content, url, header_image_url, publish_date, type) VALUES (?, ?, ?, ?, ?, ?, ?); -upsertNewsResources: +upsertNewsResource: INSERT INTO news_resource (id, title, content, url, header_image_url, publish_date, type) VALUES (?, ?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET @@ -65,4 +65,4 @@ VALUES (?, ?); deleteNewsResources: DELETE FROM news_resource -WHERE id IN (:ids); +WHERE id IN :ids; diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq index 04349f386..f14fcb96c 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq @@ -18,11 +18,11 @@ CREATE TRIGGER news_resource_au AFTER UPDATE ON news_resource BEGIN INSERT INTO news_resource_fts (rowid, news_resource_id, title, content) VALUES (new.rowid, new.id, new.title, new.content); END; -insertAll: -INSERT INTO news_resource_fts (news_resource_id, title, content) SELECT id, title, content FROM news_resource; +insert: +INSERT INTO news_resource_fts (news_resource_id, title, content) VALUES (:news_resource_id, :title, :content); searchAllNewsResources: -SELECT news_resource_id FROM news_resource_fts WHERE news_resource_fts MATCH :query; +SELECT news_resource_id FROM news_resource_fts WHERE news_resource_fts.title MATCH :query; getCount: SELECT count(*) FROM news_resource_fts; From b758f6073d106103313e2521795bc826f1fec2e7 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 15 Feb 2024 10:15:11 -0800 Subject: [PATCH 23/35] Fix compilation errors in NewsResourceFtsDao --- .../core/database/dao/NewsResourceFtsDao.kt | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt index 0834446e3..a2764afa5 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt @@ -28,10 +28,10 @@ import kotlinx.coroutines.flow.Flow * DAO for [NewsResourceFtsEntity] access. */ class NewsResourceFtsDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) { - private val query = db.newsResourceFtsQueries + private val dbQuery = db.newsResourceFtsQueries suspend fun insertAll(newsResources: List) { newsResources.forEach { - query.insert( + dbQuery.insert( news_resource_id = it.newsResourceId, title = it.title, content = it.content, @@ -40,11 +40,13 @@ class NewsResourceFtsDao(db: NiaDatabase, private val dispatcher: CoroutineDispa } fun searchAllNewsResources(query: String): Flow> { - return query.searchAllNewsResources(query) + return dbQuery.searchAllNewsResources(query) + .asFlow() + .mapToList(dispatcher) } fun getCount(): Flow { - return query.getCount() + return dbQuery.getCount() .asFlow() .mapToOneNotNull(dispatcher) } From 147baf204373f82ce3f2981f1ccee229b26fb2c5 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 15 Feb 2024 10:22:40 -0800 Subject: [PATCH 24/35] Implement RecentSearchQueryDao --- .../core/database/dao/RecentSearchQueryDao.kt | 42 ++++++++++++++----- .../core/database/RecentSearchQuery.sq | 5 +-- 2 files changed, 33 insertions(+), 14 deletions(-) diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/RecentSearchQueryDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/RecentSearchQueryDao.kt index 826575828..6fa361e09 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/RecentSearchQueryDao.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/RecentSearchQueryDao.kt @@ -16,23 +16,43 @@ package com.google.samples.apps.nowinandroid.core.database.dao -import androidx.room.Dao -import androidx.room.Query -import androidx.room.Upsert +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import com.google.samples.apps.nowinandroid.core.database.NiaDatabase +import com.google.samples.apps.nowinandroid.core.database.Recent_search_query import com.google.samples.apps.nowinandroid.core.database.model.RecentSearchQueryEntity +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow +import kotlinx.datetime.Instant /** * DAO for [RecentSearchQueryEntity] access */ -@Dao -interface RecentSearchQueryDao { - @Query(value = "SELECT * FROM recentSearchQueries ORDER BY queriedDate DESC LIMIT :limit") - fun getRecentSearchQueryEntities(limit: Int): Flow> +class RecentSearchQueryDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) { - @Upsert - suspend fun insertOrReplaceRecentSearchQuery(recentSearchQuery: RecentSearchQueryEntity) + private val query = db.recentSearchQueryQueries - @Query(value = "DELETE FROM recentSearchQueries") - suspend fun clearRecentSearchQueries() + fun getRecentSearchQueryEntities(limit: Int): Flow> { + return query.getRecentSearchQueryEntities(limit.toLong()) { query, timestamp -> + RecentSearchQueryEntity( + query = query, + queriedDate = Instant.fromEpochMilliseconds(timestamp ?: 0L), + ) + } + .asFlow() + .mapToList(dispatcher) + } + + suspend fun insertOrReplaceRecentSearchQuery(recentSearchQuery: RecentSearchQueryEntity) { + query.insertOrReplaceRecentSearchQuery( + recent_search_query = Recent_search_query( + query = recentSearchQuery.query, + queried_date = recentSearchQuery.queriedDate.toEpochMilliseconds(), + ) + ) + } + + suspend fun clearRecentSearchQueries() { + query.clearRecentSearchQueries() + } } diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq index d1dc9557e..83c0896b2 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/RecentSearchQuery.sq @@ -1,6 +1,5 @@ CREATE TABLE recent_search_query ( - id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, - query TEXT NOT NULL, + query TEXT NOT NULL PRIMARY KEY, queried_date INTEGER DEFAULT CURRENT_TIMESTAMP ); @@ -8,7 +7,7 @@ getRecentSearchQueryEntities: SELECT * FROM recent_search_query ORDER BY queried_date DESC LIMIT :limit; insertOrReplaceRecentSearchQuery: -INSERT OR REPLACE INTO recent_search_query (query) VALUES (:query); +INSERT OR REPLACE INTO recent_search_query (query) VALUES :query; clearRecentSearchQueries: DELETE FROM recent_search_query; From ef2fe0252d0004f5dc99ce5b0ac6650b6f346a32 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 15 Feb 2024 10:51:19 -0800 Subject: [PATCH 25/35] Implement TopicDao --- .../core/database/dao/TopicDao.kt | 134 +++++++++++++----- .../apps/nowinandroid/core/database/Topics.sq | 6 +- 2 files changed, 101 insertions(+), 39 deletions(-) diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicDao.kt index e876458ee..200aa9f1d 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicDao.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicDao.kt @@ -16,61 +16,123 @@ package com.google.samples.apps.nowinandroid.core.database.dao -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query -import androidx.room.Upsert +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import app.cash.sqldelight.coroutines.mapToOne +import com.google.samples.apps.nowinandroid.core.database.NiaDatabase import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map /** * DAO for [TopicEntity] access */ -@Dao -interface TopicDao { - @Query( - value = """ - SELECT * FROM topics - WHERE id = :topicId - """, - ) - fun getTopicEntity(topicId: String): Flow - @Query(value = "SELECT * FROM topics") - fun getTopicEntities(): Flow> +class TopicDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) { - @Query(value = "SELECT * FROM topics") - suspend fun getOneOffTopicEntities(): List + private val query = db.topicsQueries - @Query( - value = """ - SELECT * FROM topics - WHERE id IN (:ids) - """, - ) - fun getTopicEntities(ids: Set): Flow> + fun getTopicEntity(topicId: String): Flow { + return query.getTopicEntity(topicId) { id, name, shortDescription, longDescription, url, imageUrl -> + TopicEntity( + id = id, + name = name, + shortDescription = shortDescription, + longDescription = longDescription, + url = url, + imageUrl = imageUrl, + ) + } + .asFlow() + .mapToOne(dispatcher) + } + + fun getTopicEntities(): Flow> { + return query.getOneOffTopicEntities { id, name, shortDescription, longDescription, url, imageUrl -> + TopicEntity( + id = id, + name = name, + shortDescription = shortDescription, + longDescription = longDescription, + url = url, + imageUrl = imageUrl, + ) + } + .asFlow() + .mapToList(dispatcher) + } + + suspend fun getOneOffTopicEntities(): List { + // TODO: Use flow? + return query.getOneOffTopicEntities { id, name, shortDescription, longDescription, url, imageUrl -> + TopicEntity( + id = id, + name = name, + shortDescription = shortDescription, + longDescription = longDescription, + url = url, + imageUrl = imageUrl, + ) + }.executeAsList() + } + + fun getTopicEntities(ids: Set): Flow> { + return query.getTopicEntities { id, name, shortDescription, longDescription, url, imageUrl -> + TopicEntity( + id = id, + name = name, + shortDescription = shortDescription, + longDescription = longDescription, + url = url, + imageUrl = imageUrl, + ) + } + .asFlow() + .mapToList(dispatcher) + .map { + it.filter { topic -> topic.id in ids } + } + } /** * Inserts [topicEntities] into the db if they don't exist, and ignores those that do */ - @Insert(onConflict = OnConflictStrategy.IGNORE) - suspend fun insertOrIgnoreTopics(topicEntities: List): List + suspend fun insertOrIgnoreTopics(topicEntities: List): List { + topicEntities.map { + query.insertOrIgnoreTopic( + id = it.id, + name = it.name, + short_description = it.shortDescription, + long_description = it.longDescription, + url = it.url, + image_url = it.imageUrl, + ) + } + // TODO return the ids of the inserted topics + return topicEntities.mapNotNull { it.id.toLongOrNull() } + } /** * Inserts or updates [entities] in the db under the specified primary keys */ - @Upsert - suspend fun upsertTopics(entities: List) + suspend fun upsertTopics(entities: List) { + entities.forEach { + query.upsertTopic( + id = it.id, + name = it.name, + short_description = it.shortDescription, + long_description = it.longDescription, + url = it.url, + image_url = it.imageUrl, + ) + } + } /** * Deletes rows in the db matching the specified [ids] */ - @Query( - value = """ - DELETE FROM topics - WHERE id in (:ids) - """, - ) - suspend fun deleteTopics(ids: List) + suspend fun deleteTopics(ids: List) { + query.deleteTopics(ids) + } } diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq index 7aa803a34..90359bb5a 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/Topics.sq @@ -16,11 +16,11 @@ SELECT * FROM topic; getOneOffTopicEntities: SELECT * FROM topic; -insertOrIgnoreTopics: +insertOrIgnoreTopic: INSERT OR IGNORE INTO topic(id, name, short_description, long_description, url, image_url) VALUES (?, ?, ?, ?, ?, ?); -upsertTopics: +upsertTopic: INSERT INTO topic(id, name, short_description, long_description, url, image_url) VALUES (?, ?, ?, ?, ?, ?) ON CONFLICT(id) DO UPDATE SET @@ -31,4 +31,4 @@ url = excluded.url, image_url = excluded.image_url; deleteTopics: -DELETE FROM topic WHERE id IN (:ids); +DELETE FROM topic WHERE id IN :ids; From 5d1988a65a6be5b0d808e27c3665c7d860247632 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 15 Feb 2024 10:58:14 -0800 Subject: [PATCH 26/35] Implement TopicsFts --- .../core/database/dao/TopicFtsDao.kt | 45 ++++++++++++++----- .../database/{TopicsFts.sq => TopicFts.sq} | 2 +- 2 files changed, 34 insertions(+), 13 deletions(-) rename core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/{TopicsFts.sq => TopicFts.sq} (99%) diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicFtsDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicFtsDao.kt index 25dea5b83..303fd14b0 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicFtsDao.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/TopicFtsDao.kt @@ -16,24 +16,45 @@ package com.google.samples.apps.nowinandroid.core.database.dao -import androidx.room.Dao -import androidx.room.Insert -import androidx.room.OnConflictStrategy -import androidx.room.Query +import app.cash.sqldelight.coroutines.asFlow +import app.cash.sqldelight.coroutines.mapToList +import app.cash.sqldelight.coroutines.mapToOne +import com.google.samples.apps.nowinandroid.core.database.NiaDatabase import com.google.samples.apps.nowinandroid.core.database.model.TopicFtsEntity +import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map /** * DAO for [TopicFtsEntity] access. */ -@Dao -interface TopicFtsDao { - @Insert(onConflict = OnConflictStrategy.REPLACE) - suspend fun insertAll(topics: List) +class TopicFtsDao(db: NiaDatabase, private val dispatcher: CoroutineDispatcher) { - @Query("SELECT topicId FROM topicsFts WHERE topicsFts MATCH :query") - fun searchAllTopics(query: String): Flow> + private val dbQuery = db.topicFtsQueries - @Query("SELECT count(*) FROM topicsFts") - fun getCount(): Flow + suspend fun insertAll(topics: List) { + topics.forEach { + dbQuery.insert( + topic_id = it.topicId, + name = it.name, + short_description = it.shortDescription, + long_description = it.longDescription, + ) + } + } + + fun searchAllTopics(query: String): Flow> { + return dbQuery.searchAllTopics(query) { + it.orEmpty() + } + .asFlow() + .mapToList(dispatcher) + } + + fun getCount(): Flow { + return dbQuery.getCount() + .asFlow() + .mapToOne(dispatcher) + .map { it.toInt() } + } } diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicFts.sq similarity index 99% rename from core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq rename to core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicFts.sq index 4315078b1..bf476f7fb 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicsFts.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/TopicFts.sq @@ -26,7 +26,7 @@ BEGIN WHERE topic_id = new.id; END; -insertAll: +insert: INSERT INTO topic_fts (topic_id, name, short_description, long_description) VALUES (?, ?, ?, ?) ON CONFLICT(topic_id) DO UPDATE SET From 5e792f08d9184a08c71d32dd08aa6bb034fd5dbe Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 15 Feb 2024 11:02:16 -0800 Subject: [PATCH 27/35] Remove Android room annotations --- .../core/database/dao/RecentSearchQueryDao.kt | 2 +- .../database/model/NewsResourceFtsEntity.kt | 12 -------- .../model/NewsResourceTopicCrossRef.kt | 29 ------------------- .../database/model/PopulatedNewsResource.kt | 13 --------- .../database/model/RecentSearchQueryEntity.kt | 8 ----- .../core/database/model/TopicFtsEntity.kt | 14 --------- .../core/database/util/InstantConverter.kt | 4 +-- 7 files changed, 2 insertions(+), 80 deletions(-) diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/RecentSearchQueryDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/RecentSearchQueryDao.kt index 6fa361e09..551455f23 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/RecentSearchQueryDao.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/RecentSearchQueryDao.kt @@ -48,7 +48,7 @@ class RecentSearchQueryDao(db: NiaDatabase, private val dispatcher: CoroutineDis recent_search_query = Recent_search_query( query = recentSearchQuery.query, queried_date = recentSearchQuery.queriedDate.toEpochMilliseconds(), - ) + ), ) } diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceFtsEntity.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceFtsEntity.kt index 0ba625024..38992684f 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceFtsEntity.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceFtsEntity.kt @@ -16,23 +16,11 @@ package com.google.samples.apps.nowinandroid.core.database.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.Fts4 - /** * Fts entity for the news resources. See https://developer.android.com/reference/androidx/room/Fts4. */ -@Entity(tableName = "newsResourcesFts") -@Fts4 data class NewsResourceFtsEntity( - - @ColumnInfo(name = "newsResourceId") val newsResourceId: String, - - @ColumnInfo(name = "title") val title: String, - - @ColumnInfo(name = "content") val content: String, ) diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceTopicCrossRef.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceTopicCrossRef.kt index 59bf1458f..cfd596b6c 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceTopicCrossRef.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceTopicCrossRef.kt @@ -16,39 +16,10 @@ package com.google.samples.apps.nowinandroid.core.database.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.ForeignKey -import androidx.room.Index - /** * Cross reference for many to many relationship between [NewsResourceEntity] and [TopicEntity] */ -@Entity( - tableName = "news_resources_topics", - primaryKeys = ["news_resource_id", "topic_id"], - foreignKeys = [ - ForeignKey( - entity = NewsResourceEntity::class, - parentColumns = ["id"], - childColumns = ["news_resource_id"], - onDelete = ForeignKey.CASCADE, - ), - ForeignKey( - entity = TopicEntity::class, - parentColumns = ["id"], - childColumns = ["topic_id"], - onDelete = ForeignKey.CASCADE, - ), - ], - indices = [ - Index(value = ["news_resource_id"]), - Index(value = ["topic_id"]), - ], -) data class NewsResourceTopicCrossRef( - @ColumnInfo(name = "news_resource_id") val newsResourceId: String, - @ColumnInfo(name = "topic_id") val topicId: String, ) diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt index a70342401..e0a3708f1 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/PopulatedNewsResource.kt @@ -16,26 +16,13 @@ package com.google.samples.apps.nowinandroid.core.database.model -import androidx.room.Embedded -import androidx.room.Junction -import androidx.room.Relation import com.google.samples.apps.nowinandroid.core.model.data.NewsResource /** * External data layer representation of a fully populated NiA news resource */ data class PopulatedNewsResource( - @Embedded val entity: NewsResourceEntity, - @Relation( - parentColumn = "id", - entityColumn = "id", - associateBy = Junction( - value = NewsResourceTopicCrossRef::class, - parentColumn = "news_resource_id", - entityColumn = "topic_id", - ), - ) val topics: List, ) diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/RecentSearchQueryEntity.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/RecentSearchQueryEntity.kt index 9c7439233..24318d3de 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/RecentSearchQueryEntity.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/RecentSearchQueryEntity.kt @@ -16,20 +16,12 @@ package com.google.samples.apps.nowinandroid.core.database.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey import kotlinx.datetime.Instant /** * Defines an database entity that stored recent search queries. */ -@Entity( - tableName = "recentSearchQueries", -) data class RecentSearchQueryEntity( - @PrimaryKey val query: String, - @ColumnInfo val queriedDate: Instant, ) diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicFtsEntity.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicFtsEntity.kt index 23d56f2df..7c9c39183 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicFtsEntity.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/TopicFtsEntity.kt @@ -16,27 +16,13 @@ package com.google.samples.apps.nowinandroid.core.database.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.Fts4 - /** * Fts entity for the topic. See https://developer.android.com/reference/androidx/room/Fts4. */ -@Entity(tableName = "topicsFts") -@Fts4 data class TopicFtsEntity( - - @ColumnInfo(name = "topicId") val topicId: String, - - @ColumnInfo(name = "name") val name: String, - - @ColumnInfo(name = "shortDescription") val shortDescription: String, - - @ColumnInfo(name = "longDescription") val longDescription: String, ) diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt index 0b79c2099..e2082f4c3 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/util/InstantConverter.kt @@ -16,15 +16,13 @@ package com.google.samples.apps.nowinandroid.core.database.util -import androidx.room.TypeConverter import kotlinx.datetime.Instant internal class InstantConverter { - @TypeConverter + fun longToInstant(value: Long?): Instant? = value?.let(Instant::fromEpochMilliseconds) - @TypeConverter fun instantToLong(instant: Instant?): Long? = instant?.toEpochMilliseconds() } From e0ac78cb12f27dc0923f8e7ba0a1a8ab61c5f391 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 15 Feb 2024 13:43:15 -0800 Subject: [PATCH 28/35] Fix compile errors in DriverModule --- core/database/build.gradle.kts | 1 + .../{DriverFactory.kt => DriverModule.kt} | 8 +++++++- .../{DaosModule.kt => DatabaseModule.kt} | 18 +++++++++++------- .../{DriverFactory.kt => DriverModule.kt} | 5 ++++- .../core/database/model/NewsResourceEntity.kt | 9 --------- .../{DriverFactory.kt => DriverModule.kt} | 5 ++++- .../{DriverFactory.kt => DriverModule.kt} | 5 ++++- .../{DriverFactory.kt => DriverModule.kt} | 7 ++++++- 8 files changed, 37 insertions(+), 21 deletions(-) rename core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/{DriverFactory.kt => DriverModule.kt} (84%) rename core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/{DaosModule.kt => DatabaseModule.kt} (73%) rename core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/{DriverFactory.kt => DriverModule.kt} (87%) rename core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/{DriverFactory.kt => DriverModule.kt} (90%) rename core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/{DriverFactory.kt => DriverModule.kt} (89%) rename core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/{DriverFactory.kt => DriverModule.kt} (88%) diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 5a746f6db..381a92a17 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -57,6 +57,7 @@ kotlin { val jsMain by getting { dependencies { implementation(libs.sqldelight.webworker.driver) + implementation(npm("sql.js", "1.6.2")) implementation(devNpm("copy-webpack-plugin", "9.1.0")) } } diff --git a/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt b/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt similarity index 84% rename from core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt rename to core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt index 92b754383..7ffe43ffb 100644 --- a/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt +++ b/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt @@ -22,8 +22,14 @@ import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.android.AndroidSqliteDriver +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Inject +import me.tatarka.inject.annotations.Provides -actual class DriverFactory(private val context: Context) { +@Inject +actual class DriverModule(private val context: Context) { + + @Provides actual suspend fun provideDbDriver( schema: SqlSchema>, ): SqlDriver { diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt similarity index 73% rename from core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt index b9c0a947a..6e412f799 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DaosModule.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt @@ -16,38 +16,42 @@ package com.google.samples.apps.nowinandroid.core.database +import app.cash.sqldelight.db.SqlDriver import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceDao import com.google.samples.apps.nowinandroid.core.database.dao.NewsResourceFtsDao import com.google.samples.apps.nowinandroid.core.database.dao.RecentSearchQueryDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao +import kotlinx.coroutines.Dispatchers import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides -@Component -internal object DaosModule { +internal object DatabaseModule { + @Provides + fun providesNiaDatabase(driver: SqlDriver): NiaDatabase = NiaDatabase(driver) + @Provides fun providesTopicsDao( database: NiaDatabase, - ): TopicDao = database.topicDao() + ): TopicDao = TopicDao(database, Dispatchers.Default) @Provides fun providesNewsResourceDao( database: NiaDatabase, - ): NewsResourceDao = database.newsResourceDao() + ): NewsResourceDao = NewsResourceDao(database, Dispatchers.Default) @Provides fun providesTopicFtsDao( database: NiaDatabase, - ): TopicFtsDao = database.topicFtsDao() + ): TopicFtsDao = TopicFtsDao(database, Dispatchers.Default) @Provides fun providesNewsResourceFtsDao( database: NiaDatabase, - ): NewsResourceFtsDao = database.newsResourceFtsDao() + ): NewsResourceFtsDao = NewsResourceFtsDao(database, Dispatchers.Default) @Provides fun providesRecentSearchQueryDao( database: NiaDatabase, - ): RecentSearchQueryDao = database.recentSearchQueryDao() + ): RecentSearchQueryDao = RecentSearchQueryDao(database, Dispatchers.Default) } diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt similarity index 87% rename from core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt rename to core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt index 6d6166b52..344c62d86 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt @@ -19,8 +19,11 @@ package com.google.samples.apps.nowinandroid.core.database import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides -expect class DriverFactory { +expect class DriverModule { + @Provides suspend fun provideDbDriver( schema: SqlSchema>, ): SqlDriver diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt index 9450a24ad..955130c5b 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/model/NewsResourceEntity.kt @@ -16,27 +16,18 @@ package com.google.samples.apps.nowinandroid.core.database.model -import androidx.room.ColumnInfo -import androidx.room.Entity -import androidx.room.PrimaryKey import com.google.samples.apps.nowinandroid.core.model.data.NewsResource import kotlinx.datetime.Instant /** * Defines an NiA news resource. */ -@Entity( - tableName = "news_resources", -) data class NewsResourceEntity( - @PrimaryKey val id: String, val title: String, val content: String, val url: String, - @ColumnInfo(name = "header_image_url") val headerImageUrl: String?, - @ColumnInfo(name = "publish_date") val publishDate: Instant, val type: String, ) diff --git a/core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt b/core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt similarity index 90% rename from core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt rename to core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt index 12e025f06..298cd9873 100644 --- a/core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt +++ b/core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt @@ -20,9 +20,12 @@ import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.worker.WebWorkerDriver +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides import org.w3c.dom.Worker -actual class DriverFactory { +actual class DriverModule { + @Provides actual suspend fun provideDbDriver( schema: SqlSchema>, ): SqlDriver { diff --git a/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt b/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt similarity index 89% rename from core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt rename to core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt index a5be2e3c4..54b6efc42 100644 --- a/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt +++ b/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt @@ -20,8 +20,11 @@ import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides -actual class DriverFactory { +actual class DriverModule { + @Provides actual suspend fun provideDbDriver( schema: SqlSchema>, ): SqlDriver { diff --git a/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt b/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt similarity index 88% rename from core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt rename to core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt index ffe918cbc..2a2eeb977 100644 --- a/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverFactory.kt +++ b/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt @@ -21,8 +21,13 @@ import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.native.NativeSqliteDriver +import me.tatarka.inject.annotations.Component +import me.tatarka.inject.annotations.Provides -actual class DriverFactory { +@Component +actual class DriverModule { + + @Provides actual suspend fun provideDbDriver( schema: SqlSchema>, ): SqlDriver { From ebfc988fd550dc8f89b002db41f36c8cfcb3b721 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 15 Feb 2024 14:10:36 -0800 Subject: [PATCH 29/35] Remove unused convention --- build-logic/convention/build.gradle.kts | 4 ---- 1 file changed, 4 deletions(-) diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index 308846d13..2c84a8003 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -114,9 +114,5 @@ gradlePlugin { id = "nowinandroid.kmp.library" implementationClass = "KmpLibraryConventionPlugin" } - register("kmpAndroidLibrary") { - id = "nowinandroid.kmp.android.library" - implementationClass = "KmpAndroidLibraryConventionPlugin" - } } } From fdf29ab32e3ae1f43b9e3f30cb9ef358676760d5 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 15 Feb 2024 14:15:57 -0800 Subject: [PATCH 30/35] Add necessary dependencies and fix compilation errors --- core/database/build.gradle.kts | 7 +++---- .../nowinandroid/core/database/dao/NewsResourceFtsDao.kt | 4 +++- .../apps/nowinandroid/core/database/DriverModule.kt | 1 - 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 381a92a17..3ff4e180f 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -61,10 +61,9 @@ kotlin { implementation(devNpm("copy-webpack-plugin", "9.1.0")) } } - val commonTest by getting { - dependencies { - implementation(libs.kotlin.test) - } + commonTest.dependencies { + implementation(libs.kotlin.test) + implementation(libs.kotlinx.coroutines.test) } } } diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt index a2764afa5..3f619f8b2 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceFtsDao.kt @@ -23,6 +23,7 @@ import com.google.samples.apps.nowinandroid.core.database.NiaDatabase import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceFtsEntity import kotlinx.coroutines.CoroutineDispatcher import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.map /** * DAO for [NewsResourceFtsEntity] access. @@ -45,9 +46,10 @@ class NewsResourceFtsDao(db: NiaDatabase, private val dispatcher: CoroutineDispa .mapToList(dispatcher) } - fun getCount(): Flow { + fun getCount(): Flow { return dbQuery.getCount() .asFlow() .mapToOneNotNull(dispatcher) + .map { it.toInt() } } } diff --git a/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt b/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt index 2a2eeb977..cdb3a75bb 100644 --- a/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt +++ b/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt @@ -24,7 +24,6 @@ import app.cash.sqldelight.driver.native.NativeSqliteDriver import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides -@Component actual class DriverModule { @Provides From 92392ba13d1082bd9adac7bdbc53fa657261a57d Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 15 Feb 2024 17:15:00 -0800 Subject: [PATCH 31/35] Add implementations in the test module --- core/database/build.gradle.kts | 46 ++++++++----------- .../core/database/DriverModule.kt | 1 - .../core/database/BaseTest.android.kt | 28 +++++++++++ .../core/database/DatabaseModule.kt | 1 - .../core/database/DriverModule.kt | 1 - .../nowinandroid/core/database/BaseTest.kt | 31 +++++++++++++ .../core/database/dao/NewsResourceDaoTest.kt | 26 ++++------- .../core/database/DriverModule.kt | 1 - .../nowinandroid/core/database/BaseTest.js.kt | 29 ++++++++++++ .../core/database/DriverModule.kt | 1 - .../core/database/BaseTest.jvm.kt | 26 +++++++++++ .../core/database/DriverModule.kt | 1 - .../core/database/BaseTest.native.kt | 26 +++++++++++ 13 files changed, 170 insertions(+), 48 deletions(-) create mode 100644 core/database/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.android.kt create mode 100644 core/database/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.kt rename core/database/src/{androidTest => commonTest}/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt (93%) create mode 100644 core/database/src/jsTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.js.kt create mode 100644 core/database/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.jvm.kt create mode 100644 core/database/src/nativeTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.native.kt diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 3ff4e180f..51c382775 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -30,36 +30,30 @@ android { kotlin { sourceSets { - val commonMain by getting { - dependencies { - api(projects.core.model) - implementation(libs.kotlinx.datetime) - implementation(libs.kotlinInject.runtime) - implementation(libs.sqldelight.coroutines.extensions) - implementation(libs.sqldelight.primitive.adapters) - } + commonMain.dependencies { + api(projects.core.model) + implementation(libs.kotlinx.datetime) + implementation(libs.kotlinInject.runtime) + implementation(libs.sqldelight.coroutines.extensions) + implementation(libs.sqldelight.primitive.adapters) } - val androidMain by getting { - dependencies { - implementation(libs.sqldelight.android.driver) - } + androidMain.dependencies { + implementation(libs.sqldelight.android.driver) } - val nativeMain by getting { - dependencies { - implementation(libs.sqldelight.native.driver) - } + androidUnitTest.dependencies { + implementation(libs.androidx.test.core) } - val jvmMain by getting { - dependencies { - implementation(libs.sqldelight.sqlite.driver) - } + nativeMain.dependencies { + implementation(libs.sqldelight.native.driver) } - val jsMain by getting { - dependencies { - implementation(libs.sqldelight.webworker.driver) - implementation(npm("sql.js", "1.6.2")) - implementation(devNpm("copy-webpack-plugin", "9.1.0")) - } + + jvmMain.dependencies { + implementation(libs.sqldelight.sqlite.driver) + } + jsMain.dependencies { + implementation(libs.sqldelight.webworker.driver) + implementation(npm("sql.js", "1.6.2")) + implementation(devNpm("copy-webpack-plugin", "9.1.0")) } commonTest.dependencies { implementation(libs.kotlin.test) diff --git a/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt b/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt index 7ffe43ffb..4e703461d 100644 --- a/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt +++ b/core/database/src/androidMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt @@ -22,7 +22,6 @@ import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.android.AndroidSqliteDriver -import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Inject import me.tatarka.inject.annotations.Provides diff --git a/core/database/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.android.kt b/core/database/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.android.kt new file mode 100644 index 000000000..e478729b9 --- /dev/null +++ b/core/database/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.android.kt @@ -0,0 +1,28 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.database + +import android.content.Context +import androidx.test.core.app.ApplicationProvider +import app.cash.sqldelight.async.coroutines.synchronous +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.android.AndroidSqliteDriver + +actual suspend fun createDriver(): SqlDriver { + val context: Context = ApplicationProvider.getApplicationContext() + return AndroidSqliteDriver(NiaDatabase.Schema.synchronous(), context, "nia-database-test.db") +} diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt index 6e412f799..d5bddadc4 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DatabaseModule.kt @@ -23,7 +23,6 @@ import com.google.samples.apps.nowinandroid.core.database.dao.RecentSearchQueryD import com.google.samples.apps.nowinandroid.core.database.dao.TopicDao import com.google.samples.apps.nowinandroid.core.database.dao.TopicFtsDao import kotlinx.coroutines.Dispatchers -import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides internal object DatabaseModule { diff --git a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt index 344c62d86..aaeaf9b15 100644 --- a/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt +++ b/core/database/src/commonMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt @@ -19,7 +19,6 @@ package com.google.samples.apps.nowinandroid.core.database import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema -import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides expect class DriverModule { diff --git a/core/database/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.kt b/core/database/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.kt new file mode 100644 index 000000000..a337ab8f9 --- /dev/null +++ b/core/database/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.kt @@ -0,0 +1,31 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.database + +import app.cash.sqldelight.db.SqlDriver +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.test.runTest + +/** + * Init driver for each platform. Should *always* be called to setup test + */ +expect suspend fun createDriver(): SqlDriver +fun testing(block: suspend CoroutineScope.(NiaDatabase) -> Unit) = runTest { + val driver = createDriver() + block(NiaDatabase(driver)) + driver.close() +} diff --git a/core/database/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt b/core/database/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt similarity index 93% rename from core/database/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt rename to core/database/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt index 55e817618..8b873e8cb 100644 --- a/core/database/src/androidTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt +++ b/core/database/src/commonTest/kotlin/com/google/samples/apps/nowinandroid/core/database/dao/NewsResourceDaoTest.kt @@ -1,5 +1,5 @@ /* - * Copyright 2022 The Android Open Source Project + * Copyright 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,36 +16,30 @@ package com.google.samples.apps.nowinandroid.core.database.dao -import android.content.Context -import androidx.room.Room -import androidx.test.core.app.ApplicationProvider import com.google.samples.apps.nowinandroid.core.database.NiaDatabase +import com.google.samples.apps.nowinandroid.core.database.createDriver import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceEntity import com.google.samples.apps.nowinandroid.core.database.model.NewsResourceTopicCrossRef import com.google.samples.apps.nowinandroid.core.database.model.TopicEntity import com.google.samples.apps.nowinandroid.core.database.model.asExternalModel +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.first import kotlinx.coroutines.test.runTest import kotlinx.datetime.Instant -import org.junit.Before -import org.junit.Test +import kotlin.test.BeforeTest +import kotlin.test.Test import kotlin.test.assertEquals class NewsResourceDaoTest { private lateinit var newsResourceDao: NewsResourceDao private lateinit var topicDao: TopicDao - private lateinit var db: NiaDatabase - @Before - fun createDb() { - val context = ApplicationProvider.getApplicationContext() - db = Room.inMemoryDatabaseBuilder( - context, - NiaDatabase::class.java, - ).build() - newsResourceDao = db.newsResourceDao() - topicDao = db.topicDao() + @BeforeTest + fun setup() = runTest { + val db = NiaDatabase(createDriver()) + newsResourceDao = NewsResourceDao(db, Dispatchers.Unconfined) + topicDao = TopicDao(db, Dispatchers.Unconfined) } @Test diff --git a/core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt b/core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt index 298cd9873..4a34bc105 100644 --- a/core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt +++ b/core/database/src/jsMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt @@ -20,7 +20,6 @@ import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.worker.WebWorkerDriver -import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides import org.w3c.dom.Worker diff --git a/core/database/src/jsTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.js.kt b/core/database/src/jsTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.js.kt new file mode 100644 index 000000000..39e519254 --- /dev/null +++ b/core/database/src/jsTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.js.kt @@ -0,0 +1,29 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.database + +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.worker.WebWorkerDriver +import org.w3c.dom.Worker + +actual suspend fun createDriver(): SqlDriver { + return WebWorkerDriver( + Worker( + js("""new URL("@cashapp/sqldelight-sqljs-worker/sqljs.worker.js", import.meta.url)"""), + ), + ).also { NiaDatabase.Schema.create(it).await() } +} diff --git a/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt b/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt index 54b6efc42..8b8e797dc 100644 --- a/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt +++ b/core/database/src/jvmMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt @@ -20,7 +20,6 @@ import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver -import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides actual class DriverModule { diff --git a/core/database/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.jvm.kt b/core/database/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.jvm.kt new file mode 100644 index 000000000..a77aa2349 --- /dev/null +++ b/core/database/src/jvmTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.jvm.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.database + +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver +import com.google.samples.apps.nowinandroid.core.database.NiaDatabase.Companion.Schema + +actual suspend fun createDriver(): SqlDriver { + return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) + .also { Schema.create(it).await() } +} diff --git a/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt b/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt index cdb3a75bb..cd54d58d2 100644 --- a/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt +++ b/core/database/src/nativeMain/kotlin/com/google/samples/apps/nowinandroid/core/database/DriverModule.kt @@ -21,7 +21,6 @@ import app.cash.sqldelight.db.QueryResult import app.cash.sqldelight.db.SqlDriver import app.cash.sqldelight.db.SqlSchema import app.cash.sqldelight.driver.native.NativeSqliteDriver -import me.tatarka.inject.annotations.Component import me.tatarka.inject.annotations.Provides actual class DriverModule { diff --git a/core/database/src/nativeTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.native.kt b/core/database/src/nativeTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.native.kt new file mode 100644 index 000000000..d7b6b1c9d --- /dev/null +++ b/core/database/src/nativeTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.native.kt @@ -0,0 +1,26 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.samples.apps.nowinandroid.core.database + +import app.cash.sqldelight.async.coroutines.synchronous +import app.cash.sqldelight.db.SqlDriver +import app.cash.sqldelight.driver.native.NativeSqliteDriver +import com.google.samples.apps.nowinandroid.core.database.NiaDatabase.Companion.Schema + +actual suspend fun createDriver(): SqlDriver { + return NativeSqliteDriver(Schema.synchronous(), "nia-database-test.db") +} From 34e29817857c71479988114c1ea01923e89ac275 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Thu, 15 Feb 2024 20:54:57 -0800 Subject: [PATCH 32/35] Using Java driver in the android tests --- core/database/build.gradle.kts | 3 +-- .../apps/nowinandroid/core/database/BaseTest.android.kt | 9 +++------ .../apps/nowinandroid/core/database/NewsResourceFts.sq | 2 -- 3 files changed, 4 insertions(+), 10 deletions(-) diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 51c382775..405649aa8 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -41,12 +41,11 @@ kotlin { implementation(libs.sqldelight.android.driver) } androidUnitTest.dependencies { - implementation(libs.androidx.test.core) + implementation(libs.sqldelight.sqlite.driver) } nativeMain.dependencies { implementation(libs.sqldelight.native.driver) } - jvmMain.dependencies { implementation(libs.sqldelight.sqlite.driver) } diff --git a/core/database/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.android.kt b/core/database/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.android.kt index e478729b9..56e63a063 100644 --- a/core/database/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.android.kt +++ b/core/database/src/androidUnitTest/kotlin/com/google/samples/apps/nowinandroid/core/database/BaseTest.android.kt @@ -16,13 +16,10 @@ package com.google.samples.apps.nowinandroid.core.database -import android.content.Context -import androidx.test.core.app.ApplicationProvider -import app.cash.sqldelight.async.coroutines.synchronous import app.cash.sqldelight.db.SqlDriver -import app.cash.sqldelight.driver.android.AndroidSqliteDriver +import app.cash.sqldelight.driver.jdbc.sqlite.JdbcSqliteDriver actual suspend fun createDriver(): SqlDriver { - val context: Context = ApplicationProvider.getApplicationContext() - return AndroidSqliteDriver(NiaDatabase.Schema.synchronous(), context, "nia-database-test.db") + return JdbcSqliteDriver(JdbcSqliteDriver.IN_MEMORY) + .also { NiaDatabase.Schema.create(it).await() } } diff --git a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq index f14fcb96c..59795c2e4 100644 --- a/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq +++ b/core/database/src/commonMain/sqldelight/com/google/samples/apps/nowinandroid/core/database/NewsResourceFts.sq @@ -1,7 +1,5 @@ CREATE VIRTUAL TABLE news_resource_fts USING FTS4( - tokenizer = porter, - content = news_resources, news_resource_id TEXT NOT NULL, title TEXT NOT NULL, content TEXT NOT NULL, From 006732f24a89ad34e77cb79ad711f9f752451076 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 16 Feb 2024 09:55:35 -0800 Subject: [PATCH 33/35] Suppress warning: expect'/'actual' classes are in Beta --- .../samples/apps/nowinandroid/KotlinMultiplatform.kt | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt index 544f440a4..cc99af1f7 100644 --- a/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt +++ b/build-logic/convention/src/main/kotlin/com/google/samples/apps/nowinandroid/KotlinMultiplatform.kt @@ -18,8 +18,10 @@ package com.google.samples.apps.nowinandroid import org.gradle.api.Project import org.gradle.kotlin.dsl.configure +import org.gradle.kotlin.dsl.withType import org.jetbrains.kotlin.gradle.ExperimentalKotlinGradlePluginApi import org.jetbrains.kotlin.gradle.dsl.KotlinMultiplatformExtension +import org.jetbrains.kotlin.gradle.tasks.KotlinCompile import org.jetbrains.kotlin.konan.target.HostManager /** @@ -83,6 +85,16 @@ internal fun Project.configureKotlinMultiplatform() { project.tasks.named("linuxX64Test") { enabled = HostManager.hostIsLinux } project.tasks.named("linkDebugTestLinuxX64") { enabled = HostManager.hostIsLinux } + tasks.withType().configureEach { + kotlinOptions { + freeCompilerArgs = freeCompilerArgs + listOf( + // Suppress warning:'expect'/'actual' classes (including interfaces, objects, + // annotations, enums, and 'actual' typealiases) are in Beta. + "-Xexpect-actual-classes", + ) + } + } + // Fixes Cannot locate tasks that match ':core:model:testClasses' as task 'testClasses' // not found in project ':core:model'. Some candidates are: 'jsTestClasses', 'jvmTestClasses'. project.tasks.create("testClasses") { From af1ab659819a3d4bd8c1d6f69669d4991eb8bc5b Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 16 Feb 2024 10:40:53 -0800 Subject: [PATCH 34/35] Add new KotlinInjectConventionPlugin --- build-logic/convention/build.gradle.kts | 8 ++++ .../kotlin/KotlinInjectConventionPlugin.kt | 40 +++++++++++++++++++ core/database/build.gradle.kts | 12 +----- gradle/libs.versions.toml | 7 +++- 4 files changed, 54 insertions(+), 13 deletions(-) create mode 100644 build-logic/convention/src/main/kotlin/KotlinInjectConventionPlugin.kt diff --git a/build-logic/convention/build.gradle.kts b/build-logic/convention/build.gradle.kts index 2c84a8003..a91e06232 100644 --- a/build-logic/convention/build.gradle.kts +++ b/build-logic/convention/build.gradle.kts @@ -114,5 +114,13 @@ gradlePlugin { id = "nowinandroid.kmp.library" implementationClass = "KmpLibraryConventionPlugin" } + register("kotlinInject") { + id = "nowinandroid.kmp.inject" + implementationClass = "KotlinInjectConventionPlugin" + } + register("sqldelight") { + id = "nowinandroid.sqldelight" + implementationClass = "SqlDelightConventionPlugin" + } } } diff --git a/build-logic/convention/src/main/kotlin/KotlinInjectConventionPlugin.kt b/build-logic/convention/src/main/kotlin/KotlinInjectConventionPlugin.kt new file mode 100644 index 000000000..61e491ff1 --- /dev/null +++ b/build-logic/convention/src/main/kotlin/KotlinInjectConventionPlugin.kt @@ -0,0 +1,40 @@ +/* + * Copyright 2024 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import com.google.samples.apps.nowinandroid.libs +import org.gradle.api.Plugin +import org.gradle.api.Project +import org.gradle.kotlin.dsl.dependencies + +class KotlinInjectConventionPlugin: Plugin { + override fun apply(target: Project) { + with(target) { + with(pluginManager) { + apply("com.google.devtools.ksp") + } + dependencies { + add("kspCommonMainMetadata", libs.findLibrary("kotlin.inject.compiler.ksp").get()) + add("commonMainImplementation", libs.findLibrary("kotlin.inject.runtime").get()) + // KSP will eventually have better multiplatform support and we'll be able to simply have + // `ksp libs.kotlinInject.compiler` in the dependencies block of each source set + // https://github.com/google/ksp/pull/1021 + add("kspIosX64", libs.findLibrary("kotlin.inject.compiler.ksp").get()) + add("kspIosArm64", libs.findLibrary("kotlin.inject.compiler.ksp").get()) + add("kspIosSimulatorArm64", libs.findLibrary("kotlin.inject.compiler.ksp").get()) + } + } + } +} \ No newline at end of file diff --git a/core/database/build.gradle.kts b/core/database/build.gradle.kts index 405649aa8..27cf0206b 100644 --- a/core/database/build.gradle.kts +++ b/core/database/build.gradle.kts @@ -16,8 +16,8 @@ plugins { alias(libs.plugins.nowinandroid.kmp.library) + alias(libs.plugins.nowinandroid.kotlin.inject) alias(libs.plugins.sqldelight.gradle.plugin) - alias(libs.plugins.ksp) } android { @@ -33,7 +33,6 @@ kotlin { commonMain.dependencies { api(projects.core.model) implementation(libs.kotlinx.datetime) - implementation(libs.kotlinInject.runtime) implementation(libs.sqldelight.coroutines.extensions) implementation(libs.sqldelight.primitive.adapters) } @@ -70,12 +69,3 @@ sqldelight { } } } - -dependencies { - // KSP will eventually have better multiplatform support and we'll be able to simply have - // `ksp libs.kotlinInject.compiler` in the dependencies block of each source set - // https://github.com/google/ksp/pull/1021 - add("kspIosX64", libs.kotlinInject.compiler) - add("kspIosArm64", libs.kotlinInject.compiler) - add("kspIosSimulatorArm64", libs.kotlinInject.compiler) -} diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 7e6b1cc3d..053e1ab7f 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -164,8 +164,8 @@ sqldelight-sqlite-driver = { group = "app.cash.sqldelight", name = "sqlite-drive sqldelight-webworker-driver = { group = "app.cash.sqldelight", name = "web-worker-driver", version.ref = "sqldelight" } sqldelight-coroutines-extensions = { group = "app.cash.sqldelight", name = "coroutines-extensions", version.ref = "sqldelight" } sqldelight-primitive-adapters = { group = "app.cash.sqldelight", name = "primitive-adapters", version.ref = "sqldelight" } -kotlinInject-compiler = { module = 'me.tatarka.inject:kotlin-inject-compiler-ksp', version.ref = 'kotlinInject' } -kotlinInject-runtime = { module = 'me.tatarka.inject:kotlin-inject-runtime', version.ref = 'kotlinInject' } +kotlin-inject-compiler-ksp = { group = "me.tatarka.inject", name = "kotlin-inject-compiler-ksp", version.ref = "kotlinInject" } +kotlin-inject-runtime = { group = "me.tatarka.inject", name = "kotlin-inject-runtime", version.ref = "kotlinInject" } # Dependencies of the included build-logic android-gradlePlugin = { group = "com.android.tools.build", name = "gradle", version.ref = "androidGradlePlugin" } @@ -214,3 +214,6 @@ nowinandroid-android-room = { id = "nowinandroid.android.room", version = "unspe nowinandroid-android-test = { id = "nowinandroid.android.test", version = "unspecified" } nowinandroid-jvm-library = { id = "nowinandroid.jvm.library", version = "unspecified" } nowinandroid-kmp-library = { id = "nowinandroid.kmp.library", version = "unspecified" } +nowinandroid-kotlin-inject = { id = "nowinandroid.kmp.inject", version = "unspecified" } +nowinandroid-sqldelight = { id = "nowinandroid.sqldelight", version = "unspecified" } + From b6bfc020af887a82b22befc41b1e7be9e04aeee7 Mon Sep 17 00:00:00 2001 From: lihenggui Date: Fri, 16 Feb 2024 10:49:00 -0800 Subject: [PATCH 35/35] Update kotlinUpgradeYarnLock --- kotlin-js-store/yarn.lock | 140 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 136 insertions(+), 4 deletions(-) diff --git a/kotlin-js-store/yarn.lock b/kotlin-js-store/yarn.lock index e7d5142ea..366040fcd 100644 --- a/kotlin-js-store/yarn.lock +++ b/kotlin-js-store/yarn.lock @@ -57,6 +57,27 @@ resolved "https://registry.yarnpkg.com/@js-joda/core/-/core-3.2.0.tgz#3e61e21b7b2b8a6be746df1335cf91d70db2a273" integrity sha512-PMqgJ0sw5B7FKb2d5bWYIoxjri+QlW/Pys7+Rw82jSH0QN3rB05jZ/VrrsUdh1w4+i2kw9JOejXGq/KhDOX7Kg== +"@nodelib/fs.scandir@2.1.5": + version "2.1.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" + integrity sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g== + dependencies: + "@nodelib/fs.stat" "2.0.5" + run-parallel "^1.1.9" + +"@nodelib/fs.stat@2.0.5", "@nodelib/fs.stat@^2.0.2": + version "2.0.5" + resolved "https://registry.yarnpkg.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz#5bd262af94e9d25bd1e71b05deed44876a222e8b" + integrity sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A== + +"@nodelib/fs.walk@^1.2.3": + version "1.2.8" + resolved "https://registry.yarnpkg.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz#e95737e8bb6746ddedf69c556953494f196fe69a" + integrity sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg== + dependencies: + "@nodelib/fs.scandir" "2.1.5" + fastq "^1.6.0" + "@socket.io/component-emitter@~3.1.0": version "3.1.0" resolved "https://registry.yarnpkg.com/@socket.io/component-emitter/-/component-emitter-3.1.0.tgz#96116f2a912e0c02817345b3c10751069920d553" @@ -321,6 +342,11 @@ argparse@^2.0.1: resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== +array-union@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/array-union/-/array-union-2.1.0.tgz#b798420adbeb1de828d84acd8a2e23d3efe85e8d" + integrity sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw== + balanced-match@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" @@ -534,6 +560,18 @@ cookie@~0.4.1: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== +copy-webpack-plugin@9.1.0: + version "9.1.0" + resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-9.1.0.tgz#2d2c460c4c4695ec0a58afb2801a1205256c4e6b" + integrity sha512-rxnR7PaGigJzhqETHGmAcxKnLZSR5u1Y3/bcIv/1FnqXedcL/E2ewK7ZCNrArJKCiSv8yVXhTqetJh8inDvfsA== + dependencies: + fast-glob "^3.2.7" + glob-parent "^6.0.1" + globby "^11.0.3" + normalize-path "^3.0.0" + schema-utils "^3.1.1" + serialize-javascript "^6.0.0" + cors@~2.8.5: version "2.8.5" resolved "https://registry.yarnpkg.com/cors/-/cors-2.8.5.tgz#eac11da51592dd86b9f06f6e7ac293b3df875d29" @@ -610,6 +648,13 @@ diff@5.0.0: resolved "https://registry.yarnpkg.com/diff/-/diff-5.0.0.tgz#7ed6ad76d859d030787ec35855f5b1daf31d852b" integrity sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w== +dir-glob@^3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/dir-glob/-/dir-glob-3.0.1.tgz#56dbf73d992a4a93ba1584f4534063fd2e41717f" + integrity sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA== + dependencies: + path-type "^4.0.0" + dom-serialize@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/dom-serialize/-/dom-serialize-2.2.1.tgz#562ae8999f44be5ea3076f5419dcd59eb43ac95b" @@ -749,6 +794,17 @@ fast-deep-equal@^3.1.1: resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== +fast-glob@^3.2.7, fast-glob@^3.2.9: + version "3.3.2" + resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129" + integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow== + dependencies: + "@nodelib/fs.stat" "^2.0.2" + "@nodelib/fs.walk" "^1.2.3" + glob-parent "^5.1.2" + merge2 "^1.3.0" + micromatch "^4.0.4" + fast-json-stable-stringify@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" @@ -759,6 +815,13 @@ fastest-levenshtein@^1.0.12: resolved "https://registry.yarnpkg.com/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz#210e61b6ff181de91ea9b3d1b84fdedd47e034e5" integrity sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg== +fastq@^1.6.0: + version "1.17.1" + resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.17.1.tgz#2a523f07a4e7b1e81a42b91b8bf2254107753b47" + integrity sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w== + dependencies: + reusify "^1.0.4" + fill-range@^7.0.1: version "7.0.1" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" @@ -855,13 +918,20 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.2, get-intrinsic@^1.2.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" -glob-parent@~5.1.2: +glob-parent@^5.1.2, glob-parent@~5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4" integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow== dependencies: is-glob "^4.0.1" +glob-parent@^6.0.1: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + glob-to-regexp@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e" @@ -891,6 +961,18 @@ glob@^7.1.3, glob@^7.1.7: once "^1.3.0" path-is-absolute "^1.0.0" +globby@^11.0.3: + version "11.1.0" + resolved "https://registry.yarnpkg.com/globby/-/globby-11.1.0.tgz#bd4be98bb042f83d796f7e3811991fbe82a0d34b" + integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g== + dependencies: + array-union "^2.1.0" + dir-glob "^3.0.1" + fast-glob "^3.2.9" + ignore "^5.2.0" + merge2 "^1.4.1" + slash "^3.0.0" + gopd@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c" @@ -971,6 +1053,11 @@ iconv-lite@^0.6.3: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" +ignore@^5.2.0: + version "5.3.1" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.1.tgz#5073e554cd42c5b33b394375f538b8593e34d4ef" + integrity sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw== + import-local@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/import-local/-/import-local-3.1.0.tgz#b4479df8a5fd44f6cdce24070675676063c95cb4" @@ -1021,7 +1108,7 @@ is-fullwidth-code-point@^3.0.0: resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d" integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg== -is-glob@^4.0.1, is-glob@~4.0.1: +is-glob@^4.0.1, is-glob@^4.0.3, is-glob@~4.0.1: version "4.0.3" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== @@ -1216,6 +1303,19 @@ merge-stream@^2.0.0: resolved "https://registry.yarnpkg.com/merge-stream/-/merge-stream-2.0.0.tgz#52823629a14dd00c9770fb6ad47dc6310f2c1f60" integrity sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w== +merge2@^1.3.0, merge2@^1.4.1: + version "1.4.1" + resolved "https://registry.yarnpkg.com/merge2/-/merge2-1.4.1.tgz#4368892f885e907455a6fd7dc55c0c9d404990ae" + integrity sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg== + +micromatch@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-4.0.5.tgz#bc8999a7cbbf77cdc89f132f6e467051b49090c6" + integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA== + dependencies: + braces "^3.0.2" + picomatch "^2.3.1" + mime-db@1.52.0: version "1.52.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" @@ -1415,12 +1515,17 @@ path-parse@^1.0.7: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.7.tgz#fbc114b60ca42b30d9daf5858e4bd68bbedb6735" integrity sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw== +path-type@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" + integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== + picocolors@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c" integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== -picomatch@^2.0.4, picomatch@^2.2.1: +picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1: version "2.3.1" resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42" integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA== @@ -1449,6 +1554,11 @@ qs@6.11.0: dependencies: side-channel "^1.0.4" +queue-microtask@^1.2.2: + version "1.2.3" + resolved "https://registry.yarnpkg.com/queue-microtask/-/queue-microtask-1.2.3.tgz#4929228bbc724dfac43e0efb058caf7b6cfb6243" + integrity sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A== + randombytes@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/randombytes/-/randombytes-2.1.0.tgz#df6f84372f0270dc65cdf6291349ab7a473d4f2a" @@ -1516,6 +1626,11 @@ resolve@^1.20.0: path-parse "^1.0.7" supports-preserve-symlinks-flag "^1.0.0" +reusify@^1.0.4: + version "1.0.4" + resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" + integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== + rfdc@^1.3.0: version "1.3.1" resolved "https://registry.yarnpkg.com/rfdc/-/rfdc-1.3.1.tgz#2b6d4df52dffe8bb346992a10ea9451f24373a8f" @@ -1528,6 +1643,13 @@ rimraf@^3.0.0, rimraf@^3.0.2: dependencies: glob "^7.1.3" +run-parallel@^1.1.9: + version "1.2.0" + resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" + integrity sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA== + dependencies: + queue-microtask "^1.2.2" + safe-buffer@^5.1.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" @@ -1554,7 +1676,7 @@ serialize-javascript@6.0.0: dependencies: randombytes "^2.1.0" -serialize-javascript@^6.0.1: +serialize-javascript@^6.0.0, serialize-javascript@^6.0.1: version "6.0.2" resolved "https://registry.yarnpkg.com/serialize-javascript/-/serialize-javascript-6.0.2.tgz#defa1e055c83bf6d59ea805d8da862254eb6a6c2" integrity sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g== @@ -1607,6 +1729,11 @@ side-channel@^1.0.4: get-intrinsic "^1.2.4" object-inspect "^1.13.1" +slash@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634" + integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q== + socket.io-adapter@~2.5.2: version "2.5.2" resolved "https://registry.yarnpkg.com/socket.io-adapter/-/socket.io-adapter-2.5.2.tgz#5de9477c9182fdc171cd8c8364b9a8894ec75d12" @@ -1662,6 +1789,11 @@ source-map@^0.6.0, source-map@^0.6.1: resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== +sql.js@1.6.2: + version "1.6.2" + resolved "https://registry.yarnpkg.com/sql.js/-/sql.js-1.6.2.tgz#7fb70ff68089434826a39b8f5afb2170d682eb3f" + integrity sha512-9iucI5fXQa+Gspeqf/BNB20PxJIn5LhXDt4mjXoFPqXdR+NqtFs15SdKpSIJ6s529aGL9zFR9p2eSCIEiMsNGA== + statuses@2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"