@wireio/shared 1.0.1 → 1.0.3
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/README.md +424 -0
- package/package.json +5 -1
- package/src/guards/applyTypeGuardExtras.ts +0 -9
- package/src/guards/array.ts +0 -5
- package/src/guards/assert.ts +0 -83
- package/src/guards/class.ts +0 -56
- package/src/guards/guard-tools.ts +0 -78
- package/src/guards/index.ts +0 -7
- package/src/guards/primitive.ts +0 -77
- package/src/guards/types.ts +0 -15
- package/src/helpers/Deferred.ts +0 -149
- package/src/helpers/index.ts +0 -1
- package/src/index.ts +0 -3
- package/src/logging/Appender.ts +0 -5
- package/src/logging/Formatter.ts +0 -8
- package/src/logging/InternalLogger.ts +0 -6
- package/src/logging/Level.ts +0 -19
- package/src/logging/LogRecord.ts +0 -17
- package/src/logging/Logger.ts +0 -222
- package/src/logging/LoggingManager.ts +0 -306
- package/src/logging/appenders/ConsoleAppender.ts +0 -110
- package/src/logging/appenders/DebugAppender.ts +0 -254
- package/src/logging/appenders/FileAppender.ts +0 -281
- package/src/logging/appenders/aws-firehose/AWSFirehoseAppender.ts +0 -176
- package/src/logging/appenders/aws-firehose/AWSFirehoseCredentialManager.ts +0 -132
- package/src/logging/appenders/index.ts +0 -3
- package/src/logging/context/LogContext.ts +0 -74
- package/src/logging/context/LogContextContainer.ts +0 -28
- package/src/logging/context/LogContextProvider.ts +0 -43
- package/src/logging/context/index.ts +0 -3
- package/src/logging/context/providers/node/NodeContextProvider.ts +0 -45
- package/src/logging/context/providers/node/index.ts +0 -3
- package/src/logging/examples/example-debug-appender.ts +0 -19
- package/src/logging/examples/example-file-appender.ts +0 -19
- package/src/logging/getLogger.ts +0 -9
- package/src/logging/index-node.ts +0 -2
- package/src/logging/index.ts +0 -11
- package/src/logging/util.ts +0 -6
- package/tsconfig.json +0 -9
- package/tsconfig.tsbuildinfo +0 -1
package/README.md
ADDED
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
# @wireio/shared
|
|
2
|
+
|
|
3
|
+
Core shared utilities for Wire applications, providing a logging framework, type guards, and async helpers that work across browser and Node.js environments.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @wireio/shared
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Modules
|
|
12
|
+
|
|
13
|
+
- [Logging](#logging) - Flexible logging framework with multiple appenders
|
|
14
|
+
- [Guards](#guards) - Type guards and assertion utilities
|
|
15
|
+
- [Helpers](#helpers) - Async utilities including `Deferred`
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Logging
|
|
20
|
+
|
|
21
|
+
A flexible, extensible logging framework supporting multiple log levels, appenders, and contextual logging.
|
|
22
|
+
|
|
23
|
+
### Basic Usage
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { getLoggingManager } from "@wireio/shared"
|
|
27
|
+
|
|
28
|
+
const logManager = getLoggingManager()
|
|
29
|
+
const log = logManager.getLogger("my-module")
|
|
30
|
+
|
|
31
|
+
log.trace("Detailed trace message")
|
|
32
|
+
log.debug("Debug information")
|
|
33
|
+
log.info("Informational message")
|
|
34
|
+
log.warn("Warning message")
|
|
35
|
+
log.error("Error occurred", new Error("Something went wrong"))
|
|
36
|
+
log.fatal("Fatal error - application cannot continue")
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
### Log Levels
|
|
40
|
+
|
|
41
|
+
Levels are ordered by severity (lowest to highest):
|
|
42
|
+
|
|
43
|
+
| Level | Threshold | Description |
|
|
44
|
+
|-------|-----------|-------------|
|
|
45
|
+
| `trace` | 0 | Fine-grained debugging |
|
|
46
|
+
| `debug` | 1 | Debugging information |
|
|
47
|
+
| `info` | 2 | Informational messages |
|
|
48
|
+
| `warn` | 3 | Warning conditions |
|
|
49
|
+
| `error` | 4 | Error conditions |
|
|
50
|
+
| `fatal` | 5 | Critical failures |
|
|
51
|
+
|
|
52
|
+
### Configuring the Logging Manager
|
|
53
|
+
|
|
54
|
+
```typescript
|
|
55
|
+
import { getLoggingManager, Level, ConsoleAppender } from "@wireio/shared"
|
|
56
|
+
|
|
57
|
+
const logManager = getLoggingManager()
|
|
58
|
+
|
|
59
|
+
// Set the root logging level
|
|
60
|
+
logManager.setRootLevel(Level.debug)
|
|
61
|
+
|
|
62
|
+
// Replace default appenders
|
|
63
|
+
logManager.setAppenders(new ConsoleAppender({ prettyPrint: true }))
|
|
64
|
+
|
|
65
|
+
// Add additional appenders
|
|
66
|
+
logManager.addAppenders(new MyCustomAppender())
|
|
67
|
+
|
|
68
|
+
// Configure threshold overrides for specific categories
|
|
69
|
+
logManager.addThresholdOverrides(
|
|
70
|
+
[/^database/, "debug"], // Enable debug for database.* categories
|
|
71
|
+
["auth", "trace"] // Enable trace for exact "auth" category
|
|
72
|
+
)
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### Environment-Based Debug Patterns
|
|
76
|
+
|
|
77
|
+
Set the `DEBUG_PATTERNS` environment variable to enable debug logging for specific categories:
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
DEBUG_PATTERNS="database.*,auth.*" node app.js
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Level Checking
|
|
84
|
+
|
|
85
|
+
Check if a level is enabled before expensive operations:
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
if (log.isDebugEnabled()) {
|
|
89
|
+
log.debug("Expensive data:", computeExpensiveDebugInfo())
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Log Records
|
|
94
|
+
|
|
95
|
+
Each log record contains:
|
|
96
|
+
|
|
97
|
+
```typescript
|
|
98
|
+
interface LogRecord {
|
|
99
|
+
timestamp: number // EPOCH Milliseconds
|
|
100
|
+
category: string // Logger category
|
|
101
|
+
level: LevelKind // Log level
|
|
102
|
+
message: string // Log message
|
|
103
|
+
args?: any[] // Additional arguments
|
|
104
|
+
data?: any // Structured data
|
|
105
|
+
tags?: string[] // Tags for filtering
|
|
106
|
+
url?: string // Source URL (auto-populated)
|
|
107
|
+
env?: string // Environment (auto-populated)
|
|
108
|
+
errorMessage?: string // Error message (if Error passed)
|
|
109
|
+
errorStack?: string // Error stack trace (if Error passed)
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Built-in Appenders
|
|
114
|
+
|
|
115
|
+
#### ConsoleAppender
|
|
116
|
+
|
|
117
|
+
Default appender that writes to the console:
|
|
118
|
+
|
|
119
|
+
```typescript
|
|
120
|
+
import { ConsoleAppender } from "@wireio/shared"
|
|
121
|
+
|
|
122
|
+
const appender = new ConsoleAppender({
|
|
123
|
+
prettyPrint: true,
|
|
124
|
+
formatter: ({ level, message, category }) =>
|
|
125
|
+
`[${category}] (${level}) ${message}`
|
|
126
|
+
})
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### AWSFirehoseAppender
|
|
130
|
+
|
|
131
|
+
Streams logs to AWS Kinesis Data Firehose for centralized log aggregation:
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
import { AWSFirehoseAppender, AWSFirehoseCredentialManager } from "@wireio/shared"
|
|
135
|
+
|
|
136
|
+
const credManager = new AWSFirehoseCredentialManager()
|
|
137
|
+
const firehoseAppender = new AWSFirehoseAppender(credManager)
|
|
138
|
+
|
|
139
|
+
logManager.addAppenders(firehoseAppender)
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**Features:**
|
|
143
|
+
- Automatic credential refresh before expiration (3-minute buffer)
|
|
144
|
+
- Batched record delivery (up to 10 records per batch)
|
|
145
|
+
- Queue management with 10,000 record limit (oldest records dropped when exceeded)
|
|
146
|
+
- Automatic retry on expired token errors
|
|
147
|
+
- Event-driven credential handling via EventEmitter3
|
|
148
|
+
|
|
149
|
+
**Credential Manager Events:**
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
credManager.on("received", (creds) => {
|
|
153
|
+
console.log("Credentials received, stream:", creds.streamName)
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
credManager.on("expired", () => {
|
|
157
|
+
console.log("Credentials expired")
|
|
158
|
+
})
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
**AWS Credentials Structure:**
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
interface AWSCredentials {
|
|
165
|
+
accessKeyId: string
|
|
166
|
+
secretAccessKey: string
|
|
167
|
+
sessionToken: string
|
|
168
|
+
streamName: string // Firehose delivery stream name
|
|
169
|
+
region: string // AWS region
|
|
170
|
+
expiration: string // ISO 8601 expiration timestamp
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
The credential manager fetches temporary credentials from an HTTP endpoint and automatically schedules refresh before expiration.
|
|
175
|
+
|
|
176
|
+
### Custom Appenders
|
|
177
|
+
|
|
178
|
+
Implement the `Appender` interface:
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
import { Appender, LogRecord } from "@wireio/shared"
|
|
182
|
+
|
|
183
|
+
class MyCustomAppender implements Appender {
|
|
184
|
+
append(record: LogRecord): void {
|
|
185
|
+
// Send to external service, write to file, etc.
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
---
|
|
191
|
+
|
|
192
|
+
## Guards
|
|
193
|
+
|
|
194
|
+
Type guards and assertion utilities for runtime type checking with TypeScript type narrowing.
|
|
195
|
+
|
|
196
|
+
### Primitive Guards
|
|
197
|
+
|
|
198
|
+
```typescript
|
|
199
|
+
import {
|
|
200
|
+
isString, isNumber, isBoolean, isFunction,
|
|
201
|
+
isObject, isArray, isNil, isDefined, isPromise
|
|
202
|
+
} from "@wireio/shared"
|
|
203
|
+
|
|
204
|
+
if (isString(value)) {
|
|
205
|
+
// value is narrowed to string
|
|
206
|
+
console.log(value.toUpperCase())
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (isDefined(maybeNull)) {
|
|
210
|
+
// maybeNull is narrowed to exclude null/undefined
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (isPromise(result)) {
|
|
214
|
+
// result is narrowed to Promise<any>
|
|
215
|
+
await result
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Available Guards
|
|
220
|
+
|
|
221
|
+
| Guard | Type Narrowing |
|
|
222
|
+
|-------|----------------|
|
|
223
|
+
| `isNil(o)` | `undefined \| null` |
|
|
224
|
+
| `isDefined(o)` | Excludes `undefined \| null` |
|
|
225
|
+
| `isString(o)` | `string` |
|
|
226
|
+
| `isNumber(o)` | `number` (excludes NaN) |
|
|
227
|
+
| `isBoolean(o)` | `boolean` |
|
|
228
|
+
| `isFunction(o)` | `Function` |
|
|
229
|
+
| `isObject(o)` | `Object` |
|
|
230
|
+
| `isArray(o)` | `Array<T>` |
|
|
231
|
+
| `isPromise(o)` | `Promise<any>` |
|
|
232
|
+
| `isSymbol(o)` | `Symbol` |
|
|
233
|
+
| `isDate(o)` | `Date` |
|
|
234
|
+
| `isPrimitive(o)` | `boolean \| string \| number` |
|
|
235
|
+
|
|
236
|
+
### Assertions
|
|
237
|
+
|
|
238
|
+
```typescript
|
|
239
|
+
import { assert, AssertError } from "@wireio/shared"
|
|
240
|
+
|
|
241
|
+
// Throws AssertError if condition is false
|
|
242
|
+
assert(user !== null, "User must be defined")
|
|
243
|
+
|
|
244
|
+
// With function condition
|
|
245
|
+
assert(() => array.length > 0, "Array must not be empty")
|
|
246
|
+
|
|
247
|
+
// Lifted assertions for reuse
|
|
248
|
+
const assertPositive = assert.lift(
|
|
249
|
+
(n: number) => n > 0,
|
|
250
|
+
(n) => `Expected positive number, got ${n}`
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
assertPositive(5) // OK
|
|
254
|
+
assertPositive(-1) // Throws AssertError
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Safe Value Access
|
|
258
|
+
|
|
259
|
+
```typescript
|
|
260
|
+
import { getValue, guard } from "@wireio/shared"
|
|
261
|
+
|
|
262
|
+
// Get value with fallback on error
|
|
263
|
+
const name = getValue(() => user.profile.name, "Unknown")
|
|
264
|
+
|
|
265
|
+
// Execute function, swallow errors
|
|
266
|
+
guard(() => riskyOperation())
|
|
267
|
+
|
|
268
|
+
// With error handler
|
|
269
|
+
guard(() => riskyOperation(), (err) => console.error(err))
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Deferred (a `Promise` you manage)
|
|
275
|
+
|
|
276
|
+
A `Deferred` is a Promise that can be resolved or rejected externally, useful for coordinating async operations.
|
|
277
|
+
|
|
278
|
+
```typescript
|
|
279
|
+
import { Deferred, DeferredStatus } from "@wireio/shared"
|
|
280
|
+
|
|
281
|
+
// Create a deferred promise
|
|
282
|
+
const deferred = new Deferred<string>()
|
|
283
|
+
|
|
284
|
+
// Resolve it later
|
|
285
|
+
setTimeout(() => deferred.resolve("done"), 1000)
|
|
286
|
+
|
|
287
|
+
// Await the promise
|
|
288
|
+
const result = await deferred.promise
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
### Deferred API
|
|
292
|
+
|
|
293
|
+
```typescript
|
|
294
|
+
// Create with optional existing promise to wrap
|
|
295
|
+
const deferred = new Deferred<T>(existingPromise?)
|
|
296
|
+
|
|
297
|
+
// The underlying promise
|
|
298
|
+
deferred.promise: Promise<T>
|
|
299
|
+
|
|
300
|
+
// Resolve the promise
|
|
301
|
+
deferred.resolve(value?: T): void
|
|
302
|
+
|
|
303
|
+
// Reject the promise
|
|
304
|
+
deferred.reject(error: any): void
|
|
305
|
+
|
|
306
|
+
// Cancel (prevents future resolve/reject)
|
|
307
|
+
deferred.cancel(): void
|
|
308
|
+
|
|
309
|
+
// Status checks
|
|
310
|
+
deferred.isFulfilled(): boolean
|
|
311
|
+
deferred.isRejected(): boolean
|
|
312
|
+
deferred.isSettled(): boolean
|
|
313
|
+
deferred.isCancelled(): boolean
|
|
314
|
+
deferred.status(): DeferredStatus
|
|
315
|
+
|
|
316
|
+
// Get result (throws if not settled)
|
|
317
|
+
deferred.value: T
|
|
318
|
+
deferred.error: Error | undefined
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
### Static Methods
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
// Create an already-resolved Deferred
|
|
325
|
+
const resolved = Deferred.resolve("value")
|
|
326
|
+
|
|
327
|
+
// Delay utility
|
|
328
|
+
await Deferred.delay(1000) // Wait 1 second
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Use Cases
|
|
332
|
+
|
|
333
|
+
**Timeout wrapper:**
|
|
334
|
+
```typescript
|
|
335
|
+
async function withTimeout<T>(promise: Promise<T>, ms: number): Promise<T> {
|
|
336
|
+
const deferred = new Deferred<T>()
|
|
337
|
+
const timer = setTimeout(() => deferred.reject(new Error("Timeout")), ms)
|
|
338
|
+
|
|
339
|
+
promise
|
|
340
|
+
.then((v) => { clearTimeout(timer); deferred.resolve(v) })
|
|
341
|
+
.catch((e) => { clearTimeout(timer); deferred.reject(e) })
|
|
342
|
+
|
|
343
|
+
return deferred.promise
|
|
344
|
+
}
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
**Coordination:**
|
|
348
|
+
```typescript
|
|
349
|
+
class ResourceLoader {
|
|
350
|
+
private loadDeferred: Deferred<Resource> | null = null
|
|
351
|
+
|
|
352
|
+
async load(): Promise<Resource> {
|
|
353
|
+
if (this.loadDeferred) return this.loadDeferred.promise
|
|
354
|
+
|
|
355
|
+
this.loadDeferred = new Deferred()
|
|
356
|
+
try {
|
|
357
|
+
const resource = await fetchResource()
|
|
358
|
+
this.loadDeferred.resolve(resource)
|
|
359
|
+
} catch (e) {
|
|
360
|
+
this.loadDeferred.reject(e)
|
|
361
|
+
}
|
|
362
|
+
return this.loadDeferred.promise
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
---
|
|
368
|
+
|
|
369
|
+
## API Reference
|
|
370
|
+
|
|
371
|
+
### Logging Exports
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
import {
|
|
375
|
+
// Manager
|
|
376
|
+
LoggingManager,
|
|
377
|
+
getLoggingManager,
|
|
378
|
+
|
|
379
|
+
// Logger
|
|
380
|
+
Logger,
|
|
381
|
+
LoggerOptions,
|
|
382
|
+
|
|
383
|
+
// Levels
|
|
384
|
+
Level,
|
|
385
|
+
LevelKind,
|
|
386
|
+
LevelNames,
|
|
387
|
+
LevelThresholds,
|
|
388
|
+
|
|
389
|
+
// Records
|
|
390
|
+
LogRecord,
|
|
391
|
+
|
|
392
|
+
// Appenders
|
|
393
|
+
Appender,
|
|
394
|
+
ConsoleAppender,
|
|
395
|
+
DebugAppender,
|
|
396
|
+
AWSFirehoseAppender,
|
|
397
|
+
|
|
398
|
+
// Formatters
|
|
399
|
+
Formatter,
|
|
400
|
+
} from "@wireio/shared"
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
### Guards Exports
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
import {
|
|
407
|
+
// Primitives
|
|
408
|
+
isNil, isDefined, isString, isNumber, isBoolean,
|
|
409
|
+
isFunction, isObject, isArray, isPromise, isSymbol,
|
|
410
|
+
isDate, isPrimitive,
|
|
411
|
+
|
|
412
|
+
// Assertions
|
|
413
|
+
assert, AssertError,
|
|
414
|
+
|
|
415
|
+
// Utilities
|
|
416
|
+
getValue, guard,
|
|
417
|
+
} from "@wireio/shared"
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Helpers Exports
|
|
421
|
+
|
|
422
|
+
```typescript
|
|
423
|
+
import { Deferred, DeferredStatus } from "@wireio/shared"
|
|
424
|
+
```
|
package/package.json
CHANGED
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wireio/shared",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
8
|
+
"files": [
|
|
9
|
+
"lib",
|
|
10
|
+
"README.md"
|
|
11
|
+
],
|
|
8
12
|
"type": "module",
|
|
9
13
|
"types": "lib/index.d.ts",
|
|
10
14
|
"module": "lib/index.js",
|
package/src/guards/array.ts
DELETED
package/src/guards/assert.ts
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { isFunction, isString } from "./primitive.js"
|
|
2
|
-
|
|
3
|
-
export interface AssertOptions {
|
|
4
|
-
logErrorsToConsole: boolean
|
|
5
|
-
}
|
|
6
|
-
|
|
7
|
-
export const DefaultAssertOptions:AssertOptions = {
|
|
8
|
-
logErrorsToConsole: false
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
let assertOptions:AssertOptions = {...DefaultAssertOptions}
|
|
12
|
-
|
|
13
|
-
export function getAssertOptions() {
|
|
14
|
-
return {...assertOptions}
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function setGlobalAssertOptions(options: AssertOptions) {
|
|
18
|
-
assertOptions = {...assertOptions, ...options}
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export class AssertError extends Error {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
constructor(message: string, public readonly cause?: Error) {
|
|
26
|
-
super(message)
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const assertLift:AssertLift = <T>(test: (value:T) => boolean, messageProvider: string | ((value: T) => string)) => {
|
|
32
|
-
return (nextValue: T) => {
|
|
33
|
-
const ok = test(nextValue)
|
|
34
|
-
if (!ok) {
|
|
35
|
-
const message = isFunction(messageProvider) ? messageProvider(nextValue) : messageProvider ?? "no message"
|
|
36
|
-
throw new AssertError(message)
|
|
37
|
-
}
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export type AssertLift = <T>(test: (value:T) => boolean, message: string | ((value: T) => string)) =>
|
|
42
|
-
(nextValue: T) => void | never
|
|
43
|
-
|
|
44
|
-
export interface Assert {
|
|
45
|
-
(
|
|
46
|
-
test: (() => boolean) | boolean,
|
|
47
|
-
msg?: null | ((err?: Error) => Error | string) | Error | string | undefined,
|
|
48
|
-
overrideOptions?: Partial<AssertOptions>
|
|
49
|
-
): void | never
|
|
50
|
-
|
|
51
|
-
lift: AssertLift
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
export const assert: Assert = Object.assign((
|
|
56
|
-
test: (() => boolean) | boolean,
|
|
57
|
-
msg?: null | ((err?: Error) => Error | string) | Error | string | undefined,
|
|
58
|
-
overrideOptions: Partial<AssertOptions> = {}
|
|
59
|
-
): void | never => {
|
|
60
|
-
const options = {...assertOptions, ...overrideOptions}
|
|
61
|
-
let result: boolean = false
|
|
62
|
-
let error: Error = undefined
|
|
63
|
-
|
|
64
|
-
try {
|
|
65
|
-
result = isFunction(test) ? test() : test
|
|
66
|
-
} catch (err) {
|
|
67
|
-
error = err
|
|
68
|
-
if (options.logErrorsToConsole) {
|
|
69
|
-
console.error(`Assert failed: "${test}"`, err)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (!result || !!error) {
|
|
74
|
-
const errOut = !msg ? (error?.message ?? "unknown") :
|
|
75
|
-
isFunction(msg) ? msg(error) :
|
|
76
|
-
msg
|
|
77
|
-
throw isString(errOut) ? new AssertError(errOut, error) : errOut
|
|
78
|
-
}
|
|
79
|
-
}, {
|
|
80
|
-
lift: assertLift
|
|
81
|
-
})
|
|
82
|
-
|
|
83
|
-
|
package/src/guards/class.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Is ES6+ class
|
|
3
|
-
*
|
|
4
|
-
* @see https://stackoverflow.com/questions/29093396/how-do-you-check-the-difference-between-an-ecmascript-6-class-and-function/49510834
|
|
5
|
-
*
|
|
6
|
-
* @param {any} value
|
|
7
|
-
* @returns {boolean}
|
|
8
|
-
*/
|
|
9
|
-
import type { ClassConstructor, TypeGuard } from "./types"
|
|
10
|
-
|
|
11
|
-
export function isNativeClass <T = any>(value:any): value is ClassConstructor<T> {
|
|
12
|
-
return typeof value === 'function' && value.toString().indexOf('class') === 0
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
// Character positions
|
|
16
|
-
const INDEX_OF_FUNCTION_NAME = 9 // "function X", X is at index 9
|
|
17
|
-
const FIRST_UPPERCASE_INDEX_IN_ASCII = 65 // A is at index 65 in ASCII
|
|
18
|
-
const LAST_UPPERCASE_INDEX_IN_ASCII = 90 // Z is at index 90 in ASCII
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Is Conventional Class
|
|
22
|
-
* Looks for function with capital first letter MyClass
|
|
23
|
-
* First letter is the 9th character
|
|
24
|
-
* If changed, isClass must also be updated
|
|
25
|
-
* @param {any} value
|
|
26
|
-
* @returns {boolean}
|
|
27
|
-
*/
|
|
28
|
-
export function isConventionalClass <T = any>(value:any): value is ClassConstructor<T> {
|
|
29
|
-
if ( typeof value !== 'function' ) return false
|
|
30
|
-
const c = value.toString().charCodeAt(INDEX_OF_FUNCTION_NAME)
|
|
31
|
-
return c >= FIRST_UPPERCASE_INDEX_IN_ASCII && c <= LAST_UPPERCASE_INDEX_IN_ASCII
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
export function isClass<T = any>(value: any, includeConventional: boolean = true): value is ClassConstructor<T> {
|
|
36
|
-
return isNativeClass<T>(value) || (includeConventional && isConventionalClass<T>(value))
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
export function createInstanceOfGuard<T, Ctor extends ClassConstructor<T>>(ctor: Ctor): ((o: any) => o is T) {
|
|
44
|
-
return (o: any): o is T => o instanceof ctor
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export function instanceOf<T extends {}, Ctor extends ClassConstructor<T>>(ctor: Ctor) {
|
|
48
|
-
return createInstanceOfGuard<T,Ctor>(ctor)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function createGenericGuard<T>(tester: (val:any) => val is T):TypeGuard<T>
|
|
52
|
-
export function createGenericGuard<T>(type:{new():T}, tester: (val:any) => val is T):TypeGuard<T>
|
|
53
|
-
export function createGenericGuard<T>(typeOrTest: ({new():T} | ((val:any) => val is T)), tester?: (val:any) => val is T) {
|
|
54
|
-
return tester as TypeGuard<T>
|
|
55
|
-
}
|
|
56
|
-
|
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
import { isNil, isPromise } from "./primitive.js"
|
|
2
|
-
import type { ErrorHandler } from "./types.js"
|
|
3
|
-
|
|
4
|
-
export type GuardErrorHandler = (err:Error) => void
|
|
5
|
-
|
|
6
|
-
let globalErrorHandler:GuardErrorHandler | null
|
|
7
|
-
|
|
8
|
-
export function setGuardErrorHandler(errorHandler:GuardErrorHandler | null = null) {
|
|
9
|
-
globalErrorHandler = errorHandler
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export type ReturnTypeOrAny<Fn extends () => any> = (ReturnType<Fn> extends never ? unknown : ReturnType<Fn>)
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Get a value in a guarded fashion
|
|
17
|
-
* ensuring no exception
|
|
18
|
-
*
|
|
19
|
-
* @param fn
|
|
20
|
-
* @param defaultValue
|
|
21
|
-
* @returns {any}
|
|
22
|
-
* @param errorHandler
|
|
23
|
-
*/
|
|
24
|
-
export function getValue<Fn extends () => any, T extends ReturnType<Fn> >(
|
|
25
|
-
fn:Fn,
|
|
26
|
-
defaultValue: T = undefined,
|
|
27
|
-
errorHandler: ErrorHandler = globalErrorHandler
|
|
28
|
-
):T extends Promise<infer T2> ? Promise<T2> : T {
|
|
29
|
-
|
|
30
|
-
let result = null
|
|
31
|
-
|
|
32
|
-
try {
|
|
33
|
-
result = fn()
|
|
34
|
-
if (isPromise(result))
|
|
35
|
-
return result
|
|
36
|
-
.catch(err => {
|
|
37
|
-
if (errorHandler)
|
|
38
|
-
errorHandler(err)
|
|
39
|
-
|
|
40
|
-
return defaultValue
|
|
41
|
-
})
|
|
42
|
-
.then(value => isNil(value) ? defaultValue : value) as any
|
|
43
|
-
} catch (err) {
|
|
44
|
-
if (errorHandler)
|
|
45
|
-
errorHandler(err)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
return isNil(result) ?
|
|
49
|
-
(defaultValue as any) : result
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
export type GuardFn = <Fn extends () => any, T extends ReturnType<Fn> >(fn:Fn, localErrorHandler?: GuardErrorHandler | null) => (void | Promise<void>)
|
|
54
|
-
export type GuardTool = GuardFn & {
|
|
55
|
-
lift: <T>(fn:() => T, localErrorHandler?: GuardErrorHandler | null) => ((fn:() => T, localErrorHandler?: GuardErrorHandler | null) => (void | Promise<void>))
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Execute a function guarded from exception
|
|
62
|
-
*
|
|
63
|
-
* @param fn
|
|
64
|
-
* @param localErrorHandler
|
|
65
|
-
* @returns {(fn:()=>any)=>(fn:()=>any)=>any}
|
|
66
|
-
*/
|
|
67
|
-
const guardFn:GuardFn = <Fn extends () => any, T extends ReturnType<Fn> >(fn:Fn, localErrorHandler: ((err: Error) => void) | null = null):void | Promise<void> => {
|
|
68
|
-
const value = getValue<Fn,T>(fn, undefined, localErrorHandler)
|
|
69
|
-
if (isPromise(value))
|
|
70
|
-
return value.then(() => undefined as void)
|
|
71
|
-
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export const guard: GuardTool = Object.assign(guardFn, {
|
|
75
|
-
lift: <Fn extends () => any, T extends ReturnType<Fn> >(fn:Fn, localErrorHandler?: GuardErrorHandler | null) =>
|
|
76
|
-
(fn:Fn, localErrorHandler?: GuardErrorHandler | null): (void | Promise<void>) =>
|
|
77
|
-
guardFn<Fn,T>(fn,localErrorHandler)
|
|
78
|
-
})
|