postgresdk 0.16.2 → 0.16.4

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.
Files changed (3) hide show
  1. package/dist/cli.js +148 -86
  2. package/dist/index.js +148 -86
  3. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1035,7 +1035,7 @@ function postgresTypeToTsType(column, enums) {
1035
1035
  return "string";
1036
1036
  case "json":
1037
1037
  case "jsonb":
1038
- return "Record<string, any>";
1038
+ return "JsonValue";
1039
1039
  case "uuid":
1040
1040
  return "string";
1041
1041
  case "text[]":
@@ -3175,10 +3175,15 @@ function isVectorType2(pgType) {
3175
3175
  const t = pgType.toLowerCase();
3176
3176
  return t === "vector" || t === "halfvec" || t === "sparsevec" || t === "bit";
3177
3177
  }
3178
+ function isJsonbType(pgType) {
3179
+ const t = pgType.toLowerCase();
3180
+ return t === "json" || t === "jsonb";
3181
+ }
3178
3182
  function emitClient(table, graph, opts, model) {
3179
3183
  const Type = pascal(table.name);
3180
3184
  const ext = opts.useJsExtensions ? ".js" : "";
3181
3185
  const hasVectorColumns = table.columns.some((c) => isVectorType2(c.pgType));
3186
+ const hasJsonbColumns = table.columns.some((c) => isJsonbType(c.pgType));
3182
3187
  if (process.env.SDK_DEBUG) {
3183
3188
  const vectorCols = table.columns.filter((c) => isVectorType2(c.pgType));
3184
3189
  if (vectorCols.length > 0) {
@@ -3264,60 +3269,58 @@ ${typeImports}
3264
3269
  ${otherTableImports.join(`
3265
3270
  `)}
3266
3271
 
3267
- /**
3268
- * Helper type to merge JSONB type overrides into base types
3269
- * @example
3270
- * type UserWithMetadata = MergeJsonb<SelectUser, { metadata: { tags: string[] } }>;
3271
- */
3272
- type MergeJsonb<TBase, TJsonb> = Omit<TBase, keyof TJsonb> & TJsonb;
3273
-
3274
3272
  /**
3275
3273
  * Client for ${table.name} table operations
3276
3274
  */
3277
3275
  export class ${Type}Client extends BaseClient {
3278
3276
  private readonly resource = "/v1/${table.name}";
3279
3277
 
3280
- /**
3278
+ ${hasJsonbColumns ? ` /**
3281
3279
  * Create a new ${table.name} record
3282
3280
  * @param data - The data to insert
3283
3281
  * @returns The created record
3284
- */
3285
- async create(data: Insert${Type}): Promise<Select${Type}>;
3286
- /**
3287
- * Create a new ${table.name} record with JSONB type overrides
3288
- * @param data - The data to insert
3289
- * @returns The created record with typed JSONB fields
3290
3282
  * @example
3283
+ * // With JSONB type override:
3291
3284
  * type Metadata = { tags: string[]; prefs: { theme: 'light' | 'dark' } };
3292
3285
  * const user = await client.create<{ metadata: Metadata }>({ name: 'Alice', metadata: { tags: [], prefs: { theme: 'light' } } });
3293
3286
  */
3294
- async create<TJsonb extends Partial<Select${Type}>>(
3295
- data: MergeJsonb<Insert${Type}, TJsonb>
3296
- ): Promise<MergeJsonb<Select${Type}, TJsonb>>;
3297
- async create(data: any): Promise<any> {
3298
- return this.post<any>(this.resource, data);
3299
- }
3287
+ async create<TJsonb extends Partial<Select${Type}> = {}>(
3288
+ data: Insert${Type}<TJsonb>
3289
+ ): Promise<Select${Type}<TJsonb>> {
3290
+ return this.post<Select${Type}<TJsonb>>(this.resource, data);
3291
+ }` : ` /**
3292
+ * Create a new ${table.name} record
3293
+ * @param data - The data to insert
3294
+ * @returns The created record
3295
+ */
3296
+ async create(data: Insert${Type}): Promise<Select${Type}> {
3297
+ return this.post<Select${Type}>(this.resource, data);
3298
+ }`}
3300
3299
 
3301
- /**
3300
+ ${hasJsonbColumns ? ` /**
3302
3301
  * Get a ${table.name} record by primary key
3303
3302
  * @param pk - The primary key value${hasCompositePk ? "s" : ""}
3304
3303
  * @returns The record if found, null otherwise
3304
+ * @example
3305
+ * // With JSONB type override:
3306
+ * const user = await client.getByPk<{ metadata: Metadata }>('user-id');
3305
3307
  */
3306
- async getByPk(pk: ${pkType}): Promise<Select${Type} | null>;
3307
- /**
3308
- * Get a ${table.name} record by primary key with JSONB type overrides
3308
+ async getByPk<TJsonb extends Partial<Select${Type}> = {}>(
3309
+ pk: ${pkType}
3310
+ ): Promise<Select${Type}<TJsonb> | null> {
3311
+ const path = ${pkPathExpr};
3312
+ return this.get<Select${Type}<TJsonb> | null>(\`\${this.resource}/\${path}\`);
3313
+ }` : ` /**
3314
+ * Get a ${table.name} record by primary key
3309
3315
  * @param pk - The primary key value${hasCompositePk ? "s" : ""}
3310
- * @returns The record with typed JSONB fields if found, null otherwise
3316
+ * @returns The record if found, null otherwise
3311
3317
  */
3312
- async getByPk<TJsonb extends Partial<Select${Type}>>(
3313
- pk: ${pkType}
3314
- ): Promise<MergeJsonb<Select${Type}, TJsonb> | null>;
3315
- async getByPk(pk: ${pkType}): Promise<any> {
3318
+ async getByPk(pk: ${pkType}): Promise<Select${Type} | null> {
3316
3319
  const path = ${pkPathExpr};
3317
- return this.get<any>(\`\${this.resource}/\${path}\`);
3318
- }
3320
+ return this.get<Select${Type} | null>(\`\${this.resource}/\${path}\`);
3321
+ }`}
3319
3322
 
3320
- /**
3323
+ ${hasJsonbColumns ? ` /**
3321
3324
  * List ${table.name} records with pagination and filtering
3322
3325
  * @param params - Query parameters
3323
3326
  * @param params.where - Filter conditions using operators like $eq, $gt, $in, $like, etc.
@@ -3327,45 +3330,41 @@ export class ${Type}Client extends BaseClient {
3327
3330
  * @param params.offset - Number of records to skip for pagination
3328
3331
  * @param params.include - Related records to include (see listWith* methods for typed includes)
3329
3332
  * @returns Paginated results with data, total count, and hasMore flag
3333
+ * @example
3334
+ * // With JSONB type override:
3335
+ * const users = await client.list<{ metadata: Metadata }>({ where: { status: 'active' } });
3330
3336
  */
3331
- async list(params?: {
3332
- include?: any;
3333
- limit?: number;
3334
- offset?: number;
3335
- where?: Where<Select${Type}>;
3336
- orderBy?: string | string[];
3337
- order?: "asc" | "desc" | ("asc" | "desc")[];
3338
- }): Promise<PaginatedResponse<Select${Type}>>;${hasVectorColumns ? `
3339
- /**
3340
- * List ${table.name} records with vector similarity search
3341
- * @param params - Query parameters with vector search enabled
3342
- * @param params.vector - Vector similarity search configuration
3343
- * @returns Paginated results with _distance field included
3344
- */
3345
- async list(params: {
3337
+ async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
3346
3338
  include?: any;
3347
3339
  limit?: number;
3348
3340
  offset?: number;
3349
- where?: Where<Select${Type}>;
3350
- vector: {
3341
+ where?: Where<Select${Type}<TJsonb>>;${hasVectorColumns ? `
3342
+ vector?: {
3351
3343
  field: string;
3352
3344
  query: number[];
3353
3345
  metric?: "cosine" | "l2" | "inner";
3354
3346
  maxDistance?: number;
3355
- };
3347
+ };` : ""}
3356
3348
  orderBy?: string | string[];
3357
3349
  order?: "asc" | "desc" | ("asc" | "desc")[];
3358
- }): Promise<PaginatedResponse<Select${Type} & { _distance: number }>>;` : ""}
3359
- /**
3360
- * List ${table.name} records with pagination and filtering, with JSONB type overrides
3361
- * @param params - Query parameters with typed JSONB fields in where clause
3362
- * @returns Paginated results with typed JSONB fields
3350
+ }): Promise<PaginatedResponse<Select${Type}<TJsonb>${hasVectorColumns ? " & { _distance?: number }" : ""}>> {
3351
+ return this.post<PaginatedResponse<Select${Type}<TJsonb>${hasVectorColumns ? " & { _distance?: number }" : ""}>>(\`\${this.resource}/list\`, params ?? {});
3352
+ }` : ` /**
3353
+ * List ${table.name} records with pagination and filtering
3354
+ * @param params - Query parameters
3355
+ * @param params.where - Filter conditions using operators like $eq, $gt, $in, $like, etc.
3356
+ * @param params.orderBy - Column(s) to sort by
3357
+ * @param params.order - Sort direction(s): "asc" or "desc"
3358
+ * @param params.limit - Maximum number of records to return (default: 50, max: 100)
3359
+ * @param params.offset - Number of records to skip for pagination
3360
+ * @param params.include - Related records to include (see listWith* methods for typed includes)
3361
+ * @returns Paginated results with data, total count, and hasMore flag
3363
3362
  */
3364
- async list<TJsonb extends Partial<Select${Type}>>(params?: {
3363
+ async list(params?: {
3365
3364
  include?: any;
3366
3365
  limit?: number;
3367
3366
  offset?: number;
3368
- where?: Where<MergeJsonb<Select${Type}, TJsonb>>;${hasVectorColumns ? `
3367
+ where?: Where<Select${Type}>;${hasVectorColumns ? `
3369
3368
  vector?: {
3370
3369
  field: string;
3371
3370
  query: number[];
@@ -3374,51 +3373,58 @@ export class ${Type}Client extends BaseClient {
3374
3373
  };` : ""}
3375
3374
  orderBy?: string | string[];
3376
3375
  order?: "asc" | "desc" | ("asc" | "desc")[];
3377
- }): Promise<PaginatedResponse<MergeJsonb<Select${Type}, TJsonb>>>;
3378
- async list(params?: any): Promise<any> {
3379
- return this.post<any>(\`\${this.resource}/list\`, params ?? {});
3380
- }
3376
+ }): Promise<PaginatedResponse<Select${Type}${hasVectorColumns ? " & { _distance?: number }" : ""}>> {
3377
+ return this.post<PaginatedResponse<Select${Type}${hasVectorColumns ? " & { _distance?: number }" : ""}>>(\`\${this.resource}/list\`, params ?? {});
3378
+ }`}
3381
3379
 
3382
- /**
3380
+ ${hasJsonbColumns ? ` /**
3383
3381
  * Update a ${table.name} record by primary key
3384
3382
  * @param pk - The primary key value${hasCompositePk ? "s" : ""}
3385
3383
  * @param patch - Partial data to update
3386
3384
  * @returns The updated record if found, null otherwise
3385
+ * @example
3386
+ * // With JSONB type override:
3387
+ * const user = await client.update<{ metadata: Metadata }>('user-id', { metadata: { tags: ['new'] } });
3387
3388
  */
3388
- async update(pk: ${pkType}, patch: Update${Type}): Promise<Select${Type} | null>;
3389
- /**
3390
- * Update a ${table.name} record by primary key with JSONB type overrides
3389
+ async update<TJsonb extends Partial<Select${Type}> = {}>(
3390
+ pk: ${pkType},
3391
+ patch: Update${Type}<TJsonb>
3392
+ ): Promise<Select${Type}<TJsonb> | null> {
3393
+ const path = ${pkPathExpr};
3394
+ return this.patch<Select${Type}<TJsonb> | null>(\`\${this.resource}/\${path}\`, patch);
3395
+ }` : ` /**
3396
+ * Update a ${table.name} record by primary key
3391
3397
  * @param pk - The primary key value${hasCompositePk ? "s" : ""}
3392
- * @param patch - Partial data to update with typed JSONB fields
3393
- * @returns The updated record with typed JSONB fields if found, null otherwise
3398
+ * @param patch - Partial data to update
3399
+ * @returns The updated record if found, null otherwise
3394
3400
  */
3395
- async update<TJsonb extends Partial<Select${Type}>>(
3396
- pk: ${pkType},
3397
- patch: MergeJsonb<Update${Type}, TJsonb>
3398
- ): Promise<MergeJsonb<Select${Type}, TJsonb> | null>;
3399
- async update(pk: ${pkType}, patch: any): Promise<any> {
3401
+ async update(pk: ${pkType}, patch: Update${Type}): Promise<Select${Type} | null> {
3400
3402
  const path = ${pkPathExpr};
3401
- return this.patch<any>(\`\${this.resource}/\${path}\`, patch);
3402
- }
3403
+ return this.patch<Select${Type} | null>(\`\${this.resource}/\${path}\`, patch);
3404
+ }`}
3403
3405
 
3404
- /**
3406
+ ${hasJsonbColumns ? ` /**
3405
3407
  * Delete a ${table.name} record by primary key
3406
3408
  * @param pk - The primary key value${hasCompositePk ? "s" : ""}
3407
3409
  * @returns The deleted record if found, null otherwise
3410
+ * @example
3411
+ * // With JSONB type override:
3412
+ * const user = await client.delete<{ metadata: Metadata }>('user-id');
3408
3413
  */
3409
- async delete(pk: ${pkType}): Promise<Select${Type} | null>;
3410
- /**
3411
- * Delete a ${table.name} record by primary key with JSONB type overrides
3414
+ async delete<TJsonb extends Partial<Select${Type}> = {}>(
3415
+ pk: ${pkType}
3416
+ ): Promise<Select${Type}<TJsonb> | null> {
3417
+ const path = ${pkPathExpr};
3418
+ return this.del<Select${Type}<TJsonb> | null>(\`\${this.resource}/\${path}\`);
3419
+ }` : ` /**
3420
+ * Delete a ${table.name} record by primary key
3412
3421
  * @param pk - The primary key value${hasCompositePk ? "s" : ""}
3413
- * @returns The deleted record with typed JSONB fields if found, null otherwise
3422
+ * @returns The deleted record if found, null otherwise
3414
3423
  */
3415
- async delete<TJsonb extends Partial<Select${Type}>>(
3416
- pk: ${pkType}
3417
- ): Promise<MergeJsonb<Select${Type}, TJsonb> | null>;
3418
- async delete(pk: ${pkType}): Promise<any> {
3424
+ async delete(pk: ${pkType}): Promise<Select${Type} | null> {
3419
3425
  const path = ${pkPathExpr};
3420
- return this.del<any>(\`\${this.resource}/\${path}\`);
3421
- }
3426
+ return this.del<Select${Type} | null>(\`\${this.resource}/\${path}\`);
3427
+ }`}
3422
3428
  ${includeMethodsCode}}
3423
3429
  `;
3424
3430
  }
@@ -3986,9 +3992,14 @@ function tsTypeFor(pgType, opts, enums) {
3986
3992
  return "unknown";
3987
3993
  return "string";
3988
3994
  }
3995
+ function isJsonbType2(pgType) {
3996
+ const t = pgType.toLowerCase();
3997
+ return t === "json" || t === "jsonb";
3998
+ }
3989
3999
  var pascal2 = (s) => s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
3990
4000
  function emitTypes(table, opts, enums) {
3991
4001
  const Type = pascal2(table.name);
4002
+ const hasJsonbColumns = table.columns.some((col) => isJsonbType2(col.pgType));
3992
4003
  const insertFields = table.columns.map((col) => {
3993
4004
  const base = tsTypeFor(col.pgType, opts, enums);
3994
4005
  const optional = col.hasDefault || col.nullable ? "?" : "";
@@ -4002,6 +4013,57 @@ function emitTypes(table, opts, enums) {
4002
4013
  return ` ${col.name}: ${valueType};`;
4003
4014
  }).join(`
4004
4015
  `);
4016
+ const baseInsertType = `type _Insert${Type}Base = {
4017
+ ${insertFields}
4018
+ };`;
4019
+ const baseSelectType = `type _Select${Type}Base = {
4020
+ ${selectFields}
4021
+ };`;
4022
+ if (hasJsonbColumns) {
4023
+ return `/**
4024
+ * AUTO-GENERATED FILE - DO NOT EDIT
4025
+ *
4026
+ * This file was automatically generated by PostgreSDK.
4027
+ * Any manual changes will be overwritten on the next generation.
4028
+ *
4029
+ * To make changes, modify your schema or configuration and regenerate.
4030
+ */
4031
+
4032
+ ${baseInsertType}
4033
+
4034
+ ${baseSelectType}
4035
+
4036
+ /**
4037
+ * Type for inserting a new ${table.name} record.
4038
+ * Fields with defaults or nullable columns are optional.
4039
+ *
4040
+ * Generic parameter TJsonb allows overriding JSONB column types:
4041
+ * @example Insert${Type}<{ metadata: MyMetadataType }>
4042
+ */
4043
+ export type Insert${Type}<TJsonb extends Partial<_Insert${Type}Base> = {}> =
4044
+ Omit<_Insert${Type}Base, keyof TJsonb> & TJsonb;
4045
+
4046
+ /**
4047
+ * Type for updating an existing ${table.name} record.
4048
+ * All fields are optional, allowing partial updates.
4049
+ *
4050
+ * Generic parameter TJsonb allows overriding JSONB column types:
4051
+ * @example Update${Type}<{ metadata: MyMetadataType }>
4052
+ */
4053
+ export type Update${Type}<TJsonb extends Partial<_Select${Type}Base> = {}> =
4054
+ Partial<Omit<_Insert${Type}Base, keyof TJsonb> & TJsonb>;
4055
+
4056
+ /**
4057
+ * Type representing a ${table.name} record from the database.
4058
+ * All fields are included as returned by SELECT queries.
4059
+ *
4060
+ * Generic parameter TJsonb allows overriding JSONB column types:
4061
+ * @example Select${Type}<{ metadata: MyMetadataType }>
4062
+ */
4063
+ export type Select${Type}<TJsonb extends Partial<_Select${Type}Base> = {}> =
4064
+ Omit<_Select${Type}Base, keyof TJsonb> & TJsonb;
4065
+ `;
4066
+ }
4005
4067
  return `/**
4006
4068
  * AUTO-GENERATED FILE - DO NOT EDIT
4007
4069
  *
package/dist/index.js CHANGED
@@ -1034,7 +1034,7 @@ function postgresTypeToTsType(column, enums) {
1034
1034
  return "string";
1035
1035
  case "json":
1036
1036
  case "jsonb":
1037
- return "Record<string, any>";
1037
+ return "JsonValue";
1038
1038
  case "uuid":
1039
1039
  return "string";
1040
1040
  case "text[]":
@@ -2346,10 +2346,15 @@ function isVectorType2(pgType) {
2346
2346
  const t = pgType.toLowerCase();
2347
2347
  return t === "vector" || t === "halfvec" || t === "sparsevec" || t === "bit";
2348
2348
  }
2349
+ function isJsonbType(pgType) {
2350
+ const t = pgType.toLowerCase();
2351
+ return t === "json" || t === "jsonb";
2352
+ }
2349
2353
  function emitClient(table, graph, opts, model) {
2350
2354
  const Type = pascal(table.name);
2351
2355
  const ext = opts.useJsExtensions ? ".js" : "";
2352
2356
  const hasVectorColumns = table.columns.some((c) => isVectorType2(c.pgType));
2357
+ const hasJsonbColumns = table.columns.some((c) => isJsonbType(c.pgType));
2353
2358
  if (process.env.SDK_DEBUG) {
2354
2359
  const vectorCols = table.columns.filter((c) => isVectorType2(c.pgType));
2355
2360
  if (vectorCols.length > 0) {
@@ -2435,60 +2440,58 @@ ${typeImports}
2435
2440
  ${otherTableImports.join(`
2436
2441
  `)}
2437
2442
 
2438
- /**
2439
- * Helper type to merge JSONB type overrides into base types
2440
- * @example
2441
- * type UserWithMetadata = MergeJsonb<SelectUser, { metadata: { tags: string[] } }>;
2442
- */
2443
- type MergeJsonb<TBase, TJsonb> = Omit<TBase, keyof TJsonb> & TJsonb;
2444
-
2445
2443
  /**
2446
2444
  * Client for ${table.name} table operations
2447
2445
  */
2448
2446
  export class ${Type}Client extends BaseClient {
2449
2447
  private readonly resource = "/v1/${table.name}";
2450
2448
 
2451
- /**
2449
+ ${hasJsonbColumns ? ` /**
2452
2450
  * Create a new ${table.name} record
2453
2451
  * @param data - The data to insert
2454
2452
  * @returns The created record
2455
- */
2456
- async create(data: Insert${Type}): Promise<Select${Type}>;
2457
- /**
2458
- * Create a new ${table.name} record with JSONB type overrides
2459
- * @param data - The data to insert
2460
- * @returns The created record with typed JSONB fields
2461
2453
  * @example
2454
+ * // With JSONB type override:
2462
2455
  * type Metadata = { tags: string[]; prefs: { theme: 'light' | 'dark' } };
2463
2456
  * const user = await client.create<{ metadata: Metadata }>({ name: 'Alice', metadata: { tags: [], prefs: { theme: 'light' } } });
2464
2457
  */
2465
- async create<TJsonb extends Partial<Select${Type}>>(
2466
- data: MergeJsonb<Insert${Type}, TJsonb>
2467
- ): Promise<MergeJsonb<Select${Type}, TJsonb>>;
2468
- async create(data: any): Promise<any> {
2469
- return this.post<any>(this.resource, data);
2470
- }
2458
+ async create<TJsonb extends Partial<Select${Type}> = {}>(
2459
+ data: Insert${Type}<TJsonb>
2460
+ ): Promise<Select${Type}<TJsonb>> {
2461
+ return this.post<Select${Type}<TJsonb>>(this.resource, data);
2462
+ }` : ` /**
2463
+ * Create a new ${table.name} record
2464
+ * @param data - The data to insert
2465
+ * @returns The created record
2466
+ */
2467
+ async create(data: Insert${Type}): Promise<Select${Type}> {
2468
+ return this.post<Select${Type}>(this.resource, data);
2469
+ }`}
2471
2470
 
2472
- /**
2471
+ ${hasJsonbColumns ? ` /**
2473
2472
  * Get a ${table.name} record by primary key
2474
2473
  * @param pk - The primary key value${hasCompositePk ? "s" : ""}
2475
2474
  * @returns The record if found, null otherwise
2475
+ * @example
2476
+ * // With JSONB type override:
2477
+ * const user = await client.getByPk<{ metadata: Metadata }>('user-id');
2476
2478
  */
2477
- async getByPk(pk: ${pkType}): Promise<Select${Type} | null>;
2478
- /**
2479
- * Get a ${table.name} record by primary key with JSONB type overrides
2479
+ async getByPk<TJsonb extends Partial<Select${Type}> = {}>(
2480
+ pk: ${pkType}
2481
+ ): Promise<Select${Type}<TJsonb> | null> {
2482
+ const path = ${pkPathExpr};
2483
+ return this.get<Select${Type}<TJsonb> | null>(\`\${this.resource}/\${path}\`);
2484
+ }` : ` /**
2485
+ * Get a ${table.name} record by primary key
2480
2486
  * @param pk - The primary key value${hasCompositePk ? "s" : ""}
2481
- * @returns The record with typed JSONB fields if found, null otherwise
2487
+ * @returns The record if found, null otherwise
2482
2488
  */
2483
- async getByPk<TJsonb extends Partial<Select${Type}>>(
2484
- pk: ${pkType}
2485
- ): Promise<MergeJsonb<Select${Type}, TJsonb> | null>;
2486
- async getByPk(pk: ${pkType}): Promise<any> {
2489
+ async getByPk(pk: ${pkType}): Promise<Select${Type} | null> {
2487
2490
  const path = ${pkPathExpr};
2488
- return this.get<any>(\`\${this.resource}/\${path}\`);
2489
- }
2491
+ return this.get<Select${Type} | null>(\`\${this.resource}/\${path}\`);
2492
+ }`}
2490
2493
 
2491
- /**
2494
+ ${hasJsonbColumns ? ` /**
2492
2495
  * List ${table.name} records with pagination and filtering
2493
2496
  * @param params - Query parameters
2494
2497
  * @param params.where - Filter conditions using operators like $eq, $gt, $in, $like, etc.
@@ -2498,45 +2501,41 @@ export class ${Type}Client extends BaseClient {
2498
2501
  * @param params.offset - Number of records to skip for pagination
2499
2502
  * @param params.include - Related records to include (see listWith* methods for typed includes)
2500
2503
  * @returns Paginated results with data, total count, and hasMore flag
2504
+ * @example
2505
+ * // With JSONB type override:
2506
+ * const users = await client.list<{ metadata: Metadata }>({ where: { status: 'active' } });
2501
2507
  */
2502
- async list(params?: {
2503
- include?: any;
2504
- limit?: number;
2505
- offset?: number;
2506
- where?: Where<Select${Type}>;
2507
- orderBy?: string | string[];
2508
- order?: "asc" | "desc" | ("asc" | "desc")[];
2509
- }): Promise<PaginatedResponse<Select${Type}>>;${hasVectorColumns ? `
2510
- /**
2511
- * List ${table.name} records with vector similarity search
2512
- * @param params - Query parameters with vector search enabled
2513
- * @param params.vector - Vector similarity search configuration
2514
- * @returns Paginated results with _distance field included
2515
- */
2516
- async list(params: {
2508
+ async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
2517
2509
  include?: any;
2518
2510
  limit?: number;
2519
2511
  offset?: number;
2520
- where?: Where<Select${Type}>;
2521
- vector: {
2512
+ where?: Where<Select${Type}<TJsonb>>;${hasVectorColumns ? `
2513
+ vector?: {
2522
2514
  field: string;
2523
2515
  query: number[];
2524
2516
  metric?: "cosine" | "l2" | "inner";
2525
2517
  maxDistance?: number;
2526
- };
2518
+ };` : ""}
2527
2519
  orderBy?: string | string[];
2528
2520
  order?: "asc" | "desc" | ("asc" | "desc")[];
2529
- }): Promise<PaginatedResponse<Select${Type} & { _distance: number }>>;` : ""}
2530
- /**
2531
- * List ${table.name} records with pagination and filtering, with JSONB type overrides
2532
- * @param params - Query parameters with typed JSONB fields in where clause
2533
- * @returns Paginated results with typed JSONB fields
2521
+ }): Promise<PaginatedResponse<Select${Type}<TJsonb>${hasVectorColumns ? " & { _distance?: number }" : ""}>> {
2522
+ return this.post<PaginatedResponse<Select${Type}<TJsonb>${hasVectorColumns ? " & { _distance?: number }" : ""}>>(\`\${this.resource}/list\`, params ?? {});
2523
+ }` : ` /**
2524
+ * List ${table.name} records with pagination and filtering
2525
+ * @param params - Query parameters
2526
+ * @param params.where - Filter conditions using operators like $eq, $gt, $in, $like, etc.
2527
+ * @param params.orderBy - Column(s) to sort by
2528
+ * @param params.order - Sort direction(s): "asc" or "desc"
2529
+ * @param params.limit - Maximum number of records to return (default: 50, max: 100)
2530
+ * @param params.offset - Number of records to skip for pagination
2531
+ * @param params.include - Related records to include (see listWith* methods for typed includes)
2532
+ * @returns Paginated results with data, total count, and hasMore flag
2534
2533
  */
2535
- async list<TJsonb extends Partial<Select${Type}>>(params?: {
2534
+ async list(params?: {
2536
2535
  include?: any;
2537
2536
  limit?: number;
2538
2537
  offset?: number;
2539
- where?: Where<MergeJsonb<Select${Type}, TJsonb>>;${hasVectorColumns ? `
2538
+ where?: Where<Select${Type}>;${hasVectorColumns ? `
2540
2539
  vector?: {
2541
2540
  field: string;
2542
2541
  query: number[];
@@ -2545,51 +2544,58 @@ export class ${Type}Client extends BaseClient {
2545
2544
  };` : ""}
2546
2545
  orderBy?: string | string[];
2547
2546
  order?: "asc" | "desc" | ("asc" | "desc")[];
2548
- }): Promise<PaginatedResponse<MergeJsonb<Select${Type}, TJsonb>>>;
2549
- async list(params?: any): Promise<any> {
2550
- return this.post<any>(\`\${this.resource}/list\`, params ?? {});
2551
- }
2547
+ }): Promise<PaginatedResponse<Select${Type}${hasVectorColumns ? " & { _distance?: number }" : ""}>> {
2548
+ return this.post<PaginatedResponse<Select${Type}${hasVectorColumns ? " & { _distance?: number }" : ""}>>(\`\${this.resource}/list\`, params ?? {});
2549
+ }`}
2552
2550
 
2553
- /**
2551
+ ${hasJsonbColumns ? ` /**
2554
2552
  * Update a ${table.name} record by primary key
2555
2553
  * @param pk - The primary key value${hasCompositePk ? "s" : ""}
2556
2554
  * @param patch - Partial data to update
2557
2555
  * @returns The updated record if found, null otherwise
2556
+ * @example
2557
+ * // With JSONB type override:
2558
+ * const user = await client.update<{ metadata: Metadata }>('user-id', { metadata: { tags: ['new'] } });
2558
2559
  */
2559
- async update(pk: ${pkType}, patch: Update${Type}): Promise<Select${Type} | null>;
2560
- /**
2561
- * Update a ${table.name} record by primary key with JSONB type overrides
2560
+ async update<TJsonb extends Partial<Select${Type}> = {}>(
2561
+ pk: ${pkType},
2562
+ patch: Update${Type}<TJsonb>
2563
+ ): Promise<Select${Type}<TJsonb> | null> {
2564
+ const path = ${pkPathExpr};
2565
+ return this.patch<Select${Type}<TJsonb> | null>(\`\${this.resource}/\${path}\`, patch);
2566
+ }` : ` /**
2567
+ * Update a ${table.name} record by primary key
2562
2568
  * @param pk - The primary key value${hasCompositePk ? "s" : ""}
2563
- * @param patch - Partial data to update with typed JSONB fields
2564
- * @returns The updated record with typed JSONB fields if found, null otherwise
2569
+ * @param patch - Partial data to update
2570
+ * @returns The updated record if found, null otherwise
2565
2571
  */
2566
- async update<TJsonb extends Partial<Select${Type}>>(
2567
- pk: ${pkType},
2568
- patch: MergeJsonb<Update${Type}, TJsonb>
2569
- ): Promise<MergeJsonb<Select${Type}, TJsonb> | null>;
2570
- async update(pk: ${pkType}, patch: any): Promise<any> {
2572
+ async update(pk: ${pkType}, patch: Update${Type}): Promise<Select${Type} | null> {
2571
2573
  const path = ${pkPathExpr};
2572
- return this.patch<any>(\`\${this.resource}/\${path}\`, patch);
2573
- }
2574
+ return this.patch<Select${Type} | null>(\`\${this.resource}/\${path}\`, patch);
2575
+ }`}
2574
2576
 
2575
- /**
2577
+ ${hasJsonbColumns ? ` /**
2576
2578
  * Delete a ${table.name} record by primary key
2577
2579
  * @param pk - The primary key value${hasCompositePk ? "s" : ""}
2578
2580
  * @returns The deleted record if found, null otherwise
2581
+ * @example
2582
+ * // With JSONB type override:
2583
+ * const user = await client.delete<{ metadata: Metadata }>('user-id');
2579
2584
  */
2580
- async delete(pk: ${pkType}): Promise<Select${Type} | null>;
2581
- /**
2582
- * Delete a ${table.name} record by primary key with JSONB type overrides
2585
+ async delete<TJsonb extends Partial<Select${Type}> = {}>(
2586
+ pk: ${pkType}
2587
+ ): Promise<Select${Type}<TJsonb> | null> {
2588
+ const path = ${pkPathExpr};
2589
+ return this.del<Select${Type}<TJsonb> | null>(\`\${this.resource}/\${path}\`);
2590
+ }` : ` /**
2591
+ * Delete a ${table.name} record by primary key
2583
2592
  * @param pk - The primary key value${hasCompositePk ? "s" : ""}
2584
- * @returns The deleted record with typed JSONB fields if found, null otherwise
2593
+ * @returns The deleted record if found, null otherwise
2585
2594
  */
2586
- async delete<TJsonb extends Partial<Select${Type}>>(
2587
- pk: ${pkType}
2588
- ): Promise<MergeJsonb<Select${Type}, TJsonb> | null>;
2589
- async delete(pk: ${pkType}): Promise<any> {
2595
+ async delete(pk: ${pkType}): Promise<Select${Type} | null> {
2590
2596
  const path = ${pkPathExpr};
2591
- return this.del<any>(\`\${this.resource}/\${path}\`);
2592
- }
2597
+ return this.del<Select${Type} | null>(\`\${this.resource}/\${path}\`);
2598
+ }`}
2593
2599
  ${includeMethodsCode}}
2594
2600
  `;
2595
2601
  }
@@ -3157,9 +3163,14 @@ function tsTypeFor(pgType, opts, enums) {
3157
3163
  return "unknown";
3158
3164
  return "string";
3159
3165
  }
3166
+ function isJsonbType2(pgType) {
3167
+ const t = pgType.toLowerCase();
3168
+ return t === "json" || t === "jsonb";
3169
+ }
3160
3170
  var pascal2 = (s) => s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
3161
3171
  function emitTypes(table, opts, enums) {
3162
3172
  const Type = pascal2(table.name);
3173
+ const hasJsonbColumns = table.columns.some((col) => isJsonbType2(col.pgType));
3163
3174
  const insertFields = table.columns.map((col) => {
3164
3175
  const base = tsTypeFor(col.pgType, opts, enums);
3165
3176
  const optional = col.hasDefault || col.nullable ? "?" : "";
@@ -3173,6 +3184,57 @@ function emitTypes(table, opts, enums) {
3173
3184
  return ` ${col.name}: ${valueType};`;
3174
3185
  }).join(`
3175
3186
  `);
3187
+ const baseInsertType = `type _Insert${Type}Base = {
3188
+ ${insertFields}
3189
+ };`;
3190
+ const baseSelectType = `type _Select${Type}Base = {
3191
+ ${selectFields}
3192
+ };`;
3193
+ if (hasJsonbColumns) {
3194
+ return `/**
3195
+ * AUTO-GENERATED FILE - DO NOT EDIT
3196
+ *
3197
+ * This file was automatically generated by PostgreSDK.
3198
+ * Any manual changes will be overwritten on the next generation.
3199
+ *
3200
+ * To make changes, modify your schema or configuration and regenerate.
3201
+ */
3202
+
3203
+ ${baseInsertType}
3204
+
3205
+ ${baseSelectType}
3206
+
3207
+ /**
3208
+ * Type for inserting a new ${table.name} record.
3209
+ * Fields with defaults or nullable columns are optional.
3210
+ *
3211
+ * Generic parameter TJsonb allows overriding JSONB column types:
3212
+ * @example Insert${Type}<{ metadata: MyMetadataType }>
3213
+ */
3214
+ export type Insert${Type}<TJsonb extends Partial<_Insert${Type}Base> = {}> =
3215
+ Omit<_Insert${Type}Base, keyof TJsonb> & TJsonb;
3216
+
3217
+ /**
3218
+ * Type for updating an existing ${table.name} record.
3219
+ * All fields are optional, allowing partial updates.
3220
+ *
3221
+ * Generic parameter TJsonb allows overriding JSONB column types:
3222
+ * @example Update${Type}<{ metadata: MyMetadataType }>
3223
+ */
3224
+ export type Update${Type}<TJsonb extends Partial<_Select${Type}Base> = {}> =
3225
+ Partial<Omit<_Insert${Type}Base, keyof TJsonb> & TJsonb>;
3226
+
3227
+ /**
3228
+ * Type representing a ${table.name} record from the database.
3229
+ * All fields are included as returned by SELECT queries.
3230
+ *
3231
+ * Generic parameter TJsonb allows overriding JSONB column types:
3232
+ * @example Select${Type}<{ metadata: MyMetadataType }>
3233
+ */
3234
+ export type Select${Type}<TJsonb extends Partial<_Select${Type}Base> = {}> =
3235
+ Omit<_Select${Type}Base, keyof TJsonb> & TJsonb;
3236
+ `;
3237
+ }
3176
3238
  return `/**
3177
3239
  * AUTO-GENERATED FILE - DO NOT EDIT
3178
3240
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgresdk",
3
- "version": "0.16.2",
3
+ "version": "0.16.4",
4
4
  "description": "Generate a typed server/client SDK from a Postgres schema (includes, Zod, Hono).",
5
5
  "type": "module",
6
6
  "bin": {