sysml-validate 0.15.0 → 0.15.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.
package/out/main.js CHANGED
@@ -30760,6 +30760,9 @@ function isExhibitStateDecl(item) {
30760
30760
  var ExitAction = "ExitAction";
30761
30761
  var ExposeMember = "ExposeMember";
30762
30762
  var ExposePath = "ExposePath";
30763
+ function isExposePath(item) {
30764
+ return reflection2.isInstance(item, ExposePath);
30765
+ }
30763
30766
  var ExpressionDecl = "ExpressionDecl";
30764
30767
  var FeatureDecl = "FeatureDecl";
30765
30768
  function isFeatureDecl(item) {
@@ -58052,14 +58055,14 @@ function evaluateFilterCondition(node, metadataTypes, resolveRefs) {
58052
58055
  return node.items.length === 1 ? evaluateFilterCondition(node.items[0], metadataTypes, resolveRefs) : void 0;
58053
58056
  }
58054
58057
  if (isBinExpr(node)) {
58055
- if (node.op === "and" || node.op === "&") {
58058
+ if (CONJUNCTION_OPERATORS.has(node.op)) {
58056
58059
  const left = evaluateFilterCondition(node.left, metadataTypes, resolveRefs);
58057
58060
  const right = evaluateFilterCondition(node.right, metadataTypes, resolveRefs);
58058
58061
  if (left === false || right === false)
58059
58062
  return false;
58060
58063
  return left === void 0 || right === void 0 ? void 0 : true;
58061
58064
  }
58062
- if (node.op === "or" || node.op === "|") {
58065
+ if (DISJUNCTION_OPERATORS.has(node.op)) {
58063
58066
  const left = evaluateFilterCondition(node.left, metadataTypes, resolveRefs);
58064
58067
  const right = evaluateFilterCondition(node.right, metadataTypes, resolveRefs);
58065
58068
  if (left === true || right === true)
@@ -58070,6 +58073,42 @@ function evaluateFilterCondition(node, metadataTypes, resolveRefs) {
58070
58073
  }
58071
58074
  return void 0;
58072
58075
  }
58076
+ var CONJUNCTION_OPERATORS = /* @__PURE__ */ new Set(["and", "&"]);
58077
+ var DISJUNCTION_OPERATORS = /* @__PURE__ */ new Set(["or", "|"]);
58078
+ function undecidableFilterCondition(condition) {
58079
+ if (!condition)
58080
+ return void 0;
58081
+ if (isSelfClassifyExpr(condition)) {
58082
+ return condition.op === "@" || condition.op === "@@" ? void 0 : { node: condition, construct: `the '${condition.op}' classification operator` };
58083
+ }
58084
+ if (isNotExpr(condition) && condition.operand) {
58085
+ return undecidableFilterCondition(condition.operand);
58086
+ }
58087
+ if (condition.$type === "NotExpr")
58088
+ return void 0;
58089
+ if (isParenExpr(condition)) {
58090
+ return condition.items.length === 1 ? undecidableFilterCondition(condition.items[0]) : { node: condition, construct: "a parenthesized expression list" };
58091
+ }
58092
+ if (isBinExpr(condition)) {
58093
+ if (CONJUNCTION_OPERATORS.has(condition.op) || DISJUNCTION_OPERATORS.has(condition.op)) {
58094
+ return undecidableFilterCondition(condition.left) ?? undecidableFilterCondition(condition.right);
58095
+ }
58096
+ return { node: condition, construct: `the '${condition.op}' operator` };
58097
+ }
58098
+ return { node: condition, construct: describeFilterExpr(condition) };
58099
+ }
58100
+ function describeFilterExpr(node) {
58101
+ if (isPostfixOp(node)) {
58102
+ if ((node.dot || node.select) && node.field)
58103
+ return `the '.${node.field}' feature access`;
58104
+ if (node.arrow && node.invoke)
58105
+ return `the '->${node.invoke}' invocation`;
58106
+ return "this postfix expression";
58107
+ }
58108
+ if (isPathExpr(node))
58109
+ return "a plain feature reference";
58110
+ return "this expression form";
58111
+ }
58073
58112
  function attachedMetadataTypes(node, resolveRefs) {
58074
58113
  const siblings = node.$container?.members;
58075
58114
  if (!Array.isArray(siblings))
@@ -58713,6 +58752,13 @@ var DIAGNOSTIC_MESSAGES = {
58713
58752
  // implicit-redefinition resolution this validator does not yet model.
58714
58753
  RES017_DUPLICATE_MEMBER: (name) => `'${name}' is already declared in this namespace. Members must be distinguishable by name \u2014 rename or remove the duplicate.`,
58715
58754
  RES017_DUPLICATE_SHORT_NAME: (name) => `Short name '<${name}>' is already used in this namespace. Members must be distinguishable \u2014 rename the short name.`,
58755
+ // issue #102 — REQ-387. The filter evaluator (`import-visibility.ts`,
58756
+ // `evaluateFilterCondition`) decides metadata-presence tests and the Boolean
58757
+ // operators over them; every other form fails OPEN, keeping the member. Failing
58758
+ // open is the right default — under-importing would manufacture spurious
58759
+ // RES001s — but an unevaluated filter produces a scope or view that looks
58760
+ // authoritative and is not, so the fail-open is stated rather than assumed.
58761
+ RES018_UNEVALUATED_FILTER: (subject, construct) => `${subject} filter is not evaluated: ${construct} is outside the metadata-filter evaluator, which decides '@'/'@@' metadata tests combined with 'not', 'and'/'&', and 'or'/'|'. The filter fails open \u2014 it is treated as pass-through and removes nothing.`,
58716
58762
  // REQ-007 — SysML v1 → v2 migration guardrail.
58717
58763
  MIG001_V1_KEYWORD: (keyword, v2, note) => `'${keyword}' is a SysML v1 construct, not a SysML v2 keyword. ${note} Use '${v2}'.`,
58718
58764
  MIG002_V1_STEREOTYPE_KNOWN: (stereotype, v2, note) => `SysML v2 has no stereotypes. ${note} Replace \xAB${stereotype}\xBB with '${v2}'.`,
@@ -60567,6 +60613,7 @@ ${baseIndent}}`;
60567
60613
  checkCrossReferences(node, accept) {
60568
60614
  const index = this.buildIndex();
60569
60615
  const imports = [];
60616
+ const exposePaths = [];
60570
60617
  const decls = [];
60571
60618
  const verifyStmts = [];
60572
60619
  const satisfyStmts = [];
@@ -60578,7 +60625,9 @@ ${baseIndent}}`;
60578
60625
  const path10 = importedPath(child);
60579
60626
  if (alias && path10 && importWildcard(child) === "none")
60580
60627
  aliasMap.set(alias, path10);
60581
- } else if (isVerifyStmt(child))
60628
+ } else if (isExposePath(child))
60629
+ exposePaths.push(child);
60630
+ else if (isVerifyStmt(child))
60582
60631
  verifyStmts.push(child);
60583
60632
  else if (isSatisfyStmt(child))
60584
60633
  satisfyStmts.push(child);
@@ -60594,6 +60643,7 @@ ${baseIndent}}`;
60594
60643
  this.checkPrivateImports(imports, index, accept);
60595
60644
  this.checkPrivateWildcardReferences(node, imports, index, accept);
60596
60645
  this.checkImportCycles(imports, index, accept);
60646
+ this.checkUnevaluatedFilters(imports, exposePaths, accept);
60597
60647
  if (sysmlDiagnosticSettings.unusedImports !== "off") {
60598
60648
  this.checkUnusedImports(node, imports, index, sysmlDiagnosticSettings.unusedImports, accept);
60599
60649
  }
@@ -61212,6 +61262,34 @@ ${baseIndent}}`;
61212
61262
  }
61213
61263
  return false;
61214
61264
  }
61265
+ /**
61266
+ * REQ-387 — RES018: an `import` or `expose` filter the metadata-filter
61267
+ * evaluator cannot decide (issue #102).
61268
+ *
61269
+ * The evaluator keeps a member it cannot decide, so an unevaluated filter
61270
+ * silently contributes the members it was written to exclude — a scope or a
61271
+ * view that reads as authoritative and is not. Failing open stays (narrowing
61272
+ * on a guess would manufacture unresolved-name errors); what changes is that
61273
+ * the model now says so at the filter that is not applied.
61274
+ *
61275
+ * Default severity is `info` rather than a warning: the model is legal and the
61276
+ * limitation is this evaluator's, not the author's. Teams that want an
61277
+ * unevaluated filter to break a build raise it per code through
61278
+ * `sysml.validation.severities` (REQ-240).
61279
+ */
61280
+ checkUnevaluatedFilters(imports, exposePaths, accept) {
61281
+ const report = (condition, subject) => {
61282
+ const undecidable = undecidableFilterCondition(condition);
61283
+ if (!undecidable)
61284
+ return;
61285
+ accept(severity("RES018", "info"), DIAGNOSTIC_MESSAGES.RES018_UNEVALUATED_FILTER(subject, undecidable.construct), { node: undecidable.node, code: "RES018" });
61286
+ };
61287
+ for (const imp of imports)
61288
+ for (const filter3 of imp.filters)
61289
+ report(filter3.condition, "Import");
61290
+ for (const path10 of exposePaths)
61291
+ report(path10.condition, "Expose");
61292
+ }
61215
61293
  // REQ-324 — RES009 unused import (configurable, OFF by default)
61216
61294
  checkUnusedImports(root3, imports, index, level, accept) {
61217
61295
  if (imports.length === 0)
@@ -69031,7 +69109,7 @@ async function runValidation(command) {
69031
69109
  }
69032
69110
 
69033
69111
  // src/main.ts
69034
- var VERSION2 = true ? "0.15.0" : "dev";
69112
+ var VERSION2 = true ? "0.15.1" : "dev";
69035
69113
  async function main(argv) {
69036
69114
  const command = parseArgs(argv);
69037
69115
  if (command.kind === "help") {