@formspec/ts-plugin 0.1.0-alpha.21 → 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/README.md +56 -0
- package/dist/__tests__/downstream-authoring-host.test.d.ts +2 -0
- package/dist/__tests__/downstream-authoring-host.test.d.ts.map +1 -0
- package/dist/__tests__/semantic-service.test.d.ts +2 -0
- package/dist/__tests__/semantic-service.test.d.ts.map +1 -0
- package/dist/index.cjs +486 -277
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +483 -271
- package/dist/index.js.map +1 -1
- package/dist/perf-utils.d.ts +3 -0
- package/dist/perf-utils.d.ts.map +1 -0
- package/dist/reference-host-example.d.ts +14 -0
- package/dist/reference-host-example.d.ts.map +1 -0
- package/dist/semantic-service.d.ts +116 -0
- package/dist/semantic-service.d.ts.map +1 -0
- package/dist/service.d.ts +38 -34
- package/dist/service.d.ts.map +1 -1
- package/dist/ts-plugin-alpha.d.ts +470 -0
- package/dist/ts-plugin-beta.d.ts +470 -0
- package/dist/ts-plugin-internal.d.ts +470 -0
- package/dist/ts-plugin.d.ts +458 -0
- package/dist/workspace.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/index.cjs
CHANGED
|
@@ -30,24 +30,31 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
|
|
|
30
30
|
// src/index.ts
|
|
31
31
|
var index_exports = {};
|
|
32
32
|
__export(index_exports, {
|
|
33
|
+
FORMSPEC_ANALYSIS_PROTOCOL_VERSION: () => import_protocol4.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
34
|
+
FORMSPEC_ANALYSIS_SCHEMA_VERSION: () => import_protocol4.FORMSPEC_ANALYSIS_SCHEMA_VERSION,
|
|
35
|
+
FormSpecPluginService: () => FormSpecPluginService,
|
|
36
|
+
FormSpecSemanticService: () => FormSpecSemanticService,
|
|
37
|
+
createLanguageServiceProxy: () => createLanguageServiceProxy,
|
|
33
38
|
init: () => init
|
|
34
39
|
});
|
|
35
40
|
module.exports = __toCommonJS(index_exports);
|
|
41
|
+
var import_protocol4 = require("@formspec/analysis/protocol");
|
|
36
42
|
|
|
37
43
|
// src/service.ts
|
|
38
44
|
var import_promises = __toESM(require("fs/promises"), 1);
|
|
39
45
|
var import_node_net = __toESM(require("net"), 1);
|
|
40
|
-
var
|
|
41
|
-
var
|
|
42
|
-
var
|
|
46
|
+
var ts2 = require("typescript");
|
|
47
|
+
var import_protocol3 = require("@formspec/analysis/protocol");
|
|
48
|
+
var import_internal3 = require("@formspec/analysis/internal");
|
|
43
49
|
|
|
44
50
|
// src/workspace.ts
|
|
45
51
|
var import_node_os = __toESM(require("os"), 1);
|
|
46
52
|
var import_node_path = __toESM(require("path"), 1);
|
|
47
53
|
var import_protocol = require("@formspec/analysis/protocol");
|
|
54
|
+
var import_internal = require("@formspec/analysis/internal");
|
|
48
55
|
function getFormSpecWorkspaceRuntimePaths(workspaceRoot, platform = process.platform, userScope = getFormSpecUserScope()) {
|
|
49
|
-
const workspaceId = (0,
|
|
50
|
-
const runtimeDirectory = (0,
|
|
56
|
+
const workspaceId = (0, import_internal.getFormSpecWorkspaceId)(workspaceRoot);
|
|
57
|
+
const runtimeDirectory = (0, import_internal.getFormSpecWorkspaceRuntimeDirectory)(workspaceRoot);
|
|
51
58
|
const sanitizedUserScope = sanitizeScopeSegment(userScope);
|
|
52
59
|
const endpoint = platform === "win32" ? {
|
|
53
60
|
kind: "windows-pipe",
|
|
@@ -60,7 +67,7 @@ function getFormSpecWorkspaceRuntimePaths(workspaceRoot, platform = process.plat
|
|
|
60
67
|
workspaceRoot,
|
|
61
68
|
workspaceId,
|
|
62
69
|
runtimeDirectory,
|
|
63
|
-
manifestPath: (0,
|
|
70
|
+
manifestPath: (0, import_internal.getFormSpecManifestPath)(workspaceRoot),
|
|
64
71
|
endpoint
|
|
65
72
|
};
|
|
66
73
|
}
|
|
@@ -93,112 +100,139 @@ function sanitizeScopeSegment(value) {
|
|
|
93
100
|
return sanitized.length > 0 ? sanitized : "formspec";
|
|
94
101
|
}
|
|
95
102
|
|
|
103
|
+
// src/semantic-service.ts
|
|
104
|
+
var ts = require("typescript");
|
|
105
|
+
var import_protocol2 = require("@formspec/analysis/protocol");
|
|
106
|
+
var import_internal2 = require("@formspec/analysis/internal");
|
|
107
|
+
|
|
96
108
|
// src/constants.ts
|
|
97
109
|
var FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES = 256 * 1024;
|
|
98
110
|
var FORM_SPEC_PLUGIN_SOCKET_IDLE_TIMEOUT_MS = 3e4;
|
|
99
111
|
var FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS = 50;
|
|
100
112
|
var FORM_SPEC_PLUGIN_DEFAULT_SNAPSHOT_DEBOUNCE_MS = 250;
|
|
101
|
-
var FORM_SPEC_PLUGIN_PERFORMANCE_EVENT = {
|
|
102
|
-
handleQuery: "plugin.handleQuery"
|
|
103
|
-
};
|
|
104
113
|
|
|
105
|
-
// src/
|
|
106
|
-
|
|
114
|
+
// src/perf-utils.ts
|
|
115
|
+
function formatPerformanceEvent(event) {
|
|
116
|
+
const detailEntries = Object.entries(event.detail ?? {}).map(([key, value]) => `${key}=${String(value)}`).join(" ");
|
|
117
|
+
return `${event.durationMs.toFixed(1)}ms ${event.name}${detailEntries === "" ? "" : ` ${detailEntries}`}`;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// src/semantic-service.ts
|
|
121
|
+
var STATS_ONLY_EVENT_NAMES = /* @__PURE__ */ new Set([
|
|
122
|
+
"analysis.syntheticCheckBatch.cacheHit",
|
|
123
|
+
"analysis.narrowSyntheticCheckBatch.cacheHit",
|
|
124
|
+
"analysis.syntheticCheckBatch.cacheMiss",
|
|
125
|
+
"analysis.narrowSyntheticCheckBatch.cacheMiss",
|
|
126
|
+
"analysis.syntheticCheckBatch.createProgram",
|
|
127
|
+
"analysis.narrowSyntheticCheckBatch.createProgram"
|
|
128
|
+
]);
|
|
129
|
+
var StatsOnlyPerformanceRecorder = class {
|
|
130
|
+
mutableEvents = [];
|
|
131
|
+
get events() {
|
|
132
|
+
return this.mutableEvents;
|
|
133
|
+
}
|
|
134
|
+
measure(name, detail, callback) {
|
|
135
|
+
const result = callback();
|
|
136
|
+
if (STATS_ONLY_EVENT_NAMES.has(name)) {
|
|
137
|
+
this.mutableEvents.push({
|
|
138
|
+
name,
|
|
139
|
+
durationMs: 0,
|
|
140
|
+
...detail === void 0 ? {} : { detail }
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
return result;
|
|
144
|
+
}
|
|
145
|
+
record(event) {
|
|
146
|
+
if (STATS_ONLY_EVENT_NAMES.has(event.name)) {
|
|
147
|
+
this.mutableEvents.push(event);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
var FormSpecSemanticService = class {
|
|
107
152
|
constructor(options) {
|
|
108
153
|
this.options = options;
|
|
109
|
-
this.runtimePaths = getFormSpecWorkspaceRuntimePaths(options.workspaceRoot);
|
|
110
|
-
this.manifest = createFormSpecAnalysisManifest(
|
|
111
|
-
options.workspaceRoot,
|
|
112
|
-
options.typescriptVersion,
|
|
113
|
-
Date.now()
|
|
114
|
-
);
|
|
115
154
|
}
|
|
116
|
-
manifest;
|
|
117
|
-
runtimePaths;
|
|
118
155
|
snapshotCache = /* @__PURE__ */ new Map();
|
|
119
156
|
refreshTimers = /* @__PURE__ */ new Map();
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
157
|
+
stats = {
|
|
158
|
+
queryTotals: {
|
|
159
|
+
completion: 0,
|
|
160
|
+
hover: 0,
|
|
161
|
+
diagnostics: 0,
|
|
162
|
+
fileSnapshot: 0
|
|
163
|
+
},
|
|
164
|
+
queryPathTotals: {
|
|
165
|
+
diagnostics: { cold: 0, warm: 0 },
|
|
166
|
+
fileSnapshot: { cold: 0, warm: 0 }
|
|
167
|
+
},
|
|
168
|
+
fileSnapshotCacheHits: 0,
|
|
169
|
+
fileSnapshotCacheMisses: 0,
|
|
170
|
+
syntheticBatchCacheHits: 0,
|
|
171
|
+
syntheticBatchCacheMisses: 0,
|
|
172
|
+
syntheticCompileCount: 0,
|
|
173
|
+
syntheticCompileApplications: 0
|
|
174
|
+
};
|
|
175
|
+
/** Resolves semantic completion context for a comment cursor position. */
|
|
176
|
+
getCompletionContext(filePath, offset) {
|
|
177
|
+
this.stats.queryTotals.completion += 1;
|
|
178
|
+
return this.runMeasured(
|
|
179
|
+
"semantic.getCompletionContext",
|
|
180
|
+
{ filePath, offset },
|
|
181
|
+
(performance2) => this.withCommentQueryContext(filePath, offset, performance2, (context) => ({
|
|
182
|
+
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
183
|
+
sourceHash: context.sourceHash,
|
|
184
|
+
context: (0, import_internal2.serializeCompletionContext)(
|
|
185
|
+
(0, import_internal2.getSemanticCommentCompletionContextAtOffset)(context.sourceFile.text, offset, {
|
|
186
|
+
checker: context.checker,
|
|
187
|
+
...context.placement === null ? {} : { placement: context.placement },
|
|
188
|
+
...context.subjectType === void 0 ? {} : { subjectType: context.subjectType }
|
|
189
|
+
})
|
|
190
|
+
)
|
|
191
|
+
}))
|
|
192
|
+
);
|
|
123
193
|
}
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
154
|
-
const newlineIndex = buffer.indexOf("\n");
|
|
155
|
-
if (newlineIndex < 0) {
|
|
156
|
-
return;
|
|
157
|
-
}
|
|
158
|
-
const payload = buffer.slice(0, newlineIndex);
|
|
159
|
-
const remaining = buffer.slice(newlineIndex + 1);
|
|
160
|
-
if (remaining.trim().length > 0) {
|
|
161
|
-
this.options.logger?.info(
|
|
162
|
-
`[FormSpec] Ignoring extra semantic query payload data for ${this.runtimePaths.workspaceRoot}`
|
|
163
|
-
);
|
|
164
|
-
}
|
|
165
|
-
buffer = remaining;
|
|
166
|
-
this.respondToSocket(socket, payload);
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
await new Promise((resolve, reject) => {
|
|
170
|
-
const handleError = (error) => {
|
|
171
|
-
reject(error);
|
|
194
|
+
/** Resolves semantic hover payload for a comment cursor position. */
|
|
195
|
+
getHover(filePath, offset) {
|
|
196
|
+
this.stats.queryTotals.hover += 1;
|
|
197
|
+
return this.runMeasured(
|
|
198
|
+
"semantic.getHover",
|
|
199
|
+
{ filePath, offset },
|
|
200
|
+
(performance2) => this.withCommentQueryContext(filePath, offset, performance2, (context) => ({
|
|
201
|
+
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
202
|
+
sourceHash: context.sourceHash,
|
|
203
|
+
hover: (0, import_internal2.serializeHoverInfo)(
|
|
204
|
+
(0, import_internal2.getCommentHoverInfoAtOffset)(context.sourceFile.text, offset, {
|
|
205
|
+
checker: context.checker,
|
|
206
|
+
...context.placement === null ? {} : { placement: context.placement },
|
|
207
|
+
...context.subjectType === void 0 ? {} : { subjectType: context.subjectType }
|
|
208
|
+
})
|
|
209
|
+
)
|
|
210
|
+
}))
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
/** Returns canonical FormSpec diagnostics for a file in the current host program. */
|
|
214
|
+
getDiagnostics(filePath) {
|
|
215
|
+
this.stats.queryTotals.diagnostics += 1;
|
|
216
|
+
return this.runMeasured("semantic.getDiagnostics", { filePath }, (performance2) => {
|
|
217
|
+
const { snapshot, cacheState } = this.getFileSnapshotWithCacheState(filePath, performance2);
|
|
218
|
+
this.recordQueryPath("diagnostics", cacheState);
|
|
219
|
+
return {
|
|
220
|
+
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
221
|
+
sourceHash: snapshot.sourceHash,
|
|
222
|
+
diagnostics: snapshot.diagnostics
|
|
172
223
|
};
|
|
173
|
-
this.server?.once("error", handleError);
|
|
174
|
-
this.server?.listen(this.runtimePaths.endpoint.address, () => {
|
|
175
|
-
this.server?.off("error", handleError);
|
|
176
|
-
resolve();
|
|
177
|
-
});
|
|
178
224
|
});
|
|
179
|
-
await this.writeManifest();
|
|
180
225
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
if (server?.listening === true) {
|
|
190
|
-
await new Promise((resolve, reject) => {
|
|
191
|
-
server.close((error) => {
|
|
192
|
-
if (error === void 0) {
|
|
193
|
-
resolve();
|
|
194
|
-
return;
|
|
195
|
-
}
|
|
196
|
-
reject(error);
|
|
197
|
-
});
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
await this.cleanupRuntimeArtifacts();
|
|
226
|
+
/** Returns the full serialized semantic snapshot for a file. */
|
|
227
|
+
getFileSnapshot(filePath) {
|
|
228
|
+
this.stats.queryTotals.fileSnapshot += 1;
|
|
229
|
+
return this.runMeasured("semantic.getFileSnapshot", { filePath }, (performance2) => {
|
|
230
|
+
const { snapshot, cacheState } = this.getFileSnapshotWithCacheState(filePath, performance2);
|
|
231
|
+
this.recordQueryPath("fileSnapshot", cacheState);
|
|
232
|
+
return snapshot;
|
|
233
|
+
});
|
|
201
234
|
}
|
|
235
|
+
/** Schedules a debounced background refresh for the file snapshot cache. */
|
|
202
236
|
scheduleSnapshotRefresh(filePath) {
|
|
203
237
|
const existing = this.refreshTimers.get(filePath);
|
|
204
238
|
if (existing !== void 0) {
|
|
@@ -206,7 +240,7 @@ var FormSpecPluginService = class {
|
|
|
206
240
|
}
|
|
207
241
|
const timer = setTimeout(() => {
|
|
208
242
|
try {
|
|
209
|
-
this.getFileSnapshot(filePath
|
|
243
|
+
this.getFileSnapshot(filePath);
|
|
210
244
|
} catch (error) {
|
|
211
245
|
this.options.logger?.info(
|
|
212
246
|
`[FormSpec] Failed to refresh semantic snapshot for ${filePath}: ${String(error)}`
|
|
@@ -214,93 +248,75 @@ var FormSpecPluginService = class {
|
|
|
214
248
|
}
|
|
215
249
|
this.refreshTimers.delete(filePath);
|
|
216
250
|
}, this.options.snapshotDebounceMs ?? FORM_SPEC_PLUGIN_DEFAULT_SNAPSHOT_DEBOUNCE_MS);
|
|
251
|
+
timer.unref();
|
|
217
252
|
this.refreshTimers.set(filePath, timer);
|
|
218
253
|
}
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
FORM_SPEC_PLUGIN_PERFORMANCE_EVENT.handleQuery,
|
|
224
|
-
{
|
|
225
|
-
kind: query.kind,
|
|
226
|
-
...query.kind === "health" ? {} : { filePath: query.filePath }
|
|
227
|
-
},
|
|
228
|
-
() => this.executeQuery(query, performance)
|
|
229
|
-
);
|
|
230
|
-
if (performance !== void 0) {
|
|
231
|
-
this.logPerformanceEvents(performance.events);
|
|
232
|
-
}
|
|
233
|
-
return response;
|
|
234
|
-
}
|
|
235
|
-
respondToSocket(socket, payload) {
|
|
236
|
-
try {
|
|
237
|
-
const query = JSON.parse(payload);
|
|
238
|
-
if (!(0, import_protocol2.isFormSpecSemanticQuery)(query)) {
|
|
239
|
-
throw new Error("Invalid FormSpec semantic query payload");
|
|
240
|
-
}
|
|
241
|
-
const response = this.handleQuery(query);
|
|
242
|
-
socket.end(`${JSON.stringify(response)}
|
|
243
|
-
`);
|
|
244
|
-
} catch (error) {
|
|
245
|
-
socket.end(
|
|
246
|
-
`${JSON.stringify({
|
|
247
|
-
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
248
|
-
kind: "error",
|
|
249
|
-
error: error instanceof Error ? error.message : String(error)
|
|
250
|
-
})}
|
|
251
|
-
`
|
|
252
|
-
);
|
|
254
|
+
/** Clears pending timers and cached semantic snapshots. */
|
|
255
|
+
dispose() {
|
|
256
|
+
for (const timer of this.refreshTimers.values()) {
|
|
257
|
+
clearTimeout(timer);
|
|
253
258
|
}
|
|
259
|
+
this.refreshTimers.clear();
|
|
260
|
+
this.snapshotCache.clear();
|
|
254
261
|
}
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
262
|
+
/** Returns a copy of the current performance and cache counters. */
|
|
263
|
+
getStats() {
|
|
264
|
+
return {
|
|
265
|
+
queryTotals: { ...this.stats.queryTotals },
|
|
266
|
+
queryPathTotals: {
|
|
267
|
+
diagnostics: { ...this.stats.queryPathTotals.diagnostics },
|
|
268
|
+
fileSnapshot: { ...this.stats.queryPathTotals.fileSnapshot }
|
|
269
|
+
},
|
|
270
|
+
fileSnapshotCacheHits: this.stats.fileSnapshotCacheHits,
|
|
271
|
+
fileSnapshotCacheMisses: this.stats.fileSnapshotCacheMisses,
|
|
272
|
+
syntheticBatchCacheHits: this.stats.syntheticBatchCacheHits,
|
|
273
|
+
syntheticBatchCacheMisses: this.stats.syntheticBatchCacheMisses,
|
|
274
|
+
syntheticCompileCount: this.stats.syntheticCompileCount,
|
|
275
|
+
syntheticCompileApplications: this.stats.syntheticCompileApplications
|
|
276
|
+
};
|
|
260
277
|
}
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
278
|
+
runMeasured(name, detail, fn) {
|
|
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));
|
|
281
|
+
this.updateStatsFromPerformanceEvents(performance2.events);
|
|
282
|
+
if (this.options.enablePerformanceLogging === true) {
|
|
283
|
+
this.logPerformanceEvents(name, performance2.events);
|
|
265
284
|
}
|
|
285
|
+
return result;
|
|
266
286
|
}
|
|
267
|
-
withCommentQueryContext(filePath, offset,
|
|
268
|
-
return (0,
|
|
269
|
-
|
|
270
|
-
"
|
|
287
|
+
withCommentQueryContext(filePath, offset, performance2, handler) {
|
|
288
|
+
return (0, import_internal2.optionalMeasure)(
|
|
289
|
+
performance2,
|
|
290
|
+
"semantic.resolveCommentQueryContext",
|
|
271
291
|
{
|
|
272
292
|
filePath,
|
|
273
293
|
offset
|
|
274
294
|
},
|
|
275
295
|
() => {
|
|
276
|
-
const environment = this.getSourceEnvironment(filePath,
|
|
296
|
+
const environment = this.getSourceEnvironment(filePath, performance2);
|
|
277
297
|
if (environment === null) {
|
|
278
|
-
return
|
|
279
|
-
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
280
|
-
kind: "error",
|
|
281
|
-
error: `Unable to resolve TypeScript source file for ${filePath}`
|
|
282
|
-
};
|
|
298
|
+
return null;
|
|
283
299
|
}
|
|
284
|
-
const declaration = (0,
|
|
285
|
-
|
|
286
|
-
"
|
|
300
|
+
const declaration = (0, import_internal2.optionalMeasure)(
|
|
301
|
+
performance2,
|
|
302
|
+
"semantic.findDeclarationForCommentOffset",
|
|
287
303
|
{
|
|
288
304
|
filePath,
|
|
289
305
|
offset
|
|
290
306
|
},
|
|
291
|
-
() => (0,
|
|
307
|
+
() => (0, import_internal2.findDeclarationForCommentOffset)(environment.sourceFile, offset)
|
|
292
308
|
);
|
|
293
|
-
const placement = declaration === null ? null : (0,
|
|
294
|
-
|
|
295
|
-
"
|
|
309
|
+
const placement = declaration === null ? null : (0, import_internal2.optionalMeasure)(
|
|
310
|
+
performance2,
|
|
311
|
+
"semantic.resolveDeclarationPlacement",
|
|
296
312
|
void 0,
|
|
297
|
-
() => (0,
|
|
313
|
+
() => (0, import_internal2.resolveDeclarationPlacement)(declaration)
|
|
298
314
|
);
|
|
299
|
-
const subjectType = declaration === null ? void 0 : (0,
|
|
300
|
-
|
|
301
|
-
"
|
|
315
|
+
const subjectType = declaration === null ? void 0 : (0, import_internal2.optionalMeasure)(
|
|
316
|
+
performance2,
|
|
317
|
+
"semantic.getSubjectType",
|
|
302
318
|
void 0,
|
|
303
|
-
() => (0,
|
|
319
|
+
() => (0, import_internal2.getSubjectType)(declaration, environment.checker)
|
|
304
320
|
);
|
|
305
321
|
return handler({
|
|
306
322
|
...environment,
|
|
@@ -311,11 +327,12 @@ var FormSpecPluginService = class {
|
|
|
311
327
|
}
|
|
312
328
|
);
|
|
313
329
|
}
|
|
314
|
-
|
|
315
|
-
const startedAt = (0,
|
|
316
|
-
const environment = this.getSourceEnvironment(filePath,
|
|
330
|
+
getFileSnapshotWithCacheState(filePath, performance2) {
|
|
331
|
+
const startedAt = (0, import_internal2.getFormSpecPerformanceNow)();
|
|
332
|
+
const environment = this.getSourceEnvironment(filePath, performance2);
|
|
317
333
|
if (environment === null) {
|
|
318
|
-
|
|
334
|
+
this.stats.fileSnapshotCacheMisses += 1;
|
|
335
|
+
const snapshot2 = {
|
|
319
336
|
filePath,
|
|
320
337
|
sourceHash: "",
|
|
321
338
|
generatedAt: this.getNow().toISOString(),
|
|
@@ -323,155 +340,107 @@ var FormSpecPluginService = class {
|
|
|
323
340
|
diagnostics: [
|
|
324
341
|
{
|
|
325
342
|
code: "MISSING_SOURCE_FILE",
|
|
343
|
+
category: "infrastructure",
|
|
326
344
|
message: `Unable to resolve TypeScript source file for ${filePath}`,
|
|
327
345
|
range: { start: 0, end: 0 },
|
|
328
|
-
severity: "warning"
|
|
346
|
+
severity: "warning",
|
|
347
|
+
relatedLocations: [],
|
|
348
|
+
data: {
|
|
349
|
+
filePath
|
|
350
|
+
}
|
|
329
351
|
}
|
|
330
352
|
]
|
|
331
353
|
};
|
|
332
|
-
|
|
333
|
-
name: "
|
|
334
|
-
durationMs: (0,
|
|
354
|
+
performance2.record({
|
|
355
|
+
name: "semantic.getFileSnapshot.result",
|
|
356
|
+
durationMs: (0, import_internal2.getFormSpecPerformanceNow)() - startedAt,
|
|
335
357
|
detail: {
|
|
336
358
|
filePath,
|
|
337
359
|
cache: "missing-source"
|
|
338
360
|
}
|
|
339
361
|
});
|
|
340
|
-
return
|
|
362
|
+
return {
|
|
363
|
+
snapshot: snapshot2,
|
|
364
|
+
cacheState: "missing-source"
|
|
365
|
+
};
|
|
341
366
|
}
|
|
342
367
|
const cached = this.snapshotCache.get(filePath);
|
|
343
368
|
if (cached?.sourceHash === environment.sourceHash) {
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
369
|
+
this.stats.fileSnapshotCacheHits += 1;
|
|
370
|
+
performance2.record({
|
|
371
|
+
name: "semantic.getFileSnapshot.result",
|
|
372
|
+
durationMs: (0, import_internal2.getFormSpecPerformanceNow)() - startedAt,
|
|
347
373
|
detail: {
|
|
348
374
|
filePath,
|
|
349
375
|
cache: "hit"
|
|
350
376
|
}
|
|
351
377
|
});
|
|
352
|
-
return
|
|
378
|
+
return {
|
|
379
|
+
snapshot: cached.snapshot,
|
|
380
|
+
cacheState: "hit"
|
|
381
|
+
};
|
|
353
382
|
}
|
|
354
|
-
|
|
383
|
+
this.stats.fileSnapshotCacheMisses += 1;
|
|
384
|
+
const snapshot = (0, import_internal2.buildFormSpecAnalysisFileSnapshot)(environment.sourceFile, {
|
|
355
385
|
checker: environment.checker,
|
|
356
386
|
now: () => this.getNow(),
|
|
357
|
-
|
|
387
|
+
performance: performance2
|
|
358
388
|
});
|
|
359
389
|
this.snapshotCache.set(filePath, {
|
|
360
390
|
sourceHash: environment.sourceHash,
|
|
361
391
|
snapshot
|
|
362
392
|
});
|
|
363
|
-
|
|
364
|
-
name: "
|
|
365
|
-
durationMs: (0,
|
|
393
|
+
performance2.record({
|
|
394
|
+
name: "semantic.getFileSnapshot.result",
|
|
395
|
+
durationMs: (0, import_internal2.getFormSpecPerformanceNow)() - startedAt,
|
|
366
396
|
detail: {
|
|
367
397
|
filePath,
|
|
368
398
|
cache: "miss"
|
|
369
399
|
}
|
|
370
400
|
});
|
|
371
|
-
return
|
|
401
|
+
return {
|
|
402
|
+
snapshot,
|
|
403
|
+
cacheState: "miss"
|
|
404
|
+
};
|
|
372
405
|
}
|
|
373
406
|
getNow() {
|
|
374
407
|
return this.options.now?.() ?? /* @__PURE__ */ new Date();
|
|
375
408
|
}
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
381
|
-
kind: "health",
|
|
382
|
-
manifest: this.manifest
|
|
383
|
-
};
|
|
384
|
-
case "completion":
|
|
385
|
-
return this.withCommentQueryContext(
|
|
386
|
-
query.filePath,
|
|
387
|
-
query.offset,
|
|
388
|
-
(context) => ({
|
|
389
|
-
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
390
|
-
kind: "completion",
|
|
391
|
-
sourceHash: context.sourceHash,
|
|
392
|
-
context: (0, import_internal.serializeCompletionContext)(
|
|
393
|
-
(0, import_internal.getSemanticCommentCompletionContextAtOffset)(context.sourceFile.text, query.offset, {
|
|
394
|
-
checker: context.checker,
|
|
395
|
-
...context.placement === null ? {} : { placement: context.placement },
|
|
396
|
-
...context.subjectType === void 0 ? {} : { subjectType: context.subjectType }
|
|
397
|
-
})
|
|
398
|
-
)
|
|
399
|
-
}),
|
|
400
|
-
performance
|
|
401
|
-
);
|
|
402
|
-
case "hover":
|
|
403
|
-
return this.withCommentQueryContext(
|
|
404
|
-
query.filePath,
|
|
405
|
-
query.offset,
|
|
406
|
-
(context) => ({
|
|
407
|
-
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
408
|
-
kind: "hover",
|
|
409
|
-
sourceHash: context.sourceHash,
|
|
410
|
-
hover: (0, import_internal.serializeHoverInfo)(
|
|
411
|
-
(0, import_internal.getCommentHoverInfoAtOffset)(context.sourceFile.text, query.offset, {
|
|
412
|
-
checker: context.checker,
|
|
413
|
-
...context.placement === null ? {} : { placement: context.placement },
|
|
414
|
-
...context.subjectType === void 0 ? {} : { subjectType: context.subjectType }
|
|
415
|
-
})
|
|
416
|
-
)
|
|
417
|
-
}),
|
|
418
|
-
performance
|
|
419
|
-
);
|
|
420
|
-
case "diagnostics": {
|
|
421
|
-
const snapshot = this.getFileSnapshot(query.filePath, performance);
|
|
422
|
-
return {
|
|
423
|
-
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
424
|
-
kind: "diagnostics",
|
|
425
|
-
sourceHash: snapshot.sourceHash,
|
|
426
|
-
diagnostics: snapshot.diagnostics
|
|
427
|
-
};
|
|
428
|
-
}
|
|
429
|
-
case "file-snapshot":
|
|
430
|
-
return {
|
|
431
|
-
protocolVersion: import_protocol2.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
432
|
-
kind: "file-snapshot",
|
|
433
|
-
snapshot: this.getFileSnapshot(query.filePath, performance)
|
|
434
|
-
};
|
|
435
|
-
default: {
|
|
436
|
-
throw new Error(`Unhandled semantic query: ${JSON.stringify(query)}`);
|
|
437
|
-
}
|
|
438
|
-
}
|
|
439
|
-
}
|
|
440
|
-
getSourceEnvironment(filePath, performance) {
|
|
441
|
-
return (0, import_internal.optionalMeasure)(
|
|
442
|
-
performance,
|
|
443
|
-
"plugin.getSourceEnvironment",
|
|
409
|
+
getSourceEnvironment(filePath, performance2) {
|
|
410
|
+
return (0, import_internal2.optionalMeasure)(
|
|
411
|
+
performance2,
|
|
412
|
+
"semantic.getSourceEnvironment",
|
|
444
413
|
{
|
|
445
414
|
filePath
|
|
446
415
|
},
|
|
447
416
|
() => {
|
|
448
|
-
const program = (0,
|
|
449
|
-
|
|
450
|
-
"
|
|
417
|
+
const program = (0, import_internal2.optionalMeasure)(
|
|
418
|
+
performance2,
|
|
419
|
+
"semantic.sourceEnvironment.getProgram",
|
|
451
420
|
void 0,
|
|
452
421
|
() => this.options.getProgram()
|
|
453
422
|
);
|
|
454
423
|
if (program === void 0) {
|
|
455
424
|
return null;
|
|
456
425
|
}
|
|
457
|
-
const sourceFile = (0,
|
|
458
|
-
|
|
459
|
-
"
|
|
426
|
+
const sourceFile = (0, import_internal2.optionalMeasure)(
|
|
427
|
+
performance2,
|
|
428
|
+
"semantic.sourceEnvironment.getSourceFile",
|
|
460
429
|
void 0,
|
|
461
430
|
() => program.getSourceFile(filePath)
|
|
462
431
|
);
|
|
463
432
|
if (sourceFile === void 0) {
|
|
464
433
|
return null;
|
|
465
434
|
}
|
|
466
|
-
const checker = (0,
|
|
467
|
-
|
|
468
|
-
"
|
|
435
|
+
const checker = (0, import_internal2.optionalMeasure)(
|
|
436
|
+
performance2,
|
|
437
|
+
"semantic.sourceEnvironment.getTypeChecker",
|
|
469
438
|
void 0,
|
|
470
439
|
() => program.getTypeChecker()
|
|
471
440
|
);
|
|
472
|
-
const sourceHash = (0,
|
|
473
|
-
|
|
474
|
-
"
|
|
441
|
+
const sourceHash = (0, import_internal2.optionalMeasure)(
|
|
442
|
+
performance2,
|
|
443
|
+
"semantic.sourceEnvironment.computeTextHash",
|
|
475
444
|
void 0,
|
|
476
445
|
() => (0, import_protocol2.computeFormSpecTextHash)(sourceFile.text)
|
|
477
446
|
);
|
|
@@ -483,19 +452,38 @@ var FormSpecPluginService = class {
|
|
|
483
452
|
}
|
|
484
453
|
);
|
|
485
454
|
}
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
455
|
+
recordQueryPath(kind, cacheState) {
|
|
456
|
+
if (cacheState === "hit") {
|
|
457
|
+
this.stats.queryPathTotals[kind].warm += 1;
|
|
489
458
|
return;
|
|
490
459
|
}
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
460
|
+
this.stats.queryPathTotals[kind].cold += 1;
|
|
461
|
+
}
|
|
462
|
+
updateStatsFromPerformanceEvents(events) {
|
|
463
|
+
for (const event of events) {
|
|
464
|
+
if (event.name === "analysis.syntheticCheckBatch.cacheHit" || event.name === "analysis.narrowSyntheticCheckBatch.cacheHit") {
|
|
465
|
+
this.stats.syntheticBatchCacheHits += 1;
|
|
466
|
+
continue;
|
|
467
|
+
}
|
|
468
|
+
if (event.name === "analysis.syntheticCheckBatch.cacheMiss" || event.name === "analysis.narrowSyntheticCheckBatch.cacheMiss") {
|
|
469
|
+
this.stats.syntheticBatchCacheMisses += 1;
|
|
470
|
+
const applicationCount = event.detail?.["applicationCount"];
|
|
471
|
+
if (typeof applicationCount === "number") {
|
|
472
|
+
this.stats.syntheticCompileApplications += applicationCount;
|
|
473
|
+
}
|
|
474
|
+
continue;
|
|
497
475
|
}
|
|
476
|
+
if (event.name === "analysis.syntheticCheckBatch.createProgram" || event.name === "analysis.narrowSyntheticCheckBatch.createProgram") {
|
|
477
|
+
this.stats.syntheticCompileCount += 1;
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
logPerformanceEvents(rootEventName, events) {
|
|
482
|
+
const logger = this.options.logger;
|
|
483
|
+
if (logger === void 0 || events.length === 0) {
|
|
484
|
+
return;
|
|
498
485
|
}
|
|
486
|
+
const rootEvent = [...events].reverse().find((event) => event.name === rootEventName);
|
|
499
487
|
if (rootEvent === void 0) {
|
|
500
488
|
return;
|
|
501
489
|
}
|
|
@@ -503,18 +491,234 @@ var FormSpecPluginService = class {
|
|
|
503
491
|
if (rootEvent.durationMs < thresholdMs) {
|
|
504
492
|
return;
|
|
505
493
|
}
|
|
506
|
-
const sortedHotspots = [...events].filter((event) => event.name !==
|
|
494
|
+
const sortedHotspots = [...events].filter((event) => event.name !== rootEventName).sort((left, right) => right.durationMs - left.durationMs).slice(0, 8);
|
|
507
495
|
const lines = [
|
|
508
|
-
`[FormSpec][perf] ${
|
|
496
|
+
`[FormSpec][perf] ${formatPerformanceEvent(rootEvent)}`,
|
|
509
497
|
...sortedHotspots.map((event) => ` ${formatPerformanceEvent(event)}`)
|
|
510
498
|
];
|
|
511
499
|
logger.info(lines.join("\n"));
|
|
512
500
|
}
|
|
513
501
|
};
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
502
|
+
|
|
503
|
+
// src/service.ts
|
|
504
|
+
var FormSpecPluginService = class {
|
|
505
|
+
constructor(options) {
|
|
506
|
+
this.options = options;
|
|
507
|
+
this.semanticService = new FormSpecSemanticService(options);
|
|
508
|
+
this.runtimePaths = getFormSpecWorkspaceRuntimePaths(options.workspaceRoot);
|
|
509
|
+
this.manifest = createFormSpecAnalysisManifest(
|
|
510
|
+
options.workspaceRoot,
|
|
511
|
+
options.typescriptVersion,
|
|
512
|
+
Date.now()
|
|
513
|
+
);
|
|
514
|
+
}
|
|
515
|
+
manifest;
|
|
516
|
+
runtimePaths;
|
|
517
|
+
semanticService;
|
|
518
|
+
server = null;
|
|
519
|
+
getManifest() {
|
|
520
|
+
return this.manifest;
|
|
521
|
+
}
|
|
522
|
+
/**
|
|
523
|
+
* Returns the underlying semantic service used by this reference wrapper.
|
|
524
|
+
*
|
|
525
|
+
* @public
|
|
526
|
+
*/
|
|
527
|
+
getSemanticService() {
|
|
528
|
+
return this.semanticService;
|
|
529
|
+
}
|
|
530
|
+
async start() {
|
|
531
|
+
if (this.server !== null) {
|
|
532
|
+
return;
|
|
533
|
+
}
|
|
534
|
+
await import_promises.default.mkdir(this.runtimePaths.runtimeDirectory, { recursive: true });
|
|
535
|
+
if (this.runtimePaths.endpoint.kind === "unix-socket") {
|
|
536
|
+
await import_promises.default.rm(this.runtimePaths.endpoint.address, { force: true });
|
|
537
|
+
}
|
|
538
|
+
this.server = import_node_net.default.createServer((socket) => {
|
|
539
|
+
let buffer = "";
|
|
540
|
+
socket.setEncoding("utf8");
|
|
541
|
+
socket.setTimeout(FORM_SPEC_PLUGIN_SOCKET_IDLE_TIMEOUT_MS, () => {
|
|
542
|
+
this.options.logger?.info(
|
|
543
|
+
`[FormSpec] Closing idle semantic query socket for ${this.runtimePaths.workspaceRoot}`
|
|
544
|
+
);
|
|
545
|
+
socket.destroy();
|
|
546
|
+
});
|
|
547
|
+
socket.on("data", (chunk) => {
|
|
548
|
+
buffer += String(chunk);
|
|
549
|
+
if (buffer.length > FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES) {
|
|
550
|
+
socket.end(
|
|
551
|
+
`${JSON.stringify({
|
|
552
|
+
protocolVersion: import_protocol3.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
553
|
+
kind: "error",
|
|
554
|
+
error: `FormSpec semantic query exceeded ${String(FORM_SPEC_PLUGIN_MAX_SOCKET_PAYLOAD_BYTES)} bytes`
|
|
555
|
+
})}
|
|
556
|
+
`
|
|
557
|
+
);
|
|
558
|
+
return;
|
|
559
|
+
}
|
|
560
|
+
const newlineIndex = buffer.indexOf("\n");
|
|
561
|
+
if (newlineIndex < 0) {
|
|
562
|
+
return;
|
|
563
|
+
}
|
|
564
|
+
const payload = buffer.slice(0, newlineIndex);
|
|
565
|
+
const remaining = buffer.slice(newlineIndex + 1);
|
|
566
|
+
if (remaining.trim().length > 0) {
|
|
567
|
+
this.options.logger?.info(
|
|
568
|
+
`[FormSpec] Ignoring extra semantic query payload data for ${this.runtimePaths.workspaceRoot}`
|
|
569
|
+
);
|
|
570
|
+
}
|
|
571
|
+
buffer = remaining;
|
|
572
|
+
this.respondToSocket(socket, payload);
|
|
573
|
+
});
|
|
574
|
+
});
|
|
575
|
+
await new Promise((resolve, reject) => {
|
|
576
|
+
const handleError = (error) => {
|
|
577
|
+
reject(error);
|
|
578
|
+
};
|
|
579
|
+
this.server?.once("error", handleError);
|
|
580
|
+
this.server?.listen(this.runtimePaths.endpoint.address, () => {
|
|
581
|
+
this.server?.off("error", handleError);
|
|
582
|
+
resolve();
|
|
583
|
+
});
|
|
584
|
+
});
|
|
585
|
+
await this.writeManifest();
|
|
586
|
+
}
|
|
587
|
+
async stop() {
|
|
588
|
+
this.semanticService.dispose();
|
|
589
|
+
const server = this.server;
|
|
590
|
+
this.server = null;
|
|
591
|
+
if (server?.listening === true) {
|
|
592
|
+
await new Promise((resolve, reject) => {
|
|
593
|
+
server.close((error) => {
|
|
594
|
+
if (error === void 0) {
|
|
595
|
+
resolve();
|
|
596
|
+
return;
|
|
597
|
+
}
|
|
598
|
+
reject(error);
|
|
599
|
+
});
|
|
600
|
+
});
|
|
601
|
+
}
|
|
602
|
+
await this.cleanupRuntimeArtifacts();
|
|
603
|
+
}
|
|
604
|
+
scheduleSnapshotRefresh(filePath) {
|
|
605
|
+
this.semanticService.scheduleSnapshotRefresh(filePath);
|
|
606
|
+
}
|
|
607
|
+
handleQuery(query) {
|
|
608
|
+
if (this.options.enablePerformanceLogging === true) {
|
|
609
|
+
const startedAt = performance.now();
|
|
610
|
+
const response = this.executeQuery(query);
|
|
611
|
+
this.logQueryDuration(query, performance.now() - startedAt);
|
|
612
|
+
return response;
|
|
613
|
+
}
|
|
614
|
+
return this.executeQuery(query);
|
|
615
|
+
}
|
|
616
|
+
executeQuery(query) {
|
|
617
|
+
switch (query.kind) {
|
|
618
|
+
case "health":
|
|
619
|
+
return {
|
|
620
|
+
protocolVersion: import_protocol3.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
621
|
+
kind: "health",
|
|
622
|
+
manifest: this.manifest
|
|
623
|
+
};
|
|
624
|
+
case "completion": {
|
|
625
|
+
const result = this.semanticService.getCompletionContext(query.filePath, query.offset);
|
|
626
|
+
if (result === null) {
|
|
627
|
+
return {
|
|
628
|
+
protocolVersion: import_protocol3.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
629
|
+
kind: "error",
|
|
630
|
+
error: `Unable to resolve TypeScript source file for ${query.filePath}`
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
return {
|
|
634
|
+
...result,
|
|
635
|
+
kind: "completion"
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
case "hover": {
|
|
639
|
+
const result = this.semanticService.getHover(query.filePath, query.offset);
|
|
640
|
+
if (result === null) {
|
|
641
|
+
return {
|
|
642
|
+
protocolVersion: import_protocol3.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
643
|
+
kind: "error",
|
|
644
|
+
error: `Unable to resolve TypeScript source file for ${query.filePath}`
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
return {
|
|
648
|
+
...result,
|
|
649
|
+
kind: "hover"
|
|
650
|
+
};
|
|
651
|
+
}
|
|
652
|
+
case "diagnostics": {
|
|
653
|
+
const result = this.semanticService.getDiagnostics(query.filePath);
|
|
654
|
+
return {
|
|
655
|
+
...result,
|
|
656
|
+
kind: "diagnostics"
|
|
657
|
+
};
|
|
658
|
+
}
|
|
659
|
+
case "file-snapshot":
|
|
660
|
+
return {
|
|
661
|
+
protocolVersion: import_protocol3.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
662
|
+
kind: "file-snapshot",
|
|
663
|
+
snapshot: this.semanticService.getFileSnapshot(query.filePath)
|
|
664
|
+
};
|
|
665
|
+
default: {
|
|
666
|
+
throw new Error(`Unhandled semantic query: ${JSON.stringify(query)}`);
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
}
|
|
670
|
+
respondToSocket(socket, payload) {
|
|
671
|
+
try {
|
|
672
|
+
const query = JSON.parse(payload);
|
|
673
|
+
if (!(0, import_protocol3.isFormSpecSemanticQuery)(query)) {
|
|
674
|
+
throw new Error("Invalid FormSpec semantic query payload");
|
|
675
|
+
}
|
|
676
|
+
const response = this.handleQuery(query);
|
|
677
|
+
socket.end(`${JSON.stringify(response)}
|
|
678
|
+
`);
|
|
679
|
+
} catch (error) {
|
|
680
|
+
socket.end(
|
|
681
|
+
`${JSON.stringify({
|
|
682
|
+
protocolVersion: import_protocol3.FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
683
|
+
kind: "error",
|
|
684
|
+
error: error instanceof Error ? error.message : String(error)
|
|
685
|
+
})}
|
|
686
|
+
`
|
|
687
|
+
);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
async writeManifest() {
|
|
691
|
+
const tempManifestPath = `${this.runtimePaths.manifestPath}.tmp`;
|
|
692
|
+
await import_promises.default.writeFile(tempManifestPath, `${JSON.stringify(this.manifest, null, 2)}
|
|
693
|
+
`, "utf8");
|
|
694
|
+
await import_promises.default.rename(tempManifestPath, this.runtimePaths.manifestPath);
|
|
695
|
+
}
|
|
696
|
+
async cleanupRuntimeArtifacts() {
|
|
697
|
+
await import_promises.default.rm(this.runtimePaths.manifestPath, { force: true });
|
|
698
|
+
if (this.runtimePaths.endpoint.kind === "unix-socket") {
|
|
699
|
+
await import_promises.default.rm(this.runtimePaths.endpoint.address, { force: true });
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
logQueryDuration(query, durationMs) {
|
|
703
|
+
const logger = this.options.logger;
|
|
704
|
+
if (logger === void 0) {
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
const thresholdMs = this.options.performanceLogThresholdMs ?? FORM_SPEC_PLUGIN_DEFAULT_PERFORMANCE_LOG_THRESHOLD_MS;
|
|
708
|
+
if (durationMs < thresholdMs) {
|
|
709
|
+
return;
|
|
710
|
+
}
|
|
711
|
+
const event = {
|
|
712
|
+
name: "plugin.handleQuery",
|
|
713
|
+
durationMs,
|
|
714
|
+
detail: {
|
|
715
|
+
kind: query.kind,
|
|
716
|
+
...query.kind === "health" ? {} : { filePath: query.filePath }
|
|
717
|
+
}
|
|
718
|
+
};
|
|
719
|
+
logger.info(`[FormSpec][perf] ${formatPerformanceEvent(event)}`);
|
|
720
|
+
}
|
|
721
|
+
};
|
|
518
722
|
function createLanguageServiceProxy(languageService, semanticService) {
|
|
519
723
|
const wrapWithSnapshotRefresh = (fn) => {
|
|
520
724
|
return (fileName, ...args) => {
|
|
@@ -623,12 +827,17 @@ function init(modules) {
|
|
|
623
827
|
return {
|
|
624
828
|
create(info) {
|
|
625
829
|
const service = getOrCreateService(info, typescriptVersion);
|
|
626
|
-
return createLanguageServiceProxy(info.languageService, service);
|
|
830
|
+
return createLanguageServiceProxy(info.languageService, service.getSemanticService());
|
|
627
831
|
}
|
|
628
832
|
};
|
|
629
833
|
}
|
|
630
834
|
// Annotate the CommonJS export names for ESM import in node:
|
|
631
835
|
0 && (module.exports = {
|
|
836
|
+
FORMSPEC_ANALYSIS_PROTOCOL_VERSION,
|
|
837
|
+
FORMSPEC_ANALYSIS_SCHEMA_VERSION,
|
|
838
|
+
FormSpecPluginService,
|
|
839
|
+
FormSpecSemanticService,
|
|
840
|
+
createLanguageServiceProxy,
|
|
632
841
|
init
|
|
633
842
|
});
|
|
634
843
|
//# sourceMappingURL=index.cjs.map
|