{"id":18665455,"url":"https://github.com/cb1kenobi/gawk","last_synced_at":"2026-03-09T13:02:10.454Z","repository":{"id":49884125,"uuid":"53222133","full_name":"cb1kenobi/gawk","owner":"cb1kenobi","description":"Observable JavaScript object model","archived":false,"fork":false,"pushed_at":"2021-06-09T00:57:50.000Z","size":2367,"stargazers_count":18,"open_issues_count":1,"forks_count":3,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-10-06T22:53:48.089Z","etag":null,"topics":["javascript","json","model","nodejs","notify","object","observable","observe","watch"],"latest_commit_sha":null,"homepage":null,"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/cb1kenobi.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2016-03-05T20:37:33.000Z","updated_at":"2024-03-18T13:47:17.000Z","dependencies_parsed_at":"2022-09-01T04:31:20.690Z","dependency_job_id":null,"html_url":"https://github.com/cb1kenobi/gawk","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/cb1kenobi/gawk","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cb1kenobi%2Fgawk","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cb1kenobi%2Fgawk/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cb1kenobi%2Fgawk/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cb1kenobi%2Fgawk/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cb1kenobi","download_url":"https://codeload.github.com/cb1kenobi/gawk/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cb1kenobi%2Fgawk/sbom","scorecard":{"id":268394,"data":{"date":"2025-08-11","repo":{"name":"github.com/cb1kenobi/gawk","commit":"7e8e3c57693142dfd3caecb07542d7d1a0e622c3"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/22 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'master'"],"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 11 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"name":"Vulnerabilities","score":0,"reason":"12 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: GHSA-ww39-953v-wcq6","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-qrpm-p2h7-hrv2","Warn: Project is vulnerable to: GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: GHSA-9wv6-86v2-598j","Warn: Project is vulnerable to: GHSA-c2qf-rxjj-qqgw","Warn: Project is vulnerable to: GHSA-j8xg-fqg3-53r7"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-08-17T12:45:23.498Z","repository_id":49884125,"created_at":"2025-08-17T12:45:23.498Z","updated_at":"2025-08-17T12:45:23.498Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30297111,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T11:12:22.024Z","status":"ssl_error","status_checked_at":"2026-03-09T11:10:54.577Z","response_time":61,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"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":["javascript","json","model","nodejs","notify","object","observable","observe","watch"],"created_at":"2024-11-07T08:27:48.664Z","updated_at":"2026-03-09T13:02:10.419Z","avatar_url":"https://github.com/cb1kenobi.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# gawk\n\n[![NPM Version][npm-image]][npm-url]\n[![NPM Downloads][downloads-image]][downloads-url]\n[![Travis CI Build][travis-image]][travis-url]\n[![Appveyor CI Build][appveyor-image]][appveyor-url]\n[![Deps][david-image]][david-url]\n[![Dev Deps][david-dev-image]][david-dev-url]\n\nGawk wraps JavaScript objects and arrays using ES2015 proxies making them observable. Once a\nJavaScript object (or array) is gawked, you can listen for changes including deeply nested changes.\n\nOnly objects and arrays can be gawked. All other types are passed through.\n\nGawked objects and arrays can be interacted with as if they were regular objects/arrays. Built-in\nfunctions such as `JSON.stringify()` work as expected.\n\n\u003e Note: gawk uses ES2015 proxies and thus requires Node.js 6 or newer.\n\n## Installation\n\n    npm i --save gawk\n\n## Examples\n\n```js\nimport gawk from 'gawk';\n\nconst obj = gawk({\n    foo: 'bar'\n});\n\ngawk.watch(obj, (obj, src) =\u003e {\n    console.info('object changed!');\n    console.info('new value =', obj);\n});\n\nobj.foo = 'baz';\n\nconsole.info(obj); // { foo: 'baz' }\n```\n\nYou can also be notified if a deep child is changed:\n\n```js\nconst obj = gawk({\n    foo: {\n        bar: ['a', 'b']\n    }\n});\n\ngawk.watch(obj, (obj, src) =\u003e {\n    console.info('object changed!');\n    console.info('new value =', obj);\n});\n\nobj.foo.bar.push('c', 'd');\n\nconsole.info(obj); // { foo: { bar: ['a', 'b', 'c', 'd'] } }\n```\n\nTo filter watch notifications, simply pass in the property name or array of\nproperty names used to filter the gawk object.\n\n```js\nconst obj = gawk({\n\tfoo: {\n\t\tbar: 'hello'\n\t}\n});\n\ngawk.watch(obj, [ 'foo', 'bar' ], value =\u003e {\n\tconsole.info(`bar changed to ${value}`);\n});\n\nobj.foo.bar = 'world!';\n```\n\nTo stop watching, simply call `gawk.unwatch()` with the original listener function.\n\n```js\nconst obj = gawk({ /* ... */ });\n\nfunction onChange(obj, src) {\n\tconsole.log('changed!');\n}\n\ngawk.watch(obj, onChange);\n\nobj.foo = 'bar';\n\ngawk.unwatch(obj, onChange);\n\nobj.foo = 'baz'; // does not fire onChange()\n```\n\nTo update an object and preserve the listeners, you can use the `gawk.set()` function.\n\n```js\nconst obj = gawk({\n\tfoo: 'bar'\n});\n\ngawk.watch(obj, () =\u003e {\n\tconsole.log('changed!');\n});\n\ngawk.set(obj, { baz: 'wiz' });\n\nconsole.log(obj); // { baz: 'wiz' }\n\nobj.baz = 'wow';\n\nconsole.log(obj); // { baz: 'wow' }\n```\n\n## API\n\n### `gawk(obj)`\n\nGawks the specified object.\n\n * `obj` - (Object) The object to gawk.\n\nIf `obj` is not an object, is `null`, or is a built-in object such as `JSON` or `Math`, then the\noriginal value is returned, otherwise it returns the gawked object.\n\nNote that the returned object is a proxy-wrapped version of the original input object. You can\ninteract with the object as you normally would. Some Array methods are wrapped to suppress multiple\nchange notifications for a single call or to workaround limitations of the proxied objects.\n\n### `isGawked(obj)`\n\nDetermines if an variable is a gawked object.\n\n * `obj` - (Object) The object to check if gawked.\n\nReturns `true` if the specified object has been gawked, otherwise `false`\n\n### `gawk.set(dest, src)`\n\n### `gawk.set(dest, src, compareFn)`\n\nReplaces the entire definition of a gawked object with another object or array while preserving any\nlisteners and dispatches change nofications afterwards.\n\n * `dest` - (Object|Array) The destination object. It will be automatically gawked if not already.\n * `src` - (Object|Array) The source object or array to copy from.\n * `compareFn` - (Function) An optional function to call when comparing elements of an array.\n\nReturns the original `dest` object.\n\n### `gawk.watch(subject, listener)`\n\n### `gawk.watch(subject, filter, listener)`\n\nStarts watching the specified gawked object for changes.\n\n * `subject` - (Object) The gawked object to watch.\n * `filter` - (Array[String] | String) [optional] A list of one or more nested namespaces to filter.\n   When a filter is not specified, then it watches the entire object for changes.\n * `listener` - (Function) The function to call when a change occurs.\n\nReturns the original `subject` value.\n\nThe filter is works by matching only a specific namespace pattern. For example, say you have a\ngawked object `{ foo: { bar: { baz: 'wiz' } } }` and filtering by `[ 'foo', 'bar' ]`. When you set\n`foo.bar.baz = 'pow'`, the listener function will be called with the value `{ baz: 'pow' }`. When\nyou set `foo.bar.raf = 'muk'`, this change will _not_ be emitted.\n\n### `gawk.unwatch(obj)`\n\n### `gawk.unwatch(obj, fn)`\n\nStops watching a gawked object.\n\n* `subject` - (Object) The gawked object to watch.\n* `listener` - (Function) [optional] The function to call when a change occurs. When a `listener` is\n  not specified, all listeners are removed from the gawked object.\n\nReturns the original `subject` value.\n\n### `gawk.merge(obj, ...objs)`\n\nPerforms a shallow merge of one or more objects into the specified gawked object.\n\n * `obj` - (Object) The gawked object to merge values into.\n * `...objs` - (Object) One or more objects to merge in.\n\nReturns the original gawked object `obj`.\n\nNote that subsequent object properties will overwrite existing values. Only a single change event is\nemitted after all objects have been merged.\n\n### `gawk.mergeDeep(obj, ...objs)`\n\nPerforms a deep merge of one or more objects into the specified gawked object.\n\n* `obj` - (Object) The gawked object to merge values into.\n* `...objs` - (Object) One or more objects to merge in.\n\nReturns the original gawked object `obj`.\n\nJust as `gawk.merge()`, subsequent object properties will overwrite existing values. Only a single\nchange event is emitted after all objects have been merged.\n\nIf the destination is an array, the two arrays are concatenated.\n\n## Performance\n\nEach gawked object is wrapped in a `Proxy` and contains a object with the gawked object state:\nlisteners, parents, previous values, and notification queue. These data structures are\ncreated on an as needed basis. A gawked object with no listeners uses an insignificant amount of\nmemory. However, if you have an object with many levels of deeply nested objects, it could add up\nquickly.\n\nAs soon as listeners are added via `gawk.watch()`, it will create the `Map` of listeners. If you add\na filter, then a `WeakMap` of previous values gets created. Each gawked object also tracks its\nparents using a `Set`. Again, if filters are not used or the object is not nested (i.e. has no\nparent), then there's not a significant amount of memory used.\n\nFor runtime performance, the more listeners, the slower the notification system is. This is\nespecially true if the object being modified is deeply nested. Notifications are propagated through\neach parent and each parent's parent and so on.\n\nThere aren't any official benchmarks. It's up to you to decide if Gawk is right for your app.\nExercise common sense. Don't gawk objects with several dozens of deeply nested objects. Don't add\nthousands of listeners.\n\n## Upgrading to v4\n\nGawk v4 has dropped all gawk data types. You must always call `gawk()`.\n\nChange all `new GawkObject()` calls to `gawk({})` and `new GawkArray()` to `gawk([])`.\n\nSince Gawk v3 and newer uses ES6 Proxies, you no longer need to call `obj.get()`, `obj.set()`,\n`obj.delete()`, etc.\n\nMethods `obj.watch()`, `obj.merge()`, and `obj.mergeDeep()` have moved to `gawk.watch()`,\n`gawk.merge()`, and `gawk.mergeDeep()`. The first argument must be a gawk object.\n\nStarting in v3, Gawk no longer hashes values. This means speed. Gawk v3+ is about 19 times faster\nthan v1 and v2.\n\n## License\n\nMIT\n\n[npm-image]: https://img.shields.io/npm/v/gawk.svg\n[npm-url]: https://npmjs.org/package/gawk\n[downloads-image]: https://img.shields.io/npm/dm/gawk.svg\n[downloads-url]: https://npmjs.org/package/gawk\n[travis-image]: https://travis-ci.org/cb1kenobi/gawk.svg?branch=master\n[travis-url]: https://travis-ci.org/cb1kenobi/gawk\n[appveyor-image]: https://ci.appveyor.com/api/projects/status/1ee7r1drlswy5jk6?svg=true\n[appveyor-url]: https://ci.appveyor.com/project/cb1kenobi/gawk\n[david-image]: https://img.shields.io/david/cb1kenobi/gawk.svg\n[david-url]: https://david-dm.org/cb1kenobi/gawk\n[david-dev-image]: https://img.shields.io/david/dev/cb1kenobi/gawk.svg\n[david-dev-url]: https://david-dm.org/cb1kenobi/gawk#info=devDependencies\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcb1kenobi%2Fgawk","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcb1kenobi%2Fgawk","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcb1kenobi%2Fgawk/lists"}