{"id":21502763,"url":"https://github.com/fuzetsu/mergerino","last_synced_at":"2025-10-30T18:31:54.884Z","repository":{"id":34872757,"uuid":"186032189","full_name":"fuzetsu/mergerino","owner":"fuzetsu","description":"immutable merge util for state management","archived":false,"fork":false,"pushed_at":"2023-03-04T03:43:00.000Z","size":156,"stargazers_count":71,"open_issues_count":10,"forks_count":6,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-12-07T04:41:18.795Z","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":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fuzetsu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","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":"2019-05-10T17:55:48.000Z","updated_at":"2024-03-06T22:51:27.000Z","dependencies_parsed_at":"2024-06-18T16:58:42.328Z","dependency_job_id":"4284e127-8331-409e-9bbe-25f0b5393190","html_url":"https://github.com/fuzetsu/mergerino","commit_stats":{"total_commits":43,"total_committers":3,"mean_commits":"14.333333333333334","dds":"0.41860465116279066","last_synced_commit":"233102a5caf96995ed7ca9009ade6e8159392555"},"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuzetsu%2Fmergerino","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuzetsu%2Fmergerino/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuzetsu%2Fmergerino/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fuzetsu%2Fmergerino/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fuzetsu","download_url":"https://codeload.github.com/fuzetsu/mergerino/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":230520390,"owners_count":18238948,"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-11-23T18:17:38.707Z","updated_at":"2025-10-30T18:31:54.791Z","avatar_url":"https://github.com/fuzetsu.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Mergerino [![npm](https://img.shields.io/npm/v/mergerino.svg)](https://www.npmjs.com/package/mergerino) [![size](https://img.shields.io/bundlephobia/minzip/mergerino)](https://unpkg.com/mergerino@latest/dist/mergerino.min.js)\n\nAn immutable merge util for state management.\n\nMergerino works very well with the [meiosis](http://meiosis.js.org/) state management pattern and is offered as a [setup option](https://github.com/foxdonut/meiosis/tree/master/helpers/setup#mergerino-setup).\n\n[Click here to see available builds of mergerino on npm](https://unpkg.com/mergerino/dist/).\n\n## ESM installation\n\n```js\nimport merge from 'https://unpkg.com/mergerino?module'\n\nconst state = {\n  user: {\n    name: 'John',\n    weight: 180,\n    age: 34,\n    height: 177\n  },\n  other: {\n    many: true,\n    properties: true\n  }\n}\nconst newState = merge(state, {\n  user: {\n    name: 'Bob',\n    weight: undefined,\n    age: age =\u003e age / 2\n  },\n  other: () =\u003e ({ replaced: true })\n})\n\n/*\nresult = {\n  user: {\n    name: 'Bob',\n    age: 17,\n    height: 177\n  },\n  other: {\n    replaced: true\n  }\n}\n*/\n```\n\n[playground](https://flems.io/#0=N4IgZglgNgpgziAXAbVAOwIYFsZJAOgAsAXLKEAGhAGMB7NYmBvEAXwvW10QICsEqdBk2J4IWAA60ATsQAEOaQHMYcsNNpY5AchLEJcRAHojAVzQSA1kvx0sRxSukQ0tAPxZaAE1OxtAHTRAoTh5UIxGOQBeOWBAuTlTOBhpRFj4hLlMHDTtAClaQjRtCgyEgHcYCCUSNIBGAA4ABlK0TLkMFTSAZgAWVvbCKprieoB2MYz2DNpiIdT0tsysDDQATzTiaVMYAcyJDQkU4gh4Te2YKcDWYPpQrJhygGViCNUYxxgACnDGCkWEkkUmk4ksEtkYLkAEK0ABGJTKckq1VqiTQXhgkDQMC8ewSnUhHRU0QAfETVEY5AAmKZ7WbzNJfACUpLkX2AcmkMAkUAw1Bx5x2clYTOuoqCaBCtFg+CgtCUX20MmqLgwUBKcl+MHFUplcoV2gkEWoQy8Guxz1ejCZlBAyVg1BOdzwdUQTTYHBAELwtjgAho9EYzB4bAAuqwgA)\n\n- `state` is left intact\n- each part of `state` that your patch instruction touched will be shallow copied into `newState`\n- untouched properties retain the same references.\n\n## ES5 browser installation\n\n```html\n\u003cscript src=\"https://unpkg.com/mergerino\"\u003e\u003c/script\u003e\n\u003cscript\u003e\n  const state = {\n    user: {\n      name: 'John',\n      weight: 180,\n      age: 34,\n      height: 177\n    },\n    other: {\n      many: true,\n      properties: true\n    }\n  }\n  const newState = mergerino(state, {\n    user: {\n      name: 'Bob',\n      weight: undefined,\n      age: function (age) {\n        return age / 2\n      }\n    },\n    other: function () {\n      return { replaced: true }\n    }\n  })\n\n  /*\n  result = {\n    user: {\n      name: 'Bob',\n      age: 17,\n      height: 177\n    },\n    other: {\n      replaced: true\n    }\n  }\n  */\n\u003c/script\u003e\n```\n\n[playground](https://flems.io/#0=N4IgZglgNgpgziAXAbVAOwIYFsZJAOgAsAXLKEAGhAGMB7NYmBvAHjmoCcIAHYgAjgdqAXgA6IEsW5xEAelkBXNNwDWAc3x0ssnBzUwuaWuIB8LWey68TlEHBixqxCPQSIQABkQBWAIwgAXwp0bFx3fAArBCo6BiZiPFi4fmSMRj5hPmBRND4+BXsORCycvLzMHGKAcgApWkI0KopSsoB3GAg1EmLfAA4PZtyyjH1igGYAFkGyvkIOruIegHYllqCW2mI5opKhvKwMNABPYuIOBRhpsu4OWm4DZ3hT85g1nICcpP40GFaAZWIaRgGT4un0hloAApUowKLs8gUDMVsns+BUYNUAEK0ABGTRaeXanW6+TQABMYJAfmSrnkRhi+GAlE4XGhIfSAJTwmZ8DgwYgKDi5el8WR8ABMBL4HyG6yGm22xSZaBZ9EhXJRMz5AqFWV5MG4UAw1BgZOeF2lbzQAQ5OU+rlosHwUFoakhVVoXDUEEwUCaAkBjFtKodTpdbqq3DS1DmZP9P3+gZgwcCwRA6Lwmjg0Ro9EYzHcgQAugEgA)\n\n## Usage Guide\n\nMergerino is made up of a single function `merge(target, ...patches)`.\n\nPatches in mergerino are expressed as plain JavaScript objects:\n\n```js\nmerge(state, { I: { am: { just: { an: 'object' } } } })\n```\n\nMergerino merges immutably meaning that the `target` object will never be mutated (changed). Instead each object along the path your patch specifies will be shallow copied into a new object.\n\nThe advantage of this is that patch operations are relatively quick because they only copy the parts of the object that are touched.\n\nThis means you can use strict equality (`===`) to determine where an object was changed by a patch operation:\n\n```js\nconst state = { obj: {} }\nconst newState = merge(state, { newProp: true })\nconsole.log(state === newState) // false\nconsole.log(state.obj === newState.obj) // true\n```\n\nIf you want to fully remove a property from an object specify `undefined` as the value.\n\n```js\nconst state = { deleteMe: true }\nconst newState = merge(state, { deleteMe: undefined })\nconsole.log(state) // { deleteMe: true }\nconsole.log(newState) // {}\n```\n\nUse `null` instead of `undefined` if you don't want the key to be deleted.\n\nIf you want to replace a property based on its current value, use a function.\n\n```js\nconst state = { age: 10, obj: { foo: 'bar' } }\nconst newState = merge(state, { age: x =\u003e x * 2, obj: () =\u003e ({ replaced: true }) })\nconsole.log(state) // { age: 10, obj: { foo: 'bar' } }\nconsole.log(newState) // { age: 20, obj: { replaced: true } }\n```\n\nIf you pass a function it will receive the current value as the first argument and the merge function as the second. The return value will be the replacement. The value you return will bypass merging logic and simply overwrite the property. This is useful when you want to replace an object without merging. If you would like to merge from within a function patch then use the merge function provided as the second argument.\n\n## Multiple Patches\n\nYou can pass multiple patches in a single merge call, array arguments will be flattened before processing.\n\nAll the following are valid:\n\n```js\nmerge(state, [{}, {}, {}])\nmerge(state, {}, {}, {})\nmerge(state, [[[[{}]]]])\nmerge(state, [{}], [{}], [{}])\n```\n\nAnother nice side effect of flattening array arguments is that you can easily add conditions to your patches using nested arrays:\n\n```js\nmerge(state, [\n  { week: 56 },\n  state.age \u003c 10 \u0026\u0026 { child: true },\n  state.job === 'programmer' \u0026\u0026 [\n    state.promote \u0026\u0026 { promoted: true },\n    !state.salaryPaid \u0026\u0026 { schedulePayment: true }\n  ]\n])\n```\n\nIf all the above conditions are false (except the job check) the final patch array before flattening will look like this:\n\n```js\npatches === [{ week: 56 }, false, [false, false]]\n```\n\nSince falsy patches are ignored only `{ week: 56 }` will be merged.\n\nAnother option is to use the spread operator to combine multiple patches into one, but it's harder/messier to write conditions using this technique as you can see:\n\n```js\nmerge(state, {\n  { week: 56 },\n  ...(state.age \u003c 10 ? { child: true } : {}),\n  ...(state.job === 'programmer'\n    ? {\n        ...(state.promote ? { promoted: true } : {}),\n        ...(!state.salaryPaid ? { schedulePayment: true } : {})\n      }\n    : {})\n})\n```\n\n## As a reducer\n\nMergerino can be used as a reducer where patches are fed into a function which is then applied to a central state object. In these cases you may not have a reference to the full state object to base your patch on.\n\nIn order to help in this scenario mergerino supports passing a function as a top level patch. This function acts exactly the same as a function passed to a specific property. It receives the full state object as the first argument, the merge function as the second.\n\n```js\n// state-manager.js\nlet state = { count: 10 }\nconst update = patch =\u003e (state = merge(state, patch))\n\n// other.js\nupdate({ newProp: true })\n// want to use value of count to patch\nupdate((state, m) =\u003e m(state, { double: state.count * 2 }))\n\n// back in state-manager.js\nconsole.log(state) // { count: 10, newProp: true, double: 20 }\n\n// if you don't use the merge function the top level object will be replaced\nupdate(state =\u003e ({}))\nconsole.log(state) // {}\n```\n\n## Credits\n\nCheck out [patchinko](https://github.com/barneycarroll/patchinko) by [Barney Carroll](https://github.com/barneycarroll). It was a huge inspiration for `mergerino`.\n\nIt takes a much more explicit approach to signaling patch operations and has some interesting API options.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuzetsu%2Fmergerino","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffuzetsu%2Fmergerino","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffuzetsu%2Fmergerino/lists"}