@rigxyz/tapd 0.4.0 → 0.5.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/dist/bin.js +2 -2
- package/dist/bin.js.map +1 -1
- package/dist/{chunk-YPZ2UNI5.js → chunk-XMYPUAYD.js} +144 -96
- package/dist/chunk-XMYPUAYD.js.map +1 -0
- package/dist/index.d.ts +16 -3
- package/dist/index.js +1 -1
- package/package.json +12 -5
- package/dist/chunk-YPZ2UNI5.js.map +0 -1
|
@@ -159,7 +159,14 @@ var RelayClient = class {
|
|
|
159
159
|
);
|
|
160
160
|
return expectJson(route, res);
|
|
161
161
|
}
|
|
162
|
-
/**
|
|
162
|
+
/**
|
|
163
|
+
* PUT bytes to a presigned URL. Throws on non-2xx — except 409, which
|
|
164
|
+
* means the content-addressed object is already stored (same hash = same
|
|
165
|
+
* bytes). That's idempotent success, not a conflict: two files with
|
|
166
|
+
* identical content (e.g. several empty files), or an initial upload that
|
|
167
|
+
* overlaps the daemon's first scan, race to PUT the same key. Treating
|
|
168
|
+
* 409 as fatal aborts the whole push and wedges sync at "N pending".
|
|
169
|
+
*/
|
|
163
170
|
async putObjectBytes(uploadUrl, bytes) {
|
|
164
171
|
const route = "PUT <uploadUrl>";
|
|
165
172
|
const res = await this.fetch(uploadUrl, {
|
|
@@ -167,6 +174,7 @@ var RelayClient = class {
|
|
|
167
174
|
body: new Uint8Array(bytes),
|
|
168
175
|
headers: { "content-length": String(bytes.byteLength) }
|
|
169
176
|
});
|
|
177
|
+
if (res.status === 409) return;
|
|
170
178
|
if (!res.ok) {
|
|
171
179
|
throw new RelayError(route, res.status, await readJson(res));
|
|
172
180
|
}
|
|
@@ -285,9 +293,8 @@ function joinUrl(base, suffix) {
|
|
|
285
293
|
}
|
|
286
294
|
|
|
287
295
|
// src/state-db.ts
|
|
288
|
-
import { mkdirSync as mkdirSync2 } from "fs";
|
|
296
|
+
import { mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2, renameSync } from "fs";
|
|
289
297
|
import { dirname as dirname2 } from "path";
|
|
290
|
-
import Database from "better-sqlite3";
|
|
291
298
|
var CURSOR_PREFIX = "chg_";
|
|
292
299
|
function cursorGreaterThan(a, b) {
|
|
293
300
|
const an = a.startsWith(CURSOR_PREFIX) ? safeBigInt(a.slice(CURSOR_PREFIX.length)) : null;
|
|
@@ -303,93 +310,109 @@ function safeBigInt(s) {
|
|
|
303
310
|
return null;
|
|
304
311
|
}
|
|
305
312
|
}
|
|
306
|
-
var
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
);
|
|
319
|
-
|
|
313
|
+
var pendingFlushers = /* @__PURE__ */ new Set();
|
|
314
|
+
var exitHookInstalled = false;
|
|
315
|
+
function installExitHook() {
|
|
316
|
+
if (exitHookInstalled) return;
|
|
317
|
+
exitHookInstalled = true;
|
|
318
|
+
process.once("exit", () => {
|
|
319
|
+
for (const flush of pendingFlushers) {
|
|
320
|
+
try {
|
|
321
|
+
flush();
|
|
322
|
+
} catch {
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
});
|
|
326
|
+
}
|
|
327
|
+
function loadStore(dbPath) {
|
|
328
|
+
try {
|
|
329
|
+
const parsed = JSON.parse(readFileSync2(dbPath, "utf8"));
|
|
330
|
+
if (parsed && parsed.version === 1 && parsed.meta && parsed.paths) {
|
|
331
|
+
return { version: 1, meta: parsed.meta, paths: parsed.paths };
|
|
332
|
+
}
|
|
333
|
+
} catch {
|
|
334
|
+
}
|
|
335
|
+
return { version: 1, meta: {}, paths: {} };
|
|
336
|
+
}
|
|
320
337
|
function openStateDb(dbPath) {
|
|
321
338
|
mkdirSync2(dirname2(dbPath), { recursive: true });
|
|
322
|
-
const
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
const
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
const
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
339
|
+
const store = loadStore(dbPath);
|
|
340
|
+
let dirty = false;
|
|
341
|
+
let timer = null;
|
|
342
|
+
const flush = () => {
|
|
343
|
+
if (timer) {
|
|
344
|
+
clearTimeout(timer);
|
|
345
|
+
timer = null;
|
|
346
|
+
}
|
|
347
|
+
if (!dirty) return;
|
|
348
|
+
const tmp = `${dbPath}.tmp`;
|
|
349
|
+
writeFileSync2(tmp, JSON.stringify(store), "utf8");
|
|
350
|
+
renameSync(tmp, dbPath);
|
|
351
|
+
dirty = false;
|
|
352
|
+
};
|
|
353
|
+
const schedule = () => {
|
|
354
|
+
dirty = true;
|
|
355
|
+
if (timer) return;
|
|
356
|
+
timer = setTimeout(flush, 100);
|
|
357
|
+
timer.unref?.();
|
|
358
|
+
};
|
|
359
|
+
pendingFlushers.add(flush);
|
|
360
|
+
installExitHook();
|
|
342
361
|
return {
|
|
343
362
|
getCursor() {
|
|
344
|
-
|
|
345
|
-
return row?.value ?? "chg_0";
|
|
363
|
+
return store.meta.cursor ?? "chg_0";
|
|
346
364
|
},
|
|
347
365
|
setCursor(cursor) {
|
|
348
|
-
const current =
|
|
366
|
+
const current = store.meta.cursor;
|
|
349
367
|
if (current && !cursorGreaterThan(cursor, current)) return;
|
|
350
|
-
|
|
368
|
+
store.meta.cursor = cursor;
|
|
369
|
+
schedule();
|
|
351
370
|
},
|
|
352
371
|
upsertPath(state) {
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
372
|
+
store.paths[state.path] = {
|
|
373
|
+
lastAppliedChangeId: state.lastAppliedChangeId,
|
|
374
|
+
lastSeenHash: state.lastSeenHash,
|
|
375
|
+
localDirty: state.localDirty,
|
|
376
|
+
lastScanAt: state.lastScanAt
|
|
377
|
+
};
|
|
378
|
+
schedule();
|
|
360
379
|
},
|
|
361
380
|
getPath(path) {
|
|
362
|
-
const
|
|
363
|
-
if (!
|
|
381
|
+
const r = store.paths[path];
|
|
382
|
+
if (!r) return null;
|
|
364
383
|
return {
|
|
365
|
-
path
|
|
366
|
-
lastAppliedChangeId:
|
|
367
|
-
lastSeenHash:
|
|
368
|
-
localDirty:
|
|
369
|
-
lastScanAt:
|
|
384
|
+
path,
|
|
385
|
+
lastAppliedChangeId: r.lastAppliedChangeId,
|
|
386
|
+
lastSeenHash: r.lastSeenHash,
|
|
387
|
+
localDirty: r.localDirty,
|
|
388
|
+
lastScanAt: r.lastScanAt
|
|
370
389
|
};
|
|
371
390
|
},
|
|
372
391
|
deletePath(path) {
|
|
373
|
-
|
|
392
|
+
if (path in store.paths) {
|
|
393
|
+
delete store.paths[path];
|
|
394
|
+
schedule();
|
|
395
|
+
}
|
|
374
396
|
},
|
|
375
397
|
listPaths() {
|
|
376
|
-
return
|
|
398
|
+
return Object.keys(store.paths);
|
|
377
399
|
},
|
|
378
400
|
setMeta(key, value) {
|
|
379
|
-
|
|
401
|
+
store.meta[key] = value;
|
|
402
|
+
schedule();
|
|
380
403
|
},
|
|
381
404
|
getMeta(key) {
|
|
382
|
-
|
|
383
|
-
return row?.value ?? null;
|
|
405
|
+
return store.meta[key] ?? null;
|
|
384
406
|
},
|
|
385
407
|
close() {
|
|
386
|
-
|
|
408
|
+
flush();
|
|
409
|
+
pendingFlushers.delete(flush);
|
|
387
410
|
}
|
|
388
411
|
};
|
|
389
412
|
}
|
|
390
413
|
|
|
391
414
|
// src/tapignore.ts
|
|
392
|
-
import { existsSync as existsSync2, readFileSync as
|
|
415
|
+
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
393
416
|
import { join as join2 } from "path";
|
|
394
417
|
import ignore from "ignore";
|
|
395
418
|
var BUILTIN_IGNORE_LINES = [
|
|
@@ -425,7 +448,7 @@ function loadIgnore(rootDir) {
|
|
|
425
448
|
ig.add(BUILTIN_IGNORE_LINES.join("\n"));
|
|
426
449
|
const projectIgnore = join2(rootDir, ".tapignore");
|
|
427
450
|
if (existsSync2(projectIgnore)) {
|
|
428
|
-
ig.add(
|
|
451
|
+
ig.add(readFileSync3(projectIgnore, "utf8"));
|
|
429
452
|
}
|
|
430
453
|
return ig;
|
|
431
454
|
}
|
|
@@ -560,11 +583,35 @@ async function hashFile(absPath) {
|
|
|
560
583
|
|
|
561
584
|
// src/init.ts
|
|
562
585
|
import { createHash as createHash2 } from "crypto";
|
|
563
|
-
import { readFileSync as
|
|
586
|
+
import { readFileSync as readFileSync4 } from "fs";
|
|
564
587
|
import { join as join4 } from "path";
|
|
588
|
+
|
|
589
|
+
// src/pool.ts
|
|
590
|
+
async function mapPool(items, limit, fn) {
|
|
591
|
+
let next = 0;
|
|
592
|
+
let failed = false;
|
|
593
|
+
const worker = async () => {
|
|
594
|
+
while (!failed) {
|
|
595
|
+
const idx = next++;
|
|
596
|
+
if (idx >= items.length) break;
|
|
597
|
+
const item = items[idx];
|
|
598
|
+
try {
|
|
599
|
+
await fn(item);
|
|
600
|
+
} catch (err) {
|
|
601
|
+
failed = true;
|
|
602
|
+
throw err;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
};
|
|
606
|
+
const n = Math.max(1, Math.min(limit, items.length));
|
|
607
|
+
await Promise.all(Array.from({ length: n }, worker));
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
// src/init.ts
|
|
565
611
|
function sha256OfBuffer(bytes) {
|
|
566
612
|
return `sha256:${createHash2("sha256").update(bytes).digest("hex")}`;
|
|
567
613
|
}
|
|
614
|
+
var UPLOAD_CONCURRENCY = 8;
|
|
568
615
|
var AlreadyInitializedError = class extends Error {
|
|
569
616
|
constructor(path) {
|
|
570
617
|
super(`already initialized \u2014 ${path} exists`);
|
|
@@ -609,27 +656,27 @@ async function init(opts) {
|
|
|
609
656
|
const mutatedDuringScan = /* @__PURE__ */ new Set();
|
|
610
657
|
let uploaded = 0;
|
|
611
658
|
let reused = 0;
|
|
612
|
-
|
|
613
|
-
const prep = await client.prepareUpload({ hash, size: f.size });
|
|
659
|
+
await mapPool([...uniqueHashes.values()], UPLOAD_CONCURRENCY, async (f) => {
|
|
660
|
+
const prep = await client.prepareUpload({ hash: f.hash, size: f.size });
|
|
614
661
|
if (prep.exists) {
|
|
615
662
|
reused += 1;
|
|
616
|
-
|
|
663
|
+
return;
|
|
617
664
|
}
|
|
618
|
-
const bytes =
|
|
665
|
+
const bytes = readFileSync4(join4(opts.rootDir, f.path));
|
|
619
666
|
const actual = sha256OfBuffer(bytes);
|
|
620
|
-
if (actual !== hash) {
|
|
667
|
+
if (actual !== f.hash) {
|
|
621
668
|
mutatedDuringScan.add(f.path);
|
|
622
669
|
scan.warnings.push({
|
|
623
670
|
path: f.path,
|
|
624
671
|
reason: "invalid_path"
|
|
625
672
|
// closest existing warning kind; semantics are file-mutated-mid-scan
|
|
626
673
|
});
|
|
627
|
-
|
|
674
|
+
return;
|
|
628
675
|
}
|
|
629
676
|
await client.putObjectBytes(prep.uploadUrl, bytes);
|
|
630
|
-
await client.completeUpload({ hash, size: f.size });
|
|
677
|
+
await client.completeUpload({ hash: f.hash, size: f.size });
|
|
631
678
|
uploaded += 1;
|
|
632
|
-
}
|
|
679
|
+
});
|
|
633
680
|
const events = [
|
|
634
681
|
...hashed.filter((f) => !mutatedDuringScan.has(f.path)).map((f) => ({
|
|
635
682
|
op: "write",
|
|
@@ -786,10 +833,10 @@ import {
|
|
|
786
833
|
existsSync as existsSync3,
|
|
787
834
|
mkdirSync as mkdirSync3,
|
|
788
835
|
readdirSync as readdirSync2,
|
|
789
|
-
readFileSync as
|
|
836
|
+
readFileSync as readFileSync5,
|
|
790
837
|
realpathSync,
|
|
791
838
|
statSync as statSync2,
|
|
792
|
-
writeFileSync as
|
|
839
|
+
writeFileSync as writeFileSync3
|
|
793
840
|
} from "fs";
|
|
794
841
|
import { dirname as dirname3, isAbsolute, join as join6, relative } from "path";
|
|
795
842
|
var NAME_RE = /^[a-z0-9][a-z0-9_-]*$/;
|
|
@@ -875,14 +922,14 @@ var MOUNT_FILE_SUFFIX = ".local.json";
|
|
|
875
922
|
function writeImportMount(rootDir, name, mount) {
|
|
876
923
|
const path = importMountFile(rootDir, name);
|
|
877
924
|
mkdirSync3(dirname3(path), { recursive: true });
|
|
878
|
-
|
|
925
|
+
writeFileSync3(path, JSON.stringify(mount, null, 2) + "\n", { mode: 384 });
|
|
879
926
|
}
|
|
880
927
|
function readImportMount(rootDir, name) {
|
|
881
928
|
const file = importMountFile(rootDir, name);
|
|
882
929
|
if (!existsSync3(file)) return { ok: false, reason: "missing_token_file" };
|
|
883
930
|
let parsed;
|
|
884
931
|
try {
|
|
885
|
-
parsed = JSON.parse(
|
|
932
|
+
parsed = JSON.parse(readFileSync5(file, "utf8"));
|
|
886
933
|
} catch {
|
|
887
934
|
return { ok: false, reason: "malformed_json" };
|
|
888
935
|
}
|
|
@@ -966,7 +1013,7 @@ function pathsOverlap(a, b) {
|
|
|
966
1013
|
}
|
|
967
1014
|
|
|
968
1015
|
// src/import-invite.ts
|
|
969
|
-
import { existsSync as existsSync4, readFileSync as
|
|
1016
|
+
import { existsSync as existsSync4, readFileSync as readFileSync6, rmSync } from "fs";
|
|
970
1017
|
import { dirname as dirname4, join as joinPath2 } from "path";
|
|
971
1018
|
var ImportValidationFailed = class extends Error {
|
|
972
1019
|
constructor(reason) {
|
|
@@ -1063,7 +1110,7 @@ function readRawMount(rootDir, name) {
|
|
|
1063
1110
|
const file = importMountFile(rootDir, name);
|
|
1064
1111
|
if (!existsSync4(file)) return null;
|
|
1065
1112
|
try {
|
|
1066
|
-
return JSON.parse(
|
|
1113
|
+
return JSON.parse(readFileSync6(file, "utf8"));
|
|
1067
1114
|
} catch {
|
|
1068
1115
|
return null;
|
|
1069
1116
|
}
|
|
@@ -1115,11 +1162,11 @@ import {
|
|
|
1115
1162
|
lstatSync,
|
|
1116
1163
|
mkdirSync as mkdirSync4,
|
|
1117
1164
|
readdirSync as readdirSync3,
|
|
1118
|
-
readFileSync as
|
|
1119
|
-
renameSync,
|
|
1165
|
+
readFileSync as readFileSync7,
|
|
1166
|
+
renameSync as renameSync2,
|
|
1120
1167
|
rmdirSync,
|
|
1121
1168
|
rmSync as rmSync2,
|
|
1122
|
-
writeFileSync as
|
|
1169
|
+
writeFileSync as writeFileSync4
|
|
1123
1170
|
} from "fs";
|
|
1124
1171
|
import { basename, dirname as dirname5, extname, join as join7, sep as sep2 } from "path";
|
|
1125
1172
|
var STATE_KEY_CONFLICT_COUNT = "conflict_count";
|
|
@@ -1141,7 +1188,7 @@ function sha256Hex(bytes) {
|
|
|
1141
1188
|
return createHash3("sha256").update(bytes).digest("hex");
|
|
1142
1189
|
}
|
|
1143
1190
|
function sha256OfFile(absPath) {
|
|
1144
|
-
return `sha256:${sha256Hex(
|
|
1191
|
+
return `sha256:${sha256Hex(readFileSync7(absPath))}`;
|
|
1145
1192
|
}
|
|
1146
1193
|
function conflictSidecarPath(path, ev) {
|
|
1147
1194
|
const dir = dirname5(path);
|
|
@@ -1274,8 +1321,8 @@ async function applyEvent(opts) {
|
|
|
1274
1321
|
const sidecarAbs = join7(rootDir, sidecarPath);
|
|
1275
1322
|
mkdirSync4(dirname5(sidecarAbs), { recursive: true });
|
|
1276
1323
|
const tmp2 = `${sidecarAbs}.tap-tmp.${randomBytes(6).toString("hex")}`;
|
|
1277
|
-
|
|
1278
|
-
|
|
1324
|
+
writeFileSync4(tmp2, bytes, { mode: ev.executable ? 493 : 420 });
|
|
1325
|
+
renameSync2(tmp2, sidecarAbs);
|
|
1279
1326
|
stateDb.upsertPath({
|
|
1280
1327
|
path: ev.path,
|
|
1281
1328
|
lastAppliedChangeId: ev.id,
|
|
@@ -1288,8 +1335,8 @@ async function applyEvent(opts) {
|
|
|
1288
1335
|
}
|
|
1289
1336
|
mkdirSync4(dirname5(abs), { recursive: true });
|
|
1290
1337
|
const tmp = `${abs}.tap-tmp.${randomBytes(6).toString("hex")}`;
|
|
1291
|
-
|
|
1292
|
-
|
|
1338
|
+
writeFileSync4(tmp, bytes, { mode: ev.executable ? 493 : 420 });
|
|
1339
|
+
renameSync2(tmp, abs);
|
|
1293
1340
|
stateDb.upsertPath({
|
|
1294
1341
|
path: ev.path,
|
|
1295
1342
|
lastAppliedChangeId: ev.id,
|
|
@@ -1601,8 +1648,9 @@ async function startSubscription(opts) {
|
|
|
1601
1648
|
|
|
1602
1649
|
// src/uploader.ts
|
|
1603
1650
|
import { createHash as createHash4 } from "crypto";
|
|
1604
|
-
import { readFileSync as
|
|
1651
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
1605
1652
|
import { join as join8 } from "path";
|
|
1653
|
+
var UPLOAD_CONCURRENCY2 = 8;
|
|
1606
1654
|
function sha256OfBuffer2(bytes) {
|
|
1607
1655
|
return `sha256:${createHash4("sha256").update(bytes).digest("hex")}`;
|
|
1608
1656
|
}
|
|
@@ -1674,23 +1722,23 @@ async function scanAndPush(opts) {
|
|
|
1674
1722
|
const skipWarnings = [];
|
|
1675
1723
|
let uploaded = 0;
|
|
1676
1724
|
let reused = 0;
|
|
1677
|
-
|
|
1678
|
-
const prep = await client.prepareUpload({ hash, size: f.size });
|
|
1725
|
+
await mapPool([...uniqueHashes.values()], UPLOAD_CONCURRENCY2, async (f) => {
|
|
1726
|
+
const prep = await client.prepareUpload({ hash: f.hash, size: f.size });
|
|
1679
1727
|
if (prep.exists) {
|
|
1680
1728
|
reused += 1;
|
|
1681
|
-
|
|
1729
|
+
return;
|
|
1682
1730
|
}
|
|
1683
|
-
const bytes =
|
|
1731
|
+
const bytes = readFileSync8(join8(rootDir, f.path));
|
|
1684
1732
|
const actual = sha256OfBuffer2(bytes);
|
|
1685
|
-
if (actual !== hash) {
|
|
1733
|
+
if (actual !== f.hash) {
|
|
1686
1734
|
mutatedPaths.add(f.path);
|
|
1687
1735
|
skipWarnings.push({ path: f.path, reason: `mutated_during_scan:${actual}` });
|
|
1688
|
-
|
|
1736
|
+
return;
|
|
1689
1737
|
}
|
|
1690
1738
|
await client.putObjectBytes(prep.uploadUrl, bytes);
|
|
1691
|
-
await client.completeUpload({ hash, size: f.size });
|
|
1739
|
+
await client.completeUpload({ hash: f.hash, size: f.size });
|
|
1692
1740
|
uploaded += 1;
|
|
1693
|
-
}
|
|
1741
|
+
});
|
|
1694
1742
|
const events = [];
|
|
1695
1743
|
for (const w of diff.writes) {
|
|
1696
1744
|
if (mutatedPaths.has(w.path)) continue;
|
|
@@ -1989,4 +2037,4 @@ export {
|
|
|
1989
2037
|
scanAndPush,
|
|
1990
2038
|
start
|
|
1991
2039
|
};
|
|
1992
|
-
//# sourceMappingURL=chunk-
|
|
2040
|
+
//# sourceMappingURL=chunk-XMYPUAYD.js.map
|