@istar-ts/core 0.11.0 → 0.12.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/README.md +9 -0
- package/dist/index.cjs +180 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +133 -0
- package/dist/index.d.ts +133 -0
- package/dist/index.js +171 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -304,6 +304,15 @@ toPistar(extended); // writes the "metamodel" block
|
|
|
304
304
|
into a store (`store.load` or `store.replace`) to keep editing.
|
|
305
305
|
- `pistarType` works as for any extension, so `istar.<Name>` types on disk still read.
|
|
306
306
|
|
|
307
|
+
## Diagnostics
|
|
308
|
+
|
|
309
|
+
`GoalDiagnostic` (`{ elementId, key?, severity, message, source?, range?, code?, data? }`) is the
|
|
310
|
+
data shape for findings about elements and links. Analysers publish them per source to a
|
|
311
|
+
`createDiagnosticsStore()`; `getAll()` returns the union of every source, deduplicated by
|
|
312
|
+
`(elementId, key, message)`. `fromLspDiagnostics` and `fromNodeIdDiagnostics` adapt LSP and MutRoSe
|
|
313
|
+
diagnostics. Diagnostics are never part of the model and never serialized. `@istar-ts/react`
|
|
314
|
+
shows them as badges and in the inspector (see its README).
|
|
315
|
+
|
|
307
316
|
## Credits
|
|
308
317
|
|
|
309
318
|
The metamodel, link constraints, element shapes, and file format are derived from
|
package/dist/index.cjs
CHANGED
|
@@ -2027,12 +2027,183 @@ function validateModelProperties(model, schemas) {
|
|
|
2027
2027
|
return issues;
|
|
2028
2028
|
}
|
|
2029
2029
|
//#endregion
|
|
2030
|
+
//#region src/diagnostics.ts
|
|
2031
|
+
/** Most severe first. */
|
|
2032
|
+
const DIAGNOSTIC_SEVERITIES = [
|
|
2033
|
+
"error",
|
|
2034
|
+
"warning",
|
|
2035
|
+
"info",
|
|
2036
|
+
"hint"
|
|
2037
|
+
];
|
|
2038
|
+
const RANK = {
|
|
2039
|
+
error: 0,
|
|
2040
|
+
warning: 1,
|
|
2041
|
+
info: 2,
|
|
2042
|
+
hint: 3
|
|
2043
|
+
};
|
|
2044
|
+
/** Negative when `a` is more severe than `b`. */
|
|
2045
|
+
function compareSeverity(a, b) {
|
|
2046
|
+
return RANK[a] - RANK[b];
|
|
2047
|
+
}
|
|
2048
|
+
/** The most severe severity in a list (what a badge shows), or `undefined` if it is empty. */
|
|
2049
|
+
function worstDiagnosticSeverity(diagnostics) {
|
|
2050
|
+
let worst;
|
|
2051
|
+
for (const d of diagnostics ?? []) {
|
|
2052
|
+
if (worst === void 0 || RANK[d.severity] < RANK[worst]) worst = d.severity;
|
|
2053
|
+
if (worst === "error") break;
|
|
2054
|
+
}
|
|
2055
|
+
return worst;
|
|
2056
|
+
}
|
|
2057
|
+
function identity(d) {
|
|
2058
|
+
return `${d.elementId}\u0000${d.key ?? ""}\u0000${d.message}`;
|
|
2059
|
+
}
|
|
2060
|
+
/**
|
|
2061
|
+
* The union of several lists, in order, without duplicates: diagnostics with the same
|
|
2062
|
+
* `(elementId, key, message)` count once, keeping the first one's position and the most severe
|
|
2063
|
+
* severity among them.
|
|
2064
|
+
*/
|
|
2065
|
+
function mergeDiagnostics(lists) {
|
|
2066
|
+
const result = [];
|
|
2067
|
+
const index = /* @__PURE__ */ new Map();
|
|
2068
|
+
for (const list of lists) for (const d of list ?? []) {
|
|
2069
|
+
const id = identity(d);
|
|
2070
|
+
const at = index.get(id);
|
|
2071
|
+
if (at === void 0) {
|
|
2072
|
+
index.set(id, result.length);
|
|
2073
|
+
result.push(d);
|
|
2074
|
+
} else if (RANK[d.severity] < RANK[result[at].severity]) result[at] = {
|
|
2075
|
+
...result[at],
|
|
2076
|
+
severity: d.severity
|
|
2077
|
+
};
|
|
2078
|
+
}
|
|
2079
|
+
return result;
|
|
2080
|
+
}
|
|
2081
|
+
/** Diagnostics by element (or link) id, each list in the input order. */
|
|
2082
|
+
function groupDiagnostics(diagnostics) {
|
|
2083
|
+
const map = /* @__PURE__ */ new Map();
|
|
2084
|
+
for (const d of diagnostics ?? []) {
|
|
2085
|
+
const list = map.get(d.elementId);
|
|
2086
|
+
if (list) list.push(d);
|
|
2087
|
+
else map.set(d.elementId, [d]);
|
|
2088
|
+
}
|
|
2089
|
+
return map;
|
|
2090
|
+
}
|
|
2091
|
+
const NONE = [];
|
|
2092
|
+
function createDiagnosticsStore() {
|
|
2093
|
+
const bySource = /* @__PURE__ */ new Map();
|
|
2094
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
2095
|
+
let merged = NONE;
|
|
2096
|
+
const changed = () => {
|
|
2097
|
+
merged = bySource.size === 0 ? NONE : mergeDiagnostics(bySource.values());
|
|
2098
|
+
for (const listener of listeners) listener();
|
|
2099
|
+
};
|
|
2100
|
+
return {
|
|
2101
|
+
publish(source, diagnostics) {
|
|
2102
|
+
if (diagnostics.length === 0) {
|
|
2103
|
+
if (bySource.delete(source)) changed();
|
|
2104
|
+
return;
|
|
2105
|
+
}
|
|
2106
|
+
bySource.set(source, diagnostics.map((d) => d.source === void 0 ? {
|
|
2107
|
+
...d,
|
|
2108
|
+
source
|
|
2109
|
+
} : d));
|
|
2110
|
+
changed();
|
|
2111
|
+
},
|
|
2112
|
+
clear(source) {
|
|
2113
|
+
if (source === void 0) {
|
|
2114
|
+
if (bySource.size === 0) return;
|
|
2115
|
+
bySource.clear();
|
|
2116
|
+
} else if (!bySource.delete(source)) return;
|
|
2117
|
+
changed();
|
|
2118
|
+
},
|
|
2119
|
+
sources: () => [...bySource.keys()],
|
|
2120
|
+
get: (source) => bySource.get(source) ?? NONE,
|
|
2121
|
+
getAll: () => merged,
|
|
2122
|
+
subscribe(listener) {
|
|
2123
|
+
listeners.add(listener);
|
|
2124
|
+
return () => listeners.delete(listener);
|
|
2125
|
+
}
|
|
2126
|
+
};
|
|
2127
|
+
}
|
|
2128
|
+
const LSP_SEVERITIES = [
|
|
2129
|
+
"error",
|
|
2130
|
+
"warning",
|
|
2131
|
+
"info",
|
|
2132
|
+
"hint"
|
|
2133
|
+
];
|
|
2134
|
+
function anchorOf(data) {
|
|
2135
|
+
if (typeof data !== "object" || data === null) return {};
|
|
2136
|
+
const record = data;
|
|
2137
|
+
const elementId = typeof record.elementId === "string" ? record.elementId : typeof record.nodeId === "string" ? record.nodeId : void 0;
|
|
2138
|
+
return {
|
|
2139
|
+
...elementId ? { elementId } : {},
|
|
2140
|
+
...typeof record.key === "string" ? { key: record.key } : {}
|
|
2141
|
+
};
|
|
2142
|
+
}
|
|
2143
|
+
function convertLsp(d, options) {
|
|
2144
|
+
const anchor = anchorOf(d.data);
|
|
2145
|
+
const elementId = anchor.elementId ?? options.elementIdFor?.(d);
|
|
2146
|
+
if (!elementId) return void 0;
|
|
2147
|
+
const base = options.severityNumbering === "vscode" ? 0 : 1;
|
|
2148
|
+
const severity = d.severity === void 0 ? "error" : LSP_SEVERITIES[d.severity - base] ?? "error";
|
|
2149
|
+
const code = typeof d.code === "object" ? d.code.value : d.code;
|
|
2150
|
+
return {
|
|
2151
|
+
elementId,
|
|
2152
|
+
...anchor.key !== void 0 ? { key: anchor.key } : {},
|
|
2153
|
+
severity,
|
|
2154
|
+
message: typeof d.message === "string" ? d.message : d.message.value,
|
|
2155
|
+
...d.source !== void 0 ? { source: d.source } : {},
|
|
2156
|
+
...d.range ? { range: d.range } : {},
|
|
2157
|
+
...code !== void 0 ? { code: String(code) } : {},
|
|
2158
|
+
...d.data !== void 0 ? { data: d.data } : {}
|
|
2159
|
+
};
|
|
2160
|
+
}
|
|
2161
|
+
/**
|
|
2162
|
+
* One LSP diagnostic anchored by `data.elementId` (or `data.nodeId`), or `undefined` when it
|
|
2163
|
+
* names no element. For range-anchored diagnostics use `fromLspDiagnostics` with `elementIdFor`.
|
|
2164
|
+
*/
|
|
2165
|
+
function fromLspDiagnostic(diagnostic) {
|
|
2166
|
+
return convertLsp(diagnostic, {});
|
|
2167
|
+
}
|
|
2168
|
+
/** LSP diagnostics as goal diagnostics; those that can't be anchored to an element are dropped. */
|
|
2169
|
+
function fromLspDiagnostics(diagnostics, options = {}) {
|
|
2170
|
+
const result = [];
|
|
2171
|
+
for (const d of diagnostics) {
|
|
2172
|
+
const converted = convertLsp(d, options);
|
|
2173
|
+
if (converted) result.push(converted);
|
|
2174
|
+
}
|
|
2175
|
+
return result;
|
|
2176
|
+
}
|
|
2177
|
+
/** One `{ nodeId, severity, message }` diagnostic, or `undefined` without a `nodeId`. */
|
|
2178
|
+
function fromNodeIdDiagnostic(diagnostic) {
|
|
2179
|
+
const { nodeId, severity, ...rest } = diagnostic;
|
|
2180
|
+
if (!nodeId) return void 0;
|
|
2181
|
+
return {
|
|
2182
|
+
...rest,
|
|
2183
|
+
elementId: nodeId,
|
|
2184
|
+
severity: Object.hasOwn(RANK, severity) ? severity : "info"
|
|
2185
|
+
};
|
|
2186
|
+
}
|
|
2187
|
+
/**
|
|
2188
|
+
* `{ nodeId, severity, message }` diagnostics (MutRoSe) as goal diagnostics, dropping those
|
|
2189
|
+
* without a node: `store.publish('mutrose', fromNodeIdDiagnostics(message.diagnostics))`.
|
|
2190
|
+
*/
|
|
2191
|
+
function fromNodeIdDiagnostics(diagnostics) {
|
|
2192
|
+
const result = [];
|
|
2193
|
+
for (const d of diagnostics) {
|
|
2194
|
+
const converted = fromNodeIdDiagnostic(d);
|
|
2195
|
+
if (converted) result.push(converted);
|
|
2196
|
+
}
|
|
2197
|
+
return result;
|
|
2198
|
+
}
|
|
2199
|
+
//#endregion
|
|
2030
2200
|
exports.ACTOR_KINDS = ACTOR_KINDS;
|
|
2031
2201
|
exports.ACTOR_LINK_KINDS = ACTOR_LINK_KINDS;
|
|
2032
2202
|
exports.CONTRIBUTION_LABELS = CONTRIBUTION_LABELS;
|
|
2033
2203
|
exports.DEFAULT_DIAGRAM = DEFAULT_DIAGRAM;
|
|
2034
2204
|
exports.DEFAULT_ELEMENT_SIZE = DEFAULT_ELEMENT_SIZE;
|
|
2035
2205
|
exports.DEPENDENCY_LINK_KINDS = DEPENDENCY_LINK_KINDS;
|
|
2206
|
+
exports.DIAGNOSTIC_SEVERITIES = DIAGNOSTIC_SEVERITIES;
|
|
2036
2207
|
exports.ELEMENT_KINDS = ELEMENT_KINDS;
|
|
2037
2208
|
exports.FILE_METAMODEL_KEY = FILE_METAMODEL_KEY;
|
|
2038
2209
|
exports.ISTAR_2_0 = ISTAR_2_0;
|
|
@@ -2054,7 +2225,9 @@ exports.addElement = addElement;
|
|
|
2054
2225
|
exports.boolean = boolean;
|
|
2055
2226
|
exports.canLink = canLink;
|
|
2056
2227
|
exports.childrenOf = childrenOf;
|
|
2228
|
+
exports.compareSeverity = compareSeverity;
|
|
2057
2229
|
exports.connect = connect;
|
|
2230
|
+
exports.createDiagnosticsStore = createDiagnosticsStore;
|
|
2058
2231
|
exports.createEmptyModel = createEmptyModel;
|
|
2059
2232
|
exports.createModelStore = createModelStore;
|
|
2060
2233
|
exports.defaultIdGenerator = defaultIdGenerator;
|
|
@@ -2071,7 +2244,12 @@ exports.enumOf = enumOf;
|
|
|
2071
2244
|
exports.extendMetamodel = extendMetamodel;
|
|
2072
2245
|
exports.fileMetamodelExtension = fileMetamodelExtension;
|
|
2073
2246
|
exports.fileMetamodelOf = fileMetamodelOf;
|
|
2247
|
+
exports.fromLspDiagnostic = fromLspDiagnostic;
|
|
2248
|
+
exports.fromLspDiagnostics = fromLspDiagnostics;
|
|
2249
|
+
exports.fromNodeIdDiagnostic = fromNodeIdDiagnostic;
|
|
2250
|
+
exports.fromNodeIdDiagnostics = fromNodeIdDiagnostics;
|
|
2074
2251
|
exports.getSourceLayout = getSourceLayout;
|
|
2252
|
+
exports.groupDiagnostics = groupDiagnostics;
|
|
2075
2253
|
exports.hostMetamodelOf = hostMetamodelOf;
|
|
2076
2254
|
exports.inheritMetamodel = inheritMetamodel;
|
|
2077
2255
|
exports.inheritSourceLayout = inheritSourceLayout;
|
|
@@ -2091,6 +2269,7 @@ exports.isNodeKindIn = isNodeKindIn;
|
|
|
2091
2269
|
exports.isNodeLinkKind = isNodeLinkKind;
|
|
2092
2270
|
exports.linkKindDefinition = linkKindDefinition;
|
|
2093
2271
|
exports.linksOf = linksOf;
|
|
2272
|
+
exports.mergeDiagnostics = mergeDiagnostics;
|
|
2094
2273
|
exports.metamodelOf = metamodelOf;
|
|
2095
2274
|
exports.moveElement = moveElement;
|
|
2096
2275
|
exports.nestElement = nestElement;
|
|
@@ -2112,5 +2291,6 @@ exports.validateModel = validateModel;
|
|
|
2112
2291
|
exports.validateModelProperties = validateModelProperties;
|
|
2113
2292
|
exports.withFileMetamodel = withFileMetamodel;
|
|
2114
2293
|
exports.withMetamodel = withMetamodel;
|
|
2294
|
+
exports.worstDiagnosticSeverity = worstDiagnosticSeverity;
|
|
2115
2295
|
|
|
2116
2296
|
//# sourceMappingURL=index.cjs.map
|