@seedvault/cli 0.4.3 → 0.4.4

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.
Files changed (2) hide show
  1. package/dist/sv.js +43 -14
  2. package/package.json +1 -1
package/dist/sv.js CHANGED
@@ -158,6 +158,9 @@ function createClient(serverUrl, token) {
158
158
  if (opts.contentType) {
159
159
  headers["Content-Type"] = opts.contentType;
160
160
  }
161
+ if (opts.extraHeaders) {
162
+ Object.assign(headers, opts.extraHeaders);
163
+ }
161
164
  const res = await fetch(`${base}${path}`, {
162
165
  method,
163
166
  headers,
@@ -199,10 +202,16 @@ function createClient(serverUrl, token) {
199
202
  const res = await request("GET", "/v1/contributors");
200
203
  return res.json();
201
204
  },
202
- async putFile(username, path, content) {
205
+ async putFile(username, path, content, opts) {
206
+ const extraHeaders = {};
207
+ if (opts?.originCtime)
208
+ extraHeaders["X-Origin-Ctime"] = opts.originCtime;
209
+ if (opts?.originMtime)
210
+ extraHeaders["X-Origin-Mtime"] = opts.originMtime;
203
211
  const res = await request("PUT", `/v1/files/${username}/${encodePath(path)}`, {
204
212
  body: content,
205
- contentType: "text/markdown"
213
+ contentType: "text/markdown",
214
+ extraHeaders: Object.keys(extraHeaders).length > 0 ? extraHeaders : undefined
206
215
  });
207
216
  return res.json();
208
217
  },
@@ -2148,7 +2157,8 @@ class RetryQueue {
2148
2157
  const op = this.items[0];
2149
2158
  try {
2150
2159
  if (op.type === "put" && op.content !== null) {
2151
- await this.client.putFile(op.username, op.serverPath, op.content);
2160
+ const opts = op.originCtime || op.originMtime ? { originCtime: op.originCtime ?? undefined, originMtime: op.originMtime ?? undefined } : undefined;
2161
+ await this.client.putFile(op.username, op.serverPath, op.content, opts);
2152
2162
  } else if (op.type === "delete") {
2153
2163
  await this.client.deleteFile(op.username, op.serverPath);
2154
2164
  }
@@ -2228,7 +2238,9 @@ class Syncer {
2228
2238
  username: this.username,
2229
2239
  serverPath: f.path,
2230
2240
  content: null,
2231
- queuedAt: new Date().toISOString()
2241
+ queuedAt: new Date().toISOString(),
2242
+ originCtime: null,
2243
+ originMtime: null
2232
2244
  });
2233
2245
  }
2234
2246
  });
@@ -2244,7 +2256,7 @@ class Syncer {
2244
2256
  const { files: serverFiles } = await this.client.listFiles(this.username, collection.name + "/");
2245
2257
  const serverMap = new Map;
2246
2258
  for (const f of serverFiles) {
2247
- serverMap.set(f.path, f.modifiedAt);
2259
+ serverMap.set(f.path, f);
2248
2260
  }
2249
2261
  const localFiles = await walkMd(collection.path);
2250
2262
  const localServerPaths = new Set;
@@ -2253,8 +2265,9 @@ class Syncer {
2253
2265
  const relPath = toPosixPath(relative5(collection.path, localFile.path));
2254
2266
  const serverPath = `${collection.name}/${relPath}`;
2255
2267
  localServerPaths.add(serverPath);
2256
- const serverMod = serverMap.get(serverPath);
2257
- if (serverMod) {
2268
+ const serverEntry = serverMap.get(serverPath);
2269
+ if (serverEntry) {
2270
+ const serverMod = serverEntry.originMtime || serverEntry.modifiedAt;
2258
2271
  const serverDate = new Date(serverMod).getTime();
2259
2272
  const localDate = localFile.mtimeMs;
2260
2273
  if (localDate <= serverDate) {
@@ -2263,11 +2276,16 @@ class Syncer {
2263
2276
  }
2264
2277
  }
2265
2278
  const content = await readFile(localFile.path, "utf-8");
2266
- toUpload.push({ serverPath, content });
2279
+ const originCtime = new Date(localFile.birthtimeMs).toISOString();
2280
+ const originMtime = new Date(localFile.mtimeMs).toISOString();
2281
+ toUpload.push({ serverPath, content, originCtime, originMtime });
2267
2282
  }
2268
2283
  await pooled(toUpload, SYNC_CONCURRENCY, async (item) => {
2269
2284
  try {
2270
- await this.client.putFile(this.username, item.serverPath, item.content);
2285
+ await this.client.putFile(this.username, item.serverPath, item.content, {
2286
+ originCtime: item.originCtime,
2287
+ originMtime: item.originMtime
2288
+ });
2271
2289
  uploaded++;
2272
2290
  } catch {
2273
2291
  this.queue.enqueue({
@@ -2275,7 +2293,9 @@ class Syncer {
2275
2293
  username: this.username,
2276
2294
  serverPath: item.serverPath,
2277
2295
  content: item.content,
2278
- queuedAt: new Date().toISOString()
2296
+ queuedAt: new Date().toISOString(),
2297
+ originCtime: item.originCtime,
2298
+ originMtime: item.originMtime
2279
2299
  });
2280
2300
  }
2281
2301
  });
@@ -2329,14 +2349,21 @@ class Syncer {
2329
2349
  }
2330
2350
  async handleEvent(event) {
2331
2351
  if (event.type === "add" || event.type === "change") {
2332
- const content = await readFile(event.localPath, "utf-8");
2352
+ const [content, s] = await Promise.all([
2353
+ readFile(event.localPath, "utf-8"),
2354
+ stat4(event.localPath)
2355
+ ]);
2356
+ const originCtime = new Date(s.birthtimeMs).toISOString();
2357
+ const originMtime = new Date(s.mtimeMs).toISOString();
2333
2358
  this.log(`PUT ${event.serverPath} (${content.length} bytes)`);
2334
2359
  this.queue.enqueue({
2335
2360
  type: "put",
2336
2361
  username: this.username,
2337
2362
  serverPath: event.serverPath,
2338
2363
  content,
2339
- queuedAt: new Date().toISOString()
2364
+ queuedAt: new Date().toISOString(),
2365
+ originCtime,
2366
+ originMtime
2340
2367
  });
2341
2368
  } else if (event.type === "unlink") {
2342
2369
  this.log(`DELETE ${event.serverPath}`);
@@ -2345,7 +2372,9 @@ class Syncer {
2345
2372
  username: this.username,
2346
2373
  serverPath: event.serverPath,
2347
2374
  content: null,
2348
- queuedAt: new Date().toISOString()
2375
+ queuedAt: new Date().toISOString(),
2376
+ originCtime: null,
2377
+ originMtime: null
2349
2378
  });
2350
2379
  }
2351
2380
  }
@@ -2384,7 +2413,7 @@ async function walkDirRecursive(dir, results) {
2384
2413
  await walkDirRecursive(full, results);
2385
2414
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
2386
2415
  const s = await stat4(full);
2387
- results.push({ path: full, mtimeMs: s.mtimeMs });
2416
+ results.push({ path: full, mtimeMs: s.mtimeMs, birthtimeMs: s.birthtimeMs });
2388
2417
  }
2389
2418
  }
2390
2419
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@seedvault/cli",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "sv": "bin/sv.mjs"