@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
package/README.md CHANGED
@@ -69,18 +69,39 @@ const db = drizzle(sqlite)
69
69
  const crud = createCrud(db)
70
70
  ```
71
71
 
72
- ### Prisma (SQLite) — async
72
+ ### Prisma (SQLite / Postgres / MySQL) — async
73
73
 
74
74
  ```ts
75
75
  import { PrismaClient } from "@prisma/client"
76
76
  import { createCrud } from "@b4moss/crudian/prisma"
77
77
 
78
78
  const client = new PrismaClient()
79
- const crud = createCrud(client)
79
+ // Default dialect is "sqlite". For server DBs:
80
+ const crudPg = createCrud(client, { dialect: "postgres" })
81
+ const crudMy = createCrud(client, { dialect: "mysql" })
82
+ const crudSqlite = createCrud(client) // or { dialect: "sqlite" }
80
83
 
81
- await crud.create("items", { name: "alpha", score: 1 })
84
+ await crudSqlite.create("items", { name: "alpha", score: 1 })
82
85
  ```
83
86
 
87
+ Match `options.dialect` to the Prisma schema `provider`. Placeholders and
88
+ insert-return strategies follow the dialect (`$n` + `RETURNING` on Postgres;
89
+ `` `ident` `` + `LAST_INSERT_ID` fetch on MySQL — no `RETURNING`).
90
+
91
+ ### Connection pool / lifetime (JS)
92
+
93
+ crudian never opens connections. For **Postgres / MySQL**, configure pooling on
94
+ the client you inject:
95
+
96
+ | Adapter | How to set pool |
97
+ |---------|-----------------|
98
+ | **Prisma** | Prisma datasource / connection URL params / engine pool settings (caller-owned). See Prisma docs for your provider. |
99
+ | **bun-sqlite / drizzle (sqlite) / libsql** | No pool settings needed (no-op). |
100
+
101
+ Optional type for docs / future drivers: `PoolOptions` from `@b4moss/crudian`
102
+ (not applied by Prisma — configure the client instead). Go GORM uses
103
+ `crudian.Options.Pool` / `ApplyPool` (see `packages/go/README.md`).
104
+
84
105
  ### libSQL (`@libsql/client`) — async
85
106
 
86
107
  ```ts
@@ -238,6 +259,15 @@ crud.count("items") // 5
238
259
  crud.count("items", { where: where().eq("name", "n0") }) // 1
239
260
  ```
240
261
 
262
+ ### `exists(table, query?)` → boolean
263
+
264
+ Whether any row matches (`CountQuery` shape). Does not return a count. Prefer this over `count(...) > 0` when you only need presence (e.g. email already registered).
265
+
266
+ ```ts
267
+ crud.exists("items") // true if the table has any row
268
+ crud.exists("items", { where: where().eq("name", "n0") }) // true / false
269
+ ```
270
+
241
271
  ### `upsert(table, cols)` → row
242
272
 
243
273
  Conflict target is primary key `id`. Inserts or updates; returns the row. `id` is required in `cols`.
@@ -376,17 +406,21 @@ Nestable `and` / `or`. Empty `in([])` is rejected.
376
406
 
377
407
  | Topic | Behavior |
378
408
  |-------|----------|
379
- | Entry | `createCrud(db)` — inject a caller-owned client; exposed as `crud.db` |
409
+ | Entry | `createCrud(db, options?)` — inject a caller-owned client; exposed as `crud.db` |
410
+ | Dialects | Shared Dialect layer: SQLite (all adapters), Postgres / MySQL via **Prisma** (`options.dialect`) |
411
+ | Drizzle | **SQLite (better-sqlite3) only** in this line; PG/MySQL subpaths are out of scope |
380
412
  | Sync vs async | `bun-sqlite` / `drizzle` sync; `prisma` / `libsql` return `Promise`s |
381
413
  | `read` / `update` / `duplicate` miss | `null` |
382
414
  | `delete` / bulk miss | `0` |
383
- | Upsert conflict | primary key `id` |
384
- | Pagination | cursor on `id` ASC only (no offset) |
415
+ | Upsert conflict | application-level on PK (default `id`; no SQL `ON CONFLICT`) |
416
+ | Pagination | `paging?: "offset" \| "cursor"` (default `"offset"`) |
385
417
  | `columns` | optional on `read` / `search` / `list`; omit → `*` |
386
418
  | `search.total` / `count` | full where count; not page length |
419
+ | `exists` | boolean presence; same input as `count` |
420
+ | Pool | caller-owned; Prisma via datasource settings; SQLite adapters no-op |
387
421
  | Errors | minimal `CrudianError`; other errors propagate from the driver |
388
422
  | Identifiers | string required; no format validation |
389
- | Out of scope | relations, migrations, full-text search, ORM models |
423
+ | Out of scope | relations, migrations, full-text search, ORM models, TypeORM (#43) |
390
424
 
391
425
  ## License
392
426
 
@@ -1,2 +1,6 @@
1
- export { compileWhere, quoteIdent, resolveWhere, } from "../sqlite/sql.js";
1
+ import type { WhereNode } from "../index.js";
2
+ import { quoteIdent, resolveWhere, type CompiledWhere } from "../sqlite/sql.js";
3
+ export { quoteIdent, resolveWhere };
4
+ /** SQLite-default compileWhere (compat for bun-sqlite tests). */
5
+ export declare function compileWhere(node: WhereNode | undefined, startIndex?: number): CompiledWhere;
2
6
  //# sourceMappingURL=sql.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/bun-sqlite/sql.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,GACb,MAAM,kBAAkB,CAAA"}
1
+ {"version":3,"file":"sql.d.ts","sourceRoot":"","sources":["../../src/bun-sqlite/sql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE5C,OAAO,EAEL,UAAU,EACV,YAAY,EACZ,KAAK,aAAa,EACnB,MAAM,kBAAkB,CAAA;AAEzB,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,CAAA;AAEnC,iEAAiE;AACjE,wBAAgB,YAAY,CAC1B,IAAI,EAAE,SAAS,GAAG,SAAS,EAC3B,UAAU,SAAI,GACb,aAAa,CAEf"}
@@ -1 +1,7 @@
1
- export { compileWhere, quoteIdent, resolveWhere, } from "../sqlite/sql.js";
1
+ import { sqliteDialect } from "../dialect/sqlite.js";
2
+ import { compileWhere as compileWhereDialect, quoteIdent, resolveWhere, } from "../sqlite/sql.js";
3
+ export { quoteIdent, resolveWhere };
4
+ /** SQLite-default compileWhere (compat for bun-sqlite tests). */
5
+ export function compileWhere(node, startIndex = 1) {
6
+ return compileWhereDialect(sqliteDialect, node, startIndex);
7
+ }
@@ -0,0 +1,7 @@
1
+ import type { Dialect, DialectName } from "./types.js";
2
+ export type { Dialect, DialectName, DescribeColumnsQuery } from "./types.js";
3
+ export { sqliteDialect } from "./sqlite.js";
4
+ export { postgresDialect } from "./postgres.js";
5
+ export { mysqlDialect } from "./mysql.js";
6
+ export declare function resolveDialect(name: DialectName | undefined): Dialect;
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/dialect/index.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAA;AAEtD,YAAY,EAAE,OAAO,EAAE,WAAW,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AAC5E,OAAO,EAAE,aAAa,EAAE,MAAM,aAAa,CAAA;AAC3C,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEzC,wBAAgB,cAAc,CAAC,IAAI,EAAE,WAAW,GAAG,SAAS,GAAG,OAAO,CAWrE"}
@@ -0,0 +1,19 @@
1
+ import { CrudianError } from "../index.js";
2
+ import { mysqlDialect } from "./mysql.js";
3
+ import { postgresDialect } from "./postgres.js";
4
+ import { sqliteDialect } from "./sqlite.js";
5
+ export { sqliteDialect } from "./sqlite.js";
6
+ export { postgresDialect } from "./postgres.js";
7
+ export { mysqlDialect } from "./mysql.js";
8
+ export function resolveDialect(name) {
9
+ switch (name ?? "sqlite") {
10
+ case "sqlite":
11
+ return sqliteDialect;
12
+ case "postgres":
13
+ return postgresDialect;
14
+ case "mysql":
15
+ return mysqlDialect;
16
+ default:
17
+ throw new CrudianError(`unknown dialect: ${String(name)}`);
18
+ }
19
+ }
@@ -0,0 +1,4 @@
1
+ import type { Dialect } from "./types.js";
2
+ /** MySQL dialect: `` `ident` ``, `?`, LAST_INSERT_ID, information_schema (no RETURNING). */
3
+ export declare const mysqlDialect: Dialect;
4
+ //# sourceMappingURL=mysql.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mysql.d.ts","sourceRoot":"","sources":["../../src/dialect/mysql.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAGzC,4FAA4F;AAC5F,eAAO,MAAM,YAAY,EAAE,OA0B1B,CAAA"}
@@ -0,0 +1,24 @@
1
+ import { assertIdent } from "./types.js";
2
+ /** MySQL dialect: `` `ident` ``, `?`, LAST_INSERT_ID, information_schema (no RETURNING). */
3
+ export const mysqlDialect = {
4
+ name: "mysql",
5
+ supportsInsertReturning: false,
6
+ quoteIdent(name) {
7
+ const n = assertIdent(name);
8
+ return `\`${n.replaceAll("`", "``")}\``;
9
+ },
10
+ placeholder(_i) {
11
+ return "?";
12
+ },
13
+ lastInsertIdSql() {
14
+ return "SELECT LAST_INSERT_ID() AS id";
15
+ },
16
+ describeColumns(table) {
17
+ const n = assertIdent(table);
18
+ return {
19
+ sql: `SELECT COLUMN_NAME AS name FROM information_schema.COLUMNS` +
20
+ ` WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ?`,
21
+ args: [n],
22
+ };
23
+ },
24
+ };
@@ -0,0 +1,4 @@
1
+ import type { Dialect } from "./types.js";
2
+ /** Postgres dialect: `"ident"`, `$n`, RETURNING *, information_schema. */
3
+ export declare const postgresDialect: Dialect;
4
+ //# sourceMappingURL=postgres.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgres.d.ts","sourceRoot":"","sources":["../../src/dialect/postgres.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAGzC,0EAA0E;AAC1E,eAAO,MAAM,eAAe,EAAE,OA2B7B,CAAA"}
@@ -0,0 +1,25 @@
1
+ import { assertIdent } from "./types.js";
2
+ /** Postgres dialect: `"ident"`, `$n`, RETURNING *, information_schema. */
3
+ export const postgresDialect = {
4
+ name: "postgres",
5
+ supportsInsertReturning: true,
6
+ quoteIdent(name) {
7
+ const n = assertIdent(name);
8
+ return `"${n.replaceAll('"', '""')}"`;
9
+ },
10
+ placeholder(i) {
11
+ return `$${i}`;
12
+ },
13
+ lastInsertIdSql() {
14
+ // Prefer RETURNING; this is a fallback for sync-style paths.
15
+ return "SELECT lastval() AS id";
16
+ },
17
+ describeColumns(table) {
18
+ const n = assertIdent(table);
19
+ return {
20
+ sql: `SELECT column_name AS name FROM information_schema.columns` +
21
+ ` WHERE table_schema = current_schema() AND table_name = $1`,
22
+ args: [n],
23
+ };
24
+ },
25
+ };
@@ -0,0 +1,4 @@
1
+ import type { Dialect } from "./types.js";
2
+ /** SQLite / libSQL dialect: `"ident"`, `?`, PRAGMA, last_insert_rowid / RETURNING. */
3
+ export declare const sqliteDialect: Dialect;
4
+ //# sourceMappingURL=sqlite.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sqlite.d.ts","sourceRoot":"","sources":["../../src/dialect/sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,YAAY,CAAA;AAGzC,sFAAsF;AACtF,eAAO,MAAM,aAAa,EAAE,OAwB3B,CAAA"}
@@ -0,0 +1,23 @@
1
+ import { assertIdent } from "./types.js";
2
+ /** SQLite / libSQL dialect: `"ident"`, `?`, PRAGMA, last_insert_rowid / RETURNING. */
3
+ export const sqliteDialect = {
4
+ name: "sqlite",
5
+ supportsInsertReturning: true,
6
+ quoteIdent(name) {
7
+ const n = assertIdent(name);
8
+ return `"${n.replaceAll('"', '""')}"`;
9
+ },
10
+ placeholder(_i) {
11
+ return "?";
12
+ },
13
+ lastInsertIdSql() {
14
+ return "SELECT last_insert_rowid() AS id";
15
+ },
16
+ describeColumns(table) {
17
+ const n = assertIdent(table);
18
+ return {
19
+ sql: `PRAGMA table_info(${sqliteDialect.quoteIdent(n)})`,
20
+ args: [],
21
+ };
22
+ },
23
+ };
@@ -0,0 +1,25 @@
1
+ /** Supported SQL dialect names. */
2
+ export type DialectName = "sqlite" | "postgres" | "mysql";
3
+ /** Result of compiling a column-description query for PK checks. */
4
+ export type DescribeColumnsQuery = {
5
+ sql: string;
6
+ args: unknown[];
7
+ };
8
+ /**
9
+ * SQL dialect hooks for shared CRUD.
10
+ * Pool / connection lifetime is intentionally not part of this interface.
11
+ */
12
+ export type Dialect = {
13
+ readonly name: DialectName;
14
+ quoteIdent(name: string): string;
15
+ /** 1-based placeholder index into the statement args. */
16
+ placeholder(i: number): string;
17
+ /** When true, async create prefers `INSERT … RETURNING *`. */
18
+ readonly supportsInsertReturning: boolean;
19
+ /** Query returning a single column `id` for fetch-after-insert. */
20
+ lastInsertIdSql(): string;
21
+ /** List column names (`name` field) for a table. */
22
+ describeColumns(table: string): DescribeColumnsQuery;
23
+ };
24
+ export declare function assertIdent(name: string): string;
25
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/dialect/types.ts"],"names":[],"mappings":"AAEA,mCAAmC;AACnC,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,UAAU,GAAG,OAAO,CAAA;AAEzD,oEAAoE;AACpE,MAAM,MAAM,oBAAoB,GAAG;IACjC,GAAG,EAAE,MAAM,CAAA;IACX,IAAI,EAAE,OAAO,EAAE,CAAA;CAChB,CAAA;AAED;;;GAGG;AACH,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAA;IAC1B,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAA;IAChC,yDAAyD;IACzD,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;IAC9B,8DAA8D;IAC9D,QAAQ,CAAC,uBAAuB,EAAE,OAAO,CAAA;IACzC,mEAAmE;IACnE,eAAe,IAAI,MAAM,CAAA;IACzB,oDAAoD;IACpD,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,oBAAoB,CAAA;CACrD,CAAA;AAED,wBAAgB,WAAW,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAGhD"}
@@ -0,0 +1,5 @@
1
+ import { assertString } from "../index.js";
2
+ export function assertIdent(name) {
3
+ assertString(name, "identifier");
4
+ return name;
5
+ }
package/dist/index.d.ts CHANGED
@@ -8,5 +8,6 @@
8
8
  */
9
9
  export { CrudianError, assertString } from "./errors.js";
10
10
  export { WhereBuilder, where, isWhereBuilder, type Op, type CondNode, type GroupNode, type WhereNode, } from "./where.js";
11
- export type { Row, OffsetSearchResult, CursorSearchResult, SearchResult, WhereInput, ReadQuery, SearchQuery, CountQuery, DeleteQuery, UpdateQuery, DuplicateQuery, CreateCrudOptions, } from "./types.js";
11
+ export type { Row, OffsetSearchResult, CursorSearchResult, SearchResult, WhereInput, ReadQuery, SearchQuery, CountQuery, DeleteQuery, UpdateQuery, DuplicateQuery, CreateCrudOptions, CreateCrudDialect, } from "./types.js";
12
+ export type { PoolOptions } from "./pool.js";
12
13
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EACL,YAAY,EACZ,KAAK,EACL,cAAc,EACd,KAAK,EAAE,EACP,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,SAAS,GACf,MAAM,YAAY,CAAA;AACnB,YAAY,EACV,GAAG,EACH,kBAAkB,EAClB,kBAAkB,EAClB,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,WAAW,EACX,WAAW,EACX,cAAc,EACd,iBAAiB,GAClB,MAAM,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,YAAY,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACxD,OAAO,EACL,YAAY,EACZ,KAAK,EACL,cAAc,EACd,KAAK,EAAE,EACP,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,SAAS,GACf,MAAM,YAAY,CAAA;AACnB,YAAY,EACV,GAAG,EACH,kBAAkB,EAClB,kBAAkB,EAClB,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,WAAW,EACX,WAAW,EACX,cAAc,EACd,iBAAiB,EACjB,iBAAiB,GAClB,MAAM,YAAY,CAAA;AACnB,YAAY,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA"}
package/dist/pool.d.ts ADDED
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Optional pool / lifetime hints for documentation and future driver adapters.
3
+ * Prisma owns pooling via datasource / engine settings — callers configure that
4
+ * on the PrismaClient they inject. crudian does not open connections.
5
+ *
6
+ * Not part of the Dialect interface (#105 / #73).
7
+ */
8
+ export type PoolOptions = {
9
+ /** Max open connections (driver-dependent). */
10
+ maxOpenConns?: number;
11
+ /** Max idle connections. */
12
+ maxIdleConns?: number;
13
+ /** Max connection lifetime in milliseconds. */
14
+ connMaxLifetimeMs?: number;
15
+ /** Max idle time in milliseconds. */
16
+ connMaxIdleTimeMs?: number;
17
+ };
18
+ //# sourceMappingURL=pool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pool.d.ts","sourceRoot":"","sources":["../src/pool.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,+CAA+C;IAC/C,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,4BAA4B;IAC5B,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,+CAA+C;IAC/C,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,qCAAqC;IACrC,iBAAiB,CAAC,EAAE,MAAM,CAAA;CAC3B,CAAA"}
package/dist/pool.js ADDED
@@ -0,0 +1 @@
1
+ export {};
@@ -8,8 +8,9 @@ export type PrismaLikeClient = {
8
8
  };
9
9
  export type PrismaCrud = AsyncSqliteCrud<PrismaLikeClient>;
10
10
  /**
11
- * Create a Crud bound to a PrismaClient (SQLite).
11
+ * Create a Crud bound to a PrismaClient.
12
12
  * Methods are async. The injected client is exposed as `crud.db`.
13
+ * Pass `options.dialect` for Postgres / MySQL (`"sqlite"` default).
13
14
  */
14
15
  export declare function createCrud(client: PrismaLikeClient, options?: CreateCrudOptions): PrismaCrud;
15
16
  //# sourceMappingURL=crud.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../src/prisma/crud.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAClE,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAA;AAGhC,0EAA0E;AAC1E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACvE,eAAe,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAC7E,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,gBAAgB,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CACtE,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAA;AAU1D;;;GAGG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,gBAAgB,EACxB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,UAAU,CA8CZ"}
1
+ {"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../src/prisma/crud.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAElE,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAA;AAGhC,0EAA0E;AAC1E,MAAM,MAAM,gBAAgB,GAAG;IAC7B,iBAAiB,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACvE,eAAe,CAAC,CAAC,GAAG,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;IAC7E,YAAY,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,gBAAgB,KAAK,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CACtE,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAA;AAU1D;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,gBAAgB,EACxB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,UAAU,CAgDZ"}
@@ -1,5 +1,6 @@
1
1
  import { CrudianError } from "../index.js";
2
- import { createAsyncSqliteCrud, } from "../sqlite/async-crud.js";
2
+ import { resolveDialect } from "../dialect/index.js";
3
+ import { createAsyncCrud, } from "../sqlite/async-crud.js";
3
4
  function normalizeRow(row) {
4
5
  const out = { ...row };
5
6
  for (const [k, v] of Object.entries(out)) {
@@ -9,8 +10,9 @@ function normalizeRow(row) {
9
10
  return out;
10
11
  }
11
12
  /**
12
- * Create a Crud bound to a PrismaClient (SQLite).
13
+ * Create a Crud bound to a PrismaClient.
13
14
  * Methods are async. The injected client is exposed as `crud.db`.
15
+ * Pass `options.dialect` for Postgres / MySQL (`"sqlite"` default).
14
16
  */
15
17
  export function createCrud(client, options) {
16
18
  if (client == null) {
@@ -23,7 +25,8 @@ export function createCrud(client, options) {
23
25
  throw new CrudianError("db must be a PrismaClient");
24
26
  }
25
27
  let active = client;
26
- return createAsyncSqliteCrud(client, {
28
+ const dialect = resolveDialect(options?.dialect);
29
+ return createAsyncCrud(client, {
27
30
  async run(sql, args = []) {
28
31
  const changes = await active.$executeRawUnsafe(sql, ...args);
29
32
  return { changes: Number(changes ?? 0) };
@@ -50,7 +53,7 @@ export function createCrud(client, options) {
50
53
  }
51
54
  });
52
55
  },
53
- }, options);
56
+ }, dialect, options);
54
57
  }
55
58
  function normalizeQueryResult(result) {
56
59
  if (result == null)
@@ -0,0 +1,16 @@
1
+ import { type PrismaLikeClient } from "./index.js";
2
+ export type DialectName = "postgres" | "mysql";
3
+ export type ServerDbHarness = {
4
+ dialect: DialectName;
5
+ envKey: "DATABASE_URL_POSTGRES" | "DATABASE_URL_MYSQL";
6
+ createClient: () => PrismaLikeClient & {
7
+ $disconnect(): Promise<void>;
8
+ $executeRawUnsafe(q: string, ...a: unknown[]): Promise<number>;
9
+ };
10
+ createItemsDDL: string;
11
+ dropItemsDDL: string;
12
+ createAltPkDDL: string;
13
+ dropAltPkDDL: string;
14
+ };
15
+ export declare function registerPrismaServerDialectTests(h: ServerDbHarness): void;
16
+ //# sourceMappingURL=server-dialect-harness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-dialect-harness.d.ts","sourceRoot":"","sources":["../../src/prisma/server-dialect-harness.ts"],"names":[],"mappings":"AAOA,OAAO,EAAc,KAAK,gBAAgB,EAAE,MAAM,YAAY,CAAA;AAE9D,MAAM,MAAM,WAAW,GAAG,UAAU,GAAG,OAAO,CAAA;AAE9C,MAAM,MAAM,eAAe,GAAG;IAC5B,OAAO,EAAE,WAAW,CAAA;IACpB,MAAM,EAAE,uBAAuB,GAAG,oBAAoB,CAAA;IACtD,YAAY,EAAE,MAAM,gBAAgB,GAAG;QAAE,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;QAAC,iBAAiB,CAAC,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;KAAE,CAAA;IACvI,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;IACpB,cAAc,EAAE,MAAM,CAAA;IACtB,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,wBAAgB,gCAAgC,CAAC,CAAC,EAAE,eAAe,QA0IlE"}
@@ -0,0 +1,120 @@
1
+ /**
2
+ * Shared contract suite for Prisma against Postgres / MySQL.
3
+ * Skipped automatically when the DATABASE_URL_* env var is unset.
4
+ */
5
+ import assert from "node:assert/strict";
6
+ import { after, before, describe, test } from "node:test";
7
+ import { CrudianError, where } from "../index.js";
8
+ import { createCrud } from "./index.js";
9
+ export function registerPrismaServerDialectTests(h) {
10
+ const url = process.env[h.envKey];
11
+ const suite = describe;
12
+ suite(`prisma.${h.dialect}`, { skip: !url }, () => {
13
+ let client;
14
+ let crud;
15
+ before(async () => {
16
+ if (!url)
17
+ return;
18
+ process.env[h.envKey] = url;
19
+ client = h.createClient();
20
+ await client.$executeRawUnsafe(h.dropItemsDDL);
21
+ await client.$executeRawUnsafe(h.createItemsDDL);
22
+ crud = createCrud(client, { dialect: h.dialect });
23
+ });
24
+ after(async () => {
25
+ if (!url)
26
+ return;
27
+ try {
28
+ await client.$executeRawUnsafe(h.dropItemsDDL);
29
+ await client.$executeRawUnsafe(h.dropAltPkDDL);
30
+ }
31
+ catch {
32
+ /* ignore */
33
+ }
34
+ await client.$disconnect();
35
+ });
36
+ async function reset() {
37
+ await client.$executeRawUnsafe(h.dropItemsDDL);
38
+ await client.$executeRawUnsafe(h.createItemsDDL);
39
+ }
40
+ test("正常系: create / read / update / delete", async () => {
41
+ await reset();
42
+ const created = await crud.create("items", { name: "alice", score: 10 });
43
+ assert.equal(created.name, "alice");
44
+ assert.ok(created.id != null);
45
+ const got = await crud.read("items", {
46
+ where: where().eq("id", created.id),
47
+ });
48
+ assert.equal(got?.name, "alice");
49
+ const updated = await crud.update("items", { score: 20 }, { where: where().eq("id", created.id) });
50
+ assert.equal(updated?.score, 20);
51
+ const n = await crud.delete("items", {
52
+ where: where().eq("id", created.id),
53
+ });
54
+ assert.equal(n, 1);
55
+ assert.equal(await crud.read("items", { where: where().eq("id", created.id) }), null);
56
+ });
57
+ test("正常系: count / exists / search offset / cursor", async () => {
58
+ await reset();
59
+ await crud.bulkCreate("items", [
60
+ { name: "a", score: 1 },
61
+ { name: "b", score: 2 },
62
+ { name: "c", score: 3 },
63
+ ]);
64
+ assert.equal(await crud.count("items"), 3);
65
+ assert.equal(await crud.exists("items", { where: where().eq("name", "b") }), true);
66
+ assert.equal(await crud.exists("items", { where: where().eq("name", "z") }), false);
67
+ const page = await crud.search("items", { limit: 2, offset: 0 });
68
+ assert.equal(page.items.length, 2);
69
+ assert.equal("total" in page && page.total, 3);
70
+ assert.equal("hasMore" in page && page.hasMore, true);
71
+ const cursorPage = await crud.search("items", {
72
+ paging: "cursor",
73
+ limit: 2,
74
+ });
75
+ assert.equal(cursorPage.items.length, 2);
76
+ assert.ok("nextCursor" in cursorPage);
77
+ });
78
+ test("正常系: upsert / duplicate / transaction", async () => {
79
+ await reset();
80
+ const a = await crud.create("items", { name: "x", score: 1 });
81
+ const up = await crud.upsert("items", {
82
+ id: a.id,
83
+ name: "x",
84
+ score: 9,
85
+ });
86
+ assert.equal(up.score, 9);
87
+ const dup = await crud.duplicate("items", {
88
+ where: where().eq("id", a.id),
89
+ overrides: { name: "y" },
90
+ });
91
+ assert.ok(dup);
92
+ assert.notEqual(dup.id, a.id);
93
+ assert.equal(dup.name, "y");
94
+ await crud.transaction(async () => {
95
+ await crud.create("items", { name: "tx", score: 1 });
96
+ });
97
+ assert.equal(await crud.exists("items", { where: where().eq("name", "tx") }), true);
98
+ });
99
+ test("正常系: 可変 PK", async () => {
100
+ await reset();
101
+ await client.$executeRawUnsafe(h.dropAltPkDDL);
102
+ await client.$executeRawUnsafe(h.createAltPkDDL);
103
+ const alt = createCrud(client, { dialect: h.dialect, pk: "item_id" });
104
+ const row = await alt.create("alt_items", { name: "pk" });
105
+ assert.ok(row.item_id != null);
106
+ const got = await alt.read("alt_items", {
107
+ where: where().eq("item_id", row.item_id),
108
+ });
109
+ assert.equal(got?.name, "pk");
110
+ await client.$executeRawUnsafe(h.dropAltPkDDL);
111
+ });
112
+ test("異常系: 空 in / 不正 dialect は拒否", async () => {
113
+ await reset();
114
+ await assert.rejects(() => crud.search("items", {
115
+ where: where().in("id", []),
116
+ }), CrudianError);
117
+ assert.throws(() => createCrud(client, { dialect: "nope" }), CrudianError);
118
+ });
119
+ });
120
+ }
@@ -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 AsyncSqliteExecutor = {
3
4
  run(sql: string, args?: unknown[]): Promise<{
4
5
  changes: number;
@@ -22,7 +23,11 @@ export type AsyncSqliteCrud<TDb> = {
22
23
  search<T extends Row = Row>(table: string, query?: SearchQuery): Promise<SearchResult<T>>;
23
24
  list<T extends Row = Row>(table: string, query?: SearchQuery): Promise<SearchResult<T>>;
24
25
  count(table: string, query?: CountQuery): Promise<number>;
26
+ exists(table: string, query?: CountQuery): Promise<boolean>;
25
27
  transaction<T>(fn: () => Promise<T>): Promise<T>;
26
28
  };
29
+ /** Dialect-aware async CRUD factory. */
30
+ export declare function createAsyncCrud<TDb>(db: TDb, ex: AsyncSqliteExecutor, dialect: Dialect, options?: CreateCrudOptions): AsyncSqliteCrud<TDb>;
31
+ /** SQLite async CRUD (compat). */
27
32
  export declare function createAsyncSqliteCrud<TDb>(db: TDb, ex: AsyncSqliteExecutor, options?: CreateCrudOptions): AsyncSqliteCrud<TDb>;
28
33
  //# sourceMappingURL=async-crud.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"async-crud.d.ts","sourceRoot":"","sources":["../../src/sqlite/async-crud.ts"],"names":[],"mappings":"AAAA,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,iBAAiB,EACtB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,GAAG,EACR,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAA;AAIpB,MAAM,MAAM,mBAAmB,GAAG;IAChC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC;QAAE,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAA;IAChE,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,GAAG,SAAS,CAAC,CAAA;IAC5D,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,OAAO,EAAE,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,CAAA;IAClD,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CACjD,CAAA;AAED,MAAM,MAAM,eAAe,CAAC,GAAG,IAAI;IACjC,QAAQ,CAAC,EAAE,EAAE,GAAG,CAAA;IAChB,MAAM,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,EACxB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,CAAC,CAAC,CAAA;IACb,IAAI,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,SAAS,GAAG,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;IAC9E,MAAM,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,EACxB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;IACpB,MAAM,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC1D,MAAM,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,EACxB,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC5B,OAAO,CAAC,CAAC,CAAC,CAAA;IACb,SAAS,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,EAC3B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,cAAc,GACpB,OAAO,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;IACpB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3E,UAAU,CACR,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC7B,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,MAAM,CAAC,CAAA;IAClB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC9D,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IAC3E,MAAM,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,EACxB,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3B,IAAI,CAAC,CAAC,SAAS,GAAG,GAAG,GAAG,EACtB,KAAK,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,WAAW,GAClB,OAAO,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,CAAA;IAC3B,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC,CAAA;IACzD,WAAW,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAA;CACjD,CAAA;AAoBD,wBAAgB,qBAAqB,CAAC,GAAG,EACvC,EAAE,EAAE,GAAG,EACP,EAAE,EAAE,mBAAmB,EACvB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,eAAe,CAAC,GAAG,CAAC,CAsVtB"}
1
+ {"version":3,"file":"async-crud.d.ts","sourceRoot":"","sources":["../../src/sqlite/async-crud.ts"],"names":[],"mappings":"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"}