sysml-diagram 0.16.0 → 0.17.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.
package/out/main.js CHANGED
@@ -161447,18 +161447,39 @@ function parseConstructions(text) {
161447
161447
  function sourceOf(node) {
161448
161448
  return node.$cstNode?.root.fullText;
161449
161449
  }
161450
- function cleanBlockBody(inner) {
161451
- let body = inner.trim();
161452
- if (body.startsWith("/*"))
161450
+ function cleanBlockBodyLines(raw, rawOffset) {
161451
+ let cut = raw.length - raw.trimStart().length;
161452
+ let body = raw.slice(cut).trimEnd();
161453
+ if (body.startsWith("/*")) {
161453
161454
  body = body.slice(2);
161455
+ cut += 2;
161456
+ }
161454
161457
  if (body.endsWith("*/"))
161455
161458
  body = body.slice(0, -2);
161456
- const lines = body.split("\n").map((line2) => line2.replace(/^\s*\*+ ?/u, "").replace(/\s+$/u, ""));
161457
- while (lines.length > 0 && lines[0].trim() === "")
161459
+ const lines = [];
161460
+ let cursor = cut;
161461
+ for (const rawLine of body.split("\n")) {
161462
+ const gutter = /^\s*\*+ ?/u.exec(rawLine);
161463
+ const skip = gutter ? gutter[0].length : 0;
161464
+ lines.push({
161465
+ text: rawLine.slice(skip).replace(/\s+$/u, ""),
161466
+ offset: rawOffset + cursor + skip
161467
+ });
161468
+ cursor += rawLine.length + 1;
161469
+ }
161470
+ while (lines.length > 0 && lines[0].text.trim() === "")
161458
161471
  lines.shift();
161459
- while (lines.length > 0 && lines[lines.length - 1].trim() === "")
161472
+ while (lines.length > 0 && lines[lines.length - 1].text.trim() === "")
161460
161473
  lines.pop();
161461
- return lines.join("\n").trim();
161474
+ if (lines.length > 0) {
161475
+ const lead = lines[0].text.length - lines[0].text.trimStart().length;
161476
+ if (lead > 0)
161477
+ lines[0] = { text: lines[0].text.slice(lead), offset: lines[0].offset + lead };
161478
+ }
161479
+ return lines;
161480
+ }
161481
+ function cleanBlockBody(inner) {
161482
+ return cleanBlockBodyLines(inner, 0).map((line2) => line2.text).join("\n");
161462
161483
  }
161463
161484
  function docCommentBody(node) {
161464
161485
  const raw = node.doc;
@@ -161471,7 +161492,7 @@ function docCommentBody(node) {
161471
161492
  const body = cleanBlockBody(raw.slice(start2, end + 2));
161472
161493
  return body.length > 0 ? body : void 0;
161473
161494
  }
161474
- function trailingBlockBody(node) {
161495
+ function trailingBlockBodySpan(node) {
161475
161496
  const cst = node.$cstNode;
161476
161497
  const text = sourceOf(node);
161477
161498
  if (!cst || text === void 0)
@@ -161485,8 +161506,22 @@ function trailingBlockBody(node) {
161485
161506
  const close = rest.indexOf("*/", open + 2);
161486
161507
  if (close < 0)
161487
161508
  return void 0;
161488
- const body = cleanBlockBody(rest.slice(open, close + 2));
161489
- return body.length > 0 ? body : void 0;
161509
+ const raw = rest.slice(open, close + 2);
161510
+ const lines = cleanBlockBodyLines(raw, cst.end + open);
161511
+ const body = lines.map((line2) => line2.text).join("\n");
161512
+ if (body.length === 0)
161513
+ return void 0;
161514
+ return { text: body, lines, open: cst.end + open, close: cst.end + close + 2 };
161515
+ }
161516
+ function trailingBlockBody(node) {
161517
+ return trailingBlockBodySpan(node)?.text;
161518
+ }
161519
+ function bodyOffsetAt(span, line2, character) {
161520
+ const entry = Number.isInteger(line2) ? span.lines[line2] : void 0;
161521
+ if (!entry)
161522
+ return span.open;
161523
+ const column = Number.isFinite(character) ? character : 0;
161524
+ return entry.offset + Math.max(0, Math.min(column, entry.text.length));
161490
161525
  }
161491
161526
 
161492
161527
  // ../language-server/out/src/services/escaped-name.js
@@ -163515,10 +163550,10 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163515
163550
  addPorts(usage, id2, false);
163516
163551
  if (self2)
163517
163552
  addPorts(self2.usage, self2.id, true);
163518
- const bareName = /* @__PURE__ */ new Map();
163553
+ const bareName2 = /* @__PURE__ */ new Map();
163519
163554
  for (const [k, v] of localPort) {
163520
163555
  const pn = k.split(".").pop();
163521
- bareName.set(pn, bareName.has(pn) && bareName.get(pn) !== v ? null : v);
163556
+ bareName2.set(pn, bareName2.has(pn) && bareName2.get(pn) !== v ? null : v);
163522
163557
  }
163523
163558
  return (pathText) => {
163524
163559
  if (!pathText)
@@ -163534,7 +163569,7 @@ var SysmlDiagramModelProvider = class _SysmlDiagramModelProvider {
163534
163569
  if (localPort.has(firstLast))
163535
163570
  return localPort.get(firstLast);
163536
163571
  }
163537
- const bare = bareName.get(segs[segs.length - 1]);
163572
+ const bare = bareName2.get(segs[segs.length - 1]);
163538
163573
  if (bare)
163539
163574
  return bare;
163540
163575
  return segs.length === 1 ? localNode.get(segs[0]) : void 0;
@@ -168926,6 +168961,18 @@ var DIAGNOSTIC_MESSAGES = {
168926
168961
  // `first a then b;` and the `then` occurrence shorthand in a part), so the
168927
168962
  // message names the construct it means rather than "action language".
168928
168963
  SEM011_ACTION_ONLY_CONSTRUCT: (construct, clause, owner) => `${construct} may only be declared in the body of an action definition or usage (OMG SysML v2 Part 1 \xA7${clause}), not in ${owner}.`,
168964
+ // REQ-392 — issue #150. A `rep … language "sysml"` body is a legal representation
168965
+ // of the element it annotates (OMG KerML §7.4.3), so the two faults are "this is
168966
+ // not SysML" and "this is not THIS element". The parser's own wording is carried
168967
+ // through verbatim, because it names the token the reader has to go and fix.
168968
+ SYN076_EMBEDDED_REPRESENTATION_SYNTAX: (language, detail) => `This representation declares language "${language}", so its body must be valid ${language}: ${detail}`,
168969
+ SEM012_REPRESENTATION_WRONG_KIND: (expected, actual) => `A textual representation must represent the element it annotates. This body represents '${actual}', but it annotates '${expected}'.`,
168970
+ // REQ-392 — The body is resolved in the represented element's own context, so an
168971
+ // unresolvable name there is the same defect it would be in ordinary code. It is
168972
+ // reported under its own code so a team can tune representations separately.
168973
+ RES020_REPRESENTATION_UNRESOLVED: (name, language) => `'${name}' cannot be resolved from here, so this ${language} representation is not a valid representation of the element it annotates.`,
168974
+ RES020_REPRESENTATION_PATH_SEGMENT: (segment, owner, language) => `Feature path segment '${segment}' does not exist on '${owner}', so this ${language} representation is not a valid representation of the element it annotates.`,
168975
+ SEM012_REPRESENTATION_WRONG_NAME: (expected, actual) => `A textual representation must represent the element it annotates. This body represents '${actual}', but it annotates '${expected}'.`,
168929
168976
  RES016_LIBRARY_SHADOWING: (name) => `'${name}' shadows a standard-library element of the same name; the library symbol is hidden in this scope.`,
168930
168977
  // issue #183 — KerML namespace distinguishability (validateNamespaceDistinguishability).
168931
168978
  // Owned siblings sharing a name/short name (or an alias clashing with an owned
@@ -171923,6 +171970,193 @@ function isNamespaceOnlyDecl(node) {
171923
171970
  return node.isDef === true;
171924
171971
  }
171925
171972
 
171973
+ // ../language-server/out/src/services/embedded-representation.js
171974
+ var EMBEDDED_LANGUAGES = /* @__PURE__ */ new Set(["sysml", "kerml"]);
171975
+ var EXPRESSION_VALUED_OWNERS = /* @__PURE__ */ new Set([
171976
+ "AssertConstraintStmt",
171977
+ "AssumeConstraintStmt",
171978
+ "RequireConstraintStmt",
171979
+ "ConstraintDecl",
171980
+ "InvShorthand",
171981
+ "CalcDecl",
171982
+ "ExpressionDecl"
171983
+ ]);
171984
+ function isEmbeddedLanguage(raw) {
171985
+ if (typeof raw !== "string")
171986
+ return false;
171987
+ return EMBEDDED_LANGUAGES.has(raw.replace(/^"|"$/gu, "").trim().toLowerCase());
171988
+ }
171989
+ function bareName(node) {
171990
+ const name = node?.name;
171991
+ return typeof name === "string" && name.length > 0 ? name.replace(/^'|'$/gu, "") : void 0;
171992
+ }
171993
+ function kindOf(node) {
171994
+ const decl = /^([A-Z][a-z]+)Decl$/.exec(node.$type);
171995
+ if (decl) {
171996
+ const base = decl[1].toLowerCase();
171997
+ return node.isDef ? `${base} def` : base;
171998
+ }
171999
+ switch (node.$type) {
172000
+ case "Package":
172001
+ return "package";
172002
+ case "InvShorthand":
172003
+ return "inv";
172004
+ case "AssertConstraintStmt":
172005
+ return "assert constraint";
172006
+ case "AssumeConstraintStmt":
172007
+ return "assume constraint";
172008
+ case "RequireConstraintStmt":
172009
+ return "require constraint";
172010
+ default:
172011
+ return node.$type;
172012
+ }
172013
+ }
172014
+ function hasErrors(result) {
172015
+ return result.lexerErrors.length > 0 || result.parserErrors.length > 0;
172016
+ }
172017
+ function describeParserError(error) {
172018
+ const image = error.token?.image ?? "";
172019
+ const expecting = /Expecting token of type '([^']+)'/u.exec(error.message ?? "");
172020
+ if (image === "") {
172021
+ return expecting ? `the body ends while '${expecting[1]}' is still expected.` : "the body ends before it is complete.";
172022
+ }
172023
+ if (error.name === "NotAllInputParsedException") {
172024
+ return `'${image}' is left over after the representation is already complete.`;
172025
+ }
172026
+ if (expecting)
172027
+ return `expected '${expecting[1]}' but found '${image}'.`;
172028
+ return `unexpected '${image}'.`;
172029
+ }
172030
+ function describeLexerError(error) {
172031
+ const character = /unexpected character: ->(.*?)<-/u.exec(error.message ?? "");
172032
+ return character ? `unexpected character '${character[1]}'.` : error.message ?? "the body cannot be read.";
172033
+ }
172034
+ function failureOf(result, span) {
172035
+ const endLine = Math.max(0, span.lines.length - 1);
172036
+ const end = { line: endLine, character: span.lines[endLine]?.text.length ?? 0 };
172037
+ const lexical = result.lexerErrors[0];
172038
+ const syntactic = result.parserErrors[0];
172039
+ if (lexical && Number.isFinite(lexical.line)) {
172040
+ return {
172041
+ line: lexical.line - 1,
172042
+ character: Math.max(0, (Number.isFinite(lexical.column) ? lexical.column : 1) - 1),
172043
+ message: describeLexerError(lexical)
172044
+ };
172045
+ }
172046
+ if (syntactic) {
172047
+ const token = syntactic.token;
172048
+ const message = describeParserError(syntactic);
172049
+ if (Number.isFinite(token?.startLine) && Number.isFinite(token?.startColumn)) {
172050
+ return { line: token.startLine - 1, character: token.startColumn - 1, message };
172051
+ }
172052
+ return { ...end, message };
172053
+ }
172054
+ return { ...end, message: lexical ? describeLexerError(lexical) : "the body is not valid SysML." };
172055
+ }
172056
+ function conformance(parsed, owner) {
172057
+ if (!parsed || !owner || owner.$type === "Document")
172058
+ return { status: "ok" };
172059
+ const sameKind = parsed.$type === owner.$type && Boolean(parsed.isDef) === Boolean(owner.isDef);
172060
+ if (!sameKind) {
172061
+ return { status: "mismatch", mismatch: { reason: "kind", expected: kindOf(owner), actual: kindOf(parsed) } };
172062
+ }
172063
+ const expected = bareName(owner);
172064
+ const actual = bareName(parsed);
172065
+ if (expected && actual && expected !== actual) {
172066
+ return { status: "mismatch", mismatch: { reason: "name", expected, actual } };
172067
+ }
172068
+ return { status: "ok" };
172069
+ }
172070
+ function nodesOf(root4) {
172071
+ return [root4, ...ast_utils_exports.streamAllContents(root4)];
172072
+ }
172073
+ function declaredNames(root4) {
172074
+ const names = /* @__PURE__ */ new Set();
172075
+ for (const node of nodesOf(root4)) {
172076
+ const name = node.name;
172077
+ if (typeof name === "string" && name.length > 0)
172078
+ names.add(name.replace(/^'|'$/gu, ""));
172079
+ }
172080
+ return names;
172081
+ }
172082
+ function graftInto(fragment, owner) {
172083
+ const mutable = fragment;
172084
+ mutable.$container = owner;
172085
+ mutable.$containerProperty = "members";
172086
+ }
172087
+ function firstSegment(refText) {
172088
+ return (refText.split("::")[0] ?? refText).replace(/^'|'$/gu, "").trim();
172089
+ }
172090
+ function unresolvedReferences(fragment, owner) {
172091
+ graftInto(fragment, owner);
172092
+ const own = declaredNames(fragment);
172093
+ const out = [];
172094
+ for (const node of nodesOf(fragment)) {
172095
+ for (const [property3, value] of Object.entries(node)) {
172096
+ if (property3.startsWith("$"))
172097
+ continue;
172098
+ for (const candidate of Array.isArray(value) ? value : [value]) {
172099
+ if (!isReference(candidate))
172100
+ continue;
172101
+ const reference = candidate;
172102
+ const refText = reference.$refText;
172103
+ if (!refText || own.has(firstSegment(refText)))
172104
+ continue;
172105
+ let resolved;
172106
+ try {
172107
+ resolved = reference.ref;
172108
+ } catch {
172109
+ return [];
172110
+ }
172111
+ if (resolved)
172112
+ continue;
172113
+ const range = reference.$refNode?.range;
172114
+ if (!range)
172115
+ continue;
172116
+ out.push({
172117
+ refText,
172118
+ line: range.start.line,
172119
+ character: range.start.character,
172120
+ length: Math.max(1, range.end.character - range.start.character)
172121
+ });
172122
+ }
172123
+ }
172124
+ }
172125
+ return out;
172126
+ }
172127
+ function evaluateRepresentation(parser, rep) {
172128
+ if (!isEmbeddedLanguage(rep.language))
172129
+ return void 0;
172130
+ const span = trailingBlockBodySpan(rep);
172131
+ if (!span)
172132
+ return void 0;
172133
+ const owner = rep.$container;
172134
+ const context = owner && owner.$type !== "Document" ? owner : void 0;
172135
+ const declaration = parser.parse(span.text, { rule: "NamespaceElement" });
172136
+ if (!hasErrors(declaration)) {
172137
+ return {
172138
+ span,
172139
+ verdict: conformance(declaration.value, owner),
172140
+ unresolved: context ? unresolvedReferences(declaration.value, context) : [],
172141
+ fragment: declaration.value,
172142
+ tier: "declaration"
172143
+ };
172144
+ }
172145
+ if (owner && EXPRESSION_VALUED_OWNERS.has(owner.$type)) {
172146
+ const expression = parser.parse(span.text, { rule: "Expr" });
172147
+ if (!hasErrors(expression)) {
172148
+ return {
172149
+ span,
172150
+ verdict: { status: "ok" },
172151
+ unresolved: context ? unresolvedReferences(expression.value, context) : [],
172152
+ fragment: expression.value,
172153
+ tier: "expression"
172154
+ };
172155
+ }
172156
+ }
172157
+ return { span, verdict: { status: "unparsed", failure: failureOf(declaration, span) }, unresolved: [] };
172158
+ }
172159
+
171926
172160
  // ../language-server/out/src/services/conformance.js
171927
172161
  var SPECIALIZATION_KINDS3 = /* @__PURE__ */ new Set([
171928
172162
  ":>",
@@ -172358,7 +172592,14 @@ var SysmlValidator = class _SysmlValidator {
172358
172592
  langiumDocuments;
172359
172593
  astNodeLocator;
172360
172594
  featurePaths;
172595
+ // REQ-396 — One SysmlValidator instance is registered against BOTH the SysML and
172596
+ // the KerML services (sysml-module.ts), so the parser used to read an embedded
172597
+ // representation is looked up from the document being validated rather than
172598
+ // captured here. The two share one grammar today; this keeps that from becoming
172599
+ // load-bearing.
172600
+ serviceRegistry;
172361
172601
  constructor(services) {
172602
+ this.serviceRegistry = services.shared.ServiceRegistry;
172362
172603
  this.indexManager = services.shared.workspace.IndexManager;
172363
172604
  this.langiumDocuments = services.shared.workspace.LangiumDocuments;
172364
172605
  this.astNodeLocator = services.workspace.AstNodeLocator;
@@ -172374,23 +172615,38 @@ var SysmlValidator = class _SysmlValidator {
172374
172615
  // Langium linker cannot diagnose a missing intermediate feature. Resolve the
172375
172616
  // chain here and underline only the first invalid segment.
172376
172617
  checkPathExpr(node, accept) {
172618
+ const fault = this.featurePathFault(node);
172619
+ if (!fault)
172620
+ return;
172621
+ accept(severity("RES001", "error"), DIAGNOSTIC_MESSAGES.RES001_FEATURE_PATH_SEGMENT(fault.segment, fault.owner), {
172622
+ node,
172623
+ range: fault.range,
172624
+ code: "RES001",
172625
+ data: { featurePathSegment: true }
172626
+ });
172627
+ }
172628
+ // REQ-317, REQ-396 — The one place that decides whether a written feature path
172629
+ // has a provably absent segment. Extracted so an embedded representation body is
172630
+ // judged by exactly this rule (issue #150 review): a body is grafted onto the
172631
+ // element it represents and is therefore unreachable from the document walk that
172632
+ // dispatches `checkPathExpr`, so without a shared decision the two would drift
172633
+ // and a body would quietly escape a diagnostic the surrounding model receives.
172634
+ featurePathFault(node) {
172377
172635
  let declaration = node.$container;
172378
172636
  while (declaration && !isPartDecl(declaration))
172379
172637
  declaration = declaration.$container;
172380
172638
  const modifiers2 = declaration?.modifiers ?? [];
172381
172639
  if (!declaration || !modifiers2.includes("ref"))
172382
- return;
172640
+ return void 0;
172383
172641
  const resolution = this.featurePaths.resolve(node);
172384
172642
  if (resolution.unresolvedIndex === void 0)
172385
- return;
172643
+ return void 0;
172386
172644
  const invalid = resolution.segments[resolution.unresolvedIndex];
172387
- const owner = resolution.unresolvedIndex > 0 ? resolution.segments[resolution.unresolvedIndex - 1].text : "the current scope";
172388
- accept(severity("RES001", "error"), DIAGNOSTIC_MESSAGES.RES001_FEATURE_PATH_SEGMENT(invalid.text, owner), {
172389
- node,
172390
- range: invalid.cst.range,
172391
- code: "RES001",
172392
- data: { featurePathSegment: true }
172393
- });
172645
+ return {
172646
+ segment: invalid.text,
172647
+ owner: resolution.unresolvedIndex > 0 ? resolution.segments[resolution.unresolvedIndex - 1].text : "the current scope",
172648
+ range: invalid.cst.range
172649
+ };
172394
172650
  }
172395
172651
  // REQ-389 — Namespace qualification versus feature chaining in every written path
172396
172652
  // issue #213 — RES019: `::` binds tighter than `.`. Each dot-separated link of
@@ -172621,6 +172877,7 @@ var SysmlValidator = class _SysmlValidator {
172621
172877
  this.checkConnectorForms(node, accept);
172622
172878
  this.checkActionStartSuccessions(node, accept);
172623
172879
  this.checkAnnotationForms(node, accept);
172880
+ this.checkEmbeddedRepresentations(node, accept);
172624
172881
  this.checkLanguageDisjointness(node, accept);
172625
172882
  this.checkKermlWellFormedness(node, accept);
172626
172883
  this.checkFilterExpressions(node, accept);
@@ -172961,6 +173218,132 @@ ${baseIndent}}`;
172961
173218
  }
172962
173219
  }
172963
173220
  }
173221
+ // REQ-396 — SYN076/SEM012 embedded textual representations (issue #150).
173222
+ // A `rep … language "sysml"` (or `"kerml"`, matched case-insensitively) is not
173223
+ // opaque content: OMG KerML §7.4.3 requires its body to be a legal representation
173224
+ // of the element it annotates. So the body is processed by the comment-body rules,
173225
+ // parsed, and compared against that element:
173226
+ // SYN076 — the body is not valid SysML, reported ON the offending character
173227
+ // inside the body rather than on the `rep` keyword.
173228
+ // SEM012 — the body parses but represents a different element (wrong kind, or
173229
+ // the right kind under a different name).
173230
+ // Every other language stays opaque and is never parsed — an `alf`, `ocl` or
173231
+ // `yaml` body is content, and the vendored OMG corpus holds only those.
173232
+ checkEmbeddedRepresentations(node, accept) {
173233
+ const doc = ast_utils_exports.getDocument(node);
173234
+ if (!doc?.textDocument)
173235
+ return;
173236
+ let parser;
173237
+ for (const child of ast_utils_exports.streamAllContents(node)) {
173238
+ if (child.$type !== "RepStmt")
173239
+ continue;
173240
+ if (!isEmbeddedLanguage(child.language))
173241
+ continue;
173242
+ parser ??= this.serviceRegistry.getServices(doc.uri).parser.LangiumParser;
173243
+ const evaluated = evaluateRepresentation(parser, child);
173244
+ if (!evaluated)
173245
+ continue;
173246
+ const { span, verdict } = evaluated;
173247
+ const language = String(child.language ?? "").replace(/^"|"$/gu, "");
173248
+ if (verdict.status === "unparsed") {
173249
+ const { line: line2, character, message } = verdict.failure;
173250
+ const anchor = bodyOffsetAt(span, line2, character);
173251
+ const after = bodyOffsetAt(span, line2, character + 1);
173252
+ const from = after > anchor ? anchor : bodyOffsetAt(span, line2, Math.max(0, character - 1));
173253
+ accept(severity("SYN076", "error"), DIAGNOSTIC_MESSAGES.SYN076_EMBEDDED_REPRESENTATION_SYNTAX(language, message), {
173254
+ node: child,
173255
+ range: {
173256
+ start: doc.textDocument.positionAt(from),
173257
+ end: doc.textDocument.positionAt(Math.max(after, anchor))
173258
+ },
173259
+ code: "SYN076"
173260
+ });
173261
+ continue;
173262
+ }
173263
+ for (const reference of evaluated.unresolved) {
173264
+ accept(severity("RES020", "warning"), DIAGNOSTIC_MESSAGES.RES020_REPRESENTATION_UNRESOLVED(reference.refText, language), {
173265
+ node: child,
173266
+ range: {
173267
+ start: doc.textDocument.positionAt(bodyOffsetAt(span, reference.line, reference.character)),
173268
+ end: doc.textDocument.positionAt(bodyOffsetAt(span, reference.line, reference.character + reference.length))
173269
+ },
173270
+ code: "RES020"
173271
+ });
173272
+ }
173273
+ if (evaluated.fragment) {
173274
+ this.checkEmbeddedDialect(child, evaluated.fragment, language, span, doc, accept);
173275
+ this.checkEmbeddedFeaturePaths(child, evaluated.fragment, language, span, doc, accept);
173276
+ }
173277
+ if (verdict.status === "ok")
173278
+ continue;
173279
+ const { reason, expected, actual } = verdict.mismatch;
173280
+ const owner = child.$container;
173281
+ accept(severity("SEM012", "warning"), reason === "kind" ? DIAGNOSTIC_MESSAGES.SEM012_REPRESENTATION_WRONG_KIND(expected, actual) : DIAGNOSTIC_MESSAGES.SEM012_REPRESENTATION_WRONG_NAME(expected, actual), {
173282
+ node: child,
173283
+ range: {
173284
+ start: doc.textDocument.positionAt(bodyOffsetAt(span, 0, 0)),
173285
+ end: doc.textDocument.positionAt(bodyOffsetAt(span, 0, span.lines[0]?.text.length ?? 0))
173286
+ },
173287
+ code: "SEM012",
173288
+ relatedInformation: owner ? [relatedInfo(owner, "The element this representation annotates.")].filter((info) => info !== void 0) : void 0
173289
+ });
173290
+ }
173291
+ }
173292
+ // REQ-396 — SYN100/SYN101 inside a representation body (issue #150 review).
173293
+ // The normative rule is that the body is legal in the language the representation
173294
+ // NAMES, which is not the language of the file it sits in: a `.sysml` file may
173295
+ // carry a `language "kerml"` representation, and its body may then use no
173296
+ // SysML-only construct. `checkLanguageDisjointness` answers a different question
173297
+ // — what may this FILE contain — from the host URI, so the same tables are read
173298
+ // here against the declared language instead.
173299
+ checkEmbeddedDialect(rep, fragment, language, span, doc, accept) {
173300
+ const dialect = language.trim().toLowerCase();
173301
+ const isKerml = dialect === "kerml";
173302
+ const forbidden = isKerml ? SYSML_ONLY_TYPES : KERML_ONLY_TYPES;
173303
+ const code = isKerml ? "SYN100" : "SYN101";
173304
+ for (const node of [fragment, ...ast_utils_exports.streamAllContents(fragment)]) {
173305
+ if (!forbidden.has(node.$type))
173306
+ continue;
173307
+ const range = node.$cstNode?.range;
173308
+ if (!range)
173309
+ continue;
173310
+ accept(severity(code, "error"), isKerml ? `'${declKeyword(node)}' is a SysML construct and is not available in a "${language}" representation.` : `'${declKeyword(node)}' is a KerML-only construct \u2014 use the SysML form in a "${language}" representation.`, {
173311
+ node: rep,
173312
+ range: this.bodyRange(span, doc, range),
173313
+ code
173314
+ });
173315
+ }
173316
+ }
173317
+ // REQ-396 — RES020 for a written feature path with a provably absent segment
173318
+ // (issue #150 review). `checkPathExpr` is registered as a node-level check, so
173319
+ // Langium dispatches it by walking the document AST and never reaches the grafted
173320
+ // fragment. The decision itself is shared with it through `featurePathFault`, so
173321
+ // a body is held to the same rule as the model around it rather than a parallel
173322
+ // one that could drift; only the code differs, keeping every fault found IN a
173323
+ // representation body under the one severity a team can tune.
173324
+ checkEmbeddedFeaturePaths(rep, fragment, language, span, doc, accept) {
173325
+ for (const node of [fragment, ...ast_utils_exports.streamAllContents(fragment)]) {
173326
+ if (!this.featurePaths.isPathExpr(node))
173327
+ continue;
173328
+ const fault = this.featurePathFault(node);
173329
+ if (!fault)
173330
+ continue;
173331
+ accept(severity("RES020", "warning"), DIAGNOSTIC_MESSAGES.RES020_REPRESENTATION_PATH_SEGMENT(fault.segment, fault.owner, language), {
173332
+ node: rep,
173333
+ range: this.bodyRange(span, doc, fault.range),
173334
+ code: "RES020"
173335
+ });
173336
+ }
173337
+ }
173338
+ // REQ-396 — A range inside a parsed body, expressed in document coordinates.
173339
+ // Fragment CST positions are line/character within the PROCESSED body, which is
173340
+ // exactly what the span's line table converts.
173341
+ bodyRange(span, doc, range) {
173342
+ return {
173343
+ start: doc.textDocument.positionAt(bodyOffsetAt(span, range.start.line, range.start.character)),
173344
+ end: doc.textDocument.positionAt(bodyOffsetAt(span, range.end.line, range.end.character))
173345
+ };
173346
+ }
172964
173347
  // REQ-311 — SYN050/051 connector syntax, with a specific message in place of
172965
173348
  // the grammar's generic parse error:
172966
173349
  // SYN050 — `connect a with b;` (use `connect a to b;`).
@@ -197539,7 +197922,7 @@ async function runExport(command) {
197539
197922
  }
197540
197923
 
197541
197924
  // src/main.ts
197542
- var VERSION2 = true ? "0.16.0" : "dev";
197925
+ var VERSION2 = true ? "0.17.0" : "dev";
197543
197926
  function display(file) {
197544
197927
  const rel2 = path9.relative(process.cwd(), file);
197545
197928
  return rel2 && !rel2.startsWith("..") ? rel2.split(path9.sep).join("/") : file;