@likec4/language-server 1.41.0 → 1.42.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/browser/package.json +4 -0
  2. package/browser-worker/package.json +4 -0
  3. package/dist/LikeC4LanguageServices.d.ts +1 -1
  4. package/dist/LikeC4LanguageServices.mjs +3 -2
  5. package/dist/Rpc.mjs +30 -24
  6. package/dist/ast.d.ts +1 -7
  7. package/dist/ast.mjs +0 -10
  8. package/dist/bundled.mjs +4125 -3660
  9. package/dist/documentation/documentation-provider.mjs +1 -1
  10. package/dist/filesystem/FileSystemWatcher.d.ts +2 -2
  11. package/dist/filesystem/LikeC4FileSystem.mjs +10 -4
  12. package/dist/filesystem/index.d.ts +1 -1
  13. package/dist/formatting/LikeC4Formatter.mjs +41 -10
  14. package/dist/formatting/utils.d.ts +3 -3
  15. package/dist/formatting/utils.mjs +1 -1
  16. package/dist/generated/ast.d.ts +35 -16
  17. package/dist/generated/ast.mjs +69 -26
  18. package/dist/generated/grammar.mjs +1 -1
  19. package/dist/lsp/CompletionProvider.mjs +1 -1
  20. package/dist/lsp/DocumentLinkProvider.d.ts +1 -1
  21. package/dist/lsp/DocumentLinkProvider.mjs +1 -1
  22. package/dist/lsp/DocumentSymbolProvider.mjs +1 -1
  23. package/dist/mcp/NoopLikeC4MCPServer.d.ts +1 -1
  24. package/dist/mcp/NoopLikeC4MCPServer.mjs +1 -1
  25. package/dist/mcp/server/StdioLikeC4MCPServer.mjs +4 -1
  26. package/dist/mcp/server/StreamableLikeC4MCPServer.mjs +3 -3
  27. package/dist/mcp/server/WithMCPServer.mjs +2 -2
  28. package/dist/mcp/tools/_common.mjs +2 -2
  29. package/dist/model/builder/MergedSpecification.d.ts +3 -3
  30. package/dist/model/builder/MergedSpecification.mjs +13 -39
  31. package/dist/model/builder/buildModel.mjs +14 -17
  32. package/dist/model/model-builder.d.ts +1 -1
  33. package/dist/model/model-builder.mjs +12 -9
  34. package/dist/model/model-locator.d.ts +5 -0
  35. package/dist/model/model-locator.mjs +40 -3
  36. package/dist/model/model-parser-where.mjs +1 -2
  37. package/dist/model/model-parser.d.ts +19 -2
  38. package/dist/model/parser/Base.mjs +8 -8
  39. package/dist/model/parser/DeploymentModelParser.d.ts +1 -0
  40. package/dist/model/parser/DeploymentModelParser.mjs +7 -7
  41. package/dist/model/parser/DeploymentViewParser.d.ts +1 -0
  42. package/dist/model/parser/FqnRefParser.d.ts +2 -0
  43. package/dist/model/parser/FqnRefParser.mjs +16 -11
  44. package/dist/model/parser/GlobalsParser.d.ts +8 -2
  45. package/dist/model/parser/ModelParser.d.ts +1 -0
  46. package/dist/model/parser/ModelParser.mjs +16 -11
  47. package/dist/model/parser/PredicatesParser.d.ts +1 -0
  48. package/dist/model/parser/SpecificationParser.mjs +4 -4
  49. package/dist/model/parser/ViewsParser.d.ts +12 -2
  50. package/dist/model/parser/ViewsParser.mjs +123 -31
  51. package/dist/model-change/ModelChanges.d.ts +1 -1
  52. package/dist/module.mjs +3 -2
  53. package/dist/protocol.d.ts +28 -4
  54. package/dist/references/scope-computation.mjs +2 -3
  55. package/dist/references/scope-provider.d.ts +2 -2
  56. package/dist/references/scope-provider.mjs +8 -15
  57. package/dist/test/testServices.d.ts +2 -0
  58. package/dist/test/testServices.mjs +10 -11
  59. package/dist/utils/disposable.mjs +2 -2
  60. package/dist/utils/index.mjs +1 -1
  61. package/dist/validation/_shared.d.ts +1 -1
  62. package/dist/validation/deployment-checks.d.ts +1 -1
  63. package/dist/validation/deployment-checks.mjs +4 -1
  64. package/dist/validation/dynamic-view.d.ts +3 -2
  65. package/dist/validation/dynamic-view.mjs +21 -2
  66. package/dist/validation/element-ref.d.ts +2 -2
  67. package/dist/validation/element-ref.mjs +1 -1
  68. package/dist/validation/imports.d.ts +0 -1
  69. package/dist/validation/imports.mjs +0 -5
  70. package/dist/validation/index.d.ts +1 -1
  71. package/dist/validation/index.mjs +19 -13
  72. package/dist/validation/view-predicates/relation-with.d.ts +1 -1
  73. package/dist/validation/view.d.ts +1 -1
  74. package/dist/view-utils/index.d.ts +0 -1
  75. package/dist/view-utils/index.mjs +0 -1
  76. package/dist/views/likec4-views.d.ts +6 -0
  77. package/dist/views/likec4-views.mjs +31 -18
  78. package/dist/workspace/ProjectsManager.d.ts +23 -31
  79. package/dist/workspace/ProjectsManager.mjs +78 -89
  80. package/dist/workspace/WorkspaceManager.mjs +1 -1
  81. package/likec4lib/package.json +4 -0
  82. package/package.json +24 -28
  83. package/protocol/package.json +4 -0
  84. package/dist/view-utils/resolve-relative-paths.d.ts +0 -2
  85. package/dist/view-utils/resolve-relative-paths.mjs +0 -78
@@ -1,15 +1,16 @@
1
- import { isLikeC4Config, validateProjectConfig } from "@likec4/config";
1
+ import {
2
+ isLikeC4Config,
3
+ validateProjectConfig
4
+ } from "@likec4/config";
2
5
  import { BiMap, delay, invariant, memoizeProp, nonNullable } from "@likec4/core/utils";
3
- import { loggable } from "@likec4/log";
4
6
  import { deepEqual } from "fast-equals";
5
7
  import {
6
8
  interruptAndCheck,
7
9
  URI,
8
10
  WorkspaceCache
9
11
  } from "langium";
10
- import PQueue from "p-queue";
11
12
  import picomatch from "picomatch";
12
- import { hasAtLeast, isNullish, isTruthy, map, pickBy, pipe, prop, sortBy } from "remeda";
13
+ import { hasAtLeast, isNullish, map, pipe, prop, sortBy } from "remeda";
13
14
  import {
14
15
  joinRelativeURL,
15
16
  parseFilename,
@@ -31,6 +32,14 @@ export function ProjectFolder(folder) {
31
32
  folder = normalizeUri(folder);
32
33
  return withTrailingSlash(folder);
33
34
  }
35
+ const DefaultProject = {
36
+ id: "default",
37
+ config: {
38
+ name: "default",
39
+ exclude: ["**/node_modules/**"]
40
+ },
41
+ exclude: picomatch("**/node_modules/**", { dot: true })
42
+ };
34
43
  export class ProjectsManager {
35
44
  constructor(services) {
36
45
  this.services = services;
@@ -40,15 +49,7 @@ export class ProjectsManager {
40
49
  * The global project ID used for all documents
41
50
  * that are not part of a specific project.
42
51
  */
43
- static DefaultProjectId = "default";
44
- static DefaultProject = {
45
- id: ProjectsManager.DefaultProjectId,
46
- config: {
47
- name: ProjectsManager.DefaultProjectId,
48
- exclude: ["**/node_modules/**"]
49
- },
50
- exclude: picomatch("**/node_modules/**", { dot: true })
51
- };
52
+ static DefaultProjectId = DefaultProject.id;
52
53
  /**
53
54
  * Configured default project ID.
54
55
  * (it is used in CLI and Vite plugin)
@@ -57,21 +58,14 @@ export class ProjectsManager {
57
58
  /**
58
59
  * The mapping between project config files and project IDs.
59
60
  */
60
- projectIdToFolder = new BiMap();
61
+ #projectIdToFolder = new BiMap();
61
62
  /**
62
63
  * Registered projects.
63
64
  * Sorted descending by the number of segments in the folder path.
64
65
  * This ensures that the most specific project is used for a document.
65
66
  */
66
- _projects = [];
67
- excludedDocuments = /* @__PURE__ */ new WeakMap();
68
- get defaultGlobalProject() {
69
- return ProjectsManager.DefaultProject;
70
- }
71
- reloadProjectsLimiter = new PQueue({
72
- concurrency: 1,
73
- timeout: 2e4
74
- });
67
+ #projects = [];
68
+ #excludedDocuments = /* @__PURE__ */ new WeakMap();
75
69
  /**
76
70
  * Returns:
77
71
  * - configured default project ID if set
@@ -83,10 +77,10 @@ export class ProjectsManager {
83
77
  if (this.#defaultProjectId) {
84
78
  return this.#defaultProjectId;
85
79
  }
86
- if (this._projects.length > 1) {
80
+ if (this.#projects.length > 1) {
87
81
  return void 0;
88
82
  }
89
- return this._projects[0]?.id ?? ProjectsManager.DefaultProjectId;
83
+ return this.#projects[0]?.id ?? ProjectsManager.DefaultProjectId;
90
84
  }
91
85
  set defaultProjectId(id) {
92
86
  if (id === this.#defaultProjectId) {
@@ -97,15 +91,15 @@ export class ProjectsManager {
97
91
  this.#defaultProjectId = void 0;
98
92
  return;
99
93
  }
100
- invariant(this._projects.find((p) => p.id === id), `Project "${id}" not found`);
94
+ invariant(this.#projects.find((p) => p.id === id), `Project "${id}" not found`);
101
95
  logger.debug`set default project ID to ${id}`;
102
96
  this.#defaultProjectId = id;
103
97
  }
104
98
  get all() {
105
- if (hasAtLeast(this._projects, 1)) {
99
+ if (hasAtLeast(this.#projects, 1)) {
106
100
  const ids = [
107
- ...map(this._projects, prop("id")),
108
- ProjectsManager.DefaultProjectId
101
+ ...map(this.#projects, prop("id")),
102
+ DefaultProject.id
109
103
  ];
110
104
  if (this.#defaultProjectId) {
111
105
  const idx = ids.findIndex((p) => p === this.#defaultProjectId);
@@ -116,11 +110,11 @@ export class ProjectsManager {
116
110
  }
117
111
  return ids;
118
112
  }
119
- return [ProjectsManager.DefaultProjectId];
113
+ return [DefaultProject.id];
120
114
  }
121
115
  getProject(arg) {
122
116
  const id = typeof arg === "string" ? arg : arg.likec4ProjectId || this.belongsTo(arg);
123
- if (id === ProjectsManager.DefaultProjectId) {
117
+ if (id === DefaultProject.id) {
124
118
  let folderUri2;
125
119
  try {
126
120
  folderUri2 = this.services.workspace.WorkspaceManager.workspaceUri;
@@ -130,14 +124,14 @@ export class ProjectsManager {
130
124
  }
131
125
  return {
132
126
  id: ProjectsManager.DefaultProjectId,
133
- config: this.defaultGlobalProject.config,
127
+ config: DefaultProject.config,
134
128
  folderUri: folderUri2
135
129
  };
136
130
  }
137
131
  const {
138
132
  config,
139
133
  folderUri
140
- } = nonNullable(this._projects.find((p) => p.id === id), `Project "${id}" not found`);
134
+ } = nonNullable(this.#projects.find((p) => p.id === id), `Project "${id}" not found`);
141
135
  return {
142
136
  id,
143
137
  folderUri,
@@ -154,16 +148,16 @@ export class ProjectsManager {
154
148
  return this.defaultProjectId ?? ProjectsManager.DefaultProjectId;
155
149
  }
156
150
  if (projectId) {
157
- invariant(this.projectIdToFolder.has(projectId), `Project ID ${projectId} is not registered`);
151
+ invariant(this.#projectIdToFolder.has(projectId), `Project ID ${projectId} is not registered`);
158
152
  return projectId;
159
153
  }
160
154
  return nonNullable(
161
155
  this.defaultProjectId,
162
- () => `Specify exact project, known: [${[...this.projectIdToFolder.keys()].join(", ")}]`
156
+ () => `Specify exact project, known: [${[...this.#projectIdToFolder.keys()].join(", ")}]`
163
157
  );
164
158
  }
165
159
  hasMultipleProjects() {
166
- return this._projects.length > 1;
160
+ return this.#projects.length > 1;
167
161
  }
168
162
  /**
169
163
  * Checks if the specified document should be excluded from processing.
@@ -174,10 +168,10 @@ export class ProjectsManager {
174
168
  const project = this.findProjectForDocument(docUriAsString);
175
169
  return project.exclude ? project.exclude(withoutProtocol(docUriAsString)) : false;
176
170
  }
177
- let isExcluded = this.excludedDocuments.get(document);
171
+ let isExcluded = this.#excludedDocuments.get(document);
178
172
  if (isExcluded === void 0) {
179
173
  isExcluded = this.checkIfExcluded(document.uri);
180
- this.excludedDocuments.set(document, isExcluded);
174
+ this.#excludedDocuments.set(document, isExcluded);
181
175
  }
182
176
  return isExcluded;
183
177
  }
@@ -190,7 +184,7 @@ export class ProjectsManager {
190
184
  const filename = parseFilename(entry.toString(), { strict: false })?.toLowerCase();
191
185
  const isConfigFile = !!filename && isLikeC4Config(filename);
192
186
  if (isConfigFile) {
193
- if (this.defaultGlobalProject.exclude(entry.path)) {
187
+ if (DefaultProject.exclude(entry.path)) {
194
188
  logger.debug`exclude config file ${entry.path}`;
195
189
  return false;
196
190
  }
@@ -202,20 +196,15 @@ export class ProjectsManager {
202
196
  *
203
197
  * @param entry The file system entry to check
204
198
  */
205
- async loadConfigFile(entry) {
206
- if (entry.isDirectory) {
207
- return void 0;
208
- }
209
- if (this.isConfigFile(entry.uri)) {
199
+ async registerConfigFile(configFile) {
200
+ if (this.isConfigFile(configFile)) {
210
201
  try {
211
- return await this.registerProject(entry.uri);
202
+ return await this.registerProject(configFile);
212
203
  } catch (error) {
213
204
  this.services.lsp.Connection?.window.showErrorMessage(
214
- `LikeC4: Failed to register project at ${entry.uri.toString()}
215
-
216
- ${loggable(error)}`
205
+ `LikeC4: Failed to register project at ${configFile.fsPath}`
217
206
  );
218
- logger.warn("Failed to register project at {uri}", { uri: entry.uri.toString(), error });
207
+ logger.warn("Failed to register project at {uri}", { uri: configFile.fsPath, error });
219
208
  return void 0;
220
209
  }
221
210
  }
@@ -228,22 +217,28 @@ ${loggable(error)}`
228
217
  async registerProject(opts) {
229
218
  if (URI.isUri(opts)) {
230
219
  const configFile = opts;
231
- const config2 = await this.services.workspace.FileSystemProvider.loadProjectConfig(configFile);
220
+ const config = await this.services.workspace.FileSystemProvider.loadProjectConfig(configFile);
232
221
  const path = joinRelativeURL(configFile.path, "..");
233
- const folderUri2 = configFile.with({ path });
234
- return await this.registerProject({ config: config2, folderUri: folderUri2 });
222
+ const folderUri = configFile.with({ path });
223
+ return this._registerProject({ config, folderUri });
235
224
  }
236
- const config = pickBy(validateProjectConfig(opts.config), isTruthy);
237
- const { folderUri } = opts;
238
- const folder = ProjectFolder(folderUri);
239
- let project = this._projects.find((p) => p.folder === folder);
225
+ return this._registerProject(opts);
226
+ }
227
+ /**
228
+ * Registers (or reloads) likec4 project by config file or config object.
229
+ * If there is some project registered at same folder, it will be reloaded.
230
+ */
231
+ _registerProject(opts) {
232
+ const config = validateProjectConfig(opts.config);
233
+ const folder = ProjectFolder(opts.folderUri);
234
+ let project = this.#projects.find((p) => p.folder === folder);
240
235
  if (project && deepEqual(project.config, config)) {
241
236
  return project;
242
237
  }
243
- let mustReset = !!project && !deepEqual(project.config, config);
238
+ let mustReset = false;
244
239
  let id;
245
240
  if (!project) {
246
- if (this.projectIdToFolder.has(config.name)) {
241
+ if (this.#projectIdToFolder.has(config.name)) {
247
242
  logger.warn`Project "${config.name}" already exists, generating unique ID`;
248
243
  }
249
244
  id = this.uniqueProjectId(config.name);
@@ -253,17 +248,18 @@ ${loggable(error)}`
253
248
  folder,
254
249
  folderUri: URI.parse(folder)
255
250
  };
256
- mustReset = this._projects.some((p) => p.folder.startsWith(folder) || folder.startsWith(p.folder));
257
- this._projects = pipe(
258
- [...this._projects, project],
251
+ mustReset = this.#projects.some((p) => p.folder.startsWith(folder) || folder.startsWith(p.folder));
252
+ this.#projects = pipe(
253
+ [...this.#projects, project],
259
254
  sortBy(
260
255
  [({ folder: folder2 }) => withoutProtocol(folder2).split("/").length, "desc"]
261
256
  )
262
257
  );
263
258
  logger.info`register project ${project.id} folder: ${folder}`;
264
259
  } else {
260
+ mustReset = true;
265
261
  if (project.config.name !== config.name) {
266
- this.projectIdToFolder.delete(project.id);
262
+ this.#projectIdToFolder.delete(project.id);
267
263
  logger.info`unregister project ${project.id} folder: ${folder}`;
268
264
  id = this.uniqueProjectId(config.name);
269
265
  project.id = id;
@@ -275,11 +271,11 @@ ${loggable(error)}`
275
271
  project.config = config;
276
272
  }
277
273
  if (isNullish(config.exclude)) {
278
- project.exclude = this.defaultGlobalProject.exclude;
274
+ project.exclude = DefaultProject.exclude;
279
275
  } else if (hasAtLeast(config.exclude, 1)) {
280
276
  project.exclude = picomatch(config.exclude, { dot: true });
281
277
  }
282
- this.projectIdToFolder.set(project.id, folder);
278
+ this.#projectIdToFolder.set(project.id, folder);
283
279
  if (mustReset) {
284
280
  this.resetProjectIds();
285
281
  }
@@ -289,24 +285,14 @@ ${loggable(error)}`
289
285
  const documentUri = normalizeUri(document);
290
286
  return this.findProjectForDocument(documentUri).id;
291
287
  }
292
- async reloadProjects(token) {
288
+ async reloadProjects() {
293
289
  const folders = this.services.workspace.WorkspaceManager.workspaceFolders;
294
290
  if (!folders) {
295
291
  logger.warn("No workspace folders found");
296
292
  return;
297
293
  }
298
- if (this.reloadProjectsLimiter.size + this.reloadProjectsLimiter.pending > 0) {
299
- logger.debug`reload projects is already queued`;
300
- return;
301
- }
302
- this.reloadProjectsLimiter.add(async () => {
303
- await delay(100);
304
- });
305
- this.reloadProjectsLimiter.add(async () => {
306
- if (token) {
307
- await interruptAndCheck(token);
308
- }
309
- logger.debug`reload projects`;
294
+ logger.debug`schedule reload projects`;
295
+ await this.services.workspace.WorkspaceLock.write(async (cancelToken) => {
310
296
  const configFiles = [];
311
297
  for (const folder of folders) {
312
298
  try {
@@ -320,47 +306,50 @@ ${loggable(error)}`
320
306
  logger.error("Failed to scanProjectFiles, {folder}", { folder: folder.uri, error });
321
307
  }
322
308
  }
323
- if (configFiles.length === 0 && this._projects.length !== 0) {
309
+ if (configFiles.length === 0 && this.#projects.length !== 0) {
324
310
  logger.warning("No config files found, but some projects were registered before");
325
311
  }
326
- this._projects = [];
327
- this.projectIdToFolder.clear();
312
+ await delay(150);
313
+ await interruptAndCheck(cancelToken);
314
+ logger.debug`start reload projects`;
315
+ this.#projects = [];
316
+ this.#projectIdToFolder.clear();
328
317
  for (const entry of configFiles) {
329
318
  try {
330
- await this.loadConfigFile(entry);
319
+ await this.registerConfigFile(entry.uri);
331
320
  } catch (error) {
332
321
  logger.error("Failed to load config file {uri}", { uri: entry.uri.toString(), error });
333
322
  }
334
323
  }
335
324
  this.resetProjectIds();
336
- await this.rebuidDocuments();
325
+ await this.rebuidDocuments(cancelToken);
337
326
  });
338
327
  }
339
328
  uniqueProjectId(name) {
340
329
  let id = name;
341
330
  let i = 1;
342
- while (this.projectIdToFolder.has(id)) {
331
+ while (this.#projectIdToFolder.has(id)) {
343
332
  id = `${name}-${i++}`;
344
333
  }
345
334
  return id;
346
335
  }
347
336
  resetProjectIds() {
348
- if (this.#defaultProjectId && !this.projectIdToFolder.has(this.#defaultProjectId)) {
337
+ if (this.#defaultProjectId && !this.#projectIdToFolder.has(this.#defaultProjectId)) {
349
338
  this.#defaultProjectId = void 0;
350
339
  }
351
340
  this.mappingsToProject.clear();
352
- this.excludedDocuments = /* @__PURE__ */ new WeakMap();
341
+ this.#excludedDocuments = /* @__PURE__ */ new WeakMap();
353
342
  this.services.workspace.LangiumDocuments.resetProjectIds();
354
343
  }
355
- async rebuidDocuments() {
344
+ async rebuidDocuments(cancelToken) {
356
345
  const docs = this.services.workspace.LangiumDocuments.all.map((d) => d.uri).toArray();
357
346
  logger.info("invalidate and rebuild all {docs} documents", { docs: docs.length });
358
- await this.services.workspace.DocumentBuilder.update(docs, []);
347
+ await this.services.workspace.DocumentBuilder.update(docs, [], cancelToken);
359
348
  }
360
349
  findProjectForDocument(documentUri) {
361
350
  return this.mappingsToProject.get(documentUri, () => {
362
- const project = this._projects.find(({ folder }) => documentUri.startsWith(folder));
363
- return project ?? this.defaultGlobalProject;
351
+ const project = this.#projects.find(({ folder }) => documentUri.startsWith(folder));
352
+ return project ?? DefaultProject;
364
353
  });
365
354
  }
366
355
  // The mapping between document URIs and their corresponding project ID
@@ -36,7 +36,7 @@ export class LikeC4WorkspaceManager extends DefaultWorkspaceManager {
36
36
  const projects = this.services.workspace.ProjectsManager;
37
37
  for (const entry of configFiles) {
38
38
  try {
39
- await projects.loadConfigFile(entry);
39
+ await projects.registerConfigFile(entry.uri);
40
40
  } catch (error) {
41
41
  logWarnError(error);
42
42
  }
@@ -0,0 +1,4 @@
1
+ {
2
+ "types": "../dist/likec4lib.d.ts",
3
+ "module": "../dist/likec4lib.mjs"
4
+ }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@likec4/language-server",
3
3
  "description": "LikeC4 Language Server",
4
- "version": "1.41.0",
4
+ "version": "1.42.1",
5
5
  "license": "MIT",
6
6
  "bugs": "https://github.com/likec4/likec4/issues",
7
7
  "homepage": "https://likec4.dev",
@@ -9,6 +9,7 @@
9
9
  "files": [
10
10
  "bin",
11
11
  "dist",
12
+ "**/package.json",
12
13
  "!**/__mocks__/",
13
14
  "!**/__tests__/",
14
15
  "!**/*.spec.*",
@@ -20,7 +21,7 @@
20
21
  "directory": "packages/language-server"
21
22
  },
22
23
  "engines": {
23
- "node": ">=20.19.4"
24
+ "node": "^20.19.0 || >=22.18.0"
24
25
  },
25
26
  "engineStrict": true,
26
27
  "type": "module",
@@ -31,17 +32,9 @@
31
32
  "exports": {
32
33
  ".": {
33
34
  "sources": "./src/index.ts",
34
- "node": {
35
- "sources": "./src/index.ts",
35
+ "default": {
36
36
  "types": "./dist/index.d.ts",
37
- "import": "./dist/index.mjs",
38
37
  "default": "./dist/index.mjs"
39
- },
40
- "default": {
41
- "sources": "./src/browser.ts",
42
- "types": "./dist/browser.d.ts",
43
- "import": "./dist/browser.mjs",
44
- "default": "./dist/browser.mjs"
45
38
  }
46
39
  },
47
40
  "./likec4lib": {
@@ -88,38 +81,37 @@
88
81
  "esbuild": "0.25.9"
89
82
  },
90
83
  "devDependencies": {
91
- "@hono/node-server": "^1.19.1",
84
+ "@hono/node-server": "^1.19.3",
92
85
  "@modelcontextprotocol/sdk": "^1.17.5",
93
86
  "@msgpack/msgpack": "^3.1.2",
94
87
  "@smithy/util-base64": "^4.0.0",
95
- "@types/chroma-js": "^3.1.1",
96
88
  "@types/natural-compare-lite": "^1.4.2",
97
89
  "@types/node": "~20.19.11",
98
90
  "@types/picomatch": "^4.0.2",
99
91
  "@types/vscode": "^1.84.0",
100
92
  "@types/which": "^3.0.4",
101
93
  "chokidar": "^4.0.3",
102
- "chroma-js": "^3.1.2",
103
94
  "defu": "^6.1.4",
104
95
  "esm-env": "^1.2.2",
105
96
  "fast-equals": "^5.2.2",
106
97
  "fdir": "6.4.0",
107
98
  "fetch-to-node": "^2.1.0",
108
- "hono": "^4.9.6",
99
+ "hono": "^4.9.8",
109
100
  "indent-string": "^5.0.0",
110
101
  "json5": "^2.2.3",
111
102
  "langium": "3.5.0",
112
103
  "langium-cli": "3.5.2",
113
104
  "natural-compare-lite": "^1.4.0",
105
+ "oxlint": "1.18.0",
114
106
  "p-debounce": "4.0.0",
115
107
  "p-queue": "8.1.1",
116
108
  "p-timeout": "6.1.4",
117
109
  "picomatch": "^4.0.3",
118
110
  "pretty-ms": "^9.2.0",
119
- "remeda": "^2.30.0",
111
+ "remeda": "^2.31.1",
120
112
  "strip-indent": "^4.0.0",
121
- "tsx": "4.20.5",
122
- "turbo": "2.5.6",
113
+ "tsx": "4.20.6",
114
+ "turbo": "2.5.8",
123
115
  "type-fest": "^4.41.0",
124
116
  "typescript": "5.9.2",
125
117
  "ufo": "1.6.1",
@@ -131,13 +123,14 @@
131
123
  "vscode-languageserver-types": "3.17.5",
132
124
  "vscode-uri": "3.1.0",
133
125
  "which": "^5.0.0",
134
- "zod": "3.25.76",
135
- "@likec4/core": "1.41.0",
136
- "@likec4/config": "1.41.0",
137
- "@likec4/layouts": "1.41.0",
138
- "@likec4/log": "1.41.0",
139
- "@likec4/icons": "1.41.0",
140
- "@likec4/tsconfig": "1.41.0"
126
+ "zod": "^3.25.76",
127
+ "@likec4/core": "1.42.1",
128
+ "@likec4/config": "1.42.1",
129
+ "@likec4/icons": "1.42.1",
130
+ "@likec4/log": "1.42.1",
131
+ "@likec4/layouts": "1.42.1",
132
+ "@likec4/tsconfig": "1.42.1",
133
+ "@likec4/devops": "1.42.0"
141
134
  },
142
135
  "scripts": {
143
136
  "typecheck": "tsc -b --verbose",
@@ -148,12 +141,15 @@
148
141
  "watch:ts": "tsc --watch",
149
142
  "generate": "langium generate && tsx scripts/generate-icons.ts",
150
143
  "dev": "run-p \"watch:*\"",
151
- "lint": "run -T eslint src/ --fix",
144
+ "lint": "oxlint --type-aware",
145
+ "lint:fix": "oxlint --type-aware --fix",
152
146
  "lint:package": "pnpx publint ./package.tgz",
153
- "clean": "pnpm rimraf dist contrib lib src/generated src/generated-lib",
147
+ "clean": "likec4ops clean contrib src/generated src/generated-lib",
154
148
  "test": "vitest run --no-isolate",
155
149
  "test-dbg": "vitest run --no-isolate -t formating",
156
150
  "vitest:ui": "vitest --no-isolate --ui",
157
151
  "test:watch": "vitest"
158
- }
152
+ },
153
+ "types": "dist/index.d.ts",
154
+ "module": "dist/index.mjs"
159
155
  }
@@ -0,0 +1,4 @@
1
+ {
2
+ "types": "../dist/protocol.d.ts",
3
+ "module": "../dist/protocol.mjs"
4
+ }
@@ -1,2 +0,0 @@
1
- import type { LikeC4View } from '@likec4/core';
2
- export declare function resolveRelativePaths(views: LikeC4View[]): LikeC4View[];
@@ -1,78 +0,0 @@
1
- import { compareNatural, invariant } from "@likec4/core/utils";
2
- import { filter, hasAtLeast, isTruthy, map, pipe, unique } from "remeda";
3
- import { parsePath } from "ufo";
4
- function commonAncestorPath(views, sep = "/") {
5
- const uniqURIs = pipe(
6
- views,
7
- map((v) => v.docUri),
8
- filter(isTruthy),
9
- unique()
10
- );
11
- if (uniqURIs.length === 0) return "";
12
- if (uniqURIs.length === 1) {
13
- const parts2 = parsePath(uniqURIs[0]).pathname.split(sep);
14
- if (parts2.length <= 1) return sep;
15
- parts2.pop();
16
- return parts2.join(sep) + sep;
17
- }
18
- invariant(hasAtLeast(uniqURIs, 2), "Expected at least 2 unique URIs");
19
- const [baseUri, ...tail] = uniqURIs;
20
- const parts = parsePath(baseUri).pathname.split(sep);
21
- let endOfPrefix = parts.length;
22
- for (const uri of tail) {
23
- if (uri === baseUri) {
24
- continue;
25
- }
26
- const compare = parsePath(uri).pathname.split(sep);
27
- for (let i = 0; i < endOfPrefix; i++) {
28
- if (compare[i] !== parts[i]) {
29
- endOfPrefix = i;
30
- }
31
- }
32
- if (endOfPrefix === 0) return "";
33
- }
34
- const prefix = parts.slice(0, endOfPrefix).join(sep);
35
- return prefix.endsWith(sep) ? prefix : prefix + sep;
36
- }
37
- export function resolveRelativePaths(views) {
38
- const sep = "/";
39
- const commonPrefix = commonAncestorPath(views, sep);
40
- return views.map((view) => {
41
- if (!view.docUri) {
42
- return {
43
- view,
44
- parts: []
45
- };
46
- }
47
- let path = parsePath(view.docUri).pathname;
48
- if (commonPrefix.length > 0) {
49
- invariant(
50
- path.startsWith(commonPrefix),
51
- `Expect path "${path}" to start with common prefix: "${commonPrefix}"`
52
- );
53
- path = path.slice(commonPrefix.length);
54
- } else {
55
- path = path.includes(sep) ? path.slice(path.lastIndexOf(sep) + 1) : path;
56
- }
57
- return {
58
- view,
59
- parts: path.split(sep)
60
- };
61
- }).sort((a, b) => {
62
- if (a.parts.length !== b.parts.length) {
63
- return a.parts.length - b.parts.length;
64
- }
65
- for (let i = 0; i < a.parts.length; i++) {
66
- const compare = compareNatural(a.parts[i], b.parts[i]);
67
- if (compare !== 0) {
68
- return compare;
69
- }
70
- }
71
- return 0;
72
- }).map(({ parts, view }) => {
73
- return {
74
- ...view,
75
- relativePath: parts.join(sep)
76
- };
77
- });
78
- }