Skip to content

Commit 21bc88b

Browse files
committed
feat: add new areTokenSourceFetchOptionsEqual function
This function is needed for use in `components-js` so I can fix an issue reported by Thom [here](livekit-examples/agent-starter-react#298 (comment)) related to the useSession return not being a stable reference due to fetch options changing every render.
1 parent 2a470e7 commit 21bc88b

File tree

2 files changed

+68
-2
lines changed

2 files changed

+68
-2
lines changed

src/room/token-source/utils.test.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { TokenSourceResponse } from '@livekit/protocol';
22
import { describe, expect, it } from 'vitest';
3-
import { decodeTokenPayload, isResponseTokenValid } from './utils';
3+
import { areTokenSourceFetchOptionsEqual, decodeTokenPayload, isResponseTokenValid } from './utils';
44

55
// Test JWTs created for test purposes only.
66
// None of these actually auth against anything.
@@ -61,3 +61,37 @@ describe('decodeTokenPayload', () => {
6161
expect(payload.roomConfig?.agents![0].metadata).toBe('test agent metadata');
6262
});
6363
});
64+
65+
describe('areTokenSourceFetchOptionsEqual', () => {
66+
it('should ensure two identical options objects of different references are equal', () => {
67+
expect(
68+
areTokenSourceFetchOptionsEqual(
69+
{ agentName: 'my agent name' },
70+
{ agentName: 'my agent name' },
71+
),
72+
).to.equal(true);
73+
});
74+
it('should ensure two empty options objects are equal', () => {
75+
expect(areTokenSourceFetchOptionsEqual({}, {})).to.equal(true);
76+
});
77+
it('should ensure empty on the left and filled on the right are not equal', () => {
78+
expect(areTokenSourceFetchOptionsEqual({}, { agentName: 'my agent name' })).to.equal(false);
79+
});
80+
it('should ensure filled on the left and empty on the right are not equal', () => {
81+
expect(areTokenSourceFetchOptionsEqual({ agentName: 'my agent name' }, {})).to.equal(false);
82+
});
83+
it('should ensure objects with different keys/values are not equal', () => {
84+
expect(
85+
areTokenSourceFetchOptionsEqual(
86+
{ agentName: 'foo' },
87+
{ agentName: 'bar', agentMetadata: 'baz' },
88+
),
89+
).to.equal(false);
90+
expect(
91+
areTokenSourceFetchOptionsEqual(
92+
{ agentName: 'bar', agentMetadata: 'baz' },
93+
{ agentName: 'foo' },
94+
),
95+
).to.equal(false);
96+
});
97+
});

src/room/token-source/utils.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { RoomConfiguration, type TokenSourceResponse } from '@livekit/protocol';
22
import { decodeJwt } from 'jose';
3-
import type { RoomConfigurationObject, TokenPayload } from './types';
3+
import type { RoomConfigurationObject, TokenPayload, TokenSourceFetchOptions } from './types';
44

55
const ONE_SECOND_IN_MILLISECONDS = 1000;
66
const ONE_MINUTE_IN_MILLISECONDS = 60 * ONE_SECOND_IN_MILLISECONDS;
@@ -39,3 +39,35 @@ export function decodeTokenPayload(token: string) {
3939

4040
return mappedPayload;
4141
}
42+
43+
/** Given two TokenSourceFetchOptions values, check to see if they are deep equal. */
44+
export function areTokenSourceFetchOptionsEqual(
45+
a: TokenSourceFetchOptions,
46+
b: TokenSourceFetchOptions,
47+
) {
48+
const allKeysSet = new Set([...Object.keys(a), ...Object.keys(b)]) as Set<
49+
keyof TokenSourceFetchOptions
50+
>;
51+
52+
for (const key of allKeysSet) {
53+
switch (key) {
54+
case 'roomName':
55+
case 'participantName':
56+
case 'participantIdentity':
57+
case 'participantMetadata':
58+
case 'participantAttributes':
59+
case 'agentName':
60+
case 'agentMetadata':
61+
if (a[key] !== b[key]) {
62+
return false;
63+
}
64+
break;
65+
default:
66+
// ref: https://stackoverflow.com/a/58009992
67+
const exhaustiveCheckedKey: never = key;
68+
throw new Error(`Options key ${exhaustiveCheckedKey} not being checked for equality!`);
69+
}
70+
}
71+
72+
return true;
73+
}

0 commit comments

Comments
 (0)