@indigoai-us/hq-cloud 6.15.80 → 6.15.82
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/backup-prune.d.ts +18 -0
- package/dist/backup-prune.d.ts.map +1 -1
- package/dist/backup-prune.js +20 -0
- package/dist/backup-prune.js.map +1 -1
- package/dist/backup-prune.test.js +30 -1
- package/dist/backup-prune.test.js.map +1 -1
- package/dist/bin/backup-prune-runner.d.ts +4 -1
- package/dist/bin/backup-prune-runner.d.ts.map +1 -1
- package/dist/bin/backup-prune-runner.js +61 -38
- package/dist/bin/backup-prune-runner.js.map +1 -1
- package/dist/bin/backup-prune-runner.test.d.ts +2 -0
- package/dist/bin/backup-prune-runner.test.d.ts.map +1 -0
- package/dist/bin/backup-prune-runner.test.js +22 -0
- package/dist/bin/backup-prune-runner.test.js.map +1 -0
- package/dist/bin/layout-compatibility.d.ts +12 -0
- package/dist/bin/layout-compatibility.d.ts.map +1 -0
- package/dist/bin/layout-compatibility.js +50 -0
- package/dist/bin/layout-compatibility.js.map +1 -0
- package/dist/bin/layout-compatibility.test.d.ts +2 -0
- package/dist/bin/layout-compatibility.test.d.ts.map +1 -0
- package/dist/bin/layout-compatibility.test.js +87 -0
- package/dist/bin/layout-compatibility.test.js.map +1 -0
- package/dist/bin/sync-runner-company.d.ts.map +1 -1
- package/dist/bin/sync-runner-company.js +15 -0
- package/dist/bin/sync-runner-company.js.map +1 -1
- package/dist/bin/sync-runner-company.test.js +65 -0
- package/dist/bin/sync-runner-company.test.js.map +1 -1
- package/dist/bin/sync-runner-events.test.js +20 -0
- package/dist/bin/sync-runner-events.test.js.map +1 -1
- package/dist/bin/sync-runner-watch-loop.d.ts +9 -0
- package/dist/bin/sync-runner-watch-loop.d.ts.map +1 -1
- package/dist/bin/sync-runner-watch-loop.js +369 -21
- package/dist/bin/sync-runner-watch-loop.js.map +1 -1
- package/dist/bin/sync-runner.d.ts +5 -0
- package/dist/bin/sync-runner.d.ts.map +1 -1
- package/dist/bin/sync-runner.js +11 -1
- package/dist/bin/sync-runner.js.map +1 -1
- package/dist/bin/sync-runner.test.js +165 -2
- package/dist/bin/sync-runner.test.js.map +1 -1
- package/dist/cli/share.d.ts.map +1 -1
- package/dist/cli/share.js +29 -5
- package/dist/cli/share.js.map +1 -1
- package/dist/cli/share.test.js +131 -24
- package/dist/cli/share.test.js.map +1 -1
- package/dist/cli/sync.d.ts +23 -0
- package/dist/cli/sync.d.ts.map +1 -1
- package/dist/cli/sync.js +72 -7
- package/dist/cli/sync.js.map +1 -1
- package/dist/cli/sync.test.js +199 -0
- package/dist/cli/sync.test.js.map +1 -1
- package/dist/cli/watch-event-push-conflict.test.js +22 -11
- package/dist/cli/watch-event-push-conflict.test.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/journal.d.ts +18 -0
- package/dist/journal.d.ts.map +1 -1
- package/dist/journal.js +41 -17
- package/dist/journal.js.map +1 -1
- package/dist/journal.test.js +8 -1
- package/dist/journal.test.js.map +1 -1
- package/dist/outposts/self-deploy.d.ts.map +1 -1
- package/dist/outposts/self-deploy.js +10 -1
- package/dist/outposts/self-deploy.js.map +1 -1
- package/dist/outposts/self-deploy.test.js +4 -1
- package/dist/outposts/self-deploy.test.js.map +1 -1
- package/dist/s3.d.ts +62 -0
- package/dist/s3.d.ts.map +1 -1
- package/dist/s3.js +91 -1
- package/dist/s3.js.map +1 -1
- package/dist/s3.rename-retry.test.d.ts +2 -0
- package/dist/s3.rename-retry.test.d.ts.map +1 -0
- package/dist/s3.rename-retry.test.js +153 -0
- package/dist/s3.rename-retry.test.js.map +1 -0
- package/dist/sync/area-ledger-migration.d.ts +8 -1
- package/dist/sync/area-ledger-migration.d.ts.map +1 -1
- package/dist/sync/area-ledger-migration.js +23 -0
- package/dist/sync/area-ledger-migration.js.map +1 -1
- package/dist/sync/area-ledger-migration.test.js +20 -0
- package/dist/sync/area-ledger-migration.test.js.map +1 -1
- package/dist/sync/area-ledger.d.ts +10 -0
- package/dist/sync/area-ledger.d.ts.map +1 -1
- package/dist/sync/area-ledger.js +11 -0
- package/dist/sync/area-ledger.js.map +1 -1
- package/dist/sync/feature-flags.d.ts +29 -0
- package/dist/sync/feature-flags.d.ts.map +1 -1
- package/dist/sync/feature-flags.js +44 -0
- package/dist/sync/feature-flags.js.map +1 -1
- package/dist/sync/feature-flags.test.js +38 -1
- package/dist/sync/feature-flags.test.js.map +1 -1
- package/dist/sync/index.d.ts +6 -0
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +3 -0
- package/dist/sync/index.js.map +1 -1
- package/dist/sync/layout-compatibility.d.ts +17 -0
- package/dist/sync/layout-compatibility.d.ts.map +1 -0
- package/dist/sync/layout-compatibility.js +29 -0
- package/dist/sync/layout-compatibility.js.map +1 -0
- package/dist/sync/parallel-sync-coordinator.d.ts.map +1 -1
- package/dist/sync/parallel-sync-coordinator.js +0 -3
- package/dist/sync/parallel-sync-coordinator.js.map +1 -1
- package/dist/sync/parallel-sync-coordinator.test.js +14 -0
- package/dist/sync/parallel-sync-coordinator.test.js.map +1 -1
- package/dist/sync/per-area-maintenance.d.ts +45 -0
- package/dist/sync/per-area-maintenance.d.ts.map +1 -0
- package/dist/sync/per-area-maintenance.js +131 -0
- package/dist/sync/per-area-maintenance.js.map +1 -0
- package/dist/sync/per-area-maintenance.test.d.ts +2 -0
- package/dist/sync/per-area-maintenance.test.d.ts.map +1 -0
- package/dist/sync/per-area-maintenance.test.js +100 -0
- package/dist/sync/per-area-maintenance.test.js.map +1 -0
- package/dist/sync/realtime-telemetry.d.ts +49 -0
- package/dist/sync/realtime-telemetry.d.ts.map +1 -0
- package/dist/sync/realtime-telemetry.js +101 -0
- package/dist/sync/realtime-telemetry.js.map +1 -0
- package/dist/sync/realtime-telemetry.test.d.ts +2 -0
- package/dist/sync/realtime-telemetry.test.d.ts.map +1 -0
- package/dist/sync/realtime-telemetry.test.js +64 -0
- package/dist/sync/realtime-telemetry.test.js.map +1 -0
- package/dist/sync/state-store.d.ts +10 -0
- package/dist/sync/state-store.d.ts.map +1 -1
- package/dist/sync/state-store.js +18 -0
- package/dist/sync/state-store.js.map +1 -1
- package/package.json +3 -2
package/dist/cli/share.test.js
CHANGED
|
@@ -17,30 +17,40 @@ vi.mock("fs", async (importOriginal) => {
|
|
|
17
17
|
// response shape is `{ ETag: '"<hex>"' }`. uploadSymlink is the symlink-
|
|
18
18
|
// preserving sibling that puts a zero-byte object with target metadata
|
|
19
19
|
// instead of dereferencing the link.
|
|
20
|
-
vi.mock("../s3.js", () =>
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
}
|
|
20
|
+
vi.mock("../s3.js", async (importOriginal) => {
|
|
21
|
+
const actual = await importOriginal();
|
|
22
|
+
return {
|
|
23
|
+
toPosixKey: (key) => key.split("\\").join("/"),
|
|
24
|
+
uploadFile: vi.fn().mockResolvedValue({ etag: '"upload-etag"' }),
|
|
25
|
+
uploadSymlink: vi.fn().mockResolvedValue({ etag: '"upload-symlink-etag"' }),
|
|
26
|
+
downloadFile: vi.fn().mockResolvedValue(undefined),
|
|
27
|
+
listRemoteFiles: vi.fn().mockResolvedValue([]),
|
|
28
|
+
deleteRemoteFile: vi.fn().mockResolvedValue(undefined),
|
|
29
|
+
headRemoteFile: vi.fn().mockResolvedValue(null),
|
|
30
|
+
readVerifiedRemoteSymlinkTarget: vi.fn().mockResolvedValue(null),
|
|
31
|
+
primeObjectTransport: vi.fn().mockResolvedValue(undefined),
|
|
32
|
+
primeUploads: vi.fn().mockResolvedValue(undefined),
|
|
33
|
+
// Real exports share.ts now imports for the HQ-DESKTOP-54 privilege skip.
|
|
34
|
+
WINDOWS_SYMLINK_PRIVILEGE_REMEDY: "Enable Windows Developer Mode (Settings > Privacy & security > For developers) or run HQ sync once from an elevated terminal, then sync again.",
|
|
35
|
+
WindowsSymlinkPrivilegeError: class WindowsSymlinkPrivilegeError extends Error {
|
|
36
|
+
target;
|
|
37
|
+
code;
|
|
38
|
+
constructor(target, code) {
|
|
39
|
+
super(`symlink skipped: creating a file symlink to ${target} requires a Windows privilege`);
|
|
40
|
+
this.name = "WindowsSymlinkPrivilegeError";
|
|
41
|
+
this.target = target;
|
|
42
|
+
this.code = code;
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
// HQ-DESKTOP-54 (second path): the real win32 rename guard, typed error, and
|
|
46
|
+
// remedy that share.ts now imports. Real so `instanceof` matches the error the
|
|
47
|
+
// real helper throws, and the POSIX pass-through keeps existing push tests
|
|
48
|
+
// byte-identical.
|
|
49
|
+
renameOrRaiseWindowsBlocked: actual.renameOrRaiseWindowsBlocked,
|
|
50
|
+
WindowsRenameBlockedError: actual.WindowsRenameBlockedError,
|
|
51
|
+
WINDOWS_RENAME_BLOCKED_REMEDY: actual.WINDOWS_RENAME_BLOCKED_REMEDY,
|
|
52
|
+
};
|
|
53
|
+
});
|
|
44
54
|
// Mock readline so the interactive-conflict serialization test can observe
|
|
45
55
|
// prompt lifecycle (createInterface + close) without ever touching stdin.
|
|
46
56
|
// Default impl never resolves; conflict-prompt tests below override per-test
|
|
@@ -1986,6 +1996,103 @@ describe("share", () => {
|
|
|
1986
1996
|
expect(result.conflictPaths).toEqual(["v7b-priv.txt"]);
|
|
1987
1997
|
expect(fs.readFileSync(testFile, "utf-8")).toBe("local-collision-content");
|
|
1988
1998
|
});
|
|
1999
|
+
it("HQ-DESKTOP-54 (second path): a blocked push conflict-keep move-aside rename is a skip, not an error, and does not touch the local body", async () => {
|
|
2000
|
+
// Push keep moves the local body aside before adopting the remote. On a
|
|
2001
|
+
// Windows host where a program is holding that file open, the move-aside
|
|
2002
|
+
// rename EPERMs; s3 raises the typed WindowsRenameBlockedError
|
|
2003
|
+
// immediately (no retry). The push leg must downgrade it to the
|
|
2004
|
+
// deliberate skip (never type:"error") so the company stays complete and the
|
|
2005
|
+
// runner exits 0 — mirroring the symlink-privilege skip above.
|
|
2006
|
+
const companyRoot = path.join(tmpDir, "companies", "acme");
|
|
2007
|
+
fs.mkdirSync(companyRoot, { recursive: true });
|
|
2008
|
+
const testFile = path.join(companyRoot, "v7b-rename.txt");
|
|
2009
|
+
fs.writeFileSync(testFile, "local-collision-content");
|
|
2010
|
+
vi.mocked(headRemoteFile).mockResolvedValueOnce({
|
|
2011
|
+
lastModified: new Date(),
|
|
2012
|
+
etag: '"remote-version-etag"',
|
|
2013
|
+
size: 40,
|
|
2014
|
+
});
|
|
2015
|
+
// The reconciled probe fetches the (divergent) remote so a conflict is
|
|
2016
|
+
// detected; the move-aside rename below is what fails.
|
|
2017
|
+
vi.mocked(downloadFile).mockImplementation(async (_ctx, _key, dest) => {
|
|
2018
|
+
fs.writeFileSync(dest, "peer remote content");
|
|
2019
|
+
return undefined;
|
|
2020
|
+
});
|
|
2021
|
+
const realRenameSync = fs.renameSync;
|
|
2022
|
+
const renameSpy = vi.spyOn(fs, "renameSync").mockImplementation((from, to) => {
|
|
2023
|
+
if (String(from) === testFile && String(to).includes(".conflict-")) {
|
|
2024
|
+
const err = new Error("EPERM: operation not permitted, rename");
|
|
2025
|
+
err.code = "EPERM";
|
|
2026
|
+
throw err;
|
|
2027
|
+
}
|
|
2028
|
+
return realRenameSync(from, to);
|
|
2029
|
+
});
|
|
2030
|
+
const realPlatform = process.platform;
|
|
2031
|
+
Object.defineProperty(process, "platform", { value: "win32", configurable: true });
|
|
2032
|
+
const events = [];
|
|
2033
|
+
let result;
|
|
2034
|
+
try {
|
|
2035
|
+
result = await share({
|
|
2036
|
+
paths: [testFile],
|
|
2037
|
+
company: "acme",
|
|
2038
|
+
vaultConfig: mockConfig,
|
|
2039
|
+
hqRoot: tmpDir,
|
|
2040
|
+
onConflict: "keep",
|
|
2041
|
+
onEvent: (e) => events.push(e),
|
|
2042
|
+
});
|
|
2043
|
+
}
|
|
2044
|
+
finally {
|
|
2045
|
+
Object.defineProperty(process, "platform", { value: realPlatform, configurable: true });
|
|
2046
|
+
renameSpy.mockRestore();
|
|
2047
|
+
}
|
|
2048
|
+
expect(events).toContainEqual(expect.objectContaining({ type: "skip-rename-blocked", path: "v7b-rename.txt" }));
|
|
2049
|
+
expect(events.some((e) => e.type === "error")).toBe(false);
|
|
2050
|
+
// Nothing was moved or overwritten: the local body is intact and no upload
|
|
2051
|
+
// happened, so the conflict simply re-fires next pass.
|
|
2052
|
+
expect(fs.readFileSync(testFile, "utf-8")).toBe("local-collision-content");
|
|
2053
|
+
expect(result.filesUploaded).toBe(0);
|
|
2054
|
+
});
|
|
2055
|
+
it("HQ-DESKTOP-54 (second path): the push default console logger renders the rename-blocked skip and its remedy (a plain `hq sync` is not silent)", async () => {
|
|
2056
|
+
const companyRoot = path.join(tmpDir, "companies", "acme");
|
|
2057
|
+
fs.mkdirSync(companyRoot, { recursive: true });
|
|
2058
|
+
const testFile = path.join(companyRoot, "v7b-render.txt");
|
|
2059
|
+
fs.writeFileSync(testFile, "local-collision-content");
|
|
2060
|
+
vi.mocked(headRemoteFile).mockResolvedValueOnce({
|
|
2061
|
+
lastModified: new Date(),
|
|
2062
|
+
etag: '"remote-version-etag"',
|
|
2063
|
+
size: 40,
|
|
2064
|
+
});
|
|
2065
|
+
vi.mocked(downloadFile).mockImplementation(async (_ctx, _key, dest) => {
|
|
2066
|
+
fs.writeFileSync(dest, "peer remote content");
|
|
2067
|
+
return undefined;
|
|
2068
|
+
});
|
|
2069
|
+
const realRenameSync = fs.renameSync;
|
|
2070
|
+
const renameSpy = vi.spyOn(fs, "renameSync").mockImplementation((from, to) => {
|
|
2071
|
+
if (String(from) === testFile && String(to).includes(".conflict-")) {
|
|
2072
|
+
const err = new Error("EPERM: operation not permitted, rename");
|
|
2073
|
+
err.code = "EPERM";
|
|
2074
|
+
throw err;
|
|
2075
|
+
}
|
|
2076
|
+
return realRenameSync(from, to);
|
|
2077
|
+
});
|
|
2078
|
+
const realPlatform = process.platform;
|
|
2079
|
+
Object.defineProperty(process, "platform", { value: "win32", configurable: true });
|
|
2080
|
+
const warn = vi.spyOn(console, "warn").mockImplementation(() => { });
|
|
2081
|
+
const log = vi.spyOn(console, "log").mockImplementation(() => { });
|
|
2082
|
+
let rendered = [];
|
|
2083
|
+
try {
|
|
2084
|
+
// No onEvent → the push defaultConsoleLogger renders the skip.
|
|
2085
|
+
await share({ paths: [testFile], company: "acme", vaultConfig: mockConfig, hqRoot: tmpDir, onConflict: "keep" });
|
|
2086
|
+
rendered = warn.mock.calls.map((c) => String(c[0]));
|
|
2087
|
+
}
|
|
2088
|
+
finally {
|
|
2089
|
+
Object.defineProperty(process, "platform", { value: realPlatform, configurable: true });
|
|
2090
|
+
warn.mockRestore();
|
|
2091
|
+
log.mockRestore();
|
|
2092
|
+
renameSpy.mockRestore();
|
|
2093
|
+
}
|
|
2094
|
+
expect(rendered.some((line) => /v7b-render\.txt skipped/.test(line) && /holding the file open/i.test(line))).toBe(true);
|
|
2095
|
+
});
|
|
1989
2096
|
it("fresh-install SSE-KMS etag: byte-identical remote is reconciled without a conflict", async () => {
|
|
1990
2097
|
const companyRoot = path.join(tmpDir, "companies", "acme");
|
|
1991
2098
|
fs.mkdirSync(companyRoot, { recursive: true });
|