@formspec/ts-plugin 0.1.0-alpha.22 → 0.1.0-alpha.23

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.
package/dist/index.cjs CHANGED
@@ -45,15 +45,16 @@ var import_promises = __toESM(require("fs/promises"), 1);
45
45
  var import_node_net = __toESM(require("net"), 1);
46
46
  var ts2 = require("typescript");
47
47
  var import_protocol3 = require("@formspec/analysis/protocol");
48
- var import_internal2 = require("@formspec/analysis/internal");
48
+ var import_internal3 = require("@formspec/analysis/internal");
49
49
 
50
50
  // src/workspace.ts
51
51
  var import_node_os = __toESM(require("os"), 1);
52
52
  var import_node_path = __toESM(require("path"), 1);
53
53
  var import_protocol = require("@formspec/analysis/protocol");
54
+ var import_internal = require("@formspec/analysis/internal");
54
55
  function getFormSpecWorkspaceRuntimePaths(workspaceRoot, platform = process.platform, userScope = getFormSpecUserScope()) {
55
- const workspaceId = (0, import_protocol.getFormSpecWorkspaceId)(workspaceRoot);
56
- const runtimeDirectory = (0, import_protocol.getFormSpecWorkspaceRuntimeDirectory)(workspaceRoot);
56
+ const workspaceId = (0, import_internal.getFormSpecWorkspaceId)(workspaceRoot);
57
+ const runtimeDirectory = (0, import_internal.getFormSpecWorkspaceRuntimeDirectory)(workspaceRoot);
57
58
  const sanitizedUserScope = sanitizeScopeSegment(userScope);
58
59
  const endpoint = platform === "win32" ? {
59
60
  kind: "windows-pipe",
@@ -66,7 +67,7 @@ function getFormSpecWorkspaceRuntimePaths(workspaceRoot, platform = process.plat
66
67
  workspaceRoot,
67
68
  workspaceId,
68
69
  runtimeDirectory,
69
- manifestPath: (0, import_protocol.getFormSpecManifestPath)(workspaceRoot),
70
+ manifestPath: (0, import_internal.getFormSpecManifestPath)(workspaceRoot),
70
71
  endpoint
71
72
  };
72
73
  }
@@ -102,7 +103,7 @@ function sanitizeScopeSegment(value) {
102
103
  // src/semantic-service.ts
103
104
  var ts = require("typescript");
104
105
  var import_protocol2 = require("@formspec/analysis/protocol");
105
- var import_internal = require("@formspec/analysis/internal");
106
+ var import_internal2 = require("@formspec/analysis/internal");
106
107
 
107
108
  // src/constants.ts
108
109
  var FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES = 256 * 1024;
@@ -180,8 +181,8 @@ var FormSpecSemanticService = class {
180
181
  (performance2) => this.withCommentQueryContext(filePath, offset, performance2, (context) => ({
181
182
  protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
182
183
  sourceHash: context.sourceHash,
183
- context: (0, import_internal.serializeCompletionContext)(
184
- (0, import_internal.getSemanticCommentCompletionContextAtOffset)(context.sourceFile.text, offset, {
184
+ context: (0, import_internal2.serializeCompletionContext)(
185
+ (0, import_internal2.getSemanticCommentCompletionContextAtOffset)(context.sourceFile.text, offset, {
185
186
  checker: context.checker,
186
187
  ...context.placement === null ? {} : { placement: context.placement },
187
188
  ...context.subjectType === void 0 ? {} : { subjectType: context.subjectType }
@@ -199,8 +200,8 @@ var FormSpecSemanticService = class {
199
200
  (performance2) => this.withCommentQueryContext(filePath, offset, performance2, (context) => ({
200
201
  protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
201
202
  sourceHash: context.sourceHash,
202
- hover: (0, import_internal.serializeHoverInfo)(
203
- (0, import_internal.getCommentHoverInfoAtOffset)(context.sourceFile.text, offset, {
203
+ hover: (0, import_internal2.serializeHoverInfo)(
204
+ (0, import_internal2.getCommentHoverInfoAtOffset)(context.sourceFile.text, offset, {
204
205
  checker: context.checker,
205
206
  ...context.placement === null ? {} : { placement: context.placement },
206
207
  ...context.subjectType === void 0 ? {} : { subjectType: context.subjectType }
@@ -275,8 +276,8 @@ var FormSpecSemanticService = class {
275
276
  };
276
277
  }
277
278
  runMeasured(name, detail, fn) {
278
- const performance2 = this.options.enablePerformanceLogging === true ? (0, import_internal.createFormSpecPerformanceRecorder)() : new StatsOnlyPerformanceRecorder();
279
- const result = (0, import_internal.optionalMeasure)(performance2, name, detail, () => fn(performance2));
279
+ const performance2 = this.options.enablePerformanceLogging === true ? (0, import_internal2.createFormSpecPerformanceRecorder)() : new StatsOnlyPerformanceRecorder();
280
+ const result = (0, import_internal2.optionalMeasure)(performance2, name, detail, () => fn(performance2));
280
281
  this.updateStatsFromPerformanceEvents(performance2.events);
281
282
  if (this.options.enablePerformanceLogging === true) {
282
283
  this.logPerformanceEvents(name, performance2.events);
@@ -284,7 +285,7 @@ var FormSpecSemanticService = class {
284
285
  return result;
285
286
  }
286
287
  withCommentQueryContext(filePath, offset, performance2, handler) {
287
- return (0, import_internal.optionalMeasure)(
288
+ return (0, import_internal2.optionalMeasure)(
288
289
  performance2,
289
290
  "semantic.resolveCommentQueryContext",
290
291
  {
@@ -296,26 +297,26 @@ var FormSpecSemanticService = class {
296
297
  if (environment === null) {
297
298
  return null;
298
299
  }
299
- const declaration = (0, import_internal.optionalMeasure)(
300
+ const declaration = (0, import_internal2.optionalMeasure)(
300
301
  performance2,
301
302
  "semantic.findDeclarationForCommentOffset",
302
303
  {
303
304
  filePath,
304
305
  offset
305
306
  },
306
- () => (0, import_internal.findDeclarationForCommentOffset)(environment.sourceFile, offset)
307
+ () => (0, import_internal2.findDeclarationForCommentOffset)(environment.sourceFile, offset)
307
308
  );
308
- const placement = declaration === null ? null : (0, import_internal.optionalMeasure)(
309
+ const placement = declaration === null ? null : (0, import_internal2.optionalMeasure)(
309
310
  performance2,
310
311
  "semantic.resolveDeclarationPlacement",
311
312
  void 0,
312
- () => (0, import_internal.resolveDeclarationPlacement)(declaration)
313
+ () => (0, import_internal2.resolveDeclarationPlacement)(declaration)
313
314
  );
314
- const subjectType = declaration === null ? void 0 : (0, import_internal.optionalMeasure)(
315
+ const subjectType = declaration === null ? void 0 : (0, import_internal2.optionalMeasure)(
315
316
  performance2,
316
317
  "semantic.getSubjectType",
317
318
  void 0,
318
- () => (0, import_internal.getSubjectType)(declaration, environment.checker)
319
+ () => (0, import_internal2.getSubjectType)(declaration, environment.checker)
319
320
  );
320
321
  return handler({
321
322
  ...environment,
@@ -327,7 +328,7 @@ var FormSpecSemanticService = class {
327
328
  );
328
329
  }
329
330
  getFileSnapshotWithCacheState(filePath, performance2) {
330
- const startedAt = (0, import_internal.getFormSpecPerformanceNow)();
331
+ const startedAt = (0, import_internal2.getFormSpecPerformanceNow)();
331
332
  const environment = this.getSourceEnvironment(filePath, performance2);
332
333
  if (environment === null) {
333
334
  this.stats.fileSnapshotCacheMisses += 1;
@@ -352,7 +353,7 @@ var FormSpecSemanticService = class {
352
353
  };
353
354
  performance2.record({
354
355
  name: "semantic.getFileSnapshot.result",
355
- durationMs: (0, import_internal.getFormSpecPerformanceNow)() - startedAt,
356
+ durationMs: (0, import_internal2.getFormSpecPerformanceNow)() - startedAt,
356
357
  detail: {
357
358
  filePath,
358
359
  cache: "missing-source"
@@ -368,7 +369,7 @@ var FormSpecSemanticService = class {
368
369
  this.stats.fileSnapshotCacheHits += 1;
369
370
  performance2.record({
370
371
  name: "semantic.getFileSnapshot.result",
371
- durationMs: (0, import_internal.getFormSpecPerformanceNow)() - startedAt,
372
+ durationMs: (0, import_internal2.getFormSpecPerformanceNow)() - startedAt,
372
373
  detail: {
373
374
  filePath,
374
375
  cache: "hit"
@@ -380,7 +381,7 @@ var FormSpecSemanticService = class {
380
381
  };
381
382
  }
382
383
  this.stats.fileSnapshotCacheMisses += 1;
383
- const snapshot = (0, import_internal.buildFormSpecAnalysisFileSnapshot)(environment.sourceFile, {
384
+ const snapshot = (0, import_internal2.buildFormSpecAnalysisFileSnapshot)(environment.sourceFile, {
384
385
  checker: environment.checker,
385
386
  now: () => this.getNow(),
386
387
  performance: performance2
@@ -391,7 +392,7 @@ var FormSpecSemanticService = class {
391
392
  });
392
393
  performance2.record({
393
394
  name: "semantic.getFileSnapshot.result",
394
- durationMs: (0, import_internal.getFormSpecPerformanceNow)() - startedAt,
395
+ durationMs: (0, import_internal2.getFormSpecPerformanceNow)() - startedAt,
395
396
  detail: {
396
397
  filePath,
397
398
  cache: "miss"
@@ -406,14 +407,14 @@ var FormSpecSemanticService = class {
406
407
  return this.options.now?.() ?? /* @__PURE__ */ new Date();
407
408
  }
408
409
  getSourceEnvironment(filePath, performance2) {
409
- return (0, import_internal.optionalMeasure)(
410
+ return (0, import_internal2.optionalMeasure)(
410
411
  performance2,
411
412
  "semantic.getSourceEnvironment",
412
413
  {
413
414
  filePath
414
415
  },
415
416
  () => {
416
- const program = (0, import_internal.optionalMeasure)(
417
+ const program = (0, import_internal2.optionalMeasure)(
417
418
  performance2,
418
419
  "semantic.sourceEnvironment.getProgram",
419
420
  void 0,
@@ -422,7 +423,7 @@ var FormSpecSemanticService = class {
422
423
  if (program === void 0) {
423
424
  return null;
424
425
  }
425
- const sourceFile = (0, import_internal.optionalMeasure)(
426
+ const sourceFile = (0, import_internal2.optionalMeasure)(
426
427
  performance2,
427
428
  "semantic.sourceEnvironment.getSourceFile",
428
429
  void 0,
@@ -431,13 +432,13 @@ var FormSpecSemanticService = class {
431
432
  if (sourceFile === void 0) {
432
433
  return null;
433
434
  }
434
- const checker = (0, import_internal.optionalMeasure)(
435
+ const checker = (0, import_internal2.optionalMeasure)(
435
436
  performance2,
436
437
  "semantic.sourceEnvironment.getTypeChecker",
437
438
  void 0,
438
439
  () => program.getTypeChecker()
439
440
  );
440
- const sourceHash = (0, import_internal.optionalMeasure)(
441
+ const sourceHash = (0, import_internal2.optionalMeasure)(
441
442
  performance2,
442
443
  "semantic.sourceEnvironment.computeTextHash",
443
444
  void 0,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/service.ts","../src/workspace.ts","../src/semantic-service.ts","../src/constants.ts","../src/perf-utils.ts"],"sourcesContent":["import type * as tsServer from \"typescript/lib/tsserverlibrary.js\";\nexport {\n FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n FORMSPEC_ANALYSIS_SCHEMA_VERSION,\n type CommentSourceSpan,\n type CommentSpan,\n type FormSpecAnalysisCommentSnapshot,\n type FormSpecAnalysisDiagnostic,\n type FormSpecAnalysisDiagnosticCategory,\n type FormSpecAnalysisDiagnosticDataValue,\n type FormSpecAnalysisDiagnosticLocation,\n type FormSpecAnalysisManifest,\n type FormSpecAnalysisFileSnapshot,\n type FormSpecAnalysisTagSnapshot,\n type FormSpecIpcEndpoint,\n type FormSpecPlacement,\n type FormSpecSemanticQuery,\n type FormSpecSemanticResponse,\n type FormSpecSerializedCommentTargetSpecifier,\n type FormSpecSerializedCompletionContext,\n type FormSpecSerializedHoverInfo,\n type FormSpecSerializedTagDefinition,\n type FormSpecSerializedTagSemanticContext,\n type FormSpecSerializedTagSignature,\n type FormSpecTargetKind,\n} from \"@formspec/analysis/protocol\";\nimport { createLanguageServiceProxy, FormSpecPluginService } from \"./service.js\";\nexport {\n createLanguageServiceProxy,\n FormSpecPluginService,\n type FormSpecPluginServiceOptions,\n type LoggerLike,\n} from \"./service.js\";\nexport {\n FormSpecSemanticService,\n type FormSpecSemanticCompletionResult,\n type FormSpecSemanticDiagnosticsResult,\n type FormSpecSemanticHoverResult,\n type FormSpecSemanticServiceOptions,\n type FormSpecSemanticServiceStats,\n} from \"./semantic-service.js\";\n\ninterface ServiceEntry {\n readonly service: FormSpecPluginService;\n referenceCount: number;\n}\n\nconst services = new Map<string, ServiceEntry>();\nconst PERF_LOG_ENV_VAR = \"FORMSPEC_PLUGIN_PROFILE\";\nconst PERF_LOG_THRESHOLD_ENV_VAR = \"FORMSPEC_PLUGIN_PROFILE_THRESHOLD_MS\";\n\nfunction formatPluginError(error: unknown): string {\n return error instanceof Error ? (error.stack ?? error.message) : String(error);\n}\n\nfunction readBooleanEnvFlag(name: string): boolean {\n const rawValue = process.env[name];\n return rawValue === \"1\" || rawValue === \"true\";\n}\n\nfunction readNumberEnvFlag(name: string): number | undefined {\n const rawValue = process.env[name];\n if (rawValue === undefined || rawValue.trim() === \"\") {\n return undefined;\n }\n\n const parsed = Number(rawValue);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction getOrCreateService(\n info: tsServer.server.PluginCreateInfo,\n typescriptVersion: string\n): FormSpecPluginService {\n const workspaceRoot = info.project.getCurrentDirectory();\n const existing = services.get(workspaceRoot);\n if (existing !== undefined) {\n existing.referenceCount += 1;\n attachProjectCloseHandler(info, workspaceRoot, existing);\n return existing.service;\n }\n\n const performanceLogThresholdMs = readNumberEnvFlag(PERF_LOG_THRESHOLD_ENV_VAR);\n const service = new FormSpecPluginService({\n workspaceRoot,\n typescriptVersion,\n getProgram: () => info.languageService.getProgram(),\n logger: info.project.projectService.logger,\n enablePerformanceLogging: readBooleanEnvFlag(PERF_LOG_ENV_VAR),\n ...(performanceLogThresholdMs === undefined ? {} : { performanceLogThresholdMs }),\n });\n\n const serviceEntry: ServiceEntry = {\n service,\n referenceCount: 1,\n };\n attachProjectCloseHandler(info, workspaceRoot, serviceEntry);\n\n service.start().catch((error: unknown) => {\n info.project.projectService.logger.info(\n `[FormSpec] Plugin service failed to start for ${workspaceRoot}: ${formatPluginError(error)}`\n );\n services.delete(workspaceRoot);\n });\n services.set(workspaceRoot, serviceEntry);\n return service;\n}\n\nfunction attachProjectCloseHandler(\n info: tsServer.server.PluginCreateInfo,\n workspaceRoot: string,\n serviceEntry: ServiceEntry\n): void {\n const originalClose = info.project.close.bind(info.project);\n let closed = false;\n\n info.project.close = () => {\n if (closed) {\n originalClose();\n return;\n }\n\n closed = true;\n serviceEntry.referenceCount -= 1;\n if (serviceEntry.referenceCount <= 0) {\n services.delete(workspaceRoot);\n void serviceEntry.service.stop().catch((error: unknown) => {\n info.project.projectService.logger.info(\n `[FormSpec] Failed to stop plugin service for ${workspaceRoot}: ${formatPluginError(error)}`\n );\n });\n }\n originalClose();\n };\n}\n\n/**\n * Initializes the FormSpec TypeScript language service plugin.\n *\n * @public\n */\nexport function init(modules: {\n readonly typescript: typeof tsServer;\n}): tsServer.server.PluginModule {\n const typescriptVersion = modules.typescript.version;\n return {\n create(info) {\n const service = getOrCreateService(info, typescriptVersion);\n return createLanguageServiceProxy(info.languageService, service.getSemanticService());\n },\n };\n}\n","import fs from \"node:fs/promises\";\nimport net from \"node:net\";\nimport * as ts from \"typescript\";\nimport {\n FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n isFormSpecSemanticQuery,\n type FormSpecAnalysisManifest,\n type FormSpecSemanticQuery,\n type FormSpecSemanticResponse,\n} from \"@formspec/analysis/protocol\";\nimport { type FormSpecPerformanceEvent } from \"@formspec/analysis/internal\";\nimport {\n createFormSpecAnalysisManifest,\n getFormSpecWorkspaceRuntimePaths,\n type FormSpecWorkspaceRuntimePaths,\n} from \"./workspace.js\";\nimport {\n FormSpecSemanticService,\n type FormSpecSemanticServiceOptions,\n type LoggerLike,\n} from \"./semantic-service.js\";\nimport {\n FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS,\n FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES,\n FORM_SPEC_PLUGIN_SOCKET_IDLE_TIMEOUT_MS,\n} from \"./constants.js\";\nimport { formatPerformanceEvent } from \"./perf-utils.js\";\n\n/**\n * Public configuration for the reference plugin wrapper that exposes\n * `FormSpecSemanticService` over the local manifest + IPC transport.\n *\n * Supports the same semantic-service options, including\n * `enablePerformanceLogging`, `performanceLogThresholdMs`, and\n * `snapshotDebounceMs`. The packaged tsserver plugin wires these from\n * `FORMSPEC_PLUGIN_PROFILE=1` and `FORMSPEC_PLUGIN_PROFILE_THRESHOLD_MS`.\n *\n * @public\n */\nexport type FormSpecPluginServiceOptions = FormSpecSemanticServiceOptions;\n\n/**\n * Reference manifest/socket wrapper around `FormSpecSemanticService`.\n *\n * Downstream TypeScript hosts that already control their own plugin/runtime\n * lifecycle can use `FormSpecSemanticService` directly and skip this wrapper.\n *\n * @public\n */\nexport class FormSpecPluginService {\n private readonly manifest: FormSpecAnalysisManifest;\n private readonly runtimePaths: FormSpecWorkspaceRuntimePaths;\n private readonly semanticService: FormSpecSemanticService;\n private server: net.Server | null = null;\n\n public constructor(private readonly options: FormSpecPluginServiceOptions) {\n this.semanticService = new FormSpecSemanticService(options);\n this.runtimePaths = getFormSpecWorkspaceRuntimePaths(options.workspaceRoot);\n this.manifest = createFormSpecAnalysisManifest(\n options.workspaceRoot,\n options.typescriptVersion,\n Date.now()\n );\n }\n\n public getManifest(): FormSpecAnalysisManifest {\n return this.manifest;\n }\n\n /**\n * Returns the underlying semantic service used by this reference wrapper.\n *\n * @public\n */\n public getSemanticService(): FormSpecSemanticService {\n return this.semanticService;\n }\n\n public async start(): Promise<void> {\n if (this.server !== null) {\n return;\n }\n\n await fs.mkdir(this.runtimePaths.runtimeDirectory, { recursive: true });\n if (this.runtimePaths.endpoint.kind === \"unix-socket\") {\n await fs.rm(this.runtimePaths.endpoint.address, { force: true });\n }\n\n this.server = net.createServer((socket) => {\n let buffer = \"\";\n socket.setEncoding(\"utf8\");\n socket.setTimeout(FORM_SPEC_PLUGIN_SOCKET_IDLE_TIMEOUT_MS, () => {\n this.options.logger?.info(\n `[FormSpec] Closing idle semantic query socket for ${this.runtimePaths.workspaceRoot}`\n );\n socket.destroy();\n });\n socket.on(\"data\", (chunk) => {\n buffer += String(chunk);\n if (buffer.length > FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES) {\n socket.end(\n `${JSON.stringify({\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n kind: \"error\",\n error: `FormSpec semantic query exceeded ${String(FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES)} bytes`,\n } satisfies FormSpecSemanticResponse)}\\n`\n );\n return;\n }\n\n const newlineIndex = buffer.indexOf(\"\\n\");\n if (newlineIndex < 0) {\n return;\n }\n\n const payload = buffer.slice(0, newlineIndex);\n const remaining = buffer.slice(newlineIndex + 1);\n if (remaining.trim().length > 0) {\n this.options.logger?.info(\n `[FormSpec] Ignoring extra semantic query payload data for ${this.runtimePaths.workspaceRoot}`\n );\n }\n buffer = remaining;\n // The FormSpec IPC transport is intentionally one-request-per-connection.\n this.respondToSocket(socket, payload);\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n const handleError = (error: Error) => {\n reject(error);\n };\n this.server?.once(\"error\", handleError);\n this.server?.listen(this.runtimePaths.endpoint.address, () => {\n this.server?.off(\"error\", handleError);\n resolve();\n });\n });\n\n await this.writeManifest();\n }\n\n public async stop(): Promise<void> {\n this.semanticService.dispose();\n\n const server = this.server;\n this.server = null;\n if (server?.listening === true) {\n await new Promise<void>((resolve, reject) => {\n server.close((error) => {\n if (error === undefined) {\n resolve();\n return;\n }\n reject(error);\n });\n });\n }\n\n await this.cleanupRuntimeArtifacts();\n }\n\n public scheduleSnapshotRefresh(filePath: string): void {\n this.semanticService.scheduleSnapshotRefresh(filePath);\n }\n\n public handleQuery(query: FormSpecSemanticQuery): FormSpecSemanticResponse {\n if (this.options.enablePerformanceLogging === true) {\n const startedAt = performance.now();\n const response = this.executeQuery(query);\n this.logQueryDuration(query, performance.now() - startedAt);\n return response;\n }\n\n return this.executeQuery(query);\n }\n\n private executeQuery(query: FormSpecSemanticQuery): FormSpecSemanticResponse {\n switch (query.kind) {\n case \"health\":\n return {\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n kind: \"health\",\n manifest: this.manifest,\n };\n case \"completion\": {\n const result = this.semanticService.getCompletionContext(query.filePath, query.offset);\n if (result === null) {\n return {\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n kind: \"error\",\n error: `Unable to resolve TypeScript source file for ${query.filePath}`,\n };\n }\n\n return {\n ...result,\n kind: \"completion\",\n };\n }\n case \"hover\": {\n const result = this.semanticService.getHover(query.filePath, query.offset);\n if (result === null) {\n return {\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n kind: \"error\",\n error: `Unable to resolve TypeScript source file for ${query.filePath}`,\n };\n }\n\n return {\n ...result,\n kind: \"hover\",\n };\n }\n case \"diagnostics\": {\n const result = this.semanticService.getDiagnostics(query.filePath);\n return {\n ...result,\n kind: \"diagnostics\",\n };\n }\n case \"file-snapshot\":\n return {\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n kind: \"file-snapshot\",\n snapshot: this.semanticService.getFileSnapshot(query.filePath),\n };\n default: {\n throw new Error(`Unhandled semantic query: ${JSON.stringify(query)}`);\n }\n }\n }\n\n private respondToSocket(socket: net.Socket, payload: string): void {\n try {\n const query = JSON.parse(payload) as unknown;\n if (!isFormSpecSemanticQuery(query)) {\n throw new Error(\"Invalid FormSpec semantic query payload\");\n }\n const response = this.handleQuery(query);\n socket.end(`${JSON.stringify(response)}\\n`);\n } catch (error) {\n socket.end(\n `${JSON.stringify({\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n kind: \"error\",\n error: error instanceof Error ? error.message : String(error),\n } satisfies FormSpecSemanticResponse)}\\n`\n );\n }\n }\n\n private async writeManifest(): Promise<void> {\n const tempManifestPath = `${this.runtimePaths.manifestPath}.tmp`;\n await fs.writeFile(tempManifestPath, `${JSON.stringify(this.manifest, null, 2)}\\n`, \"utf8\");\n await fs.rename(tempManifestPath, this.runtimePaths.manifestPath);\n }\n\n private async cleanupRuntimeArtifacts(): Promise<void> {\n await fs.rm(this.runtimePaths.manifestPath, { force: true });\n\n if (this.runtimePaths.endpoint.kind === \"unix-socket\") {\n await fs.rm(this.runtimePaths.endpoint.address, { force: true });\n }\n }\n\n private logQueryDuration(query: FormSpecSemanticQuery, durationMs: number): void {\n const logger = this.options.logger;\n if (logger === undefined) {\n return;\n }\n\n const thresholdMs =\n this.options.performanceLogThresholdMs ??\n FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS;\n if (durationMs < thresholdMs) {\n return;\n }\n\n const event: FormSpecPerformanceEvent = {\n name: \"plugin.handleQuery\",\n durationMs,\n detail: {\n kind: query.kind,\n ...(query.kind === \"health\" ? {} : { filePath: query.filePath }),\n },\n };\n logger.info(`[FormSpec][perf] ${formatPerformanceEvent(event)}`);\n }\n}\n\n/**\n * Reference proxy wrapper that keeps FormSpec semantic snapshots fresh while\n * delegating actual TypeScript editor features to the original service.\n *\n * @public\n */\nexport function createLanguageServiceProxy(\n languageService: ts.LanguageService,\n semanticService: FormSpecSemanticService\n): ts.LanguageService {\n const wrapWithSnapshotRefresh = <Args extends readonly unknown[], Result>(\n fn: (fileName: string, ...args: Args) => Result\n ) => {\n return (fileName: string, ...args: Args): Result => {\n semanticService.scheduleSnapshotRefresh(fileName);\n return fn(fileName, ...args);\n };\n };\n\n const getSemanticDiagnostics = wrapWithSnapshotRefresh((fileName) =>\n languageService.getSemanticDiagnostics(fileName)\n );\n\n const getCompletionsAtPosition = wrapWithSnapshotRefresh(\n (fileName: string, position: number, options: ts.GetCompletionsAtPositionOptions | undefined) =>\n languageService.getCompletionsAtPosition(fileName, position, options)\n );\n\n const getQuickInfoAtPosition = wrapWithSnapshotRefresh((fileName, position: number) =>\n languageService.getQuickInfoAtPosition(fileName, position)\n );\n\n return new Proxy(languageService, {\n get(target, property, receiver) {\n switch (property) {\n case \"getSemanticDiagnostics\":\n return getSemanticDiagnostics;\n case \"getCompletionsAtPosition\":\n return getCompletionsAtPosition;\n case \"getQuickInfoAtPosition\":\n return getQuickInfoAtPosition;\n default:\n return Reflect.get(target, property, receiver) as unknown;\n }\n },\n });\n}\n\nexport type { LoggerLike };\n","import os from \"node:os\";\nimport path from \"node:path\";\nimport {\n FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n FORMSPEC_ANALYSIS_SCHEMA_VERSION,\n getFormSpecManifestPath,\n getFormSpecWorkspaceId,\n getFormSpecWorkspaceRuntimeDirectory,\n type FormSpecAnalysisManifest,\n type FormSpecIpcEndpoint,\n} from \"@formspec/analysis/protocol\";\n\nexport interface FormSpecWorkspaceRuntimePaths {\n readonly workspaceRoot: string;\n readonly workspaceId: string;\n readonly runtimeDirectory: string;\n readonly manifestPath: string;\n readonly endpoint: FormSpecIpcEndpoint;\n}\n\nexport function getFormSpecWorkspaceRuntimePaths(\n workspaceRoot: string,\n platform = process.platform,\n userScope = getFormSpecUserScope()\n): FormSpecWorkspaceRuntimePaths {\n const workspaceId = getFormSpecWorkspaceId(workspaceRoot);\n const runtimeDirectory = getFormSpecWorkspaceRuntimeDirectory(workspaceRoot);\n const sanitizedUserScope = sanitizeScopeSegment(userScope);\n const endpoint: FormSpecIpcEndpoint =\n platform === \"win32\"\n ? {\n kind: \"windows-pipe\",\n address: `\\\\\\\\.\\\\pipe\\\\formspec-${sanitizedUserScope}-${workspaceId}`,\n }\n : {\n kind: \"unix-socket\",\n address: path.join(os.tmpdir(), `formspec-${sanitizedUserScope}-${workspaceId}.sock`),\n };\n\n return {\n workspaceRoot,\n workspaceId,\n runtimeDirectory,\n manifestPath: getFormSpecManifestPath(workspaceRoot),\n endpoint,\n };\n}\n\nexport function createFormSpecAnalysisManifest(\n workspaceRoot: string,\n typescriptVersion: string,\n generation: number,\n extensionFingerprint = \"builtin\"\n): FormSpecAnalysisManifest {\n const paths = getFormSpecWorkspaceRuntimePaths(workspaceRoot);\n return {\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n analysisSchemaVersion: FORMSPEC_ANALYSIS_SCHEMA_VERSION,\n workspaceRoot,\n workspaceId: paths.workspaceId,\n endpoint: paths.endpoint,\n typescriptVersion,\n extensionFingerprint,\n generation,\n updatedAt: new Date().toISOString(),\n };\n}\n\nfunction getFormSpecUserScope(): string {\n try {\n return sanitizeScopeSegment(os.userInfo().username);\n } catch {\n return sanitizeScopeSegment(\n process.env[\"USER\"] ?? process.env[\"USERNAME\"] ?? process.env[\"LOGNAME\"] ?? \"formspec\"\n );\n }\n}\n\nfunction sanitizeScopeSegment(value: string): string {\n const trimmed = value.trim().toLowerCase();\n const sanitized = trimmed.replace(/[^a-z0-9_-]+/gu, \"-\").replace(/-+/gu, \"-\");\n return sanitized.length > 0 ? sanitized : \"formspec\";\n}\n","import * as ts from \"typescript\";\nimport {\n FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n computeFormSpecTextHash,\n type FormSpecAnalysisDiagnostic,\n type FormSpecAnalysisFileSnapshot,\n type FormSpecSerializedCompletionContext,\n type FormSpecSerializedHoverInfo,\n} from \"@formspec/analysis/protocol\";\nimport {\n buildFormSpecAnalysisFileSnapshot,\n createFormSpecPerformanceRecorder,\n findDeclarationForCommentOffset,\n getCommentHoverInfoAtOffset,\n getSemanticCommentCompletionContextAtOffset,\n getFormSpecPerformanceNow,\n getSubjectType,\n optionalMeasure,\n resolveDeclarationPlacement,\n serializeCompletionContext,\n serializeHoverInfo,\n type BuildFormSpecAnalysisFileSnapshotOptions,\n type FormSpecPerformanceEvent,\n type FormSpecPerformanceRecorder,\n} from \"@formspec/analysis/internal\";\nimport {\n FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS,\n FORM_SPEC_PLUGIN_DEFAULT_SNAPSHOT_DEBOUNCE_MS,\n} from \"./constants.js\";\nimport { formatPerformanceEvent } from \"./perf-utils.js\";\n\n/** @public */\nexport interface LoggerLike {\n info(message: string): void;\n}\n\n/** @public */\nexport interface FormSpecSemanticServiceOptions {\n /** Workspace root used for runtime paths and contextual logging. */\n readonly workspaceRoot: string;\n /** TypeScript version string reported by the host runtime. */\n readonly typescriptVersion: string;\n /** Supplies the current host program. Returns `undefined` until ready. */\n readonly getProgram: () => ts.Program | undefined;\n /** Optional logger used for profiling and refresh-failure messages. */\n readonly logger?: LoggerLike;\n /** Enables structured hotspot logging for semantic queries. */\n readonly enablePerformanceLogging?: boolean;\n /** Minimum query duration, in milliseconds, required before logging. */\n readonly performanceLogThresholdMs?: number;\n /** Debounce window, in milliseconds, for background snapshot refresh. */\n readonly snapshotDebounceMs?: number;\n /** Injectable clock used by tests and runtime snapshot timestamps. */\n readonly now?: () => Date;\n}\n\n/** @public */\nexport interface FormSpecSemanticCompletionResult {\n readonly protocolVersion: typeof FORMSPEC_ANALYSIS_PROTOCOL_VERSION;\n readonly sourceHash: string;\n readonly context: FormSpecSerializedCompletionContext;\n}\n\n/** @public */\nexport interface FormSpecSemanticHoverResult {\n readonly protocolVersion: typeof FORMSPEC_ANALYSIS_PROTOCOL_VERSION;\n readonly sourceHash: string;\n readonly hover: FormSpecSerializedHoverInfo | null;\n}\n\n/** @public */\nexport interface FormSpecSemanticDiagnosticsResult {\n readonly protocolVersion: typeof FORMSPEC_ANALYSIS_PROTOCOL_VERSION;\n readonly sourceHash: string;\n readonly diagnostics: readonly FormSpecAnalysisDiagnostic[];\n}\n\n/** @public */\nexport interface FormSpecSemanticServiceStats {\n /** Total number of calls by semantic query kind. */\n readonly queryTotals: {\n readonly completion: number;\n readonly hover: number;\n readonly diagnostics: number;\n readonly fileSnapshot: number;\n };\n /** Cold vs warm query path counts for snapshot-backed operations. */\n readonly queryPathTotals: {\n readonly diagnostics: { readonly cold: number; readonly warm: number };\n readonly fileSnapshot: { readonly cold: number; readonly warm: number };\n };\n /** Number of file snapshot cache hits. */\n readonly fileSnapshotCacheHits: number;\n /** Number of file snapshot cache misses. */\n readonly fileSnapshotCacheMisses: number;\n /** Number of synthetic batch cache hits. */\n readonly syntheticBatchCacheHits: number;\n /** Number of synthetic batch cache misses. */\n readonly syntheticBatchCacheMisses: number;\n /** Number of synthetic compiler program creations. */\n readonly syntheticCompileCount: number;\n /** Total synthetic application count compiled across misses. */\n readonly syntheticCompileApplications: number;\n}\n\ninterface CachedFileSnapshot {\n readonly sourceHash: string;\n readonly snapshot: FormSpecAnalysisFileSnapshot;\n}\n\ninterface SourceEnvironment {\n readonly sourceFile: ts.SourceFile;\n readonly checker: ts.TypeChecker;\n readonly sourceHash: string;\n}\n\ninterface CommentQueryContext extends SourceEnvironment {\n readonly declaration: ts.Node | null;\n readonly placement: ReturnType<typeof resolveDeclarationPlacement>;\n readonly subjectType: ts.Type | undefined;\n}\n\ntype SnapshotCacheState = \"hit\" | \"miss\" | \"missing-source\";\n\ninterface MutableSemanticServiceStats {\n queryTotals: {\n completion: number;\n hover: number;\n diagnostics: number;\n fileSnapshot: number;\n };\n queryPathTotals: {\n diagnostics: { cold: number; warm: number };\n fileSnapshot: { cold: number; warm: number };\n };\n fileSnapshotCacheHits: number;\n fileSnapshotCacheMisses: number;\n syntheticBatchCacheHits: number;\n syntheticBatchCacheMisses: number;\n syntheticCompileCount: number;\n syntheticCompileApplications: number;\n}\n\nconst STATS_ONLY_EVENT_NAMES = new Set<string>([\n \"analysis.syntheticCheckBatch.cacheHit\",\n \"analysis.narrowSyntheticCheckBatch.cacheHit\",\n \"analysis.syntheticCheckBatch.cacheMiss\",\n \"analysis.narrowSyntheticCheckBatch.cacheMiss\",\n \"analysis.syntheticCheckBatch.createProgram\",\n \"analysis.narrowSyntheticCheckBatch.createProgram\",\n]);\n\nclass StatsOnlyPerformanceRecorder implements FormSpecPerformanceRecorder {\n private readonly mutableEvents: FormSpecPerformanceEvent[] = [];\n\n public get events(): readonly FormSpecPerformanceEvent[] {\n return this.mutableEvents;\n }\n\n public measure<T>(\n name: string,\n detail: Readonly<Record<string, string | number | boolean>> | undefined,\n callback: () => T\n ): T {\n const result = callback();\n if (STATS_ONLY_EVENT_NAMES.has(name)) {\n this.mutableEvents.push({\n name,\n durationMs: 0,\n ...(detail === undefined ? {} : { detail }),\n });\n }\n return result;\n }\n\n public record(event: FormSpecPerformanceEvent): void {\n if (STATS_ONLY_EVENT_NAMES.has(event.name)) {\n this.mutableEvents.push(event);\n }\n }\n}\n\n/**\n * Reusable in-process semantic service for FormSpec authoring features.\n *\n * Downstream TypeScript hosts can construct this directly against their own\n * `Program` and own the final presentation of completions, hover, and\n * diagnostics. The shipped tsserver plugin is a reference wrapper over this\n * public service.\n *\n * @public\n */\nexport class FormSpecSemanticService {\n private readonly snapshotCache = new Map<string, CachedFileSnapshot>();\n private readonly refreshTimers = new Map<string, NodeJS.Timeout>();\n private readonly stats: MutableSemanticServiceStats = {\n queryTotals: {\n completion: 0,\n hover: 0,\n diagnostics: 0,\n fileSnapshot: 0,\n },\n queryPathTotals: {\n diagnostics: { cold: 0, warm: 0 },\n fileSnapshot: { cold: 0, warm: 0 },\n },\n fileSnapshotCacheHits: 0,\n fileSnapshotCacheMisses: 0,\n syntheticBatchCacheHits: 0,\n syntheticBatchCacheMisses: 0,\n syntheticCompileCount: 0,\n syntheticCompileApplications: 0,\n };\n\n public constructor(private readonly options: FormSpecSemanticServiceOptions) {}\n\n /** Resolves semantic completion context for a comment cursor position. */\n public getCompletionContext(\n filePath: string,\n offset: number\n ): FormSpecSemanticCompletionResult | null {\n this.stats.queryTotals.completion += 1;\n return this.runMeasured(\"semantic.getCompletionContext\", { filePath, offset }, (performance) =>\n this.withCommentQueryContext(filePath, offset, performance, (context) => ({\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n sourceHash: context.sourceHash,\n context: serializeCompletionContext(\n getSemanticCommentCompletionContextAtOffset(context.sourceFile.text, offset, {\n checker: context.checker,\n ...(context.placement === null ? {} : { placement: context.placement }),\n ...(context.subjectType === undefined ? {} : { subjectType: context.subjectType }),\n })\n ),\n }))\n );\n }\n\n /** Resolves semantic hover payload for a comment cursor position. */\n public getHover(filePath: string, offset: number): FormSpecSemanticHoverResult | null {\n this.stats.queryTotals.hover += 1;\n return this.runMeasured(\"semantic.getHover\", { filePath, offset }, (performance) =>\n this.withCommentQueryContext(filePath, offset, performance, (context) => ({\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n sourceHash: context.sourceHash,\n hover: serializeHoverInfo(\n getCommentHoverInfoAtOffset(context.sourceFile.text, offset, {\n checker: context.checker,\n ...(context.placement === null ? {} : { placement: context.placement }),\n ...(context.subjectType === undefined ? {} : { subjectType: context.subjectType }),\n })\n ),\n }))\n );\n }\n\n /** Returns canonical FormSpec diagnostics for a file in the current host program. */\n public getDiagnostics(filePath: string): FormSpecSemanticDiagnosticsResult {\n this.stats.queryTotals.diagnostics += 1;\n return this.runMeasured(\"semantic.getDiagnostics\", { filePath }, (performance) => {\n const { snapshot, cacheState } = this.getFileSnapshotWithCacheState(filePath, performance);\n this.recordQueryPath(\"diagnostics\", cacheState);\n return {\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n sourceHash: snapshot.sourceHash,\n diagnostics: snapshot.diagnostics,\n };\n });\n }\n\n /** Returns the full serialized semantic snapshot for a file. */\n public getFileSnapshot(filePath: string): FormSpecAnalysisFileSnapshot {\n this.stats.queryTotals.fileSnapshot += 1;\n return this.runMeasured(\"semantic.getFileSnapshot\", { filePath }, (performance) => {\n const { snapshot, cacheState } = this.getFileSnapshotWithCacheState(filePath, performance);\n this.recordQueryPath(\"fileSnapshot\", cacheState);\n return snapshot;\n });\n }\n\n /** Schedules a debounced background refresh for the file snapshot cache. */\n public scheduleSnapshotRefresh(filePath: string): void {\n const existing = this.refreshTimers.get(filePath);\n if (existing !== undefined) {\n clearTimeout(existing);\n }\n\n const timer = setTimeout(() => {\n try {\n this.getFileSnapshot(filePath);\n } catch (error: unknown) {\n this.options.logger?.info(\n `[FormSpec] Failed to refresh semantic snapshot for ${filePath}: ${String(error)}`\n );\n }\n this.refreshTimers.delete(filePath);\n }, this.options.snapshotDebounceMs ?? FORM_SPEC_PLUGIN_DEFAULT_SNAPSHOT_DEBOUNCE_MS);\n timer.unref();\n\n this.refreshTimers.set(filePath, timer);\n }\n\n /** Clears pending timers and cached semantic snapshots. */\n public dispose(): void {\n for (const timer of this.refreshTimers.values()) {\n clearTimeout(timer);\n }\n this.refreshTimers.clear();\n this.snapshotCache.clear();\n }\n\n /** Returns a copy of the current performance and cache counters. */\n public getStats(): FormSpecSemanticServiceStats {\n return {\n queryTotals: { ...this.stats.queryTotals },\n queryPathTotals: {\n diagnostics: { ...this.stats.queryPathTotals.diagnostics },\n fileSnapshot: { ...this.stats.queryPathTotals.fileSnapshot },\n },\n fileSnapshotCacheHits: this.stats.fileSnapshotCacheHits,\n fileSnapshotCacheMisses: this.stats.fileSnapshotCacheMisses,\n syntheticBatchCacheHits: this.stats.syntheticBatchCacheHits,\n syntheticBatchCacheMisses: this.stats.syntheticBatchCacheMisses,\n syntheticCompileCount: this.stats.syntheticCompileCount,\n syntheticCompileApplications: this.stats.syntheticCompileApplications,\n };\n }\n\n private runMeasured<T>(\n name: string,\n detail: Record<string, string | number>,\n fn: (performance: FormSpecPerformanceRecorder) => T\n ): T {\n const performance =\n this.options.enablePerformanceLogging === true\n ? createFormSpecPerformanceRecorder()\n : new StatsOnlyPerformanceRecorder();\n const result = optionalMeasure(performance, name, detail, () => fn(performance));\n this.updateStatsFromPerformanceEvents(performance.events);\n if (this.options.enablePerformanceLogging === true) {\n this.logPerformanceEvents(name, performance.events);\n }\n return result;\n }\n\n private withCommentQueryContext<T>(\n filePath: string,\n offset: number,\n performance: FormSpecPerformanceRecorder,\n handler: (context: CommentQueryContext) => T\n ): T | null {\n return optionalMeasure(\n performance,\n \"semantic.resolveCommentQueryContext\",\n {\n filePath,\n offset,\n },\n () => {\n const environment = this.getSourceEnvironment(filePath, performance);\n if (environment === null) {\n return null;\n }\n\n const declaration = optionalMeasure(\n performance,\n \"semantic.findDeclarationForCommentOffset\",\n {\n filePath,\n offset,\n },\n () => findDeclarationForCommentOffset(environment.sourceFile, offset)\n );\n const placement =\n declaration === null\n ? null\n : optionalMeasure(performance, \"semantic.resolveDeclarationPlacement\", undefined, () =>\n resolveDeclarationPlacement(declaration)\n );\n const subjectType =\n declaration === null\n ? undefined\n : optionalMeasure(performance, \"semantic.getSubjectType\", undefined, () =>\n getSubjectType(declaration, environment.checker)\n );\n\n return handler({\n ...environment,\n declaration,\n placement,\n subjectType,\n });\n }\n );\n }\n\n private getFileSnapshotWithCacheState(\n filePath: string,\n performance: FormSpecPerformanceRecorder\n ): {\n readonly snapshot: FormSpecAnalysisFileSnapshot;\n readonly cacheState: SnapshotCacheState;\n } {\n const startedAt = getFormSpecPerformanceNow();\n const environment = this.getSourceEnvironment(filePath, performance);\n if (environment === null) {\n this.stats.fileSnapshotCacheMisses += 1;\n const snapshot: FormSpecAnalysisFileSnapshot = {\n filePath,\n sourceHash: \"\",\n generatedAt: this.getNow().toISOString(),\n comments: [],\n diagnostics: [\n {\n code: \"MISSING_SOURCE_FILE\",\n category: \"infrastructure\",\n message: `Unable to resolve TypeScript source file for ${filePath}`,\n range: { start: 0, end: 0 },\n severity: \"warning\",\n relatedLocations: [],\n data: {\n filePath,\n },\n },\n ],\n };\n performance.record({\n name: \"semantic.getFileSnapshot.result\",\n durationMs: getFormSpecPerformanceNow() - startedAt,\n detail: {\n filePath,\n cache: \"missing-source\",\n },\n });\n return {\n snapshot,\n cacheState: \"missing-source\",\n };\n }\n\n const cached = this.snapshotCache.get(filePath);\n if (cached?.sourceHash === environment.sourceHash) {\n this.stats.fileSnapshotCacheHits += 1;\n performance.record({\n name: \"semantic.getFileSnapshot.result\",\n durationMs: getFormSpecPerformanceNow() - startedAt,\n detail: {\n filePath,\n cache: \"hit\",\n },\n });\n return {\n snapshot: cached.snapshot,\n cacheState: \"hit\",\n };\n }\n\n this.stats.fileSnapshotCacheMisses += 1;\n const snapshot = buildFormSpecAnalysisFileSnapshot(environment.sourceFile, {\n checker: environment.checker,\n now: () => this.getNow(),\n performance,\n } satisfies BuildFormSpecAnalysisFileSnapshotOptions);\n this.snapshotCache.set(filePath, {\n sourceHash: environment.sourceHash,\n snapshot,\n });\n performance.record({\n name: \"semantic.getFileSnapshot.result\",\n durationMs: getFormSpecPerformanceNow() - startedAt,\n detail: {\n filePath,\n cache: \"miss\",\n },\n });\n return {\n snapshot,\n cacheState: \"miss\",\n };\n }\n\n private getNow(): Date {\n return this.options.now?.() ?? new Date();\n }\n\n private getSourceEnvironment(\n filePath: string,\n performance: FormSpecPerformanceRecorder\n ): SourceEnvironment | null {\n return optionalMeasure(\n performance,\n \"semantic.getSourceEnvironment\",\n {\n filePath,\n },\n () => {\n const program = optionalMeasure(\n performance,\n \"semantic.sourceEnvironment.getProgram\",\n undefined,\n () => this.options.getProgram()\n );\n if (program === undefined) {\n return null;\n }\n\n const sourceFile = optionalMeasure(\n performance,\n \"semantic.sourceEnvironment.getSourceFile\",\n undefined,\n () => program.getSourceFile(filePath)\n );\n if (sourceFile === undefined) {\n return null;\n }\n\n const checker = optionalMeasure(\n performance,\n \"semantic.sourceEnvironment.getTypeChecker\",\n undefined,\n () => program.getTypeChecker()\n );\n const sourceHash = optionalMeasure(\n performance,\n \"semantic.sourceEnvironment.computeTextHash\",\n undefined,\n () => computeFormSpecTextHash(sourceFile.text)\n );\n\n return {\n sourceFile,\n checker,\n sourceHash,\n };\n }\n );\n }\n\n private recordQueryPath(\n kind: \"diagnostics\" | \"fileSnapshot\",\n cacheState: SnapshotCacheState\n ): void {\n if (cacheState === \"hit\") {\n this.stats.queryPathTotals[kind].warm += 1;\n return;\n }\n\n this.stats.queryPathTotals[kind].cold += 1;\n }\n\n private updateStatsFromPerformanceEvents(events: readonly FormSpecPerformanceEvent[]): void {\n for (const event of events) {\n if (\n event.name === \"analysis.syntheticCheckBatch.cacheHit\" ||\n event.name === \"analysis.narrowSyntheticCheckBatch.cacheHit\"\n ) {\n this.stats.syntheticBatchCacheHits += 1;\n continue;\n }\n\n if (\n event.name === \"analysis.syntheticCheckBatch.cacheMiss\" ||\n event.name === \"analysis.narrowSyntheticCheckBatch.cacheMiss\"\n ) {\n this.stats.syntheticBatchCacheMisses += 1;\n const applicationCount = event.detail?.[\"applicationCount\"];\n if (typeof applicationCount === \"number\") {\n this.stats.syntheticCompileApplications += applicationCount;\n }\n continue;\n }\n\n if (\n event.name === \"analysis.syntheticCheckBatch.createProgram\" ||\n event.name === \"analysis.narrowSyntheticCheckBatch.createProgram\"\n ) {\n this.stats.syntheticCompileCount += 1;\n }\n }\n }\n\n private logPerformanceEvents(\n rootEventName: string,\n events: readonly FormSpecPerformanceEvent[]\n ): void {\n const logger = this.options.logger;\n if (logger === undefined || events.length === 0) {\n return;\n }\n\n const rootEvent = [...events].reverse().find((event) => event.name === rootEventName);\n if (rootEvent === undefined) {\n return;\n }\n\n const thresholdMs =\n this.options.performanceLogThresholdMs ??\n FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS;\n if (rootEvent.durationMs < thresholdMs) {\n return;\n }\n\n const sortedHotspots = [...events]\n .filter((event) => event.name !== rootEventName)\n .sort((left, right) => right.durationMs - left.durationMs)\n .slice(0, 8);\n const lines = [\n `[FormSpec][perf] ${formatPerformanceEvent(rootEvent)}`,\n ...sortedHotspots.map((event) => ` ${formatPerformanceEvent(event)}`),\n ];\n logger.info(lines.join(\"\\n\"));\n }\n}\n","export const FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES = 256 * 1024;\nexport const FORM_SPEC_PLUGIN_SOCKET_IDLE_TIMEOUT_MS = 30_000;\nexport const FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS = 50;\nexport const FORM_SPEC_PLUGIN_DEFAULT_SNAPSHOT_DEBOUNCE_MS = 250;\n\nexport const FORM_SPEC_PLUGIN_PERFORMANCE_EVENT = {\n handleQuery: \"plugin.handleQuery\",\n} as const;\n","import type { FormSpecPerformanceEvent } from \"@formspec/analysis/internal\";\n\nexport function formatPerformanceEvent(event: FormSpecPerformanceEvent): string {\n const detailEntries = Object.entries(event.detail ?? {})\n .map(([key, value]) => `${key}=${String(value)}`)\n .join(\" \");\n return `${event.durationMs.toFixed(1)}ms ${event.name}${detailEntries === \"\" ? \"\" : ` ${detailEntries}`}`;\n}\n"],"mappings":"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","names":["import_protocol","ts","import_protocol","import_internal","path","os","import_protocol","performance","snapshot","fs","net"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/service.ts","../src/workspace.ts","../src/semantic-service.ts","../src/constants.ts","../src/perf-utils.ts"],"sourcesContent":["import type * as tsServer from \"typescript/lib/tsserverlibrary.js\";\nexport {\n FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n FORMSPEC_ANALYSIS_SCHEMA_VERSION,\n type CommentSourceSpan,\n type CommentSpan,\n type FormSpecAnalysisCommentSnapshot,\n type FormSpecAnalysisDiagnostic,\n type FormSpecAnalysisDiagnosticCategory,\n type FormSpecAnalysisDiagnosticDataValue,\n type FormSpecAnalysisDiagnosticLocation,\n type FormSpecAnalysisManifest,\n type FormSpecAnalysisFileSnapshot,\n type FormSpecAnalysisTagSnapshot,\n type FormSpecIpcEndpoint,\n type FormSpecPlacement,\n type FormSpecSemanticQuery,\n type FormSpecSemanticResponse,\n type FormSpecSerializedCommentTargetSpecifier,\n type FormSpecSerializedCompletionContext,\n type FormSpecSerializedHoverInfo,\n type FormSpecSerializedTagDefinition,\n type FormSpecSerializedTagSemanticContext,\n type FormSpecSerializedTagSignature,\n type FormSpecTargetKind,\n} from \"@formspec/analysis/protocol\";\nimport { createLanguageServiceProxy, FormSpecPluginService } from \"./service.js\";\nexport {\n createLanguageServiceProxy,\n FormSpecPluginService,\n type FormSpecPluginServiceOptions,\n type LoggerLike,\n} from \"./service.js\";\nexport {\n FormSpecSemanticService,\n type FormSpecSemanticCompletionResult,\n type FormSpecSemanticDiagnosticsResult,\n type FormSpecSemanticHoverResult,\n type FormSpecSemanticServiceOptions,\n type FormSpecSemanticServiceStats,\n} from \"./semantic-service.js\";\n\ninterface ServiceEntry {\n readonly service: FormSpecPluginService;\n referenceCount: number;\n}\n\nconst services = new Map<string, ServiceEntry>();\nconst PERF_LOG_ENV_VAR = \"FORMSPEC_PLUGIN_PROFILE\";\nconst PERF_LOG_THRESHOLD_ENV_VAR = \"FORMSPEC_PLUGIN_PROFILE_THRESHOLD_MS\";\n\nfunction formatPluginError(error: unknown): string {\n return error instanceof Error ? (error.stack ?? error.message) : String(error);\n}\n\nfunction readBooleanEnvFlag(name: string): boolean {\n const rawValue = process.env[name];\n return rawValue === \"1\" || rawValue === \"true\";\n}\n\nfunction readNumberEnvFlag(name: string): number | undefined {\n const rawValue = process.env[name];\n if (rawValue === undefined || rawValue.trim() === \"\") {\n return undefined;\n }\n\n const parsed = Number(rawValue);\n return Number.isFinite(parsed) ? parsed : undefined;\n}\n\nfunction getOrCreateService(\n info: tsServer.server.PluginCreateInfo,\n typescriptVersion: string\n): FormSpecPluginService {\n const workspaceRoot = info.project.getCurrentDirectory();\n const existing = services.get(workspaceRoot);\n if (existing !== undefined) {\n existing.referenceCount += 1;\n attachProjectCloseHandler(info, workspaceRoot, existing);\n return existing.service;\n }\n\n const performanceLogThresholdMs = readNumberEnvFlag(PERF_LOG_THRESHOLD_ENV_VAR);\n const service = new FormSpecPluginService({\n workspaceRoot,\n typescriptVersion,\n getProgram: () => info.languageService.getProgram(),\n logger: info.project.projectService.logger,\n enablePerformanceLogging: readBooleanEnvFlag(PERF_LOG_ENV_VAR),\n ...(performanceLogThresholdMs === undefined ? {} : { performanceLogThresholdMs }),\n });\n\n const serviceEntry: ServiceEntry = {\n service,\n referenceCount: 1,\n };\n attachProjectCloseHandler(info, workspaceRoot, serviceEntry);\n\n service.start().catch((error: unknown) => {\n info.project.projectService.logger.info(\n `[FormSpec] Plugin service failed to start for ${workspaceRoot}: ${formatPluginError(error)}`\n );\n services.delete(workspaceRoot);\n });\n services.set(workspaceRoot, serviceEntry);\n return service;\n}\n\nfunction attachProjectCloseHandler(\n info: tsServer.server.PluginCreateInfo,\n workspaceRoot: string,\n serviceEntry: ServiceEntry\n): void {\n const originalClose = info.project.close.bind(info.project);\n let closed = false;\n\n info.project.close = () => {\n if (closed) {\n originalClose();\n return;\n }\n\n closed = true;\n serviceEntry.referenceCount -= 1;\n if (serviceEntry.referenceCount <= 0) {\n services.delete(workspaceRoot);\n void serviceEntry.service.stop().catch((error: unknown) => {\n info.project.projectService.logger.info(\n `[FormSpec] Failed to stop plugin service for ${workspaceRoot}: ${formatPluginError(error)}`\n );\n });\n }\n originalClose();\n };\n}\n\n/**\n * Initializes the FormSpec TypeScript language service plugin.\n *\n * @public\n */\nexport function init(modules: {\n readonly typescript: typeof tsServer;\n}): tsServer.server.PluginModule {\n const typescriptVersion = modules.typescript.version;\n return {\n create(info) {\n const service = getOrCreateService(info, typescriptVersion);\n return createLanguageServiceProxy(info.languageService, service.getSemanticService());\n },\n };\n}\n","import fs from \"node:fs/promises\";\nimport net from \"node:net\";\nimport * as ts from \"typescript\";\nimport {\n FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n isFormSpecSemanticQuery,\n type FormSpecAnalysisManifest,\n type FormSpecSemanticQuery,\n type FormSpecSemanticResponse,\n} from \"@formspec/analysis/protocol\";\nimport { type FormSpecPerformanceEvent } from \"@formspec/analysis/internal\";\nimport {\n createFormSpecAnalysisManifest,\n getFormSpecWorkspaceRuntimePaths,\n type FormSpecWorkspaceRuntimePaths,\n} from \"./workspace.js\";\nimport {\n FormSpecSemanticService,\n type FormSpecSemanticServiceOptions,\n type LoggerLike,\n} from \"./semantic-service.js\";\nimport {\n FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS,\n FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES,\n FORM_SPEC_PLUGIN_SOCKET_IDLE_TIMEOUT_MS,\n} from \"./constants.js\";\nimport { formatPerformanceEvent } from \"./perf-utils.js\";\n\n/**\n * Public configuration for the reference plugin wrapper that exposes\n * `FormSpecSemanticService` over the local manifest + IPC transport.\n *\n * Supports the same semantic-service options, including\n * `enablePerformanceLogging`, `performanceLogThresholdMs`, and\n * `snapshotDebounceMs`. The packaged tsserver plugin wires these from\n * `FORMSPEC_PLUGIN_PROFILE=1` and `FORMSPEC_PLUGIN_PROFILE_THRESHOLD_MS`.\n *\n * @public\n */\nexport type FormSpecPluginServiceOptions = FormSpecSemanticServiceOptions;\n\n/**\n * Reference manifest/socket wrapper around `FormSpecSemanticService`.\n *\n * Downstream TypeScript hosts that already control their own plugin/runtime\n * lifecycle can use `FormSpecSemanticService` directly and skip this wrapper.\n *\n * @public\n */\nexport class FormSpecPluginService {\n private readonly manifest: FormSpecAnalysisManifest;\n private readonly runtimePaths: FormSpecWorkspaceRuntimePaths;\n private readonly semanticService: FormSpecSemanticService;\n private server: net.Server | null = null;\n\n public constructor(private readonly options: FormSpecPluginServiceOptions) {\n this.semanticService = new FormSpecSemanticService(options);\n this.runtimePaths = getFormSpecWorkspaceRuntimePaths(options.workspaceRoot);\n this.manifest = createFormSpecAnalysisManifest(\n options.workspaceRoot,\n options.typescriptVersion,\n Date.now()\n );\n }\n\n public getManifest(): FormSpecAnalysisManifest {\n return this.manifest;\n }\n\n /**\n * Returns the underlying semantic service used by this reference wrapper.\n *\n * @public\n */\n public getSemanticService(): FormSpecSemanticService {\n return this.semanticService;\n }\n\n public async start(): Promise<void> {\n if (this.server !== null) {\n return;\n }\n\n await fs.mkdir(this.runtimePaths.runtimeDirectory, { recursive: true });\n if (this.runtimePaths.endpoint.kind === \"unix-socket\") {\n await fs.rm(this.runtimePaths.endpoint.address, { force: true });\n }\n\n this.server = net.createServer((socket) => {\n let buffer = \"\";\n socket.setEncoding(\"utf8\");\n socket.setTimeout(FORM_SPEC_PLUGIN_SOCKET_IDLE_TIMEOUT_MS, () => {\n this.options.logger?.info(\n `[FormSpec] Closing idle semantic query socket for ${this.runtimePaths.workspaceRoot}`\n );\n socket.destroy();\n });\n socket.on(\"data\", (chunk) => {\n buffer += String(chunk);\n if (buffer.length > FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES) {\n socket.end(\n `${JSON.stringify({\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n kind: \"error\",\n error: `FormSpec semantic query exceeded ${String(FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES)} bytes`,\n } satisfies FormSpecSemanticResponse)}\\n`\n );\n return;\n }\n\n const newlineIndex = buffer.indexOf(\"\\n\");\n if (newlineIndex < 0) {\n return;\n }\n\n const payload = buffer.slice(0, newlineIndex);\n const remaining = buffer.slice(newlineIndex + 1);\n if (remaining.trim().length > 0) {\n this.options.logger?.info(\n `[FormSpec] Ignoring extra semantic query payload data for ${this.runtimePaths.workspaceRoot}`\n );\n }\n buffer = remaining;\n // The FormSpec IPC transport is intentionally one-request-per-connection.\n this.respondToSocket(socket, payload);\n });\n });\n\n await new Promise<void>((resolve, reject) => {\n const handleError = (error: Error) => {\n reject(error);\n };\n this.server?.once(\"error\", handleError);\n this.server?.listen(this.runtimePaths.endpoint.address, () => {\n this.server?.off(\"error\", handleError);\n resolve();\n });\n });\n\n await this.writeManifest();\n }\n\n public async stop(): Promise<void> {\n this.semanticService.dispose();\n\n const server = this.server;\n this.server = null;\n if (server?.listening === true) {\n await new Promise<void>((resolve, reject) => {\n server.close((error) => {\n if (error === undefined) {\n resolve();\n return;\n }\n reject(error);\n });\n });\n }\n\n await this.cleanupRuntimeArtifacts();\n }\n\n public scheduleSnapshotRefresh(filePath: string): void {\n this.semanticService.scheduleSnapshotRefresh(filePath);\n }\n\n public handleQuery(query: FormSpecSemanticQuery): FormSpecSemanticResponse {\n if (this.options.enablePerformanceLogging === true) {\n const startedAt = performance.now();\n const response = this.executeQuery(query);\n this.logQueryDuration(query, performance.now() - startedAt);\n return response;\n }\n\n return this.executeQuery(query);\n }\n\n private executeQuery(query: FormSpecSemanticQuery): FormSpecSemanticResponse {\n switch (query.kind) {\n case \"health\":\n return {\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n kind: \"health\",\n manifest: this.manifest,\n };\n case \"completion\": {\n const result = this.semanticService.getCompletionContext(query.filePath, query.offset);\n if (result === null) {\n return {\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n kind: \"error\",\n error: `Unable to resolve TypeScript source file for ${query.filePath}`,\n };\n }\n\n return {\n ...result,\n kind: \"completion\",\n };\n }\n case \"hover\": {\n const result = this.semanticService.getHover(query.filePath, query.offset);\n if (result === null) {\n return {\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n kind: \"error\",\n error: `Unable to resolve TypeScript source file for ${query.filePath}`,\n };\n }\n\n return {\n ...result,\n kind: \"hover\",\n };\n }\n case \"diagnostics\": {\n const result = this.semanticService.getDiagnostics(query.filePath);\n return {\n ...result,\n kind: \"diagnostics\",\n };\n }\n case \"file-snapshot\":\n return {\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n kind: \"file-snapshot\",\n snapshot: this.semanticService.getFileSnapshot(query.filePath),\n };\n default: {\n throw new Error(`Unhandled semantic query: ${JSON.stringify(query)}`);\n }\n }\n }\n\n private respondToSocket(socket: net.Socket, payload: string): void {\n try {\n const query = JSON.parse(payload) as unknown;\n if (!isFormSpecSemanticQuery(query)) {\n throw new Error(\"Invalid FormSpec semantic query payload\");\n }\n const response = this.handleQuery(query);\n socket.end(`${JSON.stringify(response)}\\n`);\n } catch (error) {\n socket.end(\n `${JSON.stringify({\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n kind: \"error\",\n error: error instanceof Error ? error.message : String(error),\n } satisfies FormSpecSemanticResponse)}\\n`\n );\n }\n }\n\n private async writeManifest(): Promise<void> {\n const tempManifestPath = `${this.runtimePaths.manifestPath}.tmp`;\n await fs.writeFile(tempManifestPath, `${JSON.stringify(this.manifest, null, 2)}\\n`, \"utf8\");\n await fs.rename(tempManifestPath, this.runtimePaths.manifestPath);\n }\n\n private async cleanupRuntimeArtifacts(): Promise<void> {\n await fs.rm(this.runtimePaths.manifestPath, { force: true });\n\n if (this.runtimePaths.endpoint.kind === \"unix-socket\") {\n await fs.rm(this.runtimePaths.endpoint.address, { force: true });\n }\n }\n\n private logQueryDuration(query: FormSpecSemanticQuery, durationMs: number): void {\n const logger = this.options.logger;\n if (logger === undefined) {\n return;\n }\n\n const thresholdMs =\n this.options.performanceLogThresholdMs ??\n FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS;\n if (durationMs < thresholdMs) {\n return;\n }\n\n const event: FormSpecPerformanceEvent = {\n name: \"plugin.handleQuery\",\n durationMs,\n detail: {\n kind: query.kind,\n ...(query.kind === \"health\" ? {} : { filePath: query.filePath }),\n },\n };\n logger.info(`[FormSpec][perf] ${formatPerformanceEvent(event)}`);\n }\n}\n\n/**\n * Reference proxy wrapper that keeps FormSpec semantic snapshots fresh while\n * delegating actual TypeScript editor features to the original service.\n *\n * @public\n */\nexport function createLanguageServiceProxy(\n languageService: ts.LanguageService,\n semanticService: FormSpecSemanticService\n): ts.LanguageService {\n const wrapWithSnapshotRefresh = <Args extends readonly unknown[], Result>(\n fn: (fileName: string, ...args: Args) => Result\n ) => {\n return (fileName: string, ...args: Args): Result => {\n semanticService.scheduleSnapshotRefresh(fileName);\n return fn(fileName, ...args);\n };\n };\n\n const getSemanticDiagnostics = wrapWithSnapshotRefresh((fileName) =>\n languageService.getSemanticDiagnostics(fileName)\n );\n\n const getCompletionsAtPosition = wrapWithSnapshotRefresh(\n (fileName: string, position: number, options: ts.GetCompletionsAtPositionOptions | undefined) =>\n languageService.getCompletionsAtPosition(fileName, position, options)\n );\n\n const getQuickInfoAtPosition = wrapWithSnapshotRefresh((fileName, position: number) =>\n languageService.getQuickInfoAtPosition(fileName, position)\n );\n\n return new Proxy(languageService, {\n get(target, property, receiver) {\n switch (property) {\n case \"getSemanticDiagnostics\":\n return getSemanticDiagnostics;\n case \"getCompletionsAtPosition\":\n return getCompletionsAtPosition;\n case \"getQuickInfoAtPosition\":\n return getQuickInfoAtPosition;\n default:\n return Reflect.get(target, property, receiver) as unknown;\n }\n },\n });\n}\n\nexport type { LoggerLike };\n","import os from \"node:os\";\nimport path from \"node:path\";\nimport {\n FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n FORMSPEC_ANALYSIS_SCHEMA_VERSION,\n type FormSpecAnalysisManifest,\n type FormSpecIpcEndpoint,\n} from \"@formspec/analysis/protocol\";\nimport {\n getFormSpecManifestPath,\n getFormSpecWorkspaceId,\n getFormSpecWorkspaceRuntimeDirectory,\n} from \"@formspec/analysis/internal\";\n\nexport interface FormSpecWorkspaceRuntimePaths {\n readonly workspaceRoot: string;\n readonly workspaceId: string;\n readonly runtimeDirectory: string;\n readonly manifestPath: string;\n readonly endpoint: FormSpecIpcEndpoint;\n}\n\nexport function getFormSpecWorkspaceRuntimePaths(\n workspaceRoot: string,\n platform = process.platform,\n userScope = getFormSpecUserScope()\n): FormSpecWorkspaceRuntimePaths {\n const workspaceId = getFormSpecWorkspaceId(workspaceRoot);\n const runtimeDirectory = getFormSpecWorkspaceRuntimeDirectory(workspaceRoot);\n const sanitizedUserScope = sanitizeScopeSegment(userScope);\n const endpoint: FormSpecIpcEndpoint =\n platform === \"win32\"\n ? {\n kind: \"windows-pipe\",\n address: `\\\\\\\\.\\\\pipe\\\\formspec-${sanitizedUserScope}-${workspaceId}`,\n }\n : {\n kind: \"unix-socket\",\n address: path.join(os.tmpdir(), `formspec-${sanitizedUserScope}-${workspaceId}.sock`),\n };\n\n return {\n workspaceRoot,\n workspaceId,\n runtimeDirectory,\n manifestPath: getFormSpecManifestPath(workspaceRoot),\n endpoint,\n };\n}\n\nexport function createFormSpecAnalysisManifest(\n workspaceRoot: string,\n typescriptVersion: string,\n generation: number,\n extensionFingerprint = \"builtin\"\n): FormSpecAnalysisManifest {\n const paths = getFormSpecWorkspaceRuntimePaths(workspaceRoot);\n return {\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n analysisSchemaVersion: FORMSPEC_ANALYSIS_SCHEMA_VERSION,\n workspaceRoot,\n workspaceId: paths.workspaceId,\n endpoint: paths.endpoint,\n typescriptVersion,\n extensionFingerprint,\n generation,\n updatedAt: new Date().toISOString(),\n };\n}\n\nfunction getFormSpecUserScope(): string {\n try {\n return sanitizeScopeSegment(os.userInfo().username);\n } catch {\n return sanitizeScopeSegment(\n process.env[\"USER\"] ?? process.env[\"USERNAME\"] ?? process.env[\"LOGNAME\"] ?? \"formspec\"\n );\n }\n}\n\nfunction sanitizeScopeSegment(value: string): string {\n const trimmed = value.trim().toLowerCase();\n const sanitized = trimmed.replace(/[^a-z0-9_-]+/gu, \"-\").replace(/-+/gu, \"-\");\n return sanitized.length > 0 ? sanitized : \"formspec\";\n}\n","import * as ts from \"typescript\";\nimport {\n FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n computeFormSpecTextHash,\n type FormSpecAnalysisDiagnostic,\n type FormSpecAnalysisFileSnapshot,\n type FormSpecSerializedCompletionContext,\n type FormSpecSerializedHoverInfo,\n} from \"@formspec/analysis/protocol\";\nimport {\n buildFormSpecAnalysisFileSnapshot,\n createFormSpecPerformanceRecorder,\n findDeclarationForCommentOffset,\n getCommentHoverInfoAtOffset,\n getSemanticCommentCompletionContextAtOffset,\n getFormSpecPerformanceNow,\n getSubjectType,\n optionalMeasure,\n resolveDeclarationPlacement,\n serializeCompletionContext,\n serializeHoverInfo,\n type BuildFormSpecAnalysisFileSnapshotOptions,\n type FormSpecPerformanceEvent,\n type FormSpecPerformanceRecorder,\n} from \"@formspec/analysis/internal\";\nimport {\n FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS,\n FORM_SPEC_PLUGIN_DEFAULT_SNAPSHOT_DEBOUNCE_MS,\n} from \"./constants.js\";\nimport { formatPerformanceEvent } from \"./perf-utils.js\";\n\n/** @public */\nexport interface LoggerLike {\n info(message: string): void;\n}\n\n/** @public */\nexport interface FormSpecSemanticServiceOptions {\n /** Workspace root used for runtime paths and contextual logging. */\n readonly workspaceRoot: string;\n /** TypeScript version string reported by the host runtime. */\n readonly typescriptVersion: string;\n /** Supplies the current host program. Returns `undefined` until ready. */\n readonly getProgram: () => ts.Program | undefined;\n /** Optional logger used for profiling and refresh-failure messages. */\n readonly logger?: LoggerLike;\n /** Enables structured hotspot logging for semantic queries. */\n readonly enablePerformanceLogging?: boolean;\n /** Minimum query duration, in milliseconds, required before logging. */\n readonly performanceLogThresholdMs?: number;\n /** Debounce window, in milliseconds, for background snapshot refresh. */\n readonly snapshotDebounceMs?: number;\n /** Injectable clock used by tests and runtime snapshot timestamps. */\n readonly now?: () => Date;\n}\n\n/** @public */\nexport interface FormSpecSemanticCompletionResult {\n readonly protocolVersion: typeof FORMSPEC_ANALYSIS_PROTOCOL_VERSION;\n readonly sourceHash: string;\n readonly context: FormSpecSerializedCompletionContext;\n}\n\n/** @public */\nexport interface FormSpecSemanticHoverResult {\n readonly protocolVersion: typeof FORMSPEC_ANALYSIS_PROTOCOL_VERSION;\n readonly sourceHash: string;\n readonly hover: FormSpecSerializedHoverInfo | null;\n}\n\n/** @public */\nexport interface FormSpecSemanticDiagnosticsResult {\n readonly protocolVersion: typeof FORMSPEC_ANALYSIS_PROTOCOL_VERSION;\n readonly sourceHash: string;\n readonly diagnostics: readonly FormSpecAnalysisDiagnostic[];\n}\n\n/** @public */\nexport interface FormSpecSemanticServiceStats {\n /** Total number of calls by semantic query kind. */\n readonly queryTotals: {\n readonly completion: number;\n readonly hover: number;\n readonly diagnostics: number;\n readonly fileSnapshot: number;\n };\n /** Cold vs warm query path counts for snapshot-backed operations. */\n readonly queryPathTotals: {\n readonly diagnostics: { readonly cold: number; readonly warm: number };\n readonly fileSnapshot: { readonly cold: number; readonly warm: number };\n };\n /** Number of file snapshot cache hits. */\n readonly fileSnapshotCacheHits: number;\n /** Number of file snapshot cache misses. */\n readonly fileSnapshotCacheMisses: number;\n /** Number of synthetic batch cache hits. */\n readonly syntheticBatchCacheHits: number;\n /** Number of synthetic batch cache misses. */\n readonly syntheticBatchCacheMisses: number;\n /** Number of synthetic compiler program creations. */\n readonly syntheticCompileCount: number;\n /** Total synthetic application count compiled across misses. */\n readonly syntheticCompileApplications: number;\n}\n\ninterface CachedFileSnapshot {\n readonly sourceHash: string;\n readonly snapshot: FormSpecAnalysisFileSnapshot;\n}\n\ninterface SourceEnvironment {\n readonly sourceFile: ts.SourceFile;\n readonly checker: ts.TypeChecker;\n readonly sourceHash: string;\n}\n\ninterface CommentQueryContext extends SourceEnvironment {\n readonly declaration: ts.Node | null;\n readonly placement: ReturnType<typeof resolveDeclarationPlacement>;\n readonly subjectType: ts.Type | undefined;\n}\n\ntype SnapshotCacheState = \"hit\" | \"miss\" | \"missing-source\";\n\ninterface MutableSemanticServiceStats {\n queryTotals: {\n completion: number;\n hover: number;\n diagnostics: number;\n fileSnapshot: number;\n };\n queryPathTotals: {\n diagnostics: { cold: number; warm: number };\n fileSnapshot: { cold: number; warm: number };\n };\n fileSnapshotCacheHits: number;\n fileSnapshotCacheMisses: number;\n syntheticBatchCacheHits: number;\n syntheticBatchCacheMisses: number;\n syntheticCompileCount: number;\n syntheticCompileApplications: number;\n}\n\nconst STATS_ONLY_EVENT_NAMES = new Set<string>([\n \"analysis.syntheticCheckBatch.cacheHit\",\n \"analysis.narrowSyntheticCheckBatch.cacheHit\",\n \"analysis.syntheticCheckBatch.cacheMiss\",\n \"analysis.narrowSyntheticCheckBatch.cacheMiss\",\n \"analysis.syntheticCheckBatch.createProgram\",\n \"analysis.narrowSyntheticCheckBatch.createProgram\",\n]);\n\nclass StatsOnlyPerformanceRecorder implements FormSpecPerformanceRecorder {\n private readonly mutableEvents: FormSpecPerformanceEvent[] = [];\n\n public get events(): readonly FormSpecPerformanceEvent[] {\n return this.mutableEvents;\n }\n\n public measure<T>(\n name: string,\n detail: Readonly<Record<string, string | number | boolean>> | undefined,\n callback: () => T\n ): T {\n const result = callback();\n if (STATS_ONLY_EVENT_NAMES.has(name)) {\n this.mutableEvents.push({\n name,\n durationMs: 0,\n ...(detail === undefined ? {} : { detail }),\n });\n }\n return result;\n }\n\n public record(event: FormSpecPerformanceEvent): void {\n if (STATS_ONLY_EVENT_NAMES.has(event.name)) {\n this.mutableEvents.push(event);\n }\n }\n}\n\n/**\n * Reusable in-process semantic service for FormSpec authoring features.\n *\n * Downstream TypeScript hosts can construct this directly against their own\n * `Program` and own the final presentation of completions, hover, and\n * diagnostics. The shipped tsserver plugin is a reference wrapper over this\n * public service.\n *\n * @public\n */\nexport class FormSpecSemanticService {\n private readonly snapshotCache = new Map<string, CachedFileSnapshot>();\n private readonly refreshTimers = new Map<string, NodeJS.Timeout>();\n private readonly stats: MutableSemanticServiceStats = {\n queryTotals: {\n completion: 0,\n hover: 0,\n diagnostics: 0,\n fileSnapshot: 0,\n },\n queryPathTotals: {\n diagnostics: { cold: 0, warm: 0 },\n fileSnapshot: { cold: 0, warm: 0 },\n },\n fileSnapshotCacheHits: 0,\n fileSnapshotCacheMisses: 0,\n syntheticBatchCacheHits: 0,\n syntheticBatchCacheMisses: 0,\n syntheticCompileCount: 0,\n syntheticCompileApplications: 0,\n };\n\n public constructor(private readonly options: FormSpecSemanticServiceOptions) {}\n\n /** Resolves semantic completion context for a comment cursor position. */\n public getCompletionContext(\n filePath: string,\n offset: number\n ): FormSpecSemanticCompletionResult | null {\n this.stats.queryTotals.completion += 1;\n return this.runMeasured(\"semantic.getCompletionContext\", { filePath, offset }, (performance) =>\n this.withCommentQueryContext(filePath, offset, performance, (context) => ({\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n sourceHash: context.sourceHash,\n context: serializeCompletionContext(\n getSemanticCommentCompletionContextAtOffset(context.sourceFile.text, offset, {\n checker: context.checker,\n ...(context.placement === null ? {} : { placement: context.placement }),\n ...(context.subjectType === undefined ? {} : { subjectType: context.subjectType }),\n })\n ),\n }))\n );\n }\n\n /** Resolves semantic hover payload for a comment cursor position. */\n public getHover(filePath: string, offset: number): FormSpecSemanticHoverResult | null {\n this.stats.queryTotals.hover += 1;\n return this.runMeasured(\"semantic.getHover\", { filePath, offset }, (performance) =>\n this.withCommentQueryContext(filePath, offset, performance, (context) => ({\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n sourceHash: context.sourceHash,\n hover: serializeHoverInfo(\n getCommentHoverInfoAtOffset(context.sourceFile.text, offset, {\n checker: context.checker,\n ...(context.placement === null ? {} : { placement: context.placement }),\n ...(context.subjectType === undefined ? {} : { subjectType: context.subjectType }),\n })\n ),\n }))\n );\n }\n\n /** Returns canonical FormSpec diagnostics for a file in the current host program. */\n public getDiagnostics(filePath: string): FormSpecSemanticDiagnosticsResult {\n this.stats.queryTotals.diagnostics += 1;\n return this.runMeasured(\"semantic.getDiagnostics\", { filePath }, (performance) => {\n const { snapshot, cacheState } = this.getFileSnapshotWithCacheState(filePath, performance);\n this.recordQueryPath(\"diagnostics\", cacheState);\n return {\n protocolVersion: FORMSPEC_ANALYSIS_PROTOCOL_VERSION,\n sourceHash: snapshot.sourceHash,\n diagnostics: snapshot.diagnostics,\n };\n });\n }\n\n /** Returns the full serialized semantic snapshot for a file. */\n public getFileSnapshot(filePath: string): FormSpecAnalysisFileSnapshot {\n this.stats.queryTotals.fileSnapshot += 1;\n return this.runMeasured(\"semantic.getFileSnapshot\", { filePath }, (performance) => {\n const { snapshot, cacheState } = this.getFileSnapshotWithCacheState(filePath, performance);\n this.recordQueryPath(\"fileSnapshot\", cacheState);\n return snapshot;\n });\n }\n\n /** Schedules a debounced background refresh for the file snapshot cache. */\n public scheduleSnapshotRefresh(filePath: string): void {\n const existing = this.refreshTimers.get(filePath);\n if (existing !== undefined) {\n clearTimeout(existing);\n }\n\n const timer = setTimeout(() => {\n try {\n this.getFileSnapshot(filePath);\n } catch (error: unknown) {\n this.options.logger?.info(\n `[FormSpec] Failed to refresh semantic snapshot for ${filePath}: ${String(error)}`\n );\n }\n this.refreshTimers.delete(filePath);\n }, this.options.snapshotDebounceMs ?? FORM_SPEC_PLUGIN_DEFAULT_SNAPSHOT_DEBOUNCE_MS);\n timer.unref();\n\n this.refreshTimers.set(filePath, timer);\n }\n\n /** Clears pending timers and cached semantic snapshots. */\n public dispose(): void {\n for (const timer of this.refreshTimers.values()) {\n clearTimeout(timer);\n }\n this.refreshTimers.clear();\n this.snapshotCache.clear();\n }\n\n /** Returns a copy of the current performance and cache counters. */\n public getStats(): FormSpecSemanticServiceStats {\n return {\n queryTotals: { ...this.stats.queryTotals },\n queryPathTotals: {\n diagnostics: { ...this.stats.queryPathTotals.diagnostics },\n fileSnapshot: { ...this.stats.queryPathTotals.fileSnapshot },\n },\n fileSnapshotCacheHits: this.stats.fileSnapshotCacheHits,\n fileSnapshotCacheMisses: this.stats.fileSnapshotCacheMisses,\n syntheticBatchCacheHits: this.stats.syntheticBatchCacheHits,\n syntheticBatchCacheMisses: this.stats.syntheticBatchCacheMisses,\n syntheticCompileCount: this.stats.syntheticCompileCount,\n syntheticCompileApplications: this.stats.syntheticCompileApplications,\n };\n }\n\n private runMeasured<T>(\n name: string,\n detail: Record<string, string | number>,\n fn: (performance: FormSpecPerformanceRecorder) => T\n ): T {\n const performance =\n this.options.enablePerformanceLogging === true\n ? createFormSpecPerformanceRecorder()\n : new StatsOnlyPerformanceRecorder();\n const result = optionalMeasure(performance, name, detail, () => fn(performance));\n this.updateStatsFromPerformanceEvents(performance.events);\n if (this.options.enablePerformanceLogging === true) {\n this.logPerformanceEvents(name, performance.events);\n }\n return result;\n }\n\n private withCommentQueryContext<T>(\n filePath: string,\n offset: number,\n performance: FormSpecPerformanceRecorder,\n handler: (context: CommentQueryContext) => T\n ): T | null {\n return optionalMeasure(\n performance,\n \"semantic.resolveCommentQueryContext\",\n {\n filePath,\n offset,\n },\n () => {\n const environment = this.getSourceEnvironment(filePath, performance);\n if (environment === null) {\n return null;\n }\n\n const declaration = optionalMeasure(\n performance,\n \"semantic.findDeclarationForCommentOffset\",\n {\n filePath,\n offset,\n },\n () => findDeclarationForCommentOffset(environment.sourceFile, offset)\n );\n const placement =\n declaration === null\n ? null\n : optionalMeasure(performance, \"semantic.resolveDeclarationPlacement\", undefined, () =>\n resolveDeclarationPlacement(declaration)\n );\n const subjectType =\n declaration === null\n ? undefined\n : optionalMeasure(performance, \"semantic.getSubjectType\", undefined, () =>\n getSubjectType(declaration, environment.checker)\n );\n\n return handler({\n ...environment,\n declaration,\n placement,\n subjectType,\n });\n }\n );\n }\n\n private getFileSnapshotWithCacheState(\n filePath: string,\n performance: FormSpecPerformanceRecorder\n ): {\n readonly snapshot: FormSpecAnalysisFileSnapshot;\n readonly cacheState: SnapshotCacheState;\n } {\n const startedAt = getFormSpecPerformanceNow();\n const environment = this.getSourceEnvironment(filePath, performance);\n if (environment === null) {\n this.stats.fileSnapshotCacheMisses += 1;\n const snapshot: FormSpecAnalysisFileSnapshot = {\n filePath,\n sourceHash: \"\",\n generatedAt: this.getNow().toISOString(),\n comments: [],\n diagnostics: [\n {\n code: \"MISSING_SOURCE_FILE\",\n category: \"infrastructure\",\n message: `Unable to resolve TypeScript source file for ${filePath}`,\n range: { start: 0, end: 0 },\n severity: \"warning\",\n relatedLocations: [],\n data: {\n filePath,\n },\n },\n ],\n };\n performance.record({\n name: \"semantic.getFileSnapshot.result\",\n durationMs: getFormSpecPerformanceNow() - startedAt,\n detail: {\n filePath,\n cache: \"missing-source\",\n },\n });\n return {\n snapshot,\n cacheState: \"missing-source\",\n };\n }\n\n const cached = this.snapshotCache.get(filePath);\n if (cached?.sourceHash === environment.sourceHash) {\n this.stats.fileSnapshotCacheHits += 1;\n performance.record({\n name: \"semantic.getFileSnapshot.result\",\n durationMs: getFormSpecPerformanceNow() - startedAt,\n detail: {\n filePath,\n cache: \"hit\",\n },\n });\n return {\n snapshot: cached.snapshot,\n cacheState: \"hit\",\n };\n }\n\n this.stats.fileSnapshotCacheMisses += 1;\n const snapshot = buildFormSpecAnalysisFileSnapshot(environment.sourceFile, {\n checker: environment.checker,\n now: () => this.getNow(),\n performance,\n } satisfies BuildFormSpecAnalysisFileSnapshotOptions);\n this.snapshotCache.set(filePath, {\n sourceHash: environment.sourceHash,\n snapshot,\n });\n performance.record({\n name: \"semantic.getFileSnapshot.result\",\n durationMs: getFormSpecPerformanceNow() - startedAt,\n detail: {\n filePath,\n cache: \"miss\",\n },\n });\n return {\n snapshot,\n cacheState: \"miss\",\n };\n }\n\n private getNow(): Date {\n return this.options.now?.() ?? new Date();\n }\n\n private getSourceEnvironment(\n filePath: string,\n performance: FormSpecPerformanceRecorder\n ): SourceEnvironment | null {\n return optionalMeasure(\n performance,\n \"semantic.getSourceEnvironment\",\n {\n filePath,\n },\n () => {\n const program = optionalMeasure(\n performance,\n \"semantic.sourceEnvironment.getProgram\",\n undefined,\n () => this.options.getProgram()\n );\n if (program === undefined) {\n return null;\n }\n\n const sourceFile = optionalMeasure(\n performance,\n \"semantic.sourceEnvironment.getSourceFile\",\n undefined,\n () => program.getSourceFile(filePath)\n );\n if (sourceFile === undefined) {\n return null;\n }\n\n const checker = optionalMeasure(\n performance,\n \"semantic.sourceEnvironment.getTypeChecker\",\n undefined,\n () => program.getTypeChecker()\n );\n const sourceHash = optionalMeasure(\n performance,\n \"semantic.sourceEnvironment.computeTextHash\",\n undefined,\n () => computeFormSpecTextHash(sourceFile.text)\n );\n\n return {\n sourceFile,\n checker,\n sourceHash,\n };\n }\n );\n }\n\n private recordQueryPath(\n kind: \"diagnostics\" | \"fileSnapshot\",\n cacheState: SnapshotCacheState\n ): void {\n if (cacheState === \"hit\") {\n this.stats.queryPathTotals[kind].warm += 1;\n return;\n }\n\n this.stats.queryPathTotals[kind].cold += 1;\n }\n\n private updateStatsFromPerformanceEvents(events: readonly FormSpecPerformanceEvent[]): void {\n for (const event of events) {\n if (\n event.name === \"analysis.syntheticCheckBatch.cacheHit\" ||\n event.name === \"analysis.narrowSyntheticCheckBatch.cacheHit\"\n ) {\n this.stats.syntheticBatchCacheHits += 1;\n continue;\n }\n\n if (\n event.name === \"analysis.syntheticCheckBatch.cacheMiss\" ||\n event.name === \"analysis.narrowSyntheticCheckBatch.cacheMiss\"\n ) {\n this.stats.syntheticBatchCacheMisses += 1;\n const applicationCount = event.detail?.[\"applicationCount\"];\n if (typeof applicationCount === \"number\") {\n this.stats.syntheticCompileApplications += applicationCount;\n }\n continue;\n }\n\n if (\n event.name === \"analysis.syntheticCheckBatch.createProgram\" ||\n event.name === \"analysis.narrowSyntheticCheckBatch.createProgram\"\n ) {\n this.stats.syntheticCompileCount += 1;\n }\n }\n }\n\n private logPerformanceEvents(\n rootEventName: string,\n events: readonly FormSpecPerformanceEvent[]\n ): void {\n const logger = this.options.logger;\n if (logger === undefined || events.length === 0) {\n return;\n }\n\n const rootEvent = [...events].reverse().find((event) => event.name === rootEventName);\n if (rootEvent === undefined) {\n return;\n }\n\n const thresholdMs =\n this.options.performanceLogThresholdMs ??\n FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS;\n if (rootEvent.durationMs < thresholdMs) {\n return;\n }\n\n const sortedHotspots = [...events]\n .filter((event) => event.name !== rootEventName)\n .sort((left, right) => right.durationMs - left.durationMs)\n .slice(0, 8);\n const lines = [\n `[FormSpec][perf] ${formatPerformanceEvent(rootEvent)}`,\n ...sortedHotspots.map((event) => ` ${formatPerformanceEvent(event)}`),\n ];\n logger.info(lines.join(\"\\n\"));\n }\n}\n","export const FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES = 256 * 1024;\nexport const FORM_SPEC_PLUGIN_SOCKET_IDLE_TIMEOUT_MS = 30_000;\nexport const FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS = 50;\nexport const FORM_SPEC_PLUGIN_DEFAULT_SNAPSHOT_DEBOUNCE_MS = 250;\n\nexport const FORM_SPEC_PLUGIN_PERFORMANCE_EVENT = {\n handleQuery: \"plugin.handleQuery\",\n} as const;\n","import type { FormSpecPerformanceEvent } from \"@formspec/analysis/internal\";\n\nexport function formatPerformanceEvent(event: FormSpecPerformanceEvent): string {\n const detailEntries = Object.entries(event.detail ?? {})\n .map(([key, value]) => `${key}=${String(value)}`)\n .join(\" \");\n return `${event.durationMs.toFixed(1)}ms ${event.name}${detailEntries === \"\" ? \"\" : ` ${detailEntries}`}`;\n}\n"],"mappings":"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","names":["import_protocol","ts","import_protocol","import_internal","path","os","import_protocol","import_internal","performance","snapshot","fs","net"]}
package/dist/index.js CHANGED
@@ -19,11 +19,13 @@ import os from "os";
19
19
  import path from "path";
20
20
  import {
21
21
  FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
22
- FORMSPEC_ANALYSIS_SCHEMA_VERSION,
22
+ FORMSPEC_ANALYSIS_SCHEMA_VERSION
23
+ } from "@formspec/analysis/protocol";
24
+ import {
23
25
  getFormSpecManifestPath,
24
26
  getFormSpecWorkspaceId,
25
27
  getFormSpecWorkspaceRuntimeDirectory
26
- } from "@formspec/analysis/protocol";
28
+ } from "@formspec/analysis/internal";
27
29
  function getFormSpecWorkspaceRuntimePaths(workspaceRoot, platform = process.platform, userScope = getFormSpecUserScope()) {
28
30
  const workspaceId = getFormSpecWorkspaceId(workspaceRoot);
29
31
  const runtimeDirectory = getFormSpecWorkspaceRuntimeDirectory(workspaceRoot);