suparisma 1.0.0 → 1.0.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 +4 -2
- package/dist/generators/coreGenerator.js +204 -42
- package/dist/generators/supabaseClientGenerator.js +0 -3
- package/dist/generators/typeGenerator.js +42 -16
- package/dist/index.js +111 -116
- package/dist/parser.js +10 -8
- package/package.json +1 -1
- package/prisma/schema.prisma +4 -2
- package/generated/hooks/useSuparismaAuditLog.ts +0 -80
- package/generated/hooks/useSuparismaThing.ts +0 -82
- package/generated/index.ts +0 -55
- package/generated/types/AuditLogTypes.ts +0 -391
- package/generated/types/ThingTypes.ts +0 -394
- package/generated/utils/core.ts +0 -1490
- package/generated/utils/supabase-client.ts +0 -10
package/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
|
|
1
|
+
(BETA) - USE IN PRODUCTION AT YOUR OWN RISK.
|
|
2
|
+
|
|
3
|
+
# Suparisma
|
|
2
4
|
Supabase + Prisma!
|
|
3
5
|
|
|
4
6
|

|
|
@@ -790,4 +792,4 @@ Contributions are welcome! Please feel free to submit a Pull Request.
|
|
|
790
792
|
|
|
791
793
|
## License
|
|
792
794
|
|
|
793
|
-
MIT
|
|
795
|
+
MIT
|
|
@@ -155,6 +155,37 @@ export type SearchState = {
|
|
|
155
155
|
clearQueries: () => void;
|
|
156
156
|
};
|
|
157
157
|
|
|
158
|
+
/**
|
|
159
|
+
* Compare two values for sorting with proper type handling
|
|
160
|
+
*/
|
|
161
|
+
function compareValues(a: any, b: any, direction: 'asc' | 'desc'): number {
|
|
162
|
+
// Handle undefined/null values
|
|
163
|
+
if (a === undefined || a === null) return direction === 'asc' ? -1 : 1;
|
|
164
|
+
if (b === undefined || b === null) return direction === 'asc' ? 1 : -1;
|
|
165
|
+
|
|
166
|
+
// Handle numbers properly to ensure numeric comparison
|
|
167
|
+
if (typeof a === 'number' && typeof b === 'number') {
|
|
168
|
+
return direction === 'asc'
|
|
169
|
+
? a - b
|
|
170
|
+
: b - a;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Handle dates (convert to timestamps for comparison)
|
|
174
|
+
if (a instanceof Date && b instanceof Date) {
|
|
175
|
+
return direction === 'asc'
|
|
176
|
+
? a.getTime() - b.getTime()
|
|
177
|
+
: b.getTime() - a.getTime();
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Handle strings or mixed types with string conversion
|
|
181
|
+
const aStr = String(a);
|
|
182
|
+
const bStr = String(b);
|
|
183
|
+
|
|
184
|
+
return direction === 'asc'
|
|
185
|
+
? aStr.localeCompare(bStr)
|
|
186
|
+
: bStr.localeCompare(aStr);
|
|
187
|
+
}
|
|
188
|
+
|
|
158
189
|
/**
|
|
159
190
|
* Convert a type-safe where filter to Supabase filter string
|
|
160
191
|
*/
|
|
@@ -316,11 +347,17 @@ export function applyOrderBy<T>(
|
|
|
316
347
|
|
|
317
348
|
// Apply each order by clause
|
|
318
349
|
let orderedQuery = query;
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
350
|
+
|
|
351
|
+
// Handle orderBy as array or single object
|
|
352
|
+
const orderByArray = Array.isArray(orderBy) ? orderBy : [orderBy];
|
|
353
|
+
|
|
354
|
+
for (const orderByClause of orderByArray) {
|
|
355
|
+
for (const [key, direction] of Object.entries(orderByClause)) {
|
|
356
|
+
// @ts-ignore: Supabase typing issue
|
|
357
|
+
orderedQuery = orderedQuery.order(key, {
|
|
358
|
+
ascending: direction === 'asc'
|
|
359
|
+
});
|
|
360
|
+
}
|
|
324
361
|
}
|
|
325
362
|
|
|
326
363
|
return orderedQuery;
|
|
@@ -387,9 +424,29 @@ export function createSuparismaHook<
|
|
|
387
424
|
offset,
|
|
388
425
|
} = options;
|
|
389
426
|
|
|
390
|
-
//
|
|
391
|
-
const
|
|
392
|
-
|
|
427
|
+
// Refs to store the latest options for realtime handlers
|
|
428
|
+
const whereRef = useRef(where);
|
|
429
|
+
const orderByRef = useRef(orderBy);
|
|
430
|
+
const limitRef = useRef(limit);
|
|
431
|
+
const offsetRef = useRef(offset);
|
|
432
|
+
|
|
433
|
+
// Update refs whenever options change
|
|
434
|
+
useEffect(() => {
|
|
435
|
+
whereRef.current = where;
|
|
436
|
+
}, [where]);
|
|
437
|
+
|
|
438
|
+
useEffect(() => {
|
|
439
|
+
orderByRef.current = orderBy;
|
|
440
|
+
}, [orderBy]);
|
|
441
|
+
|
|
442
|
+
useEffect(() => {
|
|
443
|
+
limitRef.current = limit;
|
|
444
|
+
}, [limit]);
|
|
445
|
+
|
|
446
|
+
useEffect(() => {
|
|
447
|
+
offsetRef.current = offset;
|
|
448
|
+
}, [offset]);
|
|
449
|
+
|
|
393
450
|
// Single data collection for holding results
|
|
394
451
|
const [data, setData] = useState<TWithRelations[]>([]);
|
|
395
452
|
const [error, setError] = useState<Error | null>(null);
|
|
@@ -773,14 +830,11 @@ export function createSuparismaHook<
|
|
|
773
830
|
|
|
774
831
|
const channelId = channelName || \`changes_to_\${tableName}_\${Math.random().toString(36).substring(2, 15)}\`;
|
|
775
832
|
|
|
776
|
-
//
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
const
|
|
780
|
-
|
|
781
|
-
const currentOffset = offset;
|
|
782
|
-
|
|
783
|
-
console.log(\`Setting up subscription for \${tableName} with filter: \${currentFilter}\`);
|
|
833
|
+
// Use a dynamic filter string builder inside the event handler or rely on Supabase
|
|
834
|
+
// For the subscription filter, we must use the initial computedFilter or a stable one.
|
|
835
|
+
// However, for client-side logic (sorting, adding/removing from list), we use refs.
|
|
836
|
+
const initialComputedFilter = where ? buildFilterString(where) : realtimeFilter;
|
|
837
|
+
console.log(\`Setting up subscription for \${tableName} with initial filter: \${initialComputedFilter}\`);
|
|
784
838
|
|
|
785
839
|
const channel = supabase
|
|
786
840
|
.channel(channelId)
|
|
@@ -790,11 +844,17 @@ export function createSuparismaHook<
|
|
|
790
844
|
event: '*',
|
|
791
845
|
schema: 'public',
|
|
792
846
|
table: tableName,
|
|
793
|
-
filter:
|
|
847
|
+
filter: initialComputedFilter, // Subscription filter uses initial state
|
|
794
848
|
},
|
|
795
849
|
(payload) => {
|
|
796
850
|
console.log(\`Received \${payload.eventType} event for \${tableName}\`, payload);
|
|
797
851
|
|
|
852
|
+
// Access current options via refs inside the event handler
|
|
853
|
+
const currentWhere = whereRef.current;
|
|
854
|
+
const currentOrderBy = orderByRef.current;
|
|
855
|
+
const currentLimit = limitRef.current;
|
|
856
|
+
const currentOffset = offsetRef.current; // Not directly used in handlers but good for consistency
|
|
857
|
+
|
|
798
858
|
// Skip realtime updates when search is active
|
|
799
859
|
if (isSearchingRef.current) return;
|
|
800
860
|
|
|
@@ -806,7 +866,7 @@ export function createSuparismaHook<
|
|
|
806
866
|
console.log(\`Processing INSERT for \${tableName}\`, { newRecord });
|
|
807
867
|
|
|
808
868
|
// Check if this record matches our filter if we have one
|
|
809
|
-
if (currentWhere) {
|
|
869
|
+
if (currentWhere) { // Use ref value
|
|
810
870
|
let matchesFilter = true;
|
|
811
871
|
|
|
812
872
|
// Check each filter condition
|
|
@@ -838,27 +898,43 @@ export function createSuparismaHook<
|
|
|
838
898
|
}
|
|
839
899
|
|
|
840
900
|
// Add the new record to the data
|
|
841
|
-
let newData = [
|
|
901
|
+
let newData = [...prev, newRecord]; // Changed: Use spread on prev for immutability
|
|
842
902
|
|
|
843
903
|
// Apply ordering if specified
|
|
844
|
-
if (currentOrderBy) {
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
904
|
+
if (currentOrderBy) { // Use ref value
|
|
905
|
+
// Convert orderBy to array format for consistency if it's an object
|
|
906
|
+
const orderByArray = Array.isArray(currentOrderBy)
|
|
907
|
+
? currentOrderBy
|
|
908
|
+
: [currentOrderBy];
|
|
909
|
+
|
|
910
|
+
// Apply each sort in sequence
|
|
911
|
+
newData = [...newData].sort((a, b) => {
|
|
912
|
+
// Check each orderBy clause in sequence
|
|
913
|
+
for (const orderByClause of orderByArray) {
|
|
914
|
+
for (const [field, direction] of Object.entries(orderByClause)) {
|
|
915
|
+
const aValue = a[field as keyof typeof a];
|
|
916
|
+
const bValue = b[field as keyof typeof b];
|
|
917
|
+
|
|
918
|
+
// Skip if values are equal and move to next criterion
|
|
919
|
+
if (aValue === bValue) continue;
|
|
920
|
+
|
|
921
|
+
// Use the compareValues function for proper type handling
|
|
922
|
+
return compareValues(aValue, bValue, direction as 'asc' | 'desc');
|
|
855
923
|
}
|
|
856
|
-
}
|
|
857
|
-
|
|
924
|
+
}
|
|
925
|
+
return 0; // Equal if all criteria match
|
|
926
|
+
});
|
|
927
|
+
} else if (hasCreatedAt) {
|
|
928
|
+
// Default sort by createdAt desc if no explicit sort but has timestamp
|
|
929
|
+
newData = [...newData].sort((a, b) => {
|
|
930
|
+
const aValue = a[createdAtField as keyof typeof a];
|
|
931
|
+
const bValue = b[createdAtField as keyof typeof b];
|
|
932
|
+
return compareValues(aValue, bValue, 'desc');
|
|
933
|
+
});
|
|
858
934
|
}
|
|
859
935
|
|
|
860
936
|
// Apply limit if specified
|
|
861
|
-
if (currentLimit && currentLimit > 0) {
|
|
937
|
+
if (currentLimit && currentLimit > 0) { // Use ref value
|
|
862
938
|
newData = newData.slice(0, currentLimit);
|
|
863
939
|
}
|
|
864
940
|
|
|
@@ -874,6 +950,10 @@ export function createSuparismaHook<
|
|
|
874
950
|
} else if (payload.eventType === 'UPDATE') {
|
|
875
951
|
// Process update event
|
|
876
952
|
setData((prev) => {
|
|
953
|
+
// Access current options via refs
|
|
954
|
+
const currentOrderBy = orderByRef.current;
|
|
955
|
+
const currentLimit = limitRef.current; // If needed for re-fetch logic on update
|
|
956
|
+
|
|
877
957
|
// Skip if search is active
|
|
878
958
|
if (isSearchingRef.current) {
|
|
879
959
|
return prev;
|
|
@@ -886,15 +966,57 @@ export function createSuparismaHook<
|
|
|
886
966
|
: item
|
|
887
967
|
);
|
|
888
968
|
|
|
969
|
+
// Apply ordering again after update to ensure consistency
|
|
970
|
+
let sortedData = [...newData];
|
|
971
|
+
|
|
972
|
+
// Apply ordering if specified
|
|
973
|
+
if (currentOrderBy) { // Use ref value
|
|
974
|
+
// Convert orderBy to array format for consistency if it's an object
|
|
975
|
+
const orderByArray = Array.isArray(currentOrderBy)
|
|
976
|
+
? currentOrderBy
|
|
977
|
+
: [currentOrderBy];
|
|
978
|
+
|
|
979
|
+
// Apply each sort in sequence
|
|
980
|
+
sortedData = sortedData.sort((a, b) => {
|
|
981
|
+
// Check each orderBy clause in sequence
|
|
982
|
+
for (const orderByClause of orderByArray) {
|
|
983
|
+
for (const [field, direction] of Object.entries(orderByClause)) {
|
|
984
|
+
const aValue = a[field as keyof typeof a];
|
|
985
|
+
const bValue = b[field as keyof typeof b];
|
|
986
|
+
|
|
987
|
+
// Skip if values are equal and move to next criterion
|
|
988
|
+
if (aValue === bValue) continue;
|
|
989
|
+
|
|
990
|
+
// Use the compareValues function for proper type handling
|
|
991
|
+
return compareValues(aValue, bValue, direction as 'asc' | 'desc');
|
|
992
|
+
}
|
|
993
|
+
}
|
|
994
|
+
return 0; // Equal if all criteria match
|
|
995
|
+
});
|
|
996
|
+
} else if (hasCreatedAt) {
|
|
997
|
+
// Default sort by createdAt desc if no explicit sort but has timestamp
|
|
998
|
+
sortedData = sortedData.sort((a, b) => {
|
|
999
|
+
const aValue = a[createdAtField as keyof typeof a];
|
|
1000
|
+
const bValue = b[createdAtField as keyof typeof b];
|
|
1001
|
+
return compareValues(aValue, bValue, 'desc');
|
|
1002
|
+
});
|
|
1003
|
+
}
|
|
1004
|
+
|
|
889
1005
|
// Fetch the updated count after the data changes
|
|
890
1006
|
// For updates, the count might not change but we fetch anyway to be consistent
|
|
891
1007
|
setTimeout(() => fetchTotalCount(), 0);
|
|
892
1008
|
|
|
893
|
-
return
|
|
1009
|
+
return sortedData;
|
|
894
1010
|
});
|
|
895
1011
|
} else if (payload.eventType === 'DELETE') {
|
|
896
1012
|
// Process delete event
|
|
897
1013
|
setData((prev) => {
|
|
1014
|
+
// Access current options via refs
|
|
1015
|
+
const currentWhere = whereRef.current;
|
|
1016
|
+
const currentOrderBy = orderByRef.current;
|
|
1017
|
+
const currentLimit = limitRef.current;
|
|
1018
|
+
const currentOffset = offsetRef.current;
|
|
1019
|
+
|
|
898
1020
|
// Skip if search is active
|
|
899
1021
|
if (isSearchingRef.current) {
|
|
900
1022
|
return prev;
|
|
@@ -913,21 +1035,61 @@ export function createSuparismaHook<
|
|
|
913
1035
|
setTimeout(() => fetchTotalCount(), 0);
|
|
914
1036
|
|
|
915
1037
|
// If we need to maintain the size with a limit
|
|
916
|
-
if (currentLimit && currentLimit > 0 && filteredData.length < currentSize && currentSize === currentLimit) {
|
|
1038
|
+
if (currentLimit && currentLimit > 0 && filteredData.length < currentSize && currentSize === currentLimit) { // Use ref value
|
|
917
1039
|
console.log(\`Record deleted with limit \${currentLimit}, will fetch additional record to maintain size\`);
|
|
918
1040
|
|
|
919
1041
|
// Use setTimeout to ensure this state update completes first
|
|
920
1042
|
setTimeout(() => {
|
|
921
1043
|
findMany({
|
|
922
|
-
where: currentWhere,
|
|
923
|
-
orderBy: currentOrderBy,
|
|
924
|
-
take: currentLimit,
|
|
925
|
-
skip: currentOffset
|
|
1044
|
+
where: currentWhere, // Use ref value
|
|
1045
|
+
orderBy: currentOrderBy, // Use ref value
|
|
1046
|
+
take: currentLimit, // Use ref value
|
|
1047
|
+
skip: currentOffset // Use ref value (passed as skip to findMany)
|
|
926
1048
|
});
|
|
927
1049
|
}, 0);
|
|
1050
|
+
|
|
1051
|
+
// Return the filtered data without resizing for now
|
|
1052
|
+
// The findMany call above will update the data later
|
|
1053
|
+
return filteredData;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// Re-apply ordering to maintain consistency
|
|
1057
|
+
let sortedData = [...filteredData];
|
|
1058
|
+
|
|
1059
|
+
// Apply ordering if specified
|
|
1060
|
+
if (currentOrderBy) { // Use ref value
|
|
1061
|
+
// Convert orderBy to array format for consistency if it's an object
|
|
1062
|
+
const orderByArray = Array.isArray(currentOrderBy)
|
|
1063
|
+
? currentOrderBy
|
|
1064
|
+
: [currentOrderBy];
|
|
1065
|
+
|
|
1066
|
+
// Apply each sort in sequence
|
|
1067
|
+
sortedData = sortedData.sort((a, b) => {
|
|
1068
|
+
// Check each orderBy clause in sequence
|
|
1069
|
+
for (const orderByClause of orderByArray) {
|
|
1070
|
+
for (const [field, direction] of Object.entries(orderByClause)) {
|
|
1071
|
+
const aValue = a[field as keyof typeof a];
|
|
1072
|
+
const bValue = b[field as keyof typeof b];
|
|
1073
|
+
|
|
1074
|
+
// Skip if values are equal and move to next criterion
|
|
1075
|
+
if (aValue === bValue) continue;
|
|
1076
|
+
|
|
1077
|
+
// Use the compareValues function for proper type handling
|
|
1078
|
+
return compareValues(aValue, bValue, direction as 'asc' | 'desc');
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
return 0; // Equal if all criteria match
|
|
1082
|
+
});
|
|
1083
|
+
} else if (hasCreatedAt) {
|
|
1084
|
+
// Default sort by createdAt desc if no explicit sort but has timestamp
|
|
1085
|
+
sortedData = sortedData.sort((a, b) => {
|
|
1086
|
+
const aValue = a[createdAtField as keyof typeof a];
|
|
1087
|
+
const bValue = b[createdAtField as keyof typeof b];
|
|
1088
|
+
return compareValues(aValue, bValue, 'desc');
|
|
1089
|
+
});
|
|
928
1090
|
}
|
|
929
1091
|
|
|
930
|
-
return
|
|
1092
|
+
return sortedData;
|
|
931
1093
|
});
|
|
932
1094
|
}
|
|
933
1095
|
}
|
|
@@ -942,7 +1104,7 @@ export function createSuparismaHook<
|
|
|
942
1104
|
return () => {
|
|
943
1105
|
console.log(\`Unsubscribing from \${channelId}\`);
|
|
944
1106
|
if (channelRef.current) {
|
|
945
|
-
channelRef.current
|
|
1107
|
+
supabase.removeChannel(channelRef.current); // Correct way to remove channel
|
|
946
1108
|
channelRef.current = null;
|
|
947
1109
|
}
|
|
948
1110
|
|
|
@@ -951,7 +1113,7 @@ export function createSuparismaHook<
|
|
|
951
1113
|
searchTimeoutRef.current = null;
|
|
952
1114
|
}
|
|
953
1115
|
};
|
|
954
|
-
}, [realtime, channelName,
|
|
1116
|
+
}, [realtime, channelName, tableName, initialLoadRef]); // Removed where, orderBy, limit, offset from deps
|
|
955
1117
|
|
|
956
1118
|
// Create a memoized options object to prevent unnecessary re-renders
|
|
957
1119
|
const optionsRef = useRef({ where, orderBy, limit, offset });
|
|
@@ -9,11 +9,8 @@ const path_1 = __importDefault(require("path"));
|
|
|
9
9
|
const config_1 = require("../config"); // Ensure this is UTILS_DIR
|
|
10
10
|
function generateSupabaseClientFile() {
|
|
11
11
|
const supabaseClientContent = `// THIS FILE IS AUTO-GENERATED - DO NOT EDIT DIRECTLY
|
|
12
|
-
|
|
13
12
|
import { createClient } from '@supabase/supabase-js';
|
|
14
13
|
|
|
15
|
-
console.log(\`NEXT_PUBLIC_SUPABASE_URL: \${process.env.NEXT_PUBLIC_SUPABASE_URL}\`);
|
|
16
|
-
console.log(\`NEXT_PUBLIC_SUPABASE_ANON_KEY: \${process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY}\`);
|
|
17
14
|
export const supabase = createClient(
|
|
18
15
|
process.env.NEXT_PUBLIC_SUPABASE_URL!,
|
|
19
16
|
process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY!
|
|
@@ -46,14 +46,27 @@ function generateModelTypesFile(model) {
|
|
|
46
46
|
.filter((field) => !relationObjectFields.includes(field.name) && !foreignKeyFields.includes(field.name))
|
|
47
47
|
.map((field) => {
|
|
48
48
|
const isOptional = field.isOptional;
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
49
|
+
let baseType;
|
|
50
|
+
switch (field.type) {
|
|
51
|
+
case 'Int':
|
|
52
|
+
case 'Float':
|
|
53
|
+
baseType = 'number';
|
|
54
|
+
break;
|
|
55
|
+
case 'Boolean':
|
|
56
|
+
baseType = 'boolean';
|
|
57
|
+
break;
|
|
58
|
+
case 'DateTime':
|
|
59
|
+
baseType = 'string'; // ISO date string
|
|
60
|
+
break;
|
|
61
|
+
case 'Json':
|
|
62
|
+
baseType = 'any'; // Or a more specific structured type if available
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
// Covers String, Enum names (e.g., "SomeEnum"), Bytes, Decimal, etc.
|
|
66
|
+
baseType = 'string';
|
|
67
|
+
}
|
|
68
|
+
const finalType = field.isList ? `${baseType}[]` : baseType;
|
|
69
|
+
return ` ${field.name}${isOptional ? '?' : ''}: ${finalType};`;
|
|
57
70
|
});
|
|
58
71
|
// Add foreign key fields
|
|
59
72
|
foreignKeyFields.forEach((field) => {
|
|
@@ -70,14 +83,27 @@ function generateModelTypesFile(model) {
|
|
|
70
83
|
.map((field) => {
|
|
71
84
|
// Make fields with default values optional in CreateInput
|
|
72
85
|
const isOptional = field.isOptional || defaultValueFields.includes(field.name);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
86
|
+
let baseType;
|
|
87
|
+
switch (field.type) {
|
|
88
|
+
case 'Int':
|
|
89
|
+
case 'Float':
|
|
90
|
+
baseType = 'number';
|
|
91
|
+
break;
|
|
92
|
+
case 'Boolean':
|
|
93
|
+
baseType = 'boolean';
|
|
94
|
+
break;
|
|
95
|
+
case 'DateTime':
|
|
96
|
+
baseType = 'string'; // ISO date string
|
|
97
|
+
break;
|
|
98
|
+
case 'Json':
|
|
99
|
+
baseType = 'any'; // Or a more specific structured type if available
|
|
100
|
+
break;
|
|
101
|
+
default:
|
|
102
|
+
// Covers String, Enum names (e.g., "SomeEnum"), Bytes, Decimal, etc.
|
|
103
|
+
baseType = 'string';
|
|
104
|
+
}
|
|
105
|
+
const finalType = field.isList ? `${baseType}[]` : baseType;
|
|
106
|
+
return ` ${field.name}${isOptional ? '?' : ''}: ${finalType};`;
|
|
81
107
|
});
|
|
82
108
|
// Add foreign key fields to CreateInput
|
|
83
109
|
foreignKeyFields.forEach((field) => {
|