suparisma 1.2.3 → 1.2.6

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.
Files changed (130) hide show
  1. package/dist/generators/coreGenerator.js +74 -16
  2. package/dist/generators/hookGenerator.js +6 -2
  3. package/dist/generators/typeGenerator.js +43 -31
  4. package/dist/index.js +6 -1
  5. package/package.json +1 -1
  6. package/tmp/generated-test/hooks/useSuparismaAsset.ts +94 -0
  7. package/tmp/generated-test/hooks/useSuparismaChapter.ts +96 -0
  8. package/tmp/generated-test/hooks/useSuparismaCourse.ts +96 -0
  9. package/tmp/generated-test/hooks/useSuparismaDeviceSession.ts +94 -0
  10. package/tmp/generated-test/hooks/useSuparismaEnrollment.ts +92 -0
  11. package/tmp/generated-test/hooks/useSuparismaLesson.ts +96 -0
  12. package/tmp/generated-test/hooks/useSuparismaLessonPurchase.ts +92 -0
  13. package/tmp/generated-test/hooks/useSuparismaLessonQuestion.ts +96 -0
  14. package/tmp/generated-test/hooks/useSuparismaPayoutMethod.ts +96 -0
  15. package/tmp/generated-test/hooks/useSuparismaPayoutRequest.ts +96 -0
  16. package/tmp/generated-test/hooks/useSuparismaQuestionOption.ts +92 -0
  17. package/tmp/generated-test/hooks/useSuparismaSavedPaymentMethod.ts +96 -0
  18. package/tmp/generated-test/hooks/useSuparismaTeacherPayoutInfo.ts +96 -0
  19. package/tmp/generated-test/hooks/useSuparismaThing.ts +96 -0
  20. package/tmp/generated-test/hooks/useSuparismaUser.ts +96 -0
  21. package/tmp/generated-test/hooks/useSuparismaVideoNote.ts +96 -0
  22. package/tmp/generated-test/hooks/useSuparismaWallet.ts +96 -0
  23. package/tmp/generated-test/hooks/useSuparismaWalletTransaction.ts +96 -0
  24. package/tmp/generated-test/hooks/useSuparismaWatchProgress.ts +96 -0
  25. package/tmp/generated-test/index.ts +140 -0
  26. package/tmp/generated-test/types/AssetTypes.ts +485 -0
  27. package/tmp/generated-test/types/ChapterTypes.ts +488 -0
  28. package/tmp/generated-test/types/CourseTypes.ts +519 -0
  29. package/tmp/generated-test/types/DeviceSessionTypes.ts +489 -0
  30. package/tmp/generated-test/types/EnrollmentTypes.ts +495 -0
  31. package/tmp/generated-test/types/LessonPurchaseTypes.ts +490 -0
  32. package/tmp/generated-test/types/LessonQuestionTypes.ts +496 -0
  33. package/tmp/generated-test/types/LessonTypes.ts +517 -0
  34. package/tmp/generated-test/types/PayoutMethodTypes.ts +517 -0
  35. package/tmp/generated-test/types/PayoutRequestTypes.ts +528 -0
  36. package/tmp/generated-test/types/QuestionOptionTypes.ts +479 -0
  37. package/tmp/generated-test/types/SavedPaymentMethodTypes.ts +497 -0
  38. package/tmp/generated-test/types/TeacherPayoutInfoTypes.ts +480 -0
  39. package/tmp/generated-test/types/ThingTypes.ts +482 -0
  40. package/tmp/generated-test/types/UserTypes.ts +487 -0
  41. package/tmp/generated-test/types/VideoNoteTypes.ts +489 -0
  42. package/tmp/generated-test/types/WalletTransactionTypes.ts +505 -0
  43. package/tmp/generated-test/types/WalletTypes.ts +480 -0
  44. package/tmp/generated-test/types/WatchProgressTypes.ts +493 -0
  45. package/tmp/generated-test/utils/core.ts +2306 -0
  46. package/tmp/generated-test/utils/supabase-client.ts +17 -0
  47. package/tmp/generated-test2/hooks/useSuparismaAsset.ts +94 -0
  48. package/tmp/generated-test2/hooks/useSuparismaChapter.ts +96 -0
  49. package/tmp/generated-test2/hooks/useSuparismaCourse.ts +96 -0
  50. package/tmp/generated-test2/hooks/useSuparismaDeviceSession.ts +94 -0
  51. package/tmp/generated-test2/hooks/useSuparismaEnrollment.ts +92 -0
  52. package/tmp/generated-test2/hooks/useSuparismaLesson.ts +96 -0
  53. package/tmp/generated-test2/hooks/useSuparismaLessonPurchase.ts +92 -0
  54. package/tmp/generated-test2/hooks/useSuparismaLessonQuestion.ts +96 -0
  55. package/tmp/generated-test2/hooks/useSuparismaPayoutMethod.ts +96 -0
  56. package/tmp/generated-test2/hooks/useSuparismaPayoutRequest.ts +96 -0
  57. package/tmp/generated-test2/hooks/useSuparismaQuestionOption.ts +92 -0
  58. package/tmp/generated-test2/hooks/useSuparismaSavedPaymentMethod.ts +96 -0
  59. package/tmp/generated-test2/hooks/useSuparismaTeacherPayoutInfo.ts +96 -0
  60. package/tmp/generated-test2/hooks/useSuparismaThing.ts +96 -0
  61. package/tmp/generated-test2/hooks/useSuparismaUser.ts +96 -0
  62. package/tmp/generated-test2/hooks/useSuparismaVideoNote.ts +96 -0
  63. package/tmp/generated-test2/hooks/useSuparismaWallet.ts +96 -0
  64. package/tmp/generated-test2/hooks/useSuparismaWalletTransaction.ts +96 -0
  65. package/tmp/generated-test2/hooks/useSuparismaWatchProgress.ts +96 -0
  66. package/tmp/generated-test2/index.ts +140 -0
  67. package/tmp/generated-test2/types/AssetTypes.ts +485 -0
  68. package/tmp/generated-test2/types/ChapterTypes.ts +488 -0
  69. package/tmp/generated-test2/types/CourseTypes.ts +522 -0
  70. package/tmp/generated-test2/types/DeviceSessionTypes.ts +489 -0
  71. package/tmp/generated-test2/types/EnrollmentTypes.ts +495 -0
  72. package/tmp/generated-test2/types/LessonPurchaseTypes.ts +490 -0
  73. package/tmp/generated-test2/types/LessonQuestionTypes.ts +496 -0
  74. package/tmp/generated-test2/types/LessonTypes.ts +517 -0
  75. package/tmp/generated-test2/types/PayoutMethodTypes.ts +517 -0
  76. package/tmp/generated-test2/types/PayoutRequestTypes.ts +528 -0
  77. package/tmp/generated-test2/types/QuestionOptionTypes.ts +479 -0
  78. package/tmp/generated-test2/types/SavedPaymentMethodTypes.ts +497 -0
  79. package/tmp/generated-test2/types/TeacherPayoutInfoTypes.ts +480 -0
  80. package/tmp/generated-test2/types/ThingTypes.ts +482 -0
  81. package/tmp/generated-test2/types/UserTypes.ts +490 -0
  82. package/tmp/generated-test2/types/VideoNoteTypes.ts +489 -0
  83. package/tmp/generated-test2/types/WalletTransactionTypes.ts +505 -0
  84. package/tmp/generated-test2/types/WalletTypes.ts +480 -0
  85. package/tmp/generated-test2/types/WatchProgressTypes.ts +493 -0
  86. package/tmp/generated-test2/utils/core.ts +2306 -0
  87. package/tmp/generated-test2/utils/supabase-client.ts +17 -0
  88. package/tmp/generated-test3/hooks/useSuparismaAsset.ts +94 -0
  89. package/tmp/generated-test3/hooks/useSuparismaChapter.ts +98 -0
  90. package/tmp/generated-test3/hooks/useSuparismaCourse.ts +98 -0
  91. package/tmp/generated-test3/hooks/useSuparismaDeviceSession.ts +94 -0
  92. package/tmp/generated-test3/hooks/useSuparismaEnrollment.ts +94 -0
  93. package/tmp/generated-test3/hooks/useSuparismaLesson.ts +98 -0
  94. package/tmp/generated-test3/hooks/useSuparismaLessonPurchase.ts +94 -0
  95. package/tmp/generated-test3/hooks/useSuparismaLessonQuestion.ts +98 -0
  96. package/tmp/generated-test3/hooks/useSuparismaPayoutMethod.ts +96 -0
  97. package/tmp/generated-test3/hooks/useSuparismaPayoutRequest.ts +96 -0
  98. package/tmp/generated-test3/hooks/useSuparismaQuestionOption.ts +94 -0
  99. package/tmp/generated-test3/hooks/useSuparismaSavedPaymentMethod.ts +96 -0
  100. package/tmp/generated-test3/hooks/useSuparismaTeacherPayoutInfo.ts +98 -0
  101. package/tmp/generated-test3/hooks/useSuparismaThing.ts +96 -0
  102. package/tmp/generated-test3/hooks/useSuparismaUser.ts +98 -0
  103. package/tmp/generated-test3/hooks/useSuparismaVideoNote.ts +98 -0
  104. package/tmp/generated-test3/hooks/useSuparismaWallet.ts +98 -0
  105. package/tmp/generated-test3/hooks/useSuparismaWalletTransaction.ts +98 -0
  106. package/tmp/generated-test3/hooks/useSuparismaWatchProgress.ts +98 -0
  107. package/tmp/generated-test3/index.ts +140 -0
  108. package/tmp/generated-test3/types/AssetTypes.ts +485 -0
  109. package/tmp/generated-test3/types/ChapterTypes.ts +488 -0
  110. package/tmp/generated-test3/types/CourseTypes.ts +522 -0
  111. package/tmp/generated-test3/types/DeviceSessionTypes.ts +489 -0
  112. package/tmp/generated-test3/types/EnrollmentTypes.ts +495 -0
  113. package/tmp/generated-test3/types/LessonPurchaseTypes.ts +490 -0
  114. package/tmp/generated-test3/types/LessonQuestionTypes.ts +496 -0
  115. package/tmp/generated-test3/types/LessonTypes.ts +517 -0
  116. package/tmp/generated-test3/types/PayoutMethodTypes.ts +517 -0
  117. package/tmp/generated-test3/types/PayoutRequestTypes.ts +528 -0
  118. package/tmp/generated-test3/types/QuestionOptionTypes.ts +479 -0
  119. package/tmp/generated-test3/types/SavedPaymentMethodTypes.ts +497 -0
  120. package/tmp/generated-test3/types/TeacherPayoutInfoTypes.ts +480 -0
  121. package/tmp/generated-test3/types/ThingTypes.ts +482 -0
  122. package/tmp/generated-test3/types/UserTypes.ts +490 -0
  123. package/tmp/generated-test3/types/VideoNoteTypes.ts +489 -0
  124. package/tmp/generated-test3/types/WalletTransactionTypes.ts +505 -0
  125. package/tmp/generated-test3/types/WalletTypes.ts +480 -0
  126. package/tmp/generated-test3/types/WatchProgressTypes.ts +493 -0
  127. package/tmp/generated-test3/utils/core.ts +2316 -0
  128. package/tmp/generated-test3/utils/supabase-client.ts +17 -0
  129. package/tmp/prisma-test-schema-2.prisma +339 -0
  130. package/tmp/prisma-test-schema.prisma +317 -0
@@ -194,11 +194,14 @@ export type SelectInput<T> = {
194
194
  * // Include a relation with specific fields
195
195
  * { posts: { select: { id: true, title: true } } }
196
196
  */
197
- export type IncludeInput = {
198
- [key: string]: boolean | { select?: Record<string, boolean> };
199
- };
197
+ export type IncludeValue = boolean | { select?: Record<string, boolean> };
200
198
 
201
- export type SuparismaOptions<TWhereInput, TOrderByInput, TSelectInput = Record<string, boolean>> = {
199
+ export type SuparismaOptions<
200
+ TWhereInput,
201
+ TOrderByInput,
202
+ TSelectInput = Record<string, boolean>,
203
+ TIncludeInput = Record<never, never>
204
+ > = {
202
205
  /** Whether to enable realtime updates (default: true) */
203
206
  realtime?: boolean;
204
207
  /** Custom channel name for realtime subscription */
@@ -222,7 +225,14 @@ export type SuparismaOptions<TWhereInput, TOrderByInput, TSelectInput = Record<s
222
225
  * Include related records (foreign key relations).
223
226
  * @example { posts: true } or { posts: { select: { id: true, title: true } } }
224
227
  */
225
- include?: IncludeInput;
228
+ include?: TIncludeInput;
229
+ /**
230
+ * Whether to enable the hook (default: true).
231
+ * When false, the hook will not fetch data or set up realtime subscriptions.
232
+ * Useful for conditional fetching, e.g., waiting for auth/user data to be ready.
233
+ * @example enabled: !!user?.id
234
+ */
235
+ enabled?: boolean;
226
236
  };
227
237
 
228
238
  /**
@@ -794,7 +804,8 @@ function matchesFilter<T>(record: any, filter: T): boolean {
794
804
  */
795
805
  export function buildSelectString<TSelect, TInclude>(
796
806
  select?: TSelect,
797
- include?: TInclude
807
+ include?: TInclude,
808
+ relationMappings?: Record<string, string>
798
809
  ): string {
799
810
  const parts: string[] = [];
800
811
 
@@ -812,9 +823,16 @@ export function buildSelectString<TSelect, TInclude>(
812
823
  // Handle include - add related records
813
824
  if (include && typeof include === 'object') {
814
825
  for (const [relationName, relationValue] of Object.entries(include)) {
826
+ const relatedTableName = relationMappings?.[relationName] || relationName;
827
+ // If mapping exists, use PostgREST alias syntax: alias:foreignTable(...)
828
+ const embedName =
829
+ relationMappings?.[relationName] && relatedTableName !== relationName
830
+ ? \`\${relationName}:\${relatedTableName}\`
831
+ : relationName;
832
+
815
833
  if (relationValue === true) {
816
834
  // Include all fields from the relation
817
- parts.push(\`\${relationName}(*)\`);
835
+ parts.push(\`\${embedName}(*)\`);
818
836
  } else if (typeof relationValue === 'object' && relationValue !== null) {
819
837
  // Include specific fields from the relation
820
838
  const relationOptions = relationValue as { select?: Record<string, boolean> };
@@ -824,12 +842,12 @@ export function buildSelectString<TSelect, TInclude>(
824
842
  .map(([key]) => key);
825
843
 
826
844
  if (relationFields.length > 0) {
827
- parts.push(\`\${relationName}(\${relationFields.join(',')})\`);
845
+ parts.push(\`\${embedName}(\${relationFields.join(',')})\`);
828
846
  } else {
829
- parts.push(\`\${relationName}(*)\`);
847
+ parts.push(\`\${embedName}(*)\`);
830
848
  }
831
849
  } else {
832
- parts.push(\`\${relationName}(*)\`);
850
+ parts.push(\`\${embedName}(*)\`);
833
851
  }
834
852
  }
835
853
  }
@@ -904,6 +922,7 @@ export function createSuparismaHook<
904
922
  defaultValues?: Record<string, string>;
905
923
  createdAtField?: string;
906
924
  updatedAtField?: string;
925
+ relationMappings?: Record<string, string>;
907
926
  }) {
908
927
  const {
909
928
  tableName,
@@ -912,7 +931,8 @@ export function createSuparismaHook<
912
931
  searchFields = [],
913
932
  defaultValues = {},
914
933
  createdAtField = 'createdAt',
915
- updatedAtField = 'updatedAt'
934
+ updatedAtField = 'updatedAt',
935
+ relationMappings = {}
916
936
  } = config;
917
937
 
918
938
  /**
@@ -945,10 +965,11 @@ export function createSuparismaHook<
945
965
  offset,
946
966
  select,
947
967
  include,
968
+ enabled = true,
948
969
  } = options;
949
970
 
950
971
  // Build the select string once for reuse
951
- const selectString = buildSelectString(select, include);
972
+ const selectString = buildSelectString(select, include, relationMappings);
952
973
 
953
974
  // Refs to store the latest options for realtime handlers
954
975
  const whereRef = useRef(where);
@@ -956,6 +977,7 @@ export function createSuparismaHook<
956
977
  const limitRef = useRef(limit);
957
978
  const offsetRef = useRef(offset);
958
979
  const selectStringRef = useRef(selectString);
980
+ const enabledRef = useRef(enabled);
959
981
 
960
982
  // Update refs whenever options change
961
983
  useEffect(() => {
@@ -978,6 +1000,10 @@ export function createSuparismaHook<
978
1000
  selectStringRef.current = selectString;
979
1001
  }, [selectString]);
980
1002
 
1003
+ useEffect(() => {
1004
+ enabledRef.current = enabled;
1005
+ }, [enabled]);
1006
+
981
1007
  // Single data collection for holding results
982
1008
  const [data, setData] = useState<TWithRelations[]>([]);
983
1009
  const [error, setError] = useState<Error | null>(null);
@@ -998,7 +1024,8 @@ export function createSuparismaHook<
998
1024
  // Function to fetch the total count from Supabase with current filters
999
1025
  const fetchTotalCount = useCallback(async () => {
1000
1026
  try {
1001
- // Skip count updates during search
1027
+ // Skip count updates when disabled or during search
1028
+ if (!enabledRef.current) return;
1002
1029
  if (isSearchingRef.current) return;
1003
1030
 
1004
1031
  let countQuery = supabase.from(tableName).select('*', { count: 'exact', head: true });
@@ -1429,7 +1456,8 @@ export function createSuparismaHook<
1429
1456
 
1430
1457
  // Set up realtime subscription for the list - ONCE and listen to ALL events
1431
1458
  useEffect(() => {
1432
- if (!realtime) return;
1459
+ // Skip subscription if not enabled or realtime is off
1460
+ if (!enabled || !realtime) return;
1433
1461
 
1434
1462
  // Clean up previous subscription if it exists
1435
1463
  if (channelRef.current) {
@@ -1731,7 +1759,7 @@ export function createSuparismaHook<
1731
1759
  searchTimeoutRef.current = null;
1732
1760
  }
1733
1761
  };
1734
- }, [realtime, channelName, tableName]); // NEVER include 'where' - subscription should persist
1762
+ }, [realtime, channelName, tableName, enabled]); // NEVER include 'where' - subscription should persist
1735
1763
 
1736
1764
  // Create a memoized options object to prevent unnecessary re-renders
1737
1765
  const optionsRef = useRef({ where, orderBy, limit, offset, selectString });
@@ -1763,6 +1791,12 @@ export function createSuparismaHook<
1763
1791
 
1764
1792
  // Load initial data and refetch when options change (BUT NEVER TOUCH SUBSCRIPTION)
1765
1793
  useEffect(() => {
1794
+ // Skip fetching if not enabled
1795
+ if (!enabled) {
1796
+ setLoading(false); // Don't show loading spinner when disabled
1797
+ return;
1798
+ }
1799
+
1766
1800
  // Skip if search is active
1767
1801
  if (isSearchingRef.current) return;
1768
1802
 
@@ -1795,7 +1829,31 @@ export function createSuparismaHook<
1795
1829
 
1796
1830
  // Initial count fetch
1797
1831
  fetchTotalCount();
1798
- }, [findMany, where, orderBy, limit, offset, optionsChanged, fetchTotalCount]);
1832
+ }, [findMany, where, orderBy, limit, offset, optionsChanged, fetchTotalCount, enabled]);
1833
+
1834
+ // Track previous enabled state to detect changes from false to true
1835
+ const prevEnabledRef = useRef(enabled);
1836
+
1837
+ // Fetch when enabled changes from false to true
1838
+ useEffect(() => {
1839
+ const wasDisabled = !prevEnabledRef.current;
1840
+ const isNowEnabled = enabled;
1841
+
1842
+ // Update the previous value
1843
+ prevEnabledRef.current = enabled;
1844
+
1845
+ // If we just became enabled and have already done initial load, refetch
1846
+ if (wasDisabled && isNowEnabled && initialLoadRef.current) {
1847
+ console.log(\`Hook enabled for \${tableName}, fetching data\`);
1848
+ findMany({
1849
+ where,
1850
+ orderBy,
1851
+ take: limit,
1852
+ skip: offset
1853
+ });
1854
+ fetchTotalCount();
1855
+ }
1856
+ }, [enabled, findMany, where, orderBy, limit, offset, fetchTotalCount]);
1799
1857
 
1800
1858
  /**
1801
1859
  * Create a new record with the provided data.
@@ -16,7 +16,7 @@ const config_1 = require("../config");
16
16
  * @param modelInfo - Processed model information with metadata
17
17
  */
18
18
  function generateModelHookFile(modelInfo) {
19
- const { modelName, tableName, hasCreatedAt, hasUpdatedAt, searchFields, defaultValues, createdAtField = 'createdAt', // Default to camelCase but use actual field name if provided
19
+ const { modelName, tableName, hasCreatedAt, hasUpdatedAt, searchFields, defaultValues, relationMappings, createdAtField = 'createdAt', // Default to camelCase but use actual field name if provided
20
20
  updatedAtField = 'updatedAt' // Default to camelCase but use actual field name if provided
21
21
  } = modelInfo;
22
22
  // Configure search fields if available
@@ -29,6 +29,10 @@ function generateModelHookFile(modelInfo) {
29
29
  : '';
30
30
  // Add createdAt/updatedAt field name config
31
31
  const fieldNamesConfig = `${hasCreatedAt ? `,\n // Field name for createdAt from Prisma schema\n createdAtField: "${createdAtField}"` : ''}${hasUpdatedAt ? `,\n // Field name for updatedAt from Prisma schema\n updatedAtField: "${updatedAtField}"` : ''}`;
32
+ // Relation mappings config (Prisma relation field -> related table name for PostgREST embedding)
33
+ const relationMappingsConfig = relationMappings && Object.keys(relationMappings).length > 0
34
+ ? `,\n // Relation mappings for PostgREST embedding\n relationMappings: ${JSON.stringify(relationMappings)}`
35
+ : '';
32
36
  // Generate the hook content
33
37
  const hookContent = `// THIS FILE IS AUTO-GENERATED - DO NOT EDIT DIRECTLY
34
38
  // Edit the generator script instead: scripts/generate-realtime-hooks.ts
@@ -118,7 +122,7 @@ export const ${config_1.HOOK_NAME_PREFIX}${modelName} = createSuparismaHook<
118
122
  >({
119
123
  tableName: '${tableName}',
120
124
  hasCreatedAt: ${hasCreatedAt},
121
- hasUpdatedAt: ${hasUpdatedAt}${searchConfig}${defaultValuesConfig}${fieldNamesConfig}
125
+ hasUpdatedAt: ${hasUpdatedAt}${searchConfig}${defaultValuesConfig}${fieldNamesConfig}${relationMappingsConfig}
122
126
  }) as unknown as (options?: Use${modelName}Options) => ${modelName}HookApi;
123
127
  `;
124
128
  // Output to the HOOKS_DIR
@@ -10,7 +10,7 @@ const config_1 = require("../config");
10
10
  /**
11
11
  * Generate model-specific types for a model
12
12
  */
13
- function generateModelTypesFile(model) {
13
+ function generateModelTypesFile(model, modelNameToTableName = {}) {
14
14
  const modelName = model.name || '';
15
15
  const tableName = model.mappedName || modelName;
16
16
  // Identify foreign key fields (these end with _id)
@@ -21,6 +21,13 @@ function generateModelTypesFile(model) {
21
21
  const relationObjectFields = model.fields
22
22
  .filter((field) => field.isRelation && !foreignKeyFields.includes(field.name))
23
23
  .map((field) => field.name);
24
+ // Collect relation fields with their target model types (for include + WithRelations typing)
25
+ const relationFields = model.fields.filter((field) => relationObjectFields.includes(field.name) && field.type !== modelName);
26
+ // Map relation field name -> actual related table name (for PostgREST embedding)
27
+ const relationMappings = {};
28
+ for (const rel of relationFields) {
29
+ relationMappings[rel.name] = modelNameToTableName[rel.type] || rel.type;
30
+ }
24
31
  // Fields that have default values (should be optional in CreateInput)
25
32
  const defaultValueFields = model.fields
26
33
  .filter((field) => field.hasDefaultValue)
@@ -82,7 +89,7 @@ function generateModelTypesFile(model) {
82
89
  };
83
90
  // Create a manual property list for WithRelations interface
84
91
  const withRelationsProps = model.fields
85
- .filter((field) => !relationObjectFields.includes(field.name) && !foreignKeyFields.includes(field.name))
92
+ .filter((field) => !relationObjectFields.includes(field.name))
86
93
  .map((field) => {
87
94
  const isOptional = field.isOptional;
88
95
  const finalType = getFieldType(field);
@@ -95,6 +102,20 @@ function generateModelTypesFile(model) {
95
102
  withRelationsProps.push(` ${field}${fieldInfo.isOptional ? '?' : ''}: ${fieldInfo.type === 'Int' ? 'number' : 'string'};`);
96
103
  }
97
104
  });
105
+ // Add relation object fields as OPTIONAL (only present when included via include/select)
106
+ relationFields.forEach((field) => {
107
+ const relatedModel = field.type;
108
+ const relatedType = field.isList
109
+ ? `${relatedModel}WithRelations[]`
110
+ : `${relatedModel}WithRelations${field.isOptional ? ' | null' : ''}`;
111
+ withRelationsProps.push(` ${field.name}?: ${relatedType};`);
112
+ });
113
+ // Build type-only imports for related model types (to type relations and include.select)
114
+ const relatedModelImports = Array.from(new Set(relationFields.map((f) => f.type).filter((t) => t && t !== modelName)));
115
+ // Note: We can't import multiple files in one statement. We'll generate one per model below.
116
+ const relationTypeImportStatements = relatedModelImports
117
+ .map((m) => `import type { ${m}WithRelations, ${m}SelectInput } from './${m}Types';`)
118
+ .join('\n');
98
119
  // Create a manual property list for CreateInput
99
120
  const createInputProps = model.fields
100
121
  .filter((field) => !relationObjectFields.includes(field.name) &&
@@ -142,7 +163,8 @@ function generateModelTypesFile(model) {
142
163
  // Edit the generator script instead
143
164
 
144
165
  ${customImports}import type { ${modelName} } from '@prisma/client';
145
- import type { ModelResult, SuparismaOptions, SearchQuery, SearchState, FilterOperators } from '../utils/core';
166
+ import type { ModelResult, SuparismaOptions, SearchQuery, SearchState, FilterOperators, IncludeValue } from '../utils/core';
167
+ ${relationTypeImportStatements}
146
168
 
147
169
  /**
148
170
  * Extended ${modelName} type that includes relation fields.
@@ -349,41 +371,28 @@ export type ${modelName}WhereUniqueInput = {
349
371
  * });
350
372
  */
351
373
  export type ${modelName}OrderByInput = {
352
- [key in keyof ${modelName}WithRelations]?: 'asc' | 'desc';
374
+ [key in keyof ${modelName}SelectInput]?: 'asc' | 'desc';
353
375
  };
354
376
 
355
377
  /**
356
- * Select specific fields to return from ${modelName} queries.
357
- * Set fields to \`true\` to include them in the response.
358
- *
359
- * @example
360
- * // Only return id and name
361
- * ${modelName.toLowerCase()}.findMany({
362
- * select: { id: true, name: true }
363
- * });
378
+ * Select specific scalar fields to return from ${modelName} queries.
379
+ * Relation fields are intentionally excluded; use \`include\` for relations.
364
380
  */
365
381
  export type ${modelName}SelectInput = {
366
- [key in keyof ${modelName}WithRelations]?: boolean;
382
+ ${model.fields
383
+ .filter((f) => !relationObjectFields.includes(f.name))
384
+ .map((f) => ` ${f.name}?: boolean;`)
385
+ .join('\n')}
367
386
  };
368
387
 
369
388
  /**
370
389
  * Include related records when querying ${modelName}.
371
- * Set relation names to \`true\` to include all fields, or use an object to select specific fields.
372
- *
373
- * @example
374
- * // Include all fields from a relation
375
- * ${modelName.toLowerCase()}.findMany({
376
- * include: { relatedModel: true }
377
- * });
378
- *
379
- * @example
380
- * // Include specific fields from a relation
381
- * ${modelName.toLowerCase()}.findMany({
382
- * include: { relatedModel: { select: { id: true, name: true } } }
383
- * });
390
+ * Only real Prisma relation fields are allowed here.
384
391
  */
385
392
  export type ${modelName}IncludeInput = {
386
- [key: string]: boolean | { select?: Record<string, boolean> };
393
+ ${relationFields
394
+ .map((f) => ` ${f.name}?: IncludeValue | { select?: ${f.type}SelectInput };`)
395
+ .join('\n')}
387
396
  };
388
397
 
389
398
  /**
@@ -400,10 +409,12 @@ export type ${modelName}ManyResult = ModelResult<${modelName}WithRelations[]>;
400
409
  * Configuration options for the ${modelName} hook.
401
410
  * Includes where filters, ordering, pagination, and field selection.
402
411
  */
403
- export type Use${modelName}Options = SuparismaOptions<${modelName}WhereInput, ${modelName}OrderByInput, ${modelName}SelectInput> & {
404
- /** Include related records (foreign key relations) */
405
- include?: ${modelName}IncludeInput;
406
- };
412
+ export type Use${modelName}Options = SuparismaOptions<
413
+ ${modelName}WhereInput,
414
+ ${modelName}OrderByInput,
415
+ ${modelName}SelectInput,
416
+ ${modelName}IncludeInput
417
+ >;
407
418
 
408
419
  /**
409
420
  * The complete API for interacting with ${modelName} records.
@@ -726,6 +737,7 @@ ${createInputProps
726
737
  defaultValues: Object.keys(defaultValues).length > 0 ? defaultValues : undefined,
727
738
  createdAtField,
728
739
  updatedAtField,
740
+ relationMappings: Object.keys(relationMappings).length > 0 ? relationMappings : undefined,
729
741
  zodImports: model.zodImports // Pass through zod imports
730
742
  };
731
743
  }
package/dist/index.js CHANGED
@@ -484,9 +484,14 @@ async function generateHooks() {
484
484
  (0, coreGenerator_1.generateCoreFile)();
485
485
  const models = (0, parser_1.parsePrismaSchema)(config_1.PRISMA_SCHEMA_PATH);
486
486
  await configurePrismaTablesForSuparisma(config_1.PRISMA_SCHEMA_PATH);
487
+ // Map Prisma model name -> actual table name (respects @@map)
488
+ const modelNameToTableName = {};
489
+ for (const m of models) {
490
+ modelNameToTableName[m.name] = m.mappedName || m.name;
491
+ }
487
492
  const modelInfos = [];
488
493
  for (const model of models) {
489
- const modelInfo = (0, typeGenerator_1.generateModelTypesFile)(model);
494
+ const modelInfo = (0, typeGenerator_1.generateModelTypesFile)(model, modelNameToTableName);
490
495
  (0, hookGenerator_1.generateModelHookFile)(modelInfo);
491
496
  modelInfos.push(modelInfo);
492
497
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "suparisma",
3
- "version": "1.2.3",
3
+ "version": "1.2.6",
4
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": {
@@ -0,0 +1,94 @@
1
+ // THIS FILE IS AUTO-GENERATED - DO NOT EDIT DIRECTLY
2
+ // Edit the generator script instead: scripts/generate-realtime-hooks.ts
3
+
4
+ // Corrected import for core hook factory
5
+ import { createSuparismaHook } from '../utils/core';
6
+ import type {
7
+ AssetWithRelations,
8
+ AssetCreateInput,
9
+ AssetUpdateInput,
10
+ AssetWhereInput,
11
+ AssetWhereUniqueInput,
12
+ AssetOrderByInput,
13
+ AssetSelectInput,
14
+ AssetIncludeInput,
15
+ AssetHookApi,
16
+ UseAssetOptions
17
+ } from '../types/AssetTypes';
18
+
19
+ /**
20
+ * A Prisma-like hook for interacting with Asset records with real-time capabilities.
21
+ *
22
+ * This hook provides CRUD operations, real-time updates, and search functionality.
23
+ *
24
+ * @param options - Optional configuration options for the hook
25
+ * @returns An object with data state and methods for interacting with Asset records
26
+ *
27
+ * @example
28
+ * // Basic usage - get all Asset records with realtime updates
29
+ * const asset = useSuparismaAsset();
30
+ * const { data, loading, error } = asset;
31
+ *
32
+ * @example
33
+ * // With filtering and ordering
34
+ * const asset = useSuparismaAsset({
35
+ * where: { active: true },
36
+ * orderBy: { createdAt: 'desc' }, // Note: Using actual Prisma field name
37
+ * limit: 10
38
+ * });
39
+ *
40
+ * @example
41
+ * // Create a new record
42
+ * const result = await asset.create({
43
+ * name: "Example Name",
44
+ * // other fields...
45
+ * });
46
+ *
47
+ * @example
48
+ * // Update a record
49
+ * const result = await asset.update({
50
+ * where: { id: "123" },
51
+ * data: { name: "Updated Name" }
52
+ * });
53
+ *
54
+ * @example
55
+ * // Delete a record
56
+ * const result = await asset.delete({ id: "123" });
57
+ *
58
+ * @example
59
+ * // Find records with specific criteria
60
+ * const result = await asset.findMany({
61
+ * where: { // filters },
62
+ * orderBy: { // ordering },
63
+ * take: 20 // limit
64
+ * });
65
+ *
66
+ * @example
67
+ * // Select specific fields only
68
+ * const asset = useSuparismaAsset({
69
+ * select: { id: true, name: true }
70
+ * });
71
+ *
72
+ * @example
73
+ * // Include related records
74
+ * const asset = useSuparismaAsset({
75
+ * include: { relatedModel: true }
76
+ * });
77
+ */
78
+ export const useSuparismaAsset = createSuparismaHook<
79
+ AssetWithRelations,
80
+ AssetWithRelations,
81
+ AssetCreateInput,
82
+ AssetUpdateInput,
83
+ AssetWhereInput,
84
+ AssetWhereUniqueInput,
85
+ AssetOrderByInput
86
+ >({
87
+ tableName: 'Asset',
88
+ hasCreatedAt: true,
89
+ hasUpdatedAt: false,
90
+ // Default values from schema
91
+ defaultValues: {"id":"uuid(","createdAt":"now("},
92
+ // Field name for createdAt from Prisma schema
93
+ createdAtField: "createdAt"
94
+ }) as unknown as (options?: UseAssetOptions) => AssetHookApi;
@@ -0,0 +1,96 @@
1
+ // THIS FILE IS AUTO-GENERATED - DO NOT EDIT DIRECTLY
2
+ // Edit the generator script instead: scripts/generate-realtime-hooks.ts
3
+
4
+ // Corrected import for core hook factory
5
+ import { createSuparismaHook } from '../utils/core';
6
+ import type {
7
+ ChapterWithRelations,
8
+ ChapterCreateInput,
9
+ ChapterUpdateInput,
10
+ ChapterWhereInput,
11
+ ChapterWhereUniqueInput,
12
+ ChapterOrderByInput,
13
+ ChapterSelectInput,
14
+ ChapterIncludeInput,
15
+ ChapterHookApi,
16
+ UseChapterOptions
17
+ } from '../types/ChapterTypes';
18
+
19
+ /**
20
+ * A Prisma-like hook for interacting with Chapter records with real-time capabilities.
21
+ *
22
+ * This hook provides CRUD operations, real-time updates, and search functionality.
23
+ *
24
+ * @param options - Optional configuration options for the hook
25
+ * @returns An object with data state and methods for interacting with Chapter records
26
+ *
27
+ * @example
28
+ * // Basic usage - get all Chapter records with realtime updates
29
+ * const chapter = useSuparismaChapter();
30
+ * const { data, loading, error } = chapter;
31
+ *
32
+ * @example
33
+ * // With filtering and ordering
34
+ * const chapter = useSuparismaChapter({
35
+ * where: { active: true },
36
+ * orderBy: { createdAt: 'desc' }, // Note: Using actual Prisma field name
37
+ * limit: 10
38
+ * });
39
+ *
40
+ * @example
41
+ * // Create a new record
42
+ * const result = await chapter.create({
43
+ * name: "Example Name",
44
+ * // other fields...
45
+ * });
46
+ *
47
+ * @example
48
+ * // Update a record
49
+ * const result = await chapter.update({
50
+ * where: { id: "123" },
51
+ * data: { name: "Updated Name" }
52
+ * });
53
+ *
54
+ * @example
55
+ * // Delete a record
56
+ * const result = await chapter.delete({ id: "123" });
57
+ *
58
+ * @example
59
+ * // Find records with specific criteria
60
+ * const result = await chapter.findMany({
61
+ * where: { // filters },
62
+ * orderBy: { // ordering },
63
+ * take: 20 // limit
64
+ * });
65
+ *
66
+ * @example
67
+ * // Select specific fields only
68
+ * const chapter = useSuparismaChapter({
69
+ * select: { id: true, name: true }
70
+ * });
71
+ *
72
+ * @example
73
+ * // Include related records
74
+ * const chapter = useSuparismaChapter({
75
+ * include: { relatedModel: true }
76
+ * });
77
+ */
78
+ export const useSuparismaChapter = createSuparismaHook<
79
+ ChapterWithRelations,
80
+ ChapterWithRelations,
81
+ ChapterCreateInput,
82
+ ChapterUpdateInput,
83
+ ChapterWhereInput,
84
+ ChapterWhereUniqueInput,
85
+ ChapterOrderByInput
86
+ >({
87
+ tableName: 'Chapter',
88
+ hasCreatedAt: true,
89
+ hasUpdatedAt: true,
90
+ // Default values from schema
91
+ defaultValues: {"id":"uuid(","order":"0","createdAt":"now("},
92
+ // Field name for createdAt from Prisma schema
93
+ createdAtField: "createdAt",
94
+ // Field name for updatedAt from Prisma schema
95
+ updatedAtField: "updatedAt"
96
+ }) as unknown as (options?: UseChapterOptions) => ChapterHookApi;
@@ -0,0 +1,96 @@
1
+ // THIS FILE IS AUTO-GENERATED - DO NOT EDIT DIRECTLY
2
+ // Edit the generator script instead: scripts/generate-realtime-hooks.ts
3
+
4
+ // Corrected import for core hook factory
5
+ import { createSuparismaHook } from '../utils/core';
6
+ import type {
7
+ CourseWithRelations,
8
+ CourseCreateInput,
9
+ CourseUpdateInput,
10
+ CourseWhereInput,
11
+ CourseWhereUniqueInput,
12
+ CourseOrderByInput,
13
+ CourseSelectInput,
14
+ CourseIncludeInput,
15
+ CourseHookApi,
16
+ UseCourseOptions
17
+ } from '../types/CourseTypes';
18
+
19
+ /**
20
+ * A Prisma-like hook for interacting with Course records with real-time capabilities.
21
+ *
22
+ * This hook provides CRUD operations, real-time updates, and search functionality.
23
+ *
24
+ * @param options - Optional configuration options for the hook
25
+ * @returns An object with data state and methods for interacting with Course records
26
+ *
27
+ * @example
28
+ * // Basic usage - get all Course records with realtime updates
29
+ * const course = useSuparismaCourse();
30
+ * const { data, loading, error } = course;
31
+ *
32
+ * @example
33
+ * // With filtering and ordering
34
+ * const course = useSuparismaCourse({
35
+ * where: { active: true },
36
+ * orderBy: { createdAt: 'desc' }, // Note: Using actual Prisma field name
37
+ * limit: 10
38
+ * });
39
+ *
40
+ * @example
41
+ * // Create a new record
42
+ * const result = await course.create({
43
+ * name: "Example Name",
44
+ * // other fields...
45
+ * });
46
+ *
47
+ * @example
48
+ * // Update a record
49
+ * const result = await course.update({
50
+ * where: { id: "123" },
51
+ * data: { name: "Updated Name" }
52
+ * });
53
+ *
54
+ * @example
55
+ * // Delete a record
56
+ * const result = await course.delete({ id: "123" });
57
+ *
58
+ * @example
59
+ * // Find records with specific criteria
60
+ * const result = await course.findMany({
61
+ * where: { // filters },
62
+ * orderBy: { // ordering },
63
+ * take: 20 // limit
64
+ * });
65
+ *
66
+ * @example
67
+ * // Select specific fields only
68
+ * const course = useSuparismaCourse({
69
+ * select: { id: true, name: true }
70
+ * });
71
+ *
72
+ * @example
73
+ * // Include related records
74
+ * const course = useSuparismaCourse({
75
+ * include: { relatedModel: true }
76
+ * });
77
+ */
78
+ export const useSuparismaCourse = createSuparismaHook<
79
+ CourseWithRelations,
80
+ CourseWithRelations,
81
+ CourseCreateInput,
82
+ CourseUpdateInput,
83
+ CourseWhereInput,
84
+ CourseWhereUniqueInput,
85
+ CourseOrderByInput
86
+ >({
87
+ tableName: 'Course',
88
+ hasCreatedAt: true,
89
+ hasUpdatedAt: true,
90
+ // Default values from schema
91
+ defaultValues: {"id":"uuid(","published":"false","createdAt":"now(","allowIndividualLessonPurchase":"false","blockScreenRecording":"true","maxDevices":"0","price":"0","pricingType":"\"paid\"","defaultLessonPrice":"0"},
92
+ // Field name for createdAt from Prisma schema
93
+ createdAtField: "createdAt",
94
+ // Field name for updatedAt from Prisma schema
95
+ updatedAtField: "updatedAt"
96
+ }) as unknown as (options?: UseCourseOptions) => CourseHookApi;