{"id":22558237,"url":"https://github.com/fliphub/flipchain","last_synced_at":"2026-01-07T02:48:30.246Z","repository":{"id":57238459,"uuid":"90695390","full_name":"fliphub/flipchain","owner":"fliphub","description":"NOTICE: replaced by https://github.com/fluents/chain-able","archived":false,"fork":false,"pushed_at":"2017-05-14T08:26:08.000Z","size":16,"stargazers_count":1,"open_issues_count":2,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-02-02T12:15:41.782Z","etag":null,"topics":["chain","chainable","fluent","map","set"],"latest_commit_sha":null,"homepage":"","language":null,"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/fliphub.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}},"created_at":"2017-05-09T02:50:34.000Z","updated_at":"2017-05-30T23:25:02.000Z","dependencies_parsed_at":"2022-08-26T15:11:55.865Z","dependency_job_id":null,"html_url":"https://github.com/fliphub/flipchain","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fliphub%2Fflipchain","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fliphub%2Fflipchain/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fliphub%2Fflipchain/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fliphub%2Fflipchain/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fliphub","download_url":"https://codeload.github.com/fliphub/flipchain/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246025801,"owners_count":20711574,"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":["chain","chainable","fluent","map","set"],"created_at":"2024-12-07T20:13:02.319Z","updated_at":"2026-01-07T02:48:30.221Z","avatar_url":"https://github.com/fliphub.png","language":null,"funding_links":[],"categories":[],"sub_categories":[],"readme":"# ⛓ flipchain\n\n[![NPM version][flipchain-npm-image]][flipchain-npm-url]\n[![MIT License][license-image]][license-url]\n[![fliphub][gitter-badge]][gitter-url]\n[![flipfam][flipfam-image]][flipfam-url]\n[![fluent](https://img.shields.io/badge/⛓-fluent-9659F7.svg)](https://www.npmjs.com/package/flipchain)\n\n[flipchain-npm-image]: https://img.shields.io/npm/v/flipchain.svg\n[flipchain-npm-url]: https://npmjs.org/package/flipchain\n[license-image]: http://img.shields.io/badge/license-MIT-blue.svg?style=flat\n[license-url]: https://spdx.org/licenses/MIT\n[gitter-badge]: https://img.shields.io/gitter/room/fliphub/pink.svg\n[gitter-url]: https://gitter.im/fliphub/Lobby\n[flipfam-image]: https://img.shields.io/badge/%F0%9F%8F%97%20%F0%9F%92%A0-flipfam-9659F7.svg\n[flipfam-url]: https://www.npmjs.com/package/flipfam\n\n\n\u003e core chaining library, heavily based on  [webpack-chain](https://github.com/mozilla-rpweb/webpack-chain), but not webpack-specific.\n\n\n# 🏰 benefits\n\nwriting an api using flipchain means writing a single fluent api, but getting 3 apis as a result!\n- 🍉 rehydratable configurations\n- ⛓ fluent chainable api\n- 🍦 object configs that are easily merged deep\n\n\n# 🌊 what?\n![fluent](https://upload.wikimedia.org/wikipedia/commons/thumb/e/ef/Stone_skimming_-Patagonia-9Mar2010.jpg/1920px-Stone_skimming_-Patagonia-9Mar2010.jpg)\n\nimagine there are 2 people by the water, \u0026 the goal is to make 10 splashes.\n\nas with most skills, you get better at skipping rocks the more that you do it.\na lot of people cannot skip rocks, or they do not like to skip rocks, but they can still throw rocks and make a splash. think of this like non-fluent/vanilla-calls\n```js\nChain.prop()\nChain.longer()\nChain.intoSomeShapes()\n```\n\nor throwing a really huge rock into the water, and getting the splashes to make more splashes.\n```js\nChain.from({\n  prop: null,\n  longer: null,\n  intoSomeShapes: null,\n})\n```\n\n\n![pebbles](http://www.joulesevans.com/wp-content/uploads/2012/01/pebbles-mentor-page.jpg)\nusing method chaining looks similar to skipping rocks\n```js\nChain\n  .prop()\n  .longer()\n  .intoSomeShapes()\n```\n\nwriting an application with a fluent interface allows people to use it all three ways, and you only have to write it one way.\n\n\n# 📘 examples\n\n## 👋 intro\n\n```js\nconst ChainedMap = require('./ChainedMapExtendable')\n\nclass EasyFluent extends ChainedMap {\n  constructor(parent) {\n    super(parent)\n\n    // extend a list of strings for easy chainable methods\n    this.extend(['eh'])\n\n    // same as .extend,\n    // but when called with no arguments,\n    // default is used (`true` in this case)\n    // third param is optionally a prefix for inversified\n    // for example, `no` =\u003e `noCanada()` for inverse value\n    this.extendPrefixed(['canada'], true, 'no')\n  }\n\n  // if more advanced data changes are needed\n  // or if the syntax is preferred for use with typescript or flowtype\n  // .set, .get, .has are available\n  igloo(igloo) {\n    this.set('igloo', igloo)\n    return this\n  }\n\n  toConfig() {\n    return this.entries()\n  }\n}\n\n// {igloo: 'fire', canada: false, eh: 'moose'}\nconst config = new EasyFluent()\n  .igloo('fire')\n  .noCanada()\n  .eh('moose')\n  .toConfig()\n\n// this is == config\nconst hydrated = new EasyFluent()\n  .from(config)\n  .toConfig()\n\n// canada is now true\nconst merged = new EasyFluent()\n  .merge(config)\n  .merge({canada: true})\n  .toConfig()\n```\n\n\n## 🕳🏊 advanced\n\n```js\nconst ChainedMap = require('./ChainedMapExtendable')\nconst ChainedSet = require('./ChainedSet')\n\nclass Advanced extends ChainedMap {\n  static init(parent) {\n    return new Advanced(parent)\n  }\n  constructor(parent) {\n    super(parent)\n    this.list = new ChainedSet(this)\n    this.extend(['eh'])\n    this.extendWith(['canada'], true)\n  }\n\n  addName(name) {\n    this.list.add(name)\n    return this\n  }\n\n  igloo(igloo) {\n    this.set('igloo', igloo)\n    return this\n  }\n\n  toConfig() {\n    return Object.assign(this.entries(), {\n      list: this.list.values().map(name =\u003e name),\n    })\n  }\n\n  // since we have additional data that is not simple key value\n  // we do additional (albeit easy) steps to rehydrate\n  from(obj) {\n    super.from(obj)\n\n    Object\n      .keys(obj)\n      .forEach(key =\u003e {\n        const val = obj[key]\n        switch (key) {\n          case 'list': return val\n            .filter(name =\u003e name)\n            .forEach(name =\u003e this.addName(name))\n        }\n      })\n\n    return this\n  }\n\n  // same with `from`\n  // we do additional simple steps to merge in lists\n  merge(obj) {\n    Object\n      .keys(obj)\n      .filter(key =\u003e obj[key])\n      .forEach(key =\u003e {\n        const val = obj[key]\n        switch (key) {\n          case 'list': return val\n            .filter(name =\u003e name)\n            .forEach(v =\u003e this.addName(v))\n        }\n      })\n\n    // built-in merging\n    // can use `.mergeReal` to merge only `real` values\n    // and `.merge` to merge any\n    super.merge(obj)\n\n    return this\n  }\n}\n\nconst chain = Advanced\n  .init()\n  .igloo('brr')\n  .canada()\n  .eh('eh!')\n  .addName('thing one')\n  .addName('thing two')\n\n// true, `eh!`\nchain.has('igloo')\nchain.get('eh')\n\nconst result = chain.toConfig()\n\nconst hydrated = Advanced\n  .init()\n  .from(result)\n  .toConfig()\n\nconst merged = Advanced\n  .init()\n  .merge(hydrated)\n  .merge({igloo: 'whaaaat'})\n\n// can use toConfig,\n// and safely continue editing `merged`\n// with a snapshot of the object data saved as `mergedResult`\nconst mergedResult = merged.toConfig()\n\n// hydrated === result === {\n//   igloo: 'brr',\n//   canada: 'canada',\n//   eh: 'eh!',\n//   list: [ 'thing one', 'thing two' ]\n// }\n\n// merged === {\n//   igloo: 'whaaaat',\n//   canada: 'canada',\n//   eh: 'eh!',\n//   list: [ 'thing one', 'thing two' ]\n// }\n```\n\n# 🌊 types\n- there are [jsdoc blocks](http://usejsdoc.org/index.html) for all methods\n\n# 🌐 api\n- every chain has `.className` for easy debugging\n- every chain has this.parent\n- and this.parent is hidden on inspection by [🕵🗜 inspector-gadget](https://www.npmjs.com/package/inspector-gadget) for easier debugging\n\n### ChainedSet\n- [Set](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Set)\n- prepend =\u003e this\n- clear =\u003e this\n- delete =\u003e this\n- values =\u003e array of entries\n- has =\u003e boolean\n- merge =\u003e merge an object into the chain\n- when =\u003e conditional instance callback\n\n### ChainedMap\n- [Map](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Map)\n\n#### extendAlias\n- `(methodsToAlias: Array\u003cstring\u003e, methodToAlias: string, [thisArg])`\n- alias a list of methods\n- @returns `this`\n\n### from\n- `(obj: Object)`\n- checks each property of the object\n- calls the chains accordingly\n- rehydrates a chain from an object\n\n\n### other\n- decorateParent (using [childparent](https://www.npmjs.com/package/childparent))\n- clear() =\u003e this // clearsAll\n- delete(key) =\u003e this\n- entries =\u003e {keysAndValues}\n- values =\u003e Object.values\n- get(key) =\u003e entry\n- has(key) =\u003e boolean\n- set(key, val) =\u003e this\n\n##### if key is an array, merge in the value,\n##### usually should use ChainedSet for this\n- concat(key, val) =\u003e this\n- append(key, val) =\u003e this\n\n##### merging\n- mergeReal(obj) =\u003e this // only merges non-undefined values\n- merge(obj) =\u003e this\n- clean =\u003e this\n- when =\u003e conditional instance callback\n\n\n\n\n# 🔗 links \u0026 more\n- [Martin Fowler on FluentInterface](https://www.martinfowler.com/bliki/FluentInterface.html)\n\n#  `chainMapTill`\nlets you chain until the required keys are set via chains, or if they are passed in, then it auto returns `parent`\n\n# `chainedMapExtendable`\n- has chains with `.extends` able to use `default` values when calling it\n- also can add `prefixes` (default `no`) so if you use `cache` default `true`, it can add `noCache` which does the inverse\n- set up for being chains of chains when you add a few decorating chains dynamically\n\n\n# 📝🌊 TODO\n\n```js\n  // using `izz` to validate types when setting\n  this.extendType(['str'], 'string')\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffliphub%2Fflipchain","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffliphub%2Fflipchain","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffliphub%2Fflipchain/lists"}