source: imaps-frontend/node_modules/keyv/src/index.js@ d565449

main
Last change on this file since d565449 was d565449, checked in by stefan toskovski <stefantoska84@…>, 4 weeks ago

Update repo after prototype presentation

  • Property mode set to 100644
File size: 6.4 KB
Line 
1'use strict';
2
3const EventEmitter = require('events');
4const JSONB = require('json-buffer');
5
6const loadStore = options => {
7 const adapters = {
8 redis: '@keyv/redis',
9 rediss: '@keyv/redis',
10 mongodb: '@keyv/mongo',
11 mongo: '@keyv/mongo',
12 sqlite: '@keyv/sqlite',
13 postgresql: '@keyv/postgres',
14 postgres: '@keyv/postgres',
15 mysql: '@keyv/mysql',
16 etcd: '@keyv/etcd',
17 offline: '@keyv/offline',
18 tiered: '@keyv/tiered',
19 };
20 if (options.adapter || options.uri) {
21 const adapter = options.adapter || /^[^:+]*/.exec(options.uri)[0];
22 return new (require(adapters[adapter]))(options);
23 }
24
25 return new Map();
26};
27
28const iterableAdapters = [
29 'sqlite',
30 'postgres',
31 'mysql',
32 'mongo',
33 'redis',
34 'tiered',
35];
36
37class Keyv extends EventEmitter {
38 constructor(uri, {emitErrors = true, ...options} = {}) {
39 super();
40 this.opts = {
41 namespace: 'keyv',
42 serialize: JSONB.stringify,
43 deserialize: JSONB.parse,
44 ...((typeof uri === 'string') ? {uri} : uri),
45 ...options,
46 };
47
48 if (!this.opts.store) {
49 const adapterOptions = {...this.opts};
50 this.opts.store = loadStore(adapterOptions);
51 }
52
53 if (this.opts.compression) {
54 const compression = this.opts.compression;
55 this.opts.serialize = compression.serialize.bind(compression);
56 this.opts.deserialize = compression.deserialize.bind(compression);
57 }
58
59 if (typeof this.opts.store.on === 'function' && emitErrors) {
60 this.opts.store.on('error', error => this.emit('error', error));
61 }
62
63 this.opts.store.namespace = this.opts.namespace;
64
65 const generateIterator = iterator => async function * () {
66 for await (const [key, raw] of typeof iterator === 'function'
67 ? iterator(this.opts.store.namespace)
68 : iterator) {
69 const data = await this.opts.deserialize(raw);
70 if (this.opts.store.namespace && !key.includes(this.opts.store.namespace)) {
71 continue;
72 }
73
74 if (typeof data.expires === 'number' && Date.now() > data.expires) {
75 this.delete(key);
76 continue;
77 }
78
79 yield [this._getKeyUnprefix(key), data.value];
80 }
81 };
82
83 // Attach iterators
84 if (typeof this.opts.store[Symbol.iterator] === 'function' && this.opts.store instanceof Map) {
85 this.iterator = generateIterator(this.opts.store);
86 } else if (typeof this.opts.store.iterator === 'function' && this.opts.store.opts
87 && this._checkIterableAdaptar()) {
88 this.iterator = generateIterator(this.opts.store.iterator.bind(this.opts.store));
89 }
90 }
91
92 _checkIterableAdaptar() {
93 return iterableAdapters.includes(this.opts.store.opts.dialect)
94 || iterableAdapters.findIndex(element => this.opts.store.opts.url.includes(element)) >= 0;
95 }
96
97 _getKeyPrefix(key) {
98 return `${this.opts.namespace}:${key}`;
99 }
100
101 _getKeyPrefixArray(keys) {
102 return keys.map(key => `${this.opts.namespace}:${key}`);
103 }
104
105 _getKeyUnprefix(key) {
106 return key
107 .split(':')
108 .splice(1)
109 .join(':');
110 }
111
112 get(key, options) {
113 const {store} = this.opts;
114 const isArray = Array.isArray(key);
115 const keyPrefixed = isArray ? this._getKeyPrefixArray(key) : this._getKeyPrefix(key);
116 if (isArray && store.getMany === undefined) {
117 const promises = [];
118 for (const key of keyPrefixed) {
119 promises.push(Promise.resolve()
120 .then(() => store.get(key))
121 .then(data => (typeof data === 'string') ? this.opts.deserialize(data) : (this.opts.compression ? this.opts.deserialize(data) : data))
122 .then(data => {
123 if (data === undefined || data === null) {
124 return undefined;
125 }
126
127 if (typeof data.expires === 'number' && Date.now() > data.expires) {
128 return this.delete(key).then(() => undefined);
129 }
130
131 return (options && options.raw) ? data : data.value;
132 }),
133 );
134 }
135
136 return Promise.allSettled(promises)
137 .then(values => {
138 const data = [];
139 for (const value of values) {
140 data.push(value.value);
141 }
142
143 return data;
144 });
145 }
146
147 return Promise.resolve()
148 .then(() => isArray ? store.getMany(keyPrefixed) : store.get(keyPrefixed))
149 .then(data => (typeof data === 'string') ? this.opts.deserialize(data) : (this.opts.compression ? this.opts.deserialize(data) : data))
150 .then(data => {
151 if (data === undefined || data === null) {
152 return undefined;
153 }
154
155 if (isArray) {
156 return data.map((row, index) => {
157 if ((typeof row === 'string')) {
158 row = this.opts.deserialize(row);
159 }
160
161 if (row === undefined || row === null) {
162 return undefined;
163 }
164
165 if (typeof row.expires === 'number' && Date.now() > row.expires) {
166 this.delete(key[index]).then(() => undefined);
167 return undefined;
168 }
169
170 return (options && options.raw) ? row : row.value;
171 });
172 }
173
174 if (typeof data.expires === 'number' && Date.now() > data.expires) {
175 return this.delete(key).then(() => undefined);
176 }
177
178 return (options && options.raw) ? data : data.value;
179 });
180 }
181
182 set(key, value, ttl) {
183 const keyPrefixed = this._getKeyPrefix(key);
184 if (typeof ttl === 'undefined') {
185 ttl = this.opts.ttl;
186 }
187
188 if (ttl === 0) {
189 ttl = undefined;
190 }
191
192 const {store} = this.opts;
193
194 return Promise.resolve()
195 .then(() => {
196 const expires = (typeof ttl === 'number') ? (Date.now() + ttl) : null;
197 if (typeof value === 'symbol') {
198 this.emit('error', 'symbol cannot be serialized');
199 }
200
201 value = {value, expires};
202 return this.opts.serialize(value);
203 })
204 .then(value => store.set(keyPrefixed, value, ttl))
205 .then(() => true);
206 }
207
208 delete(key) {
209 const {store} = this.opts;
210 if (Array.isArray(key)) {
211 const keyPrefixed = this._getKeyPrefixArray(key);
212 if (store.deleteMany === undefined) {
213 const promises = [];
214 for (const key of keyPrefixed) {
215 promises.push(store.delete(key));
216 }
217
218 return Promise.allSettled(promises)
219 .then(values => values.every(x => x.value === true));
220 }
221
222 return Promise.resolve()
223 .then(() => store.deleteMany(keyPrefixed));
224 }
225
226 const keyPrefixed = this._getKeyPrefix(key);
227 return Promise.resolve()
228 .then(() => store.delete(keyPrefixed));
229 }
230
231 clear() {
232 const {store} = this.opts;
233 return Promise.resolve()
234 .then(() => store.clear());
235 }
236
237 has(key) {
238 const keyPrefixed = this._getKeyPrefix(key);
239 const {store} = this.opts;
240 return Promise.resolve()
241 .then(async () => {
242 if (typeof store.has === 'function') {
243 return store.has(keyPrefixed);
244 }
245
246 const value = await store.get(keyPrefixed);
247 return value !== undefined;
248 });
249 }
250
251 disconnect() {
252 const {store} = this.opts;
253 if (typeof store.disconnect === 'function') {
254 return store.disconnect();
255 }
256 }
257}
258
259module.exports = Keyv;
Note: See TracBrowser for help on using the repository browser.