typescript 7.1.0-dev.20260917.1 → 7.1.0-dev.20260918.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.
@@ -11,6 +11,7 @@ import { ModuleResolutionKind } from "#enums/moduleResolutionKind";
11
11
  import { NewLineKind } from "#enums/newLineKind";
12
12
  import { NodeBuilderFlags } from "#enums/nodeBuilderFlags";
13
13
  import { ObjectFlags } from "#enums/objectFlags";
14
+ import { ScriptKind } from "#enums/scriptKind";
14
15
  import { SignatureFlags } from "#enums/signatureFlags";
15
16
  import { SignatureKind } from "#enums/signatureKind";
16
17
  import { SymbolFlags } from "#enums/symbolFlags";
@@ -23,28 +24,31 @@ import { encodeNode, uint8ArrayToBase64, } from "../node/encoder.js";
23
24
  import { decodeNode, getNodeId, parseNodeHandle, readParseOptionsKey, readSourceFileHash, RemoteSourceFile, } from "../node/node.js";
24
25
  import { Wtf8Decoder } from "../node/wtf8.js";
25
26
  import { createGetCanonicalFileName, toPath, } from "../path.js";
26
- import { resolveFileName, toUpdateSnapshotRequest, } from "../proto.js";
27
+ import { resolveFileName, toCreateSnapshotRequest, } from "../proto.js";
27
28
  import { SourceFileCache } from "../sourceFileCache.js";
28
29
  import { Client, } from "./client.js";
29
30
  export { formatDiagnostics, formatDiagnosticsWithColorAndContext } from "../diagnosticFormatter.js";
30
31
  export { documentURIToFileName, fileNameToDocumentURI } from "../path.js";
31
- export { CheckFlags, CompletionItemKind, DiagnosticCategory, ElementFlags, EmitOnly, IndexKind, JsxEmit, ModifierFlags, ModuleKind, ModuleResolutionKind, NodeBuilderFlags, ObjectFlags, SignatureFlags, SignatureKind, SymbolFlags, TypeFlags, TypeFormatFlags, TypePredicateKind };
32
+ export { CheckFlags, CompletionItemKind, DiagnosticCategory, ElementFlags, EmitOnly, IndexKind, JsxEmit, ModifierFlags, ModuleKind, ModuleResolutionKind, NodeBuilderFlags, ObjectFlags, ScriptKind, SignatureFlags, SignatureKind, SymbolFlags, TypeFlags, TypeFormatFlags, TypePredicateKind };
32
33
  export { all, defer } from "./generatorSupport.js";
34
+ import { sourceFileResponseToUint8Array } from "../node/encoder.js";
33
35
  import { executeRequestGenerators, } from "./generatorSupport.js";
34
36
  export class API {
35
37
  client;
36
38
  sourceFileCache;
37
39
  toPath;
38
40
  currentDirectory;
41
+ decoder = new Wtf8Decoder();
39
42
  getCanonicalFileNameWorker;
40
43
  initialized = false;
41
44
  initializing;
42
45
  activeSnapshots = new Set();
43
- latestSnapshot;
46
+ printer;
44
47
  internal;
45
48
  constructor(options = {}) {
46
49
  this.client = new Client(options);
47
50
  this.sourceFileCache = new SourceFileCache();
51
+ this.printer = new Printer(this.client);
48
52
  this.internal = new InternalAPI(this.client, this.ensureInitialized);
49
53
  }
50
54
  /**
@@ -165,6 +169,42 @@ export class API {
165
169
  return yield* apiRequest("parseJsonConfigFileContent", { json, ...options });
166
170
  });
167
171
  }
172
+ get createSourceFile() {
173
+ const owner = this;
174
+ return cacheGeneratorMethod(owner, "createSourceFile", function (fileName, sourceText, options = {}) {
175
+ owner.ensureInitialized();
176
+ const data = owner.client.apiRequestBinary("createSourceFile", { fileName, sourceText, options });
177
+ if (!data) {
178
+ throw new Error("createSourceFile returned no source file");
179
+ }
180
+ return new RemoteSourceFile(data, owner.decoder, owner.client.getTimingCollector());
181
+ }, function* (fileName, sourceText, options = {}) {
182
+ yield* owner.ensureInitialized.gen();
183
+ const data = sourceFileResponseToUint8Array(yield* apiRequest("createSourceFile", { fileName, sourceText, options }));
184
+ if (!data) {
185
+ throw new Error("createSourceFile returned no source file");
186
+ }
187
+ return new RemoteSourceFile(data, owner.decoder, owner.client.getTimingCollector());
188
+ });
189
+ }
190
+ get createSourceFileFromFile() {
191
+ const owner = this;
192
+ return cacheGeneratorMethod(owner, "createSourceFileFromFile", function (file, options = {}) {
193
+ owner.ensureInitialized();
194
+ const data = owner.client.apiRequestBinary("createSourceFileFromFile", { fileName: resolveFileName(file), options });
195
+ if (!data) {
196
+ throw new Error("createSourceFileFromFile returned no source file");
197
+ }
198
+ return new RemoteSourceFile(data, owner.decoder, owner.client.getTimingCollector());
199
+ }, function* (file, options = {}) {
200
+ yield* owner.ensureInitialized.gen();
201
+ const data = sourceFileResponseToUint8Array(yield* apiRequest("createSourceFileFromFile", { fileName: resolveFileName(file), options }));
202
+ if (!data) {
203
+ throw new Error("createSourceFileFromFile returned no source file");
204
+ }
205
+ return new RemoteSourceFile(data, owner.decoder, owner.client.getTimingCollector());
206
+ });
207
+ }
168
208
  get transpileModule() {
169
209
  const owner = this;
170
210
  return cacheGeneratorMethod(owner, "transpileModule", function (input, options = {}) {
@@ -205,82 +245,125 @@ export class API {
205
245
  return yield* apiRequest("transpileDeclarationFromFile", { fileName: resolveFileName(file), options });
206
246
  });
207
247
  }
208
- get updateSnapshot() {
248
+ get createSnapshot() {
209
249
  const owner = this;
210
- return cacheGeneratorMethod(owner, "updateSnapshot", function (params) {
211
- return owner.updateSnapshotWorker(params);
212
- }, function* (params) {
213
- return yield* owner.updateSnapshotWorker.gen(params);
214
- });
250
+ function createSnapshot(params) {
251
+ owner.ensureInitialized();
252
+ const requestParams = toCreateSnapshotRequest(params);
253
+ const data = owner.client.apiRequest("createSnapshot", requestParams);
254
+ const snapshot = new Snapshot(data, owner.client, owner.sourceFileCache, owner.toPath, owner, () => {
255
+ owner.activeSnapshots.delete(snapshot);
256
+ owner.sourceFileCache.releaseSnapshot(snapshot.id);
257
+ }, owner.createSnapshotUpdater(() => snapshot), undefined);
258
+ owner.activeSnapshots.add(snapshot);
259
+ return snapshot;
260
+ }
261
+ function* gen(params) {
262
+ yield* owner.ensureInitialized.gen();
263
+ const requestParams = toCreateSnapshotRequest(params);
264
+ const data = yield* apiRequest("createSnapshot", requestParams);
265
+ const snapshot = new Snapshot(data, owner.client, owner.sourceFileCache, owner.toPath, owner, () => {
266
+ owner.activeSnapshots.delete(snapshot);
267
+ owner.sourceFileCache.releaseSnapshot(snapshot.id);
268
+ }, owner.createSnapshotUpdater(() => snapshot), undefined);
269
+ owner.activeSnapshots.add(snapshot);
270
+ return snapshot;
271
+ }
272
+ return cacheGeneratorMethod(owner, "createSnapshot", createSnapshot, gen);
215
273
  }
216
- /** @internal */
217
- get updateSnapshotFrom() {
274
+ get updateSnapshot() {
218
275
  const owner = this;
219
- return cacheGeneratorMethod(owner, "updateSnapshotFrom", function (baseSnapshot, params) {
276
+ return cacheGeneratorMethod(owner, "updateSnapshot", function (baseSnapshot, params) {
277
+ owner.ensureInitialized();
220
278
  if (!owner.activeSnapshots.has(baseSnapshot) || baseSnapshot.isDisposed()) {
221
279
  throw new Error("Cannot update an inactive snapshot");
222
280
  }
223
- if (baseSnapshot !== owner.latestSnapshot) {
224
- // TODO: Support forking active memory/cache snapshots once the server-side
225
- // ownership, project state, and cache semantics have been worked out.
226
- throw new Error("Snapshot.update can only update the latest snapshot");
227
- }
228
- return owner.updateSnapshotWorker(params, baseSnapshot);
281
+ const data = owner.client.apiRequest("updateSnapshot", {
282
+ snapshot: baseSnapshot.id,
283
+ changes: toCreateSnapshotRequest(params),
284
+ });
285
+ owner.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes);
286
+ const snapshot = new Snapshot(data, owner.client, owner.sourceFileCache, owner.toPath, owner, () => {
287
+ owner.activeSnapshots.delete(snapshot);
288
+ owner.sourceFileCache.releaseSnapshot(snapshot.id);
289
+ }, owner.createSnapshotUpdater(() => snapshot), baseSnapshot);
290
+ owner.activeSnapshots.add(snapshot);
291
+ return snapshot;
229
292
  }, function* (baseSnapshot, params) {
293
+ yield* owner.ensureInitialized.gen();
230
294
  if (!owner.activeSnapshots.has(baseSnapshot) || baseSnapshot.isDisposed()) {
231
295
  throw new Error("Cannot update an inactive snapshot");
232
296
  }
233
- if (baseSnapshot !== owner.latestSnapshot) {
234
- // TODO: Support forking active memory/cache snapshots once the server-side
235
- // ownership, project state, and cache semantics have been worked out.
236
- throw new Error("Snapshot.update can only update the latest snapshot");
237
- }
238
- return yield* owner.updateSnapshotWorker.gen(params, baseSnapshot);
297
+ const data = yield* apiRequest("updateSnapshot", {
298
+ snapshot: baseSnapshot.id,
299
+ changes: toCreateSnapshotRequest(params),
300
+ });
301
+ owner.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes);
302
+ const snapshot = new Snapshot(data, owner.client, owner.sourceFileCache, owner.toPath, owner, () => {
303
+ owner.activeSnapshots.delete(snapshot);
304
+ owner.sourceFileCache.releaseSnapshot(snapshot.id);
305
+ }, owner.createSnapshotUpdater(() => snapshot), baseSnapshot);
306
+ owner.activeSnapshots.add(snapshot);
307
+ return snapshot;
239
308
  });
240
309
  }
241
- get updateSnapshotWorker() {
310
+ createSnapshotUpdater(getSnapshot) {
311
+ const update = ((params) => this.updateSnapshot(getSnapshot(), params));
312
+ const owner = this;
313
+ update.gen = function* (params) {
314
+ return yield* owner.updateSnapshot.gen(getSnapshot(), params);
315
+ };
316
+ return update;
317
+ }
318
+ /**
319
+ * Returns the language server's current canonical snapshot after atomically
320
+ * adopting any supplied API-driven changes. Only available on LSP-connected APIs.
321
+ */
322
+ get getCurrentLanguageServerSnapshot() {
242
323
  const owner = this;
243
- return cacheGeneratorMethod(owner, "updateSnapshotWorker", function (params, baseSnapshot) {
324
+ function getCurrentLanguageServerSnapshot(...args) {
244
325
  owner.ensureInitialized();
245
- const requestParams = toUpdateSnapshotRequest(params, baseSnapshot?.id);
246
- const data = owner.client.apiRequest("updateSnapshot", requestParams);
247
- // Retain cached source files from previous snapshot for unchanged files
248
- if (owner.latestSnapshot) {
249
- owner.sourceFileCache.retainForSnapshot(data.snapshot, owner.latestSnapshot.id, data.changes);
250
- if (owner.latestSnapshot.isDisposed()) {
251
- owner.sourceFileCache.releaseSnapshot(owner.latestSnapshot.id);
252
- }
326
+ const changes = args[0];
327
+ const baseSnapshot = args[1];
328
+ if (baseSnapshot && (!owner.activeSnapshots.has(baseSnapshot) || baseSnapshot.isDisposed())) {
329
+ throw new Error("Cannot use an inactive snapshot as a response base");
330
+ }
331
+ const data = owner.client.apiRequest("getCurrentLanguageServerSnapshot", {
332
+ ...(baseSnapshot ? { baseSnapshot: baseSnapshot.id } : {}),
333
+ ...(changes ? { changes } : {}),
334
+ });
335
+ if (baseSnapshot) {
336
+ owner.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes);
253
337
  }
254
338
  const snapshot = new Snapshot(data, owner.client, owner.sourceFileCache, owner.toPath, owner, () => {
255
339
  owner.activeSnapshots.delete(snapshot);
256
- if (snapshot !== owner.latestSnapshot) {
257
- owner.sourceFileCache.releaseSnapshot(snapshot.id);
258
- }
259
- });
260
- owner.latestSnapshot = snapshot;
340
+ owner.sourceFileCache.releaseSnapshot(snapshot.id);
341
+ }, owner.createSnapshotUpdater(() => snapshot), baseSnapshot);
261
342
  owner.activeSnapshots.add(snapshot);
262
343
  return snapshot;
263
- }, function* (params, baseSnapshot) {
344
+ }
345
+ function* gen(...args) {
264
346
  yield* owner.ensureInitialized.gen();
265
- const requestParams = toUpdateSnapshotRequest(params, baseSnapshot?.id);
266
- const data = yield* apiRequest("updateSnapshot", requestParams);
267
- // Retain cached source files from previous snapshot for unchanged files
268
- if (owner.latestSnapshot) {
269
- owner.sourceFileCache.retainForSnapshot(data.snapshot, owner.latestSnapshot.id, data.changes);
270
- if (owner.latestSnapshot.isDisposed()) {
271
- owner.sourceFileCache.releaseSnapshot(owner.latestSnapshot.id);
272
- }
347
+ const changes = args[0];
348
+ const baseSnapshot = args[1];
349
+ if (baseSnapshot && (!owner.activeSnapshots.has(baseSnapshot) || baseSnapshot.isDisposed())) {
350
+ throw new Error("Cannot use an inactive snapshot as a response base");
351
+ }
352
+ const data = yield* apiRequest("getCurrentLanguageServerSnapshot", {
353
+ ...(baseSnapshot ? { baseSnapshot: baseSnapshot.id } : {}),
354
+ ...(changes ? { changes } : {}),
355
+ });
356
+ if (baseSnapshot) {
357
+ owner.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes);
273
358
  }
274
359
  const snapshot = new Snapshot(data, owner.client, owner.sourceFileCache, owner.toPath, owner, () => {
275
360
  owner.activeSnapshots.delete(snapshot);
276
- if (snapshot !== owner.latestSnapshot) {
277
- owner.sourceFileCache.releaseSnapshot(snapshot.id);
278
- }
279
- });
280
- owner.latestSnapshot = snapshot;
361
+ owner.sourceFileCache.releaseSnapshot(snapshot.id);
362
+ }, owner.createSnapshotUpdater(() => snapshot), baseSnapshot);
281
363
  owner.activeSnapshots.add(snapshot);
282
364
  return snapshot;
283
- });
365
+ }
366
+ return cacheGeneratorMethod(owner, "getCurrentLanguageServerSnapshot", getCurrentLanguageServerSnapshot, gen);
284
367
  }
285
368
  [globalThis.Symbol.dispose]() {
286
369
  this.close();
@@ -293,11 +376,6 @@ export class API {
293
376
  for (const snapshot of [...owner.activeSnapshots]) {
294
377
  snapshot.dispose();
295
378
  }
296
- // Release the latest snapshot's cache refs if still held
297
- if (owner.latestSnapshot) {
298
- owner.sourceFileCache.releaseSnapshot(owner.latestSnapshot.id);
299
- owner.latestSnapshot = undefined;
300
- }
301
379
  owner.sourceFileCache.clear();
302
380
  }
303
381
  finally {
@@ -309,11 +387,6 @@ export class API {
309
387
  for (const snapshot of [...owner.activeSnapshots]) {
310
388
  yield* snapshot.dispose.gen();
311
389
  }
312
- // Release the latest snapshot's cache refs if still held
313
- if (owner.latestSnapshot) {
314
- owner.sourceFileCache.releaseSnapshot(owner.latestSnapshot.id);
315
- owner.latestSnapshot = undefined;
316
- }
317
390
  owner.sourceFileCache.clear();
318
391
  }
319
392
  finally {
@@ -331,16 +404,13 @@ export class API {
331
404
  if (!owner.activeSnapshots.has(baseSnapshot) || baseSnapshot.isDisposed()) {
332
405
  throw new Error("Cannot run a temporary file update on an inactive snapshot");
333
406
  }
334
- const data = owner.client.apiRequest("updateTemporarySnapshot", { snapshot: baseSnapshot.id, file, newText });
335
- // Retain cached source files from the base snapshot for files unchanged by
336
- // the temporary update. The temporary snapshot is not the latest snapshot, so
337
- // we never release the latest snapshot's cache here.
338
- owner.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes);
339
- const snapshot = new Snapshot(data, owner.client, owner.sourceFileCache, owner.toPath, owner, () => {
340
- owner.activeSnapshots.delete(snapshot);
341
- owner.sourceFileCache.releaseSnapshot(snapshot.id);
407
+ const snapshot = baseSnapshot.update({
408
+ fileSystem: {
409
+ kind: "layer",
410
+ files: { [resolveFileName(file)]: newText },
411
+ },
412
+ ensurePrograms: true,
342
413
  });
343
- owner.activeSnapshots.add(snapshot);
344
414
  try {
345
415
  cb(snapshot);
346
416
  }
@@ -352,16 +422,13 @@ export class API {
352
422
  if (!owner.activeSnapshots.has(baseSnapshot) || baseSnapshot.isDisposed()) {
353
423
  throw new Error("Cannot run a temporary file update on an inactive snapshot");
354
424
  }
355
- const data = yield* apiRequest("updateTemporarySnapshot", { snapshot: baseSnapshot.id, file, newText });
356
- // Retain cached source files from the base snapshot for files unchanged by
357
- // the temporary update. The temporary snapshot is not the latest snapshot, so
358
- // we never release the latest snapshot's cache here.
359
- owner.sourceFileCache.retainForSnapshot(data.snapshot, baseSnapshot.id, data.changes);
360
- const snapshot = new Snapshot(data, owner.client, owner.sourceFileCache, owner.toPath, owner, () => {
361
- owner.activeSnapshots.delete(snapshot);
362
- owner.sourceFileCache.releaseSnapshot(snapshot.id);
425
+ const snapshot = yield* baseSnapshot.update.gen({
426
+ fileSystem: {
427
+ kind: "layer",
428
+ files: { [resolveFileName(file)]: newText },
429
+ },
430
+ ensurePrograms: true,
363
431
  });
364
- owner.activeSnapshots.add(snapshot);
365
432
  try {
366
433
  yield* (cb(snapshot) ?? []);
367
434
  }
@@ -398,69 +465,32 @@ export class API {
398
465
  return owner.client.resetTimingInfo();
399
466
  });
400
467
  }
401
- isProgramActive(program) {
402
- const project = program.getProject();
403
- for (const snapshot of this.activeSnapshots) {
404
- if (!snapshot.isDisposed() && snapshot.getProject(project.configFileName)?.program === program) {
405
- return true;
406
- }
407
- }
408
- return false;
409
- }
410
- /**
411
- * Creates a program from current filesystem state, or derives one from oldProgram after applying fileChanges.
412
- */
468
+ /** Creates a program from current filesystem state. */
413
469
  get createProgram() {
414
470
  const owner = this;
415
- return cacheGeneratorMethod(owner, "createProgram", function (rootFiles, createProgramOptions, oldProgram, fileChanges) {
471
+ return cacheGeneratorMethod(owner, "createProgram", function (rootFiles, createProgramOptions) {
416
472
  owner.ensureInitialized();
417
- if (fileChanges && !oldProgram) {
418
- throw new Error("fileChanges requires an oldProgram");
419
- }
420
- if (oldProgram && !owner.isProgramActive(oldProgram)) {
421
- throw new Error("oldProgram must belong to this API instance and reference an active snapshot");
422
- }
423
- const data = owner.client.apiRequest("createProgram", {
424
- rootFiles,
425
- createProgramOptions,
426
- oldProgram: oldProgram ? { snapshot: oldProgram.snapshotId, project: oldProgram.getProject().id } : undefined,
427
- fileChanges,
473
+ const snapshot = owner.createSnapshot({
474
+ createPrograms: [{ rootFiles, options: createProgramOptions }],
428
475
  });
429
- if (!data.project) {
476
+ const program = snapshot.operation.createdPrograms[0];
477
+ if (!program) {
478
+ snapshot.dispose();
430
479
  throw new Error("createProgram did not return a project");
431
480
  }
432
- const snapshot = new Snapshot({ snapshot: data.snapshot, projects: [data.project] }, owner.client, owner.sourceFileCache, owner.toPath, owner, () => {
433
- owner.activeSnapshots.delete(snapshot);
434
- owner.sourceFileCache.releaseSnapshot(snapshot.id);
435
- });
436
- const program = snapshot.getProjects()[0].program;
437
481
  program.setOwnedSnapshot(snapshot);
438
- owner.activeSnapshots.add(snapshot);
439
482
  return program;
440
- }, function* (rootFiles, createProgramOptions, oldProgram, fileChanges) {
483
+ }, function* (rootFiles, createProgramOptions) {
441
484
  yield* owner.ensureInitialized.gen();
442
- if (fileChanges && !oldProgram) {
443
- throw new Error("fileChanges requires an oldProgram");
444
- }
445
- if (oldProgram && !owner.isProgramActive(oldProgram)) {
446
- throw new Error("oldProgram must belong to this API instance and reference an active snapshot");
447
- }
448
- const data = yield* apiRequest("createProgram", {
449
- rootFiles,
450
- createProgramOptions,
451
- oldProgram: oldProgram ? { snapshot: oldProgram.snapshotId, project: oldProgram.getProject().id } : undefined,
452
- fileChanges,
485
+ const snapshot = yield* owner.createSnapshot.gen({
486
+ createPrograms: [{ rootFiles, options: createProgramOptions }],
453
487
  });
454
- if (!data.project) {
488
+ const program = snapshot.operation.createdPrograms[0];
489
+ if (!program) {
490
+ yield* snapshot.dispose.gen();
455
491
  throw new Error("createProgram did not return a project");
456
492
  }
457
- const snapshot = new Snapshot({ snapshot: data.snapshot, projects: [data.project] }, owner.client, owner.sourceFileCache, owner.toPath, owner, () => {
458
- owner.activeSnapshots.delete(snapshot);
459
- owner.sourceFileCache.releaseSnapshot(snapshot.id);
460
- });
461
- const program = snapshot.getProjects()[0].program;
462
493
  program.setOwnedSnapshot(snapshot);
463
- owner.activeSnapshots.add(snapshot);
464
494
  return program;
465
495
  });
466
496
  }
@@ -510,37 +540,76 @@ export class InternalAPI {
510
540
  }
511
541
  export class Snapshot {
512
542
  id;
543
+ operation;
513
544
  projectMap;
514
545
  toPath;
515
546
  client;
516
547
  disposed = false;
517
548
  disposePromise;
518
549
  onDispose;
519
- api;
520
550
  snapshotRegistry;
551
+ projectDataMap;
552
+ updateSnapshot;
521
553
  internal;
522
- constructor(data, client, sourceFileCache, toPath, api, onDispose) {
554
+ constructor(data, client, sourceFileCache, toPath, formatDiagnosticsHost, onDispose, updateSnapshot, baseSnapshot) {
523
555
  this.id = data.snapshot;
524
556
  this.client = client;
525
557
  this.toPath = toPath;
526
- this.api = api;
527
558
  this.onDispose = onDispose;
559
+ this.updateSnapshot = updateSnapshot;
528
560
  this.projectMap = new Map();
561
+ const projectDataMap = new Map(baseSnapshot?.projectDataMap);
562
+ for (const projectId of data.changes?.removedProjects ?? []) {
563
+ projectDataMap.delete(projectId);
564
+ }
565
+ for (const projectData of data.projects) {
566
+ projectDataMap.set(projectData.id, projectData);
567
+ }
568
+ this.projectDataMap = new Map([...projectDataMap].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0));
529
569
  this.snapshotRegistry = new SnapshotObjectRegistry(client, this.id, projectId => this.projectMap.get(projectId));
530
- for (const projData of data.projects) {
531
- const project = new Project(projData, this.id, client, sourceFileCache, toPath, api, this.snapshotRegistry);
532
- this.projectMap.set(toPath(projData.configFileName), project);
570
+ for (const projData of this.projectDataMap.values()) {
571
+ const project = new Project(projData, this.id, client, sourceFileCache, toPath, formatDiagnosticsHost, this.snapshotRegistry);
572
+ this.projectMap.set(projData.id, project);
533
573
  }
574
+ this.operation = {
575
+ ...(data.operation.createdPrograms ? { createdPrograms: data.operation.createdPrograms.map(projectId => this.requireProject(projectId).program) } : {}),
576
+ ...(data.operation.openedFiles ? { openedFiles: data.operation.openedFiles.map(result => ({ project: this.requireProject(result.project) })) } : {}),
577
+ };
534
578
  this.internal = new SnapshotInternalAPI(this.id, client);
535
579
  }
536
580
  getProjects() {
537
581
  this.ensureNotDisposed();
538
582
  return [...this.projectMap.values()];
539
583
  }
540
- getProject(configFileName) {
584
+ getConfiguredProject(configFileName) {
541
585
  this.ensureNotDisposed();
542
586
  return this.projectMap.get(this.toPath(configFileName));
543
587
  }
588
+ getProject(projectId) {
589
+ this.ensureNotDisposed();
590
+ return this.projectMap.get(projectId);
591
+ }
592
+ getProgram(projectId) {
593
+ return this.getProject(projectId)?.program;
594
+ }
595
+ get update() {
596
+ const owner = this;
597
+ function update(params) {
598
+ owner.ensureNotDisposed();
599
+ return owner.updateSnapshot(params);
600
+ }
601
+ function* gen(params) {
602
+ owner.ensureNotDisposed();
603
+ return yield* owner.updateSnapshot.gen(params);
604
+ }
605
+ return cacheGeneratorMethod(owner, "update", update, gen);
606
+ }
607
+ /**
608
+ * Gets the default project for a given file from the configured projects and
609
+ * inferred project already loaded in the snapshot. Synthetic projects are not
610
+ * considered. Files that have been opened with `openFiles` are guaranteed to
611
+ * have a result.
612
+ */
544
613
  get getDefaultProjectForFile() {
545
614
  const owner = this;
546
615
  return cacheGeneratorMethod(owner, "getDefaultProjectForFile", function (file) {
@@ -551,7 +620,7 @@ export class Snapshot {
551
620
  });
552
621
  if (!data)
553
622
  return undefined;
554
- return owner.projectMap.get(owner.toPath(data.configFileName));
623
+ return owner.projectMap.get(data.id);
555
624
  }, function* (file) {
556
625
  owner.ensureNotDisposed();
557
626
  const data = yield* apiRequest("getDefaultProjectForFile", {
@@ -560,21 +629,7 @@ export class Snapshot {
560
629
  });
561
630
  if (!data)
562
631
  return undefined;
563
- return owner.projectMap.get(owner.toPath(data.configFileName));
564
- });
565
- }
566
- /**
567
- * Creates the next snapshot, layering its filesystem over this snapshot's
568
- * filesystem. This snapshot must still be active and be the latest snapshot.
569
- */
570
- get update() {
571
- const owner = this;
572
- return cacheGeneratorMethod(owner, "update", function (params) {
573
- owner.ensureNotDisposed();
574
- return owner.api.updateSnapshotFrom(owner, params);
575
- }, function* (params) {
576
- owner.ensureNotDisposed();
577
- return yield* owner.api.updateSnapshotFrom.gen(owner, params);
632
+ return owner.projectMap.get(data.id);
578
633
  });
579
634
  }
580
635
  [globalThis.Symbol.dispose]() {
@@ -630,6 +685,13 @@ export class Snapshot {
630
685
  throw new Error("Snapshot is disposed");
631
686
  }
632
687
  }
688
+ requireProject(projectId) {
689
+ const project = this.projectMap.get(projectId);
690
+ if (!project) {
691
+ throw new Error(`Snapshot operation returned unknown project '${projectId}'`);
692
+ }
693
+ return project;
694
+ }
633
695
  }
634
696
  class SnapshotObjectRegistry {
635
697
  symbols = new Map();
@@ -641,7 +703,7 @@ class SnapshotObjectRegistry {
641
703
  this.snapshotId = snapshotId;
642
704
  this.resolveProject = resolveProject;
643
705
  }
644
- /** Resolve a project id (a config file path) to its Project within this snapshot. */
706
+ /** Resolve a project ID to its Project within this snapshot. */
645
707
  getProject(projectId) {
646
708
  return this.resolveProject(projectId);
647
709
  }
@@ -1099,6 +1161,7 @@ export class Project {
1099
1161
  id;
1100
1162
  configFileName;
1101
1163
  currentDirectory;
1164
+ dirty;
1102
1165
  parsedCommandLine;
1103
1166
  /** @deprecated Use `parsedCommandLine.options`. */
1104
1167
  compilerOptions;
@@ -1106,7 +1169,6 @@ export class Project {
1106
1169
  rootFiles;
1107
1170
  program;
1108
1171
  checker;
1109
- emitter;
1110
1172
  languageService;
1111
1173
  client;
1112
1174
  snapshotId;
@@ -1114,6 +1176,7 @@ export class Project {
1114
1176
  this.id = data.id;
1115
1177
  this.configFileName = data.configFileName;
1116
1178
  this.currentDirectory = data.currentDirectory;
1179
+ this.dirty = data.dirty;
1117
1180
  if (!data.parsedCommandLine?.options) {
1118
1181
  throw new Error(`Project '${data.configFileName}' has no parsed command line`);
1119
1182
  }
@@ -1125,7 +1188,6 @@ export class Project {
1125
1188
  this.program = new Program(snapshotId, this, client, sourceFileCache, toPath, formatDiagnosticsHost);
1126
1189
  const objectRegistry = new ProjectObjectRegistry(client, snapshotId, this, snapshotRegistry);
1127
1190
  this.checker = new Checker(snapshotId, this, client, objectRegistry);
1128
- this.emitter = new Emitter(client);
1129
1191
  this.languageService = new LanguageService(snapshotId, this, client, objectRegistry);
1130
1192
  }
1131
1193
  /** @deprecated Use `languageService.getImportAdderEdits`. */
@@ -1339,6 +1401,7 @@ export class LanguageService {
1339
1401
  export class Program {
1340
1402
  /** @internal */
1341
1403
  snapshotId;
1404
+ id;
1342
1405
  project;
1343
1406
  client;
1344
1407
  sourceFileCache;
@@ -1350,6 +1413,7 @@ export class Program {
1350
1413
  disposePromise;
1351
1414
  constructor(snapshotId, project, client, sourceFileCache, toPath, formatDiagnosticsHost) {
1352
1415
  this.snapshotId = snapshotId;
1416
+ this.id = project.id;
1353
1417
  this.project = project;
1354
1418
  this.client = client;
1355
1419
  this.sourceFileCache = sourceFileCache;
@@ -1431,11 +1495,11 @@ export class Program {
1431
1495
  return retained;
1432
1496
  }
1433
1497
  // Fetch from server
1434
- const binaryData = owner.client.apiRequestBinary("getSourceFile", {
1498
+ const binaryData = sourceFileResponseToUint8Array(yield* apiRequest("getSourceFile", {
1435
1499
  snapshot: owner.snapshotId,
1436
1500
  project: owner.project.id,
1437
1501
  file,
1438
- });
1502
+ }));
1439
1503
  if (!binaryData) {
1440
1504
  return undefined;
1441
1505
  }
@@ -1707,11 +1771,11 @@ export class Program {
1707
1771
  }
1708
1772
  return new RemoteSourceFile(binaryData, owner.decoder);
1709
1773
  }, function* (file) {
1710
- const binaryData = owner.client.apiRequestBinary("getConfigSourceFile", {
1774
+ const binaryData = sourceFileResponseToUint8Array(yield* apiRequest("getConfigSourceFile", {
1711
1775
  snapshot: owner.snapshotId,
1712
1776
  project: owner.project.id,
1713
1777
  file,
1714
- });
1778
+ }));
1715
1779
  if (!binaryData) {
1716
1780
  return undefined;
1717
1781
  }
@@ -2843,13 +2907,13 @@ export class Checker {
2843
2907
  return undefined;
2844
2908
  return decodeNode(binaryData);
2845
2909
  }, function* (type, enclosingDeclaration, flags) {
2846
- const binaryData = owner.client.apiRequestBinary("typeToTypeNode", {
2910
+ const binaryData = sourceFileResponseToUint8Array(yield* apiRequest("typeToTypeNode", {
2847
2911
  snapshot: owner.snapshotId,
2848
2912
  project: owner.project.id,
2849
2913
  type: type.id,
2850
2914
  location: enclosingDeclaration ? getNodeId(enclosingDeclaration) : undefined,
2851
2915
  flags,
2852
- });
2916
+ }));
2853
2917
  if (!binaryData)
2854
2918
  return undefined;
2855
2919
  return decodeNode(binaryData);
@@ -2870,14 +2934,14 @@ export class Checker {
2870
2934
  return undefined;
2871
2935
  return decodeNode(binaryData);
2872
2936
  }, function* (signature, kind, enclosingDeclaration, flags) {
2873
- const binaryData = owner.client.apiRequestBinary("signatureToSignatureDeclaration", {
2937
+ const binaryData = sourceFileResponseToUint8Array(yield* apiRequest("signatureToSignatureDeclaration", {
2874
2938
  snapshot: owner.snapshotId,
2875
2939
  project: owner.project.id,
2876
2940
  signature: signature.id,
2877
2941
  kind,
2878
2942
  location: enclosingDeclaration ? getNodeId(enclosingDeclaration) : undefined,
2879
2943
  flags,
2880
- });
2944
+ }));
2881
2945
  if (!binaryData)
2882
2946
  return undefined;
2883
2947
  return decodeNode(binaryData);
@@ -3584,7 +3648,7 @@ export class Checker {
3584
3648
  });
3585
3649
  }
3586
3650
  }
3587
- export class Emitter {
3651
+ export class Printer {
3588
3652
  client;
3589
3653
  constructor(client) {
3590
3654
  this.client = client;
@@ -3611,6 +3675,28 @@ export class Emitter {
3611
3675
  });
3612
3676
  });
3613
3677
  }
3678
+ get printFile() {
3679
+ const owner = this;
3680
+ return cacheGeneratorMethod(owner, "printFile", function (sourceFile, options = {}) {
3681
+ const encoded = encodeNode(sourceFile);
3682
+ const base64 = uint8ArrayToBase64(encoded);
3683
+ return owner.client.apiRequest("printNode", {
3684
+ data: base64,
3685
+ preserveSourceNewlines: options.preserveSourceNewlines,
3686
+ neverAsciiEscape: options.neverAsciiEscape,
3687
+ terminateUnterminatedLiterals: options.terminateUnterminatedLiterals,
3688
+ });
3689
+ }, function* (sourceFile, options = {}) {
3690
+ const encoded = encodeNode(sourceFile);
3691
+ const base64 = uint8ArrayToBase64(encoded);
3692
+ return yield* apiRequest("printNode", {
3693
+ data: base64,
3694
+ preserveSourceNewlines: options.preserveSourceNewlines,
3695
+ neverAsciiEscape: options.neverAsciiEscape,
3696
+ terminateUnterminatedLiterals: options.terminateUnterminatedLiterals,
3697
+ });
3698
+ });
3699
+ }
3614
3700
  }
3615
3701
  export class SnapshotInternalAPI {
3616
3702
  snapshotId;