@easy-cms/drizzle 0.9.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +12 -0
- package/dist/index.js +51 -25
- package/package.json +4 -4
package/dist/index.d.ts
CHANGED
|
@@ -58,11 +58,18 @@ interface Dialect {
|
|
|
58
58
|
* (any element when it returns undefined). Anything but an array has no elements.
|
|
59
59
|
*/
|
|
60
60
|
someElement(array: AnyColumn | SQL, condition: (el: JsonElement) => SQL | undefined): SQL;
|
|
61
|
+
/**
|
|
62
|
+
* True when a JSON column contains `pattern` (Postgres `@>`, which can skip unpacking arrays).
|
|
63
|
+
* Optional: without it, equality inside blocks uses `someElement`.
|
|
64
|
+
*/
|
|
65
|
+
jsonContains?(column: AnyColumn, pattern: unknown): SQL;
|
|
61
66
|
/**
|
|
62
67
|
* For sorting: the value at `path` in the first element that has one, stepping through the
|
|
63
68
|
* nested `lists` in each element (in order) like `someElement`.
|
|
64
69
|
*/
|
|
65
70
|
firstElementValue(array: AnyColumn | SQL, lists: readonly (readonly string[])[], path: readonly string[], type: JsonValueType): SQL;
|
|
71
|
+
/** SQL that copies the whole database to a file while it is in use (SQLite: `VACUUM INTO`). */
|
|
72
|
+
backupSQL?(file: string): string;
|
|
66
73
|
/** Placeholder for the n-th parameter (1-based) in raw SQL. */
|
|
67
74
|
param(n: number): string;
|
|
68
75
|
/** CREATE TABLE IF NOT EXISTS for the migrations table. */
|
|
@@ -85,6 +92,11 @@ interface DrizzleAdapterOptions {
|
|
|
85
92
|
interface Connection {
|
|
86
93
|
readonly db: DrizzleDb;
|
|
87
94
|
readonly runner: SqlRunner;
|
|
95
|
+
/**
|
|
96
|
+
* With a single-writer dialect: identifies the database (e.g. the SQLite file), so every
|
|
97
|
+
* instance in this process that writes to it shares one queue.
|
|
98
|
+
*/
|
|
99
|
+
readonly writeQueue?: string;
|
|
88
100
|
close(): Promise<void>;
|
|
89
101
|
}
|
|
90
102
|
/**
|
package/dist/index.js
CHANGED
|
@@ -300,7 +300,10 @@ function buildTable(builder, name, kind, fields, options) {
|
|
|
300
300
|
add("id", dialect.serial("id"), "id:int");
|
|
301
301
|
add("created_at", dialect.text("created_at").notNull(), "text!");
|
|
302
302
|
add("updated_at", dialect.text("updated_at").notNull(), "text!");
|
|
303
|
-
if (options.drafts)
|
|
303
|
+
if (options.drafts) {
|
|
304
|
+
add("status", dialect.text("status").notNull().default("draft"), "status");
|
|
305
|
+
indexes.push({ column: "status", unique: false });
|
|
306
|
+
}
|
|
304
307
|
indexes.push({ column: "created_at", unique: false });
|
|
305
308
|
} else {
|
|
306
309
|
const parentKind = options.parentIdKind ?? "integer";
|
|
@@ -937,9 +940,33 @@ var WhereBuilder = class {
|
|
|
937
940
|
if (inner === null) return void 0;
|
|
938
941
|
return operatorSQL(this.dialect, el.get(inner, type), field, op, value, path);
|
|
939
942
|
};
|
|
943
|
+
const contains = this.containment(target, op, value);
|
|
944
|
+
if (contains) return contains;
|
|
940
945
|
const some = this.dialect.someElement(target.column, (el) => match(el, 0));
|
|
941
946
|
return inner === null && value === false ? not(some) : some;
|
|
942
947
|
}
|
|
948
|
+
/**
|
|
949
|
+
* `equals` and `in` as JSON containment where the database has it: `layout.items.title = x`
|
|
950
|
+
* becomes `layout @> [{"items": [{"title": x}]}]`, which matches exactly the same documents
|
|
951
|
+
* without unpacking every array.
|
|
952
|
+
*/
|
|
953
|
+
containment(target, op, value) {
|
|
954
|
+
const { jsonContains } = this.dialect;
|
|
955
|
+
const { lists, path, type, field } = target.value;
|
|
956
|
+
if (!jsonContains || !path) return void 0;
|
|
957
|
+
const values = op === "equals" ? [value] : op === "in" && Array.isArray(value) ? value : null;
|
|
958
|
+
if (!values || values.length === 0 || values.some((v) => v === null || v === void 0))
|
|
959
|
+
return void 0;
|
|
960
|
+
const nest = (segments, inner) => segments.reduceRight((acc, segment) => ({ [segment]: acc }), inner);
|
|
961
|
+
const patterns = values.map((raw) => {
|
|
962
|
+
const coerced = coerce(field, raw);
|
|
963
|
+
let pattern = type === "text" && typeof coerced !== "string" ? String(coerced) : coerced;
|
|
964
|
+
pattern = nest(path, pattern);
|
|
965
|
+
for (const list of [...lists].reverse()) pattern = nest(list, [pattern]);
|
|
966
|
+
return jsonContains(target.column, [pattern]);
|
|
967
|
+
});
|
|
968
|
+
return patterns.length === 1 ? patterns[0] : or(...patterns);
|
|
969
|
+
}
|
|
943
970
|
/** Conditions on array rows or hasMany values become (NOT) EXISTS subqueries. */
|
|
944
971
|
child(parent, child, rest, op, value, path, locale) {
|
|
945
972
|
const table = child.table;
|
|
@@ -993,6 +1020,7 @@ var WhereBuilder = class {
|
|
|
993
1020
|
// src/database.ts
|
|
994
1021
|
var DEFAULT_TABLE_PREFIX = "ecms_";
|
|
995
1022
|
var DEFAULT_MIGRATION_DIR = "easy-cms/migrations";
|
|
1023
|
+
var writeQueues = /* @__PURE__ */ new Map();
|
|
996
1024
|
async function connectDatabase(dialect, connection, args, options = {}) {
|
|
997
1025
|
const prefix = options.tablePrefix ?? DEFAULT_TABLE_PREFIX;
|
|
998
1026
|
try {
|
|
@@ -1084,12 +1112,23 @@ var DrizzleDatabase = class {
|
|
|
1084
1112
|
const [row] = await this.db.select({ total: count() }).from(model.root.table).where(this.where(args.collection, args.where));
|
|
1085
1113
|
return Number(row?.total ?? 0);
|
|
1086
1114
|
}
|
|
1087
|
-
|
|
1088
|
-
|
|
1115
|
+
/**
|
|
1116
|
+
* Runs a write; with a single-writer database, after the writes before it in this process are
|
|
1117
|
+
* done. (Other processes are waited for by the database's busy timeout.)
|
|
1118
|
+
*/
|
|
1089
1119
|
write(run) {
|
|
1090
1120
|
if (!this.dialect.singleWriter) return run();
|
|
1091
|
-
const
|
|
1092
|
-
|
|
1121
|
+
const key = this.connection.writeQueue ?? "";
|
|
1122
|
+
const result = (writeQueues.get(key) ?? Promise.resolve()).then(run);
|
|
1123
|
+
const settled = result.then(
|
|
1124
|
+
() => {
|
|
1125
|
+
},
|
|
1126
|
+
() => {
|
|
1127
|
+
}
|
|
1128
|
+
);
|
|
1129
|
+
writeQueues.set(key, settled);
|
|
1130
|
+
void settled.then(() => {
|
|
1131
|
+
if (writeQueues.get(key) === settled) writeQueues.delete(key);
|
|
1093
1132
|
});
|
|
1094
1133
|
return result;
|
|
1095
1134
|
}
|
|
@@ -1150,30 +1189,17 @@ var DrizzleDatabase = class {
|
|
|
1150
1189
|
migrationStatus() {
|
|
1151
1190
|
return this.migrator.status();
|
|
1152
1191
|
}
|
|
1192
|
+
get backup() {
|
|
1193
|
+
const { backupSQL } = this.dialect;
|
|
1194
|
+
if (!backupSQL) return void 0;
|
|
1195
|
+
return (file) => this.write(async () => {
|
|
1196
|
+
await this.connection.runner.query(backupSQL(file));
|
|
1197
|
+
});
|
|
1198
|
+
}
|
|
1153
1199
|
async destroy() {
|
|
1154
1200
|
await this.connection.close();
|
|
1155
1201
|
}
|
|
1156
1202
|
};
|
|
1157
|
-
var BUSY_TIMEOUT = 1e4;
|
|
1158
|
-
async function retryWhileBusy(run) {
|
|
1159
|
-
const until = Date.now() + BUSY_TIMEOUT;
|
|
1160
|
-
for (let delay = 5; ; delay = Math.min(delay * 2, 250)) {
|
|
1161
|
-
try {
|
|
1162
|
-
return await run();
|
|
1163
|
-
} catch (error) {
|
|
1164
|
-
if (!isBusy(error) || Date.now() + delay > until) throw error;
|
|
1165
|
-
await new Promise((resolve2) => setTimeout(resolve2, delay + Math.random() * delay));
|
|
1166
|
-
}
|
|
1167
|
-
}
|
|
1168
|
-
}
|
|
1169
|
-
function isBusy(error) {
|
|
1170
|
-
for (let e = error; e; e = e.cause) {
|
|
1171
|
-
const code = e.code;
|
|
1172
|
-
if (typeof code === "string" && code.startsWith("SQLITE_BUSY")) return true;
|
|
1173
|
-
if (e === e.cause) break;
|
|
1174
|
-
}
|
|
1175
|
-
return false;
|
|
1176
|
-
}
|
|
1177
1203
|
export {
|
|
1178
1204
|
DEFAULT_MIGRATION_DIR,
|
|
1179
1205
|
DEFAULT_TABLE_PREFIX,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@easy-cms/drizzle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Shared Drizzle ORM layer for Easy CMS database adapters",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -23,17 +23,17 @@
|
|
|
23
23
|
"drizzle-orm": "0.45.3"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
|
-
"@easy-cms/core": "^0.
|
|
26
|
+
"@easy-cms/core": "^0.10.0"
|
|
27
27
|
},
|
|
28
28
|
"devDependencies": {
|
|
29
|
-
"@easy-cms/core": "^0.
|
|
29
|
+
"@easy-cms/core": "^0.10.0"
|
|
30
30
|
},
|
|
31
31
|
"publishConfig": {
|
|
32
32
|
"access": "public"
|
|
33
33
|
},
|
|
34
34
|
"repository": {
|
|
35
35
|
"type": "git",
|
|
36
|
-
"url": "git+https://github.com/maritonx/easy-
|
|
36
|
+
"url": "git+https://github.com/maritonx/easy-cms.git",
|
|
37
37
|
"directory": "packages/drizzle"
|
|
38
38
|
},
|
|
39
39
|
"keywords": [
|