diff --git a/CHANGELOG.md b/CHANGELOG.md index 278cc25..cc9e316 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ## master +- Add `performFailures: 'throw' | 'warn' | 'ignore'` option to `createCable()`. ([@palkan][]) + ## 0.9.1 (2024-07-31) - Add `info` event to Cable and Channel. ([@palkan][]) diff --git a/README.md b/README.md index 410f28c..ba8a611 100644 --- a/README.md +++ b/README.md @@ -728,6 +728,23 @@ export default createCable({ }); ``` +## Other configuration options + +There is a plenty of options available to configure the client. Please, refer to the full list in the [TS definition file](./packages/core/cable/index.d.ts). +Here are the most popular/useful ones (and not yet mentioned in the docs): + +```js +import { createCable } from '@anycable/core' + +// Below you can find some options and their default values +const cable = createCable({ + logLevel: 'info', // use 'debug' for more verbose output and troubleshooting + performFailures: 'throw', // indicates how to treat channel.perform(...) failures; use `warn` or 'ignore' to silence errors + lazy: true, // if true, the connection would be established only when the first subscription is made + logger: console, // custom logger (must implement `log`, `info`, `warn`, `error`, `debug` methods) +}) +``` + ## Further reading - [Architecture](./docs/architecture.md) diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 5e7da2d..1d0ac29 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -2,6 +2,8 @@ ## master +- Add `performFailures: 'throw' | 'warn' | 'ignore'` option to `createCable()`. ([@palkan][]) + ## 0.9.1 (2024-07-31) - Add `info` event to Cable and Channel. ([@palkan][]) diff --git a/packages/core/cable/index.d.ts b/packages/core/cable/index.d.ts index 740239d..8ca4399 100644 --- a/packages/core/cable/index.d.ts +++ b/packages/core/cable/index.d.ts @@ -40,6 +40,7 @@ export type CableOptions = { logger?: Logger lazy?: boolean hubOptions?: HubOptions + performFailures?: 'throw' | 'warn' | 'ignore' } export type CableState = diff --git a/packages/core/cable/index.js b/packages/core/cable/index.js index b879497..1cbcdcf 100644 --- a/packages/core/cable/index.js +++ b/packages/core/cable/index.js @@ -50,12 +50,21 @@ export class PubSubChannel extends Channel { export const STATE = Symbol('state') export class Cable { - constructor({ transport, protocol, encoder, logger, lazy, hubOptions }) { + constructor({ + transport, + protocol, + encoder, + logger, + lazy, + hubOptions, + performFailures + }) { this.emitter = createNanoEvents() this.transport = transport this.encoder = encoder this.logger = logger || new NoopLogger() this.protocol = protocol + this.performFailures = performFailures || 'throw' this.protocol.attached(this) @@ -566,6 +575,21 @@ export class Cable { } async perform(identifier, action, payload) { + if (this.performFailures === 'throw') { + return this._perform(identifier, action, payload) + } + + try { + return await this._perform(identifier, action, payload) + } catch (err) { + if (this.performFailures === 'warn') { + this.logger.warn('perform failed', { error: err }) + } + return undefined + } + } + + async _perform(identifier, action, payload) { if (this.state === 'connecting') { await this.pendingConnect() } diff --git a/packages/core/cable/index.test.ts b/packages/core/cable/index.test.ts index 916a720..beadded 100644 --- a/packages/core/cable/index.test.ts +++ b/packages/core/cable/index.test.ts @@ -18,7 +18,7 @@ import { } from '../index.js' import { TestTransport } from '../transport/testing' import { TestLogger } from '../logger/testing' -import { PUBSUB_CHANNEL, PubSubChannel } from './index.js' +import { PUBSUB_CHANNEL, PubSubChannel, CableOptions } from './index.js' class TestProtocol implements Protocol { cable!: Cable @@ -101,18 +101,22 @@ let transport: TestTransport let logger: TestLogger let cable: Cable let encoder: Encoder +let cableOptions: CableOptions beforeEach(() => { logger = new TestLogger() transport = new TestTransport('ws:///') encoder = new JSONEncoder() protocol = new TestProtocol() - cable = new Cable({ + + cableOptions = { protocol, encoder, logger, transport - }) + } + + cable = new Cable(cableOptions) }) describe('initialize', () => { @@ -887,9 +891,33 @@ describe('channels', () => { cable.closed() - return expect( + await expect( cable.perform(channel.identifier, 'do', { foo: 'bar' }) ).rejects.toEqual(Error('No connection')) + + let cable2 = new Cable({ ...cableOptions, performFailures: 'warn' }) + cable2.connect() + cable2.connected() + let channel2 = cable2.subscribeTo('test') + await channel2.ensureSubscribed() + cable2.closed() + + await expect( + cable2.perform(channel.identifier, 'do', { foo: 'bar' }) + ).resolves.toBeUndefined() + expect(logger.warnings).toHaveLength(1) + + let cable3 = new Cable({ ...cableOptions, performFailures: 'ignore' }) + cable3.connect() + cable3.connected() + let channel3 = cable3.subscribeTo('test') + await channel3.ensureSubscribed() + cable3.closed() + + await expect( + cable3.perform(channel.identifier, 'do', { foo: 'bar' }) + ).resolves.toBeUndefined() + expect(logger.warnings).toHaveLength(1) }) it('send when closed', () => {