suparisma 1.1.2 → 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 +558 -18
- package/SEARCH_FEATURES.md +430 -0
- package/dist/config.js +2 -1
- package/dist/generators/coreGenerator.js +189 -43
- package/dist/generators/supabaseClientGenerator.js +69 -7
- package/dist/generators/typeGenerator.js +4 -21
- package/dist/index.js +206 -15
- package/dist/parser.js +47 -37
- package/package.json +22 -3
- package/prisma/schema.prisma +6 -1
|
@@ -23,9 +23,13 @@ import { supabase } from './supabase-client';
|
|
|
23
23
|
* @example
|
|
24
24
|
* // Search for users with names containing "john"
|
|
25
25
|
* const query = { field: "name", value: "john" };
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* // Search across multiple fields
|
|
29
|
+
* const query = { field: "multi", value: "john" };
|
|
26
30
|
*/
|
|
27
31
|
export type SearchQuery = {
|
|
28
|
-
/** The field name to search in */
|
|
32
|
+
/** The field name to search in, or "multi" for multi-field search */
|
|
29
33
|
field: string;
|
|
30
34
|
/** The search term/value to look for */
|
|
31
35
|
value: string;
|
|
@@ -34,6 +38,51 @@ export type SearchQuery = {
|
|
|
34
38
|
// Define type for Supabase query builder
|
|
35
39
|
export type SupabaseQueryBuilder = ReturnType<ReturnType<typeof supabase.from>['select']>;
|
|
36
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Utility function to escape regex special characters for safe RegExp usage
|
|
43
|
+
* Prevents "Invalid regular expression" errors when search terms contain special characters
|
|
44
|
+
*/
|
|
45
|
+
export function escapeRegexCharacters(str: string): string {
|
|
46
|
+
// Escape all special regex characters: ( ) [ ] { } + * ? ^ $ | . \\
|
|
47
|
+
return str.replace(/[()\\[\\]{}+*?^$|.\\\\]/g, '\\\\\\\\$&');
|
|
48
|
+
}
|
|
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
|
+
|
|
37
86
|
/**
|
|
38
87
|
* Advanced filter operators for complex queries
|
|
39
88
|
* @example
|
|
@@ -162,10 +211,22 @@ export type ModelResult<T> = Promise<{
|
|
|
162
211
|
* users.search.addQuery({ field: "name", value: "john" });
|
|
163
212
|
*
|
|
164
213
|
* @example
|
|
214
|
+
* // Search across multiple fields
|
|
215
|
+
* users.search.searchMultiField("john doe");
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
165
218
|
* // Check if search is loading
|
|
166
219
|
* if (users.search.loading) {
|
|
167
220
|
* return <div>Searching...</div>;
|
|
168
221
|
* }
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* // Get current search terms for highlighting
|
|
225
|
+
* const searchTerms = users.search.getCurrentSearchTerms();
|
|
226
|
+
*
|
|
227
|
+
* @example
|
|
228
|
+
* // Safely escape regex characters
|
|
229
|
+
* const escaped = users.search.escapeRegex("user@example.com");
|
|
169
230
|
*/
|
|
170
231
|
export type SearchState = {
|
|
171
232
|
/** Current active search queries */
|
|
@@ -180,6 +241,14 @@ export type SearchState = {
|
|
|
180
241
|
removeQuery: (field: string) => void;
|
|
181
242
|
/** Clear all search queries and return to normal data fetching */
|
|
182
243
|
clearQueries: () => void;
|
|
244
|
+
/** Search across multiple fields (convenience method) */
|
|
245
|
+
searchMultiField: (value: string) => void;
|
|
246
|
+
/** Search in a specific field (convenience method) */
|
|
247
|
+
searchField: (field: string, value: string) => void;
|
|
248
|
+
/** Get current search terms for custom highlighting */
|
|
249
|
+
getCurrentSearchTerms: () => string[];
|
|
250
|
+
/** Safely escape regex special characters */
|
|
251
|
+
escapeRegex: (text: string) => string;
|
|
183
252
|
};
|
|
184
253
|
|
|
185
254
|
/**
|
|
@@ -897,7 +966,39 @@ export function createSuparismaHook<
|
|
|
897
966
|
setSearchQueries([]);
|
|
898
967
|
isSearchingRef.current = false;
|
|
899
968
|
findMany({ where, orderBy, take: limit, skip: offset });
|
|
900
|
-
}, [where, orderBy, limit, offset])
|
|
969
|
+
}, [where, orderBy, limit, offset]),
|
|
970
|
+
|
|
971
|
+
// Search across multiple fields (convenience method)
|
|
972
|
+
searchMultiField: useCallback((value: string) => {
|
|
973
|
+
if (searchFields.length <= 1) {
|
|
974
|
+
console.warn('Multi-field search requires at least 2 searchable fields');
|
|
975
|
+
return;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
setSearchQueries([{ field: 'multi', value }]);
|
|
979
|
+
executeSearch([{ field: 'multi', value }]);
|
|
980
|
+
}, [searchFields.length]),
|
|
981
|
+
|
|
982
|
+
// Search in a specific field (convenience method)
|
|
983
|
+
searchField: useCallback((field: string, value: string) => {
|
|
984
|
+
if (!searchFields.includes(field)) {
|
|
985
|
+
console.warn(\`Field "\${field}" is not searchable. Available fields: \${searchFields.join(', ')}\`);
|
|
986
|
+
return;
|
|
987
|
+
}
|
|
988
|
+
|
|
989
|
+
setSearchQueries([{ field, value }]);
|
|
990
|
+
executeSearch([{ field, value }]);
|
|
991
|
+
}, [searchFields]),
|
|
992
|
+
|
|
993
|
+
// Get current search terms for custom highlighting
|
|
994
|
+
getCurrentSearchTerms: useCallback(() => {
|
|
995
|
+
return searchQueries.map(q => q.value.trim());
|
|
996
|
+
}, [searchQueries]),
|
|
997
|
+
|
|
998
|
+
// Safely escape regex special characters
|
|
999
|
+
escapeRegex: useCallback((text: string) => {
|
|
1000
|
+
return escapeRegexCharacters(text);
|
|
1001
|
+
}, [])
|
|
901
1002
|
};
|
|
902
1003
|
|
|
903
1004
|
// Execute search based on queries
|
|
@@ -920,13 +1021,52 @@ export function createSuparismaHook<
|
|
|
920
1021
|
try {
|
|
921
1022
|
let results: TWithRelations[] = [];
|
|
922
1023
|
|
|
1024
|
+
// Validate search queries
|
|
1025
|
+
const validQueries = queries.filter(query => {
|
|
1026
|
+
if (!query.field || !query.value) {
|
|
1027
|
+
console.warn('Invalid search query - missing field or value:', query);
|
|
1028
|
+
return false;
|
|
1029
|
+
}
|
|
1030
|
+
// Allow "multi" as a special field for multi-field search
|
|
1031
|
+
if (query.field === 'multi' && searchFields.length > 1) {
|
|
1032
|
+
return true;
|
|
1033
|
+
}
|
|
1034
|
+
if (!searchFields.includes(query.field)) {
|
|
1035
|
+
console.warn(\`Field "\${query.field}" is not searchable. Available fields: \${searchFields.join(', ')}, or "multi" for multi-field search\`);
|
|
1036
|
+
return false;
|
|
1037
|
+
}
|
|
1038
|
+
return true;
|
|
1039
|
+
});
|
|
1040
|
+
|
|
1041
|
+
if (validQueries.length === 0) {
|
|
1042
|
+
console.log('No valid search queries found');
|
|
1043
|
+
setData([]);
|
|
1044
|
+
setCount(0);
|
|
1045
|
+
return;
|
|
1046
|
+
}
|
|
1047
|
+
|
|
923
1048
|
// Execute RPC function for each query using Promise.all
|
|
924
|
-
const searchPromises =
|
|
925
|
-
// Build function name
|
|
926
|
-
const functionName =
|
|
1049
|
+
const searchPromises = validQueries.map(query => {
|
|
1050
|
+
// Build function name based on field type
|
|
1051
|
+
const functionName = query.field === 'multi'
|
|
1052
|
+
? \`search_\${tableName.toLowerCase()}_multi_field\`
|
|
1053
|
+
: \`search_\${tableName.toLowerCase()}_by_\${query.field.toLowerCase()}_prefix\`;
|
|
1054
|
+
|
|
1055
|
+
console.log(\`🔍 Executing search: \${functionName}(search_prefix: "\${query.value.trim()}")\`);
|
|
927
1056
|
|
|
928
|
-
// Call RPC function
|
|
929
|
-
return supabase.rpc(functionName, {
|
|
1057
|
+
// Call RPC function with proper error handling
|
|
1058
|
+
return Promise.resolve(supabase.rpc(functionName, { search_prefix: query.value.trim() }))
|
|
1059
|
+
.then((result: any) => ({
|
|
1060
|
+
...result,
|
|
1061
|
+
queryField: query.field,
|
|
1062
|
+
queryValue: query.value
|
|
1063
|
+
}))
|
|
1064
|
+
.catch((error: any) => ({
|
|
1065
|
+
data: null,
|
|
1066
|
+
error: error,
|
|
1067
|
+
queryField: query.field,
|
|
1068
|
+
queryValue: query.value
|
|
1069
|
+
}));
|
|
930
1070
|
});
|
|
931
1071
|
|
|
932
1072
|
// Execute all search queries in parallel
|
|
@@ -934,82 +1074,88 @@ export function createSuparismaHook<
|
|
|
934
1074
|
|
|
935
1075
|
// Combine and deduplicate results
|
|
936
1076
|
const allResults: Record<string, TWithRelations> = {};
|
|
1077
|
+
let hasErrors = false;
|
|
937
1078
|
|
|
938
1079
|
// Process each search result
|
|
939
|
-
searchResults.forEach((result, index) => {
|
|
1080
|
+
searchResults.forEach((result: any, index: number) => {
|
|
940
1081
|
if (result.error) {
|
|
941
|
-
console.error(
|
|
1082
|
+
console.error(\`🔍 Search error for field "\${result.queryField}" with value "\${result.queryValue}":\`, result.error);
|
|
1083
|
+
hasErrors = true;
|
|
942
1084
|
return;
|
|
943
1085
|
}
|
|
944
1086
|
|
|
945
|
-
if (result.data) {
|
|
1087
|
+
if (result.data && Array.isArray(result.data)) {
|
|
1088
|
+
console.log(\`🔍 Search results for "\${result.queryField}": \${result.data.length} items\`);
|
|
1089
|
+
|
|
946
1090
|
// Add each result, using id as key to deduplicate
|
|
947
1091
|
for (const item of result.data as TWithRelations[]) {
|
|
948
1092
|
// @ts-ignore: Assume item has an id property
|
|
949
|
-
if (item.id) {
|
|
1093
|
+
if (item && typeof item === 'object' && 'id' in item && item.id) {
|
|
950
1094
|
// @ts-ignore: Add to results using id as key
|
|
951
1095
|
allResults[item.id] = item;
|
|
952
1096
|
}
|
|
953
1097
|
}
|
|
1098
|
+
} else if (result.data) {
|
|
1099
|
+
console.warn(\`🔍 Unexpected search result format for "\${result.queryField}":\`, typeof result.data);
|
|
954
1100
|
}
|
|
955
1101
|
});
|
|
956
1102
|
|
|
957
1103
|
// Convert back to array
|
|
958
1104
|
results = Object.values(allResults);
|
|
1105
|
+
console.log(\`🔍 Combined search results: \${results.length} unique items\`);
|
|
959
1106
|
|
|
960
|
-
// Apply any where conditions client-side
|
|
1107
|
+
// Apply any where conditions client-side (now using the proper filter function)
|
|
961
1108
|
if (where) {
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
// Skip complex filters for now
|
|
966
|
-
continue;
|
|
967
|
-
}
|
|
968
|
-
|
|
969
|
-
if (item[key as keyof typeof item] !== value) {
|
|
970
|
-
return false;
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
return true;
|
|
974
|
-
});
|
|
1109
|
+
const originalCount = results.length;
|
|
1110
|
+
results = results.filter((item) => matchesFilter(item, where));
|
|
1111
|
+
console.log(\`🔍 After applying where filter: \${results.length}/\${originalCount} items\`);
|
|
975
1112
|
}
|
|
976
1113
|
|
|
977
1114
|
// Set count directly for search results
|
|
978
1115
|
setCount(results.length);
|
|
979
1116
|
|
|
980
|
-
// Apply ordering if needed
|
|
1117
|
+
// Apply ordering if needed (using the proper compare function)
|
|
981
1118
|
if (orderBy) {
|
|
982
|
-
const
|
|
983
|
-
if (orderEntries.length > 0) {
|
|
984
|
-
const [orderField, direction] = orderEntries[0] || [];
|
|
1119
|
+
const orderByArray = Array.isArray(orderBy) ? orderBy : [orderBy];
|
|
985
1120
|
results = [...results].sort((a, b) => {
|
|
986
|
-
|
|
987
|
-
const
|
|
1121
|
+
for (const orderByClause of orderByArray) {
|
|
1122
|
+
for (const [field, direction] of Object.entries(orderByClause)) {
|
|
1123
|
+
const aValue = a[field as keyof typeof a];
|
|
1124
|
+
const bValue = b[field as keyof typeof b];
|
|
988
1125
|
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
return aValue > bValue ? -1 : aValue < bValue ? 1 : 0;
|
|
1126
|
+
if (aValue === bValue) continue;
|
|
1127
|
+
|
|
1128
|
+
return compareValues(aValue, bValue, direction as 'asc' | 'desc');
|
|
993
1129
|
}
|
|
994
|
-
}
|
|
995
|
-
|
|
1130
|
+
}
|
|
1131
|
+
return 0;
|
|
1132
|
+
});
|
|
996
1133
|
}
|
|
997
1134
|
|
|
998
1135
|
// Apply pagination if needed
|
|
999
1136
|
let paginatedResults = results;
|
|
1000
|
-
if (limit && limit > 0) {
|
|
1001
|
-
paginatedResults = results.slice(0, limit);
|
|
1002
|
-
}
|
|
1003
|
-
|
|
1004
1137
|
if (offset && offset > 0) {
|
|
1005
1138
|
paginatedResults = paginatedResults.slice(offset);
|
|
1006
1139
|
}
|
|
1007
1140
|
|
|
1141
|
+
if (limit && limit > 0) {
|
|
1142
|
+
paginatedResults = paginatedResults.slice(0, limit);
|
|
1143
|
+
}
|
|
1144
|
+
|
|
1145
|
+
console.log(\`🔍 Final search results: \${paginatedResults.length} items (total: \${results.length})\`);
|
|
1146
|
+
|
|
1008
1147
|
// Update data with search results
|
|
1009
1148
|
setData(paginatedResults);
|
|
1149
|
+
|
|
1150
|
+
// Show error if there were issues but still show partial results
|
|
1151
|
+
if (hasErrors && results.length === 0) {
|
|
1152
|
+
setError(new Error('Search failed - please check if search functions are properly configured'));
|
|
1153
|
+
}
|
|
1010
1154
|
} catch (err) {
|
|
1011
|
-
console.error('Search error:', err);
|
|
1155
|
+
console.error('🔍 Search error:', err);
|
|
1012
1156
|
setError(err as Error);
|
|
1157
|
+
setData([]);
|
|
1158
|
+
setCount(0);
|
|
1013
1159
|
} finally {
|
|
1014
1160
|
setSearchLoading(false);
|
|
1015
1161
|
}
|
|
@@ -1580,7 +1726,7 @@ export function createSuparismaHook<
|
|
|
1580
1726
|
if (defaultValue.includes('now()') || defaultValue.includes('now')) {
|
|
1581
1727
|
appliedDefaults[field] = now.toISOString(); // Database expects ISO string
|
|
1582
1728
|
} else if (defaultValue.includes('uuid()') || defaultValue.includes('uuid')) {
|
|
1583
|
-
appliedDefaults[field] =
|
|
1729
|
+
appliedDefaults[field] = generateUUID();
|
|
1584
1730
|
} else if (defaultValue.includes('cuid()') || defaultValue.includes('cuid')) {
|
|
1585
1731
|
// Simple cuid-like implementation for client-side
|
|
1586
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");
|
|
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
|
-
|
|
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
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
}
|
|
@@ -118,33 +118,16 @@ function generateModelTypesFile(model) {
|
|
|
118
118
|
// Generate imports section for zod custom types
|
|
119
119
|
let customImports = '';
|
|
120
120
|
if (model.zodImports && model.zodImports.length > 0) {
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
// Get the zod schemas file path from environment variable
|
|
124
|
-
const zodSchemasPath = process.env.ZOD_SCHEMAS_FILE_PATH || '../commonTypes';
|
|
125
|
-
// Add custom imports with environment variable path
|
|
126
|
-
customImports += model.zodImports
|
|
127
|
-
.map(zodImport => {
|
|
128
|
-
// Extract the types from the original import statement
|
|
129
|
-
const typeMatch = zodImport.importStatement.match(/import\s+{\s*([^}]+)\s*}\s+from/);
|
|
130
|
-
if (typeMatch) {
|
|
131
|
-
const types = typeMatch[1].trim();
|
|
132
|
-
return `import { ${types} } from '${zodSchemasPath}'`;
|
|
133
|
-
}
|
|
134
|
-
// Fallback to original import if parsing fails
|
|
135
|
-
return zodImport.importStatement;
|
|
136
|
-
})
|
|
137
|
-
.join('\n') + '\n\n';
|
|
138
|
-
// Add type definitions for imported zod schemas if needed
|
|
139
|
-
// This is a simplified approach - you might want to make this more sophisticated
|
|
121
|
+
// For projects without zod dependency, fallback to any types instead of importing zod
|
|
122
|
+
// This prevents TypeScript errors when zod is not installed
|
|
140
123
|
const customTypeDefinitions = model.zodImports
|
|
141
124
|
.flatMap(zodImport => zodImport.types)
|
|
142
125
|
.filter((type, index, array) => array.indexOf(type) === index) // Remove duplicates
|
|
143
126
|
.map(type => {
|
|
144
|
-
// If it ends with 'Schema', create a corresponding type
|
|
127
|
+
// If it ends with 'Schema', create a corresponding type using any instead of zod
|
|
145
128
|
if (type.endsWith('Schema')) {
|
|
146
129
|
const typeName = type.replace('Schema', '');
|
|
147
|
-
return
|
|
130
|
+
return `// Fallback type when zod is not available\nexport type ${typeName} = any;`;
|
|
148
131
|
}
|
|
149
132
|
return '';
|
|
150
133
|
})
|