@indigoai-us/hq-cloud 6.15.2 → 6.15.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/bin/sync-mutation.d.ts +1 -3
  2. package/dist/bin/sync-mutation.d.ts.map +1 -1
  3. package/dist/bin/sync-mutation.js +5 -5
  4. package/dist/bin/sync-mutation.js.map +1 -1
  5. package/dist/bin/sync-mutation.test.js +36 -137
  6. package/dist/bin/sync-mutation.test.js.map +1 -1
  7. package/dist/cli/sync.d.ts +7 -6
  8. package/dist/cli/sync.d.ts.map +1 -1
  9. package/dist/cli/sync.js +21 -37
  10. package/dist/cli/sync.js.map +1 -1
  11. package/dist/cli/sync.test.js +27 -47
  12. package/dist/cli/sync.test.js.map +1 -1
  13. package/dist/sync/base-version-resolver.d.ts +11 -0
  14. package/dist/sync/base-version-resolver.d.ts.map +1 -0
  15. package/dist/sync/base-version-resolver.js +48 -0
  16. package/dist/sync/base-version-resolver.js.map +1 -0
  17. package/dist/sync/base-version-resolver.test.d.ts +2 -0
  18. package/dist/sync/base-version-resolver.test.d.ts.map +1 -0
  19. package/dist/sync/base-version-resolver.test.js +24 -0
  20. package/dist/sync/base-version-resolver.test.js.map +1 -0
  21. package/dist/sync/lease-client.d.ts +17 -0
  22. package/dist/sync/lease-client.d.ts.map +1 -1
  23. package/dist/sync/lease-client.js +46 -1
  24. package/dist/sync/lease-client.js.map +1 -1
  25. package/dist/sync/mutation-client.d.ts +2 -0
  26. package/dist/sync/mutation-client.d.ts.map +1 -1
  27. package/dist/sync/mutation-client.js +2 -0
  28. package/dist/sync/mutation-client.js.map +1 -1
  29. package/dist/sync/mutation-session.d.ts +49 -0
  30. package/dist/sync/mutation-session.d.ts.map +1 -0
  31. package/dist/sync/mutation-session.js +282 -0
  32. package/dist/sync/mutation-session.js.map +1 -0
  33. package/dist/sync/mutation-session.test.d.ts +2 -0
  34. package/dist/sync/mutation-session.test.d.ts.map +1 -0
  35. package/dist/sync/mutation-session.test.js +190 -0
  36. package/dist/sync/mutation-session.test.js.map +1 -0
  37. package/dist/sync/uploader-finalization.d.ts +20 -0
  38. package/dist/sync/uploader-finalization.d.ts.map +1 -1
  39. package/dist/sync/uploader-finalization.js +48 -0
  40. package/dist/sync/uploader-finalization.js.map +1 -1
  41. package/dist/sync/uploader-finalization.test.js +15 -0
  42. package/dist/sync/uploader-finalization.test.js.map +1 -1
  43. package/package.json +1 -1
@@ -0,0 +1,282 @@
1
+ /**
2
+ * Local, enrolled V2 mutation session for the desktop sidecar boundary.
3
+ *
4
+ * This intentionally obtains fresh inventory + lease authority through the
5
+ * same RealtimePassAuthorizer used by the sync machinery. The caller gives
6
+ * us only a change; bearer, lease, device binding, revisions, and generation
7
+ * remain local to hq-cloud and never cross stdin.
8
+ */
9
+ import { createHash, randomUUID } from "node:crypto";
10
+ import * as fs from "node:fs";
11
+ import * as os from "node:os";
12
+ import * as path from "node:path";
13
+ import { CognitoAuthError, CognitoRefreshError, getValidAccessToken, VaultClient } from "../index.js";
14
+ import { pickCanonicalPersonEntity } from "../vault-client.js";
15
+ import { HQ_CLOUD_VERSION } from "../version.js";
16
+ import { getOrCreateMachineId } from "../lib/machine-id.js";
17
+ import { RealtimeEnrollmentUnavailableError, RealtimeLeaseClient, RealtimePassAuthorizer, membershipRevisionFromLease, } from "./lease-client.js";
18
+ import { MutationClient } from "./mutation-client.js";
19
+ import { DurableAttemptSpool } from "./multipart-uploader.js";
20
+ import { RealtimeRolloutClient } from "./realtime-rollout.js";
21
+ import { resolveAuthoritativeBaseVersion } from "./base-version-resolver.js";
22
+ import { UploaderFinalizer, openUploaderFinalizationStore } from "./uploader-finalization.js";
23
+ const DEFAULT_API_URL = process.env.HQ_VAULT_API_URL ?? "https://hqapi.hq.computer";
24
+ const EMPTY_SHA256 = `sha256:${createHash("sha256").update("").digest("hex")}`;
25
+ export const SYNC_MUTATION_NOT_ENROLLED_EXIT = 3;
26
+ export class SyncMutationNotEnrolledError extends Error {
27
+ constructor() {
28
+ super("local hq-cloud session is not enrolled for realtime V2 mutations");
29
+ this.name = "SyncMutationNotEnrolledError";
30
+ }
31
+ }
32
+ function defaultHqRoot() {
33
+ return process.env.HQ_HOME ?? process.env.HQ_ROOT ?? path.join(os.homedir(), "hq");
34
+ }
35
+ function defaultCognitoConfig() {
36
+ return {
37
+ region: process.env.AWS_REGION ?? "us-east-1",
38
+ userPoolDomain: process.env.HQ_COGNITO_DOMAIN ?? "vault-indigo-hq-prod",
39
+ clientId: process.env.HQ_COGNITO_CLIENT_ID ?? "7acei2c8v870enheptb1j5foln",
40
+ port: process.env.HQ_COGNITO_CALLBACK_PORT ? Number(process.env.HQ_COGNITO_CALLBACK_PORT) : 8765,
41
+ };
42
+ }
43
+ function fingerprint(value) {
44
+ return createHash("sha256").update(JSON.stringify(value)).digest("hex");
45
+ }
46
+ function sourceDigest(bytes) {
47
+ return `sha256:${createHash("sha256").update(bytes).digest("hex")}`;
48
+ }
49
+ /**
50
+ * Builds complete V2 requests from one local enrolled session. A fresh lease
51
+ * is issued for every submission; stale/revoked inventory never falls back to
52
+ * a legacy path.
53
+ */
54
+ export class LocalEnrolledMutationSession {
55
+ apiUrl;
56
+ hqRoot;
57
+ getAccessToken;
58
+ resolvePersonalScopeUid;
59
+ mutationClient;
60
+ authorizer;
61
+ deviceId;
62
+ capabilityVector;
63
+ resolveBaseVersion;
64
+ constructor(deps = {}) {
65
+ this.apiUrl = (deps.apiUrl ?? DEFAULT_API_URL).replace(/\/+$/, "");
66
+ this.hqRoot = deps.hqRoot ?? defaultHqRoot();
67
+ this.getAccessToken = deps.getAccessToken ?? (() => getValidAccessToken(defaultCognitoConfig(), { interactive: false }));
68
+ this.resolvePersonalScopeUid = deps.resolvePersonalScopeUid ?? (async (bearer) => {
69
+ const client = new VaultClient({ apiUrl: this.apiUrl, authToken: bearer, region: process.env.AWS_REGION ?? "us-east-1" });
70
+ const person = pickCanonicalPersonEntity(await client.entity.listByType("person"));
71
+ if (!person?.uid)
72
+ throw new SyncMutationNotEnrolledError();
73
+ return person.uid;
74
+ });
75
+ this.mutationClient = deps.createMutationClient?.(this.apiUrl) ?? new MutationClient({ apiUrl: this.apiUrl });
76
+ this.deviceId = deps.deviceId ?? getOrCreateMachineId(this.hqRoot);
77
+ this.capabilityVector = deps.capabilityVector ?? {
78
+ hqCloud: HQ_CLOUD_VERSION,
79
+ hqDesktop: process.env.HQ_INSTALLER_VERSION ?? "none",
80
+ protocol: 2,
81
+ journal: 3,
82
+ durableApply: 2,
83
+ };
84
+ this.resolveBaseVersion = deps.resolveBaseVersion ?? ((input) => resolveAuthoritativeBaseVersion(input, input.canonicalPath));
85
+ this.authorizer = deps.authorizer ?? new RealtimePassAuthorizer(this.getAccessToken, new RealtimeRolloutClient({ apiUrl: this.apiUrl }), new RealtimeLeaseClient({ apiUrl: this.apiUrl }));
86
+ }
87
+ async submit(change) {
88
+ let bearer;
89
+ try {
90
+ bearer = await this.getAccessToken();
91
+ }
92
+ catch (error) {
93
+ if (error instanceof CognitoAuthError && !(error instanceof CognitoRefreshError && !error.requiresReauth)) {
94
+ throw new SyncMutationNotEnrolledError();
95
+ }
96
+ throw error;
97
+ }
98
+ const scopeUid = await this.resolvePersonalScopeUid(bearer);
99
+ let authorized;
100
+ try {
101
+ authorized = await this.authorizer.authorizeWithBearer(bearer, {
102
+ deviceId: this.deviceId,
103
+ capabilityVector: this.capabilityVector,
104
+ configurationFingerprint: fingerprint(this.capabilityVector),
105
+ scopeFingerprint: fingerprint([scopeUid]),
106
+ scopeUids: [scopeUid],
107
+ });
108
+ }
109
+ catch (error) {
110
+ if (error instanceof RealtimeEnrollmentUnavailableError)
111
+ throw new SyncMutationNotEnrolledError();
112
+ throw error;
113
+ }
114
+ const scope = authorized.scopes[0];
115
+ if (!scope)
116
+ throw new SyncMutationNotEnrolledError();
117
+ if (Date.parse(scope.lease.expiresAt) <= Date.now()) {
118
+ throw new Error("realtime mutation: lease expired before local submission");
119
+ }
120
+ const membershipRevision = membershipRevisionFromLease(scope.lease);
121
+ const stateRoot = path.join(this.hqRoot, ".hq", "realtime-mutation-state");
122
+ const releasePathLock = await acquireMutationPathLock(stateRoot, scopeUid, change.canonicalPath);
123
+ try {
124
+ const finalizer = new UploaderFinalizer(openUploaderFinalizationStore({
125
+ rootDir: stateRoot,
126
+ scopeId: `${scopeUid}:uploader`,
127
+ }));
128
+ const authorization = {
129
+ bearer: authorized.bearer,
130
+ deviceId: this.deviceId,
131
+ capabilityVector: this.capabilityVector,
132
+ lease: scope.lease,
133
+ };
134
+ const existing = finalizer.getState().paths[change.canonicalPath];
135
+ const planned = existing?.plannedMutation;
136
+ if (planned) {
137
+ const recovered = await this.resumePlan(finalizer, planned, authorization, membershipRevision, scope.grant.scopeRevision);
138
+ if (recovered.status.status !== "committed" || isSameChange(planned, change))
139
+ return recovered;
140
+ }
141
+ const previous = finalizer.getState().paths[change.canonicalPath];
142
+ const source = change.op === "upsert" ? requireBytes(change) : undefined;
143
+ const sha256 = source ? sourceDigest(source) : previous?.sha256 ?? EMPTY_SHA256;
144
+ const generation = (previous?.generation ?? -1) + 1;
145
+ const baseVersionId = previous?.synchronizedVersionId ?? this.resolveBaseVersion({ apiUrl: this.apiUrl, hqRoot: this.hqRoot, scopeUid, canonicalPath: change.canonicalPath });
146
+ finalizer.recordDirty({ path: change.canonicalPath, generation, sha256, baseVersionId });
147
+ const plan = {
148
+ path: change.canonicalPath, generation, sha256, baseVersionId,
149
+ clientMutationId: randomUUID(), operation: change.op, membershipRevision, scopeRevision: scope.grant.scopeRevision,
150
+ ...(source ? { bytes: persistBytes(path.join(stateRoot, "pending-by-client-id"), randomUUID(), source, sourceModeFor(this.hqRoot, change.canonicalPath)) } : {}),
151
+ };
152
+ finalizer.recordMutationPlan(plan);
153
+ return this.submitPlan(finalizer, plan, authorization);
154
+ }
155
+ finally {
156
+ releasePathLock();
157
+ }
158
+ }
159
+ async resumePlan(finalizer, plan, authorization, membershipRevision, scopeRevision) {
160
+ if (plan.membershipRevision !== membershipRevision || plan.scopeRevision !== scopeRevision) {
161
+ throw new Error("realtime mutation: pending attempt authorization changed; recover only after V2 reset");
162
+ }
163
+ return this.submitPlan(finalizer, plan, authorization);
164
+ }
165
+ async submitPlan(finalizer, plan, authorization) {
166
+ const bytes = plan.operation === "upsert" ? readPersistedBytes(plan) : undefined;
167
+ const request = {
168
+ contractVersion: 2,
169
+ deviceId: this.deviceId,
170
+ clientMutationId: plan.clientMutationId,
171
+ canonicalPath: plan.path,
172
+ operation: plan.operation,
173
+ baseVersionId: plan.baseVersionId,
174
+ generation: plan.generation,
175
+ membershipRevision: plan.membershipRevision,
176
+ scopeRevision: plan.scopeRevision,
177
+ ...(plan.operation === "upsert" ? { source: { kind: "regular", mode: plan.bytes.mode, sha256: plan.sha256, size: bytes.byteLength } } : {}),
178
+ };
179
+ const result = await this.mutationClient.submit({
180
+ authorization, request, ...(bytes ? { bytes, spoolDirectory: path.dirname(plan.bytes.filePath) } : {}),
181
+ onPrepared: (attemptId) => finalizer.recordAttempt({ attemptId, path: plan.path, generation: plan.generation, sha256: plan.sha256, baseVersionId: plan.baseVersionId }),
182
+ });
183
+ if (result.status.status === "committed") {
184
+ // A lost prepare/commit response can leave the server terminal before a
185
+ // process had a chance to append onPrepared. Reconstruct the immutable
186
+ // attempt from the pre-prepare plan before finalization consumes it.
187
+ if (!finalizer.getState().paths[plan.path]?.attempts[result.status.attemptId]) {
188
+ finalizer.recordAttempt({ attemptId: result.status.attemptId, path: plan.path, generation: plan.generation, sha256: plan.sha256, baseVersionId: plan.baseVersionId });
189
+ }
190
+ finalizer.finalize({ attemptId: result.status.attemptId, path: plan.path, generation: plan.generation, sha256: plan.sha256, versionId: result.status.versionId, deltaId: result.status.deltaId });
191
+ if (plan.bytes) {
192
+ try {
193
+ fs.unlinkSync(plan.bytes.filePath);
194
+ }
195
+ catch { /* a retained committed spool is harmless recovery evidence */ }
196
+ }
197
+ }
198
+ return result;
199
+ }
200
+ }
201
+ function requireBytes(change) {
202
+ if (!change.bytes)
203
+ throw new Error("sync mutation: upsert requires contentBase64");
204
+ return change.bytes;
205
+ }
206
+ function persistBytes(directory, spoolId, bytes, mode) {
207
+ const spool = DurableAttemptSpool.create(directory, spoolId, bytes);
208
+ return { filePath: spool.filePath, size: spool.size, sha256: spool.sha256, mode };
209
+ }
210
+ function sourceModeFor(hqRoot, canonicalPath) {
211
+ const root = path.resolve(hqRoot);
212
+ const localPath = path.resolve(root, canonicalPath);
213
+ const relative = path.relative(root, localPath);
214
+ if (!relative || relative.startsWith(`..${path.sep}`) || path.isAbsolute(relative)) {
215
+ throw new Error("realtime mutation: canonical path escapes the local HQ root");
216
+ }
217
+ const stat = fs.statSync(localPath);
218
+ if (!stat.isFile())
219
+ throw new Error("realtime mutation: upsert source is not a regular file");
220
+ return stat.mode & 0o777;
221
+ }
222
+ /**
223
+ * Cross-process, stale-owner-recovering serialization for one scope/path.
224
+ * It covers generation allocation through commit, so concurrent sidecars cannot
225
+ * both reserve the same durable generation. The async poll deliberately keeps
226
+ * this process's event loop free while another local submission is in flight.
227
+ */
228
+ async function acquireMutationPathLock(stateRoot, scopeUid, canonicalPath) {
229
+ const directory = path.join(stateRoot, "submission-locks");
230
+ fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
231
+ const owner = `${process.pid}\n`;
232
+ const lockPath = path.join(directory, createHash("sha256").update(`${scopeUid}\0${canonicalPath}`).digest("hex") + ".lock");
233
+ for (;;) {
234
+ try {
235
+ const fd = fs.openSync(lockPath, "wx", 0o600);
236
+ try {
237
+ fs.writeFileSync(fd, owner);
238
+ fs.fsyncSync(fd);
239
+ }
240
+ finally {
241
+ fs.closeSync(fd);
242
+ }
243
+ return () => {
244
+ try {
245
+ if (fs.readFileSync(lockPath, "utf8") === owner)
246
+ fs.unlinkSync(lockPath);
247
+ }
248
+ catch {
249
+ // A stale owner can be reclaimed by a later process.
250
+ }
251
+ };
252
+ }
253
+ catch (error) {
254
+ if (error.code !== "EEXIST")
255
+ throw error;
256
+ try {
257
+ const ownerPid = Number(fs.readFileSync(lockPath, "utf8"));
258
+ try {
259
+ process.kill(ownerPid, 0);
260
+ }
261
+ catch (probeError) {
262
+ if (probeError.code === "ESRCH")
263
+ fs.unlinkSync(lockPath);
264
+ }
265
+ }
266
+ catch (readError) {
267
+ if (readError.code === "ENOENT")
268
+ continue;
269
+ }
270
+ await new Promise((resolve) => setTimeout(resolve, 20));
271
+ }
272
+ }
273
+ }
274
+ function readPersistedBytes(plan) {
275
+ if (!plan.bytes)
276
+ throw new Error("realtime mutation: durable recovery bytes are unavailable");
277
+ return Uint8Array.from(fs.readFileSync(plan.bytes.filePath));
278
+ }
279
+ function isSameChange(plan, change) {
280
+ return plan.operation === change.op && (change.op === "delete" || plan.sha256 === sourceDigest(requireBytes(change)));
281
+ }
282
+ //# sourceMappingURL=mutation-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutation-session.js","sourceRoot":"","sources":["../../src/sync/mutation-session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=mutation-session.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutation-session.test.d.ts","sourceRoot":"","sources":["../../src/sync/mutation-session.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,190 @@
1
+ import * as fs from "node:fs";
2
+ import * as os from "node:os";
3
+ import * as path from "node:path";
4
+ import { describe, expect, it, vi } from "vitest";
5
+ import { LocalEnrolledMutationSession, SyncMutationNotEnrolledError, } from "./mutation-session.js";
6
+ import { RealtimeEnrollmentUnavailableError } from "./lease-client.js";
7
+ import { CognitoAuthError, CognitoRefreshError } from "../cognito-auth.js";
8
+ function enrolledAuthorization(overrides = {}) {
9
+ const leasePayload = Buffer.from(JSON.stringify({ membershipRevision: "membership_7" })).toString("base64url");
10
+ return {
11
+ bearer: "bearer_1",
12
+ scopes: [{
13
+ grant: { scopeUid: "person_1", scopeHandle: "scope_1", scopeRevision: "scope_9", state: "v2-ready", cursor: "cursor_1" },
14
+ lease: {
15
+ lease: `${leasePayload}.signature`,
16
+ issuedAt: new Date(Date.now() - 1_000).toISOString(),
17
+ expiresAt: new Date(Date.now() + 30_000).toISOString(),
18
+ authorizationEpoch: "epoch_1",
19
+ ...overrides,
20
+ },
21
+ }],
22
+ };
23
+ }
24
+ function committed() {
25
+ return { requestDigest: "digest_1", status: { status: "committed", attemptId: "attempt_1", versionId: "version_1", deltaId: "delta_1" } };
26
+ }
27
+ describe("LocalEnrolledMutationSession", () => {
28
+ it("builds a complete authorized V2 upsert from only path and bytes", async () => {
29
+ const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-cloud-mutation-session-"));
30
+ const authorization = enrolledAuthorization();
31
+ fs.mkdirSync(path.join(hqRoot, "notes"), { recursive: true });
32
+ fs.writeFileSync(path.join(hqRoot, "notes", "a.md"), Buffer.from([0, 1, 2]));
33
+ fs.chmodSync(path.join(hqRoot, "notes", "a.md"), 0o754);
34
+ const authorizeWithBearer = vi.fn().mockResolvedValue(authorization);
35
+ let submitted;
36
+ const submit = vi.fn(async (input) => {
37
+ submitted = input;
38
+ await input.onPrepared?.("attempt_1");
39
+ return committed();
40
+ });
41
+ try {
42
+ const session = new LocalEnrolledMutationSession({
43
+ hqRoot,
44
+ deviceId: "device_1",
45
+ getAccessToken: async () => "bearer_1",
46
+ resolvePersonalScopeUid: async () => "person_1",
47
+ authorizer: { authorizeWithBearer },
48
+ resolveBaseVersion: () => "version_sync",
49
+ createMutationClient: () => ({ submit }),
50
+ });
51
+ await expect(session.submit({ canonicalPath: "notes/a.md", op: "upsert", bytes: Uint8Array.from([0, 1, 2]) })).resolves.toEqual(committed());
52
+ expect(authorizeWithBearer).toHaveBeenCalledWith("bearer_1", expect.objectContaining({ deviceId: "device_1", scopeUids: ["person_1"] }));
53
+ expect(submitted).toMatchObject({
54
+ authorization: { bearer: "bearer_1", deviceId: "device_1", lease: authorization.scopes[0].lease },
55
+ request: expect.objectContaining({
56
+ contractVersion: 2,
57
+ canonicalPath: "notes/a.md",
58
+ operation: "upsert",
59
+ baseVersionId: "version_sync",
60
+ generation: 0,
61
+ membershipRevision: "membership_7",
62
+ scopeRevision: "scope_9",
63
+ source: expect.objectContaining({ size: 3, mode: 0o754 }),
64
+ }),
65
+ bytes: Uint8Array.from([0, 1, 2]),
66
+ });
67
+ }
68
+ finally {
69
+ fs.rmSync(hqRoot, { recursive: true, force: true });
70
+ }
71
+ });
72
+ it("submits a delete without bytes", async () => {
73
+ const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-cloud-mutation-session-"));
74
+ const submit = vi.fn(async (input) => {
75
+ await input.onPrepared?.("attempt_1");
76
+ return committed();
77
+ });
78
+ try {
79
+ const session = new LocalEnrolledMutationSession({
80
+ hqRoot, deviceId: "device_1", getAccessToken: async () => "bearer_1",
81
+ resolvePersonalScopeUid: async () => "person_1", authorizer: { authorizeWithBearer: async () => enrolledAuthorization() },
82
+ resolveBaseVersion: () => null,
83
+ createMutationClient: () => ({ submit }),
84
+ });
85
+ await session.submit({ canonicalPath: "notes/a.md", op: "delete" });
86
+ expect(submit).toHaveBeenCalledWith(expect.objectContaining({
87
+ request: expect.objectContaining({ operation: "delete" }),
88
+ }));
89
+ expect(submit.mock.calls[0][0].request).not.toHaveProperty("source");
90
+ expect(submit.mock.calls[0][0]).not.toHaveProperty("bytes");
91
+ }
92
+ finally {
93
+ fs.rmSync(hqRoot, { recursive: true, force: true });
94
+ }
95
+ });
96
+ it("fails closed before V2 I/O when the local session is not enrolled or rollout-enabled", async () => {
97
+ const submit = vi.fn();
98
+ const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-cloud-mutation-session-"));
99
+ const session = new LocalEnrolledMutationSession({
100
+ hqRoot,
101
+ getAccessToken: async () => "bearer_1", resolvePersonalScopeUid: async () => "person_1",
102
+ authorizer: { authorizeWithBearer: async () => { throw new RealtimeEnrollmentUnavailableError(); } },
103
+ createMutationClient: () => ({ submit }),
104
+ });
105
+ await expect(session.submit({ canonicalPath: "notes/a.md", op: "delete" })).rejects.toBeInstanceOf(SyncMutationNotEnrolledError);
106
+ expect(submit).not.toHaveBeenCalled();
107
+ fs.rmSync(hqRoot, { recursive: true, force: true });
108
+ });
109
+ it("replays the exact durable prepared attempt before allocating another generation", async () => {
110
+ const hqRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-cloud-mutation-session-"));
111
+ fs.mkdirSync(path.join(hqRoot, "notes"), { recursive: true });
112
+ fs.writeFileSync(path.join(hqRoot, "notes", "a.md"), Buffer.from([1, 2, 3]));
113
+ const firstSubmit = vi.fn(async (input) => {
114
+ await input.onPrepared?.("attempt_1");
115
+ throw new Error("lost commit response");
116
+ });
117
+ const resolveBaseVersion = vi.fn(() => "version_sync");
118
+ const common = {
119
+ hqRoot, deviceId: "device_1", getAccessToken: async () => "bearer_1",
120
+ resolvePersonalScopeUid: async () => "person_1", authorizer: { authorizeWithBearer: async () => enrolledAuthorization() },
121
+ resolveBaseVersion,
122
+ };
123
+ try {
124
+ const first = new LocalEnrolledMutationSession({ ...common, createMutationClient: () => ({ submit: firstSubmit }) });
125
+ await expect(first.submit({ canonicalPath: "notes/a.md", op: "upsert", bytes: Uint8Array.from([1, 2, 3]) })).rejects.toThrow("lost commit response");
126
+ const firstRequest = firstSubmit.mock.calls[0][0].request;
127
+ const recoveredSubmit = vi.fn(async (_input) => committed());
128
+ const second = new LocalEnrolledMutationSession({ ...common, createMutationClient: () => ({ submit: recoveredSubmit }) });
129
+ await expect(second.submit({ canonicalPath: "notes/a.md", op: "upsert", bytes: Uint8Array.from([1, 2, 3]) })).resolves.toEqual(committed());
130
+ expect(recoveredSubmit.mock.calls[0][0].request).toMatchObject({
131
+ clientMutationId: firstRequest.clientMutationId,
132
+ generation: firstRequest.generation,
133
+ baseVersionId: "version_sync",
134
+ });
135
+ expect(recoveredSubmit.mock.calls[0][0].bytes).toEqual(Uint8Array.from([1, 2, 3]));
136
+ expect(resolveBaseVersion).toHaveBeenCalledTimes(1);
137
+ }
138
+ finally {
139
+ fs.rmSync(hqRoot, { recursive: true, force: true });
140
+ }
141
+ });
142
+ it("reports a missing local login as not enrolled without starting mutation I/O", async () => {
143
+ const submit = vi.fn();
144
+ const session = new LocalEnrolledMutationSession({
145
+ getAccessToken: async () => { throw new CognitoAuthError("No valid HQ session"); },
146
+ resolvePersonalScopeUid: async () => "person_1",
147
+ authorizer: { authorizeWithBearer: async () => enrolledAuthorization() },
148
+ createMutationClient: () => ({ submit }),
149
+ });
150
+ await expect(session.submit({ canonicalPath: "notes/a.md", op: "delete" })).rejects.toBeInstanceOf(SyncMutationNotEnrolledError);
151
+ expect(submit).not.toHaveBeenCalled();
152
+ });
153
+ it("keeps a transient token refresh failure retryable", async () => {
154
+ const transient = new CognitoRefreshError("refresh temporarily unavailable", false, 503);
155
+ const submit = vi.fn();
156
+ const session = new LocalEnrolledMutationSession({
157
+ getAccessToken: async () => { throw transient; },
158
+ resolvePersonalScopeUid: async () => "person_1",
159
+ authorizer: { authorizeWithBearer: async () => enrolledAuthorization() },
160
+ createMutationClient: () => ({ submit }),
161
+ });
162
+ await expect(session.submit({ canonicalPath: "notes/a.md", op: "delete" })).rejects.toBe(transient);
163
+ expect(submit).not.toHaveBeenCalled();
164
+ });
165
+ it("refuses stale leases or a stale-revision authorization before mutation I/O", async () => {
166
+ const staleLeaseSubmit = vi.fn();
167
+ const staleLeaseRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-cloud-mutation-session-"));
168
+ const staleLease = new LocalEnrolledMutationSession({
169
+ hqRoot: staleLeaseRoot,
170
+ getAccessToken: async () => "bearer_1", resolvePersonalScopeUid: async () => "person_1",
171
+ authorizer: { authorizeWithBearer: async () => enrolledAuthorization({ expiresAt: new Date(Date.now() - 1).toISOString() }) },
172
+ createMutationClient: () => ({ submit: staleLeaseSubmit }),
173
+ });
174
+ await expect(staleLease.submit({ canonicalPath: "notes/a.md", op: "delete" })).rejects.toThrow("lease expired");
175
+ expect(staleLeaseSubmit).not.toHaveBeenCalled();
176
+ const staleRevisionSubmit = vi.fn();
177
+ const staleRevisionRoot = fs.mkdtempSync(path.join(os.tmpdir(), "hq-cloud-mutation-session-"));
178
+ const staleRevision = new LocalEnrolledMutationSession({
179
+ hqRoot: staleRevisionRoot,
180
+ getAccessToken: async () => "bearer_1", resolvePersonalScopeUid: async () => "person_1",
181
+ authorizer: { authorizeWithBearer: async () => { throw new Error("realtime authorization: scope revision is stale"); } },
182
+ createMutationClient: () => ({ submit: staleRevisionSubmit }),
183
+ });
184
+ await expect(staleRevision.submit({ canonicalPath: "notes/a.md", op: "delete" })).rejects.toThrow("scope revision is stale");
185
+ expect(staleRevisionSubmit).not.toHaveBeenCalled();
186
+ fs.rmSync(staleLeaseRoot, { recursive: true, force: true });
187
+ fs.rmSync(staleRevisionRoot, { recursive: true, force: true });
188
+ });
189
+ });
190
+ //# sourceMappingURL=mutation-session.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mutation-session.test.js","sourceRoot":"","sources":["../../src/sync/mutation-session.test.ts"],"names":[],"mappings":"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"}
@@ -23,6 +23,23 @@ export interface DirtyGeneration {
23
23
  export interface LocalAttempt extends DirtyGeneration {
24
24
  attemptId: string;
25
25
  }
26
+ /**
27
+ * The immutable local identity and recovery bytes selected before `prepare`.
28
+ * Keeping this in the v3 journal makes a lost prepare response replayable with
29
+ * the same idempotency key rather than allocating a second generation.
30
+ */
31
+ export interface PlannedMutation extends DirtyGeneration {
32
+ clientMutationId: string;
33
+ operation: "upsert" | "delete";
34
+ membershipRevision: string;
35
+ scopeRevision: string;
36
+ bytes?: {
37
+ filePath: string;
38
+ size: number;
39
+ sha256: `sha256:${string}`;
40
+ mode: number;
41
+ };
42
+ }
26
43
  export interface CommittedMutation {
27
44
  attemptId: string;
28
45
  path: string;
@@ -52,6 +69,7 @@ export interface UploaderPathState {
52
69
  baseVersionId: string | null;
53
70
  synchronizedVersionId: string | null;
54
71
  pendingAttemptId?: string;
72
+ plannedMutation?: PlannedMutation;
55
73
  attempts: Record<string, LocalAttempt>;
56
74
  }
57
75
  export interface UploaderFinalizationState {
@@ -82,6 +100,8 @@ export declare class UploaderFinalizer {
82
100
  getState(): UploaderFinalizationState;
83
101
  recordDirty(input: DirtyGeneration): void;
84
102
  recordAttempt(input: LocalAttempt): void;
103
+ /** Persist the replay identity and durable source before any prepare I/O. */
104
+ recordMutationPlan(input: PlannedMutation): void;
85
105
  /**
86
106
  * Atomically records the logical server result and the self-delta marker.
87
107
  * It never changes the scope cursor: only consumeSelfDelta may do that.
@@ -1 +1 @@
1
- {"version":3,"file":"uploader-finalization.d.ts","sourceRoot":"","sources":["../../src/sync/uploader-finalization.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"uploader-finalization.d.ts","sourceRoot":"","sources":["../../src/sync/uploader-finalization.ts"],"names":[],"mappings":"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"}