{"id":21669324,"url":"https://github.com/attack-monkey/lean-state","last_synced_at":"2026-05-20T02:32:53.908Z","repository":{"id":91874062,"uuid":"300403075","full_name":"attack-monkey/lean-state","owner":"attack-monkey","description":"A State Manager built for Lean Functional Typescript","archived":false,"fork":false,"pushed_at":"2020-10-24T20:39:13.000Z","size":15,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2024-04-27T00:40:49.872Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"TypeScript","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/attack-monkey.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":"2020-10-01T19:39:47.000Z","updated_at":"2020-10-24T20:39:15.000Z","dependencies_parsed_at":null,"dependency_job_id":"bdb20aa9-84e5-44f0-ac8d-54a8dc03e36d","html_url":"https://github.com/attack-monkey/lean-state","commit_stats":{"total_commits":7,"total_committers":1,"mean_commits":7.0,"dds":0.0,"last_synced_commit":"a531bbb3e88eafc3d6f185f55329c81abb146ba7"},"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/attack-monkey%2Flean-state","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/attack-monkey%2Flean-state/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/attack-monkey%2Flean-state/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/attack-monkey%2Flean-state/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/attack-monkey","download_url":"https://codeload.github.com/attack-monkey/lean-state/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":244570068,"owners_count":20473974,"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-25T12:20:49.572Z","updated_at":"2026-05-20T02:32:53.903Z","avatar_url":"https://github.com/attack-monkey.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# lean-state 2.0\nA State Manager built for Lean Functional Typescript\n\n\u003e Checkout: https://github.com/attack-monkey/Lean-Functional-Typescript\n\n# Changes from 1.x\n\n- Syntax changes to add more context to queries\n\n# Install\n\n```\n\nnpm i lean-state\n\n```\n\n# Use\n\n```\n\nimport { register } from 'lean-state'\n\n```\n\n# Basics\n\n```typescript\n\nimport { register } from 'lean-state'\n\ntype Global = {\n  greeting: string\n}\n\ntype Listeners =\n  | 'listener1'\n  | 'listener2'\n  | 'listener3'\n\n// register\nconst global = register\u003cGlobal, Listeners\u003e()\n\n// set\nglobal.set('greeting').with('hello world')\n\n// get\nconsole.log(\n  global.get().greeting\n)\n\n// get the current and pass to function\nglobal.once(\n  ({ greeting }) =\u003e console.log(greeting)\n)\n\n// set up a listener...\nglobal\n  .listenOn('listener1')\n  .for(['greeting'])\n  .subscribe(({ greeting }) =\u003e console.log('listener1: ' + greeting))\n\n// make changes and the listener will react\nglobal.set('greeting').with('howdy y\\'all')\nglobal.set('greeting').with('wassup')\nglobal.set('greeting').with('g\\'day mate')\n\n// set up a listener that auto tears down when a condition is no longer met\nglobal\n  .listenOn('listener2')\n  .for(['greeting'])\n  .while(({ greeting }) =\u003e greeting !== 'stop listening')\n  .subscribe(({ greeting }) =\u003e console.log('listener2: ' + greeting))\n\n// make changes and both listeners will react\nglobal.set('greeting').with('good day')\n\n// this next change will trigger listener2 to tear down...\nglobal.set('greeting').with('stop listening')\n\n// listeners can also be set up to listen, starting from the next change...\nglobal\n  .listenOn('listener3')\n  .fromNext(['greeting'])\n  .while(({ greeting }) =\u003e greeting !== 'stop listening')\n  .subscribe(({ greeting }) =\u003e console.log('listener3: ' + greeting))\n\n// And more changes...\nglobal.set('greeting').with('heeeey!!!')\nglobal.set('greeting').with('stop listening')\n\n```\n\n# A complex example\n\nLean-state is a key-value store, so while complex data can be stored at a given key, \nlisteners can only listen to changes at that top-level key.\n\nThis isn't really a problem at all - but does change the way you think about listening to changes in data.\n\nIf for example a key stores a Record of items - but your app is focused on changes only at a given item - then it's a good idea to\nalso capture which id is being focused on and which id is being changed.\n\n```typescript\n\nimport { register } from 'lean-state'\n\ntype Car = {\n  make: string\n  color: string\n}\n\ntype Global = {\n  cars: Record\u003cstring, Car\u003e\n  lastUpdatedCar: string,\n  focusOnCar: string\n}\n\ntype Listeners = \n  | 'listener1'\n  | 'listener2'\n\nconst global = register\u003cGlobal, Listeners\u003e()\n\nconst listenToAllCarChanges = () =\u003e {\n  global\n    .listenOn('listener1')\n    .fromNext(['lastUpdatedCar'])\n    .subscribe(({ cars, lastUpdatedCar }) =\u003e\n      console.log(`change to car ${lastUpdatedCar} =\u003e ${JSON.stringify(cars[lastUpdatedCar], null, 2)}`)\n    )\n}\n\nconst listenToNewFocusCar = (id: string) =\u003e {\n  // Set up the car to focus on\n  global.set('focusOnCar').with(id)\n  global\n    .listenOn('listener2')\n    // listen to the next change to lastUpdatedCar\n    .fromNext(['lastUpdatedCar'])\n    .subscribe(({ cars, focusOnCar, lastUpdatedCar }) =\u003e {\n      if(lastUpdatedCar === focusOnCar) {\n        console.log(`Focussed car is ${focusOnCar} and it changed to =\u003e ${JSON.stringify(cars[focusOnCar], null, 2)}`)\n      }\n    })\n}\n\nconst setInitialData = () =\u003e {\n  global.set('cars').with({\n    '1': { make: 'Toyota', color: 'red' },\n    '2': { make: 'Toyota', color: 'blue'}\n  })\n  global.set('focusOnCar').with('1')\n}\n\nconst updateCar = (id: string) =\u003e ({\n  with: (car: Car) =\u003e {\n    global.set('cars').at(id).with(car)\n    global.set('lastUpdatedCar').with(id)\n  }\n})\n\n// Set up data\nsetInitialData()\n\n// Set up listeners\nlistenToAllCarChanges()\nlistenToNewFocusCar('1')\n\n// Make updates\nupdateCar('3').with({ make: 'Corvette', color: 'red' })\nupdateCar('1').with({ make: 'Ferrari', color: 'pink' })\n\n// Change focus car, which re-creates the listener\nlistenToNewFocusCar('2')\n\n// Make more changes\nupdateCar('1').with({ make: 'Ferrari', color: 'red' })\nupdateCar('2').with({ make: 'Lamborgini', color: 'grey' })\n\n```","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fattack-monkey%2Flean-state","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fattack-monkey%2Flean-state","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fattack-monkey%2Flean-state/lists"}