@playruo/embed 1.1.2 → 1.1.3

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
@@ -36,30 +36,60 @@ export const Demo = () => (
36
36
  ## Headless mode (with renderState)
37
37
 
38
38
  ```tsx
39
+ "use client";
39
40
  import { PlayruoEmbed } from "@playruo/embed";
40
-
41
- <PlayruoEmbed
42
- // operation is optional, defaults to "iframe"
43
- storeUrl="https://store.steampowered.com/app/2751000/..."
44
- showIframe
45
- renderState={({ status, message }) => {
46
- if (status === "loading") return <div>Loading demo…</div>;
47
- if (status === "error") return <div>Error: {message}</div>;
48
- if (status === "available")
49
- return (
50
- <div className="flex gap-3">
51
- <button>Try Now</button>
52
- </div>
53
- );
54
- // unavailable
55
- return (
56
- <div className="flex gap-3">
57
- <span>{message ?? "Unavailable"}</span>
58
- <button>Buy Now</button>
59
- </div>
60
- );
61
- }}
62
- />
41
+ import { useState } from "react";
42
+
43
+ export const Demo = () => {
44
+ const [showIframe, setShowIframe] = useState(false);
45
+
46
+ return (
47
+ <PlayruoEmbed
48
+ operation="iframe"
49
+ storeUrl="https://store.steampowered.com/app/2751000/..."
50
+ showIframe={showIframe}
51
+ renderState={({ status, message, actions }) => {
52
+ if (status === "loading") return <div>Loading demo…</div>;
53
+ if (status === "error") return <div>Error: {message}</div>;
54
+
55
+ if (status === "available") {
56
+ return (
57
+ <div className="flex gap-3">
58
+ <button
59
+ onClick={() => {
60
+ setShowIframe(true);
61
+ actions.tryNow();
62
+ }}
63
+ disabled={!actions.canTryNow}
64
+ >
65
+ Try Now
66
+ </button>
67
+ <button
68
+ onClick={actions.buyNow}
69
+ disabled={!actions.canBuyNow}
70
+ >
71
+ Buy Now
72
+ </button>
73
+ </div>
74
+ );
75
+ }
76
+
77
+ // unavailable
78
+ return (
79
+ <div className="flex gap-3">
80
+ <span>{message ?? "Unavailable"}</span>
81
+ <button
82
+ onClick={actions.buyNow}
83
+ disabled={!actions.canBuyNow}
84
+ >
85
+ Buy Now
86
+ </button>
87
+ </div>
88
+ );
89
+ }}
90
+ />
91
+ );
92
+ };
63
93
  ```
64
94
 
65
95
  ### Props
@@ -76,9 +106,10 @@ import { PlayruoEmbed } from "@playruo/embed";
76
106
 
77
107
  #### Render API
78
108
 
79
- - **renderState({ status, message })**: single render entry point
109
+ - **renderState({ status, message, actions })**: single render entry point
80
110
  - `status`: "loading" | "available" | "unavailable" | "error"
81
111
  - `message?`: optional status message (e.g. availability reason)
112
+ - `actions`: object with `tryNow()`, `buyNow()`, `canTryNow`, `canBuyNow`
82
113
 
83
114
 
84
115
  ### Events
@@ -99,51 +130,173 @@ export interface PlayruoEmbedState {
99
130
  }
100
131
  ```
101
132
 
102
- ## Commands (postMessage)
133
+ ## Actions API
134
+
135
+ The `renderState` callback receives an `actions` object with:
103
136
 
104
- The component exposes two imperative actions via the render API:
137
+ - **`actions.tryNow()`** → sends `try_now` command to the iframe
138
+ - **`actions.buyNow()`** → sends `buy_now` command to the iframe
139
+ - **`actions.canTryNow`** → boolean indicating if try now is available
140
+ - **`actions.canBuyNow`** → boolean indicating if buy now is available
105
141
 
106
- - `tryNow()` → posts `"try_now"` to the iframe
107
- - `buyNow()` → posts `"buy_now"` to the iframe
142
+ ```tsx
143
+ renderState={({ actions }) => (
144
+ <button onClick={actions.tryNow} disabled={!actions.canTryNow}>
145
+ Try Now
146
+ </button>
147
+ )}
148
+ ```
108
149
 
109
150
  ## Incoming messages (from iframe)
110
151
 
111
152
  The component listens to Playruo postMessage events and triggers the corresponding callbacks:
112
153
 
113
- - `"playruo:ready"` (also accepts legacy `"playruo_ready"`) → `onReady()`
114
- - `"playruo:state"` (also accepts legacy `"playruo_state_update"`) → `onStateChange(state)`
115
- - `"playruo:demoCompleted"` (also accepts legacy `"playruo_demo_completed"`) → `onDemoCompleted(data)`
116
- - `"playruo:purchaseRequest"` (also accepts legacy `"playruo_purchase_request"`) → `onPurchaseRequest({ storeUrl })`
117
- - `"playruo:error"` (also accepts legacy `"playruo_error"`) → `onError(message)`
154
+ - `"playruo_ready"` → `onReady()`
155
+ - `"playruo_state_update"` → `onStateChange(state)`
156
+ - `"playruo_demo_completed"` → `onDemoCompleted(data)`
157
+ - `"playruo_purchase_request"` → `onPurchaseRequest({ storeUrl })`
158
+ - `"playruo_error"` → `onError(message)`
118
159
 
119
- ## URL builder
160
+ ## URL builder (Advanced)
120
161
 
121
- You can build the iframe URL manually if needed:
162
+ For advanced use cases, you can build the iframe URL manually:
122
163
 
123
164
  ```ts
124
165
  import { buildEmbedUrl } from "@playruo/embed";
125
166
 
126
167
  const url = buildEmbedUrl({
127
- operation: "iframe", // default
168
+ baseUrl: "https://playruo.example.com",
169
+ operation: "iframe", // optional, default: "iframe"
128
170
  storeUrl: "https://store.steampowered.com/app/2751000/...", // optional
129
171
  });
130
- ``;
131
-
132
- The URL points to `"/embed"` and accepts the query params:
172
+ ```
133
173
 
134
- - `operation` (string, default: `"iframe"`)
135
- - `storeUrl` (string, optional)
174
+ The URL points to `{baseUrl}/embed` with query params:
175
+ - `operation` (optional)
176
+ - `storeUrl` (optional)
136
177
 
137
178
  ## Iframe behavior
138
179
 
139
180
  - The iframe is always present in the DOM. Set `showIframe` to control visibility.
140
181
  - The iframe has `allow="fullscreen *; autoplay *; gamepad *"`.
141
182
 
142
- ## Types
183
+ ## TypeScript Types
184
+
185
+ ### Component Types
186
+
187
+ ```typescript
188
+ import type {
189
+ PlayruoEmbedProps,
190
+ PlayruoEmbedState,
191
+ PlayruoEmbedActions,
192
+ PlayruoEmbedRenderState,
193
+ DemoCompletedPayload,
194
+ } from "@playruo/embed";
195
+ ```
196
+
197
+ ### PostMessage Communication Types
198
+
199
+ For advanced use cases where you need to handle communication with the embed iframe directly:
200
+
201
+ ```typescript
202
+ import type {
203
+ PlayruoEmbedMessage,
204
+ PlayruoEmbedCommand,
205
+ PlayruoReadyMessage,
206
+ PlayruoStateUpdateMessage,
207
+ PlayruoPurchaseRequestMessage,
208
+ PlayruoDemoCompletedMessage,
209
+ PlayruoErrorMessage,
210
+ TryNowCommand,
211
+ BuyNowCommand,
212
+ GetStateCommand,
213
+ } from "@playruo/embed";
214
+ import { isPlayruoEmbedMessage, isPlayruoEmbedCommand } from "@playruo/embed";
215
+ ```
216
+
217
+ #### Example: Custom Message Handler
218
+
219
+ ```typescript
220
+ "use client";
221
+ import { useEffect } from "react";
222
+ import type { PlayruoEmbedMessage } from "@playruo/embed";
223
+ import { isPlayruoEmbedMessage } from "@playruo/embed";
224
+
225
+ export const MyComponent = () => {
226
+ useEffect(() => {
227
+ const handleMessage = (event: MessageEvent) => {
228
+ if (isPlayruoEmbedMessage(event.data)) {
229
+ const message = event.data as PlayruoEmbedMessage;
230
+
231
+ switch (message.type) {
232
+ case 'playruo_ready':
233
+ console.log("Embed is ready!");
234
+ break;
235
+ case 'playruo_state_update':
236
+ console.log("State updated:", message.data);
237
+ break;
238
+ case 'playruo_purchase_request':
239
+ window.open(message.data.storeUrl, "_blank");
240
+ break;
241
+ case 'playruo_demo_completed':
242
+ console.log("Demo completed:", message.data.reason);
243
+ break;
244
+ case 'playruo_error':
245
+ console.error("Embed error:", message.data);
246
+ break;
247
+ }
248
+ }
249
+ };
250
+
251
+ window.addEventListener('message', handleMessage);
252
+ return () => window.removeEventListener('message', handleMessage);
253
+ }, []);
254
+
255
+ // ...
256
+ };
257
+ ```
258
+
259
+ #### Example: Sending Commands
260
+
261
+ ```typescript
262
+ import type { PlayruoEmbedCommand } from "@playruo/embed";
143
263
 
144
- Named exports:
264
+ const sendCommandToEmbed = (command: PlayruoEmbedCommand) => {
265
+ const iframe = document.querySelector('iframe');
266
+ iframe?.contentWindow?.postMessage(command, '*');
267
+ };
268
+
269
+ // Send commands
270
+ sendCommandToEmbed({ type: 'try_now' });
271
+ sendCommandToEmbed({ type: 'buy_now' });
272
+ sendCommandToEmbed({ type: 'get_state' });
273
+ ```
145
274
 
146
- - `PlayruoEmbed`
147
- - `buildEmbedUrl`
148
- - `PlayruoEmbedState`
149
- - `PlayruoEmbedMessage<T>`
275
+ ### Exported Types
276
+
277
+ **Component:**
278
+ - `PlayruoEmbed` - Main React component
279
+ - `PlayruoEmbedProps` - Component props
280
+ - `PlayruoEmbedActions` - Actions API
281
+ - `PlayruoEmbedRenderState` - Render state API
282
+ - `PlayruoEmbedState` - Embed state
283
+ - `DemoCompletedPayload` - Demo completion data
284
+
285
+ **Messages (Embed → Host):**
286
+ - `PlayruoEmbedMessage` - Union of all message types
287
+ - `PlayruoReadyMessage`
288
+ - `PlayruoStateUpdateMessage`
289
+ - `PlayruoPurchaseRequestMessage`
290
+ - `PlayruoDemoCompletedMessage`
291
+ - `PlayruoErrorMessage`
292
+
293
+ **Commands (Host → Embed):**
294
+ - `PlayruoEmbedCommand` - Union of all command types
295
+ - `TryNowCommand`
296
+ - `BuyNowCommand`
297
+ - `GetStateCommand`
298
+
299
+ **Utilities:**
300
+ - `buildEmbedUrl` - URL builder function
301
+ - `isPlayruoEmbedMessage` - Type guard for messages
302
+ - `isPlayruoEmbedCommand` - Type guard for commands
package/dist/index.cjs CHANGED
@@ -21,7 +21,9 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
21
21
  var index_exports = {};
22
22
  __export(index_exports, {
23
23
  PlayruoEmbed: () => PlayruoEmbed,
24
- buildEmbedUrl: () => buildEmbedUrl
24
+ buildEmbedUrl: () => buildEmbedUrl,
25
+ isPlayruoEmbedCommand: () => isPlayruoEmbedCommand,
26
+ isPlayruoEmbedMessage: () => isPlayruoEmbedMessage
25
27
  });
26
28
  module.exports = __toCommonJS(index_exports);
27
29
 
@@ -66,18 +68,18 @@ var PlayruoEmbed = ({
66
68
  (0, import_react.useEffect)(() => {
67
69
  const handler = (event) => {
68
70
  const msg = event?.data ?? {};
69
- if (!msg || typeof msg.type !== "string" || !msg.type.startsWith("playruo")) return;
70
- if (msg.type === "playruo:ready" || msg.type === "playruo_ready") {
71
+ if (!msg || typeof msg.type !== "string" || !msg.type.startsWith("playruo_")) return;
72
+ if (msg.type === "playruo_ready") {
71
73
  setIsReady(true);
72
74
  onReady?.();
73
- } else if (msg.type === "playruo:state" || msg.type === "playruo_state_update") {
75
+ } else if (msg.type === "playruo_state_update") {
74
76
  setState(msg.data);
75
77
  onStateChange?.(msg.data);
76
- } else if (msg.type === "playruo:demoCompleted" || msg.type === "playruo_demo_completed") {
78
+ } else if (msg.type === "playruo_demo_completed") {
77
79
  onDemoCompleted?.(msg.data);
78
- } else if (msg.type === "playruo:purchaseRequest" || msg.type === "playruo_purchase_request") {
80
+ } else if (msg.type === "playruo_purchase_request") {
79
81
  onPurchaseRequest?.(msg.data);
80
- } else if (msg.type === "playruo:error" || msg.type === "playruo_error") {
82
+ } else if (msg.type === "playruo_error") {
81
83
  const message = msg.data?.message ?? "Unknown error";
82
84
  setErrorMessage(message);
83
85
  onError?.(message);
@@ -130,8 +132,24 @@ var PlayruoEmbed = ({
130
132
  )
131
133
  ] });
132
134
  };
135
+
136
+ // src/post-message-types.ts
137
+ var isPlayruoEmbedMessage = (data) => {
138
+ if (!data || typeof data !== "object") return false;
139
+ const msg = data;
140
+ if (typeof msg.type !== "string") return false;
141
+ return msg.type === "playruo_ready" || msg.type === "playruo_state_update" || msg.type === "playruo_purchase_request" || msg.type === "playruo_demo_completed" || msg.type === "playruo_error";
142
+ };
143
+ var isPlayruoEmbedCommand = (data) => {
144
+ if (!data || typeof data !== "object") return false;
145
+ const cmd = data;
146
+ if (typeof cmd.type !== "string") return false;
147
+ return cmd.type === "try_now" || cmd.type === "buy_now" || cmd.type === "get_state";
148
+ };
133
149
  // Annotate the CommonJS export names for ESM import in node:
134
150
  0 && (module.exports = {
135
151
  PlayruoEmbed,
136
- buildEmbedUrl
152
+ buildEmbedUrl,
153
+ isPlayruoEmbedCommand,
154
+ isPlayruoEmbedMessage
137
155
  });
package/dist/index.d.cts CHANGED
@@ -11,10 +11,6 @@ interface PlayruoEmbedState {
11
11
  status?: string;
12
12
  };
13
13
  }
14
- interface PlayruoEmbedMessage<T = unknown> {
15
- type: `playruo:${string}`;
16
- data?: T;
17
- }
18
14
  interface DemoCompletedPayload {
19
15
  reason?: string;
20
16
  storeUrl?: string;
@@ -56,6 +52,49 @@ interface PlayruoEmbedProps {
56
52
  }
57
53
  declare const PlayruoEmbed: FC<PlayruoEmbedProps>;
58
54
 
55
+ interface PlayruoReadyMessage {
56
+ type: "playruo_ready";
57
+ }
58
+ interface PlayruoStateUpdateMessage {
59
+ type: "playruo_state_update";
60
+ data: PlayruoEmbedState;
61
+ }
62
+ interface PlayruoPurchaseRequestMessage {
63
+ type: "playruo_purchase_request";
64
+ data: {
65
+ storeUrl: string;
66
+ };
67
+ }
68
+ interface PlayruoDemoCompletedMessage {
69
+ type: "playruo_demo_completed";
70
+ data: DemoCompletedPayload;
71
+ }
72
+ interface PlayruoErrorMessage {
73
+ type: "playruo_error";
74
+ data: {
75
+ message: string;
76
+ } | string;
77
+ }
78
+ type PlayruoEmbedMessage = PlayruoReadyMessage | PlayruoStateUpdateMessage | PlayruoPurchaseRequestMessage | PlayruoDemoCompletedMessage | PlayruoErrorMessage;
79
+ interface TryNowCommand {
80
+ type: "try_now";
81
+ }
82
+ interface BuyNowCommand {
83
+ type: "buy_now";
84
+ }
85
+ interface GetStateCommand {
86
+ type: "get_state";
87
+ }
88
+ type PlayruoEmbedCommand = TryNowCommand | BuyNowCommand | GetStateCommand;
89
+ /**
90
+ * Type guard to check if a message event is a Playruo embed message
91
+ */
92
+ declare const isPlayruoEmbedMessage: (data: unknown) => data is PlayruoEmbedMessage;
93
+ /**
94
+ * Type guard to check if a command is a valid Playruo embed command
95
+ */
96
+ declare const isPlayruoEmbedCommand: (data: unknown) => data is PlayruoEmbedCommand;
97
+
59
98
  interface BuildUrlParams {
60
99
  baseUrl: string;
61
100
  operation?: string;
@@ -63,4 +102,4 @@ interface BuildUrlParams {
63
102
  }
64
103
  declare const buildEmbedUrl: ({ baseUrl, operation, storeUrl }: BuildUrlParams) => string;
65
104
 
66
- export { type DemoCompletedPayload, PlayruoEmbed, type PlayruoEmbedActions, type PlayruoEmbedMessage, type PlayruoEmbedProps, type PlayruoEmbedRenderState, type PlayruoEmbedState, buildEmbedUrl };
105
+ export { type BuyNowCommand, type DemoCompletedPayload, type GetStateCommand, type PlayruoDemoCompletedMessage, PlayruoEmbed, type PlayruoEmbedActions, type PlayruoEmbedCommand, type PlayruoEmbedMessage, type PlayruoEmbedProps, type PlayruoEmbedRenderState, type PlayruoEmbedState, type PlayruoErrorMessage, type PlayruoPurchaseRequestMessage, type PlayruoReadyMessage, type PlayruoStateUpdateMessage, type TryNowCommand, buildEmbedUrl, isPlayruoEmbedCommand, isPlayruoEmbedMessage };
package/dist/index.d.ts CHANGED
@@ -11,10 +11,6 @@ interface PlayruoEmbedState {
11
11
  status?: string;
12
12
  };
13
13
  }
14
- interface PlayruoEmbedMessage<T = unknown> {
15
- type: `playruo:${string}`;
16
- data?: T;
17
- }
18
14
  interface DemoCompletedPayload {
19
15
  reason?: string;
20
16
  storeUrl?: string;
@@ -56,6 +52,49 @@ interface PlayruoEmbedProps {
56
52
  }
57
53
  declare const PlayruoEmbed: FC<PlayruoEmbedProps>;
58
54
 
55
+ interface PlayruoReadyMessage {
56
+ type: "playruo_ready";
57
+ }
58
+ interface PlayruoStateUpdateMessage {
59
+ type: "playruo_state_update";
60
+ data: PlayruoEmbedState;
61
+ }
62
+ interface PlayruoPurchaseRequestMessage {
63
+ type: "playruo_purchase_request";
64
+ data: {
65
+ storeUrl: string;
66
+ };
67
+ }
68
+ interface PlayruoDemoCompletedMessage {
69
+ type: "playruo_demo_completed";
70
+ data: DemoCompletedPayload;
71
+ }
72
+ interface PlayruoErrorMessage {
73
+ type: "playruo_error";
74
+ data: {
75
+ message: string;
76
+ } | string;
77
+ }
78
+ type PlayruoEmbedMessage = PlayruoReadyMessage | PlayruoStateUpdateMessage | PlayruoPurchaseRequestMessage | PlayruoDemoCompletedMessage | PlayruoErrorMessage;
79
+ interface TryNowCommand {
80
+ type: "try_now";
81
+ }
82
+ interface BuyNowCommand {
83
+ type: "buy_now";
84
+ }
85
+ interface GetStateCommand {
86
+ type: "get_state";
87
+ }
88
+ type PlayruoEmbedCommand = TryNowCommand | BuyNowCommand | GetStateCommand;
89
+ /**
90
+ * Type guard to check if a message event is a Playruo embed message
91
+ */
92
+ declare const isPlayruoEmbedMessage: (data: unknown) => data is PlayruoEmbedMessage;
93
+ /**
94
+ * Type guard to check if a command is a valid Playruo embed command
95
+ */
96
+ declare const isPlayruoEmbedCommand: (data: unknown) => data is PlayruoEmbedCommand;
97
+
59
98
  interface BuildUrlParams {
60
99
  baseUrl: string;
61
100
  operation?: string;
@@ -63,4 +102,4 @@ interface BuildUrlParams {
63
102
  }
64
103
  declare const buildEmbedUrl: ({ baseUrl, operation, storeUrl }: BuildUrlParams) => string;
65
104
 
66
- export { type DemoCompletedPayload, PlayruoEmbed, type PlayruoEmbedActions, type PlayruoEmbedMessage, type PlayruoEmbedProps, type PlayruoEmbedRenderState, type PlayruoEmbedState, buildEmbedUrl };
105
+ export { type BuyNowCommand, type DemoCompletedPayload, type GetStateCommand, type PlayruoDemoCompletedMessage, PlayruoEmbed, type PlayruoEmbedActions, type PlayruoEmbedCommand, type PlayruoEmbedMessage, type PlayruoEmbedProps, type PlayruoEmbedRenderState, type PlayruoEmbedState, type PlayruoErrorMessage, type PlayruoPurchaseRequestMessage, type PlayruoReadyMessage, type PlayruoStateUpdateMessage, type TryNowCommand, buildEmbedUrl, isPlayruoEmbedCommand, isPlayruoEmbedMessage };
package/dist/index.js CHANGED
@@ -39,18 +39,18 @@ var PlayruoEmbed = ({
39
39
  useEffect(() => {
40
40
  const handler = (event) => {
41
41
  const msg = event?.data ?? {};
42
- if (!msg || typeof msg.type !== "string" || !msg.type.startsWith("playruo")) return;
43
- if (msg.type === "playruo:ready" || msg.type === "playruo_ready") {
42
+ if (!msg || typeof msg.type !== "string" || !msg.type.startsWith("playruo_")) return;
43
+ if (msg.type === "playruo_ready") {
44
44
  setIsReady(true);
45
45
  onReady?.();
46
- } else if (msg.type === "playruo:state" || msg.type === "playruo_state_update") {
46
+ } else if (msg.type === "playruo_state_update") {
47
47
  setState(msg.data);
48
48
  onStateChange?.(msg.data);
49
- } else if (msg.type === "playruo:demoCompleted" || msg.type === "playruo_demo_completed") {
49
+ } else if (msg.type === "playruo_demo_completed") {
50
50
  onDemoCompleted?.(msg.data);
51
- } else if (msg.type === "playruo:purchaseRequest" || msg.type === "playruo_purchase_request") {
51
+ } else if (msg.type === "playruo_purchase_request") {
52
52
  onPurchaseRequest?.(msg.data);
53
- } else if (msg.type === "playruo:error" || msg.type === "playruo_error") {
53
+ } else if (msg.type === "playruo_error") {
54
54
  const message = msg.data?.message ?? "Unknown error";
55
55
  setErrorMessage(message);
56
56
  onError?.(message);
@@ -103,7 +103,23 @@ var PlayruoEmbed = ({
103
103
  )
104
104
  ] });
105
105
  };
106
+
107
+ // src/post-message-types.ts
108
+ var isPlayruoEmbedMessage = (data) => {
109
+ if (!data || typeof data !== "object") return false;
110
+ const msg = data;
111
+ if (typeof msg.type !== "string") return false;
112
+ return msg.type === "playruo_ready" || msg.type === "playruo_state_update" || msg.type === "playruo_purchase_request" || msg.type === "playruo_demo_completed" || msg.type === "playruo_error";
113
+ };
114
+ var isPlayruoEmbedCommand = (data) => {
115
+ if (!data || typeof data !== "object") return false;
116
+ const cmd = data;
117
+ if (typeof cmd.type !== "string") return false;
118
+ return cmd.type === "try_now" || cmd.type === "buy_now" || cmd.type === "get_state";
119
+ };
106
120
  export {
107
121
  PlayruoEmbed,
108
- buildEmbedUrl
122
+ buildEmbedUrl,
123
+ isPlayruoEmbedCommand,
124
+ isPlayruoEmbedMessage
109
125
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@playruo/embed",
3
- "version": "1.1.2",
3
+ "version": "1.1.3",
4
4
  "description": "React SDK to embed Playruo with postMessage API.",
5
5
  "license": "MIT",
6
6
  "type": "module",