sabha-meetings-react 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.mts CHANGED
@@ -19,8 +19,13 @@ type SabhaConfig = {
19
19
  /** Optional bearer access token */
20
20
  accessToken?: string;
21
21
  refreshToken?: string;
22
- /** Persist/reuse `vm_session` outside the browser */
22
+ /** Persist/reuse `vm_session` (also sent as x-sabha-session for proxies). */
23
23
  sessionCookie?: string;
24
+ /**
25
+ * Persist `vm_session` in localStorage after login and restore on configure.
26
+ * Default true in browsers.
27
+ */
28
+ persistSession?: boolean;
24
29
  /** Send cookies (`credentials: "include"`). Default true. */
25
30
  withCredentials?: boolean;
26
31
  socketPath?: string;
@@ -342,6 +347,7 @@ declare class SabhaClient {
342
347
  getConnection(): MeetingConnection | null;
343
348
  leaveMeeting(): Promise<void>;
344
349
  private applySession;
350
+ private persistSessionCookie;
345
351
  private tryRefresh;
346
352
  private getOrigin;
347
353
  private ensureConfigured;
@@ -427,5 +433,11 @@ declare function toSabhaError(error: unknown, fallback?: string): SabhaError;
427
433
 
428
434
  declare function normalizeMeeting(raw: unknown): Meeting;
429
435
 
430
- export { ContentProtection, DEFAULT_BASE_URL, DEFAULT_MEDIA_POLICY, MediaController, MeetingConnection, SabhaClient, SabhaError, SabhaProvider, createSabhaClient, normalizeMeeting, toSabhaError, useSabha, useSabhaMeetings };
436
+ /** Readable session bridge header (browsers cannot set the Cookie request header). */
437
+ declare const SABHA_SESSION_HEADER = "x-sabha-session";
438
+ declare const SABHA_SESSION_STORAGE_KEY = "sabha_vm_session";
439
+ declare function readStoredSessionCookie(): string | null;
440
+ declare function writeStoredSessionCookie(value: string | null): void;
441
+
442
+ export { ContentProtection, DEFAULT_BASE_URL, DEFAULT_MEDIA_POLICY, MediaController, MeetingConnection, SABHA_SESSION_HEADER, SABHA_SESSION_STORAGE_KEY, SabhaClient, SabhaError, SabhaProvider, createSabhaClient, normalizeMeeting, readStoredSessionCookie, toSabhaError, useSabha, useSabhaMeetings, writeStoredSessionCookie };
431
443
  export type { AuthSession, AuthUser, CaptureAttemptType, CreateMeetingInput, LocalMediaState, LoginInput, MediaPolicy, MediaTrackKind, Meeting, MeetingConnectionOptions, MeetingConnectionState, MeetingInvitee, MeetingOrganizer, MeetingParticipant, MeetingRoomEventMap, RegisterInput, SabhaClientStatus, SabhaConfig, SabhaContextValue, SabhaOrg, SabhaProviderProps, SabhaSocketEvents, SabhaTokens, UpdateMeetingInput, VerifyEmailInput };
package/dist/index.d.ts CHANGED
@@ -19,8 +19,13 @@ type SabhaConfig = {
19
19
  /** Optional bearer access token */
20
20
  accessToken?: string;
21
21
  refreshToken?: string;
22
- /** Persist/reuse `vm_session` outside the browser */
22
+ /** Persist/reuse `vm_session` (also sent as x-sabha-session for proxies). */
23
23
  sessionCookie?: string;
24
+ /**
25
+ * Persist `vm_session` in localStorage after login and restore on configure.
26
+ * Default true in browsers.
27
+ */
28
+ persistSession?: boolean;
24
29
  /** Send cookies (`credentials: "include"`). Default true. */
25
30
  withCredentials?: boolean;
26
31
  socketPath?: string;
@@ -342,6 +347,7 @@ declare class SabhaClient {
342
347
  getConnection(): MeetingConnection | null;
343
348
  leaveMeeting(): Promise<void>;
344
349
  private applySession;
350
+ private persistSessionCookie;
345
351
  private tryRefresh;
346
352
  private getOrigin;
347
353
  private ensureConfigured;
@@ -427,5 +433,11 @@ declare function toSabhaError(error: unknown, fallback?: string): SabhaError;
427
433
 
428
434
  declare function normalizeMeeting(raw: unknown): Meeting;
429
435
 
430
- export { ContentProtection, DEFAULT_BASE_URL, DEFAULT_MEDIA_POLICY, MediaController, MeetingConnection, SabhaClient, SabhaError, SabhaProvider, createSabhaClient, normalizeMeeting, toSabhaError, useSabha, useSabhaMeetings };
436
+ /** Readable session bridge header (browsers cannot set the Cookie request header). */
437
+ declare const SABHA_SESSION_HEADER = "x-sabha-session";
438
+ declare const SABHA_SESSION_STORAGE_KEY = "sabha_vm_session";
439
+ declare function readStoredSessionCookie(): string | null;
440
+ declare function writeStoredSessionCookie(value: string | null): void;
441
+
442
+ export { ContentProtection, DEFAULT_BASE_URL, DEFAULT_MEDIA_POLICY, MediaController, MeetingConnection, SABHA_SESSION_HEADER, SABHA_SESSION_STORAGE_KEY, SabhaClient, SabhaError, SabhaProvider, createSabhaClient, normalizeMeeting, readStoredSessionCookie, toSabhaError, useSabha, useSabhaMeetings, writeStoredSessionCookie };
431
443
  export type { AuthSession, AuthUser, CaptureAttemptType, CreateMeetingInput, LocalMediaState, LoginInput, MediaPolicy, MediaTrackKind, Meeting, MeetingConnectionOptions, MeetingConnectionState, MeetingInvitee, MeetingOrganizer, MeetingParticipant, MeetingRoomEventMap, RegisterInput, SabhaClientStatus, SabhaConfig, SabhaContextValue, SabhaOrg, SabhaProviderProps, SabhaSocketEvents, SabhaTokens, UpdateMeetingInput, VerifyEmailInput };
package/dist/index.js CHANGED
@@ -163,6 +163,30 @@ function toSabhaError(error, fallback = "Sabha request failed") {
163
163
  return new SabhaError(fallback, { details: error });
164
164
  }
165
165
 
166
+ // src/session.ts
167
+ var SABHA_SESSION_HEADER = "x-sabha-session";
168
+ var SABHA_SESSION_STORAGE_KEY = "sabha_vm_session";
169
+ function readStoredSessionCookie() {
170
+ try {
171
+ if (typeof localStorage === "undefined") return null;
172
+ return localStorage.getItem(SABHA_SESSION_STORAGE_KEY);
173
+ } catch {
174
+ return null;
175
+ }
176
+ }
177
+ function writeStoredSessionCookie(value) {
178
+ try {
179
+ if (typeof localStorage === "undefined") return;
180
+ if (!value) localStorage.removeItem(SABHA_SESSION_STORAGE_KEY);
181
+ else localStorage.setItem(SABHA_SESSION_STORAGE_KEY, value);
182
+ } catch {
183
+ }
184
+ }
185
+ function parseVmSession(cookieHeader) {
186
+ const match = /(?:^|,\s*)vm_session=([^;]+)/i.exec(cookieHeader);
187
+ return match?.[1] ? decodeURIComponent(match[1].trim()) : null;
188
+ }
189
+
166
190
  // src/http/client.ts
167
191
  function joinUrl(baseUrl, path) {
168
192
  const base = baseUrl.replace(/\/+$/, "");
@@ -196,15 +220,21 @@ function messageFromBody(body, fallback) {
196
220
  if (typeof record.error === "string") return record.error;
197
221
  return fallback;
198
222
  }
199
- function readSetCookie(response) {
223
+ function isBrowser() {
224
+ const runtime = globalThis;
225
+ return typeof runtime.window !== "undefined" && typeof runtime.document !== "undefined";
226
+ }
227
+ function readSessionFromResponse(response) {
228
+ const bridged = response.headers.get(SABHA_SESSION_HEADER);
229
+ if (bridged?.trim()) return bridged.trim();
200
230
  const anyHeaders = response.headers;
201
231
  const list = typeof anyHeaders.getSetCookie === "function" ? anyHeaders.getSetCookie() : (() => {
202
232
  const single = response.headers.get("set-cookie");
203
233
  return single ? [single] : [];
204
234
  })();
205
235
  for (const entry of list) {
206
- const match = /(?:^|,\s*)vm_session=([^;]+)/i.exec(entry);
207
- if (match?.[1]) return match[1];
236
+ const value = parseVmSession(entry);
237
+ if (value) return value;
208
238
  }
209
239
  return null;
210
240
  }
@@ -218,10 +248,8 @@ var HttpClient = class {
218
248
  Accept: "application/json",
219
249
  ...options.headers
220
250
  };
221
- const runtime = globalThis;
222
- const isBrowser = typeof runtime.window !== "undefined" && typeof runtime.document !== "undefined";
223
251
  const origin = this.options.getOrigin();
224
- if (!isBrowser && origin) headers.Origin = origin;
252
+ if (!isBrowser() && origin) headers.Origin = origin;
225
253
  const tokens = this.options.getTokens();
226
254
  const needsAuth = options.auth !== false;
227
255
  if (tokens?.accessToken) {
@@ -233,7 +261,10 @@ var HttpClient = class {
233
261
  );
234
262
  }
235
263
  if (tokens?.sessionCookie) {
236
- headers.Cookie = `vm_session=${tokens.sessionCookie}`;
264
+ headers[SABHA_SESSION_HEADER] = tokens.sessionCookie;
265
+ if (!isBrowser()) {
266
+ headers.Cookie = `vm_session=${tokens.sessionCookie}`;
267
+ }
237
268
  }
238
269
  if (options.body !== void 0) {
239
270
  headers["Content-Type"] = "application/json";
@@ -247,7 +278,7 @@ var HttpClient = class {
247
278
  credentials: this.options.withCredentials ? "include" : "same-origin"
248
279
  }
249
280
  );
250
- const session = readSetCookie(response);
281
+ const session = readSessionFromResponse(response);
251
282
  if (session) this.options.onSessionCookie?.(session);
252
283
  const body = await parseBody(response);
253
284
  if (response.status === 401 && needsAuth && !retried && this.options.onUnauthorized) {
@@ -832,6 +863,7 @@ var SabhaClient = class {
832
863
  );
833
864
  this.config = {
834
865
  withCredentials: true,
866
+ persistSession: true,
835
867
  ...this.config,
836
868
  ...config,
837
869
  baseUrl
@@ -840,14 +872,12 @@ var SabhaClient = class {
840
872
  ...DEFAULT_MEDIA_POLICY,
841
873
  ...this.config.mediaPolicy
842
874
  };
875
+ const stored = this.config.persistSession !== false ? readStoredSessionCookie() : null;
843
876
  this.tokens = {
844
877
  accessToken: config.accessToken ?? this.tokens?.accessToken,
845
878
  refreshToken: config.refreshToken ?? this.tokens?.refreshToken,
846
- sessionCookie: config.sessionCookie ?? this.tokens?.sessionCookie
879
+ sessionCookie: config.sessionCookie ?? this.tokens?.sessionCookie ?? stored ?? void 0
847
880
  };
848
- if (!this.tokens.accessToken && !this.tokens.sessionCookie && !this.tokens.refreshToken) {
849
- if (!config.accessToken && !config.sessionCookie && !config.refreshToken) ;
850
- }
851
881
  const hasAnyToken = Boolean(
852
882
  this.tokens.accessToken || this.tokens.sessionCookie || this.tokens.refreshToken
853
883
  );
@@ -861,10 +891,7 @@ var SabhaClient = class {
861
891
  this.tokens = tokens;
862
892
  },
863
893
  onSessionCookie: (cookie) => {
864
- this.tokens = {
865
- ...this.tokens ?? {},
866
- sessionCookie: cookie ?? void 0
867
- };
894
+ this.persistSessionCookie(cookie);
868
895
  },
869
896
  onUnauthorized: () => this.tryRefresh()
870
897
  });
@@ -906,28 +933,41 @@ var SabhaClient = class {
906
933
  clearSession() {
907
934
  this.tokens = null;
908
935
  this.user = null;
936
+ this.persistSessionCookie(null);
909
937
  this.activeConnection?.disconnect();
910
938
  this.activeConnection = null;
911
939
  }
912
940
  async login(input) {
913
941
  const session = await this.auth().login(input);
914
942
  this.applySession(session);
943
+ if (session.accessToken) {
944
+ this.persistSessionCookie(this.tokens?.sessionCookie ?? null);
945
+ } else if (session.sessionCookie || this.tokens?.sessionCookie) {
946
+ this.persistSessionCookie(session.sessionCookie ?? this.tokens?.sessionCookie ?? null);
947
+ }
915
948
  try {
916
949
  const me = await this.auth().me();
917
950
  this.user = me;
918
951
  session.user = me;
952
+ session.sessionCookie = this.tokens?.sessionCookie;
953
+ return session;
919
954
  } catch (error) {
920
- this.clearSession();
921
- throw new SabhaError(
922
- "Login returned a user profile but no Sabha session was established. The API did not set `vm_session` (and no JWT token was returned), so POST /api/meetings will return 401 Sign in first. Fix session cookies on sabha-api (Set-Cookie: vm_session; SameSite=None; Secure for cross-origin) or return a bearer token from /api/auth/login.",
923
- {
924
- code: "SESSION_NOT_ESTABLISHED",
925
- status: 401,
926
- cause: error
927
- }
955
+ const hasCredential = Boolean(
956
+ this.tokens?.accessToken || this.tokens?.sessionCookie
928
957
  );
958
+ if (!hasCredential) {
959
+ this.clearSession();
960
+ throw new SabhaError(
961
+ "Login returned a user profile but no auth credential. Need JWT `token` from /api/auth/login (Postman) or Set-Cookie vm_session (exposed to the app as x-sabha-session via /sabha proxy). Without that, POST /api/meetings returns 401 Sign in first.",
962
+ {
963
+ code: "SESSION_NOT_ESTABLISHED",
964
+ status: 401,
965
+ cause: error
966
+ }
967
+ );
968
+ }
969
+ throw error;
929
970
  }
930
- return session;
931
971
  }
932
972
  /** Throws if /api/auth/me fails — call before create/start meeting. */
933
973
  async ensureAuthenticated() {
@@ -1057,6 +1097,23 @@ var SabhaClient = class {
1057
1097
  refreshToken: session.refreshToken ?? this.tokens?.refreshToken,
1058
1098
  sessionCookie: session.sessionCookie ?? this.tokens?.sessionCookie
1059
1099
  };
1100
+ if (this.tokens.sessionCookie) {
1101
+ this.persistSessionCookie(this.tokens.sessionCookie);
1102
+ }
1103
+ }
1104
+ persistSessionCookie(cookie) {
1105
+ if (cookie) {
1106
+ this.tokens = {
1107
+ ...this.tokens ?? {},
1108
+ sessionCookie: cookie
1109
+ };
1110
+ } else if (this.tokens) {
1111
+ const { sessionCookie: _removed, ...rest } = this.tokens;
1112
+ this.tokens = Object.keys(rest).length ? rest : null;
1113
+ }
1114
+ if (this.config.persistSession !== false) {
1115
+ writeStoredSessionCookie(cookie ?? null);
1116
+ }
1060
1117
  }
1061
1118
  async tryRefresh() {
1062
1119
  if (!this.tokens?.refreshToken) return false;
@@ -1119,6 +1176,7 @@ function SabhaProvider({
1119
1176
  const resolvedConfig = {
1120
1177
  baseUrl: DEFAULT_BASE_URL,
1121
1178
  withCredentials: true,
1179
+ persistSession: true,
1122
1180
  ...config
1123
1181
  };
1124
1182
  const client = react.useMemo(
@@ -1456,13 +1514,17 @@ exports.DEFAULT_BASE_URL = DEFAULT_BASE_URL;
1456
1514
  exports.DEFAULT_MEDIA_POLICY = DEFAULT_MEDIA_POLICY;
1457
1515
  exports.MediaController = MediaController;
1458
1516
  exports.MeetingConnection = MeetingConnection;
1517
+ exports.SABHA_SESSION_HEADER = SABHA_SESSION_HEADER;
1518
+ exports.SABHA_SESSION_STORAGE_KEY = SABHA_SESSION_STORAGE_KEY;
1459
1519
  exports.SabhaClient = SabhaClient;
1460
1520
  exports.SabhaError = SabhaError;
1461
1521
  exports.SabhaProvider = SabhaProvider;
1462
1522
  exports.createSabhaClient = createSabhaClient;
1463
1523
  exports.normalizeMeeting = normalizeMeeting;
1524
+ exports.readStoredSessionCookie = readStoredSessionCookie;
1464
1525
  exports.toSabhaError = toSabhaError;
1465
1526
  exports.useSabha = useSabha;
1466
1527
  exports.useSabhaMeetings = useSabhaMeetings;
1528
+ exports.writeStoredSessionCookie = writeStoredSessionCookie;
1467
1529
  //# sourceMappingURL=index.js.map
1468
1530
  //# sourceMappingURL=index.js.map