@documentation-robotics/cli 0.1.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/PYTHON_CLI_PARITY.md +265 -0
- package/PYTHON_CLI_STRUCTURE_UPDATE.md +246 -0
- package/README.md +926 -0
- package/biome.json +42 -0
- package/bunfig.toml +3 -0
- package/dist/ai/claude-client.js +71 -0
- package/dist/ai/claude-client.js.map +7 -0
- package/dist/ai/context-provider.js +129 -0
- package/dist/ai/context-provider.js.map +7 -0
- package/dist/ai/tools.js +231 -0
- package/dist/ai/tools.js.map +7 -0
- package/dist/cli.js +224 -0
- package/dist/cli.js.map +7 -0
- package/dist/commands/add.js +51 -0
- package/dist/commands/add.js.map +7 -0
- package/dist/commands/changeset.js +174 -0
- package/dist/commands/changeset.js.map +7 -0
- package/dist/commands/chat.js +111 -0
- package/dist/commands/chat.js.map +7 -0
- package/dist/commands/conformance.js +132 -0
- package/dist/commands/conformance.js.map +7 -0
- package/dist/commands/delete.js +31 -0
- package/dist/commands/delete.js.map +7 -0
- package/dist/commands/element.js +28 -0
- package/dist/commands/element.js.map +7 -0
- package/dist/commands/export.js +65 -0
- package/dist/commands/export.js.map +7 -0
- package/dist/commands/info.js +40 -0
- package/dist/commands/info.js.map +7 -0
- package/dist/commands/init.js +45 -0
- package/dist/commands/init.js.map +7 -0
- package/dist/commands/list.js +32 -0
- package/dist/commands/list.js.map +7 -0
- package/dist/commands/migrate.js +36 -0
- package/dist/commands/migrate.js.map +7 -0
- package/dist/commands/project.js +74 -0
- package/dist/commands/project.js.map +7 -0
- package/dist/commands/relationship.js +118 -0
- package/dist/commands/relationship.js.map +7 -0
- package/dist/commands/search.js +48 -0
- package/dist/commands/search.js.map +7 -0
- package/dist/commands/show.js +38 -0
- package/dist/commands/show.js.map +7 -0
- package/dist/commands/trace.js +60 -0
- package/dist/commands/trace.js.map +7 -0
- package/dist/commands/update.js +37 -0
- package/dist/commands/update.js.map +7 -0
- package/dist/commands/upgrade.js +152 -0
- package/dist/commands/upgrade.js.map +7 -0
- package/dist/commands/validate.js +71 -0
- package/dist/commands/validate.js.map +7 -0
- package/dist/commands/version.js +20 -0
- package/dist/commands/version.js.map +7 -0
- package/dist/commands/visualize.js +51 -0
- package/dist/commands/visualize.js.map +7 -0
- package/dist/core/active-changeset.js +52 -0
- package/dist/core/active-changeset.js.map +7 -0
- package/dist/core/changeset.js +244 -0
- package/dist/core/changeset.js.map +7 -0
- package/dist/core/dependency-tracker.js +322 -0
- package/dist/core/dependency-tracker.js.map +7 -0
- package/dist/core/element.js +65 -0
- package/dist/core/element.js.map +7 -0
- package/dist/core/layer.js +75 -0
- package/dist/core/layer.js.map +7 -0
- package/dist/core/manifest.js +57 -0
- package/dist/core/manifest.js.map +7 -0
- package/dist/core/migration-registry.js +131 -0
- package/dist/core/migration-registry.js.map +7 -0
- package/dist/core/model.js +455 -0
- package/dist/core/model.js.map +7 -0
- package/dist/core/projection-engine.js +288 -0
- package/dist/core/projection-engine.js.map +7 -0
- package/dist/core/reference-registry.js +188 -0
- package/dist/core/reference-registry.js.map +7 -0
- package/dist/core/relationship-registry.js +114 -0
- package/dist/core/relationship-registry.js.map +7 -0
- package/dist/core/relationships.js +78 -0
- package/dist/core/relationships.js.map +7 -0
- package/dist/export/archimate-exporter.js +111 -0
- package/dist/export/archimate-exporter.js.map +7 -0
- package/dist/export/export-manager.js +63 -0
- package/dist/export/export-manager.js.map +7 -0
- package/dist/export/graphml-exporter.js +39 -0
- package/dist/export/graphml-exporter.js.map +7 -0
- package/dist/export/index.js +17 -0
- package/dist/export/index.js.map +7 -0
- package/dist/export/json-schema-exporter.js +60 -0
- package/dist/export/json-schema-exporter.js.map +7 -0
- package/dist/export/markdown-exporter.js +99 -0
- package/dist/export/markdown-exporter.js.map +7 -0
- package/dist/export/openapi-exporter.js +85 -0
- package/dist/export/openapi-exporter.js.map +7 -0
- package/dist/export/plantuml-exporter.js +60 -0
- package/dist/export/plantuml-exporter.js.map +7 -0
- package/dist/export/types.js +36 -0
- package/dist/export/types.js.map +7 -0
- package/dist/schemas/bundled/01-motivation-layer.schema.json +942 -0
- package/dist/schemas/bundled/02-business-layer.schema.json +1293 -0
- package/dist/schemas/bundled/03-security-layer.schema.json +2177 -0
- package/dist/schemas/bundled/04-application-layer.schema.json +1328 -0
- package/dist/schemas/bundled/05-technology-layer.schema.json +1465 -0
- package/dist/schemas/bundled/06-api-layer.schema.json +1743 -0
- package/dist/schemas/bundled/07-data-model-layer.schema.json +1253 -0
- package/dist/schemas/bundled/08-datastore-layer.schema.json +892 -0
- package/dist/schemas/bundled/09-ux-layer.schema.json +1506 -0
- package/dist/schemas/bundled/10-navigation-layer.schema.json +843 -0
- package/dist/schemas/bundled/11-apm-observability-layer.schema.json +1430 -0
- package/dist/schemas/bundled/12-testing-layer.schema.json +1091 -0
- package/dist/schemas/bundled/common/layer-extensions.schema.json +266 -0
- package/dist/schemas/bundled/common/predicates.schema.json +493 -0
- package/dist/schemas/bundled/common/relationships.schema.json +791 -0
- package/dist/schemas/bundled/common/source-references.schema.json +71 -0
- package/dist/schemas/bundled/link-registry.json +1839 -0
- package/dist/schemas/bundled/link-registry.schema.json +339 -0
- package/dist/schemas/bundled/relationship-catalog.json +1302 -0
- package/dist/schemas/bundled/relationship-type.schema.json +221 -0
- package/dist/server/server.js +1207 -0
- package/dist/server/server.js.map +7 -0
- package/dist/server/telemetry-middleware.js +24 -0
- package/dist/server/telemetry-middleware.js.map +7 -0
- package/dist/telemetry/console-interceptor.js +34 -0
- package/dist/telemetry/console-interceptor.js.map +7 -0
- package/dist/telemetry/index.js +54 -0
- package/dist/telemetry/index.js.map +7 -0
- package/dist/telemetry/resilient-exporter.js +41 -0
- package/dist/telemetry/resilient-exporter.js.map +7 -0
- package/dist/telemetry/resilient-log-exporter.js +41 -0
- package/dist/telemetry/resilient-log-exporter.js.map +7 -0
- package/dist/telemetry/test-instrumentation.js +32 -0
- package/dist/telemetry/test-instrumentation.js.map +7 -0
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +7 -0
- package/dist/utils/element-utils.js +11 -0
- package/dist/utils/element-utils.js.map +7 -0
- package/dist/utils/errors.js +105 -0
- package/dist/utils/errors.js.map +7 -0
- package/dist/utils/file-io.js +69 -0
- package/dist/utils/file-io.js.map +7 -0
- package/dist/utils/globals.js +28 -0
- package/dist/utils/globals.js.map +7 -0
- package/dist/utils/model-path.js +22 -0
- package/dist/utils/model-path.js.map +7 -0
- package/dist/utils/project-paths.js +45 -0
- package/dist/utils/project-paths.js.map +7 -0
- package/dist/utils/spec-installer.js +85 -0
- package/dist/utils/spec-installer.js.map +7 -0
- package/dist/utils/spec-version.js +33 -0
- package/dist/utils/spec-version.js.map +7 -0
- package/dist/validators/index.js +15 -0
- package/dist/validators/index.js.map +7 -0
- package/dist/validators/naming-validator.js +82 -0
- package/dist/validators/naming-validator.js.map +7 -0
- package/dist/validators/reference-validator.js +106 -0
- package/dist/validators/reference-validator.js.map +7 -0
- package/dist/validators/schema-validator.js +148 -0
- package/dist/validators/schema-validator.js.map +7 -0
- package/dist/validators/semantic-validator.js +80 -0
- package/dist/validators/semantic-validator.js.map +7 -0
- package/dist/validators/types.js +48 -0
- package/dist/validators/types.js.map +7 -0
- package/dist/validators/validator.js +67 -0
- package/dist/validators/validator.js.map +7 -0
- package/docs/TEST-INSTRUMENTATION.md +405 -0
- package/docs/telemetry.md +427 -0
- package/package.json +70 -0
- package/test-debug.js +33 -0
- package/test-special.mjs +46 -0
- package/test_runner.ts +7 -0
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import { readFile } from "fs/promises";
|
|
2
|
+
import { parse as parseYAML } from "yaml";
|
|
3
|
+
import { Element } from "./element.js";
|
|
4
|
+
class PropertyTransformer {
|
|
5
|
+
config;
|
|
6
|
+
constructor(config) {
|
|
7
|
+
this.config = config;
|
|
8
|
+
}
|
|
9
|
+
apply(value) {
|
|
10
|
+
if (value == null)
|
|
11
|
+
return null;
|
|
12
|
+
const strValue = String(value);
|
|
13
|
+
switch (this.config.type) {
|
|
14
|
+
case "uppercase":
|
|
15
|
+
return strValue.toUpperCase();
|
|
16
|
+
case "lowercase":
|
|
17
|
+
return strValue.toLowerCase();
|
|
18
|
+
case "kebab":
|
|
19
|
+
return toKebabCase(strValue);
|
|
20
|
+
case "snake":
|
|
21
|
+
return toSnakeCase(strValue);
|
|
22
|
+
case "pascal":
|
|
23
|
+
return toPascalCase(strValue);
|
|
24
|
+
case "prefix":
|
|
25
|
+
return `${this.config.value}${strValue}`;
|
|
26
|
+
case "suffix":
|
|
27
|
+
return `${strValue}${this.config.value}`;
|
|
28
|
+
case "template":
|
|
29
|
+
return this.config.value?.replace("{value}", strValue) ?? strValue;
|
|
30
|
+
default:
|
|
31
|
+
return value;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
class ConditionEvaluator {
|
|
36
|
+
config;
|
|
37
|
+
constructor(config) {
|
|
38
|
+
this.config = config;
|
|
39
|
+
}
|
|
40
|
+
evaluate(element) {
|
|
41
|
+
const fieldValue = getNestedProperty(element, this.config.field);
|
|
42
|
+
switch (this.config.operator) {
|
|
43
|
+
case "exists":
|
|
44
|
+
return fieldValue != null;
|
|
45
|
+
case "equals":
|
|
46
|
+
return fieldValue === this.config.value;
|
|
47
|
+
case "not_equals":
|
|
48
|
+
return fieldValue !== this.config.value;
|
|
49
|
+
case "contains":
|
|
50
|
+
return fieldValue ? String(fieldValue).includes(String(this.config.value)) : !1;
|
|
51
|
+
case "matches":
|
|
52
|
+
return fieldValue && this.config.pattern ? new RegExp(this.config.pattern).test(String(fieldValue)) : !1;
|
|
53
|
+
case "gt":
|
|
54
|
+
return fieldValue > this.config.value;
|
|
55
|
+
case "lt":
|
|
56
|
+
return fieldValue < this.config.value;
|
|
57
|
+
case "in":
|
|
58
|
+
return Array.isArray(this.config.value) ? this.config.value.includes(fieldValue) : !1;
|
|
59
|
+
default:
|
|
60
|
+
return !1;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
class ProjectionEngine {
|
|
65
|
+
model;
|
|
66
|
+
rules = [];
|
|
67
|
+
registry;
|
|
68
|
+
constructor(model, rulesPath) {
|
|
69
|
+
this.model = model, rulesPath && this.loadRulesSync(rulesPath);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Set reference registry for element registration
|
|
73
|
+
*/
|
|
74
|
+
setRegistry(registry) {
|
|
75
|
+
this.registry = registry;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Load projection rules from YAML file
|
|
79
|
+
*/
|
|
80
|
+
async loadRules(path) {
|
|
81
|
+
try {
|
|
82
|
+
const content = await readFile(path, "utf-8"), data = parseYAML(content);
|
|
83
|
+
if (!data.projections || !Array.isArray(data.projections))
|
|
84
|
+
throw new Error("Invalid projection rules file: missing projections array");
|
|
85
|
+
this.rules = data.projections.map((proj) => this.parseProjectionRule(proj));
|
|
86
|
+
} catch (error) {
|
|
87
|
+
throw new Error(`Failed to load projection rules from ${path}: ${error instanceof Error ? error.message : String(error)}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Synchronous version for constructor
|
|
92
|
+
*/
|
|
93
|
+
loadRulesSync(_path) {
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Add a projection rule programmatically
|
|
97
|
+
*/
|
|
98
|
+
addRule(rule) {
|
|
99
|
+
this.rules.push(rule);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Get all loaded rules
|
|
103
|
+
*/
|
|
104
|
+
getRules() {
|
|
105
|
+
return [...this.rules];
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Clear all rules
|
|
109
|
+
*/
|
|
110
|
+
clearRules() {
|
|
111
|
+
this.rules = [];
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Parse YAML projection definition into ProjectionRule
|
|
115
|
+
*/
|
|
116
|
+
parseProjectionRule(data) {
|
|
117
|
+
const [from_layer, from_type] = data.from.split("."), [to_layer, to_type] = data.to.split("."), ruleData = Array.isArray(data.rules) ? data.rules[0] : data.rules, conditions = (data.conditions || []).map((c) => ({
|
|
118
|
+
field: c.field,
|
|
119
|
+
operator: c.operator,
|
|
120
|
+
value: c.value,
|
|
121
|
+
pattern: c.pattern
|
|
122
|
+
})), property_mappings = [];
|
|
123
|
+
if (ruleData.properties) {
|
|
124
|
+
for (const [target, mapping] of Object.entries(ruleData.properties))
|
|
125
|
+
if (typeof mapping == "string")
|
|
126
|
+
property_mappings.push({ source: mapping, target });
|
|
127
|
+
else if (typeof mapping == "object") {
|
|
128
|
+
const m = mapping;
|
|
129
|
+
property_mappings.push({
|
|
130
|
+
source: m.source,
|
|
131
|
+
target,
|
|
132
|
+
default: m.default,
|
|
133
|
+
required: m.required ?? !1,
|
|
134
|
+
transform: m.transform
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return {
|
|
139
|
+
name: data.name,
|
|
140
|
+
from_layer,
|
|
141
|
+
from_type,
|
|
142
|
+
to_layer,
|
|
143
|
+
to_type,
|
|
144
|
+
name_template: ruleData.name_template,
|
|
145
|
+
property_mappings,
|
|
146
|
+
conditions,
|
|
147
|
+
template_file: ruleData.template,
|
|
148
|
+
create_bidirectional: ruleData.create_bidirectional ?? !0
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Find rules applicable to a source element
|
|
153
|
+
*/
|
|
154
|
+
findApplicableRules(source, targetLayer) {
|
|
155
|
+
return this.rules.filter((rule) => {
|
|
156
|
+
if (rule.from_layer !== source.layer || rule.from_type && rule.from_type !== source.type || targetLayer && rule.to_layer !== targetLayer)
|
|
157
|
+
return !1;
|
|
158
|
+
if (rule.conditions) {
|
|
159
|
+
for (const condition of rule.conditions)
|
|
160
|
+
if (!new ConditionEvaluator(condition).evaluate(source))
|
|
161
|
+
return !1;
|
|
162
|
+
}
|
|
163
|
+
return !0;
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
/**
|
|
167
|
+
* Project a single element to target layer
|
|
168
|
+
*/
|
|
169
|
+
async projectElement(source, targetLayer, rule, dryRun = !1) {
|
|
170
|
+
if (!rule) {
|
|
171
|
+
const applicableRules = this.findApplicableRules(source, targetLayer);
|
|
172
|
+
if (applicableRules.length === 0)
|
|
173
|
+
throw new Error(`No applicable projection rule found for element ${source.id} to layer ${targetLayer}`);
|
|
174
|
+
rule = applicableRules[0];
|
|
175
|
+
}
|
|
176
|
+
const projected = this.buildProjectedElement(source, rule);
|
|
177
|
+
if (!dryRun) {
|
|
178
|
+
const layer = await this.model.getLayer(rule.to_layer);
|
|
179
|
+
if (!layer)
|
|
180
|
+
throw new Error(`Target layer ${rule.to_layer} not found`);
|
|
181
|
+
layer.addElement(projected), this.registry && this.registry.registerElement(projected);
|
|
182
|
+
}
|
|
183
|
+
return projected;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Build projected element from source and rule
|
|
187
|
+
*/
|
|
188
|
+
buildProjectedElement(source, rule) {
|
|
189
|
+
const name = this.renderTemplate(rule.name_template, source), data = {
|
|
190
|
+
id: generateElementId(rule.to_layer, rule.to_type, name),
|
|
191
|
+
name,
|
|
192
|
+
type: rule.to_type,
|
|
193
|
+
layer: rule.to_layer
|
|
194
|
+
};
|
|
195
|
+
for (const mapping of rule.property_mappings) {
|
|
196
|
+
let value;
|
|
197
|
+
if (mapping.source.includes("{") || mapping.source.includes("{{") ? value = this.renderTemplate(mapping.source, source) : value = this.getSourceValue(source, mapping.source), value == null) {
|
|
198
|
+
if (mapping.required)
|
|
199
|
+
throw new Error(`Required property ${mapping.target} is missing from source ${source.id}`);
|
|
200
|
+
value = mapping.default;
|
|
201
|
+
}
|
|
202
|
+
mapping.transform && value !== null && value !== void 0 && (value = new PropertyTransformer(mapping.transform).apply(value)), value != null && setNestedProperty(data, mapping.target, value);
|
|
203
|
+
}
|
|
204
|
+
return rule.create_bidirectional !== !1 && (data.properties || (data.properties = {}), data.properties.realizes = source.id), new Element(data);
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Get value from source element (supports dot notation)
|
|
208
|
+
*/
|
|
209
|
+
getSourceValue(source, path) {
|
|
210
|
+
return path === "id" ? source.id : path === "name" ? source.name : path === "type" ? source.type : path === "layer" ? source.layer : path === "description" ? source.description : getNestedProperty(source, path);
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Render template string with source element data
|
|
214
|
+
*/
|
|
215
|
+
renderTemplate(template, source) {
|
|
216
|
+
const context = {
|
|
217
|
+
id: source.id,
|
|
218
|
+
name: source.name,
|
|
219
|
+
type: source.type,
|
|
220
|
+
layer: source.layer,
|
|
221
|
+
description: source.description,
|
|
222
|
+
properties: source.properties || {},
|
|
223
|
+
name_pascal: toPascalCase(source.name),
|
|
224
|
+
name_kebab: toKebabCase(source.name),
|
|
225
|
+
name_snake: toSnakeCase(source.name)
|
|
226
|
+
};
|
|
227
|
+
let result = template;
|
|
228
|
+
const regex = /\{source\.(\w+)\}/g;
|
|
229
|
+
return result = result.replace(regex, (match, prop) => context[prop] !== void 0 ? String(context[prop]) : match), result;
|
|
230
|
+
}
|
|
231
|
+
/**
|
|
232
|
+
* Project all elements that match rules
|
|
233
|
+
*/
|
|
234
|
+
async projectAll(fromLayer, toLayer, dryRun = !1) {
|
|
235
|
+
const projected = [], layers = fromLayer ? [await this.model.getLayer(fromLayer)] : Array.from(this.model.layers.values());
|
|
236
|
+
for (const layer of layers)
|
|
237
|
+
if (layer)
|
|
238
|
+
for (const element of layer.listElements()) {
|
|
239
|
+
const applicableRules = this.findApplicableRules(element, toLayer);
|
|
240
|
+
for (const rule of applicableRules)
|
|
241
|
+
try {
|
|
242
|
+
const projectedElement = await this.projectElement(element, rule.to_layer, rule, dryRun);
|
|
243
|
+
projected.push(projectedElement);
|
|
244
|
+
} catch (error) {
|
|
245
|
+
console.warn(`Warning: Failed to project ${element.id} with rule ${rule.name}: ${error instanceof Error ? error.message : String(error)}`);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return projected;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function getNestedProperty(obj, path) {
|
|
252
|
+
const parts = path.split(".");
|
|
253
|
+
let current = obj;
|
|
254
|
+
for (const part of parts)
|
|
255
|
+
if (current && typeof current == "object")
|
|
256
|
+
current = current[part];
|
|
257
|
+
else
|
|
258
|
+
return;
|
|
259
|
+
return current;
|
|
260
|
+
}
|
|
261
|
+
function setNestedProperty(obj, path, value) {
|
|
262
|
+
const parts = path.split(".");
|
|
263
|
+
let current = obj;
|
|
264
|
+
for (let i = 0; i < parts.length - 1; i++) {
|
|
265
|
+
const part = parts[i];
|
|
266
|
+
(!current[part] || typeof current[part] != "object") && (current[part] = {}), current = current[part];
|
|
267
|
+
}
|
|
268
|
+
current[parts[parts.length - 1]] = value;
|
|
269
|
+
}
|
|
270
|
+
function toKebabCase(text) {
|
|
271
|
+
return text.toLowerCase().replace(/\s+/g, "-").replace(/_/g, "-");
|
|
272
|
+
}
|
|
273
|
+
function toSnakeCase(text) {
|
|
274
|
+
return text.toLowerCase().replace(/\s+/g, "_").replace(/-/g, "_");
|
|
275
|
+
}
|
|
276
|
+
function toPascalCase(text) {
|
|
277
|
+
return text.replace(/-/g, " ").replace(/_/g, " ").split(/\s+/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join("");
|
|
278
|
+
}
|
|
279
|
+
function generateElementId(layer, type, name) {
|
|
280
|
+
const kebabName = toKebabCase(name);
|
|
281
|
+
return `${layer}.${type}.${kebabName}`;
|
|
282
|
+
}
|
|
283
|
+
export {
|
|
284
|
+
ConditionEvaluator,
|
|
285
|
+
ProjectionEngine,
|
|
286
|
+
PropertyTransformer
|
|
287
|
+
};
|
|
288
|
+
//# sourceMappingURL=projection-engine.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/core/projection-engine.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Projection Engine - Python CLI Compatible Implementation\n *\n * Automatically creates elements across layers based on projection rules\n * with property transformations, conditional logic, and template rendering.\n *\n * Spec: cli-validation/projection-engine-spec.md\n */\n\nimport { readFile } from 'fs/promises';\nimport { parse as parseYAML } from 'yaml';\nimport { Model } from './model.js';\nimport { Element } from './element.js';\nimport { ReferenceRegistry } from './reference-registry.js';\n\n/**\n * Transform types for property mapping\n */\nexport type TransformType =\n | 'uppercase'\n | 'lowercase'\n | 'kebab'\n | 'snake'\n | 'pascal'\n | 'prefix'\n | 'suffix'\n | 'template';\n\n/**\n * Condition operators for filtering\n */\nexport type ConditionOperator =\n | 'exists'\n | 'equals'\n | 'not_equals'\n | 'contains'\n | 'matches'\n | 'gt'\n | 'lt'\n | 'in';\n\n/**\n * Property transformation configuration\n */\nexport interface PropertyTransform {\n type: TransformType;\n value?: string; // For prefix/suffix/template\n}\n\n/**\n * Condition for filtering source elements\n */\nexport interface ProjectionCondition {\n field: string;\n operator: ConditionOperator;\n value?: any;\n pattern?: string; // For regex matching\n}\n\n/**\n * Property mapping from source to target\n */\nexport interface PropertyMapping {\n source: string;\n target: string;\n default?: any;\n required?: boolean;\n transform?: PropertyTransform;\n}\n\n/**\n * Projection rule definition\n */\nexport interface ProjectionRule {\n name: string;\n from_layer: string;\n from_type: string;\n to_layer: string;\n to_type: string;\n name_template: string;\n property_mappings: PropertyMapping[];\n conditions?: ProjectionCondition[];\n template_file?: string;\n create_bidirectional?: boolean;\n}\n\n/**\n * Helper class for property transformations\n */\nexport class PropertyTransformer {\n constructor(private config: PropertyTransform) {}\n\n apply(value: any): any {\n if (value === null || value === undefined) return null;\n\n const strValue = String(value);\n\n switch (this.config.type) {\n case 'uppercase':\n return strValue.toUpperCase();\n\n case 'lowercase':\n return strValue.toLowerCase();\n\n case 'kebab':\n return toKebabCase(strValue);\n\n case 'snake':\n return toSnakeCase(strValue);\n\n case 'pascal':\n return toPascalCase(strValue);\n\n case 'prefix':\n return `${this.config.value}${strValue}`;\n\n case 'suffix':\n return `${strValue}${this.config.value}`;\n\n case 'template':\n return this.config.value?.replace('{value}', strValue) ?? strValue;\n\n default:\n return value;\n }\n }\n}\n\n/**\n * Helper class for condition evaluation\n */\nexport class ConditionEvaluator {\n constructor(private config: ProjectionCondition) {}\n\n evaluate(element: any): boolean {\n const fieldValue = getNestedProperty(element, this.config.field);\n\n switch (this.config.operator) {\n case 'exists':\n return fieldValue !== null && fieldValue !== undefined;\n\n case 'equals':\n return fieldValue === this.config.value;\n\n case 'not_equals':\n return fieldValue !== this.config.value;\n\n case 'contains':\n return fieldValue\n ? String(fieldValue).includes(String(this.config.value))\n : false;\n\n case 'matches':\n return fieldValue && this.config.pattern\n ? new RegExp(this.config.pattern).test(String(fieldValue))\n : false;\n\n case 'gt':\n return fieldValue > this.config.value;\n\n case 'lt':\n return fieldValue < this.config.value;\n\n case 'in':\n return Array.isArray(this.config.value)\n ? this.config.value.includes(fieldValue)\n : false;\n\n default:\n return false;\n }\n }\n}\n\n/**\n * Projection Engine - Creates elements across layers based on rules\n */\nexport class ProjectionEngine {\n private rules: ProjectionRule[] = [];\n private registry?: ReferenceRegistry;\n\n constructor(private model: Model, rulesPath?: string) {\n if (rulesPath) {\n this.loadRulesSync(rulesPath);\n }\n }\n\n /**\n * Set reference registry for element registration\n */\n setRegistry(registry: ReferenceRegistry): void {\n this.registry = registry;\n }\n\n /**\n * Load projection rules from YAML file\n */\n async loadRules(path: string): Promise<void> {\n try {\n const content = await readFile(path, 'utf-8');\n const data = parseYAML(content);\n\n if (!data.projections || !Array.isArray(data.projections)) {\n throw new Error('Invalid projection rules file: missing projections array');\n }\n\n this.rules = data.projections.map((proj: any) =>\n this.parseProjectionRule(proj)\n );\n } catch (error) {\n throw new Error(\n `Failed to load projection rules from ${path}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n\n /**\n * Synchronous version for constructor\n */\n private loadRulesSync(_path: string): void {\n // For now, rules must be loaded async via loadRules()\n // This is a placeholder for backward compatibility\n }\n\n /**\n * Add a projection rule programmatically\n */\n addRule(rule: ProjectionRule): void {\n this.rules.push(rule);\n }\n\n /**\n * Get all loaded rules\n */\n getRules(): ProjectionRule[] {\n return [...this.rules];\n }\n\n /**\n * Clear all rules\n */\n clearRules(): void {\n this.rules = [];\n }\n\n /**\n * Parse YAML projection definition into ProjectionRule\n */\n private parseProjectionRule(data: any): ProjectionRule {\n // Split from/to into layer.type\n const [from_layer, from_type] = data.from.split('.');\n const [to_layer, to_type] = data.to.split('.');\n\n // Get first rule definition (simplified - Python CLI could support multiple)\n const ruleData = Array.isArray(data.rules) ? data.rules[0] : data.rules;\n\n // Parse conditions\n const conditions: ProjectionCondition[] = (data.conditions || []).map(\n (c: any) => ({\n field: c.field,\n operator: c.operator,\n value: c.value,\n pattern: c.pattern,\n })\n );\n\n // Parse property mappings\n const property_mappings: PropertyMapping[] = [];\n if (ruleData.properties) {\n for (const [target, mapping] of Object.entries(ruleData.properties)) {\n if (typeof mapping === 'string') {\n // Simple format: { target: source }\n property_mappings.push({ source: mapping as string, target });\n } else if (typeof mapping === 'object') {\n // Advanced format with transform\n const m = mapping as any;\n property_mappings.push({\n source: m.source,\n target,\n default: m.default,\n required: m.required ?? false,\n transform: m.transform,\n });\n }\n }\n }\n\n return {\n name: data.name,\n from_layer,\n from_type,\n to_layer,\n to_type,\n name_template: ruleData.name_template,\n property_mappings,\n conditions,\n template_file: ruleData.template,\n create_bidirectional: ruleData.create_bidirectional ?? true,\n };\n }\n\n /**\n * Find rules applicable to a source element\n */\n findApplicableRules(\n source: Element,\n targetLayer?: string\n ): ProjectionRule[] {\n return this.rules.filter((rule) => {\n // Check layer match\n if (rule.from_layer !== source.layer) return false;\n\n // Check type match (if specified)\n if (rule.from_type && rule.from_type !== source.type) return false;\n\n // Check target layer filter (if specified)\n if (targetLayer && rule.to_layer !== targetLayer) return false;\n\n // Evaluate all conditions (ALL must be true)\n if (rule.conditions) {\n for (const condition of rule.conditions) {\n const evaluator = new ConditionEvaluator(condition);\n if (!evaluator.evaluate(source)) {\n return false;\n }\n }\n }\n\n return true;\n });\n }\n\n /**\n * Project a single element to target layer\n */\n async projectElement(\n source: Element,\n targetLayer: string,\n rule?: ProjectionRule,\n dryRun: boolean = false\n ): Promise<Element> {\n // Find rule if not provided\n if (!rule) {\n const applicableRules = this.findApplicableRules(source, targetLayer);\n if (applicableRules.length === 0) {\n throw new Error(\n `No applicable projection rule found for element ${source.id} to layer ${targetLayer}`\n );\n }\n rule = applicableRules[0];\n }\n\n // Build projected element\n const projected = this.buildProjectedElement(source, rule);\n\n // If not dry run, add to model\n if (!dryRun) {\n const layer = await this.model.getLayer(rule.to_layer);\n if (!layer) {\n throw new Error(`Target layer ${rule.to_layer} not found`);\n }\n\n // Add element to layer\n layer.addElement(projected);\n\n // Register with reference registry if available\n if (this.registry) {\n this.registry.registerElement(projected);\n }\n }\n\n return projected;\n }\n\n /**\n * Build projected element from source and rule\n */\n private buildProjectedElement(\n source: Element,\n rule: ProjectionRule\n ): Element {\n // Render element name\n const name = this.renderTemplate(rule.name_template, source);\n\n // Generate element ID\n const id = generateElementId(rule.to_layer, rule.to_type, name);\n\n // Initialize element data\n const data: any = {\n id,\n name,\n type: rule.to_type,\n layer: rule.to_layer,\n };\n\n // Map properties\n for (const mapping of rule.property_mappings) {\n let value: any;\n\n // Get source value\n if (mapping.source.includes('{') || mapping.source.includes('{{')) {\n // Template string\n value = this.renderTemplate(mapping.source, source);\n } else {\n // Direct property or nested property\n value = this.getSourceValue(source, mapping.source);\n }\n\n // Use default if null\n if (value === null || value === undefined) {\n if (mapping.required) {\n throw new Error(\n `Required property ${mapping.target} is missing from source ${source.id}`\n );\n }\n value = mapping.default;\n }\n\n // Apply transformation if specified\n if (mapping.transform && value !== null && value !== undefined) {\n const transformer = new PropertyTransformer(mapping.transform);\n value = transformer.apply(value);\n }\n\n // Set target property\n if (value !== null && value !== undefined) {\n setNestedProperty(data, mapping.target, value);\n }\n }\n\n // Add bidirectional reference if enabled\n if (rule.create_bidirectional !== false) {\n if (!data.properties) data.properties = {};\n data.properties.realizes = source.id;\n }\n\n // Create Element instance\n return new Element(data);\n }\n\n /**\n * Get value from source element (supports dot notation)\n */\n private getSourceValue(source: Element, path: string): any {\n // Check for direct element properties\n if (path === 'id') return source.id;\n if (path === 'name') return source.name;\n if (path === 'type') return source.type;\n if (path === 'layer') return source.layer;\n if (path === 'description') return source.description;\n\n // Check nested properties\n return getNestedProperty(source, path);\n }\n\n /**\n * Render template string with source element data\n */\n private renderTemplate(template: string, source: Element): string {\n // Build context\n const context: any = {\n id: source.id,\n name: source.name,\n type: source.type,\n layer: source.layer,\n description: source.description,\n properties: (source as any).properties || {},\n name_pascal: toPascalCase(source.name),\n name_kebab: toKebabCase(source.name),\n name_snake: toSnakeCase(source.name),\n };\n\n // Simple template replacement for {source.xxx}\n let result = template;\n const regex = /\\{source\\.(\\w+)\\}/g;\n result = result.replace(regex, (match, prop) => {\n return context[prop] !== undefined ? String(context[prop]) : match;\n });\n\n return result;\n }\n\n /**\n * Project all elements that match rules\n */\n async projectAll(\n fromLayer?: string,\n toLayer?: string,\n dryRun: boolean = false\n ): Promise<Element[]> {\n const projected: Element[] = [];\n\n // Get source elements\n const layers = fromLayer\n ? [await this.model.getLayer(fromLayer)]\n : Array.from(this.model.layers.values());\n\n for (const layer of layers) {\n if (!layer) continue;\n\n for (const element of layer.listElements()) {\n // Find applicable rules\n const applicableRules = this.findApplicableRules(\n element,\n toLayer\n );\n\n // Project with each applicable rule\n for (const rule of applicableRules) {\n try {\n const projectedElement = await this.projectElement(\n element,\n rule.to_layer,\n rule,\n dryRun\n );\n projected.push(projectedElement);\n } catch (error) {\n // Log warning and continue\n console.warn(\n `Warning: Failed to project ${element.id} with rule ${rule.name}: ${error instanceof Error ? error.message : String(error)}`\n );\n }\n }\n }\n }\n\n return projected;\n }\n}\n\n/**\n * Helper: Get nested property from object\n */\nfunction getNestedProperty(obj: any, path: string): any {\n const parts = path.split('.');\n let current = obj;\n\n for (const part of parts) {\n if (current && typeof current === 'object') {\n current = current[part];\n } else {\n return undefined;\n }\n }\n\n return current;\n}\n\n/**\n * Helper: Set nested property on object\n */\nfunction setNestedProperty(obj: any, path: string, value: any): void {\n const parts = path.split('.');\n let current = obj;\n\n for (let i = 0; i < parts.length - 1; i++) {\n const part = parts[i];\n if (!current[part] || typeof current[part] !== 'object') {\n current[part] = {};\n }\n current = current[part];\n }\n\n current[parts[parts.length - 1]] = value;\n}\n\n/**\n * Helper: Convert to kebab-case\n */\nfunction toKebabCase(text: string): string {\n return text\n .toLowerCase()\n .replace(/\\s+/g, '-')\n .replace(/_/g, '-');\n}\n\n/**\n * Helper: Convert to snake_case\n */\nfunction toSnakeCase(text: string): string {\n return text\n .toLowerCase()\n .replace(/\\s+/g, '_')\n .replace(/-/g, '_');\n}\n\n/**\n * Helper: Convert to PascalCase\n */\nfunction toPascalCase(text: string): string {\n const words = text\n .replace(/-/g, ' ')\n .replace(/_/g, ' ')\n .split(/\\s+/);\n return words\n .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())\n .join('');\n}\n\n/**\n * Helper: Generate element ID\n */\nfunction generateElementId(layer: string, type: string, name: string): string {\n const kebabName = toKebabCase(name);\n return `${layer}.${type}.${kebabName}`;\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import Graph from "graphology";
|
|
2
|
+
class ReferenceRegistry {
|
|
3
|
+
references;
|
|
4
|
+
targetIndex;
|
|
5
|
+
typeIndex;
|
|
6
|
+
// Known reference property names from Python CLI
|
|
7
|
+
static KNOWN_REF_PROPERTIES = /* @__PURE__ */ new Set([
|
|
8
|
+
"realizes",
|
|
9
|
+
"realizedBy",
|
|
10
|
+
"serves",
|
|
11
|
+
"servedBy",
|
|
12
|
+
"accesses",
|
|
13
|
+
"accessedBy",
|
|
14
|
+
"uses",
|
|
15
|
+
"usedBy",
|
|
16
|
+
"composedOf",
|
|
17
|
+
"partOf",
|
|
18
|
+
"flows",
|
|
19
|
+
"triggers",
|
|
20
|
+
"archimateRef",
|
|
21
|
+
"businessActorRef",
|
|
22
|
+
"stakeholderRef",
|
|
23
|
+
"motivationGoalRef",
|
|
24
|
+
"dataObjectRef",
|
|
25
|
+
"apiOperationRef",
|
|
26
|
+
"applicationServiceRef",
|
|
27
|
+
"schemaRef"
|
|
28
|
+
]);
|
|
29
|
+
constructor() {
|
|
30
|
+
this.references = /* @__PURE__ */ new Map(), this.targetIndex = /* @__PURE__ */ new Map(), this.typeIndex = /* @__PURE__ */ new Map();
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Add a reference to the registry
|
|
34
|
+
*/
|
|
35
|
+
addReference(reference) {
|
|
36
|
+
const sourceKey = reference.source;
|
|
37
|
+
this.references.has(sourceKey) || this.references.set(sourceKey, []), this.references.get(sourceKey).push(reference);
|
|
38
|
+
const targetKey = reference.target;
|
|
39
|
+
this.targetIndex.has(targetKey) || this.targetIndex.set(targetKey, []), this.targetIndex.get(targetKey).push(reference);
|
|
40
|
+
const typeKey = reference.type;
|
|
41
|
+
this.typeIndex.has(typeKey) || this.typeIndex.set(typeKey, []), this.typeIndex.get(typeKey).push(reference);
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Get all references from a source element
|
|
45
|
+
*/
|
|
46
|
+
getReferencesFrom(sourceId) {
|
|
47
|
+
return this.references.get(sourceId) ?? [];
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Get all references to a target element
|
|
51
|
+
*/
|
|
52
|
+
getReferencesTo(targetId) {
|
|
53
|
+
return this.targetIndex.get(targetId) ?? [];
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Get all references of a specific type
|
|
57
|
+
*/
|
|
58
|
+
getReferencesByType(type) {
|
|
59
|
+
return this.typeIndex.get(type) ?? [];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Check if a reference exists
|
|
63
|
+
*/
|
|
64
|
+
hasReference(sourceId, targetId) {
|
|
65
|
+
return (this.references.get(sourceId) ?? []).some((ref) => ref.target === targetId);
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Get all references
|
|
69
|
+
*/
|
|
70
|
+
getAllReferences() {
|
|
71
|
+
const allRefs = [];
|
|
72
|
+
for (const refs of this.references.values())
|
|
73
|
+
allRefs.push(...refs);
|
|
74
|
+
return allRefs;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Clear all references
|
|
78
|
+
*/
|
|
79
|
+
clear() {
|
|
80
|
+
this.references.clear(), this.targetIndex.clear(), this.typeIndex.clear();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Register an element and all its references
|
|
84
|
+
* Scans element properties for known reference names
|
|
85
|
+
*/
|
|
86
|
+
registerElement(element) {
|
|
87
|
+
if (element.references)
|
|
88
|
+
for (const ref of element.references)
|
|
89
|
+
this.addReference(ref);
|
|
90
|
+
if (element.properties) {
|
|
91
|
+
for (const [propName, propValue] of Object.entries(element.properties))
|
|
92
|
+
if (ReferenceRegistry.KNOWN_REF_PROPERTIES.has(propName)) {
|
|
93
|
+
if (typeof propValue == "string")
|
|
94
|
+
this.addReference({
|
|
95
|
+
source: element.id,
|
|
96
|
+
target: propValue,
|
|
97
|
+
type: propName
|
|
98
|
+
});
|
|
99
|
+
else if (Array.isArray(propValue))
|
|
100
|
+
for (const target of propValue)
|
|
101
|
+
typeof target == "string" && this.addReference({
|
|
102
|
+
source: element.id,
|
|
103
|
+
target,
|
|
104
|
+
type: propName
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
const nestedRefs = this.scanNestedReferences(element.id, element.properties);
|
|
108
|
+
for (const ref of nestedRefs)
|
|
109
|
+
this.addReference(ref);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Recursively scan nested structures for reference properties
|
|
114
|
+
* Matches Python CLI behavior: looks for properties ending in "Ref" or "Reference"
|
|
115
|
+
*/
|
|
116
|
+
scanNestedReferences(sourceId, data, path = "") {
|
|
117
|
+
const references = [];
|
|
118
|
+
if (!data || typeof data != "object")
|
|
119
|
+
return references;
|
|
120
|
+
if (Array.isArray(data)) {
|
|
121
|
+
for (let i = 0; i < data.length; i++) {
|
|
122
|
+
const itemPath = path ? `${path}[${i}]` : `[${i}]`;
|
|
123
|
+
references.push(...this.scanNestedReferences(sourceId, data[i], itemPath));
|
|
124
|
+
}
|
|
125
|
+
return references;
|
|
126
|
+
}
|
|
127
|
+
for (const [key, value] of Object.entries(data)) {
|
|
128
|
+
const currentPath = path ? `${path}.${key}` : key;
|
|
129
|
+
if (!(!path && ReferenceRegistry.KNOWN_REF_PROPERTIES.has(key)))
|
|
130
|
+
if (key.endsWith("Ref") || key.endsWith("Reference")) {
|
|
131
|
+
if (typeof value == "string")
|
|
132
|
+
references.push({
|
|
133
|
+
source: sourceId,
|
|
134
|
+
target: value,
|
|
135
|
+
type: key,
|
|
136
|
+
description: `Nested reference at ${currentPath}`
|
|
137
|
+
});
|
|
138
|
+
else if (Array.isArray(value))
|
|
139
|
+
for (const target of value)
|
|
140
|
+
typeof target == "string" && references.push({
|
|
141
|
+
source: sourceId,
|
|
142
|
+
target,
|
|
143
|
+
type: key,
|
|
144
|
+
description: `Nested reference at ${currentPath}`
|
|
145
|
+
});
|
|
146
|
+
} else value && typeof value == "object" && !Array.isArray(value) ? references.push(...this.scanNestedReferences(sourceId, value, currentPath)) : Array.isArray(value) && references.push(...this.scanNestedReferences(sourceId, value, currentPath));
|
|
147
|
+
}
|
|
148
|
+
return references;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Find broken references (references to non-existent elements)
|
|
152
|
+
*/
|
|
153
|
+
findBrokenReferences(validIds) {
|
|
154
|
+
return this.getAllReferences().filter((ref) => !validIds.has(ref.target));
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get dependency graph as a directed graph using graphology
|
|
158
|
+
* Nodes are element IDs, edges represent references
|
|
159
|
+
*/
|
|
160
|
+
getDependencyGraph() {
|
|
161
|
+
const graph = new Graph({ type: "directed" }), nodes = /* @__PURE__ */ new Set();
|
|
162
|
+
for (const ref of this.getAllReferences())
|
|
163
|
+
nodes.add(ref.source), nodes.add(ref.target);
|
|
164
|
+
for (const node of nodes)
|
|
165
|
+
graph.addNode(node);
|
|
166
|
+
for (const ref of this.getAllReferences())
|
|
167
|
+
graph.addEdge(ref.source, ref.target, {
|
|
168
|
+
type: ref.type,
|
|
169
|
+
description: ref.description
|
|
170
|
+
});
|
|
171
|
+
return graph;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get statistics about registered references
|
|
175
|
+
*/
|
|
176
|
+
getStats() {
|
|
177
|
+
return {
|
|
178
|
+
totalReferences: this.getAllReferences().length,
|
|
179
|
+
uniqueSources: this.references.size,
|
|
180
|
+
uniqueTargets: this.targetIndex.size,
|
|
181
|
+
referenceTypes: Array.from(this.typeIndex.keys())
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
export {
|
|
186
|
+
ReferenceRegistry
|
|
187
|
+
};
|
|
188
|
+
//# sourceMappingURL=reference-registry.js.map
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 3,
|
|
3
|
+
"sources": ["../../src/core/reference-registry.ts"],
|
|
4
|
+
"sourcesContent": ["import Graph from 'graphology';\nimport type { Reference, Element } from \"../types/index.js\";\n\n/**\n * Reference registry - tracks and validates cross-layer references\n */\nexport class ReferenceRegistry {\n private references: Map<string, Reference[]>;\n private targetIndex: Map<string, Reference[]>;\n private typeIndex: Map<string, Reference[]>;\n\n // Known reference property names from Python CLI\n private static readonly KNOWN_REF_PROPERTIES = new Set([\n 'realizes', 'realizedBy',\n 'serves', 'servedBy',\n 'accesses', 'accessedBy',\n 'uses', 'usedBy',\n 'composedOf', 'partOf',\n 'flows', 'triggers',\n 'archimateRef', 'businessActorRef', 'stakeholderRef',\n 'motivationGoalRef', 'dataObjectRef', 'apiOperationRef',\n 'applicationServiceRef', 'schemaRef'\n ]);\n\n constructor() {\n this.references = new Map();\n this.targetIndex = new Map();\n this.typeIndex = new Map();\n }\n\n /**\n * Add a reference to the registry\n */\n addReference(reference: Reference): void {\n // Add to source index\n const sourceKey = reference.source;\n if (!this.references.has(sourceKey)) {\n this.references.set(sourceKey, []);\n }\n this.references.get(sourceKey)!.push(reference);\n\n // Add to target index\n const targetKey = reference.target;\n if (!this.targetIndex.has(targetKey)) {\n this.targetIndex.set(targetKey, []);\n }\n this.targetIndex.get(targetKey)!.push(reference);\n\n // Add to type index\n const typeKey = reference.type;\n if (!this.typeIndex.has(typeKey)) {\n this.typeIndex.set(typeKey, []);\n }\n this.typeIndex.get(typeKey)!.push(reference);\n }\n\n /**\n * Get all references from a source element\n */\n getReferencesFrom(sourceId: string): Reference[] {\n return this.references.get(sourceId) ?? [];\n }\n\n /**\n * Get all references to a target element\n */\n getReferencesTo(targetId: string): Reference[] {\n return this.targetIndex.get(targetId) ?? [];\n }\n\n /**\n * Get all references of a specific type\n */\n getReferencesByType(type: string): Reference[] {\n return this.typeIndex.get(type) ?? [];\n }\n\n /**\n * Check if a reference exists\n */\n hasReference(sourceId: string, targetId: string): boolean {\n const refs = this.references.get(sourceId) ?? [];\n return refs.some(ref => ref.target === targetId);\n }\n\n /**\n * Get all references\n */\n getAllReferences(): Reference[] {\n const allRefs: Reference[] = [];\n for (const refs of this.references.values()) {\n allRefs.push(...refs);\n }\n return allRefs;\n }\n\n /**\n * Clear all references\n */\n clear(): void {\n this.references.clear();\n this.targetIndex.clear();\n this.typeIndex.clear();\n }\n\n /**\n * Register an element and all its references\n * Scans element properties for known reference names\n */\n registerElement(element: Element): void {\n // Extract references from explicit references array\n if (element.references) {\n for (const ref of element.references) {\n this.addReference(ref);\n }\n }\n\n // Scan properties for known reference properties\n if (element.properties) {\n for (const [propName, propValue] of Object.entries(element.properties)) {\n if (ReferenceRegistry.KNOWN_REF_PROPERTIES.has(propName)) {\n // Handle string value\n if (typeof propValue === 'string') {\n this.addReference({\n source: element.id,\n target: propValue,\n type: propName\n });\n }\n // Handle array of strings\n else if (Array.isArray(propValue)) {\n for (const target of propValue) {\n if (typeof target === 'string') {\n this.addReference({\n source: element.id,\n target: target,\n type: propName\n });\n }\n }\n }\n }\n }\n\n // Scan nested properties for *Ref/*Reference properties\n const nestedRefs = this.scanNestedReferences(element.id, element.properties);\n for (const ref of nestedRefs) {\n this.addReference(ref);\n }\n }\n }\n\n /**\n * Recursively scan nested structures for reference properties\n * Matches Python CLI behavior: looks for properties ending in \"Ref\" or \"Reference\"\n */\n private scanNestedReferences(\n sourceId: string,\n data: any,\n path: string = ''\n ): Reference[] {\n const references: Reference[] = [];\n\n if (!data || typeof data !== 'object') {\n return references;\n }\n\n // Handle arrays\n if (Array.isArray(data)) {\n for (let i = 0; i < data.length; i++) {\n const itemPath = path ? `${path}[${i}]` : `[${i}]`;\n references.push(...this.scanNestedReferences(sourceId, data[i], itemPath));\n }\n return references;\n }\n\n // Scan object properties\n for (const [key, value] of Object.entries(data)) {\n const currentPath = path ? `${path}.${key}` : key;\n\n // Skip if this is a top-level known reference property (already handled)\n if (!path && ReferenceRegistry.KNOWN_REF_PROPERTIES.has(key)) {\n continue;\n }\n\n // Check if property name ends with \"Ref\" or \"Reference\"\n if (key.endsWith('Ref') || key.endsWith('Reference')) {\n // Handle string value\n if (typeof value === 'string') {\n references.push({\n source: sourceId,\n target: value,\n type: key,\n description: `Nested reference at ${currentPath}`,\n });\n }\n // Handle array of strings\n else if (Array.isArray(value)) {\n for (const target of value) {\n if (typeof target === 'string') {\n references.push({\n source: sourceId,\n target: target,\n type: key,\n description: `Nested reference at ${currentPath}`,\n });\n }\n }\n }\n }\n // Recurse into nested objects\n else if (value && typeof value === 'object' && !Array.isArray(value)) {\n references.push(...this.scanNestedReferences(sourceId, value, currentPath));\n }\n // Recurse into arrays\n else if (Array.isArray(value)) {\n references.push(...this.scanNestedReferences(sourceId, value, currentPath));\n }\n }\n\n return references;\n }\n\n /**\n * Find broken references (references to non-existent elements)\n */\n findBrokenReferences(validIds: Set<string>): Reference[] {\n return this.getAllReferences().filter(ref => !validIds.has(ref.target));\n }\n\n /**\n * Get dependency graph as a directed graph using graphology\n * Nodes are element IDs, edges represent references\n */\n getDependencyGraph(): Graph {\n const graph = new Graph({ type: 'directed' });\n\n // Add all unique nodes\n const nodes = new Set<string>();\n for (const ref of this.getAllReferences()) {\n nodes.add(ref.source);\n nodes.add(ref.target);\n }\n\n for (const node of nodes) {\n graph.addNode(node);\n }\n\n // Add edges for all references\n for (const ref of this.getAllReferences()) {\n // Use reference type as edge data\n graph.addEdge(ref.source, ref.target, {\n type: ref.type,\n description: ref.description,\n });\n }\n\n return graph;\n }\n\n /**\n * Get statistics about registered references\n */\n getStats(): {\n totalReferences: number;\n uniqueSources: number;\n uniqueTargets: number;\n referenceTypes: string[];\n } {\n return {\n totalReferences: this.getAllReferences().length,\n uniqueSources: this.references.size,\n uniqueTargets: this.targetIndex.size,\n referenceTypes: Array.from(this.typeIndex.keys()),\n };\n }\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": []
|
|
7
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
class RelationshipRegistry {
|
|
2
|
+
relationships;
|
|
3
|
+
relationshipTypes;
|
|
4
|
+
predicateMap;
|
|
5
|
+
layerIndex;
|
|
6
|
+
constructor() {
|
|
7
|
+
this.relationships = /* @__PURE__ */ new Map(), this.relationshipTypes = /* @__PURE__ */ new Map(), this.predicateMap = /* @__PURE__ */ new Map(), this.layerIndex = /* @__PURE__ */ new Map();
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Register a relationship type
|
|
11
|
+
*/
|
|
12
|
+
registerType(metadata) {
|
|
13
|
+
this.relationshipTypes.set(metadata.id, metadata), this.predicateMap.set(metadata.predicate, metadata);
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Get relationship type by ID
|
|
17
|
+
*/
|
|
18
|
+
getType(id) {
|
|
19
|
+
return this.relationshipTypes.get(id);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Get relationship type by predicate
|
|
23
|
+
*/
|
|
24
|
+
getTypeByPredicate(predicate) {
|
|
25
|
+
return this.predicateMap.get(predicate);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Add a relationship to the registry
|
|
29
|
+
*/
|
|
30
|
+
addRelationship(relationship) {
|
|
31
|
+
const layerKey = relationship.source.split("-")[0];
|
|
32
|
+
this.relationships.has(relationship.source) || this.relationships.set(relationship.source, []), this.relationships.get(relationship.source).push(relationship), this.layerIndex.has(layerKey) || this.layerIndex.set(layerKey, []), this.layerIndex.get(layerKey).push(relationship);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Get all relationships from a source element
|
|
36
|
+
*/
|
|
37
|
+
getRelationshipsFrom(sourceId) {
|
|
38
|
+
return this.relationships.get(sourceId) ?? [];
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get all relationships in a layer
|
|
42
|
+
*/
|
|
43
|
+
getRelationshipsByLayer(layerPrefix) {
|
|
44
|
+
return this.layerIndex.get(layerPrefix) ?? [];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Get all relationships with a specific predicate
|
|
48
|
+
*/
|
|
49
|
+
getRelationshipsByPredicate(predicate) {
|
|
50
|
+
const allRels = [];
|
|
51
|
+
for (const rels of this.relationships.values())
|
|
52
|
+
allRels.push(...rels.filter((rel) => rel.predicate === predicate));
|
|
53
|
+
return allRels;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Check if a relationship exists
|
|
57
|
+
*/
|
|
58
|
+
hasRelationship(sourceId, targetId, predicate) {
|
|
59
|
+
return (this.relationships.get(sourceId) ?? []).some((rel) => rel.target === targetId && rel.predicate === predicate);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Get all relationships
|
|
63
|
+
*/
|
|
64
|
+
getAllRelationships() {
|
|
65
|
+
const allRels = [];
|
|
66
|
+
for (const rels of this.relationships.values())
|
|
67
|
+
allRels.push(...rels);
|
|
68
|
+
return allRels;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Get all relationship types
|
|
72
|
+
*/
|
|
73
|
+
getAllTypes() {
|
|
74
|
+
return Array.from(this.relationshipTypes.values());
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Check if a predicate is valid
|
|
78
|
+
*/
|
|
79
|
+
isValidPredicate(predicate) {
|
|
80
|
+
return this.predicateMap.has(predicate);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get valid predicates for a layer
|
|
84
|
+
*/
|
|
85
|
+
getValidPredicatesForLayer(layer) {
|
|
86
|
+
const layerPrefix = layer.split("-")[0];
|
|
87
|
+
return this.getAllTypes().filter((type) => !type.applicable_layers || type.applicable_layers.includes(layerPrefix)).map((type) => type.predicate);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Clear all relationships and types
|
|
91
|
+
*/
|
|
92
|
+
clear() {
|
|
93
|
+
this.relationships.clear(), this.relationshipTypes.clear(), this.predicateMap.clear(), this.layerIndex.clear();
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Get statistics about registered relationships
|
|
97
|
+
*/
|
|
98
|
+
getStats() {
|
|
99
|
+
const predicates = /* @__PURE__ */ new Set();
|
|
100
|
+
for (const rels of this.relationships.values())
|
|
101
|
+
for (const rel of rels)
|
|
102
|
+
predicates.add(rel.predicate);
|
|
103
|
+
return {
|
|
104
|
+
totalRelationships: this.getAllRelationships().length,
|
|
105
|
+
uniquePredicates: predicates.size,
|
|
106
|
+
uniqueLayers: this.layerIndex.size,
|
|
107
|
+
totalTypes: this.relationshipTypes.size
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
export {
|
|
112
|
+
RelationshipRegistry
|
|
113
|
+
};
|
|
114
|
+
//# sourceMappingURL=relationship-registry.js.map
|