npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@spiritex/spiritex-core

v0.5.0

Published

The SpiritEx Server Core.

Readme

@spiritex/spiritex-core

  • Version: 0.5.0
  • Dated: 2025-10-01

Table of Contents

Overview

About SpiritEx Core

SpiritEx Core is a comprehensive Node.js server library that provides the foundational functionality needed for building distributed service-oriented applications.

Core Architecture

  • Modular Service Architecture: Services are organized in folders with separate schema and implementation files.
  • Multi-Transport Support: InProcess, HTTP, and AMQP transports
  • Built-in Standard Services: Member (authentication), Diagnostic (server info), SDK (client generation)

Key Features

  • User Management: Complete authentication system with JWT tokens, API keys, and session management
  • Dynamic SDK Generation: Automatically generates client SDKs and documentation from service schemas
  • Comprehensive Logging: Structured logging with log4js
  • MCP Plugin Support: Model Context Protocol integration for AI tooling
  • Peer-to-Peer Networking: Services can be distributed across multiple server instances

Dependencies & Tech Stack

  • NodeJS: Server development platform.
  • Express.js: HTTP server framework
  • Sequelize: ORM with MySQL2 and SQLite3 support
  • AMQP: Message queue transport via amqplib
  • Security: bcrypt for password hashing, JWT for tokens
  • Testing: Mocha test framework

Service Structure

Services follow a standardized pattern:

  • ServiceSchema.js: Defines API interface using JSON Schema
  • ServiceService.js: Implements business logic
  • Three user types: Network, Service, User with different access levels

Transport Layer

  • HTTP: REST API with CORS support and MCP plugin
  • AMQP: Message queue-based communication
  • InProcess: Direct function calls for same-process services

Concepts

Schema

Schema is a description of data objects and functions defined by a service. Every SpiritEx service must have a schema definition for it. Service schemas are used by SpiritEx Core to:

  • Name and parameterize transport endpoints (e.g. callable urls).
  • Define database table structures. Service data objects can be mapped to database tables.
  • Generate MCP Tools. Each service function becomes a tool in the MCP service.
  • Generate Client SDKs. Dynamic generation of programming libraries to work with the server.
  • Generate server documentation. SDK documentation constructed from service schema metadata.

Network

A network is group of SpiritEx Core servers that are configured to work together. All servera in a network share the same the services, schema, and network urls. The network urls list each service on the network and the url to locate it. A network is a single logical server that can be scaled by adding more servers.

Network Architecture

Through configuration, SpiritEx Core can be configured to run as a standalone server or as a client of another SpiritEx server. A SpiritEx server can be run with only certain services enabled and rely upon other servers for the disabled services. A group of SpiritEx servers that are configured to work together constitute a Network and represents a single logical server. Services can be scaled individually by running multiple server instances behind load balancers and proxies.

Peer Networking

SpiritEx Core provides a peer-to-peer network for server-to-server communication. During startup, SpiritEx Core will generate a ServiceClient module that is designed to be used as a client for all Services running on the network. Application services can call other services in the network, regardless of which server instance those services are running on.

Server Configuration

SpiritEx servers are driven by a single json configuration object, split into logical sections, that controls the server's behavior.

This configuration object is used to customize the following Core features:

  • Server startup and shutdown.
  • Service registration and discovery.
  • User authentication and authorization.
  • Logging and monitoring.
  • Database access and management.

Service Definitions

Service APIs are described by a json schema that defines a service's data objects, its commands, command paramters, and command return values. SpiritEx Core provides schema extensions to support an underlying database table. This schema drives the behavior of the following Core features:

  • Transport endpoint naming and parameters.
  • Database table structures.
  • MCP Tool Generation
  • Client SDK generation.
  • Server documentation.

Configuration

Server Configuration

Server configuration is provided by a configuration object that can be read from a JSON file or created in code. It is typical practice to have different configuration files for each server instance or environment. For example, you might have a configuration files for testing, staging, and production.

The Server Configuration object has these basic sections:

  • General: Contains the server name, version, and other general information.
  • Logger: Controls the logger output.
  • Network: Controls the network settings. The network describes a set of servers and their hosted services.
    • Credentials: Contains the network credentials. Used by applications when making service calls within the network.
    • Urls: Contains the urls for each service. These urls can point to the local server instance or to another server in the network. This is used by the Client SDK when making service calls.
  • Transports: Enable, disable, and configure the three transports: InProcess, Http, and Amqp.
  • Services: Enable, disable, and configure the services hosted by the server.

Services

About SpiritEx Services

Service Schema

Service structure is defined by json schema. It describes data objects used by the service in addition to service commands.

User Types

There are three main user types in the network:

  • Network: This is the highest level of access. It can access all services in the network.
  • Service: Used by services, it allows services to call each other.
  • User: Used by applications, it allows applications to call services on behalf of users.

All three of these user types require a user account such as those managed by the Member Service. This allows services to track who is calling which endpoints. Services can also further segeregate certian users by emplying the metadata field of the user account.

The Diagnostic Service

The Diagnostic Service is publicly available and provides server information. It can be used to verify that the server is running and obtain platform information.

Methods

| Method | Description | | :--- | :--- | | ServerInfo() | Gets server info. | | ServerError() | Throws a server error. |

The Sdk Service

The Sdk Service is also publicly available and provides access to the server's Client SDKs and documentation. It can be used to obtain the Client SDK for a specific service and user type. It can also be used to obtain schema and documentation for network services.

Methods

| Method | Description | | :--- | :--- | | NetworkUrls() | Gets the server addresses for all services on the network. | | Schema( UserType ) | Gets the schema for all services hosted by this server. | | Client( Transport, Platform, UserType ) | Downloads the SDK Client as a single source file for the target transport and platform. | | Documentation( Transport, Platform, UserType, UseLinks ) | Downloads the SDK documentation as a single markdown file. |

The Member Service

The Member Service is responsible for user management, authentication, and authorization. It runs on top of an Identity Provider service (such as Clerk) which associates a user's email address with a password.

Available Identity Providers

  • Local : Uses the local database to manage users and credentials.
  • Clerk : Uses Clerk [https://clerk.com/] to manage users and authentication.
  • ClerkMock : Uses a mock identity provider for testing purposes (always authenticates).

It is used to manage members and their access to the server. It is also used by users to authenticate with the server. This service can be disabled or overwritten by a custom implementation.

Methods

| Method | Description | | :--- | :--- | | NewSession( Strategy, Identifier, Secret ) | Authenticate with the network and retrieve a new network session and token. The new token is also returned in the Authorization header of the response | | NewNetworkToken() | Generate a new network token for an existing session. The new token is also returned in the Authorization header of the response. | | GetMySession() | Gets this session. | | ListMyApiKeys() | Lists the ApiKeys owned by the current user. | | CreateMyApiKey( Description, ExpirationMS ) | Creates an ApiKey for the current user. | | DestroyMyApiKey( ApiKeyID ) | Destroys an ApiKey. | | GetMyApiKey( ApiKeyID ) | Retrieves an ApiKey. | | LockMyApiKey( ApiKeyID ) | Locks an ApiKey on the network. | | UnlockMyApiKey( ApiKeyID ) | Unlocks an ApiKey on the network. |

Many more Member commands exist to manage sessions, api keys, etc.

Member Authenticators

The Always Authenticator

This authenticator approves all requests. It is useful during development and testing.

Configuration

{
	Services: {
		Member: {
			Authenticator: {
				Always: {} // No configuration options.
			}
		}
	}
}

The Json Authenticator

This authenticator approves requests against a pre-configured set of users. It is useful during development and testing.

Configuration

{
	Services: {
		Member: {
			Authenticator: {
				Json: {
					Users: {
						"admin@local": {
							first_name: 'Admin',
							last_name: 'User',
							secret: 'password',
						},
						"super@local": {
							first_name: 'Super',
							last_name: 'User',
							secret: 'password',
						},
					},
				}
			}
		}
	}
}

The Clerk Authenticator

This authenticator forwards requests the Clerk service (https://clerk.com).

Configuration

{
	Services: {
		Member: {
			Authenticator: {
				Clerk: {
					api_key: '', // ???
					publishable_key: 'pk_...',
					secret_key: 'sk_...',
					frontend_url: 'http://localhost:5500/public',
					clerk_domain: 'https://{domain}.clerk.accounts.dev',
					clerk_api_path: '/v1/client',
					// clerk_api_path: '/v1',
					jwt_public_key: `-----BEGIN PUBLIC KEY-----
					...
					-----END PUBLIC KEY-----`,
				},
			}
		}
	}
}

Custom Authenticators

Custom authenticators can be created by implementing an Authenticator object and setting it as the Member.Authenticator.

An Authenticator implements the Signin and GetSession methods:

// Simple-Authenticator.js :
module.exports = {

	//---------------------------------------------------------------------
	// Validates Secret for the given Identifier (email, phone, etc.)
	// Returns a SessionId and AuthorizationToken.
	Signin:	async function ( Identifier, Secret )
	{
		var session_id = `session-${Identifier}`; // Make up a session id.
		// Add some validation irl ...
		return {
			response: {
				status: "ok",
				created_session_id: session_id,
			},
			authorization_token: { // Simple plain text token (should use JWT irl).
				timestamp: Date.now(),
				identifier: Identifier,
				session_id: session_id,
			},
		};
	},

	//---------------------------------------------------------------------
	// Validates AuthorizationToken.
	// Does a session lookup and returns the associated user object.
	GetSession: async function ( AuthenticatorSessionID, AuthorizationToken )
	{
		// Validate parameters.
		if ( !AuthenticatorSessionID ) { throw new Error( `Missing session id.` ); }
		if ( !AuthorizationToken ) { throw new Error( `Missing authorization.` ); }
		if ( !AuthorizationToken.timestamp ) { throw new Error( `Invalid authorization.` ); }
		if ( !AuthorizationToken.identifier ) { throw new Error( `Invalid authorization.` ); }
		// Validate request.
		if ( AuthorizationToken.session_id !== AuthenticatorSessionID ) { throw new Error( `Invalid authorization.` ); }

	},

};

*** Simple Authenticator Example ***

const JSON_WEB_TOKEN = require( 'jsonwebtoken' );
const JWT_OPTIONS = { algorithm: 'HS256' };

const Server = ServerFactory( { ... server configuration ... } );

Server.Services.Member.Authenticator = {
	
	//---------------------------------------------------------------------
	Signin:	async function ( Identifier, Secret )
		{
			var result = {
				response: {
					status: "ok",
					created_session_id: `always-${Identifier}`,
				},
			};
			result.authorization_token = JSON_WEB_TOKEN.sign( Identifier, Server.Config.Network.network_key, JWT_OPTIONS );
			Logger.trace( `Always Authenticate > Signin for [${Identifier}]: ${result.response.status}` );
			return result;
		};


	//---------------------------------------------------------------------
	Plugin.GetSession =
		async function ( AuthenticatorSessionID, AuthorizationToken )
		{
			var identifier = JSON_WEB_TOKEN.decode( AuthorizationToken, Server.Config.Network.network_key );
			if ( AuthenticatorSessionID !== `always-${identifier}` ) { throw new Error( `Invalid Authenticator Session ID.` ); }
			var result = {
				response: {
					status: "ok",
					id: AuthenticatorSessionID,
					user: {
						id: `always-${identifier}`,
						first_name: identifier.split( '@' )[ 0 ],
						last_name: identifier.split( '@' )[ 1 ],
						primary_email_address_id: `always-${identifier}`,
						email_addresses: [
							{ id: `always-${identifier}`, email_address: identifier, },
						],
					},
				},
			};
			Logger.trace( `Always Authenticate > GetSession for [${AuthenticatorSessionID}]: ${result.response.status}` );
			return result;
		};

}

await Server.StartupServer();

Application Services

Applications using Core can define and host new application services. All services are remotable with any of the defined transports. Application services are defined in a folder and provide files that define the service via a schema and an implementation file that codes the details of the service commands.

The distinction between schema and implementation is important because they are loaded at different points within the Server's intialization.

Service Folder Structure

The server configuration file contains an entry for services_folder which specifies the root folder where the application's service files will be found. This root folder will have a subfolder named for each application service.

Services/					<--- services_folder
	|_ Calc/				<--- Calc Service
		|_ CalcSchema.js
		|_ CalcService.js
		|_ other-files.js
	|_ Echo/				<--- Echo Service
	...

Service Schema

The service schema will be named after the service (e.g. EchoSchema.js). Each service can use json schema to define custom objects that are used by the service. The service schema also defines the service's interface so that transports can make its endpoints available to applications.

When the schema is loaded, is provided with an instance of the Server object. This allows the schema to access the server's configuration and other services.

The service schema provides the structure to provide service entry points for each transport. The service schema is also used to generate client documentation and SDKs for services (see the Sdk service).

'use strict';
// Services/Echo/EchoSchema.js
// Definition file for the Echo service.

module.exports = function ( Server )
{
	return {

		$Objects: {}, // This service has no objects defined

		//---------------------------------------------------------------------
		// Echos the given text. Echo.EchoText( 'Hello' ) --> 'Hello'
		EchoText: {
			description: 'Echoes the given text.',
			groups: [],
			Arguments: { // <-- Json schema describing the command arguments
				type: 'object',
				properties: {
					Text: { type: 'string', description: 'The text to echo.' },
				},
				required: [ 'Text' ],
			},
			Returns: { // <-- Json schema describing the return value
				type: 'string',
				description: 'The echoed text.',
			},
		},

		//---------------------------------------------------------------------
		// Reverses the given text. Echo.ReverseText( 'Hello' ) --> 'olleH'
		ReverseText: {
			description: 'Reverses the given text.',
			groups: [],
			Arguments: {
				type: 'object',
				properties: {
					Text: { type: 'string', description: 'The text to be reversed.' },
				},
				required: [ 'Text' ],
			},
			Returns: {
				type: 'string',
				description: 'The reversed text.',
			},
		},

	};
};

Service Implementation

The service implementation file will be named after the service (e.g. EchoService.js). This file is read during the server startup and is used to create the service object.

The service implementation file is provided with an instance of the Server object. This allows the service implementation to access the server's configuration and other services.

'use strict';
// Services/Echo/EchoService.js
// Implementation file for the Echo service.

const SERVICE_NAME = 'Echo';

module.exports = function ( Server )
{
	var Logger = Server.LogManager.NewLogger( { service: SERVICE_NAME } );

	//---------------------------------------------------------------------
	var Service = {
		_Control: {
			service_name: SERVICE_NAME,
			service_enabled: Server.Config.Services[ SERVICE_NAME ].enabled,
			Startup:
				async function ()
				{
					Logger.debug( `Starting ${SERVICE_NAME} service ...` );
					Logger.debug( `${SERVICE_NAME} service started.` );
					return;
				},
			Shutdown:
				async function ()
				{
					Logger.debug( `Shutting down ${SERVICE_NAME} service ...` );
					Logger.debug( `${SERVICE_NAME} service stopped.` );
					return;
				},
		},
	};

	//---------------------------------------------------------------------
	Service.EchoText =
		async function ( Text, ApiContext )
		{
			Logger.debug( `Echoing the text: [${Text}].` );
			return Text;
		};

	//---------------------------------------------------------------------
	Service.ReverseText =
		async function ( Text, ApiContext )
		{
			Logger.debug( `Reversing the text: [${Text}].` );
			var text = Text.split( '' ).reverse().join( '' );
			return text;
		};

	//---------------------------------------------------------------------
	return Service;
}

Transports

About SpiritEx Transports

Clients

Client Sdk

The server can generate a client SDK and documentation for each service. The SDK is generated from the service schema and can be used to access the service from a client application. The documentation is generated from the service schema and can be used to understand the service's interface.

The resulting Client SDK and documentation will contain all services defined in the server. This includes services that are disabled in the service's configuration. In these cases, the Client SDK will rely on the Client.Urls configuration to access the correct service implementation.

The Client SDK is self-contained and can be used to access services from any application. The Client SDK can also be used by Javascript applications running in a browser. Part od the Server's initialization is to generate a complete set of Client SDKs for all services defined in the network. It then loads the Client SDK and exposes it to applications via the Server.ServiceClient object. This allows applications to access any service in the network without having to know the specific server that hosts the service.

When the Client SDK is generated, it is generated for each of the three main user types: Network, Service, and User.

Utilities

Logger

SqlManager

Generator

CommandLine

Deployment

Docker

Samples

Minimal Example

npm install --save @spiritex/spiritex-core
const ServerFactory = require( '@spiritex/spiritex-core'  );
const ServerConfig = require( './Config.js' );
const Server = ServerFactory( ServerConfig );

await Server.InitializeServer();
await Server.StartupServer();

...

await Server.ShutdownServer();