Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

## [v2.0.4] - 2026-04-13

### Changed
- Refactor to use `@aegisjsproject/disposable-registry`

### [v2.0.3] - 2026-03-15

### Fixed
Expand Down
11 changes: 6 additions & 5 deletions callbackRegistry.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import '@shgysk8zer0/polyfills';
import './shims.js';
import { createCallback, unregisterCallback, hasCallback, callCallback, closeRegistration, registerCallback, CallbackRegistryKey } from '@aegisjsproject/callback-registry/callbacks.js';
import { createCallback, unregisterCallback, hasCallback, callCallback, closeRegistration, registerCallback } from '@aegisjsproject/callback-registry/callbacks.js';
import { RegistryKey } from '@aegisjsproject/disposable-registry';
import { describe, test } from 'node:test';
import assert from 'node:assert';

Expand All @@ -9,7 +10,7 @@ const sum = createCallback((...nums) => nums.reduce((sum, num) => sum + num));

describe('Test callback registry', () => {
test('Creating a callback should return the string key.', { signal }, () => {
assert.ok(sum instanceof CallbackRegistryKey, 'Callback key should be a `CallbackRegistryKey`.');
assert.ok(sum instanceof RegistryKey, 'Callback key should be a `RegistryKey`.');
});

test('Callbacks are registered correctly', { signal }, () => {
Expand All @@ -26,9 +27,9 @@ describe('Test callback registry', () => {
});

test('Check callback registry with optional `stack`', { signal }, () => {
const stack = new DisposableStack();
const cb = registerCallback('disposable:log', console.log, { stack });
assert.ok(cb instanceof CallbackRegistryKey, 'Registering callbacks should return disposable String keys.');
using stack = new DisposableStack();
using cb = registerCallback('disposable:log', console.log, { stack });
assert.ok(cb instanceof RegistryKey, 'Registering callbacks should return disposable String keys.');
assert.ok(hasCallback(cb), 'Callback should be registered');
stack.dispose();
assert.ok(! hasCallback(cb), 'Callback should be unregistered after disposal.');
Expand Down
54 changes: 24 additions & 30 deletions callbacks.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@ import {
registerEventAttribute, hasEventAttribute, registerSignal, abortController,
} from './events.js';

import {
Registry, RegistryKey, getFromRegistry, hasRegistryKey, registerKey, unregisterKey, listRegistryKeys,
} from '@aegisjsproject/disposable-registry';

let _isRegistrationOpen = true;

const $$ = (selector, base = document) => base.querySelectorAll(selector);
Expand Down Expand Up @@ -56,9 +60,9 @@ export const FUNCS = {
};

/**
* @type {Map<string, function>}
* @type {Registry<string, function>}
*/
const registry = new Map([
const registry = new Registry([
[FUNCS.debug.log, console.log],
[FUNCS.debug.warn, console.warn],
[FUNCS.debug.error, console.error],
Expand Down Expand Up @@ -170,11 +174,7 @@ const registry = new Map([
[FUNCS.ui.exitFullsceen, () => document.exitFullscreen()],
]);

export class CallbackRegistryKey extends String {
[Symbol.dispose]() {
registry.delete(this.toString());
}
}
export const CallbackRegistryKey = RegistryKey;

/**
* Check if callback registry is open
Expand All @@ -195,31 +195,31 @@ export const closeRegistration = () => _isRegistrationOpen = false;
*
* @returns {Array} A frozen array listing keys to all registered callbacks
*/
export const listCallbacks = () => Object.freeze(Array.from(registry.keys()));
export const listCallbacks = () => listRegistryKeys(registry);

/**
* Check if a callback is registered
*
* @param {CallbackRegistryKey|string} name The name/key to check for in callback registry
* @returns {boolean} Whether or not a callback is registered
*/
export const hasCallback = name => registry.has(name?.toString());
export const hasCallback = name => hasRegistryKey(name, registry);

/**
* Get a callback from the registry by name/key
*
* @param {CallbackRegistryKey|string} name The name/key of the callback to get
* @param {RegistryKey|string} name The name/key of the callback to get
* @returns {Function|undefined} The corresponding function registered under that name/key
*/
export const getCallback = name => registry.get(name?.toString());
export const getCallback = name => getFromRegistry(name, registry);

/**
* Remove a callback from the registry
*
* @param {CallbackRegistryKey|string} name The name/key of the callback to get
* @param {RegistryKey|string} name The name/key of the callback to get
* @returns {boolean} Whether or not the callback was successfully unregisterd
*/
export const unregisterCallback = name => _isRegistrationOpen && registry.delete(name?.toString());
export const unregisterCallback = name => _isRegistrationOpen && unregisterKey(name, registry);

/**
* Remove all callbacks from the registry
Expand All @@ -234,21 +234,21 @@ export const clearRegistry = () => registry.clear();
* @param {Function} callback Callback function to register
* @param {object} [config]
* @param {DisposableStack|AsyncDisposableStack} [config.stack] Optional `DisposableStack` to handle disposal and unregistering.
* @returns {CallbackRegistryKey} The automatically generated key/name of the registered callback
* @returns {RegistryKey} The automatically generated key/name of the registered callback
*/
export const createCallback = (callback, { stack } = {}) => registerCallback('aegis:callback:' + crypto.randomUUID(), callback, { stack });

/**
* Call a callback fromt the registry by name/key
*
* @param {CallbackRegistryKey|string} name The name/key of the registered function
* @param {RegistryKey|string} name The name/key of the registered function
* @param {...any} args Any arguments to pass along to the function
* @returns {any} Whatever the return value of the function is
* @throws {Error} Throws if callback is not found or any error resulting from calling the function
*/
export function callCallback(name, ...args) {
if (registry.has(name?.toString())) {
return registry.get(name?.toString()).apply(this || globalThis, args);
if (hasRegistryKey(name, registry)) {
return getFromRegistry(name, registry)?.apply?.(this || globalThis, args);
} else {
throw new Error(`No ${name} function registered.`);
}
Expand All @@ -257,35 +257,29 @@ export function callCallback(name, ...args) {
/**
* Register a named callback in registry
*
* @param {CallbackRegistryKey|string} name The name/key to register the callback under
* @param {RegistryKey|string} name The name/key to register the callback under
* @param {Function} callback The callback value to register
* @param {object} config
* @param {DisposableStack|AsyncDisposableStack} [config.stack] Optional `DisposableStack` to handle disposal and unregistering.
* @returns {CallbackRegistryKey} The registered name/key
* @returns {RegistryKey} The registered name/key
*/
export function registerCallback(name, callback, { stack } = {}) {
if (typeof name === 'string') {
return registerCallback(new CallbackRegistryKey(name), callback, { stack });
} else if (! (name instanceof CallbackRegistryKey)) {
return registerCallback(new RegistryKey(name), callback, { stack });
} else if (! (name instanceof RegistryKey)) {
throw new TypeError('Callback name must be a disposable string/CallbackRegistryKey.');
} else if (typeof callback === 'object' && typeof callback.handleEvent === 'function') {
return registerCallback(name, callback.handleEvent.bind(callback), { stack });
} else if (! (typeof callback === 'function' || typeof callback?.handleEvent === 'function')) {
throw new TypeError('Callback must be a function.');
} else if (! _isRegistrationOpen) {
throw new TypeError('Cannot register new callbacks because registry is closed.');
} else if (registry.has(name?.toString())) {
} else if (hasRegistryKey(name, registry)) {
throw new Error(`Handler "${name}" is already registered.`);
} else if (stack instanceof DisposableStack || stack instanceof AsyncDisposableStack) {
const key = stack.use(new CallbackRegistryKey(name));
registry.set(key.toString(), callback);

return key;
return stack.use(registerKey(name, callback, registry));
} else {
const key = new CallbackRegistryKey(name);
registry.set(key.toString(), callback);

return key;
return registerKey(name, callback, registry);
}
}

Expand Down
65 changes: 59 additions & 6 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@aegisjsproject/callback-registry",
"version": "2.0.3",
"version": "2.0.4",
"description": " A callback registry for AegisJSProject",
"keywords": [
"aegis",
Expand Down Expand Up @@ -79,13 +79,15 @@
"devDependencies": {
"@rollup/plugin-terser": "^1.0.0",
"@shgysk8zer0/eslint-config": "^1.0.7",
"@shgysk8zer0/importmap": "^1.8.17",
"@shgysk8zer0/polyfills": "^0.6.2",
"@shgysk8zer0/rollup-import": "^2.0.3",
"eslint": "^10.0.3",
"http-server": "^14.1.1",
"rollup": "^4.59.0"
},
"dependencies": {
"@aegisjsproject/disposable-registry": "^1.0.2",
"@shgysk8zer0/signals": "^0.0.3"
}
}
6 changes: 4 additions & 2 deletions rollup.config.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import terser from '@rollup/plugin-terser';

const external = ['@shgysk8zer0/signals', '@aegisjsproject/disposable-callbacks'];

export default [{
input: 'callbackRegistry.js',
external: ['@shgysk8zer0/signals'],
external,
output: [{
file: 'callbackRegistry.cjs',
format: 'cjs',
Expand All @@ -14,7 +16,7 @@ export default [{
}],
}, {
input: 'callbacks.js',
external: ['@shgysk8zer0/signals'],
external,
output: [{
file: 'callbacks.cjs',
format: 'cjs',
Expand Down
Loading