sedentary 0.0.23 → 0.0.26

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.
@@ -86,5 +86,23 @@ class DB {
86
86
  }
87
87
  exports.DB = DB;
88
88
  class Transaction {
89
+ constructor() {
90
+ this.entries = [];
91
+ }
92
+ addEntry(entry) {
93
+ this.entries.push(entry);
94
+ }
95
+ clean() {
96
+ const { entries } = this;
97
+ for (const entry of entries)
98
+ delete entry.tx;
99
+ this.entries = [];
100
+ }
101
+ async commit() {
102
+ this.clean();
103
+ }
104
+ async rollback() {
105
+ this.clean();
106
+ }
89
107
  }
90
108
  exports.Transaction = Transaction;
@@ -1,18 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Sedentary = exports.Type = exports.EntryBase = void 0;
3
+ exports.Sedentary = exports.Type = exports.Transaction = exports.EntryBase = void 0;
4
4
  const db_1 = require("./db");
5
5
  var db_2 = require("./db");
6
6
  Object.defineProperty(exports, "EntryBase", { enumerable: true, get: function () { return db_2.EntryBase; } });
7
+ Object.defineProperty(exports, "Transaction", { enumerable: true, get: function () { return db_2.Transaction; } });
7
8
  Object.defineProperty(exports, "Type", { enumerable: true, get: function () { return db_2.Type; } });
9
+ const operators = ["=", ">", "<", ">=", "<=", "<>", "IN", "IS NULL", "LIKE", "NOT"];
8
10
  const allowedOption = ["indexes", "int8id", "parent", "primaryKey", "sync", "tableName"];
9
11
  const reservedNames = [
10
- ...["attributeName", "attributes", "base", "class", "construct", "constructor", "defaultValue", "entry", "fieldName", "foreignKeys", "load"],
12
+ ...["attr2field", "attributeName", "attributes", "base", "class", "construct", "constructor", "defaultValue", "entry", "fieldName", "foreignKeys", "load"],
11
13
  ...["loaded", "methods", "name", "postLoad", "postSave", "preLoad", "preSave", "primaryKey", "prototype", "save", "size", "tableName", "tx", "type"]
12
14
  ];
13
15
  class Sedentary {
14
16
  constructor(options) {
15
- this.sync = true;
17
+ this.doSync = true;
16
18
  this.models = {};
17
19
  if (!options)
18
20
  options = {};
@@ -21,7 +23,7 @@ class Sedentary {
21
23
  for (const k in options)
22
24
  if (!["autoSync", "log", "sync"].includes(k))
23
25
  throw new Error(`new Sedentary: 'options' argument: Unknown '${k}' option`);
24
- const { autoSync, log, sync } = Object.assign({ autoSync: true, sync: true }, options);
26
+ const { autoSync, log, sync } = { autoSync: true, sync: true, ...options };
25
27
  if (typeof autoSync !== "boolean")
26
28
  throw new Error("new Sedentary: 'autoSync' option: Wrong type, expected 'boolean'");
27
29
  if (log !== null && log !== undefined && !(log instanceof Function))
@@ -32,7 +34,7 @@ class Sedentary {
32
34
  this.db = null;
33
35
  // eslint-disable-next-line no-console
34
36
  this.log = log ? log : log === null ? () => { } : console.log;
35
- this.sync = sync;
37
+ this.doSync = sync;
36
38
  }
37
39
  DATETIME() {
38
40
  return new db_1.Type({ base: Date, type: "DATETIME" });
@@ -60,6 +62,24 @@ class Sedentary {
60
62
  if (!this.db)
61
63
  throw new Error("Package sedentary can't be used directly. Please check: https://www.npmjs.com/package/sedentary#disclaimer");
62
64
  }
65
+ checkOrderBy(order, attributes, modelName) {
66
+ if (!order)
67
+ return true;
68
+ if (!(order instanceof Array))
69
+ return false;
70
+ const provided = {};
71
+ for (const attribute of order) {
72
+ if (typeof attribute !== "string")
73
+ return false;
74
+ const attributeName = attribute.startsWith("-") ? attribute.substring(1) : attribute;
75
+ if (!(attributeName in attributes))
76
+ throw new Error(`${modelName}.load: 'order' argument: '${attributeName}' is not an attribute name`);
77
+ if (provided[attributeName])
78
+ throw new Error(`${modelName}.load: 'order' argument: Reused '${attributeName}' attribute`);
79
+ provided[attributeName] = true;
80
+ }
81
+ return true;
82
+ }
63
83
  checkSize(size, message) {
64
84
  const str = size.toString();
65
85
  const parsed = parseInt(str, 10);
@@ -67,28 +87,103 @@ class Sedentary {
67
87
  throw new Error(message);
68
88
  return parsed;
69
89
  }
90
+ createWhere(modelName, attributes, where) {
91
+ if (typeof where === "string")
92
+ return [where, true, true];
93
+ if (typeof where !== "object")
94
+ throw new Error(`${modelName}.load: 'where' argument: Wrong type, expected 'Array', 'Object' or 'string'`);
95
+ if (!where)
96
+ return ["", false, false];
97
+ if (where instanceof Array) {
98
+ const length = where.length;
99
+ if (!length)
100
+ throw new Error(`${modelName}.load: 'where' argument: Empty Array`);
101
+ if (!["AND", "NOT", "OR"].includes(where[0]))
102
+ throw new Error(`${modelName}.load: 'where' argument: Wrong logical operator, expected 'AND', 'OR' or 'NOT'`);
103
+ if (length === 1)
104
+ return ["", false, false];
105
+ if (where[0] === "NOT") {
106
+ if (length > 2)
107
+ throw new Error(`${modelName}.load: 'where' argument: 'NOT' operator is unary`);
108
+ const [res] = this.createWhere(modelName, attributes, where[1]);
109
+ return [res === "" ? "" : `NOT (${res})`, false, false];
110
+ }
111
+ const conditions = where
112
+ .filter((_, i) => i)
113
+ .map(_ => this.createWhere(modelName, attributes, _))
114
+ .filter(([_]) => _);
115
+ if (conditions.length === 1)
116
+ return conditions[0];
117
+ const isOr = where[0] === "OR";
118
+ return [isOr ? conditions.map(([_, , a]) => (a ? `(${_})` : _)).join(" OR ") : conditions.map(([_, o]) => (o ? `(${_})` : _)).join(" AND "), isOr, false];
119
+ }
120
+ const conditions = [];
121
+ for (const key in where) {
122
+ const field = attributes[key];
123
+ if (!field)
124
+ throw new Error(`${modelName}.load: 'where' argument: Unknown '${key}' attribute`);
125
+ const value = where[key];
126
+ if (value instanceof Array) {
127
+ const operator = value[0];
128
+ const length = value.length;
129
+ if (!length)
130
+ throw new Error(`${modelName}.load: 'where' argument: Missing arithmetic operator, expected one of: ${operators.map(_ => `'${_}'`).join(", ")}`);
131
+ if (!operators.includes(operator))
132
+ throw new Error(`${modelName}.load: 'where' argument: Wrong arithmetic operator, expected one of: ${operators.map(_ => `'${_}'`).join(", ")}`);
133
+ if (operator === "IS NULL") {
134
+ if (length !== 1)
135
+ throw new Error(`${modelName}.load: 'where' argument: 'IS NULL' operator is unary`);
136
+ conditions.push(`${field} IS NULL`);
137
+ }
138
+ else if (operator === "NOT") {
139
+ if (length !== 1)
140
+ throw new Error(`${modelName}.load: 'where' argument: 'NOT' operator is unary`);
141
+ conditions.push(`NOT ${field}`);
142
+ }
143
+ else {
144
+ if (length !== 2)
145
+ throw new Error(`${modelName}.load: 'where' argument: '${operator}' operator is binary`);
146
+ if (operator === "IN") {
147
+ if (!(value[1] instanceof Array))
148
+ throw new Error(`${modelName}.load: 'where' argument: 'IN' right operand: Wrong type, expected Array`);
149
+ conditions.push(`${field} IN (${value[1].map(_ => this.escape(_)).join(", ")})`);
150
+ }
151
+ else
152
+ conditions.push(`${field} ${operator} ${this.escape(value[1])}`);
153
+ }
154
+ }
155
+ else
156
+ conditions.push(`${field} = ${this.escape(value)}`);
157
+ }
158
+ return [conditions.length ? conditions.join(" AND ") : "", false, false];
159
+ }
70
160
  async connect(sync) {
71
161
  try {
72
162
  this.checkDB();
73
163
  this.log("Connecting...");
74
164
  await this.db.connect();
75
165
  this.log("Connected");
76
- if (this.autoSync || sync) {
77
- this.log("Syncing...");
78
- await this.db.syncDataBase();
79
- this.log("Synced");
80
- }
166
+ if (this.autoSync || sync)
167
+ await this.sync();
81
168
  }
82
169
  catch (e) {
83
170
  this.log("Connecting: " + (e instanceof Error ? e.message : JSON.stringify(e)));
84
171
  throw e;
85
172
  }
86
173
  }
174
+ async sync() {
175
+ this.log("Syncing...");
176
+ await this.db.syncDataBase();
177
+ this.log("Synced");
178
+ }
87
179
  async end() {
88
180
  this.log("Closing connection...");
89
181
  await this.db.end();
90
182
  this.log("Connection closed");
91
183
  }
184
+ async begin() {
185
+ return this.db.begin();
186
+ }
92
187
  escape(value) {
93
188
  return this.db.escape(value);
94
189
  }
@@ -117,13 +212,14 @@ class Sedentary {
117
212
  if (options.parent && options.primaryKey)
118
213
  throw new Error(`Sedentary.model: '${modelName}' model: 'parent' and 'primaryKey' options conflict each other`);
119
214
  let autoIncrement = true;
120
- const { indexes, int8id, parent, primaryKey, sync, tableName } = Object.assign({ sync: this.sync, tableName: modelName }, options);
215
+ const { indexes, int8id, parent, primaryKey, sync, tableName } = { sync: this.doSync, tableName: modelName, ...options };
121
216
  let aarray = int8id
122
- ? [new db_1.Attribute(Object.assign(Object.assign({}, this.INT8()), { attributeName: "id", fieldName: "id", modelName, notNull: true, tableName, unique: true }))]
123
- : [new db_1.Attribute(Object.assign(Object.assign({}, this.INT(4)), { attributeName: "id", fieldName: "id", modelName, notNull: true, tableName, unique: true }))];
217
+ ? [new db_1.Attribute({ ...this.INT8(), attributeName: "id", fieldName: "id", modelName, notNull: true, tableName, unique: true })]
218
+ : [new db_1.Attribute({ ...this.INT(4), attributeName: "id", fieldName: "id", modelName, notNull: true, tableName, unique: true })];
124
219
  let constraints = [{ attribute: aarray[0], constraintName: `${tableName}_id_unique`, type: "u" }];
125
220
  const iarray = [];
126
- const pk = aarray[0];
221
+ let pk = aarray[0];
222
+ let attr2field = { id: "id" };
127
223
  if (!methods)
128
224
  methods = {};
129
225
  if (!(methods instanceof Object))
@@ -136,6 +232,8 @@ class Sedentary {
136
232
  if (primaryKey && !Object.keys(attributes).includes(primaryKey))
137
233
  throw new Error(`Sedentary.model: '${modelName}' model: 'primaryKey' option: Attribute '${primaryKey}' does not exists`);
138
234
  if (parent || primaryKey) {
235
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
236
+ attr2field = parent ? { ...parent.attr2field } : {};
139
237
  autoIncrement = false;
140
238
  aarray = [];
141
239
  constraints = [];
@@ -148,18 +246,18 @@ class Sedentary {
148
246
  throw new Error(`${message1} 'this.FKEY' can't be used directly`);
149
247
  if (func !== this.DATETIME && func !== this.INT && func !== this.INT8 && func !== this.VARCHAR)
150
248
  throw new Error(`${message1} ${message2}`);
151
- return new db_1.Attribute(Object.assign({ attributeName, defaultValue, fieldName, modelName, notNull, tableName, unique }, func()));
249
+ return new db_1.Attribute({ attributeName, defaultValue, fieldName, modelName, notNull, tableName, unique, ...func() });
152
250
  };
153
251
  const attributeDefinition = attributes[attributeName];
154
252
  let { base, defaultValue, fieldName, foreignKey, notNull, size, type, unique } = (() => {
155
253
  const ret = (() => {
156
254
  if (attributeDefinition instanceof db_1.Type)
157
- return new db_1.Attribute(Object.assign({ attributeName, fieldName: attributeName, modelName, notNull: false, tableName }, attributeDefinition));
255
+ return new db_1.Attribute({ attributeName, fieldName: attributeName, modelName, notNull: false, tableName, ...attributeDefinition });
158
256
  if (attributeDefinition instanceof Function)
159
257
  return call(undefined, attributeName, false, false, attributeDefinition, `Sedentary.model: '${modelName}' model: '${attributeName}' attribute:`, "Wrong type, expected 'Attribute'");
160
258
  if (!(attributeDefinition instanceof Object))
161
259
  throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: Wrong attribute type, expected 'Attribute'`);
162
- const attributeDefaults = Object.assign({ defaultValue: undefined, fieldName: attributeName, notNull: false, unique: false }, attributeDefinition);
260
+ const attributeDefaults = { defaultValue: undefined, fieldName: attributeName, notNull: false, unique: false, ...attributeDefinition };
163
261
  const { defaultValue, fieldName, notNull, unique, type } = attributeDefaults;
164
262
  if (defaultValue === null)
165
263
  throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'defaultValue' option: Does 'null' default value really makes sense?`);
@@ -172,7 +270,7 @@ class Sedentary {
172
270
  if (type === undefined)
173
271
  throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: Missing 'type' option`);
174
272
  if (type instanceof db_1.Type)
175
- return new db_1.Attribute(Object.assign({ attributeName, defaultValue, fieldName, modelName, notNull, tableName, unique }, type));
273
+ return new db_1.Attribute({ attributeName, defaultValue, fieldName, modelName, notNull, tableName, unique, ...type });
176
274
  if (type instanceof Function)
177
275
  return call(defaultValue, fieldName, notNull, unique, type, `Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'type' option:`, "Wrong type, expected 'Type'");
178
276
  throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'type' option: Wrong type, expected 'Type'`);
@@ -212,7 +310,10 @@ class Sedentary {
212
310
  if (defaultValue)
213
311
  notNull = true;
214
312
  const attribute = new db_1.Attribute({ attributeName, base, defaultValue, fieldName, foreignKey, modelName, notNull, size, tableName, type, unique });
313
+ if (primaryKey === attributeName)
314
+ pk = attribute;
215
315
  aarray.push(attribute);
316
+ attr2field[attributeName] = fieldName;
216
317
  if (foreignKey)
217
318
  constraints.push({ attribute, constraintName: `fkey_${fieldName}_${foreignKey.tableName}_${foreignKey.fieldName}`, type: "f" });
218
319
  if (unique)
@@ -247,7 +348,7 @@ class Sedentary {
247
348
  for (const k in idx)
248
349
  if (!["attributes", "type", "unique"].includes(k))
249
350
  throw new Error(`Sedentary.model: '${modelName}' model: '${indexName}' index: Unknown index option '${k}'`);
250
- ({ attributes, type, unique } = Object.assign({ type: "btree", unique: false }, idx));
351
+ ({ attributes, type, unique } = { type: "btree", unique: false, ...idx });
251
352
  if (!attributes)
252
353
  throw new Error(`Sedentary.model: '${modelName}' model: '${indexName}' index: Missing 'attributes' option`);
253
354
  if (attributes instanceof Array)
@@ -270,7 +371,6 @@ class Sedentary {
270
371
  iarray.push({ fields: attributes, indexName, type, unique });
271
372
  }
272
373
  }
273
- this.db.tables.push(new db_1.Table({ autoIncrement, constraints, attributes: aarray, indexes: iarray, parent, sync, tableName }));
274
374
  this.models[modelName] = true;
275
375
  const foreignKeys = aarray
276
376
  .filter(_ => _.foreignKey)
@@ -311,27 +411,42 @@ class Sedentary {
311
411
  for (const foreignKey in parent.foreignKeys)
312
412
  if (foreignKey + "Load" === method)
313
413
  throw new Error(`Sedentary.model: '${modelName}' model: '${method}' method: conflicts with an inferred methods of '${parent.modelName}' model`);
314
- if (method in parent.methods)
315
- throw new Error(`Sedentary.model: '${modelName}' model: '${method}' method: conflicts with a method of '${parent.modelName}' model`);
316
414
  }
317
415
  checkParent(parent.parent);
318
416
  };
319
417
  checkParent(parent);
320
418
  const ret = class extends (parent || db_1.EntryBase) {
321
419
  };
322
- const load = (boh) => new Promise((resolve, reject) => setTimeout(() => {
323
- if (boh)
324
- return resolve([new ret()]);
325
- reject(new Error("boh"));
326
- }, 10));
420
+ const table = new db_1.Table({ autoIncrement, constraints, attributes: aarray, indexes: iarray, model: ret, parent, pk, sync, tableName });
421
+ this.db.tables.push(table);
422
+ const load_ = this.db.load(tableName, attr2field, pk, ret, table);
423
+ const load = async (where, order, tx) => {
424
+ if (order instanceof db_1.Transaction) {
425
+ tx = order;
426
+ order = undefined;
427
+ }
428
+ if (!this.checkOrderBy(order, attr2field, modelName))
429
+ throw new Error(`${modelName}.load: 'order' argument: Wrong type, expected 'string[]'`);
430
+ const [str] = this.createWhere(modelName, attr2field, where);
431
+ const ret = await load_(str, order, tx);
432
+ return ret;
433
+ };
327
434
  Object.defineProperty(load, "name", { value: modelName + ".load" });
328
435
  Object.defineProperty(ret, "name", { value: modelName });
329
436
  Object.defineProperty(ret, "load", { value: load });
437
+ Object.defineProperty(ret, "attr2field", { value: attr2field });
330
438
  Object.defineProperty(ret, "attributes", { value: attributes });
331
439
  Object.defineProperty(ret, "foreignKeys", { value: foreignKeys });
332
440
  Object.defineProperty(ret, "methods", { value: methods });
333
441
  Object.assign(ret.prototype, methods);
334
- ret.prototype.save = this.db.save(tableName, aarray);
442
+ const save = this.db.save(tableName, attr2field, pk);
443
+ ret.prototype.save = async function () {
444
+ this.preSave();
445
+ const ret = await save.call(this);
446
+ if (ret)
447
+ this.postSave();
448
+ return ret;
449
+ };
335
450
  Object.defineProperty(ret.prototype.save, "name", { value: modelName + ".save" });
336
451
  for (const attribute of aarray)
337
452
  Object.defineProperty(ret, attribute.attributeName, { value: attribute });
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
+ }
@@ -0,0 +1,454 @@
1
+ import { Attribute, EntryBase, Table, Transaction, Type } from "./db";
2
+ export { EntryBase, 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
+ }
@@ -1,6 +1,6 @@
1
- export declare type Natural = Date | Record<string, unknown> | boolean | number | string;
1
+ export declare type Natural = Date | Record<string, unknown> | boolean | number | string | null;
2
2
  export declare class EntryBase {
3
- constructor(from?: Partial<EntryBase> | "load");
3
+ constructor(from?: Partial<EntryBase>);
4
4
  construct(): void;
5
5
  postLoad(): void;
6
6
  postSave(): void;
@@ -57,7 +57,11 @@ interface ITable {
57
57
  autoIncrement: boolean;
58
58
  constraints: Constraint[];
59
59
  indexes: Index[];
60
+ model: {
61
+ load: (where: any, order?: string[], tx?: Transaction) => Promise<EntryBase[]>;
62
+ };
60
63
  parent?: Attribute<Natural, unknown>;
64
+ pk: Attribute<Natural, unknown>;
61
65
  sync: boolean;
62
66
  tableName: string;
63
67
  }
@@ -67,7 +71,7 @@ export declare class Table extends Table_base {
67
71
  oid?: number;
68
72
  findField(name: string): Attribute<Natural, unknown>;
69
73
  }
70
- export declare abstract class DB {
74
+ export declare abstract class DB<T extends Transaction> {
71
75
  tables: Table[];
72
76
  protected log: (message: string) => void;
73
77
  protected sync: boolean;
@@ -78,8 +82,10 @@ export declare abstract class DB {
78
82
  protected indexesEq(a: Index, b: Index): boolean;
79
83
  syncDataBase(): Promise<void>;
80
84
  protected syncLog(message: string): void;
85
+ abstract begin(): Promise<T>;
81
86
  abstract escape(value: Natural): string;
82
- abstract save(tableName: string, attributes: Attribute<Natural, unknown>[]): (this: Record<string, Natural>) => Promise<boolean>;
87
+ abstract load(tableName: string, attributes: Record<string, string>, pk: Attribute<Natural, unknown>, model: new () => EntryBase, table: Table): (where: string, order?: string[], tx?: Transaction) => Promise<EntryBase[]>;
88
+ abstract save(tableName: string, attributes: Record<string, string>, pk: Attribute<Natural, unknown>): (this: EntryBase & Record<string, Natural>) => Promise<boolean>;
83
89
  abstract dropConstraints(table: Table): Promise<number[]>;
84
90
  abstract dropFields(table: Table): Promise<void>;
85
91
  abstract dropIndexes(table: Table, constraintIndexes: number[]): Promise<void>;
@@ -90,5 +96,10 @@ export declare abstract class DB {
90
96
  abstract syncTable(table: Table): Promise<void>;
91
97
  }
92
98
  export declare class Transaction {
99
+ private entries;
100
+ addEntry(entry: EntryBase): void;
101
+ clean(): void;
102
+ commit(): Promise<void>;
103
+ rollback(): Promise<void>;
93
104
  }
94
105
  export {};
@@ -1,5 +1,5 @@
1
- import { Attribute, DB, EntryBase, ForeignKeyOptions, Natural, Type } from "./db";
2
- export { EntryBase, ForeignKeyActions, ForeignKeyOptions, Natural, Type } from "./db";
1
+ import { Attribute, DB, EntryBase, ForeignKeyOptions, Natural, Transaction, Type } from "./db";
2
+ export { EntryBase, ForeignKeyActions, ForeignKeyOptions, Natural, 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;
@@ -16,7 +16,7 @@ declare type ForeignKeysAttributes<T, k> = T extends AttributeDefinition<Natural
16
16
  declare type ForeignKeys<A extends AttributesDefinition> = {
17
17
  [a in keyof A]?: ForeignKeysAttributes<A[a], a>;
18
18
  }[keyof A];
19
- declare type Native__<T> = T extends Type<infer N, unknown> ? N : never;
19
+ declare type Native__<T> = T extends Type<infer N, unknown> ? N | null : never;
20
20
  declare type Native_<T> = T extends () => Type<infer N, infer E> ? Native__<Type<N, E>> : Native__<T>;
21
21
  declare type Native<T> = T extends AttributeOptions<infer N, infer E> ? Native__<Type<N, E>> : Native_<T>;
22
22
  export declare type IndexAttributes = string[] | string;
@@ -39,11 +39,12 @@ export interface ModelOptions extends BaseModelOptions {
39
39
  parent?: Attribute<Natural, EntryBase>;
40
40
  primaryKey?: string;
41
41
  }
42
- declare type ConditionAttribute<N extends Natural> = N | [">" | "<" | ">=" | "<=" | "<>", N] | ["IN", ...N[]] | ["IS NULL"] | ["LIKE", string];
42
+ declare type ConditionAttribute<N extends Natural> = N | ["=" | ">" | "<" | ">=" | "<=" | "<>", N] | ["IN", N[]] | ["IS NULL"] | ["LIKE", string] | ["NOT"];
43
43
  declare type ConditionBase<A extends AttributesDefinition> = string | {
44
44
  [a in keyof A]?: ConditionAttribute<Native<A[a]>>;
45
45
  };
46
46
  declare type Condition<A extends AttributesDefinition> = ConditionBase<A> | ["NOT", Condition<A>] | ["AND", ...Condition<A>[]] | ["OR", ...Condition<A>[]];
47
+ declare type Order<A extends AttributesDefinition> = (keyof A | `-${string & keyof A}`)[];
47
48
  declare type UnionToIntersection<U> = (U extends unknown ? (k: U) => void : never) extends (k: infer I) => void ? I : never;
48
49
  declare type IsUnion<T> = [T] extends [UnionToIntersection<T>] ? false : true;
49
50
  declare type BaseKeyType<B extends boolean> = IsUnion<B> extends true ? number : B extends true ? string : number;
@@ -61,16 +62,19 @@ declare type ModelAttributesIf<A extends AttributesDefinition, T> = keyof A exte
61
62
  declare type ModelAttributes<A extends AttributesDefinition, B extends boolean, K extends string, P extends ModelStd> = K extends keyof A ? A : ModelAttributesIf<A, P extends new () => EntryBase ? P["attributes"] : {
62
63
  id: Type<BaseKeyType<B>, unknown>;
63
64
  }>;
64
- declare type ModelBase<N extends Natural, A extends AttributesDefinition, EA extends Record<string, Natural | undefined>, EM extends EntryBase, E extends EntryBase> = (new (from?: EA) => E) & Attribute<N, E> & {
65
+ interface ModelLoad<A extends AttributesDefinition, E extends EntryBase> {
65
66
  attributes: A;
67
+ load(where: Condition<A>, order?: Order<A>, tx?: Transaction): Promise<E[]>;
68
+ load(where: Condition<A>, tx: Transaction): Promise<E[]>;
69
+ }
70
+ declare type ModelBase<N extends Natural, A extends AttributesDefinition, EA extends Record<string, Natural | undefined>, EM extends EntryBase, E extends EntryBase> = (new (from?: EA) => E) & Attribute<N, E> & {
66
71
  foreignKeys: Record<string, boolean>;
67
72
  methods: EM;
68
73
  parent?: ModelStd;
69
74
  tableName: string;
70
- load: (where?: Condition<A>) => Promise<E[]>;
71
75
  } & {
72
76
  [a in keyof A]: Attribute<Native<A[a]>, E>;
73
- };
77
+ } & ModelLoad<A, E>;
74
78
  declare type Model<N extends Natural, A extends AttributesDefinition, EM extends EntryBase> = ModelBase<N, A, EntryBaseAttributes<A>, EM, EntryBaseAttributes<A> & EM>;
75
79
  declare type ModelStd = Attribute<Natural, EntryBase> & {
76
80
  attributes: AttributesDefinition;
@@ -79,19 +83,24 @@ declare type ModelStd = Attribute<Natural, EntryBase> & {
79
83
  parent?: ModelStd;
80
84
  };
81
85
  export declare type Entry<M> = M extends new () => infer E ? E : never;
82
- export declare type Where<M> = M extends {
83
- load: (where: infer T) => void;
86
+ export declare type OrderBy<M> = M extends {
87
+ load(where: unknown, order?: infer T, tx?: Transaction): void;
88
+ load(where: unknown, tx?: Transaction): void;
84
89
  } ? Exclude<T, undefined> : never;
90
+ export declare type Where<M> = M extends {
91
+ load(where: infer T, order?: unknown, tx?: Transaction): void;
92
+ load(where: unknown, tx?: Transaction): void;
93
+ } ? T : never;
85
94
  export interface SedentaryOptions {
86
95
  autoSync?: boolean;
87
96
  log?: ((message: string) => void) | null;
88
97
  sync?: boolean;
89
98
  }
90
- export declare class Sedentary {
99
+ export declare class Sedentary<D extends DB<T>, T extends Transaction> {
91
100
  protected autoSync: boolean;
92
- protected db: DB;
101
+ protected db: D;
102
+ protected doSync: boolean;
93
103
  protected log: (...data: unknown[]) => void;
94
- protected sync: boolean;
95
104
  private models;
96
105
  constructor(options?: SedentaryOptions);
97
106
  DATETIME(): Type<Date, unknown>;
@@ -99,10 +108,14 @@ export declare class Sedentary {
99
108
  INT(size?: number): Type<number, unknown>;
100
109
  INT8(): Type<string, unknown>;
101
110
  VARCHAR(size?: number): Type<string, unknown>;
102
- checkDB(): void;
103
- checkSize(size: number, message: string): number;
111
+ private checkDB;
112
+ private checkOrderBy;
113
+ private checkSize;
114
+ private createWhere;
104
115
  connect(sync?: boolean): Promise<void>;
116
+ sync(): Promise<void>;
105
117
  end(): Promise<void>;
118
+ begin(): Promise<T>;
106
119
  escape(value: Natural): string;
107
120
  model<A extends AttributesDefinition, B extends boolean, K extends string, P extends ModelStd, EM extends EntryMethods<A, P>>(modelName: string, attributes: A, options?: BaseModelOptions & {
108
121
  int8id?: B;
package/package.json CHANGED
@@ -8,18 +8,18 @@
8
8
  "dependencies": {},
9
9
  "description": "The ORM which never needs to migrate",
10
10
  "devDependencies": {
11
- "@types/mocha": "9.0.0",
12
- "@types/node": "17.0.5",
11
+ "@types/mocha": "9.1.0",
12
+ "@types/node": "17.0.19",
13
13
  "@types/yamljs": "0.2.31",
14
- "@typescript-eslint/eslint-plugin": "5.8.1",
15
- "@typescript-eslint/parser": "5.8.1",
16
- "eslint": "8.5.0",
17
- "mocha": "9.1.3",
14
+ "@typescript-eslint/eslint-plugin": "5.12.1",
15
+ "@typescript-eslint/parser": "5.12.1",
16
+ "eslint": "8.9.0",
17
+ "mocha": "9.2.1",
18
18
  "nyc": "15.1.0",
19
19
  "prettier": "2.5.1",
20
- "ts-node": "10.4.0",
21
- "tsd": "0.19.0",
22
- "typescript": "4.5.4",
20
+ "ts-node": "10.5.0",
21
+ "tsd": "0.19.1",
22
+ "typescript": "4.5.5",
23
23
  "yamljs": "0.3.0"
24
24
  },
25
25
  "engines": {
@@ -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,13 @@
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 --declaration",
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
- "module": "commonjs",
69
69
  "noImplicitAny": true,
70
70
  "noImplicitReturns": true,
71
71
  "noImplicitThis": true,
@@ -73,10 +73,9 @@
73
73
  "strictBindCallApply": true,
74
74
  "strictFunctionTypes": true,
75
75
  "strictNullChecks": true,
76
- "strictPropertyInitialization": true,
77
- "target": "es2017"
76
+ "strictPropertyInitialization": true
78
77
  }
79
78
  },
80
- "types": "index.d.ts",
81
- "version": "0.0.23"
79
+ "types": "./dist/types/index.d.ts",
80
+ "version": "0.0.26"
82
81
  }