diff --git a/e2e/react-router/src/routes/root.tsx b/e2e/react-router/src/routes/root.tsx
index e5a16b527..751142950 100644
--- a/e2e/react-router/src/routes/root.tsx
+++ b/e2e/react-router/src/routes/root.tsx
@@ -301,6 +301,42 @@ export default function Root() {
>
LDObserve.stop()
+
+
Session Properties
+
+ Add custom session-level attributes via{' '}
+ LDRecord.addSessionProperties.
+
+
+
+
+
+
)
}
diff --git a/sdk/highlight-run/src/__tests__/record-add-session-properties.test.ts b/sdk/highlight-run/src/__tests__/record-add-session-properties.test.ts
new file mode 100644
index 000000000..2e5887b6f
--- /dev/null
+++ b/sdk/highlight-run/src/__tests__/record-add-session-properties.test.ts
@@ -0,0 +1,49 @@
+import { beforeEach, describe, expect, it, vi } from 'vitest'
+
+vi.mock('../client/workers/highlight-client-worker?worker&inline', () => {
+ class FakeWorker {
+ onmessage = (_e: any) => {}
+ postMessage = vi.fn((_message: any) => null)
+ }
+ return { default: FakeWorker }
+})
+
+import { RecordSDK } from '../sdk/record'
+import { LDRecord } from '../sdk/LDRecord'
+import { MessageType } from '../client/workers/types'
+
+describe('Record addSessionProperties', () => {
+ let recordImpl: RecordSDK
+
+ beforeEach(() => {
+ recordImpl = new RecordSDK({
+ organizationID: '1',
+ environment: 'test',
+ sessionSecureID: 'test-session',
+ })
+ })
+
+ it('posts Properties message with session type to the worker', () => {
+ const postSpy = vi.spyOn((recordImpl as any)._worker, 'postMessage')
+
+ const props = { plan: 'pro', count: 1, active: true }
+ recordImpl.addSessionProperties(props)
+
+ expect(postSpy).toHaveBeenCalled()
+ const call = postSpy.mock.calls[0]?.[0] as any
+ expect(call?.message?.type).toBe(MessageType.Properties)
+ expect(call?.message?.propertyType).toEqual({ type: 'session' })
+ expect(call?.message?.propertiesObject).toMatchObject(props)
+ })
+
+ it('LDRecord proxies addSessionProperties to implementation', () => {
+ // Load the implementation into the LDRecord buffered proxy
+ LDRecord.load(recordImpl)
+
+ const spy = vi.spyOn(recordImpl, 'addSessionProperties')
+ const props = { foo: 'bar' }
+ LDRecord.addSessionProperties(props)
+
+ expect(spy).toHaveBeenCalledWith(props)
+ })
+})
diff --git a/sdk/highlight-run/src/api/record.ts b/sdk/highlight-run/src/api/record.ts
index 023fa1503..247dceb2a 100644
--- a/sdk/highlight-run/src/api/record.ts
+++ b/sdk/highlight-run/src/api/record.ts
@@ -14,6 +14,11 @@ export interface Record {
* Stop the session recording.
*/
stop: () => void
+ /**
+ * Add custom session-level properties. These are attached to the current session
+ * and are searchable, but do not create timeline Track events.
+ */
+ addSessionProperties: (properties: { [key: string]: any }) => void
/**
* Snapshot an HTML