@animaj/ts-utils 0.0.0-stage → 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 +16 -0
- package/README.md +283 -2
- package/dist/array.d.ts +4 -0
- package/dist/array.js +19 -0
- package/dist/cache-manager/cache-manager.d.ts +19 -0
- package/dist/cache-manager/cache-manager.js +2 -0
- package/dist/cache-manager/in-memory.d.ts +35 -0
- package/dist/cache-manager/in-memory.js +121 -0
- package/dist/dates.d.ts +5 -0
- package/dist/dates.js +21 -0
- package/dist/dev.d.ts +20 -0
- package/dist/dev.js +123 -0
- package/dist/function.d.ts +3 -0
- package/dist/function.js +13 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +17 -0
- package/dist/string.d.ts +7 -0
- package/dist/string.js +47 -0
- package/dist/tests.d.ts +26 -0
- package/dist/tests.js +57 -0
- package/package.json +48 -5
package/LICENSE
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2020-2026 Julien Khong
|
|
4
|
+
Copyright (c) 2026 Animaj
|
|
5
|
+
|
|
6
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
7
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
8
|
+
copyright notice and this permission notice appear in all copies.
|
|
9
|
+
|
|
10
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
11
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
12
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
13
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
14
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
15
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
16
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,284 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @animaj/ts-utils
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Provides syntactic sugar and shortcuts to bullet-proof your TypeScript/JavaScript code.
|
|
4
|
+
|
|
5
|
+
## Contents
|
|
6
|
+
|
|
7
|
+
- [Install](#install)
|
|
8
|
+
- [DevUtils](#devutils) — `isSet` · `isNotSet` · `isAllSet` · `isOneNotSet` · `isEmpty` · `getFieldValue` · `getChildFieldValue` · `makeSingleton` · `debounce` · `getError`
|
|
9
|
+
- [StringUtils](#stringutils) — `isBlank` · `capitalize` · `uncapitalize` · `replaceTrailing` · `replaceLeading`
|
|
10
|
+
- [ArrayUtils](#arrayutils) — `isEmpty` · `getField`
|
|
11
|
+
- [DateUtils](#dateutils) — `addDays` · `addMinutes` · `addSeconds`
|
|
12
|
+
- [FunctionUtils](#functionutils) — `isAsync`
|
|
13
|
+
- [InMemory](#inmemory-cache) — TTL-based in-memory cache
|
|
14
|
+
- [TestUtils](#testutils) — async test helpers
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Install
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
npm install @animaj/ts-utils
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## DevUtils
|
|
27
|
+
|
|
28
|
+
### `isSet(value)`
|
|
29
|
+
Returns `true` if the value is not `null`, not `undefined`, and not the string `'undefined'`.
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { DevUtils } from '@animaj/ts-utils';
|
|
33
|
+
|
|
34
|
+
DevUtils.isSet(null); // false
|
|
35
|
+
DevUtils.isSet(undefined); // false
|
|
36
|
+
DevUtils.isSet('undefined'); // false
|
|
37
|
+
DevUtils.isSet(0); // true
|
|
38
|
+
DevUtils.isSet(''); // true
|
|
39
|
+
DevUtils.isSet(false); // true
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### `isNotSet(value)`
|
|
43
|
+
Inverse of `isSet`.
|
|
44
|
+
|
|
45
|
+
### `isAllSet(memberNames, data)` / `isOneNotSet(memberNames, data)`
|
|
46
|
+
Check if all (or at least one) named fields of an object are set.
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
DevUtils.isAllSet(['id', 'name'], { id: 1, name: 'foo' }); // true
|
|
50
|
+
DevUtils.isOneNotSet(['id', 'name'], { id: 1 }); // true
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### `isEmpty(value)`
|
|
54
|
+
Returns `true` for `null`, `undefined`, empty/blank strings, empty arrays, and empty objects.
|
|
55
|
+
|
|
56
|
+
```ts
|
|
57
|
+
DevUtils.isEmpty(null); // true
|
|
58
|
+
DevUtils.isEmpty(' '); // true
|
|
59
|
+
DevUtils.isEmpty([]); // true
|
|
60
|
+
DevUtils.isEmpty({}); // true
|
|
61
|
+
DevUtils.isEmpty([1]); // false
|
|
62
|
+
DevUtils.isEmpty({ a: 1 }); // false
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
### `getFieldValue(struct, fieldname, default)`
|
|
66
|
+
Returns the field value if set, otherwise the default.
|
|
67
|
+
|
|
68
|
+
```ts
|
|
69
|
+
DevUtils.getFieldValue({ age: 30 }, 'age', 0); // 30
|
|
70
|
+
DevUtils.getFieldValue({}, 'age', 0); // 0
|
|
71
|
+
DevUtils.getFieldValue(null, 'age', 0); // 0
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### `getChildFieldValue(struct, fieldnames, default)`
|
|
75
|
+
Traverses nested objects safely, returning the default if any level is missing.
|
|
76
|
+
|
|
77
|
+
```ts
|
|
78
|
+
const obj = { a: { b: { c: 'deep' } } };
|
|
79
|
+
DevUtils.getChildFieldValue(obj, ['a', 'b', 'c'], 'default'); // 'deep'
|
|
80
|
+
DevUtils.getChildFieldValue(obj, ['a', 'x', 'c'], 'default'); // 'default'
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### `makeSingleton(asyncFactory)`
|
|
84
|
+
Wraps an async factory so it is called only once. Subsequent calls return the cached instance.
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const getDb = DevUtils.makeSingleton(() => connectToDatabase());
|
|
88
|
+
await getDb(); // connects
|
|
89
|
+
await getDb(); // returns cached connection
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### `debounce(fn, waitMs)`
|
|
93
|
+
Returns a debounced version of `fn` that fires only after `waitMs` ms of inactivity.
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
const save = DevUtils.debounce(() => api.save(), 300);
|
|
97
|
+
save(); save(); save(); // only the last call fires after 300ms
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
### `getError(e)`
|
|
101
|
+
Normalizes any thrown value to an `Error` instance. Useful in `catch (e: unknown)` blocks.
|
|
102
|
+
|
|
103
|
+
```ts
|
|
104
|
+
try { ... } catch (e: unknown) {
|
|
105
|
+
const err = DevUtils.getError(e); // always an Error
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## StringUtils
|
|
112
|
+
|
|
113
|
+
```ts
|
|
114
|
+
import { StringUtils } from '@animaj/ts-utils';
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
| Method | Description |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `isBlank(str)` | `true` if string is null, undefined, or contains only whitespace |
|
|
120
|
+
| `capitalize(word)` | Uppercases the first character |
|
|
121
|
+
| `uncapitalize(word)` | Lowercases the first character |
|
|
122
|
+
| `replaceTrailing(value, char, replacement)` | Replaces trailing occurrences of `char` |
|
|
123
|
+
| `replaceLeading(value, char, replacement)` | Replaces leading occurrences of `char` |
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
StringUtils.capitalize('hello'); // 'Hello'
|
|
127
|
+
StringUtils.uncapitalize('World'); // 'world'
|
|
128
|
+
StringUtils.replaceTrailing('foo///', '/', ''); // 'foo'
|
|
129
|
+
StringUtils.replaceLeading('///foo', '/', ''); // 'foo'
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
---
|
|
133
|
+
|
|
134
|
+
## ArrayUtils
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import { ArrayUtils } from '@animaj/ts-utils';
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
| Method | Description |
|
|
141
|
+
|---|---|
|
|
142
|
+
| `isEmpty(array)` | `true` if array is null, undefined, or has no elements |
|
|
143
|
+
| `getField(array, index, fieldname, default)` | Safely reads a field from an element at a given index |
|
|
144
|
+
|
|
145
|
+
```ts
|
|
146
|
+
ArrayUtils.isEmpty([]); // true
|
|
147
|
+
ArrayUtils.getField([{ id: 1 }], 0, 'id', null); // 1
|
|
148
|
+
ArrayUtils.getField([], 0, 'id', null); // null
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
---
|
|
152
|
+
|
|
153
|
+
## DateUtils
|
|
154
|
+
|
|
155
|
+
```ts
|
|
156
|
+
import { DateUtils } from '@animaj/ts-utils';
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
| Method | Description |
|
|
160
|
+
|---|---|
|
|
161
|
+
| `addDays(date, n)` | Returns a new Date with `n` days added |
|
|
162
|
+
| `addMinutes(date, n)` | Returns a new Date with `n` minutes added |
|
|
163
|
+
| `addSeconds(date, n)` | Returns a new Date with `n` seconds added |
|
|
164
|
+
|
|
165
|
+
Accepts negative values to subtract. The original date is never mutated.
|
|
166
|
+
|
|
167
|
+
```ts
|
|
168
|
+
DateUtils.addDays(new Date(), 7); // one week from now
|
|
169
|
+
DateUtils.addSeconds(new Date(), -30); // 30 seconds ago
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
---
|
|
173
|
+
|
|
174
|
+
## FunctionUtils
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import { FunctionUtils } from '@animaj/ts-utils';
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
| Method | Description |
|
|
181
|
+
|---|---|
|
|
182
|
+
| `isAsync(fn)` | `true` if the function is async |
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
FunctionUtils.isAsync(async () => {}); // true
|
|
186
|
+
FunctionUtils.isAsync(() => {}); // false
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## InMemory (cache)
|
|
192
|
+
|
|
193
|
+
A TTL-based in-memory cache with per-entry TTL support and automatic background cleanup.
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import { InMemory } from '@animaj/ts-utils';
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
### Constructor options
|
|
200
|
+
|
|
201
|
+
| Option | Default | Description |
|
|
202
|
+
|---|---|---|
|
|
203
|
+
| `ttlInSec` | 3600 (1h) | Default TTL for entries |
|
|
204
|
+
| `autoTtlCleanIntervalInSec` | 3600 (1h) | How often expired entries are swept |
|
|
205
|
+
| `autoWipeAllIntervalInSec` | disabled | How often the entire cache is cleared (opt-in) |
|
|
206
|
+
|
|
207
|
+
### Methods
|
|
208
|
+
|
|
209
|
+
#### `add({ id, d, ttlInSec? })`
|
|
210
|
+
Adds or overwrites an entry. `ttlInSec` overrides the global default for this entry.
|
|
211
|
+
|
|
212
|
+
```ts
|
|
213
|
+
const cache = new InMemory<User>({ ttlInSec: 300 });
|
|
214
|
+
await cache.add({ id: 'u1', d: user });
|
|
215
|
+
await cache.add({ id: 'u2', d: user, ttlInSec: 60 }); // custom TTL
|
|
216
|
+
```
|
|
217
|
+
|
|
218
|
+
#### `fetch(id)`
|
|
219
|
+
Returns the cached value or `undefined` if missing or expired.
|
|
220
|
+
|
|
221
|
+
```ts
|
|
222
|
+
const user = await cache.fetch('u1'); // User | undefined
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
#### `fetchMultiple(ids)`
|
|
226
|
+
Splits a list of ids into cached hits and ids that still need to be fetched from the source.
|
|
227
|
+
|
|
228
|
+
```ts
|
|
229
|
+
const { cachedData, idsToRequest } = await cache.fetchMultiple(['u1', 'u2', 'u3']);
|
|
230
|
+
// cachedData → [{ id: 'u1', data: User }, ...]
|
|
231
|
+
// idsToRequest → ['u3']
|
|
232
|
+
|
|
233
|
+
const fresh = await api.getUsers(idsToRequest);
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
#### `remove(id)`
|
|
237
|
+
Removes a single entry immediately.
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
await cache.remove('u1');
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
#### `forceTtlClean()`
|
|
244
|
+
Triggers an immediate sweep to remove all expired entries.
|
|
245
|
+
|
|
246
|
+
#### `forceWipeAll()`
|
|
247
|
+
Clears all entries regardless of TTL.
|
|
248
|
+
|
|
249
|
+
#### `destroy()`
|
|
250
|
+
Stops background intervals and clears the cache. Call this when the cache instance is no longer needed to prevent timer leaks.
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
await cache.destroy();
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## TestUtils
|
|
259
|
+
|
|
260
|
+
Helpers for structuring async tests with setup / run / assert phases.
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
import { TestUtils } from '@animaj/ts-utils';
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
### `testExpectedSuccess({ setPreconditions, run, checkPostconditions })`
|
|
267
|
+
For testing a happy path. Returns `{ success: true }` or `{ success: false, error }`.
|
|
268
|
+
|
|
269
|
+
```ts
|
|
270
|
+
const result = await TestUtils.testExpectedSuccess({
|
|
271
|
+
setPreconditions: async () => buildInput(),
|
|
272
|
+
run: async (input) => myService.process(input),
|
|
273
|
+
checkPostconditions: async (output) => {
|
|
274
|
+
expect(output.status).toBe('ok');
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
expect(result.success).toBe(true);
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### `testExpectedError({ setPreconditions, run, checkError, checkPostconditions })`
|
|
281
|
+
For testing flows that return an error value (no exception thrown).
|
|
282
|
+
|
|
283
|
+
### `testExpectedException({ setPreconditions, run, checkError, checkPostconditions })`
|
|
284
|
+
For testing flows that are expected to throw.
|
package/dist/array.d.ts
ADDED
package/dist/array.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ArrayUtils = void 0;
|
|
4
|
+
const dev_1 = require("./dev");
|
|
5
|
+
class ArrayUtils {
|
|
6
|
+
static isEmpty(array) {
|
|
7
|
+
return dev_1.DevUtils.isEmpty(array);
|
|
8
|
+
}
|
|
9
|
+
static getField(array, indexValue, fieldname, defval) {
|
|
10
|
+
if (!Array.isArray(array)) {
|
|
11
|
+
return defval;
|
|
12
|
+
}
|
|
13
|
+
if (dev_1.DevUtils.isSet(array) && dev_1.DevUtils.isSet(array[indexValue]) && dev_1.DevUtils.isSet(array[indexValue][fieldname])) {
|
|
14
|
+
return array[indexValue][fieldname];
|
|
15
|
+
}
|
|
16
|
+
return defval;
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
exports.ArrayUtils = ArrayUtils;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface CacheManager<Data> {
|
|
2
|
+
fetch: (id: string) => Promise<Data | undefined>;
|
|
3
|
+
add: (o: {
|
|
4
|
+
id: string;
|
|
5
|
+
d: Data;
|
|
6
|
+
ttlInSec?: number;
|
|
7
|
+
}) => Promise<void>;
|
|
8
|
+
remove: (id: string) => Promise<void>;
|
|
9
|
+
fetchMultiple: (ids: string[]) => Promise<{
|
|
10
|
+
idsToRequest: string[];
|
|
11
|
+
cachedData: Array<{
|
|
12
|
+
id: string;
|
|
13
|
+
data: Data;
|
|
14
|
+
}>;
|
|
15
|
+
}>;
|
|
16
|
+
forceTtlClean: () => Promise<void>;
|
|
17
|
+
forceWipeAll: () => Promise<void>;
|
|
18
|
+
destroy: () => Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { CacheManager } from './cache-manager';
|
|
2
|
+
export declare class InMemory<Data> implements CacheManager<Data> {
|
|
3
|
+
private readonly cache;
|
|
4
|
+
private readonly ttlInSec;
|
|
5
|
+
private readonly autoTtlCleanIntervalInSec;
|
|
6
|
+
private autoTtlCleanIntervalHandle;
|
|
7
|
+
private readonly autoWipeAllIntervalInSec;
|
|
8
|
+
private autoWipeAllIntervalHandle;
|
|
9
|
+
constructor(options?: {
|
|
10
|
+
ttlInSec?: number;
|
|
11
|
+
autoTtlCleanIntervalInSec?: number;
|
|
12
|
+
autoWipeAllIntervalInSec?: number;
|
|
13
|
+
});
|
|
14
|
+
fetchMultiple(ids: string[]): Promise<{
|
|
15
|
+
idsToRequest: string[];
|
|
16
|
+
cachedData: Array<{
|
|
17
|
+
id: string;
|
|
18
|
+
data: Data;
|
|
19
|
+
}>;
|
|
20
|
+
}>;
|
|
21
|
+
fetch(id: string): Promise<Data | undefined>;
|
|
22
|
+
add(o: {
|
|
23
|
+
id: string;
|
|
24
|
+
d: Data;
|
|
25
|
+
ttlInSec?: number;
|
|
26
|
+
}): Promise<void>;
|
|
27
|
+
remove(id: string): Promise<void>;
|
|
28
|
+
forceTtlClean(): Promise<void>;
|
|
29
|
+
private setAutoTtlCleanIntervalHandle;
|
|
30
|
+
private ttlClean;
|
|
31
|
+
forceWipeAll(): Promise<void>;
|
|
32
|
+
private setWipeAllIntervalHandle;
|
|
33
|
+
private wipeAll;
|
|
34
|
+
destroy(): Promise<void>;
|
|
35
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.InMemory = void 0;
|
|
13
|
+
const dates_1 = require("../dates");
|
|
14
|
+
const oneMinInSec = 60;
|
|
15
|
+
const _60minInSec = 60 * oneMinInSec;
|
|
16
|
+
const MAX_INTEGER_32 = Math.pow(2, 31) - 1;
|
|
17
|
+
class InMemory {
|
|
18
|
+
constructor(options) {
|
|
19
|
+
var _a, _b;
|
|
20
|
+
this.cache = new Map();
|
|
21
|
+
this.ttlInSec = _60minInSec;
|
|
22
|
+
this.autoTtlCleanIntervalInSec = _60minInSec;
|
|
23
|
+
if (options !== undefined) {
|
|
24
|
+
this.ttlInSec = (_a = options.ttlInSec) !== null && _a !== void 0 ? _a : this.ttlInSec;
|
|
25
|
+
this.autoTtlCleanIntervalInSec = (_b = options.autoTtlCleanIntervalInSec) !== null && _b !== void 0 ? _b : this.autoTtlCleanIntervalInSec;
|
|
26
|
+
this.autoWipeAllIntervalInSec = options.autoWipeAllIntervalInSec;
|
|
27
|
+
}
|
|
28
|
+
this.setAutoTtlCleanIntervalHandle();
|
|
29
|
+
this.setWipeAllIntervalHandle();
|
|
30
|
+
}
|
|
31
|
+
fetchMultiple(ids) {
|
|
32
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
33
|
+
const idsToRequest = [];
|
|
34
|
+
const cachedData = [];
|
|
35
|
+
yield Promise.all(ids.map((id) => __awaiter(this, void 0, void 0, function* () {
|
|
36
|
+
const data = yield this.fetch(id);
|
|
37
|
+
if (data !== undefined) {
|
|
38
|
+
cachedData.push({ id, data });
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
idsToRequest.push(id);
|
|
42
|
+
}
|
|
43
|
+
})));
|
|
44
|
+
return {
|
|
45
|
+
idsToRequest,
|
|
46
|
+
cachedData,
|
|
47
|
+
};
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
fetch(id) {
|
|
51
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
52
|
+
const data = this.cache.get(id);
|
|
53
|
+
if (data === undefined) {
|
|
54
|
+
return undefined;
|
|
55
|
+
}
|
|
56
|
+
const timeLimit = dates_1.DateUtils.addSeconds(new Date(), -data.ttlInSec);
|
|
57
|
+
const isValidData = data.timestamp > timeLimit.getTime();
|
|
58
|
+
if (isValidData) {
|
|
59
|
+
return data.data;
|
|
60
|
+
}
|
|
61
|
+
this.remove(id);
|
|
62
|
+
return undefined;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
add(o) {
|
|
66
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
67
|
+
var _a;
|
|
68
|
+
const ttlInSec = (_a = o.ttlInSec) !== null && _a !== void 0 ? _a : this.ttlInSec;
|
|
69
|
+
this.cache.set(o.id, { data: o.d, timestamp: new Date().getTime(), ttlInSec });
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
remove(id) {
|
|
73
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
74
|
+
this.cache.delete(id);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
forceTtlClean() {
|
|
78
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
79
|
+
this.ttlClean();
|
|
80
|
+
this.setAutoTtlCleanIntervalHandle();
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
setAutoTtlCleanIntervalHandle() {
|
|
84
|
+
clearInterval(this.autoTtlCleanIntervalHandle);
|
|
85
|
+
this.autoTtlCleanIntervalHandle = setInterval(() => this.ttlClean(), toSafeIntervalMs(this.autoTtlCleanIntervalInSec));
|
|
86
|
+
}
|
|
87
|
+
ttlClean() {
|
|
88
|
+
this.cache.forEach((data, id) => {
|
|
89
|
+
const timeLimit = dates_1.DateUtils.addSeconds(new Date(), -data.ttlInSec);
|
|
90
|
+
if (data.timestamp <= timeLimit.getTime()) {
|
|
91
|
+
this.cache.delete(id);
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
forceWipeAll() {
|
|
96
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
97
|
+
this.wipeAll();
|
|
98
|
+
this.setWipeAllIntervalHandle();
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
setWipeAllIntervalHandle() {
|
|
102
|
+
clearInterval(this.autoWipeAllIntervalHandle);
|
|
103
|
+
if (this.autoWipeAllIntervalInSec !== undefined) {
|
|
104
|
+
this.autoWipeAllIntervalHandle = setInterval(() => this.wipeAll(), toSafeIntervalMs(this.autoWipeAllIntervalInSec));
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
wipeAll() {
|
|
108
|
+
this.cache.clear();
|
|
109
|
+
}
|
|
110
|
+
destroy() {
|
|
111
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
112
|
+
clearInterval(this.autoTtlCleanIntervalHandle);
|
|
113
|
+
clearInterval(this.autoWipeAllIntervalHandle);
|
|
114
|
+
this.cache.clear();
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
exports.InMemory = InMemory;
|
|
119
|
+
function toSafeIntervalMs(sec) {
|
|
120
|
+
return Math.min(sec * 1000, MAX_INTEGER_32);
|
|
121
|
+
}
|
package/dist/dates.d.ts
ADDED
package/dist/dates.js
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DateUtils = void 0;
|
|
4
|
+
class DateUtils {
|
|
5
|
+
static addDays(d, quantity) {
|
|
6
|
+
const newDate = new Date(d);
|
|
7
|
+
newDate.setDate(newDate.getDate() + quantity);
|
|
8
|
+
return newDate;
|
|
9
|
+
}
|
|
10
|
+
static addMinutes(d, quantity) {
|
|
11
|
+
const newDate = new Date(d);
|
|
12
|
+
newDate.setMinutes(newDate.getMinutes() + quantity);
|
|
13
|
+
return newDate;
|
|
14
|
+
}
|
|
15
|
+
static addSeconds(d, quantity) {
|
|
16
|
+
const newDate = new Date(d);
|
|
17
|
+
newDate.setSeconds(newDate.getSeconds() + quantity);
|
|
18
|
+
return newDate;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.DateUtils = DateUtils;
|
package/dist/dev.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export declare class DevUtils {
|
|
2
|
+
static isSet<T = any>(obj: T | null | undefined): obj is T;
|
|
3
|
+
static isNotSet<T = any>(obj: T | null | undefined): obj is null | undefined;
|
|
4
|
+
static isOneNotSet(memberNames: string[], data: any): boolean;
|
|
5
|
+
static isAllSet(memberNames: string[], data: any): boolean;
|
|
6
|
+
static getFieldValue<T>(struct: Record<string, any>, fieldname: string, defval: T): T;
|
|
7
|
+
private static isEmptyArray;
|
|
8
|
+
private static isEmptyString;
|
|
9
|
+
static getChildFieldValue<T>(struct: Record<string, any>, fieldnames: string[], defval: T): T;
|
|
10
|
+
static isEmpty(value: any): boolean;
|
|
11
|
+
static makeSingleton<T>(callback: () => Promise<T>): () => Promise<T>;
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param func the function to execute
|
|
15
|
+
* @param waitForInMs the debounce time in milliseconds
|
|
16
|
+
* @returns
|
|
17
|
+
*/
|
|
18
|
+
static debounce<F extends (...args: any[]) => void>(func: F, waitForInMs: number): (...args: Parameters<F>) => void;
|
|
19
|
+
static getError(e: unknown): Error;
|
|
20
|
+
}
|
package/dist/dev.js
ADDED
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DevUtils = void 0;
|
|
4
|
+
class DevUtils {
|
|
5
|
+
static isSet(obj) {
|
|
6
|
+
const res = undefined !== obj && null !== obj;
|
|
7
|
+
if (typeof obj === 'string') {
|
|
8
|
+
return res && 'undefined' !== obj;
|
|
9
|
+
}
|
|
10
|
+
return res;
|
|
11
|
+
}
|
|
12
|
+
static isNotSet(obj) {
|
|
13
|
+
return !this.isSet(obj);
|
|
14
|
+
}
|
|
15
|
+
static isOneNotSet(memberNames, data) {
|
|
16
|
+
let ret = false;
|
|
17
|
+
for (const member of memberNames) {
|
|
18
|
+
if (this.isNotSet(data[member])) {
|
|
19
|
+
ret = true;
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return ret;
|
|
24
|
+
}
|
|
25
|
+
static isAllSet(memberNames, data) {
|
|
26
|
+
return !this.isOneNotSet(memberNames, data);
|
|
27
|
+
}
|
|
28
|
+
static getFieldValue(struct, fieldname, defval) {
|
|
29
|
+
if (this.isSet(struct) && this.isSet(struct[fieldname])) {
|
|
30
|
+
return struct[fieldname];
|
|
31
|
+
}
|
|
32
|
+
return defval;
|
|
33
|
+
}
|
|
34
|
+
static isEmptyArray(array) {
|
|
35
|
+
if (!Array.isArray(array)) {
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return this.isNotSet(array) || array.length === 0;
|
|
39
|
+
}
|
|
40
|
+
static isEmptyString(str) {
|
|
41
|
+
if ('string' !== typeof str) {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
return this.isNotSet(str) || str.trim().length === 0;
|
|
45
|
+
}
|
|
46
|
+
static getChildFieldValue(struct, fieldnames, defval) {
|
|
47
|
+
let val = defval;
|
|
48
|
+
for (const fieldname of fieldnames) {
|
|
49
|
+
val = this.getFieldValue(struct, fieldname, defval);
|
|
50
|
+
if (val === defval) {
|
|
51
|
+
return defval;
|
|
52
|
+
}
|
|
53
|
+
struct = struct[fieldname];
|
|
54
|
+
}
|
|
55
|
+
return val;
|
|
56
|
+
}
|
|
57
|
+
static isEmpty(value) {
|
|
58
|
+
if (this.isNotSet(value)) {
|
|
59
|
+
return true;
|
|
60
|
+
}
|
|
61
|
+
if (Array.isArray(value)) {
|
|
62
|
+
return this.isEmptyArray(value);
|
|
63
|
+
}
|
|
64
|
+
if ('string' === typeof value) {
|
|
65
|
+
return this.isEmptyString(value);
|
|
66
|
+
}
|
|
67
|
+
if ('object' === typeof value) {
|
|
68
|
+
return Object.keys(value).length === 0;
|
|
69
|
+
}
|
|
70
|
+
return false;
|
|
71
|
+
}
|
|
72
|
+
static makeSingleton(callback) {
|
|
73
|
+
let singleton;
|
|
74
|
+
return () => {
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
if (singleton !== undefined) {
|
|
77
|
+
resolve(singleton);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
callback()
|
|
81
|
+
.then((sglton) => {
|
|
82
|
+
singleton = sglton;
|
|
83
|
+
resolve(singleton);
|
|
84
|
+
})
|
|
85
|
+
.catch((err) => {
|
|
86
|
+
reject(err);
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
*
|
|
93
|
+
* @param func the function to execute
|
|
94
|
+
* @param waitForInMs the debounce time in milliseconds
|
|
95
|
+
* @returns
|
|
96
|
+
*/
|
|
97
|
+
static debounce(func, waitForInMs) {
|
|
98
|
+
let timeout;
|
|
99
|
+
return (...args) => {
|
|
100
|
+
clearTimeout(timeout);
|
|
101
|
+
timeout = setTimeout(() => {
|
|
102
|
+
func(...args);
|
|
103
|
+
}, waitForInMs);
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
static getError(e) {
|
|
107
|
+
if (e instanceof Error) {
|
|
108
|
+
return e;
|
|
109
|
+
}
|
|
110
|
+
if (typeof e === 'string') {
|
|
111
|
+
return new Error(e);
|
|
112
|
+
}
|
|
113
|
+
let error = 'unknown error';
|
|
114
|
+
if (e !== undefined && e.message !== undefined) {
|
|
115
|
+
error = e.message;
|
|
116
|
+
}
|
|
117
|
+
if (e !== undefined && e.error !== undefined) {
|
|
118
|
+
error += ' > ' + e.error;
|
|
119
|
+
}
|
|
120
|
+
return new Error(error);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
exports.DevUtils = DevUtils;
|
package/dist/function.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FunctionUtils = void 0;
|
|
4
|
+
const dev_1 = require("./dev");
|
|
5
|
+
class FunctionUtils {
|
|
6
|
+
static isAsync(fn) {
|
|
7
|
+
if (dev_1.DevUtils.isNotSet(fn)) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
return fn.toString().includes('__awaiter') || fn.constructor.name === 'AsyncFunction';
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
exports.FunctionUtils = FunctionUtils;
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { DevUtils } from './dev';
|
|
2
|
+
export { StringUtils } from './string';
|
|
3
|
+
export { ArrayUtils } from './array';
|
|
4
|
+
export { FunctionUtils } from './function';
|
|
5
|
+
export { TestUtils } from './tests';
|
|
6
|
+
export { DateUtils } from './dates';
|
|
7
|
+
export { InMemory } from './cache-manager/in-memory';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InMemory = exports.DateUtils = exports.TestUtils = exports.FunctionUtils = exports.ArrayUtils = exports.StringUtils = exports.DevUtils = void 0;
|
|
4
|
+
var dev_1 = require("./dev");
|
|
5
|
+
Object.defineProperty(exports, "DevUtils", { enumerable: true, get: function () { return dev_1.DevUtils; } });
|
|
6
|
+
var string_1 = require("./string");
|
|
7
|
+
Object.defineProperty(exports, "StringUtils", { enumerable: true, get: function () { return string_1.StringUtils; } });
|
|
8
|
+
var array_1 = require("./array");
|
|
9
|
+
Object.defineProperty(exports, "ArrayUtils", { enumerable: true, get: function () { return array_1.ArrayUtils; } });
|
|
10
|
+
var function_1 = require("./function");
|
|
11
|
+
Object.defineProperty(exports, "FunctionUtils", { enumerable: true, get: function () { return function_1.FunctionUtils; } });
|
|
12
|
+
var tests_1 = require("./tests");
|
|
13
|
+
Object.defineProperty(exports, "TestUtils", { enumerable: true, get: function () { return tests_1.TestUtils; } });
|
|
14
|
+
var dates_1 = require("./dates");
|
|
15
|
+
Object.defineProperty(exports, "DateUtils", { enumerable: true, get: function () { return dates_1.DateUtils; } });
|
|
16
|
+
var in_memory_1 = require("./cache-manager/in-memory");
|
|
17
|
+
Object.defineProperty(exports, "InMemory", { enumerable: true, get: function () { return in_memory_1.InMemory; } });
|
package/dist/string.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export declare class StringUtils {
|
|
2
|
+
static isBlank(str: string): boolean;
|
|
3
|
+
static replaceTrailing(value: string, charToTrim: string, replacement: string): string;
|
|
4
|
+
static replaceLeading(value: string, charToTrim: string, replacement: string): string;
|
|
5
|
+
static uncapitalize(word: string): string;
|
|
6
|
+
static capitalize(word: string): string;
|
|
7
|
+
}
|
package/dist/string.js
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.StringUtils = void 0;
|
|
4
|
+
const dev_1 = require("./dev");
|
|
5
|
+
class StringUtils {
|
|
6
|
+
static isBlank(str) {
|
|
7
|
+
if ('string' !== typeof str) {
|
|
8
|
+
return true;
|
|
9
|
+
}
|
|
10
|
+
return dev_1.DevUtils.isEmpty(str);
|
|
11
|
+
}
|
|
12
|
+
static replaceTrailing(value, charToTrim, replacement) {
|
|
13
|
+
if (dev_1.DevUtils.isNotSet(value) || '' === value) {
|
|
14
|
+
return '';
|
|
15
|
+
}
|
|
16
|
+
const regExp = new RegExp(charToTrim[0] + '+$');
|
|
17
|
+
return value.replace(regExp, replacement);
|
|
18
|
+
}
|
|
19
|
+
static replaceLeading(value, charToTrim, replacement) {
|
|
20
|
+
if (dev_1.DevUtils.isNotSet(value) || '' === value) {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
const regExp = new RegExp('^' + charToTrim[0] + '+');
|
|
24
|
+
return value.replace(regExp, replacement);
|
|
25
|
+
}
|
|
26
|
+
static uncapitalize(word) {
|
|
27
|
+
if (dev_1.DevUtils.isNotSet(word) || '' === word) {
|
|
28
|
+
return '';
|
|
29
|
+
}
|
|
30
|
+
const lowercase = word[0].toLowerCase();
|
|
31
|
+
if (word.length > 1) {
|
|
32
|
+
return lowercase + word.slice(1);
|
|
33
|
+
}
|
|
34
|
+
return lowercase;
|
|
35
|
+
}
|
|
36
|
+
static capitalize(word) {
|
|
37
|
+
if (dev_1.DevUtils.isNotSet(word) || '' === word) {
|
|
38
|
+
return '';
|
|
39
|
+
}
|
|
40
|
+
const uppercase = word[0].toUpperCase();
|
|
41
|
+
if (word.length > 1) {
|
|
42
|
+
return uppercase + word.slice(1);
|
|
43
|
+
}
|
|
44
|
+
return uppercase;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
exports.StringUtils = StringUtils;
|
package/dist/tests.d.ts
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
type Result = {
|
|
2
|
+
success: true;
|
|
3
|
+
} | {
|
|
4
|
+
success: false;
|
|
5
|
+
error: Error;
|
|
6
|
+
};
|
|
7
|
+
export declare class TestUtils {
|
|
8
|
+
static testExpectedSuccess<Input, Output>(o: {
|
|
9
|
+
setPreconditions: () => Promise<Input>;
|
|
10
|
+
run: (i: Input) => Promise<Output>;
|
|
11
|
+
checkPostconditions: (o: Output) => Promise<void>;
|
|
12
|
+
}): Promise<Result>;
|
|
13
|
+
static testExpectedError<Input, Output>(o: {
|
|
14
|
+
setPreconditions: () => Promise<Input>;
|
|
15
|
+
run: (i: Input) => Promise<Output>;
|
|
16
|
+
checkError: (o: Output) => Promise<void>;
|
|
17
|
+
checkPostconditions: (o: Output) => Promise<void>;
|
|
18
|
+
}): Promise<Result>;
|
|
19
|
+
static testExpectedException<Input, Output>(o: {
|
|
20
|
+
setPreconditions: () => Promise<Input>;
|
|
21
|
+
run: (i: Input) => Promise<Output>;
|
|
22
|
+
checkError: (e: unknown) => Promise<void>;
|
|
23
|
+
checkPostconditions: () => Promise<void>;
|
|
24
|
+
}): Promise<Result>;
|
|
25
|
+
}
|
|
26
|
+
export {};
|
package/dist/tests.js
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.TestUtils = void 0;
|
|
13
|
+
const dev_1 = require("./dev");
|
|
14
|
+
class TestUtils {
|
|
15
|
+
static testExpectedSuccess(o) {
|
|
16
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
17
|
+
try {
|
|
18
|
+
const input = yield o.setPreconditions();
|
|
19
|
+
const output = yield o.run(input);
|
|
20
|
+
yield o.checkPostconditions(output);
|
|
21
|
+
return { success: true };
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
return { success: false, error: dev_1.DevUtils.getError(e) };
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
static testExpectedError(o) {
|
|
29
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
30
|
+
try {
|
|
31
|
+
const input = yield o.setPreconditions();
|
|
32
|
+
const output = yield o.run(input);
|
|
33
|
+
yield o.checkError(output);
|
|
34
|
+
yield o.checkPostconditions(output);
|
|
35
|
+
return { success: true };
|
|
36
|
+
}
|
|
37
|
+
catch (e) {
|
|
38
|
+
return { success: false, error: dev_1.DevUtils.getError(e) };
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
static testExpectedException(o) {
|
|
43
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
44
|
+
try {
|
|
45
|
+
const input = yield o.setPreconditions();
|
|
46
|
+
yield o.run(input);
|
|
47
|
+
return { success: false, error: new Error('No exception thrown') };
|
|
48
|
+
}
|
|
49
|
+
catch (e) {
|
|
50
|
+
yield o.checkError(e);
|
|
51
|
+
yield o.checkPostconditions();
|
|
52
|
+
return { success: true };
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
exports.TestUtils = TestUtils;
|
package/package.json
CHANGED
|
@@ -1,6 +1,49 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
2
|
+
"name": "@animaj/ts-utils",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Shared TypeScript helpers for Animaj services: null/empty guards, safe nested reads, async singletons, TTL in-memory cache, test helpers",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist/**/*"
|
|
9
|
+
],
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "make build",
|
|
12
|
+
"test": "make test",
|
|
13
|
+
"format": "make format",
|
|
14
|
+
"lint": "make lint",
|
|
15
|
+
"prepublishOnly": "npm run lint && npm test",
|
|
16
|
+
"preversion": "npm run lint",
|
|
17
|
+
"version": "npm run format && git add -A src"
|
|
18
|
+
},
|
|
19
|
+
"author": "Animaj",
|
|
20
|
+
"contributors": [
|
|
21
|
+
"Julien Khong (original author)",
|
|
22
|
+
"Stefano (current maintainer)"
|
|
23
|
+
],
|
|
24
|
+
"license": "ISC",
|
|
25
|
+
"homepage": "https://github.com/animaj-lab/ts-utils#readme",
|
|
26
|
+
"repository": {
|
|
27
|
+
"type": "git",
|
|
28
|
+
"url": "git+https://github.com/animaj-lab/ts-utils.git"
|
|
29
|
+
},
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/animaj-lab/ts-utils/issues"
|
|
32
|
+
},
|
|
33
|
+
"publishConfig": {
|
|
34
|
+
"access": "public"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@typescript-eslint/eslint-plugin": "^2.30.0",
|
|
38
|
+
"@typescript-eslint/parser": "^2.30.0",
|
|
39
|
+
"eslint": "^6.8.0",
|
|
40
|
+
"eslint-config-prettier": "^6.11.0",
|
|
41
|
+
"husky": "^4.2.3",
|
|
42
|
+
"minimist": ">=1.2.2",
|
|
43
|
+
"mocha": "^5.1.0",
|
|
44
|
+
"prettier": "^2.0.2",
|
|
45
|
+
"typescript": "^6.0.3",
|
|
46
|
+
"vitest": "4.1.8"
|
|
47
|
+
},
|
|
48
|
+
"keywords": []
|
|
49
|
+
}
|