@quantform/sqlite 0.6.6 → 0.7.0-beta.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/sqlite-language.d.ts +10 -0
- package/dist/sqlite-language.d.ts.map +1 -0
- package/dist/sqlite-language.js +80 -0
- package/dist/sqlite-language.spec.d.ts +2 -0
- package/dist/sqlite-language.spec.d.ts.map +1 -0
- package/dist/sqlite-language.spec.js +50 -0
- package/dist/sqlite-storage.d.ts +11 -8
- package/dist/sqlite-storage.d.ts.map +1 -1
- package/dist/sqlite-storage.js +55 -59
- package/dist/sqlite-storage.spec.d.ts +2 -0
- package/dist/sqlite-storage.spec.d.ts.map +1 -0
- package/dist/sqlite-storage.spec.js +204 -0
- package/jest.config.ts +24 -2
- package/package.json +4 -4
- package/src/index.ts +8 -1
- package/src/sqlite-language.spec.ts +53 -0
- package/src/sqlite-language.ts +114 -0
- package/src/sqlite-storage.spec.ts +232 -0
- package/src/sqlite-storage.ts +63 -87
- package/tsconfig.json +7 -4
- package/dist/error.d.ts +0 -2
- package/dist/error.d.ts.map +0 -1
- package/dist/error.js +0 -7
- package/src/error.ts +0 -3
- package/src/sqlite-feed.spec.ts +0 -157
- package/src/sqlite-measurement.spec.ts +0 -150
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { d, eq, lt, Storage } from '@quantform/core';
|
|
2
|
+
|
|
3
|
+
import { SQLiteLanguage } from './sqlite-language';
|
|
4
|
+
|
|
5
|
+
describe(SQLiteLanguage.name, () => {
|
|
6
|
+
test('create table for object', async () => {
|
|
7
|
+
const object = Storage.createObject('orders', {
|
|
8
|
+
timestamp: 'number',
|
|
9
|
+
price: 'decimal',
|
|
10
|
+
id: 'string'
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
const sql = SQLiteLanguage.createTable(object);
|
|
14
|
+
|
|
15
|
+
expect(sql).toEqual(
|
|
16
|
+
`CREATE TABLE IF NOT EXISTS "orders" (timestamp INTEGER NOT NULL, price TEXT NOT NULL, id TEXT NOT NULL, PRIMARY KEY (timestamp))`
|
|
17
|
+
);
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
test('select to query object', async () => {
|
|
21
|
+
const object = Storage.createObject('orders', {
|
|
22
|
+
timestamp: 'number',
|
|
23
|
+
price: 'decimal',
|
|
24
|
+
id: 'string'
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
const sql = SQLiteLanguage.query(object, {
|
|
28
|
+
where: {
|
|
29
|
+
timestamp: lt(100),
|
|
30
|
+
id: eq('unique-id'),
|
|
31
|
+
price: eq(d('1.123456789123456789'))
|
|
32
|
+
},
|
|
33
|
+
limit: 3,
|
|
34
|
+
offset: 2
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
expect(sql).toEqual(
|
|
38
|
+
`SELECT timestamp, price, id FROM "orders" WHERE timestamp < 100 AND id = 'unique-id' AND price = '1.123456789123456789' LIMIT 3 OFFSET 2`
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
test('replace statement', async () => {
|
|
43
|
+
const object = Storage.createObject('orders', {
|
|
44
|
+
timestamp: 'number',
|
|
45
|
+
price: 'decimal',
|
|
46
|
+
id: 'string'
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const sql = SQLiteLanguage.replace(object);
|
|
50
|
+
|
|
51
|
+
expect(sql).toEqual(`REPLACE INTO "orders" (timestamp, price, id) VALUES(?, ?, ?)`);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import {
|
|
2
|
+
InferQueryObject,
|
|
3
|
+
Query,
|
|
4
|
+
QueryMappingType,
|
|
5
|
+
QueryObject,
|
|
6
|
+
QueryObjectType,
|
|
7
|
+
QueryWhere
|
|
8
|
+
} from '@quantform/core';
|
|
9
|
+
|
|
10
|
+
export class SQLiteLanguage {
|
|
11
|
+
static getType(type: QueryMappingType) {
|
|
12
|
+
switch (type) {
|
|
13
|
+
case 'number':
|
|
14
|
+
return 'INTEGER';
|
|
15
|
+
case 'decimal':
|
|
16
|
+
return 'TEXT';
|
|
17
|
+
case 'string':
|
|
18
|
+
return 'TEXT';
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
static getValue(type: QueryMappingType, value: any) {
|
|
23
|
+
switch (type) {
|
|
24
|
+
case 'number':
|
|
25
|
+
return value;
|
|
26
|
+
case 'decimal':
|
|
27
|
+
return `'${value.toString()}'`;
|
|
28
|
+
case 'string':
|
|
29
|
+
return `'${value}'`;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
static getConstraint(
|
|
34
|
+
columnName: string,
|
|
35
|
+
type: QueryMappingType,
|
|
36
|
+
constraint: QueryWhere
|
|
37
|
+
) {
|
|
38
|
+
switch (constraint?.type) {
|
|
39
|
+
case 'eq':
|
|
40
|
+
return `${columnName} = ${SQLiteLanguage.getValue(type, constraint.value)}`;
|
|
41
|
+
case 'gt':
|
|
42
|
+
return `${columnName} > ${SQLiteLanguage.getValue(type, constraint.value)}`;
|
|
43
|
+
case 'lt':
|
|
44
|
+
return `${columnName} < ${SQLiteLanguage.getValue(type, constraint.value)}`;
|
|
45
|
+
case 'between':
|
|
46
|
+
return `${columnName} > ${SQLiteLanguage.getValue(
|
|
47
|
+
type,
|
|
48
|
+
constraint.min
|
|
49
|
+
)} AND ${columnName} < ${SQLiteLanguage.getValue(type, constraint.max)}`;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
static createTable<T extends QueryObjectType<K>, K extends QueryObject>(type: T) {
|
|
54
|
+
const columns = `${Object.entries(type.type)
|
|
55
|
+
.map(([name, type]) => `${name} ${SQLiteLanguage.getType(type)} NOT NULL`)
|
|
56
|
+
.join(', ')}`;
|
|
57
|
+
|
|
58
|
+
return `CREATE TABLE IF NOT EXISTS "${type.discriminator}" (${columns}, PRIMARY KEY (timestamp))`;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static query<T extends QueryObjectType<K>, K extends QueryObject>(
|
|
62
|
+
type: T,
|
|
63
|
+
query: Query<InferQueryObject<T>>
|
|
64
|
+
) {
|
|
65
|
+
const columns = `${Object.entries(type.type)
|
|
66
|
+
.map(([name]) => `${name}`)
|
|
67
|
+
.join(', ')}`;
|
|
68
|
+
|
|
69
|
+
let sql = `SELECT ${columns} FROM "${type.discriminator}"`;
|
|
70
|
+
|
|
71
|
+
if (query.where) {
|
|
72
|
+
const where = Array.of<string>();
|
|
73
|
+
|
|
74
|
+
for (const columnName of Object.keys(query.where)) {
|
|
75
|
+
const constraint = query.where[columnName];
|
|
76
|
+
if (!constraint) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
where.push(
|
|
81
|
+
SQLiteLanguage.getConstraint(columnName, type.type[columnName], constraint)
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
sql = `${sql} WHERE ${where.join(' AND ')}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (query.orderBy) {
|
|
89
|
+
sql = `${sql} ORDER BY timestamp ${query.orderBy ?? 'ASC'}`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
if (query.limit) {
|
|
93
|
+
sql = `${sql} LIMIT ${query.limit}`;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (query.offset) {
|
|
97
|
+
sql = `${sql} OFFSET ${query.offset}`;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return sql;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
static replace<T extends QueryObjectType<K>, K extends QueryObject>(type: T) {
|
|
104
|
+
const columns = `${Object.entries(type.type)
|
|
105
|
+
.map(([name]) => `${name}`)
|
|
106
|
+
.join(', ')}`;
|
|
107
|
+
|
|
108
|
+
return `REPLACE INTO "${type.discriminator}" (${columns}) VALUES(${Object.entries(
|
|
109
|
+
type.type
|
|
110
|
+
)
|
|
111
|
+
.map(() => '?')
|
|
112
|
+
.join(', ')})`;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { existsSync, unlinkSync } from 'fs';
|
|
2
|
+
|
|
3
|
+
import { d, eq, gt, lt, makeTestModule, Storage } from '@quantform/core';
|
|
4
|
+
|
|
5
|
+
import { SQLiteStorage } from './sqlite-storage';
|
|
6
|
+
|
|
7
|
+
describe(SQLiteStorage.name, () => {
|
|
8
|
+
let fixtures: Awaited<ReturnType<typeof getFixtures>>;
|
|
9
|
+
|
|
10
|
+
beforeEach(async () => {
|
|
11
|
+
fixtures = await getFixtures();
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
afterEach(() => {
|
|
15
|
+
fixtures.dispose();
|
|
16
|
+
});
|
|
17
|
+
/*
|
|
18
|
+
test('index return the names of discriminators', async () => {
|
|
19
|
+
const { sut } = fixtures;
|
|
20
|
+
|
|
21
|
+
await sut.save({ discriminator: 'pricing' }, [{ timestamp: 1, message: 'test-1' }]);
|
|
22
|
+
await sut.save({ discriminator: 'ordering' }, [{ timestamp: 1, message: 'test-1' }]);
|
|
23
|
+
|
|
24
|
+
const index = await sut.index();
|
|
25
|
+
|
|
26
|
+
expect(index).toEqual(['pricing', 'ordering']);
|
|
27
|
+
});
|
|
28
|
+
*/
|
|
29
|
+
test('write and read single object', async () => {
|
|
30
|
+
const { sut, object } = fixtures;
|
|
31
|
+
|
|
32
|
+
await sut.save(object, [
|
|
33
|
+
{ timestamp: 1, id: '123 123', price: d('1.123456789123456789'), quantity: 5 }
|
|
34
|
+
]);
|
|
35
|
+
|
|
36
|
+
const set = await sut.query(object, {
|
|
37
|
+
where: {
|
|
38
|
+
id: eq('123 123')
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
expect(set).toEqual([
|
|
43
|
+
{ timestamp: 1, id: '123 123', price: d('1.123456789123456789'), quantity: 5 }
|
|
44
|
+
]);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('save and read full data', async () => {
|
|
48
|
+
const { sut } = fixtures;
|
|
49
|
+
|
|
50
|
+
const pricing = Storage.createObject('pricing', {
|
|
51
|
+
timestamp: 'number',
|
|
52
|
+
rate: 'decimal'
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
await sut.save(pricing, [
|
|
56
|
+
{ timestamp: 1, rate: d(1) },
|
|
57
|
+
{ timestamp: 2, rate: d(2) },
|
|
58
|
+
{ timestamp: 3, rate: d(3) },
|
|
59
|
+
{ timestamp: 4, rate: d(4) },
|
|
60
|
+
{ timestamp: 5, rate: d(5) }
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
const set = await sut.query(pricing, {});
|
|
64
|
+
|
|
65
|
+
expect(set).toEqual([
|
|
66
|
+
{ timestamp: 1, rate: d(1) },
|
|
67
|
+
{ timestamp: 2, rate: d(2) },
|
|
68
|
+
{ timestamp: 3, rate: d(3) },
|
|
69
|
+
{ timestamp: 4, rate: d(4) },
|
|
70
|
+
{ timestamp: 5, rate: d(5) }
|
|
71
|
+
]);
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('save and read limited data', async () => {
|
|
75
|
+
const { sut } = fixtures;
|
|
76
|
+
|
|
77
|
+
const pricing = Storage.createObject('pricing', {
|
|
78
|
+
timestamp: 'number',
|
|
79
|
+
rate: 'decimal'
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await sut.save(pricing, [
|
|
83
|
+
{ timestamp: 1, rate: d(1) },
|
|
84
|
+
{ timestamp: 2, rate: d(2) },
|
|
85
|
+
{ timestamp: 3, rate: d(3) },
|
|
86
|
+
{ timestamp: 4, rate: d(4) },
|
|
87
|
+
{ timestamp: 5, rate: d(5) }
|
|
88
|
+
]);
|
|
89
|
+
|
|
90
|
+
const set = await sut.query(pricing, { limit: 3 });
|
|
91
|
+
|
|
92
|
+
expect(set).toEqual([
|
|
93
|
+
{ timestamp: 1, rate: d(1) },
|
|
94
|
+
{ timestamp: 2, rate: d(2) },
|
|
95
|
+
{ timestamp: 3, rate: d(3) }
|
|
96
|
+
]);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('save and read desc ordered data', async () => {
|
|
100
|
+
const { sut } = fixtures;
|
|
101
|
+
|
|
102
|
+
const pricing = Storage.createObject('pricing', {
|
|
103
|
+
timestamp: 'number',
|
|
104
|
+
rate: 'decimal'
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
await sut.save(pricing, [
|
|
108
|
+
{ timestamp: 1, rate: d(1) },
|
|
109
|
+
{ timestamp: 2, rate: d(2) },
|
|
110
|
+
{ timestamp: 3, rate: d(3) },
|
|
111
|
+
{ timestamp: 4, rate: d(4) },
|
|
112
|
+
{ timestamp: 5, rate: d(5) }
|
|
113
|
+
]);
|
|
114
|
+
|
|
115
|
+
const set = await sut.query(pricing, { orderBy: 'DESC' });
|
|
116
|
+
|
|
117
|
+
expect(set).toEqual([
|
|
118
|
+
{ timestamp: 5, rate: d(5) },
|
|
119
|
+
{ timestamp: 4, rate: d(4) },
|
|
120
|
+
{ timestamp: 3, rate: d(3) },
|
|
121
|
+
{ timestamp: 2, rate: d(2) },
|
|
122
|
+
{ timestamp: 1, rate: d(1) }
|
|
123
|
+
]);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('save and read filtered eq data', async () => {
|
|
127
|
+
const { sut } = fixtures;
|
|
128
|
+
|
|
129
|
+
const pricing = Storage.createObject('pricing', {
|
|
130
|
+
timestamp: 'number',
|
|
131
|
+
rate: 'decimal'
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
await sut.save(pricing, [
|
|
135
|
+
{ timestamp: 1, rate: d(1) },
|
|
136
|
+
{ timestamp: 2, rate: d(2) },
|
|
137
|
+
{ timestamp: 3, rate: d(3) },
|
|
138
|
+
{ timestamp: 4, rate: d(4) },
|
|
139
|
+
{ timestamp: 5, rate: d(5) }
|
|
140
|
+
]);
|
|
141
|
+
|
|
142
|
+
const set = await sut.query(pricing, {
|
|
143
|
+
where: {
|
|
144
|
+
timestamp: eq(4)
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
expect(set).toEqual([{ timestamp: 4, rate: d(4) }]);
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('save and read filtered lt data', async () => {
|
|
152
|
+
const { sut } = fixtures;
|
|
153
|
+
|
|
154
|
+
const pricing = Storage.createObject('pricing', {
|
|
155
|
+
timestamp: 'number',
|
|
156
|
+
rate: 'decimal'
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
await sut.save(pricing, [
|
|
160
|
+
{ timestamp: 1, rate: d(1) },
|
|
161
|
+
{ timestamp: 2, rate: d(2) },
|
|
162
|
+
{ timestamp: 3, rate: d(3) },
|
|
163
|
+
{ timestamp: 4, rate: d(4) },
|
|
164
|
+
{ timestamp: 5, rate: d(5) }
|
|
165
|
+
]);
|
|
166
|
+
|
|
167
|
+
const set = await sut.query(pricing, {
|
|
168
|
+
where: {
|
|
169
|
+
timestamp: lt(3)
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
expect(set).toEqual([
|
|
174
|
+
{ timestamp: 1, rate: d(1) },
|
|
175
|
+
{ timestamp: 2, rate: d(2) }
|
|
176
|
+
]);
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
test('save and read filtered gt data', async () => {
|
|
180
|
+
const { sut } = fixtures;
|
|
181
|
+
|
|
182
|
+
const pricing = Storage.createObject('pricing', {
|
|
183
|
+
timestamp: 'number',
|
|
184
|
+
rate: 'decimal'
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
await sut.save(pricing, [
|
|
188
|
+
{ timestamp: 1, rate: d(1) },
|
|
189
|
+
{ timestamp: 2, rate: d(2) },
|
|
190
|
+
{ timestamp: 3, rate: d(3) },
|
|
191
|
+
{ timestamp: 4, rate: d(4) },
|
|
192
|
+
{ timestamp: 5, rate: d(5) }
|
|
193
|
+
]);
|
|
194
|
+
|
|
195
|
+
const set = await sut.query(pricing, {
|
|
196
|
+
where: {
|
|
197
|
+
timestamp: gt(3)
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
expect(set).toEqual([
|
|
202
|
+
{ timestamp: 4, rate: d(4) },
|
|
203
|
+
{ timestamp: 5, rate: d(5) }
|
|
204
|
+
]);
|
|
205
|
+
});
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
async function getFixtures() {
|
|
209
|
+
const { get } = await makeTestModule([
|
|
210
|
+
{
|
|
211
|
+
provide: 'storage',
|
|
212
|
+
useValue: new SQLiteStorage('test.db')
|
|
213
|
+
}
|
|
214
|
+
]);
|
|
215
|
+
|
|
216
|
+
const sut = get<SQLiteStorage>('storage');
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
sut,
|
|
220
|
+
object: Storage.createObject('test', {
|
|
221
|
+
timestamp: 'number',
|
|
222
|
+
price: 'decimal',
|
|
223
|
+
quantity: 'number',
|
|
224
|
+
id: 'string'
|
|
225
|
+
}),
|
|
226
|
+
dispose() {
|
|
227
|
+
if (existsSync(sut.filename)) {
|
|
228
|
+
unlinkSync(sut.filename);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
package/src/sqlite-storage.ts
CHANGED
|
@@ -1,37 +1,38 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SessionBuilder,
|
|
3
|
-
SessionFeature,
|
|
4
|
-
Storage,
|
|
5
|
-
StorageDocument,
|
|
6
|
-
StorageQueryOptions,
|
|
7
|
-
workingDirectory
|
|
8
|
-
} from '@quantform/core';
|
|
9
1
|
import { Database } from 'better-sqlite3';
|
|
10
2
|
import * as bettersqlite3 from 'better-sqlite3';
|
|
11
3
|
import { existsSync, mkdirSync } from 'fs';
|
|
12
4
|
import { dirname, join } from 'path';
|
|
13
5
|
|
|
14
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
d,
|
|
8
|
+
InferQueryObject,
|
|
9
|
+
provider,
|
|
10
|
+
Query,
|
|
11
|
+
QueryObject,
|
|
12
|
+
QueryObjectType,
|
|
13
|
+
Storage,
|
|
14
|
+
StorageFactory,
|
|
15
|
+
workingDirectory
|
|
16
|
+
} from '@quantform/core';
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
18
|
+
import { SQLiteLanguage } from './sqlite-language';
|
|
19
|
+
|
|
20
|
+
@provider()
|
|
21
|
+
export class SQLiteStorageFactory implements StorageFactory {
|
|
22
|
+
constructor(private readonly directory?: string) {}
|
|
23
|
+
|
|
24
|
+
for(key: string): Storage {
|
|
25
|
+
return new SQLiteStorage(
|
|
26
|
+
join(this.directory ?? workingDirectory(), `/${key}.sqlite`)
|
|
21
27
|
);
|
|
22
|
-
}
|
|
28
|
+
}
|
|
23
29
|
}
|
|
24
30
|
|
|
25
31
|
export class SQLiteStorage implements Storage {
|
|
26
|
-
protected connection
|
|
27
|
-
|
|
28
|
-
constructor(private readonly filename: string) {}
|
|
29
|
-
|
|
30
|
-
private tryConnect() {
|
|
31
|
-
if (this.connection) {
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
32
|
+
protected connection: Database;
|
|
33
|
+
private tables?: string[];
|
|
34
34
|
|
|
35
|
+
constructor(readonly filename: string) {
|
|
35
36
|
if (!existsSync(dirname(this.filename))) {
|
|
36
37
|
mkdirSync(dirname(this.filename), { recursive: true });
|
|
37
38
|
}
|
|
@@ -39,92 +40,67 @@ export class SQLiteStorage implements Storage {
|
|
|
39
40
|
this.connection = bettersqlite3(this.filename);
|
|
40
41
|
}
|
|
41
42
|
|
|
42
|
-
private tryCreateTable(table: string) {
|
|
43
|
-
if (!this.connection) {
|
|
44
|
-
throw noConnectionError();
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
this.connection.exec(
|
|
48
|
-
`CREATE TABLE IF NOT EXISTS "${table}" (
|
|
49
|
-
timestamp INTEGER NOT NULL,
|
|
50
|
-
kind TEXT NOT NULL,
|
|
51
|
-
json TEXT NOT NULL,
|
|
52
|
-
PRIMARY KEY (timestamp, kind)
|
|
53
|
-
)`
|
|
54
|
-
);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
43
|
async index(): Promise<Array<string>> {
|
|
58
|
-
this.tryConnect();
|
|
59
|
-
|
|
60
|
-
if (!this.connection) {
|
|
61
|
-
throw noConnectionError();
|
|
62
|
-
}
|
|
63
|
-
|
|
64
44
|
return this.connection
|
|
65
45
|
.prepare("SELECT name FROM sqlite_master WHERE type='table'")
|
|
66
46
|
.all()
|
|
67
47
|
.map(it => it.name);
|
|
68
48
|
}
|
|
69
49
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
50
|
+
// eslint-disable-next-line complexity
|
|
51
|
+
async query<T extends QueryObjectType<K>, K extends QueryObject>(
|
|
52
|
+
type: T,
|
|
53
|
+
query: Query<InferQueryObject<T>>
|
|
54
|
+
): Promise<InferQueryObject<T>[]> {
|
|
55
|
+
if (!this.tables) {
|
|
56
|
+
this.tables = await this.index();
|
|
75
57
|
}
|
|
76
58
|
|
|
77
|
-
if (
|
|
78
|
-
!this.connection
|
|
79
|
-
.prepare(
|
|
80
|
-
`SELECT name FROM sqlite_master WHERE type='table' AND name='${library}'`
|
|
81
|
-
)
|
|
82
|
-
.all().length
|
|
83
|
-
) {
|
|
59
|
+
if (!this.tables.includes(type.discriminator)) {
|
|
84
60
|
return [];
|
|
85
61
|
}
|
|
86
62
|
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
Math.min(options.count, 50000)
|
|
101
|
-
);
|
|
102
|
-
|
|
103
|
-
if (isBackward) {
|
|
104
|
-
rows = rows.reverse();
|
|
105
|
-
}
|
|
63
|
+
const objects = await this.connection
|
|
64
|
+
.prepare(SQLiteLanguage.query(type, query))
|
|
65
|
+
.all();
|
|
66
|
+
|
|
67
|
+
const types = Object.keys(type.type);
|
|
68
|
+
|
|
69
|
+
objects.forEach(it => {
|
|
70
|
+
for (const prop of types) {
|
|
71
|
+
if (type.type[prop] == 'decimal') {
|
|
72
|
+
it[prop] = d(it[prop]);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
});
|
|
106
76
|
|
|
107
|
-
return
|
|
77
|
+
return objects;
|
|
108
78
|
}
|
|
109
79
|
|
|
110
|
-
async save
|
|
111
|
-
|
|
80
|
+
async save<T extends QueryObjectType<K>, K extends QueryObject>(
|
|
81
|
+
type: T,
|
|
82
|
+
objects: InferQueryObject<T>[]
|
|
83
|
+
): Promise<void> {
|
|
84
|
+
if (!this.tables) {
|
|
85
|
+
this.tables = await this.index();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (!this.tables.includes(type.discriminator)) {
|
|
89
|
+
this.connection.exec(SQLiteLanguage.createTable(type));
|
|
112
90
|
|
|
113
|
-
|
|
114
|
-
throw noConnectionError();
|
|
91
|
+
this.tables = undefined;
|
|
115
92
|
}
|
|
116
93
|
|
|
117
|
-
this.
|
|
94
|
+
const statement = this.connection.prepare(SQLiteLanguage.replace(type));
|
|
95
|
+
|
|
96
|
+
const types = Object.keys(type.type);
|
|
118
97
|
|
|
119
|
-
const
|
|
120
|
-
REPLACE INTO "${library}" (timestamp, kind, json)
|
|
121
|
-
VALUES(?, ?, ?);
|
|
122
|
-
`);
|
|
98
|
+
const mapper = (it: InferQueryObject<T>) => types.map(type => it[type].toString());
|
|
123
99
|
|
|
124
100
|
const insertMany = this.connection.transaction(rows =>
|
|
125
|
-
rows.forEach((it:
|
|
101
|
+
rows.forEach((it: InferQueryObject<T>) => statement.run(mapper(it)))
|
|
126
102
|
);
|
|
127
103
|
|
|
128
|
-
insertMany(
|
|
104
|
+
insertMany(objects);
|
|
129
105
|
}
|
|
130
106
|
}
|
package/tsconfig.json
CHANGED
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"extends": "../../tsconfig.lib.json",
|
|
3
3
|
"compilerOptions": {
|
|
4
|
+
"rootDir": "src",
|
|
5
|
+
"baseUrl": ".",
|
|
4
6
|
"outDir": "dist",
|
|
7
|
+
"paths": {
|
|
8
|
+
"@lib/*": ["src/*"]
|
|
9
|
+
},
|
|
5
10
|
"esModuleInterop": false
|
|
6
11
|
},
|
|
7
12
|
"include": [
|
|
8
13
|
"src/**/*",
|
|
9
14
|
],
|
|
10
15
|
"exclude": [
|
|
11
|
-
"jest.config.
|
|
12
|
-
"
|
|
13
|
-
"**/*.test.ts",
|
|
14
|
-
"lib/**/*"
|
|
16
|
+
"jest.config.js",
|
|
17
|
+
"dist/**/*"
|
|
15
18
|
]
|
|
16
19
|
}
|
package/dist/error.d.ts
DELETED
package/dist/error.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"error.d.ts","sourceRoot":"","sources":["../src/error.ts"],"names":[],"mappings":"AAAA,wBAAgB,iBAAiB,UAEhC"}
|
package/dist/error.js
DELETED
package/src/error.ts
DELETED