@askdkc/dsh-lsp-server 0.1.12 → 0.1.13

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 (34) hide show
  1. package/README.md +8 -0
  2. package/lib/code-intelligence-DvhxPnpu.js +890 -0
  3. package/lib/code-intelligence-DvhxPnpu.js.map +1 -0
  4. package/lib/code-intelligence-contracts-CWXTofdI.js +8 -0
  5. package/lib/code-intelligence-contracts-CWXTofdI.js.map +1 -0
  6. package/lib/code-intelligence-contracts.js +3 -0
  7. package/lib/code-intelligence.js +5 -0
  8. package/lib/errors-DtyWAUi0.js.map +1 -1
  9. package/lib/extra-tool.js +135 -7
  10. package/lib/extra-tool.js.map +1 -1
  11. package/lib/launch-ByUvgkVv.js.map +1 -1
  12. package/lib/provider.js +639 -41
  13. package/lib/provider.js.map +1 -1
  14. package/lib/structure/grammars/manifest.json +40 -0
  15. package/lib/structure/grammars/tree-sitter-javascript.wasm +0 -0
  16. package/lib/structure/grammars/tree-sitter-tsx.wasm +0 -0
  17. package/lib/structure/grammars/tree-sitter-typescript.wasm +0 -0
  18. package/lib/structure/parser-worker.mjs +200 -0
  19. package/lib/types/code-intelligence-contracts.d.ts +113 -0
  20. package/lib/types/code-intelligence.d.ts +5 -0
  21. package/lib/types/errors.d.ts +1 -1
  22. package/lib/types/extra-tool.d.ts +2 -1
  23. package/lib/types/provider.d.ts +4 -0
  24. package/lib/types/runtime/webstack-runtime.d.ts +13 -1
  25. package/lib/types/service.d.ts +16 -0
  26. package/lib/types/structure/code-intelligence.d.ts +23 -0
  27. package/lib/types/structure/coordinates.d.ts +14 -0
  28. package/lib/types/structure/engine.d.ts +49 -0
  29. package/lib/types/structure/protocol.d.ts +19 -0
  30. package/lib/types/structure/queries.d.ts +4 -0
  31. package/lib/types/structure/snapshots.d.ts +37 -0
  32. package/lib/types/structure/types.d.ts +147 -0
  33. package/lib/types/structure/worker-pool.d.ts +66 -0
  34. package/package.json +17 -4
package/lib/provider.js CHANGED
@@ -1,8 +1,12 @@
1
1
  import { WebstackLspError } from "./errors-DtyWAUi0.js";
2
2
  import { SERVER_IDS, createWebstackLspService, initializationOptions, resolveConfig, resolveLaunch, serverConfiguration } from "./launch-ByUvgkVv.js";
3
3
  import { mergeCompletions, mergeDiagnostics } from "./completion-B7NPVCEN.js";
4
+ import { DocumentSnapshotStore, createCodeIntelligenceToolServiceV1 } from "./code-intelligence-DvhxPnpu.js";
4
5
  import { LspProviderId } from "@deepseek-ai/dsh-lsp";
6
+ import { isAbsolute } from "node:path";
5
7
  import { TextDecoder } from "node:util";
8
+ import { randomUUID } from "node:crypto";
9
+ import { symbols } from "@deepseek-ai/cordis";
6
10
 
7
11
  //#region src/runtime/cancellation.ts
8
12
  function abortError() {
@@ -25,11 +29,11 @@ function onAbort(signal, callback) {
25
29
  async function withAbort(promise, signal) {
26
30
  throwIfAborted(signal);
27
31
  if (!signal) return promise;
28
- return new Promise((resolve, reject) => {
32
+ return new Promise((resolve$1, reject) => {
29
33
  const remove = onAbort(signal, () => reject(abortError()));
30
34
  promise.then((value) => {
31
35
  remove();
32
- resolve(value);
36
+ resolve$1(value);
33
37
  }, (error) => {
34
38
  remove();
35
39
  reject(error);
@@ -143,17 +147,17 @@ const CLASSIFIERS = [
143
147
  ];
144
148
  function classifyFile(filePath, bladeSuffixes = [".blade.php"]) {
145
149
  const normalizedPath = filePath.replaceAll("\\", "/");
146
- const basename = normalizedPath.slice(normalizedPath.lastIndexOf("/") + 1).toLowerCase();
147
- if (!basename || basename === "." || basename === ".." || basename.startsWith(".")) return void 0;
150
+ const basename$1 = normalizedPath.slice(normalizedPath.lastIndexOf("/") + 1).toLowerCase();
151
+ if (!basename$1 || basename$1 === "." || basename$1 === ".." || basename$1.startsWith(".")) return void 0;
148
152
  const customBlade = bladeSuffixes.map((suffix) => suffix.toLowerCase()).sort((left, right) => right.length - left.length);
149
153
  const match = [...customBlade.map((suffix) => ({
150
154
  suffix,
151
155
  kind: "blade"
152
- })), ...CLASSIFIERS.filter((item) => !customBlade.includes(item.suffix))].find((item) => basename.endsWith(item.suffix));
156
+ })), ...CLASSIFIERS.filter((item) => !customBlade.includes(item.suffix))].find((item) => basename$1.endsWith(item.suffix));
153
157
  if (!match) return void 0;
154
158
  return {
155
159
  path: normalizedPath,
156
- basename,
160
+ basename: basename$1,
157
161
  suffix: match.suffix,
158
162
  kind: match.kind
159
163
  };
@@ -401,8 +405,8 @@ var JsonRpcConnection = class {
401
405
  throwIfAborted(signal);
402
406
  if (this.closed) throw new Error("connection is closed");
403
407
  const id = this.nextId++;
404
- const promise = new Promise((resolve, reject) => this.pending.set(id, {
405
- resolve,
408
+ const promise = new Promise((resolve$1, reject) => this.pending.set(id, {
409
+ resolve: resolve$1,
406
410
  reject,
407
411
  method
408
412
  }));
@@ -451,7 +455,7 @@ var JsonRpcConnection = class {
451
455
  for (const message of messages) this.dispatch(message);
452
456
  }
453
457
  dispatch(value) {
454
- if (!isRecord$4(value) || value.jsonrpc !== "2.0") return this.close(new JsonRpcFrameError("invalid JSON-RPC message"));
458
+ if (!isRecord$5(value) || value.jsonrpc !== "2.0") return this.close(new JsonRpcFrameError("invalid JSON-RPC message"));
455
459
  if ("method" in value && typeof value.method === "string") {
456
460
  if ("id" in value) {
457
461
  const request = value;
@@ -480,12 +484,12 @@ var JsonRpcConnection = class {
480
484
  }
481
485
  }
482
486
  write(message) {
483
- return new Promise((resolve, reject) => {
484
- this.output.write(encodeJsonRpc(message), (error) => error ? reject(error) : resolve());
487
+ return new Promise((resolve$1, reject) => {
488
+ this.output.write(encodeJsonRpc(message), (error) => error ? reject(error) : resolve$1());
485
489
  });
486
490
  }
487
491
  };
488
- function isRecord$4(value) {
492
+ function isRecord$5(value) {
489
493
  return typeof value === "object" && value !== null && !Array.isArray(value);
490
494
  }
491
495
 
@@ -650,8 +654,8 @@ var ServerPool = class {
650
654
  async serialize(key, task, signal) {
651
655
  const previous = this.queues.get(key) ?? Promise.resolve();
652
656
  let release;
653
- const current = new Promise((resolve) => {
654
- release = resolve;
657
+ const current = new Promise((resolve$1) => {
658
+ release = resolve$1;
655
659
  });
656
660
  this.queues.set(key, current);
657
661
  try {
@@ -718,29 +722,29 @@ function normalizeHover(value) {
718
722
  function normalizeContents(value) {
719
723
  if (typeof value === "string") return value;
720
724
  if (Array.isArray(value)) {
721
- const parts = value.map((item) => typeof item === "string" ? item : isRecord$3(item) && typeof item.value === "string" ? item.value : void 0);
725
+ const parts = value.map((item) => typeof item === "string" ? item : isRecord$4(item) && typeof item.value === "string" ? item.value : void 0);
722
726
  return parts.every((item) => item !== void 0) ? parts.join("\n") : void 0;
723
727
  }
724
- if (isRecord$3(value) && typeof value.value === "string") return value.value;
728
+ if (isRecord$4(value) && typeof value.value === "string") return value.value;
725
729
  }
726
730
  function isRange$2(value) {
727
- return isRecord$3(value) && isPosition$2(value.start) && isPosition$2(value.end);
731
+ return isRecord$4(value) && isPosition$2(value.start) && isPosition$2(value.end);
728
732
  }
729
733
  function isPosition$2(value) {
730
- return isRecord$3(value) && Number.isInteger(value.line) && Number.isInteger(value.character) && value.line >= 0 && value.character >= 0;
734
+ return isRecord$4(value) && Number.isInteger(value.line) && Number.isInteger(value.character) && value.line >= 0 && value.character >= 0;
731
735
  }
732
- function isRecord$3(value) {
736
+ function isRecord$4(value) {
733
737
  return typeof value === "object" && value !== null && !Array.isArray(value);
734
738
  }
735
739
 
736
740
  //#endregion
737
741
  //#region src/protocol/normalize-location.ts
738
742
  function normalizeLocation(value) {
739
- if (isRecord$2(value) && typeof value.targetUri === "string" && isRange$1(value.targetSelectionRange)) return {
743
+ if (isRecord$3(value) && typeof value.targetUri === "string" && isRange$1(value.targetSelectionRange)) return {
740
744
  uri: value.targetUri,
741
745
  range: value.targetSelectionRange
742
746
  };
743
- if (!isRecord$2(value) || typeof value.uri !== "string" || !isRange$1(value.range)) return void 0;
747
+ if (!isRecord$3(value) || typeof value.uri !== "string" || !isRange$1(value.range)) return void 0;
744
748
  return {
745
749
  uri: value.uri,
746
750
  range: value.range
@@ -750,19 +754,19 @@ function normalizeLocations(value) {
750
754
  return (Array.isArray(value) ? value : value === null || value === void 0 ? [] : [value]).map(normalizeLocation).filter((location) => location !== void 0);
751
755
  }
752
756
  function isRange$1(value) {
753
- return isRecord$2(value) && isPosition$1(value.start) && isPosition$1(value.end);
757
+ return isRecord$3(value) && isPosition$1(value.start) && isPosition$1(value.end);
754
758
  }
755
759
  function isPosition$1(value) {
756
- return isRecord$2(value) && Number.isInteger(value.line) && Number.isInteger(value.character) && value.line >= 0 && value.character >= 0;
760
+ return isRecord$3(value) && Number.isInteger(value.line) && Number.isInteger(value.character) && value.line >= 0 && value.character >= 0;
757
761
  }
758
- function isRecord$2(value) {
762
+ function isRecord$3(value) {
759
763
  return typeof value === "object" && value !== null && !Array.isArray(value);
760
764
  }
761
765
 
762
766
  //#endregion
763
767
  //#region src/protocol/normalize-diagnostic.ts
764
768
  function normalizeDiagnostic(value, server, fallbackUri) {
765
- if (!isRecord$1(value) || !isRange(value.range) || typeof value.message !== "string") return void 0;
769
+ if (!isRecord$2(value) || !isRange(value.range) || typeof value.message !== "string") return void 0;
766
770
  const uri = typeof value.uri === "string" ? value.uri : fallbackUri;
767
771
  const severity = typeof value.severity === "number" && value.severity >= 1 && value.severity <= 4 ? value.severity : void 0;
768
772
  const code = typeof value.code === "string" || typeof value.code === "number" ? value.code : void 0;
@@ -780,19 +784,19 @@ function normalizeDiagnostics(value, server, fallbackUri) {
780
784
  return (Array.isArray(value) ? value : []).map((item) => normalizeDiagnostic(item, server, fallbackUri)).filter((item) => item !== void 0);
781
785
  }
782
786
  function isRange(value) {
783
- return isRecord$1(value) && isPosition(value.start) && isPosition(value.end);
787
+ return isRecord$2(value) && isPosition(value.start) && isPosition(value.end);
784
788
  }
785
789
  function isPosition(value) {
786
- return isRecord$1(value) && Number.isInteger(value.line) && Number.isInteger(value.character) && value.line >= 0 && value.character >= 0;
790
+ return isRecord$2(value) && Number.isInteger(value.line) && Number.isInteger(value.character) && value.line >= 0 && value.character >= 0;
787
791
  }
788
- function isRecord$1(value) {
792
+ function isRecord$2(value) {
789
793
  return typeof value === "object" && value !== null && !Array.isArray(value);
790
794
  }
791
795
 
792
796
  //#endregion
793
797
  //#region src/protocol/normalize-completion.ts
794
798
  function normalizeCompletionItem(value, server) {
795
- if (!isRecord(value) || typeof value.label !== "string" || value.label.length === 0) return void 0;
799
+ if (!isRecord$1(value) || typeof value.label !== "string" || value.label.length === 0) return void 0;
796
800
  return {
797
801
  server,
798
802
  label: value.label,
@@ -807,9 +811,9 @@ function normalizeCompletionItem(value, server) {
807
811
  };
808
812
  }
809
813
  function normalizeCompletion(value, server) {
810
- return (isRecord(value) && Array.isArray(value.items) ? value.items : Array.isArray(value) ? value : []).map((item) => normalizeCompletionItem(item, server)).filter((item) => item !== void 0);
814
+ return (isRecord$1(value) && Array.isArray(value.items) ? value.items : Array.isArray(value) ? value : []).map((item) => normalizeCompletionItem(item, server)).filter((item) => item !== void 0);
811
815
  }
812
- function isRecord(value) {
816
+ function isRecord$1(value) {
813
817
  return typeof value === "object" && value !== null && !Array.isArray(value);
814
818
  }
815
819
 
@@ -831,6 +835,451 @@ function sameRange(left, right) {
831
835
  return JSON.stringify(left) === JSON.stringify(right);
832
836
  }
833
837
 
838
+ //#endregion
839
+ //#region src/structure/protocol.ts
840
+ const CAPABILITIES = [
841
+ "snapshots",
842
+ "structure",
843
+ "semantic"
844
+ ];
845
+ const MAX_FILES = 100;
846
+ const MAX_INPUT_BYTES = 16 * 1024 * 1024;
847
+ const MAX_OUTPUT_BYTES = 64 * 1024;
848
+ const MAX_CALLS = 100;
849
+ const MAX_BATCH_MS = 3e4;
850
+ const RESPONSE_BYTES = 32 * 1024;
851
+ /** Host-bound V1 facade. Each lease owns its handles and cumulative evaluation budget. */
852
+ var CodeIntelligenceProtocolService = class {
853
+ version = 1;
854
+ capabilities = CAPABILITIES;
855
+ leases = /* @__PURE__ */ new Set();
856
+ closed = false;
857
+ constructor(options) {
858
+ this.options = options;
859
+ }
860
+ async bind(binding, signal) {
861
+ if (this.closed) throw new WebstackLspError("LSP_DISPOSED", "code-intelligence service is disposed");
862
+ validateBinding(binding);
863
+ signal.throwIfAborted();
864
+ await binding.assertCurrent();
865
+ const contextFs = binding.context.get("fs", false);
866
+ const contextSubprocess = binding.context.get("subprocess", false);
867
+ if (identity(contextFs) !== identity(this.options.fs) || identity(contextSubprocess) !== identity(this.options.subprocess)) throw new WebstackLspError("LSP_CODE_INTELLIGENCE_UNAVAILABLE", "host services do not match the provider scope");
868
+ const scope = {
869
+ ownerId: `${binding.owner.agentId}:code:${randomUUID()}`,
870
+ sessionId: binding.owner.sessionId,
871
+ workspaceRoot: binding.workspaceRoot
872
+ };
873
+ const lease = new CodeLease(binding, scope, this.options.backend, this.options.capabilities, identity(this.options.fs), identity(this.options.subprocess), () => this.leases.delete(lease));
874
+ this.leases.add(lease);
875
+ try {
876
+ await lease.assertCurrent(signal);
877
+ if (this.closed) throw new WebstackLspError("LSP_DISPOSED", "code-intelligence service is disposed");
878
+ return lease;
879
+ } catch (error) {
880
+ await lease.dispose();
881
+ throw error;
882
+ }
883
+ }
884
+ async dispose() {
885
+ if (this.closed) return;
886
+ this.closed = true;
887
+ await Promise.all([...this.leases].map((lease) => lease.dispose()));
888
+ }
889
+ };
890
+ var CodeLease = class {
891
+ lifetime = new AbortController();
892
+ snapshots = /* @__PURE__ */ new Map();
893
+ nodes = /* @__PURE__ */ new Map();
894
+ calls = 0;
895
+ files = 0;
896
+ inputBytes = 0;
897
+ outputBytes = 0;
898
+ started = performance.now();
899
+ disposed = false;
900
+ disposal;
901
+ constructor(binding, scope, backend, capabilities, fsIdentity, subprocessIdentity, onDispose) {
902
+ this.binding = binding;
903
+ this.scope = scope;
904
+ this.backend = backend;
905
+ this.capabilities = capabilities;
906
+ this.fsIdentity = fsIdentity;
907
+ this.subprocessIdentity = subprocessIdentity;
908
+ this.onDispose = onDispose;
909
+ }
910
+ async assertCurrent(signal) {
911
+ signal.throwIfAborted();
912
+ if (this.disposed) throw new WebstackLspError("LSP_STALE_SNAPSHOT", "code evaluation has ended");
913
+ await this.binding.assertCurrent();
914
+ if (identity(this.binding.context.get("fs", false)) !== this.fsIdentity || identity(this.binding.context.get("subprocess", false)) !== this.subprocessIdentity) throw new WebstackLspError("LSP_STALE_SNAPSHOT", "host service scope changed");
915
+ signal.throwIfAborted();
916
+ }
917
+ async request(input, caller) {
918
+ const release = input?.method === "snapshot.release";
919
+ if (!release) this.calls++;
920
+ if (this.disposed) return this.account(outcome("stale", "evaluation_closed"));
921
+ if (!validCodeRequest(input)) return this.account(outcome("unsupported", "invalid_request"));
922
+ if (caller.aborted) return this.account(outcome(caller.reason?.name === "TimeoutError" ? "timeout" : "cancelled", caller.reason?.name === "TimeoutError" ? "caller_deadline" : "caller_cancelled"));
923
+ if (release) return this.release(input.handle);
924
+ if (this.calls > MAX_CALLS || this.outputBytes >= MAX_OUTPUT_BYTES || this.files >= MAX_FILES && input.method === "snapshot.open" || this.inputBytes >= MAX_INPUT_BYTES && input.method === "snapshot.open") return this.account(outcome("limit_exceeded", "evaluation_budget"));
925
+ const remaining = MAX_BATCH_MS - (performance.now() - this.started);
926
+ if (remaining <= 0) return this.account(outcome("timeout", "batch_deadline"));
927
+ if ("handle" in input && !this.snapshots.has(input.handle) || "nodeHandle" in input && !this.nodes.has(input.nodeHandle)) return this.account(outcome("stale", "unknown_handle"));
928
+ const duration = input.method === "semantic.query" ? 1e4 : 2e3;
929
+ const timeout = AbortSignal.timeout(Math.max(1, Math.floor(Math.min(remaining, duration))));
930
+ const signal = AbortSignal.any([
931
+ caller,
932
+ this.lifetime.signal,
933
+ timeout
934
+ ]);
935
+ try {
936
+ await this.assertCurrent(signal);
937
+ let response;
938
+ if (input.method === "capabilities") response = this.capabilitiesResponse(await this.capabilities(signal));
939
+ else if (input.method === "snapshot.open") response = await this.open(input, signal);
940
+ else response = await this.query(input, signal);
941
+ await this.assertCurrent(signal);
942
+ return this.account(boundResponse(response));
943
+ } catch (error) {
944
+ if (signal.aborted) return this.account(outcome(this.lifetime.signal.aborted || caller.aborted && caller.reason?.name !== "TimeoutError" ? "cancelled" : "timeout", caller.aborted ? caller.reason?.name === "TimeoutError" ? "caller_deadline" : "caller_cancelled" : "operation_deadline"));
945
+ if (error instanceof WebstackLspError) return this.account(mapError(error));
946
+ return this.account(outcome("unavailable", "provider_operation_failed"));
947
+ }
948
+ }
949
+ async open(input, signal) {
950
+ this.files++;
951
+ const opened = await this.backend.snapshot.open(this.scope, input.path, signal);
952
+ const version = `${opened.version}:${opened.contentHash}`;
953
+ const response = {
954
+ protocol: "code-intelligence/v1",
955
+ status: "ok",
956
+ sourceKind: "disk",
957
+ freshness: "pinned",
958
+ snapshotVersion: version,
959
+ truncated: false,
960
+ omitted: 0,
961
+ inputBytes: opened.sourceBytes,
962
+ data: {
963
+ handle: opened.handle,
964
+ path: opened.filePath,
965
+ language: opened.language ?? "unsupported",
966
+ parseErrors: opened.parseErrors
967
+ }
968
+ };
969
+ if (this.inputBytes + opened.sourceBytes > MAX_INPUT_BYTES) {
970
+ await this.backend.snapshot.release(this.scope, opened.handle).catch(() => {});
971
+ return outcome("limit_exceeded", "input_budget");
972
+ }
973
+ this.inputBytes += opened.sourceBytes;
974
+ this.snapshots.set(opened.handle, {
975
+ version,
976
+ path: opened.filePath
977
+ });
978
+ return response;
979
+ }
980
+ async query(input, signal) {
981
+ const handle = "handle" in input ? input.handle : this.nodes.get(input.nodeHandle);
982
+ const snapshot = this.snapshots.get(handle);
983
+ if (!snapshot) return outcome("stale", "unknown_handle");
984
+ const metadata = {
985
+ snapshotVersion: snapshot.version,
986
+ freshness: "current"
987
+ };
988
+ if (input.method === "structure.outline") {
989
+ const result = await this.backend.structure.outline(this.scope, input.handle, {
990
+ ...input.range ? { range: input.range } : {},
991
+ ...input.limit ? { limit: input.limit } : {}
992
+ }, signal);
993
+ const items = result.declarations.map(projectNode);
994
+ this.rememberNodes(handle, items);
995
+ const status = result.truncated && result.status === "ok" ? "partial" : result.status;
996
+ return responseFor(status, result.status === "unsupported" ? "grammar_missing" : result.errors.hasError ? "parse_errors" : void 0, metadata, { items }, result.truncated, result.truncated ? 1 : 0);
997
+ }
998
+ if (input.method === "structure.enclosing") {
999
+ const result = await this.backend.structure.enclosing(this.scope, input.handle, input.position, input.kinds ? { kinds: input.kinds } : void 0, signal);
1000
+ const items = [...result.node ? [result.node] : [], ...result.ancestors].map(projectNode);
1001
+ this.rememberNodes(handle, items);
1002
+ return responseFor(result.status, result.status === "unsupported" ? "grammar_missing" : result.errors?.hasError ? "parse_errors" : void 0, metadata, { items });
1003
+ }
1004
+ if (input.method === "structure.query") {
1005
+ const result = await this.backend.structure.query(this.scope, input.handle, input.queryId, {
1006
+ ...input.range ? { range: input.range } : {},
1007
+ ...input.limit ? { limit: input.limit } : {}
1008
+ }, signal);
1009
+ const items = result.captures.map((capture) => ({
1010
+ handle: capture.nodeHandle,
1011
+ kind: capture.kind,
1012
+ range: capture.range,
1013
+ ...capture.name ? { name: capture.name } : {}
1014
+ }));
1015
+ this.rememberNodes(handle, items);
1016
+ return responseFor(result.truncated && result.status === "ok" ? "partial" : result.status, void 0, metadata, {
1017
+ items,
1018
+ queryId: result.queryId
1019
+ }, result.truncated, result.truncated ? 1 : 0);
1020
+ }
1021
+ if (input.method === "structure.span") {
1022
+ const result = await this.backend.structure.span(this.scope, input.nodeHandle, input.maxChars, signal);
1023
+ return responseFor(result.status, void 0, metadata, {
1024
+ text: result.text,
1025
+ range: result.range
1026
+ }, result.truncated, result.truncated ? 1 : 0);
1027
+ }
1028
+ return this.semantic(input, metadata, signal);
1029
+ }
1030
+ async semantic(input, metadata, signal) {
1031
+ const result = await this.backend.semantic.query(this.scope, input.handle, {
1032
+ operation: input.kind,
1033
+ ...input.position ? { position: input.position } : {},
1034
+ ...input.limit ? { maxResults: input.limit } : {}
1035
+ }, signal);
1036
+ if (result.status === "unsupported" || result.status === "unavailable" || result.status === "stale" || result.status === "timeout" || result.status === "cancelled" || result.status === "limit_exceeded") return responseFor(result.status, `semantic_${result.status}`, metadata);
1037
+ const value = result.result;
1038
+ if (input.kind === "hover") {
1039
+ if (!isRecord(value) || value.kind !== "hover") return outcome("unavailable", "semantic_result_invalid");
1040
+ const hover = value.hover;
1041
+ if (hover === null) return responseFor(result.status, void 0, metadata, { hover: null });
1042
+ if (!isRecord(hover) || typeof hover.contents !== "string") return outcome("unavailable", "semantic_result_invalid");
1043
+ const contents = trimUtf16(hover.contents, 16e3);
1044
+ const truncated = contents !== hover.contents;
1045
+ return responseFor(result.status, void 0, metadata, { hover: {
1046
+ contents,
1047
+ ...hover.range ? { range: hover.range } : {}
1048
+ } }, truncated, truncated ? hover.contents.length - contents.length : 0);
1049
+ }
1050
+ if (input.kind === "completion") {
1051
+ if (!isRecord(value) || !Array.isArray(value.items)) return outcome("unavailable", "semantic_result_invalid");
1052
+ let truncated = Boolean(result.truncated);
1053
+ const items = value.items.map((item) => ({
1054
+ label: typeof item.label === "string" ? item.label.slice(0, 1e3) : "",
1055
+ ...typeof item.detail === "string" ? { detail: item.detail.slice(0, 4e3) } : {},
1056
+ ...typeof item.documentation === "string" ? { documentation: item.documentation.slice(0, 8e3) } : {},
1057
+ ...Number.isInteger(item.kind) ? { kind: item.kind } : {},
1058
+ ...typeof item.insertText === "string" ? { insertText: item.insertText.slice(0, 8e3) } : {}
1059
+ }));
1060
+ if (items.some((item, index) => item.label !== value.items[index]?.label || item.detail !== value.items[index]?.detail || item.documentation !== value.items[index]?.documentation || item.insertText !== value.items[index]?.insertText)) truncated = true;
1061
+ const limit = input.limit ?? 200;
1062
+ const omitted = (result.omitted ?? 0) + Math.max(0, items.length - limit);
1063
+ return responseFor(truncated || omitted ? "partial" : result.status, void 0, metadata, { items: items.slice(0, limit) }, truncated || omitted > 0, omitted);
1064
+ }
1065
+ if (input.kind === "diagnostics") {
1066
+ if (!isRecord(value) || !Array.isArray(value.diagnostics)) return outcome("unavailable", "semantic_result_invalid");
1067
+ if (value.skipped) return responseFor("unsupported", "unsupported_file_type", metadata);
1068
+ let truncated = Boolean(result.truncated);
1069
+ const items = value.diagnostics.map((item) => {
1070
+ const message = typeof item.message === "string" ? item.message.slice(0, 8e3) : "";
1071
+ if (message !== item.message) truncated = true;
1072
+ return {
1073
+ uri: item.uri,
1074
+ range: item.range,
1075
+ message,
1076
+ ...Number.isInteger(item.severity) ? { severity: item.severity } : {}
1077
+ };
1078
+ });
1079
+ const limit = input.limit ?? 200;
1080
+ const omitted = (result.omitted ?? 0) + Math.max(0, items.length - limit);
1081
+ return responseFor(truncated || omitted || result.status === "partial" ? "partial" : result.status, void 0, metadata, { items: items.slice(0, limit) }, truncated || omitted > 0, omitted);
1082
+ }
1083
+ if (!isRecord(value) || value.kind !== "locations" || !Array.isArray(value.locations)) return outcome("unavailable", "semantic_result_invalid");
1084
+ return responseFor(result.status, void 0, metadata, { items: value.locations }, Boolean(result.truncated), result.omitted ?? 0);
1085
+ }
1086
+ async release(handle) {
1087
+ const known = this.snapshots.delete(handle);
1088
+ for (const [node, snapshot] of this.nodes) if (snapshot === handle) this.nodes.delete(node);
1089
+ if (!known) return responseFor("ok", "released", {
1090
+ sourceKind: "disk",
1091
+ freshness: "unknown"
1092
+ }, { released: false });
1093
+ const result = await this.backend.snapshot.release(this.scope, handle);
1094
+ return responseFor("ok", "released", {
1095
+ sourceKind: "disk",
1096
+ freshness: "unknown"
1097
+ }, result);
1098
+ }
1099
+ capabilitiesResponse(capabilities) {
1100
+ return responseFor("ok", "capabilities", {
1101
+ sourceKind: "disk",
1102
+ freshness: "unknown"
1103
+ }, capabilities);
1104
+ }
1105
+ rememberNodes(snapshotHandle, items) {
1106
+ for (const item of items) this.nodes.set(item.handle, snapshotHandle);
1107
+ }
1108
+ account(response) {
1109
+ if (Buffer.byteLength(JSON.stringify(response)) > RESPONSE_BYTES) response = outcome("limit_exceeded", "response_limit");
1110
+ this.outputBytes += Buffer.byteLength(JSON.stringify(response));
1111
+ if (this.outputBytes > MAX_OUTPUT_BYTES) return outcome("limit_exceeded", "response_budget");
1112
+ return response;
1113
+ }
1114
+ dispose() {
1115
+ if (this.disposal) return this.disposal;
1116
+ this.disposed = true;
1117
+ this.lifetime.abort();
1118
+ this.snapshots.clear();
1119
+ this.nodes.clear();
1120
+ this.onDispose();
1121
+ this.disposal = Promise.resolve(this.backend.releaseOwner?.(this.scope)).then(() => void 0, () => void 0);
1122
+ return this.disposal;
1123
+ }
1124
+ };
1125
+ function outcome(status, reason) {
1126
+ return {
1127
+ protocol: "code-intelligence/v1",
1128
+ status,
1129
+ reason,
1130
+ sourceKind: "disk",
1131
+ freshness: status === "stale" ? "stale" : "unknown",
1132
+ truncated: false,
1133
+ omitted: 0
1134
+ };
1135
+ }
1136
+ function responseFor(status, reason, metadata, data, truncated = false, omitted = 0) {
1137
+ const successful = status === "ok" || status === "partial";
1138
+ return {
1139
+ protocol: "code-intelligence/v1",
1140
+ status,
1141
+ ...reason ? { reason } : {},
1142
+ sourceKind: "disk",
1143
+ freshness: metadata.freshness,
1144
+ ...metadata.snapshotVersion ? { snapshotVersion: metadata.snapshotVersion } : {},
1145
+ truncated,
1146
+ omitted,
1147
+ ...successful && data !== void 0 ? { data } : {}
1148
+ };
1149
+ }
1150
+ function boundResponse(response) {
1151
+ if (Buffer.byteLength(JSON.stringify(response), "utf8") <= RESPONSE_BYTES) return response;
1152
+ if (isRecord(response.data) && Array.isArray(response.data.items)) {
1153
+ const items = [...response.data.items];
1154
+ let omitted = response.omitted;
1155
+ while (items.length && Buffer.byteLength(JSON.stringify({
1156
+ ...response,
1157
+ data: {
1158
+ ...response.data,
1159
+ items
1160
+ }
1161
+ }), "utf8") > RESPONSE_BYTES) {
1162
+ items.pop();
1163
+ omitted++;
1164
+ }
1165
+ const shortened = {
1166
+ ...response,
1167
+ status: "partial",
1168
+ reason: response.reason ?? "response_truncated",
1169
+ truncated: true,
1170
+ omitted,
1171
+ data: {
1172
+ ...response.data,
1173
+ items
1174
+ }
1175
+ };
1176
+ if (Buffer.byteLength(JSON.stringify(shortened), "utf8") <= RESPONSE_BYTES) return shortened;
1177
+ }
1178
+ return outcome("limit_exceeded", "response_limit");
1179
+ }
1180
+ function mapError(error) {
1181
+ if (error.code === "LSP_STALE_SNAPSHOT") return outcome("stale", "source_changed");
1182
+ if (error.code === "LSP_STRUCTURE_TIMEOUT") return outcome("timeout", "parser_deadline");
1183
+ if (error.code === "LSP_STRUCTURE_LIMIT" || error.code === "LSP_SNAPSHOT_LIMIT") return outcome("limit_exceeded", "provider_limit");
1184
+ if (error.code === "LSP_STRUCTURE_UNSUPPORTED" || error.code === "LSP_UNSUPPORTED_OPERATION") return outcome("unsupported", "capability_missing");
1185
+ if (error.code === "LSP_STRUCTURE_BUSY") return outcome("unavailable", "parser_busy");
1186
+ if (error.code === "LSP_CODE_INTELLIGENCE_UNAVAILABLE" || error.code === "LSP_STRUCTURE_UNAVAILABLE" || error.code === "LSP_UNAVAILABLE") return outcome("unavailable", "provider_unavailable");
1187
+ if (error.code === "LSP_WORKSPACE_REQUIRED") return outcome("unavailable", "invalid_path");
1188
+ return outcome("unavailable", "provider_operation_failed");
1189
+ }
1190
+ function projectNode(node) {
1191
+ return {
1192
+ handle: node.handle,
1193
+ kind: node.kind,
1194
+ range: node.range,
1195
+ ...node.name ? { name: node.name } : {}
1196
+ };
1197
+ }
1198
+ function trimUtf16(value, limit) {
1199
+ if (value.length <= limit) return value;
1200
+ const end = value.charCodeAt(limit - 1) >= 55296 && value.charCodeAt(limit - 1) <= 56319 ? limit - 1 : limit;
1201
+ return value.slice(0, end);
1202
+ }
1203
+ function validCodeRequest(value) {
1204
+ if (!isRecord(value) || typeof value.method !== "string") return false;
1205
+ const allowed = {
1206
+ capabilities: ["method"],
1207
+ "snapshot.open": ["method", "path"],
1208
+ "snapshot.release": ["method", "handle"],
1209
+ "structure.outline": [
1210
+ "method",
1211
+ "handle",
1212
+ "range",
1213
+ "limit"
1214
+ ],
1215
+ "structure.enclosing": [
1216
+ "method",
1217
+ "handle",
1218
+ "position",
1219
+ "kinds"
1220
+ ],
1221
+ "structure.query": [
1222
+ "method",
1223
+ "handle",
1224
+ "queryId",
1225
+ "range",
1226
+ "limit"
1227
+ ],
1228
+ "structure.span": [
1229
+ "method",
1230
+ "nodeHandle",
1231
+ "maxChars"
1232
+ ],
1233
+ "semantic.query": [
1234
+ "method",
1235
+ "handle",
1236
+ "kind",
1237
+ "position",
1238
+ "limit"
1239
+ ]
1240
+ }[value.method];
1241
+ if (!allowed || Object.keys(value).some((key) => !allowed.includes(key))) return false;
1242
+ const handle = "handle" in value ? value.handle : "nodeHandle" in value ? value.nodeHandle : void 0;
1243
+ if (handle !== void 0 && (typeof handle !== "string" || !handle.length || handle.length > 256)) return false;
1244
+ if (value.method === "snapshot.open") return typeof value.path === "string" && value.path.length > 0 && value.path.length <= 4096;
1245
+ if (value.method === "capabilities" || value.method === "snapshot.release") return true;
1246
+ if ("limit" in value && value.limit !== void 0 && (!Number.isSafeInteger(value.limit) || value.limit < 1 || value.limit > 200)) return false;
1247
+ if ("range" in value && value.range !== void 0 && !validRange(value.range)) return false;
1248
+ if (value.method === "structure.outline") return value.limit === void 0 || value.limit <= 100;
1249
+ if (value.method === "structure.enclosing") return validPosition(value.position) && (value.kinds === void 0 || Array.isArray(value.kinds) && value.kinds.length <= 30 && value.kinds.every((kind) => typeof kind === "string" && kind.length <= 100));
1250
+ if (value.method === "structure.query") return [
1251
+ "declarations",
1252
+ "calls",
1253
+ "imports"
1254
+ ].includes(String(value.queryId));
1255
+ if (value.method === "structure.span") return value.maxChars === void 0 || Number.isSafeInteger(value.maxChars) && value.maxChars >= 1 && value.maxChars <= 8e3;
1256
+ if (value.method === "semantic.query") return [
1257
+ "definition",
1258
+ "references",
1259
+ "implementation",
1260
+ "hover",
1261
+ "diagnostics",
1262
+ "completion"
1263
+ ].includes(String(value.kind)) && (value.kind === "diagnostics" || validPosition(value.position));
1264
+ return false;
1265
+ }
1266
+ function validRange(value) {
1267
+ if (!isRecord(value) || !validPosition(value.start) || !validPosition(value.end)) return false;
1268
+ return value.end.line > value.start.line || value.end.line === value.start.line && value.end.character >= value.start.character;
1269
+ }
1270
+ function validPosition(value) {
1271
+ return isRecord(value) && Number.isSafeInteger(value.line) && Number.isSafeInteger(value.character) && value.line >= 0 && value.character >= 0;
1272
+ }
1273
+ function isRecord(value) {
1274
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1275
+ }
1276
+ function identity(value) {
1277
+ return value && typeof value === "object" ? value[symbols.original] ?? value : value;
1278
+ }
1279
+ function validateBinding(binding) {
1280
+ if (!binding || !binding.owner || typeof binding.owner.agentId !== "string" || typeof binding.owner.sessionId !== "string" || typeof binding.workspaceRoot !== "string" || !isAbsolute(binding.workspaceRoot) || !binding.scope || typeof binding.assertCurrent !== "function" || !binding.context || typeof binding.context.get !== "function") throw new WebstackLspError("LSP_CODE_INTELLIGENCE_UNAVAILABLE", "invalid trusted code host binding");
1281
+ }
1282
+
834
1283
  //#endregion
835
1284
  //#region src/runtime/webstack-runtime.ts
836
1285
  const methods = {
@@ -862,6 +1311,10 @@ const clientCapabilities = {
862
1311
  };
863
1312
  var WebstackLspRuntime = class {
864
1313
  pool;
1314
+ snapshots;
1315
+ codeIntelligence;
1316
+ codeIntelligenceRuntime;
1317
+ semanticOperations = /* @__PURE__ */ new Map();
865
1318
  workspaces = /* @__PURE__ */ new Map();
866
1319
  lifetime = new AbortController();
867
1320
  live = /* @__PURE__ */ new Map();
@@ -871,6 +1324,17 @@ var WebstackLspRuntime = class {
871
1324
  this.config = config;
872
1325
  this.fs = fs;
873
1326
  this.subprocess = subprocess;
1327
+ this.snapshots = new DocumentSnapshotStore((workspaceRoot, filePath, maxBytes, signal) => readSource(this.fs, workspaceRoot, filePath, maxBytes, signal), config.limits.maxDocumentBytes);
1328
+ this.codeIntelligenceRuntime = createCodeIntelligenceToolServiceV1({
1329
+ snapshots: this.snapshots,
1330
+ semanticQuery: (scope, snapshot, request, signal) => this.semanticFromSnapshot(scope, snapshot, request, signal)
1331
+ });
1332
+ this.codeIntelligence = new CodeIntelligenceProtocolService({
1333
+ backend: this.codeIntelligenceRuntime,
1334
+ fs: this.fs,
1335
+ subprocess: this.subprocess,
1336
+ capabilities: (signal) => this.codeCapabilities(signal)
1337
+ });
874
1338
  this.pool = new ServerPool(async (rawId, key, signal) => {
875
1339
  const id = rawId, source = this.workspaces.get(key);
876
1340
  const spec = await resolveLaunch(id, config, source.workspace);
@@ -921,6 +1385,9 @@ var WebstackLspRuntime = class {
921
1385
  async navigation(request, signal) {
922
1386
  const deadline = this.signal(request.operation === "hover" ? this.config.timeouts.primaryHoverMs : this.config.timeouts.navigationMs, signal);
923
1387
  const [source, route] = await this.prepare(request, deadline);
1388
+ return this.navigationFromSource(request, source, route, deadline, signal);
1389
+ }
1390
+ async navigationFromSource(request, source, route, deadline, signal) {
924
1391
  this.validatePosition(source.text, request.position);
925
1392
  const result = await this.document(route.primary.server, source, route.primary.languageId, deadline, async (session, connection) => {
926
1393
  if (!supportsOperation(session.capabilities, request.operation)) throw new WebstackLspError("LSP_UNSUPPORTED_OPERATION", `${route.primary.server} does not support ${request.operation}`);
@@ -969,6 +1436,9 @@ var WebstackLspRuntime = class {
969
1436
  async completion(request, signal) {
970
1437
  const deadline = this.signal(this.config.timeouts.completionMs, signal);
971
1438
  const [source, route] = await this.prepare(request, deadline);
1439
+ return this.completionFromSource(request, source, route, deadline, signal);
1440
+ }
1441
+ async completionFromSource(request, source, route, deadline, signal) {
972
1442
  this.validatePosition(source.text, request.position);
973
1443
  const query = (id, language) => this.document(id, source, language, deadline, (session, connection) => {
974
1444
  if (!session.capabilities.completionProvider) throw new WebstackLspError("LSP_UNSUPPORTED_OPERATION", `${id} does not support completion`);
@@ -1000,7 +1470,7 @@ var WebstackLspRuntime = class {
1000
1470
  async diagnostics(request, signal) {
1001
1471
  const deadline = this.signal(this.config.timeouts.diagnosticsMs, signal);
1002
1472
  if (!classifyFile(request.filePath, this.config.routing.bladeSuffixes)) {
1003
- await readSource(this.fs, request.workspaceRoot, request.filePath, this.config.limits.maxDocumentBytes, deadline);
1473
+ await this.snapshots.readFresh(providerScope(request.workspaceRoot), request.filePath, deadline);
1004
1474
  return {
1005
1475
  diagnostics: [],
1006
1476
  skipped: {
@@ -1010,6 +1480,9 @@ var WebstackLspRuntime = class {
1010
1480
  };
1011
1481
  }
1012
1482
  const [source, route] = await this.prepare(request, deadline);
1483
+ return this.diagnosticsFromSource(request, source, route, deadline, signal);
1484
+ }
1485
+ async diagnosticsFromSource(request, source, route, deadline, signal) {
1013
1486
  const selected = [route.primary, ...route.auxiliary && this.config.tailwind.enabled ? [route.auxiliary] : []].filter((item) => this.config.servers[item.server].enabled && (!request.servers || request.servers.includes(item.server)));
1014
1487
  if (!selected.length) throw new WebstackLspError("LSP_UNAVAILABLE", "no selected diagnostic server supports this file");
1015
1488
  const omittedServers = [];
@@ -1021,10 +1494,10 @@ var WebstackLspRuntime = class {
1021
1494
  return (await connection.request("textDocument/diagnostic", { textDocument: { uri: source.uri } }, deadline))?.items ?? [];
1022
1495
  }
1023
1496
  let remove = () => {};
1024
- const published = new Promise((resolve) => {
1497
+ const published = new Promise((resolve$1) => {
1025
1498
  remove = connection.onNotification((message) => {
1026
1499
  const p = message.params;
1027
- if (message.method === "textDocument/publishDiagnostics" && p?.uri === source.uri && (p.version === void 0 || p.version === 1)) resolve(p.diagnostics);
1500
+ if (message.method === "textDocument/publishDiagnostics" && p?.uri === source.uri && (p.version === void 0 || p.version === 1)) resolve$1(p.diagnostics);
1028
1501
  });
1029
1502
  });
1030
1503
  try {
@@ -1051,7 +1524,62 @@ var WebstackLspRuntime = class {
1051
1524
  ...omittedServers.length ? { omittedServers: omittedServers.sort() } : {}
1052
1525
  };
1053
1526
  }
1054
- async status() {
1527
+ async semanticFromSnapshot(scope, snapshot, request, signal) {
1528
+ const source = snapshot.source;
1529
+ this.rememberWorkspace(source);
1530
+ const file = classifyFile(snapshot.descriptor.filePath, this.config.routing.bladeSuffixes);
1531
+ if (!file) {
1532
+ if (request.operation === "diagnostics") return {
1533
+ diagnostics: [],
1534
+ skipped: {
1535
+ reason: "unsupported_file_type",
1536
+ filePath: snapshot.descriptor.filePath
1537
+ }
1538
+ };
1539
+ throw new WebstackLspError("LSP_UNAVAILABLE", "unsupported file extension");
1540
+ }
1541
+ const route = routeFor(file);
1542
+ let result;
1543
+ if (request.operation === "diagnostics") {
1544
+ const deadline = this.signal(this.config.timeouts.diagnosticsMs, signal);
1545
+ result = await this.diagnosticsFromSource({
1546
+ workspaceRoot: scope.workspaceRoot,
1547
+ filePath: snapshot.descriptor.filePath,
1548
+ ...request.severity ? { severity: request.severity } : {},
1549
+ ...request.servers ? { servers: request.servers } : {}
1550
+ }, source, route, deadline, signal);
1551
+ } else if (request.operation === "completion") {
1552
+ const deadline = this.signal(this.config.timeouts.completionMs, signal);
1553
+ result = await this.completionFromSource({
1554
+ workspaceRoot: scope.workspaceRoot,
1555
+ filePath: snapshot.descriptor.filePath,
1556
+ position: request.position
1557
+ }, source, route, deadline, signal);
1558
+ } else {
1559
+ const operation = {
1560
+ definition: "goToDefinition",
1561
+ references: "findReferences",
1562
+ implementation: "goToImplementation",
1563
+ hover: "hover"
1564
+ }[request.operation];
1565
+ const position = request.position;
1566
+ const deadline = this.signal(request.operation === "hover" ? this.config.timeouts.primaryHoverMs : this.config.timeouts.navigationMs, signal);
1567
+ result = await this.navigationFromSource({
1568
+ workspaceRoot: scope.workspaceRoot,
1569
+ filePath: snapshot.descriptor.filePath,
1570
+ languageId: route.primary.languageId,
1571
+ operation,
1572
+ position
1573
+ }, source, route, deadline, signal);
1574
+ }
1575
+ if (!("skipped" in result && result.skipped)) {
1576
+ const operations = this.semanticOperations.get(route.primary.languageId) ?? /* @__PURE__ */ new Set();
1577
+ operations.add(request.operation);
1578
+ this.semanticOperations.set(route.primary.languageId, operations);
1579
+ }
1580
+ return result;
1581
+ }
1582
+ async status(signal) {
1055
1583
  return {
1056
1584
  providerId: this.config.providerId,
1057
1585
  supportedFileSuffixes: supportedFileSuffixes(this.config.routing.bladeSuffixes),
@@ -1060,7 +1588,7 @@ var WebstackLspRuntime = class {
1060
1588
  let available = false;
1061
1589
  try {
1062
1590
  const launch = await resolveLaunch(id, this.config, ".");
1063
- await this.subprocess.resolveExecutable(launch.command, launch.env, this.signal(5e3));
1591
+ await this.subprocess.resolveExecutable(launch.command, launch.env, this.signal(5e3, signal));
1064
1592
  available = true;
1065
1593
  } catch {}
1066
1594
  return {
@@ -1074,11 +1602,69 @@ var WebstackLspRuntime = class {
1074
1602
  }))
1075
1603
  };
1076
1604
  }
1605
+ async codeCapabilities(signal) {
1606
+ signal.throwIfAborted();
1607
+ const [provider, lsp] = await Promise.all([Promise.resolve(this.codeIntelligenceRuntime.capabilities()), this.status(signal)]);
1608
+ const server = lsp.servers.find((item) => item.id === "typescript");
1609
+ const languageCapabilities = provider.parsers.map((parser) => {
1610
+ const semanticIds = parser.language === "javascript" ? ["javascript", "javascriptreact"] : parser.language === "tsx" ? ["typescriptreact"] : ["typescript"];
1611
+ const semantic = [...new Set(semanticIds.flatMap((id) => [...this.semanticOperations.get(id) ?? []]))];
1612
+ const semanticReady = semantic.length > 0 && Boolean(server?.enabled && server.available);
1613
+ const reason = !parser.available ? "grammar_unavailable" : !server?.enabled || !server.available ? "server_unavailable" : !semanticReady ? "not_yet_verified" : void 0;
1614
+ return {
1615
+ language: parser.language,
1616
+ structure: parser.available,
1617
+ semantic,
1618
+ semanticReady,
1619
+ ...reason ? { reason } : {}
1620
+ };
1621
+ });
1622
+ signal.throwIfAborted();
1623
+ return {
1624
+ version: 1,
1625
+ capabilities: [
1626
+ "snapshots",
1627
+ "structure",
1628
+ "semantic"
1629
+ ],
1630
+ languages: provider.parsers.filter((parser) => parser.available).map((parser) => parser.language),
1631
+ queryIds: [
1632
+ "declarations",
1633
+ "calls",
1634
+ "imports"
1635
+ ],
1636
+ semanticReady: languageCapabilities.some((language) => language.semanticReady),
1637
+ sourceKinds: ["disk"],
1638
+ languageCapabilities,
1639
+ limits: {
1640
+ documentBytes: provider.limits.maxDocumentBytes,
1641
+ captureItems: provider.limits.maxCaptures,
1642
+ outlineItems: provider.limits.maxOutline,
1643
+ spanChars: provider.limits.maxSpanChars,
1644
+ responseBytes: provider.limits.maxResponseBytes,
1645
+ files: 100,
1646
+ inputBytes: 16777216,
1647
+ outputBytes: 65536,
1648
+ calls: 100,
1649
+ batchMs: 3e4,
1650
+ structureMs: 2e3,
1651
+ semanticMs: 1e4
1652
+ }
1653
+ };
1654
+ }
1077
1655
  async dispose() {
1078
1656
  this.lifetime.abort();
1079
- await this.pool.disposeAll();
1080
- this.workspaces.clear();
1081
- this.live.clear();
1657
+ try {
1658
+ await this.codeIntelligence.dispose?.();
1659
+ } finally {
1660
+ try {
1661
+ await this.pool.disposeAll();
1662
+ } finally {
1663
+ await this.codeIntelligenceRuntime.dispose?.();
1664
+ this.workspaces.clear();
1665
+ this.live.clear();
1666
+ }
1667
+ }
1082
1668
  }
1083
1669
  signal(ms, signal) {
1084
1670
  return AbortSignal.any([
@@ -1091,15 +1677,18 @@ var WebstackLspRuntime = class {
1091
1677
  throwIfAborted(signal);
1092
1678
  const file = classifyFile(request.filePath, this.config.routing.bladeSuffixes);
1093
1679
  if (!file) throw new WebstackLspError("LSP_UNAVAILABLE", "unsupported file extension");
1094
- const source = await readSource(this.fs, request.workspaceRoot, request.filePath, this.config.limits.maxDocumentBytes, signal).catch((error) => {
1680
+ const source = await this.snapshots.readFresh(providerScope(request.workspaceRoot), request.filePath, signal).catch((error) => {
1095
1681
  throwIfAborted(signal);
1096
1682
  throw error;
1097
1683
  });
1684
+ this.rememberWorkspace(source);
1685
+ return [source, routeFor(file)];
1686
+ }
1687
+ rememberWorkspace(source) {
1098
1688
  this.workspaces.set(source.key, {
1099
1689
  ...source,
1100
1690
  text: ""
1101
1691
  });
1102
- return [source, routeFor(file)];
1103
1692
  }
1104
1693
  validatePosition(text, position) {
1105
1694
  const line = text.split(/\r?\n/)[position.line];
@@ -1132,6 +1721,13 @@ var WebstackLspRuntime = class {
1132
1721
  } : void 0);
1133
1722
  }
1134
1723
  };
1724
+ function providerScope(workspaceRoot) {
1725
+ return {
1726
+ ownerId: "lsp-provider",
1727
+ sessionId: "lsp-provider",
1728
+ workspaceRoot
1729
+ };
1730
+ }
1135
1731
 
1136
1732
  //#endregion
1137
1733
  //#region src/provider.ts
@@ -1175,6 +1771,8 @@ async function apply(ctx, config = {}) {
1175
1771
  }
1176
1772
  yield ctx.lsp.registerProvider(createCompositeProvider(runtime, resolved));
1177
1773
  yield ctx.provide("webstackLsp", createWebstackLspService(runtime));
1774
+ yield ctx.provide("codeIntelligence", runtime.codeIntelligence);
1775
+ yield ctx.provide("codeIntelligenceRuntime", runtime.codeIntelligenceRuntime);
1178
1776
  }, "webstack LSP provider");
1179
1777
  }
1180
1778