@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 +17 -14
- package/index.d.ts +19 -6
- package/package.json +1 -1
- package/react.d.ts +3 -14
- package/react.js +45 -22
- package/roleplay-sdk.js +30 -10
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
|
-
|
|
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
|
-
|
|
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
|
|
48
|
+
If you need a backend shape, the secure flow looks like this:
|
|
43
49
|
|
|
44
50
|
```ts
|
|
45
|
-
const
|
|
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
|
|
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
|
-
|
|
223
|
-
userEmail: "...",
|
|
226
|
+
userToken: "...",
|
|
224
227
|
onReady() { console.log("ready"); },
|
|
225
228
|
});
|
|
226
229
|
|
package/index.d.ts
CHANGED
|
@@ -1,13 +1,9 @@
|
|
|
1
|
-
|
|
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
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
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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_...',
|
|
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 =
|
|
92
|
-
|
|
93
|
-
|
|
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 =
|
|
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
|
-
|
|
402
|
-
|
|
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 =
|
|
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";
|