This commit is contained in:
ProgrammerIn-wonderland
2025-08-21 17:12:26 -04:00
11 changed files with 426 additions and 61 deletions

View File

@@ -19,6 +19,20 @@
const APIError = require("../api/APIError");
/**
* Creates an anti-CSRF middleware that validates CSRF tokens in incoming requests.
* This middleware protects against Cross-Site Request Forgery attacks by verifying
* that requests contain a valid anti-CSRF token in the request body.
*
* @param {Object} options - Configuration options for the middleware
* @returns {Function} Express middleware function that validates CSRF tokens
*
* @example
* // Apply anti-CSRF protection to a route
* app.post('/api/secure-endpoint', anticsrf(), (req, res) => {
* // Route handler code
* });
*/
const anticsrf = options => async (req, res, next) => {
const svc_antiCSRF = req.services.get('anti-csrf');
if ( ! req.body.anti_csrf ) {

View File

@@ -179,24 +179,24 @@ class DeepSeekService extends BaseService {
{
id: 'deepseek-chat',
name: 'DeepSeek Chat',
context: 64000,
context: 128000,
cost: {
currency: 'usd-cents',
tokens: 1_000_000,
input: 14,
output: 28,
input: 56,
output: 168,
},
max_tokens: 8000,
},
{
id: 'deepseek-reasoner',
name: 'DeepSeek Reasoner',
context: 64000,
context: 128000,
cost: {
currency: 'usd-cents',
tokens: 1_000_000,
input: 55,
output: 219,
input: 56,
output: 168,
},
max_tokens: 64000,
}

View File

@@ -31,11 +31,11 @@ const PRIVATE_UID_SECRET = config.private_uid_secret
/**
* Represents an Actor in the system, extending functionality from AdvancedBase.
* The Actor class is responsible for managing actor instances, including
* creating new actors, generating unique identifiers, and handling related types
* that represent different roles within the context of the application.
*/
* Represents an Actor in the system, extending functionality from AdvancedBase.
* The Actor class is responsible for managing actor instances, including
* creating new actors, generating unique identifiers, and handling related types
* that represent different roles within the context of the application.
*/
class Actor extends AdvancedBase {
static MODULES = {
uuidv5: require('uuid').v5,
@@ -43,9 +43,9 @@ class Actor extends AdvancedBase {
}
static system_actor_ = null;
/**
* Retrieves the system actor instance, creating it if it doesn't exist.
*
* This static method ensures that there is only one instance of the system actor.
* If the system actor has not yet been created, it will be instantiated with a
* new SystemActorType.
@@ -61,6 +61,16 @@ class Actor extends AdvancedBase {
return this.system_actor_;
}
/**
* Creates a new Actor instance with the specified type and parameters.
* Resolves user and app references from UIDs if provided in the parameters.
*
* @param {Function} type - The ActorType constructor to instantiate.
* @param {Object} params - Parameters for the actor type.
* @param {string} [params.user_uid] - UUID of the user to resolve.
* @param {string} [params.app_uid] - UID of the app to resolve.
* @returns {Promise<Actor>} A new Actor instance.
*/
static async create (type, params) {
params = { ...params };
if ( params.user_uid ) {
@@ -75,12 +85,12 @@ class Actor extends AdvancedBase {
}
/**
* Initializes the Actor instance with the provided parameters.
* This constructor assigns object properties from the input object to the instance.
*
* @param {Object} o - The object containing actor parameters.
* @param {...any} a - Additional arguments passed to the parent class constructor.
*/
* Initializes the Actor instance with the provided parameters.
* This constructor assigns object properties from the input object to the instance.
*
* @param {Object} o - The object containing actor parameters.
* @param {...any} a - Additional arguments passed to the parent class constructor.
*/
constructor (o, ...a) {
super(o, ...a);
for ( const k in o ) {
@@ -88,10 +98,20 @@ class Actor extends AdvancedBase {
}
}
/**
* Gets the unique identifier for this actor.
*
* @returns {string} The actor's UID from its type.
*/
get uid () {
return this.type.uid;
}
/**
* Returns fields suitable for logging this actor.
*
* @returns {Object} Object containing UID and optionally username for logging.
*/
toLogFields () {
return {
uid: this.type.uid,
@@ -102,13 +122,13 @@ class Actor extends AdvancedBase {
}
/**
* Generates a cryptographically-secure deterministic UUID
* from an actor's UID. The generated UUID is derived by
* applying SHA-256 HMAC to the actor's UID using a secret,
* then formatting the result as a UUID V5.
*
* @returns {string} The derived UUID corresponding to the actor's UID.
*/
* Generates a cryptographically-secure deterministic UUID
* from an actor's UID. The generated UUID is derived by
* applying SHA-256 HMAC to the actor's UID using a secret,
* then formatting the result as a UUID V5.
*
* @returns {string} The derived UUID corresponding to the actor's UID.
*/
get private_uid () {
// Pass the UUID through SHA-2 first because UUIDv5
// is not cryptographically secure (it uses SHA-1)
@@ -139,6 +159,12 @@ class Actor extends AdvancedBase {
});
}
/**
* Creates a related actor of the specified type based on the current actor.
*
* @param {Function} type_class - The ActorType class to create a related actor for.
* @returns {Actor} A new Actor instance with the related type.
*/
get_related_actor (type_class) {
const actor = this.clone();
actor.type = this.type.get_related_type(type_class);
@@ -146,7 +172,16 @@ class Actor extends AdvancedBase {
}
}
/**
* Base class for all actor types in the system.
* Provides common initialization functionality for actor type instances.
*/
class ActorType {
/**
* Initializes the ActorType with the provided properties.
*
* @param {Object} o - Object containing properties to assign to this instance.
*/
constructor (o) {
for ( const k in o ) {
this[k] = o[k];
@@ -155,15 +190,28 @@ class ActorType {
}
/**
* Class representing the system actor type within the actor framework.
* This type serves as a specific implementation of an actor that
* represents a system-level entity and provides methods for UID retrieval
* and related type management.
*/
* Class representing the system actor type within the actor framework.
* This type serves as a specific implementation of an actor that
* represents a system-level entity and provides methods for UID retrieval
* and related type management.
*/
class SystemActorType extends ActorType {
/**
* Gets the unique identifier for the system actor.
*
* @returns {string} Always returns 'system'.
*/
get uid () {
return 'system';
}
/**
* Gets a related actor type for the system actor.
*
* @param {Function} type_class - The ActorType class to get a related type for.
* @returns {SystemActorType} Returns this instance if type_class is SystemActorType.
* @throws {Error} If the requested type_class is not supported.
*/
get_related_type (type_class) {
if ( type_class === SystemActorType ) {
return this;
@@ -174,14 +222,27 @@ class SystemActorType extends ActorType {
/**
* Represents the type of a User Actor in the system, allowing operations and relations
* specific to user actors. This class extends the base functionality to uniquely identify
* user actors and define how they relate to other types of actors within the system.
*/
* Represents the type of a User Actor in the system, allowing operations and relations
* specific to user actors. This class extends the base functionality to uniquely identify
* user actors and define how they relate to other types of actors within the system.
*/
class UserActorType extends ActorType {
/**
* Gets the unique identifier for the user actor.
*
* @returns {string} The UID in format 'user:{uuid}'.
*/
get uid () {
return 'user:' + this.user.uuid;
}
/**
* Gets a related actor type for the user actor.
*
* @param {Function} type_class - The ActorType class to get a related type for.
* @returns {UserActorType} Returns this instance if type_class is UserActorType.
* @throws {Error} If the requested type_class is not supported.
*/
get_related_type (type_class) {
if ( type_class === UserActorType ) {
return this;
@@ -189,16 +250,30 @@ class UserActorType extends ActorType {
throw new Error(`cannot get ${type_class.name} from ${this.constructor.name}`)
}
}
/**
* Represents a user actor type in the application. This class defines the structure
* and behavior specific to user actors, including obtaining unique identifiers and
* retrieving related actor types. It extends the base actor type functionality
* to cater to user-specific needs.
*/
* Represents a user actor type in the application. This class defines the structure
* and behavior specific to user actors, including obtaining unique identifiers and
* retrieving related actor types. It extends the base actor type functionality
* to cater to user-specific needs.
*/
class AppUnderUserActorType extends ActorType {
/**
* Gets the unique identifier for the app-under-user actor.
*
* @returns {string} The UID in format 'app-under-user:{user_uuid}:{app_uid}'.
*/
get uid () {
return 'app-under-user:' + this.user.uuid + ':' + this.app.uid;
}
/**
* Gets a related actor type for the app-under-user actor.
*
* @param {Function} type_class - The ActorType class to get a related type for.
* @returns {UserActorType|AppUnderUserActorType} The related actor type instance.
* @throws {Error} If the requested type_class is not supported.
*/
get_related_type (type_class) {
if ( type_class === UserActorType ) {
return new UserActorType({ user: this.user });
@@ -212,26 +287,33 @@ class AppUnderUserActorType extends ActorType {
/**
* Represents the type of access tokens in the system.
* An AccessTokenActorType associates an authorizer and an authorized actor
* with a string token, facilitating permission checks and identity management.
*/
* Represents the type of access tokens in the system.
* An AccessTokenActorType associates an authorizer and an authorized actor
* with a string token, facilitating permission checks and identity management.
*/
class AccessTokenActorType extends ActorType {
// authorizer: an Actor who authorized the token
// authorized: an Actor who is authorized by the token
// token: a string
/**
* Gets the unique identifier for the access token actor.
* The UID is constructed based on the authorizer's UID, the authorized actor's UID (if available),
* and the token string. This UID format is useful for identifying the access token's context.
*
* @returns {string} The generated UID for the access token.
*/
get uid () {
return 'access-token:' + this.authorizer.uid +
':' + ( this.authorized?.uid ?? '<none>' ) +
':' + this.token;
}
/**
* Generate a unique identifier (UID) for the access token.
* The UID is constructed based on the authorizer's UID, the authorized actor's UID (if available),
* and the token string. This UID format is useful for identifying the access token's context.
* Throws an error as getting related actors is not supported for access tokens.
* This would be dangerous because of ambiguity between authorizer and authorized.
*
* @returns {string} The generated UID for the access token.
* @throws {Error} Always throws an error indicating this operation is not supported.
*/
get_related_actor () {
// This would be dangerous because of ambiguity
@@ -242,29 +324,42 @@ class AccessTokenActorType extends ActorType {
/**
* Represents a Site Actor Type, which encapsulates information about a site-specific actor.
* This class is used to manage details related to the site and implement functionalities
* pertinent to site-level operations and interactions in the actor framework.
*/
* Represents a Site Actor Type, which encapsulates information about a site-specific actor.
* This class is used to manage details related to the site and implement functionalities
* pertinent to site-level operations and interactions in the actor framework.
*/
class SiteActorType {
/**
* Constructor for the SiteActorType class.
* Initializes a new instance of SiteActorType with the provided properties.
*
* @param {Object} o - The properties to initialize the SiteActorType with.
* @param {...*} a - Additional arguments.
*/
* Constructor for the SiteActorType class.
* Initializes a new instance of SiteActorType with the provided properties.
*
* @param {Object} o - The properties to initialize the SiteActorType with.
* @param {...*} a - Additional arguments.
*/
constructor (o, ...a) {
for ( const k in o ) {
this[k] = o[k];
}
}
/**
* Gets the unique identifier for the site actor.
*
* @returns {string} The UID in format 'site:{site_name}'.
*/
get uid () {
return `site:` + this.site.name
}
}
/**
* Adapts various input types to a proper Actor instance.
* If no actor is provided, attempts to get one from the current context.
* Handles legacy user objects by wrapping them in UserActorType.
*
* @param {Actor|Object} [actor] - The actor to adapt, or undefined to use context.
* @returns {Actor} A properly formatted Actor instance.
*/
Actor.adapt = function (actor) {
actor = actor || Context.get('actor');

View File

@@ -30,6 +30,11 @@ const BaseService = require("../BaseService");
* A token expires when it is evicted from the queue.
*/
class CircularQueue {
/**
* Creates a new CircularQueue instance with the specified size.
*
* @param {number} size - The maximum number of items the queue can hold
*/
constructor (size) {
this.size = size;
this.queue = [];
@@ -37,6 +42,11 @@ class CircularQueue {
this.map = new Map();
}
/**
* Adds an item to the queue. If the queue is full, the oldest item is removed.
*
* @param {*} item - The item to add to the queue
*/
push (item) {
if ( this.queue[this.index] ) {
this.map.delete(this.queue[this.index]);
@@ -46,14 +56,32 @@ class CircularQueue {
this.index = (this.index + 1) % this.size;
}
/**
* Retrieves an item from the queue at the specified relative index.
*
* @param {number} index - The relative index from the current position
* @returns {*} The item at the specified index
*/
get (index) {
return this.queue[(this.index + index) % this.size];
}
/**
* Checks if the queue contains the specified item.
*
* @param {*} item - The item to check for
* @returns {boolean} True if the item exists in the queue, false otherwise
*/
has (item) {
return this.map.has(item);
}
/**
* Attempts to consume (remove) an item from the queue if it exists.
*
* @param {*} item - The item to consume
* @returns {boolean} True if the item was found and consumed, false otherwise
*/
maybe_consume (item) {
if ( this.has(item) ) {
const index = this.map.get(item);
@@ -81,6 +109,12 @@ class AntiCSRFService extends BaseService {
this.map_session_to_tokens = {};
}
/**
* Sets up the route handler for getting anti-CSRF tokens.
* Registers the '/get-anticsrf-token' endpoint that returns a new token for authenticated users.
*
* @returns {void}
*/
['__on_install.routes'] () {
const { app } = this.services.get('web-server');
@@ -106,6 +140,13 @@ class AntiCSRFService extends BaseService {
}));
}
/**
* Creates a new anti-CSRF token for the specified session.
* If no token queue exists for the session, a new one is created.
*
* @param {string} session - The session identifier
* @returns {string} The newly created token
*/
create_token (session) {
let tokens = this.map_session_to_tokens[session];
if ( ! tokens ) {
@@ -117,6 +158,13 @@ class AntiCSRFService extends BaseService {
return token;
}
/**
* Attempts to consume (validate and remove) a token for the specified session.
*
* @param {string} session - The session identifier
* @param {string} token - The token to consume
* @returns {boolean} True if the token was valid and consumed, false otherwise
*/
consume_token (session, token) {
const tokens = this.map_session_to_tokens[session];
if ( ! tokens ) return false;
@@ -135,6 +183,13 @@ class AntiCSRFService extends BaseService {
return require('crypto').randomBytes(32).toString('hex');
}
/**
* Runs unit tests for the AntiCSRFService functionality.
* Tests token generation, expiration, and consumption behavior.
*
* @param {Object} params - Test parameters
* @param {Function} params.assert - Assertion function for testing
*/
_test ({ assert }) {
// Do this several times, like a user would
for ( let i=0 ; i < 30 ; i++ ) {

View File

@@ -196,7 +196,7 @@ class FileCacheService extends AdvancedBase {
// If the file is still in pending it means we waited too long;
// it's possible that reading the file failed is is delayed.
if ( tracker.phase === FileTracker.PHASE_PENDING ) {
return this._get_path(await fsNode.get('uid'));
return null;
}
// Since we waited for the file to be ready, it's not impossible

View File

@@ -612,6 +612,8 @@ function resetToOriginalValues() {
}
async function edit_app_section(cur_app_name, tab = 'deploy') {
puter.ui.showSpinner();
$('section:not(.sidebar)').hide();
$('.tab-btn').removeClass('active');
$('.tab-btn[data-tab="apps"]').addClass('active');

View File

@@ -442,9 +442,15 @@ const ipc_listener = async (event, handled) => {
is_selectable_body = true;
// Open dialog
let path = event.data.options?.path ?? '/' + window.user.username + '/Desktop';
if ( (''+path).toLowerCase().startsWith('%appdata%') ) {
path = path.slice('%appdata%'.length);
if ( path !== '' && ! path.startsWith('/') ) path = '/' + path;
path = '/' + window.user.username + '/AppData/' + app_uuid + path;
}
UIWindow({
allowed_file_types: allowed_file_types,
path: '/' + window.user.username + '/Desktop',
path,
// this is the uuid of the window to which this dialog will return
parent_uuid: event.data.appInstanceID,
onDialogCancel: () => {

View File

@@ -16,7 +16,16 @@
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* Base class that provides utilities for working with inheritance chains and static properties.
*/
class BasicBase {
/**
* Gets the inheritance chain for the current instance, starting from the most derived class
* and working up to BasicBase (excluded).
* @returns {Array<Function>} Array of constructor functions in inheritance order
*/
_get_inheritance_chain () {
const chain = [];
let cls = this.constructor;
@@ -27,6 +36,14 @@ class BasicBase {
return chain.reverse();
}
/**
* Merges static array properties from all classes in the inheritance chain.
* Avoids duplicating the same array reference from contiguous members
* of the inheritance chain (useful when using the decorator pattern with
* multiple classes sharing a common base)
* @param {string} key - The name of the static property to merge
* @returns {Array} Combined array containing all values from the inheritance chain
*/
_get_merged_static_array (key) {
const chain = this._get_inheritance_chain();
const values = [];
@@ -40,6 +57,12 @@ class BasicBase {
return values;
}
/**
* Merges static object properties from all classes in the inheritance chain.
* Properties from derived classes override those from base classes.
* @param {string} key - The name of the static property to merge
* @returns {Object} Combined object containing all properties from the inheritance chain
*/
_get_merged_static_object (key) {
// TODO: check objects by reference - same object in a subclass shouldn't count
const chain = this._get_inheritance_chain();

View File

@@ -20,33 +20,59 @@
const { AdvancedBase } = require("../AdvancedBase");
const ServiceFeature = require("../features/ServiceFeature");
/** @type {Function} No-operation async function */
const NOOP = async () => {};
/** Service trait */
/** @type {Symbol} Service trait symbol */
const TService = Symbol('TService');
/**
* Service will be incrementally updated to consolidate
* Service class that will be incrementally updated to consolidate
* BaseService in Puter's backend with Service in Puter's frontend,
* becoming the common base for both and a useful utility in general.
*
* @class Service
* @extends AdvancedBase
*/
class Service extends AdvancedBase {
/** @type {Array} Array of features this service supports */
static FEATURES = [
ServiceFeature,
];
/**
* Handles events by calling the appropriate event handler
*
* @param {string} id - The event identifier
* @param {Array} args - Arguments to pass to the event handler
* @returns {Promise<*>} The result of the event handler
*/
async __on (id, args) {
const handler = this.__get_event_handler(id);
return await handler(id, ...args);
}
/**
* Retrieves the event handler for a given event ID
*
* @param {string} id - The event identifier
* @returns {Function} The event handler function or NOOP if not found
*/
__get_event_handler (id) {
return this[`__on_${id}`]?.bind?.(this)
|| this.constructor[`__on_${id}`]?.bind?.(this.constructor)
|| NOOP;
}
/**
* Factory method to create a new service instance
*
* @param {Object} config - Configuration object
* @param {Object} config.parameters - Parameters for service construction
* @param {Object} config.context - Context for the service
* @returns {Service} A new service instance
*/
static create ({ parameters, context }) {
const ins = new this();
ins._.context = context;
@@ -55,7 +81,14 @@ class Service extends AdvancedBase {
}
static IMPLEMENTS = {
/** @type {Object} Implementation of the TService trait */
[TService]: {
/**
* Initializes the service by running init hooks and calling _init if present
*
* @param {...*} a - Arguments to pass to _init method
* @returns {*} Result of _init method if it exists
*/
init (...a) {
if ( this._.init_hooks ) {
for ( const hook of this._.init_hooks ) {
@@ -65,12 +98,23 @@ class Service extends AdvancedBase {
if ( ! this._init ) return;
return this._init(...a);
},
/**
* Constructs the service with given parameters
*
* @param {Object} o - Parameters object
* @returns {*} Result of _construct method if it exists
*/
construct (o) {
this.$parameters = {};
for ( const k in o ) this.$parameters[k] = o[k];
if ( ! this._construct ) return;
return this._construct(o);
},
/**
* Gets the dependencies for this service
*
* @returns {Array} Array of dependencies
*/
get_depends () {
return [
...(this.constructor.DEPENDS ?? []),

View File

@@ -17,13 +17,28 @@
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
/**
* A context object that manages hierarchical property inheritance and sub-context creation.
* Properties are copied with their descriptors to maintain getter/setter behavior.
*/
class Context {
/**
* Creates a new Context instance with the provided values.
* @param {Object} [values={}] - Initial values to set on the context, with their property descriptors preserved
*/
constructor (values = {}) {
const descs = Object.getOwnPropertyDescriptors(values);
for ( const k in descs ) {
Object.defineProperty(this, k, descs[k]);
}
}
/**
* Creates a sub-context that follows specific properties from a source object.
* The returned context will have getters that reference the source object's properties.
* @param {Object} source - The source object to follow properties from
* @param {string[]} keys - Array of property names to follow from the source
* @returns {Context} A new sub-context with getters pointing to the source properties
*/
follow (source, keys) {
const values = {};
for ( const k of keys ) {
@@ -33,6 +48,12 @@ class Context {
}
return this.sub(values);
}
/**
* Creates a sub-context that inherits from the current context with additional or overridden values.
* Nested Context instances are recursively sub-contexted with corresponding new values.
* @param {Object} [newValues={}] - New values to add or override in the sub-context
* @returns {Context} A new context that inherits from this context with the new values applied
*/
sub (newValues) {
if ( newValues === undefined ) newValues = {};
const sub = Object.create(this);

View File

@@ -20,6 +20,10 @@
const { AdvancedBase } = require("../AdvancedBase");
const { TLogger, AS } = require("../traits/traits");
/**
* Logger implementation that stores log entries in an internal array buffer.
* Useful for testing or collecting log entries for later processing.
*/
class ArrayLogger extends AdvancedBase {
static PROPERTIES = {
buffer: {
@@ -28,6 +32,13 @@ class ArrayLogger extends AdvancedBase {
}
static IMPLEMENTS = {
[TLogger]: {
/**
* Logs a message by storing it in the internal buffer array.
* @param {string} level - The log level (e.g., 'info', 'warn', 'error')
* @param {string} message - The log message
* @param {Object} fields - Additional fields to include with the log entry
* @param {Array} values - Additional values to log
*/
log (level, message, fields, values) {
this.buffer.push({ level, message, fields, values });
}
@@ -35,6 +46,10 @@ class ArrayLogger extends AdvancedBase {
}
}
/**
* Logger that filters log entries based on enabled categories.
* Only logs messages for categories that have been explicitly enabled.
*/
class CategorizedToggleLogger extends AdvancedBase {
static PROPERTIES = {
categories: {
@@ -49,6 +64,14 @@ class CategorizedToggleLogger extends AdvancedBase {
}
static IMPLEMENTS = {
[TLogger]: {
/**
* Logs a message only if the category specified in fields is enabled.
* @param {string} level - The log level
* @param {string} message - The log message
* @param {Object} fields - Fields object that should contain a 'category' property
* @param {Array} values - Additional values to log
* @returns {*} Result from delegate logger if category is enabled, undefined otherwise
*/
log (level, message, fields, values) {
const category = fields.category;
if ( ! this.categories[category] ) return;
@@ -56,14 +79,26 @@ class CategorizedToggleLogger extends AdvancedBase {
}
}
}
/**
* Enables logging for the specified category.
* @param {string} category - The category to enable
*/
on (category) {
this.categories[category] = true;
}
/**
* Disables logging for the specified category.
* @param {string} category - The category to disable
*/
off (category) {
delete this.categories[category];
}
}
/**
* Logger that can be enabled or disabled globally.
* When disabled, all log messages are ignored.
*/
class ToggleLogger extends AdvancedBase {
static PROPERTIES = {
enabled: {
@@ -78,6 +113,14 @@ class ToggleLogger extends AdvancedBase {
}
static IMPLEMENTS = {
[TLogger]: {
/**
* Logs a message only if the logger is enabled.
* @param {string} level - The log level
* @param {string} message - The log message
* @param {Object} fields - Additional fields to include
* @param {Array} values - Additional values to log
* @returns {*} Result from delegate logger if enabled, undefined otherwise
*/
log (level, message, fields, values) {
if ( ! this.enabled) return;
return this.delegate.log(level, message, fields, values);
@@ -86,6 +129,10 @@ class ToggleLogger extends AdvancedBase {
}
}
/**
* Logger that outputs formatted messages to the console.
* Supports colored output using ANSI escape codes and different log levels.
*/
class ConsoleLogger extends AdvancedBase {
static MODULES = {
// This would be cool, if it worked in a browser.
@@ -125,6 +172,13 @@ class ConsoleLogger extends AdvancedBase {
}
static IMPLEMENTS = {
[TLogger]: {
/**
* Logs a formatted message to the console with color coding based on log level.
* @param {string} level - The log level (info, warn, error, debug)
* @param {string} message - The main log message
* @param {Object} fields - Additional fields to display
* @param {Array} values - Additional values to pass to console
*/
log (level, message, fields, values) {
const require = this.require;
const util = require('util');
@@ -147,6 +201,9 @@ class ConsoleLogger extends AdvancedBase {
}
}
/**
* Logger that adds a prefix to all log messages before delegating to another logger.
*/
class PrefixLogger extends AdvancedBase {
static PROPERTIES = {
prefix: {
@@ -161,6 +218,14 @@ class PrefixLogger extends AdvancedBase {
}
static IMPLEMENTS = {
[TLogger]: {
/**
* Logs a message with the configured prefix prepended to the message.
* @param {string} level - The log level
* @param {string} message - The original message
* @param {Object} fields - Additional fields to include
* @param {Array} values - Additional values to log
* @returns {*} Result from the delegate logger
*/
log (level, message, fields, values) {
return this.delegate.log(
level, this.prefix + message,
@@ -171,6 +236,9 @@ class PrefixLogger extends AdvancedBase {
}
}
/**
* Logger that adds default fields to all log entries before delegating to another logger.
*/
class FieldsLogger extends AdvancedBase {
static PROPERTIES = {
fields: {
@@ -186,6 +254,14 @@ class FieldsLogger extends AdvancedBase {
static IMPLEMENTS = {
[TLogger]: {
/**
* Logs a message with the configured default fields merged with provided fields.
* @param {string} level - The log level
* @param {string} message - The log message
* @param {Object} fields - Additional fields that will be merged with default fields
* @param {Array} values - Additional values to log
* @returns {*} Result from the delegate logger
*/
log (level, message, fields, values) {
return this.delegate.log(
level, message,
@@ -197,6 +273,10 @@ class FieldsLogger extends AdvancedBase {
}
}
/**
* Facade that provides a convenient interface for logging operations.
* Supports method chaining and category management.
*/
class LoggerFacade extends AdvancedBase {
static PROPERTIES = {
impl: {
@@ -213,12 +293,24 @@ class LoggerFacade extends AdvancedBase {
static IMPLEMENTS = {
[TLogger]: {
/**
* Basic log implementation (currently just outputs to console).
* @param {string} level - The log level
* @param {string} message - The log message
* @param {Object} fields - Additional fields
* @param {Array} values - Additional values
*/
log (level, message, fields, values) {
console.log()
}
}
}
/**
* Creates a new logger facade with additional default fields.
* @param {Object} fields - Default fields to add to all log entries
* @returns {LoggerFacade} New logger facade instance with the specified fields
*/
fields (fields) {
const new_delegate = new FieldsLogger({
fields,
@@ -229,13 +321,26 @@ class LoggerFacade extends AdvancedBase {
});
}
/**
* Logs an info-level message.
* @param {string} message - The message to log
* @param {...*} values - Additional values to include in the log
*/
info (message, ...values) {
this.impl.log('info', message, {}, values);
}
/**
* Enables logging for a specific category.
* @param {string} category - The category to enable
*/
on (category) {
this.cat.on(category);
}
/**
* Disables logging for a specific category.
* @param {string} category - The category to disable
*/
off (category) {
this.cat.off(category);
}