{"id":19251977,"url":"https://github.com/jiangjie/happy-opfs","last_synced_at":"2026-01-20T11:02:12.732Z","repository":{"id":236970245,"uuid":"793532871","full_name":"JiangJie/happy-opfs","owner":"JiangJie","description":"A browser-compatible fs module inspired by the Deno fs and @std/fs APIs, based on OPFS implementation.","archived":false,"fork":false,"pushed_at":"2025-07-31T07:31:45.000Z","size":723,"stargazers_count":18,"open_issues_count":2,"forks_count":3,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-09-03T05:17:58.992Z","etag":null,"topics":["download","file","fs","opfs","origin","private","system","unzip","upload","zip"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/JiangJie.png","metadata":{"files":{"readme":"README.cn.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-04-29T12:01:01.000Z","updated_at":"2025-08-29T07:28:34.000Z","dependencies_parsed_at":"2024-04-29T13:29:42.028Z","dependency_job_id":"7a302af7-6d35-4192-8ee3-b38fb40bd9d9","html_url":"https://github.com/JiangJie/happy-opfs","commit_stats":null,"previous_names":["jiangjie/happy-opfs"],"tags_count":44,"template":false,"template_full_name":null,"purl":"pkg:github/JiangJie/happy-opfs","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JiangJie%2Fhappy-opfs","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JiangJie%2Fhappy-opfs/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JiangJie%2Fhappy-opfs/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JiangJie%2Fhappy-opfs/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/JiangJie","download_url":"https://codeload.github.com/JiangJie/happy-opfs/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/JiangJie%2Fhappy-opfs/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279017499,"owners_count":26086085,"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-13T02:00:06.723Z","response_time":61,"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":["download","file","fs","opfs","origin","private","system","unzip","upload","zip"],"created_at":"2024-11-09T18:24:51.669Z","updated_at":"2026-01-20T11:02:12.678Z","avatar_url":"https://github.com/JiangJie.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# happy-opfs\n\n[![License](https://img.shields.io/npm/l/happy-opfs.svg)](LICENSE)\n[![Build Status](https://github.com/JiangJie/happy-opfs/actions/workflows/test.yml/badge.svg)](https://github.com/JiangJie/happy-opfs/actions/workflows/test.yml)\n[![codecov](https://codecov.io/gh/JiangJie/happy-opfs/graph/badge.svg)](https://codecov.io/gh/JiangJie/happy-opfs)\n[![NPM version](https://img.shields.io/npm/v/happy-opfs.svg)](https://npmjs.org/package/happy-opfs)\n[![NPM downloads](https://badgen.net/npm/dm/happy-opfs)](https://npmjs.org/package/happy-opfs)\n[![JSR Version](https://jsr.io/badges/@happy-js/happy-opfs)](https://jsr.io/@happy-js/happy-opfs)\n[![JSR Score](https://jsr.io/badges/@happy-js/happy-opfs/score)](https://jsr.io/@happy-js/happy-opfs/score)\n\n基于 [OPFS](https://developer.mozilla.org/zh-CN/docs/Web/API/File_System_API/Origin_private_file_system) 的浏览器文件系统模块，提供 [Deno](https://deno.land/api#File_System) 风格的 API。\n\n---\n\n[English](README.md) | [API 文档](https://jiangjie.github.io/happy-opfs/)\n\n---\n\n## 为什么选择 happy-opfs\n\n标准的 OPFS API 与我们熟悉的基于路径的文件系统 API（如 Node.js 和 Deno）差异较大。本库通过在浏览器中提供 Deno 风格的 API 来弥合这一差距。\n\n所有异步 API 返回 [Result](https://github.com/JiangJie/happy-rusty) 类型（类似 Rust），提供更好的错误处理体验。\n\n## 安装\n\n```sh\npnpm add happy-opfs\n# 或\nnpm install happy-opfs\n# 或\nyarn add happy-opfs\n# 或通过 JSR\njsr add @happy-js/happy-opfs\n```\n\n\u003e [!NOTE]\n\u003e 本项目依赖 JSR 的 `@std/path`，需要在 `.npmrc` 中添加：\n\u003e ```\n\u003e @jsr:registry=https://npm.jsr.io\n\u003e ```\n\n## 功能\n\n| 分类 | API |\n|------|-----|\n| **核心** | `createFile`, `mkdir`, `readDir`, `readFile`, `writeFile`, `remove`, `stat` |\n| **扩展** | `appendFile`, `copy`, `move`, `exists`, `emptyDir`, `readTextFile`, `readBlobFile`, `readJsonFile`, `writeJsonFile` |\n| **流式** | `readFile` 配合 `{ encoding: 'stream' }`, `openWritableFileStream` |\n| **临时文件** | `mkTemp`, `generateTempPath`, `pruneTemp`, `deleteTemp` |\n| **压缩** | `zip`, `unzip`, `zipFromUrl`, `unzipFromUrl` |\n| **网络** | `downloadFile`, `uploadFile` |\n| **同步** | 所有核心操作都有同步版本（如 `mkdirSync`, `readFileSync`），通过 Web Workers 实现。使用 `SyncChannel.connect`, `SyncChannel.listen`, `SyncChannel.attach`, `SyncChannel.isReady` 进行设置 |\n\n## 示例\n\n本地运行示例：\n\n```sh\npnpm run eg\n# 打开 https://localhost:5173\n```\n\n### 快速开始\n\n```ts\nimport { mkdir, writeFile, readTextFile, remove } from 'happy-opfs';\n\n// 写入和读取文件\nawait mkdir('/data');\nawait writeFile('/data/hello.txt', 'Hello, OPFS!');\n\n(await readTextFile('/data/hello.txt')).inspect((content) =\u003e {\n    console.log(content); // 'Hello, OPFS!'\n});\n\nawait remove('/data');\n```\n\n更多示例请参阅 [examples/](./examples/) 目录：\n\n- [基本用法](./examples/basic.ts) - 文件 CRUD 操作\n- [下载和上传](./examples/download-upload.ts) - 带进度的网络操作\n- [压缩操作](./examples/zip.ts) - 压缩和解压文件\n- [流式操作](./examples/stream.ts) - 使用流读写文件\n- [同步 API](./examples/sync-api.ts) - 通过 Worker 实现的同步操作\n- [共享 Messenger](./examples/shared-messenger.ts) - 在不同上下文间共享同步实例\n\n## 浏览器兼容性\n\n| 浏览器 | 版本 |\n|--------|------|\n| Chrome | 86+  |\n| Edge   | 86+  |\n| Firefox| 111+ |\n| Safari | 15.2+|\n\n详细兼容性信息请参阅 [MDN - OPFS](https://developer.mozilla.org/zh-CN/docs/Web/API/File_System_API/Origin_private_file_system#browser_compatibility)。\n\n可以安装 [OPFS Explorer](https://chromewebstore.google.com/detail/acndjpgkpaclldomagafnognkcgjignd) 来可视化查看文件系统。\n\n## 从 1.x 迁移到 2.x\n\n### 破坏性变更 1：`readFile` 默认返回类型\n\n在 1.x 中，`readFile` 默认返回 `ArrayBuffer`。在 2.x 中，默认返回 `Uint8Array`。\n\n```ts\n// 1.x - 默认返回 ArrayBuffer\nconst result = await readFile('/path/to/file');\n\n// 2.x - 默认返回 Uint8Array\nconst result = await readFile('/path/to/file');\n\n// 迁移方案：如需 ArrayBuffer，使用 .buffer 属性\nconst uint8Array = await readFile('/path/to/file');\nconst arrayBuffer = uint8Array.unwrap().buffer;\n```\n\n### 破坏性变更 2：移除 `readFileStream` 和 `writeFileStream`\n\n这些已废弃的 API 已被移除，请使用新的替代方案：\n\n```ts\n// 1.x\nconst stream = await readFileStream('/path/to/file');\nconst writable = await writeFileStream('/path/to/file');\n\n// 2.x\nconst stream = await readFile('/path/to/file', { encoding: 'stream' });\nconst writable = await openWritableFileStream('/path/to/file');\n```\n\n## 测试覆盖率\n\n覆盖率在真实浏览器环境中使用 V8 provider 收集。\n\n- `src/sync/channel/listen.ts` 被排除，因为它运行在 Web Worker 上下文中，V8 无法对其插桩\n- `src/async/core/*.ts` 有部分分支通过 `createSyncAccessHandle` 在 Worker 上下文中运行，已通过 mock 测试覆盖\n\n## 许可证\n\n[MIT](LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjiangjie%2Fhappy-opfs","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fjiangjie%2Fhappy-opfs","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fjiangjie%2Fhappy-opfs/lists"}