{"id":16531369,"url":"https://github.com/marcbachmann/metalman","last_synced_at":"2025-04-04T20:42:40.813Z","repository":{"id":49712917,"uuid":"69918733","full_name":"marcbachmann/metalman","owner":"marcbachmann","description":"Composes configurable methods which are based on middlewares.","archived":false,"fork":false,"pushed_at":"2023-12-15T14:32:59.000Z","size":213,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":3,"default_branch":"master","last_synced_at":"2024-10-24T15:49:25.839Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/marcbachmann.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-10-03T23:14:48.000Z","updated_at":"2023-03-23T18:02:19.000Z","dependencies_parsed_at":"2023-12-15T15:45:50.767Z","dependency_job_id":null,"html_url":"https://github.com/marcbachmann/metalman","commit_stats":{"total_commits":26,"total_committers":3,"mean_commits":8.666666666666666,"dds":0.1923076923076923,"last_synced_commit":"0e3147394d5a02a5451dd890d659f4d972e94b88"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcbachmann%2Fmetalman","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcbachmann%2Fmetalman/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcbachmann%2Fmetalman/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/marcbachmann%2Fmetalman/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/marcbachmann","download_url":"https://codeload.github.com/marcbachmann/metalman/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247242678,"owners_count":20907134,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-11T18:08:38.722Z","updated_at":"2025-04-04T20:42:40.793Z","avatar_url":"https://github.com/marcbachmann.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# metalman\n\n[![Greenkeeper badge](https://badges.greenkeeper.io/marcbachmann/metalman.svg)](https://greenkeeper.io/)\n\nA recursion-free middleware framework for composable methods with native promise support.\nMiddlewares are factory functions that should return a command handler function.\n\nMiddlewares receive the command config which can be used for initialization and to register command handlers if they like to.\n\nA parameter passed during command invocation runs through all the middlewares until a middleware stops execution using an error.\nReturning `undefined` in a middleware handler means it should continue with the execution of the next middleware without doing any changes on the input parameter.\n\n## Example method\n\n```js\nconst metalman = require('metalman')\n\n// Load middlewares\n// registers a `schema` handler, see the `schema` attribute in the example below\nconst schema = require('metalman-schema')\n\n// registers an `action` handler, see the `action` attribute in the example below\n// Basically it just proxies the `action` attribute of a command to the middleware factory\n//   e.g. `function action (command) { return command.action }`\nconst action = metalman.action\n\n// Pass the middlewares to the command factory\nconst command = metalman([schema, metalman.action])\n\nmodule.exports = {\n  doSomething: command({\n    async action (cmd) {\n      return somethingAsync(cmd)\n    }\n  }),\n  createUser: command({\n    schema: {\n      required: ['id', 'name'],\n      properties: {\n        id: {type: 'string', maxLength: 36},\n        name: {type: 'string'}\n      }\n    },\n\n    // This middleware framework only supports\n    // one input argument and an optional callback\n    // We like to keep it simple (and fast), so we can stream objects into those methods\n    action (cmd) {\n      // cmd.id is definitely a string\n      // cmd.name is also a string\n\n      // By returning a value, you can change what gets passed\n      // into the next middleware as input parameter.\n      // If you don't need to change the output parameter,\n      // and just go to the next middleware, you can return `undefined`\n      return {id: cmd.id, name: cmd.name}\n    }\n  })\n}\n\n// module.exports.createUser({id: 'some-id', name: 'foobar'}, console.log)\n```\n\n\n## Api\n\n### metalman([middlewares])\n\nInstatiates a new object where you can pass some middlewares.\n```js\nconst metalman = require('metalman')\nconst commands = metalman([metalman.action])\n```\n\nA middleware is a factory that should return a command handler. It receives the config object of a command that's passed using `instance(config)`.\n\n### metalman.action\n\nThe simplest middleware there is. It executes the provided function passed as `action` property on a command config.\n\n```js\nconst metalman = require('metalman')\nconst commands = metalman([metalman.action])\nconst someCommand = commands({action (param) { throw new Error(param) }})\n\n// `someCommand()` returns a callback and therefore `awaiting` it\n// will throw an error 'Hello' as we're just proxying it to the error instance\n// in the example.\nawait someCommand('Hello')\n```\n\n### instance(config)\n\nConstructs a new function using the provided config which gets passed to the middlewares that conditionally can register a middleware.\n\n The config parameter must be an object.\n\n### instance.object({...methods})\n\nA helper to create multiple methods and return an object\n\ne.g.\n```js\nconst metalman = require('metalman')\nconst commands = metalman([metalman.action])\n\n// The following declaration will basically return an object\nmodule.exports = commands.object({\n  ping: {\n    action (cmd) { return 'pong' }\n  },\n  someAsyncFunction: {\n    async action (cmd) { await 'something' }\n  }\n})\n\n// The returned methods are automatically promisified and callbackified\n// Just provide the optional callback as parameter, to execute it as callback\n// It will look like something similar like that:\nmodule.exports = {\n  ping (cmd, [callback]) { return 'pong' },\n  someAsyncFunction (cmd, [callback]) { await 'something' }\n}\n\ne.g. if you want to mix in your custom functions, just use a spread operator\n\n// The following declaration will basically return an object\nmodule.exports = {\n  someOtherMethod () { return 'Hello World' },\n  ...commands.object({\n    ping: {\n      action (cmd) { return 'pong' }\n    },\n    someAsyncFunction: {\n      async action (cmd) { await 'something' }\n    }\n  })\n}\n```\n\n### instance.define('name', config)\n\nJust another helper to declare some commands.\ne.g.\n```js\nmodule.exports = commands\n  .define('ping', {action () { return 'pong' }})\n  .define('someAsyncFunction', {async action () { await 'something' }})\n```\n\n## Example middleware\n\nThe action middleware exposed as `require('metalman').action`.\n\n```js\nfunction actionMiddleware (config) { return config.action }\n```\n\n```js\nmodule.exports = schemaValidation\n\n// The command config directly gets passed to the factory\nfunction schemaValidation (commandConfig) {\n  // In case the command doesn't need a middleware, just return a falsy value\n  if (!commandConfig.schema) return\n  const validator = require('ajv')(commandConfig.schema)\n\n  return function validate (command) {\n    // returning `undefined` continues the middleware execution\n    if (validator(command)) return\n    else throw new Error(JSON.stringify(validator.errors)\n  }\n}\n```\n\n## Example websocket server\n\nCheck out /examples/server.js and /examples/client.js\n\n```js\nconst schema = require('metalman-schema')\nconst action = require('metalman-action')\nconst Methodman = require('methodman')\nconst metalman = require('metalman')\nconst websocket = require('websocket-stream')\n\nconst command = metalman([schema, action])\nconst commands = {\n  echo: command({\n    schema: {type: 'string'},\n    action (str) {\n      return str\n    }\n  })\n}\n\nfunction onWebsocketStream (stream) {\n  const methodman = Methodman(stream)\n  methodman.commands(commands)\n}\n\nconst http = require('http')\nconst server = http.createServer(function (req, res) { res.end() })\nwebsocket.createServer({server}, onWebsocketStream)\n\nconst port = process.env.PORT || 0\nserver.listen(port, function (err) {\n  if (err) return console.error(err)\n  console.log('Listening on http://localhost:%s', server.address().port)\n})\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcbachmann%2Fmetalman","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmarcbachmann%2Fmetalman","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmarcbachmann%2Fmetalman/lists"}