diff --git a/src/backend/src/services/AnomalyService.js b/src/backend/src/services/AnomalyService.js index 0c684e3a..7fa51a33 100644 --- a/src/backend/src/services/AnomalyService.js +++ b/src/backend/src/services/AnomalyService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -18,9 +19,26 @@ */ const BaseService = require("./BaseService"); +// Symbol used to indicate a denial of service instruction in anomaly handling. const DENY_SERVICE_INSTRUCTION = Symbol('DENY_SERVICE_INSTRUCTION'); + +/** +* @class AnomalyService +* @extends BaseService +* @description The AnomalyService class is responsible for managing and processing anomaly detection types and configurations. +* It allows the registration of different types with associated handlers, enabling the detection of anomalies based on specified criteria. +*/ class AnomalyService extends BaseService { + /** + * AnomalyService class that extends BaseService and provides methods + * for registering anomaly types and handling incoming data for those anomalies. + * + * The register method allows the registration of different anomaly types + * and their respective configurations, including custom handlers for data + * evaluation. It supports two modes of operation: a direct handler or + * a threshold-based evaluation. + */ _construct () { this.types = {}; } @@ -39,6 +57,16 @@ class AnomalyService extends BaseService { } this.types[type] = type_instance; } + /** + * Registers a new type with the service, including its configuration and handler. + * + * @param {string} type - The name of the type to register. + * @param {Object} config - The configuration object for the type. + * @param {Function} [config.handler] - An optional handler function for the type. + * @param {number} [config.high] - An optional threshold value; triggers the handler if exceeded. + * + * @returns {void} + */ async note (id, data) { const type = this.types[id]; if ( ! type ) return; diff --git a/src/backend/src/services/AppInformationService.js b/src/backend/src/services/AppInformationService.js index b473de12..8da8d78d 100644 --- a/src/backend/src/services/AppInformationService.js +++ b/src/backend/src/services/AppInformationService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"xai"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -23,6 +24,17 @@ const { DB_READ } = require("./database/consts"); const uuidv4 = require('uuid').v4; + +/** +* @class AppInformationService +* @description +* The AppInformationService class manages application-related information, +* including caching, statistical data, and tags for applications within the Puter ecosystem. +* It provides methods for refreshing application data, managing app statistics, +* and handling tags associated with apps. This service is crucial for maintaining +* up-to-date information about applications, facilitating features like app listings, +* recent apps, and tag-based app discovery. +*/ class AppInformationService { constructor ({ services }) { this.services = services; @@ -37,33 +49,87 @@ class AppInformationService { // await new Promise(rslv => setTimeout(rslv, 500)) await this._refresh_app_cache(); + /** + * Refreshes the application cache by querying the database for all apps and updating the key-value store. + * + * This method is called periodically to ensure that the in-memory cache reflects the latest + * state from the database. It uses the 'database' service to fetch app data and then updates + * multiple cache entries for quick lookups by name, ID, and UID. + * + * @async + */ asyncSafeSetInterval(async () => { this._refresh_app_cache(); }, 30 * 1000); await this._refresh_app_stats(); + /** + * Refreshes the cache of recently opened apps. + * This method updates the 'recent' collection with the UIDs of apps sorted by their most recent timestamp. + * + * @async + * @returns {Promise} A promise that resolves when the cache has been refreshed. + */ asyncSafeSetInterval(async () => { this._refresh_app_stats(); }, 120 * 1000); // This stat is more expensive so we don't update it as often await this._refresh_app_stat_referrals(); + /** + * Refreshes the app referral statistics. + * This method is computationally expensive and thus runs less frequently. + * It queries the database for user counts referred by each app's origin URL. + * + * @async + */ asyncSafeSetInterval(async () => { this._refresh_app_stat_referrals(); }, 15 * MINUTE); await this._refresh_recent_cache(); + /** + * Refreshes the recent cache by updating the list of recently added or updated apps. + * This method fetches all app data, filters for approved apps, sorts them by timestamp, + * and updates the `this.collections.recent` array with the UIDs of the most recent 50 apps. + * + * @async + * @private + */ asyncSafeSetInterval(async () => { this._refresh_recent_cache(); }, 120 * 1000); await this._refresh_tags(); + /** + * Refreshes the tags cache by iterating through all approved apps, + * extracting their tags, and organizing them into a structured format. + * This method updates the `this.tags` object with the latest tag information. + * + * @async + * @method + * @memberof AppInformationService + */ asyncSafeSetInterval(async () => { this._refresh_tags(); } , 120 * 1000); })(); } + + /** + * Retrieves and returns statistical data for a specific application. + * + * This method fetches various metrics such as the number of times the app has been opened, + * the count of unique users who have opened the app, and the number of referrals attributed to the app. + * It uses cached data where available to improve performance. + * + * @param {string} app_uid - The unique identifier for the application. + * @returns {Promise} An object containing: + * - {number} open_count - The total number of times the app has been opened. + * - {number} user_count - The count of unique users who have opened the app. + * - {number|null} referral_count - The number of referrals, or null if the data is not available or too expensive to retrieve. + */ async get_stats (app_uid) { const db = this.services.get('database').get(DB_READ, 'apps'); @@ -100,6 +166,19 @@ class AppInformationService { }; } + + /** + * Retrieves various statistics for a given app. + * + * This method fetches the open count, user count, and referral count for an app identified by its UID. + * It uses cached values where available to improve performance, but will query the database if necessary. + * + * @param {string} app_uid - The unique identifier of the app for which to retrieve stats. + * @returns {Promise} An object containing: + * - {number} open_count - Total number of times the app was opened. + * - {number} user_count - Number of unique users who opened the app. + * - {number|null} referral_count - Number of referrals attributed to the app. This value might not be reported if not cached. + */ async _refresh_app_cache () { this.log.tick('refresh app cache'); @@ -114,6 +193,17 @@ class AppInformationService { } } + + /** + * Refreshes the application cache by querying the database for all apps and updating the key-value store. + * + * @async + * @returns {Promise} A promise that resolves when the cache refresh operation is complete. + * + * @notes + * - This method logs a tick event for performance monitoring. + * - It populates the cache with app data indexed by name, id, and uid. + */ async _refresh_app_stats () { this.log.tick('refresh app stats'); @@ -144,6 +234,13 @@ class AppInformationService { } } + + /** + * Refreshes the cache of app statistics including open and user counts. + * This method updates the cache every 120 seconds to ensure data freshness. + * + * @async + */ async _refresh_app_stat_referrals () { this.log.tick('refresh app stat referrals'); @@ -176,6 +273,17 @@ class AppInformationService { this.log.info('DONE refresh app stat referrals'); } + + /** + * Updates the cache with recently updated apps. + * + * @description This method refreshes the cache containing the most recently updated applications. + * It fetches all app UIDs, retrieves the corresponding app data, filters for approved apps, + * sorts them by timestamp in descending order, and updates the 'recent' collection with + * the UIDs of the top 50 most recent apps. + * + * @returns {Promise} Resolves when the cache has been updated. + */ async _refresh_recent_cache () { const app_keys = kv.keys(`apps:uid:*`); // console.log('APP KEYS', app_keys); @@ -194,6 +302,16 @@ class AppInformationService { this.collections.recent = apps.map(app => app.uid).slice(0, 50); } + + /** + * Refreshes the cache of recently added or updated apps. + * + * This method retrieves all apps from the cache, filters for approved listings, + * sorts them by timestamp in descending order, and updates the `recent` collection + * with the UIDs of the most recent 50 apps. + * + * @returns {Promise} + */ async _refresh_tags () { const app_keys = kv.keys(`apps:uid:*`); // console.log('APP KEYS', app_keys); @@ -229,6 +347,22 @@ class AppInformationService { this.tags = new_tags; } + + /** + * Deletes an application from the system. + * + * This method performs the following actions: + * - Retrieves the app data from cache or database if not provided. + * - Deletes the app record from the database. + * - Removes the app from all relevant caches (by name, id, and uid). + * - Removes the app from the recent collection if present. + * - Removes the app from any associated tags. + * + * @param {string} app_uid - The unique identifier of the app to be deleted. + * @param {Object} [app] - The app object, if already fetched. If not provided, it will be retrieved. + * @throws {Error} If the app is not found in either cache or database. + * @returns {Promise} A promise that resolves when the app has been successfully deleted. + */ async delete_app (app_uid, app) { const db = this.services.get('database').get(DB_READ, 'apps'); diff --git a/src/backend/src/services/BaseService.js b/src/backend/src/services/BaseService.js index 1b169fae..5e7e676e 100644 --- a/src/backend/src/services/BaseService.js +++ b/src/backend/src/services/BaseService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-params":{"service":"xai"},"ai-refs":["../../doc/contributors/boot-sequence.md"],"ai-commented":{"service":"xai"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -18,8 +19,25 @@ */ const { concepts } = require("@heyputer/putility"); + + +// This is a no-op function that AI is incapable of writing a comment for. +// That said, I suppose it didn't need one anyway. const NOOP = async () => {}; + +/** +* @class BaseService +* @extends concepts.Service +* @description +* BaseService is the foundational class for all services in the Puter backend. +* It provides lifecycle methods like `construct` and `init` that are invoked during +* different phases of the boot sequence. This class ensures that services can be +* instantiated, initialized, and activated in a coordinated manner through +* events emitted by the Kernel. It also manages common service resources like +* logging and error handling, and supports legacy services by allowing +* instantiation after initialization but before consolidation. +*/ class BaseService extends concepts.Service { constructor (service_resources, ...a) { const { services, config, my_config, name, args } = service_resources; @@ -36,10 +54,29 @@ class BaseService extends concepts.Service { } } + + /** + * Initializes the service with configuration and dependencies. + * This method sets up logging and error handling, and calls a custom `_init` method if defined. + * + * @param {Object} args - Arguments passed to the service for initialization. + * @returns {Promise} A promise that resolves when initialization is complete. + */ async construct () { await (this._construct || NOOP).call(this, this.args); } + + /** + * Performs the initialization phase of the service lifecycle. + * This method sets up logging and error handling for the service, + * then calls the service-specific initialization logic if defined. + * + * @async + * @memberof BaseService + * @instance + * @returns {Promise} A promise that resolves when initialization is complete. + */ async init () { const services = this.services; this.log = services.get('log-service').create(this.service_name); @@ -48,6 +85,15 @@ class BaseService extends concepts.Service { await (this._init || NOOP).call(this, this.args); } + + /** + * Handles an event by retrieving the appropriate event handler + * and executing it with the provided arguments. + * + * @param {string} id - The identifier of the event to handle. + * @param {Array} args - The arguments to pass to the event handler. + * @returns {Promise} The result of the event handler execution. + */ async __on (id, args) { const handler = this.__get_event_handler(id); diff --git a/src/backend/src/services/BootScriptService.js b/src/backend/src/services/BootScriptService.js index 4b587cee..29f6e183 100644 --- a/src/backend/src/services/BootScriptService.js +++ b/src/backend/src/services/BootScriptService.js @@ -1,10 +1,29 @@ +// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}} const { Context } = require("../util/context"); const BaseService = require("./BaseService"); + +/** +* @class BootScriptService +* @extends BaseService +* @description The BootScriptService class extends BaseService and is responsible for +* managing and executing boot scripts. It provides methods to handle boot scripts when +* the system is ready and to run individual script commands. +*/ class BootScriptService extends BaseService { static MODULES = { fs: require('fs'), } + /** + * Loads and executes a boot script if specified in the arguments. + * + * This method reads the provided boot script file, parses it, and runs the script using the `run_script` method. + * If no boot script is specified in the arguments, the method returns immediately. + * + * @async + * @function + * @returns {Promise} + */ async ['__on_boot.ready'] () { const args = Context.get('args'); if ( ! args['boot-script'] ) return; @@ -17,6 +36,17 @@ class BootScriptService extends BaseService { await this.run_script(boot_json); } + + /** + * Executes a series of commands defined in a JSON boot script. + * + * This method processes each command in the boot_json array. + * If the command is recognized within the predefined scope, it will be executed. + * If not, an error is thrown. + * + * @param {Array} boot_json - An array of commands to execute. + * @throws {Error} Thrown if an unknown command is encountered. + */ async run_script (boot_json) { const scope = { runner: 'boot-script', diff --git a/src/backend/src/services/CleanEmailService.js b/src/backend/src/services/CleanEmailService.js index 5fe29192..1d58f8f9 100644 --- a/src/backend/src/services/CleanEmailService.js +++ b/src/backend/src/services/CleanEmailService.js @@ -1,6 +1,15 @@ +// METADATA // {"ai-commented":{"service":"claude"}} const { can } = require("../util/langutil"); const BaseService = require("./BaseService"); + +/** +* CleanEmailService - A service class for cleaning and validating email addresses +* Handles email normalization by applying provider-specific rules (e.g. Gmail's dot-insensitivity), +* manages subaddressing (plus addressing), and validates against blocked domains. +* Extends BaseService to integrate with the application's service infrastructure. +* @extends BaseService +*/ class CleanEmailService extends BaseService { static NAMED_RULES = { // For some providers, dots don't matter @@ -54,6 +63,12 @@ class CleanEmailService extends BaseService { static DOMAIN_NONDISTINCT = { 'googlemail.com': 'gmail.com', } + /** + * Maps non-distinct email domains to their canonical equivalents. + * For example, 'googlemail.com' is mapped to 'gmail.com' since they + * represent the same email service. + * @type {Object.} + */ _construct () { this.named_rules = this.constructor.NAMED_RULES; this.providers = this.constructor.PROVIDERS; @@ -62,6 +77,16 @@ class CleanEmailService extends BaseService { } clean (email) { + /** + * Cleans an email address by applying provider-specific rules and standardizations + * @param {string} email - The email address to clean + * @returns {string} The cleaned email address with applied rules and standardizations + * + * Splits email into local and domain parts, applies provider-specific rules like: + * - Removing dots for certain providers (Gmail, iCloud) + * - Handling subaddressing (removing +suffix) + * - Normalizing domains (e.g. googlemail.com -> gmail.com) + */ const eml = (() => { const [local, domain] = email.split('@'); return { local, domain }; @@ -101,6 +126,15 @@ class CleanEmailService extends BaseService { return eml.local + '@' + eml.domain; } + + /** + * Validates an email address against blocked domains and custom validation rules + * @param {string} email - The email address to validate + * @returns {Promise} True if email is valid, false if blocked or invalid + * @description First cleans the email, then checks against blocked domains from config. + * Emits 'email.validate' event to allow custom validation rules. Event handlers can + * set event.allow=false to reject the email. + */ async validate (email) { email = this.clean(email); const config = this.global_config; diff --git a/src/backend/src/services/ClientOperationService.js b/src/backend/src/services/ClientOperationService.js index 58c93deb..c670f61c 100644 --- a/src/backend/src/services/ClientOperationService.js +++ b/src/backend/src/services/ClientOperationService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"openai-completion","model":"gpt-4o"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -18,7 +19,14 @@ */ const { Context } = require("../util/context"); +// Key for tracing operations in the context, used for logging and tracking. const CONTEXT_KEY = Context.make_context_key('operation-trace'); +/** +* Class representing a tracker for individual client operations. +* The ClientOperationTracker class is designed to handle the metadata +* and attributes associated with each operation, allowing for better +* management and organization of client data during processing. +*/ class ClientOperationTracker { constructor (parameters) { this.name = parameters.name || 'untitled'; @@ -29,11 +37,26 @@ class ClientOperationTracker { } } + +/** +* Class representing the ClientOperationService, which manages the +* operations related to client interactions. It provides methods to +* add new operations and handle their associated client operation +* trackers, ensuring efficient management and tracking of client-side +* operations during their lifecycle. +*/ class ClientOperationService { constructor ({ services }) { this.operations_ = []; } + + /** + * Adds a new operation to the service by creating a ClientOperationTracker instance. + * + * @param {Object} parameters - The parameters for the new operation. + * @returns {Promise} A promise that resolves to the created ClientOperationTracker instance. + */ async add_operation (parameters) { const tracker = new ClientOperationTracker(parameters); diff --git a/src/backend/src/services/CommandService.js b/src/backend/src/services/CommandService.js index b944f7a5..d609caff 100644 --- a/src/backend/src/services/CommandService.js +++ b/src/backend/src/services/CommandService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"claude"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -18,15 +19,36 @@ */ const BaseService = require("./BaseService"); + +/** +* Represents a Command class that encapsulates command execution functionality. +* Each Command instance contains a specification (spec) that defines its ID, +* name, description, handler function, and optional argument completer. +* The class provides methods for executing commands and handling command +* argument completion. +*/ class Command { constructor(spec) { this.spec_ = spec; } + + /** + * Gets the unique identifier for this command + * @returns {string} The command's ID as specified in the constructor + */ get id() { return this.spec_.id; } + + /** + * Executes the command with given arguments and logging + * @param {Array} args - Command arguments to pass to the handler + * @param {Object} [log=console] - Logger object for output, defaults to console + * @returns {Promise} + * @throws {Error} Logs any errors that occur during command execution + */ async execute(args, log) { log = log ?? console; const { id, name, description, handler } = this.spec_; @@ -46,10 +68,30 @@ class Command { } } + +/** +* CommandService class manages the registration, execution, and handling of commands in the Puter system. +* Extends BaseService to provide command-line interface functionality. Maintains a collection of Command +* objects, supports command registration with namespaces, command execution with arguments, and provides +* command lookup capabilities. Includes built-in help command functionality. +* @extends BaseService +*/ class CommandService extends BaseService { + /** + * Service for managing and executing commands in the system. + * Extends BaseService to provide command registration, execution and lookup functionality. + * Commands are stored internally with unique IDs and can be executed with arguments. + * Built-in 'help' command is registered during initialization. + */ async _construct () { this.commands_ = []; } + /** + * Initializes the command service's internal state + * Called during service construction to set up the empty commands array + * @private + * @returns {Promise} + */ async _init () { this.commands_.push(new Command({ id: 'help', @@ -73,6 +115,14 @@ class CommandService extends BaseService { } } + + /** + * Executes a command with the given arguments and logging context + * @param {string[]} args - Array of command arguments where first element is command name + * @param {Object} log - Logger object for output (defaults to console if not provided) + * @returns {Promise} + * @throws {Error} If command execution fails + */ async executeCommand(args, log) { const [commandName, ...commandArgs] = args; const command = this.commands_.find(c => c.spec_.id === commandName); @@ -80,17 +130,37 @@ class CommandService extends BaseService { log.error(`unknown command: ${commandName}`); return; } + /** + * Executes a command with the given arguments in a global context + * @param {string[]} args - Array of command arguments where first element is command name + * @param {Object} log - Logger object for output + * @returns {Promise} + * @throws {Error} If command execution fails + */ await globalThis.root_context.arun(async () => { await command.execute(commandArgs, log); }); } + + /** + * Executes a raw command string by splitting it into arguments and executing the command + * @param {string} text - Raw command string to execute + * @param {object} log - Logger object for output (defaults to console if not provided) + * @returns {Promise} + * @todo Replace basic whitespace splitting with proper tokenizer (obvious-json) + */ async executeRawCommand(text, log) { // TODO: add obvious-json as a tokenizer const args = text.split(/\s+/); await this.executeCommand(args, log); } + + /** + * Gets a list of all registered command names/IDs + * @returns {string[]} Array of command identifier strings + */ get commandNames() { return this.commands_.map(command => command.id); } diff --git a/src/backend/src/services/CommentService.js b/src/backend/src/services/CommentService.js index 3420ccd3..774f13d8 100644 --- a/src/backend/src/services/CommentService.js +++ b/src/backend/src/services/CommentService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"claude"}} const APIError = require("../api/APIError"); const FSNodeParam = require("../api/filesystem/FSNodeParam"); const { get_user } = require("../helpers"); @@ -6,15 +7,37 @@ const { Endpoint } = require("../util/expressutil"); const BaseService = require("./BaseService"); const { DB_WRITE } = require("./database/consts"); + +/** +* CommentService class handles all comment-related functionality in the system. +* Extends BaseService to provide comment creation, retrieval, and attachment capabilities +* for filesystem entries. Manages database operations for user comments and their +* associations with filesystem nodes. Provides REST API endpoints for comment +* operations including posting new comments and listing existing comments. +* @extends BaseService +*/ class CommentService extends BaseService { static MODULES = { uuidv4: require('uuid').v4, } + /** + * Static module dependencies used by the CommentService class + * @property {Function} uuidv4 - UUID v4 generator function from the uuid package + */ _init () { const svc_database = this.services.get('database'); this.db = svc_database.get(DB_WRITE, 'notification'); } ['__on_install.routes'] (_, { app }) { + /** + * Installs route handlers for comment-related endpoints + * Sets up POST routes for creating and listing comments on filesystem entries + * + * @param {*} _ Unused parameter + * @param {Object} options Installation options + * @param {Express} options.app Express application instance + * @private + */ const r_comment = (() => { const require = this.require; const express = require('express'); @@ -113,6 +136,16 @@ class CommentService extends BaseService { } + + /** + * Creates a new comment with the given text + * + * @param {Object} params - The parameters object + * @param {Object} params.req - Express request object containing user and body data + * @param {Object} params.res - Express response object + * @returns {Promise} The created comment object with id and uid + * @throws {APIError} If text field is missing from request body + */ async create_comment_ ({ req, res }) { if ( ! req.body.text ) { throw APIError.create('field_missing', null, { key: 'text' }); @@ -135,6 +168,15 @@ class CommentService extends BaseService { }; } + + /** + * Attaches a comment to a filesystem entry + * + * @param {Object} params - The parameters object + * @param {Object} params.node - The filesystem node to attach the comment to + * @param {Object} params.comment - The comment object containing id and other details + * @returns {Promise} Resolves when comment is successfully attached + */ async attach_comment_to_fsentry ({ node, comment }) { await this.db.write( 'INSERT INTO `user_fsentry_comments` ' + @@ -144,6 +186,14 @@ class CommentService extends BaseService { ); } + + /** + * Retrieves all comments associated with a filesystem entry + * + * @param {Object} params - The parameters object + * @param {Object} params.node - The filesystem node to get comments for + * @returns {Promise} Array of comment objects with user info attached + */ async get_comments_for_fsentry ({ node }) { const comments = await this.db.read( 'SELECT * FROM `user_comments` ' + diff --git a/src/backend/src/services/ConfigurableCountingService.js b/src/backend/src/services/ConfigurableCountingService.js index b3229699..db1a68c3 100644 --- a/src/backend/src/services/ConfigurableCountingService.js +++ b/src/backend/src/services/ConfigurableCountingService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -27,6 +28,16 @@ const hash = v => { return sum.digest(); } + +/** +* @class ConfigurableCountingService +* @extends BaseService +* @description The ConfigurableCountingService class extends BaseService and is responsible for managing and incrementing +* configurable counting types for different services. It handles the initialization of the database connection, +* defines counting types and SQL columns, and provides a method to increment counts based on specific service +* types and values. This class is used to manage usage counts for various services, ensuring accurate tracking +* and updating of counts in the database. +*/ class ConfigurableCountingService extends BaseService { static counting_types = { gpt: { @@ -73,10 +84,34 @@ class ConfigurableCountingService extends BaseService { ], } + + /** + * Initializes the database connection for the ConfigurableCountingService. + * This method sets up the database service for writing counting data. + * + * @async + * @function _init + * @returns {Promise} A promise that resolves when the database connection is established. + * @memberof ConfigurableCountingService + */ async _init () { this.db = this.services.get('database').get(DB_WRITE, 'counting'); } + + /** + * Increments the count for a given service based on the provided parameters. + * This method builds an SQL query to update the count and other custom values + * in the database. It handles different SQL dialects (MySQL and SQLite) and + * ensures that the pricing category is correctly hashed and stored. + * + * @param {Object} params - The parameters for incrementing the count. + * @param {string} params.service_name - The name of the service. + * @param {string} params.service_type - The type of the service. + * @param {Object} params.values - The values to be incremented. + * @throws {Error} If the service type is unknown or if there are no more available columns. + * @returns {Promise} A promise that resolves when the count is successfully incremented. + */ async increment ({ service_name, service_type, values }) { values = values ? {...values} : {}; diff --git a/src/backend/src/services/Container.js b/src/backend/src/services/Container.js index 44878f8f..6ac89d4f 100644 --- a/src/backend/src/services/Container.js +++ b/src/backend/src/services/Container.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"mistral","model":"mistral-large-latest"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -23,6 +24,15 @@ const { CompositeError } = require("../util/errorutil"); const { TeePromise } = require("../util/promise"); // 17 lines of code instead of an entire dependency-injection framework +/** +* The `Container` class is a lightweight dependency-injection container designed to manage +* service instances within the application. It provides functionality for registering, +* retrieving, and managing the lifecycle of services, including initialization and event +* handling. This class is intended to simplify dependency management and ensure that services +* are properly initialized and available throughout the application. +* +* @class +*/ class Container { constructor ({ logger }) { this.logger = logger; @@ -122,6 +132,12 @@ class Container { } } has (name) { return !! this.instances_[name]; } + /** + * Checks if a service is registered in the container. + * + * @param {String} name - The name of the service to check. + * @returns {Boolean} - Returns true if the service is registered, false otherwise. + */ get values () { const values = {}; for ( const k in this.instances_ ) { @@ -141,6 +157,18 @@ class Container { return this.instances_; } + + /** + * Initializes all registered services in the container. + * + * This method first constructs each service by calling its `construct` method, + * and then initializes each service by calling its `init` method. If any service + * initialization fails, it logs the failures and throws a `CompositeError` + * containing details of all failed initializations. + * + * @returns {Promise} A promise that resolves when all services are + * initialized or rejects if any service initialization fails. + */ async init () { for ( const k in this.instances_ ) { this.logger.info(`constructing ${k}`); @@ -166,6 +194,17 @@ class Container { } } + + /** + * Emits an event to all registered services. + * + * This method sends an event identified by `id` along with any additional arguments to all + * services registered in the container. If a logger is available, it logs the event. + * + * @param {string} id - The identifier of the event. + * @param {...*} args - Additional arguments to pass to the event handler. + * @returns {Promise} A promise that resolves when all event handlers have completed. + */ async emit (id, ...args) { if ( this.logger ) { this.logger.noticeme(`services:event ${id}`, { args }); @@ -180,6 +219,14 @@ class Container { } } + +/** +* @class ProxyContainer +* @classdesc The ProxyContainer class is a proxy for the Container class, allowing for delegation of service management tasks. +* It extends the functionality of the Container class by providing a delegation mechanism. +* This class is useful for scenarios where you need to manage services through a proxy, +* enabling additional flexibility and control over service instances. +*/ class ProxyContainer { constructor (delegate) { this.delegate = delegate; @@ -200,6 +247,12 @@ class ProxyContainer { } return this.delegate.has(name); } + /** + * Checks if the container has a service with the specified name. + * + * @param {string} name - The name of the service to check. + * @returns {boolean} - Returns true if the service exists, false otherwise. + */ get values () { const values = {}; Object.assign(values, this.delegate.values); diff --git a/src/backend/src/services/ContextInitService.js b/src/backend/src/services/ContextInitService.js index 3267d881..66ce0545 100644 --- a/src/backend/src/services/ContextInitService.js +++ b/src/backend/src/services/ContextInitService.js @@ -1,3 +1,4 @@ +// METADATA // {"ai-commented":{"service":"claude"}} /* * Copyright (C) 2024 Puter Technologies Inc. * @@ -20,7 +21,24 @@ const { Context } = require("../util/context"); const BaseService = require("./BaseService"); // DRY: (2/3) - src/util/context.js; move install() to base class +/** +* @class ContextInitExpressMiddleware +* @description Express middleware that initializes context values for requests. +* Manages a collection of value initializers that can be synchronous values +* or asynchronous factory functions. Each initializer sets a key-value pair +* in the request context. Part of a DRY implementation shared with context.js. +* TODO: Consider moving install() method to base class. +*/ class ContextInitExpressMiddleware { + /** + * Express middleware class that initializes context values for requests + * + * Manages a list of value initializers that populate the Context with + * either static values or async-generated values when handling requests. + * Part of DRY pattern with src/util/context.js. + * + * @class + */ constructor () { this.value_initializers_ = []; } @@ -30,6 +48,11 @@ class ContextInitExpressMiddleware { install (app) { app.use(this.run.bind(this)); } + /** + * Installs the middleware into the Express application + * @param {Express} app - The Express application instance + * @returns {void} + */ async run (req, res, next) { const x = Context.get(); for ( const initializer of this.value_initializers_ ) { @@ -43,7 +66,23 @@ class ContextInitExpressMiddleware { } } + +/** +* @class ContextInitService +* @extends BaseService +* @description Service responsible for initializing and managing context values in the application. +* Provides methods to register both synchronous values and asynchronous factories for context +* initialization. Works in conjunction with Express middleware to ensure proper context setup +* for each request. Extends BaseService to integrate with the application's service architecture. +*/ class ContextInitService extends BaseService { + /** + * Service for initializing request context with values and async factories. + * Extends BaseService to provide middleware for Express that populates the Context + * with registered values and async-generated values at the start of each request. + * + * @extends BaseService + */ _construct () { this.mw = new ContextInitExpressMiddleware(); } @@ -57,6 +96,11 @@ class ContextInitService extends BaseService { key, async_factory, }); } + /** + * Registers an asynchronous factory function to initialize a context value + * @param {string} key - The key to store the value under in the context + * @param {Function} async_factory - Async function that returns the value to store + */ async ['__on_install.middlewares.context-aware'] (_, { app }) { this.mw.install(app); await this.services.emit('install.context-initializers');