@vitraun/react-native 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE.txt ADDED
@@ -0,0 +1,10 @@
1
+ Vitraun — software license
2
+
3
+ Copyright (c) Vitraun. All rights reserved.
4
+
5
+ This package is proprietary. Redistribution, modification, sublicensing,
6
+ or use beyond the rights expressly granted in your written agreement with
7
+ Vitraun is prohibited unless Vitraun authorizes otherwise in writing.
8
+
9
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND. For
10
+ licensing inquiries, contact Vitraun.
package/README.md ADDED
@@ -0,0 +1,90 @@
1
+ # @vitraun/react-native
2
+
3
+ Official Vitraun Try-On adapter for React Native apps. The SDK calls **embed-init**, loads the returned **`vtoUrl`** in a WebView, and forwards cart events to your app.
4
+
5
+ The integrator **does not** hardcode the try-on URL.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @vitraun/react-native react-native-webview
11
+ ```
12
+
13
+ Peer dependencies: `react`, `react-native`, `react-native-webview`.
14
+
15
+ ## Prerequisites (Vitraun panel)
16
+
17
+ - Register **merchant ID** and **widget ID**
18
+ - Enable **`allowWebView`** on the channel
19
+ - Register **iOS bundle ID** and/or **Android package name**
20
+ - Add camera permission:
21
+ - iOS: `NSCameraUsageDescription` in `Info.plist`
22
+ - Android: `CAMERA` in `AndroidManifest.xml`
23
+
24
+ ## Usage
25
+
26
+ ```tsx
27
+ import { VitraunTryOn } from '@vitraun/react-native'
28
+
29
+ export const TryOnScreen = () => (
30
+ <VitraunTryOn
31
+ merchantId="vtrn-mch-key-00000000-0000-4000-8000-000000000001"
32
+ widgetId="vtrn-wdg-key-00000000-0000-4000-8000-000000000002"
33
+ lang="pt-BR"
34
+ platform="ios"
35
+ bundleId="com.sua.loja"
36
+ packageName="com.sua.loja"
37
+ isolatedSku="8028997081552"
38
+ basketOpensIn="event"
39
+ checkoutOpensIn="event"
40
+ onEvent={(event) => {
41
+ switch (event.type) {
42
+ case 'addToCart':
43
+ break
44
+ case 'redirectToCart':
45
+ break
46
+ default:
47
+ break
48
+ }
49
+ }}
50
+ />
51
+ )
52
+ ```
53
+
54
+ ## Init without UI
55
+
56
+ ```ts
57
+ import { initializeVitraunSession } from '@vitraun/react-native'
58
+
59
+ const session = await initializeVitraunSession({
60
+ state: {
61
+ merchantId: 'vtrn-mch-key-...',
62
+ widgetId: 'vtrn-wdg-key-...',
63
+ lang: 'en-US',
64
+ app: { platform: 'android', packageName: 'com.sua.loja' },
65
+ },
66
+ })
67
+
68
+ // session.vtoUrl — load in your own WebView if needed
69
+ ```
70
+
71
+ ## Staging
72
+
73
+ ```tsx
74
+ <VitraunTryOn env="staging" merchantId="..." widgetId="..." />
75
+ ```
76
+
77
+ ## Events
78
+
79
+ Same contract as `@vitraun/webar`:
80
+
81
+ - `addToCart`
82
+ - `removeFromCart`
83
+ - `redirectToCart`
84
+ - `analysisFinished`
85
+
86
+ ## Related docs
87
+
88
+ - Mobile HTTP contract: [`../docs/mobile-sdk-contract.md`](../docs/mobile-sdk-contract.md)
89
+ - iOS native stub: [`../ios/README.md`](../ios/README.md)
90
+ - Android native stub: [`../android/README.md`](../android/README.md)
@@ -0,0 +1,116 @@
1
+ import type { StyleProp, ViewStyle } from 'react-native'
2
+ import type {
3
+ PostEmbedInitOptions,
4
+ VitraunEmbedInitSession,
5
+ VitraunEmbedInitState,
6
+ VitraunVTOEventType,
7
+ } from '@vitraun/core'
8
+
9
+ export type {
10
+ PostEmbedInitOptions,
11
+ VitraunEmbedInitSession,
12
+ VitraunEmbedInitState,
13
+ VitraunVTOEventType,
14
+ } from '@vitraun/core'
15
+
16
+ export {
17
+ VITRAUN_VTO_DEFAULT_EVENT_TYPES,
18
+ VITRAUN_VTO_EXTRA_EVENT_TYPES,
19
+ initializeVitraunSession,
20
+ postEmbedInit,
21
+ } from '@vitraun/core'
22
+
23
+ export type VitraunBridgeEvent = {
24
+ type: VitraunVTOEventType
25
+ detail?: unknown
26
+ }
27
+
28
+ export type VitraunTryOnBasketOpensIn = 'blank' | 'self' | 'event'
29
+ export type VitraunTryOnCheckoutOpensIn = 'blank' | 'self' | 'event'
30
+ export type VitraunTryOnFlow = 'checkout' | 'catalog'
31
+
32
+ export type VitraunTryOnProps = {
33
+ merchantId: string
34
+ widgetId: string
35
+ lang?: string
36
+ env?: string
37
+ apiPort?: number | null
38
+ appPort?: number | null
39
+ isolatedSku?: string
40
+ applySkus?: string[]
41
+ flow?: VitraunTryOnFlow
42
+ basketOpensIn?: VitraunTryOnBasketOpensIn
43
+ checkoutOpensIn?: VitraunTryOnCheckoutOpensIn
44
+ useSimplePage?: boolean
45
+ showDetails?: boolean
46
+ showProductPrice?: boolean
47
+ priceFrom?: number | null
48
+ priceTo?: number | null
49
+ currencySymbol?: string | null
50
+ isSimulationMode?: boolean
51
+ platform?: string
52
+ bundleId?: string
53
+ packageName?: string
54
+ appId?: string
55
+ appVersion?: string
56
+ attestationToken?: string
57
+ embedParentOrigin?: string | null
58
+ pageUrl?: string | null
59
+ referrer?: string | null
60
+ userAgent?: string | null
61
+ timezone?: string | null
62
+ initTimeoutMs?: number
63
+ autoInit?: boolean
64
+ onEvent?: (event: VitraunBridgeEvent) => void
65
+ onError?: (error: Error) => void
66
+ onReady?: () => void
67
+ style?: StyleProp<ViewStyle>
68
+ }
69
+
70
+ export type UseVitraunInitOptions = {
71
+ enabled?: boolean
72
+ state: VitraunEmbedInitState
73
+ sessionStorage?: PostEmbedInitOptions['sessionStorage']
74
+ sessionInstanceId?: string
75
+ timeoutMs?: number
76
+ }
77
+
78
+ export type UseVitraunInitResult = {
79
+ status: 'idle' | 'loading' | 'ready' | 'error'
80
+ session: VitraunEmbedInitSession | null
81
+ error: Error | null
82
+ reload: () => Promise<VitraunEmbedInitSession | null>
83
+ }
84
+
85
+ export const handleVitraunWebViewMessage: (
86
+ raw: string,
87
+ onEvent?: (event: VitraunBridgeEvent) => void,
88
+ ) => void
89
+
90
+ export const parseVitraunBridgeMessage: (
91
+ raw: string,
92
+ ) => VitraunBridgeEvent | null
93
+
94
+ export const buildInjectedBridgeScript: () => string
95
+ export const buildNativeConfigInjectScript: (
96
+ overrides: Record<string, unknown> | null | undefined,
97
+ ) => string
98
+ export const buildInjectedJavaScriptBeforeContentLoaded: (
99
+ nativeOverrides: Record<string, unknown> | null | undefined,
100
+ ) => string
101
+
102
+ export const buildEmbedInitStateFromProps: (
103
+ props: VitraunTryOnProps,
104
+ ) => VitraunEmbedInitState
105
+
106
+ export const buildNativeConfigOverrides: (
107
+ props: VitraunTryOnProps,
108
+ ) => Record<string, unknown> | null
109
+
110
+ export const useVitraunInit: (
111
+ options: UseVitraunInitOptions,
112
+ ) => UseVitraunInitResult
113
+
114
+ export const VitraunTryOn: (
115
+ props: VitraunTryOnProps,
116
+ ) => JSX.Element
package/dist/index.js ADDED
@@ -0,0 +1,418 @@
1
+ // src/index.js
2
+ import {
3
+ VITRAUN_VTO_DEFAULT_EVENT_TYPES as VITRAUN_VTO_DEFAULT_EVENT_TYPES2,
4
+ VITRAUN_VTO_EXTRA_EVENT_TYPES,
5
+ initializeVitraunSession,
6
+ postEmbedInit as postEmbedInit2
7
+ } from "@vitraun/core";
8
+
9
+ // src/bridge.js
10
+ import { VITRAUN_VTO_DEFAULT_EVENT_TYPES } from "@vitraun/core";
11
+ var DEFAULT_EVENT_TYPES = new Set(VITRAUN_VTO_DEFAULT_EVENT_TYPES);
12
+ var parseVitraunBridgeMessage = (raw) => {
13
+ if (typeof raw !== "string" || !raw.trim()) {
14
+ return null;
15
+ }
16
+ try {
17
+ const data = JSON.parse(raw);
18
+ if (!data || typeof data !== "object") {
19
+ return null;
20
+ }
21
+ const type = typeof data.type === "string" ? data.type.trim() : "";
22
+ if (!type || !DEFAULT_EVENT_TYPES.has(type)) {
23
+ return null;
24
+ }
25
+ return {
26
+ type,
27
+ detail: "detail" in data ? data.detail : void 0
28
+ };
29
+ } catch {
30
+ return null;
31
+ }
32
+ };
33
+ var handleVitraunWebViewMessage = (raw, onEvent) => {
34
+ const event = parseVitraunBridgeMessage(raw);
35
+ if (!event || typeof onEvent !== "function") {
36
+ return;
37
+ }
38
+ onEvent(event);
39
+ };
40
+
41
+ // src/injected-bridge.js
42
+ var buildInjectedBridgeScript = () => `
43
+ (function() {
44
+ if (window.__vitraunRnBridgeInstalled) {
45
+ return true;
46
+ }
47
+ window.__vitraunRnBridgeInstalled = true;
48
+ window.addEventListener('message', function(event) {
49
+ var data = event.data;
50
+ if (!data || typeof data !== 'object' || !data.type) {
51
+ return;
52
+ }
53
+ if (window.ReactNativeWebView && typeof window.ReactNativeWebView.postMessage === 'function') {
54
+ window.ReactNativeWebView.postMessage(JSON.stringify(data));
55
+ }
56
+ });
57
+ true;
58
+ })();`;
59
+ var buildNativeConfigInjectScript = (overrides) => {
60
+ if (!overrides || Object.keys(overrides).length === 0) {
61
+ return "";
62
+ }
63
+ const serialized = JSON.stringify(overrides);
64
+ return `
65
+ (function() {
66
+ window.__TRYON_NATIVE_CONFIG__ = Object.assign(
67
+ window.__TRYON_NATIVE_CONFIG__ || {},
68
+ ${serialized}
69
+ );
70
+ true;
71
+ })();`;
72
+ };
73
+ var buildInjectedJavaScriptBeforeContentLoaded = (nativeOverrides) => {
74
+ const parts = [
75
+ buildNativeConfigInjectScript(nativeOverrides),
76
+ buildInjectedBridgeScript()
77
+ ].filter((part) => part.trim().length > 0);
78
+ return parts.join("\n");
79
+ };
80
+
81
+ // src/native-config-inject.js
82
+ var buildNativeConfigOverrides = (props) => {
83
+ const overrides = {};
84
+ if (props.basketOpensIn) {
85
+ overrides.basketOpensIn = props.basketOpensIn;
86
+ }
87
+ if (props.checkoutOpensIn) {
88
+ overrides.checkoutOpensIn = props.checkoutOpensIn;
89
+ }
90
+ if (props.flow) {
91
+ overrides.flow = props.flow;
92
+ }
93
+ if (props.useSimplePage != null) {
94
+ overrides.useSimplePage = props.useSimplePage;
95
+ }
96
+ if (props.showDetails != null) {
97
+ overrides.showDetails = props.showDetails;
98
+ }
99
+ if (props.showProductPrice != null) {
100
+ overrides.showProductPrice = props.showProductPrice;
101
+ }
102
+ if (props.priceFrom != null) {
103
+ overrides.priceFrom = props.priceFrom;
104
+ }
105
+ if (props.priceTo != null) {
106
+ overrides.priceTo = props.priceTo;
107
+ }
108
+ if (props.currencySymbol != null) {
109
+ overrides.currencySymbol = props.currencySymbol;
110
+ }
111
+ if (props.isolatedSku) {
112
+ overrides.isolatedSku = props.isolatedSku;
113
+ }
114
+ if (Array.isArray(props.applySkus) && props.applySkus.length > 0) {
115
+ overrides.applySkus = props.applySkus;
116
+ }
117
+ if (props.isSimulationMode != null) {
118
+ overrides.isSimulationMode = props.isSimulationMode;
119
+ }
120
+ return Object.keys(overrides).length > 0 ? overrides : null;
121
+ };
122
+ var buildEmbedInitStateFromProps = (props) => {
123
+ const platform = typeof props.platform === "string" && props.platform.trim() ? props.platform.trim() : void 0;
124
+ return {
125
+ merchantId: String(props.merchantId ?? "").trim(),
126
+ widgetId: String(props.widgetId ?? "").trim(),
127
+ env: props.env ?? "",
128
+ apiPort: props.apiPort ?? null,
129
+ appPort: props.appPort ?? null,
130
+ lang: props.lang ?? "en-US",
131
+ isSimulationMode: props.isSimulationMode === true,
132
+ app: {
133
+ appId: props.appId ?? null,
134
+ platform: platform ?? null,
135
+ bundleId: props.bundleId ?? null,
136
+ packageName: props.packageName ?? null,
137
+ appVersion: props.appVersion ?? null,
138
+ attestationToken: props.attestationToken ?? null
139
+ },
140
+ context: {
141
+ origin: props.embedParentOrigin ?? null,
142
+ pageUrl: props.pageUrl ?? null,
143
+ referrer: props.referrer ?? null,
144
+ userAgent: props.userAgent ?? null,
145
+ timezone: props.timezone ?? null
146
+ }
147
+ };
148
+ };
149
+
150
+ // src/use-vitraun-init.js
151
+ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
152
+ import { postEmbedInit } from "@vitraun/core";
153
+ var useVitraunInit = (options) => {
154
+ const enabled = options.enabled !== false;
155
+ const [status, setStatus] = useState(
156
+ /** @type {'idle' | 'loading' | 'ready' | 'error'} */
157
+ "idle"
158
+ );
159
+ const [session, setSession] = useState(
160
+ /** @type {VitraunEmbedInitSession | null} */
161
+ null
162
+ );
163
+ const [error, setError] = useState(
164
+ /** @type {Error | null} */
165
+ null
166
+ );
167
+ const requestIdRef = useRef(0);
168
+ const initOptions = useMemo(
169
+ () => ({
170
+ state: options.state,
171
+ sessionStorage: options.sessionStorage,
172
+ sessionInstanceId: options.sessionInstanceId,
173
+ timeoutMs: options.timeoutMs
174
+ }),
175
+ [
176
+ options.state,
177
+ options.sessionStorage,
178
+ options.sessionInstanceId,
179
+ options.timeoutMs
180
+ ]
181
+ );
182
+ const runInit = useCallback(async () => {
183
+ if (!enabled) {
184
+ return null;
185
+ }
186
+ const merchantId = initOptions.state.merchantId?.trim();
187
+ const widgetId = initOptions.state.widgetId?.trim();
188
+ if (!merchantId || !widgetId) {
189
+ const validationError = new Error(
190
+ !merchantId ? "The merchant-id attribute is required." : "The widget-id attribute is required."
191
+ );
192
+ setStatus("error");
193
+ setError(validationError);
194
+ setSession(null);
195
+ return null;
196
+ }
197
+ const requestId = requestIdRef.current + 1;
198
+ requestIdRef.current = requestId;
199
+ setStatus("loading");
200
+ setError(null);
201
+ try {
202
+ const result = await postEmbedInit(fetch, initOptions);
203
+ if (requestIdRef.current !== requestId) {
204
+ return null;
205
+ }
206
+ setSession(result);
207
+ setStatus("ready");
208
+ return result;
209
+ } catch (initError) {
210
+ if (requestIdRef.current !== requestId) {
211
+ return null;
212
+ }
213
+ const normalized = initError instanceof Error ? initError : new Error("Could not start the virtual try-on. Please try again.");
214
+ setSession(null);
215
+ setStatus("error");
216
+ setError(normalized);
217
+ return null;
218
+ }
219
+ }, [enabled, initOptions]);
220
+ useEffect(() => {
221
+ if (!enabled) {
222
+ return;
223
+ }
224
+ void runInit();
225
+ }, [enabled, runInit]);
226
+ return {
227
+ status,
228
+ session,
229
+ error,
230
+ reload: runInit
231
+ };
232
+ };
233
+
234
+ // src/vitraun-try-on.jsx
235
+ import { useEffect as useEffect2, useMemo as useMemo2 } from "react";
236
+ import { ActivityIndicator, StyleSheet, Text, View } from "react-native";
237
+ import WebView from "react-native-webview";
238
+ import { jsx } from "react/jsx-runtime";
239
+ var styles = StyleSheet.create({
240
+ container: {
241
+ flex: 1
242
+ },
243
+ center: {
244
+ flex: 1,
245
+ alignItems: "center",
246
+ justifyContent: "center",
247
+ padding: 24
248
+ },
249
+ errorText: {
250
+ color: "#b00020",
251
+ textAlign: "center"
252
+ }
253
+ });
254
+ var VitraunTryOn = (props) => {
255
+ const {
256
+ merchantId,
257
+ widgetId,
258
+ lang,
259
+ env,
260
+ apiPort,
261
+ appPort,
262
+ isolatedSku,
263
+ applySkus,
264
+ flow,
265
+ basketOpensIn,
266
+ checkoutOpensIn,
267
+ useSimplePage,
268
+ showDetails,
269
+ showProductPrice,
270
+ priceFrom,
271
+ priceTo,
272
+ currencySymbol,
273
+ isSimulationMode,
274
+ platform,
275
+ bundleId,
276
+ packageName,
277
+ appId,
278
+ appVersion,
279
+ attestationToken,
280
+ embedParentOrigin,
281
+ pageUrl,
282
+ referrer,
283
+ userAgent,
284
+ timezone,
285
+ initTimeoutMs,
286
+ autoInit = true,
287
+ onEvent,
288
+ onError,
289
+ onReady,
290
+ style
291
+ } = props;
292
+ const embedState = useMemo2(
293
+ () => buildEmbedInitStateFromProps({
294
+ merchantId,
295
+ widgetId,
296
+ lang,
297
+ env,
298
+ apiPort,
299
+ appPort,
300
+ isSimulationMode,
301
+ platform,
302
+ bundleId,
303
+ packageName,
304
+ appId,
305
+ appVersion,
306
+ attestationToken,
307
+ embedParentOrigin,
308
+ pageUrl,
309
+ referrer,
310
+ userAgent,
311
+ timezone
312
+ }),
313
+ [
314
+ merchantId,
315
+ widgetId,
316
+ lang,
317
+ env,
318
+ apiPort,
319
+ appPort,
320
+ isSimulationMode,
321
+ platform,
322
+ bundleId,
323
+ packageName,
324
+ appId,
325
+ appVersion,
326
+ attestationToken,
327
+ embedParentOrigin,
328
+ pageUrl,
329
+ referrer,
330
+ userAgent,
331
+ timezone
332
+ ]
333
+ );
334
+ const { status, session, error } = useVitraunInit({
335
+ enabled: autoInit,
336
+ state: embedState,
337
+ timeoutMs: initTimeoutMs
338
+ });
339
+ const injectedScript = useMemo2(
340
+ () => buildInjectedJavaScriptBeforeContentLoaded(
341
+ buildNativeConfigOverrides({
342
+ basketOpensIn,
343
+ checkoutOpensIn,
344
+ flow,
345
+ useSimplePage,
346
+ showDetails,
347
+ showProductPrice,
348
+ priceFrom,
349
+ priceTo,
350
+ currencySymbol,
351
+ isolatedSku,
352
+ applySkus,
353
+ isSimulationMode
354
+ })
355
+ ),
356
+ [
357
+ basketOpensIn,
358
+ checkoutOpensIn,
359
+ flow,
360
+ useSimplePage,
361
+ showDetails,
362
+ showProductPrice,
363
+ priceFrom,
364
+ priceTo,
365
+ currencySymbol,
366
+ isolatedSku,
367
+ applySkus,
368
+ isSimulationMode
369
+ ]
370
+ );
371
+ useEffect2(() => {
372
+ if (status === "ready" && typeof onReady === "function") {
373
+ onReady();
374
+ }
375
+ }, [status, onReady]);
376
+ useEffect2(() => {
377
+ if (status === "error" && error && typeof onError === "function") {
378
+ onError(error);
379
+ }
380
+ }, [status, error, onError]);
381
+ if (status === "loading" || status === "idle") {
382
+ return /* @__PURE__ */ jsx(View, { style: [styles.center, style], accessibilityRole: "progressbar", children: /* @__PURE__ */ jsx(ActivityIndicator, { size: "large" }) });
383
+ }
384
+ if (status === "error" || !session?.vtoUrl) {
385
+ const message = error?.message ?? "Could not start the virtual try-on. Please try again.";
386
+ return /* @__PURE__ */ jsx(View, { style: [styles.center, style], accessibilityRole: "alert", children: /* @__PURE__ */ jsx(Text, { style: styles.errorText, children: message }) });
387
+ }
388
+ return /* @__PURE__ */ jsx(View, { style: [styles.container, style], children: /* @__PURE__ */ jsx(
389
+ WebView,
390
+ {
391
+ source: { uri: session.vtoUrl },
392
+ style: styles.container,
393
+ mediaPlaybackRequiresUserAction: false,
394
+ allowsInlineMediaPlayback: true,
395
+ javaScriptEnabled: true,
396
+ domStorageEnabled: true,
397
+ injectedJavaScriptBeforeContentLoaded: injectedScript,
398
+ onMessage: (event) => {
399
+ handleVitraunWebViewMessage(event.nativeEvent.data, onEvent);
400
+ }
401
+ }
402
+ ) });
403
+ };
404
+ export {
405
+ VITRAUN_VTO_DEFAULT_EVENT_TYPES2 as VITRAUN_VTO_DEFAULT_EVENT_TYPES,
406
+ VITRAUN_VTO_EXTRA_EVENT_TYPES,
407
+ VitraunTryOn,
408
+ buildEmbedInitStateFromProps,
409
+ buildInjectedBridgeScript,
410
+ buildInjectedJavaScriptBeforeContentLoaded,
411
+ buildNativeConfigInjectScript,
412
+ buildNativeConfigOverrides,
413
+ handleVitraunWebViewMessage,
414
+ initializeVitraunSession,
415
+ parseVitraunBridgeMessage,
416
+ postEmbedInit2 as postEmbedInit,
417
+ useVitraunInit
418
+ };
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@vitraun/react-native",
3
+ "version": "0.1.0",
4
+ "description": "Vitraun Try-On React Native adapter (WebView + embed-init bridge)",
5
+ "license": "SEE LICENSE IN LICENSE.txt",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/clagils/tryon-new-version.git",
9
+ "directory": "vto-npm/react-native"
10
+ },
11
+ "homepage": "https://github.com/clagils/tryon-new-version/tree/main/vto-npm/react-native",
12
+ "bugs": {
13
+ "url": "https://github.com/clagils/tryon-new-version/issues"
14
+ },
15
+ "publishConfig": {
16
+ "access": "public"
17
+ },
18
+ "private": false,
19
+ "type": "module",
20
+ "main": "./dist/index.js",
21
+ "module": "./dist/index.js",
22
+ "types": "./dist/index.d.ts",
23
+ "files": [
24
+ "LICENSE.txt",
25
+ "dist",
26
+ "README.md"
27
+ ],
28
+ "scripts": {
29
+ "build": "node ./scripts/build.mjs",
30
+ "clean": "rm -rf dist",
31
+ "test": "vitest run"
32
+ },
33
+ "dependencies": {
34
+ "@vitraun/core": "^0.2.0"
35
+ },
36
+ "peerDependencies": {
37
+ "react": ">=18",
38
+ "react-native": ">=0.72",
39
+ "react-native-webview": ">=13"
40
+ },
41
+ "devDependencies": {
42
+ "@babel/core": "^7.28.0",
43
+ "@babel/preset-react": "^7.27.1",
44
+ "esbuild": "^0.25.9",
45
+ "react": "^19.2.5",
46
+ "vitest": "^3.2.4"
47
+ }
48
+ }