cradova 1.0.0
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/LICENSE +201 -0
- package/cradova.png +0 -0
- package/docs/README.md +0 -0
- package/index.d.ts +52 -0
- package/index.js +342 -0
- package/index.ts +366 -0
- package/package.json +36 -0
- package/scripts/JsonDB.d.ts +298 -0
- package/scripts/JsonDB.js +698 -0
- package/scripts/JsonDB.ts +794 -0
- package/scripts/Metrics.d.ts +51 -0
- package/scripts/Metrics.js +56 -0
- package/scripts/Metrics.ts +66 -0
- package/scripts/Router.d.ts +12 -0
- package/scripts/Router.js +95 -0
- package/scripts/Router.ts +105 -0
- package/scripts/Screen.d.ts +11 -0
- package/scripts/Screen.js +62 -0
- package/scripts/Screen.ts +62 -0
- package/scripts/animate.d.ts +25 -0
- package/scripts/animate.js +47 -0
- package/scripts/animate.ts +57 -0
- package/scripts/css.d.ts +20 -0
- package/scripts/css.js +41 -0
- package/scripts/css.ts +46 -0
- package/scripts/dispatcher.d.ts +1 -0
- package/scripts/dispatcher.js +57 -0
- package/scripts/dispatcher.ts +58 -0
- package/scripts/file-system.d.ts +2 -0
- package/scripts/file-system.js +175 -0
- package/scripts/file-system.ts +177 -0
- package/scripts/fullscreen.d.ts +4 -0
- package/scripts/fullscreen.js +29 -0
- package/scripts/fullscreen.ts +30 -0
- package/scripts/init.d.ts +2 -0
- package/scripts/init.js +17 -0
- package/scripts/init.ts +18 -0
- package/scripts/localStorage.d.ts +9 -0
- package/scripts/localStorage.js +26 -0
- package/scripts/localStorage.ts +37 -0
- package/scripts/media.d.ts +22 -0
- package/scripts/media.js +48 -0
- package/scripts/media.ts +51 -0
- package/scripts/reuse.ts +74 -0
- package/scripts/speaker.d.ts +2 -0
- package/scripts/speaker.js +16 -0
- package/scripts/speaker.ts +25 -0
- package/scripts/store.d.ts +10 -0
- package/scripts/store.js +56 -0
- package/scripts/store.ts +47 -0
- package/scripts/swipe.d.ts +9 -0
- package/scripts/swipe.js +113 -0
- package/scripts/swipe.ts +129 -0
- package/scripts/widget.d.ts +2 -0
- package/scripts/widget.js +19 -0
- package/scripts/widget.ts +23 -0
- package/service-worker.d.ts +2 -0
- package/service-worker.js +40 -0
- package/service-worker.ts +52 -0
- package/tsconfig.json +11 -0
|
@@ -0,0 +1,794 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* JSON DB DataBase MIT Licence © 2022
|
|
3
|
+
* ************************************
|
|
4
|
+
* Created by Friday Candour @uiedbooker
|
|
5
|
+
* email > fridaymaxtour@gmail.com
|
|
6
|
+
* github > www.github.com/FridayCandour
|
|
7
|
+
* telegram > @uiedbooker
|
|
8
|
+
* JSONDB @version 1.0.0
|
|
9
|
+
* */
|
|
10
|
+
|
|
11
|
+
export const JSONDBversion = "1.0.0";
|
|
12
|
+
let fs: unknown,
|
|
13
|
+
fileURLToPath,
|
|
14
|
+
isNode = false,
|
|
15
|
+
_dirname: unknown;
|
|
16
|
+
if (!globalThis.localStorage) {
|
|
17
|
+
isNode = true;
|
|
18
|
+
fs = await import("fs");
|
|
19
|
+
const dr = await import("path");
|
|
20
|
+
const fp = await import("url");
|
|
21
|
+
fileURLToPath = fp.fileURLToPath;
|
|
22
|
+
_dirname = dr
|
|
23
|
+
.dirname(fileURLToPath(import.meta.url))
|
|
24
|
+
.split("node_modules")[0];
|
|
25
|
+
}
|
|
26
|
+
const schema = class {
|
|
27
|
+
base_name: string;
|
|
28
|
+
name: string;
|
|
29
|
+
last_index: number;
|
|
30
|
+
columns: string;
|
|
31
|
+
relations: string | null;
|
|
32
|
+
constructor(
|
|
33
|
+
schema_configuration_object: Record<string, string>,
|
|
34
|
+
validators: {
|
|
35
|
+
validateColumns: (arg0: string) => void;
|
|
36
|
+
validateRelations: (arg0: string) => void;
|
|
37
|
+
}
|
|
38
|
+
) {
|
|
39
|
+
// validations
|
|
40
|
+
if (!schema_configuration_object.columns) {
|
|
41
|
+
throw new Error(
|
|
42
|
+
"JSONDB: can't create an empty table should have some columns"
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
validators.validateColumns(schema_configuration_object.columns);
|
|
46
|
+
|
|
47
|
+
const isEmptyObject = function (obj: any) {
|
|
48
|
+
// for checking for empty objects
|
|
49
|
+
for (const name in obj) {
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
return true;
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
if (
|
|
56
|
+
schema_configuration_object.relations &&
|
|
57
|
+
!isEmptyObject(schema_configuration_object.relations)
|
|
58
|
+
) {
|
|
59
|
+
validators.validateRelations(schema_configuration_object.relations);
|
|
60
|
+
}
|
|
61
|
+
// assignment
|
|
62
|
+
this.base_name = "";
|
|
63
|
+
this.name = schema_configuration_object.name;
|
|
64
|
+
this.last_index = -1;
|
|
65
|
+
this.columns = schema_configuration_object.columns;
|
|
66
|
+
this.relations = schema_configuration_object.relations || null;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
class JSONDBTableWrapper {
|
|
71
|
+
put: (name: string, value: any) => Promise<void>;
|
|
72
|
+
get: (name: string) => Promise<unknown>;
|
|
73
|
+
validator: (incoming: { [x: string]: any }, tables: string | any[]) => {};
|
|
74
|
+
self: any;
|
|
75
|
+
keys: any;
|
|
76
|
+
constructor(self: unknown, keys: any) {
|
|
77
|
+
this.put = async (name: string, value: any) => {
|
|
78
|
+
function cb(err: string) {
|
|
79
|
+
if (err) {
|
|
80
|
+
throw new Error(
|
|
81
|
+
"JSONDB: error failed to update entities in database because " + err
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
if (isNode) {
|
|
86
|
+
fs.writeFile(name + ".json", JSON.stringify(value), cb);
|
|
87
|
+
} else {
|
|
88
|
+
localStorage.setItem(name, JSON.stringify(value));
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
this.get = async (name: any) => {
|
|
92
|
+
return new Promise(function (res, rej) {
|
|
93
|
+
try {
|
|
94
|
+
if (!isNode) {
|
|
95
|
+
res(JSON.parse(localStorage.getItem(name)));
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
fs.readFile(
|
|
100
|
+
_dirname + "/" + name + ".json",
|
|
101
|
+
{ encoding: "utf-8" },
|
|
102
|
+
function (err: string, data: string) {
|
|
103
|
+
if (err) {
|
|
104
|
+
return rej(
|
|
105
|
+
"JSONDB: error failed to retrieve entities from database because " +
|
|
106
|
+
err
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
res(JSON.parse(data));
|
|
111
|
+
} catch (error) {
|
|
112
|
+
try {
|
|
113
|
+
res(JSON.parse(fs.readFileSync(name + ".json", "utf-8")));
|
|
114
|
+
} catch (error) {}
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
);
|
|
118
|
+
} catch (error) {}
|
|
119
|
+
});
|
|
120
|
+
};
|
|
121
|
+
this.validator = (
|
|
122
|
+
incoming: { [x: string]: any },
|
|
123
|
+
tables: string | any[]
|
|
124
|
+
) => {
|
|
125
|
+
// works for type, nulllable and unique validations.
|
|
126
|
+
const outgoing: Record<string, string> = {};
|
|
127
|
+
for (const prop in this.self.columns) {
|
|
128
|
+
if (
|
|
129
|
+
this.self.columns[prop].nullable !== true &&
|
|
130
|
+
!Object.hasOwnProperty.call(incoming, prop)
|
|
131
|
+
) {
|
|
132
|
+
throw new Error(
|
|
133
|
+
"JSONDB: error failed to validate incoming data because " +
|
|
134
|
+
prop +
|
|
135
|
+
" is required for " +
|
|
136
|
+
this.self.name +
|
|
137
|
+
" Schema"
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (
|
|
142
|
+
!this.self.columns[prop].nullable &&
|
|
143
|
+
typeof incoming[prop] !== this.self.columns[prop].type
|
|
144
|
+
) {
|
|
145
|
+
throw new Error(
|
|
146
|
+
"JSONDB: error failed to validate incoming data because " +
|
|
147
|
+
prop +
|
|
148
|
+
"'s value " +
|
|
149
|
+
incoming[prop] +
|
|
150
|
+
" has got a wrong data type of " +
|
|
151
|
+
typeof incoming[prop] +
|
|
152
|
+
" for " +
|
|
153
|
+
this.self.name +
|
|
154
|
+
" should be " +
|
|
155
|
+
this.self.columns[prop].type +
|
|
156
|
+
" type instead"
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (this.self.columns[prop].unique === true) {
|
|
161
|
+
for (let i = 0; i < tables.length; i++) {
|
|
162
|
+
const element = tables[i];
|
|
163
|
+
if (element[prop] === incoming[prop]) {
|
|
164
|
+
throw new Error(
|
|
165
|
+
"JSONDB: error failed to validate incoming data because " +
|
|
166
|
+
prop +
|
|
167
|
+
" is unique for " +
|
|
168
|
+
this.self.name +
|
|
169
|
+
" Schema can't have more than one instance"
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// cleaning time
|
|
176
|
+
outgoing[prop] = incoming[prop];
|
|
177
|
+
}
|
|
178
|
+
return outgoing;
|
|
179
|
+
};
|
|
180
|
+
this.self = self;
|
|
181
|
+
this.keys = keys;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Save with relations
|
|
186
|
+
* ---------------------
|
|
187
|
+
* @type .saveWithRelations(target table, schema, schema | schema[]) => Promise(object)
|
|
188
|
+
* @example
|
|
189
|
+
* // single relation
|
|
190
|
+
await PollTable.saveWithRelations(MessageTable, Poll, message);
|
|
191
|
+
// arrays of relations
|
|
192
|
+
await PollTable.saveWithRelations(MessageTable, Poll, allMessages);
|
|
193
|
+
*/
|
|
194
|
+
async saveWithRelations(
|
|
195
|
+
table: { self: { name: string | number } },
|
|
196
|
+
incoming: { index: string | number },
|
|
197
|
+
relations: string | any[]
|
|
198
|
+
) {
|
|
199
|
+
if (!relations) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
const db = await this.get(this.self.base_name);
|
|
203
|
+
db.last_access_time = Date();
|
|
204
|
+
if (incoming && typeof incoming.index !== "number") {
|
|
205
|
+
throw new Error("JsonDB: save before saving with relations");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
db.tables[this.self.name][incoming.index] = incoming;
|
|
209
|
+
if (relations && Array.isArray(relations)) {
|
|
210
|
+
for (let i = 0; i < relations.length; i++) {
|
|
211
|
+
if (db.Entities[this.self.name].relations[table.self.name]) {
|
|
212
|
+
if (
|
|
213
|
+
db.Entities[this.self.name].relations[table.self.name].type ===
|
|
214
|
+
"many"
|
|
215
|
+
) {
|
|
216
|
+
db.tables[this.self.name][incoming.index].relations[
|
|
217
|
+
table.self.name
|
|
218
|
+
] = !db.tables[this.self.name][incoming.index].relations[
|
|
219
|
+
table.self.name
|
|
220
|
+
]
|
|
221
|
+
? [relations[i]]
|
|
222
|
+
: [
|
|
223
|
+
...db.tables[this.self.name][incoming.index].relations[
|
|
224
|
+
table.self.name
|
|
225
|
+
],
|
|
226
|
+
relations[i],
|
|
227
|
+
];
|
|
228
|
+
} else {
|
|
229
|
+
db.tables[this.self.name][incoming.index].relations[
|
|
230
|
+
table.self.name
|
|
231
|
+
] = relations[i];
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
} else {
|
|
236
|
+
if (relations) {
|
|
237
|
+
if (db.Entities[this.self.name].relations[table.self.name]) {
|
|
238
|
+
if (
|
|
239
|
+
db.Entities[this.self.name].relations[table.self.name].type ===
|
|
240
|
+
"many"
|
|
241
|
+
) {
|
|
242
|
+
db.tables[this.self.name][incoming.index].relations[
|
|
243
|
+
table.self.name
|
|
244
|
+
] = !db.tables[this.self.name][incoming.index].relations[
|
|
245
|
+
table.self.name
|
|
246
|
+
]
|
|
247
|
+
? [relations]
|
|
248
|
+
: [
|
|
249
|
+
...db.tables[this.self.name][incoming.index].relations[
|
|
250
|
+
table.self.name
|
|
251
|
+
],
|
|
252
|
+
relations,
|
|
253
|
+
];
|
|
254
|
+
} else {
|
|
255
|
+
db.tables[this.self.name][incoming.index].relations[
|
|
256
|
+
table.self.name
|
|
257
|
+
] = relations;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
await this.put(this.self.base_name, db);
|
|
263
|
+
return db.tables[this.self.name][incoming.index];
|
|
264
|
+
}
|
|
265
|
+
/**
|
|
266
|
+
* Save table into a Jsondb instance
|
|
267
|
+
* -----------------------------
|
|
268
|
+
* @type .save(schema)=> Promise(object)
|
|
269
|
+
* @example
|
|
270
|
+
await PollTable.save(poll)
|
|
271
|
+
*/
|
|
272
|
+
async save(incoming: { index: number; relations: {} }) {
|
|
273
|
+
// db.tables[this.self.name] = db.tables[this.self.name].sort(
|
|
274
|
+
// (a, b) => a.index - b.index
|
|
275
|
+
// );
|
|
276
|
+
const db = await this.get(this.self.base_name);
|
|
277
|
+
db.last_access_time = Date();
|
|
278
|
+
if (typeof incoming.index !== "number") {
|
|
279
|
+
incoming = this.validator(incoming, db.tables[this.self.name]);
|
|
280
|
+
if (this.self.relations && !incoming.relations) {
|
|
281
|
+
incoming.relations = {};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
db.Entities[this.self.name].last_index += 1;
|
|
285
|
+
incoming.index = db.Entities[this.self.name].last_index;
|
|
286
|
+
db.tables[this.self.name].push(incoming);
|
|
287
|
+
} else {
|
|
288
|
+
db.tables[this.self.name][incoming.index] = incoming;
|
|
289
|
+
}
|
|
290
|
+
await this.put(this.self.base_name, db);
|
|
291
|
+
return incoming;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* Save table into a Jsondb instance
|
|
295
|
+
* -----------------------------
|
|
296
|
+
* @type .remove(schema)=> Promise(object)
|
|
297
|
+
* @example
|
|
298
|
+
await PollTable.remove(poll)
|
|
299
|
+
*/
|
|
300
|
+
async remove(entity: { index: string | number }) {
|
|
301
|
+
const db = await this.get(this.self.base_name);
|
|
302
|
+
db.last_access_time = Date();
|
|
303
|
+
// db.tables[this.self.name].splice(entity.index, 1);
|
|
304
|
+
db.tables[this.self.name][entity.index] = null;
|
|
305
|
+
await this.put(this.self.base_name, db);
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Save table into a Jsondb instance
|
|
309
|
+
* -----------------------------
|
|
310
|
+
* @type .count(schema)=> Promise(number)
|
|
311
|
+
* @example
|
|
312
|
+
await PollTable.count(poll)
|
|
313
|
+
*/
|
|
314
|
+
async count() {
|
|
315
|
+
const db = await this.get(this.self.base_name);
|
|
316
|
+
db.last_access_time = Date();
|
|
317
|
+
return db.tables[this.self.name].length;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Save table into a Jsondb instance
|
|
321
|
+
* -----------------------------
|
|
322
|
+
* @type .getAll()=> Promise(object[])
|
|
323
|
+
* @example
|
|
324
|
+
await PollTable.getAll()
|
|
325
|
+
*/
|
|
326
|
+
async getAll() {
|
|
327
|
+
const db = await this.get(this.self.base_name);
|
|
328
|
+
db.last_access_time = Date();
|
|
329
|
+
return db.tables[this.self.name];
|
|
330
|
+
}
|
|
331
|
+
/**
|
|
332
|
+
* get entities with any of the values specifiled from a Jsondb instance
|
|
333
|
+
* -----------------------------
|
|
334
|
+
* @type .getWhereAny({prop: value}, number | undefind)=> Promise(object)
|
|
335
|
+
* @example
|
|
336
|
+
await PollTable.getWhereAny({name: "friday", age: 121, class: "senior"}) // gets all
|
|
337
|
+
await PollTable.getWhereAny({email: "fridaymaxtour@gmail.com"}, 2) // gets 2 if they are up to two
|
|
338
|
+
*/
|
|
339
|
+
async getWhereAny(
|
|
340
|
+
props: { [s: string]: unknown } | ArrayLike<unknown>,
|
|
341
|
+
number: number
|
|
342
|
+
) {
|
|
343
|
+
const results = [];
|
|
344
|
+
let all;
|
|
345
|
+
const db = await this.get(this.self.base_name);
|
|
346
|
+
db.last_access_time = Date();
|
|
347
|
+
all = db.tables[this.self.name];
|
|
348
|
+
for (let i = 0; i < all.length; i++) {
|
|
349
|
+
const element = all[i];
|
|
350
|
+
for (const [k, v] of Object.entries(props)) {
|
|
351
|
+
if (element[k] && element[k] === v) {
|
|
352
|
+
results.push(element);
|
|
353
|
+
if (typeof number === "number" && results.length === number) {
|
|
354
|
+
return results;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return results;
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* get entities with the given prop of type "string" where the values specifiled is included
|
|
364
|
+
* -----------------------------
|
|
365
|
+
* @type .getWhereAnyPropsIncludes({prop: value}, number | undefind)=> Promise(object)
|
|
366
|
+
*
|
|
367
|
+
* @example prop must be type string!
|
|
368
|
+
*
|
|
369
|
+
await PollTable.getWhereAnyPropsIncludes({name: "fri"}) // gets all
|
|
370
|
+
await PollTable.getWhereAnyPropsIncludes({name: "fri"}, 2) // gets 2 if they are up to two
|
|
371
|
+
*/
|
|
372
|
+
async getWhereAnyPropsIncludes(
|
|
373
|
+
props: { [s: string]: unknown } | ArrayLike<unknown>,
|
|
374
|
+
number: number
|
|
375
|
+
) {
|
|
376
|
+
const results = [];
|
|
377
|
+
let all;
|
|
378
|
+
const db = await this.get(this.self.base_name);
|
|
379
|
+
db.last_access_time = Date();
|
|
380
|
+
all = db.tables[this.self.name];
|
|
381
|
+
for (let i = 0; i < all.length; i++) {
|
|
382
|
+
const element = all[i];
|
|
383
|
+
for (const [k, v] of Object.entries(props)) {
|
|
384
|
+
if (element[k] && typeof v === "string" && element[k].includes(v)) {
|
|
385
|
+
results.push(element);
|
|
386
|
+
if (typeof number === "number" && results.length === number) {
|
|
387
|
+
return results;
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return results;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/**
|
|
396
|
+
* get an entity with the values specifiled from a Jsondb instance
|
|
397
|
+
* -----------------------------
|
|
398
|
+
* @type .getOne({prop: value})=> Promise(object)
|
|
399
|
+
* @example
|
|
400
|
+
|
|
401
|
+
await PollTable.getOne({email: "fridaymaxtour@gamail.com"}) // gets one
|
|
402
|
+
|
|
403
|
+
*/
|
|
404
|
+
async getOne(props: { [s: string]: unknown } | ArrayLike<unknown>) {
|
|
405
|
+
let results = null;
|
|
406
|
+
const db = await this.get(this.self.base_name);
|
|
407
|
+
db.last_access_time = Date();
|
|
408
|
+
const all = db.tables[this.self.name];
|
|
409
|
+
for (let i = 0; i < all.length; i++) {
|
|
410
|
+
const element = all[i];
|
|
411
|
+
for (const [k, v] of Object.entries(props)) {
|
|
412
|
+
if (element[k] && element[k] === v) {
|
|
413
|
+
results = element;
|
|
414
|
+
break;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
return results;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
const JSONDBConnection = class {
|
|
423
|
+
Entities: any;
|
|
424
|
+
keys: any;
|
|
425
|
+
constructor(Entities: any, keys?: any) {
|
|
426
|
+
this.Entities = Entities;
|
|
427
|
+
this.keys = keys;
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
/**
|
|
431
|
+
* Get a table from JSONDB
|
|
432
|
+
*------------------------
|
|
433
|
+
* @example
|
|
434
|
+
*
|
|
435
|
+
*
|
|
436
|
+
const details = {
|
|
437
|
+
password: "password",
|
|
438
|
+
username: "jsondb_username",
|
|
439
|
+
};
|
|
440
|
+
// getting connection instance into JSONDB
|
|
441
|
+
const connection = await database.createJSONDBConnection(details);
|
|
442
|
+
// getting a table
|
|
443
|
+
const MessageTable = connection.getTable("Message");
|
|
444
|
+
* */
|
|
445
|
+
getTable(table_name: string) {
|
|
446
|
+
for (const [tableName, table] of Object.entries(this.Entities)) {
|
|
447
|
+
if (table_name === tableName) {
|
|
448
|
+
return new JSONDBTableWrapper(table, this.keys);
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
};
|
|
453
|
+
|
|
454
|
+
/**
|
|
455
|
+
* Create a new JSONDB object
|
|
456
|
+
*------------------------
|
|
457
|
+
* @class
|
|
458
|
+
|
|
459
|
+
* const database = new JSONDB()
|
|
460
|
+
*
|
|
461
|
+
* Creates a new JSONDB object
|
|
462
|
+
*
|
|
463
|
+
* .
|
|
464
|
+
* */
|
|
465
|
+
|
|
466
|
+
class JSONDB {
|
|
467
|
+
DB_NAME: string;
|
|
468
|
+
username: string;
|
|
469
|
+
encrypted: boolean;
|
|
470
|
+
initialised: boolean;
|
|
471
|
+
time_created: string;
|
|
472
|
+
version: string;
|
|
473
|
+
last_access_time: string;
|
|
474
|
+
visuality: string;
|
|
475
|
+
Entities: {};
|
|
476
|
+
tables: {};
|
|
477
|
+
password: string;
|
|
478
|
+
constructor() {
|
|
479
|
+
this.DB_NAME = "";
|
|
480
|
+
this.username = "";
|
|
481
|
+
this.encrypted = false;
|
|
482
|
+
this.initialised = false;
|
|
483
|
+
this.time_created = Date();
|
|
484
|
+
this.version = JSONDBversion;
|
|
485
|
+
this.last_access_time = "";
|
|
486
|
+
this.visuality = "";
|
|
487
|
+
this.Entities = {};
|
|
488
|
+
this.tables = {};
|
|
489
|
+
this.password = "";
|
|
490
|
+
}
|
|
491
|
+
async getDB(name: string) {
|
|
492
|
+
return new Promise(function (res, rej) {
|
|
493
|
+
if (!isNode) {
|
|
494
|
+
res(JSON.parse(localStorage.getItem(name)));
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
try {
|
|
499
|
+
fs.readFile(
|
|
500
|
+
_dirname + "/" + name + ".json",
|
|
501
|
+
{ encoding: "utf-8" },
|
|
502
|
+
function (err: any, data: string) {
|
|
503
|
+
if (err) {
|
|
504
|
+
return rej(err);
|
|
505
|
+
}
|
|
506
|
+
try {
|
|
507
|
+
res(JSON.parse(data));
|
|
508
|
+
} catch (error) {
|
|
509
|
+
try {
|
|
510
|
+
res(JSON.parse(fs.readFileSync(name + ".json", "utf-8")));
|
|
511
|
+
} catch (error) {}
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
);
|
|
515
|
+
} catch (error) {}
|
|
516
|
+
});
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/**
|
|
520
|
+
* Schema constructor for Jsondb
|
|
521
|
+
* -----------------------------
|
|
522
|
+
*
|
|
523
|
+
* name @type string
|
|
524
|
+
*
|
|
525
|
+
* columns @type object {
|
|
526
|
+
*
|
|
527
|
+
* type > @type any of number > string > boolean > blob and must be specified
|
|
528
|
+
*
|
|
529
|
+
* nullable @type bolean true > false default false
|
|
530
|
+
*
|
|
531
|
+
* unique @type bolean true > false default false
|
|
532
|
+
*
|
|
533
|
+
* }
|
|
534
|
+
*
|
|
535
|
+
* relations @type object {
|
|
536
|
+
*
|
|
537
|
+
* target: entity schema @type object,
|
|
538
|
+
*
|
|
539
|
+
* attachment_name: @type string,
|
|
540
|
+
*
|
|
541
|
+
* type : @type string should be "many" or "one"
|
|
542
|
+
*
|
|
543
|
+
* }
|
|
544
|
+
*
|
|
545
|
+
*
|
|
546
|
+
*
|
|
547
|
+
* @example
|
|
548
|
+
*
|
|
549
|
+
* const MessageSchema = database.schema({
|
|
550
|
+
name: "Message",
|
|
551
|
+
columns: {
|
|
552
|
+
vote: {
|
|
553
|
+
type: "number",
|
|
554
|
+
},
|
|
555
|
+
time: {
|
|
556
|
+
type: "string",
|
|
557
|
+
nullable: true,
|
|
558
|
+
},
|
|
559
|
+
value: {
|
|
560
|
+
type: "string",
|
|
561
|
+
},
|
|
562
|
+
},
|
|
563
|
+
});
|
|
564
|
+
*
|
|
565
|
+
* const PollSchema = new JSONDB.schema({
|
|
566
|
+
name: "Poll",
|
|
567
|
+
columns: {
|
|
568
|
+
value: {
|
|
569
|
+
type: "varchar",
|
|
570
|
+
},
|
|
571
|
+
},
|
|
572
|
+
relations: {
|
|
573
|
+
Message: {
|
|
574
|
+
target: Message,
|
|
575
|
+
type: "many-to-one",
|
|
576
|
+
},
|
|
577
|
+
},
|
|
578
|
+
});
|
|
579
|
+
*/
|
|
580
|
+
|
|
581
|
+
schema(schema_configuration_object: Record<string, string>) {
|
|
582
|
+
return new schema(schema_configuration_object, {
|
|
583
|
+
validateColumns: this.validateColumns,
|
|
584
|
+
validateRelations: this.validateRelations,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
/**
|
|
588
|
+
* Create a new JSONDB instance
|
|
589
|
+
*------------------------
|
|
590
|
+
* @example
|
|
591
|
+
* // creates a JSONDB object
|
|
592
|
+
* const Database = new JSONDB()
|
|
593
|
+
* // database configuration object
|
|
594
|
+
* const config = {
|
|
595
|
+
DB_NAME: "my db",
|
|
596
|
+
password: "password",
|
|
597
|
+
username: "jsondb_username",
|
|
598
|
+
encrypted: false,
|
|
599
|
+
}
|
|
600
|
+
// Creates a new JSONDB instance
|
|
601
|
+
* Database.init(config)
|
|
602
|
+
* */
|
|
603
|
+
init(config: {
|
|
604
|
+
name: string;
|
|
605
|
+
password: string;
|
|
606
|
+
username: string;
|
|
607
|
+
encrypted: boolean;
|
|
608
|
+
}) {
|
|
609
|
+
console.log(`\x1B[32m JSONDB version ${JSONDBversion} \x1B[39m`);
|
|
610
|
+
this.initialised = true;
|
|
611
|
+
this.DB_NAME = config.name;
|
|
612
|
+
this.password = config.password || "";
|
|
613
|
+
this.username = config.username || "";
|
|
614
|
+
this.encrypted = config.encrypted || false;
|
|
615
|
+
this.time_created = Date();
|
|
616
|
+
this.tables = {};
|
|
617
|
+
try {
|
|
618
|
+
let wasThere;
|
|
619
|
+
if (isNode) {
|
|
620
|
+
wasThere = fs.readFileSync(config.name + ".json", "utf-8");
|
|
621
|
+
} else {
|
|
622
|
+
wasThere = localStorage.getItem(config.name);
|
|
623
|
+
}
|
|
624
|
+
if (wasThere) {
|
|
625
|
+
return;
|
|
626
|
+
}
|
|
627
|
+
} catch (error) {}
|
|
628
|
+
if (!config.password) {
|
|
629
|
+
throw new Error("JSONDB: error password is empty ");
|
|
630
|
+
}
|
|
631
|
+
if (!config.username) {
|
|
632
|
+
throw new Error("JSONDB: error username is empty ");
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function cb(err: string) {
|
|
636
|
+
if (err) {
|
|
637
|
+
throw new Error(
|
|
638
|
+
"JSONDB: error failed to create database because " + err
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
if (isNode) {
|
|
643
|
+
fs.writeFile(config.name + ".json", JSON.stringify(this), cb);
|
|
644
|
+
} else {
|
|
645
|
+
let db = JSON.stringify(this);
|
|
646
|
+
localStorage.setItem(config.name, db);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
|
|
650
|
+
/**
|
|
651
|
+
* Create secure connection a Jsondb instance
|
|
652
|
+
* -----------------------------
|
|
653
|
+
* @example
|
|
654
|
+
*
|
|
655
|
+
* const details = {
|
|
656
|
+
password: "password",
|
|
657
|
+
username: "jsondb_username",
|
|
658
|
+
};
|
|
659
|
+
const connection = await database.createJSONDBConnection(details);
|
|
660
|
+
*/
|
|
661
|
+
|
|
662
|
+
async createJSONDBConnection(details: { username: any; password: any }) {
|
|
663
|
+
if (!this.initialised) {
|
|
664
|
+
throw new Error("JSONDB: you haven't create a JSONDB instance yet");
|
|
665
|
+
}
|
|
666
|
+
if (
|
|
667
|
+
details.username !== this.username ||
|
|
668
|
+
details.password !== this.password
|
|
669
|
+
) {
|
|
670
|
+
throw new Error("JSONDB: Access Denied");
|
|
671
|
+
}
|
|
672
|
+
const connection = await this.getDB(this.DB_NAME);
|
|
673
|
+
connection.last_access_time = Date();
|
|
674
|
+
|
|
675
|
+
return new JSONDBConnection(connection.Entities);
|
|
676
|
+
}
|
|
677
|
+
validateRelations(relations: { [s: string]: unknown } | ArrayLike<unknown>) {
|
|
678
|
+
const types = ["many", "one"];
|
|
679
|
+
for (const [relation, value] of Object.entries(relations)) {
|
|
680
|
+
if (typeof value.target !== "object") {
|
|
681
|
+
throw new Error(
|
|
682
|
+
"JSONDB: wrong relationship target type given " +
|
|
683
|
+
value.target +
|
|
684
|
+
" should be object only"
|
|
685
|
+
);
|
|
686
|
+
}
|
|
687
|
+
if (!types.includes(value.type)) {
|
|
688
|
+
throw new Error(
|
|
689
|
+
"JSONDB: wrong relationship type given " +
|
|
690
|
+
value.type +
|
|
691
|
+
" should be many or one"
|
|
692
|
+
);
|
|
693
|
+
}
|
|
694
|
+
if (value.cascade && typeof value.cascade !== "boolean") {
|
|
695
|
+
throw new Error(
|
|
696
|
+
"JSONDB: wrong cascade value given " +
|
|
697
|
+
value.cascade +
|
|
698
|
+
" should be true or false"
|
|
699
|
+
);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
validateColumns(columns: { [s: string]: unknown } | ArrayLike<unknown>) {
|
|
704
|
+
const types = ["number", "string", "boolean", "blob"];
|
|
705
|
+
for (const [column, value] of Object.entries(columns)) {
|
|
706
|
+
if (column) {
|
|
707
|
+
if (!types.includes(value.type)) {
|
|
708
|
+
throw new Error(
|
|
709
|
+
"JSONDB: wrong data type given " +
|
|
710
|
+
value.type +
|
|
711
|
+
" only number, string, boolean and blob are accepted"
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
if (value.unique && typeof value.unique !== "boolean") {
|
|
715
|
+
throw new Error(
|
|
716
|
+
"JSONDB: wrong unique value given " +
|
|
717
|
+
value.unique +
|
|
718
|
+
" should be true or false"
|
|
719
|
+
);
|
|
720
|
+
}
|
|
721
|
+
if (value.nullable && typeof value.nullable !== "boolean") {
|
|
722
|
+
throw new Error(
|
|
723
|
+
"JSONDB: wrong nullable value given " +
|
|
724
|
+
value.nullable +
|
|
725
|
+
" should be true or false"
|
|
726
|
+
);
|
|
727
|
+
}
|
|
728
|
+
}
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* Assemble Entities into Jsondb
|
|
734
|
+
* -----------------------------
|
|
735
|
+
* @example
|
|
736
|
+
*
|
|
737
|
+
* const MessageSchema = database.schema({
|
|
738
|
+
name: "Message",
|
|
739
|
+
columns: {
|
|
740
|
+
vote: {
|
|
741
|
+
type: "number",
|
|
742
|
+
},
|
|
743
|
+
time: {
|
|
744
|
+
type: "string",
|
|
745
|
+
nullable: true,
|
|
746
|
+
},
|
|
747
|
+
value: {
|
|
748
|
+
type: "string",
|
|
749
|
+
},
|
|
750
|
+
},
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
database.assemble([MessageSchema]);
|
|
754
|
+
*
|
|
755
|
+
*/
|
|
756
|
+
|
|
757
|
+
assemble(allEntities: string | any[]) {
|
|
758
|
+
if (!this.initialised) {
|
|
759
|
+
throw new Error("JSONDB: you haven't create a JSONDB instance yet");
|
|
760
|
+
}
|
|
761
|
+
try {
|
|
762
|
+
const wasThere = fs.readFileSync(this.DB_NAME + ".json", "utf-8");
|
|
763
|
+
if (wasThere) {
|
|
764
|
+
return;
|
|
765
|
+
}
|
|
766
|
+
} catch (error) {}
|
|
767
|
+
if (!Array.isArray(allEntities) || typeof allEntities[0] !== "object") {
|
|
768
|
+
throw new Error("JSONDB: invalid entity array list, can't be assembled");
|
|
769
|
+
}
|
|
770
|
+
for (let i = 0; i < allEntities.length; i++) {
|
|
771
|
+
this.Entities[allEntities[i].name] = allEntities[i];
|
|
772
|
+
this.Entities[allEntities[i].name].base_name = this.DB_NAME;
|
|
773
|
+
this.tables[allEntities[i].name] = [];
|
|
774
|
+
}
|
|
775
|
+
function cb(err: string) {
|
|
776
|
+
if (err) {
|
|
777
|
+
throw new Error(
|
|
778
|
+
"JSONDB: error failed to assemble entities into database because " +
|
|
779
|
+
err
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
if (isNode) {
|
|
784
|
+
fs.writeFile(this.DB_NAME + ".json", JSON.stringify(this), cb);
|
|
785
|
+
} else {
|
|
786
|
+
localStorage.setItem(this.DB_NAME, JSON.stringify(this));
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* @exports
|
|
793
|
+
*/
|
|
794
|
+
export default JSONDB;
|