typescript 7.1.0-dev.20260928.1 → 7.1.0-dev.20260930.4

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 (77) hide show
  1. package/dist/api/async/api.d.ts +58 -35
  2. package/dist/api/async/api.d.ts.map +1 -1
  3. package/dist/api/async/api.js +219 -117
  4. package/dist/api/async/api.js.map +1 -1
  5. package/dist/api/async/client.d.ts.map +1 -1
  6. package/dist/api/async/client.js +21 -35
  7. package/dist/api/async/client.js.map +1 -1
  8. package/dist/api/fs.d.ts +39 -13
  9. package/dist/api/fs.d.ts.map +1 -1
  10. package/dist/api/fs.js +13 -108
  11. package/dist/api/fs.js.map +1 -1
  12. package/dist/api/fsCallbacks.d.ts +27 -0
  13. package/dist/api/fsCallbacks.d.ts.map +1 -0
  14. package/dist/api/fsCallbacks.js +111 -0
  15. package/dist/api/fsCallbacks.js.map +1 -0
  16. package/dist/api/node/node.d.ts +8 -2
  17. package/dist/api/node/node.d.ts.map +1 -1
  18. package/dist/api/node/node.infrastructure.d.ts +1 -0
  19. package/dist/api/node/node.infrastructure.d.ts.map +1 -1
  20. package/dist/api/node/node.infrastructure.js +8 -1
  21. package/dist/api/node/node.infrastructure.js.map +1 -1
  22. package/dist/api/node/node.js +14 -2
  23. package/dist/api/node/node.js.map +1 -1
  24. package/dist/api/options.d.ts +5 -3
  25. package/dist/api/options.d.ts.map +1 -1
  26. package/dist/api/options.js +10 -0
  27. package/dist/api/options.js.map +1 -1
  28. package/dist/api/path.d.ts +1 -0
  29. package/dist/api/path.d.ts.map +1 -1
  30. package/dist/api/path.js +3 -0
  31. package/dist/api/path.js.map +1 -1
  32. package/dist/api/proto.d.ts +26 -3
  33. package/dist/api/proto.d.ts.map +1 -1
  34. package/dist/api/proto.generated.d.ts +72 -34
  35. package/dist/api/proto.generated.d.ts.map +1 -1
  36. package/dist/api/proto.generated.js +2 -0
  37. package/dist/api/proto.generated.js.map +1 -1
  38. package/dist/api/proto.js +20 -0
  39. package/dist/api/proto.js.map +1 -1
  40. package/dist/api/sourceFileCache.d.ts +29 -13
  41. package/dist/api/sourceFileCache.d.ts.map +1 -1
  42. package/dist/api/sourceFileCache.js +97 -16
  43. package/dist/api/sourceFileCache.js.map +1 -1
  44. package/dist/api/sync/api.d.ts +64 -44
  45. package/dist/api/sync/api.d.ts.map +1 -1
  46. package/dist/api/sync/api.js +339 -191
  47. package/dist/api/sync/api.js.map +1 -1
  48. package/dist/api/sync/client.d.ts.map +1 -1
  49. package/dist/api/sync/client.js +11 -26
  50. package/dist/api/sync/client.js.map +1 -1
  51. package/dist/ast/ast.generated.d.ts +3 -3
  52. package/dist/ast/ast.generated.d.ts.map +1 -1
  53. package/dist/ast/jsdoc.d.ts +2 -1
  54. package/dist/ast/jsdoc.d.ts.map +1 -1
  55. package/dist/ast/jsdoc.js +14 -8
  56. package/dist/ast/jsdoc.js.map +1 -1
  57. package/dist/enums/objectFlags.enum.d.ts +1 -2
  58. package/dist/enums/objectFlags.enum.d.ts.map +1 -1
  59. package/dist/enums/objectFlags.enum.js +1 -2
  60. package/dist/enums/objectFlags.enum.js.map +1 -1
  61. package/dist/enums/objectFlags.js +1 -2
  62. package/dist/enums/objectFlags.js.map +1 -1
  63. package/dist/enums/scriptTarget.enum.d.ts +1 -0
  64. package/dist/enums/scriptTarget.enum.d.ts.map +1 -1
  65. package/dist/enums/scriptTarget.enum.js +1 -0
  66. package/dist/enums/scriptTarget.enum.js.map +1 -1
  67. package/dist/enums/scriptTarget.js +1 -0
  68. package/dist/enums/scriptTarget.js.map +1 -1
  69. package/dist/enums/symbolOwnerKind.d.ts +2 -0
  70. package/dist/enums/symbolOwnerKind.d.ts.map +1 -0
  71. package/dist/enums/symbolOwnerKind.enum.d.ts +5 -0
  72. package/dist/enums/symbolOwnerKind.enum.d.ts.map +1 -0
  73. package/dist/enums/symbolOwnerKind.enum.js +7 -0
  74. package/dist/enums/symbolOwnerKind.enum.js.map +1 -0
  75. package/dist/enums/symbolOwnerKind.js +7 -0
  76. package/dist/enums/symbolOwnerKind.js.map +1 -0
  77. package/package.json +9 -9
@@ -14,21 +14,22 @@ import { ScriptKind } from "#enums/scriptKind";
14
14
  import { SignatureFlags } from "#enums/signatureFlags";
15
15
  import { SignatureKind } from "#enums/signatureKind";
16
16
  import { SymbolFlags } from "#enums/symbolFlags";
17
+ import { SymbolOwnerKind } from "#enums/symbolOwnerKind";
17
18
  import { TypeFlags } from "#enums/typeFlags";
18
19
  import { TypeFormatFlags } from "#enums/typeFormatFlags";
19
20
  import { TypePredicateKind } from "#enums/typePredicateKind";
20
21
  import { ModifierFlags, unescapeLeadingUnderscores, } from "../../ast/index.js";
21
22
  import { assertNever } from "../../internal/utils.js";
22
23
  import { encodeNode, uint8ArrayToBase64, } from "../node/encoder.js";
23
- import { decodeNode, getNodeId, parseNodeHandle, readParseOptionsKey, readSourceFileHash, readSourceFileLease, RemoteSourceFile, } from "../node/node.js";
24
+ import { decodeNode, getNodeId, parseNodeHandle, readSourceFileLease, RemoteSourceFile, } from "../node/node.js";
24
25
  import { Wtf8Decoder } from "../node/wtf8.js";
25
26
  import { createGetCanonicalFileName, toPath, } from "../path.js";
26
- import { resolveFileName, toCreateSnapshotRequest, } from "../proto.js";
27
- import { SourceFileCache } from "../sourceFileCache.js";
27
+ import { resolveFileName, toCreateSnapshotRequest, validateSymbolResponse, } from "../proto.js";
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, ScriptKind, SignatureFlags, SignatureKind, SymbolFlags, TypeFlags, TypeFormatFlags, TypePredicateKind };
32
+ export { CheckFlags, CompletionItemKind, DiagnosticCategory, ElementFlags, EmitOnly, IndexKind, JsxEmit, ModifierFlags, ModuleKind, ModuleResolutionKind, NodeBuilderFlags, ObjectFlags, ScriptKind, SignatureFlags, SignatureKind, SymbolFlags, SymbolOwnerKind, TypeFlags, TypeFormatFlags, TypePredicateKind };
32
33
  let nextModuleResolutionCallbackId = 0;
33
34
  function registerModuleResolutionCallback(client, callback, getSnapshot) {
34
35
  const name = `resolveModuleName/${++nextModuleResolutionCallbackId}`;
@@ -177,18 +178,27 @@ export class API {
177
178
  }
178
179
  return this.retainSourceFileResponse(data);
179
180
  }
181
+ /**
182
+ * Retain an ordinary remote source file independently of the snapshot or lease that produced it.
183
+ */
184
+ async retainSourceFile(sourceFile) {
185
+ await this.ensureInitialized();
186
+ if (!(sourceFile instanceof RemoteSourceFile)) {
187
+ throw new TypeError("Only remote source files can be retained");
188
+ }
189
+ const cached = this.sourceFileCache.get(sourceFile);
190
+ if (cached && cached !== sourceFile) {
191
+ throw new Error("Source file is no longer the canonical cached instance");
192
+ }
193
+ const result = await this.client.apiRequest("retainSourceFile", { file: sourceFileDescriptor(sourceFile) });
194
+ return this.addSourceFileLease(sourceFile, result.lease);
195
+ }
180
196
  retainSourceFileResponse(data) {
181
197
  const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
182
198
  const lease = readSourceFileLease(view);
183
199
  try {
184
200
  const decoded = new RemoteSourceFile(data, this.decoder, this.client.getTimingCollector());
185
- const sourceFile = this.sourceFileCache.setForLease(decoded.path, decoded, readParseOptionsKey(view), readSourceFileHash(view), lease);
186
- const retained = new RetainedSourceFile(sourceFile, lease, this.client, () => {
187
- this.activeSourceFileLeases.delete(lease);
188
- this.sourceFileCache.releaseLease(lease);
189
- });
190
- this.activeSourceFileLeases.set(lease, retained);
191
- return retained;
201
+ return this.addSourceFileLease(decoded, lease);
192
202
  }
193
203
  catch (error) {
194
204
  // @sync-skip-block-start
@@ -203,6 +213,15 @@ export class API {
203
213
  throw error;
204
214
  }
205
215
  }
216
+ addSourceFileLease(sourceFile, lease) {
217
+ const cached = this.sourceFileCache.setForLease(sourceFile, lease);
218
+ const retained = new RetainedSourceFile(cached, lease, this.client, () => {
219
+ this.activeSourceFileLeases.delete(lease);
220
+ this.sourceFileCache.releaseLease(lease);
221
+ });
222
+ this.activeSourceFileLeases.set(lease, retained);
223
+ return retained;
224
+ }
206
225
  async transpileModule(input, options = {}) {
207
226
  await this.ensureInitialized();
208
227
  return this.client.apiRequest("transpileModule", { input, options });
@@ -403,6 +422,16 @@ export class API {
403
422
  return program;
404
423
  }
405
424
  }
425
+ function sourceFileDescriptor(sourceFile) {
426
+ return {
427
+ fileName: sourceFile.fileName,
428
+ path: sourceFile.path,
429
+ contentHash: sourceFile.contentHash,
430
+ parseOptionsKey: sourceFile.parseOptionsKey,
431
+ scriptKind: sourceFile.scriptKind,
432
+ nodeId: sourceFile.nodeId,
433
+ };
434
+ }
406
435
  /** An independently retained source file and its disposable remote-lifetime lease. */
407
436
  export class RetainedSourceFile {
408
437
  sourceFile;
@@ -486,7 +515,7 @@ export class Snapshot {
486
515
  projectDataMap.set(projectData.id, projectData);
487
516
  }
488
517
  this.projectDataMap = new Map([...projectDataMap].sort(([a], [b]) => a < b ? -1 : a > b ? 1 : 0));
489
- this.snapshotRegistry = new SnapshotObjectRegistry(client, this.id, projectId => this.projectMap.get(projectId));
518
+ this.snapshotRegistry = new SnapshotObjectRegistry(this.id);
490
519
  for (const projData of this.projectDataMap.values()) {
491
520
  const project = new Project(projData, this.id, client, sourceFileCache, toPath, formatDiagnosticsHost, this.snapshotRegistry);
492
521
  this.projectMap.set(projData.id, project);
@@ -612,23 +641,28 @@ export class ModuleResolver {
612
641
  }
613
642
  class SnapshotObjectRegistry {
614
643
  symbols = new Map();
615
- client;
644
+ projectRegistries = new Map();
616
645
  snapshotId;
617
- resolveProject;
618
- constructor(client, snapshotId, resolveProject) {
619
- this.client = client;
646
+ constructor(snapshotId) {
620
647
  this.snapshotId = snapshotId;
621
- this.resolveProject = resolveProject;
622
648
  }
623
- /** Resolve a project ID to its Project within this snapshot. */
624
- getProject(projectId) {
625
- return this.resolveProject(projectId);
649
+ addProjectRegistry(registry) {
650
+ this.projectRegistries.set(registry.project.id, registry);
626
651
  }
627
652
  getOrCreateSymbol(data) {
628
- let symbol = this.symbols.get(data.id);
653
+ const reference = data.reference;
654
+ if (reference.kind !== SymbolOwnerKind.Snapshot)
655
+ throw new Error(`Symbol ${reference.id} is not snapshot-owned`);
656
+ let symbol = this.symbols.get(reference.id);
629
657
  if (!symbol) {
630
- symbol = new Symbol(data, this);
631
- this.symbols.set(data.id, symbol);
658
+ if (reference.snapshot !== this.snapshotId) {
659
+ throw new Error(`Symbol ${reference.id} belongs to snapshot ${reference.snapshot}, not ${this.snapshotId}`);
660
+ }
661
+ const registry = this.projectRegistries.get(reference.project);
662
+ if (!registry)
663
+ throw new Error(`Symbol ${reference.id} references unknown project '${reference.project}'`);
664
+ symbol = new Symbol(data, { kind: SymbolOwnerKind.Snapshot, registry });
665
+ this.symbols.set(reference.id, symbol);
632
666
  }
633
667
  return symbol;
634
668
  }
@@ -638,65 +672,49 @@ class SnapshotObjectRegistry {
638
672
  clear() {
639
673
  this.symbols.clear();
640
674
  }
641
- async fetchSymbol(source, method, handle, projectId) {
642
- if (!handle)
643
- return undefined;
644
- const cached = this.getSymbol(handle);
645
- if (cached)
646
- return cached;
647
- const data = await this.client.apiRequest(method, {
648
- snapshot: this.snapshotId,
649
- project: projectId,
650
- objectId: source.id,
651
- });
652
- if (!data)
653
- throw new Error(`${method} returned null symbol for ${source.constructor.name} ${source.id}`);
654
- return this.getOrCreateSymbol(data);
655
- }
656
- async fetchSymbols(source, method, handles, projectId) {
657
- if (handles) {
658
- const result = new Array(handles.length);
659
- let allCached = true;
660
- for (let i = 0; i < handles.length; i++) {
661
- const cached = this.getSymbol(handles[i]);
662
- if (!cached) {
663
- allCached = false;
664
- break;
665
- }
666
- result[i] = cached;
667
- }
668
- if (allCached)
669
- return result;
670
- }
671
- const symbolData = await this.client.apiRequest(method, {
672
- snapshot: this.snapshotId,
673
- project: projectId,
674
- objectId: source.id,
675
- });
676
- if (symbolData == null)
677
- return [];
678
- else
679
- return symbolData.map(data => this.getOrCreateSymbol(data));
680
- }
681
675
  }
682
676
  class ProjectObjectRegistry {
683
677
  client;
684
678
  snapshotId;
685
679
  project;
686
680
  snapshotRegistry;
681
+ sourceFileCache;
687
682
  types = new Map();
688
683
  signatures = new Map();
689
- constructor(client, snapshotId, project, snapshotRegistry) {
684
+ disposed = false;
685
+ constructor(client, snapshotId, project, snapshotRegistry, sourceFileCache) {
690
686
  this.client = client;
691
687
  this.snapshotId = snapshotId;
692
688
  this.project = project;
693
689
  this.snapshotRegistry = snapshotRegistry;
690
+ this.sourceFileCache = sourceFileCache;
691
+ snapshotRegistry.addProjectRegistry(this);
694
692
  }
695
693
  getOrCreateSymbol(data) {
696
- return this.snapshotRegistry.getOrCreateSymbol(data);
694
+ this.ensureNotDisposed();
695
+ validateSymbolResponse(data);
696
+ const reference = data.reference;
697
+ if (reference.kind === SymbolOwnerKind.Snapshot) {
698
+ return this.snapshotRegistry.getOrCreateSymbol(data);
699
+ }
700
+ const record = this.sourceFileCache.getOrCreateRecord(reference.file, this.snapshotId, this.project.id);
701
+ return this.sourceFileCache.getOrCreateSymbol(record, reference.file, reference.id, () => new Symbol(data, {
702
+ kind: SymbolOwnerKind.File,
703
+ owner: { record, cache: this.sourceFileCache, client: this.client },
704
+ }));
697
705
  }
698
- getSymbol(id) {
699
- return this.snapshotRegistry.getSymbol(id);
706
+ /** Find an already-interned symbol and retain its file record for this registry. */
707
+ getCachedSymbol(reference) {
708
+ this.ensureNotDisposed();
709
+ if (reference.file === undefined) {
710
+ return this.snapshotRegistry.getSymbol(reference.id);
711
+ }
712
+ const record = this.sourceFileCache.findRecord(reference.file);
713
+ const symbol = record?.symbols.get(reference.id);
714
+ if (record && symbol) {
715
+ this.sourceFileCache.retainRecord(record, this.snapshotId, this.project.id);
716
+ }
717
+ return symbol;
700
718
  }
701
719
  getOrCreateType(data) {
702
720
  let type = this.types.get(data.id);
@@ -724,9 +742,14 @@ class ProjectObjectRegistry {
724
742
  return this.signatures.get(id);
725
743
  }
726
744
  clear() {
745
+ this.disposed = true;
727
746
  this.types.clear();
728
747
  this.signatures.clear();
729
748
  }
749
+ ensureNotDisposed() {
750
+ if (this.disposed)
751
+ throw new Error("Project object registry is disposed");
752
+ }
730
753
  async fetchOptionalType(source, method, handle) {
731
754
  if (handle !== false) {
732
755
  if (!handle)
@@ -750,8 +773,22 @@ class ProjectObjectRegistry {
750
773
  throw new Error(`${method} returned no type for ${source.constructor.name} ${source.id}`);
751
774
  return result;
752
775
  }
753
- async fetchSymbol(source, method, handle) {
754
- return this.snapshotRegistry.fetchSymbol(source, method, handle, this.project.id);
776
+ async fetchSymbol(source, method, reference) {
777
+ if (!reference)
778
+ return undefined;
779
+ const cached = this.getCachedSymbol(reference);
780
+ if (cached)
781
+ return cached;
782
+ const data = source instanceof Symbol
783
+ ? await this.client.apiRequest(method, { symbol: source.reference })
784
+ : await this.client.apiRequest(method, {
785
+ snapshot: this.snapshotId,
786
+ project: this.project.id,
787
+ objectId: source.id,
788
+ });
789
+ if (!data)
790
+ throw new Error(`${method} returned null symbol for ${source.constructor.name} ${source.id}`);
791
+ return this.getOrCreateSymbol(data);
755
792
  }
756
793
  async fetchSignature(source, method, handle) {
757
794
  if (!handle)
@@ -793,8 +830,29 @@ class ProjectObjectRegistry {
793
830
  else
794
831
  return typesData.map(data => this.getOrCreateType(data));
795
832
  }
796
- async fetchSymbols(source, method, handles) {
797
- return this.snapshotRegistry.fetchSymbols(source, method, handles, this.project.id);
833
+ async fetchSymbols(source, method, references) {
834
+ if (references) {
835
+ const result = new Array(references.length);
836
+ for (let i = 0; i < references.length; i++) {
837
+ const cached = this.getCachedSymbol(references[i]);
838
+ if (!cached) {
839
+ return this.fetchSymbolsFromServer(source, method);
840
+ }
841
+ result[i] = cached;
842
+ }
843
+ return result;
844
+ }
845
+ return this.fetchSymbolsFromServer(source, method);
846
+ }
847
+ async fetchSymbolsFromServer(source, method) {
848
+ const data = source instanceof Symbol
849
+ ? await this.client.apiRequest(method, { symbol: source.reference })
850
+ : await this.client.apiRequest(method, {
851
+ snapshot: this.snapshotId,
852
+ project: this.project.id,
853
+ objectId: source.id,
854
+ });
855
+ return data?.map(symbol => this.getOrCreateSymbol(symbol)) ?? [];
798
856
  }
799
857
  // getBaseTypes is a checker-level endpoint keyed by `type` (not `objectId`),
800
858
  // so it cannot go through fetchTypes. This helper reuses that server method.
@@ -896,7 +954,7 @@ export class Project {
896
954
  this.client = client;
897
955
  this.snapshotId = snapshotId;
898
956
  this.program = new Program(snapshotId, this, client, sourceFileCache, toPath, formatDiagnosticsHost);
899
- const objectRegistry = new ProjectObjectRegistry(client, snapshotId, this, snapshotRegistry);
957
+ const objectRegistry = new ProjectObjectRegistry(client, snapshotId, this, snapshotRegistry, sourceFileCache);
900
958
  this.checker = new Checker(snapshotId, this, client, objectRegistry);
901
959
  this.languageService = new LanguageService(snapshotId, this, client, objectRegistry);
902
960
  }
@@ -929,7 +987,7 @@ export class LanguageService {
929
987
  case "importSymbol":
930
988
  const importSymbolAction = {
931
989
  kind: "importSymbol",
932
- symbol: action.symbol.id,
990
+ symbol: action.symbol.reference,
933
991
  };
934
992
  if (action.isValidTypeOnlyUseSite !== undefined) {
935
993
  importSymbolAction.isValidTypeOnlyUseSite = action.isValidTypeOnlyUseSite;
@@ -1073,12 +1131,9 @@ export class Program {
1073
1131
  if (!binaryData) {
1074
1132
  return undefined;
1075
1133
  }
1076
- const view = new DataView(binaryData.buffer, binaryData.byteOffset, binaryData.byteLength);
1077
- const contentHash = readSourceFileHash(view);
1078
- const parseOptionsKey = readParseOptionsKey(view);
1079
1134
  // Create a new RemoteSourceFile and cache it (set returns existing if hash matches)
1080
- const sourceFile = new RemoteSourceFile(binaryData, this.decoder, this.client.getTimingCollector());
1081
- return this.sourceFileCache.set(path, sourceFile, parseOptionsKey, contentHash, this.snapshotId, this.project.id);
1135
+ const decoded = new RemoteSourceFile(binaryData, this.decoder, this.client.getTimingCollector());
1136
+ return this.sourceFileCache.set(decoded, this.snapshotId, this.project.id);
1082
1137
  }
1083
1138
  async getResolvedModule(file, moduleName, mode) {
1084
1139
  const result = await this.client.apiRequest("getResolvedModule", {
@@ -1535,14 +1590,14 @@ export class Checker {
1535
1590
  const data = await this.client.apiRequest("getTypesOfSymbols", {
1536
1591
  snapshot: this.snapshotId,
1537
1592
  project: this.project.id,
1538
- symbols: symbolOrSymbols.map(s => s.id),
1593
+ symbols: symbolOrSymbols.map(symbol => symbol.reference),
1539
1594
  });
1540
1595
  return data.map(d => this.objectRegistry.getOrCreateType(d));
1541
1596
  }
1542
1597
  const data = await this.client.apiRequest("getTypeOfSymbol", {
1543
1598
  snapshot: this.snapshotId,
1544
1599
  project: this.project.id,
1545
- symbol: symbolOrSymbols.id,
1600
+ symbol: symbolOrSymbols.reference,
1546
1601
  });
1547
1602
  return this.objectRegistry.getOrCreateType(data);
1548
1603
  }
@@ -1555,7 +1610,7 @@ export class Checker {
1555
1610
  const data = await this.client.apiRequest("getDeclaredTypeOfSymbol", {
1556
1611
  snapshot: this.snapshotId,
1557
1612
  project: this.project.id,
1558
- symbol: symbol.id,
1613
+ symbol: symbol.reference,
1559
1614
  });
1560
1615
  return this.objectRegistry.getOrCreateType(data);
1561
1616
  }
@@ -1569,7 +1624,7 @@ export class Checker {
1569
1624
  const data = await this.client.apiRequest("getNonMissingTypeOfSymbol", {
1570
1625
  snapshot: this.snapshotId,
1571
1626
  project: this.project.id,
1572
- symbol: symbol.id,
1627
+ symbol: symbol.reference,
1573
1628
  });
1574
1629
  return this.objectRegistry.getOrCreateType(data);
1575
1630
  }
@@ -1578,7 +1633,7 @@ export class Checker {
1578
1633
  snapshot: this.snapshotId,
1579
1634
  project: this.project.id,
1580
1635
  file,
1581
- symbol: symbol.id,
1636
+ symbol: symbol.reference,
1582
1637
  });
1583
1638
  return (data ?? []).map(h => new NodeHandle(h, this.project));
1584
1639
  }
@@ -1786,7 +1841,7 @@ export class Checker {
1786
1841
  const data = await this.client.apiRequest("getTypeOfSymbolAtLocation", {
1787
1842
  snapshot: this.snapshotId,
1788
1843
  project: this.project.id,
1789
- symbol: symbol.id,
1844
+ symbol: symbol.reference,
1790
1845
  location: getNodeId(location),
1791
1846
  });
1792
1847
  return this.objectRegistry.getOrCreateType(data);
@@ -1904,7 +1959,7 @@ export class Checker {
1904
1959
  return this.client.apiRequest("isReadonlySymbol", {
1905
1960
  snapshot: this.snapshotId,
1906
1961
  project: this.project.id,
1907
- symbol: symbol.id,
1962
+ symbol: symbol.reference,
1908
1963
  });
1909
1964
  }
1910
1965
  /** Get the return type of a signature. Always returns a type. */
@@ -2058,7 +2113,7 @@ export class Checker {
2058
2113
  const data = await this.client.apiRequest("getAliasedSymbol", {
2059
2114
  snapshot: this.snapshotId,
2060
2115
  project: this.project.id,
2061
- symbol: symbol.id,
2116
+ symbol: symbol.reference,
2062
2117
  });
2063
2118
  return this.objectRegistry.getOrCreateSymbol(data);
2064
2119
  }
@@ -2070,14 +2125,14 @@ export class Checker {
2070
2125
  return this.client.apiRequest("getFullyQualifiedName", {
2071
2126
  snapshot: this.snapshotId,
2072
2127
  project: this.project.id,
2073
- symbol: symbol.id,
2128
+ symbol: symbol.reference,
2074
2129
  });
2075
2130
  }
2076
2131
  async getImmediateAliasedSymbol(symbol) {
2077
2132
  const data = await this.client.apiRequest("getImmediateAliasedSymbol", {
2078
2133
  snapshot: this.snapshotId,
2079
2134
  project: this.project.id,
2080
- symbol: symbol.id,
2135
+ symbol: symbol.reference,
2081
2136
  });
2082
2137
  return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined;
2083
2138
  }
@@ -2089,7 +2144,7 @@ export class Checker {
2089
2144
  const data = await this.client.apiRequest("getTargetSymbol", {
2090
2145
  snapshot: this.snapshotId,
2091
2146
  project: this.project.id,
2092
- symbol: symbol.id,
2147
+ symbol: symbol.reference,
2093
2148
  });
2094
2149
  return this.objectRegistry.getOrCreateSymbol(data);
2095
2150
  }
@@ -2099,7 +2154,7 @@ export class Checker {
2099
2154
  const data = await this.client.apiRequest("getExportSymbolOfSymbolForChecker", {
2100
2155
  snapshot: this.snapshotId,
2101
2156
  project: this.project.id,
2102
- symbol: symbol.id,
2157
+ symbol: symbol.reference,
2103
2158
  });
2104
2159
  return this.objectRegistry.getOrCreateSymbol(data);
2105
2160
  }
@@ -2157,7 +2212,7 @@ export class Checker {
2157
2212
  const data = await this.client.apiRequest("getExportsOfModule", {
2158
2213
  snapshot: this.snapshotId,
2159
2214
  project: this.project.id,
2160
- symbol: symbol.id,
2215
+ symbol: symbol.reference,
2161
2216
  });
2162
2217
  return data ? data.map(d => this.objectRegistry.getOrCreateSymbol(d)) : [];
2163
2218
  }
@@ -2165,7 +2220,7 @@ export class Checker {
2165
2220
  const data = await this.client.apiRequest("getMemberInModuleExports", {
2166
2221
  snapshot: this.snapshotId,
2167
2222
  project: this.project.id,
2168
- symbol: symbol.id,
2223
+ symbol: symbol.reference,
2169
2224
  name,
2170
2225
  });
2171
2226
  return data ? this.objectRegistry.getOrCreateSymbol(data) : undefined;
@@ -2174,7 +2229,7 @@ export class Checker {
2174
2229
  const data = await this.client.apiRequest("getJsDocTags", {
2175
2230
  snapshot: this.snapshotId,
2176
2231
  project: this.project.id,
2177
- symbol: symbol.id,
2232
+ symbol: symbol.reference,
2178
2233
  });
2179
2234
  return data ?? [];
2180
2235
  }
@@ -2182,7 +2237,7 @@ export class Checker {
2182
2237
  return this.client.apiRequest("getDocumentationComment", {
2183
2238
  snapshot: this.snapshotId,
2184
2239
  project: this.project.id,
2185
- symbol: symbol.id,
2240
+ symbol: symbol.reference,
2186
2241
  });
2187
2242
  }
2188
2243
  /**
@@ -2265,71 +2320,89 @@ export class NodeHandle {
2265
2320
  * is remembered so callers don't have to pass it explicitly.
2266
2321
  */
2267
2322
  canonicalProject;
2323
+ /** The owning source file of a file-owned symbol's declaration. */
2324
+ fileOwner;
2268
2325
  index;
2269
2326
  kind;
2270
2327
  path;
2271
- constructor(handle, canonicalProject) {
2328
+ constructor(handle, canonicalProject, fileOwner) {
2272
2329
  const parsed = parseNodeHandle(handle);
2273
2330
  this.index = parsed.index;
2274
2331
  this.kind = parsed.kind;
2275
2332
  this.path = parsed.path;
2276
2333
  this.canonicalProject = canonicalProject;
2334
+ this.fileOwner = fileOwner;
2277
2335
  }
2278
2336
  /**
2279
2337
  * Resolve this handle to the actual AST node by fetching the source file from a project
2280
2338
  * and looking up the node by index. If no project is passed, the project that produced
2281
- * the handle is used.
2339
+ * the handle is used. Declarations of file-owned symbols identify an exact source file and
2340
+ * resolve through it, independently of any project.
2282
2341
  */
2283
2342
  async resolve(project = this.canonicalProject) {
2343
+ if (this.fileOwner) {
2344
+ const sourceFile = this.fileOwner.record.file ?? await this.fetchOwnerFile(this.fileOwner);
2345
+ return sourceFile.getOrCreateNodeAtIndex(this.index);
2346
+ }
2347
+ if (!project)
2348
+ throw new Error(`Node handle for '${this.path}' has no project context`);
2284
2349
  const sourceFile = await project.program.getSourceFile(this.path);
2285
2350
  if (!sourceFile) {
2286
2351
  return undefined;
2287
2352
  }
2288
2353
  return sourceFile.getOrCreateNodeAtIndex(this.index);
2289
2354
  }
2355
+ async fetchOwnerFile(fileOwner) {
2356
+ const data = await fileOwner.client.apiRequestBinary("getCachedSourceFile", { file: fileOwner.record.descriptor });
2357
+ if (!data)
2358
+ throw new Error(`Source file '${fileOwner.record.descriptor.fileName}' is not available`);
2359
+ return fileOwner.cache.attachFile(fileOwner.record, new RemoteSourceFile(data, new Wtf8Decoder(), fileOwner.client.getTimingCollector()));
2360
+ }
2290
2361
  }
2291
2362
  export class Symbol {
2292
- objectRegistry;
2293
- /**
2294
- * The project this symbol was first observed in, used as the default project for
2295
- * lookups that need a project context (members/exports/parent). Symbols are shared
2296
- * snapshot-wide, so these lookups can otherwise be ambiguous about which project to use.
2297
- */
2298
- canonicalProject;
2299
- id;
2363
+ storage;
2364
+ get id() {
2365
+ return this.reference.id;
2366
+ }
2300
2367
  /** The escaped (`__String`) name, used as the key in member/export tables. */
2301
2368
  escapedName;
2302
2369
  /** The display name (escaped underscores removed). */
2303
2370
  name;
2304
2371
  flags;
2305
2372
  checkFlags;
2373
+ /** @internal */
2374
+ reference;
2306
2375
  declarations;
2307
2376
  valueDeclaration;
2308
2377
  parent;
2309
2378
  exportSymbol;
2310
2379
  membersCache;
2311
2380
  exportsCache;
2312
- constructor(data, objectRegistry) {
2313
- this.objectRegistry = objectRegistry;
2314
- this.id = data.id;
2381
+ constructor(data, storage) {
2382
+ if (data.reference.kind !== storage.kind)
2383
+ throw new Error(`Symbol ${data.reference.id} has mismatched ownership and storage`);
2384
+ this.storage = storage;
2385
+ this.reference = data.reference;
2315
2386
  this.escapedName = data.name;
2316
2387
  this.name = unescapeLeadingUnderscores(data.name);
2317
2388
  this.flags = data.flags;
2318
2389
  this.checkFlags = data.checkFlags;
2319
- const canonicalProject = objectRegistry.getProject(data.project);
2320
- if (!canonicalProject) {
2321
- throw new Error(`Symbol ${data.id} references unknown canonical project '${data.project}'`);
2322
- }
2323
- this.canonicalProject = canonicalProject;
2324
- this.declarations = (data.declarations ?? []).map(d => new NodeHandle(d, canonicalProject));
2325
- this.valueDeclaration = data.valueDeclaration ? new NodeHandle(data.valueDeclaration, canonicalProject) : undefined;
2390
+ // A file-owned symbol has no canonical project; its declarations resolve through its file.
2391
+ const project = storage.kind === SymbolOwnerKind.Snapshot ? storage.registry.project : undefined;
2392
+ const fileOwner = storage.kind === SymbolOwnerKind.File ? storage.owner : undefined;
2393
+ this.declarations = (data.declarations ?? []).map(handle => new NodeHandle(handle, project, fileOwner));
2394
+ this.valueDeclaration = data.valueDeclaration
2395
+ ? new NodeHandle(data.valueDeclaration, project, fileOwner)
2396
+ : undefined;
2326
2397
  if (data.parent !== undefined)
2327
2398
  this.parent = data.parent;
2328
2399
  if (data.exportSymbol !== undefined)
2329
2400
  this.exportSymbol = data.exportSymbol;
2330
2401
  }
2331
2402
  async getParent() {
2332
- return this.objectRegistry.fetchSymbol(this, "getParentOfSymbol", this.parent, this.canonicalProject.id);
2403
+ if (!this.parent)
2404
+ return undefined;
2405
+ return this.fetchSymbol("getParentOfSymbol", this.parent);
2333
2406
  }
2334
2407
  /**
2335
2408
  * Get this symbol's members keyed by escaped name. The result is cached on
@@ -2346,7 +2419,7 @@ export class Symbol {
2346
2419
  return this.exportsCache ??= this.fetchSymbolTable("getExportsOfSymbol");
2347
2420
  }
2348
2421
  async fetchSymbolTable(method) {
2349
- const symbols = await this.objectRegistry.fetchSymbols(this, method, undefined, this.canonicalProject.id);
2422
+ const symbols = await this.fetchSymbols(method);
2350
2423
  const table = new Map();
2351
2424
  for (const symbol of symbols) {
2352
2425
  table.set(symbol.escapedName, symbol);
@@ -2356,7 +2429,7 @@ export class Symbol {
2356
2429
  async getExportSymbol() {
2357
2430
  if (!this.exportSymbol)
2358
2431
  return this;
2359
- return this.objectRegistry.fetchSymbol(this, "getExportSymbolOfSymbol", this.exportSymbol, this.canonicalProject.id);
2432
+ return this.fetchSymbol("getExportSymbolOfSymbol", this.exportSymbol);
2360
2433
  }
2361
2434
  async getJsDocTags(checker) {
2362
2435
  return checker.getJsDocTagsOfSymbol(this);
@@ -2364,6 +2437,35 @@ export class Symbol {
2364
2437
  async getDocumentationComment(checker) {
2365
2438
  return checker.getDocumentationCommentOfSymbol(this);
2366
2439
  }
2440
+ async fetchSymbol(method, reference) {
2441
+ if (this.storage.kind === SymbolOwnerKind.Snapshot) {
2442
+ return this.storage.registry.fetchSymbol(this, method, reference);
2443
+ }
2444
+ const fileOwner = this.storage.owner;
2445
+ // A file-owned symbol's relationships are always owned by the same file.
2446
+ const cached = reference.file === fileOwner.record.descriptor.nodeId ? fileOwner.record.symbols.get(reference.id) : undefined;
2447
+ if (cached)
2448
+ return cached;
2449
+ const data = await fileOwner.client.apiRequest(method, { symbol: this.reference });
2450
+ if (!data)
2451
+ throw new Error(`${method} returned null symbol for Symbol ${this.id}`);
2452
+ return this.internFileSymbol(fileOwner, data);
2453
+ }
2454
+ async fetchSymbols(method) {
2455
+ if (this.storage.kind === SymbolOwnerKind.Snapshot) {
2456
+ return this.storage.registry.fetchSymbols(this, method);
2457
+ }
2458
+ const fileOwner = this.storage.owner;
2459
+ const data = await fileOwner.client.apiRequest(method, { symbol: this.reference });
2460
+ return data?.map(symbol => this.internFileSymbol(fileOwner, symbol)) ?? [];
2461
+ }
2462
+ internFileSymbol(fileOwner, data) {
2463
+ validateSymbolResponse(data);
2464
+ const reference = data.reference;
2465
+ if (reference.kind !== SymbolOwnerKind.File)
2466
+ throw new Error(`Symbol ${reference.id} is not file-owned`);
2467
+ return fileOwner.cache.getOrCreateSymbol(fileOwner.record, reference.file, reference.id, () => new Symbol(data, { kind: SymbolOwnerKind.File, owner: fileOwner }));
2468
+ }
2367
2469
  }
2368
2470
  class TypeObject {
2369
2471
  objectRegistry;