over-zero 0.0.35 → 0.0.37
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/cli.cjs +16 -10
- package/dist/cjs/cli.js +15 -8
- package/dist/cjs/cli.js.map +1 -1
- package/dist/cjs/cli.native.js +18 -10
- package/dist/cjs/cli.native.js.map +1 -1
- package/dist/cjs/createPermissions.cjs +7 -6
- package/dist/cjs/createPermissions.js +6 -6
- package/dist/cjs/createPermissions.js.map +1 -1
- package/dist/cjs/createPermissions.native.js +8 -6
- package/dist/cjs/createPermissions.native.js.map +1 -1
- package/dist/cjs/createUseQuery.cjs +18 -27
- package/dist/cjs/createUseQuery.js +18 -19
- package/dist/cjs/createUseQuery.js.map +1 -1
- package/dist/cjs/createUseQuery.native.js +19 -32
- package/dist/cjs/createUseQuery.native.js.map +1 -1
- package/dist/cjs/createZeroClient.cjs +38 -16
- package/dist/cjs/createZeroClient.js +40 -28
- package/dist/cjs/createZeroClient.js.map +2 -2
- package/dist/cjs/createZeroClient.native.js +45 -21
- package/dist/cjs/createZeroClient.native.js.map +1 -1
- package/dist/cjs/createZeroServer.cjs +16 -16
- package/dist/cjs/createZeroServer.js +15 -19
- package/dist/cjs/createZeroServer.js.map +2 -2
- package/dist/cjs/createZeroServer.native.js +19 -50
- package/dist/cjs/createZeroServer.native.js.map +1 -1
- package/dist/cjs/helpers/batchQuery.cjs +1 -1
- package/dist/cjs/helpers/batchQuery.js +1 -1
- package/dist/cjs/helpers/batchQuery.native.js +1 -1
- package/dist/cjs/helpers/batchQuery.native.js.map +1 -1
- package/dist/cjs/helpers/createMutators.cjs +1 -1
- package/dist/cjs/helpers/createMutators.js +1 -1
- package/dist/cjs/helpers/createMutators.js.map +1 -1
- package/dist/cjs/helpers/createMutators.native.js +1 -1
- package/dist/cjs/helpers/didRunPermissionCheck.cjs +1 -1
- package/dist/cjs/helpers/didRunPermissionCheck.js +1 -1
- package/dist/cjs/helpers/didRunPermissionCheck.native.js +1 -1
- package/dist/cjs/helpers/didRunPermissionCheck.native.js.map +1 -1
- package/dist/cjs/helpers/ensureLoggedIn.cjs +1 -1
- package/dist/cjs/helpers/ensureLoggedIn.js +1 -1
- package/dist/cjs/helpers/ensureLoggedIn.js.map +1 -1
- package/dist/cjs/helpers/ensureLoggedIn.native.js +1 -1
- package/dist/cjs/helpers/mutatorContext.cjs +1 -1
- package/dist/cjs/helpers/mutatorContext.js +1 -1
- package/dist/cjs/helpers/mutatorContext.native.js +1 -1
- package/dist/cjs/helpers/mutatorContext.native.js.map +1 -1
- package/dist/cjs/helpers/prettyFormatZeroQuery.cjs +1 -1
- package/dist/cjs/helpers/prettyFormatZeroQuery.js +1 -1
- package/dist/cjs/helpers/prettyFormatZeroQuery.native.js +1 -1
- package/dist/cjs/helpers/prettyFormatZeroQuery.native.js.map +1 -1
- package/dist/cjs/helpers/useZeroDebug.cjs +3 -3
- package/dist/cjs/helpers/useZeroDebug.js +2 -2
- package/dist/cjs/helpers/useZeroDebug.js.map +1 -1
- package/dist/cjs/helpers/useZeroDebug.native.js +5 -3
- package/dist/cjs/helpers/useZeroDebug.native.js.map +1 -1
- package/dist/cjs/where.cjs +1 -1
- package/dist/cjs/where.js +1 -1
- package/dist/cjs/where.js.map +1 -1
- package/dist/cjs/where.native.js +1 -1
- package/dist/cjs/where.native.js.map +1 -1
- package/dist/esm/cli.js +15 -8
- package/dist/esm/cli.js.map +1 -1
- package/dist/esm/cli.mjs +16 -10
- package/dist/esm/cli.mjs.map +1 -1
- package/dist/esm/cli.native.js +18 -10
- package/dist/esm/cli.native.js.map +1 -1
- package/dist/esm/createPermissions.js +5 -4
- package/dist/esm/createPermissions.js.map +1 -1
- package/dist/esm/createPermissions.mjs +5 -4
- package/dist/esm/createPermissions.mjs.map +1 -1
- package/dist/esm/createPermissions.native.js +6 -4
- package/dist/esm/createPermissions.native.js.map +1 -1
- package/dist/esm/createUseQuery.js +17 -19
- package/dist/esm/createUseQuery.js.map +1 -1
- package/dist/esm/createUseQuery.mjs +17 -26
- package/dist/esm/createUseQuery.mjs.map +1 -1
- package/dist/esm/createUseQuery.native.js +18 -31
- package/dist/esm/createUseQuery.native.js.map +1 -1
- package/dist/esm/createZeroClient.js +55 -30
- package/dist/esm/createZeroClient.js.map +2 -2
- package/dist/esm/createZeroClient.mjs +39 -17
- package/dist/esm/createZeroClient.mjs.map +1 -1
- package/dist/esm/createZeroClient.native.js +47 -23
- package/dist/esm/createZeroClient.native.js.map +1 -1
- package/dist/esm/createZeroServer.js +18 -20
- package/dist/esm/createZeroServer.js.map +1 -1
- package/dist/esm/createZeroServer.mjs +16 -16
- package/dist/esm/createZeroServer.mjs.map +1 -1
- package/dist/esm/createZeroServer.native.js +19 -50
- package/dist/esm/createZeroServer.native.js.map +1 -1
- package/dist/esm/helpers/batchQuery.js +1 -1
- package/dist/esm/helpers/batchQuery.mjs +1 -1
- package/dist/esm/helpers/batchQuery.native.js +1 -1
- package/dist/esm/helpers/createMutators.js +1 -1
- package/dist/esm/helpers/createMutators.mjs +1 -1
- package/dist/esm/helpers/createMutators.native.js +1 -1
- package/dist/esm/helpers/didRunPermissionCheck.js +1 -1
- package/dist/esm/helpers/didRunPermissionCheck.mjs +1 -1
- package/dist/esm/helpers/didRunPermissionCheck.native.js +1 -1
- package/dist/esm/helpers/ensureLoggedIn.js +1 -1
- package/dist/esm/helpers/ensureLoggedIn.mjs +1 -1
- package/dist/esm/helpers/ensureLoggedIn.native.js +1 -1
- package/dist/esm/helpers/mutatorContext.js +1 -1
- package/dist/esm/helpers/mutatorContext.mjs +1 -1
- package/dist/esm/helpers/mutatorContext.native.js +1 -1
- package/dist/esm/helpers/prettyFormatZeroQuery.js +1 -1
- package/dist/esm/helpers/prettyFormatZeroQuery.mjs +1 -1
- package/dist/esm/helpers/prettyFormatZeroQuery.native.js +1 -1
- package/dist/esm/helpers/useZeroDebug.js +2 -2
- package/dist/esm/helpers/useZeroDebug.js.map +1 -1
- package/dist/esm/helpers/useZeroDebug.mjs +3 -3
- package/dist/esm/helpers/useZeroDebug.mjs.map +1 -1
- package/dist/esm/helpers/useZeroDebug.native.js +5 -3
- package/dist/esm/helpers/useZeroDebug.native.js.map +1 -1
- package/dist/esm/where.js +1 -1
- package/dist/esm/where.mjs +1 -1
- package/dist/esm/where.native.js +1 -1
- package/package.json +2 -2
- package/readme.md +20 -6
- package/src/cli.ts +23 -22
- package/src/createPermissions.ts +6 -4
- package/src/createUseQuery.tsx +34 -69
- package/src/createZeroClient.tsx +81 -33
- package/src/createZeroServer.ts +27 -35
- package/src/helpers/batchQuery.ts +1 -1
- package/src/helpers/createMutators.ts +1 -1
- package/src/helpers/didRunPermissionCheck.ts +1 -1
- package/src/helpers/ensureLoggedIn.ts +1 -1
- package/src/helpers/mutatorContext.ts +1 -1
- package/src/helpers/prettyFormatZeroQuery.ts +1 -1
- package/src/helpers/useZeroDebug.ts +3 -3
- package/src/types.ts +2 -2
- package/src/where.ts +1 -1
- package/types/createPermissions.d.ts.map +1 -1
- package/types/createUseQuery.d.ts +7 -9
- package/types/createUseQuery.d.ts.map +1 -1
- package/types/createZeroClient.d.ts +5 -6
- package/types/createZeroClient.d.ts.map +1 -1
- package/types/createZeroServer.d.ts +69 -18
- package/types/createZeroServer.d.ts.map +1 -1
- package/types/types.d.ts +2 -2
- package/types/types.d.ts.map +1 -1
package/readme.md
CHANGED
|
@@ -212,7 +212,7 @@ generates all files needed to connect your models and queries:
|
|
|
212
212
|
- `models.ts` - aggregates all model files into a single import
|
|
213
213
|
- `types.ts` - generates TypeScript types from table schemas
|
|
214
214
|
- `tables.ts` - exports table schemas (separate to avoid circular types)
|
|
215
|
-
- `
|
|
215
|
+
- `syncedQueries.ts` - generates synced query definitions with valibot validators
|
|
216
216
|
|
|
217
217
|
**options:**
|
|
218
218
|
|
|
@@ -283,7 +283,7 @@ export { schema as channel } from '~/data/models/channel'
|
|
|
283
283
|
export { schema as message } from '~/data/models/message'
|
|
284
284
|
```
|
|
285
285
|
|
|
286
|
-
**
|
|
286
|
+
**syncedQueries.ts:**
|
|
287
287
|
|
|
288
288
|
```ts
|
|
289
289
|
import * as v from 'valibot'
|
|
@@ -329,12 +329,14 @@ client:
|
|
|
329
329
|
|
|
330
330
|
```tsx
|
|
331
331
|
import { createZeroClient } from 'over-zero'
|
|
332
|
-
import { schema } from '
|
|
333
|
-
import { models } from '
|
|
332
|
+
import { schema } from '~/data/schema'
|
|
333
|
+
import { models } from '~/data/generated/models'
|
|
334
|
+
import * as groupedQueries from '~/data/generated/groupedQueries'
|
|
334
335
|
|
|
335
|
-
export const { ProvideZero, useQuery, zero } = createZeroClient({
|
|
336
|
+
export const { ProvideZero, useQuery, zero, usePermission } = createZeroClient({
|
|
336
337
|
schema,
|
|
337
338
|
models,
|
|
339
|
+
groupedQueries,
|
|
338
340
|
})
|
|
339
341
|
|
|
340
342
|
// in your app root
|
|
@@ -352,17 +354,19 @@ server:
|
|
|
352
354
|
|
|
353
355
|
```ts
|
|
354
356
|
import { createZeroServer } from 'over-zero/server'
|
|
357
|
+
import { syncedQueries } from '~/data/generated/syncedQueries'
|
|
355
358
|
|
|
356
359
|
export const zeroServer = createZeroServer({
|
|
357
360
|
schema,
|
|
358
361
|
models,
|
|
359
362
|
database: process.env.DATABASE_URL,
|
|
363
|
+
queries: syncedQueries, // required for synced queries / pull endpoint
|
|
360
364
|
createServerActions: () => ({
|
|
361
365
|
sendEmail: async (to, subject, body) => { ... }
|
|
362
366
|
})
|
|
363
367
|
})
|
|
364
368
|
|
|
365
|
-
//
|
|
369
|
+
// push endpoint for mutations
|
|
366
370
|
app.post('/api/zero/push', async (req) => {
|
|
367
371
|
const authData = await getAuthFromRequest(req)
|
|
368
372
|
const { response } = await zeroServer.handleMutationRequest({
|
|
@@ -371,6 +375,16 @@ app.post('/api/zero/push', async (req) => {
|
|
|
371
375
|
})
|
|
372
376
|
return response
|
|
373
377
|
})
|
|
378
|
+
|
|
379
|
+
// pull endpoint for synced queries
|
|
380
|
+
app.post('/api/zero/pull', async (req) => {
|
|
381
|
+
const authData = await getAuthFromRequest(req)
|
|
382
|
+
const { response } = await zeroServer.handleQueryRequest({
|
|
383
|
+
authData,
|
|
384
|
+
request: req
|
|
385
|
+
})
|
|
386
|
+
return response
|
|
387
|
+
})
|
|
374
388
|
```
|
|
375
389
|
|
|
376
390
|
type augmentation:
|
package/src/cli.ts
CHANGED
|
@@ -453,14 +453,14 @@ function generateSyncedQueriesFile(
|
|
|
453
453
|
const sortedFiles = Array.from(queryByFile.keys()).sort()
|
|
454
454
|
|
|
455
455
|
const imports = `// auto-generated by: over-zero generate
|
|
456
|
-
// server-side
|
|
457
|
-
import {
|
|
456
|
+
// server-side query definitions with validators
|
|
457
|
+
import { defineQuery, defineQueries } from '@rocicorp/zero'
|
|
458
458
|
import * as v from 'valibot'
|
|
459
459
|
import * as Queries from './groupedQueries'
|
|
460
460
|
`
|
|
461
461
|
|
|
462
|
-
// generate grouped
|
|
463
|
-
const
|
|
462
|
+
// generate grouped definitions by namespace
|
|
463
|
+
const namespaceDefs = sortedFiles
|
|
464
464
|
.map((file) => {
|
|
465
465
|
const fileQueries = queryByFile
|
|
466
466
|
.get(file)!
|
|
@@ -474,7 +474,7 @@ import * as Queries from './groupedQueries'
|
|
|
474
474
|
l.startsWith('export const QueryParams')
|
|
475
475
|
)
|
|
476
476
|
|
|
477
|
-
let validatorDef = '
|
|
477
|
+
let validatorDef = ''
|
|
478
478
|
if (schemaLineIndex !== -1) {
|
|
479
479
|
const schemaLines: string[] = []
|
|
480
480
|
let openBraces = 0
|
|
@@ -500,31 +500,32 @@ import * as Queries from './groupedQueries'
|
|
|
500
500
|
validatorDef = schemaLines.join('\n')
|
|
501
501
|
}
|
|
502
502
|
|
|
503
|
-
//
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
: `v.parser(v.tuple([${validatorDef}]))`
|
|
508
|
-
|
|
509
|
-
// namespaced query name: file.queryName
|
|
510
|
-
const namespacedName = `${q.sourceFile}.${q.name}`
|
|
511
|
-
|
|
512
|
-
// for void queries, no arg parameter
|
|
513
|
-
const queryFn =
|
|
514
|
-
validatorDef === 'v.void()'
|
|
515
|
-
? `() => Queries.${file}.${q.name}()`
|
|
516
|
-
: `(arg) => Queries.${file}.${q.name}(arg)`
|
|
503
|
+
// for void queries, no validator and no args
|
|
504
|
+
if (!validatorDef) {
|
|
505
|
+
return ` ${q.name}: defineQuery(() => Queries.${file}.${q.name}()),`
|
|
506
|
+
}
|
|
517
507
|
|
|
518
|
-
|
|
508
|
+
// defineQuery with validator and args
|
|
509
|
+
return ` ${q.name}: defineQuery(
|
|
510
|
+
${validatorDef},
|
|
511
|
+
({ args }) => Queries.${file}.${q.name}(args),
|
|
512
|
+
),`
|
|
519
513
|
})
|
|
520
514
|
.join('\n')
|
|
521
515
|
|
|
522
|
-
return `
|
|
516
|
+
return `const ${file} = {\n${queryDefs}\n}`
|
|
523
517
|
})
|
|
524
518
|
.join('\n\n')
|
|
525
519
|
|
|
520
|
+
// build the defineQueries call with all namespaces
|
|
521
|
+
const queriesObject = sortedFiles.map((file) => ` ${file},`).join('\n')
|
|
522
|
+
|
|
526
523
|
return `${imports}
|
|
527
|
-
${
|
|
524
|
+
${namespaceDefs}
|
|
525
|
+
|
|
526
|
+
export const queries = defineQueries({
|
|
527
|
+
${queriesObject}
|
|
528
|
+
})
|
|
528
529
|
`
|
|
529
530
|
}
|
|
530
531
|
|
package/src/createPermissions.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import { ensure, EnsureError } from '@
|
|
1
|
+
import { ensure, EnsureError } from '@take-out/helpers'
|
|
2
2
|
|
|
3
3
|
import { setDidRunPermissionCheck } from './helpers/didRunPermissionCheck'
|
|
4
4
|
import { mutatorContext } from './helpers/mutatorContext'
|
|
5
5
|
import { prettyFormatZeroQuery } from './helpers/prettyFormatZeroQuery'
|
|
6
|
+
import { getZQL } from './state'
|
|
6
7
|
import { getWhereTableName } from './where'
|
|
7
8
|
|
|
8
9
|
import type { AuthData, Can, TableName, Transaction, Where } from './types'
|
|
@@ -96,7 +97,8 @@ export function createPermissions<Schema extends ZeroSchema>({
|
|
|
96
97
|
return
|
|
97
98
|
}
|
|
98
99
|
|
|
99
|
-
const
|
|
100
|
+
const zqlBuilder = getZQL() as any
|
|
101
|
+
const queryBase = zqlBuilder[tableName] as Query<any, any>
|
|
100
102
|
let query: Query<any, any, any> | null = null
|
|
101
103
|
|
|
102
104
|
try {
|
|
@@ -106,9 +108,9 @@ export function createPermissions<Schema extends ZeroSchema>({
|
|
|
106
108
|
})
|
|
107
109
|
.one()
|
|
108
110
|
|
|
109
|
-
ensure(await query)
|
|
111
|
+
ensure(await tx.run(query))
|
|
110
112
|
} catch (err) {
|
|
111
|
-
const errorTitle = `${
|
|
113
|
+
const errorTitle = `${tableName} with auth id: ${authData?.id}`
|
|
112
114
|
|
|
113
115
|
if (err instanceof EnsureError) {
|
|
114
116
|
let msg = `[permission] 🚫 Not Allowed: ${errorTitle}`
|
package/src/createUseQuery.tsx
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { syncedQuery } from '@rocicorp/zero'
|
|
2
1
|
import { useQuery as zeroUseQuery } from '@rocicorp/zero/react'
|
|
3
2
|
import { use, useMemo, type Context } from 'react'
|
|
4
3
|
|
|
@@ -6,10 +5,9 @@ import { useZeroDebug } from './helpers/useZeroDebug'
|
|
|
6
5
|
import { getQueryName } from './queryRegistry'
|
|
7
6
|
|
|
8
7
|
import type {
|
|
8
|
+
AnyQueryRegistry,
|
|
9
9
|
HumanReadable,
|
|
10
10
|
Query,
|
|
11
|
-
ReadonlyJSONValue,
|
|
12
|
-
SyncedQuery,
|
|
13
11
|
Schema as ZeroSchema,
|
|
14
12
|
} from '@rocicorp/zero'
|
|
15
13
|
|
|
@@ -26,77 +24,43 @@ export type PlainQueryFn<
|
|
|
26
24
|
TReturn extends Query<any, any, any> = Query<any, any, any>,
|
|
27
25
|
> = (args: TArg) => TReturn
|
|
28
26
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
Schema extends ZeroSchema,
|
|
32
|
-
TTable extends keyof Schema['tables'] & string,
|
|
33
|
-
TReturn,
|
|
34
|
-
> =
|
|
35
|
-
| Query<Schema, TTable, TReturn>
|
|
36
|
-
| SyncedQuery<any, any, any, any, Query<Schema, TTable, TReturn>>
|
|
37
|
-
|
|
38
|
-
// the useQuery hook type with conditional inline overload
|
|
39
|
-
export type UseQueryHook<
|
|
40
|
-
Schema extends ZeroSchema,
|
|
41
|
-
DisableInline extends boolean = false,
|
|
42
|
-
> = {
|
|
43
|
-
// overload 1: inline query (only when DisableInline is false)
|
|
44
|
-
<TTable extends keyof Schema['tables'] & string, TReturn>(
|
|
45
|
-
query: DisableInline extends true ? never : InlineQuery<Schema, TTable, TReturn>,
|
|
46
|
-
options?: UseQueryOptions | boolean
|
|
47
|
-
): QueryResult<TReturn>;
|
|
48
|
-
|
|
49
|
-
// overload 2: plain function with params
|
|
27
|
+
export type UseQueryHook<Schema extends ZeroSchema> = {
|
|
28
|
+
// overload 1: plain function with params
|
|
50
29
|
<TArg, TTable extends keyof Schema['tables'] & string, TReturn>(
|
|
51
|
-
fn: PlainQueryFn<TArg, Query<
|
|
30
|
+
fn: PlainQueryFn<TArg, Query<TTable, Schema, TReturn>>,
|
|
52
31
|
params: TArg,
|
|
53
32
|
options?: UseQueryOptions | boolean
|
|
54
33
|
): QueryResult<TReturn>;
|
|
55
34
|
|
|
56
|
-
// overload
|
|
35
|
+
// overload 2: plain function with no params
|
|
57
36
|
<TTable extends keyof Schema['tables'] & string, TReturn>(
|
|
58
|
-
fn: PlainQueryFn<void, Query<
|
|
37
|
+
fn: PlainQueryFn<void, Query<TTable, Schema, TReturn>>,
|
|
59
38
|
options?: UseQueryOptions | boolean
|
|
60
39
|
): QueryResult<TReturn>
|
|
61
40
|
}
|
|
62
41
|
|
|
63
|
-
export function createUseQuery<
|
|
64
|
-
Schema extends ZeroSchema,
|
|
65
|
-
const DisableInline extends boolean = false,
|
|
66
|
-
>({
|
|
42
|
+
export function createUseQuery<Schema extends ZeroSchema>({
|
|
67
43
|
DisabledContext,
|
|
68
|
-
|
|
44
|
+
customQueries,
|
|
69
45
|
}: {
|
|
70
46
|
DisabledContext: Context<boolean>
|
|
71
|
-
|
|
72
|
-
}): UseQueryHook<Schema
|
|
73
|
-
const queryCache = new Map<string, SyncedQuery<any, any, any, any, any>>()
|
|
74
|
-
const parseAny = (x: unknown[]): [ReadonlyJSONValue] => [x[0] as ReadonlyJSONValue]
|
|
75
|
-
|
|
47
|
+
customQueries: AnyQueryRegistry
|
|
48
|
+
}): UseQueryHook<Schema> {
|
|
76
49
|
function useQuery(...args: any[]): any {
|
|
77
50
|
const disabled = use(DisabledContext)
|
|
78
|
-
const [
|
|
79
|
-
|
|
80
|
-
// detect which calling pattern is being used
|
|
81
|
-
const isPlainFunction = typeof queryOrFn === 'function' && !('queryName' in queryOrFn)
|
|
82
|
-
|
|
83
|
-
const { actualQuery, options } = useMemo(() => {
|
|
84
|
-
if (!isPlainFunction) {
|
|
85
|
-
// pattern 1: original api - useQuery(query, options)
|
|
86
|
-
return {
|
|
87
|
-
actualQuery: queryOrFn,
|
|
88
|
-
options: paramsOrOptions,
|
|
89
|
-
}
|
|
90
|
-
}
|
|
51
|
+
const [fn, paramsOrOptions, optionsArg] = args
|
|
91
52
|
|
|
92
|
-
|
|
53
|
+
const { queryRequest, options } = useMemo(() => {
|
|
93
54
|
const queryName = getQueryName(fn)
|
|
94
|
-
|
|
95
55
|
if (!queryName) {
|
|
96
|
-
|
|
56
|
+
const fnName = fn?.name || 'anonymous'
|
|
57
|
+
throw new Error(
|
|
58
|
+
`Query function '${fnName}' not registered. ` +
|
|
59
|
+
`Ensure it is exported from a queries file and passed to createZeroClient via groupedQueries.`
|
|
60
|
+
)
|
|
97
61
|
}
|
|
98
62
|
|
|
99
|
-
// determine if this is
|
|
63
|
+
// determine if this is with params or no params
|
|
100
64
|
const hasParams =
|
|
101
65
|
optionsArg !== undefined ||
|
|
102
66
|
(paramsOrOptions &&
|
|
@@ -107,27 +71,28 @@ export function createUseQuery<
|
|
|
107
71
|
const params = hasParams ? paramsOrOptions : undefined
|
|
108
72
|
const opts = hasParams ? optionsArg : paramsOrOptions
|
|
109
73
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
})
|
|
115
|
-
queryCache.set(queryName, synced)
|
|
116
|
-
}
|
|
74
|
+
// look up the CustomQuery from the shared registry
|
|
75
|
+
// queryName is "namespace.name" format, e.g., "user.userById"
|
|
76
|
+
const [namespace, name] = queryName.split('.', 2)
|
|
77
|
+
const customQuery = (customQueries as any)[namespace]?.[name]
|
|
117
78
|
|
|
118
|
-
|
|
119
|
-
|
|
79
|
+
if (!customQuery) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
`CustomQuery '${queryName}' not found. ` +
|
|
82
|
+
`Check that the query is exported and the namespace/name matches.`
|
|
83
|
+
)
|
|
84
|
+
}
|
|
120
85
|
|
|
121
|
-
|
|
122
|
-
}, [queryOrFn, paramsOrOptions, optionsArg, isPlainFunction])
|
|
86
|
+
const queryRequest = params !== undefined ? customQuery(params) : customQuery()
|
|
123
87
|
|
|
124
|
-
|
|
88
|
+
return { queryRequest, options: opts }
|
|
89
|
+
}, [fn, paramsOrOptions, optionsArg])
|
|
125
90
|
|
|
126
|
-
const out = zeroUseQuery(
|
|
91
|
+
const out = zeroUseQuery(queryRequest as any, options)
|
|
127
92
|
|
|
128
93
|
if (process.env.NODE_ENV === 'development') {
|
|
129
94
|
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
130
|
-
useZeroDebug(
|
|
95
|
+
useZeroDebug(queryRequest as any, options, out)
|
|
131
96
|
}
|
|
132
97
|
|
|
133
98
|
if (disabled) {
|
|
@@ -137,5 +102,5 @@ export function createUseQuery<
|
|
|
137
102
|
return out
|
|
138
103
|
}
|
|
139
104
|
|
|
140
|
-
return useQuery as UseQueryHook<Schema
|
|
105
|
+
return useQuery as UseQueryHook<Schema>
|
|
141
106
|
}
|
package/src/createZeroClient.tsx
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
|
|
1
|
+
import { defineQueries, defineQuery } from '@rocicorp/zero'
|
|
2
|
+
import {
|
|
3
|
+
useConnectionState,
|
|
4
|
+
useZero,
|
|
5
|
+
ZeroProvider,
|
|
6
|
+
useQuery as zeroUseQuery,
|
|
7
|
+
} from '@rocicorp/zero/react'
|
|
8
|
+
import { createEmitter, mapObject } from '@take-out/helpers'
|
|
9
|
+
import {
|
|
10
|
+
createContext,
|
|
11
|
+
memo,
|
|
12
|
+
use,
|
|
13
|
+
useEffect,
|
|
14
|
+
useMemo,
|
|
15
|
+
useRef,
|
|
16
|
+
type ReactNode,
|
|
17
|
+
} from 'react'
|
|
4
18
|
|
|
5
19
|
import { createPermissions } from './createPermissions'
|
|
6
20
|
import { createUseQuery } from './createUseQuery'
|
|
@@ -9,7 +23,7 @@ import { prettyFormatZeroQuery } from './helpers/prettyFormatZeroQuery'
|
|
|
9
23
|
import { registerQuery } from './queryRegistry'
|
|
10
24
|
import { setAuthData, setSchema } from './state'
|
|
11
25
|
|
|
12
|
-
import type { AuthData, GenericModels, GetZeroMutators, ZeroEvent } from './types'
|
|
26
|
+
import type { AuthData, GenericModels, GetZeroMutators, Where, ZeroEvent } from './types'
|
|
13
27
|
import type { Row, Zero, ZeroOptions, Schema as ZeroSchema } from '@rocicorp/zero'
|
|
14
28
|
|
|
15
29
|
export type GroupedQueries = Record<string, Record<string, (...args: any[]) => any>>
|
|
@@ -17,17 +31,14 @@ export type GroupedQueries = Record<string, Record<string, (...args: any[]) => a
|
|
|
17
31
|
export function createZeroClient<
|
|
18
32
|
Schema extends ZeroSchema,
|
|
19
33
|
Models extends GenericModels,
|
|
20
|
-
const DisableInlineQueries extends boolean = false,
|
|
21
34
|
>({
|
|
22
35
|
schema,
|
|
23
36
|
models,
|
|
24
37
|
groupedQueries,
|
|
25
|
-
disableInlineQueries = false as DisableInlineQueries,
|
|
26
38
|
}: {
|
|
27
39
|
schema: Schema
|
|
28
40
|
models: Models
|
|
29
41
|
groupedQueries: GroupedQueries
|
|
30
|
-
disableInlineQueries?: DisableInlineQueries
|
|
31
42
|
}) {
|
|
32
43
|
type ZeroMutators = GetZeroMutators<Models>
|
|
33
44
|
type ZeroInstance = Zero<Schema, ZeroMutators>
|
|
@@ -36,25 +47,45 @@ export function createZeroClient<
|
|
|
36
47
|
setSchema(schema)
|
|
37
48
|
|
|
38
49
|
// build query registry from grouped queries
|
|
50
|
+
// this creates ONE shared defineQueries registry that matches the server's structure
|
|
51
|
+
const wrappedNamespaces: Record<
|
|
52
|
+
string,
|
|
53
|
+
Record<string, ReturnType<typeof defineQuery>>
|
|
54
|
+
> = {}
|
|
55
|
+
|
|
39
56
|
for (const [namespace, queries] of Object.entries(groupedQueries)) {
|
|
57
|
+
wrappedNamespaces[namespace] = {}
|
|
40
58
|
for (const [name, fn] of Object.entries(queries)) {
|
|
41
59
|
registerQuery(fn, `${namespace}.${name}`)
|
|
60
|
+
// wrap each plain function in defineQuery
|
|
61
|
+
wrappedNamespaces[namespace][name] = defineQuery(({ args }: { args: any }) =>
|
|
62
|
+
fn(args)
|
|
63
|
+
)
|
|
42
64
|
}
|
|
43
65
|
}
|
|
44
66
|
|
|
67
|
+
// create the single shared CustomQuery registry
|
|
68
|
+
const customQueries = defineQueries(wrappedNamespaces)
|
|
69
|
+
|
|
45
70
|
const DisabledContext = createContext(false)
|
|
46
71
|
|
|
47
|
-
const modelWritePermissions = mapObject(models, (val) => val.permissions) as
|
|
48
|
-
|
|
49
|
-
|
|
72
|
+
const modelWritePermissions = mapObject(models, (val) => val.permissions) as Record<
|
|
73
|
+
TableName,
|
|
74
|
+
Where<any, any> | undefined
|
|
75
|
+
>
|
|
50
76
|
|
|
51
77
|
let latestZeroInstance: ZeroInstance | null = null
|
|
52
78
|
|
|
53
|
-
//
|
|
54
|
-
//
|
|
79
|
+
// Proxy allows swapping the Zero instance on login without breaking existing references.
|
|
80
|
+
// Ideally rocicorp/zero would support .setAuth() natively, but for now we swap instances.
|
|
55
81
|
const zero: ZeroInstance = new Proxy({} as never, {
|
|
56
82
|
get(_, key) {
|
|
57
|
-
|
|
83
|
+
if (latestZeroInstance === null) {
|
|
84
|
+
throw new Error(
|
|
85
|
+
`Zero instance not initialized. Ensure ZeroProvider is mounted before accessing 'zero'.`
|
|
86
|
+
)
|
|
87
|
+
}
|
|
88
|
+
return Reflect.get(latestZeroInstance, key, latestZeroInstance)
|
|
58
89
|
},
|
|
59
90
|
})
|
|
60
91
|
|
|
@@ -72,9 +103,9 @@ export function createZeroClient<
|
|
|
72
103
|
const AuthDataContext = createContext<AuthData>({} as AuthData)
|
|
73
104
|
const useAuthData = () => use(AuthDataContext)
|
|
74
105
|
|
|
75
|
-
const useQuery = createUseQuery<Schema
|
|
106
|
+
const useQuery = createUseQuery<Schema>({
|
|
76
107
|
DisabledContext,
|
|
77
|
-
|
|
108
|
+
customQueries,
|
|
78
109
|
})
|
|
79
110
|
|
|
80
111
|
// we don't want flickers as you move around and these queries are re-run
|
|
@@ -83,7 +114,7 @@ export function createZeroClient<
|
|
|
83
114
|
// always update once the query resolves
|
|
84
115
|
function usePermission<K extends TableName>(
|
|
85
116
|
table: K,
|
|
86
|
-
objOrId: string | Partial<Row<
|
|
117
|
+
objOrId: string | Partial<Row<any>> | undefined,
|
|
87
118
|
enabled = typeof objOrId !== 'undefined',
|
|
88
119
|
debug = false
|
|
89
120
|
): boolean | null {
|
|
@@ -93,7 +124,7 @@ export function createZeroClient<
|
|
|
93
124
|
const permission = modelWritePermissions[table]
|
|
94
125
|
|
|
95
126
|
const query = (() => {
|
|
96
|
-
let baseQuery = zero.query[table].one()
|
|
127
|
+
let baseQuery = (zero.query as any)[table].one()
|
|
97
128
|
|
|
98
129
|
if (disabled || !enabled || !permission) {
|
|
99
130
|
return baseQuery
|
|
@@ -109,9 +140,8 @@ export function createZeroClient<
|
|
|
109
140
|
})
|
|
110
141
|
})()
|
|
111
142
|
|
|
112
|
-
//
|
|
113
|
-
|
|
114
|
-
const [data, status] = useQuery(query as any, {
|
|
143
|
+
// usePermission is internal and uses inline queries directly via zeroUseQuery
|
|
144
|
+
const [data, status] = zeroUseQuery(query, {
|
|
115
145
|
enabled: Boolean(enabled && permission && authData && objOrId),
|
|
116
146
|
})
|
|
117
147
|
|
|
@@ -164,20 +194,9 @@ export function createZeroClient<
|
|
|
164
194
|
|
|
165
195
|
return (
|
|
166
196
|
<AuthDataContext.Provider value={authData}>
|
|
167
|
-
<ZeroProvider
|
|
168
|
-
schema={schema}
|
|
169
|
-
kvStore="mem"
|
|
170
|
-
onError={(error) => {
|
|
171
|
-
console.error(`Zero Error:`, error)
|
|
172
|
-
zeroEvents.emit({
|
|
173
|
-
type: 'error',
|
|
174
|
-
message: error,
|
|
175
|
-
})
|
|
176
|
-
}}
|
|
177
|
-
mutators={mutators}
|
|
178
|
-
{...props}
|
|
179
|
-
>
|
|
197
|
+
<ZeroProvider schema={schema} kvStore="mem" mutators={mutators as any} {...props}>
|
|
180
198
|
<SetZeroInstance />
|
|
199
|
+
<ConnectionMonitor zeroEvents={zeroEvents} />
|
|
181
200
|
{children}
|
|
182
201
|
</ZeroProvider>
|
|
183
202
|
</AuthDataContext.Provider>
|
|
@@ -200,6 +219,35 @@ export function createZeroClient<
|
|
|
200
219
|
return null
|
|
201
220
|
}
|
|
202
221
|
|
|
222
|
+
// monitors connection state and emits events (replaces onError callback removed in 0.25)
|
|
223
|
+
const ConnectionMonitor = memo(
|
|
224
|
+
({
|
|
225
|
+
zeroEvents,
|
|
226
|
+
}: {
|
|
227
|
+
zeroEvents: ReturnType<typeof createEmitter<ZeroEvent | null>>
|
|
228
|
+
}) => {
|
|
229
|
+
const state = useConnectionState()
|
|
230
|
+
const prevState = useRef(state.name)
|
|
231
|
+
|
|
232
|
+
useEffect(() => {
|
|
233
|
+
if (state.name !== prevState.current) {
|
|
234
|
+
const reason = 'reason' in state ? state.reason : ''
|
|
235
|
+
prevState.current = state.name
|
|
236
|
+
|
|
237
|
+
if (state.name === 'error' || state.name === 'needs-auth') {
|
|
238
|
+
const message = typeof reason === 'string' ? reason : state.name
|
|
239
|
+
zeroEvents.emit({
|
|
240
|
+
type: 'error',
|
|
241
|
+
message,
|
|
242
|
+
})
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
}, [state, zeroEvents])
|
|
246
|
+
|
|
247
|
+
return null
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
|
|
203
251
|
return {
|
|
204
252
|
zeroEvents,
|
|
205
253
|
ProvideZero,
|
package/src/createZeroServer.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { mustGetQuery } from '@rocicorp/zero'
|
|
2
|
+
import { PushProcessor } from '@rocicorp/zero/pg'
|
|
3
|
+
import { handleQueryRequest as zeroHandleQueryRequest } from '@rocicorp/zero/server'
|
|
2
4
|
import { zeroNodePg } from '@rocicorp/zero/server/adapters/pg'
|
|
3
|
-
import { assertString, randomId } from '@
|
|
5
|
+
import { assertString, randomId } from '@take-out/helpers'
|
|
4
6
|
import { Pool } from 'pg'
|
|
5
7
|
|
|
6
8
|
import { createPermissions } from './createPermissions'
|
|
@@ -16,31 +18,23 @@ import type {
|
|
|
16
18
|
Transaction,
|
|
17
19
|
} from './types'
|
|
18
20
|
import type {
|
|
21
|
+
AnyQueryRegistry,
|
|
19
22
|
HumanReadable,
|
|
20
23
|
Query,
|
|
21
|
-
ReadonlyJSONValue,
|
|
22
|
-
SyncedQuery,
|
|
23
24
|
Schema as ZeroSchema,
|
|
24
25
|
} from '@rocicorp/zero'
|
|
25
26
|
import type { TransactionProviderInput } from '@rocicorp/zero/pg'
|
|
26
27
|
|
|
27
|
-
// grouped synced queries: { namespace: { queryName: SyncedQuery } }
|
|
28
|
-
export type SyncedQueries = Record<
|
|
29
|
-
string,
|
|
30
|
-
Record<string, SyncedQuery<any, any, any, any, any>>
|
|
31
|
-
>
|
|
32
|
-
|
|
33
28
|
export function createZeroServer<
|
|
34
29
|
Schema extends ZeroSchema,
|
|
35
30
|
Models extends GenericModels,
|
|
36
31
|
ServerActions extends Record<string, unknown>,
|
|
37
|
-
Queries extends SyncedQueries = Record<string, never>,
|
|
38
32
|
>({
|
|
39
33
|
createServerActions,
|
|
40
34
|
database,
|
|
41
35
|
schema,
|
|
42
36
|
models,
|
|
43
|
-
|
|
37
|
+
queries,
|
|
44
38
|
}: {
|
|
45
39
|
/**
|
|
46
40
|
* The DB connection string, same as ZERO_UPSTREAM_DB
|
|
@@ -49,7 +43,7 @@ export function createZeroServer<
|
|
|
49
43
|
schema: Schema
|
|
50
44
|
models: Models
|
|
51
45
|
createServerActions: () => ServerActions
|
|
52
|
-
|
|
46
|
+
queries?: AnyQueryRegistry
|
|
53
47
|
}) {
|
|
54
48
|
setSchema(schema)
|
|
55
49
|
|
|
@@ -59,6 +53,10 @@ export function createZeroServer<
|
|
|
59
53
|
schema,
|
|
60
54
|
new Pool({
|
|
61
55
|
connectionString: dbString,
|
|
56
|
+
// handle self-signed certificates in production
|
|
57
|
+
ssl: dbString.includes('sslmode=require')
|
|
58
|
+
? { rejectUnauthorized: false }
|
|
59
|
+
: undefined,
|
|
62
60
|
})
|
|
63
61
|
)
|
|
64
62
|
|
|
@@ -112,16 +110,6 @@ export function createZeroServer<
|
|
|
112
110
|
}
|
|
113
111
|
}
|
|
114
112
|
|
|
115
|
-
// build flat lookup map from grouped syncedQueries: "namespace.queryName" => SyncedQuery
|
|
116
|
-
const queryLookup = new Map<string, SyncedQuery<any, any, any, any, any>>()
|
|
117
|
-
if (syncedQueries) {
|
|
118
|
-
for (const [namespace, queries] of Object.entries(syncedQueries)) {
|
|
119
|
-
for (const [queryName, syncedQuery] of Object.entries(queries)) {
|
|
120
|
-
queryLookup.set(`${namespace}.${queryName}`, syncedQuery)
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
113
|
const handleQueryRequest = async ({
|
|
126
114
|
authData,
|
|
127
115
|
request,
|
|
@@ -129,20 +117,24 @@ export function createZeroServer<
|
|
|
129
117
|
authData: AuthData | null
|
|
130
118
|
request: Request
|
|
131
119
|
}) => {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return {
|
|
139
|
-
// @ts-expect-error zero bug atm
|
|
140
|
-
query: q(...args),
|
|
141
|
-
}
|
|
120
|
+
if (!queries) {
|
|
121
|
+
throw new Error(
|
|
122
|
+
'No queries registered with createZeroServer. ' +
|
|
123
|
+
'Pass the syncedQueries registry to createZeroServer via the queries option.'
|
|
124
|
+
)
|
|
142
125
|
}
|
|
143
126
|
|
|
127
|
+
// set authData globally for permission checks in query functions
|
|
144
128
|
setAuthData(authData || {})
|
|
145
|
-
|
|
129
|
+
|
|
130
|
+
const response = await zeroHandleQueryRequest(
|
|
131
|
+
(name, args) => {
|
|
132
|
+
const query = (mustGetQuery as any)(queries, name)
|
|
133
|
+
return query.fn({ args, ctx: authData })
|
|
134
|
+
},
|
|
135
|
+
schema,
|
|
136
|
+
request
|
|
137
|
+
)
|
|
146
138
|
|
|
147
139
|
return {
|
|
148
140
|
response,
|
|
@@ -195,7 +187,7 @@ export function createZeroServer<
|
|
|
195
187
|
}
|
|
196
188
|
|
|
197
189
|
function query<R>(
|
|
198
|
-
cb: (q: Transaction['query']) => Query<
|
|
190
|
+
cb: (q: Transaction['query']) => Query<any, Schema, R>
|
|
199
191
|
): Promise<HumanReadable<R>> {
|
|
200
192
|
return transaction(async (tx) => {
|
|
201
193
|
return cb(tx.query)
|