@mountra/mountra-sdk 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,40 @@
1
+ # Changelog
2
+
3
+ ## 0.3.0 - 2026-10-07
4
+
5
+ ### Added
6
+
7
+ - Added an opt-in IndexedDB download cache through the `downloadCache` client option:
8
+ - Entries are isolated per workspace and stored per chunk, so unchanged chunks of a modified file are reused.
9
+ - The cache is bounded by `maxBytes` and evicts least recently used chunks.
10
+ - A file served within `ttlMs` needs no request to Mountra.
11
+ - Added the per-download `cache` option (`default`, `no-cache`, `no-store`), `getDownloadCacheUsage()`, `clearDownloadCache()`, `IndexedDBDownloadCacheStore`, and `InMemoryDownloadCacheStore`.
12
+
13
+ ### Changed
14
+
15
+ - `DownloadResult.export` is now optional: it is absent when the file was served entirely from the download cache. The new `DownloadResult.cachedBytes` reports bytes read from the cache.
16
+ - With the download cache enabled, multi-chunk files are downloaded chunk by chunk even without File System Access.
17
+ - Files uploaded through the client are cached by default, so downloading them needs no network until `ttlMs` passes. With `cache: "no-store"` on the upload, or when a chunk cannot be cached, the upload only invalidates the file's previous cache entry.
18
+
19
+ ### Fixed
20
+
21
+ - `exportFile`, `downloadFile`, and `downloadExport` now send the client's workspace when the call names none; Mountra previously rejected these requests.
22
+ - `splitFileIntoChunks` and `uploadFile({ chunkCount })` no longer produce empty or negative-sized trailing chunks when `chunkCount` does not divide the file size. `chunkCount` is now the maximum number of fixed-size chunks, so such uploads are no longer rejected by the server.
23
+
24
+ ## 0.2.0 - 2026-08-19
25
+
26
+ ### Added
27
+
28
+ - Added streaming browser downloads using the File System Access API, including a save-location picker and incremental writes.
29
+ - Added IndexedDB-backed download checkpoints, automatic retry, Range-based resume, and resumable downloads after refresh or network interruption.
30
+ - Added multi-chunk export metadata with per-chunk signed URLs and sequential client-side assembly when File System Access is available.
31
+ - Added download progress callbacks with total size, downloaded bytes, one-second-window speed, elapsed time, retry count, and remaining-time estimation.
32
+ - Added the `MountraFile` resource API:
33
+ - `client.file({ wsid, path })`
34
+ - `client.file({ wsid, ino })`
35
+ - `file.export()`, `file.download()`, and `file.resume()`
36
+
37
+ ### Changed
38
+
39
+ - `exportFile` responses now include the complete file size and, for multi-chunk files, the ordered chunk export URLs.
40
+ - Existing parameter-based upload and download APIs remain available for compatibility.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @mountra/mountra-sdk
2
2
 
3
- 用于浏览器前端接入 Mountra 的 TypeScript SDK。当前提供统一的文件上传工具:普通 chunk 使用签名 PUT,jumbo chunk 使用 S3 Multipart。
3
+ 用于浏览器前端接入 Mountra 的 TypeScript SDK。提供文件上传、流式下载、断点续传和多 chunk 客户端组装。
4
4
 
5
5
  ## 使用
6
6
 
@@ -92,7 +92,119 @@ const result = await mountra.resumeUpload(file, prepared.context);
92
92
 
93
93
  `getUploadStatus` 返回每个 chunk 的 `pending/uploaded` 状态,以及 pending chunk 可直接使用的 PUT 目标。服务端会根据对象存储中的对象重新校验状态,因此客户端不应仅依赖内存中的上传进度。
94
94
 
95
- `chunkCount > 1` 时请求使用 `chunk_method: "fix"`。云端会为每个 chunk 保存独立的 descriptor,并在 status 查询中根据对象存储重新确认上传状态。
95
+ `chunkCount > 1` 时请求使用 `chunk_method: "fix"`:每个 chunk 为 `ceil(size / chunkCount)` 字节,只有最后一个 chunk 可以更小,因此无法整除时实际 chunk 数可能少于 `chunkCount`。未配置 `external_url` 的 Mountra 部署在导出时会用 S3 Multipart 合并 chunk,此时除最后一个外每个 chunk 都需要至少 5 MiB。云端会为每个 chunk 保存独立的 descriptor,并在 status 查询中根据对象存储重新确认上传状态。
96
+
97
+ ## 下载
98
+
99
+ 文件操作也可以使用资源对象风格。`path` 和 `ino` 必须二选一;`wsid` 是
100
+ `workspaceId` 的简写。未在对象中提供 workspace 时,会使用创建
101
+ `MountraClient` 时配置的 workspace:
102
+
103
+ ```ts
104
+ const file = mountra.file({
105
+ wsid: 17,
106
+ path: "/documents/report.pdf",
107
+ });
108
+
109
+ const exported = await file.export();
110
+ await file.download({
111
+ downloadFilename: "report.pdf",
112
+ onProgress: console.log,
113
+ });
114
+
115
+ // 页面刷新后,自动选择这个文件最新的可恢复下载 checkpoint
116
+ await file.resume();
117
+ ```
118
+
119
+ 也可以通过 inode 定位:
120
+
121
+ ```ts
122
+ const file = mountra.file({ ino: "12345" });
123
+ await file.download();
124
+ ```
125
+
126
+ `file.export()`、`file.download()` 和 `file.resume()` 会始终使用创建对象
127
+ 时确定的文件身份,行为参数只能通过各方法的 options 传入。
128
+
129
+ `exportFile` 返回完整文件大小 `size`。多 chunk 文件还会返回按顺序排列的 `chunks`,每个 chunk 都包含独立的签名 URL;`url` 始终是服务端顺序组装后的完整文件 URL。
130
+
131
+ ```ts
132
+ const result = await mountra.downloadFile({
133
+ path: "/documents/report.pdf",
134
+ downloadFilename: "report.pdf",
135
+ onProgress: ({ downloadedBytes, totalBytes, speedBytesPerSecond, estimatedRemainingSeconds }) => {
136
+ console.log({ downloadedBytes, totalBytes, speedBytesPerSecond, estimatedRemainingSeconds });
137
+ },
138
+ });
139
+ ```
140
+
141
+ 在支持 File System Access API 的 Chromium 浏览器中,SDK 会先调用 `showSaveFilePicker`,随后使用 `ReadableStream` 将数据直接写入用户选择的文件。多 chunk 文件优先逐个下载 chunk 并按顺序追加;其他浏览器使用服务端组装 URL 并返回 Blob。
142
+
143
+ 下载进度会从 streaming response 实际写入的字节数统计,速度使用最近 1 秒窗口计算。网络错误和常见临时 HTTP 错误默认重试 3 次,可通过 `maxRetries`、`retryDelayMs` 和 `AbortSignal` 调整。
144
+
145
+ FSA 下载的中间状态默认保存到本地 IndexedDB,包括 chunk 偏移、已写入字节数以及可持久化的文件句柄。刷新页面后可以列出并恢复任务:
146
+
147
+ ```ts
148
+ const pending = await mountra.listResumableDownloads();
149
+ if (pending[0]) {
150
+ await mountra.resumeDownload(pending[0].key, { onProgress: console.log });
151
+ }
152
+ ```
153
+
154
+ 可以通过 `downloadCheckpointStore` 注入自己的 `DownloadCheckpointStore`(测试时可使用 `InMemoryDownloadCheckpointStore`)。SDK 不会把短期签名 URL 写入 checkpoint;恢复时会重新请求 export URL。
155
+
156
+ ## 下载缓存
157
+
158
+ 创建客户端时传入 `downloadCache` 即可启用基于 IndexedDB 的本地下载缓存(默认关闭,传 `{}` 使用默认值):
159
+
160
+ ```ts
161
+ const mountra = createMountraClient({
162
+ baseUrl: "https://mountra.example.com",
163
+ token: "your-bearer-token",
164
+ workspaceCuid: "workspace-cuid",
165
+ downloadCache: {
166
+ maxBytes: 1024 * 1024 * 1024, // 缓存上限,默认 512 MiB;超出后按 LRU 淘汰
167
+ ttlMs: 10 * 60 * 1000, // 文件的有效期,默认 5 分钟;0 表示每次都向 Mountra 确认
168
+ },
169
+ });
170
+ ```
171
+
172
+ 下载时 SDK 先检查缓存:
173
+
174
+ - 文件未过期且所有 chunk 都在缓存中:直接从 IndexedDB 读取,不访问 Mountra,`result.export` 为空。
175
+ - 未命中或已过期:调用 `/api/file/export` 获取最新的 chunk 布局。已缓存的 chunk 从本地读取,其余 chunk 下载后写入缓存,文件重新开始计算有效期。
176
+
177
+ `result.cachedBytes` 是从缓存读取的字节数。
178
+
179
+ 缓存以 chunk 为单位,按 chunk 的 `storage_key` 索引。启用缓存后,多 chunk 文件即使没有 File System Access 也会逐个 chunk 下载,文件修改后未变化的 chunk 仍可复用。只有内容寻址的 chunk(`.../blobs/sha256/<hash>`)会被缓存,local-first 文件等内容可能变化的对象不会缓存。大于 `maxChunkBytes`(默认 128 MiB)的 chunk 不缓存,因为写入缓存前要在内存中暂存整个 chunk。
180
+
181
+ 所有条目都以 `baseUrl` 和 workspace 作为命名空间,与服务端一致,`workspaceCuid` 优先于 `workspaceId`。一个 workspace 的缓存不会被另一个 workspace 读到。`maxBytes` 是所有 workspace 共享的总上限。
182
+
183
+ 通过客户端 `upload` 的文件默认也会写入缓存:上传完成后,各 chunk 直接从本地文件写入 IndexedDB,并按路径和 inode 记录文件布局,之后在有效期内下载这个文件不需要访问网络。缓存规则与下载相同(只缓存内容寻址的 chunk,并受 `maxChunkBytes` 限制)。如果有 chunk 无法缓存,或者上传时传入 `cache: "no-store"`,就只让该文件原有的缓存失效。
184
+
185
+ 有效期内其他客户端对文件的修改不会被感知。单次下载可以用 `cache` 选项调整行为:
186
+
187
+ ```ts
188
+ await file.download({ cache: "no-cache" }); // 先向 Mountra 确认,但仍复用已缓存的 chunk
189
+ await file.download({ cache: "no-store" }); // 不读也不写缓存
190
+ await mountra.upload(blob, { path: "/backup.tar", cache: "no-store" }); // 上传后不写入缓存
191
+
192
+ await mountra.getDownloadCacheUsage(); // { bytes, chunks }
193
+ await mountra.clearDownloadCache({ workspaceCuid: "workspace-cuid" }); // 清除一个 workspace
194
+ await mountra.clearDownloadCache(); // 清除全部
195
+ ```
196
+
197
+ 缓存位于浏览器的 IndexedDB 中,同一浏览器配置文件下的页面都能读取。用户退出登录时应调用 `clearDownloadCache()`;多个用户共用浏览器时,可以为每个用户使用独立的数据库:
198
+
199
+ ```ts
200
+ import { IndexedDBDownloadCacheStore } from "@mountra/mountra-sdk";
201
+
202
+ const downloadCache = {
203
+ store: new IndexedDBDownloadCacheStore({ databaseName: `mountra-download-cache-${userId}` }),
204
+ };
205
+ ```
206
+
207
+ IndexedDB 不可用时(例如非浏览器环境)SDK 回退到内存缓存;缓存读写失败只会当作未命中,不会导致下载失败。
96
208
 
97
209
  ## 开发
98
210
 
@@ -105,22 +217,16 @@ pnpm --dir mountra-sdk build
105
217
 
106
218
  ## 发布到 NPM
107
219
 
108
- 先使用拥有 `@mountra` scope 发布权限的账号登录 NPM:
220
+ SDK 由 GitLab CI 发布:在 `main` 上提交新的 `version` 和对应的 `## X.Y.Z - YYYY-MM-DD` CHANGELOG 段落后,推送 `sdk-vX.Y.Z` Tag,`publish:mountra-sdk` 任务会用 `npm stage publish` 把这个版本暂存到 npm,再由 maintainer 通过 2FA 批准上线。完整流程和一次性的 CI 配置见 [docs/release.md](../docs/release.md#mountra-sdk)。
109
221
 
110
- ```bash
111
- npm login
112
- ```
113
-
114
- 在仓库根目录执行:
222
+ 发布脚本也可以在本地运行。先使用拥有 `@mountra` scope 发布权限的账号登录 NPM(或设置 `NPM_TOKEN`),然后在仓库根目录执行:
115
223
 
116
224
  ```bash
225
+ npm login
226
+ pnpm publish:sdk --dry-run # 预览将要发布的内容
117
227
  pnpm publish:sdk
118
228
  ```
119
229
 
120
- 发布脚本会依次执行类型检查、测试和构建,然后发布公开的 `@mountra/mountra-sdk` package。实际发布前可以先预览将要发布的内容:
121
-
122
- ```bash
123
- pnpm publish:sdk --dry-run
124
- ```
230
+ 脚本会先检查 CHANGELOG 中有当前版本的段落、`mountra-sdk` 没有未提交的改动,再依次执行类型检查、测试和构建,最后发布。版本已经在 registry 上时直接退出,不会重复发布;带预发布后缀的版本(如 `0.4.0-beta.1`)发布到 `next` dist-tag,可用 `NPM_DIST_TAG` 覆盖。
125
231
 
126
- 发布包只包含 `dist/` 和本 README,不包含源码与测试文件。
232
+ 加上 `--stage`(如 `pnpm publish:sdk --stage`)时改用 `npm stage publish`,需要 npm 11.15.0 及以上。默认使用 `https://registry.npmjs.org/`,如需发布到其他 registry,设置 `NPM_REGISTRY` 环境变量。发布包包含 `dist/`、README 和 CHANGELOG,不包含源码与测试文件。