cogsbox-shape 0.5.159 → 0.5.161

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/schema.d.ts CHANGED
@@ -213,6 +213,7 @@ export declare function createSchema<T extends {
213
213
  }, R extends Record<string, any> = {}, TActualSchema extends Omit<T & R, typeof SchemaWrapperBrand> = Omit<T & R, typeof SchemaWrapperBrand>>(schema: T, relations?: R): {
214
214
  pk: string[] | null;
215
215
  clientPk: string[] | null;
216
+ isClientRecord: ((record: any) => boolean) | undefined;
216
217
  sqlSchema: z.ZodObject<Prettify<DeriveSchemaByKey<TActualSchema, "zodSqlSchema">>>;
217
218
  clientSchema: z.ZodObject<Prettify<DeriveSchemaByKey<TActualSchema, "zodClientSchema">>>;
218
219
  validationSchema: z.ZodObject<Prettify<DeriveSchemaByKey<TActualSchema, "zodValidationSchema">>>;
package/dist/schema.js CHANGED
@@ -171,16 +171,25 @@ function createBuilder(config) {
171
171
  }
172
172
  const newCompletedStages = new Set(completedStages);
173
173
  newCompletedStages.add("new");
174
- // Create union of the SQL type and the new client type
175
- const hasProvidedSchema = !!schemaOrModifier;
176
- const clientAndServerSchema = hasProvidedSchema
177
- ? finalSchema
178
- : z.union([config.sqlZod, finalSchema]);
179
174
  const newConfig = { ...config.sqlConfig };
180
175
  if (clientPk) {
181
- // Add our metadata flag to the config
182
176
  newConfig.isClientPk = true;
183
177
  }
178
+ // When clientPk is true, ALWAYS union the SQL type with the client type
179
+ // because records can be either DB-sourced (number) or client-created (string)
180
+ let clientAndServerSchema;
181
+ if (clientPk) {
182
+ // Always union for clientPk fields
183
+ clientAndServerSchema = z.union([config.sqlZod, finalSchema]);
184
+ }
185
+ else if (schemaOrModifier) {
186
+ // Schema provided without clientPk — use as-is
187
+ clientAndServerSchema = finalSchema;
188
+ }
189
+ else {
190
+ // No schema provided — union with SQL type
191
+ clientAndServerSchema = z.union([config.sqlZod, finalSchema]);
192
+ }
184
193
  return createBuilder({
185
194
  ...config,
186
195
  stage: "new",
@@ -188,7 +197,7 @@ function createBuilder(config) {
188
197
  newZod: finalSchema,
189
198
  initialValue: actualValue,
190
199
  clientZod: clientAndServerSchema,
191
- validationZod: clientAndServerSchema, // Our internal name
200
+ validationZod: clientAndServerSchema,
192
201
  completedStages: newCompletedStages,
193
202
  });
194
203
  },
@@ -460,6 +469,7 @@ export function createSchema(schema, relations) {
460
469
  const fullSchema = { ...schema, ...(relations || {}) };
461
470
  let pkKeys = [];
462
471
  let clientPkKeys = [];
472
+ // FIRST PASS: Collect all fields and PKs
463
473
  for (const key in fullSchema) {
464
474
  const value = fullSchema[key];
465
475
  if (key === "_tableName" ||
@@ -470,7 +480,6 @@ export function createSchema(schema, relations) {
470
480
  typeof value === "function")
471
481
  continue;
472
482
  const definition = fullSchema[key];
473
- // Handle new-style references
474
483
  if (isReference(definition)) {
475
484
  const targetField = definition.getter();
476
485
  if (targetField && targetField.config) {
@@ -488,27 +497,18 @@ export function createSchema(schema, relations) {
488
497
  fieldTransforms[key] = config.transforms;
489
498
  }
490
499
  }
491
- continue; // Skip the rest of the logic for references
500
+ continue;
492
501
  }
493
- // THEN, handle all other fields that have a config (builders, relations, etc.)
494
502
  if (definition && definition.config) {
495
503
  const config = definition.config;
496
- if (config.sql?.pk && !config.sql?.isForeignKey) {
497
- pkKeys.push(key);
498
- }
499
- if (config.sql?.isClientPk) {
500
- clientPkKeys.push(key);
501
- }
502
- // The rest of the logic for builders
504
+ // ... pk collection logic ...
503
505
  const sqlConfig = config.sql;
504
506
  if (sqlConfig &&
505
507
  typeof sqlConfig === "object" &&
506
508
  ["hasMany", "hasOne", "belongsTo", "manyToMany"].includes(sqlConfig.type)) {
507
- // This is for relations, which also aren't PKs, so we just continue.
508
509
  continue;
509
510
  }
510
511
  else {
511
- // This is for regular s.sql() fields
512
512
  sqlFields[key] = config.zodSqlSchema;
513
513
  clientFields[key] = config.zodClientSchema;
514
514
  serverFields[key] = config.zodValidationSchema;
@@ -517,19 +517,56 @@ export function createSchema(schema, relations) {
517
517
  }
518
518
  const initialValueOrFn = config.initialValue;
519
519
  defaultGenerators[key] = initialValueOrFn;
520
- defaultValues[key] = isFunction(initialValueOrFn)
520
+ // Get the raw default value
521
+ let rawDefault = isFunction(initialValueOrFn)
521
522
  ? initialValueOrFn()
522
523
  : initialValueOrFn;
524
+ // Apply toClient transform if it exists
525
+ if (config.transforms?.toClient && rawDefault !== undefined) {
526
+ defaultValues[key] = config.transforms.toClient(rawDefault);
527
+ }
528
+ else {
529
+ defaultValues[key] = rawDefault;
530
+ }
523
531
  }
524
532
  }
525
533
  }
534
+ // AFTER THE LOOP: Build isClientRecord checker
535
+ let isClientRecord;
536
+ const explicitChecker = fullSchema.__isClientChecker;
537
+ if (explicitChecker) {
538
+ isClientRecord = explicitChecker;
539
+ }
540
+ else if (clientPkKeys.length > 0) {
541
+ const autoChecks = [];
542
+ for (const key of clientPkKeys) {
543
+ const field = fullSchema[key];
544
+ const sqlType = field?.config?.sql?.type;
545
+ const initialValue = field?.config?.initialValue;
546
+ const dbIsNumeric = sqlType === "int";
547
+ const clientIsString = typeof initialValue === "string";
548
+ if (dbIsNumeric && clientIsString) {
549
+ autoChecks.push({ key, check: (val) => typeof val === "string" });
550
+ }
551
+ }
552
+ if (autoChecks.length > 0) {
553
+ isClientRecord = (record) => autoChecks.some(({ key, check }) => check(record[key]));
554
+ }
555
+ }
526
556
  const generateDefaults = () => {
527
557
  const freshDefaults = {};
528
558
  for (const key in defaultGenerators) {
529
559
  const generatorOrValue = defaultGenerators[key];
530
- freshDefaults[key] = isFunction(generatorOrValue)
531
- ? generatorOrValue() // Call the function to get a fresh value
532
- : generatorOrValue; // Use the static value
560
+ let rawValue = isFunction(generatorOrValue)
561
+ ? generatorOrValue()
562
+ : generatorOrValue;
563
+ // Apply toClient transform if it exists
564
+ if (fieldTransforms[key]?.toClient && rawValue !== undefined) {
565
+ freshDefaults[key] = fieldTransforms[key].toClient(rawValue);
566
+ }
567
+ else {
568
+ freshDefaults[key] = rawValue;
569
+ }
533
570
  }
534
571
  return freshDefaults;
535
572
  };
@@ -552,8 +589,9 @@ export function createSchema(schema, relations) {
552
589
  return dbObject;
553
590
  };
554
591
  return {
555
- pk: pkKeys?.length ? pkKeys : null,
556
- clientPk: clientPkKeys ? clientPkKeys : null,
592
+ pk: pkKeys.length ? pkKeys : null,
593
+ clientPk: clientPkKeys.length ? clientPkKeys : null,
594
+ isClientRecord, // NOW IT'S IN SCOPE
557
595
  sqlSchema: z.object(sqlFields),
558
596
  clientSchema: z.object(clientFields),
559
597
  validationSchema: z.object(serverFields),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cogsbox-shape",
3
- "version": "0.5.159",
3
+ "version": "0.5.161",
4
4
  "description": "A TypeScript library for creating type-safe database schemas with Zod validation, SQL type definitions, and automatic client/server transformations. Unifies client, server, and database types through a single schema definition, with built-in support for relationships and serialization.",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",