@roku-sdk/components 0.0.0-stage → 2.19.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.
Files changed (212) hide show
  1. package/BREAKING_CHANGES.md +84 -0
  2. package/LICENSE +21 -0
  3. package/README.md +19 -2
  4. package/core-jsx.d.ts +53 -0
  5. package/eslint.config.mjs +3 -0
  6. package/jsx.d.ts +273 -0
  7. package/package.json +85 -3
  8. package/src/__tests__/container.test.tsx +150 -0
  9. package/src/__tests__/effect-gradient-validation.test.tsx +133 -0
  10. package/src/__tests__/glyphs.test.ts +202 -0
  11. package/src/__tests__/modal-boundary-export.test.ts +12 -0
  12. package/src/__tests__/native-modal.test.tsx +224 -0
  13. package/src/__tests__/tween-release.test.tsx +125 -0
  14. package/src/__tests__/tween-stops-shape.test.tsx +165 -0
  15. package/src/__tests__/tween-without-compat.test.tsx +64 -0
  16. package/src/__tests__/tween.test.tsx +195 -0
  17. package/src/blended-busy-spinner.props.ts +37 -0
  18. package/src/blended-busy-spinner.tsx +13 -0
  19. package/src/bounding-tracker.props.ts +18 -0
  20. package/src/bounding-tracker.tsx +13 -0
  21. package/src/button-row.tsx +126 -0
  22. package/src/channel-store.props.ts +425 -0
  23. package/src/channel-store.tsx +76 -0
  24. package/src/container.tsx +130 -0
  25. package/src/core/jsx-runtime.ts +39 -0
  26. package/src/focusable-group.props.ts +8 -0
  27. package/src/focusable-group.tsx +17 -0
  28. package/src/focusable.props.ts +14 -0
  29. package/src/focusable.tsx +17 -0
  30. package/src/glyphs.ts +116 -0
  31. package/src/helpers/index.tsx +4 -0
  32. package/src/helpers/resolution-context.tsx +68 -0
  33. package/src/helpers/scale-for-device-resolution.ts +31 -0
  34. package/src/index.tsx +70 -0
  35. package/src/jsx-props.ts +87 -0
  36. package/src/kdfs.d.ts +14 -0
  37. package/src/kdfs.ts +15 -0
  38. package/src/key-handler.props.ts +33 -0
  39. package/src/key-handler.tsx +24 -0
  40. package/src/modal-dialog.props.ts +38 -0
  41. package/src/modal-dialog.tsx +41 -0
  42. package/src/retain-asset.ts +24 -0
  43. package/src/sg-intrinsics.json +1153 -0
  44. package/src/sg-nodes/__tests__/__snapshots__/font.test.tsx.snap +13 -0
  45. package/src/sg-nodes/__tests__/__snapshots__/prop-forwarding.test.tsx.snap +4409 -0
  46. package/src/sg-nodes/__tests__/core-props.test.ts +301 -0
  47. package/src/sg-nodes/__tests__/font.test.tsx +141 -0
  48. package/src/sg-nodes/__tests__/imperative-methods.test.ts +276 -0
  49. package/src/sg-nodes/__tests__/prop-forwarding.test.tsx +1049 -0
  50. package/src/sg-nodes/__tests__/solid-integration.test.tsx +719 -0
  51. package/src/sg-nodes/animated-image.props.ts +64 -0
  52. package/src/sg-nodes/animated-image.tsx +23 -0
  53. package/src/sg-nodes/animation.props.ts +292 -0
  54. package/src/sg-nodes/animation.tsx +190 -0
  55. package/src/sg-nodes/array-grid.ts +602 -0
  56. package/src/sg-nodes/audio.props.ts +302 -0
  57. package/src/sg-nodes/audio.tsx +57 -0
  58. package/src/sg-nodes/busy-spinner.props.ts +59 -0
  59. package/src/sg-nodes/busy-spinner.tsx +84 -0
  60. package/src/sg-nodes/button-group.props.ts +141 -0
  61. package/src/sg-nodes/button-group.tsx +30 -0
  62. package/src/sg-nodes/button.props.ts +119 -0
  63. package/src/sg-nodes/button.tsx +31 -0
  64. package/src/sg-nodes/check-list.props.ts +71 -0
  65. package/src/sg-nodes/check-list.tsx +55 -0
  66. package/src/sg-nodes/content-node.props.ts +1316 -0
  67. package/src/sg-nodes/content-node.tsx +153 -0
  68. package/src/sg-nodes/core-props.ts +197 -0
  69. package/src/sg-nodes/custom-keyboard.props.ts +108 -0
  70. package/src/sg-nodes/custom-keyboard.tsx +48 -0
  71. package/src/sg-nodes/dialog.props.ts +159 -0
  72. package/src/sg-nodes/dialog.tsx +36 -0
  73. package/src/sg-nodes/dynamic-key-grid.props.ts +22 -0
  74. package/src/sg-nodes/dynamic-key-grid.tsx +16 -0
  75. package/src/sg-nodes/dynamic-keyboard.props.ts +30 -0
  76. package/src/sg-nodes/dynamic-keyboard.tsx +18 -0
  77. package/src/sg-nodes/dynamic-mini-keyboard.props.ts +22 -0
  78. package/src/sg-nodes/dynamic-mini-keyboard.tsx +18 -0
  79. package/src/sg-nodes/dynamic-pinpad.props.ts +57 -0
  80. package/src/sg-nodes/dynamic-pinpad.tsx +18 -0
  81. package/src/sg-nodes/effect.props.ts +119 -0
  82. package/src/sg-nodes/effect.tsx +167 -0
  83. package/src/sg-nodes/font.props.ts +45 -0
  84. package/src/sg-nodes/font.tsx +80 -0
  85. package/src/sg-nodes/group.props.ts +105 -0
  86. package/src/sg-nodes/group.tsx +30 -0
  87. package/src/sg-nodes/label-list.props.ts +86 -0
  88. package/src/sg-nodes/label-list.tsx +64 -0
  89. package/src/sg-nodes/label.props.ts +207 -0
  90. package/src/sg-nodes/label.tsx +56 -0
  91. package/src/sg-nodes/layout-group.props.ts +69 -0
  92. package/src/sg-nodes/layout-group.tsx +75 -0
  93. package/src/sg-nodes/list-panel.props.ts +103 -0
  94. package/src/sg-nodes/list-panel.tsx +49 -0
  95. package/src/sg-nodes/markup-grid.props.ts +78 -0
  96. package/src/sg-nodes/markup-grid.tsx +38 -0
  97. package/src/sg-nodes/markup-list.props.ts +66 -0
  98. package/src/sg-nodes/markup-list.tsx +38 -0
  99. package/src/sg-nodes/mask-group.props.ts +78 -0
  100. package/src/sg-nodes/mask-group.tsx +59 -0
  101. package/src/sg-nodes/media-playback-methods.ts +20 -0
  102. package/src/sg-nodes/node.ts +77 -0
  103. package/src/sg-nodes/overhang.props.ts +169 -0
  104. package/src/sg-nodes/overhang.tsx +29 -0
  105. package/src/sg-nodes/panel-set.props.ts +71 -0
  106. package/src/sg-nodes/panel-set.tsx +53 -0
  107. package/src/sg-nodes/panel.props.ts +176 -0
  108. package/src/sg-nodes/panel.tsx +56 -0
  109. package/src/sg-nodes/poster-grid.props.ts +277 -0
  110. package/src/sg-nodes/poster-grid.tsx +45 -0
  111. package/src/sg-nodes/poster.props.ts +125 -0
  112. package/src/sg-nodes/poster.tsx +44 -0
  113. package/src/sg-nodes/radio-button-list.props.ts +59 -0
  114. package/src/sg-nodes/radio-button-list.tsx +68 -0
  115. package/src/sg-nodes/rectangle.props.ts +29 -0
  116. package/src/sg-nodes/rectangle.tsx +27 -0
  117. package/src/sg-nodes/row-list.props.ts +288 -0
  118. package/src/sg-nodes/row-list.tsx +47 -0
  119. package/src/sg-nodes/scene.props.ts +65 -0
  120. package/src/sg-nodes/scene.tsx +15 -0
  121. package/src/sg-nodes/scrollable-text.props.ts +133 -0
  122. package/src/sg-nodes/scrollable-text.tsx +64 -0
  123. package/src/sg-nodes/scrolling-label.props.ts +106 -0
  124. package/src/sg-nodes/scrolling-label.tsx +42 -0
  125. package/src/sg-nodes/simple-label.props.ts +156 -0
  126. package/src/sg-nodes/simple-label.tsx +59 -0
  127. package/src/sg-nodes/sound-effect.props.ts +130 -0
  128. package/src/sg-nodes/sound-effect.tsx +65 -0
  129. package/src/sg-nodes/standard-dialog.props.ts +520 -0
  130. package/src/sg-nodes/standard-dialog.tsx +428 -0
  131. package/src/sg-nodes/standard-keyboard-dialog.props.ts +84 -0
  132. package/src/sg-nodes/standard-keyboard-dialog.tsx +58 -0
  133. package/src/sg-nodes/standard-message-dialog.props.ts +56 -0
  134. package/src/sg-nodes/standard-message-dialog.tsx +47 -0
  135. package/src/sg-nodes/standard-pinpad-dialog.props.ts +64 -0
  136. package/src/sg-nodes/standard-pinpad-dialog.tsx +44 -0
  137. package/src/sg-nodes/standard-progress-dialog.props.ts +30 -0
  138. package/src/sg-nodes/standard-progress-dialog.tsx +40 -0
  139. package/src/sg-nodes/target-group.props.ts +278 -0
  140. package/src/sg-nodes/target-group.tsx +63 -0
  141. package/src/sg-nodes/target-list.props.ts +128 -0
  142. package/src/sg-nodes/target-list.tsx +66 -0
  143. package/src/sg-nodes/target-set.props.ts +63 -0
  144. package/src/sg-nodes/target-set.tsx +42 -0
  145. package/src/sg-nodes/time-grid.props.ts +646 -0
  146. package/src/sg-nodes/time-grid.tsx +87 -0
  147. package/src/sg-nodes/tween.props.ts +115 -0
  148. package/src/sg-nodes/types.ts +150 -0
  149. package/src/sg-nodes/video.props.ts +642 -0
  150. package/src/sg-nodes/video.tsx +75 -0
  151. package/src/sg-nodes/voice-text-edit-box.props.ts +91 -0
  152. package/src/sg-nodes/voice-text-edit-box.tsx +15 -0
  153. package/src/sg-nodes/zoom-row-list.props.ts +416 -0
  154. package/src/sg-nodes/zoom-row-list.tsx +42 -0
  155. package/src/standard-dialog-next/__tests__/standard-dialog-next.test.tsx +186 -0
  156. package/src/standard-dialog-next/index.ts +5 -0
  157. package/src/standard-dialog-next/standard-dialog-next.props.ts +80 -0
  158. package/src/standard-dialog-next/standard-dialog-next.tsx +326 -0
  159. package/src/test-helpers/tween-test-setup.ts +78 -0
  160. package/src/tween.tsx +173 -0
  161. package/src/types.ts +8 -0
  162. package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.brs +13 -0
  163. package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.xml +10 -0
  164. package/src/xml-components/blended-busy-spinner/BlendedBusySpinner.xml.d.ts +42 -0
  165. package/src/xml-components/bounding-tracker/BoundingTracker.brs +26 -0
  166. package/src/xml-components/bounding-tracker/BoundingTracker.xml +14 -0
  167. package/src/xml-components/bounding-tracker/BoundingTracker.xml.d.ts +42 -0
  168. package/src/xml-components/channel-store/channelStoreExt.brs +159 -0
  169. package/src/xml-components/channel-store/channelStoreExt.xml +29 -0
  170. package/src/xml-components/channel-store/channelStoreExt.xml.d.ts +57 -0
  171. package/src/xml-components/focus/focusable-group.brs +251 -0
  172. package/src/xml-components/focus/focusable-group.xml +15 -0
  173. package/src/xml-components/focus/focusable-group.xml.d.ts +42 -0
  174. package/src/xml-components/focus/focusable.xml +18 -0
  175. package/src/xml-components/focus/focusable.xml.d.ts +48 -0
  176. package/src/xml-components/keyboards/CustomAddressKDF.keyboard.json +547 -0
  177. package/src/xml-components/keyboards/CustomKeyboard.brs +322 -0
  178. package/src/xml-components/keyboards/CustomKeyboard.xml +45 -0
  179. package/src/xml-components/keyboards/CustomKeyboard.xml.d.ts +73 -0
  180. package/src/xml-components/keyboards/KeyGridExt.xml +4 -0
  181. package/src/xml-components/keyboards/KeyGridExt.xml.d.ts +37 -0
  182. package/src/xml-components/keyboards/KeyboardExt.xml +4 -0
  183. package/src/xml-components/keyboards/KeyboardExt.xml.d.ts +37 -0
  184. package/src/xml-components/keyboards/MOPPinPadCustomKDF.keyboard.json +151 -0
  185. package/src/xml-components/keyboards/MiniKeyboardExt.xml +4 -0
  186. package/src/xml-components/keyboards/MiniKeyboardExt.xml.d.ts +37 -0
  187. package/src/xml-components/keyboards/PhonePadCustomKDF.keyboard.json +42 -0
  188. package/src/xml-components/keyboards/PinPadExt.brs +102 -0
  189. package/src/xml-components/keyboards/PinPadExt.xml +17 -0
  190. package/src/xml-components/keyboards/PinPadExt.xml.d.ts +49 -0
  191. package/src/xml-components/keyboards/WifiCustomKDF.keyboard.json +1242 -0
  192. package/src/xml-components/keyhandler/KeyHandler.brs +30 -0
  193. package/src/xml-components/keyhandler/KeyHandler.xml +16 -0
  194. package/src/xml-components/keyhandler/KeyHandler.xml.d.ts +45 -0
  195. package/src/xml-components/managed-list-panel/ManagedListPanel.brs +137 -0
  196. package/src/xml-components/managed-list-panel/ManagedListPanel.xml +10 -0
  197. package/src/xml-components/managed-list-panel/ManagedListPanel.xml.d.ts +37 -0
  198. package/src/xml-components/modal/Modal.brs +105 -0
  199. package/src/xml-components/modal/Modal.xml +22 -0
  200. package/src/xml-components/modal/Modal.xml.d.ts +54 -0
  201. package/src/xml-components/modal/StandardDialogExt.xml +10 -0
  202. package/src/xml-components/modal/StandardDialogExt.xml.d.ts +37 -0
  203. package/src/xml-components/modal/StandardKeyboardDialogExt.xml +10 -0
  204. package/src/xml-components/modal/StandardKeyboardDialogExt.xml.d.ts +37 -0
  205. package/src/xml-components/modal/StandardMessageDialogExt.xml +10 -0
  206. package/src/xml-components/modal/StandardMessageDialogExt.xml.d.ts +37 -0
  207. package/src/xml-components/modal/StandardPinPadDialogExt.xml +10 -0
  208. package/src/xml-components/modal/StandardPinPadDialogExt.xml.d.ts +37 -0
  209. package/src/xml-components/modal/StandardProgressDialogExt.xml +10 -0
  210. package/src/xml-components/modal/StandardProgressDialogExt.xml.d.ts +37 -0
  211. package/src/xml-components/modal/tests/Modal.test.brs +195 -0
  212. package/tsconfig.json +10 -0
@@ -0,0 +1,425 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import type { EventHandler, ResObject } from "./sg-nodes/types";
5
+
6
+ export interface IChannelStoreRequestStatusEvent {
7
+ command: string;
8
+ context: Record<string, string>;
9
+ requestid: number;
10
+ result: Record<string, string | number | boolean>;
11
+ status: number;
12
+ statusmessage: string;
13
+ }
14
+
15
+ /**
16
+ * Command strings supported by the ChannelStore node.
17
+ *
18
+ * @see {@link https://developer.roku.com/dev/docs/channelstore}
19
+ */
20
+ export type ChannelStoreCommand =
21
+ | "none"
22
+ | "getUserData"
23
+ | "getUserRegionData"
24
+ | "storeChannelCredData"
25
+ | "getChannelCred"
26
+ | "getCatalog"
27
+ | "doOrder"
28
+ | "getPurchases"
29
+ | "getAllPurchases"
30
+ | "requestPartnerOrder"
31
+ | "confirmPartnerOrder"
32
+ | "getDeviceAttestationToken";
33
+
34
+ /** Context for the RFI (Request for Information) screen. */
35
+ export type ChannelStoreUserDataContext = "signin" | "signup";
36
+
37
+ /**
38
+ * Custom request object for advanced ChannelStore operations.
39
+ */
40
+ export interface IChannelStoreRequest {
41
+ command?: string;
42
+ params?: ResObject<Record<string, string | number | boolean>>;
43
+ context?: ResObject<Record<string, string>>;
44
+ }
45
+
46
+ /**
47
+ * Roku customer account information returned by the `getUserData` command.
48
+ *
49
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | getUserData Command}
50
+ */
51
+ export interface IUserData {
52
+ email?: string;
53
+ phone?: string;
54
+ firstName?: string;
55
+ lastName?: string;
56
+ street?: string;
57
+ city?: string;
58
+ state?: string;
59
+ zip?: string;
60
+ country?: string;
61
+ birth?: string;
62
+ gender?: string;
63
+ }
64
+
65
+ export type EventHandlerUserData = EventHandler<IUserData>;
66
+
67
+ /**
68
+ * Region data returned by the `getUserRegionData` command.
69
+ *
70
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | getUserRegionData Command}
71
+ */
72
+ export interface IUserRegionData {
73
+ state: string;
74
+ zip: string;
75
+ country: string;
76
+ }
77
+
78
+ export type EventHandlerUserRegionData = EventHandler<IUserRegionData>;
79
+
80
+ /**
81
+ * Configuration for the RFI (Request for Information) screen.
82
+ *
83
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | requestedUserDataInfo Field}
84
+ */
85
+ export interface IRequestedUserDataInfo {
86
+ /** Whether the RFI screen is for sign-up or sign-in. */
87
+ context?: ChannelStoreUserDataContext;
88
+ /**
89
+ * When true, the RFI signup screen displays the values of the requested customer
90
+ * information. Has no effect if context is "signin".
91
+ * @default false
92
+ */
93
+ forceShowData?: boolean;
94
+ [key: string]: unknown;
95
+ }
96
+
97
+ /**
98
+ * Status returned by the `storeChannelCredData` command.
99
+ *
100
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | storeChannelCredData Command}
101
+ */
102
+ export interface IStoreChannelCredDataStatus {
103
+ /** JSON string with status, error, and error_detail fields. */
104
+ response: string;
105
+ /** Request status; 0 indicates success. */
106
+ status: number;
107
+ }
108
+
109
+ export type EventHandlerStoreChannelCredDataStatus = EventHandler<IStoreChannelCredDataStatus>;
110
+
111
+ /**
112
+ * Channel credential data returned by the `getChannelCred` command.
113
+ *
114
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | getChannelCred Command}
115
+ */
116
+ export interface IChannelCred {
117
+ /** The app ID (e.g., "2213" for Roku Media Player). */
118
+ channelId: string;
119
+ /** Error description; empty string on success. */
120
+ errorCode: string;
121
+ /** JSON string containing roku_pucid, token_type, channel_data, and error fields. */
122
+ json: string;
123
+ /** Unique identifier of the device. */
124
+ publisherDeviceId: string;
125
+ /** Request status; 0 indicates success. */
126
+ status: number;
127
+ }
128
+
129
+ export type EventHandlerChannelCred = EventHandler<IChannelCred>;
130
+
131
+ /**
132
+ * Product information returned by the `getCatalog` command.
133
+ *
134
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | getCatalog Command}
135
+ */
136
+ export interface IProduct {
137
+ /** Product identifier from the Developer Dashboard. */
138
+ code: string;
139
+ /** Item name / description. */
140
+ name: string;
141
+ /** Quantity (for consumable products). */
142
+ qty: number;
143
+ /** Product type (e.g., "MonthlySub"). */
144
+ productType: string;
145
+ /** Localized cost with currency symbol. */
146
+ cost: string;
147
+ /** Free trial length (e.g., 1 for 1-month, 7 for 7-day). */
148
+ freeTrialQuantity: number;
149
+ /** Free trial unit ("Days" or "Months"). */
150
+ freeTrialType: string;
151
+ /** Introductory pricing discounted price. */
152
+ trialCost: number;
153
+ /** Months of introductory pricing. */
154
+ trialQuantity: number;
155
+ /** Unit for introductory pricing ("months"). */
156
+ trialType: string;
157
+ /** Whether the product is "saved" or "approved for sale". */
158
+ status: string;
159
+ /** Subscription purchase date. */
160
+ purchaseDate: string;
161
+ }
162
+
163
+ export type EventHandlerCatalog = EventHandler<IProduct[]>;
164
+
165
+ /**
166
+ * An item in an order for the `doOrder` command.
167
+ *
168
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | order Field}
169
+ */
170
+ export interface IOrderItem {
171
+ /** Product identifier. */
172
+ code: string;
173
+ /** Quantity to purchase. */
174
+ qty: number;
175
+ /** Set to "Upgrade" or "Downgrade" for subscription plan changes. */
176
+ action?: string;
177
+ }
178
+
179
+ /**
180
+ * Status returned by the `doOrder` command.
181
+ *
182
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | doOrder Command}
183
+ */
184
+ export interface IOrderStatus {
185
+ /** Completion status (1 = success, 2 = interrupted, 0 = network error, etc.). */
186
+ status: number;
187
+ /** Description of the completion status. */
188
+ statusMessage: string;
189
+ }
190
+
191
+ export type EventHandlerOrderStatus = EventHandler<IOrderStatus>;
192
+
193
+ export type EventHandlerOrderItemsStatus = EventHandler<IPurchase[]>;
194
+
195
+ /**
196
+ * Status returned by `getPurchases` / `getAllPurchases` commands.
197
+ */
198
+ export interface IPurchasesStatus {
199
+ status: number;
200
+ statusMessage: string;
201
+ }
202
+
203
+ export type EventHandlerPurchasesStatus = EventHandler<IPurchasesStatus>;
204
+
205
+ /**
206
+ * Purchase information returned by `getPurchases` or `getAllPurchases` commands.
207
+ *
208
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | getPurchases Command}
209
+ */
210
+ export interface IPurchase {
211
+ /** Product identifier. */
212
+ code: string;
213
+ /** Localized cost with currency symbol. */
214
+ cost: string;
215
+ /** Subscription expiration date (ISO 8601). */
216
+ expirationDate: string;
217
+ /** Free trial length. */
218
+ freeTrialQuantity: number;
219
+ /** Free trial unit ("Days" or "Months"). */
220
+ freeTrialType: string;
221
+ /** Whether subscription is past due ("true" if in dunning state). */
222
+ inDunning: string;
223
+ /** Item name / description. */
224
+ name: string;
225
+ /** Product type (e.g., "MonthlySub"). */
226
+ productType: string;
227
+ /** Where the purchase was made ("web" or "device"). */
228
+ purchaseChannel: string;
229
+ /** How the purchase was made ("isu" or "iap"). */
230
+ purchaseContext: string;
231
+ /** Purchase date (ISO 8601). */
232
+ purchaseDate: string;
233
+ /** Transaction ID. */
234
+ purchaseId: string;
235
+ /** Quantity purchased. */
236
+ qty: number;
237
+ /** Subscription renewal date (ISO 8601). */
238
+ renewalDate: string;
239
+ /** "Valid" for current subscriptions, "Invalid" for canceled/expired. */
240
+ status: string;
241
+ /** Description of the status. */
242
+ statusMessage: string;
243
+ /** Introductory pricing discounted price. */
244
+ trialCost: number;
245
+ /** Months of introductory pricing. */
246
+ trialQuantity: number;
247
+ /** Unit for introductory pricing ("months"). */
248
+ trialType: string;
249
+ }
250
+
251
+ export type EventHandlerPurchases = EventHandler<IPurchase[]>;
252
+
253
+ /**
254
+ * Partner order data for TVOD apps (`requestPartnerOrder` / `confirmPartnerOrder`).
255
+ *
256
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | requestPartnerOrder Field}
257
+ */
258
+ export interface IPartnerOrder {
259
+ /** Original price of the product (no currency symbol, e.g., "3.99"). */
260
+ priceDisplay: string;
261
+ /** Final price including discounts (no currency symbol). */
262
+ price: string;
263
+ /** Alphanumeric coupon code for discounted pricing. */
264
+ couponCode?: string;
265
+ /** Publisher-specific SKU or unique identifier. */
266
+ contentKey: string;
267
+ /** Product identifier. */
268
+ code: string;
269
+ /** Product description (e.g., movie title). */
270
+ title?: string;
271
+ /** Order ID returned by Roku (for confirmPartnerOrder). */
272
+ orderID?: string;
273
+ pin?: string;
274
+ }
275
+
276
+ export type EventHandlerPartnerOrder = EventHandler<IPartnerOrder>;
277
+
278
+ /**
279
+ * Device attestation token returned by the `getDeviceAttestationToken` command.
280
+ *
281
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | getDeviceAttestationToken Command}
282
+ */
283
+ export interface IDeviceAttestationToken {
284
+ /** Request status; 0 indicates success. */
285
+ status: number;
286
+ /** The nonce used to generate the token. */
287
+ nonce: string;
288
+ /** Signed JWT for device attestation verification. */
289
+ token: string;
290
+ }
291
+
292
+ export type EventHandlerDeviceAttestationToken = EventHandler<IDeviceAttestationToken>;
293
+
294
+ /**
295
+ * Properties for the ChannelStore component.
296
+ *
297
+ * The ChannelStore component provides an interface to the Roku Streaming Store, enabling in-app purchases,
298
+ * user authentication, and data retrieval. It supports commands for fetching user data, managing orders,
299
+ * retrieving catalogs, and handling credentials. This component wraps the `<channelStoreExt>` XML element
300
+ * and exposes its fields as TypeScript props and event handlers.
301
+ *
302
+ * @see {@link https://developer.roku.com/dev/docs/channelstore}
303
+ */
304
+ export interface IChannelStoreProps {
305
+ id?: string;
306
+ /**
307
+ * Specifies the command to be executed (e.g., "getUserData", "getCatalog").
308
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | ChannelStore Commands}
309
+ */
310
+ command?: ChannelStoreCommand;
311
+ /**
312
+ * Specifies the Roku customer account fields to be retrieved when the `getUserData` command
313
+ * is executed. Set to "all" for all available fields, or a comma-separated list of specific
314
+ * fields (e.g., "email,firstname,lastname").
315
+ * @default "all"
316
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | requestedUserData Field}
317
+ */
318
+ requestedUserData?: string;
319
+ /**
320
+ * Configures whether the RFI screen is used for sign-ups or sign-ins.
321
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | requestedUserDataInfo Field}
322
+ */
323
+ requestedUserDataInfoObject?: IRequestedUserDataInfo;
324
+ /**
325
+ * Fired when user data is retrieved from the `getUserData` command. Returns `invalid`
326
+ * if the user clicked Cancel on the RFI screen.
327
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | userData Field}
328
+ */
329
+ onUserDataObject?: EventHandlerUserData;
330
+ /**
331
+ * Fired when user region data is retrieved from the `getUserRegionData` command.
332
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | getUserRegionData Command}
333
+ */
334
+ onUserRegionDataObject?: EventHandlerUserRegionData;
335
+ /**
336
+ * Custom request object for advanced ChannelStore operations.
337
+ */
338
+ request?: IChannelStoreRequest;
339
+ /**
340
+ * Fired when a custom request completes.
341
+ */
342
+ onRequestStatus?: EventHandler<IChannelStoreRequestStatusEvent>;
343
+ /**
344
+ * Data to be stored securely for channel credentials via the `storeChannelCredData` command.
345
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | channelCredData Field}
346
+ */
347
+ channelCredData?: string;
348
+ /**
349
+ * Fired when storing channel credential data completes.
350
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | storeChannelCredData Command}
351
+ */
352
+ onStoreChannelCredDataStatusObject?: EventHandlerStoreChannelCredDataStatus;
353
+ /**
354
+ * Fired when channel credentials are retrieved via the `getChannelCred` command.
355
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | getChannelCred Command}
356
+ */
357
+ onChannelCredObject?: EventHandlerChannelCred;
358
+ /**
359
+ * Fired when the catalog is retrieved from the `getCatalog` command.
360
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | catalog Field}
361
+ */
362
+ onCatalogObject?: EventHandlerCatalog;
363
+ /**
364
+ * Contains the order to be filled when the `doOrder` command is executed.
365
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | order Field}
366
+ */
367
+ orderObject?: IOrderItem[];
368
+ /**
369
+ * Enables incremental population of the order field. Each time this is set,
370
+ * the order is modified (use negative `qty` to remove items).
371
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | deltaOrder Field}
372
+ */
373
+ deltaOrder?: IOrderItem;
374
+ /**
375
+ * Fired when the `doOrder` command completes with completion status.
376
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | orderStatus Field}
377
+ */
378
+ onOrderStatusObject?: EventHandlerOrderStatus;
379
+ /**
380
+ * Fired when order items are processed (child nodes of orderStatus).
381
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | doOrder Command}
382
+ */
383
+ onOrderItemsStatusObject?: EventHandlerOrderItemsStatus;
384
+ /**
385
+ * Fired when purchases are retrieved from `getPurchases` or `getAllPurchases` commands
386
+ * (completion status).
387
+ *
388
+ * Receives the command's status, not the purchase data: the backing field
389
+ * `purchasesStatusObject` is an `assocarray` holding the request node's own fields, and is
390
+ * `{}` when the request failed. The purchases themselves arrive on
391
+ * {@link IChannelStoreProps.onPurchasesObject}.
392
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | purchases Field}
393
+ */
394
+ onPurchasesStatusObject?: EventHandlerPurchasesStatus;
395
+ /**
396
+ * Fired when purchase data is available from `getPurchases` or `getAllPurchases` commands.
397
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | purchases Field}
398
+ */
399
+ onPurchasesObject?: EventHandlerPurchases;
400
+ /**
401
+ * Specifies the product to be ordered from a TVOD app.
402
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | requestPartnerOrder Field}
403
+ */
404
+ requestPartnerOrderObject?: IPartnerOrder;
405
+ /**
406
+ * Fired when requesting a partner order completes.
407
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | requestPartnerOrderStatus Field}
408
+ */
409
+ onRequestPartnerOrderStatusObject?: EventHandlerPartnerOrder;
410
+ /**
411
+ * Confirms the product being ordered from a TVOD app.
412
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | confirmPartnerOrder Field}
413
+ */
414
+ confirmPartnerOrderObject?: IPartnerOrder;
415
+ /**
416
+ * Fired when confirming a partner order completes.
417
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | confirmPartnerOrderStatus Field}
418
+ */
419
+ onConfirmPartnerOrderStatusObject?: EventHandlerPartnerOrder;
420
+ /**
421
+ * Fired when a device attestation token is generated via `getDeviceAttestationToken`.
422
+ * @see {@link https://developer.roku.com/dev/docs/channelstore | getDeviceAttestationToken Command}
423
+ */
424
+ onDeviceAttestationTokenObject?: EventHandlerDeviceAttestationToken;
425
+ }
@@ -0,0 +1,76 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ // TODO: This component uses a custom XML extension (channelStoreExt) rather than a
5
+ // native SG node, so it lives in src/ alongside other custom components (like modal-dialog)
6
+ // instead of in sg-nodes/.
7
+
8
+ import type { IChannelStoreProps } from "./channel-store.props";
9
+ import { retainAsset } from "./retain-asset";
10
+ import { Component } from "./sg-nodes/types";
11
+ import ChannelStoreXML from "./xml-components/channel-store/channelStoreExt.xml";
12
+ export type { IChannelStoreRequestStatusEvent } from "./channel-store.props";
13
+
14
+ export type {
15
+ ChannelStoreCommand,
16
+ ChannelStoreUserDataContext,
17
+ IChannelStoreRequest,
18
+ IUserData,
19
+ EventHandlerUserData,
20
+ IUserRegionData,
21
+ EventHandlerUserRegionData,
22
+ IRequestedUserDataInfo,
23
+ IStoreChannelCredDataStatus,
24
+ EventHandlerStoreChannelCredDataStatus,
25
+ IChannelCred,
26
+ EventHandlerChannelCred,
27
+ IProduct,
28
+ EventHandlerCatalog,
29
+ IOrderItem,
30
+ IOrderStatus,
31
+ EventHandlerOrderStatus,
32
+ EventHandlerOrderItemsStatus,
33
+ IPurchasesStatus,
34
+ EventHandlerPurchasesStatus,
35
+ IPurchase,
36
+ EventHandlerPurchases,
37
+ IPartnerOrder,
38
+ EventHandlerPartnerOrder,
39
+ IDeviceAttestationToken,
40
+ EventHandlerDeviceAttestationToken,
41
+ IChannelStoreProps
42
+ } from "./channel-store.props";
43
+
44
+ /**
45
+ * ChannelStore provides an interface to the Roku Streaming Store for in-app purchases,
46
+ * user authentication, subscription management, and credential storage.
47
+ *
48
+ * Usage follows a command pattern: set required data fields, observe the result event,
49
+ * then set `command` to trigger execution. Results arrive asynchronously via event handlers.
50
+ *
51
+ * @example
52
+ * ```tsx
53
+ * // Retrieve user data for sign-in
54
+ * <ChannelStore
55
+ * requestedUserData="email,firstname,lastname"
56
+ * command="getUserData"
57
+ * onUserDataObject={(data) => console.log("User data:", data)}
58
+ * />
59
+ * ```
60
+ *
61
+ * @example
62
+ * ```tsx
63
+ * // Perform an in-app purchase
64
+ * <ChannelStore
65
+ * orderObject={[{ code: "item1", qty: 1 }]}
66
+ * command="doOrder"
67
+ * onOrderStatusObject={(status) => console.log("Order status:", status)}
68
+ * />
69
+ * ```
70
+ *
71
+ * @see {@link https://developer.roku.com/dev/docs/channelstore}
72
+ */
73
+ export const ChannelStore: Component<IChannelStoreProps> = props => {
74
+ retainAsset(ChannelStoreXML);
75
+ return <channelStoreExt {...props}></channelStoreExt>;
76
+ };
@@ -0,0 +1,130 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import { transactionManager } from "@roku-sdk/common/transaction";
5
+ import { ComponentRef, DomNode } from "@roku-sdk/interfaces";
6
+ import { Component, JSX, splitProps, tmTransaction } from "@roku-sdk/solid";
7
+
8
+ import { IGroupProps } from "./sg-nodes/group";
9
+
10
+ /**
11
+ * Reference type for the Container component.
12
+ *
13
+ * Extends {@link ComponentRef} with imperative methods for dynamic node
14
+ * insertion and removal. This provides a typed public API for pooling
15
+ * patterns that need to reparent nodes at runtime.
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * let containerRef!: ContainerRef;
20
+ *
21
+ * // Acquire a pooled node and insert it
22
+ * const node = pool.acquire();
23
+ * containerRef.insertNode(node.element);
24
+ *
25
+ * // Later, remove the node (returns it to pool)
26
+ * containerRef.removeNode(node.element);
27
+ * pool.release(node);
28
+ * ```
29
+ */
30
+ export interface ContainerRef extends ComponentRef<IGroupProps> {
31
+ /**
32
+ * Inserts a node as a child of this container.
33
+ * The node is appended to the end of the children list.
34
+ *
35
+ * @param node - The DomNode to insert into this container
36
+ */
37
+ insertNode: (node: DomNode) => void;
38
+
39
+ /**
40
+ * Removes a node from this container.
41
+ * The node is marked for removal and will be detached on the next commit.
42
+ *
43
+ * On firmware 15.2.x the parent link is the only strong reference to a
44
+ * SceneGraph node, so a committed detach destroys it and a later
45
+ * `insertNode` of the same node hands the render thread a deleted
46
+ * reference. Pooling that has to run on 15.2.x should reparent through
47
+ * {@link ContainerRef.insertNode} into a hidden container rather than
48
+ * detaching.
49
+ *
50
+ * @param node - The DomNode to remove from this container
51
+ */
52
+ removeNode: (node: DomNode) => void;
53
+ }
54
+
55
+ /**
56
+ * Props for the Container component.
57
+ */
58
+ export interface IContainerProps extends Omit<IGroupProps, "ref"> {
59
+ /** Callback ref to receive the ContainerRef with imperative methods. */
60
+ ref?: (ref: ContainerRef) => void;
61
+ /** Child elements to render inside the container. */
62
+ children?: JSX.Element;
63
+ }
64
+
65
+ /**
66
+ * Container is a Group wrapper that exposes typed imperative methods for
67
+ * dynamic node insertion and removal.
68
+ *
69
+ * This component is designed for advanced pooling patterns where nodes need
70
+ * to be reparented at runtime without triggering full reactive re-renders.
71
+ * It wraps the raw `tmTransaction.insertNode/removeNode` calls with a typed
72
+ * API, eliminating the need for `as any` casts and eslint-disable comments.
73
+ *
74
+ * @example
75
+ * ```tsx
76
+ * import { Container, ContainerRef } from "@roku-sdk/components";
77
+ *
78
+ * const PooledGrid: Component = () => {
79
+ * let slotContainerRef!: ContainerRef;
80
+ *
81
+ * createEffect(() => {
82
+ * const node = pool.acquire();
83
+ * slotContainerRef.insertNode(node.element);
84
+ * });
85
+ *
86
+ * return (
87
+ * <Container ref={(el) => (slotContainerRef = el)}>
88
+ * {/* Pooled nodes are inserted here dynamically *\/}
89
+ * </Container>
90
+ * );
91
+ * };
92
+ * ```
93
+ *
94
+ * @see {@link ContainerRef} for the imperative methods available on the ref
95
+ */
96
+ export const Container: Component<IContainerProps> = props => {
97
+ // Use splitProps to preserve reactivity on spread props
98
+ const [local, groupProps] = splitProps(props, ["ref", "children"]);
99
+
100
+ return (
101
+ <group
102
+ {...groupProps}
103
+ ref={(el: DomNode) => {
104
+ // Extend the element with imperative methods
105
+ const containerRef = el as ContainerRef;
106
+
107
+ // `tmTransaction` assumes an open transaction — the Solid renderer
108
+ // opens one before every call it makes. These methods run outside the
109
+ // renderer, so they open it themselves. `insertNode` reaches native
110
+ // `appendNode` synchronously (firmware 15.2.x raises a render-thread
111
+ // exception if no transaction is open) and `removeNode` only queues
112
+ // the detach for the pre-commit hook, which a commit has to drive.
113
+ containerRef.insertNode = (node: DomNode) => {
114
+ transactionManager.startTransaction();
115
+ tmTransaction.insertNode(el, node);
116
+ };
117
+
118
+ containerRef.removeNode = (node: DomNode) => {
119
+ transactionManager.startTransaction();
120
+ tmTransaction.removeNode(el, node);
121
+ };
122
+
123
+ // Pass the extended ref to the consumer
124
+ local.ref?.(containerRef);
125
+ }}
126
+ >
127
+ {local.children}
128
+ </group>
129
+ );
130
+ };
@@ -0,0 +1,39 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ /**
5
+ * JSX runtime entry point for the **frozen core** property contract.
6
+ *
7
+ * This module exists to be named by `jsxImportSource` in an application's tsconfig:
8
+ *
9
+ * ```jsonc
10
+ * { "compilerOptions": { "jsxImportSource": "@roku-sdk/components/core" } }
11
+ * ```
12
+ *
13
+ * TypeScript then resolves JSX intrinsic element types from
14
+ * `@roku-sdk/components/core/jsx-runtime`, and the sibling `core-jsx.d.ts` augments exactly this
15
+ * specifier with an intrinsic map keyed to the `I*CoreProps` interfaces. So `<poster>` resolves to
16
+ * `IPosterCoreProps` rather than the full `IPosterProps`, and a property outside the frozen subset
17
+ * is a compile error naming the property.
18
+ *
19
+ * Two things make this the enforcement point rather than a convenience:
20
+ *
21
+ * 1. **A module-scoped `JSX` namespace replaces the global one, it does not merge with it.** The
22
+ * full intrinsic map in `rsg-ts/src/types/sg-jsx.d.ts` augments a different specifier, so it is
23
+ * not in scope for a build that points here — provided that map is kept out of the program.
24
+ * That is the one easily-missed part of the setup: an app opting in must also drop
25
+ * `@roku-sdk/components/jsx` from `types[]` and the inherited `sg-jsx.d.ts` from `files[]`,
26
+ * because the star re-export below would otherwise carry those augmentations back in.
27
+ * 2. **Application source is unchanged.** Only the tsconfig differs.
28
+ *
29
+ * The runtime behaviour is unchanged — the factory is re-exported from `@roku-sdk/solid` — because
30
+ * the divergence is purely a compile-time *type* contract, not a second implementation. Builds do
31
+ * not read `jsxImportSource` at all: esbuild is configured with a classic `jsxFactory` and
32
+ * `babel-preset-solid` is pinned to `moduleName: "@roku-sdk/solid"`.
33
+ *
34
+ * The re-export must stay a star re-export. TypeScript resolves `JSX.Element`,
35
+ * `JSX.ElementChildrenAttribute` and `JSX.IntrinsicAttributes` from this same module, and those
36
+ * come from SolidJS; a named re-export of the factory alone would leave them undefined.
37
+ */
38
+
39
+ export * from "@roku-sdk/solid/jsx-runtime";
@@ -0,0 +1,8 @@
1
+ // Copyright (c) 2026 Roku, Inc.
2
+ // SPDX-License-Identifier: MIT
3
+
4
+ import type { IGroupProps } from "./sg-nodes/group.props";
5
+
6
+ export interface IFocusableGroupProps extends IGroupProps {
7
+ rememberLastFocus?: boolean;
8
+ }