@vibecodeapp/sdk 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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 vibecode
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,110 @@
1
+ # @vibecodeapp/sdk
2
+
3
+ SDK for Vibecodeapp.com - Web polyfills and Metro configuration for React Native.
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @vibecodeapp/sdk
9
+ # or
10
+ yarn add @vibecodeapp/sdk
11
+ # or
12
+ pnpm add @vibecodeapp/sdk
13
+ ```
14
+
15
+ ## Metro Configuration
16
+
17
+ The SDK provides a Metro wrapper that adds:
18
+ - Web polyfills for native modules
19
+ - HTTP/2 remote cache support
20
+ - Configurable local file caching
21
+
22
+ ### Basic Usage
23
+
24
+ ```javascript
25
+ // metro.config.js
26
+ const { getDefaultConfig } = require("expo/metro-config");
27
+ const { withVibecodeMetro } = require("@vibecodeapp/sdk/metro");
28
+
29
+ const config = getDefaultConfig(__dirname);
30
+
31
+ module.exports = withVibecodeMetro(config);
32
+ ```
33
+
34
+ ### With NativeWind
35
+
36
+ ```javascript
37
+ // metro.config.js
38
+ const { getDefaultConfig } = require("expo/metro-config");
39
+ const { withNativeWind } = require("nativewind/metro");
40
+ const { withVibecodeMetro } = require("@vibecodeapp/sdk/metro");
41
+
42
+ const config = getDefaultConfig(__dirname);
43
+
44
+ // Apply Vibecode config first, then NativeWind
45
+ module.exports = withNativeWind(
46
+ withVibecodeMetro(config),
47
+ { input: "./global.css" }
48
+ );
49
+ ```
50
+
51
+ ### With Remote HTTP Cache
52
+
53
+ ```javascript
54
+ // metro.config.js
55
+ const { getDefaultConfig } = require("expo/metro-config");
56
+ const { withVibecodeMetro } = require("@vibecodeapp/sdk/metro");
57
+
58
+ const config = getDefaultConfig(__dirname);
59
+
60
+ module.exports = withVibecodeMetro(config, {
61
+ httpEndpoint: process.env.METRO_CACHE_HTTP_ENDPOINT,
62
+ httpTimeout: 30000,
63
+ httpMaxConnections: 64,
64
+ httpMaxConcurrent: 256,
65
+ });
66
+ ```
67
+
68
+ ### Configuration Options
69
+
70
+ | Option | Type | Default | Description |
71
+ |--------|------|---------|-------------|
72
+ | `enableCache` | boolean | `true` | Enable cache configuration |
73
+ | `cacheDir` | string | `~/.metro-cache` | Local cache directory |
74
+ | `cacheVersion` | string | `"1"` | Cache version (increment to invalidate) |
75
+ | `httpEndpoint` | string | - | HTTP/2 cache server URL |
76
+ | `httpTimeout` | number | `30000` | HTTP request timeout (ms) |
77
+ | `httpMaxConnections` | number | `64` | HTTP/2 connection pool size |
78
+ | `httpMaxConcurrent` | number | `256` | Max concurrent HTTP requests |
79
+
80
+ ### Environment Variables
81
+
82
+ The SDK also respects these environment variables:
83
+
84
+ | Variable | Description |
85
+ |----------|-------------|
86
+ | `METRO_CACHE_DIR` | Local cache directory |
87
+ | `METRO_CACHE_VERSION` | Cache version string |
88
+ | `METRO_CACHE_HTTP_ENDPOINT` | HTTP/2 cache server URL |
89
+
90
+ ## Web Polyfills
91
+
92
+ The SDK automatically provides web polyfills for:
93
+
94
+ | Native Module | Web Implementation |
95
+ |---------------|-------------------|
96
+ | `expo-haptics` | Vibration API |
97
+ | `expo-secure-store` | localStorage |
98
+ | `react-native-maps` | Google Maps (`@teovilla/react-native-web-maps`) |
99
+ | `RefreshControl` | Custom PanResponder implementation |
100
+ | `Alert` | Web-compatible alert dialog |
101
+
102
+ ### Environment Variables for Polyfills
103
+
104
+ | Variable | Purpose |
105
+ |----------|---------|
106
+ | `EXPO_PUBLIC_GOOGLE_MAPS_API_KEY` | Google Maps API key (for web maps) |
107
+
108
+ ## License
109
+
110
+ MIT
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Vibecode SDK - AI-powered mobile app builder toolkit
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Vibecode SDK - AI-powered mobile app builder toolkit
3
+ *
4
+ * @packageDocumentation
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Web polyfill for React Native Alert
3
+ */
4
+ import AlertNew from "@blazejkustra/react-native-alert";
5
+ export default AlertNew;
6
+ //# sourceMappingURL=alert.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alert.web.d.ts","sourceRoot":"","sources":["../../src/polyfills/alert.web.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,QAAQ,MAAM,kCAAkC,CAAC;AAExD,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Web polyfill for React Native Alert
3
+ */
4
+ import AlertNew from "@blazejkustra/react-native-alert";
5
+ export default AlertNew;
6
+ //# sourceMappingURL=alert.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"alert.web.js","sourceRoot":"","sources":["../../src/polyfills/alert.web.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,QAAQ,MAAM,kCAAkC,CAAC;AAExD,eAAe,QAAQ,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Web polyfill for expo-haptics using the Vibration API
3
+ *
4
+ * This provides haptic-like feedback on web browsers that support vibration.
5
+ */
6
+ export declare enum NotificationFeedbackType {
7
+ Success = "success",
8
+ Warning = "warning",
9
+ Error = "error"
10
+ }
11
+ export declare enum ImpactFeedbackStyle {
12
+ Light = "light",
13
+ Medium = "medium",
14
+ Heavy = "heavy",
15
+ Soft = "soft",
16
+ Rigid = "rigid"
17
+ }
18
+ export declare function selectionAsync(): Promise<void>;
19
+ export declare function notificationAsync(type?: NotificationFeedbackType): Promise<void>;
20
+ export declare function impactAsync(style?: ImpactFeedbackStyle): Promise<void>;
21
+ //# sourceMappingURL=haptics.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"haptics.web.d.ts","sourceRoot":"","sources":["../../src/polyfills/haptics.web.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,oBAAY,wBAAwB;IAClC,OAAO,YAAY;IACnB,OAAO,YAAY;IACnB,KAAK,UAAU;CAChB;AAED,oBAAY,mBAAmB;IAC7B,KAAK,UAAU;IACf,MAAM,WAAW;IACjB,KAAK,UAAU;IACf,IAAI,SAAS;IACb,KAAK,UAAU;CAChB;AAsBD,wBAAsB,cAAc,IAAI,OAAO,CAAC,IAAI,CAAC,CAKpD;AAED,wBAAsB,iBAAiB,CACrC,IAAI,GAAE,wBAA2D,GAChE,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,WAAW,CAC/B,KAAK,GAAE,mBAAgD,GACtD,OAAO,CAAC,IAAI,CAAC,CAKf"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Web polyfill for expo-haptics using the Vibration API
3
+ *
4
+ * This provides haptic-like feedback on web browsers that support vibration.
5
+ */
6
+ export var NotificationFeedbackType;
7
+ (function (NotificationFeedbackType) {
8
+ NotificationFeedbackType["Success"] = "success";
9
+ NotificationFeedbackType["Warning"] = "warning";
10
+ NotificationFeedbackType["Error"] = "error";
11
+ })(NotificationFeedbackType || (NotificationFeedbackType = {}));
12
+ export var ImpactFeedbackStyle;
13
+ (function (ImpactFeedbackStyle) {
14
+ ImpactFeedbackStyle["Light"] = "light";
15
+ ImpactFeedbackStyle["Medium"] = "medium";
16
+ ImpactFeedbackStyle["Heavy"] = "heavy";
17
+ ImpactFeedbackStyle["Soft"] = "soft";
18
+ ImpactFeedbackStyle["Rigid"] = "rigid";
19
+ })(ImpactFeedbackStyle || (ImpactFeedbackStyle = {}));
20
+ const vibrationPatterns = {
21
+ [NotificationFeedbackType.Success]: [40, 100, 40],
22
+ [NotificationFeedbackType.Warning]: [50, 100, 50],
23
+ [NotificationFeedbackType.Error]: [60, 100, 60, 100, 60],
24
+ [ImpactFeedbackStyle.Light]: [40],
25
+ [ImpactFeedbackStyle.Medium]: [50],
26
+ [ImpactFeedbackStyle.Heavy]: [60],
27
+ [ImpactFeedbackStyle.Soft]: [35],
28
+ [ImpactFeedbackStyle.Rigid]: [45],
29
+ selection: [50],
30
+ };
31
+ function isVibrationAvailable() {
32
+ return (typeof window !== "undefined" &&
33
+ "navigator" in window &&
34
+ "vibrate" in navigator);
35
+ }
36
+ export async function selectionAsync() {
37
+ if (!isVibrationAvailable()) {
38
+ return;
39
+ }
40
+ navigator.vibrate(vibrationPatterns.selection);
41
+ }
42
+ export async function notificationAsync(type = NotificationFeedbackType.Success) {
43
+ if (!isVibrationAvailable()) {
44
+ return;
45
+ }
46
+ navigator.vibrate(vibrationPatterns[type]);
47
+ }
48
+ export async function impactAsync(style = ImpactFeedbackStyle.Medium) {
49
+ if (!isVibrationAvailable()) {
50
+ return;
51
+ }
52
+ navigator.vibrate(vibrationPatterns[style]);
53
+ }
54
+ //# sourceMappingURL=haptics.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"haptics.web.js","sourceRoot":"","sources":["../../src/polyfills/haptics.web.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,CAAN,IAAY,wBAIX;AAJD,WAAY,wBAAwB;IAClC,+CAAmB,CAAA;IACnB,+CAAmB,CAAA;IACnB,2CAAe,CAAA;AACjB,CAAC,EAJW,wBAAwB,KAAxB,wBAAwB,QAInC;AAED,MAAM,CAAN,IAAY,mBAMX;AAND,WAAY,mBAAmB;IAC7B,sCAAe,CAAA;IACf,wCAAiB,CAAA;IACjB,sCAAe,CAAA;IACf,oCAAa,CAAA;IACb,sCAAe,CAAA;AACjB,CAAC,EANW,mBAAmB,KAAnB,mBAAmB,QAM9B;AAED,MAAM,iBAAiB,GAAsC;IAC3D,CAAC,wBAAwB,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC;IACjD,CAAC,wBAAwB,CAAC,OAAO,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC;IACjD,CAAC,wBAAwB,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC;IACxD,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;IACjC,CAAC,mBAAmB,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;IAClC,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;IACjC,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC;IAChC,CAAC,mBAAmB,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,CAAC;IACjC,SAAS,EAAE,CAAC,EAAE,CAAC;CAChB,CAAC;AAEF,SAAS,oBAAoB;IAC3B,OAAO,CACL,OAAO,MAAM,KAAK,WAAW;QAC7B,WAAW,IAAI,MAAM;QACrB,SAAS,IAAI,SAAS,CACvB,CAAC;AACJ,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc;IAClC,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;QAC5B,OAAO;IACT,CAAC;IACD,SAAS,CAAC,OAAO,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC;AACjD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,OAAiC,wBAAwB,CAAC,OAAO;IAEjE,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;QAC5B,OAAO;IACT,CAAC;IACD,SAAS,CAAC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC;AAC7C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,WAAW,CAC/B,QAA6B,mBAAmB,CAAC,MAAM;IAEvD,IAAI,CAAC,oBAAoB,EAAE,EAAE,CAAC;QAC5B,OAAO;IACT,CAAC;IACD,SAAS,CAAC,OAAO,CAAC,iBAAiB,CAAC,KAAK,CAAC,CAAC,CAAC;AAC9C,CAAC"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Web polyfill for react-native-maps using Google Maps
3
+ */
4
+ import React from "react";
5
+ export declare const PROVIDER_GOOGLE = "google";
6
+ export declare const PROVIDER_DEFAULT: undefined;
7
+ declare const MapView: React.ForwardRefExoticComponent<Omit<any, "ref"> & React.RefAttributes<unknown>>;
8
+ export declare const Marker: any, Callout: any, Polyline: any, Polygon: any, Circle: any, Overlay: any, Heatmap: any, UrlTile: any, WMSTile: any, LocalTile: any;
9
+ export default MapView;
10
+ //# sourceMappingURL=maps.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"maps.web.d.ts","sourceRoot":"","sources":["../../src/polyfills/maps.web.ts"],"names":[],"mappings":"AAAA;;GAEG;AAIH,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,eAAO,MAAM,eAAe,WAAW,CAAC;AACxC,eAAO,MAAM,gBAAgB,WAAY,CAAC;AAK1C,QAAA,MAAM,OAAO,kFAOX,CAAC;AAUH,eAAO,MACL,MAAM,OACN,OAAO,OACP,QAAQ,OACR,OAAO,OACP,MAAM,OACN,OAAO,OACP,OAAO,OACP,OAAO,OACP,OAAO,OACP,SAAS,KACA,CAAC;AAEZ,eAAe,OAAO,CAAC"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Web polyfill for react-native-maps using Google Maps
3
+ */
4
+ // @ts-nocheck
5
+ import WebMapView, * as WebMaps from "@teovilla/react-native-web-maps";
6
+ import React from "react";
7
+ export const PROVIDER_GOOGLE = "google";
8
+ export const PROVIDER_DEFAULT = undefined;
9
+ const GOOGLE_MAPS_API_KEY = process.env
10
+ .EXPO_PUBLIC_GOOGLE_MAPS_API_KEY;
11
+ const MapView = React.forwardRef((props, ref) => {
12
+ return React.createElement(WebMapView, {
13
+ ref,
14
+ googleMapsApiKey: GOOGLE_MAPS_API_KEY,
15
+ ...props,
16
+ provider: PROVIDER_GOOGLE,
17
+ });
18
+ });
19
+ MapView.displayName = "MapView";
20
+ Object.assign(MapView, {
21
+ ...WebMaps,
22
+ PROVIDER_GOOGLE,
23
+ PROVIDER_DEFAULT,
24
+ });
25
+ export const { Marker, Callout, Polyline, Polygon, Circle, Overlay, Heatmap, UrlTile, WMSTile, LocalTile, } = WebMaps;
26
+ export default MapView;
27
+ //# sourceMappingURL=maps.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"maps.web.js","sourceRoot":"","sources":["../../src/polyfills/maps.web.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,cAAc;AACd,OAAO,UAAU,EAAE,KAAK,OAAO,MAAM,iCAAiC,CAAC;AACvE,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,eAAe,GAAG,QAAQ,CAAC;AACxC,MAAM,CAAC,MAAM,gBAAgB,GAAG,SAAS,CAAC;AAE1C,MAAM,mBAAmB,GAAI,OAAO,CAAC,GAAW;KAC7C,+BAAqD,CAAC;AAEzD,MAAM,OAAO,GAAG,KAAK,CAAC,UAAU,CAAC,CAAC,KAAU,EAAE,GAAG,EAAE,EAAE;IACnD,OAAO,KAAK,CAAC,aAAa,CAAC,UAAU,EAAE;QACrC,GAAG;QACH,gBAAgB,EAAE,mBAAmB;QACrC,GAAG,KAAK;QACR,QAAQ,EAAE,eAAe;KAC1B,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,OAAO,CAAC,WAAW,GAAG,SAAS,CAAC;AAEhC,MAAM,CAAC,MAAM,CAAC,OAAO,EAAE;IACrB,GAAG,OAAO;IACV,eAAe;IACf,gBAAgB;CACjB,CAAC,CAAC;AAEH,MAAM,CAAC,MAAM,EACX,MAAM,EACN,OAAO,EACP,QAAQ,EACR,OAAO,EACP,MAAM,EACN,OAAO,EACP,OAAO,EACP,OAAO,EACP,OAAO,EACP,SAAS,GACV,GAAG,OAAO,CAAC;AAEZ,eAAe,OAAO,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Web polyfill for React Native RefreshControl
3
+ *
4
+ * Implements pull-to-refresh functionality using PanResponder on web.
5
+ */
6
+ import React from "react";
7
+ interface WebRefreshControlProps {
8
+ refreshing: boolean;
9
+ tintColor?: string;
10
+ colors?: string[];
11
+ style?: any;
12
+ progressViewOffset?: number;
13
+ children?: React.ReactNode;
14
+ onRefresh?: () => void;
15
+ enabled?: boolean;
16
+ }
17
+ export default function WebRefreshControl({ refreshing, tintColor, colors, style, progressViewOffset, children, onRefresh, enabled, }: WebRefreshControlProps): import("react/jsx-runtime").JSX.Element;
18
+ export {};
19
+ //# sourceMappingURL=refresh-control-component.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-control-component.d.ts","sourceRoot":"","sources":["../../src/polyfills/refresh-control-component.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAkD,MAAM,OAAO,CAAC;AAUvE,UAAU,sBAAsB;IAC9B,UAAU,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,KAAK,CAAC,EAAE,GAAG,CAAC;IACZ,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B,SAAS,CAAC,EAAE,MAAM,IAAI,CAAC;IACvB,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,EACxC,UAAU,EACV,SAAS,EACT,MAAM,EACN,KAAK,EACL,kBAAkB,EAClB,QAAQ,EACR,SAAS,EACT,OAAO,GACR,EAAE,sBAAsB,2CAoJxB"}
@@ -0,0 +1,120 @@
1
+ import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ /**
3
+ * Web polyfill for React Native RefreshControl
4
+ *
5
+ * Implements pull-to-refresh functionality using PanResponder on web.
6
+ */
7
+ // @ts-nocheck
8
+ import React, { useCallback, useEffect, useMemo, useRef } from "react";
9
+ // Import from react-native-web directly to avoid infinite loop
10
+ // (metro redirects "react-native" to our polyfill on web)
11
+ import { ActivityIndicator, Animated, PanResponder, View, } from "react-native-web";
12
+ export default function WebRefreshControl({ refreshing, tintColor, colors, style, progressViewOffset, children, onRefresh, enabled, }) {
13
+ const onRefreshRef = useRef(onRefresh);
14
+ useEffect(() => {
15
+ onRefreshRef.current = onRefresh;
16
+ }, [onRefresh]);
17
+ const enabledRef = useRef(enabled);
18
+ useEffect(() => {
19
+ enabledRef.current = enabled;
20
+ }, [enabled]);
21
+ const containerRef = useRef(null);
22
+ const pullPosReachedState = useRef(0);
23
+ const pullDownSwipeMargin = useRef(new Animated.Value(0));
24
+ useEffect(() => {
25
+ Animated.timing(pullDownSwipeMargin.current, {
26
+ toValue: refreshing ? 50 : 0,
27
+ duration: 350,
28
+ useNativeDriver: false,
29
+ }).start();
30
+ if (refreshing) {
31
+ pullPosReachedState.current = 0;
32
+ }
33
+ }, [refreshing]);
34
+ const onPanResponderFinish = useCallback(() => {
35
+ if (pullPosReachedState.current && onRefreshRef.current) {
36
+ onRefreshRef.current();
37
+ }
38
+ if (!pullPosReachedState.current) {
39
+ Animated.timing(pullDownSwipeMargin.current, {
40
+ toValue: 0,
41
+ duration: 350,
42
+ useNativeDriver: false,
43
+ }).start();
44
+ }
45
+ }, []);
46
+ const panResponder = useRef(PanResponder.create({
47
+ onStartShouldSetPanResponder: () => false,
48
+ onStartShouldSetPanResponderCapture: () => false,
49
+ onMoveShouldSetPanResponder: (_, gestureState) => {
50
+ if (!containerRef.current)
51
+ return false;
52
+ // React 19 fix: use direct DOM traversal instead of findNodeHandle
53
+ const scrollContainer = containerRef.current?.firstChild;
54
+ if (!scrollContainer)
55
+ return false;
56
+ return (scrollContainer.scrollTop === 0 &&
57
+ Math.abs(gestureState.dy) > Math.abs(gestureState.dx) * 2 &&
58
+ Math.abs(gestureState.vy) > Math.abs(gestureState.vx) * 2.5);
59
+ },
60
+ onMoveShouldSetPanResponderCapture: () => false,
61
+ onPanResponderMove: (_, gestureState) => {
62
+ if (enabledRef.current !== undefined && !enabledRef.current)
63
+ return;
64
+ const adjustedDy = gestureState.dy <= 0
65
+ ? 0
66
+ : (gestureState.dy * 150) / (gestureState.dy + 120); // Diminishing returns function
67
+ pullDownSwipeMargin.current.setValue(adjustedDy);
68
+ const newValue = adjustedDy > 45 ? 1 : 0;
69
+ if (newValue !== pullPosReachedState.current) {
70
+ pullPosReachedState.current = newValue;
71
+ }
72
+ },
73
+ onPanResponderTerminationRequest: () => true,
74
+ onPanResponderRelease: onPanResponderFinish,
75
+ onPanResponderTerminate: onPanResponderFinish,
76
+ }));
77
+ const refreshIndicatorColor = useMemo(() => (tintColor ? tintColor : colors && colors.length ? colors[0] : null), [colors, tintColor]);
78
+ const containerStyle = useMemo(() => [
79
+ style,
80
+ // @ts-ignore - overflowY is valid on web
81
+ { overflowY: "hidden", overflow: "hidden", paddingTop: progressViewOffset },
82
+ ], [progressViewOffset, style]);
83
+ const indicatorTransformStyle = useMemo(() => ({
84
+ alignSelf: "center",
85
+ justifyContent: "center",
86
+ alignItems: "center",
87
+ marginTop: -40,
88
+ height: 40,
89
+ transform: [{ translateY: pullDownSwipeMargin.current }],
90
+ }), []);
91
+ // This is messing with react-native-web's internal implementation
92
+ // Will probably break if anything changes on their end
93
+ const AnimatedContentContainer = useMemo(() => {
94
+ const childElement = children;
95
+ if (!childElement?.props?.children?.type)
96
+ return null;
97
+ return withAnimated((childProps) => React.createElement(childElement.props.children.type, childProps));
98
+ }, [children]);
99
+ const childElement = children;
100
+ const newContentContainerStyle = useMemo(() => [
101
+ childElement?.props?.children?.props?.style,
102
+ { transform: [{ translateY: pullDownSwipeMargin.current }] },
103
+ ], [childElement?.props?.children?.props?.style]);
104
+ if (!childElement || !AnimatedContentContainer) {
105
+ return _jsx(_Fragment, { children: children });
106
+ }
107
+ const newChildren = React.cloneElement(childElement, {}, _jsxs(_Fragment, { children: [_jsx(Animated.View, { style: indicatorTransformStyle, children: _jsx(ActivityIndicator, { color: refreshIndicatorColor || "#999999", size: "small" }) }), _jsx(AnimatedContentContainer, { ...childElement.props.children.props, style: newContentContainerStyle })] }));
108
+ return (_jsx(View, { ref: containerRef, style: containerStyle, ...panResponder.current.panHandlers, children: newChildren }));
109
+ }
110
+ function withAnimated(WrappedComponent) {
111
+ const displayName = WrappedComponent.displayName || WrappedComponent.name || "Component";
112
+ class WithAnimated extends React.Component {
113
+ render() {
114
+ return _jsx(WrappedComponent, { ...this.props });
115
+ }
116
+ }
117
+ WithAnimated.displayName = `WithAnimated(${displayName})`;
118
+ return Animated.createAnimatedComponent(WithAnimated);
119
+ }
120
+ //# sourceMappingURL=refresh-control-component.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refresh-control-component.js","sourceRoot":"","sources":["../../src/polyfills/refresh-control-component.tsx"],"names":[],"mappings":";AAAA;;;;GAIG;AAEH,cAAc;AACd,OAAO,KAAK,EAAE,EAAE,WAAW,EAAE,SAAS,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,OAAO,CAAC;AACvE,+DAA+D;AAC/D,0DAA0D;AAC1D,OAAO,EACL,iBAAiB,EACjB,QAAQ,EACR,YAAY,EACZ,IAAI,GACL,MAAM,kBAAkB,CAAC;AAa1B,MAAM,CAAC,OAAO,UAAU,iBAAiB,CAAC,EACxC,UAAU,EACV,SAAS,EACT,MAAM,EACN,KAAK,EACL,kBAAkB,EAClB,QAAQ,EACR,SAAS,EACT,OAAO,GACgB;IACvB,MAAM,YAAY,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC;IACvC,SAAS,CAAC,GAAG,EAAE;QACb,YAAY,CAAC,OAAO,GAAG,SAAS,CAAC;IACnC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC;IAEhB,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IACnC,SAAS,CAAC,GAAG,EAAE;QACb,UAAU,CAAC,OAAO,GAAG,OAAO,CAAC;IAC/B,CAAC,EAAE,CAAC,OAAO,CAAC,CAAC,CAAC;IAEd,MAAM,YAAY,GAAG,MAAM,CAAM,IAAI,CAAC,CAAC;IACvC,MAAM,mBAAmB,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;IACtC,MAAM,mBAAmB,GAAG,MAAM,CAAC,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;IAE1D,SAAS,CAAC,GAAG,EAAE;QACb,QAAQ,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE;YAC3C,OAAO,EAAE,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YAC5B,QAAQ,EAAE,GAAG;YACb,eAAe,EAAE,KAAK;SACvB,CAAC,CAAC,KAAK,EAAE,CAAC;QACX,IAAI,UAAU,EAAE,CAAC;YACf,mBAAmB,CAAC,OAAO,GAAG,CAAC,CAAC;QAClC,CAAC;IACH,CAAC,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;IAEjB,MAAM,oBAAoB,GAAG,WAAW,CAAC,GAAG,EAAE;QAC5C,IAAI,mBAAmB,CAAC,OAAO,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;YACxD,YAAY,CAAC,OAAO,EAAE,CAAC;QACzB,CAAC;QACD,IAAI,CAAC,mBAAmB,CAAC,OAAO,EAAE,CAAC;YACjC,QAAQ,CAAC,MAAM,CAAC,mBAAmB,CAAC,OAAO,EAAE;gBAC3C,OAAO,EAAE,CAAC;gBACV,QAAQ,EAAE,GAAG;gBACb,eAAe,EAAE,KAAK;aACvB,CAAC,CAAC,KAAK,EAAE,CAAC;QACb,CAAC;IACH,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,MAAM,YAAY,GAAG,MAAM,CACzB,YAAY,CAAC,MAAM,CAAC;QAClB,4BAA4B,EAAE,GAAG,EAAE,CAAC,KAAK;QACzC,mCAAmC,EAAE,GAAG,EAAE,CAAC,KAAK;QAChD,2BAA2B,EAAE,CAAC,CAAC,EAAE,YAAY,EAAE,EAAE;YAC/C,IAAI,CAAC,YAAY,CAAC,OAAO;gBAAE,OAAO,KAAK,CAAC;YACxC,mEAAmE;YACnE,MAAM,eAAe,GAAG,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC;YACzD,IAAI,CAAC,eAAe;gBAAE,OAAO,KAAK,CAAC;YACnC,OAAO,CACL,eAAe,CAAC,SAAS,KAAK,CAAC;gBAC/B,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,CAAC;gBACzD,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,CAAC,GAAG,GAAG,CAC5D,CAAC;QACJ,CAAC;QACD,kCAAkC,EAAE,GAAG,EAAE,CAAC,KAAK;QAC/C,kBAAkB,EAAE,CAAC,CAAC,EAAE,YAAY,EAAE,EAAE;YACtC,IAAI,UAAU,CAAC,OAAO,KAAK,SAAS,IAAI,CAAC,UAAU,CAAC,OAAO;gBAAE,OAAO;YACpE,MAAM,UAAU,GACd,YAAY,CAAC,EAAE,IAAI,CAAC;gBAClB,CAAC,CAAC,CAAC;gBACH,CAAC,CAAC,CAAC,YAAY,CAAC,EAAE,GAAG,GAAG,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,GAAG,GAAG,CAAC,CAAC,CAAC,+BAA+B;YACxF,mBAAmB,CAAC,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;YACjD,MAAM,QAAQ,GAAG,UAAU,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACzC,IAAI,QAAQ,KAAK,mBAAmB,CAAC,OAAO,EAAE,CAAC;gBAC7C,mBAAmB,CAAC,OAAO,GAAG,QAAQ,CAAC;YACzC,CAAC;QACH,CAAC;QACD,gCAAgC,EAAE,GAAG,EAAE,CAAC,IAAI;QAC5C,qBAAqB,EAAE,oBAAoB;QAC3C,uBAAuB,EAAE,oBAAoB;KAC9C,CAAC,CACH,CAAC;IAEF,MAAM,qBAAqB,GAAG,OAAO,CACnC,GAAG,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,EAC1E,CAAC,MAAM,EAAE,SAAS,CAAC,CACpB,CAAC;IAEF,MAAM,cAAc,GAAG,OAAO,CAC5B,GAAG,EAAE,CAAC;QACJ,KAAK;QACL,yCAAyC;QACzC,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,kBAAkB,EAAE;KAC5E,EACD,CAAC,kBAAkB,EAAE,KAAK,CAAC,CAC5B,CAAC;IAEF,MAAM,uBAAuB,GAAG,OAAO,CACrC,GAAG,EAAE,CAAC,CAAC;QACL,SAAS,EAAE,QAAiB;QAC5B,cAAc,EAAE,QAAiB;QACjC,UAAU,EAAE,QAAiB;QAC7B,SAAS,EAAE,CAAC,EAAE;QACd,MAAM,EAAE,EAAE;QACV,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,mBAAmB,CAAC,OAAO,EAAE,CAAC;KACzD,CAAC,EACF,EAAE,CACH,CAAC;IAEF,kEAAkE;IAClE,uDAAuD;IACvD,MAAM,wBAAwB,GAAG,OAAO,CAAC,GAAG,EAAE;QAC5C,MAAM,YAAY,GAAG,QAAe,CAAC;QACrC,IAAI,CAAC,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,IAAI;YAAE,OAAO,IAAI,CAAC;QACtD,OAAO,YAAY,CAAC,CAAC,UAAe,EAAE,EAAE,CACtC,KAAK,CAAC,aAAa,CAAC,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC,CAClE,CAAC;IACJ,CAAC,EAAE,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEf,MAAM,YAAY,GAAG,QAAe,CAAC;IACrC,MAAM,wBAAwB,GAAG,OAAO,CACtC,GAAG,EAAE,CAAC;QACJ,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK;QAC3C,EAAE,SAAS,EAAE,CAAC,EAAE,UAAU,EAAE,mBAAmB,CAAC,OAAO,EAAE,CAAC,EAAE;KAC7D,EACD,CAAC,YAAY,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,CAC9C,CAAC;IAEF,IAAI,CAAC,YAAY,IAAI,CAAC,wBAAwB,EAAE,CAAC;QAC/C,OAAO,4BAAG,QAAQ,GAAI,CAAC;IACzB,CAAC;IAED,MAAM,WAAW,GAAG,KAAK,CAAC,YAAY,CACpC,YAAY,EACZ,EAAE,EACF,8BACE,KAAC,QAAQ,CAAC,IAAI,IAAC,KAAK,EAAE,uBAAuB,YAC3C,KAAC,iBAAiB,IAChB,KAAK,EAAE,qBAAqB,IAAI,SAAS,EACzC,IAAI,EAAC,OAAO,GACZ,GACY,EAChB,KAAC,wBAAwB,OACnB,YAAY,CAAC,KAAK,CAAC,QAAQ,CAAC,KAAK,EACrC,KAAK,EAAE,wBAAwB,GAC/B,IACD,CACJ,CAAC;IAEF,OAAO,CACL,KAAC,IAAI,IACH,GAAG,EAAE,YAAY,EACjB,KAAK,EAAE,cAAc,KACjB,YAAY,CAAC,OAAO,CAAC,WAAW,YAEnC,WAAW,GACP,CACR,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,gBAA0C;IAC9D,MAAM,WAAW,GACf,gBAAgB,CAAC,WAAW,IAAI,gBAAgB,CAAC,IAAI,IAAI,WAAW,CAAC;IAEvE,MAAM,YAAa,SAAQ,KAAK,CAAC,SAAc;QAG7C,MAAM;YACJ,OAAO,KAAC,gBAAgB,OAAK,IAAI,CAAC,KAAK,GAAI,CAAC;QAC9C,CAAC;;IAJM,wBAAW,GAAG,gBAAgB,WAAW,GAAG,CAAC;IAOtD,OAAO,QAAQ,CAAC,uBAAuB,CAAC,YAAY,CAAC,CAAC;AACxD,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Web polyfill for expo-secure-store using localStorage
3
+ *
4
+ * Note: localStorage is not truly "secure" on web, but provides similar API.
5
+ */
6
+ export type KeychainAccessibilityConstant = number;
7
+ export declare const AFTER_FIRST_UNLOCK: number, AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY: number, ALWAYS: number, WHEN_PASSCODE_SET_THIS_DEVICE_ONLY: number, ALWAYS_THIS_DEVICE_ONLY: number, WHEN_UNLOCKED: number, WHEN_UNLOCKED_THIS_DEVICE_ONLY: number;
8
+ export type SecureStoreOptions = {
9
+ keychainService?: string;
10
+ requireAuthentication?: boolean;
11
+ authenticationPrompt?: string;
12
+ keychainAccessible?: KeychainAccessibilityConstant;
13
+ };
14
+ export declare function isAvailableAsync(): Promise<boolean>;
15
+ export declare function deleteItemAsync(key: string, _options?: SecureStoreOptions): Promise<void>;
16
+ export declare function getItemAsync(key: string, _options?: SecureStoreOptions): Promise<string | null>;
17
+ export declare function setItemAsync(key: string, value: string, _options?: SecureStoreOptions): Promise<void>;
18
+ export declare function setItem(key: string, value: string, _options?: SecureStoreOptions): void;
19
+ export declare function getItem(key: string, _options?: SecureStoreOptions): string | null;
20
+ export declare function canUseBiometricAuthentication(): boolean;
21
+ //# sourceMappingURL=secure-store.web.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secure-store.web.d.ts","sourceRoot":"","sources":["../../src/polyfills/secure-store.web.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAcH,MAAM,MAAM,6BAA6B,GAAG,MAAM,CAAC;AAEnD,eAAO,MACL,kBAAkB,UAClB,mCAAmC,UACnC,MAAM,UACN,kCAAkC,UAClC,uBAAuB,UACvB,aAAa,UACb,8BAA8B,QACV,CAAC;AAEvB,MAAM,MAAM,kBAAkB,GAAG;IAC/B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,qBAAqB,CAAC,EAAE,OAAO,CAAC;IAChC,oBAAoB,CAAC,EAAE,MAAM,CAAC;IAC9B,kBAAkB,CAAC,EAAE,6BAA6B,CAAC;CACpD,CAAC;AAkBF,wBAAsB,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC,CAYzD;AAED,wBAAsB,eAAe,CACnC,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,kBAAuB,GAChC,OAAO,CAAC,IAAI,CAAC,CAEf;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,kBAAuB,GAChC,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAExB;AAED,wBAAsB,YAAY,CAChC,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,kBAAuB,GAChC,OAAO,CAAC,IAAI,CAAC,CAOf;AAED,wBAAgB,OAAO,CACrB,GAAG,EAAE,MAAM,EACX,KAAK,EAAE,MAAM,EACb,QAAQ,GAAE,kBAAuB,GAChC,IAAI,CAON;AAED,wBAAgB,OAAO,CACrB,GAAG,EAAE,MAAM,EACX,QAAQ,GAAE,kBAAuB,GAChC,MAAM,GAAG,IAAI,CAEf;AAED,wBAAgB,6BAA6B,IAAI,OAAO,CAEvD"}
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Web polyfill for expo-secure-store using localStorage
3
+ *
4
+ * Note: localStorage is not truly "secure" on web, but provides similar API.
5
+ */
6
+ const VALUE_BYTES_LIMIT = 2048;
7
+ const KEYCHAIN_CONSTANTS = {
8
+ AFTER_FIRST_UNLOCK: 0,
9
+ AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY: 1,
10
+ ALWAYS: 2,
11
+ WHEN_PASSCODE_SET_THIS_DEVICE_ONLY: 3,
12
+ ALWAYS_THIS_DEVICE_ONLY: 4,
13
+ WHEN_UNLOCKED: 5,
14
+ WHEN_UNLOCKED_THIS_DEVICE_ONLY: 6,
15
+ };
16
+ export const { AFTER_FIRST_UNLOCK, AFTER_FIRST_UNLOCK_THIS_DEVICE_ONLY, ALWAYS, WHEN_PASSCODE_SET_THIS_DEVICE_ONLY, ALWAYS_THIS_DEVICE_ONLY, WHEN_UNLOCKED, WHEN_UNLOCKED_THIS_DEVICE_ONLY, } = KEYCHAIN_CONSTANTS;
17
+ function isValidValue(value) {
18
+ if (typeof value !== "string") {
19
+ return false;
20
+ }
21
+ if (new Blob([value]).size > VALUE_BYTES_LIMIT) {
22
+ console.warn(`Value being stored in SecureStore is larger than ${VALUE_BYTES_LIMIT} bytes and it may not be stored successfully.`);
23
+ }
24
+ return true;
25
+ }
26
+ function getStorageKey(key) {
27
+ return `_vibecode_secure_store_${key}`;
28
+ }
29
+ export async function isAvailableAsync() {
30
+ const testKey = "__SECURE_STORE_AVAILABILITY_TEST_KEY__";
31
+ try {
32
+ localStorage.setItem(testKey, "test");
33
+ if (localStorage.getItem(testKey) !== "test") {
34
+ return false;
35
+ }
36
+ localStorage.removeItem(testKey);
37
+ return localStorage.getItem(testKey) === null;
38
+ }
39
+ catch {
40
+ return false;
41
+ }
42
+ }
43
+ export async function deleteItemAsync(key, _options = {}) {
44
+ localStorage.removeItem(getStorageKey(key));
45
+ }
46
+ export async function getItemAsync(key, _options = {}) {
47
+ return localStorage.getItem(getStorageKey(key));
48
+ }
49
+ export async function setItemAsync(key, value, _options = {}) {
50
+ if (!isValidValue(value)) {
51
+ throw new Error("Invalid value provided to SecureStore. Values must be strings; consider JSON-encoding your values if they are serializable.");
52
+ }
53
+ localStorage.setItem(getStorageKey(key), value);
54
+ }
55
+ export function setItem(key, value, _options = {}) {
56
+ if (!isValidValue(value)) {
57
+ throw new Error("Invalid value provided to SecureStore. Values must be strings; consider JSON-encoding your values if they are serializable.");
58
+ }
59
+ localStorage.setItem(getStorageKey(key), value);
60
+ }
61
+ export function getItem(key, _options = {}) {
62
+ return localStorage.getItem(getStorageKey(key));
63
+ }
64
+ export function canUseBiometricAuthentication() {
65
+ return false;
66
+ }
67
+ //# sourceMappingURL=secure-store.web.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"secure-store.web.js","sourceRoot":"","sources":["../../src/polyfills/secure-store.web.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAE/B,MAAM,kBAAkB,GAAG;IACzB,kBAAkB,EAAE,CAAC;IACrB,mCAAmC,EAAE,CAAC;IACtC,MAAM,EAAE,CAAC;IACT,kCAAkC,EAAE,CAAC;IACrC,uBAAuB,EAAE,CAAC;IAC1B,aAAa,EAAE,CAAC;IAChB,8BAA8B,EAAE,CAAC;CAClC,CAAC;AAIF,MAAM,CAAC,MAAM,EACX,kBAAkB,EAClB,mCAAmC,EACnC,MAAM,EACN,kCAAkC,EAClC,uBAAuB,EACvB,aAAa,EACb,8BAA8B,GAC/B,GAAG,kBAAkB,CAAC;AASvB,SAAS,YAAY,CAAC,KAAa;IACjC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC;IACf,CAAC;IACD,IAAI,IAAI,IAAI,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,IAAI,GAAG,iBAAiB,EAAE,CAAC;QAC/C,OAAO,CAAC,IAAI,CACV,oDAAoD,iBAAiB,+CAA+C,CACrH,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,aAAa,CAAC,GAAW;IAChC,OAAO,0BAA0B,GAAG,EAAE,CAAC;AACzC,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,gBAAgB;IACpC,MAAM,OAAO,GAAG,wCAAwC,CAAC;IACzD,IAAI,CAAC;QACH,YAAY,CAAC,OAAO,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC;QACtC,IAAI,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,MAAM,EAAE,CAAC;YAC7C,OAAO,KAAK,CAAC;QACf,CAAC;QACD,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;QACjC,OAAO,YAAY,CAAC,OAAO,CAAC,OAAO,CAAC,KAAK,IAAI,CAAC;IAChD,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,KAAK,CAAC;IACf,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CACnC,GAAW,EACX,WAA+B,EAAE;IAEjC,YAAY,CAAC,UAAU,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAW,EACX,WAA+B,EAAE;IAEjC,OAAO,YAAY,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAChC,GAAW,EACX,KAAa,EACb,WAA+B,EAAE;IAEjC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,6HAA6H,CAC9H,CAAC;IACJ,CAAC;IACD,YAAY,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,GAAW,EACX,KAAa,EACb,WAA+B,EAAE;IAEjC,IAAI,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,KAAK,CACb,6HAA6H,CAC9H,CAAC;IACJ,CAAC;IACD,YAAY,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,EAAE,KAAK,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,OAAO,CACrB,GAAW,EACX,WAA+B,EAAE;IAEjC,OAAO,YAAY,CAAC,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC;AAClD,CAAC;AAED,MAAM,UAAU,6BAA6B;IAC3C,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -0,0 +1,151 @@
1
+ const path = require("node:path");
2
+ const os = require("node:os");
3
+ const { FileStore } = require("metro-cache");
4
+ const { HttpStore } = require("./metro-http-store.cjs");
5
+
6
+ /**
7
+ * Metro configuration helper for Vibecode
8
+ * Provides web polyfills and HTTP cache store for remote caching.
9
+ */
10
+
11
+ const distPath = path.resolve(__dirname, "../dist/");
12
+
13
+ /**
14
+ * Create cache stores array for Metro config.
15
+ *
16
+ * @param {Object} options
17
+ * @param {string} [options.cacheDir] - Local cache directory (default: ~/.metro-cache)
18
+ * @param {string} [options.httpEndpoint] - HTTP/2 cache server endpoint (optional)
19
+ * @param {number} [options.httpTimeout=30000] - HTTP cache timeout in ms
20
+ * @param {number} [options.httpMaxConnections=64] - Max HTTP/2 connections
21
+ * @param {number} [options.httpMaxConcurrent=256] - Max concurrent HTTP requests
22
+ * @returns {Array} Array of cache store instances
23
+ */
24
+ function createCacheStores(options = {}) {
25
+ const cacheDir =
26
+ options.cacheDir ||
27
+ process.env.METRO_CACHE_DIR ||
28
+ path.join(os.homedir(), ".metro-cache");
29
+
30
+ const httpEndpoint = options.httpEndpoint || process.env.METRO_CACHE_HTTP_ENDPOINT;
31
+
32
+ const stores = [new FileStore({ root: cacheDir })];
33
+
34
+ if (httpEndpoint) {
35
+ stores.push(
36
+ new HttpStore({
37
+ endpoint: httpEndpoint,
38
+ timeout: options.httpTimeout ?? 30000,
39
+ maxConnections: options.httpMaxConnections ?? 64,
40
+ maxConcurrent: options.httpMaxConcurrent ?? 256,
41
+ })
42
+ );
43
+ }
44
+
45
+ return stores;
46
+ }
47
+
48
+ /**
49
+ * Wrap a Metro config with Vibecode enhancements.
50
+ *
51
+ * Features:
52
+ * - Web polyfills for expo-haptics, expo-secure-store, react-native-maps, etc.
53
+ * - Optional HTTP/2 remote cache store
54
+ * - Configurable cache settings
55
+ *
56
+ * @param {Object} config - Base Metro config
57
+ * @param {Object} [options]
58
+ * @param {boolean} [options.enableCache=true] - Enable cache configuration
59
+ * @param {string} [options.cacheDir] - Local cache directory
60
+ * @param {string} [options.cacheVersion] - Cache version string
61
+ * @param {string} [options.httpEndpoint] - HTTP/2 cache server endpoint
62
+ * @param {number} [options.httpTimeout] - HTTP cache timeout
63
+ * @param {number} [options.httpMaxConnections] - Max HTTP/2 connections
64
+ * @param {number} [options.httpMaxConcurrent] - Max concurrent requests
65
+ * @returns {Object} Enhanced Metro config
66
+ */
67
+ function withVibecodeMetro(config, options = {}) {
68
+ const originalResolveRequest = config.resolver?.resolveRequest;
69
+ const enableCache = options.enableCache !== false;
70
+
71
+ const result = {
72
+ ...config,
73
+ resolver: {
74
+ ...config.resolver,
75
+ extraNodeModules: {
76
+ assert: require.resolve("assert"),
77
+ ...config.resolver?.extraNodeModules,
78
+ },
79
+ resolveRequest: (context, moduleName, platform) => {
80
+ // Redirect expo-haptics to our web polyfill on web platform
81
+ if (moduleName === "expo-haptics" && platform === "web") {
82
+ return {
83
+ filePath: path.resolve(distPath, "polyfills/haptics.web.js"),
84
+ type: "sourceFile",
85
+ };
86
+ }
87
+
88
+ if (moduleName === "expo-secure-store" && platform === "web") {
89
+ return {
90
+ filePath: path.resolve(distPath, "polyfills/secure-store.web.js"),
91
+ type: "sourceFile",
92
+ };
93
+ }
94
+
95
+ if (moduleName === "react-native-maps" && platform === "web") {
96
+ return {
97
+ filePath: path.resolve(distPath, "polyfills/maps.web.js"),
98
+ type: "sourceFile",
99
+ };
100
+ }
101
+
102
+ if (
103
+ moduleName === "react-native-web/dist/exports/RefreshControl" &&
104
+ platform === "web"
105
+ ) {
106
+ return {
107
+ filePath: path.resolve(
108
+ distPath,
109
+ "polyfills/refresh-control-component.js"
110
+ ),
111
+ type: "sourceFile",
112
+ };
113
+ }
114
+
115
+ if (
116
+ moduleName === "react-native-web/dist/exports/Alert" &&
117
+ platform === "web"
118
+ ) {
119
+ return {
120
+ filePath: path.resolve(distPath, "polyfills/alert.web.js"),
121
+ type: "sourceFile",
122
+ };
123
+ }
124
+
125
+ if (originalResolveRequest) {
126
+ return originalResolveRequest(context, moduleName, platform);
127
+ }
128
+
129
+ return context.resolveRequest(context, moduleName, platform);
130
+ },
131
+ },
132
+ };
133
+
134
+ // Configure caching if enabled
135
+ if (enableCache) {
136
+ result.cacheStores = createCacheStores({
137
+ cacheDir: options.cacheDir,
138
+ httpEndpoint: options.httpEndpoint,
139
+ httpTimeout: options.httpTimeout,
140
+ httpMaxConnections: options.httpMaxConnections,
141
+ httpMaxConcurrent: options.httpMaxConcurrent,
142
+ });
143
+
144
+ result.cacheVersion =
145
+ options.cacheVersion || process.env.METRO_CACHE_VERSION || "1";
146
+ }
147
+
148
+ return result;
149
+ }
150
+
151
+ module.exports = { withVibecodeMetro };
@@ -0,0 +1,26 @@
1
+ import type { MetroConfig } from "metro-config";
2
+
3
+ export interface VibecodeMetroOptions {
4
+ /** Enable cache configuration (default: true) */
5
+ enableCache?: boolean;
6
+ /** Local cache directory (default: ~/.metro-cache) */
7
+ cacheDir?: string;
8
+ /** Cache version string (default: "1") */
9
+ cacheVersion?: string;
10
+ /** HTTP/2 cache server endpoint */
11
+ httpEndpoint?: string;
12
+ /** HTTP request timeout in ms (default: 30000) */
13
+ httpTimeout?: number;
14
+ /** HTTP/2 connection pool size (default: 64) */
15
+ httpMaxConnections?: number;
16
+ /** Max concurrent HTTP requests (default: 256) */
17
+ httpMaxConcurrent?: number;
18
+ }
19
+
20
+ /**
21
+ * Wrap a Metro config with Vibecode enhancements.
22
+ */
23
+ export function withVibecodeMetro(
24
+ config: MetroConfig,
25
+ options?: VibecodeMetroOptions
26
+ ): MetroConfig;
@@ -0,0 +1,274 @@
1
+ /**
2
+ * HTTP/2 Metro Cache Store
3
+ *
4
+ * A Metro cache store implementation using HTTP/2 with:
5
+ * - Connection pool (default 8 connections) for higher throughput
6
+ * - Multiplexed requests over each connection
7
+ * - Concurrency limiting (configurable max concurrent requests)
8
+ * - Non-blocking writes (fire-and-forget)
9
+ * - Blocking reads
10
+ *
11
+ * Requires an HTTP/2 server - will fail on HTTP/1.1.
12
+ */
13
+
14
+ const http2 = require("node:http2");
15
+ const zlib = require("node:zlib");
16
+ const { Buffer } = require("node:buffer");
17
+ const pLimit = require("p-limit");
18
+
19
+ const NULL_BYTE = 0x00;
20
+ const NULL_BYTE_BUFFER = Buffer.from([NULL_BYTE]);
21
+
22
+ const ZLIB_OPTIONS = {
23
+ level: 9,
24
+ };
25
+
26
+ class HttpStore {
27
+ #origin;
28
+ #path;
29
+ #timeout;
30
+ #sessions = [];
31
+ #maxConnections;
32
+ #nextSession = 0;
33
+ #limit;
34
+
35
+ /**
36
+ * @param {Object} options
37
+ * @param {string} options.endpoint - Full URL to the cache server (e.g., "https://cache.example.com/metro")
38
+ * @param {number} [options.timeout=10000] - Request timeout in milliseconds
39
+ * @param {number} [options.maxConnections=8] - Number of HTTP/2 connections in pool
40
+ * @param {number} [options.maxConcurrent=64] - Max concurrent requests in flight
41
+ */
42
+ constructor(options) {
43
+ const url = new URL(options.endpoint);
44
+ this.#origin = url.origin;
45
+ this.#path = url.pathname.replace(/\/$/, ""); // Remove trailing slash
46
+ this.#timeout = options.timeout ?? 10000;
47
+ this.#maxConnections = options.maxConnections ?? 8;
48
+ this.#limit = pLimit(options.maxConcurrent ?? 64);
49
+ }
50
+
51
+ /**
52
+ * Get an HTTP/2 session from the pool using round-robin.
53
+ * Lazily creates connections on first use.
54
+ * Never throws - returns null if connection fails.
55
+ */
56
+ #getSession() {
57
+ // Round-robin through connections
58
+ const index = this.#nextSession++ % this.#maxConnections;
59
+
60
+ // Check if we have a valid session at this index
61
+ const existing = this.#sessions[index];
62
+ if (existing && !existing.closed && !existing.destroyed) {
63
+ return existing;
64
+ }
65
+
66
+ // Create new session for this slot
67
+ try {
68
+ const session = http2.connect(this.#origin);
69
+
70
+ session.on("error", (err) => {
71
+ console.warn("[HttpStore] Session error:", err.message);
72
+ if (this.#sessions[index] === session) {
73
+ this.#sessions[index] = null;
74
+ }
75
+ });
76
+
77
+ session.on("close", () => {
78
+ if (this.#sessions[index] === session) {
79
+ this.#sessions[index] = null;
80
+ }
81
+ });
82
+
83
+ this.#sessions[index] = session;
84
+ return session;
85
+ } catch (err) {
86
+ console.warn("[HttpStore] Failed to connect:", err.message);
87
+ return null;
88
+ }
89
+ }
90
+
91
+ /**
92
+ * GET - Blocking read from cache.
93
+ * Returns null on any error (network, timeout, parse) - never throws.
94
+ * @param {Buffer} key
95
+ * @returns {Promise<any|null>}
96
+ */
97
+ async get(key) {
98
+ try {
99
+ return await this.#limit(() => this.#doGet(key));
100
+ } catch (err) {
101
+ console.warn("[HttpStore] Get failed:", err.message);
102
+ return null; // Fall back to next cache store (FileStore)
103
+ }
104
+ }
105
+
106
+ #doGet(key) {
107
+ return new Promise((resolve, reject) => {
108
+ const session = this.#getSession();
109
+ if (!session) {
110
+ reject(new Error("No session available"));
111
+ return;
112
+ }
113
+
114
+ const path = `${this.#path}/${key.toString("hex")}`;
115
+ const req = session.request({
116
+ ":method": "GET",
117
+ ":path": path,
118
+ });
119
+
120
+ const timeoutId = setTimeout(() => {
121
+ req.close(http2.constants.NGHTTP2_CANCEL);
122
+ reject(new Error("Request timed out"));
123
+ }, this.#timeout);
124
+
125
+ const chunks = [];
126
+
127
+ req.on("response", (headers) => {
128
+ const status = headers[":status"];
129
+
130
+ // 404 = cache miss
131
+ if (status === 404) {
132
+ clearTimeout(timeoutId);
133
+ req.close();
134
+ resolve(null);
135
+ return;
136
+ }
137
+
138
+ // Non-2xx = error
139
+ if (status < 200 || status >= 300) {
140
+ clearTimeout(timeoutId);
141
+ req.close();
142
+ reject(new Error(`HTTP error: ${status}`));
143
+ return;
144
+ }
145
+ });
146
+
147
+ req.on("data", (chunk) => {
148
+ chunks.push(chunk);
149
+ });
150
+
151
+ req.on("end", () => {
152
+ clearTimeout(timeoutId);
153
+
154
+ const compressed = Buffer.concat(chunks);
155
+ if (compressed.length === 0) {
156
+ resolve(null);
157
+ return;
158
+ }
159
+
160
+ // Decompress the response
161
+ zlib.gunzip(compressed, (err, buffer) => {
162
+ if (err) {
163
+ reject(err);
164
+ return;
165
+ }
166
+
167
+ try {
168
+ // If first byte is NULL_BYTE, it's a raw Buffer
169
+ if (buffer.length > 0 && buffer[0] === NULL_BYTE) {
170
+ resolve(buffer.subarray(1));
171
+ } else {
172
+ resolve(JSON.parse(buffer.toString("utf8")));
173
+ }
174
+ } catch (parseErr) {
175
+ reject(parseErr);
176
+ }
177
+ });
178
+ });
179
+
180
+ req.on("error", (err) => {
181
+ clearTimeout(timeoutId);
182
+ reject(err);
183
+ });
184
+
185
+ req.end();
186
+ });
187
+ }
188
+
189
+ /**
190
+ * SET - Non-blocking write to cache.
191
+ * Returns immediately, HTTP request happens in background.
192
+ * @param {Buffer} key
193
+ * @param {any} value
194
+ * @returns {Promise<void>}
195
+ */
196
+ set(key, value) {
197
+ // Fire and forget - queue the work but return immediately
198
+ this.#limit(() => this.#doSet(key, value)).catch((err) => {
199
+ console.warn("[HttpStore] Background write failed:", err.message);
200
+ });
201
+
202
+ return Promise.resolve();
203
+ }
204
+
205
+ #doSet(key, value) {
206
+ return new Promise((resolve, reject) => {
207
+ const session = this.#getSession();
208
+ if (!session) {
209
+ reject(new Error("No session available"));
210
+ return;
211
+ }
212
+
213
+ const path = `${this.#path}/${key.toString("hex")}`;
214
+
215
+ // Prepare data to compress
216
+ let data;
217
+ if (Buffer.isBuffer(value)) {
218
+ // Prepend NULL_BYTE marker for Buffer values
219
+ data = Buffer.concat([NULL_BYTE_BUFFER, value]);
220
+ } else {
221
+ data = Buffer.from(JSON.stringify(value) ?? "null", "utf8");
222
+ }
223
+
224
+ // Compress the data
225
+ zlib.gzip(data, ZLIB_OPTIONS, (err, compressed) => {
226
+ if (err) {
227
+ reject(err);
228
+ return;
229
+ }
230
+
231
+ const req = session.request({
232
+ ":method": "PUT",
233
+ ":path": path,
234
+ "content-type": "application/octet-stream",
235
+ "content-length": compressed.length,
236
+ });
237
+
238
+ const timeoutId = setTimeout(() => {
239
+ req.close(http2.constants.NGHTTP2_CANCEL);
240
+ reject(new Error("Request timed out"));
241
+ }, this.#timeout);
242
+
243
+ req.on("response", (headers) => {
244
+ const status = headers[":status"];
245
+
246
+ clearTimeout(timeoutId);
247
+
248
+ if (status >= 200 && status < 300) {
249
+ resolve();
250
+ } else {
251
+ reject(new Error(`HTTP error: ${status}`));
252
+ }
253
+ });
254
+
255
+ req.on("error", (err) => {
256
+ clearTimeout(timeoutId);
257
+ reject(err);
258
+ });
259
+
260
+ req.write(compressed);
261
+ req.end();
262
+ });
263
+ });
264
+ }
265
+
266
+ /**
267
+ * CLEAR - Not implemented.
268
+ */
269
+ clear() {
270
+ // No-op - Metro's HTTP cache protocol doesn't define a clear operation
271
+ }
272
+ }
273
+
274
+ module.exports = { HttpStore };
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "name": "@vibecodeapp/sdk",
3
+ "version": "0.1.0",
4
+ "description": "SDK for Vibecodeapp.com - Web polyfills and Metro configuration for React Native",
5
+ "main": "./dist/index.js",
6
+ "types": "./dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ },
12
+ "./package.json": "./package.json",
13
+ "./metro": {
14
+ "default": "./metro/index.cjs"
15
+ }
16
+ },
17
+ "files": [
18
+ "dist",
19
+ "metro"
20
+ ],
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "git+https://github.com/vibecode/vibecode-sdk.git"
24
+ },
25
+ "homepage": "https://github.com/vibecode/vibecode-sdk#readme",
26
+ "bugs": {
27
+ "url": "https://github.com/vibecode/vibecode-sdk/issues"
28
+ },
29
+ "license": "MIT",
30
+ "scripts": {
31
+ "build": "tsc",
32
+ "lint": "biome check .",
33
+ "lint:fix": "biome check --write .",
34
+ "type-check": "tsc --noEmit",
35
+ "clean": "rm -rf dist",
36
+ "prepublishOnly": "npm run clean && npm run build"
37
+ },
38
+ "dependencies": {
39
+ "@blazejkustra/react-native-alert": "^1.0.0",
40
+ "@teovilla/react-native-web-maps": "^0.9.5",
41
+ "@stardazed/streams-text-encoding": "^1.0.2",
42
+ "assert": "^2.1.0",
43
+ "p-limit": "^3.1.0"
44
+ },
45
+ "devDependencies": {
46
+ "@biomejs/biome": "^2.3.8",
47
+ "@types/bun": "^1.3.4",
48
+ "@types/react": "19.1.11",
49
+ "react": "19.1.0",
50
+ "react-native": "0.81.4",
51
+ "react-native-web": "^0.21.2",
52
+ "typescript": "^5.9.3"
53
+ },
54
+ "peerDependencies": {
55
+ "react": "*",
56
+ "react-native": "*",
57
+ "react-native-web": "*",
58
+ "metro-cache": "*"
59
+ },
60
+ "type": "module"
61
+ }