{"id":17288,"library":"machine-as-action","title":"Machine as Action Controller","description":"machine-as-action is a Node.js utility designed to bridge the gap between 'machines' – a pattern for encapsulating reusable business logic – and standard HTTP/WebSocket request-response cycles. It allows developers to define a machine and then wrap it with `asAction` to automatically handle incoming request parameters as machine inputs and map machine exits to various HTTP response types, including JSON data, rendered views, or redirects. The current stable version is 10.3.1, though recent release notes show significant changes around version 7.x. While there isn't a strict, rapid release cadence apparent from the provided data, updates have been made. Its primary differentiator lies in its deep integration with the 'machine' pattern, providing a structured and convention-over-configuration approach to expose API endpoints, particularly useful in frameworks like Sails.js where machines are a core architectural component. It offers granular control over response status codes and view rendering based on the outcome of a machine's execution, aiming to reduce boilerplate in controller logic.","status":"active","version":"10.3.1","language":"javascript","source_language":"en","source_url":"ssh://git@github.com/treelinehq/machine-as-action","tags":["javascript","machine","action","actions2","controller","sails.js","sails","blueprint","request"],"install":[{"cmd":"npm install machine-as-action","lang":"bash","label":"npm"},{"cmd":"yarn add machine-as-action","lang":"bash","label":"yarn"},{"cmd":"pnpm add machine-as-action","lang":"bash","label":"pnpm"}],"dependencies":[],"imports":[{"note":"This package is primarily CommonJS. Direct ESM import might require bundler configuration or dynamic import.","wrong":"import asAction from 'machine-as-action';","symbol":"asAction","correct":"const asAction = require('machine-as-action');"}],"quickstart":{"code":"const asAction = require('machine-as-action');\n\n// Simulate Express/Sails req/res objects for demonstration\nconst mockReq = (query = {}, body = {}) => ({ query, body });\nconst mockRes = () => {\n  let _status = 200;\n  let _data = null;\n  let _view = null;\n  let _redirect = null;\n\n  return {\n    status: function(code) { _status = code; return this; },\n    json: function(data) { _data = data; console.log(`[Response JSON] Status: ${_status}, Data: ${JSON.stringify(_data)}`); },\n    send: function(data) { _data = data; console.log(`[Response Send] Status: ${_status}, Data: ${data}`); },\n    view: function(template, locals) { _view = { template, locals }; console.log(`[Response View] Status: ${_status}, Template: ${_view.template}, Locals: ${JSON.stringify(_view.locals)}`); },\n    redirect: function(url) { _redirect = url; console.log(`[Response Redirect] Status: ${_status}, URL: ${_redirect}`); },\n    serverError: function(err) { _status = 500; _data = { error: err.message }; console.error(`[Server Error] Status: ${_status}, Data: ${JSON.stringify(_data)}`); }\n  };\n};\n\n// 1. Define an inline machine\nconst greetMachine = {\n  inputs: {\n    name: { type: 'string', required: true, example: 'World' }\n  },\n  exits: {\n    success: { outputExample: 'Hello World!' },\n    error: { outputExample: 'Could not greet.' }\n  },\n  fn: function(inputs, exits) {\n    if (inputs.name) {\n      return exits.success(`Hello, ${inputs.name}!`);\n    }\n    return exits.error(new Error('Name input is missing.'));\n  }\n};\n\n// 2. Wrap the machine as an action\nconst greetAction = asAction(greetMachine);\n\n// 3. Simulate an HTTP request for greetAction\nconsole.log('--- Simulating a simple custom action ---');\ngreetAction(mockReq({ name: 'Registry User' }), mockRes());\n\n// 4. Define an action with a custom responseType (e.g., 'view')\nconst showProfilePageAction = asAction({\n  inputs: {\n    userId: { type: 'string', required: true, example: 'user-123' }\n  },\n  exits: {\n    success: {\n      responseType: 'view',\n      viewTemplatePath: 'profile/show',\n      outputExample: { username: 'Alice', id: 'user-123' }\n    }\n  },\n  fn: function(inputs, exits) {\n    // In a real app, this would fetch user data from a database\n    const userData = { username: `User ${inputs.userId}`, id: inputs.userId };\n    return exits.success(userData);\n  }\n});\n\n// 5. Simulate an HTTP request for the view action\nconsole.log('\\n--- Simulating an action rendering a view ---');\nshowProfilePageAction(mockReq({ userId: '456' }), mockRes());","lang":"javascript","description":"This quickstart demonstrates how to wrap an inline machine with `asAction` and simulate its execution using mock `req` and `res` objects. It covers basic JSON responses and advanced view rendering, illustrating how machine outputs are mapped to HTTP responses."},"warnings":[{"fix":"Ensure all machine definitions adhere strictly to the 'machine' specification, providing `inputs`, `exits`, and a `fn` property.","message":"As of v6.1.3, undocumented and experimental tolerance of 'loose functions' (functions not conforming to the machine spec) as machine definitions is no longer supported.","severity":"breaking","affected_versions":">=6.1.3"},{"fix":"Review the documentation for `responseType`, `statusCode`, and `viewTemplatePath` options within machine exits. Test existing custom response handling thoroughly after upgrading.","message":"Version 7.0.11 introduced significant changes to custom response types and error handling, potentially altering the default behavior or configuration for `responseType`, `statusCode`, and `viewTemplatePath` in machine exits. Existing custom response logic might require adjustments.","severity":"breaking","affected_versions":">=7.0.11"},{"fix":"Be aware of the `X-Stub` header for debugging. In production (`process.env.NODE_ENV==='production'`), a warning will be logged instead. If you want to suppress this header even in development, you may need to intercept or modify responses.","message":"When generating stub data and not in production mode, `machine-as-action` automatically attaches an `X-Stub` header to responses. This behavior was introduced in v6.1.0.","severity":"gotcha","affected_versions":">=6.1.0"},{"fix":"Always explicitly define the `responseType` and `statusCode` for non-success exits to ensure predictable HTTP responses, especially for error conditions that should return specific status codes (e.g., 400 Bad Request, 404 Not Found).","message":"For non-success exits that do not explicitly configure a `responseType` (e.g., 'view', 'redirect'), `machine-as-action` defaults to using a 500 status code.","severity":"gotcha","affected_versions":">=7.0.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Ensure `asAction` is called only once for each machine. Store the result of `asAction` and reuse it, rather than calling `asAction` multiple times with the same input.","cause":"Attempting to wrap a machine with `asAction` more than once, or passing an already-wrapped action to `asAction`.","error":"Error: E_DOUBLE_WRAP: This action has already been wrapped!"},{"fix":"Verify that the machine definition object has a `fn` property, and correctly structured `inputs` and `exits` objects as per the machine spec.","cause":"The object passed to `asAction` does not conform to the expected machine specification (missing `fn`, malformed `inputs`/`exits`, etc.).","error":"Error: E_INVALID_MACHINE: The provided machine definition is invalid."},{"fix":"If working in a pure ESM project, you might need to use dynamic `import('machine-as-action')` or configure your build system (e.g., Webpack, Rollup) to handle CommonJS modules within an ESM context. This package is primarily CJS.","cause":"Attempting to use `require('machine-as-action')` in an ECMAScript module (ESM) environment without proper transpilation or dynamic import.","error":"ReferenceError: require is not defined in ES module scope"},{"fix":"Double-check that the variable holding your machine definition is correctly imported or defined and is a valid object containing a `fn` function.","cause":"The object passed to `asAction` is `undefined` or `null`, or does not have the expected `fn` property, indicating an invalid or missing machine definition.","error":"TypeError: Cannot read properties of undefined (reading 'fn')"}],"ecosystem":"npm","meta_description":null}