Skip to content

Commit

Permalink
refactor: harmonize error messages + spelling
Browse files Browse the repository at this point in the history
  • Loading branch information
b-ma committed Jan 20, 2025
1 parent b23d1e9 commit b3b3a4a
Show file tree
Hide file tree
Showing 30 changed files with 343 additions and 329 deletions.
50 changes: 21 additions & 29 deletions src/client/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -74,17 +74,17 @@ class Client {
*/
constructor(config) {
if (!isPlainObject(config)) {
throw new Error(`[soundworks:Client] Cannot instanciate Client: argument 1 should be an object`);
throw new Error(`Cannot construct Client: argument 1 must be an object`);
}

if (!('role' in config) || !isString(config.role)) {
throw new Error('[soundworks:Client] Cannot instanciate Client: Invalid ClientConfig object: property "role" is not a string defined');
throw new Error('Cannot construct Client: Invalid ClientConfig object: Property "role" must be a string');
}

// for node clients env.https is required to open the websocket
if (!isBrowser()) {
if (!('env' in config)) {
throw new Error('[soundworks:Client] Cannot instanciate Client: `config.env: ClientEnvConfig { useHttps, serverAddress, port }` must be defined');
throw new Error(`Cannot construct Client: The 'env' property with type 'ClientEnvConfig' must be defined`);
}

let missing = [];
Expand All @@ -102,7 +102,7 @@ class Client {
}

if (missing.length) {
throw new Error(`[soundworks:Client] Invalid config object, "config.env" is missing: ${missing.join(', ')}`);
throw new Error(`Cannot construct Client: Invalid 'env' property of type 'ClientEnvConfig': Fields ${missing.join(', ')} are missing`);
}
}

Expand Down Expand Up @@ -164,8 +164,8 @@ class Client {
/**
* Session id of the client.
*
* Incremeted positive integer generated and retrieved by the server during
* `client.init`. The counter is reset when the server restarts.
* Incremented positive integer generated and retrieved by the server during
* {@link Client#init}. The counter is reset when the server restarts.
*
* @type {number}
*/
Expand Down Expand Up @@ -296,11 +296,15 @@ class Client {
* import { Client } from '@soundworks/core/client.js'
*
* const client = new Client(config);
* // optionnal explicit call of `init` before `start`
* // optional explicit call of `init` before `start`
* await client.init();
* await client.start();
*/
async init() {
if (this.#status !== 'idle') {
throw new DOMException(`Cannot execute 'init' on Client: Lifecycle methods must be called in following order: init, start, stop`, 'InvalidAccessError');
}

// init socket communications
await this.#socket.init();

Expand All @@ -320,22 +324,9 @@ class Client {
});

this.#socket.addListener(CLIENT_HANDSHAKE_ERROR, (err) => {
let msg = ``;

switch (err.type) {
case 'invalid-client-type':
msg = `[soundworks:Client] ${err.message}`;
break;
case 'invalid-plugin-list':
msg = `[soundworks:Client] ${err.message}`;
break;
default:
msg = `[soundworks:Client] Undefined error: ${err.message}`;
break;
}

let msg = `Cannot execute 'init' on Client: ${err.message}`;
// These are development errors, we can just hang. If we terminate the
// socket, a reload is triggered by the launcher which is bad in terms of DX
// socket, a reload is triggered by the launcher which is not good DX.
reject(msg);
});

Expand Down Expand Up @@ -381,12 +372,13 @@ class Client {
* await client.start();
*/
async start() {
// lazily call init for convenience
if (this.#status === 'idle') {
await this.init();
}

if (this.#status === 'started') {
throw new Error(`[soundworks:Server] Cannot call "client.start()" twice`);
if (this.#status !== 'inited') {
throw new DOMException(`Cannot execute 'start' on Client: Lifecycle methods must be called in following order: init, start, stop`, 'InvalidAccessError');
}

await this.#contextManager[kClientContextManagerStart]();
Expand All @@ -397,7 +389,7 @@ class Client {
* Stops all started contexts, plugins and terminates the socket connections.
*
* In most situations, you might not need to call this method. However, it can
* be usefull for unit testing or similar situations where you want to create
* be useful for unit testing or similar situations where you want to create
* and delete several clients in the same process.
*
* @example
Expand All @@ -411,7 +403,7 @@ class Client {
*/
async stop() {
if (this.#status !== 'started') {
throw new Error(`[soundworks:Client] Cannot "client.stop()" before "client.start()"`);
throw new DOMException(`Cannot execute 'stop' on Client: Lifecycle methods must be called in following order: init, start, stop`, 'InvalidAccessError');
}

await this.#contextManager[kClientContextManagerStop]();
Expand All @@ -425,8 +417,8 @@ class Client {
* Attach and retrieve the global audit state of the application.
*
* The audit state is a {@link SharedState} instance that keeps track of
* global informations about the application such as, the number of connected
* clients, network latency estimation, etc. It is usefull for controller client
* global information about the application such as, the number of connected
* clients, network latency estimation, etc. It is useful for controller client
* roles to give the user an overview about the state of the application.
*
* The audit state is lazily attached to the client only if this method is called.
Expand All @@ -440,7 +432,7 @@ class Client {
*/
async getAuditState() {
if (this.#status === 'idle') {
throw new Error(`[soundworks.Client] Cannot access audit state before "client.init()"`);
throw new DOMException(`Cannot execute 'getAuditState' on Server: 'init' must be called first`, 'InvalidAccessError');
}

if (this.#auditState === null) {
Expand Down
16 changes: 8 additions & 8 deletions src/client/ClientContext.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ const promiseStore = new PromiseStore('Context');
* In the `soundworks` paradigm, a client has a "role" (e.g. _player_, _controller_)
* see {@link Client#role}) and can be in different "contexts" (e.g. different
* part of the experience such as sections of a music piece, etc.). This class
* provides a simple and unified way to model these reccuring aspects of an application.
* provides a simple and unified way to model these recurring aspects of an application.
*
* You can also think of a `Context` as a state of a state machine from which a
* client can `enter()` or `exit()` (be aware that `soundworks` does not provide
Expand Down Expand Up @@ -68,7 +68,7 @@ class ClientContext {
*/
constructor(client) {
if (!(client instanceof Client)) {
throw new Error(`[soundworks:ClientContext] Invalid argument, context "${this.name}" should receive a "soundworks.Client" instance as first argument`);
throw new TypeError(`Cannot create 'ClientContext' (${this.name}): Argument 1 must be an instance of Client`);
}

this.#client = client;
Expand All @@ -87,7 +87,7 @@ class ClientContext {
return;
}

promiseStore.reject(reqId, msg);
promiseStore.reject(reqId, `Cannot execute 'enter' on Context: ${msg}`);
});

this.#client.socket.addListener(CONTEXT_EXIT_RESPONSE, (reqId, contextName) => {
Expand All @@ -103,7 +103,7 @@ class ClientContext {
return;
}

promiseStore.reject(reqId, msg);
promiseStore.reject(reqId, `Cannot execute 'exit' on Context: ${msg}`);
});

this.#client.contextManager[kClientContextManagerRegister](this);
Expand All @@ -126,7 +126,7 @@ class ClientContext {
}

/**
* Optionnal user-defined name of the context (defaults to the class name).
* Optional user-defined name of the context (defaults to the class name).
*
* The context manager will match the client-side and server-side contexts based
* on this name. If the {@link ServerContextManager} don't find a corresponding
Expand All @@ -147,10 +147,10 @@ class ClientContext {
}

/**
* Start the context. This method is lazilly called when the client enters the
* Start the context. This method is lazily called when the client enters the
* context for the first time (cf. ${ClientContext#enter}). If you know some
* some heavy and/or potentially long job has to be done when starting the context
* (e.g. call to a web service) it may be a good practice to call it explicitely.
* (e.g. call to a web service) it may be a good practice to call it explicitly.
*
* This method should be implemented to perform operations that are valid for the
* whole lifetime of the context, regardless the client enters or exits the context.
Expand Down Expand Up @@ -190,7 +190,7 @@ class ClientContext {
* {@link ClientContext#name}), the corresponding server-side `enter()` method
* will be executed before the returned Promise is fulfilled.
*
* If the context has not been started yet, the `start` method is implicitely executed.
* If the context has not been started yet, the `start` method is implicitly executed.
*
* @returns {Promise<void>} - Promise that resolves when the context is entered.
* @example
Expand Down
6 changes: 3 additions & 3 deletions src/client/ClientContextManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class ClientContextManager {
*/
[kClientContextManagerRegister](context) {
if (this.#contexts.has(context.name)) {
throw new Error(`[soundworks:context-manager] Context "${context.name}" already registered`);
throw new DOMException(`Cannot register '${context.name}': a Context with same name has already been registered`, 'NotSupportedError');
}

this.#contexts.add(context);
Expand Down Expand Up @@ -86,7 +86,7 @@ class ClientContextManager {
*/
async get(contextName) {
if (!this.#contexts.has(contextName)) {
throw new Error(`[soundworks:ClientContextManager] Can't get context "${contextName}", not registered`);
throw new ReferenceError(`Cannot execute 'get' on ClientContextManager: Context '${contextName}' is not registered`);
}

const context = this.#contexts.get(contextName);
Expand All @@ -104,7 +104,7 @@ class ClientContextManager {
context[kClientContextStatus] = 'started';
} catch (err) {
context[kClientContextStatus] = 'errored';
throw new Error(err);
throw new Error(`Cannot execute 'get' on ClientContextManager: ${err.message}`);
}
}

Expand Down
2 changes: 1 addition & 1 deletion src/client/ClientPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ import BasePlugin from '../common/BasePlugin.js';
* have both a client-side and a server-side part.
*
* See [https://soundworks.dev/guide/ecosystem](https://soundworks.dev/guide/ecosystem)
* for more informations on the available plugins.
* for more information on the available plugins.
*
* _Creating new plugins should be considered an advanced usage._
*
Expand Down
17 changes: 11 additions & 6 deletions src/client/ClientPluginManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import Client from './Client.js';
* and before {@link Client#start} or {@link Server#start}
* to be properly initialized.
*
* In some sitautions, you might want to register the same plugin factory several times
* In some situations, you might want to register the same plugin factory several times
* using different ids (e.g. for watching several parts of the file system, etc.).
*
* Refer to the plugins' documentation for more precise examples, and the specific API
* they expose. See [https://soundworks.dev/guide/ecosystem](https://soundworks.dev/guide/ecosystem)
* for more informations on the available plugins.
* for more information on the available plugins.
*
* See {@link Client#pluginManager}
*
Expand Down Expand Up @@ -66,7 +66,7 @@ class ClientPluginManager extends BasePluginManager {
*/
constructor(client) {
if (!(client instanceof Client)) {
throw new Error(`[soundworks.ClientPluginManager] Invalid argument, "new ClientPluginManager(client)" should receive an instance of "soundworks.Client" as argument`);
throw new TypeError(`Cannot construct ClientPluginManager: Argument 1 must be an instance of Client`);
}

super(client);
Expand Down Expand Up @@ -96,16 +96,21 @@ class ClientPluginManager extends BasePluginManager {
* server.pluginManager.register('user-defined-id', pluginFactory);
*/
register(id, factory, options = {}, deps = []) {
// Note that additional argument checks are done on the BasePluginManager

// @todo - review all this
const ctor = factory(ClientPlugin);

if (!(ctor.prototype instanceof ClientPlugin)) {
throw new Error(`[ClientPluginManager] Invalid argument, "pluginManager.register" second argument should be a factory function returning a class extending the "Plugin" base class`);
throw new TypeError(`Cannot execute 'register' on ClientPluginManager: argument 2 must be a factory function returning a class extending the "ClientPlugin" base class`);
}

if (ctor.target === undefined || ctor.target !== 'client') {
throw new Error(`[ClientPluginManager] Invalid argument, The plugin class should implement a 'target' static field with value 'client'`);
throw new TypeError(`Cannot execute 'register' on ClientPluginManager: The plugin class must implement a 'target' static field with value 'client'`);
}

// @todo - check deps

super.register(id, ctor, options, deps);
}

Expand All @@ -130,7 +135,7 @@ class ClientPluginManager extends BasePluginManager {
*/
async get(id) {
if (this.status !== 'started') {
throw new Error(`[soundworks.ClientPluginManager] Cannot get plugin before "client.init()"`);
throw new DOMException(`Cannot execute 'get' on ClientPluginManager: 'Client#init' has not been called yet`, 'NotSupportedError');
}

return super.getUnsafe(id);
Expand Down
8 changes: 4 additions & 4 deletions src/client/ClientSocket.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ class ClientSocket {
}

if (this.#config.env.useHttps && window.location.hostname !== serverAddress) {
console.warn(`The WebSocket will try to connect at ${serverAddress} while the page is accessed from ${hostname}. This can lead to socket errors, e.g. If you run the application with self-signed certificates as the certificate may not have been accepted for ${serverAddress}. In such case you should rather access the page from ${serverAddress}.`);
console.warn(`ClientSocket will try to connect at ${serverAddress} while the page is accessed from ${hostname}. This can lead to socket errors, e.g. If you run the application with self-signed certificates as the certificate may not have been accepted for ${serverAddress}. In such case you should rather access the page from ${serverAddress}.`);
}

webSocketOptions = [];
Expand Down Expand Up @@ -121,7 +121,7 @@ class ClientSocket {
// WebSocket "native" events:
// - `close`: Fired when a connection with a websocket is closed.
// - `error`: Fired when a connection with a websocket has been closed
// because of an error, such as whensome data couldn't be sent.
// because of an error, such as when some data couldn't be sent.
// - `message`: Fired when data is received through a websocket.
// - `open`: Fired when a connection with a websocket is opened.

Expand Down Expand Up @@ -157,7 +157,7 @@ class ClientSocket {
ws.terminate ? ws.terminate() : ws.close();

if (e.error) {
const msg = `[Socket Error] code: ${e.error.code}, message: ${e.error.message}`;
const msg = `ClientSocket error - code: ${e.error.code}, message: ${e.error.message}`;
logger.log(msg);
}

Expand All @@ -177,7 +177,7 @@ class ClientSocket {
*
* Is also called when a disconnection is detected by the heartbeat (note that
* in this case, the launcher will call `client.stop()` but the listeners are
* already cleared so the event will be trigerred only once.
* already cleared so the event will be triggered only once.
*
* @private
*/
Expand Down
10 changes: 5 additions & 5 deletions src/common/BasePlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class BasePlugin {
/**
* Type of the plugin, i.e. the ClassName.
*
* Usefull to do perform some logic based on certain types of plugins without
* Useful to do perform some logic based on certain types of plugins without
* knowing under which `id` they have been registered. (e.g. creating some generic
* views, etc.)
*
Expand All @@ -78,11 +78,11 @@ class BasePlugin {
/**
* Start the plugin.
*
* This method is automatically called during the client or server `init()` lifecyle
* This method is automatically called during the client or server `init()` lifecycle
* step. After `start()` is fulfilled the plugin should be ready to use.
*
* @example
* // server-side couterpart of a plugin that creates a dedicated global shared
* // server-side counterpart of a plugin that creates a dedicated global shared
* // state on which the server-side part can attach.
* class MyPlugin extends ServerPlugin {
* constructor(server, id) {
Expand Down Expand Up @@ -112,10 +112,10 @@ class BasePlugin {
/**
* Stop the plugin.
*
* This method is automatically called during the client or server `stop()` lifecyle step.
* This method is automatically called during the client or server `stop()` lifecycle step.
*
* @example
* // server-side couterpart of a plugin that creates a dedicated global shared
* // server-side counterpart of a plugin that creates a dedicated global shared
* // state on which the client-side part can attach.
* class MyPlugin extends ServerPlugin {
* constructor(server, id) {
Expand Down
Loading

0 comments on commit b3b3a4a

Please sign in to comment.