@b4moss/crudian 0.6.0 → 0.9.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.
- package/README.md +12 -0
- package/dist/bun-sqlite/crud.d.ts +2 -1
- package/dist/bun-sqlite/crud.d.ts.map +1 -1
- package/dist/bun-sqlite/crud.js +2 -2
- package/dist/drizzle/crud.d.ts +2 -1
- package/dist/drizzle/crud.d.ts.map +1 -1
- package/dist/drizzle/crud.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/libsql/crud.d.ts +2 -1
- package/dist/libsql/crud.d.ts.map +1 -1
- package/dist/libsql/crud.js +2 -2
- package/dist/prisma/crud.d.ts +2 -1
- package/dist/prisma/crud.d.ts.map +1 -1
- package/dist/prisma/crud.js +2 -2
- package/dist/sqlite/async-crud.d.ts +3 -2
- package/dist/sqlite/async-crud.d.ts.map +1 -1
- package/dist/sqlite/async-crud.js +74 -16
- package/dist/sqlite/pk.d.ts +22 -0
- package/dist/sqlite/pk.d.ts.map +1 -0
- package/dist/sqlite/pk.js +45 -0
- package/dist/sqlite/sync-crud.d.ts +3 -2
- package/dist/sqlite/sync-crud.d.ts.map +1 -1
- package/dist/sqlite/sync-crud.js +77 -19
- package/dist/types.d.ts +19 -2
- package/dist/types.d.ts.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -4,6 +4,8 @@ CRUD abstraction for DDD repositories.
|
|
|
4
4
|
|
|
5
5
|
One npm package, adapter subpaths. Shared contracts live at the package root; backends are imported from subpaths. Callers inject their own DB/client — crudian never opens connections or reads env secrets for you.
|
|
6
6
|
|
|
7
|
+
**Versioning:** this npm package has its own SemVer (`package.json`). It is independent of the Go module (`packages/go/VERSION`). Shipping Go `0.7.0` does not require bumping this package.
|
|
8
|
+
|
|
7
9
|
## Install
|
|
8
10
|
|
|
9
11
|
```bash
|
|
@@ -236,6 +238,15 @@ crud.count("items") // 5
|
|
|
236
238
|
crud.count("items", { where: where().eq("name", "n0") }) // 1
|
|
237
239
|
```
|
|
238
240
|
|
|
241
|
+
### `exists(table, query?)` → boolean
|
|
242
|
+
|
|
243
|
+
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).
|
|
244
|
+
|
|
245
|
+
```ts
|
|
246
|
+
crud.exists("items") // true if the table has any row
|
|
247
|
+
crud.exists("items", { where: where().eq("name", "n0") }) // true / false
|
|
248
|
+
```
|
|
249
|
+
|
|
239
250
|
### `upsert(table, cols)` → row
|
|
240
251
|
|
|
241
252
|
Conflict target is primary key `id`. Inserts or updates; returns the row. `id` is required in `cols`.
|
|
@@ -382,6 +393,7 @@ Nestable `and` / `or`. Empty `in([])` is rejected.
|
|
|
382
393
|
| Pagination | cursor on `id` ASC only (no offset) |
|
|
383
394
|
| `columns` | optional on `read` / `search` / `list`; omit → `*` |
|
|
384
395
|
| `search.total` / `count` | full where count; not page length |
|
|
396
|
+
| `exists` | boolean presence; same input as `count`; use instead of `count > 0` when you only need yes/no |
|
|
385
397
|
| Errors | minimal `CrudianError`; other errors propagate from the driver |
|
|
386
398
|
| Identifiers | string required; no format validation |
|
|
387
399
|
| Out of scope | relations, migrations, full-text search, ORM models |
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import type { Database } from "bun:sqlite";
|
|
2
|
+
import { type CreateCrudOptions } from "../index.js";
|
|
2
3
|
import { type SyncSqliteCrud } from "../sqlite/sync-crud.js";
|
|
3
4
|
export type BunSqliteCrud = SyncSqliteCrud<Database>;
|
|
4
5
|
/**
|
|
5
6
|
* Create a Crud bound to a Bun SQLite Database.
|
|
6
7
|
* The same Database instance is reused for every operation.
|
|
7
8
|
*/
|
|
8
|
-
export declare function createCrud(db: Database): BunSqliteCrud;
|
|
9
|
+
export declare function createCrud(db: Database, options?: CreateCrudOptions): BunSqliteCrud;
|
|
9
10
|
//# sourceMappingURL=crud.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../src/bun-sqlite/crud.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAoB,MAAM,YAAY,CAAA;
|
|
1
|
+
{"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../src/bun-sqlite/crud.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAoB,MAAM,YAAY,CAAA;AAC5D,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAClE,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,wBAAwB,CAAA;AAM/B,MAAM,MAAM,aAAa,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAA;AAEpD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,EAAE,EAAE,QAAQ,EACZ,OAAO,CAAC,EAAE,iBAAiB,GAC1B,aAAa,CA6Bf"}
|
package/dist/bun-sqlite/crud.js
CHANGED
|
@@ -7,7 +7,7 @@ function bindings(args) {
|
|
|
7
7
|
* Create a Crud bound to a Bun SQLite Database.
|
|
8
8
|
* The same Database instance is reused for every operation.
|
|
9
9
|
*/
|
|
10
|
-
export function createCrud(db) {
|
|
10
|
+
export function createCrud(db, options) {
|
|
11
11
|
if (db == null) {
|
|
12
12
|
throw new CrudianError("db is required");
|
|
13
13
|
}
|
|
@@ -28,5 +28,5 @@ export function createCrud(db) {
|
|
|
28
28
|
transaction(fn) {
|
|
29
29
|
return db.transaction(fn)();
|
|
30
30
|
},
|
|
31
|
-
});
|
|
31
|
+
}, options);
|
|
32
32
|
}
|
package/dist/drizzle/crud.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { BetterSQLite3Database } from "drizzle-orm/better-sqlite3";
|
|
2
2
|
import type BetterSqlite3 from "better-sqlite3";
|
|
3
|
+
import { type CreateCrudOptions } from "../index.js";
|
|
3
4
|
import { type SyncSqliteCrud } from "../sqlite/sync-crud.js";
|
|
4
5
|
type DrizzleSqliteDb = BetterSQLite3Database<Record<string, unknown>> & {
|
|
5
6
|
$client: BetterSqlite3.Database;
|
|
@@ -9,6 +10,6 @@ export type DrizzleCrud = SyncSqliteCrud<DrizzleSqliteDb>;
|
|
|
9
10
|
* Create a Crud bound to a Drizzle better-sqlite3 database.
|
|
10
11
|
* Raw SQL goes through the underlying better-sqlite3 client (`db.$client`).
|
|
11
12
|
*/
|
|
12
|
-
export declare function createCrud(db: DrizzleSqliteDb): DrizzleCrud;
|
|
13
|
+
export declare function createCrud(db: DrizzleSqliteDb, options?: CreateCrudOptions): DrizzleCrud;
|
|
13
14
|
export {};
|
|
14
15
|
//# sourceMappingURL=crud.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../src/drizzle/crud.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AACvE,OAAO,KAAK,aAAa,MAAM,gBAAgB,CAAA;
|
|
1
|
+
{"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../src/drizzle/crud.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAA;AACvE,OAAO,KAAK,aAAa,MAAM,gBAAgB,CAAA;AAC/C,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAClE,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,wBAAwB,CAAA;AAE/B,KAAK,eAAe,GAAG,qBAAqB,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG;IACtE,OAAO,EAAE,aAAa,CAAC,QAAQ,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,WAAW,GAAG,cAAc,CAAC,eAAe,CAAC,CAAA;AAEzD;;;GAGG;AACH,wBAAgB,UAAU,CACxB,EAAE,EAAE,eAAe,EACnB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,WAAW,CA+Bb"}
|
package/dist/drizzle/crud.js
CHANGED
|
@@ -4,7 +4,7 @@ import { createSyncSqliteCrud, } from "../sqlite/sync-crud.js";
|
|
|
4
4
|
* Create a Crud bound to a Drizzle better-sqlite3 database.
|
|
5
5
|
* Raw SQL goes through the underlying better-sqlite3 client (`db.$client`).
|
|
6
6
|
*/
|
|
7
|
-
export function createCrud(db) {
|
|
7
|
+
export function createCrud(db, options) {
|
|
8
8
|
if (db == null) {
|
|
9
9
|
throw new CrudianError("db is required");
|
|
10
10
|
}
|
|
@@ -29,5 +29,5 @@ export function createCrud(db) {
|
|
|
29
29
|
transaction(fn) {
|
|
30
30
|
return client.transaction(fn)();
|
|
31
31
|
},
|
|
32
|
-
});
|
|
32
|
+
}, options);
|
|
33
33
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -8,5 +8,5 @@
|
|
|
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, SearchResult, WhereInput, ReadQuery, SearchQuery, CountQuery, DeleteQuery, UpdateQuery, DuplicateQuery, } from "./types.js";
|
|
11
|
+
export type { Row, OffsetSearchResult, CursorSearchResult, SearchResult, WhereInput, ReadQuery, SearchQuery, CountQuery, DeleteQuery, UpdateQuery, DuplicateQuery, CreateCrudOptions, } from "./types.js";
|
|
12
12
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -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,YAAY,EACZ,UAAU,EACV,SAAS,EACT,WAAW,EACX,UAAU,EACV,WAAW,EACX,WAAW,EACX,cAAc,
|
|
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"}
|
package/dist/libsql/crud.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CreateCrudOptions } from "../index.js";
|
|
1
2
|
import { type AsyncSqliteCrud } from "../sqlite/async-crud.js";
|
|
2
3
|
import type { Row } from "../types.js";
|
|
3
4
|
/** Result shape used by the libSQL executor bridge. */
|
|
@@ -27,5 +28,5 @@ export type LibsqlCrud = AsyncSqliteCrud<LibsqlLikeClient>;
|
|
|
27
28
|
* Methods are async. The injected client is exposed as `crud.db`.
|
|
28
29
|
* Auth / URL are configured on the caller-created client (e.g. from env).
|
|
29
30
|
*/
|
|
30
|
-
export declare function createCrud(client: LibsqlLikeClient): LibsqlCrud;
|
|
31
|
+
export declare function createCrud(client: LibsqlLikeClient, options?: CreateCrudOptions): LibsqlCrud;
|
|
31
32
|
//# sourceMappingURL=crud.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../src/libsql/crud.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../src/libsql/crud.ts"],"names":[],"mappings":"AAAA,OAAO,EAAgB,KAAK,iBAAiB,EAAE,MAAM,aAAa,CAAA;AAClE,OAAO,EAEL,KAAK,eAAe,EACrB,MAAM,yBAAyB,CAAA;AAChC,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,aAAa,CAAA;AAEtC,uDAAuD;AACvD,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,KAAK,CAAC,GAAG,GAAG,OAAO,CAAC,CAAA;IAC1B,YAAY,EAAE,MAAM,CAAA;CACrB,CAAA;AAED,gEAAgE;AAChE,MAAM,MAAM,cAAc,GAAG;IAC3B,OAAO,CACL,IAAI,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,OAAO,EAAE,CAAA;KAAE,GAAG,MAAM,GAC/C,OAAO,CAAC,eAAe,CAAC,CAAA;CAC5B,CAAA;AAED,oEAAoE;AACpE,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG;IAC9C,WAAW,CACT,IAAI,CAAC,EAAE,OAAO,GAAG,MAAM,GAAG,UAAU,GACnC,OAAO,CAAC,qBAAqB,CAAC,CAAA;CAClC,CAAA;AAED,MAAM,MAAM,qBAAqB,GAAG,cAAc,GAAG;IACnD,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACvB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;IACzB,KAAK,IAAI,IAAI,CAAA;CACd,CAAA;AAED,MAAM,MAAM,UAAU,GAAG,eAAe,CAAC,gBAAgB,CAAC,CAAA;AAa1D;;;;GAIG;AACH,wBAAgB,UAAU,CACxB,MAAM,EAAE,gBAAgB,EACxB,OAAO,CAAC,EAAE,iBAAiB,GAC1B,UAAU,CAiDZ"}
|
package/dist/libsql/crud.js
CHANGED
|
@@ -16,7 +16,7 @@ function normalizeRow(row) {
|
|
|
16
16
|
* Methods are async. The injected client is exposed as `crud.db`.
|
|
17
17
|
* Auth / URL are configured on the caller-created client (e.g. from env).
|
|
18
18
|
*/
|
|
19
|
-
export function createCrud(client) {
|
|
19
|
+
export function createCrud(client, options) {
|
|
20
20
|
if (client == null) {
|
|
21
21
|
throw new CrudianError("db is required");
|
|
22
22
|
}
|
|
@@ -58,5 +58,5 @@ export function createCrud(client) {
|
|
|
58
58
|
tx.close();
|
|
59
59
|
}
|
|
60
60
|
},
|
|
61
|
-
});
|
|
61
|
+
}, options);
|
|
62
62
|
}
|
package/dist/prisma/crud.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type CreateCrudOptions } from "../index.js";
|
|
1
2
|
import { type AsyncSqliteCrud } from "../sqlite/async-crud.js";
|
|
2
3
|
/** Minimal surface of PrismaClient used by the adapter (raw SQL + TX). */
|
|
3
4
|
export type PrismaLikeClient = {
|
|
@@ -10,5 +11,5 @@ export type PrismaCrud = AsyncSqliteCrud<PrismaLikeClient>;
|
|
|
10
11
|
* Create a Crud bound to a PrismaClient (SQLite).
|
|
11
12
|
* Methods are async. The injected client is exposed as `crud.db`.
|
|
12
13
|
*/
|
|
13
|
-
export declare function createCrud(client: PrismaLikeClient): PrismaCrud;
|
|
14
|
+
export declare function createCrud(client: PrismaLikeClient, options?: CreateCrudOptions): PrismaCrud;
|
|
14
15
|
//# sourceMappingURL=crud.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"crud.d.ts","sourceRoot":"","sources":["../../src/prisma/crud.ts"],"names":[],"mappings":"
|
|
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"}
|
package/dist/prisma/crud.js
CHANGED
|
@@ -12,7 +12,7 @@ function normalizeRow(row) {
|
|
|
12
12
|
* Create a Crud bound to a PrismaClient (SQLite).
|
|
13
13
|
* Methods are async. The injected client is exposed as `crud.db`.
|
|
14
14
|
*/
|
|
15
|
-
export function createCrud(client) {
|
|
15
|
+
export function createCrud(client, options) {
|
|
16
16
|
if (client == null) {
|
|
17
17
|
throw new CrudianError("db is required");
|
|
18
18
|
}
|
|
@@ -50,7 +50,7 @@ export function createCrud(client) {
|
|
|
50
50
|
}
|
|
51
51
|
});
|
|
52
52
|
},
|
|
53
|
-
});
|
|
53
|
+
}, options);
|
|
54
54
|
}
|
|
55
55
|
function normalizeQueryResult(result) {
|
|
56
56
|
if (result == null)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CountQuery, type DeleteQuery, type DuplicateQuery, type ReadQuery, type Row, type SearchQuery, type SearchResult, type UpdateQuery } from "../index.js";
|
|
1
|
+
import { type CountQuery, type CreateCrudOptions, type DeleteQuery, type DuplicateQuery, type ReadQuery, type Row, type SearchQuery, type SearchResult, type UpdateQuery } from "../index.js";
|
|
2
2
|
export type AsyncSqliteExecutor = {
|
|
3
3
|
run(sql: string, args?: unknown[]): Promise<{
|
|
4
4
|
changes: number;
|
|
@@ -22,7 +22,8 @@ export type AsyncSqliteCrud<TDb> = {
|
|
|
22
22
|
search<T extends Row = Row>(table: string, query?: SearchQuery): Promise<SearchResult<T>>;
|
|
23
23
|
list<T extends Row = Row>(table: string, query?: SearchQuery): Promise<SearchResult<T>>;
|
|
24
24
|
count(table: string, query?: CountQuery): Promise<number>;
|
|
25
|
+
exists(table: string, query?: CountQuery): Promise<boolean>;
|
|
25
26
|
transaction<T>(fn: () => Promise<T>): Promise<T>;
|
|
26
27
|
};
|
|
27
|
-
export declare function createAsyncSqliteCrud<TDb>(db: TDb, ex: AsyncSqliteExecutor): AsyncSqliteCrud<TDb>;
|
|
28
|
+
export declare function createAsyncSqliteCrud<TDb>(db: TDb, ex: AsyncSqliteExecutor, options?: CreateCrudOptions): AsyncSqliteCrud<TDb>;
|
|
28
29
|
//# 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,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,GAAG,EACR,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"async-crud.d.ts","sourceRoot":"","sources":["../../src/sqlite/async-crud.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CrudianError, assertString, } from "../index.js";
|
|
2
|
+
import { createAsyncPkGuard, resolvePk } from "./pk.js";
|
|
2
3
|
import { compileWhere, quoteIdent, resolveWhere } from "./sql.js";
|
|
3
4
|
function requireWhere(query, label) {
|
|
4
5
|
if (query.where === undefined || query.where === null) {
|
|
@@ -16,11 +17,14 @@ function rowFromObject(value) {
|
|
|
16
17
|
}
|
|
17
18
|
return { ...value };
|
|
18
19
|
}
|
|
19
|
-
export function createAsyncSqliteCrud(db, ex) {
|
|
20
|
+
export function createAsyncSqliteCrud(db, ex, options) {
|
|
21
|
+
const pk = resolvePk(options);
|
|
22
|
+
const ensurePkColumn = createAsyncPkGuard(pk, ex.all);
|
|
20
23
|
const crud = {
|
|
21
24
|
db,
|
|
22
25
|
async create(table, cols) {
|
|
23
26
|
assertString(table, "table");
|
|
27
|
+
await ensurePkColumn(table);
|
|
24
28
|
if (cols == null || typeof cols !== "object" || Array.isArray(cols)) {
|
|
25
29
|
throw new CrudianError("cols must be an object");
|
|
26
30
|
}
|
|
@@ -39,6 +43,7 @@ export function createAsyncSqliteCrud(db, ex) {
|
|
|
39
43
|
},
|
|
40
44
|
async read(table, query = {}) {
|
|
41
45
|
assertString(table, "table");
|
|
46
|
+
await ensurePkColumn(table);
|
|
42
47
|
const tbl = quoteIdent(table);
|
|
43
48
|
const where = compileWhere(resolveWhere(query.where));
|
|
44
49
|
const sql = `SELECT ${selectColumns(query.columns)} FROM ${tbl}` +
|
|
@@ -49,6 +54,7 @@ export function createAsyncSqliteCrud(db, ex) {
|
|
|
49
54
|
},
|
|
50
55
|
async update(table, cols, query) {
|
|
51
56
|
assertString(table, "table");
|
|
57
|
+
await ensurePkColumn(table);
|
|
52
58
|
requireWhere(query, "update");
|
|
53
59
|
if (cols == null || typeof cols !== "object" || Array.isArray(cols)) {
|
|
54
60
|
throw new CrudianError("cols must be an object");
|
|
@@ -72,6 +78,7 @@ export function createAsyncSqliteCrud(db, ex) {
|
|
|
72
78
|
},
|
|
73
79
|
async delete(table, query) {
|
|
74
80
|
assertString(table, "table");
|
|
81
|
+
await ensurePkColumn(table);
|
|
75
82
|
requireWhere(query, "delete");
|
|
76
83
|
const tbl = quoteIdent(table);
|
|
77
84
|
const where = compileWhere(resolveWhere(query.where));
|
|
@@ -83,10 +90,21 @@ export function createAsyncSqliteCrud(db, ex) {
|
|
|
83
90
|
},
|
|
84
91
|
async search(table, query = {}) {
|
|
85
92
|
assertString(table, "table");
|
|
93
|
+
await ensurePkColumn(table);
|
|
86
94
|
const limit = query.limit ?? 20;
|
|
87
95
|
if (typeof limit !== "number" || !Number.isFinite(limit) || limit <= 0) {
|
|
88
96
|
throw new CrudianError("limit must be a positive number");
|
|
89
97
|
}
|
|
98
|
+
const paging = query.paging ?? "offset";
|
|
99
|
+
if (paging !== "offset" && paging !== "cursor") {
|
|
100
|
+
throw new CrudianError('paging must be "offset" or "cursor"');
|
|
101
|
+
}
|
|
102
|
+
if (paging === "offset" && query.cursor !== undefined) {
|
|
103
|
+
throw new CrudianError("offset paging does not accept cursor");
|
|
104
|
+
}
|
|
105
|
+
if (paging === "cursor" && query.offset !== undefined) {
|
|
106
|
+
throw new CrudianError("cursor paging does not accept offset");
|
|
107
|
+
}
|
|
90
108
|
if (query.cursor != null &&
|
|
91
109
|
typeof query.cursor !== "number" &&
|
|
92
110
|
typeof query.cursor !== "string") {
|
|
@@ -94,27 +112,48 @@ export function createAsyncSqliteCrud(db, ex) {
|
|
|
94
112
|
}
|
|
95
113
|
const tbl = quoteIdent(table);
|
|
96
114
|
const where = compileWhere(resolveWhere(query.where));
|
|
115
|
+
const total = await crud.count(table, { where: query.where });
|
|
116
|
+
if (paging === "offset") {
|
|
117
|
+
const offset = query.offset ?? 0;
|
|
118
|
+
if (typeof offset !== "number" || !Number.isFinite(offset) || offset < 0) {
|
|
119
|
+
throw new CrudianError("offset must be a non-negative number");
|
|
120
|
+
}
|
|
121
|
+
const args = [...where.args];
|
|
122
|
+
const whereSql = where.sql ? ` WHERE ${where.sql}` : "";
|
|
123
|
+
const sql = `SELECT ${selectColumns(query.columns)} FROM ${tbl}` +
|
|
124
|
+
whereSql +
|
|
125
|
+
` ORDER BY ${quoteIdent(pk)} ASC LIMIT ? OFFSET ?`;
|
|
126
|
+
args.push(limit, offset);
|
|
127
|
+
const items = (await ex.all(sql, args)).map((r) => rowFromObject(r));
|
|
128
|
+
return {
|
|
129
|
+
items,
|
|
130
|
+
total,
|
|
131
|
+
offset,
|
|
132
|
+
limit,
|
|
133
|
+
hasMore: offset + items.length < total,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
97
136
|
const args = [...where.args];
|
|
98
137
|
const parts = [];
|
|
99
138
|
if (where.sql)
|
|
100
139
|
parts.push(`(${where.sql})`);
|
|
101
140
|
if (query.cursor != null) {
|
|
102
|
-
parts.push(`${quoteIdent(
|
|
141
|
+
parts.push(`${quoteIdent(pk)} > ?`);
|
|
103
142
|
args.push(query.cursor);
|
|
104
143
|
}
|
|
105
144
|
const whereSql = parts.length > 0 ? ` WHERE ${parts.join(" AND ")}` : "";
|
|
106
145
|
const sql = `SELECT ${selectColumns(query.columns)} FROM ${tbl}` +
|
|
107
146
|
whereSql +
|
|
108
|
-
` ORDER BY ${quoteIdent(
|
|
147
|
+
` ORDER BY ${quoteIdent(pk)} ASC LIMIT ?`;
|
|
109
148
|
args.push(limit + 1);
|
|
110
149
|
const rows = (await ex.all(sql, args)).map((r) => rowFromObject(r));
|
|
111
150
|
const hasMore = rows.length > limit;
|
|
112
151
|
const items = hasMore ? rows.slice(0, limit) : rows;
|
|
113
152
|
const last = items[items.length - 1];
|
|
114
|
-
const
|
|
115
|
-
|
|
153
|
+
const pkVal = last != null ? last[pk] : undefined;
|
|
154
|
+
const nextCursor = hasMore && last != null && (typeof pkVal === "number" || typeof pkVal === "string")
|
|
155
|
+
? pkVal
|
|
116
156
|
: null;
|
|
117
|
-
const total = await crud.count(table, { where: query.where });
|
|
118
157
|
return { items, nextCursor, hasMore, total };
|
|
119
158
|
},
|
|
120
159
|
async list(table, query) {
|
|
@@ -122,6 +161,7 @@ export function createAsyncSqliteCrud(db, ex) {
|
|
|
122
161
|
},
|
|
123
162
|
async count(table, query = {}) {
|
|
124
163
|
assertString(table, "table");
|
|
164
|
+
await ensurePkColumn(table);
|
|
125
165
|
const tbl = quoteIdent(table);
|
|
126
166
|
const where = compileWhere(resolveWhere(query.where));
|
|
127
167
|
const sql = `SELECT COUNT(*) AS ${quoteIdent("row_count")} FROM ${tbl}` +
|
|
@@ -129,8 +169,20 @@ export function createAsyncSqliteCrud(db, ex) {
|
|
|
129
169
|
const row = await ex.get(sql, where.args);
|
|
130
170
|
return Number(row?.row_count ?? 0);
|
|
131
171
|
},
|
|
172
|
+
async exists(table, query = {}) {
|
|
173
|
+
assertString(table, "table");
|
|
174
|
+
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}` +
|
|
178
|
+
(where.sql ? ` WHERE ${where.sql}` : "") +
|
|
179
|
+
` LIMIT 1`;
|
|
180
|
+
const row = await ex.get(sql, where.args);
|
|
181
|
+
return row != null;
|
|
182
|
+
},
|
|
132
183
|
async upsert(table, cols) {
|
|
133
184
|
assertString(table, "table");
|
|
185
|
+
await ensurePkColumn(table);
|
|
134
186
|
if (cols == null || typeof cols !== "object" || Array.isArray(cols)) {
|
|
135
187
|
throw new CrudianError("cols must be an object");
|
|
136
188
|
}
|
|
@@ -138,19 +190,20 @@ export function createAsyncSqliteCrud(db, ex) {
|
|
|
138
190
|
if (keys.length === 0) {
|
|
139
191
|
throw new CrudianError("cols must not be empty");
|
|
140
192
|
}
|
|
141
|
-
if (!Object.prototype.hasOwnProperty.call(cols,
|
|
142
|
-
throw new CrudianError(
|
|
193
|
+
if (!Object.prototype.hasOwnProperty.call(cols, pk)) {
|
|
194
|
+
throw new CrudianError(`upsert requires cols.${pk}`);
|
|
143
195
|
}
|
|
144
|
-
const id = cols
|
|
196
|
+
const id = cols[pk];
|
|
145
197
|
const existing = await crud.read(table, {
|
|
146
|
-
where: { type: "cond", op: "eq", column:
|
|
198
|
+
where: { type: "cond", op: "eq", column: pk, value: id },
|
|
147
199
|
});
|
|
148
200
|
if (existing != null) {
|
|
149
|
-
const
|
|
201
|
+
const patch = { ...cols };
|
|
202
|
+
delete patch[pk];
|
|
150
203
|
if (Object.keys(patch).length === 0)
|
|
151
204
|
return existing;
|
|
152
205
|
const updated = await crud.update(table, patch, {
|
|
153
|
-
where: { type: "cond", op: "eq", column:
|
|
206
|
+
where: { type: "cond", op: "eq", column: pk, value: id },
|
|
154
207
|
});
|
|
155
208
|
if (updated == null) {
|
|
156
209
|
throw new CrudianError("upsert update failed");
|
|
@@ -161,22 +214,25 @@ export function createAsyncSqliteCrud(db, ex) {
|
|
|
161
214
|
},
|
|
162
215
|
async duplicate(table, query) {
|
|
163
216
|
assertString(table, "table");
|
|
217
|
+
await ensurePkColumn(table);
|
|
164
218
|
requireWhere(query, "duplicate");
|
|
165
219
|
const source = await crud.read(table, { where: query.where });
|
|
166
220
|
if (source == null)
|
|
167
221
|
return null;
|
|
168
|
-
const
|
|
222
|
+
const rest = { ...source };
|
|
223
|
+
delete rest[pk];
|
|
169
224
|
const overrides = query.overrides != null &&
|
|
170
225
|
typeof query.overrides === "object" &&
|
|
171
226
|
!Array.isArray(query.overrides)
|
|
172
227
|
? query.overrides
|
|
173
228
|
: {};
|
|
174
229
|
const cols = { ...rest, ...overrides };
|
|
175
|
-
delete cols
|
|
230
|
+
delete cols[pk];
|
|
176
231
|
return crud.create(table, cols);
|
|
177
232
|
},
|
|
178
233
|
async bulkCreate(table, rows) {
|
|
179
234
|
assertString(table, "table");
|
|
235
|
+
await ensurePkColumn(table);
|
|
180
236
|
if (!Array.isArray(rows)) {
|
|
181
237
|
throw new CrudianError("rows must be an array");
|
|
182
238
|
}
|
|
@@ -194,6 +250,7 @@ export function createAsyncSqliteCrud(db, ex) {
|
|
|
194
250
|
},
|
|
195
251
|
async bulkUpdate(table, cols, query) {
|
|
196
252
|
assertString(table, "table");
|
|
253
|
+
await ensurePkColumn(table);
|
|
197
254
|
requireWhere(query, "bulkUpdate");
|
|
198
255
|
if (cols == null || typeof cols !== "object" || Array.isArray(cols)) {
|
|
199
256
|
throw new CrudianError("cols must be an object");
|
|
@@ -217,6 +274,7 @@ export function createAsyncSqliteCrud(db, ex) {
|
|
|
217
274
|
},
|
|
218
275
|
async bulkUpsert(table, rows) {
|
|
219
276
|
assertString(table, "table");
|
|
277
|
+
await ensurePkColumn(table);
|
|
220
278
|
if (!Array.isArray(rows)) {
|
|
221
279
|
throw new CrudianError("rows must be an array");
|
|
222
280
|
}
|
|
@@ -227,8 +285,8 @@ export function createAsyncSqliteCrud(db, ex) {
|
|
|
227
285
|
if (row == null || typeof row !== "object" || Array.isArray(row)) {
|
|
228
286
|
throw new CrudianError("each row must be an object");
|
|
229
287
|
}
|
|
230
|
-
if (!Object.prototype.hasOwnProperty.call(row,
|
|
231
|
-
throw new CrudianError(
|
|
288
|
+
if (!Object.prototype.hasOwnProperty.call(row, pk)) {
|
|
289
|
+
throw new CrudianError(`bulkUpsert requires each row to have ${pk}`);
|
|
232
290
|
}
|
|
233
291
|
await crud.upsert(table, row);
|
|
234
292
|
count += 1;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type CreateCrudOptions } from "../index.js";
|
|
2
|
+
/** Resolve PK column name from createCrud options. Default: `"id"`. */
|
|
3
|
+
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
|
+
type RowLike = Record<string, unknown>;
|
|
15
|
+
/**
|
|
16
|
+
* Ensure `pk` exists on `table` via PRAGMA table_info. Caches per table.
|
|
17
|
+
* Throws CrudianError when the column is missing.
|
|
18
|
+
*/
|
|
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>;
|
|
21
|
+
export {};
|
|
22
|
+
//# sourceMappingURL=pk.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { CrudianError } from "../index.js";
|
|
2
|
+
import { quoteIdent } from "./sql.js";
|
|
3
|
+
/** Resolve PK column name from createCrud options. Default: `"id"`. */
|
|
4
|
+
export function resolvePk(options) {
|
|
5
|
+
if (options == null || options.pk === undefined) {
|
|
6
|
+
return "id";
|
|
7
|
+
}
|
|
8
|
+
if (typeof options.pk !== "string") {
|
|
9
|
+
throw new CrudianError("pk must be a string");
|
|
10
|
+
}
|
|
11
|
+
if (options.pk === "") {
|
|
12
|
+
throw new CrudianError("pk must not be empty");
|
|
13
|
+
}
|
|
14
|
+
return options.pk;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Ensure `pk` exists on `table` via PRAGMA table_info. Caches per table.
|
|
18
|
+
* Throws CrudianError when the column is missing.
|
|
19
|
+
*/
|
|
20
|
+
export function createPkGuard(pk, get, all) {
|
|
21
|
+
const ok = new Set();
|
|
22
|
+
return function ensurePkColumn(table) {
|
|
23
|
+
if (ok.has(table))
|
|
24
|
+
return;
|
|
25
|
+
const rows = all(`PRAGMA table_info(${quoteIdent(table)})`);
|
|
26
|
+
const names = new Set(rows.map((r) => String(r.name ?? "")).filter((n) => n.length > 0));
|
|
27
|
+
if (!names.has(pk)) {
|
|
28
|
+
throw new CrudianError(`pk column "${pk}" does not exist on table "${table}"`);
|
|
29
|
+
}
|
|
30
|
+
ok.add(table);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
export function createAsyncPkGuard(pk, all) {
|
|
34
|
+
const ok = new Set();
|
|
35
|
+
return async function ensurePkColumn(table) {
|
|
36
|
+
if (ok.has(table))
|
|
37
|
+
return;
|
|
38
|
+
const rows = await all(`PRAGMA table_info(${quoteIdent(table)})`);
|
|
39
|
+
const names = new Set(rows.map((r) => String(r.name ?? "")).filter((n) => n.length > 0));
|
|
40
|
+
if (!names.has(pk)) {
|
|
41
|
+
throw new CrudianError(`pk column "${pk}" does not exist on table "${table}"`);
|
|
42
|
+
}
|
|
43
|
+
ok.add(table);
|
|
44
|
+
};
|
|
45
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type CountQuery, type DeleteQuery, type DuplicateQuery, type ReadQuery, type Row, type SearchQuery, type SearchResult, type UpdateQuery } from "../index.js";
|
|
1
|
+
import { type CountQuery, type CreateCrudOptions, type DeleteQuery, type DuplicateQuery, type ReadQuery, type Row, type SearchQuery, type SearchResult, type UpdateQuery } from "../index.js";
|
|
2
2
|
export type SyncSqliteExecutor = {
|
|
3
3
|
run(sql: string, args?: unknown[]): {
|
|
4
4
|
changes: number;
|
|
@@ -22,7 +22,8 @@ export type SyncSqliteCrud<TDb> = {
|
|
|
22
22
|
search<T extends Row = Row>(table: string, query?: SearchQuery): SearchResult<T>;
|
|
23
23
|
list<T extends Row = Row>(table: string, query?: SearchQuery): SearchResult<T>;
|
|
24
24
|
count(table: string, query?: CountQuery): number;
|
|
25
|
+
exists(table: string, query?: CountQuery): boolean;
|
|
25
26
|
transaction<T>(fn: () => T): T;
|
|
26
27
|
};
|
|
27
|
-
export declare function createSyncSqliteCrud<TDb>(db: TDb, ex: SyncSqliteExecutor): SyncSqliteCrud<TDb>;
|
|
28
|
+
export declare function createSyncSqliteCrud<TDb>(db: TDb, ex: SyncSqliteExecutor, options?: CreateCrudOptions): SyncSqliteCrud<TDb>;
|
|
28
29
|
//# 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":"AAAA,OAAO,EAGL,KAAK,UAAU,EACf,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,SAAS,EACd,KAAK,GAAG,EACR,KAAK,WAAW,EAChB,KAAK,YAAY,EACjB,KAAK,WAAW,EACjB,MAAM,aAAa,CAAA;
|
|
1
|
+
{"version":3,"file":"sync-crud.d.ts","sourceRoot":"","sources":["../../src/sqlite/sync-crud.ts"],"names":[],"mappings":"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"}
|
package/dist/sqlite/sync-crud.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { CrudianError, assertString, } from "../index.js";
|
|
2
|
+
import { createPkGuard, resolvePk } from "./pk.js";
|
|
2
3
|
import { compileWhere, quoteIdent, resolveWhere } from "./sql.js";
|
|
3
4
|
function requireWhere(query, label) {
|
|
4
5
|
if (query.where === undefined || query.where === null) {
|
|
@@ -16,11 +17,14 @@ function rowFromObject(value) {
|
|
|
16
17
|
}
|
|
17
18
|
return { ...value };
|
|
18
19
|
}
|
|
19
|
-
export function createSyncSqliteCrud(db, ex) {
|
|
20
|
+
export function createSyncSqliteCrud(db, ex, options) {
|
|
21
|
+
const pk = resolvePk(options);
|
|
22
|
+
const ensurePkColumn = createPkGuard(pk, ex.get, ex.all);
|
|
20
23
|
const crud = {
|
|
21
24
|
db,
|
|
22
25
|
create(table, cols) {
|
|
23
26
|
assertString(table, "table");
|
|
27
|
+
ensurePkColumn(table);
|
|
24
28
|
if (cols == null || typeof cols !== "object" || Array.isArray(cols)) {
|
|
25
29
|
throw new CrudianError("cols must be an object");
|
|
26
30
|
}
|
|
@@ -33,13 +37,15 @@ export function createSyncSqliteCrud(db, ex) {
|
|
|
33
37
|
const placeholders = keys.map(() => "?").join(", ");
|
|
34
38
|
const args = keys.map((k) => cols[k]);
|
|
35
39
|
ex.run(`INSERT INTO ${tbl} (${colSql}) VALUES (${placeholders})`, args);
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
40
|
+
const pkValue = Object.prototype.hasOwnProperty.call(cols, pk)
|
|
41
|
+
? 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]);
|
|
39
44
|
return rowFromObject(row);
|
|
40
45
|
},
|
|
41
46
|
read(table, query = {}) {
|
|
42
47
|
assertString(table, "table");
|
|
48
|
+
ensurePkColumn(table);
|
|
43
49
|
const tbl = quoteIdent(table);
|
|
44
50
|
const where = compileWhere(resolveWhere(query.where));
|
|
45
51
|
const sql = `SELECT ${selectColumns(query.columns)} FROM ${tbl}` +
|
|
@@ -50,6 +56,7 @@ export function createSyncSqliteCrud(db, ex) {
|
|
|
50
56
|
},
|
|
51
57
|
update(table, cols, query) {
|
|
52
58
|
assertString(table, "table");
|
|
59
|
+
ensurePkColumn(table);
|
|
53
60
|
requireWhere(query, "update");
|
|
54
61
|
if (cols == null || typeof cols !== "object" || Array.isArray(cols)) {
|
|
55
62
|
throw new CrudianError("cols must be an object");
|
|
@@ -73,6 +80,7 @@ export function createSyncSqliteCrud(db, ex) {
|
|
|
73
80
|
},
|
|
74
81
|
delete(table, query) {
|
|
75
82
|
assertString(table, "table");
|
|
83
|
+
ensurePkColumn(table);
|
|
76
84
|
requireWhere(query, "delete");
|
|
77
85
|
const tbl = quoteIdent(table);
|
|
78
86
|
const where = compileWhere(resolveWhere(query.where));
|
|
@@ -84,10 +92,21 @@ export function createSyncSqliteCrud(db, ex) {
|
|
|
84
92
|
},
|
|
85
93
|
search(table, query = {}) {
|
|
86
94
|
assertString(table, "table");
|
|
95
|
+
ensurePkColumn(table);
|
|
87
96
|
const limit = query.limit ?? 20;
|
|
88
97
|
if (typeof limit !== "number" || !Number.isFinite(limit) || limit <= 0) {
|
|
89
98
|
throw new CrudianError("limit must be a positive number");
|
|
90
99
|
}
|
|
100
|
+
const paging = query.paging ?? "offset";
|
|
101
|
+
if (paging !== "offset" && paging !== "cursor") {
|
|
102
|
+
throw new CrudianError('paging must be "offset" or "cursor"');
|
|
103
|
+
}
|
|
104
|
+
if (paging === "offset" && query.cursor !== undefined) {
|
|
105
|
+
throw new CrudianError("offset paging does not accept cursor");
|
|
106
|
+
}
|
|
107
|
+
if (paging === "cursor" && query.offset !== undefined) {
|
|
108
|
+
throw new CrudianError("cursor paging does not accept offset");
|
|
109
|
+
}
|
|
91
110
|
if (query.cursor != null &&
|
|
92
111
|
typeof query.cursor !== "number" &&
|
|
93
112
|
typeof query.cursor !== "string") {
|
|
@@ -95,27 +114,48 @@ export function createSyncSqliteCrud(db, ex) {
|
|
|
95
114
|
}
|
|
96
115
|
const tbl = quoteIdent(table);
|
|
97
116
|
const where = compileWhere(resolveWhere(query.where));
|
|
117
|
+
const total = crud.count(table, { where: query.where });
|
|
118
|
+
if (paging === "offset") {
|
|
119
|
+
const offset = query.offset ?? 0;
|
|
120
|
+
if (typeof offset !== "number" || !Number.isFinite(offset) || offset < 0) {
|
|
121
|
+
throw new CrudianError("offset must be a non-negative number");
|
|
122
|
+
}
|
|
123
|
+
const args = [...where.args];
|
|
124
|
+
const whereSql = where.sql ? ` WHERE ${where.sql}` : "";
|
|
125
|
+
const sql = `SELECT ${selectColumns(query.columns)} FROM ${tbl}` +
|
|
126
|
+
whereSql +
|
|
127
|
+
` ORDER BY ${quoteIdent(pk)} ASC LIMIT ? OFFSET ?`;
|
|
128
|
+
args.push(limit, offset);
|
|
129
|
+
const items = ex.all(sql, args).map((r) => rowFromObject(r));
|
|
130
|
+
return {
|
|
131
|
+
items,
|
|
132
|
+
total,
|
|
133
|
+
offset,
|
|
134
|
+
limit,
|
|
135
|
+
hasMore: offset + items.length < total,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
98
138
|
const args = [...where.args];
|
|
99
139
|
const parts = [];
|
|
100
140
|
if (where.sql)
|
|
101
141
|
parts.push(`(${where.sql})`);
|
|
102
142
|
if (query.cursor != null) {
|
|
103
|
-
parts.push(`${quoteIdent(
|
|
143
|
+
parts.push(`${quoteIdent(pk)} > ?`);
|
|
104
144
|
args.push(query.cursor);
|
|
105
145
|
}
|
|
106
146
|
const whereSql = parts.length > 0 ? ` WHERE ${parts.join(" AND ")}` : "";
|
|
107
147
|
const sql = `SELECT ${selectColumns(query.columns)} FROM ${tbl}` +
|
|
108
148
|
whereSql +
|
|
109
|
-
` ORDER BY ${quoteIdent(
|
|
149
|
+
` ORDER BY ${quoteIdent(pk)} ASC LIMIT ?`;
|
|
110
150
|
args.push(limit + 1);
|
|
111
151
|
const rows = ex.all(sql, args).map((r) => rowFromObject(r));
|
|
112
152
|
const hasMore = rows.length > limit;
|
|
113
153
|
const items = hasMore ? rows.slice(0, limit) : rows;
|
|
114
154
|
const last = items[items.length - 1];
|
|
115
|
-
const
|
|
116
|
-
|
|
155
|
+
const pkVal = last != null ? last[pk] : undefined;
|
|
156
|
+
const nextCursor = hasMore && last != null && (typeof pkVal === "number" || typeof pkVal === "string")
|
|
157
|
+
? pkVal
|
|
117
158
|
: null;
|
|
118
|
-
const total = crud.count(table, { where: query.where });
|
|
119
159
|
return { items, nextCursor, hasMore, total };
|
|
120
160
|
},
|
|
121
161
|
list(table, query) {
|
|
@@ -123,6 +163,7 @@ export function createSyncSqliteCrud(db, ex) {
|
|
|
123
163
|
},
|
|
124
164
|
count(table, query = {}) {
|
|
125
165
|
assertString(table, "table");
|
|
166
|
+
ensurePkColumn(table);
|
|
126
167
|
const tbl = quoteIdent(table);
|
|
127
168
|
const where = compileWhere(resolveWhere(query.where));
|
|
128
169
|
const sql = `SELECT COUNT(*) AS ${quoteIdent("row_count")} FROM ${tbl}` +
|
|
@@ -130,8 +171,19 @@ export function createSyncSqliteCrud(db, ex) {
|
|
|
130
171
|
const row = ex.get(sql, where.args);
|
|
131
172
|
return Number(row?.row_count ?? 0);
|
|
132
173
|
},
|
|
174
|
+
exists(table, query = {}) {
|
|
175
|
+
assertString(table, "table");
|
|
176
|
+
ensurePkColumn(table);
|
|
177
|
+
const tbl = quoteIdent(table);
|
|
178
|
+
const where = compileWhere(resolveWhere(query.where));
|
|
179
|
+
const sql = `SELECT 1 AS ${quoteIdent("ok")} FROM ${tbl}` +
|
|
180
|
+
(where.sql ? ` WHERE ${where.sql}` : "") +
|
|
181
|
+
` LIMIT 1`;
|
|
182
|
+
return ex.get(sql, where.args) != null;
|
|
183
|
+
},
|
|
133
184
|
upsert(table, cols) {
|
|
134
185
|
assertString(table, "table");
|
|
186
|
+
ensurePkColumn(table);
|
|
135
187
|
if (cols == null || typeof cols !== "object" || Array.isArray(cols)) {
|
|
136
188
|
throw new CrudianError("cols must be an object");
|
|
137
189
|
}
|
|
@@ -139,19 +191,20 @@ export function createSyncSqliteCrud(db, ex) {
|
|
|
139
191
|
if (keys.length === 0) {
|
|
140
192
|
throw new CrudianError("cols must not be empty");
|
|
141
193
|
}
|
|
142
|
-
if (!Object.prototype.hasOwnProperty.call(cols,
|
|
143
|
-
throw new CrudianError(
|
|
194
|
+
if (!Object.prototype.hasOwnProperty.call(cols, pk)) {
|
|
195
|
+
throw new CrudianError(`upsert requires cols.${pk}`);
|
|
144
196
|
}
|
|
145
|
-
const id = cols
|
|
197
|
+
const id = cols[pk];
|
|
146
198
|
const existing = crud.read(table, {
|
|
147
|
-
where: { type: "cond", op: "eq", column:
|
|
199
|
+
where: { type: "cond", op: "eq", column: pk, value: id },
|
|
148
200
|
});
|
|
149
201
|
if (existing != null) {
|
|
150
|
-
const
|
|
202
|
+
const patch = { ...cols };
|
|
203
|
+
delete patch[pk];
|
|
151
204
|
if (Object.keys(patch).length === 0)
|
|
152
205
|
return existing;
|
|
153
206
|
const updated = crud.update(table, patch, {
|
|
154
|
-
where: { type: "cond", op: "eq", column:
|
|
207
|
+
where: { type: "cond", op: "eq", column: pk, value: id },
|
|
155
208
|
});
|
|
156
209
|
if (updated == null) {
|
|
157
210
|
throw new CrudianError("upsert update failed");
|
|
@@ -162,22 +215,25 @@ export function createSyncSqliteCrud(db, ex) {
|
|
|
162
215
|
},
|
|
163
216
|
duplicate(table, query) {
|
|
164
217
|
assertString(table, "table");
|
|
218
|
+
ensurePkColumn(table);
|
|
165
219
|
requireWhere(query, "duplicate");
|
|
166
220
|
const source = crud.read(table, { where: query.where });
|
|
167
221
|
if (source == null)
|
|
168
222
|
return null;
|
|
169
|
-
const
|
|
223
|
+
const rest = { ...source };
|
|
224
|
+
delete rest[pk];
|
|
170
225
|
const overrides = query.overrides != null &&
|
|
171
226
|
typeof query.overrides === "object" &&
|
|
172
227
|
!Array.isArray(query.overrides)
|
|
173
228
|
? query.overrides
|
|
174
229
|
: {};
|
|
175
230
|
const cols = { ...rest, ...overrides };
|
|
176
|
-
delete cols
|
|
231
|
+
delete cols[pk];
|
|
177
232
|
return crud.create(table, cols);
|
|
178
233
|
},
|
|
179
234
|
bulkCreate(table, rows) {
|
|
180
235
|
assertString(table, "table");
|
|
236
|
+
ensurePkColumn(table);
|
|
181
237
|
if (!Array.isArray(rows)) {
|
|
182
238
|
throw new CrudianError("rows must be an array");
|
|
183
239
|
}
|
|
@@ -195,6 +251,7 @@ export function createSyncSqliteCrud(db, ex) {
|
|
|
195
251
|
},
|
|
196
252
|
bulkUpdate(table, cols, query) {
|
|
197
253
|
assertString(table, "table");
|
|
254
|
+
ensurePkColumn(table);
|
|
198
255
|
requireWhere(query, "bulkUpdate");
|
|
199
256
|
if (cols == null || typeof cols !== "object" || Array.isArray(cols)) {
|
|
200
257
|
throw new CrudianError("cols must be an object");
|
|
@@ -218,6 +275,7 @@ export function createSyncSqliteCrud(db, ex) {
|
|
|
218
275
|
},
|
|
219
276
|
bulkUpsert(table, rows) {
|
|
220
277
|
assertString(table, "table");
|
|
278
|
+
ensurePkColumn(table);
|
|
221
279
|
if (!Array.isArray(rows)) {
|
|
222
280
|
throw new CrudianError("rows must be an array");
|
|
223
281
|
}
|
|
@@ -228,8 +286,8 @@ export function createSyncSqliteCrud(db, ex) {
|
|
|
228
286
|
if (row == null || typeof row !== "object" || Array.isArray(row)) {
|
|
229
287
|
throw new CrudianError("each row must be an object");
|
|
230
288
|
}
|
|
231
|
-
if (!Object.prototype.hasOwnProperty.call(row,
|
|
232
|
-
throw new CrudianError(
|
|
289
|
+
if (!Object.prototype.hasOwnProperty.call(row, pk)) {
|
|
290
|
+
throw new CrudianError(`bulkUpsert requires each row to have ${pk}`);
|
|
233
291
|
}
|
|
234
292
|
crud.upsert(table, row);
|
|
235
293
|
count += 1;
|
package/dist/types.d.ts
CHANGED
|
@@ -1,12 +1,20 @@
|
|
|
1
1
|
import type { WhereBuilder, WhereNode } from "./where.js";
|
|
2
2
|
export type Row = Record<string, unknown>;
|
|
3
|
-
export type
|
|
3
|
+
export type OffsetSearchResult<T = Row> = {
|
|
4
|
+
items: T[];
|
|
5
|
+
total: number;
|
|
6
|
+
offset: number;
|
|
7
|
+
limit: number;
|
|
8
|
+
hasMore: boolean;
|
|
9
|
+
};
|
|
10
|
+
export type CursorSearchResult<T = Row> = {
|
|
4
11
|
items: T[];
|
|
5
12
|
nextCursor: number | string | null;
|
|
6
13
|
hasMore: boolean;
|
|
7
14
|
/** Rows matching `where` (ignores limit/cursor). */
|
|
8
15
|
total: number;
|
|
9
16
|
};
|
|
17
|
+
export type SearchResult<T = Row> = OffsetSearchResult<T> | CursorSearchResult<T>;
|
|
10
18
|
export type WhereInput = WhereBuilder | WhereNode;
|
|
11
19
|
export type ReadQuery = {
|
|
12
20
|
columns?: string[];
|
|
@@ -16,7 +24,11 @@ export type SearchQuery = {
|
|
|
16
24
|
columns?: string[];
|
|
17
25
|
where?: WhereInput;
|
|
18
26
|
limit?: number;
|
|
19
|
-
/**
|
|
27
|
+
/** Pagination mode. Default: `"offset"`. */
|
|
28
|
+
paging?: "offset" | "cursor";
|
|
29
|
+
/** Offset for `paging: "offset"` (default `0`). */
|
|
30
|
+
offset?: number;
|
|
31
|
+
/** Raw PK cursor (keyset; default column `id`) for `paging: "cursor"`. */
|
|
20
32
|
cursor?: number | string | null;
|
|
21
33
|
};
|
|
22
34
|
export type CountQuery = {
|
|
@@ -32,4 +44,9 @@ export type DuplicateQuery = {
|
|
|
32
44
|
where: WhereInput;
|
|
33
45
|
overrides?: Record<string, unknown>;
|
|
34
46
|
};
|
|
47
|
+
/** Options for `createCrud(db, options?)`. */
|
|
48
|
+
export type CreateCrudOptions = {
|
|
49
|
+
/** Primary key column name. Default: `"id"`. */
|
|
50
|
+
pk?: string;
|
|
51
|
+
};
|
|
35
52
|
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.d.ts.map
CHANGED
|
@@ -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,
|
|
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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@b4moss/crudian",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.0",
|
|
4
4
|
"description": "CRUD abstraction for DDD repositories",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,9 +43,9 @@
|
|
|
43
43
|
"prisma:generate": "prisma generate --schema prisma/schema.prisma",
|
|
44
44
|
"test": "bun run test:bun-sqlite && bun run test:drizzle && bun run test:prisma && bun run test:libsql",
|
|
45
45
|
"test:bun-sqlite": "bun test src/bun-sqlite",
|
|
46
|
-
"test:drizzle": "tsx --test src/drizzle/crud.test.ts",
|
|
47
|
-
"test:prisma": "bun run prisma:generate && tsx --test src/prisma/crud.test.ts",
|
|
48
|
-
"test:libsql": "tsx --test src/libsql/crud.test.ts"
|
|
46
|
+
"test:drizzle": "tsx --test src/drizzle/crud.test.ts src/drizzle/pk.test.ts",
|
|
47
|
+
"test:prisma": "bun run prisma:generate && tsx --test src/prisma/crud.test.ts src/prisma/pk.test.ts",
|
|
48
|
+
"test:libsql": "tsx --test src/libsql/crud.test.ts src/libsql/pk.test.ts"
|
|
49
49
|
},
|
|
50
50
|
"engines": {
|
|
51
51
|
"node": ">=24",
|
|
@@ -77,7 +77,7 @@
|
|
|
77
77
|
"@types/better-sqlite3": "^7.6.13",
|
|
78
78
|
"better-sqlite3": "^13",
|
|
79
79
|
"bun-types": "^1.2.0",
|
|
80
|
-
"drizzle-orm": "^0.
|
|
80
|
+
"drizzle-orm": "^0.45.2",
|
|
81
81
|
"prisma": "^6.14.0",
|
|
82
82
|
"tsx": "^4.23.12",
|
|
83
83
|
"typescript": "^5.8.0"
|