sedentary 0.0.54 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +47 -28
- package/dist/cjs/db.js +19 -14
- package/dist/cjs/index.js +141 -89
- package/dist/cjs/package.json +1 -0
- package/dist/es/db.js +16 -11
- package/dist/es/index.js +140 -89
- package/dist/types/db.d.ts +27 -23
- package/dist/types/index.d.ts +55 -37
- package/package.json +8 -31
package/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# sedentary
|
|
2
2
|
|
|
3
3
|
[![Build Status][travis-badge]][travis-url]
|
|
4
|
-
[![Code
|
|
5
|
-
[![Test Coverage][cover-badge]][
|
|
4
|
+
[![Code Quality][qlty-badge]][qlty-url]
|
|
5
|
+
[![Test Coverage][cover-badge]][qlty-url]
|
|
6
6
|
|
|
7
7
|
[![NPM version][npm-badge]][npm-url]
|
|
8
8
|
[![NPM downloads][npm-downloads-badge]][npm-url]
|
|
@@ -13,27 +13,27 @@
|
|
|
13
13
|
[![Dependents][deps-badge]][npm-url]
|
|
14
14
|
[![Donate][donate-badge]][donate-url]
|
|
15
15
|
|
|
16
|
-
[
|
|
17
|
-
[
|
|
18
|
-
[
|
|
19
|
-
[deps-badge]: https://
|
|
16
|
+
[cover-badge]: https://qlty.sh/gh/iccicci/projects/sedentary/coverage.svg
|
|
17
|
+
[qlty-badge]: https://qlty.sh/gh/iccicci/projects/sedentary/maintainability.svg
|
|
18
|
+
[qlty-url]: https://qlty.sh/gh/iccicci/projects/sedentary
|
|
19
|
+
[deps-badge]: https://img.shields.io/librariesio/dependents/npm/sedentary?logo=npm
|
|
20
20
|
[doc-badge]: https://readthedocs.org/projects/sedentary/badge/?version=latest
|
|
21
21
|
[doc-url]: https://sedentary.readthedocs.io/
|
|
22
|
-
[donate-badge]: https://
|
|
22
|
+
[donate-badge]: https://img.shields.io/static/v1?label=donate&message=bitcoin&color=blue&logo=bitcoin
|
|
23
23
|
[donate-url]: https://blockchain.info/address/1Md9WFAHrXTb3yPBwQWmUfv2RmzrtbHioB
|
|
24
24
|
[github-url]: https://github.com/iccicci/sedentary
|
|
25
|
-
[npm-downloads-badge]: https://
|
|
26
|
-
[npm-badge]: https://
|
|
25
|
+
[npm-downloads-badge]: https://img.shields.io/npm/dw/sedentary?logo=npm
|
|
26
|
+
[npm-badge]: https://img.shields.io/npm/v/sedentary?color=green&logo=npm
|
|
27
27
|
[npm-url]: https://www.npmjs.com/package/sedentary
|
|
28
|
-
[stars-badge]: https://
|
|
29
|
-
[travis-badge]: https://
|
|
28
|
+
[stars-badge]: https://img.shields.io/github/stars/iccicci/sedentary?logo=github&style=flat&color=green
|
|
29
|
+
[travis-badge]: https://img.shields.io/travis/com/iccicci/sedentary?logo=travis
|
|
30
30
|
[travis-url]: https://app.travis-ci.com/github/iccicci/sedentary
|
|
31
|
-
[types-badge]: https://
|
|
32
|
-
|
|
33
|
-
# under development
|
|
31
|
+
[types-badge]: https://img.shields.io/static/v1?label=types&message=included&color=green&logo=typescript
|
|
34
32
|
|
|
35
33
|
# Description
|
|
36
34
|
|
|
35
|
+
This package contains the base logic and types for the Sedentary ORM. **It must not be used directly** because it does not implement any DB engine; use a dedicated extension such as [sedentary-pg](https://www.npmjs.com/package/sedentary-pg) instead.
|
|
36
|
+
|
|
37
37
|
An ORM which automatically syncs the DB schema on models change, no migrations between versions are required.
|
|
38
38
|
|
|
39
39
|
This package is designed **to make easy the process of applying changes to the database after model definition
|
|
@@ -46,19 +46,19 @@ changes to the models (or the database schema). This package tries to solve thes
|
|
|
46
46
|
# Usage
|
|
47
47
|
|
|
48
48
|
```javascript
|
|
49
|
-
import {
|
|
49
|
+
import { SedentaryPG } from "sedentary-pg";
|
|
50
50
|
|
|
51
|
-
const db = new
|
|
51
|
+
const db = new SedentaryPG({ database: "db", user: "user", password: "pass" });
|
|
52
52
|
|
|
53
|
-
|
|
54
|
-
num: db.
|
|
55
|
-
str: db.VarChar(30)
|
|
53
|
+
const Items = db.model("Item", {
|
|
54
|
+
num: db.Int(),
|
|
55
|
+
str: db.VarChar({ size: 30 })
|
|
56
56
|
});
|
|
57
57
|
|
|
58
58
|
(async function () {
|
|
59
59
|
await db.connect();
|
|
60
60
|
|
|
61
|
-
const item = Items
|
|
61
|
+
const item = new Items();
|
|
62
62
|
|
|
63
63
|
item.num = 0;
|
|
64
64
|
item.str = "0";
|
|
@@ -71,6 +71,22 @@ class Items extends db.model("Item", {
|
|
|
71
71
|
})();
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
With TypeScript the instance can be typed using `Entry<typeof Model>`:
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
import { Entry, SedentaryPG } from "sedentary-pg";
|
|
78
|
+
|
|
79
|
+
const db = new SedentaryPG({ database: "db", user: "user", password: "pass" });
|
|
80
|
+
|
|
81
|
+
const Items = db.model("Item", { num: db.Int(), str: db.VarChar({ size: 30 }) });
|
|
82
|
+
type Item = Entry<typeof Items>;
|
|
83
|
+
|
|
84
|
+
const item: Item = new Items();
|
|
85
|
+
|
|
86
|
+
item.num = 0;
|
|
87
|
+
item.str = "0";
|
|
88
|
+
```
|
|
89
|
+
|
|
74
90
|
# Installation
|
|
75
91
|
|
|
76
92
|
With [npm](https://www.npmjs.com/package/sedentary):
|
|
@@ -85,9 +101,9 @@ $ npm install --save sedentary
|
|
|
85
101
|
|
|
86
102
|
A _DB engine dedicated extension_ must be used:
|
|
87
103
|
|
|
88
|
-
- MySQL: planned
|
|
89
|
-
- PostgreSQL: [sedentary-pg](https://
|
|
90
|
-
- SQLite: planned
|
|
104
|
+
- MySQL: in todo, not yet planned
|
|
105
|
+
- PostgreSQL: [sedentary-pg](https://www.npmjs.com/package/sedentary-pg)
|
|
106
|
+
- SQLite: in todo, not yet planned
|
|
91
107
|
|
|
92
108
|
# Documentation
|
|
93
109
|
|
|
@@ -97,13 +113,13 @@ The full documentation is on [sedentary.readthedocs.io](https://sedentary.readth
|
|
|
97
113
|
|
|
98
114
|
Requires:
|
|
99
115
|
|
|
100
|
-
- Node.js: **
|
|
101
|
-
- TypeScript: **
|
|
116
|
+
- Node.js: **v20**
|
|
117
|
+
- TypeScript: **v5.7** (or none if used in a JavaScript project).
|
|
102
118
|
|
|
103
119
|
The package is tested under [all Node.js versions](https://app.travis-ci.com/github/iccicci/sedentary)
|
|
104
120
|
currently supported accordingly to [Node.js Release](https://github.com/nodejs/Release#readme).
|
|
105
121
|
|
|
106
|
-
To work with the package under Windows,
|
|
122
|
+
To work with the package under Windows, `bash.exe` can be configured as the _script-shell_.
|
|
107
123
|
|
|
108
124
|
```
|
|
109
125
|
> npm config set script-shell bash.exe
|
|
@@ -115,9 +131,12 @@ To work with the package under Windows, be sure to configure `bash.exe` as your
|
|
|
115
131
|
|
|
116
132
|
# Bugs
|
|
117
133
|
|
|
118
|
-
|
|
134
|
+
Bugs and inconsistencies can be reported [@github](https://github.com/iccicci/sedentary/issues).
|
|
119
135
|
|
|
120
136
|
# Donating
|
|
121
137
|
|
|
122
|
-
|
|
138
|
+
Satoshis can be donated to this bitcoin address if the package is found useful:
|
|
139
|
+
|
|
140
|
+
<!-- cSpell: disable -->
|
|
141
|
+
|
|
123
142
|
**1Md9WFAHrXTb3yPBwQWmUfv2RmzrtbHioB**
|
package/dist/cjs/db.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.Transaction = exports.DB = exports.Table = exports.Attribute = exports.Type = exports.EntryBase = exports.transaction = exports.size = exports.loaded = exports.base = exports.actions = void 0;
|
|
4
|
+
exports.deepCopy = deepCopy;
|
|
5
|
+
exports.deepDiff = deepDiff;
|
|
4
6
|
exports.actions = Symbol("actions");
|
|
5
7
|
exports.base = Symbol("base");
|
|
6
8
|
exports.loaded = Symbol("loaded");
|
|
@@ -29,21 +31,24 @@ class EntryBase {
|
|
|
29
31
|
preLoad() { }
|
|
30
32
|
preRemove() { }
|
|
31
33
|
preSave() { }
|
|
32
|
-
|
|
33
|
-
return false;
|
|
34
|
+
remove() {
|
|
35
|
+
return Promise.resolve(false);
|
|
34
36
|
}
|
|
35
|
-
|
|
36
|
-
return false;
|
|
37
|
+
save() {
|
|
38
|
+
return Promise.resolve(false);
|
|
37
39
|
}
|
|
38
40
|
}
|
|
39
41
|
exports.EntryBase = EntryBase;
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
40
43
|
class Type {
|
|
41
44
|
constructor(from) {
|
|
42
45
|
Object.assign(this, from);
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
exports.Type = Type;
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-declaration-merging
|
|
46
50
|
class Attribute extends Type {
|
|
51
|
+
// eslint-disable-next-line @typescript-eslint/no-useless-constructor
|
|
47
52
|
constructor(from) {
|
|
48
53
|
super(from);
|
|
49
54
|
}
|
|
@@ -58,10 +63,10 @@ function autoImplement() {
|
|
|
58
63
|
}
|
|
59
64
|
class Table extends autoImplement() {
|
|
60
65
|
findAttribute(name) {
|
|
61
|
-
return this.attributes.
|
|
66
|
+
return this.attributes.find(_ => _.attributeName === name);
|
|
62
67
|
}
|
|
63
68
|
findField(name) {
|
|
64
|
-
return this.attributes.
|
|
69
|
+
return this.attributes.find(_ => _.fieldName === name);
|
|
65
70
|
}
|
|
66
71
|
}
|
|
67
72
|
exports.Table = Table;
|
|
@@ -72,12 +77,12 @@ class DB {
|
|
|
72
77
|
this.log = log;
|
|
73
78
|
}
|
|
74
79
|
findTable(name) {
|
|
75
|
-
return this.tables.
|
|
80
|
+
return this.tables.find(_ => _.tableName === name);
|
|
76
81
|
}
|
|
77
82
|
indexesEq(a, b) {
|
|
78
83
|
if (a.fields.length !== b.fields.length)
|
|
79
84
|
return false;
|
|
80
|
-
for (
|
|
85
|
+
for (let i = 0; i < a.fields.length; ++i)
|
|
81
86
|
if (a.fields[i] !== b.fields[i])
|
|
82
87
|
return false;
|
|
83
88
|
if (a.type !== b.type)
|
|
@@ -100,7 +105,7 @@ class DB {
|
|
|
100
105
|
}
|
|
101
106
|
}
|
|
102
107
|
syncLog(message) {
|
|
103
|
-
this.log(this.sync ? message :
|
|
108
|
+
this.log(this.sync ? message : `NOT SYNCING: ${message}`);
|
|
104
109
|
}
|
|
105
110
|
}
|
|
106
111
|
exports.DB = DB;
|
|
@@ -121,12 +126,13 @@ class Transaction {
|
|
|
121
126
|
}
|
|
122
127
|
this.entries = [];
|
|
123
128
|
}
|
|
124
|
-
|
|
129
|
+
commit() {
|
|
125
130
|
const { entries } = this;
|
|
126
131
|
for (const entry of entries)
|
|
127
132
|
if (entry[exports.actions])
|
|
128
133
|
entry.postCommit(entry[exports.actions]);
|
|
129
134
|
this.clean();
|
|
135
|
+
return Promise.resolve();
|
|
130
136
|
}
|
|
131
137
|
preCommit() {
|
|
132
138
|
const { entries } = this;
|
|
@@ -134,8 +140,9 @@ class Transaction {
|
|
|
134
140
|
if (entry[exports.actions])
|
|
135
141
|
entry.preCommit(entry[exports.actions]);
|
|
136
142
|
}
|
|
137
|
-
|
|
143
|
+
rollback() {
|
|
138
144
|
this.clean();
|
|
145
|
+
return Promise.resolve();
|
|
139
146
|
}
|
|
140
147
|
}
|
|
141
148
|
exports.Transaction = Transaction;
|
|
@@ -149,7 +156,6 @@ function deepCopy(o) {
|
|
|
149
156
|
ret[k] = deepCopy(v);
|
|
150
157
|
return ret;
|
|
151
158
|
}
|
|
152
|
-
exports.deepCopy = deepCopy;
|
|
153
159
|
function deepDiff(a, b) {
|
|
154
160
|
if (typeof a !== "object")
|
|
155
161
|
return a !== b;
|
|
@@ -176,4 +182,3 @@ function deepDiff(a, b) {
|
|
|
176
182
|
return true;
|
|
177
183
|
return false;
|
|
178
184
|
}
|
|
179
|
-
exports.deepDiff = deepDiff;
|
package/dist/cjs/index.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.Sedentary = exports.Type = exports.transaction = exports.Transaction = exports.Table = exports.size = exports.loaded = exports.EntryBase = exports.deepDiff = exports.deepCopy = exports.DB = exports.base = exports.Attribute = void 0;
|
|
4
|
+
exports.toSqlName = toSqlName;
|
|
4
5
|
const db_1 = require("./db");
|
|
5
6
|
var db_2 = require("./db");
|
|
6
7
|
Object.defineProperty(exports, "Attribute", { enumerable: true, get: function () { return db_2.Attribute; } });
|
|
@@ -20,9 +21,53 @@ const methods = Symbol("methods");
|
|
|
20
21
|
const operators = ["=", ">", "<", ">=", "<=", "<>", "IN", "IS NULL", "LIKE", "NOT"];
|
|
21
22
|
const allowedOption = ["indexes", "int8id", "parent", "primaryKey", "sync", "tableName"];
|
|
22
23
|
const reservedNames = [
|
|
23
|
-
|
|
24
|
-
|
|
24
|
+
"attr2field",
|
|
25
|
+
"attributeName",
|
|
26
|
+
"cancel",
|
|
27
|
+
"class",
|
|
28
|
+
"construct",
|
|
29
|
+
"constructor",
|
|
30
|
+
"defaultValue",
|
|
31
|
+
"fieldName",
|
|
32
|
+
"foreignKeys",
|
|
33
|
+
"load",
|
|
34
|
+
"modelName",
|
|
35
|
+
"name",
|
|
36
|
+
"postCommit",
|
|
37
|
+
"postLoad",
|
|
38
|
+
"postRemove",
|
|
39
|
+
"postSave",
|
|
40
|
+
"preCommit",
|
|
41
|
+
"preLoad",
|
|
42
|
+
"preRemove",
|
|
43
|
+
"preSave",
|
|
44
|
+
"primaryKey",
|
|
45
|
+
"prototype",
|
|
46
|
+
"remove",
|
|
47
|
+
"save",
|
|
48
|
+
"tableName",
|
|
49
|
+
"type",
|
|
50
|
+
"unique"
|
|
25
51
|
];
|
|
52
|
+
/** Model and attribute names: ASCII letters, digits, underscore; cannot start with digit. */
|
|
53
|
+
const reModelAttributeName = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
|
|
54
|
+
/** tableName and fieldName: ASCII lowercase letters, digits, underscore; cannot start with digit. */
|
|
55
|
+
const reSqlName = /^[a-z_][a-z0-9_]*$/;
|
|
56
|
+
/**
|
|
57
|
+
* Converts a JavaScript name to the default SQL name (snake_case).
|
|
58
|
+
* Uppercase letters become lowercase preceded by underscore, except the first character
|
|
59
|
+
* which is only lowercased without underscore.
|
|
60
|
+
*/
|
|
61
|
+
function toSqlName(name) {
|
|
62
|
+
if (typeof name !== "string" || name.length === 0)
|
|
63
|
+
throw new Error("toSqlName: 'name' must be a non-empty string");
|
|
64
|
+
let result = name[0].toLowerCase();
|
|
65
|
+
for (let i = 1; i < name.length; i++) {
|
|
66
|
+
const c = name[i];
|
|
67
|
+
result += c >= "A" && c <= "Z" ? `_${c.toLowerCase()}` : c;
|
|
68
|
+
}
|
|
69
|
+
return result;
|
|
70
|
+
}
|
|
26
71
|
class Sedentary {
|
|
27
72
|
constructor(options) {
|
|
28
73
|
this.doSync = true;
|
|
@@ -47,46 +92,56 @@ class Sedentary {
|
|
|
47
92
|
this.log = log ? log : log === null ? () => { } : console.log;
|
|
48
93
|
this.doSync = sync;
|
|
49
94
|
}
|
|
50
|
-
Boolean() {
|
|
51
|
-
return new db_1.Type({ [db_1.base]: Boolean, type: "BOOLEAN" });
|
|
95
|
+
Boolean(options) {
|
|
96
|
+
return new db_1.Type({ ...options, [db_1.base]: Boolean, type: "BOOLEAN" });
|
|
52
97
|
}
|
|
53
|
-
DateTime() {
|
|
54
|
-
return new db_1.Type({ [db_1.base]: Date, type: "DATETIME" });
|
|
98
|
+
DateTime(options) {
|
|
99
|
+
return new db_1.Type({ ...options, [db_1.base]: Date, type: "DATETIME" });
|
|
55
100
|
}
|
|
56
101
|
FKey(attribute, options) {
|
|
57
|
-
const { attributeName, fieldName, tableName, type, [db_1.base]: _base, [db_1.size]: _size } = attribute;
|
|
102
|
+
const { attributeName, fieldName, modelName, tableName, type, unique, [db_1.base]: _base, [db_1.size]: _size } = attribute;
|
|
103
|
+
if (!unique)
|
|
104
|
+
throw new Error(`Sedentary.FKey: '${modelName}' model: '${attributeName}' attribute: is not unique: can't be used as FKey target`);
|
|
58
105
|
return new db_1.Type({ [db_1.base]: _base, foreignKey: { attributeName, fieldName, options, tableName }, [db_1.size]: _size, type });
|
|
59
106
|
}
|
|
60
|
-
Float(
|
|
61
|
-
const
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
107
|
+
Float(options) {
|
|
108
|
+
const sizeFloat = "Sedentary.Float: 'size' argument: Wrong value, expected 4 or 8";
|
|
109
|
+
let storageSize;
|
|
110
|
+
let rest;
|
|
111
|
+
[storageSize, rest] = this.checkSize(sizeFloat, options);
|
|
112
|
+
if (storageSize === undefined)
|
|
113
|
+
storageSize = 8;
|
|
114
|
+
if (storageSize !== 4 && storageSize !== 8)
|
|
115
|
+
throw new Error(sizeFloat);
|
|
116
|
+
return new db_1.Type({ ...rest, [db_1.base]: Number, [db_1.size]: storageSize, type: "FLOAT" });
|
|
66
117
|
}
|
|
67
|
-
Int(
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
118
|
+
Int(options) {
|
|
119
|
+
const sizeInt = "Sedentary.Int: 'size' argument: Wrong value, expected 2 or 4";
|
|
120
|
+
let storageSize;
|
|
121
|
+
let rest;
|
|
122
|
+
[storageSize, rest] = this.checkSize(sizeInt, options);
|
|
123
|
+
if (storageSize === undefined)
|
|
124
|
+
storageSize = 4;
|
|
125
|
+
if (storageSize !== 2 && storageSize !== 4)
|
|
126
|
+
throw new Error(sizeInt);
|
|
127
|
+
return new db_1.Type({ ...rest, [db_1.base]: Number, [db_1.size]: storageSize, type: "INT" });
|
|
73
128
|
}
|
|
74
|
-
Int8() {
|
|
75
|
-
return new db_1.Type({ [db_1.base]: BigInt, [db_1.size]: 8, type: "INT8" });
|
|
129
|
+
Int8(options) {
|
|
130
|
+
return new db_1.Type({ ...options, [db_1.base]: BigInt, [db_1.size]: 8, type: "INT8" });
|
|
76
131
|
}
|
|
77
|
-
JSON() {
|
|
78
|
-
return new db_1.Type({ [db_1.base]: Object, type: "JSON" });
|
|
132
|
+
JSON(options) {
|
|
133
|
+
return new db_1.Type({ ...options, [db_1.base]: Object, type: "JSON" });
|
|
79
134
|
}
|
|
80
|
-
Number() {
|
|
81
|
-
return new db_1.Type({ [db_1.base]: Number, type: "NUMBER" });
|
|
135
|
+
Number(options) {
|
|
136
|
+
return new db_1.Type({ ...options, [db_1.base]: Number, type: "NUMBER" });
|
|
82
137
|
}
|
|
83
|
-
None() {
|
|
84
|
-
return new db_1.Type({ [db_1.base]: undefined, type: "NONE" });
|
|
138
|
+
None(options) {
|
|
139
|
+
return new db_1.Type({ ...options, [db_1.base]: undefined, type: "NONE" });
|
|
85
140
|
}
|
|
86
|
-
VarChar(
|
|
141
|
+
VarChar(options) {
|
|
87
142
|
const message = "Sedentary.VarChar: 'size' argument: Wrong value, expected positive integer";
|
|
88
|
-
|
|
89
|
-
return new db_1.Type({ [db_1.base]: String, [db_1.size]:
|
|
143
|
+
const [maxSize, rest] = this.checkSize(message, options);
|
|
144
|
+
return new db_1.Type({ ...rest, [db_1.base]: String, [db_1.size]: maxSize, type: "VARCHAR" });
|
|
90
145
|
}
|
|
91
146
|
checkDB() {
|
|
92
147
|
if (!this.db)
|
|
@@ -115,12 +170,19 @@ class Sedentary {
|
|
|
115
170
|
}
|
|
116
171
|
return true;
|
|
117
172
|
}
|
|
118
|
-
checkSize(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
173
|
+
checkSize(message, options) {
|
|
174
|
+
if (!options)
|
|
175
|
+
return [undefined, {}];
|
|
176
|
+
const { size, ...rest } = options;
|
|
177
|
+
if (size === undefined)
|
|
178
|
+
return [undefined, rest];
|
|
179
|
+
if (typeof size !== "number")
|
|
122
180
|
throw new Error(message);
|
|
123
|
-
|
|
181
|
+
if (size !== parseInt(size.toString(), 10))
|
|
182
|
+
throw new Error(message);
|
|
183
|
+
if (size <= 0)
|
|
184
|
+
throw new Error(message);
|
|
185
|
+
return [size, rest];
|
|
124
186
|
}
|
|
125
187
|
createWhere(modelName, attributes, where) {
|
|
126
188
|
if (typeof where === "string")
|
|
@@ -201,9 +263,9 @@ class Sedentary {
|
|
|
201
263
|
if (this.autoSync || sync)
|
|
202
264
|
await this.sync();
|
|
203
265
|
}
|
|
204
|
-
catch (
|
|
205
|
-
this.log(
|
|
206
|
-
throw
|
|
266
|
+
catch (error) {
|
|
267
|
+
this.log(`Connecting: ${error instanceof Error ? error.message : JSON.stringify(error)}`);
|
|
268
|
+
throw error;
|
|
207
269
|
}
|
|
208
270
|
}
|
|
209
271
|
async sync() {
|
|
@@ -227,6 +289,8 @@ class Sedentary {
|
|
|
227
289
|
this.checkDB();
|
|
228
290
|
if (typeof modelName !== "string")
|
|
229
291
|
throw new Error("Sedentary.model: 'name' argument: Wrong type, expected 'string'");
|
|
292
|
+
if (!reModelAttributeName.test(modelName))
|
|
293
|
+
throw new Error(`Sedentary.model: '${modelName}' model: Invalid model name, expected ASCII letters, digits or underscore, cannot start with digit`);
|
|
230
294
|
if (this.models[modelName])
|
|
231
295
|
throw new Error(`Sedentary.model: '${modelName}' model: Model already defined`);
|
|
232
296
|
if (!_attributes)
|
|
@@ -247,10 +311,12 @@ class Sedentary {
|
|
|
247
311
|
if (options.parent && options.primaryKey)
|
|
248
312
|
throw new Error(`Sedentary.model: '${modelName}' model: 'parent' and 'primaryKey' options conflict each other`);
|
|
249
313
|
let autoIncrement = true;
|
|
250
|
-
const { indexes, int8id, parent, primaryKey, sync, tableName } = { sync: this.doSync, tableName: modelName, ...options };
|
|
314
|
+
const { indexes, int8id, parent, primaryKey, sync, tableName } = { sync: this.doSync, tableName: toSqlName(modelName), ...options };
|
|
315
|
+
if (!reSqlName.test(tableName))
|
|
316
|
+
throw new Error(`Sedentary.model: '${modelName}' model: Invalid tableName '${tableName}', expected lowercase ASCII letters, digits or underscore, cannot start with digit`);
|
|
251
317
|
let aArray = int8id
|
|
252
|
-
? [new db_1.Attribute({ ...this.Int8(), attributeName: "id", fieldName: "id", modelName, notNull: true, tableName, unique: true })]
|
|
253
|
-
: [new db_1.Attribute({ ...this.Int(4), attributeName: "id", fieldName: "id", modelName, notNull: true, tableName, unique: true })];
|
|
318
|
+
? [new db_1.Attribute({ ...this.Int8(), attributeName: "id", fieldName: toSqlName("id"), modelName, notNull: true, tableName, unique: true })]
|
|
319
|
+
: [new db_1.Attribute({ ...this.Int({ size: 4 }), attributeName: "id", fieldName: toSqlName("id"), modelName, notNull: true, tableName, unique: true })];
|
|
254
320
|
let constraints = [{ attribute: aArray[0], constraintName: `${tableName}_id_unique`, type: "u" }];
|
|
255
321
|
const iArray = [];
|
|
256
322
|
let pk = aArray[0];
|
|
@@ -274,51 +340,37 @@ class Sedentary {
|
|
|
274
340
|
constraints = [];
|
|
275
341
|
}
|
|
276
342
|
for (const attributeName of Object.keys(_attributes).sort()) {
|
|
343
|
+
if (!reModelAttributeName.test(attributeName))
|
|
344
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: Invalid attribute name, expected ASCII letters, digits or underscore, cannot start with digit`);
|
|
277
345
|
if (reservedNames.includes(attributeName))
|
|
278
346
|
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: Reserved name`);
|
|
279
|
-
const call = (defaultValue, fieldName, notNull, unique, func, message1, message2) => {
|
|
280
|
-
if (func === this.FKey)
|
|
281
|
-
throw new Error(`${message1} 'this.FKey' can't be used directly`);
|
|
282
|
-
if (![this.Boolean, this.DateTime, this.Float, this.Int, this.JSON, this.Int8, this.None, this.Number, this.VarChar].includes(func))
|
|
283
|
-
throw new Error(`${message1} ${message2}`);
|
|
284
|
-
return new db_1.Attribute({ attributeName, defaultValue, fieldName, modelName, notNull, tableName, unique, ...func() });
|
|
285
|
-
};
|
|
286
347
|
const attributeDefinition = _attributes[attributeName];
|
|
287
348
|
let { [db_1.base]: _base, defaultValue, fieldName, foreignKey, notNull, [db_1.size]: _size, type, unique } = (() => {
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
if (type instanceof db_1.Type)
|
|
308
|
-
return new db_1.Attribute({ attributeName, defaultValue, fieldName, modelName, notNull, tableName, unique, ...type });
|
|
309
|
-
if (type instanceof Function)
|
|
310
|
-
return call(defaultValue, fieldName, notNull, unique, type, `Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'type' option:`, "Wrong type, expected 'Type'");
|
|
311
|
-
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'type' option: Wrong type, expected 'Type'`);
|
|
312
|
-
})();
|
|
313
|
-
const { [db_1.base]: _base, defaultValue } = ret;
|
|
314
|
-
if (defaultValue !== undefined) {
|
|
315
|
-
if (_base === BigInt && typeof defaultValue !== "bigint")
|
|
349
|
+
if (!(attributeDefinition instanceof db_1.Type))
|
|
350
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: '${attributeName}': Wrong type, expected 'Type'`);
|
|
351
|
+
const ret = new db_1.Attribute({ attributeName, fieldName: toSqlName(attributeName), modelName, notNull: false, tableName, unique: false, ...attributeDefinition });
|
|
352
|
+
const { defaultValue, fieldName, notNull, unique } = ret;
|
|
353
|
+
if (defaultValue === null)
|
|
354
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'defaultValue' option: Does 'null' default value really make sense?`);
|
|
355
|
+
if (typeof fieldName !== "string")
|
|
356
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'fieldName' option: Wrong type, expected 'string'`);
|
|
357
|
+
if (!reSqlName.test(fieldName)) {
|
|
358
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: Invalid fieldName '${fieldName}', ` +
|
|
359
|
+
"expected lowercase ASCII letters, digits or underscore, cannot start with digit");
|
|
360
|
+
}
|
|
361
|
+
if (typeof notNull !== "boolean")
|
|
362
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'notNull' option: Wrong type, expected 'boolean'`);
|
|
363
|
+
if (typeof unique !== "boolean")
|
|
364
|
+
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'unique' option: Wrong type, expected 'boolean'`);
|
|
365
|
+
const { [db_1.base]: _base, defaultValue: _defaultValue } = ret;
|
|
366
|
+
if (_defaultValue !== undefined) {
|
|
367
|
+
if (_base === BigInt && typeof _defaultValue !== "bigint")
|
|
316
368
|
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'defaultValue' option: Wrong type, expected 'BigInt'`);
|
|
317
|
-
if (_base === Date && !(
|
|
369
|
+
if (_base === Date && !(_defaultValue instanceof Date))
|
|
318
370
|
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'defaultValue' option: Wrong type, expected 'Date'`);
|
|
319
|
-
if (_base === Number && typeof
|
|
371
|
+
if (_base === Number && typeof _defaultValue !== "number")
|
|
320
372
|
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'defaultValue' option: Wrong type, expected 'number'`);
|
|
321
|
-
if (_base === String && typeof
|
|
373
|
+
if (_base === String && typeof _defaultValue !== "string")
|
|
322
374
|
throw new Error(`Sedentary.model: '${modelName}' model: '${attributeName}' attribute: 'defaultValue' option: Wrong type, expected 'string'`);
|
|
323
375
|
}
|
|
324
376
|
return ret;
|
|
@@ -417,9 +469,9 @@ class Sedentary {
|
|
|
417
469
|
return ret;
|
|
418
470
|
}, {});
|
|
419
471
|
for (const foreignKey in foreignKeys) {
|
|
420
|
-
if (foreignKey
|
|
472
|
+
if (`${foreignKey}Load` in _attributes)
|
|
421
473
|
throw new Error(`Sedentary.model: '${modelName}' model: '${foreignKey}' attribute: '${foreignKey}Load' inferred methods conflicts with an attribute`);
|
|
422
|
-
if (foreignKey
|
|
474
|
+
if (`${foreignKey}Load` in _methods)
|
|
423
475
|
throw new Error(`Sedentary.model: '${modelName}' model: '${foreignKey}' attribute: '${foreignKey}Load' inferred methods conflicts with a method`);
|
|
424
476
|
}
|
|
425
477
|
for (const method in _methods)
|
|
@@ -434,20 +486,20 @@ class Sedentary {
|
|
|
434
486
|
if (attribute in parent[methods])
|
|
435
487
|
throw new Error(`Sedentary.model: '${modelName}' model: '${attribute}' attribute: conflicts with a method of '${parent.modelName}' model`);
|
|
436
488
|
for (const foreignKey in parent.foreignKeys)
|
|
437
|
-
if (attribute === foreignKey
|
|
489
|
+
if (attribute === `${foreignKey}Load`)
|
|
438
490
|
throw new Error(`Sedentary.model: '${modelName}' model: '${attribute}' attribute: conflicts with an inferred methods of '${parent.modelName}' model`);
|
|
439
491
|
}
|
|
440
492
|
for (const foreignKey in foreignKeys) {
|
|
441
|
-
if (foreignKey
|
|
493
|
+
if (`${foreignKey}Load` in parent[attributes])
|
|
442
494
|
throw new Error(`Sedentary.model: '${modelName}' model: '${foreignKey}' attribute: '${foreignKey}Load' inferred methods conflicts with an attribute of '${parent.modelName}' model`);
|
|
443
|
-
if (foreignKey
|
|
495
|
+
if (`${foreignKey}Load` in parent[methods])
|
|
444
496
|
throw new Error(`Sedentary.model: '${modelName}' model: '${foreignKey}' attribute: '${foreignKey}Load' inferred methods conflicts with a method of '${parent.modelName}' model`);
|
|
445
497
|
}
|
|
446
498
|
for (const method in _methods) {
|
|
447
499
|
if (method in parent[attributes])
|
|
448
500
|
throw new Error(`Sedentary.model: '${modelName}' model: '${method}' method: conflicts with an attribute of '${parent.modelName}' model`);
|
|
449
501
|
for (const foreignKey in parent.foreignKeys)
|
|
450
|
-
if (foreignKey
|
|
502
|
+
if (`${foreignKey}Load` === method)
|
|
451
503
|
throw new Error(`Sedentary.model: '${modelName}' model: '${method}' method: conflicts with an inferred methods of '${parent.modelName}' model`);
|
|
452
504
|
}
|
|
453
505
|
checkParent(parent.parent);
|
|
@@ -464,7 +516,7 @@ class Sedentary {
|
|
|
464
516
|
this.db.tables.push(table);
|
|
465
517
|
const cancel_ = this.db.cancel(tableName);
|
|
466
518
|
const cancel = (where, tx) => cancel_(this.createWhere(modelName, attr2field, where)[0], tx);
|
|
467
|
-
Object.defineProperty(cancel, "name", { value: modelName
|
|
519
|
+
Object.defineProperty(cancel, "name", { value: `${modelName}.cancel` });
|
|
468
520
|
const load_ = this.db.load(tableName, attr2field, pk, ret, table);
|
|
469
521
|
const load = (where, ...args) => {
|
|
470
522
|
let order = undefined;
|
|
@@ -501,7 +553,7 @@ class Sedentary {
|
|
|
501
553
|
throw new Error(`${modelName}.load: 'tx' argument: Wrong type, expected 'Transaction'`);
|
|
502
554
|
return load_(this.createWhere(modelName, attr2field, where)[0], order, limit, tx, lock);
|
|
503
555
|
};
|
|
504
|
-
Object.defineProperty(load, "name", { value: modelName
|
|
556
|
+
Object.defineProperty(load, "name", { value: `${modelName}.load` });
|
|
505
557
|
Object.defineProperty(ret, "cancel", { value: cancel });
|
|
506
558
|
Object.defineProperty(ret, "name", { value: modelName });
|
|
507
559
|
Object.defineProperty(ret, "load", { value: load });
|
|
@@ -526,7 +578,7 @@ class Sedentary {
|
|
|
526
578
|
ensureActions(this).push({ action: "remove", records });
|
|
527
579
|
return records;
|
|
528
580
|
};
|
|
529
|
-
Object.defineProperty(ret.prototype.remove, "name", { value: modelName
|
|
581
|
+
Object.defineProperty(ret.prototype.remove, "name", { value: `${modelName}.remove` });
|
|
530
582
|
const save = this.db.save(tableName, attr2field, pk);
|
|
531
583
|
ret.prototype.save = async function () {
|
|
532
584
|
this.preSave();
|
|
@@ -536,7 +588,7 @@ class Sedentary {
|
|
|
536
588
|
ensureActions(this).push({ action: "save", records });
|
|
537
589
|
return records;
|
|
538
590
|
};
|
|
539
|
-
Object.defineProperty(ret.prototype.save, "name", { value: modelName
|
|
591
|
+
Object.defineProperty(ret.prototype.save, "name", { value: `${modelName}.save` });
|
|
540
592
|
for (const attribute of aArray)
|
|
541
593
|
Object.defineProperty(ret, attribute.attributeName, { value: attribute });
|
|
542
594
|
for (const key of ["attributeName", db_1.base, "fieldName", "modelName", db_1.size, "tableName", "type", "unique"])
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{\"type\":\"commonjs\"}
|