UNPKG

@livy/util

Version:
395 lines (276 loc) 15.1 kB
# `@livy/util` This package contains common utilities for building components for the [Livy](https://github.com/loilo/livy#readme) logger. --- **Table of Contents:** - [General Purpose Utilities](#general-purpose-utilities) - [Helpers](#helpers) - [Types](#types) - [`Environment`](#environment) - [`ValidatableSet`](#validatableset) - [`GatedSet`](#gatedset) - [`HTML`](#html) - [`isResettableInterface`](#isresettableinterface) - [`Mixin`](#mixin) - [`Timer`](#timer) - [Formatter-Related Utilities](#formatter-related-utilities) - [`IncludedRecordProperties`](#includedrecordproperties) - [`isFormattableHandlerInterface`](#isformattablehandlerinterface) - [`AbstractBatchFormatter`](#abstractbatchformatter) - [`LineFomatter`](#linefomatter) - [Handler-Related Utilities](#handler-related-utilities) - [`isClosableHandlerInterface`](#isclosablehandlerinterface) - [`isProcessableHandlerInterface`](#isprocessablehandlerinterface) - [`isSyncHandlerInterface`](#issynchandlerinterface) - [`MirrorSyncHandlerMixin`](#mirrorsynchandlermixin) - [`ProcessableHandlerMixin`](#processablehandlermixin) - [`RespectLevelMixin`](#respectlevelmixin) - [`FormattableHandlerMixin`](#formattablehandlermixin) - [`AbstractBatchHandler`](#abstractbatchhandler) - [`AbstractLevelBubbleHandler`](#abstractlevelbubblehandler) - [`AbstractFormattingProcessingHandler`](#abstractformattingprocessinghandler) --- ## General Purpose Utilities ### [Helpers](https://github.com/loilo/livy/tree/master/packages/util/src/helpers.ts) There's also a whole bunch general-purpose helper functions in this file. They are thoroughly documented, including examples, so you're probably best off to just dive into the source code. ### [Types](https://github.com/loilo/livy/tree/master/packages/util/src/types.ts) For TypeScript users, there are also some handy types to be found in this file. ### [`Environment`](https://github.com/loilo/livy/tree/master/packages/util/src/environment.ts) Determines whether the runtime is Node.js or a browser and derives related environment-specific data. ```js const Environment = require('@livy/util/lib/environment') Environment.isNodeJs // `true` or `false` Environment.isBrowser // `true` or `false` Environment.EOL // os.EOL in Node.js, '\n' otherwise ``` ### [`ValidatableSet`](https://github.com/loilo/livy/tree/master/packages/util/src/validatable-set.ts) An extended [`Set`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set) with the array methods [`some`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some) and [`every`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every) on top: <!-- prettier-ignore --> ```js const { ValidatableSet } = require('@livy/util/lib/validatable-set') const set = new ValidatableSet([1, 2, 3]) set.every(item => typeof item === 'number') // true set.some(item => item < 0) // false // Behaves like the corresponding array methods on empty sets: set.clear() set.every(anyCondition) // false set.some(anyCondition) // true ``` ### [`GatedSet`](https://github.com/loilo/livy/tree/master/packages/util/src/gated-set.ts) An extended [`ValidatableSet`](#validatableset) that allows to validate and reject values that go inside the set. <!-- prettier-ignore --> ```js const { GatedSet } = require('@livy/util/lib/gated-set') const integers = new GatedSet(value => { if (typeof value !== 'number' || !Number.isInteger(value)) { throw new TypeError('Set only accepts integers') } }) integers.add(5) // OK integers.add(-50) // OK integers.add(5.5) // Throws TypeError integers.add(NaN) // Throws TypeError integers.add('foo') // Throws TypeError ``` ### [`HTML`](https://github.com/loilo/livy/tree/master/packages/util/src/html.ts) A function/template tag that allows you to easily construct HTML strings without having to worry about escaping: ```js const { HTML } = require('@livy/util/lib/html') const snippet = '<em>awesome</em>' HTML`<p>Markup is ${snippet}!</p>`.toString() // '<p>Markup is &lt;em&gt;awesome&lt;/em&gt;!</p>' // Use HTML as template tag in interpolations to avoid escaping: HTML`<p>Markup is ${HTML`<em>awesome</em>`}!</p>`.toString() // '<p>Markup is <em>awesome</em>!</p>' // Use HTML as a function to avoid escaping of variables: HTML`<p>Markup is ${HTML(snippet)}!</p>`.toString() // '<p>Markup is <em>awesome</em>!</p>' ``` ### [`isResettableInterface`](https://github.com/loilo/livy/tree/master/packages/util/src/is-resettable-interface.ts) Check whether a handler or processors is resettable. > See also: [`ResettableInterface`](https://npmjs.com/package/@livy/contracts#resettableinterface) ### [`Mixin`](https://github.com/loilo/livy/tree/master/packages/util/src/mixin.ts) An approach to use TypeScript mixins slightly altered from [Mixin classes](https://mariusschulz.com/blog/mixin-classes-in-typescript). Supports extending abstract classes and correctly type-checks `super()` calls for the tradeoff of having to wrap the mixin in an additional function call: ```ts // Mixin definition: const WriteAccess = Mixin( _ => class extends _ { write(file: string, content: string) { // Do some write action } } ) // Mixin usage: class User { constructor(protected name: string) {} } class PrivilegedUser extends WriteAccess(User) { constructor(name: string, protected role: 'editor' | 'admin') { super(name) // <- type-hinted! } otherMethod() { this.write('/some/file/path', 'some content') // <- type-hinted! } } ``` ### [`Timer`](https://github.com/loilo/livy/tree/master/packages/util/src/timer.ts) A cross-runtime performance timer implementation: ```js const { Timer } = require('@livy/util/lib/timer') const timer = new Timer() // Start the timer timer.start() // ... do some work ... // Get number of milliseconds elapsed since timer.start() timer.get() // ... do some work ... // Still get number of milliseconds elapsed since timer.start() timer.get() // Stop and reset the timer timer.reset() ``` ## Formatter-Related Utilities ### [`IncludedRecordProperties`](https://github.com/loilo/livy/tree/master/packages/util/src/formatters/included-record-properties.ts) A simple TypeScript type which represents an object with all [`LogRecord`](https://github.com/loilo/livy#log-records) properties mapped to boolean values. This can be useful as type of a formatter's option that determines which parts of a log record should be represented in the formatted output — see for example the [`LineFormatter`](https://github.com/loilo/livy/tree/master/packages/util/src/formatters/line-formatter.ts) implementation. ### [`AbstractBatchFormatter`](https://github.com/loilo/livy/tree/master/packages/util/src/formatters/abstract-batch-formatter.ts) A base class for formatters to extend which implements `formatBatch` as a series of `format` calls, joined by a delimiter (which by default is the `EOL` determined by the [environment](#Environment), can be changed by overriding the `batchDelimiter` property): ```js const { AbstractBatchFormatter } = require('@livy/util/lib/abstract-batch-formatter') class QuestionableFormatter extends AbstractBatchFormatter { format(record) { return '?' } } const q = new QuestionableFormatter() q.format({ ... }) // '?' q.formatBatch([{ ... }, { ... }, { ... }]) // '?\n?\n?' ``` > See also: [`FormatterInterface`](https://npmjs.com/package/@livy/contracts#formatterinterface) ### [`LineFomatter`](https://github.com/loilo/livy/tree/master/packages/util/src/formatters/line-formatter.ts) This formatter — because it's very ubiquitous throughout the project — is implemented here to remove complexity from the dependency graph (i.e. to avoid a mutual dependency with `@livy/line-formatter`). See [`@livy/line-formatter`](https://github.com/loilo/livy/tree/master/packages/line-formatter#readme) for documentation. ## Handler-Related Utilities This includes various base classes, mixins and check functions for implementing handlers. ### [`isClosableHandlerInterface`](https://github.com/loilo/livy/tree/master/packages/util/src/handlers/is-closable-handler-interface.ts) Check whether a handler is [closable](https://npmjs.com/package/@livy/contracts#closablehandlerinterface). ### [`isFormattableHandlerInterface`](https://github.com/loilo/livy/tree/master/packages/util/src/handlers/is-formattable-handler-interface.ts) Checks whether a handler implements the [`FormattableHandlerInterface`](https://npmjs.com/package/@livy/contracts#formattablehandlerinterface) ### [`isProcessableHandlerInterface`](https://github.com/loilo/livy/tree/master/packages/util/src/handlers/is-processable-handler-interface.ts) Check whether a handler can [use processors](https://npmjs.com/package/@livy/contracts#processablehandlerinterface). ### [`isSyncHandlerInterface`](https://github.com/loilo/livy/tree/master/packages/util/src/handlers/is-sync-handler-interface.ts) Check whether a handler can be [invoked synchronously](https://npmjs.com/package/@livy/contracts#handlerinterface). ### [`MirrorSyncHandlerMixin`](https://github.com/loilo/livy/tree/master/packages/util/src/handlers/mirror-sync-handler-mixin.ts) Implements the mandatory asynchronous handler methods `handle` and `handleBatch` by replicating the behavior of their corresponding synchronous methods: <!-- prettier-ignore --> ```js const SomeBaseClass = require('SomeBaseClass') const { MirrorSyncHandlerMixin } = require('@livy/util/lib/handlers/mirror-sync-handler-mixin') class Handler extends MirrorSyncHandlerMixin(SomeBaseClass) { handleSync(record) { // ... } handleBatchSync(record) { // ... } } const handler = new Handler() handler.handle() // calls handler.handleSync() handler.handleBatch() // calls handler.handleBatchSync() ``` > See also: [`SyncHandlerInterface`](https://npmjs.com/package/@livy/contracts#handlerinterface) ### [`ProcessableHandlerMixin`](https://github.com/loilo/livy/tree/master/packages/util/src/handlers/processable-handler-mixin.ts) Makes a handler able to work with processors, implementing the [`ProcessableHandlerInterface`](https://npmjs.com/package/@livy/contracts#processablehandlerinterface). It adds: - a public `processors` [set](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Set). - an internal `processRecord` method which your handler may call with a [log record](https://npmjs.com/package/@livy/contracts#logrecord) to run all registered processors on it. - an internal `resetProcessors` method which resets all resettable processors. - a basic `reset` method which calls `resetProcessors`, thus making the handler [resettable](#isresettableinterface) ### [`RespectLevelMixin`](https://github.com/loilo/livy/tree/master/packages/util/src/handlers/respect-level-mixin.ts) Adds a public `level` property which defaults to `'debug'`, as well as a `isHandling` implementation based on that level. > See also: [`HandlerInterface`](https://npmjs.com/package/@livy/contracts#handlerinterface) ### [`FormattableHandlerMixin`](https://github.com/loilo/livy/tree/master/packages/util/src/handlers/formattable-handler-mixin.ts) Adds a `formatter` property to the applied-to class with support for an implicit default formatter: ```js const SomeBaseClass = require('SomeBaseClass') const SomeFormatter = require('SomeFormatter') const { FormattableHandlerMixin } = require('@livy/util/lib/handlers/formattable-handler-mixin') class Handler extends FormattableHandlerMixin(SomeBaseClass) { /** * Allow setting a formatter through a handler option */ constructor({ formatter, ...options }) { // Pass other options up super(options) // Set the `this.explicitFormatter` property. // If the user provides no `formatter` option, it will be undefined // and `this.formatter` will return the default formatter this.explicitFormatter = formatter } /** * Define the default formatter (required) */ get defaultFormatter() { return new SomeFormatter() } handle(record) { // ... } } ``` > See also: [`FormattableHandlerInterface`](https://npmjs.com/package/@livy/contracts#formattablehandlerinterface) ### [`AbstractBatchHandler`](https://github.com/loilo/livy/tree/master/packages/util/src/handlers/abstract-batch-handler.ts) This is a base handler class that implements the `handleBatch` and `handleBatchSync` methods by sequentially executing `handle`/`handleSync` for each passed record. > See also: [`HandlerInterface`](https://npmjs.com/package/@livy/contracts#handlerinterface) ### [`AbstractLevelBubbleHandler`](https://github.com/loilo/livy/tree/master/packages/util/src/handlers/abstract-level-bubble-handler.ts) Base handler class (extending the [`AbstractBatchHandler`](#abstractbatchhandler)) which adds a `level` and a `bubble` option and implements the `isHandling` method based on the configured level: ```js const { AbstractLevelBubbleHandler } = require('@livy/util/lib/handlers/abstract-level-bubble-handler') class Handler extends AbstractLevelBubbleHandler { handle(record) { // ...do something with the record... // Indicate bubbling behavior based on the `bubble` option: return !this.bubble } } ``` See also: - [Bubbling](https://github.com/loilo/livy#bubbling) - [`HandlerInterface`](https://npmjs.com/package/@livy/contracts#handlerinterface) - [`SyncHandlerInterface`](https://npmjs.com/package/@livy/contracts#handlerinterface) ### [`AbstractFormattingProcessingHandler`](https://github.com/loilo/livy/tree/master/packages/util/src/handlers/abstract-formatting-processing-handler.ts) Base handler class extending the [`AbstractLevelBubbleHandler`](#abstractlevelbubblehandler) with the [`FormattableHandlerMixin`](#formattablehandlermixin) and [`ProcessableHandlerMixin`](#processablehandlermixin). It completely abstracts the nitty gritty details of writing a handler with formatter and processor support away from you so you only have to implement a `write` method: ```js const { AbstractFormattingProcessingHandler } = require('@livy/util/lib/handlers/abstract-formatting-processing-handler') class FileHandler extends AbstractFormattingProcessingHandler { async write(record, formattedRecord) { await someFileHandler.write(formattedRecord) } } ``` There's also `AbstractSyncFormattingProcessingHandler` to implement a synchronous handler by implementing `writeSync`: ```js const { AbstractSyncFormattingProcessingHandler } = require('@livy/util/lib/handlers/abstract-formatting-processing-handler') class SyncFileHandler extends AbstractSyncFormattingProcessingHandler { writeSync(record, formattedRecord) { someFileHandler.writeSync(formattedRecord) } // You can still implement your own asynchronous `write` method, // but if you omit it, it will just fall back to `writeSync` instead async write(record, formattedRecord) { await someFileHandler.write(formattedRecord) } } ```