noboarding 0.1.0-alpha

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/types.d.ts ADDED
@@ -0,0 +1,185 @@
1
+ import type React from 'react';
2
+ export type ScreenType = 'noboard_screen' | 'custom_screen';
3
+ export interface ScreenConfig {
4
+ id: string;
5
+ type: ScreenType;
6
+ props: Record<string, any>;
7
+ elements?: ElementNode[];
8
+ custom_component_name?: string;
9
+ }
10
+ export type ElementType = 'vstack' | 'hstack' | 'zstack' | 'scrollview' | 'text' | 'image' | 'video' | 'lottie' | 'icon' | 'input' | 'spacer' | 'divider';
11
+ export interface ElementNode {
12
+ id: string;
13
+ type: ElementType;
14
+ style: ElementStyle;
15
+ props: Record<string, any>;
16
+ children?: ElementNode[];
17
+ position?: ElementPosition;
18
+ action?: ElementAction;
19
+ actions?: ElementAction[];
20
+ visibleWhen?: {
21
+ group: string;
22
+ hasSelection: boolean;
23
+ };
24
+ conditions?: ElementConditions;
25
+ }
26
+ export type ComparisonOperator = 'equals' | 'not_equals' | 'greater_than' | 'less_than' | 'contains' | 'in' | 'is_empty' | 'is_not_empty';
27
+ export interface Condition {
28
+ variable?: string;
29
+ operator?: ComparisonOperator;
30
+ value?: any;
31
+ all?: Condition[];
32
+ any?: Condition[];
33
+ not?: Condition;
34
+ }
35
+ export interface ElementConditions {
36
+ show_if?: Condition;
37
+ }
38
+ export interface ConditionalDestination {
39
+ if: Condition;
40
+ then: string;
41
+ else?: string | ConditionalDestination;
42
+ }
43
+ export interface ConditionalRoutes {
44
+ routes: Array<{
45
+ condition: Condition;
46
+ destination: string;
47
+ }>;
48
+ default: string;
49
+ }
50
+ export interface ElementAction {
51
+ type: 'tap' | 'navigate' | 'link' | 'toggle' | 'dismiss' | 'set_variable';
52
+ destination?: string | ConditionalDestination | ConditionalRoutes;
53
+ group?: string;
54
+ variable?: string;
55
+ value?: any;
56
+ }
57
+ export interface ElementPosition {
58
+ type?: 'relative' | 'absolute';
59
+ top?: number;
60
+ left?: number;
61
+ right?: number;
62
+ bottom?: number;
63
+ centerX?: boolean;
64
+ centerY?: boolean;
65
+ zIndex?: number;
66
+ }
67
+ export interface ElementStyle {
68
+ flex?: number;
69
+ flexDirection?: 'row' | 'column';
70
+ justifyContent?: 'flex-start' | 'center' | 'flex-end' | 'space-between' | 'space-around' | 'space-evenly';
71
+ alignItems?: 'flex-start' | 'center' | 'flex-end' | 'stretch';
72
+ alignSelf?: 'flex-start' | 'center' | 'flex-end' | 'stretch';
73
+ gap?: number;
74
+ flexWrap?: 'wrap' | 'nowrap';
75
+ overflow?: 'visible' | 'hidden' | 'scroll';
76
+ padding?: number;
77
+ paddingTop?: number;
78
+ paddingBottom?: number;
79
+ paddingLeft?: number;
80
+ paddingRight?: number;
81
+ marginTop?: number;
82
+ marginBottom?: number;
83
+ marginLeft?: number;
84
+ marginRight?: number;
85
+ width?: number | string;
86
+ height?: number | string;
87
+ maxWidth?: number;
88
+ minHeight?: number | string;
89
+ backgroundColor?: string;
90
+ backgroundGradient?: {
91
+ type: 'linear' | 'radial';
92
+ angle?: number;
93
+ colors: Array<{
94
+ color: string;
95
+ position: number;
96
+ }>;
97
+ };
98
+ opacity?: number;
99
+ borderRadius?: number;
100
+ borderWidth?: number;
101
+ borderColor?: string;
102
+ borderBottomWidth?: number;
103
+ borderBottomColor?: string;
104
+ shadowColor?: string;
105
+ shadowOpacity?: number;
106
+ shadowRadius?: number;
107
+ shadowOffsetX?: number;
108
+ shadowOffsetY?: number;
109
+ color?: string;
110
+ fontSize?: number;
111
+ fontWeight?: string;
112
+ textAlign?: 'left' | 'center' | 'right' | 'justify';
113
+ lineHeight?: number;
114
+ letterSpacing?: number;
115
+ textTransform?: 'none' | 'uppercase' | 'lowercase' | 'capitalize';
116
+ textDecorationLine?: 'none' | 'underline' | 'line-through';
117
+ }
118
+ export interface OnboardingConfig {
119
+ version: string;
120
+ screens: ScreenConfig[];
121
+ }
122
+ export interface Experiment {
123
+ id: string;
124
+ name: string;
125
+ variants: Variant[];
126
+ }
127
+ export interface Variant {
128
+ variant_id: string;
129
+ weight: number;
130
+ name: string;
131
+ screens: ScreenConfig[];
132
+ }
133
+ export interface AnalyticsEvent {
134
+ event: string;
135
+ user_id: string;
136
+ session_id: string;
137
+ timestamp: number;
138
+ properties?: Record<string, any>;
139
+ }
140
+ export interface GetConfigResponse {
141
+ config: OnboardingConfig;
142
+ version: string;
143
+ experiments: Experiment[];
144
+ organization_id: string;
145
+ }
146
+ export interface TrackEventsResponse {
147
+ success: boolean;
148
+ inserted: number;
149
+ }
150
+ export interface AssignVariantResponse {
151
+ variant_id: string;
152
+ variant_config: {
153
+ screens: ScreenConfig[];
154
+ };
155
+ cached: boolean;
156
+ }
157
+ export interface BaseComponentProps {
158
+ id: string;
159
+ analytics: Analytics;
160
+ onNext: (data?: Record<string, any>) => void;
161
+ onSkip?: () => void;
162
+ }
163
+ export interface Analytics {
164
+ track(eventName: string, properties?: Record<string, any>): void;
165
+ flush(): Promise<void>;
166
+ }
167
+ export interface CustomScreenProps {
168
+ analytics: {
169
+ track: (event: string, properties?: Record<string, any>) => void;
170
+ };
171
+ onNext: () => void;
172
+ onSkip?: () => void;
173
+ preview?: boolean;
174
+ data?: Record<string, any>;
175
+ onDataUpdate?: (data: Record<string, any>) => void;
176
+ }
177
+ export interface OnboardingFlowProps {
178
+ apiKey: string;
179
+ onComplete: (data?: Record<string, any>) => void;
180
+ onSkip?: () => void;
181
+ baseUrl?: string;
182
+ initialVariables?: Record<string, any>;
183
+ customComponents?: Record<string, React.ComponentType<CustomScreenProps>>;
184
+ onUserIdGenerated?: (userId: string) => void;
185
+ }
package/lib/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,17 @@
1
+ import { Condition, ConditionalDestination, ConditionalRoutes } from './types';
2
+ /**
3
+ * Resolve {variable_name} placeholders in a template string.
4
+ * Unknown variables resolve to empty string.
5
+ */
6
+ export declare function resolveTemplate(template: string, variables: Record<string, any>): string;
7
+ /**
8
+ * Recursively evaluate a Condition tree against the variable store.
9
+ * Supports: single comparison, all (AND), any (OR), not (negation).
10
+ * Returns true if no valid condition structure (default: show element).
11
+ */
12
+ export declare function evaluateCondition(condition: Condition, variables: Record<string, any>): boolean;
13
+ /**
14
+ * Resolve a destination (plain string or conditional) to a concrete screen ID.
15
+ * Returns the string destination or undefined.
16
+ */
17
+ export declare function resolveDestination(destination: string | ConditionalDestination | ConditionalRoutes | undefined, variables: Record<string, any>): string | undefined;
@@ -0,0 +1,118 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveTemplate = resolveTemplate;
4
+ exports.evaluateCondition = evaluateCondition;
5
+ exports.resolveDestination = resolveDestination;
6
+ /**
7
+ * Resolve {variable_name} placeholders in a template string.
8
+ * Unknown variables resolve to empty string.
9
+ */
10
+ function resolveTemplate(template, variables) {
11
+ if (!template || !template.includes('{'))
12
+ return template;
13
+ return template.replace(/\{(\w+(?:\.\w+)*)\}/g, (_match, varName) => {
14
+ // Support dot notation: "user.name" → variables["user.name"] or variables.user?.name
15
+ let value = variables[varName];
16
+ if (value === undefined && varName.includes('.')) {
17
+ const parts = varName.split('.');
18
+ value = variables[parts[0]];
19
+ for (let i = 1; i < parts.length && value != null; i++) {
20
+ value = value[parts[i]];
21
+ }
22
+ }
23
+ if (value === undefined || value === null)
24
+ return '';
25
+ return String(value);
26
+ });
27
+ }
28
+ /**
29
+ * Evaluate a single comparison against the variable store.
30
+ */
31
+ function evaluateComparison(variableName, operator, conditionValue, variables) {
32
+ const actual = variables[variableName];
33
+ switch (operator) {
34
+ case 'equals':
35
+ return actual === conditionValue;
36
+ case 'not_equals':
37
+ return actual !== conditionValue;
38
+ case 'greater_than':
39
+ return typeof actual === 'number' && actual > conditionValue;
40
+ case 'less_than':
41
+ return typeof actual === 'number' && actual < conditionValue;
42
+ case 'contains':
43
+ if (typeof actual === 'string')
44
+ return actual.includes(conditionValue);
45
+ if (Array.isArray(actual))
46
+ return actual.includes(conditionValue);
47
+ return false;
48
+ case 'in':
49
+ return Array.isArray(conditionValue) && conditionValue.includes(actual);
50
+ case 'is_empty':
51
+ return actual === undefined || actual === null || actual === '' ||
52
+ (Array.isArray(actual) && actual.length === 0);
53
+ case 'is_not_empty':
54
+ return actual !== undefined && actual !== null && actual !== '' &&
55
+ !(Array.isArray(actual) && actual.length === 0);
56
+ default:
57
+ return false;
58
+ }
59
+ }
60
+ /**
61
+ * Recursively evaluate a Condition tree against the variable store.
62
+ * Supports: single comparison, all (AND), any (OR), not (negation).
63
+ * Returns true if no valid condition structure (default: show element).
64
+ */
65
+ function evaluateCondition(condition, variables) {
66
+ if (!condition)
67
+ return true;
68
+ // AND logic
69
+ if (condition.all) {
70
+ return condition.all.every(c => evaluateCondition(c, variables));
71
+ }
72
+ // OR logic
73
+ if (condition.any) {
74
+ return condition.any.some(c => evaluateCondition(c, variables));
75
+ }
76
+ // Negation
77
+ if (condition.not) {
78
+ return !evaluateCondition(condition.not, variables);
79
+ }
80
+ // Single comparison
81
+ if (condition.variable && condition.operator) {
82
+ return evaluateComparison(condition.variable, condition.operator, condition.value, variables);
83
+ }
84
+ // No valid condition structure — default to true
85
+ return true;
86
+ }
87
+ /**
88
+ * Resolve a destination (plain string or conditional) to a concrete screen ID.
89
+ * Returns the string destination or undefined.
90
+ */
91
+ function resolveDestination(destination, variables) {
92
+ if (!destination)
93
+ return undefined;
94
+ // Plain string — backward compatible
95
+ if (typeof destination === 'string')
96
+ return destination;
97
+ // Routes array (multi-path)
98
+ if ('routes' in destination) {
99
+ const routes = destination;
100
+ for (const route of routes.routes) {
101
+ if (evaluateCondition(route.condition, variables)) {
102
+ return route.destination;
103
+ }
104
+ }
105
+ return routes.default;
106
+ }
107
+ // If/then/else
108
+ const cond = destination;
109
+ if (evaluateCondition(cond.if, variables)) {
110
+ return cond.then;
111
+ }
112
+ if (cond.else) {
113
+ if (typeof cond.else === 'string')
114
+ return cond.else;
115
+ return resolveDestination(cond.else, variables);
116
+ }
117
+ return 'next'; // fallback
118
+ }