@b4moss/crudian 0.9.0 → 0.10.0

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 (45) hide show
  1. package/README.md +32 -8
  2. package/dist/bun-sqlite/sql.d.ts +5 -1
  3. package/dist/bun-sqlite/sql.d.ts.map +1 -1
  4. package/dist/bun-sqlite/sql.js +7 -1
  5. package/dist/dialect/index.d.ts +7 -0
  6. package/dist/dialect/index.d.ts.map +1 -0
  7. package/dist/dialect/index.js +19 -0
  8. package/dist/dialect/mysql.d.ts +4 -0
  9. package/dist/dialect/mysql.d.ts.map +1 -0
  10. package/dist/dialect/mysql.js +24 -0
  11. package/dist/dialect/postgres.d.ts +4 -0
  12. package/dist/dialect/postgres.d.ts.map +1 -0
  13. package/dist/dialect/postgres.js +25 -0
  14. package/dist/dialect/sqlite.d.ts +4 -0
  15. package/dist/dialect/sqlite.d.ts.map +1 -0
  16. package/dist/dialect/sqlite.js +23 -0
  17. package/dist/dialect/types.d.ts +25 -0
  18. package/dist/dialect/types.d.ts.map +1 -0
  19. package/dist/dialect/types.js +5 -0
  20. package/dist/index.d.ts +2 -1
  21. package/dist/index.d.ts.map +1 -1
  22. package/dist/pool.d.ts +18 -0
  23. package/dist/pool.d.ts.map +1 -0
  24. package/dist/pool.js +1 -0
  25. package/dist/prisma/crud.d.ts +2 -1
  26. package/dist/prisma/crud.d.ts.map +1 -1
  27. package/dist/prisma/crud.js +7 -4
  28. package/dist/prisma/server-dialect-harness.d.ts +16 -0
  29. package/dist/prisma/server-dialect-harness.d.ts.map +1 -0
  30. package/dist/prisma/server-dialect-harness.js +120 -0
  31. package/dist/sqlite/async-crud.d.ts +4 -0
  32. package/dist/sqlite/async-crud.d.ts.map +1 -1
  33. package/dist/sqlite/async-crud.js +57 -36
  34. package/dist/sqlite/pk.d.ts +6 -13
  35. package/dist/sqlite/pk.d.ts.map +1 -1
  36. package/dist/sqlite/pk.js +13 -6
  37. package/dist/sqlite/sql.d.ts +10 -1
  38. package/dist/sqlite/sql.d.ts.map +1 -1
  39. package/dist/sqlite/sql.js +60 -23
  40. package/dist/sqlite/sync-crud.d.ts +4 -0
  41. package/dist/sqlite/sync-crud.d.ts.map +1 -1
  42. package/dist/sqlite/sync-crud.js +48 -35
  43. package/dist/types.d.ts +7 -0
  44. package/dist/types.d.ts.map +1 -1
  45. package/package.json +5 -1
@@ -1,15 +1,16 @@
1
1
  import { CrudianError, assertString, } from "../index.js";
2
+ import { sqliteDialect } from "../dialect/sqlite.js";
2
3
  import { createAsyncPkGuard, resolvePk } from "./pk.js";
3
- import { compileWhere, quoteIdent, resolveWhere } from "./sql.js";
4
+ import { compileWhere, resolveWhere } from "./sql.js";
4
5
  function requireWhere(query, label) {
5
6
  if (query.where === undefined || query.where === null) {
6
7
  throw new CrudianError(`${label} requires where`);
7
8
  }
8
9
  }
9
- function selectColumns(columns) {
10
+ function selectColumns(d, columns) {
10
11
  if (!columns || columns.length === 0)
11
12
  return "*";
12
- return columns.map((c) => quoteIdent(c)).join(", ");
13
+ return columns.map((c) => d.quoteIdent(c)).join(", ");
13
14
  }
14
15
  function rowFromObject(value) {
15
16
  if (value === null || typeof value !== "object") {
@@ -17,9 +18,11 @@ function rowFromObject(value) {
17
18
  }
18
19
  return { ...value };
19
20
  }
20
- export function createAsyncSqliteCrud(db, ex, options) {
21
+ /** Dialect-aware async CRUD factory. */
22
+ export function createAsyncCrud(db, ex, dialect, options) {
21
23
  const pk = resolvePk(options);
22
- const ensurePkColumn = createAsyncPkGuard(pk, ex.all);
24
+ const ensurePkColumn = createAsyncPkGuard(pk, dialect, ex.all);
25
+ const d = dialect;
23
26
  const crud = {
24
27
  db,
25
28
  async create(table, cols) {
@@ -32,21 +35,30 @@ export function createAsyncSqliteCrud(db, ex, options) {
32
35
  if (keys.length === 0) {
33
36
  throw new CrudianError("cols must not be empty");
34
37
  }
35
- const tbl = quoteIdent(table);
36
- const colSql = keys.map((k) => quoteIdent(k)).join(", ");
37
- const placeholders = keys.map(() => "?").join(", ");
38
+ const tbl = d.quoteIdent(table);
39
+ const colSql = keys.map((k) => d.quoteIdent(k)).join(", ");
40
+ let idx = 1;
41
+ const placeholders = keys.map(() => d.placeholder(idx++)).join(", ");
38
42
  const args = keys.map((k) => cols[k]);
39
- // Single-statement insert+fetch avoids last_insert_rowid() across pooled
40
- // connections (Prisma SQLite), which can return an empty row after COUNT.
41
- const row = await ex.get(`INSERT INTO ${tbl} (${colSql}) VALUES (${placeholders}) RETURNING *`, args);
43
+ if (d.supportsInsertReturning) {
44
+ // Single-statement insert+fetch avoids last_insert_rowid() across pooled
45
+ // connections (Prisma SQLite), which can return an empty row after COUNT.
46
+ const row = await ex.get(`INSERT INTO ${tbl} (${colSql}) VALUES (${placeholders}) RETURNING *`, args);
47
+ return rowFromObject(row);
48
+ }
49
+ await ex.run(`INSERT INTO ${tbl} (${colSql}) VALUES (${placeholders})`, args);
50
+ const pkValue = Object.prototype.hasOwnProperty.call(cols, pk)
51
+ ? cols[pk]
52
+ : Number((await ex.get(d.lastInsertIdSql()))?.id);
53
+ const row = await ex.get(`SELECT * FROM ${tbl} WHERE ${d.quoteIdent(pk)} = ${d.placeholder(1)}`, [pkValue]);
42
54
  return rowFromObject(row);
43
55
  },
44
56
  async read(table, query = {}) {
45
57
  assertString(table, "table");
46
58
  await ensurePkColumn(table);
47
- const tbl = quoteIdent(table);
48
- const where = compileWhere(resolveWhere(query.where));
49
- const sql = `SELECT ${selectColumns(query.columns)} FROM ${tbl}` +
59
+ const tbl = d.quoteIdent(table);
60
+ const where = compileWhere(d, resolveWhere(query.where));
61
+ const sql = `SELECT ${selectColumns(d, query.columns)} FROM ${tbl}` +
50
62
  (where.sql ? ` WHERE ${where.sql}` : "") +
51
63
  ` LIMIT 1`;
52
64
  const row = await ex.get(sql, where.args);
@@ -63,25 +75,27 @@ export function createAsyncSqliteCrud(db, ex, options) {
63
75
  if (keys.length === 0) {
64
76
  throw new CrudianError("cols must not be empty");
65
77
  }
66
- const tbl = quoteIdent(table);
67
- const where = compileWhere(resolveWhere(query.where));
78
+ const tbl = d.quoteIdent(table);
79
+ let idx = 1;
80
+ const sets = keys.map((k) => `${d.quoteIdent(k)} = ${d.placeholder(idx++)}`).join(", ");
81
+ const where = compileWhere(d, resolveWhere(query.where), idx);
68
82
  if (!where.sql) {
69
83
  throw new CrudianError("update requires where");
70
84
  }
71
- const sets = keys.map((k) => `${quoteIdent(k)} = ?`).join(", ");
72
85
  const args = [...keys.map((k) => cols[k]), ...where.args];
73
86
  const result = await ex.run(`UPDATE ${tbl} SET ${sets} WHERE ${where.sql}`, args);
74
87
  if (result.changes === 0)
75
88
  return null;
76
- const row = await ex.get(`SELECT * FROM ${tbl} WHERE ${where.sql} LIMIT 1`, where.args);
89
+ const fetchWhere = compileWhere(d, resolveWhere(query.where));
90
+ const row = await ex.get(`SELECT * FROM ${tbl} WHERE ${fetchWhere.sql} LIMIT 1`, fetchWhere.args);
77
91
  return row == null ? null : rowFromObject(row);
78
92
  },
79
93
  async delete(table, query) {
80
94
  assertString(table, "table");
81
95
  await ensurePkColumn(table);
82
96
  requireWhere(query, "delete");
83
- const tbl = quoteIdent(table);
84
- const where = compileWhere(resolveWhere(query.where));
97
+ const tbl = d.quoteIdent(table);
98
+ const where = compileWhere(d, resolveWhere(query.where));
85
99
  if (!where.sql) {
86
100
  throw new CrudianError("delete requires where");
87
101
  }
@@ -110,8 +124,8 @@ export function createAsyncSqliteCrud(db, ex, options) {
110
124
  typeof query.cursor !== "string") {
111
125
  throw new CrudianError("cursor must be a number, string, or null");
112
126
  }
113
- const tbl = quoteIdent(table);
114
- const where = compileWhere(resolveWhere(query.where));
127
+ const tbl = d.quoteIdent(table);
128
+ const where = compileWhere(d, resolveWhere(query.where));
115
129
  const total = await crud.count(table, { where: query.where });
116
130
  if (paging === "offset") {
117
131
  const offset = query.offset ?? 0;
@@ -119,10 +133,11 @@ export function createAsyncSqliteCrud(db, ex, options) {
119
133
  throw new CrudianError("offset must be a non-negative number");
120
134
  }
121
135
  const args = [...where.args];
136
+ let idx = where.nextIndex;
122
137
  const whereSql = where.sql ? ` WHERE ${where.sql}` : "";
123
- const sql = `SELECT ${selectColumns(query.columns)} FROM ${tbl}` +
138
+ const sql = `SELECT ${selectColumns(d, query.columns)} FROM ${tbl}` +
124
139
  whereSql +
125
- ` ORDER BY ${quoteIdent(pk)} ASC LIMIT ? OFFSET ?`;
140
+ ` ORDER BY ${d.quoteIdent(pk)} ASC LIMIT ${d.placeholder(idx++)} OFFSET ${d.placeholder(idx++)}`;
126
141
  args.push(limit, offset);
127
142
  const items = (await ex.all(sql, args)).map((r) => rowFromObject(r));
128
143
  return {
@@ -134,17 +149,18 @@ export function createAsyncSqliteCrud(db, ex, options) {
134
149
  };
135
150
  }
136
151
  const args = [...where.args];
152
+ let idx = where.nextIndex;
137
153
  const parts = [];
138
154
  if (where.sql)
139
155
  parts.push(`(${where.sql})`);
140
156
  if (query.cursor != null) {
141
- parts.push(`${quoteIdent(pk)} > ?`);
157
+ parts.push(`${d.quoteIdent(pk)} > ${d.placeholder(idx++)}`);
142
158
  args.push(query.cursor);
143
159
  }
144
160
  const whereSql = parts.length > 0 ? ` WHERE ${parts.join(" AND ")}` : "";
145
- const sql = `SELECT ${selectColumns(query.columns)} FROM ${tbl}` +
161
+ const sql = `SELECT ${selectColumns(d, query.columns)} FROM ${tbl}` +
146
162
  whereSql +
147
- ` ORDER BY ${quoteIdent(pk)} ASC LIMIT ?`;
163
+ ` ORDER BY ${d.quoteIdent(pk)} ASC LIMIT ${d.placeholder(idx++)}`;
148
164
  args.push(limit + 1);
149
165
  const rows = (await ex.all(sql, args)).map((r) => rowFromObject(r));
150
166
  const hasMore = rows.length > limit;
@@ -162,9 +178,9 @@ export function createAsyncSqliteCrud(db, ex, options) {
162
178
  async count(table, query = {}) {
163
179
  assertString(table, "table");
164
180
  await ensurePkColumn(table);
165
- const tbl = quoteIdent(table);
166
- const where = compileWhere(resolveWhere(query.where));
167
- const sql = `SELECT COUNT(*) AS ${quoteIdent("row_count")} FROM ${tbl}` +
181
+ const tbl = d.quoteIdent(table);
182
+ const where = compileWhere(d, resolveWhere(query.where));
183
+ const sql = `SELECT COUNT(*) AS ${d.quoteIdent("row_count")} FROM ${tbl}` +
168
184
  (where.sql ? ` WHERE ${where.sql}` : "");
169
185
  const row = await ex.get(sql, where.args);
170
186
  return Number(row?.row_count ?? 0);
@@ -172,9 +188,9 @@ export function createAsyncSqliteCrud(db, ex, options) {
172
188
  async exists(table, query = {}) {
173
189
  assertString(table, "table");
174
190
  await ensurePkColumn(table);
175
- const tbl = quoteIdent(table);
176
- const where = compileWhere(resolveWhere(query.where));
177
- const sql = `SELECT 1 AS ${quoteIdent("ok")} FROM ${tbl}` +
191
+ const tbl = d.quoteIdent(table);
192
+ const where = compileWhere(d, resolveWhere(query.where));
193
+ const sql = `SELECT 1 AS ${d.quoteIdent("ok")} FROM ${tbl}` +
178
194
  (where.sql ? ` WHERE ${where.sql}` : "") +
179
195
  ` LIMIT 1`;
180
196
  const row = await ex.get(sql, where.args);
@@ -259,12 +275,13 @@ export function createAsyncSqliteCrud(db, ex, options) {
259
275
  if (keys.length === 0) {
260
276
  throw new CrudianError("cols must not be empty");
261
277
  }
262
- const tbl = quoteIdent(table);
263
- const where = compileWhere(resolveWhere(query.where));
278
+ const tbl = d.quoteIdent(table);
279
+ let idx = 1;
280
+ const sets = keys.map((k) => `${d.quoteIdent(k)} = ${d.placeholder(idx++)}`).join(", ");
281
+ const where = compileWhere(d, resolveWhere(query.where), idx);
264
282
  if (!where.sql) {
265
283
  throw new CrudianError("bulkUpdate requires where");
266
284
  }
267
- const sets = keys.map((k) => `${quoteIdent(k)} = ?`).join(", ");
268
285
  const args = [...keys.map((k) => cols[k]), ...where.args];
269
286
  const result = await ex.run(`UPDATE ${tbl} SET ${sets} WHERE ${where.sql}`, args);
270
287
  return Number(result.changes ?? 0);
@@ -302,3 +319,7 @@ export function createAsyncSqliteCrud(db, ex, options) {
302
319
  };
303
320
  return crud;
304
321
  }
322
+ /** SQLite async CRUD (compat). */
323
+ export function createAsyncSqliteCrud(db, ex, options) {
324
+ return createAsyncCrud(db, ex, sqliteDialect, options);
325
+ }
@@ -1,22 +1,15 @@
1
1
  import { type CreateCrudOptions } from "../index.js";
2
+ import type { Dialect } from "../dialect/types.js";
2
3
  /** Resolve PK column name from createCrud options. Default: `"id"`. */
3
4
  export declare function resolvePk(options?: CreateCrudOptions): string;
4
- export type PkColumnChecker = {
5
- get(sql: string, args?: unknown[]): {
6
- name?: unknown;
7
- } | RowLike | undefined;
8
- };
9
- export type AsyncPkColumnChecker = {
10
- get(sql: string, args?: unknown[]): Promise<{
11
- name?: unknown;
12
- } | RowLike | undefined>;
13
- };
14
5
  type RowLike = Record<string, unknown>;
15
6
  /**
16
- * Ensure `pk` exists on `table` via PRAGMA table_info. Caches per table.
7
+ * Ensure `pk` exists on `table` via dialect.describeColumns. Caches per table.
17
8
  * Throws CrudianError when the column is missing.
18
9
  */
19
- export declare function createPkGuard(pk: string, get: (sql: string, args?: unknown[]) => RowLike | undefined, all: (sql: string, args?: unknown[]) => RowLike[]): (table: string) => void;
20
- export declare function createAsyncPkGuard(pk: string, all: (sql: string, args?: unknown[]) => Promise<RowLike[]>): (table: string) => Promise<void>;
10
+ export declare function createPkGuard(pk: string, dialect: Dialect, all: (sql: string, args?: unknown[]) => RowLike[]): (table: string) => void;
11
+ export declare function createAsyncPkGuard(pk: string, dialect: Dialect, all: (sql: string, args?: unknown[]) => Promise<RowLike[]>): (table: string) => Promise<void>;
12
+ /** @deprecated Prefer createPkGuard(pk, dialect, all). */
13
+ export declare function createPkGuardSqlite(pk: string, get: (sql: string, args?: unknown[]) => RowLike | undefined, all: (sql: string, args?: unknown[]) => RowLike[]): (table: string) => void;
21
14
  export {};
22
15
  //# sourceMappingURL=pk.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pk.d.ts","sourceRoot":"","sources":["../../src/sqlite/pk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAGlE,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAW7D;AAED,MAAM,MAAM,eAAe,GAAG;IAC5B,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,GAAG,SAAS,CAAA;CAC7E,CAAA;AAED,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,CACD,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,OAAO,EAAE,GACf,OAAO,CAAC;QAAE,IAAI,CAAC,EAAE,OAAO,CAAA;KAAE,GAAG,OAAO,GAAG,SAAS,CAAC,CAAA;CACrD,CAAA;AAED,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEtC;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,SAAS,EAC3D,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,IAGlB,OAAO,MAAM,KAAG,IAAI,CAapD;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,IAGrB,OAAO,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAanE"}
1
+ {"version":3,"file":"pk.d.ts","sourceRoot":"","sources":["../../src/sqlite/pk.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAClE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,uEAAuE;AACvE,wBAAgB,SAAS,CAAC,OAAO,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAW7D;AAED,KAAK,OAAO,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEtC;;;GAGG;AACH,wBAAgB,aAAa,CAC3B,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,IAGlB,OAAO,MAAM,KAAG,IAAI,CAcpD;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,MAAM,EACV,OAAO,EAAE,OAAO,EAChB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,CAAC,OAAO,EAAE,CAAC,IAGrB,OAAO,MAAM,KAAG,OAAO,CAAC,IAAI,CAAC,CAcnE;AAED,0DAA0D;AAC1D,wBAAgB,mBAAmB,CACjC,EAAE,EAAE,MAAM,EACV,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,GAAG,SAAS,EAC3D,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,KAAK,OAAO,EAAE,WA1CX,MAAM,KAAG,IAAI,CA8CpD"}
package/dist/sqlite/pk.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { CrudianError } from "../index.js";
2
- import { quoteIdent } from "./sql.js";
2
+ import { sqliteDialect } from "../dialect/sqlite.js";
3
3
  /** Resolve PK column name from createCrud options. Default: `"id"`. */
4
4
  export function resolvePk(options) {
5
5
  if (options == null || options.pk === undefined) {
@@ -14,15 +14,16 @@ export function resolvePk(options) {
14
14
  return options.pk;
15
15
  }
16
16
  /**
17
- * Ensure `pk` exists on `table` via PRAGMA table_info. Caches per table.
17
+ * Ensure `pk` exists on `table` via dialect.describeColumns. Caches per table.
18
18
  * Throws CrudianError when the column is missing.
19
19
  */
20
- export function createPkGuard(pk, get, all) {
20
+ export function createPkGuard(pk, dialect, all) {
21
21
  const ok = new Set();
22
22
  return function ensurePkColumn(table) {
23
23
  if (ok.has(table))
24
24
  return;
25
- const rows = all(`PRAGMA table_info(${quoteIdent(table)})`);
25
+ const q = dialect.describeColumns(table);
26
+ const rows = all(q.sql, q.args);
26
27
  const names = new Set(rows.map((r) => String(r.name ?? "")).filter((n) => n.length > 0));
27
28
  if (!names.has(pk)) {
28
29
  throw new CrudianError(`pk column "${pk}" does not exist on table "${table}"`);
@@ -30,12 +31,13 @@ export function createPkGuard(pk, get, all) {
30
31
  ok.add(table);
31
32
  };
32
33
  }
33
- export function createAsyncPkGuard(pk, all) {
34
+ export function createAsyncPkGuard(pk, dialect, all) {
34
35
  const ok = new Set();
35
36
  return async function ensurePkColumn(table) {
36
37
  if (ok.has(table))
37
38
  return;
38
- const rows = await all(`PRAGMA table_info(${quoteIdent(table)})`);
39
+ const q = dialect.describeColumns(table);
40
+ const rows = await all(q.sql, q.args);
39
41
  const names = new Set(rows.map((r) => String(r.name ?? "")).filter((n) => n.length > 0));
40
42
  if (!names.has(pk)) {
41
43
  throw new CrudianError(`pk column "${pk}" does not exist on table "${table}"`);
@@ -43,3 +45,8 @@ export function createAsyncPkGuard(pk, all) {
43
45
  ok.add(table);
44
46
  };
45
47
  }
48
+ /** @deprecated Prefer createPkGuard(pk, dialect, all). */
49
+ export function createPkGuardSqlite(pk, get, all) {
50
+ void get;
51
+ return createPkGuard(pk, sqliteDialect, all);
52
+ }
@@ -1,8 +1,17 @@
1
1
  import { type WhereInput, type WhereNode } from "../index.js";
2
+ import type { Dialect } from "../dialect/types.js";
3
+ /** @deprecated Prefer dialect.quoteIdent; kept for local helper call sites. */
2
4
  export declare function quoteIdent(name: string): string;
3
5
  export declare function resolveWhere(input: WhereInput | undefined): WhereNode | undefined;
4
- export declare function compileWhere(node: WhereNode | undefined): {
6
+ export type CompiledWhere = {
5
7
  sql: string;
6
8
  args: unknown[];
9
+ /** Next 1-based placeholder index after this fragment. */
10
+ nextIndex: number;
7
11
  };
12
+ /**
13
+ * Compile a where AST using dialect placeholders.
14
+ * @param startIndex 1-based index for the first placeholder in this fragment.
15
+ */
16
+ export declare function compileWhere(dialect: Dialect, node: WhereNode | undefined, startIndex?: number): CompiledWhere;
8
17
  //# sourceMappingURL=sql.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/sqlite/sql.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,aAAa,CAAA;AAEpB,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAG/C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAIjF;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,SAAS,GAAG;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,EAAE,CAAA;CAAE,CAyD1F"}
1
+ {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/sqlite/sql.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,SAAS,EACf,MAAM,aAAa,CAAA;AACpB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAGlD,+EAA+E;AAC/E,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAE/C;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,GAAG,SAAS,GAAG,SAAS,CAIjF;AAED,MAAM,MAAM,aAAa,GAAG;IAC1B,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,OAAO,EAAE,CAAA;IACf,0DAA0D;IAC1D,SAAS,EAAE,MAAM,CAAA;CAClB,CAAA;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,OAAO,EAChB,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,UAAU,SAAI,GACb,aAAa,CA0Ff"}
@@ -1,7 +1,8 @@
1
- import { CrudianError, assertString, isWhereBuilder, } from "../index.js";
1
+ import { CrudianError, isWhereBuilder, } from "../index.js";
2
+ import { sqliteDialect } from "../dialect/sqlite.js";
3
+ /** @deprecated Prefer dialect.quoteIdent; kept for local helper call sites. */
2
4
  export function quoteIdent(name) {
3
- assertString(name, "identifier");
4
- return `"${name.replaceAll('"', '""')}"`;
5
+ return sqliteDialect.quoteIdent(name);
5
6
  }
6
7
  export function resolveWhere(input) {
7
8
  if (input === undefined)
@@ -10,51 +11,87 @@ export function resolveWhere(input) {
10
11
  return input.toNode();
11
12
  return input;
12
13
  }
13
- export function compileWhere(node) {
14
+ /**
15
+ * Compile a where AST using dialect placeholders.
16
+ * @param startIndex 1-based index for the first placeholder in this fragment.
17
+ */
18
+ export function compileWhere(dialect, node, startIndex = 1) {
14
19
  if (!node)
15
- return { sql: "", args: [] };
20
+ return { sql: "", args: [], nextIndex: startIndex };
16
21
  if (node.type === "and" || node.type === "or") {
17
22
  if (node.children.length === 0)
18
- return { sql: "", args: [] };
23
+ return { sql: "", args: [], nextIndex: startIndex };
19
24
  const parts = [];
20
25
  const args = [];
26
+ let idx = startIndex;
21
27
  for (const child of node.children) {
22
- const compiled = compileWhere(child);
28
+ const compiled = compileWhere(dialect, child, idx);
23
29
  if (!compiled.sql)
24
30
  continue;
25
31
  parts.push(`(${compiled.sql})`);
26
32
  args.push(...compiled.args);
33
+ idx = compiled.nextIndex;
27
34
  }
28
35
  if (parts.length === 0)
29
- return { sql: "", args: [] };
30
- if (parts.length === 1)
31
- return { sql: parts[0].slice(1, -1), args };
36
+ return { sql: "", args: [], nextIndex: startIndex };
37
+ if (parts.length === 1) {
38
+ return { sql: parts[0].slice(1, -1), args, nextIndex: idx };
39
+ }
32
40
  const joiner = node.type === "and" ? " AND " : " OR ";
33
- return { sql: parts.join(joiner), args };
41
+ return { sql: parts.join(joiner), args, nextIndex: idx };
34
42
  }
35
43
  if (node.type !== "cond") {
36
44
  throw new CrudianError("invalid where node");
37
45
  }
38
- const col = quoteIdent(node.column);
46
+ const col = dialect.quoteIdent(node.column);
47
+ let idx = startIndex;
39
48
  switch (node.op) {
40
49
  case "eq":
41
- return { sql: `${col} = ?`, args: [node.value] };
50
+ return {
51
+ sql: `${col} = ${dialect.placeholder(idx)}`,
52
+ args: [node.value],
53
+ nextIndex: idx + 1,
54
+ };
42
55
  case "ne":
43
- return { sql: `${col} <> ?`, args: [node.value] };
56
+ return {
57
+ sql: `${col} <> ${dialect.placeholder(idx)}`,
58
+ args: [node.value],
59
+ nextIndex: idx + 1,
60
+ };
44
61
  case "lt":
45
- return { sql: `${col} < ?`, args: [node.value] };
62
+ return {
63
+ sql: `${col} < ${dialect.placeholder(idx)}`,
64
+ args: [node.value],
65
+ nextIndex: idx + 1,
66
+ };
46
67
  case "gt":
47
- return { sql: `${col} > ?`, args: [node.value] };
68
+ return {
69
+ sql: `${col} > ${dialect.placeholder(idx)}`,
70
+ args: [node.value],
71
+ nextIndex: idx + 1,
72
+ };
48
73
  case "lte":
49
- return { sql: `${col} <= ?`, args: [node.value] };
74
+ return {
75
+ sql: `${col} <= ${dialect.placeholder(idx)}`,
76
+ args: [node.value],
77
+ nextIndex: idx + 1,
78
+ };
50
79
  case "gte":
51
- return { sql: `${col} >= ?`, args: [node.value] };
80
+ return {
81
+ sql: `${col} >= ${dialect.placeholder(idx)}`,
82
+ args: [node.value],
83
+ nextIndex: idx + 1,
84
+ };
52
85
  case "like":
53
- return { sql: `${col} LIKE ?`, args: [node.value] };
86
+ return {
87
+ sql: `${col} LIKE ${dialect.placeholder(idx)}`,
88
+ args: [node.value],
89
+ nextIndex: idx + 1,
90
+ };
54
91
  case "isNull":
55
- return { sql: `${col} IS NULL`, args: [] };
92
+ return { sql: `${col} IS NULL`, args: [], nextIndex: idx };
56
93
  case "isNotNull":
57
- return { sql: `${col} IS NOT NULL`, args: [] };
94
+ return { sql: `${col} IS NOT NULL`, args: [], nextIndex: idx };
58
95
  case "in": {
59
96
  const values = node.value;
60
97
  if (!Array.isArray(values)) {
@@ -63,8 +100,8 @@ export function compileWhere(node) {
63
100
  if (values.length === 0) {
64
101
  throw new CrudianError("in requires a non-empty array");
65
102
  }
66
- const placeholders = values.map(() => "?").join(", ");
67
- return { sql: `${col} IN (${placeholders})`, args: values };
103
+ const placeholders = values.map(() => dialect.placeholder(idx++)).join(", ");
104
+ return { sql: `${col} IN (${placeholders})`, args: values, nextIndex: idx };
68
105
  }
69
106
  default:
70
107
  throw new CrudianError(`unknown op: ${node.op}`);
@@ -1,4 +1,5 @@
1
1
  import { type CountQuery, type CreateCrudOptions, type DeleteQuery, type DuplicateQuery, type ReadQuery, type Row, type SearchQuery, type SearchResult, type UpdateQuery } from "../index.js";
2
+ import type { Dialect } from "../dialect/types.js";
2
3
  export type SyncSqliteExecutor = {
3
4
  run(sql: string, args?: unknown[]): {
4
5
  changes: number;
@@ -25,5 +26,8 @@ export type SyncSqliteCrud<TDb> = {
25
26
  exists(table: string, query?: CountQuery): boolean;
26
27
  transaction<T>(fn: () => T): T;
27
28
  };
29
+ /** Dialect-aware sync CRUD factory. */
30
+ export declare function createSyncCrud<TDb>(db: TDb, ex: SyncSqliteExecutor, dialect: Dialect, options?: CreateCrudOptions): SyncSqliteCrud<TDb>;
31
+ /** SQLite sync CRUD (compat). */
28
32
  export declare function createSyncSqliteCrud<TDb>(db: TDb, ex: SyncSqliteExecutor, options?: CreateCrudOptions): SyncSqliteCrud<TDb>;
29
33
  //# sourceMappingURL=sync-crud.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sync-crud.d.ts","sourceRoot":"","sources":["../../src/sqlite/sync-crud.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"sync-crud.d.ts","sourceRoot":"","sources":["../../src/sqlite/sync-crud.ts"],"names":[],"mappings":"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"}