{"id":15190267,"url":"https://github.com/beenotung/userscript-helpers","last_synced_at":"2026-01-24T21:17:22.912Z","repository":{"id":257789876,"uuid":"861070943","full_name":"beenotung/userscript-helpers","owner":"beenotung","description":"Helper functions for userscript with server connection.","archived":false,"fork":false,"pushed_at":"2024-11-01T23:52:26.000Z","size":22,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-10-03T06:27:53.630Z","etag":null,"topics":["express","proxy","typescript","userscript"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/userscript-helpers","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"bsd-2-clause","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/beenotung.png","metadata":{"files":{"readme":"README.md","changelog":null,"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}},"created_at":"2024-09-21T23:23:06.000Z","updated_at":"2024-12-08T02:33:53.000Z","dependencies_parsed_at":"2025-02-12T15:44:08.826Z","dependency_job_id":"05f171fe-0735-494a-80ad-24019252983e","html_url":"https://github.com/beenotung/userscript-helpers","commit_stats":null,"previous_names":["beenotung/userscript-helpers"],"tags_count":6,"template":false,"template_full_name":null,"purl":"pkg:github/beenotung/userscript-helpers","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beenotung%2Fuserscript-helpers","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beenotung%2Fuserscript-helpers/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beenotung%2Fuserscript-helpers/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beenotung%2Fuserscript-helpers/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/beenotung","download_url":"https://codeload.github.com/beenotung/userscript-helpers/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/beenotung%2Fuserscript-helpers/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":28737043,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-01-24T19:23:36.361Z","status":"ssl_error","status_checked_at":"2026-01-24T19:23:28.966Z","response_time":89,"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":["express","proxy","typescript","userscript"],"created_at":"2024-09-27T20:21:25.381Z","updated_at":"2026-01-24T21:17:22.907Z","avatar_url":"https://github.com/beenotung.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# userscript-helpers\n\nHelper functions for userscript with server connection.\n\n[![npm Package Version](https://img.shields.io/npm/v/userscript-helpers)](https://www.npmjs.com/package/userscript-helpers)\n\n## Features\n\n- Using child window to proxy connection to custom server\n- Typescript support\n\n## Installation\n\n```bash\nnpm install userscript-helpers\n```\n\nYou can also install `userscript-helpers` with [pnpm](https://pnpm.io/), [yarn](https://yarnpkg.com/), or [slnpm](https://github.com/beenotung/slnpm)\n\n## Usage Example\n\nDetails see [server.ts](./example/server.ts) and [userscript.ts](./example/userscript.ts)\n\nIn express server:\n\n```typescript\nimport { attachFrameRoute } from 'userscript-helpers'\nimport express from 'express'\n\nlet app = express()\n\nattachFrameRoute(app)\napp.use(express.json())\napp.use(express.urlencoded({ extended: false }))\n\nlet urls = new Set\u003cstring\u003e()\n\napp.post('/img', (req, res) =\u003e {\n  let url = req.body.url\n  urls.add(url)\n  res.json({ count: urls.size })\n})\n\napp.listen(8100)\n```\n\nIn userscript:\n\n```typescript\nimport { setupFrame, sleep } from 'userscript-helpers'\n\nasync function main() {\n  let frame = await setupFrame({\n    api_origin: 'http://localhost:8100',\n  })\n  let imgs = new Set\u003cHTMLImageElement\u003e()\n  frame.startLoop({\n    async loop() {\n      let new_imgs = document.querySelectorAll('img')\n      for (let img of new_imgs) {\n        let url = img.src\n        if (!url || imgs.has(img)) continue\n        img.scrollIntoView({ behavior: 'smooth' })\n        await sleep(500)\n        let json = await frame.fetchJSON('POST', '/img', { url })\n        console.log('post img result:', json)\n        imgs.add(img)\n      }\n    },\n  })\n}\n\nmain().catch(e =\u003e console.error(e))\n```\n\n## Typescript Signature\n\n**Server-side functions**:\n\n```typescript\nexport function attachFrameRoute(app: Router | Application): void\n```\n\n**Client-side functions for userscript**:\n\n```typescript\nexport function setupFrame(options: {\n  api_origin: string\n  /**\n   * @description overwrite `console.log()` and `console.error()` by `console.debug()`\n   */\n  wrap_console?: boolean\n}): Promise\u003c{\n  proxyWindowPromise: Promise\u003cWindow\u003e\n  callAPI: \u003cT\u003e(url: string, init: RequestInit) =\u003e Promise\u003cT\u003e\n  fetchJSON: \u003cT\u003e(method: string, url: string, body?: object) =\u003e Promise\u003cT\u003e\n  postForm: \u003cT\u003e(url: string, formData: FormData) =\u003e Promise\u003cT\u003e\n  startLoop: (options: {\n    loop(): LoopResult | Promise\u003cLoopResult\u003e\n    /**\n     * @description default 1000\n     */\n    loop_interval?: number\n    /**\n     * @description default 3500\n     */\n    error_retry_interval?: number\n  }) =\u003e void\n  isCurrentIteration: () =\u003e boolean\n  stopIteration: (context?: string) =\u003e void\n}\u003e\n\nexport type LoopResult = void | 'stop'\n\nexport declare function imageToDataUrl(\n  img: HTMLImageElement,\n  /** @description default is as-is for image with dataUrl, and \"image/png\" for image with src */\n  mimeType?: ImageMimeType,\n  /** @description between 0 and 1 */\n  quality?: number,\n): Promise\u003cstring\u003e\n\nexport type ImageMimeType = 'image/png' | 'image/jpeg' | 'image/webp'\n```\n\n**Additional helper functions for any side**:\n\n```typescript\n/**\n * @description async version of setTimeout\n */\nexport function sleep(ms: number): Promise\u003cvoid\u003e\n\n/**\n * @returns mimetype, e.g. \"image/webp\"\n */\nexport function dataUrlToMimeType(dataUrl: string): string\n\n/**\n * @return extname without \".\", e.g. \"webp\"\n */\nexport function dataUrlToExtname(dataUrl: string): string\n```\n\n## License\n\nThis project is licensed with [BSD-2-Clause](./LICENSE)\n\nThis is free, libre, and open-source software. It comes down to four essential freedoms [[ref]](https://seirdy.one/2021/01/27/whatsapp-and-the-domestication-of-users.html#fnref:2):\n\n- The freedom to run the program as you wish, for any purpose\n- The freedom to study how the program works, and change it so it does your computing as you wish\n- The freedom to redistribute copies so you can help others\n- The freedom to distribute copies of your modified versions to others\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeenotung%2Fuserscript-helpers","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbeenotung%2Fuserscript-helpers","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbeenotung%2Fuserscript-helpers/lists"}