-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
DB.js
146 lines (128 loc) · 4.13 KB
/
DB.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
import { existsSync } from 'fs';
import { struct } from 'superstruct';
import Immutable from 'immutable';
import { File } from './adapters/File';
import { LocalStorage } from './adapters/LocalStorage';
import { IMMUTABLE_READ, IMMUTABLE_TYPE, IMMUTABLE_WRITE, JAYSN_PATH } from './constants';
export { JAYSN_PATH } from './constants';
export class DB {
constructor(schema, options = {}) {
const defaultOpts = {
use: 'File',
source: JAYSN_PATH,
};
const defaultData = Immutable.Map(
Object.keys(schema).reduce((V, K) => {
const result = V;
result[K] = {};
return result;
}, {})
).toJS();
// Merge options with defaultOptions
const opts = Immutable.fromJS(defaultOpts).merge(Immutable.fromJS(options)).toJS();
// Let's pick the specific adapter
switch (opts.use) {
case 'LocalStorage':
if (typeof localStorage === 'undefined' || localStorage === null) {
/* eslint-disable */
const NodeLocalStorage = require('node-localstorage').LocalStorage;
global.localStorage = new NodeLocalStorage(opts.source);
/* eslint-enable */
}
this.adapter = new LocalStorage(opts.source);
if (!localStorage.getItem(opts.source)) {
this.adapter.write(defaultData);
}
break;
default:
this.adapter = new File(opts.source);
if (!existsSync(opts.source)) {
this.adapter.write(defaultData);
}
break;
}
// Restructured the schema to use optional
this.schema = Object.keys(schema).reduce((V, K) => {
const result = V;
const childrens = Object.keys(schema[K]).reduce((NV, NK) => {
const Nresult = NV;
Nresult[NK] = struct.optional(schema[K][NK]);
return Nresult;
}, {});
result[K] = childrens;
return result;
}, {});
// Add write prototype to Available Type
Object.keys(Immutable).reduce((V, K) => {
if (IMMUTABLE_TYPE.includes(K)) {
Immutable[K].prototype.write = () => {
const data = this.getState().toJS();
this._state.push(Immutable.fromJS(this.adapter.write(data)));
this._stateId += 1;
return this.getState();
};
}
return V;
}, {});
// Init state from the db
const initState = Immutable.fromJS(this.adapter.read());
this._state = [initState];
this._stateId = 0;
this.initiateImmutableRead();
this.initiateImmutableWrite();
}
initiateImmutableRead() {
// Return Latest State when using an Immutable Read Functions
IMMUTABLE_READ.forEach((method) => {
this[method] = (...args) => {
const state = this.getState();
this._state.push(Immutable.fromJS(this.adapter.read()));
this._stateId += 1;
return state[method](...args);
};
});
}
initiateImmutableWrite() {
// Return and push newState, then increase stateId when using an Immutable Write Functions
IMMUTABLE_WRITE.forEach((method) => {
this[method] = (...args) => {
const state = this.getState();
const newState = state[method](...args);
this._state.push(Immutable.fromJS(newState.toJS()));
this._stateId += 1;
const data = this.getState().toJS();
// Validate each data with superstruct
try {
Object.keys(data).forEach((O) => {
const V = data[O];
const L = V.length;
if (
typeof V === 'object' &&
V !== null &&
typeof V !== 'function' &&
typeof L === 'number' &&
L > -1 &&
L % 1 === 0 &&
L <= Number.MAX_SAFE_INTEGER + 1
) {
V.forEach((NV) => {
struct(this.schema[O])(NV);
});
} else {
struct(this.schema[O])(V);
}
});
} catch ({ message }) {
this._state.pop();
this._stateId -= 1;
throw new TypeError(message);
}
return this.getState();
};
});
}
getState() {
return this._state[this._stateId];
}
}
export default DB;