zodvex 0.7.7 → 0.7.8-beta.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zodvex",
3
- "version": "0.7.7",
3
+ "version": "0.7.8-beta.0",
4
4
  "description": "Codec-first Zod v4 integration for Convex -- type-safe validation, encoding, DB wrapping, and codegen.",
5
5
  "keywords": ["zod", "convex", "validators", "codec", "mapping", "schema", "validation"],
6
6
  "homepage": "https://github.com/panzacoder/zodvex#readme",
@@ -1,11 +1,41 @@
1
+ import type { GenericDatabaseReader, GenericDatabaseWriter } from 'convex/server'
1
2
  import { ZodvexDatabaseReader, ZodvexDatabaseWriter } from './db'
2
3
  import type { ZodTableMap } from './schema'
3
4
 
5
+ /**
6
+ * Resolvers for the database the codec wrapper delegates to (#92).
7
+ *
8
+ * Each resolver receives the raw Convex ctx (untouched by zodvex) and returns
9
+ * the db the codec reader/writer should wrap instead of `ctx.db`. This lets
10
+ * native-shape layers — e.g. `convex-helpers/server/triggers` — sit UNDER the
11
+ * codec layer: codec on top → triggers → real db. The trigger layer sees
12
+ * wire-format writes, exactly what it is written against.
13
+ *
14
+ * ```ts
15
+ * const triggers = new Triggers<DataModel>()
16
+ * initZodvex(schema, server, {
17
+ * underlyingDb: { mutation: (ctx) => triggers.wrapDB(ctx).db }
18
+ * })
19
+ * ```
20
+ */
21
+ export type ZodvexUnderlyingDb<
22
+ QueryCtx = any,
23
+ MutationCtx = any,
24
+ Reader extends GenericDatabaseReader<any> = GenericDatabaseReader<any>,
25
+ Writer extends GenericDatabaseWriter<any> = GenericDatabaseWriter<any>
26
+ > = {
27
+ query?: (ctx: QueryCtx) => Reader
28
+ mutation?: (ctx: MutationCtx) => Writer
29
+ }
30
+
4
31
  /**
5
32
  * Creates Convex Customization objects that wrap ctx.db with codec
6
33
  * readers/writers. Returns { query, mutation } for use with
7
34
  * zCustomQuery/zCustomMutation or manual composition.
8
35
  *
36
+ * When `options.underlyingDb` is provided, the codec wrapper delegates to the
37
+ * resolved db instead of `ctx.db` (see {@link ZodvexUnderlyingDb}).
38
+ *
9
39
  * @example
10
40
  * ```typescript
11
41
  * const codec = createZodvexCustomization(schema.__zodTableMap)
@@ -19,19 +49,28 @@ import type { ZodTableMap } from './schema'
19
49
  * })
20
50
  * ```
21
51
  */
22
- export function createZodvexCustomization(tableMap: ZodTableMap) {
52
+ export function createZodvexCustomization(
53
+ tableMap: ZodTableMap,
54
+ options?: { underlyingDb?: ZodvexUnderlyingDb }
55
+ ) {
56
+ const resolveReaderDb = options?.underlyingDb?.query
57
+ const resolveWriterDb = options?.underlyingDb?.mutation
23
58
  return {
24
59
  query: {
25
60
  args: {} as Record<string, never>,
26
61
  input: async (ctx: any, _args: any, _extra?: any) => ({
27
- ctx: { db: new ZodvexDatabaseReader(ctx.db, tableMap) },
62
+ ctx: {
63
+ db: new ZodvexDatabaseReader(resolveReaderDb ? resolveReaderDb(ctx) : ctx.db, tableMap)
64
+ },
28
65
  args: {}
29
66
  })
30
67
  },
31
68
  mutation: {
32
69
  args: {} as Record<string, never>,
33
70
  input: async (ctx: any, _args: any, _extra?: any) => ({
34
- ctx: { db: new ZodvexDatabaseWriter(ctx.db, tableMap) },
71
+ ctx: {
72
+ db: new ZodvexDatabaseWriter(resolveWriterDb ? resolveWriterDb(ctx) : ctx.db, tableMap)
73
+ },
35
74
  args: {}
36
75
  })
37
76
  }
@@ -484,6 +484,18 @@ export class ZodvexDatabaseReader<
484
484
  return { db: this.db, tableMap: this.tableMap }
485
485
  }
486
486
 
487
+ /**
488
+ * Escape hatch (#85/#92): returns the database this codec wrapper delegates
489
+ * to — the composed underlying stack when `underlyingDb` is configured (e.g.
490
+ * a trigger-wrapped writer), the bare Convex db otherwise.
491
+ *
492
+ * The returned db is native-shape: reads are undecoded wire documents and it
493
+ * bypasses codec, `.withRules()`, and `.audit()` layers entirely.
494
+ */
495
+ unwrap(): GenericDatabaseReader<DataModel> {
496
+ return this.db
497
+ }
498
+
487
499
  normalizeId<TableName extends TableNamesInDataModel<DataModel>>(
488
500
  tableName: TableName,
489
501
  id: string
@@ -600,6 +612,18 @@ export class ZodvexDatabaseWriter<
600
612
  return { db: this.writerDb, tableMap: this.tableMap, reader: this }
601
613
  }
602
614
 
615
+ /**
616
+ * Escape hatch (#85/#92): returns the writer this codec wrapper delegates
617
+ * to — the composed underlying stack when `underlyingDb` is configured (e.g.
618
+ * a trigger-wrapped writer), the bare Convex db otherwise.
619
+ *
620
+ * The returned db is native-shape: writes must be wire-format and it
621
+ * bypasses codec, `.withRules()`, and `.audit()` layers entirely.
622
+ */
623
+ override unwrap(): GenericDatabaseWriter<DataModel> {
624
+ return this.writerDb
625
+ }
626
+
603
627
  // --- Write methods: encode before delegating ---
604
628
 
605
629
  insert<TableName extends TableNamesInDataModel<DataModel>>(
@@ -2,6 +2,8 @@ import type {
2
2
  ActionBuilder,
3
3
  FunctionVisibility,
4
4
  GenericActionCtx,
5
+ GenericDatabaseReader,
6
+ GenericDatabaseWriter,
5
7
  GenericDataModel,
6
8
  GenericMutationCtx,
7
9
  GenericQueryCtx,
@@ -13,7 +15,7 @@ import type { z } from 'zod'
13
15
  import { createCodecCallOverrides } from './actionCtx'
14
16
  import type { CustomBuilder } from './custom'
15
17
  import { zCustomAction, zCustomMutation, zCustomQuery } from './custom'
16
- import { createZodvexCustomization } from './customization'
18
+ import { createZodvexCustomization, type ZodvexUnderlyingDb } from './customization'
17
19
  import type { ZodvexDatabaseReader, ZodvexDatabaseWriter } from './db'
18
20
  import type { ZodValidator } from './mapping'
19
21
  import type { ZodTableMap } from './schema'
@@ -255,7 +257,28 @@ export function initZodvex<
255
257
  internalMutation: MutationBuilder<DM, 'internal'>
256
258
  internalAction: ActionBuilder<DM, 'internal'>
257
259
  },
258
- options?: { wrapDb?: true; registry?: () => AnyRegistry }
260
+ options?: {
261
+ wrapDb?: true
262
+ registry?: () => AnyRegistry
263
+ /**
264
+ * Resolve the database the codec wrapper delegates to, instead of `ctx.db`.
265
+ * Lets native-shape layers (e.g. convex-helpers triggers) sit under the
266
+ * codec layer — codec on top → triggers → real db. See #92.
267
+ *
268
+ * ```ts
269
+ * const triggers = new Triggers<DataModel>()
270
+ * initZodvex(schema, server, {
271
+ * underlyingDb: { mutation: (ctx) => triggers.wrapDB(ctx).db }
272
+ * })
273
+ * ```
274
+ */
275
+ underlyingDb?: ZodvexUnderlyingDb<
276
+ GenericQueryCtx<DM>,
277
+ GenericMutationCtx<DM>,
278
+ GenericDatabaseReader<DM>,
279
+ GenericDatabaseWriter<DM>
280
+ >
281
+ }
259
282
  ): {
260
283
  zq: ZodvexBuilder<'query', { db: ZodvexDatabaseReader<DM, DD> }, GenericQueryCtx<DM>, 'public'>
261
284
  zm: ZodvexBuilder<
@@ -279,11 +302,23 @@ export function initZodvex<
279
302
  export function initZodvex(
280
303
  schema: { __zodTableMap: ZodTableMap },
281
304
  server: InitServerBuilders,
282
- options?: { wrapDb?: boolean; registry?: () => AnyRegistry }
305
+ options?: {
306
+ wrapDb?: boolean
307
+ registry?: () => AnyRegistry
308
+ underlyingDb?: ZodvexUnderlyingDb
309
+ }
283
310
  ) {
284
- const codec = createZodvexCustomization(schema.__zodTableMap)
285
- const noOp = createNoOpCustomization()
286
311
  const wrap = options?.wrapDb !== false
312
+ if (!wrap && options?.underlyingDb) {
313
+ throw new Error(
314
+ '[zodvex] initZodvex: `underlyingDb` requires the codec db wrapper — ' +
315
+ 'remove `wrapDb: false` or drop `underlyingDb`.'
316
+ )
317
+ }
318
+ const codec = createZodvexCustomization(schema.__zodTableMap, {
319
+ underlyingDb: options?.underlyingDb
320
+ })
321
+ const noOp = createNoOpCustomization()
287
322
 
288
323
  const registryThunk = options?.registry
289
324
  const actionCust = createActionCustomization(registryThunk, noOp)
@@ -31,7 +31,10 @@ export {
31
31
  zCustomQuery
32
32
  } from '../../internal/custom'
33
33
  // Codec customization (manual composition escape hatch)
34
- export { createZodvexCustomization } from '../../internal/customization'
34
+ export {
35
+ createZodvexCustomization,
36
+ type ZodvexUnderlyingDb
37
+ } from '../../internal/customization'
35
38
  // Database wrappers (ZodvexDatabaseReader, ZodvexDatabaseWriter, etc.)
36
39
  export {
37
40
  createZodDbReader,