{"id":28697594,"url":"https://github.com/edrlab/r2-streamer-js","last_synced_at":"2026-06-03T07:13:39.477Z","repository":{"id":19285077,"uuid":"86689499","full_name":"edrlab/r2-streamer-js","owner":"edrlab","description":"NodeJS Readium2 \"streamer\"","archived":false,"fork":false,"pushed_at":"2025-06-14T09:33:32.000Z","size":5112,"stargazers_count":24,"open_issues_count":21,"forks_count":10,"subscribers_count":12,"default_branch":"develop","last_synced_at":"2026-02-07T09:52:41.266Z","etag":null,"topics":["ebooks","epub","javascript","nodejs","opds","readium","readium-2","typescript"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-3-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/edrlab.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2017-03-30T10:20:25.000Z","updated_at":"2025-11-07T18:48:19.000Z","dependencies_parsed_at":"2024-05-30T18:43:11.154Z","dependency_job_id":"b231f48d-1f2a-447f-8dc1-3f3ca85c872e","html_url":"https://github.com/edrlab/r2-streamer-js","commit_stats":null,"previous_names":["edrlab/r2-streamer-js","readium/r2-streamer-js"],"tags_count":58,"template":false,"template_full_name":null,"purl":"pkg:github/edrlab/r2-streamer-js","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edrlab%2Fr2-streamer-js","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edrlab%2Fr2-streamer-js/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edrlab%2Fr2-streamer-js/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edrlab%2Fr2-streamer-js/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/edrlab","download_url":"https://codeload.github.com/edrlab/r2-streamer-js/tar.gz/refs/heads/develop","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/edrlab%2Fr2-streamer-js/sbom","scorecard":{"id":367068,"data":{"date":"2025-08-11","repo":{"name":"github.com/edrlab/r2-streamer-js","commit":"97b8a0967d514f95228cb909a6764416fa4cea11"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":2,"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/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"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":"Maintained","score":1,"reason":"2 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 1","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":"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":"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":"Pinned-Dependencies","score":3,"reason":"dependency not pinned by hash detected -- score normalized to 3","details":["Warn: containerImage not pinned by hash: Dockerfile:1: pin your Docker image by updating node:20 to node:20@sha256:08535d63bb457126292492a7998feda843cc7094abefafa5ae2c00fe35b77958","Warn: npmCommand not pinned by hash: Dockerfile:26-30","Info:   0 out of   1 containerImage dependencies pinned","Info:   1 out of   2 npmCommand dependencies pinned"],"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":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: BSD 3-Clause \"New\" or \"Revised\" License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"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":"Branch-Protection","score":0,"reason":"branch protection not enabled on development/release branches","details":["Warn: branch protection not enabled for branch 'develop'"],"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":"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":0,"reason":"20 existing vulnerabilities detected","details":["Warn: Project is vulnerable to: GHSA-4w2v-q235-vp99","Warn: Project is vulnerable to: GHSA-cph5-m8f7-6c5x","Warn: Project is vulnerable to: GHSA-wf5p-g6vw-rhxx","Warn: Project is vulnerable to: GHSA-jr5f-v2jv-69x6","Warn: Project is vulnerable to: GHSA-3xgq-45jj-v275","Warn: Project is vulnerable to: GHSA-74fj-2j2h-c42q","Warn: Project is vulnerable to: GHSA-pw2r-vq6v-hr8c","Warn: Project is vulnerable to: GHSA-jchw-25xp-jwwc","Warn: Project is vulnerable to: GHSA-cxjh-pqwp-8mfp","Warn: Project is vulnerable to: GHSA-fjxv-7rqg-78g4","Warn: Project is vulnerable to: GHSA-vfrc-7r7c-w9mx","Warn: Project is vulnerable to: GHSA-7wwv-vh3v-89cq","Warn: Project is vulnerable to: GHSA-4xcv-9jjx-gfj3","Warn: Project is vulnerable to: GHSA-76c9-3jph-rj3q","Warn: Project is vulnerable to: GHSA-h7cp-r72f-jxh6","Warn: Project is vulnerable to: GHSA-v62p-rq8g-8h59","Warn: Project is vulnerable to: GHSA-p8p7-x288-28g6","Warn: Project is vulnerable to: GHSA-52f5-9888-hmc6","Warn: Project is vulnerable to: GHSA-72xf-g2v4-qvf3","Warn: Project is vulnerable to: GHSA-p9pc-299p-vxgp"],"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-18T12:04:07.223Z","repository_id":19285077,"created_at":"2025-08-18T12:04:07.223Z","updated_at":"2025-08-18T12:04:07.223Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33852482,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-03T02:00:06.370Z","response_time":59,"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":["ebooks","epub","javascript","nodejs","opds","readium","readium-2","typescript"],"created_at":"2025-06-14T10:04:12.849Z","updated_at":"2026-06-03T07:13:39.458Z","avatar_url":"https://github.com/edrlab.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# NodeJS / TypeScript Readium-2 \"streamer\"\n\nNodeJS implementation (written in TypeScript) and HTTP micro-services (Express middleware) for https://github.com/readium/architecture/tree/master/streamer\n\n[![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](/LICENSE)\n\n## Build status\n\n[![NPM](https://img.shields.io/npm/v/r2-streamer-js.svg)](https://www.npmjs.com/package/r2-streamer-js) [![David](https://david-dm.org/readium/r2-streamer-js/status.svg)](https://david-dm.org/readium/r2-streamer-js) [![Travis](https://travis-ci.org/readium/r2-streamer-js.svg?branch=develop)](https://travis-ci.org/readium/r2-streamer-js)\n\n[Changelog](/CHANGELOG.md)\n\n## Prerequisites\n\n1) https://nodejs.org NodeJS \u003e= 8, NPM \u003e= 5 (check with command line `node --version` and `npm --version`)\n2) OPTIONAL: https://yarnpkg.com Yarn \u003e= 1.0 (check with command line `yarn --version`)\n\n## GitHub repository\n\nhttps://github.com/readium/r2-streamer-js\n\nThere is no [github.io](https://readium.github.io/r2-streamer-js) site for this project (no [gh-pages](https://github.com/readium/r2-streamer-js/tree/gh-pages) branch).\n\n## NPM package\n\nhttps://www.npmjs.com/package/r2-streamer-js\n\nCommand line install:\n\n`npm install r2-streamer-js`\nOR\n`yarn add r2-streamer-js`\n\n...or manually add in your `package.json`:\n```json\n  \"dependencies\": {\n    \"r2-streamer-js\": \"latest\"\n  }\n```\n\nThe JavaScript code distributed in the NPM package is usable as-is (no transpilation required), as it is automatically-generated from the TypeScript source.\n\nSeveral ECMAScript flavours are provided out-of-the-box: ES5, ES6-2015, ES7-2016, ES8-2017:\n\nhttps://unpkg.com/r2-streamer-js/dist/\n\n(alternatively, GitHub mirror with semantic-versioning release tags: https://github.com/edrlab/r2-streamer-js-dist/tree/develop/dist/ )\n\nThe JavaScript code is not bundled, and it uses `require()` statement for imports (NodeJS style).\n\nMore information about NodeJS compatibility:\n\nhttp://node.green\n\nNote that web-browser Javascript is currently not supported (only NodeJS runtimes).\n\nThe type definitions (aka \"typings\") are included as `*.d.ts` files in `./node_modules/r2-streamer-js/dist/**`, so this package can be used directly in a TypeScript project.\n\nExample usage:\n\n```javascript\n// from the index file\nimport { Server } from \"r2-streamer-js\";\n\n// ES5 import (assuming node_modules/r2-streamer-js/):\nimport { Server } from \"r2-streamer-js/dist/es5/src/http/server\";\n\n// ... or alternatively using a convenient path alias in the TypeScript config (+ WebPack etc.):\nimport { Server } from \"@r2-streamer-js/http/server\";\n```\n\n## Dependencies\n\nhttps://david-dm.org/readium/r2-streamer-js\n\nA [package-lock.json](https://github.com/readium/r2-streamer-js/blob/develop/package-lock.json) is provided (modern NPM replacement for `npm-shrinkwrap.json`).\n\nA [yarn.lock](https://github.com/readium/r2-streamer-js/blob/develop/yarn.lock) file is currently *not* provided at the root of the source tree.\n\n## Continuous Integration\n\nhttps://travis-ci.org/readium/r2-streamer-js\n\nTravisCI builds are triggered automatically at every Git \"push\" in the `develop` branch.\n\n## Live demos\n\nA test server app (not production-ready) is automatically deployed at every Git \"push\" in the `develop` branch:\n\nhttps://streamer.edrlab.org\n\n## Version(s), Git revision(s)\n\nNPM package (latest published):\n\nhttps://unpkg.com/r2-streamer-js/dist/gitrev.json\n\nAlternatively, GitHub mirror with semantic-versioning release tags:\n\nhttps://raw.githack.com/edrlab/r2-streamer-js-dist/develop/dist/gitrev.json\n\nLatest deployment:\n\nhttps://streamer.edrlab.org/version\n\n## Developer quick start\n\nCommand line steps (NPM, but similar with YARN):\n\n1) `cd r2-streamer-js`\n2) `git status` (please ensure there are no local changes, especially in `package-lock.json` and the dependency versions in `package.json`)\n3) `rm -rf node_modules` (to start from a clean slate)\n4) `npm install`, or alternatively `npm ci` (both commands initialize the `node_modules` tree of package dependencies, based on the strict `package-lock.json` definition)\n5) `npm run build:all` (invoke the main build script: clean, lint, compile)\n6) `ls dist` (that's the build output which gets published as NPM package)\n7) `npm run server-debug -- PATH_TO_EPUB_OR_DIR \" -1\"` (ES8-2017 dist, path is relative or absolute, -1 means no limits for HTTP header prefetch Links)\n8) or: `npm run start -- 99` (ES6-2015 dist, default `./misc/epubs` folder, the 99 value overrides the default maximum number of HTTP header prefetch Links)\n\n## Documentation\n\n### Basic usage\n\n```javascript\n// ES5 import (assuming node_modules/r2-streamer-js/):\nimport { Server } from \"r2-streamer-js/dist/es5/src/http/server\";\n\n// ... or alternatively using a convenient path alias in the TypeScript config (+ WebPack etc.):\nimport { Server } from \"@r2-streamer-js/http/server\";\n\n// Constructor parameter is optional:\n// disableDecryption: true\n// disableOPDS\n// disableReaders: true\n// disableRemotePubUrl: true to deactivate\nconst server = new Server({\n  disableDecryption: false, // deactivates the decryption of encrypted resources (Readium LCP).\n  disableOPDS: true, // deactivates the HTTP routes for the OPDS \"micro services\" (browser, converter)\n  disableReaders: true, // deactivates the built-in \"readers\" for ReadiumWebPubManifest (HTTP static host / route).\n  disableRemotePubUrl: true, // deactivates the HTTP route for loading a remote publication.\n  maxPrefetchLinks: 5, // Link HTTP header, with rel = prefetch, see server.ts MAX_PREFETCH_LINKS (default = 10)\n});\n\n// First parameter: port number, zero means default (3000),\n// unless specified via the environment variable `PORT` (process.env.PORT).\n// Tip: the NPM package `portfinder` can be used to automatically find an available port number.\nconst url = await server.start(3000, false);\n\n// Second constructor parameter: if true, HTTPS instead of HTTP, using a randomly-generated self-signed certificate.\n// Also validates encrypted HTTP header during request-request cycles, so should only be used in runtime\n// contexts where the client side has access to the private encryption key (i.e. Electron app, see r2-navigator-js)\nconsole.log(server.isSecured()); // false\n\n// A client app that is capable of setting HTTP headers for every request originating from content webviews\n// can obtain the special encrypted header using this function:\n// (as used internally by the Electron-based `r2-navigator-js` component to secure the transport layer)\nconst nameValuePair = server.getSecureHTTPHeader(url + \"/PATH_TO_RESOURCE\");\nconsole.log(nameValuePair.name);\nconsole.log(nameValuePair.value);\n\n// http://127.0.0.1:3000\n// Note that ports 80 and 443 (HTTPS) are always implicit (ommitted).\nconsole.log(url);\n\n// `serverInfo.urlScheme` (\"http\")\n// `serverInfo.urlHost` (\"127.0.0.1\")\n// `serverInfo.urlPort` (3000)\nconsole.log(server.serverInfo());\n\n// Calls `uncachePublications()` (see below)\nserver.stop();\n\nconsole.log(server.isStarted()); // false\n```\n\nTo serve a `/robots.txt` file that completely disables search robots:\n\n```javascript\n// Call this before `server.start()`\nserver.preventRobots();\n```\n\nTo add custom HTTP routes:\n\n```javascript\n// Call these before `server.start()`.\n// They are equivalent to `app.use()` and `app.get()`, where `app` is the underlying Express instance:\n\nserver.expressUse(\"/static-files\", express.static(\"/path/to/files\", {\n  dotfiles: \"ignore\",\n  etag: true,\n  fallthrough: false,\n  immutable: true,\n  index: false,\n  maxAge: \"1d\",\n  redirect: false,\n}));\n\nserver.expressGet([\"/hello.html\"], (req: express.Request, res: express.Response) =\u003e {\n\n  // Optionally, to add permissive CORS headers to the HTTP response\n  server.setResponseCORS(res);\n\n  res.status(200).send(\"\u003chtml\u003e\u003cbody\u003eHello\u003c/body\u003e\u003c/html\u003e\");\n});\n```\n\nTo register publications references (local filesystem paths) inside the internal server state\n(which is used to create the OPDS2 feed, see below):\n\n```javascript\n// This can be called before or after `server.start()`:\n\n// the returned array contains URL routes to the ReadiumWebPubManifests,\n// e.g. `/pub/ID/manifest.json`, where `ID` is the base64 encoding of the registered path.\n// Note that the returned base64 URL path components are already URI-encoded (escaped).\n// (`=` and `/` are typically problematic edge-cases)\nconst publicationURLs = server.addPublications([\"/path/to/book.epub\"]);\n\n// ...then:\nconst publicationPaths = server.getPublications(); // [\"/path/to/book.epub\"]\n\n// ...and (calls `uncachePublication()`, see below):\nconst publicationURLs = server.removePublications([\"/path/to/book.epub\"]);\n```\n\nTo get the OPDS2 feed for the currently registered publications:\n\n```javascript\n// This launches a potentially time-consuming Node process that scans (loads) each registered Publication,\n// and stores the generated OPDS2 feed inside a temporary filesystem location.\n// So this returns `undefined` at the first call, and the client must invoke the function again later.\n// Note that both `addPublications()` and `removePublications()` clear the OPDS2 feed entirely,\n// requiring its subsequent re-generation (full scan of registered publication paths).\n// (poor design, but at this stage really just an OPDS2 demo without real use-case)\nconst opds2 = server.publicationsOPDS();\n```\n\nTo actually load+parse a publication reference (local filesystem path) into a ReadiumWebPubManifest\nPublication instance, stored in the server's state:\n\n```javascript\n// The Publication object model is defined in `r2-shared-js`\nconst publication = await server.loadOrGetCachedPublication(\"/path/to/book.epub\");\n\n// The above is basically a lazy-loader that checks the cache before loading+parsing a publication,\n// equivalent to:\nconst publication = server.cachedPublication(\"/path/to/book.epub\");\nif (!publication) {\n  publication = ...; // load and parse \"/path/to/book.epub\"\n  server.cachePublication(\"/path/to/book.epub\", publication);\n}\n\nconsole.log(server.isPublicationCached(\"/path/to/book.epub\")); // true\n\n// see also:\n// (calls `publication.freeDestroy()` to cleanup allocated objects in the Publication,\n// particularly the file handle to the underlying zip/EPUB/CBZ file)\nserver.uncachePublication(\"/path/to/book.epub\");\nserver.uncachePublications();\n```\n\nNote that HTTP/remote publications URLs can be loaded into the server's cache\nand subsequently served by the streamer without prior registration via `addPublications()`.\nHowever, publications from the local filesytem will only be served when registered,\neven if they are cached (in other words, the HTTP route is disabled when the publication is non-registered).\n\n### HTTP API (built-in routes / micro-services)\n\n[docs/http.md](/docs/http.md)\n\n### Support for remote publications\n\n[docs/remote-epub.md](/docs/remote-epub.md)\n\n### Support for OPDS feeds\n\n[docs/opds.md](/docs/opds.md)\n\n### Support for encrypted content\n\n[docs/encryption.md](/docs/encryption.md)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedrlab%2Fr2-streamer-js","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fedrlab%2Fr2-streamer-js","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fedrlab%2Fr2-streamer-js/lists"}