@b4moss/crudian 0.8.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 +41 -7
  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 +5 -0
  32. package/dist/sqlite/async-crud.d.ts.map +1 -1
  33. package/dist/sqlite/async-crud.js +65 -33
  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 +5 -0
  41. package/dist/sqlite/sync-crud.d.ts.map +1 -1
  42. package/dist/sqlite/sync-crud.js +55 -32
  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 { createPkGuard, 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 createSyncSqliteCrud(db, ex, options) {
21
+ /** Dialect-aware sync CRUD factory. */
22
+ export function createSyncCrud(db, ex, dialect, options) {
21
23
  const pk = resolvePk(options);
22
- const ensurePkColumn = createPkGuard(pk, ex.get, ex.all);
24
+ const ensurePkColumn = createPkGuard(pk, dialect, ex.all);
25
+ const d = dialect;
23
26
  const crud = {
24
27
  db,
25
28
  create(table, cols) {
@@ -32,23 +35,24 @@ export function createSyncSqliteCrud(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
43
  ex.run(`INSERT INTO ${tbl} (${colSql}) VALUES (${placeholders})`, args);
40
44
  const pkValue = Object.prototype.hasOwnProperty.call(cols, pk)
41
45
  ? cols[pk]
42
- : Number(ex.get("SELECT last_insert_rowid() AS id")?.id);
43
- const row = ex.get(`SELECT * FROM ${tbl} WHERE ${quoteIdent(pk)} = ?`, [pkValue]);
46
+ : Number(ex.get(d.lastInsertIdSql())?.id);
47
+ const row = ex.get(`SELECT * FROM ${tbl} WHERE ${d.quoteIdent(pk)} = ${d.placeholder(1)}`, [pkValue]);
44
48
  return rowFromObject(row);
45
49
  },
46
50
  read(table, query = {}) {
47
51
  assertString(table, "table");
48
52
  ensurePkColumn(table);
49
- const tbl = quoteIdent(table);
50
- const where = compileWhere(resolveWhere(query.where));
51
- const sql = `SELECT ${selectColumns(query.columns)} FROM ${tbl}` +
53
+ const tbl = d.quoteIdent(table);
54
+ const where = compileWhere(d, resolveWhere(query.where));
55
+ const sql = `SELECT ${selectColumns(d, query.columns)} FROM ${tbl}` +
52
56
  (where.sql ? ` WHERE ${where.sql}` : "") +
53
57
  ` LIMIT 1`;
54
58
  const row = ex.get(sql, where.args);
@@ -65,25 +69,27 @@ export function createSyncSqliteCrud(db, ex, options) {
65
69
  if (keys.length === 0) {
66
70
  throw new CrudianError("cols must not be empty");
67
71
  }
68
- const tbl = quoteIdent(table);
69
- const where = compileWhere(resolveWhere(query.where));
72
+ const tbl = d.quoteIdent(table);
73
+ let idx = 1;
74
+ const sets = keys.map((k) => `${d.quoteIdent(k)} = ${d.placeholder(idx++)}`).join(", ");
75
+ const where = compileWhere(d, resolveWhere(query.where), idx);
70
76
  if (!where.sql) {
71
77
  throw new CrudianError("update requires where");
72
78
  }
73
- const sets = keys.map((k) => `${quoteIdent(k)} = ?`).join(", ");
74
79
  const args = [...keys.map((k) => cols[k]), ...where.args];
75
80
  const result = ex.run(`UPDATE ${tbl} SET ${sets} WHERE ${where.sql}`, args);
76
81
  if (result.changes === 0)
77
82
  return null;
78
- const row = ex.get(`SELECT * FROM ${tbl} WHERE ${where.sql} LIMIT 1`, where.args);
83
+ const fetchWhere = compileWhere(d, resolveWhere(query.where));
84
+ const row = ex.get(`SELECT * FROM ${tbl} WHERE ${fetchWhere.sql} LIMIT 1`, fetchWhere.args);
79
85
  return row == null ? null : rowFromObject(row);
80
86
  },
81
87
  delete(table, query) {
82
88
  assertString(table, "table");
83
89
  ensurePkColumn(table);
84
90
  requireWhere(query, "delete");
85
- const tbl = quoteIdent(table);
86
- const where = compileWhere(resolveWhere(query.where));
91
+ const tbl = d.quoteIdent(table);
92
+ const where = compileWhere(d, resolveWhere(query.where));
87
93
  if (!where.sql) {
88
94
  throw new CrudianError("delete requires where");
89
95
  }
@@ -112,8 +118,8 @@ export function createSyncSqliteCrud(db, ex, options) {
112
118
  typeof query.cursor !== "string") {
113
119
  throw new CrudianError("cursor must be a number, string, or null");
114
120
  }
115
- const tbl = quoteIdent(table);
116
- const where = compileWhere(resolveWhere(query.where));
121
+ const tbl = d.quoteIdent(table);
122
+ const where = compileWhere(d, resolveWhere(query.where));
117
123
  const total = crud.count(table, { where: query.where });
118
124
  if (paging === "offset") {
119
125
  const offset = query.offset ?? 0;
@@ -121,10 +127,11 @@ export function createSyncSqliteCrud(db, ex, options) {
121
127
  throw new CrudianError("offset must be a non-negative number");
122
128
  }
123
129
  const args = [...where.args];
130
+ let idx = where.nextIndex;
124
131
  const whereSql = where.sql ? ` WHERE ${where.sql}` : "";
125
- const sql = `SELECT ${selectColumns(query.columns)} FROM ${tbl}` +
132
+ const sql = `SELECT ${selectColumns(d, query.columns)} FROM ${tbl}` +
126
133
  whereSql +
127
- ` ORDER BY ${quoteIdent(pk)} ASC LIMIT ? OFFSET ?`;
134
+ ` ORDER BY ${d.quoteIdent(pk)} ASC LIMIT ${d.placeholder(idx++)} OFFSET ${d.placeholder(idx++)}`;
128
135
  args.push(limit, offset);
129
136
  const items = ex.all(sql, args).map((r) => rowFromObject(r));
130
137
  return {
@@ -136,17 +143,18 @@ export function createSyncSqliteCrud(db, ex, options) {
136
143
  };
137
144
  }
138
145
  const args = [...where.args];
146
+ let idx = where.nextIndex;
139
147
  const parts = [];
140
148
  if (where.sql)
141
149
  parts.push(`(${where.sql})`);
142
150
  if (query.cursor != null) {
143
- parts.push(`${quoteIdent(pk)} > ?`);
151
+ parts.push(`${d.quoteIdent(pk)} > ${d.placeholder(idx++)}`);
144
152
  args.push(query.cursor);
145
153
  }
146
154
  const whereSql = parts.length > 0 ? ` WHERE ${parts.join(" AND ")}` : "";
147
- const sql = `SELECT ${selectColumns(query.columns)} FROM ${tbl}` +
155
+ const sql = `SELECT ${selectColumns(d, query.columns)} FROM ${tbl}` +
148
156
  whereSql +
149
- ` ORDER BY ${quoteIdent(pk)} ASC LIMIT ?`;
157
+ ` ORDER BY ${d.quoteIdent(pk)} ASC LIMIT ${d.placeholder(idx++)}`;
150
158
  args.push(limit + 1);
151
159
  const rows = ex.all(sql, args).map((r) => rowFromObject(r));
152
160
  const hasMore = rows.length > limit;
@@ -164,13 +172,23 @@ export function createSyncSqliteCrud(db, ex, options) {
164
172
  count(table, query = {}) {
165
173
  assertString(table, "table");
166
174
  ensurePkColumn(table);
167
- const tbl = quoteIdent(table);
168
- const where = compileWhere(resolveWhere(query.where));
169
- const sql = `SELECT COUNT(*) AS ${quoteIdent("row_count")} FROM ${tbl}` +
175
+ const tbl = d.quoteIdent(table);
176
+ const where = compileWhere(d, resolveWhere(query.where));
177
+ const sql = `SELECT COUNT(*) AS ${d.quoteIdent("row_count")} FROM ${tbl}` +
170
178
  (where.sql ? ` WHERE ${where.sql}` : "");
171
179
  const row = ex.get(sql, where.args);
172
180
  return Number(row?.row_count ?? 0);
173
181
  },
182
+ exists(table, query = {}) {
183
+ assertString(table, "table");
184
+ ensurePkColumn(table);
185
+ const tbl = d.quoteIdent(table);
186
+ const where = compileWhere(d, resolveWhere(query.where));
187
+ const sql = `SELECT 1 AS ${d.quoteIdent("ok")} FROM ${tbl}` +
188
+ (where.sql ? ` WHERE ${where.sql}` : "") +
189
+ ` LIMIT 1`;
190
+ return ex.get(sql, where.args) != null;
191
+ },
174
192
  upsert(table, cols) {
175
193
  assertString(table, "table");
176
194
  ensurePkColumn(table);
@@ -250,12 +268,13 @@ export function createSyncSqliteCrud(db, ex, options) {
250
268
  if (keys.length === 0) {
251
269
  throw new CrudianError("cols must not be empty");
252
270
  }
253
- const tbl = quoteIdent(table);
254
- const where = compileWhere(resolveWhere(query.where));
271
+ const tbl = d.quoteIdent(table);
272
+ let idx = 1;
273
+ const sets = keys.map((k) => `${d.quoteIdent(k)} = ${d.placeholder(idx++)}`).join(", ");
274
+ const where = compileWhere(d, resolveWhere(query.where), idx);
255
275
  if (!where.sql) {
256
276
  throw new CrudianError("bulkUpdate requires where");
257
277
  }
258
- const sets = keys.map((k) => `${quoteIdent(k)} = ?`).join(", ");
259
278
  const args = [...keys.map((k) => cols[k]), ...where.args];
260
279
  const result = ex.run(`UPDATE ${tbl} SET ${sets} WHERE ${where.sql}`, args);
261
280
  return Number(result.changes ?? 0);
@@ -293,3 +312,7 @@ export function createSyncSqliteCrud(db, ex, options) {
293
312
  };
294
313
  return crud;
295
314
  }
315
+ /** SQLite sync CRUD (compat). */
316
+ export function createSyncSqliteCrud(db, ex, options) {
317
+ return createSyncCrud(db, ex, sqliteDialect, options);
318
+ }
package/dist/types.d.ts CHANGED
@@ -44,9 +44,16 @@ export type DuplicateQuery = {
44
44
  where: WhereInput;
45
45
  overrides?: Record<string, unknown>;
46
46
  };
47
+ /** SQL dialect for adapters that support more than SQLite (e.g. Prisma). */
48
+ export type CreateCrudDialect = "sqlite" | "postgres" | "mysql";
47
49
  /** Options for `createCrud(db, options?)`. */
48
50
  export type CreateCrudOptions = {
49
51
  /** Primary key column name. Default: `"id"`. */
50
52
  pk?: string;
53
+ /**
54
+ * SQL dialect. Default: `"sqlite"`.
55
+ * Used by Prisma (and future multi-DB adapters). SQLite-only adapters ignore this.
56
+ */
57
+ dialect?: CreateCrudDialect;
51
58
  };
52
59
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAEzD,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEzC,MAAM,MAAM,kBAAkB,CAAC,CAAC,GAAG,GAAG,IAAI;IACxC,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,kBAAkB,CAAC,CAAC,GAAG,GAAG,IAAI;IACxC,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,GAAG,IAAI,kBAAkB,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAA;AAEjF,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,SAAS,CAAA;AAEjD,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC5B,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,UAAU,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,UAAU,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,UAAU,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC,CAAA;AAED,8CAA8C;AAC9C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gDAAgD;IAChD,EAAE,CAAC,EAAE,MAAM,CAAA;CACZ,CAAA"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAEzD,MAAM,MAAM,GAAG,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;AAEzC,MAAM,MAAM,kBAAkB,CAAC,CAAC,GAAG,GAAG,IAAI;IACxC,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,KAAK,EAAE,MAAM,CAAA;IACb,OAAO,EAAE,OAAO,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,kBAAkB,CAAC,CAAC,GAAG,GAAG,IAAI;IACxC,KAAK,EAAE,CAAC,EAAE,CAAA;IACV,UAAU,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;IAClC,OAAO,EAAE,OAAO,CAAA;IAChB,oDAAoD;IACpD,KAAK,EAAE,MAAM,CAAA;CACd,CAAA;AAED,MAAM,MAAM,YAAY,CAAC,CAAC,GAAG,GAAG,IAAI,kBAAkB,CAAC,CAAC,CAAC,GAAG,kBAAkB,CAAC,CAAC,CAAC,CAAA;AAEjF,MAAM,MAAM,UAAU,GAAG,YAAY,GAAG,SAAS,CAAA;AAEjD,MAAM,MAAM,SAAS,GAAG;IACtB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;IAClB,KAAK,CAAC,EAAE,UAAU,CAAA;IAClB,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,4CAA4C;IAC5C,MAAM,CAAC,EAAE,QAAQ,GAAG,QAAQ,CAAA;IAC5B,mDAAmD;IACnD,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0EAA0E;IAC1E,MAAM,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,CAAC,EAAE,UAAU,CAAA;CACnB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,UAAU,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,UAAU,CAAA;CAClB,CAAA;AAED,MAAM,MAAM,cAAc,GAAG;IAC3B,KAAK,EAAE,UAAU,CAAA;IACjB,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACpC,CAAA;AAED,4EAA4E;AAC5E,MAAM,MAAM,iBAAiB,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAA;AAE/D,8CAA8C;AAC9C,MAAM,MAAM,iBAAiB,GAAG;IAC9B,gDAAgD;IAChD,EAAE,CAAC,EAAE,MAAM,CAAA;IACX;;;OAGG;IACH,OAAO,CAAC,EAAE,iBAAiB,CAAA;CAC5B,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@b4moss/crudian",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "CRUD abstraction for DDD repositories",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -41,10 +41,14 @@
41
41
  "build": "tsc -p tsconfig.json",
42
42
  "prepublishOnly": "npm run build",
43
43
  "prisma:generate": "prisma generate --schema prisma/schema.prisma",
44
+ "prisma:generate:postgres": "prisma generate --schema prisma/schema.postgres.prisma",
45
+ "prisma:generate:mysql": "prisma generate --schema prisma/schema.mysql.prisma",
44
46
  "test": "bun run test:bun-sqlite && bun run test:drizzle && bun run test:prisma && bun run test:libsql",
45
47
  "test:bun-sqlite": "bun test src/bun-sqlite",
46
48
  "test:drizzle": "tsx --test src/drizzle/crud.test.ts src/drizzle/pk.test.ts",
47
49
  "test:prisma": "bun run prisma:generate && tsx --test src/prisma/crud.test.ts src/prisma/pk.test.ts",
50
+ "test:prisma:postgres": "bun run prisma:generate:postgres && DATABASE_URL_POSTGRES=\"${DATABASE_URL_POSTGRES:-postgresql://crudian:crudian@127.0.0.1:5432/crudian?sslmode=disable}\" tsx --test src/prisma/postgres.test.ts",
51
+ "test:prisma:mysql": "bun run prisma:generate:mysql && DATABASE_URL_MYSQL=\"${DATABASE_URL_MYSQL:-mysql://crudian:crudian@127.0.0.1:3306/crudian}\" tsx --test src/prisma/mysql.test.ts",
48
52
  "test:libsql": "tsx --test src/libsql/crud.test.ts src/libsql/pk.test.ts"
49
53
  },
50
54
  "engines": {