over-zero 0.0.0
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/dist/cjs/build/readPermissions.cjs +51 -0
- package/dist/cjs/build/readPermissions.js +48 -0
- package/dist/cjs/build/readPermissions.js.map +6 -0
- package/dist/cjs/build/readPermissions.native.js +56 -0
- package/dist/cjs/build/readPermissions.native.js.map +6 -0
- package/dist/cjs/build/schema.cjs +28 -0
- package/dist/cjs/build/schema.js +22 -0
- package/dist/cjs/build/schema.js.map +6 -0
- package/dist/cjs/build/schema.native.js +28 -0
- package/dist/cjs/build/schema.native.js.map +6 -0
- package/dist/cjs/createClient.cjs +89 -0
- package/dist/cjs/createClient.js +76 -0
- package/dist/cjs/createClient.js.map +6 -0
- package/dist/cjs/createClient.native.js +81 -0
- package/dist/cjs/createClient.native.js.map +6 -0
- package/dist/cjs/createMutations.cjs +50 -0
- package/dist/cjs/createMutations.js +43 -0
- package/dist/cjs/createMutations.js.map +6 -0
- package/dist/cjs/createMutations.native.js +50 -0
- package/dist/cjs/createMutations.native.js.map +6 -0
- package/dist/cjs/createPermissions.cjs +128 -0
- package/dist/cjs/createPermissions.js +121 -0
- package/dist/cjs/createPermissions.js.map +6 -0
- package/dist/cjs/createPermissions.native.js +135 -0
- package/dist/cjs/createPermissions.native.js.map +6 -0
- package/dist/cjs/createServer.cjs +92 -0
- package/dist/cjs/createServer.js +71 -0
- package/dist/cjs/createServer.js.map +6 -0
- package/dist/cjs/createServer.native.js +75 -0
- package/dist/cjs/createServer.native.js.map +6 -0
- package/dist/cjs/helpers/batchQuery.cjs +49 -0
- package/dist/cjs/helpers/batchQuery.js +38 -0
- package/dist/cjs/helpers/batchQuery.js.map +6 -0
- package/dist/cjs/helpers/batchQuery.native.js +42 -0
- package/dist/cjs/helpers/batchQuery.native.js.map +6 -0
- package/dist/cjs/helpers/clearZeroDatabase.cjs +57 -0
- package/dist/cjs/helpers/clearZeroDatabase.js +57 -0
- package/dist/cjs/helpers/clearZeroDatabase.js.map +6 -0
- package/dist/cjs/helpers/clearZeroDatabase.native.js +71 -0
- package/dist/cjs/helpers/clearZeroDatabase.native.js.map +6 -0
- package/dist/cjs/helpers/context.cjs +40 -0
- package/dist/cjs/helpers/context.js +36 -0
- package/dist/cjs/helpers/context.js.map +6 -0
- package/dist/cjs/helpers/context.native.js +42 -0
- package/dist/cjs/helpers/context.native.js.map +6 -0
- package/dist/cjs/helpers/createMutators.cjs +87 -0
- package/dist/cjs/helpers/createMutators.js +81 -0
- package/dist/cjs/helpers/createMutators.js.map +6 -0
- package/dist/cjs/helpers/createMutators.native.js +116 -0
- package/dist/cjs/helpers/createMutators.native.js.map +6 -0
- package/dist/cjs/helpers/ensureLoggedIn.cjs +33 -0
- package/dist/cjs/helpers/ensureLoggedIn.js +25 -0
- package/dist/cjs/helpers/ensureLoggedIn.js.map +6 -0
- package/dist/cjs/helpers/ensureLoggedIn.native.js +29 -0
- package/dist/cjs/helpers/ensureLoggedIn.native.js.map +6 -0
- package/dist/cjs/helpers/getAuthData.cjs +36 -0
- package/dist/cjs/helpers/getAuthData.js +29 -0
- package/dist/cjs/helpers/getAuthData.js.map +6 -0
- package/dist/cjs/helpers/getAuthData.native.js +33 -0
- package/dist/cjs/helpers/getAuthData.native.js.map +6 -0
- package/dist/cjs/helpers/prettyFormatZeroQuery.cjs +107 -0
- package/dist/cjs/helpers/prettyFormatZeroQuery.js +92 -0
- package/dist/cjs/helpers/prettyFormatZeroQuery.js.map +6 -0
- package/dist/cjs/helpers/prettyFormatZeroQuery.native.js +99 -0
- package/dist/cjs/helpers/prettyFormatZeroQuery.native.js.map +6 -0
- package/dist/cjs/helpers/setupZeroClientGlobalEffects.cjs +40 -0
- package/dist/cjs/helpers/setupZeroClientGlobalEffects.js +36 -0
- package/dist/cjs/helpers/setupZeroClientGlobalEffects.js.map +6 -0
- package/dist/cjs/helpers/setupZeroClientGlobalEffects.native.js +36 -0
- package/dist/cjs/helpers/setupZeroClientGlobalEffects.native.js.map +6 -0
- package/dist/cjs/helpers/useAuthData.cjs +32 -0
- package/dist/cjs/helpers/useAuthData.js +25 -0
- package/dist/cjs/helpers/useAuthData.js.map +6 -0
- package/dist/cjs/helpers/useAuthData.native.js +33 -0
- package/dist/cjs/helpers/useAuthData.native.js.map +6 -0
- package/dist/cjs/helpers/useZDB.cjs +70 -0
- package/dist/cjs/helpers/useZDB.js +51 -0
- package/dist/cjs/helpers/useZDB.js.map +6 -0
- package/dist/cjs/helpers/useZDB.native.js +68 -0
- package/dist/cjs/helpers/useZDB.native.js.map +6 -0
- package/dist/cjs/helpers/zeroEmitter.cjs +27 -0
- package/dist/cjs/helpers/zeroEmitter.js +22 -0
- package/dist/cjs/helpers/zeroEmitter.js.map +6 -0
- package/dist/cjs/helpers/zeroEmitter.native.js +26 -0
- package/dist/cjs/helpers/zeroEmitter.native.js.map +6 -0
- package/dist/cjs/index.cjs +23 -0
- package/dist/cjs/index.js +20 -0
- package/dist/cjs/index.js.map +6 -0
- package/dist/cjs/index.native.js +30 -0
- package/dist/cjs/index.native.js.map +6 -0
- package/dist/cjs/types.cjs +16 -0
- package/dist/cjs/types.js +14 -0
- package/dist/cjs/types.js.map +6 -0
- package/dist/cjs/types.native.js +15 -0
- package/dist/cjs/types.native.js.map +6 -0
- package/dist/esm/build/readPermissions.js +36 -0
- package/dist/esm/build/readPermissions.js.map +6 -0
- package/dist/esm/build/readPermissions.mjs +28 -0
- package/dist/esm/build/readPermissions.mjs.map +1 -0
- package/dist/esm/build/readPermissions.native.js +34 -0
- package/dist/esm/build/readPermissions.native.js.map +1 -0
- package/dist/esm/build/schema.js +7 -0
- package/dist/esm/build/schema.js.map +6 -0
- package/dist/esm/build/schema.mjs +4 -0
- package/dist/esm/build/schema.mjs.map +1 -0
- package/dist/esm/build/schema.native.js +4 -0
- package/dist/esm/build/schema.native.js.map +1 -0
- package/dist/esm/createClient.js +68 -0
- package/dist/esm/createClient.js.map +6 -0
- package/dist/esm/createClient.mjs +66 -0
- package/dist/esm/createClient.mjs.map +1 -0
- package/dist/esm/createClient.native.js +74 -0
- package/dist/esm/createClient.native.js.map +1 -0
- package/dist/esm/createMutations.js +27 -0
- package/dist/esm/createMutations.js.map +6 -0
- package/dist/esm/createMutations.mjs +27 -0
- package/dist/esm/createMutations.mjs.map +1 -0
- package/dist/esm/createMutations.native.js +29 -0
- package/dist/esm/createMutations.native.js.map +1 -0
- package/dist/esm/createPermissions.js +106 -0
- package/dist/esm/createPermissions.js.map +6 -0
- package/dist/esm/createPermissions.mjs +105 -0
- package/dist/esm/createPermissions.mjs.map +1 -0
- package/dist/esm/createPermissions.native.js +129 -0
- package/dist/esm/createPermissions.native.js.map +1 -0
- package/dist/esm/createServer.js +54 -0
- package/dist/esm/createServer.js.map +6 -0
- package/dist/esm/createServer.mjs +58 -0
- package/dist/esm/createServer.mjs.map +1 -0
- package/dist/esm/createServer.native.js +61 -0
- package/dist/esm/createServer.native.js.map +1 -0
- package/dist/esm/helpers/batchQuery.js +22 -0
- package/dist/esm/helpers/batchQuery.js.map +6 -0
- package/dist/esm/helpers/batchQuery.mjs +26 -0
- package/dist/esm/helpers/batchQuery.mjs.map +1 -0
- package/dist/esm/helpers/batchQuery.native.js +23 -0
- package/dist/esm/helpers/batchQuery.native.js.map +1 -0
- package/dist/esm/helpers/clearZeroDatabase.js +42 -0
- package/dist/esm/helpers/clearZeroDatabase.js.map +6 -0
- package/dist/esm/helpers/clearZeroDatabase.mjs +34 -0
- package/dist/esm/helpers/clearZeroDatabase.mjs.map +1 -0
- package/dist/esm/helpers/clearZeroDatabase.native.js +50 -0
- package/dist/esm/helpers/clearZeroDatabase.native.js.map +1 -0
- package/dist/esm/helpers/context.js +20 -0
- package/dist/esm/helpers/context.js.map +6 -0
- package/dist/esm/helpers/context.mjs +15 -0
- package/dist/esm/helpers/context.mjs.map +1 -0
- package/dist/esm/helpers/context.native.js +15 -0
- package/dist/esm/helpers/context.native.js.map +1 -0
- package/dist/esm/helpers/createMutators.js +69 -0
- package/dist/esm/helpers/createMutators.js.map +6 -0
- package/dist/esm/helpers/createMutators.mjs +64 -0
- package/dist/esm/helpers/createMutators.mjs.map +1 -0
- package/dist/esm/helpers/createMutators.native.js +101 -0
- package/dist/esm/helpers/createMutators.native.js.map +1 -0
- package/dist/esm/helpers/ensureLoggedIn.js +10 -0
- package/dist/esm/helpers/ensureLoggedIn.js.map +6 -0
- package/dist/esm/helpers/ensureLoggedIn.mjs +10 -0
- package/dist/esm/helpers/ensureLoggedIn.mjs.map +1 -0
- package/dist/esm/helpers/ensureLoggedIn.native.js +10 -0
- package/dist/esm/helpers/ensureLoggedIn.native.js.map +1 -0
- package/dist/esm/helpers/getAuthData.js +13 -0
- package/dist/esm/helpers/getAuthData.js.map +6 -0
- package/dist/esm/helpers/getAuthData.mjs +13 -0
- package/dist/esm/helpers/getAuthData.mjs.map +1 -0
- package/dist/esm/helpers/getAuthData.native.js +13 -0
- package/dist/esm/helpers/getAuthData.native.js.map +1 -0
- package/dist/esm/helpers/prettyFormatZeroQuery.js +76 -0
- package/dist/esm/helpers/prettyFormatZeroQuery.js.map +6 -0
- package/dist/esm/helpers/prettyFormatZeroQuery.mjs +84 -0
- package/dist/esm/helpers/prettyFormatZeroQuery.mjs.map +1 -0
- package/dist/esm/helpers/prettyFormatZeroQuery.native.js +93 -0
- package/dist/esm/helpers/prettyFormatZeroQuery.native.js.map +1 -0
- package/dist/esm/helpers/setupZeroClientGlobalEffects.js +40 -0
- package/dist/esm/helpers/setupZeroClientGlobalEffects.js.map +6 -0
- package/dist/esm/helpers/setupZeroClientGlobalEffects.mjs +41 -0
- package/dist/esm/helpers/setupZeroClientGlobalEffects.mjs.map +1 -0
- package/dist/esm/helpers/setupZeroClientGlobalEffects.native.js +41 -0
- package/dist/esm/helpers/setupZeroClientGlobalEffects.native.js.map +1 -0
- package/dist/esm/helpers/useAuthData.js +11 -0
- package/dist/esm/helpers/useAuthData.js.map +6 -0
- package/dist/esm/helpers/useAuthData.mjs +9 -0
- package/dist/esm/helpers/useAuthData.mjs.map +1 -0
- package/dist/esm/helpers/useAuthData.native.js +13 -0
- package/dist/esm/helpers/useAuthData.native.js.map +1 -0
- package/dist/esm/helpers/useZDB.js +38 -0
- package/dist/esm/helpers/useZDB.js.map +6 -0
- package/dist/esm/helpers/useZDB.mjs +47 -0
- package/dist/esm/helpers/useZDB.mjs.map +1 -0
- package/dist/esm/helpers/useZDB.native.js +55 -0
- package/dist/esm/helpers/useZDB.native.js.map +1 -0
- package/dist/esm/helpers/zeroEmitter.js +6 -0
- package/dist/esm/helpers/zeroEmitter.js.map +6 -0
- package/dist/esm/helpers/zeroEmitter.mjs +4 -0
- package/dist/esm/helpers/zeroEmitter.mjs.map +1 -0
- package/dist/esm/helpers/zeroEmitter.native.js +4 -0
- package/dist/esm/helpers/zeroEmitter.native.js.map +1 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +6 -0
- package/dist/esm/index.mjs +7 -0
- package/dist/esm/index.mjs.map +1 -0
- package/dist/esm/index.native.js +7 -0
- package/dist/esm/index.native.js.map +1 -0
- package/dist/esm/types.js +1 -0
- package/dist/esm/types.js.map +6 -0
- package/dist/esm/types.mjs +2 -0
- package/dist/esm/types.mjs.map +1 -0
- package/dist/esm/types.native.js +2 -0
- package/dist/esm/types.native.js.map +1 -0
- package/package.json +51 -0
- package/readme.md +16 -0
- package/src/createPermissions.ts +281 -0
- package/src/createZeroClient.tsx +191 -0
- package/src/createZeroServer.ts +153 -0
- package/src/helpers/batchQuery.ts +45 -0
- package/src/helpers/clearZeroDatabase.ts +68 -0
- package/src/helpers/context.ts +28 -0
- package/src/helpers/createMutators.ts +139 -0
- package/src/helpers/ensureLoggedIn.ts +8 -0
- package/src/helpers/getAuthData.tsx +12 -0
- package/src/helpers/prettyFormatZeroQuery.ts +167 -0
- package/src/helpers/useAuthData.ts +13 -0
- package/src/helpers/useZeroDebug.ts +104 -0
- package/src/helpers/zeroEmitter.ts +5 -0
- package/src/index.ts +15 -0
- package/src/mutations.ts +121 -0
- package/src/types.ts +49 -0
- package/types/createMutations.d.ts +20 -0
- package/types/createMutations.d.ts.map +1 -0
- package/types/createPermissions.d.ts +37 -0
- package/types/createPermissions.d.ts.map +1 -0
- package/types/createZeroClient.d.ts +45 -0
- package/types/createZeroClient.d.ts.map +1 -0
- package/types/createZeroServer.d.ts +61 -0
- package/types/createZeroServer.d.ts.map +1 -0
- package/types/helpers/batchQuery.d.ts +7 -0
- package/types/helpers/batchQuery.d.ts.map +1 -0
- package/types/helpers/clearZeroDatabase.d.ts +2 -0
- package/types/helpers/clearZeroDatabase.d.ts.map +1 -0
- package/types/helpers/context.d.ts +5 -0
- package/types/helpers/context.d.ts.map +1 -0
- package/types/helpers/createMutators.d.ts +16 -0
- package/types/helpers/createMutators.d.ts.map +1 -0
- package/types/helpers/ensureLoggedIn.d.ts +2 -0
- package/types/helpers/ensureLoggedIn.d.ts.map +1 -0
- package/types/helpers/getAuthData.d.ts +1 -0
- package/types/helpers/getAuthData.d.ts.map +1 -0
- package/types/helpers/prettyFormatZeroQuery.d.ts +3 -0
- package/types/helpers/prettyFormatZeroQuery.d.ts.map +1 -0
- package/types/helpers/useAuthData.d.ts +1 -0
- package/types/helpers/useAuthData.d.ts.map +1 -0
- package/types/helpers/useZeroDebug.d.ts +3 -0
- package/types/helpers/useZeroDebug.d.ts.map +1 -0
- package/types/helpers/zeroEmitter.d.ts +2 -0
- package/types/helpers/zeroEmitter.d.ts.map +1 -0
- package/types/index.d.ts +9 -0
- package/types/index.d.ts.map +1 -0
- package/types/types.d.ts +21 -0
- package/types/types.d.ts.map +1 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { showToast } from '~/interface/toast/Toast'
|
|
2
|
+
import { zero } from '../zero'
|
|
3
|
+
|
|
4
|
+
export const clearZeroDatabase = async () => {
|
|
5
|
+
try {
|
|
6
|
+
// Get all IndexedDB databases
|
|
7
|
+
const databases = await indexedDB.databases()
|
|
8
|
+
|
|
9
|
+
// Find Zero/Replicache databases
|
|
10
|
+
const zeroAndReplicacheDatabases = databases.filter((db) => {
|
|
11
|
+
if (!db.name) return false
|
|
12
|
+
const name = db.name.toLowerCase()
|
|
13
|
+
return (
|
|
14
|
+
name.includes('zero') ||
|
|
15
|
+
name.includes('replicache') ||
|
|
16
|
+
name.includes('roc') || // rocicorp prefix
|
|
17
|
+
name.startsWith('rep:') // replicache prefix
|
|
18
|
+
)
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
if (zeroAndReplicacheDatabases.length > 0) {
|
|
22
|
+
// Delete all Zero/Replicache databases
|
|
23
|
+
await Promise.all(
|
|
24
|
+
zeroAndReplicacheDatabases.map((db) => {
|
|
25
|
+
return new Promise<void>((resolve, reject) => {
|
|
26
|
+
const deleteReq = indexedDB.deleteDatabase(db.name!)
|
|
27
|
+
deleteReq.onsuccess = () => resolve()
|
|
28
|
+
deleteReq.onerror = () => reject(deleteReq.error)
|
|
29
|
+
deleteReq.onblocked = () => reject(new Error('database deletion blocked'))
|
|
30
|
+
})
|
|
31
|
+
})
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
const dbNames = zeroAndReplicacheDatabases.map((db) => db.name).join(', ')
|
|
35
|
+
showToast(
|
|
36
|
+
`Cleared ${zeroAndReplicacheDatabases.length} Zero/Replicache databases: ${dbNames}`
|
|
37
|
+
)
|
|
38
|
+
} else {
|
|
39
|
+
// Fallback: clear all IndexedDB databases
|
|
40
|
+
await Promise.all(
|
|
41
|
+
databases.map((db) => {
|
|
42
|
+
if (db.name) {
|
|
43
|
+
return new Promise<void>((resolve, reject) => {
|
|
44
|
+
const deleteReq = indexedDB.deleteDatabase(db.name!)
|
|
45
|
+
deleteReq.onsuccess = () => resolve()
|
|
46
|
+
deleteReq.onerror = () => reject(deleteReq.error)
|
|
47
|
+
deleteReq.onblocked = () => reject(new Error('database deletion blocked'))
|
|
48
|
+
})
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
)
|
|
52
|
+
showToast('Cleared all IndexedDB databases')
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Close the zero connection before reloading
|
|
56
|
+
if (zero && typeof (zero as any).close === 'function') {
|
|
57
|
+
await (zero as any).close()
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Reload the page to reinitialize Zero
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
window.location.reload()
|
|
63
|
+
}, 1000)
|
|
64
|
+
} catch (error) {
|
|
65
|
+
console.error('Error clearing Zero database:', error)
|
|
66
|
+
showToast('Error clearing Zero database')
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createAsyncContext } from '@vxrn/helpers'
|
|
2
|
+
import type { MutatorContext } from '../types'
|
|
3
|
+
|
|
4
|
+
// TODO likely should be called "DataContext" or "ZeroContext" as its useful for permissions outside mutators
|
|
5
|
+
|
|
6
|
+
const asyncContext = createAsyncContext<MutatorContext>()
|
|
7
|
+
|
|
8
|
+
// TODO probably rename mutatorContext()
|
|
9
|
+
|
|
10
|
+
export function context(): MutatorContext {
|
|
11
|
+
const currentContext = asyncContext.get()
|
|
12
|
+
if (!currentContext) {
|
|
13
|
+
throw new Error('context must be called within a mutator')
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return currentContext
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function isInZeroMutation() {
|
|
20
|
+
return !!asyncContext.get()
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function runWithContext<T>(
|
|
24
|
+
context: MutatorContext,
|
|
25
|
+
fn: () => T | Promise<T>
|
|
26
|
+
): Promise<T> {
|
|
27
|
+
return asyncContext.run(context, fn)
|
|
28
|
+
}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { isClient, isServer, mapObject, time } from '@vxrn/helpers'
|
|
2
|
+
import type { AuthData } from 'start/types'
|
|
3
|
+
import type { GenericTransaction, MutatorContext } from '../types'
|
|
4
|
+
import { runWithContext } from './context'
|
|
5
|
+
|
|
6
|
+
type MutatorProps = {
|
|
7
|
+
environment: 'server' | 'client'
|
|
8
|
+
authData: AuthData | null
|
|
9
|
+
can: (where: string, action: () => Promise<any>, obj: any) => void
|
|
10
|
+
models: any
|
|
11
|
+
asyncTasks?: Array<() => Promise<void>>
|
|
12
|
+
createServerActions?: (authData: AuthData | null) => Record<string, any>
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
type TransformMutators<T> = {
|
|
16
|
+
[K in keyof T]: T[K] extends (ctx: MutatorContext, ...args: infer Args) => infer Return
|
|
17
|
+
? (tx: GenericTransaction, ...args: Args) => Return
|
|
18
|
+
: never
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function createMutators<
|
|
22
|
+
MutatorsIn extends Record<string, any>,
|
|
23
|
+
Mutators extends {
|
|
24
|
+
[K in keyof MutatorsIn]: TransformMutators<MutatorsIn[K]>
|
|
25
|
+
},
|
|
26
|
+
>({
|
|
27
|
+
environment,
|
|
28
|
+
authData,
|
|
29
|
+
createServerActions,
|
|
30
|
+
asyncTasks = [],
|
|
31
|
+
can,
|
|
32
|
+
models,
|
|
33
|
+
}: MutatorProps) {
|
|
34
|
+
const serverActions = createServerActions?.(authData)
|
|
35
|
+
|
|
36
|
+
const modelMutators = mapObject(models, (val) => val.mutate) as {
|
|
37
|
+
[K in keyof typeof models]: (typeof models)[K]['mutate']
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function withContext<Args extends any[]>(fn: (...args: Args) => Promise<void>) {
|
|
41
|
+
return async (tx: GenericTransaction, ...args: Args): Promise<void> => {
|
|
42
|
+
const mutationContext = {
|
|
43
|
+
tx,
|
|
44
|
+
authData,
|
|
45
|
+
environment,
|
|
46
|
+
can,
|
|
47
|
+
serverActions,
|
|
48
|
+
asyncTasks,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return await runWithContext(mutationContext, () => {
|
|
52
|
+
// @ts-expect-error type shenanigan
|
|
53
|
+
// map to our mutations() helper
|
|
54
|
+
return fn(mutationContext, ...args)
|
|
55
|
+
})
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function withDevelopmentLogging<Args extends any[]>(
|
|
60
|
+
name: string,
|
|
61
|
+
fn: (...args: Args) => Promise<void>
|
|
62
|
+
) {
|
|
63
|
+
if (process.env.NODE_ENV !== 'development' && !process.env.IS_TESTING) {
|
|
64
|
+
return fn
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return async (...args: Args): Promise<void> => {
|
|
68
|
+
const startTime = performance.now()
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
if (isServer) {
|
|
72
|
+
console.info(`[mutator] ${name} start`)
|
|
73
|
+
}
|
|
74
|
+
const result = await fn(...args)
|
|
75
|
+
const duration = (performance.now() - startTime).toFixed(2)
|
|
76
|
+
if (isClient) {
|
|
77
|
+
console.groupCollapsed(`[mutator] ${name} completed in ${duration}ms`)
|
|
78
|
+
console.info('→', args[1])
|
|
79
|
+
console.info('←', result)
|
|
80
|
+
console.trace()
|
|
81
|
+
console.groupEnd()
|
|
82
|
+
} else {
|
|
83
|
+
// TODO in prod just track
|
|
84
|
+
console.info(`[mutator] ${name} completed in ${duration}ms`)
|
|
85
|
+
}
|
|
86
|
+
return result
|
|
87
|
+
} catch (error) {
|
|
88
|
+
const duration = (performance.now() - startTime).toFixed(2)
|
|
89
|
+
console.groupCollapsed(`[mutator] ${name} failed after ${duration}ms`)
|
|
90
|
+
console.error('error:', error)
|
|
91
|
+
console.info('arguments:', JSON.stringify(args[1], null, 2))
|
|
92
|
+
console.info('stack trace:', new Error().stack)
|
|
93
|
+
console.groupEnd()
|
|
94
|
+
throw error
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function withTimeoutGuard<Args extends any[]>(
|
|
100
|
+
name: string,
|
|
101
|
+
fn: (...args: Args) => Promise<void>,
|
|
102
|
+
// don't want this too high - zero runs mutations in order and waits for the last to finish it seems
|
|
103
|
+
// so if one mutation gets stuck it will just sit there
|
|
104
|
+
timeoutMs: number = time.ms.minutes(1)
|
|
105
|
+
) {
|
|
106
|
+
return async (...args: Args): Promise<void> => {
|
|
107
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
108
|
+
setTimeout(() => {
|
|
109
|
+
reject(new Error(`[mutator] ${name} timeout after ${timeoutMs}ms`))
|
|
110
|
+
}, timeoutMs)
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
return Promise.race([fn(...args), timeoutPromise])
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function decorateMutators<T extends Record<string, Record<string, any>>>(
|
|
118
|
+
modules: T
|
|
119
|
+
): Mutators {
|
|
120
|
+
const result: any = {}
|
|
121
|
+
|
|
122
|
+
for (const [moduleName, moduleExports] of Object.entries(modules)) {
|
|
123
|
+
result[moduleName] = {}
|
|
124
|
+
for (const [name, exportValue] of Object.entries(moduleExports)) {
|
|
125
|
+
if (typeof exportValue === 'function') {
|
|
126
|
+
const fullName = `${moduleName}.${name}`
|
|
127
|
+
result[moduleName][name] = withDevelopmentLogging(
|
|
128
|
+
fullName,
|
|
129
|
+
withTimeoutGuard(fullName, withContext(exportValue))
|
|
130
|
+
)
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return result
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return decorateMutators(modelMutators)
|
|
139
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { Query } from '@rocicorp/zero'
|
|
2
|
+
import { ellipsis } from '@vxrn/helpers'
|
|
3
|
+
|
|
4
|
+
export const prettyFormatZeroQuery = (
|
|
5
|
+
query: Query<any, any, any>,
|
|
6
|
+
mode: 'full' | 'minimal' = 'full'
|
|
7
|
+
): string => {
|
|
8
|
+
// @ts-expect-error accessing hidden property
|
|
9
|
+
const astObject = query['_completeAst']?.()
|
|
10
|
+
|
|
11
|
+
if (!astObject) return ''
|
|
12
|
+
|
|
13
|
+
if (mode === 'minimal') {
|
|
14
|
+
return prettyFormatMinimal(astObject)
|
|
15
|
+
}
|
|
16
|
+
return prettyFormatFull(astObject)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const prettyFormatFull = (astObject: any, indent = 0): string => {
|
|
20
|
+
if (!astObject || !astObject.table) return ''
|
|
21
|
+
|
|
22
|
+
const spaces = ' '.repeat(indent)
|
|
23
|
+
let query = astObject.table
|
|
24
|
+
let hasChainedMethods = false
|
|
25
|
+
|
|
26
|
+
// Add where conditions
|
|
27
|
+
if (astObject.where) {
|
|
28
|
+
const whereClause = formatWhere(astObject.where)
|
|
29
|
+
if (hasChainedMethods) {
|
|
30
|
+
query += `\n${spaces} ${whereClause}`
|
|
31
|
+
} else {
|
|
32
|
+
query += whereClause
|
|
33
|
+
hasChainedMethods = true
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Add limit
|
|
38
|
+
if (astObject.limit) {
|
|
39
|
+
const limitClause = `.limit(${astObject.limit})`
|
|
40
|
+
if (hasChainedMethods) {
|
|
41
|
+
query += `\n${spaces} ${limitClause}`
|
|
42
|
+
} else {
|
|
43
|
+
query += limitClause
|
|
44
|
+
hasChainedMethods = true
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// Add orderBy
|
|
49
|
+
if (astObject.orderBy && astObject.orderBy.length > 0) {
|
|
50
|
+
const orderClauses = astObject.orderBy
|
|
51
|
+
.map(([field, direction]: [string, string]) => `${field}, ${direction}`)
|
|
52
|
+
.join(', ')
|
|
53
|
+
const orderByClause = `.orderBy(${orderClauses})`
|
|
54
|
+
if (hasChainedMethods) {
|
|
55
|
+
query += `\n${spaces} ${orderByClause}`
|
|
56
|
+
} else {
|
|
57
|
+
query += orderByClause
|
|
58
|
+
hasChainedMethods = true
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Add related queries
|
|
63
|
+
if (astObject.related && astObject.related.length > 0) {
|
|
64
|
+
astObject.related.forEach((rel: any) => {
|
|
65
|
+
if (rel.subquery) {
|
|
66
|
+
const alias = rel.subquery.alias || rel.subquery.table
|
|
67
|
+
const subQuery = prettyFormatFull(rel.subquery, indent + 1)
|
|
68
|
+
query += `\n${spaces} .related(${alias}, q => q.${subQuery}`
|
|
69
|
+
}
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// Add closing parentheses
|
|
73
|
+
const closingParens = ')'.repeat(astObject.related.length)
|
|
74
|
+
query += `\n${spaces}${closingParens}`
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return query
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const prettyFormatMinimal = (astObject: any): string => {
|
|
81
|
+
if (!astObject || !astObject.table) return ''
|
|
82
|
+
|
|
83
|
+
let query = astObject.table
|
|
84
|
+
|
|
85
|
+
// Add where conditions only
|
|
86
|
+
if (astObject.where) {
|
|
87
|
+
query += formatWhere(astObject.where).replace('.where(', '(')
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Add sub-queries info if present
|
|
91
|
+
if (astObject.related && astObject.related.length > 0) {
|
|
92
|
+
const subQueries = collectSubQueryTables(astObject.related)
|
|
93
|
+
const count = subQueries.length
|
|
94
|
+
const tableNames = subQueries.join(', ')
|
|
95
|
+
query += ` (+${count}: ${ellipsis(tableNames, 30)})`
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return query
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const collectSubQueryTables = (related: any[]): string[] => {
|
|
102
|
+
const tables: string[] = []
|
|
103
|
+
|
|
104
|
+
related.forEach((rel: any) => {
|
|
105
|
+
if (rel.subquery) {
|
|
106
|
+
const tableName = rel.subquery.alias || rel.subquery.table
|
|
107
|
+
tables.push(tableName)
|
|
108
|
+
|
|
109
|
+
// Recursively collect nested sub-queries
|
|
110
|
+
if (rel.subquery.related && rel.subquery.related.length > 0) {
|
|
111
|
+
tables.push(...collectSubQueryTables(rel.subquery.related))
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
return tables
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const formatWhere = (where: any): string => {
|
|
120
|
+
if (!where) return ''
|
|
121
|
+
|
|
122
|
+
if (where.type === 'simple') {
|
|
123
|
+
const column = where.left?.name || where.left
|
|
124
|
+
const value = where.right?.value !== undefined ? where.right.value : where.right
|
|
125
|
+
const op = where.op || '='
|
|
126
|
+
|
|
127
|
+
// Special case: if column is "id" and op is "=" and value is a single item, show just the value
|
|
128
|
+
if (
|
|
129
|
+
column === 'id' &&
|
|
130
|
+
op === '=' &&
|
|
131
|
+
(typeof value === 'string' || typeof value === 'number')
|
|
132
|
+
) {
|
|
133
|
+
return `(${value})`
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
if (op === '=') {
|
|
137
|
+
return `.where(${column}, ${value})`
|
|
138
|
+
}
|
|
139
|
+
return `.where(${column}, ${op}, ${value})`
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (where.type === 'and' && where.conditions) {
|
|
143
|
+
let result = ''
|
|
144
|
+
where.conditions.forEach((condition: any, index: number) => {
|
|
145
|
+
if (index === 0) {
|
|
146
|
+
result += formatWhere(condition)
|
|
147
|
+
} else {
|
|
148
|
+
result += `.and(${formatWhere(condition).slice(1)})` // Remove the leading dot
|
|
149
|
+
}
|
|
150
|
+
})
|
|
151
|
+
return result
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
if (where.type === 'or' && where.conditions) {
|
|
155
|
+
let result = ''
|
|
156
|
+
where.conditions.forEach((condition: any, index: number) => {
|
|
157
|
+
if (index === 0) {
|
|
158
|
+
result += formatWhere(condition)
|
|
159
|
+
} else {
|
|
160
|
+
result += `.or(${formatWhere(condition).slice(1)})` // Remove the leading dot
|
|
161
|
+
}
|
|
162
|
+
})
|
|
163
|
+
return result
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return ''
|
|
167
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
// import { useMemo } from 'react'
|
|
2
|
+
// import { useAuth } from '~/auth/client/authClient'
|
|
3
|
+
// import { getAuthData } from './getAuthData'
|
|
4
|
+
|
|
5
|
+
// export const useAuthData = () => {
|
|
6
|
+
// const auth = useAuth()
|
|
7
|
+
// const userId = auth?.user?.id || 'anon'
|
|
8
|
+
|
|
9
|
+
// // biome-ignore lint/correctness/useExhaustiveDependencies: its based on id
|
|
10
|
+
// return useMemo(() => {
|
|
11
|
+
// return getAuthData()
|
|
12
|
+
// }, [userId])
|
|
13
|
+
// }
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { Query } from '@rocicorp/zero'
|
|
2
|
+
import { getCurrentComponentStack } from '@vxrn/helpers'
|
|
3
|
+
import { useEffect, useId } from 'react'
|
|
4
|
+
import { prettyFormatZeroQuery } from './prettyFormatZeroQuery'
|
|
5
|
+
|
|
6
|
+
const activeQueries = new Map<string, number>()
|
|
7
|
+
|
|
8
|
+
// AST change tracking
|
|
9
|
+
interface AstHistory {
|
|
10
|
+
asts: string[]
|
|
11
|
+
changeCount: number
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
const astHistoryByComponent = new Map<string, AstHistory>()
|
|
15
|
+
|
|
16
|
+
// control what is logged here:
|
|
17
|
+
const filterLogs = (table: string): boolean => false
|
|
18
|
+
|
|
19
|
+
const COLLAPSED = true
|
|
20
|
+
const AST_CHANGE_THRESHOLD = 4
|
|
21
|
+
const MAX_AST_HISTORY = 10
|
|
22
|
+
|
|
23
|
+
// short name because otherwise it often forces multiple lines in chrome devtools
|
|
24
|
+
// due to showing the filename next to log lines
|
|
25
|
+
export const useZeroDebug = (query: Query<any, any, any>, options: any, results: any) => {
|
|
26
|
+
// if (DEBUG_LEVEL < 2) {
|
|
27
|
+
// return
|
|
28
|
+
// }
|
|
29
|
+
|
|
30
|
+
const astObject = query['_completeAst']?.()
|
|
31
|
+
const table = astObject.table
|
|
32
|
+
const ast = JSON.stringify(astObject, null, 2)
|
|
33
|
+
const queryDisabled = !options || options?.enabled === false
|
|
34
|
+
const enabled = !queryDisabled && filterLogs(table)
|
|
35
|
+
const stack = new Error().stack
|
|
36
|
+
const isPermissionQuery = stack?.includes(`usePermission.ts`)
|
|
37
|
+
const id = useId()
|
|
38
|
+
|
|
39
|
+
// log here not in effect so we can breakpoint and find the query
|
|
40
|
+
const num = activeQueries.get(ast) || 0
|
|
41
|
+
const shouldLog = enabled && num === 0
|
|
42
|
+
if (enabled) {
|
|
43
|
+
activeQueries.set(ast, num + 1)
|
|
44
|
+
if (shouldLog) {
|
|
45
|
+
if (COLLAPSED) {
|
|
46
|
+
console.groupCollapsed(
|
|
47
|
+
`${isPermissionQuery ? `👮♂️` : `✨`}${prettyFormatZeroQuery(query, 'minimal')}`
|
|
48
|
+
)
|
|
49
|
+
console.info(id, prettyFormatZeroQuery(query, 'full'))
|
|
50
|
+
console.info('cached result', results)
|
|
51
|
+
console.trace()
|
|
52
|
+
console.groupEnd()
|
|
53
|
+
} else {
|
|
54
|
+
console.info(`✨`, prettyFormatZeroQuery(query, 'full'))
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// track AST changes per component
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (!enabled) return
|
|
62
|
+
const history = astHistoryByComponent.get(id) || { asts: [], changeCount: 0 }
|
|
63
|
+
const currentAst = ast
|
|
64
|
+
const lastAst = history.asts[history.asts.length - 1]
|
|
65
|
+
|
|
66
|
+
if (currentAst !== lastAst) {
|
|
67
|
+
history.asts.push(currentAst)
|
|
68
|
+
if (history.asts.length > MAX_AST_HISTORY) {
|
|
69
|
+
history.asts.shift()
|
|
70
|
+
}
|
|
71
|
+
history.changeCount++
|
|
72
|
+
astHistoryByComponent.set(id, history)
|
|
73
|
+
|
|
74
|
+
if (history.changeCount > AST_CHANGE_THRESHOLD) {
|
|
75
|
+
console.warn(
|
|
76
|
+
`⚠️ AST changed ${history.changeCount} times for component.
|
|
77
|
+
- id: ${id}
|
|
78
|
+
- stack: ${getCurrentComponentStack('short')}
|
|
79
|
+
- table: ${table}`,
|
|
80
|
+
{
|
|
81
|
+
componentId: id,
|
|
82
|
+
table,
|
|
83
|
+
changeCount: history.changeCount,
|
|
84
|
+
recentAsts: history.asts,
|
|
85
|
+
}
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}, [id, ast, table, enabled])
|
|
90
|
+
|
|
91
|
+
useEffect(() => {
|
|
92
|
+
if (!enabled) return
|
|
93
|
+
return () => {
|
|
94
|
+
activeQueries.set(ast, activeQueries.get(ast)! - 1)
|
|
95
|
+
}
|
|
96
|
+
}, [ast, enabled])
|
|
97
|
+
|
|
98
|
+
// cleanup AST history on unmount
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
return () => {
|
|
101
|
+
astHistoryByComponent.delete(id)
|
|
102
|
+
}
|
|
103
|
+
}, [id])
|
|
104
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export * from './createPermissions'
|
|
2
|
+
export * from './helpers/batchQuery'
|
|
3
|
+
export * from './helpers/context'
|
|
4
|
+
export * from './helpers/createMutators'
|
|
5
|
+
export * from './helpers/ensureLoggedIn'
|
|
6
|
+
|
|
7
|
+
export * from './mutations'
|
|
8
|
+
export * from './createZeroClient'
|
|
9
|
+
export * from './createZeroServer'
|
|
10
|
+
|
|
11
|
+
export type * from './types'
|
|
12
|
+
|
|
13
|
+
// export * from '~/data/schema'
|
|
14
|
+
// export * from './types'
|
|
15
|
+
// export * from '../zero'
|
package/src/mutations.ts
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { TableBuilderWithColumns } from '@rocicorp/zero'
|
|
2
|
+
import type { MutatorContext, TableInsertRow, TableUpdateRow, Where } from './types'
|
|
3
|
+
|
|
4
|
+
// two ways to use it:
|
|
5
|
+
// - mutations({}) which doesn't add the "allowed" helper or add CRUD
|
|
6
|
+
// - mutation('tableName', permissions) adds CRUD with permissions, adds allowed
|
|
7
|
+
|
|
8
|
+
type MutationBuilder<Obj = any> = (ctx: MutatorContext, obj?: Obj) => Promise<void>
|
|
9
|
+
type MutationBuilders = Record<string, MutationBuilder>
|
|
10
|
+
|
|
11
|
+
// start of adding custom can.write(message) style
|
|
12
|
+
|
|
13
|
+
// type PermissionedMutationBuilder<Permissions extends PermissionsWhere, Obj = any> = (
|
|
14
|
+
// ctx: MutatorContext & {
|
|
15
|
+
// can: any
|
|
16
|
+
// },
|
|
17
|
+
// obj?: Obj
|
|
18
|
+
// ) => Promise<void>
|
|
19
|
+
// type PermissionedMutationBuilders<Permissions extends PermissionsWhere> = Record<
|
|
20
|
+
// string,
|
|
21
|
+
// PermissionedMutationBuilder<Permissions>
|
|
22
|
+
// >
|
|
23
|
+
|
|
24
|
+
type GenericTable = TableBuilderWithColumns<any>
|
|
25
|
+
|
|
26
|
+
type CRUDMutations<Table extends GenericTable> = {
|
|
27
|
+
insert: MutationBuilder<TableInsertRow<Table>>
|
|
28
|
+
upsert: MutationBuilder<TableInsertRow<Table>>
|
|
29
|
+
update: MutationBuilder<TableUpdateRow<Table>>
|
|
30
|
+
delete: MutationBuilder<TableUpdateRow<Table>>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
type CRUDNames = 'insert' | 'upsert' | 'update' | 'delete'
|
|
34
|
+
|
|
35
|
+
type MutationsWithCRUD<Table extends GenericTable, Mutations extends MutationBuilders> = {
|
|
36
|
+
[Key in CRUDNames | keyof Mutations]: Key extends keyof Mutations
|
|
37
|
+
? Mutations[Key]
|
|
38
|
+
: Key extends keyof CRUDMutations<any>
|
|
39
|
+
? CRUDMutations<Table>[Key]
|
|
40
|
+
: never
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function mutations<Table extends GenericTable, Permissions extends Where>(
|
|
44
|
+
table: Table,
|
|
45
|
+
permissions: Permissions
|
|
46
|
+
): MutationsWithCRUD<Table, {}>
|
|
47
|
+
|
|
48
|
+
export function mutations<
|
|
49
|
+
Table extends GenericTable,
|
|
50
|
+
Permissions extends Where,
|
|
51
|
+
Mutations extends MutationBuilders,
|
|
52
|
+
>(
|
|
53
|
+
table: Table,
|
|
54
|
+
permissions: Permissions,
|
|
55
|
+
mutations: Mutations
|
|
56
|
+
): MutationsWithCRUD<Table, Mutations>
|
|
57
|
+
|
|
58
|
+
export function mutations<Mutations extends MutationBuilder>(
|
|
59
|
+
mutations: Mutations
|
|
60
|
+
): Mutations
|
|
61
|
+
|
|
62
|
+
// TODO we should enforece the CRUD mutations obj to the callier so they get it auto-typed
|
|
63
|
+
export function mutations<
|
|
64
|
+
Table extends GenericTable,
|
|
65
|
+
Mutations extends Record<string, MutationBuilder>,
|
|
66
|
+
>(table: Table | Mutations, permissions?: Where, mutations?: Mutations): Mutations {
|
|
67
|
+
if (permissions) {
|
|
68
|
+
const tableName = (table as Table).schema.name
|
|
69
|
+
|
|
70
|
+
const createCRUDMutation = (action: CRUDNames) => {
|
|
71
|
+
return async (ctx: MutatorContext, obj: any) => {
|
|
72
|
+
// run permission query - for delete before the mutation runs, for the rest after
|
|
73
|
+
// when the "can" query is unsuccessful it throws an error which will revert everything
|
|
74
|
+
const runServerPermissionCheck = async () => {
|
|
75
|
+
if (ctx.didCanPermissionsRun) {
|
|
76
|
+
// if the user-defined CRUD mutation runs their own "can", we avoid running ours
|
|
77
|
+
return
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// only validate on the server
|
|
81
|
+
if (process.env.VITE_ENVIRONMENT === 'ssr') {
|
|
82
|
+
await ctx.can(permissions, action, obj)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (action === 'delete') {
|
|
87
|
+
await runServerPermissionCheck()
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// if user defines insert run theirs, if not run plain zero:
|
|
91
|
+
const existing = mutations?.[action]
|
|
92
|
+
|
|
93
|
+
if (existing) {
|
|
94
|
+
await existing(ctx, obj)
|
|
95
|
+
} else {
|
|
96
|
+
await ctx.tx.mutate[tableName][action](obj)
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (action !== 'delete') {
|
|
100
|
+
await runServerPermissionCheck()
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const crudMutations: CRUDMutations<any> = {
|
|
106
|
+
insert: createCRUDMutation('insert'),
|
|
107
|
+
update: createCRUDMutation('update'),
|
|
108
|
+
delete: createCRUDMutation('delete'),
|
|
109
|
+
upsert: createCRUDMutation('upsert'),
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
return {
|
|
113
|
+
...mutations,
|
|
114
|
+
// overwrite regular mutations but call them if they are defined by user
|
|
115
|
+
...crudMutations,
|
|
116
|
+
} as any as Mutations
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// no schema/permissions don't add CRUD
|
|
120
|
+
return table as any
|
|
121
|
+
}
|