@apisr/drizzle-model 0.0.1 → 0.0.2
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.mts +3 -0
- package/dist/index.mjs +4 -0
- package/dist/model/builder.d.mts +20 -0
- package/dist/model/builder.mjs +18 -0
- package/dist/model/config.d.mts +23 -0
- package/dist/model/core/joins.mjs +184 -0
- package/dist/model/core/projection.mjs +28 -0
- package/dist/model/core/runtime.mjs +198 -0
- package/dist/model/core/thenable.mjs +64 -0
- package/dist/model/core/transform.mjs +39 -0
- package/dist/model/core/where.mjs +130 -0
- package/dist/model/core/with.mjs +19 -0
- package/dist/model/dialect.d.mts +12 -0
- package/dist/model/format.d.mts +4 -0
- package/dist/model/index.d.mts +1 -0
- package/dist/model/index.mjs +3 -0
- package/dist/model/methods/exclude.d.mts +8 -0
- package/dist/model/methods/include.d.mts +6 -0
- package/dist/model/methods/insert.d.mts +7 -0
- package/dist/model/methods/query/where.d.mts +14 -0
- package/dist/model/methods/return.d.mts +12 -0
- package/dist/model/methods/select.d.mts +8 -0
- package/dist/model/methods/update.d.mts +7 -0
- package/dist/model/methods/upsert.d.mts +26 -0
- package/dist/model/methods/with.d.mts +16 -0
- package/dist/model/model.d.mts +105 -0
- package/dist/model/options.d.mts +28 -0
- package/dist/model/query/operations.d.mts +66 -0
- package/dist/model/relation.d.mts +68 -0
- package/dist/model/result.d.mts +34 -0
- package/dist/model/table.d.mts +69 -0
- package/dist/types.d.mts +10 -0
- package/drizzle.config.ts +6 -6
- package/package.json +1 -1
- package/src/model/builder.ts +37 -37
- package/src/model/config.ts +28 -25
- package/src/model/core/joins.ts +337 -252
- package/src/model/core/projection.ts +49 -35
- package/src/model/core/runtime.ts +302 -221
- package/src/model/core/thenable.ts +70 -61
- package/src/model/core/transform.ts +53 -33
- package/src/model/core/where.ts +228 -162
- package/src/model/core/with.ts +21 -21
- package/src/model/dialect.ts +12 -2
- package/src/model/foreigns.ts +6 -10
- package/src/model/format.ts +8 -8
- package/src/model/methods/include.ts +1 -1
- package/src/model/methods/insert.ts +13 -10
- package/src/model/methods/levels.ts +7 -1
- package/src/model/methods/query/where.ts +49 -36
- package/src/model/methods/return.ts +13 -12
- package/src/model/methods/select.ts +29 -29
- package/src/model/methods/update.ts +3 -1
- package/src/model/methods/upsert.ts +35 -36
- package/src/model/model.ts +115 -107
- package/src/model/options.ts +44 -37
- package/src/model/query/operations.ts +73 -72
- package/src/model/relation.ts +47 -46
- package/src/model/result.ts +79 -63
- package/src/model/shape.ts +5 -4
- package/src/model/table.ts +34 -33
- package/src/types.ts +3 -3
- package/tests/builder-v2-mysql.type-test.ts +31 -20
- package/tests/builder-v2.type-test.ts +246 -253
- package/tests/builder.test.ts +1 -1
- package/tests/db.ts +3 -3
- package/tests/find.test.ts +149 -138
- package/tests/insert.test.ts +217 -203
- package/tests/relations.ts +34 -34
- package/tests/schema.ts +34 -35
package/dist/index.d.mts
ADDED
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { ModelDialect } from "./dialect.mjs";
|
|
2
|
+
import { ModelOptions } from "./options.mjs";
|
|
3
|
+
import { ModelConfig } from "./config.mjs";
|
|
4
|
+
import { Model } from "./model.mjs";
|
|
5
|
+
import { AnyRelations, EmptyRelations } from "drizzle-orm";
|
|
6
|
+
|
|
7
|
+
//#region src/model/builder.d.ts
|
|
8
|
+
declare function modelBuilder<TFullSchema extends Record<string, unknown> = Record<string, never>, TRelations extends AnyRelations = EmptyRelations, TDialect extends ModelDialect = ModelDialect>({
|
|
9
|
+
db,
|
|
10
|
+
relations,
|
|
11
|
+
schema,
|
|
12
|
+
dialect
|
|
13
|
+
}: {
|
|
14
|
+
relations: TRelations;
|
|
15
|
+
db: any;
|
|
16
|
+
schema: TFullSchema;
|
|
17
|
+
dialect: TDialect;
|
|
18
|
+
}): <TTableName extends keyof TRelations, TOptions extends ModelOptions<TRelations, TRelations[TTableName], TDialect, TOptions>>(table: TTableName, options: TOptions) => Model<ModelConfig<TRelations, TRelations[TTableName], TDialect, TOptions>>;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { modelBuilder };
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { makeModelRuntime } from "./core/runtime.mjs";
|
|
2
|
+
|
|
3
|
+
//#region src/model/builder.ts
|
|
4
|
+
function modelBuilder({ db, relations, schema, dialect }) {
|
|
5
|
+
return (table, options) => {
|
|
6
|
+
return makeModelRuntime({
|
|
7
|
+
db,
|
|
8
|
+
relations,
|
|
9
|
+
schema,
|
|
10
|
+
tableName: table,
|
|
11
|
+
dialect,
|
|
12
|
+
options: options ?? {}
|
|
13
|
+
});
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { modelBuilder };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { IsTable, TableOutput } from "./table.mjs";
|
|
2
|
+
import { ModelDialect } from "./dialect.mjs";
|
|
3
|
+
import { DrizzleColumnDataType } from "./query/operations.mjs";
|
|
4
|
+
import { MethodWhereValue } from "./methods/query/where.mjs";
|
|
5
|
+
import { ModelOptions, ResolveOptionsFormat } from "./options.mjs";
|
|
6
|
+
import { GetPrimarySerialOrDefaultKeys } from "./methods/return.mjs";
|
|
7
|
+
import { TableRelationalConfig, TablesRelationalConfig } from "drizzle-orm/relations";
|
|
8
|
+
|
|
9
|
+
//#region src/model/config.d.ts
|
|
10
|
+
type ModelConfig<TSchema extends TablesRelationalConfig = TablesRelationalConfig, TTable extends TableRelationalConfig = TableRelationalConfig, TDialect extends ModelDialect = ModelDialect, TOptions extends ModelOptions<any> = ModelOptions<any>> = {
|
|
11
|
+
schema: TSchema;
|
|
12
|
+
table: TTable;
|
|
13
|
+
dialect: TDialect;
|
|
14
|
+
options: TOptions;
|
|
15
|
+
tableOutput: TableOutput<TTable>;
|
|
16
|
+
tableColumns: IsTable<TTable["table"]>["_"]["columns"];
|
|
17
|
+
optionsFormat: ResolveOptionsFormat<TOptions["format"]>;
|
|
18
|
+
primaryKeys: keyof GetPrimarySerialOrDefaultKeys<IsTable<TTable["table"]>["_"]["columns"]>;
|
|
19
|
+
primaryKeysWithDataType: { [TKey in keyof GetPrimarySerialOrDefaultKeys<IsTable<TTable["table"]>["_"]["columns"]>]: DrizzleColumnDataType<IsTable<TTable["table"]>["_"]["columns"][TKey]> };
|
|
20
|
+
whereValue: MethodWhereValue<TSchema, TTable>;
|
|
21
|
+
};
|
|
22
|
+
//#endregion
|
|
23
|
+
export { ModelConfig };
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
import { and, eq } from "drizzle-orm";
|
|
2
|
+
|
|
3
|
+
//#region src/model/core/joins.ts
|
|
4
|
+
function isDrizzleColumn(value) {
|
|
5
|
+
return !!value && typeof value === "object" && typeof value.getSQL === "function";
|
|
6
|
+
}
|
|
7
|
+
function getPrimaryKeyField(table) {
|
|
8
|
+
for (const [k, v] of Object.entries(table)) {
|
|
9
|
+
if (!isDrizzleColumn(v)) continue;
|
|
10
|
+
if (v.primary === true) return k;
|
|
11
|
+
if (v.config?.primaryKey === true) return k;
|
|
12
|
+
}
|
|
13
|
+
if ("id" in table) return "id";
|
|
14
|
+
return Object.keys(table).find((k) => isDrizzleColumn(table[k])) ?? "id";
|
|
15
|
+
}
|
|
16
|
+
function isAllNullRow(obj) {
|
|
17
|
+
if (!obj || typeof obj !== "object") return true;
|
|
18
|
+
for (const v of Object.values(obj)) if (v !== null && v !== void 0) return false;
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
async function aliasTable(table, aliasName, dialect) {
|
|
22
|
+
if (dialect === "PostgreSQL") {
|
|
23
|
+
const mod = await import("drizzle-orm/pg-core");
|
|
24
|
+
if (typeof mod.alias === "function") return mod.alias(table, aliasName);
|
|
25
|
+
}
|
|
26
|
+
if (dialect === "MySQL") {
|
|
27
|
+
const mod = await import("drizzle-orm/mysql-core");
|
|
28
|
+
if (typeof mod.alias === "function") return mod.alias(table, aliasName);
|
|
29
|
+
}
|
|
30
|
+
if (dialect === "SQLite") {
|
|
31
|
+
const mod = await import("drizzle-orm/sqlite-core");
|
|
32
|
+
if (typeof mod.alias === "function") return mod.alias(table, aliasName);
|
|
33
|
+
}
|
|
34
|
+
return table;
|
|
35
|
+
}
|
|
36
|
+
function buildJoinOn(node) {
|
|
37
|
+
const parts = node.sourceColumns.map((src, i) => {
|
|
38
|
+
const tgt = node.targetColumns[i];
|
|
39
|
+
const tgtKey = Object.entries(node.targetTable).find(([, v]) => v === tgt)?.[0];
|
|
40
|
+
return eq(tgtKey ? node.targetAliasTable[tgtKey] : tgt, src);
|
|
41
|
+
});
|
|
42
|
+
return parts.length === 1 ? parts[0] : and(...parts);
|
|
43
|
+
}
|
|
44
|
+
function buildSelectMapForTable(table) {
|
|
45
|
+
const out = {};
|
|
46
|
+
for (const [k, v] of Object.entries(table)) if (isDrizzleColumn(v)) out[k] = v;
|
|
47
|
+
return out;
|
|
48
|
+
}
|
|
49
|
+
async function executeWithJoins(args) {
|
|
50
|
+
const { db, schema, relations, baseTableName, baseTable, dialect, whereSql, withValue, limitOne } = args;
|
|
51
|
+
const usedAliasKeys = /* @__PURE__ */ new Set();
|
|
52
|
+
const buildNode = async (parent, currentTableName, currentTable, key, value, path) => {
|
|
53
|
+
const relMeta = relations[currentTableName]?.relations?.[key];
|
|
54
|
+
if (!relMeta) throw new Error(`Unknown relation '${key}' on table '${currentTableName}'.`);
|
|
55
|
+
const targetTableName = relMeta.targetTableName;
|
|
56
|
+
const targetTable = schema[targetTableName];
|
|
57
|
+
const aliasKeyBase = [...path, key].join("__");
|
|
58
|
+
let aliasKey = aliasKeyBase;
|
|
59
|
+
let idx = 1;
|
|
60
|
+
while (usedAliasKeys.has(aliasKey)) aliasKey = `${aliasKeyBase}_${idx++}`;
|
|
61
|
+
usedAliasKeys.add(aliasKey);
|
|
62
|
+
const needsAlias = targetTableName === currentTableName || usedAliasKeys.has(`table:${targetTableName}`);
|
|
63
|
+
usedAliasKeys.add(`table:${targetTableName}`);
|
|
64
|
+
const targetAliasTable = needsAlias ? await aliasTable(targetTable, aliasKey, dialect) : targetTable;
|
|
65
|
+
const node = {
|
|
66
|
+
path: [...path, key],
|
|
67
|
+
key,
|
|
68
|
+
relationType: relMeta.relationType,
|
|
69
|
+
sourceTableName: currentTableName,
|
|
70
|
+
targetTableName,
|
|
71
|
+
sourceTable: currentTable,
|
|
72
|
+
targetTable,
|
|
73
|
+
targetAliasTable,
|
|
74
|
+
aliasKey,
|
|
75
|
+
sourceColumns: relMeta.sourceColumns ?? [],
|
|
76
|
+
targetColumns: relMeta.targetColumns ?? [],
|
|
77
|
+
pkField: getPrimaryKeyField(targetAliasTable),
|
|
78
|
+
parent,
|
|
79
|
+
children: []
|
|
80
|
+
};
|
|
81
|
+
if (value && typeof value === "object" && value !== true) for (const [childKey, childVal] of Object.entries(value)) {
|
|
82
|
+
if (childVal !== true && (typeof childVal !== "object" || childVal == null)) continue;
|
|
83
|
+
const child = await buildNode(node, targetTableName, targetAliasTable, childKey, childVal, [...path, key]);
|
|
84
|
+
node.children.push(child);
|
|
85
|
+
}
|
|
86
|
+
return node;
|
|
87
|
+
};
|
|
88
|
+
const root = {
|
|
89
|
+
path: [],
|
|
90
|
+
key: "$root",
|
|
91
|
+
relationType: "one",
|
|
92
|
+
sourceTableName: baseTableName,
|
|
93
|
+
targetTableName: baseTableName,
|
|
94
|
+
sourceTable: baseTable,
|
|
95
|
+
targetTable: baseTable,
|
|
96
|
+
targetAliasTable: baseTable,
|
|
97
|
+
aliasKey: "$base",
|
|
98
|
+
sourceColumns: [],
|
|
99
|
+
targetColumns: [],
|
|
100
|
+
pkField: getPrimaryKeyField(baseTable),
|
|
101
|
+
children: []
|
|
102
|
+
};
|
|
103
|
+
for (const [key, value] of Object.entries(withValue)) {
|
|
104
|
+
if (value !== true && (typeof value !== "object" || value == null)) continue;
|
|
105
|
+
const child = await buildNode(void 0, baseTableName, baseTable, key, value, []);
|
|
106
|
+
root.children.push(child);
|
|
107
|
+
}
|
|
108
|
+
const nodes = [];
|
|
109
|
+
const walk = (n) => {
|
|
110
|
+
for (const c of n.children) {
|
|
111
|
+
nodes.push(c);
|
|
112
|
+
walk(c);
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
walk(root);
|
|
116
|
+
const selectMap = { base: buildSelectMapForTable(baseTable) };
|
|
117
|
+
for (const n of nodes) selectMap[n.aliasKey] = buildSelectMapForTable(n.targetAliasTable);
|
|
118
|
+
let q = db.select(selectMap).from(baseTable);
|
|
119
|
+
if (whereSql) q = q.where(whereSql);
|
|
120
|
+
for (const n of nodes) {
|
|
121
|
+
const on = buildJoinOn(n);
|
|
122
|
+
q = q.leftJoin(n.targetAliasTable, on);
|
|
123
|
+
}
|
|
124
|
+
if (limitOne) q = q.limit(1);
|
|
125
|
+
const rows = await q;
|
|
126
|
+
const basePk = root.pkField;
|
|
127
|
+
const baseMap = /* @__PURE__ */ new Map();
|
|
128
|
+
const ensureManyContainer = (obj, key) => {
|
|
129
|
+
if (!Array.isArray(obj[key])) obj[key] = [];
|
|
130
|
+
};
|
|
131
|
+
const ensureOneContainer = (obj, key) => {
|
|
132
|
+
if (!(key in obj)) obj[key] = null;
|
|
133
|
+
};
|
|
134
|
+
const manyIndexByPath = /* @__PURE__ */ new Map();
|
|
135
|
+
for (const row of rows) {
|
|
136
|
+
const baseRow = row.base;
|
|
137
|
+
const baseId = baseRow[basePk];
|
|
138
|
+
if (baseId === void 0) continue;
|
|
139
|
+
const baseObj = (() => {
|
|
140
|
+
const existing = baseMap.get(baseId);
|
|
141
|
+
if (existing) return existing;
|
|
142
|
+
const created = { ...baseRow };
|
|
143
|
+
baseMap.set(baseId, created);
|
|
144
|
+
return created;
|
|
145
|
+
})();
|
|
146
|
+
for (const n of nodes) {
|
|
147
|
+
const data = row[n.aliasKey];
|
|
148
|
+
const relPath = n.path.join(".");
|
|
149
|
+
const parentPath = n.parent ? n.parent.path.join(".") : "";
|
|
150
|
+
let parentObj = baseObj;
|
|
151
|
+
if (parentPath) {
|
|
152
|
+
const parentIndex = manyIndexByPath.get(parentPath);
|
|
153
|
+
if (parentIndex && parentIndex.size) parentObj = Array.from(parentIndex.values()).at(-1);
|
|
154
|
+
else {
|
|
155
|
+
const parentKey = n.parent?.key;
|
|
156
|
+
parentObj = parentKey ? baseObj[parentKey] ?? baseObj : baseObj;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if (isAllNullRow(data)) {
|
|
160
|
+
if (n.relationType === "one") ensureOneContainer(parentObj, n.key);
|
|
161
|
+
else ensureManyContainer(parentObj, n.key);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const pk = data[n.pkField];
|
|
165
|
+
if (n.relationType === "one") parentObj[n.key] = { ...data };
|
|
166
|
+
else {
|
|
167
|
+
ensureManyContainer(parentObj, n.key);
|
|
168
|
+
const indexKey = relPath;
|
|
169
|
+
if (!manyIndexByPath.has(indexKey)) manyIndexByPath.set(indexKey, /* @__PURE__ */ new Map());
|
|
170
|
+
const idxMap = manyIndexByPath.get(indexKey);
|
|
171
|
+
if (!idxMap.has(pk)) {
|
|
172
|
+
const obj = { ...data };
|
|
173
|
+
idxMap.set(pk, obj);
|
|
174
|
+
parentObj[n.key].push(obj);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
const out = Array.from(baseMap.values());
|
|
180
|
+
return limitOne ? out[0] : out;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
//#endregion
|
|
184
|
+
export { executeWithJoins };
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
//#region src/model/core/projection.ts
|
|
2
|
+
function isDrizzleColumn(value) {
|
|
3
|
+
return !!value && typeof value === "object" && typeof value.getSQL === "function";
|
|
4
|
+
}
|
|
5
|
+
function getTableColumnsMap(table) {
|
|
6
|
+
const out = {};
|
|
7
|
+
for (const [key, value] of Object.entries(table)) if (isDrizzleColumn(value)) out[key] = value;
|
|
8
|
+
return out;
|
|
9
|
+
}
|
|
10
|
+
function buildSelectProjection(table, select, exclude) {
|
|
11
|
+
const all = getTableColumnsMap(table);
|
|
12
|
+
if (select && typeof select === "object") {
|
|
13
|
+
const picked = {};
|
|
14
|
+
for (const [key, value] of Object.entries(select)) if (value === true && key in all) picked[key] = all[key];
|
|
15
|
+
if (Object.keys(picked).length) return { selectMap: picked };
|
|
16
|
+
return { selectMap: all };
|
|
17
|
+
}
|
|
18
|
+
if (exclude && typeof exclude === "object") {
|
|
19
|
+
const omitted = { ...all };
|
|
20
|
+
for (const [key, value] of Object.entries(exclude)) if (value === true) delete omitted[key];
|
|
21
|
+
if (Object.keys(omitted).length) return { selectMap: omitted };
|
|
22
|
+
return { selectMap: all };
|
|
23
|
+
}
|
|
24
|
+
return { selectMap: all };
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
export { buildSelectProjection };
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { MutateResult, QueryResult } from "./thenable.mjs";
|
|
2
|
+
import { applyExclude, applyFormat, applySelect } from "./transform.mjs";
|
|
3
|
+
import { buildSelectProjection } from "./projection.mjs";
|
|
4
|
+
import { compileWhere } from "./where.mjs";
|
|
5
|
+
import { runWithJoins } from "./with.mjs";
|
|
6
|
+
import { and } from "drizzle-orm";
|
|
7
|
+
|
|
8
|
+
//#region src/model/core/runtime.ts
|
|
9
|
+
function compileEffectiveWhere(table, optionsWhere, stateWhere) {
|
|
10
|
+
const base = compileWhere(table, optionsWhere);
|
|
11
|
+
const extra = compileWhere(table, stateWhere);
|
|
12
|
+
if (base && extra) return and(base, extra);
|
|
13
|
+
return base ?? extra;
|
|
14
|
+
}
|
|
15
|
+
function isReturningIdDialect(dialect) {
|
|
16
|
+
return dialect === "MySQL" || dialect === "SingleStore" || dialect === "CockroachDB";
|
|
17
|
+
}
|
|
18
|
+
async function execReturn(q, mState, dialect) {
|
|
19
|
+
if (typeof q?.returning === "function") return await (mState.returnSelect ? q.returning(mState.returnSelect) : q.returning());
|
|
20
|
+
if (isReturningIdDialect(dialect) && typeof q?.$returningId === "function") return await q.$returningId();
|
|
21
|
+
return await q;
|
|
22
|
+
}
|
|
23
|
+
function normalizeUpsertTarget(table, target) {
|
|
24
|
+
if (!target) return target;
|
|
25
|
+
if (typeof target === "string") return table[target] ?? target;
|
|
26
|
+
if (Array.isArray(target)) return target.map((t) => typeof t === "string" ? table[t] ?? t : t);
|
|
27
|
+
return target;
|
|
28
|
+
}
|
|
29
|
+
function makeModelRuntime(config) {
|
|
30
|
+
const baseState = {
|
|
31
|
+
db: config.db,
|
|
32
|
+
where: void 0
|
|
33
|
+
};
|
|
34
|
+
const build = (state) => {
|
|
35
|
+
const modelObj = {
|
|
36
|
+
$model: "model",
|
|
37
|
+
$modelName: config.tableName,
|
|
38
|
+
$format: config.options.format,
|
|
39
|
+
$formatValue: void 0,
|
|
40
|
+
where(value) {
|
|
41
|
+
return build({
|
|
42
|
+
...state,
|
|
43
|
+
where: value
|
|
44
|
+
});
|
|
45
|
+
},
|
|
46
|
+
include(value) {
|
|
47
|
+
return value;
|
|
48
|
+
},
|
|
49
|
+
extend(nextOptions) {
|
|
50
|
+
return makeModelRuntime({
|
|
51
|
+
...config,
|
|
52
|
+
options: {
|
|
53
|
+
...config.options,
|
|
54
|
+
...nextOptions,
|
|
55
|
+
methods: {
|
|
56
|
+
...nextOptions?.methods ?? {},
|
|
57
|
+
...config.options?.methods ?? {}
|
|
58
|
+
},
|
|
59
|
+
format: nextOptions?.format ?? config.options?.format
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
},
|
|
63
|
+
db(db) {
|
|
64
|
+
return makeModelRuntime({
|
|
65
|
+
...config,
|
|
66
|
+
db
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
const attachMethods = (methods) => {
|
|
71
|
+
if (!methods) return;
|
|
72
|
+
for (const [key, fn] of Object.entries(methods)) if (typeof fn === "function") modelObj[key] = fn.bind(modelObj);
|
|
73
|
+
};
|
|
74
|
+
attachMethods(config.options.methods);
|
|
75
|
+
modelObj.findMany = () => {
|
|
76
|
+
const runner = async (qState) => {
|
|
77
|
+
const table = config.schema[config.tableName];
|
|
78
|
+
const whereSql = compileEffectiveWhere(table, config.options.where, state.where);
|
|
79
|
+
let result;
|
|
80
|
+
if (qState.with) result = await runWithJoins({
|
|
81
|
+
db: config.db,
|
|
82
|
+
schema: config.schema,
|
|
83
|
+
relations: config.relations,
|
|
84
|
+
tableName: config.tableName,
|
|
85
|
+
table,
|
|
86
|
+
dialect: config.dialect,
|
|
87
|
+
whereSql,
|
|
88
|
+
qState,
|
|
89
|
+
kind: "many"
|
|
90
|
+
});
|
|
91
|
+
else {
|
|
92
|
+
const { selectMap } = buildSelectProjection(table, qState.select, qState.exclude);
|
|
93
|
+
let q = config.db.select(selectMap).from(table);
|
|
94
|
+
if (whereSql) q = q.where(whereSql);
|
|
95
|
+
result = await q;
|
|
96
|
+
}
|
|
97
|
+
let out = result;
|
|
98
|
+
if (qState.select) out = applySelect(out, qState.select);
|
|
99
|
+
if (qState.exclude) out = applyExclude(out, qState.exclude);
|
|
100
|
+
if (!qState.raw) out = applyFormat(out, config.options.format);
|
|
101
|
+
return out;
|
|
102
|
+
};
|
|
103
|
+
return new QueryResult({}, runner);
|
|
104
|
+
};
|
|
105
|
+
modelObj.findFirst = () => {
|
|
106
|
+
const runner = async (qState) => {
|
|
107
|
+
const table = config.schema[config.tableName];
|
|
108
|
+
const whereSql = compileEffectiveWhere(table, config.options.where, state.where);
|
|
109
|
+
let result;
|
|
110
|
+
if (qState.with) result = await runWithJoins({
|
|
111
|
+
db: config.db,
|
|
112
|
+
schema: config.schema,
|
|
113
|
+
relations: config.relations,
|
|
114
|
+
tableName: config.tableName,
|
|
115
|
+
table,
|
|
116
|
+
dialect: config.dialect,
|
|
117
|
+
whereSql,
|
|
118
|
+
qState,
|
|
119
|
+
kind: "one"
|
|
120
|
+
});
|
|
121
|
+
else {
|
|
122
|
+
const { selectMap } = buildSelectProjection(table, qState.select, qState.exclude);
|
|
123
|
+
let q = config.db.select(selectMap).from(table);
|
|
124
|
+
if (whereSql) q = q.where(whereSql);
|
|
125
|
+
q = q.limit(1);
|
|
126
|
+
result = (await q)[0];
|
|
127
|
+
}
|
|
128
|
+
let out = result;
|
|
129
|
+
if (qState.select) out = applySelect(out, qState.select);
|
|
130
|
+
if (qState.exclude) out = applyExclude(out, qState.exclude);
|
|
131
|
+
if (!qState.raw) out = applyFormat(out, config.options.format);
|
|
132
|
+
return out;
|
|
133
|
+
};
|
|
134
|
+
return new QueryResult({}, runner);
|
|
135
|
+
};
|
|
136
|
+
modelObj.insert = (value) => {
|
|
137
|
+
const runner = async (mState) => {
|
|
138
|
+
const table = config.schema[config.tableName];
|
|
139
|
+
return await execReturn(config.db.insert(table).values(mState.value), mState, config.dialect);
|
|
140
|
+
};
|
|
141
|
+
return new MutateResult({
|
|
142
|
+
kind: "insert",
|
|
143
|
+
value
|
|
144
|
+
}, runner);
|
|
145
|
+
};
|
|
146
|
+
modelObj.update = (value) => {
|
|
147
|
+
const runner = async (mState) => {
|
|
148
|
+
const table = config.schema[config.tableName];
|
|
149
|
+
const whereSql = compileEffectiveWhere(table, config.options.where, state.where);
|
|
150
|
+
let q = config.db.update(table).set(mState.value);
|
|
151
|
+
if (whereSql) q = q.where(whereSql);
|
|
152
|
+
return await execReturn(q, mState, config.dialect);
|
|
153
|
+
};
|
|
154
|
+
return new MutateResult({
|
|
155
|
+
kind: "update",
|
|
156
|
+
value
|
|
157
|
+
}, runner);
|
|
158
|
+
};
|
|
159
|
+
modelObj.delete = () => {
|
|
160
|
+
const runner = async (mState) => {
|
|
161
|
+
const table = config.schema[config.tableName];
|
|
162
|
+
const whereSql = compileEffectiveWhere(table, config.options.where, state.where);
|
|
163
|
+
let q = config.db.delete(table);
|
|
164
|
+
if (whereSql) q = q.where(whereSql);
|
|
165
|
+
return await execReturn(q, mState, config.dialect);
|
|
166
|
+
};
|
|
167
|
+
return new MutateResult({ kind: "delete" }, runner);
|
|
168
|
+
};
|
|
169
|
+
modelObj.upsert = (value) => {
|
|
170
|
+
const runner = async (mState) => {
|
|
171
|
+
const table = config.schema[config.tableName];
|
|
172
|
+
const insertValues = mState.value.insert;
|
|
173
|
+
const updateCfg = mState.value.update;
|
|
174
|
+
const target = normalizeUpsertTarget(table, mState.value.target);
|
|
175
|
+
let updateSet = updateCfg;
|
|
176
|
+
if (typeof updateCfg === "function") updateSet = updateCfg({
|
|
177
|
+
excluded: (field) => table[field],
|
|
178
|
+
inserted: (field) => table[field]
|
|
179
|
+
});
|
|
180
|
+
let q = config.db.insert(table).values(insertValues);
|
|
181
|
+
if (q.onConflictDoUpdate) q = q.onConflictDoUpdate({
|
|
182
|
+
target,
|
|
183
|
+
set: updateSet
|
|
184
|
+
});
|
|
185
|
+
return await execReturn(q, mState, config.dialect);
|
|
186
|
+
};
|
|
187
|
+
return new MutateResult({
|
|
188
|
+
kind: "upsert",
|
|
189
|
+
value
|
|
190
|
+
}, runner);
|
|
191
|
+
};
|
|
192
|
+
return modelObj;
|
|
193
|
+
};
|
|
194
|
+
return build(baseState);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
//#endregion
|
|
198
|
+
export { makeModelRuntime };
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
//#region src/model/core/thenable.ts
|
|
2
|
+
var ThenableResult = class {
|
|
3
|
+
_execute;
|
|
4
|
+
constructor(execute) {
|
|
5
|
+
this._execute = execute;
|
|
6
|
+
}
|
|
7
|
+
then(onfulfilled, onrejected) {
|
|
8
|
+
return this._execute().then(onfulfilled, onrejected);
|
|
9
|
+
}
|
|
10
|
+
};
|
|
11
|
+
var QueryResult = class QueryResult extends ThenableResult {
|
|
12
|
+
state;
|
|
13
|
+
runner;
|
|
14
|
+
constructor(state, runner) {
|
|
15
|
+
super(() => runner(state));
|
|
16
|
+
this.state = state;
|
|
17
|
+
this.runner = runner;
|
|
18
|
+
}
|
|
19
|
+
with(value) {
|
|
20
|
+
return new QueryResult({
|
|
21
|
+
...this.state,
|
|
22
|
+
with: value
|
|
23
|
+
}, this.runner);
|
|
24
|
+
}
|
|
25
|
+
select(value) {
|
|
26
|
+
return new QueryResult({
|
|
27
|
+
...this.state,
|
|
28
|
+
select: value
|
|
29
|
+
}, this.runner);
|
|
30
|
+
}
|
|
31
|
+
exclude(value) {
|
|
32
|
+
return new QueryResult({
|
|
33
|
+
...this.state,
|
|
34
|
+
exclude: value
|
|
35
|
+
}, this.runner);
|
|
36
|
+
}
|
|
37
|
+
raw() {
|
|
38
|
+
return new QueryResult({
|
|
39
|
+
...this.state,
|
|
40
|
+
raw: true
|
|
41
|
+
}, this.runner);
|
|
42
|
+
}
|
|
43
|
+
debug() {
|
|
44
|
+
return this.state;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
var MutateResult = class MutateResult extends ThenableResult {
|
|
48
|
+
state;
|
|
49
|
+
runner;
|
|
50
|
+
constructor(state, runner) {
|
|
51
|
+
super(() => runner(state));
|
|
52
|
+
this.state = state;
|
|
53
|
+
this.runner = runner;
|
|
54
|
+
}
|
|
55
|
+
return(value) {
|
|
56
|
+
return new MutateResult({
|
|
57
|
+
...this.state,
|
|
58
|
+
returnSelect: value
|
|
59
|
+
}, this.runner);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
//#endregion
|
|
64
|
+
export { MutateResult, QueryResult };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//#region src/model/core/transform.ts
|
|
2
|
+
function applySelect(value, select) {
|
|
3
|
+
if (value == null) return value;
|
|
4
|
+
if (Array.isArray(value)) return value.map((v) => applySelect(v, select));
|
|
5
|
+
if (typeof value !== "object") return value;
|
|
6
|
+
const out = {};
|
|
7
|
+
for (const [key, sel] of Object.entries(select)) {
|
|
8
|
+
if (sel === true) {
|
|
9
|
+
out[key] = value[key];
|
|
10
|
+
continue;
|
|
11
|
+
}
|
|
12
|
+
if (sel && typeof sel === "object") out[key] = applySelect(value[key], sel);
|
|
13
|
+
}
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
function applyExclude(value, exclude) {
|
|
17
|
+
if (value == null) return value;
|
|
18
|
+
if (Array.isArray(value)) return value.map((v) => applyExclude(v, exclude));
|
|
19
|
+
if (typeof value !== "object") return value;
|
|
20
|
+
const out = { ...value };
|
|
21
|
+
for (const [key, ex] of Object.entries(exclude)) {
|
|
22
|
+
if (ex === true) {
|
|
23
|
+
delete out[key];
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
if (ex && typeof ex === "object" && key in out) out[key] = applyExclude(out[key], ex);
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
function applyFormat(value, format) {
|
|
31
|
+
if (!format) return value;
|
|
32
|
+
if (value == null) return value;
|
|
33
|
+
if (Array.isArray(value)) return value.map((v) => applyFormat(v, format));
|
|
34
|
+
if (typeof value !== "object") return value;
|
|
35
|
+
return format(value);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
//#endregion
|
|
39
|
+
export { applyExclude, applyFormat, applySelect };
|