{"id":13658955,"url":"https://github.com/scriptype/salinger","last_synced_at":"2025-08-27T13:19:30.776Z","repository":{"id":45752477,"uuid":"81115921","full_name":"scriptype/salinger","owner":"scriptype","description":"Ecosystem-free task runner that goes well with npm scripts.","archived":false,"fork":false,"pushed_at":"2019-03-23T18:58:34.000Z","size":211,"stargazers_count":69,"open_issues_count":2,"forks_count":2,"subscribers_count":6,"default_branch":"master","last_synced_at":"2025-08-19T13:24:00.427Z","etag":null,"topics":["build-automation","build-pipelines","build-tool","build-tools","npm-scripts","task-runner","toolkit"],"latest_commit_sha":null,"homepage":"https://scriptype.github.io/salinger/","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/scriptype.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-02-06T18:03:40.000Z","updated_at":"2024-03-26T19:27:48.000Z","dependencies_parsed_at":"2022-09-24T05:51:47.432Z","dependency_job_id":null,"html_url":"https://github.com/scriptype/salinger","commit_stats":null,"previous_names":[],"tags_count":20,"template":false,"template_full_name":null,"purl":"pkg:github/scriptype/salinger","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scriptype%2Fsalinger","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scriptype%2Fsalinger/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scriptype%2Fsalinger/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scriptype%2Fsalinger/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/scriptype","download_url":"https://codeload.github.com/scriptype/salinger/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/scriptype%2Fsalinger/sbom","scorecard":{"id":806454,"data":{"date":"2025-08-11","repo":{"name":"github.com/scriptype/salinger","commit":"79a06c8fadf33649132af39dcccddd8d7891fab2"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":1.7,"checks":[{"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":"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}},{"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":"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":"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":"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":"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: LICENCE:0","Info: FSF or OSI recognized license: MIT License: LICENCE: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":"Vulnerabilities","score":0,"reason":"33 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-6chw-6frg-f759","Warn: Project is vulnerable to: GHSA-v88g-cgmw-v5xw","Warn: Project is vulnerable to: GHSA-93q8-gq69-wqmw","Warn: Project is vulnerable to: GHSA-v6h2-p8h4-qcjw","Warn: Project is vulnerable to: GHSA-4gmj-3p3h-gm8h","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-q42p-pg8m-cqh6","Warn: Project is vulnerable to: GHSA-w457-6q6x-cgp9","Warn: Project is vulnerable to: GHSA-62gr-4qp9-h98f","Warn: Project is vulnerable to: GHSA-f52g-6jhx-586p","Warn: Project is vulnerable to: GHSA-2cf5-4w76-r9qv","Warn: Project is vulnerable to: GHSA-3cqr-58rm-57f8","Warn: Project is vulnerable to: GHSA-g9r4-xpmj-mj65","Warn: Project is vulnerable to: GHSA-q2c6-c6pm-g3gh","Warn: Project is vulnerable to: GHSA-765h-qjxv-5f44","Warn: Project is vulnerable to: GHSA-f2jv-r9rf-7988","Warn: Project is vulnerable to: GHSA-8j8c-7jfh-h6hx","Warn: Project is vulnerable to: GHSA-896r-f27r-55mw","Warn: Project is vulnerable to: GHSA-282f-qqgm-c34q","Warn: Project is vulnerable to: GHSA-jf85-cpcp-j695","Warn: Project is vulnerable to: GHSA-p6mc-m468-83gw","Warn: Project is vulnerable to: GHSA-29mw-wpgm-hmr9","Warn: Project is vulnerable to: GHSA-35jh-r3h4-6jhm","Warn: Project is vulnerable to: GHSA-f8q6-p94x-37v3","Warn: Project is vulnerable to: GHSA-vh95-rmgr-6w4m","Warn: Project is vulnerable to: GHSA-xvch-5gv4-984h","Warn: Project is vulnerable to: GHSA-hj48-42vr-x3v9","Warn: Project is vulnerable to: GHSA-hrpp-h998-j3pp","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-4rq4-32rv-6wp6","Warn: Project is vulnerable to: GHSA-64g7-mvw6-v9qj","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-w5p7-h5w8-2hfq"],"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-23T11:54:07.832Z","repository_id":45752477,"created_at":"2025-08-23T11:54:07.832Z","updated_at":"2025-08-23T11:54:07.832Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":272335684,"owners_count":24916516,"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-08-27T02:00:09.397Z","response_time":76,"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":["build-automation","build-pipelines","build-tool","build-tools","npm-scripts","task-runner","toolkit"],"created_at":"2024-08-02T05:01:04.075Z","updated_at":"2025-08-27T13:19:30.734Z","avatar_url":"https://github.com/scriptype.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"![Salinger](https://github.com/scriptype/salinger/blob/master/salinger.png?raw=true)\n\n\u003e Ecosystem-free task runner that goes well with npm scripts.\n\n[![Travis Status Badge](https://api.travis-ci.org/scriptype/salinger.svg?branch=master)](https://travis-ci.org/scriptype/salinger) [![AppVeyor Status Badge](https://ci.appveyor.com/api/projects/status/6e5tqfcgd3ihlksa?svg=true)](https://ci.appveyor.com/project/scriptype/salinger-npm) [![Coverage Status Badge](https://coveralls.io/repos/github/scriptype/salinger/badge.svg?branch=master)](https://coveralls.io/github/scriptype/salinger?branch=master) [![Code Climate Badge](https://codeclimate.com/github/scriptype/salinger/badges/gpa.svg)](https://codeclimate.com/github/scriptype/salinger) [![bitHound Overall Score Badge](https://www.bithound.io/github/scriptype/salinger/badges/score.svg)](https://www.bithound.io/github/scriptype/salinger)\n\nSalinger is (almost) just a Promise wrapper around the native `fs.exec()` calls. And it replaces your favorite task runner (See: [Trade-offs](#trade-offs)).\n\nEasy to step in, easy to step out. No attachment to the glue modules between the runner and the tools.\n\n![Salinger walk-through](https://cdn.rawgit.com/scriptype/salinger/master/walkthrough.svg)\n\n## Contents\n\n- [What Salinger offers](#what-salinger-offers)\n- [An example with `npm run-scripts` and Salinger](#an-example-with-npm-run-scripts-and-salinger)\n- [Motivation](#motivation)\n- [Install](#install)\n- [Getting started](#getting-started)\n- [Docs](#docs)\n - [Salinger.run()](#salingerrun)\n - [Changing the default `scripts` directory](#changing-the-default-scripts-directory)\n - [Environment variables](#environment-variables)\n- [Trade-offs](#trade-offs)\n- [Windows support](#windows-support)\n- [Contributing](#contributing)\n- [Credits](#credits)\n\n## What Salinger offers\n - A well structured build environment.\n - Write scripts in any of these: `Unix Shell`, `JavaScript`, `Python`, `Ruby`, `Perl`, `Lua`.\n - Use CLI or programmatic API for a given task, whatever suits your needs better.\n - Easily inject variables to the scripts.\n - No ecosystem of plugins to adapt to. Use the core packages.\n - A compact package.json\n - Orchestrate your scripts with promises.\n - Almost non-existent learning curve.\n\n## An example with `npm run-scripts` and Salinger\n\nLet's say we have some scripts in our `package.json`:\n\n```json\n\"scripts\": {\n  \"foo\": \"someCrazyComplicatedStuff \u0026\u0026 anotherComplicatedThingGoesRightHere\",\n  \"bar\": \"aTaskThatRequiresYouToWriteThisLongScriptInOneLine\",\n  \"fooBar\": \"npm run foo \u0026\u0026 npm run bar\"\n}\n```\n\nIf we had used Salinger, the `package.json` would look like this:\n\n```json\n\"scripts\": {\n  \"foo\": \"salinger foo\",\n  \"bar\": \"salinger bar\",\n  \"fooBar\": \"salinger fooBar\"\n}\n```\n\nAnd we would implement chaining logic in `scripts/tasks.js`:\n\n```js\nvar run = require('salinger').run\n\nmodule.exports = {\n  foo() {\n    return run('crazy_complicated')\n      .then(_ =\u003e run('another_complicated'))\n  },\n  bar() {\n    return run('my_long_script')\n  },\n  fooBar() {\n    this.foo().then(this.bar)\n  }\n}\n```\n\n(Normally we wouldn't have to `return` in tasks if we didn't reuse them.)\n\nFinally, we would have the actual scripts in `scripts/tasks/`:\n```\ncrazy_complicated.sh\nanother_complicated.js\nmy_long_script.rb\n```\n\n(Yes, they can be written in any scripting language.)\n\nSo, what did we do here? We have separated the entry points, the orchestration/chaining part and the actual script contents.\n\n- Keep the `package.json` clean and brief, it only has entry points to our build system.\n- Chain the tasks in a more familiar and powerful way, in a fresh environment.\n- Write the actual scripts in whatever way you want. CLI or programmatic; `sh` or `js` or... You decide.\n\n## Motivation\n\nAfter spending some time with npm scripts, problems arise:\n - It's unpleasant reading and writing several long lines of CLI code in the `package.json`. Not eye candy, at best.\n - A json file is apparently not the most comfortable place to write the whole script contents in it. Its syntax rules are prohibitive against writing any complex code in it.\n - The way of creating and using variables is counterintuitive.\n - We can't use the programmatic API of a tool in the `package.json` without:\n   - a) Writing the js code in one line as a parameter to `node -e` (full of backslashes).\n   - b) Creating a separate file for it, which breaks the integrity of script definitions. We have to organize these separate scripts somehow.\n \nAs a general note, an ideal task runner should run _any_ tasks we want it to. Not the _only tasks_ that are compliant with its API.\n\n## Install\n\n```\nnpm install --save-dev salinger\n```\n\n## Getting started\n\nWe have a simple [boilerplate project](https://github.com/scriptype/salinger-basic-boilerplate). It'll surely help to understand better what's going on. Really, [check it out](https://github.com/scriptype/salinger-basic-boilerplate).\n\nSo, let's start a new project and use Salinger in it.\n\nInitialize an empty project:\n```sh\nmkdir test-project\ncd test-project\nnpm init -y\n```\n\nMake sure you've installed Salinger with this:\n\n```sh\nnpm install --save-dev salinger\n```\n\nLet's have a dependency for our project:\n \n```sh\nnpm install --save-dev http-server\n```\n\nAdd a start script in the `package.json` which forwards to our start task:\n\n```json\n\"scripts\": {\n  \"start\": \"salinger start\"\n}\n```\n\nNext, create a folder named `scripts` in the root directory of our project. We'll use this folder as the home directory for Salinger-related things. It will eventually look like this:\n\n```\n├─┬ scripts/\n│ ├── env.js\n│ ├── tasks.js\n│ └─┬ tasks/\n│   └── server.sh\n```\n\nFirst, let's create the `tasks.js` inside the `scripts`:\n\n```js\nvar run = require('salinger').run\n\nmodule.exports = {\n  start() {\n    run('server')\n  }\n}\n```\n\nSo, we have our start task that `npm start` will redirect to. It runs a script called `server`, so let's create it.\n\nCreate a folder named `tasks` inside the `scripts`. This folder will contain all future script files.\n\n```sh\nmkdir scripts/tasks\n```\n\nCreate `server.sh` inside this folder, and copy the below code and save:\n\n```sh\nhttp-server -p $PORT\n```\n\nLast missing part: the script looks for a `PORT` environment variable but we didn't pass it.\n\nCreate `env.js` inside the `scripts` folder:\n\n```js\nconst PORT = process.env.PORT || '8081'\n\nmodule.exports = {\n  PORT\n}\n```\n\nVariables you export from `env.js` is accessible from all scripts, via `process.env`.\n\nLet's check what we got:\n \n```sh\nnpm start\n# starts an http server at 8081\n```\n\nNow that you can add more tasks, that executes different scripts, and chain them together.\n\nAt this point I recommend checking out the [Salinger-Basic Boilerplate](https://github.com/scriptype/salinger-basic-boilerplate) and reading the [docs](#docs) below to explore the possibilities.\n\n## Docs\n\n### Salinger.run()\n\nCurrently being the only member of Salinger's API, run takes two parameters and returns a `Promise`:\n\n - Filename of the script to run. This doesn't include the extension and it's not a path; just the filename. If Salinger finds a file with the supplied filename and a supported extension, it will execute it. Otherwise you'll see errors on your console. \n   \n   If there are multiple files with the same name but different extensions, only one of them will be selected every time (Lookup order is: `sh`, `js`, `py`, `rb`, `pl`, `lua`).\n \n - Optional - Environment variables specific to this run call. See: [Environment variables](#environment-variables).\n\n```js\nrun('do-things', {\n  HELLO: 'world'\n})\n```\n\nYou can chain run calls just like any other `Promise`:\n\n```js\nrun('foo')\n  .then(_ =\u003e run('bar'))\n  .then(_ =\u003e run('bam'))\n```\n\nConcurrently executing scripts is a no-brainer:\n\n```js\nrun('foo')\nrun('bar')\n```\n\nYou can use `Promise.all`, `Promise.race` etc.\n\nOne interesting pattern would be chaining and reusing the exported Salinger tasks:\n\n```js\n// scripts/tasks.js\nvar run = require('salinger').run\n\nmodule.exports = {\n\n  lorem() {\n    return run('foo')\n  },\n\n  ipsum() {\n    return run('bar')\n  },\n  \n  dolor() {\n    this.lorem()\n      .then(_ =\u003e run('grapes', { HERE: 'A_VARIABLE' }))\n      .then(this.ipsum)\n      .then(_ =\u003e run('trek'))\n  }\n}\n```\n\n### Changing the default `scripts` directory\n\nYou can choose to have Salinger-related files in a different folder. If that is the case, just add this config to your `package.json`:\n\n```json\n\"config\": {\n  \"salinger-home\": \"path/to/new-folder\"\n}\n```\n\nNow, you can move everything to that folder and Salinger will start to work with that path. Just be aware that you may need to fix any paths you set in `env.js`.\n\n### Environment variables\n\nThere must be a file named `env.js` in the salinger-home directory. Values exported from this module will be accessible to all tasks through `process.env`. A sample `env.js` may look like this:\n\n```js\nvar path = require('path')\n\nconst SRC = path.join(__dirname, '..', 'src')\nconst DIST = path.join(__dirname, '..', 'dist')\n\nconst PORT = 8080\n\nmodule.exports = {\n  SRC,\n  DIST,\n  PORT\n}\n```\n\nThis will extend the process.env during the execution of the scripts.\n\nAlso, Salinger's run method takes an optional second parameter which also extends process.env with the provided values. But, these values are available only for this specific `run` call. Let's say we run a script from a task, like this:\n\n```js\nmyTask(these, parameters, are, coming, from, CLI) {\n\n  // Maybe do some logic depending on the values of CLI parameters.\n  // ...\n\n  run('my-script', {\n  \n    // Or inject those parameters as environment variables to a script\n    these: these,\n    parameters: parameters,\n    are: are,\n    from: from,\n    CLI: CLI,\n    \n    // Let's pass a variable that conflicts with an existing key in the env.js\n    PORT: 5001\n    \n  })\n  \n}\n```\n\nAnd, of course, add an entry point for this task to package.json:\n\n```json\n\"scripts\": {\n  \"myTask\": \"salinger myTask hello there from planet earth\"\n}\n```\n\nSay, this is a production environment and there's already a `PORT` environment variable independent from all of these. Now when we run `npm run myTask`, that PORT variable will be overridden by 8080, since it's defined in env.js. And, since we specify that variable again, in the second parameter of run call of 'my-script', it gets overriden again just for this execution of 'my-script'. So, `PORT` is 5001 for my-script, only for this call.\n\nWhat's exported from env.js, though, will be accessible from process.env (not persistent) during the execution of all scripts.\n\n## Trade-offs\n\nThis project doesn't claim to be a full-fledged build solution. It helps bringing some consistency and some freedom to the build scripts of projects, especially to the ones that are formerly written with npm run scripts.\n\nSalinger currently doesn't (and, by nature, probably will never) use virtual-fs or streams, which puts it behind the tools like Gulp, in terms of build performance. If your priority is superior build performance, just use Gulp or whatever suits your needs better.\n\nSalinger is more about freedom. It's ecosystem-free, learning-curve-free, provides freedom to choose betwen the CLI and the API. This freedom comes with little to no abstraction. Therefore, it has little to no performance improvements or optimizations.\n\n## Windows support\n\nSalinger may or may not work on `cmd.exe`. Consider using one of these:\n\n - [Git Bash](https://git-scm.com/downloads)\n - [Cygwin](https://cygwin.com/install.html)\n - [Bash on Ubuntu on Windows](https://msdn.microsoft.com/en-us/commandline/wsl/about)\n \n...and everything should be fine.\n\nIf you encounter a problem with Salinger on Windows, please see the [Windows Issues](https://github.com/scriptype/salinger/labels/windows) and open a new one if necessary.\n\n## Contributing\n\nSee: [CONTRIBUTING.md](CONTRIBUTING.md)\n\n## Credits\n\nMany thanks to Ufuk Sarp Selçok ([@ufuksarp](https://twitter.com/ufuksarp)) for the project logo.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscriptype%2Fsalinger","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fscriptype%2Fsalinger","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fscriptype%2Fsalinger/lists"}