@pushframe/sdk 0.1.4 → 0.1.8

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 (288) hide show
  1. package/lib/commonjs/PushframeProvider.js +69 -0
  2. package/lib/commonjs/PushframeProvider.js.map +1 -0
  3. package/lib/commonjs/PushframeScreen.js +69 -0
  4. package/lib/commonjs/PushframeScreen.js.map +1 -0
  5. package/lib/commonjs/bindings.js +73 -0
  6. package/lib/commonjs/bindings.js.map +1 -0
  7. package/lib/commonjs/components/ButtonComponent.js +72 -0
  8. package/lib/commonjs/components/ButtonComponent.js.map +1 -0
  9. package/lib/commonjs/components/FlatListComponent.js +73 -0
  10. package/lib/commonjs/components/FlatListComponent.js.map +1 -0
  11. package/lib/commonjs/components/ImageComponent.js +67 -0
  12. package/lib/commonjs/components/ImageComponent.js.map +1 -0
  13. package/lib/commonjs/components/PushFrameComponent.js +179 -0
  14. package/lib/commonjs/components/PushFrameComponent.js.map +1 -0
  15. package/lib/commonjs/components/PushFrameProvider.js +115 -0
  16. package/lib/commonjs/components/PushFrameProvider.js.map +1 -0
  17. package/lib/commonjs/components/PushFrameScreen.js +39 -0
  18. package/lib/commonjs/components/PushFrameScreen.js.map +1 -0
  19. package/lib/commonjs/components/ScrollViewComponent.js +64 -0
  20. package/lib/commonjs/components/ScrollViewComponent.js.map +1 -0
  21. package/lib/commonjs/components/StackComponent.js +61 -0
  22. package/lib/commonjs/components/StackComponent.js.map +1 -0
  23. package/lib/commonjs/components/TextComponent.js +62 -0
  24. package/lib/commonjs/components/TextComponent.js.map +1 -0
  25. package/lib/commonjs/conditions.js +44 -0
  26. package/lib/commonjs/conditions.js.map +1 -0
  27. package/lib/commonjs/context/PushFrameContext.js +33 -0
  28. package/lib/commonjs/context/PushFrameContext.js.map +1 -0
  29. package/lib/commonjs/index.js +200 -0
  30. package/lib/commonjs/index.js.map +1 -0
  31. package/lib/commonjs/overlays/BottomSheetHost.js +144 -0
  32. package/lib/commonjs/overlays/BottomSheetHost.js.map +1 -0
  33. package/lib/commonjs/overlays/ToastHost.js +135 -0
  34. package/lib/commonjs/overlays/ToastHost.js.map +1 -0
  35. package/lib/commonjs/package.json +1 -0
  36. package/lib/commonjs/primitives/ActivityIndicator.js +24 -0
  37. package/lib/commonjs/primitives/ActivityIndicator.js.map +1 -0
  38. package/lib/commonjs/primitives/FlatList.js +34 -0
  39. package/lib/commonjs/primitives/FlatList.js.map +1 -0
  40. package/lib/commonjs/primitives/Image.js +33 -0
  41. package/lib/commonjs/primitives/Image.js.map +1 -0
  42. package/lib/commonjs/primitives/KeyboardAvoidingView.js +24 -0
  43. package/lib/commonjs/primitives/KeyboardAvoidingView.js.map +1 -0
  44. package/lib/commonjs/primitives/Modal.js +24 -0
  45. package/lib/commonjs/primitives/Modal.js.map +1 -0
  46. package/lib/commonjs/primitives/Pressable.js +26 -0
  47. package/lib/commonjs/primitives/Pressable.js.map +1 -0
  48. package/lib/commonjs/primitives/SafeAreaView.js +38 -0
  49. package/lib/commonjs/primitives/SafeAreaView.js.map +1 -0
  50. package/lib/commonjs/primitives/ScrollView.js +26 -0
  51. package/lib/commonjs/primitives/ScrollView.js.map +1 -0
  52. package/lib/commonjs/primitives/StatusBar.js +24 -0
  53. package/lib/commonjs/primitives/StatusBar.js.map +1 -0
  54. package/lib/commonjs/primitives/Switch.js +28 -0
  55. package/lib/commonjs/primitives/Switch.js.map +1 -0
  56. package/lib/commonjs/primitives/Text.js +37 -0
  57. package/lib/commonjs/primitives/Text.js.map +1 -0
  58. package/lib/commonjs/primitives/TextInput.js +31 -0
  59. package/lib/commonjs/primitives/TextInput.js.map +1 -0
  60. package/lib/commonjs/primitives/View.js +24 -0
  61. package/lib/commonjs/primitives/View.js.map +1 -0
  62. package/lib/commonjs/primitives/index.js +97 -0
  63. package/lib/commonjs/primitives/index.js.map +1 -0
  64. package/lib/commonjs/registry/ComponentRegistry.js +70 -0
  65. package/lib/commonjs/registry/ComponentRegistry.js.map +1 -0
  66. package/lib/commonjs/registry.js +94 -0
  67. package/lib/commonjs/registry.js.map +1 -0
  68. package/lib/commonjs/renderer/RecursiveRenderer.js +202 -0
  69. package/lib/commonjs/renderer/RecursiveRenderer.js.map +1 -0
  70. package/lib/commonjs/renderer/bindingResolver.js +98 -0
  71. package/lib/commonjs/renderer/bindingResolver.js.map +1 -0
  72. package/lib/commonjs/renderer/conditionalEvaluator.js +31 -0
  73. package/lib/commonjs/renderer/conditionalEvaluator.js.map +1 -0
  74. package/lib/commonjs/renderer.js +107 -0
  75. package/lib/commonjs/renderer.js.map +1 -0
  76. package/lib/commonjs/schema.js +79 -0
  77. package/lib/commonjs/schema.js.map +1 -0
  78. package/lib/commonjs/transformer/index.js +1055 -0
  79. package/lib/commonjs/transformer/index.js.map +1 -0
  80. package/lib/commonjs/transport.js +86 -0
  81. package/lib/commonjs/transport.js.map +1 -0
  82. package/lib/module/PushframeProvider.js +62 -0
  83. package/lib/module/PushframeProvider.js.map +1 -0
  84. package/lib/module/PushframeScreen.js +65 -0
  85. package/lib/module/PushframeScreen.js.map +1 -0
  86. package/lib/module/bindings.js +68 -0
  87. package/lib/module/bindings.js.map +1 -0
  88. package/lib/module/components/ButtonComponent.js +67 -0
  89. package/lib/module/components/ButtonComponent.js.map +1 -0
  90. package/lib/module/components/FlatListComponent.js +68 -0
  91. package/lib/module/components/FlatListComponent.js.map +1 -0
  92. package/lib/module/components/ImageComponent.js +62 -0
  93. package/lib/module/components/ImageComponent.js.map +1 -0
  94. package/lib/module/components/PushFrameComponent.js +174 -0
  95. package/lib/module/components/PushFrameComponent.js.map +1 -0
  96. package/lib/module/components/PushFrameProvider.js +110 -0
  97. package/lib/module/components/PushFrameProvider.js.map +1 -0
  98. package/lib/module/components/PushFrameScreen.js +34 -0
  99. package/lib/module/components/PushFrameScreen.js.map +1 -0
  100. package/lib/module/components/ScrollViewComponent.js +59 -0
  101. package/lib/module/components/ScrollViewComponent.js.map +1 -0
  102. package/lib/module/components/StackComponent.js +56 -0
  103. package/lib/module/components/StackComponent.js.map +1 -0
  104. package/lib/module/components/TextComponent.js +57 -0
  105. package/lib/module/components/TextComponent.js.map +1 -0
  106. package/lib/module/conditions.js +40 -0
  107. package/lib/module/conditions.js.map +1 -0
  108. package/lib/module/context/PushFrameContext.js +29 -0
  109. package/lib/module/context/PushFrameContext.js.map +1 -0
  110. package/lib/module/index.js +99 -0
  111. package/lib/module/index.js.map +1 -0
  112. package/lib/module/overlays/BottomSheetHost.js +139 -0
  113. package/lib/module/overlays/BottomSheetHost.js.map +1 -0
  114. package/lib/module/overlays/ToastHost.js +130 -0
  115. package/lib/module/overlays/ToastHost.js.map +1 -0
  116. package/lib/module/primitives/ActivityIndicator.js +19 -0
  117. package/lib/module/primitives/ActivityIndicator.js.map +1 -0
  118. package/lib/module/primitives/FlatList.js +29 -0
  119. package/lib/module/primitives/FlatList.js.map +1 -0
  120. package/lib/module/primitives/Image.js +28 -0
  121. package/lib/module/primitives/Image.js.map +1 -0
  122. package/lib/module/primitives/KeyboardAvoidingView.js +19 -0
  123. package/lib/module/primitives/KeyboardAvoidingView.js.map +1 -0
  124. package/lib/module/primitives/Modal.js +19 -0
  125. package/lib/module/primitives/Modal.js.map +1 -0
  126. package/lib/module/primitives/Pressable.js +21 -0
  127. package/lib/module/primitives/Pressable.js.map +1 -0
  128. package/lib/module/primitives/SafeAreaView.js +33 -0
  129. package/lib/module/primitives/SafeAreaView.js.map +1 -0
  130. package/lib/module/primitives/ScrollView.js +21 -0
  131. package/lib/module/primitives/ScrollView.js.map +1 -0
  132. package/lib/module/primitives/StatusBar.js +19 -0
  133. package/lib/module/primitives/StatusBar.js.map +1 -0
  134. package/lib/module/primitives/Switch.js +23 -0
  135. package/lib/module/primitives/Switch.js.map +1 -0
  136. package/lib/module/primitives/Text.js +32 -0
  137. package/lib/module/primitives/Text.js.map +1 -0
  138. package/lib/module/primitives/TextInput.js +26 -0
  139. package/lib/module/primitives/TextInput.js.map +1 -0
  140. package/lib/module/primitives/View.js +19 -0
  141. package/lib/module/primitives/View.js.map +1 -0
  142. package/lib/module/primitives/index.js +16 -0
  143. package/lib/module/primitives/index.js.map +1 -0
  144. package/lib/module/registry/ComponentRegistry.js +66 -0
  145. package/lib/module/registry/ComponentRegistry.js.map +1 -0
  146. package/lib/module/registry.js +88 -0
  147. package/lib/module/registry.js.map +1 -0
  148. package/lib/module/renderer/RecursiveRenderer.js +197 -0
  149. package/lib/module/renderer/RecursiveRenderer.js.map +1 -0
  150. package/lib/module/renderer/bindingResolver.js +92 -0
  151. package/lib/module/renderer/bindingResolver.js.map +1 -0
  152. package/lib/module/renderer/conditionalEvaluator.js +28 -0
  153. package/lib/module/renderer/conditionalEvaluator.js.map +1 -0
  154. package/lib/module/renderer.js +103 -0
  155. package/lib/module/renderer.js.map +1 -0
  156. package/lib/module/schema.js +74 -0
  157. package/lib/module/schema.js.map +1 -0
  158. package/lib/module/transformer/index.js +1051 -0
  159. package/lib/module/transformer/index.js.map +1 -0
  160. package/lib/module/transport.js +82 -0
  161. package/lib/module/transport.js.map +1 -0
  162. package/lib/typescript/PushframeProvider.d.ts +58 -0
  163. package/lib/typescript/PushframeProvider.d.ts.map +1 -0
  164. package/lib/typescript/PushframeScreen.d.ts +36 -0
  165. package/lib/typescript/PushframeScreen.d.ts.map +1 -0
  166. package/lib/typescript/bindings.d.ts +29 -0
  167. package/lib/typescript/bindings.d.ts.map +1 -0
  168. package/lib/typescript/components/ButtonComponent.d.ts +11 -0
  169. package/lib/typescript/components/ButtonComponent.d.ts.map +1 -0
  170. package/lib/typescript/components/FlatListComponent.d.ts +28 -0
  171. package/lib/typescript/components/FlatListComponent.d.ts.map +1 -0
  172. package/lib/typescript/components/ImageComponent.d.ts +12 -0
  173. package/lib/typescript/components/ImageComponent.d.ts.map +1 -0
  174. package/lib/typescript/components/PushFrameComponent.d.ts +48 -0
  175. package/lib/typescript/components/PushFrameComponent.d.ts.map +1 -0
  176. package/lib/typescript/components/PushFrameProvider.d.ts +51 -0
  177. package/lib/typescript/components/PushFrameProvider.d.ts.map +1 -0
  178. package/lib/typescript/components/PushFrameScreen.d.ts +15 -0
  179. package/lib/typescript/components/PushFrameScreen.d.ts.map +1 -0
  180. package/lib/typescript/components/ScrollViewComponent.d.ts +19 -0
  181. package/lib/typescript/components/ScrollViewComponent.d.ts.map +1 -0
  182. package/lib/typescript/components/StackComponent.d.ts +16 -0
  183. package/lib/typescript/components/StackComponent.d.ts.map +1 -0
  184. package/lib/typescript/components/TextComponent.d.ts +13 -0
  185. package/lib/typescript/components/TextComponent.d.ts.map +1 -0
  186. package/lib/typescript/conditions.d.ts +12 -0
  187. package/lib/typescript/conditions.d.ts.map +1 -0
  188. package/lib/typescript/context/PushFrameContext.d.ts +57 -0
  189. package/lib/typescript/context/PushFrameContext.d.ts.map +1 -0
  190. package/lib/typescript/index.d.ts +74 -0
  191. package/lib/typescript/index.d.ts.map +1 -0
  192. package/lib/typescript/overlays/BottomSheetHost.d.ts +21 -0
  193. package/lib/typescript/overlays/BottomSheetHost.d.ts.map +1 -0
  194. package/lib/typescript/overlays/ToastHost.d.ts +12 -0
  195. package/lib/typescript/overlays/ToastHost.d.ts.map +1 -0
  196. package/lib/typescript/primitives/ActivityIndicator.d.ts +12 -0
  197. package/lib/typescript/primitives/ActivityIndicator.d.ts.map +1 -0
  198. package/lib/typescript/primitives/FlatList.d.ts +29 -0
  199. package/lib/typescript/primitives/FlatList.d.ts.map +1 -0
  200. package/lib/typescript/primitives/Image.d.ts +20 -0
  201. package/lib/typescript/primitives/Image.d.ts.map +1 -0
  202. package/lib/typescript/primitives/KeyboardAvoidingView.d.ts +12 -0
  203. package/lib/typescript/primitives/KeyboardAvoidingView.d.ts.map +1 -0
  204. package/lib/typescript/primitives/Modal.d.ts +12 -0
  205. package/lib/typescript/primitives/Modal.d.ts.map +1 -0
  206. package/lib/typescript/primitives/Pressable.d.ts +14 -0
  207. package/lib/typescript/primitives/Pressable.d.ts.map +1 -0
  208. package/lib/typescript/primitives/SafeAreaView.d.ts +20 -0
  209. package/lib/typescript/primitives/SafeAreaView.d.ts.map +1 -0
  210. package/lib/typescript/primitives/ScrollView.d.ts +15 -0
  211. package/lib/typescript/primitives/ScrollView.d.ts.map +1 -0
  212. package/lib/typescript/primitives/StatusBar.d.ts +12 -0
  213. package/lib/typescript/primitives/StatusBar.d.ts.map +1 -0
  214. package/lib/typescript/primitives/Switch.d.ts +19 -0
  215. package/lib/typescript/primitives/Switch.d.ts.map +1 -0
  216. package/lib/typescript/primitives/Text.d.ts +25 -0
  217. package/lib/typescript/primitives/Text.d.ts.map +1 -0
  218. package/lib/typescript/primitives/TextInput.d.ts +25 -0
  219. package/lib/typescript/primitives/TextInput.d.ts.map +1 -0
  220. package/lib/typescript/primitives/View.d.ts +12 -0
  221. package/lib/typescript/primitives/View.d.ts.map +1 -0
  222. package/lib/typescript/primitives/index.d.ts +27 -0
  223. package/lib/typescript/primitives/index.d.ts.map +1 -0
  224. package/lib/typescript/registry/ComponentRegistry.d.ts +21 -0
  225. package/lib/typescript/registry/ComponentRegistry.d.ts.map +1 -0
  226. package/lib/typescript/registry.d.ts +57 -0
  227. package/lib/typescript/registry.d.ts.map +1 -0
  228. package/lib/typescript/renderer/RecursiveRenderer.d.ts +32 -0
  229. package/lib/typescript/renderer/RecursiveRenderer.d.ts.map +1 -0
  230. package/lib/typescript/renderer/bindingResolver.d.ts +26 -0
  231. package/lib/typescript/renderer/bindingResolver.d.ts.map +1 -0
  232. package/lib/typescript/renderer/conditionalEvaluator.d.ts +15 -0
  233. package/lib/typescript/renderer/conditionalEvaluator.d.ts.map +1 -0
  234. package/lib/typescript/renderer.d.ts +29 -0
  235. package/lib/typescript/renderer.d.ts.map +1 -0
  236. package/lib/typescript/schema.d.ts +84 -0
  237. package/lib/typescript/schema.d.ts.map +1 -0
  238. package/lib/typescript/transformer/index.d.ts +49 -0
  239. package/lib/typescript/transformer/index.d.ts.map +1 -0
  240. package/lib/typescript/transport.d.ts +19 -0
  241. package/lib/typescript/transport.d.ts.map +1 -0
  242. package/package.json +20 -18
  243. package/src/PushframeProvider.tsx +119 -0
  244. package/src/PushframeScreen.tsx +107 -0
  245. package/src/bindings.ts +72 -0
  246. package/src/components/ButtonComponent.tsx +87 -0
  247. package/src/components/FlatListComponent.tsx +86 -0
  248. package/src/components/ImageComponent.tsx +70 -0
  249. package/src/components/PushFrameComponent.tsx +221 -0
  250. package/src/components/PushFrameProvider.tsx +177 -0
  251. package/src/components/PushFrameScreen.tsx +30 -0
  252. package/src/components/ScrollViewComponent.tsx +65 -0
  253. package/src/components/StackComponent.tsx +69 -0
  254. package/src/components/TextComponent.tsx +60 -0
  255. package/src/conditions.ts +46 -0
  256. package/src/context/PushFrameContext.ts +89 -0
  257. package/src/index.ts +119 -0
  258. package/src/overlays/BottomSheetHost.tsx +175 -0
  259. package/src/overlays/ToastHost.tsx +147 -0
  260. package/src/primitives/ActivityIndicator.tsx +21 -0
  261. package/src/primitives/FlatList.tsx +49 -0
  262. package/src/primitives/Image.tsx +26 -0
  263. package/src/primitives/KeyboardAvoidingView.tsx +21 -0
  264. package/src/primitives/Modal.tsx +17 -0
  265. package/src/primitives/Pressable.tsx +19 -0
  266. package/src/primitives/SafeAreaView.tsx +42 -0
  267. package/src/primitives/ScrollView.tsx +21 -0
  268. package/src/primitives/StatusBar.tsx +17 -0
  269. package/src/primitives/Switch.tsx +24 -0
  270. package/src/primitives/Text.tsx +43 -0
  271. package/src/primitives/TextInput.tsx +42 -0
  272. package/src/primitives/View.tsx +17 -0
  273. package/src/primitives/index.ts +38 -0
  274. package/src/registry/ComponentRegistry.ts +99 -0
  275. package/src/registry.ts +99 -0
  276. package/src/renderer/RecursiveRenderer.tsx +242 -0
  277. package/src/renderer/bindingResolver.ts +94 -0
  278. package/src/renderer/conditionalEvaluator.ts +29 -0
  279. package/src/renderer.tsx +124 -0
  280. package/src/schema.ts +132 -0
  281. package/src/transformer/index.ts +1016 -0
  282. package/src/transport.ts +104 -0
  283. package/dist/index.d.mts +0 -534
  284. package/dist/index.d.ts +0 -534
  285. package/dist/index.js +0 -1572
  286. package/dist/index.js.map +0 -1
  287. package/dist/index.mjs +0 -1541
  288. package/dist/index.mjs.map +0 -1
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import type { ReactElement } from 'react';
3
+ import type { SchemaNode } from '../context/PushFrameContext';
4
+ import type { ComponentRegistry } from '../registry/ComponentRegistry';
5
+ export interface DispatchAction {
6
+ (actionName: string, payload?: Record<string, unknown>): void;
7
+ }
8
+ export interface RecursiveRendererProps {
9
+ node: SchemaNode;
10
+ /** Current merged data context for binding resolution. */
11
+ context: Record<string, unknown>;
12
+ registry: ComponentRegistry;
13
+ /** Handles all actions — built-ins and host-app bubbling. Provided by the slot component. */
14
+ dispatchAction: DispatchAction;
15
+ /** Shown when a component type is not found in the registry. */
16
+ fallbackComponent?: React.ReactNode;
17
+ }
18
+ /**
19
+ * Stateless recursive renderer. Evaluates schema nodes and produces React elements.
20
+ *
21
+ * - Evaluates `if` expression (string or non-string); returns null when falsy
22
+ * - Handles `visible` prop: returns null when visible resolves to false
23
+ * - Handles FlatList nodes with per-item context injection via `itemTemplate`
24
+ * - Supports `data` (transformer) and `items` (legacy) as FlatList data source
25
+ * - Resolves all prop bindings against current context
26
+ * - Merges `_propValues` into child context (custom component prop injection)
27
+ * - Wires actions to component event handler props
28
+ * - Looks up component in registry; renders fallback or null when not found
29
+ * - Recurses into children
30
+ */
31
+ export declare function RecursiveRenderer({ node, context, registry, dispatchAction, fallbackComponent, }: RecursiveRendererProps): ReactElement | null;
32
+ //# sourceMappingURL=RecursiveRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RecursiveRenderer.d.ts","sourceRoot":"","sources":["../../../src/renderer/RecursiveRenderer.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAC1B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAA8B,MAAM,6BAA6B,CAAC;AAC1F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAQvE,MAAM,WAAW,cAAc;IAC7B,CAAC,UAAU,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC;CAC/D;AAED,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,UAAU,CAAC;IACjB,0DAA0D;IAC1D,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,6FAA6F;IAC7F,cAAc,EAAE,cAAc,CAAC;IAC/B,gEAAgE;IAChE,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACrC;AA4HD;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,cAAc,EACd,iBAAiB,GAClB,EAAE,sBAAsB,GAAG,YAAY,GAAG,IAAI,CA0E9C"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Binding resolver for PushFrame {{expression}} syntax.
3
+ *
4
+ * Rules:
5
+ * - A value that is entirely "{{expr}}" resolves to the bound value (any type)
6
+ * - A value with inline bindings "Hello {{user.name}}" resolves to a string
7
+ * - Unresolved paths return undefined — never throws
8
+ * - Non-string values are returned as-is
9
+ */
10
+ /**
11
+ * Resolve a single prop value against context.
12
+ *
13
+ * - String values: check for full binding "{{expr}}" or inline bindings "Hello {{name}}"
14
+ * - Non-string values: returned as-is
15
+ */
16
+ export declare function resolveValue(value: unknown, context: Record<string, unknown>): unknown;
17
+ /**
18
+ * Resolve all values in a props map against the context.
19
+ * Recursively resolves nested objects and arrays.
20
+ */
21
+ export declare function resolveProps(props: Record<string, unknown> | undefined, context: Record<string, unknown>): Record<string, unknown>;
22
+ /**
23
+ * Recursively resolve binding expressions in a value (including nested objects/arrays).
24
+ */
25
+ export declare function resolveDeep(value: unknown, context: Record<string, unknown>): unknown;
26
+ //# sourceMappingURL=bindingResolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bindingResolver.d.ts","sourceRoot":"","sources":["../../../src/renderer/bindingResolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAwBH;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAsBtF;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,EAC1C,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAOzB;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAYrF"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Evaluate the `if` expression of a SchemaNode against the current context.
3
+ *
4
+ * Returns true → node should render
5
+ * Returns false → node (and its entire subtree) should be skipped
6
+ *
7
+ * Falsy values: false, null, undefined, 0, "" all hide the node.
8
+ * When `ifExpr` is undefined/null the node always renders.
9
+ *
10
+ * Accepts `unknown` to match the transformer's `if?: unknown` output:
11
+ * - String values are resolved as {{}} binding expressions
12
+ * - Non-string values (boolean, number, null) are coerced directly
13
+ */
14
+ export declare function evaluateIf(ifExpr: unknown, context: Record<string, unknown>): boolean;
15
+ //# sourceMappingURL=conditionalEvaluator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conditionalEvaluator.d.ts","sourceRoot":"","sources":["../../../src/renderer/conditionalEvaluator.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;GAYG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,OAAO,EACf,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC/B,OAAO,CAUT"}
@@ -0,0 +1,29 @@
1
+ import type { ReactElement } from 'react';
2
+ import type { PushframeNode } from './schema';
3
+ import type { ComponentRegistry } from './registry';
4
+ import type { PushframeContext } from './bindings';
5
+ import type { PushframeAction } from './schema';
6
+ /**
7
+ * Called by the host app whenever an action is triggered.
8
+ * `eventName` is the prop key (e.g. "onPress").
9
+ * `action` is the resolved action descriptor from the schema.
10
+ */
11
+ export type ActionHandler = (eventName: string, action: PushframeAction) => void;
12
+ interface RenderNodeOptions {
13
+ node: PushframeNode;
14
+ context: PushframeContext;
15
+ registry: ComponentRegistry;
16
+ onAction?: ActionHandler;
17
+ /** Key to help React reconcile siblings. */
18
+ key?: string;
19
+ }
20
+ /**
21
+ * Recursively render a single PushframeNode.
22
+ *
23
+ * Returns null when:
24
+ * - The node's condition evaluates to false
25
+ * - The node's type is not found in the registry
26
+ */
27
+ export declare function renderNode({ node, context, registry, onAction, key, }: RenderNodeOptions): ReactElement | null;
28
+ export {};
29
+ //# sourceMappingURL=renderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"renderer.d.ts","sourceRoot":"","sources":["../../src/renderer.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAC1C,OAAO,KAAK,EAAE,aAAa,EAAc,MAAM,UAAU,CAAC;AAC1D,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAGnD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAEhD;;;;GAIG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,KAAK,IAAI,CAAC;AAEjF,UAAU,iBAAiB;IACzB,IAAI,EAAE,aAAa,CAAC;IACpB,OAAO,EAAE,gBAAgB,CAAC;IAC1B,QAAQ,EAAE,iBAAiB,CAAC;IAC5B,QAAQ,CAAC,EAAE,aAAa,CAAC;IACzB,4CAA4C;IAC5C,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED;;;;;;GAMG;AACH,wBAAgB,UAAU,CAAC,EACzB,IAAI,EACJ,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,GAAG,GACJ,EAAE,iBAAiB,GAAG,YAAY,GAAG,IAAI,CA+DzC"}
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Pushframe Schema — typed definitions for screen definitions.
3
+ *
4
+ * A screen is described as a tree of PushframeNode objects.
5
+ * Props can be static values OR data-bindings via { "$bind": "path.to.value" }.
6
+ * Conditions gate whether a node renders at all.
7
+ * Actions declare event handlers; the host app is responsible for handling them.
8
+ */
9
+ export declare const SCHEMA_VERSION: "1.0";
10
+ export type SchemaVersion = typeof SCHEMA_VERSION;
11
+ /**
12
+ * A binding reference to a value in the runtime context.
13
+ * e.g. { "$bind": "user.firstName" }
14
+ */
15
+ export interface Binding {
16
+ readonly $bind: string;
17
+ }
18
+ export declare function isBinding(value: unknown): value is Binding;
19
+ /**
20
+ * A prop value is either a static primitive / object, or a binding.
21
+ */
22
+ export type PropValue = string | number | boolean | null | Binding | PropValue[] | {
23
+ [key: string]: PropValue;
24
+ };
25
+ export type PropsMap = Record<string, PropValue>;
26
+ /**
27
+ * A condition gates whether a node should render.
28
+ *
29
+ * Supported operators:
30
+ * equals — strict equality (===)
31
+ * notEquals — strict inequality (!==)
32
+ * truthy — if the resolved value is truthy (no `value` needed)
33
+ * falsy — if the resolved value is falsy (no `value` needed)
34
+ */
35
+ export type ConditionOperator = 'equals' | 'notEquals' | 'truthy' | 'falsy';
36
+ export interface Condition {
37
+ /** The binding (or static value) to test. */
38
+ readonly if: Binding | PropValue;
39
+ /** Comparison operator. Defaults to "truthy" when omitted. */
40
+ readonly operator?: ConditionOperator;
41
+ /**
42
+ * The right-hand side value to compare against.
43
+ * Required for `equals` / `notEquals`; ignored for `truthy` / `falsy`.
44
+ */
45
+ readonly equals?: PropValue;
46
+ }
47
+ /** Navigate to another screen by ID. */
48
+ export interface NavigateAction {
49
+ readonly type: 'navigate';
50
+ readonly screen: string;
51
+ readonly params?: Record<string, PropValue>;
52
+ }
53
+ /** Emit a named custom event. The host app decides what to do with it. */
54
+ export interface CustomAction {
55
+ readonly type: 'custom';
56
+ readonly event: string;
57
+ readonly payload?: Record<string, PropValue>;
58
+ }
59
+ export type PushframeAction = NavigateAction | CustomAction;
60
+ export type ActionsMap = Record<string, PushframeAction>;
61
+ /**
62
+ * A single node in the UI tree.
63
+ *
64
+ * `type` maps to a registered component (e.g. "stack", "text", "button", "image").
65
+ * `props` are passed (after binding resolution) directly to the component.
66
+ * `children` are rendered recursively.
67
+ * `conditions` — when present, node is skipped if the condition evaluates to false.
68
+ * `actions` — event handler definitions; passed to the component as resolved callbacks.
69
+ */
70
+ export interface PushframeNode {
71
+ readonly type: string;
72
+ readonly props?: PropsMap;
73
+ readonly children?: readonly PushframeNode[];
74
+ readonly conditions?: Condition;
75
+ readonly actions?: ActionsMap;
76
+ }
77
+ export interface ScreenDefinition {
78
+ readonly schemaVersion: SchemaVersion;
79
+ readonly id: string;
80
+ readonly root: PushframeNode;
81
+ /** Optional display name — used for debugging / logging. */
82
+ readonly name?: string;
83
+ }
84
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../src/schema.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,eAAO,MAAM,cAAc,EAAG,KAAc,CAAC;AAC7C,MAAM,MAAM,aAAa,GAAG,OAAO,cAAc,CAAC;AAMlD;;;GAGG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,OAAO,CAO1D;AAED;;GAEG;AACH,MAAM,MAAM,SAAS,GACjB,MAAM,GACN,MAAM,GACN,OAAO,GACP,IAAI,GACJ,OAAO,GACP,SAAS,EAAE,GACX;IAAE,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,CAAA;CAAE,CAAC;AAEjC,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAMjD;;;;;;;;GAQG;AACH,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,OAAO,CAAC;AAE5E,MAAM,WAAW,SAAS;IACxB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,EAAE,OAAO,GAAG,SAAS,CAAC;IACjC,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,CAAC,EAAE,iBAAiB,CAAC;IACtC;;;OAGG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,CAAC;CAC7B;AAMD,wCAAwC;AACxC,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAC1B,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CAC7C;AAED,0EAA0E;AAC1E,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IACxB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;CAC9C;AAED,MAAM,MAAM,eAAe,GAAG,cAAc,GAAG,YAAY,CAAC;AAE5D,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,eAAe,CAAC,CAAC;AAMzD;;;;;;;;GAQG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,KAAK,CAAC,EAAE,QAAQ,CAAC;IAC1B,QAAQ,CAAC,QAAQ,CAAC,EAAE,SAAS,aAAa,EAAE,CAAC;IAC7C,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC;IAChC,QAAQ,CAAC,OAAO,CAAC,EAAE,UAAU,CAAC;CAC/B;AAMD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IACtC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,aAAa,CAAC;IAC7B,4DAA4D;IAC5D,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAC;CACxB"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * Craft.js → PushFrame SDUI transformer
3
+ *
4
+ * Converts a Craft.js editor schema (produced by the dashboard) into the
5
+ * SDUINode tree consumed by the SDK's RecursiveRenderer at runtime.
6
+ *
7
+ * This module is intended for use by the backend/service layer or the
8
+ * dashboard — it is NOT executed inside the React Native app at runtime.
9
+ */
10
+ import type { Action, SchemaNode } from '../context/PushFrameContext';
11
+ export type CraftNode = {
12
+ type: {
13
+ resolvedName: string;
14
+ };
15
+ props?: Record<string, unknown>;
16
+ nodes?: string[];
17
+ parent?: string;
18
+ };
19
+ export type CraftSchema = Record<string, CraftNode>;
20
+ /**
21
+ * The SDUI node shape produced by this transformer and consumed by the SDK's
22
+ * RecursiveRenderer. Aligned with `SchemaNode` from the SDK context.
23
+ */
24
+ export interface SDUINode extends SchemaNode {
25
+ id: string;
26
+ type: string;
27
+ props: Record<string, unknown>;
28
+ children?: SDUINode[];
29
+ /** FlatList per-item render template. */
30
+ itemTemplate?: SDUINode;
31
+ /** Conditional render expression. Falsy → node is hidden. */
32
+ if?: unknown;
33
+ actions?: Action[];
34
+ }
35
+ export interface CustomComponentDef {
36
+ rootNodeId: string;
37
+ nodes: CraftSchema;
38
+ }
39
+ export type CustomComponentsMap = Record<string, CustomComponentDef>;
40
+ /**
41
+ * Convert a Craft.js editor schema into an SDUINode tree ready for the SDK's
42
+ * RecursiveRenderer.
43
+ *
44
+ * @param craftJson - The full Craft.js node map (keyed by node ID, must include "ROOT").
45
+ * @param customComponents - Optional map of reusable component definitions.
46
+ * @returns The root SDUINode.
47
+ */
48
+ export declare function transformCraftToSDUI(craftJson: CraftSchema, customComponents?: CustomComponentsMap): SDUINode;
49
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/transformer/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAItE,MAAM,MAAM,SAAS,GAAG;IACpB,IAAI,EAAE;QAAE,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC;IAC/B,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AA+BpD;;;GAGG;AACH,MAAM,WAAW,QAAS,SAAQ,UAAU;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAC/B,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;IACtB,yCAAyC;IACzC,YAAY,CAAC,EAAE,QAAQ,CAAC;IACxB,6DAA6D;IAC7D,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACtB;AA0SD,MAAM,WAAW,kBAAkB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,MAAM,mBAAmB,GAAG,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;AAonBrE;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAChC,SAAS,EAAE,WAAW,EACtB,gBAAgB,GAAE,mBAAwB,GAC3C,QAAQ,CAOV"}
@@ -0,0 +1,19 @@
1
+ import type { ScreenDefinition } from './schema';
2
+ /**
3
+ * Fetch a screen definition from the Pushframe server.
4
+ *
5
+ * URL: GET {serverUrl}/screens/{screenId}?projectKey={projectKey}
6
+ *
7
+ * Handles both the native SDK format ({ schemaVersion, id, root })
8
+ * and the server envelope format ({ key, version, schema, publishedAt }).
9
+ *
10
+ * Results are cached in memory for the lifetime of the JS bundle.
11
+ * Call `clearScreenCache()` to force a refetch (e.g. on logout or hard reload).
12
+ */
13
+ export declare function fetchScreenDefinition(serverUrl: string, projectKey: string, screenId: string): Promise<ScreenDefinition>;
14
+ /**
15
+ * Invalidate the entire in-memory screen cache.
16
+ * Useful after a forced refresh or when the project key changes.
17
+ */
18
+ export declare function clearScreenCache(): void;
19
+ //# sourceMappingURL=transport.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"transport.d.ts","sourceRoot":"","sources":["../../src/transport.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAiB,MAAM,UAAU,CAAC;AAwDhE;;;;;;;;;;GAUG;AACH,wBAAsB,qBAAqB,CACzC,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC,gBAAgB,CAAC,CAwB3B;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,IAAI,CAEvC"}
package/package.json CHANGED
@@ -1,27 +1,20 @@
1
1
  {
2
2
  "name": "@pushframe/sdk",
3
- "version": "0.1.4",
3
+ "version": "0.1.8",
4
4
  "description": "Pushframe React Native SDK — Server-Driven UI rendering engine",
5
- "main": "./dist/index.js",
6
- "module": "./dist/index.mjs",
7
- "react-native": "./dist/index.mjs",
8
- "types": "./dist/index.d.ts",
9
- "exports": {
10
- ".": {
11
- "types": "./dist/index.d.ts",
12
- "import": "./dist/index.mjs",
13
- "react-native": "./dist/index.mjs",
14
- "require": "./dist/index.js",
15
- "default": "./dist/index.mjs"
16
- }
17
- },
5
+ "main": "lib/commonjs/index.js",
6
+ "module": "lib/module/index.js",
7
+ "react-native": "src/index.ts",
8
+ "source": "src/index.ts",
9
+ "types": "lib/typescript/index.d.ts",
18
10
  "files": [
19
- "dist",
11
+ "src",
12
+ "lib",
20
13
  "README.md"
21
14
  ],
22
15
  "scripts": {
23
- "build": "tsup",
24
- "dev": "tsup --watch",
16
+ "build": "bob build",
17
+ "prepare": "bob build",
25
18
  "typecheck": "tsc --noEmit",
26
19
  "lint": "eslint src --ext .ts,.tsx"
27
20
  },
@@ -33,9 +26,18 @@
33
26
  "devDependencies": {
34
27
  "@types/react": "^18.2.0",
35
28
  "@types/react-native": "^0.73.0",
36
- "tsup": "^8.0.0",
29
+ "react-native-builder-bob": "latest",
37
30
  "typescript": "^5.4.0"
38
31
  },
32
+ "react-native-builder-bob": {
33
+ "source": "src",
34
+ "output": "lib",
35
+ "targets": [
36
+ "commonjs",
37
+ "module",
38
+ "typescript"
39
+ ]
40
+ },
39
41
  "keywords": [
40
42
  "react-native",
41
43
  "server-driven-ui",
@@ -0,0 +1,119 @@
1
+ import React, { createContext, useContext, useMemo } from 'react';
2
+ import type { ReactNode } from 'react';
3
+ import { ComponentRegistry, createDefaultRegistry } from './registry';
4
+ import type { PushframeComponent } from './registry';
5
+ import type { PushframeContext } from './bindings';
6
+ import type { ActionHandler } from './renderer';
7
+
8
+ export const DEFAULT_SERVER_URL = 'http://localhost:3001';
9
+
10
+ // ---------------------------------------------------------------------------
11
+ // Context shape
12
+ // ---------------------------------------------------------------------------
13
+
14
+ interface PushframeContextValue {
15
+ /** Runtime data that screen definitions bind to via { "$bind": "..." }. */
16
+ context: PushframeContext;
17
+
18
+ /** Component registry used by all screens under this provider. */
19
+ registry: ComponentRegistry;
20
+
21
+ /** Identifies which Pushframe project's screens to fetch. */
22
+ projectKey: string;
23
+
24
+ /** Base URL of the Pushframe server. Defaults to http://localhost:3001. */
25
+ serverUrl: string;
26
+
27
+ /** Optional global action handler. */
28
+ onAction?: ActionHandler;
29
+ }
30
+
31
+ const PushframeCtx = createContext<PushframeContextValue | null>(null);
32
+
33
+ // ---------------------------------------------------------------------------
34
+ // Hook
35
+ // ---------------------------------------------------------------------------
36
+
37
+ export function usePushframe(): PushframeContextValue {
38
+ const value = useContext(PushframeCtx);
39
+ if (!value) {
40
+ throw new Error('[Pushframe] usePushframe() must be called inside a <PushframeProvider>.');
41
+ }
42
+ return value;
43
+ }
44
+
45
+ // ---------------------------------------------------------------------------
46
+ // Provider
47
+ // ---------------------------------------------------------------------------
48
+
49
+ export interface PushframeProviderProps {
50
+ /**
51
+ * The Pushframe project key. All screens fetched under this provider will
52
+ * be scoped to this project.
53
+ */
54
+ projectKey: string;
55
+
56
+ /**
57
+ * Base URL of the Pushframe server.
58
+ * @default "http://localhost:3001"
59
+ */
60
+ serverUrl?: string;
61
+
62
+ /**
63
+ * Runtime data (user info, feature flags, etc.) that screen definitions
64
+ * can bind to via { "$bind": "path.to.value" }.
65
+ */
66
+ context: PushframeContext;
67
+
68
+ /**
69
+ * Optional pre-configured registry. When omitted, the default registry
70
+ * (stack, scrollview, flatlist, text, button, image) is used.
71
+ * Takes precedence over `customComponents` when both are provided.
72
+ */
73
+ registry?: ComponentRegistry;
74
+
75
+ /**
76
+ * Custom components to add on top of the default registry.
77
+ * Map of type string → component, e.g. `{ 'my-card': MyCardComponent }`.
78
+ * These override any built-in component with the same type string.
79
+ * Ignored when `registry` is also provided.
80
+ */
81
+ customComponents?: Record<string, PushframeComponent>;
82
+
83
+ /**
84
+ * Called whenever an action is triggered from a rendered screen.
85
+ * The host app is responsible for handling navigate / custom actions.
86
+ */
87
+ onAction?: ActionHandler;
88
+
89
+ children: ReactNode;
90
+ }
91
+
92
+ export function PushframeProvider({
93
+ projectKey,
94
+ serverUrl = DEFAULT_SERVER_URL,
95
+ context,
96
+ registry,
97
+ customComponents,
98
+ onAction,
99
+ children,
100
+ }: PushframeProviderProps) {
101
+ const resolvedRegistry = useMemo(() => {
102
+ if (registry) return registry;
103
+ const defaultReg = createDefaultRegistry();
104
+ if (customComponents) {
105
+ for (const [type, component] of Object.entries(customComponents)) {
106
+ defaultReg.register(type, component);
107
+ }
108
+ }
109
+ return defaultReg;
110
+ // eslint-disable-next-line react-hooks/exhaustive-deps
111
+ }, [registry, customComponents]);
112
+
113
+ const value = useMemo<PushframeContextValue>(
114
+ () => ({ projectKey, serverUrl, context, registry: resolvedRegistry, onAction }),
115
+ [projectKey, serverUrl, context, resolvedRegistry, onAction],
116
+ );
117
+
118
+ return <PushframeCtx.Provider value={value}>{children}</PushframeCtx.Provider>;
119
+ }
@@ -0,0 +1,107 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import type { ReactElement } from 'react';
3
+ import { usePushframe } from './PushframeProvider';
4
+ import { renderNode } from './renderer';
5
+ import { fetchScreenDefinition } from './transport';
6
+ import type { ScreenDefinition } from './schema';
7
+ import type { ActionHandler } from './renderer';
8
+ import { SCHEMA_VERSION } from './schema';
9
+
10
+ export interface PushframeScreenProps {
11
+ /**
12
+ * Fetch the screen definition from the server by ID.
13
+ * Requires `projectKey` and `serverUrl` to be set on the parent
14
+ * <PushframeProvider>.
15
+ *
16
+ * Mutually exclusive with `definition`.
17
+ */
18
+ screenId?: string;
19
+
20
+ /**
21
+ * Use a locally-provided screen definition (static / offline mode).
22
+ * Mutually exclusive with `screenId`.
23
+ */
24
+ definition?: ScreenDefinition;
25
+
26
+ /**
27
+ * Optional per-screen action handler. Takes precedence over the global
28
+ * handler set on <PushframeProvider>.
29
+ */
30
+ onAction?: ActionHandler;
31
+
32
+ /**
33
+ * Rendered while the screen definition is being fetched from the server.
34
+ * Defaults to null (renders nothing).
35
+ */
36
+ fallback?: ReactElement | null;
37
+
38
+ /**
39
+ * Rendered when the fetch fails.
40
+ * Defaults to null (renders nothing, error is logged to console).
41
+ */
42
+ errorFallback?: ReactElement | null;
43
+ }
44
+
45
+ export function PushframeScreen({
46
+ screenId,
47
+ definition: staticDefinition,
48
+ onAction,
49
+ fallback = null,
50
+ errorFallback = null,
51
+ }: PushframeScreenProps) {
52
+ if (!screenId && !staticDefinition) {
53
+ throw new Error('[Pushframe] <PushframeScreen> requires either screenId or definition.');
54
+ }
55
+
56
+ const { context, registry, projectKey, serverUrl, onAction: globalOnAction } = usePushframe();
57
+ const effectiveOnAction = onAction ?? globalOnAction;
58
+
59
+ // When a static definition is passed we skip the fetch entirely.
60
+ const [fetchedDefinition, setFetchedDefinition] = useState<ScreenDefinition | null>(null);
61
+ const [fetchError, setFetchError] = useState<Error | null>(null);
62
+
63
+ useEffect(() => {
64
+ if (!screenId) return;
65
+
66
+ let cancelled = false;
67
+ setFetchedDefinition(null);
68
+ setFetchError(null);
69
+
70
+ fetchScreenDefinition(serverUrl, projectKey, screenId)
71
+ .then((def) => {
72
+ if (!cancelled) setFetchedDefinition(def);
73
+ })
74
+ .catch((err: unknown) => {
75
+ if (!cancelled) {
76
+ const error = err instanceof Error ? err : new Error(String(err));
77
+ console.error(`[Pushframe] Error fetching screen "${screenId}":`, error.message);
78
+ setFetchError(error);
79
+ }
80
+ });
81
+
82
+ return () => {
83
+ cancelled = true;
84
+ };
85
+ }, [screenId, projectKey, serverUrl]);
86
+
87
+ const definition = staticDefinition ?? fetchedDefinition;
88
+
89
+ if (!definition) {
90
+ return fetchError ? errorFallback : fallback;
91
+ }
92
+
93
+ if (definition.schemaVersion !== SCHEMA_VERSION) {
94
+ console.warn(
95
+ `[Pushframe] Schema version mismatch: expected "${SCHEMA_VERSION}", ` +
96
+ `got "${definition.schemaVersion}" for screen "${definition.id}".`,
97
+ );
98
+ }
99
+
100
+ return renderNode({
101
+ node: definition.root,
102
+ context,
103
+ registry,
104
+ ...(effectiveOnAction !== undefined && { onAction: effectiveOnAction }),
105
+ key: definition.id,
106
+ });
107
+ }
@@ -0,0 +1,72 @@
1
+ import { isBinding } from './schema';
2
+ import type { PropValue, PropsMap } from './schema';
3
+
4
+ /**
5
+ * Runtime context passed into the renderer.
6
+ * Any JSON-serialisable data structure is valid.
7
+ */
8
+ export type PushframeContext = Record<string, unknown>;
9
+
10
+ /**
11
+ * Resolve a dot-separated path against an object.
12
+ *
13
+ * e.g. resolvePath({ user: { firstName: "Alex" } }, "user.firstName") → "Alex"
14
+ *
15
+ * Returns undefined when any segment along the path is absent.
16
+ */
17
+ export function resolvePath(context: PushframeContext, path: string): unknown {
18
+ const parts = path.split('.');
19
+ let current: unknown = context;
20
+ for (const part of parts) {
21
+ if (current === null || current === undefined || typeof current !== 'object') {
22
+ return undefined;
23
+ }
24
+ current = (current as Record<string, unknown>)[part];
25
+ }
26
+ return current;
27
+ }
28
+
29
+ /**
30
+ * Recursively resolve all $bind references in a PropValue against the context.
31
+ *
32
+ * - Binding objects → resolved to the context value (or undefined if missing)
33
+ * - Arrays → each element resolved
34
+ * - Plain objects → each value resolved
35
+ * - Primitives → returned as-is
36
+ */
37
+ export function resolveValue(value: PropValue, context: PushframeContext): unknown {
38
+ if (isBinding(value)) {
39
+ return resolvePath(context, value.$bind);
40
+ }
41
+
42
+ if (Array.isArray(value)) {
43
+ return value.map((item) => resolveValue(item, context));
44
+ }
45
+
46
+ if (typeof value === 'object' && value !== null) {
47
+ const resolved: Record<string, unknown> = {};
48
+ for (const [k, v] of Object.entries(value as Record<string, PropValue>)) {
49
+ resolved[k] = resolveValue(v, context);
50
+ }
51
+ return resolved;
52
+ }
53
+
54
+ // string | number | boolean | null
55
+ return value;
56
+ }
57
+
58
+ /**
59
+ * Resolve all entries in a props map against the context.
60
+ * Returns a plain Record<string, unknown> ready to spread onto a component.
61
+ */
62
+ export function resolveProps(
63
+ props: PropsMap | undefined,
64
+ context: PushframeContext,
65
+ ): Record<string, unknown> {
66
+ if (!props) return {};
67
+ const resolved: Record<string, unknown> = {};
68
+ for (const [key, value] of Object.entries(props)) {
69
+ resolved[key] = resolveValue(value, context);
70
+ }
71
+ return resolved;
72
+ }