@payez/next-mvp 4.1.9 → 4.1.10
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 +792 -792
- package/dist/api-handlers/auth/refresh.js +38 -15
- package/dist/auth/better-auth.js +7 -18
- package/dist/components/account/MobileNavDrawer.js +18 -18
- package/dist/components/account/UserAvatarMenu.js +1 -1
- package/dist/components/admin/AlertSettingsTab.js +40 -40
- package/dist/components/admin/LoggingSettingsTab.js +28 -28
- package/dist/lib/ensure-fresh-access-token.js +30 -7
- package/dist/lib/idp-fetch.js +25 -9
- package/dist/lib/session-store.d.ts +1 -0
- package/dist/lib/session-store.js +12 -8
- package/dist/pages/login/page.js +28 -28
- package/dist/pages/settings/EnhancedSettingsPage.js +17 -17
- package/package.json +893 -893
- package/scripts/dev-broker.ps1 +35 -35
- package/scripts/dev-local.ps1 +45 -45
- package/src/api/auth-handler.ts +548 -548
- package/src/api-handlers/account/change-password.ts +142 -142
- package/src/api-handlers/account/update-phone.ts +79 -79
- package/src/api-handlers/account/verify-email.ts +125 -125
- package/src/api-handlers/account/verify-sms.ts +125 -125
- package/src/api-handlers/admin/analytics.ts +443 -443
- package/src/api-handlers/admin/audit.ts +223 -223
- package/src/api-handlers/admin/index.ts +63 -63
- package/src/api-handlers/admin/redis-sessions.ts +251 -251
- package/src/api-handlers/admin/sessions.ts +318 -318
- package/src/api-handlers/admin/site-logs.ts +365 -365
- package/src/api-handlers/admin/users.ts +242 -242
- package/src/api-handlers/admin/vibe-data.ts +324 -324
- package/src/api-handlers/auth/refresh.ts +705 -682
- package/src/api-handlers/auth/signout.ts +199 -199
- package/src/api-handlers/auth/status.ts +20 -20
- package/src/api-handlers/auth/update-session.ts +110 -110
- package/src/api-handlers/auth/verify-code.ts +112 -112
- package/src/api-handlers/session/refresh-viability.ts +36 -36
- package/src/api-handlers/test/force-expire.ts +60 -60
- package/src/auth/auth-decision.ts +229 -229
- package/src/auth/better-auth.ts +7 -18
- package/src/auth/route-config.ts +219 -219
- package/src/auth/types/auth-types.ts +555 -555
- package/src/auth/types/index.ts +7 -7
- package/src/auth/utils/idp-client.ts +445 -445
- package/src/auth/utils/index.ts +6 -6
- package/src/auth/utils/token-utils.ts +243 -243
- package/src/client/AuthContext.tsx +143 -143
- package/src/client/fetch-with-auth.ts +47 -47
- package/src/components/SessionSync.tsx +138 -138
- package/src/components/SignalRHealthCheck.tsx +131 -131
- package/src/components/account/MobileNavDrawer.tsx +305 -305
- package/src/components/account/UserAvatarMenu.tsx +266 -266
- package/src/components/account/index.ts +13 -13
- package/src/components/admin/AlertSettingsTab.tsx +728 -728
- package/src/components/admin/AnalyticsTab.tsx +703 -703
- package/src/components/admin/DataBrowserTab.tsx +505 -505
- package/src/components/admin/LoggingSettingsTab.tsx +665 -665
- package/src/components/admin/SessionsTab.tsx +414 -414
- package/src/components/admin/StatsTab.tsx +379 -379
- package/src/components/admin/VibeAdminLayout.tsx +187 -187
- package/src/components/admin/index.ts +59 -59
- package/src/components/auth/FederatedAuthSection.tsx +95 -95
- package/src/components/auth/ModeAwareLoginPage.tsx +135 -135
- package/src/components/auth/ModeAwareSignupPage.tsx +267 -267
- package/src/components/auth/TraditionalAuthSection.tsx +99 -99
- package/src/components/recovery/CompleteStep.tsx +36 -36
- package/src/components/recovery/InitiateRecoveryStep.tsx +68 -68
- package/src/components/recovery/SelectMethodStep.tsx +73 -73
- package/src/components/recovery/SetPasswordStep.tsx +97 -97
- package/src/components/recovery/VerifyCodeStep.tsx +90 -90
- package/src/components/reserved/ReservedRecoveryWarning.tsx +160 -160
- package/src/components/reserved/ReservedStatusBox.tsx +118 -118
- package/src/components/ui/BetaBadge.tsx +58 -58
- package/src/components/ui/Footer.tsx +93 -93
- package/src/config/env.ts +57 -57
- package/src/config/logger.ts +62 -62
- package/src/config/vibe-log-transport.ts +264 -264
- package/src/hooks/useAuth.ts +117 -117
- package/src/hooks/useAuthSettings.ts +97 -97
- package/src/hooks/useAvailableProviders.ts +120 -120
- package/src/hooks/usePasswordValidation.ts +127 -127
- package/src/hooks/usePublicAuthSettings.ts +149 -149
- package/src/hooks/useSessionExpiration.ts +101 -101
- package/src/hooks/useViabilitySession.ts +336 -336
- package/src/index.ts +60 -60
- package/src/lib/api-handler.ts +643 -643
- package/src/lib/app-slug.ts +178 -178
- package/src/lib/ensure-fresh-access-token.ts +358 -335
- package/src/lib/geolocation.ts +265 -265
- package/src/lib/idp-fetch.ts +26 -10
- package/src/lib/jwt-decode.ts +82 -82
- package/src/lib/roles.ts +177 -177
- package/src/lib/session-store.ts +4 -1
- package/src/lib/site-logger.ts +245 -245
- package/src/lib/standardized-client-api.ts +901 -901
- package/src/lib/test-aware-get-token.ts +41 -41
- package/src/lib/user-agent-parser.ts +252 -252
- package/src/logging/api/admin-analytics.ts +51 -51
- package/src/logging/api/audit-log.ts +53 -53
- package/src/logging/components/AdminAnalyticsLayout.tsx +49 -49
- package/src/logging/components/AuditLogViewer.tsx +125 -125
- package/src/logging/components/ErrorMetricsCard.tsx +98 -98
- package/src/logging/components/HealthMetricsCard.tsx +70 -70
- package/src/logging/hooks/useAdminAnalytics.ts +22 -22
- package/src/logging/hooks/useAuditLog.ts +24 -24
- package/src/logging/hooks/useErrorMetrics.ts +40 -40
- package/src/logging/hooks/useHealthMetrics.ts +44 -44
- package/src/logging/index.ts +18 -18
- package/src/logging/types/analytics.ts +81 -81
- package/src/logging/types/audit.ts +31 -31
- package/src/logging/types/index.ts +3 -3
- package/src/middleware/create-middleware.ts +663 -663
- package/src/middleware/rbac-check.ts +279 -279
- package/src/middleware/twofa-presets.ts +224 -224
- package/src/pages/admin-login/page.tsx +221 -221
- package/src/pages/admin-page-permissions/PagePermissionsAdminPage.tsx +527 -527
- package/src/pages/admin-page-permissions/index.ts +7 -7
- package/src/pages/admin-roles/RolesAdminPage.tsx +533 -533
- package/src/pages/admin-roles/modals.tsx +469 -469
- package/src/pages/client-admin/ClientSiteAdminPage.tsx +382 -382
- package/src/pages/client-admin/index.ts +33 -33
- package/src/pages/coming-soon/page.tsx +83 -83
- package/src/pages/login/page.tsx +456 -456
- package/src/pages/profile/EnhancedProfilePage.tsx +485 -485
- package/src/pages/profile/index.ts +9 -9
- package/src/pages/profile/page.tsx +166 -166
- package/src/pages/recovery/page.tsx +234 -234
- package/src/pages/roles/MyRolesPage.tsx +211 -211
- package/src/pages/roles/components.tsx +294 -294
- package/src/pages/roles/index.ts +17 -17
- package/src/pages/security/EnhancedSecurityPage.tsx +565 -565
- package/src/pages/security/index.ts +9 -9
- package/src/pages/security/page.tsx +507 -507
- package/src/pages/settings/EnhancedSettingsPage.tsx +642 -642
- package/src/pages/settings/index.ts +9 -9
- package/src/pages/settings/page.tsx +47 -47
- package/src/pages/showcase/ShowcasePage.tsx +532 -532
- package/src/pages/showcase/index.ts +13 -13
- package/src/pages/test-env/EmergencyLogoutPage.tsx +180 -180
- package/src/pages/test-env/JwtInspectPage.tsx +456 -456
- package/src/pages/test-env/RefreshTokenPage.tsx +157 -157
- package/src/pages/test-env/TestEnvPage.tsx +118 -118
- package/src/pages/test-env/index.ts +25 -25
- package/src/pages/verify-code/page.tsx +652 -652
- package/src/routes/account/update-phone.ts +13 -13
- package/src/routes/auth/logout.ts +108 -108
- package/src/routes/auth/nextauth.ts +45 -45
- package/src/routes/auth/settings.ts +75 -75
- package/src/routes/auth/viability.ts +224 -224
- package/src/server/auth-guard.ts +170 -170
- package/src/server/slim-middleware.ts +117 -117
- package/src/server/with-auth.ts +93 -93
- package/src/stores/authStore.ts +1899 -1899
- package/src/templates/instrumentation.ts +41 -41
- package/src/utils/logout.ts +30 -30
- package/src/vibe/client.ts +590 -590
- package/src/vibe/enterprise-auth.ts +208 -208
- package/src/vibe/errors.ts +185 -185
- package/src/vibe/generic.ts +431 -431
- package/src/vibe/hooks/index.ts +367 -367
- package/src/vibe/index.ts +132 -132
- package/src/vibe/sessions.ts +551 -551
- package/src/vibe/types.ts +577 -577
package/src/vibe/hooks/index.ts
CHANGED
|
@@ -1,367 +1,367 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* =============================================================================
|
|
3
|
-
* VIBE REACT QUERY HOOKS
|
|
4
|
-
* =============================================================================
|
|
5
|
-
*
|
|
6
|
-
* React Query wrappers for Vibe App operations.
|
|
7
|
-
* Provides caching, refetching, and mutation handling out of the box.
|
|
8
|
-
*
|
|
9
|
-
* Usage:
|
|
10
|
-
* import { useVibeQuery, useVibeMutation } from '@payez/next-mvp/vibe/hooks'
|
|
11
|
-
*
|
|
12
|
-
* // Query with auto caching
|
|
13
|
-
* const { data, isLoading, error } = useVibeQuery('users', {
|
|
14
|
-
* where: { status: 'active' },
|
|
15
|
-
* take: 10
|
|
16
|
-
* })
|
|
17
|
-
*
|
|
18
|
-
* // Mutations
|
|
19
|
-
* const createUser = useVibeMutation('users', 'create')
|
|
20
|
-
* await createUser.mutateAsync({ email: 'test@example.com' })
|
|
21
|
-
*
|
|
22
|
-
* =============================================================================
|
|
23
|
-
*/
|
|
24
|
-
|
|
25
|
-
'use client';
|
|
26
|
-
|
|
27
|
-
import {
|
|
28
|
-
useQuery,
|
|
29
|
-
useMutation,
|
|
30
|
-
useQueryClient,
|
|
31
|
-
type UseQueryOptions,
|
|
32
|
-
type UseMutationOptions,
|
|
33
|
-
type QueryKey,
|
|
34
|
-
} from '@tanstack/react-query';
|
|
35
|
-
|
|
36
|
-
import { vibe, createVibeClient } from '../client';
|
|
37
|
-
import type {
|
|
38
|
-
VibeTableName,
|
|
39
|
-
VibeTableType,
|
|
40
|
-
FindManyOptions,
|
|
41
|
-
FindManyResult,
|
|
42
|
-
CreateOptions,
|
|
43
|
-
UpdateOptions,
|
|
44
|
-
VibeClientConfig,
|
|
45
|
-
} from '../client';
|
|
46
|
-
import type { VibeError } from '../errors';
|
|
47
|
-
|
|
48
|
-
// -----------------------------------------------------------------------------
|
|
49
|
-
// QUERY KEY FACTORY
|
|
50
|
-
// -----------------------------------------------------------------------------
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Generate consistent query keys for Vibe queries.
|
|
54
|
-
*/
|
|
55
|
-
export const vibeKeys = {
|
|
56
|
-
all: ['vibe'] as const,
|
|
57
|
-
table: (table: VibeTableName) => [...vibeKeys.all, table] as const,
|
|
58
|
-
list: (table: VibeTableName, options?: FindManyOptions<unknown>) =>
|
|
59
|
-
[...vibeKeys.table(table), 'list', options] as const,
|
|
60
|
-
detail: (table: VibeTableName, id: number) =>
|
|
61
|
-
[...vibeKeys.table(table), 'detail', id] as const,
|
|
62
|
-
count: (table: VibeTableName, options?: { where?: unknown }) =>
|
|
63
|
-
[...vibeKeys.table(table), 'count', options] as const,
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// -----------------------------------------------------------------------------
|
|
67
|
-
// USE VIBE QUERY HOOK
|
|
68
|
-
// -----------------------------------------------------------------------------
|
|
69
|
-
|
|
70
|
-
export interface UseVibeQueryOptions<T extends VibeTableName>
|
|
71
|
-
extends Omit<
|
|
72
|
-
UseQueryOptions<FindManyResult<VibeTableType<T>>, VibeError>,
|
|
73
|
-
'queryKey' | 'queryFn'
|
|
74
|
-
> {
|
|
75
|
-
/** Vibe client config (optional - uses default if not provided) */
|
|
76
|
-
config?: VibeClientConfig;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
/**
|
|
80
|
-
* React Query hook for fetching Vibe data.
|
|
81
|
-
*
|
|
82
|
-
* @param table - The table to query
|
|
83
|
-
* @param options - Query options (where, take, skip, orderBy)
|
|
84
|
-
* @param queryOptions - React Query options
|
|
85
|
-
*
|
|
86
|
-
* @example
|
|
87
|
-
* ```tsx
|
|
88
|
-
* const { data, isLoading } = useVibeQuery('users', {
|
|
89
|
-
* where: { status: 'active' },
|
|
90
|
-
* take: 10,
|
|
91
|
-
* orderBy: { created_at: 'desc' }
|
|
92
|
-
* })
|
|
93
|
-
* ```
|
|
94
|
-
*/
|
|
95
|
-
export function useVibeQuery<T extends VibeTableName>(
|
|
96
|
-
table: T,
|
|
97
|
-
options?: FindManyOptions<VibeTableType<T>>,
|
|
98
|
-
queryOptions?: UseVibeQueryOptions<T>
|
|
99
|
-
) {
|
|
100
|
-
const client = queryOptions?.config ? createVibeClient(queryOptions.config) : vibe;
|
|
101
|
-
|
|
102
|
-
return useQuery<FindManyResult<VibeTableType<T>>, VibeError>({
|
|
103
|
-
queryKey: vibeKeys.list(table, options as FindManyOptions<unknown>),
|
|
104
|
-
queryFn: () => (client[table] as any).findMany(options),
|
|
105
|
-
...queryOptions,
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// -----------------------------------------------------------------------------
|
|
110
|
-
// USE VIBE DETAIL HOOK
|
|
111
|
-
// -----------------------------------------------------------------------------
|
|
112
|
-
|
|
113
|
-
export interface UseVibeDetailOptions<T extends VibeTableName>
|
|
114
|
-
extends Omit<
|
|
115
|
-
UseQueryOptions<VibeTableType<T> | null, VibeError>,
|
|
116
|
-
'queryKey' | 'queryFn'
|
|
117
|
-
> {
|
|
118
|
-
/** Vibe client config (optional - uses default if not provided) */
|
|
119
|
-
config?: VibeClientConfig;
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* React Query hook for fetching a single Vibe record by ID.
|
|
124
|
-
*
|
|
125
|
-
* @param table - The table to query
|
|
126
|
-
* @param id - The record ID
|
|
127
|
-
* @param queryOptions - React Query options
|
|
128
|
-
*
|
|
129
|
-
* @example
|
|
130
|
-
* ```tsx
|
|
131
|
-
* const { data: user, isLoading } = useVibeDetail('users', 123)
|
|
132
|
-
* ```
|
|
133
|
-
*/
|
|
134
|
-
export function useVibeDetail<T extends VibeTableName>(
|
|
135
|
-
table: T,
|
|
136
|
-
id: number | null | undefined,
|
|
137
|
-
queryOptions?: UseVibeDetailOptions<T>
|
|
138
|
-
) {
|
|
139
|
-
const client = queryOptions?.config ? createVibeClient(queryOptions.config) : vibe;
|
|
140
|
-
|
|
141
|
-
return useQuery<VibeTableType<T> | null, VibeError>({
|
|
142
|
-
queryKey: vibeKeys.detail(table, id!),
|
|
143
|
-
queryFn: () => (client[table] as any).findUniqueOrNull({ where: { id } }),
|
|
144
|
-
enabled: id != null && (queryOptions?.enabled ?? true),
|
|
145
|
-
...queryOptions,
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
// -----------------------------------------------------------------------------
|
|
150
|
-
// USE VIBE COUNT HOOK
|
|
151
|
-
// -----------------------------------------------------------------------------
|
|
152
|
-
|
|
153
|
-
export interface UseVibeCountOptions<T extends VibeTableName>
|
|
154
|
-
extends Omit<UseQueryOptions<number, VibeError>, 'queryKey' | 'queryFn'> {
|
|
155
|
-
/** Vibe client config (optional - uses default if not provided) */
|
|
156
|
-
config?: VibeClientConfig;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* React Query hook for counting Vibe records.
|
|
161
|
-
*
|
|
162
|
-
* @param table - The table to query
|
|
163
|
-
* @param options - Count options (where)
|
|
164
|
-
* @param queryOptions - React Query options
|
|
165
|
-
*
|
|
166
|
-
* @example
|
|
167
|
-
* ```tsx
|
|
168
|
-
* const { data: count } = useVibeCount('users', { where: { status: 'active' } })
|
|
169
|
-
* ```
|
|
170
|
-
*/
|
|
171
|
-
export function useVibeCount<T extends VibeTableName>(
|
|
172
|
-
table: T,
|
|
173
|
-
options?: { where?: FindManyOptions<VibeTableType<T>>['where'] },
|
|
174
|
-
queryOptions?: UseVibeCountOptions<T>
|
|
175
|
-
) {
|
|
176
|
-
const client = queryOptions?.config ? createVibeClient(queryOptions.config) : vibe;
|
|
177
|
-
|
|
178
|
-
return useQuery<number, VibeError>({
|
|
179
|
-
queryKey: vibeKeys.count(table, options),
|
|
180
|
-
queryFn: () => (client[table] as any).count(options),
|
|
181
|
-
...queryOptions,
|
|
182
|
-
});
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// -----------------------------------------------------------------------------
|
|
186
|
-
// USE VIBE MUTATION HOOK
|
|
187
|
-
// -----------------------------------------------------------------------------
|
|
188
|
-
|
|
189
|
-
type MutationOperation = 'create' | 'update' | 'delete';
|
|
190
|
-
|
|
191
|
-
type MutationInput<T extends VibeTableName, Op extends MutationOperation> =
|
|
192
|
-
Op extends 'create' ? CreateOptions<VibeTableType<T>>['data'] :
|
|
193
|
-
Op extends 'update' ? UpdateOptions<VibeTableType<T>> :
|
|
194
|
-
Op extends 'delete' ? { id: number } :
|
|
195
|
-
never;
|
|
196
|
-
|
|
197
|
-
type MutationResult<T extends VibeTableName, Op extends MutationOperation> =
|
|
198
|
-
Op extends 'create' ? VibeTableType<T> :
|
|
199
|
-
Op extends 'update' ? VibeTableType<T> :
|
|
200
|
-
Op extends 'delete' ? VibeTableType<T> :
|
|
201
|
-
never;
|
|
202
|
-
|
|
203
|
-
export interface UseVibeMutationOptions<
|
|
204
|
-
T extends VibeTableName,
|
|
205
|
-
Op extends MutationOperation
|
|
206
|
-
> extends Omit<
|
|
207
|
-
UseMutationOptions<MutationResult<T, Op>, VibeError, MutationInput<T, Op>>,
|
|
208
|
-
'mutationFn'
|
|
209
|
-
> {
|
|
210
|
-
/** Vibe client config (optional - uses default if not provided) */
|
|
211
|
-
config?: VibeClientConfig;
|
|
212
|
-
/** Automatically invalidate list queries after mutation (default: true) */
|
|
213
|
-
invalidateOnSuccess?: boolean;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
/**
|
|
217
|
-
* React Query mutation hook for Vibe operations.
|
|
218
|
-
*
|
|
219
|
-
* @param table - The table to mutate
|
|
220
|
-
* @param operation - The operation type (create, update, delete)
|
|
221
|
-
* @param mutationOptions - React Query mutation options
|
|
222
|
-
*
|
|
223
|
-
* @example
|
|
224
|
-
* ```tsx
|
|
225
|
-
* const createUser = useVibeMutation('users', 'create')
|
|
226
|
-
*
|
|
227
|
-
* // Create a user
|
|
228
|
-
* await createUser.mutateAsync({
|
|
229
|
-
* email: 'test@example.com',
|
|
230
|
-
* name: 'Test User'
|
|
231
|
-
* })
|
|
232
|
-
*
|
|
233
|
-
* const updateUser = useVibeMutation('users', 'update')
|
|
234
|
-
*
|
|
235
|
-
* // Update a user
|
|
236
|
-
* await updateUser.mutateAsync({
|
|
237
|
-
* where: { id: 123 },
|
|
238
|
-
* data: { name: 'Updated Name' }
|
|
239
|
-
* })
|
|
240
|
-
*
|
|
241
|
-
* const deleteUser = useVibeMutation('users', 'delete')
|
|
242
|
-
*
|
|
243
|
-
* // Delete a user
|
|
244
|
-
* await deleteUser.mutateAsync({ id: 123 })
|
|
245
|
-
* ```
|
|
246
|
-
*/
|
|
247
|
-
export function useVibeMutation<
|
|
248
|
-
T extends VibeTableName,
|
|
249
|
-
Op extends MutationOperation
|
|
250
|
-
>(
|
|
251
|
-
table: T,
|
|
252
|
-
operation: Op,
|
|
253
|
-
mutationOptions?: UseVibeMutationOptions<T, Op>
|
|
254
|
-
) {
|
|
255
|
-
const queryClient = useQueryClient();
|
|
256
|
-
const client = mutationOptions?.config ? createVibeClient(mutationOptions.config) : vibe;
|
|
257
|
-
const { invalidateOnSuccess = true, ...restOptions } = mutationOptions || {};
|
|
258
|
-
|
|
259
|
-
const { onSuccess: userOnSuccess, ...otherOptions } = restOptions;
|
|
260
|
-
|
|
261
|
-
return useMutation<MutationResult<T, Op>, VibeError, MutationInput<T, Op>>({
|
|
262
|
-
mutationFn: async (input) => {
|
|
263
|
-
const delegate = client[table] as any;
|
|
264
|
-
|
|
265
|
-
switch (operation) {
|
|
266
|
-
case 'create':
|
|
267
|
-
return delegate.create({ data: input });
|
|
268
|
-
case 'update':
|
|
269
|
-
const updateInput = input as UpdateOptions<VibeTableType<T>>;
|
|
270
|
-
return delegate.update(updateInput);
|
|
271
|
-
case 'delete':
|
|
272
|
-
const deleteInput = input as { id: number };
|
|
273
|
-
return delegate.delete({ where: { id: deleteInput.id } });
|
|
274
|
-
default:
|
|
275
|
-
throw new Error(`Unknown operation: ${operation}`);
|
|
276
|
-
}
|
|
277
|
-
},
|
|
278
|
-
onSuccess: (...args) => {
|
|
279
|
-
// Invalidate list queries for this table
|
|
280
|
-
if (invalidateOnSuccess) {
|
|
281
|
-
queryClient.invalidateQueries({ queryKey: vibeKeys.table(table) });
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// Call user's onSuccess if provided
|
|
285
|
-
userOnSuccess?.(...args);
|
|
286
|
-
},
|
|
287
|
-
...otherOptions,
|
|
288
|
-
});
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// -----------------------------------------------------------------------------
|
|
292
|
-
// CONVENIENCE HOOKS
|
|
293
|
-
// -----------------------------------------------------------------------------
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* Convenience hook for creating records.
|
|
297
|
-
*/
|
|
298
|
-
export function useVibeCreate<T extends VibeTableName>(
|
|
299
|
-
table: T,
|
|
300
|
-
options?: Omit<UseVibeMutationOptions<T, 'create'>, never>
|
|
301
|
-
) {
|
|
302
|
-
return useVibeMutation(table, 'create', options);
|
|
303
|
-
}
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Convenience hook for updating records.
|
|
307
|
-
*/
|
|
308
|
-
export function useVibeUpdate<T extends VibeTableName>(
|
|
309
|
-
table: T,
|
|
310
|
-
options?: Omit<UseVibeMutationOptions<T, 'update'>, never>
|
|
311
|
-
) {
|
|
312
|
-
return useVibeMutation(table, 'update', options);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* Convenience hook for deleting records.
|
|
317
|
-
*/
|
|
318
|
-
export function useVibeDelete<T extends VibeTableName>(
|
|
319
|
-
table: T,
|
|
320
|
-
options?: Omit<UseVibeMutationOptions<T, 'delete'>, never>
|
|
321
|
-
) {
|
|
322
|
-
return useVibeMutation(table, 'delete', options);
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
// -----------------------------------------------------------------------------
|
|
326
|
-
// PREFETCH UTILITIES
|
|
327
|
-
// -----------------------------------------------------------------------------
|
|
328
|
-
|
|
329
|
-
/**
|
|
330
|
-
* Prefetch Vibe data for SSR/SSG.
|
|
331
|
-
*
|
|
332
|
-
* @example
|
|
333
|
-
* ```tsx
|
|
334
|
-
* // In getServerSideProps or generateStaticParams
|
|
335
|
-
* await prefetchVibeQuery(queryClient, 'users', { take: 10 })
|
|
336
|
-
* ```
|
|
337
|
-
*/
|
|
338
|
-
export async function prefetchVibeQuery<T extends VibeTableName>(
|
|
339
|
-
queryClient: ReturnType<typeof useQueryClient>,
|
|
340
|
-
table: T,
|
|
341
|
-
options?: FindManyOptions<VibeTableType<T>>,
|
|
342
|
-
config?: VibeClientConfig
|
|
343
|
-
) {
|
|
344
|
-
const client = config ? createVibeClient(config) : vibe;
|
|
345
|
-
|
|
346
|
-
await queryClient.prefetchQuery({
|
|
347
|
-
queryKey: vibeKeys.list(table, options as FindManyOptions<unknown>),
|
|
348
|
-
queryFn: () => (client[table] as any).findMany(options),
|
|
349
|
-
});
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
/**
|
|
353
|
-
* Prefetch a single Vibe record for SSR/SSG.
|
|
354
|
-
*/
|
|
355
|
-
export async function prefetchVibeDetail<T extends VibeTableName>(
|
|
356
|
-
queryClient: ReturnType<typeof useQueryClient>,
|
|
357
|
-
table: T,
|
|
358
|
-
id: number,
|
|
359
|
-
config?: VibeClientConfig
|
|
360
|
-
) {
|
|
361
|
-
const client = config ? createVibeClient(config) : vibe;
|
|
362
|
-
|
|
363
|
-
await queryClient.prefetchQuery({
|
|
364
|
-
queryKey: vibeKeys.detail(table, id),
|
|
365
|
-
queryFn: () => (client[table] as any).findUniqueOrNull({ where: { id } }),
|
|
366
|
-
});
|
|
367
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* =============================================================================
|
|
3
|
+
* VIBE REACT QUERY HOOKS
|
|
4
|
+
* =============================================================================
|
|
5
|
+
*
|
|
6
|
+
* React Query wrappers for Vibe App operations.
|
|
7
|
+
* Provides caching, refetching, and mutation handling out of the box.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* import { useVibeQuery, useVibeMutation } from '@payez/next-mvp/vibe/hooks'
|
|
11
|
+
*
|
|
12
|
+
* // Query with auto caching
|
|
13
|
+
* const { data, isLoading, error } = useVibeQuery('users', {
|
|
14
|
+
* where: { status: 'active' },
|
|
15
|
+
* take: 10
|
|
16
|
+
* })
|
|
17
|
+
*
|
|
18
|
+
* // Mutations
|
|
19
|
+
* const createUser = useVibeMutation('users', 'create')
|
|
20
|
+
* await createUser.mutateAsync({ email: 'test@example.com' })
|
|
21
|
+
*
|
|
22
|
+
* =============================================================================
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
'use client';
|
|
26
|
+
|
|
27
|
+
import {
|
|
28
|
+
useQuery,
|
|
29
|
+
useMutation,
|
|
30
|
+
useQueryClient,
|
|
31
|
+
type UseQueryOptions,
|
|
32
|
+
type UseMutationOptions,
|
|
33
|
+
type QueryKey,
|
|
34
|
+
} from '@tanstack/react-query';
|
|
35
|
+
|
|
36
|
+
import { vibe, createVibeClient } from '../client';
|
|
37
|
+
import type {
|
|
38
|
+
VibeTableName,
|
|
39
|
+
VibeTableType,
|
|
40
|
+
FindManyOptions,
|
|
41
|
+
FindManyResult,
|
|
42
|
+
CreateOptions,
|
|
43
|
+
UpdateOptions,
|
|
44
|
+
VibeClientConfig,
|
|
45
|
+
} from '../client';
|
|
46
|
+
import type { VibeError } from '../errors';
|
|
47
|
+
|
|
48
|
+
// -----------------------------------------------------------------------------
|
|
49
|
+
// QUERY KEY FACTORY
|
|
50
|
+
// -----------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Generate consistent query keys for Vibe queries.
|
|
54
|
+
*/
|
|
55
|
+
export const vibeKeys = {
|
|
56
|
+
all: ['vibe'] as const,
|
|
57
|
+
table: (table: VibeTableName) => [...vibeKeys.all, table] as const,
|
|
58
|
+
list: (table: VibeTableName, options?: FindManyOptions<unknown>) =>
|
|
59
|
+
[...vibeKeys.table(table), 'list', options] as const,
|
|
60
|
+
detail: (table: VibeTableName, id: number) =>
|
|
61
|
+
[...vibeKeys.table(table), 'detail', id] as const,
|
|
62
|
+
count: (table: VibeTableName, options?: { where?: unknown }) =>
|
|
63
|
+
[...vibeKeys.table(table), 'count', options] as const,
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
// -----------------------------------------------------------------------------
|
|
67
|
+
// USE VIBE QUERY HOOK
|
|
68
|
+
// -----------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
export interface UseVibeQueryOptions<T extends VibeTableName>
|
|
71
|
+
extends Omit<
|
|
72
|
+
UseQueryOptions<FindManyResult<VibeTableType<T>>, VibeError>,
|
|
73
|
+
'queryKey' | 'queryFn'
|
|
74
|
+
> {
|
|
75
|
+
/** Vibe client config (optional - uses default if not provided) */
|
|
76
|
+
config?: VibeClientConfig;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* React Query hook for fetching Vibe data.
|
|
81
|
+
*
|
|
82
|
+
* @param table - The table to query
|
|
83
|
+
* @param options - Query options (where, take, skip, orderBy)
|
|
84
|
+
* @param queryOptions - React Query options
|
|
85
|
+
*
|
|
86
|
+
* @example
|
|
87
|
+
* ```tsx
|
|
88
|
+
* const { data, isLoading } = useVibeQuery('users', {
|
|
89
|
+
* where: { status: 'active' },
|
|
90
|
+
* take: 10,
|
|
91
|
+
* orderBy: { created_at: 'desc' }
|
|
92
|
+
* })
|
|
93
|
+
* ```
|
|
94
|
+
*/
|
|
95
|
+
export function useVibeQuery<T extends VibeTableName>(
|
|
96
|
+
table: T,
|
|
97
|
+
options?: FindManyOptions<VibeTableType<T>>,
|
|
98
|
+
queryOptions?: UseVibeQueryOptions<T>
|
|
99
|
+
) {
|
|
100
|
+
const client = queryOptions?.config ? createVibeClient(queryOptions.config) : vibe;
|
|
101
|
+
|
|
102
|
+
return useQuery<FindManyResult<VibeTableType<T>>, VibeError>({
|
|
103
|
+
queryKey: vibeKeys.list(table, options as FindManyOptions<unknown>),
|
|
104
|
+
queryFn: () => (client[table] as any).findMany(options),
|
|
105
|
+
...queryOptions,
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// -----------------------------------------------------------------------------
|
|
110
|
+
// USE VIBE DETAIL HOOK
|
|
111
|
+
// -----------------------------------------------------------------------------
|
|
112
|
+
|
|
113
|
+
export interface UseVibeDetailOptions<T extends VibeTableName>
|
|
114
|
+
extends Omit<
|
|
115
|
+
UseQueryOptions<VibeTableType<T> | null, VibeError>,
|
|
116
|
+
'queryKey' | 'queryFn'
|
|
117
|
+
> {
|
|
118
|
+
/** Vibe client config (optional - uses default if not provided) */
|
|
119
|
+
config?: VibeClientConfig;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* React Query hook for fetching a single Vibe record by ID.
|
|
124
|
+
*
|
|
125
|
+
* @param table - The table to query
|
|
126
|
+
* @param id - The record ID
|
|
127
|
+
* @param queryOptions - React Query options
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```tsx
|
|
131
|
+
* const { data: user, isLoading } = useVibeDetail('users', 123)
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
export function useVibeDetail<T extends VibeTableName>(
|
|
135
|
+
table: T,
|
|
136
|
+
id: number | null | undefined,
|
|
137
|
+
queryOptions?: UseVibeDetailOptions<T>
|
|
138
|
+
) {
|
|
139
|
+
const client = queryOptions?.config ? createVibeClient(queryOptions.config) : vibe;
|
|
140
|
+
|
|
141
|
+
return useQuery<VibeTableType<T> | null, VibeError>({
|
|
142
|
+
queryKey: vibeKeys.detail(table, id!),
|
|
143
|
+
queryFn: () => (client[table] as any).findUniqueOrNull({ where: { id } }),
|
|
144
|
+
enabled: id != null && (queryOptions?.enabled ?? true),
|
|
145
|
+
...queryOptions,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// -----------------------------------------------------------------------------
|
|
150
|
+
// USE VIBE COUNT HOOK
|
|
151
|
+
// -----------------------------------------------------------------------------
|
|
152
|
+
|
|
153
|
+
export interface UseVibeCountOptions<T extends VibeTableName>
|
|
154
|
+
extends Omit<UseQueryOptions<number, VibeError>, 'queryKey' | 'queryFn'> {
|
|
155
|
+
/** Vibe client config (optional - uses default if not provided) */
|
|
156
|
+
config?: VibeClientConfig;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* React Query hook for counting Vibe records.
|
|
161
|
+
*
|
|
162
|
+
* @param table - The table to query
|
|
163
|
+
* @param options - Count options (where)
|
|
164
|
+
* @param queryOptions - React Query options
|
|
165
|
+
*
|
|
166
|
+
* @example
|
|
167
|
+
* ```tsx
|
|
168
|
+
* const { data: count } = useVibeCount('users', { where: { status: 'active' } })
|
|
169
|
+
* ```
|
|
170
|
+
*/
|
|
171
|
+
export function useVibeCount<T extends VibeTableName>(
|
|
172
|
+
table: T,
|
|
173
|
+
options?: { where?: FindManyOptions<VibeTableType<T>>['where'] },
|
|
174
|
+
queryOptions?: UseVibeCountOptions<T>
|
|
175
|
+
) {
|
|
176
|
+
const client = queryOptions?.config ? createVibeClient(queryOptions.config) : vibe;
|
|
177
|
+
|
|
178
|
+
return useQuery<number, VibeError>({
|
|
179
|
+
queryKey: vibeKeys.count(table, options),
|
|
180
|
+
queryFn: () => (client[table] as any).count(options),
|
|
181
|
+
...queryOptions,
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// -----------------------------------------------------------------------------
|
|
186
|
+
// USE VIBE MUTATION HOOK
|
|
187
|
+
// -----------------------------------------------------------------------------
|
|
188
|
+
|
|
189
|
+
type MutationOperation = 'create' | 'update' | 'delete';
|
|
190
|
+
|
|
191
|
+
type MutationInput<T extends VibeTableName, Op extends MutationOperation> =
|
|
192
|
+
Op extends 'create' ? CreateOptions<VibeTableType<T>>['data'] :
|
|
193
|
+
Op extends 'update' ? UpdateOptions<VibeTableType<T>> :
|
|
194
|
+
Op extends 'delete' ? { id: number } :
|
|
195
|
+
never;
|
|
196
|
+
|
|
197
|
+
type MutationResult<T extends VibeTableName, Op extends MutationOperation> =
|
|
198
|
+
Op extends 'create' ? VibeTableType<T> :
|
|
199
|
+
Op extends 'update' ? VibeTableType<T> :
|
|
200
|
+
Op extends 'delete' ? VibeTableType<T> :
|
|
201
|
+
never;
|
|
202
|
+
|
|
203
|
+
export interface UseVibeMutationOptions<
|
|
204
|
+
T extends VibeTableName,
|
|
205
|
+
Op extends MutationOperation
|
|
206
|
+
> extends Omit<
|
|
207
|
+
UseMutationOptions<MutationResult<T, Op>, VibeError, MutationInput<T, Op>>,
|
|
208
|
+
'mutationFn'
|
|
209
|
+
> {
|
|
210
|
+
/** Vibe client config (optional - uses default if not provided) */
|
|
211
|
+
config?: VibeClientConfig;
|
|
212
|
+
/** Automatically invalidate list queries after mutation (default: true) */
|
|
213
|
+
invalidateOnSuccess?: boolean;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* React Query mutation hook for Vibe operations.
|
|
218
|
+
*
|
|
219
|
+
* @param table - The table to mutate
|
|
220
|
+
* @param operation - The operation type (create, update, delete)
|
|
221
|
+
* @param mutationOptions - React Query mutation options
|
|
222
|
+
*
|
|
223
|
+
* @example
|
|
224
|
+
* ```tsx
|
|
225
|
+
* const createUser = useVibeMutation('users', 'create')
|
|
226
|
+
*
|
|
227
|
+
* // Create a user
|
|
228
|
+
* await createUser.mutateAsync({
|
|
229
|
+
* email: 'test@example.com',
|
|
230
|
+
* name: 'Test User'
|
|
231
|
+
* })
|
|
232
|
+
*
|
|
233
|
+
* const updateUser = useVibeMutation('users', 'update')
|
|
234
|
+
*
|
|
235
|
+
* // Update a user
|
|
236
|
+
* await updateUser.mutateAsync({
|
|
237
|
+
* where: { id: 123 },
|
|
238
|
+
* data: { name: 'Updated Name' }
|
|
239
|
+
* })
|
|
240
|
+
*
|
|
241
|
+
* const deleteUser = useVibeMutation('users', 'delete')
|
|
242
|
+
*
|
|
243
|
+
* // Delete a user
|
|
244
|
+
* await deleteUser.mutateAsync({ id: 123 })
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
export function useVibeMutation<
|
|
248
|
+
T extends VibeTableName,
|
|
249
|
+
Op extends MutationOperation
|
|
250
|
+
>(
|
|
251
|
+
table: T,
|
|
252
|
+
operation: Op,
|
|
253
|
+
mutationOptions?: UseVibeMutationOptions<T, Op>
|
|
254
|
+
) {
|
|
255
|
+
const queryClient = useQueryClient();
|
|
256
|
+
const client = mutationOptions?.config ? createVibeClient(mutationOptions.config) : vibe;
|
|
257
|
+
const { invalidateOnSuccess = true, ...restOptions } = mutationOptions || {};
|
|
258
|
+
|
|
259
|
+
const { onSuccess: userOnSuccess, ...otherOptions } = restOptions;
|
|
260
|
+
|
|
261
|
+
return useMutation<MutationResult<T, Op>, VibeError, MutationInput<T, Op>>({
|
|
262
|
+
mutationFn: async (input) => {
|
|
263
|
+
const delegate = client[table] as any;
|
|
264
|
+
|
|
265
|
+
switch (operation) {
|
|
266
|
+
case 'create':
|
|
267
|
+
return delegate.create({ data: input });
|
|
268
|
+
case 'update':
|
|
269
|
+
const updateInput = input as UpdateOptions<VibeTableType<T>>;
|
|
270
|
+
return delegate.update(updateInput);
|
|
271
|
+
case 'delete':
|
|
272
|
+
const deleteInput = input as { id: number };
|
|
273
|
+
return delegate.delete({ where: { id: deleteInput.id } });
|
|
274
|
+
default:
|
|
275
|
+
throw new Error(`Unknown operation: ${operation}`);
|
|
276
|
+
}
|
|
277
|
+
},
|
|
278
|
+
onSuccess: (...args) => {
|
|
279
|
+
// Invalidate list queries for this table
|
|
280
|
+
if (invalidateOnSuccess) {
|
|
281
|
+
queryClient.invalidateQueries({ queryKey: vibeKeys.table(table) });
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
// Call user's onSuccess if provided
|
|
285
|
+
userOnSuccess?.(...args);
|
|
286
|
+
},
|
|
287
|
+
...otherOptions,
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// -----------------------------------------------------------------------------
|
|
292
|
+
// CONVENIENCE HOOKS
|
|
293
|
+
// -----------------------------------------------------------------------------
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Convenience hook for creating records.
|
|
297
|
+
*/
|
|
298
|
+
export function useVibeCreate<T extends VibeTableName>(
|
|
299
|
+
table: T,
|
|
300
|
+
options?: Omit<UseVibeMutationOptions<T, 'create'>, never>
|
|
301
|
+
) {
|
|
302
|
+
return useVibeMutation(table, 'create', options);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/**
|
|
306
|
+
* Convenience hook for updating records.
|
|
307
|
+
*/
|
|
308
|
+
export function useVibeUpdate<T extends VibeTableName>(
|
|
309
|
+
table: T,
|
|
310
|
+
options?: Omit<UseVibeMutationOptions<T, 'update'>, never>
|
|
311
|
+
) {
|
|
312
|
+
return useVibeMutation(table, 'update', options);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Convenience hook for deleting records.
|
|
317
|
+
*/
|
|
318
|
+
export function useVibeDelete<T extends VibeTableName>(
|
|
319
|
+
table: T,
|
|
320
|
+
options?: Omit<UseVibeMutationOptions<T, 'delete'>, never>
|
|
321
|
+
) {
|
|
322
|
+
return useVibeMutation(table, 'delete', options);
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
// -----------------------------------------------------------------------------
|
|
326
|
+
// PREFETCH UTILITIES
|
|
327
|
+
// -----------------------------------------------------------------------------
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Prefetch Vibe data for SSR/SSG.
|
|
331
|
+
*
|
|
332
|
+
* @example
|
|
333
|
+
* ```tsx
|
|
334
|
+
* // In getServerSideProps or generateStaticParams
|
|
335
|
+
* await prefetchVibeQuery(queryClient, 'users', { take: 10 })
|
|
336
|
+
* ```
|
|
337
|
+
*/
|
|
338
|
+
export async function prefetchVibeQuery<T extends VibeTableName>(
|
|
339
|
+
queryClient: ReturnType<typeof useQueryClient>,
|
|
340
|
+
table: T,
|
|
341
|
+
options?: FindManyOptions<VibeTableType<T>>,
|
|
342
|
+
config?: VibeClientConfig
|
|
343
|
+
) {
|
|
344
|
+
const client = config ? createVibeClient(config) : vibe;
|
|
345
|
+
|
|
346
|
+
await queryClient.prefetchQuery({
|
|
347
|
+
queryKey: vibeKeys.list(table, options as FindManyOptions<unknown>),
|
|
348
|
+
queryFn: () => (client[table] as any).findMany(options),
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
/**
|
|
353
|
+
* Prefetch a single Vibe record for SSR/SSG.
|
|
354
|
+
*/
|
|
355
|
+
export async function prefetchVibeDetail<T extends VibeTableName>(
|
|
356
|
+
queryClient: ReturnType<typeof useQueryClient>,
|
|
357
|
+
table: T,
|
|
358
|
+
id: number,
|
|
359
|
+
config?: VibeClientConfig
|
|
360
|
+
) {
|
|
361
|
+
const client = config ? createVibeClient(config) : vibe;
|
|
362
|
+
|
|
363
|
+
await queryClient.prefetchQuery({
|
|
364
|
+
queryKey: vibeKeys.detail(table, id),
|
|
365
|
+
queryFn: () => (client[table] as any).findUniqueOrNull({ where: { id } }),
|
|
366
|
+
});
|
|
367
|
+
}
|