cachette 3.0.1 → 4.0.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.
@@ -0,0 +1,303 @@
1
+ import { expect } from 'chai';
2
+ import * as sinon from 'sinon';
3
+
4
+ import { RedisCache, SIZE_THRESHOLD_WARNING_BYTES } from '../src/lib/RedisCache';
5
+
6
+
7
+ describe('RedisCache', () => {
8
+
9
+ describe('constructor', () => {
10
+ it('will not crash the application given an invalid Redis URL', async () => {
11
+ let cache = new RedisCache('redis://localhost:9999');
12
+ await cache.getValue('test');
13
+ await cache.setValue('test', 'value');
14
+ cache = new RedisCache('rediss://localhost:9999');
15
+ await cache.getValue('test');
16
+ await cache.setValue('test', 'value');
17
+ });
18
+
19
+ it('will raise an error if given a Redis URL without protocol', async () => {
20
+ expect(
21
+ () => new RedisCache('rer17kq3qdwc5wmy.4gzf3f.ng.0001.use1.cache.amazonaws.com'),
22
+ ).to.throw();
23
+ });
24
+
25
+ it('will raise an error if given a Redis URL with an invalid protocol', async () => {
26
+ expect(
27
+ () => new RedisCache('potato://rer17kq3qdwc5wmy.4gzf3f.ng.0001.use1.cache.amazonaws.com'),
28
+ ).to.throw();
29
+ });
30
+ });
31
+
32
+ describe('value serialization', () => {
33
+
34
+ it('can serialize the null value', () => {
35
+ let value = RedisCache.serializeValue(null);
36
+ expect(value).to.equal(RedisCache.NULL_VALUE);
37
+ value = RedisCache.deserializeValue(value);
38
+ expect(value).to.equal(null);
39
+ });
40
+
41
+ it('can serialize the true value', () => {
42
+ let value = RedisCache.serializeValue(true);
43
+ expect(value).to.equal(RedisCache.TRUE_VALUE);
44
+ value = RedisCache.deserializeValue(value);
45
+ expect(value).to.equal(true);
46
+ });
47
+
48
+ it('can serialize an object', () => {
49
+ const obj = {
50
+ level1: {
51
+ level2: {
52
+ level3: true,
53
+ },
54
+ },
55
+ };
56
+ let value = RedisCache.serializeValue(obj);
57
+ expect(value.startsWith(RedisCache.JSON_PREFIX)).to.be.true;
58
+ value = RedisCache.deserializeValue(value);
59
+ expect(value).to.deep.equal(obj);
60
+ });
61
+
62
+ it('can serialize an object with a nested map', () => {
63
+ const mapStructure: Map<string, {
64
+ checksum: number;
65
+ originCommentId?: string;
66
+ }> = new Map();
67
+ mapStructure.set('key1', { checksum: 1, originCommentId: 'c1' });
68
+ mapStructure.set('key2', { checksum: 2, originCommentId: 'c2' });
69
+ const obj = {
70
+ level1: {
71
+ level2: {
72
+ level3: {
73
+ l3Map: mapStructure,
74
+ l3Bool: true,
75
+ },
76
+ l2Map: mapStructure,
77
+ },
78
+ l1Map: mapStructure,
79
+ },
80
+ };
81
+ let value = RedisCache.serializeValue(obj);
82
+ expect(value.startsWith(RedisCache.JSON_PREFIX)).to.be.true;
83
+ value = RedisCache.deserializeValue(value);
84
+ expect(value).to.deep.equal(obj);
85
+ });
86
+
87
+ it('can serialize an object with a nested set', () => {
88
+ const setStructure: Set<string> = new Set();
89
+ setStructure.add('key1');
90
+ setStructure.add('key2');
91
+ const obj = {
92
+ level1: {
93
+ level2: {
94
+ level3: {
95
+ l3Set: setStructure,
96
+ l3Bool: false,
97
+ },
98
+ l2Set: setStructure,
99
+ },
100
+ l1Set: setStructure,
101
+ },
102
+ };
103
+ let value = RedisCache.serializeValue(obj);
104
+ expect(value.startsWith(RedisCache.JSON_PREFIX)).to.be.true;
105
+ value = RedisCache.deserializeValue(value);
106
+ expect(value).to.deep.equal(obj);
107
+ });
108
+
109
+ it('can serialize the false value', () => {
110
+ let value = RedisCache.serializeValue(false);
111
+ expect(value).to.equal(RedisCache.FALSE_VALUE);
112
+ value = RedisCache.deserializeValue(value);
113
+ expect(value).to.equal(false);
114
+ });
115
+
116
+ it('will leave a string untouched', () => {
117
+ let value = RedisCache.serializeValue('string');
118
+ expect(value).to.equal('string');
119
+ value = RedisCache.deserializeValue('string');
120
+ expect(value).to.equal('string');
121
+ });
122
+
123
+ it('will convert null to undefined when deserializing', () => {
124
+ const value = RedisCache.deserializeValue(null);
125
+ expect(value).to.equal(undefined);
126
+ });
127
+
128
+ });
129
+
130
+ describe('setValue', async () => {
131
+ it('can set values', async function (): Promise<void> {
132
+ if (!process.env.TEST_REDIS_URL) {
133
+ this.skip();
134
+ }
135
+
136
+ const cache = new RedisCache(process.env.TEST_REDIS_URL as string);
137
+ await cache.isReady();
138
+
139
+ // Just to be sure that the cache is really empty...
140
+ await cache.clear();
141
+
142
+ const wasSet = await cache.setValue('key', 'value');
143
+ expect(wasSet).to.be.true;
144
+ expect(await cache.itemCount()).to.equal(1);
145
+ const value = await cache.getValue('key');
146
+ expect(value).to.equal('value');
147
+ expect(await cache.itemCount()).to.equal(1);
148
+ });
149
+
150
+ it('can set/get numbers', async function (): Promise<void> {
151
+ if (!process.env.TEST_REDIS_URL) {
152
+ this.skip();
153
+ }
154
+
155
+ const cache = new RedisCache(process.env.TEST_REDIS_URL as string);
156
+ await cache.isReady();
157
+ await cache.clear();
158
+
159
+ await cache.setValue('numZero', 0);
160
+ expect(await cache.getValue('numZero')).to.equal(0);
161
+
162
+ await cache.setValue('numFloat', 123.456);
163
+ expect(await cache.getValue('numFloat')).to.equal(123.456);
164
+
165
+ await cache.setValue('numNegative', -99);
166
+ expect(await cache.getValue('numNegative')).to.equal(-99);
167
+
168
+ await cache.setValue('numMax', Number.MAX_SAFE_INTEGER);
169
+ expect(await cache.getValue('numMax')).to.equal(Number.MAX_SAFE_INTEGER);
170
+
171
+ await cache.setValue('numInfinity', Infinity);
172
+ expect(await cache.getValue('numInfinity')).to.equal(Infinity);
173
+
174
+ await cache.setValue('numBarf', 0.1 + 0.2); // 0.30000000000000004, IEEE754
175
+ expect(await cache.getValue('numBarf')).to.equal(0.1 + 0.2);
176
+ });
177
+
178
+ it('can set values with a TTL', async function (): Promise<void> {
179
+ if (!process.env.TEST_REDIS_URL) {
180
+ this.skip();
181
+ }
182
+
183
+ const cache = new RedisCache(process.env.TEST_REDIS_URL as string);
184
+ await cache.isReady();
185
+
186
+ // Just to be sure that the cache is really empty...
187
+ await cache.clear();
188
+
189
+ const wasSet = await cache.setValue('key', 'value', 30000);
190
+ expect(wasSet).to.be.true;
191
+
192
+ const value = await cache.getValue('key');
193
+ expect(value).to.equal('value');
194
+
195
+ expect(await cache.itemCount()).to.equal(1);
196
+
197
+ const ttl = await cache.getTtl('key');
198
+ expect(ttl).to.exist;
199
+ expect(ttl).to.be.above(0);
200
+ expect(ttl).to.not.be.above(30000000);
201
+ });
202
+
203
+ it('can set a boolean value', async function (): Promise<void> {
204
+ if (!process.env.TEST_REDIS_URL) {
205
+ this.skip();
206
+ }
207
+
208
+ const cache = new RedisCache(process.env.TEST_REDIS_URL as string);
209
+ await cache.isReady();
210
+
211
+ // Just to be sure that the cache is really empty...
212
+ await cache.clear();
213
+
214
+ let wasSet = await cache.setValue('key', true);
215
+ expect(wasSet).to.be.true;
216
+ let value = await cache.getValue('key');
217
+ expect(value).to.be.true;
218
+
219
+ wasSet = await cache.setValue('key', false);
220
+ expect(wasSet).to.be.true;
221
+ value = await cache.getValue('key');
222
+ expect(value).to.be.false;
223
+
224
+ expect(await cache.itemCount()).to.equal(1);
225
+ });
226
+
227
+ it('emits a warning when setting a large-enough value', async function () {
228
+ if (!process.env.TEST_REDIS_URL) {
229
+ this.skip();
230
+ }
231
+
232
+ const cache = new RedisCache(process.env.TEST_REDIS_URL as string);
233
+ const warnSpy = sinon.spy();
234
+ cache.on('warn', warnSpy);
235
+ await cache.isReady();
236
+
237
+ const key = `emits-warning-on-large-value-${Math.random()}`;
238
+ await cache.setValue(key, 'a'.repeat(SIZE_THRESHOLD_WARNING_BYTES));
239
+
240
+ const warningsAfterSetLargeKey = warnSpy.getCalls().map(c => c.firstArg).filter(msg => msg.includes('Writing large value to Redis!'));
241
+ expect(warningsAfterSetLargeKey.length).to.equal(1);
242
+ });
243
+
244
+ it('does NOT emit a warning when setting a small-enough value', async function () {
245
+ if (!process.env.TEST_REDIS_URL) {
246
+ this.skip();
247
+ }
248
+
249
+ const cache = new RedisCache(process.env.TEST_REDIS_URL as string);
250
+ const warnSpy = sinon.spy();
251
+ cache.on('warn', warnSpy);
252
+ await cache.isReady();
253
+
254
+ const key = `doesnt-emit-warning-on-small-value-${Math.random()}`;
255
+ await cache.setValue(key, 'a'.repeat(SIZE_THRESHOLD_WARNING_BYTES - 1));
256
+
257
+ const warningsAfterSetLargeKey = warnSpy.getCalls().map(c => c.firstArg).filter(msg => msg.includes('Writing large value to Redis!'));
258
+ expect(warningsAfterSetLargeKey.length).to.equal(0);
259
+ });
260
+ });
261
+
262
+ describe('itemCount', async () => {
263
+
264
+ it('can count the items in the redis cache.', async function (): Promise<void> {
265
+ if (!process.env.TEST_REDIS_URL) {
266
+ this.skip();
267
+ }
268
+
269
+ const cache = new RedisCache(process.env.TEST_REDIS_URL as string);
270
+ await cache.isReady();
271
+
272
+ // Just to be sure that the cache is really empty...
273
+ await cache.clear();
274
+
275
+ await cache.setValue('test1', 'value1');
276
+ await cache.setValue('test2', 'value2');
277
+ await cache.setValue('test3', 'value3');
278
+ expect(await cache.itemCount()).to.equal(3);
279
+ await cache.clear();
280
+ expect(await cache.itemCount()).to.equal(0);
281
+ });
282
+ });
283
+
284
+ describe('waitForReplication', () => {
285
+ it('wait for replication', async function (): Promise<void> {
286
+ if (!process.env.TEST_REDIS_URL) {
287
+ this.skip();
288
+ }
289
+
290
+ const cache = new RedisCache(process.env.TEST_REDIS_URL as string);
291
+ await cache.isReady();
292
+
293
+ await cache.clear();
294
+
295
+ await cache.setValue('test1', 'value1');
296
+
297
+ const replicationAcknowledged = await cache.waitForReplication(0, 50);
298
+
299
+ // No replicas so we expect 0. This test basically confirms that waitForReplication doesn't crash. 🤷‍♂️
300
+ expect(replicationAcknowledged).to.equal(0);
301
+ });
302
+ });
303
+ });
@@ -0,0 +1,306 @@
1
+ import { expect } from 'chai';
2
+ import * as sinon from 'sinon';
3
+
4
+ import { WriteThroughCache, LocalCache } from '../src/';
5
+
6
+ function makeFakeWriteThroughCache(): WriteThroughCache {
7
+ const cache = new WriteThroughCache('redis://localhost:9999');
8
+ cache['redisCacheForWriting'] = new LocalCache();
9
+ cache['redisCacheForWriting'].getTtl = async () => 1;
10
+ cache['redisCacheForReading'] = cache['redisCacheForWriting'];
11
+ return cache;
12
+ }
13
+
14
+
15
+ describe('WriteThroughCache', () => {
16
+
17
+ it('will fallback to using a local cache is no connection to redis is made', async () => {
18
+
19
+ const cache = new WriteThroughCache('redis://localhost:9999');
20
+
21
+ // We know the connection was not made, but we should still be able to use the local cache.
22
+ await cache.setValue('key', 'value');
23
+ const value = await cache.getValue('key');
24
+ expect(value).to.equal('value');
25
+
26
+ });
27
+
28
+ describe('setValue', () => {
29
+
30
+ it('will write the value in both caches', async () => {
31
+
32
+ const cache = makeFakeWriteThroughCache();
33
+
34
+ const response = await cache.setValue('key', 'value');
35
+ expect(response).to.be.true;
36
+
37
+ let value = await cache.getValue('key');
38
+ expect(value).to.equal('value');
39
+
40
+ value = await cache['localCache'].getValue('key');
41
+ expect(value).to.equal('value');
42
+
43
+ value = await cache['redisCacheForReading'].getValue('key');
44
+ expect(value).to.equal('value');
45
+
46
+ });
47
+
48
+ });
49
+
50
+ describe('getValue', () => {
51
+
52
+ it('will populate the local cache with right ttl when fetching from redis', async function (): Promise<void> {
53
+
54
+ if (!process.env.TEST_REDIS_URL) {
55
+ this.skip();
56
+ }
57
+ const cache = new WriteThroughCache(process.env.TEST_REDIS_URL as string);
58
+ const spy = sinon.spy(cache['localCache'], 'setValue');
59
+
60
+ // await for Redis connection to be up
61
+ await cache.isReady();
62
+
63
+ await cache['redisCacheForWriting'].setValue('key', 'value', 100);
64
+
65
+ let value = await cache.getValue('key');
66
+ expect(value).to.equal('value');
67
+ sinon.assert.calledWith(spy, 'key', 'value', sinon.match(ttl => ttl > 99.9 && ttl <= 100));
68
+
69
+ value = await cache['localCache'].getValue('key');
70
+ expect(value).to.equal('value');
71
+
72
+ });
73
+
74
+ it('will get directly from the local cache if available', async () => {
75
+
76
+ const cache = makeFakeWriteThroughCache();
77
+
78
+ await cache['localCache'].setValue('key', 'value');
79
+
80
+ let value = await cache.getValue('key');
81
+ expect(value).to.equal('value');
82
+
83
+ value = await cache['redisCacheForReading'].getValue('key');
84
+ expect(value).not.to.exist;
85
+
86
+ });
87
+
88
+ it('will populate the local cache for a null value', async () => {
89
+
90
+ const cache = makeFakeWriteThroughCache();
91
+
92
+ await cache['redisCacheForWriting'].setValue('key', null);
93
+
94
+ let value = await cache.getValue('key');
95
+ expect(value).to.equal(null);
96
+
97
+ value = await cache['localCache'].getValue('key');
98
+ expect(value).to.equal(null);
99
+
100
+ });
101
+
102
+ it('will populate the local cache for an empty string', async () => {
103
+
104
+ const cache = makeFakeWriteThroughCache();
105
+
106
+ await cache['redisCacheForWriting'].setValue('key', '');
107
+
108
+ let value = await cache.getValue('key');
109
+ expect(value).to.equal('');
110
+
111
+ value = await cache['localCache'].getValue('key');
112
+ expect(value).to.equal('');
113
+
114
+ });
115
+
116
+ it('returns nothing if value in cache has expired', async function (): Promise<void> {
117
+ if (!process.env.TEST_REDIS_URL) {
118
+ this.skip();
119
+ }
120
+ const cache = new WriteThroughCache(process.env.TEST_REDIS_URL as string);
121
+ await cache.setValue('fakeKey', 'fakeValue', 0.1);
122
+ // sleep 100 ms
123
+ await new Promise(resolve => setTimeout(resolve, 150));
124
+ const fakeValue = await cache.getValue('fakeKey');
125
+ expect(fakeValue).to.be.undefined;
126
+ });
127
+
128
+ });
129
+
130
+ describe('delValue', () => {
131
+
132
+ it('will delete values from both caches', async () => {
133
+
134
+ const cache = makeFakeWriteThroughCache();
135
+
136
+ await cache.setValue('key', 'value');
137
+
138
+ let value = await cache.getValue('key');
139
+ expect(value).to.equal('value');
140
+
141
+ value = await cache['localCache'].getValue('key');
142
+ expect(value).to.equal('value');
143
+
144
+ await cache.delValue('key');
145
+
146
+ value = await cache.getValue('key');
147
+ expect(value).not.to.exist;
148
+
149
+ value = await cache['localCache'].getValue('key');
150
+ expect(value).not.to.exist;
151
+
152
+ });
153
+
154
+ });
155
+
156
+ describe('clear', () => {
157
+
158
+ it('clears from both caches', async () => {
159
+ const cache = makeFakeWriteThroughCache();
160
+
161
+ const base = [...Array(10).keys()];
162
+ await Promise.all(base.map(i => cache.setValue(`key${i}`, i)));
163
+ expect(await cache.itemCount()).to.equal(20);
164
+
165
+ let values = await Promise.all(base.map(i => cache.getValue(`key${i}`)));
166
+ expect(values.sort()).to.eql(base);
167
+
168
+ await cache.clear();
169
+ expect(await cache.itemCount()).to.equal(0);
170
+
171
+ values = await Promise.all(base.map(i => cache.getValue(`key${i}`)));
172
+ values.forEach(value => expect(value).to.be.undefined);
173
+ });
174
+
175
+ });
176
+
177
+ describe('metrics', () => {
178
+ let sinonClock: sinon.SinonFakeTimers;
179
+
180
+ beforeEach(async () => {
181
+ sinonClock = sinon.useFakeTimers({
182
+ now: 0,
183
+ toFake: ['setInterval'],
184
+ });
185
+ });
186
+
187
+ afterEach(() => {
188
+ delete process.env.CACHETTE_METRICS_PERIOD_MINUTES;
189
+ sinonClock.restore();
190
+ })
191
+
192
+ it('stores metrics if env. var is set', async function () {
193
+ if (!process.env.TEST_REDIS_URL) {
194
+ this.skip();
195
+ }
196
+ process.env.CACHETTE_METRICS_PERIOD_MINUTES = '1';
197
+ const cache = new WriteThroughCache(process.env.TEST_REDIS_URL as string);
198
+ await cache.isReady();
199
+ expect(cache['metrics'].enabled).to.be.true;
200
+ const key = `key-metrics-${Math.random()}`;
201
+
202
+ // Getting bogus value to test doubleMisses
203
+ await cache.getValue(`non-existing-value-${Math.random()}`);
204
+ expect(cache['metrics'].localHits).to.equal(0);
205
+ expect(cache['metrics'].redisHits).to.equal(0);
206
+ expect(cache['metrics'].doubleMisses).to.equal(1);
207
+
208
+ // Setting in *Local* + *Redis* caches to test localHits
209
+ await cache.setValue(key, 'val');
210
+ await cache.getValue(key);
211
+ expect(cache['metrics'].localHits).to.equal(1);
212
+ expect(cache['metrics'].redisHits).to.equal(0);
213
+ expect(cache['metrics'].doubleMisses).to.equal(1);
214
+
215
+ // Evicting from private *Local* cache to test redisHits
216
+ await cache['localCache'].delValue(key);
217
+ await cache.getValue(key);
218
+ expect(cache['metrics'].localHits).to.equal(1);
219
+ expect(cache['metrics'].redisHits).to.equal(1);
220
+ expect(cache['metrics'].doubleMisses).to.equal(1);
221
+ });
222
+
223
+ it('does not store metrics by default', async function () {
224
+ if (!process.env.TEST_REDIS_URL) {
225
+ this.skip();
226
+ }
227
+ // *Not* setting process.env.CACHETTE_METRICS_PERIOD_MINUTES
228
+ const cache = new WriteThroughCache(process.env.TEST_REDIS_URL as string);
229
+ await cache.isReady();
230
+ const key = `key-metrics-${Math.random()}`;
231
+
232
+ // Causing activity that would have caused { localHits, redisHits, doubleMisses }
233
+ await cache.getValue(`non-existing-value-${Math.random()}`);
234
+ await cache.setValue(key, 'val');
235
+ await cache.getValue(key);
236
+ await cache['localCache'].delValue(key);
237
+ await cache.getValue(key);
238
+ expect(cache['metrics'].enabled).to.be.false;
239
+ expect(cache['metrics'].localHits).to.equal(0);
240
+ expect(cache['metrics'].redisHits).to.equal(0);
241
+ expect(cache['metrics'].doubleMisses).to.equal(0);
242
+ });
243
+
244
+ it('does not store metrics if env. var is invalid', async function () {
245
+ if (!process.env.TEST_REDIS_URL) {
246
+ this.skip();
247
+ }
248
+ process.env.CACHETTE_METRICS_PERIOD_MINUTES = 'foo'; // invalid: not a positive integer
249
+ const cache = new WriteThroughCache(process.env.TEST_REDIS_URL as string);
250
+ await cache.isReady();
251
+ const key = `key-metrics-${Math.random()}`;
252
+
253
+ // Causing activity that would have caused { localHits, redisHits, doubleMisses }
254
+ await cache.getValue(`non-existing-value-${Math.random()}`);
255
+ await cache.setValue(key, 'val');
256
+ await cache.getValue(key);
257
+ await cache['localCache'].delValue(key);
258
+ await cache.getValue(key);
259
+ expect(cache['metrics'].enabled).to.be.false;
260
+ expect(cache['metrics'].localHits).to.equal(0);
261
+ expect(cache['metrics'].redisHits).to.equal(0);
262
+ expect(cache['metrics'].doubleMisses).to.equal(0);
263
+ });
264
+
265
+ it('emits metrics periodically', async function () {
266
+ if (!process.env.TEST_REDIS_URL) {
267
+ this.skip();
268
+ }
269
+
270
+ process.env.CACHETTE_METRICS_PERIOD_MINUTES = '1';
271
+ const cache = new WriteThroughCache(process.env.TEST_REDIS_URL as string);
272
+ const infoSpy = sinon.spy();
273
+ cache.on('info', infoSpy);
274
+ await cache.isReady();
275
+
276
+ // Causing 1 localHit, 1 redisHit, 1 doubleMiss
277
+ const key = `key-metrics-${Math.random()}`;
278
+ await cache.getValue(`non-existing-value-${Math.random()}`);
279
+ await cache.setValue(key, 'val');
280
+ await cache.getValue(key);
281
+ await cache['localCache'].delValue(key);
282
+ await cache.getValue(key);
283
+
284
+ // Testing we emitted at requested period
285
+ const metricsReports0 = infoSpy.getCalls().map(c => c.firstArg).filter(msg => msg.includes('metrics'));
286
+ expect(metricsReports0.length).to.equal(0);
287
+
288
+ sinonClock.tick(59999); // t = 1min minus a millisecond
289
+ const metricsReports59s = infoSpy.getCalls().map(c => c.firstArg).filter(msg => msg.includes('metrics'));
290
+ expect(metricsReports59s.length).to.equal(0);
291
+
292
+ sinonClock.tick(2); // t = 1min plus a millisecond
293
+ const metricsReports61s = infoSpy.getCalls().map(c => c.firstArg).filter(msg => msg.includes('metrics'));
294
+ expect(metricsReports61s.length).to.equal(1);
295
+ expect(metricsReports61s[0]).to.equal('WriteThroughCache metrics during last 1 min - Total: 3, Local hits: 1 (33%), Redis hits: 1 (33%), Double misses: 1 (33%).')
296
+
297
+ // Causing no activity for a minute and testing we emit again
298
+ sinonClock.tick(60000); // t = 2min plus a millisecond
299
+ const metricsReports121s = infoSpy.getCalls().map(c => c.firstArg).filter(msg => msg.includes('metrics'));
300
+ expect(metricsReports121s.length).to.equal(2);
301
+ expect(metricsReports121s[1]).to.equal('WriteThroughCache metrics during last 1 min - Total: 0, Local hits: 0 (0%), Redis hits: 0 (0%), Double misses: 0 (0%).')
302
+ });
303
+
304
+ });
305
+
306
+ });
@@ -0,0 +1,12 @@
1
+ 'use strict';
2
+
3
+ // https://mochajs.org/#configuring-mocha-nodejs
4
+ // https://mochajs.org/#configuration-format
5
+ // eslint-disable-next-line no-undef
6
+ module.exports = {
7
+ require: ['source-map-support/register'],
8
+ recursive: true,
9
+ exit: true,
10
+ timeout: 60000,
11
+ 'no-colors': true,
12
+ };
@@ -0,0 +1,10 @@
1
+ 'use strict';
2
+
3
+ // https://mochajs.org/#configuring-mocha-nodejs
4
+ // https://mochajs.org/#configuration-format
5
+ // eslint-disable-next-line no-undef
6
+ module.exports = {
7
+ require: ['source-map-support/register'],
8
+ recursive: true,
9
+ exit: true,
10
+ };
package/tsconfig.json ADDED
@@ -0,0 +1,22 @@
1
+ {
2
+ "compilerOptions": {
3
+ "baseUrl": ".",
4
+ "declaration": true,
5
+ "experimentalDecorators": true,
6
+ "incremental": true,
7
+ "lib": ["ES2023"],
8
+ "listFiles": false,
9
+ "module": "commonjs",
10
+ "moduleResolution": "node",
11
+ "noUnusedLocals": true,
12
+ "outDir": "dist",
13
+ "rootDir": ".",
14
+ "sourceMap": true,
15
+ "strictNullChecks": true,
16
+ "target": "ES2022"
17
+ },
18
+ "include": [
19
+ "src/**/*",
20
+ "test/**/*"
21
+ ]
22
+ }