@wireio/shared 1.0.1 → 1.0.2

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.
Files changed (2) hide show
  1. package/README.md +424 -0
  2. package/package.json +1 -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,6 +1,6 @@
1
1
  {
2
2
  "name": "@wireio/shared",
3
- "version": "1.0.1",
3
+ "version": "1.0.2",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"