@granite-js/react-native 0.1.29 → 0.1.31

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/CHANGELOG.md CHANGED
@@ -1,5 +1,38 @@
1
1
  # @granite-js/react-native
2
2
 
3
+ ## 0.1.31
4
+
5
+ ### Patch Changes
6
+
7
+ - 0ff9b13: feat(router): screenOptions in Route
8
+ - 37ae3f3: feat(router): supports standard schema
9
+ - Updated dependencies [0ff9b13]
10
+ - Updated dependencies [9bf8b50]
11
+ - Updated dependencies [e957833]
12
+ - Updated dependencies [37ae3f3]
13
+ - @granite-js/native@0.1.31
14
+ - @granite-js/plugin-core@0.1.31
15
+ - @granite-js/mpack@0.1.31
16
+ - @granite-js/image@0.1.31
17
+ - @granite-js/jest@0.1.31
18
+ - @granite-js/lottie@0.1.31
19
+ - @granite-js/style-utils@0.1.31
20
+ - @granite-js/cli@0.1.31
21
+
22
+ ## 0.1.30
23
+
24
+ ### Patch Changes
25
+
26
+ - Updated dependencies [9e9ea71]
27
+ - @granite-js/mpack@0.1.30
28
+ - @granite-js/cli@0.1.30
29
+ - @granite-js/image@0.1.30
30
+ - @granite-js/jest@0.1.30
31
+ - @granite-js/lottie@0.1.30
32
+ - @granite-js/native@0.1.30
33
+ - @granite-js/plugin-core@0.1.30
34
+ - @granite-js/style-utils@0.1.30
35
+
3
36
  ## 0.1.29
4
37
 
5
38
  ### Patch Changes
@@ -1,14 +1,21 @@
1
1
  import { type ParamListBase } from '@granite-js/native/@react-navigation/native';
2
- import { NativeStackNavigationProp } from '@granite-js/native/@react-navigation/native-stack';
2
+ import { NativeStackNavigationOptions, NativeStackNavigationProp } from '@granite-js/native/@react-navigation/native-stack';
3
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
3
4
  import { defaultParserParams } from './utils/defaultParserParams';
5
+ import { type InferOutput, type InferInput } from './utils/standardSchema';
4
6
  export interface RouteOptions<T extends Readonly<object | undefined>> {
5
7
  parserParams?: (params: Record<string, unknown>) => Record<string, unknown>;
6
- validateParams?: (params: Readonly<object | undefined>) => T;
8
+ validateParams?: ((params: Readonly<object | undefined>) => T) | StandardSchemaV1<any, T>;
7
9
  component: React.FC<any>;
10
+ screenOptions?: NativeStackNavigationOptions | ((context: {
11
+ params: T;
12
+ }) => NativeStackNavigationOptions);
13
+ }
14
+ export interface RegisterScreenInput {
8
15
  }
9
- export type NavigationProps = NativeStackNavigationProp<keyof RegisterScreen extends never ? ParamListBase : RegisterScreen>;
10
16
  export interface RegisterScreen {
11
17
  }
18
+ export type NavigationProps = NativeStackNavigationProp<keyof RegisterScreenInput extends never ? ParamListBase : RegisterScreenInput>;
12
19
  export declare function useNavigation(): NavigationProps;
13
20
  export type RouteHooksOptions<TScreen extends keyof RegisterScreen> = {
14
21
  from: TScreen;
@@ -17,13 +24,16 @@ export type RouteHooksOptions<TScreen extends keyof RegisterScreen> = {
17
24
  strict: false;
18
25
  from?: never;
19
26
  };
20
- export declare const routeMap: Map<"/test", {
21
- options: Omit<RouteOptions<any>, "component">;
27
+ export declare const routeMap: Map<keyof RegisterScreenInput, {
28
+ options: Omit<RouteOptions<any>, "component" | "screenOptions">;
22
29
  component: React.FC<any>;
30
+ screenOptions?: NativeStackNavigationOptions | ((context: {
31
+ params: any;
32
+ }) => NativeStackNavigationOptions);
23
33
  }>;
24
34
  export declare function useMatchOptions<TScreen extends keyof RegisterScreen>(options: RouteHooksOptions<TScreen>): {
25
35
  parserParams: ((params: Record<string, unknown>) => Record<string, unknown>) | typeof defaultParserParams;
26
- validateParams?: ((params: Readonly<object | undefined>) => any) | undefined;
36
+ validateParams?: StandardSchemaV1<any, any> | ((params: Readonly<object | undefined>) => any) | undefined;
27
37
  } | null;
28
38
  export declare function useParams<TScreen extends keyof RegisterScreen>(options: {
29
39
  from: TScreen;
@@ -32,8 +42,19 @@ export declare function useParams<TScreen extends keyof RegisterScreen>(options:
32
42
  export declare function useParams(options: {
33
43
  strict: false;
34
44
  }): Readonly<object | undefined>;
35
- export declare const createRoute: <T extends Readonly<object | undefined>>(path: keyof RegisterScreen, options: RouteOptions<T>) => {
36
- _path: "/test";
45
+ export declare function createRoute<TSchema extends StandardSchemaV1<any, any>>(path: keyof RegisterScreenInput, options: Omit<RouteOptions<any>, 'validateParams'> & {
46
+ validateParams: TSchema;
47
+ }): {
48
+ _path: keyof RegisterScreenInput;
49
+ useNavigation: typeof useNavigation;
50
+ useParams: () => InferOutput<TSchema>;
51
+ _inputType?: InferInput<TSchema>;
52
+ _outputType?: InferOutput<TSchema>;
53
+ };
54
+ export declare function createRoute<T extends Readonly<object | undefined>>(path: keyof RegisterScreenInput, options: RouteOptions<T>): {
55
+ _path: keyof RegisterScreenInput;
37
56
  useNavigation: typeof useNavigation;
38
57
  useParams: () => T;
58
+ _inputType?: T;
59
+ _outputType?: T;
39
60
  };
@@ -1,9 +1,35 @@
1
1
  declare module './createRoute' {
2
+ interface RegisterScreenInput {
3
+ '/test': {
4
+ id: string;
5
+ name: string;
6
+ };
7
+ '/test-schema': {
8
+ id: string;
9
+ count: number;
10
+ };
11
+ '/test-transform': {
12
+ id: string;
13
+ };
14
+ '/test-with-defaults': {
15
+ animation?: boolean;
16
+ };
17
+ }
2
18
  interface RegisterScreen {
3
19
  '/test': {
4
20
  id: string;
5
21
  name: string;
6
22
  };
23
+ '/test-schema': {
24
+ id: string;
25
+ count: number;
26
+ };
27
+ '/test-transform': {
28
+ id: number;
29
+ };
30
+ '/test-with-defaults': {
31
+ animation: boolean;
32
+ };
7
33
  }
8
34
  }
9
35
  export {};
@@ -1,3 +1,4 @@
1
+ import { NativeStackNavigationOptions } from '@granite-js/native/@react-navigation/native-stack';
1
2
  import { Screen } from './Screen';
2
3
  /**
3
4
  * @name RouteScreen
@@ -13,4 +14,9 @@ export interface RouteScreen {
13
14
  * @description Screen component
14
15
  */
15
16
  component: Screen;
17
+ /**
18
+ * @name screenOptions
19
+ * @description Screen options for React Navigation (can be static or a function that receives route params)
20
+ */
21
+ screenOptions?: NativeStackNavigationOptions;
16
22
  }
@@ -0,0 +1,13 @@
1
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
2
+ /**
3
+ * Type guard to check if a value is a StandardSchema v1 compliant schema
4
+ */
5
+ export declare function isStandardSchema(value: unknown): value is StandardSchemaV1;
6
+ /**
7
+ * Infer the output type from a StandardSchema
8
+ */
9
+ export type InferOutput<TSchema> = TSchema extends StandardSchemaV1<any, infer Output> ? Output : never;
10
+ /**
11
+ * Infer the input type from a StandardSchema
12
+ */
13
+ export type InferInput<TSchema> = TSchema extends StandardSchemaV1<infer Input, any> ? Input : never;
@@ -0,0 +1,10 @@
1
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
2
+ /**
3
+ * Validates route parameters using either a StandardSchema or a validation function.
4
+ *
5
+ * @param validateParams - The validation schema or function to apply
6
+ * @param parsedParams - The parsed parameters to validate
7
+ * @returns The validated parameters
8
+ * @throws Error if validation fails or async validation is attempted
9
+ */
10
+ export declare function validateRouteParams(validateParams: ((params: Readonly<object | undefined>) => any) | StandardSchemaV1<any, any>, parsedParams: Readonly<object | undefined>): any;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@granite-js/react-native",
3
- "version": "0.1.29",
3
+ "version": "0.1.31",
4
4
  "description": "The Granite Framework",
5
5
  "bin": {
6
6
  "granite": "./bin/cli.js"
@@ -91,7 +91,7 @@
91
91
  "@babel/core": "^7.24.9",
92
92
  "@babel/preset-env": "^7.24.8",
93
93
  "@babel/preset-typescript": "^7.24.7",
94
- "@granite-js/native": "0.1.29",
94
+ "@granite-js/native": "0.1.31",
95
95
  "@testing-library/dom": "^10.4.0",
96
96
  "@testing-library/react": "^16.1.0",
97
97
  "@types/babel__core": "^7",
@@ -108,7 +108,8 @@
108
108
  "react-native": "0.72.6",
109
109
  "tsup": "^8.5.0",
110
110
  "typescript": "5.8.3",
111
- "vitest": "^2.1.8"
111
+ "vitest": "^2.1.8",
112
+ "zod": "^4.1.12"
112
113
  },
113
114
  "peerDependencies": {
114
115
  "@granite-js/native": "*",
@@ -117,13 +118,14 @@
117
118
  "react-native": "*"
118
119
  },
119
120
  "dependencies": {
120
- "@granite-js/cli": "0.1.29",
121
- "@granite-js/image": "0.1.29",
122
- "@granite-js/jest": "0.1.29",
123
- "@granite-js/lottie": "0.1.29",
124
- "@granite-js/mpack": "0.1.29",
125
- "@granite-js/plugin-core": "0.1.29",
126
- "@granite-js/style-utils": "0.1.29",
121
+ "@granite-js/cli": "0.1.31",
122
+ "@granite-js/image": "0.1.31",
123
+ "@granite-js/jest": "0.1.31",
124
+ "@granite-js/lottie": "0.1.31",
125
+ "@granite-js/mpack": "0.1.31",
126
+ "@granite-js/plugin-core": "0.1.31",
127
+ "@granite-js/style-utils": "0.1.31",
128
+ "@standard-schema/spec": "^1.0.0",
127
129
  "es-toolkit": "^1.39.8",
128
130
  "react-native-url-polyfill": "3.0.0"
129
131
  },
@@ -1,12 +1,40 @@
1
1
  import { assertType, describe, it } from 'vitest';
2
- import { createRoute, useParams } from './createRoute';
2
+ import { z } from 'zod';
3
+ import { createRoute, useNavigation, useParams } from './createRoute';
3
4
 
4
5
  declare module './createRoute' {
6
+ interface RegisterScreenInput {
7
+ '/test': {
8
+ id: string;
9
+ name: string;
10
+ };
11
+ '/test-schema': {
12
+ id: string;
13
+ count: number;
14
+ };
15
+ '/test-transform': {
16
+ id: string;
17
+ };
18
+ '/test-with-defaults': {
19
+ animation?: boolean;
20
+ };
21
+ }
22
+
5
23
  interface RegisterScreen {
6
24
  '/test': {
7
25
  id: string;
8
26
  name: string;
9
27
  };
28
+ '/test-schema': {
29
+ id: string;
30
+ count: number;
31
+ };
32
+ '/test-transform': {
33
+ id: number;
34
+ };
35
+ '/test-with-defaults': {
36
+ animation: boolean;
37
+ };
10
38
  }
11
39
  }
12
40
 
@@ -50,3 +78,70 @@ describe('createRoute', () => {
50
78
  assertType(useParams({ from: '/test', strict: false }));
51
79
  });
52
80
  });
81
+
82
+ describe('createRoute with StandardSchema', () => {
83
+ it('should infer correct type from StandardSchema', () => {
84
+ const RouteWithSchema = createRoute('/test-schema', {
85
+ component: () => null,
86
+ validateParams: z.object({
87
+ id: z.string(),
88
+ count: z.number(),
89
+ }),
90
+ });
91
+
92
+ assertType<{
93
+ id: string;
94
+ count: number;
95
+ }>(RouteWithSchema.useParams());
96
+ });
97
+
98
+ it('should infer output type from transformation', () => {
99
+ const RouteWithTransform = createRoute('/test-transform', {
100
+ component: () => null,
101
+ validateParams: z.object({
102
+ id: z.string().transform((v) => parseInt(v)),
103
+ }),
104
+ });
105
+
106
+ // Should be number (output), not string (input)
107
+ assertType<{
108
+ id: number;
109
+ }>(RouteWithTransform.useParams());
110
+ });
111
+
112
+ it('should work with useParams hook', () => {
113
+ createRoute('/test-schema', {
114
+ component: () => null,
115
+ validateParams: z.object({
116
+ id: z.string(),
117
+ count: z.number(),
118
+ }),
119
+ });
120
+
121
+ assertType<{
122
+ id: string;
123
+ count: number;
124
+ }>(useParams({ from: '/test-schema' }));
125
+ });
126
+
127
+ it('should separate input and output types with defaults', () => {
128
+ const RouteWithDefaults = createRoute('/test-with-defaults', {
129
+ component: () => null,
130
+ validateParams: z.object({
131
+ animation: z.boolean().default(true),
132
+ }),
133
+ });
134
+
135
+ // useParams returns output type (required)
136
+ assertType<{ animation: boolean }>(RouteWithDefaults.useParams());
137
+
138
+ // navigation.navigate should accept input type (optional)
139
+ const navigation = useNavigation();
140
+
141
+ // Should accept with animation parameter
142
+ navigation.navigate('/test-with-defaults', { animation: false });
143
+
144
+ // Should accept without animation parameter (default will be used)
145
+ navigation.navigate('/test-with-defaults', {});
146
+ });
147
+ });
@@ -3,25 +3,35 @@ import {
3
3
  useNavigation as useNavigationNative,
4
4
  useRoute,
5
5
  } from '@granite-js/native/@react-navigation/native';
6
- import { NativeStackNavigationProp } from '@granite-js/native/@react-navigation/native-stack';
6
+ import {
7
+ NativeStackNavigationOptions,
8
+ NativeStackNavigationProp,
9
+ } from '@granite-js/native/@react-navigation/native-stack';
10
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
7
11
  import { useMemo } from 'react';
8
12
  import { RESERVED_PATHS } from './constants';
9
13
  import { defaultParserParams } from './utils/defaultParserParams';
14
+ import { type InferOutput, type InferInput } from './utils/standardSchema';
15
+ import { validateRouteParams } from './utils/validateRouteParams';
10
16
 
11
17
  export interface RouteOptions<T extends Readonly<object | undefined>> {
12
18
  parserParams?: (params: Record<string, unknown>) => Record<string, unknown>;
13
- validateParams?: (params: Readonly<object | undefined>) => T;
19
+ validateParams?: ((params: Readonly<object | undefined>) => T) | StandardSchemaV1<any, T>;
14
20
  component: React.FC<any>;
21
+ screenOptions?: NativeStackNavigationOptions | ((context: { params: T }) => NativeStackNavigationOptions);
15
22
  }
16
23
 
17
- export type NavigationProps = NativeStackNavigationProp<
18
- // @ts-expect-error - override type
19
- keyof RegisterScreen extends never ? ParamListBase : RegisterScreen
20
- >;
24
+ // eslint-disable-next-line @typescript-eslint/no-empty-object-type
25
+ export interface RegisterScreenInput {}
21
26
 
22
27
  // eslint-disable-next-line @typescript-eslint/no-empty-object-type
23
28
  export interface RegisterScreen {}
24
29
 
30
+ export type NavigationProps = NativeStackNavigationProp<
31
+ // @ts-expect-error - override type
32
+ keyof RegisterScreenInput extends never ? ParamListBase : RegisterScreenInput
33
+ >;
34
+
25
35
  export function useNavigation() {
26
36
  return useNavigationNative<NavigationProps>();
27
37
  }
@@ -37,8 +47,12 @@ export type RouteHooksOptions<TScreen extends keyof RegisterScreen> =
37
47
  };
38
48
 
39
49
  export const routeMap = new Map<
40
- keyof RegisterScreen,
41
- { options: Omit<RouteOptions<any>, 'component'>; component: React.FC<any> }
50
+ keyof RegisterScreenInput,
51
+ {
52
+ options: Omit<RouteOptions<any>, 'component' | 'screenOptions'>;
53
+ component: React.FC<any>;
54
+ screenOptions?: NativeStackNavigationOptions | ((context: { params: any }) => NativeStackNavigationOptions);
55
+ }
42
56
  >();
43
57
 
44
58
  export function useMatchOptions<TScreen extends keyof RegisterScreen>(options: RouteHooksOptions<TScreen>) {
@@ -140,23 +154,56 @@ export function useParams<TScreen extends keyof RegisterScreen>(
140
154
  }
141
155
 
142
156
  const parsedParams = routeOptions.parserParams(route.params as Record<string, string>);
143
- return isStrict && routeOptions.validateParams ? routeOptions.validateParams(parsedParams) : parsedParams;
157
+
158
+ if (!isStrict || !routeOptions.validateParams) {
159
+ return parsedParams;
160
+ }
161
+
162
+ return validateRouteParams(routeOptions.validateParams, parsedParams);
144
163
  }, [routeOptions, route.params, isStrict]);
145
164
 
146
165
  return params;
147
166
  }
148
167
 
149
- export const createRoute = <T extends Readonly<object | undefined>>(
150
- path: keyof RegisterScreen,
168
+ // Overload 1: StandardSchema pattern
169
+ export function createRoute<TSchema extends StandardSchemaV1<any, any>>(
170
+ path: keyof RegisterScreenInput,
171
+ options: Omit<RouteOptions<any>, 'validateParams'> & {
172
+ validateParams: TSchema;
173
+ }
174
+ ): {
175
+ _path: keyof RegisterScreenInput;
176
+ useNavigation: typeof useNavigation;
177
+ useParams: () => InferOutput<TSchema>;
178
+ _inputType?: InferInput<TSchema>;
179
+ _outputType?: InferOutput<TSchema>;
180
+ };
181
+
182
+ // Overload 2: Function pattern
183
+ export function createRoute<T extends Readonly<object | undefined>>(
184
+ path: keyof RegisterScreenInput,
151
185
  options: RouteOptions<T>
152
- ) => {
153
- const { component, ...restOptions } = options;
154
- routeMap.set(path, { options: restOptions, component });
186
+ ): {
187
+ _path: keyof RegisterScreenInput;
188
+ useNavigation: typeof useNavigation;
189
+ useParams: () => T;
190
+ _inputType?: T;
191
+ _outputType?: T;
192
+ };
193
+
194
+ // Implementation
195
+ export function createRoute(path: keyof RegisterScreenInput, options: RouteOptions<any>) {
196
+ const { component, screenOptions, ...restOptions } = options;
197
+ routeMap.set(path, {
198
+ options: restOptions,
199
+ component,
200
+ screenOptions: screenOptions,
201
+ });
155
202
 
156
- const _path = path as keyof RegisterScreen;
203
+ const _path = path as keyof RegisterScreenInput;
157
204
  return {
158
205
  _path,
159
206
  useNavigation,
160
- useParams: () => useParams({ from: _path, strict: true }) as T,
207
+ useParams: () => useParams({ from: _path as keyof RegisterScreen, strict: true }),
161
208
  };
162
- };
209
+ }
@@ -50,9 +50,9 @@ export function useRouterControls({
50
50
  };
51
51
 
52
52
  const routePath = routeScreen.path;
53
- const screenOptions = routeScreen.component?.screenOptions ?? {};
53
+ const options = routeScreen.screenOptions ?? {};
54
54
 
55
- return <StackNavigator.Screen key={routePath} name={routePath} component={Component} options={screenOptions} />;
55
+ return <StackNavigator.Screen key={routePath} name={routePath} component={Component} options={options} />;
56
56
  });
57
57
  }, [registerScreens, layoutScreenMap, ScreenContainer]);
58
58
 
@@ -1,3 +1,4 @@
1
+ import { NativeStackNavigationOptions } from '@granite-js/native/@react-navigation/native-stack';
1
2
  import { Screen } from './Screen';
2
3
 
3
4
  /**
@@ -14,4 +15,9 @@ export interface RouteScreen {
14
15
  * @description Screen component
15
16
  */
16
17
  component: Screen;
18
+ /**
19
+ * @name screenOptions
20
+ * @description Screen options for React Navigation (can be static or a function that receives route params)
21
+ */
22
+ screenOptions?: NativeStackNavigationOptions;
17
23
  }
@@ -1,6 +1,7 @@
1
1
  import { getRoutePath } from './path';
2
2
  import { routeMap } from '../createRoute';
3
3
  import { RequireContext, RouteScreen } from '../types';
4
+ import { validateRouteParams } from './validateRouteParams';
4
5
 
5
6
  /**
6
7
  * @kind function
@@ -33,9 +34,37 @@ export function getRouteScreens(context: RequireContext): RouteScreen[] {
33
34
  throw new Error(`Page component not found in ${key}.`);
34
35
  }
35
36
 
37
+ // Retrieve route configuration from routeMap
38
+ const routeMapEntry = routeMap.get(context(key)?.Route?._path);
39
+
40
+ // Get screenOptions from routeMap or component (routeMap takes priority)
41
+ const rawScreenOptions = routeMapEntry?.screenOptions ?? component.screenOptions;
42
+
43
+ // If screenOptions is a function, wrap it to apply parserParams and validateParams
44
+ const screenOptions =
45
+ typeof rawScreenOptions === 'function'
46
+ ? ({ route }: { route: { params?: Record<string, unknown> }; navigation: unknown }) => {
47
+ const rawParams = route.params ?? {};
48
+ const parserParams = routeMapEntry?.options?.parserParams;
49
+ const validateParams = routeMapEntry?.options?.validateParams;
50
+
51
+ // Apply parserParams if available
52
+ const parsedParams = parserParams ? parserParams(rawParams) : rawParams;
53
+
54
+ // Apply validateParams if available
55
+ try {
56
+ const validatedParams = validateParams ? validateRouteParams(validateParams, parsedParams) : parsedParams;
57
+ return rawScreenOptions({ params: validatedParams });
58
+ } catch {
59
+ return rawScreenOptions({ params: { _error: true } });
60
+ }
61
+ }
62
+ : rawScreenOptions;
63
+
36
64
  return {
37
65
  path,
38
66
  component,
67
+ screenOptions,
39
68
  };
40
69
  });
41
70
 
@@ -0,0 +1,18 @@
1
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
2
+
3
+ /**
4
+ * Type guard to check if a value is a StandardSchema v1 compliant schema
5
+ */
6
+ export function isStandardSchema(value: unknown): value is StandardSchemaV1 {
7
+ return typeof value === 'object' && value !== null && '~standard' in value;
8
+ }
9
+
10
+ /**
11
+ * Infer the output type from a StandardSchema
12
+ */
13
+ export type InferOutput<TSchema> = TSchema extends StandardSchemaV1<any, infer Output> ? Output : never;
14
+
15
+ /**
16
+ * Infer the input type from a StandardSchema
17
+ */
18
+ export type InferInput<TSchema> = TSchema extends StandardSchemaV1<infer Input, any> ? Input : never;
@@ -0,0 +1,40 @@
1
+ import type { StandardSchemaV1 } from '@standard-schema/spec';
2
+ import { isStandardSchema } from './standardSchema';
3
+
4
+ /**
5
+ * Validates route parameters using either a StandardSchema or a validation function.
6
+ *
7
+ * @param validateParams - The validation schema or function to apply
8
+ * @param parsedParams - The parsed parameters to validate
9
+ * @returns The validated parameters
10
+ * @throws Error if validation fails or async validation is attempted
11
+ */
12
+ export function validateRouteParams(
13
+ validateParams: ((params: Readonly<object | undefined>) => any) | StandardSchemaV1<any, any>,
14
+ parsedParams: Readonly<object | undefined>
15
+ ): any {
16
+ // Check if validateParams is a StandardSchema
17
+ if (isStandardSchema(validateParams)) {
18
+ const result = validateParams['~standard'].validate(parsedParams);
19
+
20
+ // Handle async results
21
+ if (result instanceof Promise) {
22
+ throw new Error('Async validation is not supported');
23
+ }
24
+
25
+ // Handle validation failures
26
+ if ('issues' in result && result.issues) {
27
+ const messages = result.issues.map((i) => i.message).join(', ');
28
+ throw new Error(`Parameter validation failed: ${messages}`);
29
+ }
30
+
31
+ return result.value;
32
+ }
33
+
34
+ // Function pattern
35
+ if (typeof validateParams === 'function') {
36
+ return validateParams(parsedParams);
37
+ }
38
+
39
+ return parsedParams;
40
+ }
@@ -96,4 +96,4 @@ function HiddenView({ style, pointerEvents = 'none', ...props }: ComponentProps<
96
96
  * Gradient value for BottomCTA. Set as a fixed value to avoid peerDeps.
97
97
  */
98
98
  const GRADIENT_HEIGHT = 37;
99
- const DEFAULT_BACKGROUND_COLOR = '#ffffff';
99
+ const DEFAULT_BACKGROUND_COLOR = 'transparent';