{"id":26548677,"url":"https://github.com/izure1/ryoiki","last_synced_at":"2026-02-21T13:01:14.432Z","repository":{"id":263609013,"uuid":"890931664","full_name":"izure1/ryoiki","owner":"izure1","description":"A range-based locking library for JavaScript, enabling concurrent read locks and exclusive write locks with seamless management of overlapping ranges.","archived":false,"fork":false,"pushed_at":"2025-02-28T14:34:20.000Z","size":75,"stargazers_count":1,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-10-09T15:08:59.226Z","etag":null,"topics":["async-lock","concurrency","javascript","lock","locking","range-lock","typescript"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","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/izure1.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"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-11-19T12:40:50.000Z","updated_at":"2025-02-28T14:34:23.000Z","dependencies_parsed_at":"2024-11-19T13:47:38.519Z","dependency_job_id":"f5625dfb-1357-427b-8b42-3f7d6e67f0e8","html_url":"https://github.com/izure1/ryoiki","commit_stats":null,"previous_names":["izure1/ryoiki"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/izure1/ryoiki","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izure1%2Fryoiki","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izure1%2Fryoiki/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izure1%2Fryoiki/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izure1%2Fryoiki/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/izure1","download_url":"https://codeload.github.com/izure1/ryoiki/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/izure1%2Fryoiki/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":29681468,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-02-21T12:30:22.644Z","status":"ssl_error","status_checked_at":"2026-02-21T12:29:55.402Z","response_time":107,"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":["async-lock","concurrency","javascript","lock","locking","range-lock","typescript"],"created_at":"2025-03-22T06:25:58.072Z","updated_at":"2026-02-21T13:01:14.413Z","avatar_url":"https://github.com/izure1.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Ryoiki\r\n\r\n[![](https://data.jsdelivr.com/v1/package/npm/ryoiki/badge)](https://www.jsdelivr.com/package/npm/ryoiki)\r\n![Node.js workflow](https://github.com/izure1/ryoiki/actions/workflows/node.js.yml/badge.svg)\r\n\r\n`Ryoiki` is a JavaScript mutex library that provides read/write locks based on specific ranges.  \r\nThis library helps maintain consistency and stability by controlling concurrent access to data.\r\n\r\nThis library supports **Async** and **TypeScript**.\r\n\r\n## 📦 Installation\r\n\r\n```bash\r\nnpm install ryoiki\r\n```\r\n\r\nor\r\n\r\n```typescript\r\nimport { Ryoiki } from 'https://cdn.jsdelivr.net/npm/ryoiki@1/+esm'\r\n```\r\n\r\n## 🚀 Usage\r\n\r\n### Basic Example\r\n\r\n```typescript\r\nimport { Ryoiki } from 'ryoiki'\r\n\r\nconst ryoiki = new Ryoiki()\r\n\r\nlet lockId: string; // Store lock ID externally\r\nawait ryoiki.readLock([0, 10], async (_lockId) =\u003e {\r\n  lockId = _lockId\r\n  console.log('Reading from range [0, 10]')\r\n}).finally(() =\u003e ryoiki.readUnlock(lockId)) // Always unlock\r\n```\r\n\r\n### Key Concepts\r\n\r\n#### 1. **Default Lock Range**\r\n\r\n- If the first parameter of `readLock` or `writeLock` is omitted, it defaults to `[-Infinity, Infinity]`.\r\n\r\n- Example:\r\n\r\n  ```typescript\r\n  let lockId: string\r\n  await ryoiki.readLock(async (_lockId) =\u003e {\r\n    lockId = _lockId\r\n    console.log('Read lock applied to the entire range')\r\n  }).finally(() =\u003e ryoiki.readUnlock(lockId))\r\n  ```\r\n\r\n#### 2. **Lock Waiting Behavior**\r\n\r\n- **Read Lock**:\r\n  - Can execute immediately if overlapping with other read locks.\r\n  - Waits if overlapping with a write lock.\r\n- **Write Lock**:\r\n  - Waits if overlapping with other read or write locks.\r\n\r\n#### 3. **Timeout Behavior**\r\n\r\n- Both `readLock` and `writeLock` now support an optional `timeout` parameter.\r\n- **Timeout**: The lock request will wait for the specified time in milliseconds before throwing an error if the lock cannot be acquired.\r\n  - Defaults to `Infinity`, meaning it will wait indefinitely unless otherwise specified.\r\n  - If the lock cannot be acquired within the given `timeout` period, a timeout error is thrown.\r\n\r\n#### 4. **Unlocking**\r\n\r\n- Always use `finally` to release locks, even if an error occurs in the callback.\r\n- Correct Usage:\r\n\r\n  ```typescript\r\n  let lockId: string\r\n  await ryoiki.writeLock([0, 10], async (_lockId) =\u003e {\r\n    lockId = _lockId\r\n    throw new Error('Exception occurred')\r\n  }).finally(() =\u003e ryoiki.writeUnlock(lockId)) // Always unlock\r\n  ```\r\n\r\n#### 5. **Locks, Deadlocks, and Caution**\r\n\r\n- **`readLock`** and **`writeLock`** are used to manage access to data by locking specific ranges.  \r\n  - A **read lock** allows multiple readers but waits if a write lock exists.\r\n  - A **write lock** prevents both read and write locks in the same range, ensuring exclusive access.\r\n\r\n- **Deadlock** occurs when two or more processes are unable to proceed because each is waiting for the other to release a lock.  \r\n  In the context of `Ryoiki`, this could happen if:\r\n  - A `readLock` is waiting for a `writeLock` to release, and the `writeLock` is waiting for a `readLock` to release.\r\n  \r\n  To prevent deadlocks:\r\n  - Ensure that locks are always released as soon as they are no longer needed.\r\n  - Use `timeout` to avoid waiting indefinitely.\r\n\r\n- For a deeper understanding of these concepts, you can refer to these Wikipedia articles:\r\n  - [Read-Write Lock](https://en.wikipedia.org/wiki/Readers%E2%80%93writer_lock)\r\n  - [Deadlock](https://en.wikipedia.org/wiki/Deadlock)\r\n\r\n## 📖 API\r\n\r\n### `readLock(range?: [number, number], callback: (lockId: string) =\u003e Promise\u003cT\u003e, timeout?: number): Promise\u003cT\u003e`\r\n\r\n- **Description**: Requests a read lock for the specified range.\r\n- **Parameters**:\r\n  - `range`: Range to lock as `[start, end]`. Defaults to `[-Infinity, Infinity]`.\r\n  - `callback`: Async function executed after lock is acquired.\r\n    - `lockId`: Unique ID for the current lock.\r\n  - `timeout`: Optional timeout in milliseconds. If the lock cannot be acquired within the specified time, the operation will throw a timeout error.\r\n    - Defaults to `undefined` (wait indefinitely).\r\n- **Returns**: The result of the callback function.\r\n\r\n### `writeLock(range?: [number, number], callback: (lockId: string) =\u003e Promise\u003cT\u003e, timeout?: number): Promise\u003cT\u003e`\r\n\r\n- **Description**: Requests a write lock for the specified range.\r\n- **Parameters**:\r\n  - `range`: Range to lock as `[start, end]`. Defaults to `[-Infinity, Infinity]`.\r\n  - `callback`: Async function executed after lock is acquired.\r\n    - `lockId`: Unique ID for the current lock.\r\n  - `timeout`: Optional timeout in milliseconds. If the lock cannot be acquired within the specified time, the operation will throw a timeout error.\r\n    - Defaults to `undefined` (wait indefinitely).\r\n- **Returns**: The result of the callback function.\r\n\r\n### `readUnlock(lockId: string): void`\r\n\r\n- **Description**: Releases a read lock.\r\n\r\n### `writeUnlock(lockId: string): void`\r\n\r\n- **Description**: Releases a write lock.\r\n\r\n### `range(start: number, length: number): [number, number]`\r\n\r\n- **Description**: Creates a tuple `[start, start + length]` based on the given start and length.\r\n- **Usage Example**:\r\n\r\n  ```typescript\r\n  const r = ryoiki.range(0, 10) // [0, 10]\r\n  const s = ryoiki.range(5, 10) // [5, 15]\r\n  ```\r\n\r\n## 🌟 Examples\r\n\r\n### Read and Write Operations with Timeout\r\n\r\n```typescript\r\nconst ryoiki = new Ryoiki()\r\n\r\nlet lockId: string\r\n\r\n// Read Lock with timeout\r\nawait ryoiki.readLock([0, 10], async (_lockId) =\u003e {\r\n  lockId = _lockId\r\n  console.log('Reading from range [0, 10]')\r\n}, 1000).finally(() =\u003e ryoiki.readUnlock(lockId)) // Always unlock\r\n\r\n// Write Lock with timeout\r\nawait ryoiki.writeLock([5, 15], async (_lockId) =\u003e {\r\n  lockId = _lockId\r\n  console.log('Writing to range [5, 15]')\r\n}, 1000).finally(() =\u003e ryoiki.writeUnlock(lockId)) // Always unlock\r\n```\r\n\r\n## 📜 License\r\n\r\nMIT License. Feel free to use and contribute! 🙌\r\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fizure1%2Fryoiki","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fizure1%2Fryoiki","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fizure1%2Fryoiki/lists"}