@rehers/rehers-roleplay-sdk 2.5.1 → 2.5.2

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/README.md CHANGED
@@ -6,26 +6,32 @@
6
6
  npm install @rehers/rehers-roleplay-sdk
7
7
  ```
8
8
 
9
+ For a working end-to-end example in this repo, use `sdk-demo/`.
10
+
9
11
  ---
10
12
 
11
13
  ## 1. Wrap your app with the Provider
12
14
 
13
15
  Add this once, above all your routes. It initializes the SDK for the logged-in Seamless user.
14
16
 
17
+ Production flow:
18
+
19
+ 1. Your backend requests a short-lived `userToken` from `POST /api/seamless/auth/user-token`
20
+ 2. Your frontend receives that `userToken`
21
+ 3. You pass `publishableKey` + `userToken` into the SDK
22
+
23
+ The browser should not mint sessions from raw `userId`, `userEmail`, or `userRole` in production.
24
+
15
25
  ```tsx
16
26
  import { SeamlessRoleplayProvider } from "@rehers/rehers-roleplay-sdk/react";
17
27
 
18
28
  function App() {
19
- // You already have the logged-in user from your auth/session.
20
- // The SDK needs their id and email.
21
- const me = useCurrentUser(); // however you get the logged-in user
29
+ const userToken = useRoleplayUserToken(); // fetched from your backend
22
30
 
23
31
  return (
24
32
  <SeamlessRoleplayProvider
25
33
  publishableKey="pk_live_..."
26
- userId={String(me.id)}
27
- userEmail={me.username}
28
- userRole={me.orgRole}
34
+ userToken={userToken}
29
35
  onReady={() => console.log("Roleplay SDK ready")}
30
36
  onError={(err) => console.error("Roleplay SDK error", err)}
31
37
  >
@@ -39,17 +45,15 @@ function App() {
39
45
  }
40
46
  ```
41
47
 
42
- If you fetch the user via API:
48
+ If you need a backend shape, the secure flow looks like this:
43
49
 
44
50
  ```ts
45
- const res = await fetch("https://api.seamless.ai/api/users/me", {
51
+ const tokenRes = await fetch("/api/roleplay/user-token", {
52
+ method: "POST",
46
53
  credentials: "include",
47
54
  }).then((r) => r.json());
48
55
 
49
- const me = res.data ?? res;
50
- // me.id → pass as userId (convert to string)
51
- // me.username → pass as userEmail
52
- // me.orgRole → pass as userRole directly
56
+ const userToken = tokenRes.userToken;
53
57
  ```
54
58
 
55
59
  That's the only setup. Everything below just works.
@@ -219,8 +223,7 @@ import "@rehers/rehers-roleplay-sdk";
219
223
  // Initialize once
220
224
  SeamlessRoleplay.init({
221
225
  publishableKey: "pk_live_...",
222
- userId: "...",
223
- userEmail: "...",
226
+ userToken: "...",
224
227
  onReady() { console.log("ready"); },
225
228
  });
226
229
 
package/index.d.ts CHANGED
@@ -1,13 +1,9 @@
1
- export interface SeamlessRoleplayInitOptions {
1
+ interface SeamlessRoleplayInitBase {
2
2
  /** Publishable API key (starts with pk_live_ or pk_test_) */
3
3
  publishableKey: string;
4
- /** Logged-in Seamless user ID. Pass String(me.id) from GET /api/users/me */
5
- userId: string;
6
- /** Logged-in Seamless user email. Pass me.username from GET /api/users/me */
7
- userEmail: string;
8
4
  /** Optional user role for syncing permissions ("owner" | "admin" | "member") */
9
5
  userRole?: "owner" | "admin" | "member";
10
- /** Optional signed JWT for identity verification */
6
+ /** Optional short-lived signed JWT for identity verification */
11
7
  userToken?: string;
12
8
  /** Override the app origin — where the iframe loads from (for dev/testing only) */
13
9
  origin?: string;
@@ -17,6 +13,23 @@ export interface SeamlessRoleplayInitOptions {
17
13
  onError?: (error: { code: string; message: string }) => void;
18
14
  }
19
15
 
16
+ export type SeamlessRoleplayInitOptions =
17
+ | (SeamlessRoleplayInitBase & {
18
+ /** Preferred production auth path: signed Seamless bootstrap token */
19
+ userToken: string;
20
+ /** Optional fallback fields kept for local demos/internal tools */
21
+ userId?: string;
22
+ userEmail?: string;
23
+ })
24
+ | (SeamlessRoleplayInitBase & {
25
+ /** Legacy/demo fallback path when no signed token is available */
26
+ userToken?: string;
27
+ /** Logged-in Seamless user ID. Pass String(me.id) from GET /api/users/me */
28
+ userId: string;
29
+ /** Logged-in Seamless user email. Pass me.username from GET /api/users/me */
30
+ userEmail: string;
31
+ });
32
+
20
33
  export interface SeamlessRoleplayOpenData {
21
34
  /** Full name of the contact */
22
35
  name: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rehers/rehers-roleplay-sdk",
3
- "version": "2.5.1",
3
+ "version": "2.5.2",
4
4
  "description": "Seamless Roleplay SDK — embed roleplay call sessions via a modal + iframe",
5
5
  "main": "roleplay-sdk.js",
6
6
  "types": "index.d.ts",
package/react.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { type ReactNode } from "react";
2
- import type { SeamlessRoleplaySDK, AddToScenarioContact, AddToScenarioCompleteData } from "@rehers/rehers-roleplay-sdk";
2
+ import type { SeamlessRoleplaySDK, AddToScenarioContact, AddToScenarioCompleteData, SeamlessRoleplayInitOptions } from "@rehers/rehers-roleplay-sdk";
3
3
  import "@rehers/rehers-roleplay-sdk";
4
4
  export type { AddToScenarioContact, AddToScenarioCompleteData };
5
5
  interface SeamlessRoleplayContextValue {
@@ -10,20 +10,9 @@ interface SeamlessRoleplayContextValue {
10
10
  } | null;
11
11
  sdk: SeamlessRoleplaySDK;
12
12
  }
13
- export interface SeamlessRoleplayProviderProps {
14
- publishableKey: string;
15
- userId: string;
16
- userEmail: string;
17
- userRole?: "owner" | "admin" | "member";
18
- userToken?: string;
19
- origin?: string;
20
- onReady?: () => void;
21
- onError?: (error: {
22
- code: string;
23
- message: string;
24
- }) => void;
13
+ export type SeamlessRoleplayProviderProps = SeamlessRoleplayInitOptions & {
25
14
  children: ReactNode;
26
- }
15
+ };
27
16
  export declare function SeamlessRoleplayProvider({ publishableKey, userId, userEmail, userRole, userToken, origin, onReady, onError, children, }: SeamlessRoleplayProviderProps): import("react/jsx-runtime").JSX.Element;
28
17
  export declare function useSeamlessRoleplay(): SeamlessRoleplayContextValue;
29
18
  export interface RoleplayDialogProps {
package/react.js CHANGED
@@ -36,28 +36,51 @@ export function SeamlessRoleplayProvider({ publishableKey, userId, userEmail, us
36
36
  }
37
37
  mountedRef.current = true;
38
38
  setState({ isReady: false, error: null });
39
- sdk.init({
40
- publishableKey,
41
- userId,
42
- userEmail,
43
- userRole,
44
- userToken,
45
- origin,
46
- onReady: () => {
47
- var _a;
48
- if (!mountedRef.current)
49
- return;
50
- setState({ isReady: true, error: null });
51
- (_a = onReadyRef.current) === null || _a === void 0 ? void 0 : _a.call(onReadyRef);
52
- },
53
- onError: (err) => {
54
- var _a;
55
- if (!mountedRef.current)
56
- return;
57
- setState({ isReady: false, error: err });
58
- (_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef, err);
59
- },
60
- });
39
+ const initOptions = userToken
40
+ ? {
41
+ publishableKey,
42
+ userToken,
43
+ userId,
44
+ userEmail,
45
+ userRole,
46
+ origin,
47
+ onReady: () => {
48
+ var _a;
49
+ if (!mountedRef.current)
50
+ return;
51
+ setState({ isReady: true, error: null });
52
+ (_a = onReadyRef.current) === null || _a === void 0 ? void 0 : _a.call(onReadyRef);
53
+ },
54
+ onError: (err) => {
55
+ var _a;
56
+ if (!mountedRef.current)
57
+ return;
58
+ setState({ isReady: false, error: err });
59
+ (_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef, err);
60
+ },
61
+ }
62
+ : {
63
+ publishableKey,
64
+ userId: userId || "",
65
+ userEmail: userEmail || "",
66
+ userRole,
67
+ origin,
68
+ onReady: () => {
69
+ var _a;
70
+ if (!mountedRef.current)
71
+ return;
72
+ setState({ isReady: true, error: null });
73
+ (_a = onReadyRef.current) === null || _a === void 0 ? void 0 : _a.call(onReadyRef);
74
+ },
75
+ onError: (err) => {
76
+ var _a;
77
+ if (!mountedRef.current)
78
+ return;
79
+ setState({ isReady: false, error: err });
80
+ (_a = onErrorRef.current) === null || _a === void 0 ? void 0 : _a.call(onErrorRef, err);
81
+ },
82
+ };
83
+ sdk.init(initOptions);
61
84
  return () => {
62
85
  mountedRef.current = false;
63
86
  providerMountCount--;
package/roleplay-sdk.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * Publishable-key auth model. No build step required.
5
5
  *
6
6
  * Usage:
7
- * SeamlessRoleplay.init({ publishableKey: 'pk_live_...', userId: 'user_789' });
7
+ * SeamlessRoleplay.init({ publishableKey: 'pk_live_...', userToken: 'jwt...' });
8
8
  * SeamlessRoleplay.open({ name: '...', domain: '...', company: '...', title: '...' });
9
9
  */
10
10
  (function () {
@@ -71,6 +71,16 @@
71
71
  return DEFAULT_API_ORIGIN;
72
72
  }
73
73
 
74
+ function buildIframeSrc(path) {
75
+ var targetPath = path || "/embed/roleplay-call";
76
+ var url = new URL(targetPath, getOrigin());
77
+ // Force the embedded app to drop any stale auth before it handles the
78
+ // SDK session-init message. Without this, hosted app sessions can leak
79
+ // through when switching users or entering trial mode in the demo.
80
+ url.searchParams.set("seamlessResetAuth", "1");
81
+ return url.toString();
82
+ }
83
+
74
84
  function sendMsg(iframeEl, msg) {
75
85
  try {
76
86
  if (iframeEl && iframeEl.contentWindow) {
@@ -88,10 +98,9 @@
88
98
 
89
99
  fetchingSession = new Promise(function (resolve, reject) {
90
100
  var url = getApiOrigin() + "/api/sdk/session";
91
- var body = { userId: userId };
92
- if (userEmail) body.userEmail = userEmail;
93
- if (userRole) body.userRole = userRole;
94
- if (userToken) body.userToken = userToken;
101
+ var body = userToken
102
+ ? { userToken: userToken }
103
+ : { userId: userId, userEmail: userEmail, userRole: userRole };
95
104
 
96
105
  var xhr = new XMLHttpRequest();
97
106
  xhr.open("POST", url, true);
@@ -381,7 +390,7 @@
381
390
 
382
391
  function createIframe(path) {
383
392
  var iframeEl = document.createElement("iframe");
384
- iframeEl.src = getOrigin() + (path || "/embed/roleplay-call");
393
+ iframeEl.src = buildIframeSrc(path);
385
394
  iframeEl.allow = "camera; microphone; display-capture; autoplay";
386
395
  iframeEl.style.width = "100%";
387
396
  iframeEl.style.height = "100%";
@@ -398,8 +407,18 @@
398
407
  */
399
408
  init: function (opts) {
400
409
  try {
401
- if (!opts || !opts.publishableKey || !opts.userId || !opts.userEmail) {
402
- logError("init", "requires { publishableKey, userId, userEmail }");
410
+ var hasUserToken = !!(opts && opts.userToken && String(opts.userToken).trim());
411
+ var hasLegacyIdentity = !!(opts && opts.userId && opts.userEmail);
412
+
413
+ if (!opts || !opts.publishableKey || (!hasUserToken && !hasLegacyIdentity)) {
414
+ var error = {
415
+ code: "INVALID_INIT",
416
+ message: "requires { publishableKey, userToken } or legacy { publishableKey, userId, userEmail }",
417
+ };
418
+ logError("init", error.message);
419
+ if (opts && typeof opts.onError === "function") {
420
+ try { opts.onError(error); } catch (_) {}
421
+ }
403
422
  return;
404
423
  }
405
424
 
@@ -411,9 +430,10 @@
411
430
  fetchingSession = null;
412
431
  sessionToken = null;
413
432
  sessionExpiresAt = 0;
433
+ paymentLink = null;
414
434
 
415
435
  publishableKey = opts.publishableKey;
416
- userId = opts.userId;
436
+ userId = opts.userId || null;
417
437
  userEmail = opts.userEmail || null;
418
438
  userRole = opts.userRole || null;
419
439
  userToken = opts.userToken || null;
@@ -650,7 +670,7 @@
650
670
  cs.boxShadow = "0 25px 60px rgba(0,0,0,0.3)";
651
671
 
652
672
  var iframeEl = document.createElement("iframe");
653
- iframeEl.src = getOrigin() + "/embed/add-to-scenario";
673
+ iframeEl.src = buildIframeSrc("/embed/add-to-scenario");
654
674
  iframeEl.style.width = "100%";
655
675
  iframeEl.style.height = "100%";
656
676
  iframeEl.style.border = "none";