@pushframe/sdk 0.1.5 → 0.1.9
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/lib/commonjs/PushframeProvider.js +69 -0
- package/lib/commonjs/PushframeProvider.js.map +1 -0
- package/lib/commonjs/PushframeScreen.js +69 -0
- package/lib/commonjs/PushframeScreen.js.map +1 -0
- package/lib/commonjs/bindings.js +73 -0
- package/lib/commonjs/bindings.js.map +1 -0
- package/lib/commonjs/components/ButtonComponent.js +72 -0
- package/lib/commonjs/components/ButtonComponent.js.map +1 -0
- package/lib/commonjs/components/FlatListComponent.js +73 -0
- package/lib/commonjs/components/FlatListComponent.js.map +1 -0
- package/lib/commonjs/components/ImageComponent.js +67 -0
- package/lib/commonjs/components/ImageComponent.js.map +1 -0
- package/lib/commonjs/components/PushFrameComponent.js +179 -0
- package/lib/commonjs/components/PushFrameComponent.js.map +1 -0
- package/lib/commonjs/components/PushFrameProvider.js +115 -0
- package/lib/commonjs/components/PushFrameProvider.js.map +1 -0
- package/lib/commonjs/components/PushFrameScreen.js +39 -0
- package/lib/commonjs/components/PushFrameScreen.js.map +1 -0
- package/lib/commonjs/components/ScrollViewComponent.js +64 -0
- package/lib/commonjs/components/ScrollViewComponent.js.map +1 -0
- package/lib/commonjs/components/StackComponent.js +61 -0
- package/lib/commonjs/components/StackComponent.js.map +1 -0
- package/lib/commonjs/components/TextComponent.js +62 -0
- package/lib/commonjs/components/TextComponent.js.map +1 -0
- package/lib/commonjs/conditions.js +44 -0
- package/lib/commonjs/conditions.js.map +1 -0
- package/lib/commonjs/context/PushFrameContext.js +33 -0
- package/lib/commonjs/context/PushFrameContext.js.map +1 -0
- package/lib/commonjs/index.js +200 -0
- package/lib/commonjs/index.js.map +1 -0
- package/lib/commonjs/overlays/BottomSheetHost.js +144 -0
- package/lib/commonjs/overlays/BottomSheetHost.js.map +1 -0
- package/lib/commonjs/overlays/ToastHost.js +135 -0
- package/lib/commonjs/overlays/ToastHost.js.map +1 -0
- package/lib/commonjs/package.json +1 -0
- package/lib/commonjs/primitives/ActivityIndicator.js +24 -0
- package/lib/commonjs/primitives/ActivityIndicator.js.map +1 -0
- package/lib/commonjs/primitives/FlatList.js +34 -0
- package/lib/commonjs/primitives/FlatList.js.map +1 -0
- package/lib/commonjs/primitives/Image.js +33 -0
- package/lib/commonjs/primitives/Image.js.map +1 -0
- package/lib/commonjs/primitives/KeyboardAvoidingView.js +24 -0
- package/lib/commonjs/primitives/KeyboardAvoidingView.js.map +1 -0
- package/lib/commonjs/primitives/Modal.js +24 -0
- package/lib/commonjs/primitives/Modal.js.map +1 -0
- package/lib/commonjs/primitives/Pressable.js +26 -0
- package/lib/commonjs/primitives/Pressable.js.map +1 -0
- package/lib/commonjs/primitives/SafeAreaView.js +38 -0
- package/lib/commonjs/primitives/SafeAreaView.js.map +1 -0
- package/lib/commonjs/primitives/ScrollView.js +26 -0
- package/lib/commonjs/primitives/ScrollView.js.map +1 -0
- package/lib/commonjs/primitives/StatusBar.js +24 -0
- package/lib/commonjs/primitives/StatusBar.js.map +1 -0
- package/lib/commonjs/primitives/Switch.js +28 -0
- package/lib/commonjs/primitives/Switch.js.map +1 -0
- package/lib/commonjs/primitives/Text.js +37 -0
- package/lib/commonjs/primitives/Text.js.map +1 -0
- package/lib/commonjs/primitives/TextInput.js +31 -0
- package/lib/commonjs/primitives/TextInput.js.map +1 -0
- package/lib/commonjs/primitives/View.js +24 -0
- package/lib/commonjs/primitives/View.js.map +1 -0
- package/lib/commonjs/primitives/index.js +97 -0
- package/lib/commonjs/primitives/index.js.map +1 -0
- package/lib/commonjs/registry/ComponentRegistry.js +70 -0
- package/lib/commonjs/registry/ComponentRegistry.js.map +1 -0
- package/lib/commonjs/registry.js +94 -0
- package/lib/commonjs/registry.js.map +1 -0
- package/lib/commonjs/renderer/RecursiveRenderer.js +202 -0
- package/lib/commonjs/renderer/RecursiveRenderer.js.map +1 -0
- package/lib/commonjs/renderer/bindingResolver.js +98 -0
- package/lib/commonjs/renderer/bindingResolver.js.map +1 -0
- package/lib/commonjs/renderer/conditionalEvaluator.js +31 -0
- package/lib/commonjs/renderer/conditionalEvaluator.js.map +1 -0
- package/lib/commonjs/renderer.js +107 -0
- package/lib/commonjs/renderer.js.map +1 -0
- package/lib/commonjs/schema.js +79 -0
- package/lib/commonjs/schema.js.map +1 -0
- package/lib/commonjs/transformer/index.js +1055 -0
- package/lib/commonjs/transformer/index.js.map +1 -0
- package/lib/commonjs/transport.js +86 -0
- package/lib/commonjs/transport.js.map +1 -0
- package/lib/module/PushframeProvider.js +62 -0
- package/lib/module/PushframeProvider.js.map +1 -0
- package/lib/module/PushframeScreen.js +65 -0
- package/lib/module/PushframeScreen.js.map +1 -0
- package/lib/module/bindings.js +68 -0
- package/lib/module/bindings.js.map +1 -0
- package/lib/module/components/ButtonComponent.js +67 -0
- package/lib/module/components/ButtonComponent.js.map +1 -0
- package/lib/module/components/FlatListComponent.js +68 -0
- package/lib/module/components/FlatListComponent.js.map +1 -0
- package/lib/module/components/ImageComponent.js +62 -0
- package/lib/module/components/ImageComponent.js.map +1 -0
- package/lib/module/components/PushFrameComponent.js +174 -0
- package/lib/module/components/PushFrameComponent.js.map +1 -0
- package/lib/module/components/PushFrameProvider.js +110 -0
- package/lib/module/components/PushFrameProvider.js.map +1 -0
- package/lib/module/components/PushFrameScreen.js +34 -0
- package/lib/module/components/PushFrameScreen.js.map +1 -0
- package/lib/module/components/ScrollViewComponent.js +59 -0
- package/lib/module/components/ScrollViewComponent.js.map +1 -0
- package/lib/module/components/StackComponent.js +56 -0
- package/lib/module/components/StackComponent.js.map +1 -0
- package/lib/module/components/TextComponent.js +57 -0
- package/lib/module/components/TextComponent.js.map +1 -0
- package/lib/module/conditions.js +40 -0
- package/lib/module/conditions.js.map +1 -0
- package/lib/module/context/PushFrameContext.js +29 -0
- package/lib/module/context/PushFrameContext.js.map +1 -0
- package/lib/module/index.js +99 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/overlays/BottomSheetHost.js +139 -0
- package/lib/module/overlays/BottomSheetHost.js.map +1 -0
- package/lib/module/overlays/ToastHost.js +130 -0
- package/lib/module/overlays/ToastHost.js.map +1 -0
- package/lib/module/primitives/ActivityIndicator.js +19 -0
- package/lib/module/primitives/ActivityIndicator.js.map +1 -0
- package/lib/module/primitives/FlatList.js +29 -0
- package/lib/module/primitives/FlatList.js.map +1 -0
- package/lib/module/primitives/Image.js +28 -0
- package/lib/module/primitives/Image.js.map +1 -0
- package/lib/module/primitives/KeyboardAvoidingView.js +19 -0
- package/lib/module/primitives/KeyboardAvoidingView.js.map +1 -0
- package/lib/module/primitives/Modal.js +19 -0
- package/lib/module/primitives/Modal.js.map +1 -0
- package/lib/module/primitives/Pressable.js +21 -0
- package/lib/module/primitives/Pressable.js.map +1 -0
- package/lib/module/primitives/SafeAreaView.js +33 -0
- package/lib/module/primitives/SafeAreaView.js.map +1 -0
- package/lib/module/primitives/ScrollView.js +21 -0
- package/lib/module/primitives/ScrollView.js.map +1 -0
- package/lib/module/primitives/StatusBar.js +19 -0
- package/lib/module/primitives/StatusBar.js.map +1 -0
- package/lib/module/primitives/Switch.js +23 -0
- package/lib/module/primitives/Switch.js.map +1 -0
- package/lib/module/primitives/Text.js +32 -0
- package/lib/module/primitives/Text.js.map +1 -0
- package/lib/module/primitives/TextInput.js +26 -0
- package/lib/module/primitives/TextInput.js.map +1 -0
- package/lib/module/primitives/View.js +19 -0
- package/lib/module/primitives/View.js.map +1 -0
- package/lib/module/primitives/index.js +16 -0
- package/lib/module/primitives/index.js.map +1 -0
- package/lib/module/registry/ComponentRegistry.js +66 -0
- package/lib/module/registry/ComponentRegistry.js.map +1 -0
- package/lib/module/registry.js +88 -0
- package/lib/module/registry.js.map +1 -0
- package/lib/module/renderer/RecursiveRenderer.js +197 -0
- package/lib/module/renderer/RecursiveRenderer.js.map +1 -0
- package/lib/module/renderer/bindingResolver.js +92 -0
- package/lib/module/renderer/bindingResolver.js.map +1 -0
- package/lib/module/renderer/conditionalEvaluator.js +28 -0
- package/lib/module/renderer/conditionalEvaluator.js.map +1 -0
- package/lib/module/renderer.js +103 -0
- package/lib/module/renderer.js.map +1 -0
- package/lib/module/schema.js +74 -0
- package/lib/module/schema.js.map +1 -0
- package/lib/module/transformer/index.js +1051 -0
- package/lib/module/transformer/index.js.map +1 -0
- package/lib/module/transport.js +82 -0
- package/lib/module/transport.js.map +1 -0
- package/lib/typescript/PushframeProvider.d.ts +58 -0
- package/lib/typescript/PushframeProvider.d.ts.map +1 -0
- package/lib/typescript/PushframeScreen.d.ts +36 -0
- package/lib/typescript/PushframeScreen.d.ts.map +1 -0
- package/lib/typescript/bindings.d.ts +29 -0
- package/lib/typescript/bindings.d.ts.map +1 -0
- package/lib/typescript/components/ButtonComponent.d.ts +11 -0
- package/lib/typescript/components/ButtonComponent.d.ts.map +1 -0
- package/lib/typescript/components/FlatListComponent.d.ts +28 -0
- package/lib/typescript/components/FlatListComponent.d.ts.map +1 -0
- package/lib/typescript/components/ImageComponent.d.ts +12 -0
- package/lib/typescript/components/ImageComponent.d.ts.map +1 -0
- package/lib/typescript/components/PushFrameComponent.d.ts +48 -0
- package/lib/typescript/components/PushFrameComponent.d.ts.map +1 -0
- package/lib/typescript/components/PushFrameProvider.d.ts +51 -0
- package/lib/typescript/components/PushFrameProvider.d.ts.map +1 -0
- package/lib/typescript/components/PushFrameScreen.d.ts +15 -0
- package/lib/typescript/components/PushFrameScreen.d.ts.map +1 -0
- package/lib/typescript/components/ScrollViewComponent.d.ts +19 -0
- package/lib/typescript/components/ScrollViewComponent.d.ts.map +1 -0
- package/lib/typescript/components/StackComponent.d.ts +16 -0
- package/lib/typescript/components/StackComponent.d.ts.map +1 -0
- package/lib/typescript/components/TextComponent.d.ts +13 -0
- package/lib/typescript/components/TextComponent.d.ts.map +1 -0
- package/lib/typescript/conditions.d.ts +12 -0
- package/lib/typescript/conditions.d.ts.map +1 -0
- package/lib/typescript/context/PushFrameContext.d.ts +57 -0
- package/lib/typescript/context/PushFrameContext.d.ts.map +1 -0
- package/lib/typescript/index.d.ts +74 -0
- package/lib/typescript/index.d.ts.map +1 -0
- package/lib/typescript/overlays/BottomSheetHost.d.ts +21 -0
- package/lib/typescript/overlays/BottomSheetHost.d.ts.map +1 -0
- package/lib/typescript/overlays/ToastHost.d.ts +12 -0
- package/lib/typescript/overlays/ToastHost.d.ts.map +1 -0
- package/lib/typescript/primitives/ActivityIndicator.d.ts +12 -0
- package/lib/typescript/primitives/ActivityIndicator.d.ts.map +1 -0
- package/lib/typescript/primitives/FlatList.d.ts +29 -0
- package/lib/typescript/primitives/FlatList.d.ts.map +1 -0
- package/lib/typescript/primitives/Image.d.ts +20 -0
- package/lib/typescript/primitives/Image.d.ts.map +1 -0
- package/lib/typescript/primitives/KeyboardAvoidingView.d.ts +12 -0
- package/lib/typescript/primitives/KeyboardAvoidingView.d.ts.map +1 -0
- package/lib/typescript/primitives/Modal.d.ts +12 -0
- package/lib/typescript/primitives/Modal.d.ts.map +1 -0
- package/lib/typescript/primitives/Pressable.d.ts +14 -0
- package/lib/typescript/primitives/Pressable.d.ts.map +1 -0
- package/lib/typescript/primitives/SafeAreaView.d.ts +20 -0
- package/lib/typescript/primitives/SafeAreaView.d.ts.map +1 -0
- package/lib/typescript/primitives/ScrollView.d.ts +15 -0
- package/lib/typescript/primitives/ScrollView.d.ts.map +1 -0
- package/lib/typescript/primitives/StatusBar.d.ts +12 -0
- package/lib/typescript/primitives/StatusBar.d.ts.map +1 -0
- package/lib/typescript/primitives/Switch.d.ts +19 -0
- package/lib/typescript/primitives/Switch.d.ts.map +1 -0
- package/lib/typescript/primitives/Text.d.ts +25 -0
- package/lib/typescript/primitives/Text.d.ts.map +1 -0
- package/lib/typescript/primitives/TextInput.d.ts +25 -0
- package/lib/typescript/primitives/TextInput.d.ts.map +1 -0
- package/lib/typescript/primitives/View.d.ts +12 -0
- package/lib/typescript/primitives/View.d.ts.map +1 -0
- package/lib/typescript/primitives/index.d.ts +27 -0
- package/lib/typescript/primitives/index.d.ts.map +1 -0
- package/lib/typescript/registry/ComponentRegistry.d.ts +21 -0
- package/lib/typescript/registry/ComponentRegistry.d.ts.map +1 -0
- package/lib/typescript/registry.d.ts +57 -0
- package/lib/typescript/registry.d.ts.map +1 -0
- package/lib/typescript/renderer/RecursiveRenderer.d.ts +32 -0
- package/lib/typescript/renderer/RecursiveRenderer.d.ts.map +1 -0
- package/lib/typescript/renderer/bindingResolver.d.ts +26 -0
- package/lib/typescript/renderer/bindingResolver.d.ts.map +1 -0
- package/lib/typescript/renderer/conditionalEvaluator.d.ts +15 -0
- package/lib/typescript/renderer/conditionalEvaluator.d.ts.map +1 -0
- package/lib/typescript/renderer.d.ts +29 -0
- package/lib/typescript/renderer.d.ts.map +1 -0
- package/lib/typescript/schema.d.ts +84 -0
- package/lib/typescript/schema.d.ts.map +1 -0
- package/lib/typescript/transformer/index.d.ts +49 -0
- package/lib/typescript/transformer/index.d.ts.map +1 -0
- package/lib/typescript/transport.d.ts +19 -0
- package/lib/typescript/transport.d.ts.map +1 -0
- package/package.json +20 -18
- package/src/PushframeProvider.tsx +119 -0
- package/src/PushframeScreen.tsx +107 -0
- package/src/bindings.ts +72 -0
- package/src/components/ButtonComponent.tsx +87 -0
- package/src/components/FlatListComponent.tsx +86 -0
- package/src/components/ImageComponent.tsx +70 -0
- package/src/components/PushFrameComponent.tsx +221 -0
- package/src/components/PushFrameProvider.tsx +177 -0
- package/src/components/PushFrameScreen.tsx +30 -0
- package/src/components/ScrollViewComponent.tsx +65 -0
- package/src/components/StackComponent.tsx +69 -0
- package/src/components/TextComponent.tsx +60 -0
- package/src/conditions.ts +46 -0
- package/src/context/PushFrameContext.ts +89 -0
- package/src/index.ts +119 -0
- package/src/overlays/BottomSheetHost.tsx +175 -0
- package/src/overlays/ToastHost.tsx +147 -0
- package/src/primitives/ActivityIndicator.tsx +21 -0
- package/src/primitives/FlatList.tsx +49 -0
- package/src/primitives/Image.tsx +26 -0
- package/src/primitives/KeyboardAvoidingView.tsx +21 -0
- package/src/primitives/Modal.tsx +17 -0
- package/src/primitives/Pressable.tsx +19 -0
- package/src/primitives/SafeAreaView.tsx +42 -0
- package/src/primitives/ScrollView.tsx +21 -0
- package/src/primitives/StatusBar.tsx +17 -0
- package/src/primitives/Switch.tsx +24 -0
- package/src/primitives/Text.tsx +43 -0
- package/src/primitives/TextInput.tsx +42 -0
- package/src/primitives/View.tsx +17 -0
- package/src/primitives/index.ts +38 -0
- package/src/registry/ComponentRegistry.ts +99 -0
- package/src/registry.ts +99 -0
- package/src/renderer/RecursiveRenderer.tsx +242 -0
- package/src/renderer/bindingResolver.ts +94 -0
- package/src/renderer/conditionalEvaluator.ts +29 -0
- package/src/renderer.tsx +124 -0
- package/src/schema.ts +132 -0
- package/src/transformer/index.ts +1016 -0
- package/src/transport.ts +104 -0
- package/dist/index.d.mts +0 -534
- package/dist/index.d.ts +0 -534
- package/dist/index.js +0 -1572
- package/dist/index.js.map +0 -1
- package/dist/index.mjs +0 -1541
- 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.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"description": "Pushframe React Native SDK — Server-Driven UI rendering engine",
|
|
5
|
-
"main": "
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
-
"
|
|
9
|
-
"
|
|
10
|
-
".": {
|
|
11
|
-
"types": "./dist/index.d.ts",
|
|
12
|
-
"react-native": "./dist/index.mjs",
|
|
13
|
-
"import": "./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
|
-
"
|
|
11
|
+
"src",
|
|
12
|
+
"lib",
|
|
20
13
|
"README.md"
|
|
21
14
|
],
|
|
22
15
|
"scripts": {
|
|
23
|
-
"build": "
|
|
24
|
-
"
|
|
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
|
-
"
|
|
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
|
+
}
|
package/src/bindings.ts
ADDED
|
@@ -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
|
+
}
|