sedentary 0.0.25 → 0.0.28
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/{db.js → dist/cjs/db.js}
RENAMED
|
File without changes
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Sedentary = exports.Type = exports.Transaction = exports.EntryBase = void 0;
|
|
3
|
+
exports.Sedentary = exports.Type = exports.Transaction = exports.Table = exports.EntryBase = exports.DB = exports.Attribute = void 0;
|
|
4
4
|
const db_1 = require("./db");
|
|
5
5
|
var db_2 = require("./db");
|
|
6
|
+
Object.defineProperty(exports, "Attribute", { enumerable: true, get: function () { return db_2.Attribute; } });
|
|
7
|
+
Object.defineProperty(exports, "DB", { enumerable: true, get: function () { return db_2.DB; } });
|
|
6
8
|
Object.defineProperty(exports, "EntryBase", { enumerable: true, get: function () { return db_2.EntryBase; } });
|
|
9
|
+
Object.defineProperty(exports, "Table", { enumerable: true, get: function () { return db_2.Table; } });
|
|
7
10
|
Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return db_2.Transaction; } });
|
|
8
11
|
Object.defineProperty(exports, "Type", { enumerable: true, get: function () { return db_2.Type; } });
|
|
9
12
|
const operators = ["=", ">", "<", ">=", "<=", "<>", "IN", "IS NULL", "LIKE", "NOT"];
|
|
@@ -23,7 +26,7 @@ class Sedentary {
|
|
|
23
26
|
for (const k in options)
|
|
24
27
|
if (!["autoSync", "log", "sync"].includes(k))
|
|
25
28
|
throw new Error(`new Sedentary: 'options' argument: Unknown '${k}' option`);
|
|
26
|
-
const { autoSync, log, sync } =
|
|
29
|
+
const { autoSync, log, sync } = { autoSync: true, sync: true, ...options };
|
|
27
30
|
if (typeof autoSync !== "boolean")
|
|
28
31
|
throw new Error("new Sedentary: 'autoSync' option: Wrong type, expected 'boolean'");
|
|
29
32
|
if (log !== null && log !== undefined && !(log instanceof Function))
|
|
@@ -212,10 +215,10 @@ class Sedentary {
|
|
|
212
215
|
if (options.parent && options.primaryKey)
|
|
213
216
|
throw new Error(`Sedentary.model: '${modelName}' model: 'parent' and 'primaryKey' options conflict each other`);
|
|
214
217
|
let autoIncrement = true;
|
|
215
|
-
const { indexes, int8id, parent, primaryKey, sync, tableName } =
|
|
218
|
+
const { indexes, int8id, parent, primaryKey, sync, tableName } = { sync: this.doSync, tableName: modelName, ...options };
|
|
216
219
|
let aarray = int8id
|
|
217
|
-
? [new db_1.Attribute(
|
|
218
|
-
: [new db_1.Attribute(
|
|
220
|
+
? [new db_1.Attribute({ ...this.INT8(), attributeName: "id", fieldName: "id", modelName, notNull: true, tableName, unique: true })]
|
|
221
|
+
: [new db_1.Attribute({ ...this.INT(4), attributeName: "id", fieldName: "id", modelName, notNull: true, tableName, unique: true })];
|
|
219
222
|
let constraints = [{ attribute: aarray[0], constraintName: `${tableName}_id_unique`, type: "u" }];
|
|
220
223
|
const iarray = [];
|
|
221
224
|
let pk = aarray[0];
|
|
@@ -233,7 +236,7 @@ class Sedentary {
|
|
|
233
236
|
throw new Error(`Sedentary.model: '${modelName}' model: 'primaryKey' option: Attribute '${primaryKey}' does not exists`);
|
|
234
237
|
if (parent || primaryKey) {
|
|
235
238
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
236
|
-
attr2field = parent ?
|
|
239
|
+
attr2field = parent ? { ...parent.attr2field } : {};
|
|
237
240
|
autoIncrement = false;
|
|
238
241
|
aarray = [];
|
|
239
242
|
constraints = [];
|
|
@@ -246,18 +249,18 @@ class Sedentary {
|
|
|
246
249
|
throw new Error(`${message1} 'this.FKEY' can't be used directly`);
|
|
247
250
|
if (func !== this.DATETIME && func !== this.INT && func !== this.INT8 && func !== this.VARCHAR)
|
|
248
251
|
throw new Error(`${message1} ${message2}`);
|
|
249
|
-
return new db_1.Attribute(
|
|
252
|
+
return new db_1.Attribute({ attributeName, defaultValue, fieldName, modelName, notNull, tableName, unique, ...func() });
|
|
250
253
|
};
|
|
251
254
|
const attributeDefinition = attributes[attributeName];
|
|
252
255
|
let { base, defaultValue, fieldName, foreignKey, notNull, size, type, unique } = (() => {
|
|
253
256
|
const ret = (() => {
|
|
254
257
|
if (attributeDefinition instanceof db_1.Type)
|
|
255
|
-
return new db_1.Attribute(
|
|
258
|
+
return new db_1.Attribute({ attributeName, fieldName: attributeName, modelName, notNull: false, tableName, ...attributeDefinition });
|
|
256
259
|
if (attributeDefinition instanceof Function)
|
|
257
260
|
return call(undefined, attributeName, false, false, attributeDefinition, `Sedentary.model: '${modelName}' model: '${attributeName}' attribute:`, "Wrong type, expected 'Attribute'");
|
|
258
261
|
if (!(attributeDefinition instanceof Object))
|
|
259
262
|
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: Wrong attribute type, expected 'Attribute'`);
|
|
260
|
-
const attributeDefaults =
|
|
263
|
+
const attributeDefaults = { defaultValue: undefined, fieldName: attributeName, notNull: false, unique: false, ...attributeDefinition };
|
|
261
264
|
const { defaultValue, fieldName, notNull, unique, type } = attributeDefaults;
|
|
262
265
|
if (defaultValue === null)
|
|
263
266
|
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'defaultValue' option: Does 'null' default value really makes sense?`);
|
|
@@ -270,7 +273,7 @@ class Sedentary {
|
|
|
270
273
|
if (type === undefined)
|
|
271
274
|
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: Missing 'type' option`);
|
|
272
275
|
if (type instanceof db_1.Type)
|
|
273
|
-
return new db_1.Attribute(
|
|
276
|
+
return new db_1.Attribute({ attributeName, defaultValue, fieldName, modelName, notNull, tableName, unique, ...type });
|
|
274
277
|
if (type instanceof Function)
|
|
275
278
|
return call(defaultValue, fieldName, notNull, unique, type, `Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'type' option:`, "Wrong type, expected 'Type'");
|
|
276
279
|
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'type' option: Wrong type, expected 'Type'`);
|
|
@@ -348,7 +351,7 @@ class Sedentary {
|
|
|
348
351
|
for (const k in idx)
|
|
349
352
|
if (!["attributes", "type", "unique"].includes(k))
|
|
350
353
|
throw new Error(`Sedentary.model: '${modelName}' model: '${indexName}' index: Unknown index option '${k}'`);
|
|
351
|
-
({ attributes, type, unique } =
|
|
354
|
+
({ attributes, type, unique } = { type: "btree", unique: false, ...idx });
|
|
352
355
|
if (!attributes)
|
|
353
356
|
throw new Error(`Sedentary.model: '${modelName}' model: '${indexName}' index: Missing 'attributes' option`);
|
|
354
357
|
if (attributes instanceof Array)
|
package/dist/es/db.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
export class EntryBase {
|
|
2
|
+
constructor(from) {
|
|
3
|
+
if (from === "load")
|
|
4
|
+
this.preLoad();
|
|
5
|
+
else {
|
|
6
|
+
if (from)
|
|
7
|
+
Object.assign(this, from);
|
|
8
|
+
this.construct();
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
construct() { }
|
|
12
|
+
postLoad() { }
|
|
13
|
+
postSave() { }
|
|
14
|
+
preLoad() { }
|
|
15
|
+
preSave() { }
|
|
16
|
+
async save() {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
export class Type {
|
|
21
|
+
constructor(from) {
|
|
22
|
+
Object.assign(this, from);
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
export class Attribute extends Type {
|
|
26
|
+
constructor(from) {
|
|
27
|
+
super(from);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
function autoImplement() {
|
|
31
|
+
return class {
|
|
32
|
+
constructor(defaults) {
|
|
33
|
+
Object.assign(this, defaults);
|
|
34
|
+
}
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
export class Table extends autoImplement() {
|
|
38
|
+
autoIncrementOwn;
|
|
39
|
+
oid;
|
|
40
|
+
findField(name) {
|
|
41
|
+
return this.attributes.filter(_ => _.fieldName === name)[0];
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export class DB {
|
|
45
|
+
tables = [];
|
|
46
|
+
log;
|
|
47
|
+
sync = true;
|
|
48
|
+
constructor(log) {
|
|
49
|
+
this.log = log;
|
|
50
|
+
}
|
|
51
|
+
findTable(name) {
|
|
52
|
+
return this.tables.filter(_ => _.tableName === name)[0];
|
|
53
|
+
}
|
|
54
|
+
indexesEq(a, b) {
|
|
55
|
+
if (a.fields.length !== b.fields.length)
|
|
56
|
+
return false;
|
|
57
|
+
for (const i in a.fields)
|
|
58
|
+
if (a.fields[i] !== b.fields[i])
|
|
59
|
+
return false;
|
|
60
|
+
if (a.type !== b.type)
|
|
61
|
+
return false;
|
|
62
|
+
if (a.unique !== b.unique)
|
|
63
|
+
return false;
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
async syncDataBase() {
|
|
67
|
+
for (const table of this.tables) {
|
|
68
|
+
this.sync = table.sync;
|
|
69
|
+
await this.syncTable(table);
|
|
70
|
+
const indexes = await this.dropConstraints(table);
|
|
71
|
+
await this.dropIndexes(table, indexes);
|
|
72
|
+
await this.dropFields(table);
|
|
73
|
+
await this.syncFields(table);
|
|
74
|
+
await this.syncSequence(table);
|
|
75
|
+
await this.syncConstraints(table);
|
|
76
|
+
await this.syncIndexes(table);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
syncLog(message) {
|
|
80
|
+
this.log(this.sync ? message : "NOT SYNCING: " + message);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
export class Transaction {
|
|
84
|
+
entries = [];
|
|
85
|
+
addEntry(entry) {
|
|
86
|
+
this.entries.push(entry);
|
|
87
|
+
}
|
|
88
|
+
clean() {
|
|
89
|
+
const { entries } = this;
|
|
90
|
+
for (const entry of entries)
|
|
91
|
+
delete entry.tx;
|
|
92
|
+
this.entries = [];
|
|
93
|
+
}
|
|
94
|
+
async commit() {
|
|
95
|
+
this.clean();
|
|
96
|
+
}
|
|
97
|
+
async rollback() {
|
|
98
|
+
this.clean();
|
|
99
|
+
}
|
|
100
|
+
}
|
package/dist/es/index.js
ADDED
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
import { Attribute, EntryBase, Table, Transaction, Type } from "./db";
|
|
2
|
+
export { Attribute, DB, EntryBase, Table, Transaction, Type } from "./db";
|
|
3
|
+
const operators = ["=", ">", "<", ">=", "<=", "<>", "IN", "IS NULL", "LIKE", "NOT"];
|
|
4
|
+
const allowedOption = ["indexes", "int8id", "parent", "primaryKey", "sync", "tableName"];
|
|
5
|
+
const reservedNames = [
|
|
6
|
+
...["attr2field", "attributeName", "attributes", "base", "class", "construct", "constructor", "defaultValue", "entry", "fieldName", "foreignKeys", "load"],
|
|
7
|
+
...["loaded", "methods", "name", "postLoad", "postSave", "preLoad", "preSave", "primaryKey", "prototype", "save", "size", "tableName", "tx", "type"]
|
|
8
|
+
];
|
|
9
|
+
export class Sedentary {
|
|
10
|
+
autoSync;
|
|
11
|
+
db;
|
|
12
|
+
doSync = true;
|
|
13
|
+
log;
|
|
14
|
+
models = {};
|
|
15
|
+
constructor(options) {
|
|
16
|
+
if (!options)
|
|
17
|
+
options = {};
|
|
18
|
+
if (!(options instanceof Object))
|
|
19
|
+
throw new Error("new Sedentary: 'options' argument: Wrong type, expected 'Object'");
|
|
20
|
+
for (const k in options)
|
|
21
|
+
if (!["autoSync", "log", "sync"].includes(k))
|
|
22
|
+
throw new Error(`new Sedentary: 'options' argument: Unknown '${k}' option`);
|
|
23
|
+
const { autoSync, log, sync } = { autoSync: true, sync: true, ...options };
|
|
24
|
+
if (typeof autoSync !== "boolean")
|
|
25
|
+
throw new Error("new Sedentary: 'autoSync' option: Wrong type, expected 'boolean'");
|
|
26
|
+
if (log !== null && log !== undefined && !(log instanceof Function))
|
|
27
|
+
throw new Error("new Sedentary: 'log' option: Wrong type, expected 'null' or 'Function'");
|
|
28
|
+
if (typeof sync !== "boolean")
|
|
29
|
+
throw new Error("new Sedentary: 'sync' option: Wrong type, expected 'boolean'");
|
|
30
|
+
this.autoSync = autoSync;
|
|
31
|
+
this.db = null;
|
|
32
|
+
// eslint-disable-next-line no-console
|
|
33
|
+
this.log = log ? log : log === null ? () => { } : console.log;
|
|
34
|
+
this.doSync = sync;
|
|
35
|
+
}
|
|
36
|
+
DATETIME() {
|
|
37
|
+
return new Type({ base: Date, type: "DATETIME" });
|
|
38
|
+
}
|
|
39
|
+
FKEY(attribute, options) {
|
|
40
|
+
const { attributeName, base, fieldName, size, tableName, type } = attribute;
|
|
41
|
+
return new Type({ base, foreignKey: { attributeName, fieldName, options, tableName }, size, type });
|
|
42
|
+
}
|
|
43
|
+
INT(size) {
|
|
44
|
+
const message = "Sedentary.INT: 'size' argument: Wrong value, expected 2 or 4";
|
|
45
|
+
size = size ? this.checkSize(size, message) : 4;
|
|
46
|
+
if (size !== 2 && size !== 4)
|
|
47
|
+
throw new Error(message);
|
|
48
|
+
return new Type({ base: Number, size, type: "INT" });
|
|
49
|
+
}
|
|
50
|
+
INT8() {
|
|
51
|
+
return new Type({ base: String, size: 8, type: "INT8" });
|
|
52
|
+
}
|
|
53
|
+
VARCHAR(size) {
|
|
54
|
+
const message = "Sedentary.VARCHAR: 'size' argument: Wrong value, expected positive integer";
|
|
55
|
+
size = size ? this.checkSize(size, message) : undefined;
|
|
56
|
+
return new Type({ base: String, size, type: "VARCHAR" });
|
|
57
|
+
}
|
|
58
|
+
checkDB() {
|
|
59
|
+
if (!this.db)
|
|
60
|
+
throw new Error("Package sedentary can't be used directly. Please check: https://www.npmjs.com/package/sedentary#disclaimer");
|
|
61
|
+
}
|
|
62
|
+
checkOrderBy(order, attributes, modelName) {
|
|
63
|
+
if (!order)
|
|
64
|
+
return true;
|
|
65
|
+
if (!(order instanceof Array))
|
|
66
|
+
return false;
|
|
67
|
+
const provided = {};
|
|
68
|
+
for (const attribute of order) {
|
|
69
|
+
if (typeof attribute !== "string")
|
|
70
|
+
return false;
|
|
71
|
+
const attributeName = attribute.startsWith("-") ? attribute.substring(1) : attribute;
|
|
72
|
+
if (!(attributeName in attributes))
|
|
73
|
+
throw new Error(`${modelName}.load: 'order' argument: '${attributeName}' is not an attribute name`);
|
|
74
|
+
if (provided[attributeName])
|
|
75
|
+
throw new Error(`${modelName}.load: 'order' argument: Reused '${attributeName}' attribute`);
|
|
76
|
+
provided[attributeName] = true;
|
|
77
|
+
}
|
|
78
|
+
return true;
|
|
79
|
+
}
|
|
80
|
+
checkSize(size, message) {
|
|
81
|
+
const str = size.toString();
|
|
82
|
+
const parsed = parseInt(str, 10);
|
|
83
|
+
if (str !== parsed.toString())
|
|
84
|
+
throw new Error(message);
|
|
85
|
+
return parsed;
|
|
86
|
+
}
|
|
87
|
+
createWhere(modelName, attributes, where) {
|
|
88
|
+
if (typeof where === "string")
|
|
89
|
+
return [where, true, true];
|
|
90
|
+
if (typeof where !== "object")
|
|
91
|
+
throw new Error(`${modelName}.load: 'where' argument: Wrong type, expected 'Array', 'Object' or 'string'`);
|
|
92
|
+
if (!where)
|
|
93
|
+
return ["", false, false];
|
|
94
|
+
if (where instanceof Array) {
|
|
95
|
+
const length = where.length;
|
|
96
|
+
if (!length)
|
|
97
|
+
throw new Error(`${modelName}.load: 'where' argument: Empty Array`);
|
|
98
|
+
if (!["AND", "NOT", "OR"].includes(where[0]))
|
|
99
|
+
throw new Error(`${modelName}.load: 'where' argument: Wrong logical operator, expected 'AND', 'OR' or 'NOT'`);
|
|
100
|
+
if (length === 1)
|
|
101
|
+
return ["", false, false];
|
|
102
|
+
if (where[0] === "NOT") {
|
|
103
|
+
if (length > 2)
|
|
104
|
+
throw new Error(`${modelName}.load: 'where' argument: 'NOT' operator is unary`);
|
|
105
|
+
const [res] = this.createWhere(modelName, attributes, where[1]);
|
|
106
|
+
return [res === "" ? "" : `NOT (${res})`, false, false];
|
|
107
|
+
}
|
|
108
|
+
const conditions = where
|
|
109
|
+
.filter((_, i) => i)
|
|
110
|
+
.map(_ => this.createWhere(modelName, attributes, _))
|
|
111
|
+
.filter(([_]) => _);
|
|
112
|
+
if (conditions.length === 1)
|
|
113
|
+
return conditions[0];
|
|
114
|
+
const isOr = where[0] === "OR";
|
|
115
|
+
return [isOr ? conditions.map(([_, , a]) => (a ? `(${_})` : _)).join(" OR ") : conditions.map(([_, o]) => (o ? `(${_})` : _)).join(" AND "), isOr, false];
|
|
116
|
+
}
|
|
117
|
+
const conditions = [];
|
|
118
|
+
for (const key in where) {
|
|
119
|
+
const field = attributes[key];
|
|
120
|
+
if (!field)
|
|
121
|
+
throw new Error(`${modelName}.load: 'where' argument: Unknown '${key}' attribute`);
|
|
122
|
+
const value = where[key];
|
|
123
|
+
if (value instanceof Array) {
|
|
124
|
+
const operator = value[0];
|
|
125
|
+
const length = value.length;
|
|
126
|
+
if (!length)
|
|
127
|
+
throw new Error(`${modelName}.load: 'where' argument: Missing arithmetic operator, expected one of: ${operators.map(_ => `'${_}'`).join(", ")}`);
|
|
128
|
+
if (!operators.includes(operator))
|
|
129
|
+
throw new Error(`${modelName}.load: 'where' argument: Wrong arithmetic operator, expected one of: ${operators.map(_ => `'${_}'`).join(", ")}`);
|
|
130
|
+
if (operator === "IS NULL") {
|
|
131
|
+
if (length !== 1)
|
|
132
|
+
throw new Error(`${modelName}.load: 'where' argument: 'IS NULL' operator is unary`);
|
|
133
|
+
conditions.push(`${field} IS NULL`);
|
|
134
|
+
}
|
|
135
|
+
else if (operator === "NOT") {
|
|
136
|
+
if (length !== 1)
|
|
137
|
+
throw new Error(`${modelName}.load: 'where' argument: 'NOT' operator is unary`);
|
|
138
|
+
conditions.push(`NOT ${field}`);
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
if (length !== 2)
|
|
142
|
+
throw new Error(`${modelName}.load: 'where' argument: '${operator}' operator is binary`);
|
|
143
|
+
if (operator === "IN") {
|
|
144
|
+
if (!(value[1] instanceof Array))
|
|
145
|
+
throw new Error(`${modelName}.load: 'where' argument: 'IN' right operand: Wrong type, expected Array`);
|
|
146
|
+
conditions.push(`${field} IN (${value[1].map(_ => this.escape(_)).join(", ")})`);
|
|
147
|
+
}
|
|
148
|
+
else
|
|
149
|
+
conditions.push(`${field} ${operator} ${this.escape(value[1])}`);
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else
|
|
153
|
+
conditions.push(`${field} = ${this.escape(value)}`);
|
|
154
|
+
}
|
|
155
|
+
return [conditions.length ? conditions.join(" AND ") : "", false, false];
|
|
156
|
+
}
|
|
157
|
+
async connect(sync) {
|
|
158
|
+
try {
|
|
159
|
+
this.checkDB();
|
|
160
|
+
this.log("Connecting...");
|
|
161
|
+
await this.db.connect();
|
|
162
|
+
this.log("Connected");
|
|
163
|
+
if (this.autoSync || sync)
|
|
164
|
+
await this.sync();
|
|
165
|
+
}
|
|
166
|
+
catch (e) {
|
|
167
|
+
this.log("Connecting: " + (e instanceof Error ? e.message : JSON.stringify(e)));
|
|
168
|
+
throw e;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
async sync() {
|
|
172
|
+
this.log("Syncing...");
|
|
173
|
+
await this.db.syncDataBase();
|
|
174
|
+
this.log("Synced");
|
|
175
|
+
}
|
|
176
|
+
async end() {
|
|
177
|
+
this.log("Closing connection...");
|
|
178
|
+
await this.db.end();
|
|
179
|
+
this.log("Connection closed");
|
|
180
|
+
}
|
|
181
|
+
async begin() {
|
|
182
|
+
return this.db.begin();
|
|
183
|
+
}
|
|
184
|
+
escape(value) {
|
|
185
|
+
return this.db.escape(value);
|
|
186
|
+
}
|
|
187
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
188
|
+
model(modelName, attributes, options, methods) {
|
|
189
|
+
this.checkDB();
|
|
190
|
+
if (typeof modelName !== "string")
|
|
191
|
+
throw new Error("Sedentary.model: 'name' argument: Wrong type, expected 'string'");
|
|
192
|
+
if (this.models[modelName])
|
|
193
|
+
throw new Error(`Sedentary.model: '${modelName}' model: Model already defined`);
|
|
194
|
+
if (!attributes)
|
|
195
|
+
attributes = {};
|
|
196
|
+
if (!(attributes instanceof Object))
|
|
197
|
+
throw new Error(`Sedentary.model: '${modelName}' model: 'attributes' argument: Wrong type, expected 'Object'`);
|
|
198
|
+
if (!options)
|
|
199
|
+
options = {};
|
|
200
|
+
if (!(options instanceof Object))
|
|
201
|
+
throw new Error(`Sedentary.model: '${modelName}' model: 'options' argument: Wrong type, expected 'Object'`);
|
|
202
|
+
for (const k in options)
|
|
203
|
+
if (!allowedOption.includes(k))
|
|
204
|
+
throw new Error(`Sedentary.model: '${modelName}' model: 'options' argument: Unknown '${k}' option`);
|
|
205
|
+
if (options.int8id && options.parent)
|
|
206
|
+
throw new Error(`Sedentary.model: '${modelName}' model: 'int8id' and 'parent' options conflict each other`);
|
|
207
|
+
if (options.int8id && options.primaryKey)
|
|
208
|
+
throw new Error(`Sedentary.model: '${modelName}' model: 'int8id' and 'primaryKey' options conflict each other`);
|
|
209
|
+
if (options.parent && options.primaryKey)
|
|
210
|
+
throw new Error(`Sedentary.model: '${modelName}' model: 'parent' and 'primaryKey' options conflict each other`);
|
|
211
|
+
let autoIncrement = true;
|
|
212
|
+
const { indexes, int8id, parent, primaryKey, sync, tableName } = { sync: this.doSync, tableName: modelName, ...options };
|
|
213
|
+
let aarray = int8id
|
|
214
|
+
? [new Attribute({ ...this.INT8(), attributeName: "id", fieldName: "id", modelName, notNull: true, tableName, unique: true })]
|
|
215
|
+
: [new Attribute({ ...this.INT(4), attributeName: "id", fieldName: "id", modelName, notNull: true, tableName, unique: true })];
|
|
216
|
+
let constraints = [{ attribute: aarray[0], constraintName: `${tableName}_id_unique`, type: "u" }];
|
|
217
|
+
const iarray = [];
|
|
218
|
+
let pk = aarray[0];
|
|
219
|
+
let attr2field = { id: "id" };
|
|
220
|
+
if (!methods)
|
|
221
|
+
methods = {};
|
|
222
|
+
if (!(methods instanceof Object))
|
|
223
|
+
throw new Error(`Sedentary.model: '${modelName}' model: 'methods' option: Wrong type, expected 'Object'`);
|
|
224
|
+
if (parent)
|
|
225
|
+
if (!parent.attributes)
|
|
226
|
+
throw new Error(`Sedentary.model: '${modelName}' model: 'parent' option: Wrong type, expected 'Model'`);
|
|
227
|
+
if (primaryKey && typeof primaryKey !== "string")
|
|
228
|
+
throw new Error(`Sedentary.model: '${modelName}' model: 'primaryKey' option: Wrong type, expected 'string'`);
|
|
229
|
+
if (primaryKey && !Object.keys(attributes).includes(primaryKey))
|
|
230
|
+
throw new Error(`Sedentary.model: '${modelName}' model: 'primaryKey' option: Attribute '${primaryKey}' does not exists`);
|
|
231
|
+
if (parent || primaryKey) {
|
|
232
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
233
|
+
attr2field = parent ? { ...parent.attr2field } : {};
|
|
234
|
+
autoIncrement = false;
|
|
235
|
+
aarray = [];
|
|
236
|
+
constraints = [];
|
|
237
|
+
}
|
|
238
|
+
for (const attributeName of Object.keys(attributes).sort()) {
|
|
239
|
+
if (reservedNames.includes(attributeName))
|
|
240
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: Reserved name`);
|
|
241
|
+
const call = (defaultValue, fieldName, notNull, unique, func, message1, message2) => {
|
|
242
|
+
if (func === this.FKEY)
|
|
243
|
+
throw new Error(`${message1} 'this.FKEY' can't be used directly`);
|
|
244
|
+
if (func !== this.DATETIME && func !== this.INT && func !== this.INT8 && func !== this.VARCHAR)
|
|
245
|
+
throw new Error(`${message1} ${message2}`);
|
|
246
|
+
return new Attribute({ attributeName, defaultValue, fieldName, modelName, notNull, tableName, unique, ...func() });
|
|
247
|
+
};
|
|
248
|
+
const attributeDefinition = attributes[attributeName];
|
|
249
|
+
let { base, defaultValue, fieldName, foreignKey, notNull, size, type, unique } = (() => {
|
|
250
|
+
const ret = (() => {
|
|
251
|
+
if (attributeDefinition instanceof Type)
|
|
252
|
+
return new Attribute({ attributeName, fieldName: attributeName, modelName, notNull: false, tableName, ...attributeDefinition });
|
|
253
|
+
if (attributeDefinition instanceof Function)
|
|
254
|
+
return call(undefined, attributeName, false, false, attributeDefinition, `Sedentary.model: '${modelName}' model: '${attributeName}' attribute:`, "Wrong type, expected 'Attribute'");
|
|
255
|
+
if (!(attributeDefinition instanceof Object))
|
|
256
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: Wrong attribute type, expected 'Attribute'`);
|
|
257
|
+
const attributeDefaults = { defaultValue: undefined, fieldName: attributeName, notNull: false, unique: false, ...attributeDefinition };
|
|
258
|
+
const { defaultValue, fieldName, notNull, unique, type } = attributeDefaults;
|
|
259
|
+
if (defaultValue === null)
|
|
260
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'defaultValue' option: Does 'null' default value really makes sense?`);
|
|
261
|
+
if (typeof fieldName !== "string")
|
|
262
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'fieldName' option: Wrong type, expected 'string'`);
|
|
263
|
+
if (typeof notNull !== "boolean")
|
|
264
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'notNull' option: Wrong type, expected 'boolean'`);
|
|
265
|
+
if (typeof unique !== "boolean")
|
|
266
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'unique' option: Wrong type, expected 'boolean'`);
|
|
267
|
+
if (type === undefined)
|
|
268
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: Missing 'type' option`);
|
|
269
|
+
if (type instanceof Type)
|
|
270
|
+
return new Attribute({ attributeName, defaultValue, fieldName, modelName, notNull, tableName, unique, ...type });
|
|
271
|
+
if (type instanceof Function)
|
|
272
|
+
return call(defaultValue, fieldName, notNull, unique, type, `Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'type' option:`, "Wrong type, expected 'Type'");
|
|
273
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'type' option: Wrong type, expected 'Type'`);
|
|
274
|
+
})();
|
|
275
|
+
const { base, defaultValue } = ret;
|
|
276
|
+
if (defaultValue !== undefined) {
|
|
277
|
+
if (base === Date && !(defaultValue instanceof Date))
|
|
278
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'defaultValue' option: Wrong type, expected 'Date'`);
|
|
279
|
+
if (base === Number && typeof defaultValue !== "number")
|
|
280
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'defaultValue' option: Wrong type, expected 'number'`);
|
|
281
|
+
if (base === String && typeof defaultValue !== "string")
|
|
282
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'defaultValue' option: Wrong type, expected 'string'`);
|
|
283
|
+
}
|
|
284
|
+
return ret;
|
|
285
|
+
})();
|
|
286
|
+
if (foreignKey) {
|
|
287
|
+
const options = foreignKey.options || {};
|
|
288
|
+
if (foreignKey.options !== undefined && !(foreignKey.options instanceof Object))
|
|
289
|
+
throw new Error(`Sedentary.FKEY: '${modelName}' model: '${attributeName}' attribute: Wrong options type, expected 'Object'`);
|
|
290
|
+
for (const k in options)
|
|
291
|
+
if (!["onDelete", "onUpdate"].includes(k))
|
|
292
|
+
throw new Error(`Sedentary.FKEY: '${modelName}' model: '${attributeName}' attribute: Unknown option '${k}'`);
|
|
293
|
+
for (const onChange of ["onDelete", "onUpdate"]) {
|
|
294
|
+
const actions = ["cascade", "no action", "restrict", "set default", "set null"];
|
|
295
|
+
let action = options[onChange];
|
|
296
|
+
if (!action)
|
|
297
|
+
action = options[onChange] = "no action";
|
|
298
|
+
if (action && !actions.includes(action))
|
|
299
|
+
throw new Error(`Sedentary.FKEY: '${modelName}' model: '${attributeName}' attribute: '${onChange}' option: Wrong value, expected ${actions.map(_ => `'${_}'`).join(" | ")}`);
|
|
300
|
+
}
|
|
301
|
+
foreignKey.options = options;
|
|
302
|
+
}
|
|
303
|
+
if (primaryKey === attributeName) {
|
|
304
|
+
notNull = true;
|
|
305
|
+
unique = true;
|
|
306
|
+
}
|
|
307
|
+
if (defaultValue)
|
|
308
|
+
notNull = true;
|
|
309
|
+
const attribute = new Attribute({ attributeName, base, defaultValue, fieldName, foreignKey, modelName, notNull, size, tableName, type, unique });
|
|
310
|
+
if (primaryKey === attributeName)
|
|
311
|
+
pk = attribute;
|
|
312
|
+
aarray.push(attribute);
|
|
313
|
+
attr2field[attributeName] = fieldName;
|
|
314
|
+
if (foreignKey)
|
|
315
|
+
constraints.push({ attribute, constraintName: `fkey_${fieldName}_${foreignKey.tableName}_${foreignKey.fieldName}`, type: "f" });
|
|
316
|
+
if (unique)
|
|
317
|
+
constraints.push({ attribute, constraintName: `${tableName}_${fieldName}_unique`, type: "u" });
|
|
318
|
+
}
|
|
319
|
+
if (indexes) {
|
|
320
|
+
const flds = attributes;
|
|
321
|
+
if (!(indexes instanceof Object))
|
|
322
|
+
throw new Error(`Sedentary.model: '${modelName}' model: 'indexes' option: Wrong type, expected 'Object'`);
|
|
323
|
+
for (const indexName in indexes) {
|
|
324
|
+
if (aarray.filter(({ fieldName, unique }) => unique && `${tableName}_${fieldName}_unique` === indexName).length !== 0)
|
|
325
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${indexName}' index: index name already inferred by the unique constraint on an attribute`);
|
|
326
|
+
const idx = indexes[indexName];
|
|
327
|
+
const checkAttribute = (attribute, l) => {
|
|
328
|
+
if (typeof attribute !== "string")
|
|
329
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${indexName}' index: #${l + 1} attribute: Wrong type, expected 'string'`);
|
|
330
|
+
if (!(attribute in flds))
|
|
331
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${indexName}' index: #${l + 1} attribute: Unknown attribute '${attribute}'`);
|
|
332
|
+
};
|
|
333
|
+
let attributes;
|
|
334
|
+
let type = "btree";
|
|
335
|
+
let unique = false;
|
|
336
|
+
if (idx instanceof Array) {
|
|
337
|
+
idx.forEach(checkAttribute);
|
|
338
|
+
attributes = idx;
|
|
339
|
+
}
|
|
340
|
+
else if (typeof idx === "string") {
|
|
341
|
+
checkAttribute(idx, 0);
|
|
342
|
+
attributes = [idx];
|
|
343
|
+
}
|
|
344
|
+
else if (idx instanceof Object) {
|
|
345
|
+
for (const k in idx)
|
|
346
|
+
if (!["attributes", "type", "unique"].includes(k))
|
|
347
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${indexName}' index: Unknown index option '${k}'`);
|
|
348
|
+
({ attributes, type, unique } = { type: "btree", unique: false, ...idx });
|
|
349
|
+
if (!attributes)
|
|
350
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${indexName}' index: Missing 'attributes' option`);
|
|
351
|
+
if (attributes instanceof Array)
|
|
352
|
+
attributes.forEach(checkAttribute);
|
|
353
|
+
else if (typeof attributes === "string") {
|
|
354
|
+
checkAttribute(attributes, 0);
|
|
355
|
+
attributes = [attributes];
|
|
356
|
+
}
|
|
357
|
+
else
|
|
358
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${indexName}' index: 'attributes' option: Wrong type, expected 'FieldNames'`);
|
|
359
|
+
if (typeof type !== "string")
|
|
360
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${indexName}' index: 'type' option: Wrong type, expected 'string'`);
|
|
361
|
+
if (!["btree", "hash"].includes(type))
|
|
362
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${indexName}' index: 'type' option: Wrong value, expected 'btree' or 'hash'`);
|
|
363
|
+
if (typeof unique !== "boolean")
|
|
364
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${indexName}' index: 'unique' option: Wrong type, expected 'boolean'`);
|
|
365
|
+
}
|
|
366
|
+
else
|
|
367
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${indexName}' index: Wrong type, expected 'Object'`);
|
|
368
|
+
iarray.push({ fields: attributes, indexName, type, unique });
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
this.models[modelName] = true;
|
|
372
|
+
const foreignKeys = aarray
|
|
373
|
+
.filter(_ => _.foreignKey)
|
|
374
|
+
.reduce((ret, curr) => {
|
|
375
|
+
ret[curr.attributeName] = true;
|
|
376
|
+
return ret;
|
|
377
|
+
}, {});
|
|
378
|
+
for (const foreignKey in foreignKeys) {
|
|
379
|
+
if (foreignKey + "Load" in attributes)
|
|
380
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${foreignKey}' attribute: '${foreignKey}Load' inferred methods conflicts with an attribute`);
|
|
381
|
+
if (foreignKey + "Load" in methods)
|
|
382
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${foreignKey}' attribute: '${foreignKey}Load' inferred methods conflicts with a method`);
|
|
383
|
+
}
|
|
384
|
+
for (const method in methods)
|
|
385
|
+
if (method in attributes)
|
|
386
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${method}' method: conflicts with an attribute`);
|
|
387
|
+
const checkParent = (parent) => {
|
|
388
|
+
if (!parent)
|
|
389
|
+
return;
|
|
390
|
+
for (const attribute in attributes) {
|
|
391
|
+
if (attribute in parent.attributes)
|
|
392
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attribute}' attribute: conflicts with an attribute of '${parent.modelName}' model`);
|
|
393
|
+
if (attribute in parent.methods)
|
|
394
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attribute}' attribute: conflicts with a method of '${parent.modelName}' model`);
|
|
395
|
+
for (const foreignKey in parent.foreignKeys)
|
|
396
|
+
if (attribute === foreignKey + "Load")
|
|
397
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attribute}' attribute: conflicts with an inferred methods of '${parent.modelName}' model`);
|
|
398
|
+
}
|
|
399
|
+
for (const foreignKey in foreignKeys) {
|
|
400
|
+
if (foreignKey + "Load" in parent.attributes)
|
|
401
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${foreignKey}' attribute: '${foreignKey}Load' inferred methods conflicts with an attribute of '${parent.modelName}' model`);
|
|
402
|
+
if (foreignKey + "Load" in parent.methods)
|
|
403
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${foreignKey}' attribute: '${foreignKey}Load' inferred methods conflicts with a method of '${parent.modelName}' model`);
|
|
404
|
+
}
|
|
405
|
+
for (const method in methods) {
|
|
406
|
+
if (method in parent.attributes)
|
|
407
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${method}' method: conflicts with an attribute of '${parent.modelName}' model`);
|
|
408
|
+
for (const foreignKey in parent.foreignKeys)
|
|
409
|
+
if (foreignKey + "Load" === method)
|
|
410
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${method}' method: conflicts with an inferred methods of '${parent.modelName}' model`);
|
|
411
|
+
}
|
|
412
|
+
checkParent(parent.parent);
|
|
413
|
+
};
|
|
414
|
+
checkParent(parent);
|
|
415
|
+
const ret = class extends (parent || EntryBase) {
|
|
416
|
+
};
|
|
417
|
+
const table = new Table({ autoIncrement, constraints, attributes: aarray, indexes: iarray, model: ret, parent, pk, sync, tableName });
|
|
418
|
+
this.db.tables.push(table);
|
|
419
|
+
const load_ = this.db.load(tableName, attr2field, pk, ret, table);
|
|
420
|
+
const load = async (where, order, tx) => {
|
|
421
|
+
if (order instanceof Transaction) {
|
|
422
|
+
tx = order;
|
|
423
|
+
order = undefined;
|
|
424
|
+
}
|
|
425
|
+
if (!this.checkOrderBy(order, attr2field, modelName))
|
|
426
|
+
throw new Error(`${modelName}.load: 'order' argument: Wrong type, expected 'string[]'`);
|
|
427
|
+
const [str] = this.createWhere(modelName, attr2field, where);
|
|
428
|
+
const ret = await load_(str, order, tx);
|
|
429
|
+
return ret;
|
|
430
|
+
};
|
|
431
|
+
Object.defineProperty(load, "name", { value: modelName + ".load" });
|
|
432
|
+
Object.defineProperty(ret, "name", { value: modelName });
|
|
433
|
+
Object.defineProperty(ret, "load", { value: load });
|
|
434
|
+
Object.defineProperty(ret, "attr2field", { value: attr2field });
|
|
435
|
+
Object.defineProperty(ret, "attributes", { value: attributes });
|
|
436
|
+
Object.defineProperty(ret, "foreignKeys", { value: foreignKeys });
|
|
437
|
+
Object.defineProperty(ret, "methods", { value: methods });
|
|
438
|
+
Object.assign(ret.prototype, methods);
|
|
439
|
+
const save = this.db.save(tableName, attr2field, pk);
|
|
440
|
+
ret.prototype.save = async function () {
|
|
441
|
+
this.preSave();
|
|
442
|
+
const ret = await save.call(this);
|
|
443
|
+
if (ret)
|
|
444
|
+
this.postSave();
|
|
445
|
+
return ret;
|
|
446
|
+
};
|
|
447
|
+
Object.defineProperty(ret.prototype.save, "name", { value: modelName + ".save" });
|
|
448
|
+
for (const attribute of aarray)
|
|
449
|
+
Object.defineProperty(ret, attribute.attributeName, { value: attribute });
|
|
450
|
+
for (const key of ["attributeName", "base", "fieldName", "modelName", "size", "tableName", "type", "unique"])
|
|
451
|
+
Object.defineProperty(ret, key, { value: pk[key] });
|
|
452
|
+
return ret;
|
|
453
|
+
}
|
|
454
|
+
}
|
|
File without changes
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Attribute, DB, EntryBase, ForeignKeyOptions, Natural, Transaction, Type } from "./db";
|
|
2
|
-
export { EntryBase, ForeignKeyActions, ForeignKeyOptions, Natural, Transaction, Type } from "./db";
|
|
2
|
+
export { Attribute, DB, EntryBase, ForeignKeyActions, ForeignKeyOptions, Index, Natural, Table, Transaction, Type } from "./db";
|
|
3
3
|
export declare type TypeDefinition<N extends Natural, E> = (() => Type<N, E>) | Type<N, E>;
|
|
4
4
|
export interface AttributeOptions<N extends Natural, E> {
|
|
5
5
|
defaultValue?: N;
|
package/package.json
CHANGED
|
@@ -39,6 +39,8 @@
|
|
|
39
39
|
"sqlite"
|
|
40
40
|
],
|
|
41
41
|
"license": "MIT",
|
|
42
|
+
"main": "./dist/cjs/index.js",
|
|
43
|
+
"module": "./dist/es/index.js",
|
|
42
44
|
"name": "sedentary",
|
|
43
45
|
"prettier": {
|
|
44
46
|
"arrowParens": "avoid",
|
|
@@ -57,15 +59,14 @@
|
|
|
57
59
|
"packagejson": "node -r ts-node/register utils.ts packagejson",
|
|
58
60
|
"test": "mocha -r ts-node/register test/*ts",
|
|
59
61
|
"travis": "node -r ts-node/register utils.ts travis",
|
|
60
|
-
"tsc": "tsc
|
|
62
|
+
"tsc": "tsc -p tsconfig.cjs.json && tsc -p tsconfig.es.json && tsc -p tsconfig.types.json",
|
|
61
63
|
"version": "node -r ts-node/register utils.ts version"
|
|
62
64
|
},
|
|
63
65
|
"tsd": {
|
|
64
66
|
"compilerOptions": {
|
|
65
67
|
"alwaysStrict": true,
|
|
66
|
-
"declaration": true,
|
|
67
68
|
"esModuleInterop": true,
|
|
68
|
-
"
|
|
69
|
+
"moduleResolution": "node",
|
|
69
70
|
"noImplicitAny": true,
|
|
70
71
|
"noImplicitReturns": true,
|
|
71
72
|
"noImplicitThis": true,
|
|
@@ -73,10 +74,9 @@
|
|
|
73
74
|
"strictBindCallApply": true,
|
|
74
75
|
"strictFunctionTypes": true,
|
|
75
76
|
"strictNullChecks": true,
|
|
76
|
-
"strictPropertyInitialization": true
|
|
77
|
-
"target": "es2017"
|
|
77
|
+
"strictPropertyInitialization": true
|
|
78
78
|
}
|
|
79
79
|
},
|
|
80
|
-
"types": "index.d.ts",
|
|
81
|
-
"version": "0.0.
|
|
80
|
+
"types": "./dist/types/index.d.ts",
|
|
81
|
+
"version": "0.0.28"
|
|
82
82
|
}
|