Skip to content

Commit 0b373c7

Browse files
authored
feat: SQLite agent tools, web-ready storage, and Rozenite for Web docs (#228)
## Summary This PR adds SQLite agent tooling, improves how official plugins behave on web, refreshes documentation for [Rozenite for Web](https://rozenite.dev/docs/rozenite-for-web), and replaces the playground webpack web screen with plugin-focused sections. ### SQLite plugin - **Agents:** LLM workflows can list registered databases and run SQL (including multi-statement scripts) via new agent tools wired from `useRozeniteSqlitePlugin`. - **Web:** The SQLite plugin runtime is **not** loaded in browser builds for now, because of upstream `expo-sqlite` issues on web. Documentation states that SQLite in DevTools is for iOS and Android until upstream stabilizes. ### Storage plugin - **Web:** In development, Async Storage and Expo Secure Store adapters work in the inspector on web; the MMKV adapter stays an empty inspector on web because MMKV does not run in the browser. ### Playground (web) - The webpack web entry no longer uses the JSONPlaceholder smoke test. It lists supported plugins and wires the corresponding hooks (controls, overlay, performance monitor, React Navigation, Redux, TanStack Query, etc.). SQLite is shown as disabled on web only. - Metro config adds WASM asset resolution and COEP/COOP headers where needed for web SQLite experiments elsewhere. ### Documentation - Official plugin pages note Rozenite for Web where the plugin runs in the browser in development. - SQLite docs describe agent tools and the current web limitation. ## Test plan - [x] `pnpm` typecheck / lint for touched packages as appropriate - [x] Native playground: SQLite and Storage plugins behave as before - [x] Web playground: plugin sections render; SQLite section explains disabled state
1 parent e1e5bd7 commit 0b373c7

20 files changed

Lines changed: 514 additions & 212 deletions

.changeset/sqlite-agent-tools.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@rozenite/sqlite-plugin': minor
3+
---
4+
5+
Add agent tool support to the SQLite plugin. LLM agents can now use `list-databases` to discover registered databases and `execute-sql` to run any SQL — including multi-statement scripts — against a specific database.

.changeset/storage-plugin-web.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@rozenite/storage-plugin': minor
3+
---
4+
5+
The Storage plugin now runs in **development on web** (React Native for Web) when using Rozenite for Web. AsyncStorage and Expo SecureStore adapters work as on native; **MMKV** stays unavailable in the browser, so the MMKV adapter resolves to an **empty inspector** (same as production) without loading `react-native-mmkv` in your web bundle.

apps/playground/metro.config.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,18 @@ config.server = {
4141
},
4242
};
4343

44+
// Add wasm asset support
45+
config.resolver.assetExts.push('wasm');
46+
47+
// Add COEP and COOP headers to support SharedArrayBuffer
48+
config.server.enhanceMiddleware = (middleware) => {
49+
return (req, res, next) => {
50+
res.setHeader('Cross-Origin-Embedder-Policy', 'credentialless');
51+
res.setHeader('Cross-Origin-Opener-Policy', 'same-origin');
52+
return middleware(req, res, next);
53+
};
54+
};
55+
4456
module.exports = composeMetroConfigTransformers([
4557
withRozenite,
4658
{
Lines changed: 25 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,35 @@
1-
import { useState } from 'react';
1+
import { SafeAreaView, ScrollView, StyleSheet, Text } from 'react-native';
22
import {
3-
ActivityIndicator,
4-
Pressable,
5-
SafeAreaView,
6-
ScrollView,
7-
StyleSheet,
8-
Text,
9-
View,
10-
} from 'react-native';
11-
import { api, type Post, type User } from './utils/network-activity/api';
3+
ControlsPluginSection,
4+
OverlayPluginSection,
5+
PerformanceMonitorPluginSection,
6+
ReactNavigationPluginSection,
7+
ReduxDevToolsPluginSection,
8+
SqlitePluginSection,
9+
StoragePluginSection,
10+
TanStackQueryPluginSection,
11+
} from './WebPluginSections';
1212

1313
export default function App() {
14-
const [users, setUsers] = useState<User[]>([]);
15-
const [post, setPost] = useState<Post | null>(null);
16-
const [error, setError] = useState<string | null>(null);
17-
const [loading, setLoading] = useState(false);
18-
19-
const loadUsers = async () => {
20-
setLoading(true);
21-
setError(null);
22-
23-
try {
24-
setUsers(await api.getUsers());
25-
} catch (nextError) {
26-
setError(
27-
nextError instanceof Error ? nextError.message : 'Failed to load users',
28-
);
29-
} finally {
30-
setLoading(false);
31-
}
32-
};
33-
34-
const createTestPost = async () => {
35-
setLoading(true);
36-
setError(null);
37-
38-
try {
39-
setPost(
40-
await api.createPost({
41-
title: 'Webpack web smoke test',
42-
body: 'Testing the React Native Web webpack setup.',
43-
userId: 1,
44-
}),
45-
);
46-
} catch (nextError) {
47-
setError(
48-
nextError instanceof Error
49-
? nextError.message
50-
: 'Failed to create post',
51-
);
52-
} finally {
53-
setLoading(false);
54-
}
55-
};
56-
5714
return (
5815
<SafeAreaView style={styles.safeArea}>
5916
<ScrollView contentContainerStyle={styles.content}>
60-
<Text style={styles.eyebrow}>React Native Web</Text>
61-
<Text style={styles.title}>Playground webpack smoke test</Text>
17+
<Text style={styles.eyebrow}>Rozenite for Web</Text>
6218
<Text style={styles.description}>
63-
This uses `webpack` plus `react-native-web` against the existing
64-
`src/main.tsx` entry.
19+
Requirements: install the Rozenite Chrome extension, add
20+
`@rozenite/web`, wrap your web bundler config with `withRozeniteWeb`,
21+
and load `require(&apos;@rozenite/web&apos;)` in your web entry point. Then run
22+
the app in development on a Chromium-based browser.
6523
</Text>
6624

67-
<View style={styles.actions}>
68-
<Pressable onPress={loadUsers} style={styles.primaryButton}>
69-
<Text style={styles.primaryButtonText}>Load users</Text>
70-
</Pressable>
71-
<Pressable onPress={createTestPost} style={styles.secondaryButton}>
72-
<Text style={styles.secondaryButtonText}>Create test post</Text>
73-
</Pressable>
74-
</View>
75-
76-
{loading ? (
77-
<View style={styles.card}>
78-
<ActivityIndicator color="#8b5cf6" />
79-
<Text style={styles.muted}>Running request...</Text>
80-
</View>
81-
) : null}
82-
83-
{error ? (
84-
<View style={styles.errorCard}>
85-
<Text style={styles.errorTitle}>Request failed</Text>
86-
<Text style={styles.errorText}>{error}</Text>
87-
</View>
88-
) : null}
89-
90-
{post ? (
91-
<View style={styles.card}>
92-
<Text style={styles.sectionTitle}>Latest created post</Text>
93-
<Text style={styles.cardTitle}>{post.title}</Text>
94-
<Text style={styles.cardText}>{post.body}</Text>
95-
</View>
96-
) : null}
97-
98-
<View style={styles.card}>
99-
<Text style={styles.sectionTitle}>Fetched users</Text>
100-
{users.length === 0 ? (
101-
<Text style={styles.muted}>No users loaded yet.</Text>
102-
) : (
103-
users.map((user) => (
104-
<View key={user.id} style={styles.userRow}>
105-
<Text style={styles.cardTitle}>{user.name}</Text>
106-
<Text style={styles.cardText}>{user.email}</Text>
107-
</View>
108-
))
109-
)}
110-
</View>
25+
<StoragePluginSection />
26+
<ReactNavigationPluginSection />
27+
<ControlsPluginSection />
28+
<OverlayPluginSection />
29+
<PerformanceMonitorPluginSection />
30+
<ReduxDevToolsPluginSection />
31+
<SqlitePluginSection />
32+
<TanStackQueryPluginSection />
11133
</ScrollView>
11234
</SafeAreaView>
11335
);
@@ -125,6 +47,7 @@ const styles = StyleSheet.create({
12547
width: '100%',
12648
maxWidth: 880,
12749
alignSelf: 'center',
50+
paddingBottom: 48,
12851
},
12952
eyebrow: {
13053
color: '#8b5cf6',
@@ -133,95 +56,10 @@ const styles = StyleSheet.create({
13356
textTransform: 'uppercase',
13457
letterSpacing: 1,
13558
},
136-
title: {
137-
color: '#f8fafc',
138-
fontSize: 36,
139-
fontWeight: '800',
140-
},
14159
description: {
14260
color: '#cbd5e1',
14361
fontSize: 16,
14462
lineHeight: 24,
145-
maxWidth: 640,
146-
},
147-
actions: {
148-
flexDirection: 'row',
149-
flexWrap: 'wrap',
150-
gap: 12,
151-
},
152-
primaryButton: {
153-
backgroundColor: '#8b5cf6',
154-
borderRadius: 999,
155-
paddingHorizontal: 18,
156-
paddingVertical: 12,
157-
},
158-
secondaryButton: {
159-
backgroundColor: '#111827',
160-
borderRadius: 999,
161-
borderWidth: 1,
162-
borderColor: '#334155',
163-
paddingHorizontal: 18,
164-
paddingVertical: 12,
165-
},
166-
primaryButtonText: {
167-
color: '#ffffff',
168-
fontSize: 15,
169-
fontWeight: '700',
170-
},
171-
secondaryButtonText: {
172-
color: '#e2e8f0',
173-
fontSize: 15,
174-
fontWeight: '700',
175-
},
176-
card: {
177-
backgroundColor: '#111827',
178-
borderRadius: 20,
179-
borderWidth: 1,
180-
borderColor: '#1f2937',
181-
gap: 10,
182-
padding: 20,
183-
},
184-
errorCard: {
185-
backgroundColor: '#3f1d2e',
186-
borderRadius: 20,
187-
borderWidth: 1,
188-
borderColor: '#7f1d1d',
189-
gap: 8,
190-
padding: 20,
191-
},
192-
sectionTitle: {
193-
color: '#f8fafc',
194-
fontSize: 18,
195-
fontWeight: '700',
196-
},
197-
errorTitle: {
198-
color: '#fecaca',
199-
fontSize: 18,
200-
fontWeight: '700',
201-
},
202-
errorText: {
203-
color: '#fee2e2',
204-
fontSize: 14,
205-
lineHeight: 20,
206-
},
207-
cardTitle: {
208-
color: '#f8fafc',
209-
fontSize: 16,
210-
fontWeight: '600',
211-
},
212-
cardText: {
213-
color: '#cbd5e1',
214-
fontSize: 14,
215-
lineHeight: 20,
216-
},
217-
muted: {
218-
color: '#94a3b8',
219-
fontSize: 14,
220-
},
221-
userRow: {
222-
gap: 4,
223-
borderTopWidth: 1,
224-
borderTopColor: '#1f2937',
225-
paddingTop: 12,
63+
maxWidth: 720,
22664
},
22765
});

0 commit comments

Comments
 (0)