@typemf/xmi 0.5.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/dist/index.cjs ADDED
@@ -0,0 +1,707 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ NamespaceCollector: () => NamespaceCollector,
34
+ XmiResourceFactory: () => XmiResourceFactory,
35
+ XmiSerializer: () => XmiSerializer,
36
+ allAttributesOf: () => allAttributesOf,
37
+ allStructuralFeaturesOf: () => allStructuralFeaturesOf,
38
+ computeEmfFragment: () => computeEmfFragment,
39
+ escapeAttributeValue: () => escapeAttributeValue,
40
+ escapeText: () => escapeText,
41
+ parseXmlDocument: () => parseXmlDocument,
42
+ registerXmiFormat: () => registerXmiFormat,
43
+ resolveEmfFragment: () => resolveEmfFragment
44
+ });
45
+ module.exports = __toCommonJS(index_exports);
46
+
47
+ // src/emf-fragment.ts
48
+ function allStructuralFeaturesOf(eClass) {
49
+ const seen = /* @__PURE__ */ new Set();
50
+ const result = [];
51
+ function visit(c) {
52
+ if (seen.has(c)) return;
53
+ seen.add(c);
54
+ for (const parent of c.getESuperTypes()) visit(parent);
55
+ result.push(...c.getEStructuralFeatures());
56
+ }
57
+ visit(eClass);
58
+ return result;
59
+ }
60
+ function allAttributesOf(eClass) {
61
+ return allStructuralFeaturesOf(eClass).filter((f) => !("isContainment" in f));
62
+ }
63
+ function computeEmfFragment(target, roots) {
64
+ const idAttribute = allAttributesOf(target.eClass()).find((attr) => attr.isID());
65
+ if (idAttribute && target.eIsSet(idAttribute)) {
66
+ return String(target.eGet(idAttribute));
67
+ }
68
+ const segments = [];
69
+ let current = target;
70
+ let container = current.eContainer();
71
+ while (container) {
72
+ const feature = current.eContainingFeature();
73
+ if (!feature) {
74
+ throw new Error("Object has a container but no containing feature - inconsistent containment state.");
75
+ }
76
+ let segment = `@${feature.getName()}`;
77
+ if (feature.isMany()) {
78
+ const siblings = [...container.eGet(feature)];
79
+ const index = siblings.indexOf(current);
80
+ if (index === -1) {
81
+ throw new Error(`Object not found within its own containing feature '${feature.getName()}'.`);
82
+ }
83
+ segment += `.${index}`;
84
+ }
85
+ segments.unshift(segment);
86
+ current = container;
87
+ container = current.eContainer();
88
+ }
89
+ const rootIndex = roots.indexOf(current);
90
+ if (rootIndex === -1) {
91
+ throw new Error("Target object is not reachable from the given roots via containment.");
92
+ }
93
+ const path = segments.join("/");
94
+ if (roots.length === 1) {
95
+ return path === "" ? "/" : `//${path}`;
96
+ }
97
+ return path === "" ? `/${rootIndex}` : `/${rootIndex}/${path}`;
98
+ }
99
+ function resolveEmfFragment(fragment, roots) {
100
+ if (fragment === "/") {
101
+ return roots.length === 1 ? roots[0] : void 0;
102
+ }
103
+ if (fragment.startsWith("//")) {
104
+ const rest = fragment.slice(2);
105
+ if (roots.length !== 1) return void 0;
106
+ if (rest.startsWith("@")) {
107
+ return walkSegments(rest, roots[0]);
108
+ }
109
+ return walkNamedSegments(rest, roots[0]);
110
+ }
111
+ if (fragment.startsWith("/")) {
112
+ const rest = fragment.slice(1);
113
+ const slashIndex = rest.indexOf("/");
114
+ const rootIndexToken = slashIndex === -1 ? rest : rest.slice(0, slashIndex);
115
+ const rootIndex = Number(rootIndexToken);
116
+ if (!Number.isInteger(rootIndex) || rootIndex < 0 || rootIndex >= roots.length) return void 0;
117
+ const root = roots[rootIndex];
118
+ if (slashIndex === -1) return root;
119
+ return walkSegments(rest.slice(slashIndex + 1), root);
120
+ }
121
+ return findByIdValue(fragment, roots);
122
+ }
123
+ function walkNamedSegments(path, start) {
124
+ const segments = path.split("/");
125
+ let current = start;
126
+ for (const name of segments) {
127
+ if (!current) return void 0;
128
+ const currentClass = current.eClass();
129
+ const eClassifiersFeature = allStructuralFeaturesOf(currentClass).filter((feature) => feature.getName() === "eClassifiers").at(0);
130
+ if (eClassifiersFeature) {
131
+ current = findNamedChild(current, eClassifiersFeature, name);
132
+ continue;
133
+ }
134
+ const eStructuralFeaturesFeature = allStructuralFeaturesOf(currentClass).filter((feature) => feature.getName() === "eStructuralFeatures").at(0);
135
+ if (eStructuralFeaturesFeature) {
136
+ current = findNamedChild(current, eStructuralFeaturesFeature, name);
137
+ continue;
138
+ }
139
+ return void 0;
140
+ }
141
+ return current;
142
+ }
143
+ function findNamedChild(container, listFeature, name) {
144
+ const children = [...container.eGet(listFeature)];
145
+ return children.find((child) => {
146
+ const nameFeature = allStructuralFeaturesOf(child.eClass()).filter((feature) => feature.getName() === "name").at(0);
147
+ return nameFeature !== void 0 && child.eGet(nameFeature) === name;
148
+ });
149
+ }
150
+ function walkSegments(path, start) {
151
+ let current = start;
152
+ for (const segment of path.split("/")) {
153
+ if (!segment.startsWith("@")) return void 0;
154
+ const dotIndex = segment.indexOf(".");
155
+ const featureName = dotIndex === -1 ? segment.slice(1) : segment.slice(1, dotIndex);
156
+ const feature = allStructuralFeaturesOf(current.eClass()).filter((feature2) => feature2.getName() === featureName).at(0);
157
+ if (!feature) return void 0;
158
+ if (feature.isMany()) {
159
+ const indexToken = dotIndex === -1 ? void 0 : segment.slice(dotIndex + 1);
160
+ const index = Number(indexToken);
161
+ if (indexToken === void 0 || !Number.isInteger(index)) return void 0;
162
+ const list = [...current.eGet(feature)];
163
+ const next = list[index];
164
+ if (!next) return void 0;
165
+ current = next;
166
+ } else {
167
+ current = current.eGet(feature);
168
+ }
169
+ }
170
+ return current;
171
+ }
172
+ function findByIdValue(idValue, roots) {
173
+ for (const root of roots) {
174
+ const found = findByIdValueIn(root, idValue);
175
+ if (found) return found;
176
+ }
177
+ return void 0;
178
+ }
179
+ function findByIdValueIn(obj, idValue) {
180
+ const idAttribute = allAttributesOf(obj.eClass()).find((attr) => attr.isID());
181
+ if (idAttribute && obj.eIsSet(idAttribute) && String(obj.eGet(idAttribute)) === idValue) {
182
+ return obj;
183
+ }
184
+ for (const child of obj.eContents()) {
185
+ const found = findByIdValueIn(child, idValue);
186
+ if (found) return found;
187
+ }
188
+ return void 0;
189
+ }
190
+
191
+ // src/namespace-collector.ts
192
+ var NamespaceCollector = class {
193
+ prefixToUri = /* @__PURE__ */ new Map();
194
+ uriToPrefix = /* @__PURE__ */ new Map();
195
+ prefixFor(pkg) {
196
+ const nsURI = pkg.getNsURI();
197
+ if (!nsURI) {
198
+ throw new Error(`EPackage '${pkg.getName()}' has no nsURI set - cannot serialize instances of it.`);
199
+ }
200
+ const existing = this.uriToPrefix.get(nsURI);
201
+ if (existing) return existing;
202
+ const base = pkg.getNsPrefix() || pkg.getName() || "ns";
203
+ let candidate = base;
204
+ let suffix = 0;
205
+ while (this.prefixToUri.has(candidate) && this.prefixToUri.get(candidate) !== nsURI) {
206
+ suffix += 1;
207
+ candidate = `${base}${suffix}`;
208
+ }
209
+ this.prefixToUri.set(candidate, nsURI);
210
+ this.uriToPrefix.set(nsURI, candidate);
211
+ return candidate;
212
+ }
213
+ entries() {
214
+ return [...this.prefixToUri.entries()];
215
+ }
216
+ };
217
+
218
+ // src/xmi-resource-factory.ts
219
+ var import_core2 = require("@typemf/core");
220
+
221
+ // src/xmi-serializer.ts
222
+ var import_core = require("@typemf/core");
223
+
224
+ // src/xml-text.ts
225
+ function escapeAttributeValue(value) {
226
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/\r/g, "&#13;").replace(/\n/g, "&#10;").replace(/\t/g, "&#9;");
227
+ }
228
+ function escapeText(value) {
229
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
230
+ }
231
+
232
+ // src/xml-dom.ts
233
+ async function parseXmlDocument(text) {
234
+ const globalDOMParser = globalThis.DOMParser;
235
+ if (globalDOMParser) {
236
+ const doc2 = new globalDOMParser().parseFromString(text, "application/xml");
237
+ const parseError = doc2.getElementsByTagName("parsererror")[0];
238
+ if (parseError) {
239
+ throw new Error(`Failed to parse XML: ${parseError.textContent ?? "unknown parse error"}`);
240
+ }
241
+ return doc2;
242
+ }
243
+ const xmldom = await import("@xmldom/xmldom");
244
+ const errors = [];
245
+ const parser = new xmldom.DOMParser({
246
+ onError: (level, message) => {
247
+ if (level === "error" || level === "fatalError") errors.push(message);
248
+ }
249
+ });
250
+ const doc = parser.parseFromString(text, "application/xml");
251
+ if (errors.length > 0) {
252
+ throw new Error(`Failed to parse XML: ${errors.join("; ")}`);
253
+ }
254
+ return doc;
255
+ }
256
+
257
+ // src/xmi-serializer.ts
258
+ var XMI_NS = "http://www.omg.org/XMI";
259
+ var XSI_NS = "http://www.w3.org/2001/XMLSchema-instance";
260
+ var XmiSerializer = class {
261
+ computeFragment(target, roots) {
262
+ return computeEmfFragment(target, roots);
263
+ }
264
+ resolveFragment(fragment, roots) {
265
+ return resolveEmfFragment(fragment, roots);
266
+ }
267
+ async serialize(roots, resource) {
268
+ if (roots.length !== 1) {
269
+ throw new Error(
270
+ `XmiSerializer currently only supports exactly one root object (got ${roots.length}) - multi-root documents need the <xmi:XMI> wrapper form, not yet implemented. See NOTES.md.`
271
+ );
272
+ }
273
+ const root = roots[0];
274
+ const namespaces = new NamespaceCollector();
275
+ const rootPrefix = namespaces.prefixFor(requirePackage(root.eClass()));
276
+ const parts = writeObjectParts(root, `${rootPrefix}:${root.eClass().getName()}`, roots, resource, namespaces);
277
+ const nsAttrs = [
278
+ ["xmlns:xmi", XMI_NS],
279
+ ["xmlns:xsi", XSI_NS],
280
+ ...namespaces.entries().map(([prefix, uri]) => [`xmlns:${prefix}`, uri])
281
+ ];
282
+ const schemaLocationAttrs = [];
283
+ const rootPackage = requirePackage(root.eClass());
284
+ const rootPackageResource = (0, import_core.getResourceOf)(rootPackage);
285
+ if (rootPackageResource && rootPackageResource !== resource) {
286
+ const location = relativeOrAbsolutePath(resource.getURI(), rootPackageResource.getURI());
287
+ schemaLocationAttrs.push(["xsi:schemaLocation", `${rootPackage.getNsURI()} ${location}`]);
288
+ }
289
+ const allAttrs = [
290
+ ["xmi:version", "2.0"],
291
+ ...nsAttrs,
292
+ ...schemaLocationAttrs,
293
+ ...parts.attributes
294
+ ];
295
+ const attrStr = allAttrs.map(([k, v]) => ` ${k}="${escapeAttributeValue(v)}"`).join("");
296
+ const xml = `<?xml version="1.0" encoding="UTF-8"?>
297
+ ` + (parts.childrenXml ? `<${parts.tagName}${attrStr}>${parts.childrenXml}</${parts.tagName}>` : `<${parts.tagName}${attrStr}/>`);
298
+ return new TextEncoder().encode(xml);
299
+ }
300
+ /**
301
+ * Root-element only (not a full-tree walk), all declared xmlns:* values (not filtered to ones
302
+ * some xsi:type actually uses) - the simpler of the two real tradeoffs this method has for XMI,
303
+ * chosen deliberately: this project's own writer always declares every namespace a document
304
+ * needs on the root element (see NamespaceCollector/the ['xmlns:xmi', ...] block above), so this
305
+ * is exactly correct for anything this project produces. A third-party or hand-edited XMI file
306
+ * that declares a namespace deeper in the tree (legal XML, just non-standard practice for this
307
+ * kind of document) would have that namespace missed here - an accepted, documented limitation,
308
+ * not an oversight.
309
+ */
310
+ async peekReferencedNsURIs(content) {
311
+ const text = new TextDecoder().decode(content);
312
+ const doc = await parseXmlDocument(text);
313
+ const root = doc.documentElement;
314
+ if (!root) return [];
315
+ const nsURIs = [];
316
+ for (const attr of Array.from(root.attributes)) {
317
+ if (!attr.name.startsWith("xmlns:")) continue;
318
+ if (attr.value === XMI_NS || attr.value === XSI_NS) continue;
319
+ nsURIs.push(attr.value);
320
+ }
321
+ return nsURIs;
322
+ }
323
+ async deserialize(content, resource) {
324
+ const text = new TextDecoder().decode(content);
325
+ const doc = await parseXmlDocument(text);
326
+ const root = doc.documentElement;
327
+ if (!root) {
328
+ throw new Error(`'${resource.getURI().toString()}' has no document element - not a valid XML document.`);
329
+ }
330
+ const packageRegistry = resource.getResourceSet()?.getPackageRegistry();
331
+ if (!packageRegistry) {
332
+ throw new Error(
333
+ `Cannot deserialize '${resource.getURI().toString()}': its Resource has no ResourceSet, which is needed to look up EPackages by namespace URI.`
334
+ );
335
+ }
336
+ const ctx = { packageRegistry };
337
+ const elementToObj = /* @__PURE__ */ new Map();
338
+ const rootEClass = resolveElementEClass(root, packageRegistry);
339
+ const rootObj = constructShell(root, rootEClass, ctx, elementToObj);
340
+ const roots = [rootObj];
341
+ completeObject(root, rootObj, ctx, elementToObj, roots);
342
+ return roots;
343
+ }
344
+ };
345
+ function writeObjectParts(obj, tagName, roots, sourceResource, namespaces) {
346
+ const attributes = [];
347
+ let childrenXml = "";
348
+ for (const feature of obj.eClass().getEAllStructuralFeatures()) {
349
+ if (feature.isDerived() || feature.isTransient()) continue;
350
+ if (!obj.eIsSet(feature)) continue;
351
+ if (!isReferenceFeature(feature)) {
352
+ if (feature.isMany()) {
353
+ for (const value of obj.eGet(feature)) {
354
+ childrenXml += `<${feature.getName()}>${escapeText(encodeAttributeValue(value, feature))}</${feature.getName()}>`;
355
+ }
356
+ } else {
357
+ attributes.push([feature.getName(), encodeAttributeValue(obj.eGet(feature), feature)]);
358
+ }
359
+ continue;
360
+ }
361
+ const refFeature = feature;
362
+ const values = refFeature.isMany() ? [...obj.eGet(feature)] : [obj.eGet(feature)];
363
+ if (refFeature.isContainment()) {
364
+ for (const child of values) {
365
+ const childTag = childElementTag(child, refFeature, namespaces);
366
+ const childParts = writeObjectParts(child, feature.getName(), roots, sourceResource, namespaces);
367
+ const attrs = childTag.xsiType ? [["xsi:type", childTag.xsiType], ...childParts.attributes] : childParts.attributes;
368
+ childrenXml += renderElement({
369
+ tagName: childParts.tagName,
370
+ attributes: attrs,
371
+ childrenXml: childParts.childrenXml
372
+ });
373
+ }
374
+ continue;
375
+ }
376
+ const encoded = values.map((v) => encodeReferenceValue(v, refFeature, roots, sourceResource, namespaces));
377
+ const anyCrossDocument = encoded.some((e) => e.crossDocument);
378
+ if (anyCrossDocument) {
379
+ for (const e of encoded) {
380
+ const attrs = [["href", e.value]];
381
+ if (e.xsiType) attrs.unshift(["xsi:type", e.xsiType]);
382
+ childrenXml += renderElement({ tagName: feature.getName(), attributes: attrs, childrenXml: "" });
383
+ }
384
+ } else {
385
+ const anyPolymorphic = encoded.some((e) => e.xsiType);
386
+ const tokens = encoded.map(
387
+ (e) => anyPolymorphic ? `${e.xsiType ?? sameTypeToken(refFeature, namespaces)} ${e.value}` : e.value
388
+ );
389
+ attributes.push([feature.getName(), tokens.join(" ")]);
390
+ }
391
+ }
392
+ return { tagName, attributes, childrenXml };
393
+ }
394
+ function renderElement(parts) {
395
+ const attrStr = parts.attributes.map(([k, v]) => ` ${k}="${escapeAttributeValue(v)}"`).join("");
396
+ return parts.childrenXml ? `<${parts.tagName}${attrStr}>${parts.childrenXml}</${parts.tagName}>` : `<${parts.tagName}${attrStr}/>`;
397
+ }
398
+ function childElementTag(child, feature, namespaces) {
399
+ const declared = feature.getEType();
400
+ const actual = child.eClass();
401
+ if (declared === actual) return {};
402
+ const prefix = namespaces.prefixFor(requirePackage(actual));
403
+ return { xsiType: `${prefix}:${actual.getName()}` };
404
+ }
405
+ function sameTypeToken(feature, namespaces) {
406
+ const declared = feature.getEType();
407
+ const prefix = namespaces.prefixFor(requirePackage(declared));
408
+ return `${prefix}:${declared.getName()}`;
409
+ }
410
+ function encodeReferenceValue(target, feature, roots, sourceResource, namespaces) {
411
+ const declared = feature.getEType();
412
+ if (target.eIsProxy()) {
413
+ const proxy = target;
414
+ const actual2 = proxy.eClass();
415
+ const xsiType2 = declared !== actual2 ? `${namespaces.prefixFor(requirePackage(actual2))}:${actual2.getName()}` : void 0;
416
+ return { value: proxy.getProxyURI().toString(), crossDocument: true, xsiType: xsiType2 };
417
+ }
418
+ const targetResource = (0, import_core.getResourceOf)(target);
419
+ const actual = target.eClass();
420
+ const xsiType = declared !== actual ? `${namespaces.prefixFor(requirePackage(actual))}:${actual.getName()}` : void 0;
421
+ if (isReachableFromRoots(target, roots)) {
422
+ return { value: fragmentForTarget(target, targetResource, roots), crossDocument: false, xsiType };
423
+ }
424
+ const ecoreOwnPath = ecoreOwnFragmentPath(target);
425
+ if (ecoreOwnPath !== void 0) {
426
+ return { value: `${import_core.EcorePackageImpl.eINSTANCE.getNsURI()}#${ecoreOwnPath}`, crossDocument: true, xsiType };
427
+ }
428
+ if (targetResource) {
429
+ return {
430
+ value: `${targetResource.getURI().toString()}#${fragmentForTarget(target, targetResource, roots)}`,
431
+ crossDocument: true,
432
+ xsiType
433
+ };
434
+ }
435
+ throw new Error(
436
+ `Cannot encode a reference to an object of class '${actual.getName()}': it has no resource of its own, is not part of Ecore's own metamodel, and is not reachable from this document's own roots via containment.`
437
+ );
438
+ }
439
+ function isReachableFromRoots(target, roots) {
440
+ let current = target;
441
+ while (current) {
442
+ if (roots.includes(current)) return true;
443
+ current = current.eContainer();
444
+ }
445
+ return false;
446
+ }
447
+ function ecoreOwnFragmentPath(target) {
448
+ const ecore = import_core.EcorePackageImpl.eINSTANCE;
449
+ if ((0, import_core.isEClassifier)(target)) {
450
+ return target.getEPackage() === ecore ? `//${target.getName() ?? ""}` : void 0;
451
+ }
452
+ const containingClass = hasContainingClass(target) ? target.getEContainingClass() : void 0;
453
+ if (containingClass && containingClass.getEPackage() === ecore && isNamed(target)) {
454
+ return `//${containingClass.getName() ?? ""}/${target.getName() ?? ""}`;
455
+ }
456
+ return void 0;
457
+ }
458
+ function hasContainingClass(obj) {
459
+ return typeof obj.getEContainingClass === "function";
460
+ }
461
+ function isNamed(obj) {
462
+ return typeof obj.getName === "function";
463
+ }
464
+ function fragmentForTarget(target, owningResource, fallbackRoots) {
465
+ if (!owningResource) return computeEmfFragment(target, fallbackRoots);
466
+ const roots = owningResource.getContents().toArray();
467
+ const serializer = owningResource.getSerializer();
468
+ return serializer.computeFragment ? serializer.computeFragment(target, roots) : (0, import_core.computeFragment)(target, roots);
469
+ }
470
+ function isReferenceFeature(feature) {
471
+ return "isContainment" in feature;
472
+ }
473
+ function requirePackage(eClass) {
474
+ const pkg = eClass.getEPackage();
475
+ if (!pkg) throw new Error(`EClass '${eClass.getName()}' has no owning EPackage - cannot serialize instances of it.`);
476
+ return pkg;
477
+ }
478
+ function encodeAttributeValue(value, feature) {
479
+ const eType = feature.getEType();
480
+ if (eType?.getName() === "EDate" && value instanceof Date) {
481
+ return value.toISOString();
482
+ }
483
+ return String(value);
484
+ }
485
+ function resolveElementEClass(element, packageRegistry) {
486
+ const xsiType = element.getAttributeNS(XSI_NS, "type");
487
+ if (xsiType) {
488
+ return resolvePrefixedName(xsiType, element, packageRegistry);
489
+ }
490
+ const nsURI = element.namespaceURI;
491
+ if (!nsURI) {
492
+ throw new Error(`Element <${element.tagName}> has no namespace - cannot determine its EPackage.`);
493
+ }
494
+ const pkg = packageRegistry.getPackage(nsURI);
495
+ if (!pkg) {
496
+ throw new Error(`No EPackage registered for namespace '${nsURI}' (element <${element.tagName}>).`);
497
+ }
498
+ const classifier = pkg.getEClassifier(element.localName);
499
+ if (!classifier) {
500
+ throw new Error(`Package '${nsURI}' has no classifier named '${element.localName}'.`);
501
+ }
502
+ return classifier;
503
+ }
504
+ function resolveContainmentChildEClass(element, declared, packageRegistry) {
505
+ const xsiType = element.getAttributeNS(XSI_NS, "type");
506
+ if (xsiType) return resolvePrefixedName(xsiType, element, packageRegistry);
507
+ return declared;
508
+ }
509
+ function resolvePrefixedName(token, contextElement, packageRegistry) {
510
+ const [prefix, name] = token.includes(":") ? token.split(":") : [void 0, token];
511
+ const nsURI = prefix ? contextElement.lookupNamespaceURI(prefix) : contextElement.namespaceURI;
512
+ if (!nsURI) {
513
+ throw new Error(`Could not resolve namespace prefix '${prefix ?? "<default>"}' for type token '${token}'.`);
514
+ }
515
+ const pkg = packageRegistry.getPackage(nsURI);
516
+ if (!pkg) {
517
+ throw new Error(`No EPackage registered for namespace '${nsURI}' (type token '${token}').`);
518
+ }
519
+ const classifier = pkg.getEClassifier(name);
520
+ if (!classifier) {
521
+ throw new Error(`Package '${nsURI}' has no classifier named '${name}'.`);
522
+ }
523
+ return classifier;
524
+ }
525
+ function constructShell(element, eClass, ctx, elementToObj) {
526
+ const obj = eClass.getEPackage()?.getEFactoryInstance()?.create(eClass);
527
+ if (!obj) throw new Error("Could not create object");
528
+ elementToObj.set(element, obj);
529
+ const nameFeature = allStructuralFeaturesOf(eClass).filter((f) => f.getName() === "name").at(0);
530
+ if (nameFeature) {
531
+ const nameAttrValue = element.getAttribute("name");
532
+ if (nameAttrValue !== null) {
533
+ obj.eSet(nameFeature, decodeAttributeValue(nameAttrValue, nameFeature));
534
+ }
535
+ }
536
+ const idFeature = eClass.getEIDAttribute();
537
+ const idFeatureName = idFeature?.getName();
538
+ if (idFeature && idFeatureName && idFeature !== nameFeature) {
539
+ const idAttrValue = element.getAttribute(idFeatureName);
540
+ if (idAttrValue !== null) {
541
+ obj.eSet(idFeature, decodeAttributeValue(idAttrValue, idFeature));
542
+ }
543
+ }
544
+ for (let i = 0; i < element.childNodes.length; i++) {
545
+ const node = element.childNodes.item(i);
546
+ if (!node || node.nodeType !== 1) continue;
547
+ const child = node;
548
+ const feature = allStructuralFeaturesOf(eClass).filter((feature2) => feature2.getName() === child.localName).at(0);
549
+ if (!feature || !isReferenceFeature(feature)) continue;
550
+ const refFeature = feature;
551
+ if (!refFeature.isContainment()) continue;
552
+ const declaredEClass = refFeature.getEType();
553
+ const childEClass = resolveContainmentChildEClass(child, declaredEClass, ctx.packageRegistry);
554
+ const childObj = constructShell(child, childEClass, ctx, elementToObj);
555
+ if (feature.isMany()) {
556
+ const list = obj.eGet(feature);
557
+ list.add(childObj);
558
+ } else {
559
+ obj.eSet(feature, childObj);
560
+ }
561
+ }
562
+ return obj;
563
+ }
564
+ function completeObject(element, obj, ctx, elementToObj, roots) {
565
+ const eClass = obj.eClass();
566
+ for (let i = 0; i < element.attributes.length; i++) {
567
+ const attr = element.attributes.item(i);
568
+ if (attr.namespaceURI === XMI_NS || attr.namespaceURI === XSI_NS) continue;
569
+ if (attr.name.startsWith("xmlns")) continue;
570
+ const feature = allStructuralFeaturesOf(eClass).filter((feature2) => feature2.getName() === attr.localName).at(0);
571
+ if (!feature) continue;
572
+ if (!isReferenceFeature(feature)) {
573
+ if (feature.isMany()) {
574
+ continue;
575
+ }
576
+ obj.eSet(feature, decodeAttributeValue(attr.value, feature));
577
+ continue;
578
+ }
579
+ const refFeature = feature;
580
+ const resolved = decodeAttributeFormReference(attr.value, refFeature, element, roots, ctx.packageRegistry);
581
+ if (refFeature.isMany()) {
582
+ const list = obj.eGet(refFeature);
583
+ for (const r of resolved) list.add(r);
584
+ } else {
585
+ obj.eSet(refFeature, resolved[0]);
586
+ }
587
+ }
588
+ for (let i = 0; i < element.childNodes.length; i++) {
589
+ const node = element.childNodes.item(i);
590
+ if (!node || node.nodeType !== 1) continue;
591
+ const child = node;
592
+ const feature = allStructuralFeaturesOf(eClass).filter((feature2) => feature2.getName() === child.localName).at(0);
593
+ if (!feature) continue;
594
+ if (!isReferenceFeature(feature)) {
595
+ const value = decodeAttributeValue(child.textContent ?? "", feature);
596
+ if (feature.isMany()) {
597
+ const list = obj.eGet(feature);
598
+ list.add(value);
599
+ } else {
600
+ obj.eSet(feature, value);
601
+ }
602
+ continue;
603
+ }
604
+ const refFeature = feature;
605
+ if (refFeature.isContainment()) {
606
+ const childObj = elementToObj.get(child);
607
+ if (!childObj) {
608
+ throw new Error(
609
+ `Internal error: no shell was constructed for <${child.tagName}> during pass 1 - constructShell and completeObject have diverged on which children count as containment.`
610
+ );
611
+ }
612
+ completeObject(child, childObj, ctx, elementToObj, roots);
613
+ continue;
614
+ }
615
+ const href = child.getAttribute("href");
616
+ if (!href) {
617
+ throw new Error(`<${child.tagName}> under <${element.tagName}> is a non-containment reference but has no href.`);
618
+ }
619
+ const xsiType = child.getAttributeNS(XSI_NS, "type");
620
+ const declared = refFeature.getEType();
621
+ const proxyEClass = xsiType ? resolvePrefixedName(xsiType, child, ctx.packageRegistry) : declared;
622
+ const proxy = new import_core.ProxyEObjectImpl(proxyEClass, import_core.URI.parse(href));
623
+ if (feature.isMany()) {
624
+ const list = obj.eGet(feature);
625
+ list.add(proxy);
626
+ } else {
627
+ obj.eSet(feature, proxy);
628
+ }
629
+ }
630
+ }
631
+ function decodeAttributeFormReference(rawValue, feature, contextElement, roots, packageRegistry) {
632
+ const tokens = rawValue.split(/\s+/).filter((t) => t.length > 0);
633
+ const paired = tokens.length > 0 && tokens.length % 2 === 0 && tokens.every((t, i) => i % 2 === 0 ? looksLikeTypeToken(t) : true);
634
+ const pairs = paired ? tokens.filter((_, i) => i % 2 === 1).map((fragment, i) => ({ typeToken: tokens[i * 2], fragment })) : tokens.map((fragment) => ({ typeToken: void 0, fragment }));
635
+ return pairs.map(({ typeToken, fragment }) => {
636
+ if (isAbsoluteUri(fragment)) {
637
+ const declared = feature.getEType();
638
+ const proxyEClass = typeToken ? resolvePrefixedName(typeToken, contextElement, packageRegistry) : declared;
639
+ return new import_core.ProxyEObjectImpl(proxyEClass, import_core.URI.parse(fragment));
640
+ }
641
+ const normalized = fragment.startsWith("#") ? fragment.slice(1) : fragment;
642
+ const resolved = resolveEmfFragment(normalized, roots);
643
+ if (!resolved) {
644
+ throw new Error(
645
+ `Unresolved same-document reference '${fragment}' on feature '${feature.getName()}': no object matches this fragment in this document.`
646
+ );
647
+ }
648
+ return resolved;
649
+ });
650
+ }
651
+ function relativeOrAbsolutePath(fromUri, toUri) {
652
+ if (fromUri.getScheme() !== toUri.getScheme()) return toUri.toString();
653
+ const fromSegments = fromUri.getPath().split("/").filter((s) => s.length > 0);
654
+ const toSegments = toUri.getPath().split("/").filter((s) => s.length > 0);
655
+ const fromDir = fromSegments.slice(0, -1);
656
+ const toDir = toSegments.slice(0, -1);
657
+ const toFile = toSegments[toSegments.length - 1] ?? "";
658
+ let commonLength = 0;
659
+ while (commonLength < fromDir.length && commonLength < toDir.length && fromDir[commonLength] === toDir[commonLength]) {
660
+ commonLength++;
661
+ }
662
+ const upSegments = fromDir.slice(commonLength).map(() => "..");
663
+ const downSegments = toDir.slice(commonLength);
664
+ const relativeSegments = [...upSegments, ...downSegments, toFile];
665
+ return relativeSegments.join("/");
666
+ }
667
+ function looksLikeTypeToken(token) {
668
+ return token.includes(":") && !token.startsWith("/") && !token.startsWith("#");
669
+ }
670
+ function isAbsoluteUri(token) {
671
+ return /^[a-zA-Z][a-zA-Z0-9+.-]*:(\/\/|#)/.test(token);
672
+ }
673
+ function decodeAttributeValue(value, feature) {
674
+ const eType = feature.getEType();
675
+ const typeName = eType?.getName();
676
+ if (typeName === "EDate") return new Date(value);
677
+ if (typeName === "EInt" || typeName === "ELong" || typeName === "EDouble" || typeName === "EFloat")
678
+ return Number(value);
679
+ if (typeName === "EBoolean") return value === "true";
680
+ return value;
681
+ }
682
+
683
+ // src/xmi-resource-factory.ts
684
+ var XmiResourceFactory = class {
685
+ createResource(uri) {
686
+ return new import_core2.ResourceImpl(uri, new XmiSerializer());
687
+ }
688
+ };
689
+ function registerXmiFormat(registry) {
690
+ registry.registerForExtension("xmi", new XmiResourceFactory());
691
+ registry.registerForExtension("ecore", new XmiResourceFactory());
692
+ }
693
+ // Annotate the CommonJS export names for ESM import in node:
694
+ 0 && (module.exports = {
695
+ NamespaceCollector,
696
+ XmiResourceFactory,
697
+ XmiSerializer,
698
+ allAttributesOf,
699
+ allStructuralFeaturesOf,
700
+ computeEmfFragment,
701
+ escapeAttributeValue,
702
+ escapeText,
703
+ parseXmlDocument,
704
+ registerXmiFormat,
705
+ resolveEmfFragment
706
+ });
707
+ //# sourceMappingURL=index.cjs.map