@zeno-lib/schema 0.2.1 → 0.3.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/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import { InferInsertModel, InferSelectModel, Table } from "drizzle-orm";
1
+ import { Column, InferInsertModel, InferSelectModel, Table } from "drizzle-orm";
2
2
  import { BuildRefine, BuildSchema, CoerceOptions, CreateSchemaFactoryOptions, NoUnknownKeys, createInsertSchema, createSchemaFactory, createSelectSchema, createUpdateSchema } from "drizzle-orm/zod";
3
3
  import { z } from "zod";
4
4
  //#region src/index.d.ts
@@ -21,8 +21,10 @@ type DefineTableSchemaOptions<TTable extends Table, TCoerce extends CoerceOption
21
21
  };
22
22
  type RefineFromOptions<TOptions, TKey extends string> = TKey extends keyof TOptions ? NonNullable<TOptions[TKey]> extends Record<string, unknown> ? NonNullable<TOptions[TKey]> : undefined : undefined;
23
23
  type OmitKeys<TSchema, TKeys extends PropertyKey> = TSchema extends z.ZodObject<infer TShape, infer TConfig> ? z.ZodObject<{ [K in keyof TShape as K extends TKeys ? never : K]: TShape[K]; }, TConfig> : never;
24
+ type IsInsertOptional<TColumn> = TColumn extends Column ? TColumn["_"]["notNull"] extends true ? TColumn["_"]["hasDefault"] extends true ? true : false : true : false;
25
+ type OptionalRefinedInsert<TSchema, TTable extends Table, TRefine> = TSchema extends z.ZodObject<infer TShape, infer TConfig> ? z.ZodObject<{ [K in keyof TShape]: K extends keyof TRefine ? TRefine[K] extends ((schema: never) => unknown) ? (K extends keyof TableColumns<TTable> ? IsInsertOptional<TableColumns<TTable>[K]> : false) extends true ? TShape[K] extends z.ZodOptional ? TShape[K] : z.ZodOptional<TShape[K]> : TShape[K] : TShape[K] : TShape[K]; }, TConfig> : never;
24
26
  type DefineTableSchemaResult<TTable extends Table, TCoerce extends CoerceOptions, TOptions> = {
25
- insert: OmitKeys<BuildSchema<"insert", TableColumns<TTable>, RefineFromOptions<TOptions, "insert">, TCoerce>, AuditKeys<TTable>>;
27
+ insert: OmitKeys<OptionalRefinedInsert<BuildSchema<"insert", TableColumns<TTable>, RefineFromOptions<TOptions, "insert">, TCoerce>, TTable, RefineFromOptions<TOptions, "insert">>, AuditKeys<TTable>>;
26
28
  select: BuildSchema<"select", TableColumns<TTable>, RefineFromOptions<TOptions, "select">, TCoerce>;
27
29
  update: OmitKeys<BuildSchema<"update", TableColumns<TTable>, RefineFromOptions<TOptions, "update">, TCoerce>, AuditKeys<TTable>>;
28
30
  };
package/package.json CHANGED
@@ -1,6 +1,10 @@
1
1
  {
2
2
  "name": "@zeno-lib/schema",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
+ "repository": {
5
+ "url": "https://github.com/zeno-lib/zeno.git",
6
+ "directory": "packages/schema"
7
+ },
4
8
  "type": "module",
5
9
  "exports": {
6
10
  ".": {
@@ -13,24 +17,20 @@
13
17
  "dist",
14
18
  "src"
15
19
  ],
16
- "publishConfig": {
17
- "access": "public"
18
- },
19
- "repository": {
20
- "url": "https://github.com/zeno-lib/zeno.git",
21
- "directory": "packages/schema"
20
+ "devDependencies": {
21
+ "@zeno-lib/typescript": "^1.1.0",
22
+ "drizzle-orm": "1.0.0-rc.5-5935859",
23
+ "tsdown": "^0.23.0",
24
+ "typescript": "7.0.2",
25
+ "vitest": "5.0.3",
26
+ "zod": "^4.6.5"
22
27
  },
23
28
  "peerDependencies": {
24
- "drizzle-orm": "1.0.0-rc.3",
29
+ "drizzle-orm": "1.0.0-rc.5-5935859",
25
30
  "zod": ">=4"
26
31
  },
27
- "devDependencies": {
28
- "drizzle-orm": "1.0.0-rc.3",
29
- "tsdown": "^0.22.14",
30
- "typescript": "7.0.2",
31
- "vitest": "4.1.11",
32
- "zod": "^4.4.3",
33
- "@zeno-lib/typescript": "^1.1.0"
32
+ "publishConfig": {
33
+ "access": "public"
34
34
  },
35
35
  "scripts": {
36
36
  "build": "tsdown",
@@ -12,11 +12,9 @@ type StandardSchema<T> = {
12
12
  }
13
13
  }
14
14
 
15
- function acceptsFormsSchema<TFormData>(
15
+ declare function acceptsFormsSchema<TFormData>(
16
16
  _schema: StandardSchema<TFormData>
17
- ): void {
18
- return
19
- }
17
+ ): void
20
18
 
21
19
  const posts = pgTable("posts", {
22
20
  id: integer("id").primaryKey().generatedAlwaysAsIdentity(),
@@ -80,3 +78,29 @@ test("defaulted audit columns leave the insert and update types", () => {
80
78
  insert: { createdBy: (schema) => schema },
81
79
  })
82
80
  })
81
+
82
+ test("a function refinement keeps a nullable or defaulted insert column optional", () => {
83
+ const drafts = pgTable("drafts", {
84
+ note: text("note"),
85
+ status: text("status").notNull().default("open"),
86
+ title: text("title").notNull(),
87
+ })
88
+ const schemas = defineTableSchema(drafts, {
89
+ insert: {
90
+ note: (schema) => schema.min(1),
91
+ status: (schema) => schema.min(1),
92
+ title: (schema) => schema.min(1),
93
+ },
94
+ })
95
+
96
+ expectTypeOf<z.infer<typeof schemas.insert>>().toEqualTypeOf<{
97
+ note?: string | null | undefined
98
+ status?: string | undefined
99
+ title: string
100
+ }>()
101
+ expectTypeOf<z.input<typeof schemas.insert>>().toEqualTypeOf<{
102
+ note?: string | null | undefined
103
+ status?: string | undefined
104
+ title: string
105
+ }>()
106
+ })
package/src/index.test.ts CHANGED
@@ -210,4 +210,23 @@ describe("defineTableSchema", () => {
210
210
  expect(schemas.insert.safeParse({ id: 11 }).success).toBe(false)
211
211
  expect(schemas.insert.safeParse({ id: 5 }).success).toBe(true)
212
212
  })
213
+
214
+ it("keeps a function-refined nullable insert column optional", () => {
215
+ const drafts = pgTable("drafts", {
216
+ note: text("note"),
217
+ title: text("title").notNull(),
218
+ })
219
+ const schemas = defineTableSchema(drafts, {
220
+ insert: { note: (schema) => schema.min(1) },
221
+ })
222
+
223
+ expect(schemas.insert.parse({ title: "x" })).toEqual({ title: "x" })
224
+ expect(schemas.insert.parse({ note: null, title: "x" })).toEqual({
225
+ note: null,
226
+ title: "x",
227
+ })
228
+ expect(schemas.insert.safeParse({ note: "", title: "x" }).success).toBe(
229
+ false
230
+ )
231
+ })
213
232
  })
package/src/index.ts CHANGED
@@ -99,17 +99,54 @@ type OmitKeys<TSchema, TKeys extends PropertyKey> =
99
99
  >
100
100
  : never
101
101
 
102
+ // Drizzle types a function refinement on an insert column as required, even
103
+ // when the column is nullable or defaulted and the runtime wraps it in
104
+ // `.optional()`. This puts the wrapper back into the type.
105
+ type IsInsertOptional<TColumn> = TColumn extends Column
106
+ ? TColumn["_"]["notNull"] extends true
107
+ ? TColumn["_"]["hasDefault"] extends true
108
+ ? true
109
+ : false
110
+ : true
111
+ : false
112
+
113
+ type OptionalRefinedInsert<TSchema, TTable extends Table, TRefine> =
114
+ TSchema extends z.ZodObject<infer TShape, infer TConfig>
115
+ ? z.ZodObject<
116
+ {
117
+ [K in keyof TShape]: K extends keyof TRefine
118
+ ? TRefine[K] extends (schema: never) => unknown
119
+ ? (
120
+ K extends keyof TableColumns<TTable>
121
+ ? IsInsertOptional<TableColumns<TTable>[K]>
122
+ : false
123
+ ) extends true
124
+ ? TShape[K] extends z.ZodOptional
125
+ ? TShape[K]
126
+ : z.ZodOptional<TShape[K]>
127
+ : TShape[K]
128
+ : TShape[K]
129
+ : TShape[K]
130
+ },
131
+ TConfig
132
+ >
133
+ : never
134
+
102
135
  type DefineTableSchemaResult<
103
136
  TTable extends Table,
104
137
  TCoerce extends CoerceOptions,
105
138
  TOptions,
106
139
  > = {
107
140
  insert: OmitKeys<
108
- BuildSchema<
109
- "insert",
110
- TableColumns<TTable>,
111
- RefineFromOptions<TOptions, "insert">,
112
- TCoerce
141
+ OptionalRefinedInsert<
142
+ BuildSchema<
143
+ "insert",
144
+ TableColumns<TTable>,
145
+ RefineFromOptions<TOptions, "insert">,
146
+ TCoerce
147
+ >,
148
+ TTable,
149
+ RefineFromOptions<TOptions, "insert">
113
150
  >,
114
151
  AuditKeys<TTable>
115
152
  >