suparisma 1.2.1 → 1.2.2

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/README.md CHANGED
@@ -16,6 +16,7 @@ A powerful, typesafe React hook generator for Supabase, driven by your Prisma sc
16
16
  - [Why Suparisma?](#why-suparisma)
17
17
  - [Features](#features)
18
18
  - [Installation](#installation)
19
+ - [React Native / Expo Setup](#react-native--expo-setup)
19
20
  - [Quick Start](#quick-start)
20
21
  - [Detailed Usage](#detailed-usage)
21
22
  - [Basic CRUD Operations](#basic-crud-operations)
@@ -60,7 +61,7 @@ Suparisma bridges this gap by:
60
61
  - Enabling easy **pagination, filtering, and search** on your data
61
62
  - Leveraging both **Prisma** and **Supabase** official SDKs
62
63
  - Respecting **Supabase's auth rules** for secure database access
63
- - Working seamlessly with any React environment (Next.js, Remix, Tanstack Router, etc.)
64
+ - Working seamlessly with any React environment (Next.js, Remix, Tanstack Router, React Native, etc.)
64
65
 
65
66
  ## Features
66
67
 
@@ -70,7 +71,7 @@ Suparisma bridges this gap by:
70
71
  - 🔍 **Full-text search** with configurable annotations *(currently under maintenance)*
71
72
  - 🔢 **Pagination and sorting** built into every hook
72
73
  - 🧩 **Prisma-like API** that feels familiar if you already use Prisma
73
- - 📱 **Works with any React framework** including Next.js, Remix, etc.
74
+ - 📱 **Works with any React framework** including Next.js, Remix, React Native, and Expo
74
75
  - 🛠️ **Simple CLI** to generate hooks with a single command
75
76
 
76
77
  ## Installation
@@ -86,6 +87,110 @@ yarn add suparisma
86
87
  pnpm install suparisma
87
88
  ```
88
89
 
90
+ ## React Native / Expo Setup
91
+
92
+ Suparisma fully supports React Native and Expo projects. Follow these additional steps for mobile development:
93
+
94
+ ### 1. Install Dependencies
95
+
96
+ ```bash
97
+ # Install Suparisma and required dependencies
98
+ pnpm install suparisma @supabase/supabase-js @react-native-async-storage/async-storage react-native-url-polyfill
99
+
100
+ # For UUID generation support (recommended)
101
+ pnpm install react-native-get-random-values
102
+ ```
103
+
104
+ ### 2. Add Polyfills
105
+
106
+ Add these imports at the very top of your app's entry point (e.g., `App.tsx` or `index.js`):
107
+
108
+ ```tsx
109
+ // App.tsx or index.js - Add these at the VERY TOP before any other imports
110
+ import 'react-native-get-random-values'; // Required for UUID generation
111
+ import 'react-native-url-polyfill/auto'; // Required for Supabase
112
+ ```
113
+
114
+ ### 3. Set Environment Variables
115
+
116
+ For **Expo** projects, use the `EXPO_PUBLIC_` prefix in your `.env` file:
117
+
118
+ ```bash
119
+ EXPO_PUBLIC_SUPABASE_URL="https://your-project.supabase.co"
120
+ EXPO_PUBLIC_SUPABASE_ANON_KEY="your-anon-key"
121
+ ```
122
+
123
+ For **bare React Native** projects, use `react-native-dotenv` or similar.
124
+
125
+ ### 4. Generate Hooks for React Native
126
+
127
+ Set the `SUPARISMA_PLATFORM` environment variable when generating:
128
+
129
+ ```bash
130
+ # Generate hooks for React Native / Expo
131
+ SUPARISMA_PLATFORM=react-native npx suparisma generate
132
+ ```
133
+
134
+ Or add it to your `package.json` scripts:
135
+
136
+ ```json
137
+ {
138
+ "scripts": {
139
+ "suparisma:generate": "SUPARISMA_PLATFORM=react-native npx suparisma generate"
140
+ }
141
+ }
142
+ ```
143
+
144
+ ### 5. Use the Hooks
145
+
146
+ The hooks work exactly the same as in web projects:
147
+
148
+ ```tsx
149
+ import React from 'react';
150
+ import { View, Text, FlatList, TouchableOpacity } from 'react-native';
151
+ import useSuparisma from './src/suparisma/generated';
152
+
153
+ function ThingList() {
154
+ const {
155
+ data: things,
156
+ loading,
157
+ error,
158
+ create: createThing
159
+ } = useSuparisma.thing();
160
+
161
+ if (loading) return <Text>Loading...</Text>;
162
+ if (error) return <Text>Error: {error.message}</Text>;
163
+
164
+ return (
165
+ <View>
166
+ <FlatList
167
+ data={things}
168
+ keyExtractor={(item) => item.id}
169
+ renderItem={({ item }) => (
170
+ <Text>{item.name} (Number: {item.someNumber})</Text>
171
+ )}
172
+ />
173
+
174
+ <TouchableOpacity
175
+ onPress={() => createThing({
176
+ name: "New Thing",
177
+ someNumber: Math.floor(Math.random() * 100)
178
+ })}
179
+ >
180
+ <Text>Add Thing</Text>
181
+ </TouchableOpacity>
182
+ </View>
183
+ );
184
+ }
185
+ ```
186
+
187
+ ### Platform Detection
188
+
189
+ The generated Supabase client automatically configures itself for React Native with:
190
+ - **AsyncStorage** for auth persistence
191
+ - **Session detection** disabled (not applicable in mobile)
192
+ - **Auto refresh token** enabled
193
+
89
194
  ## Quick Start
90
195
 
91
196
  1. **Add a Prisma schema**: Ensure you have a valid `prisma/schema.prisma` file in your project
@@ -139,6 +244,7 @@ Note: you can adjust the prisma schema path and the generated files output path
139
244
  ```bash
140
245
  SUPARISMA_PRISMA_SCHEMA_PATH="./prisma/schema.prisma"
141
246
  SUPARISMA_OUTPUT_DIR="./src/suparisma/generated"
247
+ SUPARISMA_PLATFORM="web" # or "react-native" for React Native/Expo projects
142
248
  ```
143
249
  Also make sure to not change any of these generated files directly as **they will always be overwritten**
144
250
 
@@ -1291,10 +1397,13 @@ export default function ThingTable() {
1291
1397
  |----------|----------|-------------|---------|
1292
1398
  | `DATABASE_URL` | Yes | Postgres database URL used by Prisma | `postgresql://user:pass@host:port/db` |
1293
1399
  | `DIRECT_URL` | Yes | Direct URL to Postgres DB for realtime setup | `postgresql://user:pass@host:port/db` |
1294
- | `NEXT_PUBLIC_SUPABASE_URL` | Yes | Your Supabase project URL | `https://xyz.supabase.co` |
1295
- | `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Yes | Supabase anonymous key | `eyJh...` |
1400
+ | `NEXT_PUBLIC_SUPABASE_URL` | Yes (Web) | Your Supabase project URL (Next.js) | `https://xyz.supabase.co` |
1401
+ | `NEXT_PUBLIC_SUPABASE_ANON_KEY` | Yes (Web) | Supabase anonymous key (Next.js) | `eyJh...` |
1402
+ | `EXPO_PUBLIC_SUPABASE_URL` | Yes (RN) | Your Supabase project URL (Expo) | `https://xyz.supabase.co` |
1403
+ | `EXPO_PUBLIC_SUPABASE_ANON_KEY` | Yes (RN) | Supabase anonymous key (Expo) | `eyJh...` |
1296
1404
  | `SUPARISMA_OUTPUT_DIR` | No | Custom output directory | `src/lib/suparisma` |
1297
1405
  | `SUPARISMA_PRISMA_SCHEMA_PATH` | No | Custom schema path | `db/schema.prisma` |
1406
+ | `SUPARISMA_PLATFORM` | No | Target platform: `web` or `react-native` | `react-native` |
1298
1407
 
1299
1408
  ### CLI Commands
1300
1409
 
@@ -1464,6 +1573,12 @@ GRANT USAGE, SELECT ON ALL SEQUENCES IN SCHEMA public TO anon;
1464
1573
  ALTER DEFAULT PRIVILEGES IN SCHEMA public
1465
1574
  GRANT USAGE, SELECT ON SEQUENCES TO anon;
1466
1575
  ```
1576
+
1577
+ You could also try debugging on a table, the following is NOT recommended but you can debug permissions given to anon, service_account and give all access to anon key to make sure that's not the issue:
1578
+ ```sql
1579
+ GRANT ALL ON TABLE "(tableName)" TO anon;
1580
+ GRANT ALL ON TABLE "(tableName)" TO authenticated;
1581
+ ```
1467
1582
 
1468
1583
  **"Unknown command: undefined"**
1469
1584
 
package/dist/config.js CHANGED
@@ -3,7 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.HOOK_NAME_PREFIX = exports.UTILS_DIR = exports.HOOKS_DIR = exports.TYPES_DIR = exports.OUTPUT_DIR = exports.PRISMA_SCHEMA_PATH = void 0;
6
+ exports.PLATFORM = exports.HOOK_NAME_PREFIX = exports.UTILS_DIR = exports.HOOKS_DIR = exports.TYPES_DIR = exports.OUTPUT_DIR = exports.PRISMA_SCHEMA_PATH = void 0;
7
7
  // Configuration
8
8
  const path_1 = __importDefault(require("path"));
9
9
  // Use current working directory for all paths
@@ -14,3 +14,4 @@ exports.TYPES_DIR = `${exports.OUTPUT_DIR}/types`;
14
14
  exports.HOOKS_DIR = `${exports.OUTPUT_DIR}/hooks`;
15
15
  exports.UTILS_DIR = `${exports.OUTPUT_DIR}/utils`;
16
16
  exports.HOOK_NAME_PREFIX = 'useSuparisma';
17
+ exports.PLATFORM = process.env.SUPARISMA_PLATFORM || 'web';
@@ -47,6 +47,42 @@ export function escapeRegexCharacters(str: string): string {
47
47
  return str.replace(/[()\\[\\]{}+*?^$|.\\\\]/g, '\\\\\\\\$&');
48
48
  }
49
49
 
50
+ /**
51
+ * Generate a UUID v4, with fallback for environments without crypto.randomUUID()
52
+ * Works in: browsers, Node.js, and React Native (with react-native-get-random-values polyfill)
53
+ *
54
+ * For React Native, ensure you have installed and imported the polyfill:
55
+ * - pnpm install react-native-get-random-values
56
+ * - Import at app entry point: import 'react-native-get-random-values';
57
+ */
58
+ export function generateUUID(): string {
59
+ // Try native crypto.randomUUID() first (modern browsers & Node.js 16.7+)
60
+ if (typeof crypto !== 'undefined' && typeof crypto.randomUUID === 'function') {
61
+ return crypto.randomUUID();
62
+ }
63
+
64
+ // Fallback using crypto.getRandomValues() (works with react-native-get-random-values polyfill)
65
+ if (typeof crypto !== 'undefined' && typeof crypto.getRandomValues === 'function') {
66
+ const bytes = new Uint8Array(16);
67
+ crypto.getRandomValues(bytes);
68
+
69
+ // Set version (4) and variant (RFC 4122)
70
+ bytes[6] = (bytes[6] & 0x0f) | 0x40; // Version 4
71
+ bytes[8] = (bytes[8] & 0x3f) | 0x80; // Variant RFC 4122
72
+
73
+ const hex = Array.from(bytes, (b) => b.toString(16).padStart(2, '0')).join('');
74
+ return \`\${hex.slice(0, 8)}-\${hex.slice(8, 12)}-\${hex.slice(12, 16)}-\${hex.slice(16, 20)}-\${hex.slice(20)}\`;
75
+ }
76
+
77
+ // Last resort fallback using Math.random() (not cryptographically secure)
78
+ console.warn('[Suparisma] crypto API not available, using Math.random() fallback for UUID generation');
79
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
80
+ const r = (Math.random() * 16) | 0;
81
+ const v = c === 'x' ? r : (r & 0x3) | 0x8;
82
+ return v.toString(16);
83
+ });
84
+ }
85
+
50
86
  /**
51
87
  * Advanced filter operators for complex queries
52
88
  * @example
@@ -1690,7 +1726,7 @@ export function createSuparismaHook<
1690
1726
  if (defaultValue.includes('now()') || defaultValue.includes('now')) {
1691
1727
  appliedDefaults[field] = now.toISOString(); // Database expects ISO string
1692
1728
  } else if (defaultValue.includes('uuid()') || defaultValue.includes('uuid')) {
1693
- appliedDefaults[field] = crypto.randomUUID();
1729
+ appliedDefaults[field] = generateUUID();
1694
1730
  } else if (defaultValue.includes('cuid()') || defaultValue.includes('cuid')) {
1695
1731
  // Simple cuid-like implementation for client-side
1696
1732
  appliedDefaults[field] = 'c' + Math.random().toString(36).substring(2, 15);
@@ -6,21 +6,83 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.generateSupabaseClientFile = generateSupabaseClientFile;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
- const config_1 = require("../config"); // Ensure this is UTILS_DIR
9
+ const config_1 = require("../config");
10
+ /**
11
+ * Generate the Supabase client file based on the target platform.
12
+ * Supports both web (Next.js, etc.) and React Native/Expo.
13
+ */
10
14
  function generateSupabaseClientFile() {
11
- const supabaseClientContent = `// THIS FILE IS AUTO-GENERATED - DO NOT EDIT DIRECTLY
15
+ let supabaseClientContent;
16
+ if (config_1.PLATFORM === 'react-native') {
17
+ // React Native / Expo compatible client
18
+ supabaseClientContent = `// THIS FILE IS AUTO-GENERATED - DO NOT EDIT DIRECTLY
19
+ // Platform: React Native / Expo
20
+ //
21
+ // IMPORTANT: Before using Suparisma in React Native, ensure you have:
22
+ // 1. Installed required dependencies:
23
+ // pnpm install @supabase/supabase-js @react-native-async-storage/async-storage react-native-url-polyfill
24
+ //
25
+ // 2. Added polyfills at your app's entry point (e.g., App.tsx or index.js):
26
+ // import 'react-native-url-polyfill/auto';
27
+ //
28
+ // 3. Set your Supabase credentials below or via environment variables
29
+
30
+ import AsyncStorage from '@react-native-async-storage/async-storage';
12
31
  import { createClient } from '@supabase/supabase-js';
13
32
 
14
- export const supabase = createClient(
15
- process.env.NEXT_PUBLIC_SUPABASE_URL!,
16
- process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
17
- );
33
+ // Option 1: Set your Supabase credentials directly (for quick setup)
34
+ // const SUPABASE_URL = 'https://your-project.supabase.co';
35
+ // const SUPABASE_ANON_KEY = 'your-anon-key';
36
+
37
+ // Option 2: Use environment variables (recommended for production)
38
+ // With Expo, use expo-constants or babel-plugin-inline-dotenv
39
+ // With bare React Native, use react-native-dotenv
40
+ const SUPABASE_URL = process.env.EXPO_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL || '';
41
+ const SUPABASE_ANON_KEY = process.env.EXPO_PUBLIC_SUPABASE_ANON_KEY || process.env.SUPABASE_ANON_KEY || '';
42
+
43
+ if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
44
+ console.warn(
45
+ '[Suparisma] Supabase credentials not found. Please set EXPO_PUBLIC_SUPABASE_URL and EXPO_PUBLIC_SUPABASE_ANON_KEY ' +
46
+ 'in your environment variables, or update the credentials directly in this file.'
47
+ );
48
+ }
49
+
50
+ export const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, {
51
+ auth: {
52
+ storage: AsyncStorage,
53
+ autoRefreshToken: true,
54
+ persistSession: true,
55
+ detectSessionInUrl: false, // Important for React Native
56
+ },
57
+ });
18
58
  `;
59
+ }
60
+ else {
61
+ // Web platform (Next.js, Remix, etc.)
62
+ supabaseClientContent = `// THIS FILE IS AUTO-GENERATED - DO NOT EDIT DIRECTLY
63
+ // Platform: Web (Next.js, Remix, etc.)
64
+ import { createClient } from '@supabase/supabase-js';
65
+
66
+ // For Next.js, use NEXT_PUBLIC_ prefix
67
+ // For other frameworks, adjust the environment variable names as needed
68
+ const SUPABASE_URL = process.env.NEXT_PUBLIC_SUPABASE_URL || process.env.SUPABASE_URL || '';
69
+ const SUPABASE_ANON_KEY = process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY || process.env.SUPABASE_ANON_KEY || '';
70
+
71
+ if (!SUPABASE_URL || !SUPABASE_ANON_KEY) {
72
+ console.warn(
73
+ '[Suparisma] Supabase credentials not found. Please set NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY ' +
74
+ '(or SUPABASE_URL and SUPABASE_ANON_KEY) in your environment variables.'
75
+ );
76
+ }
77
+
78
+ export const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY);
79
+ `;
80
+ }
19
81
  // Output to the UTILS_DIR
20
82
  const outputPath = path_1.default.join(config_1.UTILS_DIR, 'supabase-client.ts');
21
83
  if (!fs_1.default.existsSync(config_1.UTILS_DIR)) {
22
84
  fs_1.default.mkdirSync(config_1.UTILS_DIR, { recursive: true });
23
85
  }
24
86
  fs_1.default.writeFileSync(outputPath, supabaseClientContent);
25
- console.log(`🚀 Generated Supabase client file at: ${outputPath}`);
87
+ console.log(`🚀 Generated Supabase client file at: ${outputPath} (platform: ${config_1.PLATFORM})`);
26
88
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "suparisma",
3
- "version": "1.2.1",
4
- "description": "Opinionated typesafe React realtime CRUD hooks generator for all your Supabase tables, powered by Prisma.",
3
+ "version": "1.2.2",
4
+ "description": "Opinionated typesafe React realtime CRUD hooks generator for all your Supabase tables, powered by Prisma. Works with Next.js, Remix, React Native, and Expo.",
5
5
  "main": "dist/index.js",
6
6
  "repository": {
7
7
  "type": "git",
@@ -20,12 +20,15 @@
20
20
  },
21
21
  "keywords": [
22
22
  "react",
23
+ "react-native",
24
+ "expo",
23
25
  "hooks",
24
26
  "supabase",
25
27
  "prisma",
26
28
  "typescript",
27
29
  "nextjs",
28
- "realtime"
30
+ "realtime",
31
+ "mobile"
29
32
  ],
30
33
  "dependencies": {
31
34
  "@supabase/supabase-js": "^2.49.4",
@@ -41,5 +44,21 @@
41
44
  "@types/react": "^19.1.4",
42
45
  "prisma": "^6.8.2",
43
46
  "supabase-js": "link:@types/@supabase/supabase-js"
47
+ },
48
+ "peerDependencies": {
49
+ "@react-native-async-storage/async-storage": ">=1.19.0",
50
+ "react-native-get-random-values": ">=1.9.0",
51
+ "react-native-url-polyfill": ">=2.0.0"
52
+ },
53
+ "peerDependenciesMeta": {
54
+ "@react-native-async-storage/async-storage": {
55
+ "optional": true
56
+ },
57
+ "react-native-get-random-values": {
58
+ "optional": true
59
+ },
60
+ "react-native-url-polyfill": {
61
+ "optional": true
62
+ }
44
63
  }
45
64
  }