on-zero 0.6.14 → 0.6.16
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/asyncTasks.cjs +51 -0
- package/dist/cjs/asyncTasks.native.js +84 -0
- package/dist/cjs/asyncTasks.native.js.map +1 -0
- package/dist/cjs/cli.cjs +8 -1
- package/dist/cjs/cli.native.js +8 -1
- package/dist/cjs/cli.native.js.map +1 -1
- package/dist/cjs/createSchemaFromDrizzle.cjs +4 -4
- package/dist/cjs/createSchemaFromDrizzle.native.js +4 -4
- package/dist/cjs/createSchemaFromDrizzle.native.js.map +1 -1
- package/dist/cjs/createZeroDirectServer.cjs +269 -0
- package/dist/cjs/createZeroDirectServer.native.js +287 -0
- package/dist/cjs/createZeroDirectServer.native.js.map +1 -0
- package/dist/cjs/createZeroSQLiteServer.cjs +8 -277
- package/dist/cjs/createZeroSQLiteServer.native.js +8 -390
- package/dist/cjs/createZeroSQLiteServer.native.js.map +1 -1
- package/dist/cjs/createZeroSQLiteServer.test.cjs +36 -9
- package/dist/cjs/createZeroSQLiteServer.test.native.js +37 -9
- package/dist/cjs/createZeroSQLiteServer.test.native.js.map +1 -1
- package/dist/cjs/createZeroServer.test.cjs +79 -1
- package/dist/cjs/createZeroServer.test.native.js +85 -1
- package/dist/cjs/createZeroServer.test.native.js.map +1 -1
- package/dist/cjs/createZeroServerCore.cjs +28 -208
- package/dist/cjs/createZeroServerCore.native.js +33 -218
- package/dist/cjs/createZeroServerCore.native.js.map +1 -1
- package/dist/cjs/generate.cjs +15 -5
- package/dist/cjs/generate.native.js +15 -5
- package/dist/cjs/generate.native.js.map +1 -1
- package/dist/cjs/generate.test.cjs +55 -0
- package/dist/cjs/generate.test.native.js +55 -0
- package/dist/cjs/generate.test.native.js.map +1 -1
- package/dist/cjs/helpers/createMutators.cjs +2 -2
- package/dist/cjs/helpers/createMutators.native.js +2 -2
- package/dist/cjs/helpers/createMutators.native.js.map +1 -1
- package/dist/cjs/index.cjs +1 -0
- package/dist/cjs/index.native.js +1 -0
- package/dist/cjs/index.native.js.map +1 -1
- package/dist/cjs/server.cjs +2 -1
- package/dist/cjs/sqliteRuntime.cjs +318 -0
- package/dist/cjs/sqliteRuntime.native.js +438 -0
- package/dist/cjs/sqliteRuntime.native.js.map +1 -0
- package/dist/cjs/syncHost.cjs +26 -12
- package/dist/cjs/syncHost.native.js +32 -16
- package/dist/cjs/syncHost.native.js.map +1 -1
- package/dist/cjs/syncHost.test.cjs +15 -4
- package/dist/cjs/syncHost.test.native.js +16 -4
- package/dist/cjs/syncHost.test.native.js.map +1 -1
- package/dist/cjs/vite-plugin.cjs +2 -1
- package/dist/cjs/vite-plugin.native.js +2 -1
- package/dist/cjs/vite-plugin.native.js.map +1 -1
- package/dist/esm/asyncTasks.mjs +26 -0
- package/dist/esm/asyncTasks.mjs.map +1 -0
- package/dist/esm/asyncTasks.native.js +56 -0
- package/dist/esm/asyncTasks.native.js.map +1 -0
- package/dist/esm/cli.mjs +8 -1
- package/dist/esm/cli.mjs.map +1 -1
- package/dist/esm/cli.native.js +8 -1
- package/dist/esm/cli.native.js.map +1 -1
- package/dist/esm/createSchemaFromDrizzle.mjs +2 -2
- package/dist/esm/createSchemaFromDrizzle.native.js +2 -2
- package/dist/esm/createZeroDirectServer.mjs +241 -0
- package/dist/esm/createZeroDirectServer.mjs.map +1 -0
- package/dist/esm/createZeroDirectServer.native.js +256 -0
- package/dist/esm/createZeroDirectServer.native.js.map +1 -0
- package/dist/esm/createZeroSQLiteServer.mjs +3 -272
- package/dist/esm/createZeroSQLiteServer.mjs.map +1 -1
- package/dist/esm/createZeroSQLiteServer.native.js +3 -385
- package/dist/esm/createZeroSQLiteServer.native.js.map +1 -1
- package/dist/esm/createZeroSQLiteServer.test.mjs +36 -9
- package/dist/esm/createZeroSQLiteServer.test.mjs.map +1 -1
- package/dist/esm/createZeroSQLiteServer.test.native.js +37 -9
- package/dist/esm/createZeroSQLiteServer.test.native.js.map +1 -1
- package/dist/esm/createZeroServer.test.mjs +80 -2
- package/dist/esm/createZeroServer.test.mjs.map +1 -1
- package/dist/esm/createZeroServer.test.native.js +86 -2
- package/dist/esm/createZeroServer.test.native.js.map +1 -1
- package/dist/esm/createZeroServerCore.mjs +26 -205
- package/dist/esm/createZeroServerCore.mjs.map +1 -1
- package/dist/esm/createZeroServerCore.native.js +32 -216
- package/dist/esm/createZeroServerCore.native.js.map +1 -1
- package/dist/esm/generate.mjs +15 -5
- package/dist/esm/generate.mjs.map +1 -1
- package/dist/esm/generate.native.js +15 -5
- package/dist/esm/generate.native.js.map +1 -1
- package/dist/esm/generate.test.mjs +55 -1
- package/dist/esm/generate.test.mjs.map +1 -1
- package/dist/esm/generate.test.native.js +55 -1
- package/dist/esm/generate.test.native.js.map +1 -1
- package/dist/esm/helpers/createMutators.mjs +2 -2
- package/dist/esm/helpers/createMutators.mjs.map +1 -1
- package/dist/esm/helpers/createMutators.native.js +2 -2
- package/dist/esm/helpers/createMutators.native.js.map +1 -1
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/index.mjs +1 -0
- package/dist/esm/index.mjs.map +1 -1
- package/dist/esm/index.native.js +1 -0
- package/dist/esm/index.native.js.map +1 -1
- package/dist/esm/server.mjs +1 -0
- package/dist/esm/server.mjs.map +1 -1
- package/dist/esm/sqliteRuntime.mjs +289 -0
- package/dist/esm/sqliteRuntime.mjs.map +1 -0
- package/dist/esm/sqliteRuntime.native.js +406 -0
- package/dist/esm/sqliteRuntime.native.js.map +1 -0
- package/dist/esm/syncHost.mjs +24 -10
- package/dist/esm/syncHost.mjs.map +1 -1
- package/dist/esm/syncHost.native.js +30 -14
- package/dist/esm/syncHost.native.js.map +1 -1
- package/dist/esm/syncHost.test.mjs +15 -4
- package/dist/esm/syncHost.test.mjs.map +1 -1
- package/dist/esm/syncHost.test.native.js +16 -4
- package/dist/esm/syncHost.test.native.js.map +1 -1
- package/dist/esm/vite-plugin.mjs +2 -1
- package/dist/esm/vite-plugin.mjs.map +1 -1
- package/dist/esm/vite-plugin.native.js +2 -1
- package/dist/esm/vite-plugin.native.js.map +1 -1
- package/lint/ssr-guards.js +8 -7
- package/package.json +5 -6
- package/readme.md +13 -4
- package/src/asyncTasks.ts +33 -0
- package/src/cli.ts +7 -1
- package/src/createSchemaFromDrizzle.ts +2 -2
- package/src/createZeroDirectServer.ts +411 -0
- package/src/createZeroSQLiteServer.test.ts +28 -12
- package/src/createZeroSQLiteServer.ts +14 -481
- package/src/createZeroServer.test.ts +86 -2
- package/src/createZeroServerCore.ts +39 -343
- package/src/generate.test.ts +75 -1
- package/src/generate.ts +30 -4
- package/src/helpers/createMutators.ts +3 -3
- package/src/helpers/mutatorContext.ts +1 -1
- package/src/index.ts +1 -0
- package/src/server.ts +2 -0
- package/src/sqliteRuntime.ts +512 -0
- package/src/syncHost.test.ts +12 -5
- package/src/syncHost.ts +55 -16
- package/src/types.ts +21 -3
- package/src/vite-plugin.ts +1 -0
- package/test-fixtures/server-return-types.ts +20 -2
- package/types/asyncTasks.d.ts +7 -0
- package/types/asyncTasks.d.ts.map +1 -0
- package/types/createSchemaFromDrizzle.d.ts +2 -2
- package/types/createSchemaFromDrizzle.d.ts.map +1 -1
- package/types/createZeroDirectServer.d.ts +80 -0
- package/types/createZeroDirectServer.d.ts.map +1 -0
- package/types/createZeroSQLiteServer.d.ts +5 -30
- package/types/createZeroSQLiteServer.d.ts.map +1 -1
- package/types/createZeroServerCore.d.ts +30 -87
- package/types/createZeroServerCore.d.ts.map +1 -1
- package/types/generate.d.ts +2 -0
- package/types/generate.d.ts.map +1 -1
- package/types/helpers/createMutators.d.ts +3 -3
- package/types/helpers/createMutators.d.ts.map +1 -1
- package/types/index.d.ts +1 -0
- package/types/index.d.ts.map +1 -1
- package/types/server.d.ts +2 -0
- package/types/server.d.ts.map +1 -1
- package/types/sqliteRuntime.d.ts +71 -0
- package/types/sqliteRuntime.d.ts.map +1 -0
- package/types/syncHost.d.ts +10 -19
- package/types/syncHost.d.ts.map +1 -1
- package/types/types.d.ts +18 -2
- package/types/types.d.ts.map +1 -1
- package/types/vite-plugin.d.ts.map +1 -1
|
@@ -1,47 +1,23 @@
|
|
|
1
|
-
import { mustGetQuery } from '@rocicorp/zero'
|
|
2
1
|
import {
|
|
3
2
|
PushProcessor,
|
|
4
3
|
handleQueryRequest as zeroHandleQueryRequest,
|
|
5
4
|
} from '@rocicorp/zero/server'
|
|
6
5
|
|
|
7
|
-
import {
|
|
8
|
-
import { createMutators } from './helpers/createMutators'
|
|
6
|
+
import { runAsyncTaskEntries } from './asyncTasks'
|
|
9
7
|
import {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
8
|
+
createAsyncTaskCollector,
|
|
9
|
+
createZeroDirectServerRuntime,
|
|
10
|
+
resolveServerQuery,
|
|
11
|
+
type TransactionAttemptLifecycle,
|
|
12
|
+
type ZeroServerCommonOptions,
|
|
13
|
+
type ZeroServerDatabase,
|
|
14
|
+
} from './createZeroDirectServer'
|
|
15
|
+
import { createMutators } from './helpers/createMutators'
|
|
16
|
+
import { runWithAuthScope } from './helpers/mutatorContext'
|
|
15
17
|
import { runWithQueryContext } from './helpers/queryContext'
|
|
16
|
-
import { getMutationsPermissions } from './modelRegistry'
|
|
17
|
-
import { setCustomQueries } from './run'
|
|
18
|
-
import { getZQL, setEnvironment, setSchema } from './state'
|
|
19
|
-
import { setEvaluatingPermission } from './where'
|
|
20
|
-
import { setRunner } from './zeroRunner'
|
|
21
18
|
|
|
22
|
-
import type {
|
|
23
|
-
|
|
24
|
-
AsyncAction,
|
|
25
|
-
AuthData,
|
|
26
|
-
GenericModels,
|
|
27
|
-
MutatorContext,
|
|
28
|
-
QueryBuilder,
|
|
29
|
-
Transaction,
|
|
30
|
-
} from './types'
|
|
31
|
-
import type {
|
|
32
|
-
AnyQueryRegistry,
|
|
33
|
-
HumanReadable,
|
|
34
|
-
Query,
|
|
35
|
-
Schema as ZeroSchema,
|
|
36
|
-
} from '@rocicorp/zero'
|
|
37
|
-
import type { Database, TransactionProviderInput } from '@rocicorp/zero/server'
|
|
38
|
-
|
|
39
|
-
type MutateAuthData = Pick<AuthData, 'email' | 'id'> & Partial<AuthData>
|
|
40
|
-
|
|
41
|
-
type MutateOptions = {
|
|
42
|
-
authData?: MutateAuthData
|
|
43
|
-
awaitEffects?: boolean
|
|
44
|
-
}
|
|
19
|
+
import type { AuthData, GenericModels } from './types'
|
|
20
|
+
import type { Schema as ZeroSchema } from '@rocicorp/zero'
|
|
45
21
|
|
|
46
22
|
type AlreadyProcessedMutation = {
|
|
47
23
|
id: {
|
|
@@ -58,122 +34,12 @@ type PushRequestBody = {
|
|
|
58
34
|
clientGroupID?: unknown
|
|
59
35
|
}
|
|
60
36
|
|
|
61
|
-
type ServerMutate<Models extends GenericModels> = {
|
|
62
|
-
[Key in keyof Models]: {
|
|
63
|
-
[K in keyof Models[Key]['mutate']]: Models[Key]['mutate'][K] extends (
|
|
64
|
-
ctx: MutatorContext,
|
|
65
|
-
arg: infer Arg,
|
|
66
|
-
) => any
|
|
67
|
-
? (arg: Arg, options?: MutateOptions) => Promise<void>
|
|
68
|
-
: (options?: MutateOptions) => Promise<void>
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
export type ValidateQueryArgs = {
|
|
73
|
-
authData: AuthData | null
|
|
74
|
-
queryName: string
|
|
75
|
-
params: unknown
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
export type ValidateMutationArgs = {
|
|
79
|
-
authData: AuthData | null
|
|
80
|
-
mutatorName: string
|
|
81
|
-
tableName: string
|
|
82
|
-
args: unknown
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export type ValidateQueryFn = (args: ValidateQueryArgs) => void
|
|
86
|
-
export type ValidateMutationFn = (args: ValidateMutationArgs) => void | Promise<void>
|
|
87
|
-
|
|
88
|
-
export type ServerQueryResolverOptions = {
|
|
89
|
-
authData: AuthData | null
|
|
90
|
-
name: string
|
|
91
|
-
args: unknown
|
|
92
|
-
queries: AnyQueryRegistry
|
|
93
|
-
permissions: ReturnType<typeof createPermissions>
|
|
94
|
-
validateQuery?: ValidateQueryFn
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
export function resolveServerQuery({
|
|
98
|
-
authData,
|
|
99
|
-
name,
|
|
100
|
-
args,
|
|
101
|
-
queries,
|
|
102
|
-
permissions,
|
|
103
|
-
validateQuery,
|
|
104
|
-
}: ServerQueryResolverOptions) {
|
|
105
|
-
if (name.startsWith('permission.')) {
|
|
106
|
-
const table = name.slice('permission.'.length)
|
|
107
|
-
const { objOrId } = args as {
|
|
108
|
-
objOrId: string | Record<string, any>
|
|
109
|
-
}
|
|
110
|
-
const permission = getMutationsPermissions(table)
|
|
111
|
-
if (!permission) {
|
|
112
|
-
throw new Error(`[permission] no permission defined for table: ${table}`)
|
|
113
|
-
}
|
|
114
|
-
setEvaluatingPermission(true)
|
|
115
|
-
try {
|
|
116
|
-
return (getZQL() as any)[table]
|
|
117
|
-
.where((eb: any) => {
|
|
118
|
-
return permissions.buildPermissionQuery(
|
|
119
|
-
authData,
|
|
120
|
-
eb,
|
|
121
|
-
permission,
|
|
122
|
-
objOrId,
|
|
123
|
-
table,
|
|
124
|
-
)
|
|
125
|
-
})
|
|
126
|
-
.one()
|
|
127
|
-
} finally {
|
|
128
|
-
setEvaluatingPermission(false)
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
if (validateQuery) {
|
|
133
|
-
validateQuery({ authData, queryName: name, params: args })
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
const query = (mustGetQuery as any)(queries, name)
|
|
137
|
-
return query.fn({ args, ctx: authData })
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export type TransactionAttemptLifecycle = {
|
|
141
|
-
beginAttempt(): void
|
|
142
|
-
completeAttempt(): void
|
|
143
|
-
rollbackAttempt(): void
|
|
144
|
-
commitTransaction(): void
|
|
145
|
-
rollbackTransaction(): void
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export type ZeroServerDatabase = Database<Transaction>
|
|
149
|
-
|
|
150
37
|
export type PersistAlreadyProcessedArgs = {
|
|
151
38
|
clientGroupID: string
|
|
152
39
|
mutations: AlreadyProcessedMutation[]
|
|
153
40
|
upstreamSchema: string
|
|
154
41
|
}
|
|
155
42
|
|
|
156
|
-
export type ZeroServerCommonOptions<
|
|
157
|
-
Schema extends ZeroSchema,
|
|
158
|
-
Models extends GenericModels,
|
|
159
|
-
ServerActions extends Record<string, unknown>,
|
|
160
|
-
> = {
|
|
161
|
-
schema: Schema
|
|
162
|
-
models: Models
|
|
163
|
-
createServerActions: () => ServerActions
|
|
164
|
-
queries?: AnyQueryRegistry
|
|
165
|
-
/** generated validators keyed by model and mutation name */
|
|
166
|
-
mutations?: Record<string, Record<string, any>>
|
|
167
|
-
/** synchronous query authorization hook; throw to reject */
|
|
168
|
-
validateQuery?: ValidateQueryFn
|
|
169
|
-
/** mutation authorization hook; throw to reject */
|
|
170
|
-
validateMutation?: ValidateMutationFn
|
|
171
|
-
/** controls which operations the admin role bypasses */
|
|
172
|
-
defaultAllowAdminRole?: AdminRoleMode
|
|
173
|
-
/** auth data used by direct server mutations when no scope provides it */
|
|
174
|
-
defaultMutateAuthData?: MutateAuthData
|
|
175
|
-
}
|
|
176
|
-
|
|
177
43
|
type ZeroServerCoreOptions<
|
|
178
44
|
Schema extends ZeroSchema,
|
|
179
45
|
Models extends GenericModels,
|
|
@@ -183,60 +49,6 @@ type ZeroServerCoreOptions<
|
|
|
183
49
|
persistAlreadyProcessed?: (args: PersistAlreadyProcessedArgs) => Promise<void>
|
|
184
50
|
}
|
|
185
51
|
|
|
186
|
-
function createAsyncTaskCollector() {
|
|
187
|
-
const committed: AsyncAction[] = []
|
|
188
|
-
let attempt: AsyncAction[] | undefined
|
|
189
|
-
let completedAttempt: AsyncAction[] | undefined
|
|
190
|
-
const current = () => attempt ?? committed
|
|
191
|
-
const queue = new Proxy([] as AsyncAction[], {
|
|
192
|
-
deleteProperty(_target, property) {
|
|
193
|
-
return Reflect.deleteProperty(current(), property)
|
|
194
|
-
},
|
|
195
|
-
get(_target, property) {
|
|
196
|
-
const target = current()
|
|
197
|
-
const value = Reflect.get(target, property, target)
|
|
198
|
-
return typeof value === 'function' ? value.bind(target) : value
|
|
199
|
-
},
|
|
200
|
-
getOwnPropertyDescriptor(_target, property) {
|
|
201
|
-
return Reflect.getOwnPropertyDescriptor(current(), property)
|
|
202
|
-
},
|
|
203
|
-
has(_target, property) {
|
|
204
|
-
return Reflect.has(current(), property)
|
|
205
|
-
},
|
|
206
|
-
ownKeys() {
|
|
207
|
-
return Reflect.ownKeys(current())
|
|
208
|
-
},
|
|
209
|
-
set(_target, property, value) {
|
|
210
|
-
return Reflect.set(current(), property, value)
|
|
211
|
-
},
|
|
212
|
-
})
|
|
213
|
-
|
|
214
|
-
const lifecycle: TransactionAttemptLifecycle = {
|
|
215
|
-
beginAttempt() {
|
|
216
|
-
attempt = []
|
|
217
|
-
completedAttempt = undefined
|
|
218
|
-
},
|
|
219
|
-
completeAttempt() {
|
|
220
|
-
completedAttempt = attempt
|
|
221
|
-
attempt = undefined
|
|
222
|
-
},
|
|
223
|
-
rollbackAttempt() {
|
|
224
|
-
attempt = undefined
|
|
225
|
-
completedAttempt = undefined
|
|
226
|
-
},
|
|
227
|
-
commitTransaction() {
|
|
228
|
-
if (completedAttempt) committed.push(...completedAttempt)
|
|
229
|
-
completedAttempt = undefined
|
|
230
|
-
},
|
|
231
|
-
rollbackTransaction() {
|
|
232
|
-
attempt = undefined
|
|
233
|
-
completedAttempt = undefined
|
|
234
|
-
},
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
return { committed, lifecycle, queue }
|
|
238
|
-
}
|
|
239
|
-
|
|
240
52
|
export function createZeroServerCore<
|
|
241
53
|
Schema extends ZeroSchema,
|
|
242
54
|
Models extends GenericModels,
|
|
@@ -245,7 +57,7 @@ export function createZeroServerCore<
|
|
|
245
57
|
createDatabase,
|
|
246
58
|
createServerActions,
|
|
247
59
|
defaultAllowAdminRole = 'all',
|
|
248
|
-
defaultMutateAuthData
|
|
60
|
+
defaultMutateAuthData,
|
|
249
61
|
models,
|
|
250
62
|
mutations: mutationValidators,
|
|
251
63
|
persistAlreadyProcessed,
|
|
@@ -254,13 +66,17 @@ export function createZeroServerCore<
|
|
|
254
66
|
validateMutation,
|
|
255
67
|
validateQuery,
|
|
256
68
|
}: ZeroServerCoreOptions<Schema, Models, ServerActions>) {
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
69
|
+
const { permissions, server } = createZeroDirectServerRuntime({
|
|
70
|
+
createDatabase,
|
|
71
|
+
createServerActions,
|
|
72
|
+
defaultAllowAdminRole,
|
|
73
|
+
defaultMutateAuthData,
|
|
74
|
+
models,
|
|
75
|
+
mutations: mutationValidators,
|
|
76
|
+
queries,
|
|
262
77
|
schema,
|
|
263
|
-
|
|
78
|
+
validateMutation,
|
|
79
|
+
validateQuery,
|
|
264
80
|
})
|
|
265
81
|
|
|
266
82
|
const handleMutationRequest = async ({
|
|
@@ -275,7 +91,7 @@ export function createZeroServerCore<
|
|
|
275
91
|
const tasks = createAsyncTaskCollector()
|
|
276
92
|
const recoveryRequest = request.clone()
|
|
277
93
|
const mutators = createMutators({
|
|
278
|
-
|
|
94
|
+
enqueueTask: tasks.enqueueTask,
|
|
279
95
|
can: permissions.can,
|
|
280
96
|
createServerActions,
|
|
281
97
|
environment: 'server',
|
|
@@ -295,11 +111,11 @@ export function createZeroServerCore<
|
|
|
295
111
|
}
|
|
296
112
|
|
|
297
113
|
if (!skipAsyncTasks && tasks.committed.length) {
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
)
|
|
114
|
+
const entries = tasks.committed.map((entry) => ({
|
|
115
|
+
...entry,
|
|
116
|
+
task: () => runWithAuthScope(authData, entry.task),
|
|
117
|
+
}))
|
|
118
|
+
await runAsyncTaskEntries(entries, { background: () => undefined })
|
|
303
119
|
}
|
|
304
120
|
|
|
305
121
|
return {
|
|
@@ -342,135 +158,13 @@ export function createZeroServerCore<
|
|
|
342
158
|
: zeroHandleQueryRequest({ handler, schema, request, userID }),
|
|
343
159
|
)
|
|
344
160
|
|
|
345
|
-
return {
|
|
346
|
-
response,
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
|
-
async function runTransaction<
|
|
351
|
-
CB extends (tx: Transaction) => Promise<any>,
|
|
352
|
-
Returns extends CB extends (tx: Transaction) => Promise<infer X> ? X : never,
|
|
353
|
-
>(database: ZeroServerDatabase, query: CB): Promise<Returns> {
|
|
354
|
-
try {
|
|
355
|
-
if (isInZeroMutation()) {
|
|
356
|
-
const { tx } = mutatorContext()
|
|
357
|
-
return await query(tx)
|
|
358
|
-
}
|
|
359
|
-
return (await database.transaction(query, dummyTransactionInput)) as Returns
|
|
360
|
-
} catch (err) {
|
|
361
|
-
if ((err as any)?.name !== 'PermissionError') {
|
|
362
|
-
console.error(`Error running transaction(): ${err}`)
|
|
363
|
-
}
|
|
364
|
-
throw err
|
|
365
|
-
}
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
async function transaction<
|
|
369
|
-
CB extends (tx: Transaction) => Promise<any>,
|
|
370
|
-
Returns extends CB extends (tx: Transaction) => Promise<infer X> ? X : never,
|
|
371
|
-
>(query: CB): Promise<Returns> {
|
|
372
|
-
return runTransaction(createDatabase(), query)
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
async function runMutate(
|
|
376
|
-
modelName: string,
|
|
377
|
-
mutatorName: string,
|
|
378
|
-
mutatorArg: unknown,
|
|
379
|
-
options?: MutateOptions,
|
|
380
|
-
) {
|
|
381
|
-
let authData = options?.authData
|
|
382
|
-
if (!authData) {
|
|
383
|
-
const scoped = getScopedAuthData()
|
|
384
|
-
if (scoped) authData = scoped as MutateAuthData
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
const tasks = createAsyncTaskCollector()
|
|
388
|
-
const mutators = createMutators({
|
|
389
|
-
models,
|
|
390
|
-
environment: 'server',
|
|
391
|
-
asyncTasks: tasks.queue,
|
|
392
|
-
authData: {
|
|
393
|
-
...defaultMutateAuthData,
|
|
394
|
-
...authData,
|
|
395
|
-
},
|
|
396
|
-
createServerActions,
|
|
397
|
-
can: permissions.can,
|
|
398
|
-
validateMutation,
|
|
399
|
-
mutationValidators,
|
|
400
|
-
})
|
|
401
|
-
const modelMutators = mutators[modelName as keyof typeof mutators] as Record<
|
|
402
|
-
string,
|
|
403
|
-
(tx: Transaction, arg?: unknown) => Promise<void>
|
|
404
|
-
>
|
|
405
|
-
const mutator = modelMutators[mutatorName]
|
|
406
|
-
|
|
407
|
-
await runTransaction(
|
|
408
|
-
withTransactionLifecycle(createDatabase(), tasks.lifecycle),
|
|
409
|
-
async (tx) => {
|
|
410
|
-
await mutator(tx, mutatorArg)
|
|
411
|
-
},
|
|
412
|
-
)
|
|
413
|
-
|
|
414
|
-
if (tasks.committed.length) {
|
|
415
|
-
const resolvedAuth = authData ?? null
|
|
416
|
-
const promise = Promise.all(
|
|
417
|
-
tasks.committed.map((task) => runWithAuthScope(resolvedAuth, task)),
|
|
418
|
-
)
|
|
419
|
-
if (options?.awaitEffects) {
|
|
420
|
-
await promise
|
|
421
|
-
} else {
|
|
422
|
-
promise.catch((err: unknown) => {
|
|
423
|
-
console.error('[mutate] async tasks failed', err)
|
|
424
|
-
})
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
}
|
|
428
|
-
|
|
429
|
-
const mutate = new Proxy({} as ServerMutate<Models>, {
|
|
430
|
-
get(_, modelName: string) {
|
|
431
|
-
return new Proxy(
|
|
432
|
-
{},
|
|
433
|
-
{
|
|
434
|
-
get(_, mutatorName: string) {
|
|
435
|
-
return (arg: unknown, options?: MutateOptions) =>
|
|
436
|
-
runMutate(modelName, mutatorName, arg, options)
|
|
437
|
-
},
|
|
438
|
-
},
|
|
439
|
-
)
|
|
440
|
-
},
|
|
441
|
-
})
|
|
442
|
-
|
|
443
|
-
function query<R>(
|
|
444
|
-
cb: (q: QueryBuilder) => Query<any, Schema, R>,
|
|
445
|
-
authData?: AuthData | null,
|
|
446
|
-
): Promise<HumanReadable<R>> {
|
|
447
|
-
const run = () =>
|
|
448
|
-
transaction(async (tx) => {
|
|
449
|
-
return tx.run(cb(getZQL()))
|
|
450
|
-
}) as any
|
|
451
|
-
|
|
452
|
-
if (authData !== undefined) {
|
|
453
|
-
return runWithQueryContext({ authData }, run)
|
|
454
|
-
}
|
|
455
|
-
return run()
|
|
161
|
+
return { response }
|
|
456
162
|
}
|
|
457
163
|
|
|
458
|
-
if (queries) {
|
|
459
|
-
setCustomQueries(queries)
|
|
460
|
-
}
|
|
461
|
-
|
|
462
|
-
setRunner((queryObj) => {
|
|
463
|
-
return transaction(async (tx) => {
|
|
464
|
-
return tx.run(queryObj)
|
|
465
|
-
})
|
|
466
|
-
})
|
|
467
|
-
|
|
468
164
|
return {
|
|
469
165
|
handleMutationRequest,
|
|
470
166
|
handleQueryRequest,
|
|
471
|
-
|
|
472
|
-
mutate,
|
|
473
|
-
query,
|
|
167
|
+
...server,
|
|
474
168
|
}
|
|
475
169
|
}
|
|
476
170
|
|
|
@@ -549,9 +243,11 @@ function alreadyProcessedMutations(response: unknown): AlreadyProcessedMutation[
|
|
|
549
243
|
})
|
|
550
244
|
}
|
|
551
245
|
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
246
|
+
export type {
|
|
247
|
+
ValidateMutationArgs,
|
|
248
|
+
ValidateMutationFn,
|
|
249
|
+
ValidateQueryArgs,
|
|
250
|
+
ValidateQueryFn,
|
|
251
|
+
ZeroServerCommonOptions,
|
|
252
|
+
ZeroServerDatabase,
|
|
253
|
+
} from './createZeroDirectServer'
|
package/src/generate.test.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
existsSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readFileSync,
|
|
5
|
+
rmSync,
|
|
6
|
+
symlinkSync,
|
|
7
|
+
writeFileSync,
|
|
8
|
+
} from 'node:fs'
|
|
2
9
|
import { tmpdir } from 'node:os'
|
|
3
10
|
import { join } from 'node:path'
|
|
4
11
|
import { runInNewContext } from 'node:vm'
|
|
@@ -211,6 +218,24 @@ export const allPosts = () => zero.query.post
|
|
|
211
218
|
const second = await generate({ dir: testDir, silent: true })
|
|
212
219
|
expect(second.filesChanged).toBe(0)
|
|
213
220
|
})
|
|
221
|
+
|
|
222
|
+
test('force regenerates without source changes', async () => {
|
|
223
|
+
writeFileSync(
|
|
224
|
+
join(testDir, 'models/post.ts'),
|
|
225
|
+
`export const schema = table('post', { id: string() })`,
|
|
226
|
+
)
|
|
227
|
+
writeFileSync(
|
|
228
|
+
join(testDir, 'queries/post.ts'),
|
|
229
|
+
`export const allPosts = () => zero.query.post`,
|
|
230
|
+
)
|
|
231
|
+
await generate({ dir: testDir, silent: true })
|
|
232
|
+
const syncedQueriesPath = join(testDir, 'generated/syncedQueries.ts')
|
|
233
|
+
rmSync(syncedQueriesPath)
|
|
234
|
+
|
|
235
|
+
await generate({ dir: testDir, silent: true, force: true })
|
|
236
|
+
|
|
237
|
+
expect(existsSync(syncedQueriesPath)).toBe(true)
|
|
238
|
+
})
|
|
214
239
|
})
|
|
215
240
|
|
|
216
241
|
describe('mutations', () => {
|
|
@@ -617,6 +642,55 @@ export const mutate = mutations(schema, perm, {
|
|
|
617
642
|
expect(content).not.toMatch(/rename:[\s\S]*count/)
|
|
618
643
|
})
|
|
619
644
|
|
|
645
|
+
test('resolves instantiated Zero row fields through Partial and Omit', async () => {
|
|
646
|
+
symlinkSync(
|
|
647
|
+
join(import.meta.dirname, '../../../node_modules'),
|
|
648
|
+
join(testDir, 'node_modules'),
|
|
649
|
+
'dir',
|
|
650
|
+
)
|
|
651
|
+
writeFileSync(
|
|
652
|
+
join(testDir, 'models/types.ts'),
|
|
653
|
+
`
|
|
654
|
+
import type { Row } from '@rocicorp/zero'
|
|
655
|
+
import { drizzleZeroConfig } from 'drizzle-zero-sqlite'
|
|
656
|
+
import { sqliteTable, text } from 'drizzle-orm/sqlite-core'
|
|
657
|
+
|
|
658
|
+
const item = sqliteTable('item', {
|
|
659
|
+
id: text().primaryKey(),
|
|
660
|
+
name: text().notNull(),
|
|
661
|
+
description: text().notNull(),
|
|
662
|
+
})
|
|
663
|
+
const schema = drizzleZeroConfig({ item })
|
|
664
|
+
|
|
665
|
+
export type Item = Row<(typeof schema)['tables']['item']>
|
|
666
|
+
`,
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
writeFileSync(
|
|
670
|
+
join(testDir, 'models/item.ts'),
|
|
671
|
+
`
|
|
672
|
+
import { mutations } from 'on-zero'
|
|
673
|
+
import type { Item } from './types'
|
|
674
|
+
|
|
675
|
+
type ItemUpdate = { id: string } & Partial<Omit<Item, 'id'>>
|
|
676
|
+
|
|
677
|
+
export const mutate = mutations('item', {
|
|
678
|
+
update: async ({ tx }, props: ItemUpdate) => {
|
|
679
|
+
await tx.mutate.item.update(props)
|
|
680
|
+
},
|
|
681
|
+
})
|
|
682
|
+
`,
|
|
683
|
+
)
|
|
684
|
+
|
|
685
|
+
await generate({ dir: testDir, silent: true })
|
|
686
|
+
|
|
687
|
+
const content = readFileSync(join(testDir, 'generated/syncedMutations.ts'), 'utf-8')
|
|
688
|
+
expect(content).toContain('name: v.optional(v.string())')
|
|
689
|
+
expect(content).toContain('description: v.optional(v.string())')
|
|
690
|
+
expect(content).not.toContain('name: v.optional(v.unknown())')
|
|
691
|
+
expect(content).not.toContain('description: v.optional(v.unknown())')
|
|
692
|
+
})
|
|
693
|
+
|
|
620
694
|
test('skips symbol-keyed properties when resolving imported mutation param types', async () => {
|
|
621
695
|
writeFileSync(
|
|
622
696
|
join(testDir, 'models/types.ts'),
|
package/src/generate.ts
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
import type { ExtractedMutation, ModelMutations, SchemaColumn } from './generate-helpers'
|
|
18
18
|
|
|
19
19
|
const hash = (s: string) => createHash('sha256').update(s).digest('hex')
|
|
20
|
+
const GENERATOR_CACHE_VERSION = '1'
|
|
20
21
|
|
|
21
22
|
const isGeneratorSourceFile = (name: string) =>
|
|
22
23
|
name.endsWith('.ts') &&
|
|
@@ -442,6 +443,19 @@ function extractSchemaColumns(
|
|
|
442
443
|
visit(sourceFile)
|
|
443
444
|
}
|
|
444
445
|
|
|
446
|
+
function getInstantiatedPropertyType(
|
|
447
|
+
checker: import('typescript').TypeChecker,
|
|
448
|
+
parent: import('typescript').Type,
|
|
449
|
+
name: string,
|
|
450
|
+
) {
|
|
451
|
+
const resolveProperty: unknown = Reflect.get(checker, 'getTypeOfPropertyOfType')
|
|
452
|
+
if (typeof resolveProperty !== 'function') return undefined
|
|
453
|
+
// typescript exposes this runtime method but omits it from TypeChecker.
|
|
454
|
+
return Reflect.apply(resolveProperty, checker, [parent, name]) as
|
|
455
|
+
| import('typescript').Type
|
|
456
|
+
| undefined
|
|
457
|
+
}
|
|
458
|
+
|
|
445
459
|
// convert a ts.Type to valibot code by walking the type checker AST
|
|
446
460
|
function tsTypeToValibot(
|
|
447
461
|
ts: typeof import('typescript'),
|
|
@@ -528,7 +542,14 @@ function tsTypeToValibot(
|
|
|
528
542
|
}
|
|
529
543
|
|
|
530
544
|
// resolve symbol property type with fallbacks
|
|
531
|
-
const resolveSymbolType = (
|
|
545
|
+
const resolveSymbolType = (
|
|
546
|
+
parent: import('typescript').Type,
|
|
547
|
+
prop: import('typescript').Symbol,
|
|
548
|
+
) => {
|
|
549
|
+
// mapped and conditional types can expose transient symbols with no
|
|
550
|
+
// declaration, so resolve each property against its concrete parent.
|
|
551
|
+
const instantiated = getInstantiatedPropertyType(checker, parent, prop.getName())
|
|
552
|
+
if (instantiated) return instantiated
|
|
532
553
|
if (prop.valueDeclaration)
|
|
533
554
|
return checker.getTypeOfSymbolAtLocation(prop, prop.valueDeclaration)
|
|
534
555
|
if ((prop as any).declarations?.[0])
|
|
@@ -544,7 +565,7 @@ function tsTypeToValibot(
|
|
|
544
565
|
for (const prop of props) {
|
|
545
566
|
const name = prop.getName()
|
|
546
567
|
if (shouldSkipObjectKey(name)) continue
|
|
547
|
-
const propType = resolveSymbolType(prop)
|
|
568
|
+
const propType = resolveSymbolType(type, prop)
|
|
548
569
|
const isOptional = !!(prop.getFlags() & ts.SymbolFlags.Optional)
|
|
549
570
|
let val = recurse(propType)
|
|
550
571
|
if (isOptional && !val.startsWith('v.optional(')) {
|
|
@@ -589,7 +610,7 @@ function tsTypeToValibot(
|
|
|
589
610
|
for (const prop of props) {
|
|
590
611
|
const name = prop.getName()
|
|
591
612
|
if (shouldSkipObjectKey(name)) continue
|
|
592
|
-
const propType = resolveSymbolType(prop)
|
|
613
|
+
const propType = resolveSymbolType(type, prop)
|
|
593
614
|
const isOptional = !!(prop.getFlags() & ts.SymbolFlags.Optional)
|
|
594
615
|
let val = recurse(propType)
|
|
595
616
|
if (isOptional && !val.startsWith('v.optional(')) {
|
|
@@ -773,6 +794,8 @@ export interface GenerateOptions {
|
|
|
773
794
|
after?: string
|
|
774
795
|
/** suppress output */
|
|
775
796
|
silent?: boolean
|
|
797
|
+
/** ignore the generation cache */
|
|
798
|
+
force?: boolean
|
|
776
799
|
}
|
|
777
800
|
|
|
778
801
|
export interface WatchOptions extends GenerateOptions {
|
|
@@ -789,7 +812,7 @@ export interface GenerateResult {
|
|
|
789
812
|
}
|
|
790
813
|
|
|
791
814
|
export async function generate(options: GenerateOptions): Promise<GenerateResult> {
|
|
792
|
-
const { dir, after, silent } = options
|
|
815
|
+
const { dir, after, silent, force } = options
|
|
793
816
|
const baseDir = resolve(dir)
|
|
794
817
|
// support both mutations/ (new) and models/ (legacy) directories
|
|
795
818
|
const mutationsDir = resolve(baseDir, 'mutations')
|
|
@@ -812,6 +835,8 @@ export async function generate(options: GenerateOptions): Promise<GenerateResult
|
|
|
812
835
|
// still re-runs generate on real model/query edits.
|
|
813
836
|
const inputHash = hashInputTree(baseDir, generatedDir)
|
|
814
837
|
if (
|
|
838
|
+
!force &&
|
|
839
|
+
generateCache.__generatorVersion === GENERATOR_CACHE_VERSION &&
|
|
815
840
|
generateCache.__inputHash === inputHash &&
|
|
816
841
|
existsSync(resolve(generatedDir, 'models.ts'))
|
|
817
842
|
) {
|
|
@@ -1134,6 +1159,7 @@ export async function generate(options: GenerateOptions): Promise<GenerateResult
|
|
|
1134
1159
|
}
|
|
1135
1160
|
|
|
1136
1161
|
// record the input hash + counts so the next boot can skip a no-op regen.
|
|
1162
|
+
generateCache.__generatorVersion = GENERATOR_CACHE_VERSION
|
|
1137
1163
|
generateCache.__inputHash = inputHash
|
|
1138
1164
|
generateCache.__counts = JSON.stringify({
|
|
1139
1165
|
modelCount: allModelFiles.length,
|
|
@@ -25,7 +25,7 @@ export function createMutators<Models extends GenericModels>({
|
|
|
25
25
|
environment,
|
|
26
26
|
authData,
|
|
27
27
|
createServerActions,
|
|
28
|
-
|
|
28
|
+
enqueueTask = () => {},
|
|
29
29
|
can,
|
|
30
30
|
models,
|
|
31
31
|
validateMutation,
|
|
@@ -35,7 +35,7 @@ export function createMutators<Models extends GenericModels>({
|
|
|
35
35
|
authData: AuthData | null
|
|
36
36
|
can: Can
|
|
37
37
|
models: Models
|
|
38
|
-
|
|
38
|
+
enqueueTask?: NonNullable<MutatorContext['server']>['enqueueTask']
|
|
39
39
|
createServerActions?: () => Record<string, any>
|
|
40
40
|
validateMutation?: ValidateMutationFn
|
|
41
41
|
/** valibot schemas keyed by model.mutationName, auto-validates args before running */
|
|
@@ -61,7 +61,7 @@ export function createMutators<Models extends GenericModels>({
|
|
|
61
61
|
environment === 'server'
|
|
62
62
|
? ({
|
|
63
63
|
actions: serverActions || {},
|
|
64
|
-
|
|
64
|
+
enqueueTask,
|
|
65
65
|
} as MutatorContext['server'])
|
|
66
66
|
: undefined,
|
|
67
67
|
}
|
|
@@ -13,7 +13,7 @@ const asyncContext =
|
|
|
13
13
|
contextStore.__onZeroMutatorContext__ ??
|
|
14
14
|
(contextStore.__onZeroMutatorContext__ = createAsyncContext<MutatorContext>())
|
|
15
15
|
|
|
16
|
-
// lightweight auth-only scope for
|
|
16
|
+
// lightweight auth-only scope for async tasks (where mutation context is gone but authData is needed)
|
|
17
17
|
const authScopeContext =
|
|
18
18
|
contextStore.__onZeroAuthScopeContext__ ??
|
|
19
19
|
(contextStore.__onZeroAuthScopeContext__ = createAsyncContext<AuthData | null>())
|
package/src/index.ts
CHANGED
package/src/server.ts
CHANGED