diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..b134dc1 --- /dev/null +++ b/.editorconfig @@ -0,0 +1,22 @@ +# https://editorconfig.org +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true +indent_style = space +indent_size = 2 + +[*.md] +trim_trailing_whitespace = false + +[*.{kt,kts,java,swift,cs,dart}] +indent_size = 4 + +[*.{gradle,xml}] +indent_size = 4 + +[Makefile] +indent_style = tab diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 834d3c7..b194161 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -31,28 +31,30 @@ jobs: name: JS/TS packages runs-on: [self-hosted] steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: '20' - run: npm ci --ignore-scripts + - run: npm run versions:check - run: npm run build - run: npm run lint - run: npm test + - run: npm run typecheck --workspace=@screeb/react-native flutter: name: Flutter package runs-on: [self-hosted] steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 with: java-version: '17' distribution: 'temurin' - - uses: android-actions/setup-android@v3 + - uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3 with: packages: tools platform-tools build-tools;35.0.0 platforms;android-35 - - uses: subosito/flutter-action@v2 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2 with: flutter-version: '3.x' channel: 'stable' @@ -60,20 +62,21 @@ jobs: run: git config --global --add safe.directory "$FLUTTER_ROOT" - run: cd packages/sdk-flutter && flutter pub get - run: cd packages/sdk-flutter && flutter analyze + - run: cd packages/sdk-flutter && flutter test maui-android: name: .NET MAUI Android SDK runs-on: [self-hosted] steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 with: java-version: '17' distribution: 'temurin' - - uses: android-actions/setup-android@v3 + - uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3 with: packages: tools platform-tools build-tools;35.0.0 platforms;android-35 - - uses: actions/setup-dotnet@v4 + - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 with: dotnet-version: '9.0.x' - name: Install MAUI workloads @@ -90,8 +93,8 @@ jobs: runs-on: macos-latest if: ${{ github.event_name == 'workflow_dispatch' && inputs.run_macos_native }} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-dotnet@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 with: dotnet-version: '9.0.x' - name: Install MAUI workloads @@ -113,7 +116,7 @@ jobs: name: Kotlin Multiplatform Android SDK runs-on: [self-hosted] steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - name: Check native Android artifact id: native-android run: | @@ -126,12 +129,12 @@ jobs: echo "::warning::app.screeb.sdk:survey:${version} is not published yet; skipping KMP Android build." echo "available=false" >> "$GITHUB_OUTPUT" fi - - uses: actions/setup-java@v4 + - uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 if: steps.native-android.outputs.available == 'true' with: java-version: '17' distribution: 'temurin' - - uses: android-actions/setup-android@v3 + - uses: android-actions/setup-android@9fc6c4e9069bf8d3d10b2204b1fb8f6ef7065407 # v3 if: steps.native-android.outputs.available == 'true' with: packages: tools platform-tools build-tools;35.0.0 platforms;android-35 @@ -147,8 +150,8 @@ jobs: runs-on: macos-latest if: ${{ github.event_name == 'workflow_dispatch' && inputs.run_macos_native }} steps: - - uses: actions/checkout@v4 - - uses: actions/setup-java@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 with: java-version: '17' distribution: 'temurin' diff --git a/.github/workflows/publish-angular.yml b/.github/workflows/publish-angular.yml index 1d8852e..ef4a673 100644 --- a/.github/workflows/publish-angular.yml +++ b/.github/workflows/publish-angular.yml @@ -12,12 +12,11 @@ jobs: id-token: write contents: read steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: '22' registry-url: 'https://registry.npmjs.org' - - run: npm install -g npm@latest - - run: npm install --ignore-scripts + - run: npm ci --ignore-scripts - run: npm run build - run: npm publish --workspace=packages/sdk-angular --access public --provenance diff --git a/.github/workflows/publish-browser.yml b/.github/workflows/publish-browser.yml index 74f4589..e8de9c5 100644 --- a/.github/workflows/publish-browser.yml +++ b/.github/workflows/publish-browser.yml @@ -12,12 +12,11 @@ jobs: id-token: write contents: read steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: '22' registry-url: 'https://registry.npmjs.org' - - run: npm install -g npm@latest - - run: npm install --ignore-scripts + - run: npm ci --ignore-scripts - run: npm run build - run: npm publish --workspace=packages/sdk-browser --access public --provenance diff --git a/.github/workflows/publish-flutter.yml b/.github/workflows/publish-flutter.yml index a4c9cd9..b4904dc 100644 --- a/.github/workflows/publish-flutter.yml +++ b/.github/workflows/publish-flutter.yml @@ -9,8 +9,8 @@ jobs: publish: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 - - uses: subosito/flutter-action@v2 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2 with: flutter-version: '3.x' channel: 'stable' diff --git a/.github/workflows/publish-kmp.yml b/.github/workflows/publish-kmp.yml index 7e19661..224b812 100644 --- a/.github/workflows/publish-kmp.yml +++ b/.github/workflows/publish-kmp.yml @@ -9,13 +9,13 @@ jobs: publish: runs-on: macos-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: actions/setup-node@v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: '20' - - uses: actions/setup-java@v4 + - uses: actions/setup-java@c1e323688fd81a25caa38c78aa6df2d33d3e20d9 # v4 with: java-version: '17' distribution: 'temurin' @@ -23,12 +23,14 @@ jobs: - run: npm run versions:check - name: Download Screeb iOS XCFramework + env: + GH_TOKEN: ${{ github.token }} run: | set -euo pipefail SCREEB_IOS_SDK_VERSION=$(grep '^SCREEB_IOS_SDK_VERSION=' packages/sdk-kmp/gradle.properties | cut -d= -f2) - SCREEB_IOS_URL=$(curl -sL "https://api.github.com/repos/ScreebApp/sdk-ios-public/releases/tags/v${SCREEB_IOS_SDK_VERSION}" \ - | python3 -c "import sys,json; print(next(a['browser_download_url'] for a in json.load(sys.stdin)['assets'] if a['name']=='Screeb.zip'))") - curl -sL "$SCREEB_IOS_URL" -o /tmp/Screeb.zip + # Authenticated download (anonymous api.github.com calls are rate-limited + # on shared runners and come back without 'assets'). + gh release download "${SCREEB_IOS_SDK_VERSION}" --repo ScreebApp/sdk-ios-public --pattern 'Screeb.zip' --output /tmp/Screeb.zip --clobber unzip -q /tmp/Screeb.zip -d /tmp/screeb_ios mkdir -p packages/sdk-kmp/native/ios cp -r /tmp/screeb_ios/Screeb.xcframework packages/sdk-kmp/native/ios/ @@ -45,4 +47,4 @@ jobs: GPG_KEY_ID: ${{ secrets.GPG_KEY_ID }} GPG_KEY: ${{ secrets.GPG_KEY }} GPG_PASSWORD: ${{ secrets.GPG_PASSWORD }} - run: ./gradlew publishAllPublicationsToOSSRHRepository --no-daemon + run: ./gradlew publishToSonatype closeAndReleaseSonatypeStagingRepository --no-daemon diff --git a/.github/workflows/publish-maui.yml b/.github/workflows/publish-maui.yml index c185090..5421b20 100644 --- a/.github/workflows/publish-maui.yml +++ b/.github/workflows/publish-maui.yml @@ -11,21 +11,34 @@ jobs: permissions: id-token: write # enable GitHub OIDC token issuance for NuGet Trusted Publishing steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 - - uses: actions/setup-dotnet@v4 + - uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4 with: dotnet-version: '9.0.x' + # macos-latest also preinstalls .NET 10; without a global.json `dotnet` + # resolves to the newest SDK, whose workload set no longer matches the + # net9.0 targets (NETSDK1147 asking for maui-android). + - name: Pin the .NET 9 SDK + run: | + set -euo pipefail + DOTNET9=$(dotnet --list-sdks | awk '/^9\./{v=$1} END{print v}') + test -n "$DOTNET9" + printf '{"sdk":{"version":"%s","rollForward":"latestFeature"}}\n' "$DOTNET9" > global.json + dotnet --version + - name: Install MAUI workloads - run: dotnet workload install android ios + run: dotnet workload install maui-android maui-ios - name: Download Screeb iOS XCFramework env: GH_TOKEN: ${{ github.token }} run: | + set -euo pipefail + SCREEB_IOS_SDK_VERSION=$(node -p "require('./sdk-versions.json').native.ios") mkdir -p packages/sdk-maui/native/ios - gh release download --repo ScreebApp/sdk-ios-public --pattern 'Screeb.zip' --output /tmp/Screeb.zip --clobber + gh release download "${SCREEB_IOS_SDK_VERSION}" --repo ScreebApp/sdk-ios-public --pattern 'Screeb.zip' --output /tmp/Screeb.zip --clobber unzip -q /tmp/Screeb.zip -d /tmp/screeb_ios cp -r /tmp/screeb_ios/Screeb.xcframework packages/sdk-maui/native/ios/ @@ -43,7 +56,7 @@ jobs: - name: NuGet login (OIDC -> temporary API key) id: login - uses: NuGet/login@v1 + uses: NuGet/login@8d196754b4036150537f80ac539e15c2f1028841 # v1 with: user: alexays diff --git a/.github/workflows/publish-react.yml b/.github/workflows/publish-react.yml index 87f5908..a03dcf8 100644 --- a/.github/workflows/publish-react.yml +++ b/.github/workflows/publish-react.yml @@ -12,12 +12,11 @@ jobs: id-token: write contents: read steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: '22' registry-url: 'https://registry.npmjs.org' - - run: npm install -g npm@latest - - run: npm install --ignore-scripts + - run: npm ci --ignore-scripts - run: npm run build - run: npm publish --workspace=packages/sdk-react --access public --provenance diff --git a/.github/workflows/publish-reactnative.yml b/.github/workflows/publish-reactnative.yml index 3304094..7e0a0e2 100644 --- a/.github/workflows/publish-reactnative.yml +++ b/.github/workflows/publish-reactnative.yml @@ -12,12 +12,13 @@ jobs: id-token: write contents: read steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: '22' registry-url: 'https://registry.npmjs.org' + # npm Trusted Publishing (tokenless OIDC) needs npm >= 11.5 — node 22 ships 10.x - run: npm install -g npm@latest - - run: npm install --ignore-scripts + - run: npm ci --ignore-scripts - run: npm run build - run: npm publish --workspace=packages/sdk-reactnative --access public --provenance diff --git a/.github/workflows/publish-svelte.yml b/.github/workflows/publish-svelte.yml new file mode 100644 index 0000000..3cdf0c1 --- /dev/null +++ b/.github/workflows/publish-svelte.yml @@ -0,0 +1,22 @@ +name: Publish @screeb/sdk-svelte + +on: + push: + tags: + - '@screeb/sdk-svelte@*' + +jobs: + publish: + runs-on: ubuntu-latest + permissions: + id-token: write + contents: read + steps: + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 + with: + node-version: '22' + registry-url: 'https://registry.npmjs.org' + - run: npm ci --ignore-scripts + - run: npm run build + - run: npm publish --workspace=packages/sdk-svelte --access public --provenance diff --git a/.github/workflows/publish-vue.yml b/.github/workflows/publish-vue.yml index 4794a96..f553c7a 100644 --- a/.github/workflows/publish-vue.yml +++ b/.github/workflows/publish-vue.yml @@ -12,12 +12,11 @@ jobs: id-token: write contents: read steps: - - uses: actions/checkout@v4 - - uses: actions/setup-node@v4 + - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4 with: node-version: '22' registry-url: 'https://registry.npmjs.org' - - run: npm install -g npm@latest - - run: npm install --ignore-scripts + - run: npm ci --ignore-scripts - run: npm run build - run: npm publish --workspace=packages/sdk-vue --access public --provenance diff --git a/.gitignore b/.gitignore index c66f403..8b6f404 100644 --- a/.gitignore +++ b/.gitignore @@ -13,6 +13,12 @@ build dist storybook-static +# Android native build output (NDK/CMake), e.g. RN prefab under example-*/android/app/.cxx +.cxx/ + +# generated API reference (typedoc) — regenerate with `npm run build:doc` +packages/*/docs/ + # misc .DS_Store .env.local @@ -43,3 +49,12 @@ packages/sdk-maui/bin/ packages/sdk-maui/obj/ examples/example-maui/bin/ examples/example-maui/obj/ + +# build-local-ios-xcframework.mjs temp workdirs (cleaned by the script; belt and braces) +packages/*/native/ios/.build-*/ + +# CocoaPods install output (regenerated by `pod install`) +examples/example-reactnative/ios/Pods/ + +# React Native local env (per-machine node path, written by pod install) +.xcode.env.local diff --git a/.husky/pre-commit b/.husky/pre-commit index 0bae0fe..75fac8e 100755 --- a/.husky/pre-commit +++ b/.husky/pre-commit @@ -2,4 +2,3 @@ . "$(dirname -- "$0")/_/husky.sh" npm run lint -npm run build:doc && git add -A packages/*/docs diff --git a/.nvmrc b/.nvmrc new file mode 100644 index 0000000..209e3ef --- /dev/null +++ b/.nvmrc @@ -0,0 +1 @@ +20 diff --git a/commitlint.config.js b/commitlint.config.js index 02bdf17..0cd12e5 100644 --- a/commitlint.config.js +++ b/commitlint.config.js @@ -14,12 +14,22 @@ module.exports = { "example-angular", "sdk-vue", "example-vue", + "sdk-svelte", + "example-svelte", "sdk-maui", "example-maui", "sdk-flutter", "example-flutter", + "sdk-kmp", + "example-kmp", "sdk-react-native", - "example-react-native" + "sdk-reactnative", + "example-react-native", + "example-reactnative", + "example-android", + "example-ios", + "example-ionic", + "example-expo" ]], "scope-empty": [2, "never"], "scope-min-length": [2, "always", 1], diff --git a/examples/example-android/app/build.gradle b/examples/example-android/app/build.gradle index 4c8423c..d66fb11 100644 --- a/examples/example-android/app/build.gradle +++ b/examples/example-android/app/build.gradle @@ -38,5 +38,5 @@ android { } dependencies { - implementation 'app.screeb.sdk:survey:4.0.0' + implementation 'app.screeb.sdk:survey:4.0.2' } diff --git a/examples/example-android/app/src/main/kotlin/app/screeb/example/MainActivity.kt b/examples/example-android/app/src/main/kotlin/app/screeb/example/MainActivity.kt index a05f801..edb243e 100644 --- a/examples/example-android/app/src/main/kotlin/app/screeb/example/MainActivity.kt +++ b/examples/example-android/app/src/main/kotlin/app/screeb/example/MainActivity.kt @@ -69,6 +69,10 @@ class MainActivity : Activity() { status = body("Starting...") content.addView(status) + content.addView(action("Init SDK") { + initializeScreeb() + }) + content.addView(action("Set identity") { Screeb.setIdentity( "android-example-user", @@ -84,6 +88,37 @@ class MainActivity : Activity() { setStatus("Visitor properties sent") }) + content.addView(action("Assign group") { + Screeb.assignGroup( + "company", + "Screeb", + hashMapOf("plan" to "public-example", "source" to "native-android-example"), + ) + setStatus("Group assigned") + }) + + content.addView(action("Unassign group") { + Screeb.unassignGroup( + "company", + "Screeb", + hashMapOf("source" to "native-android-example"), + ) + setStatus("Group unassigned") + }) + + content.addView(action("Reset identity") { + Screeb.resetIdentity() + setStatus("Identity reset") + }) + + content.addView(action("Get identity") { + Screeb.getIdentity { identity, error -> + runOnUiThread { + setStatus(error?.message ?: identity.toString()) + } + } + }) + content.addView(action("Track event") { Screeb.trackEvent( "android_example_button_clicked", @@ -99,7 +134,7 @@ class MainActivity : Activity() { content.addView(action("Start survey") { Screeb.startSurvey( - surveyId = "replace-with-survey-id", + surveyId = "1b1fe0c4-d41d-4307-9ca0-b0b66cce8cff", allowMultipleResponses = true, hiddenFields = hashMapOf("example" to "android"), ignoreSurveyStatus = true, @@ -112,7 +147,7 @@ class MainActivity : Activity() { content.addView(action("Start message") { Screeb.startMessage( - messageId = "replace-with-message-id", + messageId = "642929b9-28f1-4cb5-b153-f482777e0003", allowMultipleResponses = true, hiddenFields = hashMapOf("example" to "android"), ignoreMessageStatus = true, @@ -141,6 +176,14 @@ class MainActivity : Activity() { } }) + content.addView(action("Debug targeting") { + Screeb.debugTargeting { result, error -> + runOnUiThread { + setStatus(error?.message ?: result.ifBlank { "Targeting debug command sent" }) + } + } + }) + content.addView(action("Close SDK") { Screeb.closeSdk() setStatus("SDK closed") diff --git a/examples/example-angular/package.json b/examples/example-angular/package.json index ae44394..fb64388 100644 --- a/examples/example-angular/package.json +++ b/examples/example-angular/package.json @@ -5,12 +5,12 @@ "description": "Screeb's example app for Angular sdk.", "homepage": "https://screeb.app", "bugs": { - "url": "https://github.com/ScreebApp/sdk-js/issues", + "url": "https://github.com/ScreebApp/sdk/issues", "email": "support@screeb.app" }, "repository": { "type": "git", - "url": "https://github.com/ScreebApp/sdk-js.git", + "url": "https://github.com/ScreebApp/sdk.git", "directory": "examples/example-angular" }, "license": "MIT", @@ -19,7 +19,7 @@ "scripts": { "build": "tsc && vite build", "clean": "rm -Rf dist", - "lint": "eslint .", + "lint": "eslint \"src/**/*.ts\"", "start": "vite" }, "dependencies": { @@ -37,10 +37,8 @@ "@typescript-eslint/eslint-plugin": "^6.7.5", "eslint": "^8.51.0", "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jest": "^27.4.2", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-prettier": "^5.0.1", - "jest": "^29.7.0", "typescript": "^5.2.2", "vite": "^4.4.11" } diff --git a/examples/example-angular/src/home.component.ts b/examples/example-angular/src/home.component.ts index e863529..092f19a 100644 --- a/examples/example-angular/src/home.component.ts +++ b/examples/example-angular/src/home.component.ts @@ -1,7 +1,12 @@ import { CommonModule } from "@angular/common"; -import { Component, Inject, OnInit } from "@angular/core"; +import { Component, Inject } from "@angular/core"; import { Screeb } from "@screeb/sdk-angular"; +const WEBSITE_ID = "0e2b609a-8dce-4695-a80f-966fbfa87a88"; +const USER_ID = "dev@screeb.app"; +const SURVEY_ID = "1b1fe0c4-d41d-4307-9ca0-b0b66cce8cff"; +const MESSAGE_ID = "642929b9-28f1-4cb5-b153-f482777e0003"; + @Component({ imports: [CommonModule], selector: "app-home", @@ -32,6 +37,38 @@ import { Screeb } from "@screeb/sdk-angular"; font-weight: 700; color: #5e21f1; } + + .actions { + display: flex; + flex-direction: column; + align-items: flex-start; + gap: 0.5em; + margin-top: 1em; + } + + button { + background: #5e21f1; + border: none; + border-radius: 8px; + color: #fff; + cursor: pointer; + font-family: Rubik; + font-size: 1em; + font-weight: 500; + padding: 10px 18px; + } + + pre { + background: #f7f5ff; + border: 1px solid #ded6ff; + border-radius: 8px; + color: #303140; + margin-top: 1em; + overflow: auto; + padding: 16px; + text-align: left; + white-space: pre-wrap; + } `, ], template: ` @@ -48,22 +85,121 @@ import { Screeb } from "@screeb/sdk-angular";  to learn how to integrate  Screeb.

+
+ + + + + + + + + + + + + + + +
+
{{ result }}
`, }) -export class HomeComponent implements OnInit { +export class HomeComponent { + result = ""; + // eslint-disable-next-line no-unused-vars constructor(@Inject(Screeb) private screeb: Screeb) {} - ngOnInit(): void { - this.screeb.debug(); + async initSdk(): Promise { + await this.screeb.load(); + await this.screeb.init(WEBSITE_ID, USER_ID, { + authenticated: true, + firstname: "John", + last_seen_at: new Date(), + lastname: "Smith", + org_size: 20, + }); + this.result = "SDK initialized."; + } - this.screeb.eventTrack("example-angular started", { - test: 123, + async setIdentity(): Promise { + await this.screeb.identity(USER_ID, { + authenticated: true, + firstname: "John", + lastname: "Smith", }); - this.screeb.identityProperties({ hello: "I'm a dev." }); - this.screeb.identityGroupAssign("cohort", "Screeb Developers"); + this.result = "Identity set."; + } + + async setVisitorProperties(): Promise { + await this.screeb.identityProperties({ hello: "I'm a dev." }); + this.result = "Visitor properties set."; + } + + async assignGroup(): Promise { + await this.screeb.identityGroupAssign("cohort", "Screeb Developers"); + this.result = "Group assigned."; + } + + async unassignGroup(): Promise { + await this.screeb.identityGroupUnassign("cohort", "Screeb Developers"); + this.result = "Group unassigned."; + } + + async resetIdentity(): Promise { + await this.screeb.identityReset(); + this.result = "Identity reset."; + } + + async getIdentity(): Promise { + const identity = await this.screeb.identityGet(); + this.result = JSON.stringify(identity, null, 2); + } + + async trackEvent(): Promise { + await this.screeb.eventTrack("example-angular started", { test: 123 }); + this.result = "Event tracked."; + } + + async startSurvey(): Promise { + await this.screeb.surveyStart(SURVEY_ID); + this.result = "Survey started."; + } + + async startMessage(): Promise { + await this.screeb.messageStart(MESSAGE_ID); + this.result = "Message started."; + } + + async sessionReplayStart(): Promise { + await this.screeb.sessionReplayStart(); + this.result = "Session replay started."; + } + + async sessionReplayStop(): Promise { + await this.screeb.sessionReplayStop(); + this.result = "Session replay stopped."; + } + + async debugSdk(): Promise { + const result = await this.screeb.debug(); + this.result = String(result ?? "See console for debug output."); + } + + async debugTargeting(): Promise { + const result = await this.screeb.targetingDebug(); + this.result = String(result ?? "See console for targeting output."); + } - // eslint-disable-next-line no-console - setTimeout(() => this.screeb.identityGet().then(console.log), 500); + async closeSdk(): Promise { + await this.screeb.close(); + this.result = "SDK closed."; } } diff --git a/examples/example-browser/package.json b/examples/example-browser/package.json index 325bc27..6b55e39 100644 --- a/examples/example-browser/package.json +++ b/examples/example-browser/package.json @@ -5,12 +5,12 @@ "description": "Screeb's example app for browser sdk.", "homepage": "https://screeb.app", "bugs": { - "url": "https://github.com/ScreebApp/sdk-js/issues", + "url": "https://github.com/ScreebApp/sdk/issues", "email": "support@screeb.app" }, "repository": { "type": "git", - "url": "https://github.com/ScreebApp/sdk-js.git", + "url": "https://github.com/ScreebApp/sdk.git", "directory": "examples/example-browser" }, "license": "MIT", @@ -19,7 +19,7 @@ "scripts": { "build": "vite build", "clean": "rm -Rf dist", - "lint": "eslint .", + "lint": "eslint \"src/**/*.{ts,js}\"", "start": "vite" }, "dependencies": { @@ -32,11 +32,9 @@ "@typescript-eslint/eslint-plugin": "^6.7.5", "eslint": "^8.51.0", "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jest": "^27.4.2", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-prettier": "^5.0.1", "eslint-plugin-svelte": "^2.34.0", - "jest": "^29.7.0", "svelte": "^4.2.1", "svelte-check": "^3.5.2", "tslib": "^2.6.2", diff --git a/examples/example-browser/src/App.svelte b/examples/example-browser/src/App.svelte index 32c5f60..e49b8f5 100644 --- a/examples/example-browser/src/App.svelte +++ b/examples/example-browser/src/App.svelte @@ -3,7 +3,18 @@ import { onMount } from "svelte" import screebLogo from "./assets/screeb.png" - onMount(async () => { + // Placeholder ids — replace with real ones from your Screeb workspace. + const SURVEY_ID = "1b1fe0c4-d41d-4307-9ca0-b0b66cce8cff" + const MESSAGE_ID = "642929b9-28f1-4cb5-b153-f482777e0003" + + let result = "" + + const show = (value: unknown) => { + result = + typeof value === "string" ? value : JSON.stringify(value, null, 2) + } + + const initSDK = async () => { await Screeb.load() // eslint-disable-next-line no-console console.log("screeb loaded") @@ -25,20 +36,86 @@ version: "1.0.0", } ) + } - // eslint-disable-next-line no-console - console.log(await Screeb.debug()) + const onInitSDK = async () => { + await initSDK() + show("SDK initialized") + } - await Screeb.eventTrack("screeb-sdk-browser-example started", { - test: 123, + const onSetIdentity = async () => { + await Screeb.identity("dev@screeb.app", { + authenticated: true, + firstname: "John", + lastname: "Smith", }) + show("Identity set") + } + + const onSetVisitorProperties = async () => { await Screeb.identityProperties({ hello: "I'm a dev." }) + show("Visitor properties set") + } + + const onAssignGroup = async () => { await Screeb.identityGroupAssign("cohort", "Screeb Developers") + show("Group assigned") + } + + const onUnassignGroup = async () => { + await Screeb.identityGroupUnassign("cohort", "Screeb Developers") + show("Group unassigned") + } + + const onResetIdentity = async () => { + await Screeb.identityReset() + show("Identity reset") + } + + const onGetIdentity = async () => { + show(await Screeb.identityGet()) + } + + const onTrackEvent = async () => { + await Screeb.eventTrack("screeb-sdk-browser-example event", { test: 123 }) + show("Event tracked") + } + + const onStartSurvey = async () => { + await Screeb.surveyStart(SURVEY_ID) + show("Survey started") + } + + const onStartMessage = async () => { + await Screeb.messageStart(MESSAGE_ID) + show("Message started") + } - setTimeout(async () => { - // eslint-disable-next-line no-console - console.log(await Screeb.identityGet()) - }, 200) + const onSessionReplayStart = async () => { + await Screeb.sessionReplayStart() + show("Session replay started") + } + + const onSessionReplayStop = async () => { + await Screeb.sessionReplayStop() + show("Session replay stopped") + } + + const onDebugSDK = async () => { + show(await Screeb.debug()) + } + + const onDebugTargeting = async () => { + show(await Screeb.targetingDebug()) + } + + const onCloseSDK = async () => { + await Screeb.close() + show("SDK closed") + } + + onMount(async () => { + await initSDK() }) @@ -58,6 +135,34 @@ to learn how to integrate Screeb.

+ +
+ + + + + + + + + + + + + + + +
+ + {#if result} +
{result}
+ {/if} diff --git a/examples/example-svelte/src/app.css b/examples/example-svelte/src/app.css new file mode 100644 index 0000000..2b909ec --- /dev/null +++ b/examples/example-svelte/src/app.css @@ -0,0 +1,41 @@ +html, +body { + height: 100%; + position: relative; + width: 100%; +} + +body { + box-sizing: border-box; + color: #333; + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, + Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + margin: 0; + padding: 8px; +} + +a { + color: #5e21f1; + text-decoration: none; +} + +a:hover { + text-decoration: underline; +} + +a:visited { + color: #5e21f1; +} + +button, +input, +select, +textarea { + border: 1px solid #ccc; + border-radius: 2px; + box-sizing: border-box; + font-family: inherit; + font-size: inherit; + margin: 0 0 0.5em; + padding: 0.4em; +} diff --git a/examples/example-svelte/src/main.ts b/examples/example-svelte/src/main.ts new file mode 100644 index 0000000..7c3e646 --- /dev/null +++ b/examples/example-svelte/src/main.ts @@ -0,0 +1,9 @@ +import "./app.css"; + +import App from "./App.svelte"; + +const app = new App({ + target: document.getElementById("app"), +}); + +export default app; diff --git a/examples/example-svelte/src/vite-env.d.ts b/examples/example-svelte/src/vite-env.d.ts new file mode 100644 index 0000000..4078e74 --- /dev/null +++ b/examples/example-svelte/src/vite-env.d.ts @@ -0,0 +1,2 @@ +/// +/// diff --git a/examples/example-svelte/svelte.config.js b/examples/example-svelte/svelte.config.js new file mode 100644 index 0000000..de2ddd6 --- /dev/null +++ b/examples/example-svelte/svelte.config.js @@ -0,0 +1,7 @@ +import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; + +export default { + // Consult https://svelte.dev/docs#compile-time-svelte-preprocess + // for more information about preprocessors + preprocess: vitePreprocess(), +}; diff --git a/examples/example-svelte/tsconfig.json b/examples/example-svelte/tsconfig.json new file mode 100644 index 0000000..d9090c5 --- /dev/null +++ b/examples/example-svelte/tsconfig.json @@ -0,0 +1,22 @@ +{ + "extends": "@tsconfig/svelte/tsconfig.json", + "compilerOptions": { + "outDir": "./dist", + "target": "ESNext", + "useDefineForClassFields": true, + "module": "ESNext", + "resolveJsonModule": true, + "allowJs": true, + "checkJs": true, + "isolatedModules": true + }, + "include": [ + "svelte.config.js", + "vite.config.ts", + "src/**/*.d.ts", + "src/**/*.ts", + "src/**/*.js", + "src/**/*.svelte" + ], + "references": [{ "path": "./tsconfig.node.json" }] +} diff --git a/examples/example-svelte/tsconfig.node.json b/examples/example-svelte/tsconfig.node.json new file mode 100644 index 0000000..494bfe0 --- /dev/null +++ b/examples/example-svelte/tsconfig.node.json @@ -0,0 +1,9 @@ +{ + "compilerOptions": { + "composite": true, + "skipLibCheck": true, + "module": "ESNext", + "moduleResolution": "bundler" + }, + "include": ["vite.config.ts"] +} diff --git a/examples/example-svelte/vite.config.ts b/examples/example-svelte/vite.config.ts new file mode 100644 index 0000000..09d6eaa --- /dev/null +++ b/examples/example-svelte/vite.config.ts @@ -0,0 +1,7 @@ +import { svelte } from "@sveltejs/vite-plugin-svelte"; +import { defineConfig } from "vite"; + +// https://vitejs.dev/config/ +export default defineConfig({ + plugins: [svelte()], +}); diff --git a/examples/example-vue/.eslintrc b/examples/example-vue/.eslintrc new file mode 100644 index 0000000..fba1fac --- /dev/null +++ b/examples/example-vue/.eslintrc @@ -0,0 +1,12 @@ +{ + "extends": [ + "@screeb/eslint-config" + ], + "parserOptions": { + "project": ["tsconfig.json"], + "tsconfigRootDir": "./" + }, + "rules": { + "semi": 0 + } +} diff --git a/examples/example-vue/package.json b/examples/example-vue/package.json index 76ac2d0..26412be 100644 --- a/examples/example-vue/package.json +++ b/examples/example-vue/package.json @@ -5,12 +5,12 @@ "description": "Screeb's example app for Vue SDK.", "homepage": "https://screeb.app", "bugs": { - "url": "https://github.com/ScreebApp/sdk-js/issues", + "url": "https://github.com/ScreebApp/sdk/issues", "email": "support@screeb.app" }, "repository": { "type": "git", - "url": "https://github.com/ScreebApp/sdk-js.git", + "url": "https://github.com/ScreebApp/sdk.git", "directory": "examples/example-vue" }, "license": "MIT", @@ -19,7 +19,7 @@ "scripts": { "build": "vue-tsc && vite build", "clean": "rm -Rf dist", - "lint": "eslint .", + "lint": "eslint \"src/**/*.ts\"", "start": "vite" }, "dependencies": { diff --git a/examples/example-vue/src/App.vue b/examples/example-vue/src/App.vue index 3bc6484..788afe2 100644 --- a/examples/example-vue/src/App.vue +++ b/examples/example-vue/src/App.vue @@ -1,21 +1,113 @@ + + diff --git a/examples/example-vue/src/main.ts b/examples/example-vue/src/main.ts index 6cd56aa..562f43d 100644 --- a/examples/example-vue/src/main.ts +++ b/examples/example-vue/src/main.ts @@ -1,13 +1,19 @@ -import { createApp } from "vue"; import { ScreebPlugin } from "@screeb/sdk-vue"; +import { createApp } from "vue"; import App from "./App.vue"; import "./index.css"; createApp(App) .use(ScreebPlugin, { - websiteId: "0e2b609a-8dce-4695-a80f-966fbfa87a88", autoInit: true, + hooks: { + onReady: (payload: unknown) => { + // eslint-disable-next-line no-console + console.log("onReady", payload); + }, + version: "1.0.0", + }, userId: "dev+1@screeb.app", userProperties: { authenticated: true, @@ -16,12 +22,6 @@ createApp(App) lastname: "Smith", org_size: 20, }, - hooks: { - onReady: (payload: unknown) => { - // eslint-disable-next-line no-console - console.log("onReady", payload); - }, - version: "1.0.0", - }, + websiteId: "0e2b609a-8dce-4695-a80f-966fbfa87a88", }) .mount("#app"); diff --git a/package-lock.json b/package-lock.json index a775370..72383b1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,7 +10,7 @@ "packages/*", "examples/*" ], - "dependencies": { + "devDependencies": { "@commitlint/cli": "^17.7.2", "@commitlint/config-conventional": "^17.7.0", "@tsconfig/svelte": "^5.0.2", @@ -21,7 +21,8 @@ "typescript": "^5.2.2" }, "engines": { - "node": ">=20.0.0" + "node": ">=20.0.0", + "npm": ">=10.0.0" }, "optionalDependencies": { "@rollup/rollup-linux-x64-gnu": "4.22.5" @@ -46,10 +47,8 @@ "@typescript-eslint/eslint-plugin": "^6.7.5", "eslint": "^8.51.0", "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jest": "^27.4.2", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-prettier": "^5.0.1", - "jest": "^29.7.0", "typescript": "^5.2.2", "vite": "^4.4.11" } @@ -72,11 +71,9 @@ "@typescript-eslint/eslint-plugin": "^6.7.5", "eslint": "^8.51.0", "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jest": "^27.4.2", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-prettier": "^5.0.1", "eslint-plugin-svelte": "^2.34.0", - "jest": "^29.7.0", "svelte": "^4.2.1", "svelte-check": "^3.5.2", "tslib": "^2.6.2", @@ -106,32 +103,21 @@ "examples/example-expo": { "version": "1.0.0", "dependencies": { - "@expo/vector-icons": "^15.0.3", - "@react-navigation/bottom-tabs": "^7.4.0", - "@react-navigation/elements": "^2.6.3", "@react-navigation/native": "^7.1.8", "@screeb/react-native": "*", "expo": "~54.0.22", - "expo-constants": "~18.0.10", - "expo-font": "~14.0.9", - "expo-haptics": "~15.0.7", - "expo-image": "~3.0.10", - "expo-linking": "~8.0.8", - "expo-router": "~6.0.14", - "expo-splash-screen": "~31.0.10", - "expo-status-bar": "~3.0.8", - "expo-symbols": "~1.0.7", - "expo-system-ui": "~6.0.8", - "expo-web-browser": "~15.0.9", + "expo-haptics": "~15.0.8", + "expo-image": "~3.0.11", + "expo-router": "~6.0.24", + "expo-status-bar": "~3.0.9", + "expo-symbols": "~1.0.8", + "expo-updates": "~29.0.18", + "expo-web-browser": "~15.0.11", "react": "19.1.0", - "react-dom": "19.1.0", "react-native": "0.81.5", - "react-native-gesture-handler": "~2.28.0", "react-native-reanimated": "~4.1.1", "react-native-safe-area-context": "~5.6.0", - "react-native-screens": "~4.16.0", - "react-native-web": "~0.21.0", - "react-native-worklets": "0.5.1" + "react-native-screens": "~4.16.0" }, "devDependencies": { "@types/react": "~19.1.0", @@ -829,6 +815,8 @@ }, "examples/example-expo/node_modules/expo-haptics": { "version": "15.0.8", + "resolved": "https://registry.npmjs.org/expo-haptics/-/expo-haptics-15.0.8.tgz", + "integrity": "sha512-lftutojy8Qs8zaDzzjwM3gKHFZ8bOOEZDCkmh2Ddpe95Ra6kt2izeOfOfKuP/QEh0MZ1j9TfqippyHdRd1ZM9g==", "license": "MIT", "peerDependencies": { "expo": "*" @@ -836,6 +824,8 @@ }, "examples/example-expo/node_modules/expo-image": { "version": "3.0.11", + "resolved": "https://registry.npmjs.org/expo-image/-/expo-image-3.0.11.tgz", + "integrity": "sha512-4TudfUCLgYgENv+f48omnU8tjS2S0Pd9EaON5/s1ZUBRwZ7K8acEr4NfvLPSaeXvxW24iLAiyQ7sV7BXQH3RoA==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -860,6 +850,7 @@ "examples/example-expo/node_modules/expo-linking": { "version": "8.0.12", "license": "MIT", + "peer": true, "dependencies": { "expo-constants": "~18.0.13", "invariant": "^2.2.4" @@ -869,6 +860,19 @@ "react-native": "*" } }, + "examples/example-expo/node_modules/expo-manifests": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/expo-manifests/-/expo-manifests-1.0.11.tgz", + "integrity": "sha512-6zItytTewN37Cjhp3glUg0ozrgW2GwB8x9wtfzUNoJIMmxO38nnGdTLMaotYhRqdf5PP2Dzdmej1HDHXVNUpRw==", + "license": "MIT", + "dependencies": { + "@expo/config": "~12.0.13", + "expo-json-utils": "~0.15.0" + }, + "peerDependencies": { + "expo": "*" + } + }, "examples/example-expo/node_modules/expo-modules-core": { "version": "3.0.30", "license": "MIT", @@ -881,7 +885,9 @@ } }, "examples/example-expo/node_modules/expo-router": { - "version": "6.0.23", + "version": "6.0.24", + "resolved": "https://registry.npmjs.org/expo-router/-/expo-router-6.0.24.tgz", + "integrity": "sha512-KIssKXnwjrdCxPWC8GsMTfKTwng40VrCsO16O9x6fKlfUwBW8itxY9PpCGyQeMJkiEADa+DUhtrDgl+uHg4/DA==", "license": "MIT", "dependencies": { "@expo/metro-runtime": "^6.1.2", @@ -894,7 +900,7 @@ "client-only": "^0.0.1", "debug": "^4.3.4", "escape-string-regexp": "^4.0.0", - "expo-server": "^1.0.5", + "expo-server": "^1.0.7", "fast-deep-equal": "^3.1.3", "invariant": "^2.2.4", "nanoid": "^3.3.8", @@ -914,7 +920,7 @@ "@testing-library/react-native": ">= 12.0.0", "expo": "*", "expo-constants": "^18.0.13", - "expo-linking": "^8.0.11", + "expo-linking": "^8.0.12", "react": "*", "react-dom": "*", "react-native": "*", @@ -1046,16 +1052,6 @@ } } }, - "examples/example-expo/node_modules/expo-splash-screen": { - "version": "31.0.13", - "license": "MIT", - "dependencies": { - "@expo/prebuild-config": "^54.0.8" - }, - "peerDependencies": { - "expo": "*" - } - }, "examples/example-expo/node_modules/expo-status-bar": { "version": "3.0.9", "license": "MIT", @@ -1069,6 +1065,8 @@ }, "examples/example-expo/node_modules/expo-symbols": { "version": "1.0.8", + "resolved": "https://registry.npmjs.org/expo-symbols/-/expo-symbols-1.0.8.tgz", + "integrity": "sha512-7bNjK350PaQgxBf0owpmSYkdZIpdYYmaPttDBb2WIp6rIKtcEtdzdfmhsc2fTmjBURHYkg36+eCxBFXO25/1hw==", "license": "MIT", "dependencies": { "sf-symbols-typescript": "^2.0.0" @@ -1078,26 +1076,87 @@ "react-native": "*" } }, - "examples/example-expo/node_modules/expo-system-ui": { - "version": "6.0.9", + "examples/example-expo/node_modules/expo-updates": { + "version": "29.0.18", + "resolved": "https://registry.npmjs.org/expo-updates/-/expo-updates-29.0.18.tgz", + "integrity": "sha512-qn0YDM7wVwghQAaxb9NYzzwrmj+v8Djz5H+Zft4/myG9SPg4ICAfDy52IVF0K+/GH/oNgsFfzmmLl6hIkDu42g==", "license": "MIT", "dependencies": { - "@react-native/normalize-colors": "0.81.5", - "debug": "^4.3.2" + "@expo/code-signing-certificates": "^0.0.6", + "@expo/plist": "^0.4.9", + "@expo/spawn-async": "^1.7.2", + "arg": "4.1.0", + "chalk": "^4.1.2", + "debug": "^4.3.4", + "expo-eas-client": "~1.0.8", + "expo-manifests": "~1.0.11", + "expo-structured-headers": "~5.0.0", + "expo-updates-interface": "~2.0.0", + "getenv": "^2.0.0", + "glob": "^13.0.0", + "ignore": "^5.3.1", + "resolve-from": "^5.0.0" + }, + "bin": { + "expo-updates": "bin/cli.js" }, "peerDependencies": { "expo": "*", - "react-native": "*", - "react-native-web": "*" + "react": "*", + "react-native": "*" + } + }, + "examples/example-expo/node_modules/expo-updates-interface": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/expo-updates-interface/-/expo-updates-interface-2.0.0.tgz", + "integrity": "sha512-pTzAIufEZdVPKql6iMi5ylVSPqV1qbEopz9G6TSECQmnNde2nwq42PxdFBaUEd8IZJ/fdJLQnOT3m6+XJ5s7jg==", + "license": "MIT", + "peerDependencies": { + "expo": "*" + } + }, + "examples/example-expo/node_modules/expo-updates/node_modules/arg": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.0.tgz", + "integrity": "sha512-ZWc51jO3qegGkVh8Hwpv636EkbesNV5ZNQPCtRa+0qytRYPEs9IYT9qITY9buezqUH5uqyzlWLcufrzU2rffdg==", + "license": "MIT" + }, + "examples/example-expo/node_modules/expo-updates/node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" }, - "peerDependenciesMeta": { - "react-native-web": { - "optional": true - } + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "examples/example-expo/node_modules/expo-updates/node_modules/minimatch": { + "version": "10.2.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.5.tgz", + "integrity": "sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==", + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.5" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "examples/example-expo/node_modules/expo-web-browser": { "version": "15.0.11", + "resolved": "https://registry.npmjs.org/expo-web-browser/-/expo-web-browser-15.0.11.tgz", + "integrity": "sha512-r2LS4Ro6DgUPZkcaEfgt8mp9eJuoA93x11Jh7S6utFe0FEzvUNn2yFhxg8XVwESaaHGt2k5V8LuK36rsp0BeIw==", "license": "MIT", "peerDependencies": { "expo": "*", @@ -1462,6 +1521,7 @@ "examples/example-expo/node_modules/react-dom": { "version": "19.1.0", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.26.0" }, @@ -1524,19 +1584,6 @@ } } }, - "examples/example-expo/node_modules/react-native-gesture-handler": { - "version": "2.28.0", - "license": "MIT", - "dependencies": { - "@egjs/hammerjs": "^2.0.17", - "hoist-non-react-statics": "^3.3.0", - "invariant": "^2.2.4" - }, - "peerDependencies": { - "react": "*", - "react-native": "*" - } - }, "examples/example-expo/node_modules/react-native-is-edge-to-edge": { "version": "1.3.1", "license": "MIT", @@ -1547,6 +1594,8 @@ }, "examples/example-expo/node_modules/react-native-reanimated": { "version": "4.1.7", + "resolved": "https://registry.npmjs.org/react-native-reanimated/-/react-native-reanimated-4.1.7.tgz", + "integrity": "sha512-Q4H6xA3Tn7QL0/E/KjI86I1KK4tcf+ErRE04LH34Etka2oVQhW6oXQ+Q8ZcDCVxiWp5vgbBH6XcH8BOo4w/Rhg==", "license": "MIT", "dependencies": { "react-native-is-edge-to-edge": "^1.2.1", @@ -1559,7 +1608,9 @@ } }, "examples/example-expo/node_modules/react-native-reanimated/node_modules/semver": { - "version": "7.8.0", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -1590,30 +1641,37 @@ } }, "examples/example-expo/node_modules/react-native-worklets": { - "version": "0.5.1", + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/react-native-worklets/-/react-native-worklets-0.8.3.tgz", + "integrity": "sha512-oCBJROyLU7yG/1R8s0INMflygTH71bx+5XcYkH0CM938TlhSoVbiunE1WVW5FZa51vwYqfLie/IXMX2s1Kh3eg==", "license": "MIT", + "peer": true, "dependencies": { - "@babel/plugin-transform-arrow-functions": "^7.0.0-0", - "@babel/plugin-transform-class-properties": "^7.0.0-0", - "@babel/plugin-transform-classes": "^7.0.0-0", - "@babel/plugin-transform-nullish-coalescing-operator": "^7.0.0-0", - "@babel/plugin-transform-optional-chaining": "^7.0.0-0", - "@babel/plugin-transform-shorthand-properties": "^7.0.0-0", - "@babel/plugin-transform-template-literals": "^7.0.0-0", - "@babel/plugin-transform-unicode-regex": "^7.0.0-0", - "@babel/preset-typescript": "^7.16.7", + "@babel/plugin-transform-arrow-functions": "^7.27.1", + "@babel/plugin-transform-class-properties": "^7.27.1", + "@babel/plugin-transform-classes": "^7.28.4", + "@babel/plugin-transform-nullish-coalescing-operator": "^7.27.1", + "@babel/plugin-transform-optional-chaining": "^7.27.1", + "@babel/plugin-transform-shorthand-properties": "^7.27.1", + "@babel/plugin-transform-template-literals": "^7.27.1", + "@babel/plugin-transform-unicode-regex": "^7.27.1", + "@babel/preset-typescript": "^7.27.1", "convert-source-map": "^2.0.0", - "semver": "7.7.2" + "semver": "^7.7.3" }, "peerDependencies": { - "@babel/core": "^7.0.0-0", + "@babel/core": "*", + "@react-native/metro-config": "*", "react": "*", - "react-native": "*" + "react-native": "0.81 - 0.85" } }, "examples/example-expo/node_modules/react-native-worklets/node_modules/semver": { - "version": "7.7.2", + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", "license": "ISC", + "peer": true, "bin": { "semver": "bin/semver.js" }, @@ -1751,10 +1809,8 @@ "@typescript-eslint/eslint-plugin": "^6.7.5", "eslint": "^8.51.0", "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jest": "^27.4.2", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-prettier": "^5.0.1", - "jest": "^29.7.0", "typescript": "^5.4.0", "vite": "^5.2.0" } @@ -1928,10 +1984,8 @@ "@vitejs/plugin-react-swc": "^3.4.0", "eslint": "^8.51.0", "eslint-plugin-import": "^2.28.1", - "eslint-plugin-jest": "^27.4.2", "eslint-plugin-jsx-a11y": "^6.7.1", "eslint-plugin-prettier": "^5.0.1", - "jest": "^29.7.0", "typescript": "^5.2.2", "vite": "^4.4.11" } @@ -2354,6 +2408,51 @@ "async-limiter": "~1.0.0" } }, + "examples/example-svelte": { + "name": "@screeb/sdk-svelte-example", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@screeb/sdk-svelte": "*" + }, + "devDependencies": { + "@screeb/eslint-config": "^0.1.6", + "@sveltejs/vite-plugin-svelte": "^2.4.6", + "@tsconfig/svelte": "^5.0.2", + "@typescript-eslint/eslint-plugin": "^6.7.5", + "eslint": "^8.51.0", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-prettier": "^5.0.1", + "eslint-plugin-svelte": "^2.34.0", + "svelte": "^4.2.1", + "svelte-check": "^3.5.2", + "tslib": "^2.6.2", + "typescript": "^5.2.2", + "vite": "^4.4.11" + } + }, + "examples/example-svelte/node_modules/svelte-check": { + "version": "3.8.6", + "resolved": "https://registry.npmjs.org/svelte-check/-/svelte-check-3.8.6.tgz", + "integrity": "sha512-ij0u4Lw/sOTREP13BdWZjiXD/BlHE6/e2e34XzmVmsp5IN4kVa3PWP65NM32JAgwjZlwBg/+JtiNV1MM8khu0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.17", + "chokidar": "^3.4.1", + "picocolors": "^1.0.0", + "sade": "^1.7.4", + "svelte-preprocess": "^5.1.3", + "typescript": "^5.0.3" + }, + "bin": { + "svelte-check": "bin/svelte-check" + }, + "peerDependencies": { + "svelte": "^3.55.0 || ^4.0.0-next.0 || ^4.0.0 || ^5.0.0-next.0" + } + }, "examples/example-vue": { "name": "@screeb/sdk-vue-example", "version": "0.1.0", @@ -2389,6 +2488,7 @@ }, "node_modules/@ampproject/remapping": { "version": "2.2.1", + "dev": true, "license": "Apache-2.0", "dependencies": { "@jridgewell/gen-mapping": "^0.3.0", @@ -5556,6 +5656,7 @@ }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", + "dev": true, "license": "MIT" }, "node_modules/@colors/colors": { @@ -5568,6 +5669,7 @@ }, "node_modules/@commitlint/cli": { "version": "17.7.2", + "dev": true, "license": "MIT", "dependencies": { "@commitlint/format": "^17.4.4", @@ -5590,6 +5692,7 @@ }, "node_modules/@commitlint/config-conventional": { "version": "17.7.0", + "dev": true, "license": "MIT", "dependencies": { "conventional-changelog-conventionalcommits": "^6.1.0" @@ -5600,6 +5703,7 @@ }, "node_modules/@commitlint/config-validator": { "version": "17.6.7", + "dev": true, "license": "MIT", "dependencies": { "@commitlint/types": "^17.4.4", @@ -5611,6 +5715,7 @@ }, "node_modules/@commitlint/ensure": { "version": "17.6.7", + "dev": true, "license": "MIT", "dependencies": { "@commitlint/types": "^17.4.4", @@ -5626,6 +5731,7 @@ }, "node_modules/@commitlint/execute-rule": { "version": "17.4.0", + "dev": true, "license": "MIT", "engines": { "node": ">=v14" @@ -5633,6 +5739,7 @@ }, "node_modules/@commitlint/format": { "version": "17.4.4", + "dev": true, "license": "MIT", "dependencies": { "@commitlint/types": "^17.4.4", @@ -5644,6 +5751,7 @@ }, "node_modules/@commitlint/is-ignored": { "version": "17.7.0", + "dev": true, "license": "MIT", "dependencies": { "@commitlint/types": "^17.4.4", @@ -5655,6 +5763,7 @@ }, "node_modules/@commitlint/lint": { "version": "17.7.0", + "dev": true, "license": "MIT", "dependencies": { "@commitlint/is-ignored": "^17.7.0", @@ -5668,6 +5777,7 @@ }, "node_modules/@commitlint/load": { "version": "17.7.2", + "dev": true, "license": "MIT", "dependencies": { "@commitlint/config-validator": "^17.6.7", @@ -5691,6 +5801,7 @@ }, "node_modules/@commitlint/message": { "version": "17.4.2", + "dev": true, "license": "MIT", "engines": { "node": ">=v14" @@ -5698,6 +5809,7 @@ }, "node_modules/@commitlint/parse": { "version": "17.7.0", + "dev": true, "license": "MIT", "dependencies": { "@commitlint/types": "^17.4.4", @@ -5710,6 +5822,7 @@ }, "node_modules/@commitlint/read": { "version": "17.5.1", + "dev": true, "license": "MIT", "dependencies": { "@commitlint/top-level": "^17.4.0", @@ -5724,6 +5837,7 @@ }, "node_modules/@commitlint/resolve-extends": { "version": "17.6.7", + "dev": true, "license": "MIT", "dependencies": { "@commitlint/config-validator": "^17.6.7", @@ -5739,6 +5853,7 @@ }, "node_modules/@commitlint/rules": { "version": "17.7.0", + "dev": true, "license": "MIT", "dependencies": { "@commitlint/ensure": "^17.6.7", @@ -5753,6 +5868,7 @@ }, "node_modules/@commitlint/to-lines": { "version": "17.4.0", + "dev": true, "license": "MIT", "engines": { "node": ">=v14" @@ -5760,6 +5876,7 @@ }, "node_modules/@commitlint/top-level": { "version": "17.4.0", + "dev": true, "license": "MIT", "dependencies": { "find-up": "^5.0.0" @@ -5770,6 +5887,7 @@ }, "node_modules/@commitlint/types": { "version": "17.4.4", + "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.1.0" @@ -5780,6 +5898,7 @@ }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "0.3.9" @@ -5790,6 +5909,7 @@ }, "node_modules/@cspotcode/source-map-support/node_modules/@jridgewell/trace-mapping": { "version": "0.3.9", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.0.3", @@ -5804,18 +5924,9 @@ "node": ">=10.0.0" } }, - "node_modules/@egjs/hammerjs": { - "version": "2.0.17", - "license": "MIT", - "dependencies": { - "@types/hammerjs": "^2.0.36" - }, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/@emnapi/core": { "version": "1.10.0", + "dev": true, "license": "MIT", "dependencies": { "@emnapi/wasi-threads": "1.2.1", @@ -5824,6 +5935,7 @@ }, "node_modules/@emnapi/runtime": { "version": "1.10.0", + "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.4.0" @@ -5831,6 +5943,7 @@ }, "node_modules/@emnapi/wasi-threads": { "version": "1.2.1", + "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.4.0" @@ -7003,7 +7116,9 @@ } }, "node_modules/@expo/plist": { - "version": "0.4.8", + "version": "0.4.9", + "resolved": "https://registry.npmjs.org/@expo/plist/-/plist-0.4.9.tgz", + "integrity": "sha512-MPVpmKGfnQEnrCzgxuXcmPP/y/t6AVm+DcSb2Myp21LKWv1N3l8uFxMggesfF4ixAxkRlGmMMx9GyDC9M+XklQ==", "license": "MIT", "dependencies": { "@xmldom/xmldom": "^0.8.8", @@ -7157,6 +7272,7 @@ }, "node_modules/@hutson/parse-repository-url": { "version": "3.0.2", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">=6.9.0" @@ -7572,6 +7688,7 @@ }, "node_modules/@isaacs/cliui": { "version": "8.0.2", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^5.1.2", @@ -7587,6 +7704,7 @@ }, "node_modules/@isaacs/cliui/node_modules/ansi-regex": { "version": "6.1.0", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -7597,6 +7715,7 @@ }, "node_modules/@isaacs/cliui/node_modules/ansi-styles": { "version": "6.2.1", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -7607,10 +7726,12 @@ }, "node_modules/@isaacs/cliui/node_modules/emoji-regex": { "version": "9.2.2", + "dev": true, "license": "MIT" }, "node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", + "dev": true, "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", @@ -7626,6 +7747,7 @@ }, "node_modules/@isaacs/cliui/node_modules/strip-ansi": { "version": "7.1.0", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" @@ -7639,6 +7761,7 @@ }, "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { "version": "8.1.0", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", @@ -7671,6 +7794,7 @@ }, "node_modules/@isaacs/string-locale-compare": { "version": "1.1.0", + "dev": true, "license": "ISC" }, "node_modules/@isaacs/ttlcache": { @@ -7769,6 +7893,7 @@ }, "node_modules/@jest/console": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -7784,6 +7909,7 @@ }, "node_modules/@jest/core": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@jest/console": "^29.7.0", @@ -7852,6 +7978,7 @@ }, "node_modules/@jest/expect": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "expect": "^29.7.0", @@ -7863,6 +7990,7 @@ }, "node_modules/@jest/expect-utils": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "jest-get-type": "^29.6.3" @@ -7888,6 +8016,7 @@ }, "node_modules/@jest/globals": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", @@ -7901,6 +8030,7 @@ }, "node_modules/@jest/reporters": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^0.2.3", @@ -7952,6 +8082,7 @@ }, "node_modules/@jest/source-map": { "version": "29.6.3", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", @@ -7964,6 +8095,7 @@ }, "node_modules/@jest/test-result": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@jest/console": "^29.7.0", @@ -7977,6 +8109,7 @@ }, "node_modules/@jest/test-sequencer": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@jest/test-result": "^29.7.0", @@ -8090,6 +8223,7 @@ }, "node_modules/@lerna/create": { "version": "8.1.8", + "dev": true, "license": "MIT", "dependencies": { "@npmcli/arborist": "7.5.4", @@ -8170,6 +8304,7 @@ }, "node_modules/@lerna/create/node_modules/chalk": { "version": "4.1.0", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -8184,6 +8319,7 @@ }, "node_modules/@lerna/create/node_modules/execa": { "version": "5.0.0", + "dev": true, "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", @@ -8205,6 +8341,7 @@ }, "node_modules/@lerna/create/node_modules/get-stream": { "version": "6.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -8215,6 +8352,7 @@ }, "node_modules/@lerna/create/node_modules/is-stream": { "version": "2.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -8222,6 +8360,7 @@ }, "node_modules/@lerna/create/node_modules/minimatch": { "version": "3.0.5", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -8232,6 +8371,7 @@ }, "node_modules/@lerna/create/node_modules/yargs-parser": { "version": "21.1.1", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -8239,6 +8379,7 @@ }, "node_modules/@napi-rs/wasm-runtime": { "version": "0.2.4", + "dev": true, "license": "MIT", "dependencies": { "@emnapi/core": "^1.1.0", @@ -8321,6 +8462,7 @@ }, "node_modules/@npmcli/agent": { "version": "2.2.2", + "dev": true, "license": "ISC", "dependencies": { "agent-base": "^7.1.0", @@ -8335,10 +8477,12 @@ }, "node_modules/@npmcli/agent/node_modules/lru-cache": { "version": "10.4.3", + "dev": true, "license": "ISC" }, "node_modules/@npmcli/arborist": { "version": "7.5.4", + "dev": true, "license": "ISC", "dependencies": { "@isaacs/string-locale-compare": "^1.1.0", @@ -8386,6 +8530,7 @@ }, "node_modules/@npmcli/arborist/node_modules/brace-expansion": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -8393,6 +8538,7 @@ }, "node_modules/@npmcli/arborist/node_modules/hosted-git-info": { "version": "7.0.2", + "dev": true, "license": "ISC", "dependencies": { "lru-cache": "^10.0.1" @@ -8403,6 +8549,7 @@ }, "node_modules/@npmcli/arborist/node_modules/json-parse-even-better-errors": { "version": "3.0.2", + "dev": true, "license": "MIT", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -8410,10 +8557,12 @@ }, "node_modules/@npmcli/arborist/node_modules/lru-cache": { "version": "10.4.3", + "dev": true, "license": "ISC" }, "node_modules/@npmcli/arborist/node_modules/minimatch": { "version": "9.0.5", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -8427,6 +8576,7 @@ }, "node_modules/@npmcli/fs": { "version": "3.1.1", + "dev": true, "license": "ISC", "dependencies": { "semver": "^7.3.5" @@ -8437,6 +8587,7 @@ }, "node_modules/@npmcli/git": { "version": "5.0.8", + "dev": true, "license": "ISC", "dependencies": { "@npmcli/promise-spawn": "^7.0.0", @@ -8455,6 +8606,7 @@ }, "node_modules/@npmcli/git/node_modules/ini": { "version": "4.1.3", + "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -8462,6 +8614,7 @@ }, "node_modules/@npmcli/git/node_modules/isexe": { "version": "3.1.1", + "dev": true, "license": "ISC", "engines": { "node": ">=16" @@ -8469,10 +8622,12 @@ }, "node_modules/@npmcli/git/node_modules/lru-cache": { "version": "10.4.3", + "dev": true, "license": "ISC" }, "node_modules/@npmcli/git/node_modules/which": { "version": "4.0.0", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^3.1.1" @@ -8486,6 +8641,7 @@ }, "node_modules/@npmcli/installed-package-contents": { "version": "2.1.0", + "dev": true, "license": "ISC", "dependencies": { "npm-bundled": "^3.0.0", @@ -8500,6 +8656,7 @@ }, "node_modules/@npmcli/map-workspaces": { "version": "3.0.6", + "dev": true, "license": "ISC", "dependencies": { "@npmcli/name-from-folder": "^2.0.0", @@ -8513,6 +8670,7 @@ }, "node_modules/@npmcli/map-workspaces/node_modules/brace-expansion": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -8520,6 +8678,7 @@ }, "node_modules/@npmcli/map-workspaces/node_modules/glob": { "version": "10.4.5", + "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -8538,6 +8697,7 @@ }, "node_modules/@npmcli/map-workspaces/node_modules/minimatch": { "version": "9.0.5", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -8551,6 +8711,7 @@ }, "node_modules/@npmcli/map-workspaces/node_modules/minipass": { "version": "7.1.2", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -8558,6 +8719,7 @@ }, "node_modules/@npmcli/metavuln-calculator": { "version": "7.1.1", + "dev": true, "license": "ISC", "dependencies": { "cacache": "^18.0.0", @@ -8572,6 +8734,7 @@ }, "node_modules/@npmcli/metavuln-calculator/node_modules/json-parse-even-better-errors": { "version": "3.0.2", + "dev": true, "license": "MIT", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -8616,6 +8779,7 @@ }, "node_modules/@npmcli/name-from-folder": { "version": "2.0.0", + "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -8623,6 +8787,7 @@ }, "node_modules/@npmcli/node-gyp": { "version": "3.0.0", + "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -8630,6 +8795,7 @@ }, "node_modules/@npmcli/package-json": { "version": "5.2.0", + "dev": true, "license": "ISC", "dependencies": { "@npmcli/git": "^5.0.0", @@ -8646,6 +8812,7 @@ }, "node_modules/@npmcli/package-json/node_modules/brace-expansion": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -8653,6 +8820,7 @@ }, "node_modules/@npmcli/package-json/node_modules/glob": { "version": "10.4.5", + "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -8671,6 +8839,7 @@ }, "node_modules/@npmcli/package-json/node_modules/hosted-git-info": { "version": "7.0.2", + "dev": true, "license": "ISC", "dependencies": { "lru-cache": "^10.0.1" @@ -8681,6 +8850,7 @@ }, "node_modules/@npmcli/package-json/node_modules/json-parse-even-better-errors": { "version": "3.0.2", + "dev": true, "license": "MIT", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -8688,10 +8858,12 @@ }, "node_modules/@npmcli/package-json/node_modules/lru-cache": { "version": "10.4.3", + "dev": true, "license": "ISC" }, "node_modules/@npmcli/package-json/node_modules/minimatch": { "version": "9.0.5", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -8705,6 +8877,7 @@ }, "node_modules/@npmcli/package-json/node_modules/minipass": { "version": "7.1.2", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -8712,6 +8885,7 @@ }, "node_modules/@npmcli/package-json/node_modules/normalize-package-data": { "version": "6.0.2", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^7.0.0", @@ -8724,6 +8898,7 @@ }, "node_modules/@npmcli/promise-spawn": { "version": "7.0.2", + "dev": true, "license": "ISC", "dependencies": { "which": "^4.0.0" @@ -8734,6 +8909,7 @@ }, "node_modules/@npmcli/promise-spawn/node_modules/isexe": { "version": "3.1.1", + "dev": true, "license": "ISC", "engines": { "node": ">=16" @@ -8741,6 +8917,7 @@ }, "node_modules/@npmcli/promise-spawn/node_modules/which": { "version": "4.0.0", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^3.1.1" @@ -8754,6 +8931,7 @@ }, "node_modules/@npmcli/query": { "version": "3.1.0", + "dev": true, "license": "ISC", "dependencies": { "postcss-selector-parser": "^6.0.10" @@ -8764,6 +8942,7 @@ }, "node_modules/@npmcli/redact": { "version": "2.0.1", + "dev": true, "license": "ISC", "engines": { "node": "^16.14.0 || >=18.0.0" @@ -8771,6 +8950,7 @@ }, "node_modules/@npmcli/run-script": { "version": "8.1.0", + "dev": true, "license": "ISC", "dependencies": { "@npmcli/node-gyp": "^3.0.0", @@ -8786,6 +8966,7 @@ }, "node_modules/@npmcli/run-script/node_modules/isexe": { "version": "3.1.1", + "dev": true, "license": "ISC", "engines": { "node": ">=16" @@ -8793,6 +8974,7 @@ }, "node_modules/@npmcli/run-script/node_modules/which": { "version": "4.0.0", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^3.1.1" @@ -8806,6 +8988,7 @@ }, "node_modules/@nrwl/devkit": { "version": "19.8.2", + "dev": true, "license": "MIT", "dependencies": { "@nx/devkit": "19.8.2" @@ -8813,6 +8996,7 @@ }, "node_modules/@nrwl/tao": { "version": "19.8.2", + "dev": true, "license": "MIT", "dependencies": { "nx": "19.8.2", @@ -8824,6 +9008,7 @@ }, "node_modules/@nx/devkit": { "version": "19.8.2", + "dev": true, "license": "MIT", "dependencies": { "@nrwl/devkit": "19.8.2", @@ -8842,6 +9027,7 @@ }, "node_modules/@nx/devkit/node_modules/brace-expansion": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -8849,6 +9035,7 @@ }, "node_modules/@nx/devkit/node_modules/minimatch": { "version": "9.0.3", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -8862,6 +9049,7 @@ }, "node_modules/@nx/devkit/node_modules/yargs-parser": { "version": "21.1.1", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -8872,6 +9060,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8888,6 +9077,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8904,6 +9094,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8920,6 +9111,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8936,6 +9128,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8952,6 +9145,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8968,6 +9162,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -8984,6 +9179,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -9000,6 +9196,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -9016,6 +9213,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -9027,6 +9225,7 @@ }, "node_modules/@octokit/auth-token": { "version": "3.0.4", + "dev": true, "license": "MIT", "engines": { "node": ">= 14" @@ -9034,6 +9233,7 @@ }, "node_modules/@octokit/core": { "version": "4.2.4", + "dev": true, "license": "MIT", "dependencies": { "@octokit/auth-token": "^3.0.0", @@ -9050,6 +9250,7 @@ }, "node_modules/@octokit/endpoint": { "version": "7.0.6", + "dev": true, "license": "MIT", "dependencies": { "@octokit/types": "^9.0.0", @@ -9062,6 +9263,7 @@ }, "node_modules/@octokit/graphql": { "version": "5.0.6", + "dev": true, "license": "MIT", "dependencies": { "@octokit/request": "^6.0.0", @@ -9074,14 +9276,17 @@ }, "node_modules/@octokit/openapi-types": { "version": "18.1.1", + "dev": true, "license": "MIT" }, "node_modules/@octokit/plugin-enterprise-rest": { "version": "6.0.1", + "dev": true, "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { "version": "6.1.2", + "dev": true, "license": "MIT", "dependencies": { "@octokit/tsconfig": "^1.0.2", @@ -9096,6 +9301,7 @@ }, "node_modules/@octokit/plugin-request-log": { "version": "1.0.4", + "dev": true, "license": "MIT", "peerDependencies": { "@octokit/core": ">=3" @@ -9103,6 +9309,7 @@ }, "node_modules/@octokit/plugin-rest-endpoint-methods": { "version": "7.2.3", + "dev": true, "license": "MIT", "dependencies": { "@octokit/types": "^10.0.0" @@ -9116,6 +9323,7 @@ }, "node_modules/@octokit/plugin-rest-endpoint-methods/node_modules/@octokit/types": { "version": "10.0.0", + "dev": true, "license": "MIT", "dependencies": { "@octokit/openapi-types": "^18.0.0" @@ -9123,6 +9331,7 @@ }, "node_modules/@octokit/request": { "version": "6.2.8", + "dev": true, "license": "MIT", "dependencies": { "@octokit/endpoint": "^7.0.0", @@ -9138,6 +9347,7 @@ }, "node_modules/@octokit/request-error": { "version": "3.0.3", + "dev": true, "license": "MIT", "dependencies": { "@octokit/types": "^9.0.0", @@ -9150,6 +9360,7 @@ }, "node_modules/@octokit/rest": { "version": "19.0.11", + "dev": true, "license": "MIT", "dependencies": { "@octokit/core": "^4.2.1", @@ -9163,10 +9374,12 @@ }, "node_modules/@octokit/tsconfig": { "version": "1.0.2", + "dev": true, "license": "MIT" }, "node_modules/@octokit/types": { "version": "9.3.2", + "dev": true, "license": "MIT", "dependencies": { "@octokit/openapi-types": "^18.0.0" @@ -9186,6 +9399,7 @@ }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", + "dev": true, "license": "MIT", "optional": true, "engines": { @@ -9840,7 +10054,6 @@ }, "node_modules/@react-native/babel-preset": { "version": "0.81.1", - "devOptional": true, "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", @@ -9898,7 +10111,6 @@ }, "node_modules/@react-native/babel-preset/node_modules/@babel/plugin-transform-async-to-generator": { "version": "7.28.6", - "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.28.6", @@ -9914,7 +10126,6 @@ }, "node_modules/@react-native/babel-preset/node_modules/@babel/plugin-transform-runtime": { "version": "7.29.0", - "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.28.6", @@ -9933,7 +10144,6 @@ }, "node_modules/@react-native/babel-preset/node_modules/@react-native/babel-plugin-codegen": { "version": "0.81.1", - "devOptional": true, "license": "MIT", "dependencies": { "@babel/traverse": "^7.25.3", @@ -9945,7 +10155,6 @@ }, "node_modules/@react-native/babel-preset/node_modules/@react-native/codegen": { "version": "0.81.1", - "devOptional": true, "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", @@ -9965,7 +10174,6 @@ }, "node_modules/@react-native/babel-preset/node_modules/babel-plugin-polyfill-corejs3": { "version": "0.13.0", - "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.5", @@ -9977,7 +10185,6 @@ }, "node_modules/@react-native/babel-preset/node_modules/babel-plugin-polyfill-regenerator": { "version": "0.6.8", - "devOptional": true, "license": "MIT", "dependencies": { "@babel/helper-define-polyfill-provider": "^0.6.8" @@ -9988,7 +10195,6 @@ }, "node_modules/@react-native/babel-preset/node_modules/semver": { "version": "6.3.1", - "devOptional": true, "license": "ISC", "bin": { "semver": "bin/semver.js" @@ -10398,7 +10604,6 @@ }, "node_modules/@react-native/metro-babel-transformer": { "version": "0.81.1", - "devOptional": true, "license": "MIT", "dependencies": { "@babel/core": "^7.25.2", @@ -10415,7 +10620,6 @@ }, "node_modules/@react-native/metro-config": { "version": "0.81.1", - "devOptional": true, "license": "MIT", "dependencies": { "@react-native/js-polyfills": "0.81.1", @@ -10429,7 +10633,6 @@ }, "node_modules/@react-native/metro-config/node_modules/@react-native/js-polyfills": { "version": "0.81.1", - "devOptional": true, "license": "MIT", "engines": { "node": ">= 20.19.4" @@ -10662,6 +10865,28 @@ } } }, + "node_modules/@rollup/plugin-terser": { + "version": "0.4.4", + "resolved": "https://registry.npmjs.org/@rollup/plugin-terser/-/plugin-terser-0.4.4.tgz", + "integrity": "sha512-XHeJC5Bgvs8LfukDwWZp7yeqin6ns8RTl2B9avbejt6tZqsqvVoWI7ZTQrcNsfKEDWBTnTxM8nMDkO2IFFbd0A==", + "license": "MIT", + "dependencies": { + "serialize-javascript": "^6.0.1", + "smob": "^1.0.0", + "terser": "^5.17.4" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "rollup": "^2.0.0||^3.0.0||^4.0.0" + }, + "peerDependenciesMeta": { + "rollup": { + "optional": true + } + } + }, "node_modules/@rollup/plugin-typescript": { "version": "11.1.6", "license": "MIT", @@ -10981,6 +11206,14 @@ "resolved": "examples/example-react", "link": true }, + "node_modules/@screeb/sdk-svelte": { + "resolved": "packages/sdk-svelte", + "link": true + }, + "node_modules/@screeb/sdk-svelte-example": { + "resolved": "examples/example-svelte", + "link": true + }, "node_modules/@screeb/sdk-vue": { "resolved": "packages/sdk-vue", "link": true @@ -11017,6 +11250,7 @@ }, "node_modules/@sigstore/bundle": { "version": "2.3.2", + "dev": true, "license": "Apache-2.0", "dependencies": { "@sigstore/protobuf-specs": "^0.3.2" @@ -11027,6 +11261,7 @@ }, "node_modules/@sigstore/core": { "version": "1.1.0", + "dev": true, "license": "Apache-2.0", "engines": { "node": "^16.14.0 || >=18.0.0" @@ -11034,6 +11269,7 @@ }, "node_modules/@sigstore/protobuf-specs": { "version": "0.3.2", + "dev": true, "license": "Apache-2.0", "engines": { "node": "^16.14.0 || >=18.0.0" @@ -11041,6 +11277,7 @@ }, "node_modules/@sigstore/sign": { "version": "2.3.2", + "dev": true, "license": "Apache-2.0", "dependencies": { "@sigstore/bundle": "^2.3.2", @@ -11056,6 +11293,7 @@ }, "node_modules/@sigstore/tuf": { "version": "2.3.4", + "dev": true, "license": "Apache-2.0", "dependencies": { "@sigstore/protobuf-specs": "^0.3.2", @@ -11067,6 +11305,7 @@ }, "node_modules/@sigstore/verify": { "version": "1.2.1", + "dev": true, "license": "Apache-2.0", "dependencies": { "@sigstore/bundle": "^2.3.2", @@ -11243,7 +11482,7 @@ }, "node_modules/@swc/core": { "version": "1.7.26", - "devOptional": true, + "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { @@ -11283,6 +11522,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -11300,6 +11540,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -11317,6 +11558,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "Apache-2.0", "optional": true, "os": [ @@ -11334,6 +11576,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -11351,6 +11594,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -11368,6 +11612,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -11385,6 +11630,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -11402,6 +11648,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -11419,6 +11666,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -11436,6 +11684,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "Apache-2.0 AND MIT", "optional": true, "os": [ @@ -11448,12 +11697,12 @@ }, "node_modules/@swc/counter": { "version": "0.1.3", - "devOptional": true, + "dev": true, "license": "Apache-2.0" }, "node_modules/@swc/types": { "version": "0.1.12", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "@swc/counter": "^0.1.3" @@ -11474,26 +11723,32 @@ }, "node_modules/@tsconfig/node10": { "version": "1.0.9", + "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node12": { "version": "1.0.11", + "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node14": { "version": "1.0.3", + "dev": true, "license": "MIT" }, "node_modules/@tsconfig/node16": { "version": "1.0.4", + "dev": true, "license": "MIT" }, "node_modules/@tsconfig/svelte": { "version": "5.0.2", + "dev": true, "license": "MIT" }, "node_modules/@tufjs/canonical-json": { "version": "2.0.0", + "dev": true, "license": "MIT", "engines": { "node": "^16.14.0 || >=18.0.0" @@ -11501,6 +11756,7 @@ }, "node_modules/@tufjs/models": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "@tufjs/canonical-json": "2.0.0", @@ -11512,6 +11768,7 @@ }, "node_modules/@tufjs/models/node_modules/brace-expansion": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -11519,6 +11776,7 @@ }, "node_modules/@tufjs/models/node_modules/minimatch": { "version": "9.0.5", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -11532,6 +11790,7 @@ }, "node_modules/@tybys/wasm-util": { "version": "0.9.0", + "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.4.0" @@ -11669,10 +11928,6 @@ "@types/node": "*" } }, - "node_modules/@types/hammerjs": { - "version": "2.0.46", - "license": "MIT" - }, "node_modules/@types/http-errors": { "version": "2.0.4", "dev": true, @@ -11749,10 +12004,12 @@ }, "node_modules/@types/minimatch": { "version": "3.0.5", + "dev": true, "license": "MIT" }, "node_modules/@types/minimist": { "version": "1.2.3", + "dev": true, "license": "MIT" }, "node_modules/@types/node": { @@ -11769,6 +12026,7 @@ }, "node_modules/@types/normalize-package-data": { "version": "2.4.4", + "dev": true, "license": "MIT" }, "node_modules/@types/parse-path": { @@ -11783,6 +12041,7 @@ }, "node_modules/@types/pug": { "version": "2.0.7", + "dev": true, "license": "MIT" }, "node_modules/@types/qs": { @@ -12929,10 +13188,12 @@ }, "node_modules/@yarnpkg/lockfile": { "version": "1.1.0", + "dev": true, "license": "BSD-2-Clause" }, "node_modules/@yarnpkg/parsers": { "version": "3.0.0-rc.46", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "js-yaml": "^3.10.0", @@ -12944,6 +13205,7 @@ }, "node_modules/@yarnpkg/parsers/node_modules/argparse": { "version": "1.0.10", + "dev": true, "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" @@ -12951,6 +13213,7 @@ }, "node_modules/@yarnpkg/parsers/node_modules/js-yaml": { "version": "3.14.1", + "dev": true, "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -12962,10 +13225,12 @@ }, "node_modules/@yarnpkg/parsers/node_modules/sprintf-js": { "version": "1.0.3", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/@zkochan/js-yaml": { "version": "0.0.7", + "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" @@ -12981,6 +13246,7 @@ }, "node_modules/abbrev": { "version": "2.0.0", + "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -13043,6 +13309,7 @@ }, "node_modules/acorn-walk": { "version": "8.2.0", + "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -13050,6 +13317,7 @@ }, "node_modules/add-stream": { "version": "1.0.0", + "dev": true, "license": "MIT" }, "node_modules/adjust-sourcemap-loader": { @@ -13097,6 +13365,7 @@ }, "node_modules/aggregate-error": { "version": "3.1.0", + "dev": true, "license": "MIT", "dependencies": { "clean-stack": "^2.0.0", @@ -13108,6 +13377,7 @@ }, "node_modules/ajv": { "version": "8.12.0", + "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", @@ -13160,6 +13430,7 @@ }, "node_modules/ansi-colors": { "version": "4.1.3", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -13270,6 +13541,7 @@ }, "node_modules/aproba": { "version": "2.0.0", + "dev": true, "license": "ISC" }, "node_modules/are-we-there-yet": { @@ -13286,6 +13558,7 @@ }, "node_modules/arg": { "version": "4.1.3", + "dev": true, "license": "MIT" }, "node_modules/argparse": { @@ -13304,6 +13577,7 @@ }, "node_modules/aria-query": { "version": "5.3.0", + "dev": true, "license": "Apache-2.0", "dependencies": { "dequal": "^2.0.3" @@ -13344,6 +13618,7 @@ }, "node_modules/array-differ": { "version": "3.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -13356,6 +13631,7 @@ }, "node_modules/array-ify": { "version": "1.0.0", + "dev": true, "license": "MIT" }, "node_modules/array-includes": { @@ -13493,6 +13769,7 @@ }, "node_modules/arrify": { "version": "1.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -13528,6 +13805,7 @@ }, "node_modules/async": { "version": "3.2.6", + "dev": true, "license": "MIT" }, "node_modules/async-function": { @@ -13560,6 +13838,7 @@ }, "node_modules/asynckit": { "version": "0.4.0", + "dev": true, "license": "MIT" }, "node_modules/autoprefixer": { @@ -13618,6 +13897,7 @@ }, "node_modules/axios": { "version": "1.7.7", + "dev": true, "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -13627,6 +13907,7 @@ }, "node_modules/axobject-query": { "version": "4.1.0", + "dev": true, "license": "Apache-2.0", "engines": { "node": ">= 0.4" @@ -13905,6 +14186,7 @@ }, "node_modules/before-after-hook": { "version": "2.2.3", + "dev": true, "license": "Apache-2.0" }, "node_modules/better-opn": { @@ -13934,6 +14216,7 @@ }, "node_modules/bin-links": { "version": "4.0.4", + "dev": true, "license": "ISC", "dependencies": { "cmd-shim": "^6.0.0", @@ -13947,6 +14230,7 @@ }, "node_modules/binary-extensions": { "version": "2.2.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -13954,6 +14238,7 @@ }, "node_modules/bl": { "version": "4.1.0", + "devOptional": true, "license": "MIT", "dependencies": { "buffer": "^5.5.0", @@ -14124,6 +14409,7 @@ }, "node_modules/buffer-crc32": { "version": "0.2.13", + "dev": true, "license": "MIT", "engines": { "node": "*" @@ -14149,6 +14435,7 @@ }, "node_modules/byte-size": { "version": "8.1.1", + "dev": true, "license": "MIT", "engines": { "node": ">=12.17" @@ -14236,6 +14523,7 @@ }, "node_modules/cacache": { "version": "18.0.4", + "dev": true, "license": "ISC", "dependencies": { "@npmcli/fs": "^3.1.0", @@ -14257,6 +14545,7 @@ }, "node_modules/cacache/node_modules/brace-expansion": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -14264,6 +14553,7 @@ }, "node_modules/cacache/node_modules/glob": { "version": "10.4.5", + "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -14282,10 +14572,12 @@ }, "node_modules/cacache/node_modules/lru-cache": { "version": "10.4.3", + "dev": true, "license": "ISC" }, "node_modules/cacache/node_modules/minimatch": { "version": "9.0.5", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -14299,6 +14591,7 @@ }, "node_modules/cacache/node_modules/minipass": { "version": "7.1.2", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -14364,6 +14657,7 @@ }, "node_modules/camelcase-keys": { "version": "6.2.2", + "dev": true, "license": "MIT", "dependencies": { "camelcase": "^5.3.1", @@ -14411,6 +14705,7 @@ }, "node_modules/char-regex": { "version": "1.0.2", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -14418,10 +14713,12 @@ }, "node_modules/chardet": { "version": "0.7.0", + "dev": true, "license": "MIT" }, "node_modules/chokidar": { "version": "3.5.3", + "dev": true, "funding": [ { "type": "individual", @@ -14447,6 +14744,7 @@ }, "node_modules/chokidar/node_modules/glob-parent": { "version": "5.1.2", + "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" @@ -14457,6 +14755,7 @@ }, "node_modules/chownr": { "version": "2.0.0", + "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -14564,10 +14863,12 @@ }, "node_modules/cjs-module-lexer": { "version": "1.4.1", + "dev": true, "license": "MIT" }, "node_modules/clean-stack": { "version": "2.2.0", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -14575,6 +14876,7 @@ }, "node_modules/cli-cursor": { "version": "3.1.0", + "devOptional": true, "license": "MIT", "dependencies": { "restore-cursor": "^3.1.0" @@ -14595,6 +14897,7 @@ }, "node_modules/cli-width": { "version": "3.0.0", + "dev": true, "license": "ISC", "engines": { "node": ">= 10" @@ -14625,6 +14928,7 @@ }, "node_modules/clone-deep": { "version": "4.0.1", + "dev": true, "license": "MIT", "dependencies": { "is-plain-object": "^2.0.4", @@ -14637,6 +14941,7 @@ }, "node_modules/clone-deep/node_modules/is-plain-object": { "version": "2.0.4", + "dev": true, "license": "MIT", "dependencies": { "isobject": "^3.0.1" @@ -14647,6 +14952,7 @@ }, "node_modules/cmd-shim": { "version": "6.0.3", + "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -14654,6 +14960,7 @@ }, "node_modules/co": { "version": "4.6.0", + "dev": true, "license": "MIT", "engines": { "iojs": ">= 1.0.0", @@ -14662,6 +14969,7 @@ }, "node_modules/code-red": { "version": "1.0.4", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.15", @@ -14673,6 +14981,7 @@ }, "node_modules/collect-v8-coverage": { "version": "1.0.2", + "dev": true, "license": "MIT" }, "node_modules/color": { @@ -14710,6 +15019,7 @@ }, "node_modules/color-support": { "version": "1.1.3", + "dev": true, "license": "ISC", "bin": { "color-support": "bin.js" @@ -14722,6 +15032,7 @@ }, "node_modules/columnify": { "version": "1.6.0", + "dev": true, "license": "MIT", "dependencies": { "strip-ansi": "^6.0.1", @@ -14733,6 +15044,7 @@ }, "node_modules/combined-stream": { "version": "1.0.8", + "dev": true, "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" @@ -15222,6 +15534,7 @@ }, "node_modules/common-ancestor-path": { "version": "1.0.1", + "dev": true, "license": "ISC" }, "node_modules/common-path-prefix": { @@ -15236,6 +15549,7 @@ }, "node_modules/compare-func": { "version": "2.0.0", + "dev": true, "license": "MIT", "dependencies": { "array-ify": "^1.0.0", @@ -15296,6 +15610,7 @@ }, "node_modules/concat-stream": { "version": "2.0.0", + "dev": true, "engines": [ "node >= 6.0" ], @@ -15354,6 +15669,7 @@ }, "node_modules/console-control-strings": { "version": "1.1.0", + "dev": true, "license": "ISC" }, "node_modules/content-disposition": { @@ -15399,6 +15715,7 @@ }, "node_modules/conventional-changelog-angular": { "version": "6.0.0", + "dev": true, "license": "ISC", "dependencies": { "compare-func": "^2.0.0" @@ -15409,6 +15726,7 @@ }, "node_modules/conventional-changelog-conventionalcommits": { "version": "6.1.0", + "dev": true, "license": "ISC", "dependencies": { "compare-func": "^2.0.0" @@ -15419,6 +15737,7 @@ }, "node_modules/conventional-changelog-core": { "version": "5.0.1", + "dev": true, "license": "MIT", "dependencies": { "add-stream": "^1.0.0", @@ -15439,6 +15758,7 @@ }, "node_modules/conventional-changelog-core/node_modules/git-raw-commits": { "version": "3.0.0", + "dev": true, "license": "MIT", "dependencies": { "dargs": "^7.0.0", @@ -15454,6 +15774,7 @@ }, "node_modules/conventional-changelog-preset-loader": { "version": "3.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=14" @@ -15461,6 +15782,7 @@ }, "node_modules/conventional-changelog-writer": { "version": "6.0.1", + "dev": true, "license": "MIT", "dependencies": { "conventional-commits-filter": "^3.0.0", @@ -15594,6 +15916,7 @@ }, "node_modules/conventional-commits-filter": { "version": "3.0.0", + "dev": true, "license": "MIT", "dependencies": { "lodash.ismatch": "^4.4.0", @@ -15605,6 +15928,7 @@ }, "node_modules/conventional-commits-parser": { "version": "4.0.0", + "dev": true, "license": "MIT", "dependencies": { "is-text-path": "^1.0.1", @@ -15621,6 +15945,7 @@ }, "node_modules/conventional-recommended-bump": { "version": "7.0.1", + "dev": true, "license": "MIT", "dependencies": { "concat-stream": "^2.0.0", @@ -15640,6 +15965,7 @@ }, "node_modules/conventional-recommended-bump/node_modules/git-raw-commits": { "version": "3.0.0", + "dev": true, "license": "MIT", "dependencies": { "dargs": "^7.0.0", @@ -15672,7 +15998,7 @@ }, "node_modules/copy-anything": { "version": "2.0.6", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "is-what": "^3.14.1" @@ -15746,6 +16072,7 @@ }, "node_modules/core-util-is": { "version": "1.0.3", + "dev": true, "license": "MIT" }, "node_modules/cors": { @@ -15762,6 +16089,7 @@ }, "node_modules/cosmiconfig": { "version": "8.3.6", + "dev": true, "license": "MIT", "dependencies": { "import-fresh": "^3.3.0", @@ -15786,6 +16114,7 @@ }, "node_modules/cosmiconfig-typescript-loader": { "version": "4.4.0", + "dev": true, "license": "MIT", "engines": { "node": ">=v14.21.3" @@ -15799,6 +16128,7 @@ }, "node_modules/create-jest": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -15818,6 +16148,7 @@ }, "node_modules/create-require": { "version": "1.1.1", + "dev": true, "license": "MIT" }, "node_modules/critters": { @@ -15834,31 +16165,6 @@ "pretty-bytes": "^5.3.0" } }, - "node_modules/cross-fetch": { - "version": "3.2.0", - "license": "MIT", - "dependencies": { - "node-fetch": "^2.7.0" - } - }, - "node_modules/cross-fetch/node_modules/node-fetch": { - "version": "2.7.0", - "license": "MIT", - "dependencies": { - "whatwg-url": "^5.0.0" - }, - "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" - }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "license": "MIT", @@ -15871,13 +16177,6 @@ "node": ">= 8" } }, - "node_modules/css-in-js-utils": { - "version": "3.1.0", - "license": "MIT", - "dependencies": { - "hyphenate-style-name": "^1.0.3" - } - }, "node_modules/css-loader": { "version": "6.8.1", "dev": true, @@ -15920,6 +16219,7 @@ }, "node_modules/css-tree": { "version": "2.3.1", + "dev": true, "license": "MIT", "dependencies": { "mdn-data": "2.0.30", @@ -15942,6 +16242,7 @@ }, "node_modules/cssesc": { "version": "3.0.0", + "dev": true, "license": "MIT", "bin": { "cssesc": "bin/cssesc" @@ -15992,6 +16293,7 @@ }, "node_modules/dargs": { "version": "7.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -16107,6 +16409,7 @@ }, "node_modules/dateformat": { "version": "3.0.3", + "dev": true, "license": "MIT", "engines": { "node": "*" @@ -16141,6 +16444,7 @@ }, "node_modules/decamelize": { "version": "1.2.0", + "devOptional": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -16148,6 +16452,7 @@ }, "node_modules/decamelize-keys": { "version": "1.1.1", + "dev": true, "license": "MIT", "dependencies": { "decamelize": "^1.1.0", @@ -16162,6 +16467,7 @@ }, "node_modules/decamelize-keys/node_modules/map-obj": { "version": "1.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -16181,6 +16487,7 @@ }, "node_modules/dedent": { "version": "1.5.3", + "dev": true, "license": "MIT", "peerDependencies": { "babel-plugin-macros": "^3.1.0" @@ -16483,6 +16790,7 @@ }, "node_modules/delayed-stream": { "version": "1.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=0.4.0" @@ -16510,10 +16818,12 @@ }, "node_modules/deprecation": { "version": "2.3.1", + "dev": true, "license": "ISC" }, "node_modules/dequal": { "version": "2.0.3", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -16534,6 +16844,7 @@ }, "node_modules/detect-indent": { "version": "6.1.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -16548,6 +16859,7 @@ }, "node_modules/detect-newline": { "version": "3.1.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -16569,6 +16881,7 @@ }, "node_modules/diff": { "version": "4.0.2", + "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" @@ -16576,6 +16889,7 @@ }, "node_modules/diff-sequences": { "version": "29.6.3", + "dev": true, "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" @@ -16695,6 +17009,7 @@ }, "node_modules/dot-prop": { "version": "5.3.0", + "dev": true, "license": "MIT", "dependencies": { "is-obj": "^2.0.0" @@ -16741,10 +17056,12 @@ }, "node_modules/duplexer": { "version": "0.1.2", + "dev": true, "license": "MIT" }, "node_modules/eastasianwidth": { "version": "0.2.0", + "dev": true, "license": "MIT" }, "node_modules/ee-first": { @@ -16753,6 +17070,7 @@ }, "node_modules/ejs": { "version": "3.1.10", + "dev": true, "license": "Apache-2.0", "dependencies": { "jake": "^10.8.5" @@ -16770,6 +17088,7 @@ }, "node_modules/emittery": { "version": "0.13.1", + "dev": true, "license": "MIT", "engines": { "node": ">=12" @@ -16799,6 +17118,7 @@ }, "node_modules/encoding": { "version": "0.1.13", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -16807,6 +17127,7 @@ }, "node_modules/encoding/node_modules/iconv-lite": { "version": "0.6.3", + "dev": true, "license": "MIT", "optional": true, "dependencies": { @@ -16818,6 +17139,7 @@ }, "node_modules/end-of-stream": { "version": "1.4.4", + "dev": true, "license": "MIT", "dependencies": { "once": "^1.4.0" @@ -16885,6 +17207,7 @@ }, "node_modules/enquirer": { "version": "2.3.6", + "dev": true, "license": "MIT", "dependencies": { "ansi-colors": "^4.1.1" @@ -16929,6 +17252,7 @@ }, "node_modules/env-paths": { "version": "2.2.1", + "devOptional": true, "license": "MIT", "engines": { "node": ">=6" @@ -16936,6 +17260,7 @@ }, "node_modules/envinfo": { "version": "7.13.0", + "devOptional": true, "license": "MIT", "bin": { "envinfo": "dist/cli.js" @@ -16946,10 +17271,12 @@ }, "node_modules/err-code": { "version": "2.0.3", + "dev": true, "license": "MIT" }, "node_modules/errno": { "version": "0.1.8", + "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -16962,6 +17289,7 @@ }, "node_modules/error-ex": { "version": "1.3.2", + "devOptional": true, "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" @@ -17178,6 +17506,7 @@ }, "node_modules/es6-promise": { "version": "3.3.1", + "dev": true, "license": "MIT" }, "node_modules/esbuild": { @@ -19105,6 +19434,7 @@ }, "node_modules/estree-walker": { "version": "3.0.3", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.0" @@ -19149,6 +19479,7 @@ }, "node_modules/eventemitter3": { "version": "4.0.7", + "dev": true, "license": "MIT" }, "node_modules/events": { @@ -19169,6 +19500,7 @@ }, "node_modules/execa": { "version": "5.1.1", + "devOptional": true, "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", @@ -19190,12 +19522,14 @@ }, "node_modules/exit": { "version": "0.1.2", + "dev": true, "engines": { "node": ">= 0.8.0" } }, "node_modules/expect": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@jest/expect-utils": "^29.7.0", @@ -19208,6 +19542,18 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, + "node_modules/expo-eas-client": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/expo-eas-client/-/expo-eas-client-1.0.8.tgz", + "integrity": "sha512-5or11NJhSeDoHHI6zyvQDW2cz/yFyE+1Cz8NTs5NK8JzC7J0JrkUgptWtxyfB6Xs/21YRNifd3qgbBN3hfKVgA==", + "license": "MIT" + }, + "node_modules/expo-json-utils": { + "version": "0.15.0", + "resolved": "https://registry.npmjs.org/expo-json-utils/-/expo-json-utils-0.15.0.tgz", + "integrity": "sha512-duRT6oGl80IDzH2LD2yEFWNwGIC2WkozsB6HF3cDYNoNNdUvFk6uN3YiwsTsqVM/D0z6LEAQ01/SlYvN+Fw0JQ==", + "license": "MIT" + }, "node_modules/expo-modules-autolinking": { "version": "3.0.25", "license": "MIT", @@ -19230,12 +19576,20 @@ } }, "node_modules/expo-server": { - "version": "1.0.6", + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/expo-server/-/expo-server-1.0.7.tgz", + "integrity": "sha512-mcmyML3oXcqFUXUxtdtCL1O00ztNI2v76d+MdniXRUgHNxIcHZ05zo+DqBaOOT6LQnPk4vA4YHqQl7iGUfRb3g==", "license": "MIT", "engines": { "node": ">=20.16.0" } }, + "node_modules/expo-structured-headers": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/expo-structured-headers/-/expo-structured-headers-5.0.0.tgz", + "integrity": "sha512-RmrBtnSphk5REmZGV+lcdgdpxyzio5rJw8CXviHE6qH5pKQQ83fhMEcigvrkBdsn2Efw2EODp4Yxl1/fqMvOZw==", + "license": "MIT" + }, "node_modules/exponential-backoff": { "version": "3.1.1", "license": "Apache-2.0" @@ -19347,6 +19701,7 @@ }, "node_modules/external-editor": { "version": "3.1.0", + "dev": true, "license": "MIT", "dependencies": { "chardet": "^0.7.0", @@ -19359,6 +19714,7 @@ }, "node_modules/external-editor/node_modules/tmp": { "version": "0.0.33", + "dev": true, "license": "MIT", "dependencies": { "os-tmpdir": "~1.0.2" @@ -19465,54 +19821,6 @@ "bser": "2.1.1" } }, - "node_modules/fbjs": { - "version": "3.0.5", - "license": "MIT", - "dependencies": { - "cross-fetch": "^3.1.5", - "fbjs-css-vars": "^1.0.0", - "loose-envify": "^1.0.0", - "object-assign": "^4.1.0", - "promise": "^7.1.1", - "setimmediate": "^1.0.5", - "ua-parser-js": "^1.0.35" - } - }, - "node_modules/fbjs-css-vars": { - "version": "1.0.2", - "license": "MIT" - }, - "node_modules/fbjs/node_modules/promise": { - "version": "7.3.1", - "license": "MIT", - "dependencies": { - "asap": "~2.0.3" - } - }, - "node_modules/fbjs/node_modules/ua-parser-js": { - "version": "1.0.41", - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/ua-parser-js" - }, - { - "type": "paypal", - "url": "https://paypal.me/faisalman" - }, - { - "type": "github", - "url": "https://github.com/sponsors/faisalman" - } - ], - "license": "MIT", - "bin": { - "ua-parser-js": "script/cli.js" - }, - "engines": { - "node": "*" - } - }, "node_modules/fd-package-json": { "version": "2.0.0", "dev": true, @@ -19531,6 +19839,7 @@ }, "node_modules/figures": { "version": "3.2.0", + "dev": true, "license": "MIT", "dependencies": { "escape-string-regexp": "^1.0.5" @@ -19554,6 +19863,7 @@ }, "node_modules/filelist": { "version": "1.0.4", + "dev": true, "license": "Apache-2.0", "dependencies": { "minimatch": "^5.0.1" @@ -19561,6 +19871,7 @@ }, "node_modules/filelist/node_modules/brace-expansion": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -19568,6 +19879,7 @@ }, "node_modules/filelist/node_modules/minimatch": { "version": "5.1.6", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -19751,6 +20063,7 @@ }, "node_modules/flat": { "version": "5.0.2", + "dev": true, "license": "BSD-3-Clause", "bin": { "flat": "cli.js" @@ -19791,6 +20104,7 @@ }, "node_modules/follow-redirects": { "version": "1.15.9", + "dev": true, "funding": [ { "type": "individual", @@ -19827,6 +20141,7 @@ }, "node_modules/foreground-child": { "version": "3.3.0", + "dev": true, "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", @@ -19841,6 +20156,7 @@ }, "node_modules/foreground-child/node_modules/signal-exit": { "version": "4.1.0", + "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -19851,6 +20167,7 @@ }, "node_modules/form-data": { "version": "4.0.0", + "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -19897,6 +20214,7 @@ }, "node_modules/front-matter": { "version": "4.0.2", + "dev": true, "license": "MIT", "dependencies": { "js-yaml": "^3.13.1" @@ -19904,6 +20222,7 @@ }, "node_modules/front-matter/node_modules/argparse": { "version": "1.0.10", + "dev": true, "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" @@ -19911,6 +20230,7 @@ }, "node_modules/front-matter/node_modules/js-yaml": { "version": "3.14.1", + "dev": true, "license": "MIT", "dependencies": { "argparse": "^1.0.7", @@ -19922,14 +20242,17 @@ }, "node_modules/front-matter/node_modules/sprintf-js": { "version": "1.0.3", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/fs-constants": { "version": "1.0.0", + "dev": true, "license": "MIT" }, "node_modules/fs-extra": { "version": "11.2.0", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", @@ -19942,6 +20265,7 @@ }, "node_modules/fs-minipass": { "version": "3.0.3", + "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.3" @@ -19952,6 +20276,7 @@ }, "node_modules/fs-minipass/node_modules/minipass": { "version": "7.1.2", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -20101,6 +20426,7 @@ }, "node_modules/get-pkg-repo": { "version": "4.2.1", + "dev": true, "license": "MIT", "dependencies": { "@hutson/parse-repository-url": "^3.0.0", @@ -20117,6 +20443,7 @@ }, "node_modules/get-pkg-repo/node_modules/cliui": { "version": "7.0.4", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^4.2.0", @@ -20126,6 +20453,7 @@ }, "node_modules/get-pkg-repo/node_modules/readable-stream": { "version": "2.3.8", + "dev": true, "license": "MIT", "dependencies": { "core-util-is": "~1.0.0", @@ -20139,10 +20467,12 @@ }, "node_modules/get-pkg-repo/node_modules/safe-buffer": { "version": "5.1.2", + "dev": true, "license": "MIT" }, "node_modules/get-pkg-repo/node_modules/string_decoder": { "version": "1.1.1", + "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.1.0" @@ -20150,6 +20480,7 @@ }, "node_modules/get-pkg-repo/node_modules/through2": { "version": "2.0.5", + "dev": true, "license": "MIT", "dependencies": { "readable-stream": "~2.3.6", @@ -20158,6 +20489,7 @@ }, "node_modules/get-pkg-repo/node_modules/yargs": { "version": "16.2.0", + "dev": true, "license": "MIT", "dependencies": { "cliui": "^7.0.2", @@ -20174,6 +20506,7 @@ }, "node_modules/get-port": { "version": "5.1.1", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -20196,6 +20529,7 @@ }, "node_modules/get-stream": { "version": "6.0.1", + "devOptional": true, "license": "MIT", "engines": { "node": ">=10" @@ -20269,6 +20603,7 @@ }, "node_modules/git-raw-commits": { "version": "2.0.11", + "dev": true, "license": "MIT", "dependencies": { "dargs": "^7.0.0", @@ -20286,6 +20621,7 @@ }, "node_modules/git-remote-origin-url": { "version": "2.0.0", + "dev": true, "license": "MIT", "dependencies": { "gitconfiglocal": "^1.0.0", @@ -20297,6 +20633,7 @@ }, "node_modules/git-remote-origin-url/node_modules/pify": { "version": "2.3.0", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -20304,6 +20641,7 @@ }, "node_modules/git-semver-tags": { "version": "5.0.1", + "dev": true, "license": "MIT", "dependencies": { "meow": "^8.1.2", @@ -20318,6 +20656,7 @@ }, "node_modules/git-up": { "version": "7.0.0", + "dev": true, "license": "MIT", "dependencies": { "is-ssh": "^1.4.0", @@ -20326,6 +20665,7 @@ }, "node_modules/git-url-parse": { "version": "14.0.0", + "dev": true, "license": "MIT", "dependencies": { "git-up": "^7.0.0" @@ -20333,6 +20673,7 @@ }, "node_modules/gitconfiglocal": { "version": "1.0.0", + "dev": true, "license": "BSD", "dependencies": { "ini": "^1.3.2" @@ -20395,6 +20736,7 @@ }, "node_modules/global-dirs": { "version": "0.1.1", + "dev": true, "license": "MIT", "dependencies": { "ini": "^1.3.4" @@ -20496,6 +20838,7 @@ }, "node_modules/handlebars": { "version": "4.7.8", + "dev": true, "license": "MIT", "dependencies": { "minimist": "^1.2.5", @@ -20515,6 +20858,7 @@ }, "node_modules/hard-rejection": { "version": "2.1.0", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -20587,6 +20931,7 @@ }, "node_modules/has-unicode": { "version": "2.0.1", + "dev": true, "license": "ISC" }, "node_modules/hasown": { @@ -20635,19 +20980,9 @@ "hermes-estree": "0.29.1" } }, - "node_modules/hoist-non-react-statics": { - "version": "3.3.2", - "license": "BSD-3-Clause", - "dependencies": { - "react-is": "^16.7.0" - } - }, - "node_modules/hoist-non-react-statics/node_modules/react-is": { - "version": "16.13.1", - "license": "MIT" - }, "node_modules/hosted-git-info": { "version": "4.1.0", + "dev": true, "license": "ISC", "dependencies": { "lru-cache": "^6.0.0" @@ -20658,6 +20993,7 @@ }, "node_modules/hosted-git-info/node_modules/lru-cache": { "version": "6.0.0", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -20668,6 +21004,7 @@ }, "node_modules/hosted-git-info/node_modules/yallist": { "version": "4.0.0", + "dev": true, "license": "ISC" }, "node_modules/hpack.js": { @@ -20736,6 +21073,7 @@ }, "node_modules/html-escaper": { "version": "2.0.2", + "dev": true, "license": "MIT" }, "node_modules/htmlparser2": { @@ -20758,6 +21096,7 @@ }, "node_modules/http-cache-semantics": { "version": "4.1.1", + "dev": true, "license": "BSD-2-Clause" }, "node_modules/http-deceiver": { @@ -20806,6 +21145,7 @@ }, "node_modules/http-proxy-agent": { "version": "7.0.2", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.0", @@ -20862,6 +21202,7 @@ }, "node_modules/human-signals": { "version": "2.1.0", + "devOptional": true, "license": "Apache-2.0", "engines": { "node": ">=10.17.0" @@ -20877,6 +21218,7 @@ }, "node_modules/husky": { "version": "8.0.3", + "dev": true, "license": "MIT", "bin": { "husky": "lib/bin.js" @@ -20888,12 +21230,9 @@ "url": "https://github.com/sponsors/typicode" } }, - "node_modules/hyphenate-style-name": { - "version": "1.1.0", - "license": "BSD-3-Clause" - }, "node_modules/iconv-lite": { "version": "0.4.24", + "devOptional": true, "license": "MIT", "dependencies": { "safer-buffer": ">= 2.1.2 < 3" @@ -20940,6 +21279,7 @@ }, "node_modules/ignore-walk": { "version": "6.0.5", + "dev": true, "license": "ISC", "dependencies": { "minimatch": "^9.0.0" @@ -20950,6 +21290,7 @@ }, "node_modules/ignore-walk/node_modules/brace-expansion": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -20957,6 +21298,7 @@ }, "node_modules/ignore-walk/node_modules/minimatch": { "version": "9.0.5", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -20970,6 +21312,7 @@ }, "node_modules/image-size": { "version": "0.5.5", + "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -20982,7 +21325,7 @@ }, "node_modules/immutable": { "version": "4.3.4", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/import-fresh": { @@ -21008,6 +21351,7 @@ }, "node_modules/import-local": { "version": "3.1.0", + "dev": true, "license": "MIT", "dependencies": { "pkg-dir": "^4.2.0", @@ -21041,6 +21385,7 @@ }, "node_modules/indent-string": { "version": "4.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -21069,6 +21414,7 @@ }, "node_modules/init-package-json": { "version": "6.0.3", + "dev": true, "license": "ISC", "dependencies": { "@npmcli/package-json": "^5.0.0", @@ -21091,15 +21437,9 @@ "tslib": "^2.0.0" } }, - "node_modules/inline-style-prefixer": { - "version": "7.0.1", - "license": "MIT", - "dependencies": { - "css-in-js-utils": "^3.1.0" - } - }, "node_modules/inquirer": { "version": "8.2.6", + "dev": true, "license": "MIT", "dependencies": { "ansi-escapes": "^4.2.1", @@ -21124,6 +21464,7 @@ }, "node_modules/inquirer/node_modules/wrap-ansi": { "version": "6.2.0", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -21165,6 +21506,7 @@ }, "node_modules/ip-address": { "version": "9.0.5", + "dev": true, "license": "MIT", "dependencies": { "jsbn": "1.1.0", @@ -21227,6 +21569,7 @@ }, "node_modules/is-arrayish": { "version": "0.2.1", + "devOptional": true, "license": "MIT" }, "node_modules/is-async-function": { @@ -21263,6 +21606,7 @@ }, "node_modules/is-binary-path": { "version": "2.1.0", + "dev": true, "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" @@ -21318,6 +21662,7 @@ }, "node_modules/is-ci": { "version": "3.0.1", + "dev": true, "license": "MIT", "dependencies": { "ci-info": "^3.2.0" @@ -21413,6 +21758,7 @@ }, "node_modules/is-generator-fn": { "version": "2.1.0", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -21588,6 +21934,7 @@ }, "node_modules/is-interactive": { "version": "1.0.0", + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -21595,6 +21942,7 @@ }, "node_modules/is-lambda": { "version": "1.0.1", + "dev": true, "license": "MIT" }, "node_modules/is-map": { @@ -21648,6 +21996,7 @@ }, "node_modules/is-obj": { "version": "2.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -21673,6 +22022,7 @@ }, "node_modules/is-plain-obj": { "version": "1.1.0", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -21680,6 +22030,7 @@ }, "node_modules/is-plain-object": { "version": "5.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -21692,6 +22043,7 @@ }, "node_modules/is-reference": { "version": "3.0.2", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "*" @@ -21752,6 +22104,7 @@ }, "node_modules/is-ssh": { "version": "1.4.0", + "dev": true, "license": "MIT", "dependencies": { "protocols": "^2.0.1" @@ -21759,6 +22112,7 @@ }, "node_modules/is-stream": { "version": "2.0.1", + "devOptional": true, "license": "MIT", "engines": { "node": ">=8" @@ -21800,6 +22154,7 @@ }, "node_modules/is-text-path": { "version": "1.0.1", + "dev": true, "license": "MIT", "dependencies": { "text-extensions": "^1.0.0" @@ -21840,6 +22195,7 @@ }, "node_modules/is-unicode-supported": { "version": "0.1.0", + "devOptional": true, "license": "MIT", "engines": { "node": ">=10" @@ -21890,7 +22246,7 @@ }, "node_modules/is-what": { "version": "3.14.1", - "devOptional": true, + "dev": true, "license": "MIT" }, "node_modules/is-windows": { @@ -21913,6 +22269,7 @@ }, "node_modules/isarray": { "version": "1.0.0", + "dev": true, "license": "MIT" }, "node_modules/isbinaryfile": { @@ -21932,6 +22289,7 @@ }, "node_modules/isobject": { "version": "3.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -21961,6 +22319,7 @@ }, "node_modules/istanbul-lib-instrument": { "version": "6.0.3", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "@babel/core": "^7.23.9", @@ -21975,6 +22334,7 @@ }, "node_modules/istanbul-lib-report": { "version": "3.0.1", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", @@ -21987,6 +22347,7 @@ }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "debug": "^4.1.1", @@ -21999,6 +22360,7 @@ }, "node_modules/istanbul-reports": { "version": "3.1.7", + "dev": true, "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", @@ -22026,6 +22388,7 @@ }, "node_modules/jackspeak": { "version": "3.4.3", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" @@ -22039,6 +22402,7 @@ }, "node_modules/jake": { "version": "10.9.2", + "dev": true, "license": "Apache-2.0", "dependencies": { "async": "^3.2.3", @@ -22060,6 +22424,7 @@ }, "node_modules/jest": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@jest/core": "^29.7.0", @@ -22084,6 +22449,7 @@ }, "node_modules/jest-changed-files": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "execa": "^5.0.0", @@ -22096,6 +22462,7 @@ }, "node_modules/jest-circus": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", @@ -22125,6 +22492,7 @@ }, "node_modules/jest-cli": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@jest/core": "^29.7.0", @@ -22156,6 +22524,7 @@ }, "node_modules/jest-config": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", @@ -22199,6 +22568,7 @@ }, "node_modules/jest-diff": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.0.0", @@ -22212,6 +22582,7 @@ }, "node_modules/jest-docblock": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "detect-newline": "^3.0.0" @@ -22222,6 +22593,7 @@ }, "node_modules/jest-each": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", @@ -22307,6 +22679,7 @@ }, "node_modules/jest-leak-detector": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "jest-get-type": "^29.6.3", @@ -22318,6 +22691,7 @@ }, "node_modules/jest-matcher-utils": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.0.0", @@ -22361,6 +22735,7 @@ }, "node_modules/jest-pnp-resolver": { "version": "1.2.3", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -22383,6 +22758,7 @@ }, "node_modules/jest-resolve": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "chalk": "^4.0.0", @@ -22401,6 +22777,7 @@ }, "node_modules/jest-resolve-dependencies": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "jest-regex-util": "^29.6.3", @@ -22412,6 +22789,7 @@ }, "node_modules/jest-runner": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@jest/console": "^29.7.0", @@ -22442,6 +22820,7 @@ }, "node_modules/jest-runtime": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", @@ -22473,6 +22852,7 @@ }, "node_modules/jest-snapshot": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", @@ -22542,6 +22922,7 @@ }, "node_modules/jest-watcher": { "version": "29.7.0", + "dev": true, "license": "MIT", "dependencies": { "@jest/test-result": "^29.7.0", @@ -22623,6 +23004,7 @@ }, "node_modules/jsbn": { "version": "1.1.0", + "dev": true, "license": "MIT" }, "node_modules/jsc-safe-url": { @@ -22757,14 +23139,17 @@ }, "node_modules/json-parse-better-errors": { "version": "1.0.2", + "dev": true, "license": "MIT" }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", + "devOptional": true, "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "1.0.0", + "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { @@ -22773,6 +23158,7 @@ }, "node_modules/json-stringify-nice": { "version": "1.1.4", + "dev": true, "license": "ISC", "funding": { "url": "https://github.com/sponsors/isaacs" @@ -22780,6 +23166,7 @@ }, "node_modules/json-stringify-safe": { "version": "5.0.1", + "dev": true, "license": "ISC" }, "node_modules/json-with-bigint": { @@ -22803,6 +23190,7 @@ }, "node_modules/jsonfile": { "version": "6.1.0", + "dev": true, "license": "MIT", "dependencies": { "universalify": "^2.0.0" @@ -22813,6 +23201,7 @@ }, "node_modules/jsonparse": { "version": "1.3.1", + "dev": true, "engines": [ "node >= 0.2.0" ], @@ -22820,6 +23209,7 @@ }, "node_modules/JSONStream": { "version": "1.3.5", + "dev": true, "license": "(MIT OR Apache-2.0)", "dependencies": { "jsonparse": "^1.2.0", @@ -22848,10 +23238,12 @@ }, "node_modules/just-diff": { "version": "6.0.2", + "dev": true, "license": "MIT" }, "node_modules/just-diff-apply": { "version": "5.5.0", + "dev": true, "license": "MIT" }, "node_modules/karma": { @@ -23036,6 +23428,7 @@ }, "node_modules/kind-of": { "version": "6.0.3", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -23096,6 +23489,7 @@ }, "node_modules/lerna": { "version": "8.1.8", + "dev": true, "license": "MIT", "dependencies": { "@lerna/create": "8.1.8", @@ -23189,6 +23583,7 @@ }, "node_modules/lerna/node_modules/chalk": { "version": "4.1.0", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", @@ -23203,6 +23598,7 @@ }, "node_modules/lerna/node_modules/conventional-changelog-angular": { "version": "7.0.0", + "dev": true, "license": "ISC", "dependencies": { "compare-func": "^2.0.0" @@ -23213,6 +23609,7 @@ }, "node_modules/lerna/node_modules/execa": { "version": "5.0.0", + "dev": true, "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", @@ -23234,6 +23631,7 @@ }, "node_modules/lerna/node_modules/get-stream": { "version": "6.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -23244,6 +23642,7 @@ }, "node_modules/lerna/node_modules/is-stream": { "version": "2.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -23251,6 +23650,7 @@ }, "node_modules/lerna/node_modules/minimatch": { "version": "3.0.5", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" @@ -23261,6 +23661,7 @@ }, "node_modules/lerna/node_modules/yargs-parser": { "version": "21.1.1", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -23268,7 +23669,7 @@ }, "node_modules/less": { "version": "4.1.3", - "devOptional": true, + "dev": true, "license": "Apache-2.0", "dependencies": { "copy-anything": "^2.0.1", @@ -23312,6 +23713,7 @@ }, "node_modules/less/node_modules/make-dir": { "version": "2.1.0", + "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -23325,6 +23727,7 @@ }, "node_modules/less/node_modules/mime": { "version": "1.6.0", + "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -23337,6 +23740,7 @@ }, "node_modules/less/node_modules/pify": { "version": "4.0.1", + "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -23346,6 +23750,7 @@ }, "node_modules/less/node_modules/semver": { "version": "5.7.2", + "dev": true, "license": "ISC", "optional": true, "peer": true, @@ -23373,6 +23778,7 @@ }, "node_modules/libnpmaccess": { "version": "8.0.6", + "dev": true, "license": "ISC", "dependencies": { "npm-package-arg": "^11.0.2", @@ -23384,6 +23790,7 @@ }, "node_modules/libnpmpublish": { "version": "9.0.9", + "dev": true, "license": "ISC", "dependencies": { "ci-info": "^4.0.0", @@ -23401,6 +23808,7 @@ }, "node_modules/libnpmpublish/node_modules/ci-info": { "version": "4.0.0", + "dev": true, "funding": [ { "type": "github", @@ -23414,6 +23822,7 @@ }, "node_modules/libnpmpublish/node_modules/hosted-git-info": { "version": "7.0.2", + "dev": true, "license": "ISC", "dependencies": { "lru-cache": "^10.0.1" @@ -23424,10 +23833,12 @@ }, "node_modules/libnpmpublish/node_modules/lru-cache": { "version": "10.4.3", + "dev": true, "license": "ISC" }, "node_modules/libnpmpublish/node_modules/normalize-package-data": { "version": "6.0.2", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^7.0.0", @@ -23520,7 +23931,7 @@ }, "node_modules/lilconfig": { "version": "2.1.0", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -23528,6 +23939,7 @@ }, "node_modules/lines-and-columns": { "version": "2.0.3", + "dev": true, "license": "MIT", "engines": { "node": "^12.20.0 || ^14.13.1 || >=16.0.0" @@ -23535,6 +23947,7 @@ }, "node_modules/load-json-file": { "version": "6.2.0", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.1.15", @@ -23548,6 +23961,7 @@ }, "node_modules/load-json-file/node_modules/type-fest": { "version": "0.6.0", + "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=8" @@ -23571,6 +23985,7 @@ }, "node_modules/locate-character": { "version": "3.0.0", + "dev": true, "license": "MIT" }, "node_modules/locate-path": { @@ -23588,10 +24003,12 @@ }, "node_modules/lodash": { "version": "4.17.21", + "dev": true, "license": "MIT" }, "node_modules/lodash.camelcase": { "version": "4.3.0", + "dev": true, "license": "MIT" }, "node_modules/lodash.capitalize": { @@ -23610,14 +24027,17 @@ }, "node_modules/lodash.isfunction": { "version": "3.0.9", + "dev": true, "license": "MIT" }, "node_modules/lodash.ismatch": { "version": "4.4.0", + "dev": true, "license": "MIT" }, "node_modules/lodash.isplainobject": { "version": "4.0.6", + "dev": true, "license": "MIT" }, "node_modules/lodash.isstring": { @@ -23627,6 +24047,7 @@ }, "node_modules/lodash.kebabcase": { "version": "4.1.1", + "dev": true, "license": "MIT" }, "node_modules/lodash.memoize": { @@ -23640,14 +24061,17 @@ }, "node_modules/lodash.mergewith": { "version": "4.6.2", + "dev": true, "license": "MIT" }, "node_modules/lodash.snakecase": { "version": "4.1.1", + "dev": true, "license": "MIT" }, "node_modules/lodash.startcase": { "version": "4.4.0", + "dev": true, "license": "MIT" }, "node_modules/lodash.throttle": { @@ -23656,6 +24080,7 @@ }, "node_modules/lodash.uniq": { "version": "4.5.0", + "dev": true, "license": "MIT" }, "node_modules/lodash.uniqby": { @@ -23665,10 +24090,12 @@ }, "node_modules/lodash.upperfirst": { "version": "4.3.1", + "dev": true, "license": "MIT" }, "node_modules/log-symbols": { "version": "4.1.0", + "devOptional": true, "license": "MIT", "dependencies": { "chalk": "^4.1.0", @@ -23864,6 +24291,7 @@ }, "node_modules/make-dir": { "version": "4.0.0", + "dev": true, "license": "MIT", "dependencies": { "semver": "^7.5.3" @@ -23877,10 +24305,12 @@ }, "node_modules/make-error": { "version": "1.3.6", + "dev": true, "license": "ISC" }, "node_modules/make-fetch-happen": { "version": "13.0.1", + "dev": true, "license": "ISC", "dependencies": { "@npmcli/agent": "^2.0.0", @@ -23902,6 +24332,7 @@ }, "node_modules/make-fetch-happen/node_modules/minipass": { "version": "7.1.2", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -23916,6 +24347,7 @@ }, "node_modules/map-obj": { "version": "4.3.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -23949,6 +24381,7 @@ }, "node_modules/mdn-data": { "version": "2.0.30", + "dev": true, "license": "CC0-1.0" }, "node_modules/media-typer": { @@ -23976,6 +24409,7 @@ }, "node_modules/meow": { "version": "8.1.2", + "dev": true, "license": "MIT", "dependencies": { "@types/minimist": "^1.2.0", @@ -23999,6 +24433,7 @@ }, "node_modules/meow/node_modules/find-up": { "version": "4.1.0", + "dev": true, "license": "MIT", "dependencies": { "locate-path": "^5.0.0", @@ -24010,10 +24445,12 @@ }, "node_modules/meow/node_modules/hosted-git-info": { "version": "2.8.9", + "dev": true, "license": "ISC" }, "node_modules/meow/node_modules/locate-path": { "version": "5.0.0", + "dev": true, "license": "MIT", "dependencies": { "p-locate": "^4.1.0" @@ -24024,6 +24461,7 @@ }, "node_modules/meow/node_modules/p-limit": { "version": "2.3.0", + "dev": true, "license": "MIT", "dependencies": { "p-try": "^2.0.0" @@ -24037,6 +24475,7 @@ }, "node_modules/meow/node_modules/p-locate": { "version": "4.1.0", + "dev": true, "license": "MIT", "dependencies": { "p-limit": "^2.2.0" @@ -24047,6 +24486,7 @@ }, "node_modules/meow/node_modules/read-pkg": { "version": "5.2.0", + "dev": true, "license": "MIT", "dependencies": { "@types/normalize-package-data": "^2.4.0", @@ -24060,6 +24500,7 @@ }, "node_modules/meow/node_modules/read-pkg-up": { "version": "7.0.1", + "dev": true, "license": "MIT", "dependencies": { "find-up": "^4.1.0", @@ -24075,6 +24516,7 @@ }, "node_modules/meow/node_modules/read-pkg-up/node_modules/type-fest": { "version": "0.8.1", + "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=8" @@ -24082,6 +24524,7 @@ }, "node_modules/meow/node_modules/read-pkg/node_modules/normalize-package-data": { "version": "2.5.0", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^2.1.4", @@ -24092,6 +24535,7 @@ }, "node_modules/meow/node_modules/read-pkg/node_modules/type-fest": { "version": "0.6.0", + "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=8" @@ -24099,6 +24543,7 @@ }, "node_modules/meow/node_modules/semver": { "version": "5.7.2", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver" @@ -24106,6 +24551,7 @@ }, "node_modules/meow/node_modules/type-fest": { "version": "0.18.1", + "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" @@ -24707,6 +25153,7 @@ }, "node_modules/mimic-fn": { "version": "2.1.0", + "devOptional": true, "license": "MIT", "engines": { "node": ">=6" @@ -24725,6 +25172,7 @@ }, "node_modules/min-indent": { "version": "1.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -24772,6 +25220,7 @@ }, "node_modules/minimist-options": { "version": "4.1.0", + "dev": true, "license": "MIT", "dependencies": { "arrify": "^1.0.1", @@ -24784,6 +25233,7 @@ }, "node_modules/minipass": { "version": "5.0.0", + "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -24791,6 +25241,7 @@ }, "node_modules/minipass-collect": { "version": "2.0.1", + "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.3" @@ -24801,6 +25252,7 @@ }, "node_modules/minipass-collect/node_modules/minipass": { "version": "7.1.2", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -24808,6 +25260,7 @@ }, "node_modules/minipass-fetch": { "version": "3.0.5", + "dev": true, "license": "MIT", "dependencies": { "minipass": "^7.0.3", @@ -24823,6 +25276,7 @@ }, "node_modules/minipass-fetch/node_modules/minipass": { "version": "7.1.2", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -24830,6 +25284,7 @@ }, "node_modules/minipass-flush": { "version": "1.0.5", + "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -24840,6 +25295,7 @@ }, "node_modules/minipass-flush/node_modules/minipass": { "version": "3.3.6", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -24850,6 +25306,7 @@ }, "node_modules/minipass-flush/node_modules/yallist": { "version": "4.0.0", + "dev": true, "license": "ISC" }, "node_modules/minipass-json-stream": { @@ -24879,6 +25336,7 @@ }, "node_modules/minipass-pipeline": { "version": "1.2.4", + "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -24889,6 +25347,7 @@ }, "node_modules/minipass-pipeline/node_modules/minipass": { "version": "3.3.6", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -24899,10 +25358,12 @@ }, "node_modules/minipass-pipeline/node_modules/yallist": { "version": "4.0.0", + "dev": true, "license": "ISC" }, "node_modules/minipass-sized": { "version": "1.0.3", + "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -24913,6 +25374,7 @@ }, "node_modules/minipass-sized/node_modules/minipass": { "version": "3.3.6", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -24923,10 +25385,12 @@ }, "node_modules/minipass-sized/node_modules/yallist": { "version": "4.0.0", + "dev": true, "license": "ISC" }, "node_modules/minizlib": { "version": "2.1.2", + "dev": true, "license": "MIT", "dependencies": { "minipass": "^3.0.0", @@ -24938,6 +25402,7 @@ }, "node_modules/minizlib/node_modules/minipass": { "version": "3.3.6", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -24948,10 +25413,12 @@ }, "node_modules/minizlib/node_modules/yallist": { "version": "4.0.0", + "dev": true, "license": "ISC" }, "node_modules/mkdirp": { "version": "0.5.6", + "dev": true, "license": "MIT", "dependencies": { "minimist": "^1.2.6" @@ -24962,6 +25429,7 @@ }, "node_modules/modify-values": { "version": "1.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -24969,6 +25437,7 @@ }, "node_modules/mri": { "version": "1.2.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -25007,6 +25476,7 @@ }, "node_modules/multimatch": { "version": "5.0.0", + "dev": true, "license": "MIT", "dependencies": { "@types/minimatch": "^3.0.3", @@ -25024,6 +25494,7 @@ }, "node_modules/multimatch/node_modules/arrify": { "version": "2.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -25031,6 +25502,7 @@ }, "node_modules/mute-stream": { "version": "0.0.8", + "dev": true, "license": "ISC" }, "node_modules/mz": { @@ -25078,6 +25550,7 @@ }, "node_modules/needle": { "version": "3.2.0", + "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -25095,6 +25568,7 @@ }, "node_modules/needle/node_modules/debug": { "version": "3.2.7", + "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -25104,6 +25578,7 @@ }, "node_modules/needle/node_modules/iconv-lite": { "version": "0.6.3", + "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -25123,6 +25598,7 @@ }, "node_modules/neo-async": { "version": "2.6.2", + "dev": true, "license": "MIT" }, "node_modules/nested-error-stacks": { @@ -25217,6 +25693,7 @@ }, "node_modules/node-fetch": { "version": "2.6.7", + "dev": true, "license": "MIT", "dependencies": { "whatwg-url": "^5.0.0" @@ -25247,6 +25724,7 @@ }, "node_modules/node-gyp": { "version": "10.2.0", + "dev": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.0", @@ -25280,6 +25758,7 @@ }, "node_modules/node-gyp/node_modules/brace-expansion": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -25287,6 +25766,7 @@ }, "node_modules/node-gyp/node_modules/glob": { "version": "10.4.5", + "dev": true, "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", @@ -25305,6 +25785,7 @@ }, "node_modules/node-gyp/node_modules/isexe": { "version": "3.1.1", + "dev": true, "license": "ISC", "engines": { "node": ">=16" @@ -25312,6 +25793,7 @@ }, "node_modules/node-gyp/node_modules/minimatch": { "version": "9.0.5", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -25325,6 +25807,7 @@ }, "node_modules/node-gyp/node_modules/minipass": { "version": "7.1.2", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -25332,6 +25815,7 @@ }, "node_modules/node-gyp/node_modules/which": { "version": "4.0.0", + "dev": true, "license": "ISC", "dependencies": { "isexe": "^3.1.1" @@ -25349,6 +25833,7 @@ }, "node_modules/node-machine-id": { "version": "1.1.12", + "dev": true, "license": "MIT" }, "node_modules/node-releases": { @@ -25369,6 +25854,7 @@ }, "node_modules/nopt": { "version": "7.2.1", + "dev": true, "license": "ISC", "dependencies": { "abbrev": "^2.0.0" @@ -25382,6 +25868,7 @@ }, "node_modules/normalize-package-data": { "version": "3.0.3", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^4.0.1", @@ -25410,6 +25897,7 @@ }, "node_modules/npm-bundled": { "version": "3.0.1", + "dev": true, "license": "ISC", "dependencies": { "npm-normalize-package-bin": "^3.0.0" @@ -25420,6 +25908,7 @@ }, "node_modules/npm-install-checks": { "version": "6.3.0", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "semver": "^7.1.1" @@ -25430,6 +25919,7 @@ }, "node_modules/npm-normalize-package-bin": { "version": "3.0.1", + "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -25464,6 +25954,7 @@ }, "node_modules/npm-packlist": { "version": "8.0.2", + "dev": true, "license": "ISC", "dependencies": { "ignore-walk": "^6.0.4" @@ -25474,6 +25965,7 @@ }, "node_modules/npm-pick-manifest": { "version": "9.1.0", + "dev": true, "license": "ISC", "dependencies": { "npm-install-checks": "^6.0.0", @@ -25487,6 +25979,7 @@ }, "node_modules/npm-registry-fetch": { "version": "17.1.0", + "dev": true, "license": "ISC", "dependencies": { "@npmcli/redact": "^2.0.0", @@ -25504,6 +25997,7 @@ }, "node_modules/npm-registry-fetch/node_modules/minipass": { "version": "7.1.2", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -25511,6 +26005,7 @@ }, "node_modules/npm-run-path": { "version": "4.0.1", + "devOptional": true, "license": "MIT", "dependencies": { "path-key": "^3.0.0" @@ -25555,6 +26050,7 @@ }, "node_modules/nx": { "version": "19.8.2", + "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -25624,6 +26120,7 @@ }, "node_modules/nx/node_modules/brace-expansion": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -25631,6 +26128,7 @@ }, "node_modules/nx/node_modules/minimatch": { "version": "9.0.3", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -25644,6 +26142,7 @@ }, "node_modules/nx/node_modules/ora": { "version": "5.3.0", + "dev": true, "license": "MIT", "dependencies": { "bl": "^4.0.3", @@ -25664,6 +26163,7 @@ }, "node_modules/nx/node_modules/yargs-parser": { "version": "21.1.1", + "dev": true, "license": "ISC", "engines": { "node": ">=12" @@ -25865,6 +26365,7 @@ }, "node_modules/onetime": { "version": "5.1.2", + "devOptional": true, "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" @@ -25908,6 +26409,7 @@ }, "node_modules/ora": { "version": "5.4.1", + "devOptional": true, "license": "MIT", "dependencies": { "bl": "^4.1.0", @@ -25944,6 +26446,7 @@ }, "node_modules/os-tmpdir": { "version": "1.0.2", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" @@ -25967,6 +26470,7 @@ }, "node_modules/p-finally": { "version": "1.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -26000,6 +26504,7 @@ }, "node_modules/p-map": { "version": "4.0.0", + "dev": true, "license": "MIT", "dependencies": { "aggregate-error": "^3.0.0" @@ -26013,6 +26518,7 @@ }, "node_modules/p-map-series": { "version": "2.1.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -26020,6 +26526,7 @@ }, "node_modules/p-pipe": { "version": "3.1.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -26030,6 +26537,7 @@ }, "node_modules/p-queue": { "version": "6.6.2", + "dev": true, "license": "MIT", "dependencies": { "eventemitter3": "^4.0.4", @@ -26044,6 +26552,7 @@ }, "node_modules/p-reduce": { "version": "2.1.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -26071,6 +26580,7 @@ }, "node_modules/p-timeout": { "version": "3.2.0", + "dev": true, "license": "MIT", "dependencies": { "p-finally": "^1.0.0" @@ -26088,6 +26598,7 @@ }, "node_modules/p-waterfall": { "version": "2.1.1", + "dev": true, "license": "MIT", "dependencies": { "p-reduce": "^2.0.0" @@ -26131,10 +26642,12 @@ }, "node_modules/package-json-from-dist": { "version": "1.0.1", + "dev": true, "license": "BlueOak-1.0.0" }, "node_modules/pacote": { "version": "18.0.6", + "dev": true, "license": "ISC", "dependencies": { "@npmcli/git": "^5.0.0", @@ -26164,6 +26677,7 @@ }, "node_modules/pacote/node_modules/minipass": { "version": "7.1.2", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -26186,6 +26700,7 @@ }, "node_modules/parse-conflict-json": { "version": "3.0.1", + "dev": true, "license": "ISC", "dependencies": { "json-parse-even-better-errors": "^3.0.0", @@ -26198,6 +26713,7 @@ }, "node_modules/parse-conflict-json/node_modules/json-parse-even-better-errors": { "version": "3.0.2", + "dev": true, "license": "MIT", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -26205,6 +26721,7 @@ }, "node_modules/parse-json": { "version": "5.2.0", + "devOptional": true, "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", @@ -26221,11 +26738,12 @@ }, "node_modules/parse-json/node_modules/lines-and-columns": { "version": "1.2.4", + "devOptional": true, "license": "MIT" }, "node_modules/parse-node-version": { "version": "1.0.1", - "devOptional": true, + "dev": true, "license": "MIT", "engines": { "node": ">= 0.10" @@ -26233,6 +26751,7 @@ }, "node_modules/parse-path": { "version": "7.0.0", + "dev": true, "license": "MIT", "dependencies": { "protocols": "^2.0.0" @@ -26250,6 +26769,7 @@ }, "node_modules/parse-url": { "version": "8.1.0", + "dev": true, "license": "MIT", "dependencies": { "parse-path": "^7.0.0" @@ -26331,6 +26851,7 @@ }, "node_modules/path-scurry": { "version": "1.11.1", + "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "lru-cache": "^10.2.0", @@ -26345,6 +26866,7 @@ }, "node_modules/path-scurry/node_modules/lru-cache": { "version": "10.4.3", + "dev": true, "license": "ISC" }, "node_modules/path-to-regexp": { @@ -26371,6 +26893,7 @@ }, "node_modules/periscopic": { "version": "3.1.0", + "dev": true, "license": "MIT", "dependencies": { "@types/estree": "^1.0.0", @@ -26394,6 +26917,7 @@ }, "node_modules/pify": { "version": "5.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=10" @@ -26424,6 +26948,7 @@ }, "node_modules/pkg-dir": { "version": "4.2.0", + "dev": true, "license": "MIT", "dependencies": { "find-up": "^4.0.0" @@ -26434,6 +26959,7 @@ }, "node_modules/pkg-dir/node_modules/find-up": { "version": "4.1.0", + "dev": true, "license": "MIT", "dependencies": { "locate-path": "^5.0.0", @@ -26445,6 +26971,7 @@ }, "node_modules/pkg-dir/node_modules/locate-path": { "version": "5.0.0", + "dev": true, "license": "MIT", "dependencies": { "p-locate": "^4.1.0" @@ -26455,6 +26982,7 @@ }, "node_modules/pkg-dir/node_modules/p-limit": { "version": "2.3.0", + "dev": true, "license": "MIT", "dependencies": { "p-try": "^2.0.0" @@ -26468,6 +26996,7 @@ }, "node_modules/pkg-dir/node_modules/p-locate": { "version": "4.1.0", + "dev": true, "license": "MIT", "dependencies": { "p-limit": "^2.2.0" @@ -26522,7 +27051,7 @@ }, "node_modules/postcss": { "version": "8.4.31", - "devOptional": true, + "dev": true, "funding": [ { "type": "opencollective", @@ -26549,7 +27078,7 @@ }, "node_modules/postcss-load-config": { "version": "3.1.4", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "lilconfig": "^2.0.5", @@ -26693,6 +27222,7 @@ }, "node_modules/postcss-selector-parser": { "version": "6.1.2", + "dev": true, "license": "MIT", "dependencies": { "cssesc": "^3.0.0", @@ -26704,6 +27234,7 @@ }, "node_modules/postcss-value-parser": { "version": "4.2.0", + "dev": true, "license": "MIT" }, "node_modules/prelude-ls": { @@ -26789,10 +27320,12 @@ }, "node_modules/process-nextick-args": { "version": "2.0.1", + "dev": true, "license": "MIT" }, "node_modules/proggy": { "version": "2.0.0", + "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -26814,6 +27347,7 @@ }, "node_modules/promise-all-reject-late": { "version": "1.0.1", + "dev": true, "license": "ISC", "funding": { "url": "https://github.com/sponsors/isaacs" @@ -26821,6 +27355,7 @@ }, "node_modules/promise-call-limit": { "version": "3.0.2", + "dev": true, "license": "ISC", "funding": { "url": "https://github.com/sponsors/isaacs" @@ -26828,10 +27363,12 @@ }, "node_modules/promise-inflight": { "version": "1.0.1", + "dev": true, "license": "ISC" }, "node_modules/promise-retry": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "err-code": "^2.0.2", @@ -26861,6 +27398,7 @@ }, "node_modules/promzard": { "version": "1.0.2", + "dev": true, "license": "ISC", "dependencies": { "read": "^3.0.1" @@ -26886,6 +27424,7 @@ }, "node_modules/protocols": { "version": "2.0.1", + "dev": true, "license": "MIT" }, "node_modules/proxy-addr": { @@ -26936,10 +27475,12 @@ }, "node_modules/proxy-from-env": { "version": "1.1.0", + "dev": true, "license": "MIT" }, "node_modules/prr": { "version": "1.0.1", + "dev": true, "license": "MIT", "optional": true, "peer": true @@ -26967,6 +27508,7 @@ }, "node_modules/pure-rand": { "version": "6.1.0", + "dev": true, "funding": [ { "type": "individual", @@ -27055,6 +27597,7 @@ }, "node_modules/quick-lru": { "version": "4.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -27062,7 +27605,6 @@ }, "node_modules/randombytes": { "version": "2.1.0", - "dev": true, "license": "MIT", "dependencies": { "safe-buffer": "^5.1.0" @@ -27584,32 +28126,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/react-native-web": { - "version": "0.21.2", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.6", - "@react-native/normalize-colors": "^0.74.1", - "fbjs": "^3.0.4", - "inline-style-prefixer": "^7.0.1", - "memoize-one": "^6.0.0", - "nullthrows": "^1.1.1", - "postcss-value-parser": "^4.2.0", - "styleq": "^0.1.3" - }, - "peerDependencies": { - "react": "^18.0.0 || ^19.0.0", - "react-dom": "^18.0.0 || ^19.0.0" - } - }, - "node_modules/react-native-web/node_modules/@react-native/normalize-colors": { - "version": "0.74.89", - "license": "MIT" - }, - "node_modules/react-native-web/node_modules/memoize-one": { - "version": "6.0.0", - "license": "MIT" - }, "node_modules/react-refresh": { "version": "0.14.2", "license": "MIT", @@ -27682,6 +28198,7 @@ }, "node_modules/read": { "version": "3.0.1", + "dev": true, "license": "ISC", "dependencies": { "mute-stream": "^1.0.0" @@ -27692,6 +28209,7 @@ }, "node_modules/read-cmd-shim": { "version": "4.0.0", + "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -27713,6 +28231,7 @@ }, "node_modules/read-package-json-fast": { "version": "3.0.2", + "dev": true, "license": "ISC", "dependencies": { "json-parse-even-better-errors": "^3.0.0", @@ -27724,6 +28243,7 @@ }, "node_modules/read-package-json-fast/node_modules/json-parse-even-better-errors": { "version": "3.0.2", + "dev": true, "license": "MIT", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -27821,6 +28341,7 @@ }, "node_modules/read-pkg": { "version": "3.0.0", + "dev": true, "license": "MIT", "dependencies": { "load-json-file": "^4.0.0", @@ -27833,6 +28354,7 @@ }, "node_modules/read-pkg-up": { "version": "3.0.0", + "dev": true, "license": "MIT", "dependencies": { "find-up": "^2.0.0", @@ -27844,6 +28366,7 @@ }, "node_modules/read-pkg-up/node_modules/find-up": { "version": "2.1.0", + "dev": true, "license": "MIT", "dependencies": { "locate-path": "^2.0.0" @@ -27854,6 +28377,7 @@ }, "node_modules/read-pkg-up/node_modules/locate-path": { "version": "2.0.0", + "dev": true, "license": "MIT", "dependencies": { "p-locate": "^2.0.0", @@ -27865,6 +28389,7 @@ }, "node_modules/read-pkg-up/node_modules/p-limit": { "version": "1.3.0", + "dev": true, "license": "MIT", "dependencies": { "p-try": "^1.0.0" @@ -27875,6 +28400,7 @@ }, "node_modules/read-pkg-up/node_modules/p-locate": { "version": "2.0.0", + "dev": true, "license": "MIT", "dependencies": { "p-limit": "^1.1.0" @@ -27885,6 +28411,7 @@ }, "node_modules/read-pkg-up/node_modules/p-try": { "version": "1.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -27892,6 +28419,7 @@ }, "node_modules/read-pkg-up/node_modules/path-exists": { "version": "3.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -27899,10 +28427,12 @@ }, "node_modules/read-pkg/node_modules/hosted-git-info": { "version": "2.8.9", + "dev": true, "license": "ISC" }, "node_modules/read-pkg/node_modules/load-json-file": { "version": "4.0.0", + "dev": true, "license": "MIT", "dependencies": { "graceful-fs": "^4.1.2", @@ -27916,6 +28446,7 @@ }, "node_modules/read-pkg/node_modules/normalize-package-data": { "version": "2.5.0", + "dev": true, "license": "BSD-2-Clause", "dependencies": { "hosted-git-info": "^2.1.4", @@ -27926,6 +28457,7 @@ }, "node_modules/read-pkg/node_modules/parse-json": { "version": "4.0.0", + "dev": true, "license": "MIT", "dependencies": { "error-ex": "^1.3.1", @@ -27937,6 +28469,7 @@ }, "node_modules/read-pkg/node_modules/path-type": { "version": "3.0.0", + "dev": true, "license": "MIT", "dependencies": { "pify": "^3.0.0" @@ -27947,6 +28480,7 @@ }, "node_modules/read-pkg/node_modules/pify": { "version": "3.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -27954,6 +28488,7 @@ }, "node_modules/read-pkg/node_modules/semver": { "version": "5.7.2", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver" @@ -27961,6 +28496,7 @@ }, "node_modules/read-pkg/node_modules/strip-bom": { "version": "3.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -27968,6 +28504,7 @@ }, "node_modules/read/node_modules/mute-stream": { "version": "1.0.0", + "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -27975,6 +28512,7 @@ }, "node_modules/readable-stream": { "version": "3.6.2", + "devOptional": true, "license": "MIT", "dependencies": { "inherits": "^2.0.3", @@ -27987,6 +28525,7 @@ }, "node_modules/readdirp": { "version": "3.6.0", + "dev": true, "license": "MIT", "dependencies": { "picomatch": "^2.2.1" @@ -27997,6 +28536,7 @@ }, "node_modules/redent": { "version": "3.0.0", + "dev": true, "license": "MIT", "dependencies": { "indent-string": "^4.0.0", @@ -28975,6 +29515,7 @@ }, "node_modules/resolve-cwd": { "version": "3.0.0", + "dev": true, "license": "MIT", "dependencies": { "resolve-from": "^5.0.0" @@ -28992,6 +29533,7 @@ }, "node_modules/resolve-global": { "version": "1.0.0", + "dev": true, "license": "MIT", "dependencies": { "global-dirs": "^0.1.1" @@ -29054,6 +29596,7 @@ }, "node_modules/restore-cursor": { "version": "3.1.0", + "devOptional": true, "license": "MIT", "dependencies": { "onetime": "^5.1.0", @@ -29065,6 +29608,7 @@ }, "node_modules/retry": { "version": "0.12.0", + "dev": true, "license": "MIT", "engines": { "node": ">= 4" @@ -29085,6 +29629,7 @@ }, "node_modules/rimraf": { "version": "4.4.1", + "dev": true, "license": "ISC", "dependencies": { "glob": "^9.2.0" @@ -29101,6 +29646,7 @@ }, "node_modules/rimraf/node_modules/brace-expansion": { "version": "2.0.1", + "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" @@ -29108,6 +29654,7 @@ }, "node_modules/rimraf/node_modules/glob": { "version": "9.3.5", + "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", @@ -29124,6 +29671,7 @@ }, "node_modules/rimraf/node_modules/minimatch": { "version": "8.0.4", + "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" @@ -29137,6 +29685,7 @@ }, "node_modules/rimraf/node_modules/minipass": { "version": "4.2.8", + "dev": true, "license": "ISC", "engines": { "node": ">=8" @@ -29273,6 +29822,7 @@ }, "node_modules/run-async": { "version": "2.4.1", + "dev": true, "license": "MIT", "engines": { "node": ">=0.12.0" @@ -29308,6 +29858,7 @@ }, "node_modules/sade": { "version": "1.8.1", + "dev": true, "license": "MIT", "dependencies": { "mri": "^1.1.0" @@ -29395,10 +29946,12 @@ }, "node_modules/safer-buffer": { "version": "2.1.2", + "devOptional": true, "license": "MIT" }, "node_modules/sander": { "version": "0.5.1", + "dev": true, "license": "MIT", "dependencies": { "es6-promise": "^3.1.2", @@ -29409,6 +29962,7 @@ }, "node_modules/sander/node_modules/rimraf": { "version": "2.7.1", + "dev": true, "license": "ISC", "dependencies": { "glob": "^7.1.3" @@ -29419,7 +29973,7 @@ }, "node_modules/sass": { "version": "1.64.1", - "devOptional": true, + "dev": true, "license": "MIT", "dependencies": { "chokidar": ">=3.0.0 <4.0.0", @@ -29616,7 +30170,6 @@ }, "node_modules/serialize-javascript": { "version": "6.0.2", - "dev": true, "license": "BSD-3-Clause", "dependencies": { "randombytes": "^2.1.0" @@ -29710,6 +30263,7 @@ }, "node_modules/set-blocking": { "version": "2.0.0", + "devOptional": true, "license": "ISC" }, "node_modules/set-function-length": { @@ -29755,10 +30309,6 @@ "node": ">= 0.4" } }, - "node_modules/setimmediate": { - "version": "1.0.5", - "license": "MIT" - }, "node_modules/setprototypeof": { "version": "1.2.0", "license": "ISC" @@ -29772,6 +30322,7 @@ }, "node_modules/shallow-clone": { "version": "3.0.1", + "dev": true, "license": "MIT", "dependencies": { "kind-of": "^6.0.2" @@ -29893,6 +30444,7 @@ }, "node_modules/sigstore": { "version": "2.3.1", + "dev": true, "license": "Apache-2.0", "dependencies": { "@sigstore/bundle": "^2.3.2", @@ -29991,12 +30543,22 @@ }, "node_modules/smart-buffer": { "version": "4.2.0", + "dev": true, "license": "MIT", "engines": { "node": ">= 6.0.0", "npm": ">= 3.0.0" } }, + "node_modules/smob": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/smob/-/smob-1.6.2.tgz", + "integrity": "sha512-RQsvleCbF8cVHEv+xuDGaA4pOizFqJ0GgjtMSRo6oP8pnN7WsigHgVGey6aILRBKv4W2YOMHLqbKdnB6hpB9fw==", + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/socket.io": { "version": "4.8.0", "dev": true, @@ -30075,6 +30637,7 @@ }, "node_modules/socks": { "version": "2.8.3", + "dev": true, "license": "MIT", "dependencies": { "ip-address": "^9.0.5", @@ -30087,6 +30650,7 @@ }, "node_modules/socks-proxy-agent": { "version": "8.0.5", + "dev": true, "license": "MIT", "dependencies": { "agent-base": "^7.1.2", @@ -30099,6 +30663,7 @@ }, "node_modules/sorcery": { "version": "0.11.0", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.4.14", @@ -30112,6 +30677,7 @@ }, "node_modules/sort-keys": { "version": "2.0.0", + "dev": true, "license": "MIT", "dependencies": { "is-plain-obj": "^1.0.0" @@ -30167,6 +30733,7 @@ }, "node_modules/source-map-support": { "version": "0.5.13", + "dev": true, "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", @@ -30175,6 +30742,7 @@ }, "node_modules/spdx-correct": { "version": "3.2.0", + "dev": true, "license": "Apache-2.0", "dependencies": { "spdx-expression-parse": "^3.0.0", @@ -30183,10 +30751,12 @@ }, "node_modules/spdx-exceptions": { "version": "2.3.0", + "dev": true, "license": "CC-BY-3.0" }, "node_modules/spdx-expression-parse": { "version": "3.0.1", + "dev": true, "license": "MIT", "dependencies": { "spdx-exceptions": "^2.1.0", @@ -30195,6 +30765,7 @@ }, "node_modules/spdx-license-ids": { "version": "3.0.16", + "dev": true, "license": "CC0-1.0" }, "node_modules/spdy": { @@ -30227,6 +30798,7 @@ }, "node_modules/split": { "version": "1.0.1", + "dev": true, "license": "MIT", "dependencies": { "through": "2" @@ -30244,6 +30816,7 @@ }, "node_modules/split2": { "version": "3.2.2", + "dev": true, "license": "ISC", "dependencies": { "readable-stream": "^3.0.0" @@ -30251,10 +30824,12 @@ }, "node_modules/sprintf-js": { "version": "1.1.3", + "dev": true, "license": "BSD-3-Clause" }, "node_modules/ssri": { "version": "10.0.6", + "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.3" @@ -30265,6 +30840,7 @@ }, "node_modules/ssri/node_modules/minipass": { "version": "7.1.2", + "dev": true, "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" @@ -30401,6 +30977,7 @@ }, "node_modules/string_decoder": { "version": "1.3.0", + "devOptional": true, "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" @@ -30408,6 +30985,7 @@ }, "node_modules/string-length": { "version": "4.0.2", + "dev": true, "license": "MIT", "dependencies": { "char-regex": "^1.0.2", @@ -30437,6 +31015,7 @@ "node_modules/string-width-cjs": { "name": "string-width", "version": "4.2.3", + "dev": true, "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", @@ -30557,6 +31136,7 @@ "node_modules/strip-ansi-cjs": { "name": "strip-ansi", "version": "6.0.1", + "dev": true, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" @@ -30567,6 +31147,7 @@ }, "node_modules/strip-bom": { "version": "4.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -30574,6 +31155,7 @@ }, "node_modules/strip-final-newline": { "version": "2.0.0", + "devOptional": true, "license": "MIT", "engines": { "node": ">=6" @@ -30581,6 +31163,7 @@ }, "node_modules/strip-indent": { "version": "3.0.0", + "dev": true, "license": "MIT", "dependencies": { "min-indent": "^1.0.0" @@ -30612,6 +31195,7 @@ }, "node_modules/strong-log-transformer": { "version": "2.1.0", + "dev": true, "license": "Apache-2.0", "dependencies": { "duplexer": "^0.1.1", @@ -30629,10 +31213,6 @@ "version": "0.4.1", "license": "MIT" }, - "node_modules/styleq": { - "version": "0.1.3", - "license": "MIT" - }, "node_modules/sucrase": { "version": "3.35.1", "license": "MIT", @@ -30697,6 +31277,7 @@ }, "node_modules/svelte": { "version": "4.2.19", + "dev": true, "license": "MIT", "dependencies": { "@ampproject/remapping": "^2.2.1", @@ -30720,6 +31301,7 @@ }, "node_modules/svelte-check": { "version": "4.0.0", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.25", @@ -30741,6 +31323,7 @@ }, "node_modules/svelte-check/node_modules/fdir": { "version": "6.3.0", + "dev": true, "license": "MIT", "peerDependencies": { "picomatch": "^3 || ^4" @@ -30752,9 +31335,10 @@ } }, "node_modules/svelte-check/node_modules/picomatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", - "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.5.tgz", + "integrity": "sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==", + "dev": true, "license": "MIT", "optional": true, "peer": true, @@ -30831,6 +31415,7 @@ }, "node_modules/svelte-preprocess": { "version": "5.1.4", + "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -30891,6 +31476,7 @@ }, "node_modules/svelte-preprocess/node_modules/magic-string": { "version": "0.30.11", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -30898,6 +31484,7 @@ }, "node_modules/svelte/node_modules/magic-string": { "version": "0.30.11", + "dev": true, "license": "MIT", "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0" @@ -30942,6 +31529,7 @@ }, "node_modules/tar": { "version": "6.2.1", + "dev": true, "license": "ISC", "dependencies": { "chownr": "^2.0.0", @@ -30957,6 +31545,7 @@ }, "node_modules/tar-stream": { "version": "2.2.0", + "dev": true, "license": "MIT", "dependencies": { "bl": "^4.0.3", @@ -30971,6 +31560,7 @@ }, "node_modules/tar/node_modules/fs-minipass": { "version": "2.1.0", + "dev": true, "license": "ISC", "dependencies": { "minipass": "^3.0.0" @@ -30981,6 +31571,7 @@ }, "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { "version": "3.3.6", + "dev": true, "license": "ISC", "dependencies": { "yallist": "^4.0.0" @@ -30991,6 +31582,7 @@ }, "node_modules/tar/node_modules/mkdirp": { "version": "1.0.4", + "dev": true, "license": "MIT", "bin": { "mkdirp": "bin/cmd.js" @@ -31001,10 +31593,12 @@ }, "node_modules/tar/node_modules/yallist": { "version": "4.0.0", + "dev": true, "license": "ISC" }, "node_modules/temp-dir": { "version": "1.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -31193,6 +31787,7 @@ }, "node_modules/text-extensions": { "version": "1.9.0", + "dev": true, "license": "MIT", "engines": { "node": ">=0.10" @@ -31225,10 +31820,12 @@ }, "node_modules/through": { "version": "2.3.8", + "dev": true, "license": "MIT" }, "node_modules/through2": { "version": "4.0.2", + "dev": true, "license": "MIT", "dependencies": { "readable-stream": "3" @@ -31288,6 +31885,7 @@ }, "node_modules/tmp": { "version": "0.2.3", + "dev": true, "license": "MIT", "engines": { "node": ">=14.14" @@ -31338,6 +31936,7 @@ }, "node_modules/tr46": { "version": "0.0.3", + "dev": true, "license": "MIT" }, "node_modules/tree-kill": { @@ -31350,6 +31949,7 @@ }, "node_modules/treeverse": { "version": "3.0.0", + "dev": true, "license": "ISC", "engines": { "node": "^14.17.0 || ^16.13.0 || >=18.0.0" @@ -31357,6 +31957,7 @@ }, "node_modules/trim-newlines": { "version": "3.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=8" @@ -31444,6 +32045,7 @@ }, "node_modules/ts-node": { "version": "10.9.1", + "dev": true, "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", @@ -31485,6 +32087,7 @@ }, "node_modules/tsconfig-paths": { "version": "4.2.0", + "dev": true, "license": "MIT", "dependencies": { "json5": "^2.2.2", @@ -31497,6 +32100,7 @@ }, "node_modules/tsconfig-paths/node_modules/strip-bom": { "version": "3.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -31529,6 +32133,7 @@ }, "node_modules/tuf-js": { "version": "2.2.1", + "dev": true, "license": "MIT", "dependencies": { "@tufjs/models": "2.0.1", @@ -31655,6 +32260,7 @@ }, "node_modules/typedarray": { "version": "0.0.6", + "dev": true, "license": "MIT" }, "node_modules/typedarray-to-buffer": { @@ -31758,6 +32364,7 @@ }, "node_modules/uglify-js": { "version": "3.19.3", + "dev": true, "license": "BSD-2-Clause", "optional": true, "bin": { @@ -31849,6 +32456,7 @@ }, "node_modules/unique-filename": { "version": "3.0.0", + "dev": true, "license": "ISC", "dependencies": { "unique-slug": "^4.0.0" @@ -31859,6 +32467,7 @@ }, "node_modules/unique-slug": { "version": "4.0.0", + "dev": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" @@ -31869,10 +32478,12 @@ }, "node_modules/universal-user-agent": { "version": "6.0.1", + "dev": true, "license": "ISC" }, "node_modules/universalify": { "version": "2.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">= 10.0.0" @@ -31923,6 +32534,7 @@ }, "node_modules/upath": { "version": "2.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=4", @@ -32036,6 +32648,7 @@ }, "node_modules/util-deprecate": { "version": "1.0.2", + "devOptional": true, "license": "MIT" }, "node_modules/utils-merge": { @@ -32047,6 +32660,7 @@ }, "node_modules/uuid": { "version": "10.0.0", + "dev": true, "funding": [ "https://github.com/sponsors/broofa", "https://github.com/sponsors/ctavan" @@ -32058,10 +32672,12 @@ }, "node_modules/v8-compile-cache-lib": { "version": "3.0.1", + "dev": true, "license": "MIT" }, "node_modules/v8-to-istanbul": { "version": "9.3.0", + "dev": true, "license": "ISC", "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", @@ -32074,6 +32690,7 @@ }, "node_modules/validate-npm-package-license": { "version": "3.0.4", + "dev": true, "license": "Apache-2.0", "dependencies": { "spdx-correct": "^3.0.0", @@ -32275,6 +32892,7 @@ }, "node_modules/walk-up-path": { "version": "3.0.1", + "dev": true, "license": "ISC" }, "node_modules/walker": { @@ -32317,6 +32935,7 @@ }, "node_modules/webidl-conversions": { "version": "3.0.1", + "dev": true, "license": "BSD-2-Clause" }, "node_modules/webpack": { @@ -32647,6 +33266,7 @@ }, "node_modules/whatwg-url": { "version": "5.0.0", + "dev": true, "license": "MIT", "dependencies": { "tr46": "~0.0.3", @@ -32778,6 +33398,7 @@ }, "node_modules/wide-align": { "version": "1.1.5", + "dev": true, "license": "ISC", "dependencies": { "string-width": "^1.0.2 || 2 || 3 || 4" @@ -32944,6 +33565,7 @@ }, "node_modules/wordwrap": { "version": "1.0.0", + "dev": true, "license": "MIT" }, "node_modules/wrap-ansi": { @@ -32964,6 +33586,7 @@ "node_modules/wrap-ansi-cjs": { "name": "wrap-ansi", "version": "7.0.0", + "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", @@ -32983,6 +33606,7 @@ }, "node_modules/write-file-atomic": { "version": "5.0.1", + "dev": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", @@ -32994,6 +33618,7 @@ }, "node_modules/write-file-atomic/node_modules/signal-exit": { "version": "4.1.0", + "dev": true, "license": "ISC", "engines": { "node": ">=14" @@ -33004,6 +33629,7 @@ }, "node_modules/write-json-file": { "version": "3.2.0", + "dev": true, "license": "MIT", "dependencies": { "detect-indent": "^5.0.0", @@ -33019,6 +33645,7 @@ }, "node_modules/write-json-file/node_modules/detect-indent": { "version": "5.0.0", + "dev": true, "license": "MIT", "engines": { "node": ">=4" @@ -33026,6 +33653,7 @@ }, "node_modules/write-json-file/node_modules/make-dir": { "version": "2.1.0", + "dev": true, "license": "MIT", "dependencies": { "pify": "^4.0.1", @@ -33037,6 +33665,7 @@ }, "node_modules/write-json-file/node_modules/pify": { "version": "4.0.1", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -33044,6 +33673,7 @@ }, "node_modules/write-json-file/node_modules/semver": { "version": "5.7.2", + "dev": true, "license": "ISC", "bin": { "semver": "bin/semver" @@ -33051,6 +33681,7 @@ }, "node_modules/write-json-file/node_modules/write-file-atomic": { "version": "2.4.3", + "dev": true, "license": "ISC", "dependencies": { "graceful-fs": "^4.1.11", @@ -33060,6 +33691,7 @@ }, "node_modules/write-pkg": { "version": "4.0.0", + "dev": true, "license": "MIT", "dependencies": { "sort-keys": "^2.0.0", @@ -33072,6 +33704,7 @@ }, "node_modules/write-pkg/node_modules/type-fest": { "version": "0.4.1", + "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=6" @@ -33182,6 +33815,7 @@ }, "node_modules/xtend": { "version": "4.0.2", + "dev": true, "license": "MIT", "engines": { "node": ">=0.4" @@ -33208,7 +33842,7 @@ }, "node_modules/yaml": { "version": "1.10.2", - "devOptional": true, + "dev": true, "license": "ISC", "engines": { "node": ">= 6" @@ -33232,6 +33866,7 @@ }, "node_modules/yargs-parser": { "version": "20.2.9", + "dev": true, "license": "ISC", "engines": { "node": ">=10" @@ -33246,6 +33881,7 @@ }, "node_modules/yn": { "version": "3.1.1", + "dev": true, "license": "MIT", "engines": { "node": ">=6" @@ -33298,7 +33934,6 @@ "@typescript-eslint/parser": "^6.7.5", "eslint": "^8.51.0", "eslint-config-prettier": "^9.0.0", - "jest": "^29.7.0", "prettier": "^3.0.3", "typescript": "^5.2.2" } @@ -34909,7 +35544,7 @@ }, "packages/sdk-reactnative": { "name": "@screeb/react-native", - "version": "3.3.2", + "version": "4.0.2", "license": "MIT", "devDependencies": { "@commitlint/config-conventional": "^19.8.1", @@ -35773,6 +36408,48 @@ "url": "https://github.com/sponsors/eemeli" } }, + "packages/sdk-svelte": { + "name": "@screeb/sdk-svelte", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@screeb/sdk-browser": "^0.7.0" + }, + "devDependencies": { + "@screeb/eslint-config": "^0.1.7", + "@screeb/typescript-config": "^0.1.12", + "@types/node": "^20.8.4", + "@typescript-eslint/eslint-plugin": "^6.7.5", + "eslint": "^8.51.0", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-prettier": "^5.0.1", + "prettier": "^3.0.3", + "rollup": "^4.0.2", + "svelte": ">=4.0.0", + "typescript": "^5.2.2" + }, + "peerDependencies": { + "svelte": ">=4.0.0" + } + }, + "packages/sdk-svelte/node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "packages/sdk-svelte/node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "packages/sdk-vue": { "name": "@screeb/sdk-vue", "version": "0.3.0", @@ -35802,6 +36479,7 @@ "version": "0.1.13", "license": "MIT", "dependencies": { + "@rollup/plugin-terser": "^0.4.4", "@rollup/plugin-typescript": "^11.1.6" } } diff --git a/package.json b/package.json index 6895085..595e30e 100644 --- a/package.json +++ b/package.json @@ -17,12 +17,32 @@ "prepare": "npm run build && husky install", "start": "lerna run start", "test": "lerna run --parallel test", + "versions:sync": "node scripts/sync-sdk-versions.mjs", + "versions:check": "node scripts/sync-sdk-versions.mjs --check", + "ci:js": "npm run versions:check && npm run build && npm run lint", + "release:check": "npm run versions:check && npm run build && npm run lint", + "examples:build": "npm run build --workspace=examples/example-browser && npm run build --workspace=examples/example-react && npm run build --workspace=examples/example-vue && npm run build --workspace=examples/example-svelte && npm run build --workspace=examples/example-angular && npm run build --workspace=examples/example-ionic", + "examples:lint": "npm run lint --workspace=examples/example-browser && npm run lint --workspace=examples/example-react && npm run lint --workspace=examples/example-vue && npm run lint --workspace=examples/example-svelte && npm run lint --workspace=examples/example-angular && npm run lint --workspace=examples/example-ionic", + "examples:check": "npm run examples:lint && npm run examples:build", + "docs:reference:update": "node scripts/update-public-docs-reference.mjs", + "docs:reference:check": "node scripts/update-public-docs-reference.mjs --check", + "docs:reference:coverage": "node scripts/update-public-docs-reference.mjs --check-capabilities", + "verify:release": "node scripts/verify-release.mjs", + "size:sdks": "node scripts/report-sdk-sizes.mjs", "upgrade:all": "npm-upgrade && lerna exec --concurrency 1 npm-upgrade", - "preversion": "npm run clean && npm i && npm run lint && npm run build && npm run test", + "preversion": "npm run clean && npm i && npm run versions:check && npm run lint && npm run build && npm run test", "version": "lerna version && npm run build && npm run push:release", "push:release": "git push origin HEAD && for t in $(git tag --points-at HEAD); do git push origin \"refs/tags/$t\"; done" }, - "dependencies": { + "optionalDependencies": { + "@rollup/rollup-linux-x64-gnu": "4.22.5" + }, + "packageManager": "npm@10.8.2", + "engines": { + "node": ">=20.0.0", + "npm": ">=10.0.0" + }, + "devDependencies": { "@commitlint/cli": "^17.7.2", "@commitlint/config-conventional": "^17.7.0", "@tsconfig/svelte": "^5.0.2", @@ -31,12 +51,5 @@ "svelte-check": "^4.0.0", "svelte-preprocess": "^5.0.4", "typescript": "^5.2.2" - }, - "optionalDependencies": { - "@rollup/rollup-linux-x64-gnu": "4.22.5" - }, - "packageManager": "npm@10.8.2", - "engines": { - "node": ">=20.0.0" } } diff --git a/packages/eslint-config/package.json b/packages/eslint-config/package.json index 8cd05db..065839f 100644 --- a/packages/eslint-config/package.json +++ b/packages/eslint-config/package.json @@ -21,7 +21,6 @@ "@typescript-eslint/parser": "^6.7.5", "eslint": "^8.51.0", "eslint-config-prettier": "^9.0.0", - "jest": "^29.7.0", "prettier": "^3.0.3", "typescript": "^5.2.2" } diff --git a/packages/sdk-angular/README.md b/packages/sdk-angular/README.md index 84f6192..0441ad9 100644 --- a/packages/sdk-angular/README.md +++ b/packages/sdk-angular/README.md @@ -129,7 +129,7 @@ For a more advanced usage and a complete API documentation, see [documentation g For further information, see [our developer documentation](https://developers.screeb.app/sdk-js/install). ## Support -For any issues, please contact our support team at support@screeb.com. +For any issues, please contact our support team at support@screeb.app. ## Contributing All third party contributors acknowledge that any contributions they provide will be made under the same open source license that the open source project is provided under. diff --git a/packages/sdk-angular/docs/.nojekyll b/packages/sdk-angular/docs/.nojekyll deleted file mode 100644 index e2ac661..0000000 --- a/packages/sdk-angular/docs/.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/packages/sdk-angular/docs/README.md b/packages/sdk-angular/docs/README.md deleted file mode 100644 index 5460a23..0000000 --- a/packages/sdk-angular/docs/README.md +++ /dev/null @@ -1,11 +0,0 @@ -@screeb/sdk-angular - -# @screeb/sdk-angular - -## Table of contents - -### Classes - -- [Screeb](classes/Screeb.md) -- [ScreebConfig](classes/ScreebConfig.md) -- [ScreebModule](classes/ScreebModule.md) diff --git a/packages/sdk-angular/docs/classes/Screeb.md b/packages/sdk-angular/docs/classes/Screeb.md deleted file mode 100644 index a739c9a..0000000 --- a/packages/sdk-angular/docs/classes/Screeb.md +++ /dev/null @@ -1,613 +0,0 @@ -[@screeb/sdk-angular](../README.md) / Screeb - -# Class: Screeb - -## Table of contents - -### Constructors - -- [constructor](Screeb.md#constructor) - -### Properties - -- [config](Screeb.md#config) -- [isInitialized](Screeb.md#isinitialized) - -### Methods - -- [close](Screeb.md#close) -- [debug](Screeb.md#debug) -- [ensureScreeb](Screeb.md#ensurescreeb) -- [eventTrack](Screeb.md#eventtrack) -- [identity](Screeb.md#identity) -- [identityGet](Screeb.md#identityget) -- [identityGroupAssign](Screeb.md#identitygroupassign) -- [identityGroupUnassign](Screeb.md#identitygroupunassign) -- [identityProperties](Screeb.md#identityproperties) -- [identityReset](Screeb.md#identityreset) -- [init](Screeb.md#init) -- [load](Screeb.md#load) -- [messageClose](Screeb.md#messageclose) -- [messageStart](Screeb.md#messagestart) -- [sessionReplayStart](Screeb.md#sessionreplaystart) -- [sessionReplayStop](Screeb.md#sessionreplaystop) -- [surveyClose](Screeb.md#surveyclose) -- [surveyStart](Screeb.md#surveystart) -- [targetingDebug](Screeb.md#targetingdebug) - -## Constructors - -### constructor - -• **new Screeb**(`config`): [`Screeb`](Screeb.md) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `config` | [`ScreebConfig`](ScreebConfig.md) | - -#### Returns - -[`Screeb`](Screeb.md) - -## Properties - -### config - -• `Private` **config**: [`ScreebConfig`](ScreebConfig.md) - -___ - -### isInitialized - -• `Private` **isInitialized**: `boolean` = `false` - -## Methods - -### close - -▸ **close**(): `Promise`\<`unknown`\> - -Shutdowns current Screeb session. - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -this.screeb.close(); -``` - -___ - -### debug - -▸ **debug**(): `Promise`\<`unknown`\> - -Prints the actual state information of Screeb tag. - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -this.screeb.debug(); -// ******************* SCREEB SESSION DEBUG ********************* -// Screeb channel id: -// Screeb channel type: widget -// Screeb respondent id: -// Screeb survey id: none -// Screeb response id: none -// -// Screeb current session start: Thu May 04 2023 16:53:49 GMT+0200 (Central European Summer Time) -// Screeb current session last activity: Thu May 04 2023 17:41:30 GMT+0200 (Central European Summer Time) -// -// Screeb targeting engine status: disabled -// Screeb targeting engine: 3 surveys -// -// Detected platform: desktop -// Detected locale: en-GB -// Detected timezone: -120 -// ************************************************************** -``` - -___ - -### ensureScreeb - -▸ **ensureScreeb**(`functionName`, `onlyLoaded?`): `Promise`\<`void`\> - -#### Parameters - -| Name | Type | Default value | -| :------ | :------ | :------ | -| `functionName` | `string` | `undefined` | -| `onlyLoaded` | `boolean` | `false` | - -#### Returns - -`Promise`\<`void`\> - -___ - -### eventTrack - -▸ **eventTrack**(`eventName`, `eventProperties?`): `Promise`\<`unknown`\> - -Tracks a user event. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `eventName` | `string` | The event name. | -| `eventProperties?` | `PropertyRecord` | The properties of your event. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date. ``` | - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -this.screeb.eventTrack( - "Product added to cart", - { - product_name: 'Red bike 2021', - category: 'sport', - color: 'red', - price: 299, - count: 1, - reference: '2CF093TG1', - delivery_method: 'UPS', - user_logged: false, - added_at: new Date(), - } -); -``` - -___ - -### identity - -▸ **identity**(`userId`, `userProperties?`): `Promise`\<`unknown`\> - -Change the current user identity. -Warning: Running surveys will be closed. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `userId` | `string` | The unique identifier of your user. | -| `userProperties?` | `PropertyRecord` | The properties of your user. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date. ``` | - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -this.screeb.identity( - "", - { - firstname: '', - lastname: '', - plan: '', - last_seen_at: new Date(), - authenticated: true - } -); -``` - -___ - -### identityGet - -▸ **identityGet**(): `Promise`\<`ScreebIdentityGetReturn`\> - -Retrieves the current user identity. - -#### Returns - -`Promise`\<`ScreebIdentityGetReturn`\> - -**`Example`** - -```ts -console.log(await this.screeb.identityGet()); -// { -// channel_id: "", -// anonymous_id: "", -// user_id: "", -// is_ready: true, -// session_id: "", -// session_start: "2023-05-04T16:30:15.882Z", -// session_end: "2023-05-04T17:02:09.087Z", -// } -``` - -___ - -### identityGroupAssign - -▸ **identityGroupAssign**(`groupName`, `groupType?`, `groupProperties?`): `Promise`\<`unknown`\> - -Assigns the current user to a group. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `groupName` | `string` | | -| `groupType?` | `string` | | -| `groupProperties?` | `PropertyRecord` | The properties of your user group. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date. ``` | - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -this.screeb.identityGroupAssign( - 'company', - 'Apple', - { - address_line_1: 'Apple Campus', - address_line_2: '1 Infinite Loop', - city: 'Cupertino', - zipcode: 95014, - state: 'California', - country: 'United states', - } -); -``` - -___ - -### identityGroupUnassign - -▸ **identityGroupUnassign**(`groupName`, `groupType?`): `Promise`\<`unknown`\> - -Unassigns the current user to a group. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `groupName` | `string` | The name of your user group. | -| `groupType?` | `string` | The type of your user group. | - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -this.screeb.identityGroupUnassign('company', 'Apple'); -``` - -___ - -### identityProperties - -▸ **identityProperties**(`userProperties`): `Promise`\<`unknown`\> - -Adds properties to the current user identity. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `userProperties` | `PropertyRecord` | The properties of your user. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date. ``` | - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -// Set user properties -this.screeb.identityProperties( - { - firstname: '', - lastname: '', - plan: '', - last_seen_at: new Date(), - authenticated: true - } -); - -// Delete user property : set values to null -this.screeb.identityProperties( - { - age: null, - company: null, - logged: true, - } -); -``` - -___ - -### identityReset - -▸ **identityReset**(): `Promise`\<`unknown`\> - -Resets the current user identity. -Warning: This command must be called only once, since it creates a new identity on Screeb side. - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -this.screeb.identityReset(); -``` - -___ - -### init - -▸ **init**(`websiteId`, `userId?`, `userProperties?`, `hooks?`, `language?`, `spaNavigationHandler?`): `Promise`\<`unknown`\> - -Initializes Screeb tag. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `websiteId` | `string` | Your website/channel id. | -| `userId?` | `string` | The unique identifier of your user. | -| `userProperties?` | `PropertyRecord` | The properties of your user. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date ``` | -| `hooks?` | `HooksInit` | - | -| `language?` | `string` | Force a specific language for the tag. eg: 'en'. default: browser language. | -| `spaNavigationHandler?` | `SpaNavigationHandler` | - | - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -this.screeb.init( - "", - "", - { - firstname: '', - lastname: '', - plan: '', - last_seen_at: new Date(), - authenticated: true - }, - { - version: "1.0.0", - onReady: (payload) => console.log("Screeb SDK is ready!", payload), - }, - "en" -); -``` - -___ - -### load - -▸ **load**(`options?`): `Promise`\<`undefined`\> - -Appends Screeb tag into your dom. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `options?` | `ScreebOptions` | Screeb module options. | - -#### Returns - -`Promise`\<`undefined`\> - -**`Example`** - -```ts -this.screeb.load(); -``` - -___ - -### messageClose - -▸ **messageClose**(): `Promise`\<`unknown`\> - -Interrupts a running message. - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -this.screeb.messageClose(); -``` - -___ - -### messageStart - -▸ **messageStart**(`messageId`, `allowMultipleResponses?`, `hiddenFields?`, `hooks?`, `language?`): `Promise`\<`unknown`\> - -Starts a message by its ID. - -#### Parameters - -| Name | Type | Default value | -| :------ | :------ | :------ | -| `messageId` | `string` | `undefined` | -| `allowMultipleResponses` | `boolean` | `true` | -| `hiddenFields?` | `PropertyRecord` | `undefined` | -| `hooks?` | `HooksMessageStart` | `undefined` | -| `language?` | `string` | `undefined` | - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -this.screeb.messageStart( - '', - false, - { - color: "green", - article_id: 42 - }, - { - version: "1.0.0", - onMessageShowed: (payload) => console.log("Message showed", payload), - }, - "en" -); -``` - -___ - -### sessionReplayStart - -▸ **sessionReplayStart**(): `Promise`\<`unknown`\> - -Starts a session replay. - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -this.screeb.sessionReplayStart(); -``` - -___ - -### sessionReplayStop - -▸ **sessionReplayStop**(): `Promise`\<`unknown`\> - -Interrupts a running session replay. - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -this.screeb.sessionReplayStop(); -``` - -___ - -### surveyClose - -▸ **surveyClose**(): `Promise`\<`unknown`\> - -Interrupts a running survey. - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -this.screeb.surveyClose(); -``` - -___ - -### surveyStart - -▸ **surveyStart**(`surveyId`, `distributionId`, `allowMultipleResponses`, `hiddenFields?`, `hooks?`, `language?`, `selectors?`): `Promise`\<`unknown`\> - -Starts a survey by its ID. - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `surveyId` | `string` | -| `distributionId` | `string` | -| `allowMultipleResponses` | `boolean` | -| `hiddenFields?` | `PropertyRecord` | -| `hooks?` | `HooksSurveyStart` | -| `language?` | `string` | -| `selectors?` | `string` \| `string`[] | - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -this.screeb.surveyStart( - '', - '', - false, - { // optional - color: "green", - article_id: 42 - }, - { // optional - version: "1.0.0", - onSurveyShowed: (payload) => console.log("Survey showed", payload), - }, - "en", // optional - "#screeb-survey-container" // optional -); -``` - -___ - -### targetingDebug - -▸ **targetingDebug**(): `Promise`\<`unknown`\> - -Prints the current state of the targeting engine. - -#### Returns - -`Promise`\<`unknown`\> - -**`Example`** - -```ts -console.log(await this.screeb.targetingDebug()); -// targeting ************ SCREEB TARGETING RULES DEBUG ************** -// Disabled surveys are not listed here. -// -// Screeb channel id: -// Screeb respondent id: -// -// Survey : -// https://admin.screeb.app/org/last/survey//share -// -// - Rule of type "Device type (desktop/mobile/tablet)": true 🟢 -// - Rule of type "Multiple display": true 🟢 -// - Rule of type "Capping per time between survey display on current respondent": true 🟢 -// - Rule of type "User event count": false 🔴 -// - Rule of type "Capping per respondent display count": false 🔴 -``` diff --git a/packages/sdk-angular/docs/classes/ScreebConfig.md b/packages/sdk-angular/docs/classes/ScreebConfig.md deleted file mode 100644 index c566db5..0000000 --- a/packages/sdk-angular/docs/classes/ScreebConfig.md +++ /dev/null @@ -1,109 +0,0 @@ -[@screeb/sdk-angular](../README.md) / ScreebConfig - -# Class: ScreebConfig - -Configuration of Screeb module - -## Table of contents - -### Constructors - -- [constructor](ScreebConfig.md#constructor) - -### Properties - -- [autoInit](ScreebConfig.md#autoinit) -- [hooks](ScreebConfig.md#hooks) -- [options](ScreebConfig.md#options) -- [shouldLoad](ScreebConfig.md#shouldload) -- [spaNavigationHandler](ScreebConfig.md#spanavigationhandler) -- [userId](ScreebConfig.md#userid) -- [userProperties](ScreebConfig.md#userproperties) -- [websiteId](ScreebConfig.md#websiteid) - -## Constructors - -### constructor - -• **new ScreebConfig**(): [`ScreebConfig`](ScreebConfig.md) - -#### Returns - -[`ScreebConfig`](ScreebConfig.md) - -## Properties - -### autoInit - -• `Optional` **autoInit**: `boolean` - -Indicates if Screeb should be automatically initialized. - -**`Remarks`** - -if `true`, 'init' does not need to be called manually - -___ - -### hooks - -• `Optional` **hooks**: `HooksInit` - -Hooks to define callback for various event - -___ - -### options - -• `Optional` **options**: `ScreebOptions` - -Screeb tag initialization options. - -**`Remarks`** - -this is used is really particular cases, handle with care. - -___ - -### shouldLoad - -• `Optional` **shouldLoad**: `boolean` - -Indicates if Screeb should be automatically loaded. -This will ping to the Screeb servers. - -**`Remarks`** - -if `true`, 'load' does not need to be called manually. Can be used for multistaged environments - -___ - -### spaNavigationHandler - -• `Optional` **spaNavigationHandler**: `SpaNavigationHandler` - -Optional handler for the `in-page-spa` "Navigate to URL" target (custom SPA routers). - -___ - -### userId - -• `Optional` **userId**: `string` - -The unique identifier of your user. - -___ - -### userProperties - -• `Optional` **userProperties**: `PropertyRecord` - -The properties of your user. - -___ - -### websiteId - -• `Optional` **websiteId**: `string` - -Your website/channel id. diff --git a/packages/sdk-angular/docs/classes/ScreebModule.md b/packages/sdk-angular/docs/classes/ScreebModule.md deleted file mode 100644 index 9420771..0000000 --- a/packages/sdk-angular/docs/classes/ScreebModule.md +++ /dev/null @@ -1,69 +0,0 @@ -[@screeb/sdk-angular](../README.md) / ScreebModule - -# Class: ScreebModule - -## Table of contents - -### Constructors - -- [constructor](ScreebModule.md#constructor) - -### Methods - -- [forRoot](ScreebModule.md#forroot) - -## Constructors - -### constructor - -• **new ScreebModule**(`config`, `screeb`): [`ScreebModule`](ScreebModule.md) - -#### Parameters - -| Name | Type | -| :------ | :------ | -| `config` | [`ScreebConfig`](ScreebConfig.md) | -| `screeb` | [`Screeb`](Screeb.md) | - -#### Returns - -[`ScreebModule`](ScreebModule.md) - -## Methods - -### forRoot - -▸ **forRoot**(`config`): `ModuleWithProviders`\<[`ScreebModule`](ScreebModule.md)\> - -This is used to initialize Screeb at the top of your Angular application - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `config` | [`ScreebConfig`](ScreebConfig.md) | Configuration to pass to Screeb browser SDK | - -#### Returns - -`ModuleWithProviders`\<[`ScreebModule`](ScreebModule.md)\> - -**`Example`** - -```ts -ScreebModule.forRoot({ - autoInit: true, - websiteId: "", - userId: "", - userProperties: { - firstname: '', - lastname: '', - plan: '', - last_seen_at: new Date(), - authenticated: true - }, - { - version: "1.0.0", - onReady: (payload) => console.log("Screeb SDK is ready!", payload), - }, -}) -``` diff --git a/packages/sdk-angular/package.json b/packages/sdk-angular/package.json index eb168ff..ef3d3bf 100644 --- a/packages/sdk-angular/package.json +++ b/packages/sdk-angular/package.json @@ -36,7 +36,6 @@ }, "files": [ "dist", - "docs", "README.md" ], "scripts": { @@ -46,7 +45,7 @@ "build:doc": "typedoc projects/sdk-angular/src/public-api.ts", "clean": "rm -Rf dist", "lint": "eslint .", - "test": "echo '@TODO'", + "test": "echo \"no unit tests yet\"", "replace-version": "replace-in-files --string='0.0.0-dev' --replacement=$npm_package_version projects/sdk-angular/src/lib/constants.ts" }, "dependencies": { diff --git a/packages/sdk-angular/projects/sdk-angular/src/lib/screeb.ts b/packages/sdk-angular/projects/sdk-angular/src/lib/screeb.ts index e4c41b1..6bbe137 100644 --- a/packages/sdk-angular/projects/sdk-angular/src/lib/screeb.ts +++ b/packages/sdk-angular/projects/sdk-angular/src/lib/screeb.ts @@ -323,8 +323,8 @@ export class Screeb { */ public async surveyStart( surveyId: string, - distributionId: string, - allowMultipleResponses: boolean, + distributionId?: string, + allowMultipleResponses = true, hiddenFields?: _Screeb.PropertyRecord, hooks?: _Screeb.HooksSurveyStart, language?: string, @@ -423,6 +423,27 @@ export class Screeb { return _Screeb.sessionReplayStart(); } + /** + * Marks an element as sensitive in Screeb session replay. + */ + public ScreebMaskText(element: T): T { + return _Screeb.ScreebMaskText(element); + } + + /** + * Excludes an element from Screeb session replay capture. + */ + public ScreebNoCapture(element: T): T { + return _Screeb.ScreebNoCapture(element); + } + + /** + * Sets a stable Screeb element ID for IAM targeting and replay context. + */ + public ScreebId(element: T, id: string): T { + return _Screeb.ScreebId(element, id); + } + /** * Prints the current state of the targeting engine. * diff --git a/packages/sdk-angular/projects/sdk-angular/src/public-api.ts b/packages/sdk-angular/projects/sdk-angular/src/public-api.ts index 56d076b..e11a09b 100644 --- a/packages/sdk-angular/projects/sdk-angular/src/public-api.ts +++ b/packages/sdk-angular/projects/sdk-angular/src/public-api.ts @@ -5,3 +5,7 @@ export { ScreebModule } from "./lib/screeb.module"; export { ScreebConfig } from "./lib/screeb-config"; export { Screeb } from "./lib/screeb"; + +// Re-export the underlying browser SDK types (PropertyRecord, ScreebOptions, +// HooksInit, Survey, …) so consumers don't need a direct @screeb/sdk-browser dep. +export type * from "@screeb/sdk-browser"; diff --git a/packages/sdk-browser/README.md b/packages/sdk-browser/README.md index cfffa88..9830f49 100644 --- a/packages/sdk-browser/README.md +++ b/packages/sdk-browser/README.md @@ -85,7 +85,7 @@ For a more advanced usage and a complete API documentation, see [documentation g For further information, see [our developer documentation](https://developers.screeb.app/sdk-js/install). ## Support -For any issues, please contact our support team at support@screeb.com. +For any issues, please contact our support team at support@screeb.app. ## Contributing All third party contributors acknowledge that any contributions they provide will be made under the same open source license that the open source project is provided under. diff --git a/packages/sdk-browser/docs/.nojekyll b/packages/sdk-browser/docs/.nojekyll deleted file mode 100644 index e2ac661..0000000 --- a/packages/sdk-browser/docs/.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/packages/sdk-browser/docs/README.md b/packages/sdk-browser/docs/README.md deleted file mode 100644 index 16bc90c..0000000 --- a/packages/sdk-browser/docs/README.md +++ /dev/null @@ -1,1282 +0,0 @@ -@screeb/sdk-browser - -# @screeb/sdk-browser - -## Table of contents - -### Type Aliases - -- [Channel](README.md#channel) -- [ChannelType](README.md#channeltype) -- [HookCommonProperties](README.md#hookcommonproperties) -- [HookCommonPropertiesMessage](README.md#hookcommonpropertiesmessage) -- [HookCommonPropertiesSurvey](README.md#hookcommonpropertiessurvey) -- [HookOnButtonNavigateCompleted](README.md#hookonbuttonnavigatecompleted) -- [HookOnButtonNavigateStarted](README.md#hookonbuttonnavigatestarted) -- [HookOnMessageCompleted](README.md#hookonmessagecompleted) -- [HookOnMessageDisplayAllowed](README.md#hookonmessagedisplayallowed) -- [HookOnMessageHidden](README.md#hookonmessagehidden) -- [HookOnMessageShowed](README.md#hookonmessageshowed) -- [HookOnMessageStarted](README.md#hookonmessagestarted) -- [HookOnQuestionReplied](README.md#hookonquestionreplied) -- [HookOnReady](README.md#hookonready) -- [HookOnSurveyCompleted](README.md#hookonsurveycompleted) -- [HookOnSurveyDisplayAllowed](README.md#hookonsurveydisplayallowed) -- [HookOnSurveyHidden](README.md#hookonsurveyhidden) -- [HookOnSurveyShowed](README.md#hookonsurveyshowed) -- [HookOnSurveyStarted](README.md#hookonsurveystarted) -- [HooksInit](README.md#hooksinit) -- [HooksMessageStart](README.md#hooksmessagestart) -- [HooksSurveyStart](README.md#hookssurveystart) -- [Message](README.md#message) -- [PropertyRecord](README.md#propertyrecord) -- [PropertyType](README.md#propertytype) -- [ResponseItem](README.md#responseitem) -- [ResponseItemAnswer](README.md#responseitemanswer) -- [ResponseItemQuestion](README.md#responseitemquestion) -- [ResponseStatus](README.md#responsestatus) -- [ScreebEndpoints](README.md#screebendpoints) -- [ScreebFunction](README.md#screebfunction) -- [ScreebIdentityGetReturn](README.md#screebidentitygetreturn) -- [ScreebObject](README.md#screebobject) -- [ScreebOptions](README.md#screeboptions) -- [SpaNavigationHandler](README.md#spanavigationhandler) -- [Survey](README.md#survey) -- [SurveyFormat](README.md#surveyformat) -- [SurveyPosition](README.md#surveyposition) -- [SurveySize](README.md#surveysize) -- [User](README.md#user) - -### Functions - -- [close](README.md#close) -- [debug](README.md#debug) -- [eventTrack](README.md#eventtrack) -- [identity](README.md#identity) -- [identityGet](README.md#identityget) -- [identityGroupAssign](README.md#identitygroupassign) -- [identityGroupUnassign](README.md#identitygroupunassign) -- [identityProperties](README.md#identityproperties) -- [identityReset](README.md#identityreset) -- [init](README.md#init) -- [isLoaded](README.md#isloaded) -- [load](README.md#load) -- [messageClose](README.md#messageclose) -- [messageStart](README.md#messagestart) -- [sessionReplayStart](README.md#sessionreplaystart) -- [sessionReplayStop](README.md#sessionreplaystop) -- [surveyClose](README.md#surveyclose) -- [surveyStart](README.md#surveystart) -- [targetingDebug](README.md#targetingdebug) - -## Type Aliases - -### Channel - -Ƭ **Channel**: `Object` - -#### Type declaration - -| Name | Type | -| :------ | :------ | -| `id` | `string` | -| `type` | [`ChannelType`](README.md#channeltype) | - -___ - -### ChannelType - -Ƭ **ChannelType**: ``"widget"`` \| ``"ios"`` \| ``"android"`` - -___ - -### HookCommonProperties - -Ƭ **HookCommonProperties**: `Object` - -#### Type declaration - -| Name | Type | -| :------ | :------ | -| `channel` | [`Channel`](README.md#channel) | -| `user` | [`User`](README.md#user) | - -___ - -### HookCommonPropertiesMessage - -Ƭ **HookCommonPropertiesMessage**: [`HookCommonProperties`](README.md#hookcommonproperties) & \{ `message`: [`Message`](README.md#message) } - -___ - -### HookCommonPropertiesSurvey - -Ƭ **HookCommonPropertiesSurvey**: [`HookCommonProperties`](README.md#hookcommonproperties) & \{ `survey`: [`Survey`](README.md#survey) } - -___ - -### HookOnButtonNavigateCompleted - -Ƭ **HookOnButtonNavigateCompleted**: (`data`: [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) & \{ `button`: \{ `url`: `string` ; `url_target?`: `string` } ; `response`: \{ `id`: `string` } }) => `void` - -#### Type declaration - -▸ (`data`): `void` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `data` | [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) & \{ `button`: \{ `url`: `string` ; `url_target?`: `string` } ; `response`: \{ `id`: `string` } } | - -##### Returns - -`void` - -___ - -### HookOnButtonNavigateStarted - -Ƭ **HookOnButtonNavigateStarted**: (`data`: [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) & \{ `button`: \{ `url`: `string` ; `url_target?`: `string` } ; `response`: \{ `id`: `string` } }) => `void` - -#### Type declaration - -▸ (`data`): `void` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `data` | [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) & \{ `button`: \{ `url`: `string` ; `url_target?`: `string` } ; `response`: \{ `id`: `string` } } | - -##### Returns - -`void` - -___ - -### HookOnMessageCompleted - -Ƭ **HookOnMessageCompleted**: (`data`: [`HookCommonPropertiesMessage`](README.md#hookcommonpropertiesmessage) & \{ `response`: \{ `id`: `string` ; `items`: [`ResponseItem`](README.md#responseitem)[] } }) => `void` - -#### Type declaration - -▸ (`data`): `void` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `data` | [`HookCommonPropertiesMessage`](README.md#hookcommonpropertiesmessage) & \{ `response`: \{ `id`: `string` ; `items`: [`ResponseItem`](README.md#responseitem)[] } } | - -##### Returns - -`void` - -___ - -### HookOnMessageDisplayAllowed - -Ƭ **HookOnMessageDisplayAllowed**: (`data`: [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey)) => `boolean` - -#### Type declaration - -▸ (`data`): `boolean` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `data` | [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) | - -##### Returns - -`boolean` - -___ - -### HookOnMessageHidden - -Ƭ **HookOnMessageHidden**: (`data`: [`HookCommonPropertiesMessage`](README.md#hookcommonpropertiesmessage) & \{ `response`: \{ `hide_reason`: [`ResponseStatus`](README.md#responsestatus) ; `id`: `string` ; `items`: [`ResponseItem`](README.md#responseitem)[] } }) => `void` - -#### Type declaration - -▸ (`data`): `void` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `data` | [`HookCommonPropertiesMessage`](README.md#hookcommonpropertiesmessage) & \{ `response`: \{ `hide_reason`: [`ResponseStatus`](README.md#responsestatus) ; `id`: `string` ; `items`: [`ResponseItem`](README.md#responseitem)[] } } | - -##### Returns - -`void` - -___ - -### HookOnMessageShowed - -Ƭ **HookOnMessageShowed**: (`data`: [`HookCommonPropertiesMessage`](README.md#hookcommonpropertiesmessage) & \{ `response`: \{ `id`: `string` ; `items`: [`ResponseItem`](README.md#responseitem)[] } }) => `void` - -#### Type declaration - -▸ (`data`): `void` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `data` | [`HookCommonPropertiesMessage`](README.md#hookcommonpropertiesmessage) & \{ `response`: \{ `id`: `string` ; `items`: [`ResponseItem`](README.md#responseitem)[] } } | - -##### Returns - -`void` - -___ - -### HookOnMessageStarted - -Ƭ **HookOnMessageStarted**: (`data`: [`HookCommonPropertiesMessage`](README.md#hookcommonpropertiesmessage) & \{ `response`: \{ `id`: `string` } }) => `void` - -#### Type declaration - -▸ (`data`): `void` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `data` | [`HookCommonPropertiesMessage`](README.md#hookcommonpropertiesmessage) & \{ `response`: \{ `id`: `string` } } | - -##### Returns - -`void` - -___ - -### HookOnQuestionReplied - -Ƭ **HookOnQuestionReplied**: (`data`: [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) & \{ `response`: \{ `answer`: [`ResponseItemAnswer`](README.md#responseitemanswer) ; `id`: `string` ; `items`: [`ResponseItem`](README.md#responseitem)[] ; `question`: [`ResponseItemQuestion`](README.md#responseitemquestion) ; `status`: ``null`` } }) => `void` - -#### Type declaration - -▸ (`data`): `void` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `data` | [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) & \{ `response`: \{ `answer`: [`ResponseItemAnswer`](README.md#responseitemanswer) ; `id`: `string` ; `items`: [`ResponseItem`](README.md#responseitem)[] ; `question`: [`ResponseItemQuestion`](README.md#responseitemquestion) ; `status`: ``null`` } } | - -##### Returns - -`void` - -___ - -### HookOnReady - -Ƭ **HookOnReady**: (`data`: \{ `channel`: [`Channel`](README.md#channel) ; `user`: [`User`](README.md#user) }) => `void` - -#### Type declaration - -▸ (`data`): `void` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `data` | `Object` | -| `data.channel` | [`Channel`](README.md#channel) | -| `data.user` | [`User`](README.md#user) | - -##### Returns - -`void` - -___ - -### HookOnSurveyCompleted - -Ƭ **HookOnSurveyCompleted**: (`data`: [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) & \{ `response`: \{ `id`: `string` ; `items`: [`ResponseItem`](README.md#responseitem)[] } }) => `void` - -#### Type declaration - -▸ (`data`): `void` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `data` | [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) & \{ `response`: \{ `id`: `string` ; `items`: [`ResponseItem`](README.md#responseitem)[] } } | - -##### Returns - -`void` - -___ - -### HookOnSurveyDisplayAllowed - -Ƭ **HookOnSurveyDisplayAllowed**: (`data`: [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey)) => `boolean` - -#### Type declaration - -▸ (`data`): `boolean` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `data` | [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) | - -##### Returns - -`boolean` - -___ - -### HookOnSurveyHidden - -Ƭ **HookOnSurveyHidden**: (`data`: [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) & \{ `response`: \{ `hide_reason`: [`ResponseStatus`](README.md#responsestatus) ; `id`: `string` ; `items`: [`ResponseItem`](README.md#responseitem)[] } }) => `void` - -#### Type declaration - -▸ (`data`): `void` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `data` | [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) & \{ `response`: \{ `hide_reason`: [`ResponseStatus`](README.md#responsestatus) ; `id`: `string` ; `items`: [`ResponseItem`](README.md#responseitem)[] } } | - -##### Returns - -`void` - -___ - -### HookOnSurveyShowed - -Ƭ **HookOnSurveyShowed**: (`data`: [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) & \{ `response`: \{ `id`: `string` ; `items`: [`ResponseItem`](README.md#responseitem)[] } }) => `void` - -#### Type declaration - -▸ (`data`): `void` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `data` | [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) & \{ `response`: \{ `id`: `string` ; `items`: [`ResponseItem`](README.md#responseitem)[] } } | - -##### Returns - -`void` - -___ - -### HookOnSurveyStarted - -Ƭ **HookOnSurveyStarted**: (`data`: [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) & \{ `response`: \{ `id`: `string` } }) => `void` - -#### Type declaration - -▸ (`data`): `void` - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `data` | [`HookCommonPropertiesSurvey`](README.md#hookcommonpropertiessurvey) & \{ `response`: \{ `id`: `string` } } | - -##### Returns - -`void` - -___ - -### HooksInit - -Ƭ **HooksInit**: [`HooksSurveyStart`](README.md#hookssurveystart) & [`HooksMessageStart`](README.md#hooksmessagestart) & \{ `onMessageDisplayAllowed?`: [`HookOnMessageDisplayAllowed`](README.md#hookonmessagedisplayallowed) ; `onReady?`: [`HookOnReady`](README.md#hookonready) ; `onSurveyDisplayAllowed?`: [`HookOnSurveyDisplayAllowed`](README.md#hookonsurveydisplayallowed) ; `version`: `string` } - -This is the Screeb tag hooks object available on `init` command. - -___ - -### HooksMessageStart - -Ƭ **HooksMessageStart**: `Object` - -This is the Screeb tag hooks object available on `message.start` command. - -#### Type declaration - -| Name | Type | Description | -| :------ | :------ | :------ | -| `onButtonNavigateCompleted?` | [`HookOnButtonNavigateCompleted`](README.md#hookonbuttonnavigatecompleted) | This hook is triggered after a message/tour button "Navigate to URL" action ran (only fires when the SDK survives the navigation, e.g. `new-tab` or `in-page-spa`). | -| `onButtonNavigateStarted?` | [`HookOnButtonNavigateStarted`](README.md#hookonbuttonnavigatestarted) | This hook is triggered before a message/tour button "Navigate to URL" action runs. | -| `onMessageCompleted?` | [`HookOnMessageCompleted`](README.md#hookonmessagecompleted) | This hook is triggered when a message is completed | -| `onMessageHidden?` | [`HookOnMessageHidden`](README.md#hookonmessagehidden) | This hook is triggered when a message is hidden | -| `onMessageShowed?` | [`HookOnMessageShowed`](README.md#hookonmessageshowed) | This hook is triggered when a message is displayed on screen (also triggered when page is reloaded) | -| `onMessageStarted?` | [`HookOnMessageStarted`](README.md#hookonmessagestarted) | This hook is triggered when a message is started | -| `onQuestionReplied?` | [`HookOnQuestionReplied`](README.md#hookonquestionreplied) | This hook is triggered when a question is answered | - -___ - -### HooksSurveyStart - -Ƭ **HooksSurveyStart**: `Object` - -This is the Screeb tag hooks object available on `survey.start` command. - -#### Type declaration - -| Name | Type | Description | -| :------ | :------ | :------ | -| `onButtonNavigateCompleted?` | [`HookOnButtonNavigateCompleted`](README.md#hookonbuttonnavigatecompleted) | This hook is triggered after a message/tour button "Navigate to URL" action ran (only fires when the SDK survives the navigation, e.g. `new-tab` or `in-page-spa`). | -| `onButtonNavigateStarted?` | [`HookOnButtonNavigateStarted`](README.md#hookonbuttonnavigatestarted) | This hook is triggered before a message/tour button "Navigate to URL" action runs. | -| `onQuestionReplied?` | [`HookOnQuestionReplied`](README.md#hookonquestionreplied) | This hook is triggered when a question is answered | -| `onSurveyCompleted?` | [`HookOnSurveyCompleted`](README.md#hookonsurveycompleted) | This hook is triggered when a survey is completed | -| `onSurveyHidden?` | [`HookOnSurveyHidden`](README.md#hookonsurveyhidden) | This hook is triggered when a survey is hidden | -| `onSurveyShowed?` | [`HookOnSurveyShowed`](README.md#hookonsurveyshowed) | This hook is triggered when a survey is displayed on screen (also triggered when page is reloaded) | -| `onSurveyStarted?` | [`HookOnSurveyStarted`](README.md#hookonsurveystarted) | This hook is triggered when a survey is started | - -___ - -### Message - -Ƭ **Message**: `Object` - -#### Type declaration - -| Name | Type | -| :------ | :------ | -| `id` | `string` | -| `messagey_size` | ``100`` | - -___ - -### PropertyRecord - -Ƭ **PropertyRecord**: `Object` - -This is a property object that are supported by Screeb. - -#### Index signature - -▪ [key: `string`]: [`PropertyType`](README.md#propertytype) \| [`PropertyType`](README.md#propertytype)[] - -___ - -### PropertyType - -Ƭ **PropertyType**: `number` \| `boolean` \| `string` \| `Date` \| [`PropertyRecord`](README.md#propertyrecord) - -This is property types that are supported by Screeb. - -___ - -### ResponseItem - -Ƭ **ResponseItem**: `Object` - -#### Type declaration - -| Name | Type | -| :------ | :------ | -| `answer` | [`ResponseItemAnswer`](README.md#responseitemanswer) | -| `question` | [`ResponseItemQuestion`](README.md#responseitemquestion) | - -___ - -### ResponseItemAnswer - -Ƭ **ResponseItemAnswer**: `Object` - -#### Type declaration - -| Name | Type | -| :------ | :------ | -| `fields?` | \{ `boolean?`: `boolean` ; `number?`: `number` ; `text?`: `string` ; `time?`: `string` ; `type`: ``"string"`` \| ``"number"`` \| ``"boolean"`` \| ``"none"`` \| ``"time"`` \| ``"file"`` }[] | -| `replied_at?` | `string` | - -___ - -### ResponseItemQuestion - -Ƭ **ResponseItemQuestion**: `Object` - -#### Type declaration - -| Name | Type | -| :------ | :------ | -| `id` | `string` | -| `title` | `string` | -| `type` | ``"text"`` \| ``"video"`` | -| `url?` | `string` | - -___ - -### ResponseStatus - -Ƭ **ResponseStatus**: ``"displayed"`` \| ``"started"`` \| ``"ended"`` \| ``"closed"`` \| ``"interrupted"`` - -___ - -### ScreebEndpoints - -Ƭ **ScreebEndpoints**: `Object` - -Custom collector URLs used to bypass AdBlockers by routing Screeb traffic -through your own domain (reverse proxy). - -Every key is optional: any omitted endpoint falls back to Screeb's default -URL. All endpoints must use `https://` (or `wss://` for the WebSocket one), -and the domains must be allow-listed in Screeb Admin → Settings → Custom Domains. - -**`See`** - -https://developers.screeb.app/sdk-js/custom-collector-url - -#### Type declaration - -| Name | Type | Description | -| :------ | :------ | :------ | -| `centipede?` | `string` | WebSocket (real-time targeting). Default: `wss://centipede.screeb.app`. | -| `hostedPage?` | `string` | Hosted survey page. Default: `https://survey.screeb.app`. | -| `report?` | `string` | Response reporting. Default: `https://r.screeb.app/rpc`. | -| `rpc?` | `string` | REST API (surveys, tracking). Default: `https://rpc.screeb.app/rpc`. | -| `static?` | `string` | Static assets (images, fonts). Default: `https://static.screeb.app`. | - -___ - -### ScreebFunction - -Ƭ **ScreebFunction**: (...`_`: `unknown`[]) => `void` \| `Promise`\<`unknown`\> - -#### Type declaration - -▸ (`..._`): `void` \| `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `..._` | `unknown`[] | - -##### Returns - -`void` \| `Promise`\<`unknown`\> - -___ - -### ScreebIdentityGetReturn - -Ƭ **ScreebIdentityGetReturn**: `Object` - -This is the object returned by the function `identityGet()`. - -#### Type declaration - -| Name | Type | Description | -| :------ | :------ | :------ | -| `anonymous_id` | `string` | Anonymous id given to each user | -| `channel_id` | `string` | The current channel id with which the tag was initialized | -| `is_ready` | `boolean` | `true` if the tag is loaded, initialized and ready to rock | -| `session_end` | `string` | The current user session end time | -| `session_id` | `string` | The current user session id | -| `session_start` | `string` | The current user session start time | -| `user_id` | `string` | The authenticated id assigned to the user. | - -___ - -### ScreebObject - -Ƭ **ScreebObject**: [`ScreebFunction`](README.md#screebfunction) & \{ `q?`: \{ `args`: `unknown`[] ; `ko`: (`reason?`: `unknown`) => `void` ; `ok`: (`value?`: `unknown`) => `void` ; `v`: `number` }[] } - -This is the Screeb object publicly exposed in browser `window`. - -___ - -### ScreebOptions - -Ƭ **ScreebOptions**: `Object` - -This is the Screeb tag options object. - -#### Type declaration - -| Name | Type | Description | -| :------ | :------ | :------ | -| `endpoints?` | [`ScreebEndpoints`](README.md#screebendpoints) | Override Screeb's collector URLs to route traffic through your own domain (custom domains / AdBlocker bypass). Only the endpoints you provide are overridden; the rest fall back to Screeb's defaults. **`See`** https://developers.screeb.app/sdk-js/custom-collector-url | -| `screebEndpoint?` | `string` | Please don't do this. | -| `sdkName?` | `string` | SDK name (eg: sdk-browser, sdk-react, sdk-angular, etc...) | -| `sdkVersion?` | `string` | SDK version (eg: 1.2.3) | -| `window?` | `Window` | If you're running Screeb tag in an iframe, please set the inner window here. | - -___ - -### SpaNavigationHandler - -Ƭ **SpaNavigationHandler**: (`url`: `string`) => `void` \| `Promise`\<`void`\> - -Host-provided navigation handler for the `in-page-spa` "Navigate to URL" -target. It runs in your page (where your SPA router lives) instead of the tag -doing a `history.pushState` + `popstate` dispatch itself. Provide this when -your router does not resync on `popstate` (most React Router / Vue Router / -Angular Router setups do, so this is only needed for custom routers). May be -async so `onButtonNavigateCompleted` can await the route change. - -#### Type declaration - -▸ (`url`): `void` \| `Promise`\<`void`\> - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `url` | `string` | - -##### Returns - -`void` \| `Promise`\<`void`\> - -___ - -### Survey - -Ƭ **Survey**: `Object` - -#### Type declaration - -| Name | Type | -| :------ | :------ | -| `id` | `string` | -| `survey_format` | [`SurveyFormat`](README.md#surveyformat) | -| `survey_position` | [`SurveyPosition`](README.md#surveyposition) | -| `survey_size` | ``100`` | - -___ - -### SurveyFormat - -Ƭ **SurveyFormat**: ``"conversationnal"`` \| ``"cards"`` - -___ - -### SurveyPosition - -Ƭ **SurveyPosition**: ``"center-left"`` \| ``"center-center"`` \| ``"center-right"`` \| ``"bottom-left"`` \| ``"bottom-center"`` \| ``"bottom-right"`` - -___ - -### SurveySize - -Ƭ **SurveySize**: ``25`` \| ``50`` \| ``75`` \| ``100`` \| ``125`` \| ``150`` - -___ - -### User - -Ƭ **User**: `Object` - -#### Type declaration - -| Name | Type | -| :------ | :------ | -| `anonymous_id` | `string` | -| `userId?` | `string` | - -## Functions - -### close - -▸ **close**(): `void` \| `Promise`\<`unknown`\> - -Shutdowns current Screeb session. - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.close(); -``` - -___ - -### debug - -▸ **debug**(): `void` \| `Promise`\<`unknown`\> - -Prints the actual state information of Screeb tag. - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.debug(); -// ******************* SCREEB SESSION DEBUG ********************* -// Screeb channel id: -// Screeb channel type: widget -// Screeb respondent id: -// Screeb survey id: none -// Screeb response id: none -// -// Screeb current session start: Thu May 04 2023 16:53:49 GMT+0200 (Central European Summer Time) -// Screeb current session last activity: Thu May 04 2023 17:41:30 GMT+0200 (Central European Summer Time) -// -// Screeb targeting engine status: disabled -// Screeb targeting engine: 3 surveys -// -// Detected platform: desktop -// Detected locale: en-GB -// Detected timezone: -120 -// ************************************************************** -``` - -___ - -### eventTrack - -▸ **eventTrack**(`eventName`, `eventProperties?`): `void` \| `Promise`\<`unknown`\> - -Tracks a user event. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `eventName` | `string` | The event name. | -| `eventProperties?` | [`PropertyRecord`](README.md#propertyrecord) | The properties of your event. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date. ``` | - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.eventTrack( - "Product added to cart", - { - product_name: 'Red bike 2021', - category: 'sport', - color: 'red', - price: 299, - count: 1, - reference: '2CF093TG1', - delivery_method: 'UPS', - user_logged: false, - added_at: new Date(), - } -); -``` - -___ - -### identity - -▸ **identity**(`userId`, `userProperties?`): `void` \| `Promise`\<`unknown`\> - -Change the current user identity. -Warning: Running surveys will be closed. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `userId` | `string` | The unique identifier of your user. | -| `userProperties?` | [`PropertyRecord`](README.md#propertyrecord) | The properties of your user. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date. ``` | - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.identity( - "", - { - firstname: '', - lastname: '', - plan: '', - last_seen_at: new Date(), - authenticated: true - } -); -``` - -___ - -### identityGet - -▸ **identityGet**(): `Promise`\<[`ScreebIdentityGetReturn`](README.md#screebidentitygetreturn)\> - -Retrieves the current user identity. - -#### Returns - -`Promise`\<[`ScreebIdentityGetReturn`](README.md#screebidentitygetreturn)\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -console.log(await Screeb.identityGet()); -// { -// anonymous_id: "", -// user_id: "", -// session_id: "", -// session_start: "2023-05-04T16:30:15.882Z", -// session_end: "2023-05-04T17:02:09.087Z", -// channel_id: "", -// is_ready: true, -// } -``` - -___ - -### identityGroupAssign - -▸ **identityGroupAssign**(`groupName`, `groupType?`, `groupProperties?`): `void` \| `Promise`\<`unknown`\> - -Assigns the current user to a group. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `groupName` | `string` | | -| `groupType?` | `string` | | -| `groupProperties?` | [`PropertyRecord`](README.md#propertyrecord) | The properties of your user group. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date. ``` | - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.identityGroupAssign( - 'company', - 'Apple', - { - address_line_1: 'Apple Campus', - address_line_2: '1 Infinite Loop', - city: 'Cupertino', - zipcode: 95014, - state: 'California', - country: 'United states', - } -); -``` - -___ - -### identityGroupUnassign - -▸ **identityGroupUnassign**(`groupName`, `groupType?`): `void` \| `Promise`\<`unknown`\> - -Unassigns the current user to a group. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `groupName` | `string` | The name of your user group. | -| `groupType?` | `string` | The type of your user group. | - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.identityGroupUnassign('company', 'Apple'); -``` - -___ - -### identityProperties - -▸ **identityProperties**(`userProperties`): `void` \| `Promise`\<`unknown`\> - -Adds properties to the current user identity. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `userProperties` | [`PropertyRecord`](README.md#propertyrecord) | The properties of your user. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date. ``` | - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -// Set user properties -Screeb.identityProperties( - { - firstname: '', - lastname: '', - plan: '', - last_seen_at: new Date(), - authenticated: true - } -); - -// Delete user property : set values to null -Screeb.identityProperties( - { - age: null, - company: null, - logged: true, - } -); -``` - -___ - -### identityReset - -▸ **identityReset**(): `void` \| `Promise`\<`unknown`\> - -Resets the current user identity. -Warning: This command must be called only once, since it creates a new identity on Screeb side. - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.identityReset(); -``` - -___ - -### init - -▸ **init**(`websiteId`, `userId?`, `userProperties?`, `hooks?`, `language?`, `spaNavigationHandler?`): `void` \| `Promise`\<`unknown`\> - -Initializes Screeb tag. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `websiteId` | `string` | Your website/channel id. | -| `userId?` | `string` | The unique identifier of your user. | -| `userProperties?` | [`PropertyRecord`](README.md#propertyrecord) | The properties of your user. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date ``` | -| `hooks?` | [`HooksInit`](README.md#hooksinit) | Hooks to be called when SDK is ready or a survey is showed, started, completed, hidden or when a question is replied. | -| `language?` | `string` | Force a specific language for the tag. eg: 'en'. default: browser language. | -| `spaNavigationHandler?` | [`SpaNavigationHandler`](README.md#spanavigationhandler) | Optional handler for the `in-page-spa` "Navigate to URL" target. Runs in your page so your SPA router can navigate without a full reload. Only needed for custom routers that don't resync on `popstate` (React Router / Vue Router / Angular Router work out of the box). May be async so `onButtonNavigateCompleted` awaits it. | - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.init( - "", - "", - { - firstname: '', - lastname: '', - plan: '', - last_seen_at: new Date(), - authenticated: true - }, - { - version: "1.0.0", - onReady: (payload) => console.log("Screeb SDK is ready!", payload), - onButtonNavigateStarted: (payload) => console.log("Button navigate started", payload), - onButtonNavigateCompleted: (payload) => console.log("Button navigate completed", payload), - }, - "en" -); -``` - -___ - -### isLoaded - -▸ **isLoaded**(): `undefined` \| `boolean` - -Checks if Screeb tag has been loaded. - -#### Returns - -`undefined` \| `boolean` - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -console.log(Screeb.isLoaded()); // false -Screeb.load(); -console.log(Screeb.isLoaded()); // true -``` - -___ - -### load - -▸ **load**(`options?`): `Promise`\<`undefined`\> - -Appends Screeb tag into your dom. - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `options` | [`ScreebOptions`](README.md#screeboptions) | Screeb module options. | - -#### Returns - -`Promise`\<`undefined`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.load(); -``` - -___ - -### messageClose - -▸ **messageClose**(): `void` \| `Promise`\<`unknown`\> - -Interrupts a running message. - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.messageClose(); -``` - -___ - -### messageStart - -▸ **messageStart**(`messageId`, `allowMultipleResponses?`, `hiddenFields?`, `hooks?`, `language?`): `void` \| `Promise`\<`unknown`\> - -Starts a message by its ID. - -#### Parameters - -| Name | Type | Default value | -| :------ | :------ | :------ | -| `messageId` | `string` | `undefined` | -| `allowMultipleResponses` | `boolean` | `true` | -| `hiddenFields` | [`PropertyRecord`](README.md#propertyrecord) | `{}` | -| `hooks?` | [`HooksMessageStart`](README.md#hooksmessagestart) | `undefined` | -| `language?` | `string` | `undefined` | - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.messageStart( - '', - false, - { - color: "green", - article_id: 42 - }, - { - version: "1.0.0", - onMessageShowed: (payload) => console.log("Message showed", payload), - }, - "en" -); -``` - -___ - -### sessionReplayStart - -▸ **sessionReplayStart**(): `void` \| `Promise`\<`unknown`\> - -Starts a session replay. - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.sessionReplayStart(); -``` - -___ - -### sessionReplayStop - -▸ **sessionReplayStop**(): `void` \| `Promise`\<`unknown`\> - -Interrupts a running session replay. - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.sessionReplayStop(); -``` - -___ - -### surveyClose - -▸ **surveyClose**(): `void` \| `Promise`\<`unknown`\> - -Interrupts a running survey. - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.surveyClose(); -``` - -___ - -### surveyStart - -▸ **surveyStart**(`surveyId`, `distributionId?`, `allowMultipleResponses?`, `hiddenFields?`, `hooks?`, `language?`, `selectors?`): `void` \| `Promise`\<`unknown`\> - -Starts a survey by its ID. - -#### Parameters - -| Name | Type | Default value | -| :------ | :------ | :------ | -| `surveyId` | `string` | `undefined` | -| `distributionId?` | `string` | `undefined` | -| `allowMultipleResponses` | `boolean` | `true` | -| `hiddenFields` | [`PropertyRecord`](README.md#propertyrecord) | `{}` | -| `hooks?` | [`HooksSurveyStart`](README.md#hookssurveystart) | `undefined` | -| `language?` | `string` | `undefined` | -| `selectors?` | `string` \| `string`[] | `undefined` | - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -Screeb.surveyStart( - '', - '', - false, - { // optional - color: "green", - article_id: 42 - }, - { // optional - version: "1.0.0", - onSurveyShowed: (payload) => console.log("Survey showed", payload), - }, - "en", // optional - "#screeb-survey-container" // optional -); -``` - -___ - -### targetingDebug - -▸ **targetingDebug**(): `void` \| `Promise`\<`unknown`\> - -Prints the current state of the targeting engine. - -#### Returns - -`void` \| `Promise`\<`unknown`\> - -**`Example`** - -```ts -import * as Screeb from "@screeb/sdk-browser"; - -console.log(await Screeb.targetingDebug()); -// targeting ************ SCREEB TARGETING RULES DEBUG ************** -// Disabled surveys are not listed here. -// -// Screeb channel id: -// Screeb respondent id: -// -// Survey : -// https://admin.screeb.app/org/last/survey//share -// -// - Rule of type "Device type (desktop/mobile/tablet)": true 🟢 -// - Rule of type "Multiple display": true 🟢 -// - Rule of type "Capping per time between survey display on current respondent": true 🟢 -// - Rule of type "User event count": false 🔴 -// - Rule of type "Capping per respondent display count": false 🔴 -``` diff --git a/packages/sdk-browser/jest.config.cjs b/packages/sdk-browser/jest.config.cjs new file mode 100644 index 0000000..e616a68 --- /dev/null +++ b/packages/sdk-browser/jest.config.cjs @@ -0,0 +1,7 @@ +/** @type {import('jest').Config} */ +module.exports = { + testEnvironment: "jsdom", + transform: { + "^.+\\.tsx?$": ["ts-jest", { tsconfig: "/tsconfig.spec.json" }], + }, +}; diff --git a/packages/sdk-browser/jest.config.ts b/packages/sdk-browser/jest.config.ts deleted file mode 100644 index 1f23ba4..0000000 --- a/packages/sdk-browser/jest.config.ts +++ /dev/null @@ -1,6 +0,0 @@ -const configuration = { - preset: "ts-jest", - testEnvironment: "jsdom", -}; - -module.exports = configuration; diff --git a/packages/sdk-browser/package.json b/packages/sdk-browser/package.json index 29e191b..eb88b40 100644 --- a/packages/sdk-browser/package.json +++ b/packages/sdk-browser/package.json @@ -30,7 +30,6 @@ "types": "dist/es/index.d.ts", "files": [ "dist", - "docs", "README.md" ], "scripts": { @@ -38,7 +37,7 @@ "build:doc": "typedoc src/index.ts", "clean": "rm -Rf dist", "lint": "eslint .", - "test": "echo 'test disabled'" + "test": "jest" }, "devDependencies": { "@screeb/eslint-config": "^0.1.8", diff --git a/packages/sdk-browser/src/index.test.ts b/packages/sdk-browser/src/index.test.ts index e59df2a..d9736a9 100644 --- a/packages/sdk-browser/src/index.test.ts +++ b/packages/sdk-browser/src/index.test.ts @@ -4,7 +4,9 @@ declare const window: Window & { $screeb?: Screeb.ScreebObject }; describe("Screeb", () => { describe("load", () => { - afterEach(() => { + beforeEach(() => { + // Reset the shared tag state between tests. + delete window.$screeb; document.getElementsByTagName("html")[0].innerHTML = ""; }); @@ -33,16 +35,15 @@ describe("Screeb", () => { it("should enqueue commands", () => { Screeb.load({ screebEndpoint: "https://t.not-screeb.app/custom-tag.js" }); - expect(window.$screeb?.q).toEqual(undefined); - - Screeb.init("website-uuid", "user-uuid", { test: 123 }); - + // load() bootstraps the queue with the internal SDK-identity command. expect(window.$screeb?.q).toEqual([ { args: [ - "init", - "website-uuid", - { identity: { id: "user-uuid", properties: { test: 123 } } }, + "client.internal.web", + { + secondary_sdk_name: "sdk-browser", + secondary_sdk_version: expect.any(String), + }, ], // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore @@ -53,6 +54,23 @@ describe("Screeb", () => { v: 1, }, ]); + + Screeb.init("website-uuid", "user-uuid", { test: 123 }); + + expect(window.$screeb?.q).toContainEqual({ + args: [ + "init", + "website-uuid", + { identity: { id: "user-uuid", properties: { test: 123 } } }, + ], + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + ko: expect.any(Function), + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + ok: expect.any(Function), + v: 1, + }); }); }); }); diff --git a/packages/sdk-browser/src/index.ts b/packages/sdk-browser/src/index.ts index 5e61fb7..ef96c0d 100644 --- a/packages/sdk-browser/src/index.ts +++ b/packages/sdk-browser/src/index.ts @@ -557,6 +557,32 @@ export const sessionReplayStop = () => callScreebCommand("session-replay.stop"); export const sessionReplayStart = () => callScreebCommand("session-replay.start"); +/** + * Marks an element as sensitive in Screeb session replay. + * + * The tag records it with masked text content. + */ +export const ScreebMaskText = (element: T): T => { + element.classList.add("screeb-mask-text"); + return element; +}; + +/** + * Excludes an element from Screeb session replay capture. + */ +export const ScreebNoCapture = (element: T): T => { + element.classList.add("screeb-no-capture"); + return element; +}; + +/** + * Sets a stable Screeb element ID for IAM targeting and replay context. + */ +export const ScreebId = (element: T, id: string): T => { + element.setAttribute("data-screeb-id", id); + return element; +}; + /** * Prints the current state of the targeting engine. * diff --git a/packages/sdk-browser/tsconfig.spec.json b/packages/sdk-browser/tsconfig.spec.json new file mode 100644 index 0000000..a47ab35 --- /dev/null +++ b/packages/sdk-browser/tsconfig.spec.json @@ -0,0 +1,7 @@ +{ + "extends": "./tsconfig.json", + "include": ["src/**/*.test.ts"], + "compilerOptions": { + "types": ["jest", "node"] + } +} diff --git a/packages/sdk-flutter/CHANGELOG.md b/packages/sdk-flutter/CHANGELOG.md index a893d46..96db849 100644 --- a/packages/sdk-flutter/CHANGELOG.md +++ b/packages/sdk-flutter/CHANGELOG.md @@ -1,5 +1,18 @@ # CHANGELOGS +## Version 4.0.2 - Jul 8, 2026 + +**Improvements:** + +- More robust session replay, even under heavy memory pressure. +- Lower CPU and memory usage while recording. +- In-app surveys and messages stay reliably in the foreground. + +**Native SDK Versions:** + +- Android SDK version 4.0.2: [Release Notes](https://www.notion.so/screeb/Android-SDK-4e588a60a5b94e40bfb44595c9defe60) +- iOS SDK version 4.0.2: [Release Notes](https://www.notion.so/screeb/iOS-SDK-4ad6756875a14f2eb60711b86150a29d) + ## Version 3.1.0 - Feb 10, 2026 **Improvements:** diff --git a/packages/sdk-flutter/README.md b/packages/sdk-flutter/README.md index 57202ac..a0f406e 100644 --- a/packages/sdk-flutter/README.md +++ b/packages/sdk-flutter/README.md @@ -57,7 +57,7 @@ import 'package:plugin_screeb/plugin_screeb.dart'; PluginScreeb.initSdk(""); // Optional: identify visitor -PluginScreeb.setIdentity("", { +PluginScreeb.setIdentity("", properties: { "firstname": "", "lastname": "", "plan": "", @@ -76,7 +76,7 @@ For further information, see [our developer documentation](https://developers.sc ## Support -For any issues, please contact our support team at support@screeb.com. +For any issues, please contact our support team at support@screeb.app. ## Contributing diff --git a/packages/sdk-flutter/android/build.gradle b/packages/sdk-flutter/android/build.gradle index 00981e8..b62ddba 100644 --- a/packages/sdk-flutter/android/build.gradle +++ b/packages/sdk-flutter/android/build.gradle @@ -1,9 +1,12 @@ group 'app.screeb.plugin_screeb' -version '3.1.0' +version '4.0.2' buildscript { ext.kotlin_version = '1.8.22' repositories { + if (providers.gradleProperty("SCREEB_USE_LOCAL_SDK").orElse(providers.environmentVariable("SCREEB_USE_LOCAL_SDK")).orNull == "true") { + mavenLocal() + } google() mavenCentral() } @@ -16,6 +19,9 @@ buildscript { rootProject.allprojects { repositories { + if (providers.gradleProperty("SCREEB_USE_LOCAL_SDK").orElse(providers.environmentVariable("SCREEB_USE_LOCAL_SDK")).orNull == "true") { + mavenLocal() + } google() mavenCentral() } @@ -50,6 +56,6 @@ android { } dependencies { - api "app.screeb.sdk:survey:3.1.1" + api "app.screeb.sdk:survey:4.0.2" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" } diff --git a/packages/sdk-flutter/android/src/main/kotlin/app/screeb/plugin_screeb/PluginScreebPlugin.kt b/packages/sdk-flutter/android/src/main/kotlin/app/screeb/plugin_screeb/PluginScreebPlugin.kt index d0d9543..634db44 100644 --- a/packages/sdk-flutter/android/src/main/kotlin/app/screeb/plugin_screeb/PluginScreebPlugin.kt +++ b/packages/sdk-flutter/android/src/main/kotlin/app/screeb/plugin_screeb/PluginScreebPlugin.kt @@ -9,7 +9,6 @@ import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel import io.flutter.plugin.common.MethodChannel.MethodCallHandler import io.flutter.plugin.common.MethodChannel.Result -import org.json.JSONObject import java.util.HashMap class PluginScreebPlugin : FlutterPlugin, MethodCallHandler { @@ -20,7 +19,7 @@ class PluginScreebPlugin : FlutterPlugin, MethodCallHandler { channel = MethodChannel(flutterPluginBinding.binaryMessenger, "plugin_screeb") channel.setMethodCallHandler(this) context = flutterPluginBinding.applicationContext - Screeb.setSecondarySDK("flutter", "3.1.0") + Screeb.setSecondarySDK("flutter", "4.0.2") } override fun onMethodCall(@NonNull call: MethodCall, @NonNull result: Result) { @@ -34,28 +33,7 @@ class PluginScreebPlugin : FlutterPlugin, MethodCallHandler { val initOptions = (arguments[3] as? Map<*, *>)?.toProperty() val hooks = (arguments[4] as? Map<*, *>)?.toProperty() val language: String? = arguments[5] as? String - var mapHooks = hashMapOf() - if (hooks != null) { - hooks.forEach { (key, value) -> - if (key == "version"){ - mapHooks[key as String] = value as String - } else { - mapHooks[key as String] = { payload: Any -> channel.invokeMethod("handleHooks", hashMapOf("hookId" to value, "payload" to payload.toString()), object : MethodChannel.Result { - override fun success(result: Any?) { - val obj = payload as? JSONObject ?: return - val hookId = obj.getString("hook_id") ?: return - Screeb.onHookResult(hookId, result) - } - - override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { - println("error: $errorCode, $errorMessage, $errorDetails") - } - - override fun notImplemented() {} - }) } - } - } - } + val mapHooks = buildHooks(hooks) ?: hashMapOf() Screeb.pluginInit(channelId, userId, properties, initOptions, mapHooks, language) return result.success(true) @@ -105,35 +83,14 @@ class PluginScreebPlugin : FlutterPlugin, MethodCallHandler { val hooks = (arguments[4] as? Map<*, *>)?.toProperty() val language = arguments[5] as? String val distributionId = arguments[6] as? String - var mapHooks = hashMapOf() - if (hooks != null) { - hooks.forEach { (key, value) -> - if (key == "version"){ - mapHooks[key as String] = value as String - } else { - mapHooks[key as String] = { payload:Any -> channel.invokeMethod("handleHooks", hashMapOf("hookId" to value, "payload" to payload.toString()), object : MethodChannel.Result { - override fun success(result: Any?) { - val obj = payload as? JSONObject ?: return - val hookId = obj.getString("hook_id") ?: return - Screeb.onHookResult(hookId, result) - } - - override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { - println("error: $errorCode, $errorMessage, $errorDetails") - } - - override fun notImplemented() {} - }) } - } - } - } + val mapHooks = buildHooks(hooks) Screeb.startSurvey( surveyId = surveyId, allowMultipleResponses, (hiddenFields?.filterValues { it != null }) as HashMap?, ignoreSurveyStatus, - mapHooks.isNotEmpty().let { if (it) mapHooks else null }, + mapHooks, language, distributionId ) @@ -147,35 +104,14 @@ class PluginScreebPlugin : FlutterPlugin, MethodCallHandler { val hooks = (arguments[4] as? Map<*, *>)?.toProperty() val language = arguments[5] as? String val distributionId = arguments[6] as? String - var mapHooks = hashMapOf() - if (hooks != null) { - hooks.forEach { (key, value) -> - if (key == "version"){ - mapHooks[key as String] = value as String - } else { - mapHooks[key as String] = { payload:Any -> channel.invokeMethod("handleHooks", hashMapOf("hookId" to value, "payload" to payload.toString()), object : MethodChannel.Result { - override fun success(result: Any?) { - val obj = payload as? JSONObject ?: return - val hookId = obj.getString("hook_id") ?: return - Screeb.onHookResult(hookId, result) - } - - override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { - println("error: $errorCode, $errorMessage, $errorDetails") - } - - override fun notImplemented() {} - }) } - } - } - } + val mapHooks = buildHooks(hooks) Screeb.startMessage( messageId = messageId, allowMultipleResponses, (hiddenFields?.filterValues { it != null }) as HashMap?, ignoreMessageStatus, - mapHooks.isNotEmpty().let { if (it) mapHooks else null }, + mapHooks, language, distributionId ) @@ -244,6 +180,42 @@ class PluginScreebPlugin : FlutterPlugin, MethodCallHandler { channel.setMethodCallHandler(null) } + private fun buildHooks(hooks: Map?): HashMap? { + if (hooks == null) return null + + val hookIds = hashMapOf() + hooks.forEach { (key, value) -> + if (value is String) { + hookIds[key] = value + } + } + + if (hookIds.isEmpty()) return null + + return Screeb.makeHooks(hookIds) { hookId, nativeHookId, payload -> + channel.invokeMethod( + "handleHooks", + hashMapOf("hookId" to hookId, "payload" to payload), + hookResultCallback(nativeHookId) + ) + } + } + + private fun hookResultCallback(nativeHookId: String): MethodChannel.Result = + object : MethodChannel.Result { + override fun success(result: Any?) { + if (nativeHookId.isNotEmpty()) { + Screeb.onHookResult(nativeHookId, result) + } + } + + override fun error(errorCode: String, errorMessage: String?, errorDetails: Any?) { + println("error: $errorCode, $errorMessage, $errorDetails") + } + + override fun notImplemented() {} + } + companion object { const val CALL_INIT_SDK = "initSdk" const val CALL_SET_IDENTITY = "setIdentity" diff --git a/packages/sdk-flutter/ios/Classes/SwiftPluginScreebPlugin.swift b/packages/sdk-flutter/ios/Classes/SwiftPluginScreebPlugin.swift index b19425e..dc7da46 100644 --- a/packages/sdk-flutter/ios/Classes/SwiftPluginScreebPlugin.swift +++ b/packages/sdk-flutter/ios/Classes/SwiftPluginScreebPlugin.swift @@ -7,7 +7,7 @@ public class SwiftPluginScreebPlugin: NSObject, FlutterPlugin { static let instance = SwiftPluginScreebPlugin() public static func register(with registrar: FlutterPluginRegistrar) { - Screeb.setSecondarySDK(name: "flutter", version: "3.1.0") + Screeb.setSecondarySDK(name: "flutter", version: "4.0.2") SwiftPluginScreebPlugin.channel = FlutterMethodChannel(name: "plugin_screeb", binaryMessenger: registrar.messenger()) registrar.addMethodCallDelegate(instance, channel: SwiftPluginScreebPlugin.channel!) registrar.addApplicationDelegate(instance) @@ -23,26 +23,7 @@ public class SwiftPluginScreebPlugin: NSObject, FlutterPlugin { let initOptions: [String: Any?]? = args[3] as? [String: Any?] let hooks: [String: Any?]? = args[4] as? [String: Any?] let language: String? = args[5] as? String - var mapHooks: [String: Any?]? = nil - if (hooks != nil) { - mapHooks = [:] - hooks?.forEach{ hook in - if (hook.key == "version") { - mapHooks![hook.key] = hook.value as? String - } else { - mapHooks![hook.key] = {(payload: Any) -> () in DispatchQueue.main.async { - SwiftPluginScreebPlugin.channel!.invokeMethod("handleHooks", arguments: ["hookId": hook.value, "payload": String(describing: payload)]) { (result: Any?) in - if let obj = payload as? [String: Any?] { - if let hookId = obj["hook_id"] as? String { - Screeb.onHookResult(hookId, result) - } - } - } - }} - } - } - } - + let mapHooks = buildHooks(hooks) let initOptionsDict: NSDictionary = NSDictionary(dictionary: (initOptions ?? [:]).compactMapValues { $0 }) let initOptionsFinal = InitOptions(dict: initOptionsDict) Screeb.initSdk(context: nil, channelId: channelId, identity: userId, visitorProperty: property ?? [:], initOptions: initOptionsFinal, hooks: mapHooks as GlobalHooks?, language: language) @@ -107,23 +88,7 @@ public class SwiftPluginScreebPlugin: NSObject, FlutterPlugin { let hooks: [String: Any?]? = args[4] as? [String: Any?] let language: String? = args[5] as? String let distributionId: String? = args[6] as? String - var mapHooks: [String: Any?]? = nil - if (hooks != nil) { - mapHooks = [:] - hooks?.forEach{ hook in - if (hook.key == "version") { - mapHooks![hook.key] = hook.value as? String - } else { - mapHooks![hook.key] = {(payload:Any) -> () in DispatchQueue.main.async { - if let obj = payload as? [String: Any?] { - if let hookId = obj["hook_id"] as? String { - Screeb.onHookResult(hookId, result) - } - } - }} - } - } - } + let mapHooks = buildHooks(hooks) Screeb.startSurvey( surveyId: surveyId, allowMultipleResponses: allowMultipleResponses, @@ -145,23 +110,7 @@ public class SwiftPluginScreebPlugin: NSObject, FlutterPlugin { let hooks: [String: Any?]? = args[4] as? [String: Any?] let language: String? = args[5] as? String let distributionId: String? = args[6] as? String - var mapHooks: [String: Any?]? = nil - if (hooks != nil) { - mapHooks = [:] - hooks?.forEach{ hook in - if (hook.key == "version") { - mapHooks![hook.key] = hook.value as? String - } else { - mapHooks![hook.key] = {(payload:Any) -> () in DispatchQueue.main.async { - if let obj = payload as? [String: Any?] { - if let hookId = obj["hook_id"] as? String { - Screeb.onHookResult(hookId, result) - } - } - }} - } - } - } + let mapHooks = buildHooks(hooks) Screeb.startMessage( messageId: messageId, allowMultipleResponses: allowMultipleResponses, @@ -224,4 +173,33 @@ public class SwiftPluginScreebPlugin: NSObject, FlutterPlugin { break } } + + private func buildHooks(_ hooks: [String: Any?]?) -> [String: Any?]? { + guard let hooks else { return nil } + + var hookIds: [String: String] = [:] + hooks.forEach { hook in + if let value = hook.value as? String { + hookIds[hook.key] = value + } + } + + guard !hookIds.isEmpty else { return nil } + + let mapHooks = Screeb.makeHooks(hookIds) { hookId, nativeHookId, payload in + SwiftPluginScreebPlugin.dispatchHook(hookId: hookId, nativeHookId: nativeHookId, payload: payload) + } + return mapHooks.mapValues { $0 as Any? } + } + + private static func dispatchHook(hookId: String, nativeHookId: String, payload: String) { + guard let channel else { return } + DispatchQueue.main.async { + channel.invokeMethod("handleHooks", arguments: ["hookId": hookId, "payload": payload]) { callbackResult in + if !nativeHookId.isEmpty { + Screeb.onHookResult(nativeHookId, callbackResult) + } + } + } + } } diff --git a/packages/sdk-flutter/ios/plugin_screeb.podspec b/packages/sdk-flutter/ios/plugin_screeb.podspec index 1f7e745..25518d1 100644 --- a/packages/sdk-flutter/ios/plugin_screeb.podspec +++ b/packages/sdk-flutter/ios/plugin_screeb.podspec @@ -4,7 +4,7 @@ # Pod::Spec.new do |s| s.name = 'plugin_screeb' - s.version = '3.1.0' + s.version = '4.0.2' s.summary = 'Screeb - Continuous Product Discovery Without the Time Sink' s.description = 'Screeb - Continuous Product Discovery Without the Time Sink' s.homepage = 'https://screeb.app' @@ -21,5 +21,9 @@ Pod::Spec.new do |s| s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES', 'EXCLUDED_ARCHS[sdk=iphonesimulator*]' => 'i386' } s.swift_version = '5.0' - s.dependency 'Screeb', '3.1.1' + if ENV['SCREEB_USE_LOCAL_SDK'] == 'true' + s.dependency 'Screeb' + else + s.dependency 'Screeb', '4.0.2' + end end diff --git a/packages/sdk-flutter/lib/plugin_screeb.dart b/packages/sdk-flutter/lib/plugin_screeb.dart index 8654580..b4e0f0c 100644 --- a/packages/sdk-flutter/lib/plugin_screeb.dart +++ b/packages/sdk-flutter/lib/plugin_screeb.dart @@ -1,13 +1,47 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; +import 'package:flutter/semantics.dart'; import 'package:flutter/services.dart'; +import 'package:flutter/widgets.dart'; class PluginScreeb { static const MethodChannel _channel = MethodChannel('plugin_screeb'); static Map hooksRegistry = {}; + // Screeb's native session-replay mirror reads the widget tree through Flutter's accessibility + // bridge. Flutter only builds that semantics tree when a screen reader is active, so we force it + // on (and keep the handle alive) — otherwise the mirror sees an opaque surface and captures + // nothing. Cheap: semantics are only serialized to the native side, no UI change. + static SemanticsHandle? _semanticsHandle; + static void _ensureSemanticsEnabled() { + try { + WidgetsFlutterBinding.ensureInitialized(); + _semanticsHandle ??= SemanticsBinding.instance.ensureSemantics(); + } catch (_) { + // Semantics are best-effort; never block SDK init on them. + } + } + + static Map? _buildHooksMap(Map? hooks) { + if (hooks == null) { + return null; + } + + final mapHooksId = {}; + hooks.forEach((key, value) { + if (key == "version") { + mapHooksId[key] = value.toString(); + } else { + final uuid = UniqueKey().toString() + key; + hooksRegistry[uuid] = value; + mapHooksId[key] = uuid; + } + }); + return mapHooksId; + } + /// Provides a way to initialize the SDK with a specific channel ID /// /// Call this method first elsewhere subsequent calls will fail @@ -20,20 +54,9 @@ class PluginScreeb { Map? hooks, String? language}) { _channel.setMethodCallHandler(channelHandler); + _ensureSemanticsEnabled(); - Map? mapHooksId; - if (hooks != null) { - mapHooksId = {}; - hooks.forEach((key, value) { - if (key == "version") { - mapHooksId![key] = value.toString(); - } else { - String uuid = UniqueKey().toString() + key; - hooksRegistry[uuid] = value; - mapHooksId![key] = uuid; - } - }); - } + final mapHooksId = _buildHooksMap(hooks); return _channel.invokeMethod('initSdk', [ channelId, @@ -49,37 +72,49 @@ class PluginScreeb { /// /// Providing a [userId] is important to sharpen the Screeb targeting engine /// and avoid survey triggering more than necessary. - static Future setIdentity(String userId, {Map? properties}) => + static Future setIdentity(String userId, + {Map? properties}) => _channel.invokeMethod('setIdentity', [userId, _formatDates(properties)]); /// Send to Screeb backend the user's custom [properties] /// /// This api call is important to trigger a survey where the targeting is /// configured using visitor properties parameters. - static Future setProperty(Map? properties) => + static Future setProperties(Map? properties) => _channel.invokeMethod('setProperty', [_formatDates(properties)]); + /// Deprecated alias for [setProperties]. + @Deprecated('Use setProperties instead.') + static Future setProperty(Map? properties) => + setProperties(properties); + /// Send to Screeb backend a group assignation for current user [properties] /// /// This api call is important to improve analysis. - static Future assignGroup(String? groupType, String groupName, {Map? properties}) => - _channel.invokeMethod('assignGroup', [groupType, groupName, _formatDates(properties)]); + static Future assignGroup(String? groupType, String groupName, + {Map? properties}) => + _channel.invokeMethod( + 'assignGroup', [groupType, groupName, _formatDates(properties)]); /// Send to Screeb backend a group unassignation for current user [properties] /// /// This api call is important to improve analysis. - static Future unassignGroup(String? groupType, String groupName, Map? properties) => - _channel.invokeMethod('unassignGroup', [groupType, groupName, _formatDates(properties)]); + static Future unassignGroup(String? groupType, String groupName, + Map? properties) => + _channel.invokeMethod( + 'unassignGroup', [groupType, groupName, _formatDates(properties)]); /// Send to Screeb backend a tracking [eventId] with optional [properties] - static Future trackEvent(String eventId, {Map? properties}) => + static Future trackEvent(String eventId, + {Map? properties}) => _channel.invokeMethod('trackEvent', [eventId, _formatDates(properties)]); /// Send to Screeb backend a tracking [screen] name with optional [properties] /// /// This api call is important to trigger a survey where the targeting is /// configured using screens parameters. - static Future trackScreen(String screen, {Map? properties}) => + static Future trackScreen(String screen, + {Map? properties}) => _channel.invokeMethod('trackScreen', [screen, _formatDates(properties)]); /// Provide a way to start a survey with a specific [surveyId] @@ -95,19 +130,7 @@ class PluginScreeb { String? language, String? distributionId, }) { - Map? mapHooksId; - if (hooks != null) { - mapHooksId = {}; - hooks.forEach((key, value) { - if (key == "version") { - mapHooksId![key] = value.toString(); - } else { - String uuid = UniqueKey().toString() + key; - hooksRegistry[uuid] = value; - mapHooksId![key] = uuid; - } - }); - } + final mapHooksId = _buildHooksMap(hooks); return _channel.invokeMethod('startSurvey', [ surveyId, @@ -133,19 +156,7 @@ class PluginScreeb { String? language, String? distributionId, }) { - Map? mapHooksId; - if (hooks != null) { - mapHooksId = {}; - hooks.forEach((key, value) { - if (key == "version") { - mapHooksId![key] = value.toString(); - } else { - String uuid = UniqueKey().toString() + key; - hooksRegistry[uuid] = value; - mapHooksId![key] = uuid; - } - }); - } + final mapHooksId = _buildHooksMap(hooks); return _channel.invokeMethod('startMessage', [ messageId, @@ -166,25 +177,30 @@ class PluginScreeb { ///Provide a way to close the survey /// ///Its the opposite of startSurvey - static Future closeSurvey({String? surveyId}) => _channel.invokeMethod('closeSurvey', [surveyId]); + static Future closeSurvey({String? surveyId}) => + _channel.invokeMethod('closeSurvey', [surveyId]); ///Provide a way to close the message /// ///Its the opposite of startMessage - static Future closeMessage({String? messageId}) => _channel.invokeMethod('closeMessage', [messageId]); + static Future closeMessage({String? messageId}) => + _channel.invokeMethod('closeMessage', [messageId]); ///Provide a way to start session replay recording - static Future sessionReplayStart() => _channel.invokeMethod('sessionReplayStart', []); + static Future sessionReplayStart() => + _channel.invokeMethod('sessionReplayStart', []); ///Provide a way to stop session replay recording /// ///Its the opposite of sessionReplayStart - static Future sessionReplayStop() => _channel.invokeMethod('sessionReplayStop', []); + static Future sessionReplayStop() => + _channel.invokeMethod('sessionReplayStop', []); ///Provide a way to reset the identity of the user /// ///You can use it on the disconnection of a user for example to make it anonymous - static Future resetIdentity() => _channel.invokeMethod('resetIdentity', []); + static Future resetIdentity() => + _channel.invokeMethod('resetIdentity', []); /// Provides a way to get the current visitor identity static Future?> getIdentity() async { @@ -213,7 +229,8 @@ class PluginScreeb { static Future channelHandler(MethodCall methodCall) async { switch (methodCall.method) { case "handleHooks": - return handleHooks(methodCall.arguments["hookId"], methodCall.arguments["payload"]); + return handleHooks( + methodCall.arguments["hookId"], methodCall.arguments["payload"]); default: throw Exception("Method not implemented"); } @@ -231,12 +248,116 @@ class PluginScreeb { } /// Format payloads so DateTime properties are correctly interpreted by the SDK - static Map? _formatDates(Map? properties) => properties?.map( - (key, value) => MapEntry( - key, - value is DateTime - ? '${value.toIso8601String()}+${value.timeZoneOffset.inHours.toString().padLeft(2, '0')}:00' - : value, - ), - ); + static Map? _formatDates(Map? properties) { + final formatted = _formatValue(properties); + return formatted is Map ? formatted : null; + } + + static dynamic _formatValue(dynamic value) { + if (value is DateTime) { + return _formatDateTime(value); + } + if (value is Map) { + return value.map((key, nestedValue) => + MapEntry(key.toString(), _formatValue(nestedValue))); + } + if (value is Iterable) { + return value.map(_formatValue).toList(); + } + return value; + } + + static String _formatDateTime(DateTime value) { + final offset = value.timeZoneOffset; + final sign = offset.isNegative ? '-' : '+'; + final absOffset = offset.abs(); + final offsetHours = absOffset.inHours.toString().padLeft(2, '0'); + final offsetMinutes = (absOffset.inMinutes % 60).toString().padLeft(2, '0'); + final localValue = value.isUtc ? value : value.toLocal(); + String two(int number) => number.toString().padLeft(2, '0'); + String three(int number) => number.toString().padLeft(3, '0'); + + return '${localValue.year.toString().padLeft(4, '0')}-' + '${two(localValue.month)}-' + '${two(localValue.day)}T' + '${two(localValue.hour)}:' + '${two(localValue.minute)}:' + '${two(localValue.second)}.' + '${three(localValue.millisecond)}' + '$sign$offsetHours:$offsetMinutes'; + } +} + +/// Marks [child] as sensitive in Screeb session replay. +/// +/// The underlying native SDK masks the view instead of recording its content. +class ScreebMaskText extends StatelessWidget { + const ScreebMaskText({Key? key, required this.child}) : super(key: key); + + final Widget child; + + @override + Widget build(BuildContext context) { + return Semantics( + identifier: _screebPlatformMarker( + android: 'screeb-mask-text', + ios: 'screebMaskText', + ), + container: true, + child: child, + ); + } +} + +/// Excludes [child] from Screeb session replay capture. +class ScreebNoCapture extends StatelessWidget { + const ScreebNoCapture({Key? key, required this.child}) : super(key: key); + + final Widget child; + + @override + Widget build(BuildContext context) { + return Semantics( + identifier: _screebPlatformMarker( + android: 'screeb-no-capture', + ios: 'screebNoCapture', + ), + container: true, + child: child, + ); + } +} + +/// Sets a stable Screeb element ID on [child] for IAM targeting. +class ScreebId extends StatelessWidget { + const ScreebId(this.id, {Key? key, required this.child}) : super(key: key); + + final String id; + final Widget child; + + @override + Widget build(BuildContext context) { + return Semantics( + identifier: 'screebId:$id', + container: true, + child: child, + ); + } +} + +String _screebPlatformMarker({ + required String android, + required String ios, +}) { + switch (defaultTargetPlatform) { + case TargetPlatform.android: + return android; + case TargetPlatform.iOS: + return ios; + case TargetPlatform.fuchsia: + case TargetPlatform.linux: + case TargetPlatform.macOS: + case TargetPlatform.windows: + return ios; + } } diff --git a/packages/sdk-flutter/pubspec.yaml b/packages/sdk-flutter/pubspec.yaml index 1b8a450..0fc91fa 100644 --- a/packages/sdk-flutter/pubspec.yaml +++ b/packages/sdk-flutter/pubspec.yaml @@ -1,6 +1,6 @@ name: plugin_screeb description: Continuous Product Discovery -version: 3.1.0 +version: 4.0.2 homepage: https://screeb.app repository: https://github.com/ScreebApp/sdk diff --git a/packages/sdk-flutter/test/screeb_hooks_test.dart b/packages/sdk-flutter/test/screeb_hooks_test.dart new file mode 100644 index 0000000..e2d573f --- /dev/null +++ b/packages/sdk-flutter/test/screeb_hooks_test.dart @@ -0,0 +1,51 @@ +import 'package:flutter_test/flutter_test.dart'; +import 'package:plugin_screeb/plugin_screeb.dart'; + +void main() { + setUp(() { + PluginScreeb.hooksRegistry.clear(); + }); + + tearDown(() { + PluginScreeb.hooksRegistry.clear(); + }); + + test('handleHooks forwards payload to the registered hook', () async { + String? receivedPayload; + PluginScreeb.hooksRegistry['wrapper-hook'] = (String payload) { + receivedPayload = payload; + return true; + }; + + final result = await PluginScreeb.handleHooks( + 'wrapper-hook', + '{"hook_id":"native-hook"}', + ); + + expect(receivedPayload, '{"hook_id":"native-hook"}'); + expect(result, isTrue); + }); + + test('handleHooks awaits async hook results', () async { + PluginScreeb.hooksRegistry['wrapper-hook'] = (String payload) async { + await Future.delayed(Duration.zero); + return payload.contains('native-hook'); + }; + + final result = await PluginScreeb.handleHooks( + 'wrapper-hook', + '{"hook_id":"native-hook"}', + ); + + expect(result, isTrue); + }); + + test('handleHooks returns null when hook is not registered', () async { + final result = await PluginScreeb.handleHooks( + 'missing-hook', + '{"hook_id":"native-hook"}', + ); + + expect(result, isNull); + }); +} diff --git a/packages/sdk-flutter/test/screeb_privacy_widgets_test.dart b/packages/sdk-flutter/test/screeb_privacy_widgets_test.dart new file mode 100644 index 0000000..4588550 --- /dev/null +++ b/packages/sdk-flutter/test/screeb_privacy_widgets_test.dart @@ -0,0 +1,51 @@ +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_test/flutter_test.dart'; +import 'package:plugin_screeb/plugin_screeb.dart'; + +void main() { + testWidgets('ScreebMaskText adds the native mask marker', (tester) async { + debugDefaultTargetPlatformOverride = TargetPlatform.android; + + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: ScreebMaskText(child: Text('Secret')), + ), + ); + + final semantics = tester.widget(find.byType(Semantics)); + + expect(semantics.properties.identifier, 'screeb-mask-text'); + debugDefaultTargetPlatformOverride = null; + }); + + testWidgets('ScreebNoCapture adds the native no-capture marker', (tester) async { + debugDefaultTargetPlatformOverride = TargetPlatform.iOS; + + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: ScreebNoCapture(child: Text('Hidden')), + ), + ); + + final semantics = tester.widget(find.byType(Semantics)); + + expect(semantics.properties.identifier, 'screebNoCapture'); + debugDefaultTargetPlatformOverride = null; + }); + + testWidgets('ScreebId prefixes stable element IDs', (tester) async { + await tester.pumpWidget( + const Directionality( + textDirection: TextDirection.ltr, + child: ScreebId('checkout_button', child: Text('Checkout')), + ), + ); + + final semantics = tester.widget(find.byType(Semantics)); + + expect(semantics.properties.identifier, 'screebId:checkout_button'); + }); +} diff --git a/packages/sdk-kmp/README.md b/packages/sdk-kmp/README.md index 81555f1..178dd68 100644 --- a/packages/sdk-kmp/README.md +++ b/packages/sdk-kmp/README.md @@ -72,7 +72,7 @@ Screeb.initSdk( channelId = "YOUR_CHANNEL_ID", userId = "user_123", properties = mapOf("plan" to "pro"), - initOptions = ScreebInitOptions(isDebugMode = true), + initOptions = ScreebInitOptions(isDebugMode = false), ) // Track events @@ -158,7 +158,7 @@ All methods return `Boolean?` (or the appropriate type), and `null` on unexpecte ## Support -For any issues, please contact our support team at support@screeb.com. +For any issues, please contact our support team at support@screeb.app. ## Contributing diff --git a/packages/sdk-kmp/build.gradle.kts b/packages/sdk-kmp/build.gradle.kts index b6ab271..39a0a8b 100644 --- a/packages/sdk-kmp/build.gradle.kts +++ b/packages/sdk-kmp/build.gradle.kts @@ -6,6 +6,7 @@ plugins { id("com.android.library") version "8.7.3" id("maven-publish") id("signing") + id("io.github.gradle-nexus.publish-plugin") version "2.0.0" } group = project.property("GROUP") as String @@ -152,19 +153,16 @@ publishing { } } } +} + +nexusPublishing { repositories { - maven { - name = "OSSRH" - url = uri( - if ((version as String).endsWith("SNAPSHOT")) - "https://s01.oss.sonatype.org/content/repositories/snapshots/" - else - "https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/" - ) - credentials { - username = System.getenv("OSSRH_USERNAME") - password = System.getenv("OSSRH_PASSWORD") - } + sonatype { + // Central Publishing Portal (legacy s01.oss.sonatype.org is decommissioned) + nexusUrl.set(uri("https://ossrh-staging-api.central.sonatype.com/service/local/")) + snapshotRepositoryUrl.set(uri("https://central.sonatype.com/repository/maven-snapshots/")) + username.set(System.getenv("OSSRH_USERNAME")) + password.set(System.getenv("OSSRH_PASSWORD")) } } } @@ -178,3 +176,9 @@ signing { sign(publishing.publications) } } + +// The shared javadoc jar is signed once per publication into the same .asc: +// every publish task must wait for all sign tasks or Gradle fails validation. +tasks.withType().configureEach { + dependsOn(tasks.withType()) +} diff --git a/packages/sdk-kmp/gradle.properties b/packages/sdk-kmp/gradle.properties index 13eec84..d4c3a42 100644 --- a/packages/sdk-kmp/gradle.properties +++ b/packages/sdk-kmp/gradle.properties @@ -5,9 +5,9 @@ android.useAndroidX=true # Publishing GROUP=app.screeb.sdk.kmp -VERSION_NAME=0.1.0 -SCREEB_ANDROID_SDK_VERSION=4.0.0 -SCREEB_IOS_SDK_VERSION=4.0.0 +VERSION_NAME=4.0.2 +SCREEB_ANDROID_SDK_VERSION=4.0.2 +SCREEB_IOS_SDK_VERSION=4.0.2 POM_ARTIFACT_ID=screeb-kmp POM_NAME=Screeb KMP SDK POM_DESCRIPTION=Kotlin Multiplatform SDK for Screeb survey & messaging platform diff --git a/packages/sdk-kmp/src/androidMain/kotlin/app/screeb/sdk/kmp/Screeb.android.kt b/packages/sdk-kmp/src/androidMain/kotlin/app/screeb/sdk/kmp/Screeb.android.kt index bf835a7..709d229 100644 --- a/packages/sdk-kmp/src/androidMain/kotlin/app/screeb/sdk/kmp/Screeb.android.kt +++ b/packages/sdk-kmp/src/androidMain/kotlin/app/screeb/sdk/kmp/Screeb.android.kt @@ -31,7 +31,7 @@ actual object Screeb { language, ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun closeSdk(): Boolean? = withContext(Dispatchers.Main) { @@ -40,7 +40,7 @@ actual object Screeb { HooksScope.reset() AndroidScreeb.closeSdk() true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun setIdentity( @@ -53,7 +53,7 @@ actual object Screeb { ScreebUtils.formatProperties(properties)?.let { HashMap(it) }, ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun setProperties(properties: Map?): Boolean? = @@ -62,11 +62,11 @@ actual object Screeb { val props = ScreebUtils.formatProperties(properties)?.let { HashMap(it) } if (props != null) AndroidScreeb.setVisitorProperties(props) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun resetIdentity(): Boolean? = withContext(Dispatchers.Main) { - runCatching { AndroidScreeb.resetIdentity(); true }.getOrNull() + runCatching { AndroidScreeb.resetIdentity(); true }.orNullRethrowingCancellation() } actual suspend fun getIdentity(): Map? = withContext(Dispatchers.Main) { @@ -77,7 +77,7 @@ actual object Screeb { else cont.resume(identity?.filterValues { it != null }?.mapValues { it.value!! }) } } - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun assignGroup( @@ -91,7 +91,7 @@ actual object Screeb { ScreebUtils.formatProperties(properties)?.let { HashMap(it) }, ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun unassignGroup( @@ -105,7 +105,7 @@ actual object Screeb { ScreebUtils.formatProperties(properties)?.let { HashMap(it) }, ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun trackEvent(name: String, properties: Map?): Boolean? = @@ -116,7 +116,7 @@ actual object Screeb { ScreebUtils.formatProperties(properties)?.let { HashMap(it) }, ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun trackScreen(name: String, properties: Map?): Boolean? = @@ -127,7 +127,7 @@ actual object Screeb { ScreebUtils.formatProperties(properties)?.let { HashMap(it) }, ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun startSurvey( @@ -149,11 +149,11 @@ actual object Screeb { language, distributionId, ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun closeSurvey(surveyId: String?): Boolean? = withContext(Dispatchers.Main) { - runCatching { AndroidScreeb.closeSurvey(surveyId); true }.getOrNull() + runCatching { AndroidScreeb.closeSurvey(surveyId); true }.orNullRethrowingCancellation() } actual suspend fun startMessage( @@ -175,19 +175,19 @@ actual object Screeb { language, distributionId, ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun closeMessage(messageId: String?): Boolean? = withContext(Dispatchers.Main) { - runCatching { AndroidScreeb.closeMessage(messageId); true }.getOrNull() + runCatching { AndroidScreeb.closeMessage(messageId); true }.orNullRethrowingCancellation() } actual suspend fun sessionReplayStart(): Boolean? = withContext(Dispatchers.Main) { - runCatching { AndroidScreeb.sessionReplayStart(); true }.getOrNull() + runCatching { AndroidScreeb.sessionReplayStart(); true }.orNullRethrowingCancellation() } actual suspend fun sessionReplayStop(): Boolean? = withContext(Dispatchers.Main) { - runCatching { AndroidScreeb.sessionReplayStop(); true }.getOrNull() + runCatching { AndroidScreeb.sessionReplayStop(); true }.orNullRethrowingCancellation() } actual suspend fun debug(): String? = withContext(Dispatchers.Main) { @@ -198,7 +198,7 @@ actual object Screeb { else cont.resume(info) } } - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun debugTargeting(): String? = withContext(Dispatchers.Main) { @@ -209,6 +209,10 @@ actual object Screeb { else cont.resume(info) } } - }.getOrNull() + }.orNullRethrowingCancellation() + } + + actual fun handleDeepLink(url: String?) { + AndroidScreeb.handleDeepLink(url) } } diff --git a/packages/sdk-kmp/src/commonMain/kotlin/app/screeb/sdk/kmp/Internal.kt b/packages/sdk-kmp/src/commonMain/kotlin/app/screeb/sdk/kmp/Internal.kt new file mode 100644 index 0000000..a9400fe --- /dev/null +++ b/packages/sdk-kmp/src/commonMain/kotlin/app/screeb/sdk/kmp/Internal.kt @@ -0,0 +1,17 @@ +package app.screeb.sdk.kmp + +import kotlin.coroutines.cancellation.CancellationException + +/** + * Returns the encapsulated value, or `null` when the call failed. + * + * Unlike [Result.getOrNull], a [CancellationException] is rethrown instead of + * being swallowed, so coroutine cancellation (structured concurrency) keeps + * working when a Screeb call is cancelled. + */ +internal fun Result.orNullRethrowingCancellation(): T? = + when (val error = exceptionOrNull()) { + null -> getOrNull() + is CancellationException -> throw error + else -> null + } diff --git a/packages/sdk-kmp/src/commonMain/kotlin/app/screeb/sdk/kmp/Screeb.kt b/packages/sdk-kmp/src/commonMain/kotlin/app/screeb/sdk/kmp/Screeb.kt index 6830e12..8826c7b 100644 --- a/packages/sdk-kmp/src/commonMain/kotlin/app/screeb/sdk/kmp/Screeb.kt +++ b/packages/sdk-kmp/src/commonMain/kotlin/app/screeb/sdk/kmp/Screeb.kt @@ -76,4 +76,11 @@ expect object Screeb { suspend fun debug(): String? suspend fun debugTargeting(): String? + + /** + * Forward an incoming deep link (e.g. `screeb-://inspector`) to the SDK so it can + * act on it — used by the in-app message editor / native inspector. Pass the URL of the launch + * (or new) intent. On Android, call this from your Activity's `onCreate` and `onNewIntent`. + */ + fun handleDeepLink(url: String?) } diff --git a/packages/sdk-kmp/src/commonMain/kotlin/app/screeb/sdk/kmp/SdkVersion.kt b/packages/sdk-kmp/src/commonMain/kotlin/app/screeb/sdk/kmp/SdkVersion.kt index 104d14a..ec3a5d6 100644 --- a/packages/sdk-kmp/src/commonMain/kotlin/app/screeb/sdk/kmp/SdkVersion.kt +++ b/packages/sdk-kmp/src/commonMain/kotlin/app/screeb/sdk/kmp/SdkVersion.kt @@ -1,3 +1,3 @@ package app.screeb.sdk.kmp -internal const val SDK_VERSION = "0.1.0" +internal const val SDK_VERSION = "4.0.2" diff --git a/packages/sdk-kmp/src/iosMain/kotlin/app/screeb/sdk/kmp/Screeb.ios.kt b/packages/sdk-kmp/src/iosMain/kotlin/app/screeb/sdk/kmp/Screeb.ios.kt index ec9e6cc..a23f026 100644 --- a/packages/sdk-kmp/src/iosMain/kotlin/app/screeb/sdk/kmp/Screeb.ios.kt +++ b/packages/sdk-kmp/src/iosMain/kotlin/app/screeb/sdk/kmp/Screeb.ios.kt @@ -48,7 +48,7 @@ actual object Screeb { language = language, ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun closeSdk(): Boolean? = withContext(Dispatchers.Main) { @@ -57,7 +57,7 @@ actual object Screeb { HooksScope.reset() NativeScreeb.closeSdk() true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun setIdentity( @@ -70,7 +70,7 @@ actual object Screeb { visitorProperty = properties.toObjCMap(), ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun setProperties(properties: Map?): Boolean? = @@ -78,11 +78,11 @@ actual object Screeb { runCatching { NativeScreeb.visitorProperty(properties.toObjCMap()) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun resetIdentity(): Boolean? = withContext(Dispatchers.Main) { - runCatching { NativeScreeb.resetIdentity(); true }.getOrNull() + runCatching { NativeScreeb.resetIdentity(); true }.orNullRethrowingCancellation() } actual suspend fun getIdentity(): Map? = withContext(Dispatchers.Main) { @@ -93,7 +93,7 @@ actual object Screeb { else cont.resume((identity as Map?).toKotlinMap()) } } - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun assignGroup( @@ -108,7 +108,7 @@ actual object Screeb { properties = properties.toObjCMap(), ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun unassignGroup( @@ -123,7 +123,7 @@ actual object Screeb { properties = properties.toObjCMap(), ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun trackEvent(name: String, properties: Map?): Boolean? = @@ -134,7 +134,7 @@ actual object Screeb { trackingEventProperties = properties.toObjCMap(), ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun trackScreen(name: String, properties: Map?): Boolean? = @@ -145,7 +145,7 @@ actual object Screeb { trackingEventProperties = properties.toObjCMap(), ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun startSurvey( @@ -169,11 +169,11 @@ actual object Screeb { distributionId = distributionId, ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun closeSurvey(surveyId: String?): Boolean? = withContext(Dispatchers.Main) { - runCatching { NativeScreeb.closeSurvey(surveyId); true }.getOrNull() + runCatching { NativeScreeb.closeSurvey(surveyId); true }.orNullRethrowingCancellation() } actual suspend fun startMessage( @@ -197,19 +197,19 @@ actual object Screeb { distributionId = distributionId, ) true - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun closeMessage(messageId: String?): Boolean? = withContext(Dispatchers.Main) { - runCatching { NativeScreeb.closeMessage(messageId); true }.getOrNull() + runCatching { NativeScreeb.closeMessage(messageId); true }.orNullRethrowingCancellation() } actual suspend fun sessionReplayStart(): Boolean? = withContext(Dispatchers.Main) { - runCatching { NativeScreeb.sessionReplayStart(); true }.getOrNull() + runCatching { NativeScreeb.sessionReplayStart(); true }.orNullRethrowingCancellation() } actual suspend fun sessionReplayStop(): Boolean? = withContext(Dispatchers.Main) { - runCatching { NativeScreeb.sessionReplayStop(); true }.getOrNull() + runCatching { NativeScreeb.sessionReplayStop(); true }.orNullRethrowingCancellation() } actual suspend fun debug(): String? = withContext(Dispatchers.Main) { @@ -220,7 +220,7 @@ actual object Screeb { else cont.resume(info) } } - }.getOrNull() + }.orNullRethrowingCancellation() } actual suspend fun debugTargeting(): String? = withContext(Dispatchers.Main) { @@ -231,6 +231,13 @@ actual object Screeb { else cont.resume(info) } } - }.getOrNull() + }.orNullRethrowingCancellation() + } + + // Forward to the native SDK (editor/survey/message links open in-app). Call it + // from your AppDelegate's open-URL entry point or a shared linking listener. + actual fun handleDeepLink(url: String?) { + if (url == null) return + NativeScreeb.handleDeepLinkWithUrl(platform.Foundation.NSURL.URLWithString(url)) } } diff --git a/packages/sdk-maui/Platforms/Android/HooksAndroid.cs b/packages/sdk-maui/Platforms/Android/HooksAndroid.cs index ca25138..ac7483e 100644 --- a/packages/sdk-maui/Platforms/Android/HooksAndroid.cs +++ b/packages/sdk-maui/Platforms/Android/HooksAndroid.cs @@ -13,48 +13,74 @@ internal static class HooksAndroid private static IDictionary? BuildHooksMap(Dictionary uuidMap) { if (uuidMap.Count == 0) return null; - var map = new Dictionary(); - foreach (var (key, value) in uuidMap) + return App.Screeb.Sdk.Screeb.Instance.MakeHooks(uuidMap, new ScreebHookCallbackAdapter()); + } + + internal static Java.Lang.Object? ToJavaObject(object? value) + { + return value switch { - if (key == "version") - { - map[key] = new Java.Lang.String(value); - } - else - { - var uuid = value; // capture - map[key] = new KotlinHookCallback(payload => - { - var fn = HooksRegistry.Get(uuid); - if (fn != null) - { - Task.Run(() => fn(payload?.ToString() ?? "{}")); - } - }); - } + null => null, + Java.Lang.Object javaObject => javaObject, + string s => new Java.Lang.String(s), + bool b => Java.Lang.Boolean.ValueOf(b), + int i => Java.Lang.Integer.ValueOf(i), + long l => Java.Lang.Long.ValueOf(l), + float f => Java.Lang.Float.ValueOf(f), + double d => Java.Lang.Double.ValueOf(d), + IDictionary dict => ToJavaDictionary(dict), + IEnumerable list => ToJavaList(list), + _ => new Java.Lang.String(value.ToString() ?? "") + }; + } + + private static Java.Util.HashMap ToJavaDictionary(IDictionary dict) + { + var map = new Java.Util.HashMap(); + foreach (var (key, value) in dict) + { + map.Put(new Java.Lang.String(key), ToJavaObject(value)); } return map; } -} - -/// -/// Wraps a C# Action as a Kotlin Function1<Object, Unit> so the -/// Android Screeb SDK can call it as a hook callback. -/// -[Android.Runtime.Register("app/screeb/maui/KotlinHookCallback")] -internal class KotlinHookCallback : Java.Lang.Object, Kotlin.Jvm.Functions.IFunction1 -{ - private readonly Action _onHook; - public KotlinHookCallback(Action onHook) + private static Java.Util.ArrayList ToJavaList(IEnumerable list) { - _onHook = onHook; + var array = new Java.Util.ArrayList(); + foreach (var item in list) + { + var javaItem = ToJavaObject(item); + if (javaItem != null) array.Add(javaItem); + } + return array; } +} - public Java.Lang.Object? Invoke(Java.Lang.Object? p0) +internal sealed class ScreebHookCallbackAdapter : Java.Lang.Object, App.Screeb.Sdk.IScreebHookCallback +{ + public void Invoke(string hookId, string nativeHookId, string payload) { - _onHook(p0); - return null; // Kotlin Unit → null + var fn = HooksRegistry.Get(hookId); + if (fn == null) return; + + Task.Run(async () => + { + try + { + var callbackResult = await fn(payload).ConfigureAwait(false); + if (!string.IsNullOrWhiteSpace(nativeHookId)) + { + App.Screeb.Sdk.Screeb.Instance.OnHookResult(nativeHookId, HooksAndroid.ToJavaObject(callbackResult)); + } + } + catch + { + if (!string.IsNullOrWhiteSpace(nativeHookId)) + { + App.Screeb.Sdk.Screeb.Instance.OnHookResult(nativeHookId, Java.Lang.Boolean.ValueOf(false)); + } + } + }); } } diff --git a/packages/sdk-maui/Platforms/Android/Screeb.Android.cs b/packages/sdk-maui/Platforms/Android/Screeb.Android.cs index f6877b1..32e7969 100644 --- a/packages/sdk-maui/Platforms/Android/Screeb.Android.cs +++ b/packages/sdk-maui/Platforms/Android/Screeb.Android.cs @@ -21,8 +21,22 @@ public static partial class Screeb return tcs.Task; } - private static Task OnMain(Action body) => - OnMain(tcs => { body(); tcs.SetResult(true); }); + // Fire-and-forget calls return true on success and false on failure (never + // faults the task, so an unobserved fire-and-forget call can't crash the app). + private static Task OnMain(Action body) + { + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + _mainHandler.Post(() => + { + try { body(); tcs.SetResult(true); } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[Screeb] call failed: {ex}"); + tcs.SetResult(false); + } + }); + return tcs.Task; + } private static Task OnMain(Action> body) where T : class { @@ -56,6 +70,9 @@ public static partial class Screeb App.Screeb.Sdk.Screeb.Instance.CloseSdk(); }); + public static partial Task HandleDeepLink(string url) + => OnMain(() => App.Screeb.Sdk.Screeb.Instance.HandleDeepLink(Android.Net.Uri.Parse(url))); + public static partial Task SetIdentity(string userId, Dictionary? properties) => OnMain(() => App.Screeb.Sdk.Screeb.Instance.SetIdentity( userId, ToJavaDictionary(ScreebUtils.FormatProperties(properties)))); @@ -149,12 +166,29 @@ public static partial class Screeb if (dict == null) return null; var map = new Dictionary(); foreach (var (k, v) in dict) - map[k] = v is string s ? new Java.Lang.String(s) : - v is bool b ? Java.Lang.Boolean.ValueOf(b)! : - v is int i ? Java.Lang.Integer.ValueOf(i)! : - v is long l ? Java.Lang.Long.ValueOf(l)! : - v is double d ? Java.Lang.Double.ValueOf(d)! : - new Java.Lang.String(v.ToString() ?? ""); + map[k] = ToJavaObject(v); + return map; + } + + private static Java.Lang.Object ToJavaObject(object value) + { + if (value is string s) return new Java.Lang.String(s); + if (value is bool b) return Java.Lang.Boolean.ValueOf(b)!; + if (value is int i) return Java.Lang.Integer.ValueOf(i)!; + if (value is long l) return Java.Lang.Long.ValueOf(l)!; + if (value is float f) return Java.Lang.Float.ValueOf(f)!; + if (value is double d) return Java.Lang.Double.ValueOf(d)!; + if (value is Java.Lang.Object javaObject) return javaObject; + if (value is Dictionary nested) return ToJavaHashMap(nested); + if (value is IDictionary nestedDict) return ToJavaHashMap(new Dictionary(nestedDict)); + return new Java.Lang.String(value.ToString() ?? ""); + } + + private static Java.Util.HashMap ToJavaHashMap(Dictionary dict) + { + var map = new Java.Util.HashMap(); + foreach (var (key, value) in dict) + map.Put(new Java.Lang.String(key), ToJavaObject(value)); return map; } diff --git a/packages/sdk-maui/Platforms/Android/ScreebView.Android.cs b/packages/sdk-maui/Platforms/Android/ScreebView.Android.cs new file mode 100644 index 0000000..d3dd22e --- /dev/null +++ b/packages/sdk-maui/Platforms/Android/ScreebView.Android.cs @@ -0,0 +1,54 @@ +#if ANDROID +using Android.Views; +using Microsoft.Maui.Controls; +using AndroidView = Android.Views.View; + +namespace Screeb.Maui; + +internal static partial class ScreebViewPlatform +{ + internal static partial void ApplyId(VisualElement element, string id) => + Apply(element, view => SetTag(view, "screeb_id", new Java.Lang.String(id))); + + internal static partial void ApplyMaskText(VisualElement element) => + Apply(element, view => SetTag(view, "screeb_sensitive_tag", Java.Lang.Boolean.True!)); + + internal static partial void ApplyNoCapture(VisualElement element) => + Apply(element, view => SetTag(view, "screeb_no_capture_tag", Java.Lang.Boolean.True!)); + + private static void Apply(VisualElement element, Action action) + { + if (element.Handler?.PlatformView is AndroidView view) + { + action(view); + return; + } + + void OnHandlerChanged(object? sender, EventArgs args) + { + element.HandlerChanged -= OnHandlerChanged; + if (element.Handler?.PlatformView is AndroidView attachedView) + { + action(attachedView); + } + } + + element.HandlerChanged += OnHandlerChanged; + } + + private static void SetTag(AndroidView view, string resourceName, Java.Lang.Object value) + { + var id = view.Resources?.GetIdentifier(resourceName, "id", view.Context?.PackageName) ?? 0; + if (id == 0) + { + // Fail safe: a privacy/targeting marker must never crash the host app. + // The id is missing when the native Screeb resources aren't merged + // (native SDK absent, resource shrinking, wrong package). + System.Diagnostics.Debug.WriteLine( + $"[Screeb] Unable to resolve Android resource id '{resourceName}'; skipping marker."); + return; + } + view.SetTag(id, value); + } +} +#endif diff --git a/packages/sdk-maui/Platforms/iOS/ApiDefinitions.cs b/packages/sdk-maui/Platforms/iOS/ApiDefinitions.cs index 7fcee24..40c4f5d 100644 --- a/packages/sdk-maui/Platforms/iOS/ApiDefinitions.cs +++ b/packages/sdk-maui/Platforms/iOS/ApiDefinitions.cs @@ -1,4 +1,6 @@ // packages/sdk-maui/Platforms/iOS/ApiDefinitions.cs +#nullable enable + using System; using Foundation; using UIKit; @@ -30,6 +32,14 @@ void InitSdk( [Export("closeSdk")] void CloseSdk(); + [Static] + [Export("handleDeepLinkWithUrl:")] + void HandleDeepLink([NullAllowed] NSUrl url); + + [Static] + [Export("makeHooks:callback:")] + NSDictionary MakeHooks(NSDictionary hookIds, Action callback); + [Static] [Export("setIdentity:visitorProperty:")] void SetIdentity(string uniqueVisitorId, NSDictionary visitorProperty); @@ -92,6 +102,10 @@ void StartMessage( [Export("closeMessage:")] void CloseMessage([NullAllowed] string messageId); + [Static] + [Export("onHookResult:result:")] + void OnHookResult(string hook, [NullAllowed] NSObject result); + [Static] [Export("sessionReplayStart")] void SessionReplayStart(); @@ -109,6 +123,24 @@ void StartMessage( void DebugTargeting(Action completion); } +[Static] +[DisableDefaultCtor] +[BaseType(typeof(NSObject))] +interface ScreebView +{ + [Static] + [Export("screebId:id:")] + void ScreebId(UIView view, string id); + + [Static] + [Export("screebMaskText:")] + void ScreebMaskText(UIView view); + + [Static] + [Export("screebNoCapture:")] + void ScreebNoCapture(UIView view); +} + [BaseType(typeof(NSObject))] interface InitOptions { diff --git a/packages/sdk-maui/Platforms/iOS/HooksIOS.cs b/packages/sdk-maui/Platforms/iOS/HooksIOS.cs index 2cf3494..7e54e35 100644 --- a/packages/sdk-maui/Platforms/iOS/HooksIOS.cs +++ b/packages/sdk-maui/Platforms/iOS/HooksIOS.cs @@ -1,16 +1,11 @@ #if IOS using Foundation; +using NativeScreeb = Screeb.iOS.Binding.Screeb; namespace Screeb.Maui; internal static class HooksIOS { - /// - /// Converts hook UUIDs to NSDictionary for passing to native iOS SDK. - /// NOTE: ObjC block-based hook callbacks are not yet supported in v0.1.0. - /// The "version" key is passed as a string; other hook keys are registered - /// in HooksRegistry but callbacks will not fire from the native iOS side. - /// internal static NSDictionary? ToNSDictionary(Dictionary uuidMap) { if (uuidMap.Count == 0) return null; @@ -22,6 +17,57 @@ internal static class HooksIOS keys.Add(new NSString(key)); values.Add(new NSString(value)); } + + var hookIds = NSDictionary.FromObjectsAndKeys(values.ToArray(), keys.ToArray()); + return NativeScreeb.MakeHooks(hookIds, (wrapperHookId, nativeHookId, payload) => + { + var fn = HooksRegistry.Get(wrapperHookId); + if (fn == null || string.IsNullOrWhiteSpace(nativeHookId)) return; + + Task.Run(async () => + { + try + { + var callbackResult = await fn(payload).ConfigureAwait(false); + NativeScreeb.OnHookResult(nativeHookId, ToNSObject(callbackResult)); + } + catch + { + NativeScreeb.OnHookResult(nativeHookId, NSNumber.FromBoolean(false)); + } + }); + }); + } + + private static NSObject? ToNSObject(object? value) + { + return value switch + { + null => null, + NSObject nsObject => nsObject, + string s => new NSString(s), + bool b => NSNumber.FromBoolean(b), + int i => NSNumber.FromInt32(i), + long l => NSNumber.FromInt64(l), + float f => NSNumber.FromFloat(f), + double d => NSNumber.FromDouble(d), + IDictionary dict => ToNSDictionary(dict), + IEnumerable list => NSArray.FromNSObjects(list.Select(ToNSObject).Where(item => item != null).ToArray()!), + _ => new NSString(value.ToString() ?? "") + }; + } + + private static NSDictionary ToNSDictionary(IDictionary dict) + { + var keys = new List(); + var values = new List(); + foreach (var (key, value) in dict) + { + var nsValue = ToNSObject(value); + if (nsValue == null) continue; + keys.Add(new NSString(key)); + values.Add(nsValue); + } return NSDictionary.FromObjectsAndKeys(values.ToArray(), keys.ToArray()); } } diff --git a/packages/sdk-maui/Platforms/iOS/Screeb.iOS.cs b/packages/sdk-maui/Platforms/iOS/Screeb.iOS.cs index 7407e6f..300ce47 100644 --- a/packages/sdk-maui/Platforms/iOS/Screeb.iOS.cs +++ b/packages/sdk-maui/Platforms/iOS/Screeb.iOS.cs @@ -19,8 +19,22 @@ public static partial class Screeb return tcs.Task; } - private static Task OnMain(Action body) => - OnMain(tcs => { body(); tcs.SetResult(true); }); + // Fire-and-forget calls return true on success and false on failure (never + // faults the task, so an unobserved fire-and-forget call can't crash the app). + private static Task OnMain(Action body) + { + var tcs = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); + NSRunLoop.Main.BeginInvokeOnMainThread(() => + { + try { body(); tcs.SetResult(true); } + catch (Exception ex) + { + System.Diagnostics.Debug.WriteLine($"[Screeb] call failed: {ex}"); + tcs.SetResult(false); + } + }); + return tcs.Task; + } private static Task OnMain(Action> body) where T : class { @@ -51,6 +65,9 @@ public static partial class Screeb public static partial Task CloseSdk() => OnMain(() => { HooksRegistry.UnregisterAll(); NativeScreeb.CloseSdk(); }); + public static partial Task HandleDeepLink(string url) + => OnMain(() => NativeScreeb.HandleDeepLink(new NSUrl(url))); + public static partial Task SetIdentity(string userId, Dictionary? properties) => OnMain(() => NativeScreeb.SetIdentity( userId, ToNSDictionary(ScreebUtils.FormatProperties(properties)) ?? new NSDictionary())); @@ -144,16 +161,25 @@ public static partial class Screeb foreach (var (k, v) in dict) { keys.Add(new NSString(k)); - values.Add(v is string s ? new NSString(s) : - v is bool b ? NSNumber.FromBoolean(b) : - v is int i ? NSNumber.FromInt32(i) : - v is long l ? NSNumber.FromInt64(l) : - v is double d ? NSNumber.FromDouble(d) : - (NSObject)new NSString(v.ToString() ?? "")); + values.Add(ToNSObject(v)); } return NSDictionary.FromObjectsAndKeys(values.ToArray(), keys.ToArray()); } + private static NSObject ToNSObject(object value) + { + if (value is NSObject nsObject) return nsObject; + if (value is string s) return new NSString(s); + if (value is bool b) return NSNumber.FromBoolean(b); + if (value is int i) return NSNumber.FromInt32(i); + if (value is long l) return NSNumber.FromInt64(l); + if (value is float f) return NSNumber.FromFloat(f); + if (value is double d) return NSNumber.FromDouble(d); + if (value is Dictionary nested) return ToNSDictionary(nested) ?? new NSDictionary(); + if (value is IDictionary nestedDict) return ToNSDictionary(new Dictionary(nestedDict)) ?? new NSDictionary(); + return new NSString(value.ToString() ?? ""); + } + private static Dictionary? FromNSDictionary(NSDictionary? dict) { if (dict == null) return null; diff --git a/packages/sdk-maui/Platforms/iOS/ScreebClassPtr.cs b/packages/sdk-maui/Platforms/iOS/ScreebClassPtr.cs index 0165514..d0f0317 100644 --- a/packages/sdk-maui/Platforms/iOS/ScreebClassPtr.cs +++ b/packages/sdk-maui/Platforms/iOS/ScreebClassPtr.cs @@ -10,4 +10,9 @@ public static unsafe partial class Screeb { static readonly NativeHandle class_ptr = Class.GetHandle("Screeb"); } + + public static unsafe partial class ScreebView + { + static readonly NativeHandle class_ptr = Class.GetHandle("ScreebView"); + } } diff --git a/packages/sdk-maui/Platforms/iOS/ScreebView.iOS.cs b/packages/sdk-maui/Platforms/iOS/ScreebView.iOS.cs new file mode 100644 index 0000000..e4173cb --- /dev/null +++ b/packages/sdk-maui/Platforms/iOS/ScreebView.iOS.cs @@ -0,0 +1,39 @@ +#if IOS +using Microsoft.Maui.Controls; +using NativeScreebView = Screeb.iOS.Binding.ScreebView; +using IosView = UIKit.UIView; + +namespace Screeb.Maui; + +internal static partial class ScreebViewPlatform +{ + internal static partial void ApplyId(VisualElement element, string id) => + Apply(element, view => NativeScreebView.ScreebId(view, id)); + + internal static partial void ApplyMaskText(VisualElement element) => + Apply(element, NativeScreebView.ScreebMaskText); + + internal static partial void ApplyNoCapture(VisualElement element) => + Apply(element, NativeScreebView.ScreebNoCapture); + + private static void Apply(VisualElement element, Action action) + { + if (element.Handler?.PlatformView is IosView view) + { + action(view); + return; + } + + void OnHandlerChanged(object? sender, EventArgs args) + { + element.HandlerChanged -= OnHandlerChanged; + if (element.Handler?.PlatformView is IosView attachedView) + { + action(attachedView); + } + } + + element.HandlerChanged += OnHandlerChanged; + } +} +#endif diff --git a/packages/sdk-maui/README.md b/packages/sdk-maui/README.md index b039f53..abb2e00 100644 --- a/packages/sdk-maui/README.md +++ b/packages/sdk-maui/README.md @@ -76,7 +76,7 @@ public partial class App : Application channelId: "", userId: "user-123", properties: new Dictionary { ["plan"] = "premium" }, - initOptions: new ScreebInitOptions { IsDebugMode = true } + initOptions: new ScreebInitOptions { IsDebugMode = false } ); } } diff --git a/packages/sdk-maui/Screeb.cs b/packages/sdk-maui/Screeb.cs index 66477a4..167cb15 100644 --- a/packages/sdk-maui/Screeb.cs +++ b/packages/sdk-maui/Screeb.cs @@ -37,6 +37,12 @@ public static partial class Screeb /// true if closure succeeded, false if it failed or SDK not initialized, null if unsupported. public static partial Task CloseSdk(); + /// Forward a Screeb deep link (screeb-<channel-id> scheme) to the SDK — + /// editor, survey and message links open in-app. Call from your platform's + /// open-URL entry point (iOS OpenUrl / Android OnNewIntent). + /// true if the link was forwarded, false on failure, null if unsupported. + public static partial Task HandleDeepLink(string url); + /// Identify the current user with optional properties. /// Visitor identifier. /// User properties. Supports string, int, double, bool, DateTime, and nested Dictionary values. Pass null for no properties. diff --git a/packages/sdk-maui/ScreebMaui.csproj b/packages/sdk-maui/ScreebMaui.csproj index d31a0c2..3375091 100644 --- a/packages/sdk-maui/ScreebMaui.csproj +++ b/packages/sdk-maui/ScreebMaui.csproj @@ -6,9 +6,10 @@ enable enable true + true Screeb.Maui - 0.1.0 + 4.0.2 Screeb Screeb Screeb Continuous Product Discovery SDK for .NET MAUI (Android + iOS) @@ -20,6 +21,10 @@ git + + + + @@ -31,6 +36,7 @@ + @@ -45,17 +51,42 @@ + + + + + + + + + + + + + + + + + + diff --git a/packages/sdk-maui/ScreebUtils.cs b/packages/sdk-maui/ScreebUtils.cs index 4382e85..d998d6b 100644 --- a/packages/sdk-maui/ScreebUtils.cs +++ b/packages/sdk-maui/ScreebUtils.cs @@ -8,6 +8,9 @@ public static class ScreebUtils /// Mirrors Flutter's _formatDates and React Native's normalizeValue. /// public static Dictionary? FormatProperties(Dictionary? props) + => FormatNativeProperties(props); + + public static Dictionary? FormatNativeProperties(Dictionary? props) { if (props == null) return null; var result = new Dictionary(props.Count); @@ -20,7 +23,9 @@ public static class ScreebUtils { DateTimeOffset dto => FormatDateTimeOffset(dto), DateTime dt => FormatDateTimeOffset(new DateTimeOffset(dt)), - Dictionary nested => FormatProperties(nested)!, + Dictionary nested => FormatNativeProperties(nested)!, + IDictionary nested => FormatNativeProperties(new Dictionary(nested))!, + IEnumerable values => values.Select(FormatValue).ToArray(), _ => value }; diff --git a/packages/sdk-maui/ScreebViewExtensions.cs b/packages/sdk-maui/ScreebViewExtensions.cs new file mode 100644 index 0000000..ce6cab9 --- /dev/null +++ b/packages/sdk-maui/ScreebViewExtensions.cs @@ -0,0 +1,35 @@ +using Microsoft.Maui.Controls; + +namespace Screeb.Maui; + +public static class ScreebViewExtensions +{ + public static T ScreebId(this T element, string id) where T : VisualElement + { + ArgumentNullException.ThrowIfNull(element); + ArgumentException.ThrowIfNullOrWhiteSpace(id); + ScreebViewPlatform.ApplyId(element, id); + return element; + } + + public static T ScreebMaskText(this T element) where T : VisualElement + { + ArgumentNullException.ThrowIfNull(element); + ScreebViewPlatform.ApplyMaskText(element); + return element; + } + + public static T ScreebNoCapture(this T element) where T : VisualElement + { + ArgumentNullException.ThrowIfNull(element); + ScreebViewPlatform.ApplyNoCapture(element); + return element; + } +} + +internal static partial class ScreebViewPlatform +{ + internal static partial void ApplyId(VisualElement element, string id); + internal static partial void ApplyMaskText(VisualElement element); + internal static partial void ApplyNoCapture(VisualElement element); +} diff --git a/packages/sdk-maui/Transforms/AndroidMetadata.xml b/packages/sdk-maui/Transforms/AndroidMetadata.xml new file mode 100644 index 0000000..08bfc9b --- /dev/null +++ b/packages/sdk-maui/Transforms/AndroidMetadata.xml @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/sdk-maui/native/ios/LICENSE b/packages/sdk-maui/native/ios/LICENSE new file mode 100644 index 0000000..13479a1 --- /dev/null +++ b/packages/sdk-maui/native/ios/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2026 Screeb, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + diff --git a/packages/sdk-maui/readme/screeb-logo.svg b/packages/sdk-maui/readme/screeb-logo.svg new file mode 100644 index 0000000..b74bd6e --- /dev/null +++ b/packages/sdk-maui/readme/screeb-logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/packages/sdk-maui/tests/ScreebHooksTests.cs b/packages/sdk-maui/tests/ScreebHooksTests.cs new file mode 100644 index 0000000..c66799e --- /dev/null +++ b/packages/sdk-maui/tests/ScreebHooksTests.cs @@ -0,0 +1,67 @@ +using Screeb.Maui; +using Xunit; + +namespace Screeb.Maui.Tests; + +public class ScreebHooksTests +{ + [Fact] + public void RegisterHooks_NullHooks_ReturnsEmptyMap() + { + HooksRegistry.UnregisterAll(); + + var result = HooksRegistry.RegisterHooks(null); + + Assert.Empty(result); + } + + [Fact] + public async Task RegisteredHook_ForwardsPayloadAndReturnsResult() + { + HooksRegistry.UnregisterAll(); + string? receivedPayload = null; + var hooks = new ScreebHooks + { + Version = "1.0", + Callbacks = + { + ["onSurveyShowed"] = payload => + { + receivedPayload = payload; + return Task.FromResult(true); + } + } + }; + + var uuidMap = HooksRegistry.RegisterHooks(hooks); + var uuid = uuidMap["onSurveyShowed"]; + var callback = HooksRegistry.Get(uuid); + + Assert.Equal("1.0", uuidMap["version"]); + Assert.NotNull(callback); + Assert.EndsWith("_onSurveyShowed", uuid); + Assert.True((bool)(await callback!(@"{""hook_id"":""native-hook""}")!)!); + Assert.Equal(@"{""hook_id"":""native-hook""}", receivedPayload); + } + + [Fact] + public void Unregister_RemovesOnlyRequestedHooks() + { + HooksRegistry.UnregisterAll(); + var hooks = new ScreebHooks + { + Version = "1.0", + Callbacks = + { + ["first"] = _ => Task.FromResult(true), + ["second"] = _ => Task.FromResult(false) + } + }; + var uuidMap = HooksRegistry.RegisterHooks(hooks); + + HooksRegistry.Unregister(new[] { uuidMap["first"] }); + + Assert.Null(HooksRegistry.Get(uuidMap["first"])); + Assert.NotNull(HooksRegistry.Get(uuidMap["second"])); + } +} diff --git a/packages/sdk-maui/tests/ScreebUtilsTests.cs b/packages/sdk-maui/tests/ScreebUtilsTests.cs index 0cf30ef..0ee3950 100644 --- a/packages/sdk-maui/tests/ScreebUtilsTests.cs +++ b/packages/sdk-maui/tests/ScreebUtilsTests.cs @@ -43,6 +43,25 @@ public void FormatProperties_NestedDictionary_RecursivelyFormats() Assert.Equal("2024-06-01T00:00:00.000+00:00", nested["at"]); } + [Fact] + public void FormatNativeProperties_NestedDictionary_RemainsDictionary() + { + var input = new Dictionary + { + ["meta"] = new Dictionary + { + ["plan"] = "pro", + ["at"] = new DateTimeOffset(2024, 6, 1, 0, 0, 0, TimeSpan.Zero) + } + }; + + var result = ScreebUtils.FormatNativeProperties(input)!; + + var nested = Assert.IsType>(result["meta"]); + Assert.Equal("pro", nested["plan"]); + Assert.Equal("2024-06-01T00:00:00.000+00:00", nested["at"]); + } + [Fact] public void FormatProperties_DateTimeUtcValue_ConvertsToIso8601WithZeroOffset() { diff --git a/packages/sdk-maui/tests/ScreebUtilsTests.csproj b/packages/sdk-maui/tests/ScreebUtilsTests.csproj index 2125b43..77bfbcc 100644 --- a/packages/sdk-maui/tests/ScreebUtilsTests.csproj +++ b/packages/sdk-maui/tests/ScreebUtilsTests.csproj @@ -12,7 +12,8 @@ runtime; build; native; contentfiles; analyzers all - + + diff --git a/packages/sdk-react/README.md b/packages/sdk-react/README.md index 70d74ca..4472e7d 100644 --- a/packages/sdk-react/README.md +++ b/packages/sdk-react/README.md @@ -122,7 +122,7 @@ For a more advanced usage and a complete API documentation, see [documentation g For further information, see [our developer documentation](https://developers.screeb.app/sdk-js/install). ## Support -For any issues, please contact our support team at support@screeb.com. +For any issues, please contact our support team at support@screeb.app. ## Contributing All third party contributors acknowledge that any contributions they provide will be made under the same open source license that the open source project is provided under. diff --git a/packages/sdk-react/docs/.nojekyll b/packages/sdk-react/docs/.nojekyll deleted file mode 100644 index e2ac661..0000000 --- a/packages/sdk-react/docs/.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/packages/sdk-react/docs/README.md b/packages/sdk-react/docs/README.md deleted file mode 100644 index 1af3301..0000000 --- a/packages/sdk-react/docs/README.md +++ /dev/null @@ -1,750 +0,0 @@ -@screeb/sdk-react - -# @screeb/sdk-react - -## Table of contents - -### Type Aliases - -- [CloseFunction](README.md#closefunction) -- [DebugFunction](README.md#debugfunction) -- [EventTrackFunction](README.md#eventtrackfunction) -- [IdentityFunction](README.md#identityfunction) -- [IdentityGetFunction](README.md#identitygetfunction) -- [IdentityGroupAssignFunction](README.md#identitygroupassignfunction) -- [IdentityGroupUnassignFunction](README.md#identitygroupunassignfunction) -- [IdentityPropertiesFunction](README.md#identitypropertiesfunction) -- [IdentityResetFunction](README.md#identityresetfunction) -- [InitFunction](README.md#initfunction) -- [LoadFunction](README.md#loadfunction) -- [MessageCloseFunction](README.md#messageclosefunction) -- [MessageStartFunction](README.md#messagestartfunction) -- [ScreebContextValues](README.md#screebcontextvalues) -- [ScreebProps](README.md#screebprops) -- [ScreebProviderProps](README.md#screebproviderprops) -- [SessionReplayStartFunction](README.md#sessionreplaystartfunction) -- [SessionReplayStopFunction](README.md#sessionreplaystopfunction) -- [SurveyCloseFunction](README.md#surveyclosefunction) -- [SurveyStartFunction](README.md#surveystartfunction) -- [TargetingDebugFunction](README.md#targetingdebugfunction) - -### Functions - -- [ScreebProvider](README.md#screebprovider) -- [useScreeb](README.md#usescreeb) - -## Type Aliases - -### CloseFunction - -Ƭ **CloseFunction**: () => `Promise`\<`void`\> - -Shutdowns current Screeb session. - -**`Example`** - -```ts -const { close } = useScreeb(); - -close(); -``` - -#### Type declaration - -▸ (): `Promise`\<`void`\> - -##### Returns - -`Promise`\<`void`\> - -___ - -### DebugFunction - -Ƭ **DebugFunction**: () => `Promise`\<`unknown`\> - -Prints the actual state information of Screeb tag. - -**`Example`** - -```ts -const { debug } = useScreeb(); - -debug(); -// ******************* SCREEB SESSION DEBUG ********************* -// Screeb channel id: -// Screeb channel type: widget -// Screeb respondent id: -// Screeb survey id: none -// Screeb response id: none -// -// Screeb current session start: Thu May 04 2023 16:53:49 GMT+0200 (Central European Summer Time) -// Screeb current session last activity: Thu May 04 2023 17:41:30 GMT+0200 (Central European Summer Time) -// -// Screeb targeting engine status: disabled -// Screeb targeting engine: 3 surveys -// -// Detected platform: desktop -// Detected locale: en-GB -// Detected timezone: -120 -// ************************************************************** -``` - -#### Type declaration - -▸ (): `Promise`\<`unknown`\> - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### EventTrackFunction - -Ƭ **EventTrackFunction**: (`eventName`: `string`, `eventProperties?`: `PropertyRecord`) => `Promise`\<`unknown`\> - -Tracks a user event. - -**`Example`** - -```ts -const { eventTrack } = useScreeb(); - -eventTrack( - "Product added to cart", - { - product_name: 'Red bike 2021', - category: 'sport', - color: 'red', - price: 299, - count: 1, - reference: '2CF093TG1', - delivery_method: 'UPS', - user_logged: false, - added_at: new Date(), - } -); -``` - -#### Type declaration - -▸ (`eventName`, `eventProperties?`): `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `eventName` | `string` | The event name. | -| `eventProperties?` | `PropertyRecord` | The properties of your event. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date. ``` | - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### IdentityFunction - -Ƭ **IdentityFunction**: (`userId`: `string`, `userProperties?`: `PropertyRecord`) => `Promise`\<`unknown`\> - -Change the current user identity. -Warning: Running surveys will be closed. - -**`Example`** - -```ts -const { identity } = useScreeb(); - -identity( - "", - { - firstname: '', - lastname: '', - plan: '', - last_seen_at: new Date(), - authenticated: true - } -); -``` - -#### Type declaration - -▸ (`userId`, `userProperties?`): `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `userId` | `string` | The unique identifier of your user. | -| `userProperties?` | `PropertyRecord` | The properties of your user. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date. ``` | - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### IdentityGetFunction - -Ƭ **IdentityGetFunction**: () => `Promise`\<`ScreebIdentityGetReturn`\> - -Retrieves the current user identity. - -**`Example`** - -```ts -const { identityGet } = useScreeb(); - -console.log(await identityGet()); -// { -// anonymous_id: "", -// user_id: "", -// session_id: "", -// session_start: "2023-05-04T16:30:15.882Z", -// session_end: "2023-05-04T17:02:09.087Z", -// channel_id: "", -// is_ready: true, -// } -``` - -#### Type declaration - -▸ (): `Promise`\<`ScreebIdentityGetReturn`\> - -##### Returns - -`Promise`\<`ScreebIdentityGetReturn`\> - -___ - -### IdentityGroupAssignFunction - -Ƭ **IdentityGroupAssignFunction**: (`groupName`: `string`, `groupType?`: `string`, `groupProperties?`: `PropertyRecord`) => `Promise`\<`unknown`\> - -Assigns the current user to a group. - -**`Example`** - -```ts -const { identityGroupAssign } = useScreeb(); - -identityGroupAssign( - 'company', - 'Apple', - { - address_line_1: 'Apple Campus', - address_line_2: '1 Infinite Loop', - city: 'Cupertino', - zipcode: 95014, - state: 'California', - country: 'United states', - } -); -``` - -#### Type declaration - -▸ (`groupName`, `groupType?`, `groupProperties?`): `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `groupName` | `string` | | -| `groupType?` | `string` | | -| `groupProperties?` | `PropertyRecord` | The properties of your user group. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date. ``` | - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### IdentityGroupUnassignFunction - -Ƭ **IdentityGroupUnassignFunction**: (`groupName`: `string`, `groupType?`: `string`) => `Promise`\<`unknown`\> - -Unassigns the current user to a group. - -**`Example`** - -```ts -const { identityGroupUnassign } = useScreeb(); - -identityGroupUnassign('company', 'Apple'); -``` - -#### Type declaration - -▸ (`groupName`, `groupType?`): `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `groupName` | `string` | The name of your user group. | -| `groupType?` | `string` | The type of your user group. | - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### IdentityPropertiesFunction - -Ƭ **IdentityPropertiesFunction**: (`userProperties`: `PropertyRecord`) => `Promise`\<`unknown`\> - -Adds properties to the current user identity. - -**`Example`** - -```ts -const { identityProperties } = useScreeb(); - -// Set user properties -identityProperties( - { - firstname: '', - lastname: '', - plan: '', - last_seen_at: new Date(), - authenticated: true - } -); - -// Delete user property : set values to null -identityProperties( - { - age: null, - company: null, - logged: true, - } -); -``` - -#### Type declaration - -▸ (`userProperties`): `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `userProperties` | `PropertyRecord` | The properties of your user. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date. ``` | - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### IdentityResetFunction - -Ƭ **IdentityResetFunction**: () => `Promise`\<`unknown`\> - -Resets the current user identity. -Warning: This command must be called only once, since it creates a new identity on Screeb side. - -**`Example`** - -```ts -const { identityReset } = useScreeb(); - -identityReset(); -``` - -#### Type declaration - -▸ (): `Promise`\<`unknown`\> - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### InitFunction - -Ƭ **InitFunction**: (`websiteId`: `string`, `userId?`: `string`, `userProperties?`: `PropertyRecord`, `hooks?`: `HooksInit`, `language?`: `string`, `spaNavigationHandler?`: `SpaNavigationHandler`) => `Promise`\<`void`\> - -Initializes Screeb tag. - -**`Example`** - -```ts -const { init } = useScreeb(); - -init( - "", - "", - { - firstname: '', - lastname: '', - plan: '', - last_seen_at: new Date(), - authenticated: true - }, - "en" -); -``` - -#### Type declaration - -▸ (`websiteId`, `userId?`, `userProperties?`, `hooks?`, `language?`, `spaNavigationHandler?`): `Promise`\<`void`\> - -##### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `websiteId` | `string` | Your website/channel id. | -| `userId?` | `string` | The unique identifier of your user. | -| `userProperties?` | `PropertyRecord` | The properties of your user. ```text Requirements: - Property names must be limited to 128 characters - No more than 1000 attributes - Supported types for values: string, number, boolean and Date ``` | -| `hooks?` | `HooksInit` | - | -| `language?` | `string` | Force a specific language for the tag. eg: 'en'. default: browser language. | -| `spaNavigationHandler?` | `SpaNavigationHandler` | - | - -##### Returns - -`Promise`\<`void`\> - -___ - -### LoadFunction - -Ƭ **LoadFunction**: (`options?`: `ScreebOptions`) => `Promise`\<`void`\> - -Appends Screeb tag into your dom. - -**`Example`** - -```ts -const { load } = useScreeb(); - -load(); -``` - -#### Type declaration - -▸ (`options?`): `Promise`\<`void`\> - -##### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `options?` | `ScreebOptions` | Screeb module options. | - -##### Returns - -`Promise`\<`void`\> - -___ - -### MessageCloseFunction - -Ƭ **MessageCloseFunction**: () => `Promise`\<`unknown`\> - -Interrupts a running message. - -**`Example`** - -```ts -const { messageClose } = useScreeb(); - -messageClose(); -``` - -#### Type declaration - -▸ (): `Promise`\<`unknown`\> - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### MessageStartFunction - -Ƭ **MessageStartFunction**: (`messageId`: `string`, `allowMultipleResponses?`: `boolean`, `hiddenFields?`: `PropertyRecord`, `hooks?`: `HooksMessageStart`, `language?`: `string`) => `Promise`\<`unknown`\> - -Starts a message by its ID. - -**`Example`** - -```ts -const { messageStart } = useScreeb(); - -messageStart( - '', - false, - { - color: "green", - article_id: 42 - } -); -``` - -#### Type declaration - -▸ (`messageId`, `allowMultipleResponses?`, `hiddenFields?`, `hooks?`, `language?`): `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `messageId` | `string` | -| `allowMultipleResponses?` | `boolean` | -| `hiddenFields?` | `PropertyRecord` | -| `hooks?` | `HooksMessageStart` | -| `language?` | `string` | - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### ScreebContextValues - -Ƭ **ScreebContextValues**: `Object` - -Screeb context API - -#### Type declaration - -| Name | Type | -| :------ | :------ | -| `close` | [`CloseFunction`](README.md#closefunction) | -| `debug` | [`DebugFunction`](README.md#debugfunction) | -| `eventTrack` | [`EventTrackFunction`](README.md#eventtrackfunction) | -| `identity` | [`IdentityFunction`](README.md#identityfunction) | -| `identityGet` | [`IdentityGetFunction`](README.md#identitygetfunction) | -| `identityGroupAssign` | [`IdentityGroupAssignFunction`](README.md#identitygroupassignfunction) | -| `identityGroupUnassign` | [`IdentityGroupUnassignFunction`](README.md#identitygroupunassignfunction) | -| `identityProperties` | [`IdentityPropertiesFunction`](README.md#identitypropertiesfunction) | -| `identityReset` | [`IdentityResetFunction`](README.md#identityresetfunction) | -| `init` | [`InitFunction`](README.md#initfunction) | -| `load` | [`LoadFunction`](README.md#loadfunction) | -| `messageClose` | [`MessageCloseFunction`](README.md#messageclosefunction) | -| `messageStart` | [`MessageStartFunction`](README.md#messagestartfunction) | -| `sessionReplayStart` | [`SessionReplayStartFunction`](README.md#sessionreplaystartfunction) | -| `sessionReplayStop` | [`SessionReplayStopFunction`](README.md#sessionreplaystopfunction) | -| `surveyClose` | [`SurveyCloseFunction`](README.md#surveyclosefunction) | -| `surveyStart` | [`SurveyStartFunction`](README.md#surveystartfunction) | -| `targetingDebug` | [`TargetingDebugFunction`](README.md#targetingdebugfunction) | - -___ - -### ScreebProps - -Ƭ **ScreebProps**: `Object` - -Properties of Screeb provider - -#### Type declaration - -| Name | Type | Description | -| :------ | :------ | :------ | -| `hooks?` | `HooksInit` | Hooks to define callback for various event | -| `language?` | `string` | The language you want to force | -| `spaNavigationHandler?` | `SpaNavigationHandler` | Optional handler for the `in-page-spa` "Navigate to URL" target (custom SPA routers). | -| `userId?` | `string` | The unique identifier of your user. | -| `userProperties?` | `PropertyRecord` | The properties of your user. | -| `websiteId` | `string` | Your website/channel id. | - -___ - -### ScreebProviderProps - -Ƭ **ScreebProviderProps**: \{ `autoInit?`: `boolean` ; `options?`: `ScreebOptions` ; `shouldLoad?`: `boolean` } & `Partial`\<[`ScreebProps`](README.md#screebprops)\> - -Properties of Screeb provider - -___ - -### SessionReplayStartFunction - -Ƭ **SessionReplayStartFunction**: () => `Promise`\<`unknown`\> - -Starts a session replay. - -**`Example`** - -```ts -const { sessionReplayStart } = useScreeb(); - -sessionReplayStart(); -``` - -#### Type declaration - -▸ (): `Promise`\<`unknown`\> - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### SessionReplayStopFunction - -Ƭ **SessionReplayStopFunction**: () => `Promise`\<`unknown`\> - -Interrupts a running session replay. - -**`Example`** - -```ts -const { sessionReplayStop } = useScreeb(); - -sessionReplayStop(); -``` - -#### Type declaration - -▸ (): `Promise`\<`unknown`\> - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### SurveyCloseFunction - -Ƭ **SurveyCloseFunction**: () => `Promise`\<`unknown`\> - -Interrupts a running survey. - -**`Example`** - -```ts -const { surveyClose } = useScreeb(); - -surveyClose(); -``` - -#### Type declaration - -▸ (): `Promise`\<`unknown`\> - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### SurveyStartFunction - -Ƭ **SurveyStartFunction**: (`surveyId`: `string`, `distributionId?`: `string`, `allowMultipleResponses?`: `boolean`, `hiddenFields?`: `PropertyRecord`, `hooks?`: `HooksSurveyStart`, `language?`: `string`, `selectors?`: `string` \| `string`[]) => `Promise`\<`unknown`\> - -Starts a survey by its ID. - -**`Example`** - -```ts -const { surveyStart } = useScreeb(); - -surveyStart( - '', - '', - false, - { // optional - color: "green", - article_id: 42 - }, - { // optional - version: "1.0.0", - onSurveyShowed: (payload) => console.log("Survey showed", payload), - }, - "en", // optional - "#screeb-survey-container" // optional -); -``` - -#### Type declaration - -▸ (`surveyId`, `distributionId?`, `allowMultipleResponses?`, `hiddenFields?`, `hooks?`, `language?`, `selectors?`): `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `surveyId` | `string` | -| `distributionId?` | `string` | -| `allowMultipleResponses?` | `boolean` | -| `hiddenFields?` | `PropertyRecord` | -| `hooks?` | `HooksSurveyStart` | -| `language?` | `string` | -| `selectors?` | `string` \| `string`[] | - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### TargetingDebugFunction - -Ƭ **TargetingDebugFunction**: () => `Promise`\<`unknown`\> - -Prints the current state of the targeting engine. - -**`Example`** - -```ts -const { targetingDebug } = useScreeb(); - -console.log(await targetingDebug()); -// targeting ************ SCREEB TARGETING RULES DEBUG ************** -// Disabled surveys are not listed here. -// -// Screeb channel id: -// Screeb respondent id: -// -// Survey : -// https://admin.screeb.app/org/last/survey//share -// -// - Rule of type "Device type (desktop/mobile/tablet)": true 🟢 -// - Rule of type "Multiple display": true 🟢 -// - Rule of type "Capping per time between survey display on current respondent": true 🟢 -// - Rule of type "User event count": false 🔴 -// - Rule of type "Capping per respondent display count": false 🔴 -``` - -#### Type declaration - -▸ (): `Promise`\<`unknown`\> - -##### Returns - -`Promise`\<`unknown`\> - -## Functions - -### ScreebProvider - -▸ **ScreebProvider**(`props`, `deprecatedLegacyContext?`): `ReactNode` - -#### Parameters - -| Name | Type | Description | -| :------ | :------ | :------ | -| `props` | `PropsWithChildren`\<[`ScreebProviderProps`](README.md#screebproviderprops)\> | - | -| `deprecatedLegacyContext?` | `any` | **`Deprecated`** **`See`** [React Docs](https://legacy.reactjs.org/docs/legacy-context.html#referencing-context-in-lifecycle-methods) | - -#### Returns - -`ReactNode` - -___ - -### useScreeb - -▸ **useScreeb**(): [`ScreebContextValues`](README.md#screebcontextvalues) - -#### Returns - -[`ScreebContextValues`](README.md#screebcontextvalues) diff --git a/packages/sdk-react/package.json b/packages/sdk-react/package.json index 5466003..2d14a7c 100644 --- a/packages/sdk-react/package.json +++ b/packages/sdk-react/package.json @@ -30,7 +30,6 @@ "types": "dist/es/index.d.ts", "files": [ "dist", - "docs", "README.md" ], "scripts": { @@ -38,7 +37,7 @@ "build:doc": "typedoc src/index.ts", "clean": "rm -Rf dist", "lint": "eslint .", - "test": "echo '@TODO'" + "test": "echo \"no unit tests yet\"" }, "dependencies": { "@screeb/sdk-browser": "^0.7.0" diff --git a/packages/sdk-react/src/provider.tsx b/packages/sdk-react/src/provider.tsx index 0e96113..8ac1432 100644 --- a/packages/sdk-react/src/provider.tsx +++ b/packages/sdk-react/src/provider.tsx @@ -303,6 +303,9 @@ export const ScreebProvider: React.FC< const providerValue = React.useMemo( () => ({ + ScreebId: Screeb.ScreebId, + ScreebMaskText: Screeb.ScreebMaskText, + ScreebNoCapture: Screeb.ScreebNoCapture, close, debug, eventTrack, @@ -314,12 +317,12 @@ export const ScreebProvider: React.FC< identityReset, init, load, - surveyClose, - surveyStart, messageClose, messageStart, - sessionReplayStop, sessionReplayStart, + sessionReplayStop, + surveyClose, + surveyStart, targetingDebug, }), [ diff --git a/packages/sdk-react/src/types.ts b/packages/sdk-react/src/types.ts index f2c6e02..3a46030 100644 --- a/packages/sdk-react/src/types.ts +++ b/packages/sdk-react/src/types.ts @@ -450,6 +450,9 @@ export type SessionReplayStartFunction = () => Promise; * ``` */ export type TargetingDebugFunction = () => Promise; +export type ScreebMaskTextFunction = (element: T) => T; +export type ScreebNoCaptureFunction = (element: T) => T; +export type ScreebIdFunction = (element: T, id: string) => T; /** Screeb context API */ export type ScreebContextValues = { @@ -471,6 +474,9 @@ export type ScreebContextValues = { sessionReplayStart: SessionReplayStartFunction; sessionReplayStop: SessionReplayStopFunction; targetingDebug: TargetingDebugFunction; + ScreebMaskText: ScreebMaskTextFunction; + ScreebNoCapture: ScreebNoCaptureFunction; + ScreebId: ScreebIdFunction; }; /** Properties of Screeb provider */ diff --git a/packages/sdk-reactnative/README.md b/packages/sdk-reactnative/README.md index 3ff6be7..def3af7 100644 --- a/packages/sdk-reactnative/README.md +++ b/packages/sdk-reactnative/README.md @@ -54,13 +54,13 @@ cd ios && pod install Basic usage: ```ts -import { Screeb } from '@screeb/react-native'; +import { initSdk, setIdentity } from '@screeb/react-native'; // Initialize Screeb SDK -Screeb.initSdk(""); +initSdk(""); // Optional: identify visitor -Screeb.setIdentity("", { +setIdentity("", { firstname: "", lastname: "", plan: "", @@ -112,7 +112,7 @@ Re-run `prebuild` whenever native configuration changes. Use `npm run prebuild - ## Support -For any issues, please contact our support team at support@screeb.com. +For any issues, please contact our support team at support@screeb.app. ## Contributing diff --git a/packages/sdk-reactnative/ScreebReactNative.podspec b/packages/sdk-reactnative/ScreebReactNative.podspec index f3281c8..5867f96 100644 --- a/packages/sdk-reactnative/ScreebReactNative.podspec +++ b/packages/sdk-reactnative/ScreebReactNative.podspec @@ -11,12 +11,16 @@ Pod::Spec.new do |s| s.authors = package["author"] s.platforms = { :ios => "12.0" } - s.source = { :git => "https://github.com/ScreebApp/sdk-reactnative.git", :tag => "#{s.version}" } + s.source = { :git => "https://github.com/ScreebApp/sdk.git", :tag => "#{s.version}" } s.source_files = "ios/**/*.{h,m,mm,cpp,swift}" s.private_header_files = "ios/**/*.h" - s.dependency "Screeb", '~> 3.1.1' + if ENV["SCREEB_USE_LOCAL_SDK"] == "true" + s.dependency "Screeb" + else + s.dependency "Screeb", '~> 4.0.2' + end s.dependency "React-Core" install_modules_dependencies(s) end diff --git a/packages/sdk-reactnative/android/build.gradle b/packages/sdk-reactnative/android/build.gradle index 7f23e5d..a4c83a3 100644 --- a/packages/sdk-reactnative/android/build.gradle +++ b/packages/sdk-reactnative/android/build.gradle @@ -4,6 +4,9 @@ buildscript { } repositories { + if (providers.gradleProperty("SCREEB_USE_LOCAL_SDK").orElse(providers.environmentVariable("SCREEB_USE_LOCAL_SDK")).orNull == "true") { + mavenLocal() + } google() mavenCentral() } @@ -65,6 +68,9 @@ android { } repositories { + if (providers.gradleProperty("SCREEB_USE_LOCAL_SDK").orElse(providers.environmentVariable("SCREEB_USE_LOCAL_SDK")).orNull == "true") { + mavenLocal() + } mavenCentral() google() } @@ -74,5 +80,5 @@ def kotlin_version = getExtOrDefault("kotlinVersion") dependencies { implementation "com.facebook.react:react-android" implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" - api "app.screeb.sdk:survey:3.1.1" + api "app.screeb.sdk:survey:4.0.2" } diff --git a/packages/sdk-reactnative/android/src/main/java/app/screeb/reactnative/ScreebReactNativeModule.kt b/packages/sdk-reactnative/android/src/main/java/app/screeb/reactnative/ScreebReactNativeModule.kt index c91c6ed..efcac54 100644 --- a/packages/sdk-reactnative/android/src/main/java/app/screeb/reactnative/ScreebReactNativeModule.kt +++ b/packages/sdk-reactnative/android/src/main/java/app/screeb/reactnative/ScreebReactNativeModule.kt @@ -27,32 +27,8 @@ class ScreebReactNativeModule(reactContext: ReactApplicationContext) : language: String?, promise: Promise ) { - Screeb.setSecondarySDK("react-native", "3.1.1") - - val mapHooks: HashMap? = hooks?.let { readable -> - hashMapOf().apply { - val itKeys = readable.keySetIterator() - while (itKeys.hasNextKey()) { - val key = itKeys.nextKey() - if (key == "version") { - put(key, readable.getString(key)!!) - } else { - val value = readable.getString(key) - put(key) { payload: Any -> - reactApplicationContext - .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) - .emit( - "ScreebEvent", - Arguments.createMap().apply { - putString("hookId", value!!) - putString("payload", payload.toString()) - } - ) - } - } - } - } - } + Screeb.setSecondarySDK("react-native", "4.0.2") + val mapHooks = makeHooks(hooks) Handler(Looper.getMainLooper()).post { Screeb.pluginInit(channelId, userId, fromReadableMap(properties), fromReadableMap(initOptions), mapHooks, language) @@ -112,30 +88,7 @@ class ScreebReactNativeModule(reactContext: ReactApplicationContext) : distributionId: String?, promise: Promise ) { - val mapHooks: HashMap? = hooks?.let { readable -> - hashMapOf().apply { - val itKeys = readable.keySetIterator() - while (itKeys.hasNextKey()) { - val key = itKeys.nextKey() - if (key == "version") { - put(key, readable.getString(key)!!) - } else { - val value = readable.getString(key) - put(key) { payload: Any -> - reactApplicationContext - .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) - .emit( - "ScreebEvent", - Arguments.createMap().apply { - putString("hookId", value!!) - putString("payload", payload.toString()) - } - ) - } - } - } - } - } + val mapHooks = makeHooks(hooks) Handler(Looper.getMainLooper()).post { Screeb.startSurvey( @@ -161,30 +114,7 @@ class ScreebReactNativeModule(reactContext: ReactApplicationContext) : distributionId: String?, promise: Promise ) { - val mapHooks: HashMap? = hooks?.let { readable -> - hashMapOf().apply { - val itKeys = readable.keySetIterator() - while (itKeys.hasNextKey()) { - val key = itKeys.nextKey() - if (key == "version") { - put(key, readable.getString(key)!!) - } else { - val value = readable.getString(key) - put(key) { payload: Any -> - reactApplicationContext - .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) - .emit( - "ScreebEvent", - Arguments.createMap().apply { - putString("hookId", value!!) - putString("payload", payload.toString()) - } - ) - } - } - } - } - } + val mapHooks = makeHooks(hooks) Handler(Looper.getMainLooper()).post { Screeb.startMessage( @@ -264,6 +194,13 @@ class ScreebReactNativeModule(reactContext: ReactApplicationContext) : } } + override fun handleDeepLink(url: String, promise: Promise) { + Handler(Looper.getMainLooper()).post { + Screeb.handleDeepLink(android.net.Uri.parse(url)) + promise.resolve(null) + } + } + override fun closeSurvey(surveyId: String?, promise: Promise) { Handler(Looper.getMainLooper()).post { Screeb.closeSurvey(surveyId) @@ -288,6 +225,37 @@ class ScreebReactNativeModule(reactContext: ReactApplicationContext) : private fun fromReadableMap(readableMap: ReadableMap?): HashMap? = readableMap?.toHashMap() as? HashMap + private fun makeHooks(hooks: ReadableMap?): HashMap? { + if (hooks == null) return null + + val hookIds = hashMapOf() + val keys = hooks.keySetIterator() + while (keys.hasNextKey()) { + val key = keys.nextKey() + val value = hooks.getString(key) ?: continue + hookIds[key] = value + } + + if (hookIds.isEmpty()) return null + + return Screeb.makeHooks(hookIds) { hookId, nativeHookId, payload -> + emitHookEvent(hookId, nativeHookId, payload) + } + } + + private fun emitHookEvent(hookId: String, nativeHookId: String, payload: String) { + reactApplicationContext + .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter::class.java) + .emit( + "ScreebEvent", + Arguments.createMap().apply { + putString("hookId", hookId) + putString("nativeHookId", nativeHookId) + putString("payload", payload) + } + ) + } + companion object { const val NAME = "ScreebReactNative" } diff --git a/packages/sdk-reactnative/ios/ScreebReactNative.m b/packages/sdk-reactnative/ios/ScreebReactNative.m index 73ec805..783ef90 100644 --- a/packages/sdk-reactnative/ios/ScreebReactNative.m +++ b/packages/sdk-reactnative/ios/ScreebReactNative.m @@ -8,26 +8,40 @@ @interface RCT_EXTERN_MODULE(ScreebReactNative, RCTEventEmitter) properties:(nullable NSDictionary *)properties hooks:(nullable NSDictionary *)hooks initOptions:(nullable NSDictionary *)initOptions - language:(nullable NSString *)language) + language:(nullable NSString *)language + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(setIdentity:(NSString *)userId - properties:(nullable NSDictionary *)properties) + properties:(nullable NSDictionary *)properties + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(setProperties:(nullable NSDictionary *)properties) +RCT_EXTERN_METHOD(setProperties:(nullable NSDictionary *)properties + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(assignGroup:(nullable NSString *)type name:(NSString *)name - properties:(nullable NSDictionary *)properties) + properties:(nullable NSDictionary *)properties + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(unassignGroup:(nullable NSString *)type name:(NSString *)name - properties:(nullable NSDictionary *)properties) + properties:(nullable NSDictionary *)properties + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(trackEvent:(NSString *)name - properties:(nullable NSDictionary *)properties) + properties:(nullable NSDictionary *)properties + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(trackScreen:(NSString *)name - properties:(nullable NSDictionary *)properties) + properties:(nullable NSDictionary *)properties + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(startSurvey:(NSString *)surveyId allowMultipleResponses:(BOOL)allowMultipleResponses @@ -35,7 +49,9 @@ @interface RCT_EXTERN_MODULE(ScreebReactNative, RCTEventEmitter) ignoreSurveyStatus:(BOOL)ignoreSurveyStatus hooks:(nullable NSDictionary *)hooks language:(nullable NSString *)language - distributionId:(nullable NSString *)distributionId) + distributionId:(nullable NSString *)distributionId + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(startMessage:(NSString *)messageId allowMultipleResponses:(BOOL)allowMultipleResponses @@ -43,7 +59,9 @@ @interface RCT_EXTERN_MODULE(ScreebReactNative, RCTEventEmitter) ignoreMessageStatus:(BOOL)ignoreMessageStatus hooks:(nullable NSDictionary *)hooks language:(nullable NSString *)language - distributionId:(nullable NSString *)distributionId) + distributionId:(nullable NSString *)distributionId + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(debug:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) @@ -51,22 +69,36 @@ @interface RCT_EXTERN_MODULE(ScreebReactNative, RCTEventEmitter) RCT_EXTERN_METHOD(debugTargeting:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(sessionReplayStart) +RCT_EXTERN_METHOD(sessionReplayStart:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(sessionReplayStop) +RCT_EXTERN_METHOD(sessionReplayStop:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(resetIdentity) +RCT_EXTERN_METHOD(resetIdentity:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(getIdentity:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(closeSdk) +RCT_EXTERN_METHOD(closeSdk:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) + +RCT_EXTERN_METHOD(handleDeepLink:(NSString *)url + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(closeSurvey:(nullable NSString *)surveyId) +RCT_EXTERN_METHOD(closeSurvey:(nullable NSString *)surveyId + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) -RCT_EXTERN_METHOD(closeMessage:(nullable NSString *)messageId) +RCT_EXTERN_METHOD(closeMessage:(nullable NSString *)messageId + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(onHookResult:(NSString *)hookId - payload:(nullable NSDictionary *)payload) + payload:(nullable NSDictionary *)payload + resolve:(RCTPromiseResolveBlock)resolve + reject:(RCTPromiseRejectBlock)reject) @end diff --git a/packages/sdk-reactnative/ios/ScreebReactNative.swift b/packages/sdk-reactnative/ios/ScreebReactNative.swift index 2cbf1cc..71399bd 100644 --- a/packages/sdk-reactnative/ios/ScreebReactNative.swift +++ b/packages/sdk-reactnative/ios/ScreebReactNative.swift @@ -17,147 +17,128 @@ class ScreebReactNative: RCTEventEmitter { hasListeners = false } - @objc(initSdk:userId:properties:hooks:initOptions:language:) + @objc(initSdk:userId:properties:hooks:initOptions:language:resolve:reject:) func initSdk( _ channelId: String, userId userId_: String?, properties properties_: [String: Any]?, hooks hooks_: [String: Any]?, initOptions initOptions_: [String: Any]?, - language language_: String? + language language_: String?, + resolve: @escaping RCTPromiseResolveBlock, + reject: @escaping RCTPromiseRejectBlock ) { - Screeb.setSecondarySDK(name: "react-native", version: "3.1.1") - var mapHooks: [String: Any]? = nil - if (hooks_ != nil) { - mapHooks = [:] - hooks_?.forEach{ hook in - if (hook.key == "version") { - if let v = hook.value as? String { - mapHooks![hook.key] = v - } - } else { - mapHooks![hook.key] = { [weak self] (payload: Any) -> () in - self?.emitHookEvent(hookId: hook.value, payload: payload) - } - } - } - } + Screeb.setSecondarySDK(name: "react-native", version: "4.0.2") + let mapHooks = makeHooks(hooks_) let initOptionsDict: NSDictionary = NSDictionary(dictionary: (initOptions_ ?? [:]).compactMapValues { $0 }) let initOptionsFinal = InitOptions(dict: initOptionsDict) DispatchQueue.main.async { Screeb.initSdk(context: nil, channelId: channelId, identity: userId_, visitorProperty: properties_ ?? [:], initOptions: initOptionsFinal, hooks: mapHooks, language: language_) + resolve(nil) } } - @objc func setIdentity(_ userId: String, properties properties_: [String: Any]?) { + @objc func setIdentity(_ userId: String, properties properties_: [String: Any]?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.main.async { Screeb.setIdentity(uniqueVisitorId: userId, visitorProperty: properties_ ?? [:]) + resolve(nil) } } - @objc func trackEvent(_ eventId: String, properties properties_: [String: Any]?) { + @objc func trackEvent(_ eventId: String, properties properties_: [String: Any]?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.main.async { Screeb.trackEvent(name: eventId, trackingEventProperties: properties_ ?? [:]) + resolve(nil) } } - @objc func trackScreen(_ screen: String, properties properties_: [String: Any]?) { + @objc func trackScreen(_ screen: String, properties properties_: [String: Any]?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.main.async { Screeb.trackScreen(name: screen, trackingEventProperties: properties_ ?? [:]) + resolve(nil) } } - @objc(setProperties:) - func setVisitorPropertiesImpl(_ properties: [String: Any]?) { + @objc(setProperties:resolve:reject:) + func setVisitorPropertiesImpl(_ properties: [String: Any]?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.main.async { Screeb.visitorProperty(visitorProperty: properties ?? [:]) + resolve(nil) } } - @objc func startSurvey(_ surveyId: String, allowMultipleResponses allowMultipleResponses_: Bool, hiddenFields hiddenFields_: [String: Any]?,ignoreSurveyStatus ignoreSurveyStatus_: Bool, hooks hooks_: [String: Any]?, language language_: String?, distributionId distributionId_: String?) { - var mapHooks: [String: Any]? = nil - if (hooks_ != nil) { - mapHooks = [:] - hooks_?.forEach{ hook in - if(hook.key == "version"){ - if let v = hook.value as? String { - mapHooks![hook.key] = v - } - } else { - mapHooks![hook.key] = { [weak self] (payload: Any) -> () in - self?.emitHookEvent(hookId: hook.value, payload: payload) - } - } - } - } + @objc func startSurvey(_ surveyId: String, allowMultipleResponses allowMultipleResponses_: Bool, hiddenFields hiddenFields_: [String: Any]?,ignoreSurveyStatus ignoreSurveyStatus_: Bool, hooks hooks_: [String: Any]?, language language_: String?, distributionId distributionId_: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + let mapHooks = makeHooks(hooks_) DispatchQueue.main.async { Screeb.startSurvey(surveyId: surveyId, allowMultipleResponses: allowMultipleResponses_, hiddenFields: hiddenFields_ ?? [:], ignoreSurveyStatus: ignoreSurveyStatus_, hooks: mapHooks, language: language_, distributionId: distributionId_) + resolve(nil) } } - @objc func startMessage(_ messageId: String, allowMultipleResponses allowMultipleResponses_: Bool, hiddenFields hiddenFields_: [String: Any]?, ignoreMessageStatus ignoreMessageStatus_: Bool, hooks hooks_: [String: Any]?, language language_: String?, distributionId distributionId_: String?) { - var mapHooks: [String: Any]? = nil - if (hooks_ != nil) { - mapHooks = [:] - hooks_?.forEach{ hook in - if(hook.key == "version"){ - if let v = hook.value as? String { - mapHooks![hook.key] = v - } - } else { - mapHooks![hook.key] = { [weak self] (payload: Any) -> () in - self?.emitHookEvent(hookId: hook.value, payload: payload) - } - } - } - } + @objc func startMessage(_ messageId: String, allowMultipleResponses allowMultipleResponses_: Bool, hiddenFields hiddenFields_: [String: Any]?, ignoreMessageStatus ignoreMessageStatus_: Bool, hooks hooks_: [String: Any]?, language language_: String?, distributionId distributionId_: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { + let mapHooks = makeHooks(hooks_) DispatchQueue.main.async { Screeb.startMessage(messageId: messageId, allowMultipleResponses: allowMultipleResponses_, hiddenFields: hiddenFields_ ?? [:], ignoreMessageStatus: ignoreMessageStatus_, hooks: mapHooks, language: language_, distributionId: distributionId_) + resolve(nil) } } - @objc func assignGroup(_ type: String?, name name_: String, properties properties_: [String: Any]?) { + @objc func assignGroup(_ type: String?, name name_: String, properties properties_: [String: Any]?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.main.async { Screeb.assignGroup(type: type, name: name_, properties: properties_ ?? [:]) + resolve(nil) } } - @objc func unassignGroup(_ type: String?, name name_: String, properties properties_: [String: Any]?) { + @objc func unassignGroup(_ type: String?, name name_: String, properties properties_: [String: Any]?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.main.async { Screeb.unassignGroup(type: type, name: name_, properties: properties_ ?? [:]) + resolve(nil) } } - @objc func resetIdentity(){ + @objc func resetIdentity(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock){ DispatchQueue.main.async { Screeb.resetIdentity() + resolve(nil) } } - @objc func closeSdk(){ + @objc func closeSdk(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock){ DispatchQueue.main.async { Screeb.closeSdk() + resolve(nil) + } + } + + @objc func handleDeepLink(_ url: String, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock){ + DispatchQueue.main.async { + Screeb.handleDeepLink(url: URL(string: url)) + resolve(nil) } } - @objc func closeSurvey(_ surveyId: String?){ + @objc func closeSurvey(_ surveyId: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock){ DispatchQueue.main.async { Screeb.closeSurvey(surveyId: surveyId) + resolve(nil) } } - @objc func closeMessage(_ messageId: String?){ + @objc func closeMessage(_ messageId: String?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock){ DispatchQueue.main.async { Screeb.closeMessage(messageId: messageId) + resolve(nil) } } - @objc func onHookResult(_ hookId: String, payload: [String: Any]?) { + @objc func onHookResult(_ hookId: String, payload: [String: Any]?, resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock) { DispatchQueue.main.async { if payload != nil { Screeb.onHookResult(hookId, payload!["result"]) } + resolve(nil) } } @@ -187,15 +168,17 @@ class ScreebReactNative: RCTEventEmitter { } } - @objc func sessionReplayStart(){ + @objc func sessionReplayStart(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock){ DispatchQueue.main.async { Screeb.sessionReplayStart() + resolve(nil) } } - @objc func sessionReplayStop(){ + @objc func sessionReplayStop(_ resolve: @escaping RCTPromiseResolveBlock, reject: @escaping RCTPromiseRejectBlock){ DispatchQueue.main.async { Screeb.sessionReplayStop() + resolve(nil) } } @@ -212,12 +195,25 @@ class ScreebReactNative: RCTEventEmitter { } } - private func emitHookEvent(hookId: Any?, payload: Any) { - guard let hookKey = hookId as? String else { - return + private func makeHooks(_ hooks: [String: Any]?) -> [String: Any]? { + guard let hooks else { return nil } + + var hookIds: [String: String] = [:] + hooks.forEach { hook in + if let value = hook.value as? String { + hookIds[hook.key] = value + } } - let body: [String: Any] = ["hookId": hookKey, "payload": payload] + guard !hookIds.isEmpty else { return nil } + + return Screeb.makeHooks(hookIds) { [weak self] hookId, nativeHookId, payload in + self?.emitHookEvent(hookId: hookId, nativeHookId: nativeHookId, payload: payload) + } + } + + private func emitHookEvent(hookId: String, nativeHookId: String, payload: String) { + let body: [String: Any] = ["hookId": hookId, "nativeHookId": nativeHookId, "payload": payload] let sendBlock = { [weak self] in guard let self else { return } diff --git a/packages/sdk-reactnative/package.json b/packages/sdk-reactnative/package.json index 2178655..ab16880 100644 --- a/packages/sdk-reactnative/package.json +++ b/packages/sdk-reactnative/package.json @@ -1,6 +1,6 @@ { "name": "@screeb/react-native", - "version": "3.3.2", + "version": "4.0.2", "description": "Continuous Product Discovery", "main": "./lib/module/index.js", "types": "./lib/typescript/src/index.d.ts", @@ -39,7 +39,8 @@ "lint": "eslint . --ext .js,.ts,.tsx", "clean": "del-cli --force android/build ../../examples/example-reactnative/android/build ../../examples/example-reactnative/ios/build ../../examples/example-expo/android ../../examples/example-expo/ios lib", "prepare": "bob build", - "release": "release-it --only-version" + "release": "release-it --only-version", + "test": "echo \"no unit tests yet\"" }, "keywords": [ "react-native", @@ -48,16 +49,18 @@ ], "repository": { "type": "git", - "url": "git+https://github.com/ScreebApp/sdk.git" + "url": "https://github.com/ScreebApp/sdk.git", + "directory": "packages/sdk-reactnative" }, - "author": "Alexis (https://github.com/Alexays)", + "author": "Screeb's frontend team", "license": "MIT", "bugs": { - "url": "https://github.com/ScreebApp/sdk/issues" + "url": "https://github.com/ScreebApp/sdk/issues", + "email": "support@screeb.app" }, - "homepage": "https://github.com/ScreebApp/sdk#readme", + "homepage": "https://screeb.app", "publishConfig": { - "registry": "https://registry.npmjs.org/" + "access": "public" }, "devDependencies": { "@commitlint/config-conventional": "^19.8.1", diff --git a/packages/sdk-reactnative/src/NativeScreebReactNative.ts b/packages/sdk-reactnative/src/NativeScreebReactNative.ts index 706d5da..2e80308 100644 --- a/packages/sdk-reactnative/src/NativeScreebReactNative.ts +++ b/packages/sdk-reactnative/src/NativeScreebReactNative.ts @@ -63,6 +63,7 @@ export interface Spec extends TurboModule { resetIdentity(): Promise; getIdentity(): Promise<{ [key: string]: unknown } | null>; closeSdk(): Promise; + handleDeepLink(url: string): Promise; closeSurvey(surveyId?: string): Promise; closeMessage(messageId?: string): Promise; onHookResult( diff --git a/packages/sdk-reactnative/src/index.tsx b/packages/sdk-reactnative/src/index.tsx index 5ff437a..60486e2 100644 --- a/packages/sdk-reactnative/src/index.tsx +++ b/packages/sdk-reactnative/src/index.tsx @@ -3,11 +3,25 @@ import { NativeEventEmitter, NativeModules, Platform, + View, + type ViewProps, } from 'react-native'; import ScreebReactNative from './NativeScreebReactNative'; -// biome-ignore lint/suspicious/noExplicitAny: . -let emitter: any; +type ScreebEvent = { + hookId?: string; + nativeHookId?: string; + payload?: unknown; +}; +type ScreebEventEmitter = { + addListener( + eventType: 'ScreebEvent', + listener: (event: ScreebEvent) => void + ): { remove: () => void }; +}; + +let emitter: ScreebEventEmitter | undefined; +let eventSubscription: { remove: () => void } | undefined; type HookFn = (payload: string) => unknown | Promise; type HookMap = { @@ -20,6 +34,10 @@ type InitOptions = { isDebugMode?: boolean; disableMirror?: boolean; }; +export type ScreebPrivacyViewProps = ViewProps; +export type ScreebIdProps = ViewProps & { + id: string; +}; // initSdk export function initSdk( @@ -30,36 +48,9 @@ export function initSdk( initOptions?: InitOptions, language?: string ) { - // Use NativeEventEmitter on both platforms; pass the native module on iOS - // and rely on the default emitter on Android. - if (Platform.OS === 'ios') { - emitter = new NativeEventEmitter(NativeModules.ScreebReactNative); - } else { - emitter = DeviceEventEmitter; - } - - emitter.addListener('ScreebEvent', handleEvent); - - let mapHooksId: HookIdsMap | undefined; - if (hooks != null) { - mapHooksId = {}; - Object.keys(hooks).forEach((key) => { - if (key === 'version') { - const v = hooks.version ?? undefined; - if (v) - mapHooksId = { ...mapHooksId, version: v } as { - [key: string]: string; - }; - } else { - const uuid = Date.now().toString() + Math.random().toString() + key; - const fn = (hooks as Record)[key]; - if (typeof fn === 'function') { - hooksRegistry.set(uuid, fn as HookFn); - } - mapHooksId = { ...mapHooksId, [key]: uuid }; - } - }); - } + registerEventListener(); + hooksRegistry.clear(); + const mapHooksId = buildHooksMap(hooks); return ScreebReactNative.initSdk( channelId, @@ -130,26 +121,7 @@ export function startSurvey( language?: string, distributionId?: string ) { - let mapHooksId: HookIdsMap | undefined; - if (hooks !== undefined) { - mapHooksId = {}; - Object.keys(hooks).forEach((key) => { - if (key === 'version') { - const v = hooks.version ?? undefined; - if (v) - mapHooksId = { ...mapHooksId, version: v } as { - [key: string]: string; - }; - } else { - const uuid = Date.now().toString() + Math.random().toString() + key; - const fn = (hooks as Record)[key]; - if (typeof fn === 'function') { - hooksRegistry.set(uuid, fn as HookFn); - } - mapHooksId = { ...mapHooksId, [key]: uuid }; - } - }); - } + const mapHooksId = buildHooksMap(hooks); return ScreebReactNative.startSurvey( surveyId, allowMultipleResponses ?? true, @@ -171,26 +143,7 @@ export function startMessage( language?: string, distributionId?: string ) { - let mapHooksId: HookIdsMap | undefined; - if (hooks !== undefined) { - mapHooksId = {}; - Object.keys(hooks).forEach((key) => { - if (key === 'version') { - const v = hooks.version ?? undefined; - if (v) - mapHooksId = { ...mapHooksId, version: v } as { - [key: string]: string; - }; - } else { - const uuid = Date.now().toString() + Math.random().toString() + key; - const fn = (hooks as Record)[key]; - if (typeof fn === 'function') { - hooksRegistry.set(uuid, fn as HookFn); - } - mapHooksId = { ...mapHooksId, [key]: uuid }; - } - }); - } + const mapHooksId = buildHooksMap(hooks); return ScreebReactNative.startMessage( messageId, allowMultipleResponses ?? true, @@ -232,12 +185,19 @@ export function getIdentity() { return ScreebReactNative.getIdentity(); } +// handleDeepLink — forward a Screeb deep link (screeb- scheme) to +// the SDK: editor, survey and message links open in-app. Wire it to your +// linking events (e.g. expo-linking / RN Linking url listener). +export function handleDeepLink(url: string) { + return ScreebReactNative.handleDeepLink(url); +} + // closeSdk export function closeSdk() { - if (emitter) { - emitter.removeAllListeners('ScreebEvent'); - emitter = undefined; - } + eventSubscription?.remove(); + eventSubscription = undefined; + emitter = undefined; + hooksRegistry.clear(); return ScreebReactNative.closeSdk(); } @@ -251,12 +211,103 @@ export function closeMessage(messageId?: string) { return ScreebReactNative.closeMessage(messageId); } +export function ScreebMaskText(props: ScreebPrivacyViewProps) { + const marker = screebPlatformMarker({ + android: 'screeb-mask-text', + ios: 'screebMaskText', + }); + + return ( + + ); +} + +export function ScreebNoCapture(props: ScreebPrivacyViewProps) { + const marker = screebPlatformMarker({ + android: 'screeb-no-capture', + ios: 'screebNoCapture', + }); + + return ( + + ); +} + +export function ScreebId({ id, ...props }: ScreebIdProps) { + const marker = `screebId:${id}`; + + return ( + + ); +} + const hooksRegistry = new Map< string, (payload: string) => unknown | Promise >(); -function handleEvent(event: { hookId?: string; payload?: unknown }) { +function screebPlatformMarker(markers: { android: string; ios: string }) { + return Platform.OS === 'android' ? markers.android : markers.ios; +} + +function registerEventListener() { + eventSubscription?.remove(); + + // Use NativeEventEmitter on both platforms; pass the native module on iOS + // and rely on the default emitter on Android. + if (Platform.OS === 'ios') { + emitter = new NativeEventEmitter( + NativeModules.ScreebReactNative + ) as unknown as ScreebEventEmitter; + } else { + emitter = DeviceEventEmitter as unknown as ScreebEventEmitter; + } + + eventSubscription = emitter?.addListener('ScreebEvent', handleEvent); +} + +function buildHooksMap(hooks?: HookMap | null): HookIdsMap | undefined { + if (hooks == null) { + return undefined; + } + + const mapHooksId: HookIdsMap = {}; + Object.keys(hooks).forEach((key) => { + if (key === 'version') { + const version = hooks.version ?? undefined; + if (version) { + mapHooksId.version = version; + } + return; + } + + const uuid = Date.now().toString() + Math.random().toString() + key; + const fn = (hooks as Record)[key]; + if (typeof fn === 'function') { + hooksRegistry.set(uuid, fn as HookFn); + } + mapHooksId[key] = uuid; + }); + + return mapHooksId; +} + +function handleEvent(event: ScreebEvent) { if (event?.hookId != null) { const hook = hooksRegistry.get(event.hookId); if (hook != null) { @@ -265,12 +316,19 @@ function handleEvent(event: { hookId?: string; payload?: unknown }) { ? JSON.stringify(event.payload) : event.payload : '{}'; - const result = hook(payload); - const parsedPayload = JSON.parse(payload); - const originalHookId = parsedPayload?.hook_id; + let originalHookId = event.nativeHookId; + if (!originalHookId) { + try { + const parsedPayload = JSON.parse(payload) as { hook_id?: string }; + originalHookId = parsedPayload?.hook_id; + } catch (error) { + console.error(error); + return; + } + } if (originalHookId) { - if (result instanceof Promise) { - result + try { + Promise.resolve(hook(payload)) .then((hookResult) => { ScreebReactNative.onHookResult(originalHookId, { result: hookResult, @@ -278,9 +336,15 @@ function handleEvent(event: { hookId?: string; payload?: unknown }) { }) .catch((error) => { console.error(error); + ScreebReactNative.onHookResult(originalHookId, { + result: false, + }); }); - } else { - ScreebReactNative.onHookResult(originalHookId, { result }); + } catch (error) { + console.error(error); + ScreebReactNative.onHookResult(originalHookId, { + result: false, + }); } } } @@ -329,14 +393,18 @@ function normalizeValue(value: unknown): unknown { // Format payloads so DateTime properties are correctly interpreted by the SDK function formatDateValue(value: Date): string { - const timezoneOffsetHours = -value.getTimezoneOffset() / 60; - const sign = timezoneOffsetHours >= 0 ? '+' : '-'; - const offset = Math.abs(timezoneOffsetHours).toString().padStart(2, '0'); + const timezoneOffsetMinutes = -value.getTimezoneOffset(); + const sign = timezoneOffsetMinutes >= 0 ? '+' : '-'; + const absOffset = Math.abs(timezoneOffsetMinutes); + const offsetHours = Math.floor(absOffset / 60) + .toString() + .padStart(2, '0'); + const offsetMinutes = (absOffset % 60).toString().padStart(2, '0'); const pad = (n: number, l = 2) => n.toString().padStart(l, '0'); const isoWithoutTimezone = `${value.getFullYear()}-${pad( value.getMonth() + 1 )}-${pad(value.getDate())}T${pad(value.getHours())}:${pad( value.getMinutes() )}:${pad(value.getSeconds())}.${pad(value.getMilliseconds(), 3)}`; - return `${isoWithoutTimezone}${sign}${offset}:00`; + return `${isoWithoutTimezone}${sign}${offsetHours}:${offsetMinutes}`; } diff --git a/packages/sdk-reactnative/test-types/hooks.ts b/packages/sdk-reactnative/test-types/hooks.ts new file mode 100644 index 0000000..55df4d1 --- /dev/null +++ b/packages/sdk-reactnative/test-types/hooks.ts @@ -0,0 +1,13 @@ +import { initSdk, startMessage, startSurvey } from '../src'; + +const hooks = { + version: '1.0', + onSurveyDisplayAllowed: async (payload: string) => + payload.includes('hook_id'), +}; + +initSdk('channel-id', 'user-id', { plan: 'pro' }, hooks); + +startSurvey('survey-id', true, { source: 'test' }, true, hooks); + +startMessage('message-id', true, { source: 'test' }, true, hooks); diff --git a/packages/sdk-reactnative/test-types/privacy-helpers.tsx b/packages/sdk-reactnative/test-types/privacy-helpers.tsx new file mode 100644 index 0000000..bcf60b2 --- /dev/null +++ b/packages/sdk-reactnative/test-types/privacy-helpers.tsx @@ -0,0 +1,14 @@ +import { View } from 'react-native'; +import { ScreebId, ScreebMaskText, ScreebNoCapture } from '../src'; + +export const privacyHelperNodes = [ + + + , + + + , + + + , +]; diff --git a/packages/sdk-svelte/.eslintrc b/packages/sdk-svelte/.eslintrc new file mode 100644 index 0000000..903ef04 --- /dev/null +++ b/packages/sdk-svelte/.eslintrc @@ -0,0 +1,8 @@ +{ + "ignorePatterns": [ + "dist" + ], + "extends": [ + "@screeb/eslint-config" + ] +} diff --git a/packages/sdk-svelte/README.md b/packages/sdk-svelte/README.md new file mode 100644 index 0000000..d41825d --- /dev/null +++ b/packages/sdk-svelte/README.md @@ -0,0 +1,123 @@ +

+ + Logo + +

+

@screeb/sdk-svelte

+

+ Screeb's browser SDK, optimized for Svelte 4. + + Continuous Product Discovery, Without the Time Sink. + + Screeb is the only Continuous Product Discovery platform that lets you analyse users' behaviour, ask in-app questions, recruit people for interviews and analyse data in a blink with AI. +

+ +

+ + ci + + + version + + + min size + + + minzipped size + + downloads +

+ +## Installation + +This library is published in the NPM registry and can be installed using any compatible package manager. + +```sh +npm install @screeb/sdk-svelte --save + +# For Yarn, use the command below. +yarn add @screeb/sdk-svelte +``` + +## Package Size + +Current package size snapshot: + +- npm tarball: 7.1 KB +- unpacked package: 42.3 KB + +## Usage + +Basic usage: + +Call `setScreebContext` once in the root component that wraps your app: + +```svelte + + + + +``` + +## Usage + +Use `useScreeb()` in any child component: + +```svelte + + + +``` + +## Identify after login + +```svelte + + +``` + +For a working example, see our [Screeb Svelte SDK example app](https://github.com/ScreebApp/sdk/tree/master/examples/example-svelte). + +## Documentation + +- Install guide: [developers.screeb.app/sdk-svelte/install](https://developers.screeb.app/sdk-svelte/install) +- API reference: [developers.screeb.app/sdk-svelte/reference](https://developers.screeb.app/sdk-svelte/reference) + +## Support + +For any issues, please contact our support team at support@screeb.app. + +## Contributing + +All third party contributors acknowledge that any contributions they provide will be made under the same open source license that the open source project is provided under. + +## License + +Released under [MIT License](https://github.com/ScreebApp/sdk/blob/master/LICENSE). diff --git a/packages/sdk-svelte/package.json b/packages/sdk-svelte/package.json new file mode 100644 index 0000000..5b9d45f --- /dev/null +++ b/packages/sdk-svelte/package.json @@ -0,0 +1,65 @@ +{ + "name": "@screeb/sdk-svelte", + "version": "0.1.0", + "description": "Screeb's browser SDK, optimized for Svelte.", + "keywords": [ + "product discovery", + "product management", + "survey", + "analytics", + "user feedback", + "user research", + "svelte" + ], + "homepage": "https://screeb.app", + "bugs": { + "url": "https://github.com/ScreebApp/sdk/issues", + "email": "support@screeb.app" + }, + "repository": { + "type": "git", + "url": "https://github.com/ScreebApp/sdk.git", + "directory": "packages/sdk-svelte" + }, + "license": "MIT", + "author": "Screeb's frontend team", + "sideEffects": false, + "type": "module", + "module": "dist/es/index.mjs", + "main": "dist/cjs/index.cjs", + "types": "dist/es/index.d.ts", + "files": [ + "dist", + "README.md" + ], + "scripts": { + "build": "rollup -c ../../node_modules/@screeb/typescript-config/src/rollup.config.js", + "clean": "rm -Rf dist", + "lint": "eslint .", + "release:check": "npm run build --workspace=@screeb/sdk-browser && npm run build && npm run lint", + "test": "echo \"no unit tests yet\"" + }, + "dependencies": { + "@screeb/sdk-browser": "^0.7.0" + }, + "devDependencies": { + "@screeb/eslint-config": "^0.1.7", + "@screeb/typescript-config": "^0.1.12", + "@types/node": "^20.8.4", + "@typescript-eslint/eslint-plugin": "^6.7.5", + "eslint": "^8.51.0", + "eslint-plugin-import": "^2.28.1", + "eslint-plugin-jsx-a11y": "^6.7.1", + "eslint-plugin-prettier": "^5.0.1", + "prettier": "^3.0.3", + "rollup": "^4.0.2", + "svelte": ">=4.0.0", + "typescript": "^5.2.2" + }, + "peerDependencies": { + "svelte": ">=4.0.0" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/sdk-svelte/readme/screeb-logo.svg b/packages/sdk-svelte/readme/screeb-logo.svg new file mode 100644 index 0000000..b74bd6e --- /dev/null +++ b/packages/sdk-svelte/readme/screeb-logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/packages/sdk-svelte/src/client.ts b/packages/sdk-svelte/src/client.ts new file mode 100644 index 0000000..5002b51 --- /dev/null +++ b/packages/sdk-svelte/src/client.ts @@ -0,0 +1,166 @@ +import * as Screeb from "@screeb/sdk-browser"; + +import CONSTANTS from "./constants"; +import * as logger from "./logger"; +import { ScreebClient, ScreebConfig } from "./types"; + +export const createScreebClient = (config: ScreebConfig): ScreebClient => { + let isInitialized = false; + + const ensureScreeb = async function ReturnType>( + functionName: string, + callback: T, + onlyLoaded = false, + ): Promise> { + const shouldLoad = config.shouldLoad ?? true; + if (!Screeb.isLoaded() && !shouldLoad) { + const message = + "Screeb instance is not loaded because `shouldLoad` is set to `false` in the Svelte provider"; + logger.log("warn", message); + return Promise.reject(message); + } + + if (!isInitialized && !onlyLoaded) { + const message = [ + `"${functionName}" was called but Screeb has not been initialized yet. `, + `Please call 'init' before calling '${functionName}' or `, + "set 'autoInit' to true in the Svelte provider config.", + ].join(""); + logger.log("warn", message); + return Promise.reject(message); + } + + return Promise.resolve(callback()); + }; + + const init = async ( + websiteId: string, + userId?: string, + userProperties?: Screeb.PropertyRecord, + hooks?: Screeb.HooksInit, + language?: string, + ) => { + await ensureScreeb( + "init", + () => { + if (!isInitialized) { + Screeb.init(websiteId, userId, userProperties, hooks, language); + isInitialized = true; + } + }, + true, + ); + }; + + const load = async (options?: Screeb.ScreebOptions) => { + if (!Screeb.isLoaded()) { + Screeb.load({ + sdkName: "sdk-svelte", + sdkVersion: CONSTANTS.version, + ...options, + }); + } + + if (config.autoInit && !isInitialized) { + if (config.websiteId) { + await init( + config.websiteId, + config.userId, + config.userProperties, + config.hooks, + config.language, + ); + } else { + logger.log( + "warn", + "autoInit is set to true, but no websiteId have been provided.", + ); + } + } + }; + + const close = async () => { + if (Screeb.isLoaded()) { + await Screeb.close(); + isInitialized = false; + } + }; + + return { + ScreebId: Screeb.ScreebId, + ScreebMaskText: Screeb.ScreebMaskText, + ScreebNoCapture: Screeb.ScreebNoCapture, + close, + debug: async () => ensureScreeb("debug", () => Screeb.debug()), + eventTrack: async (eventName, eventProperties) => + ensureScreeb("eventTrack", () => + Screeb.eventTrack(eventName, eventProperties), + ), + identity: async (userId, userProperties) => + ensureScreeb("identity", () => Screeb.identity(userId, userProperties)), + identityGet: async () => + ensureScreeb("identityGet", () => Screeb.identityGet()), + identityGroupAssign: async (groupName, groupType, groupProperties) => + ensureScreeb("identityGroupAssign", () => + Screeb.identityGroupAssign(groupName, groupType, groupProperties), + ), + identityGroupUnassign: async (groupName, groupType) => + ensureScreeb("identityGroupUnassign", () => + Screeb.identityGroupUnassign(groupName, groupType), + ), + identityProperties: async (userProperties) => + ensureScreeb("identityProperties", () => + Screeb.identityProperties(userProperties), + ), + identityReset: async () => + ensureScreeb("identityReset", () => Screeb.identityReset()), + init, + load, + messageClose: async () => + ensureScreeb("messageClose", () => Screeb.messageClose()), + messageStart: async ( + messageId, + allowMultipleResponses, + hiddenFields, + hooks, + language, + ) => + ensureScreeb("messageStart", () => + Screeb.messageStart( + messageId, + allowMultipleResponses, + hiddenFields, + hooks, + language, + ), + ), + sessionReplayStart: async () => + ensureScreeb("sessionReplayStart", () => Screeb.sessionReplayStart()), + sessionReplayStop: async () => + ensureScreeb("sessionReplayStop", () => Screeb.sessionReplayStop()), + surveyClose: async () => + ensureScreeb("surveyClose", () => Screeb.surveyClose()), + surveyStart: async ( + surveyId, + distributionId, + allowMultipleResponses, + hiddenFields, + hooks, + language, + selectors, + ) => + ensureScreeb("surveyStart", () => + Screeb.surveyStart( + surveyId, + distributionId, + allowMultipleResponses, + hiddenFields, + hooks, + language, + selectors, + ), + ), + targetingDebug: async () => + ensureScreeb("targetingDebug", () => Screeb.targetingDebug()), + }; +}; diff --git a/packages/sdk-svelte/src/constants.ts b/packages/sdk-svelte/src/constants.ts new file mode 100644 index 0000000..fe9cb36 --- /dev/null +++ b/packages/sdk-svelte/src/constants.ts @@ -0,0 +1,3 @@ +const CONSTANTS = { version: "0.1.0" }; + +export default CONSTANTS; diff --git a/packages/sdk-svelte/src/context.ts b/packages/sdk-svelte/src/context.ts new file mode 100644 index 0000000..a538615 --- /dev/null +++ b/packages/sdk-svelte/src/context.ts @@ -0,0 +1,35 @@ +import { getContext, setContext } from "svelte"; + +import { createScreebClient } from "./client"; +import * as logger from "./logger"; +import { ScreebClient, ScreebConfig } from "./types"; +import { isSSR } from "./utils"; + +export const SCREEB_CONTEXT_KEY = Symbol("screeb"); + +export function setScreebContext(config: ScreebConfig): ScreebClient { + const client = createScreebClient(config); + + setContext(SCREEB_CONTEXT_KEY, client); + + if (!isSSR && (config.shouldLoad ?? true)) { + void client.load(config.options); + } + + return client; +} + +export const provideScreeb = setScreebContext; + +export function useScreeb(): ScreebClient { + const context = getContext(SCREEB_CONTEXT_KEY); + + if (!context) { + logger.log( + "warn", + "`useScreeb` must be called inside a component tree where `setScreebContext` or `provideScreeb` has been called.", + ); + } + + return context as ScreebClient; +} diff --git a/packages/sdk-svelte/src/index.ts b/packages/sdk-svelte/src/index.ts new file mode 100644 index 0000000..eb6b921 --- /dev/null +++ b/packages/sdk-svelte/src/index.ts @@ -0,0 +1,8 @@ +export { createScreebClient } from "./client"; +export { + provideScreeb, + SCREEB_CONTEXT_KEY, + setScreebContext, + useScreeb, +} from "./context"; +export * from "./types"; diff --git a/packages/sdk-svelte/src/logger.ts b/packages/sdk-svelte/src/logger.ts new file mode 100644 index 0000000..698a24c --- /dev/null +++ b/packages/sdk-svelte/src/logger.ts @@ -0,0 +1,8 @@ +type LogLevel = "warn"; + +export const log = (level: LogLevel, message: string) => { + if (level === "warn") { + // eslint-disable-next-line no-console + console.warn(message); + } +}; diff --git a/packages/sdk-svelte/src/types.ts b/packages/sdk-svelte/src/types.ts new file mode 100644 index 0000000..d60547d --- /dev/null +++ b/packages/sdk-svelte/src/types.ts @@ -0,0 +1,132 @@ +/* eslint-disable no-unused-vars */ +import { + HooksInit, + HooksMessageStart, + HooksSurveyStart, + PropertyRecord, + ScreebIdentityGetReturn, + ScreebOptions, +} from "@screeb/sdk-browser"; + +/** Configuration for the Svelte Screeb provider. */ +export type ScreebConfig = { + /** Your website/channel id. */ + websiteId: string; + /** The unique identifier of your user. */ + userId?: string; + /** The properties of your user. */ + userProperties?: PropertyRecord; + /** Hooks to define callbacks for Screeb events. */ + hooks?: HooksInit; + /** Force a specific language (for example, "en"). Defaults to the browser language. */ + language?: string; + /** + * Indicates if Screeb should be automatically loaded. + * Set to false to prevent the SDK from loading, for example during SSR or tests. + * @default true + */ + shouldLoad?: boolean; + /** + * Indicates if Screeb should be automatically initialized after load. + * @default false + */ + autoInit?: boolean; + /** Screeb tag loading options. */ + options?: ScreebOptions; +}; + +export type CloseFunction = () => Promise; +export type DebugFunction = () => Promise; + +export type EventTrackFunction = ( + eventName: string, + eventProperties?: PropertyRecord, +) => Promise; + +export type IdentityFunction = ( + userId: string, + userProperties?: PropertyRecord, +) => Promise; + +export type IdentityGetFunction = () => Promise; + +export type IdentityGroupAssignFunction = ( + groupName: string, + groupType?: string, + groupProperties?: PropertyRecord, +) => Promise; + +export type IdentityGroupUnassignFunction = ( + groupName: string, + groupType?: string, +) => Promise; + +export type IdentityPropertiesFunction = ( + userProperties: PropertyRecord, +) => Promise; + +export type IdentityResetFunction = () => Promise; + +export type InitFunction = ( + websiteId: string, + userId?: string, + userProperties?: PropertyRecord, + hooks?: HooksInit, + language?: string, +) => Promise; + +export type LoadFunction = (options?: ScreebOptions) => Promise; + +export type SurveyCloseFunction = () => Promise; + +export type SurveyStartFunction = ( + surveyId: string, + distributionId?: string, + allowMultipleResponses?: boolean, + hiddenFields?: PropertyRecord, + hooks?: HooksSurveyStart, + language?: string, + selectors?: string | string[], +) => Promise; + +export type MessageCloseFunction = () => Promise; + +export type MessageStartFunction = ( + messageId: string, + allowMultipleResponses?: boolean, + hiddenFields?: PropertyRecord, + hooks?: HooksMessageStart, + language?: string, +) => Promise; + +export type SessionReplayStopFunction = () => Promise; +export type SessionReplayStartFunction = () => Promise; +export type TargetingDebugFunction = () => Promise; +export type ScreebMaskTextFunction = (element: T) => T; +export type ScreebNoCaptureFunction = (element: T) => T; +export type ScreebIdFunction = (element: T, id: string) => T; + +/** All Screeb methods provided by `useScreeb()`. */ +export type ScreebClient = { + close: CloseFunction; + debug: DebugFunction; + eventTrack: EventTrackFunction; + identity: IdentityFunction; + identityGet: IdentityGetFunction; + identityGroupAssign: IdentityGroupAssignFunction; + identityGroupUnassign: IdentityGroupUnassignFunction; + identityProperties: IdentityPropertiesFunction; + identityReset: IdentityResetFunction; + init: InitFunction; + load: LoadFunction; + messageClose: MessageCloseFunction; + messageStart: MessageStartFunction; + sessionReplayStart: SessionReplayStartFunction; + sessionReplayStop: SessionReplayStopFunction; + surveyClose: SurveyCloseFunction; + surveyStart: SurveyStartFunction; + targetingDebug: TargetingDebugFunction; + ScreebMaskText: ScreebMaskTextFunction; + ScreebNoCapture: ScreebNoCaptureFunction; + ScreebId: ScreebIdFunction; +}; diff --git a/packages/sdk-svelte/src/utils.ts b/packages/sdk-svelte/src/utils.ts new file mode 100644 index 0000000..19c0976 --- /dev/null +++ b/packages/sdk-svelte/src/utils.ts @@ -0,0 +1 @@ +export const isSSR = typeof window === "undefined"; diff --git a/packages/sdk-svelte/tsconfig.json b/packages/sdk-svelte/tsconfig.json new file mode 100644 index 0000000..5c272f8 --- /dev/null +++ b/packages/sdk-svelte/tsconfig.json @@ -0,0 +1,10 @@ +{ + "extends": "@screeb/typescript-config/src/tsconfig.json", + "include": ["src"], + "exclude": ["dist", "**/*.test.ts"], + "compilerOptions": { + "outDir": "dist", + "declaration": true, + "declarationDir": "." + } +} diff --git a/packages/sdk-vue/docs/.nojekyll b/packages/sdk-vue/docs/.nojekyll deleted file mode 100644 index e2ac661..0000000 --- a/packages/sdk-vue/docs/.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/packages/sdk-vue/docs/README.md b/packages/sdk-vue/docs/README.md deleted file mode 100644 index c6cf5e8..0000000 --- a/packages/sdk-vue/docs/README.md +++ /dev/null @@ -1,438 +0,0 @@ -@screeb/sdk-vue - -# @screeb/sdk-vue - -## Table of contents - -### Type Aliases - -- [CloseFunction](README.md#closefunction) -- [DebugFunction](README.md#debugfunction) -- [EventTrackFunction](README.md#eventtrackfunction) -- [IdentityFunction](README.md#identityfunction) -- [IdentityGetFunction](README.md#identitygetfunction) -- [IdentityGroupAssignFunction](README.md#identitygroupassignfunction) -- [IdentityGroupUnassignFunction](README.md#identitygroupunassignfunction) -- [IdentityPropertiesFunction](README.md#identitypropertiesfunction) -- [IdentityResetFunction](README.md#identityresetfunction) -- [InitFunction](README.md#initfunction) -- [LoadFunction](README.md#loadfunction) -- [MessageCloseFunction](README.md#messageclosefunction) -- [MessageStartFunction](README.md#messagestartfunction) -- [ScreebConfig](README.md#screebconfig) -- [ScreebContextValues](README.md#screebcontextvalues) -- [SessionReplayStartFunction](README.md#sessionreplaystartfunction) -- [SessionReplayStopFunction](README.md#sessionreplaystopfunction) -- [SurveyCloseFunction](README.md#surveyclosefunction) -- [SurveyStartFunction](README.md#surveystartfunction) -- [TargetingDebugFunction](README.md#targetingdebugfunction) - -### Variables - -- [SCREEB\_PLUGIN\_KEY](README.md#screeb_plugin_key) -- [ScreebPlugin](README.md#screebplugin) - -### Functions - -- [useScreeb](README.md#usescreeb) - -## Type Aliases - -### CloseFunction - -Ƭ **CloseFunction**: () => `Promise`\<`void`\> - -#### Type declaration - -▸ (): `Promise`\<`void`\> - -##### Returns - -`Promise`\<`void`\> - -___ - -### DebugFunction - -Ƭ **DebugFunction**: () => `Promise`\<`unknown`\> - -#### Type declaration - -▸ (): `Promise`\<`unknown`\> - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### EventTrackFunction - -Ƭ **EventTrackFunction**: (`eventName`: `string`, `eventProperties?`: `PropertyRecord`) => `Promise`\<`unknown`\> - -#### Type declaration - -▸ (`eventName`, `eventProperties?`): `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `eventName` | `string` | -| `eventProperties?` | `PropertyRecord` | - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### IdentityFunction - -Ƭ **IdentityFunction**: (`userId`: `string`, `userProperties?`: `PropertyRecord`) => `Promise`\<`unknown`\> - -#### Type declaration - -▸ (`userId`, `userProperties?`): `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `userId` | `string` | -| `userProperties?` | `PropertyRecord` | - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### IdentityGetFunction - -Ƭ **IdentityGetFunction**: () => `Promise`\<`ScreebIdentityGetReturn`\> - -#### Type declaration - -▸ (): `Promise`\<`ScreebIdentityGetReturn`\> - -##### Returns - -`Promise`\<`ScreebIdentityGetReturn`\> - -___ - -### IdentityGroupAssignFunction - -Ƭ **IdentityGroupAssignFunction**: (`groupName`: `string`, `groupType?`: `string`, `groupProperties?`: `PropertyRecord`) => `Promise`\<`unknown`\> - -#### Type declaration - -▸ (`groupName`, `groupType?`, `groupProperties?`): `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `groupName` | `string` | -| `groupType?` | `string` | -| `groupProperties?` | `PropertyRecord` | - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### IdentityGroupUnassignFunction - -Ƭ **IdentityGroupUnassignFunction**: (`groupName`: `string`, `groupType?`: `string`) => `Promise`\<`unknown`\> - -#### Type declaration - -▸ (`groupName`, `groupType?`): `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `groupName` | `string` | -| `groupType?` | `string` | - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### IdentityPropertiesFunction - -Ƭ **IdentityPropertiesFunction**: (`userProperties`: `PropertyRecord`) => `Promise`\<`unknown`\> - -#### Type declaration - -▸ (`userProperties`): `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `userProperties` | `PropertyRecord` | - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### IdentityResetFunction - -Ƭ **IdentityResetFunction**: () => `Promise`\<`unknown`\> - -#### Type declaration - -▸ (): `Promise`\<`unknown`\> - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### InitFunction - -Ƭ **InitFunction**: (`websiteId`: `string`, `userId?`: `string`, `userProperties?`: `PropertyRecord`, `hooks?`: `HooksInit`, `language?`: `string`, `spaNavigationHandler?`: `SpaNavigationHandler`) => `Promise`\<`void`\> - -#### Type declaration - -▸ (`websiteId`, `userId?`, `userProperties?`, `hooks?`, `language?`, `spaNavigationHandler?`): `Promise`\<`void`\> - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `websiteId` | `string` | -| `userId?` | `string` | -| `userProperties?` | `PropertyRecord` | -| `hooks?` | `HooksInit` | -| `language?` | `string` | -| `spaNavigationHandler?` | `SpaNavigationHandler` | - -##### Returns - -`Promise`\<`void`\> - -___ - -### LoadFunction - -Ƭ **LoadFunction**: (`options?`: `ScreebOptions`) => `Promise`\<`void`\> - -#### Type declaration - -▸ (`options?`): `Promise`\<`void`\> - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `options?` | `ScreebOptions` | - -##### Returns - -`Promise`\<`void`\> - -___ - -### MessageCloseFunction - -Ƭ **MessageCloseFunction**: () => `Promise`\<`unknown`\> - -#### Type declaration - -▸ (): `Promise`\<`unknown`\> - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### MessageStartFunction - -Ƭ **MessageStartFunction**: (`messageId`: `string`, `allowMultipleResponses?`: `boolean`, `hiddenFields?`: `PropertyRecord`, `hooks?`: `HooksMessageStart`, `language?`: `string`) => `Promise`\<`unknown`\> - -#### Type declaration - -▸ (`messageId`, `allowMultipleResponses?`, `hiddenFields?`, `hooks?`, `language?`): `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `messageId` | `string` | -| `allowMultipleResponses?` | `boolean` | -| `hiddenFields?` | `PropertyRecord` | -| `hooks?` | `HooksMessageStart` | -| `language?` | `string` | - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### ScreebConfig - -Ƭ **ScreebConfig**: `Object` - -Configuration for the ScreebPlugin - -#### Type declaration - -| Name | Type | Description | -| :------ | :------ | :------ | -| `autoInit?` | `boolean` | Indicates if Screeb should be automatically initialized. When true, `init` is called automatically with `websiteId` and `userId`. **`Default`** ```ts false ``` | -| `hooks?` | `HooksInit` | Hooks to define callback for various Screeb events | -| `language?` | `string` | Force a specific language (e.g. 'en'). Default: browser language. | -| `options?` | `ScreebOptions` | Screeb tag initialization options — handle with care. | -| `shouldLoad?` | `boolean` | Indicates if Screeb should be automatically loaded. Set to false to prevent the SDK from loading (e.g. in CI). **`Default`** ```ts true ``` | -| `spaNavigationHandler?` | `SpaNavigationHandler` | Optional handler for the `in-page-spa` "Navigate to URL" target (custom SPA routers). | -| `userId?` | `string` | The unique identifier of your user. | -| `userProperties?` | `PropertyRecord` | The properties of your user. | -| `websiteId` | `string` | Your website/channel id. | - -___ - -### ScreebContextValues - -Ƭ **ScreebContextValues**: `Object` - -All Screeb methods provided via `useScreeb()` - -#### Type declaration - -| Name | Type | -| :------ | :------ | -| `close` | [`CloseFunction`](README.md#closefunction) | -| `debug` | [`DebugFunction`](README.md#debugfunction) | -| `eventTrack` | [`EventTrackFunction`](README.md#eventtrackfunction) | -| `identity` | [`IdentityFunction`](README.md#identityfunction) | -| `identityGet` | [`IdentityGetFunction`](README.md#identitygetfunction) | -| `identityGroupAssign` | [`IdentityGroupAssignFunction`](README.md#identitygroupassignfunction) | -| `identityGroupUnassign` | [`IdentityGroupUnassignFunction`](README.md#identitygroupunassignfunction) | -| `identityProperties` | [`IdentityPropertiesFunction`](README.md#identitypropertiesfunction) | -| `identityReset` | [`IdentityResetFunction`](README.md#identityresetfunction) | -| `init` | [`InitFunction`](README.md#initfunction) | -| `load` | [`LoadFunction`](README.md#loadfunction) | -| `messageClose` | [`MessageCloseFunction`](README.md#messageclosefunction) | -| `messageStart` | [`MessageStartFunction`](README.md#messagestartfunction) | -| `sessionReplayStart` | [`SessionReplayStartFunction`](README.md#sessionreplaystartfunction) | -| `sessionReplayStop` | [`SessionReplayStopFunction`](README.md#sessionreplaystopfunction) | -| `surveyClose` | [`SurveyCloseFunction`](README.md#surveyclosefunction) | -| `surveyStart` | [`SurveyStartFunction`](README.md#surveystartfunction) | -| `targetingDebug` | [`TargetingDebugFunction`](README.md#targetingdebugfunction) | - -___ - -### SessionReplayStartFunction - -Ƭ **SessionReplayStartFunction**: () => `Promise`\<`unknown`\> - -#### Type declaration - -▸ (): `Promise`\<`unknown`\> - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### SessionReplayStopFunction - -Ƭ **SessionReplayStopFunction**: () => `Promise`\<`unknown`\> - -#### Type declaration - -▸ (): `Promise`\<`unknown`\> - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### SurveyCloseFunction - -Ƭ **SurveyCloseFunction**: () => `Promise`\<`unknown`\> - -#### Type declaration - -▸ (): `Promise`\<`unknown`\> - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### SurveyStartFunction - -Ƭ **SurveyStartFunction**: (`surveyId`: `string`, `distributionId?`: `string`, `allowMultipleResponses?`: `boolean`, `hiddenFields?`: `PropertyRecord`, `hooks?`: `HooksSurveyStart`, `language?`: `string`, `selectors?`: `string` \| `string`[]) => `Promise`\<`unknown`\> - -#### Type declaration - -▸ (`surveyId`, `distributionId?`, `allowMultipleResponses?`, `hiddenFields?`, `hooks?`, `language?`, `selectors?`): `Promise`\<`unknown`\> - -##### Parameters - -| Name | Type | -| :------ | :------ | -| `surveyId` | `string` | -| `distributionId?` | `string` | -| `allowMultipleResponses?` | `boolean` | -| `hiddenFields?` | `PropertyRecord` | -| `hooks?` | `HooksSurveyStart` | -| `language?` | `string` | -| `selectors?` | `string` \| `string`[] | - -##### Returns - -`Promise`\<`unknown`\> - -___ - -### TargetingDebugFunction - -Ƭ **TargetingDebugFunction**: () => `Promise`\<`unknown`\> - -#### Type declaration - -▸ (): `Promise`\<`unknown`\> - -##### Returns - -`Promise`\<`unknown`\> - -## Variables - -### SCREEB\_PLUGIN\_KEY - -• `Const` **SCREEB\_PLUGIN\_KEY**: `InjectionKey`\<[`ScreebContextValues`](README.md#screebcontextvalues)\> - -___ - -### ScreebPlugin - -• `Const` **ScreebPlugin**: `Plugin` - -## Functions - -### useScreeb - -▸ **useScreeb**(): [`ScreebContextValues`](README.md#screebcontextvalues) - -#### Returns - -[`ScreebContextValues`](README.md#screebcontextvalues) diff --git a/packages/sdk-vue/package.json b/packages/sdk-vue/package.json index ef4b7a9..4141a05 100644 --- a/packages/sdk-vue/package.json +++ b/packages/sdk-vue/package.json @@ -30,7 +30,6 @@ "types": "dist/es/index.d.ts", "files": [ "dist", - "docs", "README.md" ], "scripts": { @@ -38,7 +37,7 @@ "build:doc": "typedoc src/index.ts", "clean": "rm -Rf dist", "lint": "eslint .", - "test": "echo '@TODO'" + "test": "echo \"no unit tests yet\"" }, "dependencies": { "@screeb/sdk-browser": "^0.7.0" diff --git a/packages/sdk-vue/readme/screeb-logo.svg b/packages/sdk-vue/readme/screeb-logo.svg new file mode 100644 index 0000000..b74bd6e --- /dev/null +++ b/packages/sdk-vue/readme/screeb-logo.svg @@ -0,0 +1,15 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/packages/sdk-vue/src/plugin.ts b/packages/sdk-vue/src/plugin.ts index da6a3f3..c066a4f 100644 --- a/packages/sdk-vue/src/plugin.ts +++ b/packages/sdk-vue/src/plugin.ts @@ -240,6 +240,9 @@ export const ScreebPlugin: Plugin = { ); const context: ScreebContextValues = { + ScreebId: Screeb.ScreebId, + ScreebMaskText: Screeb.ScreebMaskText, + ScreebNoCapture: Screeb.ScreebNoCapture, close, debug, eventTrack, @@ -251,12 +254,12 @@ export const ScreebPlugin: Plugin = { identityReset, init, load, - surveyClose, - surveyStart, messageClose, messageStart, - sessionReplayStop, sessionReplayStart, + sessionReplayStop, + surveyClose, + surveyStart, targetingDebug, }; diff --git a/packages/sdk-vue/src/types.ts b/packages/sdk-vue/src/types.ts index 43c391d..543cd12 100644 --- a/packages/sdk-vue/src/types.ts +++ b/packages/sdk-vue/src/types.ts @@ -107,6 +107,9 @@ export type MessageStartFunction = ( export type SessionReplayStopFunction = () => Promise; export type SessionReplayStartFunction = () => Promise; export type TargetingDebugFunction = () => Promise; +export type ScreebMaskTextFunction = (element: T) => T; +export type ScreebNoCaptureFunction = (element: T) => T; +export type ScreebIdFunction = (element: T, id: string) => T; /** All Screeb methods provided via `useScreeb()` */ export type ScreebContextValues = { @@ -128,4 +131,7 @@ export type ScreebContextValues = { sessionReplayStart: SessionReplayStartFunction; sessionReplayStop: SessionReplayStopFunction; targetingDebug: TargetingDebugFunction; + ScreebMaskText: ScreebMaskTextFunction; + ScreebNoCapture: ScreebNoCaptureFunction; + ScreebId: ScreebIdFunction; }; diff --git a/packages/typescript-config/package.json b/packages/typescript-config/package.json index e1535ce..147a594 100644 --- a/packages/typescript-config/package.json +++ b/packages/typescript-config/package.json @@ -18,6 +18,7 @@ "sideEffects": false, "type": "module", "dependencies": { - "@rollup/plugin-typescript": "^11.1.6" + "@rollup/plugin-typescript": "^11.1.6", + "@rollup/plugin-terser": "^0.4.4" } } diff --git a/packages/typescript-config/src/rollup.config.js b/packages/typescript-config/src/rollup.config.js index ce476e2..6418509 100644 --- a/packages/typescript-config/src/rollup.config.js +++ b/packages/typescript-config/src/rollup.config.js @@ -3,6 +3,7 @@ import path from 'path' import fs from 'fs' import typescript from '@rollup/plugin-typescript'; +import terser from '@rollup/plugin-terser'; const watcher = (globs) => ({ buildStart () { @@ -18,18 +19,27 @@ const updateVersion = () => ({ } }) +// Keep the `CONSTANTS` binding intact (the updateVersion footer reassigns +// CONSTANTS.version at runtime) and avoid constant-folding its reads. +const minify = () => terser({ + mangle: { reserved: ['CONSTANTS'] }, + compress: { evaluate: false, reduce_vars: false }, +}) + export default { input: "src/index.ts", output: [ { plugins: [ - updateVersion() + updateVersion(), + minify(), ], file: "dist/es/index.mjs", format: "esm" }, { plugins: [ - updateVersion() + updateVersion(), + minify(), ], file: "dist/cjs/index.cjs", format: "cjs" diff --git a/packages/typescript-config/src/tsconfig.json b/packages/typescript-config/src/tsconfig.json index 636266b..2525874 100644 --- a/packages/typescript-config/src/tsconfig.json +++ b/packages/typescript-config/src/tsconfig.json @@ -1,6 +1,6 @@ { "compilerOptions": { - "target": "es5", + "target": "es2020", "lib": [ "dom", "dom.iterable", diff --git a/scripts/build-local-ios-xcframework.mjs b/scripts/build-local-ios-xcframework.mjs new file mode 100755 index 0000000..062ccd6 --- /dev/null +++ b/scripts/build-local-ios-xcframework.mjs @@ -0,0 +1,128 @@ +#!/usr/bin/env node + +import { spawnSync } from "node:child_process"; +import { mkdirSync, mkdtempSync, readdirSync, rmSync, unlinkSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = resolve(dirname(fileURLToPath(import.meta.url)), ".."); + +function argValue(name) { + const index = process.argv.indexOf(name); + return index >= 0 ? process.argv[index + 1] : undefined; +} + +function run(command, args, cwd) { + // Clean environment: when invoked from an Xcode build phase (KMP host app), + // the inherited SDKROOT/ARCHS/TOOLCHAINS poison the nested xcodebuild + // archive of sdk-ios and break its link step. + const env = { + PATH: process.env.PATH, + HOME: process.env.HOME, + TMPDIR: process.env.TMPDIR, + DEVELOPER_DIR: process.env.DEVELOPER_DIR, + }; + const result = spawnSync(command, args, { + cwd, + stdio: "inherit", + env, + }); + if (result.status !== 0) { + process.exit(result.status ?? 1); + } +} + +function removeFilesMatching(path, matcher) { + for (const entry of readdirSync(path, { withFileTypes: true })) { + const child = resolve(path, entry.name); + if (entry.isDirectory()) { + removeFilesMatching(child, matcher); + continue; + } + if (entry.isFile() && matcher(child)) { + unlinkSync(child); + } + } +} + +const sdkIosPath = resolve(argValue("--sdk-ios") || process.env.SCREEB_IOS_SDK_PATH || resolve(root, "../sdk-ios")); +const output = resolve(argValue("--output") || process.env.SCREEB_IOS_XCFRAMEWORK_PATH || resolve(root, ".local/ios/Screeb.xcframework")); +mkdirSync(dirname(output), { recursive: true }); // mkdtemp requires an existing parent +const workDir = mkdtempSync(resolve(dirname(output), ".build-")); +const derivedDataPath = resolve(workDir, "DerivedData"); +const iosArchive = resolve(workDir, "Screeb-iOS.xcarchive"); +const simulatorArchive = resolve(workDir, "Screeb-iOS-simulator.xcarchive"); + +rmSync(output, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 }); +mkdirSync(workDir, { recursive: true }); +mkdirSync(dirname(output), { recursive: true }); + +const archiveSettings = [ + "SKIP_INSTALL=NO", + "BUILD_LIBRARY_FOR_DISTRIBUTION=YES", + "DEPLOYMENT_POSTPROCESSING=YES", + "STRIP_INSTALLED_PRODUCT=YES", + "COPY_PHASE_STRIP=YES", + "STRIP_STYLE=non-global", + "DEBUG_INFORMATION_FORMAT=dwarf-with-dsym", + // preserve-types-as-written: the public class Screeb shadows the module Screeb, + // so module-qualified names (Screeb.InitOptions…) in the emitted swiftinterface + // fail to recompile in Swift host apps consuming the xcframework. + `OTHER_SWIFT_FLAGS=$(inherited) -debug-prefix-map ${sdkIosPath}=. -Xfrontend -module-interface-preserve-types-as-written`, +]; +// SCREEB_TELEMETRY=true adds the SDK's local perf/debug logging (never for +// published builds — this script only produces local test artifacts). +if (process.env.SCREEB_TELEMETRY === "true") { + archiveSettings.push("SWIFT_ACTIVE_COMPILATION_CONDITIONS=$(inherited) SCREEB_TELEMETRY"); +} + +run("xcodebuild", [ + "archive", + "-quiet", + "-project", + "Screeb.xcodeproj", + "-scheme", + "Screeb-Package", + "-destination", + "generic/platform=iOS", + "-sdk", + "iphoneos", + "-archivePath", + iosArchive, + "-derivedDataPath", + derivedDataPath, + ...archiveSettings, +], sdkIosPath); + +run("xcodebuild", [ + "archive", + "-quiet", + "-project", + "Screeb.xcodeproj", + "-scheme", + "Screeb-Package", + "-destination", + "generic/platform=iOS Simulator", + "-sdk", + "iphonesimulator", + "-archivePath", + simulatorArchive, + "-derivedDataPath", + derivedDataPath, + ...archiveSettings, +], sdkIosPath); + +run("xcodebuild", [ + "-create-xcframework", + "-framework", + resolve(iosArchive, "Products/Library/Frameworks/Screeb.framework"), + "-framework", + resolve(simulatorArchive, "Products/Library/Frameworks/Screeb.framework"), + "-output", + output, +], sdkIosPath); + +removeFilesMatching(output, (file) => file.endsWith(".abi.json")); + +// The archives/DerivedData workdir weighs ~200MB per run — never leave it behind. +rmSync(workDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 200 }); diff --git a/scripts/report-sdk-sizes.mjs b/scripts/report-sdk-sizes.mjs new file mode 100755 index 0000000..ece1c89 --- /dev/null +++ b/scripts/report-sdk-sizes.mjs @@ -0,0 +1,211 @@ +#!/usr/bin/env node + +import { spawnSync } from "node:child_process"; +import { + existsSync, + readdirSync, + readFileSync, + statSync, +} from "node:fs"; +import { homedir } from "node:os"; +import { basename, dirname, join, relative, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const versions = JSON.parse(readFileSync(resolve(root, "sdk-versions.json"), "utf8")); +const androidVersion = process.env.SCREEB_ANDROID_SDK_VERSION || versions.native.android; +const mavenRepo = process.env.SCREEB_LOCAL_MAVEN_REPOSITORY || resolve(homedir(), ".m2/repository"); +const shouldBuild = !process.argv.includes("--no-build"); + +const npmSdks = [ + "packages/sdk-browser", + "packages/sdk-react", + "packages/sdk-vue", + "packages/sdk-svelte", + "packages/sdk-angular", + "packages/sdk-reactnative", +]; + +function envWithTooling(extra = {}) { + const env = { ...process.env, SCREEB_USE_LOCAL_SDK: "true", ...extra }; + env.GRADLE_OPTS = `${env.GRADLE_OPTS || ""} -Dorg.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=1g -Dkotlin.compiler.execution.strategy=in-process -Dkotlin.incremental=false`.trim(); + env.KOTLIN_DAEMON_JVMARGS = env.KOTLIN_DAEMON_JVMARGS || "-Xmx2g -XX:MaxMetaspaceSize=1g"; + return env; +} + +function run(command, cwd, options = {}) { + console.log(`\n$ ${command}\n cwd: ${relative(root, cwd) || "."}`); + const result = spawnSync(command, { + cwd, + env: envWithTooling(options.env), + shell: true, + stdio: options.capture ? "pipe" : "inherit", + encoding: options.capture ? "utf8" : undefined, + }); + if (result.status !== 0) { + const output = [result.stdout, result.stderr].filter(Boolean).join("\n"); + throw new Error(output || `Command failed: ${command}`); + } + return result.stdout; +} + +function formatBytes(bytes) { + if (bytes < 1024) { + return `${bytes} B`; + } + if (bytes < 1024 * 1024) { + return `${(bytes / 1024).toFixed(1)} KB`; + } + return `${(bytes / 1024 / 1024).toFixed(2)} MB`; +} + +function walkFiles(path, options = {}) { + if (!existsSync(path)) { + return []; + } + const files = []; + for (const entry of readdirSync(path, { withFileTypes: true })) { + const child = join(path, entry.name); + const relativeChild = relative(path, child); + if (options.excludeNames?.has(entry.name)) { + continue; + } + if (entry.isDirectory()) { + files.push(...walkFiles(child, options)); + continue; + } + if (!options.match || options.match(child, relativeChild)) { + files.push(child); + } + } + return files; +} + +function totalSize(files) { + return files.reduce((total, file) => total + statSync(file).size, 0); +} + +function printSize(label, bytes, suffix = "") { + console.log(`${label}: ${formatBytes(bytes)} (${bytes} bytes)${suffix}`); +} + +function npmPackSize(packagePath) { + const output = run("npm pack --dry-run --json --ignore-scripts", resolve(root, packagePath), { capture: true }); + const [pack] = JSON.parse(output.trim()); + return { + packageSize: pack.size, + unpackedSize: pack.unpackedSize, + entryCount: pack.entryCount, + }; +} + +function buildAll() { + for (const packagePath of npmSdks) { + run("npm run build", resolve(root, packagePath)); + } + + run("./gradlew build --no-daemon", resolve(root, "packages/sdk-kmp")); + run("flutter pub get", resolve(root, "packages/sdk-flutter")); + run("dotnet build packages/sdk-maui/ScreebMaui.csproj -f net9.0-android -c Release", root); + run("dotnet build packages/sdk-maui/ScreebMaui.csproj -f net9.0-ios -c Release", root); +} + +function reportNativeAndroid() { + const aar = resolve(mavenRepo, `app/screeb/sdk/survey/${androidVersion}/survey-${androidVersion}.aar`); + if (!existsSync(aar)) { + console.log(`Android native AAR ${androidVersion}: missing (${aar})`); + return; + } + printSize(`Android native AAR ${androidVersion}`, statSync(aar).size); +} + +function reportNpmSdks() { + for (const packagePath of npmSdks) { + const packageJson = JSON.parse(readFileSync(resolve(root, packagePath, "package.json"), "utf8")); + const size = npmPackSize(packagePath); + printSize( + `${packageJson.name} npm tarball`, + size.packageSize, + `, unpacked ${formatBytes(size.unpackedSize)}, ${size.entryCount} files` + ); + } +} + +function reportFlutter() { + const packagePath = resolve(root, "packages/sdk-flutter"); + const files = walkFiles(packagePath, { + excludeNames: new Set([".dart_tool", ".gradle", "build", ".git"]), + match: (path) => { + const rel = relative(packagePath, path); + return !rel.startsWith("example/") && !rel.includes("/build/"); + }, + }); + printSize("plugin_screeb Flutter package source", totalSize(files), `, ${files.length} files`); +} + +function reportKmp() { + const libs = resolve(root, "packages/sdk-kmp/build/libs"); + const files = walkFiles(libs, { + match: (path) => /\.(jar|klib)$/.test(path), + }); + if (files.length === 0) { + console.log(`Screeb KMP Maven artifacts: missing (${libs})`); + return; + } + printSize("Screeb KMP Maven artifacts", totalSize(files), `, ${files.length} files`); +} + +function reportMaui() { + const packages = walkFiles(resolve(root, "packages/sdk-maui/bin/packages"), { + match: (path) => path.endsWith(".nupkg"), + }); + if (packages.length > 0) { + for (const file of packages) { + printSize(`Screeb.Maui NuGet ${basename(file)}`, statSync(file).size); + } + return; + } + + const dlls = walkFiles(resolve(root, "packages/sdk-maui/bin/Release"), { + match: (path) => /Screeb\.Maui\.dll$/.test(path), + }); + if (dlls.length === 0) { + console.log("Screeb.Maui: no NuGet package or DLL build output found"); + return; + } + for (const file of dlls) { + printSize(`Screeb.Maui ${relative(root, file)}`, statSync(file).size); + } +} + +function reportNativeIos() { + const xcframework = resolve(root, "packages/sdk-kmp/native/ios/Screeb.xcframework"); + const files = walkFiles(xcframework, { + match: (path) => !path.endsWith(".abi.json"), + }); + if (files.length === 0) { + console.log(`iOS native xcframework: missing (${xcframework})`); + return; + } + printSize("iOS native xcframework (all slices)", totalSize(files), `, ${files.length} files`); + + const deviceSlice = resolve(xcframework, "ios-arm64/Screeb.framework"); + const deviceFiles = walkFiles(deviceSlice, { + match: (path) => !path.endsWith(".abi.json"), + }); + if (deviceFiles.length > 0) { + printSize("iOS native app embed estimate (device slice)", totalSize(deviceFiles), `, ${deviceFiles.length} files`); + } +} + +if (shouldBuild) { + buildAll(); +} + +console.log("\nSDK sizes"); +reportNativeAndroid(); +reportNativeIos(); +reportNpmSdks(); +reportFlutter(); +reportKmp(); +reportMaui(); diff --git a/scripts/sync-sdk-versions.mjs b/scripts/sync-sdk-versions.mjs new file mode 100755 index 0000000..3a08b65 --- /dev/null +++ b/scripts/sync-sdk-versions.mjs @@ -0,0 +1,178 @@ +#!/usr/bin/env node + +import { readFileSync, writeFileSync } from "node:fs"; +import { dirname, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const manifestPath = resolve(root, "sdk-versions.json"); +const semverPattern = /^\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?$/; + +function readManifest() { + return JSON.parse(readFileSync(manifestPath, "utf8")); +} + +function writeJson(path, value) { + writeFileSync(path, `${JSON.stringify(value, null, 2)}\n`); +} + +function setByPath(object, dottedPath, value) { + const parts = dottedPath.split("."); + let current = object; + for (const part of parts.slice(0, -1)) { + if (!current[part] || typeof current[part] !== "object") { + throw new Error(`Unknown version path: ${dottedPath}`); + } + current = current[part]; + } + const leaf = parts.at(-1); + if (!leaf || !(leaf in current)) { + throw new Error(`Unknown version path: ${dottedPath}`); + } + current[leaf] = value; +} + +function replaceInFile(file, pattern, replacement) { + const path = resolve(root, file); + const before = readFileSync(path, "utf8"); + if (!pattern.test(before)) { + throw new Error(`No version match found in ${file}`); + } + const after = before.replace(pattern, replacement); + writeIfNeeded(file, before, after); + if (!check && after !== before) { + console.log(`${file}`); + } +} + +function replacePackageVersion(file, version) { + const path = resolve(root, file); + const before = readFileSync(path, "utf8"); + const json = JSON.parse(before); + json.version = version; + const after = `${JSON.stringify(json, null, 2)}\n`; + writeIfNeeded(file, before, after); + if (!check && after !== before) { + console.log(`${file}`); + } +} + +function parseArgs(args) { + const sets = []; + let check = false; + + for (let index = 0; index < args.length; index += 1) { + const arg = args[index]; + if (arg === "--check") { + check = true; + continue; + } + if (arg === "--set") { + const path = args[index + 1]; + const version = args[index + 2]; + if (!path || !version) { + throw new Error("Usage: node scripts/sync-sdk-versions.mjs --set "); + } + sets.push([path, version]); + index += 2; + continue; + } + throw new Error(`Unknown argument: ${arg}`); + } + + return { check, sets }; +} + +const { check, sets } = parseArgs(process.argv.slice(2)); +const manifest = readManifest(); +const changedFiles = []; + +function writeIfNeeded(file, before, after) { + if (after === before) { + return; + } + if (check) { + changedFiles.push(file); + return; + } + writeFileSync(resolve(root, file), after); +} + +for (const [path, version] of sets) { + if (!semverPattern.test(version)) { + throw new Error(`Invalid semantic version for ${path}: ${version}`); + } + setByPath(manifest, path, version); +} + +if (sets.length > 0 && !check) { + writeJson(manifestPath, manifest); + console.log("sdk-versions.json"); +} + +const androidVersion = manifest.native.android; +const iosVersion = manifest.native.ios; +const flutterVersion = manifest.wrappers.flutter; +const kmpVersion = manifest.wrappers.kmp; +const mauiVersion = manifest.wrappers.maui; +const reactNativeVersion = manifest.wrappers.reactNative; +const svelteVersion = manifest.wrappers.svelte; + +replacePackageVersion("packages/sdk-reactnative/package.json", reactNativeVersion); +replacePackageVersion("packages/sdk-svelte/package.json", svelteVersion); +replaceInFile("packages/sdk-flutter/pubspec.yaml", /^version: .+$/m, `version: ${flutterVersion}`); +replaceInFile("packages/sdk-flutter/android/build.gradle", /^version '.+'$/m, `version '${flutterVersion}'`); +replaceInFile("packages/sdk-flutter/android/build.gradle", /api "app\.screeb\.sdk:survey:[^"]+"/, `api "app.screeb.sdk:survey:${androidVersion}"`); +replaceInFile("packages/sdk-flutter/ios/plugin_screeb.podspec", /s\.version\s+=\s+'[^']+'/, `s.version = '${flutterVersion}'`); +replaceInFile("packages/sdk-flutter/ios/plugin_screeb.podspec", /s\.dependency 'Screeb', '[^']+'/, `s.dependency 'Screeb', '${iosVersion}'`); +replaceInFile("packages/sdk-reactnative/android/build.gradle", /api "app\.screeb\.sdk:survey:[^"]+"/, `api "app.screeb.sdk:survey:${androidVersion}"`); +replaceInFile("packages/sdk-reactnative/ScreebReactNative.podspec", /s\.dependency "Screeb", '~> [^']+'/, `s.dependency "Screeb", '~> ${iosVersion}'`); +replaceInFile( + "packages/sdk-reactnative/android/src/main/java/app/screeb/reactnative/ScreebReactNativeModule.kt", + /Screeb\.setSecondarySDK\("react-native", "[^"]+"\)/, + `Screeb.setSecondarySDK("react-native", "${reactNativeVersion}")`, +); +replaceInFile( + "packages/sdk-reactnative/ios/ScreebReactNative.swift", + /Screeb\.setSecondarySDK\(name: "react-native", version: "[^"]+"\)/, + `Screeb.setSecondarySDK(name: "react-native", version: "${reactNativeVersion}")`, +); +replaceInFile( + "packages/sdk-flutter/android/src/main/kotlin/app/screeb/plugin_screeb/PluginScreebPlugin.kt", + /Screeb\.setSecondarySDK\("flutter", "[^"]+"\)/, + `Screeb.setSecondarySDK("flutter", "${flutterVersion}")`, +); +replaceInFile( + "packages/sdk-flutter/ios/Classes/SwiftPluginScreebPlugin.swift", + /Screeb\.setSecondarySDK\(name: "flutter", version: "[^"]+"\)/, + `Screeb.setSecondarySDK(name: "flutter", version: "${flutterVersion}")`, +); +replaceInFile("packages/sdk-kmp/gradle.properties", /^VERSION_NAME=.*$/m, `VERSION_NAME=${kmpVersion}`); +replaceInFile("packages/sdk-kmp/gradle.properties", /^SCREEB_ANDROID_SDK_VERSION=.*$/m, `SCREEB_ANDROID_SDK_VERSION=${androidVersion}`); +replaceInFile("packages/sdk-kmp/gradle.properties", /^SCREEB_IOS_SDK_VERSION=.*$/m, `SCREEB_IOS_SDK_VERSION=${iosVersion}`); +replaceInFile( + "packages/sdk-kmp/src/commonMain/kotlin/app/screeb/sdk/kmp/SdkVersion.kt", + /internal const val SDK_VERSION = "[^"]+"/, + `internal const val SDK_VERSION = "${kmpVersion}"`, +); +replaceInFile( + "packages/sdk-svelte/src/constants.ts", + /const CONSTANTS = \{ version: "[^"]+" \};/, + `const CONSTANTS = { version: "${svelteVersion}" };`, +); +replaceInFile("packages/sdk-maui/ScreebMaui.csproj", /()[^<]+(<\/Version>)/, `$1${mauiVersion}$2`); +replaceInFile( + "packages/sdk-maui/ScreebMaui.csproj", + /(Include="app\.screeb\.sdk:survey"\s*\n\s*Version=")[^"]+(")/, + `$1${androidVersion}$2`, +); +replaceInFile("examples/example-android/app/build.gradle", /implementation 'app\.screeb\.sdk:survey:[^']+'/, `implementation 'app.screeb.sdk:survey:${androidVersion}'`); + +if (check && changedFiles.length > 0) { + console.error(`Version files are not synced:\n${changedFiles.map((file) => `- ${file}`).join("\n")}`); + process.exit(1); +} + +if (check) { + console.log("Version files are synced."); +} diff --git a/scripts/verify-release.mjs b/scripts/verify-release.mjs new file mode 100644 index 0000000..fb3b91f --- /dev/null +++ b/scripts/verify-release.mjs @@ -0,0 +1,185 @@ +#!/usr/bin/env node + +import { spawnSync } from "node:child_process"; +import { existsSync } from "node:fs"; +import { homedir, platform } from "node:os"; +import { dirname, relative, resolve } from "node:path"; +import { fileURLToPath } from "node:url"; + +const root = resolve(dirname(fileURLToPath(import.meta.url)), ".."); +const androidRoot = resolve(root, "../sdk-android"); +const iosRoot = resolve(root, "../sdk-ios"); +const isMac = platform() === "darwin"; + +function envWithTooling(extra = {}) { + const env = { ...process.env, SCREEB_USE_LOCAL_SDK: "true", ...extra }; + if (!env.SCREEB_LOCAL_MAVEN_REPOSITORY) { + env.SCREEB_LOCAL_MAVEN_REPOSITORY = resolve(homedir(), ".m2/repository"); + } + return env; +} + +function defaultIosTestDestination() { + if (process.env.SCREEB_IOS_TEST_DESTINATION) { + return process.env.SCREEB_IOS_TEST_DESTINATION; + } + + const result = spawnSync("xcrun simctl list devices available --json", { + shell: true, + encoding: "utf8", + }); + if (result.status !== 0) { + return "platform=iOS Simulator,name=iPhone 17 Pro,OS=latest"; + } + + const devices = JSON.parse(result.stdout); + for (const [runtime, runtimeDevices] of Object.entries(devices.devices ?? {})) { + if (!runtime.includes("iOS")) { + continue; + } + const device = runtimeDevices.find((candidate) => candidate.isAvailable && candidate.name.startsWith("iPhone")); + if (!device) { + continue; + } + const os = runtime.match(/iOS-(\d+-\d+)/)?.[1]?.replace("-", "."); + return `platform=iOS Simulator,name=${device.name}${os ? `,OS=${os}` : ""}`; + } + + return "platform=iOS Simulator,name=iPhone 17 Pro,OS=latest"; +} + +const checks = [ + { + scope: "versions", + label: "version manifest is synced", + cwd: root, + command: "npm run versions:check", + }, + { + scope: "android", + label: "Android SDK release gates", + cwd: androidRoot, + command: "./gradlew :sdk:testDebugUnitTest :sdk:lintRelease :sdk:assembleRelease --no-daemon", + skip: () => !existsSync(resolve(androidRoot, "settings.gradle")) && !existsSync(resolve(androidRoot, "settings.gradle.kts")), + reason: "sdk-android checkout not found next to sdk", + }, + { + scope: "flutter", + label: "Flutter wrapper analyze/tests", + cwd: resolve(root, "packages/sdk-flutter"), + command: "flutter analyze && flutter test", + }, + { + scope: "react-native", + label: "React Native wrapper build/typecheck", + cwd: root, + command: "npm run build --workspace=@screeb/react-native && npm run typecheck --workspace=@screeb/react-native", + }, + { + scope: "kmp", + label: "KMP wrapper tests with local native SDKs", + cwd: resolve(root, "packages/sdk-kmp"), + command: "./gradlew allTests --no-daemon", + }, + { + scope: "maui", + label: "MAUI unit tests", + cwd: root, + command: "dotnet test packages/sdk-maui/tests/ScreebUtilsTests.csproj", + }, + { + scope: "maui", + label: "MAUI Android release build", + cwd: root, + command: "dotnet build packages/sdk-maui/ScreebMaui.csproj -f net9.0-android -c Release", + }, + { + scope: "maui-ios", + label: "MAUI iOS release build", + cwd: root, + command: "dotnet build packages/sdk-maui/ScreebMaui.csproj -f net9.0-ios -c Release", + skip: () => !isMac, + reason: "iOS build requires macOS/Xcode", + }, + { + scope: "ios", + label: "iOS SDK tests", + cwd: iosRoot, + command: () => `xcodebuild test -scheme Screeb-Package -destination '${defaultIosTestDestination()}' -quiet`, + skip: () => !isMac || !existsSync(resolve(iosRoot, "Package.swift")), + reason: "sdk-ios checkout or macOS/Xcode toolchain not available", + }, +]; + +function parseArgs() { + const scopes = new Set(); + let listOnly = false; + + for (const arg of process.argv.slice(2)) { + if (arg === "--list") { + listOnly = true; + } else if (arg.startsWith("--scope=")) { + for (const scope of arg.slice("--scope=".length).split(",")) { + if (scope) scopes.add(scope); + } + } else { + throw new Error(`Unknown argument: ${arg}`); + } + } + + return { listOnly, scopes }; +} + +function selectedChecks(scopes) { + if (scopes.size === 0) { + return checks; + } + return checks.filter((check) => scopes.has(check.scope)); +} + +function printMatrix(items) { + console.log("Release validation matrix"); + for (const check of items) { + const status = check.skip?.() ? `skip: ${check.reason}` : "run"; + const command = typeof check.command === "function" ? check.command() : check.command; + console.log(`- [${check.scope}] ${check.label}: ${status}`); + console.log(` ${relative(root, check.cwd) || "."} $ ${command}`); + } +} + +function run(check) { + if (check.skip?.()) { + console.log(`\n- SKIP ${check.label}: ${check.reason}`); + return; + } + + console.log(`\n- RUN ${check.label}`); + console.log(` cwd: ${relative(root, check.cwd) || "."}`); + const command = typeof check.command === "function" ? check.command() : check.command; + console.log(` $ ${command}`); + + const result = spawnSync(command, { + cwd: check.cwd, + env: envWithTooling(), + shell: true, + stdio: "inherit", + }); + if (result.status !== 0) { + throw new Error(`${check.label} failed with exit code ${result.status}`); + } +} + +const { listOnly, scopes } = parseArgs(); +const items = selectedChecks(scopes); +if (items.length === 0) { + throw new Error(`No checks matched scopes: ${[...scopes].join(", ")}`); +} + +printMatrix(items); + +if (!listOnly) { + for (const check of items) { + run(check); + } + console.log("\nRelease validation passed."); +} diff --git a/sdk-versions.json b/sdk-versions.json new file mode 100644 index 0000000..24df724 --- /dev/null +++ b/sdk-versions.json @@ -0,0 +1,13 @@ +{ + "native": { + "android": "4.0.2", + "ios": "4.0.2" + }, + "wrappers": { + "flutter": "4.0.2", + "kmp": "4.0.2", + "maui": "4.0.2", + "reactNative": "4.0.2", + "svelte": "0.1.0" + } +}