diff --git a/.github/workflows/build-and-test-ios.yml b/.github/workflows/build-and-test-ios.yml index cfbef3ec2..a1cd98b67 100644 --- a/.github/workflows/build-and-test-ios.yml +++ b/.github/workflows/build-and-test-ios.yml @@ -11,15 +11,15 @@ on: env: # Define environment variables for the workflow. # These can be overridden by the caller if needed. - WORKSPACE: PingSampleApp/ios/PingSampleApp.xcworkspace + WORKSPACE: PingTestRunner/ios/PingTestRunner.xcworkspace SCHEME: RNPackagesTests CONFIGURATION: Debug DESTINATION: platform=iOS Simulator,name=iPhone 16e,OS=latest DERIVED_DATA_PATH: DerivedData XCODE_VERSION: '26.1.1' RUBY_VERSION: '2.6.10' - BUNDLE_GEMFILE: ${{ github.workspace }}/PingSampleApp/Gemfile - IOS_DIRECTORY: PingSampleApp/ios + BUNDLE_GEMFILE: ${{ github.workspace }}/PingTestRunner/Gemfile + IOS_DIRECTORY: PingTestRunner/ios jobs: build-and-test-ios: runs-on: macos-15-large @@ -66,14 +66,14 @@ jobs: ${{ runner.os }}-pods- # Cache DerivedData + # Note: restore-keys is intentionally omitted. A partial/stale DerivedData cache + # causes Clang module mtime mismatch errors when SDK framework files on the runner + # are newer than the cached .pcm files. Only an exact cache hit is safe to restore. - name: Cache DerivedData uses: actions/cache@v3 with: path: ${{ env.DERIVED_DATA_PATH }} - key: ${{ runner.os }}-xcode-${{ env.XCODE_VERSION }}-deriveddata-${{ hashFiles('PingSampleApp/ios/Podfile.lock', 'PingSampleApp/ios/PingSampleApp.xcodeproj/project.pbxproj', 'PingSampleApp/ios/PingSampleApp.xcodeproj/xcshareddata/xcschemes/RNPackagesTests.xcscheme', 'packages/*/*.podspec', 'packages/*/ios/**/*.swift', 'packages/*/ios/**/*.{m,mm,h,cpp}') }} - restore-keys: | - ${{ runner.os }}-xcode-${{ env.XCODE_VERSION }}-deriveddata- - + key: ${{ runner.os }}-xcode-${{ env.XCODE_VERSION }}-deriveddata-${{ hashFiles('PingTestRunner/ios/Podfile.lock', 'PingTestRunner/ios/PingTestRunner.xcodeproj/project.pbxproj', 'PingTestRunner/ios/PingTestRunner.xcodeproj/xcshareddata/xcschemes/RNPackagesTests.xcscheme', 'packages/*/*.podspec', 'packages/*/ios/**/*.swift', 'packages/*/ios/**/*.{m,mm,h,cpp}') }} # Setup Node.js environment and install dependencies. - name: Setup project dependencies uses: ./.github/actions/setup-proj @@ -87,6 +87,15 @@ jobs: - name: Select Xcode version run: sudo xcode-select -s /Applications/Xcode_${{ env.XCODE_VERSION }}.app && /usr/bin/xcodebuild -version + # Clang module caches are sensitive to SDK header mtimes on the runner image. + # This must run AFTER Xcode is selected so the active SDK is finalised. + # Keep the broader DerivedData cache for build products, but always force module + # and SDK stat caches to be rebuilt to avoid stale .pcm restore failures. + - name: Reset Xcode module caches + run: | + rm -rf "${{ github.workspace }}/${{ env.DERIVED_DATA_PATH }}/ModuleCache.noindex" + rm -rf "${{ github.workspace }}/${{ env.DERIVED_DATA_PATH }}/SDKStatCaches.noindex" + # Install xcresultparser to .xcresult files generated by xcodebuild for test reporting. - name: Install xcresultparser run: | diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 000000000..ccd055c75 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,11 @@ +/* + * Copyright (c) 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ + +module.exports = { + singleQuote: true, + trailingComma: 'all', +} diff --git a/.whitesource b/.whitesource index 5fc3d9be0..6b2345add 100644 --- a/.whitesource +++ b/.whitesource @@ -7,7 +7,6 @@ }, "scanSettingsSAST": { "enableScan": true, - "configExternalURL": "./.mendsastcli-config.json", "scanPullRequests": true, "incrementalScan": true, "findingSuppressions": "enabled", diff --git a/.yarn/install-state.gz b/.yarn/install-state.gz index 1fc113628..ab469124f 100644 Binary files a/.yarn/install-state.gz and b/.yarn/install-state.gz differ diff --git a/PingSampleApp/.eslintrc.js b/PingSampleApp/.eslintrc.js index a840ff6c4..01603d190 100644 --- a/PingSampleApp/.eslintrc.js +++ b/PingSampleApp/.eslintrc.js @@ -8,4 +8,23 @@ module.exports = { root: true, extends: '@react-native', + rules: { + 'no-void': ['warn', { allowAsStatement: true }], + }, + overrides: [ + { + files: ['*.ts', '*.tsx'], + rules: { + '@typescript-eslint/no-unused-vars': [ + 'error', + { + varsIgnorePattern: '^_', + argsIgnorePattern: '^_', + caughtErrorsIgnorePattern: '^_', + destructuredArrayIgnorePattern: '^_', + }, + ], + }, + }, + ], }; diff --git a/PingSampleApp/App.tsx b/PingSampleApp/App.tsx index 3ae57fb49..09bb9dc6d 100644 --- a/PingSampleApp/App.tsx +++ b/PingSampleApp/App.tsx @@ -9,7 +9,7 @@ import React, { useEffect, useMemo, useState } from 'react'; import { NavigationContainer } from '@react-navigation/native'; import { createNativeStackNavigator } from '@react-navigation/native-stack'; import { Text, TextInput } from 'react-native'; -import MultiStorageScreen from './ui/MultiStorageScreeen'; +import MultiStorageScreen from './ui/MultiStorageScreen'; import HomeScreen from './ui/HomeScreen'; import ConfigurationScreen from './ui/ConfigurationScreen'; import JourneyRouteScreen from './ui/JourneyRouteScreen'; @@ -29,7 +29,7 @@ import { sampleAppClientProfiles, } from './src/clients'; import { configureBrowser } from '@ping-identity/rn-browser'; -import { configureLogger, logger } from '@ping-identity/rn-logger'; +import { logger } from '@ping-identity/rn-logger'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; import { colors } from './src/styles/colors'; @@ -66,14 +66,15 @@ export default function App() { const browserLogger = useMemo(() => logger({ level: 'debug' }), []); const [initError, setInitError] = useState(null); const [selectedProfileKey, setSelectedProfileKey] = useState( - DEFAULT_SAMPLE_APP_CLIENT_PROFILE_KEY + DEFAULT_SAMPLE_APP_CLIENT_PROFILE_KEY, ); const selectedProfile = useMemo( () => - sampleAppClientProfiles.find((profile) => profile.key === selectedProfileKey) ?? - sampleAppClientProfiles[0], - [selectedProfileKey] + sampleAppClientProfiles.find( + profile => profile.key === selectedProfileKey, + ) ?? sampleAppClientProfiles[0], + [selectedProfileKey], ); useEffect(() => { @@ -85,7 +86,8 @@ export default function App() { style: [textDefaults.style, { fontFamily: 'Montserrat-Regular' }], }; - const textInputComponent = TextInput as unknown as ComponentWithDefaultStyle; + const textInputComponent = + TextInput as unknown as ComponentWithDefaultStyle; const textInputDefaults = textInputComponent.defaultProps ?? {}; textInputComponent.defaultProps = { ...textInputDefaults, @@ -107,7 +109,9 @@ export default function App() { console.error('Failed to initialize Journey client', error); if (isMounted) { const message = - error instanceof Error ? error.message : 'Failed to initialize Journey client.'; + error instanceof Error + ? error.message + : 'Failed to initialize Journey client.'; setInitError(message); } } @@ -115,27 +119,27 @@ export default function App() { void initializeJourneyClient(); - // ( TODO: REVISIT )Global SDK logger baseline. Tune this when debugging integration issues. - configureLogger({ level: 'info' }); - // Browser defaults used by OIDC/browser flows in this sample app. - configureBrowser({ - android: { - customTabs: { - showTitle: false, - urlBarHidingEnabled: true, - colorScheme: 'dark', - }, - authTabs: { - ephemeral: true, - colorScheme: 'dark', - toolbarColor: colors.browserToolbar, - navigationBarColor: colors.browserNavigationBar, + configureBrowser( + { + android: { + customTabs: { + showTitle: false, + urlBarHidingEnabled: true, + colorScheme: 'dark', + }, + authTabs: { + ephemeral: true, + colorScheme: 'dark', + toolbarColor: colors.browserToolbar, + navigationBarColor: colors.browserNavigationBar, + }, }, }, - }, { - logger: browserLogger, - }); + { + logger: browserLogger, + }, + ); return () => { isMounted = false; @@ -164,7 +168,7 @@ export default function App() { name="Home" options={{ title: 'PingIdentity Demo', headerShown: false }} > - {(props) => ( + {props => ( - {(props) => ( + {props => ( - - {(props) => ( + + {props => ( /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; - E066FA01A78FCF682DD07933 /* [CP] Check Pods Manifest.lock */ = { - isa = PBXShellScriptBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - inputFileListPaths = ( - ); - inputPaths = ( - "${PODS_PODFILE_DIR_PATH}/Podfile.lock", - "${PODS_ROOT}/Manifest.lock", - ); - name = "[CP] Check Pods Manifest.lock"; - outputFileListPaths = ( - ); - outputPaths = ( - "$(DERIVED_FILE_DIR)/Pods-PingSampleApp-RNPackagesTests-checkManifestLockResult.txt", - ); - runOnlyForDeploymentPostprocessing = 0; - shellPath = /bin/sh; - shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; - showEnvVarsInLog = 0; - }; E235C05ADACE081382539298 /* [CP] Copy Pods Resources */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -398,24 +267,8 @@ ); runOnlyForDeploymentPostprocessing = 0; }; - 3466ED8E2F5B937B00E6A758 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - 58D1E2442F9AA3D900A9C001 /* RNPackagesTests.swift in Sources */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; /* End PBXSourcesBuildPhase section */ -/* Begin PBXTargetDependency section */ - 3466ED972F5B937B00E6A758 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 13B07F861A680F5B00A75B9A /* PingSampleApp */; - targetProxy = 3466ED962F5B937B00E6A758 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - /* Begin XCBuildConfiguration section */ 13B07F941A680F5B00A75B9A /* Debug */ = { isa = XCBuildConfiguration; @@ -471,79 +324,6 @@ }; name = Release; }; - 3466ED982F5B937B00E6A758 /* Debug */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = C2B771211279C46F8CE60D23 /* Pods-PingSampleApp-RNPackagesTests.debug.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = dwarf; - DEVELOPMENT_TEAM = QXX7WN94SM; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu17; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 1.0; - MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.pingidentity.RNPackagesTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRING_CATALOG_GENERATE_SYMBOLS = NO; - SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_OPTIMIZATION_LEVEL = "-Onone"; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PingSampleApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/PingSampleApp"; - }; - name = Debug; - }; - 3466ED992F5B937B00E6A758 /* Release */ = { - isa = XCBuildConfiguration; - baseConfigurationReference = 95DFD7557CF7911438C8069E /* Pods-PingSampleApp-RNPackagesTests.release.xcconfig */; - buildSettings = { - ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; - BUNDLE_LOADER = "$(TEST_HOST)"; - CLANG_ANALYZER_NONNULL = YES; - CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; - CLANG_ENABLE_OBJC_WEAK = YES; - CLANG_WARN_DOCUMENTATION_COMMENTS = YES; - CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; - CODE_SIGN_STYLE = Automatic; - COPY_PHASE_STRIP = NO; - CURRENT_PROJECT_VERSION = 1; - DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; - DEVELOPMENT_TEAM = QXX7WN94SM; - ENABLE_USER_SCRIPT_SANDBOXING = NO; - GCC_C_LANGUAGE_STANDARD = gnu17; - GENERATE_INFOPLIST_FILE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 16.0; - LOCALIZATION_PREFERS_STRING_CATALOGS = YES; - MARKETING_VERSION = 1.0; - MTL_FAST_MATH = YES; - PRODUCT_BUNDLE_IDENTIFIER = com.pingidentity.RNPackagesTests; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRING_CATALOG_GENERATE_SYMBOLS = NO; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_COMPILATION_MODE = wholemodule; - SWIFT_EMIT_LOC_STRINGS = NO; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - TEST_HOST = "$(BUILT_PRODUCTS_DIR)/PingSampleApp.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/PingSampleApp"; - }; - name = Release; - }; 83CBBA201A601CBA00E9B192 /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -730,15 +510,6 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; - 3466ED9A2F5B937B00E6A758 /* Build configuration list for PBXNativeTarget "RNPackagesTests" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 3466ED982F5B937B00E6A758 /* Debug */, - 3466ED992F5B937B00E6A758 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; 83CBB9FA1A601CBA00E9B192 /* Build configuration list for PBXProject "PingSampleApp" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/PingSampleApp/ios/Podfile b/PingSampleApp/ios/Podfile index 456855514..2c6b706ed 100644 --- a/PingSampleApp/ios/Podfile +++ b/PingSampleApp/ios/Podfile @@ -68,25 +68,12 @@ target 'PingSampleApp' do pod 'PingOidc', :git => ping_ios_sdk_git, :branch => ping_ios_sdk_branch - pod 'RNPingCore', :path => '../../packages/core' - pod 'RNPingJourney', - :path => '../../node_modules/@ping-identity/rn-journey', - :testspecs => ['Tests'] - pod 'RNPingOidc', - :path => '../../node_modules/@ping-identity/rn-oidc', - :testspecs => ['Tests'] + pod 'RNPingCore', :path => '../../packages/core' + pod 'RNPingJourney', + :path => '../../node_modules/@ping-identity/rn-journey' + pod 'RNPingOidc', + :path => '../../node_modules/@ping-identity/rn-oidc' - target 'RNPackagesTests' do - inherit! :complete - - # Test specs for each package - using same paths as autolinking - pod 'RNPingCore', :path => '../../packages/core', :testspecs => ['Tests'] - pod 'RNPingDeviceId', :path => '../../node_modules/@ping-identity/rn-device-id', :testspecs => ['Tests'] - pod 'RNPingLogger', :path => '../../node_modules/@ping-identity/rn-logger', :testspecs => ['Tests'] - pod 'RNPingOidc', :path => '../../node_modules/@ping-identity/rn-oidc', :testspecs => ['Tests'] - pod 'RNPingStorage', :path => '../../node_modules/@ping-identity/rn-storage', :testspecs => ['Tests'] - end - post_install do |installer| # https://github.com/facebook/react-native/blob/main/packages/react-native/scripts/react_native_pods.rb#L197-L202 react_native_post_install( diff --git a/PingSampleApp/ios/Podfile.lock b/PingSampleApp/ios/Podfile.lock index d34be97c5..d1fd17917 100644 --- a/PingSampleApp/ios/Podfile.lock +++ b/PingSampleApp/ios/Podfile.lock @@ -2364,35 +2364,6 @@ PODS: - ReactCommon/turbomodule/core - SocketRocket - Yoga - - RNPingCore/Tests (0.1.0): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - SocketRocket - - Yoga - RNPingDeviceId (0.1.0): - boost - DoubleConversion @@ -2424,37 +2395,6 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingDeviceId/Tests (0.1.0): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - PingDeviceId - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - RNPingCore - - SocketRocket - - Yoga - RNPingDeviceProfile (0.1.0): - boost - DoubleConversion @@ -2518,38 +2458,6 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingJourney/Tests (0.1.0): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - PingJourney - - PingJourneyPlugin - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - RNPingCore - - SocketRocket - - Yoga - RNPingLogger (0.1.0): - boost - DoubleConversion @@ -2581,37 +2489,6 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingLogger/Tests (0.1.0): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - PingLogger - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - RNPingCore - - SocketRocket - - Yoga - RNPingOidc (0.1.0): - boost - DoubleConversion @@ -2643,37 +2520,6 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingOidc/Tests (0.1.0): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - PingOidc (= 1.3.1) - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - RNPingCore - - SocketRocket - - Yoga - RNPingStorage (0.1.0): - boost - DoubleConversion @@ -2705,37 +2551,6 @@ PODS: - RNPingCore - SocketRocket - Yoga - - RNPingStorage/Tests (0.1.0): - - boost - - DoubleConversion - - fast_float - - fmt - - glog - - hermes-engine - - PingStorage - - RCT-Folly - - RCT-Folly/Fabric - - RCTRequired - - RCTTypeSafety - - React-Core - - React-debug - - React-Fabric - - React-featureflags - - React-graphics - - React-hermes - - React-ImageManager - - React-jsi - - React-NativeModulesApple - - React-RCTFabric - - React-renderercss - - React-rendererdebug - - React-utils - - ReactCodegen - - ReactCommon/turbomodule/bridging - - ReactCommon/turbomodule/core - - RNPingCore - - SocketRocket - - Yoga - RNScreens (4.18.0): - boost - DoubleConversion @@ -2919,18 +2734,12 @@ DEPENDENCIES: - "RNCAsyncStorage (from `../../node_modules/@react-native-async-storage/async-storage`)" - "RNPingBrowser (from `../../node_modules/@ping-identity/rn-browser`)" - RNPingCore (from `../../packages/core`) - - RNPingCore/Tests (from `../../packages/core`) - "RNPingDeviceId (from `../../node_modules/@ping-identity/rn-device-id`)" - - "RNPingDeviceId/Tests (from `../../node_modules/@ping-identity/rn-device-id`)" - "RNPingDeviceProfile (from `../../node_modules/@ping-identity/rn-device-profile`)" - "RNPingJourney (from `../../node_modules/@ping-identity/rn-journey`)" - - "RNPingJourney/Tests (from `../../node_modules/@ping-identity/rn-journey`)" - "RNPingLogger (from `../../node_modules/@ping-identity/rn-logger`)" - - "RNPingLogger/Tests (from `../../node_modules/@ping-identity/rn-logger`)" - "RNPingOidc (from `../../node_modules/@ping-identity/rn-oidc`)" - - "RNPingOidc/Tests (from `../../node_modules/@ping-identity/rn-oidc`)" - "RNPingStorage (from `../../node_modules/@ping-identity/rn-storage`)" - - "RNPingStorage/Tests (from `../../node_modules/@ping-identity/rn-storage`)" - RNScreens (from `../../node_modules/react-native-screens`) - RNVectorIcons (from `../../node_modules/react-native-vector-icons`) - SocketRocket (~> 0.7.1) @@ -3287,6 +3096,6 @@ SPEC CHECKSUMS: SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748 Yoga: 703055a9f39562521cdb8657162dfd80f8c174c3 -PODFILE CHECKSUM: 613aba9f37bfa99dda43d9597da49cd313641ae2 +PODFILE CHECKSUM: 75ad62a53ebca2cac224cdae8a311d87aefae9af COCOAPODS: 1.15.2 diff --git a/PingSampleApp/metro.config.js b/PingSampleApp/metro.config.js index 2c6ff673b..86c5ced5f 100644 --- a/PingSampleApp/metro.config.js +++ b/PingSampleApp/metro.config.js @@ -13,7 +13,10 @@ const oidcPackage = path.resolve(__dirname, '../packages/oidc'); const journeyPackage = path.resolve(__dirname, '../packages/journey'); const browserPackage = path.resolve(__dirname, '../packages/browser'); const deviceIdPackage = path.resolve(__dirname, '../packages/device-id'); -const deviceProfilePackage = path.resolve(__dirname, '../packages/device-profile'); +const deviceProfilePackage = path.resolve( + __dirname, + '../packages/device-profile', +); const storagePackage = path.resolve(__dirname, '../packages/storage'); const corePackage = path.resolve(__dirname, '../packages/core'); const loggerPackage = path.resolve(__dirname, '../packages/logger'); diff --git a/PingSampleApp/package.json b/PingSampleApp/package.json index dbcb90790..bb053c3b9 100644 --- a/PingSampleApp/package.json +++ b/PingSampleApp/package.json @@ -5,7 +5,7 @@ "scripts": { "android": "react-native run-android", "ios": "react-native run-ios", - "lint": "eslint --no-config-lookup --config ../eslint.config.mjs \"src/**/*.{js,jsx,ts,tsx}\" \"ui/**/*.{js,jsx,ts,tsx}\" \"__tests__/**/*.{js,jsx,ts,tsx}\" \"*.{js,jsx,ts,tsx}\"", + "lint": "ESLINT_USE_FLAT_CONFIG=false eslint \"src/**/*.{js,jsx,ts,tsx}\" \"ui/**/*.{js,jsx,ts,tsx}\" \"__tests__/**/*.{js,jsx,ts,tsx}\" \"*.{js,jsx,ts,tsx}\"", "start": "react-native start", "clean-install": "cd android && rm -rf .gradle app/build && cd .. && cd ios && rm -rf Pods build && cd .. && rm -rf build && bundle install --path vendor/bundle && bundle exec pod install --repo-update --project-directory=ios", "codegen": "npx react-native codegen", diff --git a/PingSampleApp/src/clients.ts b/PingSampleApp/src/clients.ts index a8dc2b19b..531604023 100644 --- a/PingSampleApp/src/clients.ts +++ b/PingSampleApp/src/clients.ts @@ -27,7 +27,7 @@ import { */ const journeyScopes = (Config.JOURNEY_SCOPES ?? '') .split(',') - .map((item) => item.trim()) + .map(item => item.trim()) .filter(Boolean); /** @@ -35,7 +35,7 @@ const journeyScopes = (Config.JOURNEY_SCOPES ?? '') */ const aicScopes = (Config.AIC_SCOPES ?? '') .split(',') - .map((item) => item.trim()) + .map(item => item.trim()) .filter(Boolean); /** @@ -43,7 +43,7 @@ const aicScopes = (Config.AIC_SCOPES ?? '') */ const pingOneScopes = (Config.PINGONE_SCOPES ?? '') .split(',') - .map((item) => item.trim()) + .map(item => item.trim()) .filter(Boolean); /** @@ -118,7 +118,8 @@ export const journeyOidcClient = createOidcClient({ /** * OIDC web client derived from Journey environment settings. */ -export const journeyOidcWebClient: OidcWebClient = createOidcWebClient(journeyOidcClient); +export const journeyOidcWebClient: OidcWebClient = + createOidcWebClient(journeyOidcClient); /** * OIDC client configuration used to create the sample web-capable OIDC client. @@ -197,7 +198,8 @@ const pingOneOidcClient = createOidcClient({ * This wrapper exposes browser-driven authorization methods while reusing the * same underlying OIDC configuration and token storage behavior. */ -export const sampleOidcWebClient: OidcWebClient = createOidcWebClient(sampleOidcClient); +export const sampleOidcWebClient: OidcWebClient = + createOidcWebClient(sampleOidcClient); /** * Web-capable OIDC client for the PingOne test tenant profile. @@ -352,4 +354,5 @@ export const sampleAppClientProfiles: readonly SampleAppClientProfile[] = [ /** * Default selected profile key for sample app boot. */ -export const DEFAULT_SAMPLE_APP_CLIENT_PROFILE_KEY = sampleAppClientProfiles[0].key; +export const DEFAULT_SAMPLE_APP_CLIENT_PROFILE_KEY = + sampleAppClientProfiles[0].key; diff --git a/PingSampleApp/src/styles/common.ts b/PingSampleApp/src/styles/common.ts index 45637b8d6..b8da4344c 100644 --- a/PingSampleApp/src/styles/common.ts +++ b/PingSampleApp/src/styles/common.ts @@ -409,7 +409,7 @@ export const commonStyles = StyleSheet.create({ fontSize: 14, lineHeight: 20, }, - deviceIdText:{ + deviceIdText: { marginTop: 3, fontSize: 11, color: colors.homeFooterText, @@ -631,7 +631,6 @@ export const commonStyles = StyleSheet.create({ marginBottom: 14, }, - suspendedBox: { borderRadius: 10, padding: 12, diff --git a/PingSampleApp/src/styles/typography.ts b/PingSampleApp/src/styles/typography.ts index 3c374c4d5..fe6a01d38 100644 --- a/PingSampleApp/src/styles/typography.ts +++ b/PingSampleApp/src/styles/typography.ts @@ -9,4 +9,4 @@ export const typography = { title: 22, body: 16, small: 14, -}; \ No newline at end of file +}; diff --git a/PingSampleApp/src/tokenStorages.ts b/PingSampleApp/src/tokenStorages.ts index 6cd4ab268..5b63d630c 100644 --- a/PingSampleApp/src/tokenStorages.ts +++ b/PingSampleApp/src/tokenStorages.ts @@ -9,8 +9,8 @@ import { configureOidcStorage, configureSessionStorage, type StorageConfig, -} from "@ping-identity/rn-storage"; -import { logger } from "@ping-identity/rn-logger"; +} from '@ping-identity/rn-storage'; +import { logger } from '@ping-identity/rn-logger'; /** * Information about a registered storage instance. @@ -23,7 +23,7 @@ export type StorageInfo = { let oidcStorage: StorageInfo | null = null; let sessionStorage: StorageInfo | null = null; -const storageLogger = logger({ level: "debug" }); +const storageLogger = logger({ level: 'debug' }); /** * Configures and returns the OIDC storage instance. @@ -35,12 +35,12 @@ export function configureOidcStorageInfo(): StorageInfo { if (!oidcStorage) { const baseConfig: StorageConfig = { android: { - keyAlias: "ping.oidc", - fileName: "ping_oidc_tokens", + keyAlias: 'ping.oidc', + fileName: 'ping_oidc_tokens', cacheStrategy: CacheStrategy.NO_CACHE, }, ios: { - account: "com.pingidentity.rnsampleapp.oidc", + account: 'com.pingidentity.rnsampleapp.oidc', encryptor: true, cacheable: false, }, @@ -49,7 +49,7 @@ export function configureOidcStorageInfo(): StorageInfo { logger: storageLogger, }); oidcStorage = { config: resolvedConfig }; - console.log("Created OIDC Storage:", resolvedConfig); + console.log('Created OIDC Storage:', resolvedConfig); } return oidcStorage; } @@ -64,12 +64,12 @@ export function configureSessionStorageInfo(): StorageInfo { if (!sessionStorage) { const baseConfig: StorageConfig = { android: { - keyAlias: "ping.session", - fileName: "ping_session_store", + keyAlias: 'ping.session', + fileName: 'ping_session_store', cacheStrategy: CacheStrategy.NO_CACHE, }, ios: { - account: "com.pingidentity.rnsampleapp.session", + account: 'com.pingidentity.rnsampleapp.session', encryptor: true, cacheable: false, }, @@ -78,7 +78,7 @@ export function configureSessionStorageInfo(): StorageInfo { logger: storageLogger, }); sessionStorage = { config: resolvedConfig }; - console.log("Created Session Storage:", resolvedConfig); + console.log('Created Session Storage:', resolvedConfig); } return sessionStorage; } @@ -88,6 +88,9 @@ export function configureSessionStorageInfo(): StorageInfo { * * @returns An object containing both storage instances (may be null if not yet configured) */ -export function getTokenStorages(): { oidcStorage: StorageInfo | null; sessionStorage: StorageInfo | null } { +export function getTokenStorages(): { + oidcStorage: StorageInfo | null; + sessionStorage: StorageInfo | null; +} { return { oidcStorage, sessionStorage }; } diff --git a/PingSampleApp/ui/BrowserScreen.tsx b/PingSampleApp/ui/BrowserScreen.tsx index f06796d50..cf882b926 100644 --- a/PingSampleApp/ui/BrowserScreen.tsx +++ b/PingSampleApp/ui/BrowserScreen.tsx @@ -6,12 +6,7 @@ */ import React, { useMemo, useState } from 'react'; -import { - View, - Text, - TouchableOpacity, - ScrollView, -} from 'react-native'; +import { View, Text, TouchableOpacity, ScrollView } from 'react-native'; import type { BrowserError } from '@ping-identity/rn-browser'; import { open } from '@ping-identity/rn-browser'; import { logger } from '@ping-identity/rn-logger'; @@ -24,12 +19,9 @@ import PayloadViewer from './components/atoms/PayloadViewer'; */ export default function BrowserScreen() { const browserLogger = useMemo(() => logger({ level: 'debug' }), []); - const [url, setUrl] = useState( - 'https://www.pingidentity.com', - ); - const [callbackUrlScheme, setCallbackUrlScheme] = useState( - 'org.forgerock.demo', - ); + const [url, setUrl] = useState('https://www.pingidentity.com'); + const [callbackUrlScheme, setCallbackUrlScheme] = + useState('org.forgerock.demo'); const [redirectUri, setRedirectUri] = useState( 'org.forgerock.demo://oauth2redirect', ); @@ -46,16 +38,20 @@ export default function BrowserScreen() { setResult(''); try { - const response = await open(overrideUrl ?? url, { - callbackUrlScheme, - redirectUri: redirectUri.trim() ? redirectUri : undefined, - ios: { - browserMode: 'login', - browserType: 'ephemeralAuthSession' - } - }, { - logger: browserLogger, - }); + const response = await open( + overrideUrl ?? url, + { + callbackUrlScheme, + redirectUri: redirectUri.trim() ? redirectUri : undefined, + ios: { + browserMode: 'login', + browserType: 'ephemeralAuthSession', + }, + }, + { + logger: browserLogger, + }, + ); setResult(JSON.stringify(response, null, 2)); } catch (e: unknown) { const errorPayload = e as BrowserError; @@ -65,9 +61,11 @@ export default function BrowserScreen() { } }; - return ( - + Browser diff --git a/PingSampleApp/ui/ConfigurationScreen.tsx b/PingSampleApp/ui/ConfigurationScreen.tsx index c917fa8ef..33e11c779 100644 --- a/PingSampleApp/ui/ConfigurationScreen.tsx +++ b/PingSampleApp/ui/ConfigurationScreen.tsx @@ -27,7 +27,7 @@ type Props = NativeStackScreenProps & { * @returns Profile map keyed by section title. */ function groupProfilesBySection( - profiles: readonly SampleAppClientProfile[] + profiles: readonly SampleAppClientProfile[], ): ReadonlyMap { const grouped = new Map(); @@ -54,13 +54,19 @@ export default function ConfigurationScreen(props: Props): React.ReactElement { const groupedProfiles = useMemo( () => groupProfilesBySection(profiles), - [profiles] + [profiles], ); - const selectedProfile = useMemo( - () => profiles.find((profile) => profile.key === selectedProfileKey), - [profiles, selectedProfileKey] - ); + // NOTE: `selectedProfile` was removed because it triggered an ESLint error: + // @typescript-eslint/no-unused-vars — 'selectedProfile' is assigned a value but never used. + // The variable was computed but never referenced in the JSX. + // It seems selectedProfileKey is sufficient for determining which profile is selected when rendering the list, so selectedProfile was not necessary. + // If we need to display additional details about the selected profile in the future, we can consider reintroducing it at that time. + // + // const selectedProfile = useMemo( + // () => profiles.find((profile) => profile.key === selectedProfileKey), + // [profiles, selectedProfileKey] + // ); return ( {group} - {items.map((profile) => { + {items.map(profile => { const selected = profile.key === selectedProfileKey; const iconName = selected ? 'check' : 'check-box-outline-blank'; return ( @@ -86,9 +92,15 @@ export default function ConfigurationScreen(props: Props): React.ReactElement { accessibilityState={{ selected }} > - {profile.name} - {profile.host} - {profile.environment} + + {profile.name} + + + {profile.host} + + + {profile.environment} + = { * Displays a simple device profile collector demo. */ export default function DeviceProfileScreen(): React.ReactElement { + const deviceProfileLogger = useMemo(() => logger({ level: 'debug' }), []); const [collectorSelections, setCollectorSelections] = useState< Record >(defaultCollectorSelections); @@ -48,7 +50,7 @@ export default function DeviceProfileScreen(): React.ReactElement { const availableCollectors = useMemo( () => Object.keys(defaultCollectorSelections) as DeviceProfileCollector[], - [] + [], ); const toggleCollector = (collector: DeviceProfileCollector): void => { @@ -60,7 +62,7 @@ export default function DeviceProfileScreen(): React.ReactElement { const onCollect = async (): Promise => { const collectors = availableCollectors.filter( - collector => collectorSelections[collector] + collector => collectorSelections[collector], ); if (collectors.length === 0) { @@ -74,7 +76,9 @@ export default function DeviceProfileScreen(): React.ReactElement { setProfile(null); try { - const collectedProfile = await collectDeviceProfile(collectors); + const collectedProfile = await collectDeviceProfile(collectors, { + logger: deviceProfileLogger, + }); setProfile(collectedProfile); } catch (err: unknown) { const errorPayload = err as DeviceProfileError; @@ -93,7 +97,10 @@ export default function DeviceProfileScreen(): React.ReactElement { }; return ( - + Device Profile diff --git a/PingSampleApp/ui/HomeScreen.tsx b/PingSampleApp/ui/HomeScreen.tsx index 08a7cafa9..80dfb5452 100644 --- a/PingSampleApp/ui/HomeScreen.tsx +++ b/PingSampleApp/ui/HomeScreen.tsx @@ -12,7 +12,9 @@ import { RootStackParamList } from '../App'; import { getDeviceId } from '@ping-identity/rn-device-id'; import MaterialIcon from 'react-native-vector-icons/MaterialIcons'; import { colors } from '../src/styles/colors'; -import HomeMenuRow, { type HomeMenuItem } from './components/molecules/HomeMenuRow'; +import HomeMenuRow, { + type HomeMenuItem, +} from './components/molecules/HomeMenuRow'; type HomeScreenNavProp = NativeStackNavigationProp; type Props = { @@ -39,10 +41,13 @@ function formatDeviceIdError(error: unknown): string { /** * Home screen with entry points for each SDK demo flow. */ -export default function HomeScreen({ navigation, selectedConfigName }: Props) { +export default function HomeScreen({ + navigation, + selectedConfigName: _selectedConfigName, +}: Props) { const [deviceId, setDeviceId] = useState(null); const [deviceIdError, setDeviceIdError] = useState(null); - const runtime = global as { + const runtime = globalThis as { RN$Bridgeless?: boolean; nativeFabricUIManager?: unknown; }; @@ -135,7 +140,9 @@ export default function HomeScreen({ navigation, selectedConfigName }: Props) { icon={item.icon} comingSoon={item.comingSoon} disabled={isDisabled} - onPress={() => navigation.navigate(item.screen as keyof RootStackParamList)} + onPress={() => + navigation.navigate(item.screen as keyof RootStackParamList) + } /> ); }; @@ -168,7 +175,9 @@ export default function HomeScreen({ navigation, selectedConfigName }: Props) { source={require('../assets/ping-logo.jpg')} style={commonStyles.homeHeaderLogo} /> - React Native Sample App + + React Native Sample App + Version 1.0 @@ -198,7 +207,11 @@ export default function HomeScreen({ navigation, selectedConfigName }: Props) { - + Device ID {!deviceIdError ? ( Secured @@ -206,7 +219,9 @@ export default function HomeScreen({ navigation, selectedConfigName }: Props) { {deviceIdError ? ( - {deviceIdError} + + {deviceIdError} + ) : ( {deviceId ?? 'Loading...'} @@ -219,7 +234,9 @@ export default function HomeScreen({ navigation, selectedConfigName }: Props) { ) : null} - React Native Unified SDK + + React Native Unified SDK + {`New Arch: ${isNewArchEnabled ? 'Enabled' : 'Disabled'}`} diff --git a/PingSampleApp/ui/JourneyFormMinimalScreen.tsx b/PingSampleApp/ui/JourneyFormMinimalScreen.tsx index d162fcc06..d8fa31c34 100644 --- a/PingSampleApp/ui/JourneyFormMinimalScreen.tsx +++ b/PingSampleApp/ui/JourneyFormMinimalScreen.tsx @@ -6,13 +6,7 @@ */ import React, { useState } from 'react'; -import { - Alert, - ScrollView, - Text, - TouchableOpacity, - View, -} from 'react-native'; +import { Alert, ScrollView, Text, TouchableOpacity, View } from 'react-native'; import { callbackType } from '@ping-identity/rn-types'; import { useJourney, useJourneyForm } from '@ping-identity/rn-journey'; import { commonStyles } from '../src/styles/common'; @@ -40,7 +34,10 @@ export default function JourneyFormMinimalScreen(): React.ReactElement { const continueJourney = async (): Promise => { if (!form.canSubmit) { - Alert.alert('Cannot continue', form.issues[0]?.message ?? 'Resolve callback issues first.'); + Alert.alert( + 'Cannot continue', + form.issues[0]?.message ?? 'Resolve callback issues first.', + ); return; } await actions.next(form.input); @@ -51,13 +48,21 @@ export default function JourneyFormMinimalScreen(): React.ReactElement { const passwordFields = form.getFieldsByType(callbackType.PasswordCallback); const passwordField = passwordFields[0]; - const didSetName = form.setValueByType(callbackType.NameCallback, 'demo-user'); - const didSetPassword = form.setValueByType(callbackType.PasswordCallback, 'demo-password'); + const didSetName = form.setValueByType( + callbackType.NameCallback, + 'demo-user', + ); + const didSetPassword = form.setValueByType( + callbackType.PasswordCallback, + 'demo-password', + ); if (!didSetName || !didSetPassword) { Alert.alert( 'Demo credentials skipped', - `Name field: ${nameField ? 'found' : 'missing'}, Password field: ${passwordField ? 'found' : 'missing'}` + `Name field: ${nameField ? 'found' : 'missing'}, Password field: ${ + passwordField ? 'found' : 'missing' + }`, ); } }; @@ -65,7 +70,9 @@ export default function JourneyFormMinimalScreen(): React.ReactElement { return ( - Journey (minimal useJourneyForm) + + Journey (minimal useJourneyForm) + startJourney().catch((cause) => Alert.alert('Start failed', String(cause)))} + onPress={() => + startJourney().catch(cause => + Alert.alert('Start failed', String(cause)), + ) + } disabled={actions.loading} > @@ -86,7 +97,7 @@ export default function JourneyFormMinimalScreen(): React.ReactElement { {node?.type === 'ContinueNode' ? ( <> - {form.fields.map((field) => ( + {form.fields.map(field => ( 0 ? ( - {form.issues[0]?.message} + + {form.issues[0]?.message} + ) : null} {form.meta.hasManual ? ( @@ -106,11 +119,17 @@ export default function JourneyFormMinimalScreen(): React.ReactElement { onPress={applyDemoCredentials} disabled={actions.loading} > - Apply Demo Credentials + + Apply Demo Credentials + continueJourney().catch((cause) => Alert.alert('Continue failed', String(cause)))} + onPress={() => + continueJourney().catch(cause => + Alert.alert('Continue failed', String(cause)), + ) + } disabled={actions.loading} > Continue @@ -129,7 +148,11 @@ export default function JourneyFormMinimalScreen(): React.ReactElement { Success actions.logoutUser().catch((cause) => Alert.alert('Logout failed', String(cause)))} + onPress={() => + actions + .logoutUser() + .catch(cause => Alert.alert('Logout failed', String(cause))) + } > Logout @@ -137,7 +160,9 @@ export default function JourneyFormMinimalScreen(): React.ReactElement { ) : null} {node?.type === 'ErrorNode' || node?.type === 'FailureNode' ? ( - {node.message || node.cause || 'Journey failed'} + + {node.message || node.cause || 'Journey failed'} + ) : null} {actions.error ? ( diff --git a/PingSampleApp/ui/JourneyFullScreen.tsx b/PingSampleApp/ui/JourneyFullScreen.tsx index 6cabd22d7..52bfa2fe7 100644 --- a/PingSampleApp/ui/JourneyFullScreen.tsx +++ b/PingSampleApp/ui/JourneyFullScreen.tsx @@ -22,7 +22,10 @@ import { type JourneyNextInput, useJourney, } from '@ping-identity/rn-journey'; -import { callbackType, nativeExtensionCallbackType } from '@ping-identity/rn-types'; +import { + callbackType, + nativeExtensionCallbackType, +} from '@ping-identity/rn-types'; import { commonStyles } from '../src/styles/common'; import { journeyFullScreenStyles as styles } from '../src/styles/journeyStyles'; import PingTextInput from './components/atoms/PingTextInput'; @@ -75,13 +78,15 @@ const integrationRequiredCallbackTypes = new Set([ * @param callbacks - Callback collection from a ContinueNode. * @returns Indexed callback entries. */ -function indexCallbacks(callbacks: JourneyCallback[] | undefined): IndexedCallback[] { +function indexCallbacks( + callbacks: JourneyCallback[] | undefined, +): IndexedCallback[] { if (!callbacks || callbacks.length === 0) { return []; } const counts = new Map(); - return callbacks.map((callback) => { + return callbacks.map(callback => { const type = callback.type; const typeIndex = counts.get(type) ?? 0; counts.set(type, typeIndex + 1); @@ -104,8 +109,8 @@ function readOptions(callback: JourneyCallback): string[] { const candidate = Array.isArray(callback.options) ? callback.options : Array.isArray(callback.choices) - ? callback.choices - : []; + ? callback.choices + : []; return candidate.map((item, index) => { if (typeof item === 'string') { @@ -134,12 +139,12 @@ function readOptions(callback: JourneyCallback): string[] { */ function buildManualNextInput( callbacks: IndexedCallback[], - values: Record + values: Record, ): { input: JourneyNextInput; issues: string[] } { const mutations: JourneyCallbackInput[] = []; const issues: string[] = []; - callbacks.forEach((entry) => { + callbacks.forEach(entry => { const { key, type, typeIndex, callback } = entry; if (outputOnlyCallbackTypes.has(type)) { @@ -179,8 +184,8 @@ function buildManualNextInput( typeof rawValue === 'number' ? rawValue : typeof rawValue === 'string' - ? Number(rawValue) - : Number.NaN; + ? Number(rawValue) + : Number.NaN; if (!Number.isFinite(numericValue)) { issues.push(`${type} requires a numeric value.`); return; @@ -193,13 +198,16 @@ function buildManualNextInput( return; } - if (type === callbackType.ChoiceCallback || type === callbackType.ConfirmationCallback) { + if ( + type === callbackType.ChoiceCallback || + type === callbackType.ConfirmationCallback + ) { const selectedIndex = typeof rawValue === 'number' ? rawValue : typeof rawValue === 'string' - ? Number(rawValue) - : Number.NaN; + ? Number(rawValue) + : Number.NaN; if (!Number.isFinite(selectedIndex)) { issues.push(`${type} requires selecting one option.`); return; @@ -222,7 +230,10 @@ function buildManualNextInput( allowUserDefinedQuestions: false, }; - if (!kbaValue.selectedQuestion.trim() || !kbaValue.selectedAnswer.trim()) { + if ( + !kbaValue.selectedQuestion.trim() || + !kbaValue.selectedAnswer.trim() + ) { issues.push(`${type} requires both question and answer.`); return; } @@ -233,7 +244,9 @@ function buildManualNextInput( value: { selectedQuestion: kbaValue.selectedQuestion, selectedAnswer: kbaValue.selectedAnswer, - allowUserDefinedQuestions: Boolean(kbaValue.allowUserDefinedQuestions), + allowUserDefinedQuestions: Boolean( + kbaValue.allowUserDefinedQuestions, + ), }, }); return; @@ -275,20 +288,23 @@ function callbackLabel(callback: JourneyCallback): string { * @returns Journey full screen element. */ export default function JourneyFullScreen(): React.ReactElement { - const [node, { start, next, resume, user, logoutUser, loading, error }] = useJourney(); + const [node, { start, next, resume, user, logoutUser, loading, error }] = + useJourney(); const [journeyName, setJourneyName] = useState('Login'); const [resumeUrl, setResumeUrl] = useState(''); const [sessionPayload, setSessionPayload] = useState(null); - const [values, setValues] = useState>({}); + const [values, setValues] = useState< + Record + >({}); const [issues, setIssues] = useState([]); const indexedCallbacks = useMemo( () => (node?.type === 'ContinueNode' ? indexCallbacks(node.callbacks) : []), - [node] + [node], ); const setField = useCallback((key: string, value: JourneyFormValue): void => { - setValues((previous) => ({ + setValues(previous => ({ ...previous, [key]: value, })); @@ -395,7 +411,8 @@ export default function JourneyFullScreen(): React.ReactElement { {type} - Requires additional integration module. Submit from this screen is blocked. + Requires additional integration module. Submit from this screen is + blocked. ); @@ -416,14 +433,17 @@ export default function JourneyFullScreen(): React.ReactElement { setField(key, value)} + onValueChange={value => setField(key, value)} /> ); } - if (type === callbackType.ChoiceCallback || type === callbackType.ConfirmationCallback) { + if ( + type === callbackType.ChoiceCallback || + type === callbackType.ConfirmationCallback + ) { const options = readOptions(callback); return ( @@ -434,7 +454,10 @@ export default function JourneyFullScreen(): React.ReactElement { return ( setField(key, optionIndex)} > + onChangeText={text => setField(key, { ...value, selectedQuestion: text, @@ -481,7 +504,7 @@ export default function JourneyFullScreen(): React.ReactElement { label="Security answer" placeholder="Security answer" value={value.selectedAnswer} - onChangeText={(text) => + onChangeText={text => setField(key, { ...value, selectedAnswer: text, @@ -492,7 +515,7 @@ export default function JourneyFullScreen(): React.ReactElement { Allow custom questions + onValueChange={enabled => setField(key, { ...value, allowUserDefinedQuestions: enabled, @@ -505,15 +528,18 @@ export default function JourneyFullScreen(): React.ReactElement { } const secureTextEntry = - type === callbackType.PasswordCallback || type === callbackType.ValidatedCreatePasswordCallback; + type === callbackType.PasswordCallback || + type === callbackType.ValidatedCreatePasswordCallback; const keyboardType = - type === callbackType.NumberAttributeInputCallback ? 'numeric' : 'default'; + type === callbackType.NumberAttributeInputCallback + ? 'numeric' + : 'default'; const textValue = typeof currentValue === 'string' ? currentValue : typeof callback.value === 'string' - ? callback.value - : ''; + ? callback.value + : ''; return ( @@ -525,18 +551,21 @@ export default function JourneyFullScreen(): React.ReactElement { secureTextEntry={secureTextEntry} allowPasswordToggle={secureTextEntry} keyboardType={keyboardType} - onChangeText={(text) => setField(key, text)} + onChangeText={text => setField(key, text)} autoCapitalize="none" placeholder={label} /> ); }, - [setField, values] + [setField, values], ); return ( - + Journey (useJourney only) diff --git a/PingSampleApp/ui/JourneyRouteScreen.tsx b/PingSampleApp/ui/JourneyRouteScreen.tsx index 71a2e9737..610612925 100644 --- a/PingSampleApp/ui/JourneyRouteScreen.tsx +++ b/PingSampleApp/ui/JourneyRouteScreen.tsx @@ -38,7 +38,7 @@ export default function JourneyRouteScreen(props: Props): React.ReactElement { const usedTestJourneySet = useMemo>( () => new Set(usedTestJourneys), - [usedTestJourneys] + [usedTestJourneys], ); useEffect(() => { @@ -49,8 +49,12 @@ export default function JourneyRouteScreen(props: Props): React.ReactElement { AsyncStorage.getItem(USED_TEST_JOURNEYS_STORAGE_KEY), ]); - const recentParsed = recentStored ? (JSON.parse(recentStored) as string[]) : []; - const usedParsed = usedStored ? (JSON.parse(usedStored) as string[]) : []; + const recentParsed = recentStored + ? (JSON.parse(recentStored) as string[]) + : []; + const usedParsed = usedStored + ? (JSON.parse(usedStored) as string[]) + : []; setRecentJourneys(buildRecentJourneySuggestions(recentParsed)); setUsedTestJourneys(buildUsedTestJourneys(usedParsed)); @@ -62,11 +66,14 @@ export default function JourneyRouteScreen(props: Props): React.ReactElement { const saveRecentJourney = useCallback( async (name: string): Promise => { - const updated = [name, ...recentJourneys.filter((item) => item !== name)]; + const updated = [name, ...recentJourneys.filter(item => item !== name)]; setRecentJourneys(updated); - await AsyncStorage.setItem(RECENT_JOURNEYS_STORAGE_KEY, JSON.stringify(updated)); + await AsyncStorage.setItem( + RECENT_JOURNEYS_STORAGE_KEY, + JSON.stringify(updated), + ); }, - [recentJourneys] + [recentJourneys], ); const markTestJourneyUsed = useCallback( @@ -75,11 +82,17 @@ export default function JourneyRouteScreen(props: Props): React.ReactElement { if (!isTestJourneyName(trimmedName)) { return; } - const updated = [trimmedName, ...usedTestJourneys.filter((item) => item !== trimmedName)]; + const updated = [ + trimmedName, + ...usedTestJourneys.filter(item => item !== trimmedName), + ]; setUsedTestJourneys(updated); - await AsyncStorage.setItem(USED_TEST_JOURNEYS_STORAGE_KEY, JSON.stringify(updated)); + await AsyncStorage.setItem( + USED_TEST_JOURNEYS_STORAGE_KEY, + JSON.stringify(updated), + ); }, - [usedTestJourneys] + [usedTestJourneys], ); const handleStart = useCallback(async (): Promise => { @@ -92,7 +105,9 @@ export default function JourneyRouteScreen(props: Props): React.ReactElement { try { await Promise.all([ saveRecentJourney(trimmedName), - SHOW_AM_TEST_JOURNEY_SUGGESTIONS ? markTestJourneyUsed(trimmedName) : Promise.resolve(), + SHOW_AM_TEST_JOURNEY_SUGGESTIONS + ? markTestJourneyUsed(trimmedName) + : Promise.resolve(), ]); } catch { // Ignore local suggestion persistence failures and continue to Journey flow. @@ -110,7 +125,7 @@ export default function JourneyRouteScreen(props: Props): React.ReactElement { setJourneyName(name); void markTestJourneyUsed(name); }, - [markTestJourneyUsed] + [markTestJourneyUsed], ); return ( @@ -125,7 +140,11 @@ export default function JourneyRouteScreen(props: Props): React.ReactElement { journeyName={journeyName} onJourneyNameChange={setJourneyName} recentJourneys={recentJourneys} - testJourneys={SHOW_AM_TEST_JOURNEY_SUGGESTIONS ? [...TEST_JOURNEY_NAME_SUGGESTIONS] : []} + testJourneys={ + SHOW_AM_TEST_JOURNEY_SUGGESTIONS + ? [...TEST_JOURNEY_NAME_SUGGESTIONS] + : [] + } usedTestJourneys={usedTestJourneySet} onPressRecentJourney={handlePressRecentJourney} onPressTestJourney={handlePressTestJourney} diff --git a/PingSampleApp/ui/LogoutScreen.tsx b/PingSampleApp/ui/LogoutScreen.tsx index 5e69c187f..fe98f9345 100644 --- a/PingSampleApp/ui/LogoutScreen.tsx +++ b/PingSampleApp/ui/LogoutScreen.tsx @@ -52,7 +52,7 @@ export default function LogoutScreen(): React.ReactElement { useCallback(() => { void refreshSessionState(); return undefined; - }, [refreshSessionState]) + }, [refreshSessionState]), ); const handleLogoutAll = useCallback(async (): Promise => { @@ -64,13 +64,17 @@ export default function LogoutScreen(): React.ReactElement { try { await journeyActions.logoutUser(); } catch (error) { - errors.push(error instanceof Error ? error.message : 'Journey logout failed'); + errors.push( + error instanceof Error ? error.message : 'Journey logout failed', + ); } try { await oidcActions.logout(); } catch (error) { - errors.push(error instanceof Error ? error.message : 'OIDC logout failed'); + errors.push( + error instanceof Error ? error.message : 'OIDC logout failed', + ); } await refreshSessionState(); @@ -94,7 +98,9 @@ export default function LogoutScreen(): React.ReactElement { await oidcActions.logout(); setStatusMessage('OIDC Web session logged out.'); } catch (error) { - setErrorMessage(error instanceof Error ? error.message : 'OIDC Web logout failed'); + setErrorMessage( + error instanceof Error ? error.message : 'OIDC Web logout failed', + ); } finally { setBusyOidc(false); await refreshSessionState(); @@ -109,7 +115,9 @@ export default function LogoutScreen(): React.ReactElement { await journeyActions.logoutUser(); setStatusMessage('Journey session logged out.'); } catch (error) { - setErrorMessage(error instanceof Error ? error.message : 'Journey logout failed'); + setErrorMessage( + error instanceof Error ? error.message : 'Journey logout failed', + ); } finally { setBusyJourney(false); await refreshSessionState(); @@ -121,8 +129,10 @@ export default function LogoutScreen(): React.ReactElement { return ( - - + {hasAnySession ? ( ) : ( - {}} variant="secondary" disabled /> + {}} + variant="secondary" + disabled + /> )} {journeyActive ? ( - Logout from Journey authentication + + Logout from Journey authentication + Status: Active - Logout from OIDC Web authentication + + Logout from OIDC Web authentication + Status: Active + {/* OIDC STORAGE CARD */} OIDC Storage @@ -62,12 +60,18 @@ export default function MultiStorageScreen() { setSessionStorage(configureSessionStorageInfo()); }} > - Configure Session Storage + + Configure Session Storage + ) : ( )} diff --git a/PingSampleApp/ui/OidcScreen.tsx b/PingSampleApp/ui/OidcScreen.tsx index cde9d2e73..0d3741539 100644 --- a/PingSampleApp/ui/OidcScreen.tsx +++ b/PingSampleApp/ui/OidcScreen.tsx @@ -28,7 +28,10 @@ export default function OidcScreen(props: OidcScreenProps): React.ReactElement { const { clientConfig } = props; return ( - + ); diff --git a/PingSampleApp/ui/TokenScreen.tsx b/PingSampleApp/ui/TokenScreen.tsx index d738a0820..9e879cb61 100644 --- a/PingSampleApp/ui/TokenScreen.tsx +++ b/PingSampleApp/ui/TokenScreen.tsx @@ -37,7 +37,9 @@ const JOURNEY_AUTH_REQUIRED_MESSAGE = */ export default function TokenScreen(): React.ReactElement { const [activeTab, setActiveTab] = useState('Journey'); - const [tokenOutputByTab, setTokenOutputByTab] = useState>({ + const [tokenOutputByTab, setTokenOutputByTab] = useState< + Record + >({ Journey: getEmptyMessage('Journey'), OIDC: getEmptyMessage('OIDC'), }); @@ -48,9 +50,9 @@ export default function TokenScreen(): React.ReactElement { const setActiveTabOutput = useCallback( (value: string): void => { - setTokenOutputByTab((previous) => ({ ...previous, [activeTab]: value })); + setTokenOutputByTab(previous => ({ ...previous, [activeTab]: value })); }, - [activeTab] + [activeTab], ); const handleAccessToken = useCallback(async (): Promise => { @@ -85,7 +87,9 @@ export default function TokenScreen(): React.ReactElement { ) { setActiveTabOutput(JOURNEY_AUTH_REQUIRED_MESSAGE); } else { - setActiveTabOutput(error instanceof Error ? error.message : 'Token retrieval failed'); + setActiveTabOutput( + error instanceof Error ? error.message : 'Token retrieval failed', + ); } } finally { setLoading(false); @@ -128,7 +132,9 @@ export default function TokenScreen(): React.ReactElement { ) { setActiveTabOutput(JOURNEY_AUTH_REQUIRED_MESSAGE); } else { - setActiveTabOutput(error instanceof Error ? error.message : 'Token refresh failed'); + setActiveTabOutput( + error instanceof Error ? error.message : 'Token refresh failed', + ); } } finally { setLoading(false); @@ -149,8 +155,8 @@ export default function TokenScreen(): React.ReactElement { : 'No active OIDC user to revoke.', }, null, - 2 - ) + 2, + ), ); return; } @@ -165,14 +171,16 @@ export default function TokenScreen(): React.ReactElement { session, }, null, - 2 - ) + 2, + ), ); } else { setActiveTabOutput(getEmptyMessage('Journey')); } } catch (error) { - setActiveTabOutput(error instanceof Error ? error.message : 'Token revoke failed'); + setActiveTabOutput( + error instanceof Error ? error.message : 'Token revoke failed', + ); } finally { setLoading(false); } @@ -180,7 +188,11 @@ export default function TokenScreen(): React.ReactElement { return ( - + ; +type UserProfileNavigationProp = NativeStackNavigationProp< + RootStackParamList, + 'UserProfile' +>; type Props = { navigation: UserProfileNavigationProp; @@ -33,13 +36,18 @@ type Props = { * @param props.navigation Stack navigation controller. * @returns User profile screen element. */ -export default function UserProfileScreen({ navigation }: Props): React.ReactElement { +export default function UserProfileScreen({ + navigation, +}: Props): React.ReactElement { const [activeTab, setActiveTab] = useState('Journey'); - const [journeySession, setJourneySession] = useState(null); + const [journeySession, setJourneySession] = + useState(null); const [journeyLoading, setJourneyLoading] = useState(false); const [journeyError, setJourneyError] = useState(null); - const [showRawJourneyUserInfo, setShowRawJourneyUserInfo] = useState(false); - const [showRawOidcUserInfo, setShowRawOidcUserInfo] = useState(false); + const [showRawJourneyUserInfo, setShowRawJourneyUserInfo] = + useState(false); + const [showRawOidcUserInfo, setShowRawOidcUserInfo] = + useState(false); const [, journeyActions] = useJourney(); const [oidcState, oidcActions] = useOidc(); @@ -57,7 +65,9 @@ export default function UserProfileScreen({ navigation }: Props): React.ReactEle } } catch (error) { const message = - error instanceof Error ? error.message : 'Unable to resolve Journey session.'; + error instanceof Error + ? error.message + : 'Unable to resolve Journey session.'; setJourneyError(message); setJourneySession(null); } finally { @@ -86,7 +96,7 @@ export default function UserProfileScreen({ navigation }: Props): React.ReactEle void refreshOidcSession(); } return undefined; - }, [activeTab, refreshJourneySession, refreshOidcSession]) + }, [activeTab, refreshJourneySession, refreshOidcSession]), ); /** @@ -124,7 +134,9 @@ export default function UserProfileScreen({ navigation }: Props): React.ReactEle session={journeySession} error={journeyError} showRawUserInfo={showRawJourneyUserInfo} - onToggleRawUserInfo={() => setShowRawJourneyUserInfo((value) => !value)} + onToggleRawUserInfo={() => + setShowRawJourneyUserInfo(value => !value) + } onStartJourney={() => navigation.navigate('JourneyRoute')} /> ) : null} @@ -136,7 +148,7 @@ export default function UserProfileScreen({ navigation }: Props): React.ReactEle hasSession={oidcState.isAuthenticated} error={oidcState.error?.message ?? null} showRawUserInfo={showRawOidcUserInfo} - onToggleRawUserInfo={() => setShowRawOidcUserInfo((value) => !value)} + onToggleRawUserInfo={() => setShowRawOidcUserInfo(value => !value)} onStartOidc={async () => { try { await oidcActions.authorize(); diff --git a/PingSampleApp/ui/components/atoms/KeyValueList.tsx b/PingSampleApp/ui/components/atoms/KeyValueList.tsx index 67923a8aa..290430ab0 100644 --- a/PingSampleApp/ui/components/atoms/KeyValueList.tsx +++ b/PingSampleApp/ui/components/atoms/KeyValueList.tsx @@ -37,12 +37,14 @@ type KeyValueListProps = { * @param props List props. * @returns Key/value list element. */ -export default function KeyValueList(props: KeyValueListProps): React.ReactElement { +export default function KeyValueList( + props: KeyValueListProps, +): React.ReactElement { const { items, textStyle, style } = props; return ( - {items.map((item) => ( + {items.map(item => ( {item.label}: {item.value} diff --git a/PingSampleApp/ui/components/atoms/LabeledSwitchRow.tsx b/PingSampleApp/ui/components/atoms/LabeledSwitchRow.tsx index a78888e5d..deff2361b 100644 --- a/PingSampleApp/ui/components/atoms/LabeledSwitchRow.tsx +++ b/PingSampleApp/ui/components/atoms/LabeledSwitchRow.tsx @@ -45,13 +45,15 @@ type LabeledSwitchRowProps = { * @returns Labeled switch row element. */ export default function LabeledSwitchRow( - props: LabeledSwitchRowProps + props: LabeledSwitchRowProps, ): React.ReactElement { const { label, value, onValueChange, disabled, rowStyle, labelStyle } = props; return ( - {label} + + {label} + ({ borderColor: selected ? activeBorderColor : inactiveBorderColor, borderWidth: selected ? 2 : 1.5, }), - [selected, activeBorderColor, inactiveBorderColor] + [selected, activeBorderColor, inactiveBorderColor], ); const floatingLabelStateStyle = useMemo( () => ({ color: selected ? activeBorderColor : inactiveBorderColor, backgroundColor: active ? labelBackgroundColor : 'transparent', }), - [selected, active, activeBorderColor, inactiveBorderColor, labelBackgroundColor] + [ + selected, + active, + activeBorderColor, + inactiveBorderColor, + labelBackgroundColor, + ], ); const animatedLabelStyle = useMemo( () => ({ @@ -131,13 +147,13 @@ export default function PingTextInput(props: PingTextInputProps): React.ReactEle outputRange: [0, 5], }), }), - [labelAnimation] + [labelAnimation], ); const inputTextStateStyle = useMemo( () => ({ color: selected ? activeTextColor : inactiveTextColor, }), - [selected, activeTextColor, inactiveTextColor] + [selected, activeTextColor, inactiveTextColor], ); useEffect(() => { @@ -154,7 +170,7 @@ export default function PingTextInput(props: PingTextInputProps): React.ReactEle setIsFocused(true); onFocus?.(event); }, - [onFocus] + [onFocus], ); const handleBlur = useCallback>( @@ -162,17 +178,13 @@ export default function PingTextInput(props: PingTextInputProps): React.ReactEle setIsFocused(false); onBlur?.(event); }, - [onBlur] + [onBlur], ); return ( {showFloatingLabel && floatingLabel.length > 0 ? ( setPasswordVisible((previousValue) => !previousValue)} + accessibilityLabel={ + passwordVisible ? 'Hide password' : 'Show password' + } + onPress={() => setPasswordVisible(previousValue => !previousValue)} style={styles.passwordToggle} > - {loading ? : {label}} + {loading ? ( + + ) : ( + {label} + )} ); } diff --git a/PingSampleApp/ui/components/molecules/AuthSourceTabs.tsx b/PingSampleApp/ui/components/molecules/AuthSourceTabs.tsx index 34223ff83..c9d00ab16 100644 --- a/PingSampleApp/ui/components/molecules/AuthSourceTabs.tsx +++ b/PingSampleApp/ui/components/molecules/AuthSourceTabs.tsx @@ -36,18 +36,21 @@ export type AuthSourceTabsProps = { * @returns Tab strip element. */ export default function AuthSourceTabs( - props: AuthSourceTabsProps + props: AuthSourceTabsProps, ): React.ReactElement { const { tabs, activeTab, onTabChange } = props; return ( - {tabs.map((tab) => { + {tabs.map(tab => { const selected = tab === activeTab; return ( onTabChange(tab)} > {title || badgeText || headerRight ? ( - {title ? {title} : null} + {title ? ( + {title} + ) : null} {badgeText ? ( {badgeText} @@ -84,7 +82,9 @@ export default function CardSection(props: CardSectionProps): React.ReactElement ) : null} {subtitle ? ( - {subtitle} + + {subtitle} + ) : null} {children} diff --git a/PingSampleApp/ui/components/molecules/CollapsiblePayloadSection.tsx b/PingSampleApp/ui/components/molecules/CollapsiblePayloadSection.tsx index 1fedcaf48..ff29af534 100644 --- a/PingSampleApp/ui/components/molecules/CollapsiblePayloadSection.tsx +++ b/PingSampleApp/ui/components/molecules/CollapsiblePayloadSection.tsx @@ -48,9 +48,16 @@ export type CollapsiblePayloadSectionProps = { * @returns Collapsible payload section element. */ export default function CollapsiblePayloadSection( - props: CollapsiblePayloadSectionProps + props: CollapsiblePayloadSectionProps, ): React.ReactElement { - const { title, payload, expanded, onToggle, loading = false, isError = false } = props; + const { + title, + payload, + expanded, + onToggle, + loading = false, + isError = false, + } = props; return ( @@ -60,9 +67,16 @@ export default function CollapsiblePayloadSection( {expanded ? 'v' : '>'} {title} - {!expanded && loading ? : null} + {!expanded && loading ? ( + + ) : null} - {expanded ? : null} + {expanded ? ( + + ) : null} ); } diff --git a/PingSampleApp/ui/components/molecules/EmptyStateCard.tsx b/PingSampleApp/ui/components/molecules/EmptyStateCard.tsx index 31b2d489f..466948d79 100644 --- a/PingSampleApp/ui/components/molecules/EmptyStateCard.tsx +++ b/PingSampleApp/ui/components/molecules/EmptyStateCard.tsx @@ -42,15 +42,21 @@ export type EmptyStateCardProps = { * @param props Empty state card props. * @returns Empty state card element. */ -export default function EmptyStateCard(props: EmptyStateCardProps): React.ReactElement { +export default function EmptyStateCard( + props: EmptyStateCardProps, +): React.ReactElement { const { title, message, ctaLabel, onCtaPress, errorMessage } = props; return ( {title} {message} - {ctaLabel && onCtaPress ? : null} - {errorMessage ? {errorMessage} : null} + {ctaLabel && onCtaPress ? ( + + ) : null} + {errorMessage ? ( + {errorMessage} + ) : null} ); } diff --git a/PingSampleApp/ui/components/molecules/HomeMenuRow.tsx b/PingSampleApp/ui/components/molecules/HomeMenuRow.tsx index e09606c59..280dcd656 100644 --- a/PingSampleApp/ui/components/molecules/HomeMenuRow.tsx +++ b/PingSampleApp/ui/components/molecules/HomeMenuRow.tsx @@ -48,11 +48,21 @@ export type HomeMenuItem = { * @returns Home menu row element. */ export default function HomeMenuRow(props: HomeMenuItem): React.ReactElement { - const { title, subtitle, icon, disabled = false, comingSoon = false, onPress } = props; + const { + title, + subtitle, + icon, + disabled = false, + comingSoon = false, + onPress, + } = props; return ( @@ -66,7 +76,12 @@ export default function HomeMenuRow(props: HomeMenuItem): React.ReactElement { - + {title} {comingSoon ? ( @@ -75,12 +90,22 @@ export default function HomeMenuRow(props: HomeMenuItem): React.ReactElement { ) : null} - + {subtitle} - + {'>'} diff --git a/PingSampleApp/ui/journey/components/molecules/renderers/JourneyBooleanField.tsx b/PingSampleApp/ui/journey/components/molecules/renderers/JourneyBooleanField.tsx index 49aba3a7f..6d99750eb 100644 --- a/PingSampleApp/ui/journey/components/molecules/renderers/JourneyBooleanField.tsx +++ b/PingSampleApp/ui/journey/components/molecules/renderers/JourneyBooleanField.tsx @@ -22,7 +22,7 @@ import type { JourneyFieldRendererProps } from './types'; * @returns Boolean field card. */ export default function JourneyBooleanField( - props: JourneyFieldRendererProps + props: JourneyFieldRendererProps, ): React.ReactElement { const { field, currentValue, setFieldValue } = props; const promptText = resolvePromptText(field.prompt, field.message); @@ -40,11 +40,13 @@ export default function JourneyBooleanField( ) : null} {termsText.length > 0 ? ( - {termsText} + + {termsText} + ) : null} setFieldValue(field.id, value)} + onValueChange={value => setFieldValue(field.id, value)} /> ); diff --git a/PingSampleApp/ui/journey/components/molecules/renderers/JourneyChoiceField.tsx b/PingSampleApp/ui/journey/components/molecules/renderers/JourneyChoiceField.tsx index f1eaf6e75..3029cf0b0 100644 --- a/PingSampleApp/ui/journey/components/molecules/renderers/JourneyChoiceField.tsx +++ b/PingSampleApp/ui/journey/components/molecules/renderers/JourneyChoiceField.tsx @@ -23,7 +23,7 @@ import type { JourneyFieldRendererProps } from './types'; * @returns Choice field card. */ export default function JourneyChoiceField( - props: JourneyFieldRendererProps + props: JourneyFieldRendererProps, ): React.ReactElement { const { field, currentValue, setFieldValue } = props; const selected = readNumber(currentValue, readNumber(field.defaultValue, -1)); @@ -35,9 +35,11 @@ export default function JourneyChoiceField( {promptText} ) : null} {!field.options || field.options.length === 0 ? ( - No options were provided by this callback. + + No options were provided by this callback. + ) : null} - {field.options?.map((option) => ( + {field.options?.map(option => ( ; } - if (field.capability === 'integration_required' || field.capability === 'unsupported') { + if ( + field.capability === 'integration_required' || + field.capability === 'unsupported' + ) { return ; } diff --git a/PingSampleApp/ui/journey/components/molecules/renderers/JourneyKbaField.tsx b/PingSampleApp/ui/journey/components/molecules/renderers/JourneyKbaField.tsx index cab6f7fe3..5b258d89e 100644 --- a/PingSampleApp/ui/journey/components/molecules/renderers/JourneyKbaField.tsx +++ b/PingSampleApp/ui/journey/components/molecules/renderers/JourneyKbaField.tsx @@ -30,28 +30,32 @@ type JourneyKbaValue = { * @returns KBA field card. */ export default function JourneyKbaField( - props: JourneyFieldRendererProps + props: JourneyFieldRendererProps, ): React.ReactElement { const { field, currentValue, setFieldValue } = props; const promptText = resolvePromptText(field.prompt, field.message); - const [isQuestionDropdownOpen, setQuestionDropdownOpen] = useState(false); + const [isQuestionDropdownOpen, setQuestionDropdownOpen] = + useState(false); - const kbaValue: JourneyKbaValue = - (currentValue as JourneyKbaValue | undefined) ?? { - selectedQuestion: '', - selectedAnswer: '', - allowUserDefinedQuestions: false, - }; + const kbaValue: JourneyKbaValue = (currentValue as + | JourneyKbaValue + | undefined) ?? { + selectedQuestion: '', + selectedAnswer: '', + allowUserDefinedQuestions: false, + }; const questionOptions = useMemo( () => - (field.options ?? []).map((option) => - resolveOptionLabel(option.label, option.value, option.index) + (field.options ?? []).map(option => + resolveOptionLabel(option.label, option.value, option.index), ), - [field.options] + [field.options], ); const selectedQuestion = readString(kbaValue.selectedQuestion); const selectedQuestionLabel = - selectedQuestion.length > 0 ? selectedQuestion : 'Select a security question'; + selectedQuestion.length > 0 + ? selectedQuestion + : 'Select a security question'; return ( @@ -62,9 +66,13 @@ export default function JourneyKbaField( setQuestionDropdownOpen((previousValue) => !previousValue)} + onPress={() => + setQuestionDropdownOpen(previousValue => !previousValue) + } > - {selectedQuestionLabel} + + {selectedQuestionLabel} + {isQuestionDropdownOpen ? '▲' : '▼'} @@ -86,7 +94,7 @@ export default function JourneyKbaField( selectedAnswer: readString(kbaValue.selectedAnswer), allowUserDefinedQuestions: readBoolean( kbaValue.allowUserDefinedQuestions, - false + false, ), }); setQuestionDropdownOpen(false); @@ -109,16 +117,18 @@ export default function JourneyKbaField( ) : null} 0 ? fieldStyles.topGap : undefined} + containerStyle={ + questionOptions.length > 0 ? fieldStyles.topGap : undefined + } label="Answer" value={readString(kbaValue.selectedAnswer)} - onChangeText={(text) => + onChangeText={text => setFieldValue(field.id, { selectedQuestion, selectedAnswer: text, allowUserDefinedQuestions: readBoolean( kbaValue.allowUserDefinedQuestions, - false + false, ), }) } diff --git a/PingSampleApp/ui/journey/components/molecules/renderers/JourneyOutputField.tsx b/PingSampleApp/ui/journey/components/molecules/renderers/JourneyOutputField.tsx index 1ca9b712f..a9f80b89a 100644 --- a/PingSampleApp/ui/journey/components/molecules/renderers/JourneyOutputField.tsx +++ b/PingSampleApp/ui/journey/components/molecules/renderers/JourneyOutputField.tsx @@ -21,7 +21,7 @@ import type { JourneyFieldRendererProps } from './types'; * @returns Output-only field card. */ export default function JourneyOutputField( - props: JourneyFieldRendererProps + props: JourneyFieldRendererProps, ): React.ReactElement { const { field } = props; const promptText = resolvePromptText(field.prompt, field.message); @@ -84,7 +84,9 @@ export default function JourneyOutputField( onStartShouldSetResponderCapture={(): boolean => true} onMoveShouldSetResponderCapture={(): boolean => true} > - {toDisplayString(field.raw.value)} + + {toDisplayString(field.raw.value)} + ); diff --git a/PingSampleApp/ui/journey/components/molecules/renderers/JourneyTextField.tsx b/PingSampleApp/ui/journey/components/molecules/renderers/JourneyTextField.tsx index 6ce72ea24..a2217b707 100644 --- a/PingSampleApp/ui/journey/components/molecules/renderers/JourneyTextField.tsx +++ b/PingSampleApp/ui/journey/components/molecules/renderers/JourneyTextField.tsx @@ -19,7 +19,7 @@ import PingTextInput from '../../../../components/atoms/PingTextInput'; * @returns Text field card. */ export default function JourneyTextField( - props: JourneyFieldRendererProps + props: JourneyFieldRendererProps, ): React.ReactElement { const { field, currentValue, setFieldValue } = props; const promptText = resolvePromptText(field.prompt, field.message); @@ -31,7 +31,7 @@ export default function JourneyTextField( 0 ? promptText : field.ref.type} value={toDisplayString(currentValue)} - onChangeText={(text) => setFieldValue(field.id, text)} + onChangeText={text => setFieldValue(field.id, text)} secureTextEntry={secureTextEntry} allowPasswordToggle={isPasswordField} keyboardType={field.kind === 'number' ? 'numeric' : 'default'} diff --git a/PingSampleApp/ui/journey/components/molecules/renderers/JourneyUnsupportedField.tsx b/PingSampleApp/ui/journey/components/molecules/renderers/JourneyUnsupportedField.tsx index b28af64e7..7fb156b7c 100644 --- a/PingSampleApp/ui/journey/components/molecules/renderers/JourneyUnsupportedField.tsx +++ b/PingSampleApp/ui/journey/components/molecules/renderers/JourneyUnsupportedField.tsx @@ -17,7 +17,7 @@ import type { JourneyFieldRendererProps } from './types'; * @returns Warning field card. */ export default function JourneyUnsupportedField( - props: JourneyFieldRendererProps + props: JourneyFieldRendererProps, ): React.ReactElement { const { field } = props; diff --git a/PingSampleApp/ui/journey/components/molecules/renderers/valueReaders.ts b/PingSampleApp/ui/journey/components/molecules/renderers/valueReaders.ts index 244abf08f..aa3dd1faa 100644 --- a/PingSampleApp/ui/journey/components/molecules/renderers/valueReaders.ts +++ b/PingSampleApp/ui/journey/components/molecules/renderers/valueReaders.ts @@ -78,10 +78,7 @@ export function toDisplayString(value: unknown): string { * @param message - Native callback message value. * @returns Display-ready prompt text. */ -export function resolvePromptText( - prompt: unknown, - message: unknown -): string { +export function resolvePromptText(prompt: unknown, message: unknown): string { const promptText = readString(prompt, '').trim(); if (promptText.length > 0) { return promptText; @@ -106,7 +103,7 @@ export function resolvePromptText( export function resolveOptionLabel( label: unknown, value: unknown, - index: number + index: number, ): string { const labelText = readString(label, '').trim(); if (labelText.length > 0) { diff --git a/PingSampleApp/ui/journey/components/organisms/JourneyClientPanel.tsx b/PingSampleApp/ui/journey/components/organisms/JourneyClientPanel.tsx index ee71dd7ab..e4242b5bf 100644 --- a/PingSampleApp/ui/journey/components/organisms/JourneyClientPanel.tsx +++ b/PingSampleApp/ui/journey/components/organisms/JourneyClientPanel.tsx @@ -5,7 +5,13 @@ * of the MIT license. See the LICENSE file for details. */ -import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import React, { + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from 'react'; import { Alert, Linking, Text, TouchableOpacity, View } from 'react-native'; import { collectDeviceProfileForJourney } from '@ping-identity/rn-device-profile'; import { @@ -51,7 +57,7 @@ export type JourneyClientPanelProps = { * @returns Journey panel element. */ export default function JourneyClientPanel( - props: JourneyClientPanelProps + props: JourneyClientPanelProps, ): React.ReactElement { const { journeyClient, @@ -61,13 +67,17 @@ export default function JourneyClientPanel( onAuthenticated, requireSuccessConfirmation = false, } = props; - const [node, { start, next, resume, user, logoutUser, loading, error }] = useJourney(journeyClient); + const [node, { start, next, resume, user, logoutUser, loading, error }] = + useJourney(journeyClient); const form = useJourneyForm(node); - const [journeyName, setJourneyName] = useState(initialJourneyName?.trim() ?? ''); + const [journeyName, setJourneyName] = useState( + initialJourneyName?.trim() ?? '', + ); const [resumeUrl, setResumeUrl] = useState(''); const [hasActiveSession, setHasActiveSession] = useState(false); - const [isSessionCheckRunning, setIsSessionCheckRunning] = useState(true); + const [isSessionCheckRunning, setIsSessionCheckRunning] = + useState(true); const hasAutoStartedRef = useRef(false); const lastAutoResumedUrlRef = useRef(null); @@ -77,12 +87,17 @@ export default function JourneyClientPanel( const fields = form.fields; const deviceProfileFields = form.getFieldsByType('DeviceProfileCallback'); const hasDeviceProfileCallback = deviceProfileFields.length > 0; - const hasPollingWaitCallback = form.getFieldsByType('PollingWaitCallback').length > 0; - const hasSuspendedCallback = form.getFieldsByType('SuspendedTextOutputCallback').length > 0; - const pollingWaitMs = useMemo(() => resolvePollingWaitMs(fields), [fields]); + const hasPollingWaitCallback = + form.getFieldsByType('PollingWaitCallback').length > 0; + const hasSuspendedCallback = + form.getFieldsByType('SuspendedTextOutputCallback').length > 0; + const pollingWaitMs = useMemo( + () => resolvePollingWaitMs(fields), + [fields], + ); const deviceProfileRequestKey = useMemo(() => { return deviceProfileFields - .map((field) => `${field.ref.type}:${field.ref.typeIndex}`) + .map(field => `${field.ref.type}:${field.ref.typeIndex}`) .join('|'); }, [deviceProfileFields]); @@ -112,7 +127,9 @@ export default function JourneyClientPanel( return; } - if (lastAutoDeviceProfileRequestKeyRef.current === deviceProfileRequestKey) { + if ( + lastAutoDeviceProfileRequestKeyRef.current === deviceProfileRequestKey + ) { return; } @@ -120,7 +137,9 @@ export default function JourneyClientPanel( const runAutoDeviceProfile = async (): Promise => { try { - await collectDeviceProfileForJourney(journeyClient, [...DEVICE_PROFILE_COLLECTORS]); + await collectDeviceProfileForJourney(journeyClient, [ + ...DEVICE_PROFILE_COLLECTORS, + ]); if (!form.meta.hasManual && !hasPollingWaitCallback) { await next({}); @@ -142,17 +161,20 @@ export default function JourneyClientPanel( node?.type, ]); - const refreshSession = useCallback(async (showError = true): Promise => { - try { - const session = await user(); - setHasActiveSession(Boolean(session)); - } catch (cause) { - setHasActiveSession(false); - if (showError) { - Alert.alert('Session refresh failed', String(cause)); + const refreshSession = useCallback( + async (showError = true): Promise => { + try { + const session = await user(); + setHasActiveSession(Boolean(session)); + } catch (cause) { + setHasActiveSession(false); + if (showError) { + Alert.alert('Session refresh failed', String(cause)); + } } - } - }, [user]); + }, + [user], + ); useEffect(() => { // Bootstrap current session state on mount/client change. @@ -208,7 +230,12 @@ export default function JourneyClientPanel( hasNotifiedAuthenticatedRef.current = true; onAuthenticated(); - }, [hasActiveSession, node?.type, onAuthenticated, requireSuccessConfirmation]); + }, [ + hasActiveSession, + node?.type, + onAuthenticated, + requireSuccessConfirmation, + ]); const onContinueAfterSuccess = useCallback((): void => { // Explicit continuation path for UX that requires user confirmation @@ -298,7 +325,10 @@ export default function JourneyClientPanel( const subscription = Linking.addEventListener('url', ({ url }) => { const trimmedResumeUrl = url.trim(); - if (!trimmedResumeUrl || lastAutoResumedUrlRef.current === trimmedResumeUrl) { + if ( + !trimmedResumeUrl || + lastAutoResumedUrlRef.current === trimmedResumeUrl + ) { return; } @@ -351,7 +381,7 @@ export default function JourneyClientPanel( Alert.alert( 'Cannot continue', - firstIssue?.message ?? 'Resolve callback issues before submitting.' + firstIssue?.message ?? 'Resolve callback issues before submitting.', ); return; } @@ -384,7 +414,10 @@ export default function JourneyClientPanel( {showSuccessScreen ? ( - + Logout {requireSuccessConfirmation && onAuthenticated ? ( @@ -411,8 +444,8 @@ export default function JourneyClientPanel( {typeof node.cause === 'string' ? node.cause : typeof node.message === 'string' - ? node.message - : 'An unexpected failure occurred.'} + ? node.message + : 'An unexpected failure occurred.'} ) : null} @@ -422,12 +455,14 @@ export default function JourneyClientPanel( ) : null} - {!showCallbackScreen && !showSuccessScreen && !loading && !isSessionCheckRunning ? ( + {!showCallbackScreen && + !showSuccessScreen && + !loading && + !isSessionCheckRunning ? ( No active Journey flow. Start from Journey Configuration. ) : null} - ); diff --git a/PingSampleApp/ui/journey/components/organisms/JourneyContinuePanel.tsx b/PingSampleApp/ui/journey/components/organisms/JourneyContinuePanel.tsx index 26597f690..bc054f8df 100644 --- a/PingSampleApp/ui/journey/components/organisms/JourneyContinuePanel.tsx +++ b/PingSampleApp/ui/journey/components/organisms/JourneyContinuePanel.tsx @@ -7,7 +7,10 @@ import React, { useCallback, useMemo } from 'react'; import { Text, TouchableOpacity } from 'react-native'; -import type { JourneyCallbackType, JourneyFormResult } from '@ping-identity/rn-journey'; +import type { + JourneyCallbackType, + JourneyFormResult, +} from '@ping-identity/rn-journey'; import { commonStyles } from '../../../../src/styles/common'; import { journeyClientPanelStyles as styles } from '../../../../src/styles/journeyStyles'; import JourneyFieldRenderer from '../molecules/renderers/JourneyFieldRenderer'; @@ -34,7 +37,7 @@ export type JourneyContinuePanelProps = { * @returns Continue node panel markup. */ export default function JourneyContinuePanel( - props: JourneyContinuePanelProps + props: JourneyContinuePanelProps, ): React.ReactElement { const { form, @@ -48,8 +51,8 @@ export default function JourneyContinuePanel( const { fields, values, meta, setValue } = form; const callbackTypes = useMemo>( - () => new Set(fields.map((field) => field.ref.type)), - [fields] + () => new Set(fields.map(field => field.ref.type)), + [fields], ); // These flags drive integration UX: // - DeviceProfile/Suspended/Polling callbacks are handled by panel-level effects. @@ -61,9 +64,9 @@ export default function JourneyContinuePanel( // Integration-required callbacks are currently not auto-wired in this sample // (for example FIDO/Protect/IdP/ReCaptcha/Binding), so we block auto-advance. const hasBlockingIntegration = fields.some( - (field) => + field => field.capability === 'integration_required' && - field.ref.type !== 'DeviceProfileCallback' + field.ref.type !== 'DeviceProfileCallback', ); const hasUnsupportedCallbacks = meta.hasUnsupported; const hasUnacceptedRequiredAgreements = meta.hasRequiredConsentMissing; @@ -74,7 +77,8 @@ export default function JourneyContinuePanel( !hasDeviceProfileCallback && !hasSuspendedCallback && !hasPollingWaitCallback; - const shouldShowContinueButton = hasManualSubmit || canAutoAdvanceWithContinueButton; + const shouldShowContinueButton = + hasManualSubmit || canAutoAdvanceWithContinueButton; const submitDisabled = loading || hasUnacceptedRequiredAgreements || @@ -82,7 +86,7 @@ export default function JourneyContinuePanel( hasUnsupportedCallbacks; const pollingWaitSeconds = Math.max( 1, - Math.ceil((pollingWaitMs ?? DEFAULT_AUTO_POLLING_WAIT_MS) / 1000) + Math.ceil((pollingWaitMs ?? DEFAULT_AUTO_POLLING_WAIT_MS) / 1000), ); const handleResumePress = useCallback(async (): Promise => { @@ -96,7 +100,7 @@ export default function JourneyContinuePanel( return ( <> {/* `setFieldValue` writes into `useJourneyForm` state, consumed as `form.input` on submit. */} - {fields.map((field) => ( + {fields.map(field => ( - This node includes callbacks that require extra native integrations (FIDO, - Protect, IdP, ReCaptcha, or Binding). Configure those modules to continue - this journey. + This node includes callbacks that require extra native integrations + (FIDO, Protect, IdP, ReCaptcha, or Binding). Configure those modules + to continue this journey. ) : null} {hasUnsupportedCallbacks ? ( - This node includes callback types not handled by the helper submit planner. - Add custom handling for these callbacks to continue. + This node includes callback types not handled by the helper submit + planner. Add custom handling for these callbacks to continue. ) : null} @@ -137,7 +141,10 @@ export default function JourneyContinuePanel( autoCapitalize="none" /> @@ -161,7 +168,8 @@ export default function JourneyContinuePanel( {!hasManualSubmit && hasPollingWaitCallback ? ( - Polling callback detected. Continuing automatically in {pollingWaitSeconds}s. + Polling callback detected. Continuing automatically in{' '} + {pollingWaitSeconds}s. ) : null} diff --git a/PingSampleApp/ui/journey/components/organisms/JourneyDebugPanel.tsx b/PingSampleApp/ui/journey/components/organisms/JourneyDebugPanel.tsx index 50c848c80..556d0dc79 100644 --- a/PingSampleApp/ui/journey/components/organisms/JourneyDebugPanel.tsx +++ b/PingSampleApp/ui/journey/components/organisms/JourneyDebugPanel.tsx @@ -29,7 +29,7 @@ export type JourneyDebugPanelProps = { * @returns Debug panel element. */ export default function JourneyDebugPanel( - props: JourneyDebugPanelProps + props: JourneyDebugPanelProps, ): React.ReactElement { const { entries, onClear } = props; @@ -49,7 +49,7 @@ export default function JourneyDebugPanel( {entries.length === 0 ? ( No debug events yet. ) : ( - entries.map((entry) => ( + entries.map(entry => ( [{entry.timestamp}] {entry.title} diff --git a/PingSampleApp/ui/journey/components/organisms/JourneyStartPanel.tsx b/PingSampleApp/ui/journey/components/organisms/JourneyStartPanel.tsx index 86371a96a..2f8cb804f 100644 --- a/PingSampleApp/ui/journey/components/organisms/JourneyStartPanel.tsx +++ b/PingSampleApp/ui/journey/components/organisms/JourneyStartPanel.tsx @@ -45,13 +45,13 @@ export type JourneyStartPanelProps = { */ function buildSuggestionRows( suggestions: string[], - rowCount: number + rowCount: number, ): string[][] { const rows: string[][] = Array.from({ length: rowCount }, () => []); suggestions.forEach((suggestion, index) => { rows[index % rowCount].push(suggestion); }); - return rows.filter((row) => row.length > 0); + return rows.filter(row => row.length > 0); } /** @@ -61,7 +61,7 @@ function buildSuggestionRows( * @returns Journey start panel markup. */ export default function JourneyStartPanel( - props: JourneyStartPanelProps + props: JourneyStartPanelProps, ): React.ReactElement { const { showJourneyInput, @@ -95,8 +95,13 @@ export default function JourneyStartPanel( AM Test Journeys {useWrappedSuggestions ? ( - - {testJourneys.map((name) => { + + {testJourneys.map(name => { return ( {testJourneyRows.map((row, rowIndex) => ( - - {row.map((name) => { + + {row.map(name => { return ( onPressTestJourney(name)} style={commonStyles.suggestionChip} > - {name} + + {name} + ); })} @@ -144,8 +154,13 @@ export default function JourneyStartPanel( Recent Journeys {useWrappedSuggestions ? ( - - {recentJourneys.map((name) => ( + + {recentJourneys.map(name => ( onPressRecentJourney(name)} @@ -167,14 +182,19 @@ export default function JourneyStartPanel( > {recentJourneyRows.map((row, rowIndex) => ( - - {row.map((name) => ( + + {row.map(name => ( onPressRecentJourney(name)} style={commonStyles.suggestionChip} > - {name} + + {name} + ))} @@ -187,7 +207,9 @@ export default function JourneyStartPanel( ) : null} - {loading ? : null} + {loading ? ( + + ) : null} {canStart ? ( name === normalized); + return TEST_JOURNEY_NAME_SUGGESTIONS.some(name => name === normalized); } /** @@ -97,12 +97,14 @@ export function isTestJourneyName(journeyName: string): boolean { * @param recentJourneys - Recently used journey names loaded from storage. * @returns Unique recent journey names excluding built-in test suggestions. */ -export function buildRecentJourneySuggestions(recentJourneys: string[]): string[] { +export function buildRecentJourneySuggestions( + recentJourneys: string[], +): string[] { const seen = new Set(); return recentJourneys - .map((value) => value.trim()) - .filter((value) => { + .map(value => value.trim()) + .filter(value => { if (!value || seen.has(value) || isTestJourneyName(value)) { return false; } @@ -120,8 +122,8 @@ export function buildRecentJourneySuggestions(recentJourneys: string[]): string[ export function buildUsedTestJourneys(usedJourneys: string[]): string[] { const seen = new Set(); return usedJourneys - .map((value) => value.trim()) - .filter((value) => { + .map(value => value.trim()) + .filter(value => { if (!value || seen.has(value) || !isTestJourneyName(value)) { return false; } @@ -174,14 +176,22 @@ export function extractGivenName(session: unknown): string | undefined { * @param fields - Normalized callback fields. * @returns Polling wait time in milliseconds when available. */ -export function resolvePollingWaitMs(fields: JourneyNormalizedField[]): number | null { - const pollingField = fields.find((field) => field.ref.type === 'PollingWaitCallback'); +export function resolvePollingWaitMs( + fields: JourneyNormalizedField[], +): number | null { + const pollingField = fields.find( + field => field.ref.type === 'PollingWaitCallback', + ); if (!pollingField) { return null; } const waitTime = pollingField.raw.waitTime; - if (typeof waitTime === 'number' && Number.isFinite(waitTime) && waitTime > 0) { + if ( + typeof waitTime === 'number' && + Number.isFinite(waitTime) && + waitTime > 0 + ) { return waitTime; } if (typeof waitTime === 'string') { diff --git a/PingSampleApp/ui/journey/utils/debug.ts b/PingSampleApp/ui/journey/utils/debug.ts index 91529a285..04e88af8e 100644 --- a/PingSampleApp/ui/journey/utils/debug.ts +++ b/PingSampleApp/ui/journey/utils/debug.ts @@ -27,7 +27,7 @@ export type JourneyDebugEntry = { */ export function createJourneyDebugEntry( title: string, - payload?: unknown + payload?: unknown, ): JourneyDebugEntry { return { id: `${Date.now()}-${Math.random().toString(16).slice(2, 8)}`, @@ -45,7 +45,7 @@ export function createJourneyDebugEntry( */ export function sanitizeDebugPayload(value: unknown): unknown { if (Array.isArray(value)) { - return value.map((item) => sanitizeDebugPayload(item)); + return value.map(item => sanitizeDebugPayload(item)); } if (!value || typeof value !== 'object') { @@ -68,7 +68,7 @@ export function sanitizeDebugPayload(value: unknown): unknown { result[key] = sanitizeDebugPayload(nestedValue); return result; }, - {} + {}, ); } diff --git a/PingSampleApp/ui/oidc/components/molecules/OidcActionsCard.tsx b/PingSampleApp/ui/oidc/components/molecules/OidcActionsCard.tsx index 23a0b6b77..3fe97a9f7 100644 --- a/PingSampleApp/ui/oidc/components/molecules/OidcActionsCard.tsx +++ b/PingSampleApp/ui/oidc/components/molecules/OidcActionsCard.tsx @@ -55,7 +55,9 @@ type OidcActionsCardProps = { * @param props Actions card props. * @returns OIDC actions card element. */ -export default function OidcActionsCard(props: OidcActionsCardProps): React.ReactElement { +export default function OidcActionsCard( + props: OidcActionsCardProps, +): React.ReactElement { const { loading, isAuthenticated, @@ -70,7 +72,11 @@ export default function OidcActionsCard(props: OidcActionsCardProps): React.Reac return ( Userinfo Summary - + setShowRawUserInfo((previous) => !previous)} + onPress={() => setShowRawUserInfo(previous => !previous)} /> {showRawUserInfo ? ( - + ) : null} ) : null} diff --git a/PingSampleApp/ui/token/components/molecules/TokenActionsCard.tsx b/PingSampleApp/ui/token/components/molecules/TokenActionsCard.tsx index 0ad62f2fc..ecb2dc8b9 100644 --- a/PingSampleApp/ui/token/components/molecules/TokenActionsCard.tsx +++ b/PingSampleApp/ui/token/components/molecules/TokenActionsCard.tsx @@ -43,7 +43,9 @@ type TokenActionsCardProps = { * @param props - Actions card props. * @returns Token actions card element. */ -export default function TokenActionsCard(props: TokenActionsCardProps): React.ReactElement { +export default function TokenActionsCard( + props: TokenActionsCardProps, +): React.ReactElement { const { loading, onAccessToken, onRefresh, onRevoke, onClear } = props; return ( diff --git a/PingSampleApp/ui/token/components/molecules/TokenOutputCard.tsx b/PingSampleApp/ui/token/components/molecules/TokenOutputCard.tsx index 63775c2d8..a876da90f 100644 --- a/PingSampleApp/ui/token/components/molecules/TokenOutputCard.tsx +++ b/PingSampleApp/ui/token/components/molecules/TokenOutputCard.tsx @@ -29,7 +29,9 @@ type TokenOutputCardProps = { * @param props - Output card props. * @returns Token output card element. */ -export default function TokenOutputCard(props: TokenOutputCardProps): React.ReactElement { +export default function TokenOutputCard( + props: TokenOutputCardProps, +): React.ReactElement { const { tokenOutput, showComingSoonBadge } = props; return ( diff --git a/PingSampleApp/ui/token/components/organisms/TokenDaVinciPanel.tsx b/PingSampleApp/ui/token/components/organisms/TokenDaVinciPanel.tsx index 43196c0cf..ffbbe8e4e 100644 --- a/PingSampleApp/ui/token/components/organisms/TokenDaVinciPanel.tsx +++ b/PingSampleApp/ui/token/components/organisms/TokenDaVinciPanel.tsx @@ -24,7 +24,9 @@ type TokenDaVinciPanelProps = { * @param props - DaVinci token panel props. * @returns DaVinci token panel element. */ -export default function TokenDaVinciPanel(props: TokenDaVinciPanelProps): React.ReactElement { +export default function TokenDaVinciPanel( + props: TokenDaVinciPanelProps, +): React.ReactElement { const { tokenOutput } = props; return ; diff --git a/PingSampleApp/ui/token/components/organisms/TokenJourneyPanel.tsx b/PingSampleApp/ui/token/components/organisms/TokenJourneyPanel.tsx index daa20ff1e..4650df23e 100644 --- a/PingSampleApp/ui/token/components/organisms/TokenJourneyPanel.tsx +++ b/PingSampleApp/ui/token/components/organisms/TokenJourneyPanel.tsx @@ -45,8 +45,11 @@ type TokenJourneyPanelProps = { * @param props - Journey token panel props. * @returns Journey token panel element. */ -export default function TokenJourneyPanel(props: TokenJourneyPanelProps): React.ReactElement { - const { tokenOutput, loading, onAccessToken, onRefresh, onRevoke, onClear } = props; +export default function TokenJourneyPanel( + props: TokenJourneyPanelProps, +): React.ReactElement { + const { tokenOutput, loading, onAccessToken, onRefresh, onRevoke, onClear } = + props; return ( <> diff --git a/PingSampleApp/ui/token/components/organisms/TokenOidcPanel.tsx b/PingSampleApp/ui/token/components/organisms/TokenOidcPanel.tsx index a425fdf07..51f6db5aa 100644 --- a/PingSampleApp/ui/token/components/organisms/TokenOidcPanel.tsx +++ b/PingSampleApp/ui/token/components/organisms/TokenOidcPanel.tsx @@ -45,8 +45,11 @@ type TokenOidcPanelProps = { * @param props OIDC token panel props. * @returns OIDC token panel element. */ -export default function TokenOidcPanel(props: TokenOidcPanelProps): React.ReactElement { - const { tokenOutput, loading, onAccessToken, onRefresh, onRevoke, onClear } = props; +export default function TokenOidcPanel( + props: TokenOidcPanelProps, +): React.ReactElement { + const { tokenOutput, loading, onAccessToken, onRefresh, onRevoke, onClear } = + props; return ( <> @@ -61,4 +64,3 @@ export default function TokenOidcPanel(props: TokenOidcPanelProps): React.ReactE ); } - diff --git a/PingSampleApp/ui/userProfile/components/molecules/UserProfileInfoCard.tsx b/PingSampleApp/ui/userProfile/components/molecules/UserProfileInfoCard.tsx index d9b434bef..18bd00e5d 100644 --- a/PingSampleApp/ui/userProfile/components/molecules/UserProfileInfoCard.tsx +++ b/PingSampleApp/ui/userProfile/components/molecules/UserProfileInfoCard.tsx @@ -9,7 +9,9 @@ import React from 'react'; import { Text, View } from 'react-native'; import { commonStyles } from '../../../../src/styles/common'; import AsyncActionButton from '../../../components/molecules/AsyncActionButton'; -import KeyValueList, { type KeyValueItem } from '../../../components/atoms/KeyValueList'; +import KeyValueList, { + type KeyValueItem, +} from '../../../components/atoms/KeyValueList'; import UserProfileJsonBlock from './UserProfileJsonBlock'; /** @@ -59,7 +61,7 @@ const asDisplayValue = (value: unknown): string | null => { */ const readProfileField = ( payload: Record | null, - keys: readonly string[] + keys: readonly string[], ): string => { if (!payload) { return 'Not available'; @@ -90,15 +92,15 @@ const wrapProfileValue = (value: string): string => { * @returns User info card element. */ export default function UserProfileInfoCard( - props: UserProfileInfoCardProps + props: UserProfileInfoCardProps, ): React.ReactElement { const { title, userInfo, showRawUserInfo, onToggleRawUserInfo } = props; const firstName = wrapProfileValue( - readProfileField(userInfo, ['given_name', 'firstName', 'first_name']) + readProfileField(userInfo, ['given_name', 'firstName', 'first_name']), ); const familyName = wrapProfileValue( - readProfileField(userInfo, ['family_name', 'lastName', 'last_name']) + readProfileField(userInfo, ['family_name', 'lastName', 'last_name']), ); const email = wrapProfileValue(readProfileField(userInfo, ['email'])); const infoItems: KeyValueItem[] = [ @@ -110,14 +112,19 @@ export default function UserProfileInfoCard( return ( {title} - + - {showRawUserInfo ? : null} + {showRawUserInfo ? ( + + ) : null} ); } diff --git a/PingSampleApp/ui/userProfile/components/molecules/UserProfileJsonBlock.tsx b/PingSampleApp/ui/userProfile/components/molecules/UserProfileJsonBlock.tsx index f525da46c..7379214f5 100644 --- a/PingSampleApp/ui/userProfile/components/molecules/UserProfileJsonBlock.tsx +++ b/PingSampleApp/ui/userProfile/components/molecules/UserProfileJsonBlock.tsx @@ -31,7 +31,7 @@ type UserProfileJsonBlockProps = { * @returns JSON block element. */ export default function UserProfileJsonBlock( - props: UserProfileJsonBlockProps + props: UserProfileJsonBlockProps, ): React.ReactElement { const { title, payload } = props; diff --git a/PingSampleApp/ui/userProfile/components/organisms/UserProfileJourneyPanel.tsx b/PingSampleApp/ui/userProfile/components/organisms/UserProfileJourneyPanel.tsx index 54ffe4b74..6f2d682c7 100644 --- a/PingSampleApp/ui/userProfile/components/organisms/UserProfileJourneyPanel.tsx +++ b/PingSampleApp/ui/userProfile/components/organisms/UserProfileJourneyPanel.tsx @@ -50,15 +50,24 @@ type UserProfileJourneyPanelProps = { * @returns Journey profile panel element. */ export default function UserProfileJourneyPanel( - props: UserProfileJourneyPanelProps + props: UserProfileJourneyPanelProps, ): React.ReactElement { - const { loading, session, error, showRawUserInfo, onToggleRawUserInfo, onStartJourney } = props; + const { + loading, + session, + error, + showRawUserInfo, + onToggleRawUserInfo, + onStartJourney, + } = props; if (loading) { return ( - Checking Journey session... + + Checking Journey session... + ); } diff --git a/PingSampleApp/ui/userProfile/components/organisms/UserProfileOidcPanel.tsx b/PingSampleApp/ui/userProfile/components/organisms/UserProfileOidcPanel.tsx index 48eb240eb..158baf1ec 100644 --- a/PingSampleApp/ui/userProfile/components/organisms/UserProfileOidcPanel.tsx +++ b/PingSampleApp/ui/userProfile/components/organisms/UserProfileOidcPanel.tsx @@ -53,7 +53,7 @@ type UserProfileOidcPanelProps = { * @returns OIDC profile panel element. */ export default function UserProfileOidcPanel( - props: UserProfileOidcPanelProps + props: UserProfileOidcPanelProps, ): React.ReactElement { const { loading, @@ -69,7 +69,9 @@ export default function UserProfileOidcPanel( return ( - Checking OIDC session... + + Checking OIDC session... + ); } diff --git a/PingTestRunner/__tests__/integration/browser.test.ts b/PingTestRunner/__tests__/integration/browser.test.ts index d3d981539..4f3b5b751 100644 --- a/PingTestRunner/__tests__/integration/browser.test.ts +++ b/PingTestRunner/__tests__/integration/browser.test.ts @@ -14,6 +14,8 @@ * - Handles native errors gracefully */ +export {}; + const TEST_URL = 'https://example.com/oauth2/authorize'; // ─── helpers ──────────────────────────────────────────────────────────────── diff --git a/PingTestRunner/__tests__/integration/device-id.test.ts b/PingTestRunner/__tests__/integration/device-id.test.ts index e5b422ce3..691a10c9e 100644 --- a/PingTestRunner/__tests__/integration/device-id.test.ts +++ b/PingTestRunner/__tests__/integration/device-id.test.ts @@ -15,6 +15,8 @@ * - Propagates native errors to callers */ +export {}; + type NativeDeviceIdMock = { getDefaultDeviceId: jest.Mock; }; diff --git a/PingTestRunner/__tests__/integration/device-profile.test.ts b/PingTestRunner/__tests__/integration/device-profile.test.ts index 0ed9c5275..7c763c4ed 100644 --- a/PingTestRunner/__tests__/integration/device-profile.test.ts +++ b/PingTestRunner/__tests__/integration/device-profile.test.ts @@ -15,6 +15,8 @@ * - Propagates native errors to callers */ +export {}; + type NativeDeviceProfileMock = { collectDeviceProfile: jest.Mock; collectDeviceProfileForJourney: jest.Mock; diff --git a/PingTestRunner/__tests__/integration/journey.test.ts b/PingTestRunner/__tests__/integration/journey.test.ts index 8fd82597b..8e10598ad 100644 --- a/PingTestRunner/__tests__/integration/journey.test.ts +++ b/PingTestRunner/__tests__/integration/journey.test.ts @@ -19,6 +19,8 @@ * - dispose() cleans up the native instance */ +export {}; + type NativeJourneyMock = { configureJourney: jest.Mock; start: jest.Mock; @@ -110,7 +112,6 @@ describe('@ping-identity/rn-journey — integration', () => { it('throws when serverUrl is missing', async () => { const mod = await loadJourney(makeMock()); expect(() => - // @ts-expect-error — intentional bad input mod.createJourneyClient({}) ).toThrow(); }); diff --git a/PingTestRunner/__tests__/integration/logger.test.ts b/PingTestRunner/__tests__/integration/logger.test.ts index df9be2701..bcc6e3235 100644 --- a/PingTestRunner/__tests__/integration/logger.test.ts +++ b/PingTestRunner/__tests__/integration/logger.test.ts @@ -9,23 +9,19 @@ * Integration tests for @ping-identity/rn-logger * * Validates that the logger package: - * - Exports `logger` and `configureLogger` factory functions + * - Exports the `logger` factory function * - Creates logger instances with the correct level API * - Delegates log calls through the JS logger API * - Handles level changes at runtime */ -import { logger, configureLogger } from '@ping-identity/rn-logger'; +import { logger } from '@ping-identity/rn-logger'; describe('@ping-identity/rn-logger — integration', () => { describe('exports', () => { it('exports logger factory function', () => { expect(typeof logger).toBe('function'); }); - - it('exports configureLogger function', () => { - expect(typeof configureLogger).toBe('function'); - }); }); describe('logger()', () => { @@ -62,18 +58,6 @@ describe('@ping-identity/rn-logger — integration', () => { }); }); - describe('configureLogger()', () => { - it('returns a logger when native module is present', () => { - // configureLogger may return a full LoggerInstance or throw if native is absent - try { - const result = configureLogger({ level: 'info' }); - expect(result).toBeDefined(); - } catch { - // Native module not available in test environment — acceptable - } - }); - }); - describe('type contracts', () => { it('logger instance satisfies LoggerInstance shape', () => { const log = logger({ level: 'debug' }); diff --git a/PingTestRunner/__tests__/integration/oidc.test.ts b/PingTestRunner/__tests__/integration/oidc.test.ts index 5ffaad0b6..df2391887 100644 --- a/PingTestRunner/__tests__/integration/oidc.test.ts +++ b/PingTestRunner/__tests__/integration/oidc.test.ts @@ -23,6 +23,7 @@ const VALID_CONFIG = { discoveryEndpoint: 'https://example.com/.well-known/openid-configuration', clientId: 'test-client', redirectUri: 'org.forgerock.demo://oauth2redirect', + scopes: ['openid', 'profile'], }; describe('@ping-identity/rn-oidc — integration', () => { diff --git a/PingSampleApp/ios/PingSampleApp.xcodeproj/xcshareddata/xcschemes/RNPackagesTests.xcscheme b/PingTestRunner/ios/PingTestRunner.xcodeproj/xcshareddata/xcschemes/RNPackagesTests.xcscheme similarity index 74% rename from PingSampleApp/ios/PingSampleApp.xcodeproj/xcshareddata/xcschemes/RNPackagesTests.xcscheme rename to PingTestRunner/ios/PingTestRunner.xcodeproj/xcshareddata/xcschemes/RNPackagesTests.xcscheme index fc3fd0d3e..2150f7b08 100644 --- a/PingSampleApp/ios/PingSampleApp.xcodeproj/xcshareddata/xcschemes/RNPackagesTests.xcscheme +++ b/PingTestRunner/ios/PingTestRunner.xcodeproj/xcshareddata/xcschemes/RNPackagesTests.xcscheme @@ -16,23 +16,9 @@ - - - - + BuildableName = "PingTestRunner.app" + BlueprintName = "PingTestRunner" + ReferencedContainer = "container:PingTestRunner.xcodeproj"> @@ -47,23 +33,12 @@ + BuildableName = "PingTestRunner.app" + BlueprintName = "PingTestRunner" + ReferencedContainer = "container:PingTestRunner.xcodeproj"> - - - - @@ -136,9 +111,9 @@ + BuildableName = "PingTestRunner.app" + BlueprintName = "PingTestRunner" + ReferencedContainer = "container:PingTestRunner.xcodeproj"> @@ -153,9 +128,9 @@ + BuildableName = "PingTestRunner.app" + BlueprintName = "PingTestRunner" + ReferencedContainer = "container:PingTestRunner.xcodeproj"> diff --git a/PingTestRunner/package.json b/PingTestRunner/package.json index 949c97136..ed5fda9de 100644 --- a/PingTestRunner/package.json +++ b/PingTestRunner/package.json @@ -21,7 +21,6 @@ "test:e2e:ios": "sh -c 'export DETOX_SERVER_PORT=${DETOX_SERVER_PORT:-8099}; export NODE_NO_WARNINGS=1; yarn e2e:android:free-detox-port && detox test --configuration ios.sim \"$@\"' --", "build:e2e:android": "detox build --configuration android.emu", "build:e2e:ios": "detox build --configuration ios.sim", - "lint": "eslint --no-config-lookup --config ../eslint.config.mjs \"__tests__/**/*.{js,jsx,ts,tsx}\" \"e2e/**/*.{js,jsx,ts,tsx}\" \"*.{js,jsx,ts,tsx}\"", "typecheck": "tsc --noEmit -p tsconfig.json" }, "dependencies": { diff --git a/PingTestRunner/tsconfig.json b/PingTestRunner/tsconfig.json index f8e54c741..562fb9a72 100644 --- a/PingTestRunner/tsconfig.json +++ b/PingTestRunner/tsconfig.json @@ -1,7 +1,17 @@ { "compilerOptions": { - "jsx": "react" + "jsx": "react", + "paths": { + "@ping-identity/rn-browser": ["../packages/browser/src/index"], + "@ping-identity/rn-device-id": ["../packages/device-id/src/index"], + "@ping-identity/rn-device-profile": ["../packages/device-profile/src/index"], + "@ping-identity/rn-journey": ["../packages/journey/src/index"], + "@ping-identity/rn-logger": ["../packages/logger/src/index"], + "@ping-identity/rn-oidc": ["../packages/oidc/src/index"], + "@ping-identity/rn-storage": ["../packages/storage/src/index"], + "@ping-identity/rn-types": ["../packages/types/src/index"] + } }, "extends": "@react-native/typescript-config", - "include": ["App.tsx", "index.js", "__tests__/**/*", "e2e/**/*"] + "include": ["App.tsx", "index.js", "__tests__/**/*", "e2e/**/*", "scenarios/**/*"] } diff --git a/docs/browser/.nojekyll b/docs/browser/.nojekyll deleted file mode 100644 index e2ac6616a..000000000 --- a/docs/browser/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/browser/assets/hierarchy.js b/docs/browser/assets/hierarchy.js deleted file mode 100644 index 88636f05d..000000000 --- a/docs/browser/assets/hierarchy.js +++ /dev/null @@ -1 +0,0 @@ -window.hierarchyData = "eJyrVirKzy8pVrKKjtVRKkpNy0lNLsnMzwMKVNfWAgCbHgqm" \ No newline at end of file diff --git a/docs/browser/assets/highlight.css b/docs/browser/assets/highlight.css deleted file mode 100644 index efe5ddfb8..000000000 --- a/docs/browser/assets/highlight.css +++ /dev/null @@ -1,85 +0,0 @@ -:root { - --light-hl-0: #795E26; - --dark-hl-0: #DCDCAA; - --light-hl-1: #000000; - --dark-hl-1: #D4D4D4; - --light-hl-2: #A31515; - --dark-hl-2: #CE9178; - --light-hl-3: #AF00DB; - --dark-hl-3: #C586C0; - --light-hl-4: #001080; - --dark-hl-4: #9CDCFE; - --light-hl-5: #0000FF; - --dark-hl-5: #569CD6; - --light-hl-6: #0070C1; - --dark-hl-6: #4FC1FF; - --light-hl-7: #008000; - --dark-hl-7: #6A9955; - --light-hl-8: #267F99; - --dark-hl-8: #4EC9B0; - --light-code-background: #FFFFFF; - --dark-code-background: #1E1E1E; -} - -@media (prefers-color-scheme: light) { :root { - --hl-0: var(--light-hl-0); - --hl-1: var(--light-hl-1); - --hl-2: var(--light-hl-2); - --hl-3: var(--light-hl-3); - --hl-4: var(--light-hl-4); - --hl-5: var(--light-hl-5); - --hl-6: var(--light-hl-6); - --hl-7: var(--light-hl-7); - --hl-8: var(--light-hl-8); - --code-background: var(--light-code-background); -} } - -@media (prefers-color-scheme: dark) { :root { - --hl-0: var(--dark-hl-0); - --hl-1: var(--dark-hl-1); - --hl-2: var(--dark-hl-2); - --hl-3: var(--dark-hl-3); - --hl-4: var(--dark-hl-4); - --hl-5: var(--dark-hl-5); - --hl-6: var(--dark-hl-6); - --hl-7: var(--dark-hl-7); - --hl-8: var(--dark-hl-8); - --code-background: var(--dark-code-background); -} } - -:root[data-theme='light'] { - --hl-0: var(--light-hl-0); - --hl-1: var(--light-hl-1); - --hl-2: var(--light-hl-2); - --hl-3: var(--light-hl-3); - --hl-4: var(--light-hl-4); - --hl-5: var(--light-hl-5); - --hl-6: var(--light-hl-6); - --hl-7: var(--light-hl-7); - --hl-8: var(--light-hl-8); - --code-background: var(--light-code-background); -} - -:root[data-theme='dark'] { - --hl-0: var(--dark-hl-0); - --hl-1: var(--dark-hl-1); - --hl-2: var(--dark-hl-2); - --hl-3: var(--dark-hl-3); - --hl-4: var(--dark-hl-4); - --hl-5: var(--dark-hl-5); - --hl-6: var(--dark-hl-6); - --hl-7: var(--dark-hl-7); - --hl-8: var(--dark-hl-8); - --code-background: var(--dark-code-background); -} - -.hl-0 { color: var(--hl-0); } -.hl-1 { color: var(--hl-1); } -.hl-2 { color: var(--hl-2); } -.hl-3 { color: var(--hl-3); } -.hl-4 { color: var(--hl-4); } -.hl-5 { color: var(--hl-5); } -.hl-6 { color: var(--hl-6); } -.hl-7 { color: var(--hl-7); } -.hl-8 { color: var(--hl-8); } -pre, code { background: var(--code-background); } diff --git a/docs/browser/assets/icons.js b/docs/browser/assets/icons.js deleted file mode 100644 index 58882d76d..000000000 --- a/docs/browser/assets/icons.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - addIcons(); - function addIcons() { - if (document.readyState === "loading") return document.addEventListener("DOMContentLoaded", addIcons); - const svg = document.body.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "svg")); - svg.innerHTML = `MMNEPVFCICPMFPCPTTAAATR`; - svg.style.display = "none"; - if (location.protocol === "file:") updateUseElements(); - } - - function updateUseElements() { - document.querySelectorAll("use").forEach(el => { - if (el.getAttribute("href").includes("#icon-")) { - el.setAttribute("href", el.getAttribute("href").replace(/.*#/, "#")); - } - }); - } -})() \ No newline at end of file diff --git a/docs/browser/assets/icons.svg b/docs/browser/assets/icons.svg deleted file mode 100644 index 50ad5799d..000000000 --- a/docs/browser/assets/icons.svg +++ /dev/null @@ -1 +0,0 @@ -MMNEPVFCICPMFPCPTTAAATR \ No newline at end of file diff --git a/docs/browser/assets/main.js b/docs/browser/assets/main.js deleted file mode 100644 index 2363f64c2..000000000 --- a/docs/browser/assets/main.js +++ /dev/null @@ -1,60 +0,0 @@ -"use strict"; -window.translations={"copy":"Copy","copied":"Copied!","normally_hidden":"This member is normally hidden due to your filter settings.","hierarchy_expand":"Expand","hierarchy_collapse":"Collapse","folder":"Folder","kind_1":"Project","kind_2":"Module","kind_4":"Namespace","kind_8":"Enumeration","kind_16":"Enumeration Member","kind_32":"Variable","kind_64":"Function","kind_128":"Class","kind_256":"Interface","kind_512":"Constructor","kind_1024":"Property","kind_2048":"Method","kind_4096":"Call Signature","kind_8192":"Index Signature","kind_16384":"Constructor Signature","kind_32768":"Parameter","kind_65536":"Type Literal","kind_131072":"Type Parameter","kind_262144":"Accessor","kind_524288":"Get Signature","kind_1048576":"Set Signature","kind_2097152":"Type Alias","kind_4194304":"Reference","kind_8388608":"Document"}; -"use strict";(()=>{var De=Object.create;var le=Object.defineProperty;var Fe=Object.getOwnPropertyDescriptor;var Ne=Object.getOwnPropertyNames;var Ve=Object.getPrototypeOf,Be=Object.prototype.hasOwnProperty;var qe=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var je=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ne(e))!Be.call(t,i)&&i!==n&&le(t,i,{get:()=>e[i],enumerable:!(r=Fe(e,i))||r.enumerable});return t};var $e=(t,e,n)=>(n=t!=null?De(Ve(t)):{},je(e||!t||!t.__esModule?le(n,"default",{value:t,enumerable:!0}):n,t));var pe=qe((de,he)=>{(function(){var t=function(e){var n=new t.Builder;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),n.searchPipeline.add(t.stemmer),e.call(n,n),n.build()};t.version="2.3.9";t.utils={},t.utils.warn=function(e){return function(n){e.console&&console.warn&&console.warn(n)}}(this),t.utils.asString=function(e){return e==null?"":e.toString()},t.utils.clone=function(e){if(e==null)return e;for(var n=Object.create(null),r=Object.keys(e),i=0;i0){var d=t.utils.clone(n)||{};d.position=[a,c],d.index=s.length,s.push(new t.Token(r.slice(a,o),d))}a=o+1}}return s},t.tokenizer.separator=/[\s\-]+/;t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions=Object.create(null),t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. -`,e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(r){var i=t.Pipeline.registeredFunctions[r];if(i)n.add(i);else throw new Error("Cannot load unregistered function: "+r)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(n){t.Pipeline.warnIfFunctionNotRegistered(n),this._stack.push(n)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");r=r+1,this._stack.splice(r,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");this._stack.splice(r,0,n)},t.Pipeline.prototype.remove=function(e){var n=this._stack.indexOf(e);n!=-1&&this._stack.splice(n,1)},t.Pipeline.prototype.run=function(e){for(var n=this._stack.length,r=0;r1&&(oe&&(r=s),o!=e);)i=r-n,s=n+Math.floor(i/2),o=this.elements[s*2];if(o==e||o>e)return s*2;if(ol?d+=2:a==l&&(n+=r[c+1]*i[d+1],c+=2,d+=2);return n},t.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},t.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),n=1,r=0;n0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new t.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new t.TokenSet;s.node.edges["*"]=l}if(s.str.length==0&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var c=s.node.edges["*"];else{var c=new t.TokenSet;s.node.edges["*"]=c}s.str.length==1&&(c.final=!0),i.push({node:c,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var d=s.str.charAt(0),m=s.str.charAt(1),p;m in s.node.edges?p=s.node.edges[m]:(p=new t.TokenSet,s.node.edges[m]=p),s.str.length==1&&(p.final=!0),i.push({node:p,editsRemaining:s.editsRemaining-1,str:d+s.str.slice(2)})}}}return r},t.TokenSet.fromString=function(e){for(var n=new t.TokenSet,r=n,i=0,s=e.length;i=e;n--){var r=this.uncheckedNodes[n],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r.char]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}};t.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},t.Index.prototype.search=function(e){return this.query(function(n){var r=new t.QueryParser(e,n);r.parse()})},t.Index.prototype.query=function(e){for(var n=new t.Query(this.fields),r=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),l=0;l1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,n){var r=e[this._ref],i=Object.keys(this._fields);this._documents[r]=n||{},this.documentCount+=1;for(var s=0;s=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,n;do e=this.next(),n=e.charCodeAt(0);while(n>47&&n<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var n=e.next();if(n==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(n.charCodeAt(0)==92){e.escapeCharacter();continue}if(n==":")return t.QueryLexer.lexField;if(n=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(n=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(n=="+"&&e.width()===1||n=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(n.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,n){this.lexer=new t.QueryLexer(e),this.query=n,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var n=e.peekLexeme();if(n!=null)switch(n.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expected either a field or a term, found "+n.type;throw n.str.length>=1&&(r+=" with value '"+n.str+"'"),new t.QueryParseError(r,n.start,n.end)}},t.QueryParser.parsePresence=function(e){var n=e.consumeLexeme();if(n!=null){switch(n.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var r="unrecognised presence operator'"+n.str+"'";throw new t.QueryParseError(r,n.start,n.end)}var i=e.peekLexeme();if(i==null){var r="expecting term or field, found nothing";throw new t.QueryParseError(r,n.start,n.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(r,i.start,i.end)}}},t.QueryParser.parseField=function(e){var n=e.consumeLexeme();if(n!=null){if(e.query.allFields.indexOf(n.str)==-1){var r=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+n.str+"', possible fields: "+r;throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.fields=[n.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,n.start,n.end)}switch(s.type){case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseTerm=function(e){var n=e.consumeLexeme();if(n!=null){e.currentClause.term=n.str.toLowerCase(),n.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var r=e.peekLexeme();if(r==null){e.nextClause();return}switch(r.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+r.type+"'";throw new t.QueryParseError(i,r.start,r.end)}}},t.QueryParser.parseEditDistance=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="edit distance must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.editDistance=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="boost must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.boost=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},function(e,n){typeof define=="function"&&define.amd?define(n):typeof de=="object"?he.exports=n():e.lunr=n()}(this,function(){return t})})()});window.translations||={copy:"Copy",copied:"Copied!",normally_hidden:"This member is normally hidden due to your filter settings.",hierarchy_expand:"Expand",hierarchy_collapse:"Collapse",folder:"Folder",kind_1:"Project",kind_2:"Module",kind_4:"Namespace",kind_8:"Enumeration",kind_16:"Enumeration Member",kind_32:"Variable",kind_64:"Function",kind_128:"Class",kind_256:"Interface",kind_512:"Constructor",kind_1024:"Property",kind_2048:"Method",kind_4096:"Call Signature",kind_8192:"Index Signature",kind_16384:"Constructor Signature",kind_32768:"Parameter",kind_65536:"Type Literal",kind_131072:"Type Parameter",kind_262144:"Accessor",kind_524288:"Get Signature",kind_1048576:"Set Signature",kind_2097152:"Type Alias",kind_4194304:"Reference",kind_8388608:"Document"};var ce=[];function G(t,e){ce.push({selector:e,constructor:t})}var J=class{alwaysVisibleMember=null;constructor(){this.createComponents(document.body),this.ensureFocusedElementVisible(),this.listenForCodeCopies(),window.addEventListener("hashchange",()=>this.ensureFocusedElementVisible()),document.body.style.display||(this.ensureFocusedElementVisible(),this.updateIndexVisibility(),this.scrollToHash())}createComponents(e){ce.forEach(n=>{e.querySelectorAll(n.selector).forEach(r=>{r.dataset.hasInstance||(new n.constructor({el:r,app:this}),r.dataset.hasInstance=String(!0))})})}filterChanged(){this.ensureFocusedElementVisible()}showPage(){document.body.style.display&&(document.body.style.removeProperty("display"),this.ensureFocusedElementVisible(),this.updateIndexVisibility(),this.scrollToHash())}scrollToHash(){if(location.hash){let e=document.getElementById(location.hash.substring(1));if(!e)return;e.scrollIntoView({behavior:"instant",block:"start"})}}ensureActivePageVisible(){let e=document.querySelector(".tsd-navigation .current"),n=e?.parentElement;for(;n&&!n.classList.contains(".tsd-navigation");)n instanceof HTMLDetailsElement&&(n.open=!0),n=n.parentElement;if(e&&!ze(e)){let r=e.getBoundingClientRect().top-document.documentElement.clientHeight/4;document.querySelector(".site-menu").scrollTop=r,document.querySelector(".col-sidebar").scrollTop=r}}updateIndexVisibility(){let e=document.querySelector(".tsd-index-content"),n=e?.open;e&&(e.open=!0),document.querySelectorAll(".tsd-index-section").forEach(r=>{r.style.display="block";let i=Array.from(r.querySelectorAll(".tsd-index-link")).every(s=>s.offsetParent==null);r.style.display=i?"none":"block"}),e&&(e.open=n)}ensureFocusedElementVisible(){if(this.alwaysVisibleMember&&(this.alwaysVisibleMember.classList.remove("always-visible"),this.alwaysVisibleMember.firstElementChild.remove(),this.alwaysVisibleMember=null),!location.hash)return;let e=document.getElementById(location.hash.substring(1));if(!e)return;let n=e.parentElement;for(;n&&n.tagName!=="SECTION";)n=n.parentElement;if(!n)return;let r=n.offsetParent==null,i=n;for(;i!==document.body;)i instanceof HTMLDetailsElement&&(i.open=!0),i=i.parentElement;if(n.offsetParent==null){this.alwaysVisibleMember=n,n.classList.add("always-visible");let s=document.createElement("p");s.classList.add("warning"),s.textContent=window.translations.normally_hidden,n.prepend(s)}r&&e.scrollIntoView()}listenForCodeCopies(){document.querySelectorAll("pre > button").forEach(e=>{let n;e.addEventListener("click",()=>{e.previousElementSibling instanceof HTMLElement&&navigator.clipboard.writeText(e.previousElementSibling.innerText.trim()),e.textContent=window.translations.copied,e.classList.add("visible"),clearTimeout(n),n=setTimeout(()=>{e.classList.remove("visible"),n=setTimeout(()=>{e.textContent=window.translations.copy},100)},1e3)})})}};function ze(t){let e=t.getBoundingClientRect(),n=Math.max(document.documentElement.clientHeight,window.innerHeight);return!(e.bottom<0||e.top-n>=0)}var ue=(t,e=100)=>{let n;return()=>{clearTimeout(n),n=setTimeout(()=>t(),e)}};var ge=$e(pe(),1);async function A(t){let e=Uint8Array.from(atob(t),s=>s.charCodeAt(0)),r=new Blob([e]).stream().pipeThrough(new DecompressionStream("deflate")),i=await new Response(r).text();return JSON.parse(i)}async function fe(t,e){if(!window.searchData)return;let n=await A(window.searchData);t.data=n,t.index=ge.Index.load(n.index),e.classList.remove("loading"),e.classList.add("ready")}function ve(){let t=document.getElementById("tsd-search");if(!t)return;let e={base:document.documentElement.dataset.base+"/"},n=document.getElementById("tsd-search-script");t.classList.add("loading"),n&&(n.addEventListener("error",()=>{t.classList.remove("loading"),t.classList.add("failure")}),n.addEventListener("load",()=>{fe(e,t)}),fe(e,t));let r=document.querySelector("#tsd-search input"),i=document.querySelector("#tsd-search .results");if(!r||!i)throw new Error("The input field or the result list wrapper was not found");i.addEventListener("mouseup",()=>{re(t)}),r.addEventListener("focus",()=>t.classList.add("has-focus")),We(t,i,r,e)}function We(t,e,n,r){n.addEventListener("input",ue(()=>{Ue(t,e,n,r)},200)),n.addEventListener("keydown",i=>{i.key=="Enter"?Je(e,t):i.key=="ArrowUp"?(me(e,n,-1),i.preventDefault()):i.key==="ArrowDown"&&(me(e,n,1),i.preventDefault())}),document.body.addEventListener("keypress",i=>{i.altKey||i.ctrlKey||i.metaKey||!n.matches(":focus")&&i.key==="/"&&(i.preventDefault(),n.focus())}),document.body.addEventListener("keyup",i=>{t.classList.contains("has-focus")&&(i.key==="Escape"||!e.matches(":focus-within")&&!n.matches(":focus"))&&(n.blur(),re(t))})}function re(t){t.classList.remove("has-focus")}function Ue(t,e,n,r){if(!r.index||!r.data)return;e.textContent="";let i=n.value.trim(),s;if(i){let o=i.split(" ").map(a=>a.length?`*${a}*`:"").join(" ");s=r.index.search(o)}else s=[];for(let o=0;oa.score-o.score);for(let o=0,a=Math.min(10,s.length);o`,d=ye(l.name,i);globalThis.DEBUG_SEARCH_WEIGHTS&&(d+=` (score: ${s[o].score.toFixed(2)})`),l.parent&&(d=` - ${ye(l.parent,i)}.${d}`);let m=document.createElement("li");m.classList.value=l.classes??"";let p=document.createElement("a");p.href=r.base+l.url,p.innerHTML=c+d,m.append(p),p.addEventListener("focus",()=>{e.querySelector(".current")?.classList.remove("current"),m.classList.add("current")}),e.appendChild(m)}}function me(t,e,n){let r=t.querySelector(".current");if(!r)r=t.querySelector(n==1?"li:first-child":"li:last-child"),r&&r.classList.add("current");else{let i=r;if(n===1)do i=i.nextElementSibling??void 0;while(i instanceof HTMLElement&&i.offsetParent==null);else do i=i.previousElementSibling??void 0;while(i instanceof HTMLElement&&i.offsetParent==null);i?(r.classList.remove("current"),i.classList.add("current")):n===-1&&(r.classList.remove("current"),e.focus())}}function Je(t,e){let n=t.querySelector(".current");if(n||(n=t.querySelector("li:first-child")),n){let r=n.querySelector("a");r&&(window.location.href=r.href),re(e)}}function ye(t,e){if(e==="")return t;let n=t.toLocaleLowerCase(),r=e.toLocaleLowerCase(),i=[],s=0,o=n.indexOf(r);for(;o!=-1;)i.push(ne(t.substring(s,o)),`${ne(t.substring(o,o+r.length))}`),s=o+r.length,o=n.indexOf(r,s);return i.push(ne(t.substring(s))),i.join("")}var Ge={"&":"&","<":"<",">":">","'":"'",'"':"""};function ne(t){return t.replace(/[&<>"'"]/g,e=>Ge[e])}var I=class{el;app;constructor(e){this.el=e.el,this.app=e.app}};var H="mousedown",Ee="mousemove",B="mouseup",X={x:0,y:0},xe=!1,ie=!1,Xe=!1,D=!1,be=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);document.documentElement.classList.add(be?"is-mobile":"not-mobile");be&&"ontouchstart"in document.documentElement&&(Xe=!0,H="touchstart",Ee="touchmove",B="touchend");document.addEventListener(H,t=>{ie=!0,D=!1;let e=H=="touchstart"?t.targetTouches[0]:t;X.y=e.pageY||0,X.x=e.pageX||0});document.addEventListener(Ee,t=>{if(ie&&!D){let e=H=="touchstart"?t.targetTouches[0]:t,n=X.x-(e.pageX||0),r=X.y-(e.pageY||0);D=Math.sqrt(n*n+r*r)>10}});document.addEventListener(B,()=>{ie=!1});document.addEventListener("click",t=>{xe&&(t.preventDefault(),t.stopImmediatePropagation(),xe=!1)});var Y=class extends I{active;className;constructor(e){super(e),this.className=this.el.dataset.toggle||"",this.el.addEventListener(B,n=>this.onPointerUp(n)),this.el.addEventListener("click",n=>n.preventDefault()),document.addEventListener(H,n=>this.onDocumentPointerDown(n)),document.addEventListener(B,n=>this.onDocumentPointerUp(n))}setActive(e){if(this.active==e)return;this.active=e,document.documentElement.classList.toggle("has-"+this.className,e),this.el.classList.toggle("active",e);let n=(this.active?"to-has-":"from-has-")+this.className;document.documentElement.classList.add(n),setTimeout(()=>document.documentElement.classList.remove(n),500)}onPointerUp(e){D||(this.setActive(!0),e.preventDefault())}onDocumentPointerDown(e){if(this.active){if(e.target.closest(".col-sidebar, .tsd-filter-group"))return;this.setActive(!1)}}onDocumentPointerUp(e){if(!D&&this.active&&e.target.closest(".col-sidebar")){let n=e.target.closest("a");if(n){let r=window.location.href;r.indexOf("#")!=-1&&(r=r.substring(0,r.indexOf("#"))),n.href.substring(0,r.length)==r&&setTimeout(()=>this.setActive(!1),250)}}}};var se;try{se=localStorage}catch{se={getItem(){return null},setItem(){}}}var C=se;var Le=document.head.appendChild(document.createElement("style"));Le.dataset.for="filters";var Z=class extends I{key;value;constructor(e){super(e),this.key=`filter-${this.el.name}`,this.value=this.el.checked,this.el.addEventListener("change",()=>{this.setLocalStorage(this.el.checked)}),this.setLocalStorage(this.fromLocalStorage()),Le.innerHTML+=`html:not(.${this.key}) .tsd-is-${this.el.name} { display: none; } -`,this.app.updateIndexVisibility()}fromLocalStorage(){let e=C.getItem(this.key);return e?e==="true":this.el.checked}setLocalStorage(e){C.setItem(this.key,e.toString()),this.value=e,this.handleValueChange()}handleValueChange(){this.el.checked=this.value,document.documentElement.classList.toggle(this.key,this.value),this.app.filterChanged(),this.app.updateIndexVisibility()}};var oe=new Map,ae=class{open;accordions=[];key;constructor(e,n){this.key=e,this.open=n}add(e){this.accordions.push(e),e.open=this.open,e.addEventListener("toggle",()=>{this.toggle(e.open)})}toggle(e){for(let n of this.accordions)n.open=e;C.setItem(this.key,e.toString())}},K=class extends I{constructor(e){super(e);let n=this.el.querySelector("summary"),r=n.querySelector("a");r&&r.addEventListener("click",()=>{location.assign(r.href)});let i=`tsd-accordion-${n.dataset.key??n.textContent.trim().replace(/\s+/g,"-").toLowerCase()}`,s;if(oe.has(i))s=oe.get(i);else{let o=C.getItem(i),a=o?o==="true":this.el.open;s=new ae(i,a),oe.set(i,s)}s.add(this.el)}};function Se(t){let e=C.getItem("tsd-theme")||"os";t.value=e,we(e),t.addEventListener("change",()=>{C.setItem("tsd-theme",t.value),we(t.value)})}function we(t){document.documentElement.dataset.theme=t}var ee;function Ce(){let t=document.getElementById("tsd-nav-script");t&&(t.addEventListener("load",Te),Te())}async function Te(){let t=document.getElementById("tsd-nav-container");if(!t||!window.navigationData)return;let e=await A(window.navigationData);ee=document.documentElement.dataset.base,ee.endsWith("/")||(ee+="/"),t.innerHTML="";for(let n of e)Ie(n,t,[]);window.app.createComponents(t),window.app.showPage(),window.app.ensureActivePageVisible()}function Ie(t,e,n){let r=e.appendChild(document.createElement("li"));if(t.children){let i=[...n,t.text],s=r.appendChild(document.createElement("details"));s.className=t.class?`${t.class} tsd-accordion`:"tsd-accordion";let o=s.appendChild(document.createElement("summary"));o.className="tsd-accordion-summary",o.dataset.key=i.join("$"),o.innerHTML='',ke(t,o);let a=s.appendChild(document.createElement("div"));a.className="tsd-accordion-details";let l=a.appendChild(document.createElement("ul"));l.className="tsd-nested-navigation";for(let c of t.children)Ie(c,l,i)}else ke(t,r,t.class)}function ke(t,e,n){if(t.path){let r=e.appendChild(document.createElement("a"));if(r.href=ee+t.path,n&&(r.className=n),location.pathname===r.pathname&&!r.href.includes("#")&&r.classList.add("current"),t.kind){let i=window.translations[`kind_${t.kind}`].replaceAll('"',""");r.innerHTML=``}r.appendChild(document.createElement("span")).textContent=t.text}else{let r=e.appendChild(document.createElement("span")),i=window.translations.folder.replaceAll('"',""");r.innerHTML=``,r.appendChild(document.createElement("span")).textContent=t.text}}var te=document.documentElement.dataset.base;te.endsWith("/")||(te+="/");function Pe(){document.querySelector(".tsd-full-hierarchy")?Ye():document.querySelector(".tsd-hierarchy")&&Ze()}function Ye(){document.addEventListener("click",r=>{let i=r.target;for(;i.parentElement&&i.parentElement.tagName!="LI";)i=i.parentElement;i.dataset.dropdown&&(i.dataset.dropdown=String(i.dataset.dropdown!=="true"))});let t=new Map,e=new Set;for(let r of document.querySelectorAll(".tsd-full-hierarchy [data-refl]")){let i=r.querySelector("ul");t.has(r.dataset.refl)?e.add(r.dataset.refl):i&&t.set(r.dataset.refl,i)}for(let r of e)n(r);function n(r){let i=t.get(r).cloneNode(!0);i.querySelectorAll("[id]").forEach(s=>{s.removeAttribute("id")}),i.querySelectorAll("[data-dropdown]").forEach(s=>{s.dataset.dropdown="false"});for(let s of document.querySelectorAll(`[data-refl="${r}"]`)){let o=tt(),a=s.querySelector("ul");s.insertBefore(o,a),o.dataset.dropdown=String(!!a),a||s.appendChild(i.cloneNode(!0))}}}function Ze(){let t=document.getElementById("tsd-hierarchy-script");t&&(t.addEventListener("load",Qe),Qe())}async function Qe(){let t=document.querySelector(".tsd-panel.tsd-hierarchy:has(h4 a)");if(!t||!window.hierarchyData)return;let e=+t.dataset.refl,n=await A(window.hierarchyData),r=t.querySelector("ul"),i=document.createElement("ul");if(i.classList.add("tsd-hierarchy"),Ke(i,n,e),r.querySelectorAll("li").length==i.querySelectorAll("li").length)return;let s=document.createElement("span");s.classList.add("tsd-hierarchy-toggle"),s.textContent=window.translations.hierarchy_expand,t.querySelector("h4 a")?.insertAdjacentElement("afterend",s),s.insertAdjacentText("beforebegin",", "),s.addEventListener("click",()=>{s.textContent===window.translations.hierarchy_expand?(r.insertAdjacentElement("afterend",i),r.remove(),s.textContent=window.translations.hierarchy_collapse):(i.insertAdjacentElement("afterend",r),i.remove(),s.textContent=window.translations.hierarchy_expand)})}function Ke(t,e,n){let r=e.roots.filter(i=>et(e,i,n));for(let i of r)t.appendChild(_e(e,i,n))}function _e(t,e,n,r=new Set){if(r.has(e))return;r.add(e);let i=t.reflections[e],s=document.createElement("li");if(s.classList.add("tsd-hierarchy-item"),e===n){let o=s.appendChild(document.createElement("span"));o.textContent=i.name,o.classList.add("tsd-hierarchy-target")}else{for(let a of i.uniqueNameParents||[]){let l=t.reflections[a],c=s.appendChild(document.createElement("a"));c.textContent=l.name,c.href=te+l.url,c.className=l.class+" tsd-signature-type",s.append(document.createTextNode("."))}let o=s.appendChild(document.createElement("a"));o.textContent=t.reflections[e].name,o.href=te+i.url,o.className=i.class+" tsd-signature-type"}if(i.children){let o=s.appendChild(document.createElement("ul"));o.classList.add("tsd-hierarchy");for(let a of i.children){let l=_e(t,a,n,r);l&&o.appendChild(l)}}return r.delete(e),s}function et(t,e,n){if(e===n)return!0;let r=new Set,i=[t.reflections[e]];for(;i.length;){let s=i.pop();if(!r.has(s)){r.add(s);for(let o of s.children||[]){if(o===n)return!0;i.push(t.reflections[o])}}}return!1}function tt(){let t=document.createElementNS("http://www.w3.org/2000/svg","svg");return t.setAttribute("width","20"),t.setAttribute("height","20"),t.setAttribute("viewBox","0 0 24 24"),t.setAttribute("fill","none"),t.innerHTML='',t}G(Y,"a[data-toggle]");G(K,".tsd-accordion");G(Z,".tsd-filter-item input[type=checkbox]");var Oe=document.getElementById("tsd-theme");Oe&&Se(Oe);var nt=new J;Object.defineProperty(window,"app",{value:nt});ve();Ce();Pe();})(); -/*! Bundled license information: - -lunr/lunr.js: - (** - * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 - * Copyright (C) 2020 Oliver Nightingale - * @license MIT - *) - (*! - * lunr.utils - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Set - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.tokenizer - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Pipeline - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Vector - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.stemmer - * Copyright (C) 2020 Oliver Nightingale - * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt - *) - (*! - * lunr.stopWordFilter - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.trimmer - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.TokenSet - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Index - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Builder - * Copyright (C) 2020 Oliver Nightingale - *) -*/ diff --git a/docs/browser/assets/navigation.js b/docs/browser/assets/navigation.js deleted file mode 100644 index 934c7c979..000000000 --- a/docs/browser/assets/navigation.js +++ /dev/null @@ -1 +0,0 @@ -window.navigationData = "eJyV0T0PgjAQBuD/0pmIEj8io8TBiURH42DgECK2zfWIGuN/F6qJBSngeu97Ty/p/sEIbsR8tkJxVYCB4El2Yg6TR0rLMd0lKLcWjlK65GXjnPGY+d54uZjMvKfTlNaIAi2Qzv5xAhFDl1XlA71QAg8lZYIri2g0BppbUEVOFu4d9kmbcNd/YGupT470rxUIn9UvmhQ80oTb7NTN+dTgRPlyG1HNO9YQFFDHBWb+wxxemMLo/A==" \ No newline at end of file diff --git a/docs/browser/assets/search.js b/docs/browser/assets/search.js deleted file mode 100644 index 9866a0fae..000000000 --- a/docs/browser/assets/search.js +++ /dev/null @@ -1 +0,0 @@ -window.searchData = "eJytl8lu2zAQht+FvQqOR9Zi+dighxyKAE3Ti2EEtsQ0QrQYlNwFht+9Q0oySZNyqKSXIFz+b8acn4uOhNW/G7JaH8lrXmVkFQUeqbYlJSuS1tVz/vPA6Gc+hzLikQMrcOD5UKVtXlfNzeWU2UtbFjgvLbZNQ5FLyMmzoBltaDuOVYddkfWeVhYU776K8OdJDKF/5vRhb8UvOwPbv3va3GhjV6kw92Vq2ypjdZ69Cfv09MSH5HQF7ZH9ltGqvcxw1mnGQud14xq2m/qukCNr+IWxmtnDi6H31EUIb+uMXuHy4Unsu/uHXn+PdrnfC+tcBLDOcffArtN+5wvnCu4L00vbTmovkB1y3Rs996u5mK4plZ30QymN1Hm8EB+pQrotit02fX1kxUP6QssRF4386kGNimZQX90v7rVgNMsZTdtHlk9KadAdhO4/JTN6aIwk4XByvMcC32hzKFp7Jt2YrfAbjyCU/iGrI/lFWYMhsd+fLWYJznzOaZHx+66LhdK6LHmmHsnq9CD+3fTTfuC61oxP7mbfzIm3nnt+MPPjeLPx1oNYDIiOgSF7hBCwBTYhGELQhD62fJvQN4S+Jlxga2ETLgzhQhMG2ApswsAQBpowxFboQTKDKNGEoSEMNWGErcgWMTKEkSaMsRXbhLEhjDXhEltLm3BpCJeaEB20TmzCxBAmugG4H8DqHTDNAxfuEfax+8diIN1BwH0BVg+BaSLQXQTcG2D1EZhGAt1JEIw5AkwvgW4m4BYBqw/B9FPfJXY+bvmWZnfdCYAb9/yYOpKn/lw4H3RHEuCf00meAthSDgI+xgP1t13aPwklaSFJiykk2j2MJCiSoGgyKBW3sITFEhZPgZUXHPTrGYSWnEDij+56uLwVIChAmAJk/VWgsJQqYv0nsLp3lCQlEpQ4cSzPACUvX8nLd+MNH1G74YNI4pQSuFVA3MVSH0p92CnAzfPIuV7MpSQvnYjdB5oEKAvltk7aW0dZcWUXgts2FN+XltVW/fk2CM+bfb6nRV6hYr05nf4BQZgMuw=="; \ No newline at end of file diff --git a/docs/browser/assets/style.css b/docs/browser/assets/style.css deleted file mode 100644 index 2ab8b836e..000000000 --- a/docs/browser/assets/style.css +++ /dev/null @@ -1,1611 +0,0 @@ -@layer typedoc { - :root { - /* Light */ - --light-color-background: #f2f4f8; - --light-color-background-secondary: #eff0f1; - --light-color-warning-text: #222; - --light-color-background-warning: #e6e600; - --light-color-accent: #c5c7c9; - --light-color-active-menu-item: var(--light-color-accent); - --light-color-text: #222; - --light-color-text-aside: #6e6e6e; - - --light-color-icon-background: var(--light-color-background); - --light-color-icon-text: var(--light-color-text); - - --light-color-comment-tag-text: var(--light-color-text); - --light-color-comment-tag: var(--light-color-background); - - --light-color-link: #1f70c2; - --light-color-focus-outline: #3584e4; - - --light-color-ts-keyword: #056bd6; - --light-color-ts-project: #b111c9; - --light-color-ts-module: var(--light-color-ts-project); - --light-color-ts-namespace: var(--light-color-ts-project); - --light-color-ts-enum: #7e6f15; - --light-color-ts-enum-member: var(--light-color-ts-enum); - --light-color-ts-variable: #4760ec; - --light-color-ts-function: #572be7; - --light-color-ts-class: #1f70c2; - --light-color-ts-interface: #108024; - --light-color-ts-constructor: var(--light-color-ts-class); - --light-color-ts-property: #9f5f30; - --light-color-ts-method: #be3989; - --light-color-ts-reference: #ff4d82; - --light-color-ts-call-signature: var(--light-color-ts-method); - --light-color-ts-index-signature: var(--light-color-ts-property); - --light-color-ts-constructor-signature: var( - --light-color-ts-constructor - ); - --light-color-ts-parameter: var(--light-color-ts-variable); - /* type literal not included as links will never be generated to it */ - --light-color-ts-type-parameter: #a55c0e; - --light-color-ts-accessor: #c73c3c; - --light-color-ts-get-signature: var(--light-color-ts-accessor); - --light-color-ts-set-signature: var(--light-color-ts-accessor); - --light-color-ts-type-alias: #d51270; - /* reference not included as links will be colored with the kind that it points to */ - --light-color-document: #000000; - - --light-color-alert-note: #0969d9; - --light-color-alert-tip: #1a7f37; - --light-color-alert-important: #8250df; - --light-color-alert-warning: #9a6700; - --light-color-alert-caution: #cf222e; - - --light-external-icon: url("data:image/svg+xml;utf8,"); - --light-color-scheme: light; - - /* Dark */ - --dark-color-background: #2b2e33; - --dark-color-background-secondary: #1e2024; - --dark-color-background-warning: #bebe00; - --dark-color-warning-text: #222; - --dark-color-accent: #9096a2; - --dark-color-active-menu-item: #5d5d6a; - --dark-color-text: #f5f5f5; - --dark-color-text-aside: #dddddd; - - --dark-color-icon-background: var(--dark-color-background-secondary); - --dark-color-icon-text: var(--dark-color-text); - - --dark-color-comment-tag-text: var(--dark-color-text); - --dark-color-comment-tag: var(--dark-color-background); - - --dark-color-link: #00aff4; - --dark-color-focus-outline: #4c97f2; - - --dark-color-ts-keyword: #3399ff; - --dark-color-ts-project: #e358ff; - --dark-color-ts-module: var(--dark-color-ts-project); - --dark-color-ts-namespace: var(--dark-color-ts-project); - --dark-color-ts-enum: #f4d93e; - --dark-color-ts-enum-member: var(--dark-color-ts-enum); - --dark-color-ts-variable: #798dff; - --dark-color-ts-function: #a280ff; - --dark-color-ts-class: #8ac4ff; - --dark-color-ts-interface: #6cff87; - --dark-color-ts-constructor: var(--dark-color-ts-class); - --dark-color-ts-property: #ff984d; - --dark-color-ts-method: #ff4db8; - --dark-color-ts-reference: #ff4d82; - --dark-color-ts-call-signature: var(--dark-color-ts-method); - --dark-color-ts-index-signature: var(--dark-color-ts-property); - --dark-color-ts-constructor-signature: var(--dark-color-ts-constructor); - --dark-color-ts-parameter: var(--dark-color-ts-variable); - /* type literal not included as links will never be generated to it */ - --dark-color-ts-type-parameter: #e07d13; - --dark-color-ts-accessor: #ff6060; - --dark-color-ts-get-signature: var(--dark-color-ts-accessor); - --dark-color-ts-set-signature: var(--dark-color-ts-accessor); - --dark-color-ts-type-alias: #ff6492; - /* reference not included as links will be colored with the kind that it points to */ - --dark-color-document: #ffffff; - - --dark-color-alert-note: #0969d9; - --dark-color-alert-tip: #1a7f37; - --dark-color-alert-important: #8250df; - --dark-color-alert-warning: #9a6700; - --dark-color-alert-caution: #cf222e; - - --dark-external-icon: url("data:image/svg+xml;utf8,"); - --dark-color-scheme: dark; - } - - @media (prefers-color-scheme: light) { - :root { - --color-background: var(--light-color-background); - --color-background-secondary: var( - --light-color-background-secondary - ); - --color-background-warning: var(--light-color-background-warning); - --color-warning-text: var(--light-color-warning-text); - --color-accent: var(--light-color-accent); - --color-active-menu-item: var(--light-color-active-menu-item); - --color-text: var(--light-color-text); - --color-text-aside: var(--light-color-text-aside); - - --color-icon-background: var(--light-color-icon-background); - --color-icon-text: var(--light-color-icon-text); - - --color-comment-tag-text: var(--light-color-text); - --color-comment-tag: var(--light-color-background); - - --color-link: var(--light-color-link); - --color-focus-outline: var(--light-color-focus-outline); - - --color-ts-keyword: var(--light-color-ts-keyword); - --color-ts-project: var(--light-color-ts-project); - --color-ts-module: var(--light-color-ts-module); - --color-ts-namespace: var(--light-color-ts-namespace); - --color-ts-enum: var(--light-color-ts-enum); - --color-ts-enum-member: var(--light-color-ts-enum-member); - --color-ts-variable: var(--light-color-ts-variable); - --color-ts-function: var(--light-color-ts-function); - --color-ts-class: var(--light-color-ts-class); - --color-ts-interface: var(--light-color-ts-interface); - --color-ts-constructor: var(--light-color-ts-constructor); - --color-ts-property: var(--light-color-ts-property); - --color-ts-method: var(--light-color-ts-method); - --color-ts-reference: var(--light-color-ts-reference); - --color-ts-call-signature: var(--light-color-ts-call-signature); - --color-ts-index-signature: var(--light-color-ts-index-signature); - --color-ts-constructor-signature: var( - --light-color-ts-constructor-signature - ); - --color-ts-parameter: var(--light-color-ts-parameter); - --color-ts-type-parameter: var(--light-color-ts-type-parameter); - --color-ts-accessor: var(--light-color-ts-accessor); - --color-ts-get-signature: var(--light-color-ts-get-signature); - --color-ts-set-signature: var(--light-color-ts-set-signature); - --color-ts-type-alias: var(--light-color-ts-type-alias); - --color-document: var(--light-color-document); - - --color-alert-note: var(--light-color-alert-note); - --color-alert-tip: var(--light-color-alert-tip); - --color-alert-important: var(--light-color-alert-important); - --color-alert-warning: var(--light-color-alert-warning); - --color-alert-caution: var(--light-color-alert-caution); - - --external-icon: var(--light-external-icon); - --color-scheme: var(--light-color-scheme); - } - } - - @media (prefers-color-scheme: dark) { - :root { - --color-background: var(--dark-color-background); - --color-background-secondary: var( - --dark-color-background-secondary - ); - --color-background-warning: var(--dark-color-background-warning); - --color-warning-text: var(--dark-color-warning-text); - --color-accent: var(--dark-color-accent); - --color-active-menu-item: var(--dark-color-active-menu-item); - --color-text: var(--dark-color-text); - --color-text-aside: var(--dark-color-text-aside); - - --color-icon-background: var(--dark-color-icon-background); - --color-icon-text: var(--dark-color-icon-text); - - --color-comment-tag-text: var(--dark-color-text); - --color-comment-tag: var(--dark-color-background); - - --color-link: var(--dark-color-link); - --color-focus-outline: var(--dark-color-focus-outline); - - --color-ts-keyword: var(--dark-color-ts-keyword); - --color-ts-project: var(--dark-color-ts-project); - --color-ts-module: var(--dark-color-ts-module); - --color-ts-namespace: var(--dark-color-ts-namespace); - --color-ts-enum: var(--dark-color-ts-enum); - --color-ts-enum-member: var(--dark-color-ts-enum-member); - --color-ts-variable: var(--dark-color-ts-variable); - --color-ts-function: var(--dark-color-ts-function); - --color-ts-class: var(--dark-color-ts-class); - --color-ts-interface: var(--dark-color-ts-interface); - --color-ts-constructor: var(--dark-color-ts-constructor); - --color-ts-property: var(--dark-color-ts-property); - --color-ts-method: var(--dark-color-ts-method); - --color-ts-reference: var(--dark-color-ts-reference); - --color-ts-call-signature: var(--dark-color-ts-call-signature); - --color-ts-index-signature: var(--dark-color-ts-index-signature); - --color-ts-constructor-signature: var( - --dark-color-ts-constructor-signature - ); - --color-ts-parameter: var(--dark-color-ts-parameter); - --color-ts-type-parameter: var(--dark-color-ts-type-parameter); - --color-ts-accessor: var(--dark-color-ts-accessor); - --color-ts-get-signature: var(--dark-color-ts-get-signature); - --color-ts-set-signature: var(--dark-color-ts-set-signature); - --color-ts-type-alias: var(--dark-color-ts-type-alias); - --color-document: var(--dark-color-document); - - --color-alert-note: var(--dark-color-alert-note); - --color-alert-tip: var(--dark-color-alert-tip); - --color-alert-important: var(--dark-color-alert-important); - --color-alert-warning: var(--dark-color-alert-warning); - --color-alert-caution: var(--dark-color-alert-caution); - - --external-icon: var(--dark-external-icon); - --color-scheme: var(--dark-color-scheme); - } - } - - html { - color-scheme: var(--color-scheme); - } - - body { - margin: 0; - } - - :root[data-theme="light"] { - --color-background: var(--light-color-background); - --color-background-secondary: var(--light-color-background-secondary); - --color-background-warning: var(--light-color-background-warning); - --color-warning-text: var(--light-color-warning-text); - --color-icon-background: var(--light-color-icon-background); - --color-accent: var(--light-color-accent); - --color-active-menu-item: var(--light-color-active-menu-item); - --color-text: var(--light-color-text); - --color-text-aside: var(--light-color-text-aside); - --color-icon-text: var(--light-color-icon-text); - - --color-comment-tag-text: var(--light-color-text); - --color-comment-tag: var(--light-color-background); - - --color-link: var(--light-color-link); - --color-focus-outline: var(--light-color-focus-outline); - - --color-ts-keyword: var(--light-color-ts-keyword); - --color-ts-project: var(--light-color-ts-project); - --color-ts-module: var(--light-color-ts-module); - --color-ts-namespace: var(--light-color-ts-namespace); - --color-ts-enum: var(--light-color-ts-enum); - --color-ts-enum-member: var(--light-color-ts-enum-member); - --color-ts-variable: var(--light-color-ts-variable); - --color-ts-function: var(--light-color-ts-function); - --color-ts-class: var(--light-color-ts-class); - --color-ts-interface: var(--light-color-ts-interface); - --color-ts-constructor: var(--light-color-ts-constructor); - --color-ts-property: var(--light-color-ts-property); - --color-ts-method: var(--light-color-ts-method); - --color-ts-reference: var(--light-color-ts-reference); - --color-ts-call-signature: var(--light-color-ts-call-signature); - --color-ts-index-signature: var(--light-color-ts-index-signature); - --color-ts-constructor-signature: var( - --light-color-ts-constructor-signature - ); - --color-ts-parameter: var(--light-color-ts-parameter); - --color-ts-type-parameter: var(--light-color-ts-type-parameter); - --color-ts-accessor: var(--light-color-ts-accessor); - --color-ts-get-signature: var(--light-color-ts-get-signature); - --color-ts-set-signature: var(--light-color-ts-set-signature); - --color-ts-type-alias: var(--light-color-ts-type-alias); - --color-document: var(--light-color-document); - - --color-note: var(--light-color-note); - --color-tip: var(--light-color-tip); - --color-important: var(--light-color-important); - --color-warning: var(--light-color-warning); - --color-caution: var(--light-color-caution); - - --external-icon: var(--light-external-icon); - --color-scheme: var(--light-color-scheme); - } - - :root[data-theme="dark"] { - --color-background: var(--dark-color-background); - --color-background-secondary: var(--dark-color-background-secondary); - --color-background-warning: var(--dark-color-background-warning); - --color-warning-text: var(--dark-color-warning-text); - --color-icon-background: var(--dark-color-icon-background); - --color-accent: var(--dark-color-accent); - --color-active-menu-item: var(--dark-color-active-menu-item); - --color-text: var(--dark-color-text); - --color-text-aside: var(--dark-color-text-aside); - --color-icon-text: var(--dark-color-icon-text); - - --color-comment-tag-text: var(--dark-color-text); - --color-comment-tag: var(--dark-color-background); - - --color-link: var(--dark-color-link); - --color-focus-outline: var(--dark-color-focus-outline); - - --color-ts-keyword: var(--dark-color-ts-keyword); - --color-ts-project: var(--dark-color-ts-project); - --color-ts-module: var(--dark-color-ts-module); - --color-ts-namespace: var(--dark-color-ts-namespace); - --color-ts-enum: var(--dark-color-ts-enum); - --color-ts-enum-member: var(--dark-color-ts-enum-member); - --color-ts-variable: var(--dark-color-ts-variable); - --color-ts-function: var(--dark-color-ts-function); - --color-ts-class: var(--dark-color-ts-class); - --color-ts-interface: var(--dark-color-ts-interface); - --color-ts-constructor: var(--dark-color-ts-constructor); - --color-ts-property: var(--dark-color-ts-property); - --color-ts-method: var(--dark-color-ts-method); - --color-ts-reference: var(--dark-color-ts-reference); - --color-ts-call-signature: var(--dark-color-ts-call-signature); - --color-ts-index-signature: var(--dark-color-ts-index-signature); - --color-ts-constructor-signature: var( - --dark-color-ts-constructor-signature - ); - --color-ts-parameter: var(--dark-color-ts-parameter); - --color-ts-type-parameter: var(--dark-color-ts-type-parameter); - --color-ts-accessor: var(--dark-color-ts-accessor); - --color-ts-get-signature: var(--dark-color-ts-get-signature); - --color-ts-set-signature: var(--dark-color-ts-set-signature); - --color-ts-type-alias: var(--dark-color-ts-type-alias); - --color-document: var(--dark-color-document); - - --color-note: var(--dark-color-note); - --color-tip: var(--dark-color-tip); - --color-important: var(--dark-color-important); - --color-warning: var(--dark-color-warning); - --color-caution: var(--dark-color-caution); - - --external-icon: var(--dark-external-icon); - --color-scheme: var(--dark-color-scheme); - } - - *:focus-visible, - .tsd-accordion-summary:focus-visible svg { - outline: 2px solid var(--color-focus-outline); - } - - .always-visible, - .always-visible .tsd-signatures { - display: inherit !important; - } - - h1, - h2, - h3, - h4, - h5, - h6 { - line-height: 1.2; - } - - h1 { - font-size: 1.875rem; - margin: 0.67rem 0; - } - - h2 { - font-size: 1.5rem; - margin: 0.83rem 0; - } - - h3 { - font-size: 1.25rem; - margin: 1rem 0; - } - - h4 { - font-size: 1.05rem; - margin: 1.33rem 0; - } - - h5 { - font-size: 1rem; - margin: 1.5rem 0; - } - - h6 { - font-size: 0.875rem; - margin: 2.33rem 0; - } - - dl, - menu, - ol, - ul { - margin: 1em 0; - } - - dd { - margin: 0 0 0 34px; - } - - .container { - max-width: 1700px; - padding: 0 2rem; - } - - /* Footer */ - footer { - border-top: 1px solid var(--color-accent); - padding-top: 1rem; - padding-bottom: 1rem; - max-height: 3.5rem; - } - footer > p { - margin: 0 1em; - } - - .container-main { - margin: 0 auto; - /* toolbar, footer, margin */ - min-height: calc(100vh - 41px - 56px - 4rem); - } - - @keyframes fade-in { - from { - opacity: 0; - } - to { - opacity: 1; - } - } - @keyframes fade-out { - from { - opacity: 1; - visibility: visible; - } - to { - opacity: 0; - } - } - @keyframes fade-in-delayed { - 0% { - opacity: 0; - } - 33% { - opacity: 0; - } - 100% { - opacity: 1; - } - } - @keyframes fade-out-delayed { - 0% { - opacity: 1; - visibility: visible; - } - 66% { - opacity: 0; - } - 100% { - opacity: 0; - } - } - @keyframes pop-in-from-right { - from { - transform: translate(100%, 0); - } - to { - transform: translate(0, 0); - } - } - @keyframes pop-out-to-right { - from { - transform: translate(0, 0); - visibility: visible; - } - to { - transform: translate(100%, 0); - } - } - body { - background: var(--color-background); - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", - Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; - font-size: 16px; - color: var(--color-text); - } - - a { - color: var(--color-link); - text-decoration: none; - } - a:hover { - text-decoration: underline; - } - a.external[target="_blank"] { - background-image: var(--external-icon); - background-position: top 3px right; - background-repeat: no-repeat; - padding-right: 13px; - } - a.tsd-anchor-link { - color: var(--color-text); - } - - code, - pre { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; - padding: 0.2em; - margin: 0; - font-size: 0.875rem; - border-radius: 0.8em; - } - - pre { - position: relative; - white-space: pre-wrap; - word-wrap: break-word; - padding: 10px; - border: 1px solid var(--color-accent); - margin-bottom: 8px; - } - pre code { - padding: 0; - font-size: 100%; - } - pre > button { - position: absolute; - top: 10px; - right: 10px; - opacity: 0; - transition: opacity 0.1s; - box-sizing: border-box; - } - pre:hover > button, - pre > button.visible { - opacity: 1; - } - - blockquote { - margin: 1em 0; - padding-left: 1em; - border-left: 4px solid gray; - } - - .tsd-typography { - line-height: 1.333em; - } - .tsd-typography ul { - list-style: square; - padding: 0 0 0 20px; - margin: 0; - } - .tsd-typography .tsd-index-panel h3, - .tsd-index-panel .tsd-typography h3, - .tsd-typography h4, - .tsd-typography h5, - .tsd-typography h6 { - font-size: 1em; - } - .tsd-typography h5, - .tsd-typography h6 { - font-weight: normal; - } - .tsd-typography p, - .tsd-typography ul, - .tsd-typography ol { - margin: 1em 0; - } - .tsd-typography table { - border-collapse: collapse; - border: none; - } - .tsd-typography td, - .tsd-typography th { - padding: 6px 13px; - border: 1px solid var(--color-accent); - } - .tsd-typography thead, - .tsd-typography tr:nth-child(even) { - background-color: var(--color-background-secondary); - } - - .tsd-alert { - padding: 8px 16px; - margin-bottom: 16px; - border-left: 0.25em solid var(--alert-color); - } - .tsd-alert blockquote > :last-child, - .tsd-alert > :last-child { - margin-bottom: 0; - } - .tsd-alert-title { - color: var(--alert-color); - display: inline-flex; - align-items: center; - } - .tsd-alert-title span { - margin-left: 4px; - } - - .tsd-alert-note { - --alert-color: var(--color-alert-note); - } - .tsd-alert-tip { - --alert-color: var(--color-alert-tip); - } - .tsd-alert-important { - --alert-color: var(--color-alert-important); - } - .tsd-alert-warning { - --alert-color: var(--color-alert-warning); - } - .tsd-alert-caution { - --alert-color: var(--color-alert-caution); - } - - .tsd-breadcrumb { - margin: 0; - padding: 0; - color: var(--color-text-aside); - } - .tsd-breadcrumb a { - color: var(--color-text-aside); - text-decoration: none; - } - .tsd-breadcrumb a:hover { - text-decoration: underline; - } - .tsd-breadcrumb li { - display: inline; - } - .tsd-breadcrumb li:after { - content: " / "; - } - - .tsd-comment-tags { - display: flex; - flex-direction: column; - } - dl.tsd-comment-tag-group { - display: flex; - align-items: center; - overflow: hidden; - margin: 0.5em 0; - } - dl.tsd-comment-tag-group dt { - display: flex; - margin-right: 0.5em; - font-size: 0.875em; - font-weight: normal; - } - dl.tsd-comment-tag-group dd { - margin: 0; - } - code.tsd-tag { - padding: 0.25em 0.4em; - border: 0.1em solid var(--color-accent); - margin-right: 0.25em; - font-size: 70%; - } - h1 code.tsd-tag:first-of-type { - margin-left: 0.25em; - } - - dl.tsd-comment-tag-group dd:before, - dl.tsd-comment-tag-group dd:after { - content: " "; - } - dl.tsd-comment-tag-group dd pre, - dl.tsd-comment-tag-group dd:after { - clear: both; - } - dl.tsd-comment-tag-group p { - margin: 0; - } - - .tsd-panel.tsd-comment .lead { - font-size: 1.1em; - line-height: 1.333em; - margin-bottom: 2em; - } - .tsd-panel.tsd-comment .lead:last-child { - margin-bottom: 0; - } - - .tsd-filter-visibility h4 { - font-size: 1rem; - padding-top: 0.75rem; - padding-bottom: 0.5rem; - margin: 0; - } - .tsd-filter-item:not(:last-child) { - margin-bottom: 0.5rem; - } - .tsd-filter-input { - display: flex; - width: -moz-fit-content; - width: fit-content; - align-items: center; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - cursor: pointer; - } - .tsd-filter-input input[type="checkbox"] { - cursor: pointer; - position: absolute; - width: 1.5em; - height: 1.5em; - opacity: 0; - } - .tsd-filter-input input[type="checkbox"]:disabled { - pointer-events: none; - } - .tsd-filter-input svg { - cursor: pointer; - width: 1.5em; - height: 1.5em; - margin-right: 0.5em; - border-radius: 0.33em; - /* Leaving this at full opacity breaks event listeners on Firefox. - Don't remove unless you know what you're doing. */ - opacity: 0.99; - } - .tsd-filter-input input[type="checkbox"]:focus-visible + svg { - outline: 2px solid var(--color-focus-outline); - } - .tsd-checkbox-background { - fill: var(--color-accent); - } - input[type="checkbox"]:checked ~ svg .tsd-checkbox-checkmark { - stroke: var(--color-text); - } - .tsd-filter-input input:disabled ~ svg > .tsd-checkbox-background { - fill: var(--color-background); - stroke: var(--color-accent); - stroke-width: 0.25rem; - } - .tsd-filter-input input:disabled ~ svg > .tsd-checkbox-checkmark { - stroke: var(--color-accent); - } - - .settings-label { - font-weight: bold; - text-transform: uppercase; - display: inline-block; - } - - .tsd-filter-visibility .settings-label { - margin: 0.75rem 0 0.5rem 0; - } - - .tsd-theme-toggle .settings-label { - margin: 0.75rem 0.75rem 0 0; - } - - .tsd-hierarchy h4 label:hover span { - text-decoration: underline; - } - - .tsd-hierarchy { - list-style: square; - margin: 0; - } - .tsd-hierarchy-target { - font-weight: bold; - } - .tsd-hierarchy-toggle { - color: var(--color-link); - cursor: pointer; - } - - .tsd-full-hierarchy:not(:last-child) { - margin-bottom: 1em; - padding-bottom: 1em; - border-bottom: 1px solid var(--color-accent); - } - .tsd-full-hierarchy, - .tsd-full-hierarchy ul { - list-style: none; - margin: 0; - padding: 0; - } - .tsd-full-hierarchy ul { - padding-left: 1.5rem; - } - .tsd-full-hierarchy a { - padding: 0.25rem 0 !important; - font-size: 1rem; - display: inline-flex; - align-items: center; - color: var(--color-text); - } - .tsd-full-hierarchy svg[data-dropdown] { - cursor: pointer; - } - .tsd-full-hierarchy svg[data-dropdown="false"] { - transform: rotate(-90deg); - } - .tsd-full-hierarchy svg[data-dropdown="false"] ~ ul { - display: none; - } - - .tsd-panel-group.tsd-index-group { - margin-bottom: 0; - } - .tsd-index-panel .tsd-index-list { - list-style: none; - line-height: 1.333em; - margin: 0; - padding: 0.25rem 0 0 0; - overflow: hidden; - display: grid; - grid-template-columns: repeat(3, 1fr); - column-gap: 1rem; - grid-template-rows: auto; - } - @media (max-width: 1024px) { - .tsd-index-panel .tsd-index-list { - grid-template-columns: repeat(2, 1fr); - } - } - @media (max-width: 768px) { - .tsd-index-panel .tsd-index-list { - grid-template-columns: repeat(1, 1fr); - } - } - .tsd-index-panel .tsd-index-list li { - -webkit-page-break-inside: avoid; - -moz-page-break-inside: avoid; - -ms-page-break-inside: avoid; - -o-page-break-inside: avoid; - page-break-inside: avoid; - } - - .tsd-flag { - display: inline-block; - padding: 0.25em 0.4em; - border-radius: 4px; - color: var(--color-comment-tag-text); - background-color: var(--color-comment-tag); - text-indent: 0; - font-size: 75%; - line-height: 1; - font-weight: normal; - } - - .tsd-anchor { - position: relative; - top: -100px; - } - - .tsd-member { - position: relative; - } - .tsd-member .tsd-anchor + h3 { - display: flex; - align-items: center; - margin-top: 0; - margin-bottom: 0; - border-bottom: none; - } - - .tsd-navigation.settings { - margin: 1rem 0; - } - .tsd-navigation > a, - .tsd-navigation .tsd-accordion-summary { - width: calc(100% - 0.25rem); - display: flex; - align-items: center; - } - .tsd-navigation a, - .tsd-navigation summary > span, - .tsd-page-navigation a { - display: flex; - width: calc(100% - 0.25rem); - align-items: center; - padding: 0.25rem; - color: var(--color-text); - text-decoration: none; - box-sizing: border-box; - } - .tsd-navigation a.current, - .tsd-page-navigation a.current { - background: var(--color-active-menu-item); - } - .tsd-navigation a:hover, - .tsd-page-navigation a:hover { - text-decoration: underline; - } - .tsd-navigation ul, - .tsd-page-navigation ul { - margin-top: 0; - margin-bottom: 0; - padding: 0; - list-style: none; - } - .tsd-navigation li, - .tsd-page-navigation li { - padding: 0; - max-width: 100%; - } - .tsd-navigation .tsd-nav-link { - display: none; - } - .tsd-nested-navigation { - margin-left: 3rem; - } - .tsd-nested-navigation > li > details { - margin-left: -1.5rem; - } - .tsd-small-nested-navigation { - margin-left: 1.5rem; - } - .tsd-small-nested-navigation > li > details { - margin-left: -1.5rem; - } - - .tsd-page-navigation-section { - margin-left: 10px; - } - .tsd-page-navigation-section > summary { - padding: 0.25rem; - } - .tsd-page-navigation-section > div { - margin-left: 20px; - } - .tsd-page-navigation ul { - padding-left: 1.75rem; - } - - #tsd-sidebar-links a { - margin-top: 0; - margin-bottom: 0.5rem; - line-height: 1.25rem; - } - #tsd-sidebar-links a:last-of-type { - margin-bottom: 0; - } - - a.tsd-index-link { - padding: 0.25rem 0 !important; - font-size: 1rem; - line-height: 1.25rem; - display: inline-flex; - align-items: center; - color: var(--color-text); - } - .tsd-accordion-summary { - list-style-type: none; /* hide marker on non-safari */ - outline: none; /* broken on safari, so just hide it */ - } - .tsd-accordion-summary::-webkit-details-marker { - display: none; /* hide marker on safari */ - } - .tsd-accordion-summary, - .tsd-accordion-summary a { - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; - - cursor: pointer; - } - .tsd-accordion-summary a { - width: calc(100% - 1.5rem); - } - .tsd-accordion-summary > * { - margin-top: 0; - margin-bottom: 0; - padding-top: 0; - padding-bottom: 0; - } - .tsd-accordion .tsd-accordion-summary > svg { - margin-left: 0.25rem; - vertical-align: text-top; - } - /* - * We need to be careful to target the arrow indicating whether the accordion - * is open, but not any other SVGs included in the details element. - */ - .tsd-accordion:not([open]) > .tsd-accordion-summary > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h1 > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h2 > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h3 > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h4 > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h5 > svg:first-child { - transform: rotate(-90deg); - } - .tsd-index-content > :not(:first-child) { - margin-top: 0.75rem; - } - .tsd-index-heading { - margin-top: 1.5rem; - margin-bottom: 0.75rem; - } - - .tsd-no-select { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - } - .tsd-kind-icon { - margin-right: 0.5rem; - width: 1.25rem; - height: 1.25rem; - min-width: 1.25rem; - min-height: 1.25rem; - } - .tsd-signature > .tsd-kind-icon { - margin-right: 0.8rem; - } - - .tsd-panel { - margin-bottom: 2.5rem; - } - .tsd-panel.tsd-member { - margin-bottom: 4rem; - } - .tsd-panel:empty { - display: none; - } - .tsd-panel > h1, - .tsd-panel > h2, - .tsd-panel > h3 { - margin: 1.5rem -1.5rem 0.75rem -1.5rem; - padding: 0 1.5rem 0.75rem 1.5rem; - } - .tsd-panel > h1.tsd-before-signature, - .tsd-panel > h2.tsd-before-signature, - .tsd-panel > h3.tsd-before-signature { - margin-bottom: 0; - border-bottom: none; - } - - .tsd-panel-group { - margin: 2rem 0; - } - .tsd-panel-group.tsd-index-group { - margin: 2rem 0; - } - .tsd-panel-group.tsd-index-group details { - margin: 2rem 0; - } - .tsd-panel-group > .tsd-accordion-summary { - margin-bottom: 1rem; - } - - #tsd-search { - transition: background-color 0.2s; - } - #tsd-search .title { - position: relative; - z-index: 2; - } - #tsd-search .field { - position: absolute; - left: 0; - top: 0; - right: 2.5rem; - height: 100%; - } - #tsd-search .field input { - box-sizing: border-box; - position: relative; - top: -50px; - z-index: 1; - width: 100%; - padding: 0 10px; - opacity: 0; - outline: 0; - border: 0; - background: transparent; - color: var(--color-text); - } - #tsd-search .field label { - position: absolute; - overflow: hidden; - right: -40px; - } - #tsd-search .field input, - #tsd-search .title, - #tsd-toolbar-links a { - transition: opacity 0.2s; - } - #tsd-search .results { - position: absolute; - visibility: hidden; - top: 40px; - width: 100%; - margin: 0; - padding: 0; - list-style: none; - box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); - } - #tsd-search .results li { - background-color: var(--color-background); - line-height: initial; - padding: 4px; - } - #tsd-search .results li:nth-child(even) { - background-color: var(--color-background-secondary); - } - #tsd-search .results li.state { - display: none; - } - #tsd-search .results li.current:not(.no-results), - #tsd-search .results li:hover:not(.no-results) { - background-color: var(--color-accent); - } - #tsd-search .results a { - display: flex; - align-items: center; - padding: 0.25rem; - box-sizing: border-box; - } - #tsd-search .results a:before { - top: 10px; - } - #tsd-search .results span.parent { - color: var(--color-text-aside); - font-weight: normal; - } - #tsd-search.has-focus { - background-color: var(--color-accent); - } - #tsd-search.has-focus .field input { - top: 0; - opacity: 1; - } - #tsd-search.has-focus .title, - #tsd-search.has-focus #tsd-toolbar-links a { - z-index: 0; - opacity: 0; - } - #tsd-search.has-focus .results { - visibility: visible; - } - #tsd-search.loading .results li.state.loading { - display: block; - } - #tsd-search.failure .results li.state.failure { - display: block; - } - - #tsd-toolbar-links { - position: absolute; - top: 0; - right: 2rem; - height: 100%; - display: flex; - align-items: center; - justify-content: flex-end; - } - #tsd-toolbar-links a { - margin-left: 1.5rem; - } - #tsd-toolbar-links a:hover { - text-decoration: underline; - } - - .tsd-signature { - margin: 0 0 1rem 0; - padding: 1rem 0.5rem; - border: 1px solid var(--color-accent); - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; - font-size: 14px; - overflow-x: auto; - } - - .tsd-signature-keyword { - color: var(--color-ts-keyword); - font-weight: normal; - } - - .tsd-signature-symbol { - color: var(--color-text-aside); - font-weight: normal; - } - - .tsd-signature-type { - font-style: italic; - font-weight: normal; - } - - .tsd-signatures { - padding: 0; - margin: 0 0 1em 0; - list-style-type: none; - } - .tsd-signatures .tsd-signature { - margin: 0; - border-color: var(--color-accent); - border-width: 1px 0; - transition: background-color 0.1s; - } - .tsd-signatures .tsd-index-signature:not(:last-child) { - margin-bottom: 1em; - } - .tsd-signatures .tsd-index-signature .tsd-signature { - border-width: 1px; - } - .tsd-description .tsd-signatures .tsd-signature { - border-width: 1px; - } - - ul.tsd-parameter-list, - ul.tsd-type-parameter-list { - list-style: square; - margin: 0; - padding-left: 20px; - } - ul.tsd-parameter-list > li.tsd-parameter-signature, - ul.tsd-type-parameter-list > li.tsd-parameter-signature { - list-style: none; - margin-left: -20px; - } - ul.tsd-parameter-list h5, - ul.tsd-type-parameter-list h5 { - font-size: 16px; - margin: 1em 0 0.5em 0; - } - .tsd-sources { - margin-top: 1rem; - font-size: 0.875em; - } - .tsd-sources a { - color: var(--color-text-aside); - text-decoration: underline; - } - .tsd-sources ul { - list-style: none; - padding: 0; - } - - .tsd-page-toolbar { - position: sticky; - z-index: 1; - top: 0; - left: 0; - width: 100%; - color: var(--color-text); - background: var(--color-background-secondary); - border-bottom: 1px var(--color-accent) solid; - transition: transform 0.3s ease-in-out; - } - .tsd-page-toolbar a { - color: var(--color-text); - text-decoration: none; - } - .tsd-page-toolbar a.title { - font-weight: bold; - } - .tsd-page-toolbar a.title:hover { - text-decoration: underline; - } - .tsd-page-toolbar .tsd-toolbar-contents { - display: flex; - justify-content: space-between; - height: 2.5rem; - margin: 0 auto; - } - .tsd-page-toolbar .table-cell { - position: relative; - white-space: nowrap; - line-height: 40px; - } - .tsd-page-toolbar .table-cell:first-child { - width: 100%; - } - .tsd-page-toolbar .tsd-toolbar-icon { - box-sizing: border-box; - line-height: 0; - padding: 12px 0; - } - - .tsd-widget { - display: inline-block; - overflow: hidden; - opacity: 0.8; - height: 40px; - transition: - opacity 0.1s, - background-color 0.2s; - vertical-align: bottom; - cursor: pointer; - } - .tsd-widget:hover { - opacity: 0.9; - } - .tsd-widget.active { - opacity: 1; - background-color: var(--color-accent); - } - .tsd-widget.no-caption { - width: 40px; - } - .tsd-widget.no-caption:before { - margin: 0; - } - - .tsd-widget.options, - .tsd-widget.menu { - display: none; - } - input[type="checkbox"] + .tsd-widget:before { - background-position: -120px 0; - } - input[type="checkbox"]:checked + .tsd-widget:before { - background-position: -160px 0; - } - - img { - max-width: 100%; - } - - .tsd-member-summary-name { - display: inline-flex; - align-items: center; - padding: 0.25rem; - text-decoration: none; - } - - .tsd-anchor-icon { - display: inline-flex; - align-items: center; - margin-left: 0.5rem; - color: var(--color-text); - } - - .tsd-anchor-icon svg { - width: 1em; - height: 1em; - visibility: hidden; - } - - .tsd-member-summary-name:hover > .tsd-anchor-icon svg, - .tsd-anchor-link:hover > .tsd-anchor-icon svg { - visibility: visible; - } - - .deprecated { - text-decoration: line-through !important; - } - - .warning { - padding: 1rem; - color: var(--color-warning-text); - background: var(--color-background-warning); - } - - .tsd-kind-project { - color: var(--color-ts-project); - } - .tsd-kind-module { - color: var(--color-ts-module); - } - .tsd-kind-namespace { - color: var(--color-ts-namespace); - } - .tsd-kind-enum { - color: var(--color-ts-enum); - } - .tsd-kind-enum-member { - color: var(--color-ts-enum-member); - } - .tsd-kind-variable { - color: var(--color-ts-variable); - } - .tsd-kind-function { - color: var(--color-ts-function); - } - .tsd-kind-class { - color: var(--color-ts-class); - } - .tsd-kind-interface { - color: var(--color-ts-interface); - } - .tsd-kind-constructor { - color: var(--color-ts-constructor); - } - .tsd-kind-property { - color: var(--color-ts-property); - } - .tsd-kind-method { - color: var(--color-ts-method); - } - .tsd-kind-reference { - color: var(--color-ts-reference); - } - .tsd-kind-call-signature { - color: var(--color-ts-call-signature); - } - .tsd-kind-index-signature { - color: var(--color-ts-index-signature); - } - .tsd-kind-constructor-signature { - color: var(--color-ts-constructor-signature); - } - .tsd-kind-parameter { - color: var(--color-ts-parameter); - } - .tsd-kind-type-parameter { - color: var(--color-ts-type-parameter); - } - .tsd-kind-accessor { - color: var(--color-ts-accessor); - } - .tsd-kind-get-signature { - color: var(--color-ts-get-signature); - } - .tsd-kind-set-signature { - color: var(--color-ts-set-signature); - } - .tsd-kind-type-alias { - color: var(--color-ts-type-alias); - } - - /* if we have a kind icon, don't color the text by kind */ - .tsd-kind-icon ~ span { - color: var(--color-text); - } - - * { - scrollbar-width: thin; - scrollbar-color: var(--color-accent) var(--color-icon-background); - } - - *::-webkit-scrollbar { - width: 0.75rem; - } - - *::-webkit-scrollbar-track { - background: var(--color-icon-background); - } - - *::-webkit-scrollbar-thumb { - background-color: var(--color-accent); - border-radius: 999rem; - border: 0.25rem solid var(--color-icon-background); - } - - /* mobile */ - @media (max-width: 769px) { - .tsd-widget.options, - .tsd-widget.menu { - display: inline-block; - } - - .container-main { - display: flex; - } - html .col-content { - float: none; - max-width: 100%; - width: 100%; - } - html .col-sidebar { - position: fixed !important; - overflow-y: auto; - -webkit-overflow-scrolling: touch; - z-index: 1024; - top: 0 !important; - bottom: 0 !important; - left: auto !important; - right: 0 !important; - padding: 1.5rem 1.5rem 0 0; - width: 75vw; - visibility: hidden; - background-color: var(--color-background); - transform: translate(100%, 0); - } - html .col-sidebar > *:last-child { - padding-bottom: 20px; - } - html .overlay { - content: ""; - display: block; - position: fixed; - z-index: 1023; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.75); - visibility: hidden; - } - - .to-has-menu .overlay { - animation: fade-in 0.4s; - } - - .to-has-menu .col-sidebar { - animation: pop-in-from-right 0.4s; - } - - .from-has-menu .overlay { - animation: fade-out 0.4s; - } - - .from-has-menu .col-sidebar { - animation: pop-out-to-right 0.4s; - } - - .has-menu body { - overflow: hidden; - } - .has-menu .overlay { - visibility: visible; - } - .has-menu .col-sidebar { - visibility: visible; - transform: translate(0, 0); - display: flex; - flex-direction: column; - gap: 1.5rem; - max-height: 100vh; - padding: 1rem 2rem; - } - .has-menu .tsd-navigation { - max-height: 100%; - } - #tsd-toolbar-links { - display: none; - } - .tsd-navigation .tsd-nav-link { - display: flex; - } - } - - /* one sidebar */ - @media (min-width: 770px) { - .container-main { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); - grid-template-areas: "sidebar content"; - margin: 2rem auto; - } - - .col-sidebar { - grid-area: sidebar; - } - .col-content { - grid-area: content; - padding: 0 1rem; - } - } - @media (min-width: 770px) and (max-width: 1399px) { - .col-sidebar { - max-height: calc(100vh - 2rem - 42px); - overflow: auto; - position: sticky; - top: 42px; - padding-top: 1rem; - } - .site-menu { - margin-top: 1rem; - } - } - - /* two sidebars */ - @media (min-width: 1200px) { - .container-main { - grid-template-columns: minmax(0, 1fr) minmax(0, 2.5fr) minmax( - 0, - 20rem - ); - grid-template-areas: "sidebar content toc"; - } - - .col-sidebar { - display: contents; - } - - .page-menu { - grid-area: toc; - padding-left: 1rem; - } - .site-menu { - grid-area: sidebar; - } - - .site-menu { - margin-top: 1rem; - } - - .page-menu, - .site-menu { - max-height: calc(100vh - 2rem - 42px); - overflow: auto; - position: sticky; - top: 42px; - } - } -} diff --git a/docs/browser/functions/configureBrowser.html b/docs/browser/functions/configureBrowser.html deleted file mode 100644 index 98b44e592..000000000 --- a/docs/browser/functions/configureBrowser.html +++ /dev/null @@ -1,4 +0,0 @@ -configureBrowser | @react-native-pingidentity/browser
  • Configure global browser behavior.

    -

    Android applies Custom Tabs/Auth Tabs settings globally; iOS is a no-op.

    -

    Parameters

    Returns void

diff --git a/docs/browser/functions/open.html b/docs/browser/functions/open.html deleted file mode 100644 index e1f845a16..000000000 --- a/docs/browser/functions/open.html +++ /dev/null @@ -1,6 +0,0 @@ -open | @react-native-pingidentity/browser
  • Launch a secure system browser and wait for redirect or cancellation.

    -

    Android uses Auth Tabs/Custom Tabs; iOS uses ASWebAuthenticationSession.

    -

    Parameters

    • url: string

      Target URL to open.

      -
    • options: BrowserOpenOptions

      Per-launch options and callback configuration.

      -

    Returns Promise<BrowserResult>

    The browser result when the redirect is received or the user cancels.

    -
diff --git a/docs/browser/functions/resetBrowser.html b/docs/browser/functions/resetBrowser.html deleted file mode 100644 index 431f06f91..000000000 --- a/docs/browser/functions/resetBrowser.html +++ /dev/null @@ -1,3 +0,0 @@ -resetBrowser | @react-native-pingidentity/browser
  • Reset any in-flight browser session.

    -

    iOS cancels the current browser flow if active; Android is a no-op.

    -

    Returns void

diff --git a/docs/browser/index.html b/docs/browser/index.html deleted file mode 100644 index 2642c7d2a..000000000 --- a/docs/browser/index.html +++ /dev/null @@ -1,49 +0,0 @@ -@react-native-pingidentity/browser

@react-native-pingidentity/browser

-

Ping Identity

-

Ping Identity React Native Browser

The Ping Identity React Native Browser module provides a safe, system-browser flow for OIDC/OAuth -logins. It launches Custom Tabs/Auth Tabs on Android and ASWebAuthenticationSession on iOS, then -returns the redirect URL to JavaScript.

-

Add the package and let autolinking wire the native code:

-
yarn add @react-native-pingidentity/browser
cd ios && pod install -
- -

Apply global customization for Custom Tabs/Auth Tabs. iOS currently ignores these options.

-
import { configureBrowser } from '@react-native-pingidentity/browser';

configureBrowser({
android: {
customTabs: {
showTitle: false,
urlBarHidingEnabled: true,
colorScheme: 'system',
},
authTabs: {
ephemeral: true,
},
},
}); -
- -

iOS browser behavior is configured per call via the ios options on open(...). These options -do not have a global configuration equivalent on iOS.

-

Configure the manifest placeholder for your app's redirect URI scheme. This is used as a fallback -when Auth Tabs are not available and Custom Tabs must rely on the manifest scheme:

-
android {
-  defaultConfig {
-    // For redirect URI "com.example.app://callback", configure:
-    manifestPlaceholders["appRedirectUriScheme"] = "com.example.app"
-  }
-}
-
- -
import { open } from '@react-native-pingidentity/browser';

const result = await open('https://example.com', {
callbackUrlScheme: 'com.example.app',
redirectUri: 'com.example.app://callback',
ios: {
browserType: 'authSession',
browserMode: 'login',
},
});

// result: { type: 'success', url } | { type: 'cancel' } -
- -

Security note: The module does not validate or sanitize the url you pass to open. Only launch -trusted URLs in your app (for example, enforce an https scheme and allow-listed hosts).

-

Native promise rejections map to the shared GenericError contract from -@ping-identity/rn-types. Errors are rejected as exceptions; cancellations are resolved as -{ type: 'cancel' } instead of being rejected.

-

Error codes:

-
    -
  • BROWSER_OPEN_ERROR for validation/launch failures
  • -
-
import type { BrowserError } from '@react-native-pingidentity/browser';

try {
await open('https://example.com', { callbackUrlScheme: 'com.example.app' });
} catch (e) {
const error = e as BrowserError;
// error.type, error.error, error.message, error.code, error.status
} -
- -
    -
  • Add an iOS test runner target to execute module unit tests.
  • -
-
diff --git a/docs/browser/modules.html b/docs/browser/modules.html deleted file mode 100644 index 6a5453e01..000000000 --- a/docs/browser/modules.html +++ /dev/null @@ -1 +0,0 @@ -@react-native-pingidentity/browser
diff --git a/docs/browser/types/BrowserConfig.html b/docs/browser/types/BrowserConfig.html deleted file mode 100644 index 3e705629e..000000000 --- a/docs/browser/types/BrowserConfig.html +++ /dev/null @@ -1,2 +0,0 @@ -BrowserConfig | @react-native-pingidentity/browser
BrowserConfig: { android?: AndroidBrowserConfig; ios?: IOSBrowserConfig }

Cross-platform configuration wrapper.

-

Type declaration

  • Optionalandroid?: AndroidBrowserConfig
  • Optionalios?: IOSBrowserConfig
diff --git a/docs/browser/types/BrowserError.html b/docs/browser/types/BrowserError.html deleted file mode 100644 index 4f1cc5326..000000000 --- a/docs/browser/types/BrowserError.html +++ /dev/null @@ -1,4 +0,0 @@ -BrowserError | @react-native-pingidentity/browser
BrowserError: GenericError

Error payload returned when browser operations fail.

-

Matches the shared native/JS error contract defined in @ping-identity/rn-types.

-

Rejections use this shape; cancellations resolve as { type: 'cancel' }.

-
diff --git a/docs/browser/types/BrowserErrorCode.html b/docs/browser/types/BrowserErrorCode.html deleted file mode 100644 index c7f9d0dd0..000000000 --- a/docs/browser/types/BrowserErrorCode.html +++ /dev/null @@ -1,3 +0,0 @@ -BrowserErrorCode | @react-native-pingidentity/browser
BrowserErrorCode: "BROWSER_OPEN_ERROR"

Stable error codes emitted by the Browser module.

-

Keep these in sync with the native error constants.

-
diff --git a/docs/browser/types/BrowserOpenOptions.html b/docs/browser/types/BrowserOpenOptions.html deleted file mode 100644 index 5f23a0b9a..000000000 --- a/docs/browser/types/BrowserOpenOptions.html +++ /dev/null @@ -1,20 +0,0 @@ -BrowserOpenOptions | @react-native-pingidentity/browser
BrowserOpenOptions: {
    callbackUrlScheme: string;
    ios?: IOSBrowserOpenOptions;
    redirectUri?: string;
}

Configuration for launching the browser.

-

Type declaration

  • callbackUrlScheme: string

    App callback scheme used to receive the redirect.

    -

    Examples:

    -
      -
    • "myapp"
    • -
    • "com.company.myapp"
    • -
    -

    iOS: -Passed to ASWebAuthenticationSession callbackURLScheme.

    -

    Android: -Used for per-launch redirect handling and must match -the manifestPlaceholder appRedirectUriScheme.

    -
  • Optionalios?: IOSBrowserOpenOptions

    iOS-only options.

    -
  • OptionalredirectUri?: string

    Optional full redirect URI override.

    -

    Android: -Supported and recommended for per-launch configuration.

    -

    iOS: -Typically handled by the authentication layer; ignored if not applicable.

    -

The callbackUrlScheme is required to receive the redirect back into the app.

-
diff --git a/docs/browser/types/BrowserResult.html b/docs/browser/types/BrowserResult.html deleted file mode 100644 index 258450ecd..000000000 --- a/docs/browser/types/BrowserResult.html +++ /dev/null @@ -1,2 +0,0 @@ -BrowserResult | @react-native-pingidentity/browser
BrowserResult: { type: "success"; url: string } | { type: "cancel" }

Result of a browser launch.

-
diff --git a/docs/browser/types/IOSBrowserOpenOptions.html b/docs/browser/types/IOSBrowserOpenOptions.html deleted file mode 100644 index c58bf9173..000000000 --- a/docs/browser/types/IOSBrowserOpenOptions.html +++ /dev/null @@ -1,4 +0,0 @@ -IOSBrowserOpenOptions | @react-native-pingidentity/browser
IOSBrowserOpenOptions: {
    browserMode?: "login" | "logout" | "custom";
    browserType?:
        | "authSession"
        | "ephemeralAuthSession"
        | "nativeBrowserApp"
        | "sfViewController";
}

iOS-only options for launching a browser session.

-

Type declaration

  • OptionalbrowserMode?: "login" | "logout" | "custom"

    Browser mode (reserved; currently informational).

    -
  • OptionalbrowserType?: "authSession" | "ephemeralAuthSession" | "nativeBrowserApp" | "sfViewController"

    Browser type for iOS.

    -
diff --git a/docs/journey/.nojekyll b/docs/journey/.nojekyll deleted file mode 100644 index e2ac6616a..000000000 --- a/docs/journey/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/journey/assets/hierarchy.js b/docs/journey/assets/hierarchy.js deleted file mode 100644 index 88636f05d..000000000 --- a/docs/journey/assets/hierarchy.js +++ /dev/null @@ -1 +0,0 @@ -window.hierarchyData = "eJyrVirKzy8pVrKKjtVRKkpNy0lNLsnMzwMKVNfWAgCbHgqm" \ No newline at end of file diff --git a/docs/journey/assets/highlight.css b/docs/journey/assets/highlight.css deleted file mode 100644 index 55e79231b..000000000 --- a/docs/journey/assets/highlight.css +++ /dev/null @@ -1,99 +0,0 @@ -:root { - --light-hl-0: #795E26; - --dark-hl-0: #DCDCAA; - --light-hl-1: #000000; - --dark-hl-1: #D4D4D4; - --light-hl-2: #A31515; - --dark-hl-2: #CE9178; - --light-hl-3: #AF00DB; - --dark-hl-3: #C586C0; - --light-hl-4: #001080; - --dark-hl-4: #9CDCFE; - --light-hl-5: #0000FF; - --dark-hl-5: #569CD6; - --light-hl-6: #0070C1; - --dark-hl-6: #4FC1FF; - --light-hl-7: #098658; - --dark-hl-7: #B5CEA8; - --light-hl-8: #267F99; - --dark-hl-8: #4EC9B0; - --light-hl-9: #800000; - --dark-hl-9: #808080; - --light-hl-10: #E50000; - --dark-hl-10: #9CDCFE; - --light-code-background: #FFFFFF; - --dark-code-background: #1E1E1E; -} - -@media (prefers-color-scheme: light) { :root { - --hl-0: var(--light-hl-0); - --hl-1: var(--light-hl-1); - --hl-2: var(--light-hl-2); - --hl-3: var(--light-hl-3); - --hl-4: var(--light-hl-4); - --hl-5: var(--light-hl-5); - --hl-6: var(--light-hl-6); - --hl-7: var(--light-hl-7); - --hl-8: var(--light-hl-8); - --hl-9: var(--light-hl-9); - --hl-10: var(--light-hl-10); - --code-background: var(--light-code-background); -} } - -@media (prefers-color-scheme: dark) { :root { - --hl-0: var(--dark-hl-0); - --hl-1: var(--dark-hl-1); - --hl-2: var(--dark-hl-2); - --hl-3: var(--dark-hl-3); - --hl-4: var(--dark-hl-4); - --hl-5: var(--dark-hl-5); - --hl-6: var(--dark-hl-6); - --hl-7: var(--dark-hl-7); - --hl-8: var(--dark-hl-8); - --hl-9: var(--dark-hl-9); - --hl-10: var(--dark-hl-10); - --code-background: var(--dark-code-background); -} } - -:root[data-theme='light'] { - --hl-0: var(--light-hl-0); - --hl-1: var(--light-hl-1); - --hl-2: var(--light-hl-2); - --hl-3: var(--light-hl-3); - --hl-4: var(--light-hl-4); - --hl-5: var(--light-hl-5); - --hl-6: var(--light-hl-6); - --hl-7: var(--light-hl-7); - --hl-8: var(--light-hl-8); - --hl-9: var(--light-hl-9); - --hl-10: var(--light-hl-10); - --code-background: var(--light-code-background); -} - -:root[data-theme='dark'] { - --hl-0: var(--dark-hl-0); - --hl-1: var(--dark-hl-1); - --hl-2: var(--dark-hl-2); - --hl-3: var(--dark-hl-3); - --hl-4: var(--dark-hl-4); - --hl-5: var(--dark-hl-5); - --hl-6: var(--dark-hl-6); - --hl-7: var(--dark-hl-7); - --hl-8: var(--dark-hl-8); - --hl-9: var(--dark-hl-9); - --hl-10: var(--dark-hl-10); - --code-background: var(--dark-code-background); -} - -.hl-0 { color: var(--hl-0); } -.hl-1 { color: var(--hl-1); } -.hl-2 { color: var(--hl-2); } -.hl-3 { color: var(--hl-3); } -.hl-4 { color: var(--hl-4); } -.hl-5 { color: var(--hl-5); } -.hl-6 { color: var(--hl-6); } -.hl-7 { color: var(--hl-7); } -.hl-8 { color: var(--hl-8); } -.hl-9 { color: var(--hl-9); } -.hl-10 { color: var(--hl-10); } -pre, code { background: var(--code-background); } diff --git a/docs/journey/assets/icons.js b/docs/journey/assets/icons.js deleted file mode 100644 index 58882d76d..000000000 --- a/docs/journey/assets/icons.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - addIcons(); - function addIcons() { - if (document.readyState === "loading") return document.addEventListener("DOMContentLoaded", addIcons); - const svg = document.body.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "svg")); - svg.innerHTML = `MMNEPVFCICPMFPCPTTAAATR`; - svg.style.display = "none"; - if (location.protocol === "file:") updateUseElements(); - } - - function updateUseElements() { - document.querySelectorAll("use").forEach(el => { - if (el.getAttribute("href").includes("#icon-")) { - el.setAttribute("href", el.getAttribute("href").replace(/.*#/, "#")); - } - }); - } -})() \ No newline at end of file diff --git a/docs/journey/assets/icons.svg b/docs/journey/assets/icons.svg deleted file mode 100644 index 50ad5799d..000000000 --- a/docs/journey/assets/icons.svg +++ /dev/null @@ -1 +0,0 @@ -MMNEPVFCICPMFPCPTTAAATR \ No newline at end of file diff --git a/docs/journey/assets/main.js b/docs/journey/assets/main.js deleted file mode 100644 index 2363f64c2..000000000 --- a/docs/journey/assets/main.js +++ /dev/null @@ -1,60 +0,0 @@ -"use strict"; -window.translations={"copy":"Copy","copied":"Copied!","normally_hidden":"This member is normally hidden due to your filter settings.","hierarchy_expand":"Expand","hierarchy_collapse":"Collapse","folder":"Folder","kind_1":"Project","kind_2":"Module","kind_4":"Namespace","kind_8":"Enumeration","kind_16":"Enumeration Member","kind_32":"Variable","kind_64":"Function","kind_128":"Class","kind_256":"Interface","kind_512":"Constructor","kind_1024":"Property","kind_2048":"Method","kind_4096":"Call Signature","kind_8192":"Index Signature","kind_16384":"Constructor Signature","kind_32768":"Parameter","kind_65536":"Type Literal","kind_131072":"Type Parameter","kind_262144":"Accessor","kind_524288":"Get Signature","kind_1048576":"Set Signature","kind_2097152":"Type Alias","kind_4194304":"Reference","kind_8388608":"Document"}; -"use strict";(()=>{var De=Object.create;var le=Object.defineProperty;var Fe=Object.getOwnPropertyDescriptor;var Ne=Object.getOwnPropertyNames;var Ve=Object.getPrototypeOf,Be=Object.prototype.hasOwnProperty;var qe=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var je=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ne(e))!Be.call(t,i)&&i!==n&&le(t,i,{get:()=>e[i],enumerable:!(r=Fe(e,i))||r.enumerable});return t};var $e=(t,e,n)=>(n=t!=null?De(Ve(t)):{},je(e||!t||!t.__esModule?le(n,"default",{value:t,enumerable:!0}):n,t));var pe=qe((de,he)=>{(function(){var t=function(e){var n=new t.Builder;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),n.searchPipeline.add(t.stemmer),e.call(n,n),n.build()};t.version="2.3.9";t.utils={},t.utils.warn=function(e){return function(n){e.console&&console.warn&&console.warn(n)}}(this),t.utils.asString=function(e){return e==null?"":e.toString()},t.utils.clone=function(e){if(e==null)return e;for(var n=Object.create(null),r=Object.keys(e),i=0;i0){var d=t.utils.clone(n)||{};d.position=[a,c],d.index=s.length,s.push(new t.Token(r.slice(a,o),d))}a=o+1}}return s},t.tokenizer.separator=/[\s\-]+/;t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions=Object.create(null),t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. -`,e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(r){var i=t.Pipeline.registeredFunctions[r];if(i)n.add(i);else throw new Error("Cannot load unregistered function: "+r)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(n){t.Pipeline.warnIfFunctionNotRegistered(n),this._stack.push(n)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");r=r+1,this._stack.splice(r,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");this._stack.splice(r,0,n)},t.Pipeline.prototype.remove=function(e){var n=this._stack.indexOf(e);n!=-1&&this._stack.splice(n,1)},t.Pipeline.prototype.run=function(e){for(var n=this._stack.length,r=0;r1&&(oe&&(r=s),o!=e);)i=r-n,s=n+Math.floor(i/2),o=this.elements[s*2];if(o==e||o>e)return s*2;if(ol?d+=2:a==l&&(n+=r[c+1]*i[d+1],c+=2,d+=2);return n},t.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},t.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),n=1,r=0;n0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new t.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new t.TokenSet;s.node.edges["*"]=l}if(s.str.length==0&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var c=s.node.edges["*"];else{var c=new t.TokenSet;s.node.edges["*"]=c}s.str.length==1&&(c.final=!0),i.push({node:c,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var d=s.str.charAt(0),m=s.str.charAt(1),p;m in s.node.edges?p=s.node.edges[m]:(p=new t.TokenSet,s.node.edges[m]=p),s.str.length==1&&(p.final=!0),i.push({node:p,editsRemaining:s.editsRemaining-1,str:d+s.str.slice(2)})}}}return r},t.TokenSet.fromString=function(e){for(var n=new t.TokenSet,r=n,i=0,s=e.length;i=e;n--){var r=this.uncheckedNodes[n],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r.char]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}};t.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},t.Index.prototype.search=function(e){return this.query(function(n){var r=new t.QueryParser(e,n);r.parse()})},t.Index.prototype.query=function(e){for(var n=new t.Query(this.fields),r=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),l=0;l1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,n){var r=e[this._ref],i=Object.keys(this._fields);this._documents[r]=n||{},this.documentCount+=1;for(var s=0;s=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,n;do e=this.next(),n=e.charCodeAt(0);while(n>47&&n<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var n=e.next();if(n==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(n.charCodeAt(0)==92){e.escapeCharacter();continue}if(n==":")return t.QueryLexer.lexField;if(n=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(n=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(n=="+"&&e.width()===1||n=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(n.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,n){this.lexer=new t.QueryLexer(e),this.query=n,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var n=e.peekLexeme();if(n!=null)switch(n.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expected either a field or a term, found "+n.type;throw n.str.length>=1&&(r+=" with value '"+n.str+"'"),new t.QueryParseError(r,n.start,n.end)}},t.QueryParser.parsePresence=function(e){var n=e.consumeLexeme();if(n!=null){switch(n.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var r="unrecognised presence operator'"+n.str+"'";throw new t.QueryParseError(r,n.start,n.end)}var i=e.peekLexeme();if(i==null){var r="expecting term or field, found nothing";throw new t.QueryParseError(r,n.start,n.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(r,i.start,i.end)}}},t.QueryParser.parseField=function(e){var n=e.consumeLexeme();if(n!=null){if(e.query.allFields.indexOf(n.str)==-1){var r=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+n.str+"', possible fields: "+r;throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.fields=[n.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,n.start,n.end)}switch(s.type){case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseTerm=function(e){var n=e.consumeLexeme();if(n!=null){e.currentClause.term=n.str.toLowerCase(),n.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var r=e.peekLexeme();if(r==null){e.nextClause();return}switch(r.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+r.type+"'";throw new t.QueryParseError(i,r.start,r.end)}}},t.QueryParser.parseEditDistance=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="edit distance must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.editDistance=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="boost must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.boost=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},function(e,n){typeof define=="function"&&define.amd?define(n):typeof de=="object"?he.exports=n():e.lunr=n()}(this,function(){return t})})()});window.translations||={copy:"Copy",copied:"Copied!",normally_hidden:"This member is normally hidden due to your filter settings.",hierarchy_expand:"Expand",hierarchy_collapse:"Collapse",folder:"Folder",kind_1:"Project",kind_2:"Module",kind_4:"Namespace",kind_8:"Enumeration",kind_16:"Enumeration Member",kind_32:"Variable",kind_64:"Function",kind_128:"Class",kind_256:"Interface",kind_512:"Constructor",kind_1024:"Property",kind_2048:"Method",kind_4096:"Call Signature",kind_8192:"Index Signature",kind_16384:"Constructor Signature",kind_32768:"Parameter",kind_65536:"Type Literal",kind_131072:"Type Parameter",kind_262144:"Accessor",kind_524288:"Get Signature",kind_1048576:"Set Signature",kind_2097152:"Type Alias",kind_4194304:"Reference",kind_8388608:"Document"};var ce=[];function G(t,e){ce.push({selector:e,constructor:t})}var J=class{alwaysVisibleMember=null;constructor(){this.createComponents(document.body),this.ensureFocusedElementVisible(),this.listenForCodeCopies(),window.addEventListener("hashchange",()=>this.ensureFocusedElementVisible()),document.body.style.display||(this.ensureFocusedElementVisible(),this.updateIndexVisibility(),this.scrollToHash())}createComponents(e){ce.forEach(n=>{e.querySelectorAll(n.selector).forEach(r=>{r.dataset.hasInstance||(new n.constructor({el:r,app:this}),r.dataset.hasInstance=String(!0))})})}filterChanged(){this.ensureFocusedElementVisible()}showPage(){document.body.style.display&&(document.body.style.removeProperty("display"),this.ensureFocusedElementVisible(),this.updateIndexVisibility(),this.scrollToHash())}scrollToHash(){if(location.hash){let e=document.getElementById(location.hash.substring(1));if(!e)return;e.scrollIntoView({behavior:"instant",block:"start"})}}ensureActivePageVisible(){let e=document.querySelector(".tsd-navigation .current"),n=e?.parentElement;for(;n&&!n.classList.contains(".tsd-navigation");)n instanceof HTMLDetailsElement&&(n.open=!0),n=n.parentElement;if(e&&!ze(e)){let r=e.getBoundingClientRect().top-document.documentElement.clientHeight/4;document.querySelector(".site-menu").scrollTop=r,document.querySelector(".col-sidebar").scrollTop=r}}updateIndexVisibility(){let e=document.querySelector(".tsd-index-content"),n=e?.open;e&&(e.open=!0),document.querySelectorAll(".tsd-index-section").forEach(r=>{r.style.display="block";let i=Array.from(r.querySelectorAll(".tsd-index-link")).every(s=>s.offsetParent==null);r.style.display=i?"none":"block"}),e&&(e.open=n)}ensureFocusedElementVisible(){if(this.alwaysVisibleMember&&(this.alwaysVisibleMember.classList.remove("always-visible"),this.alwaysVisibleMember.firstElementChild.remove(),this.alwaysVisibleMember=null),!location.hash)return;let e=document.getElementById(location.hash.substring(1));if(!e)return;let n=e.parentElement;for(;n&&n.tagName!=="SECTION";)n=n.parentElement;if(!n)return;let r=n.offsetParent==null,i=n;for(;i!==document.body;)i instanceof HTMLDetailsElement&&(i.open=!0),i=i.parentElement;if(n.offsetParent==null){this.alwaysVisibleMember=n,n.classList.add("always-visible");let s=document.createElement("p");s.classList.add("warning"),s.textContent=window.translations.normally_hidden,n.prepend(s)}r&&e.scrollIntoView()}listenForCodeCopies(){document.querySelectorAll("pre > button").forEach(e=>{let n;e.addEventListener("click",()=>{e.previousElementSibling instanceof HTMLElement&&navigator.clipboard.writeText(e.previousElementSibling.innerText.trim()),e.textContent=window.translations.copied,e.classList.add("visible"),clearTimeout(n),n=setTimeout(()=>{e.classList.remove("visible"),n=setTimeout(()=>{e.textContent=window.translations.copy},100)},1e3)})})}};function ze(t){let e=t.getBoundingClientRect(),n=Math.max(document.documentElement.clientHeight,window.innerHeight);return!(e.bottom<0||e.top-n>=0)}var ue=(t,e=100)=>{let n;return()=>{clearTimeout(n),n=setTimeout(()=>t(),e)}};var ge=$e(pe(),1);async function A(t){let e=Uint8Array.from(atob(t),s=>s.charCodeAt(0)),r=new Blob([e]).stream().pipeThrough(new DecompressionStream("deflate")),i=await new Response(r).text();return JSON.parse(i)}async function fe(t,e){if(!window.searchData)return;let n=await A(window.searchData);t.data=n,t.index=ge.Index.load(n.index),e.classList.remove("loading"),e.classList.add("ready")}function ve(){let t=document.getElementById("tsd-search");if(!t)return;let e={base:document.documentElement.dataset.base+"/"},n=document.getElementById("tsd-search-script");t.classList.add("loading"),n&&(n.addEventListener("error",()=>{t.classList.remove("loading"),t.classList.add("failure")}),n.addEventListener("load",()=>{fe(e,t)}),fe(e,t));let r=document.querySelector("#tsd-search input"),i=document.querySelector("#tsd-search .results");if(!r||!i)throw new Error("The input field or the result list wrapper was not found");i.addEventListener("mouseup",()=>{re(t)}),r.addEventListener("focus",()=>t.classList.add("has-focus")),We(t,i,r,e)}function We(t,e,n,r){n.addEventListener("input",ue(()=>{Ue(t,e,n,r)},200)),n.addEventListener("keydown",i=>{i.key=="Enter"?Je(e,t):i.key=="ArrowUp"?(me(e,n,-1),i.preventDefault()):i.key==="ArrowDown"&&(me(e,n,1),i.preventDefault())}),document.body.addEventListener("keypress",i=>{i.altKey||i.ctrlKey||i.metaKey||!n.matches(":focus")&&i.key==="/"&&(i.preventDefault(),n.focus())}),document.body.addEventListener("keyup",i=>{t.classList.contains("has-focus")&&(i.key==="Escape"||!e.matches(":focus-within")&&!n.matches(":focus"))&&(n.blur(),re(t))})}function re(t){t.classList.remove("has-focus")}function Ue(t,e,n,r){if(!r.index||!r.data)return;e.textContent="";let i=n.value.trim(),s;if(i){let o=i.split(" ").map(a=>a.length?`*${a}*`:"").join(" ");s=r.index.search(o)}else s=[];for(let o=0;oa.score-o.score);for(let o=0,a=Math.min(10,s.length);o`,d=ye(l.name,i);globalThis.DEBUG_SEARCH_WEIGHTS&&(d+=` (score: ${s[o].score.toFixed(2)})`),l.parent&&(d=` - ${ye(l.parent,i)}.${d}`);let m=document.createElement("li");m.classList.value=l.classes??"";let p=document.createElement("a");p.href=r.base+l.url,p.innerHTML=c+d,m.append(p),p.addEventListener("focus",()=>{e.querySelector(".current")?.classList.remove("current"),m.classList.add("current")}),e.appendChild(m)}}function me(t,e,n){let r=t.querySelector(".current");if(!r)r=t.querySelector(n==1?"li:first-child":"li:last-child"),r&&r.classList.add("current");else{let i=r;if(n===1)do i=i.nextElementSibling??void 0;while(i instanceof HTMLElement&&i.offsetParent==null);else do i=i.previousElementSibling??void 0;while(i instanceof HTMLElement&&i.offsetParent==null);i?(r.classList.remove("current"),i.classList.add("current")):n===-1&&(r.classList.remove("current"),e.focus())}}function Je(t,e){let n=t.querySelector(".current");if(n||(n=t.querySelector("li:first-child")),n){let r=n.querySelector("a");r&&(window.location.href=r.href),re(e)}}function ye(t,e){if(e==="")return t;let n=t.toLocaleLowerCase(),r=e.toLocaleLowerCase(),i=[],s=0,o=n.indexOf(r);for(;o!=-1;)i.push(ne(t.substring(s,o)),`${ne(t.substring(o,o+r.length))}`),s=o+r.length,o=n.indexOf(r,s);return i.push(ne(t.substring(s))),i.join("")}var Ge={"&":"&","<":"<",">":">","'":"'",'"':"""};function ne(t){return t.replace(/[&<>"'"]/g,e=>Ge[e])}var I=class{el;app;constructor(e){this.el=e.el,this.app=e.app}};var H="mousedown",Ee="mousemove",B="mouseup",X={x:0,y:0},xe=!1,ie=!1,Xe=!1,D=!1,be=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);document.documentElement.classList.add(be?"is-mobile":"not-mobile");be&&"ontouchstart"in document.documentElement&&(Xe=!0,H="touchstart",Ee="touchmove",B="touchend");document.addEventListener(H,t=>{ie=!0,D=!1;let e=H=="touchstart"?t.targetTouches[0]:t;X.y=e.pageY||0,X.x=e.pageX||0});document.addEventListener(Ee,t=>{if(ie&&!D){let e=H=="touchstart"?t.targetTouches[0]:t,n=X.x-(e.pageX||0),r=X.y-(e.pageY||0);D=Math.sqrt(n*n+r*r)>10}});document.addEventListener(B,()=>{ie=!1});document.addEventListener("click",t=>{xe&&(t.preventDefault(),t.stopImmediatePropagation(),xe=!1)});var Y=class extends I{active;className;constructor(e){super(e),this.className=this.el.dataset.toggle||"",this.el.addEventListener(B,n=>this.onPointerUp(n)),this.el.addEventListener("click",n=>n.preventDefault()),document.addEventListener(H,n=>this.onDocumentPointerDown(n)),document.addEventListener(B,n=>this.onDocumentPointerUp(n))}setActive(e){if(this.active==e)return;this.active=e,document.documentElement.classList.toggle("has-"+this.className,e),this.el.classList.toggle("active",e);let n=(this.active?"to-has-":"from-has-")+this.className;document.documentElement.classList.add(n),setTimeout(()=>document.documentElement.classList.remove(n),500)}onPointerUp(e){D||(this.setActive(!0),e.preventDefault())}onDocumentPointerDown(e){if(this.active){if(e.target.closest(".col-sidebar, .tsd-filter-group"))return;this.setActive(!1)}}onDocumentPointerUp(e){if(!D&&this.active&&e.target.closest(".col-sidebar")){let n=e.target.closest("a");if(n){let r=window.location.href;r.indexOf("#")!=-1&&(r=r.substring(0,r.indexOf("#"))),n.href.substring(0,r.length)==r&&setTimeout(()=>this.setActive(!1),250)}}}};var se;try{se=localStorage}catch{se={getItem(){return null},setItem(){}}}var C=se;var Le=document.head.appendChild(document.createElement("style"));Le.dataset.for="filters";var Z=class extends I{key;value;constructor(e){super(e),this.key=`filter-${this.el.name}`,this.value=this.el.checked,this.el.addEventListener("change",()=>{this.setLocalStorage(this.el.checked)}),this.setLocalStorage(this.fromLocalStorage()),Le.innerHTML+=`html:not(.${this.key}) .tsd-is-${this.el.name} { display: none; } -`,this.app.updateIndexVisibility()}fromLocalStorage(){let e=C.getItem(this.key);return e?e==="true":this.el.checked}setLocalStorage(e){C.setItem(this.key,e.toString()),this.value=e,this.handleValueChange()}handleValueChange(){this.el.checked=this.value,document.documentElement.classList.toggle(this.key,this.value),this.app.filterChanged(),this.app.updateIndexVisibility()}};var oe=new Map,ae=class{open;accordions=[];key;constructor(e,n){this.key=e,this.open=n}add(e){this.accordions.push(e),e.open=this.open,e.addEventListener("toggle",()=>{this.toggle(e.open)})}toggle(e){for(let n of this.accordions)n.open=e;C.setItem(this.key,e.toString())}},K=class extends I{constructor(e){super(e);let n=this.el.querySelector("summary"),r=n.querySelector("a");r&&r.addEventListener("click",()=>{location.assign(r.href)});let i=`tsd-accordion-${n.dataset.key??n.textContent.trim().replace(/\s+/g,"-").toLowerCase()}`,s;if(oe.has(i))s=oe.get(i);else{let o=C.getItem(i),a=o?o==="true":this.el.open;s=new ae(i,a),oe.set(i,s)}s.add(this.el)}};function Se(t){let e=C.getItem("tsd-theme")||"os";t.value=e,we(e),t.addEventListener("change",()=>{C.setItem("tsd-theme",t.value),we(t.value)})}function we(t){document.documentElement.dataset.theme=t}var ee;function Ce(){let t=document.getElementById("tsd-nav-script");t&&(t.addEventListener("load",Te),Te())}async function Te(){let t=document.getElementById("tsd-nav-container");if(!t||!window.navigationData)return;let e=await A(window.navigationData);ee=document.documentElement.dataset.base,ee.endsWith("/")||(ee+="/"),t.innerHTML="";for(let n of e)Ie(n,t,[]);window.app.createComponents(t),window.app.showPage(),window.app.ensureActivePageVisible()}function Ie(t,e,n){let r=e.appendChild(document.createElement("li"));if(t.children){let i=[...n,t.text],s=r.appendChild(document.createElement("details"));s.className=t.class?`${t.class} tsd-accordion`:"tsd-accordion";let o=s.appendChild(document.createElement("summary"));o.className="tsd-accordion-summary",o.dataset.key=i.join("$"),o.innerHTML='',ke(t,o);let a=s.appendChild(document.createElement("div"));a.className="tsd-accordion-details";let l=a.appendChild(document.createElement("ul"));l.className="tsd-nested-navigation";for(let c of t.children)Ie(c,l,i)}else ke(t,r,t.class)}function ke(t,e,n){if(t.path){let r=e.appendChild(document.createElement("a"));if(r.href=ee+t.path,n&&(r.className=n),location.pathname===r.pathname&&!r.href.includes("#")&&r.classList.add("current"),t.kind){let i=window.translations[`kind_${t.kind}`].replaceAll('"',""");r.innerHTML=``}r.appendChild(document.createElement("span")).textContent=t.text}else{let r=e.appendChild(document.createElement("span")),i=window.translations.folder.replaceAll('"',""");r.innerHTML=``,r.appendChild(document.createElement("span")).textContent=t.text}}var te=document.documentElement.dataset.base;te.endsWith("/")||(te+="/");function Pe(){document.querySelector(".tsd-full-hierarchy")?Ye():document.querySelector(".tsd-hierarchy")&&Ze()}function Ye(){document.addEventListener("click",r=>{let i=r.target;for(;i.parentElement&&i.parentElement.tagName!="LI";)i=i.parentElement;i.dataset.dropdown&&(i.dataset.dropdown=String(i.dataset.dropdown!=="true"))});let t=new Map,e=new Set;for(let r of document.querySelectorAll(".tsd-full-hierarchy [data-refl]")){let i=r.querySelector("ul");t.has(r.dataset.refl)?e.add(r.dataset.refl):i&&t.set(r.dataset.refl,i)}for(let r of e)n(r);function n(r){let i=t.get(r).cloneNode(!0);i.querySelectorAll("[id]").forEach(s=>{s.removeAttribute("id")}),i.querySelectorAll("[data-dropdown]").forEach(s=>{s.dataset.dropdown="false"});for(let s of document.querySelectorAll(`[data-refl="${r}"]`)){let o=tt(),a=s.querySelector("ul");s.insertBefore(o,a),o.dataset.dropdown=String(!!a),a||s.appendChild(i.cloneNode(!0))}}}function Ze(){let t=document.getElementById("tsd-hierarchy-script");t&&(t.addEventListener("load",Qe),Qe())}async function Qe(){let t=document.querySelector(".tsd-panel.tsd-hierarchy:has(h4 a)");if(!t||!window.hierarchyData)return;let e=+t.dataset.refl,n=await A(window.hierarchyData),r=t.querySelector("ul"),i=document.createElement("ul");if(i.classList.add("tsd-hierarchy"),Ke(i,n,e),r.querySelectorAll("li").length==i.querySelectorAll("li").length)return;let s=document.createElement("span");s.classList.add("tsd-hierarchy-toggle"),s.textContent=window.translations.hierarchy_expand,t.querySelector("h4 a")?.insertAdjacentElement("afterend",s),s.insertAdjacentText("beforebegin",", "),s.addEventListener("click",()=>{s.textContent===window.translations.hierarchy_expand?(r.insertAdjacentElement("afterend",i),r.remove(),s.textContent=window.translations.hierarchy_collapse):(i.insertAdjacentElement("afterend",r),i.remove(),s.textContent=window.translations.hierarchy_expand)})}function Ke(t,e,n){let r=e.roots.filter(i=>et(e,i,n));for(let i of r)t.appendChild(_e(e,i,n))}function _e(t,e,n,r=new Set){if(r.has(e))return;r.add(e);let i=t.reflections[e],s=document.createElement("li");if(s.classList.add("tsd-hierarchy-item"),e===n){let o=s.appendChild(document.createElement("span"));o.textContent=i.name,o.classList.add("tsd-hierarchy-target")}else{for(let a of i.uniqueNameParents||[]){let l=t.reflections[a],c=s.appendChild(document.createElement("a"));c.textContent=l.name,c.href=te+l.url,c.className=l.class+" tsd-signature-type",s.append(document.createTextNode("."))}let o=s.appendChild(document.createElement("a"));o.textContent=t.reflections[e].name,o.href=te+i.url,o.className=i.class+" tsd-signature-type"}if(i.children){let o=s.appendChild(document.createElement("ul"));o.classList.add("tsd-hierarchy");for(let a of i.children){let l=_e(t,a,n,r);l&&o.appendChild(l)}}return r.delete(e),s}function et(t,e,n){if(e===n)return!0;let r=new Set,i=[t.reflections[e]];for(;i.length;){let s=i.pop();if(!r.has(s)){r.add(s);for(let o of s.children||[]){if(o===n)return!0;i.push(t.reflections[o])}}}return!1}function tt(){let t=document.createElementNS("http://www.w3.org/2000/svg","svg");return t.setAttribute("width","20"),t.setAttribute("height","20"),t.setAttribute("viewBox","0 0 24 24"),t.setAttribute("fill","none"),t.innerHTML='',t}G(Y,"a[data-toggle]");G(K,".tsd-accordion");G(Z,".tsd-filter-item input[type=checkbox]");var Oe=document.getElementById("tsd-theme");Oe&&Se(Oe);var nt=new J;Object.defineProperty(window,"app",{value:nt});ve();Ce();Pe();})(); -/*! Bundled license information: - -lunr/lunr.js: - (** - * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 - * Copyright (C) 2020 Oliver Nightingale - * @license MIT - *) - (*! - * lunr.utils - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Set - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.tokenizer - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Pipeline - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Vector - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.stemmer - * Copyright (C) 2020 Oliver Nightingale - * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt - *) - (*! - * lunr.stopWordFilter - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.trimmer - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.TokenSet - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Index - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Builder - * Copyright (C) 2020 Oliver Nightingale - *) -*/ diff --git a/docs/journey/assets/navigation.js b/docs/journey/assets/navigation.js deleted file mode 100644 index a2a18aa74..000000000 --- a/docs/journey/assets/navigation.js +++ /dev/null @@ -1 +0,0 @@ -window.navigationData = "eJyd1tlOAjEUBuB3mWviFpfonRKNaBQj6o3xotCDNJR20sWIxnd3FodZ4JRTr0j4f78ptXNmXr8TB58uOUtutDcKlhdeSH6ffTVQqXePYL10SS9JmZtlJbdMwe4Gqjszt5BZfy4UT84O9k5P9o8Ofnrdq/SZlGM2mSNyFUdqxTq2kEXnP+4Lkx4oeFGMvMJTpm2x8wpVlQIUuhFFSJW0mop3TCpConRpjDYIVGQxTl9zbLtWOdG7EiB5n6VsLKRwS0TttGLs27wTUPM8xhumTmgVEstGjPkI0xCYxVRNm8UdOIZpf3GEFpxAdSFCDN3IqzzWs9tAGy0+p5w5wO6ZtR7Rv9Z6fj7JDwi25EYjwgz+n+oCUbzT3Et0U/9SorV6QiHaKqd6+PC5p8+dvBqY+lVM1syCSfEFvLhhUbTVItpDwSfljgefBt1ahD5MQQ14+WfesMB8Q9rEaz0Y/SE4mOwzxc5Wq0N0R6Phk54DtugqpmqOGVfOcGyRzQpV9eOFcANr0dnXaMSbgSdyp0W0ny2YgZpqBK3iCG0E1uInq9HYZo5b7721N/WqHJu77UbbOz5sUBMD2exGXttqb0MtgHZO8iawUwlgqpoZ1Xuo3eSttwKkt9VP2UTVKYnIn4NhJm+sUW+/lmx6hw==" \ No newline at end of file diff --git a/docs/journey/assets/search.js b/docs/journey/assets/search.js deleted file mode 100644 index e8a9efb33..000000000 --- a/docs/journey/assets/search.js +++ /dev/null @@ -1 +0,0 @@ -window.searchData = "eJytnVmT27gRgP+L8jrlJXHwmLddV1JxUl6ndtf74nJtyRLHViyJig5vHJf/ewieDXQDbGj4ZM+Q3WgAX3cDTYLzbXWu/7ysHt99W33eHberx0w9rI7rQ7V6XG3O1fpa/aO+nY/V15f7XXW8rh5Wt/O+ufZ0O26uu/p4+YG468Wn62Hf3LrZry+XqtG+Wn1/IBr4cNvttz9X/72+Op5ulG77Bq7aY30+rPe7/1Uv1/v9h/Xm84VQjW/iqu+7+q9z/WW3rc6EbucOruLbZRhGQud0MV7d35q+BlWaG4JqRVLmqRbuGPy9rj//2Gkb9V+/nqpxCMANQf1pIibDL9f1+crT95c//miv9xJA98PqtD4bYiljX3RiPgOODXFR7fcCCzV/ri635j8xBowiC5nQoHGOMqAXWGwEnpoefYocgkFmMSO+1J9jp6EXWXAadsenOnoqeqGFzLhc6t+afh3jfPJSX3uhhczY1x/r2/VtLJqd2LKAbneXU32Jg2OSWWw81tvd8WPkYAwyCxlRnc913HwMEvcbEMhGvzSBcB8I3t31e3LdkM2bf0+ebGfdws93G3tpNaOyH8VRKDiMtnh4JjefmsXW2eflfkMmseeY4hn1n+tt9Zu5kzRquHrPfA7rPr92eMdzWghrv0ez6bd/RO7R+Ga33bxpFjKvti/r49Pu4+28Ns5HN+K5+d52X9fb277qVPkbhHfd01In7/Hc/mLEGrXJb97xgdqGbDjeH3SSQTLsqXUzHOyW+5vva9ZHdmCuGDPkjOT5S3V+e97PqhuH0gjczvu5XvWic6u89f7AbXq4+fnNbppctPPFHdTuePfzG77uDlV982Qb3PJ0+/ObbhZiH31rN9zyePfzGz6EXB+3PN1+V9Men/nV7FHfnAKbZXgH33+e6vOm+vF29eyWkNK+k63YuhMLdtNSMLNvrn8NhUWfKceaGR3Dpsxk4baE9Pt6f5vJ9tN9z8n5dkXL3xB/nq+z65RJ4eC8nUiYYks4PL9fmKMHbRhkFjOi+U3130gjBpnnGOFbiqHypb0eYxUv7dSAqpYBjUOCADLBTk7S3A6a2mDAbcbL93jLKOzp63T9Lu27ar/9p7mDVj5cvlv3y/Vp/WG3312/BlqYbrq7nS7kBdrobuAjFvAhV1+MB0HZmaXA+kPlWfB5DBgkFjIgEMo8BrACWdCA0BT/Uj0FrGmuLpEoLF0ROWKUm1laNtdezZBFtM5HizTBu0Xun+9sW1Hfbtm6KcKBIhQOHrSdjc2O8GyNPNqGTmZBI07n+nDypT6/HaPYgqYcmjXk+qO3LOK1ZZJbdHL+c9udq3hKgOCC5nz2JsGAKb3QgmZsZvJlwBhLdEGTttXT+rYP7QsCRvXCrMwQZ1Yd2icGLJrkloR5/Wc8x63M84zwbaRvHw6766vL5Va99NZBnZvuWXoBFbNtRKzuOSbbtZ/tLFxQ9BlR0mMBM0LyjXgy8/3KE5E8RrQy8zmUb8Rmtv7vm5BekLNyCprj4e4n66WX0AMt6s6YfeaxMy9S+TgMx8sgHhwDUtHcHt+7oZ4zaxBd3CQzh56APGvTILuAUYGd9Ovquvbvo81VPhyf1pfX6+Nt7dukQYV9LxuRwyASXssPwuERb/S9uV2boXhz3Ps22LQZdStWd2ILmfLqeK0+ds+5fgku7jw27SZ57hqPb9zbxhVPp/p8jTTqZsktZMwwPC+bhUij4nVDv/ctBY9dwwhtOhWHUcWdJs6Wn96etuur79kEuu3eUlcolk/XI0r/JicGSmc4Eo0SswPJCopfZmp32IBRYhkDAomCbp+VHtjNz6RQ2gR24uSPQiA3eYaBlZHYBhyCmQc339+/TOOXKrR9ow1oZHj1vFgjomZhsGK5idjsq/U5ejRaqWXH42w6F2PDILBM8+2L44HHMrQNrdSyEeJjdQ3UH2kzGpmnXmZZIy4/ffXvdsK2XD58ZVWKY02636KFDRocON6gwYufb5Bn6WDeeH3lfQt4uHpXiePXN4EXe4er/AVJ4KmKpSzmkcooODN/t82muly87xFR7fcyjFeJmEYEXiai2me9TuRvOoBL8D0McMM90PzV/8Zve+lunf5q3nj5rlcyAq/XMs4rOetN31oPaBqXmvNLvF5qNlb6imW41ebm+SoZq9nAMRzcLOsEDqtZ/+Eb3Crn3A2r0dCRG9ws77QNq2H/QRvcLOcIA7O3geM1VHdZJ2uYTfsP1VAtc87TsAfaf5SGHmzOKRqeKwUP0BDexDw7w2p87tgMbp5/YoZlQPCwDG6deU7Gafr9Q/9Wy+O31Zfq3Ga/x5V4IV+UjXBf93h819nUqK4Phy4rbOvNrf3v+/6236vNtT6bm7u7f0hWD++SB6VeyKR4//7h3SDcXmh/MeiYftMKps1PKSWYIsHUEhTNT4ISFEhQWIKy+UlSghIJSktQNT8pSlAhQWUJ6uYnTQlqJKgtwaz5KaMEMySYWYJ581P+IMsXwhLLkVhuiRXNTwUWK5BYYYk19LwrsViJxEp76g0JaYIFUwxN6lDTYpMSogQ2NjepoSEVhCgGJ7XJSQ0PqSREMTqpzU5qiEgVIYrhSW16UsNEqglRjE9q85MaKtKMEMUApTZBqSEjzUnPxBilNkep4SMtSGEMU2rTlBpK0pIUxkilNlPCcCLISCQwVcKmShhSBBmNBOZKOPGoDUh0RCJCkk2WMLQIMioJzJaw2RKGF0FGJoHpEjZdwhAjyOgkMF/C5ksYZgQZoQQmTNiECcOMIAkTmDBhEyYMM4IkTGDChE2YMMwIkjCBCRM2YdIwI0nCJCZM2oRJw4wkCZOYMGkTJg0zkiRMYsKkk/XatEfnPSLx2YRJw4wkopfEfEmbL2mIkSRfEvMlbb6kIUaSfEnMl7T5koYYSfIlMV/S5ksaYiTJl8R8SZsvaYiRJF8S8yVtvpQhRpF8KcyXsvlShhhF8qUwX8rmSxliFMmXwnwpmy9liFEkXwrzpZyVVbu0IvhSxNLK5ksZYlSTHfWLQmpbGPOlbL6UIUYR+VFhupRNlzK8KJIuhelSNl3K8KJIuhSmS9l0KcOLIulSmC5l06UNL5qkS2O6tE2XNrxoki6N6dI2Xdrwokm6NKZL23Rpw4sm6dKYLm3TpZVnljWmSzsr93bpTqZWTSzebbp05kVTY760zZc2xGh614D50jZfuvD4k8Z0aZsubXjRZMjVmC5t05W1dJFOkWG6MpuurKWLdIoM05XZdGUtXaRTZJiuzKYrM7xkpFNkmK7MpiszxGTEViTDdGU2XZnhJSNdIsN0Zc7esN0cki6REdtDm67M8JKRXGeYrsymKzPEZCSaGeYrs/nKDDEZvanFfGU2X7khJiP5yjFfuc1XbojJSL5yzFdu85UbYjKSrxzzldt85YaYnOQrx3zlNl+5j68c85XbfOWGmJyM1znmK7f5yg0xOQlnjvnKnfpDW4Ag4cyJIoTNV26IyYnolWO6cpuu3PCSE3vlHLOV22wVhpacyBIFJquwySoMKzmJZYHJKmyyCsNKTmJZYLIKm6yiJYvEssBkFTZZhaGlILEsMFuFzVZhaClItgrMVmGzVRhaCpKtArNV2GwVhpaCZKvAbBVOhastcZGBryDKXDZdhSGmIANfgfkqbL5Kw0xBBr4SE1bahJWph+sS81XafJXC400lpqu06SqlxyVKzFZps1W2bJEuUWK2SputsmWLdIkSs1XabJUtW6RLlJit0marNLSUpEuUmK3SZqs0tJSkS5SYrdKpobZFVNIlSqKQ6lZSDS4l6RPdNVsc/K6XN8yUpFt011x5p6aaGHBK0jO6a668U1hNDD0l6RzdNVfeqa4mBqCSrlYmRIU1cUqsiWGopAuWCVFmTZw6a+Jf6nfXXHmn2Jq03NE1z4QotyZOvTVpyxVUeTkh6q2JU3BN2oprQmLfXXQVOPj1lXz6OQ5VzUfl/Laen5D4p2RN3wGwq+ontAdQlX23tN/V9hPaBaj6vlvg7yr8Ce0DVJXfLfO3tXvioU1K1fndQn9bvSce3aRUpd8t9bfVe+IBTkpV+t1Sf1frp57iUKV+t9bfFfvJBzkEd06xP23r9/SzHAI6p9qftgV88nEOUe1PnXJ/2lbwySc6RLk/der9aVvCJx/qEPX+1Cn4p20Nn3yuQxT8h9+1T3+/VOYgQ3cC3Ty8ha9zflv90T8eLscXhL6tmlTz+O379+lxcPMTeCJsrpnmWkXmXROkLJ10pSxV4EMdkxZVTGpUEaWne+VwUpWD3jX7Fp6q8X10oEcBPUUn2aygePqmo7qTwgwobPbcLEXjlwUnNQL0TzDtAW83AwwSgAHTnv5FMmANmH/BBKB9yQ0MNLCj2R3zVHRfA5uUSD0pkZqnpP1s97+7dylwz4BVCUuffRYazDswLeOxPb4AAvwsA46WdXLG9znq+u+NAmXA3VKeSePpUzBxYO5z3pgPp28mJQUYnYIXjMCHu0AEAfOleBM2vWUOnEKCkUni9AyvPwNtGmrjOSt63xyoU1Adz9v6tyHB3CfQqJRHkO/4HphHMP4FW+lwXBJABdAseCPmnHcEukqgixdZAkfvQF9BCC54KcY9agiUAS8qeF5kT6gGI5bxoOg/OAO8B0QX1UcXzZvF7iVfyBekNOUNO1pW5ADSvOyzL7Nz/ckuoAz0rkh6ZTzD+vRgr3/O/SlG0AIIGzkvlw6JZ/xELkiqoPOCh4SjDY2nAgaqqK5bKlFyU8AVFM9dHb1ukBMAH2Z+8+Zws2UELMbZ13/hFawxQF9llC6cgBO4bk55eQYqcxdRZncNFPI8pVfYpht6zaqBVh3V5VZp9wEdoA70WsfbWPdfgQMKwfzqKKxbhe3XoIA24Hc6youbJcmhO9IJYkIOok5cZxttOMYUwDEK3qIN6MOeC5Kj4i28XHVWkNVgAaCjgJ703YZD6KDXYIaLqDn51GwS1sOfZgHLcZAtY9XhSUnBKKZRozh+YxeEPqBMRCkjt+YKAKiihu7oRBcBBk1EjZrRhCI8mFIR5bTjnyzaooW7Bp3NovAznwfvJgOHfAHWViLK54zW+lQdzT/W5+SBcmCyiJqf0/iXl0B2AsnpHl2n7q86APOAQ4uoEZ0Ob4C9C9xyJlGLmvZk1fhdL9Bj4C4yyl26as+u+7AVCA1AYR6V7YBCNzNnoON5VBqYDuCAYczhMEYtj4y68TPTcOWQQJW8cXQTewboy3gBov+cKfBgkN80D5Dxb7mA7sAB4jnV8J11QBZQItlKxhNDwBq419adaMqMeeNHx8Awg/VTpjq5nAeBuzopQBdLpgqcsSSAW/LiY3diERgC8ns/QCmPoDEbkCVlWCZlqiO8Q4ERVzzv7f7cBBgjoEIyVeBgl8F1C89Jh2+MAiXA1TMehe1nDIEGgE3OSwr9GW8wIMDRZU+xeWrJU2av2EHEznjTPJ7lBF5qbcYGCHmDTNXCMsBexotk/cc/QKAHNpXcsekO5QIlQMfQLV44G86dgkGCG0wxhDIeAuAPlgAM4IqFByPhoBIgLbnW4DpGAcJQyZsy96sXYNhhrT7hdu2K91QFnEBeUKTWXimsyA0Ox3x21B9rB9taEAAGppgDBj42AQYL7qASHp3j32QBHIAxl7yhcqdNgWFS/TBpnut1XztwiqoamKR50aRZP/SrNcswYFekmqf2D58Cm4BJXFVO5QpO1wAAb5SoBW0KK3RyYJOXJnFNA3RP9cstPTwsS3hUEF4I5rFkhJj3D6vT7lTtd8dG5N3779//DxcnqA0="; \ No newline at end of file diff --git a/docs/journey/assets/style.css b/docs/journey/assets/style.css deleted file mode 100644 index 2ab8b836e..000000000 --- a/docs/journey/assets/style.css +++ /dev/null @@ -1,1611 +0,0 @@ -@layer typedoc { - :root { - /* Light */ - --light-color-background: #f2f4f8; - --light-color-background-secondary: #eff0f1; - --light-color-warning-text: #222; - --light-color-background-warning: #e6e600; - --light-color-accent: #c5c7c9; - --light-color-active-menu-item: var(--light-color-accent); - --light-color-text: #222; - --light-color-text-aside: #6e6e6e; - - --light-color-icon-background: var(--light-color-background); - --light-color-icon-text: var(--light-color-text); - - --light-color-comment-tag-text: var(--light-color-text); - --light-color-comment-tag: var(--light-color-background); - - --light-color-link: #1f70c2; - --light-color-focus-outline: #3584e4; - - --light-color-ts-keyword: #056bd6; - --light-color-ts-project: #b111c9; - --light-color-ts-module: var(--light-color-ts-project); - --light-color-ts-namespace: var(--light-color-ts-project); - --light-color-ts-enum: #7e6f15; - --light-color-ts-enum-member: var(--light-color-ts-enum); - --light-color-ts-variable: #4760ec; - --light-color-ts-function: #572be7; - --light-color-ts-class: #1f70c2; - --light-color-ts-interface: #108024; - --light-color-ts-constructor: var(--light-color-ts-class); - --light-color-ts-property: #9f5f30; - --light-color-ts-method: #be3989; - --light-color-ts-reference: #ff4d82; - --light-color-ts-call-signature: var(--light-color-ts-method); - --light-color-ts-index-signature: var(--light-color-ts-property); - --light-color-ts-constructor-signature: var( - --light-color-ts-constructor - ); - --light-color-ts-parameter: var(--light-color-ts-variable); - /* type literal not included as links will never be generated to it */ - --light-color-ts-type-parameter: #a55c0e; - --light-color-ts-accessor: #c73c3c; - --light-color-ts-get-signature: var(--light-color-ts-accessor); - --light-color-ts-set-signature: var(--light-color-ts-accessor); - --light-color-ts-type-alias: #d51270; - /* reference not included as links will be colored with the kind that it points to */ - --light-color-document: #000000; - - --light-color-alert-note: #0969d9; - --light-color-alert-tip: #1a7f37; - --light-color-alert-important: #8250df; - --light-color-alert-warning: #9a6700; - --light-color-alert-caution: #cf222e; - - --light-external-icon: url("data:image/svg+xml;utf8,"); - --light-color-scheme: light; - - /* Dark */ - --dark-color-background: #2b2e33; - --dark-color-background-secondary: #1e2024; - --dark-color-background-warning: #bebe00; - --dark-color-warning-text: #222; - --dark-color-accent: #9096a2; - --dark-color-active-menu-item: #5d5d6a; - --dark-color-text: #f5f5f5; - --dark-color-text-aside: #dddddd; - - --dark-color-icon-background: var(--dark-color-background-secondary); - --dark-color-icon-text: var(--dark-color-text); - - --dark-color-comment-tag-text: var(--dark-color-text); - --dark-color-comment-tag: var(--dark-color-background); - - --dark-color-link: #00aff4; - --dark-color-focus-outline: #4c97f2; - - --dark-color-ts-keyword: #3399ff; - --dark-color-ts-project: #e358ff; - --dark-color-ts-module: var(--dark-color-ts-project); - --dark-color-ts-namespace: var(--dark-color-ts-project); - --dark-color-ts-enum: #f4d93e; - --dark-color-ts-enum-member: var(--dark-color-ts-enum); - --dark-color-ts-variable: #798dff; - --dark-color-ts-function: #a280ff; - --dark-color-ts-class: #8ac4ff; - --dark-color-ts-interface: #6cff87; - --dark-color-ts-constructor: var(--dark-color-ts-class); - --dark-color-ts-property: #ff984d; - --dark-color-ts-method: #ff4db8; - --dark-color-ts-reference: #ff4d82; - --dark-color-ts-call-signature: var(--dark-color-ts-method); - --dark-color-ts-index-signature: var(--dark-color-ts-property); - --dark-color-ts-constructor-signature: var(--dark-color-ts-constructor); - --dark-color-ts-parameter: var(--dark-color-ts-variable); - /* type literal not included as links will never be generated to it */ - --dark-color-ts-type-parameter: #e07d13; - --dark-color-ts-accessor: #ff6060; - --dark-color-ts-get-signature: var(--dark-color-ts-accessor); - --dark-color-ts-set-signature: var(--dark-color-ts-accessor); - --dark-color-ts-type-alias: #ff6492; - /* reference not included as links will be colored with the kind that it points to */ - --dark-color-document: #ffffff; - - --dark-color-alert-note: #0969d9; - --dark-color-alert-tip: #1a7f37; - --dark-color-alert-important: #8250df; - --dark-color-alert-warning: #9a6700; - --dark-color-alert-caution: #cf222e; - - --dark-external-icon: url("data:image/svg+xml;utf8,"); - --dark-color-scheme: dark; - } - - @media (prefers-color-scheme: light) { - :root { - --color-background: var(--light-color-background); - --color-background-secondary: var( - --light-color-background-secondary - ); - --color-background-warning: var(--light-color-background-warning); - --color-warning-text: var(--light-color-warning-text); - --color-accent: var(--light-color-accent); - --color-active-menu-item: var(--light-color-active-menu-item); - --color-text: var(--light-color-text); - --color-text-aside: var(--light-color-text-aside); - - --color-icon-background: var(--light-color-icon-background); - --color-icon-text: var(--light-color-icon-text); - - --color-comment-tag-text: var(--light-color-text); - --color-comment-tag: var(--light-color-background); - - --color-link: var(--light-color-link); - --color-focus-outline: var(--light-color-focus-outline); - - --color-ts-keyword: var(--light-color-ts-keyword); - --color-ts-project: var(--light-color-ts-project); - --color-ts-module: var(--light-color-ts-module); - --color-ts-namespace: var(--light-color-ts-namespace); - --color-ts-enum: var(--light-color-ts-enum); - --color-ts-enum-member: var(--light-color-ts-enum-member); - --color-ts-variable: var(--light-color-ts-variable); - --color-ts-function: var(--light-color-ts-function); - --color-ts-class: var(--light-color-ts-class); - --color-ts-interface: var(--light-color-ts-interface); - --color-ts-constructor: var(--light-color-ts-constructor); - --color-ts-property: var(--light-color-ts-property); - --color-ts-method: var(--light-color-ts-method); - --color-ts-reference: var(--light-color-ts-reference); - --color-ts-call-signature: var(--light-color-ts-call-signature); - --color-ts-index-signature: var(--light-color-ts-index-signature); - --color-ts-constructor-signature: var( - --light-color-ts-constructor-signature - ); - --color-ts-parameter: var(--light-color-ts-parameter); - --color-ts-type-parameter: var(--light-color-ts-type-parameter); - --color-ts-accessor: var(--light-color-ts-accessor); - --color-ts-get-signature: var(--light-color-ts-get-signature); - --color-ts-set-signature: var(--light-color-ts-set-signature); - --color-ts-type-alias: var(--light-color-ts-type-alias); - --color-document: var(--light-color-document); - - --color-alert-note: var(--light-color-alert-note); - --color-alert-tip: var(--light-color-alert-tip); - --color-alert-important: var(--light-color-alert-important); - --color-alert-warning: var(--light-color-alert-warning); - --color-alert-caution: var(--light-color-alert-caution); - - --external-icon: var(--light-external-icon); - --color-scheme: var(--light-color-scheme); - } - } - - @media (prefers-color-scheme: dark) { - :root { - --color-background: var(--dark-color-background); - --color-background-secondary: var( - --dark-color-background-secondary - ); - --color-background-warning: var(--dark-color-background-warning); - --color-warning-text: var(--dark-color-warning-text); - --color-accent: var(--dark-color-accent); - --color-active-menu-item: var(--dark-color-active-menu-item); - --color-text: var(--dark-color-text); - --color-text-aside: var(--dark-color-text-aside); - - --color-icon-background: var(--dark-color-icon-background); - --color-icon-text: var(--dark-color-icon-text); - - --color-comment-tag-text: var(--dark-color-text); - --color-comment-tag: var(--dark-color-background); - - --color-link: var(--dark-color-link); - --color-focus-outline: var(--dark-color-focus-outline); - - --color-ts-keyword: var(--dark-color-ts-keyword); - --color-ts-project: var(--dark-color-ts-project); - --color-ts-module: var(--dark-color-ts-module); - --color-ts-namespace: var(--dark-color-ts-namespace); - --color-ts-enum: var(--dark-color-ts-enum); - --color-ts-enum-member: var(--dark-color-ts-enum-member); - --color-ts-variable: var(--dark-color-ts-variable); - --color-ts-function: var(--dark-color-ts-function); - --color-ts-class: var(--dark-color-ts-class); - --color-ts-interface: var(--dark-color-ts-interface); - --color-ts-constructor: var(--dark-color-ts-constructor); - --color-ts-property: var(--dark-color-ts-property); - --color-ts-method: var(--dark-color-ts-method); - --color-ts-reference: var(--dark-color-ts-reference); - --color-ts-call-signature: var(--dark-color-ts-call-signature); - --color-ts-index-signature: var(--dark-color-ts-index-signature); - --color-ts-constructor-signature: var( - --dark-color-ts-constructor-signature - ); - --color-ts-parameter: var(--dark-color-ts-parameter); - --color-ts-type-parameter: var(--dark-color-ts-type-parameter); - --color-ts-accessor: var(--dark-color-ts-accessor); - --color-ts-get-signature: var(--dark-color-ts-get-signature); - --color-ts-set-signature: var(--dark-color-ts-set-signature); - --color-ts-type-alias: var(--dark-color-ts-type-alias); - --color-document: var(--dark-color-document); - - --color-alert-note: var(--dark-color-alert-note); - --color-alert-tip: var(--dark-color-alert-tip); - --color-alert-important: var(--dark-color-alert-important); - --color-alert-warning: var(--dark-color-alert-warning); - --color-alert-caution: var(--dark-color-alert-caution); - - --external-icon: var(--dark-external-icon); - --color-scheme: var(--dark-color-scheme); - } - } - - html { - color-scheme: var(--color-scheme); - } - - body { - margin: 0; - } - - :root[data-theme="light"] { - --color-background: var(--light-color-background); - --color-background-secondary: var(--light-color-background-secondary); - --color-background-warning: var(--light-color-background-warning); - --color-warning-text: var(--light-color-warning-text); - --color-icon-background: var(--light-color-icon-background); - --color-accent: var(--light-color-accent); - --color-active-menu-item: var(--light-color-active-menu-item); - --color-text: var(--light-color-text); - --color-text-aside: var(--light-color-text-aside); - --color-icon-text: var(--light-color-icon-text); - - --color-comment-tag-text: var(--light-color-text); - --color-comment-tag: var(--light-color-background); - - --color-link: var(--light-color-link); - --color-focus-outline: var(--light-color-focus-outline); - - --color-ts-keyword: var(--light-color-ts-keyword); - --color-ts-project: var(--light-color-ts-project); - --color-ts-module: var(--light-color-ts-module); - --color-ts-namespace: var(--light-color-ts-namespace); - --color-ts-enum: var(--light-color-ts-enum); - --color-ts-enum-member: var(--light-color-ts-enum-member); - --color-ts-variable: var(--light-color-ts-variable); - --color-ts-function: var(--light-color-ts-function); - --color-ts-class: var(--light-color-ts-class); - --color-ts-interface: var(--light-color-ts-interface); - --color-ts-constructor: var(--light-color-ts-constructor); - --color-ts-property: var(--light-color-ts-property); - --color-ts-method: var(--light-color-ts-method); - --color-ts-reference: var(--light-color-ts-reference); - --color-ts-call-signature: var(--light-color-ts-call-signature); - --color-ts-index-signature: var(--light-color-ts-index-signature); - --color-ts-constructor-signature: var( - --light-color-ts-constructor-signature - ); - --color-ts-parameter: var(--light-color-ts-parameter); - --color-ts-type-parameter: var(--light-color-ts-type-parameter); - --color-ts-accessor: var(--light-color-ts-accessor); - --color-ts-get-signature: var(--light-color-ts-get-signature); - --color-ts-set-signature: var(--light-color-ts-set-signature); - --color-ts-type-alias: var(--light-color-ts-type-alias); - --color-document: var(--light-color-document); - - --color-note: var(--light-color-note); - --color-tip: var(--light-color-tip); - --color-important: var(--light-color-important); - --color-warning: var(--light-color-warning); - --color-caution: var(--light-color-caution); - - --external-icon: var(--light-external-icon); - --color-scheme: var(--light-color-scheme); - } - - :root[data-theme="dark"] { - --color-background: var(--dark-color-background); - --color-background-secondary: var(--dark-color-background-secondary); - --color-background-warning: var(--dark-color-background-warning); - --color-warning-text: var(--dark-color-warning-text); - --color-icon-background: var(--dark-color-icon-background); - --color-accent: var(--dark-color-accent); - --color-active-menu-item: var(--dark-color-active-menu-item); - --color-text: var(--dark-color-text); - --color-text-aside: var(--dark-color-text-aside); - --color-icon-text: var(--dark-color-icon-text); - - --color-comment-tag-text: var(--dark-color-text); - --color-comment-tag: var(--dark-color-background); - - --color-link: var(--dark-color-link); - --color-focus-outline: var(--dark-color-focus-outline); - - --color-ts-keyword: var(--dark-color-ts-keyword); - --color-ts-project: var(--dark-color-ts-project); - --color-ts-module: var(--dark-color-ts-module); - --color-ts-namespace: var(--dark-color-ts-namespace); - --color-ts-enum: var(--dark-color-ts-enum); - --color-ts-enum-member: var(--dark-color-ts-enum-member); - --color-ts-variable: var(--dark-color-ts-variable); - --color-ts-function: var(--dark-color-ts-function); - --color-ts-class: var(--dark-color-ts-class); - --color-ts-interface: var(--dark-color-ts-interface); - --color-ts-constructor: var(--dark-color-ts-constructor); - --color-ts-property: var(--dark-color-ts-property); - --color-ts-method: var(--dark-color-ts-method); - --color-ts-reference: var(--dark-color-ts-reference); - --color-ts-call-signature: var(--dark-color-ts-call-signature); - --color-ts-index-signature: var(--dark-color-ts-index-signature); - --color-ts-constructor-signature: var( - --dark-color-ts-constructor-signature - ); - --color-ts-parameter: var(--dark-color-ts-parameter); - --color-ts-type-parameter: var(--dark-color-ts-type-parameter); - --color-ts-accessor: var(--dark-color-ts-accessor); - --color-ts-get-signature: var(--dark-color-ts-get-signature); - --color-ts-set-signature: var(--dark-color-ts-set-signature); - --color-ts-type-alias: var(--dark-color-ts-type-alias); - --color-document: var(--dark-color-document); - - --color-note: var(--dark-color-note); - --color-tip: var(--dark-color-tip); - --color-important: var(--dark-color-important); - --color-warning: var(--dark-color-warning); - --color-caution: var(--dark-color-caution); - - --external-icon: var(--dark-external-icon); - --color-scheme: var(--dark-color-scheme); - } - - *:focus-visible, - .tsd-accordion-summary:focus-visible svg { - outline: 2px solid var(--color-focus-outline); - } - - .always-visible, - .always-visible .tsd-signatures { - display: inherit !important; - } - - h1, - h2, - h3, - h4, - h5, - h6 { - line-height: 1.2; - } - - h1 { - font-size: 1.875rem; - margin: 0.67rem 0; - } - - h2 { - font-size: 1.5rem; - margin: 0.83rem 0; - } - - h3 { - font-size: 1.25rem; - margin: 1rem 0; - } - - h4 { - font-size: 1.05rem; - margin: 1.33rem 0; - } - - h5 { - font-size: 1rem; - margin: 1.5rem 0; - } - - h6 { - font-size: 0.875rem; - margin: 2.33rem 0; - } - - dl, - menu, - ol, - ul { - margin: 1em 0; - } - - dd { - margin: 0 0 0 34px; - } - - .container { - max-width: 1700px; - padding: 0 2rem; - } - - /* Footer */ - footer { - border-top: 1px solid var(--color-accent); - padding-top: 1rem; - padding-bottom: 1rem; - max-height: 3.5rem; - } - footer > p { - margin: 0 1em; - } - - .container-main { - margin: 0 auto; - /* toolbar, footer, margin */ - min-height: calc(100vh - 41px - 56px - 4rem); - } - - @keyframes fade-in { - from { - opacity: 0; - } - to { - opacity: 1; - } - } - @keyframes fade-out { - from { - opacity: 1; - visibility: visible; - } - to { - opacity: 0; - } - } - @keyframes fade-in-delayed { - 0% { - opacity: 0; - } - 33% { - opacity: 0; - } - 100% { - opacity: 1; - } - } - @keyframes fade-out-delayed { - 0% { - opacity: 1; - visibility: visible; - } - 66% { - opacity: 0; - } - 100% { - opacity: 0; - } - } - @keyframes pop-in-from-right { - from { - transform: translate(100%, 0); - } - to { - transform: translate(0, 0); - } - } - @keyframes pop-out-to-right { - from { - transform: translate(0, 0); - visibility: visible; - } - to { - transform: translate(100%, 0); - } - } - body { - background: var(--color-background); - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", - Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; - font-size: 16px; - color: var(--color-text); - } - - a { - color: var(--color-link); - text-decoration: none; - } - a:hover { - text-decoration: underline; - } - a.external[target="_blank"] { - background-image: var(--external-icon); - background-position: top 3px right; - background-repeat: no-repeat; - padding-right: 13px; - } - a.tsd-anchor-link { - color: var(--color-text); - } - - code, - pre { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; - padding: 0.2em; - margin: 0; - font-size: 0.875rem; - border-radius: 0.8em; - } - - pre { - position: relative; - white-space: pre-wrap; - word-wrap: break-word; - padding: 10px; - border: 1px solid var(--color-accent); - margin-bottom: 8px; - } - pre code { - padding: 0; - font-size: 100%; - } - pre > button { - position: absolute; - top: 10px; - right: 10px; - opacity: 0; - transition: opacity 0.1s; - box-sizing: border-box; - } - pre:hover > button, - pre > button.visible { - opacity: 1; - } - - blockquote { - margin: 1em 0; - padding-left: 1em; - border-left: 4px solid gray; - } - - .tsd-typography { - line-height: 1.333em; - } - .tsd-typography ul { - list-style: square; - padding: 0 0 0 20px; - margin: 0; - } - .tsd-typography .tsd-index-panel h3, - .tsd-index-panel .tsd-typography h3, - .tsd-typography h4, - .tsd-typography h5, - .tsd-typography h6 { - font-size: 1em; - } - .tsd-typography h5, - .tsd-typography h6 { - font-weight: normal; - } - .tsd-typography p, - .tsd-typography ul, - .tsd-typography ol { - margin: 1em 0; - } - .tsd-typography table { - border-collapse: collapse; - border: none; - } - .tsd-typography td, - .tsd-typography th { - padding: 6px 13px; - border: 1px solid var(--color-accent); - } - .tsd-typography thead, - .tsd-typography tr:nth-child(even) { - background-color: var(--color-background-secondary); - } - - .tsd-alert { - padding: 8px 16px; - margin-bottom: 16px; - border-left: 0.25em solid var(--alert-color); - } - .tsd-alert blockquote > :last-child, - .tsd-alert > :last-child { - margin-bottom: 0; - } - .tsd-alert-title { - color: var(--alert-color); - display: inline-flex; - align-items: center; - } - .tsd-alert-title span { - margin-left: 4px; - } - - .tsd-alert-note { - --alert-color: var(--color-alert-note); - } - .tsd-alert-tip { - --alert-color: var(--color-alert-tip); - } - .tsd-alert-important { - --alert-color: var(--color-alert-important); - } - .tsd-alert-warning { - --alert-color: var(--color-alert-warning); - } - .tsd-alert-caution { - --alert-color: var(--color-alert-caution); - } - - .tsd-breadcrumb { - margin: 0; - padding: 0; - color: var(--color-text-aside); - } - .tsd-breadcrumb a { - color: var(--color-text-aside); - text-decoration: none; - } - .tsd-breadcrumb a:hover { - text-decoration: underline; - } - .tsd-breadcrumb li { - display: inline; - } - .tsd-breadcrumb li:after { - content: " / "; - } - - .tsd-comment-tags { - display: flex; - flex-direction: column; - } - dl.tsd-comment-tag-group { - display: flex; - align-items: center; - overflow: hidden; - margin: 0.5em 0; - } - dl.tsd-comment-tag-group dt { - display: flex; - margin-right: 0.5em; - font-size: 0.875em; - font-weight: normal; - } - dl.tsd-comment-tag-group dd { - margin: 0; - } - code.tsd-tag { - padding: 0.25em 0.4em; - border: 0.1em solid var(--color-accent); - margin-right: 0.25em; - font-size: 70%; - } - h1 code.tsd-tag:first-of-type { - margin-left: 0.25em; - } - - dl.tsd-comment-tag-group dd:before, - dl.tsd-comment-tag-group dd:after { - content: " "; - } - dl.tsd-comment-tag-group dd pre, - dl.tsd-comment-tag-group dd:after { - clear: both; - } - dl.tsd-comment-tag-group p { - margin: 0; - } - - .tsd-panel.tsd-comment .lead { - font-size: 1.1em; - line-height: 1.333em; - margin-bottom: 2em; - } - .tsd-panel.tsd-comment .lead:last-child { - margin-bottom: 0; - } - - .tsd-filter-visibility h4 { - font-size: 1rem; - padding-top: 0.75rem; - padding-bottom: 0.5rem; - margin: 0; - } - .tsd-filter-item:not(:last-child) { - margin-bottom: 0.5rem; - } - .tsd-filter-input { - display: flex; - width: -moz-fit-content; - width: fit-content; - align-items: center; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - cursor: pointer; - } - .tsd-filter-input input[type="checkbox"] { - cursor: pointer; - position: absolute; - width: 1.5em; - height: 1.5em; - opacity: 0; - } - .tsd-filter-input input[type="checkbox"]:disabled { - pointer-events: none; - } - .tsd-filter-input svg { - cursor: pointer; - width: 1.5em; - height: 1.5em; - margin-right: 0.5em; - border-radius: 0.33em; - /* Leaving this at full opacity breaks event listeners on Firefox. - Don't remove unless you know what you're doing. */ - opacity: 0.99; - } - .tsd-filter-input input[type="checkbox"]:focus-visible + svg { - outline: 2px solid var(--color-focus-outline); - } - .tsd-checkbox-background { - fill: var(--color-accent); - } - input[type="checkbox"]:checked ~ svg .tsd-checkbox-checkmark { - stroke: var(--color-text); - } - .tsd-filter-input input:disabled ~ svg > .tsd-checkbox-background { - fill: var(--color-background); - stroke: var(--color-accent); - stroke-width: 0.25rem; - } - .tsd-filter-input input:disabled ~ svg > .tsd-checkbox-checkmark { - stroke: var(--color-accent); - } - - .settings-label { - font-weight: bold; - text-transform: uppercase; - display: inline-block; - } - - .tsd-filter-visibility .settings-label { - margin: 0.75rem 0 0.5rem 0; - } - - .tsd-theme-toggle .settings-label { - margin: 0.75rem 0.75rem 0 0; - } - - .tsd-hierarchy h4 label:hover span { - text-decoration: underline; - } - - .tsd-hierarchy { - list-style: square; - margin: 0; - } - .tsd-hierarchy-target { - font-weight: bold; - } - .tsd-hierarchy-toggle { - color: var(--color-link); - cursor: pointer; - } - - .tsd-full-hierarchy:not(:last-child) { - margin-bottom: 1em; - padding-bottom: 1em; - border-bottom: 1px solid var(--color-accent); - } - .tsd-full-hierarchy, - .tsd-full-hierarchy ul { - list-style: none; - margin: 0; - padding: 0; - } - .tsd-full-hierarchy ul { - padding-left: 1.5rem; - } - .tsd-full-hierarchy a { - padding: 0.25rem 0 !important; - font-size: 1rem; - display: inline-flex; - align-items: center; - color: var(--color-text); - } - .tsd-full-hierarchy svg[data-dropdown] { - cursor: pointer; - } - .tsd-full-hierarchy svg[data-dropdown="false"] { - transform: rotate(-90deg); - } - .tsd-full-hierarchy svg[data-dropdown="false"] ~ ul { - display: none; - } - - .tsd-panel-group.tsd-index-group { - margin-bottom: 0; - } - .tsd-index-panel .tsd-index-list { - list-style: none; - line-height: 1.333em; - margin: 0; - padding: 0.25rem 0 0 0; - overflow: hidden; - display: grid; - grid-template-columns: repeat(3, 1fr); - column-gap: 1rem; - grid-template-rows: auto; - } - @media (max-width: 1024px) { - .tsd-index-panel .tsd-index-list { - grid-template-columns: repeat(2, 1fr); - } - } - @media (max-width: 768px) { - .tsd-index-panel .tsd-index-list { - grid-template-columns: repeat(1, 1fr); - } - } - .tsd-index-panel .tsd-index-list li { - -webkit-page-break-inside: avoid; - -moz-page-break-inside: avoid; - -ms-page-break-inside: avoid; - -o-page-break-inside: avoid; - page-break-inside: avoid; - } - - .tsd-flag { - display: inline-block; - padding: 0.25em 0.4em; - border-radius: 4px; - color: var(--color-comment-tag-text); - background-color: var(--color-comment-tag); - text-indent: 0; - font-size: 75%; - line-height: 1; - font-weight: normal; - } - - .tsd-anchor { - position: relative; - top: -100px; - } - - .tsd-member { - position: relative; - } - .tsd-member .tsd-anchor + h3 { - display: flex; - align-items: center; - margin-top: 0; - margin-bottom: 0; - border-bottom: none; - } - - .tsd-navigation.settings { - margin: 1rem 0; - } - .tsd-navigation > a, - .tsd-navigation .tsd-accordion-summary { - width: calc(100% - 0.25rem); - display: flex; - align-items: center; - } - .tsd-navigation a, - .tsd-navigation summary > span, - .tsd-page-navigation a { - display: flex; - width: calc(100% - 0.25rem); - align-items: center; - padding: 0.25rem; - color: var(--color-text); - text-decoration: none; - box-sizing: border-box; - } - .tsd-navigation a.current, - .tsd-page-navigation a.current { - background: var(--color-active-menu-item); - } - .tsd-navigation a:hover, - .tsd-page-navigation a:hover { - text-decoration: underline; - } - .tsd-navigation ul, - .tsd-page-navigation ul { - margin-top: 0; - margin-bottom: 0; - padding: 0; - list-style: none; - } - .tsd-navigation li, - .tsd-page-navigation li { - padding: 0; - max-width: 100%; - } - .tsd-navigation .tsd-nav-link { - display: none; - } - .tsd-nested-navigation { - margin-left: 3rem; - } - .tsd-nested-navigation > li > details { - margin-left: -1.5rem; - } - .tsd-small-nested-navigation { - margin-left: 1.5rem; - } - .tsd-small-nested-navigation > li > details { - margin-left: -1.5rem; - } - - .tsd-page-navigation-section { - margin-left: 10px; - } - .tsd-page-navigation-section > summary { - padding: 0.25rem; - } - .tsd-page-navigation-section > div { - margin-left: 20px; - } - .tsd-page-navigation ul { - padding-left: 1.75rem; - } - - #tsd-sidebar-links a { - margin-top: 0; - margin-bottom: 0.5rem; - line-height: 1.25rem; - } - #tsd-sidebar-links a:last-of-type { - margin-bottom: 0; - } - - a.tsd-index-link { - padding: 0.25rem 0 !important; - font-size: 1rem; - line-height: 1.25rem; - display: inline-flex; - align-items: center; - color: var(--color-text); - } - .tsd-accordion-summary { - list-style-type: none; /* hide marker on non-safari */ - outline: none; /* broken on safari, so just hide it */ - } - .tsd-accordion-summary::-webkit-details-marker { - display: none; /* hide marker on safari */ - } - .tsd-accordion-summary, - .tsd-accordion-summary a { - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; - - cursor: pointer; - } - .tsd-accordion-summary a { - width: calc(100% - 1.5rem); - } - .tsd-accordion-summary > * { - margin-top: 0; - margin-bottom: 0; - padding-top: 0; - padding-bottom: 0; - } - .tsd-accordion .tsd-accordion-summary > svg { - margin-left: 0.25rem; - vertical-align: text-top; - } - /* - * We need to be careful to target the arrow indicating whether the accordion - * is open, but not any other SVGs included in the details element. - */ - .tsd-accordion:not([open]) > .tsd-accordion-summary > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h1 > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h2 > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h3 > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h4 > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h5 > svg:first-child { - transform: rotate(-90deg); - } - .tsd-index-content > :not(:first-child) { - margin-top: 0.75rem; - } - .tsd-index-heading { - margin-top: 1.5rem; - margin-bottom: 0.75rem; - } - - .tsd-no-select { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - } - .tsd-kind-icon { - margin-right: 0.5rem; - width: 1.25rem; - height: 1.25rem; - min-width: 1.25rem; - min-height: 1.25rem; - } - .tsd-signature > .tsd-kind-icon { - margin-right: 0.8rem; - } - - .tsd-panel { - margin-bottom: 2.5rem; - } - .tsd-panel.tsd-member { - margin-bottom: 4rem; - } - .tsd-panel:empty { - display: none; - } - .tsd-panel > h1, - .tsd-panel > h2, - .tsd-panel > h3 { - margin: 1.5rem -1.5rem 0.75rem -1.5rem; - padding: 0 1.5rem 0.75rem 1.5rem; - } - .tsd-panel > h1.tsd-before-signature, - .tsd-panel > h2.tsd-before-signature, - .tsd-panel > h3.tsd-before-signature { - margin-bottom: 0; - border-bottom: none; - } - - .tsd-panel-group { - margin: 2rem 0; - } - .tsd-panel-group.tsd-index-group { - margin: 2rem 0; - } - .tsd-panel-group.tsd-index-group details { - margin: 2rem 0; - } - .tsd-panel-group > .tsd-accordion-summary { - margin-bottom: 1rem; - } - - #tsd-search { - transition: background-color 0.2s; - } - #tsd-search .title { - position: relative; - z-index: 2; - } - #tsd-search .field { - position: absolute; - left: 0; - top: 0; - right: 2.5rem; - height: 100%; - } - #tsd-search .field input { - box-sizing: border-box; - position: relative; - top: -50px; - z-index: 1; - width: 100%; - padding: 0 10px; - opacity: 0; - outline: 0; - border: 0; - background: transparent; - color: var(--color-text); - } - #tsd-search .field label { - position: absolute; - overflow: hidden; - right: -40px; - } - #tsd-search .field input, - #tsd-search .title, - #tsd-toolbar-links a { - transition: opacity 0.2s; - } - #tsd-search .results { - position: absolute; - visibility: hidden; - top: 40px; - width: 100%; - margin: 0; - padding: 0; - list-style: none; - box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); - } - #tsd-search .results li { - background-color: var(--color-background); - line-height: initial; - padding: 4px; - } - #tsd-search .results li:nth-child(even) { - background-color: var(--color-background-secondary); - } - #tsd-search .results li.state { - display: none; - } - #tsd-search .results li.current:not(.no-results), - #tsd-search .results li:hover:not(.no-results) { - background-color: var(--color-accent); - } - #tsd-search .results a { - display: flex; - align-items: center; - padding: 0.25rem; - box-sizing: border-box; - } - #tsd-search .results a:before { - top: 10px; - } - #tsd-search .results span.parent { - color: var(--color-text-aside); - font-weight: normal; - } - #tsd-search.has-focus { - background-color: var(--color-accent); - } - #tsd-search.has-focus .field input { - top: 0; - opacity: 1; - } - #tsd-search.has-focus .title, - #tsd-search.has-focus #tsd-toolbar-links a { - z-index: 0; - opacity: 0; - } - #tsd-search.has-focus .results { - visibility: visible; - } - #tsd-search.loading .results li.state.loading { - display: block; - } - #tsd-search.failure .results li.state.failure { - display: block; - } - - #tsd-toolbar-links { - position: absolute; - top: 0; - right: 2rem; - height: 100%; - display: flex; - align-items: center; - justify-content: flex-end; - } - #tsd-toolbar-links a { - margin-left: 1.5rem; - } - #tsd-toolbar-links a:hover { - text-decoration: underline; - } - - .tsd-signature { - margin: 0 0 1rem 0; - padding: 1rem 0.5rem; - border: 1px solid var(--color-accent); - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; - font-size: 14px; - overflow-x: auto; - } - - .tsd-signature-keyword { - color: var(--color-ts-keyword); - font-weight: normal; - } - - .tsd-signature-symbol { - color: var(--color-text-aside); - font-weight: normal; - } - - .tsd-signature-type { - font-style: italic; - font-weight: normal; - } - - .tsd-signatures { - padding: 0; - margin: 0 0 1em 0; - list-style-type: none; - } - .tsd-signatures .tsd-signature { - margin: 0; - border-color: var(--color-accent); - border-width: 1px 0; - transition: background-color 0.1s; - } - .tsd-signatures .tsd-index-signature:not(:last-child) { - margin-bottom: 1em; - } - .tsd-signatures .tsd-index-signature .tsd-signature { - border-width: 1px; - } - .tsd-description .tsd-signatures .tsd-signature { - border-width: 1px; - } - - ul.tsd-parameter-list, - ul.tsd-type-parameter-list { - list-style: square; - margin: 0; - padding-left: 20px; - } - ul.tsd-parameter-list > li.tsd-parameter-signature, - ul.tsd-type-parameter-list > li.tsd-parameter-signature { - list-style: none; - margin-left: -20px; - } - ul.tsd-parameter-list h5, - ul.tsd-type-parameter-list h5 { - font-size: 16px; - margin: 1em 0 0.5em 0; - } - .tsd-sources { - margin-top: 1rem; - font-size: 0.875em; - } - .tsd-sources a { - color: var(--color-text-aside); - text-decoration: underline; - } - .tsd-sources ul { - list-style: none; - padding: 0; - } - - .tsd-page-toolbar { - position: sticky; - z-index: 1; - top: 0; - left: 0; - width: 100%; - color: var(--color-text); - background: var(--color-background-secondary); - border-bottom: 1px var(--color-accent) solid; - transition: transform 0.3s ease-in-out; - } - .tsd-page-toolbar a { - color: var(--color-text); - text-decoration: none; - } - .tsd-page-toolbar a.title { - font-weight: bold; - } - .tsd-page-toolbar a.title:hover { - text-decoration: underline; - } - .tsd-page-toolbar .tsd-toolbar-contents { - display: flex; - justify-content: space-between; - height: 2.5rem; - margin: 0 auto; - } - .tsd-page-toolbar .table-cell { - position: relative; - white-space: nowrap; - line-height: 40px; - } - .tsd-page-toolbar .table-cell:first-child { - width: 100%; - } - .tsd-page-toolbar .tsd-toolbar-icon { - box-sizing: border-box; - line-height: 0; - padding: 12px 0; - } - - .tsd-widget { - display: inline-block; - overflow: hidden; - opacity: 0.8; - height: 40px; - transition: - opacity 0.1s, - background-color 0.2s; - vertical-align: bottom; - cursor: pointer; - } - .tsd-widget:hover { - opacity: 0.9; - } - .tsd-widget.active { - opacity: 1; - background-color: var(--color-accent); - } - .tsd-widget.no-caption { - width: 40px; - } - .tsd-widget.no-caption:before { - margin: 0; - } - - .tsd-widget.options, - .tsd-widget.menu { - display: none; - } - input[type="checkbox"] + .tsd-widget:before { - background-position: -120px 0; - } - input[type="checkbox"]:checked + .tsd-widget:before { - background-position: -160px 0; - } - - img { - max-width: 100%; - } - - .tsd-member-summary-name { - display: inline-flex; - align-items: center; - padding: 0.25rem; - text-decoration: none; - } - - .tsd-anchor-icon { - display: inline-flex; - align-items: center; - margin-left: 0.5rem; - color: var(--color-text); - } - - .tsd-anchor-icon svg { - width: 1em; - height: 1em; - visibility: hidden; - } - - .tsd-member-summary-name:hover > .tsd-anchor-icon svg, - .tsd-anchor-link:hover > .tsd-anchor-icon svg { - visibility: visible; - } - - .deprecated { - text-decoration: line-through !important; - } - - .warning { - padding: 1rem; - color: var(--color-warning-text); - background: var(--color-background-warning); - } - - .tsd-kind-project { - color: var(--color-ts-project); - } - .tsd-kind-module { - color: var(--color-ts-module); - } - .tsd-kind-namespace { - color: var(--color-ts-namespace); - } - .tsd-kind-enum { - color: var(--color-ts-enum); - } - .tsd-kind-enum-member { - color: var(--color-ts-enum-member); - } - .tsd-kind-variable { - color: var(--color-ts-variable); - } - .tsd-kind-function { - color: var(--color-ts-function); - } - .tsd-kind-class { - color: var(--color-ts-class); - } - .tsd-kind-interface { - color: var(--color-ts-interface); - } - .tsd-kind-constructor { - color: var(--color-ts-constructor); - } - .tsd-kind-property { - color: var(--color-ts-property); - } - .tsd-kind-method { - color: var(--color-ts-method); - } - .tsd-kind-reference { - color: var(--color-ts-reference); - } - .tsd-kind-call-signature { - color: var(--color-ts-call-signature); - } - .tsd-kind-index-signature { - color: var(--color-ts-index-signature); - } - .tsd-kind-constructor-signature { - color: var(--color-ts-constructor-signature); - } - .tsd-kind-parameter { - color: var(--color-ts-parameter); - } - .tsd-kind-type-parameter { - color: var(--color-ts-type-parameter); - } - .tsd-kind-accessor { - color: var(--color-ts-accessor); - } - .tsd-kind-get-signature { - color: var(--color-ts-get-signature); - } - .tsd-kind-set-signature { - color: var(--color-ts-set-signature); - } - .tsd-kind-type-alias { - color: var(--color-ts-type-alias); - } - - /* if we have a kind icon, don't color the text by kind */ - .tsd-kind-icon ~ span { - color: var(--color-text); - } - - * { - scrollbar-width: thin; - scrollbar-color: var(--color-accent) var(--color-icon-background); - } - - *::-webkit-scrollbar { - width: 0.75rem; - } - - *::-webkit-scrollbar-track { - background: var(--color-icon-background); - } - - *::-webkit-scrollbar-thumb { - background-color: var(--color-accent); - border-radius: 999rem; - border: 0.25rem solid var(--color-icon-background); - } - - /* mobile */ - @media (max-width: 769px) { - .tsd-widget.options, - .tsd-widget.menu { - display: inline-block; - } - - .container-main { - display: flex; - } - html .col-content { - float: none; - max-width: 100%; - width: 100%; - } - html .col-sidebar { - position: fixed !important; - overflow-y: auto; - -webkit-overflow-scrolling: touch; - z-index: 1024; - top: 0 !important; - bottom: 0 !important; - left: auto !important; - right: 0 !important; - padding: 1.5rem 1.5rem 0 0; - width: 75vw; - visibility: hidden; - background-color: var(--color-background); - transform: translate(100%, 0); - } - html .col-sidebar > *:last-child { - padding-bottom: 20px; - } - html .overlay { - content: ""; - display: block; - position: fixed; - z-index: 1023; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.75); - visibility: hidden; - } - - .to-has-menu .overlay { - animation: fade-in 0.4s; - } - - .to-has-menu .col-sidebar { - animation: pop-in-from-right 0.4s; - } - - .from-has-menu .overlay { - animation: fade-out 0.4s; - } - - .from-has-menu .col-sidebar { - animation: pop-out-to-right 0.4s; - } - - .has-menu body { - overflow: hidden; - } - .has-menu .overlay { - visibility: visible; - } - .has-menu .col-sidebar { - visibility: visible; - transform: translate(0, 0); - display: flex; - flex-direction: column; - gap: 1.5rem; - max-height: 100vh; - padding: 1rem 2rem; - } - .has-menu .tsd-navigation { - max-height: 100%; - } - #tsd-toolbar-links { - display: none; - } - .tsd-navigation .tsd-nav-link { - display: flex; - } - } - - /* one sidebar */ - @media (min-width: 770px) { - .container-main { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); - grid-template-areas: "sidebar content"; - margin: 2rem auto; - } - - .col-sidebar { - grid-area: sidebar; - } - .col-content { - grid-area: content; - padding: 0 1rem; - } - } - @media (min-width: 770px) and (max-width: 1399px) { - .col-sidebar { - max-height: calc(100vh - 2rem - 42px); - overflow: auto; - position: sticky; - top: 42px; - padding-top: 1rem; - } - .site-menu { - margin-top: 1rem; - } - } - - /* two sidebars */ - @media (min-width: 1200px) { - .container-main { - grid-template-columns: minmax(0, 1fr) minmax(0, 2.5fr) minmax( - 0, - 20rem - ); - grid-template-areas: "sidebar content toc"; - } - - .col-sidebar { - display: contents; - } - - .page-menu { - grid-area: toc; - padding-left: 1rem; - } - .site-menu { - grid-area: sidebar; - } - - .site-menu { - margin-top: 1rem; - } - - .page-menu, - .site-menu { - max-height: calc(100vh - 2rem - 42px); - overflow: auto; - position: sticky; - top: 42px; - } - } -} diff --git a/docs/journey/functions/JourneyProvider.html b/docs/journey/functions/JourneyProvider.html deleted file mode 100644 index 42717c281..000000000 --- a/docs/journey/functions/JourneyProvider.html +++ /dev/null @@ -1,4 +0,0 @@ -JourneyProvider | @ping-identity/rn-journey
diff --git a/docs/journey/functions/buildNextInput.html b/docs/journey/functions/buildNextInput.html deleted file mode 100644 index 3153bd071..000000000 --- a/docs/journey/functions/buildNextInput.html +++ /dev/null @@ -1,5 +0,0 @@ -buildNextInput | @ping-identity/rn-journey
diff --git a/docs/journey/functions/createJourneyClient.html b/docs/journey/functions/createJourneyClient.html deleted file mode 100644 index 83bc23252..000000000 --- a/docs/journey/functions/createJourneyClient.html +++ /dev/null @@ -1,5 +0,0 @@ -createJourneyClient | @ping-identity/rn-journey
diff --git a/docs/journey/functions/normalizeCallbacks.html b/docs/journey/functions/normalizeCallbacks.html deleted file mode 100644 index e14341d17..000000000 --- a/docs/journey/functions/normalizeCallbacks.html +++ /dev/null @@ -1,4 +0,0 @@ -normalizeCallbacks | @ping-identity/rn-journey
diff --git a/docs/journey/functions/useJourney.html b/docs/journey/functions/useJourney.html deleted file mode 100644 index 41c7d2af3..000000000 --- a/docs/journey/functions/useJourney.html +++ /dev/null @@ -1,12 +0,0 @@ -useJourney | @ping-identity/rn-journey
  • React helper hook for Journey flows.

    -

    Parameters

    • client: JourneyClient

      Optional Journey client returned by createJourneyClient(...).

      -

    Returns JourneyHookResult

    Tuple containing current node state and Journey actions.

    -

    Provide a client directly for local screen state, or omit it when using -JourneyProvider to share one Journey state across multiple screens.

    -

    When no client is available via argument or provider.

    -
  • React helper hook for Journey flows.

    -

    Returns JourneyHookResult

    Tuple containing current node state and Journey actions.

    -

    Provide a client directly for local screen state, or omit it when using -JourneyProvider to share one Journey state across multiple screens.

    -

    When no client is available via argument or provider.

    -
diff --git a/docs/journey/functions/useJourneyForm.html b/docs/journey/functions/useJourneyForm.html deleted file mode 100644 index 0e57f1d36..000000000 --- a/docs/journey/functions/useJourneyForm.html +++ /dev/null @@ -1,6 +0,0 @@ -useJourneyForm | @ping-identity/rn-journey
  • Headless callback form helper for Journey nodes.

    -

    Parameters

    • node: undefined | null | JourneyNode

      Current Journey node from useJourney.

      -

    Returns JourneyFormResult

    Normalized fields, managed values, and submit planning helpers.

    -

    This hook does not render UI or auto-execute integration-specific callbacks. -It only manages callback form state and submit planning.

    -
diff --git a/docs/journey/index.html b/docs/journey/index.html deleted file mode 100644 index a8fcbabfb..000000000 --- a/docs/journey/index.html +++ /dev/null @@ -1,191 +0,0 @@ -@ping-identity/rn-journey

@ping-identity/rn-journey

-

Ping Identity

-

Ping Identity React Native Journey

This module exposes native-backed Journey clients for Android and iOS.

-

Add the package and let autolinking wire the native code:

-
yarn add @ping-identity/rn-journey
cd ios && pod install -
- -

Optional integration packages:

-
yarn add @react-native-pingidentity/storage
yarn add @react-native-pingidentity/logger -
- -
import { createJourneyClient } from '@ping-identity/rn-journey';
import {
CacheStrategy,
configureOidcStorage,
configureSessionStorage,
} from '@react-native-pingidentity/storage';

const sessionStorage = configureSessionStorage({
android: {
fileName: 'journey-session',
keyAlias: 'journey-session',
strongBoxPreferred: true,
cacheStrategy: 'cache_on_failure',
},
ios: {
account: 'com.example.app.session',
encryptor: true,
cacheable: false,
},
});

const oidcStorage = configureOidcStorage({
android: {
fileName: 'journey-oidc',
keyAlias: 'journey-oidc',
strongBoxPreferred: true,
cacheStrategy: CacheStrategy.CACHE_ON_FAILURE,
},
ios: {
account: 'com.example.app.oidc',
encryptor: true,
cacheable: false,
},
});

const client = createJourneyClient({
serverUrl: 'https://example.com/am',
realm: 'alpha',
cookie: 'iPlanetDirectoryPro',
timeout: 30000,
modules: {
oidc: {
clientId: 'rn-client',
discoveryEndpoint: 'https://example.com/am/oauth2/alpha/.well-known/openid-configuration',
redirectUri: 'com.example.app://callback',
scopes: ['openid', 'profile', 'email'],
storage: oidcStorage,
},
session: {
storage: sessionStorage,
},
},
}); -
- -

Pass module integrations through config.modules. The JS API is createJourneyClient(config). -Storage handles in modules.session.storage and modules.oidc.storage must come from -configureSessionStorage(...) / configureOidcStorage(...).

-

If you install the logger package, pass either a JS logger instance or a native logger handle. -Both logger and nativeLogger values must be created via @react-native-pingidentity/logger. -If the logger package is not installed/configured, do not pass logger values in Journey config.

-
import { createJourneyClient } from '@ping-identity/rn-journey';
import { logger, configureLogger } from '@react-native-pingidentity/logger';

const jsLogger = logger({ level: 'debug' });
const nativeLogger = configureLogger({ level: 'warn' });

const client = createJourneyClient({
serverUrl: 'https://example.com/am',
logger: jsLogger,
modules: {
oidc: {
clientId: 'rn-client',
discoveryEndpoint: 'https://example.com/am/oauth2/alpha/.well-known/openid-configuration',
redirectUri: 'com.example.app://callback',
scopes: ['openid'],
nativeLogger,
},
},
}); -
- -
-

TODO(iOS SDK parity): modules.oidc.signOutRedirectUri is currently ignored on iOS.

-
-
const firstNode = await client.start('Login');
const nextNode = await client.next({
callbacks: [
{ type: 'NameCallback', value: 'demo-user' },
{ type: 'PasswordCallback', value: 'demo-password' },
],
});
const resumedNode = await client.resume('com.example.app://callback?code=123');
const session = await client.user();
const refreshedSession = await client.refresh();
const userInfo = await client.userinfo();
const ssoToken = await client.ssoToken();
await client.revoke();
await client.logoutUser();
await client.dispose(); -
- -

useJourney auto-advances PollingWaitCallback nodes when no manual or blocking integration callbacks are present.

-

After a Journey login succeeds, use the following operations to inspect and manage the active user session:

-
const userSession = await client.user();
const ssoToken = await client.ssoToken();

if (userSession) {
const refreshedSession = await client.refresh();
const userInfo = await client.userinfo();
await client.revoke();
}

await client.logoutUser(); -
- -
    -
  • user() returns token/session payload (accessToken, optional refreshToken, expiresIn, optional userInfo).
  • -
  • ssoToken() returns Journey SSO session payload (value, successUrl, realm) when available.
  • -
  • refresh() refreshes token payload for the active user.
  • -
  • userinfo() fetches user claims for the active user.
  • -
  • revoke() revokes access/refresh tokens for the active user.
  • -
  • logoutUser() signs out and clears the active Journey user session.
  • -
-
import { useJourney } from '@ping-identity/rn-journey';

const [node, actions] = useJourney(client);

await actions.start('Login');

if (node?.type === 'ContinueNode') {
await actions.next({
callbacks: [{ type: 'NameCallback', value: 'demo-user' }],
});
} -
- -
import { JourneyProvider, useJourney } from '@ping-identity/rn-journey';

function App(): React.ReactElement {
return (
<JourneyProvider client={client}>
<AuthNavigator />
</JourneyProvider>
);
}

function LoginScreen(): React.ReactElement {
const [node, actions] = useJourney();
return <></>;
} -
- -
import { callbackType } from '@ping-identity/rn-types';
import { useJourney, useJourneyForm } from '@ping-identity/rn-journey';

const [node, actions] = useJourney(client);
const form = useJourneyForm(node);

const usernameField = form.getFieldByType(callbackType.NameCallback);
const passwordField = form.getFieldByType(callbackType.PasswordCallback);

form.setValueByType(callbackType.NameCallback, 'demo-user');
form.setValueByType(callbackType.PasswordCallback, 'demo-password');

if (form.canSubmit) {
await actions.next(form.input);
} -
- -

useJourneyForm is headless. It manages normalized fields and submit planning, but does not render UI and does not auto-run integration-required callbacks.

-
-

TODO(test-runner app): add Journey integration and E2E tests (including SuspendedTextOutputCallback deep link/email resume flow) once the test-runner app is set up.

-
-

The following AM core callbacks are supported on Android and iOS:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Callback TypeInput Handling
BooleanAttributeInputCallbackManual input
ChoiceCallbackManual input
ConfirmationCallbackManual input
ConsentMappingCallbackManual input
HiddenValueCallbackManual input
KbaCreateCallbackManual input
MetadataCallbackOutput-only
NameCallbackManual input
NumberAttributeInputCallbackManual input
PasswordCallbackManual input
PollingWaitCallbackOutput-only
StringAttributeInputCallbackManual input
SuspendedTextOutputCallbackOutput-only
TermsAndConditionsCallbackManual input
TextInputCallbackManual input
TextOutputCallbackOutput-only
ValidatedCreatePasswordCallbackManual input
ValidatedCreateUsernameCallbackManual input
-

Integration-dependent families (for example, device profile, FIDO/FIDO2, PingOne Protect, redirect/IdP, and ReCaptcha callbacks) are surfaced in node payloads and require client-side integration before submission.

-

If Journey uses OIDC redirects, configure the app redirect scheme. For a redirect URI of -com.example.app://callback, add the manifest placeholder:

-
android {
-  defaultConfig {
-    manifestPlaceholders["appRedirectUriScheme"] = "com.example.app"
-  }
-}
-
- -

Add the redirect intent filter to CustomTabActivity:

-

-    
-        
-
-        
-        
-
-        
-    
-
-
- -

All promise rejections use the shared GenericError contract from @ping-identity/rn-types.

-
import type { JourneyError } from '@ping-identity/rn-journey';

try {
await client.start('Login');
} catch (error) {
const journeyError = error as JourneyError;
console.log(journeyError.type, journeyError.error, journeyError.message);
} -
- -

Stable Journey error codes:

-
    -
  • JOURNEY_CONFIG_ERROR
  • -
  • JOURNEY_INIT_ERROR
  • -
  • JOURNEY_START_ERROR
  • -
  • JOURNEY_NEXT_ERROR
  • -
  • JOURNEY_RESUME_ERROR
  • -
  • JOURNEY_USER_ERROR
  • -
  • JOURNEY_LOGOUT_ERROR
  • -
  • JOURNEY_DISPOSE_ERROR
  • -
  • JOURNEY_STATE_ERROR
  • -
  • JOURNEY_CALLBACK_APPLY_ERROR
  • -
  • JOURNEY_UNSUPPORTED_CALLBACK_ERROR
  • -
  • JOURNEY_MISSING_INTEGRATION_ERROR
  • -
-
diff --git a/docs/journey/modules.html b/docs/journey/modules.html deleted file mode 100644 index 12541b0f1..000000000 --- a/docs/journey/modules.html +++ /dev/null @@ -1 +0,0 @@ -@ping-identity/rn-journey
diff --git a/docs/journey/types/JourneyBuildNextInputResult.html b/docs/journey/types/JourneyBuildNextInputResult.html deleted file mode 100644 index 8faaa744d..000000000 --- a/docs/journey/types/JourneyBuildNextInputResult.html +++ /dev/null @@ -1,5 +0,0 @@ -JourneyBuildNextInputResult | @ping-identity/rn-journey

Type Alias JourneyBuildNextInputResult

JourneyBuildNextInputResult: {
    canSubmit: boolean;
    input: JourneyNextInput;
    issues: JourneySubmitIssue[];
}

Result shape returned by callback submit builder helper.

-

Type declaration

  • canSubmit: boolean

    Indicates whether input can be safely submitted to next().

    -
  • input: JourneyNextInput

    Callback payload built from normalized fields and form values.

    -
  • issues: JourneySubmitIssue[]

    Blocking/non-blocking issues found while building payload.

    -
diff --git a/docs/journey/types/JourneyCallback.html b/docs/journey/types/JourneyCallback.html deleted file mode 100644 index 1ab65337f..000000000 --- a/docs/journey/types/JourneyCallback.html +++ /dev/null @@ -1,9 +0,0 @@ -JourneyCallback | @ping-identity/rn-journey
JourneyCallback: Omit<NodeCallback, "type"> & {
    message?: string;
    prompt?: string;
    type: JourneyCallbackType;
    value?: unknown;
    [key: string]: unknown;
}

Native callback payload surfaced to JavaScript.

-

Type declaration

  • [key: string]: unknown

    Optional callback metadata emitted by native.

    -
  • Optionalmessage?: string

    Optional user-facing message emitted by native.

    -
  • Optionalprompt?: string

    Optional user-facing prompt string emitted by native.

    -
  • type: JourneyCallbackType

    Native callback type (for example, NameCallback).

    -
  • Optionalvalue?: unknown

    Optional callback value emitted by native.

    -

Extends shared callback shape while widening type so native-extension callbacks -are represented without type assertions.

-
diff --git a/docs/journey/types/JourneyCallbackInput.html b/docs/journey/types/JourneyCallbackInput.html deleted file mode 100644 index f417d0814..000000000 --- a/docs/journey/types/JourneyCallbackInput.html +++ /dev/null @@ -1,5 +0,0 @@ -JourneyCallbackInput | @ping-identity/rn-journey

Type Alias JourneyCallbackInput

JourneyCallbackInput: {
    index?: number;
    type: JourneyCallbackType;
    value?: JourneyCallbackInputValue;
}

Callback mutation entry submitted to next().

-

Type declaration

diff --git a/docs/journey/types/JourneyCallbackInputValue.html b/docs/journey/types/JourneyCallbackInputValue.html deleted file mode 100644 index 6c8674693..000000000 --- a/docs/journey/types/JourneyCallbackInputValue.html +++ /dev/null @@ -1,2 +0,0 @@ -JourneyCallbackInputValue | @ping-identity/rn-journey

Type Alias JourneyCallbackInputValue

JourneyCallbackInputValue:
    | string
    | number
    | boolean
    | null
    | Record<string, unknown>
    | unknown[]

Callback input value submitted to next().

-
diff --git a/docs/journey/types/JourneyCallbackType.html b/docs/journey/types/JourneyCallbackType.html deleted file mode 100644 index 125a5e048..000000000 --- a/docs/journey/types/JourneyCallbackType.html +++ /dev/null @@ -1,4 +0,0 @@ -JourneyCallbackType | @ping-identity/rn-journey

Type Alias JourneyCallbackType

JourneyCallbackType: CallbackType | string & {}

Callback type union used by Journey types.

-

Includes known SDK callback literals while allowing native-extension callback -strings that are not yet part of shared SDK types.

-
diff --git a/docs/journey/types/JourneyClient.html b/docs/journey/types/JourneyClient.html deleted file mode 100644 index 5d96a19e6..000000000 --- a/docs/journey/types/JourneyClient.html +++ /dev/null @@ -1,26 +0,0 @@ -JourneyClient | @ping-identity/rn-journey
JourneyClient: {
    dispose: () => Promise<void>;
    getId: () => Promise<string>;
    init: () => Promise<string>;
    logoutUser: () => Promise<boolean>;
    next: (input?: JourneyNextInput) => Promise<JourneyNode>;
    refresh: () => Promise<JourneyUserSession | null>;
    resume: (uri: string) => Promise<JourneyNode>;
    revoke: () => Promise<boolean>;
    ssoToken: () => Promise<JourneySSOToken | null>;
    start: (
        journeyName: string,
        options?: JourneyStartOptions,
    ) => Promise<JourneyNode>;
    user: () => Promise<JourneyUserSession | null>;
    userinfo: () => Promise<JourneyUserInfo | null>;
}

Native-backed Journey client contract.

-

Type declaration

  • dispose: () => Promise<void>

    Dispose the native Journey instance and release runtime state.

    -

    When disposal fails.

    -
  • getId: () => Promise<string>

    Returns the native Journey instance identifier.

    -

    When the Journey is not configured.

    -
  • init: () => Promise<string>

    Explicitly initialize the native Journey instance.

    -

    When the Journey cannot be configured.

    -
  • logoutUser: () => Promise<boolean>

    Logout active Journey user/session.

    -

    When logout fails.

    -
  • next: (input?: JourneyNextInput) => Promise<JourneyNode>

    Advance an active Journey by applying callback input.

    -

    When callback application or progression fails.

    -
  • refresh: () => Promise<JourneyUserSession | null>

    Refresh active Journey user access token.

    -

    When refresh fails.

    -
  • resume: (uri: string) => Promise<JourneyNode>

    Resume a suspended Journey flow from a deep link URL.

    -

    When resume fails.

    -
  • revoke: () => Promise<boolean>

    Revoke active Journey user access/refresh tokens.

    -

    When revoke fails.

    -
  • ssoToken: () => Promise<JourneySSOToken | null>

    Resolve active Journey SSO token payload.

    -

    When SSO token retrieval fails.

    -
  • start: (journeyName: string, options?: JourneyStartOptions) => Promise<JourneyNode>

    Start a Journey by name.

    -

    When start fails.

    -
  • user: () => Promise<JourneyUserSession | null>

    Resolve active user session details.

    -

    When session retrieval fails.

    -
  • userinfo: () => Promise<JourneyUserInfo | null>

    Resolve active Journey userinfo claims.

    -

    When userinfo retrieval fails.

    -
diff --git a/docs/journey/types/JourneyConfig.html b/docs/journey/types/JourneyConfig.html deleted file mode 100644 index cd7d33e16..000000000 --- a/docs/journey/types/JourneyConfig.html +++ /dev/null @@ -1,9 +0,0 @@ -JourneyConfig | @ping-identity/rn-journey
JourneyConfig: {
    cookie?: string;
    logger?: LoggerInstance;
    modules?: JourneyModules;
    realm?: string;
    serverUrl: string;
    timeout?: number;
}

Journey client configuration.

-

Type declaration

  • Optionalcookie?: string

    Optional Journey cookie name override.

    -
  • Optionallogger?: LoggerInstance

    Optional Journey logger used by the native workflow.

    -

    Must be created by @react-native-pingidentity/logger (logger(...)).

    -
  • Optionalmodules?: JourneyModules

    Optional Journey module integrations.

    -
  • Optionalrealm?: string

    Optional Journey realm.

    -
  • serverUrl: string

    Base AM/Ping server URL.

    -
  • Optionaltimeout?: number

    Optional network timeout in milliseconds.

    -
diff --git a/docs/journey/types/JourneyError.html b/docs/journey/types/JourneyError.html deleted file mode 100644 index 08b5b37d9..000000000 --- a/docs/journey/types/JourneyError.html +++ /dev/null @@ -1,4 +0,0 @@ -JourneyError | @ping-identity/rn-journey
JourneyError: GenericError

Journey error payload contract.

-

Mirrors the shared GenericError contract to keep error semantics aligned -across all native-backed React Native modules.

-
diff --git a/docs/journey/types/JourneyErrorCode.html b/docs/journey/types/JourneyErrorCode.html deleted file mode 100644 index e3214bf2f..000000000 --- a/docs/journey/types/JourneyErrorCode.html +++ /dev/null @@ -1,2 +0,0 @@ -JourneyErrorCode | @ping-identity/rn-journey
JourneyErrorCode:
    | "JOURNEY_CONFIG_ERROR"
    | "JOURNEY_INIT_ERROR"
    | "JOURNEY_START_ERROR"
    | "JOURNEY_NEXT_ERROR"
    | "JOURNEY_RESUME_ERROR"
    | "JOURNEY_USER_ERROR"
    | "JOURNEY_LOGOUT_ERROR"
    | "JOURNEY_DISPOSE_ERROR"
    | "JOURNEY_STATE_ERROR"
    | "JOURNEY_CALLBACK_APPLY_ERROR"
    | "JOURNEY_UNSUPPORTED_CALLBACK_ERROR"
    | "JOURNEY_MISSING_INTEGRATION_ERROR"

Stable Journey error codes surfaced by native and JS guardrails.

-
diff --git a/docs/journey/types/JourneyFieldCapability.html b/docs/journey/types/JourneyFieldCapability.html deleted file mode 100644 index 402f6209f..000000000 --- a/docs/journey/types/JourneyFieldCapability.html +++ /dev/null @@ -1,2 +0,0 @@ -JourneyFieldCapability | @ping-identity/rn-journey

Type Alias JourneyFieldCapability

JourneyFieldCapability:
    | "manual"
    | "output_only"
    | "integration_required"
    | "unsupported"

Callback execution capability classification for normalized fields.

-
diff --git a/docs/journey/types/JourneyFieldKind.html b/docs/journey/types/JourneyFieldKind.html deleted file mode 100644 index 8fd4127a3..000000000 --- a/docs/journey/types/JourneyFieldKind.html +++ /dev/null @@ -1,2 +0,0 @@ -JourneyFieldKind | @ping-identity/rn-journey
JourneyFieldKind:
    | "text"
    | "password"
    | "number"
    | "boolean"
    | "choice"
    | "kba"
    | "output"
    | "unknown"

High-level field kind used by headless callback helpers.

-
diff --git a/docs/journey/types/JourneyFieldOption.html b/docs/journey/types/JourneyFieldOption.html deleted file mode 100644 index ecda9fbd2..000000000 --- a/docs/journey/types/JourneyFieldOption.html +++ /dev/null @@ -1,6 +0,0 @@ -JourneyFieldOption | @ping-identity/rn-journey

Type Alias JourneyFieldOption

JourneyFieldOption: { index: number; label: string; value: unknown }

Choice-style option surfaced by normalized callback helpers.

-

Type declaration

  • index: number

    Zero-based option index.

    -
  • label: string

    Display label provided by callback payload.

    -

    This value may be empty when callback data omits an explicit label.

    -
  • value: unknown

    Original option payload from native callback data.

    -
diff --git a/docs/journey/types/JourneyFieldRef.html b/docs/journey/types/JourneyFieldRef.html deleted file mode 100644 index d73b8dd58..000000000 --- a/docs/journey/types/JourneyFieldRef.html +++ /dev/null @@ -1,4 +0,0 @@ -JourneyFieldRef | @ping-identity/rn-journey
JourneyFieldRef: { type: JourneyCallbackType; typeIndex: number }

Strongly typed callback reference for one callback instance in a node.

-

Type declaration

diff --git a/docs/journey/types/JourneyFormMeta.html b/docs/journey/types/JourneyFormMeta.html deleted file mode 100644 index 08f31cc22..000000000 --- a/docs/journey/types/JourneyFormMeta.html +++ /dev/null @@ -1,7 +0,0 @@ -JourneyFormMeta | @ping-identity/rn-journey
JourneyFormMeta: {
    hasIntegrationRequired: boolean;
    hasManual: boolean;
    hasOutputOnly: boolean;
    hasRequiredConsentMissing: boolean;
    hasUnsupported: boolean;
}

Derived metadata returned by useJourneyForm.

-

Type declaration

  • hasIntegrationRequired: boolean

    True when at least one callback requires extra module integration.

    -
  • hasManual: boolean

    True when at least one callback requires manual value submission.

    -
  • hasOutputOnly: boolean

    True when at least one callback is output-only.

    -
  • hasRequiredConsentMissing: boolean

    True when helper planner reports missing required consent callbacks.

    -
  • hasUnsupported: boolean

    True when at least one callback type is currently unsupported by helper logic.

    -
diff --git a/docs/journey/types/JourneyFormResult.html b/docs/journey/types/JourneyFormResult.html deleted file mode 100644 index fb1cf1ae7..000000000 --- a/docs/journey/types/JourneyFormResult.html +++ /dev/null @@ -1,17 +0,0 @@ -JourneyFormResult | @ping-identity/rn-journey

Type Alias JourneyFormResult

JourneyFormResult: {
    buildInput: (
        overrides?: Partial<JourneyFormValues>,
    ) => JourneyBuildNextInputResult;
    canSubmit: boolean;
    clearValue: (fieldId: string) => void;
    fields: JourneyNormalizedField[];
    getField: (fieldId: string) => JourneyNormalizedField | undefined;
    getFieldByType: (
        callbackType: JourneyCallbackType,
        typeIndex?: number,
    ) => JourneyNormalizedField | undefined;
    getFieldsByType: (
        callbackType: JourneyCallbackType,
    ) => JourneyNormalizedField[];
    input: JourneyNextInput;
    issues: JourneySubmitIssue[];
    meta: JourneyFormMeta;
    reset: (nextValues?: JourneyFormValues) => void;
    setValue: (fieldId: string, value: JourneyFormValue) => void;
    setValueByType: (
        callbackType: JourneyCallbackType,
        value: JourneyFormValue,
        typeIndex?: number,
    ) => boolean;
    setValues: (updater: JourneyFormValuesUpdater) => void;
    values: JourneyFormValues;
}

Return contract for useJourneyForm.

-

Type declaration

diff --git a/docs/journey/types/JourneyFormValue.html b/docs/journey/types/JourneyFormValue.html deleted file mode 100644 index 843f6f469..000000000 --- a/docs/journey/types/JourneyFormValue.html +++ /dev/null @@ -1,2 +0,0 @@ -JourneyFormValue | @ping-identity/rn-journey
JourneyFormValue:
    | string
    | number
    | boolean
    | null
    | {
        allowUserDefinedQuestions: boolean;
        selectedAnswer: string;
        selectedQuestion: string;
    }

Supported form value shape for normalized callback fields.

-
diff --git a/docs/journey/types/JourneyFormValues.html b/docs/journey/types/JourneyFormValues.html deleted file mode 100644 index 9a6f6727f..000000000 --- a/docs/journey/types/JourneyFormValues.html +++ /dev/null @@ -1,2 +0,0 @@ -JourneyFormValues | @ping-identity/rn-journey

Type Alias JourneyFormValues

JourneyFormValues: Record<string, JourneyFormValue | undefined>

Form value map keyed by normalized callback field id.

-
diff --git a/docs/journey/types/JourneyFormValuesUpdater.html b/docs/journey/types/JourneyFormValuesUpdater.html deleted file mode 100644 index 69792cb74..000000000 --- a/docs/journey/types/JourneyFormValuesUpdater.html +++ /dev/null @@ -1,2 +0,0 @@ -JourneyFormValuesUpdater | @ping-identity/rn-journey

Type Alias JourneyFormValuesUpdater

JourneyFormValuesUpdater:
    | Partial<JourneyFormValues>
    | (previous: JourneyFormValues) => Partial<JourneyFormValues>

Updater argument accepted by setValues from useJourneyForm.

-
diff --git a/docs/journey/types/JourneyHookActions.html b/docs/journey/types/JourneyHookActions.html deleted file mode 100644 index ef8e59ebd..000000000 --- a/docs/journey/types/JourneyHookActions.html +++ /dev/null @@ -1,24 +0,0 @@ -JourneyHookActions | @ping-identity/rn-journey

Type Alias JourneyHookActions

JourneyHookActions: {
    dispose: () => Promise<void>;
    error: JourneyError | null;
    loading: boolean;
    logoutUser: () => Promise<boolean>;
    next: (input?: JourneyNextInput) => Promise<JourneyNode>;
    refresh: () => Promise<JourneyUserSession | null>;
    resume: (uri: string) => Promise<JourneyNode>;
    revoke: () => Promise<boolean>;
    ssoToken: () => Promise<JourneySSOToken | null>;
    start: (
        journeyName: string,
        options?: JourneyStartOptions,
    ) => Promise<JourneyNode>;
    user: () => Promise<JourneyUserSession | null>;
    userinfo: () => Promise<JourneyUserInfo | null>;
}

Actions exposed by useJourney.

-

Type declaration

  • dispose: () => Promise<void>

    Dispose native Journey resources.

    -

    When disposal fails.

    -
  • error: JourneyError | null

    Last hook-level error, if any.

    -
  • loading: boolean

    Indicates whether a Journey action is in progress.

    -
  • logoutUser: () => Promise<boolean>

    Logout the active Journey user/session.

    -

    When logout fails.

    -
  • next: (input?: JourneyNextInput) => Promise<JourneyNode>

    Advance the active Journey node.

    -

    When no node exists or progression fails.

    -
  • refresh: () => Promise<JourneyUserSession | null>

    Refresh active Journey user access token.

    -

    When refresh fails.

    -
  • resume: (uri: string) => Promise<JourneyNode>

    Resume a suspended Journey using a resume URI.

    -

    When resume fails.

    -
  • revoke: () => Promise<boolean>

    Revoke active Journey user access/refresh tokens.

    -

    When revoke fails.

    -
  • ssoToken: () => Promise<JourneySSOToken | null>

    Resolve active Journey SSO token payload.

    -

    When SSO token retrieval fails.

    -
  • start: (journeyName: string, options?: JourneyStartOptions) => Promise<JourneyNode>

    Start a Journey by name.

    -

    When start fails.

    -
  • user: () => Promise<JourneyUserSession | null>

    Resolve the active Journey user session.

    -

    When session retrieval fails.

    -
  • userinfo: () => Promise<JourneyUserInfo | null>

    Resolve active Journey userinfo payload.

    -

    When userinfo retrieval fails.

    -
diff --git a/docs/journey/types/JourneyHookResult.html b/docs/journey/types/JourneyHookResult.html deleted file mode 100644 index 7060ca0a2..000000000 --- a/docs/journey/types/JourneyHookResult.html +++ /dev/null @@ -1,2 +0,0 @@ -JourneyHookResult | @ping-identity/rn-journey

Type Alias JourneyHookResult

JourneyHookResult: readonly [JourneyNode | null, JourneyHookActions]

Tuple result returned by useJourney.

-
diff --git a/docs/journey/types/JourneyModules.html b/docs/journey/types/JourneyModules.html deleted file mode 100644 index ef89bfe50..000000000 --- a/docs/journey/types/JourneyModules.html +++ /dev/null @@ -1,6 +0,0 @@ -JourneyModules | @ping-identity/rn-journey
JourneyModules: {
    oidc?: JourneyOidcModuleConfig;
    session?: { storage?: SessionStorageHandle };
}

Optional Journey module integrations.

-

Type declaration

  • Optionaloidc?: JourneyOidcModuleConfig

    OIDC module composition.

    -
  • Optionalsession?: { storage?: SessionStorageHandle }

    Session module configuration.

    -
    • Optionalstorage?: SessionStorageHandle

      Session storage handle created by the storage module.

      -

      Applied natively on both Android and iOS.

      -
diff --git a/docs/journey/types/JourneyNextInput.html b/docs/journey/types/JourneyNextInput.html deleted file mode 100644 index d7c0cbbdd..000000000 --- a/docs/journey/types/JourneyNextInput.html +++ /dev/null @@ -1,3 +0,0 @@ -JourneyNextInput | @ping-identity/rn-journey
JourneyNextInput: { callbacks?: JourneyCallbackInput[] }

Payload for advancing a Journey node.

-

Type declaration

  • Optionalcallbacks?: JourneyCallbackInput[]

    Callback mutations to apply before calling native next().

    -
diff --git a/docs/journey/types/JourneyNode.html b/docs/journey/types/JourneyNode.html deleted file mode 100644 index 17baabb62..000000000 --- a/docs/journey/types/JourneyNode.html +++ /dev/null @@ -1,8 +0,0 @@ -JourneyNode | @ping-identity/rn-journey
JourneyNode: Omit<Node, "callbacks"> & {
    callbacks?: JourneyCallback[];
    cause?: string;
    input?: Record<string, unknown>;
    message?: string;
    type?: JourneyNodeType;
}

Journey node payload returned by native execution.

-

Type declaration

  • Optionalcallbacks?: JourneyCallback[]

    Callback collection when additional user input is required.

    -
  • Optionalcause?: string

    Optional failure cause message for FailureNode.

    -
  • Optionalinput?: Record<string, unknown>

    Optional raw input payload from native node.

    -
  • Optionalmessage?: string

    Optional node-level message from native/server.

    -
  • Optionaltype?: JourneyNodeType

    Terminal/non-terminal node discriminator.

    -

Extends shared node shape while replacing callbacks with Journey-native callback payloads.

-
diff --git a/docs/journey/types/JourneyNodeType.html b/docs/journey/types/JourneyNodeType.html deleted file mode 100644 index ae8b4abf3..000000000 --- a/docs/journey/types/JourneyNodeType.html +++ /dev/null @@ -1,2 +0,0 @@ -JourneyNodeType | @ping-identity/rn-journey
JourneyNodeType: "ContinueNode" | "ErrorNode" | "FailureNode" | "SuccessNode"

Node discriminator returned by the Journey bridge.

-
diff --git a/docs/journey/types/JourneyNormalizedField.html b/docs/journey/types/JourneyNormalizedField.html deleted file mode 100644 index 5a3185344..000000000 --- a/docs/journey/types/JourneyNormalizedField.html +++ /dev/null @@ -1,14 +0,0 @@ -JourneyNormalizedField | @ping-identity/rn-journey

Type Alias JourneyNormalizedField

JourneyNormalizedField: {
    capability: JourneyFieldCapability;
    defaultValue?: JourneyFormValue;
    id: string;
    kind: JourneyFieldKind;
    message?: string;
    options?: JourneyFieldOption[];
    prompt: string;
    raw: JourneyCallback;
    ref: JourneyFieldRef;
    required: boolean;
}

Normalized callback field shape used by headless callback helpers.

-

Type declaration

  • capability: JourneyFieldCapability

    Capability classification for submit behavior.

    -
  • OptionaldefaultValue?: JourneyFormValue

    Optional default value derived from callback payload.

    -
  • id: string

    Stable internal field key.

    -

    Treat this as an opaque identifier and do not rely on its string format.

    -
  • kind: JourneyFieldKind

    UI-oriented field kind.

    -
  • Optionalmessage?: string

    Optional callback message value.

    -
  • Optionaloptions?: JourneyFieldOption[]

    Option collection for choice/confirmation callbacks.

    -
  • prompt: string

    Prompt text provided by the native callback payload.

    -

    This value may be empty when the callback does not provide a prompt.

    -
  • raw: JourneyCallback

    Original native callback payload.

    -
  • ref: JourneyFieldRef

    Typed callback reference for this field.

    -
  • required: boolean

    Indicates whether this callback is marked as required by callback payload.

    -
diff --git a/docs/journey/types/JourneyOidcModuleConfig.html b/docs/journey/types/JourneyOidcModuleConfig.html deleted file mode 100644 index d28a6de4d..000000000 --- a/docs/journey/types/JourneyOidcModuleConfig.html +++ /dev/null @@ -1,8 +0,0 @@ -JourneyOidcModuleConfig | @ping-identity/rn-journey

Type Alias JourneyOidcModuleConfig

JourneyOidcModuleConfig: OidcCoreConfig & {
    logger?: LoggerInstance;
    nativeLogger?: NativeLoggerHandle;
    storage?: OidcStorageHandle;
}

Journey OIDC module configuration.

-

Type declaration

  • Optionallogger?: LoggerInstance

    Optional JavaScript logger instance.

    -

    Must be created by @react-native-pingidentity/logger (logger(...)).

    -
  • OptionalnativeLogger?: NativeLoggerHandle

    Optional native logger handle.

    -

    Must be created by @react-native-pingidentity/logger (configureLogger(...)).

    -
  • Optionalstorage?: OidcStorageHandle

    Optional OIDC token storage handle created by the storage module.

    -

Extends the shared OIDC core shape with Journey-supported RN handles.

-
diff --git a/docs/journey/types/JourneyOidcOpenIdConfiguration.html b/docs/journey/types/JourneyOidcOpenIdConfiguration.html deleted file mode 100644 index 427be7957..000000000 --- a/docs/journey/types/JourneyOidcOpenIdConfiguration.html +++ /dev/null @@ -1,2 +0,0 @@ -JourneyOidcOpenIdConfiguration | @ping-identity/rn-journey

Type Alias JourneyOidcOpenIdConfiguration

JourneyOidcOpenIdConfiguration: OidcOpenIdConfiguration

Optional OpenID endpoint override for Journey OIDC composition.

-
diff --git a/docs/journey/types/JourneyProviderProps.html b/docs/journey/types/JourneyProviderProps.html deleted file mode 100644 index 51009d588..000000000 --- a/docs/journey/types/JourneyProviderProps.html +++ /dev/null @@ -1,4 +0,0 @@ -JourneyProviderProps | @ping-identity/rn-journey

Type Alias JourneyProviderProps

JourneyProviderProps: { children: React.ReactNode; client: JourneyClient }

Props for JourneyProvider.

-

Type declaration

  • children: React.ReactNode

    Descendant React nodes.

    -
  • client: JourneyClient

    Journey client shared across a screen tree.

    -
diff --git a/docs/journey/types/JourneySSOToken.html b/docs/journey/types/JourneySSOToken.html deleted file mode 100644 index a131b2853..000000000 --- a/docs/journey/types/JourneySSOToken.html +++ /dev/null @@ -1,5 +0,0 @@ -JourneySSOToken | @ping-identity/rn-journey
JourneySSOToken: { realm: string; successUrl: string; value: string }

SSO token payload resolved from Journey session storage.

-

Type declaration

  • realm: string

    Realm associated with the SSO token.

    -
  • successUrl: string

    Success URL associated with the SSO token.

    -
  • value: string

    SSO token value.

    -
diff --git a/docs/journey/types/JourneyStartOptions.html b/docs/journey/types/JourneyStartOptions.html deleted file mode 100644 index 69e7b1e2b..000000000 --- a/docs/journey/types/JourneyStartOptions.html +++ /dev/null @@ -1,4 +0,0 @@ -JourneyStartOptions | @ping-identity/rn-journey

Type Alias JourneyStartOptions

JourneyStartOptions: { forceAuth?: boolean; noSession?: boolean }

Optional flags when starting a Journey.

-

Type declaration

  • OptionalforceAuth?: boolean

    Force authentication even if an SSO session exists.

    -
  • OptionalnoSession?: boolean

    Ignore existing session state and start a fresh flow.

    -
diff --git a/docs/journey/types/JourneySubmitIssue.html b/docs/journey/types/JourneySubmitIssue.html deleted file mode 100644 index 92dede7fb..000000000 --- a/docs/journey/types/JourneySubmitIssue.html +++ /dev/null @@ -1,6 +0,0 @@ -JourneySubmitIssue | @ping-identity/rn-journey

Type Alias JourneySubmitIssue

JourneySubmitIssue: {
    callbackType?: JourneyCallbackType;
    code: JourneySubmitIssueCode;
    fieldId?: string;
    message: string;
}

Helper issue record for callback submit planning.

-

Type declaration

  • OptionalcallbackType?: JourneyCallbackType

    Optional callback type associated with the issue.

    -
  • code: JourneySubmitIssueCode

    Stable issue code.

    -
  • OptionalfieldId?: string

    Optional normalized field id associated with the issue.

    -
  • message: string

    Human-readable issue message for UI display.

    -
diff --git a/docs/journey/types/JourneySubmitIssueCode.html b/docs/journey/types/JourneySubmitIssueCode.html deleted file mode 100644 index 7e5e9e053..000000000 --- a/docs/journey/types/JourneySubmitIssueCode.html +++ /dev/null @@ -1,2 +0,0 @@ -JourneySubmitIssueCode | @ping-identity/rn-journey

Type Alias JourneySubmitIssueCode

JourneySubmitIssueCode:
    | "NO_ACTIVE_CONTINUE_NODE"
    | "INTEGRATION_REQUIRED"
    | "UNSUPPORTED_CALLBACK"
    | "REQUIRED_CONSENT_MISSING"
    | "INVALID_VALUE"

Stable issue code surfaced by callback submit helper.

-
diff --git a/docs/journey/types/JourneyUserInfo.html b/docs/journey/types/JourneyUserInfo.html deleted file mode 100644 index bac77daa6..000000000 --- a/docs/journey/types/JourneyUserInfo.html +++ /dev/null @@ -1,3 +0,0 @@ -JourneyUserInfo | @ping-identity/rn-journey
JourneyUserInfo: Record<string, unknown>

User profile claims resolved from OIDC userinfo.

-

Keys and value shapes are deployment-specific and controlled by AM/Ping configuration.

-
diff --git a/docs/journey/types/JourneyUserSession.html b/docs/journey/types/JourneyUserSession.html deleted file mode 100644 index 7ca5def73..000000000 --- a/docs/journey/types/JourneyUserSession.html +++ /dev/null @@ -1,4 +0,0 @@ -JourneyUserSession | @ping-identity/rn-journey

Type Alias JourneyUserSession

JourneyUserSession: Pick<Tokens, "accessToken" | "refreshToken"> & {
    expiresIn?: number;
    userInfo?: JourneyUserInfo;
}

Session payload exposed by user().

-

Type declaration

  • OptionalexpiresIn?: number

    Optional token expiry in seconds.

    -
  • OptionaluserInfo?: JourneyUserInfo

    Optional user profile claims.

    -
diff --git a/docs/oidc/.nojekyll b/docs/oidc/.nojekyll deleted file mode 100644 index e2ac6616a..000000000 --- a/docs/oidc/.nojekyll +++ /dev/null @@ -1 +0,0 @@ -TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. \ No newline at end of file diff --git a/docs/oidc/assets/hierarchy.js b/docs/oidc/assets/hierarchy.js deleted file mode 100644 index 88636f05d..000000000 --- a/docs/oidc/assets/hierarchy.js +++ /dev/null @@ -1 +0,0 @@ -window.hierarchyData = "eJyrVirKzy8pVrKKjtVRKkpNy0lNLsnMzwMKVNfWAgCbHgqm" \ No newline at end of file diff --git a/docs/oidc/assets/highlight.css b/docs/oidc/assets/highlight.css deleted file mode 100644 index 16e72aa8f..000000000 --- a/docs/oidc/assets/highlight.css +++ /dev/null @@ -1,99 +0,0 @@ -:root { - --light-hl-0: #795E26; - --dark-hl-0: #DCDCAA; - --light-hl-1: #000000; - --dark-hl-1: #D4D4D4; - --light-hl-2: #A31515; - --dark-hl-2: #CE9178; - --light-hl-3: #AF00DB; - --dark-hl-3: #C586C0; - --light-hl-4: #001080; - --dark-hl-4: #9CDCFE; - --light-hl-5: #0000FF; - --dark-hl-5: #569CD6; - --light-hl-6: #0070C1; - --dark-hl-6: #4FC1FF; - --light-hl-7: #008000; - --dark-hl-7: #6A9955; - --light-hl-8: #267F99; - --dark-hl-8: #4EC9B0; - --light-hl-9: #800000; - --dark-hl-9: #808080; - --light-hl-10: #E50000; - --dark-hl-10: #9CDCFE; - --light-code-background: #FFFFFF; - --dark-code-background: #1E1E1E; -} - -@media (prefers-color-scheme: light) { :root { - --hl-0: var(--light-hl-0); - --hl-1: var(--light-hl-1); - --hl-2: var(--light-hl-2); - --hl-3: var(--light-hl-3); - --hl-4: var(--light-hl-4); - --hl-5: var(--light-hl-5); - --hl-6: var(--light-hl-6); - --hl-7: var(--light-hl-7); - --hl-8: var(--light-hl-8); - --hl-9: var(--light-hl-9); - --hl-10: var(--light-hl-10); - --code-background: var(--light-code-background); -} } - -@media (prefers-color-scheme: dark) { :root { - --hl-0: var(--dark-hl-0); - --hl-1: var(--dark-hl-1); - --hl-2: var(--dark-hl-2); - --hl-3: var(--dark-hl-3); - --hl-4: var(--dark-hl-4); - --hl-5: var(--dark-hl-5); - --hl-6: var(--dark-hl-6); - --hl-7: var(--dark-hl-7); - --hl-8: var(--dark-hl-8); - --hl-9: var(--dark-hl-9); - --hl-10: var(--dark-hl-10); - --code-background: var(--dark-code-background); -} } - -:root[data-theme='light'] { - --hl-0: var(--light-hl-0); - --hl-1: var(--light-hl-1); - --hl-2: var(--light-hl-2); - --hl-3: var(--light-hl-3); - --hl-4: var(--light-hl-4); - --hl-5: var(--light-hl-5); - --hl-6: var(--light-hl-6); - --hl-7: var(--light-hl-7); - --hl-8: var(--light-hl-8); - --hl-9: var(--light-hl-9); - --hl-10: var(--light-hl-10); - --code-background: var(--light-code-background); -} - -:root[data-theme='dark'] { - --hl-0: var(--dark-hl-0); - --hl-1: var(--dark-hl-1); - --hl-2: var(--dark-hl-2); - --hl-3: var(--dark-hl-3); - --hl-4: var(--dark-hl-4); - --hl-5: var(--dark-hl-5); - --hl-6: var(--dark-hl-6); - --hl-7: var(--dark-hl-7); - --hl-8: var(--dark-hl-8); - --hl-9: var(--dark-hl-9); - --hl-10: var(--dark-hl-10); - --code-background: var(--dark-code-background); -} - -.hl-0 { color: var(--hl-0); } -.hl-1 { color: var(--hl-1); } -.hl-2 { color: var(--hl-2); } -.hl-3 { color: var(--hl-3); } -.hl-4 { color: var(--hl-4); } -.hl-5 { color: var(--hl-5); } -.hl-6 { color: var(--hl-6); } -.hl-7 { color: var(--hl-7); } -.hl-8 { color: var(--hl-8); } -.hl-9 { color: var(--hl-9); } -.hl-10 { color: var(--hl-10); } -pre, code { background: var(--code-background); } diff --git a/docs/oidc/assets/icons.js b/docs/oidc/assets/icons.js deleted file mode 100644 index 58882d76d..000000000 --- a/docs/oidc/assets/icons.js +++ /dev/null @@ -1,18 +0,0 @@ -(function() { - addIcons(); - function addIcons() { - if (document.readyState === "loading") return document.addEventListener("DOMContentLoaded", addIcons); - const svg = document.body.appendChild(document.createElementNS("http://www.w3.org/2000/svg", "svg")); - svg.innerHTML = `MMNEPVFCICPMFPCPTTAAATR`; - svg.style.display = "none"; - if (location.protocol === "file:") updateUseElements(); - } - - function updateUseElements() { - document.querySelectorAll("use").forEach(el => { - if (el.getAttribute("href").includes("#icon-")) { - el.setAttribute("href", el.getAttribute("href").replace(/.*#/, "#")); - } - }); - } -})() \ No newline at end of file diff --git a/docs/oidc/assets/icons.svg b/docs/oidc/assets/icons.svg deleted file mode 100644 index 50ad5799d..000000000 --- a/docs/oidc/assets/icons.svg +++ /dev/null @@ -1 +0,0 @@ -MMNEPVFCICPMFPCPTTAAATR \ No newline at end of file diff --git a/docs/oidc/assets/main.js b/docs/oidc/assets/main.js deleted file mode 100644 index 2363f64c2..000000000 --- a/docs/oidc/assets/main.js +++ /dev/null @@ -1,60 +0,0 @@ -"use strict"; -window.translations={"copy":"Copy","copied":"Copied!","normally_hidden":"This member is normally hidden due to your filter settings.","hierarchy_expand":"Expand","hierarchy_collapse":"Collapse","folder":"Folder","kind_1":"Project","kind_2":"Module","kind_4":"Namespace","kind_8":"Enumeration","kind_16":"Enumeration Member","kind_32":"Variable","kind_64":"Function","kind_128":"Class","kind_256":"Interface","kind_512":"Constructor","kind_1024":"Property","kind_2048":"Method","kind_4096":"Call Signature","kind_8192":"Index Signature","kind_16384":"Constructor Signature","kind_32768":"Parameter","kind_65536":"Type Literal","kind_131072":"Type Parameter","kind_262144":"Accessor","kind_524288":"Get Signature","kind_1048576":"Set Signature","kind_2097152":"Type Alias","kind_4194304":"Reference","kind_8388608":"Document"}; -"use strict";(()=>{var De=Object.create;var le=Object.defineProperty;var Fe=Object.getOwnPropertyDescriptor;var Ne=Object.getOwnPropertyNames;var Ve=Object.getPrototypeOf,Be=Object.prototype.hasOwnProperty;var qe=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports);var je=(t,e,n,r)=>{if(e&&typeof e=="object"||typeof e=="function")for(let i of Ne(e))!Be.call(t,i)&&i!==n&&le(t,i,{get:()=>e[i],enumerable:!(r=Fe(e,i))||r.enumerable});return t};var $e=(t,e,n)=>(n=t!=null?De(Ve(t)):{},je(e||!t||!t.__esModule?le(n,"default",{value:t,enumerable:!0}):n,t));var pe=qe((de,he)=>{(function(){var t=function(e){var n=new t.Builder;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),n.searchPipeline.add(t.stemmer),e.call(n,n),n.build()};t.version="2.3.9";t.utils={},t.utils.warn=function(e){return function(n){e.console&&console.warn&&console.warn(n)}}(this),t.utils.asString=function(e){return e==null?"":e.toString()},t.utils.clone=function(e){if(e==null)return e;for(var n=Object.create(null),r=Object.keys(e),i=0;i0){var d=t.utils.clone(n)||{};d.position=[a,c],d.index=s.length,s.push(new t.Token(r.slice(a,o),d))}a=o+1}}return s},t.tokenizer.separator=/[\s\-]+/;t.Pipeline=function(){this._stack=[]},t.Pipeline.registeredFunctions=Object.create(null),t.Pipeline.registerFunction=function(e,n){n in this.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[e.label]=e},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn(`Function is not registered with pipeline. This may cause problems when serialising the index. -`,e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(r){var i=t.Pipeline.registeredFunctions[r];if(i)n.add(i);else throw new Error("Cannot load unregistered function: "+r)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(n){t.Pipeline.warnIfFunctionNotRegistered(n),this._stack.push(n)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");r=r+1,this._stack.splice(r,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var r=this._stack.indexOf(e);if(r==-1)throw new Error("Cannot find existingFn");this._stack.splice(r,0,n)},t.Pipeline.prototype.remove=function(e){var n=this._stack.indexOf(e);n!=-1&&this._stack.splice(n,1)},t.Pipeline.prototype.run=function(e){for(var n=this._stack.length,r=0;r1&&(oe&&(r=s),o!=e);)i=r-n,s=n+Math.floor(i/2),o=this.elements[s*2];if(o==e||o>e)return s*2;if(ol?d+=2:a==l&&(n+=r[c+1]*i[d+1],c+=2,d+=2);return n},t.Vector.prototype.similarity=function(e){return this.dot(e)/this.magnitude()||0},t.Vector.prototype.toArray=function(){for(var e=new Array(this.elements.length/2),n=1,r=0;n0){var o=s.str.charAt(0),a;o in s.node.edges?a=s.node.edges[o]:(a=new t.TokenSet,s.node.edges[o]=a),s.str.length==1&&(a.final=!0),i.push({node:a,editsRemaining:s.editsRemaining,str:s.str.slice(1)})}if(s.editsRemaining!=0){if("*"in s.node.edges)var l=s.node.edges["*"];else{var l=new t.TokenSet;s.node.edges["*"]=l}if(s.str.length==0&&(l.final=!0),i.push({node:l,editsRemaining:s.editsRemaining-1,str:s.str}),s.str.length>1&&i.push({node:s.node,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)}),s.str.length==1&&(s.node.final=!0),s.str.length>=1){if("*"in s.node.edges)var c=s.node.edges["*"];else{var c=new t.TokenSet;s.node.edges["*"]=c}s.str.length==1&&(c.final=!0),i.push({node:c,editsRemaining:s.editsRemaining-1,str:s.str.slice(1)})}if(s.str.length>1){var d=s.str.charAt(0),m=s.str.charAt(1),p;m in s.node.edges?p=s.node.edges[m]:(p=new t.TokenSet,s.node.edges[m]=p),s.str.length==1&&(p.final=!0),i.push({node:p,editsRemaining:s.editsRemaining-1,str:d+s.str.slice(2)})}}}return r},t.TokenSet.fromString=function(e){for(var n=new t.TokenSet,r=n,i=0,s=e.length;i=e;n--){var r=this.uncheckedNodes[n],i=r.child.toString();i in this.minimizedNodes?r.parent.edges[r.char]=this.minimizedNodes[i]:(r.child._str=i,this.minimizedNodes[i]=r.child),this.uncheckedNodes.pop()}};t.Index=function(e){this.invertedIndex=e.invertedIndex,this.fieldVectors=e.fieldVectors,this.tokenSet=e.tokenSet,this.fields=e.fields,this.pipeline=e.pipeline},t.Index.prototype.search=function(e){return this.query(function(n){var r=new t.QueryParser(e,n);r.parse()})},t.Index.prototype.query=function(e){for(var n=new t.Query(this.fields),r=Object.create(null),i=Object.create(null),s=Object.create(null),o=Object.create(null),a=Object.create(null),l=0;l1?this._b=1:this._b=e},t.Builder.prototype.k1=function(e){this._k1=e},t.Builder.prototype.add=function(e,n){var r=e[this._ref],i=Object.keys(this._fields);this._documents[r]=n||{},this.documentCount+=1;for(var s=0;s=this.length)return t.QueryLexer.EOS;var e=this.str.charAt(this.pos);return this.pos+=1,e},t.QueryLexer.prototype.width=function(){return this.pos-this.start},t.QueryLexer.prototype.ignore=function(){this.start==this.pos&&(this.pos+=1),this.start=this.pos},t.QueryLexer.prototype.backup=function(){this.pos-=1},t.QueryLexer.prototype.acceptDigitRun=function(){var e,n;do e=this.next(),n=e.charCodeAt(0);while(n>47&&n<58);e!=t.QueryLexer.EOS&&this.backup()},t.QueryLexer.prototype.more=function(){return this.pos1&&(e.backup(),e.emit(t.QueryLexer.TERM)),e.ignore(),e.more())return t.QueryLexer.lexText},t.QueryLexer.lexEditDistance=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.EDIT_DISTANCE),t.QueryLexer.lexText},t.QueryLexer.lexBoost=function(e){return e.ignore(),e.acceptDigitRun(),e.emit(t.QueryLexer.BOOST),t.QueryLexer.lexText},t.QueryLexer.lexEOS=function(e){e.width()>0&&e.emit(t.QueryLexer.TERM)},t.QueryLexer.termSeparator=t.tokenizer.separator,t.QueryLexer.lexText=function(e){for(;;){var n=e.next();if(n==t.QueryLexer.EOS)return t.QueryLexer.lexEOS;if(n.charCodeAt(0)==92){e.escapeCharacter();continue}if(n==":")return t.QueryLexer.lexField;if(n=="~")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexEditDistance;if(n=="^")return e.backup(),e.width()>0&&e.emit(t.QueryLexer.TERM),t.QueryLexer.lexBoost;if(n=="+"&&e.width()===1||n=="-"&&e.width()===1)return e.emit(t.QueryLexer.PRESENCE),t.QueryLexer.lexText;if(n.match(t.QueryLexer.termSeparator))return t.QueryLexer.lexTerm}},t.QueryParser=function(e,n){this.lexer=new t.QueryLexer(e),this.query=n,this.currentClause={},this.lexemeIdx=0},t.QueryParser.prototype.parse=function(){this.lexer.run(),this.lexemes=this.lexer.lexemes;for(var e=t.QueryParser.parseClause;e;)e=e(this);return this.query},t.QueryParser.prototype.peekLexeme=function(){return this.lexemes[this.lexemeIdx]},t.QueryParser.prototype.consumeLexeme=function(){var e=this.peekLexeme();return this.lexemeIdx+=1,e},t.QueryParser.prototype.nextClause=function(){var e=this.currentClause;this.query.clause(e),this.currentClause={}},t.QueryParser.parseClause=function(e){var n=e.peekLexeme();if(n!=null)switch(n.type){case t.QueryLexer.PRESENCE:return t.QueryParser.parsePresence;case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expected either a field or a term, found "+n.type;throw n.str.length>=1&&(r+=" with value '"+n.str+"'"),new t.QueryParseError(r,n.start,n.end)}},t.QueryParser.parsePresence=function(e){var n=e.consumeLexeme();if(n!=null){switch(n.str){case"-":e.currentClause.presence=t.Query.presence.PROHIBITED;break;case"+":e.currentClause.presence=t.Query.presence.REQUIRED;break;default:var r="unrecognised presence operator'"+n.str+"'";throw new t.QueryParseError(r,n.start,n.end)}var i=e.peekLexeme();if(i==null){var r="expecting term or field, found nothing";throw new t.QueryParseError(r,n.start,n.end)}switch(i.type){case t.QueryLexer.FIELD:return t.QueryParser.parseField;case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var r="expecting term or field, found '"+i.type+"'";throw new t.QueryParseError(r,i.start,i.end)}}},t.QueryParser.parseField=function(e){var n=e.consumeLexeme();if(n!=null){if(e.query.allFields.indexOf(n.str)==-1){var r=e.query.allFields.map(function(o){return"'"+o+"'"}).join(", "),i="unrecognised field '"+n.str+"', possible fields: "+r;throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.fields=[n.str];var s=e.peekLexeme();if(s==null){var i="expecting term, found nothing";throw new t.QueryParseError(i,n.start,n.end)}switch(s.type){case t.QueryLexer.TERM:return t.QueryParser.parseTerm;default:var i="expecting term, found '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseTerm=function(e){var n=e.consumeLexeme();if(n!=null){e.currentClause.term=n.str.toLowerCase(),n.str.indexOf("*")!=-1&&(e.currentClause.usePipeline=!1);var r=e.peekLexeme();if(r==null){e.nextClause();return}switch(r.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+r.type+"'";throw new t.QueryParseError(i,r.start,r.end)}}},t.QueryParser.parseEditDistance=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="edit distance must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.editDistance=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},t.QueryParser.parseBoost=function(e){var n=e.consumeLexeme();if(n!=null){var r=parseInt(n.str,10);if(isNaN(r)){var i="boost must be numeric";throw new t.QueryParseError(i,n.start,n.end)}e.currentClause.boost=r;var s=e.peekLexeme();if(s==null){e.nextClause();return}switch(s.type){case t.QueryLexer.TERM:return e.nextClause(),t.QueryParser.parseTerm;case t.QueryLexer.FIELD:return e.nextClause(),t.QueryParser.parseField;case t.QueryLexer.EDIT_DISTANCE:return t.QueryParser.parseEditDistance;case t.QueryLexer.BOOST:return t.QueryParser.parseBoost;case t.QueryLexer.PRESENCE:return e.nextClause(),t.QueryParser.parsePresence;default:var i="Unexpected lexeme type '"+s.type+"'";throw new t.QueryParseError(i,s.start,s.end)}}},function(e,n){typeof define=="function"&&define.amd?define(n):typeof de=="object"?he.exports=n():e.lunr=n()}(this,function(){return t})})()});window.translations||={copy:"Copy",copied:"Copied!",normally_hidden:"This member is normally hidden due to your filter settings.",hierarchy_expand:"Expand",hierarchy_collapse:"Collapse",folder:"Folder",kind_1:"Project",kind_2:"Module",kind_4:"Namespace",kind_8:"Enumeration",kind_16:"Enumeration Member",kind_32:"Variable",kind_64:"Function",kind_128:"Class",kind_256:"Interface",kind_512:"Constructor",kind_1024:"Property",kind_2048:"Method",kind_4096:"Call Signature",kind_8192:"Index Signature",kind_16384:"Constructor Signature",kind_32768:"Parameter",kind_65536:"Type Literal",kind_131072:"Type Parameter",kind_262144:"Accessor",kind_524288:"Get Signature",kind_1048576:"Set Signature",kind_2097152:"Type Alias",kind_4194304:"Reference",kind_8388608:"Document"};var ce=[];function G(t,e){ce.push({selector:e,constructor:t})}var J=class{alwaysVisibleMember=null;constructor(){this.createComponents(document.body),this.ensureFocusedElementVisible(),this.listenForCodeCopies(),window.addEventListener("hashchange",()=>this.ensureFocusedElementVisible()),document.body.style.display||(this.ensureFocusedElementVisible(),this.updateIndexVisibility(),this.scrollToHash())}createComponents(e){ce.forEach(n=>{e.querySelectorAll(n.selector).forEach(r=>{r.dataset.hasInstance||(new n.constructor({el:r,app:this}),r.dataset.hasInstance=String(!0))})})}filterChanged(){this.ensureFocusedElementVisible()}showPage(){document.body.style.display&&(document.body.style.removeProperty("display"),this.ensureFocusedElementVisible(),this.updateIndexVisibility(),this.scrollToHash())}scrollToHash(){if(location.hash){let e=document.getElementById(location.hash.substring(1));if(!e)return;e.scrollIntoView({behavior:"instant",block:"start"})}}ensureActivePageVisible(){let e=document.querySelector(".tsd-navigation .current"),n=e?.parentElement;for(;n&&!n.classList.contains(".tsd-navigation");)n instanceof HTMLDetailsElement&&(n.open=!0),n=n.parentElement;if(e&&!ze(e)){let r=e.getBoundingClientRect().top-document.documentElement.clientHeight/4;document.querySelector(".site-menu").scrollTop=r,document.querySelector(".col-sidebar").scrollTop=r}}updateIndexVisibility(){let e=document.querySelector(".tsd-index-content"),n=e?.open;e&&(e.open=!0),document.querySelectorAll(".tsd-index-section").forEach(r=>{r.style.display="block";let i=Array.from(r.querySelectorAll(".tsd-index-link")).every(s=>s.offsetParent==null);r.style.display=i?"none":"block"}),e&&(e.open=n)}ensureFocusedElementVisible(){if(this.alwaysVisibleMember&&(this.alwaysVisibleMember.classList.remove("always-visible"),this.alwaysVisibleMember.firstElementChild.remove(),this.alwaysVisibleMember=null),!location.hash)return;let e=document.getElementById(location.hash.substring(1));if(!e)return;let n=e.parentElement;for(;n&&n.tagName!=="SECTION";)n=n.parentElement;if(!n)return;let r=n.offsetParent==null,i=n;for(;i!==document.body;)i instanceof HTMLDetailsElement&&(i.open=!0),i=i.parentElement;if(n.offsetParent==null){this.alwaysVisibleMember=n,n.classList.add("always-visible");let s=document.createElement("p");s.classList.add("warning"),s.textContent=window.translations.normally_hidden,n.prepend(s)}r&&e.scrollIntoView()}listenForCodeCopies(){document.querySelectorAll("pre > button").forEach(e=>{let n;e.addEventListener("click",()=>{e.previousElementSibling instanceof HTMLElement&&navigator.clipboard.writeText(e.previousElementSibling.innerText.trim()),e.textContent=window.translations.copied,e.classList.add("visible"),clearTimeout(n),n=setTimeout(()=>{e.classList.remove("visible"),n=setTimeout(()=>{e.textContent=window.translations.copy},100)},1e3)})})}};function ze(t){let e=t.getBoundingClientRect(),n=Math.max(document.documentElement.clientHeight,window.innerHeight);return!(e.bottom<0||e.top-n>=0)}var ue=(t,e=100)=>{let n;return()=>{clearTimeout(n),n=setTimeout(()=>t(),e)}};var ge=$e(pe(),1);async function A(t){let e=Uint8Array.from(atob(t),s=>s.charCodeAt(0)),r=new Blob([e]).stream().pipeThrough(new DecompressionStream("deflate")),i=await new Response(r).text();return JSON.parse(i)}async function fe(t,e){if(!window.searchData)return;let n=await A(window.searchData);t.data=n,t.index=ge.Index.load(n.index),e.classList.remove("loading"),e.classList.add("ready")}function ve(){let t=document.getElementById("tsd-search");if(!t)return;let e={base:document.documentElement.dataset.base+"/"},n=document.getElementById("tsd-search-script");t.classList.add("loading"),n&&(n.addEventListener("error",()=>{t.classList.remove("loading"),t.classList.add("failure")}),n.addEventListener("load",()=>{fe(e,t)}),fe(e,t));let r=document.querySelector("#tsd-search input"),i=document.querySelector("#tsd-search .results");if(!r||!i)throw new Error("The input field or the result list wrapper was not found");i.addEventListener("mouseup",()=>{re(t)}),r.addEventListener("focus",()=>t.classList.add("has-focus")),We(t,i,r,e)}function We(t,e,n,r){n.addEventListener("input",ue(()=>{Ue(t,e,n,r)},200)),n.addEventListener("keydown",i=>{i.key=="Enter"?Je(e,t):i.key=="ArrowUp"?(me(e,n,-1),i.preventDefault()):i.key==="ArrowDown"&&(me(e,n,1),i.preventDefault())}),document.body.addEventListener("keypress",i=>{i.altKey||i.ctrlKey||i.metaKey||!n.matches(":focus")&&i.key==="/"&&(i.preventDefault(),n.focus())}),document.body.addEventListener("keyup",i=>{t.classList.contains("has-focus")&&(i.key==="Escape"||!e.matches(":focus-within")&&!n.matches(":focus"))&&(n.blur(),re(t))})}function re(t){t.classList.remove("has-focus")}function Ue(t,e,n,r){if(!r.index||!r.data)return;e.textContent="";let i=n.value.trim(),s;if(i){let o=i.split(" ").map(a=>a.length?`*${a}*`:"").join(" ");s=r.index.search(o)}else s=[];for(let o=0;oa.score-o.score);for(let o=0,a=Math.min(10,s.length);o`,d=ye(l.name,i);globalThis.DEBUG_SEARCH_WEIGHTS&&(d+=` (score: ${s[o].score.toFixed(2)})`),l.parent&&(d=` - ${ye(l.parent,i)}.${d}`);let m=document.createElement("li");m.classList.value=l.classes??"";let p=document.createElement("a");p.href=r.base+l.url,p.innerHTML=c+d,m.append(p),p.addEventListener("focus",()=>{e.querySelector(".current")?.classList.remove("current"),m.classList.add("current")}),e.appendChild(m)}}function me(t,e,n){let r=t.querySelector(".current");if(!r)r=t.querySelector(n==1?"li:first-child":"li:last-child"),r&&r.classList.add("current");else{let i=r;if(n===1)do i=i.nextElementSibling??void 0;while(i instanceof HTMLElement&&i.offsetParent==null);else do i=i.previousElementSibling??void 0;while(i instanceof HTMLElement&&i.offsetParent==null);i?(r.classList.remove("current"),i.classList.add("current")):n===-1&&(r.classList.remove("current"),e.focus())}}function Je(t,e){let n=t.querySelector(".current");if(n||(n=t.querySelector("li:first-child")),n){let r=n.querySelector("a");r&&(window.location.href=r.href),re(e)}}function ye(t,e){if(e==="")return t;let n=t.toLocaleLowerCase(),r=e.toLocaleLowerCase(),i=[],s=0,o=n.indexOf(r);for(;o!=-1;)i.push(ne(t.substring(s,o)),`${ne(t.substring(o,o+r.length))}`),s=o+r.length,o=n.indexOf(r,s);return i.push(ne(t.substring(s))),i.join("")}var Ge={"&":"&","<":"<",">":">","'":"'",'"':"""};function ne(t){return t.replace(/[&<>"'"]/g,e=>Ge[e])}var I=class{el;app;constructor(e){this.el=e.el,this.app=e.app}};var H="mousedown",Ee="mousemove",B="mouseup",X={x:0,y:0},xe=!1,ie=!1,Xe=!1,D=!1,be=/Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i.test(navigator.userAgent);document.documentElement.classList.add(be?"is-mobile":"not-mobile");be&&"ontouchstart"in document.documentElement&&(Xe=!0,H="touchstart",Ee="touchmove",B="touchend");document.addEventListener(H,t=>{ie=!0,D=!1;let e=H=="touchstart"?t.targetTouches[0]:t;X.y=e.pageY||0,X.x=e.pageX||0});document.addEventListener(Ee,t=>{if(ie&&!D){let e=H=="touchstart"?t.targetTouches[0]:t,n=X.x-(e.pageX||0),r=X.y-(e.pageY||0);D=Math.sqrt(n*n+r*r)>10}});document.addEventListener(B,()=>{ie=!1});document.addEventListener("click",t=>{xe&&(t.preventDefault(),t.stopImmediatePropagation(),xe=!1)});var Y=class extends I{active;className;constructor(e){super(e),this.className=this.el.dataset.toggle||"",this.el.addEventListener(B,n=>this.onPointerUp(n)),this.el.addEventListener("click",n=>n.preventDefault()),document.addEventListener(H,n=>this.onDocumentPointerDown(n)),document.addEventListener(B,n=>this.onDocumentPointerUp(n))}setActive(e){if(this.active==e)return;this.active=e,document.documentElement.classList.toggle("has-"+this.className,e),this.el.classList.toggle("active",e);let n=(this.active?"to-has-":"from-has-")+this.className;document.documentElement.classList.add(n),setTimeout(()=>document.documentElement.classList.remove(n),500)}onPointerUp(e){D||(this.setActive(!0),e.preventDefault())}onDocumentPointerDown(e){if(this.active){if(e.target.closest(".col-sidebar, .tsd-filter-group"))return;this.setActive(!1)}}onDocumentPointerUp(e){if(!D&&this.active&&e.target.closest(".col-sidebar")){let n=e.target.closest("a");if(n){let r=window.location.href;r.indexOf("#")!=-1&&(r=r.substring(0,r.indexOf("#"))),n.href.substring(0,r.length)==r&&setTimeout(()=>this.setActive(!1),250)}}}};var se;try{se=localStorage}catch{se={getItem(){return null},setItem(){}}}var C=se;var Le=document.head.appendChild(document.createElement("style"));Le.dataset.for="filters";var Z=class extends I{key;value;constructor(e){super(e),this.key=`filter-${this.el.name}`,this.value=this.el.checked,this.el.addEventListener("change",()=>{this.setLocalStorage(this.el.checked)}),this.setLocalStorage(this.fromLocalStorage()),Le.innerHTML+=`html:not(.${this.key}) .tsd-is-${this.el.name} { display: none; } -`,this.app.updateIndexVisibility()}fromLocalStorage(){let e=C.getItem(this.key);return e?e==="true":this.el.checked}setLocalStorage(e){C.setItem(this.key,e.toString()),this.value=e,this.handleValueChange()}handleValueChange(){this.el.checked=this.value,document.documentElement.classList.toggle(this.key,this.value),this.app.filterChanged(),this.app.updateIndexVisibility()}};var oe=new Map,ae=class{open;accordions=[];key;constructor(e,n){this.key=e,this.open=n}add(e){this.accordions.push(e),e.open=this.open,e.addEventListener("toggle",()=>{this.toggle(e.open)})}toggle(e){for(let n of this.accordions)n.open=e;C.setItem(this.key,e.toString())}},K=class extends I{constructor(e){super(e);let n=this.el.querySelector("summary"),r=n.querySelector("a");r&&r.addEventListener("click",()=>{location.assign(r.href)});let i=`tsd-accordion-${n.dataset.key??n.textContent.trim().replace(/\s+/g,"-").toLowerCase()}`,s;if(oe.has(i))s=oe.get(i);else{let o=C.getItem(i),a=o?o==="true":this.el.open;s=new ae(i,a),oe.set(i,s)}s.add(this.el)}};function Se(t){let e=C.getItem("tsd-theme")||"os";t.value=e,we(e),t.addEventListener("change",()=>{C.setItem("tsd-theme",t.value),we(t.value)})}function we(t){document.documentElement.dataset.theme=t}var ee;function Ce(){let t=document.getElementById("tsd-nav-script");t&&(t.addEventListener("load",Te),Te())}async function Te(){let t=document.getElementById("tsd-nav-container");if(!t||!window.navigationData)return;let e=await A(window.navigationData);ee=document.documentElement.dataset.base,ee.endsWith("/")||(ee+="/"),t.innerHTML="";for(let n of e)Ie(n,t,[]);window.app.createComponents(t),window.app.showPage(),window.app.ensureActivePageVisible()}function Ie(t,e,n){let r=e.appendChild(document.createElement("li"));if(t.children){let i=[...n,t.text],s=r.appendChild(document.createElement("details"));s.className=t.class?`${t.class} tsd-accordion`:"tsd-accordion";let o=s.appendChild(document.createElement("summary"));o.className="tsd-accordion-summary",o.dataset.key=i.join("$"),o.innerHTML='',ke(t,o);let a=s.appendChild(document.createElement("div"));a.className="tsd-accordion-details";let l=a.appendChild(document.createElement("ul"));l.className="tsd-nested-navigation";for(let c of t.children)Ie(c,l,i)}else ke(t,r,t.class)}function ke(t,e,n){if(t.path){let r=e.appendChild(document.createElement("a"));if(r.href=ee+t.path,n&&(r.className=n),location.pathname===r.pathname&&!r.href.includes("#")&&r.classList.add("current"),t.kind){let i=window.translations[`kind_${t.kind}`].replaceAll('"',""");r.innerHTML=``}r.appendChild(document.createElement("span")).textContent=t.text}else{let r=e.appendChild(document.createElement("span")),i=window.translations.folder.replaceAll('"',""");r.innerHTML=``,r.appendChild(document.createElement("span")).textContent=t.text}}var te=document.documentElement.dataset.base;te.endsWith("/")||(te+="/");function Pe(){document.querySelector(".tsd-full-hierarchy")?Ye():document.querySelector(".tsd-hierarchy")&&Ze()}function Ye(){document.addEventListener("click",r=>{let i=r.target;for(;i.parentElement&&i.parentElement.tagName!="LI";)i=i.parentElement;i.dataset.dropdown&&(i.dataset.dropdown=String(i.dataset.dropdown!=="true"))});let t=new Map,e=new Set;for(let r of document.querySelectorAll(".tsd-full-hierarchy [data-refl]")){let i=r.querySelector("ul");t.has(r.dataset.refl)?e.add(r.dataset.refl):i&&t.set(r.dataset.refl,i)}for(let r of e)n(r);function n(r){let i=t.get(r).cloneNode(!0);i.querySelectorAll("[id]").forEach(s=>{s.removeAttribute("id")}),i.querySelectorAll("[data-dropdown]").forEach(s=>{s.dataset.dropdown="false"});for(let s of document.querySelectorAll(`[data-refl="${r}"]`)){let o=tt(),a=s.querySelector("ul");s.insertBefore(o,a),o.dataset.dropdown=String(!!a),a||s.appendChild(i.cloneNode(!0))}}}function Ze(){let t=document.getElementById("tsd-hierarchy-script");t&&(t.addEventListener("load",Qe),Qe())}async function Qe(){let t=document.querySelector(".tsd-panel.tsd-hierarchy:has(h4 a)");if(!t||!window.hierarchyData)return;let e=+t.dataset.refl,n=await A(window.hierarchyData),r=t.querySelector("ul"),i=document.createElement("ul");if(i.classList.add("tsd-hierarchy"),Ke(i,n,e),r.querySelectorAll("li").length==i.querySelectorAll("li").length)return;let s=document.createElement("span");s.classList.add("tsd-hierarchy-toggle"),s.textContent=window.translations.hierarchy_expand,t.querySelector("h4 a")?.insertAdjacentElement("afterend",s),s.insertAdjacentText("beforebegin",", "),s.addEventListener("click",()=>{s.textContent===window.translations.hierarchy_expand?(r.insertAdjacentElement("afterend",i),r.remove(),s.textContent=window.translations.hierarchy_collapse):(i.insertAdjacentElement("afterend",r),i.remove(),s.textContent=window.translations.hierarchy_expand)})}function Ke(t,e,n){let r=e.roots.filter(i=>et(e,i,n));for(let i of r)t.appendChild(_e(e,i,n))}function _e(t,e,n,r=new Set){if(r.has(e))return;r.add(e);let i=t.reflections[e],s=document.createElement("li");if(s.classList.add("tsd-hierarchy-item"),e===n){let o=s.appendChild(document.createElement("span"));o.textContent=i.name,o.classList.add("tsd-hierarchy-target")}else{for(let a of i.uniqueNameParents||[]){let l=t.reflections[a],c=s.appendChild(document.createElement("a"));c.textContent=l.name,c.href=te+l.url,c.className=l.class+" tsd-signature-type",s.append(document.createTextNode("."))}let o=s.appendChild(document.createElement("a"));o.textContent=t.reflections[e].name,o.href=te+i.url,o.className=i.class+" tsd-signature-type"}if(i.children){let o=s.appendChild(document.createElement("ul"));o.classList.add("tsd-hierarchy");for(let a of i.children){let l=_e(t,a,n,r);l&&o.appendChild(l)}}return r.delete(e),s}function et(t,e,n){if(e===n)return!0;let r=new Set,i=[t.reflections[e]];for(;i.length;){let s=i.pop();if(!r.has(s)){r.add(s);for(let o of s.children||[]){if(o===n)return!0;i.push(t.reflections[o])}}}return!1}function tt(){let t=document.createElementNS("http://www.w3.org/2000/svg","svg");return t.setAttribute("width","20"),t.setAttribute("height","20"),t.setAttribute("viewBox","0 0 24 24"),t.setAttribute("fill","none"),t.innerHTML='',t}G(Y,"a[data-toggle]");G(K,".tsd-accordion");G(Z,".tsd-filter-item input[type=checkbox]");var Oe=document.getElementById("tsd-theme");Oe&&Se(Oe);var nt=new J;Object.defineProperty(window,"app",{value:nt});ve();Ce();Pe();})(); -/*! Bundled license information: - -lunr/lunr.js: - (** - * lunr - http://lunrjs.com - A bit like Solr, but much smaller and not as bright - 2.3.9 - * Copyright (C) 2020 Oliver Nightingale - * @license MIT - *) - (*! - * lunr.utils - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Set - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.tokenizer - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Pipeline - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Vector - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.stemmer - * Copyright (C) 2020 Oliver Nightingale - * Includes code from - http://tartarus.org/~martin/PorterStemmer/js.txt - *) - (*! - * lunr.stopWordFilter - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.trimmer - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.TokenSet - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Index - * Copyright (C) 2020 Oliver Nightingale - *) - (*! - * lunr.Builder - * Copyright (C) 2020 Oliver Nightingale - *) -*/ diff --git a/docs/oidc/assets/navigation.js b/docs/oidc/assets/navigation.js deleted file mode 100644 index 8450f1e30..000000000 --- a/docs/oidc/assets/navigation.js +++ /dev/null @@ -1 +0,0 @@ -window.navigationData = "eJyN01FPwjAQB/Dv0mciSEQCb2Qx0acZDfGB8DDXgzWMtmmvRjR+dzZqcNMr3tOS/a+/tb3b6lMgvKOYi1zJchGwMk59QG5RGe3FQNgCqybFgwU/pGquKtzXTeFOaSnm49Fsej0Zfw1o9wl8qPEiG0s4alYr0CQWE76RGb1R27QUc45355xxFHQK2EJmJCSVNuRI98bsFmWykZ2Yq6Xb95NyrWcskDzjOeRIuQX9IGN/giva01AmUcbRH515UxJc87TkHfYKOOLSAzke7XvO+hd4TY/9OfxPKh0090v9Q5ug40gMf9f0zdsbkiO2R4mJjfbQ7vVSWje/wAR/+iIlfEd/Fq+PJKe2rQ==" \ No newline at end of file diff --git a/docs/oidc/assets/search.js b/docs/oidc/assets/search.js deleted file mode 100644 index d56660fa3..000000000 --- a/docs/oidc/assets/search.js +++ /dev/null @@ -1 +0,0 @@ -window.searchData = "eJytmttu20gMht9Fe2u4noN8yF0RLNACBVpssd2LIChUWWmEOJYh2el2g7z7zuhImhybSnzlKBr+5Iw+cg7Sc1QWv6ro6uY5esi36+hqbifRNnnMoqsoLbNkn33O1+n1Js+2+2gSHcqNu3F32Kb7vNhW746bTO/3jxvXLt0kVZU53Sh6mZyU/if7IVDvW0kdeKMvZfGUr7OSUYa3pZKHqg6FUWvvnBTSs9VCxRoF+KEoHt43Gr3q/vcua+IDd08qq5kegiyzal+UmUDuj+/f/c3BAIhPol1S+mdC4pw2ViH3yWF/X5T5fyMCgCaXCGFfPGRbufuu+SVcl9mdG877MYPfGVzCvcOwzLd3hdw/sLhM/5/caI7pftv+Es43xc/isJc779tfwnm6yZJS7rtr/krXoVryV1YdNqEhaG6+qkZ93bsyHJCt78nrU159KpJ1vv15Vq4dqrza9Aanh6sxPf2c8uq9KzfOLk9d47U8iOTI7O2h+NyT+m/bvt1pXe1Ckw1x27e+TG8/hmsT22NRZZI476eYkwlCYujNys7s7aFkZVmIH3vX+FVuuWTuljzud8dRgO7LkzrFC7hTen0BbC3CXcO2Z8rvfb5Zl+y0Hw5hsHl1ENwYv++o+bwLreyOm8hHOknLb8nmkIlVO5TT8qmzC/eWKJwe9SowLZwKpLO5WBDbYpuODaKzuVgQh/xTkSab0Y/lkG96u4sF4xY2+fZDzibkqWBqu/v8TFqODGadV7tN8ntkKIPVxQLZlcXjbuyQ9EYXCyNZr3PfLtl8SUr3r31Wjs7lXmIHJd4S4sk6Fpw0j1qMXlpeh6YNwVYfr+q4hRwQ6VZxpxdurQEzOHZ5dndJvZ3fWIochveU1KVkOylyemInSb2KlmrCvgb2j1xXz24dRS6z7fprVlUuLURuXfOqb/4a16EsuC62dzm3KYK3R2fYZzfXfVw3xocy2fPdZFqN9vRnYF1b//91atfFmmOhvzda9W9+x+X/fUZLUAB6FXH61xavTf5jd5LUFzg8kfjHHkVpL+pjIOlpF8+mvMBd8Jjo2J3gfCjkjqJHT7kHx7Kz7fMTHtaRzXmDzelRO3Wuy/qVnepK3QfOSljPZ89KqNPbSeR8Zv9GV8/Rk1tN+TJ5Fempma6c8V2ebdb+FUkTjZMuHh+bR7ku0kP9523b7FuW7ovSN25av5tFk5vZxMynZjG/vZ3cdMb1jfofncbwn9pQuSvFGSpiqJChdleaM9TEUCND464MZ2iIoUGG1l1ZztASQ4sMY3cVc4YxMYyR4dxdzSdGTfV8iQznxHCODBfuajHRi+lqZpDhghgukOHSXS05wyUxXCJDR9DNaqLjqVkhuxWxW+Hn73FQM86louyoI3hqehQ3QorhBwOkPBaKRUhRhhSGSHk0FIuRohwpDJLyeCgWJUVZUhgm5RFRLE6K8qQwUMpjouasMWVKYaiUR0Ut2NGmXCkMlvK4qCXrmbKlMFwqRJeieCnMl675WrGFgvKlMV/aE6PZuqYpX/qoQNUViq1tmqlRmC/tidF8faN8acyX9sRoFk5N+dKYL+2J0SycmvKlMV/aE6NZODXlS2O+tCdGs3BqypfGfGlPjF6wxpQvjfnSnhnNwqkpYRoTZjwzmiXMUMIMJsx4ZgxLmKGEGUyY8cwYljBDCTNH02A9D7KEGWYmxIQZz4zhZ1FKmMGEGc+MYQkzlDCDCTOeGRNzRchQwgwmzATnRUMBMxgwE5waDeXLYL5MqH4ZSpfBdNnw/GgpXRbTZWu62IyylC6L6bI1XWxGWUqXxXTZmi42oyylyx4ttGq62IyyzFoL02U9L5bNKEvpspgu63mxbEZZSpfFdNkgXZbSZTFdNkiXpXRZTJcN0WUpXRbTFYfpiildMaYrDq++YkpXjOmKPS+WLT8xpSvGdMUmWARiSleM6Yo9L+yqOqZwxUcr+Ti4BIqZxXzzr3rb5fZb+2z9sdl+uV0TeFP1HH1vt2Vu5my3gs+Rmxyvnl9ehm2YuwI7MX/PO+NPyQdJowdJV8RFksOedtCZDzLzxs4NzSi17u3yoOmWZUN3ZyK14W0mGDUDZGRBtR+mgFDAMLmlvkyjOegAgQARLRvr5tO/wm3XqdxsUBMOTi/2K/tB9RTookivfz0FcAJBGZkKPEsGQgAoKxRqzl7BeINgtEzDnxOBIEC+mQ5r2YMjn8qAcQZdU7IsBl//AB0QndvHSXTAu00wTCDNjIyk7lAQBAPpUe1Qyca8fQ0NAlqA57YUaXio+0pSdF8cAEkLJGVjhSRpcTKgqhhZVeHT2IDQjDy0Riht35cAOTB4VlapvBxJHgOosPL+1Tpp/cpi0LIgEa28i/dF8ZCk5FmC8ZLH5bWYGQY8RCVL7E6s/YIDaIG4lDywYpdt/Q96NwUewxL0Vh7hrv/mG+QASIHRQrvmUymgBhJey/WaY2nwMIGMlRXD4ARmQQ9jWUjdlw1gwEFARibSv3QaVMBTWzZWpv21soLWf48+aIJyL8uh7k0RYBQkopq1AbW/saz0E+41mM70QqTRvhQcNEDNWrTD1f7aEYqITwUegZKNOfgMCXQP6OiVTKeqF1mIKgCVVAMvQMEQqXZsYhkGwxvKQQ4U91Vjptpf0/5aQV/dzmWX77JNvnVKN7cvL/8Dfp61rA=="; \ No newline at end of file diff --git a/docs/oidc/assets/style.css b/docs/oidc/assets/style.css deleted file mode 100644 index 2ab8b836e..000000000 --- a/docs/oidc/assets/style.css +++ /dev/null @@ -1,1611 +0,0 @@ -@layer typedoc { - :root { - /* Light */ - --light-color-background: #f2f4f8; - --light-color-background-secondary: #eff0f1; - --light-color-warning-text: #222; - --light-color-background-warning: #e6e600; - --light-color-accent: #c5c7c9; - --light-color-active-menu-item: var(--light-color-accent); - --light-color-text: #222; - --light-color-text-aside: #6e6e6e; - - --light-color-icon-background: var(--light-color-background); - --light-color-icon-text: var(--light-color-text); - - --light-color-comment-tag-text: var(--light-color-text); - --light-color-comment-tag: var(--light-color-background); - - --light-color-link: #1f70c2; - --light-color-focus-outline: #3584e4; - - --light-color-ts-keyword: #056bd6; - --light-color-ts-project: #b111c9; - --light-color-ts-module: var(--light-color-ts-project); - --light-color-ts-namespace: var(--light-color-ts-project); - --light-color-ts-enum: #7e6f15; - --light-color-ts-enum-member: var(--light-color-ts-enum); - --light-color-ts-variable: #4760ec; - --light-color-ts-function: #572be7; - --light-color-ts-class: #1f70c2; - --light-color-ts-interface: #108024; - --light-color-ts-constructor: var(--light-color-ts-class); - --light-color-ts-property: #9f5f30; - --light-color-ts-method: #be3989; - --light-color-ts-reference: #ff4d82; - --light-color-ts-call-signature: var(--light-color-ts-method); - --light-color-ts-index-signature: var(--light-color-ts-property); - --light-color-ts-constructor-signature: var( - --light-color-ts-constructor - ); - --light-color-ts-parameter: var(--light-color-ts-variable); - /* type literal not included as links will never be generated to it */ - --light-color-ts-type-parameter: #a55c0e; - --light-color-ts-accessor: #c73c3c; - --light-color-ts-get-signature: var(--light-color-ts-accessor); - --light-color-ts-set-signature: var(--light-color-ts-accessor); - --light-color-ts-type-alias: #d51270; - /* reference not included as links will be colored with the kind that it points to */ - --light-color-document: #000000; - - --light-color-alert-note: #0969d9; - --light-color-alert-tip: #1a7f37; - --light-color-alert-important: #8250df; - --light-color-alert-warning: #9a6700; - --light-color-alert-caution: #cf222e; - - --light-external-icon: url("data:image/svg+xml;utf8,"); - --light-color-scheme: light; - - /* Dark */ - --dark-color-background: #2b2e33; - --dark-color-background-secondary: #1e2024; - --dark-color-background-warning: #bebe00; - --dark-color-warning-text: #222; - --dark-color-accent: #9096a2; - --dark-color-active-menu-item: #5d5d6a; - --dark-color-text: #f5f5f5; - --dark-color-text-aside: #dddddd; - - --dark-color-icon-background: var(--dark-color-background-secondary); - --dark-color-icon-text: var(--dark-color-text); - - --dark-color-comment-tag-text: var(--dark-color-text); - --dark-color-comment-tag: var(--dark-color-background); - - --dark-color-link: #00aff4; - --dark-color-focus-outline: #4c97f2; - - --dark-color-ts-keyword: #3399ff; - --dark-color-ts-project: #e358ff; - --dark-color-ts-module: var(--dark-color-ts-project); - --dark-color-ts-namespace: var(--dark-color-ts-project); - --dark-color-ts-enum: #f4d93e; - --dark-color-ts-enum-member: var(--dark-color-ts-enum); - --dark-color-ts-variable: #798dff; - --dark-color-ts-function: #a280ff; - --dark-color-ts-class: #8ac4ff; - --dark-color-ts-interface: #6cff87; - --dark-color-ts-constructor: var(--dark-color-ts-class); - --dark-color-ts-property: #ff984d; - --dark-color-ts-method: #ff4db8; - --dark-color-ts-reference: #ff4d82; - --dark-color-ts-call-signature: var(--dark-color-ts-method); - --dark-color-ts-index-signature: var(--dark-color-ts-property); - --dark-color-ts-constructor-signature: var(--dark-color-ts-constructor); - --dark-color-ts-parameter: var(--dark-color-ts-variable); - /* type literal not included as links will never be generated to it */ - --dark-color-ts-type-parameter: #e07d13; - --dark-color-ts-accessor: #ff6060; - --dark-color-ts-get-signature: var(--dark-color-ts-accessor); - --dark-color-ts-set-signature: var(--dark-color-ts-accessor); - --dark-color-ts-type-alias: #ff6492; - /* reference not included as links will be colored with the kind that it points to */ - --dark-color-document: #ffffff; - - --dark-color-alert-note: #0969d9; - --dark-color-alert-tip: #1a7f37; - --dark-color-alert-important: #8250df; - --dark-color-alert-warning: #9a6700; - --dark-color-alert-caution: #cf222e; - - --dark-external-icon: url("data:image/svg+xml;utf8,"); - --dark-color-scheme: dark; - } - - @media (prefers-color-scheme: light) { - :root { - --color-background: var(--light-color-background); - --color-background-secondary: var( - --light-color-background-secondary - ); - --color-background-warning: var(--light-color-background-warning); - --color-warning-text: var(--light-color-warning-text); - --color-accent: var(--light-color-accent); - --color-active-menu-item: var(--light-color-active-menu-item); - --color-text: var(--light-color-text); - --color-text-aside: var(--light-color-text-aside); - - --color-icon-background: var(--light-color-icon-background); - --color-icon-text: var(--light-color-icon-text); - - --color-comment-tag-text: var(--light-color-text); - --color-comment-tag: var(--light-color-background); - - --color-link: var(--light-color-link); - --color-focus-outline: var(--light-color-focus-outline); - - --color-ts-keyword: var(--light-color-ts-keyword); - --color-ts-project: var(--light-color-ts-project); - --color-ts-module: var(--light-color-ts-module); - --color-ts-namespace: var(--light-color-ts-namespace); - --color-ts-enum: var(--light-color-ts-enum); - --color-ts-enum-member: var(--light-color-ts-enum-member); - --color-ts-variable: var(--light-color-ts-variable); - --color-ts-function: var(--light-color-ts-function); - --color-ts-class: var(--light-color-ts-class); - --color-ts-interface: var(--light-color-ts-interface); - --color-ts-constructor: var(--light-color-ts-constructor); - --color-ts-property: var(--light-color-ts-property); - --color-ts-method: var(--light-color-ts-method); - --color-ts-reference: var(--light-color-ts-reference); - --color-ts-call-signature: var(--light-color-ts-call-signature); - --color-ts-index-signature: var(--light-color-ts-index-signature); - --color-ts-constructor-signature: var( - --light-color-ts-constructor-signature - ); - --color-ts-parameter: var(--light-color-ts-parameter); - --color-ts-type-parameter: var(--light-color-ts-type-parameter); - --color-ts-accessor: var(--light-color-ts-accessor); - --color-ts-get-signature: var(--light-color-ts-get-signature); - --color-ts-set-signature: var(--light-color-ts-set-signature); - --color-ts-type-alias: var(--light-color-ts-type-alias); - --color-document: var(--light-color-document); - - --color-alert-note: var(--light-color-alert-note); - --color-alert-tip: var(--light-color-alert-tip); - --color-alert-important: var(--light-color-alert-important); - --color-alert-warning: var(--light-color-alert-warning); - --color-alert-caution: var(--light-color-alert-caution); - - --external-icon: var(--light-external-icon); - --color-scheme: var(--light-color-scheme); - } - } - - @media (prefers-color-scheme: dark) { - :root { - --color-background: var(--dark-color-background); - --color-background-secondary: var( - --dark-color-background-secondary - ); - --color-background-warning: var(--dark-color-background-warning); - --color-warning-text: var(--dark-color-warning-text); - --color-accent: var(--dark-color-accent); - --color-active-menu-item: var(--dark-color-active-menu-item); - --color-text: var(--dark-color-text); - --color-text-aside: var(--dark-color-text-aside); - - --color-icon-background: var(--dark-color-icon-background); - --color-icon-text: var(--dark-color-icon-text); - - --color-comment-tag-text: var(--dark-color-text); - --color-comment-tag: var(--dark-color-background); - - --color-link: var(--dark-color-link); - --color-focus-outline: var(--dark-color-focus-outline); - - --color-ts-keyword: var(--dark-color-ts-keyword); - --color-ts-project: var(--dark-color-ts-project); - --color-ts-module: var(--dark-color-ts-module); - --color-ts-namespace: var(--dark-color-ts-namespace); - --color-ts-enum: var(--dark-color-ts-enum); - --color-ts-enum-member: var(--dark-color-ts-enum-member); - --color-ts-variable: var(--dark-color-ts-variable); - --color-ts-function: var(--dark-color-ts-function); - --color-ts-class: var(--dark-color-ts-class); - --color-ts-interface: var(--dark-color-ts-interface); - --color-ts-constructor: var(--dark-color-ts-constructor); - --color-ts-property: var(--dark-color-ts-property); - --color-ts-method: var(--dark-color-ts-method); - --color-ts-reference: var(--dark-color-ts-reference); - --color-ts-call-signature: var(--dark-color-ts-call-signature); - --color-ts-index-signature: var(--dark-color-ts-index-signature); - --color-ts-constructor-signature: var( - --dark-color-ts-constructor-signature - ); - --color-ts-parameter: var(--dark-color-ts-parameter); - --color-ts-type-parameter: var(--dark-color-ts-type-parameter); - --color-ts-accessor: var(--dark-color-ts-accessor); - --color-ts-get-signature: var(--dark-color-ts-get-signature); - --color-ts-set-signature: var(--dark-color-ts-set-signature); - --color-ts-type-alias: var(--dark-color-ts-type-alias); - --color-document: var(--dark-color-document); - - --color-alert-note: var(--dark-color-alert-note); - --color-alert-tip: var(--dark-color-alert-tip); - --color-alert-important: var(--dark-color-alert-important); - --color-alert-warning: var(--dark-color-alert-warning); - --color-alert-caution: var(--dark-color-alert-caution); - - --external-icon: var(--dark-external-icon); - --color-scheme: var(--dark-color-scheme); - } - } - - html { - color-scheme: var(--color-scheme); - } - - body { - margin: 0; - } - - :root[data-theme="light"] { - --color-background: var(--light-color-background); - --color-background-secondary: var(--light-color-background-secondary); - --color-background-warning: var(--light-color-background-warning); - --color-warning-text: var(--light-color-warning-text); - --color-icon-background: var(--light-color-icon-background); - --color-accent: var(--light-color-accent); - --color-active-menu-item: var(--light-color-active-menu-item); - --color-text: var(--light-color-text); - --color-text-aside: var(--light-color-text-aside); - --color-icon-text: var(--light-color-icon-text); - - --color-comment-tag-text: var(--light-color-text); - --color-comment-tag: var(--light-color-background); - - --color-link: var(--light-color-link); - --color-focus-outline: var(--light-color-focus-outline); - - --color-ts-keyword: var(--light-color-ts-keyword); - --color-ts-project: var(--light-color-ts-project); - --color-ts-module: var(--light-color-ts-module); - --color-ts-namespace: var(--light-color-ts-namespace); - --color-ts-enum: var(--light-color-ts-enum); - --color-ts-enum-member: var(--light-color-ts-enum-member); - --color-ts-variable: var(--light-color-ts-variable); - --color-ts-function: var(--light-color-ts-function); - --color-ts-class: var(--light-color-ts-class); - --color-ts-interface: var(--light-color-ts-interface); - --color-ts-constructor: var(--light-color-ts-constructor); - --color-ts-property: var(--light-color-ts-property); - --color-ts-method: var(--light-color-ts-method); - --color-ts-reference: var(--light-color-ts-reference); - --color-ts-call-signature: var(--light-color-ts-call-signature); - --color-ts-index-signature: var(--light-color-ts-index-signature); - --color-ts-constructor-signature: var( - --light-color-ts-constructor-signature - ); - --color-ts-parameter: var(--light-color-ts-parameter); - --color-ts-type-parameter: var(--light-color-ts-type-parameter); - --color-ts-accessor: var(--light-color-ts-accessor); - --color-ts-get-signature: var(--light-color-ts-get-signature); - --color-ts-set-signature: var(--light-color-ts-set-signature); - --color-ts-type-alias: var(--light-color-ts-type-alias); - --color-document: var(--light-color-document); - - --color-note: var(--light-color-note); - --color-tip: var(--light-color-tip); - --color-important: var(--light-color-important); - --color-warning: var(--light-color-warning); - --color-caution: var(--light-color-caution); - - --external-icon: var(--light-external-icon); - --color-scheme: var(--light-color-scheme); - } - - :root[data-theme="dark"] { - --color-background: var(--dark-color-background); - --color-background-secondary: var(--dark-color-background-secondary); - --color-background-warning: var(--dark-color-background-warning); - --color-warning-text: var(--dark-color-warning-text); - --color-icon-background: var(--dark-color-icon-background); - --color-accent: var(--dark-color-accent); - --color-active-menu-item: var(--dark-color-active-menu-item); - --color-text: var(--dark-color-text); - --color-text-aside: var(--dark-color-text-aside); - --color-icon-text: var(--dark-color-icon-text); - - --color-comment-tag-text: var(--dark-color-text); - --color-comment-tag: var(--dark-color-background); - - --color-link: var(--dark-color-link); - --color-focus-outline: var(--dark-color-focus-outline); - - --color-ts-keyword: var(--dark-color-ts-keyword); - --color-ts-project: var(--dark-color-ts-project); - --color-ts-module: var(--dark-color-ts-module); - --color-ts-namespace: var(--dark-color-ts-namespace); - --color-ts-enum: var(--dark-color-ts-enum); - --color-ts-enum-member: var(--dark-color-ts-enum-member); - --color-ts-variable: var(--dark-color-ts-variable); - --color-ts-function: var(--dark-color-ts-function); - --color-ts-class: var(--dark-color-ts-class); - --color-ts-interface: var(--dark-color-ts-interface); - --color-ts-constructor: var(--dark-color-ts-constructor); - --color-ts-property: var(--dark-color-ts-property); - --color-ts-method: var(--dark-color-ts-method); - --color-ts-reference: var(--dark-color-ts-reference); - --color-ts-call-signature: var(--dark-color-ts-call-signature); - --color-ts-index-signature: var(--dark-color-ts-index-signature); - --color-ts-constructor-signature: var( - --dark-color-ts-constructor-signature - ); - --color-ts-parameter: var(--dark-color-ts-parameter); - --color-ts-type-parameter: var(--dark-color-ts-type-parameter); - --color-ts-accessor: var(--dark-color-ts-accessor); - --color-ts-get-signature: var(--dark-color-ts-get-signature); - --color-ts-set-signature: var(--dark-color-ts-set-signature); - --color-ts-type-alias: var(--dark-color-ts-type-alias); - --color-document: var(--dark-color-document); - - --color-note: var(--dark-color-note); - --color-tip: var(--dark-color-tip); - --color-important: var(--dark-color-important); - --color-warning: var(--dark-color-warning); - --color-caution: var(--dark-color-caution); - - --external-icon: var(--dark-external-icon); - --color-scheme: var(--dark-color-scheme); - } - - *:focus-visible, - .tsd-accordion-summary:focus-visible svg { - outline: 2px solid var(--color-focus-outline); - } - - .always-visible, - .always-visible .tsd-signatures { - display: inherit !important; - } - - h1, - h2, - h3, - h4, - h5, - h6 { - line-height: 1.2; - } - - h1 { - font-size: 1.875rem; - margin: 0.67rem 0; - } - - h2 { - font-size: 1.5rem; - margin: 0.83rem 0; - } - - h3 { - font-size: 1.25rem; - margin: 1rem 0; - } - - h4 { - font-size: 1.05rem; - margin: 1.33rem 0; - } - - h5 { - font-size: 1rem; - margin: 1.5rem 0; - } - - h6 { - font-size: 0.875rem; - margin: 2.33rem 0; - } - - dl, - menu, - ol, - ul { - margin: 1em 0; - } - - dd { - margin: 0 0 0 34px; - } - - .container { - max-width: 1700px; - padding: 0 2rem; - } - - /* Footer */ - footer { - border-top: 1px solid var(--color-accent); - padding-top: 1rem; - padding-bottom: 1rem; - max-height: 3.5rem; - } - footer > p { - margin: 0 1em; - } - - .container-main { - margin: 0 auto; - /* toolbar, footer, margin */ - min-height: calc(100vh - 41px - 56px - 4rem); - } - - @keyframes fade-in { - from { - opacity: 0; - } - to { - opacity: 1; - } - } - @keyframes fade-out { - from { - opacity: 1; - visibility: visible; - } - to { - opacity: 0; - } - } - @keyframes fade-in-delayed { - 0% { - opacity: 0; - } - 33% { - opacity: 0; - } - 100% { - opacity: 1; - } - } - @keyframes fade-out-delayed { - 0% { - opacity: 1; - visibility: visible; - } - 66% { - opacity: 0; - } - 100% { - opacity: 0; - } - } - @keyframes pop-in-from-right { - from { - transform: translate(100%, 0); - } - to { - transform: translate(0, 0); - } - } - @keyframes pop-out-to-right { - from { - transform: translate(0, 0); - visibility: visible; - } - to { - transform: translate(100%, 0); - } - } - body { - background: var(--color-background); - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Noto Sans", - Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"; - font-size: 16px; - color: var(--color-text); - } - - a { - color: var(--color-link); - text-decoration: none; - } - a:hover { - text-decoration: underline; - } - a.external[target="_blank"] { - background-image: var(--external-icon); - background-position: top 3px right; - background-repeat: no-repeat; - padding-right: 13px; - } - a.tsd-anchor-link { - color: var(--color-text); - } - - code, - pre { - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; - padding: 0.2em; - margin: 0; - font-size: 0.875rem; - border-radius: 0.8em; - } - - pre { - position: relative; - white-space: pre-wrap; - word-wrap: break-word; - padding: 10px; - border: 1px solid var(--color-accent); - margin-bottom: 8px; - } - pre code { - padding: 0; - font-size: 100%; - } - pre > button { - position: absolute; - top: 10px; - right: 10px; - opacity: 0; - transition: opacity 0.1s; - box-sizing: border-box; - } - pre:hover > button, - pre > button.visible { - opacity: 1; - } - - blockquote { - margin: 1em 0; - padding-left: 1em; - border-left: 4px solid gray; - } - - .tsd-typography { - line-height: 1.333em; - } - .tsd-typography ul { - list-style: square; - padding: 0 0 0 20px; - margin: 0; - } - .tsd-typography .tsd-index-panel h3, - .tsd-index-panel .tsd-typography h3, - .tsd-typography h4, - .tsd-typography h5, - .tsd-typography h6 { - font-size: 1em; - } - .tsd-typography h5, - .tsd-typography h6 { - font-weight: normal; - } - .tsd-typography p, - .tsd-typography ul, - .tsd-typography ol { - margin: 1em 0; - } - .tsd-typography table { - border-collapse: collapse; - border: none; - } - .tsd-typography td, - .tsd-typography th { - padding: 6px 13px; - border: 1px solid var(--color-accent); - } - .tsd-typography thead, - .tsd-typography tr:nth-child(even) { - background-color: var(--color-background-secondary); - } - - .tsd-alert { - padding: 8px 16px; - margin-bottom: 16px; - border-left: 0.25em solid var(--alert-color); - } - .tsd-alert blockquote > :last-child, - .tsd-alert > :last-child { - margin-bottom: 0; - } - .tsd-alert-title { - color: var(--alert-color); - display: inline-flex; - align-items: center; - } - .tsd-alert-title span { - margin-left: 4px; - } - - .tsd-alert-note { - --alert-color: var(--color-alert-note); - } - .tsd-alert-tip { - --alert-color: var(--color-alert-tip); - } - .tsd-alert-important { - --alert-color: var(--color-alert-important); - } - .tsd-alert-warning { - --alert-color: var(--color-alert-warning); - } - .tsd-alert-caution { - --alert-color: var(--color-alert-caution); - } - - .tsd-breadcrumb { - margin: 0; - padding: 0; - color: var(--color-text-aside); - } - .tsd-breadcrumb a { - color: var(--color-text-aside); - text-decoration: none; - } - .tsd-breadcrumb a:hover { - text-decoration: underline; - } - .tsd-breadcrumb li { - display: inline; - } - .tsd-breadcrumb li:after { - content: " / "; - } - - .tsd-comment-tags { - display: flex; - flex-direction: column; - } - dl.tsd-comment-tag-group { - display: flex; - align-items: center; - overflow: hidden; - margin: 0.5em 0; - } - dl.tsd-comment-tag-group dt { - display: flex; - margin-right: 0.5em; - font-size: 0.875em; - font-weight: normal; - } - dl.tsd-comment-tag-group dd { - margin: 0; - } - code.tsd-tag { - padding: 0.25em 0.4em; - border: 0.1em solid var(--color-accent); - margin-right: 0.25em; - font-size: 70%; - } - h1 code.tsd-tag:first-of-type { - margin-left: 0.25em; - } - - dl.tsd-comment-tag-group dd:before, - dl.tsd-comment-tag-group dd:after { - content: " "; - } - dl.tsd-comment-tag-group dd pre, - dl.tsd-comment-tag-group dd:after { - clear: both; - } - dl.tsd-comment-tag-group p { - margin: 0; - } - - .tsd-panel.tsd-comment .lead { - font-size: 1.1em; - line-height: 1.333em; - margin-bottom: 2em; - } - .tsd-panel.tsd-comment .lead:last-child { - margin-bottom: 0; - } - - .tsd-filter-visibility h4 { - font-size: 1rem; - padding-top: 0.75rem; - padding-bottom: 0.5rem; - margin: 0; - } - .tsd-filter-item:not(:last-child) { - margin-bottom: 0.5rem; - } - .tsd-filter-input { - display: flex; - width: -moz-fit-content; - width: fit-content; - align-items: center; - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - cursor: pointer; - } - .tsd-filter-input input[type="checkbox"] { - cursor: pointer; - position: absolute; - width: 1.5em; - height: 1.5em; - opacity: 0; - } - .tsd-filter-input input[type="checkbox"]:disabled { - pointer-events: none; - } - .tsd-filter-input svg { - cursor: pointer; - width: 1.5em; - height: 1.5em; - margin-right: 0.5em; - border-radius: 0.33em; - /* Leaving this at full opacity breaks event listeners on Firefox. - Don't remove unless you know what you're doing. */ - opacity: 0.99; - } - .tsd-filter-input input[type="checkbox"]:focus-visible + svg { - outline: 2px solid var(--color-focus-outline); - } - .tsd-checkbox-background { - fill: var(--color-accent); - } - input[type="checkbox"]:checked ~ svg .tsd-checkbox-checkmark { - stroke: var(--color-text); - } - .tsd-filter-input input:disabled ~ svg > .tsd-checkbox-background { - fill: var(--color-background); - stroke: var(--color-accent); - stroke-width: 0.25rem; - } - .tsd-filter-input input:disabled ~ svg > .tsd-checkbox-checkmark { - stroke: var(--color-accent); - } - - .settings-label { - font-weight: bold; - text-transform: uppercase; - display: inline-block; - } - - .tsd-filter-visibility .settings-label { - margin: 0.75rem 0 0.5rem 0; - } - - .tsd-theme-toggle .settings-label { - margin: 0.75rem 0.75rem 0 0; - } - - .tsd-hierarchy h4 label:hover span { - text-decoration: underline; - } - - .tsd-hierarchy { - list-style: square; - margin: 0; - } - .tsd-hierarchy-target { - font-weight: bold; - } - .tsd-hierarchy-toggle { - color: var(--color-link); - cursor: pointer; - } - - .tsd-full-hierarchy:not(:last-child) { - margin-bottom: 1em; - padding-bottom: 1em; - border-bottom: 1px solid var(--color-accent); - } - .tsd-full-hierarchy, - .tsd-full-hierarchy ul { - list-style: none; - margin: 0; - padding: 0; - } - .tsd-full-hierarchy ul { - padding-left: 1.5rem; - } - .tsd-full-hierarchy a { - padding: 0.25rem 0 !important; - font-size: 1rem; - display: inline-flex; - align-items: center; - color: var(--color-text); - } - .tsd-full-hierarchy svg[data-dropdown] { - cursor: pointer; - } - .tsd-full-hierarchy svg[data-dropdown="false"] { - transform: rotate(-90deg); - } - .tsd-full-hierarchy svg[data-dropdown="false"] ~ ul { - display: none; - } - - .tsd-panel-group.tsd-index-group { - margin-bottom: 0; - } - .tsd-index-panel .tsd-index-list { - list-style: none; - line-height: 1.333em; - margin: 0; - padding: 0.25rem 0 0 0; - overflow: hidden; - display: grid; - grid-template-columns: repeat(3, 1fr); - column-gap: 1rem; - grid-template-rows: auto; - } - @media (max-width: 1024px) { - .tsd-index-panel .tsd-index-list { - grid-template-columns: repeat(2, 1fr); - } - } - @media (max-width: 768px) { - .tsd-index-panel .tsd-index-list { - grid-template-columns: repeat(1, 1fr); - } - } - .tsd-index-panel .tsd-index-list li { - -webkit-page-break-inside: avoid; - -moz-page-break-inside: avoid; - -ms-page-break-inside: avoid; - -o-page-break-inside: avoid; - page-break-inside: avoid; - } - - .tsd-flag { - display: inline-block; - padding: 0.25em 0.4em; - border-radius: 4px; - color: var(--color-comment-tag-text); - background-color: var(--color-comment-tag); - text-indent: 0; - font-size: 75%; - line-height: 1; - font-weight: normal; - } - - .tsd-anchor { - position: relative; - top: -100px; - } - - .tsd-member { - position: relative; - } - .tsd-member .tsd-anchor + h3 { - display: flex; - align-items: center; - margin-top: 0; - margin-bottom: 0; - border-bottom: none; - } - - .tsd-navigation.settings { - margin: 1rem 0; - } - .tsd-navigation > a, - .tsd-navigation .tsd-accordion-summary { - width: calc(100% - 0.25rem); - display: flex; - align-items: center; - } - .tsd-navigation a, - .tsd-navigation summary > span, - .tsd-page-navigation a { - display: flex; - width: calc(100% - 0.25rem); - align-items: center; - padding: 0.25rem; - color: var(--color-text); - text-decoration: none; - box-sizing: border-box; - } - .tsd-navigation a.current, - .tsd-page-navigation a.current { - background: var(--color-active-menu-item); - } - .tsd-navigation a:hover, - .tsd-page-navigation a:hover { - text-decoration: underline; - } - .tsd-navigation ul, - .tsd-page-navigation ul { - margin-top: 0; - margin-bottom: 0; - padding: 0; - list-style: none; - } - .tsd-navigation li, - .tsd-page-navigation li { - padding: 0; - max-width: 100%; - } - .tsd-navigation .tsd-nav-link { - display: none; - } - .tsd-nested-navigation { - margin-left: 3rem; - } - .tsd-nested-navigation > li > details { - margin-left: -1.5rem; - } - .tsd-small-nested-navigation { - margin-left: 1.5rem; - } - .tsd-small-nested-navigation > li > details { - margin-left: -1.5rem; - } - - .tsd-page-navigation-section { - margin-left: 10px; - } - .tsd-page-navigation-section > summary { - padding: 0.25rem; - } - .tsd-page-navigation-section > div { - margin-left: 20px; - } - .tsd-page-navigation ul { - padding-left: 1.75rem; - } - - #tsd-sidebar-links a { - margin-top: 0; - margin-bottom: 0.5rem; - line-height: 1.25rem; - } - #tsd-sidebar-links a:last-of-type { - margin-bottom: 0; - } - - a.tsd-index-link { - padding: 0.25rem 0 !important; - font-size: 1rem; - line-height: 1.25rem; - display: inline-flex; - align-items: center; - color: var(--color-text); - } - .tsd-accordion-summary { - list-style-type: none; /* hide marker on non-safari */ - outline: none; /* broken on safari, so just hide it */ - } - .tsd-accordion-summary::-webkit-details-marker { - display: none; /* hide marker on safari */ - } - .tsd-accordion-summary, - .tsd-accordion-summary a { - -moz-user-select: none; - -webkit-user-select: none; - -ms-user-select: none; - user-select: none; - - cursor: pointer; - } - .tsd-accordion-summary a { - width: calc(100% - 1.5rem); - } - .tsd-accordion-summary > * { - margin-top: 0; - margin-bottom: 0; - padding-top: 0; - padding-bottom: 0; - } - .tsd-accordion .tsd-accordion-summary > svg { - margin-left: 0.25rem; - vertical-align: text-top; - } - /* - * We need to be careful to target the arrow indicating whether the accordion - * is open, but not any other SVGs included in the details element. - */ - .tsd-accordion:not([open]) > .tsd-accordion-summary > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h1 > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h2 > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h3 > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h4 > svg:first-child, - .tsd-accordion:not([open]) > .tsd-accordion-summary > h5 > svg:first-child { - transform: rotate(-90deg); - } - .tsd-index-content > :not(:first-child) { - margin-top: 0.75rem; - } - .tsd-index-heading { - margin-top: 1.5rem; - margin-bottom: 0.75rem; - } - - .tsd-no-select { - -webkit-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - } - .tsd-kind-icon { - margin-right: 0.5rem; - width: 1.25rem; - height: 1.25rem; - min-width: 1.25rem; - min-height: 1.25rem; - } - .tsd-signature > .tsd-kind-icon { - margin-right: 0.8rem; - } - - .tsd-panel { - margin-bottom: 2.5rem; - } - .tsd-panel.tsd-member { - margin-bottom: 4rem; - } - .tsd-panel:empty { - display: none; - } - .tsd-panel > h1, - .tsd-panel > h2, - .tsd-panel > h3 { - margin: 1.5rem -1.5rem 0.75rem -1.5rem; - padding: 0 1.5rem 0.75rem 1.5rem; - } - .tsd-panel > h1.tsd-before-signature, - .tsd-panel > h2.tsd-before-signature, - .tsd-panel > h3.tsd-before-signature { - margin-bottom: 0; - border-bottom: none; - } - - .tsd-panel-group { - margin: 2rem 0; - } - .tsd-panel-group.tsd-index-group { - margin: 2rem 0; - } - .tsd-panel-group.tsd-index-group details { - margin: 2rem 0; - } - .tsd-panel-group > .tsd-accordion-summary { - margin-bottom: 1rem; - } - - #tsd-search { - transition: background-color 0.2s; - } - #tsd-search .title { - position: relative; - z-index: 2; - } - #tsd-search .field { - position: absolute; - left: 0; - top: 0; - right: 2.5rem; - height: 100%; - } - #tsd-search .field input { - box-sizing: border-box; - position: relative; - top: -50px; - z-index: 1; - width: 100%; - padding: 0 10px; - opacity: 0; - outline: 0; - border: 0; - background: transparent; - color: var(--color-text); - } - #tsd-search .field label { - position: absolute; - overflow: hidden; - right: -40px; - } - #tsd-search .field input, - #tsd-search .title, - #tsd-toolbar-links a { - transition: opacity 0.2s; - } - #tsd-search .results { - position: absolute; - visibility: hidden; - top: 40px; - width: 100%; - margin: 0; - padding: 0; - list-style: none; - box-shadow: 0 0 4px rgba(0, 0, 0, 0.25); - } - #tsd-search .results li { - background-color: var(--color-background); - line-height: initial; - padding: 4px; - } - #tsd-search .results li:nth-child(even) { - background-color: var(--color-background-secondary); - } - #tsd-search .results li.state { - display: none; - } - #tsd-search .results li.current:not(.no-results), - #tsd-search .results li:hover:not(.no-results) { - background-color: var(--color-accent); - } - #tsd-search .results a { - display: flex; - align-items: center; - padding: 0.25rem; - box-sizing: border-box; - } - #tsd-search .results a:before { - top: 10px; - } - #tsd-search .results span.parent { - color: var(--color-text-aside); - font-weight: normal; - } - #tsd-search.has-focus { - background-color: var(--color-accent); - } - #tsd-search.has-focus .field input { - top: 0; - opacity: 1; - } - #tsd-search.has-focus .title, - #tsd-search.has-focus #tsd-toolbar-links a { - z-index: 0; - opacity: 0; - } - #tsd-search.has-focus .results { - visibility: visible; - } - #tsd-search.loading .results li.state.loading { - display: block; - } - #tsd-search.failure .results li.state.failure { - display: block; - } - - #tsd-toolbar-links { - position: absolute; - top: 0; - right: 2rem; - height: 100%; - display: flex; - align-items: center; - justify-content: flex-end; - } - #tsd-toolbar-links a { - margin-left: 1.5rem; - } - #tsd-toolbar-links a:hover { - text-decoration: underline; - } - - .tsd-signature { - margin: 0 0 1rem 0; - padding: 1rem 0.5rem; - border: 1px solid var(--color-accent); - font-family: Menlo, Monaco, Consolas, "Courier New", monospace; - font-size: 14px; - overflow-x: auto; - } - - .tsd-signature-keyword { - color: var(--color-ts-keyword); - font-weight: normal; - } - - .tsd-signature-symbol { - color: var(--color-text-aside); - font-weight: normal; - } - - .tsd-signature-type { - font-style: italic; - font-weight: normal; - } - - .tsd-signatures { - padding: 0; - margin: 0 0 1em 0; - list-style-type: none; - } - .tsd-signatures .tsd-signature { - margin: 0; - border-color: var(--color-accent); - border-width: 1px 0; - transition: background-color 0.1s; - } - .tsd-signatures .tsd-index-signature:not(:last-child) { - margin-bottom: 1em; - } - .tsd-signatures .tsd-index-signature .tsd-signature { - border-width: 1px; - } - .tsd-description .tsd-signatures .tsd-signature { - border-width: 1px; - } - - ul.tsd-parameter-list, - ul.tsd-type-parameter-list { - list-style: square; - margin: 0; - padding-left: 20px; - } - ul.tsd-parameter-list > li.tsd-parameter-signature, - ul.tsd-type-parameter-list > li.tsd-parameter-signature { - list-style: none; - margin-left: -20px; - } - ul.tsd-parameter-list h5, - ul.tsd-type-parameter-list h5 { - font-size: 16px; - margin: 1em 0 0.5em 0; - } - .tsd-sources { - margin-top: 1rem; - font-size: 0.875em; - } - .tsd-sources a { - color: var(--color-text-aside); - text-decoration: underline; - } - .tsd-sources ul { - list-style: none; - padding: 0; - } - - .tsd-page-toolbar { - position: sticky; - z-index: 1; - top: 0; - left: 0; - width: 100%; - color: var(--color-text); - background: var(--color-background-secondary); - border-bottom: 1px var(--color-accent) solid; - transition: transform 0.3s ease-in-out; - } - .tsd-page-toolbar a { - color: var(--color-text); - text-decoration: none; - } - .tsd-page-toolbar a.title { - font-weight: bold; - } - .tsd-page-toolbar a.title:hover { - text-decoration: underline; - } - .tsd-page-toolbar .tsd-toolbar-contents { - display: flex; - justify-content: space-between; - height: 2.5rem; - margin: 0 auto; - } - .tsd-page-toolbar .table-cell { - position: relative; - white-space: nowrap; - line-height: 40px; - } - .tsd-page-toolbar .table-cell:first-child { - width: 100%; - } - .tsd-page-toolbar .tsd-toolbar-icon { - box-sizing: border-box; - line-height: 0; - padding: 12px 0; - } - - .tsd-widget { - display: inline-block; - overflow: hidden; - opacity: 0.8; - height: 40px; - transition: - opacity 0.1s, - background-color 0.2s; - vertical-align: bottom; - cursor: pointer; - } - .tsd-widget:hover { - opacity: 0.9; - } - .tsd-widget.active { - opacity: 1; - background-color: var(--color-accent); - } - .tsd-widget.no-caption { - width: 40px; - } - .tsd-widget.no-caption:before { - margin: 0; - } - - .tsd-widget.options, - .tsd-widget.menu { - display: none; - } - input[type="checkbox"] + .tsd-widget:before { - background-position: -120px 0; - } - input[type="checkbox"]:checked + .tsd-widget:before { - background-position: -160px 0; - } - - img { - max-width: 100%; - } - - .tsd-member-summary-name { - display: inline-flex; - align-items: center; - padding: 0.25rem; - text-decoration: none; - } - - .tsd-anchor-icon { - display: inline-flex; - align-items: center; - margin-left: 0.5rem; - color: var(--color-text); - } - - .tsd-anchor-icon svg { - width: 1em; - height: 1em; - visibility: hidden; - } - - .tsd-member-summary-name:hover > .tsd-anchor-icon svg, - .tsd-anchor-link:hover > .tsd-anchor-icon svg { - visibility: visible; - } - - .deprecated { - text-decoration: line-through !important; - } - - .warning { - padding: 1rem; - color: var(--color-warning-text); - background: var(--color-background-warning); - } - - .tsd-kind-project { - color: var(--color-ts-project); - } - .tsd-kind-module { - color: var(--color-ts-module); - } - .tsd-kind-namespace { - color: var(--color-ts-namespace); - } - .tsd-kind-enum { - color: var(--color-ts-enum); - } - .tsd-kind-enum-member { - color: var(--color-ts-enum-member); - } - .tsd-kind-variable { - color: var(--color-ts-variable); - } - .tsd-kind-function { - color: var(--color-ts-function); - } - .tsd-kind-class { - color: var(--color-ts-class); - } - .tsd-kind-interface { - color: var(--color-ts-interface); - } - .tsd-kind-constructor { - color: var(--color-ts-constructor); - } - .tsd-kind-property { - color: var(--color-ts-property); - } - .tsd-kind-method { - color: var(--color-ts-method); - } - .tsd-kind-reference { - color: var(--color-ts-reference); - } - .tsd-kind-call-signature { - color: var(--color-ts-call-signature); - } - .tsd-kind-index-signature { - color: var(--color-ts-index-signature); - } - .tsd-kind-constructor-signature { - color: var(--color-ts-constructor-signature); - } - .tsd-kind-parameter { - color: var(--color-ts-parameter); - } - .tsd-kind-type-parameter { - color: var(--color-ts-type-parameter); - } - .tsd-kind-accessor { - color: var(--color-ts-accessor); - } - .tsd-kind-get-signature { - color: var(--color-ts-get-signature); - } - .tsd-kind-set-signature { - color: var(--color-ts-set-signature); - } - .tsd-kind-type-alias { - color: var(--color-ts-type-alias); - } - - /* if we have a kind icon, don't color the text by kind */ - .tsd-kind-icon ~ span { - color: var(--color-text); - } - - * { - scrollbar-width: thin; - scrollbar-color: var(--color-accent) var(--color-icon-background); - } - - *::-webkit-scrollbar { - width: 0.75rem; - } - - *::-webkit-scrollbar-track { - background: var(--color-icon-background); - } - - *::-webkit-scrollbar-thumb { - background-color: var(--color-accent); - border-radius: 999rem; - border: 0.25rem solid var(--color-icon-background); - } - - /* mobile */ - @media (max-width: 769px) { - .tsd-widget.options, - .tsd-widget.menu { - display: inline-block; - } - - .container-main { - display: flex; - } - html .col-content { - float: none; - max-width: 100%; - width: 100%; - } - html .col-sidebar { - position: fixed !important; - overflow-y: auto; - -webkit-overflow-scrolling: touch; - z-index: 1024; - top: 0 !important; - bottom: 0 !important; - left: auto !important; - right: 0 !important; - padding: 1.5rem 1.5rem 0 0; - width: 75vw; - visibility: hidden; - background-color: var(--color-background); - transform: translate(100%, 0); - } - html .col-sidebar > *:last-child { - padding-bottom: 20px; - } - html .overlay { - content: ""; - display: block; - position: fixed; - z-index: 1023; - top: 0; - left: 0; - right: 0; - bottom: 0; - background-color: rgba(0, 0, 0, 0.75); - visibility: hidden; - } - - .to-has-menu .overlay { - animation: fade-in 0.4s; - } - - .to-has-menu .col-sidebar { - animation: pop-in-from-right 0.4s; - } - - .from-has-menu .overlay { - animation: fade-out 0.4s; - } - - .from-has-menu .col-sidebar { - animation: pop-out-to-right 0.4s; - } - - .has-menu body { - overflow: hidden; - } - .has-menu .overlay { - visibility: visible; - } - .has-menu .col-sidebar { - visibility: visible; - transform: translate(0, 0); - display: flex; - flex-direction: column; - gap: 1.5rem; - max-height: 100vh; - padding: 1rem 2rem; - } - .has-menu .tsd-navigation { - max-height: 100%; - } - #tsd-toolbar-links { - display: none; - } - .tsd-navigation .tsd-nav-link { - display: flex; - } - } - - /* one sidebar */ - @media (min-width: 770px) { - .container-main { - display: grid; - grid-template-columns: minmax(0, 1fr) minmax(0, 2fr); - grid-template-areas: "sidebar content"; - margin: 2rem auto; - } - - .col-sidebar { - grid-area: sidebar; - } - .col-content { - grid-area: content; - padding: 0 1rem; - } - } - @media (min-width: 770px) and (max-width: 1399px) { - .col-sidebar { - max-height: calc(100vh - 2rem - 42px); - overflow: auto; - position: sticky; - top: 42px; - padding-top: 1rem; - } - .site-menu { - margin-top: 1rem; - } - } - - /* two sidebars */ - @media (min-width: 1200px) { - .container-main { - grid-template-columns: minmax(0, 1fr) minmax(0, 2.5fr) minmax( - 0, - 20rem - ); - grid-template-areas: "sidebar content toc"; - } - - .col-sidebar { - display: contents; - } - - .page-menu { - grid-area: toc; - padding-left: 1rem; - } - .site-menu { - grid-area: sidebar; - } - - .site-menu { - margin-top: 1rem; - } - - .page-menu, - .site-menu { - max-height: calc(100vh - 2rem - 42px); - overflow: auto; - position: sticky; - top: 42px; - } - } -} diff --git a/docs/oidc/functions/OidcProvider.html b/docs/oidc/functions/OidcProvider.html deleted file mode 100644 index 80d691a8d..000000000 --- a/docs/oidc/functions/OidcProvider.html +++ /dev/null @@ -1,9 +0,0 @@ -OidcProvider | @ping-identity/rn-oidc
  • Shares one OIDC hook state instance across a subtree.

    -

    Parameters

    • props: OidcProviderProps

      Provider props.

      -
      • children: React.ReactNode

        Descendant React nodes.

        -
      • client: OidcWebClient

        OIDC web client shared across descendants.

        -

    Returns ReactElement

    OIDC provider element.

    -
    <OidcProvider client={oidcWebClient}>
    <OidcScreen />
    </OidcProvider> -
    - -
diff --git a/docs/oidc/functions/createOidcClient.html b/docs/oidc/functions/createOidcClient.html deleted file mode 100644 index 94a8a1de5..000000000 --- a/docs/oidc/functions/createOidcClient.html +++ /dev/null @@ -1,7 +0,0 @@ -createOidcClient | @ping-identity/rn-oidc
  • Create a native-backed OIDC client.

    -

    Parameters

    Returns OidcClient

    OIDC client handle that wraps the native instance.

    -

    If you configured storage with configureOidcStorage, pass the returned -configuration in config.storage to bind the native token storage.

    -

    Error when required configuration is missing or invalid.

    -
diff --git a/docs/oidc/functions/createOidcWebClient.html b/docs/oidc/functions/createOidcWebClient.html deleted file mode 100644 index 7cf0e8516..000000000 --- a/docs/oidc/functions/createOidcWebClient.html +++ /dev/null @@ -1,4 +0,0 @@ -createOidcWebClient | @ping-identity/rn-oidc

Function createOidcWebClient

diff --git a/docs/oidc/functions/useOidc.html b/docs/oidc/functions/useOidc.html deleted file mode 100644 index a24f90ea7..000000000 --- a/docs/oidc/functions/useOidc.html +++ /dev/null @@ -1,18 +0,0 @@ -useOidc | @ping-identity/rn-oidc
  • Access OIDC state/actions from a provided or contextual client.

    -

    Parameters

    • Optionalclient: OidcWebClient

      Optional explicit OIDC web client.

      -

    Returns OidcHookResult

    Tuple of current OIDC state and actions.

    -

    Resolution order:

    -
      -
    1. Explicit client argument.
    2. -
    3. Nearest OidcProvider context.
    4. -
    5. Internal missing-client fallback that surfaces state errors on action calls.
    6. -
    -

    Using context:

    -
    const [state, actions] = useOidc();
    -
    - -

    Using an explicit client:

    -
    const [state, actions] = useOidc(oidcWebClient);
    -
    - -
diff --git a/docs/oidc/index.html b/docs/oidc/index.html deleted file mode 100644 index c3789e693..000000000 --- a/docs/oidc/index.html +++ /dev/null @@ -1,115 +0,0 @@ -@ping-identity/rn-oidc

@ping-identity/rn-oidc

-

Ping Identity

-

Ping Identity React Native OIDC

This module exposes native-backed OIDC clients for PingOne and ForgeRock platforms.

-

Add the package and let autolinking wire the native code:

-
yarn add @ping-identity/rn-oidc
cd ios && pod install -
- -

Optional integration packages:

-
yarn add @react-native-pingidentity/storage
yarn add @react-native-pingidentity/logger -
- -

The native OIDC SDK already bundles the required browser components, so you do not need the -React Native Browser package unless you plan to use it directly elsewhere in your app.

-
import { createOidcClient } from '@ping-identity/rn-oidc';

const oidcClient = createOidcClient({
clientId: 'client-id',
discoveryEndpoint: 'https://example.com/.well-known/openid-configuration',
redirectUri: 'com.example.app://callback',
scopes: ['openid', 'profile'],
}); -
- -
-

TODO(Android): tokenExpiry will be reintroduced once the native Android SDK exposes it.

-
-

If you want to customize native token storage, configure it with the Storage module and pass the -handle into the OIDC client configuration.

-
import { configureOidcStorage } from '@react-native-pingidentity/storage';
import { createOidcClient } from '@ping-identity/rn-oidc';

const oidcStorage = configureOidcStorage({
android: {
fileName: 'ping-oidc',
keyAlias: 'ping-oidc',
strongBoxPreferred: true,
cacheStrategy: 'cache_on_failure',
},
ios: {
account: 'com.example.app.oidc',
encryptor: true,
cacheable: false,
},
});

const oidcClient = createOidcClient({
clientId: 'client-id',
discoveryEndpoint: 'https://example.com/.well-known/openid-configuration',
redirectUri: 'com.example.app://callback',
scopes: ['openid', 'profile'],
storage: oidcStorage,
}); -
- -

If you install the logger package, pass either a JS logger instance or a native logger handle. -Both logger and nativeLogger values must be created via @react-native-pingidentity/logger. -If the logger package is not installed/configured, do not pass logger values in OIDC config.

-
import { createOidcClient } from '@ping-identity/rn-oidc';
import { logger, configureLogger } from '@react-native-pingidentity/logger';

const jsLogger = logger({ level: 'debug' });
const nativeLogger = configureLogger({ level: 'warn' });

const oidcClient = createOidcClient({
clientId: 'client-id',
discoveryEndpoint: 'https://example.com/.well-known/openid-configuration',
redirectUri: 'com.example.app://callback',
scopes: ['openid', 'profile'],
logger: jsLogger,
nativeLogger,
}); -
- -

The OIDC client can be used without launching a browser. This is useful for headless token -operations such as refresh, revoke, or userinfo.

-
const tokens = await oidcClient.token();
const refreshed = await oidcClient.refresh();
const profile = await oidcClient.userinfo(true);
await oidcClient.revoke();
const endSession = await oidcClient.endSession(); -
- -

You may provide OpenID endpoints directly. Discovery is still required by the native SDKs, so -keep discoveryEndpoint even when using overrides.

-
const oidcClient = createOidcClient({
clientId: 'client-id',
discoveryEndpoint: 'https://example.com/.well-known/openid-configuration',
redirectUri: 'com.example.app://callback',
scopes: ['openid', 'profile'],
openId: {
authorizationEndpoint: 'https://example.com/oauth2/authorize',
tokenEndpoint: 'https://example.com/oauth2/token',
userinfoEndpoint: 'https://example.com/oauth2/userinfo',
endSessionEndpoint: 'https://example.com/oauth2/signoff',
revocationEndpoint: 'https://example.com/oauth2/revoke',
},
}); -
- -
-

TODO(iOS SDK 2.x): enforce full OpenID override requirements to match the native iOS behavior.

-
-
import { createOidcWebClient } from '@ping-identity/rn-oidc';

const oidcWebClient = createOidcWebClient(oidcClient);
const result = await oidcWebClient.authorize();

// result: { type: 'success' } | { type: 'cancel' } -
- -

If you want to customize Custom Tabs/Auth Tabs behavior, configure the Browser module once at -app startup. The OIDC module will inherit these settings.

-
import { configureBrowser } from '@react-native-pingidentity/browser';

configureBrowser({
android: {
customTabs: {
showTitle: true,
urlBarHidingEnabled: true,
colorScheme: 'system',
},
authTabs: {
ephemeral: true,
colorScheme: 'dark',
toolbarColor: '#0B3D91',
},
},
}); -
- -

The iOS OIDC SDK supports per-client browser configuration. Provide ios options when creating -the OIDC client.

-
import { createOidcClient } from '@ping-identity/rn-oidc';

const oidcClient = createOidcClient({
clientId: 'client-id',
discoveryEndpoint: 'https://example.com/.well-known/openid-configuration',
redirectUri: 'com.example.app://callback',
scopes: ['openid', 'profile'],
ios: {
browserType: 'authSession',
browserMode: 'login',
},
}); -
- -
const user = await oidcWebClient.user();
if (user) {
const tokens = await user.token();
const refreshed = await user.refresh();
const profile = await user.userinfo(true);
await user.revoke();
await user.logout();
} -
- -

Use OidcProvider when multiple screens should share one OIDC state source (auth status, user, -loading, and errors). This avoids duplicating useOidc(oidcWebClient) in each screen.

-
import { OidcProvider, useOidc } from '@ping-identity/rn-oidc';

function App(): React.ReactElement {
return (
<OidcProvider client={oidcWebClient}>
<OidcScreen />
</OidcProvider>
);
}

function OidcScreen(): React.ReactElement {
const [state, actions] = useOidc();

const onLogin = async (): Promise<void> => {
await actions.authorize();
};

const onLogout = async (): Promise<void> => {
await actions.logout();
};

return <></>;
} -
- -

Common hook state/actions:

-
    -
  • state.isAuthenticated indicates whether a user session is currently available.
  • -
  • state.user is the resolved user session object (or null).
  • -
  • state.loading indicates an in-flight OIDC operation.
  • -
  • state.error is the latest operation error (if any).
  • -
  • actions.authorize(), actions.logout(), actions.refresh(), actions.revoke(), actions.userinfo(), actions.clear().
  • -
-

If you only need OIDC in one screen, you can skip the provider and pass the client directly:

-
const [state, actions] = useOidc(oidcWebClient);
-
- -

Configure the app redirect scheme for Custom Tabs/Auth Tabs. For a redirect URI of -com.example.app://callback, add the manifest placeholder:

-
android {
-  defaultConfig {
-    manifestPlaceholders["appRedirectUriScheme"] = "com.example.app"
-  }
-}
-
- -

Add the redirect intent filter to the CustomTabActivity:

-

-    
-        
-
-        
-        
-
-        
-    
-
-
- -

For HTTPS redirect URIs, add an App Links intent filter that matches your redirect URL and host. -See the Android App Links documentation for assetlinks.json setup.

-

All promise rejections use the shared GenericError contract from @ping-identity/rn-types.

-
import type { OidcError } from '@ping-identity/rn-oidc';

try {
await oidcWebClient.authorize();
} catch (error) {
const oidcError = error as OidcError;
console.log(oidcError.type, oidcError.error, oidcError.message);
} -
- -
diff --git a/docs/oidc/modules.html b/docs/oidc/modules.html deleted file mode 100644 index c610bc22a..000000000 --- a/docs/oidc/modules.html +++ /dev/null @@ -1 +0,0 @@ -@ping-identity/rn-oidc
diff --git a/docs/oidc/types/OidcAuthorizeOptions.html b/docs/oidc/types/OidcAuthorizeOptions.html deleted file mode 100644 index a9ebd8c70..000000000 --- a/docs/oidc/types/OidcAuthorizeOptions.html +++ /dev/null @@ -1,10 +0,0 @@ -OidcAuthorizeOptions | @ping-identity/rn-oidc

Type Alias OidcAuthorizeOptions

OidcAuthorizeOptions: {
    acrValues?: string;
    additionalParameters?: Record<string, string>;
    display?: string;
    loginHint?: string;
    nonce?: string;
    prompt?: string;
    state?: string;
    uiLocales?: string;
}

Optional overrides when launching an authorization request.

-

Type declaration

  • OptionalacrValues?: string

    Optional ACR values override for this authorization request.

    -
  • OptionaladditionalParameters?: Record<string, string>

    Additional provider-specific parameters.

    -
  • Optionaldisplay?: string

    Optional display parameter for the authorization request.

    -
  • OptionalloginHint?: string

    Optional login hint for the authorization request.

    -
  • Optionalnonce?: string

    Optional nonce override for this authorization request.

    -
  • Optionalprompt?: string

    Optional prompt parameter for the authorization request.

    -
  • Optionalstate?: string

    Optional state override for this authorization request.

    -
  • OptionaluiLocales?: string

    Optional UI locales override for this authorization request.

    -
diff --git a/docs/oidc/types/OidcAuthorizeResult.html b/docs/oidc/types/OidcAuthorizeResult.html deleted file mode 100644 index 295aca3e5..000000000 --- a/docs/oidc/types/OidcAuthorizeResult.html +++ /dev/null @@ -1,2 +0,0 @@ -OidcAuthorizeResult | @ping-identity/rn-oidc

Type Alias OidcAuthorizeResult

OidcAuthorizeResult: { type: "success" } | { type: "cancel" }

Result of an authorization attempt.

-
diff --git a/docs/oidc/types/OidcClient.html b/docs/oidc/types/OidcClient.html deleted file mode 100644 index c7b516ca7..000000000 --- a/docs/oidc/types/OidcClient.html +++ /dev/null @@ -1,10 +0,0 @@ -OidcClient | @ping-identity/rn-oidc
OidcClient: {
    id: string;
    endSession(): Promise<boolean>;
    refresh(): Promise<Omit<Tokens, "tokenExpiry">>;
    revoke(): Promise<void>;
    token(): Promise<Omit<Tokens, "tokenExpiry">>;
    userinfo(cache?: boolean): Promise<Record<string, unknown>>;
}

Native-backed OIDC client handle.

-

Type declaration

  • id: string

    Internal native identifier for the client.

    -
  • endSession:function
    • End the current user session.

      -

      Returns Promise<boolean>

      Whether the end-session flow completed successfully.

      -
  • refresh:function
    • Force-refresh the token bundle.

      -

      Returns Promise<Omit<Tokens, "tokenExpiry">>

  • revoke:function
  • token:function
    • Retrieve the current token bundle.

      -

      Returns Promise<Omit<Tokens, "tokenExpiry">>

  • userinfo:function
    • Fetch user profile data from the userinfo endpoint.

      -

      Parameters

      • Optionalcache: boolean

        When true, reuse cached userinfo if available.

        -

      Returns Promise<Record<string, unknown>>

diff --git a/docs/oidc/types/OidcClientConfig.html b/docs/oidc/types/OidcClientConfig.html deleted file mode 100644 index dab23965c..000000000 --- a/docs/oidc/types/OidcClientConfig.html +++ /dev/null @@ -1,21 +0,0 @@ -OidcClientConfig | @ping-identity/rn-oidc

Type Alias OidcClientConfig

OidcClientConfig: Omit<OidcCoreConfig, "signOutRedirectUri"> & {
    ios?: IOSBrowserOpenOptions;
    logger?: LoggerInstance;
    nativeLogger?: NativeLoggerHandle;
    storage?: OidcStorageHandle;
}

Configuration for creating a native-backed OIDC client.

-

Type declaration

  • Optionalios?: IOSBrowserOpenOptions

    iOS-only browser configuration for OIDC web login.

    -

    Mirrors Ping iOS SDK OidcWebConfig settings. Ignored on Android.

    -
  • Optionallogger?: LoggerInstance

    Optional JavaScript logger instance.

    -

    Must be created by @react-native-pingidentity/logger (logger(...)).

    -
  • OptionalnativeLogger?: NativeLoggerHandle

    Optional native logger handle.

    -

    Must be created by @react-native-pingidentity/logger (configureLogger(...)).

    -
  • Optionalstorage?: OidcStorageHandle

    Optional storage configuration created by the storage module.

    -

    Pass the object returned by configureOidcStorage. The native layer uses -the embedded id to resolve the registered storage configuration.

    -

Values are serialized across the React Native bridge and must remain -platform-agnostic.

-

Basic configuration:

-
const client = createOidcClient({
clientId: 'client-id',
discoveryEndpoint: 'https://example.com/.well-known/openid-configuration',
redirectUri: 'com.example.app://callback',
scopes: ['openid', 'email', 'profile'],
}); -
- -

With OpenID override and logger:

-
const log = logger({ level: 'debug' });
const client = createOidcClient({
clientId: 'client-id',
redirectUri: 'com.example.app://callback',
scopes: ['openid'],
openId: {
authorizationEndpoint: 'https://issuer/authorize',
tokenEndpoint: 'https://issuer/token',
userinfoEndpoint: 'https://issuer/userinfo',
},
logger: log,
}); -
- -
diff --git a/docs/oidc/types/OidcError.html b/docs/oidc/types/OidcError.html deleted file mode 100644 index f5d26e4af..000000000 --- a/docs/oidc/types/OidcError.html +++ /dev/null @@ -1,3 +0,0 @@ -OidcError | @ping-identity/rn-oidc
OidcError: GenericError

Error payload returned when OIDC operations fail.

-

Matches the shared native/JS error contract defined in @ping-identity/rn-types.

-
diff --git a/docs/oidc/types/OidcErrorCode.html b/docs/oidc/types/OidcErrorCode.html deleted file mode 100644 index 7684ca392..000000000 --- a/docs/oidc/types/OidcErrorCode.html +++ /dev/null @@ -1,3 +0,0 @@ -OidcErrorCode | @ping-identity/rn-oidc

Type Alias OidcErrorCode

OidcErrorCode:
    | "OIDC_AUTHORIZE_ERROR"
    | "OIDC_HAS_USER_ERROR"
    | "OIDC_TOKEN_ERROR"
    | "OIDC_REFRESH_ERROR"
    | "OIDC_USERINFO_ERROR"
    | "OIDC_REVOKE_ERROR"
    | "OIDC_LOGOUT_ERROR"

Stable error codes emitted by the OIDC module.

-

Keep these in sync with the native error constants.

-
diff --git a/docs/oidc/types/OidcHookActions.html b/docs/oidc/types/OidcHookActions.html deleted file mode 100644 index 3509bc95e..000000000 --- a/docs/oidc/types/OidcHookActions.html +++ /dev/null @@ -1,18 +0,0 @@ -OidcHookActions | @ping-identity/rn-oidc

Type Alias OidcHookActions

OidcHookActions: {
    authorize: (
        options?: OidcAuthorizeOptions,
    ) => Promise<OidcAuthorizeResult>;
    clear: () => void;
    logout: () => Promise<boolean>;
    refresh: () => Promise<OidcTokenSet | null>;
    restore: () => Promise<OidcUser | null>;
    revoke: () => Promise<boolean>;
    token: () => Promise<OidcTokenSet | null>;
    userinfo: (cache?: boolean) => Promise<Record<string, unknown> | null>;
}

Actions exposed by useOidc.

-

Type declaration

  • authorize: (options?: OidcAuthorizeOptions) => Promise<OidcAuthorizeResult>

    Launch browser authorization.

    -

    When authorization fails.

    -
  • clear: () => void

    Clears transient hook state.

    -
  • logout: () => Promise<boolean>

    Logout active user.

    -

    When logout fails.

    -
  • refresh: () => Promise<OidcTokenSet | null>

    Refresh and cache token payload for the active user.

    -

    When refresh fails.

    -
  • restore: () => Promise<OidcUser | null>

    Resolve and cache current auth state from the web client.

    -

    When native user() fails.

    -
  • revoke: () => Promise<boolean>

    Revoke tokens for the active user.

    -

    When revoke fails.

    -
  • token: () => Promise<OidcTokenSet | null>

    Resolve and cache token payload for the active user.

    -

    When token retrieval fails.

    -
  • userinfo: (cache?: boolean) => Promise<Record<string, unknown> | null>

    Resolve and cache userinfo payload for the active user.

    -

    When userinfo retrieval fails.

    -

All async actions reject with OidcError when native calls fail.

-
diff --git a/docs/oidc/types/OidcHookResult.html b/docs/oidc/types/OidcHookResult.html deleted file mode 100644 index 162cdce34..000000000 --- a/docs/oidc/types/OidcHookResult.html +++ /dev/null @@ -1,2 +0,0 @@ -OidcHookResult | @ping-identity/rn-oidc

Type Alias OidcHookResult

OidcHookResult: readonly [OidcHookState, OidcHookActions]

Tuple returned by useOidc.

-
diff --git a/docs/oidc/types/OidcHookState.html b/docs/oidc/types/OidcHookState.html deleted file mode 100644 index 010c7d8f9..000000000 --- a/docs/oidc/types/OidcHookState.html +++ /dev/null @@ -1,9 +0,0 @@ -OidcHookState | @ping-identity/rn-oidc

Type Alias OidcHookState

OidcHookState: {
    authorizeResult: OidcAuthorizeResult | null;
    error: OidcError | null;
    isAuthenticated: boolean;
    isLoading: boolean;
    tokens: OidcTokenSet | null;
    user: OidcUser | null;
    userInfo: Record<string, unknown> | null;
}

Read-only state snapshot exposed by useOidc.

-

Type declaration

  • authorizeResult: OidcAuthorizeResult | null

    Last authorize result.

    -
  • error: OidcError | null

    Last hook-level error.

    -
  • isAuthenticated: boolean

    Whether an authenticated OIDC user is currently available.

    -
  • isLoading: boolean

    Whether a hook action is currently in progress.

    -
  • tokens: OidcTokenSet | null

    Last resolved token payload.

    -
  • user: OidcUser | null

    Last resolved OIDC user handle.

    -
  • userInfo: Record<string, unknown> | null

    Last resolved userinfo payload.

    -
diff --git a/docs/oidc/types/OidcOpenIdConfiguration.html b/docs/oidc/types/OidcOpenIdConfiguration.html deleted file mode 100644 index f8ca5615e..000000000 --- a/docs/oidc/types/OidcOpenIdConfiguration.html +++ /dev/null @@ -1,2 +0,0 @@ -OidcOpenIdConfiguration | @ping-identity/rn-oidc

Type Alias OidcOpenIdConfiguration

OidcOpenIdConfiguration: SharedOidcOpenIdConfiguration

OpenID configuration override for native clients.

-
diff --git a/docs/oidc/types/OidcProviderProps.html b/docs/oidc/types/OidcProviderProps.html deleted file mode 100644 index 0b8072026..000000000 --- a/docs/oidc/types/OidcProviderProps.html +++ /dev/null @@ -1,4 +0,0 @@ -OidcProviderProps | @ping-identity/rn-oidc

Type Alias OidcProviderProps

OidcProviderProps: { children: React.ReactNode; client: OidcWebClient }

Props for OidcProvider.

-

Type declaration

  • children: React.ReactNode

    Descendant React nodes.

    -
  • client: OidcWebClient

    OIDC web client shared across descendants.

    -
diff --git a/docs/oidc/types/OidcUser.html b/docs/oidc/types/OidcUser.html deleted file mode 100644 index 6fcaee1d3..000000000 --- a/docs/oidc/types/OidcUser.html +++ /dev/null @@ -1,9 +0,0 @@ -OidcUser | @ping-identity/rn-oidc
OidcUser: {
    logout(): Promise<void>;
    refresh(): Promise<Omit<Tokens, "tokenExpiry">>;
    revoke(): Promise<void>;
    token(): Promise<Omit<Tokens, "tokenExpiry">>;
    userinfo(cache?: boolean): Promise<Record<string, unknown>>;
}

Authenticated user handle scoped to an OIDC web client.

-

Type declaration

  • logout:function
    • Logout the current user session.

      -

      Returns Promise<void>

      Mirrors native OidcUser.logout() which does not return a value.

      -
  • refresh:function
    • Force-refresh the token bundle.

      -

      Returns Promise<Omit<Tokens, "tokenExpiry">>

  • revoke:function
  • token:function
    • Retrieve the current token bundle.

      -

      Returns Promise<Omit<Tokens, "tokenExpiry">>

  • userinfo:function
    • Fetch user profile data from the userinfo endpoint.

      -

      Parameters

      • Optionalcache: boolean

        When true, reuse cached userinfo if available.

        -

      Returns Promise<Record<string, unknown>>

diff --git a/docs/oidc/types/OidcWebClient.html b/docs/oidc/types/OidcWebClient.html deleted file mode 100644 index 831b5f173..000000000 --- a/docs/oidc/types/OidcWebClient.html +++ /dev/null @@ -1,8 +0,0 @@ -OidcWebClient | @ping-identity/rn-oidc

Type Alias OidcWebClient

OidcWebClient: {
    id: string;
    authorize(options?: OidcAuthorizeOptions): Promise<OidcAuthorizeResult>;
    user(): Promise<null | OidcUser>;
}

Web-capable OIDC client handle.

-

Type declaration

diff --git a/eslint.config.mjs b/eslint.config.mjs index d1310d78c..5351b8741 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -7,7 +7,7 @@ import js from "@eslint/js"; import globals from "globals"; import tseslint from "typescript-eslint"; -import pluginReact from "eslint-plugin-react"; +import reactHooks from "eslint-plugin-react-hooks"; import { defineConfig } from "eslint/config"; export default defineConfig([ @@ -27,16 +27,36 @@ export default defineConfig([ "**/vendor/**", ], }, - { - files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], - plugins: { - js - }, - extends: ["js/recommended"], - languageOptions: { - globals: globals.node - } -}, + { + files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], + plugins: { + js, + }, + extends: ["js/recommended"], + languageOptions: { + globals: globals.node, + }, + }, tseslint.configs.recommended, - pluginReact.configs.flat.recommended, + { + files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], + rules: { + "@typescript-eslint/no-unused-vars": [ + "error", + { + varsIgnorePattern: "^_", + argsIgnorePattern: "^_", + caughtErrorsIgnorePattern: "^_", + destructuredArrayIgnorePattern: "^_", + }, + ], + }, + }, + { + files: ["**/*.{js,mjs,cjs,ts,mts,cts,jsx,tsx}"], + plugins: { + "react-hooks": reactHooks, + }, + rules: reactHooks.configs.recommended.rules, + }, ]); diff --git a/package.json b/package.json index 5ca4076c8..b9d9c846f 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "@types/jest": "^29.5.14", "babel-jest": "^29.7.0", "eslint": "^10.0.1", - "eslint-plugin-react": "^7.37.5", + "eslint-plugin-react-hooks": "^7.0.1", "globals": "^17.3.0", "jest": "^29.7.0", "jest-junit": "^16.0.0", diff --git a/packages/browser/README.md b/packages/browser/README.md index 01266d7ec..e75afd6f4 100644 --- a/packages/browser/README.md +++ b/packages/browser/README.md @@ -30,6 +30,14 @@ yarn add @ping-identity/rn-browser cd ios && pod install ``` +Optional integration packages: + +```bash +yarn add @ping-identity/rn-logger +``` + +- `@ping-identity/rn-logger`: optional JS/native logger integration. + ## How to Use the SDK ### Configure (Android only) @@ -72,6 +80,30 @@ android { } ``` +### Configure logging (optional) + +If you install the logger package, pass a JS logger instance per call via `BrowserLoggerOptions`. +The logger must be created via `@ping-identity/rn-logger`. +If the logger package is not installed/configured, omit the logger option. + +```ts +import { open, configureBrowser, resetBrowser } from '@ping-identity/rn-browser'; +import { logger } from '@ping-identity/rn-logger'; + +const jsLogger = logger({ level: 'debug' }); + +// Pass as the last argument to any browser call +const result = await open( + 'https://example.com', + { callbackUrlScheme: 'com.example.app' }, + { logger: jsLogger }, +); + +// Also supported on configureBrowser and resetBrowser +configureBrowser({ android: { customTabs: { showTitle: true } } }, { logger: jsLogger }); +resetBrowser({ logger: jsLogger }); +``` + ### Open a browser session ```ts @@ -115,7 +147,6 @@ try { ## TODO - Add an iOS test runner target to execute module unit tests. -- Make logger integration optional (align with OIDC/Storage optional package pattern). ## License diff --git a/packages/browser/RNPingBrowser.podspec b/packages/browser/RNPingBrowser.podspec index 3398d1828..8bd4a266e 100644 --- a/packages/browser/RNPingBrowser.podspec +++ b/packages/browser/RNPingBrowser.podspec @@ -42,6 +42,9 @@ Pod::Spec.new do |s| # Native Ping SDK dependency s.dependency 'PingBrowser', '1.3.1' s.dependency 'RNPingCore' + # TODO: Remove RNPingLogger once PingBrowser exposes BrowserLauncher.logger as public. + # At that point, logger resolution can be wired through CoreRuntime.loggerRegistry + # (see RNPingBrowserCommon.swift TODO) and this direct dependency is no longer needed. s.dependency 'RNPingLogger' install_modules_dependencies(s) diff --git a/packages/browser/android/build.gradle b/packages/browser/android/build.gradle index eb9709eb3..cbf8c2261 100644 --- a/packages/browser/android/build.gradle +++ b/packages/browser/android/build.gradle @@ -90,7 +90,6 @@ dependencies { implementation("com.pingidentity.sdks:logger:2.0.0-beta1") implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.9.0" implementation(project(":ping-identity_rn-core")) - implementation(project(":ping-identity_rn-logger")) testImplementation "junit:junit:4.13.2" testImplementation "androidx.test:core:1.6.1" testImplementation "org.jetbrains.kotlinx:kotlinx-coroutines-test:1.9.0" diff --git a/packages/browser/android/src/main/java/com/pingidentity/rnbrowser/RNPingBrowserCommon.kt b/packages/browser/android/src/main/java/com/pingidentity/rnbrowser/RNPingBrowserCommon.kt index 20e328567..eb0d37887 100644 --- a/packages/browser/android/src/main/java/com/pingidentity/rnbrowser/RNPingBrowserCommon.kt +++ b/packages/browser/android/src/main/java/com/pingidentity/rnbrowser/RNPingBrowserCommon.kt @@ -22,10 +22,11 @@ import com.pingidentity.browser.BrowserLauncher import com.pingidentity.browser.BrowserCanceledException import com.pingidentity.logger.Logger import com.pingidentity.logger.NONE +import com.pingidentity.rncore.CoreRuntime import com.pingidentity.rncore.error.ErrorType import com.pingidentity.rncore.error.GenericError import com.pingidentity.rncore.error.reject -import com.pingidentity.rnlogger.RNPingLoggerCommon +import com.pingidentity.rncore.logger.LoggerHandleContract import java.net.MalformedURLException import java.net.URI import java.net.URL @@ -162,7 +163,7 @@ object RNPingBrowserCommon { } else { null } - val resolvedLogger = RNPingLoggerCommon.resolveLogger(loggerId) + val resolvedLogger = resolveLoggerFromCore(loggerId) BrowserLauncher.logger = resolvedLogger ?: Logger.NONE val callbackUrlScheme = if (options.hasKey("callbackUrlScheme")) { @@ -258,6 +259,18 @@ object RNPingBrowserCommon { // Android BrowserLauncher does not expose a public reset API. } + /** + * Resolve a native logger from the shared Core logger registry. + * + * @param id Logger handle identifier from JS. + * @return Native logger instance, or null when missing/invalid. + */ + private fun resolveLoggerFromCore(id: String?): Logger? { + if (id.isNullOrBlank()) return null + val handle = CoreRuntime.loggerRegistry.resolve(id) as? LoggerHandleContract ?: return null + return handle.nativeLogger as? Logger + } + /** * Validate whether the target browser package is installed. */ diff --git a/packages/browser/package.json b/packages/browser/package.json index b7cb64fb3..2f304260a 100644 --- a/packages/browser/package.json +++ b/packages/browser/package.json @@ -34,7 +34,6 @@ }, "dependencies": { "@ping-identity/rn-core": "^0.1.0", - "@ping-identity/rn-logger": "^0.1.0", "@ping-identity/rn-types": "^0.1.0" }, "react-native-builder-bob": { diff --git a/packages/browser/src/NativeRNPingBrowser.ts b/packages/browser/src/NativeRNPingBrowser.ts index 5c65212cb..0bfec6597 100644 --- a/packages/browser/src/NativeRNPingBrowser.ts +++ b/packages/browser/src/NativeRNPingBrowser.ts @@ -50,7 +50,8 @@ export interface Spec extends TurboModule { */ open( url: string, - options: Object + // eslint-disable-next-line @typescript-eslint/no-wrapper-object-types + options: Object, ): Promise; } @@ -72,6 +73,6 @@ export function getNativeModule(): Spec { '[@ping-identity/rn-browser] Native module RNPingBrowser not found.\n' + 'Ensure the library is linked correctly and the app has been rebuilt.\n' + 'Available NativeModules: ' + - JSON.stringify(Object.keys(NativeModules)) + JSON.stringify(Object.keys(NativeModules)), ); } diff --git a/packages/browser/src/__tests__/index.test.tsx b/packages/browser/src/__tests__/index.test.tsx index 3afd442c2..947b311af 100644 --- a/packages/browser/src/__tests__/index.test.tsx +++ b/packages/browser/src/__tests__/index.test.tsx @@ -15,7 +15,7 @@ const createReactNativeMock = (overrides: Partial) => { const base: ReactNativeMock = { NativeModules: {}, Platform: { OS: 'android' }, - TurboModuleRegistry: { get: jest.fn(() => ({ })) }, + TurboModuleRegistry: { get: jest.fn(() => ({})) }, }; return { ...base, ...overrides }; @@ -32,17 +32,6 @@ const loadModule = async ({ }) => { jest.resetModules(); - jest.doMock('@ping-identity/rn-logger', () => ({ - logger: jest.fn(() => ({ - nativeHandle: { id: 'native-none-id' }, - changeLevel: jest.fn(), - error: jest.fn(), - warn: jest.fn(), - info: jest.fn(), - debug: jest.fn(), - })), - })); - const get = jest.fn(() => turboModule); jest.doMock('react-native', () => @@ -50,10 +39,10 @@ const loadModule = async ({ Platform: { OS: platform }, NativeModules: nativeModule ?? {}, TurboModuleRegistry: { get }, - }) + }), ); - return require('../index'); + return import('../index'); }; describe('browser package', () => { @@ -134,7 +123,7 @@ describe('browser package', () => { }); expect(() => resetBrowser()).toThrow( - '[@ping-identity/rn-browser] Native module RNPingBrowser not found.' + '[@ping-identity/rn-browser] Native module RNPingBrowser not found.', ); }); @@ -150,7 +139,7 @@ describe('browser package', () => { expect(open).toHaveBeenCalledWith('https://example.com', { ...options, - loggerId: 'native-none-id', + loggerId: undefined, }); expect(result).toEqual({ type: 'cancel' }); }); @@ -164,14 +153,14 @@ describe('browser package', () => { const options = { callbackUrlScheme: 'com.example.app', - ios: { browserType: 'authSession' }, + ios: { browserType: 'authSession' as const }, }; await openBrowser('https://example.com', options); expect(open).toHaveBeenCalledWith('https://example.com', { ...options, - loggerId: 'native-none-id', + loggerId: undefined, }); }); @@ -184,33 +173,17 @@ describe('browser package', () => { const options = { callbackUrlScheme: 'com.example.app', - ios: { browserType: 'ephemeralAuthSession', browserMode: 'login' }, + ios: { + browserType: 'ephemeralAuthSession' as const, + browserMode: 'login' as const, + }, }; await openBrowser('https://example.com', options); expect(open).toHaveBeenCalledWith('https://example.com', { ...options, - loggerId: 'native-none-id', - }); - }); - - it('prefers provided nativeLogger for Android open calls', async () => { - const open = jest.fn(() => Promise.resolve({ type: 'cancel' })); - const { open: openBrowser } = await loadModule({ - platform: 'android', - nativeModule: { RNPingBrowserClassic: { open } }, - }); - - await openBrowser( - 'https://example.com', - { callbackUrlScheme: 'com.example.app' }, - { nativeLogger: { id: 'explicit-native-id' } } - ); - - expect(open).toHaveBeenCalledWith('https://example.com', { - callbackUrlScheme: 'com.example.app', - loggerId: 'explicit-native-id', + loggerId: undefined, }); }); @@ -235,7 +208,7 @@ describe('browser package', () => { }); await expect( - openBrowser('https://example.com', { callbackUrlScheme: 'com.app' }) + openBrowser('https://example.com', { callbackUrlScheme: 'com.app' }), ).resolves.toEqual({ type: 'cancel' }); expect(open).toHaveBeenCalledTimes(1); }); @@ -246,9 +219,9 @@ describe('browser package', () => { }); expect(() => - openBrowser('https://example.com', { callbackUrlScheme: 'com.app' }) + openBrowser('https://example.com', { callbackUrlScheme: 'com.app' }), ).toThrow( - '[@ping-identity/rn-browser] Native module RNPingBrowser not found.' + '[@ping-identity/rn-browser] Native module RNPingBrowser not found.', ); }); @@ -259,7 +232,7 @@ describe('browser package', () => { }); expect(() => - openBrowser('https://example.com', { callbackUrlScheme: 'com.app' }) - ).toThrow('Available NativeModules: [\"SomeOtherModule\"]'); + openBrowser('https://example.com', { callbackUrlScheme: 'com.app' }), + ).toThrow('Available NativeModules: ["SomeOtherModule"]'); }); }); diff --git a/packages/browser/src/index.tsx b/packages/browser/src/index.tsx index d840331a8..eae789bb9 100644 --- a/packages/browser/src/index.tsx +++ b/packages/browser/src/index.tsx @@ -7,8 +7,7 @@ import { Platform } from 'react-native'; import { getNativeModule } from './NativeRNPingBrowser'; -import { logger as createLogger } from '@ping-identity/rn-logger'; -import type { LoggerInstance } from '@ping-identity/rn-logger'; +import type { LoggerInstance } from '@ping-identity/rn-types'; import type { BrowserConfig, @@ -18,32 +17,26 @@ import type { } from './types'; /** - * Cached default logger used when callers do not provide one. + * No-op logger used when callers do not provide one. */ -let defaultLoggerInstance: LoggerInstance | null = null; - -/** - * Lazily initialize and return the default logger instance. - */ -const getDefaultLogger = (): LoggerInstance => { - if (!defaultLoggerInstance) { - defaultLoggerInstance = createLogger({ level: 'none' }); - } - return defaultLoggerInstance; +const noopLogger: LoggerInstance = { + nativeHandle: { id: '' }, + changeLevel: () => {}, + error: () => {}, + warn: () => {}, + info: () => {}, + debug: () => {}, }; /** * Resolve JS logger instance and native logger identifier for bridge calls. */ const resolveLogger = ( - options?: BrowserLoggerOptions + options?: BrowserLoggerOptions, ): { logger: LoggerInstance; loggerId?: string } => { - const logger = options?.logger ?? getDefaultLogger(); - const loggerId = - options?.nativeLogger?.id ?? - logger.nativeHandle?.id ?? - getDefaultLogger().nativeHandle?.id; - + const logger = options?.logger ?? noopLogger; + const rawLoggerId = logger.nativeHandle?.id; + const loggerId = rawLoggerId?.trim() ? rawLoggerId : undefined; return { logger, loggerId }; }; @@ -57,7 +50,7 @@ const resolveLogger = ( */ export function configureBrowser( config: BrowserConfig, - options?: BrowserLoggerOptions + options?: BrowserLoggerOptions, ): void { const { logger, loggerId } = resolveLogger(options); logger.debug(`Browser configure requested ${JSON.stringify({ loggerId })}`); @@ -100,7 +93,7 @@ export function resetBrowser(options?: BrowserLoggerOptions): void { export function open( url: string, options: BrowserOpenOptions, - loggerOptions?: BrowserLoggerOptions + loggerOptions?: BrowserLoggerOptions, ): Promise { const { logger, loggerId } = resolveLogger(loggerOptions); logger.info('Browser open requested'); @@ -110,7 +103,7 @@ export function open( callbackUrlScheme: options.callbackUrlScheme, redirectUri: options.redirectUri, loggerId, - })}` + })}`, ); const nativeOptions = { ...options, loggerId }; diff --git a/packages/browser/src/types/browser.types.ts b/packages/browser/src/types/browser.types.ts index daa858ea3..67f7ae10c 100644 --- a/packages/browser/src/types/browser.types.ts +++ b/packages/browser/src/types/browser.types.ts @@ -8,11 +8,8 @@ import type { GenericError, IOSBrowserOpenOptions, -} from '@ping-identity/rn-types'; -import type { LoggerInstance, - NativeLoggerHandle, -} from '@ping-identity/rn-logger'; +} from '@ping-identity/rn-types'; /** * Result of a browser launch. @@ -132,6 +129,7 @@ export type AndroidBrowserConfig = { /** * iOS-only global configuration. */ +// eslint-disable-next-line @typescript-eslint/no-empty-object-type export type IOSBrowserConfig = {}; /** @@ -144,20 +142,10 @@ export type BrowserConfig = { /** * Optional logger configuration for browser native calls. - * - * @remarks - * Mirrors OIDC and device-profile logger behavior: - * - `nativeLogger` has highest precedence for native logger application. - * - `logger.nativeHandle` is used when `nativeLogger` is not provided. */ export type BrowserLoggerOptions = { /** * Optional JavaScript logger instance. */ logger?: LoggerInstance; - - /** - * Optional native logger handle. - */ - nativeLogger?: NativeLoggerHandle; }; diff --git a/packages/browser/tsconfig.build.json b/packages/browser/tsconfig.build.json index 608e2ea18..6f0b3aea7 100644 --- a/packages/browser/tsconfig.build.json +++ b/packages/browser/tsconfig.build.json @@ -1,4 +1,11 @@ { "extends": "./tsconfig", + "compilerOptions": { + "rootDir": ".", + "paths": { + "@ping-identity/rn-browser": ["./src/index"], + "@ping-identity/rn-types": ["../types/lib/typescript/src/index"] + } + }, "exclude": ["example", "lib", "**/__tests__/**"] } diff --git a/packages/browser/tsconfig.json b/packages/browser/tsconfig.json index c3da7321c..8d6b1932e 100644 --- a/packages/browser/tsconfig.json +++ b/packages/browser/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "rootDir": ".", "paths": { - "@ping-identity/rn-browser": ["./src/index"] + "@ping-identity/rn-browser": ["./src/index"], + "@ping-identity/rn-types": ["../types/src/index"] }, "allowUnreachableCode": false, "allowUnusedLabels": false, @@ -26,5 +26,6 @@ "strict": true, "target": "ESNext", "verbatimModuleSyntax": true - } + }, + "exclude": ["lib", "**/__tests__/**"] } diff --git a/packages/device-id/README.md b/packages/device-id/README.md index fcf518de5..154caca44 100644 --- a/packages/device-id/README.md +++ b/packages/device-id/README.md @@ -46,23 +46,6 @@ const deviceId = await getDeviceId(); console.log('Device ID:', deviceId); ``` -### Optional: pass logger options - -The Device ID call accepts the same logger pattern used across the SDK modules. - -```ts -import { getDeviceId } from '@ping-identity/rn-device-id'; -import { logger, configureLogger } from '@ping-identity/rn-logger'; - -const log = logger({ level: 'debug' }); -const nativeLogger = configureLogger({ level: 'info' }); - -const deviceId = await getDeviceId({ - logger: log, - nativeLogger, -}); -``` - ### API reference ```ts diff --git a/packages/device-id/jest.config.js b/packages/device-id/jest.config.js index b64acb504..f0e9afd4c 100644 --- a/packages/device-id/jest.config.js +++ b/packages/device-id/jest.config.js @@ -10,6 +10,8 @@ module.exports = { transform: { '^.+\\.[jt]sx?$': 'babel-jest', }, + modulePathIgnorePatterns: ['/lib/'], + watchPathIgnorePatterns: ['/lib/'], reporters: [ 'default', ['jest-junit', { diff --git a/packages/device-id/src/NativeRNPingDeviceId.ts b/packages/device-id/src/NativeRNPingDeviceId.ts index 10d3b119c..c29caa6d0 100644 --- a/packages/device-id/src/NativeRNPingDeviceId.ts +++ b/packages/device-id/src/NativeRNPingDeviceId.ts @@ -9,19 +9,19 @@ import { NativeModules, TurboModuleRegistry } from 'react-native'; /** * Native module specification for RNPingDeviceId. - * + * * Defines the interface contract for the native device identifier module. * Extends TurboModule for New Architecture (Fabric) support. - * + * * This interface is implemented by native code on both iOS and Android platforms, * providing secure device identification capabilities. - * + * * @interface */ export interface Spec extends TurboModule { /** * Returns the default secure device identifier as determined by the native platform. - * + * * The default identifier is platform-specific: * - **Android**: Uses KeyStore-generated RSA key pair (cryptographically secure, hardware-backed when available) * - Generates new ID on app reinstall (KeyStore entries generally removed on uninstall) @@ -32,10 +32,10 @@ export interface Spec extends TurboModule { * - Only deleted on factory reset * - Can be shared across apps from same developer via Keychain Access Groups * - Automatically migrates legacy FRAuth identifiers - * + * * This is the recommended method for most use cases as it provides * cryptographically secure identification. - * + * * @returns A promise that resolves to the default device identifier string */ getDefaultDeviceId(): Promise; @@ -59,6 +59,6 @@ export function getNativeModule(): Spec { '[@ping-identity/rn-device-id] Native module RNPingDeviceId not found.\n' + 'Ensure the library is linked correctly and the app has been rebuilt.\n' + 'Available NativeModules: ' + - JSON.stringify(Object.keys(NativeModules)) + JSON.stringify(Object.keys(NativeModules)), ); } diff --git a/packages/device-id/src/__tests__/native-module.test.tsx b/packages/device-id/src/__tests__/native-module.test.tsx index 4b70e92e3..bf44c8345 100644 --- a/packages/device-id/src/__tests__/native-module.test.tsx +++ b/packages/device-id/src/__tests__/native-module.test.tsx @@ -33,10 +33,10 @@ const loadModule = async ({ createReactNativeMock({ NativeModules: nativeModule ?? {}, TurboModuleRegistry: { get }, - }) + }), ); - return require('../index'); + return import('../index'); }; describe('device-id native module wiring', () => { @@ -64,7 +64,7 @@ describe('device-id native module wiring', () => { const { getDeviceId } = await loadModule({}); await expect(getDeviceId()).rejects.toThrow( - '[@ping-identity/rn-device-id] Native module RNPingDeviceId not found.' + '[@ping-identity/rn-device-id] Native module RNPingDeviceId not found.', ); }); @@ -74,7 +74,7 @@ describe('device-id native module wiring', () => { }); await expect(getDeviceId()).rejects.toThrow( - 'Available NativeModules: [\"SomeOtherModule\"]' + 'Available NativeModules: ["SomeOtherModule"]', ); }); }); diff --git a/packages/device-id/src/index.tsx b/packages/device-id/src/index.tsx index 5f8b447a9..8e131feb6 100644 --- a/packages/device-id/src/index.tsx +++ b/packages/device-id/src/index.tsx @@ -8,38 +8,38 @@ import { getNativeModule } from './NativeRNPingDeviceId'; /** * Returns the default secure device identifier as determined by the native platform. - * + * * The default identifier is platform-specific and provides a high level of * cryptographic security: * - **Android**: Uses KeyStore-generated RSA key pair, cryptographically secure and hardware-backed when available * - **iOS**: Uses Keychain-backed unique identifier with secure enclave when available - * + * * This is the recommended method for most use cases. The identifier: - * + * * **Android persistence:** * - Generates a new ID when app is reinstalled (KeyStore entries are generally removed on uninstall) * - Remains consistent if KeyStore entries persist, but this is not typical * - Changes when KeyStore entries are cleared (e.g., via app data clear) - * + * * **iOS persistence:** * - Persists across app uninstalls and reinstalls (Keychain is not cleared when app is deleted) * - Persists when device is restored from encrypted iCloud or local backup * - Permanently deleted only on factory reset (entire device storage wiped) * - Can be shared across apps from the same developer using Keychain Access Groups * - Automatically migrates and preserves legacy identifiers from FRAuth, maintaining the original SHA-1 hash - * + * * - Is unique per app and device * - Uses platform-specific secure storage mechanisms - * + * * @returns A promise that resolves to the default device identifier string * * @remarks * Promise rejections use {@link DeviceIdError}. - * + * * @example * ```typescript * import { getDeviceId } from '@ping-identity/rn-device-id'; - * + * * // Get secure device identifier for authentication * const deviceId = await getDeviceId(); * console.log('Device ID:', deviceId); diff --git a/packages/device-id/tsconfig.build.json b/packages/device-id/tsconfig.build.json index 3c0636adf..69c2a208f 100644 --- a/packages/device-id/tsconfig.build.json +++ b/packages/device-id/tsconfig.build.json @@ -1,4 +1,11 @@ { "extends": "./tsconfig", - "exclude": ["example", "lib"] + "compilerOptions": { + "rootDir": ".", + "paths": { + "@ping-identity/rn-device-id": ["./src/index"], + "@ping-identity/rn-types": ["../types/lib/typescript/src/index"] + } + }, + "exclude": ["example", "lib", "**/__tests__/**"] } diff --git a/packages/device-id/tsconfig.json b/packages/device-id/tsconfig.json index 3abe34985..5ed6d89cc 100644 --- a/packages/device-id/tsconfig.json +++ b/packages/device-id/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "rootDir": ".", "paths": { - "@ping-identity/rn-device-id": ["./src/index"] + "@ping-identity/rn-device-id": ["./src/index"], + "@ping-identity/rn-types": ["../types/src/index"] }, "allowUnreachableCode": false, "allowUnusedLabels": false, @@ -26,5 +26,6 @@ "strict": true, "target": "ESNext", "verbatimModuleSyntax": true - } + }, + "exclude": ["lib", "**/__tests__/**"] } diff --git a/packages/device-profile/README.md b/packages/device-profile/README.md index 4b6637f09..97620fd8d 100644 --- a/packages/device-profile/README.md +++ b/packages/device-profile/README.md @@ -20,6 +20,7 @@ collection, permissions, and payload formatting. - [Getting started](#getting-started) - [Built-in collectors](#built-in-collectors) - [Android setup](#android-setup) +- [Configure logging](#configure-logging-optional) - [Journey integration](#journey-integration) - [API reference](#api-reference) - [Errors](#errors) @@ -48,6 +49,14 @@ If you use CocoaPods, install pods after adding the package: cd ios && pod install ``` +Optional integration packages: + +```bash +yarn add @ping-identity/rn-logger +``` + +- `@ping-identity/rn-logger`: optional JS/native logger integration. + ### Basic usage (outside Journey) ```ts @@ -172,6 +181,24 @@ app's `Info.plist` so iOS can prompt the user for permission. TODO: Re-check `@MainActor` usage in Device Profile iOS paths for potential UI-thread bottlenecks. +## Configure logging (optional) + +If you install the logger package, pass a JS logger instance per call via `DeviceProfileLoggerOptions`. +The logger must be created via `@ping-identity/rn-logger`. +If the logger package is not installed/configured, omit the logger option. + +```ts +import { collectDeviceProfile, collectDeviceProfileForJourney } from '@ping-identity/rn-device-profile'; +import { logger } from '@ping-identity/rn-logger'; + +const jsLogger = logger({ level: 'debug' }); + +// Pass as the last argument to any device profile call +await collectDeviceProfile(['platform', 'hardware'], { logger: jsLogger }); + +await collectDeviceProfileForJourney(journey, ['platform', 'hardware'], { logger: jsLogger }); +``` + ## Journey integration ```ts @@ -201,17 +228,20 @@ result object describing success. Failures reject with a shared `GenericError`. import type { DeviceProfile, DeviceProfileCollector, + DeviceProfileLoggerOptions, DeviceProfileJourneyResult, } from '@ping-identity/rn-device-profile'; import type { JourneyInstance } from '@ping-identity/rn-types'; function collectDeviceProfile( - collectors: DeviceProfileCollector[] + collectors: DeviceProfileCollector[], + options?: DeviceProfileLoggerOptions ): Promise; function collectDeviceProfileForJourney( journey: JourneyInstance, - collectors: DeviceProfileCollector[] + collectors: DeviceProfileCollector[], + options?: DeviceProfileLoggerOptions ): Promise; ``` diff --git a/packages/device-profile/android/src/main/java/com/pingidentity/rndeviceprofile/RNPingDeviceProfileCommon.kt b/packages/device-profile/android/src/main/java/com/pingidentity/rndeviceprofile/RNPingDeviceProfileCommon.kt index 7a2b20edc..5d93cc2a6 100644 --- a/packages/device-profile/android/src/main/java/com/pingidentity/rndeviceprofile/RNPingDeviceProfileCommon.kt +++ b/packages/device-profile/android/src/main/java/com/pingidentity/rndeviceprofile/RNPingDeviceProfileCommon.kt @@ -118,7 +118,6 @@ object RNPingDeviceProfileCommon { } else { deviceCollectors.collect() } - Log.d("Device Profile", "metadata collection succeeded") val bridgePayload = when (jsonElement) { is JsonObject -> JsonBridgeMapper.encodeJsonObject(jsonElement) else -> JsonBridgeMapper.encodeJsonElement(jsonElement) diff --git a/packages/device-profile/jest.config.js b/packages/device-profile/jest.config.js index 41136220f..88071ab56 100644 --- a/packages/device-profile/jest.config.js +++ b/packages/device-profile/jest.config.js @@ -11,6 +11,8 @@ module.exports = { transform: { '^.+\\.[jt]sx?$': 'babel-jest', }, + modulePathIgnorePatterns: ['/lib/'], + watchPathIgnorePatterns: ['/lib/'], reporters: [ 'default', ['jest-junit', { diff --git a/packages/device-profile/src/NativeRNPingDeviceProfile.ts b/packages/device-profile/src/NativeRNPingDeviceProfile.ts index 35c935d4b..50fbd6edf 100644 --- a/packages/device-profile/src/NativeRNPingDeviceProfile.ts +++ b/packages/device-profile/src/NativeRNPingDeviceProfile.ts @@ -5,7 +5,11 @@ * of the MIT license. See the LICENSE file for details. */ -import { NativeModules, TurboModuleRegistry, type TurboModule } from 'react-native'; +import { + NativeModules, + TurboModuleRegistry, + type TurboModule, +} from 'react-native'; import type { DeviceProfile, DeviceProfileCollector, @@ -26,7 +30,7 @@ export interface Spec extends TurboModule { * Rejects with a `DeviceProfileError` on failure. */ collectDeviceProfile( - collectors: DeviceProfileCollector[] + collectors: DeviceProfileCollector[], ): Promise; /** @@ -38,7 +42,7 @@ export interface Spec extends TurboModule { collectDeviceProfileForJourney( journeyId: string, collectors: DeviceProfileCollector[], - loggerId?: string + loggerId?: string, ): Promise; } @@ -60,7 +64,7 @@ export function getNativeModule(): Spec { '[@ping-identity/rn-device-profile] Native module RNPingDeviceProfile not found.\n' + 'Ensure the library is linked correctly and the app has been rebuilt.\n' + 'Available NativeModules: ' + - JSON.stringify(Object.keys(NativeModules)) + JSON.stringify(Object.keys(NativeModules)), ); } diff --git a/packages/device-profile/src/__tests__/index.test.tsx b/packages/device-profile/src/__tests__/index.test.tsx index efc03c3e4..2377d40a9 100644 --- a/packages/device-profile/src/__tests__/index.test.tsx +++ b/packages/device-profile/src/__tests__/index.test.tsx @@ -45,16 +45,16 @@ const loadModule = async ({ createReactNativeMock({ NativeModules: nativeModule ?? {}, TurboModuleRegistry: { get }, - }) + }), ); - return require('../index'); + return import('../index'); }; describe('device-profile package', () => { it('collects a device profile via the classic native module', async () => { const collectDeviceProfile = jest.fn(() => - Promise.resolve({ hardware: { model: 'mock' } }) + Promise.resolve({ hardware: { model: 'mock' } }), ); const { collectDeviceProfile: collect } = await loadModule({ nativeModule: { RNPingDeviceProfileClassic: { collectDeviceProfile } }, @@ -69,7 +69,7 @@ describe('device-profile package', () => { it('collects a device profile for a Journey instance', async () => { const collectDeviceProfileForJourney = jest.fn(() => - Promise.resolve({ type: 'success' }) + Promise.resolve({ type: 'success' }), ); const { collectDeviceProfileForJourney: collectForJourney } = await loadModule({ @@ -88,14 +88,14 @@ describe('device-profile package', () => { expect(collectDeviceProfileForJourney).toHaveBeenCalledWith( 'journey-123', collectors, - undefined + undefined, ); expect(result).toEqual({ type: 'success' }); }); it('uses the provided JS logger handle for Journey collection options', async () => { const collectDeviceProfileForJourney = jest.fn(() => - Promise.resolve({ type: 'success' }) + Promise.resolve({ type: 'success' }), ); const { collectDeviceProfileForJourney: collectForJourney } = await loadModule({ @@ -115,34 +115,7 @@ describe('device-profile package', () => { expect(collectDeviceProfileForJourney).toHaveBeenCalledWith( 'journey-logger-test', ['hardware'], - 'logger-from-options' - ); - }); - - it('prefers nativeLogger over logger.nativeHandle for Journey collection', async () => { - const collectDeviceProfileForJourney = jest.fn(() => - Promise.resolve({ type: 'success' }) - ); - const { collectDeviceProfileForJourney: collectForJourney } = - await loadModule({ - nativeModule: { - RNPingDeviceProfileClassic: { collectDeviceProfileForJourney }, - }, - }); - - const journey: JourneyInstance = { - getId: jest.fn(() => Promise.resolve('journey-native-logger-test')), - }; - - await collectForJourney(journey, ['network'], { - logger: createJsLogger('logger-from-options'), - nativeLogger: { id: 'explicit-native-logger' }, - }); - - expect(collectDeviceProfileForJourney).toHaveBeenCalledWith( - 'journey-native-logger-test', - ['network'], - 'explicit-native-logger' + 'logger-from-options', ); }); @@ -170,14 +143,14 @@ describe('device-profile package', () => { }; await expect(collectForJourney(journey, ['platform'])).rejects.toThrow( - 'journey failure' + 'journey failure', ); expect(collectDeviceProfileForJourney).not.toHaveBeenCalled(); }); it('uses TurboModule when New Architecture is enabled', async () => { const collectDeviceProfile = jest.fn(() => - Promise.resolve({ network: { ip: '127.0.0.1' } }) + Promise.resolve({ network: { ip: '127.0.0.1' } }), ); const { collectDeviceProfile: collect } = await loadModule({ turboModule: { collectDeviceProfile }, @@ -190,7 +163,7 @@ describe('device-profile package', () => { it('falls back to classic module when TurboModule is missing', async () => { const collectDeviceProfile = jest.fn(() => - Promise.resolve({ platform: { os: 'ios' } }) + Promise.resolve({ platform: { os: 'ios' } }), ); const { collectDeviceProfile: collect } = await loadModule({ nativeModule: { RNPingDeviceProfileClassic: { collectDeviceProfile } }, @@ -203,7 +176,7 @@ describe('device-profile package', () => { it('throws when called without a native module', async () => { await expect(loadModule({})).rejects.toThrow( - '[@ping-identity/rn-device-profile] Native module RNPingDeviceProfile not found.' + '[@ping-identity/rn-device-profile] Native module RNPingDeviceProfile not found.', ); }); @@ -211,9 +184,7 @@ describe('device-profile package', () => { await expect( loadModule({ nativeModule: { SomeOtherModule: {} }, - }) - ).rejects.toThrow( - 'Available NativeModules: [\"SomeOtherModule\"]' - ); + }), + ).rejects.toThrow('Available NativeModules: ["SomeOtherModule"]'); }); }); diff --git a/packages/device-profile/src/index.tsx b/packages/device-profile/src/index.tsx index 31264cffa..eabd4d17c 100644 --- a/packages/device-profile/src/index.tsx +++ b/packages/device-profile/src/index.tsx @@ -16,42 +16,26 @@ import type { } from './types'; /** - * Cached default logger for device-profile operations. + * No-op logger used when callers do not provide one. */ -let defaultLoggerInstance: LoggerInstance | null = null; - -const createNoopLogger = (): LoggerInstance => ({ +const noopLogger: LoggerInstance = { nativeHandle: { id: '' }, changeLevel: () => {}, error: () => {}, warn: () => {}, info: () => {}, debug: () => {}, -}); - -/** - * Lazily initializes a default logger instance. - */ -const getDefaultLogger = (): LoggerInstance => { - if (!defaultLoggerInstance) { - defaultLoggerInstance = createNoopLogger(); - } - return defaultLoggerInstance; }; /** - * Resolves JS logger instance and native logger identifier for bridge calls. + * Resolve JS logger instance and native logger identifier for bridge calls. */ const resolveLogger = ( - options?: DeviceProfileLoggerOptions + options?: DeviceProfileLoggerOptions, ): { logger: LoggerInstance; loggerId?: string } => { - const logger = options?.logger ?? getDefaultLogger(); - const rawLoggerId = - options?.nativeLogger?.id ?? - logger.nativeHandle?.id ?? - getDefaultLogger().nativeHandle?.id; + const logger = options?.logger ?? noopLogger; + const rawLoggerId = logger.nativeHandle?.id; const loggerId = rawLoggerId?.trim() ? rawLoggerId : undefined; - return { logger, loggerId }; }; @@ -60,7 +44,7 @@ const resolveLogger = ( * * @remarks * Native implementations remain authoritative for permissions, formatting, and execution. - * + * * @example * ```ts * try { @@ -72,13 +56,23 @@ const resolveLogger = ( * ``` * * @param collectors - Ordered list of predefined collectors to execute. + * @param options - Optional logger overrides for this call. * @returns A JSON-compatible device profile payload. */ export async function collectDeviceProfile( - collectors: DeviceProfileCollector[] + collectors: DeviceProfileCollector[], + options?: DeviceProfileLoggerOptions, ): Promise { - const nativeModule = getNativeModule(); - return nativeModule.collectDeviceProfile(collectors); + const { logger } = resolveLogger(options); + logger.debug('Device profile collect requested'); + try { + const profile = await getNativeModule().collectDeviceProfile(collectors); + logger.info('Device profile collect success'); + return profile; + } catch (error) { + logger.error('Device profile collect failed'); + throw error; + } } /** @@ -111,12 +105,10 @@ export async function collectDeviceProfile( export async function collectDeviceProfileForJourney( journey: JourneyInstance, collectors: DeviceProfileCollector[], - options?: DeviceProfileLoggerOptions + options?: DeviceProfileLoggerOptions, ): Promise { const { logger, loggerId } = resolveLogger(options); - logger.debug( - `Device profile journey requested ${JSON.stringify({ collectors, loggerId })}` - ); + logger.debug('Device profile journey requested'); let journeyId: string; try { logger.debug('Device profile journey getId requested'); @@ -128,18 +120,14 @@ export async function collectDeviceProfileForJourney( } const nativeModule = getNativeModule(); - logger.debug( - `Device profile native module requested ${JSON.stringify({ collectors })}` - ); + logger.debug('Device profile native call requested'); try { const result = await nativeModule.collectDeviceProfileForJourney( journeyId, collectors, - loggerId - ); - logger.debug( - `Device profile metadata collection successful ${JSON.stringify(result)}` + loggerId, ); + logger.debug('Device profile metadata collection successful'); return result; } catch (error) { logger.error('Device profile metadata collection for Journey failed'); diff --git a/packages/device-profile/src/types/deviceProfile.types.ts b/packages/device-profile/src/types/deviceProfile.types.ts index 1c599875c..d36e76f0e 100644 --- a/packages/device-profile/src/types/deviceProfile.types.ts +++ b/packages/device-profile/src/types/deviceProfile.types.ts @@ -1,12 +1,11 @@ /* -* Copyright (c) 2026 Ping Identity Corporation. All rights reserved. -* -* This software may be modified and distributed under the terms -* of the MIT license. See the LICENSE file for details. -*/ + * Copyright (c) 2026 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ -import type { GenericError } from '@ping-identity/rn-types'; -import type { LoggerInstance, NativeLoggerHandle } from '@ping-identity/rn-types'; +import type { GenericError, LoggerInstance } from '@ping-identity/rn-types'; /** * Supported device profile collectors. * @@ -75,25 +74,14 @@ export type DeviceProfileCallbackInputValue = { * The native implementation resolves this payload once the callback has been submitted. * Errors reject with {@link DeviceProfileError}. */ -export type DeviceProfileJourneyResult = - { type: 'success' }; +export type DeviceProfileJourneyResult = { type: 'success' }; /** * Optional logger configuration for device profile native calls. - * - * @remarks - * Mirrors OIDC logger behavior: - * - `nativeLogger` has highest precedence for native logger application. - * - `logger.nativeHandle` is used when `nativeLogger` is not provided. */ export type DeviceProfileLoggerOptions = { /** * Optional JavaScript logger instance. */ logger?: LoggerInstance; - - /** - * Optional native logger handle. - */ - nativeLogger?: NativeLoggerHandle; }; diff --git a/packages/device-profile/tsconfig.build.json b/packages/device-profile/tsconfig.build.json index 3c0636adf..5b292ef94 100644 --- a/packages/device-profile/tsconfig.build.json +++ b/packages/device-profile/tsconfig.build.json @@ -1,4 +1,11 @@ { "extends": "./tsconfig", - "exclude": ["example", "lib"] + "compilerOptions": { + "rootDir": ".", + "paths": { + "@ping-identity/rn-device-profile": ["./src/index"], + "@ping-identity/rn-types": ["../types/lib/typescript/src/index"] + } + }, + "exclude": ["lib", "**/__tests__/**"] } diff --git a/packages/device-profile/tsconfig.json b/packages/device-profile/tsconfig.json index e2c3f01fd..4d4d0976d 100644 --- a/packages/device-profile/tsconfig.json +++ b/packages/device-profile/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "rootDir": ".", "paths": { - "@ping-identity/rn-device-profile": ["./src/index"] + "@ping-identity/rn-device-profile": ["./src/index"], + "@ping-identity/rn-types": ["../types/src/index"] }, "allowUnreachableCode": false, "allowUnusedLabels": false, @@ -26,5 +26,6 @@ "strict": true, "target": "ESNext", "verbatimModuleSyntax": true - } + }, + "exclude": ["lib", "**/__tests__/**"] } diff --git a/packages/journey/src/NativeRNPingJourney.ts b/packages/journey/src/NativeRNPingJourney.ts index 60a87d8cf..b45b80a07 100644 --- a/packages/journey/src/NativeRNPingJourney.ts +++ b/packages/journey/src/NativeRNPingJourney.ts @@ -5,7 +5,11 @@ * of the MIT license. See the LICENSE file for details. */ -import { NativeModules, TurboModuleRegistry, type TurboModule } from 'react-native'; +import { + NativeModules, + TurboModuleRegistry, + type TurboModule, +} from 'react-native'; /** * Native callback payload returned by Journey bridge. @@ -149,7 +153,11 @@ export interface Spec extends TurboModule { * @param options Optional start flags. * @returns First native node payload. */ - start(journeyId: string, journeyName: string, options?: JourneyOptions): Promise; + start( + journeyId: string, + journeyName: string, + options?: JourneyOptions, + ): Promise; /** * Advance to the next Journey node. @@ -159,7 +167,11 @@ export interface Spec extends TurboModule { * @param input Callback mutation payload. * @returns Next native node payload. */ - next(journeyId: string, nodeId: string, input: NativeJourneyNextInput): Promise; + next( + journeyId: string, + nodeId: string, + input: NativeJourneyNextInput, + ): Promise; /** * Resume a suspended Journey flow. @@ -248,7 +260,7 @@ export function getNativeModule(): Spec { '[@ping-identity/rn-journey] Native module RNPingJourney not found.\n' + 'Ensure the library is linked correctly and the app has been rebuilt.\n' + 'Available NativeModules: ' + - JSON.stringify(Object.keys(NativeModules)) + JSON.stringify(Object.keys(NativeModules)), ); } diff --git a/packages/journey/src/__tests__/callbackHelpers.test.ts b/packages/journey/src/__tests__/callbackHelpers.test.ts index 46199b247..51cb88f8d 100644 --- a/packages/journey/src/__tests__/callbackHelpers.test.ts +++ b/packages/journey/src/__tests__/callbackHelpers.test.ts @@ -40,7 +40,11 @@ describe('Journey callback helpers', () => { const node: JourneyNode = { type: 'ContinueNode', callbacks: [ - { type: 'TextOutputCallback', message: 'Server message only', output: [] }, + { + type: 'TextOutputCallback', + message: 'Server message only', + output: [], + }, ], }; @@ -111,7 +115,9 @@ describe('Journey callback helpers', () => { it('does not treat negative selectedIndex as a default value', () => { const node: JourneyNode = { type: 'ContinueNode', - callbacks: [{ type: 'ConfirmationCallback', selectedIndex: -1, output: [] }], + callbacks: [ + { type: 'ConfirmationCallback', selectedIndex: -1, output: [] }, + ], }; const fields = normalizeCallbacks(node); @@ -123,7 +129,9 @@ describe('Journey callback helpers', () => { it('resolves required when callback payload uses isRequired key', () => { const node: JourneyNode = { type: 'ContinueNode', - callbacks: [{ type: 'TermsAndConditionsCallback', isRequired: true, output: [] }], + callbacks: [ + { type: 'TermsAndConditionsCallback', isRequired: true, output: [] }, + ], }; const fields = normalizeCallbacks(node); @@ -173,7 +181,8 @@ describe('Journey callback helpers', () => { expect(result.issues).toEqual([ { code: 'INVALID_VALUE', - message: 'Callback "NumberAttributeInputCallback" requires a numeric value.', + message: + 'Callback "NumberAttributeInputCallback" requires a numeric value.', fieldId: 'NumberAttributeInputCallback:0', callbackType: 'NumberAttributeInputCallback', }, @@ -183,9 +192,7 @@ describe('Journey callback helpers', () => { it('treats device profile callback as output-only for helper submit planning', () => { const node: JourneyNode = { type: 'ContinueNode', - callbacks: [ - { type: 'DeviceProfileCallback', output: [] }, - ], + callbacks: [{ type: 'DeviceProfileCallback', output: [] }], }; const result = buildNextInput(node, {}); @@ -211,7 +218,8 @@ describe('Journey callback helpers', () => { expect(result.issues).toEqual([ { code: 'REQUIRED_CONSENT_MISSING', - message: 'Required callback "TermsAndConditionsCallback" must be accepted to continue.', + message: + 'Required callback "TermsAndConditionsCallback" must be accepted to continue.', fieldId: 'TermsAndConditionsCallback:0', callbackType: 'TermsAndConditionsCallback', }, @@ -267,7 +275,8 @@ describe('Journey callback helpers', () => { expect(rejected.issues).toEqual([ { code: 'REQUIRED_CONSENT_MISSING', - message: 'Required callback "ConsentMappingCallback" must be accepted to continue.', + message: + 'Required callback "ConsentMappingCallback" must be accepted to continue.', fieldId: 'ConsentMappingCallback:0', callbackType: 'ConsentMappingCallback', }, @@ -352,7 +361,8 @@ describe('Journey callback helpers', () => { expect(result.issues).toEqual([ { code: 'INVALID_VALUE', - message: 'Callback "KbaCreateCallback" requires non-empty KBA question and answer values.', + message: + 'Callback "KbaCreateCallback" requires non-empty KBA question and answer values.', fieldId: 'KbaCreateCallback:0', callbackType: 'KbaCreateCallback', }, @@ -379,7 +389,8 @@ describe('Journey callback helpers', () => { expect(result.issues).toEqual([ { code: 'INVALID_VALUE', - message: 'Callback "ChoiceCallback" selected option index is out of range.', + message: + 'Callback "ChoiceCallback" selected option index is out of range.', fieldId: 'ChoiceCallback:0', callbackType: 'ChoiceCallback', }, diff --git a/packages/journey/src/__tests__/index.test.tsx b/packages/journey/src/__tests__/index.test.tsx index b69d10b6a..24f2c8215 100644 --- a/packages/journey/src/__tests__/index.test.tsx +++ b/packages/journey/src/__tests__/index.test.tsx @@ -20,18 +20,34 @@ type NativeJourneyModuleMock = { }; const createNativeMock = ( - overrides: Partial = {} + overrides: Partial = {}, ): NativeJourneyModuleMock => { return { configureJourney: jest.fn(async () => 'journey-id-1'), - start: jest.fn(async () => ({ id: 'n1', type: 'ContinueNode', callbacks: [] })), - next: jest.fn(async () => ({ id: 'n2', type: 'ContinueNode', callbacks: [] })), - resume: jest.fn(async () => ({ id: 'n3', type: 'ContinueNode', callbacks: [] })), + start: jest.fn(async () => ({ + id: 'n1', + type: 'ContinueNode', + callbacks: [], + })), + next: jest.fn(async () => ({ + id: 'n2', + type: 'ContinueNode', + callbacks: [], + })), + resume: jest.fn(async () => ({ + id: 'n3', + type: 'ContinueNode', + callbacks: [], + })), getSession: jest.fn(async () => ({ accessToken: 'token' })), refresh: jest.fn(async () => ({ accessToken: 'refreshed-token' })), revoke: jest.fn(async () => true), userinfo: jest.fn(async () => ({ sub: 'user-1' })), - ssoToken: jest.fn(async () => ({ value: 'sso', successUrl: '/enduser', realm: '/alpha' })), + ssoToken: jest.fn(async () => ({ + value: 'sso', + successUrl: '/enduser', + realm: '/alpha', + })), logout: jest.fn(async () => true), dispose: jest.fn(async () => undefined), ...overrides, @@ -56,8 +72,10 @@ describe('Journey JS API', () => { expect(() => createJourneyClient({ serverUrl: '', - }) - ).toThrow('[@ping-identity/rn-journey] Missing configuration. Provide a non-empty serverUrl.'); + }), + ).toThrow( + '[@ping-identity/rn-journey] Missing configuration. Provide a non-empty serverUrl.', + ); }); it('passes storage and logger ids to configureJourney', async () => { @@ -90,34 +108,7 @@ describe('Journey JS API', () => { expect.objectContaining({ sessionStorageId: 'session-storage-id', loggerId: 'logger-id', - }) - ); - }); - - it('uses modules.oidc.nativeLogger id when top-level logger is not provided', async () => { - const native = createNativeMock(); - const { createJourneyClient } = await loadModule(native); - - const client = createJourneyClient({ - serverUrl: 'https://example.com', - modules: { - oidc: { - clientId: 'rn-client', - discoveryEndpoint: - 'https://example.com/am/oauth2/.well-known/openid-configuration', - redirectUri: 'com.example.app://oauth2redirect', - scopes: ['openid'], - nativeLogger: { id: 'oidc-native-logger-id' }, - }, - }, - }); - - await client.init(); - - expect(native.configureJourney).toHaveBeenCalledWith( - expect.objectContaining({ - loggerId: 'oidc-native-logger-id', - }) + }), ); }); @@ -153,11 +144,11 @@ describe('Journey JS API', () => { expect(native.configureJourney).toHaveBeenCalledWith( expect.objectContaining({ loggerId: undefined, - }) + }), ); }); - it('prefers top-level logger over module native logger', async () => { + it('uses top-level logger nativeHandle as loggerId', async () => { const native = createNativeMock(); const { createJourneyClient } = await loadModule(native); @@ -180,7 +171,6 @@ describe('Journey JS API', () => { 'https://example.com/am/oauth2/.well-known/openid-configuration', redirectUri: 'com.example.app://oauth2redirect', scopes: ['openid'], - nativeLogger: { id: 'oidc-native-logger-id' }, }, }, }); @@ -190,7 +180,7 @@ describe('Journey JS API', () => { expect(native.configureJourney).toHaveBeenCalledWith( expect.objectContaining({ loggerId: 'top-level-logger-id', - }) + }), ); }); @@ -207,7 +197,7 @@ describe('Journey JS API', () => { expect(native.configureJourney).toHaveBeenCalledWith( expect.objectContaining({ loggerId: undefined, - }) + }), ); }); @@ -225,7 +215,7 @@ describe('Journey JS API', () => { expect(native.configureJourney).toHaveBeenCalledWith( expect.objectContaining({ timeout: 30000, - }) + }), ); }); @@ -285,7 +275,7 @@ describe('Journey JS API', () => { additionalParameters: { audience: 'urn:example:api', }, - }) + }), ); }); @@ -315,7 +305,7 @@ describe('Journey JS API', () => { expect(native.configureJourney).toHaveBeenCalledWith( expect.objectContaining({ oidcStorageId: 'oidc-storage-id', - }) + }), ); }); @@ -333,10 +323,10 @@ describe('Journey JS API', () => { } as any, }, }, - }) + }), ).toThrow( '[@ping-identity/rn-journey] Invalid modules.session.storage handle. ' + - 'Use configureSessionStorage(...) from @ping-identity/rn-storage.' + 'Use configureSessionStorage(...) from @ping-identity/rn-storage.', ); }); @@ -360,10 +350,10 @@ describe('Journey JS API', () => { } as any, }, }, - }) + }), ).toThrow( '[@ping-identity/rn-journey] Invalid modules.oidc.storage handle. ' + - 'Use configureOidcStorage(...) from @ping-identity/rn-storage.' + 'Use configureOidcStorage(...) from @ping-identity/rn-storage.', ); }); @@ -387,11 +377,10 @@ describe('Journey JS API', () => { await client.start('Login', { forceAuth: true, noSession: true }); - expect(native.start).toHaveBeenCalledWith( - 'journey-id-1', - 'Login', - { forceAuth: true, noSession: true } - ); + expect(native.start).toHaveBeenCalledWith('journey-id-1', 'Login', { + forceAuth: true, + noSession: true, + }); }); it('throws argument error when start journey name is empty', async () => { @@ -447,7 +436,11 @@ describe('Journey JS API', () => { expect(refreshedSession).toEqual({ accessToken: 'refreshed-token' }); expect(didRevoke).toBe(true); expect(userInfo).toEqual({ sub: 'user-1' }); - expect(ssoToken).toEqual({ value: 'sso', successUrl: '/enduser', realm: '/alpha' }); + expect(ssoToken).toEqual({ + value: 'sso', + successUrl: '/enduser', + realm: '/alpha', + }); expect(didLogout).toBe(true); expect(native.getSession).toHaveBeenCalledWith('journey-id-1'); expect(native.refresh).toHaveBeenCalledWith('journey-id-1'); diff --git a/packages/journey/src/__tests__/nativeModuleAndProvider.test.tsx b/packages/journey/src/__tests__/nativeModuleAndProvider.test.tsx index 994a7deea..78fa93f01 100644 --- a/packages/journey/src/__tests__/nativeModuleAndProvider.test.tsx +++ b/packages/journey/src/__tests__/nativeModuleAndProvider.test.tsx @@ -7,7 +7,11 @@ import React, { useEffect } from 'react'; import { render, act } from '@testing-library/react-native'; -import { JourneyProvider, useJourney, type JourneyHookResult } from '../useJourney'; +import { + JourneyProvider, + useJourney, + type JourneyHookResult, +} from '../useJourney'; type JourneyClient = import('../types').JourneyClient; type JourneyNode = import('../types').JourneyNode; @@ -99,14 +103,17 @@ describe('useJourney provider and native module resolution', () => { latest = result; }} /> - + , ); await act(async () => { await requireLatest(latest)[1].start('Login'); }); - expect(client.start).toHaveBeenCalledWith('Login', { forceAuth: false, noSession: false }); + expect(client.start).toHaveBeenCalledWith('Login', { + forceAuth: false, + noSession: false, + }); }); it('throws clear error when legacy native module is unavailable', () => { @@ -117,7 +124,7 @@ describe('useJourney provider and native module resolution', () => { })); const nativeModule = require('../NativeRNPingJourney'); expect(() => nativeModule.getNativeModule()).toThrow( - '[@ping-identity/rn-journey] Native module RNPingJourney not found.' + '[@ping-identity/rn-journey] Native module RNPingJourney not found.', ); }); }); diff --git a/packages/journey/src/__tests__/useJourney.test.tsx b/packages/journey/src/__tests__/useJourney.test.tsx index 73fe1f3d7..a8e475f3a 100644 --- a/packages/journey/src/__tests__/useJourney.test.tsx +++ b/packages/journey/src/__tests__/useJourney.test.tsx @@ -55,7 +55,9 @@ function JourneyHarness(props: JourneyHarnessProps): React.ReactElement | null { * @param overrides - Optional client overrides. * @returns Journey client mock. */ -function createJourneyClientMock(overrides: Partial = {}): JourneyClient { +function createJourneyClientMock( + overrides: Partial = {}, +): JourneyClient { const continueNode: JourneyNode = { type: 'ContinueNode', callbacks: [] }; const successNode: JourneyNode = { type: 'SuccessNode' }; @@ -108,7 +110,7 @@ describe('useJourney auto polling', () => { onResult={(result) => { latest = result; }} - /> + />, ); await act(async () => { @@ -148,7 +150,7 @@ describe('useJourney auto polling', () => { onResult={(result) => { latest = result; }} - /> + />, ); await act(async () => { @@ -188,7 +190,7 @@ describe('useJourney auto polling', () => { onResult={(result) => { latest = result; }} - /> + />, ); await act(async () => { @@ -213,7 +215,9 @@ describe('useJourney auto polling', () => { type: 'ContinueNode', callbacks: [{ type: 'PollingWaitCallback', waitTime: 1200, output: [] }], }; - const nextSpy = jest.fn(async () => ({ type: 'SuccessNode' } as JourneyNode)); + const nextSpy = jest.fn( + async () => ({ type: 'SuccessNode' }) as JourneyNode, + ); const client = createJourneyClientMock({ start: jest.fn(async () => pollingNode), next: nextSpy, @@ -227,7 +231,7 @@ describe('useJourney auto polling', () => { onResult={(result) => { latest = result; }} - /> + />, ); unmount = rendered.unmount; @@ -256,7 +260,9 @@ describe('useJourney auto polling', () => { type: 'ContinueNode', callbacks: [{ type: 'NameCallback', output: [] }], }; - const nextSpy = jest.fn(async () => ({ type: 'SuccessNode' } as JourneyNode)); + const nextSpy = jest.fn( + async () => ({ type: 'SuccessNode' }) as JourneyNode, + ); const client = createJourneyClientMock({ start: jest.fn(async () => pollingNode), resume: jest.fn(async () => manualNode), @@ -270,7 +276,7 @@ describe('useJourney auto polling', () => { onResult={(result) => { latest = result; }} - /> + />, ); await act(async () => { diff --git a/packages/journey/src/__tests__/useJourneyForm.test.tsx b/packages/journey/src/__tests__/useJourneyForm.test.tsx index 1a0ac46de..fe3b21819 100644 --- a/packages/journey/src/__tests__/useJourneyForm.test.tsx +++ b/packages/journey/src/__tests__/useJourneyForm.test.tsx @@ -37,7 +37,9 @@ function requireLatest(result: JourneyFormResult | null): JourneyFormResult { * @param props - Harness props. * @returns Null render output. */ -function JourneyFormHarness(props: JourneyFormHarnessProps): React.ReactElement | null { +function JourneyFormHarness( + props: JourneyFormHarnessProps, +): React.ReactElement | null { const { node, onResult } = props; const form = useJourneyForm(node); @@ -66,7 +68,7 @@ describe('useJourneyForm', () => { onResult={(result) => { latest = result; }} - /> + />, ); const form = requireLatest(latest); @@ -91,7 +93,7 @@ describe('useJourneyForm', () => { onResult={(result) => { latest = result; }} - /> + />, ); act(() => { @@ -130,7 +132,7 @@ describe('useJourneyForm', () => { onResult={(result) => { latest = result; }} - /> + />, ); rerender = rendered.rerender; @@ -147,7 +149,7 @@ describe('useJourneyForm', () => { onResult={(result) => { latest = result; }} - /> + />, ); }); @@ -172,7 +174,7 @@ describe('useJourneyForm', () => { onResult={(result) => { latest = result; }} - /> + />, ); const form = requireLatest(latest); @@ -180,13 +182,21 @@ describe('useJourneyForm', () => { expect(form.getFieldByType('NameCallback', 1)?.id).toBe('NameCallback:1'); act(() => { - const applied = requireLatest(latest).setValueByType('NameCallback', 'typed-user', 1); + const applied = requireLatest(latest).setValueByType( + 'NameCallback', + 'typed-user', + 1, + ); expect(applied).toBe(true); }); expect(requireLatest(latest).values['NameCallback:1']).toBe('typed-user'); - const missing = requireLatest(latest).setValueByType('NameCallback', 'nope', 99); + const missing = requireLatest(latest).setValueByType( + 'NameCallback', + 'nope', + 99, + ); expect(missing).toBe(false); }); }); diff --git a/packages/journey/src/callbackHelpers.ts b/packages/journey/src/callbackHelpers.ts index e18af7937..112382ac7 100644 --- a/packages/journey/src/callbackHelpers.ts +++ b/packages/journey/src/callbackHelpers.ts @@ -5,7 +5,10 @@ * of the MIT license. See the LICENSE file for details. */ -import { callbackType, nativeExtensionCallbackType } from '@ping-identity/rn-types'; +import { + callbackType, + nativeExtensionCallbackType, +} from '@ping-identity/rn-types'; import type { JourneyBuildNextInputResult, JourneyCallback, @@ -238,7 +241,9 @@ function resolveFieldKind(type: JourneyCallbackType): JourneyFieldKind { * @param type - Callback type. * @returns Callback capability. */ -function resolveFieldCapability(type: JourneyCallbackType): JourneyFieldCapability { +function resolveFieldCapability( + type: JourneyCallbackType, +): JourneyFieldCapability { if (integrationRequiredCallbackTypes.has(type)) { return 'integration_required'; } @@ -257,7 +262,9 @@ function resolveFieldCapability(type: JourneyCallbackType): JourneyFieldCapabili * @param callback - Journey callback payload. * @returns Option list. */ -function resolveCallbackOptions(callback: JourneyCallback): JourneyFieldOption[] { +function resolveCallbackOptions( + callback: JourneyCallback, +): JourneyFieldOption[] { const mapOptions = (items: unknown[]): JourneyFieldOption[] => items.map((option, index) => ({ index, @@ -300,7 +307,7 @@ function resolveCallbackOptions(callback: JourneyCallback): JourneyFieldOption[] */ function resolveDefaultValue( callback: JourneyCallback, - type: JourneyCallbackType + type: JourneyCallbackType, ): JourneyFormValue | undefined { if (booleanCallbackTypes.has(type)) { if (hasCallbackKey(callback, 'accepted')) { @@ -311,7 +318,10 @@ function resolveDefaultValue( } return undefined; } - if (type === callbackType.ChoiceCallback || type === callbackType.ConfirmationCallback) { + if ( + type === callbackType.ChoiceCallback || + type === callbackType.ConfirmationCallback + ) { if (!hasCallbackKey(callback, 'selectedIndex')) { return undefined; } @@ -333,15 +343,22 @@ function resolveDefaultValue( const hasSelectedAnswer = hasCallbackKey(callback, 'selectedAnswer'); const hasAllowUserDefinedQuestions = hasCallbackKey( callback, - 'allowUserDefinedQuestions' + 'allowUserDefinedQuestions', ); - if (!hasSelectedQuestion && !hasSelectedAnswer && !hasAllowUserDefinedQuestions) { + if ( + !hasSelectedQuestion && + !hasSelectedAnswer && + !hasAllowUserDefinedQuestions + ) { return undefined; } return { selectedQuestion: readString(callback.selectedQuestion, ''), selectedAnswer: readString(callback.selectedAnswer, ''), - allowUserDefinedQuestions: readBoolean(callback.allowUserDefinedQuestions, false), + allowUserDefinedQuestions: readBoolean( + callback.allowUserDefinedQuestions, + false, + ), }; } if (textCallbackTypes.has(type) || passwordCallbackTypes.has(type)) { @@ -359,7 +376,9 @@ function resolveDefaultValue( * @param node - Journey node from `useJourney`. * @returns Normalized field list with deterministic ids and capability metadata. */ -export function normalizeCallbacks(node: JourneyNode | null | undefined): JourneyNormalizedField[] { +export function normalizeCallbacks( + node: JourneyNode | null | undefined, +): JourneyNormalizedField[] { if (!node || node.type !== 'ContinueNode' || !Array.isArray(node.callbacks)) { return []; } @@ -406,7 +425,7 @@ export function normalizeCallbacks(node: JourneyNode | null | undefined): Journe */ export function buildNextInput( node: JourneyNode | null | undefined, - values: JourneyFormValues + values: JourneyFormValues, ): JourneyBuildNextInputResult { if (!node || node.type !== 'ContinueNode') { return { @@ -494,7 +513,11 @@ export function buildNextInput( if (field.kind === 'choice') { const selected = readNumber(resolvedValue, Number.NaN); - if (!Number.isFinite(selected) || !Number.isInteger(selected) || selected < 0) { + if ( + !Number.isFinite(selected) || + !Number.isInteger(selected) || + selected < 0 + ) { issues.push({ code: 'INVALID_VALUE', message: `Callback "${callbackType}" requires a selected option index.`, @@ -536,19 +559,20 @@ export function buildNextInput( const selectedQuestion = readString( (kba as Record).selectedQuestion, - '' + '', ); const selectedAnswer = readString( (kba as Record).selectedAnswer, - '' + '', ); const allowUserDefinedQuestions = readBoolean( (kba as Record).allowUserDefinedQuestions, - false + false, ); if ( field.required && - (selectedQuestion.trim().length === 0 || selectedAnswer.trim().length === 0) + (selectedQuestion.trim().length === 0 || + selectedAnswer.trim().length === 0) ) { issues.push({ code: 'INVALID_VALUE', diff --git a/packages/journey/src/journey.ts b/packages/journey/src/journey.ts index d09c72e12..2712dc4ed 100644 --- a/packages/journey/src/journey.ts +++ b/packages/journey/src/journey.ts @@ -53,7 +53,7 @@ function resolveStorageHandleId( value: unknown, expectedKind: StorageHandleKind, modulePath: string, - configureMethod: 'configureSessionStorage' | 'configureOidcStorage' + configureMethod: 'configureSessionStorage' | 'configureOidcStorage', ): string | undefined { if (!value) { return undefined; @@ -71,7 +71,7 @@ function resolveStorageHandleId( ) { throw new Error( `[@ping-identity/rn-journey] Invalid ${modulePath} handle. ` + - `Use ${configureMethod}(...) from @ping-identity/rn-storage.` + `Use ${configureMethod}(...) from @ping-identity/rn-storage.`, ); } @@ -85,12 +85,10 @@ function resolveStorageHandleId( * @returns A `JourneyClient` handle for imperative Journey flows. * @throws {Error} When required configuration is missing. */ -export function createJourneyClient( - config: JourneyConfig -): JourneyClient { +export function createJourneyClient(config: JourneyConfig): JourneyClient { if (!config.serverUrl?.trim()) { throw new Error( - '[@ping-identity/rn-journey] Missing configuration. Provide a non-empty serverUrl.' + '[@ping-identity/rn-journey] Missing configuration. Provide a non-empty serverUrl.', ); } @@ -99,19 +97,18 @@ export function createJourneyClient( config.modules?.session?.storage, 'session', 'modules.session.storage', - 'configureSessionStorage' + 'configureSessionStorage', ); const oidcStorageId = resolveStorageHandleId( config.modules?.oidc?.storage, 'oidc', 'modules.oidc.storage', - 'configureOidcStorage' + 'configureOidcStorage', ); const oidcConfig = config.modules?.oidc; const jsLogger = config.logger ?? noopLogger; const rawLoggerId = config.logger?.nativeHandle?.id ?? - oidcConfig?.nativeLogger?.id ?? jsLogger.nativeHandle?.id; const loggerId = rawLoggerId?.trim() ? rawLoggerId : undefined; @@ -140,13 +137,15 @@ export function createJourneyClient( loggerId, }; - const serialize = (payload: Record): string => ( - JSON.stringify(payload, (_key, value) => ( - typeof value === 'function' ? undefined : value - )) - ); + const serialize = (payload: Record): string => + JSON.stringify(payload, (_key, value) => + typeof value === 'function' ? undefined : value, + ); - const logDebug = (message: string, payload?: Record): void => { + const logDebug = ( + message: string, + payload?: Record, + ): void => { if (payload) { jsLogger.debug(`${message} ${serialize(payload)}`); return; @@ -154,7 +153,10 @@ export function createJourneyClient( jsLogger.debug(message); }; - const logInfo = (message: string, payload?: Record): void => { + const logInfo = ( + message: string, + payload?: Record, + ): void => { if (payload) { jsLogger.info(`${message} ${serialize(payload)}`); return; @@ -165,7 +167,7 @@ export function createJourneyClient( const logError = ( message: string, error: unknown, - payload?: Record + payload?: Record, ): void => { const errorPayload: Record = { ...(payload ?? {}), @@ -208,10 +210,7 @@ export function createJourneyClient( return id; }, - async start( - journeyName: string, - options?: JourneyStartOptions - ) { + async start(journeyName: string, options?: JourneyStartOptions) { if (!journeyName.trim()) { throw { type: 'argument_error', @@ -271,7 +270,10 @@ export function createJourneyClient( logDebug('Journey user requested', { journeyId: id }); try { const session = await getSession(id); - logInfo('Journey user resolved', { journeyId: id, hasSession: Boolean(session) }); + logInfo('Journey user resolved', { + journeyId: id, + hasSession: Boolean(session), + }); return session; } catch (error) { logError('Journey user failed', error, { journeyId: id }); @@ -284,7 +286,10 @@ export function createJourneyClient( logDebug('Journey refresh requested', { journeyId: id }); try { const session = await refreshSession(id); - logInfo('Journey refresh succeeded', { journeyId: id, hasSession: Boolean(session) }); + logInfo('Journey refresh succeeded', { + journeyId: id, + hasSession: Boolean(session), + }); return session; } catch (error) { logError('Journey refresh failed', error, { journeyId: id }); @@ -310,7 +315,10 @@ export function createJourneyClient( logDebug('Journey userinfo requested', { journeyId: id }); try { const info = await getUserInfo(id); - logInfo('Journey userinfo resolved', { journeyId: id, hasUserInfo: Boolean(info) }); + logInfo('Journey userinfo resolved', { + journeyId: id, + hasUserInfo: Boolean(info), + }); return info; } catch (error) { logError('Journey userinfo failed', error, { journeyId: id }); @@ -323,7 +331,10 @@ export function createJourneyClient( logDebug('Journey ssoToken requested', { journeyId: id }); try { const token = await getSSOToken(id); - logInfo('Journey ssoToken resolved', { journeyId: id, hasSsoToken: Boolean(token) }); + logInfo('Journey ssoToken resolved', { + journeyId: id, + hasSsoToken: Boolean(token), + }); return token; } catch (error) { logError('Journey ssoToken failed', error, { journeyId: id }); diff --git a/packages/journey/src/journeyMethods.ts b/packages/journey/src/journeyMethods.ts index 89129025d..43da18efc 100644 --- a/packages/journey/src/journeyMethods.ts +++ b/packages/journey/src/journeyMethods.ts @@ -23,7 +23,7 @@ import type { NativeJourneyConfig } from './NativeRNPingJourney'; * @returns Native Journey instance identifier. */ export async function configureJourney( - config: NativeJourneyConfig + config: NativeJourneyConfig, ): Promise { return await NativeRNPingJourney.configureJourney(config); } @@ -39,7 +39,7 @@ export async function configureJourney( export async function startJourney( journeyId: string, journeyName: string, - options?: JourneyStartOptions + options?: JourneyStartOptions, ): Promise { const node = await NativeRNPingJourney.start(journeyId, journeyName, options); return node as unknown as JourneyNode; @@ -54,7 +54,7 @@ export async function startJourney( */ export async function nextNode( journeyId: string, - input: JourneyNextInput = {} + input: JourneyNextInput = {}, ): Promise { const node = await NativeRNPingJourney.next(journeyId, '', input); return node as unknown as JourneyNode; @@ -69,7 +69,7 @@ export async function nextNode( */ export async function resumeJourney( journeyId: string, - uri: string + uri: string, ): Promise { const node = await NativeRNPingJourney.resume(journeyId, uri); return node as unknown as JourneyNode; @@ -82,7 +82,7 @@ export async function resumeJourney( * @returns Session payload, or `null` when unavailable. */ export async function getSession( - journeyId: string + journeyId: string, ): Promise { const session = await NativeRNPingJourney.getSession(journeyId); return session ? (session as JourneyUserSession) : null; @@ -95,7 +95,7 @@ export async function getSession( * @returns Refreshed session payload, or `null` when unavailable. */ export async function refreshSession( - journeyId: string + journeyId: string, ): Promise { const session = await NativeRNPingJourney.refresh(journeyId); return session ? (session as JourneyUserSession) : null; @@ -107,9 +107,7 @@ export async function refreshSession( * @param journeyId - Native Journey instance identifier. * @returns `true` when revoke succeeds. */ -export async function revokeSession( - journeyId: string -): Promise { +export async function revokeSession(journeyId: string): Promise { return await NativeRNPingJourney.revoke(journeyId); } @@ -120,7 +118,7 @@ export async function revokeSession( * @returns Userinfo payload, or `null` when unavailable. */ export async function getUserInfo( - journeyId: string + journeyId: string, ): Promise { const userInfo = await NativeRNPingJourney.userinfo(journeyId); return userInfo ? (userInfo as JourneyUserInfo) : null; @@ -133,7 +131,7 @@ export async function getUserInfo( * @returns SSO token payload, or `null` when unavailable. */ export async function getSSOToken( - journeyId: string + journeyId: string, ): Promise { const ssoToken = await NativeRNPingJourney.ssoToken(journeyId); return ssoToken ? (ssoToken as JourneySSOToken) : null; @@ -145,9 +143,7 @@ export async function getSSOToken( * @param journeyId - Native Journey instance identifier. * @returns `true` when logout succeeds. */ -export async function logout( - journeyId: string -): Promise { +export async function logout(journeyId: string): Promise { return await NativeRNPingJourney.logout(journeyId); } @@ -156,8 +152,6 @@ export async function logout( * * @param journeyId - Native Journey instance identifier. */ -export async function disposeJourney( - journeyId: string -): Promise { +export async function disposeJourney(journeyId: string): Promise { await NativeRNPingJourney.dispose(journeyId); } diff --git a/packages/journey/src/types/client.types.ts b/packages/journey/src/types/client.types.ts index b212e500c..5a11b66e2 100644 --- a/packages/journey/src/types/client.types.ts +++ b/packages/journey/src/types/client.types.ts @@ -7,7 +7,11 @@ import type { JourneyNextInput, JourneyStartOptions } from './config.types'; import type { JourneyNode } from './node.types'; -import type { JourneySSOToken, JourneyUserInfo, JourneyUserSession } from './session.types'; +import type { + JourneySSOToken, + JourneyUserInfo, + JourneyUserSession, +} from './session.types'; /** * Journey imperative client API contracts. @@ -43,7 +47,7 @@ export type JourneyClient = { */ start: ( journeyName: string, - options?: JourneyStartOptions + options?: JourneyStartOptions, ) => Promise; /** diff --git a/packages/journey/src/types/config.types.ts b/packages/journey/src/types/config.types.ts index 0d78f38fc..8b285e804 100644 --- a/packages/journey/src/types/config.types.ts +++ b/packages/journey/src/types/config.types.ts @@ -7,7 +7,6 @@ import type { LoggerInstance, - NativeLoggerHandle, OidcStorageHandle, OidcCoreConfig, OidcOpenIdConfiguration, @@ -42,13 +41,6 @@ export type JourneyOidcModuleConfig = OidcCoreConfig & { * Must be created by `@ping-identity/rn-logger` (`logger(...)`). */ logger?: LoggerInstance; - /** - * Optional native logger handle. - * - * @remarks - * Must be created by `@ping-identity/rn-logger` (`configureLogger(...)`). - */ - nativeLogger?: NativeLoggerHandle; }; /** diff --git a/packages/journey/src/types/form.types.ts b/packages/journey/src/types/form.types.ts index b8a52a233..162605367 100644 --- a/packages/journey/src/types/form.types.ts +++ b/packages/journey/src/types/form.types.ts @@ -286,7 +286,9 @@ export type JourneyFormResult = { * @param overrides - Optional value overrides applied before planning. * @returns Submit planning result. */ - buildInput: (overrides?: Partial) => JourneyBuildNextInputResult; + buildInput: ( + overrides?: Partial, + ) => JourneyBuildNextInputResult; /** * Returns one normalized field by id. * @@ -300,7 +302,9 @@ export type JourneyFormResult = { * @param callbackType - Native callback type. * @returns Matching fields for the callback type, or an empty list. */ - getFieldsByType: (callbackType: JourneyCallbackType) => JourneyNormalizedField[]; + getFieldsByType: ( + callbackType: JourneyCallbackType, + ) => JourneyNormalizedField[]; /** * Returns one normalized field by callback type and per-type index. * @@ -310,7 +314,7 @@ export type JourneyFormResult = { */ getFieldByType: ( callbackType: JourneyCallbackType, - typeIndex?: number + typeIndex?: number, ) => JourneyNormalizedField | undefined; /** * Sets one field value by callback type and per-type index. @@ -323,6 +327,6 @@ export type JourneyFormResult = { setValueByType: ( callbackType: JourneyCallbackType, value: JourneyFormValue, - typeIndex?: number + typeIndex?: number, ) => boolean; }; diff --git a/packages/journey/src/types/session.types.ts b/packages/journey/src/types/session.types.ts index 8b31f1efd..ee2e951d2 100644 --- a/packages/journey/src/types/session.types.ts +++ b/packages/journey/src/types/session.types.ts @@ -36,7 +36,10 @@ export type JourneySSOToken = { /** * Session payload exposed by `user()`. */ -export type JourneyUserSession = Pick & { +export type JourneyUserSession = Pick< + Tokens, + 'accessToken' | 'refreshToken' +> & { /** * Optional token expiry in seconds. */ diff --git a/packages/journey/src/useJourney.tsx b/packages/journey/src/useJourney.tsx index 2012c6146..089b60299 100644 --- a/packages/journey/src/useJourney.tsx +++ b/packages/journey/src/useJourney.tsx @@ -35,17 +35,23 @@ const DEFAULT_AUTO_POLLING_WAIT_MS = 3000; * @returns Polling wait in milliseconds when available. */ function resolvePollingWaitMs( - fields: ReturnType + fields: ReturnType, ): number | null { // AM flows are expected to return one PollingWaitCallback per node. // If multiple are present, use the first callback in node order. - const pollingField = fields.find((field) => field.ref.type === 'PollingWaitCallback'); + const pollingField = fields.find( + (field) => field.ref.type === 'PollingWaitCallback', + ); if (!pollingField) { return null; } const waitTime = pollingField.raw.waitTime; - if (typeof waitTime === 'number' && Number.isFinite(waitTime) && waitTime > 0) { + if ( + typeof waitTime === 'number' && + Number.isFinite(waitTime) && + waitTime > 0 + ) { return waitTime; } if (typeof waitTime === 'string') { @@ -64,8 +70,12 @@ function resolvePollingWaitMs( * @param fields - Normalized callback fields. * @returns True when safe to auto-advance with `next({})`. */ -function shouldAutoPoll(fields: ReturnType): boolean { - const hasPollingWait = fields.some((field) => field.ref.type === 'PollingWaitCallback'); +function shouldAutoPoll( + fields: ReturnType, +): boolean { + const hasPollingWait = fields.some( + (field) => field.ref.type === 'PollingWaitCallback', + ); if (!hasPollingWait) { return false; } @@ -96,7 +106,10 @@ export type JourneyHookActions = { * @returns First Journey node. * @throws {JourneyError} When start fails. */ - start: (journeyName: string, options?: JourneyStartOptions) => Promise; + start: ( + journeyName: string, + options?: JourneyStartOptions, + ) => Promise; /** * Advance the active Journey node. @@ -186,7 +199,10 @@ export type JourneyHookActions = { /** * Tuple result returned by {@link useJourney}. */ -export type JourneyHookResult = readonly [JourneyNode | null, JourneyHookActions]; +export type JourneyHookResult = readonly [ + JourneyNode | null, + JourneyHookActions, +]; type JourneyContextValue = { client: JourneyClient; @@ -261,12 +277,19 @@ export type JourneyProviderProps = { * @param props - Provider props. * @returns Journey context provider element. */ -export function JourneyProvider(props: JourneyProviderProps): React.ReactElement { +export function JourneyProvider( + props: JourneyProviderProps, +): React.ReactElement { const { client, children } = props; const journey = useJourneyState(client); - const value = useMemo(() => ({ client, journey }), [client, journey]); + const value = useMemo( + () => ({ client, journey }), + [client, journey], + ); - return {children}; + return ( + {children} + ); } /** @@ -281,10 +304,13 @@ function useJourneyState(client: JourneyClient): JourneyHookResult { const [error, setError] = useState(null); const pollingTimerRef = useRef | null>(null); const normalizedFields = useMemo(() => normalizeCallbacks(node), [node]); - const canAutoPoll = useMemo(() => shouldAutoPoll(normalizedFields), [normalizedFields]); + const canAutoPoll = useMemo( + () => shouldAutoPoll(normalizedFields), + [normalizedFields], + ); const autoPollingWaitMs = useMemo( () => resolvePollingWaitMs(normalizedFields), - [normalizedFields] + [normalizedFields], ); const start = useCallback( @@ -293,7 +319,7 @@ function useJourneyState(client: JourneyClient): JourneyHookResult { options: JourneyStartOptions = { forceAuth: false, noSession: false, - } + }, ): Promise => { try { setLoading(true); @@ -309,7 +335,7 @@ function useJourneyState(client: JourneyClient): JourneyHookResult { setLoading(false); } }, - [client] + [client], ); const next = useCallback( @@ -338,7 +364,7 @@ function useJourneyState(client: JourneyClient): JourneyHookResult { setLoading(false); } }, - [client, node] + [client, node], ); const resume = useCallback( @@ -357,7 +383,7 @@ function useJourneyState(client: JourneyClient): JourneyHookResult { setLoading(false); } }, - [client] + [client], ); const user = useCallback(async (): Promise => { @@ -405,7 +431,10 @@ function useJourneyState(client: JourneyClient): JourneyHookResult { return; } - const waitMs = Math.max(500, autoPollingWaitMs ?? DEFAULT_AUTO_POLLING_WAIT_MS); + const waitMs = Math.max( + 500, + autoPollingWaitMs ?? DEFAULT_AUTO_POLLING_WAIT_MS, + ); pollingTimerRef.current = setTimeout(() => { pollingTimerRef.current = null; // `next` already updates hook error state; prevent unhandled rejections @@ -459,7 +488,9 @@ export function useJourney(client: JourneyClient): JourneyHookResult; export function useJourney(): JourneyHookResult; export function useJourney(client?: JourneyClient): JourneyHookResult { const contextValue = useContext(JourneyContext); - const localJourney = useJourneyState(client ?? contextValue?.client ?? missingJourneyClient); + const localJourney = useJourneyState( + client ?? contextValue?.client ?? missingJourneyClient, + ); if (client) { return localJourney; diff --git a/packages/journey/src/useJourneyForm.ts b/packages/journey/src/useJourneyForm.ts index 558d4029b..1a443ef93 100644 --- a/packages/journey/src/useJourneyForm.ts +++ b/packages/journey/src/useJourneyForm.ts @@ -27,7 +27,10 @@ import type { * `WeakMap` keeps this cache GC-safe so stale node entries are released * automatically when node objects are no longer referenced. */ -const normalizedFieldCache = new WeakMap(); +const normalizedFieldCache = new WeakMap< + JourneyNode, + JourneyNormalizedField[] +>(); /** * Resolves normalized callback fields with node-identity caching. @@ -35,7 +38,9 @@ const normalizedFieldCache = new WeakMap( * @param node - Current Journey node. * @returns Normalized callback fields. */ -function getNormalizedFields(node: JourneyNode | null | undefined): JourneyNormalizedField[] { +function getNormalizedFields( + node: JourneyNode | null | undefined, +): JourneyNormalizedField[] { if (!node) { return normalizeCallbacks(node); } @@ -57,7 +62,7 @@ function getNormalizedFields(node: JourneyNode | null | undefined): JourneyNorma * @returns Seed value when one should be applied. */ function resolveSeedValue( - field: JourneyNormalizedField + field: JourneyNormalizedField, ): JourneyFormValue | undefined { return field.defaultValue; } @@ -71,7 +76,7 @@ function resolveSeedValue( */ function hydrateValues( fields: JourneyNormalizedField[], - baseValues: JourneyFormValues + baseValues: JourneyFormValues, ): JourneyFormValues { const nextValues: JourneyFormValues = { ...baseValues }; fields.forEach((field) => { @@ -93,7 +98,10 @@ function hydrateValues( * @param next - Next map. * @returns True when both maps contain identical keys and values. */ -function isSameValueMap(previous: JourneyFormValues, next: JourneyFormValues): boolean { +function isSameValueMap( + previous: JourneyFormValues, + next: JourneyFormValues, +): boolean { const previousKeys = Object.keys(previous); const nextKeys = Object.keys(next); if (previousKeys.length !== nextKeys.length) { @@ -110,16 +118,19 @@ function isSameValueMap(previous: JourneyFormValues, next: JourneyFormValues): b * @param issues - Submit issues from helper planning. * @returns Form metadata. */ -function deriveMeta(fields: JourneyNormalizedField[], issues: JourneySubmitIssue[]): JourneyFormMeta { +function deriveMeta( + fields: JourneyNormalizedField[], + issues: JourneySubmitIssue[], +): JourneyFormMeta { return { hasManual: fields.some((field) => field.capability === 'manual'), hasOutputOnly: fields.some((field) => field.capability === 'output_only'), hasIntegrationRequired: fields.some( - (field) => field.capability === 'integration_required' + (field) => field.capability === 'integration_required', ), hasUnsupported: fields.some((field) => field.capability === 'unsupported'), hasRequiredConsentMissing: issues.some( - (issue) => issue.code === 'REQUIRED_CONSENT_MISSING' + (issue) => issue.code === 'REQUIRED_CONSENT_MISSING', ), }; } @@ -135,17 +146,19 @@ function deriveMeta(fields: JourneyNormalizedField[], issues: JourneySubmitIssue * @returns Normalized fields, managed values, and submit planning helpers. */ export function useJourneyForm( - node: JourneyNode | null | undefined + node: JourneyNode | null | undefined, ): JourneyFormResult { const fields = useMemo( () => getNormalizedFields(node), - [node] + [node], ); const fieldsById = useMemo>( () => new Map(fields.map((field) => [field.id, field])), - [fields] + [fields], ); - const fieldsByType = useMemo>(() => { + const fieldsByType = useMemo< + Map + >(() => { const byType = new Map(); fields.forEach((field) => { const existing = byType.get(field.ref.type); @@ -159,7 +172,7 @@ export function useJourneyForm( }, [fields]); const [values, setValuesState] = useState(() => - hydrateValues(fields, {}) + hydrateValues(fields, {}), ); useEffect(() => { @@ -171,32 +184,32 @@ export function useJourneyForm( const submitPlan = useMemo( () => buildNextInput(node, values), - [node, values] + [node, values], ); const meta = useMemo( () => deriveMeta(fields, submitPlan.issues), - [fields, submitPlan.issues] + [fields, submitPlan.issues], ); - const setValue = useCallback((fieldId: string, value: JourneyFormValue): void => { - setValuesState((previous) => { - if (previous[fieldId] === value) { - return previous; - } - return { - ...previous, - [fieldId]: value, - }; - }); - }, []); + const setValue = useCallback( + (fieldId: string, value: JourneyFormValue): void => { + setValuesState((previous) => { + if (previous[fieldId] === value) { + return previous; + } + return { + ...previous, + [fieldId]: value, + }; + }); + }, + [], + ); const setValues = useCallback((updater: JourneyFormValuesUpdater): void => { setValuesState((previous) => { - const patch = - typeof updater === 'function' - ? updater(previous) - : updater; + const patch = typeof updater === 'function' ? updater(previous) : updater; const nextValues = { ...previous, ...patch, @@ -216,43 +229,61 @@ export function useJourneyForm( }); }, []); - const reset = useCallback((nextValues: JourneyFormValues = {}): void => { - setValuesState((previous) => { - const hydrated = hydrateValues(fields, { ...nextValues }); - return isSameValueMap(previous, hydrated) ? previous : hydrated; - }); - }, [fields]); + const reset = useCallback( + (nextValues: JourneyFormValues = {}): void => { + setValuesState((previous) => { + const hydrated = hydrateValues(fields, { ...nextValues }); + return isSameValueMap(previous, hydrated) ? previous : hydrated; + }); + }, + [fields], + ); const buildInput = useCallback( - (overrides: Partial = {}): JourneyBuildNextInputResult => { + ( + overrides: Partial = {}, + ): JourneyBuildNextInputResult => { return buildNextInput(node, { ...values, ...overrides, }); }, - [node, values] + [node, values], ); - const getField = useCallback((fieldId: string): JourneyNormalizedField | undefined => { - return fieldsById.get(fieldId); - }, [fieldsById]); + const getField = useCallback( + (fieldId: string): JourneyNormalizedField | undefined => { + return fieldsById.get(fieldId); + }, + [fieldsById], + ); - const getFieldsByType = useCallback((callbackType: JourneyCallbackType): JourneyNormalizedField[] => { - return fieldsByType.get(callbackType) ?? []; - }, [fieldsByType]); + const getFieldsByType = useCallback( + (callbackType: JourneyCallbackType): JourneyNormalizedField[] => { + return fieldsByType.get(callbackType) ?? []; + }, + [fieldsByType], + ); const getFieldByType = useCallback( - (callbackType: JourneyCallbackType, typeIndex = 0): JourneyNormalizedField | undefined => { + ( + callbackType: JourneyCallbackType, + typeIndex = 0, + ): JourneyNormalizedField | undefined => { if (typeIndex < 0) { return undefined; } return fieldsByType.get(callbackType)?.[typeIndex]; }, - [fieldsByType] + [fieldsByType], ); const setValueByType = useCallback( - (callbackType: JourneyCallbackType, value: JourneyFormValue, typeIndex = 0): boolean => { + ( + callbackType: JourneyCallbackType, + value: JourneyFormValue, + typeIndex = 0, + ): boolean => { const field = getFieldByType(callbackType, typeIndex); if (!field) { return false; @@ -260,7 +291,7 @@ export function useJourneyForm( setValue(field.id, value); return true; }, - [getFieldByType, setValue] + [getFieldByType, setValue], ); return { diff --git a/packages/journey/tsconfig.build.json b/packages/journey/tsconfig.build.json index 3c0636adf..5bd4b8770 100644 --- a/packages/journey/tsconfig.build.json +++ b/packages/journey/tsconfig.build.json @@ -1,4 +1,11 @@ { "extends": "./tsconfig", - "exclude": ["example", "lib"] + "compilerOptions": { + "rootDir": ".", + "paths": { + "@ping-identity/rn-journey": ["./src/index"], + "@ping-identity/rn-types": ["../types/lib/typescript/src/index"] + } + }, + "exclude": ["lib", "**/__tests__/**"] } diff --git a/packages/journey/tsconfig.json b/packages/journey/tsconfig.json index d65b67d7d..7a4de72f7 100644 --- a/packages/journey/tsconfig.json +++ b/packages/journey/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "rootDir": ".", "paths": { - "@ping-identity/rn-journey": ["./src/index"] + "@ping-identity/rn-journey": ["./src/index"], + "@ping-identity/rn-types": ["../types/src/index"] }, "allowUnreachableCode": false, "allowUnusedLabels": false, @@ -26,5 +26,6 @@ "strict": true, "target": "ESNext", "verbatimModuleSyntax": true - } + }, + "exclude": ["lib", "**/__tests__/**"] } diff --git a/packages/logger/README.md b/packages/logger/README.md index 8a150266f..28762deea 100644 --- a/packages/logger/README.md +++ b/packages/logger/README.md @@ -45,7 +45,7 @@ cd ios && pod install ### Create a logger instance ```ts -import { configureLogger, logger } from '@ping-identity/rn-logger'; +import { logger } from '@ping-identity/rn-logger'; const log = logger({ level: 'info' }); log.info('Application started'); @@ -56,8 +56,8 @@ log.changeLevel('debug'); // Defaults to level "none" when omitted const silentLog = logger(); -// Get native logger handle for modules that accept native logger config -const logHandle = configureLogger({ level: 'info' }); +// Access the native logger handle for modules that accept native logger config +console.log(log.nativeHandle.id); ``` ### Configure a custom logger sink @@ -112,7 +112,6 @@ import type { LogLevel, } from '@ping-identity/rn-logger'; -function configureLogger(config?: LoggerConfig): NativeLoggerHandle; function logger(config?: LoggerConfig): LoggerInstance; type LoggerConfig = { @@ -128,14 +127,14 @@ type LoggerConfig = { ## Error handling -`configureLogger` throws when native logger registration fails. +`logger` throws when native logger registration fails. ```ts -import { configureLogger } from '@ping-identity/rn-logger'; +import { logger } from '@ping-identity/rn-logger'; try { - const nativeHandle = configureLogger({ level: 'info' }); - console.log(nativeHandle.id); + const log = logger({ level: 'info' }); + console.log(log.nativeHandle.id); } catch (error) { console.error('Failed to configure logger', error); } diff --git a/packages/logger/jest.config.js b/packages/logger/jest.config.js index b7e817c36..38503a0e1 100644 --- a/packages/logger/jest.config.js +++ b/packages/logger/jest.config.js @@ -11,6 +11,8 @@ module.exports = { transform: { '^.+\\.[jt]sx?$': 'babel-jest', }, + modulePathIgnorePatterns: ['/lib/'], + watchPathIgnorePatterns: ['/lib/'], reporters: [ 'default', ['jest-junit', { diff --git a/packages/logger/src/NativeRNPingLogger.ts b/packages/logger/src/NativeRNPingLogger.ts index 630dda568..3d657b5cf 100644 --- a/packages/logger/src/NativeRNPingLogger.ts +++ b/packages/logger/src/NativeRNPingLogger.ts @@ -5,26 +5,22 @@ * of the MIT license. See the LICENSE file for details. */ -import { NativeModules, TurboModuleRegistry, type TurboModule } from 'react-native'; +import { + NativeModules, + TurboModuleRegistry, + type TurboModule, +} from 'react-native'; /** * JavaScript-level logger levels. * These are mapped to native logger levels internally. */ -export type LoggerLevel = - | 'debug' - | 'info' - | 'warn' - | 'error' - | 'none'; +export type LoggerLevel = 'debug' | 'info' | 'warn' | 'error' | 'none'; /** * Native logger levels supported by the underlying platform logger. */ -export type NativeLoggerLevel = - | 'STANDARD' - | 'WARN' - | 'NONE'; +export type NativeLoggerLevel = 'STANDARD' | 'WARN' | 'NONE'; /** * Configuration options for registering a logger. @@ -73,7 +69,7 @@ export function getNativeModule(): Spec { '[@ping-identity/rn-logger] Native module Logger not found.\n' + 'Ensure the library is linked correctly and the app has been rebuilt.\n' + 'Available NativeModules: ' + - JSON.stringify(Object.keys(NativeModules)) + JSON.stringify(Object.keys(NativeModules)), ); } diff --git a/packages/logger/src/__tests__/index.test.tsx b/packages/logger/src/__tests__/index.test.tsx index d0d9d7a28..4a1b0d387 100644 --- a/packages/logger/src/__tests__/index.test.tsx +++ b/packages/logger/src/__tests__/index.test.tsx @@ -4,9 +4,8 @@ * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. */ -/* eslint-env jest */ -export {}; +import packageJson from '@ping-identity/rn-logger/package.json'; type NativeLoggerMock = { registerLogger: jest.Mock; @@ -42,16 +41,18 @@ const createSdkLoggerMock = () => { debug: jest.fn(), }; - const factory = jest.fn((options: { level: string; custom?: CustomLoggerMock }) => { - const custom = options.custom; - if (custom) { - instance.error.mockImplementation((...args) => custom.error(...args)); - instance.warn.mockImplementation((...args) => custom.warn(...args)); - instance.info.mockImplementation((...args) => custom.info(...args)); - instance.debug.mockImplementation((...args) => custom.debug(...args)); - } - return instance; - }); + const factory = jest.fn( + (options: { level: string; custom?: CustomLoggerMock }) => { + const custom = options.custom; + if (custom) { + instance.error.mockImplementation((...args) => custom.error(...args)); + instance.warn.mockImplementation((...args) => custom.warn(...args)); + instance.info.mockImplementation((...args) => custom.info(...args)); + instance.debug.mockImplementation((...args) => custom.debug(...args)); + } + return instance; + }, + ); return { factory, instance }; }; @@ -71,29 +72,20 @@ const loadModule = async () => { logger: sdkLogger.factory, })); - const module = require('../logger'); + const module = await import('../logger'); return { module, nativeLogger, sdkLogger }; }; describe('logger package', () => { - it('configureLogger registers a native logger and returns a handle', async () => { + it('logger defaults to NONE native level when no level is provided', async () => { const { module, nativeLogger } = await loadModule(); - const handle = module.configureLogger({ level: 'warn' }); - - expect(nativeLogger.registerLogger).toHaveBeenCalledWith({ level: 'WARN' }); - expect(handle).toEqual({ id: 'logger-id' }); - }); - - it('configureLogger defaults to NONE when no level provided', async () => { - const { module, nativeLogger } = await loadModule(); - - module.configureLogger(); + module.logger(); expect(nativeLogger.registerLogger).toHaveBeenCalledWith({ level: 'NONE' }); }); - it('configureLogger throws when native registration fails', async () => { + it('logger throws when native registration fails', async () => { jest.resetModules(); const nativeLogger = createNativeLoggerMock(); nativeLogger.registerLogger.mockReturnValue(''); @@ -114,10 +106,10 @@ describe('logger package', () => { })), })); - const module = require('../logger'); + const { logger } = await import('../logger'); - expect(() => module.configureLogger({ level: 'info' })).toThrow( - '[@ping-identity/rn-logger] Failed to configure native logger' + expect(() => logger({ level: 'info' })).toThrow( + '[@ping-identity/rn-logger] Failed to configure native logger', ); }); @@ -127,7 +119,9 @@ describe('logger package', () => { const instance = module.logger({ level: 'debug' }); instance.changeLevel('warn'); - expect(nativeLogger.registerLogger).toHaveBeenCalledWith({ level: 'STANDARD' }); + expect(nativeLogger.registerLogger).toHaveBeenCalledWith({ + level: 'STANDARD', + }); expect(sdkLogger.instance.changeLevel).toHaveBeenCalledWith('warn'); expect(nativeLogger.syncLogger).toHaveBeenCalledWith({ id: 'logger-id', @@ -137,7 +131,6 @@ describe('logger package', () => { it('logger tags messages with the SDK prefix', async () => { const { module, sdkLogger } = await loadModule(); - const pkg = require('@ping-identity/rn-logger/package.json'); const custom = { error: jest.fn(() => true), @@ -149,7 +142,7 @@ describe('logger package', () => { const instance = module.logger({ level: 'info', custom }); instance.info('hello'); - const expectedPrefix = `[RNPingSDK v${pkg.version}]`; + const expectedPrefix = `[RNPingSDK v${packageJson.version}]`; expect(custom.info).toHaveBeenCalledWith(`${expectedPrefix} hello`); expect(sdkLogger.instance.info).toHaveBeenCalledWith('hello'); }); diff --git a/packages/logger/src/index.tsx b/packages/logger/src/index.tsx index 56d071848..1c44d4eaf 100644 --- a/packages/logger/src/index.tsx +++ b/packages/logger/src/index.tsx @@ -8,28 +8,28 @@ /** * @packageDocumentation * @module @ping-identity/rn-logger - * + * * A React Native logger module that provides unified logging across JavaScript and native platforms. - * + * * @remarks * This module provides a logger that works seamlessly across React Native's JavaScript layer * and native iOS/Android code. It supports different log levels and custom logger implementations. - * + * * @example * Basic usage: * ```typescript * import { logger } from '@ping-identity/rn-logger'; - * + * * const log = logger({ level: 'debug' }); * log.debug('This is a debug message'); * log.info('This is an info message'); * log.warn('This is a warning'); * log.error('This is an error'); - * + * * // Change log level dynamically * log.changeLevel('error'); * ``` */ -export { configureLogger, logger } from './logger'; +export { logger } from './logger'; export type * from './types/logger.types'; diff --git a/packages/logger/src/logger.ts b/packages/logger/src/logger.ts index 679435a27..1252b9200 100644 --- a/packages/logger/src/logger.ts +++ b/packages/logger/src/logger.ts @@ -26,34 +26,32 @@ const nativeLevelMap: Record = { const sdkTag = `[RNPingSDK v${pkg.version}]`; - /** * Provide a default custom logger that returns a truthy value so the SDK * logger does not fall back to console.error (LogBox warnings in dev). */ function createCustomLogger( custom: LoggerConfig['custom'], - tag: string + tag: string, ): Required['custom'] { - const base = - custom ?? { - error: (...args: LogMessage[]) => { - console.log(...args); - return true; - }, - warn: (...args: LogMessage[]) => { - console.warn(...args); - return true; - }, - info: (...args: LogMessage[]) => { - console.info(...args); - return true; - }, - debug: (...args: LogMessage[]) => { - console.debug(...args); - return true; - }, - }; + const base = custom ?? { + error: (...args: LogMessage[]) => { + console.log(...args); + return true; + }, + warn: (...args: LogMessage[]) => { + console.warn(...args); + return true; + }, + info: (...args: LogMessage[]) => { + console.info(...args); + return true; + }, + debug: (...args: LogMessage[]) => { + console.debug(...args); + return true; + }, + }; return { error: (...args: LogMessage[]) => base.error(`${tag} ${args.join(' ')}`), @@ -78,12 +76,12 @@ function syncNativeLogger(id: string, level: LogLevel) { * @param config - Logger configuration options * @returns Handle to the registered native logger * @throws {Error} If the native logger registration fails - * @public + * @internal * @remarks * This function registers a logger with the native platform (iOS/Android) * and returns an identifier that can be used to sync logger settings. */ -export function configureLogger(config: LoggerConfig = {}): NativeLoggerHandle { +function configureLogger(config: LoggerConfig = {}): NativeLoggerHandle { const level = config.level ?? 'none'; const id = NativeLogger.registerLogger({ level: nativeLevelMap[level], @@ -91,7 +89,7 @@ export function configureLogger(config: LoggerConfig = {}): NativeLoggerHandle { if (!id) { throw new Error( - '[@ping-identity/rn-logger] Failed to configure native logger' + '[@ping-identity/rn-logger] Failed to configure native logger', ); } @@ -106,19 +104,19 @@ export function configureLogger(config: LoggerConfig = {}): NativeLoggerHandle { * @remarks * This is the main entry point for creating a logger. It configures both * JavaScript and native loggers and provides a unified interface for logging. - * + * * @example * Basic usage: * ```typescript * import { logger } from '@ping-identity/rn-logger'; - * + * * const log = logger({ level: 'debug' }); * log.debug('Debug message'); * log.info('Info message'); * log.warn('Warning message'); * log.error('Error message'); * ``` - * + * * @example * With custom logger: * ```typescript diff --git a/packages/logger/tsconfig.build.json b/packages/logger/tsconfig.build.json index 3c0636adf..d212c6dcd 100644 --- a/packages/logger/tsconfig.build.json +++ b/packages/logger/tsconfig.build.json @@ -1,4 +1,11 @@ { "extends": "./tsconfig", - "exclude": ["example", "lib"] + "compilerOptions": { + "rootDir": ".", + "paths": { + "@ping-identity/rn-logger": ["./src/index"], + "@ping-identity/rn-types": ["../types/lib/typescript/src/index"] + } + }, + "exclude": ["lib", "**/__tests__/**"] } diff --git a/packages/logger/tsconfig.json b/packages/logger/tsconfig.json index 3135758e8..a57826274 100644 --- a/packages/logger/tsconfig.json +++ b/packages/logger/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "rootDir": ".", "paths": { - "@ping-identity/rn-logger": ["./src/index"] + "@ping-identity/rn-logger": ["./src/index"], + "@ping-identity/rn-types": ["../types/src/index"] }, "allowUnreachableCode": false, "allowUnusedLabels": false, @@ -26,5 +26,6 @@ "strict": true, "target": "ESNext", "verbatimModuleSyntax": true - } + }, + "exclude": ["lib", "**/__tests__/**"] } diff --git a/packages/oidc/jest.config.js b/packages/oidc/jest.config.js index b7e817c36..38503a0e1 100644 --- a/packages/oidc/jest.config.js +++ b/packages/oidc/jest.config.js @@ -11,6 +11,8 @@ module.exports = { transform: { '^.+\\.[jt]sx?$': 'babel-jest', }, + modulePathIgnorePatterns: ['/lib/'], + watchPathIgnorePatterns: ['/lib/'], reporters: [ 'default', ['jest-junit', { diff --git a/packages/oidc/src/NativeRNPingOidc.ts b/packages/oidc/src/NativeRNPingOidc.ts index 8884cd7d7..cac44037b 100644 --- a/packages/oidc/src/NativeRNPingOidc.ts +++ b/packages/oidc/src/NativeRNPingOidc.ts @@ -41,6 +41,7 @@ export type NativeOidcClientConfig = { loginHint?: string; display?: string; prompt?: string; + // eslint-disable-next-line @typescript-eslint/no-wrapper-object-types additionalParameters?: Object; }; @@ -55,6 +56,7 @@ export type NativeOidcAuthorizeOptions = { loginHint?: string; display?: string; prompt?: string; + // eslint-disable-next-line @typescript-eslint/no-wrapper-object-types additionalParameters?: Object; }; @@ -87,17 +89,23 @@ export interface Spec extends TurboModule { createWebClient(clientId: string): string; clientToken(clientId: string): Promise; clientRefresh(clientId: string): Promise; - clientUserinfo(clientId: string, cache: boolean): Promise>; + clientUserinfo( + clientId: string, + cache: boolean, + ): Promise>; clientRevoke(clientId: string): Promise; clientEndSession(clientId: string): Promise; authorize( webClientId: string, - options: NativeOidcAuthorizeOptions + options: NativeOidcAuthorizeOptions, ): Promise; hasUser(webClientId: string): Promise; token(webClientId: string): Promise; refresh(webClientId: string): Promise; - userinfo(webClientId: string, cache: boolean): Promise>; + userinfo( + webClientId: string, + cache: boolean, + ): Promise>; revoke(webClientId: string): Promise; logout(webClientId: string): Promise; } @@ -123,6 +131,6 @@ export function getNativeModule(): Spec { '[@ping-identity/rn-oidc] Native module RNPingOidc not found.\n' + 'Ensure the library is linked correctly and the app has been rebuilt.\n' + 'Available NativeModules: ' + - JSON.stringify(Object.keys(NativeModules)) + JSON.stringify(Object.keys(NativeModules)), ); } diff --git a/packages/oidc/src/__tests__/index.test.tsx b/packages/oidc/src/__tests__/index.test.tsx index 629c358b7..1833160ca 100644 --- a/packages/oidc/src/__tests__/index.test.tsx +++ b/packages/oidc/src/__tests__/index.test.tsx @@ -5,6 +5,9 @@ * of the MIT license. See the LICENSE file for details. */ +import type { OidcStorageHandle } from '@ping-identity/rn-types'; +import type { OidcClientConfig } from '../index'; + type NativeModuleMock = { createClient: jest.Mock; createWebClient: jest.Mock; @@ -22,7 +25,9 @@ type NativeModuleMock = { logout: jest.Mock; }; -const createNativeMock = (overrides: Partial = {}): NativeModuleMock => { +const createNativeMock = ( + overrides: Partial = {}, +): NativeModuleMock => { return { createClient: jest.fn(() => 'client-id'), createWebClient: jest.fn(() => 'web-id'), @@ -66,7 +71,7 @@ const loadModule = async (nativeModule: NativeModuleMock) => { debug: jest.fn(), })), })); - return require('../index'); + return import('../index'); }; describe('OIDC JS API', () => { @@ -92,7 +97,7 @@ describe('OIDC JS API', () => { tokenEndpoint: 'https://issuer/token', userinfoEndpoint: 'https://issuer/userinfo', }, - }) + }), ); }); @@ -106,10 +111,10 @@ describe('OIDC JS API', () => { discoveryEndpoint: 'https://issuer/.well-known/openid-configuration', redirectUri: 'app://redirect', scopes: ['openid'], - storage: {} as any, - }) + storage: {} as unknown as OidcStorageHandle, + }), ).toThrow( - '[@ping-identity/rn-oidc] Invalid storage handle. Use configureOidcStorage(...) from @ping-identity/rn-storage.' + '[@ping-identity/rn-oidc] Invalid storage handle. Use configureOidcStorage(...) from @ping-identity/rn-storage.', ); }); @@ -122,9 +127,9 @@ describe('OIDC JS API', () => { clientId: 'client', redirectUri: 'app://redirect', scopes: ['openid'], - }) + }), ).toThrow( - '[@ping-identity/rn-oidc] Missing configuration. Provide discoveryEndpoint or openId.' + '[@ping-identity/rn-oidc] Missing configuration. Provide discoveryEndpoint or openId.', ); }); @@ -139,7 +144,10 @@ describe('OIDC JS API', () => { discoveryEndpoint: 'https://issuer/.well-known/openid-configuration', redirectUri: 'app://redirect', scopes: ['openid'], - storage: { id: 'storage-id', kind: 'oidc' } as any, + storage: { + id: 'storage-id', + kind: 'oidc', + } as unknown as OidcStorageHandle, logger, }); @@ -147,7 +155,7 @@ describe('OIDC JS API', () => { expect.objectContaining({ storageId: 'storage-id', loggerId: 'native-logger-id', - }) + }), ); }); @@ -161,37 +169,16 @@ describe('OIDC JS API', () => { redirectUri: 'app://redirect', scopes: ['openid'], signOutRedirectUri: 'app://logout', - } as any); + } as OidcClientConfig & { signOutRedirectUri: string }); expect(nativeModule.createClient).toHaveBeenCalledWith( expect.not.objectContaining({ signOutRedirectUri: expect.anything(), - }) - ); - }); - - it('prefers nativeLogger over logger.nativeHandle', async () => { - const nativeModule = createNativeMock(); - const { createOidcClient } = await loadModule(nativeModule); - - const logger = createJsLogger(); - logger.nativeHandle.id = 'logger-native-id'; - - createOidcClient({ - clientId: 'client', - discoveryEndpoint: 'https://issuer/.well-known/openid-configuration', - redirectUri: 'app://redirect', - scopes: ['openid'], - logger, - nativeLogger: { id: 'explicit-native-id' }, - }); - - expect(nativeModule.createClient).toHaveBeenCalledWith( - expect.objectContaining({ loggerId: 'explicit-native-id' }) + }), ); }); - it('does not pass loggerId when logger and nativeLogger are both omitted', async () => { + it('does not pass loggerId when logger is omitted', async () => { const nativeModule = createNativeMock(); const { createOidcClient } = await loadModule(nativeModule); @@ -205,7 +192,7 @@ describe('OIDC JS API', () => { expect(nativeModule.createClient).toHaveBeenCalledWith( expect.objectContaining({ loggerId: undefined, - }) + }), ); }); @@ -219,10 +206,10 @@ describe('OIDC JS API', () => { discoveryEndpoint: 'https://issuer/.well-known/openid-configuration', redirectUri: 'app://redirect', scopes: ['openid'], - storage: {} as any, - }) + storage: {} as unknown as OidcStorageHandle, + }), ).toThrow( - '[@ping-identity/rn-oidc] Invalid storage handle. Use configureOidcStorage(...) from @ping-identity/rn-storage.' + '[@ping-identity/rn-oidc] Invalid storage handle. Use configureOidcStorage(...) from @ping-identity/rn-storage.', ); }); @@ -239,16 +226,17 @@ describe('OIDC JS API', () => { storage: { id: 'storage-id', kind: 'session', - } as any, - }) + } as unknown as OidcStorageHandle, + }), ).toThrow( - '[@ping-identity/rn-oidc] Invalid storage handle. Use configureOidcStorage(...) from @ping-identity/rn-storage.' + '[@ping-identity/rn-oidc] Invalid storage handle. Use configureOidcStorage(...) from @ping-identity/rn-storage.', ); }); it('creates a web client and forwards authorize options', async () => { const nativeModule = createNativeMock(); - const { createOidcClient, createOidcWebClient } = await loadModule(nativeModule); + const { createOidcClient, createOidcWebClient } = + await loadModule(nativeModule); const client = createOidcClient({ clientId: 'client', @@ -268,7 +256,8 @@ describe('OIDC JS API', () => { it('passes empty options to authorize when none are provided', async () => { const nativeModule = createNativeMock(); - const { createOidcClient, createOidcWebClient } = await loadModule(nativeModule); + const { createOidcClient, createOidcWebClient } = + await loadModule(nativeModule); const client = createOidcClient({ clientId: 'client', @@ -296,7 +285,9 @@ describe('OIDC JS API', () => { logger, }); - expect(logger.debug).toHaveBeenCalledWith(expect.stringContaining('OIDC createClient config')); + expect(logger.debug).toHaveBeenCalledWith( + expect.stringContaining('OIDC createClient config'), + ); expect(logger.info).toHaveBeenCalledWith('OIDC createClient success'); }); @@ -307,7 +298,8 @@ describe('OIDC JS API', () => { throw error; }), }); - const { createOidcClient, createOidcWebClient } = await loadModule(nativeModule); + const { createOidcClient, createOidcWebClient } = + await loadModule(nativeModule); const logger = createJsLogger(); const client = createOidcClient({ @@ -330,7 +322,8 @@ describe('OIDC JS API', () => { throw error; }), }); - const { createOidcClient, createOidcWebClient } = await loadModule(nativeModule); + const { createOidcClient, createOidcWebClient } = + await loadModule(nativeModule); const client = createOidcClient({ clientId: 'client', @@ -344,8 +337,11 @@ describe('OIDC JS API', () => { }); it('returns null from user when hasUser is false', async () => { - const nativeModule = createNativeMock({ hasUser: jest.fn(async () => false) }); - const { createOidcClient, createOidcWebClient } = await loadModule(nativeModule); + const nativeModule = createNativeMock({ + hasUser: jest.fn(async () => false), + }); + const { createOidcClient, createOidcWebClient } = + await loadModule(nativeModule); const client = createOidcClient({ clientId: 'client', @@ -367,7 +363,8 @@ describe('OIDC JS API', () => { throw new Error('hasUser failed'); }), }); - const { createOidcClient, createOidcWebClient } = await loadModule(nativeModule); + const { createOidcClient, createOidcWebClient } = + await loadModule(nativeModule); const client = createOidcClient({ clientId: 'client', @@ -382,7 +379,8 @@ describe('OIDC JS API', () => { it('uses default cache=false for userinfo when not provided', async () => { const nativeModule = createNativeMock(); - const { createOidcClient, createOidcWebClient } = await loadModule(nativeModule); + const { createOidcClient, createOidcWebClient } = + await loadModule(nativeModule); const client = createOidcClient({ clientId: 'client', @@ -402,7 +400,8 @@ describe('OIDC JS API', () => { it('forwards userinfo cache flag when provided', async () => { const nativeModule = createNativeMock(); - const { createOidcClient, createOidcWebClient } = await loadModule(nativeModule); + const { createOidcClient, createOidcWebClient } = + await loadModule(nativeModule); const client = createOidcClient({ clientId: 'client', @@ -458,7 +457,8 @@ describe('OIDC JS API', () => { throw new Error('logout failed'); }), }); - const { createOidcClient, createOidcWebClient } = await loadModule(nativeModule); + const { createOidcClient, createOidcWebClient } = + await loadModule(nativeModule); const client = createOidcClient({ clientId: 'client', @@ -515,7 +515,8 @@ describe('OIDC JS API', () => { throw new Error('user token failed'); }), }); - const { createOidcClient, createOidcWebClient } = await loadModule(nativeModule); + const { createOidcClient, createOidcWebClient } = + await loadModule(nativeModule); const client = createOidcClient({ clientId: 'client', @@ -536,7 +537,8 @@ describe('OIDC JS API', () => { throw new Error('user refresh failed'); }), }); - const { createOidcClient, createOidcWebClient } = await loadModule(nativeModule); + const { createOidcClient, createOidcWebClient } = + await loadModule(nativeModule); const client = createOidcClient({ clientId: 'client', @@ -557,7 +559,8 @@ describe('OIDC JS API', () => { throw new Error('user revoke failed'); }), }); - const { createOidcClient, createOidcWebClient } = await loadModule(nativeModule); + const { createOidcClient, createOidcWebClient } = + await loadModule(nativeModule); const client = createOidcClient({ clientId: 'client', @@ -578,7 +581,8 @@ describe('OIDC JS API', () => { throw new Error('user userinfo failed'); }), }); - const { createOidcClient, createOidcWebClient } = await loadModule(nativeModule); + const { createOidcClient, createOidcWebClient } = + await loadModule(nativeModule); const client = createOidcClient({ clientId: 'client', @@ -644,12 +648,15 @@ describe('OIDC JS API', () => { scopes: ['openid'], }); - await expect(client.userinfo(true)).rejects.toThrow('client userinfo failed'); + await expect(client.userinfo(true)).rejects.toThrow( + 'client userinfo failed', + ); }); it('propagates client token errors with web client path untouched', async () => { const nativeModule = createNativeMock(); - const { createOidcClient, createOidcWebClient } = await loadModule(nativeModule); + const { createOidcClient, createOidcWebClient } = + await loadModule(nativeModule); const client = createOidcClient({ clientId: 'client', diff --git a/packages/oidc/src/index.tsx b/packages/oidc/src/index.tsx index 5b88175eb..9082de4de 100644 --- a/packages/oidc/src/index.tsx +++ b/packages/oidc/src/index.tsx @@ -41,13 +41,12 @@ const noopLogger: LoggerInstance = { * Strip internal token expiry fields before returning tokens to consumers. */ const sanitizeTokens = ( - tokens: { tokenExpiry?: number } & Omit + tokens: { tokenExpiry?: number } & Omit, ): Omit => { const { tokenExpiry: _tokenExpiry, ...rest } = tokens; return rest; }; - /** * Create a native-backed OIDC client. * @@ -62,27 +61,23 @@ const sanitizeTokens = ( export function createOidcClient(config: OidcClientConfig): OidcClient { if (!config.discoveryEndpoint && !config.openId) { throw new Error( - '[@ping-identity/rn-oidc] Missing configuration. Provide discoveryEndpoint or openId.' + '[@ping-identity/rn-oidc] Missing configuration. Provide discoveryEndpoint or openId.', ); } // TODO(iOS SDK 2.x): enforce full OpenID override requirements to match the native iOS behavior. const jsLogger = config.logger ?? noopLogger; - const rawLoggerId = - config.nativeLogger?.id ?? - jsLogger.nativeHandle?.id; + const rawLoggerId = jsLogger.nativeHandle?.id; const loggerId = rawLoggerId?.trim() ? rawLoggerId : undefined; jsLogger.debug( `OIDC createClient config ${JSON.stringify( config, (_key, value) => (typeof value === 'function' ? undefined : value), - 2 - )}` + 2, + )}`, ); // Ignore legacy signOutRedirectUri values if callers pass them via `any`. - const { - signOutRedirectUri: _ignoredSignOutRedirectUri, - ...nativeConfig - } = config as OidcClientConfig & { signOutRedirectUri?: string }; + const { signOutRedirectUri: _ignoredSignOutRedirectUri, ...nativeConfig } = + config as OidcClientConfig & { signOutRedirectUri?: string }; const clientId = getNativeModule().createClient({ ...nativeConfig, storageId: resolveStorageId(config.storage), @@ -116,7 +111,7 @@ export function createOidcClient(config: OidcClientConfig): OidcClient { jsLogger.debug( `OIDC client userinfo requested ${JSON.stringify({ cache: cache ?? false, - })}` + })}`, ); try { return await getNativeModule().clientUserinfo(clientId, cache ?? false); @@ -151,7 +146,9 @@ export function createOidcClient(config: OidcClientConfig): OidcClient { * * @throws Error when a storage object is provided without a valid OIDC handle. */ -function resolveStorageId(value?: OidcClientConfig['storage']): string | undefined { +function resolveStorageId( + value?: OidcClientConfig['storage'], +): string | undefined { if (!value) { return undefined; } @@ -167,7 +164,7 @@ function resolveStorageId(value?: OidcClientConfig['storage']): string | undefin ) { throw new Error( '[@ping-identity/rn-oidc] Invalid storage handle. ' + - 'Use configureOidcStorage(...) from @ping-identity/rn-storage.' + 'Use configureOidcStorage(...) from @ping-identity/rn-storage.', ); } return handle.id; @@ -210,7 +207,7 @@ export function createOidcWebClient(client: OidcClient): OidcWebClient { loggerInstance.debug( `OIDC user userinfo requested ${JSON.stringify({ cache: cache ?? false, - })}` + })}`, ); try { return await getNativeModule().userinfo(webClientId, cache ?? false); @@ -241,9 +238,13 @@ export function createOidcWebClient(client: OidcClient): OidcWebClient { return { id: webClientId, - authorize: async (options?: OidcAuthorizeOptions): Promise => { + authorize: async ( + options?: OidcAuthorizeOptions, + ): Promise => { loggerInstance.info('OIDC authorize requested'); - loggerInstance.debug(`OIDC authorize options ${JSON.stringify(options ?? {})}`); + loggerInstance.debug( + `OIDC authorize options ${JSON.stringify(options ?? {})}`, + ); try { return await getNativeModule().authorize(webClientId, options ?? {}); } catch (error) { diff --git a/packages/oidc/src/types/oidc.types.ts b/packages/oidc/src/types/oidc.types.ts index c74cce15d..ac8e279b3 100644 --- a/packages/oidc/src/types/oidc.types.ts +++ b/packages/oidc/src/types/oidc.types.ts @@ -8,7 +8,6 @@ import type { GenericError, LoggerInstance, - NativeLoggerHandle, OidcStorageHandle, OidcCoreConfig, OidcOpenIdConfiguration as SharedOidcOpenIdConfiguration, @@ -76,15 +75,6 @@ export type OidcClientConfig = Omit & { * Must be created by `@ping-identity/rn-logger` (`logger(...)`). */ logger?: LoggerInstance; - - /** - * Optional native logger handle. - * - * @remarks - * Must be created by `@ping-identity/rn-logger` (`configureLogger(...)`). - */ - nativeLogger?: NativeLoggerHandle; - }; /** diff --git a/packages/oidc/src/useOidc.tsx b/packages/oidc/src/useOidc.tsx index 5ec7ea772..a42c41b96 100644 --- a/packages/oidc/src/useOidc.tsx +++ b/packages/oidc/src/useOidc.tsx @@ -145,7 +145,8 @@ const OIDC_STATE_ERROR_CODE: OidcErrorCode = 'OIDC_STATE_ERROR'; const missingOidcClientError: OidcError = { type: 'state_error', error: OIDC_STATE_ERROR_CODE, - message: 'No OIDC client found. Use useOidc(client) or wrap with .', + message: + 'No OIDC client found. Use useOidc(client) or wrap with .', }; /** @@ -156,12 +157,18 @@ const missingOidcClientError: OidcError = { */ function toOidcError(value: unknown): OidcError { const asRecord = - typeof value === 'object' && value !== null ? (value as Record) : null; + typeof value === 'object' && value !== null + ? (value as Record) + : null; const type = asRecord?.type; const error = asRecord?.error; const message = asRecord?.message; - if (typeof type === 'string' && typeof error === 'string' && typeof message === 'string') { + if ( + typeof type === 'string' && + typeof error === 'string' && + typeof message === 'string' + ) { return { ...asRecord, type, @@ -222,7 +229,10 @@ export type OidcProviderProps = { export function OidcProvider(props: OidcProviderProps): React.ReactElement { const { client, children } = props; const oidc = useOidcState(client); - const value = useMemo(() => ({ client, oidc }), [client, oidc]); + const value = useMemo( + () => ({ client, oidc }), + [client, oidc], + ); return {children}; } @@ -242,8 +252,11 @@ function useOidcState(client: OidcWebClient): OidcHookResult { const [isAuthenticated, setIsAuthenticated] = useState(false); const [user, setUser] = useState(null); const [tokens, setTokens] = useState(null); - const [userInfo, setUserInfo] = useState | null>(null); - const [authorizeResult, setAuthorizeResult] = useState(null); + const [userInfo, setUserInfo] = useState | null>( + null, + ); + const [authorizeResult, setAuthorizeResult] = + useState(null); const [error, setError] = useState(null); /** @@ -282,13 +295,16 @@ function useOidcState(client: OidcWebClient): OidcHookResult { setError(typed); throw typed; } finally { - activeActionCountRef.current = Math.max(0, activeActionCountRef.current - 1); + activeActionCountRef.current = Math.max( + 0, + activeActionCountRef.current - 1, + ); if (activeActionCountRef.current === 0) { setIsLoading(false); } } }, - [] + [], ); /** @@ -368,7 +384,7 @@ function useOidcState(client: OidcWebClient): OidcHookResult { return result; }); }, - [clearAuthState, client, restore, withAction] + [clearAuthState, client, restore, withAction], ); /** @@ -429,7 +445,7 @@ function useOidcState(client: OidcWebClient): OidcHookResult { return nextUserInfo; }); }, - [resolveCurrentUser, withAction] + [resolveCurrentUser, withAction], ); /** @@ -480,7 +496,15 @@ function useOidcState(client: OidcWebClient): OidcHookResult { authorizeResult, error, }), - [authorizeResult, error, isAuthenticated, isLoading, tokens, user, userInfo] + [ + authorizeResult, + error, + isAuthenticated, + isLoading, + tokens, + user, + userInfo, + ], ); const actionsValue = useMemo( @@ -494,7 +518,7 @@ function useOidcState(client: OidcWebClient): OidcHookResult { logout, clear, }), - [authorize, clear, logout, refresh, restore, revoke, token, userinfo] + [authorize, clear, logout, refresh, restore, revoke, token, userinfo], ); return [stateValue, actionsValue]; diff --git a/packages/oidc/tsconfig.build.json b/packages/oidc/tsconfig.build.json index 608e2ea18..ac5e50178 100644 --- a/packages/oidc/tsconfig.build.json +++ b/packages/oidc/tsconfig.build.json @@ -1,4 +1,11 @@ { "extends": "./tsconfig", + "compilerOptions": { + "rootDir": ".", + "paths": { + "@ping-identity/rn-oidc": ["./src/index"], + "@ping-identity/rn-types": ["../types/lib/typescript/src/index"] + } + }, "exclude": ["example", "lib", "**/__tests__/**"] } diff --git a/packages/oidc/tsconfig.json b/packages/oidc/tsconfig.json index d0fc31df3..0f1f33db4 100644 --- a/packages/oidc/tsconfig.json +++ b/packages/oidc/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "rootDir": ".", "paths": { - "@ping-identity/rn-oidc": ["./src/index"] + "@ping-identity/rn-oidc": ["./src/index"], + "@ping-identity/rn-types": ["../types/src/index"] }, "allowUnreachableCode": false, "allowUnusedLabels": false, @@ -26,5 +26,6 @@ "strict": true, "target": "ESNext", "verbatimModuleSyntax": true - } + }, + "exclude": ["lib", "**/__tests__/**"] } diff --git a/packages/storage/jest.config.js b/packages/storage/jest.config.js index b7e817c36..38503a0e1 100644 --- a/packages/storage/jest.config.js +++ b/packages/storage/jest.config.js @@ -11,6 +11,8 @@ module.exports = { transform: { '^.+\\.[jt]sx?$': 'babel-jest', }, + modulePathIgnorePatterns: ['/lib/'], + watchPathIgnorePatterns: ['/lib/'], reporters: [ 'default', ['jest-junit', { diff --git a/packages/storage/src/NativeRNPingStorage.ts b/packages/storage/src/NativeRNPingStorage.ts index 8852f69db..e62a79007 100644 --- a/packages/storage/src/NativeRNPingStorage.ts +++ b/packages/storage/src/NativeRNPingStorage.ts @@ -16,14 +16,14 @@ export enum CacheStrategy { * Provides a fallback mechanism for transient storage failures. */ CACHE_ON_FAILURE = 'cache_on_failure', - + /** * Do not cache data in memory. * All operations read/write directly from/to persistent storage. * Use for sensitive data that should not remain in memory. */ NO_CACHE = 'no_cache', - + /** * Always cache data in memory. * Improves performance by reducing disk I/O. @@ -39,11 +39,11 @@ export type NativeCacheStrategy = 'cache_on_failure' | 'no_cache' | 'cache'; /** * Base configuration for storage instances across platforms. - * + * * This configuration object supports both Android and iOS platforms with * platform-specific options. Android options are nested under the `android` * property, while iOS options are nested under the `ios` property. - * + * * @example * Android-only configuration: * ```typescript @@ -56,7 +56,7 @@ export type NativeCacheStrategy = 'cache_on_failure' | 'no_cache' | 'cache'; * } * }; * ``` - * + * * @example * iOS-only configuration: * ```typescript @@ -68,7 +68,7 @@ export type NativeCacheStrategy = 'cache_on_failure' | 'no_cache' | 'cache'; * } * }; * ``` - * + * * @example * Cross-platform configuration: * ```typescript @@ -88,7 +88,7 @@ export type NativeCacheStrategy = 'cache_on_failure' | 'no_cache' | 'cache'; * } * }; * ``` - * + * * @example * Minimal configuration (uses defaults): * ```typescript @@ -102,60 +102,60 @@ export type NativeCacheStrategy = 'cache_on_failure' | 'no_cache' | 'cache'; export type BaseStorageConfig = { /** * Android-specific configuration options. - * + * * This nested object contains all Android storage settings. * All properties within this object are ignored on iOS. */ android?: { /** * Encryption key alias for Android encrypted storage. - * + * * Used by the Android Keystore system to identify the encryption key. * Each storage instance should have a unique key alias to ensure proper * data isolation. - * + * * **Platform:** Android only - * + * * @defaultValue 'defaultKey' */ keyAlias?: string; /** * File name for Android persistent storage. - * + * * Specifies the SharedPreferences file name where encrypted data is stored. * Different storage instances should use different file names to prevent * data conflicts. - * + * * **Platform:** Android only - * + * * @defaultValue 'secure_prefs' */ fileName?: string; /** * StrongBox preference for Android keystore operations. - * + * * When set to true, the SDK attempts to use hardware-backed StrongBox * for enhanced security. StrongBox is a hardware security module that * provides stronger protection against physical attacks. - * + * * Falls back to standard Keystore if StrongBox is not available on the device. - * + * * **Platform:** Android only - * + * * @defaultValue false */ strongBoxPreferred?: boolean; /** * Cache strategy for storage operations. - * + * * Defines how the SDK handles caching behavior when storing and retrieving data. * Different strategies offer different trade-offs between performance and security. - * + * * **Platform:** Android only - * + * * @see {@link CacheStrategy} for available options */ cacheStrategy?: CacheStrategy; @@ -163,49 +163,49 @@ export type BaseStorageConfig = { /** * iOS-specific configuration options. - * + * * This nested object contains all iOS Keychain-specific settings. * All properties within this object are ignored on Android. - * + * */ ios?: { /** * Keychain account identifier for iOS. - * + * * Specifies the account name used to store items in the iOS Keychain. * This identifier helps organize and isolate different types of data. * Typically uses reverse domain notation. - * + * * **Platform:** iOS only - * + * * @defaultValue 'com.pingidentity.rnsampleapp.keyalias' */ account?: string; /** * Enable encryption for iOS Keychain storage. - * + * * Controls whether data is encrypted before being stored in the Keychain: * - `true`: Uses an Encryptor for additional encryption layer * - `false`: Uses NoEncryptor (relies on Keychain's native encryption only) - * + * * **Platform:** iOS only - * + * * @defaultValue true - * + * */ encryptor?: boolean; /** * Toggle iOS cache behavior when native storage is unavailable. - * + * * Controls in-memory caching fallback when Keychain access fails or is unavailable. * This can be useful for development or testing scenarios. - * + * * **Platform:** iOS only - * + * * @defaultValue undefined (uses platform defaults) - * + * */ cacheable?: boolean; }; @@ -213,16 +213,16 @@ export type BaseStorageConfig = { /** * Native storage configuration passed to the native module. - * + * * This is a flattened version of {@link StorageConfig} where iOS-specific options * and Android-specific options are merged into the top level for easier consumption * by native code. - * + * * This type is primarily for internal use and represents the structure that * the native bridge expects to receive. - * + * * @internal - * + * * @example * ```typescript * // Flattened configuration @@ -255,24 +255,24 @@ export type NativeStorageConfig = { /** * Native module specification for RNPingStorage. - * + * * Defines the interface contract for the native storage module. * Extends TurboModule for New Architecture (Fabric) support. - * + * * This interface is implemented by native code on both iOS and Android platforms. - * + * * @interface */ export interface Spec extends TurboModule { /** * Register a session storage configuration. - * + * * Creates and stores a session storage configuration in native code, * returning a unique identifier for later retrieval. - * + * * @param config - Flattened storage configuration object * @returns A unique identifier string for the registered storage configuration - * + * * @example * ```typescript * const id = nativeModule.registerSessionStorage({ @@ -286,13 +286,13 @@ export interface Spec extends TurboModule { /** * Register an OIDC storage configuration. - * + * * Creates and stores an OIDC storage configuration in native code, * returning a unique identifier for later retrieval. - * + * * @param config - Flattened storage configuration object * @returns A unique identifier string for the registered storage configuration - * + * * @example * ```typescript * const id = nativeModule.registerOidcStorage({ @@ -307,13 +307,13 @@ export interface Spec extends TurboModule { /** * Resolve a session storage configuration by identifier. - * + * * Retrieves a previously registered session storage configuration * from native code using its unique identifier. - * + * * @param id - Registered storage configuration identifier * @returns A storage configuration object - * + * * @example * ```typescript * const config = nativeModule.configureSessionStorage('session_storage_abc123'); @@ -324,13 +324,13 @@ export interface Spec extends TurboModule { /** * Resolve an OIDC storage configuration by identifier. - * + * * Retrieves a previously registered OIDC storage configuration * from native code using its unique identifier. - * + * * @param id - Registered storage configuration identifier * @returns A storage configuration object - * + * * @example * ```typescript * const config = nativeModule.configureOidcStorage('oidc_storage_xyz789'); @@ -361,7 +361,7 @@ export function getNativeModule(): Spec { '[@ping-identity/rn-storage] Native module RNPingStorage not found.\n' + 'Ensure the library is linked correctly and the app has been rebuilt.\n' + 'Available NativeModules: ' + - JSON.stringify(Object.keys(NativeModules)) + JSON.stringify(Object.keys(NativeModules)), ); } diff --git a/packages/storage/src/__tests__/index.test.tsx b/packages/storage/src/__tests__/index.test.tsx index 7dbe9994e..fd274f385 100644 --- a/packages/storage/src/__tests__/index.test.tsx +++ b/packages/storage/src/__tests__/index.test.tsx @@ -4,24 +4,13 @@ * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. */ +import type { LoggerInstance } from '@ping-identity/rn-types'; import { CacheStrategy, configureSessionStorage, configureOidcStorage, -} from "../index"; - -const mockLogger = { - nativeHandle: { id: "native-none-id" }, - changeLevel: jest.fn(), - error: jest.fn(), - warn: jest.fn(), - info: jest.fn(), - debug: jest.fn(), -}; - -jest.mock("@ping-identity/rn-logger", () => ({ - logger: jest.fn(() => mockLogger), -})); +} from '../index'; +import type { StorageConfig } from '../index'; // Mock the Native Module const mockNativeRNPingStorage = { @@ -31,186 +20,173 @@ const mockNativeRNPingStorage = { configureOidcStorage: jest.fn(), }; -jest.mock("../NativeRNPingStorage", () => ({ +jest.mock('../NativeRNPingStorage', () => ({ getNativeModule: () => mockNativeRNPingStorage, CacheStrategy: { - CACHE_ON_FAILURE: "cache_on_failure", - NO_CACHE: "no_cache", - CACHE: "cache", + CACHE_ON_FAILURE: 'cache_on_failure', + NO_CACHE: 'no_cache', + CACHE: 'cache', }, })); -describe("Storage API", () => { +describe('Storage API', () => { beforeEach(() => { jest.clearAllMocks(); - mockLogger.nativeHandle.id = "native-none-id"; }); - describe("Factory functions", () => { - it("configureSessionStorage returns the config", () => { - mockNativeRNPingStorage.registerSessionStorage.mockReturnValue("session-id"); + describe('Factory functions', () => { + it('configureSessionStorage returns the config', () => { + mockNativeRNPingStorage.registerSessionStorage.mockReturnValue( + 'session-id', + ); mockNativeRNPingStorage.configureSessionStorage.mockReturnValue({}); const instance = configureSessionStorage({ android: { - keyAlias: "session-key", + keyAlias: 'session-key', }, }); - expect(mockNativeRNPingStorage.registerSessionStorage).toHaveBeenCalledWith({ - loggerId: "native-none-id", - keyAlias: "session-key", + expect( + mockNativeRNPingStorage.registerSessionStorage, + ).toHaveBeenCalledWith({ + keyAlias: 'session-key', }); - expect(mockNativeRNPingStorage.configureSessionStorage).toHaveBeenCalledWith("session-id"); - expect(instance).toEqual({ id: "session-id", kind: "session" }); + expect( + mockNativeRNPingStorage.configureSessionStorage, + ).toHaveBeenCalledWith('session-id'); + expect(instance).toEqual({ id: 'session-id', kind: 'session' }); }); - it("configureSessionStorage can be created with all config options", () => { + it('configureSessionStorage can be created with all config options', () => { const config = { android: { - keyAlias: "session-key", - fileName: "session.dat", + keyAlias: 'session-key', + fileName: 'session.dat', strongBoxPreferred: true, cacheStrategy: CacheStrategy.CACHE_ON_FAILURE, }, ios: { - account: "com.example.session", + account: 'com.example.session', encryptor: true, cacheable: true, }, }; - mockNativeRNPingStorage.registerSessionStorage.mockReturnValue("session-id"); + mockNativeRNPingStorage.registerSessionStorage.mockReturnValue( + 'session-id', + ); mockNativeRNPingStorage.configureSessionStorage.mockReturnValue({ - keyAlias: "session-key", - fileName: "session.dat", + keyAlias: 'session-key', + fileName: 'session.dat', strongBoxPreferred: true, - cacheStrategy: "cache_on_failure", - account: "com.example.session", + cacheStrategy: 'cache_on_failure', + account: 'com.example.session', encryptor: true, cacheable: true, }); expect(configureSessionStorage(config)).toEqual({ - id: "session-id", - kind: "session", + id: 'session-id', + kind: 'session', ...config, }); }); - it("configureSessionStorage validates config", () => { - expect(() => configureSessionStorage(null as any)).toThrow(/Missing configuration/); + it('configureSessionStorage validates config', () => { + expect(() => + configureSessionStorage(null as unknown as StorageConfig), + ).toThrow(/Missing configuration/); }); - it("configureOidcStorage returns the config", () => { - mockNativeRNPingStorage.registerOidcStorage.mockReturnValue("oidc-id"); + it('configureOidcStorage returns the config', () => { + mockNativeRNPingStorage.registerOidcStorage.mockReturnValue('oidc-id'); mockNativeRNPingStorage.configureOidcStorage.mockReturnValue({}); const instance = configureOidcStorage({ android: { - keyAlias: "oidc-key", + keyAlias: 'oidc-key', }, }); expect(mockNativeRNPingStorage.registerOidcStorage).toHaveBeenCalledWith({ - loggerId: "native-none-id", - keyAlias: "oidc-key", + keyAlias: 'oidc-key', }); - expect(mockNativeRNPingStorage.configureOidcStorage).toHaveBeenCalledWith("oidc-id"); - expect(instance).toEqual({ id: "oidc-id", kind: "oidc" }); + expect(mockNativeRNPingStorage.configureOidcStorage).toHaveBeenCalledWith( + 'oidc-id', + ); + expect(instance).toEqual({ id: 'oidc-id', kind: 'oidc' }); }); - it("configureOidcStorage can be created with all config options", () => { + it('configureOidcStorage can be created with all config options', () => { const config = { android: { - keyAlias: "oidc-key", - fileName: "oidc.dat", + keyAlias: 'oidc-key', + fileName: 'oidc.dat', strongBoxPreferred: false, cacheStrategy: CacheStrategy.NO_CACHE, }, ios: { - account: "com.example.oidc", + account: 'com.example.oidc', encryptor: false, cacheable: false, }, }; - mockNativeRNPingStorage.registerOidcStorage.mockReturnValue("oidc-id"); + mockNativeRNPingStorage.registerOidcStorage.mockReturnValue('oidc-id'); mockNativeRNPingStorage.configureOidcStorage.mockReturnValue({ - keyAlias: "oidc-key", - fileName: "oidc.dat", + keyAlias: 'oidc-key', + fileName: 'oidc.dat', strongBoxPreferred: false, - cacheStrategy: "no_cache", - account: "com.example.oidc", + cacheStrategy: 'no_cache', + account: 'com.example.oidc', encryptor: false, cacheable: false, }); expect(configureOidcStorage(config)).toEqual({ - id: "oidc-id", - kind: "oidc", + id: 'oidc-id', + kind: 'oidc', ...config, }); }); - it("configureOidcStorage validates config", () => { - expect(() => configureOidcStorage(null as any)).toThrow(/Missing configuration/); + it('configureOidcStorage validates config', () => { + expect(() => + configureOidcStorage(null as unknown as StorageConfig), + ).toThrow(/Missing configuration/); }); - it("passes loggerId from logger options", () => { + it('passes loggerId from logger options', () => { const logger = { - nativeHandle: { id: "native-logger-id" }, + nativeHandle: { id: 'native-logger-id' }, changeLevel: jest.fn(), error: jest.fn(), warn: jest.fn(), info: jest.fn(), debug: jest.fn(), }; - mockNativeRNPingStorage.registerSessionStorage.mockReturnValue("session-id"); + mockNativeRNPingStorage.registerSessionStorage.mockReturnValue( + 'session-id', + ); mockNativeRNPingStorage.configureSessionStorage.mockReturnValue({}); configureSessionStorage( { - android: { keyAlias: "session-key" }, + android: { keyAlias: 'session-key' }, }, - { logger } + { logger }, ); - expect(mockNativeRNPingStorage.registerSessionStorage).toHaveBeenCalledWith({ - loggerId: "native-logger-id", - keyAlias: "session-key", + expect( + mockNativeRNPingStorage.registerSessionStorage, + ).toHaveBeenCalledWith({ + loggerId: 'native-logger-id', + keyAlias: 'session-key', }); }); - it("prefers nativeLogger over logger.nativeHandle", () => { - const logger = { - nativeHandle: { id: "logger-native-id" }, - changeLevel: jest.fn(), - error: jest.fn(), - warn: jest.fn(), - info: jest.fn(), - debug: jest.fn(), - }; - mockNativeRNPingStorage.registerOidcStorage.mockReturnValue("oidc-id"); - mockNativeRNPingStorage.configureOidcStorage.mockReturnValue({}); - - configureOidcStorage( - { - android: { keyAlias: "oidc-key" }, - }, - { - logger, - nativeLogger: { id: "explicit-native-id" }, - } - ); - - expect(mockNativeRNPingStorage.registerOidcStorage).toHaveBeenCalledWith({ - loggerId: "explicit-native-id", - keyAlias: "oidc-key", - }); - }); - - it("falls back to default logger id when options.logger has no native handle id", () => { + it('omits loggerId when options.logger has no native handle id', () => { const logger = { nativeHandle: {}, changeLevel: jest.fn(), @@ -219,72 +195,52 @@ describe("Storage API", () => { info: jest.fn(), debug: jest.fn(), }; - mockNativeRNPingStorage.registerSessionStorage.mockReturnValue("session-id"); + mockNativeRNPingStorage.registerSessionStorage.mockReturnValue( + 'session-id', + ); mockNativeRNPingStorage.configureSessionStorage.mockReturnValue({}); configureSessionStorage( { - android: { keyAlias: "session-key" }, + android: { keyAlias: 'session-key' }, }, - { logger: logger as any } + { logger: logger as unknown as LoggerInstance }, ); - expect(mockNativeRNPingStorage.registerSessionStorage).toHaveBeenCalledWith({ - loggerId: "native-none-id", - keyAlias: "session-key", + expect( + mockNativeRNPingStorage.registerSessionStorage, + ).toHaveBeenCalledWith({ + keyAlias: 'session-key', }); }); - it("omits loggerId when explicit nativeLogger id is blank", () => { + it('logs debug and info during session storage configuration', () => { const logger = { - nativeHandle: { id: "logger-native-id" }, + nativeHandle: { id: 'native-logger-id' }, changeLevel: jest.fn(), error: jest.fn(), warn: jest.fn(), info: jest.fn(), debug: jest.fn(), }; - mockNativeRNPingStorage.registerOidcStorage.mockReturnValue("oidc-id"); - mockNativeRNPingStorage.configureOidcStorage.mockReturnValue({}); - - configureOidcStorage( - { - android: { keyAlias: "oidc-key" }, - }, - { - logger, - nativeLogger: { id: "" }, - } + mockNativeRNPingStorage.registerSessionStorage.mockReturnValue( + 'session-id', ); - - expect(mockNativeRNPingStorage.registerOidcStorage).toHaveBeenCalledWith({ - keyAlias: "oidc-key", - }); - }); - - it("logs debug and info during session storage configuration", () => { - const logger = { - nativeHandle: { id: "native-logger-id" }, - changeLevel: jest.fn(), - error: jest.fn(), - warn: jest.fn(), - info: jest.fn(), - debug: jest.fn(), - }; - mockNativeRNPingStorage.registerSessionStorage.mockReturnValue("session-id"); mockNativeRNPingStorage.configureSessionStorage.mockReturnValue({}); configureSessionStorage( { - android: { keyAlias: "session-key" }, + android: { keyAlias: 'session-key' }, }, - { logger } + { logger }, ); expect(logger.debug).toHaveBeenCalledWith( - expect.stringContaining("Storage configureSessionStorage requested") + expect.stringContaining('Storage configureSessionStorage requested'), + ); + expect(logger.info).toHaveBeenCalledWith( + 'Storage configureSessionStorage success', ); - expect(logger.info).toHaveBeenCalledWith("Storage configureSessionStorage success"); }); }); }); diff --git a/packages/storage/src/index.tsx b/packages/storage/src/index.tsx index 1f7f711e3..41d0e6475 100644 --- a/packages/storage/src/index.tsx +++ b/packages/storage/src/index.tsx @@ -4,21 +4,21 @@ * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. */ -import { getNativeModule } from "./NativeRNPingStorage"; +import { getNativeModule } from './NativeRNPingStorage'; import type { BaseStorageConfig, NativeCacheStrategy, NativeStorageConfig, -} from "./NativeRNPingStorage"; -import { CacheStrategy } from "./types"; -import type { LoggerInstance } from "@ping-identity/rn-types"; +} from './NativeRNPingStorage'; +import { CacheStrategy } from './types'; +import type { LoggerInstance } from '@ping-identity/rn-types'; import type { OidcStorage, SessionStorage, StorageConfig, StorageError, StorageLoggerOptions, -} from "./types"; +} from './types'; export type { OidcStorage, @@ -26,45 +26,30 @@ export type { StorageConfig, StorageError, StorageLoggerOptions, -} from "./types"; -export { CacheStrategy } from "./types"; +} from './types'; +export { CacheStrategy } from './types'; /** - * Cached default logger used when callers do not provide one. + * No-op logger used when callers do not provide one. */ -let defaultLoggerInstance: LoggerInstance | null = null; - -const createNoopLogger = (): LoggerInstance => ({ - nativeHandle: { id: "native-none-id" }, +const noopLogger: LoggerInstance = { + nativeHandle: { id: '' }, changeLevel: () => {}, error: () => {}, warn: () => {}, info: () => {}, debug: () => {}, -}); - -/** - * Lazily initialize and return the default logger instance. - */ -const getDefaultLogger = (): LoggerInstance => { - if (!defaultLoggerInstance) { - defaultLoggerInstance = createNoopLogger(); - } - return defaultLoggerInstance; }; /** * Resolve JS logger instance and native logger identifier for bridge calls. */ const resolveLogger = ( - options?: StorageLoggerOptions + options?: StorageLoggerOptions, ): { logger: LoggerInstance; loggerId?: string } => { - const logger = options?.logger ?? getDefaultLogger(); - const loggerId = - options?.nativeLogger?.id ?? - logger.nativeHandle?.id ?? - getDefaultLogger().nativeHandle?.id; - + const logger = options?.logger ?? noopLogger; + const rawLoggerId = logger.nativeHandle?.id; + const loggerId = rawLoggerId?.trim() ? rawLoggerId : undefined; return { logger, loggerId }; }; @@ -79,11 +64,11 @@ const resolveLogger = ( function toNativeCacheStrategy(strategy: CacheStrategy): NativeCacheStrategy { switch (strategy) { case CacheStrategy.CACHE_ON_FAILURE: - return "cache_on_failure"; + return 'cache_on_failure'; case CacheStrategy.NO_CACHE: - return "no_cache"; + return 'no_cache'; case CacheStrategy.CACHE: - return "cache"; + return 'cache'; default: { const exhaustiveCheck: never = strategy; return exhaustiveCheck; @@ -101,11 +86,11 @@ function toNativeCacheStrategy(strategy: CacheStrategy): NativeCacheStrategy { */ function fromNativeCacheStrategy(strategy: NativeCacheStrategy): CacheStrategy { switch (strategy) { - case "cache_on_failure": + case 'cache_on_failure': return CacheStrategy.CACHE_ON_FAILURE; - case "no_cache": + case 'no_cache': return CacheStrategy.NO_CACHE; - case "cache": + case 'cache': return CacheStrategy.CACHE; default: { const exhaustiveCheck: never = strategy; @@ -116,10 +101,10 @@ function fromNativeCacheStrategy(strategy: NativeCacheStrategy): CacheStrategy { /** * Validates the storage configuration. - * + * * @param config - The storage configuration to validate * @throws {StorageError} If the configuration is missing or invalid - * + * * @internal */ function validateStorageConfig(config: StorageConfig) { @@ -141,12 +126,12 @@ function validateStorageConfig(config: StorageConfig) { * * @param config - The base storage configuration * @returns The native storage configuration object with flattened platform options - * + * * @internal */ function buildNativeConfig( config: StorageConfig, - loggerId?: string + loggerId?: string, ): NativeStorageConfig { return { ...(loggerId ? { loggerId } : {}), @@ -159,8 +144,12 @@ function buildNativeConfig( ? { cacheStrategy: toNativeCacheStrategy(config.android.cacheStrategy) } : {}), ...(config.ios?.account ? { account: config.ios.account } : {}), - ...(config.ios?.encryptor !== undefined ? { encryptor: config.ios.encryptor } : {}), - ...(config.ios?.cacheable !== undefined ? { cacheable: config.ios.cacheable } : {}), + ...(config.ios?.encryptor !== undefined + ? { encryptor: config.ios.encryptor } + : {}), + ...(config.ios?.cacheable !== undefined + ? { cacheable: config.ios.cacheable } + : {}), }; } @@ -170,17 +159,22 @@ function buildNativeConfig( * * @param nativeResult - The native storage configuration to validate * @throws {StorageError} If the native result is not null, undefined, or an object - * + * * @internal */ function validateNormalizedResult( - nativeResult: NativeStorageConfig | null | undefined + nativeResult: NativeStorageConfig | null | undefined, ) { - if (nativeResult !== null && nativeResult !== undefined && typeof nativeResult !== "object") { + if ( + nativeResult !== null && + nativeResult !== undefined && + typeof nativeResult !== 'object' + ) { const error: StorageError = { type: 'parse_error', error: 'STORAGE_INVALID_RESULT', - message: '[@ping-identity/rn-storage] Failed to resolve storage configuration.', + message: + '[@ping-identity/rn-storage] Failed to resolve storage configuration.', }; throw error; } @@ -193,7 +187,7 @@ function validateNormalizedResult( * * @param parsed - The parsed native storage configuration * @returns iOS configuration object or undefined if no iOS values exist - * + * * @internal */ function buildIosConfig(parsed: NativeStorageConfig) { @@ -219,7 +213,7 @@ function buildIosConfig(parsed: NativeStorageConfig) { * * @param parsed - The parsed native storage configuration * @returns Android configuration object or undefined if no Android values exist - * + * * @internal */ function buildAndroidConfig(parsed: NativeStorageConfig) { @@ -252,11 +246,11 @@ function buildAndroidConfig(parsed: NativeStorageConfig) { * @param nativeResult - The native storage configuration to normalize * @returns Normalized StorageConfig with platform-specific options properly nested * @throws {StorageError} If the native result is not a valid configuration object - * + * * @internal */ function normalizeStorageConfig( - nativeResult: NativeStorageConfig | null | undefined + nativeResult: NativeStorageConfig | null | undefined, ): StorageConfig { validateNormalizedResult(nativeResult); @@ -281,11 +275,11 @@ function normalizeStorageConfig( */ function createSessionStorageHandle( id: string, - config: BaseStorageConfig + config: BaseStorageConfig, ): SessionStorage { return { id, - kind: "session", + kind: 'session', ...config, } as SessionStorage; } @@ -301,25 +295,25 @@ function createSessionStorageHandle( */ function createOidcStorageHandle( id: string, - config: BaseStorageConfig + config: BaseStorageConfig, ): OidcStorage { return { id, - kind: "oidc", + kind: 'oidc', ...config, } as OidcStorage; } /** * Registers and resolves a session storage handle. - * + * * This function handles registration internally and returns a normalized * storage configuration that can be passed to other modules or SDKs. * * @param config - Storage configuration parameters with platform-specific options * @returns A branded SessionStorage handle with native storage id metadata * @throws {Error} If the configuration is missing or invalid - * + * * @example * ```typescript * const sessionStorage = configureSessionStorage({ @@ -329,7 +323,7 @@ function createOidcStorageHandle( * strongBoxPreferred: true * } * }); - * + * * // Pass to Journey SDK * // initJourney({ sessionStorage, ... }); * ``` @@ -337,40 +331,39 @@ function createOidcStorageHandle( */ export function configureSessionStorage( config: StorageConfig, - options?: StorageLoggerOptions + options?: StorageLoggerOptions, ): SessionStorage { const { logger, loggerId } = resolveLogger(options); - logger.debug( - `Storage configureSessionStorage requested` - ); + logger.debug(`Storage configureSessionStorage requested`); validateStorageConfig(config); const NativeRNPingStorage = getNativeModule(); try { const storageId = NativeRNPingStorage.registerSessionStorage( - buildNativeConfig(config, loggerId) - ); - logger.debug( - `Storage configureSessionStorage registered` + buildNativeConfig(config, loggerId), ); + logger.debug(`Storage configureSessionStorage registered`); const result = NativeRNPingStorage.configureSessionStorage(storageId); - logger.info("Storage configureSessionStorage success"); - return createSessionStorageHandle(storageId, normalizeStorageConfig(result)); + logger.info('Storage configureSessionStorage success'); + return createSessionStorageHandle( + storageId, + normalizeStorageConfig(result), + ); } catch (error) { - logger.error("Storage configureSessionStorage failed"); + logger.error('Storage configureSessionStorage failed'); throw error; } } /** * Registers and resolves an OIDC storage handle. - * + * * This function handles registration internally and returns a normalized * storage configuration that can be passed to other modules or SDKs. * * @param config - Storage configuration parameters with platform-specific options * @returns A branded OidcStorage handle with native storage id metadata * @throws {Error} If the configuration is missing or invalid - * + * * @example * ```typescript * const oidcStorage = configureOidcStorage({ @@ -384,7 +377,7 @@ export function configureSessionStorage( * cacheable: false * } * }); - * + * * // Pass to OIDC configuration * // configureOidc({ storage: oidcStorage, ... }); * ``` @@ -392,26 +385,22 @@ export function configureSessionStorage( */ export function configureOidcStorage( config: StorageConfig, - options?: StorageLoggerOptions + options?: StorageLoggerOptions, ): OidcStorage { const { logger, loggerId } = resolveLogger(options); - logger.debug( - `Storage configureOidcStorage requested` - ); + logger.debug(`Storage configureOidcStorage requested`); validateStorageConfig(config); const NativeRNPingStorage = getNativeModule(); try { const storageId = NativeRNPingStorage.registerOidcStorage( - buildNativeConfig(config, loggerId) - ); - logger.debug( - `Storage configureOidcStorage registered` + buildNativeConfig(config, loggerId), ); + logger.debug(`Storage configureOidcStorage registered`); const result = NativeRNPingStorage.configureOidcStorage(storageId); - logger.info("Storage configureOidcStorage success"); + logger.info('Storage configureOidcStorage success'); return createOidcStorageHandle(storageId, normalizeStorageConfig(result)); } catch (error) { - logger.error("Storage configureOidcStorage failed"); + logger.error('Storage configureOidcStorage failed'); throw error; } } diff --git a/packages/storage/src/types/storage.types.ts b/packages/storage/src/types/storage.types.ts index 9fcda0098..02401b15d 100644 --- a/packages/storage/src/types/storage.types.ts +++ b/packages/storage/src/types/storage.types.ts @@ -4,9 +4,9 @@ * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. */ -import type { BaseStorageConfig } from "../NativeRNPingStorage"; -import { CacheStrategy } from "../NativeRNPingStorage"; -import type { GenericError, LoggerInstance, NativeLoggerHandle } from '@ping-identity/rn-types'; +import type { BaseStorageConfig } from '../NativeRNPingStorage'; +import { CacheStrategy } from '../NativeRNPingStorage'; +import type { GenericError, LoggerInstance } from '@ping-identity/rn-types'; import type { OidcStorageHandle, SessionStorageHandle, @@ -14,12 +14,12 @@ import type { /** * Storage configuration type for Journey session data. - * + * * Opaque handle returned by {@link configureSessionStorage}. * * Used by native-backed modules to resolve a previously registered * session storage configuration. - * + * * Session storage typically stores temporary authentication state and session * data during Journey flows. * @@ -29,19 +29,19 @@ import type { * * @see {@link BaseStorageConfig} for configuration input options * @see {@link configureSessionStorage} to register and resolve a configuration - * + * * @example * Basic usage: * ```typescript * import { configureSessionStorage } from '@ping-identity/rn-storage'; - * + * * const sessionStorage: SessionStorage = configureSessionStorage({ * android: { * keyAlias: 'session_key', * fileName: 'session_data' * } * }); - * + * * // Use with Journey SDK * // initJourney({ sessionStorage, ... }); * ``` @@ -50,12 +50,12 @@ export type SessionStorage = BaseStorageConfig & SessionStorageHandle; /** * Storage configuration type for OIDC tokens and authorization state. - * + * * Opaque handle returned by {@link configureOidcStorage}. * * Used by native-backed modules to resolve a previously registered OIDC * storage configuration for token and authorization state persistence. - * + * * OIDC storage is critical for securely managing authentication tokens and * should use appropriate security settings for your application's requirements. * @@ -65,23 +65,23 @@ export type SessionStorage = BaseStorageConfig & SessionStorageHandle; * * @see {@link BaseStorageConfig} for configuration input options * @see {@link configureOidcStorage} to register and resolve a configuration - * + * * @example * Basic usage: * ```typescript * import { configureOidcStorage } from '@ping-identity/rn-storage'; - * + * * const oidcStorage: OidcStorage = configureOidcStorage({ * android: { * keyAlias: 'oidc_key', * fileName: 'oidc_tokens' * } * }); - * + * * // Use with OIDC configuration * // configureOidc({ storage: oidcStorage, ... }); * ``` - * + * */ export type OidcStorage = BaseStorageConfig & OidcStorageHandle; @@ -103,11 +103,6 @@ export type StorageLoggerOptions = { * Optional JavaScript logger instance. */ logger?: LoggerInstance; - - /** - * Optional native logger handle. - */ - nativeLogger?: NativeLoggerHandle; }; export { CacheStrategy }; diff --git a/packages/storage/tsconfig.build.json b/packages/storage/tsconfig.build.json index 3c0636adf..a2840893e 100644 --- a/packages/storage/tsconfig.build.json +++ b/packages/storage/tsconfig.build.json @@ -1,4 +1,11 @@ { "extends": "./tsconfig", - "exclude": ["example", "lib"] + "compilerOptions": { + "rootDir": ".", + "paths": { + "@ping-identity/rn-storage": ["./src/index"], + "@ping-identity/rn-types": ["../types/lib/typescript/src/index"] + } + }, + "exclude": ["lib", "**/__tests__/**"] } diff --git a/packages/storage/tsconfig.json b/packages/storage/tsconfig.json index ee4184221..82d0d07df 100644 --- a/packages/storage/tsconfig.json +++ b/packages/storage/tsconfig.json @@ -1,8 +1,8 @@ { "compilerOptions": { - "rootDir": ".", "paths": { - "@ping-identity/rn-storage": ["./src/index"] + "@ping-identity/rn-storage": ["./src/index"], + "@ping-identity/rn-types": ["../types/src/index"] }, "allowUnreachableCode": false, "allowUnusedLabels": false, @@ -26,5 +26,6 @@ "strict": true, "target": "ESNext", "verbatimModuleSyntax": true - } + }, + "exclude": ["lib", "**/__tests__/**"] } diff --git a/packages/types/src/handles.types.ts b/packages/types/src/handles.types.ts index 20f11a09c..8fb280b0f 100644 --- a/packages/types/src/handles.types.ts +++ b/packages/types/src/handles.types.ts @@ -19,7 +19,7 @@ export type LogMessage = unknown; * Shared native logger handle contract. * * @remarks - * Returned by `configureLogger(...)` from `@ping-identity/rn-logger`. + * Accessible via `logger(...).nativeHandle` from `@ping-identity/rn-logger`. */ export type NativeLoggerHandle = { /** diff --git a/packages/types/tsconfig.build.json b/packages/types/tsconfig.build.json index b681c8db9..701226171 100644 --- a/packages/types/tsconfig.build.json +++ b/packages/types/tsconfig.build.json @@ -1,4 +1,7 @@ { - "extends": "./tsconfig.json", - "exclude": ["example", "lib", "**/__tests__/**"] + "extends": "./tsconfig", + "compilerOptions": { + "outDir": "./lib/typescript" + }, + "exclude": ["lib", "**/__tests__/**"] } diff --git a/packages/types/tsconfig.json b/packages/types/tsconfig.json index 5cbc1e88c..d3f5e71eb 100644 --- a/packages/types/tsconfig.json +++ b/packages/types/tsconfig.json @@ -25,5 +25,6 @@ "strict": true, "target": "ESNext", "verbatimModuleSyntax": true - } + }, + "exclude": ["lib", "**/__tests__/**"] } diff --git a/turbo.json b/turbo.json index 68e1d4230..60b037db9 100644 --- a/turbo.json +++ b/turbo.json @@ -14,7 +14,7 @@ "typecheck": { "inputs": ["$TURBO_DEFAULT$", "src/**/*.ts", "src/**/*.tsx"], "outputs": [], - "dependsOn": ["^typecheck"] + "dependsOn": ["^build", "^typecheck"] }, "prettier": { "inputs": ["$TURBO_DEFAULT$", "src/**/*.ts", "src/**/*.tsx", "src/**/*.js", "src/**/*.jsx"], diff --git a/yarn.lock b/yarn.lock index d2ab7ee4f..bedc9c14e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -50,6 +50,17 @@ __metadata: languageName: node linkType: hard +"@babel/code-frame@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/code-frame@npm:7.29.0" + dependencies: + "@babel/helper-validator-identifier": "npm:^7.28.5" + js-tokens: "npm:^4.0.0" + picocolors: "npm:^1.1.1" + checksum: 10c0/d34cc504e7765dfb576a663d97067afb614525806b5cad1a5cc1a7183b916fec8ff57fa233585e3926fd5a9e6b31aae6df91aa81ae9775fb7a28f658d3346f0d + languageName: node + linkType: hard + "@babel/compat-data@npm:^7.27.2, @babel/compat-data@npm:^7.27.7, @babel/compat-data@npm:^7.28.5": version: 7.28.5 resolution: "@babel/compat-data@npm:7.28.5" @@ -87,6 +98,29 @@ __metadata: languageName: node linkType: hard +"@babel/core@npm:^7.24.4": + version: 7.29.0 + resolution: "@babel/core@npm:7.29.0" + dependencies: + "@babel/code-frame": "npm:^7.29.0" + "@babel/generator": "npm:^7.29.0" + "@babel/helper-compilation-targets": "npm:^7.28.6" + "@babel/helper-module-transforms": "npm:^7.28.6" + "@babel/helpers": "npm:^7.28.6" + "@babel/parser": "npm:^7.29.0" + "@babel/template": "npm:^7.28.6" + "@babel/traverse": "npm:^7.29.0" + "@babel/types": "npm:^7.29.0" + "@jridgewell/remapping": "npm:^2.3.5" + convert-source-map: "npm:^2.0.0" + debug: "npm:^4.1.0" + gensync: "npm:^1.0.0-beta.2" + json5: "npm:^2.2.3" + semver: "npm:^6.3.1" + checksum: 10c0/5127d2e8e842ae409e11bcbb5c2dff9874abf5415e8026925af7308e903f4f43397341467a130490d1a39884f461bc2b67f3063bce0be44340db89687fd852aa + languageName: node + linkType: hard + "@babel/core@npm:^7.27.4": version: 7.28.6 resolution: "@babel/core@npm:7.28.6" @@ -150,6 +184,19 @@ __metadata: languageName: node linkType: hard +"@babel/generator@npm:^7.29.0": + version: 7.29.1 + resolution: "@babel/generator@npm:7.29.1" + dependencies: + "@babel/parser": "npm:^7.29.0" + "@babel/types": "npm:^7.29.0" + "@jridgewell/gen-mapping": "npm:^0.3.12" + "@jridgewell/trace-mapping": "npm:^0.3.28" + jsesc: "npm:^3.0.2" + checksum: 10c0/349086e6876258ef3fb2823030fee0f6c0eb9c3ebe35fc572e16997f8c030d765f636ddc6299edae63e760ea6658f8ee9a2edfa6d6b24c9a80c917916b973551 + languageName: node + linkType: hard + "@babel/helper-annotate-as-pure@npm:^7.27.1, @babel/helper-annotate-as-pure@npm:^7.27.3": version: 7.27.3 resolution: "@babel/helper-annotate-as-pure@npm:7.27.3" @@ -408,6 +455,17 @@ __metadata: languageName: node linkType: hard +"@babel/parser@npm:^7.24.4, @babel/parser@npm:^7.29.0": + version: 7.29.2 + resolution: "@babel/parser@npm:7.29.2" + dependencies: + "@babel/types": "npm:^7.29.0" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/e5a4e69e3ac7acdde995f37cf299a68458cfe7009dff66bd0962fd04920bef287201169006af365af479c08ff216bfefbb595e331f87f6ae7283858aebbc3317 + languageName: node + linkType: hard + "@babel/parser@npm:^7.28.6": version: 7.28.6 resolution: "@babel/parser@npm:7.28.6" @@ -1650,6 +1708,21 @@ __metadata: languageName: node linkType: hard +"@babel/traverse@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/traverse@npm:7.29.0" + dependencies: + "@babel/code-frame": "npm:^7.29.0" + "@babel/generator": "npm:^7.29.0" + "@babel/helper-globals": "npm:^7.28.0" + "@babel/parser": "npm:^7.29.0" + "@babel/template": "npm:^7.28.6" + "@babel/types": "npm:^7.29.0" + debug: "npm:^4.3.1" + checksum: 10c0/f63ef6e58d02a9fbf3c0e2e5f1c877da3e0bc57f91a19d2223d53e356a76859cbaf51171c9211c71816d94a0e69efa2732fd27ffc0e1bbc84b636e60932333eb + languageName: node + linkType: hard + "@babel/types@npm:^7.0.0, @babel/types@npm:^7.20.7, @babel/types@npm:^7.25.2, @babel/types@npm:^7.27.1, @babel/types@npm:^7.27.3, @babel/types@npm:^7.28.2, @babel/types@npm:^7.28.4, @babel/types@npm:^7.28.5, @babel/types@npm:^7.3.3, @babel/types@npm:^7.4.4": version: 7.28.5 resolution: "@babel/types@npm:7.28.5" @@ -1670,6 +1743,16 @@ __metadata: languageName: node linkType: hard +"@babel/types@npm:^7.29.0": + version: 7.29.0 + resolution: "@babel/types@npm:7.29.0" + dependencies: + "@babel/helper-string-parser": "npm:^7.27.1" + "@babel/helper-validator-identifier": "npm:^7.28.5" + checksum: 10c0/23cc3466e83bcbfab8b9bd0edaafdb5d4efdb88b82b3be6728bbade5ba2f0996f84f63b1c5f7a8c0d67efded28300898a5f930b171bb40b311bca2029c4e9b4f + languageName: node + linkType: hard + "@bcoe/v8-coverage@npm:^0.2.3": version: 0.2.3 resolution: "@bcoe/v8-coverage@npm:0.2.3" @@ -2645,7 +2728,6 @@ __metadata: resolution: "@ping-identity/rn-browser@workspace:packages/browser" dependencies: "@ping-identity/rn-core": "npm:^0.1.0" - "@ping-identity/rn-logger": "npm:^0.1.0" "@ping-identity/rn-types": "npm:^0.1.0" jest: "npm:^30.2.0" react-native-builder-bob: "npm:^0.40.14" @@ -2711,7 +2793,7 @@ __metadata: languageName: unknown linkType: soft -"@ping-identity/rn-logger@npm:^0.1.0, @ping-identity/rn-logger@workspace:*, @ping-identity/rn-logger@workspace:packages/logger": +"@ping-identity/rn-logger@workspace:*, @ping-identity/rn-logger@workspace:packages/logger": version: 0.0.0-use.local resolution: "@ping-identity/rn-logger@workspace:packages/logger" dependencies: @@ -6458,6 +6540,21 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-react-hooks@npm:^7.0.1": + version: 7.0.1 + resolution: "eslint-plugin-react-hooks@npm:7.0.1" + dependencies: + "@babel/core": "npm:^7.24.4" + "@babel/parser": "npm:^7.24.4" + hermes-parser: "npm:^0.25.1" + zod: "npm:^3.25.0 || ^4.0.0" + zod-validation-error: "npm:^3.5.0 || ^4.0.0" + peerDependencies: + eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 + checksum: 10c0/1e711d1a9d1fa9cfc51fa1572500656577201199c70c795c6a27adfc1df39e5c598f69aab6aa91117753d23cc1f11388579a2bed14921cf9a4efe60ae8618496 + languageName: node + linkType: hard + "eslint-plugin-react-native-globals@npm:^0.1.1": version: 0.1.2 resolution: "eslint-plugin-react-native-globals@npm:0.1.2" @@ -6476,7 +6573,7 @@ __metadata: languageName: node linkType: hard -"eslint-plugin-react@npm:^7.30.1, eslint-plugin-react@npm:^7.37.5": +"eslint-plugin-react@npm:^7.30.1": version: 7.37.5 resolution: "eslint-plugin-react@npm:7.37.5" dependencies: @@ -7490,6 +7587,13 @@ __metadata: languageName: node linkType: hard +"hermes-estree@npm:0.25.1": + version: 0.25.1 + resolution: "hermes-estree@npm:0.25.1" + checksum: 10c0/48be3b2fa37a0cbc77a112a89096fa212f25d06de92781b163d67853d210a8a5c3784fac23d7d48335058f7ed283115c87b4332c2a2abaaccc76d0ead1a282ac + languageName: node + linkType: hard + "hermes-estree@npm:0.28.1": version: 0.28.1 resolution: "hermes-estree@npm:0.28.1" @@ -7538,6 +7642,15 @@ __metadata: languageName: node linkType: hard +"hermes-parser@npm:^0.25.1": + version: 0.25.1 + resolution: "hermes-parser@npm:0.25.1" + dependencies: + hermes-estree: "npm:0.25.1" + checksum: 10c0/3abaa4c6f1bcc25273f267297a89a4904963ea29af19b8e4f6eabe04f1c2c7e9abd7bfc4730ddb1d58f2ea04b6fee74053d8bddb5656ec6ebf6c79cc8d14202c + languageName: node + linkType: hard + "html-escaper@npm:^2.0.0": version: 2.0.2 resolution: "html-escaper@npm:2.0.2" @@ -10744,7 +10857,7 @@ __metadata: "@types/jest": "npm:^29.5.14" babel-jest: "npm:^29.7.0" eslint: "npm:^10.0.1" - eslint-plugin-react: "npm:^7.37.5" + eslint-plugin-react-hooks: "npm:^7.0.1" globals: "npm:^17.3.0" jest: "npm:^29.7.0" jest-junit: "npm:^16.0.0" @@ -13412,3 +13525,19 @@ __metadata: checksum: 10c0/dceb44c28578b31641e13695d200d34ec4ab3966a5729814d5445b194933c096b7ced71494ce53a0e8820685d1d010df8b2422e5bf2cdea7e469d97ffbea306f languageName: node linkType: hard + +"zod-validation-error@npm:^3.5.0 || ^4.0.0": + version: 4.0.2 + resolution: "zod-validation-error@npm:4.0.2" + peerDependencies: + zod: ^3.25.0 || ^4.0.0 + checksum: 10c0/0ccfec48c46de1be440b719cd02044d4abb89ed0e14c13e637cd55bf29102f67ccdba373f25def0fc7130e5f15025be4d557a7edcc95d5a3811599aade689e1b + languageName: node + linkType: hard + +"zod@npm:^3.25.0 || ^4.0.0": + version: 4.3.6 + resolution: "zod@npm:4.3.6" + checksum: 10c0/860d25a81ab41d33aa25f8d0d07b091a04acb426e605f396227a796e9e800c44723ed96d0f53a512b57be3d1520f45bf69c0cb3b378a232a00787a2609625307 + languageName: node + linkType: hard