postgresdk 0.18.11 → 0.18.12

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 +47 -29
  2. package/dist/index.js +47 -29
  3. package/package.json +2 -2
package/dist/cli.js CHANGED
@@ -5,25 +5,43 @@ var __getProtoOf = Object.getPrototypeOf;
5
5
  var __defProp = Object.defineProperty;
6
6
  var __getOwnPropNames = Object.getOwnPropertyNames;
7
7
  var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ function __accessProp(key) {
9
+ return this[key];
10
+ }
11
+ var __toESMCache_node;
12
+ var __toESMCache_esm;
8
13
  var __toESM = (mod, isNodeMode, target) => {
14
+ var canCache = mod != null && typeof mod === "object";
15
+ if (canCache) {
16
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
17
+ var cached = cache.get(mod);
18
+ if (cached)
19
+ return cached;
20
+ }
9
21
  target = mod != null ? __create(__getProtoOf(mod)) : {};
10
22
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
11
23
  for (let key of __getOwnPropNames(mod))
12
24
  if (!__hasOwnProp.call(to, key))
13
25
  __defProp(to, key, {
14
- get: () => mod[key],
26
+ get: __accessProp.bind(mod, key),
15
27
  enumerable: true
16
28
  });
29
+ if (canCache)
30
+ cache.set(mod, to);
17
31
  return to;
18
32
  };
19
33
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
34
+ var __returnValue = (v) => v;
35
+ function __exportSetter(name, newValue) {
36
+ this[name] = __returnValue.bind(null, newValue);
37
+ }
20
38
  var __export = (target, all) => {
21
39
  for (var name in all)
22
40
  __defProp(target, name, {
23
41
  get: all[name],
24
42
  enumerable: true,
25
43
  configurable: true,
26
- set: (newValue) => all[name] = () => newValue
44
+ set: __exportSetter.bind(all, name)
27
45
  });
28
46
  };
29
47
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -3168,11 +3186,15 @@ function isVectorType(pgType) {
3168
3186
  const t = pgType.toLowerCase();
3169
3187
  return t === "vector" || t === "halfvec" || t === "sparsevec" || t === "bit";
3170
3188
  }
3189
+ function isJsonbType(pgType) {
3190
+ return pgType.toLowerCase() === "json" || pgType.toLowerCase() === "jsonb";
3191
+ }
3171
3192
  function emitHonoRoutes(table, _graph, opts) {
3172
3193
  const fileTableName = table.name;
3173
3194
  const Type = pascal(table.name);
3174
3195
  const hasVectorColumns = table.columns.some((c) => isVectorType(c.pgType));
3175
3196
  const vectorColumns = table.columns.filter((c) => isVectorType(c.pgType)).map((c) => c.name);
3197
+ const jsonbColumns = table.columns.filter((c) => isJsonbType(c.pgType)).map((c) => c.name);
3176
3198
  const rawPk = table.pk;
3177
3199
  const pkCols = Array.isArray(rawPk) ? rawPk : rawPk ? [rawPk] : [];
3178
3200
  const safePkCols = pkCols.length ? pkCols : ["id"];
@@ -3259,7 +3281,8 @@ export function register${Type}Routes(app: Hono, deps: { pg: { query: (text: str
3259
3281
  softDeleteColumn: ${softDel ? `"${softDel}"` : "null"},
3260
3282
  includeMethodsDepth: ${opts.includeMethodsDepth},
3261
3283
  allColumnNames: [${table.columns.map((c) => `"${c.name}"`).join(", ")}]${vectorColumns.length > 0 ? `,
3262
- vectorColumns: ${JSON.stringify(vectorColumns)}` : ""}
3284
+ vectorColumns: ${JSON.stringify(vectorColumns)}` : ""}${jsonbColumns.length > 0 ? `,
3285
+ jsonbColumns: ${JSON.stringify(jsonbColumns)}` : ""}
3263
3286
  };
3264
3287
  ${hasAuth ? `
3265
3288
  // \uD83D\uDD10 Auth: protect all routes for this table
@@ -3477,7 +3500,7 @@ function isVectorType2(pgType) {
3477
3500
  const t = pgType.toLowerCase();
3478
3501
  return t === "vector" || t === "halfvec" || t === "sparsevec" || t === "bit";
3479
3502
  }
3480
- function isJsonbType(pgType) {
3503
+ function isJsonbType2(pgType) {
3481
3504
  const t = pgType.toLowerCase();
3482
3505
  return t === "json" || t === "jsonb";
3483
3506
  }
@@ -3503,7 +3526,7 @@ function emitClient(table, graph, opts, model) {
3503
3526
  const Type = pascal(table.name);
3504
3527
  const ext = opts.useJsExtensions ? ".js" : "";
3505
3528
  const hasVectorColumns = table.columns.some((c) => isVectorType2(c.pgType));
3506
- const hasJsonbColumns = table.columns.some((c) => isJsonbType(c.pgType));
3529
+ const hasJsonbColumns = table.columns.some((c) => isJsonbType2(c.pgType));
3507
3530
  if (process.env.SDK_DEBUG) {
3508
3531
  const vectorCols = table.columns.filter((c) => isVectorType2(c.pgType));
3509
3532
  if (vectorCols.length > 0) {
@@ -4973,14 +4996,14 @@ function tsTypeFor(pgType, opts, enums) {
4973
4996
  return "number[]";
4974
4997
  return "string";
4975
4998
  }
4976
- function isJsonbType2(pgType) {
4999
+ function isJsonbType3(pgType) {
4977
5000
  const t = pgType.toLowerCase();
4978
5001
  return t === "json" || t === "jsonb";
4979
5002
  }
4980
5003
  var pascal2 = (s) => s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
4981
5004
  function emitTypes(table, opts, enums) {
4982
5005
  const Type = pascal2(table.name);
4983
- const hasJsonbColumns = table.columns.some((col) => isJsonbType2(col.pgType));
5006
+ const hasJsonbColumns = table.columns.some((col) => isJsonbType3(col.pgType));
4984
5007
  const insertFields = table.columns.map((col) => {
4985
5008
  const base = tsTypeFor(col.pgType, opts, enums);
4986
5009
  const optional = col.hasDefault || col.nullable ? "?" : "";
@@ -5694,6 +5717,7 @@ export interface OperationContext {
5694
5717
  softDeleteColumn?: string | null;
5695
5718
  includeMethodsDepth: number;
5696
5719
  vectorColumns?: string[];
5720
+ jsonbColumns?: string[];
5697
5721
  allColumnNames?: string[];
5698
5722
  select?: string[];
5699
5723
  exclude?: string[];
@@ -5748,22 +5772,6 @@ function buildColumnList(
5748
5772
  return finalColumns.map(col => \`"\${col}"\`).join(", ");
5749
5773
  }
5750
5774
 
5751
- /**
5752
- * Prepare query parameters for PostgreSQL.
5753
- * The pg library should handle JSONB automatically, but there are edge cases
5754
- * where explicit stringification is needed (e.g., certain pg versions or when
5755
- * objects have been through serialization/deserialization).
5756
- */
5757
- function prepareParams(params: any[]): any[] {
5758
- return params.map(p => {
5759
- if (p === null || p === undefined) return p;
5760
- // Stringify objects/arrays for JSONB - while pg should handle this automatically,
5761
- // we've observed cases where it fails without explicit stringification
5762
- if (typeof p === 'object') return JSON.stringify(p);
5763
- return p;
5764
- });
5765
- }
5766
-
5767
5775
  /**
5768
5776
  * Parse vector columns in retrieved rows.
5769
5777
  * pgvector returns vectors as strings (e.g., "[1.5,2.5,3.5]") which need to be
@@ -5809,8 +5817,13 @@ export async function createRecord(
5809
5817
  VALUES (\${placeholders})
5810
5818
  RETURNING \${returningClause}\`;
5811
5819
 
5812
- log.debug("SQL:", text, "vals:", vals);
5813
- const { rows } = await ctx.pg.query(text, prepareParams(vals));
5820
+ const preparedVals = vals.map((v, i) =>
5821
+ v !== null && v !== undefined && typeof v === 'object' && ctx.jsonbColumns?.includes(cols[i])
5822
+ ? JSON.stringify(v)
5823
+ : v
5824
+ );
5825
+ log.debug("SQL:", text, "vals:", preparedVals);
5826
+ const { rows } = await ctx.pg.query(text, preparedVals);
5814
5827
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
5815
5828
 
5816
5829
  return { data: parsedRows[0] ?? null, status: parsedRows[0] ? 201 : 500 };
@@ -5852,7 +5865,7 @@ export async function getByPk(
5852
5865
  const text = \`SELECT \${columns} FROM "\${ctx.table}" WHERE \${wherePkSql} LIMIT 1\`;
5853
5866
  log.debug(\`GET \${ctx.table} by PK:\`, pkValues, "SQL:", text);
5854
5867
 
5855
- const { rows } = await ctx.pg.query(text, prepareParams(pkValues));
5868
+ const { rows } = await ctx.pg.query(text, pkValues);
5856
5869
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
5857
5870
 
5858
5871
  if (!parsedRows[0]) {
@@ -6324,10 +6337,15 @@ export async function updateRecord(
6324
6337
 
6325
6338
  const returningClause = buildColumnList(ctx.select, ctx.exclude, ctx.allColumnNames);
6326
6339
  const text = \`UPDATE "\${ctx.table}" SET \${setSql} WHERE \${wherePkSql} RETURNING \${returningClause}\`;
6327
- const params = [...pkValues, ...Object.values(filteredData)];
6340
+ const updateVals = Object.entries(filteredData).map(([col, v]) =>
6341
+ v !== null && v !== undefined && typeof v === 'object' && ctx.jsonbColumns?.includes(col)
6342
+ ? JSON.stringify(v)
6343
+ : v
6344
+ );
6345
+ const params = [...pkValues, ...updateVals];
6328
6346
 
6329
6347
  log.debug(\`PATCH \${ctx.table} SQL:\`, text, "params:", params);
6330
- const { rows } = await ctx.pg.query(text, prepareParams(params));
6348
+ const { rows } = await ctx.pg.query(text, params);
6331
6349
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
6332
6350
 
6333
6351
  if (!parsedRows[0]) {
@@ -6378,7 +6396,7 @@ export async function deleteRecord(
6378
6396
  : \`DELETE FROM "\${ctx.table}" WHERE \${wherePkSql} RETURNING \${returningClause}\`;
6379
6397
 
6380
6398
  log.debug(\`DELETE \${ctx.softDeleteColumn ? '(soft)' : ''} \${ctx.table} SQL:\`, text, "pk:", pkValues);
6381
- const { rows } = await ctx.pg.query(text, prepareParams(pkValues));
6399
+ const { rows } = await ctx.pg.query(text, pkValues);
6382
6400
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
6383
6401
 
6384
6402
  if (!parsedRows[0]) {
package/dist/index.js CHANGED
@@ -4,25 +4,43 @@ var __getProtoOf = Object.getPrototypeOf;
4
4
  var __defProp = Object.defineProperty;
5
5
  var __getOwnPropNames = Object.getOwnPropertyNames;
6
6
  var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ function __accessProp(key) {
8
+ return this[key];
9
+ }
10
+ var __toESMCache_node;
11
+ var __toESMCache_esm;
7
12
  var __toESM = (mod, isNodeMode, target) => {
13
+ var canCache = mod != null && typeof mod === "object";
14
+ if (canCache) {
15
+ var cache = isNodeMode ? __toESMCache_node ??= new WeakMap : __toESMCache_esm ??= new WeakMap;
16
+ var cached = cache.get(mod);
17
+ if (cached)
18
+ return cached;
19
+ }
8
20
  target = mod != null ? __create(__getProtoOf(mod)) : {};
9
21
  const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
10
22
  for (let key of __getOwnPropNames(mod))
11
23
  if (!__hasOwnProp.call(to, key))
12
24
  __defProp(to, key, {
13
- get: () => mod[key],
25
+ get: __accessProp.bind(mod, key),
14
26
  enumerable: true
15
27
  });
28
+ if (canCache)
29
+ cache.set(mod, to);
16
30
  return to;
17
31
  };
18
32
  var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
33
+ var __returnValue = (v) => v;
34
+ function __exportSetter(name, newValue) {
35
+ this[name] = __returnValue.bind(null, newValue);
36
+ }
19
37
  var __export = (target, all) => {
20
38
  for (var name in all)
21
39
  __defProp(target, name, {
22
40
  get: all[name],
23
41
  enumerable: true,
24
42
  configurable: true,
25
- set: (newValue) => all[name] = () => newValue
43
+ set: __exportSetter.bind(all, name)
26
44
  });
27
45
  };
28
46
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
@@ -2207,11 +2225,15 @@ function isVectorType(pgType) {
2207
2225
  const t = pgType.toLowerCase();
2208
2226
  return t === "vector" || t === "halfvec" || t === "sparsevec" || t === "bit";
2209
2227
  }
2228
+ function isJsonbType(pgType) {
2229
+ return pgType.toLowerCase() === "json" || pgType.toLowerCase() === "jsonb";
2230
+ }
2210
2231
  function emitHonoRoutes(table, _graph, opts) {
2211
2232
  const fileTableName = table.name;
2212
2233
  const Type = pascal(table.name);
2213
2234
  const hasVectorColumns = table.columns.some((c) => isVectorType(c.pgType));
2214
2235
  const vectorColumns = table.columns.filter((c) => isVectorType(c.pgType)).map((c) => c.name);
2236
+ const jsonbColumns = table.columns.filter((c) => isJsonbType(c.pgType)).map((c) => c.name);
2215
2237
  const rawPk = table.pk;
2216
2238
  const pkCols = Array.isArray(rawPk) ? rawPk : rawPk ? [rawPk] : [];
2217
2239
  const safePkCols = pkCols.length ? pkCols : ["id"];
@@ -2298,7 +2320,8 @@ export function register${Type}Routes(app: Hono, deps: { pg: { query: (text: str
2298
2320
  softDeleteColumn: ${softDel ? `"${softDel}"` : "null"},
2299
2321
  includeMethodsDepth: ${opts.includeMethodsDepth},
2300
2322
  allColumnNames: [${table.columns.map((c) => `"${c.name}"`).join(", ")}]${vectorColumns.length > 0 ? `,
2301
- vectorColumns: ${JSON.stringify(vectorColumns)}` : ""}
2323
+ vectorColumns: ${JSON.stringify(vectorColumns)}` : ""}${jsonbColumns.length > 0 ? `,
2324
+ jsonbColumns: ${JSON.stringify(jsonbColumns)}` : ""}
2302
2325
  };
2303
2326
  ${hasAuth ? `
2304
2327
  // \uD83D\uDD10 Auth: protect all routes for this table
@@ -2516,7 +2539,7 @@ function isVectorType2(pgType) {
2516
2539
  const t = pgType.toLowerCase();
2517
2540
  return t === "vector" || t === "halfvec" || t === "sparsevec" || t === "bit";
2518
2541
  }
2519
- function isJsonbType(pgType) {
2542
+ function isJsonbType2(pgType) {
2520
2543
  const t = pgType.toLowerCase();
2521
2544
  return t === "json" || t === "jsonb";
2522
2545
  }
@@ -2542,7 +2565,7 @@ function emitClient(table, graph, opts, model) {
2542
2565
  const Type = pascal(table.name);
2543
2566
  const ext = opts.useJsExtensions ? ".js" : "";
2544
2567
  const hasVectorColumns = table.columns.some((c) => isVectorType2(c.pgType));
2545
- const hasJsonbColumns = table.columns.some((c) => isJsonbType(c.pgType));
2568
+ const hasJsonbColumns = table.columns.some((c) => isJsonbType2(c.pgType));
2546
2569
  if (process.env.SDK_DEBUG) {
2547
2570
  const vectorCols = table.columns.filter((c) => isVectorType2(c.pgType));
2548
2571
  if (vectorCols.length > 0) {
@@ -4012,14 +4035,14 @@ function tsTypeFor(pgType, opts, enums) {
4012
4035
  return "number[]";
4013
4036
  return "string";
4014
4037
  }
4015
- function isJsonbType2(pgType) {
4038
+ function isJsonbType3(pgType) {
4016
4039
  const t = pgType.toLowerCase();
4017
4040
  return t === "json" || t === "jsonb";
4018
4041
  }
4019
4042
  var pascal2 = (s) => s.split(/[_\s-]+/).map((w) => w?.[0] ? w[0].toUpperCase() + w.slice(1) : "").join("");
4020
4043
  function emitTypes(table, opts, enums) {
4021
4044
  const Type = pascal2(table.name);
4022
- const hasJsonbColumns = table.columns.some((col) => isJsonbType2(col.pgType));
4045
+ const hasJsonbColumns = table.columns.some((col) => isJsonbType3(col.pgType));
4023
4046
  const insertFields = table.columns.map((col) => {
4024
4047
  const base = tsTypeFor(col.pgType, opts, enums);
4025
4048
  const optional = col.hasDefault || col.nullable ? "?" : "";
@@ -4733,6 +4756,7 @@ export interface OperationContext {
4733
4756
  softDeleteColumn?: string | null;
4734
4757
  includeMethodsDepth: number;
4735
4758
  vectorColumns?: string[];
4759
+ jsonbColumns?: string[];
4736
4760
  allColumnNames?: string[];
4737
4761
  select?: string[];
4738
4762
  exclude?: string[];
@@ -4787,22 +4811,6 @@ function buildColumnList(
4787
4811
  return finalColumns.map(col => \`"\${col}"\`).join(", ");
4788
4812
  }
4789
4813
 
4790
- /**
4791
- * Prepare query parameters for PostgreSQL.
4792
- * The pg library should handle JSONB automatically, but there are edge cases
4793
- * where explicit stringification is needed (e.g., certain pg versions or when
4794
- * objects have been through serialization/deserialization).
4795
- */
4796
- function prepareParams(params: any[]): any[] {
4797
- return params.map(p => {
4798
- if (p === null || p === undefined) return p;
4799
- // Stringify objects/arrays for JSONB - while pg should handle this automatically,
4800
- // we've observed cases where it fails without explicit stringification
4801
- if (typeof p === 'object') return JSON.stringify(p);
4802
- return p;
4803
- });
4804
- }
4805
-
4806
4814
  /**
4807
4815
  * Parse vector columns in retrieved rows.
4808
4816
  * pgvector returns vectors as strings (e.g., "[1.5,2.5,3.5]") which need to be
@@ -4848,8 +4856,13 @@ export async function createRecord(
4848
4856
  VALUES (\${placeholders})
4849
4857
  RETURNING \${returningClause}\`;
4850
4858
 
4851
- log.debug("SQL:", text, "vals:", vals);
4852
- const { rows } = await ctx.pg.query(text, prepareParams(vals));
4859
+ const preparedVals = vals.map((v, i) =>
4860
+ v !== null && v !== undefined && typeof v === 'object' && ctx.jsonbColumns?.includes(cols[i])
4861
+ ? JSON.stringify(v)
4862
+ : v
4863
+ );
4864
+ log.debug("SQL:", text, "vals:", preparedVals);
4865
+ const { rows } = await ctx.pg.query(text, preparedVals);
4853
4866
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
4854
4867
 
4855
4868
  return { data: parsedRows[0] ?? null, status: parsedRows[0] ? 201 : 500 };
@@ -4891,7 +4904,7 @@ export async function getByPk(
4891
4904
  const text = \`SELECT \${columns} FROM "\${ctx.table}" WHERE \${wherePkSql} LIMIT 1\`;
4892
4905
  log.debug(\`GET \${ctx.table} by PK:\`, pkValues, "SQL:", text);
4893
4906
 
4894
- const { rows } = await ctx.pg.query(text, prepareParams(pkValues));
4907
+ const { rows } = await ctx.pg.query(text, pkValues);
4895
4908
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
4896
4909
 
4897
4910
  if (!parsedRows[0]) {
@@ -5363,10 +5376,15 @@ export async function updateRecord(
5363
5376
 
5364
5377
  const returningClause = buildColumnList(ctx.select, ctx.exclude, ctx.allColumnNames);
5365
5378
  const text = \`UPDATE "\${ctx.table}" SET \${setSql} WHERE \${wherePkSql} RETURNING \${returningClause}\`;
5366
- const params = [...pkValues, ...Object.values(filteredData)];
5379
+ const updateVals = Object.entries(filteredData).map(([col, v]) =>
5380
+ v !== null && v !== undefined && typeof v === 'object' && ctx.jsonbColumns?.includes(col)
5381
+ ? JSON.stringify(v)
5382
+ : v
5383
+ );
5384
+ const params = [...pkValues, ...updateVals];
5367
5385
 
5368
5386
  log.debug(\`PATCH \${ctx.table} SQL:\`, text, "params:", params);
5369
- const { rows } = await ctx.pg.query(text, prepareParams(params));
5387
+ const { rows } = await ctx.pg.query(text, params);
5370
5388
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
5371
5389
 
5372
5390
  if (!parsedRows[0]) {
@@ -5417,7 +5435,7 @@ export async function deleteRecord(
5417
5435
  : \`DELETE FROM "\${ctx.table}" WHERE \${wherePkSql} RETURNING \${returningClause}\`;
5418
5436
 
5419
5437
  log.debug(\`DELETE \${ctx.softDeleteColumn ? '(soft)' : ''} \${ctx.table} SQL:\`, text, "pk:", pkValues);
5420
- const { rows } = await ctx.pg.query(text, prepareParams(pkValues));
5438
+ const { rows } = await ctx.pg.query(text, pkValues);
5421
5439
  const parsedRows = parseVectorColumns(rows, ctx.vectorColumns);
5422
5440
 
5423
5441
  if (!parsedRows[0]) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "postgresdk",
3
- "version": "0.18.11",
3
+ "version": "0.18.12",
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",
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",
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",