@artenx/dshb 0.1.18 → 0.1.19
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/lib/client.js +2157 -567
- package/lib/client.js.map +1 -1
- package/lib/core/index.js +72 -4
- package/lib/core/index.js.map +1 -1
- package/lib/core/node-registry.d.ts +2 -108
- package/lib/core/node-registry.js +43 -2
- package/lib/core/node-registry.js.map +1 -1
- package/lib/core/routes.d.ts +4 -3
- package/lib/core/routes.js +67 -23
- package/lib/core/routes.js.map +1 -1
- package/lib/core/sync/coordinator.d.ts +48 -0
- package/lib/core/sync/coordinator.js +247 -0
- package/lib/core/sync/coordinator.js.map +1 -0
- package/lib/core/sync/host-key.d.ts +2 -0
- package/lib/core/sync/host-key.js +71 -0
- package/lib/core/sync/host-key.js.map +1 -0
- package/lib/core/sync/internal-routes.d.ts +9 -0
- package/lib/core/sync/internal-routes.js +236 -0
- package/lib/core/sync/internal-routes.js.map +1 -0
- package/lib/core/sync/model.d.ts +2 -0
- package/lib/core/sync/model.js +106 -0
- package/lib/core/sync/model.js.map +1 -0
- package/lib/core/sync/node-sync.d.ts +2 -0
- package/lib/core/sync/node-sync.js +660 -0
- package/lib/core/sync/node-sync.js.map +1 -0
- package/lib/core/sync/offline-archive.d.ts +55 -0
- package/lib/core/sync/offline-archive.js +673 -0
- package/lib/core/sync/offline-archive.js.map +1 -0
- package/lib/core/sync/offline-journal.d.ts +22 -0
- package/lib/core/sync/offline-journal.js +74 -0
- package/lib/core/sync/offline-journal.js.map +1 -0
- package/lib/core/sync/offline-routes.d.ts +7 -0
- package/lib/core/sync/offline-routes.js +192 -0
- package/lib/core/sync/offline-routes.js.map +1 -0
- package/lib/core/sync/peer-manager.d.ts +56 -0
- package/lib/core/sync/peer-manager.js +258 -0
- package/lib/core/sync/peer-manager.js.map +1 -0
- package/lib/core/sync/protocol.d.ts +2 -0
- package/lib/core/sync/protocol.js +124 -0
- package/lib/core/sync/protocol.js.map +1 -0
- package/lib/core/sync/remote-helper.d.ts +5 -0
- package/lib/core/sync/remote-helper.js +160 -0
- package/lib/core/sync/remote-helper.js.map +1 -0
- package/lib/core/sync/revision.d.ts +2 -0
- package/lib/core/sync/revision.js +77 -0
- package/lib/core/sync/revision.js.map +1 -0
- package/lib/core/sync/routes.d.ts +9 -0
- package/lib/core/sync/routes.js +339 -0
- package/lib/core/sync/routes.js.map +1 -0
- package/lib/core/sync/scheduler.d.ts +14 -0
- package/lib/core/sync/scheduler.js +81 -0
- package/lib/core/sync/scheduler.js.map +1 -0
- package/lib/core/sync/session-journal.d.ts +2 -0
- package/lib/core/sync/session-journal.js +191 -0
- package/lib/core/sync/session-journal.js.map +1 -0
- package/lib/core/sync/session-routes.d.ts +7 -0
- package/lib/core/sync/session-routes.js +67 -0
- package/lib/core/sync/session-routes.js.map +1 -0
- package/lib/core/sync/session-sync.d.ts +2 -0
- package/lib/core/sync/session-sync.js +2 -0
- package/lib/core/sync/ssh-transport.d.ts +2 -0
- package/lib/core/sync/ssh-transport.js +408 -0
- package/lib/core/sync/ssh-transport.js.map +1 -0
- package/lib/core/sync/stores.d.ts +3 -0
- package/lib/core/sync/stores.js +385 -0
- package/lib/core/sync/stores.js.map +1 -0
- package/lib/core/test.d.ts +1 -1
- package/lib/core/workspace-bindings.d.ts +2 -28
- package/lib/core/workspace-bindings.js +14 -14
- package/lib/core/workspace-bindings.js.map +1 -1
- package/lib/core/workspace-instruction-context.d.ts +3 -2260
- package/lib/core/workspace-instruction-context.js +104 -2
- package/lib/core/workspace-instruction-context.js.map +1 -0
- package/lib/core/workspace-instruction-routes.d.ts +1 -1
- package/lib/core/workspace-instructions.d.ts +2 -68
- package/lib/core/workspace-instructions.js +13 -0
- package/lib/core/workspace-instructions.js.map +1 -1
- package/lib/core/workspaces.d.ts +4 -3
- package/lib/core/workspaces.js +17 -1
- package/lib/core/workspaces.js.map +1 -1
- package/lib/host-key-C5afhAUd.d.ts +30 -0
- package/lib/index-VF8bcBeX.d.ts +2293 -0
- package/lib/{workspace-instruction-context-DytAmaPY.js → lib-gdtU_WKT.js} +159 -102
- package/lib/lib-gdtU_WKT.js.map +1 -0
- package/lib/model-BP7Gw1yk.d.ts +104 -0
- package/lib/node-registry-BTnnM5-k.d.ts +112 -0
- package/lib/node-sync-3Z9QLz2n.d.ts +89 -0
- package/lib/protocol-CV7LGE44.d.ts +35 -0
- package/lib/revision-C9sOlDZ6.d.ts +25 -0
- package/lib/router/router-fs.d.ts +12 -3
- package/lib/router/router-fs.js.map +1 -1
- package/lib/session-journal-pgPqcWn9.d.ts +40 -0
- package/lib/session-sync-EdfwUON5.js +2177 -0
- package/lib/session-sync-EdfwUON5.js.map +1 -0
- package/lib/session-sync-g6qKZzjV.d.ts +1252 -0
- package/lib/ssh-transport-BcIiLxsX.d.ts +55 -0
- package/lib/stores-CKyEt0yk.d.ts +31 -0
- package/lib/workspace-bindings-CsB0TIJB.d.ts +28 -0
- package/lib/workspace-instructions-DirYYiku.d.ts +69 -0
- package/package.json +20 -1
- package/lib/workspace-instruction-context-DytAmaPY.js.map +0 -1
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
import { HybridLogicalClock } from "./revision.js";
|
|
2
|
+
import { isNonNegativeSafeInteger, isRecord, parseSyncPeer, syncRecordKey, syncWorkspaceKey } from "./model.js";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
import { chmodSync, closeSync, fsyncSync, mkdirSync, openSync, readFileSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { basename, dirname, join, posix } from "node:path";
|
|
6
|
+
import { randomBytes } from "node:crypto";
|
|
7
|
+
//#region src/core/sync/stores.ts
|
|
8
|
+
function dshHome(home) {
|
|
9
|
+
return home ?? process.env.DSH_HOME ?? join(homedir(), ".dsh");
|
|
10
|
+
}
|
|
11
|
+
function readJson(file, validate) {
|
|
12
|
+
let content;
|
|
13
|
+
try {
|
|
14
|
+
content = readFileSync(file, "utf8");
|
|
15
|
+
} catch (error) {
|
|
16
|
+
if (isNodeError(error) && error.code === "ENOENT") return void 0;
|
|
17
|
+
throw error;
|
|
18
|
+
}
|
|
19
|
+
let value;
|
|
20
|
+
try {
|
|
21
|
+
value = JSON.parse(content);
|
|
22
|
+
} catch {
|
|
23
|
+
throw new Error(`Invalid JSON in ${basename(file)}`);
|
|
24
|
+
}
|
|
25
|
+
return validate(value);
|
|
26
|
+
}
|
|
27
|
+
function isNodeError(error) {
|
|
28
|
+
return error instanceof Error && "code" in error;
|
|
29
|
+
}
|
|
30
|
+
function atomicWrite(file, content) {
|
|
31
|
+
const dir = dirname(file);
|
|
32
|
+
mkdirSync(dir, {
|
|
33
|
+
recursive: true,
|
|
34
|
+
mode: 448
|
|
35
|
+
});
|
|
36
|
+
const temporary = `${file}.${process.pid}.${randomBytes(8).toString("hex")}.tmp`;
|
|
37
|
+
let fd;
|
|
38
|
+
try {
|
|
39
|
+
fd = openSync(temporary, "wx", 384);
|
|
40
|
+
writeFileSync(fd, content, "utf8");
|
|
41
|
+
fsyncSync(fd);
|
|
42
|
+
closeSync(fd);
|
|
43
|
+
fd = void 0;
|
|
44
|
+
renameSync(temporary, file);
|
|
45
|
+
chmodSync(file, 384);
|
|
46
|
+
} catch (error) {
|
|
47
|
+
if (fd !== void 0) closeSync(fd);
|
|
48
|
+
try {
|
|
49
|
+
unlinkSync(temporary);
|
|
50
|
+
} catch {}
|
|
51
|
+
throw error;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
function clone(value) {
|
|
55
|
+
return JSON.parse(JSON.stringify(value));
|
|
56
|
+
}
|
|
57
|
+
function instanceIdFile(home) {
|
|
58
|
+
return join(home, "dshb", "instance-id");
|
|
59
|
+
}
|
|
60
|
+
function parseInstanceId(value) {
|
|
61
|
+
const id = value.trim();
|
|
62
|
+
if (!/^i-[a-f0-9]{32}$/.test(id)) throw new Error("Invalid DSHB sync instance ID");
|
|
63
|
+
return id;
|
|
64
|
+
}
|
|
65
|
+
function getOrCreateSyncInstanceId(home) {
|
|
66
|
+
const file = instanceIdFile(dshHome(home));
|
|
67
|
+
try {
|
|
68
|
+
return parseInstanceId(readFileSync(file, "utf8"));
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (!(isNodeError(error) && error.code === "ENOENT")) throw error;
|
|
71
|
+
}
|
|
72
|
+
mkdirSync(dirname(file), {
|
|
73
|
+
recursive: true,
|
|
74
|
+
mode: 448
|
|
75
|
+
});
|
|
76
|
+
const candidate = `i-${randomBytes(16).toString("hex")}`;
|
|
77
|
+
let fd;
|
|
78
|
+
try {
|
|
79
|
+
fd = openSync(file, "wx", 384);
|
|
80
|
+
writeFileSync(fd, `${candidate}\n`, "utf8");
|
|
81
|
+
fsyncSync(fd);
|
|
82
|
+
closeSync(fd);
|
|
83
|
+
fd = void 0;
|
|
84
|
+
chmodSync(file, 384);
|
|
85
|
+
return candidate;
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (fd !== void 0) closeSync(fd);
|
|
88
|
+
if (isNodeError(error) && error.code === "EEXIST") return parseInstanceId(readFileSync(file, "utf8"));
|
|
89
|
+
throw error;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
function newPeerId() {
|
|
93
|
+
return `p-${randomBytes(12).toString("hex")}`;
|
|
94
|
+
}
|
|
95
|
+
function validatePeers(value) {
|
|
96
|
+
if (!Array.isArray(value)) throw new Error("Invalid sync peer store");
|
|
97
|
+
const peers = value.map(parseSyncPeer);
|
|
98
|
+
if (new Set(peers.map((peer) => peer.id)).size !== peers.length) throw new Error("Sync peer store contains duplicate IDs");
|
|
99
|
+
return peers;
|
|
100
|
+
}
|
|
101
|
+
var SyncPeerStore = class {
|
|
102
|
+
peers;
|
|
103
|
+
filePath;
|
|
104
|
+
constructor(home) {
|
|
105
|
+
this.filePath = join(dshHome(home), "dshb", "sync-peers.json");
|
|
106
|
+
}
|
|
107
|
+
list() {
|
|
108
|
+
return clone(this.load());
|
|
109
|
+
}
|
|
110
|
+
get(id) {
|
|
111
|
+
const peer = this.load().find((item) => item.id === id);
|
|
112
|
+
return peer ? clone(peer) : void 0;
|
|
113
|
+
}
|
|
114
|
+
create(draft) {
|
|
115
|
+
const peer = parseSyncPeer({
|
|
116
|
+
...draft,
|
|
117
|
+
id: newPeerId()
|
|
118
|
+
});
|
|
119
|
+
const next = [...this.load(), peer];
|
|
120
|
+
this.save(next);
|
|
121
|
+
return clone(peer);
|
|
122
|
+
}
|
|
123
|
+
update(id, patch) {
|
|
124
|
+
const current = this.load();
|
|
125
|
+
const index = current.findIndex((peer) => peer.id === id);
|
|
126
|
+
if (index < 0) throw new Error(`sync peer ${id} not found`);
|
|
127
|
+
const updated = parseSyncPeer({
|
|
128
|
+
...current[index],
|
|
129
|
+
...patch,
|
|
130
|
+
id
|
|
131
|
+
});
|
|
132
|
+
const next = [...current];
|
|
133
|
+
next[index] = updated;
|
|
134
|
+
this.save(next);
|
|
135
|
+
return clone(updated);
|
|
136
|
+
}
|
|
137
|
+
remove(id) {
|
|
138
|
+
const current = this.load();
|
|
139
|
+
const next = current.filter((peer) => peer.id !== id);
|
|
140
|
+
if (next.length === current.length) return false;
|
|
141
|
+
this.save(next);
|
|
142
|
+
return true;
|
|
143
|
+
}
|
|
144
|
+
load() {
|
|
145
|
+
if (this.peers) return this.peers;
|
|
146
|
+
const stored = readJson(this.filePath, validatePeers);
|
|
147
|
+
this.peers = stored ?? [];
|
|
148
|
+
return this.peers;
|
|
149
|
+
}
|
|
150
|
+
save(peers) {
|
|
151
|
+
atomicWrite(this.filePath, `${JSON.stringify(peers, null, 2)}\n`);
|
|
152
|
+
this.peers = peers;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
function validateProgress(value) {
|
|
156
|
+
if (!isRecord(value)) throw new Error("Invalid peer sync progress");
|
|
157
|
+
const progress = {};
|
|
158
|
+
for (const field of [
|
|
159
|
+
"lastRunAt",
|
|
160
|
+
"lastSuccessAt",
|
|
161
|
+
"lastManifestDigest",
|
|
162
|
+
"watermark",
|
|
163
|
+
"lastErrorCode",
|
|
164
|
+
"lastErrorMessage"
|
|
165
|
+
]) {
|
|
166
|
+
const entry = value[field];
|
|
167
|
+
if (entry !== void 0) {
|
|
168
|
+
if (typeof entry !== "string" || !entry || entry.length > (field === "lastErrorMessage" ? 256 : 512) || /[\u0000-\u001f\u007f]/.test(entry)) throw new Error(`Invalid peer sync progress field: ${field}`);
|
|
169
|
+
progress[field] = entry;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if (value.lastRunStatus !== void 0) {
|
|
173
|
+
if (value.lastRunStatus !== "running" && value.lastRunStatus !== "succeeded" && value.lastRunStatus !== "failed") throw new Error("Invalid peer sync run status");
|
|
174
|
+
progress.lastRunStatus = value.lastRunStatus;
|
|
175
|
+
}
|
|
176
|
+
if (value.lastResult !== void 0) {
|
|
177
|
+
if (!isRecord(value.lastResult)) throw new Error("Invalid peer sync run result");
|
|
178
|
+
for (const field of [
|
|
179
|
+
"imported",
|
|
180
|
+
"exported",
|
|
181
|
+
"skipped",
|
|
182
|
+
"pendingRemoval"
|
|
183
|
+
]) if (!isNonNegativeSafeInteger(value.lastResult[field])) throw new Error(`Invalid peer sync result field: ${field}`);
|
|
184
|
+
for (const field of [
|
|
185
|
+
"importedSessions",
|
|
186
|
+
"exportedSessions",
|
|
187
|
+
"skippedSessions"
|
|
188
|
+
]) if (value.lastResult[field] !== void 0 && !isNonNegativeSafeInteger(value.lastResult[field])) throw new Error(`Invalid peer sync result field: ${field}`);
|
|
189
|
+
progress.lastResult = {
|
|
190
|
+
imported: value.lastResult.imported,
|
|
191
|
+
exported: value.lastResult.exported,
|
|
192
|
+
skipped: value.lastResult.skipped,
|
|
193
|
+
pendingRemoval: value.lastResult.pendingRemoval,
|
|
194
|
+
importedSessions: value.lastResult.importedSessions ?? 0,
|
|
195
|
+
exportedSessions: value.lastResult.exportedSessions ?? 0,
|
|
196
|
+
skippedSessions: value.lastResult.skippedSessions ?? 0
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
return progress;
|
|
200
|
+
}
|
|
201
|
+
function validateRevision(value, label) {
|
|
202
|
+
if (!isRecord(value) || !isNonNegativeSafeInteger(value.physicalTimeMs) || !isNonNegativeSafeInteger(value.logicalCounter) || typeof value.writerInstanceId !== "string" || !value.writerInstanceId || /[\u0000-\u001f\u007f]/.test(value.writerInstanceId)) throw new Error(`Invalid ${label} revision`);
|
|
203
|
+
return {
|
|
204
|
+
physicalTimeMs: value.physicalTimeMs,
|
|
205
|
+
logicalCounter: value.logicalCounter,
|
|
206
|
+
writerInstanceId: value.writerInstanceId
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
function validateRecord(value) {
|
|
210
|
+
if (!isRecord(value)) throw new Error("Invalid sync record metadata");
|
|
211
|
+
if (value.kind !== "node" && value.kind !== "session") throw new Error("Invalid sync entity kind");
|
|
212
|
+
for (const key of [
|
|
213
|
+
"originInstanceId",
|
|
214
|
+
"originEntityId",
|
|
215
|
+
"localEntityId",
|
|
216
|
+
"contentDigest"
|
|
217
|
+
]) if (typeof value[key] !== "string" || !value[key].trim() || value[key].length > 4096 || /[\u0000-\u001f\u007f]/.test(value[key])) throw new Error(`Invalid sync record field: ${key}`);
|
|
218
|
+
if (value.stateDigest !== void 0 && (typeof value.stateDigest !== "string" || !/^sha256:[a-f0-9]{64}$/.test(value.stateDigest))) throw new Error("Invalid sync record state digest");
|
|
219
|
+
validateRevision(value.revision, "sync record");
|
|
220
|
+
if (value.deleted !== void 0 && typeof value.deleted !== "boolean") throw new Error("Invalid sync tombstone");
|
|
221
|
+
if (value.pendingRemoval !== void 0 && typeof value.pendingRemoval !== "boolean") throw new Error("Invalid pending removal state");
|
|
222
|
+
if (value.credentialsRequired !== void 0 && typeof value.credentialsRequired !== "boolean") throw new Error("Invalid sync credential requirement state");
|
|
223
|
+
if (value.workspaceOrigin !== void 0) {
|
|
224
|
+
if (!isRecord(value.workspaceOrigin)) throw new Error("Invalid sync workspace origin");
|
|
225
|
+
validateOrigin(value.workspaceOrigin);
|
|
226
|
+
}
|
|
227
|
+
if (value.importedVia !== void 0 && value.importedVia !== "peer" && value.importedVia !== "offline") throw new Error("Invalid sync import method");
|
|
228
|
+
if (value.sourceName !== void 0 && (typeof value.sourceName !== "string" || value.sourceName.length > 256 || /[\u0000-\u001f\u007f]/.test(value.sourceName))) throw new Error("Invalid sync source name");
|
|
229
|
+
return clone(value);
|
|
230
|
+
}
|
|
231
|
+
function validateWorkspaceMapping(value) {
|
|
232
|
+
if (!isRecord(value)) throw new Error("Invalid sync workspace mapping");
|
|
233
|
+
const originInstanceId = requiredString(value.originInstanceId, "workspace origin instance ID", 128);
|
|
234
|
+
const originWorkspaceId = requiredString(value.originWorkspaceId, "workspace origin ID", 256);
|
|
235
|
+
const localWorkspaceId = requiredString(value.localWorkspaceId, "local workspace ID", 256);
|
|
236
|
+
const localPath = requiredString(value.localPath, "local workspace path", 4096);
|
|
237
|
+
const localNodeId = requiredString(value.localNodeId, "local workspace node ID", 128);
|
|
238
|
+
const remotePath = requiredString(value.remotePath, "workspace remote path", 4096);
|
|
239
|
+
const title = requiredString(value.title, "workspace title", 256);
|
|
240
|
+
if (!posix.isAbsolute(remotePath) || posix.normalize(remotePath) !== remotePath || remotePath.split("/").includes("..")) throw new Error("Invalid workspace remote path");
|
|
241
|
+
const contentDigest = requiredString(value.contentDigest, "workspace content digest", 256);
|
|
242
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(contentDigest)) throw new Error("Invalid workspace content digest");
|
|
243
|
+
return {
|
|
244
|
+
originInstanceId,
|
|
245
|
+
originWorkspaceId,
|
|
246
|
+
localWorkspaceId,
|
|
247
|
+
localPath,
|
|
248
|
+
localNodeId,
|
|
249
|
+
remotePath,
|
|
250
|
+
title,
|
|
251
|
+
revision: validateRevision(value.revision, "workspace"),
|
|
252
|
+
contentDigest
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
function validateArchive(value, branchId) {
|
|
256
|
+
if (!isRecord(value) || value.branchId !== branchId || !/^b-[a-f0-9]{32}$/.test(branchId)) throw new Error("Invalid sync session archive");
|
|
257
|
+
const contentDigest = requiredString(value.contentDigest, "archive content digest", 256);
|
|
258
|
+
const stateDigest = value.stateDigest === void 0 ? contentDigest : requiredString(value.stateDigest, "archive state digest", 256);
|
|
259
|
+
const workspacePosition = value.workspacePosition === void 0 ? 0 : value.workspacePosition;
|
|
260
|
+
if (!/^sha256:[a-f0-9]{64}$/.test(contentDigest) || !/^sha256:[a-f0-9]{64}$/.test(stateDigest) || !isNonNegativeSafeInteger(workspacePosition)) throw new Error("Invalid sync session archive digest or order");
|
|
261
|
+
return {
|
|
262
|
+
branchId,
|
|
263
|
+
originInstanceId: requiredString(value.originInstanceId, "archive origin instance ID", 128),
|
|
264
|
+
originSessionId: requiredString(value.originSessionId, "archive origin session ID", 256),
|
|
265
|
+
localSessionId: requiredString(value.localSessionId, "archive local session ID", 256),
|
|
266
|
+
revision: validateRevision(value.revision, "archive"),
|
|
267
|
+
contentDigest,
|
|
268
|
+
stateDigest,
|
|
269
|
+
workspacePosition,
|
|
270
|
+
...value.workspaceId === void 0 ? {} : { workspaceId: requiredString(value.workspaceId, "archive workspace ID", 256) },
|
|
271
|
+
...value.workspaceOrigin === void 0 ? {} : { workspaceOrigin: validateOrigin(value.workspaceOrigin) },
|
|
272
|
+
archivedAt: requiredString(value.archivedAt, "archive timestamp", 64)
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
function validateOrigin(value) {
|
|
276
|
+
if (!isRecord(value) || typeof value.originInstanceId !== "string" || !value.originInstanceId || typeof value.originEntityId !== "string" || !value.originEntityId || value.originInstanceId.length > 512 || value.originEntityId.length > 512 || /[\u0000-\u001f\u007f]/.test(value.originInstanceId) || /[\u0000-\u001f\u007f]/.test(value.originEntityId)) throw new Error("Invalid sync origin reference");
|
|
277
|
+
return {
|
|
278
|
+
originInstanceId: value.originInstanceId,
|
|
279
|
+
originEntityId: value.originEntityId
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
function requiredString(value, label, maxLength) {
|
|
283
|
+
if (typeof value !== "string" || !value.trim() || value.length > maxLength || /[\u0000-\u001f\u007f]/.test(value)) throw new Error(`Invalid ${label}`);
|
|
284
|
+
return value;
|
|
285
|
+
}
|
|
286
|
+
function validateSyncState(value) {
|
|
287
|
+
if (!isRecord(value) || value.version !== 1) throw new Error("Unsupported sync state version");
|
|
288
|
+
if (!isRecord(value.clock) || !isNonNegativeSafeInteger(value.clock.physicalTimeMs) || !isNonNegativeSafeInteger(value.clock.logicalCounter)) throw new Error("Invalid sync clock state");
|
|
289
|
+
if (!isRecord(value.peers) || !isRecord(value.records)) throw new Error("Invalid sync state maps");
|
|
290
|
+
const workspaceValues = value.workspaces === void 0 ? {} : value.workspaces;
|
|
291
|
+
const archiveValues = value.archives === void 0 ? {} : value.archives;
|
|
292
|
+
if (!isRecord(workspaceValues) || !isRecord(archiveValues)) throw new Error("Invalid sync session state maps");
|
|
293
|
+
const peers = Object.create(null);
|
|
294
|
+
for (const [id, progress] of Object.entries(value.peers)) {
|
|
295
|
+
if (!/^p-[a-f0-9]{24}$/.test(id)) throw new Error("Invalid sync peer ID in state");
|
|
296
|
+
peers[id] = validateProgress(progress);
|
|
297
|
+
}
|
|
298
|
+
const records = Object.create(null);
|
|
299
|
+
for (const [key, metadata] of Object.entries(value.records)) {
|
|
300
|
+
const validated = validateRecord(metadata);
|
|
301
|
+
if (key !== syncRecordKey(validated.kind, validated)) throw new Error("Sync record key does not match its origin");
|
|
302
|
+
records[key] = validated;
|
|
303
|
+
}
|
|
304
|
+
const workspaces = Object.create(null);
|
|
305
|
+
for (const [key, mapping] of Object.entries(workspaceValues)) {
|
|
306
|
+
const validated = validateWorkspaceMapping(mapping);
|
|
307
|
+
if (key !== syncWorkspaceKey(validated.originInstanceId, validated.originWorkspaceId, validated.remotePath)) throw new Error("Sync workspace mapping key does not match its origin");
|
|
308
|
+
workspaces[key] = validated;
|
|
309
|
+
}
|
|
310
|
+
const archives = Object.create(null);
|
|
311
|
+
for (const [branchId, archive] of Object.entries(archiveValues)) archives[branchId] = validateArchive(archive, branchId);
|
|
312
|
+
return {
|
|
313
|
+
version: 1,
|
|
314
|
+
clock: {
|
|
315
|
+
physicalTimeMs: value.clock.physicalTimeMs,
|
|
316
|
+
logicalCounter: value.clock.logicalCounter
|
|
317
|
+
},
|
|
318
|
+
peers,
|
|
319
|
+
records,
|
|
320
|
+
workspaces,
|
|
321
|
+
archives
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
function emptySyncState() {
|
|
325
|
+
return {
|
|
326
|
+
version: 1,
|
|
327
|
+
clock: {
|
|
328
|
+
physicalTimeMs: 0,
|
|
329
|
+
logicalCounter: 0
|
|
330
|
+
},
|
|
331
|
+
peers: {},
|
|
332
|
+
records: {},
|
|
333
|
+
workspaces: {},
|
|
334
|
+
archives: {}
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
var SyncStateStore = class {
|
|
338
|
+
state;
|
|
339
|
+
home;
|
|
340
|
+
filePath;
|
|
341
|
+
constructor(home) {
|
|
342
|
+
this.home = dshHome(home);
|
|
343
|
+
this.filePath = join(this.home, "dshb", "sync-state.json");
|
|
344
|
+
}
|
|
345
|
+
instanceId() {
|
|
346
|
+
return getOrCreateSyncInstanceId(this.home);
|
|
347
|
+
}
|
|
348
|
+
read() {
|
|
349
|
+
return clone(this.load());
|
|
350
|
+
}
|
|
351
|
+
update(mutate) {
|
|
352
|
+
const next = clone(this.load());
|
|
353
|
+
mutate(next);
|
|
354
|
+
const validated = validateSyncState(next);
|
|
355
|
+
atomicWrite(this.filePath, `${JSON.stringify(validated, null, 2)}\n`);
|
|
356
|
+
this.state = validated;
|
|
357
|
+
return clone(validated);
|
|
358
|
+
}
|
|
359
|
+
nextRevision(now = Date.now()) {
|
|
360
|
+
const clock = new HybridLogicalClock(this.instanceId(), this.load().clock);
|
|
361
|
+
const revision = clock.tick(now);
|
|
362
|
+
this.update((state) => {
|
|
363
|
+
state.clock = clock.snapshot();
|
|
364
|
+
});
|
|
365
|
+
return revision;
|
|
366
|
+
}
|
|
367
|
+
observeRevision(remote, now = Date.now()) {
|
|
368
|
+
const clock = new HybridLogicalClock(this.instanceId(), this.load().clock);
|
|
369
|
+
const revision = clock.observe(remote, now);
|
|
370
|
+
this.update((state) => {
|
|
371
|
+
state.clock = clock.snapshot();
|
|
372
|
+
});
|
|
373
|
+
return revision;
|
|
374
|
+
}
|
|
375
|
+
load() {
|
|
376
|
+
if (this.state) return this.state;
|
|
377
|
+
const state = readJson(this.filePath, validateSyncState);
|
|
378
|
+
this.state = state ?? emptySyncState();
|
|
379
|
+
return this.state;
|
|
380
|
+
}
|
|
381
|
+
};
|
|
382
|
+
//#endregion
|
|
383
|
+
export { SyncPeerStore, SyncStateStore, getOrCreateSyncInstanceId };
|
|
384
|
+
|
|
385
|
+
//# sourceMappingURL=stores.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"stores.js","names":[],"sources":["../../../src/core/sync/stores.ts"],"sourcesContent":["import { randomBytes } from 'node:crypto'\nimport {\n chmodSync,\n closeSync,\n fsyncSync,\n mkdirSync,\n openSync,\n readFileSync,\n renameSync,\n unlinkSync,\n writeFileSync,\n} from 'node:fs'\nimport { homedir } from 'node:os'\nimport { basename, dirname, join, posix } from 'node:path'\nimport { HybridLogicalClock, type HlcSnapshot, type SyncRevision } from './revision.js'\nimport {\n isNonNegativeSafeInteger,\n isRecord,\n parseSyncPeer,\n syncRecordKey,\n syncWorkspaceKey,\n type SyncPeer,\n type SyncPeerDraft,\n type SyncPeerProgress,\n type SyncRecordMetadata,\n type SyncSessionArchive,\n type SyncState,\n type SyncWorkspaceMapping,\n} from './model.js'\n\nfunction dshHome(home?: string): string {\n return home ?? process.env.DSH_HOME ?? join(homedir(), '.dsh')\n}\n\nfunction readJson<T>(file: string, validate: (value: unknown) => T): T | undefined {\n let content: string\n try {\n content = readFileSync(file, 'utf8')\n } catch (error) {\n if (isNodeError(error) && error.code === 'ENOENT') return undefined\n throw error\n }\n let value: unknown\n try {\n value = JSON.parse(content)\n } catch {\n throw new Error(`Invalid JSON in ${basename(file)}`)\n }\n return validate(value)\n}\n\nfunction isNodeError(error: unknown): error is NodeJS.ErrnoException {\n return error instanceof Error && 'code' in error\n}\n\nfunction atomicWrite(file: string, content: string): void {\n const dir = dirname(file)\n mkdirSync(dir, { recursive: true, mode: 0o700 })\n const temporary = `${file}.${process.pid}.${randomBytes(8).toString('hex')}.tmp`\n let fd: number | undefined\n try {\n fd = openSync(temporary, 'wx', 0o600)\n writeFileSync(fd, content, 'utf8')\n fsyncSync(fd)\n closeSync(fd)\n fd = undefined\n renameSync(temporary, file)\n chmodSync(file, 0o600)\n } catch (error) {\n if (fd !== undefined) closeSync(fd)\n try {\n unlinkSync(temporary)\n } catch {}\n throw error\n }\n}\n\nfunction clone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T\n}\n\nfunction instanceIdFile(home: string): string {\n return join(home, 'dshb', 'instance-id')\n}\n\nfunction parseInstanceId(value: string): string {\n const id = value.trim()\n if (!/^i-[a-f0-9]{32}$/.test(id)) throw new Error('Invalid DSHB sync instance ID')\n return id\n}\n\nexport function getOrCreateSyncInstanceId(home?: string): string {\n const root = dshHome(home)\n const file = instanceIdFile(root)\n try {\n return parseInstanceId(readFileSync(file, 'utf8'))\n } catch (error) {\n if (!(isNodeError(error) && error.code === 'ENOENT')) throw error\n }\n\n mkdirSync(dirname(file), { recursive: true, mode: 0o700 })\n const candidate = `i-${randomBytes(16).toString('hex')}`\n let fd: number | undefined\n try {\n fd = openSync(file, 'wx', 0o600)\n writeFileSync(fd, `${candidate}\\n`, 'utf8')\n fsyncSync(fd)\n closeSync(fd)\n fd = undefined\n chmodSync(file, 0o600)\n return candidate\n } catch (error) {\n if (fd !== undefined) closeSync(fd)\n if (isNodeError(error) && error.code === 'EEXIST') {\n return parseInstanceId(readFileSync(file, 'utf8'))\n }\n throw error\n }\n}\n\nfunction newPeerId(): string {\n return `p-${randomBytes(12).toString('hex')}`\n}\n\nfunction validatePeers(value: unknown): SyncPeer[] {\n if (!Array.isArray(value)) throw new Error('Invalid sync peer store')\n const peers = value.map(parseSyncPeer)\n if (new Set(peers.map((peer) => peer.id)).size !== peers.length) {\n throw new Error('Sync peer store contains duplicate IDs')\n }\n return peers\n}\n\nexport class SyncPeerStore {\n private peers: SyncPeer[] | undefined\n private readonly filePath: string\n\n constructor(home?: string) {\n this.filePath = join(dshHome(home), 'dshb', 'sync-peers.json')\n }\n\n list(): SyncPeer[] {\n return clone(this.load())\n }\n\n get(id: string): SyncPeer | undefined {\n const peer = this.load().find((item) => item.id === id)\n return peer ? clone(peer) : undefined\n }\n\n create(draft: SyncPeerDraft): SyncPeer {\n const peer = parseSyncPeer({ ...draft, id: newPeerId() })\n const next = [...this.load(), peer]\n this.save(next)\n return clone(peer)\n }\n\n update(id: string, patch: Partial<Omit<SyncPeer, 'id'>>): SyncPeer {\n const current = this.load()\n const index = current.findIndex((peer) => peer.id === id)\n if (index < 0) throw new Error(`sync peer ${id} not found`)\n const updated = parseSyncPeer({ ...current[index], ...patch, id })\n const next = [...current]\n next[index] = updated\n this.save(next)\n return clone(updated)\n }\n\n remove(id: string): boolean {\n const current = this.load()\n const next = current.filter((peer) => peer.id !== id)\n if (next.length === current.length) return false\n this.save(next)\n return true\n }\n\n private load(): SyncPeer[] {\n if (this.peers) return this.peers\n const stored = readJson(this.filePath, validatePeers)\n this.peers = stored ?? []\n return this.peers\n }\n\n private save(peers: SyncPeer[]): void {\n atomicWrite(this.filePath, `${JSON.stringify(peers, null, 2)}\\n`)\n this.peers = peers\n }\n}\n\nfunction validateProgress(value: unknown): SyncPeerProgress {\n if (!isRecord(value)) throw new Error('Invalid peer sync progress')\n const progress: SyncPeerProgress = {}\n for (const field of ['lastRunAt', 'lastSuccessAt', 'lastManifestDigest', 'watermark', 'lastErrorCode', 'lastErrorMessage'] as const) {\n const entry = value[field]\n if (entry !== undefined) {\n if (\n typeof entry !== 'string' || !entry ||\n entry.length > (field === 'lastErrorMessage' ? 256 : 512) ||\n /[\\u0000-\\u001f\\u007f]/.test(entry)\n ) {\n throw new Error(`Invalid peer sync progress field: ${field}`)\n }\n progress[field] = entry\n }\n }\n if (value.lastRunStatus !== undefined) {\n if (value.lastRunStatus !== 'running' && value.lastRunStatus !== 'succeeded' && value.lastRunStatus !== 'failed') {\n throw new Error('Invalid peer sync run status')\n }\n progress.lastRunStatus = value.lastRunStatus\n }\n if (value.lastResult !== undefined) {\n if (!isRecord(value.lastResult)) throw new Error('Invalid peer sync run result')\n const counts = ['imported', 'exported', 'skipped', 'pendingRemoval'] as const\n for (const field of counts) {\n if (!isNonNegativeSafeInteger(value.lastResult[field])) throw new Error(`Invalid peer sync result field: ${field}`)\n }\n const sessionCounts = ['importedSessions', 'exportedSessions', 'skippedSessions'] as const\n for (const field of sessionCounts) {\n if (value.lastResult[field] !== undefined && !isNonNegativeSafeInteger(value.lastResult[field])) {\n throw new Error(`Invalid peer sync result field: ${field}`)\n }\n }\n progress.lastResult = {\n imported: value.lastResult.imported as number,\n exported: value.lastResult.exported as number,\n skipped: value.lastResult.skipped as number,\n pendingRemoval: value.lastResult.pendingRemoval as number,\n importedSessions: (value.lastResult.importedSessions as number | undefined) ?? 0,\n exportedSessions: (value.lastResult.exportedSessions as number | undefined) ?? 0,\n skippedSessions: (value.lastResult.skippedSessions as number | undefined) ?? 0,\n }\n }\n return progress\n}\n\nfunction validateRevision(value: unknown, label: string): SyncRevision {\n if (\n !isRecord(value) ||\n !isNonNegativeSafeInteger(value.physicalTimeMs) ||\n !isNonNegativeSafeInteger(value.logicalCounter) ||\n typeof value.writerInstanceId !== 'string' ||\n !value.writerInstanceId ||\n /[\\u0000-\\u001f\\u007f]/.test(value.writerInstanceId)\n ) {\n throw new Error(`Invalid ${label} revision`)\n }\n return {\n physicalTimeMs: value.physicalTimeMs,\n logicalCounter: value.logicalCounter,\n writerInstanceId: value.writerInstanceId,\n }\n}\n\nfunction validateRecord(value: unknown): SyncRecordMetadata {\n if (!isRecord(value)) throw new Error('Invalid sync record metadata')\n if (value.kind !== 'node' && value.kind !== 'session') throw new Error('Invalid sync entity kind')\n const requiredText = ['originInstanceId', 'originEntityId', 'localEntityId', 'contentDigest'] as const\n for (const key of requiredText) {\n if (\n typeof value[key] !== 'string' ||\n !(value[key] as string).trim() ||\n (value[key] as string).length > 4096 ||\n /[\\u0000-\\u001f\\u007f]/.test(value[key] as string)\n ) {\n throw new Error(`Invalid sync record field: ${key}`)\n }\n }\n if (value.stateDigest !== undefined && (typeof value.stateDigest !== 'string' || !/^sha256:[a-f0-9]{64}$/.test(value.stateDigest))) {\n throw new Error('Invalid sync record state digest')\n }\n validateRevision(value.revision, 'sync record')\n if (value.deleted !== undefined && typeof value.deleted !== 'boolean') throw new Error('Invalid sync tombstone')\n if (value.pendingRemoval !== undefined && typeof value.pendingRemoval !== 'boolean') {\n throw new Error('Invalid pending removal state')\n }\n if (value.credentialsRequired !== undefined && typeof value.credentialsRequired !== 'boolean') {\n throw new Error('Invalid sync credential requirement state')\n }\n if (value.workspaceOrigin !== undefined) {\n if (!isRecord(value.workspaceOrigin)) throw new Error('Invalid sync workspace origin')\n validateOrigin(value.workspaceOrigin)\n }\n if (value.importedVia !== undefined && value.importedVia !== 'peer' && value.importedVia !== 'offline') {\n throw new Error('Invalid sync import method')\n }\n if (\n value.sourceName !== undefined &&\n (typeof value.sourceName !== 'string' || value.sourceName.length > 256 || /[\\u0000-\\u001f\\u007f]/.test(value.sourceName))\n ) {\n throw new Error('Invalid sync source name')\n }\n return clone(value) as unknown as SyncRecordMetadata\n}\n\nfunction validateWorkspaceMapping(value: unknown): SyncWorkspaceMapping {\n if (!isRecord(value)) throw new Error('Invalid sync workspace mapping')\n const originInstanceId = requiredString(value.originInstanceId, 'workspace origin instance ID', 128)\n const originWorkspaceId = requiredString(value.originWorkspaceId, 'workspace origin ID', 256)\n const localWorkspaceId = requiredString(value.localWorkspaceId, 'local workspace ID', 256)\n const localPath = requiredString(value.localPath, 'local workspace path', 4096)\n const localNodeId = requiredString(value.localNodeId, 'local workspace node ID', 128)\n const remotePath = requiredString(value.remotePath, 'workspace remote path', 4096)\n const title = requiredString(value.title, 'workspace title', 256)\n if (!posix.isAbsolute(remotePath) || posix.normalize(remotePath) !== remotePath || remotePath.split('/').includes('..')) {\n throw new Error('Invalid workspace remote path')\n }\n const contentDigest = requiredString(value.contentDigest, 'workspace content digest', 256)\n if (!/^sha256:[a-f0-9]{64}$/.test(contentDigest)) throw new Error('Invalid workspace content digest')\n return {\n originInstanceId,\n originWorkspaceId,\n localWorkspaceId,\n localPath,\n localNodeId,\n remotePath,\n title,\n revision: validateRevision(value.revision, 'workspace'),\n contentDigest,\n }\n}\n\nfunction validateArchive(value: unknown, branchId: string): SyncSessionArchive {\n if (!isRecord(value) || value.branchId !== branchId || !/^b-[a-f0-9]{32}$/.test(branchId)) {\n throw new Error('Invalid sync session archive')\n }\n const contentDigest = requiredString(value.contentDigest, 'archive content digest', 256)\n const stateDigest = value.stateDigest === undefined\n ? contentDigest\n : requiredString(value.stateDigest, 'archive state digest', 256)\n const workspacePosition = value.workspacePosition === undefined ? 0 : value.workspacePosition\n if (!/^sha256:[a-f0-9]{64}$/.test(contentDigest) || !/^sha256:[a-f0-9]{64}$/.test(stateDigest) ||\n !isNonNegativeSafeInteger(workspacePosition)) throw new Error('Invalid sync session archive digest or order')\n return {\n branchId,\n originInstanceId: requiredString(value.originInstanceId, 'archive origin instance ID', 128),\n originSessionId: requiredString(value.originSessionId, 'archive origin session ID', 256),\n localSessionId: requiredString(value.localSessionId, 'archive local session ID', 256),\n revision: validateRevision(value.revision, 'archive'),\n contentDigest,\n stateDigest,\n workspacePosition,\n ...(value.workspaceId === undefined ? {} : { workspaceId: requiredString(value.workspaceId, 'archive workspace ID', 256) }),\n ...(value.workspaceOrigin === undefined ? {} : { workspaceOrigin: validateOrigin(value.workspaceOrigin) }),\n archivedAt: requiredString(value.archivedAt, 'archive timestamp', 64),\n }\n}\n\nfunction validateOrigin(value: unknown): { originInstanceId: string; originEntityId: string } {\n if (\n !isRecord(value) || typeof value.originInstanceId !== 'string' || !value.originInstanceId ||\n typeof value.originEntityId !== 'string' || !value.originEntityId ||\n value.originInstanceId.length > 512 || value.originEntityId.length > 512 ||\n /[\\u0000-\\u001f\\u007f]/.test(value.originInstanceId) || /[\\u0000-\\u001f\\u007f]/.test(value.originEntityId)\n ) throw new Error('Invalid sync origin reference')\n return { originInstanceId: value.originInstanceId, originEntityId: value.originEntityId }\n}\n\nfunction requiredString(value: unknown, label: string, maxLength: number): string {\n if (typeof value !== 'string' || !value.trim() || value.length > maxLength || /[\\u0000-\\u001f\\u007f]/.test(value)) {\n throw new Error(`Invalid ${label}`)\n }\n return value\n}\n\nfunction validateSyncState(value: unknown): SyncState {\n if (!isRecord(value) || value.version !== 1) throw new Error('Unsupported sync state version')\n if (\n !isRecord(value.clock) ||\n !isNonNegativeSafeInteger(value.clock.physicalTimeMs) ||\n !isNonNegativeSafeInteger(value.clock.logicalCounter)\n ) {\n throw new Error('Invalid sync clock state')\n }\n if (!isRecord(value.peers) || !isRecord(value.records)) throw new Error('Invalid sync state maps')\n const workspaceValues = value.workspaces === undefined ? {} : value.workspaces\n const archiveValues = value.archives === undefined ? {} : value.archives\n if (!isRecord(workspaceValues) || !isRecord(archiveValues)) throw new Error('Invalid sync session state maps')\n\n const peers = Object.create(null) as Record<string, SyncPeerProgress>\n for (const [id, progress] of Object.entries(value.peers)) {\n if (!/^p-[a-f0-9]{24}$/.test(id)) throw new Error('Invalid sync peer ID in state')\n peers[id] = validateProgress(progress)\n }\n\n const records = Object.create(null) as Record<string, SyncRecordMetadata>\n for (const [key, metadata] of Object.entries(value.records)) {\n const validated = validateRecord(metadata)\n if (key !== syncRecordKey(validated.kind, validated)) throw new Error('Sync record key does not match its origin')\n records[key] = validated\n }\n\n const workspaces = Object.create(null) as Record<string, SyncWorkspaceMapping>\n for (const [key, mapping] of Object.entries(workspaceValues)) {\n const validated = validateWorkspaceMapping(mapping)\n if (key !== syncWorkspaceKey(validated.originInstanceId, validated.originWorkspaceId, validated.remotePath)) {\n throw new Error('Sync workspace mapping key does not match its origin')\n }\n workspaces[key] = validated\n }\n\n const archives = Object.create(null) as Record<string, SyncSessionArchive>\n for (const [branchId, archive] of Object.entries(archiveValues)) archives[branchId] = validateArchive(archive, branchId)\n\n return {\n version: 1,\n clock: { physicalTimeMs: value.clock.physicalTimeMs, logicalCounter: value.clock.logicalCounter },\n peers,\n records,\n workspaces,\n archives,\n }\n}\n\nfunction emptySyncState(): SyncState {\n return { version: 1, clock: { physicalTimeMs: 0, logicalCounter: 0 }, peers: {}, records: {}, workspaces: {}, archives: {} }\n}\n\nexport class SyncStateStore {\n private state: SyncState | undefined\n private readonly home: string\n private readonly filePath: string\n\n constructor(home?: string) {\n this.home = dshHome(home)\n this.filePath = join(this.home, 'dshb', 'sync-state.json')\n }\n\n instanceId(): string {\n return getOrCreateSyncInstanceId(this.home)\n }\n\n read(): SyncState {\n return clone(this.load())\n }\n\n update(mutate: (draft: SyncState) => void): SyncState {\n const next = clone(this.load())\n mutate(next)\n const validated = validateSyncState(next)\n atomicWrite(this.filePath, `${JSON.stringify(validated, null, 2)}\\n`)\n this.state = validated\n return clone(validated)\n }\n\n nextRevision(now = Date.now()): SyncRevision {\n const clock = new HybridLogicalClock(this.instanceId(), this.load().clock)\n const revision = clock.tick(now)\n this.update((state) => {\n state.clock = clock.snapshot()\n })\n return revision\n }\n\n observeRevision(remote: SyncRevision, now = Date.now()): SyncRevision {\n const clock = new HybridLogicalClock(this.instanceId(), this.load().clock)\n const revision = clock.observe(remote, now)\n this.update((state) => {\n state.clock = clock.snapshot()\n })\n return revision\n }\n\n private load(): SyncState {\n if (this.state) return this.state\n const state = readJson(this.filePath, validateSyncState)\n this.state = state ?? emptySyncState()\n return this.state\n }\n}\n\nexport type { HlcSnapshot }\n"],"mappings":"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"}
|
package/lib/core/test.d.ts
CHANGED
|
@@ -1,28 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
mirrorPath: string;
|
|
4
|
-
nodeId: string;
|
|
5
|
-
remotePath: string;
|
|
6
|
-
workspaceId?: string;
|
|
7
|
-
}
|
|
8
|
-
interface BindingHit {
|
|
9
|
-
nodeId: string;
|
|
10
|
-
remotePath: string;
|
|
11
|
-
}
|
|
12
|
-
declare class WorkspaceBindingsStore {
|
|
13
|
-
private bindings;
|
|
14
|
-
private loaded;
|
|
15
|
-
private file;
|
|
16
|
-
private load;
|
|
17
|
-
private save;
|
|
18
|
-
add(binding: Omit<WorkspaceBinding, 'mirrorPath'> & {
|
|
19
|
-
mirrorPath: string;
|
|
20
|
-
}): void;
|
|
21
|
-
remove(mirrorPath: string): void;
|
|
22
|
-
list(): WorkspaceBinding[];
|
|
23
|
-
resolve(path: string): BindingHit | undefined;
|
|
24
|
-
mirrorRoot(nodeId: string, slug: string): string;
|
|
25
|
-
}
|
|
26
|
-
//#endregion
|
|
27
|
-
export { BindingHit, WorkspaceBinding, WorkspaceBindingsStore };
|
|
28
|
-
//# sourceMappingURL=workspace-bindings.d.ts.map
|
|
1
|
+
import { n as WorkspaceBinding, r as WorkspaceBindingsStore, t as BindingHit } from "../workspace-bindings-CsB0TIJB.js";
|
|
2
|
+
export { BindingHit, WorkspaceBinding, WorkspaceBindingsStore };
|
|
@@ -5,7 +5,7 @@ import { join } from "node:path";
|
|
|
5
5
|
function dshHome() {
|
|
6
6
|
return process.env.DSH_HOME ?? join(homedir(), ".dsh");
|
|
7
7
|
}
|
|
8
|
-
function normalize(p) {
|
|
8
|
+
function normalize$1(p) {
|
|
9
9
|
return p.replace(/\/{2,}/g, "/").replace(/\/+$/, "") || "/";
|
|
10
10
|
}
|
|
11
11
|
var WorkspaceBindingsStore = class {
|
|
@@ -20,8 +20,8 @@ var WorkspaceBindingsStore = class {
|
|
|
20
20
|
try {
|
|
21
21
|
this.bindings = JSON.parse(readFileSync(this.file(), "utf8")).map((b) => ({
|
|
22
22
|
...b,
|
|
23
|
-
mirrorPath: normalize(b.mirrorPath),
|
|
24
|
-
remotePath: normalize(b.remotePath)
|
|
23
|
+
mirrorPath: normalize$1(b.mirrorPath),
|
|
24
|
+
remotePath: normalize$1(b.remotePath)
|
|
25
25
|
}));
|
|
26
26
|
} catch {
|
|
27
27
|
this.bindings = [];
|
|
@@ -40,19 +40,19 @@ var WorkspaceBindingsStore = class {
|
|
|
40
40
|
}
|
|
41
41
|
add(binding) {
|
|
42
42
|
this.load();
|
|
43
|
-
const mirrorPath = normalize(binding.mirrorPath);
|
|
44
|
-
this.bindings = this.bindings.filter((b) => normalize(b.mirrorPath) !== mirrorPath);
|
|
43
|
+
const mirrorPath = normalize$1(binding.mirrorPath);
|
|
44
|
+
this.bindings = this.bindings.filter((b) => normalize$1(b.mirrorPath) !== mirrorPath);
|
|
45
45
|
this.bindings.push({
|
|
46
46
|
...binding,
|
|
47
47
|
mirrorPath,
|
|
48
|
-
remotePath: normalize(binding.remotePath)
|
|
48
|
+
remotePath: normalize$1(binding.remotePath)
|
|
49
49
|
});
|
|
50
50
|
this.save();
|
|
51
51
|
}
|
|
52
52
|
remove(mirrorPath) {
|
|
53
53
|
this.load();
|
|
54
|
-
const target = normalize(mirrorPath);
|
|
55
|
-
this.bindings = this.bindings.filter((b) => normalize(b.mirrorPath) !== target);
|
|
54
|
+
const target = normalize$1(mirrorPath);
|
|
55
|
+
this.bindings = this.bindings.filter((b) => normalize$1(b.mirrorPath) !== target);
|
|
56
56
|
this.save();
|
|
57
57
|
}
|
|
58
58
|
list() {
|
|
@@ -61,21 +61,21 @@ var WorkspaceBindingsStore = class {
|
|
|
61
61
|
}
|
|
62
62
|
resolve(path) {
|
|
63
63
|
this.load();
|
|
64
|
-
const target = normalize(path);
|
|
64
|
+
const target = normalize$1(path);
|
|
65
65
|
let best;
|
|
66
66
|
for (const b of this.bindings) {
|
|
67
|
-
const root = normalize(b.mirrorPath);
|
|
67
|
+
const root = normalize$1(b.mirrorPath);
|
|
68
68
|
if (target === root || target.startsWith(`${root}/`)) {
|
|
69
|
-
if (!best || root.length > normalize(best.mirrorPath).length) best = b;
|
|
69
|
+
if (!best || root.length > normalize$1(best.mirrorPath).length) best = b;
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
if (!best) return void 0;
|
|
73
|
-
const root = normalize(best.mirrorPath);
|
|
74
|
-
const remoteRoot = normalize(best.remotePath);
|
|
73
|
+
const root = normalize$1(best.mirrorPath);
|
|
74
|
+
const remoteRoot = normalize$1(best.remotePath);
|
|
75
75
|
const suffix = target === root ? "" : target.slice(root.length);
|
|
76
76
|
return {
|
|
77
77
|
nodeId: best.nodeId,
|
|
78
|
-
remotePath: normalize(`${remoteRoot}${suffix}`)
|
|
78
|
+
remotePath: normalize$1(`${remoteRoot}${suffix}`)
|
|
79
79
|
};
|
|
80
80
|
}
|
|
81
81
|
mirrorRoot(nodeId, slug) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workspace-bindings.js","names":[],"sources":["../../src/core/workspace-bindings.ts"],"sourcesContent":["import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\nexport interface WorkspaceBinding {\n mirrorPath: string\n nodeId: string\n remotePath: string\n workspaceId?: string\n}\n\nexport interface BindingHit {\n nodeId: string\n remotePath: string\n}\n\nfunction dshHome(): string {\n return process.env.DSH_HOME ?? join(homedir(), '.dsh')\n}\n\nfunction normalize(p: string): string {\n return p.replace(/\\/{2,}/g, '/').replace(/\\/+$/, '') || '/'\n}\n\nexport class WorkspaceBindingsStore {\n private bindings: WorkspaceBinding[] = []\n private loaded = false\n\n private file(): string {\n return join(dshHome(), 'dshb', 'workspace-bindings.json')\n }\n\n private load(): void {\n if (this.loaded) return\n this.loaded = true\n try {\n this.bindings = (JSON.parse(readFileSync(this.file(), 'utf8')) as WorkspaceBinding[]).map((b) => ({\n ...b,\n mirrorPath: normalize(b.mirrorPath),\n remotePath: normalize(b.remotePath),\n }))\n } catch {\n this.bindings = []\n }\n }\n\n private save(): void {\n const file = this.file()\n mkdirSync(join(file, '..'), { recursive: true, mode: 0o700 })\n const tmp = `${file}.${process.pid}.tmp`\n writeFileSync(tmp, JSON.stringify(this.bindings, null, 2), { mode: 0o600 })\n renameSync(tmp, file)\n chmodSync(file, 0o600)\n }\n\n add(binding: Omit<WorkspaceBinding, 'mirrorPath'> & { mirrorPath: string }): void {\n this.load()\n const mirrorPath = normalize(binding.mirrorPath)\n this.bindings = this.bindings.filter((b) => normalize(b.mirrorPath) !== mirrorPath)\n this.bindings.push({ ...binding, mirrorPath, remotePath: normalize(binding.remotePath) })\n this.save()\n }\n\n remove(mirrorPath: string): void {\n this.load()\n const target = normalize(mirrorPath)\n this.bindings = this.bindings.filter((b) => normalize(b.mirrorPath) !== target)\n this.save()\n }\n\n list(): WorkspaceBinding[] {\n this.load()\n return this.bindings.map((b) => ({ ...b }))\n }\n\n resolve(path: string): BindingHit | undefined {\n this.load()\n const target = normalize(path)\n let best: WorkspaceBinding | undefined\n for (const b of this.bindings) {\n const root = normalize(b.mirrorPath)\n if (target === root || target.startsWith(`${root}/`)) {\n if (!best || root.length > normalize(best.mirrorPath).length) best = b\n }\n }\n if (!best) return undefined\n const root = normalize(best.mirrorPath)\n const remoteRoot = normalize(best.remotePath)\n const suffix = target === root ? '' : target.slice(root.length)\n return { nodeId: best.nodeId, remotePath: normalize(`${remoteRoot}${suffix}`) }\n }\n\n mirrorRoot(nodeId: string, slug: string): string {\n return join(dshHome(), 'dshb', 'mirrors', nodeId, slug)\n }\n}\n"],"mappings":";;;;AAgBA,SAAS,UAAkB;CACzB,OAAO,QAAQ,IAAI,YAAY,KAAK,QAAQ,GAAG,MAAM;AACvD;AAEA,
|
|
1
|
+
{"version":3,"file":"workspace-bindings.js","names":["normalize"],"sources":["../../src/core/workspace-bindings.ts"],"sourcesContent":["import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'\nimport { homedir } from 'node:os'\nimport { join } from 'node:path'\n\nexport interface WorkspaceBinding {\n mirrorPath: string\n nodeId: string\n remotePath: string\n workspaceId?: string\n}\n\nexport interface BindingHit {\n nodeId: string\n remotePath: string\n}\n\nfunction dshHome(): string {\n return process.env.DSH_HOME ?? join(homedir(), '.dsh')\n}\n\nfunction normalize(p: string): string {\n return p.replace(/\\/{2,}/g, '/').replace(/\\/+$/, '') || '/'\n}\n\nexport class WorkspaceBindingsStore {\n private bindings: WorkspaceBinding[] = []\n private loaded = false\n\n private file(): string {\n return join(dshHome(), 'dshb', 'workspace-bindings.json')\n }\n\n private load(): void {\n if (this.loaded) return\n this.loaded = true\n try {\n this.bindings = (JSON.parse(readFileSync(this.file(), 'utf8')) as WorkspaceBinding[]).map((b) => ({\n ...b,\n mirrorPath: normalize(b.mirrorPath),\n remotePath: normalize(b.remotePath),\n }))\n } catch {\n this.bindings = []\n }\n }\n\n private save(): void {\n const file = this.file()\n mkdirSync(join(file, '..'), { recursive: true, mode: 0o700 })\n const tmp = `${file}.${process.pid}.tmp`\n writeFileSync(tmp, JSON.stringify(this.bindings, null, 2), { mode: 0o600 })\n renameSync(tmp, file)\n chmodSync(file, 0o600)\n }\n\n add(binding: Omit<WorkspaceBinding, 'mirrorPath'> & { mirrorPath: string }): void {\n this.load()\n const mirrorPath = normalize(binding.mirrorPath)\n this.bindings = this.bindings.filter((b) => normalize(b.mirrorPath) !== mirrorPath)\n this.bindings.push({ ...binding, mirrorPath, remotePath: normalize(binding.remotePath) })\n this.save()\n }\n\n remove(mirrorPath: string): void {\n this.load()\n const target = normalize(mirrorPath)\n this.bindings = this.bindings.filter((b) => normalize(b.mirrorPath) !== target)\n this.save()\n }\n\n list(): WorkspaceBinding[] {\n this.load()\n return this.bindings.map((b) => ({ ...b }))\n }\n\n resolve(path: string): BindingHit | undefined {\n this.load()\n const target = normalize(path)\n let best: WorkspaceBinding | undefined\n for (const b of this.bindings) {\n const root = normalize(b.mirrorPath)\n if (target === root || target.startsWith(`${root}/`)) {\n if (!best || root.length > normalize(best.mirrorPath).length) best = b\n }\n }\n if (!best) return undefined\n const root = normalize(best.mirrorPath)\n const remoteRoot = normalize(best.remotePath)\n const suffix = target === root ? '' : target.slice(root.length)\n return { nodeId: best.nodeId, remotePath: normalize(`${remoteRoot}${suffix}`) }\n }\n\n mirrorRoot(nodeId: string, slug: string): string {\n return join(dshHome(), 'dshb', 'mirrors', nodeId, slug)\n }\n}\n"],"mappings":";;;;AAgBA,SAAS,UAAkB;CACzB,OAAO,QAAQ,IAAI,YAAY,KAAK,QAAQ,GAAG,MAAM;AACvD;AAEA,SAASA,YAAU,GAAmB;CACpC,OAAO,EAAE,QAAQ,WAAW,GAAG,CAAC,CAAC,QAAQ,QAAQ,EAAE,KAAK;AAC1D;AAEA,IAAa,yBAAb,MAAoC;CAClC,WAAuC,CAAC;CACxC,SAAiB;CAEjB,OAAuB;EACrB,OAAO,KAAK,QAAQ,GAAG,QAAQ,yBAAyB;CAC1D;CAEA,OAAqB;EACnB,IAAI,KAAK,QAAQ;EACjB,KAAK,SAAS;EACd,IAAI;GACF,KAAK,WAAY,KAAK,MAAM,aAAa,KAAK,KAAK,GAAG,MAAM,CAAC,CAAC,CAAwB,KAAK,OAAO;IAChG,GAAG;IACH,YAAYA,YAAU,EAAE,UAAU;IAClC,YAAYA,YAAU,EAAE,UAAU;GACpC,EAAE;EACJ,QAAQ;GACN,KAAK,WAAW,CAAC;EACnB;CACF;CAEA,OAAqB;EACnB,MAAM,OAAO,KAAK,KAAK;EACvB,UAAU,KAAK,MAAM,IAAI,GAAG;GAAE,WAAW;GAAM,MAAM;EAAM,CAAC;EAC5D,MAAM,MAAM,GAAG,KAAK,GAAG,QAAQ,IAAI;EACnC,cAAc,KAAK,KAAK,UAAU,KAAK,UAAU,MAAM,CAAC,GAAG,EAAE,MAAM,IAAM,CAAC;EAC1E,WAAW,KAAK,IAAI;EACpB,UAAU,MAAM,GAAK;CACvB;CAEA,IAAI,SAA8E;EAChF,KAAK,KAAK;EACV,MAAM,aAAaA,YAAU,QAAQ,UAAU;EAC/C,KAAK,WAAW,KAAK,SAAS,QAAQ,MAAMA,YAAU,EAAE,UAAU,MAAM,UAAU;EAClF,KAAK,SAAS,KAAK;GAAE,GAAG;GAAS;GAAY,YAAYA,YAAU,QAAQ,UAAU;EAAE,CAAC;EACxF,KAAK,KAAK;CACZ;CAEA,OAAO,YAA0B;EAC/B,KAAK,KAAK;EACV,MAAM,SAASA,YAAU,UAAU;EACnC,KAAK,WAAW,KAAK,SAAS,QAAQ,MAAMA,YAAU,EAAE,UAAU,MAAM,MAAM;EAC9E,KAAK,KAAK;CACZ;CAEA,OAA2B;EACzB,KAAK,KAAK;EACV,OAAO,KAAK,SAAS,KAAK,OAAO,EAAE,GAAG,EAAE,EAAE;CAC5C;CAEA,QAAQ,MAAsC;EAC5C,KAAK,KAAK;EACV,MAAM,SAASA,YAAU,IAAI;EAC7B,IAAI;EACJ,KAAK,MAAM,KAAK,KAAK,UAAU;GAC7B,MAAM,OAAOA,YAAU,EAAE,UAAU;GACnC,IAAI,WAAW,QAAQ,OAAO,WAAW,GAAG,KAAK,EAAE,GAC7C;QAAA,CAAC,QAAQ,KAAK,SAASA,YAAU,KAAK,UAAU,CAAC,CAAC,QAAQ,OAAO;GAAA;EAEzE;EACA,IAAI,CAAC,MAAM,OAAO,KAAA;EAClB,MAAM,OAAOA,YAAU,KAAK,UAAU;EACtC,MAAM,aAAaA,YAAU,KAAK,UAAU;EAC5C,MAAM,SAAS,WAAW,OAAO,KAAK,OAAO,MAAM,KAAK,MAAM;EAC9D,OAAO;GAAE,QAAQ,KAAK;GAAQ,YAAYA,YAAU,GAAG,aAAa,QAAQ;EAAE;CAChF;CAEA,WAAW,QAAgB,MAAsB;EAC/C,OAAO,KAAK,QAAQ,GAAG,QAAQ,WAAW,QAAQ,IAAI;CACxD;AACF"}
|