diff --git a/assist/utils/httpHandlers.js b/assist/utils/httpHandlers.js
index 67b5b06f7..c6514e2dc 100644
--- a/assist/utils/httpHandlers.js
+++ b/assist/utils/httpHandlers.js
@@ -27,7 +27,9 @@ const respond = function (req, res, data) {
res.setHeader('Content-Type', 'application/json');
res.end(JSON.stringify(result));
} else {
- res.writeStatus('200 OK').writeHeader('Content-Type', 'application/json').end(JSON.stringify(result));
+ res.cork(() => {
+ res.writeStatus('200 OK').writeHeader('Content-Type', 'application/json').end(JSON.stringify(result));
+ });
}
const duration = performance.now() - req.startTs;
IncreaseTotalRequests();
diff --git a/backend/pkg/integrations/clients/sentry.go b/backend/pkg/integrations/clients/sentry.go
index b70d88ad8..7dcc284eb 100644
--- a/backend/pkg/integrations/clients/sentry.go
+++ b/backend/pkg/integrations/clients/sentry.go
@@ -31,11 +31,40 @@ type SentryEvent struct {
}
func (s *sentryClient) FetchSessionData(credentials interface{}, sessionID uint64) (interface{}, error) {
+ cfg, err := parseSentryConfig(credentials)
+ if err != nil {
+ return nil, err
+ }
+ // Fetch sentry events
+ requestUrl := prepareURLWithParams(cfg, sessionID, true)
+ list, err := makeRequest(cfg, requestUrl)
+ if err != nil {
+ return nil, err
+ }
+ if list == nil || len(list) == 0 {
+ // Fetch sentry issues if no events found
+ requestUrl = prepareURLWithParams(cfg, sessionID, false)
+ list, err = makeRequest(cfg, requestUrl)
+ if err != nil {
+ return nil, err
+ }
+ if list == nil || len(list) == 0 {
+ return nil, fmt.Errorf("no logs found")
+ }
+ }
+ result, err := json.Marshal(list)
+ if err != nil {
+ return nil, err
+ }
+ return result, nil
+}
+
+func parseSentryConfig(credentials interface{}) (sentryConfig, error) {
cfg, ok := credentials.(sentryConfig)
if !ok {
strCfg, ok := credentials.(map[string]interface{})
if !ok {
- return nil, fmt.Errorf("invalid credentials")
+ return cfg, fmt.Errorf("invalid credentials")
}
cfg = sentryConfig{}
if val, ok := strCfg["organization_slug"].(string); ok {
@@ -52,29 +81,37 @@ func (s *sentryClient) FetchSessionData(credentials interface{}, sessionID uint6
}
}
if cfg.URL == "" {
- cfg.URL = "https://sentry.io" // Default to hosted Sentry if not specified
+ cfg.URL = "https://sentry.io"
}
- requestUrl := fmt.Sprintf("%s/api/0/projects/%s/%s/issues/", cfg.URL, cfg.OrganizationSlug, cfg.ProjectSlug)
+ return cfg, nil
+}
- testCallLimit := 1
+func prepareURLWithParams(cfg sentryConfig, sessionID uint64, isEvents bool) string {
+ entities := "issues"
+ if isEvents {
+ entities = "events"
+ }
+ requestUrl := fmt.Sprintf("%s/api/0/projects/%s/%s/%s/", cfg.URL, cfg.OrganizationSlug, cfg.ProjectSlug, entities)
params := url.Values{}
- if sessionID != 0 {
- params.Add("query", fmt.Sprintf("openReplaySession.id:%d", sessionID))
- } else {
- params.Add("per_page", fmt.Sprintf("%d", testCallLimit))
+ querySign := ":"
+ if isEvents {
+ querySign = "="
}
- requestUrl += "?" + params.Encode()
+ if sessionID != 0 {
+ params.Add("query", fmt.Sprintf("openReplaySession.id%s%d", querySign, sessionID))
+ } else {
+ params.Add("per_page", "1")
+ }
+ return requestUrl + "?" + params.Encode()
+}
- // Create a new request
+func makeRequest(cfg sentryConfig, requestUrl string) ([]SentryEvent, error) {
req, err := http.NewRequest("GET", requestUrl, nil)
if err != nil {
return nil, fmt.Errorf("failed to create request: %v", err)
}
-
- // Add Authorization header
req.Header.Set("Authorization", "Bearer "+cfg.Token)
- // Send the request
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
@@ -82,30 +119,19 @@ func (s *sentryClient) FetchSessionData(credentials interface{}, sessionID uint6
}
defer resp.Body.Close()
- // Check if the response status is OK
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("failed to fetch logs, status code: %v", resp.StatusCode)
}
- // Read the response body
body, err := io.ReadAll(resp.Body)
if err != nil {
return nil, fmt.Errorf("failed to read response body: %v", err)
}
- // Parse the JSON response
var events []SentryEvent
err = json.Unmarshal(body, &events)
if err != nil {
return nil, fmt.Errorf("failed to parse JSON: %v", err)
}
- if events == nil || len(events) == 0 {
- return nil, fmt.Errorf("no logs found")
- }
-
- result, err := json.Marshal(events)
- if err != nil {
- return nil, err
- }
- return result, nil
+ return events, nil
}
diff --git a/ee/assist/package-lock.json b/ee/assist/package-lock.json
index b25dd04f7..fa4de2957 100644
--- a/ee/assist/package-lock.json
+++ b/ee/assist/package-lock.json
@@ -9,6 +9,7 @@
"version": "v1.12.0-ee",
"license": "Elastic License 2.0 (ELv2)",
"dependencies": {
+ "@fastify/deepmerge": "^2.0.1",
"@maxmind/geoip2-node": "^4.2.0",
"@socket.io/redis-adapter": "^8.2.1",
"express": "^4.21.1",
@@ -39,6 +40,21 @@
"kuler": "^2.0.0"
}
},
+ "node_modules/@fastify/deepmerge": {
+ "version": "2.0.1",
+ "resolved": "https://registry.npmjs.org/@fastify/deepmerge/-/deepmerge-2.0.1.tgz",
+ "integrity": "sha512-hx+wJQr9Ph1hY/dyzY0SxqjumMyqZDlIF6oe71dpRKDHUg7dFQfjG94qqwQ274XRjmUrwKiYadex8XplNHx3CA==",
+ "funding": [
+ {
+ "type": "github",
+ "url": "https://github.com/sponsors/fastify"
+ },
+ {
+ "type": "opencollective",
+ "url": "https://opencollective.com/fastify"
+ }
+ ]
+ },
"node_modules/@maxmind/geoip2-node": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@maxmind/geoip2-node/-/geoip2-node-4.2.0.tgz",
diff --git a/ee/assist/package.json b/ee/assist/package.json
index 74fd0374d..41f4bdbe0 100644
--- a/ee/assist/package.json
+++ b/ee/assist/package.json
@@ -18,6 +18,7 @@
},
"homepage": "https://github.com/openreplay/openreplay#readme",
"dependencies": {
+ "@fastify/deepmerge": "^2.0.1",
"@maxmind/geoip2-node": "^4.2.0",
"@socket.io/redis-adapter": "^8.2.1",
"express": "^4.21.1",
diff --git a/ee/assist/utils/helper-ee.js b/ee/assist/utils/helper-ee.js
index 2700d5f32..4abe98b2a 100644
--- a/ee/assist/utils/helper-ee.js
+++ b/ee/assist/utils/helper-ee.js
@@ -28,7 +28,7 @@ const getBodyFromUWSResponse = async function (res) {
const extractProjectKeyFromRequest = function (req) {
if (process.env.uws === "true") {
if (req.getParameter(0)) {
- debug && console.log(`[WS]where projectKey=${req.getParameter(0)}`);
+ logger.debug(`[WS]where projectKey=${req.getParameter(0)}`);
return req.getParameter(0);
}
} else {
@@ -39,7 +39,7 @@ const extractProjectKeyFromRequest = function (req) {
const extractSessionIdFromRequest = function (req) {
if (process.env.uws === "true") {
if (req.getParameter(1)) {
- debug && console.log(`[WS]where projectKey=${req.getParameter(1)}`);
+ logger.debug(`[WS]where projectKey=${req.getParameter(1)}`);
return req.getParameter(1);
}
} else {
@@ -54,15 +54,15 @@ const extractPayloadFromRequest = async function (req, res) {
};
if (process.env.uws === "true") {
if (req.getQuery("q")) {
- debug && console.log(`[WS]where q=${req.getQuery("q")}`);
+ logger.debug(`[WS]where q=${req.getQuery("q")}`);
filters.query.value = req.getQuery("q");
}
if (req.getQuery("key")) {
- debug && console.log(`[WS]where key=${req.getQuery("key")}`);
+ logger.debug(`[WS]where key=${req.getQuery("key")}`);
filters.query.key = req.getQuery("key");
}
if (req.getQuery("userId")) {
- debug && console.log(`[WS]where userId=${req.getQuery("userId")}`);
+ logger.debug(`[WS]where userId=${req.getQuery("userId")}`);
filters.filter.userID = [req.getQuery("userId")];
}
if (!filters.query.value) {
@@ -88,7 +88,7 @@ const extractPayloadFromRequest = async function (req, res) {
}
filters.filter = helper.objectToObjectOfArrays(filters.filter);
filters.filter = helper.transformFilters(filters.filter);
- debug && console.log("payload/filters:" + JSON.stringify(filters))
+ logger.debug("payload/filters:" + JSON.stringify(filters))
return Object.keys(filters).length > 0 ? filters : undefined;
}
const getAvailableRooms = async function (io) {
diff --git a/frontend/app/components/Assist/AssistView.tsx b/frontend/app/components/Assist/AssistView.tsx
index 37919238d..122ffcee3 100644
--- a/frontend/app/components/Assist/AssistView.tsx
+++ b/frontend/app/components/Assist/AssistView.tsx
@@ -2,8 +2,10 @@ import React from 'react';
import LiveSessionList from 'Shared/LiveSessionList';
import LiveSessionSearch from 'Shared/LiveSessionSearch';
import AssistSearchActions from './AssistSearchActions';
+import usePageTitle from '@/hooks/usePageTitle';
function AssistView() {
+ usePageTitle('Co-Browse - OpenReplay');
return (
diff --git a/frontend/app/components/FFlags/FFlagsList.tsx b/frontend/app/components/FFlags/FFlagsList.tsx
index e73469809..59f359c8d 100644
--- a/frontend/app/components/FFlags/FFlagsList.tsx
+++ b/frontend/app/components/FFlags/FFlagsList.tsx
@@ -1,6 +1,5 @@
import React from 'react';
import { numberWithCommas } from 'App/utils';
-import withPageTitle from 'HOCs/withPageTitle';
import withPermissions from 'HOCs/withPermissions';
import FFlagsListHeader from 'Components/FFlags/FFlagsListHeader';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
@@ -9,8 +8,10 @@ import FFlagItem from './FFlagItem';
import { useStore } from 'App/mstore';
import { observer } from 'mobx-react-lite';
import Select from 'Shared/Select';
+import usePageTitle from '@/hooks/usePageTitle';
function FFlagsList({ siteId }: { siteId: string }) {
+ usePageTitle('Feature Flags - OpenReplay');
const { featureFlagsStore, userStore } = useStore();
React.useEffect(() => {
@@ -31,7 +32,7 @@ function FFlagsList({ siteId }: { siteId: string }) {
options={[
{ label: 'All', value: '0' as const },
{ label: 'Enabled', value: '1' as const },
- { label: 'Disabled', value: '2' as const },
+ { label: 'Disabled', value: '2' as const }
]}
defaultValue={featureFlagsStore.activity}
plain
@@ -45,7 +46,7 @@ function FFlagsList({ siteId }: { siteId: string }) {
{featureFlagsStore.sort.query === ''
- ? "You haven't created any feature flags yet"
+ ? 'You haven\'t created any feature flags yet'
: 'No matching results'}
@@ -121,6 +122,4 @@ function FFlagsList({ siteId }: { siteId: string }) {
);
}
-export default withPageTitle('Feature Flags')(
- withPermissions(['FEATURE_FLAGS'])(observer(FFlagsList))
-);
+export default withPermissions(['FEATURE_FLAGS'])(observer(FFlagsList));
diff --git a/frontend/app/components/Overview/Overview.tsx b/frontend/app/components/Overview/Overview.tsx
index 4f1a776ee..46f622d7c 100644
--- a/frontend/app/components/Overview/Overview.tsx
+++ b/frontend/app/components/Overview/Overview.tsx
@@ -10,6 +10,7 @@ import FlagView from 'Components/FFlags/FlagView/FlagView';
import { observer } from 'mobx-react-lite';
import { useStore } from '@/mstore';
import NotesList from 'Shared/SessionsTabOverview/components/Notes/NoteList';
+import Bookmarks from 'Shared/SessionsTabOverview/components/Bookmarks/Bookmarks';
// @ts-ignore
interface IProps extends RouteComponentProps {
@@ -34,11 +35,17 @@ function Overview({ match: { params } }: IProps) {
return (
+ path={withSiteId(sessions(), siteId)}>
+
+
+
+
+
diff --git a/frontend/app/components/shared/SessionsTabOverview/SessionsTabOverview.tsx b/frontend/app/components/shared/SessionsTabOverview/SessionsTabOverview.tsx
index eda0a13c1..97b806034 100644
--- a/frontend/app/components/shared/SessionsTabOverview/SessionsTabOverview.tsx
+++ b/frontend/app/components/shared/SessionsTabOverview/SessionsTabOverview.tsx
@@ -9,12 +9,14 @@ import { observer } from 'mobx-react-lite';
import NoSessionsMessage from 'Shared/NoSessionsMessage/NoSessionsMessage';
import MainSearchBar from 'Shared/MainSearchBar/MainSearchBar';
import SearchActions from "../SearchActions";
+import usePageTitle from '@/hooks/usePageTitle';
function SessionsTabOverview() {
const [query, setQuery] = React.useState('');
const { aiFiltersStore, searchStore } = useStore();
const appliedFilter = searchStore.instance;
const activeTab = searchStore.activeTab;
+ usePageTitle('Sessions - OpenReplay');
const handleKeyDown = (event: any) => {
if (event.key === 'Enter') {
diff --git a/frontend/app/components/shared/SessionsTabOverview/components/Bookmarks/Bookmarks.tsx b/frontend/app/components/shared/SessionsTabOverview/components/Bookmarks/Bookmarks.tsx
new file mode 100644
index 000000000..760a366b6
--- /dev/null
+++ b/frontend/app/components/shared/SessionsTabOverview/components/Bookmarks/Bookmarks.tsx
@@ -0,0 +1,78 @@
+import React, { useEffect } from 'react';
+import SessionList from 'Shared/SessionsTabOverview/components/SessionList/SessionList';
+import NoteTags from 'Shared/SessionsTabOverview/components/Notes/NoteTags';
+import { Loader, NoContent, Pagination } from 'UI';
+import AnimatedSVG from 'Shared/AnimatedSVG';
+import { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
+import NoteItem from 'Shared/SessionsTabOverview/components/Notes/NoteItem';
+import { useStore } from '@/mstore';
+import { observer } from 'mobx-react-lite';
+import SessionItem from 'Shared/SessionItem/SessionItem';
+import usePageTitle from '@/hooks/usePageTitle';
+
+function Bookmarks() {
+ const { projectsStore, sessionStore, customFieldStore, userStore, searchStore } = useStore();
+ const isEnterprise = userStore.isEnterprise;
+ const isLoggedIn = userStore.isLoggedIn;
+ const bookmarks = sessionStore.bookmarks;
+
+ usePageTitle('Bookmarks - OpenReplay');
+
+ useEffect(() => {
+ void sessionStore.fetchBookmarkedSessions();
+ }, []);
+
+ return (
+
+
+
Bookmarks
+
+
+
+
+ {/* */}
+
+ No sessions bookmarked
+
+ }
+ >
+
+ {bookmarks.list.map((session: any) => (
+
+ {}}
+ // metaList={metaList}
+ // lastPlayedSessionId={lastPlayedSessionId}
+ bookmarked={true}
+ // toggleFavorite={toggleFavorite}
+ />
+
+ ))}
+
+
+
+
+ Showing{' '}
+ {Math.min(bookmarks.list.length, bookmarks.pageSize)} out
+ of {bookmarks.total} sessions.
+
+
sessionStore.updateBookmarksPage(page)}
+ limit={bookmarks.pageSize}
+ debounceRequest={100}
+ />
+
+
+
+
+ );
+}
+
+export default observer(Bookmarks);
diff --git a/frontend/app/components/shared/SessionsTabOverview/components/Notes/NoteList.tsx b/frontend/app/components/shared/SessionsTabOverview/components/Notes/NoteList.tsx
index eb5587d12..b0b339fac 100644
--- a/frontend/app/components/shared/SessionsTabOverview/components/Notes/NoteList.tsx
+++ b/frontend/app/components/shared/SessionsTabOverview/components/Notes/NoteList.tsx
@@ -6,8 +6,10 @@ import { observer } from 'mobx-react-lite';
import { useStore } from 'App/mstore';
import AnimatedSVG, { ICONS } from 'Shared/AnimatedSVG/AnimatedSVG';
import NoteTags from 'Shared/SessionsTabOverview/components/Notes/NoteTags';
+import usePageTitle from '@/hooks/usePageTitle';
function NotesList() {
+ usePageTitle('Notes - OpenReplay');
const { notesStore } = useStore();
React.useEffect(() => {
diff --git a/frontend/app/mstore/searchStore.ts b/frontend/app/mstore/searchStore.ts
index d354ec45d..9fdb08095 100644
--- a/frontend/app/mstore/searchStore.ts
+++ b/frontend/app/mstore/searchStore.ts
@@ -362,6 +362,7 @@ class SearchStore {
if (this.activeTags[0] && this.activeTags[0] !== 'all') {
const tagFilter = filtersMap[FilterKey.ISSUE];
+ tagFilter.type = tagFilter.type.toLowerCase();
tagFilter.value = [
issues_types.find((i: any) => i.type === this.activeTags[0])?.type,
];
diff --git a/frontend/app/mstore/sessionStore.ts b/frontend/app/mstore/sessionStore.ts
index 2a9d12cbc..f48efcd2b 100644
--- a/frontend/app/mstore/sessionStore.ts
+++ b/frontend/app/mstore/sessionStore.ts
@@ -94,12 +94,19 @@ class DevTools {
}
}
-
+interface Bookmarks {
+ list: Session[];
+ page: number;
+ total: number;
+ pageSize: number;
+ loading: boolean;
+}
export default class SessionStore {
userFilter: UserFilter = new UserFilter();
devTools: DevTools = new DevTools();
list: Session[] = [];
+ bookmarks: Bookmarks = { list: [], page: 1, total: 0, pageSize: 10, loading: false };
sessionIds: string[] = [];
current = new Session();
total = 0;
@@ -529,9 +536,33 @@ export default class SessionStore {
this.list = [];
this.total = 0;
this.sessionIds = [];
+ this.bookmarks = { list: [], page: 1, total: 0, pageSize: 10, loading: false };
}
setLastPlayedSessionId = (sessionId: string) => {
this.lastPlayedSessionId = sessionId;
}
+
+ async fetchBookmarkedSessions() {
+ try {
+ this.bookmarks.loading = true;
+ const params = {
+ page: this.bookmarks.page,
+ limit: this.bookmarks.pageSize,
+ bookmarked: true,
+ }
+ const data = await sessionService.getSessions(params);
+ this.bookmarks.list = data.sessions.map((s: any) => new Session(s));
+ this.bookmarks.total = data.total;
+ } catch (e) {
+ console.error(e);
+ } finally {
+ this.bookmarks.loading = false;
+ }
+ }
+
+ updateBookmarksPage(page: number) {
+ this.bookmarks.page = page;
+ void this.fetchBookmarkedSessions();
+ }
}
diff --git a/frontend/package.json b/frontend/package.json
index 32ec5327d..cd3966f26 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -25,7 +25,7 @@
"@ant-design/icons": "^5.2.5",
"@babel/plugin-transform-private-methods": "^7.23.3",
"@floating-ui/react-dom-interactions": "^0.10.3",
- "@medv/finder": "^3.1.0",
+ "@medv/finder": "^4.0.2",
"@sentry/browser": "^5.21.1",
"@svg-maps/world": "^1.0.1",
"@tanstack/react-query": "^5.56.2",
@@ -75,7 +75,7 @@
"recharts": "^2.12.7",
"socket.io-client": "^4.4.1",
"syncod": "^0.0.1",
- "virtua": "^0.35.1"
+ "virtua": "^0.39.2"
},
"devDependencies": {
"@babel/cli": "^7.23.0",
diff --git a/frontend/yarn.lock b/frontend/yarn.lock
index 21acd9eef..1a9de6cb8 100644
--- a/frontend/yarn.lock
+++ b/frontend/yarn.lock
@@ -2549,10 +2549,10 @@ __metadata:
languageName: node
linkType: hard
-"@medv/finder@npm:^3.1.0":
- version: 3.2.0
- resolution: "@medv/finder@npm:3.2.0"
- checksum: 10c1/5088ae315138074fa168c51d4092a17ab962c85fbb7de97bbce396adc99a525b2d9c4d92231419bbd0ee13944f16c39672d81a1d2bce3e9cbeac527cfb6790f0
+"@medv/finder@npm:^4.0.2":
+ version: 4.0.2
+ resolution: "@medv/finder@npm:4.0.2"
+ checksum: 10c1/067a99e0ce8ddecebd554edf36d6211bc630d6cae351ed93b2110c450f2bbea4852887a2978c2f94f6319d7aa055aeb8723e1514e9cc512faa628627930773b2
languageName: node
linkType: hard
@@ -11582,7 +11582,7 @@ __metadata:
"@babel/runtime": "npm:^7.23.2"
"@floating-ui/react-dom-interactions": "npm:^0.10.3"
"@jest/globals": "npm:^29.7.0"
- "@medv/finder": "npm:^3.1.0"
+ "@medv/finder": "npm:^4.0.2"
"@openreplay/sourcemap-uploader": "npm:^3.0.10"
"@sentry/browser": "npm:^5.21.1"
"@svg-maps/world": "npm:^1.0.1"
@@ -11683,7 +11683,7 @@ __metadata:
ts-jest: "npm:^29.0.5"
ts-node: "npm:^10.7.0"
typescript: "npm:^4.6.4"
- virtua: "npm:^0.35.1"
+ virtua: "npm:^0.39.2"
webpack: "npm:^5.96.0"
webpack-cli: "npm:^5.1.4"
webpack-dev-server: "npm:^5.1.0"
@@ -16255,14 +16255,14 @@ __metadata:
languageName: node
linkType: hard
-"virtua@npm:^0.35.1":
- version: 0.35.1
- resolution: "virtua@npm:0.35.1"
+"virtua@npm:^0.39.2":
+ version: 0.39.2
+ resolution: "virtua@npm:0.39.2"
peerDependencies:
react: ">=16.14.0"
react-dom: ">=16.14.0"
solid-js: ">=1.0"
- svelte: ">=4.0"
+ svelte: ">=5.0"
vue: ">=3.2"
peerDependenciesMeta:
react:
@@ -16275,7 +16275,7 @@ __metadata:
optional: true
vue:
optional: true
- checksum: 10c1/ea747bb9a3b6c41cfa08d2d57d7287224e4bbdf815dcf8736f310372df83472c9bbc4b8286341a8b5bc9d9065baf49ded5233b6013b877fbe1f00a8fc9a6ae0c
+ checksum: 10c1/4aae200bea816a167beb2ea0985be1dc6ac6da4d9410bd7d64af816ae4e41bf15d84bdb0f0bbb636b9d8d9ff01f72d6d73c1f38ff80f86143600dc1451ecf76d
languageName: node
linkType: hard
diff --git a/tracker/tracker-reactnative/android/build.gradle b/tracker/tracker-reactnative/android/build.gradle
index 1101c3467..46cbc2878 100644
--- a/tracker/tracker-reactnative/android/build.gradle
+++ b/tracker/tracker-reactnative/android/build.gradle
@@ -90,7 +90,7 @@ dependencies {
//noinspection GradleDynamicVersion
implementation("com.facebook.react:react-native:0.20.1")
implementation("org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version")
- implementation("com.github.openreplay:android-tracker:v1.1.6")
+ implementation("com.github.openreplay:android-tracker:v1.1.7")
}
diff --git a/tracker/tracker-reactnative/android/src/main/java/com/openreplay/reactnative/ReactNativeModule.kt b/tracker/tracker-reactnative/android/src/main/java/com/openreplay/reactnative/ReactNativeModule.kt
index 8919b93a0..edf9e71b2 100644
--- a/tracker/tracker-reactnative/android/src/main/java/com/openreplay/reactnative/ReactNativeModule.kt
+++ b/tracker/tracker-reactnative/android/src/main/java/com/openreplay/reactnative/ReactNativeModule.kt
@@ -116,4 +116,9 @@ class ReactNativeModule(reactContext: ReactApplicationContext) :
duration = durationULong
)
}
+
+ @ReactMethod
+ func sendMessage(type: String, message: String) {
+ OpenReplay.sendMessage(type: type, message: message)
+ }
}
diff --git a/tracker/tracker/.yarn/install-state.gz b/tracker/tracker/.yarn/install-state.gz
index 773e1c417..0955c604f 100644
Binary files a/tracker/tracker/.yarn/install-state.gz and b/tracker/tracker/.yarn/install-state.gz differ
diff --git a/tracker/tracker/CHANGELOG.md b/tracker/tracker/CHANGELOG.md
index 32a4dcbcb..c98ac6a67 100644
--- a/tracker/tracker/CHANGELOG.md
+++ b/tracker/tracker/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 15.0.5
+
+- update medv/finder to 4.0.2 for better support of css-in-js libs
+
## 15.0.4
- support for spritemaps (svg with `use` tags)
diff --git a/tracker/tracker/package.json b/tracker/tracker/package.json
index d0c141ac7..050b8d568 100644
--- a/tracker/tracker/package.json
+++ b/tracker/tracker/package.json
@@ -1,7 +1,7 @@
{
"name": "@openreplay/tracker",
"description": "The OpenReplay tracker main package",
- "version": "15.0.4",
+ "version": "15.0.5",
"keywords": [
"logging",
"replay"
@@ -70,7 +70,7 @@
"typescript": "^5.6.3"
},
"dependencies": {
- "@medv/finder": "^3.2.0",
+ "@medv/finder": "^4.0.2",
"@openreplay/network-proxy": "^1.0.5",
"error-stack-parser": "^2.0.6",
"error-stack-parser-es": "^0.1.5",