diff --git a/packages/typescript/src/api/async/api.ts b/packages/typescript/src/api/async/api.ts index 912710b5cb4a5..bba0b24ac7d51 100644 --- a/packages/typescript/src/api/async/api.ts +++ b/packages/typescript/src/api/async/api.ts @@ -407,6 +407,10 @@ export class API implements FormatDiagnosticsHo snapshot: baseSnapshot.id, changes: toCreateSnapshotRequest(params), }); + if (data.snapshot === baseSnapshot.id) { + await this.client.apiRequest("release", { snapshot: data.snapshot }); + return baseSnapshot; + } this.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes); const snapshot = new Snapshot( data, diff --git a/packages/typescript/src/api/sync/api.ts b/packages/typescript/src/api/sync/api.ts index 8f942c3feb83f..16a70705229a4 100644 --- a/packages/typescript/src/api/sync/api.ts +++ b/packages/typescript/src/api/sync/api.ts @@ -639,6 +639,10 @@ export class API implements FormatDiagnosticsHo snapshot: baseSnapshot.id, changes: toCreateSnapshotRequest(params), }); + if (data.snapshot === baseSnapshot.id) { + owner.client.apiRequest("release", { snapshot: data.snapshot }); + return baseSnapshot; + } owner.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes); const snapshot = new Snapshot( data, @@ -666,6 +670,10 @@ export class API implements FormatDiagnosticsHo snapshot: baseSnapshot.id, changes: toCreateSnapshotRequest(params), }); + if (data.snapshot === baseSnapshot.id) { + yield* apiRequest("release", { snapshot: data.snapshot }); + return baseSnapshot; + } owner.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes); const snapshot = new Snapshot( data, diff --git a/packages/typescript/test/async/api.test.ts b/packages/typescript/test/async/api.test.ts index 0a15cdff436bc..534e2ae00e7f4 100644 --- a/packages/typescript/test/async/api.test.ts +++ b/packages/typescript/test/async/api.test.ts @@ -400,6 +400,38 @@ describe("API", () => { assert.equal("openedFiles" in empty.operation, false); }); + test("snapshot.update preserves identity when the server returns the same snapshot", async () => { + await using api = spawnAPI(); + const snapshot = await api.createSnapshot(); + const client = (api as unknown as { + client: { apiRequest(method: string, params: unknown): Promise; }; + }).client; + const apiRequest = client.apiRequest.bind(client); + let duplicateReference = false; + client.apiRequest = async (method, params) => { + if ( + method === "release" + && typeof params === "object" + && params !== null + && "snapshot" in params + && params.snapshot === snapshot.id + && duplicateReference + ) { + duplicateReference = false; + return true; + } + const response = await apiRequest(method, params); + if (method !== "updateSnapshot" || typeof response !== "object" || response === null) return response; + if (!("snapshot" in response) || typeof response.snapshot !== "number") return response; + await apiRequest("release", { snapshot: response.snapshot }); + duplicateReference = true; + return { ...response, snapshot: snapshot.id }; + }; + + assert.strictEqual(await snapshot.update({}), snapshot); + assert.equal(duplicateReference, false); + }); + test("snapshot.update reconfigures a synthetic program", async () => { await using api = spawnAPI({ "/src/a.ts": `export const a = 1;`, diff --git a/packages/typescript/test/sync/api.test.ts b/packages/typescript/test/sync/api.test.ts index f20c229bbfce3..1aaad229ecc76 100644 --- a/packages/typescript/test/sync/api.test.ts +++ b/packages/typescript/test/sync/api.test.ts @@ -389,6 +389,38 @@ describe("API", () => { assert.equal("openedFiles" in empty.operation, false); }); + test("snapshot.update preserves identity when the server returns the same snapshot", () => { + using api = spawnAPI(); + const snapshot = api.createSnapshot(); + const client = (api as unknown as { + client: { apiRequest(method: string, params: unknown): unknown; }; + }).client; + const apiRequest = client.apiRequest.bind(client); + let duplicateReference = false; + client.apiRequest = (method, params) => { + if ( + method === "release" + && typeof params === "object" + && params !== null + && "snapshot" in params + && params.snapshot === snapshot.id + && duplicateReference + ) { + duplicateReference = false; + return true; + } + const response = apiRequest(method, params); + if (method !== "updateSnapshot" || typeof response !== "object" || response === null) return response; + if (!("snapshot" in response) || typeof response.snapshot !== "number") return response; + apiRequest("release", { snapshot: response.snapshot }); + duplicateReference = true; + return { ...response, snapshot: snapshot.id }; + }; + + assert.strictEqual(snapshot.update({}), snapshot); + assert.equal(duplicateReference, false); + }); + test("snapshot.update reconfigures a synthetic program", () => { using api = spawnAPI({ "/src/a.ts": `export const a = 1;`,