{"id":21310946,"url":"https://github.com/webqit/observer","last_synced_at":"2026-03-09T06:31:32.385Z","repository":{"id":41934213,"uuid":"249564230","full_name":"webqit/observer","owner":"webqit","description":"Mutation-Based Reactivity for JavaScript","archived":false,"fork":false,"pushed_at":"2026-01-11T07:28:13.000Z","size":1355,"stargazers_count":23,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2026-01-11T13:28:42.545Z","etag":null,"topics":["array-observe","events","interception","object-observe","observer","reflection"],"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/webqit.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","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,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null},"funding":{"github":"ox-harris","patreon":null,"open_collective":"webqit","ko_fi":null,"tidelift":null,"community_bridge":null,"liberapay":null,"issuehunt":null,"otechie":null,"lfx_crowdfunding":null,"custom":null}},"created_at":"2020-03-23T23:09:42.000Z","updated_at":"2026-01-11T07:28:13.000Z","dependencies_parsed_at":"2024-01-23T18:53:40.312Z","dependency_job_id":"baed05b5-1292-44ad-bd50-c746ccb28c0f","html_url":"https://github.com/webqit/observer","commit_stats":{"total_commits":213,"total_committers":4,"mean_commits":53.25,"dds":0.4976525821596244,"last_synced_commit":"ce803a9d37ed4a28351fe130be05f08de6572740"},"previous_names":["web-native/observer","web-native/reflex"],"tags_count":90,"template":false,"template_full_name":null,"purl":"pkg:github/webqit/observer","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webqit%2Fobserver","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webqit%2Fobserver/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webqit%2Fobserver/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webqit%2Fobserver/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/webqit","download_url":"https://codeload.github.com/webqit/observer/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/webqit%2Fobserver/sbom","scorecard":{"id":1240262,"data":{"date":"2025-12-01","repo":{"name":"github.com/webqit/observer","commit":"50fc1cebba5fcdcb649420d2474f3d5353b76119"},"scorecard":{"version":"v5.4.1-0.20251125161513-488797dbe611","commit":"488797dbe611ab7e16f12d88185cde5e8dc28aca"},"score":3.5,"checks":[{"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/488797dbe611ab7e16f12d88185cde5e8dc28aca/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 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/488797dbe611ab7e16f12d88185cde5e8dc28aca/docs/checks.md#code-review"}},{"name":"Maintained","score":10,"reason":"25 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 10","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/488797dbe611ab7e16f12d88185cde5e8dc28aca/docs/checks.md#maintained"}},{"name":"SAST","score":0,"reason":"no SAST tool detected","details":["Warn: no pull requests merged into dev branch"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/488797dbe611ab7e16f12d88185cde5e8dc28aca/docs/checks.md#sast"}},{"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/488797dbe611ab7e16f12d88185cde5e8dc28aca/docs/checks.md#token-permissions"}},{"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/488797dbe611ab7e16f12d88185cde5e8dc28aca/docs/checks.md#packaging"}},{"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/488797dbe611ab7e16f12d88185cde5e8dc28aca/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/488797dbe611ab7e16f12d88185cde5e8dc28aca/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/488797dbe611ab7e16f12d88185cde5e8dc28aca/docs/checks.md#cii-best-practices"}},{"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/488797dbe611ab7e16f12d88185cde5e8dc28aca/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/488797dbe611ab7e16f12d88185cde5e8dc28aca/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/488797dbe611ab7e16f12d88185cde5e8dc28aca/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: some github tokens can't read classic branch protection rules: https://github.com/ossf/scorecard-action/blob/main/docs/authentication/fine-grained-auth-token.md","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/488797dbe611ab7e16f12d88185cde5e8dc28aca/docs/checks.md#branch-protection"}},{"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/488797dbe611ab7e16f12d88185cde5e8dc28aca/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":0,"reason":"11 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: https://osv.dev/GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: https://osv.dev/GHSA-grv7-fg5c-xmjg","Warn: Project is vulnerable to: https://osv.dev/GHSA-67mh-4wv8-2f99","Warn: Project is vulnerable to: https://osv.dev/GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: https://osv.dev/GHSA-4q6p-r6v2-jvc5","Warn: Project is vulnerable to: https://osv.dev/GHSA-mh29-5h37-fv8m","Warn: Project is vulnerable to: https://osv.dev/GHSA-mwcw-c2x4-8c55","Warn: Project is vulnerable to: https://osv.dev/GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: https://osv.dev/GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: https://osv.dev/GHSA-76p7-773f-r4q5","Warn: Project is vulnerable to: https://osv.dev/GHSA-72xf-g2v4-qvf3"],"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/488797dbe611ab7e16f12d88185cde5e8dc28aca/docs/checks.md#vulnerabilities"}}]},"last_synced_at":"2025-12-07T12:19:48.514Z","repository_id":41934213,"created_at":"2025-12-07T12:19:48.514Z","updated_at":"2025-12-07T12:19:48.514Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30284774,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-09T02:57:19.223Z","status":"ssl_error","status_checked_at":"2026-03-09T02:56:26.373Z","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":["array-observe","events","interception","object-observe","observer","reflection"],"created_at":"2024-11-21T17:15:16.465Z","updated_at":"2026-03-09T06:31:32.368Z","avatar_url":"https://github.com/webqit.png","language":"JavaScript","funding_links":["https://github.com/sponsors/ox-harris","https://opencollective.com/webqit"],"categories":[],"sub_categories":[],"readme":"# The Observer API\n\n\u003c!-- BADGES/ --\u003e\n\n\u003cspan class=\"badge-npmversion\"\u003e\u003ca href=\"https://npmjs.org/package/@webqit/observer\" title=\"View this project on NPM\"\u003e\u003cimg src=\"https://img.shields.io/npm/v/@webqit/observer.svg\" alt=\"NPM version\" /\u003e\u003c/a\u003e\u003c/span\u003e \u003cspan class=\"badge-npmdownloads\"\u003e\u003ca href=\"https://npmjs.org/package/@webqit/observer\" title=\"View this project on NPM\"\u003e\u003cimg src=\"https://img.shields.io/npm/dm/@webqit/observer.svg\" alt=\"NPM downloads\" /\u003e\u003c/a\u003e\u003c/span\u003e\n\n\u003c!-- /BADGES --\u003e\n\n**[Motivation](#motivation) • [Overview](#an-overview) • [Documentation](#documentation) • [Polyfill](#the-polyfill) • [Getting Involved](#getting-involved) • [License](#license)**\n\nObserve and intercept operations on arbitrary JavaScript objects and arrays using a utility-first, general-purpose reactivity API! This API re-explores the unique design of the [`Object.observe()`](https://web.dev/es7-observe/) API and takes a stab at what could be **a unifying API** over *related but disparate* things like `Object.observe()`, [Reflect](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect) APIs, and the \"traps\" API (proxy traps)!\n\nObserver API is an upcoming proposal!\n\n## Motivation\n\nTracking mutations on JavaScript objects has historically relied on \"object wrapping\" techniques with [ES6 Proxies](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy), and on \"property mangling\" techniques with [getters and setters](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty). Besides how the first poses an *object identity* problem and the second, an *interoperability* problem, there is also much inflexibility in the programming model that each enables!\n\nThis is discussed extensively in [the introductory blog post](https://dev.to/oxharris/re-exploring-reactivity-and-introducing-the-observer-api-and-reflex-functions-4h70)\n\nWe find a design precedent to object observability in the [`Object.observe()`](https://web.dev/es7-observe/) API, which at one time checked all the boxes and touched the very pain points we have today! The idea with the new **Observer API** is to re-explore that unique design with a more wholistic approach that considers the broader subject of Reactive Programming in JavaScript!\n\n## Status\n\n+ Working implementation via a polyfill\n+ Integral to the [Quantum JS project](https://github.com/webqit/quantum-js)\n+ Actively developed\n+ Open to contributions\n\n## An Overview\n\nThe Observer API is a set of utility functions - notably, the `Observer.observe()` and `Observer.intercept()` methods - for all things object observability.\n\n\u003cdetails\u003e\u003csummary\u003eThis is documentation for Observer@2.x\u003c/summary\u003e\n\nLooking for [`Observer@1.x`](https://github.com/webqit/observer/tree/v1.7.6)?\n\n\u003c/details\u003e\n    \n### Method: `Observer.observe()`\n\nObserve mutations on arbitrary objects or arrays!\n\n```js\n// An object\nconst obj = {};\n// Mtation observer on an object\nconst abortController = Observer.observe( obj, inspect );\n```\n\n```js\n// An array\nconst arr = [];\n// Mtation observer on an array\nconst abortController = Observer.observe( arr, inspect );\n```\n\n└ *Changes are delivered [**synchronously**](https://github.com/webqit/observer/wiki/#timing-and-batching) - as they happen.*\n\n```js\n// The change handler\nfunction inspect( mutations ) {\n    mutations.forEach( mutation =\u003e {\n        console.log( mutation.type, mutation.key, mutation.value, mutation.oldValue );\n    } );\n}\n```\n\n**--\u003e** Stop observing at any time by calling `abort()` on the returned *abortController*:\n\n```js\n// Remove listener\nabortController.abort();\n```\n\n└ And you can provide your own [Abort Signal](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) instance:\n\n```js\n// Providing an AbortSignal\nconst abortController = new AbortController;\nObserver.observe( obj, inspect, { signal: abortController.signal } );\n```\n\n```js\n// Abort at any time\nabortController.abort();\n```\n\n**--\u003e** Where listeners initiate nested observers (child observers), leverage \"AbortSignal-cascading\" to tie child observers to parent observer's lifecycle:\n\n```js\n// Parent - \nconst abortController = Observer.observe( obj, ( mutations, flags ) =\u003e {\n\n    // Child\n    Observer.observe( obj, inspect, { signal: flags.signal } ); // \u003c\u003c\u003c---- AbortSignal-cascading\n\n    // Child\n    Observer.observe( obj, inspect, { signal: flags.signal } ); // \u003c\u003c\u003c---- AbortSignal-cascading\n\n} );\n```\n\n└ *\"Child\" observers get automatically aborted at parent's \"next turn\", and at parent's own abortion!*\n\n**--\u003e** Use the `options.diff` parameter to ignore mutation events whose current value is same as previous value:\n\n```js\n// Parent - \nconst abortController = Observer.observe( obj, mutations =\u003e {\n  console.log( m.type, m.value, m.oldValue );\n}, { diff: true } );\n```\n\n```js\nobj.property = 'Same value';\n```\n\n```js\nobj.property = 'Same value';\n```\n\n└ *Observer is called only on the first update!*\n\n#### Concept: *Mutation APIs*\n\nIn addition to making literal operations, you can also programmatically mutate properties of an object using the *[Reflect](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Reflect#static_methods)-like* set of operators; each operation will be reported by observers:\n\n```js\n// A single \"set\" operation on an object\nObserver.set( obj, 'prop0', 'value0' );\nObserver.defineProperty( obj, 'prop1', { get: () =\u003e 'value1' } );\nObserver.deleteProperty( obj, 'prop2' );\n```\n\n```js\n// A single \"set\" operation on an array\nObserver.set( arr, 0, 'item0' ); // Array [ 'item0' ]\nObserver.deleteProperty( arr, 0 ); // Array [ \u003c1 empty slot\u003e ]\n```\n\n\u003cdetails\u003e\u003csummary\u003ePolyfill limitations\u003c/summary\u003e\n\nIn the polyfill, object observability doesn't work with literal operations. **Beware non-reactive operations**:\n\n```js\n// Literal object operators\ndelete obj.prop0;\nobj.prop3 = 'value3';\n```\n\n```js\n// Array methods\narr.push( 'item3' );\narr.pop();\n```\n\n\u003c/details\u003e\n\n**--\u003e** Enable reactivity on *specific* properties with literal *object accessors* - using the `Observer.accessorize()` method:\n\n```js\n// Accessorize all current enumerable properties\nObserver.accessorize( obj );\n// Accessorize specific properties (existing or new)\nObserver.accessorize( obj, [ 'prop0', 'prop1', 'prop2' ] );\n\n// Make reactive UPDATES\nobj.prop0 = 'value0';\nobj.prop1 = 'value1';\nobj.prop2 = 'value2';\n```\n\n```js\n// Accessorize all current indexes\nObserver.accessorize( arr );\n// Accessorize specific indexes (existing or new)\nObserver.accessorize( arr, [ 0, 1, 2 ] );\n\n// Make reactive UPDATES\narr[ 0 ] = 'item0';\narr[ 1 ] = 'item1';\narr[ 2 ] = 'item2';\n\n// Bonus reactivity with array methods that re-index existing items\narr.unshift( 'new-item0' );\narr.shift();\n```\n\n\u003cdetails\u003e\u003csummary\u003ePolyfill limitations\u003c/summary\u003e\n\nIn the polyfill, object observability doesn't work with literal operations. **Beware non-reactive operations**:\n\n```js\n// The delete operator and object properties that haven't been accessorized\ndelete obj.prop0;\nobj.prop3 = 'value3';\n```\n\n```js\n// Array methods that do not re-index existing items\narr.push( 'item0' );\narr.pop();\n```\n\n\u003c/details\u003e\n\n**--\u003e** Enable reactivity on *arbitray* properties with *Proxies* - using the `Observer.proxy()` method:\n\n```js\n// Obtain a reactive Proxy for an object\nconst $obj = Observer.proxy( obj );\n\n// Make reactive operations\n$obj.prop1 = 'value1';\n$obj.prop4 = 'value4';\n$obj.prop8 = 'value8';\n\n// With the delete operator\ndelete $obj.prop0;\n```\n\n```js\n// Obtain a reactive Proxy for an array\nconst $arr = Observer.proxy( arr );\n\n// Make reactive operations\n$arr[ 0 ] = 'item0';\n$arr[ 1 ] = 'item1';\n$arr[ 2 ] = 'item2';\n\n// With an instance method\n$arr.push( 'item3' );\n```\n\n└ *And no problem if you end up nesting the approaches.*\n\n```js\n// 'value1'--\u003eobj\nObserver.accessorize( obj, [ 'prop0', 'prop1', 'prop2', ] );\nobj.prop1 = 'value1';\n\n// 'value1'--\u003e$obj--\u003eobj\nlet $obj = Observer.proxy( obj );\n$obj.prop1 = 'value1';\n\n// 'value1'--\u003eset()--\u003e$obj--\u003eobj\nObserver.set( $obj, 'prop1', 'value1' );\n```\n\n**--\u003e** \"Restore\" accessorized properties to their normal state by calling the `unaccessorize()` method:\n\n```js\nObserver.unaccessorize( obj, [ 'prop1', 'prop6', 'prop10' ] );\n```\n\n**--\u003e** \"Reproduce\" original objects from Proxies obtained via `Observer.proxy()` by calling the `unproxy()` method:\n\n```js\nobj = Observer.unproxy( $obj );\n```\n\n#### Concept: *Paths*\n\nObserve \"a property\" at a path in an object tree:\n\n```js\nconst obj = {\n  level1: {\n    level2: 'level2-value',\n  },\n};\n```\n\n```js\nconst path = Observer.path( 'level1', 'level2' );\nObserver.observe( obj, path, m =\u003e {\n  console.log( m.type, m.path, m.value, m.isUpdate );\n} );\n```\n\n```js\nObserver.set( obj.level1, 'level2', 'level2-new-value' );\n```\n\n\u003cdetails\u003e\u003csummary\u003eConsole\u003c/summary\u003e\n\n| type | path | value | isUpdate |\n| ---- | ---- | ----- | -------- |\n| `set` | [ `level1`, `level2`, ] | `level2-new-value` | `true` |\n\n\u003c/details\u003e\n\n└ *And the initial tree structure can be whatever*:\n\n```js\n// A tree structure that is yet to be built\nconst obj = {};\n```\n\n```js\nconst path = Observer.path( 'level1', 'level2', 'level3', 'level4' );\nObserver.observe( obj, path, m =\u003e {\n  console.log( m.type, m.path, m.value, m.isUpdate );\n} );\n```\n\n└ *Now, any operation that changes what \"the value\" at the path resolves to - either by tree extension or tree truncation - will fire our listener*:\n\n```js\nObserver.set( obj, 'level1', { level2: {}, } );\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eConsole\u003c/summary\u003e\n\n| type | path | value | isUpdate |\n| ---- | ---- | ----- | -------- |\n| `set` | [ `level1`, `level2`, `level3`, `level4`, ] | `undefined` | `false` |\n\n\u003c/details\u003e\n\n└ *Meanwhile, this next one completes the tree, and the listener reports a value at its observed path*:\n\n```js\nObserver.set( obj.level1, 'level2', { level3: { level4: 'level4-value', }, } );\n```\n\n\u003cdetails\u003e\n\u003csummary\u003eConsole\u003c/summary\u003e\n\n| type | path | value | isUpdate |\n| ---- | ---- | ----- | -------- |\n| `set` | [ `level1`, `level2`, `level3`, `level4`, ] | `level4-value` | `false` |\n\n\u003c/details\u003e\n\n**--\u003e** Use the event's `context` property to inspect the parent event if you were to find the exact point at which mutation happened in the path in an audit trail:\n\n```js\nlet context = m.context;\nconsole.log(context);\n```\n\n└ *And up again one level until the root event*:\n\n```js\nlet parentContext = context.context;\nconsole.log(parentContext);\n```\n\n**--\u003e** Observe trees that are built *asynchronously*! Where a promise is encountered along the path, further access is paused until promise resolves:\n\n```js\nObserver.set( obj.level1, 'level2', Promise.resolve( { level3: { level4: 'level4-value', }, } ) );\n```\n\n#### Concept: *Batch Mutations*\n\nMake multiple mutations at a go, and they'll be correctly delivered as a batch to observers!\n\n```js\n// Batch operations on an object\nObserver.set( obj, {\n    prop0: 'value0',\n    prop1: 'value1',\n    prop2: 'value2',\n} );\nObserver.defineProperties( obj, {\n    prop0: { value: 'value0' },\n    prop1: { value: 'value1' },\n    prop2: { get: () =\u003e 'value2' },\n} );\nObserver.deleteProperties( obj, [ 'prop0', 'prop1', 'prop2' ] );\n```\n\n```js\n// Batch operations on an array\nObserver.set( arr, {\n    '0': 'item0',\n    '1': 'item1',\n    '2': 'item2',\n} );\nObject.proxy( arr ).push( 'item3', 'item4', 'item5', );\nObject.proxy( arr ).unshift( 'new-item0' );\nObject.proxy( arr ).splice( 0 );\n```\n\n**--\u003e** Use the `Observer.batch()` to batch multiple arbitrary mutations - whether related or not:\n\n```js\nObserver.batch( arr, async () =\u003e {\n    Observer.set( arr, 0, 'item0' ); // Array [ 'item0' ]\n    await somePromise();\n    Observer.set( arr, 2, 'item2' ); // Array [ 'item0', \u003c1 empty slot\u003e, 'item2' ]\n} );\n```\n\n\u003e Method calls on a proxied instance - e.g. `Object.proxy( arr ).splice( 0 )` - also follow this strategy.\n\n### Method: `Observer.intercept()`\n\nIntercept operations on any object or array before they happen! This helps you extend standard operations on an object - `Observer.set()`,  `Observer.deleteProperty()`, etc - using Proxy-like traps.\n\n└ *Below, we intercept all \"set\" operations for an HTTP URL then transform it to an HTTPS URL.*\n\n```js\nconst setTrap = ( operation, previous, next ) =\u003e {\n    if ( operation.key === 'url' \u0026\u0026 operation.value.startsWith( 'http:' ) ) {\n        operation.value = operation.value.replace( 'http:', 'https:' );\n    }\n    return next();\n};\nObserver.intercept( obj, 'set', setTrap );\n```\n\n└ *Now, only the first of the following will fly as-is.*\n\n```js\n// Not transformed\nObserver.set( obj, 'url', 'https://webqit.io' );\n\n// Transformed\nObserver.set( obj, 'url', 'http://webqit.io' );\n```\n\n└ *And below, we intercept all \"get\" operations for a certain value to trigger a network fetch behind the scenes.*\n\n```js\nconst getTrap = ( operation, previous, next ) =\u003e {\n    if ( operation.key === 'token' ) {\n        return next( fetch( tokenUrl ) );\n    }\n    return next();\n};\nObserver.intercept( obj, 'get', getTrap );\n```\n\n└ *And all of that can go into one \"traps\" object:*\n\n```js\nObserver.intercept( obj, {\n    get: getTrap,\n    set: setTrap,\n    deleteProperty: deletePropertyTrap,\n    defineProperty: definePropertyTrap,\n    ownKeys: ownKeysTrap,\n    has: hasTrap,\n    // etc\n} );\n```\n\n## Documentation\n\nVisit the [docs](https://github.com/webqit/observer/wiki) for full details - including [Reflect API Supersets](https://github.com/webqit/observer/wiki#featuring-reflect-api-supersets), [Timing and Batching](https://github.com/webqit/observer/wiki#timing-and-batching), [API Reference](https://github.com/webqit/observer/wiki#putting-it-all-together), etc.\n\n## The Polyfill\n\nThe Observer API is being developed as something to be used today - via a polyfill. The polyfill features all of what's documented - with limitations in the area of making mutations: you can only make mutations using the [Mutation APIs](#concept-mutation-apis).\n\n\u003cdetails\u003e\u003csummary\u003eLoad from a CDN\u003c/summary\u003e\n\n```html\n\u003cscript src=\"https://unpkg.com/@webqit/observer/dist/main.js\"\u003e\u003c/script\u003e\n```\n\n\u003e `4.4` kB min + gz | `13.9` KB min [↗](https://bundlephobia.com/package/@webqit/observer)\n\n```js\n// Obtain the APIs\nconst Observer = window.webqit.Observer;\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\u003csummary\u003eInstall from NPM\u003c/summary\u003e\n\n```bash\nnpm i @webqit/observer\n```\n\n```js\n// Import\nimport Observer from '@webqit/observer';;\n```\n\n\u003c/details\u003e\n\n## Getting Involved\n\nAll forms of contributions are welcome at this time. For example, implementation details are all up for discussion. And here are specific links:\n\n+ [Project](https://github.com/webqit/observer)\n+ [Documentation](https://github.com/webqit/observer/wiki)\n+ [Discusions](https://github.com/webqit/observer/discussions)\n+ [Issues](https://github.com/webqit/observer/issues)\n\n## License\n\nMIT.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebqit%2Fobserver","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fwebqit%2Fobserver","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fwebqit%2Fobserver/lists"}