cogsbox-shape 0.5.205 → 0.5.206

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.
@@ -16,7 +16,7 @@ describe("Schema Builder Type Tests (with expect-type)", () => {
16
16
  });
17
17
  it("should correctly type a nullable integer field", () => {
18
18
  const ageField = s.sqlite({ type: "int", nullable: true });
19
- expectTypeOf(ageField.config.zodClientSchema).toEqualTypeOf();
19
+ expectTypeOf(ageField.config.zodClientCheckedSchema).toEqualTypeOf();
20
20
  });
21
21
  it("should correctly type a primary key field", () => {
22
22
  const idField = s.sqlite({ type: "int", pk: true });
@@ -34,7 +34,7 @@ describe("Schema Builder Type Tests (with expect-type)", () => {
34
34
  });
35
35
  describe("Chainable Methods", () => {
36
36
  it("should create a union type when .client provides a different type", () => {
37
- const idField = s.sqlite({ type: "int", pk: true }).clientInput({
37
+ const idField = s.sqlite({ type: "int", pk: true }).client({
38
38
  value: () => "temp-uuid-123",
39
39
  schema: z.literal("temp-uuid-123"),
40
40
  });
@@ -43,13 +43,13 @@ describe("Schema Builder Type Tests (with expect-type)", () => {
43
43
  it("should NOT create a union type when .client provides the same type", () => {
44
44
  const countField = s
45
45
  .sqlite({ type: "int" })
46
- .clientInput({ value: () => 0, schema: z.number() });
46
+ .client({ value: () => 0, schema: z.number() });
47
47
  expectTypeOf().toEqualTypeOf();
48
48
  });
49
- it("should correctly override the client schema with .clientInput()", () => {
49
+ it("should correctly override the client schema with .client()", () => {
50
50
  const statusField = s
51
51
  .sqlite({ type: "int" })
52
- .clientInput(() => z.boolean());
52
+ .client(() => z.boolean());
53
53
  expectTypeOf().toEqualTypeOf();
54
54
  expectTypeOf().toEqualTypeOf();
55
55
  expectTypeOf().toEqualTypeOf();
@@ -57,16 +57,16 @@ describe("Schema Builder Type Tests (with expect-type)", () => {
57
57
  it("should add validation to client schema with .client()", () => {
58
58
  const nameField = s
59
59
  .sqlite({ type: "varchar" })
60
- .clientInput({ value: "John" })
61
- .client((tools) => tools.clientInput.min(3));
60
+ .client({ value: "John" })
61
+ .clientCheck((tools) => tools.client.min(3));
62
62
  expectTypeOf().toEqualTypeOf();
63
63
  });
64
- it("should chain .clientInput().client().server() correctly", () => {
64
+ it("should chain .client().clientCheck().server() correctly", () => {
65
65
  const nameField = s
66
66
  .sqlite({ type: "varchar" })
67
- .clientInput({ value: "" })
68
- .client((tools) => tools.clientInput.min(3))
69
- .server((tools) => tools.clientInput.min(5));
67
+ .client({ value: "" })
68
+ .clientCheck((tools) => tools.client.min(3))
69
+ .server((tools) => tools.client.min(5));
70
70
  expectTypeOf().toEqualTypeOf();
71
71
  expectTypeOf().toEqualTypeOf();
72
72
  });
@@ -75,7 +75,7 @@ describe("Schema Builder Type Tests (with expect-type)", () => {
75
75
  // 1. Define schemas with placeholders
76
76
  const users = schema({
77
77
  _tableName: "users",
78
- id: s.sqlite({ type: "int", pk: true }).clientInput({
78
+ id: s.sqlite({ type: "int", pk: true }).client({
79
79
  value: () => "new-user",
80
80
  schema: z.literal("new-user"),
81
81
  }),
@@ -84,7 +84,7 @@ describe("Schema Builder Type Tests (with expect-type)", () => {
84
84
  const posts = schema({
85
85
  _tableName: "posts",
86
86
  id: s.sqlite({ type: "int", pk: true }),
87
- isPublished: s.sqlite({ type: "int" }).clientInput(() => z.boolean()),
87
+ isPublished: s.sqlite({ type: "int" }).client(() => z.boolean()),
88
88
  authorId: s.reference(() => users.id),
89
89
  });
90
90
  // 2. Create the registry and resolve relations
@@ -110,7 +110,7 @@ describe("Schema Builder Runtime Behavior", () => {
110
110
  // Define the schema using the new builder syntax
111
111
  const defaultsSchema = schema({
112
112
  _tableName: "defaults",
113
- fromInitialState: s.sqlite({ type: "varchar" }).clientInput({
113
+ fromInitialState: s.sqlite({ type: "varchar" }).client({
114
114
  value: () => "from-initial-state",
115
115
  schema: z.string(),
116
116
  }),
@@ -123,7 +123,7 @@ describe("Schema Builder Runtime Behavior", () => {
123
123
  defaults: {},
124
124
  });
125
125
  const defaults = box.defaults.defaults;
126
- it("should get default from .clientInput()", () => {
126
+ it("should get default from .client()", () => {
127
127
  expect(defaults.fromInitialState).toBe("from-initial-state");
128
128
  });
129
129
  it("should get default from SQL config", () => {
@@ -142,7 +142,7 @@ describe("Schema Builder Runtime Behavior", () => {
142
142
  id: s.sqlite({ type: "int", pk: true }),
143
143
  status: s
144
144
  .sqlite({ type: "int" }) // DB: 0=Inactive, 1=Active
145
- .clientInput(() => z.enum(["inactive", "active"])) // Client: "inactive" | "active"
145
+ .client(() => z.enum(["inactive", "active"])) // Client: "inactive" | "active"
146
146
  .transform({
147
147
  toClient: (dbValue) => (dbValue === 1 ? "active" : "inactive"),
148
148
  toDb: (clientValue) => (clientValue === "active" ? 1 : 0),
@@ -155,13 +155,13 @@ describe("Schema Builder Runtime Behavior", () => {
155
155
  const box = createSchemaBox({ complex: complexSchemaDef }, {
156
156
  complex: {},
157
157
  });
158
- const { client, sql, server } = box.complex.schemas;
158
+ const { client: clientChecked, sql, server } = box.complex.schemas;
159
159
  const { toClient, toDb } = box.complex.transforms;
160
160
  it("should correctly transform a DB object to a Client object", () => {
161
161
  const dbData = { id: 1, status: 1, name: "Test" };
162
162
  const clientResult = toClient(dbData);
163
163
  expect(clientResult.status).toBe("active");
164
- expect(() => client.parse(clientResult)).not.toThrow();
164
+ expect(() => clientChecked.parse(clientResult)).not.toThrow();
165
165
  });
166
166
  it("should correctly transform a Client object to a DB object", () => {
167
167
  const clientData = { id: 1, status: "inactive", name: "Test" };
@@ -183,78 +183,78 @@ describe("Tools params are actual Zod schemas at runtime", () => {
183
183
  it("should provide actual Zod schemas as tools in .client()", () => {
184
184
  let capturedTools;
185
185
  s.sqlite({ type: "varchar" })
186
- .clientInput({ value: "test" })
187
- .client((tools) => {
186
+ .client({ value: "test" })
187
+ .clientCheck((tools) => {
188
188
  capturedTools = {
189
189
  sql: tools.sql,
190
- clientInput: tools.clientInput,
191
190
  client: tools.client,
191
+ clientCheck: tools.clientCheck,
192
192
  };
193
- return tools.clientInput;
193
+ return tools.clientCheck;
194
194
  });
195
195
  expect(capturedTools).toBeDefined();
196
196
  expect(capturedTools.sql).toBeInstanceOf(z.ZodType);
197
197
  expect(typeof capturedTools.sql.parse).toBe("function");
198
198
  expect(typeof capturedTools.sql.safeParse).toBe("function");
199
- expect(capturedTools.clientInput).toBeInstanceOf(z.ZodType);
200
- expect(typeof capturedTools.clientInput.parse).toBe("function");
201
199
  expect(capturedTools.client).toBeInstanceOf(z.ZodType);
202
200
  expect(typeof capturedTools.client.parse).toBe("function");
201
+ expect(capturedTools.clientCheck).toBeInstanceOf(z.ZodType);
202
+ expect(typeof capturedTools.clientCheck.parse).toBe("function");
203
203
  });
204
204
  it("should provide actual Zod schemas as tools in .server()", () => {
205
205
  let capturedTools;
206
206
  s.sqlite({ type: "varchar" })
207
- .clientInput({ value: "test" })
208
- .client((tools) => tools.clientInput)
207
+ .client({ value: "test" })
208
+ .clientCheck((tools) => tools.client)
209
209
  .server((tools) => {
210
210
  capturedTools = {
211
211
  sql: tools.sql,
212
- clientInput: tools.clientInput,
213
212
  client: tools.client,
213
+ clientCheck: tools.clientCheck,
214
214
  };
215
- return tools.clientInput;
215
+ return tools.clientCheck;
216
216
  });
217
217
  expect(capturedTools).toBeDefined();
218
218
  expect(capturedTools.sql).toBeInstanceOf(z.ZodType);
219
- expect(capturedTools.clientInput).toBeInstanceOf(z.ZodType);
220
219
  expect(capturedTools.client).toBeInstanceOf(z.ZodType);
220
+ expect(capturedTools.clientCheck).toBeInstanceOf(z.ZodType);
221
221
  });
222
222
  it("should allow calling Zod methods on tools params in .client()", () => {
223
- let clientInputSchema;
223
+ let clientSchema;
224
224
  s.sqlite({ type: "varchar" })
225
- .clientInput({ value: "" })
226
- .client((tools) => {
227
- clientInputSchema = tools.clientInput;
228
- const validated = tools.clientInput.min(3);
225
+ .client({ value: "" })
226
+ .clientCheck((tools) => {
227
+ clientSchema = tools.client;
228
+ const validated = tools.client.min(3);
229
229
  expect(validated.safeParse("ab").success).toBe(false);
230
230
  expect(validated.safeParse("abc").success).toBe(true);
231
231
  return validated;
232
232
  });
233
- expect(clientInputSchema).toBeInstanceOf(z.ZodString);
233
+ expect(clientSchema).toBeInstanceOf(z.ZodString);
234
234
  });
235
235
  it("should allow calling Zod methods on tools params in .server()", () => {
236
- let serverClientInput;
236
+ let serverClientSchema;
237
237
  s.sqlite({ type: "varchar" })
238
- .clientInput({ value: "" })
239
- .client((tools) => tools.clientInput.min(3))
238
+ .client({ value: "" })
239
+ .clientCheck((tools) => tools.client.min(3))
240
240
  .server((tools) => {
241
- serverClientInput = tools.clientInput;
242
- const validated = tools.clientInput.min(5);
241
+ serverClientSchema = tools.client;
242
+ const validated = tools.client.min(5);
243
243
  expect(validated.safeParse("abcd").success).toBe(false);
244
244
  expect(validated.safeParse("abcde").success).toBe(true);
245
245
  return validated;
246
246
  });
247
- expect(serverClientInput).toBeInstanceOf(z.ZodString);
247
+ expect(serverClientSchema).toBeInstanceOf(z.ZodString);
248
248
  });
249
249
  it("should provide correct Zod types for numeric fields in tools", () => {
250
250
  let capturedSql;
251
251
  let capturedInput;
252
252
  s.sqlite({ type: "int" })
253
- .clientInput({ value: 0 })
254
- .client((tools) => {
253
+ .client({ value: 0 })
254
+ .clientCheck((tools) => {
255
255
  capturedSql = tools.sql;
256
- capturedInput = tools.clientInput;
257
- return tools.clientInput;
256
+ capturedInput = tools.client;
257
+ return tools.client;
258
258
  });
259
259
  expect(capturedSql).toBeInstanceOf(z.ZodNumber);
260
260
  expect(capturedSql.parse(42)).toBe(42);
@@ -264,10 +264,10 @@ describe("Tools params are actual Zod schemas at runtime", () => {
264
264
  it("should provide correct Zod types for boolean fields in tools", () => {
265
265
  let capturedInput;
266
266
  s.sqlite({ type: "int" })
267
- .clientInput(() => z.boolean())
268
- .client((tools) => {
269
- capturedInput = tools.clientInput;
270
- return tools.clientInput;
267
+ .client(() => z.boolean())
268
+ .clientCheck((tools) => {
269
+ capturedInput = tools.client;
270
+ return tools.client;
271
271
  });
272
272
  expect(capturedInput).toBeInstanceOf(z.ZodBoolean);
273
273
  expect(capturedInput.parse(true)).toBe(true);
@@ -276,22 +276,110 @@ describe("Tools params are actual Zod schemas at runtime", () => {
276
276
  it("should allow nullable() and other modifiers on tools params", () => {
277
277
  let capturedInput;
278
278
  s.sqlite({ type: "varchar", nullable: true })
279
- .clientInput({ value: null, schema: z.string().nullable() })
280
- .client((tools) => {
281
- capturedInput = tools.clientInput;
282
- expect(tools.clientInput.safeParse(null).success).toBe(true);
283
- expect(tools.clientInput.safeParse("hello").success).toBe(true);
284
- return tools.clientInput;
279
+ .client({ value: null, schema: z.string().nullable() })
280
+ .clientCheck((tools) => {
281
+ capturedInput = tools.client;
282
+ expect(tools.client.safeParse(null).success).toBe(true);
283
+ expect(tools.client.safeParse("hello").success).toBe(true);
284
+ return tools.client;
285
285
  });
286
286
  expect(capturedInput).toBeInstanceOf(z.ZodNullable);
287
287
  });
288
288
  });
289
+ describe("client vs clientChecked schema divergence after .clientCheck()", () => {
290
+ it("should retain unmodified client schema after .clientCheck() adds .min()", () => {
291
+ const nameField = s.sqlite({ type: "varchar" })
292
+ .client({ value: "John" })
293
+ .clientCheck((tools) => tools.client.min(3));
294
+ const clientSchema = nameField.config.zodClientSchema;
295
+ const clientCheckedSchema = nameField.config.zodClientCheckedSchema;
296
+ expect(clientSchema.safeParse("ab").success).toBe(true);
297
+ expect(clientCheckedSchema.safeParse("ab").success).toBe(false);
298
+ expect(clientCheckedSchema.safeParse("abc").success).toBe(true);
299
+ });
300
+ it("should diverge at box schema level after .clientCheck()", () => {
301
+ const users = schema({
302
+ _tableName: "users",
303
+ id: s.sqlite({ type: "int", pk: true }),
304
+ name: s.sqlite({ type: "varchar" })
305
+ .client({ value: "" })
306
+ .clientCheck((tools) => tools.client.min(3)),
307
+ });
308
+ const box = createSchemaBox({ users }, { users: {} });
309
+ expect(box.users.schemas.client.shape.name.safeParse("ab").success).toBe(true);
310
+ expect(box.users.schemas.clientChecked.shape.name.safeParse("ab").success).toBe(false);
311
+ expect(box.users.schemas.clientChecked.shape.name.safeParse("abc").success).toBe(true);
312
+ });
313
+ it("should apply .email() only to clientChecked schema, not client", () => {
314
+ const nameField = s.sqlite({ type: "varchar" })
315
+ .client({ value: "" })
316
+ .clientCheck((tools) => tools.client.email());
317
+ expect(nameField.config.zodClientSchema.safeParse("not-an-email").success).toBe(true);
318
+ expect(nameField.config.zodClientCheckedSchema.safeParse("not-an-email").success).toBe(false);
319
+ expect(nameField.config.zodClientCheckedSchema.safeParse("a@b.com").success).toBe(true);
320
+ });
321
+ it("should apply .max() only to clientChecked schema, not client", () => {
322
+ const countField = s.sqlite({ type: "int" })
323
+ .client({ value: () => 0, schema: z.number() })
324
+ .clientCheck((tools) => tools.client.max(100));
325
+ expect(countField.config.zodClientSchema.safeParse(200).success).toBe(true);
326
+ expect(countField.config.zodClientCheckedSchema.safeParse(200).success).toBe(false);
327
+ expect(countField.config.zodClientCheckedSchema.safeParse(50).success).toBe(true);
328
+ });
329
+ it("should produce different schemas across all three layers: client, clientChecked, server", () => {
330
+ const nameField = s.sqlite({ type: "varchar" })
331
+ .client({ value: "" })
332
+ .clientCheck((tools) => tools.client.min(3))
333
+ .server((tools) => tools.client.min(5));
334
+ expect(nameField.config.zodClientSchema.safeParse("a").success).toBe(true);
335
+ expect(nameField.config.zodClientCheckedSchema.safeParse("a").success).toBe(false);
336
+ expect(nameField.config.zodClientCheckedSchema.safeParse("abc").success).toBe(true);
337
+ expect(nameField.config.zodValidationSchema.safeParse("abc").success).toBe(false);
338
+ expect(nameField.config.zodValidationSchema.safeParse("abcde").success).toBe(true);
339
+ });
340
+ it("should preserve clientInputZod on the internal builder config after .clientCheck()", () => {
341
+ let capturedConfig;
342
+ const field = s.sqlite({ type: "varchar" })
343
+ .client({ value: "x" })
344
+ .clientCheck((tools) => {
345
+ capturedConfig = { clientInputZod: tools.clientInputZod };
346
+ return tools.client.min(3);
347
+ });
348
+ expect(field.config.zodClientSchema).toBeDefined();
349
+ expect(field.config.zodClientCheckedSchema).toBeDefined();
350
+ expect(field.config.zodClientSchema).not.toBe(field.config.zodClientCheckedSchema);
351
+ });
352
+ it("should diverge when refine targets only 'clientCheck' layer, not 'client'", () => {
353
+ const forms = schema({
354
+ _tableName: "forms",
355
+ id: s.sqlite({ type: "int", pk: true }),
356
+ password: s.sqlite({ type: "varchar" }).client({ value: "" }),
357
+ confirmPassword: s.sqlite({ type: "varchar" }).client({ value: "" }),
358
+ }).refine((r) => [
359
+ r("clientCheck", (row) => {
360
+ if (row.password !== row.confirmPassword) {
361
+ return { path: ["confirmPassword"], message: "Passwords must match" };
362
+ }
363
+ return undefined;
364
+ }),
365
+ ]);
366
+ const box = createSchemaBox({ forms }, { forms: {} });
367
+ const clientResult = box.forms.schemas.client.safeParse({
368
+ id: 1, password: "abc", confirmPassword: "def",
369
+ });
370
+ expect(clientResult.success).toBe(true);
371
+ const clientCheckedResult = box.forms.schemas.clientChecked.safeParse({
372
+ id: 1, password: "abc", confirmPassword: "def",
373
+ });
374
+ expect(clientCheckedResult.success).toBe(false);
375
+ });
376
+ });
289
377
  describe("New Session Features - Base Schema Without Relations", () => {
290
378
  const users = schema({
291
379
  _tableName: "users",
292
380
  id: s
293
381
  .sqlite({ type: "int", pk: true })
294
- .clientInput({ value: () => "user-123", schema: z.string() }),
382
+ .client({ value: () => "user-123", schema: z.string() }),
295
383
  petId: s.reference(() => pets.id),
296
384
  pets: s.hasMany(),
297
385
  });
@@ -313,7 +401,7 @@ describe("New Session Features - Base Schema Without Relations", () => {
313
401
  it("should exclude relations from base client schema", () => {
314
402
  expectTypeOf().toEqualTypeOf();
315
403
  // Runtime check - the schema shape should not include 'pets'
316
- const clientShape = box.users.schemas.client.shape;
404
+ const clientShape = box.users.schemas.clientChecked.shape;
317
405
  expect(clientShape).not.toHaveProperty("pets");
318
406
  expect(clientShape).toHaveProperty("id");
319
407
  expect(clientShape).toHaveProperty("petId");
@@ -333,7 +421,7 @@ describe("New Session Features - Base Schema Without Relations", () => {
333
421
  });
334
422
  expectTypeOf().toEqualTypeOf();
335
423
  // Runtime check
336
- const viewShape = userView.schemas.client.shape;
424
+ const viewShape = userView.schemas.clientChecked.shape;
337
425
  expect(viewShape).toHaveProperty("pets");
338
426
  expect(viewShape.pets).toBeInstanceOf(z.ZodArray);
339
427
  });
@@ -343,7 +431,7 @@ describe("New Session Features - Base Schema Without Relations", () => {
343
431
  });
344
432
  expectTypeOf().toEqualTypeOf();
345
433
  // Runtime check - owner should not have pets
346
- const shape = userViewNested.schemas.client.shape;
434
+ const shape = userViewNested.schemas.clientChecked.shape;
347
435
  if (shape.pets instanceof z.ZodArray) {
348
436
  const petSchema = shape.pets.element;
349
437
  if (petSchema instanceof z.ZodObject) {
@@ -389,8 +477,8 @@ describe("Relation Defaults in Views", () => {
389
477
  _tableName: "users",
390
478
  id: s
391
479
  .sqlite({ type: "int", pk: true })
392
- .clientInput({ value: () => "user-123", schema: z.string() }),
393
- name: s.sqlite({ type: "varchar" }).clientInput({ value: "John" }),
480
+ .client({ value: () => "user-123", schema: z.string() }),
481
+ name: s.sqlite({ type: "varchar" }).client({ value: "John" }),
394
482
  posts: s.hasMany({ count: 2 }), // Should generate 2 posts
395
483
  comments: s.hasMany([]), // Should be empty array
396
484
  profile: s.hasOne(true), // Changed from {} to true
@@ -400,7 +488,7 @@ describe("Relation Defaults in Views", () => {
400
488
  const posts = schema({
401
489
  _tableName: "posts",
402
490
  id: s.sqlite({ type: "int", pk: true }),
403
- title: s.sqlite({ type: "varchar" }).clientInput({ value: "Default Post" }),
491
+ title: s.sqlite({ type: "varchar" }).client({ value: "Default Post" }),
404
492
  authorId: s.reference(() => users.id),
405
493
  user: s.hasOne(true),
406
494
  });
@@ -409,14 +497,14 @@ describe("Relation Defaults in Views", () => {
409
497
  id: s.sqlite({ type: "int", pk: true }),
410
498
  text: s
411
499
  .sqlite({ type: "varchar" })
412
- .clientInput({ value: "Default Comment" }),
500
+ .client({ value: "Default Comment" }),
413
501
  userId: s.reference(() => users.id),
414
502
  user: s.hasOne(true),
415
503
  });
416
504
  const profiles = schema({
417
505
  _tableName: "profiles",
418
506
  id: s.sqlite({ type: "int", pk: true }),
419
- bio: s.sqlite({ type: "varchar" }).clientInput({ value: "Default Bio" }),
507
+ bio: s.sqlite({ type: "varchar" }).client({ value: "Default Bio" }),
420
508
  userId: s.reference(() => users.id),
421
509
  user: s.hasOne(true),
422
510
  });
@@ -484,10 +572,10 @@ describe("Relation Defaults in Views", () => {
484
572
  expect(defaults.posts?.[0]).not.toHaveProperty("author"); // Relation not selected
485
573
  });
486
574
  });
487
- describe("Transform affects defaults", () => {
575
+ describe("Client defaults and transforms", () => {
488
576
  const userSchema = schema({
489
577
  _tableName: "users",
490
- id: s.sqlite({ type: "int", pk: true }).clientInput({
578
+ id: s.sqlite({ type: "int", pk: true }).client({
491
579
  value: () => `temp_${Math.random().toString(36).substr(2, 9)}`,
492
580
  schema: z.string(),
493
581
  }),
@@ -496,12 +584,12 @@ describe("Transform affects defaults", () => {
496
584
  .server(() => z.email("Invalid email address")),
497
585
  isActive: s
498
586
  .sqlite({ type: "int" })
499
- .clientInput(() => z.boolean())
587
+ .client(() => z.boolean())
500
588
  .transform({
501
589
  toClient: (val) => val === 1,
502
590
  toDb: (val) => (val ? 1 : 0),
503
591
  }),
504
- role: s.sqlite({ type: "varchar" }).clientInput({
592
+ role: s.sqlite({ type: "varchar" }).client({
505
593
  value: "user",
506
594
  schema: z.enum(["user", "admin"]),
507
595
  }),
@@ -509,7 +597,7 @@ describe("Transform affects defaults", () => {
509
597
  const box = createSchemaBox({ users: userSchema }, { users: {} });
510
598
  const { transforms } = box.users;
511
599
  const defaults = box.users.generateDefaults();
512
- it("should have defaults with correct runtime values", () => {
600
+ it("should infer schema-only client defaults without running toClient", () => {
513
601
  expect(defaults.isActive).toBe(false);
514
602
  expect(typeof defaults.isActive).toBe("boolean");
515
603
  });
@@ -521,10 +609,44 @@ describe("Transform affects defaults", () => {
521
609
  expect(clientVersion.isActive).toBe(false);
522
610
  expect(typeof clientVersion.isActive).toBe("boolean");
523
611
  });
612
+ it("should prefer explicit client defaults over inferred defaults", () => {
613
+ const explicitDefaults = createSchemaBox({
614
+ users: schema({
615
+ _tableName: "users",
616
+ score: s.sqlite({ type: "int" }).client({
617
+ value: 7,
618
+ schema: z.number().min(0),
619
+ }),
620
+ isActive: s.sqlite({ type: "int" }).client({
621
+ value: true,
622
+ schema: z.boolean(),
623
+ }),
624
+ }),
625
+ }, { users: {} }).users.generateDefaults();
626
+ expect(explicitDefaults.score).toBe(7);
627
+ expect(explicitDefaults.isActive).toBe(true);
628
+ });
629
+ it("should not use toClient while generating defaults", () => {
630
+ const defaults = createSchemaBox({
631
+ users: schema({
632
+ _tableName: "users",
633
+ isActive: s
634
+ .sqlite({ type: "int" })
635
+ .client(() => z.boolean())
636
+ .transform({
637
+ toClient: () => {
638
+ throw new Error("toClient should not run for defaults");
639
+ },
640
+ toDb: (value) => (value ? 1 : 0),
641
+ }),
642
+ }),
643
+ }, { users: {} }).users.generateDefaults();
644
+ expect(defaults.isActive).toBe(false);
645
+ });
524
646
  });
525
647
  describe("UUID generation in initialState", () => {
526
648
  it("should call value function with uuid tool", () => {
527
- const field = s.sqlite({ type: "int", pk: true }).clientInput({
649
+ const field = s.sqlite({ type: "int", pk: true }).client({
528
650
  value: ({ uuid }) => uuid(),
529
651
  schema: z.string(),
530
652
  clientPk: true,
@@ -540,15 +662,15 @@ describe("UUID generation in initialState", () => {
540
662
  describe("Missing properties - parseForDb, parseFromDb, pk, clientPk, isClientRecord", () => {
541
663
  const users = schema({
542
664
  _tableName: "users",
543
- id: s.sqlite({ type: "int", pk: true }).clientInput({
665
+ id: s.sqlite({ type: "int", pk: true }).client({
544
666
  value: ({ uuid }) => uuid(),
545
667
  schema: z.string(),
546
668
  clientPk: true,
547
669
  }),
548
- name: s.sqlite({ type: "varchar" }).clientInput({ value: "John" }),
670
+ name: s.sqlite({ type: "varchar" }).client({ value: "John" }),
549
671
  isActive: s
550
672
  .sqlite({ type: "int" })
551
- .clientInput(() => z.boolean())
673
+ .client(() => z.boolean())
552
674
  .transform({
553
675
  toClient: (val) => val === 1,
554
676
  toDb: (val) => (val ? 1 : 0),
@@ -559,7 +681,7 @@ describe("Missing properties - parseForDb, parseFromDb, pk, clientPk, isClientRe
559
681
  const posts = schema({
560
682
  _tableName: "posts",
561
683
  id: s.sqlite({ type: "int", pk: true }),
562
- title: s.sqlite({ type: "varchar" }).clientInput({ value: "Untitled" }),
684
+ title: s.sqlite({ type: "varchar" }).client({ value: "Untitled" }),
563
685
  authorId: s.reference(() => users.id),
564
686
  });
565
687
  const box = createSchemaBox({ users, posts }, {
@@ -576,7 +698,7 @@ describe("Missing properties - parseForDb, parseFromDb, pk, clientPk, isClientRe
576
698
  const sqlKeys = Object.keys(box.users.schemas.sql.shape);
577
699
  expect(sqlKeys).toContain("email_address");
578
700
  expect(sqlKeys).not.toContain("email");
579
- const clientKeys = Object.keys(box.users.schemas.client.shape);
701
+ const clientKeys = Object.keys(box.users.schemas.clientChecked.shape);
580
702
  expect(clientKeys).toContain("email");
581
703
  expect(clientKeys).not.toContain("email_address");
582
704
  });
@@ -650,13 +772,13 @@ describe("Smart clientPk and isClientRecord logic", () => {
650
772
  const smartSchema = schema({
651
773
  _tableName: "smart_table",
652
774
  // 1. Auto-detect function execution
653
- id: s.sqlite({ type: "int", pk: true }).clientInput({
775
+ id: s.sqlite({ type: "int", pk: true }).client({
654
776
  value: ({ uuid }) => uuid(),
655
777
  schema: z.string(),
656
778
  clientPk: true, // Should auto-detect by dummy-executing the uuid factory
657
779
  }),
658
780
  // 2. Custom function + mapped db key
659
- mappedId: s.sqlite({ type: "int", field: "db_mapped_id" }).clientInput({
781
+ mappedId: s.sqlite({ type: "int", field: "db_mapped_id" }).client({
660
782
  value: "temp_999",
661
783
  schema: z.string(),
662
784
  clientPk: (val) => typeof val === "string" && val.startsWith("temp_"),
@@ -693,17 +815,17 @@ describe("Nested relations with transforms", () => {
693
815
  _tableName: "users",
694
816
  id: s
695
817
  .sqlite({ type: "int", pk: true })
696
- .clientInput({ value: () => "user-123", schema: z.string() }),
697
- name: s.sqlite({ type: "varchar" }).clientInput({ value: "John" }),
818
+ .client({ value: () => "user-123", schema: z.string() }),
819
+ name: s.sqlite({ type: "varchar" }).client({ value: "John" }),
698
820
  posts: s.hasMany({ count: 1 }),
699
821
  });
700
822
  const posts = schema({
701
823
  _tableName: "posts",
702
824
  id: s.sqlite({ type: "int", pk: true }),
703
- title: s.sqlite({ type: "varchar" }).clientInput({ value: "Default Post" }),
825
+ title: s.sqlite({ type: "varchar" }).client({ value: "Default Post" }),
704
826
  isPublished: s
705
827
  .sqlite({ type: "int" })
706
- .clientInput(() => z.boolean())
828
+ .client(() => z.boolean())
707
829
  .transform({
708
830
  toClient: (val) => val === 1,
709
831
  toDb: (val) => (val ? 1 : 0),
@@ -714,10 +836,10 @@ describe("Nested relations with transforms", () => {
714
836
  const comments = schema({
715
837
  _tableName: "comments",
716
838
  id: s.sqlite({ type: "int", pk: true }),
717
- text: s.sqlite({ type: "varchar" }).clientInput({ value: "Comment" }),
839
+ text: s.sqlite({ type: "varchar" }).client({ value: "Comment" }),
718
840
  isDeleted: s
719
841
  .sqlite({ type: "int" })
720
- .clientInput(() => z.boolean())
842
+ .client(() => z.boolean())
721
843
  .transform({
722
844
  toClient: (val) => val === 1,
723
845
  toDb: (val) => (val ? 1 : 0),
@@ -843,17 +965,17 @@ describe("Nested relations with transforms", () => {
843
965
  describe("sqlOnly fields", () => {
844
966
  const users = schema({
845
967
  _tableName: "users",
846
- id: s.sqlite({ type: "int", pk: true }).clientInput({
968
+ id: s.sqlite({ type: "int", pk: true }).client({
847
969
  value: () => "user-123",
848
970
  schema: z.string(),
849
971
  }),
850
- name: s.sqlite({ type: "varchar" }).clientInput({ value: "John" }),
972
+ name: s.sqlite({ type: "varchar" }).client({ value: "John" }),
851
973
  internalToken: s.sqlite({ type: "varchar", sqlOnly: true }),
852
974
  });
853
975
  const box = createSchemaBox({ users }, { users: {} });
854
976
  it("should exclude sqlOnly fields from client schema", () => {
855
977
  expectTypeOf().not.toHaveProperty("internalToken");
856
- const clientKeys = Object.keys(box.users.schemas.client.shape);
978
+ const clientKeys = Object.keys(box.users.schemas.clientChecked.shape);
857
979
  expect(clientKeys).not.toContain("internalToken");
858
980
  });
859
981
  it("should include sqlOnly fields in sql schema", () => {
@@ -873,7 +995,7 @@ describe("sqlOnly fields", () => {
873
995
  });
874
996
  it("should NOT include sqlOnly fields in toDb output", () => {
875
997
  const { toDb } = box.users.transforms;
876
- const clientData = { id: 1, name: "John" };
998
+ const clientData = { id: "1", name: "John" };
877
999
  const result = toDb(clientData);
878
1000
  expect(result).not.toHaveProperty("internalToken");
879
1001
  });
@@ -896,7 +1018,7 @@ describe("SQL enum fields", () => {
896
1018
  const box = createSchemaBox({ posts }, { posts: {} });
897
1019
  it("should validate enum values in sql, client, and server schemas", () => {
898
1020
  expect(box.posts.schemas.sql.shape.status.parse("published")).toBe("published");
899
- expect(box.posts.schemas.client.shape.status.parse("archived")).toBe("archived");
1021
+ expect(box.posts.schemas.clientChecked.shape.status.parse("archived")).toBe("archived");
900
1022
  expect(box.posts.schemas.server.shape.status.parse("draft")).toBe("draft");
901
1023
  expect(() => box.posts.schemas.server.shape.status.parse("deleted")).toThrow();
902
1024
  });
@@ -916,9 +1038,9 @@ describe("derive - computed fields", () => {
916
1038
  const users = schema({
917
1039
  _tableName: "users",
918
1040
  id: s.sqlite({ type: "int", pk: true }),
919
- fullName: s.clientInput(""),
920
- firstName: s.sqlite({ type: "varchar" }).clientInput({ value: "John" }),
921
- lastName: s.sqlite({ type: "varchar" }).clientInput({ value: "Doe" }),
1041
+ fullName: s.client(""),
1042
+ firstName: s.sqlite({ type: "varchar" }).client({ value: "John" }),
1043
+ lastName: s.sqlite({ type: "varchar" }).client({ value: "Doe" }),
922
1044
  }).derive({
923
1045
  forClient: {
924
1046
  fullName: (row) => `${row.firstName} ${row.lastName}`,
@@ -946,10 +1068,10 @@ describe("derive - computed fields", () => {
946
1068
  const products = schema({
947
1069
  _tableName: "products",
948
1070
  id: s.sqlite({ type: "int", pk: true }),
949
- price: s.sqlite({ type: "int" }).clientInput({ value: 100 }),
950
- quantity: s.sqlite({ type: "int" }).clientInput({ value: 5 }),
951
- total: s.clientInput(0),
952
- formattedPrice: s.clientInput(""),
1071
+ price: s.sqlite({ type: "int" }).client({ value: 100 }),
1072
+ quantity: s.sqlite({ type: "int" }).client({ value: 5 }),
1073
+ total: s.client(0),
1074
+ formattedPrice: s.client(""),
953
1075
  }).derive({
954
1076
  forClient: {
955
1077
  total: (row) => row.price * row.quantity,
@@ -965,8 +1087,8 @@ describe("derive - computed fields", () => {
965
1087
  const contacts = schema({
966
1088
  _tableName: "contacts",
967
1089
  id: s.sqlite({ type: "int", pk: true }),
968
- firstName: s.sqlite({ type: "varchar" }).clientInput({ value: "John" }),
969
- lastName: s.sqlite({ type: "varchar" }).clientInput({ value: "Doe" }),
1090
+ firstName: s.sqlite({ type: "varchar" }).client({ value: "John" }),
1091
+ lastName: s.sqlite({ type: "varchar" }).client({ value: "Doe" }),
970
1092
  searchName: s.sqlite({ type: "varchar", sqlOnly: true }),
971
1093
  }).derive({
972
1094
  forDb: {
@@ -988,9 +1110,9 @@ describe("derive - computed fields", () => {
988
1110
  const contacts = schema({
989
1111
  _tableName: "contacts",
990
1112
  id: s.sqlite({ type: "int", pk: true }),
991
- firstName: s.sqlite({ type: "varchar" }).clientInput({ value: "John" }),
992
- lastName: s.sqlite({ type: "varchar" }).clientInput({ value: "Doe" }),
993
- fullName: s.sqlite({ type: "varchar" }).clientInput({ value: "" }),
1113
+ firstName: s.sqlite({ type: "varchar" }).client({ value: "John" }),
1114
+ lastName: s.sqlite({ type: "varchar" }).client({ value: "Doe" }),
1115
+ fullName: s.sqlite({ type: "varchar" }).client({ value: "" }),
994
1116
  }).derive({
995
1117
  forDb: {
996
1118
  fullName: (row) => `${row.firstName} ${row.lastName}`.trim(),
@@ -1010,13 +1132,13 @@ describe("refine", () => {
1010
1132
  const events = schema({
1011
1133
  _tableName: "events",
1012
1134
  id: s.sqlite({ type: "int", pk: true }),
1013
- startDate: s.sqlite({ type: "varchar" }).clientInput({ value: "" }),
1014
- endDate: s.sqlite({ type: "varchar" }).clientInput({ value: "" }),
1015
- content: s.sqlite({ type: "varchar", nullable: true }).clientInput({
1135
+ startDate: s.sqlite({ type: "varchar" }).client({ value: "" }),
1136
+ endDate: s.sqlite({ type: "varchar" }).client({ value: "" }),
1137
+ content: s.sqlite({ type: "varchar", nullable: true }).client({
1016
1138
  value: null,
1017
1139
  schema: z.string().nullable(),
1018
1140
  }),
1019
- isPublished: s.sqlite({ type: "boolean" }).clientInput({ value: false }),
1141
+ isPublished: s.sqlite({ type: "boolean" }).client({ value: false }),
1020
1142
  }).refine((r) => [
1021
1143
  r("server", (row) => {
1022
1144
  const errors = [];
@@ -1068,10 +1190,10 @@ describe("refine", () => {
1068
1190
  const forms = schema({
1069
1191
  _tableName: "forms",
1070
1192
  id: s.sqlite({ type: "int", pk: true }),
1071
- password: s.sqlite({ type: "varchar" }).clientInput({ value: "" }),
1072
- confirmPassword: s.sqlite({ type: "varchar" }).clientInput({ value: "" }),
1193
+ password: s.sqlite({ type: "varchar" }).client({ value: "" }),
1194
+ confirmPassword: s.sqlite({ type: "varchar" }).client({ value: "" }),
1073
1195
  }).refine((r) => [
1074
- r(["clientInput", "client"], (row) => {
1196
+ r(["client", "clientCheck"], (row) => {
1075
1197
  if (row.password !== row.confirmPassword) {
1076
1198
  return {
1077
1199
  path: ["confirmPassword"],
@@ -1082,7 +1204,7 @@ describe("refine", () => {
1082
1204
  }),
1083
1205
  ]);
1084
1206
  const box = createSchemaBox({ forms }, { forms: {} });
1085
- const result = box.forms.schemas.clientInput.safeParse({
1207
+ const result = box.forms.schemas.client.safeParse({
1086
1208
  id: 1,
1087
1209
  password: "secret",
1088
1210
  confirmPassword: "different",
@@ -1091,7 +1213,7 @@ describe("refine", () => {
1091
1213
  if (!result.success) {
1092
1214
  expect(result.error.issues[0].message).toBe("Passwords must match");
1093
1215
  }
1094
- const validResult = box.forms.schemas.clientInput.safeParse({
1216
+ const validResult = box.forms.schemas.client.safeParse({
1095
1217
  id: 1,
1096
1218
  password: "secret",
1097
1219
  confirmPassword: "secret",
@@ -1102,8 +1224,8 @@ describe("refine", () => {
1102
1224
  const items = schema({
1103
1225
  _tableName: "items",
1104
1226
  id: s.sqlite({ type: "int", pk: true }),
1105
- min: s.sqlite({ type: "int" }).clientInput({ value: 0 }),
1106
- max: s.sqlite({ type: "int" }).clientInput({ value: 100 }),
1227
+ min: s.sqlite({ type: "int" }).client({ value: 0 }),
1228
+ max: s.sqlite({ type: "int" }).client({ value: 100 }),
1107
1229
  }).refine((r) => [
1108
1230
  r("server", (row) => {
1109
1231
  if (row.min > row.max) {
@@ -1122,8 +1244,8 @@ describe("refine", () => {
1122
1244
  const records = schema({
1123
1245
  _tableName: "records",
1124
1246
  id: s.sqlite({ type: "int", pk: true }),
1125
- firstName: s.sqlite({ type: "varchar" }).clientInput({ value: "" }),
1126
- lastName: s.sqlite({ type: "varchar" }).clientInput({ value: "" }),
1247
+ firstName: s.sqlite({ type: "varchar" }).client({ value: "" }),
1248
+ lastName: s.sqlite({ type: "varchar" }).client({ value: "" }),
1127
1249
  fullName: s.sqlite({ type: "varchar", sqlOnly: true }),
1128
1250
  })
1129
1251
  .derive({
@@ -1159,8 +1281,8 @@ describe("client-only fields", () => {
1159
1281
  const tasks = schema({
1160
1282
  _tableName: "tasks",
1161
1283
  id: s.sqlite({ type: "int", pk: true }),
1162
- title: s.sqlite({ type: "varchar" }).clientInput({ value: "" }),
1163
- statusLabel: s.clientInput(""),
1284
+ title: s.sqlite({ type: "varchar" }).client({ value: "" }),
1285
+ statusLabel: s.client(""),
1164
1286
  });
1165
1287
  const taskBox = createSchemaBox({ tasks }, { tasks: {} });
1166
1288
  expect(taskBox.tasks.transforms.parseForDb({
@@ -1176,7 +1298,7 @@ describe("client-only fields", () => {
1176
1298
  describe("sqlOnly with derive in relations", () => {
1177
1299
  const users = schema({
1178
1300
  _tableName: "users",
1179
- id: s.sqlite({ type: "int", pk: true }).clientInput({
1301
+ id: s.sqlite({ type: "int", pk: true }).client({
1180
1302
  value: () => "user-123",
1181
1303
  schema: z.string(),
1182
1304
  }),
@@ -1186,9 +1308,9 @@ describe("sqlOnly with derive in relations", () => {
1186
1308
  const posts = schema({
1187
1309
  _tableName: "posts",
1188
1310
  id: s.sqlite({ type: "int", pk: true }),
1189
- title: s.sqlite({ type: "varchar" }).clientInput({ value: "Post" }),
1311
+ title: s.sqlite({ type: "varchar" }).client({ value: "Post" }),
1190
1312
  authorId: s.reference(() => users.id),
1191
- preview: s.clientInput(""),
1313
+ preview: s.client(""),
1192
1314
  }).derive({
1193
1315
  forClient: {
1194
1316
  preview: (row) => row.title.substring(0, 5),
@@ -1199,7 +1321,7 @@ describe("sqlOnly with derive in relations", () => {
1199
1321
  });
1200
1322
  it("should exclude sqlOnly fields from view but include derived", () => {
1201
1323
  const view = box.users.createView({ posts: true });
1202
- const viewClientKeys = Object.keys(view.schemas.client.shape);
1324
+ const viewClientKeys = Object.keys(view.schemas.clientChecked.shape);
1203
1325
  expect(viewClientKeys).not.toContain("internalScore");
1204
1326
  expect(viewClientKeys).toContain("id");
1205
1327
  expect(viewClientKeys).toContain("posts");
@@ -1227,15 +1349,15 @@ describe("defaultsDefinition", () => {
1227
1349
  _tableName: "users",
1228
1350
  id: s
1229
1351
  .sqlite({ type: "int", pk: true })
1230
- .clientInput({ value: "user-123", schema: z.string() }),
1231
- name: s.sqlite({ type: "varchar" }).clientInput({ value: "John" }),
1352
+ .client({ value: "user-123", schema: z.string() }),
1353
+ name: s.sqlite({ type: "varchar" }).client({ value: "John" }),
1232
1354
  posts: s.hasMany({ count: 2 }),
1233
1355
  profile: s.hasOne(true),
1234
1356
  });
1235
1357
  const posts = schema({
1236
1358
  _tableName: "posts",
1237
1359
  id: s.sqlite({ type: "int", pk: true }),
1238
- title: s.sqlite({ type: "varchar" }).clientInput({ value: "Default Post" }),
1360
+ title: s.sqlite({ type: "varchar" }).client({ value: "Default Post" }),
1239
1361
  authorId: s.reference(() => users.id),
1240
1362
  user: s.hasOne(true),
1241
1363
  });
@@ -1279,19 +1401,19 @@ describe("defaultsDefinition", () => {
1279
1401
  describe("dynamic value functions re-run on each view.defaults() call", () => {
1280
1402
  const factory = schema({
1281
1403
  _tableName: "factories",
1282
- id: s.sqlite({ type: "int", pk: true }).clientInput({
1404
+ id: s.sqlite({ type: "int", pk: true }).client({
1283
1405
  value: () => `temp_${Math.random().toString(36).substr(2, 8)}`,
1284
1406
  schema: z.string(),
1285
1407
  clientPk: true,
1286
1408
  }),
1287
1409
  name: s
1288
1410
  .sqlite({ type: "varchar", length: 100 })
1289
- .clientInput({ value: "MyFactory" }),
1411
+ .client({ value: "MyFactory" }),
1290
1412
  boxes: s.hasMany({ count: 2 }),
1291
1413
  });
1292
1414
  const box = schema({
1293
1415
  _tableName: "boxes",
1294
- id: s.sqlite({ type: "int", pk: true }).clientInput({
1416
+ id: s.sqlite({ type: "int", pk: true }).client({
1295
1417
  value: () => `box_${Math.random().toString(36).substr(2, 8)}`,
1296
1418
  schema: z.string(),
1297
1419
  clientPk: true,
@@ -1301,7 +1423,7 @@ describe("dynamic value functions re-run on each view.defaults() call", () => {
1301
1423
  });
1302
1424
  const boxVariant = schema({
1303
1425
  _tableName: "box_variants",
1304
- id: s.sqlite({ type: "int", pk: true }).clientInput({
1426
+ id: s.sqlite({ type: "int", pk: true }).client({
1305
1427
  value: () => `var_${Math.random().toString(36).substr(2, 8)}`,
1306
1428
  schema: z.string(),
1307
1429
  clientPk: true,
@@ -1309,7 +1431,7 @@ describe("dynamic value functions re-run on each view.defaults() call", () => {
1309
1431
  boxId: s.reference(() => box.id),
1310
1432
  label: s
1311
1433
  .sqlite({ type: "varchar", length: 50 })
1312
- .clientInput({ value: "Standard" }),
1434
+ .client({ value: "Standard" }),
1313
1435
  });
1314
1436
  const box_ = createSchemaBox({ factory, box, boxVariant }, {
1315
1437
  factory: {