@mountra/mountra-sdk 0.2.0 → 0.4.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 +34 -0
- package/README.md +144 -15
- package/dist/index.cjs +1869 -173
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +408 -13
- package/dist/index.d.ts +408 -13
- package/dist/index.js +1865 -173
- package/dist/index.js.map +1 -1
- package/package.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.4.0 - 2026-10-07
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Added `list()` and `tree()` to read a directory's entries or its complete subtree.
|
|
8
|
+
- Added `mkdir()`, `rename()`, `move()`, `copy()`, and `delete()` for files and directories. With the `downloadCache` enabled, they also stop the download cache from serving files at the paths they change: the next download asks Mountra again and still reuses the cached chunks. A custom `DownloadCacheStore` can implement the new optional `removeFilesUnder()`; without it, the workspace's download cache is cleared instead.
|
|
9
|
+
- Added an opt-in metadata cache through the `metadataCache` client option, stored in IndexedDB per workspace:
|
|
10
|
+
- `list` and `tree` take `mode`. `sync`, the default, waits for Mountra. `async` returns the cached result at once, refreshes it in the background, and delivers a changed result through `onUpdate` (failures through `onError`).
|
|
11
|
+
- Every result fetched from Mountra is cached before it is returned, and also refreshes the cached listings and trees it overlaps.
|
|
12
|
+
- Writes through the client, including uploads, update the cached listings and trees as soon as Mountra confirms them. Renaming or moving a directory carries its cached contents to the new path.
|
|
13
|
+
- A fetch that overlapped a write through the client is not cached, and an older fetch never replaces a newer one.
|
|
14
|
+
- Added `clearMetadataCache()`, `IndexedDBMetadataCacheStore`, and `InMemoryMetadataCacheStore`.
|
|
15
|
+
|
|
16
|
+
## 0.3.0 - 2026-10-07
|
|
17
|
+
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Added an opt-in IndexedDB download cache through the `downloadCache` client option:
|
|
21
|
+
- Entries are isolated per workspace and stored per chunk, so unchanged chunks of a modified file are reused.
|
|
22
|
+
- The cache is bounded by `maxBytes` and evicts least recently used chunks.
|
|
23
|
+
- A file served within `ttlMs` needs no request to Mountra.
|
|
24
|
+
- Added the per-download `cache` option (`default`, `no-cache`, `no-store`), `getDownloadCacheUsage()`, `clearDownloadCache()`, `IndexedDBDownloadCacheStore`, and `InMemoryDownloadCacheStore`.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
|
|
28
|
+
- `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.
|
|
29
|
+
- With the download cache enabled, multi-chunk files are downloaded chunk by chunk even without File System Access.
|
|
30
|
+
- 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.
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
|
|
34
|
+
- `exportFile`, `downloadFile`, and `downloadExport` now send the client's workspace when the call names none; Mountra previously rejected these requests.
|
|
35
|
+
- `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.
|
|
36
|
+
|
|
3
37
|
## 0.2.0 - 2026-08-19
|
|
4
38
|
|
|
5
39
|
### Added
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @mountra/mountra-sdk
|
|
2
2
|
|
|
3
|
-
用于浏览器前端接入 Mountra 的 TypeScript SDK
|
|
3
|
+
用于浏览器前端接入 Mountra 的 TypeScript SDK。提供文件上传、流式下载、断点续传、多 chunk 客户端组装,以及目录列表、目录树和文件操作(可选本地元数据缓存)。
|
|
4
4
|
|
|
5
5
|
## 使用
|
|
6
6
|
|
|
@@ -92,7 +92,7 @@ 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"
|
|
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
96
|
|
|
97
97
|
## 下载
|
|
98
98
|
|
|
@@ -153,6 +153,142 @@ if (pending[0]) {
|
|
|
153
153
|
|
|
154
154
|
可以通过 `downloadCheckpointStore` 注入自己的 `DownloadCheckpointStore`(测试时可使用 `InMemoryDownloadCheckpointStore`)。SDK 不会把短期签名 URL 写入 checkpoint;恢复时会重新请求 export URL。
|
|
155
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
|
+
通过客户端执行的 `mkdir`、`rename`、`move`、`copy`、`delete`(见[目录与元数据](#目录与元数据))成功后,这些操作涉及的路径及其下所有文件的缓存布局都会失效(按路径和 inode 记录的都包括),之后下载会重新向 Mountra 获取布局,已缓存的 chunk 仍然复用。自定义的 `DownloadCacheStore` 可以实现可选的 `removeFilesUnder` 方法;未实现时,会清除该 workspace 的全部下载缓存。
|
|
186
|
+
|
|
187
|
+
有效期内其他客户端对文件的修改不会被感知。单次下载可以用 `cache` 选项调整行为:
|
|
188
|
+
|
|
189
|
+
```ts
|
|
190
|
+
await file.download({ cache: "no-cache" }); // 先向 Mountra 确认,但仍复用已缓存的 chunk
|
|
191
|
+
await file.download({ cache: "no-store" }); // 不读也不写缓存
|
|
192
|
+
await mountra.upload(blob, { path: "/backup.tar", cache: "no-store" }); // 上传后不写入缓存
|
|
193
|
+
|
|
194
|
+
await mountra.getDownloadCacheUsage(); // { bytes, chunks }
|
|
195
|
+
await mountra.clearDownloadCache({ workspaceCuid: "workspace-cuid" }); // 清除一个 workspace
|
|
196
|
+
await mountra.clearDownloadCache(); // 清除全部
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
缓存位于浏览器的 IndexedDB 中,同一浏览器配置文件下的页面都能读取。用户退出登录时应调用 `clearDownloadCache()`;多个用户共用浏览器时,可以为每个用户使用独立的数据库:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
import { IndexedDBDownloadCacheStore } from "@mountra/mountra-sdk";
|
|
203
|
+
|
|
204
|
+
const downloadCache = {
|
|
205
|
+
store: new IndexedDBDownloadCacheStore({ databaseName: `mountra-download-cache-${userId}` }),
|
|
206
|
+
};
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
IndexedDB 不可用时(例如非浏览器环境)SDK 回退到内存缓存;缓存读写失败只会当作未命中,不会导致下载失败。
|
|
210
|
+
|
|
211
|
+
## 目录与元数据
|
|
212
|
+
|
|
213
|
+
`list` 返回一个目录的直接子项,`tree` 返回一个目录及其完整子树。未传 `path` 时读取根目录 `/`:
|
|
214
|
+
|
|
215
|
+
```ts
|
|
216
|
+
const { entries } = await mountra.list({ path: "/documents" });
|
|
217
|
+
// entries: [{ name, path, ino, isDirectory, size, mtimeMs, atimeMs, uid?, uidDisplayName?, ... }],目录在前,再按名称排序
|
|
218
|
+
|
|
219
|
+
const { root } = await mountra.tree({ path: "/documents" });
|
|
220
|
+
// root: { name, path, ino, isDirectory, size, mtimeMs, children: [...] }
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
目录和文件的写操作:
|
|
224
|
+
|
|
225
|
+
```ts
|
|
226
|
+
await mountra.mkdir({ path: "/documents/2026" }); // 父目录必须存在
|
|
227
|
+
await mountra.rename({ fromPath: "/documents/2026", toName: "archive" });
|
|
228
|
+
await mountra.move({ fromPath: "/documents/archive", toPath: "/archive" }); // toPath 是完整的目标路径
|
|
229
|
+
await mountra.copy({ fromPath: "/report.pdf", toPath: "/archive/report.pdf" }); // 只能复制文件
|
|
230
|
+
await mountra.delete({ path: "/archive/report.pdf" }); // 文件或空目录
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
`mkdir`、`rename`、`move`、`copy` 返回 `{ path, ino }`。
|
|
234
|
+
|
|
235
|
+
## 元数据缓存
|
|
236
|
+
|
|
237
|
+
创建客户端时传入 `metadataCache` 即可启用基于 IndexedDB 的元数据缓存(默认关闭,传 `{}` 使用默认值):
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
const mountra = createMountraClient({
|
|
241
|
+
baseUrl: "https://mountra.example.com",
|
|
242
|
+
token: "your-bearer-token",
|
|
243
|
+
workspaceCuid: "workspace-cuid",
|
|
244
|
+
metadataCache: {
|
|
245
|
+
maxEntries: 2000, // 缓存的 list / tree 结果数上限,默认 1000;超出后先淘汰最久未刷新的
|
|
246
|
+
},
|
|
247
|
+
});
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
`list` 和 `tree` 通过 `mode` 选择读取方式:
|
|
251
|
+
|
|
252
|
+
- `sync`(默认):等待 Mountra 返回最新结果。
|
|
253
|
+
- `async`:立即返回缓存的结果(`source: "cache"`),同时在后台向 Mountra 刷新。刷新结果与已返回的结果不同时,通过 `onUpdate` 回调送达;刷新失败时调用 `onError`。没有缓存时与 `sync` 相同,等待 Mountra 返回,也不会再调用 `onUpdate`。
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
const result = await mountra.list({
|
|
257
|
+
path: "/documents",
|
|
258
|
+
mode: "async",
|
|
259
|
+
onUpdate: (fresh) => render(fresh.entries), // 只在内容变化时调用
|
|
260
|
+
onError: (error) => console.warn("刷新失败", error),
|
|
261
|
+
});
|
|
262
|
+
render(result.entries);
|
|
263
|
+
console.log(result.source, result.fetchedAt); // "cache" | "remote",以及这份结果向 Mountra 请求的时间
|
|
264
|
+
```
|
|
265
|
+
|
|
266
|
+
`signal` 同样会取消后台刷新;中止之后不再调用任何回调。未启用 `metadataCache` 时 `async` 等同于 `sync`。
|
|
267
|
+
|
|
268
|
+
缓存按以下规则保持更新:
|
|
269
|
+
|
|
270
|
+
- 每次从 Mountra 取得 `list` / `tree` 结果,都先写入缓存再返回给调用方(包括 `async` 的后台刷新)。同时会刷新与它重叠的缓存:新的 `list` 结果会更新包含该目录的 `tree`,新的 `tree` 结果会更新其下各目录的 `list`。无法准确更新的条目会被删除而不是保留旧数据,例如 `list` 中出现了子树未知的新目录时,对应的 `tree` 缓存会失效。
|
|
271
|
+
- 通过客户端执行的 `mkdir`、`rename`、`move`、`copy`、`delete` 以及上传成功后,SDK 会直接更新父目录的 `list` 缓存和包含它的 `tree` 缓存,重命名或移动目录时还会把该目录下的缓存一起迁移到新路径。因此写操作之后用 `async` 读取能立即看到变化。新条目中只有 Mountra 才知道的字段(例如所有者、服务端修改时间)会在下一次刷新后补全。
|
|
272
|
+
- 在写操作完成之前发出、完成之后才返回的读取请求,结果可能早于这次写入,因此只返回给调用方,不写入缓存;较早发出的请求也不会覆盖较新的缓存。
|
|
273
|
+
- 读取时 Mountra 返回 404,说明该目录已不存在:它会从父目录的缓存中移除,它自身及其下的缓存也会被删除。
|
|
274
|
+
|
|
275
|
+
其他客户端或其他浏览器标签页的修改只能通过刷新感知:`async` 读取会先返回旧的缓存,再通过 `onUpdate` 送达最新结果。需要确保拿到最新数据时使用 `sync`。
|
|
276
|
+
|
|
277
|
+
缓存与下载缓存一样以 `baseUrl` 和 workspace 作为命名空间(`workspaceCuid` 优先于 `workspaceId`),同一 workspace 应始终用同一种方式指定,否则会产生两份互不更新的缓存。用户退出登录时应调用 `clearMetadataCache()`;多个用户共用浏览器时,可以为每个用户使用独立的数据库:
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
import { IndexedDBMetadataCacheStore } from "@mountra/mountra-sdk";
|
|
281
|
+
|
|
282
|
+
const metadataCache = {
|
|
283
|
+
store: new IndexedDBMetadataCacheStore({ databaseName: `mountra-metadata-cache-${userId}` }),
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
await mountra.clearMetadataCache({ workspaceCuid: "workspace-cuid" }); // 清除一个 workspace
|
|
287
|
+
await mountra.clearMetadataCache(); // 清除全部
|
|
288
|
+
```
|
|
289
|
+
|
|
290
|
+
IndexedDB 不可用时 SDK 回退到内存缓存(测试时也可以直接使用 `InMemoryMetadataCacheStore`)。缓存读写失败不会导致元数据操作失败;写操作成功后若缓存无法更新,该 workspace 的元数据缓存会被清空,以免保留与写入矛盾的数据。
|
|
291
|
+
|
|
156
292
|
## 开发
|
|
157
293
|
|
|
158
294
|
```bash
|
|
@@ -164,23 +300,16 @@ pnpm --dir mountra-sdk build
|
|
|
164
300
|
|
|
165
301
|
## 发布到 NPM
|
|
166
302
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
```bash
|
|
170
|
-
npm login
|
|
171
|
-
```
|
|
303
|
+
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)。
|
|
172
304
|
|
|
173
|
-
|
|
305
|
+
发布脚本也可以在本地运行。先使用拥有 `@mountra` scope 发布权限的账号登录 NPM(或设置 `NPM_TOKEN`),然后在仓库根目录执行:
|
|
174
306
|
|
|
175
307
|
```bash
|
|
308
|
+
npm login
|
|
309
|
+
pnpm publish:sdk --dry-run # 预览将要发布的内容
|
|
176
310
|
pnpm publish:sdk
|
|
177
311
|
```
|
|
178
312
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
```bash
|
|
182
|
-
pnpm publish:sdk --dry-run
|
|
183
|
-
```
|
|
313
|
+
脚本会先检查 CHANGELOG 中有当前版本的段落、`mountra-sdk` 没有未提交的改动,再依次执行类型检查、测试和构建,最后发布。版本已经在 registry 上时直接退出,不会重复发布;带预发布后缀的版本(如 `0.4.0-beta.1`)发布到 `next` dist-tag,可用 `NPM_DIST_TAG` 覆盖。
|
|
184
314
|
|
|
185
|
-
|
|
186
|
-
`NPM_REGISTRY` 环境变量。发布包包含 `dist/`、README 和 CHANGELOG,不包含源码与测试文件。
|
|
315
|
+
加上 `--stage`(如 `pnpm publish:sdk --stage`)时改用 `npm stage publish`,需要 npm 11.15.0 及以上。默认使用 `https://registry.npmjs.org/`,如需发布到其他 registry,设置 `NPM_REGISTRY` 环境变量。发布包包含 `dist/`、README 和 CHANGELOG,不包含源码与测试文件。
|