{"id":19807602,"url":"https://github.com/notninja/scopy","last_synced_at":"2026-05-17T06:45:03.667Z","repository":{"id":57147873,"uuid":"85703104","full_name":"NotNinja/scopy","owner":"NotNinja","description":"Property scoping done quick and dirty","archived":false,"fork":false,"pushed_at":"2017-05-10T12:54:24.000Z","size":73,"stargazers_count":1,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-09-16T06:27:32.364Z","etag":null,"topics":["browser","javascript","nodejs","private","scopes","symbols"],"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/NotNinja.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-03-21T13:07:59.000Z","updated_at":"2017-05-09T23:35:56.000Z","dependencies_parsed_at":"2022-08-31T00:01:25.272Z","dependency_job_id":null,"html_url":"https://github.com/NotNinja/scopy","commit_stats":null,"previous_names":["skelp/scopy"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/NotNinja/scopy","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotNinja%2Fscopy","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotNinja%2Fscopy/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotNinja%2Fscopy/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotNinja%2Fscopy/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/NotNinja","download_url":"https://codeload.github.com/NotNinja/scopy/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/NotNinja%2Fscopy/sbom","scorecard":{"id":102799,"data":{"date":"2025-08-11","repo":{"name":"github.com/NotNinja/scopy","commit":"c83432fc56689432303c22edc7562e8a0cc449e7"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.5,"checks":[{"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":"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":"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/17 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":"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":"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":"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":"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":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"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.md:0","Info: FSF or OSI recognized license: MIT License: LICENSE.md: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":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","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/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 18 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"}}]},"last_synced_at":"2025-08-15T10:31:03.743Z","repository_id":57147873,"created_at":"2025-08-15T10:31:03.743Z","updated_at":"2025-08-15T10:31:03.743Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279003728,"owners_count":26083610,"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","status":"online","status_checked_at":"2025-10-10T02:00:06.843Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"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":["browser","javascript","nodejs","private","scopes","symbols"],"created_at":"2024-11-12T09:11:21.599Z","updated_at":"2025-10-10T11:14:02.275Z","avatar_url":"https://github.com/NotNinja.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"     .d8888b.\n    d88P  Y88b\n    Y88b.\n     \"Y888b.    .d8888b .d88b.  88888b.  888  888\n        \"Y88b. d88P\"   d88\"\"88b 888 \"88b 888  888\n          \"888 888     888  888 888  888 888  888\n    Y88b  d88P Y88b.   Y88..88P 888 d88P Y88b 888\n     \"Y8888P\"   \"Y8888P \"Y88P\"  88888P\"   \"Y88888\n                                888           888\n                                888      Y8b d88P\n                                888       \"Y88P\"\n\n[![Build Status](https://img.shields.io/travis/NotNinja/scopy/develop.svg?style=flat-square)](https://travis-ci.org/NotNinja/scopy)\n[![Coverage](https://img.shields.io/codecov/c/github/NotNinja/scopy/develop.svg?style=flat-square)](https://codecov.io/gh/NotNinja/scopy)\n[![Dev Dependency Status](https://img.shields.io/david/dev/NotNinja/scopy.svg?style=flat-square)](https://david-dm.org/NotNinja/scopy?type=dev)\n[![License](https://img.shields.io/npm/l/scopy.svg?style=flat-square)](https://github.com/NotNinja/scopy/blob/master/LICENSE.md)\n[![Release](https://img.shields.io/npm/v/scopy.svg?style=flat-square)](https://www.npmjs.com/package/scopy)\n\n[Scopy](https://github.com/NotNinja/scopy) is a lightweight quick and dirty property scoping library for JavaScript that\nutilizes ECMAScript 2015's `Symbol` where possible and falls back on good old trailing underscore naming conventions.\n\nIt doesn't really offer *true* privacy as symbols can still be looked up using `Object.getOwnPropertySymbols` and\nprefixing property names with underscores is obviously just a convention, offering no real hiding. However, the main\npurpose of Scopy is to provide a simple API that attempts to make it easier for you to make cleaner APIs by *hiding*\nproperties intended only for intended use to promote your public API.\n\nWhile closures can arguably offer better scoping, they are not always best for classes as it requires methods to be\ncreated and bound for each instance of that class within their constructor rather than building a reusable prototype.\nThis results in uglier code and performs less.\n\nWhen using Scopy, it is recommended not to attempt to use other scoping conventions in the same area of the code to get\nthe most out of its API.\n\n* [Install](#install)\n* [API](#api)\n* [Bugs](#bugs)\n* [Contributors](#contributors)\n* [License](#license)\n\n## Install\n\nInstall using the package manager for your desired environment(s):\n\n``` bash\n$ npm install --save scopy\n# OR:\n$ bower install --save scopy\n```\n\nYou'll need to have at least [Node.js](https://nodejs.org) and you'll only need [Bower](https://bower.io) if you want to\ninstall that way instead of using `npm`. While equals should be compatible with all versions of\n[Node.js](https://nodejs.org), it is only tested against version 4 and above.\n\nIf you want to simply download the file to be used in the browser you can find them below:\n\n* [Development Version](https://cdn.rawgit.com/NotNinja/scopy/master/dist/scopy.js) (22kb - [Source Map](https://cdn.rawgit.com/NotNinja/scopy/master/dist/scopy.js.map))\n* [Production Version](https://cdn.rawgit.com/NotNinja/scopy/master/dist/scopy.min.js) (1.6kb - [Source Map](https://cdn.rawgit.com/NotNinja/scopy/master/dist/scopy.min.js.map))\n\n## API\n\nAll API methods accept the same optional `options` parameter:\n\n| Option   | Description                                        | Default Value |\n| -------- | -------------------------------------------------- | ------------- |\n| `symbol` | Whether to use ES2015's `Symbol`, where supported. | `true`        |\n\nWhen specifying `options` for any API method call, it is recommended to pass the same options to all other API method\ncalls within your application/library to ensure that the same type of scoped keys are returned. Alternatively, the\n[Scopy.using](#scopyusingoptions) method can be used to create wrapped version of the Scopy API that will always use the\nspecified options for all methods called on the wrapped API, avoiding the need for duplicating the options throughout\nyour code base.\n\n### `Scopy(name[, options])`\n\nReturns a scoped key based on the specified `name` and using the `options` provided.\n\nIf the `symbol` option is enabled (which it is by default), an ES2015 `Symbol` will be returned (where supported),\notherwise this method will simply return `name` prefixed with an underscore.\n\n`Symbols` returned by this method will always be unique when called multiple times with the same `name`, however, string\nkeys will always be exactly the same.\n\nThis method is ideal when defining a key for a privately scoped property.\n\n``` javascript\n// user.js\nvar Scopy = require('scopy');\n\nvar _name = Scopy('name');\n\nfunction User(name) {\n  this[_name] = name;\n}\n\nUser.prototype.getName = function() {\n  return this[_name];\n};\n\nmodule.exports = User;\n```\n\n### `Scopy.all(names[, options])`\n\nReturns scoped keys based on the specified `names` and using the `options` provided.\n\nIf the `symbol` option is enabled (which it is by default), this method will return `names` mapped to ES2015 `Symbols\n(where supported), otherwise `names` will simply be mapped to themselves prefixed with an underscore.\n\n`Symbols` included in the mapping returned by this method will always be unique when called multiple times with the same\nname, however, string keys will always be exactly the same.\n\nThis method is ideal when defining keys for privately scoped properties.\n\n``` javascript\n// user.js\nvar Scopy = require('scopy');\n\nvar keys = Scopy.all([ 'email', 'name' ]);\n\nfunction User(email, name) {\n  this[keys.email] = email;\n  this[keys.name] = name;\n}\n\nUser.prototype.getEmail = function() {\n  return this[keys.email];\n};\n\nUser.prototype.getName = function() {\n  return this[keys.name];\n};\n\nmodule.exports = User;\n```\n\n### `Scopy.for(name[, options])`\n\nReturns a \"global\" key based on the specified `name` and using the `options` provided.\n\nIf the `symbol` option is enabled (which it is by default), an ES2015 `Symbol` will be returned from the runtime-wide\nsymbol registry (where supported), otherwise this method will simply return `name` prefixed with an underscore.\n\nUnlike [Scopy](#scopyname-options), `Symbols` returned by this method will always be the same when called multiple times\nwith the same `name`, just like string keys.\n\nIt is recommended that `name` be prefixed in order to avoid conflicts with other libraries that may also have a \"global\"\nkey of the same name.\n\nThis method is ideal when defining a key for a protected/internally scoped property.\n\n``` javascript\n// user.js\nvar Scopy = require('scopy');\nvar uuid = require('uuid/v4');\n\nvar _id = Scopy.for('example_user_id');\nvar _name = Scopy('name');\n\nfunction User(name) {\n  this[_id] = uuid();\n  this[_name] = name;\n}\n\nUser.prototype.getName = function() {\n  return this[_name];\n};\n\nmodule.exports = User;\n\n// user-logger.js\nvar EOL = require('os').EOL;\nvar Scopy = require('scopy');\n\nvar _id = Scopy.for('example_user_id');\n\nexports.login = function(output, user) {\n  var id = user[_id];\n\n  output.write('User[' + id + '] has logged in!' + EOL);\n};\n```\n\n### `Scopy.for.all(names[, options])`\n\n**Alias:** `Scopy.forAll`\n\nReturns \"global\" keys based on the specified `names` and using the `options` provided.\n\nIf the `symbol` option is enabled (which it is by default), this method will return `names` mapped to ES2015 `Symbols`\nfrom the runtime-wide symbol registry (where supported), otherwise `names` will simply be mapped to themselves prefixed\nwith an underscore.\n\nUnlike [Scopy.all](#scopyallnames-options), `Symbols` included in the mapping returned by this method will always be the\nsame when called multiple times with the same name, just like string keys.\n\nIt is recommended that each name within `names` be prefixed in order to avoid conflicts with other libraries that may\nalso have a \"global\" key of the same name.\n\nThis method is ideal when defining keys for protected/internally scoped properties.\n\n``` javascript\n// user.js\nvar Scopy = require('scopy');\nvar uuid = require('uuid/v4');\n\nvar keys = Scopy.for.all([ 'example_user_id', 'example_user_lastUpdatedBy' ]);\nvar _name = Scopy('name');\n\nfunction User(name) {\n  this[keys['example.user.id']] = uuid();\n  this[keys['example.user.lastUpdatedBy']] = null;\n  this[_name] = name;\n}\n\nUser.prototype.getName = function() {\n  return this[_name];\n};\n\nmodule.exports = User;\n\n// user-logger.js\nvar EOL = require('os').EOL;\nvar Scopy = require('scopy');\n\nvar keys = Scopy.for.all([ 'example_user_id', 'example_user_lastUpdatedBy' ]);\n\nexports.update = function(output, user) {\n  var id = user[keys['example.user.id']];\n  var lastUpdatedBy = user[keys['example.user.lastUpdatedBy']];\n\n  output.write('User[' + id + '] has been updated by ' + lastUpdatedBy + EOL);\n};\n```\n\n### `Scopy.is(obj[, options])`\n\nReturns whether the specified `obj` represents a scoped or \"global\" key based on the `options` provided.\n\nIf the `symbol` option is enabled (which it is by default), this method will return `true` if `obj` is an ES2015\n`Symbol` (where supported), otherwise it will only return `true` if and only if `obj` is a string that starts with at\nleast one underscore.\n\n``` javascript\nvar Scopy = require('scopy');\n\nScopy.is(Scopy('foo'));\n//=\u003e true\nScopy.is(Scopy('foo', { symbol: false }));\n//=\u003e true\nScopy.is('foo');\n//=\u003e false\n```\n\n### `Scopy.entries(obj[, options])`\n\nReturns the key/value pairs for all of the specified object's own enumerable properties that are not scoped, in the\nsame order as that provided by a for-in loop.\n\nThis method returns a multi-dimensional array where each item is an array containing the name and value\n(`[name, value]`) of a non-scoped enumerable property.\n\nProperties mapped to a `Symbol` are **never** included in this method but those mapped to properties whose name is\nprefixed with an underscore, created when the `symbol` option has been explicitly disabled, **will** be included\n**unless** the `symbol` option is explicitly disabled when calling this method.\n\nThis method is intended to be used just like ES2015's `Object.entries` method while also supporting cases where\n`Symbols` have not been used to scope properties.\n\n``` javascript\nvar Scopy = require('scopy');\nvar uuid = require('uuid/v4');\n\nvar _generateId = Scopy('generateId');\nvar _id = Scopy('id');\nvar _name = Scopy('name');\n\nfunction User(name) {\n  this[_id] = this[_generateId]();\n  this[_name] = name;\n  this.length = name.length;\n}\n\nUser.prototype[_generateId] = function() {\n  return uuid();\n};\n\nUser.prototype.getName = function() {\n  return this[_name];\n};\n\nScopy.entries(new User('foo'));\n//=\u003e [ [ \"length\", 3 ] ]\nScopy.entries(User.prototype);\n//=\u003e [ [ \"getName\", function() { return this[_name] } ] ]\n```\n\n### `Scopy.keys(obj[, options])`\n\nReturns the names of all of the specified object's own enumerable properties that are not scoped, in the same order as\nthat provided by a for-in loop.\n\nProperties mapped to a `Symbol` are **never** included in this method but those mapped to properties whose name is\nprefixed with an underscore, created when the `symbol` option has been explicitly disabled, **will** be included\n**unless** the `symbol` option is explicitly disabled when calling this method.\n\nThis method is intended to be used just like ES2015's `Object.keys` method while also supporting cases where `Symbols`\nhave not been used to scope properties.\n\n``` javascript\nvar Scopy = require('scopy');\nvar uuid = require('uuid/v4');\n\nvar _generateId = Scopy('generateId');\nvar _id = Scopy('id');\nvar _name = Scopy('name');\n\nfunction User(name) {\n  this[_id] = this[_generateId]();\n  this[_name] = name;\n  this.length = name.length;\n}\n\nUser.prototype[_generateId] = function() {\n  return uuid();\n};\n\nUser.prototype.getName = function() {\n  return this[_name];\n};\n\nScopy.keys(new User('foo'));\n//=\u003e [ \"length\" ]\nScopy.keys(User.prototype);\n//=\u003e [ \"getName\" ]\n```\n\n### `Scopy.values(obj[, options])`\n\nReturns the values of all of the specified object's own enumerable properties that are not scoped, in the same order as\nthat provided by a for-in loop.\n\nProperties mapped to a `Symbol` are **never** included in this method but those mapped to properties whose name is\nprefixed with an underscore, created when the`symbol` option has been explicitly disabled, **will** be included\n**unless** the `symbol` option is explicitly disabled when calling this method.\n\nThis method is intended to be used just like ES2015's`Object.values` method while also supporting cases where `Symbols`\nhave not been used to scope properties.\n\n``` javascript\nvar Scopy = require('scopy');\nvar uuid = require('uuid/v4');\n\nvar _generateId = Scopy('generateId');\nvar _id = Scopy('id');\nvar _name = Scopy('name');\n\nfunction User(name) {\n  this[_id] = this[_generateId]();\n  this[_name] = name;\n  this.length = name.length;\n}\n\nUser.prototype[_generateId] = function() {\n  return uuid();\n};\n\nUser.prototype.getName = function() {\n  return this[_name];\n};\n\nScopy.values(new User('foo'));\n//=\u003e [ 3 ]\nScopy.values(User.prototype);\n//=\u003e [ function() { return this[_name] } ]\n```\n\n### `Scopy.using([options])`\n\nReturns a version of [Scopy](#scopyname-options) that is bound (along with *all* of its methods) to the specified\n`options`.\n\nSince it's recommended that consumers use the same options, when specified, this method can be really useful as it\nallows consumers to only specify the options once. This is especially useful for those wishing to explictly disable the\n`symbol` option.\n\nAny options passed to the methods within the returned wrapped Scopy API will be ignored in favor of `options`.\n\n``` javascript\nvar Scopy = require('scopy').using({ symbol: false });\n\nScopy('foo');\n//=\u003e \"_foo\"\nScopy.all([ 'foo', 'bar' ]);\n//=\u003e { foo: \"_foo\", bar: \"_bar\" }\nScopy.is('_foo');\n//=\u003e true\nScopy.is(Symbol('foo'));\n//=\u003e false\n```\n\n## Bugs\n\nIf you have any problems with Scopy or would like to see changes currently in development you can do so\n[here](https://github.com/NotNinja/scopy/issues).\n\n## Contributors\n\nIf you want to contribute, you're a legend! Information on how you can do so can be found in\n[CONTRIBUTING.md](https://github.com/NotNinja/scopy/blob/master/CONTRIBUTING.md). We want your suggestions and pull\nrequests!\n\nA list of Scopy contributors can be found in [AUTHORS.md](https://github.com/NotNinja/scopy/blob/master/AUTHORS.md).\n\n## License\n\nSee [LICENSE.md](https://github.com/NotNinja/scopy/raw/master/LICENSE.md) for more information on our MIT license.\n\n[![Copyright !ninja](https://cdn.rawgit.com/NotNinja/branding/master/assets/copyright/base/not-ninja-copyright-186x25.png)](https://not.ninja)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotninja%2Fscopy","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnotninja%2Fscopy","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnotninja%2Fscopy/lists"}