From e02bc29bf57819f03dbcf25770235e3f4f39b8d4 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 Jun 2026 13:48:18 +0200 Subject: [PATCH 01/14] feat(sdk-browser): add ScreebMaskText, ScreebNoCapture and ScreebId DOM helpers --- packages/sdk-browser/docs/README.md | 78 +++++++++++++++++++++++++++++ packages/sdk-browser/src/index.ts | 26 ++++++++++ 2 files changed, 104 insertions(+) diff --git a/packages/sdk-browser/docs/README.md b/packages/sdk-browser/docs/README.md index bfb1235..1effaad 100644 --- a/packages/sdk-browser/docs/README.md +++ b/packages/sdk-browser/docs/README.md @@ -46,6 +46,9 @@ ### Functions +- [ScreebId](README.md#screebid) +- [ScreebMaskText](README.md#screebmasktext) +- [ScreebNoCapture](README.md#screebnocapture) - [close](README.md#close) - [debug](README.md#debug) - [eventTrack](README.md#eventtrack) @@ -617,6 +620,81 @@ ___ ## Functions +### ScreebId + +▸ **ScreebId**\<`T`\>(`element`, `id`): `T` + +Sets a stable Screeb element ID for IAM targeting and replay context. + +#### Type parameters + +| Name | Type | +| :------ | :------ | +| `T` | extends `Element` | + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `element` | `T` | +| `id` | `string` | + +#### Returns + +`T` + +___ + +### ScreebMaskText + +▸ **ScreebMaskText**\<`T`\>(`element`): `T` + +Marks an element as sensitive in Screeb session replay. + +The tag records it with masked text content. + +#### Type parameters + +| Name | Type | +| :------ | :------ | +| `T` | extends `Element` | + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `element` | `T` | + +#### Returns + +`T` + +___ + +### ScreebNoCapture + +▸ **ScreebNoCapture**\<`T`\>(`element`): `T` + +Excludes an element from Screeb session replay capture. + +#### Type parameters + +| Name | Type | +| :------ | :------ | +| `T` | extends `Element` | + +#### Parameters + +| Name | Type | +| :------ | :------ | +| `element` | `T` | + +#### Returns + +`T` + +___ + ### close ▸ **close**(): `void` \| `Promise`\<`unknown`\> diff --git a/packages/sdk-browser/src/index.ts b/packages/sdk-browser/src/index.ts index 7ef7267..fa97919 100644 --- a/packages/sdk-browser/src/index.ts +++ b/packages/sdk-browser/src/index.ts @@ -543,6 +543,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. * From 227d9159544d0ab7d53c89431d37df6b7b42722b Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 Jun 2026 13:48:37 +0200 Subject: [PATCH 02/14] feat(*): recover Svelte SDK, example and publish workflow from dev --- .github/workflows/publish-svelte.yml | 23 +++ examples/example-svelte/.eslintrc | 8 + examples/example-svelte/README.md | 27 +++ examples/example-svelte/index.html | 21 +++ examples/example-svelte/package.json | 44 +++++ examples/example-svelte/src/App.svelte | 27 +++ examples/example-svelte/src/ScreebDemo.svelte | 103 +++++++++++ examples/example-svelte/src/app.css | 41 +++++ examples/example-svelte/src/main.ts | 9 + examples/example-svelte/src/vite-env.d.ts | 2 + examples/example-svelte/svelte.config.js | 7 + examples/example-svelte/tsconfig.json | 22 +++ examples/example-svelte/tsconfig.node.json | 9 + examples/example-svelte/vite.config.ts | 7 + package-lock.json | 146 +++++++++------ packages/sdk-svelte/.eslintrc | 8 + packages/sdk-svelte/README.md | 123 +++++++++++++ packages/sdk-svelte/package.json | 64 +++++++ packages/sdk-svelte/readme/screeb-logo.svg | 15 ++ packages/sdk-svelte/src/client.ts | 166 ++++++++++++++++++ packages/sdk-svelte/src/constants.ts | 3 + packages/sdk-svelte/src/context.ts | 35 ++++ packages/sdk-svelte/src/index.ts | 8 + packages/sdk-svelte/src/logger.ts | 8 + packages/sdk-svelte/src/types.ts | 132 ++++++++++++++ packages/sdk-svelte/src/utils.ts | 1 + packages/sdk-svelte/tsconfig.json | 10 ++ 27 files changed, 1019 insertions(+), 50 deletions(-) create mode 100644 .github/workflows/publish-svelte.yml create mode 100644 examples/example-svelte/.eslintrc create mode 100644 examples/example-svelte/README.md create mode 100644 examples/example-svelte/index.html create mode 100644 examples/example-svelte/package.json create mode 100644 examples/example-svelte/src/App.svelte create mode 100644 examples/example-svelte/src/ScreebDemo.svelte create mode 100644 examples/example-svelte/src/app.css create mode 100644 examples/example-svelte/src/main.ts create mode 100644 examples/example-svelte/src/vite-env.d.ts create mode 100644 examples/example-svelte/svelte.config.js create mode 100644 examples/example-svelte/tsconfig.json create mode 100644 examples/example-svelte/tsconfig.node.json create mode 100644 examples/example-svelte/vite.config.ts create mode 100644 packages/sdk-svelte/.eslintrc create mode 100644 packages/sdk-svelte/README.md create mode 100644 packages/sdk-svelte/package.json create mode 100644 packages/sdk-svelte/readme/screeb-logo.svg create mode 100644 packages/sdk-svelte/src/client.ts create mode 100644 packages/sdk-svelte/src/constants.ts create mode 100644 packages/sdk-svelte/src/context.ts create mode 100644 packages/sdk-svelte/src/index.ts create mode 100644 packages/sdk-svelte/src/logger.ts create mode 100644 packages/sdk-svelte/src/types.ts create mode 100644 packages/sdk-svelte/src/utils.ts create mode 100644 packages/sdk-svelte/tsconfig.json diff --git a/.github/workflows/publish-svelte.yml b/.github/workflows/publish-svelte.yml new file mode 100644 index 0000000..4034898 --- /dev/null +++ b/.github/workflows/publish-svelte.yml @@ -0,0 +1,23 @@ +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@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22' + registry-url: 'https://registry.npmjs.org' + - run: npm install -g npm@latest + - run: npm install --ignore-scripts + - run: npm run build + - run: npm publish --workspace=packages/sdk-svelte --access public --provenance diff --git a/examples/example-svelte/.eslintrc b/examples/example-svelte/.eslintrc new file mode 100644 index 0000000..903ef04 --- /dev/null +++ b/examples/example-svelte/.eslintrc @@ -0,0 +1,8 @@ +{ + "ignorePatterns": [ + "dist" + ], + "extends": [ + "@screeb/eslint-config" + ] +} diff --git a/examples/example-svelte/README.md b/examples/example-svelte/README.md new file mode 100644 index 0000000..1399f61 --- /dev/null +++ b/examples/example-svelte/README.md @@ -0,0 +1,27 @@ +# `@screeb/sdk-svelte-example` + +Screeb's example app for Svelte sdk. + +> Documentation: [developers.screeb.app/sdk-svelte/install](https://developers.screeb.app/sdk-svelte/install) + +## Get started + +### Before get started + +This package requires the installation of the whole mono-repository. + +See [getting started guide](../../README.md#get-started) for more information. + +### Getting set up + +Just run + +```bash +npm start +``` + +Then go to [http://localhost:5173/](http://localhost:5173/) + +## License + +Released under [MIT License](../../LICENSE). diff --git a/examples/example-svelte/index.html b/examples/example-svelte/index.html new file mode 100644 index 0000000..f4ce244 --- /dev/null +++ b/examples/example-svelte/index.html @@ -0,0 +1,21 @@ + + + + + + + Screeb SDK Svelte example + + + + + + + +
+ + + diff --git a/examples/example-svelte/package.json b/examples/example-svelte/package.json new file mode 100644 index 0000000..7a11055 --- /dev/null +++ b/examples/example-svelte/package.json @@ -0,0 +1,44 @@ +{ + "name": "@screeb/sdk-svelte-example", + "version": "0.1.0", + "private": true, + "description": "Screeb's example app for Svelte sdk.", + "homepage": "https://screeb.app", + "bugs": { + "url": "https://github.com/ScreebApp/sdk-js/issues", + "email": "support@screeb.app" + }, + "repository": { + "type": "git", + "url": "https://github.com/ScreebApp/sdk-js.git", + "directory": "examples/example-svelte" + }, + "license": "MIT", + "author": "Screeb's frontend team", + "type": "module", + "scripts": { + "build": "vite build", + "clean": "rm -Rf dist", + "lint": "eslint \"src/**/*.{ts,js}\"", + "start": "vite" + }, + "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" + } +} diff --git a/examples/example-svelte/src/App.svelte b/examples/example-svelte/src/App.svelte new file mode 100644 index 0000000..f465065 --- /dev/null +++ b/examples/example-svelte/src/App.svelte @@ -0,0 +1,27 @@ + + + diff --git a/examples/example-svelte/src/ScreebDemo.svelte b/examples/example-svelte/src/ScreebDemo.svelte new file mode 100644 index 0000000..776ff00 --- /dev/null +++ b/examples/example-svelte/src/ScreebDemo.svelte @@ -0,0 +1,103 @@ + + +
+
S
+

Hello you!

+ +

Welcome aboard!

+

+ This Svelte example loads Screeb through @screeb/sdk-svelte, + initializes a test user, tracks an event, updates properties, and assigns a + group. +

+ + {#if lastIdentity} +
{lastIdentity}
+ {/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/package-lock.json b/package-lock.json index 1f68e87..4f04bf6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2354,6 +2354,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", @@ -10717,7 +10762,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10731,7 +10775,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10743,7 +10786,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10770,7 +10812,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10784,7 +10825,6 @@ "cpu": [ "arm" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10824,7 +10864,6 @@ "cpu": [ "ppc64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10838,7 +10877,6 @@ "cpu": [ "riscv64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10852,7 +10890,6 @@ "cpu": [ "s390x" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10905,7 +10942,6 @@ "cpu": [ "ia32" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -10981,6 +11017,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 @@ -11288,7 +11332,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=10" } @@ -11305,7 +11348,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": ">=10" } @@ -11322,7 +11364,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } @@ -11339,7 +11380,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } @@ -11356,7 +11396,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } @@ -11373,7 +11412,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } @@ -11390,7 +11428,6 @@ "os": [ "linux" ], - "peer": true, "engines": { "node": ">=10" } @@ -11407,7 +11444,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=10" } @@ -11424,7 +11460,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=10" } @@ -11441,7 +11476,6 @@ "os": [ "win32" ], - "peer": true, "engines": { "node": ">=10" } @@ -16952,7 +16986,6 @@ "version": "0.1.8", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "prr": "~1.0.1" }, @@ -20972,7 +21005,6 @@ "version": "0.5.5", "license": "MIT", "optional": true, - "peer": true, "bin": { "image-size": "bin/image-size.js" }, @@ -23314,7 +23346,6 @@ "version": "2.1.0", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "pify": "^4.0.1", "semver": "^5.6.0" @@ -23327,7 +23358,6 @@ "version": "1.6.0", "license": "MIT", "optional": true, - "peer": true, "bin": { "mime": "cli.js" }, @@ -23339,7 +23369,6 @@ "version": "4.0.1", "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">=6" } @@ -23348,7 +23377,6 @@ "version": "5.7.2", "license": "ISC", "optional": true, - "peer": true, "bin": { "semver": "bin/semver" } @@ -25080,7 +25108,6 @@ "version": "3.2.0", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "debug": "^3.2.6", "iconv-lite": "^0.6.3", @@ -25097,7 +25124,6 @@ "version": "3.2.7", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -25106,7 +25132,6 @@ "version": "0.6.3", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -26941,8 +26966,7 @@ "node_modules/prr": { "version": "1.0.1", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/psl": { "version": "1.9.0", @@ -29183,7 +29207,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -29197,7 +29220,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -29211,7 +29233,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -29225,7 +29246,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -29239,7 +29259,6 @@ "cpu": [ "arm64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -29253,7 +29272,6 @@ "cpu": [ "x64" ], - "dev": true, "license": "MIT", "optional": true, "os": [ @@ -30751,20 +30769,6 @@ } } }, - "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==", - "license": "MIT", - "optional": true, - "peer": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/svelte-eslint-parser": { "version": "0.41.1", "dev": true, @@ -35773,6 +35777,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.6.0" + }, + "devDependencies": { + "@screeb/eslint-config": "^0.1.6", + "@screeb/typescript-config": "^0.1.10", + "@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.2.1", 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..e29e7e4 --- /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.com. + +## 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..a639c49 --- /dev/null +++ b/packages/sdk-svelte/package.json @@ -0,0 +1,64 @@ +{ + "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" + }, + "dependencies": { + "@screeb/sdk-browser": "^0.6.0" + }, + "devDependencies": { + "@screeb/eslint-config": "^0.1.6", + "@screeb/typescript-config": "^0.1.10", + "@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": "." + } +} From 81f8c79ccc4aad78d7ae21c6d7d0a54483efdcdf Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 Jun 2026 13:48:42 +0200 Subject: [PATCH 03/14] feat(*): recover SDK version-sync tooling from dev --- package.json | 2 + scripts/sync-sdk-versions.mjs | 178 ++++++++++++++++++++++++++++++++++ sdk-versions.json | 13 +++ 3 files changed, 193 insertions(+) create mode 100755 scripts/sync-sdk-versions.mjs create mode 100644 sdk-versions.json diff --git a/package.json b/package.json index 6895085..b4658b3 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,8 @@ "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", "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", "version": "lerna version && npm run build && npm run push:release", 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/sdk-versions.json b/sdk-versions.json new file mode 100644 index 0000000..98b1edb --- /dev/null +++ b/sdk-versions.json @@ -0,0 +1,13 @@ +{ + "native": { + "android": "4.0.0", + "ios": "4.0.0" + }, + "wrappers": { + "flutter": "3.1.0", + "kmp": "0.1.0", + "maui": "0.1.0", + "reactNative": "3.3.1", + "svelte": "0.1.0" + } +} From 67ade43e9ed35a0119e8d1e4a261100cd5fd7865 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 Jun 2026 13:51:32 +0200 Subject: [PATCH 04/14] fix(*): use npm install in CI to tolerate lockfile/npm version skew --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 834d3c7..c376537 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -35,7 +35,7 @@ jobs: - uses: actions/setup-node@v4 with: node-version: '20' - - run: npm ci --ignore-scripts + - run: npm install --ignore-scripts - run: npm run build - run: npm run lint - run: npm test From 1122a14b3b52937d653e5b462c0e0ed87ac4dc71 Mon Sep 17 00:00:00 2001 From: Alex Date: Thu, 18 Jun 2026 13:58:56 +0200 Subject: [PATCH 05/14] feat(*): recover release/size/local-ios helper scripts from dev --- examples/example-expo/app/index.tsx | 150 +++++++++++++ .../plugins/withScreebLocalSdk.js | 101 +++++++++ examples/example-ionic/.eslintrc | 12 + examples/example-vue/.eslintrc | 12 + package.json | 2 + packages/sdk-maui/readme/screeb-logo.svg | 15 ++ packages/sdk-vue/readme/screeb-logo.svg | 15 ++ scripts/build-local-ios-xcframework.mjs | 106 +++++++++ scripts/report-sdk-sizes.mjs | 211 ++++++++++++++++++ scripts/verify-release.mjs | 185 +++++++++++++++ 10 files changed, 809 insertions(+) create mode 100644 examples/example-expo/app/index.tsx create mode 100644 examples/example-expo/plugins/withScreebLocalSdk.js create mode 100644 examples/example-ionic/.eslintrc create mode 100644 examples/example-vue/.eslintrc create mode 100644 packages/sdk-maui/readme/screeb-logo.svg create mode 100644 packages/sdk-vue/readme/screeb-logo.svg create mode 100755 scripts/build-local-ios-xcframework.mjs create mode 100755 scripts/report-sdk-sizes.mjs create mode 100644 scripts/verify-release.mjs diff --git a/examples/example-expo/app/index.tsx b/examples/example-expo/app/index.tsx new file mode 100644 index 0000000..b190994 --- /dev/null +++ b/examples/example-expo/app/index.tsx @@ -0,0 +1,150 @@ +import * as Screeb from "@screeb/react-native"; +import { useEffect } from "react"; +import { + Alert, + Button, + ScrollView, + StyleSheet, + Text, + View, +} from "react-native"; + +const PROJECT_TOKEN = "0e2b609a-8dce-4695-a80f-966fbfa87a88"; +const RESPONDENT_ID = "0021de43-6e44-443c-9903-2ab99f9c4233"; +const SURVEY_ID = "8dd42ae1-f716-429c-9843-fad62adf2ac4"; +const USER_PROPERTIES = { + locale: "fr-FR", + premium: true, +}; + +const initScreeb = async () => { + try { + await Screeb.initSdk( + PROJECT_TOKEN, + RESPONDENT_ID, + USER_PROPERTIES, + { + onReady: (payload: unknown) => { + console.log("Screeb ready", payload); + }, + onSurveyDisplayAllowed: () => { + console.log("Survey display allowed"); + return true; + }, + version: "1.0.0", + }, + {}, + ); + } catch (error) { + console.error("Failed to init Screeb", error); + Alert.alert("Screeb init failed", "Check the Metro logs for details."); + } +}; + +export default function ScreebExpoExample() { + useEffect(() => { + initScreeb(); + }, []); + + return ( + + @screeb/react-native + Expo + + Minimal Expo development build for exercising the Screeb React Native + wrapper. + + + +