@playruo/embed 1.4.0 → 1.5.0
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 +38 -0
- package/dist/index.cjs +34 -25
- package/dist/index.d.cts +16 -1
- package/dist/index.d.ts +16 -1
- package/dist/index.js +31 -24
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
React SDK to embed Playruo via iframe + postMessage.
|
|
4
4
|
|
|
5
|
+
## Documentation
|
|
6
|
+
|
|
7
|
+
- Integrator overview and React/vanilla guides: [`docs/`](./docs/)
|
|
8
|
+
- Protocol reference for webapp ↔ SDK changes: [`DOCUMENTATION.md`](./DOCUMENTATION.md)
|
|
9
|
+
- Customer Notion pages are generated from `docs/` via `npm run docs:notion:dry-run` / `npm run docs:notion:publish`
|
|
10
|
+
|
|
5
11
|
## Windowed launch
|
|
6
12
|
|
|
7
13
|
`PlayruoEmbed` requests browser fullscreen on the iframe when the user starts a
|
|
@@ -33,6 +39,38 @@ Passing it inside `operation` (`"crypticalpath?op=..."`) still works and warns:
|
|
|
33
39
|
it was the only way before this prop existed. `operationKey` wins over it. Other
|
|
34
40
|
query parameters in `operation` are kept as they are.
|
|
35
41
|
|
|
42
|
+
## Host-owned visitor identity
|
|
43
|
+
|
|
44
|
+
By default the SDK stores a UUID in `localStorage["playruo_uuid"]` and answers
|
|
45
|
+
`playruo_uuid_request` with it. Pass `clientFingerprint` when the host already
|
|
46
|
+
has a stable visitor id (account id, CRM id, …): that value is answered as
|
|
47
|
+
`uuid` instead, and localStorage is left alone. Playruo derives the session
|
|
48
|
+
fingerprint from it.
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
<PlayruoEmbed
|
|
52
|
+
operation="crypticalpath"
|
|
53
|
+
clientFingerprint={visitorId}
|
|
54
|
+
/>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
A vanilla host answers the same way, with no React:
|
|
58
|
+
|
|
59
|
+
```js
|
|
60
|
+
iframe.contentWindow.postMessage(
|
|
61
|
+
{
|
|
62
|
+
type: "playruo_uuid_response",
|
|
63
|
+
requestId,
|
|
64
|
+
uuid: visitorId,
|
|
65
|
+
},
|
|
66
|
+
"https://playruo.live",
|
|
67
|
+
);
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`resolveVisitorUuid(visitorId)` from this package is the shared helper both
|
|
71
|
+
paths use. A blank or whitespace-only value is ignored and the SDK-managed UUID
|
|
72
|
+
is used.
|
|
73
|
+
|
|
36
74
|
## Password protected operations
|
|
37
75
|
|
|
38
76
|
A protected operation renders no form of its own: it announces the gate and the
|
package/dist/index.cjs
CHANGED
|
@@ -31,10 +31,12 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
33
|
EmbedTester: () => EmbedTester,
|
|
34
|
+
PLAYRUO_UUID_STORAGE_KEY: () => PLAYRUO_UUID_STORAGE_KEY,
|
|
34
35
|
PlayruoEmbed: () => PlayruoEmbed,
|
|
35
36
|
buildEmbedUrl: () => buildEmbedUrl,
|
|
36
37
|
isPlayruoEmbedMessage: () => isPlayruoEmbedMessage,
|
|
37
|
-
isPlayruoInternalMessage: () => isPlayruoInternalMessage
|
|
38
|
+
isPlayruoInternalMessage: () => isPlayruoInternalMessage,
|
|
39
|
+
resolveVisitorUuid: () => resolveVisitorUuid
|
|
38
40
|
});
|
|
39
41
|
module.exports = __toCommonJS(index_exports);
|
|
40
42
|
|
|
@@ -81,6 +83,31 @@ var buildEmbedUrlWithOrigin = (origin, { operation, operationKey, storeUrl }) =>
|
|
|
81
83
|
return url.toString();
|
|
82
84
|
};
|
|
83
85
|
|
|
86
|
+
// src/visitor-uuid.ts
|
|
87
|
+
var PLAYRUO_UUID_STORAGE_KEY = "playruo_uuid";
|
|
88
|
+
var generateUUID = () => {
|
|
89
|
+
if (self.crypto.randomUUID) return self.crypto.randomUUID();
|
|
90
|
+
const bytes = new Uint8Array(16);
|
|
91
|
+
self.crypto.getRandomValues(bytes);
|
|
92
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
93
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
94
|
+
return [...bytes].map(
|
|
95
|
+
(b, i) => ([4, 6, 8, 10].includes(i) ? "-" : "") + b.toString(16).padStart(2, "0")
|
|
96
|
+
).join("");
|
|
97
|
+
};
|
|
98
|
+
var resolveVisitorUuid = (clientFingerprint) => {
|
|
99
|
+
const provided = clientFingerprint?.trim();
|
|
100
|
+
if (provided) {
|
|
101
|
+
return provided;
|
|
102
|
+
}
|
|
103
|
+
let uuid = localStorage.getItem(PLAYRUO_UUID_STORAGE_KEY);
|
|
104
|
+
if (!uuid) {
|
|
105
|
+
uuid = generateUUID();
|
|
106
|
+
localStorage.setItem(PLAYRUO_UUID_STORAGE_KEY, uuid);
|
|
107
|
+
}
|
|
108
|
+
return uuid;
|
|
109
|
+
};
|
|
110
|
+
|
|
84
111
|
// src/react/playruo-embed.tsx
|
|
85
112
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
86
113
|
var PlayruoEmbed = ({
|
|
@@ -88,6 +115,7 @@ var PlayruoEmbed = ({
|
|
|
88
115
|
operation,
|
|
89
116
|
operationKey,
|
|
90
117
|
storeUrl,
|
|
118
|
+
clientFingerprint,
|
|
91
119
|
showIframe = false,
|
|
92
120
|
className,
|
|
93
121
|
onReady,
|
|
@@ -573,16 +601,6 @@ var PlayruoEmbed = ({
|
|
|
573
601
|
postExitFullscreenAckToIframe,
|
|
574
602
|
storeUrl
|
|
575
603
|
]);
|
|
576
|
-
const generateUUID = () => {
|
|
577
|
-
if (self.crypto.randomUUID) return self.crypto.randomUUID();
|
|
578
|
-
const bytes = new Uint8Array(16);
|
|
579
|
-
self.crypto.getRandomValues(bytes);
|
|
580
|
-
bytes[6] = bytes[6] & 15 | 64;
|
|
581
|
-
bytes[8] = bytes[8] & 63 | 128;
|
|
582
|
-
return [...bytes].map(
|
|
583
|
-
(b, i) => ([4, 6, 8, 10].includes(i) ? "-" : "") + b.toString(16).padStart(2, "0")
|
|
584
|
-
).join("");
|
|
585
|
-
};
|
|
586
604
|
(0, import_react.useEffect)(() => {
|
|
587
605
|
const handleUUIDMessage = (event) => {
|
|
588
606
|
const iframe = iframeRef.current;
|
|
@@ -595,16 +613,11 @@ var PlayruoEmbed = ({
|
|
|
595
613
|
const { type, requestId } = event.data || {};
|
|
596
614
|
if (type === "playruo_uuid_request") {
|
|
597
615
|
try {
|
|
598
|
-
let storedUUID = localStorage.getItem("playruo_uuid");
|
|
599
|
-
if (!storedUUID) {
|
|
600
|
-
storedUUID = generateUUID();
|
|
601
|
-
localStorage.setItem("playruo_uuid", storedUUID);
|
|
602
|
-
}
|
|
603
616
|
iframe.contentWindow?.postMessage(
|
|
604
617
|
{
|
|
605
618
|
type: "playruo_uuid_response",
|
|
606
619
|
requestId,
|
|
607
|
-
uuid:
|
|
620
|
+
uuid: resolveVisitorUuid(clientFingerprint)
|
|
608
621
|
},
|
|
609
622
|
embedOrigin
|
|
610
623
|
);
|
|
@@ -614,7 +627,7 @@ var PlayruoEmbed = ({
|
|
|
614
627
|
};
|
|
615
628
|
window.addEventListener("message", handleUUIDMessage);
|
|
616
629
|
return () => window.removeEventListener("message", handleUUIDMessage);
|
|
617
|
-
}, [embedOrigin, iframeRef]);
|
|
630
|
+
}, [clientFingerprint, embedOrigin, iframeRef]);
|
|
618
631
|
(0, import_react.useEffect)(() => {
|
|
619
632
|
const handleFullscreenChange = () => {
|
|
620
633
|
const iframe = iframeRef.current;
|
|
@@ -839,12 +852,6 @@ var PlayruoEmbed = ({
|
|
|
839
852
|
},
|
|
840
853
|
onLoad: () => {
|
|
841
854
|
setIsIframeLoaded(true);
|
|
842
|
-
setTimeout(() => {
|
|
843
|
-
if (iframeRef.current) {
|
|
844
|
-
iframeRef.current.focus();
|
|
845
|
-
iframeRef.current.contentWindow?.focus();
|
|
846
|
-
}
|
|
847
|
-
}, 500);
|
|
848
855
|
},
|
|
849
856
|
onError: () => {
|
|
850
857
|
console.error("[PlayruoEmbed] Iframe failed to load");
|
|
@@ -2138,8 +2145,10 @@ var isPlayruoInternalMessage = (data) => {
|
|
|
2138
2145
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2139
2146
|
0 && (module.exports = {
|
|
2140
2147
|
EmbedTester,
|
|
2148
|
+
PLAYRUO_UUID_STORAGE_KEY,
|
|
2141
2149
|
PlayruoEmbed,
|
|
2142
2150
|
buildEmbedUrl,
|
|
2143
2151
|
isPlayruoEmbedMessage,
|
|
2144
|
-
isPlayruoInternalMessage
|
|
2152
|
+
isPlayruoInternalMessage,
|
|
2153
|
+
resolveVisitorUuid
|
|
2145
2154
|
});
|
package/dist/index.d.cts
CHANGED
|
@@ -180,6 +180,13 @@ interface PlayruoEmbedProps {
|
|
|
180
180
|
*/
|
|
181
181
|
operationKey?: string;
|
|
182
182
|
storeUrl?: string;
|
|
183
|
+
/**
|
|
184
|
+
* Stable visitor identity the host already owns (account id, CRM id, …).
|
|
185
|
+
* When set, answers `playruo_uuid_request` with this value and skips
|
|
186
|
+
* `localStorage["playruo_uuid"]`. Omit to keep the SDK-managed UUID.
|
|
187
|
+
* Playruo derives the session fingerprint from the answered `uuid`.
|
|
188
|
+
*/
|
|
189
|
+
clientFingerprint?: string;
|
|
183
190
|
showIframe?: boolean;
|
|
184
191
|
className?: string;
|
|
185
192
|
onReady?: () => void;
|
|
@@ -250,4 +257,12 @@ interface BuildUrlParams {
|
|
|
250
257
|
*/
|
|
251
258
|
declare const buildEmbedUrl: ({ operation, operationKey, storeUrl, }: BuildUrlParams) => string;
|
|
252
259
|
|
|
253
|
-
|
|
260
|
+
declare const PLAYRUO_UUID_STORAGE_KEY = "playruo_uuid";
|
|
261
|
+
/**
|
|
262
|
+
* Visitor identity the host answers on `playruo_uuid_request`.
|
|
263
|
+
* Same rule as PlayruoEmbed `clientFingerprint`: a non-empty value is used as-is
|
|
264
|
+
* and is not written to localStorage. Otherwise the SDK-managed UUID is used.
|
|
265
|
+
*/
|
|
266
|
+
declare const resolveVisitorUuid: (clientFingerprint?: string) => string;
|
|
267
|
+
|
|
268
|
+
export { type DemoCompletedPayload, EmbedTester, type EmbedTesterProps, PLAYRUO_UUID_STORAGE_KEY, type PlayruoDemoCompletedMessage, PlayruoEmbed, type PlayruoEmbedActions, type PlayruoEmbedMessage, type PlayruoEmbedPasswordState, type PlayruoEmbedProps, type PlayruoEmbedRenderState, type PlayruoEmbedState, type PlayruoErrorMessage, type PlayruoEventMessage, type PlayruoEventPayload, type PlayruoInternalMessage, type PlayruoPasswordError, type PlayruoPasswordErrorCode, type PlayruoPasswordNotRequiredMessage, type PlayruoPasswordRequiredMessage, type PlayruoPasswordResultMessage, type PlayruoPurchaseRequestMessage, type PlayruoReadyMessage, type PlayruoRequestExitFullscreenMessage, type PlayruoSessionOverMessage, type PlayruoStateUpdateMessage, type PlayruoStreamStartMessage, type PlayruoStreamStopMessage, buildEmbedUrl, isPlayruoEmbedMessage, isPlayruoInternalMessage, resolveVisitorUuid };
|
package/dist/index.d.ts
CHANGED
|
@@ -180,6 +180,13 @@ interface PlayruoEmbedProps {
|
|
|
180
180
|
*/
|
|
181
181
|
operationKey?: string;
|
|
182
182
|
storeUrl?: string;
|
|
183
|
+
/**
|
|
184
|
+
* Stable visitor identity the host already owns (account id, CRM id, …).
|
|
185
|
+
* When set, answers `playruo_uuid_request` with this value and skips
|
|
186
|
+
* `localStorage["playruo_uuid"]`. Omit to keep the SDK-managed UUID.
|
|
187
|
+
* Playruo derives the session fingerprint from the answered `uuid`.
|
|
188
|
+
*/
|
|
189
|
+
clientFingerprint?: string;
|
|
183
190
|
showIframe?: boolean;
|
|
184
191
|
className?: string;
|
|
185
192
|
onReady?: () => void;
|
|
@@ -250,4 +257,12 @@ interface BuildUrlParams {
|
|
|
250
257
|
*/
|
|
251
258
|
declare const buildEmbedUrl: ({ operation, operationKey, storeUrl, }: BuildUrlParams) => string;
|
|
252
259
|
|
|
253
|
-
|
|
260
|
+
declare const PLAYRUO_UUID_STORAGE_KEY = "playruo_uuid";
|
|
261
|
+
/**
|
|
262
|
+
* Visitor identity the host answers on `playruo_uuid_request`.
|
|
263
|
+
* Same rule as PlayruoEmbed `clientFingerprint`: a non-empty value is used as-is
|
|
264
|
+
* and is not written to localStorage. Otherwise the SDK-managed UUID is used.
|
|
265
|
+
*/
|
|
266
|
+
declare const resolveVisitorUuid: (clientFingerprint?: string) => string;
|
|
267
|
+
|
|
268
|
+
export { type DemoCompletedPayload, EmbedTester, type EmbedTesterProps, PLAYRUO_UUID_STORAGE_KEY, type PlayruoDemoCompletedMessage, PlayruoEmbed, type PlayruoEmbedActions, type PlayruoEmbedMessage, type PlayruoEmbedPasswordState, type PlayruoEmbedProps, type PlayruoEmbedRenderState, type PlayruoEmbedState, type PlayruoErrorMessage, type PlayruoEventMessage, type PlayruoEventPayload, type PlayruoInternalMessage, type PlayruoPasswordError, type PlayruoPasswordErrorCode, type PlayruoPasswordNotRequiredMessage, type PlayruoPasswordRequiredMessage, type PlayruoPasswordResultMessage, type PlayruoPurchaseRequestMessage, type PlayruoReadyMessage, type PlayruoRequestExitFullscreenMessage, type PlayruoSessionOverMessage, type PlayruoStateUpdateMessage, type PlayruoStreamStartMessage, type PlayruoStreamStopMessage, buildEmbedUrl, isPlayruoEmbedMessage, isPlayruoInternalMessage, resolveVisitorUuid };
|
package/dist/index.js
CHANGED
|
@@ -41,6 +41,31 @@ var buildEmbedUrlWithOrigin = (origin, { operation, operationKey, storeUrl }) =>
|
|
|
41
41
|
return url.toString();
|
|
42
42
|
};
|
|
43
43
|
|
|
44
|
+
// src/visitor-uuid.ts
|
|
45
|
+
var PLAYRUO_UUID_STORAGE_KEY = "playruo_uuid";
|
|
46
|
+
var generateUUID = () => {
|
|
47
|
+
if (self.crypto.randomUUID) return self.crypto.randomUUID();
|
|
48
|
+
const bytes = new Uint8Array(16);
|
|
49
|
+
self.crypto.getRandomValues(bytes);
|
|
50
|
+
bytes[6] = bytes[6] & 15 | 64;
|
|
51
|
+
bytes[8] = bytes[8] & 63 | 128;
|
|
52
|
+
return [...bytes].map(
|
|
53
|
+
(b, i) => ([4, 6, 8, 10].includes(i) ? "-" : "") + b.toString(16).padStart(2, "0")
|
|
54
|
+
).join("");
|
|
55
|
+
};
|
|
56
|
+
var resolveVisitorUuid = (clientFingerprint) => {
|
|
57
|
+
const provided = clientFingerprint?.trim();
|
|
58
|
+
if (provided) {
|
|
59
|
+
return provided;
|
|
60
|
+
}
|
|
61
|
+
let uuid = localStorage.getItem(PLAYRUO_UUID_STORAGE_KEY);
|
|
62
|
+
if (!uuid) {
|
|
63
|
+
uuid = generateUUID();
|
|
64
|
+
localStorage.setItem(PLAYRUO_UUID_STORAGE_KEY, uuid);
|
|
65
|
+
}
|
|
66
|
+
return uuid;
|
|
67
|
+
};
|
|
68
|
+
|
|
44
69
|
// src/react/playruo-embed.tsx
|
|
45
70
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
46
71
|
var PlayruoEmbed = ({
|
|
@@ -48,6 +73,7 @@ var PlayruoEmbed = ({
|
|
|
48
73
|
operation,
|
|
49
74
|
operationKey,
|
|
50
75
|
storeUrl,
|
|
76
|
+
clientFingerprint,
|
|
51
77
|
showIframe = false,
|
|
52
78
|
className,
|
|
53
79
|
onReady,
|
|
@@ -533,16 +559,6 @@ var PlayruoEmbed = ({
|
|
|
533
559
|
postExitFullscreenAckToIframe,
|
|
534
560
|
storeUrl
|
|
535
561
|
]);
|
|
536
|
-
const generateUUID = () => {
|
|
537
|
-
if (self.crypto.randomUUID) return self.crypto.randomUUID();
|
|
538
|
-
const bytes = new Uint8Array(16);
|
|
539
|
-
self.crypto.getRandomValues(bytes);
|
|
540
|
-
bytes[6] = bytes[6] & 15 | 64;
|
|
541
|
-
bytes[8] = bytes[8] & 63 | 128;
|
|
542
|
-
return [...bytes].map(
|
|
543
|
-
(b, i) => ([4, 6, 8, 10].includes(i) ? "-" : "") + b.toString(16).padStart(2, "0")
|
|
544
|
-
).join("");
|
|
545
|
-
};
|
|
546
562
|
useEffect(() => {
|
|
547
563
|
const handleUUIDMessage = (event) => {
|
|
548
564
|
const iframe = iframeRef.current;
|
|
@@ -555,16 +571,11 @@ var PlayruoEmbed = ({
|
|
|
555
571
|
const { type, requestId } = event.data || {};
|
|
556
572
|
if (type === "playruo_uuid_request") {
|
|
557
573
|
try {
|
|
558
|
-
let storedUUID = localStorage.getItem("playruo_uuid");
|
|
559
|
-
if (!storedUUID) {
|
|
560
|
-
storedUUID = generateUUID();
|
|
561
|
-
localStorage.setItem("playruo_uuid", storedUUID);
|
|
562
|
-
}
|
|
563
574
|
iframe.contentWindow?.postMessage(
|
|
564
575
|
{
|
|
565
576
|
type: "playruo_uuid_response",
|
|
566
577
|
requestId,
|
|
567
|
-
uuid:
|
|
578
|
+
uuid: resolveVisitorUuid(clientFingerprint)
|
|
568
579
|
},
|
|
569
580
|
embedOrigin
|
|
570
581
|
);
|
|
@@ -574,7 +585,7 @@ var PlayruoEmbed = ({
|
|
|
574
585
|
};
|
|
575
586
|
window.addEventListener("message", handleUUIDMessage);
|
|
576
587
|
return () => window.removeEventListener("message", handleUUIDMessage);
|
|
577
|
-
}, [embedOrigin, iframeRef]);
|
|
588
|
+
}, [clientFingerprint, embedOrigin, iframeRef]);
|
|
578
589
|
useEffect(() => {
|
|
579
590
|
const handleFullscreenChange = () => {
|
|
580
591
|
const iframe = iframeRef.current;
|
|
@@ -799,12 +810,6 @@ var PlayruoEmbed = ({
|
|
|
799
810
|
},
|
|
800
811
|
onLoad: () => {
|
|
801
812
|
setIsIframeLoaded(true);
|
|
802
|
-
setTimeout(() => {
|
|
803
|
-
if (iframeRef.current) {
|
|
804
|
-
iframeRef.current.focus();
|
|
805
|
-
iframeRef.current.contentWindow?.focus();
|
|
806
|
-
}
|
|
807
|
-
}, 500);
|
|
808
813
|
},
|
|
809
814
|
onError: () => {
|
|
810
815
|
console.error("[PlayruoEmbed] Iframe failed to load");
|
|
@@ -2097,8 +2102,10 @@ var isPlayruoInternalMessage = (data) => {
|
|
|
2097
2102
|
};
|
|
2098
2103
|
export {
|
|
2099
2104
|
EmbedTester,
|
|
2105
|
+
PLAYRUO_UUID_STORAGE_KEY,
|
|
2100
2106
|
PlayruoEmbed,
|
|
2101
2107
|
buildEmbedUrl,
|
|
2102
2108
|
isPlayruoEmbedMessage,
|
|
2103
|
-
isPlayruoInternalMessage
|
|
2109
|
+
isPlayruoInternalMessage,
|
|
2110
|
+
resolveVisitorUuid
|
|
2104
2111
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@playruo/embed",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.5.0",
|
|
4
4
|
"description": "React SDK to embed Playruo with postMessage API.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -53,6 +53,8 @@
|
|
|
53
53
|
"check:exports": "attw --pack .",
|
|
54
54
|
"test": "npm run build && vitest run --coverage",
|
|
55
55
|
"test:browser": "vitest run --config vitest.browser.config.ts",
|
|
56
|
-
"test:watch": "npm run build && vitest"
|
|
56
|
+
"test:watch": "npm run build && vitest",
|
|
57
|
+
"docs:notion:dry-run": "node scripts/sync-notion-docs.mjs",
|
|
58
|
+
"docs:notion:publish": "node scripts/sync-notion-docs.mjs --publish"
|
|
57
59
|
}
|
|
58
60
|
}
|