mirror of
https://github.com/HeyPuter/puter.git
synced 2026-02-13 09:19:28 -06:00
Merge branch 'main' of https://github.com/HeyPuter/puter
This commit is contained in:
@@ -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 ) {
|
||||
|
||||
@@ -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,
|
||||
}
|
||||
|
||||
@@ -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');
|
||||
|
||||
|
||||
@@ -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++ ) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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');
|
||||
|
||||
@@ -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: () => {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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 ?? []),
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user