### Seneca.js Quick Example: Service Composition and Communication Source: https://github.com/senecajs/seneca/blob/master/README.md This JavaScript example demonstrates how to build and compose Seneca services. It defines three simple plugins (rejector, approver, local) that handle a 'cmd:run' command. It then shows how to create Seneca instances, load plugins, listen on different transports (HTTP and default), and act on messages, including inter-service communication via clients. ```javascript 'use strict' var Seneca = require('seneca') // Functionality in seneca is composed into simple // plugins that can be loaded into seneca instances. function rejector () { this.add('cmd:run', (msg, done) => { return done(null, {tag: 'rejector'}) }) } function approver () { this.add('cmd:run', (msg, done) => { return done(null, {tag: 'approver'}) }) } function local () { this.add('cmd:run', function (msg, done) { this.prior(msg, (err, reply) => { return done(null, {tag: reply ? reply.tag : 'local'}) }) }) } // Services can listen for messages using a variety of // transports. In process and http are included by default. Seneca() .use(approver) .listen({type: 'http', port: '8260', pin: 'cmd:*'}) Seneca() .use(rejector) .listen(8270) // Load order is important, messages can be routed // to other services or handled locally. Pins are // basically filters over messages function handler (err, reply) { console.log(err, reply) } Seneca() .use(local) .act('cmd:run', handler) Seneca() .client({port: 8270, pin: 'cmd:run'}) .client({port: 8260, pin: 'cmd:run'}) .use(local) .act('cmd:run', handler) Seneca() .client({port: 8260, pin: 'cmd:run'}) .client({port: 8270, pin: 'cmd:run'}) .use(local) .act('cmd:run', handler) // Output // null { tag: 'local' } // null { tag: 'approver' } // null { tag: 'rejector' } ``` -------------------------------- ### Implement Async Plugin Initialization Source: https://context7.com/senecajs/seneca/llms.txt Demonstrates how to use the seneca.prepare method to perform asynchronous setup tasks before a plugin is marked ready. It also shows how to clean up resources using the seneca.destroy hook. ```javascript const Seneca = require('seneca') function databasePlugin(options) { const seneca = this let db = null seneca.prepare(async function() { console.log('Connecting to database...') db = await new Promise(resolve => { setTimeout(() => resolve({connected: true}), 100) }) console.log('Database connected') }) seneca.add('role:db,cmd:query', function(msg, done) { if (!db || !db.connected) { return done(new Error('Database not connected')) } done(null, {rows: [{id: 1, name: msg.name}]}) }) seneca.destroy(async function() { console.log('Closing database connection...') db = null }) return {name: 'database'} } Seneca().use(databasePlugin).ready(function() { this.act('role:db,cmd:query,name:test', console.log) }) ``` -------------------------------- ### Install Seneca.js via npm Source: https://github.com/senecajs/seneca/blob/master/README.md This command installs the Seneca.js library using npm, the Node.js package manager. It is the first step to using Seneca for building microservices. ```bash npm install seneca ``` -------------------------------- ### Synchronous Message Flow Example Source: https://github.com/senecajs/seneca/blob/master/docs/msg-transport-protocol.md Demonstrates the structure of a synchronous request and response between two services, including tracking metadata and local processing timestamps. ```javascript { a: 1, meta$: { sid: 'A', act: true, mid: 'm01', cid: 'c01', snc: true, trk: [{ sid: 'A', mid: 'm01', tms: [1461023850000] }], rtn: { urn: 'http://192.168.0.1/rtn' } } } ``` ```javascript { x: 1, meta$: { rid: 'B', res: true, mid: 'm01', cid: 'c01', trk: [{ sid: 'A', rid: 'B', mid: 'm01', tms: [1461023850000, 1461023850200, 1461023850250] }] } } ``` -------------------------------- ### seneca.ready - Execute Code When Seneca is Ready Source: https://context7.com/senecajs/seneca/llms.txt Executes a callback function once the Seneca instance has finished initializing all plugins and is ready to process messages. This can be used for setup tasks or to initiate message actions. Supports both callback and Promise-based styles, as well as multiple handlers. ```javascript const Seneca = require('seneca') // Callback style Seneca() .use('promisify') .add('cmd:hello', function(msg, done) { done(null, {message: 'Hello!'}) }) .ready(function() { console.log('Seneca ready:', this.id) this.act('cmd:hello', console.log) }) // Promise style (with promisify plugin) Seneca() .use('promisify') .add('cmd:test', function(msg, done) { done(null, {ok: true}) }) .ready() .then(function(seneca) { return seneca.post('cmd:test') }) .then(function(result) { console.log('Result:', result) }) // Multiple ready handlers const seneca = Seneca() .ready(function() { console.log('First ready handler') }) .ready(function() { console.log('Second ready handler') }) ``` -------------------------------- ### Asynchronous Message Flow Example Source: https://github.com/senecajs/seneca/blob/master/docs/msg-transport-protocol.md Illustrates an asynchronous message flow where a request is sent without expecting a response, showing the tracking metadata at the sender and receiver. ```javascript { a: 2, meta$: { sid: 'A', act: true, mid: 'm02', cid: 'c02', snc: false, trk: [{ sid: 'A', mid: 'm02', tms: [1461023851000] }] } } ``` ```javascript { a: 2, meta$: { sid: 'A', act: true, mid: 'm02', cid: 'c02', snc: false, trk: [{ sid: 'A', rid: 'B', mid: 'm02', tms: [1461023851000, 1461023851200] }] } } ``` -------------------------------- ### seneca.listen - Start Listening for Remote Messages Source: https://context7.com/senecajs/seneca/llms.txt Starts listening for inbound messages over various transports like HTTP or TCP. It can be configured with specific ports, hosts, and message patterns to filter incoming requests. Callbacks can be provided for post-listen operations. ```javascript const Seneca = require('seneca') // Basic HTTP listener on default port 10101 const seneca = Seneca() .add('cmd:convert', function(msg, done) { const colors = {red: 'FF0000', green: '00FF00', blue: '0000FF'} done(null, {hex: colors[msg.color] || '000000'}) }) .listen() // Listen on specific port Seneca() .add('cmd:greet', function(msg, done) { done(null, {greeting: 'Hello, ' + msg.name}) }) .listen(8080) // Listen with configuration Seneca() .add('role:api,cmd:status', function(msg, done) { done(null, {status: 'ok', uptime: process.uptime()}) }) .listen({ type: 'http', port: 9000, host: '0.0.0.0', pin: 'role:api,cmd:*' // Only expose matching patterns }) // Listen with callback Seneca() .add('cmd:ping', function(msg, done) { done(null, {pong: true}) }) .listen({port: 9090}, function(err) { if (err) return console.error(err) console.log('Listening on port 9090') }) ``` -------------------------------- ### Create Seneca Instance Source: https://context7.com/senecajs/seneca/llms.txt Demonstrates how to create and configure a new Seneca instance with various options like logging, timeouts, and test mode. This is the foundational step for any Seneca application. ```javascript const Seneca = require('seneca') // Basic instance const seneca = Seneca() // Instance with options const seneca = Seneca({ tag: 'my-service', // Instance tag for identification timeout: 30000, // Action timeout in milliseconds log: 'silent', // Log level: 'silent', 'test', 'standard' }) // Test mode with verbose logging const seneca = Seneca().test('print') // Quiet mode for production const seneca = Seneca().quiet() ``` -------------------------------- ### Load plugins with Seneca.use Source: https://context7.com/senecajs/seneca/llms.txt Shows how to extend Seneca functionality by loading plugins. Plugins can be defined as functions or loaded from modules, and can accept configuration options. ```javascript function salesTaxPlugin(options) { const rate = options.rate || 0.23 const country = options.country || 'IE' this.add({role: 'shop', cmd: 'salestax'}, function(msg, done) { const total = msg.net * (1 + rate) done(null, {total: total, country: country}) }) return {name: 'sales-tax'} } const seneca = Seneca() .use(salesTaxPlugin, {rate: 0.21, country: 'DE'}) ``` -------------------------------- ### Distributing Services via Network Source: https://github.com/senecajs/seneca/blob/master/README.md Illustrates how to expose a service using 'seneca.listen' and consume it via 'seneca.client', allowing actions to be executed across different processes. ```javascript // Server-side seneca.add({cmd: 'config'}, function (msg, done) { var config = {rate: 0.23} var value = config[msg.prop] done(null, { value: value }) }) seneca.listen() // Client-side seneca.add({cmd: 'salestax'}, function (msg, done) { seneca.act({cmd: 'config', prop: 'rate' }, function (err, result) { var rate = parseFloat(result.value) var total = msg.net * (1 + rate) done(null, { total: total }) }) }) seneca.client() seneca.act('cmd:salestax,net:100', function (err, result) { console.log(result.total) }) ``` -------------------------------- ### Plugin with Async Initialization Source: https://context7.com/senecajs/seneca/llms.txt Demonstrates how to create a Seneca.js plugin that performs asynchronous initialization using the `prepare` method and includes cleanup logic with `destroy`. ```APIDOC ## Plugin with Async Initialization Create a plugin with asynchronous initialization using the prepare method. ### Description This code defines a Seneca.js plugin that connects to a database asynchronously during its preparation phase. It exposes a `role:db,cmd:query` action that can only be used once the database is connected. It also includes a `destroy` method for cleanup. ### Method `seneca.use(pluginFunction)` `seneca.prepare(async function() { ... }) `seneca.destroy(async function() { ... }) ### Endpoint N/A (Plugin registration and internal actions) ### Parameters N/A ### Request Example N/A ### Response N/A ### Code Example ```javascript const Seneca = require('seneca') function databasePlugin(options) { const seneca = this let db = null // Async preparation runs before plugin is ready seneca.prepare(async function() { console.log('Connecting to database...') // Simulate async database connection db = await new Promise(resolve => { setTimeout(() => resolve({connected: true}), 100) }) console.log('Database connected') }) // Actions can use the initialized resource seneca.add('role:db,cmd:query', function(msg, done) { if (!db || !db.connected) { return done(new Error('Database not connected')) } // Simulated query done(null, {rows: [{id: 1, name: msg.name}]}) }) // Cleanup on close seneca.destroy(async function() { console.log('Closing database connection...') db = null }) return {name: 'database'} } Seneca() .use(databasePlugin) .ready(function() { this.act('role:db,cmd:query,name:test', console.log) // Output: null {rows: [{id: 1, name: 'test'}]} }) ``` ``` -------------------------------- ### Chaining Seneca Actions for Configuration Source: https://github.com/senecajs/seneca/blob/master/README.md Shows how one action can call another action to retrieve configuration data. This demonstrates the decoupling of logic from data sources. ```javascript seneca.add({cmd: 'config'}, function (msg, done) { var config = {rate: 0.23} var value = config[msg.prop] done(null, {value: value}) }) seneca.add({cmd: 'salestax'}, function (msg, done) { seneca.act({cmd: 'config', prop: 'rate'}, function (err, result) { var rate = parseFloat(result.value) var total = msg.net * (1 + rate) done(null, {total: total}) }) }) seneca.act({cmd: 'salestax', net: 100}, function (err, result) { console.log(result.total) }) ``` -------------------------------- ### Running Seneca.js Microservices Source: https://github.com/senecajs/seneca/blob/master/README.md These commands show how to run a Seneca.js microservice script. The first command runs the service normally, suitable for production or containerized environments. The second command enables test mode, providing human-readable, full debug logs for easier development and debugging. ```bash $ node microservice.js ``` ```bash $ node microservice.js --seneca.test ``` -------------------------------- ### Chain actions with Seneca.prior Source: https://context7.com/senecajs/seneca/llms.txt Explains how to use prior to call previously defined actions for the same pattern. This is useful for implementing middleware, logging, or validation logic. ```javascript seneca.add('cmd:run', function(msg, done) { console.log('Processing:', msg.value) this.prior(msg, function(err, result) { if (err) return done(err) result.processed = true done(null, result) }) }) ``` -------------------------------- ### seneca.ready Source: https://context7.com/senecajs/seneca/llms.txt Registers a callback or returns a promise that resolves when the Seneca instance is fully initialized. ```APIDOC ## [METHOD] seneca.ready ### Description Executes a callback or resolves a promise when Seneca has finished initializing all plugins and is ready to process messages. ### Method ready(callback) ### Response #### Success Response (200) - **instance** (object) - The initialized Seneca instance. ``` -------------------------------- ### Using Jsonic Abbreviated Syntax Source: https://github.com/senecajs/seneca/blob/master/README.md Demonstrates the use of Jsonic string patterns to simplify the invocation of Seneca actions. ```javascript seneca.act('cmd:salestax,net:100', function (err, result) { console.log(result.total) }) seneca.act('cmd:salestax', {net: 100}, function (err, result) { console.log(result.total) }) ``` -------------------------------- ### Action Discovery (find, has, list) Source: https://context7.com/senecajs/seneca/llms.txt Methods to query the registry for registered action patterns and their definitions. ```APIDOC ## Action Discovery Methods ### Description - **has(pattern)**: Returns true if an action exists for the pattern. - **find(pattern)**: Returns the action definition object. - **list(pattern)**: Returns an array of all actions matching the pattern. ### Parameters - **pattern** (string) - Optional - The pattern to filter actions by. ### Response Example [{cmd: 'sum', role: 'math'}, {cmd: 'product', role: 'math'}] ``` -------------------------------- ### Send messages with Seneca.post Source: https://context7.com/senecajs/seneca/llms.txt Demonstrates how to use the post method to send messages and receive a Promise, allowing for async/await syntax or standard promise chaining. This requires the promisify plugin to be loaded. ```javascript const Seneca = require('seneca') const seneca = Seneca() .use('promisify') .add('role:math,cmd:sum', function(msg, done) { done(null, {answer: msg.left + msg.right}) }) async function calculate() { const result = await seneca.post({ role: 'math', cmd: 'sum', left: 5, right: 3 }) console.log('Answer:', result.answer) } seneca.ready(calculate) ``` -------------------------------- ### Define and Execute a Basic Seneca Action Source: https://github.com/senecajs/seneca/blob/master/README.md Demonstrates how to register a pattern using 'seneca.add' and trigger it using 'seneca.act'. The action calculates a sales tax based on a provided net value. ```javascript var seneca = require('seneca')() seneca.add({cmd: 'salestax'}, function (msg, done) { var rate = 0.23 var total = msg.net * (1 + rate) done(null, {total: total}) }) seneca.act({cmd: 'salestax', net: 100}, function (err, result) { console.log(result.total) }) ``` -------------------------------- ### Run Seneca.js Project Tests and Coverage Source: https://github.com/senecajs/seneca/blob/master/README.md Commands to execute the test suite and generate a code coverage report for the project. ```bash npm run test npm run coverage; open docs/coverage.html ``` -------------------------------- ### Implement Pattern-Based Sales Tax Logic in Seneca.js Source: https://github.com/senecajs/seneca/blob/master/README.md This snippet shows how to define multiple Seneca actions for the same command with different patterns. It handles fixed rates, state-specific rates for the US, and category-specific rates for Ireland. ```javascript // fixed rate seneca.add({cmd: 'salestax'}, function (msg, done) { var rate = 0.23 var total = msg.net * (1 + rate) done(null, { total: total }) }) // local rates seneca.add({cmd: 'salestax', country: 'US'}, function (msg, done) { var state = { 'NY': 0.04, 'CA': 0.0625 } var rate = state[msg.state] var total = msg.net * (1 + rate) done(null, {total: total}) }) // categories seneca.add({ cmd: 'salestax', country: 'IE' }, function (msg, done) { var category = { 'top': 0.23, 'reduced': 0.135 } var rate = category[msg.category] var total = msg.net * (1 + rate) done(null, { total: total }) }) seneca.act('cmd:salestax,net:100,country:DE', function (err, result) { console.log('DE: ' + result.total) }) seneca.act('cmd:salestax,net:100,country:US,state:NY', function (err, result) { console.log('US,NY: ' + result.total) }) seneca.act('cmd:salestax,net:100,country:IE,category:reduced', function (err, result) { console.log('IE: ' + result.total) }) ``` -------------------------------- ### Subscribe to messages with Seneca.sub Source: https://context7.com/senecajs/seneca/llms.txt Demonstrates how to observe message flow by subscribing to patterns. Subscriptions allow monitoring of inbound or outbound messages without modifying the original action response. ```javascript seneca.sub('cmd:order', function(msg) { console.log('Order received:', msg) }) seneca.sub({cmd: 'order', out$: true}, function(msg) { console.log('Order response:', msg) }) ``` -------------------------------- ### seneca.client - Connect to Remote Seneca Services Source: https://context7.com/senecajs/seneca/llms.txt Connects to remote Seneca services to send messages. Clients can be configured with specific ports and message pins to route messages to the correct service. The `ready` callback is used to execute code once the client is connected and ready to send messages. ```javascript const Seneca = require('seneca') // Basic client connecting to default port 10101 const client = Seneca() .client() .ready(function() { this.act('cmd:config,prop:rate', function(err, result) { console.log('Rate:', result.value) }) }) // Client with specific port and pin const colorClient = Seneca() .client({ port: 9090, pin: 'cmd:convert' // Route matching messages to this server }) .ready(function() { this.act({cmd: 'convert', color: 'red'}, function(err, result) { console.log('Hex:', result.hex) // Output: Hex: FF0000 }) }) // Multiple clients for different services Seneca() .client({port: 8001, pin: 'role:auth,cmd:*'}) .client({port: 8002, pin: 'role:data,cmd:*'}) .add('cmd:login', function(msg, done) { // Will route to auth service this.act('role:auth,cmd:validate', {token: msg.token}, function(err, auth) { if (err) return done(err) // Will route to data service this.act('role:data,cmd:user', {id: auth.userId}, done) }.bind(this)) }) .ready(function() { this.act('cmd:login,token:abc123', console.log) }) ``` -------------------------------- ### Register Async Actions with seneca.message Source: https://context7.com/senecajs/seneca/llms.txt Demonstrates registering asynchronous action functions using seneca.message, which returns Promises and supports async/await syntax. This is useful for handlers involving I/O operations or other asynchronous tasks. ```javascript const Seneca = require('seneca') const seneca = Seneca() .use('promisify') // Enable promise-based API // Define async message handler seneca.message('cmd:config', async function(msg) { const config = { rate: 0.23, currency: 'EUR' } return {value: config[msg.prop]} }) // Async handler with database operations seneca.message('role:user,cmd:create', async function(msg) { // Simulated async database operation const user = { id: Date.now(), name: msg.name, email: msg.email } return {ok: true, user: user} }) // Using async/await in ready callback seneca.ready(async function() { const result = await this.post({cmd: 'config', prop: 'rate'}) console.log('Config rate:', result.value) // Output: Config rate: 0.23 }) ``` -------------------------------- ### seneca.use - Load Plugins Source: https://context7.com/senecajs/seneca/llms.txt Load plugins to extend Seneca functionality. Plugins can add actions, modify behavior, or integrate external services. ```APIDOC ## seneca.use - Load Plugins ### Description Load plugins to extend Seneca functionality. Plugins can add actions, modify behavior, or integrate external services. ### Method USE (internal Seneca method) ### Endpoint N/A (Internal Seneca plugin loading) ### Parameters - **plugin** (function or string) - Required - The plugin function or its name (if installed as an npm module). - **options** (object) - Optional - Configuration options for the plugin. ### Request Example ```javascript // Load plugin with options const seneca = Seneca() .use(salesTaxPlugin, {rate: 0.21, country: 'DE'}) // Load plugin from npm module // seneca.use('seneca-entity') ``` ### Response N/A (This method modifies the Seneca instance and does not return a direct response in the typical API sense.) ``` -------------------------------- ### Handle Errors in Seneca Actions Source: https://context7.com/senecajs/seneca/llms.txt Explains how to implement global error handlers and action-specific error reporting using seneca.fail and seneca.error. This ensures consistent error propagation and debugging across microservices. ```javascript const Seneca = require('seneca') const seneca = Seneca().error(function(err) { console.error('Seneca error:', err.message) }) seneca.add('role:user,cmd:create', function(msg, done) { if (!msg.email) { return done(this.fail('missing_email', {field: 'email'})) } if (!msg.email.includes('@')) { return done(this.error('invalid_email', {email: msg.email})) } done(null, {id: Date.now(), email: msg.email}) }) seneca.add('role:api,cmd:fetch', async function(msg) { try { const result = await fetch(msg.url) return {data: result} } catch (err) { throw this.error(err, 'api_fetch_failed', {url: msg.url}) } }) ``` -------------------------------- ### seneca.prior - Call Prior Actions Source: https://context7.com/senecajs/seneca/llms.txt Call the previously defined action for the same pattern, enabling action chains and middleware-like behavior. ```APIDOC ## seneca.prior - Call Prior Actions ### Description Call the previously defined action for the same pattern, enabling action chains and middleware-like behavior. ### Method PRIOR (internal Seneca method) ### Endpoint N/A (Internal Seneca action chaining) ### Parameters - **msg** (object) - Required - The message object to pass to the prior action. - **done** (function) - Required - The callback function to handle the result or error from the prior action. ### Request Example ```javascript // Inside an action definition: this.prior(msg, function(err, result) { if (err) return done(err) // Process result from prior action done(null, result) }) ``` ### Response N/A (This method is used internally within action definitions to chain behavior.) ``` -------------------------------- ### seneca.find / seneca.has / seneca.list - Action Discovery Source: https://context7.com/senecajs/seneca/llms.txt Provides methods to query and discover registered actions within a Seneca instance. `has()` checks for the existence of an action pattern, `find()` returns the definition of a specific action, and `list()` returns all actions matching a given pattern or all registered actions if no pattern is provided. ```javascript const Seneca = require('seneca') const seneca = Seneca() .add('role:math,cmd:sum', (msg, done) => done(null, {})) .add('role:math,cmd:product', (msg, done) => done(null, {})) .add('role:user,cmd:create', (msg, done) => done(null, {})) .add('role:user,cmd:find', (msg, done) => done(null, {})) seneca.ready(function() { // Check if a pattern has an action console.log(this.has('role:math,cmd:sum')) // Output: true console.log(this.has('role:unknown,cmd:test')) // Output: false // Find action definition for a pattern const actdef = this.find('role:math,cmd:sum') console.log('Action ID:', actdef.id) console.log('Pattern:', actdef.pattern) // List all actions matching a pattern const mathActions = this.list('role:math') console.log('Math actions:', mathActions) // Output: [{cmd: 'sum', role: 'math'}, {cmd: 'product', role: 'math'}] const allActions = this.list() console.log('Total actions:', allActions.length) }) ``` -------------------------------- ### Send Messages with seneca.act Source: https://context7.com/senecajs/seneca/llms.txt Illustrates how to send messages using seneca.act to trigger registered action patterns. It covers callback-style and string pattern syntax, and demonstrates chaining multiple 'act' calls. ```javascript const Seneca = require('seneca') const seneca = Seneca() .add('cmd:salestax', function(msg, done) { done(null, {total: msg.net * 1.23}) }) // Callback style seneca.act({cmd: 'salestax', net: 100}, function(err, result) { if (err) return console.error(err) console.log(result.total) // Output: 123 }) // Using string pattern syntax with additional data seneca.act('cmd:salestax', {net: 200}, function(err, result) { console.log(result.total) // Output: 246 }) // Pure string syntax seneca.act('cmd:salestax,net:50', function(err, result) { console.log(result.total) // Output: 61.5 }) // Chaining multiple acts seneca .act('cmd:salestax,net:100', console.log) .act('cmd:salestax,net:200', console.log) ``` -------------------------------- ### seneca.delegate - Create Scoped Instance Source: https://context7.com/senecajs/seneca/llms.txt Creates a new Seneca instance (a delegate) that inherits from the parent instance but has fixed arguments automatically merged into every message it sends. This is useful for creating scoped services or instances with pre-configured options, like a default currency for a shop service. ```javascript const Seneca = require('seneca') const seneca = Seneca() .add('role:shop,cmd:price', function(msg, done) { const prices = {widget: 10, gadget: 25} const price = prices[msg.product] || 0 const currency = msg.currency || 'USD' done(null, {price: price, currency: currency}) }) // Create a delegate with fixed currency const euroShop = seneca.delegate({currency: 'EUR'}) seneca.ready(function() { // Original instance seneca.act('role:shop,cmd:price,product:widget', function(err, result) { console.log(result) // Output: {price: 10, currency: 'USD'} }) // Delegate automatically includes currency:EUR euroShop.act('role:shop,cmd:price,product:widget', function(err, result) { console.log(result) // Output: {price: 10, currency: 'EUR'} }) }) ``` -------------------------------- ### Register Actions with seneca.add Source: https://context7.com/senecajs/seneca/llms.txt Shows how to register action functions for specific message patterns using seneca.add. This function supports various pattern syntaxes, including object-based, string-based, and patterns with validation rules using Gubu shapes. ```javascript const Seneca = require('seneca') const seneca = Seneca() // Basic pattern registration with callback style seneca.add({cmd: 'salestax'}, function(msg, done) { const rate = 0.23 const total = msg.net * (1 + rate) done(null, {total: total}) }) // Using string pattern syntax (jsonic format) seneca.add('cmd:greet', function(msg, done) { done(null, {greeting: 'Hello, ' + msg.name}) }) // Pattern with multiple properties for more specific matching seneca.add({cmd: 'salestax', country: 'US'}, function(msg, done) { const rates = {'NY': 0.04, 'CA': 0.0625} const rate = rates[msg.state] || 0.05 const total = msg.net * (1 + rate) done(null, {total: total}) }) // Using pattern with validation rules (Gubu shapes) seneca.add({ role: 'math', cmd: 'sum', left: Number, // Validates that left is a number right: Number // Validates that right is a number }, function(msg, done) { done(null, {answer: msg.left + msg.right}) }) // Test the patterns seneca.act({cmd: 'salestax', net: 100}, console.log) // Output: null {total: 123} seneca.act({cmd: 'salestax', country: 'US', state: 'NY', net: 100}, console.log) // Output: null {total: 104} ``` -------------------------------- ### Error Handling in Actions Source: https://context7.com/senecajs/seneca/llms.txt Illustrates how to handle errors within Seneca.js actions using the global `error` handler and the `fail` and `error` methods within actions. ```APIDOC ## Error Handling Handle errors in actions using the error handler and fail methods. ### Description This section details how to implement error handling in Seneca.js. It covers setting up a global error handler and using specific methods like `this.fail()` and `this.error()` within actions to generate and propagate errors, including wrapping external errors. ### Method `seneca.error(handlerFunction)` `this.fail(code, [details])` `this.error(err, [code], [details])` ### Endpoint N/A (Internal action error handling) ### Parameters N/A ### Request Example N/A ### Response N/A ### Code Example ```javascript const Seneca = require('seneca') const seneca = Seneca() // Global error handler .error(function(err) { console.error('Seneca error:', err.message) }) // Action that validates input and throws errors seneca.add('role:user,cmd:create', function(msg, done) { // Use seneca.fail to throw descriptive errors if (!msg.email) { return done(this.fail('missing_email', {field: 'email'})) } if (!msg.email.includes('@')) { return done(this.error('invalid_email', {email: msg.email})) } done(null, {id: Date.now(), email: msg.email}) }) // Action with try-catch for external errors seneca.add('role:api,cmd:fetch', async function(msg) { try { // Simulated external API call const result = await fetch(msg.url) return {data: result} } catch (err) { // Wrap external errors throw this.error(err, 'api_fetch_failed', {url: msg.url}) } }) seneca.ready(function() { // Missing email error this.act('role:user,cmd:create', function(err, result) { if (err) console.log('Error:', err.message) }) // Valid request this.act('role:user,cmd:create,email:test@example.com', function(err, result) { console.log('Created:', result) }) }) ``` ``` -------------------------------- ### seneca.delegate - Create Scoped Instance Source: https://context7.com/senecajs/seneca/llms.txt Creates a new Seneca instance that inherits from the parent but includes fixed arguments merged into every message sent. ```APIDOC ## seneca.delegate(fixedArgs) ### Description Creates a scoped instance where specified arguments are automatically injected into all outgoing messages. ### Parameters - **fixedArgs** (object) - Required - Key-value pairs to merge into every message. ### Request Example const euroShop = seneca.delegate({currency: 'EUR'}) ``` -------------------------------- ### seneca.client Source: https://context7.com/senecajs/seneca/llms.txt Connects the Seneca instance to a remote service to enable message routing across network boundaries. ```APIDOC ## [METHOD] seneca.client ### Description Connects to remote Seneca services to send messages over a transport. ### Method client(options) ### Parameters #### Options - **port** (number) - Optional - Remote port to connect to - **pin** (string) - Optional - Pattern to route to this specific client ### Request Example .client({ port: 9090, pin: 'cmd:convert' }) ### Response #### Success Response (200) - **instance** (object) - Returns the Seneca instance for chaining. ``` -------------------------------- ### seneca.post - Promise-based Message Sending Source: https://context7.com/senecajs/seneca/llms.txt Send a message and receive a Promise for the result, enabling async/await usage for asynchronous operations. ```APIDOC ## seneca.post - Promise-based Message Sending ### Description Send a message and receive a Promise for the result, enabling async/await usage. ### Method POST (conceptually, actual implementation uses internal message passing) ### Endpoint N/A (Internal Seneca message pattern) ### Parameters #### Message Pattern - **role** (string) - Required - The role of the service to target. - **cmd** (string) - Required - The command to execute. - **...other fields** (any) - Optional - Additional fields to pass in the message. ### Request Example ```json { "role": "math", "cmd": "sum", "left": 5, "right": 3 } ``` ### Response #### Success Response (200) - **...fields** (any) - Description of the result from the service. #### Response Example ```json { "answer": 8 } ``` ``` -------------------------------- ### seneca.wrap - Wrap Multiple Actions Source: https://context7.com/senecajs/seneca/llms.txt Wraps multiple actions matching a specific pattern with a common function to implement cross-cutting concerns like logging or authorization. ```APIDOC ## seneca.wrap(pattern, wrapperFunction) ### Description Wraps actions matching the provided pattern. The wrapper function receives the original message and a 'prior' function to execute the original action. ### Parameters - **pattern** (string/object) - Required - The pattern to match actions against. - **wrapperFunction** (function) - Required - The function to execute, receiving (msg, done) and using 'this.prior' to call the original action. ### Request Example seneca.wrap('role:math', function(msg, done) { ... this.prior(msg, done) ... }) ``` -------------------------------- ### Chained Synchronous Message Flow Source: https://github.com/senecajs/seneca/blob/master/docs/msg-transport-protocol.md Shows a complex interaction where service A calls B, which in turn calls C, demonstrating how the 'trk' array accumulates history across multiple hops. ```javascript { b: 1, meta$: { sid: 'B', act: true, mid: 'm04', cid: 'c03', snc: true, trk: [ { sid: 'A', rid: 'B', mid: 'm03', tms: [1461023852000, 1461023852200] }, { sid: 'B', mid: 'm04', tms: [1461023852300] } ], rtn: { urn: 'http://192.168.0.2/rtn' } } } ``` -------------------------------- ### seneca.listen Source: https://context7.com/senecajs/seneca/llms.txt Configures the Seneca instance to listen for inbound messages over a transport layer like HTTP or TCP. ```APIDOC ## [METHOD] seneca.listen ### Description Starts listening for inbound messages over a transport. This allows the instance to act as a server for other Seneca clients. ### Method listen(options, callback) ### Parameters #### Options - **type** (string) - Optional - Transport type (default: 'http') - **port** (number) - Optional - Port to listen on (default: 10101) - **host** (string) - Optional - Host address (default: '0.0.0.0') - **pin** (string) - Optional - Pattern to filter inbound messages ### Request Example .listen({ type: 'http', port: 9000, host: '0.0.0.0' }) ### Response #### Success Response (200) - **instance** (object) - Returns the Seneca instance for chaining. ``` -------------------------------- ### seneca.decorate - Extend Seneca Instance Source: https://context7.com/senecajs/seneca/llms.txt Adds custom methods or properties to the Seneca instance, making them available across all plugins and actions. ```APIDOC ## seneca.decorate(name, value) ### Description Extends the Seneca instance with new functionality or shared resources. ### Parameters - **name** (string) - Required - The name of the property or method to add. - **value** (any) - Required - The function or object to attach to the instance. ### Request Example seneca.decorate('timestamp', function() { return new Date().toISOString() }) ``` -------------------------------- ### seneca.close Source: https://context7.com/senecajs/seneca/llms.txt Gracefully shuts down the Seneca instance, ensuring all in-flight actions are completed before exiting. ```APIDOC ## [METHOD] seneca.close ### Description Closes the Seneca instance gracefully, allowing in-flight actions to complete. ### Method close(callback) ### Response #### Success Response (200) - **err** (error) - Returns null if successful, or an error object if closing fails. ``` -------------------------------- ### seneca.decorate - Extend Seneca Instance Source: https://context7.com/senecajs/seneca/llms.txt Adds custom methods or properties to a Seneca instance, making them available across all plugins and actions within that instance. This is useful for adding utility functions or shared resources like caches. Decorated methods can be accessed using `this.methodName()` within Seneca actions. ```javascript const Seneca = require('seneca') const seneca = Seneca() // Add a utility method seneca.decorate('timestamp', function() { return new Date().toISOString() }) // Add a shared resource seneca.decorate('cache', new Map()) // Use decorated methods in actions seneca.add('cmd:log', function(msg, done) { const entry = { timestamp: this.timestamp(), message: msg.message } this.cache.set(msg.id, entry) done(null, entry) }) seneca.ready(function() { this.act('cmd:log,id:1,message:Hello', function(err, result) { console.log(result) // Output: {timestamp: '2024-01-15T10:30:00.000Z', message: 'Hello'} console.log('Cached:', this.cache.get('1')) }) }) ``` -------------------------------- ### seneca.add - Register Action Patterns Source: https://context7.com/senecajs/seneca/llms.txt Registers a function to be executed when a message matching a specific pattern is received. ```APIDOC ## [METHOD] seneca.add ### Description Registers an action function for a specific message pattern. When a message matching the pattern is received, the associated function is executed. ### Method N/A (Internal Framework Method) ### Endpoint seneca.add(pattern, action) ### Parameters #### Path Parameters - **pattern** (Object/String) - Required - The JSON pattern or JSONic string to match against. - **action** (Function) - Required - The function to execute: function(msg, done) { ... } ### Request Example seneca.add({cmd: 'salestax'}, function(msg, done) { done(null, {total: msg.net * 1.23}) }) ### Response #### Success Response (200) - **result** (Object) - The result returned by the callback function. #### Response Example { "total": 123 } ``` -------------------------------- ### seneca.wrap - Wrap Multiple Actions Source: https://context7.com/senecajs/seneca/llms.txt Wraps multiple Seneca actions matching a pattern with a common function. This is useful for implementing cross-cutting concerns such as logging, authorization, or timing across a set of related actions. It takes a pattern and a function that will be executed before or after the original action. ```javascript const Seneca = require('seneca') const seneca = Seneca() .add('role:math,cmd:sum', function(msg, done) { done(null, {answer: msg.left + msg.right}) }) .add('role:math,cmd:product', function(msg, done) { done(null, {answer: msg.left * msg.right}) }) // Wrap all role:math actions with timing seneca.wrap('role:math', function(msg, done) { const start = Date.now() this.prior(msg, function(err, result) { const duration = Date.now() - start console.log('Action took:', duration, 'ms') if (result) { result.duration = duration } done(err, result) }) }) seneca.act('role:math,cmd:sum,left:5,right:3', function(err, result) { console.log(result) // Output: {answer: 8, duration: 1} }) ``` -------------------------------- ### Seneca Message Structure Source: https://github.com/senecajs/seneca/blob/master/docs/msg-transport-protocol.md Overview of the message structure including the meta$ object for tracking and correlation. ```APIDOC ## Seneca Message Protocol ### Description Seneca messages are objects containing data and a `meta$` object. The `meta$` object handles message identification (`mid`), correlation (`cid`), and tracking (`trk`) across services. ### Parameters #### Request Body - **meta$** (object) - Required - Contains protocol metadata. - **sid** (string) - Sender ID. - **mid** (string) - Unique Message ID. - **cid** (string) - Correlation ID for chained requests. - **snc** (boolean) - Synchronous flag. - **trk** (array) - Tracking history of the message. ### Response #### Success Response (200) - **meta$** (object) - Response metadata including `rid` (receiver ID) and updated `trk` array with timing (`tms`). ``` -------------------------------- ### seneca.translate - Pattern Translation Source: https://context7.com/senecajs/seneca/llms.txt Enables pattern translation, allowing one message pattern to be mapped to another. This is useful for creating aliases for actions, migrating between different action patterns, or routing messages to different implementations. It supports basic pattern matching and property mapping. ```javascript const Seneca = require('seneca') const seneca = Seneca() // Original action .add('role:math,cmd:add', function(msg, done) { done(null, {result: msg.a + msg.b}) }) // Translate legacy pattern to new pattern .translate('cmd:sum', 'role:math,cmd:add') // Translate with property mapping .translate('cmd:plus,left:*,right:*', 'role:math,cmd:add', 'a,b') seneca.ready(function() { // All these call the same action this.act('role:math,cmd:add,a:2,b:3', (err, r) => console.log(r.result)) // 5 this.act('cmd:sum,a:2,b:3', (err, r) => console.log(r.result)) // 5 this.act('cmd:plus,left:2,right:3', (err, r) => console.log(r.result)) // 5 }) ```