@likec4/language-server 0.60.3 → 1.0.0-next.0

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 (39) hide show
  1. package/dist/Rpc.d.ts +4 -1
  2. package/dist/Rpc.js +69 -44
  3. package/dist/ast.d.ts +1 -0
  4. package/dist/ast.js +20 -0
  5. package/dist/elementRef.d.ts +0 -1
  6. package/dist/lsp/index.d.ts +1 -1
  7. package/dist/lsp/index.js +1 -1
  8. package/dist/model/fqn-computation.js +2 -2
  9. package/dist/model/model-builder.d.ts +2 -2
  10. package/dist/model/model-builder.js +14 -8
  11. package/dist/model/model-locator.d.ts +6 -1
  12. package/dist/model/model-locator.js +31 -20
  13. package/dist/model/model-parser.js +19 -17
  14. package/dist/model-change/ModelChanges.d.ts +15 -0
  15. package/dist/model-change/ModelChanges.js +94 -0
  16. package/dist/model-change/changeViewLayout.d.ts +13 -0
  17. package/dist/model-change/changeViewLayout.js +30 -0
  18. package/dist/model-change/changeViewStyle.d.ts +15 -0
  19. package/dist/model-change/changeViewStyle.js +123 -0
  20. package/dist/module.d.ts +2 -0
  21. package/dist/module.js +2 -0
  22. package/dist/protocol.d.ts +34 -6
  23. package/dist/protocol.js +6 -3
  24. package/dist/references/scope-computation.d.ts +1 -1
  25. package/dist/reset.d.ts +2 -0
  26. package/dist/shared/index.d.ts +1 -1
  27. package/dist/shared/index.js +1 -1
  28. package/dist/test/testServices.d.ts +1 -1
  29. package/dist/test/testServices.js +1 -1
  30. package/dist/utils/index.d.ts +2 -0
  31. package/dist/utils/index.js +1 -0
  32. package/dist/utils/stringHash.d.ts +2 -0
  33. package/dist/utils/stringHash.js +5 -0
  34. package/dist/validation/view-predicates/incoming.js +2 -2
  35. package/dist/validation/view-predicates/index.d.ts +1 -1
  36. package/dist/validation/view-predicates/index.js +1 -1
  37. package/dist/validation/view-predicates/outgoing.js +1 -1
  38. package/dist/view-utils/resolve-relative-paths.js +1 -1
  39. package/package.json +20 -11
package/dist/Rpc.d.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import type { LikeC4Services } from './module';
2
- export declare class Rpc {
2
+ import { Disposable } from 'langium';
3
+ export declare class Rpc implements Disposable {
3
4
  private services;
5
+ private disposables;
4
6
  constructor(services: LikeC4Services);
5
7
  init(): void;
8
+ dispose(): void;
6
9
  }
7
10
  //# sourceMappingURL=Rpc.d.ts.map
package/dist/Rpc.js CHANGED
@@ -3,14 +3,24 @@ import { logError, logger } from "./logger.js";
3
3
  import { nonexhaustive } from "@likec4/core";
4
4
  import { URI, UriUtils } from "langium";
5
5
  import { isLikeC4LangiumDocument } from "./ast.js";
6
- import { buildDocuments, computeView, fetchModel, fetchRawModel, locate, onDidChangeModel } from "./protocol.js";
6
+ import {
7
+ buildDocuments,
8
+ changeView,
9
+ computeView,
10
+ fetchComputedModel,
11
+ fetchModel,
12
+ locate,
13
+ onDidChangeModel
14
+ } from "./protocol.js";
7
15
  export class Rpc {
8
16
  constructor(services) {
9
17
  this.services = services;
10
18
  }
19
+ disposables = [];
11
20
  init() {
12
21
  const modelBuilder = this.services.likec4.ModelBuilder;
13
22
  const modelLocator = this.services.likec4.ModelLocator;
23
+ const modelEditor = this.services.likec4.ModelChanges;
14
24
  const connection = this.services.shared.lsp.Connection;
15
25
  if (!connection) {
16
26
  logger.info(`[ServerRpc] no connection, not initializing`);
@@ -19,51 +29,66 @@ export class Rpc {
19
29
  logger.info(`[ServerRpc] init`);
20
30
  const LangiumDocuments = this.services.shared.workspace.LangiumDocuments;
21
31
  const DocumentBuilder = this.services.shared.workspace.DocumentBuilder;
22
- modelBuilder.onModelParsed(
23
- debounceFunction(
24
- () => void connection.sendNotification(onDidChangeModel, "").catch(logError),
25
- {
26
- before: true,
27
- after: true,
28
- wait: 250,
29
- maxWait: 1e3
30
- }
31
- )
32
- );
33
- connection.onRequest(fetchModel, async (cancelToken) => {
34
- const model = await modelBuilder.buildModel(cancelToken);
35
- return { model };
36
- });
37
- connection.onRequest(fetchRawModel, async (cancelToken) => {
38
- const rawmodel = await modelBuilder.buildRawModel(cancelToken);
39
- return { rawmodel };
40
- });
41
- connection.onRequest(computeView, async ({ viewId }, cancelToken) => {
42
- const view = await modelBuilder.computeView(viewId, cancelToken);
43
- return { view };
44
- });
45
- connection.onRequest(buildDocuments, async ({ docs }, cancelToken) => {
46
- const changed = docs.map((d) => URI.parse(d));
47
- const notChanged = (uri) => changed.every((c) => !UriUtils.equals(c, uri));
48
- const deleted = LangiumDocuments.all.filter((d) => isLikeC4LangiumDocument(d) && notChanged(d.uri)).map((d) => d.uri).toArray();
49
- logger.debug(
50
- `[ServerRpc] received request to build:
32
+ this.disposables.push(
33
+ modelBuilder.onModelParsed(
34
+ debounceFunction(
35
+ () => void connection.sendNotification(onDidChangeModel, "").catch(logError),
36
+ {
37
+ before: true,
38
+ after: true,
39
+ wait: 250,
40
+ maxWait: 1e3
41
+ }
42
+ )
43
+ ),
44
+ connection.onRequest(fetchComputedModel, async (cancelToken) => {
45
+ const model = await modelBuilder.buildComputedModel(cancelToken);
46
+ return { model };
47
+ }),
48
+ connection.onRequest(fetchModel, async (cancelToken) => {
49
+ const model = await modelBuilder.buildModel(cancelToken);
50
+ return { model };
51
+ }),
52
+ connection.onRequest(computeView, async ({ viewId }, cancelToken) => {
53
+ const view = await modelBuilder.computeView(viewId, cancelToken);
54
+ return { view };
55
+ }),
56
+ connection.onRequest(buildDocuments, async ({ docs }, cancelToken) => {
57
+ const changed = docs.map((d) => URI.parse(d));
58
+ const notChanged = (uri) => changed.every((c) => !UriUtils.equals(c, uri));
59
+ const deleted = LangiumDocuments.all.filter((d) => isLikeC4LangiumDocument(d) && notChanged(d.uri)).map((d) => d.uri).toArray();
60
+ logger.debug(
61
+ `[ServerRpc] received request to build:
51
62
  changed (total ${changed.length}):${docs.map((d) => "\n - " + d).join("")}
52
63
  deleted (total ${deleted.length}):${deleted.map((d) => "\n - " + d.toString()).join("\n")}`
53
- );
54
- await DocumentBuilder.update(changed, deleted, cancelToken);
55
- });
56
- connection.onRequest(locate, (params) => {
57
- if ("element" in params) {
58
- return modelLocator.locateElement(params.element, params.property ?? "name");
59
- }
60
- if ("relation" in params) {
61
- return modelLocator.locateRelation(params.relation);
62
- }
63
- if ("view" in params) {
64
- return modelLocator.locateView(params.view);
64
+ );
65
+ await DocumentBuilder.update(changed, deleted, cancelToken);
66
+ }),
67
+ connection.onRequest(locate, (params) => {
68
+ if ("element" in params) {
69
+ return modelLocator.locateElement(params.element, params.property ?? "name");
70
+ }
71
+ if ("relation" in params) {
72
+ return modelLocator.locateRelation(params.relation);
73
+ }
74
+ if ("view" in params) {
75
+ return modelLocator.locateView(params.view);
76
+ }
77
+ nonexhaustive(params);
78
+ }),
79
+ connection.onRequest(changeView, async (request, _cancelToken) => {
80
+ return await modelEditor.applyChange(request);
81
+ })
82
+ );
83
+ }
84
+ dispose() {
85
+ let item;
86
+ while (item = this.disposables.pop()) {
87
+ try {
88
+ item.dispose();
89
+ } catch (e) {
90
+ logError(e);
65
91
  }
66
- nonexhaustive(params);
67
- });
92
+ }
68
93
  }
69
94
  }
package/dist/ast.d.ts CHANGED
@@ -136,4 +136,5 @@ export declare function toRelationshipStyleExcludeDefaults(props?: ast.Specifica
136
136
  color?: "amber" | "blue" | "green" | "indigo" | "muted" | "primary" | "red" | "secondary" | "sky" | "slate";
137
137
  };
138
138
  export declare function toAutoLayout(direction: ast.ViewLayoutDirection): c4.ViewRuleAutoLayout['autoLayout'];
139
+ export declare function toAstViewLayoutDirection(c4: c4.ViewRuleAutoLayout['autoLayout']): ast.ViewLayoutDirection;
139
140
  //# sourceMappingURL=ast.d.ts.map
package/dist/ast.js CHANGED
@@ -236,5 +236,25 @@ export function toAutoLayout(direction) {
236
236
  case "RightLeft": {
237
237
  return "RL";
238
238
  }
239
+ default:
240
+ nonexhaustive(direction);
241
+ }
242
+ }
243
+ export function toAstViewLayoutDirection(c4) {
244
+ switch (c4) {
245
+ case "TB": {
246
+ return "TopBottom";
247
+ }
248
+ case "BT": {
249
+ return "BottomTop";
250
+ }
251
+ case "LR": {
252
+ return "LeftRight";
253
+ }
254
+ case "RL": {
255
+ return "RightLeft";
256
+ }
257
+ default:
258
+ nonexhaustive(c4);
239
259
  }
240
260
  }
@@ -2,7 +2,6 @@ import { type c4 } from '@likec4/core';
2
2
  import type { ast } from './ast';
3
3
  /**
4
4
  * Returns referenced AST Element
5
- *
6
5
  */
7
6
  export declare function elementRef(node: ast.ElementRef | ast.FqnElementRef): ast.Element | undefined;
8
7
  /**
@@ -1,7 +1,7 @@
1
1
  export * from './CodeLensProvider';
2
+ export * from './DocumentHighlightProvider';
2
3
  export * from './DocumentLinkProvider';
3
4
  export * from './DocumentSymbolProvider';
4
5
  export * from './HoverProvider';
5
6
  export * from './SemanticTokenProvider';
6
- export * from './DocumentHighlightProvider';
7
7
  //# sourceMappingURL=index.d.ts.map
package/dist/lsp/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from "./CodeLensProvider.js";
2
+ export * from "./DocumentHighlightProvider.js";
2
3
  export * from "./DocumentLinkProvider.js";
3
4
  export * from "./DocumentSymbolProvider.js";
4
5
  export * from "./HoverProvider.js";
5
6
  export * from "./SemanticTokenProvider.js";
6
- export * from "./DocumentHighlightProvider.js";
@@ -1,7 +1,7 @@
1
1
  import { AsFqn, nonexhaustive } from "@likec4/core";
2
2
  import { MultiMap } from "langium";
3
- import { isEmpty, isNil } from "remeda";
4
- import { ElementOps, ast } from "../ast.js";
3
+ import { isEmpty, isNullish as isNil } from "remeda";
4
+ import { ast, ElementOps } from "../ast.js";
5
5
  import { getFqnElementRef } from "../elementRef.js";
6
6
  export function computeDocumentFqn(document, services) {
7
7
  const c4fqns = document.c4fqns = new MultiMap();
@@ -8,9 +8,9 @@ export declare class LikeC4ModelBuilder {
8
8
  private langiumDocuments;
9
9
  private listeners;
10
10
  constructor(services: LikeC4Services);
11
- buildRawModel(cancelToken?: CancellationToken): Promise<c4.LikeC4RawModel | null>;
12
- private previousViews;
13
11
  buildModel(cancelToken?: CancellationToken): Promise<c4.LikeC4Model | null>;
12
+ private previousViews;
13
+ buildComputedModel(cancelToken?: CancellationToken): Promise<c4.LikeC4ComputedModel | null>;
14
14
  computeView(viewId: ViewID, cancelToken?: CancellationToken): Promise<c4.ComputedView | null>;
15
15
  onModelParsed(callback: ModelParsedListener): Disposable;
16
16
  private documents;
@@ -4,6 +4,7 @@ import {
4
4
  parentFqn
5
5
  } from "@likec4/core";
6
6
  import { computeView, LikeC4ModelGraph } from "@likec4/graph";
7
+ import { deepEqual as eq } from "fast-equals";
7
8
  import { DocumentState, interruptAndCheck } from "langium";
8
9
  import * as R from "remeda";
9
10
  import { Disposable } from "vscode-languageserver";
@@ -183,7 +184,8 @@ ${printDocs(docs)}`);
183
184
  }
184
185
  langiumDocuments;
185
186
  listeners = [];
186
- async buildRawModel(cancelToken) {
187
+ async buildModel(cancelToken) {
188
+ await this.services.shared.workspace.DocumentBuilder.waitUntil(DocumentState.Validated, cancelToken);
187
189
  return await this.services.shared.workspace.WorkspaceLock.read(async () => {
188
190
  if (cancelToken) {
189
191
  await interruptAndCheck(cancelToken);
@@ -202,11 +204,14 @@ ${printDocs(docs)}`);
202
204
  });
203
205
  }
204
206
  previousViews = {};
205
- async buildModel(cancelToken) {
206
- const model = await this.buildRawModel(cancelToken);
207
+ async buildComputedModel(cancelToken) {
208
+ const model = await this.buildModel(cancelToken);
207
209
  if (!model) {
208
210
  return null;
209
211
  }
212
+ if (cancelToken) {
213
+ await interruptAndCheck(cancelToken);
214
+ }
210
215
  const cache = this.services.WorkspaceCache;
211
216
  const viewsCache = this.services.WorkspaceCache;
212
217
  return cache.get(MODEL_CACHE, () => {
@@ -223,7 +228,7 @@ ${printDocs(docs)}`);
223
228
  assignNavigateTo(allViews);
224
229
  const views = R.mapToObj(allViews, (v) => {
225
230
  const previous = this.previousViews[v.id];
226
- const view = previous && R.equals(v, previous) ? previous : v;
231
+ const view = previous && eq(v, previous) ? previous : v;
227
232
  viewsCache.set(computedViewKey(v.id), view);
228
233
  return [v.id, view];
229
234
  });
@@ -236,12 +241,15 @@ ${printDocs(docs)}`);
236
241
  });
237
242
  }
238
243
  async computeView(viewId, cancelToken) {
239
- const model = await this.buildRawModel(cancelToken);
244
+ const model = await this.buildModel(cancelToken);
240
245
  const view = model?.views[viewId];
241
246
  if (!view) {
242
247
  logger.warn(`[ModelBuilder] Cannot find view ${viewId}`);
243
248
  return null;
244
249
  }
250
+ if (cancelToken) {
251
+ await interruptAndCheck(cancelToken);
252
+ }
245
253
  const cache = this.services.WorkspaceCache;
246
254
  return cache.get(computedViewKey(viewId), () => {
247
255
  const index = new LikeC4ModelGraph(model);
@@ -263,9 +271,7 @@ ${printDocs(docs)}`);
263
271
  }
264
272
  });
265
273
  const previous = this.previousViews[viewId];
266
- if (previous) {
267
- computedView = R.equals(computedView, previous) ? previous : computedView;
268
- }
274
+ computedView = previous && eq(computedView, previous) ? previous : computedView;
269
275
  this.previousViews[viewId] = computedView;
270
276
  return computedView;
271
277
  });
@@ -1,6 +1,6 @@
1
1
  import type { likec4 as c4 } from '@likec4/core';
2
2
  import type { Location } from 'vscode-languageserver-protocol';
3
- import type { ParsedAstElement } from '../ast';
3
+ import type { ParsedAstElement, ParsedLikeC4LangiumDocument } from '../ast';
4
4
  import { ast } from '../ast';
5
5
  import type { LikeC4Services } from '../module';
6
6
  export declare class LikeC4ModelLocator {
@@ -12,6 +12,11 @@ export declare class LikeC4ModelLocator {
12
12
  getParsedElement(astNode: ast.Element): ParsedAstElement | null;
13
13
  locateElement(fqn: c4.Fqn, property?: string): Location | null;
14
14
  locateRelation(relationId: c4.RelationID): Location | null;
15
+ locateViewAst(viewId: c4.ViewID): {
16
+ doc: ParsedLikeC4LangiumDocument;
17
+ view: import("../ast").ParsedAstElementView;
18
+ viewAst: ast.ElementView;
19
+ } | null;
15
20
  locateView(viewId: c4.ViewID): Location | null;
16
21
  }
17
22
  //# sourceMappingURL=model-locator.d.ts.map
@@ -73,36 +73,47 @@ export class LikeC4ModelLocator {
73
73
  }
74
74
  return null;
75
75
  }
76
- locateView(viewId) {
76
+ locateViewAst(viewId) {
77
77
  for (const doc of this.documents()) {
78
78
  const view = doc.c4Views.find((r) => r.id === viewId);
79
79
  if (!view) {
80
80
  continue;
81
81
  }
82
- const node = this.services.workspace.AstNodeLocator.getAstNode(
82
+ const viewAst = this.services.workspace.AstNodeLocator.getAstNode(
83
83
  doc.parseResult.value,
84
84
  view.astPath
85
85
  );
86
- if (!ast.isElementView(node)) {
87
- continue;
88
- }
89
- let targetNode = node.$cstNode;
90
- if (node.name) {
91
- targetNode = findNodeForProperty(node.$cstNode, "name") ?? targetNode;
92
- } else if ("viewOf" in node) {
93
- targetNode = findNodeForProperty(node.$cstNode, "viewOf") ?? targetNode;
94
- }
95
- if (!targetNode) {
96
- return null;
86
+ if (ast.isElementView(viewAst)) {
87
+ return {
88
+ doc,
89
+ view,
90
+ viewAst
91
+ };
97
92
  }
98
- return {
99
- uri: doc.uri.toString(),
100
- range: {
101
- start: targetNode.range.start,
102
- end: targetNode.range.start
103
- }
104
- };
105
93
  }
106
94
  return null;
107
95
  }
96
+ locateView(viewId) {
97
+ const res = this.locateViewAst(viewId);
98
+ if (!res) {
99
+ return null;
100
+ }
101
+ const node = res.viewAst;
102
+ let targetNode = node.$cstNode;
103
+ if (node.name) {
104
+ targetNode = findNodeForProperty(node.$cstNode, "name") ?? targetNode;
105
+ } else if ("viewOf" in node) {
106
+ targetNode = findNodeForProperty(node.$cstNode, "viewOf") ?? targetNode;
107
+ }
108
+ if (!targetNode) {
109
+ return null;
110
+ }
111
+ return {
112
+ uri: res.doc.uri.toString(),
113
+ range: {
114
+ start: targetNode.range.start,
115
+ end: targetNode.range.start
116
+ }
117
+ };
118
+ }
108
119
  }
@@ -1,6 +1,5 @@
1
1
  import { InvalidModelError, invariant, isNonEmptyArray, nonexhaustive } from "@likec4/core";
2
2
  import { AstUtils } from "langium";
3
- import objectHash from "object-hash";
4
3
  import { isTruthy } from "remeda";
5
4
  import stripIndent from "strip-indent";
6
5
  import {
@@ -18,6 +17,7 @@ import {
18
17
  } from "../ast.js";
19
18
  import { elementRef, getFqnElementRef } from "../elementRef.js";
20
19
  import { logError, logger, logWarnError } from "../logger.js";
20
+ import { stringHash } from "../utils/index.js";
21
21
  const { getDocument } = AstUtils;
22
22
  function toSingleLine(str) {
23
23
  return str ? removeIndent(str).split("\n").join(" ") : void 0;
@@ -142,11 +142,11 @@ export class LikeC4ModelParser {
142
142
  astNode.title ?? astNode.body?.props.find((p) => p.key === "title")?.value
143
143
  ) ?? "";
144
144
  const styleProp = astNode.body?.props.find(ast.isRelationStyleProperty);
145
- const id = objectHash({
145
+ const id = stringHash(
146
146
  astPath,
147
147
  source,
148
148
  target
149
- });
149
+ );
150
150
  return {
151
151
  id,
152
152
  astPath,
@@ -311,13 +311,23 @@ export class LikeC4ModelParser {
311
311
  const body = astNode.body;
312
312
  invariant(body, "ElementView body is not defined");
313
313
  const astPath = this.getAstNodePath(astNode);
314
+ let viewOf = null;
315
+ if ("viewOf" in astNode) {
316
+ const viewOfEl = elementRef(astNode.viewOf);
317
+ const _viewOf = viewOfEl && this.resolveFqn(viewOfEl);
318
+ if (!_viewOf) {
319
+ logger.warn("viewOf is not resolved: " + astNode.$cstNode?.text);
320
+ } else {
321
+ viewOf = _viewOf;
322
+ }
323
+ }
314
324
  let id = astNode.name;
315
325
  if (!id) {
316
- const doc = getDocument(astNode).uri.toString();
317
- id = objectHash({
318
- doc,
319
- astPath
320
- });
326
+ id = "view_" + stringHash(
327
+ getDocument(astNode).uri.toString(),
328
+ astPath,
329
+ viewOf ?? ""
330
+ );
321
331
  }
322
332
  const title = body.props.find((p) => p.key === "title")?.value;
323
333
  const description = body.props.find((p) => p.key === "description")?.value;
@@ -326,6 +336,7 @@ export class LikeC4ModelParser {
326
336
  const basic = {
327
337
  id,
328
338
  astPath,
339
+ ...viewOf && { viewOf },
329
340
  ...title && { title },
330
341
  ...description && { description },
331
342
  ...tags && { tags },
@@ -340,15 +351,6 @@ export class LikeC4ModelParser {
340
351
  })
341
352
  };
342
353
  ElementViewOps.writeId(astNode, basic.id);
343
- if ("viewOf" in astNode) {
344
- const viewOfEl = elementRef(astNode.viewOf);
345
- const viewOf = viewOfEl && this.resolveFqn(viewOfEl);
346
- invariant(viewOf, " viewOf is not resolved: " + astNode.$cstNode?.text);
347
- return {
348
- ...basic,
349
- viewOf
350
- };
351
- }
352
354
  if ("extends" in astNode) {
353
355
  const extendsView = astNode.extends.view.ref;
354
356
  invariant(extendsView?.name, "view extends is not resolved: " + astNode.$cstNode?.text);
@@ -0,0 +1,15 @@
1
+ import { Location, TextEdit } from 'vscode-languageserver-protocol';
2
+ import { type ParsedLikeC4LangiumDocument } from '../ast';
3
+ import type { LikeC4Services } from '../module';
4
+ import type { ChangeViewRequestParams } from '../protocol';
5
+ export declare class LikeC4ModelChanges {
6
+ private services;
7
+ private locator;
8
+ constructor(services: LikeC4Services);
9
+ applyChange(changeView: ChangeViewRequestParams): Promise<Location | null>;
10
+ protected convertToTextEdit({ viewId, changes }: ChangeViewRequestParams): {
11
+ doc: ParsedLikeC4LangiumDocument;
12
+ edits: TextEdit[];
13
+ };
14
+ }
15
+ //# sourceMappingURL=ModelChanges.d.ts.map
@@ -0,0 +1,94 @@
1
+ import { invariant, nonexhaustive } from "@likec4/core";
2
+ import { Range, TextDocumentEdit } from "vscode-languageserver-protocol";
3
+ import { changeViewLayout } from "./changeViewLayout.js";
4
+ import { changeViewStyle } from "./changeViewStyle.js";
5
+ function unionRangeOfAllEdits(edits) {
6
+ let start = Number.MAX_SAFE_INTEGER;
7
+ let end = Number.MIN_SAFE_INTEGER;
8
+ for (const edit of edits) {
9
+ start = Math.min(start, edit.range.start.line);
10
+ end = Math.max(end, edit.range.end.line);
11
+ }
12
+ return Range.create(start, 0, end, 0);
13
+ }
14
+ export class LikeC4ModelChanges {
15
+ constructor(services) {
16
+ this.services = services;
17
+ this.locator = services.likec4.ModelLocator;
18
+ }
19
+ locator;
20
+ async applyChange(changeView) {
21
+ const lspConnection = this.services.shared.lsp.Connection;
22
+ invariant(lspConnection, "LSP Connection not available");
23
+ let result = null;
24
+ await this.services.shared.workspace.WorkspaceLock.write(async () => {
25
+ const { doc, edits } = this.convertToTextEdit(changeView);
26
+ const textDocument = {
27
+ uri: doc.textDocument.uri,
28
+ version: doc.textDocument.version
29
+ };
30
+ if (!edits.length) {
31
+ return;
32
+ }
33
+ const applyResult = await lspConnection.workspace.applyEdit({
34
+ label: `LikeC4 - change view ${changeView.viewId}`,
35
+ edit: {
36
+ documentChanges: [
37
+ TextDocumentEdit.create(textDocument, edits)
38
+ ]
39
+ }
40
+ });
41
+ if (!applyResult.applied) {
42
+ lspConnection.window.showErrorMessage(`Failed to apply changes${applyResult.failureReason}`);
43
+ return;
44
+ }
45
+ result = {
46
+ uri: textDocument.uri,
47
+ range: unionRangeOfAllEdits(edits)
48
+ };
49
+ });
50
+ return result;
51
+ }
52
+ convertToTextEdit({ viewId, changes }) {
53
+ const lookup = this.locator.locateViewAst(viewId);
54
+ if (!lookup) {
55
+ throw new Error(`View not found: ${viewId}`);
56
+ }
57
+ const edits = [];
58
+ for (const change of changes) {
59
+ switch (change.op) {
60
+ case "change-color": {
61
+ edits.push(...changeViewStyle(this.services, {
62
+ ...lookup,
63
+ targets: change.targets,
64
+ key: "color",
65
+ value: change.color
66
+ }));
67
+ break;
68
+ }
69
+ case "change-shape": {
70
+ edits.push(...changeViewStyle(this.services, {
71
+ ...lookup,
72
+ targets: change.targets,
73
+ key: "shape",
74
+ value: change.shape
75
+ }));
76
+ break;
77
+ }
78
+ case "change-autolayout": {
79
+ edits.push(...changeViewLayout(this.services, {
80
+ ...lookup,
81
+ layout: change.layout
82
+ }));
83
+ break;
84
+ }
85
+ default:
86
+ nonexhaustive(change);
87
+ }
88
+ }
89
+ return {
90
+ doc: lookup.doc,
91
+ edits
92
+ };
93
+ }
94
+ }
@@ -0,0 +1,13 @@
1
+ import { type AutoLayoutDirection } from '@likec4/core';
2
+ import { TextEdit } from 'vscode-languageserver-protocol';
3
+ import { ast, type ParsedAstElementView, type ParsedLikeC4LangiumDocument } from '../ast';
4
+ import type { LikeC4Services } from '../module';
5
+ type ChangeViewLayoutArg = {
6
+ view: ParsedAstElementView;
7
+ doc: ParsedLikeC4LangiumDocument;
8
+ viewAst: ast.ElementView;
9
+ layout: AutoLayoutDirection;
10
+ };
11
+ export declare function changeViewLayout(services: LikeC4Services, { view, viewAst, layout }: ChangeViewLayoutArg): TextEdit[];
12
+ export {};
13
+ //# sourceMappingURL=changeViewLayout.d.ts.map
@@ -0,0 +1,30 @@
1
+ import { invariant } from "@likec4/core";
2
+ import { GrammarUtils } from "langium";
3
+ import { last } from "remeda";
4
+ import { TextEdit } from "vscode-languageserver-protocol";
5
+ import { ast, toAstViewLayoutDirection } from "../ast.js";
6
+ const { findNodeForProperty } = GrammarUtils;
7
+ export function changeViewLayout(services, {
8
+ view,
9
+ viewAst,
10
+ layout
11
+ }) {
12
+ const viewCstNode = viewAst.$cstNode;
13
+ invariant(viewCstNode, "viewCstNode");
14
+ const newlayout = toAstViewLayoutDirection(layout);
15
+ const existingRule = viewAst.body.rules.findLast(ast.isViewRuleAutoLayout);
16
+ if (existingRule && existingRule.$cstNode) {
17
+ const directionCstNode = findNodeForProperty(existingRule.$cstNode, "direction");
18
+ if (directionCstNode) {
19
+ return [TextEdit.replace(directionCstNode.range, newlayout)];
20
+ }
21
+ return [TextEdit.replace(existingRule.$cstNode.range, `autoLayout ${newlayout}`)];
22
+ }
23
+ const insertPos = last(viewAst.body.rules)?.$cstNode?.range.end ?? last(viewAst.body.props)?.$cstNode?.range.end ?? viewAst.body.$cstNode?.range.start;
24
+ invariant(insertPos, "insertPos is not defined");
25
+ const indent = " ".repeat(2 + viewCstNode.range.start.character);
26
+ const insert = `
27
+
28
+ ${indent}autoLayout ${newlayout}`;
29
+ return [TextEdit.insert(insertPos, insert)];
30
+ }
@@ -0,0 +1,15 @@
1
+ import { type Fqn, type NonEmptyArray } from '@likec4/core';
2
+ import { TextEdit } from 'vscode-languageserver-protocol';
3
+ import { ast, type ParsedAstElementView, type ParsedLikeC4LangiumDocument } from '../ast';
4
+ import type { LikeC4Services } from '../module';
5
+ type ChangeViewStyleArg = {
6
+ view: ParsedAstElementView;
7
+ doc: ParsedLikeC4LangiumDocument;
8
+ viewAst: ast.ElementView;
9
+ key: string;
10
+ value: string;
11
+ targets: NonEmptyArray<Fqn>;
12
+ };
13
+ export declare function changeViewStyle(services: LikeC4Services, { view, viewAst, targets, key, value }: ChangeViewStyleArg): TextEdit[];
14
+ export {};
15
+ //# sourceMappingURL=changeViewStyle.d.ts.map
@@ -0,0 +1,123 @@
1
+ import { invariant, isAncestor, nonNullable } from "@likec4/core";
2
+ import { GrammarUtils } from "langium";
3
+ import { findLast, last, partition } from "remeda";
4
+ import { TextEdit } from "vscode-languageserver-protocol";
5
+ import { ast } from "../ast.js";
6
+ const { findNodeForKeyword, findNodeForProperty } = GrammarUtils;
7
+ const asViewStyleRule = (target, key, value, indent = 0) => {
8
+ const indentStr = indent > 0 ? " ".repeat(indent) : "";
9
+ return [
10
+ indentStr + `style ${target} {`,
11
+ indentStr + ` ${key} ${value}`,
12
+ indentStr + `}`
13
+ ].join("\n");
14
+ };
15
+ const isMatchingViewRule = (fqn, index) => (rule) => {
16
+ if (!ast.isViewRuleStyle(rule)) {
17
+ return false;
18
+ }
19
+ const [target, ...rest] = rule.targets;
20
+ if (!target || rest.length > 0 || !ast.isElementRef(target)) {
21
+ return false;
22
+ }
23
+ const ref = target.el.ref;
24
+ const _fqn = ref ? index.getFqn(ref) : null;
25
+ return _fqn === fqn;
26
+ };
27
+ export function changeViewStyle(services, {
28
+ view,
29
+ viewAst,
30
+ targets,
31
+ key,
32
+ value
33
+ }) {
34
+ const viewCstNode = viewAst.$cstNode;
35
+ invariant(viewCstNode, "viewCstNode");
36
+ const insertPos = last(viewAst.body.rules)?.$cstNode?.range.end ?? last(viewAst.body.props)?.$cstNode?.range.end ?? viewAst.body.$cstNode?.range.start;
37
+ invariant(insertPos, "insertPos is not defined");
38
+ const indent = viewCstNode.range.start.character + 2;
39
+ const fqnIndex = services.likec4.FqnIndex;
40
+ const styleRules = viewAst.body.rules.filter(ast.isViewRuleStyle);
41
+ const viewOf = view.viewOf;
42
+ const targetsWithRules = targets.map((target) => {
43
+ const rule = findLast(styleRules, isMatchingViewRule(target, fqnIndex));
44
+ const fqn = viewOf && isAncestor(viewOf, target) ? target.substring(viewOf.length + 1) : target;
45
+ if (rule) {
46
+ return {
47
+ fqn,
48
+ rule
49
+ };
50
+ } else {
51
+ return {
52
+ fqn
53
+ };
54
+ }
55
+ });
56
+ const [existing, insert] = partition(
57
+ targetsWithRules,
58
+ (a) => !!a.rule
59
+ );
60
+ const modifiedRange = {
61
+ start: insertPos,
62
+ end: insertPos
63
+ };
64
+ const includeRange = (range) => {
65
+ if (range.start.line < modifiedRange.start.line) {
66
+ modifiedRange.start = range.start;
67
+ }
68
+ if (range.end.line > modifiedRange.end.line) {
69
+ modifiedRange.end = range.end;
70
+ }
71
+ };
72
+ const edits = [];
73
+ if (insert.length > 0) {
74
+ const linesToInsert = [
75
+ "",
76
+ ...insert.map(({ fqn }) => asViewStyleRule(fqn, key, value, indent))
77
+ ];
78
+ edits.push(
79
+ TextEdit.insert(
80
+ insertPos,
81
+ linesToInsert.join("\n")
82
+ )
83
+ );
84
+ modifiedRange.end = {
85
+ line: modifiedRange.end.line + linesToInsert.length,
86
+ character: last(linesToInsert)?.length ?? insertPos.character
87
+ };
88
+ }
89
+ if (existing.length > 0) {
90
+ for (const { rule } of existing) {
91
+ const ruleCstNode = rule.$cstNode;
92
+ invariant(ruleCstNode, "RuleCstNode not found");
93
+ const ruleProp = rule.styleprops.find((p) => p.key === key);
94
+ if (ruleProp && ruleProp.$cstNode) {
95
+ const { range: { start, end } } = nonNullable(
96
+ findNodeForProperty(ruleProp.$cstNode, "value"),
97
+ "cant find value cst node"
98
+ );
99
+ includeRange(ruleProp.$cstNode.range);
100
+ edits.push(TextEdit.replace({ start, end }, value));
101
+ continue;
102
+ }
103
+ const insertPos2 = findNodeForKeyword(ruleCstNode, "{")?.range.end;
104
+ invariant(insertPos2, "Opening brace not found");
105
+ const indentStr = " ".repeat(2 + ruleCstNode.range.start.character);
106
+ const insertKeyValue = indentStr + key + " " + value;
107
+ edits.push(
108
+ TextEdit.insert(
109
+ insertPos2,
110
+ "\n" + insertKeyValue
111
+ )
112
+ );
113
+ includeRange({
114
+ start: insertPos2,
115
+ end: {
116
+ line: insertPos2.line + 1,
117
+ character: insertKeyValue.length
118
+ }
119
+ });
120
+ }
121
+ }
122
+ return edits;
123
+ }
package/dist/module.d.ts CHANGED
@@ -2,6 +2,7 @@ import { type Module, WorkspaceCache } from 'langium';
2
2
  import { type DefaultSharedModuleContext, type LangiumServices, type LangiumSharedServices, type PartialLangiumServices } from 'langium/lsp';
3
3
  import { LikeC4CodeLensProvider, LikeC4DocumentHighlightProvider, LikeC4DocumentLinkProvider, LikeC4DocumentSymbolProvider, LikeC4HoverProvider, LikeC4SemanticTokenProvider } from './lsp';
4
4
  import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator, LikeC4ModelParser } from './model';
5
+ import { LikeC4ModelChanges } from './model-change/ModelChanges';
5
6
  import { LikeC4ScopeComputation, LikeC4ScopeProvider } from './references';
6
7
  import { Rpc } from './Rpc';
7
8
  import { LikeC4WorkspaceManager, NodeKindProvider, WorkspaceSymbolProvider } from './shared';
@@ -26,6 +27,7 @@ export interface LikeC4AddedServices {
26
27
  ModelParser: LikeC4ModelParser;
27
28
  ModelBuilder: LikeC4ModelBuilder;
28
29
  ModelLocator: LikeC4ModelLocator;
30
+ ModelChanges: LikeC4ModelChanges;
29
31
  };
30
32
  lsp: {
31
33
  DocumentHighlightProvider: LikeC4DocumentHighlightProvider;
package/dist/module.js CHANGED
@@ -15,6 +15,7 @@ import {
15
15
  LikeC4SemanticTokenProvider
16
16
  } from "./lsp/index.js";
17
17
  import { FqnIndex, LikeC4ModelBuilder, LikeC4ModelLocator, LikeC4ModelParser } from "./model/index.js";
18
+ import { LikeC4ModelChanges } from "./model-change/ModelChanges.js";
18
19
  import { LikeC4ScopeComputation, LikeC4ScopeProvider } from "./references/index.js";
19
20
  import { Rpc } from "./Rpc.js";
20
21
  import { LikeC4WorkspaceManager, NodeKindProvider, WorkspaceSymbolProvider } from "./shared/index.js";
@@ -35,6 +36,7 @@ export const LikeC4Module = {
35
36
  WorkspaceCache: (services) => new WorkspaceCache(services.shared),
36
37
  Rpc: bind(Rpc),
37
38
  likec4: {
39
+ ModelChanges: bind(LikeC4ModelChanges),
38
40
  FqnIndex: bind(FqnIndex),
39
41
  ModelParser: bind(LikeC4ModelParser),
40
42
  ModelBuilder: bind(LikeC4ModelBuilder),
@@ -1,22 +1,27 @@
1
- import type { ComputedView, Fqn, LikeC4Model, LikeC4RawModel, RelationID, ViewID } from '@likec4/core';
1
+ import type { AutoLayoutDirection, ComputedView, ElementShape, Fqn, LikeC4ComputedModel, LikeC4Model, NonEmptyArray, RelationID, ThemeColor, ViewID } from '@likec4/core';
2
2
  import type { DocumentUri, Location } from 'vscode-languageserver-protocol';
3
3
  import { NotificationType, RequestType, RequestType0 } from 'vscode-languageserver-protocol';
4
4
  export declare const onDidChangeModel: NotificationType<string>;
5
- export declare const fetchRawModel: RequestType0<{
6
- rawmodel: LikeC4RawModel | null;
7
- }, void>;
5
+ export type OnDidChangeModelNotification = typeof onDidChangeModel;
8
6
  export declare const fetchModel: RequestType0<{
9
7
  model: LikeC4Model | null;
10
8
  }, void>;
9
+ export type FetchModelRequest = typeof fetchModel;
10
+ export declare const fetchComputedModel: RequestType0<{
11
+ model: LikeC4ComputedModel | null;
12
+ }, void>;
13
+ export type FetchComputedModelRequest = typeof fetchComputedModel;
11
14
  export declare const computeView: RequestType<{
12
15
  viewId: ViewID;
13
16
  }, {
14
17
  view: ComputedView | null;
15
18
  }, void>;
16
- interface BuildDocumentsParams {
19
+ export type ComputeViewRequest = typeof computeView;
20
+ export interface BuildDocumentsParams {
17
21
  docs: DocumentUri[];
18
22
  }
19
23
  export declare const buildDocuments: RequestType<BuildDocumentsParams, void, void>;
24
+ export type BuildDocumentsRequest = typeof buildDocuments;
20
25
  export type LocateParams = {
21
26
  element: Fqn;
22
27
  property?: string;
@@ -26,5 +31,28 @@ export type LocateParams = {
26
31
  view: ViewID;
27
32
  };
28
33
  export declare const locate: RequestType<LocateParams, Location | null, void>;
29
- export {};
34
+ export type LocateRequest = typeof locate;
35
+ export declare namespace ChangeView {
36
+ interface ChangeColor {
37
+ op: 'change-color';
38
+ color: ThemeColor;
39
+ targets: NonEmptyArray<Fqn>;
40
+ }
41
+ interface ChangeShape {
42
+ op: 'change-shape';
43
+ shape: ElementShape;
44
+ targets: NonEmptyArray<Fqn>;
45
+ }
46
+ interface ChangeAutoLayout {
47
+ op: 'change-autolayout';
48
+ layout: AutoLayoutDirection;
49
+ }
50
+ }
51
+ export type ChangeView = ChangeView.ChangeColor | ChangeView.ChangeShape | ChangeView.ChangeAutoLayout;
52
+ export interface ChangeViewRequestParams {
53
+ viewId: ViewID;
54
+ changes: NonEmptyArray<ChangeView>;
55
+ }
56
+ export declare const changeView: RequestType<ChangeViewRequestParams, Location | null, void>;
57
+ export type ChangeViewRequest = typeof changeView;
30
58
  //# sourceMappingURL=protocol.d.ts.map
package/dist/protocol.js CHANGED
@@ -1,11 +1,14 @@
1
1
  import { NotificationType, RequestType, RequestType0 } from "vscode-languageserver-protocol";
2
2
  export const onDidChangeModel = new NotificationType("likec4/onDidChangeModel");
3
- export const fetchRawModel = new RequestType0(
4
- "likec4/fetchRaw"
3
+ export const fetchModel = new RequestType0(
4
+ "likec4/fetchModel"
5
+ );
6
+ export const fetchComputedModel = new RequestType0(
7
+ "likec4/fetchComputedModel"
5
8
  );
6
- export const fetchModel = new RequestType0("likec4/fetchModel");
7
9
  export const computeView = new RequestType(
8
10
  "likec4/computeView"
9
11
  );
10
12
  export const buildDocuments = new RequestType("likec4/build");
11
13
  export const locate = new RequestType("likec4/locate");
14
+ export const changeView = new RequestType("likec4/change-view");
@@ -1,4 +1,4 @@
1
- import { DefaultScopeComputation, MultiMap, type AstNodeDescription, type PrecomputedScopes } from 'langium';
1
+ import { type AstNodeDescription, DefaultScopeComputation, MultiMap, type PrecomputedScopes } from 'langium';
2
2
  import type { CancellationToken } from 'vscode-languageserver';
3
3
  import { ast, type LikeC4LangiumDocument } from '../ast';
4
4
  type ElementsContainer = ast.Model | ast.ElementBody | ast.ExtendElementBody;
@@ -0,0 +1,2 @@
1
+ // Do not add any other lines of code to this file!
2
+ import '@total-typescript/ts-reset'
@@ -1,4 +1,4 @@
1
- export * from './WorkspaceManager';
2
1
  export * from './NodeKindProvider';
2
+ export * from './WorkspaceManager';
3
3
  export * from './WorkspaceSymbolProvider';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,3 @@
1
- export * from "./WorkspaceManager.js";
2
1
  export * from "./NodeKindProvider.js";
2
+ export * from "./WorkspaceManager.js";
3
3
  export * from "./WorkspaceSymbolProvider.js";
@@ -14,7 +14,7 @@ export declare function createTestServices(workspace?: string): {
14
14
  errors: string[];
15
15
  warnings: string[];
16
16
  }>;
17
- buildModel: () => Promise<import("@likec4/core").LikeC4Model>;
17
+ buildModel: () => Promise<import("@likec4/core").LikeC4ComputedModel>;
18
18
  resetState: () => Promise<void>;
19
19
  };
20
20
  export type TestServices = ReturnType<typeof createTestServices>;
@@ -80,7 +80,7 @@ export function createTestServices(workspace = "file:///test/workspace") {
80
80
  };
81
81
  const buildModel = async () => {
82
82
  await validateAll();
83
- const model = await modelBuilder.buildModel();
83
+ const model = await modelBuilder.buildComputedModel();
84
84
  if (!model)
85
85
  throw new Error("No model found");
86
86
  return model;
@@ -0,0 +1,2 @@
1
+ export * from './stringHash';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ export * from "./stringHash.js";
@@ -0,0 +1,2 @@
1
+ export declare function stringHash(...str: [string, ...string[]]): string;
2
+ //# sourceMappingURL=stringHash.d.ts.map
@@ -0,0 +1,5 @@
1
+ import hash from "string-hash";
2
+ export function stringHash(...str) {
3
+ var s = str.length > 1 ? str.join(":::") : str[0];
4
+ return hash(s).toString(36);
5
+ }
@@ -1,10 +1,10 @@
1
+ import { isNullish } from "remeda";
1
2
  import { ast } from "../../ast.js";
2
- import { isNil } from "remeda";
3
3
  export const incomingExpressionChecks = (_services) => {
4
4
  return (el, accept) => {
5
5
  if (ast.isWildcardExpr(el.to) && ast.isViewRulePredicate(el.$container)) {
6
6
  const view = el.$container.$container.$container;
7
- if (isNil(view.viewOf)) {
7
+ if (isNullish(view.viewOf)) {
8
8
  accept("warning", "Predicate is ignored as it concerns all relationships", {
9
9
  node: el
10
10
  });
@@ -1,4 +1,4 @@
1
+ export * from './custom-element-expr';
1
2
  export * from './incoming';
2
3
  export * from './outgoing';
3
- export * from './custom-element-expr';
4
4
  //# sourceMappingURL=index.d.ts.map
@@ -1,3 +1,3 @@
1
+ export * from "./custom-element-expr.js";
1
2
  export * from "./incoming.js";
2
3
  export * from "./outgoing.js";
3
- export * from "./custom-element-expr.js";
@@ -1,5 +1,5 @@
1
- import { ast } from "../../ast.js";
2
1
  import { isNil } from "remeda";
2
+ import { ast } from "../../ast.js";
3
3
  export const outgoingExpressionChecks = (_services) => {
4
4
  return (el, accept) => {
5
5
  if (ast.isWildcardExpr(el.from)) {
@@ -1,6 +1,6 @@
1
+ import { invariant } from "@likec4/core";
1
2
  import { uniq, zip } from "rambdax";
2
3
  import { hasAtLeast } from "remeda";
3
- import { invariant } from "@likec4/core";
4
4
  function commonAncestorPath(views, sep = "/") {
5
5
  if (views.length <= 1)
6
6
  return "";
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@likec4/language-server",
3
3
  "description": "LikeC4 Language Server",
4
- "version": "0.60.3",
4
+ "version": "1.0.0-next.0",
5
5
  "license": "MIT",
6
6
  "bugs": "https://github.com/likec4/likec4/issues",
7
7
  "homepage": "https://likec4.dev",
@@ -29,6 +29,10 @@
29
29
  "types": "./dist/browser/index.d.ts",
30
30
  "default": "./dist/browser/index.js"
31
31
  },
32
+ "./protocol": {
33
+ "types": "./dist/protocol.d.ts",
34
+ "default": "./dist/protocol.js"
35
+ },
32
36
  "./node": {
33
37
  "types": "./dist/node/index.d.ts",
34
38
  "default": "./dist/node/index.js"
@@ -46,6 +50,10 @@
46
50
  "types": "./dist/browser/index.d.ts",
47
51
  "default": "./dist/browser/index.js"
48
52
  },
53
+ "./protocol": {
54
+ "types": "./dist/protocol.d.ts",
55
+ "default": "./dist/protocol.js"
56
+ },
49
57
  "./node": {
50
58
  "types": "./dist/node/index.d.ts",
51
59
  "default": "./dist/node/index.js"
@@ -66,16 +74,19 @@
66
74
  "test": "vitest run"
67
75
  },
68
76
  "dependencies": {
69
- "@likec4/core": "0.60.3",
70
- "@likec4/graph": "0.60.3",
77
+ "@likec4/core": "1.0.0-next.0",
78
+ "@likec4/graph": "1.0.0-next.0",
79
+ "@total-typescript/ts-reset": "^0.5.1",
71
80
  "debounce-fn": "^6.0.0",
81
+ "fast-equals": "^5.0.1",
72
82
  "langium": "^3.0.0",
73
83
  "object-hash": "^3.0.0",
74
84
  "p-debounce": "^4.0.0",
75
85
  "rambdax": "^9.1.1",
76
- "remeda": "^1.40.1",
86
+ "remeda": "^1.56.0",
87
+ "string-hash": "^1.1.3",
77
88
  "strip-indent": "^4.0.0",
78
- "type-fest": "^4.10.3",
89
+ "type-fest": "^4.13.1",
79
90
  "ufo": "^1.3.2",
80
91
  "vscode-languageserver": "9.0.1",
81
92
  "vscode-languageserver-protocol": "3.17.5",
@@ -84,15 +95,13 @@
84
95
  "devDependencies": {
85
96
  "@types/node": "^20.11.25",
86
97
  "@types/object-hash": "^3.0.6",
98
+ "@types/string-hash": "^1",
87
99
  "execa": "^8.0.1",
88
100
  "langium-cli": "^3.0.1",
89
- "npm-run-all2": "^5.0.2",
90
- "typescript": "^5.4.2",
101
+ "npm-run-all2": "^6.1.2",
102
+ "typescript": "^5.4.3",
91
103
  "unbuild": "^2.0.0",
92
104
  "vitest": "^1.4.0"
93
105
  },
94
- "packageManager": "yarn@4.1.1",
95
- "volta": {
96
- "extends": "../../package.json"
97
- }
106
+ "packageManager": "yarn@4.1.1"
98
107
  }