diff --git a/CHANGELOG.md b/CHANGELOG.md index a6bb7d2..70c2043 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 diff --git a/callbackRegistry.test.js b/callbackRegistry.test.js index 7bfff99..a07ee87 100644 --- a/callbackRegistry.test.js +++ b/callbackRegistry.test.js @@ -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'; @@ -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 }, () => { @@ -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.'); diff --git a/callbacks.js b/callbacks.js index ed48d04..7502072 100644 --- a/callbacks.js +++ b/callbacks.js @@ -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); @@ -56,9 +60,9 @@ export const FUNCS = { }; /** - * @type {Map} + * @type {Registry} */ -const registry = new Map([ +const registry = new Registry([ [FUNCS.debug.log, console.log], [FUNCS.debug.warn, console.warn], [FUNCS.debug.error, console.error], @@ -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 @@ -195,7 +195,7 @@ 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 @@ -203,23 +203,23 @@ export const listCallbacks = () => Object.freeze(Array.from(registry.keys())); * @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 @@ -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.`); } @@ -257,16 +257,16 @@ 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 }); @@ -274,18 +274,12 @@ export function registerCallback(name, callback, { stack } = {}) { 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); } } diff --git a/package-lock.json b/package-lock.json index f00bd28..30da3ce 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@aegisjsproject/callback-registry", - "version": "2.0.3", + "version": "2.0.4", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@aegisjsproject/callback-registry", - "version": "2.0.3", + "version": "2.0.4", "funding": [ { "type": "librepay", @@ -19,11 +19,13 @@ ], "license": "MIT", "dependencies": { + "@aegisjsproject/disposable-registry": "^1.0.2", "@shgysk8zer0/signals": "^0.0.3" }, "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", @@ -34,6 +36,25 @@ "node": ">=18.0.0" } }, + "node_modules/@aegisjsproject/disposable-registry": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@aegisjsproject/disposable-registry/-/disposable-registry-1.0.2.tgz", + "integrity": "sha512-cPporOr5oD5sIqm8wSXjCWYbgbaN9B2nr3AFRGQTq+l/mnhdxniRtkIczbvYHmHUBrTBCCj+ELCsAXv51Y8ZJg==", + "funding": [ + { + "type": "librepay", + "url": "https://liberapay.com/shgysk8zer0" + }, + { + "type": "github", + "url": "https://github.com/sponsors/shgysk8zer0" + } + ], + "license": "MIT", + "engines": { + "node": ">=24.10.0" + } + }, "node_modules/@aegisjsproject/sanitizer": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/@aegisjsproject/sanitizer/-/sanitizer-0.2.4.tgz", @@ -794,15 +815,44 @@ "node": ">=18.0.0" } }, + "node_modules/@shgysk8zer0/importmap": { + "version": "1.8.17", + "resolved": "https://registry.npmjs.org/@shgysk8zer0/importmap/-/importmap-1.8.17.tgz", + "integrity": "sha512-MgQ5ikFDXXlfgTh+YMUEOK9K/7NZNyl34KiHa8xZRD+hfQIqOoZwEsXayJzHjx/gZFTVb2R/VPfApnEK36rQ4A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@shgysk8zer0/npm-utils": "^1.1.6", + "@shgysk8zer0/polyfills": "^0.6.3", + "commander": "^14.0.3" + }, + "bin": { + "importmap-html": "html.js", + "importmap-utils": "cli.js" + }, + "engines": { + "node": ">=20.10.0" + } + }, + "node_modules/@shgysk8zer0/importmap/node_modules/commander": { + "version": "14.0.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-14.0.3.tgz", + "integrity": "sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + } + }, "node_modules/@shgysk8zer0/npm-utils": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/@shgysk8zer0/npm-utils/-/npm-utils-1.1.4.tgz", - "integrity": "sha512-WRucTC6HCWWHYw/NfQ6NWv+gKi8MCzaT6XBpcCZr9i304Yes27ZkKccCOyNCV5TO14FX05QSCNVn6TTuswdWUw==", + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@shgysk8zer0/npm-utils/-/npm-utils-1.1.6.tgz", + "integrity": "sha512-OGyhTUT1GKBbsE6YilaZceWLoai0YsidU02QSWYS4IDDzv7o0vxejevNn0UpI2t1b+kjiD3gHerMfllzH0YM7g==", "dev": true, "license": "MIT", "dependencies": { "@shgysk8zer0/consts": "^1.0.8", - "@shgysk8zer0/polyfills": "^0.6.2", + "@shgysk8zer0/polyfills": "^0.6.3", "js-yaml": "^4.1.1" }, "engines": { @@ -912,6 +962,7 @@ "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -1221,6 +1272,7 @@ "integrity": "sha512-+L0vBFYGIpSNIt/KWTpFonPrqYvgKw1eUI5Vn7mEogrQcWtWYtNQ7dNqC+px/J0idT3BAkiWrhfS7k+Tum8TUA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.2", @@ -2109,6 +2161,7 @@ "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@types/estree": "1.0.8" }, diff --git a/package.json b/package.json index 7db070d..44e5234 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@aegisjsproject/callback-registry", - "version": "2.0.3", + "version": "2.0.4", "description": " A callback registry for AegisJSProject", "keywords": [ "aegis", @@ -79,6 +79,7 @@ "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", @@ -86,6 +87,7 @@ "rollup": "^4.59.0" }, "dependencies": { + "@aegisjsproject/disposable-registry": "^1.0.2", "@shgysk8zer0/signals": "^0.0.3" } } diff --git a/rollup.config.js b/rollup.config.js index 99224bf..cf08643 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -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', @@ -14,7 +16,7 @@ export default [{ }], }, { input: 'callbacks.js', - external: ['@shgysk8zer0/signals'], + external, output: [{ file: 'callbacks.cjs', format: 'cjs',