From 41820baba6df523e7fdf9cd82a47d62bd54328ae Mon Sep 17 00:00:00 2001 From: KernelDeimos <7225168+KernelDeimos@users.noreply.github.com> Date: Tue, 30 Sep 2025 12:41:47 -0400 Subject: [PATCH] dev: add extension support in DriverService Adds new events that DriverService emits: `create.interfaces` and `create.drivers`. Returns the functionality of the "drivers" registry, which now expects objects provided by extensions. --- .../src/services/drivers/DriverService.js | 100 ++++++++++++++---- 1 file changed, 79 insertions(+), 21 deletions(-) diff --git a/src/backend/src/services/drivers/DriverService.js b/src/backend/src/services/drivers/DriverService.js index 54be460ce..c9be4cadf 100644 --- a/src/backend/src/services/drivers/DriverService.js +++ b/src/backend/src/services/drivers/DriverService.js @@ -26,6 +26,7 @@ const { PermissionUtil } = require("../auth/PermissionUtils.mjs"); const { Invoker } = require("../../../../putility/src/libs/invoker"); const { get_user } = require("../../helpers"); const { whatis } = require('../../util/langutil'); +const { AdvancedBase } = require("@heyputer/putility"); const strutil = require('@heyputer/putility').libs.string; @@ -131,6 +132,31 @@ class DriverService extends BaseService { }); } + async ['__on_boot.consolidation'] () { + const svc_registry = this.services.get('registry'); + const svc_event = this.services.get('event'); + + { + const col_interfaces = svc_registry.get('interfaces'); + const event = { + createInterface (name, definition) { + col_interfaces.set(name, definition); + }, + }; + await svc_event.emit('create.interfaces', event); + } + + { + const col_drivers = svc_registry.get('drivers'); + const event = { + createDriver (ifaceName, implName, definition) { + col_drivers.set(`${ifaceName}:${implName}`, definition); + }, + }; + await svc_event.emit('create.drivers', event); + } + } + /** * This method is responsible for registering collections in the service registry. * It registers 'interfaces', 'drivers', and 'types' collections. @@ -160,6 +186,7 @@ class DriverService extends BaseService { } await services.emit('driver.register.interfaces', { col_interfaces }); + await services.emit('driver.register.drivers', { col_drivers }); } @@ -302,27 +329,7 @@ class DriverService extends BaseService { }; driver = this.service_aliases[driver] ?? driver; - - /** - * This method retrieves the driver service for the provided interface name. - * It first checks if the driver service already exists in the registry, - * and if not, it throws an error. - * - * @param {string} interfaceName - The name of the interface for which to retrieve the driver service. - * @returns {DriverService} The driver service instance for the provided interface. - */ - const driver_service_exists = (() => { - return this.services.has(driver) && - this.services.get(driver).list_traits() - .includes(iface); - })(); - - if ( ! driver_service_exists ) { - const svc_apiError = this.services.get('api-error'); - throw svc_apiError.create('no_implementation_available', { iface }); - } - - const service = this.services.get(driver); + const service = this.get_service_or_throw_(driver, iface); const caps = service.as('driver-capabilities'); if ( test_mode && caps && caps.supports_test_mode(iface, method) ) { @@ -625,6 +632,57 @@ class DriverService extends BaseService { return processed_args; } + + /** + * This method retrieves the driver service for the provided interface name. + * It first checks if the driver service already exists in the registry, + * and if not, it throws an error. + * + * @param {string} interfaceName - The name of the interface for which to retrieve the driver service. + * @returns {DriverService} The driver service instance for the provided interface. + */ + get_service_or_throw_ (name, iface) { + let driver_service_exists = (() => { + return this.services.has(name) && + this.services.get(name).list_traits() + .includes(iface); + })(); + + if ( driver_service_exists ) { + return this.services.get(name); + } else { + const svc_registry = this.services.get('registry'); + const col_drivers = svc_registry.get('drivers'); + let maybe_driver = col_drivers.get(`${iface}:${name}`); + if ( maybe_driver ) { + const org = maybe_driver; + const impl = Object.create(org); + + // TraitsFeature also uses `in `, so this should cover + // all the methods that would get re-"`bind`'d" + for ( const k in org ) { + if ( ! (typeof org[k] === 'function') ) continue; + impl[k] = org[k].bind(org); + } + maybe_driver = class extends AdvancedBase { + static IMPLEMENTS = { + [iface]: impl, + }; + }; + Object.defineProperty(maybe_driver, 'name', { + value: `driver:${iface}:${name}`, + }); + return new maybe_driver(); + } + } + + if ( ! driver_service_exists ) { + const svc_apiError = this.services.get('api-error'); + throw svc_apiError.create('no_implementation_available', { iface }); + } + + return this.services.get(name); + } } module.exports = {