postgresdk 0.18.22 → 0.18.23
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/README.md +9 -0
- package/dist/cli.js +43 -19
- package/dist/index.d.ts +3 -0
- package/dist/index.js +44 -19
- package/dist/types.d.ts +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -146,6 +146,10 @@ export default {
|
|
|
146
146
|
schema: "public", // Database schema to introspect
|
|
147
147
|
outDir: "./api", // Output directory (or { client: "./sdk", server: "./api" })
|
|
148
148
|
softDeleteColumn: null, // Column name for soft deletes (e.g., "deleted_at")
|
|
149
|
+
softDeleteColumnOverrides: { // Per-table overrides (null = disable soft delete for that table)
|
|
150
|
+
// captures: "hidden_at", // different column name for a specific table
|
|
151
|
+
// audit_logs: null, // hard deletes only for this table
|
|
152
|
+
},
|
|
149
153
|
numericMode: "auto", // "auto" | "number" | "string" - How to type numeric columns
|
|
150
154
|
includeMethodsDepth: 2, // Max depth for nested includes
|
|
151
155
|
dateType: "date", // "date" | "string" - How to handle timestamps
|
|
@@ -738,6 +742,11 @@ const nested = await sdk.users.list({
|
|
|
738
742
|
}
|
|
739
743
|
});
|
|
740
744
|
|
|
745
|
+
// Soft deletes — when softDeleteColumn is configured, deleted records are hidden by default.
|
|
746
|
+
// Pass includeSoftDeleted: true to opt into seeing them (e.g., for admin/recovery UIs).
|
|
747
|
+
const allUsers = await sdk.users.list({ includeSoftDeleted: true });
|
|
748
|
+
const deletedUser = await sdk.users.getByPk("123", { includeSoftDeleted: true });
|
|
749
|
+
|
|
741
750
|
// Pagination with filtered results
|
|
742
751
|
let allResults = [];
|
|
743
752
|
let offset = 0;
|
package/dist/cli.js
CHANGED
|
@@ -3273,7 +3273,8 @@ const deleteSchema = z.object({
|
|
|
3273
3273
|
|
|
3274
3274
|
const getByPkQuerySchema = z.object({
|
|
3275
3275
|
select: z.array(z.string()).min(1).optional(),
|
|
3276
|
-
exclude: z.array(z.string()).min(1).optional()
|
|
3276
|
+
exclude: z.array(z.string()).min(1).optional(),
|
|
3277
|
+
includeSoftDeleted: z.boolean().optional()
|
|
3277
3278
|
}).strict().refine(
|
|
3278
3279
|
(data) => !(data.select && data.exclude),
|
|
3279
3280
|
{ message: "Cannot specify both 'select' and 'exclude' parameters" }
|
|
@@ -3288,7 +3289,8 @@ const listSchema = z.object({
|
|
|
3288
3289
|
offset: z.number().int().min(0).optional(),
|
|
3289
3290
|
orderBy: z.union([columnEnum, z.array(columnEnum)]).optional(),
|
|
3290
3291
|
order: z.union([z.enum(["asc", "desc"]), z.array(z.enum(["asc", "desc"]))]).optional(),
|
|
3291
|
-
distinctOn: z.union([columnEnum, z.array(columnEnum)]).optional()
|
|
3292
|
+
distinctOn: z.union([columnEnum, z.array(columnEnum)]).optional(),
|
|
3293
|
+
includeSoftDeleted: z.boolean().optional(),${hasVectorColumns ? `
|
|
3292
3294
|
vector: z.object({
|
|
3293
3295
|
field: z.string(),
|
|
3294
3296
|
query: z.array(z.number()),
|
|
@@ -3385,12 +3387,15 @@ ${hasAuth ? `
|
|
|
3385
3387
|
app.get(\`\${base}/${pkPath}\`, async (c) => {
|
|
3386
3388
|
${getPkParams}
|
|
3387
3389
|
|
|
3388
|
-
// Parse query params
|
|
3390
|
+
// Parse query params — coerce includeSoftDeleted string to boolean before Zod validation
|
|
3391
|
+
// (avoids Boolean("false")=true pitfall while keeping schema as single source of truth)
|
|
3389
3392
|
const selectParam = c.req.query("select");
|
|
3390
3393
|
const excludeParam = c.req.query("exclude");
|
|
3394
|
+
const includeSoftDeletedParam = c.req.query("includeSoftDeleted");
|
|
3391
3395
|
const queryData: any = {};
|
|
3392
3396
|
if (selectParam) queryData.select = selectParam.split(",");
|
|
3393
3397
|
if (excludeParam) queryData.exclude = excludeParam.split(",");
|
|
3398
|
+
if (includeSoftDeletedParam !== undefined) queryData.includeSoftDeleted = includeSoftDeletedParam === "true";
|
|
3394
3399
|
|
|
3395
3400
|
const queryParsed = getByPkQuerySchema.safeParse(queryData);
|
|
3396
3401
|
if (!queryParsed.success) {
|
|
@@ -3403,7 +3408,7 @@ ${hasAuth ? `
|
|
|
3403
3408
|
}
|
|
3404
3409
|
|
|
3405
3410
|
const ctx = { ...baseCtx, select: queryParsed.data.select, exclude: queryParsed.data.exclude };
|
|
3406
|
-
const result = await coreOps.getByPk(ctx, pkValues);
|
|
3411
|
+
const result = await coreOps.getByPk(ctx, pkValues, { includeSoftDeleted: queryParsed.data.includeSoftDeleted });
|
|
3407
3412
|
|
|
3408
3413
|
if (result.error) {
|
|
3409
3414
|
return c.json({ error: result.error }, result.status as any);
|
|
@@ -3909,14 +3914,14 @@ ${hasJsonbColumns ? ` /**
|
|
|
3909
3914
|
* @param options - Select specific fields to return
|
|
3910
3915
|
* @returns The record with only selected fields if found, null otherwise
|
|
3911
3916
|
*/
|
|
3912
|
-
async getByPk<TJsonb extends Partial<Select${Type}> = {}>(pk: ${pkType}, options: { select: string[] }): Promise<Partial<Select${Type}<TJsonb>> | null>;
|
|
3917
|
+
async getByPk<TJsonb extends Partial<Select${Type}> = {}>(pk: ${pkType}, options: { select: string[]; includeSoftDeleted?: boolean }): Promise<Partial<Select${Type}<TJsonb>> | null>;
|
|
3913
3918
|
/**
|
|
3914
3919
|
* Get a ${table.name} record by primary key with field exclusion
|
|
3915
3920
|
* @param pk - The primary key value${hasCompositePk ? "s" : ""}
|
|
3916
3921
|
* @param options - Exclude specific fields from return
|
|
3917
3922
|
* @returns The record without excluded fields if found, null otherwise
|
|
3918
3923
|
*/
|
|
3919
|
-
async getByPk<TJsonb extends Partial<Select${Type}> = {}>(pk: ${pkType}, options: { exclude: string[] }): Promise<Partial<Select${Type}<TJsonb>> | null>;
|
|
3924
|
+
async getByPk<TJsonb extends Partial<Select${Type}> = {}>(pk: ${pkType}, options: { exclude: string[]; includeSoftDeleted?: boolean }): Promise<Partial<Select${Type}<TJsonb>> | null>;
|
|
3920
3925
|
/**
|
|
3921
3926
|
* Get a ${table.name} record by primary key
|
|
3922
3927
|
* @param pk - The primary key value${hasCompositePk ? "s" : ""}
|
|
@@ -3925,15 +3930,16 @@ ${hasJsonbColumns ? ` /**
|
|
|
3925
3930
|
* // With JSONB type override:
|
|
3926
3931
|
* const user = await client.getByPk<{ metadata: Metadata }>('user-id');
|
|
3927
3932
|
*/
|
|
3928
|
-
async getByPk<TJsonb extends Partial<Select${Type}> = {}>(pk: ${pkType}, options?:
|
|
3933
|
+
async getByPk<TJsonb extends Partial<Select${Type}> = {}>(pk: ${pkType}, options?: { includeSoftDeleted?: boolean }): Promise<Select${Type}<TJsonb> | null>;
|
|
3929
3934
|
async getByPk<TJsonb extends Partial<Select${Type}> = {}>(
|
|
3930
3935
|
pk: ${pkType},
|
|
3931
|
-
options?: { select?: string[]; exclude?: string[] }
|
|
3936
|
+
options?: { select?: string[]; exclude?: string[]; includeSoftDeleted?: boolean }
|
|
3932
3937
|
): Promise<Select${Type}<TJsonb> | Partial<Select${Type}<TJsonb>> | null> {
|
|
3933
3938
|
const path = ${pkPathExpr};
|
|
3934
3939
|
const queryParams = new URLSearchParams();
|
|
3935
3940
|
if (options?.select) queryParams.set('select', options.select.join(','));
|
|
3936
3941
|
if (options?.exclude) queryParams.set('exclude', options.exclude.join(','));
|
|
3942
|
+
if (options?.includeSoftDeleted) queryParams.set('includeSoftDeleted', 'true');
|
|
3937
3943
|
const query = queryParams.toString();
|
|
3938
3944
|
const url = query ? \`\${this.resource}/\${path}?\${query}\` : \`\${this.resource}/\${path}\`;
|
|
3939
3945
|
return this.get<Select${Type}<TJsonb> | null>(url);
|
|
@@ -3943,28 +3949,29 @@ ${hasJsonbColumns ? ` /**
|
|
|
3943
3949
|
* @param options - Select specific fields to return
|
|
3944
3950
|
* @returns The record with only selected fields if found, null otherwise
|
|
3945
3951
|
*/
|
|
3946
|
-
async getByPk(pk: ${pkType}, options: { select: string[] }): Promise<Partial<Select${Type}> | null>;
|
|
3952
|
+
async getByPk(pk: ${pkType}, options: { select: string[]; includeSoftDeleted?: boolean }): Promise<Partial<Select${Type}> | null>;
|
|
3947
3953
|
/**
|
|
3948
3954
|
* Get a ${table.name} record by primary key with field exclusion
|
|
3949
3955
|
* @param pk - The primary key value${hasCompositePk ? "s" : ""}
|
|
3950
3956
|
* @param options - Exclude specific fields from return
|
|
3951
3957
|
* @returns The record without excluded fields if found, null otherwise
|
|
3952
3958
|
*/
|
|
3953
|
-
async getByPk(pk: ${pkType}, options: { exclude: string[] }): Promise<Partial<Select${Type}> | null>;
|
|
3959
|
+
async getByPk(pk: ${pkType}, options: { exclude: string[]; includeSoftDeleted?: boolean }): Promise<Partial<Select${Type}> | null>;
|
|
3954
3960
|
/**
|
|
3955
3961
|
* Get a ${table.name} record by primary key
|
|
3956
3962
|
* @param pk - The primary key value${hasCompositePk ? "s" : ""}
|
|
3957
3963
|
* @returns The record with all fields if found, null otherwise
|
|
3958
3964
|
*/
|
|
3959
|
-
async getByPk(pk: ${pkType}, options?:
|
|
3965
|
+
async getByPk(pk: ${pkType}, options?: { includeSoftDeleted?: boolean }): Promise<Select${Type} | null>;
|
|
3960
3966
|
async getByPk(
|
|
3961
3967
|
pk: ${pkType},
|
|
3962
|
-
options?: { select?: string[]; exclude?: string[] }
|
|
3968
|
+
options?: { select?: string[]; exclude?: string[]; includeSoftDeleted?: boolean }
|
|
3963
3969
|
): Promise<Select${Type} | Partial<Select${Type}> | null> {
|
|
3964
3970
|
const path = ${pkPathExpr};
|
|
3965
3971
|
const queryParams = new URLSearchParams();
|
|
3966
3972
|
if (options?.select) queryParams.set('select', options.select.join(','));
|
|
3967
3973
|
if (options?.exclude) queryParams.set('exclude', options.exclude.join(','));
|
|
3974
|
+
if (options?.includeSoftDeleted) queryParams.set('includeSoftDeleted', 'true');
|
|
3968
3975
|
const query = queryParams.toString();
|
|
3969
3976
|
const url = query ? \`\${this.resource}/\${path}?\${query}\` : \`\${this.resource}/\${path}\`;
|
|
3970
3977
|
return this.get<Select${Type} | null>(url);
|
|
@@ -3991,6 +3998,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3991
3998
|
orderBy?: string | string[];
|
|
3992
3999
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3993
4000
|
distinctOn?: string | string[];
|
|
4001
|
+
includeSoftDeleted?: boolean;
|
|
3994
4002
|
}): Promise<PaginatedResponse<Partial<Select${Type}<TJsonb>> & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3995
4003
|
/**
|
|
3996
4004
|
* List ${table.name} records with field exclusion
|
|
@@ -4013,6 +4021,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4013
4021
|
orderBy?: string | string[];
|
|
4014
4022
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
4015
4023
|
distinctOn?: string | string[];
|
|
4024
|
+
includeSoftDeleted?: boolean;
|
|
4016
4025
|
}): Promise<PaginatedResponse<Partial<Select${Type}<TJsonb>> & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
4017
4026
|
/**
|
|
4018
4027
|
* List ${table.name} records with pagination and filtering
|
|
@@ -4046,6 +4055,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4046
4055
|
orderBy?: string | string[];
|
|
4047
4056
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
4048
4057
|
distinctOn?: string | string[];
|
|
4058
|
+
includeSoftDeleted?: boolean;
|
|
4049
4059
|
}): Promise<PaginatedResponse<${Type}WithIncludes<TInclude> & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
4050
4060
|
async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
|
|
4051
4061
|
include?: ${Type}IncludeSpec;
|
|
@@ -4064,6 +4074,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4064
4074
|
orderBy?: string | string[];
|
|
4065
4075
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
4066
4076
|
distinctOn?: string | string[];
|
|
4077
|
+
includeSoftDeleted?: boolean;
|
|
4067
4078
|
}): Promise<PaginatedResponse<Select${Type}<TJsonb> | Partial<Select${Type}<TJsonb>>>> {
|
|
4068
4079
|
return this.post<PaginatedResponse<Select${Type}<TJsonb> & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>(\`\${this.resource}/list\`, params ?? {});
|
|
4069
4080
|
}` : ` /**
|
|
@@ -4087,6 +4098,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4087
4098
|
orderBy?: string | string[];
|
|
4088
4099
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
4089
4100
|
distinctOn?: string | string[];
|
|
4101
|
+
includeSoftDeleted?: boolean;
|
|
4090
4102
|
}): Promise<PaginatedResponse<Partial<Select${Type}> & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
4091
4103
|
/**
|
|
4092
4104
|
* List ${table.name} records with field exclusion
|
|
@@ -4109,6 +4121,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4109
4121
|
orderBy?: string | string[];
|
|
4110
4122
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
4111
4123
|
distinctOn?: string | string[];
|
|
4124
|
+
includeSoftDeleted?: boolean;
|
|
4112
4125
|
}): Promise<PaginatedResponse<Partial<Select${Type}> & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
4113
4126
|
/**
|
|
4114
4127
|
* List ${table.name} records with pagination and filtering
|
|
@@ -4136,6 +4149,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4136
4149
|
orderBy?: string | string[];
|
|
4137
4150
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
4138
4151
|
distinctOn?: string | string[];
|
|
4152
|
+
includeSoftDeleted?: boolean;
|
|
4139
4153
|
}): Promise<PaginatedResponse<${Type}WithIncludes<TInclude> & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
4140
4154
|
async list(params?: {
|
|
4141
4155
|
include?: ${Type}IncludeSpec;
|
|
@@ -4154,6 +4168,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
4154
4168
|
orderBy?: string | string[];
|
|
4155
4169
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
4156
4170
|
distinctOn?: string | string[];
|
|
4171
|
+
includeSoftDeleted?: boolean;
|
|
4157
4172
|
}): Promise<PaginatedResponse<Select${Type} | Partial<Select${Type}>>> {
|
|
4158
4173
|
return this.post<PaginatedResponse<Select${Type} & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>(\`\${this.resource}/list\`, params ?? {});
|
|
4159
4174
|
}`}
|
|
@@ -5980,7 +5995,8 @@ export async function createRecord(
|
|
|
5980
5995
|
*/
|
|
5981
5996
|
export async function getByPk(
|
|
5982
5997
|
ctx: OperationContext,
|
|
5983
|
-
pkValues: any[]
|
|
5998
|
+
pkValues: any[],
|
|
5999
|
+
opts?: { includeSoftDeleted?: boolean }
|
|
5984
6000
|
): Promise<{ data?: any; error?: string; status: number }> {
|
|
5985
6001
|
try {
|
|
5986
6002
|
const hasCompositePk = ctx.pkColumns.length > 1;
|
|
@@ -5989,7 +6005,8 @@ export async function getByPk(
|
|
|
5989
6005
|
: \`"\${ctx.pkColumns[0]}" = $1\`;
|
|
5990
6006
|
|
|
5991
6007
|
const columns = buildColumnList(ctx.select, ctx.exclude, ctx.allColumnNames);
|
|
5992
|
-
const
|
|
6008
|
+
const softDeleteFilter = ctx.softDeleteColumn && !opts?.includeSoftDeleted ? \` AND "\${ctx.softDeleteColumn}" IS NULL\` : "";
|
|
6009
|
+
const text = \`SELECT \${columns} FROM "\${ctx.table}" WHERE \${wherePkSql}\${softDeleteFilter} LIMIT 1\`;
|
|
5993
6010
|
log.debug(\`GET \${ctx.table} by PK:\`, pkValues, "SQL:", text);
|
|
5994
6011
|
|
|
5995
6012
|
const { rows } = await ctx.pg.query(text, pkValues);
|
|
@@ -6362,6 +6379,7 @@ export async function listRecords(
|
|
|
6362
6379
|
orderBy?: string | string[];
|
|
6363
6380
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
6364
6381
|
distinctOn?: string | string[];
|
|
6382
|
+
includeSoftDeleted?: boolean;
|
|
6365
6383
|
vector?: {
|
|
6366
6384
|
field: string;
|
|
6367
6385
|
query: number[];
|
|
@@ -6372,7 +6390,7 @@ export async function listRecords(
|
|
|
6372
6390
|
}
|
|
6373
6391
|
): Promise<{ data?: any; total?: number; limit?: number; offset?: number; hasMore?: boolean; error?: string; issues?: any; needsIncludes?: boolean; includeSpec?: any; status: number }> {
|
|
6374
6392
|
try {
|
|
6375
|
-
const { where: whereClause, limit = 50, offset = 0, include, orderBy, order, vector, trigram, distinctOn } = params;
|
|
6393
|
+
const { where: whereClause, limit = 50, offset = 0, include, orderBy, order, vector, trigram, distinctOn, includeSoftDeleted } = params;
|
|
6376
6394
|
|
|
6377
6395
|
// DISTINCT ON support
|
|
6378
6396
|
const distinctCols: string[] | null = distinctOn ? (Array.isArray(distinctOn) ? distinctOn : [distinctOn]) : null;
|
|
@@ -6408,8 +6426,8 @@ export async function listRecords(
|
|
|
6408
6426
|
const whereParts: string[] = [];
|
|
6409
6427
|
let whereParams: any[] = [];
|
|
6410
6428
|
|
|
6411
|
-
// Add soft delete filter if applicable
|
|
6412
|
-
if (ctx.softDeleteColumn) {
|
|
6429
|
+
// Add soft delete filter if applicable (skip if caller opts into seeing soft-deleted records)
|
|
6430
|
+
if (ctx.softDeleteColumn && !includeSoftDeleted) {
|
|
6413
6431
|
whereParts.push(\`"\${ctx.softDeleteColumn}" IS NULL\`);
|
|
6414
6432
|
}
|
|
6415
6433
|
|
|
@@ -6447,7 +6465,7 @@ export async function listRecords(
|
|
|
6447
6465
|
|
|
6448
6466
|
if (hasQueryParam && !hasThreshold && whereParams.length > 0) {
|
|
6449
6467
|
const countWhereParts: string[] = [];
|
|
6450
|
-
if (ctx.softDeleteColumn) {
|
|
6468
|
+
if (ctx.softDeleteColumn && !includeSoftDeleted) {
|
|
6451
6469
|
countWhereParts.push(\`"\${ctx.softDeleteColumn}" IS NULL\`);
|
|
6452
6470
|
}
|
|
6453
6471
|
if (whereClause) {
|
|
@@ -7418,6 +7436,12 @@ init_utils();
|
|
|
7418
7436
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
7419
7437
|
var __dirname2 = dirname2(__filename2);
|
|
7420
7438
|
var { version: CLI_VERSION } = JSON.parse(readFileSync(join2(__dirname2, "../package.json"), "utf-8"));
|
|
7439
|
+
function resolveSoftDeleteColumn(cfg, tableName) {
|
|
7440
|
+
const overrides = cfg.softDeleteColumnOverrides;
|
|
7441
|
+
if (overrides && tableName in overrides)
|
|
7442
|
+
return overrides[tableName] ?? null;
|
|
7443
|
+
return cfg.softDeleteColumn ?? null;
|
|
7444
|
+
}
|
|
7421
7445
|
async function generate(configPath) {
|
|
7422
7446
|
if (!existsSync2(configPath)) {
|
|
7423
7447
|
throw new Error(`Config file not found: ${configPath}
|
|
@@ -7517,7 +7541,7 @@ async function generate(configPath) {
|
|
|
7517
7541
|
let routeContent;
|
|
7518
7542
|
if (serverFramework === "hono") {
|
|
7519
7543
|
routeContent = emitHonoRoutes(table, graph, {
|
|
7520
|
-
softDeleteColumn: cfg.
|
|
7544
|
+
softDeleteColumn: resolveSoftDeleteColumn(cfg, table.name),
|
|
7521
7545
|
includeMethodsDepth: cfg.includeMethodsDepth || 2,
|
|
7522
7546
|
authStrategy: getAuthStrategy(normalizedAuth),
|
|
7523
7547
|
useJsExtensions: cfg.useJsExtensions,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,5 @@
|
|
|
1
1
|
import "dotenv/config";
|
|
2
|
+
import type { Config } from "./types";
|
|
3
|
+
/** Resolves the effective soft delete column for a given table, respecting per-table overrides. */
|
|
4
|
+
export declare function resolveSoftDeleteColumn(cfg: Pick<Config, "softDeleteColumn" | "softDeleteColumnOverrides">, tableName: string): string | null;
|
|
2
5
|
export declare function generate(configPath: string): Promise<void>;
|
package/dist/index.js
CHANGED
|
@@ -2312,7 +2312,8 @@ const deleteSchema = z.object({
|
|
|
2312
2312
|
|
|
2313
2313
|
const getByPkQuerySchema = z.object({
|
|
2314
2314
|
select: z.array(z.string()).min(1).optional(),
|
|
2315
|
-
exclude: z.array(z.string()).min(1).optional()
|
|
2315
|
+
exclude: z.array(z.string()).min(1).optional(),
|
|
2316
|
+
includeSoftDeleted: z.boolean().optional()
|
|
2316
2317
|
}).strict().refine(
|
|
2317
2318
|
(data) => !(data.select && data.exclude),
|
|
2318
2319
|
{ message: "Cannot specify both 'select' and 'exclude' parameters" }
|
|
@@ -2327,7 +2328,8 @@ const listSchema = z.object({
|
|
|
2327
2328
|
offset: z.number().int().min(0).optional(),
|
|
2328
2329
|
orderBy: z.union([columnEnum, z.array(columnEnum)]).optional(),
|
|
2329
2330
|
order: z.union([z.enum(["asc", "desc"]), z.array(z.enum(["asc", "desc"]))]).optional(),
|
|
2330
|
-
distinctOn: z.union([columnEnum, z.array(columnEnum)]).optional()
|
|
2331
|
+
distinctOn: z.union([columnEnum, z.array(columnEnum)]).optional(),
|
|
2332
|
+
includeSoftDeleted: z.boolean().optional(),${hasVectorColumns ? `
|
|
2331
2333
|
vector: z.object({
|
|
2332
2334
|
field: z.string(),
|
|
2333
2335
|
query: z.array(z.number()),
|
|
@@ -2424,12 +2426,15 @@ ${hasAuth ? `
|
|
|
2424
2426
|
app.get(\`\${base}/${pkPath}\`, async (c) => {
|
|
2425
2427
|
${getPkParams}
|
|
2426
2428
|
|
|
2427
|
-
// Parse query params
|
|
2429
|
+
// Parse query params — coerce includeSoftDeleted string to boolean before Zod validation
|
|
2430
|
+
// (avoids Boolean("false")=true pitfall while keeping schema as single source of truth)
|
|
2428
2431
|
const selectParam = c.req.query("select");
|
|
2429
2432
|
const excludeParam = c.req.query("exclude");
|
|
2433
|
+
const includeSoftDeletedParam = c.req.query("includeSoftDeleted");
|
|
2430
2434
|
const queryData: any = {};
|
|
2431
2435
|
if (selectParam) queryData.select = selectParam.split(",");
|
|
2432
2436
|
if (excludeParam) queryData.exclude = excludeParam.split(",");
|
|
2437
|
+
if (includeSoftDeletedParam !== undefined) queryData.includeSoftDeleted = includeSoftDeletedParam === "true";
|
|
2433
2438
|
|
|
2434
2439
|
const queryParsed = getByPkQuerySchema.safeParse(queryData);
|
|
2435
2440
|
if (!queryParsed.success) {
|
|
@@ -2442,7 +2447,7 @@ ${hasAuth ? `
|
|
|
2442
2447
|
}
|
|
2443
2448
|
|
|
2444
2449
|
const ctx = { ...baseCtx, select: queryParsed.data.select, exclude: queryParsed.data.exclude };
|
|
2445
|
-
const result = await coreOps.getByPk(ctx, pkValues);
|
|
2450
|
+
const result = await coreOps.getByPk(ctx, pkValues, { includeSoftDeleted: queryParsed.data.includeSoftDeleted });
|
|
2446
2451
|
|
|
2447
2452
|
if (result.error) {
|
|
2448
2453
|
return c.json({ error: result.error }, result.status as any);
|
|
@@ -2948,14 +2953,14 @@ ${hasJsonbColumns ? ` /**
|
|
|
2948
2953
|
* @param options - Select specific fields to return
|
|
2949
2954
|
* @returns The record with only selected fields if found, null otherwise
|
|
2950
2955
|
*/
|
|
2951
|
-
async getByPk<TJsonb extends Partial<Select${Type}> = {}>(pk: ${pkType}, options: { select: string[] }): Promise<Partial<Select${Type}<TJsonb>> | null>;
|
|
2956
|
+
async getByPk<TJsonb extends Partial<Select${Type}> = {}>(pk: ${pkType}, options: { select: string[]; includeSoftDeleted?: boolean }): Promise<Partial<Select${Type}<TJsonb>> | null>;
|
|
2952
2957
|
/**
|
|
2953
2958
|
* Get a ${table.name} record by primary key with field exclusion
|
|
2954
2959
|
* @param pk - The primary key value${hasCompositePk ? "s" : ""}
|
|
2955
2960
|
* @param options - Exclude specific fields from return
|
|
2956
2961
|
* @returns The record without excluded fields if found, null otherwise
|
|
2957
2962
|
*/
|
|
2958
|
-
async getByPk<TJsonb extends Partial<Select${Type}> = {}>(pk: ${pkType}, options: { exclude: string[] }): Promise<Partial<Select${Type}<TJsonb>> | null>;
|
|
2963
|
+
async getByPk<TJsonb extends Partial<Select${Type}> = {}>(pk: ${pkType}, options: { exclude: string[]; includeSoftDeleted?: boolean }): Promise<Partial<Select${Type}<TJsonb>> | null>;
|
|
2959
2964
|
/**
|
|
2960
2965
|
* Get a ${table.name} record by primary key
|
|
2961
2966
|
* @param pk - The primary key value${hasCompositePk ? "s" : ""}
|
|
@@ -2964,15 +2969,16 @@ ${hasJsonbColumns ? ` /**
|
|
|
2964
2969
|
* // With JSONB type override:
|
|
2965
2970
|
* const user = await client.getByPk<{ metadata: Metadata }>('user-id');
|
|
2966
2971
|
*/
|
|
2967
|
-
async getByPk<TJsonb extends Partial<Select${Type}> = {}>(pk: ${pkType}, options?:
|
|
2972
|
+
async getByPk<TJsonb extends Partial<Select${Type}> = {}>(pk: ${pkType}, options?: { includeSoftDeleted?: boolean }): Promise<Select${Type}<TJsonb> | null>;
|
|
2968
2973
|
async getByPk<TJsonb extends Partial<Select${Type}> = {}>(
|
|
2969
2974
|
pk: ${pkType},
|
|
2970
|
-
options?: { select?: string[]; exclude?: string[] }
|
|
2975
|
+
options?: { select?: string[]; exclude?: string[]; includeSoftDeleted?: boolean }
|
|
2971
2976
|
): Promise<Select${Type}<TJsonb> | Partial<Select${Type}<TJsonb>> | null> {
|
|
2972
2977
|
const path = ${pkPathExpr};
|
|
2973
2978
|
const queryParams = new URLSearchParams();
|
|
2974
2979
|
if (options?.select) queryParams.set('select', options.select.join(','));
|
|
2975
2980
|
if (options?.exclude) queryParams.set('exclude', options.exclude.join(','));
|
|
2981
|
+
if (options?.includeSoftDeleted) queryParams.set('includeSoftDeleted', 'true');
|
|
2976
2982
|
const query = queryParams.toString();
|
|
2977
2983
|
const url = query ? \`\${this.resource}/\${path}?\${query}\` : \`\${this.resource}/\${path}\`;
|
|
2978
2984
|
return this.get<Select${Type}<TJsonb> | null>(url);
|
|
@@ -2982,28 +2988,29 @@ ${hasJsonbColumns ? ` /**
|
|
|
2982
2988
|
* @param options - Select specific fields to return
|
|
2983
2989
|
* @returns The record with only selected fields if found, null otherwise
|
|
2984
2990
|
*/
|
|
2985
|
-
async getByPk(pk: ${pkType}, options: { select: string[] }): Promise<Partial<Select${Type}> | null>;
|
|
2991
|
+
async getByPk(pk: ${pkType}, options: { select: string[]; includeSoftDeleted?: boolean }): Promise<Partial<Select${Type}> | null>;
|
|
2986
2992
|
/**
|
|
2987
2993
|
* Get a ${table.name} record by primary key with field exclusion
|
|
2988
2994
|
* @param pk - The primary key value${hasCompositePk ? "s" : ""}
|
|
2989
2995
|
* @param options - Exclude specific fields from return
|
|
2990
2996
|
* @returns The record without excluded fields if found, null otherwise
|
|
2991
2997
|
*/
|
|
2992
|
-
async getByPk(pk: ${pkType}, options: { exclude: string[] }): Promise<Partial<Select${Type}> | null>;
|
|
2998
|
+
async getByPk(pk: ${pkType}, options: { exclude: string[]; includeSoftDeleted?: boolean }): Promise<Partial<Select${Type}> | null>;
|
|
2993
2999
|
/**
|
|
2994
3000
|
* Get a ${table.name} record by primary key
|
|
2995
3001
|
* @param pk - The primary key value${hasCompositePk ? "s" : ""}
|
|
2996
3002
|
* @returns The record with all fields if found, null otherwise
|
|
2997
3003
|
*/
|
|
2998
|
-
async getByPk(pk: ${pkType}, options?:
|
|
3004
|
+
async getByPk(pk: ${pkType}, options?: { includeSoftDeleted?: boolean }): Promise<Select${Type} | null>;
|
|
2999
3005
|
async getByPk(
|
|
3000
3006
|
pk: ${pkType},
|
|
3001
|
-
options?: { select?: string[]; exclude?: string[] }
|
|
3007
|
+
options?: { select?: string[]; exclude?: string[]; includeSoftDeleted?: boolean }
|
|
3002
3008
|
): Promise<Select${Type} | Partial<Select${Type}> | null> {
|
|
3003
3009
|
const path = ${pkPathExpr};
|
|
3004
3010
|
const queryParams = new URLSearchParams();
|
|
3005
3011
|
if (options?.select) queryParams.set('select', options.select.join(','));
|
|
3006
3012
|
if (options?.exclude) queryParams.set('exclude', options.exclude.join(','));
|
|
3013
|
+
if (options?.includeSoftDeleted) queryParams.set('includeSoftDeleted', 'true');
|
|
3007
3014
|
const query = queryParams.toString();
|
|
3008
3015
|
const url = query ? \`\${this.resource}/\${path}?\${query}\` : \`\${this.resource}/\${path}\`;
|
|
3009
3016
|
return this.get<Select${Type} | null>(url);
|
|
@@ -3030,6 +3037,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3030
3037
|
orderBy?: string | string[];
|
|
3031
3038
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3032
3039
|
distinctOn?: string | string[];
|
|
3040
|
+
includeSoftDeleted?: boolean;
|
|
3033
3041
|
}): Promise<PaginatedResponse<Partial<Select${Type}<TJsonb>> & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3034
3042
|
/**
|
|
3035
3043
|
* List ${table.name} records with field exclusion
|
|
@@ -3052,6 +3060,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3052
3060
|
orderBy?: string | string[];
|
|
3053
3061
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3054
3062
|
distinctOn?: string | string[];
|
|
3063
|
+
includeSoftDeleted?: boolean;
|
|
3055
3064
|
}): Promise<PaginatedResponse<Partial<Select${Type}<TJsonb>> & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3056
3065
|
/**
|
|
3057
3066
|
* List ${table.name} records with pagination and filtering
|
|
@@ -3085,6 +3094,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3085
3094
|
orderBy?: string | string[];
|
|
3086
3095
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3087
3096
|
distinctOn?: string | string[];
|
|
3097
|
+
includeSoftDeleted?: boolean;
|
|
3088
3098
|
}): Promise<PaginatedResponse<${Type}WithIncludes<TInclude> & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3089
3099
|
async list<TJsonb extends Partial<Select${Type}> = {}>(params?: {
|
|
3090
3100
|
include?: ${Type}IncludeSpec;
|
|
@@ -3103,6 +3113,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3103
3113
|
orderBy?: string | string[];
|
|
3104
3114
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3105
3115
|
distinctOn?: string | string[];
|
|
3116
|
+
includeSoftDeleted?: boolean;
|
|
3106
3117
|
}): Promise<PaginatedResponse<Select${Type}<TJsonb> | Partial<Select${Type}<TJsonb>>>> {
|
|
3107
3118
|
return this.post<PaginatedResponse<Select${Type}<TJsonb> & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>(\`\${this.resource}/list\`, params ?? {});
|
|
3108
3119
|
}` : ` /**
|
|
@@ -3126,6 +3137,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3126
3137
|
orderBy?: string | string[];
|
|
3127
3138
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3128
3139
|
distinctOn?: string | string[];
|
|
3140
|
+
includeSoftDeleted?: boolean;
|
|
3129
3141
|
}): Promise<PaginatedResponse<Partial<Select${Type}> & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3130
3142
|
/**
|
|
3131
3143
|
* List ${table.name} records with field exclusion
|
|
@@ -3148,6 +3160,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3148
3160
|
orderBy?: string | string[];
|
|
3149
3161
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3150
3162
|
distinctOn?: string | string[];
|
|
3163
|
+
includeSoftDeleted?: boolean;
|
|
3151
3164
|
}): Promise<PaginatedResponse<Partial<Select${Type}> & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3152
3165
|
/**
|
|
3153
3166
|
* List ${table.name} records with pagination and filtering
|
|
@@ -3175,6 +3188,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3175
3188
|
orderBy?: string | string[];
|
|
3176
3189
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3177
3190
|
distinctOn?: string | string[];
|
|
3191
|
+
includeSoftDeleted?: boolean;
|
|
3178
3192
|
}): Promise<PaginatedResponse<${Type}WithIncludes<TInclude> & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>;
|
|
3179
3193
|
async list(params?: {
|
|
3180
3194
|
include?: ${Type}IncludeSpec;
|
|
@@ -3193,6 +3207,7 @@ ${hasJsonbColumns ? ` /**
|
|
|
3193
3207
|
orderBy?: string | string[];
|
|
3194
3208
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
3195
3209
|
distinctOn?: string | string[];
|
|
3210
|
+
includeSoftDeleted?: boolean;
|
|
3196
3211
|
}): Promise<PaginatedResponse<Select${Type} | Partial<Select${Type}>>> {
|
|
3197
3212
|
return this.post<PaginatedResponse<Select${Type} & { _similarity?: number }${hasVectorColumns ? " & { _distance?: number }" : ""}>>(\`\${this.resource}/list\`, params ?? {});
|
|
3198
3213
|
}`}
|
|
@@ -5019,7 +5034,8 @@ export async function createRecord(
|
|
|
5019
5034
|
*/
|
|
5020
5035
|
export async function getByPk(
|
|
5021
5036
|
ctx: OperationContext,
|
|
5022
|
-
pkValues: any[]
|
|
5037
|
+
pkValues: any[],
|
|
5038
|
+
opts?: { includeSoftDeleted?: boolean }
|
|
5023
5039
|
): Promise<{ data?: any; error?: string; status: number }> {
|
|
5024
5040
|
try {
|
|
5025
5041
|
const hasCompositePk = ctx.pkColumns.length > 1;
|
|
@@ -5028,7 +5044,8 @@ export async function getByPk(
|
|
|
5028
5044
|
: \`"\${ctx.pkColumns[0]}" = $1\`;
|
|
5029
5045
|
|
|
5030
5046
|
const columns = buildColumnList(ctx.select, ctx.exclude, ctx.allColumnNames);
|
|
5031
|
-
const
|
|
5047
|
+
const softDeleteFilter = ctx.softDeleteColumn && !opts?.includeSoftDeleted ? \` AND "\${ctx.softDeleteColumn}" IS NULL\` : "";
|
|
5048
|
+
const text = \`SELECT \${columns} FROM "\${ctx.table}" WHERE \${wherePkSql}\${softDeleteFilter} LIMIT 1\`;
|
|
5032
5049
|
log.debug(\`GET \${ctx.table} by PK:\`, pkValues, "SQL:", text);
|
|
5033
5050
|
|
|
5034
5051
|
const { rows } = await ctx.pg.query(text, pkValues);
|
|
@@ -5401,6 +5418,7 @@ export async function listRecords(
|
|
|
5401
5418
|
orderBy?: string | string[];
|
|
5402
5419
|
order?: "asc" | "desc" | ("asc" | "desc")[];
|
|
5403
5420
|
distinctOn?: string | string[];
|
|
5421
|
+
includeSoftDeleted?: boolean;
|
|
5404
5422
|
vector?: {
|
|
5405
5423
|
field: string;
|
|
5406
5424
|
query: number[];
|
|
@@ -5411,7 +5429,7 @@ export async function listRecords(
|
|
|
5411
5429
|
}
|
|
5412
5430
|
): Promise<{ data?: any; total?: number; limit?: number; offset?: number; hasMore?: boolean; error?: string; issues?: any; needsIncludes?: boolean; includeSpec?: any; status: number }> {
|
|
5413
5431
|
try {
|
|
5414
|
-
const { where: whereClause, limit = 50, offset = 0, include, orderBy, order, vector, trigram, distinctOn } = params;
|
|
5432
|
+
const { where: whereClause, limit = 50, offset = 0, include, orderBy, order, vector, trigram, distinctOn, includeSoftDeleted } = params;
|
|
5415
5433
|
|
|
5416
5434
|
// DISTINCT ON support
|
|
5417
5435
|
const distinctCols: string[] | null = distinctOn ? (Array.isArray(distinctOn) ? distinctOn : [distinctOn]) : null;
|
|
@@ -5447,8 +5465,8 @@ export async function listRecords(
|
|
|
5447
5465
|
const whereParts: string[] = [];
|
|
5448
5466
|
let whereParams: any[] = [];
|
|
5449
5467
|
|
|
5450
|
-
// Add soft delete filter if applicable
|
|
5451
|
-
if (ctx.softDeleteColumn) {
|
|
5468
|
+
// Add soft delete filter if applicable (skip if caller opts into seeing soft-deleted records)
|
|
5469
|
+
if (ctx.softDeleteColumn && !includeSoftDeleted) {
|
|
5452
5470
|
whereParts.push(\`"\${ctx.softDeleteColumn}" IS NULL\`);
|
|
5453
5471
|
}
|
|
5454
5472
|
|
|
@@ -5486,7 +5504,7 @@ export async function listRecords(
|
|
|
5486
5504
|
|
|
5487
5505
|
if (hasQueryParam && !hasThreshold && whereParams.length > 0) {
|
|
5488
5506
|
const countWhereParts: string[] = [];
|
|
5489
|
-
if (ctx.softDeleteColumn) {
|
|
5507
|
+
if (ctx.softDeleteColumn && !includeSoftDeleted) {
|
|
5490
5508
|
countWhereParts.push(\`"\${ctx.softDeleteColumn}" IS NULL\`);
|
|
5491
5509
|
}
|
|
5492
5510
|
if (whereClause) {
|
|
@@ -6457,6 +6475,12 @@ init_utils();
|
|
|
6457
6475
|
var __filename2 = fileURLToPath(import.meta.url);
|
|
6458
6476
|
var __dirname2 = dirname2(__filename2);
|
|
6459
6477
|
var { version: CLI_VERSION } = JSON.parse(readFileSync(join2(__dirname2, "../package.json"), "utf-8"));
|
|
6478
|
+
function resolveSoftDeleteColumn(cfg, tableName) {
|
|
6479
|
+
const overrides = cfg.softDeleteColumnOverrides;
|
|
6480
|
+
if (overrides && tableName in overrides)
|
|
6481
|
+
return overrides[tableName] ?? null;
|
|
6482
|
+
return cfg.softDeleteColumn ?? null;
|
|
6483
|
+
}
|
|
6460
6484
|
async function generate(configPath) {
|
|
6461
6485
|
if (!existsSync2(configPath)) {
|
|
6462
6486
|
throw new Error(`Config file not found: ${configPath}
|
|
@@ -6556,7 +6580,7 @@ async function generate(configPath) {
|
|
|
6556
6580
|
let routeContent;
|
|
6557
6581
|
if (serverFramework === "hono") {
|
|
6558
6582
|
routeContent = emitHonoRoutes(table, graph, {
|
|
6559
|
-
softDeleteColumn: cfg.
|
|
6583
|
+
softDeleteColumn: resolveSoftDeleteColumn(cfg, table.name),
|
|
6560
6584
|
includeMethodsDepth: cfg.includeMethodsDepth || 2,
|
|
6561
6585
|
authStrategy: getAuthStrategy(normalizedAuth),
|
|
6562
6586
|
useJsExtensions: cfg.useJsExtensions,
|
|
@@ -6709,5 +6733,6 @@ async function generate(configPath) {
|
|
|
6709
6733
|
console.log(` Then run: postgresdk pull`);
|
|
6710
6734
|
}
|
|
6711
6735
|
export {
|
|
6736
|
+
resolveSoftDeleteColumn,
|
|
6712
6737
|
generate
|
|
6713
6738
|
};
|
package/dist/types.d.ts
CHANGED
|
@@ -23,6 +23,8 @@ export interface Config {
|
|
|
23
23
|
server: string;
|
|
24
24
|
};
|
|
25
25
|
softDeleteColumn?: string | null;
|
|
26
|
+
/** Per-table overrides for soft delete column. Use `null` to disable soft deletes for a specific table. */
|
|
27
|
+
softDeleteColumnOverrides?: Record<string, string | null>;
|
|
26
28
|
dateType?: "date" | "string";
|
|
27
29
|
numericMode?: "string" | "number" | "auto";
|
|
28
30
|
includeMethodsDepth?: number;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "postgresdk",
|
|
3
|
-
"version": "0.18.
|
|
3
|
+
"version": "0.18.23",
|
|
4
4
|
"description": "Generate a typed server/client SDK from a Postgres schema (includes, Zod, Hono).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
},
|
|
23
23
|
"scripts": {
|
|
24
24
|
"build": "bun build src/cli.ts src/index.ts --outdir dist --target node --format esm --external=pg --external=zod --external=hono --external=prompts --external=node:* && tsc -p tsconfig.build.json --emitDeclarationOnly",
|
|
25
|
-
"test": "bun test:write-files && bun test:init && bun test:gen && bun test test/test-where-clause.test.ts && bun test test/test-where-or-and.test.ts && bun test test/test-nested-include-options.test.ts && bun test test/test-include-methods-with-options.test.ts && bun test:gen-with-tests && bun test:pull && bun test:enums && bun test:typecheck && bun test:drizzle-e2e && bun test test/test-numeric-mode-integration.test.ts && bun test test/test-jsonb-array-serialization.test.ts && bun test test/test-trigram-search.test.ts",
|
|
25
|
+
"test": "bun test:write-files && bun test:init && bun test:gen && bun test test/test-where-clause.test.ts && bun test test/test-where-or-and.test.ts && bun test test/test-nested-include-options.test.ts && bun test test/test-include-methods-with-options.test.ts && bun test:gen-with-tests && bun test:pull && bun test:enums && bun test:typecheck && bun test:drizzle-e2e && bun test test/test-numeric-mode-integration.test.ts && bun test test/test-jsonb-array-serialization.test.ts && bun test test/test-trigram-search.test.ts && bun test test/test-soft-delete-config.test.ts",
|
|
26
26
|
"test:write-files": "bun test/test-write-files-if-changed.ts",
|
|
27
27
|
"test:init": "bun test/test-init.ts",
|
|
28
28
|
"test:gen": "bun test/test-gen.ts",
|