overleaf-codex 0.1.0-rc.1
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.
Potentially problematic release.
This version of overleaf-codex might be problematic. Click here for more details.
- package/LICENSE +21 -0
- package/NOTICE.md +25 -0
- package/README.md +217 -0
- package/assets/olcx-mark.svg +22 -0
- package/dist/auth/projectAuth.d.ts +19 -0
- package/dist/auth/projectAuth.js +163 -0
- package/dist/auth/projectAuth.js.map +1 -0
- package/dist/auth/redact.d.ts +3 -0
- package/dist/auth/redact.js +7 -0
- package/dist/auth/redact.js.map +1 -0
- package/dist/auth/types.d.ts +10 -0
- package/dist/auth/types.js +4 -0
- package/dist/auth/types.js.map +1 -0
- package/dist/backend/index.d.ts +6 -0
- package/dist/backend/index.js +2 -0
- package/dist/backend/index.js.map +1 -0
- package/dist/backend/olcli/client.d.ts +329 -0
- package/dist/backend/olcli/client.js +1757 -0
- package/dist/backend/olcli/client.js.map +1 -0
- package/dist/backend/olcli/index.d.ts +2 -0
- package/dist/backend/olcli/index.js +2 -0
- package/dist/backend/olcli/index.js.map +1 -0
- package/dist/backend/overleafBackend.d.ts +41 -0
- package/dist/backend/overleafBackend.js +200 -0
- package/dist/backend/overleafBackend.js.map +1 -0
- package/dist/backend/types.d.ts +73 -0
- package/dist/backend/types.js +2 -0
- package/dist/backend/types.js.map +1 -0
- package/dist/cli-behavior.d.ts +14 -0
- package/dist/cli-behavior.js +59 -0
- package/dist/cli-behavior.js.map +1 -0
- package/dist/cli.d.ts +30 -0
- package/dist/cli.js +441 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/auth.d.ts +21 -0
- package/dist/commands/auth.js +104 -0
- package/dist/commands/auth.js.map +1 -0
- package/dist/commands/compile.d.ts +7 -0
- package/dist/commands/compile.js +73 -0
- package/dist/commands/compile.js.map +1 -0
- package/dist/commands/doctor.d.ts +11 -0
- package/dist/commands/doctor.js +9 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/endpoint.d.ts +23 -0
- package/dist/commands/endpoint.js +69 -0
- package/dist/commands/endpoint.js.map +1 -0
- package/dist/commands/init.d.ts +14 -0
- package/dist/commands/init.js +48 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/status.d.ts +4 -0
- package/dist/commands/status.js +5 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/sync.d.ts +26 -0
- package/dist/commands/sync.js +139 -0
- package/dist/commands/sync.js.map +1 -0
- package/dist/commands/watch.d.ts +28 -0
- package/dist/commands/watch.js +124 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/compile/compileFlow.d.ts +32 -0
- package/dist/compile/compileFlow.js +290 -0
- package/dist/compile/compileFlow.js.map +1 -0
- package/dist/compile/pdfOutput.d.ts +12 -0
- package/dist/compile/pdfOutput.js +64 -0
- package/dist/compile/pdfOutput.js.map +1 -0
- package/dist/config/ignoreRules.d.ts +5 -0
- package/dist/config/ignoreRules.js +53 -0
- package/dist/config/ignoreRules.js.map +1 -0
- package/dist/config/overleafProject.d.ts +9 -0
- package/dist/config/overleafProject.js +61 -0
- package/dist/config/overleafProject.js.map +1 -0
- package/dist/config/projectConfig.d.ts +6 -0
- package/dist/config/projectConfig.js +180 -0
- package/dist/config/projectConfig.js.map +1 -0
- package/dist/config/projectRoot.d.ts +1 -0
- package/dist/config/projectRoot.js +36 -0
- package/dist/config/projectRoot.js.map +1 -0
- package/dist/config/types.d.ts +50 -0
- package/dist/config/types.js +34 -0
- package/dist/config/types.js.map +1 -0
- package/dist/config/vscode.d.ts +10 -0
- package/dist/config/vscode.js +134 -0
- package/dist/config/vscode.js.map +1 -0
- package/dist/diagnostics/doctor.d.ts +8 -0
- package/dist/diagnostics/doctor.js +209 -0
- package/dist/diagnostics/doctor.js.map +1 -0
- package/dist/diagnostics/status.d.ts +6 -0
- package/dist/diagnostics/status.js +110 -0
- package/dist/diagnostics/status.js.map +1 -0
- package/dist/diagnostics/types.d.ts +33 -0
- package/dist/diagnostics/types.js +2 -0
- package/dist/diagnostics/types.js.map +1 -0
- package/dist/endpoint/overleafEndpoint.d.ts +36 -0
- package/dist/endpoint/overleafEndpoint.js +105 -0
- package/dist/endpoint/overleafEndpoint.js.map +1 -0
- package/dist/errors.d.ts +32 -0
- package/dist/errors.js +53 -0
- package/dist/errors.js.map +1 -0
- package/dist/sync/apply.d.ts +14 -0
- package/dist/sync/apply.js +92 -0
- package/dist/sync/apply.js.map +1 -0
- package/dist/sync/conflicts.d.ts +7 -0
- package/dist/sync/conflicts.js +59 -0
- package/dist/sync/conflicts.js.map +1 -0
- package/dist/sync/ignore.d.ts +5 -0
- package/dist/sync/ignore.js +74 -0
- package/dist/sync/ignore.js.map +1 -0
- package/dist/sync/plan.d.ts +3 -0
- package/dist/sync/plan.js +197 -0
- package/dist/sync/plan.js.map +1 -0
- package/dist/sync/snapshot.d.ts +13 -0
- package/dist/sync/snapshot.js +82 -0
- package/dist/sync/snapshot.js.map +1 -0
- package/dist/sync/state.d.ts +16 -0
- package/dist/sync/state.js +214 -0
- package/dist/sync/state.js.map +1 -0
- package/dist/sync/types.d.ts +113 -0
- package/dist/sync/types.js +4 -0
- package/dist/sync/types.js.map +1 -0
- package/dist/testing/fakeBackend.d.ts +27 -0
- package/dist/testing/fakeBackend.js +213 -0
- package/dist/testing/fakeBackend.js.map +1 -0
- package/dist/watch/queue.d.ts +2 -0
- package/dist/watch/queue.js +91 -0
- package/dist/watch/queue.js.map +1 -0
- package/dist/watch/types.d.ts +52 -0
- package/dist/watch/types.js +2 -0
- package/dist/watch/types.js.map +1 -0
- package/dist/watch/watcher.d.ts +6 -0
- package/dist/watch/watcher.js +58 -0
- package/dist/watch/watcher.js.map +1 -0
- package/dist/watch/workflow.d.ts +30 -0
- package/dist/watch/workflow.js +62 -0
- package/dist/watch/workflow.js.map +1 -0
- package/docs/architecture.md +603 -0
- package/docs/auth.md +65 -0
- package/docs/cli-behavior.md +95 -0
- package/docs/compile.md +51 -0
- package/docs/design.md +82 -0
- package/docs/endpoint.md +84 -0
- package/docs/npm-packaging.md +148 -0
- package/docs/quickdev-queue-audit.md +193 -0
- package/docs/release-gates.md +119 -0
- package/docs/release-notes-v1.md +97 -0
- package/docs/security.md +61 -0
- package/docs/sync-state.md +305 -0
- package/docs/sync.md +50 -0
- package/docs/troubleshooting.md +124 -0
- package/docs/usage.md +184 -0
- package/examples/minimal-paper/.olcx/auth.local.example.json +7 -0
- package/examples/minimal-paper/.olcx/config.json +23 -0
- package/examples/minimal-paper/README.md +88 -0
- package/examples/minimal-paper/main.tex +23 -0
- package/package.json +66 -0
- package/src/backend/olcli/LICENSE +21 -0
- package/src/backend/olcli/README.md +26 -0
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join, win32 } from "node:path";
|
|
3
|
+
import { createOlcxError } from "../errors.js";
|
|
4
|
+
import { normalizeSyncPath } from "./ignore.js";
|
|
5
|
+
export async function applySyncPlan(input) {
|
|
6
|
+
if (input.plan.conflicts.length > 0 || input.plan.operations.some((operation) => operation.type === "conflict")) {
|
|
7
|
+
throw createOlcxError({
|
|
8
|
+
code: "SYNC_CONFLICT",
|
|
9
|
+
message: "Sync plan contains conflicts.",
|
|
10
|
+
hint: "Review conflicts, then run olcx sync --dry-run.",
|
|
11
|
+
});
|
|
12
|
+
}
|
|
13
|
+
if (input.plan.operations.some((operation) => operation.type === "deleteLocal" || operation.type === "deleteRemote")) {
|
|
14
|
+
throw createOlcxError({
|
|
15
|
+
code: "SYNC_UNSAFE_OPERATION",
|
|
16
|
+
message: "Sync plan contains delete operations that are not applied automatically.",
|
|
17
|
+
hint: "Confirm deletions manually before running olcx sync again.",
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
const result = {
|
|
21
|
+
uploaded: new Map(),
|
|
22
|
+
downloaded: [],
|
|
23
|
+
};
|
|
24
|
+
if (input.plan.dryRun) {
|
|
25
|
+
return result;
|
|
26
|
+
}
|
|
27
|
+
for (const operation of input.plan.operations) {
|
|
28
|
+
if (operation.type === "upload") {
|
|
29
|
+
const uploaded = await applyUpload(input, operation);
|
|
30
|
+
result.uploaded.set(uploaded.path, uploaded);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (operation.type === "download") {
|
|
34
|
+
const downloadedPath = await applyDownload(input, operation);
|
|
35
|
+
result.downloaded.push(downloadedPath);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return result;
|
|
39
|
+
}
|
|
40
|
+
async function applyUpload(input, operation) {
|
|
41
|
+
const path = requireSafeSyncPath(operation.path);
|
|
42
|
+
const bytes = await readFile(toAbsolutePath(input.projectRoot, path));
|
|
43
|
+
const uploaded = await input.backend.uploadFile({
|
|
44
|
+
projectId: input.projectId,
|
|
45
|
+
auth: input.auth,
|
|
46
|
+
path,
|
|
47
|
+
bytes,
|
|
48
|
+
});
|
|
49
|
+
return {
|
|
50
|
+
path,
|
|
51
|
+
exists: true,
|
|
52
|
+
contentHash: uploaded.contentHash ?? operation.local?.contentHash,
|
|
53
|
+
size: uploaded.size ?? bytes.byteLength,
|
|
54
|
+
modifiedAt: uploaded.modifiedAt,
|
|
55
|
+
remoteId: uploaded.remoteId,
|
|
56
|
+
revision: uploaded.revision,
|
|
57
|
+
binary: uploaded.binary,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async function applyDownload(input, operation) {
|
|
61
|
+
const path = requireSafeSyncPath(operation.path);
|
|
62
|
+
const bytes = await input.backend.downloadFile({
|
|
63
|
+
projectId: input.projectId,
|
|
64
|
+
auth: input.auth,
|
|
65
|
+
path,
|
|
66
|
+
remoteId: operation.remote?.remoteId,
|
|
67
|
+
});
|
|
68
|
+
const absolutePath = toAbsolutePath(input.projectRoot, path);
|
|
69
|
+
await mkdir(dirname(absolutePath), { recursive: true });
|
|
70
|
+
await writeFile(absolutePath, bytes);
|
|
71
|
+
return path;
|
|
72
|
+
}
|
|
73
|
+
function requireSafeSyncPath(path) {
|
|
74
|
+
const normalized = normalizeSyncPath(path);
|
|
75
|
+
const segments = normalized.split("/").filter(Boolean);
|
|
76
|
+
if (normalized.length === 0 ||
|
|
77
|
+
normalized.startsWith("/") ||
|
|
78
|
+
win32.isAbsolute(path) ||
|
|
79
|
+
segments.includes("..")) {
|
|
80
|
+
throw createOlcxError({
|
|
81
|
+
code: "SYNC_UNSAFE_OPERATION",
|
|
82
|
+
message: "Sync operation contains an unsafe path.",
|
|
83
|
+
hint: "Resolve the unsafe path manually, then run olcx sync --dry-run.",
|
|
84
|
+
details: { path: normalized },
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
return normalized;
|
|
88
|
+
}
|
|
89
|
+
function toAbsolutePath(projectRoot, path) {
|
|
90
|
+
return join(projectRoot, ...path.split("/"));
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=apply.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"apply.js","sourceRoot":"","sources":["../../src/sync/apply.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type ConflictReport, type SyncConflict } from "./types.js";
|
|
2
|
+
export declare function createConflictReport(input: {
|
|
3
|
+
generatedAt: string;
|
|
4
|
+
conflicts: SyncConflict[];
|
|
5
|
+
watchPaused: boolean;
|
|
6
|
+
}): ConflictReport;
|
|
7
|
+
export declare function formatConflictReport(report: ConflictReport): string;
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { redactSensitive } from "../cli-behavior.js";
|
|
2
|
+
import { CONFLICT_REPORT_PATH, SYNC_STATE_PATH, } from "./types.js";
|
|
3
|
+
const REPORT_MANUAL_STEPS = [
|
|
4
|
+
"Open each conflict path locally and in Overleaf.",
|
|
5
|
+
"Choose local, remote, or a manual merge.",
|
|
6
|
+
"Run olcx sync --dry-run.",
|
|
7
|
+
"Run olcx sync after the dry run is clean.",
|
|
8
|
+
"Restart olcx watch if you use the watcher.",
|
|
9
|
+
];
|
|
10
|
+
export function createConflictReport(input) {
|
|
11
|
+
return {
|
|
12
|
+
schemaVersion: 1,
|
|
13
|
+
generatedAt: input.generatedAt,
|
|
14
|
+
reportPath: CONFLICT_REPORT_PATH,
|
|
15
|
+
syncStatePath: SYNC_STATE_PATH,
|
|
16
|
+
watch: {
|
|
17
|
+
paused: input.watchPaused,
|
|
18
|
+
reason: "sync-conflict",
|
|
19
|
+
resumeCommand: "olcx watch",
|
|
20
|
+
},
|
|
21
|
+
conflicts: input.conflicts.map(toReportEntry),
|
|
22
|
+
manualSteps: REPORT_MANUAL_STEPS,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
export function formatConflictReport(report) {
|
|
26
|
+
return redactSensitive(JSON.stringify(report, null, 2));
|
|
27
|
+
}
|
|
28
|
+
function toReportEntry(conflict) {
|
|
29
|
+
return {
|
|
30
|
+
path: conflict.path,
|
|
31
|
+
reason: conflict.reason,
|
|
32
|
+
local: conflict.local ? toDigest(conflict.local) : undefined,
|
|
33
|
+
remote: conflict.remote ? toDigest(conflict.remote) : undefined,
|
|
34
|
+
base: conflict.base ? toBaseDigest(conflict.base) : undefined,
|
|
35
|
+
suggestedCommands: ["olcx sync --dry-run", "olcx sync"],
|
|
36
|
+
manualSteps: [
|
|
37
|
+
`Review ${conflict.path} locally and in Overleaf.`,
|
|
38
|
+
conflict.recommendation,
|
|
39
|
+
"Run olcx sync --dry-run before applying changes.",
|
|
40
|
+
],
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
function toDigest(input) {
|
|
44
|
+
return {
|
|
45
|
+
contentHash: input.contentHash,
|
|
46
|
+
size: input.size,
|
|
47
|
+
modifiedAt: input.modifiedAt,
|
|
48
|
+
remoteId: input.remoteId,
|
|
49
|
+
revision: input.revision,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
function toBaseDigest(base) {
|
|
53
|
+
return {
|
|
54
|
+
contentHash: base.contentHash,
|
|
55
|
+
size: base.size,
|
|
56
|
+
syncedAt: base.syncedAt,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=conflicts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"conflicts.js","sourceRoot":"","sources":["../../src/sync/conflicts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EACL,oBAAoB,EACpB,eAAe,GAMhB,MAAM,YAAY,CAAC;AAEpB,MAAM,mBAAmB,GAAG;IAC1B,kDAAkD;IAClD,0CAA0C;IAC1C,0BAA0B;IAC1B,2CAA2C;IAC3C,4CAA4C;CAC7C,CAAC;AAEF,MAAM,UAAU,oBAAoB,CAAC,KAIpC;IACC,OAAO;QACL,aAAa,EAAE,CAAC;QAChB,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,UAAU,EAAE,oBAAoB;QAChC,aAAa,EAAE,eAAe;QAC9B,KAAK,EAAE;YACL,MAAM,EAAE,KAAK,CAAC,WAAW;YACzB,MAAM,EAAE,eAAe;YACvB,aAAa,EAAE,YAAY;SAC5B;QACD,SAAS,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,CAAC,aAAa,CAAC;QAC7C,WAAW,EAAE,mBAAmB;KACjC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,MAAsB;IACzD,OAAO,eAAe,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,aAAa,CAAC,QAAsB;IAC3C,OAAO;QACL,IAAI,EAAE,QAAQ,CAAC,IAAI;QACnB,MAAM,EAAE,QAAQ,CAAC,MAAM;QACvB,KAAK,EAAE,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS;QAC5D,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,QAAQ,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,SAAS;QAC/D,IAAI,EAAE,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,YAAY,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,SAAS;QAC7D,iBAAiB,EAAE,CAAC,qBAAqB,EAAE,WAAW,CAAC;QACvD,WAAW,EAAE;YACX,UAAU,QAAQ,CAAC,IAAI,2BAA2B;YAClD,QAAQ,CAAC,cAAc;YACvB,kDAAkD;SACnD;KACF,CAAC;AACJ,CAAC;AAED,SAAS,QAAQ,CAAC,KAMjB;IACC,OAAO;QACL,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,QAAQ,EAAE,KAAK,CAAC,QAAQ;KACzB,CAAC;AACJ,CAAC;AAED,SAAS,YAAY,CAAC,IAAoB;IACxC,OAAO;QACL,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,IAAI,EAAE,IAAI,CAAC,IAAI;QACf,QAAQ,EAAE,IAAI,CAAC,QAAQ;KACxB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export declare const BUILT_IN_IGNORE_PATTERNS: readonly [".git/", "node_modules/", ".olcx/auth.local.json", ".olcx/*.local.json", ".olcx/*.secret.json", ".olcx/state/", "build/overleaf/", "*.aux", "*.bbl", "*.bcf", "*.blg", "*.fdb_latexmk", "*.fls", "*.log", "*.out", "*.run.xml", "*.synctex.gz", "*.toc"];
|
|
2
|
+
export declare function normalizeSyncPath(path: string): string;
|
|
3
|
+
export declare function createIgnoreMatcher(userPatterns?: readonly string[]): {
|
|
4
|
+
isIgnored(path: string): boolean;
|
|
5
|
+
};
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
export const BUILT_IN_IGNORE_PATTERNS = [
|
|
2
|
+
".git/",
|
|
3
|
+
"node_modules/",
|
|
4
|
+
".olcx/auth.local.json",
|
|
5
|
+
".olcx/*.local.json",
|
|
6
|
+
".olcx/*.secret.json",
|
|
7
|
+
".olcx/state/",
|
|
8
|
+
"build/overleaf/",
|
|
9
|
+
"*.aux",
|
|
10
|
+
"*.bbl",
|
|
11
|
+
"*.bcf",
|
|
12
|
+
"*.blg",
|
|
13
|
+
"*.fdb_latexmk",
|
|
14
|
+
"*.fls",
|
|
15
|
+
"*.log",
|
|
16
|
+
"*.out",
|
|
17
|
+
"*.run.xml",
|
|
18
|
+
"*.synctex.gz",
|
|
19
|
+
"*.toc",
|
|
20
|
+
];
|
|
21
|
+
export function normalizeSyncPath(path) {
|
|
22
|
+
return path.replace(/\\/g, "/").replace(/^\.\/+/, "").replace(/\/+/g, "/").replace(/\/+$/, "");
|
|
23
|
+
}
|
|
24
|
+
export function createIgnoreMatcher(userPatterns = []) {
|
|
25
|
+
const patterns = [...BUILT_IN_IGNORE_PATTERNS, ...userPatterns];
|
|
26
|
+
return {
|
|
27
|
+
isIgnored(path) {
|
|
28
|
+
const normalized = normalizeSyncPath(path);
|
|
29
|
+
if (isUnsafePath(normalized)) {
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
return patterns.some((pattern) => matchesPattern(normalized, pattern));
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function isUnsafePath(path) {
|
|
37
|
+
return path.startsWith("/") || path === ".." || path.startsWith("../") || path.includes("/../");
|
|
38
|
+
}
|
|
39
|
+
function matchesPattern(path, pattern) {
|
|
40
|
+
if (!pattern || pattern.startsWith("!")) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
const normalizedPattern = normalizeSyncPath(pattern);
|
|
44
|
+
if (pattern.endsWith("/") || normalizedPattern.endsWith("/")) {
|
|
45
|
+
return matchesDirectory(path, normalizedPattern);
|
|
46
|
+
}
|
|
47
|
+
if (normalizedPattern.endsWith("/**")) {
|
|
48
|
+
return matchesDirectory(path, normalizedPattern.slice(0, -3));
|
|
49
|
+
}
|
|
50
|
+
if (normalizedPattern.includes("*")) {
|
|
51
|
+
return matchesStarPattern(path, normalizedPattern);
|
|
52
|
+
}
|
|
53
|
+
return path === normalizedPattern;
|
|
54
|
+
}
|
|
55
|
+
function matchesDirectory(path, directory) {
|
|
56
|
+
const normalizedDirectory = normalizeSyncPath(directory);
|
|
57
|
+
return path === normalizedDirectory || path.startsWith(`${normalizedDirectory}/`);
|
|
58
|
+
}
|
|
59
|
+
function matchesStarPattern(path, pattern) {
|
|
60
|
+
const starIndex = pattern.indexOf("*");
|
|
61
|
+
const prefix = pattern.slice(0, starIndex);
|
|
62
|
+
const suffix = pattern.slice(starIndex + 1);
|
|
63
|
+
if (prefix === "" && !suffix.includes("/")) {
|
|
64
|
+
return basename(path).endsWith(suffix);
|
|
65
|
+
}
|
|
66
|
+
if (!path.startsWith(prefix) || !path.endsWith(suffix)) {
|
|
67
|
+
return false;
|
|
68
|
+
}
|
|
69
|
+
return !path.slice(prefix.length, path.length - suffix.length).includes("/");
|
|
70
|
+
}
|
|
71
|
+
function basename(path) {
|
|
72
|
+
return path.split("/").at(-1) ?? path;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=ignore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ignore.js","sourceRoot":"","sources":["../../src/sync/ignore.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { createIgnoreMatcher, normalizeSyncPath } from "./ignore.js";
|
|
3
|
+
const RECOMMENDATIONS = {
|
|
4
|
+
"both-modified": "Review both versions, merge manually, then run olcx sync --dry-run.",
|
|
5
|
+
"local-modified-remote-deleted": "Decide whether to keep the local file or accept the remote deletion, then run olcx sync --dry-run.",
|
|
6
|
+
"remote-modified-local-deleted": "Decide whether to restore the local file or delete the remote version, then run olcx sync --dry-run.",
|
|
7
|
+
"unsafe-delete": "Deletion is not applied automatically in v1. Confirm manually, then run olcx sync --dry-run.",
|
|
8
|
+
unsupported: "Resolve this path manually, then run olcx sync --dry-run.",
|
|
9
|
+
};
|
|
10
|
+
export function sha256Hex(content) {
|
|
11
|
+
const bytes = typeof content === "string" ? Buffer.from(content) : content;
|
|
12
|
+
return createHash("sha256").update(bytes).digest("hex");
|
|
13
|
+
}
|
|
14
|
+
export function createSyncPlan(input) {
|
|
15
|
+
const ignoreMatcher = createIgnoreMatcher(input.userIgnorePatterns);
|
|
16
|
+
const stateByPath = createStateMap(input.state.files);
|
|
17
|
+
const localByPath = createLocalMap(input.localFiles);
|
|
18
|
+
const remoteByPath = createRemoteMap(input.remoteFiles);
|
|
19
|
+
const paths = sortedUnion(stateByPath.keys(), localByPath.keys(), remoteByPath.keys());
|
|
20
|
+
const operations = [];
|
|
21
|
+
const conflicts = [];
|
|
22
|
+
for (const path of paths) {
|
|
23
|
+
const base = stateByPath.get(path);
|
|
24
|
+
const local = localByPath.get(path);
|
|
25
|
+
const remote = remoteByPath.get(path);
|
|
26
|
+
const localIgnored = local?.ignored === true;
|
|
27
|
+
if (localIgnored || ignoreMatcher.isIgnored(path)) {
|
|
28
|
+
operations.push({
|
|
29
|
+
type: "ignored",
|
|
30
|
+
path,
|
|
31
|
+
reason: "ignored by sync rules",
|
|
32
|
+
local,
|
|
33
|
+
remote,
|
|
34
|
+
base,
|
|
35
|
+
});
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const operation = planPath({
|
|
39
|
+
path,
|
|
40
|
+
base,
|
|
41
|
+
local,
|
|
42
|
+
remote,
|
|
43
|
+
allowDeletes: input.allowDeletes === true,
|
|
44
|
+
});
|
|
45
|
+
operations.push(operation);
|
|
46
|
+
if (operation.type === "conflict" && operation.conflictReason) {
|
|
47
|
+
conflicts.push({
|
|
48
|
+
path,
|
|
49
|
+
reason: operation.conflictReason,
|
|
50
|
+
local,
|
|
51
|
+
remote,
|
|
52
|
+
base,
|
|
53
|
+
recommendation: RECOMMENDATIONS[operation.conflictReason],
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
return {
|
|
58
|
+
projectId: input.projectId,
|
|
59
|
+
createdAt: input.createdAt,
|
|
60
|
+
dryRun: input.dryRun,
|
|
61
|
+
operations,
|
|
62
|
+
conflicts,
|
|
63
|
+
summary: summarizeOperations(operations),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function createStateMap(files) {
|
|
67
|
+
return new Map(Object.entries(files).map(([path, entry]) => {
|
|
68
|
+
const normalizedPath = normalizeSyncPath(entry.path || path);
|
|
69
|
+
return [normalizedPath, { ...entry, path: normalizedPath }];
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
function createLocalMap(files) {
|
|
73
|
+
return new Map(files.map((file) => {
|
|
74
|
+
const path = normalizeSyncPath(file.path);
|
|
75
|
+
return [path, { ...file, path }];
|
|
76
|
+
}));
|
|
77
|
+
}
|
|
78
|
+
function createRemoteMap(files) {
|
|
79
|
+
return new Map(files.map((file) => {
|
|
80
|
+
const path = normalizeSyncPath(file.path);
|
|
81
|
+
return [path, { ...file, path }];
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
function sortedUnion(...keySets) {
|
|
85
|
+
return [...new Set(keySets.flatMap((keys) => [...keys]))].sort();
|
|
86
|
+
}
|
|
87
|
+
function planPath(input) {
|
|
88
|
+
const { path, base, local, remote, allowDeletes } = input;
|
|
89
|
+
const localPresent = isPresentLocal(local);
|
|
90
|
+
const remotePresent = isPresentRemote(remote);
|
|
91
|
+
if ((localPresent && !local?.contentHash) || (remotePresent && !remote?.contentHash)) {
|
|
92
|
+
return conflictOperation(path, "unsupported", "present file is missing a content hash", local, remote, base);
|
|
93
|
+
}
|
|
94
|
+
if (!base) {
|
|
95
|
+
if (localPresent && !remotePresent) {
|
|
96
|
+
return operation("upload", path, "local-only file", local, remote);
|
|
97
|
+
}
|
|
98
|
+
if (!localPresent && remotePresent) {
|
|
99
|
+
return operation("download", path, "remote-only file", local, remote);
|
|
100
|
+
}
|
|
101
|
+
if (localPresent && remotePresent && local.contentHash === remote.contentHash) {
|
|
102
|
+
return operation("unchanged", path, "local and remote hashes match", local, remote);
|
|
103
|
+
}
|
|
104
|
+
return conflictOperation(path, "both-modified", "local and remote differ without a baseline", local, remote);
|
|
105
|
+
}
|
|
106
|
+
if (!localPresent && !remotePresent) {
|
|
107
|
+
return operation("unchanged", path, "both-deleted", local, remote, base);
|
|
108
|
+
}
|
|
109
|
+
if (localPresent && remotePresent) {
|
|
110
|
+
return planPresentOnBothSides(path, base, local, remote);
|
|
111
|
+
}
|
|
112
|
+
if (localPresent) {
|
|
113
|
+
return planRemoteAbsent(path, base, local, remote, allowDeletes);
|
|
114
|
+
}
|
|
115
|
+
if (remotePresent) {
|
|
116
|
+
return planLocalAbsent(path, base, local, remote, allowDeletes);
|
|
117
|
+
}
|
|
118
|
+
return conflictOperation(path, "unsupported", "path state could not be classified", local, remote, base);
|
|
119
|
+
}
|
|
120
|
+
function planPresentOnBothSides(path, base, local, remote) {
|
|
121
|
+
const localHash = local.contentHash;
|
|
122
|
+
const remoteHash = remote.contentHash;
|
|
123
|
+
const baseHash = base.contentHash;
|
|
124
|
+
if (localHash === baseHash && remoteHash === baseHash) {
|
|
125
|
+
return operation("unchanged", path, "matches baseline", local, remote, base);
|
|
126
|
+
}
|
|
127
|
+
if (localHash !== baseHash && remoteHash === baseHash) {
|
|
128
|
+
return operation("upload", path, "local changed from baseline", local, remote, base);
|
|
129
|
+
}
|
|
130
|
+
if (localHash === baseHash && remoteHash !== baseHash) {
|
|
131
|
+
return operation("download", path, "remote changed from baseline", local, remote, base);
|
|
132
|
+
}
|
|
133
|
+
if (localHash === remoteHash) {
|
|
134
|
+
return operation("unchanged", path, "local and remote already share the same changed hash", local, remote, base);
|
|
135
|
+
}
|
|
136
|
+
return conflictOperation(path, "both-modified", "local and remote both changed from baseline", local, remote, base);
|
|
137
|
+
}
|
|
138
|
+
function planRemoteAbsent(path, base, local, remote, allowDeletes) {
|
|
139
|
+
if (local.contentHash !== base.contentHash) {
|
|
140
|
+
return conflictOperation(path, "local-modified-remote-deleted", "remote deleted while local changed", local, remote, base);
|
|
141
|
+
}
|
|
142
|
+
if (allowDeletes) {
|
|
143
|
+
return operation("deleteLocal", path, "remote deletion explicitly allowed", local, remote, base);
|
|
144
|
+
}
|
|
145
|
+
return conflictOperation(path, "unsafe-delete", "remote deletion requires explicit confirmation", local, remote, base);
|
|
146
|
+
}
|
|
147
|
+
function planLocalAbsent(path, base, local, remote, allowDeletes) {
|
|
148
|
+
if (remote.contentHash !== base.contentHash) {
|
|
149
|
+
return conflictOperation(path, "remote-modified-local-deleted", "local deleted while remote changed", local, remote, base);
|
|
150
|
+
}
|
|
151
|
+
if (allowDeletes) {
|
|
152
|
+
return operation("deleteRemote", path, "local deletion explicitly allowed", local, remote, base);
|
|
153
|
+
}
|
|
154
|
+
return conflictOperation(path, "unsafe-delete", "local deletion requires explicit confirmation", local, remote, base);
|
|
155
|
+
}
|
|
156
|
+
function operation(type, path, reason, local, remote, base) {
|
|
157
|
+
return {
|
|
158
|
+
type,
|
|
159
|
+
path,
|
|
160
|
+
reason,
|
|
161
|
+
local,
|
|
162
|
+
remote,
|
|
163
|
+
base,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function conflictOperation(path, conflictReason, reason, local, remote, base) {
|
|
167
|
+
return {
|
|
168
|
+
type: "conflict",
|
|
169
|
+
path,
|
|
170
|
+
reason,
|
|
171
|
+
local,
|
|
172
|
+
remote,
|
|
173
|
+
base,
|
|
174
|
+
conflictReason,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
function isPresentLocal(snapshot) {
|
|
178
|
+
return snapshot?.exists === true;
|
|
179
|
+
}
|
|
180
|
+
function isPresentRemote(snapshot) {
|
|
181
|
+
return snapshot?.exists === true;
|
|
182
|
+
}
|
|
183
|
+
function summarizeOperations(operations) {
|
|
184
|
+
return operations.reduce((summary, operation) => ({
|
|
185
|
+
...summary,
|
|
186
|
+
[operation.type]: summary[operation.type] + 1,
|
|
187
|
+
}), {
|
|
188
|
+
upload: 0,
|
|
189
|
+
download: 0,
|
|
190
|
+
deleteLocal: 0,
|
|
191
|
+
deleteRemote: 0,
|
|
192
|
+
unchanged: 0,
|
|
193
|
+
conflict: 0,
|
|
194
|
+
ignored: 0,
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=plan.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plan.js","sourceRoot":"","sources":["../../src/sync/plan.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type ProjectAuth } from "../auth/types.js";
|
|
2
|
+
import { type OverleafBackend } from "../backend/types.js";
|
|
3
|
+
import { type LocalFileSnapshot, type RemoteFileSnapshot } from "./types.js";
|
|
4
|
+
export declare function createLocalSnapshot(input: {
|
|
5
|
+
projectRoot: string;
|
|
6
|
+
userIgnorePatterns?: string[];
|
|
7
|
+
}): Promise<LocalFileSnapshot[]>;
|
|
8
|
+
export declare function createRemoteSnapshot(_input: {
|
|
9
|
+
backend: OverleafBackend;
|
|
10
|
+
projectId: string;
|
|
11
|
+
auth: ProjectAuth;
|
|
12
|
+
userIgnorePatterns?: string[];
|
|
13
|
+
}): Promise<RemoteFileSnapshot[]>;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
import { lstat, readFile, readdir, stat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { createIgnoreMatcher, normalizeSyncPath } from "./ignore.js";
|
|
4
|
+
import { sha256Hex } from "./plan.js";
|
|
5
|
+
export async function createLocalSnapshot(input) {
|
|
6
|
+
const ignoreMatcher = createIgnoreMatcher(input.userIgnorePatterns);
|
|
7
|
+
const files = [];
|
|
8
|
+
async function visit(relativeDirectory) {
|
|
9
|
+
const absoluteDirectory = relativeDirectory
|
|
10
|
+
? join(input.projectRoot, ...relativeDirectory.split("/"))
|
|
11
|
+
: input.projectRoot;
|
|
12
|
+
const entries = await readdir(absoluteDirectory, { withFileTypes: true });
|
|
13
|
+
for (const entry of entries) {
|
|
14
|
+
const relativePath = normalizeSyncPath(relativeDirectory ? `${relativeDirectory}/${entry.name}` : entry.name);
|
|
15
|
+
if (ignoreMatcher.isIgnored(relativePath)) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const absolutePath = join(input.projectRoot, ...relativePath.split("/"));
|
|
19
|
+
const linkStats = await lstat(absolutePath);
|
|
20
|
+
if (linkStats.isSymbolicLink()) {
|
|
21
|
+
continue;
|
|
22
|
+
}
|
|
23
|
+
if (linkStats.isDirectory()) {
|
|
24
|
+
await visit(relativePath);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
if (!linkStats.isFile()) {
|
|
28
|
+
continue;
|
|
29
|
+
}
|
|
30
|
+
const bytes = await readFile(absolutePath);
|
|
31
|
+
const fileStats = await stat(absolutePath);
|
|
32
|
+
files.push({
|
|
33
|
+
path: relativePath,
|
|
34
|
+
exists: true,
|
|
35
|
+
contentHash: sha256Hex(bytes),
|
|
36
|
+
size: bytes.byteLength,
|
|
37
|
+
modifiedAt: fileStats.mtime.toISOString(),
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
await visit("");
|
|
42
|
+
return files.sort((a, b) => a.path.localeCompare(b.path));
|
|
43
|
+
}
|
|
44
|
+
export async function createRemoteSnapshot(_input) {
|
|
45
|
+
const input = _input;
|
|
46
|
+
const ignoreMatcher = createIgnoreMatcher(input.userIgnorePatterns);
|
|
47
|
+
const files = await input.backend.listFiles({ projectId: input.projectId, auth: input.auth });
|
|
48
|
+
const snapshots = [];
|
|
49
|
+
for (const file of files) {
|
|
50
|
+
const path = normalizeSyncPath(file.path);
|
|
51
|
+
if (file.kind !== "file" || ignoreMatcher.isIgnored(path)) {
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
let contentHash = isValidSha256(file.contentHash) ? file.contentHash : undefined;
|
|
55
|
+
let size = file.size;
|
|
56
|
+
if (!contentHash) {
|
|
57
|
+
const bytes = await input.backend.downloadFile({
|
|
58
|
+
projectId: input.projectId,
|
|
59
|
+
auth: input.auth,
|
|
60
|
+
path,
|
|
61
|
+
remoteId: file.remoteId,
|
|
62
|
+
});
|
|
63
|
+
contentHash = sha256Hex(bytes);
|
|
64
|
+
size = size ?? bytes.byteLength;
|
|
65
|
+
}
|
|
66
|
+
snapshots.push({
|
|
67
|
+
path,
|
|
68
|
+
exists: true,
|
|
69
|
+
contentHash,
|
|
70
|
+
size,
|
|
71
|
+
modifiedAt: file.modifiedAt,
|
|
72
|
+
remoteId: file.remoteId,
|
|
73
|
+
revision: file.revision,
|
|
74
|
+
binary: file.binary,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
return snapshots.sort((a, b) => a.path.localeCompare(b.path));
|
|
78
|
+
}
|
|
79
|
+
function isValidSha256(value) {
|
|
80
|
+
return typeof value === "string" && /^[a-f0-9]{64}$/.test(value);
|
|
81
|
+
}
|
|
82
|
+
//# sourceMappingURL=snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"snapshot.js","sourceRoot":"","sources":["../../src/sync/snapshot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAClE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAGjC,OAAO,EAAE,mBAAmB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AACrE,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,KAGzC;IACC,MAAM,aAAa,GAAG,mBAAmB,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACpE,MAAM,KAAK,GAAwB,EAAE,CAAC;IAEtC,KAAK,UAAU,KAAK,CAAC,iBAAyB;QAC5C,MAAM,iBAAiB,GAAG,iBAAiB;YACzC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;YAC1D,CAAC,CAAC,KAAK,CAAC,WAAW,CAAC;QACtB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,iBAAiB,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;QAE1E,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,MAAM,YAAY,GAAG,iBAAiB,CAAC,iBAAiB,CAAC,CAAC,CAAC,GAAG,iBAAiB,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC9G,IAAI,aAAa,CAAC,SAAS,CAAC,YAAY,CAAC,EAAE,CAAC;gBAC1C,SAAS;YACX,CAAC;YAED,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,GAAG,YAAY,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;YACzE,MAAM,SAAS,GAAG,MAAM,KAAK,CAAC,YAAY,CAAC,CAAC;YAC5C,IAAI,SAAS,CAAC,cAAc,EAAE,EAAE,CAAC;gBAC/B,SAAS;YACX,CAAC;YAED,IAAI,SAAS,CAAC,WAAW,EAAE,EAAE,CAAC;gBAC5B,MAAM,KAAK,CAAC,YAAY,CAAC,CAAC;gBAC1B,SAAS;YACX,CAAC;YAED,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,EAAE,CAAC;gBACxB,SAAS;YACX,CAAC;YAED,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,YAAY,CAAC,CAAC;YAC3C,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,CAAC;YAC3C,KAAK,CAAC,IAAI,CAAC;gBACT,IAAI,EAAE,YAAY;gBAClB,MAAM,EAAE,IAAI;gBACZ,WAAW,EAAE,SAAS,CAAC,KAAK,CAAC;gBAC7B,IAAI,EAAE,KAAK,CAAC,UAAU;gBACtB,UAAU,EAAE,SAAS,CAAC,KAAK,CAAC,WAAW,EAAE;aAC1C,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,MAAM,KAAK,CAAC,EAAE,CAAC,CAAC;IAChB,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,MAK1C;IACC,MAAM,KAAK,GAAG,MAAM,CAAC;IACrB,MAAM,aAAa,GAAG,mBAAmB,CAAC,KAAK,CAAC,kBAAkB,CAAC,CAAC;IACpE,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,IAAI,EAAE,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC;IAC9F,MAAM,SAAS,GAAyB,EAAE,CAAC;IAE3C,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,IAAI,GAAG,iBAAiB,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1C,IAAI,IAAI,CAAC,IAAI,KAAK,MAAM,IAAI,aAAa,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1D,SAAS;QACX,CAAC;QAED,IAAI,WAAW,GAAG,aAAa,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC;QACjF,IAAI,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QAErB,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,MAAM,KAAK,GAAG,MAAM,KAAK,CAAC,OAAO,CAAC,YAAY,CAAC;gBAC7C,SAAS,EAAE,KAAK,CAAC,SAAS;gBAC1B,IAAI,EAAE,KAAK,CAAC,IAAI;gBAChB,IAAI;gBACJ,QAAQ,EAAE,IAAI,CAAC,QAAQ;aACxB,CAAC,CAAC;YACH,WAAW,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC;YAC/B,IAAI,GAAG,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC;QAClC,CAAC;QAED,SAAS,CAAC,IAAI,CAAC;YACb,IAAI;YACJ,MAAM,EAAE,IAAI;YACZ,WAAW;YACX,IAAI;YACJ,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,QAAQ,EAAE,IAAI,CAAC,QAAQ;YACvB,MAAM,EAAE,IAAI,CAAC,MAAM;SACpB,CAAC,CAAC;IACL,CAAC;IAED,OAAO,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC;AAChE,CAAC;AAED,SAAS,aAAa,CAAC,KAAc;IACnC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { type ConflictReport, type RemoteFileSnapshot, type SyncPlan, type SyncStateFile } from "./types.js";
|
|
2
|
+
export declare function createEmptySyncState(updatedAt: string): SyncStateFile;
|
|
3
|
+
export declare function getSyncStatePath(projectRoot: string): string;
|
|
4
|
+
export declare function getConflictReportPath(projectRoot: string): string;
|
|
5
|
+
export declare function readSyncState(projectRoot: string, options?: {
|
|
6
|
+
now?: () => Date;
|
|
7
|
+
}): Promise<SyncStateFile>;
|
|
8
|
+
export declare function writeSyncState(projectRoot: string, state: SyncStateFile): Promise<void>;
|
|
9
|
+
export declare function writeConflictReport(projectRoot: string, report: ConflictReport): Promise<void>;
|
|
10
|
+
export declare function clearConflictReport(projectRoot: string): Promise<void>;
|
|
11
|
+
export declare function buildNextSyncState(input: {
|
|
12
|
+
previous: SyncStateFile;
|
|
13
|
+
plan: SyncPlan;
|
|
14
|
+
appliedAt: string;
|
|
15
|
+
uploadResults?: Map<string, RemoteFileSnapshot>;
|
|
16
|
+
}): SyncStateFile;
|