{"_id":"@cultivateinc/sinon-chrome","_rev":"1-a2d9112d1080ed15da644dcb33c1b8a0","name":"@cultivateinc/sinon-chrome","dist-tags":{"latest":"3.1.0"},"versions":{"3.1.0":{"name":"@cultivateinc/sinon-chrome","version":"3.1.0","description":"Mock of chrome extensions API for unit testing under nodejs","homepage":"https://github.com/acvetkov/sinon-chrome","author":{"name":"Aleksey Tsvetkov","email":"aleksey.dmitrich@gmail.com"},"contributors":[{"name":"Aleksey Tsvetkov","email":"aleksey.dmitrich@gmail.com","url":"https://github.com/acvetkov"},{"name":"Vitaly Potapov","email":"noginsk@rambler.ru","url":"https://github.com/vitalets"},{"name":"Sokratis Vidros","email":"sokratis.vidros@gmail.com","url":"https://github.com/SokratisVidros"}],"repository":{"type":"git","url":"git://github.com/acvetkov/sinon-chrome.git"},"bugs":{"url":"https://github.com/acvetkov/sinon-chrome/issues"},"keywords":["chrome","firefox","extensions","apps","test","tdd","webextensions","chrome api","sinon"],"main":"index.js","scripts":{"clean":"rm -rf ./out/","lint":"eslint ./src ./test","test":"mocha --recursive --compilers js:babel-register --require ./test/setup.js","test-navigate":"mocha --compilers js:babel-register examples/navigate.test.js","test-events":"mocha --compilers js:babel-register examples/events.test.js","test-then":"mocha --compilers js:babel-register examples/then-chrome.test.js","transpile":"babel ./src --out-dir ./out","build":"npm run clean && npm run transpile && webpack && cp -R ./src/config out/config","predeploy":"npm run clean && npm run lint && npm test && npm run build && npm run copy2out","bundle":"webpack","bundle-dev":"webpack --watch --progress","copy2out":"cp ./readme.md ./out/readme.md && cp ./package.json ./out/package.json && cp -R ./dist out/bundle"},"license":"ISC","dependencies":{"lodash":"^4.16.3","sinon":"^7.2.3","urijs":"^1.19.6"},"devDependencies":{"babel-cli":"^6.16.0","babel-core":"^6.17.0","babel-eslint":"^7.0.0","babel-loader":"^6.2.5","babel-plugin-add-module-exports":"^0.2.1","babel-preset-es2015":"^6.16.0","babel-register":"^6.16.3","chai":"^3.5.0","chai-as-promised":"^6.0.0","eslint":"^6.6.0","eslint-plugin-import":"^2.0.0","json-loader":"^0.5.4","mocha":"^3.1.0","mocha-lcov-reporter":"^1.2.0","mocha-tldr-reporter":"^1.0.0","webpack":"^1.13.2"},"gitHead":"bd5d7ceb3d2a6574206d3fa9f80522cf7da85bac","_id":"@cultivateinc/sinon-chrome@3.1.0","_nodeVersion":"16.18.1","_npmVersion":"8.19.2","dist":{"integrity":"sha512-UA53b27/WI0+pYo2NxYPCXixE8GrAwXzB2dhliXzr0NQXO2v2Us+bE1bkG+O6K3A6TZz3AUOK28GulcEH4vFSA==","shasum":"5c7cb43d4293679a016a2821eca6c60bafcf110c","tarball":"https://registry.npmjs.org/@cultivateinc/sinon-chrome/-/sinon-chrome-3.1.0.tgz","fileCount":34,"unpackedSize":4962475,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIQC3qU7lwt/zwhY2MIyBANqoj6ULFihbR2NvHDvoL3RrFQIgblQ8kHJicUFrS4aCuhQjszV84mVCDuuypSmvuBrMceM="}],"npm-signature":"-----BEGIN PGP SIGNATURE-----\r\nVersion: OpenPGP.js v4.10.10\r\nComment: https://openpgpjs.org\r\n\r\nwsFzBAEBCAAGBQJjmH/3ACEJED1NWxICdlZqFiEECWMYAoorWMhJKdjhPU1b\r\nEgJ2VmqCfRAAoNUXPyPYRg2W+A3Y7qVCrJyRTRVEfVImb5FRZhpX0Kvc5OfN\r\n8KMSIfHfmsCpK23dVPd5Sza7286ZE7e9adGycrKU0fj2vMNWacObA8lNI+HY\r\nwamZOoZYejwW7MqOKXgNPCs4qba6SU/hmTBIInLd6n6wJK9hWYxIdt7KT37O\r\nKwX68M+tNbi0d/3hN246kMs8yaQP10T+qQPYPEH3tbJkV2pK5rsfe7EC9+r3\r\n0Jl+TyePnadncJQvz5FQE2y/7v3WHARpDrMjlL6OZkzrkFX5z2WiPMc7XyaW\r\niisaQITrSJRgwhEWUIpmWVBwuLNLo5dvn4nbeyxsXb2lkEWmUuGP/Us34QNS\r\nl0eKZyQCDvpvXtf22Q8XfBs8bCK/z2XK1ZypjUdaoJ19//JZ07EX4XwdDDA0\r\nraX8O5/xQIoH02QtIP8yxZntyOUou1Ew8UAv/sHuVrw3p46I0xrL1uBJRoKI\r\nkN7z1+t/3x32eXMPuCuJd3bf1U0DnpJy9ZI5Ina9WWPTvZfnLhcTJO9O2WQp\r\nPbCEkNvNAAe2hmktZvf7FFG8FGi+S5X4UMRsolAJyVlDy/R6mRKdTPkw5iA/\r\nxnTSulr/LXdbvCqVhE9AqoRMk2RlKzl7iSVo9gY4koh20uCoeRaoLmmjsDGS\r\nk6udf7lckF+PPVCOJKsHC4mJFjz6LELfYjA=\r\n=iJVD\r\n-----END PGP SIGNATURE-----\r\n"},"_npmUser":{"name":"hdpe","email":"ryan+npm@hdpe.me"},"directories":{},"maintainers":[{"name":"hdpe","email":"ryan+npm@hdpe.me"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/sinon-chrome_3.1.0_1670938615433_0.3006480001073122"},"_hasShrinkwrap":false}},"time":{"created":"2022-12-13T13:36:55.122Z","3.1.0":"2022-12-13T13:36:55.690Z","modified":"2023-10-02T12:45:33.262Z"},"maintainers":[{"email":"harsh@wecultivate.us","name":"harshkhurana"},{"email":"ryan+npm@hdpe.me","name":"hdpe"}],"description":"Mock of chrome extensions API for unit testing under nodejs","homepage":"https://github.com/acvetkov/sinon-chrome","keywords":["chrome","firefox","extensions","apps","test","tdd","webextensions","chrome api","sinon"],"repository":{"type":"git","url":"git://github.com/acvetkov/sinon-chrome.git"},"contributors":[{"name":"Aleksey Tsvetkov","email":"aleksey.dmitrich@gmail.com","url":"https://github.com/acvetkov"},{"name":"Vitaly Potapov","email":"noginsk@rambler.ru","url":"https://github.com/vitalets"},{"name":"Sokratis Vidros","email":"sokratis.vidros@gmail.com","url":"https://github.com/SokratisVidros"}],"author":{"name":"Aleksey Tsvetkov","email":"aleksey.dmitrich@gmail.com"},"bugs":{"url":"https://github.com/acvetkov/sinon-chrome/issues"},"license":"ISC","readme":"## sinon-chrome\n\nForked from [acvetkov/sinon-chrome](https://github.com/acvetkov/sinon-chrome).\n\n```\nnpm i -D @cultivateinc/sinon-chrome\n```\n\n# Sinon-chrome\n\nSinon-chrome is helper tool for unit-testing chromium and Firefox extensions and apps. It mocks all extensions api with sinon stubs that allows you to run tests in Node.js without actual browser.\n\n# Schema support\nAPI mocks are generated using official chromium extensions API (Firefox webextensions) schemas that ensures consistency with real API. Actual schemas are taken from Chrome 53 and Firefox 49.\n\n# How it works\n\nSinon-chrome mocks all chrome api, replaced methods by [sinon stubs](http://sinonjs.org/docs/#stubs) with some sugar.\nChrome events replaced by classes with same behavior, so you can test your event handlers with manual triggering chrome events.\nAll properties has values from chrome schema files.\n\n# Usage\n\nFor mock extensions Api\n\n```js\nconst chrome = require('@cultivateinc/sinon-chrome');\n\n// or\n\nconst chrome = require('@cultivateinc/sinon-chrome/extensions');\n```\n\nFor mock apps Api\n\n```js\nconst chrome = require('@cultivateinc/sinon-chrome/apps'); // stable apps api\n```\n\n# Examples\n\nLet's write small navigation helper, which use chrome api methods.\n\n```js\nexport const navigationTarget = {\n    NEW_WINDOW: 'new-window',\n    NEW_TAB: 'new-tab',\n    CURRENT_TAB: 'current-tab',\n};\n\n/**\n * Navigate user\n * @param {String} url\n * @param {String} [target]\n * @returns {*}\n */\nexport function navigate(url, target = navigationTarget.NEW_TAB) {\n    switch (target) {\n        case navigationTarget.NEW_WINDOW:\n            return chrome.windows.create({url: url, focused: true, type: 'normal'});\n        case navigationTarget.CURRENT_TAB:\n            return chrome.tabs.update({url: url, active: true});\n        default:\n            return chrome.tabs.create({url: url, active: true});\n    }\n}\n```\n\nTest it\n\n```js\nimport chrome from '../src'; // from 'sinon-chrome'\nimport {assert} from 'chai';\nimport {navigate, navigationTarget} from './navigate';\n\ndescribe('navigate.js', function () {\n\n    const url = 'http://my-domain.com';\n\n    before(function () {\n        global.chrome = chrome;\n    });\n\n    it('should navigate to new window', function () {\n        assert.ok(chrome.windows.create.notCalled, 'windows.create should not be called');\n        navigate(url, navigationTarget.NEW_WINDOW);\n        assert.ok(chrome.windows.create.calledOnce, 'windows.create should be called');\n        assert.ok(\n            chrome.windows.create.withArgs({url, focused: true, type: 'normal'}).calledOnce,\n            'windows.create should be called with specified args'\n        );\n    });\n});\n```\n\nYou can run this example by command\n\n```\nnpm run test-navigate\n```\n\nMore tests in `examples` dir.\n\n\n## stubs api\n\nWith original sinon stubs [api](http://sinonjs.org/docs/#stubs) we add `flush` method, which reset stub behavior.\nSinon stub has same method `resetBehavior`, but it has some issues.\n\n**Example**\n\n```js\nchrome.cookie.getAll.withArgs({name: 'my_cookie'}).yields([1, 2]);\nchrome.cookie.getAll.withArgs({}).yields([3, 4]);\n\nchrome.cookie.getAll({}, list => console.log(list)); // [3, 4]\nchrome.cookie.getAll({name: 'my_cookie'}, list => console.log(list)); // [1, 2]\nchrome.cookie.getAll.flush();\nchrome.cookie.getAll({name: 'my_cookie'}, list => console.log(list)); // not called\nchrome.cookie.getAll({}, list => console.log(list)); // not called\n```\n\n### events\n\nLet's write module, which depends on chrome events\n\n```js\nexport default class EventsModule {\n    constructor() {\n        this.observe();\n    }\n\n    observe() {\n        chrome.tabs.onUpdated.addListener(tab => this.handleEvent(tab));\n    }\n\n    handleEvent(tab) {\n        chrome.runtime.sendMessage(tab.url);\n    }\n}\n```\n\nAnd test it\n\n```js\nimport chrome from '../src'; // from 'sinon-chrome'\nimport {assert} from 'chai';\nimport EventsModule from './events';\n\ndescribe('events.js', function () {\n\n    before(function () {\n        global.chrome = chrome;\n        this.events = new EventsModule();\n    });\n\n    beforeEach(function () {\n        chrome.runtime.sendMessage.flush();\n    });\n\n    it('should subscribe on chrome.tabs.onUpdated', function () {\n        assert.ok(chrome.tabs.onUpdated.addListener.calledOnce);\n    });\n\n    it('should send correct url on tabs updated event', function () {\n        assert.ok(chrome.runtime.sendMessage.notCalled);\n        chrome.tabs.onUpdated.dispatch({url: 'my-url'});\n        assert.ok(chrome.runtime.sendMessage.calledOnce);\n        assert.ok(chrome.runtime.sendMessage.withArgs('my-url').calledOnce);\n    });\n\n    after(function () {\n        chrome.flush();\n        delete global.chrome;\n    });\n});\n```\n\nYou can run this test via\n\n```\nnpm run test-events\n```\n\n### properties\n\nYou can set property values. `chrome.flush` reset properties to default values (`null` or specified by schema).\nLet's create module, which wraps chrome api with Promise. If `chrome.runtime.lastError` is set, promise will be rejected.\n\n```js\nexport const api = {\n    tabs: {\n        /**\n         * Wrapper for chrome.tabs.query\n         * @param {Object} criteria\n         * @returns {Promise}\n         */\n        query(criteria) {\n            return new Promise((resolve, reject) => {\n                chrome.tabs.query(criteria, tabs => {\n                    if (chrome.runtime.lastError) {\n                        reject(chrome.runtime.lastError);\n                    } else {\n                        resolve(tabs);\n                    }\n                });\n            });\n        }\n    }\n};\n```\n\nAnd our tests\n\n```js\nimport chrome from '../src'; // from 'sinon-chrome'\nimport chai from 'chai';\nimport {api} from './then-chrome';\nimport chaiAsPromised from 'chai-as-promised';\n\nchai.use(chaiAsPromised);\nconst assert = chai.assert;\n\ndescribe('then-chrome.js', function () {\n\n    before(function () {\n        global.chrome = chrome;\n    });\n\n    beforeEach(function () {\n        chrome.flush();\n    });\n\n    it('should reject promise', function () {\n        chrome.tabs.query.yields([1, 2]);\n        chrome.runtime.lastError = {message: 'Error'};\n        return assert.isRejected(api.tabs.query({}));\n    });\n\n    it('should resolve promise', function () {\n        chrome.runtime.lastError = null;\n        chrome.tabs.query.yields([1, 2]);\n        return assert.eventually.deepEqual(api.tabs.query({}), [1, 2]);\n    });\n\n    after(function () {\n        chrome.flush();\n        delete global.chrome;\n    });\n});\n```\n\nYou can run this test via\n\n```\nnpm run test-then\n```\n\n# Plugins\n\nSinon chrome module supports plugins, that emulates browser behavior.\nMore info on [example page](https://github.com/acvetkov/sinon-chrome/wiki/Cookie-plugin).\n\n- [Cookie plugin](https://github.com/acvetkov/sinon-chrome/wiki/Cookie-plugin)\n- [i18n plugin](https://github.com/acvetkov/sinon-chrome/tree/master/src/plugins/i18n)\n\n```js\nconst chrome = require('@cultivateinc/sinon-chrome/extensions');\nconst CookiePlugin = require('@cultivateinc/sinon-chrome/plugins').CookiePlugin;\n\nchrome.registerPlugin(new CookiePlugin());\n```\n\n## Extension namespaces\n\n* [chrome.alarms](https://developer.chrome.com/extensions/alarms)\n* [chrome.bookmarks](https://developer.chrome.com/extensions/bookmarks)\n* [chrome.browserAction](https://developer.chrome.com/extensions/browserAction)\n* [chrome.browsingData](https://developer.chrome.com/extensions/browsingData)\n* [chrome.commands](https://developer.chrome.com/extensions/commands)\n* [chrome.contentSettings](https://developer.chrome.com/extensions/contentSettings)\n* [chrome.contextMenus](https://developer.chrome.com/extensions/contextMenus)\n* [chrome.cookies](https://developer.chrome.com/extensions/cookies)\n* [chrome.debugger](https://developer.chrome.com/extensions/debugger)\n* [chrome.declarativeContent](https://developer.chrome.com/extensions/declarativeContent)\n* [chrome.desktopCapture](https://developer.chrome.com/extensions/desktopCapture)\n* [chrome.devtools](https://developer.chrome.com/extensions/devtools)\n* [chrome.dial](https://chromium.googlesource.com/chromium/src.git/+/master/chrome/common/extensions/api/dial.idl) (undocumented API for communication with DIAL-capable devices)\n* [chrome.downloads](https://developer.chrome.com/extensions/downloads)\n* [chrome.extension](https://developer.chrome.com/extensions/extension)\n* [chrome.extensionTypes](https://developer.chrome.com/extensions/extensionTypes)\n* [chrome.fontSettings](https://developer.chrome.com/extensions/fontSettings)\n* [chrome.gcm](https://developer.chrome.com/extensions/gcm)\n* [chrome.history](https://developer.chrome.com/extensions/history)\n* [chrome.i18n](https://developer.chrome.com/extensions/i18n)\n* [chrome.identity](https://developer.chrome.com/extensions/identity)\n* [chrome.idle](https://developer.chrome.com/extensions/idle)\n* [chrome.instanceID](https://developer.chrome.com/extensions/instanceID)\n* [chrome.management](https://developer.chrome.com/extensions/management)\n* [chrome.notifications](https://developer.chrome.com/extensions/notifications)\n* [chrome.omnibox](https://developer.chrome.com/extensions/omnibox)\n* [chrome.pageAction](https://developer.chrome.com/extensions/pageAction)\n* [chrome.pageCapture](https://developer.chrome.com/extensions/pageCapture)\n* [chrome.permissions](https://developer.chrome.com/extensions/permissions)\n* [chrome.power](https://developer.chrome.com/extensions/power)\n* [chrome.printerProvider](https://developer.chrome.com/extensions/printerProvider)\n* [chrome.privacy](https://developer.chrome.com/extensions/privacy)\n* [chrome.proxy](https://developer.chrome.com/extensions/proxy)\n* [chrome.runtime](https://developer.chrome.com/extensions/runtime)\n* [chrome.sessions](https://developer.chrome.com/extensions/sessions)\n* [chrome.storage](https://developer.chrome.com/extensions/storage)\n* [chrome.system](https://developer.chrome.com/extensions/system)\n* [chrome.tabCapture](https://developer.chrome.com/extensions/tabCapture)\n* [chrome.tabs](https://developer.chrome.com/extensions/tabs)\n* [chrome.topSites](https://developer.chrome.com/extensions/topSites)\n* [chrome.tts](https://developer.chrome.com/extensions/tts)\n* [chrome.ttsEngine](https://developer.chrome.com/extensions/ttsEngine)\n* [chrome.types](https://developer.chrome.com/extensions/types)\n* [chrome.webNavigation](https://developer.chrome.com/extensions/webNavigation)\n* [chrome.webRequest](https://developer.chrome.com/extensions/webRequest)\n* [chrome.webstore](https://developer.chrome.com/extensions/webstore)\n* [chrome.windows](https://developer.chrome.com/extensions/windows)\n\n## Apps namespaces\n\n* [chrome.alarms](https://developer.chrome.com/apps/alarms)\n* [chrome.app.runtime](https://developer.chrome.com/apps/app.runtime)\n* [chrome.app.window](https://developer.chrome.com/apps/app_window)\n* [chrome.bluetooth](https://developer.chrome.com/apps/bluetooth)\n* [chrome.bluetoothSocket](https://developer.chrome.com/apps/bluetoothSocket)\n* [chrome.browser](https://developer.chrome.com/apps/browser)\n* [chrome.commands](https://developer.chrome.com/apps/commands)\n* [chrome.contextMenus](https://developer.chrome.com/apps/contextMenus)\n* [chrome.events](https://developer.chrome.com/apps/events)\n* [chrome.extensionTypes](https://developer.chrome.com/apps/extensionTypes)\n* [chrome.fileSystem](https://developer.chrome.com/apps/fileSystem)\n* [chrome.gcm](https://developer.chrome.com/apps/gcm)\n* [chrome.hid](https://developer.chrome.com/apps/hid)\n* [chrome.i18n](https://developer.chrome.com/apps/i18n)\n* [chrome.identity](https://developer.chrome.com/apps/identity)\n* [chrome.idle](https://developer.chrome.com/apps/idle)\n* [chrome.instanceID](https://developer.chrome.com/apps/instanceID)\n* [chrome.mdns](https://developer.chrome.com/apps/mdns)\n* [chrome.mediaGalleries](https://developer.chrome.com/apps/mediaGalleries)\n* [chrome.notifications](https://developer.chrome.com/apps/notifications)\n* [chrome.permissions](https://developer.chrome.com/apps/permissions)\n* [chrome.power](https://developer.chrome.com/apps/power)\n* [chrome.printerProvider](https://developer.chrome.com/apps/printerProvider)\n* [chrome.runtime](https://developer.chrome.com/apps/runtime)\n* [chrome.serial](https://developer.chrome.com/apps/serial)\n* [chrome.socket](https://developer.chrome.com/apps/socket)\n* [chrome.sockets](https://developer.chrome.com/apps/sockets)\n* [chrome.storage](https://developer.chrome.com/apps/storage)\n* [chrome.syncFileSystem](https://developer.chrome.com/apps/syncFileSystem)\n* [chrome.system](https://developer.chrome.com/apps/system)\n* [chrome.tts](https://developer.chrome.com/apps/tts)\n* [chrome.types](https://developer.chrome.com/apps/types)\n* [chrome.usb](https://developer.chrome.com/apps/usb)\n* [chrome.accessibilityFeatures](https://developer.chrome.com/apps/accessibilityFeatures)\n* [chrome.bluetoothLowEnergy](https://developer.chrome.com/apps/bluetoothLowEnergy)\n* [chrome.documentScan](https://developer.chrome.com/apps/documentScan)\n* [chrome.fileSystemProvider](https://developer.chrome.com/apps/fileSystemProvider)\n* [chrome.vpnProvider](https://developer.chrome.com/apps/vpnProvider)\n* [chrome.wallpaper](https://developer.chrome.com/apps/wallpaper)\n\n## Webextensions API\n\n* [browser.alarms](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/alarms)\n* [browser.cookies](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/cookies)\n* [browser.downloads](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/downloads)\n* [browser.events](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/events)\n* [browser.manifest](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/manifest)\n* [browser.extensionTypes](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/extensionTypes)\n* [browser.extension](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/extension)\n* [browser.i18n](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/i18n)\n* [browser.idle](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/idle)\n* [browser.management](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/management)\n* [browser.notifications](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/notifications)\n* [browser.runtime](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/runtime)\n* [browser.storage](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/storage)\n* [browser.test](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/test)\n* [browser.webNavigation](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webNavigation)\n* [browser.webRequest](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest)\n* [browser.bookmarks](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/bookmarks)\n* [browser.browserAction](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/browserAction)\n* [browser.commands](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/commands)\n* [browser.contextMenusInternal](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/contextMenusInternal)\n* [browser.contextMenus](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/contextMenus)\n* [browser.history](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/history)\n* [browser.pageAction](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/pageAction)\n* [browser.tabs](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/tabs)\n* [browser.windows](https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/windows)\n\n# Useful resources\n[Awesome Browser Extensions And Apps](https://github.com/vitalets/awesome-browser-extensions-and-apps) - a curated list of awesome resources for building browser extensions and apps.\n","readmeFilename":"readme.md"}