@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/LICENSE +201 -0
- package/README.md +63 -0
- package/dist/index.cjs +707 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +149 -0
- package/dist/index.d.ts +149 -0
- package/dist/index.js +667 -0
- package/dist/index.js.map +1 -0
- package/package.json +44 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/emf-fragment.ts","../src/namespace-collector.ts","../src/xmi-resource-factory.ts","../src/xmi-serializer.ts","../src/xml-text.ts","../src/xml-dom.ts"],"sourcesContent":["export * from './emf-fragment.js';\nexport * from './namespace-collector.js';\nexport * from './xmi-resource-factory.js';\nexport * from './xmi-serializer.js';\nexport * from './xml-dom.js';\nexport * from './xml-text.js';\n","import { EObject, EStructuralFeature, EClass, EAttribute } from '@typemf/core';\n\n/**\n * Own + inherited structural features/attributes, computed manually by\n * walking getEStructuralFeatures() (own-only) up through getESuperTypes()\n * (single-level, direct supertypes) - not eClass.getEAllStructuralFeatures()/\n * getEAllAttributes() directly. Shared with xmi-serializer.ts, which has\n * the full explanation in its own doc comment: those methods are empty\n * on self-hosted, generated classifier metaclasses (one of the \"~14\n * traversal-based derived features\" that are stored fields on generated\n * classes, never auto-computed, since Pass 5/recomputeAllLists() is\n * deliberately skipped for them - see the generator's own NOTES.md).\n * getEStructuralFeatures() and getESuperTypes() are both real, populated\n * fields regardless of whether the metamodel in use is self-hosted or\n * hand-written, so this works correctly either way.\n */\nexport function allStructuralFeaturesOf(eClass: EClass): EStructuralFeature[] {\n const seen = new Set<EClass>();\n const result: EStructuralFeature[] = [];\n function visit(c: EClass): void {\n if (seen.has(c)) return;\n seen.add(c);\n for (const parent of c.getESuperTypes()) visit(parent);\n result.push(...c.getEStructuralFeatures());\n }\n visit(eClass);\n return result;\n}\n\nexport function allAttributesOf(eClass: EClass): EAttribute[] {\n return allStructuralFeaturesOf(eClass).filter((f): f is EAttribute => !('isContainment' in f));\n}\n\n/**\n * EMF's actual default fragment grammar - deliberately a separate\n * implementation from @typemf/core's computeFragment()/resolveFragment(),\n * not a shared refactor of it, per the decision to keep @typemf/xmi\n * self-contained. The underlying idea (walk containment, address by\n * feature+index, prefer an ID attribute when present) is the same one;\n * only the string grammar differs, to match what real EMF actually\n * produces and expects:\n *\n * - single-root document, target is a descendant: \"//@feature.index\"\n * (many-valued) or \"//@feature\" (single-valued), one such segment per\n * containment step, joined by \"/\"\n * - single-root document, target IS the root: \"/\"\n * - multi-root document: same, but prefixed with \"/<rootIndex>\" instead\n * of the bare \"/\"\n * - an object whose EClass has an ID attribute set: the ID value itself,\n * with no leading \"/\" at all (e.g. \"myBookId123\")\n *\n * Positional forms are fragile to reordering, same as @typemf/core's - an\n * ID attribute is preferred whenever the target EClass has one.\n */\nexport function computeEmfFragment(target: EObject, roots: EObject[]): string {\n const idAttribute = allAttributesOf(target.eClass()).find((attr) => attr.isID());\n if (idAttribute && target.eIsSet(idAttribute)) {\n return String(target.eGet(idAttribute));\n }\n\n const segments: string[] = [];\n let current = target;\n let container = current.eContainer();\n\n while (container) {\n const feature = current.eContainingFeature();\n if (!feature) {\n throw new Error('Object has a container but no containing feature - inconsistent containment state.');\n }\n let segment = `@${feature.getName()}`;\n if (feature.isMany()) {\n const siblings = [...(container.eGet(feature) as Iterable<EObject>)];\n const index = siblings.indexOf(current);\n if (index === -1) {\n throw new Error(`Object not found within its own containing feature '${feature.getName()}'.`);\n }\n segment += `.${index}`;\n }\n segments.unshift(segment);\n current = container;\n container = current.eContainer();\n }\n\n const rootIndex = roots.indexOf(current);\n if (rootIndex === -1) {\n throw new Error('Target object is not reachable from the given roots via containment.');\n }\n\n const path = segments.join('/');\n if (roots.length === 1) {\n return path === '' ? '/' : `//${path}`;\n }\n return path === '' ? `/${rootIndex}` : `/${rootIndex}/${path}`;\n}\n\n/** The inverse of computeEmfFragment(). */\nexport function resolveEmfFragment(fragment: string, roots: EObject[]): EObject | undefined {\n if (fragment === '/') {\n return roots.length === 1 ? roots[0] : undefined;\n }\n if (fragment.startsWith('//')) {\n const rest = fragment.slice(2);\n if (roots.length !== 1) return undefined;\n // Two genuinely different kinds of fragment share this \"//\" prefix,\n // disambiguated by a syntactic marker: \"@\" starts every segment of the\n // existing positional/containment form (\"@feature.index\"); its absence\n // means this is instead EMF's NAME-based form for addressing metamodel\n // elements by name (e.g. \"#//EClass\", \"#//EModelElement/eAnnotations\") -\n // used throughout real .ecore files, which are themselves ordinary XMI\n // instance documents whose root happens to be an EPackage. Both forms\n // start from the same `roots` - for a name-based fragment, roots[0] is\n // the EPackage itself, and each segment is a named-child lookup (a\n // classifier within a package, or a feature within a class) rather\n // than a containment descent by feature+index.\n if (rest.startsWith('@')) {\n return walkSegments(rest, roots[0]!);\n }\n return walkNamedSegments(rest, roots[0]!);\n }\n if (fragment.startsWith('/')) {\n const rest = fragment.slice(1);\n const slashIndex = rest.indexOf('/');\n const rootIndexToken = slashIndex === -1 ? rest : rest.slice(0, slashIndex);\n const rootIndex = Number(rootIndexToken);\n if (!Number.isInteger(rootIndex) || rootIndex < 0 || rootIndex >= roots.length) return undefined;\n const root = roots[rootIndex]!;\n if (slashIndex === -1) return root;\n return walkSegments(rest.slice(slashIndex + 1), root);\n }\n // Bare token: an ID-attribute value.\n return findByIdValue(fragment, roots);\n}\n\n/**\n * EMF's name-based fragment form, used throughout real .ecore files:\n * \"//EClass\" (a top-level classifier, looked up by name within the root\n * EPackage) or \"//EModelElement/eAnnotations\" (a named feature within that\n * classifier). Deliberately scoped to what real Ecore.ecore itself\n * actually needs - EPackage-to-classifier and EClass-to-feature lookups,\n * both by name - not a fully general named-element path covering every\n * possible EMF model-element kind (e.g. subpackages, or addressing an\n * operation by name, are not exercised by Ecore.ecore and are not\n * supported here).\n */\nfunction walkNamedSegments(path: string, start: EObject): EObject | undefined {\n const segments = path.split('/');\n let current: EObject | undefined = start;\n\n for (const name of segments) {\n if (!current) return undefined;\n const currentClass = current.eClass();\n // current is (structurally) an EPackage: look up a classifier IT\n // declares by name - via current's own \"eClassifiers\" feature VALUE,\n // not via a feature named \"eClassifiers\" found some other way.\n const eClassifiersFeature = allStructuralFeaturesOf(currentClass)\n .filter((feature) => feature.getName() === 'eClassifiers')\n .at(0);\n if (eClassifiersFeature) {\n current = findNamedChild(current, eClassifiersFeature, name);\n continue;\n }\n // Otherwise, current is (structurally) an EClass: look up a feature IT\n // declares by name, the same way - via current's own\n // \"eStructuralFeatures\" feature VALUE. (Looking the name up directly\n // on currentClass itself would instead search the shared reflective\n // shape every EClass-instance has - abstract/eSuperTypes/etc - which\n // is not what a \"ClassName/featureName\" fragment means.)\n const eStructuralFeaturesFeature = allStructuralFeaturesOf(currentClass)\n .filter((feature) => feature.getName() === 'eStructuralFeatures')\n .at(0);\n if (eStructuralFeaturesFeature) {\n current = findNamedChild(current, eStructuralFeaturesFeature, name);\n continue;\n }\n return undefined;\n }\n\n return current;\n}\n\nfunction findNamedChild(container: EObject, listFeature: EStructuralFeature, name: string): EObject | undefined {\n const children: EObject[] = [...(container.eGet(listFeature) as Iterable<EObject>)];\n return children.find((child: EObject) => {\n const nameFeature = allStructuralFeaturesOf(child.eClass())\n .filter((feature) => feature.getName() === 'name')\n .at(0);\n return nameFeature !== undefined && child.eGet(nameFeature) === name;\n });\n}\n\nfunction walkSegments(path: string, start: EObject): EObject | undefined {\n let current = start;\n for (const segment of path.split('/')) {\n if (!segment.startsWith('@')) return undefined;\n const dotIndex = segment.indexOf('.');\n const featureName = dotIndex === -1 ? segment.slice(1) : segment.slice(1, dotIndex);\n const feature = allStructuralFeaturesOf(current.eClass())\n .filter((feature) => feature.getName() === featureName)\n .at(0);\n if (!feature) return undefined;\n\n if (feature.isMany()) {\n const indexToken = dotIndex === -1 ? undefined : segment.slice(dotIndex + 1);\n const index = Number(indexToken);\n if (indexToken === undefined || !Number.isInteger(index)) return undefined;\n const list = [...(current.eGet(feature) as Iterable<EObject>)];\n const next = list[index];\n if (!next) return undefined;\n current = next;\n } else {\n current = current.eGet(feature) as EObject;\n }\n }\n return current;\n}\n\nfunction findByIdValue(idValue: string, roots: EObject[]): EObject | undefined {\n for (const root of roots) {\n const found = findByIdValueIn(root, idValue);\n if (found) return found;\n }\n return undefined;\n}\n\nfunction findByIdValueIn(obj: EObject, idValue: string): EObject | undefined {\n const idAttribute = allAttributesOf(obj.eClass()).find((attr) => attr.isID());\n if (idAttribute && obj.eIsSet(idAttribute) && String(obj.eGet(idAttribute)) === idValue) {\n return obj;\n }\n for (const child of obj.eContents()) {\n const found = findByIdValueIn(child, idValue);\n if (found) return found;\n }\n return undefined;\n}\n","import { EPackage } from '@typemf/core';\n\n/**\n * Collects the xmlns:prefix declarations a document needs while writing\n * it, mutated as writeObject() walks the graph. Deliberately not shared\n * with @typemf/json's NamespaceTable - a different package, a different\n * attribute convention (xmlns:X here vs. a $namespaces JSON object there),\n * and the two packages shouldn't depend on each other regardless.\n */\nexport class NamespaceCollector {\n private readonly prefixToUri = new Map<string, string>();\n private readonly uriToPrefix = new Map<string, string>();\n\n prefixFor(pkg: EPackage): string {\n const nsURI = pkg.getNsURI();\n if (!nsURI) {\n throw new Error(`EPackage '${pkg.getName()}' has no nsURI set - cannot serialize instances of it.`);\n }\n\n const existing = this.uriToPrefix.get(nsURI);\n if (existing) return existing;\n\n const base = pkg.getNsPrefix() || pkg.getName() || 'ns';\n let candidate = base;\n let suffix = 0;\n while (this.prefixToUri.has(candidate) && this.prefixToUri.get(candidate) !== nsURI) {\n suffix += 1;\n candidate = `${base}${suffix}`;\n }\n\n this.prefixToUri.set(candidate, nsURI);\n this.uriToPrefix.set(nsURI, candidate);\n return candidate;\n }\n\n entries(): Array<[string, string]> {\n return [...this.prefixToUri.entries()];\n }\n}\n","import { Resource, ResourceFactory, ResourceFactoryRegistry, ResourceImpl, URI } from '@typemf/core';\nimport { XmiSerializer } from './xmi-serializer.js';\n\nexport class XmiResourceFactory implements ResourceFactory {\n createResource(uri: URI): Resource {\n return new ResourceImpl(uri, new XmiSerializer());\n }\n}\n\n/**\n * Registers for both \"xmi\" and \"ecore\" - a .ecore file is itself an\n * ordinary XMI instance document (its root just happens to be an\n * EPackage), read by this exact same XmiSerializer with no separate\n * parsing logic. Interpreting one correctly still requires a meta-schema\n * (a real, registered EPackage describing what \"EClass\"/\"EAttribute\"/etc.\n * mean) to already be in the EPackageRegistry before parsing - see\n * @typemf/core's ecore/ for that piece, and NOTES.md for the full\n * bootstrapping story.\n */\nexport function registerXmiFormat(registry: ResourceFactoryRegistry): void {\n registry.registerForExtension('xmi', new XmiResourceFactory());\n registry.registerForExtension('ecore', new XmiResourceFactory());\n}\n","import {\n EClass,\n EcorePackageImpl,\n EObject,\n EPackageRegistry,\n EReference,\n EStructuralFeature,\n isEClassifier,\n ProxyEObjectImpl,\n Resource,\n URI,\n computeFragment as coreComputeFragment,\n getResourceOf,\n type EObjectSerializer,\n} from '@typemf/core';\nimport { allStructuralFeaturesOf, computeEmfFragment, resolveEmfFragment } from './emf-fragment.js';\nimport { NamespaceCollector } from './namespace-collector.js';\nimport { escapeAttributeValue, escapeText } from './xml-text.js';\nimport { parseXmlDocument } from './xml-dom.js';\n\nconst XMI_NS = 'http://www.omg.org/XMI';\nconst XSI_NS = 'http://www.w3.org/2001/XMLSchema-instance';\n\n/**\n * Targets real EMF's actual default XMI conventions (verified against\n * documented Java EMF behaviour, not assumed) so files this produces open\n * in real Eclipse-based tooling and vice versa:\n *\n * - a reference (any cardinality) becomes an XML ATTRIBUTE when its\n * target is in the SAME document, and a nested <feature href=\"...\"/>\n * CHILD ELEMENT when the target is in a DIFFERENT document - the\n * dispatch is by document locality, not by containment or cardinality\n * - fragments follow EMF's real default grammar: \"//@books.2\" (single\n * root, positional), \"/\" (single root, target is the root itself),\n * or the bare ID-attribute value when the EClass has one - see\n * emf-fragment.ts\n * - polymorphism on a cross-document reference: xsi:type on the href\n * child element\n * - polymorphism on a same-document reference: NOTES.md flags this as\n * a best-effort reconstruction, not independently verified against a\n * real Eclipse-produced file - see there before relying on it for\n * interop specifically in the polymorphic + same-document + intra-doc\n * case\n *\n * v1 scope: single-root documents only (see NOTES.md for the multi-root\n * <xmi:XMI> wrapper form, not yet implemented). Many-valued EAttributes\n * are written as repeated child elements - also flagged in NOTES.md as a\n * judgment call, not verified against EMF's own default for that specific\n * case.\n */\nexport class XmiSerializer implements EObjectSerializer {\n computeFragment(target: EObject, roots: EObject[]): string {\n return computeEmfFragment(target, roots);\n }\n\n resolveFragment(fragment: string, roots: EObject[]): EObject | undefined {\n return resolveEmfFragment(fragment, roots);\n }\n\n async serialize(roots: EObject[], resource: Resource): Promise<Uint8Array> {\n if (roots.length !== 1) {\n throw new Error(\n `XmiSerializer currently only supports exactly one root object (got ${roots.length}) - ` +\n 'multi-root documents need the <xmi:XMI> wrapper form, not yet implemented. See NOTES.md.'\n );\n }\n const root = roots[0]!;\n const namespaces = new NamespaceCollector();\n const rootPrefix = namespaces.prefixFor(requirePackage(root.eClass()));\n const parts = writeObjectParts(root, `${rootPrefix}:${root.eClass().getName()}`, roots, resource, namespaces);\n\n const nsAttrs: Array<[string, string]> = [\n ['xmlns:xmi', XMI_NS],\n ['xmlns:xsi', XSI_NS],\n ...namespaces.entries().map(([prefix, uri]): [string, string] => [`xmlns:${prefix}`, uri]),\n ];\n // xsi:schemaLocation=\"<nsURI> <path>\" for the root's own package, when that package was\n // itself loaded from a real, known file (getResourceOf - the same utility\n // encodeReferenceValue's own \"is this local\" check relies on) distinct from the document\n // being saved here - a dynamically-loaded .ecore file, the common case this project's own\n // \"New Model Instance\" dynamic mode produces. Matches the real, standard XMI/XSD convention\n // (confirmed directly against Eclipse's own output for the same scenario) that lets the\n // document be reopened later and point straight back at its own metamodel's real location,\n // without that package needing to be separately, globally registered first. Root's own\n // package only, not every package anything in the document happens to reference -\n // NamespaceCollector itself tracks prefixes and URIs, not the real EPackage objects behind\n // them, and the root's own package is what the real, reported gap was actually about.\n const schemaLocationAttrs: Array<[string, string]> = [];\n const rootPackage = requirePackage(root.eClass());\n const rootPackageResource = getResourceOf(rootPackage);\n if (rootPackageResource && rootPackageResource !== resource) {\n const location = relativeOrAbsolutePath(resource.getURI(), rootPackageResource.getURI());\n schemaLocationAttrs.push(['xsi:schemaLocation', `${rootPackage.getNsURI()} ${location}`]);\n }\n const allAttrs: Array<[string, string]> = [\n ['xmi:version', '2.0'],\n ...nsAttrs,\n ...schemaLocationAttrs,\n ...parts.attributes,\n ];\n const attrStr = allAttrs.map(([k, v]) => ` ${k}=\"${escapeAttributeValue(v)}\"`).join('');\n const xml =\n `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\\n` +\n (parts.childrenXml\n ? `<${parts.tagName}${attrStr}>${parts.childrenXml}</${parts.tagName}>`\n : `<${parts.tagName}${attrStr}/>`);\n\n return new TextEncoder().encode(xml);\n }\n\n /**\n * Root-element only (not a full-tree walk), all declared xmlns:* values (not filtered to ones\n * some xsi:type actually uses) - the simpler of the two real tradeoffs this method has for XMI,\n * chosen deliberately: this project's own writer always declares every namespace a document\n * needs on the root element (see NamespaceCollector/the ['xmlns:xmi', ...] block above), so this\n * is exactly correct for anything this project produces. A third-party or hand-edited XMI file\n * that declares a namespace deeper in the tree (legal XML, just non-standard practice for this\n * kind of document) would have that namespace missed here - an accepted, documented limitation,\n * not an oversight.\n */\n async peekReferencedNsURIs(content: Uint8Array): Promise<string[]> {\n const text = new TextDecoder().decode(content);\n const doc = await parseXmlDocument(text);\n const root = doc.documentElement;\n if (!root) return [];\n\n const nsURIs: string[] = [];\n for (const attr of Array.from(root.attributes)) {\n if (!attr.name.startsWith('xmlns:')) continue;\n if (attr.value === XMI_NS || attr.value === XSI_NS) continue;\n nsURIs.push(attr.value);\n }\n return nsURIs;\n }\n\n async deserialize(content: Uint8Array, resource: Resource): Promise<EObject[]> {\n const text = new TextDecoder().decode(content);\n const doc = await parseXmlDocument(text);\n const root = doc.documentElement;\n if (!root) {\n throw new Error(`'${resource.getURI().toString()}' has no document element - not a valid XML document.`);\n }\n\n const packageRegistry = resource.getResourceSet()?.getPackageRegistry();\n if (!packageRegistry) {\n throw new Error(\n `Cannot deserialize '${resource.getURI().toString()}': its Resource has no ResourceSet, which is ` +\n 'needed to look up EPackages by namespace URI.'\n );\n }\n\n const ctx: DeserializeContext = { packageRegistry };\n const elementToObj = new Map<Element, EObject>();\n\n // Pass 1: construct every object in the document (via its own\n // containment structure - classifiers, features, operations,\n // parameters, annotations, ... - recursively) and set only its\n // \"name\" (when its EClass has one), wiring each into its parent's\n // containment feature immediately. This establishes the complete\n // object graph and its full name/position-addressable structure\n // before anything tries to resolve a same-document reference.\n //\n // Pass 2 (completeObject, below) then re-walks the same tree - using\n // elementToObj to find the shell pass 1 already built for each\n // element, rather than constructing a new one - and fills in every\n // remaining attribute and reference.\n //\n // Necessary because real, externally-authored .ecore files contain\n // genuine FORWARD same-document references - e.g. real Ecore.ecore's\n // own \"EAttribute\" (declared first) has eSuperTypes=\"#//EStructuralFeature\"\n // (declared much later) - and resolving references inline, during\n // the same single pass that constructs objects, fails outright for\n // any such forward reference: the target simply doesn't exist yet.\n // Confirmed directly against the real file, not assumed - see\n // NOTES.md. A prior version deferred only same-document REFERENCES\n // via a pendingRefs callback queue, resolved once after the (single)\n // construction pass finished - but that doesn't help here, since the\n // reference target's shell doesn't exist during pass 1 at all in\n // that design; pass 1 here exists specifically to guarantee it does.\n const rootEClass = resolveElementEClass(root, packageRegistry);\n const rootObj = constructShell(root, rootEClass, ctx, elementToObj);\n const roots = [rootObj];\n\n completeObject(root, rootObj, ctx, elementToObj, roots);\n\n return roots;\n }\n}\n\n// ---------------------------------------------------------------------\n// Serialization\n// ---------------------------------------------------------------------\n\ninterface ElementParts {\n tagName: string;\n attributes: Array<[string, string]>;\n childrenXml: string;\n}\n\nfunction writeObjectParts(\n obj: EObject,\n tagName: string,\n roots: EObject[],\n sourceResource: Resource,\n namespaces: NamespaceCollector\n): ElementParts {\n const attributes: Array<[string, string]> = [];\n let childrenXml = '';\n\n for (const feature of obj.eClass().getEAllStructuralFeatures()) {\n // A derived/transient feature (e.g. EClass.eAllGenericSuperTypes, EAttribute.eAttributeType)\n // is, by definition, recomputed from other, real data - never independently persisted, real\n // EMF's own convention too. A real, confirmed bug found via direct reproduction against\n // Ecore.ecore itself (not specific to any one file): without this check, a derived reference\n // feature could get serialized as if it were real, stored data, and since its value may not\n // even be part of this document's own containment tree at all (e.g. a synthesized fallback\n // like EJavaObject), computeFragment/encodeReferenceValue would then fail outright, unable to\n // address something that was never meant to be written out in the first place.\n if (feature.isDerived() || feature.isTransient()) continue;\n if (!obj.eIsSet(feature)) continue;\n\n if (!isReferenceFeature(feature)) {\n if (feature.isMany()) {\n for (const value of obj.eGet(feature) as Iterable<unknown>) {\n childrenXml += `<${feature.getName()}>${escapeText(encodeAttributeValue(value, feature))}</${feature.getName()}>`;\n }\n } else {\n attributes.push([feature.getName() as string, encodeAttributeValue(obj.eGet(feature), feature)]);\n }\n continue;\n }\n\n const refFeature = feature as EReference;\n const values = refFeature.isMany() ? [...(obj.eGet(feature) as Iterable<EObject>)] : [obj.eGet(feature) as EObject];\n\n if (refFeature.isContainment()) {\n for (const child of values) {\n const childTag = childElementTag(child, refFeature, namespaces);\n const childParts = writeObjectParts(child, feature.getName() as string, roots, sourceResource, namespaces);\n const attrs = childTag.xsiType\n ? [['xsi:type', childTag.xsiType], ...childParts.attributes]\n : childParts.attributes;\n childrenXml += renderElement({\n tagName: childParts.tagName,\n attributes: attrs as Array<[string, string]>,\n childrenXml: childParts.childrenXml,\n });\n }\n continue;\n }\n\n // Non-containment: same-document -> attribute; cross-document -> child element(s).\n const encoded = values.map((v) => encodeReferenceValue(v, refFeature, roots, sourceResource, namespaces));\n const anyCrossDocument = encoded.some((e) => e.crossDocument);\n if (anyCrossDocument) {\n for (const e of encoded) {\n const attrs: Array<[string, string]> = [['href', e.value]];\n if (e.xsiType) attrs.unshift(['xsi:type', e.xsiType]);\n childrenXml += renderElement({ tagName: feature.getName() as string, attributes: attrs, childrenXml: '' });\n }\n } else {\n const anyPolymorphic = encoded.some((e) => e.xsiType);\n const tokens = encoded.map((e) =>\n anyPolymorphic ? `${e.xsiType ?? sameTypeToken(refFeature, namespaces)} ${e.value}` : e.value\n );\n attributes.push([feature.getName() as string, tokens.join(' ')]);\n }\n }\n\n return { tagName, attributes, childrenXml };\n}\n\nfunction renderElement(parts: ElementParts): string {\n const attrStr = parts.attributes.map(([k, v]) => ` ${k}=\"${escapeAttributeValue(v)}\"`).join('');\n return parts.childrenXml\n ? `<${parts.tagName}${attrStr}>${parts.childrenXml}</${parts.tagName}>`\n : `<${parts.tagName}${attrStr}/>`;\n}\n\nfunction childElementTag(child: EObject, feature: EReference, namespaces: NamespaceCollector): { xsiType?: string } {\n const declared = feature.getEType() as EClass | undefined;\n const actual = child.eClass();\n if (declared === actual) return {};\n const prefix = namespaces.prefixFor(requirePackage(actual));\n return { xsiType: `${prefix}:${actual.getName()}` };\n}\n\nfunction sameTypeToken(feature: EReference, namespaces: NamespaceCollector): string {\n const declared = feature.getEType() as EClass;\n const prefix = namespaces.prefixFor(requirePackage(declared));\n return `${prefix}:${declared.getName()}`;\n}\n\ninterface EncodedReference {\n value: string;\n crossDocument: boolean;\n xsiType?: string;\n}\n\nfunction encodeReferenceValue(\n target: EObject,\n feature: EReference,\n roots: EObject[],\n sourceResource: Resource,\n namespaces: NamespaceCollector\n): EncodedReference {\n const declared = feature.getEType() as EClass | undefined;\n\n if (target.eIsProxy()) {\n const proxy = target as ProxyEObjectImpl;\n const actual = proxy.eClass();\n const xsiType =\n declared !== actual ? `${namespaces.prefixFor(requirePackage(actual))}:${actual.getName()}` : undefined;\n return { value: proxy.getProxyURI().toString(), crossDocument: true, xsiType };\n }\n\n const targetResource = getResourceOf(target);\n const actual = target.eClass();\n const xsiType =\n declared !== actual ? `${namespaces.prefixFor(requirePackage(actual))}:${actual.getName()}` : undefined;\n\n // Genuine reachability from THIS document's own roots, not merely \"has no resource of its own\"\n // - a real, confirmed bug found via direct reproduction against Ecore.ecore itself: the old\n // check (`!targetResource || ...`) treated \"not attached to any resource\" as automatically\n // local, which is wrong for something like EcorePackage's own EJavaObject - a real, stable,\n // well-known classifier, always available, but never loaded via any real Resource.load() (it's\n // part of the self-hosted, in-memory EcorePackageImpl.eINSTANCE), so getResourceOf() correctly\n // (if misleadingly, for this purpose) returns undefined for it. Walking eContainer() against\n // `roots` directly, rather than trusting resource identity, is what actually decides this.\n if (isReachableFromRoots(target, roots)) {\n return { value: fragmentForTarget(target, targetResource, roots), crossDocument: false, xsiType };\n }\n\n // Not reachable from this document at all - but if it's part of Ecore's own, always-available\n // metamodel, it's addressed the same way real EMF addresses it: a cross-document reference\n // into Ecore's own real nsURI, with a name-based (not positional) fragment, since Ecore's own\n // classifiers/features are referenced by name in every real .ecore file, never by index.\n const ecoreOwnPath = ecoreOwnFragmentPath(target);\n if (ecoreOwnPath !== undefined) {\n return { value: `${EcorePackageImpl.eINSTANCE.getNsURI()}#${ecoreOwnPath}`, crossDocument: true, xsiType };\n }\n\n if (targetResource) {\n return {\n value: `${targetResource.getURI().toString()}#${fragmentForTarget(target, targetResource, roots)}`,\n crossDocument: true,\n xsiType,\n };\n }\n\n // Genuinely unreachable, not part of Ecore's own metamodel, and not attached to any other real\n // resource either - a real, separate problem (e.g. an object created but never actually\n // attached anywhere), kept as a clear, thrown error rather than silently papered over.\n throw new Error(\n `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.`\n );\n}\n\n/** Whether `target`'s own eContainer() chain reaches one of `roots` - the real, governing\n * question \"same-document fragment\" needs answered, not resource identity. */\nfunction isReachableFromRoots(target: EObject, roots: EObject[]): boolean {\n let current: EObject | undefined = target;\n while (current) {\n if (roots.includes(current)) return true;\n current = current.eContainer();\n }\n return false;\n}\n\n/**\n * The real EMF-style name-based path to `target` within Ecore's own, always-available metamodel\n * (e.g. \"//EJavaObject\", \"//EAttribute/eAttributeType\") - undefined if target isn't part of it at\n * all.\n *\n * Deliberately type-aware (checking getEPackage()/getEContainingClass(), not eContainer()) - a\n * real, confirmed gap found via direct reproduction: eContainer() is never actually wired up for\n * bootstrap-constructed Ecore objects at all (confirmed directly: EJavaObject.eContainer() is\n * undefined, not EcorePackageImpl.eINSTANCE, even though EJavaObject.getEPackage() correctly\n * returns it) - eBootstrapList()'s whole point is constructing the self-hosted metamodel without\n * the normal containment-wiring overhead eDidAdd() would otherwise do, so walking eContainer() to\n * discover \"is this Ecore's own\" can never work for it. getEPackage() (on a classifier) and\n * getEContainingClass() (on a feature/operation) are explicitly-set fields instead, set directly\n * during bootstrap regardless of containment wiring - the same reason a much earlier gap in this\n * project (\"EClassifier.getEPackage() returns undefined after loading\") had to be fixed by\n * setting that field directly rather than deriving it from eContainer().\n */\nfunction ecoreOwnFragmentPath(target: EObject): string | undefined {\n const ecore = EcorePackageImpl.eINSTANCE;\n\n if (isEClassifier(target)) {\n return target.getEPackage() === ecore ? `//${target.getName() ?? ''}` : undefined;\n }\n\n const containingClass = hasContainingClass(target) ? target.getEContainingClass() : undefined;\n if (containingClass && containingClass.getEPackage() === ecore && isNamed(target)) {\n return `//${containingClass.getName() ?? ''}/${target.getName() ?? ''}`;\n }\n return undefined;\n}\n\nfunction hasContainingClass(obj: EObject): obj is EObject & { getEContainingClass(): EClass | undefined } {\n return typeof (obj as { getEContainingClass?: unknown }).getEContainingClass === 'function';\n}\n\nfunction isNamed(obj: EObject): obj is EObject & { getName(): string | undefined } {\n return typeof (obj as { getName?: unknown }).getName === 'function';\n}\n\n/**\n * Defers to whichever resource actually owns `target` for how its\n * fragment should be written - the same dispatch @typemf/json's writer\n * uses, deliberately not shared code between the two packages. The\n * fallback when the owning resource's serializer doesn't override\n * computeFragment() is @typemf/core's own DEFAULT grammar, not this\n * package's EMF grammar - a plain (non-overriding) target format, like\n * @typemf/json, means \"I use core's default\", so writing anything else\n * there would produce a fragment its own reader can't resolve. XMI's own\n * grammar is used only as the fallback for the (unusual) case where\n * `target` isn't attached to any resource at all yet.\n */\nfunction fragmentForTarget(target: EObject, owningResource: Resource | undefined, fallbackRoots: EObject[]): string {\n if (!owningResource) return computeEmfFragment(target, fallbackRoots);\n const roots = owningResource.getContents().toArray();\n const serializer = owningResource.getSerializer();\n return serializer.computeFragment ? serializer.computeFragment(target, roots) : coreComputeFragment(target, roots);\n}\n\nfunction isReferenceFeature(feature: EStructuralFeature): feature is EReference {\n return 'isContainment' in feature;\n}\n\nfunction requirePackage(eClass: EClass) {\n const pkg = eClass.getEPackage();\n if (!pkg) throw new Error(`EClass '${eClass.getName()}' has no owning EPackage - cannot serialize instances of it.`);\n return pkg;\n}\n\n/**\n * EDate -> ISO string (XML has no native date type). EEnum -> literal\n * name, matching the same convention @typemf/json established (an EEnum\n * attribute's in-memory value already IS its literal's name). Every other\n * primitive is already a string/number/boolean's natural toString().\n */\nfunction encodeAttributeValue(value: unknown, feature: EStructuralFeature): string {\n const eType = feature.getEType();\n if (eType?.getName() === 'EDate' && value instanceof Date) {\n return value.toISOString();\n }\n return String(value);\n}\n\n// ---------------------------------------------------------------------\n// Deserialization\n// ---------------------------------------------------------------------\n\ninterface DeserializeContext {\n packageRegistry: EPackageRegistry;\n}\n\nfunction resolveElementEClass(element: Element, packageRegistry: EPackageRegistry): EClass {\n const xsiType = element.getAttributeNS(XSI_NS, 'type');\n if (xsiType) {\n return resolvePrefixedName(xsiType, element, packageRegistry);\n }\n const nsURI = element.namespaceURI;\n if (!nsURI) {\n throw new Error(`Element <${element.tagName}> has no namespace - cannot determine its EPackage.`);\n }\n const pkg = packageRegistry.getPackage(nsURI);\n if (!pkg) {\n throw new Error(`No EPackage registered for namespace '${nsURI}' (element <${element.tagName}>).`);\n }\n const classifier = pkg.getEClassifier(element.localName);\n if (!classifier) {\n throw new Error(`Package '${nsURI}' has no classifier named '${element.localName}'.`);\n }\n return classifier as EClass;\n}\n\n/**\n * A containment child element's EClass, WITHOUT requiring that element to\n * have a namespace of its own. Feature-name-tagged elements (<books>,\n * <cover>, ...) are matched against the parent EClass's structural\n * features by plain local-name string comparison, not namespace lookup -\n * that's how real EMF resolves them too (only classifier-identifying\n * elements/attributes - the document root, an xsi:type value - are ever\n * namespace-qualified). The declared feature type is correct in the\n * overwhelmingly common (non-polymorphic) case; xsi:type on the child\n * element, when present, overrides it - and xsi:type's own value CAN\n * still be resolved via the child element's in-scope namespace bindings\n * even though the element itself has no namespace, since prefix lookup\n * walks the ancestor chain regardless.\n */\nfunction resolveContainmentChildEClass(element: Element, declared: EClass, packageRegistry: EPackageRegistry): EClass {\n const xsiType = element.getAttributeNS(XSI_NS, 'type');\n if (xsiType) return resolvePrefixedName(xsiType, element, packageRegistry);\n return declared;\n}\n\n/** Resolves a \"prefix:Name\" token (from xsi:type or a same-document typed-reference token) via the element's own in-scope namespace bindings. */\nfunction resolvePrefixedName(token: string, contextElement: Element, packageRegistry: EPackageRegistry): EClass {\n const [prefix, name] = token.includes(':') ? token.split(':') : [undefined, token];\n const nsURI = prefix ? contextElement.lookupNamespaceURI(prefix) : contextElement.namespaceURI;\n if (!nsURI) {\n throw new Error(`Could not resolve namespace prefix '${prefix ?? '<default>'}' for type token '${token}'.`);\n }\n const pkg = packageRegistry.getPackage(nsURI);\n if (!pkg) {\n throw new Error(`No EPackage registered for namespace '${nsURI}' (type token '${token}').`);\n }\n const classifier = pkg.getEClassifier(name!);\n if (!classifier) {\n throw new Error(`Package '${nsURI}' has no classifier named '${name}'.`);\n }\n return classifier as EClass;\n}\n\n/**\n * Pass 1. Constructs an object for `element`, sets only its \"name\"\n * feature (when its EClass has one and the element has a \"name\" XML\n * attribute), records it in `elementToObj` for pass 2 to find again, and\n * recurses into every CONTAINMENT child - wiring each into this object's\n * containment feature immediately, so the full tree (not just this one\n * object) is name/position-navigable by the time pass 1 as a whole\n * finishes. Deliberately does not touch any other attribute or\n * non-containment reference - completeObject does that, in pass 2, once\n * every object in the document is guaranteed to already exist.\n *\n * Only \"name\" specifically, not a general \"ID attribute\" lookup\n * (EClass.getEIDAttribute(), which requires isID()) - confirmed by\n * reading resolveEmfFragment's own name-based path (walkNamedSegments /\n * findNamedChild in emf-fragment.ts): it matches by literally looking up\n * a feature named \"name\" and comparing its value, regardless of whether\n * that feature happens to be marked isID() - real Ecore.ecore's own\n * ENamedElement.name is not marked iD=\"true\" at all, yet \"#//EClassName\"\n * fragments throughout the file depend on exactly this lookup succeeding.\n */\nfunction constructShell(\n element: Element,\n eClass: EClass,\n ctx: DeserializeContext,\n elementToObj: Map<Element, EObject>\n): EObject {\n const obj = eClass.getEPackage()?.getEFactoryInstance()?.create(eClass);\n if (!obj) throw new Error('Could not create object');\n elementToObj.set(element, obj);\n\n // Two different fragment-resolution paths need two different early\n // fields set, confirmed by actually running the existing test suite\n // against this, not assumed:\n // - real Ecore.ecore's own \"#//ClassName\" fragments resolve via\n // walkNamedSegments/findNamedChild (emf-fragment.ts), which matches\n // literally on a feature named \"name\" - regardless of isID().\n // - an arbitrary user metamodel's bare-token ID fragments (e.g.\n // \"Dune\") resolve via findByIdValueIn, which matches on whichever\n // attribute IS marked isID() - which need not be named \"name\" at\n // all (the sample metamodel's own ID attribute is \"title\").\n // Both need to already hold their real value before any reference to\n // this object is resolved, so pass 1 sets both here, whichever exist.\n const nameFeature = allStructuralFeaturesOf(eClass)\n .filter((f) => f.getName() === 'name')\n .at(0);\n if (nameFeature) {\n const nameAttrValue = element.getAttribute('name');\n if (nameAttrValue !== null) {\n obj.eSet(nameFeature, decodeAttributeValue(nameAttrValue, nameFeature));\n }\n }\n const idFeature = eClass.getEIDAttribute();\n const idFeatureName = idFeature?.getName();\n if (idFeature && idFeatureName && idFeature !== nameFeature) {\n const idAttrValue = element.getAttribute(idFeatureName);\n if (idAttrValue !== null) {\n obj.eSet(idFeature, decodeAttributeValue(idAttrValue, idFeature));\n }\n }\n\n for (let i = 0; i < element.childNodes.length; i++) {\n const node = element.childNodes.item(i);\n if (!node || node.nodeType !== 1 /* ELEMENT_NODE */) continue;\n const child = node as unknown as Element;\n\n const feature = allStructuralFeaturesOf(eClass)\n .filter((feature) => feature.getName() === child.localName)\n .at(0);\n if (!feature || !isReferenceFeature(feature)) continue; // many-valued attribute or unrecognized: completeObject's job\n const refFeature = feature as EReference;\n if (!refFeature.isContainment()) continue; // cross-document href reference: completeObject's job\n\n const declaredEClass = refFeature.getEType() as EClass;\n const childEClass = resolveContainmentChildEClass(child, declaredEClass, ctx.packageRegistry);\n const childObj = constructShell(child, childEClass, ctx, elementToObj);\n if (feature.isMany()) {\n const list = obj.eGet(feature) as { add(v: EObject): void };\n list.add(childObj);\n } else {\n obj.eSet(feature, childObj);\n }\n }\n\n return obj;\n}\n\n/**\n * Pass 2. Fills in every attribute and reference `constructShell` (pass\n * 1) deliberately left untouched, for the object it already built for\n * `element` (looked up via `elementToObj`, or `obj` directly for the\n * element currently being completed - never constructs anything new).\n * Same-document references now resolve unconditionally inline: by the\n * time this pass runs at all, pass 1 has already finished for the WHOLE\n * document, so every possible reference target - forward or backward -\n * already exists and is already wired into the containment tree.\n */\nfunction completeObject(\n element: Element,\n obj: EObject,\n ctx: DeserializeContext,\n elementToObj: Map<Element, EObject>,\n roots: EObject[]\n): void {\n const eClass = obj.eClass();\n\n for (let i = 0; i < element.attributes.length; i++) {\n const attr = element.attributes.item(i)!;\n if (attr.namespaceURI === XMI_NS || attr.namespaceURI === XSI_NS) continue;\n if (attr.name.startsWith('xmlns')) continue;\n\n const feature = allStructuralFeaturesOf(eClass)\n .filter((feature) => feature.getName() === attr.localName)\n .at(0);\n if (!feature) continue;\n\n if (!isReferenceFeature(feature)) {\n if (feature.isMany()) {\n // Many-valued attributes are written as child elements (see the\n // class doc comment) - an attribute-form value here would be an\n // externally-authored file using a different convention; not\n // supported in v1.\n continue;\n }\n obj.eSet(feature, decodeAttributeValue(attr.value, feature));\n continue;\n }\n\n // Reference(s) written as an attribute regardless of cardinality - same-document fragments\n // (most attribute-form references) resolved immediately, since every possible same-document\n // target already exists by this point; a same-attribute cross-document reference to another\n // document entirely (see decodeAttributeFormReference's own reasoning) constructed as a proxy\n // instead, exactly like the href-child-element case just below.\n const refFeature = feature as EReference;\n const resolved = decodeAttributeFormReference(attr.value, refFeature, element, roots, ctx.packageRegistry);\n if (refFeature.isMany()) {\n const list = obj.eGet(refFeature) as { add(v: EObject): void };\n for (const r of resolved) list.add(r);\n } else {\n obj.eSet(refFeature, resolved[0]);\n }\n }\n\n for (let i = 0; i < element.childNodes.length; i++) {\n const node = element.childNodes.item(i);\n if (!node || node.nodeType !== 1 /* ELEMENT_NODE */) continue;\n const child = node as unknown as Element;\n\n const feature = allStructuralFeaturesOf(eClass)\n .filter((feature) => feature.getName() === child.localName)\n .at(0);\n if (!feature) continue;\n\n if (!isReferenceFeature(feature)) {\n // Many-valued attribute, written as a repeated child element.\n const value = decodeAttributeValue(child.textContent ?? '', feature);\n if (feature.isMany()) {\n const list = obj.eGet(feature) as { add(v: unknown): void };\n list.add(value);\n } else {\n obj.eSet(feature, value);\n }\n continue;\n }\n\n const refFeature = feature as EReference;\n if (refFeature.isContainment()) {\n // Already constructed and wired onto obj by pass 1 - just recurse\n // to fill in ITS attributes/references too.\n const childObj = elementToObj.get(child);\n if (!childObj) {\n throw new Error(\n `Internal error: no shell was constructed for <${child.tagName}> during pass 1 - ` +\n 'constructShell and completeObject have diverged on which children count as containment.'\n );\n }\n completeObject(child, childObj, ctx, elementToObj, roots);\n continue;\n }\n\n // Cross-document reference, written as a <feature href=\"...\"/> child element.\n const href = child.getAttribute('href');\n if (!href) {\n throw new Error(`<${child.tagName}> under <${element.tagName}> is a non-containment reference but has no href.`);\n }\n const xsiType = child.getAttributeNS(XSI_NS, 'type');\n const declared = refFeature.getEType() as EClass;\n const proxyEClass = xsiType ? resolvePrefixedName(xsiType, child, ctx.packageRegistry) : declared;\n const proxy = new ProxyEObjectImpl(proxyEClass, URI.parse(href));\n if (feature.isMany()) {\n const list = obj.eGet(feature) as { add(v: EObject): void };\n list.add(proxy);\n } else {\n obj.eSet(feature, proxy);\n }\n }\n}\n\n/**\n * A reference written as an XML attribute - same-document fragments (the common case) and\n * cross-document references into an entirely different document both use this same attribute\n * form in real EMF-authored XMI (a <feature href=\"...\"/> child element, the other form this\n * serializer writes, is only one of the two conventions actually in use - see writeObjectParts's\n * own \"anyPolymorphic\" attribute-form path, which already produces both from this side; this was\n * the read-side gap that had never been exercised until parsing a real, externally-authored file).\n * Each whitespace-separated fragment is classified independently: one that parses as an absolute\n * URI (a real scheme, e.g. \"http://www.eclipse.org/emf/2002/Ecore#//EString\") is cross-document,\n * resolved as a ProxyEObjectImpl exactly like the href-child-element case; anything else is a\n * same-document fragment, resolved immediately via resolveEmfFragment, since every possible\n * same-document target already exists by this point.\n */\nfunction decodeAttributeFormReference(\n rawValue: string,\n feature: EReference,\n contextElement: Element,\n roots: EObject[],\n packageRegistry: EPackageRegistry\n): EObject[] {\n const tokens = rawValue.split(/\\s+/).filter((t) => t.length > 0);\n const paired =\n tokens.length > 0 &&\n tokens.length % 2 === 0 &&\n tokens.every((t, i) => (i % 2 === 0 ? looksLikeTypeToken(t) : true));\n\n const pairs: Array<{ typeToken: string | undefined; fragment: string }> = paired\n ? tokens.filter((_, i) => i % 2 === 1).map((fragment, i) => ({ typeToken: tokens[i * 2], fragment }))\n : tokens.map((fragment) => ({ typeToken: undefined, fragment }));\n\n return pairs.map(({ typeToken, fragment }) => {\n if (isAbsoluteUri(fragment)) {\n const declared = feature.getEType() as EClass;\n const proxyEClass = typeToken ? resolvePrefixedName(typeToken, contextElement, packageRegistry) : declared;\n return new ProxyEObjectImpl(proxyEClass, URI.parse(fragment));\n }\n\n // Real EMF-authored files always write same-document attribute-form\n // references with a leading \"#\" (e.g. eType=\"#//EString\") - our own\n // writer never produces one (computeEmfFragment's own output has no\n // \"#\"), so this was never exercised by our own round-trip tests until\n // parsing a real external file. Strip it here, at the point raw XML\n // attribute text becomes a fragment to resolve, rather than teaching\n // every fragment-parsing branch to tolerate an optional prefix.\n const normalized = fragment.startsWith('#') ? fragment.slice(1) : fragment;\n const resolved = resolveEmfFragment(normalized, roots);\n if (!resolved) {\n throw new Error(\n `Unresolved same-document reference '${fragment}' on feature '${feature.getName()}': ` +\n 'no object matches this fragment in this document.'\n );\n }\n return resolved;\n });\n}\n\n/**\n * A relative path from `fromUri` to `toUri` when both share a scheme (the common, same-workspace\n * case this is actually for), else `toUri`'s own full string form as a safe fallback - a portable,\n * hand-rolled computation (segment-by-segment common-prefix comparison, \"../\" for each remaining\n * `from` segment) rather than importing node:path, since this package is deliberately environment-\n * agnostic, not Node-specific.\n */\nfunction relativeOrAbsolutePath(fromUri: URI, toUri: URI): string {\n if (fromUri.getScheme() !== toUri.getScheme()) return toUri.toString();\n\n const fromSegments = fromUri\n .getPath()\n .split('/')\n .filter((s) => s.length > 0);\n const toSegments = toUri\n .getPath()\n .split('/')\n .filter((s) => s.length > 0);\n // The `to` file's own name is never a shared \"directory\" segment to compare away - compared\n // against `from`'s own containing directory only.\n const fromDir = fromSegments.slice(0, -1);\n const toDir = toSegments.slice(0, -1);\n const toFile = toSegments[toSegments.length - 1] ?? '';\n\n let commonLength = 0;\n while (\n commonLength < fromDir.length &&\n commonLength < toDir.length &&\n fromDir[commonLength] === toDir[commonLength]\n ) {\n commonLength++;\n }\n\n const upSegments = fromDir.slice(commonLength).map(() => '..');\n const downSegments = toDir.slice(commonLength);\n const relativeSegments = [...upSegments, ...downSegments, toFile];\n return relativeSegments.join('/');\n}\n\nfunction looksLikeTypeToken(token: string): boolean {\n return token.includes(':') && !token.startsWith('/') && !token.startsWith('#');\n}\n\n/** A real scheme prefix immediately followed by \"//\" or \"#\" (e.g. \"http://...\",\n * \"platform:/resource/...#//Foo\") - the unambiguous signal of an absolute URI, never produced by\n * a same-document fragment (computeEmfFragment's own output starts with \"/\" or a digit, never a\n * scheme) or a prefixed type token like \"ecore:EDataType\" (never followed by \"//\" or \"#\"). */\nfunction isAbsoluteUri(token: string): boolean {\n return /^[a-zA-Z][a-zA-Z0-9+.-]*:(\\/\\/|#)/.test(token);\n}\n\nfunction decodeAttributeValue(value: string, feature: EStructuralFeature): unknown {\n const eType = feature.getEType();\n const typeName = eType?.getName();\n if (typeName === 'EDate') return new Date(value);\n if (typeName === 'EInt' || typeName === 'ELong' || typeName === 'EDouble' || typeName === 'EFloat')\n return Number(value);\n if (typeName === 'EBoolean') return value === 'true';\n return value;\n}\n","/**\n * Writing XML is built as direct string generation rather than through a\n * DOM Document, deliberately asymmetric with the parse side: constructing\n * a spec-correct Document identically across native DOMParser and\n * @xmldom/xmldom is real complexity (they don't expose identical\n * `document.implementation.createDocument`/XMLSerializer behaviour), while\n * generating well-formed XML text with correct escaping is simple enough\n * to not need it. Reading genuinely needs a real parser (arbitrary,\n * possibly untrusted input); writing only needs to escape values we\n * already control.\n */\n\nexport function escapeAttributeValue(value: string): string {\n return value\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/\\r/g, ' ')\n .replace(/\\n/g, ' ')\n .replace(/\\t/g, '	');\n}\n\nexport function escapeText(value: string): string {\n return value.replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>');\n}\n","/**\n * Parses `text` into a real DOM Document, using whichever DOMParser is\n * actually available: the browser's native one (feature-detected, no\n * import needed), or @xmldom/xmldom's, dynamically imported only when\n * running in Node - this is the pattern designed back when @typemf/core's\n * own ecore/ parser was first discussed, applied for real here since XMI\n * needs genuine XML parsing (unlike @typemf/json, which only ever needed\n * JSON.parse - universal in both environments, nothing to dispatch on).\n *\n * Deliberately does not attempt DTD/external-entity resolution in either\n * path - @xmldom/xmldom does not fetch external resources at all, and\n * nothing here changes that, which is the property that matters for\n * loading a .xmi/.ecore file from a source you don't fully trust (a\n * workspace file authored by someone else, a third-party plugin's model).\n */\nexport async function parseXmlDocument(text: string): Promise<Document> {\n const globalDOMParser = (globalThis as { DOMParser?: typeof DOMParser }).DOMParser;\n\n if (globalDOMParser) {\n const doc = new globalDOMParser().parseFromString(text, 'application/xml') as unknown as Document;\n // Browsers report a parse failure by embedding a <parsererror> element\n // rather than throwing.\n const parseError = doc.getElementsByTagName('parsererror')[0];\n if (parseError) {\n throw new Error(`Failed to parse XML: ${parseError.textContent ?? 'unknown parse error'}`);\n }\n return doc;\n }\n\n // Node: @xmldom/xmldom reports errors via a callback rather than a\n // <parsererror> element, and rather than throwing synchronously on\n // warnings - collect and surface fatal ones explicitly.\n const xmldom = await import('@xmldom/xmldom');\n const errors: string[] = [];\n const parser = new xmldom.DOMParser({\n onError: (level: string, message: string) => {\n if (level === 'error' || level === 'fatalError') errors.push(message);\n },\n });\n const doc = parser.parseFromString(text, 'application/xml') as unknown as Document;\n if (errors.length > 0) {\n throw new Error(`Failed to parse XML: ${errors.join('; ')}`);\n }\n return doc;\n}\n"],"mappings":"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","names":["feature","import_core","doc","actual","xsiType","coreComputeFragment","feature"]}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { EClass, EAttribute, EStructuralFeature, EObject, EPackage, ResourceFactory, URI, Resource, ResourceFactoryRegistry, EObjectSerializer } from '@typemf/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Own + inherited structural features/attributes, computed manually by
|
|
5
|
+
* walking getEStructuralFeatures() (own-only) up through getESuperTypes()
|
|
6
|
+
* (single-level, direct supertypes) - not eClass.getEAllStructuralFeatures()/
|
|
7
|
+
* getEAllAttributes() directly. Shared with xmi-serializer.ts, which has
|
|
8
|
+
* the full explanation in its own doc comment: those methods are empty
|
|
9
|
+
* on self-hosted, generated classifier metaclasses (one of the "~14
|
|
10
|
+
* traversal-based derived features" that are stored fields on generated
|
|
11
|
+
* classes, never auto-computed, since Pass 5/recomputeAllLists() is
|
|
12
|
+
* deliberately skipped for them - see the generator's own NOTES.md).
|
|
13
|
+
* getEStructuralFeatures() and getESuperTypes() are both real, populated
|
|
14
|
+
* fields regardless of whether the metamodel in use is self-hosted or
|
|
15
|
+
* hand-written, so this works correctly either way.
|
|
16
|
+
*/
|
|
17
|
+
declare function allStructuralFeaturesOf(eClass: EClass): EStructuralFeature[];
|
|
18
|
+
declare function allAttributesOf(eClass: EClass): EAttribute[];
|
|
19
|
+
/**
|
|
20
|
+
* EMF's actual default fragment grammar - deliberately a separate
|
|
21
|
+
* implementation from @typemf/core's computeFragment()/resolveFragment(),
|
|
22
|
+
* not a shared refactor of it, per the decision to keep @typemf/xmi
|
|
23
|
+
* self-contained. The underlying idea (walk containment, address by
|
|
24
|
+
* feature+index, prefer an ID attribute when present) is the same one;
|
|
25
|
+
* only the string grammar differs, to match what real EMF actually
|
|
26
|
+
* produces and expects:
|
|
27
|
+
*
|
|
28
|
+
* - single-root document, target is a descendant: "//@feature.index"
|
|
29
|
+
* (many-valued) or "//@feature" (single-valued), one such segment per
|
|
30
|
+
* containment step, joined by "/"
|
|
31
|
+
* - single-root document, target IS the root: "/"
|
|
32
|
+
* - multi-root document: same, but prefixed with "/<rootIndex>" instead
|
|
33
|
+
* of the bare "/"
|
|
34
|
+
* - an object whose EClass has an ID attribute set: the ID value itself,
|
|
35
|
+
* with no leading "/" at all (e.g. "myBookId123")
|
|
36
|
+
*
|
|
37
|
+
* Positional forms are fragile to reordering, same as @typemf/core's - an
|
|
38
|
+
* ID attribute is preferred whenever the target EClass has one.
|
|
39
|
+
*/
|
|
40
|
+
declare function computeEmfFragment(target: EObject, roots: EObject[]): string;
|
|
41
|
+
/** The inverse of computeEmfFragment(). */
|
|
42
|
+
declare function resolveEmfFragment(fragment: string, roots: EObject[]): EObject | undefined;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Collects the xmlns:prefix declarations a document needs while writing
|
|
46
|
+
* it, mutated as writeObject() walks the graph. Deliberately not shared
|
|
47
|
+
* with @typemf/json's NamespaceTable - a different package, a different
|
|
48
|
+
* attribute convention (xmlns:X here vs. a $namespaces JSON object there),
|
|
49
|
+
* and the two packages shouldn't depend on each other regardless.
|
|
50
|
+
*/
|
|
51
|
+
declare class NamespaceCollector {
|
|
52
|
+
private readonly prefixToUri;
|
|
53
|
+
private readonly uriToPrefix;
|
|
54
|
+
prefixFor(pkg: EPackage): string;
|
|
55
|
+
entries(): Array<[string, string]>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
declare class XmiResourceFactory implements ResourceFactory {
|
|
59
|
+
createResource(uri: URI): Resource;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Registers for both "xmi" and "ecore" - a .ecore file is itself an
|
|
63
|
+
* ordinary XMI instance document (its root just happens to be an
|
|
64
|
+
* EPackage), read by this exact same XmiSerializer with no separate
|
|
65
|
+
* parsing logic. Interpreting one correctly still requires a meta-schema
|
|
66
|
+
* (a real, registered EPackage describing what "EClass"/"EAttribute"/etc.
|
|
67
|
+
* mean) to already be in the EPackageRegistry before parsing - see
|
|
68
|
+
* @typemf/core's ecore/ for that piece, and NOTES.md for the full
|
|
69
|
+
* bootstrapping story.
|
|
70
|
+
*/
|
|
71
|
+
declare function registerXmiFormat(registry: ResourceFactoryRegistry): void;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Targets real EMF's actual default XMI conventions (verified against
|
|
75
|
+
* documented Java EMF behaviour, not assumed) so files this produces open
|
|
76
|
+
* in real Eclipse-based tooling and vice versa:
|
|
77
|
+
*
|
|
78
|
+
* - a reference (any cardinality) becomes an XML ATTRIBUTE when its
|
|
79
|
+
* target is in the SAME document, and a nested <feature href="..."/>
|
|
80
|
+
* CHILD ELEMENT when the target is in a DIFFERENT document - the
|
|
81
|
+
* dispatch is by document locality, not by containment or cardinality
|
|
82
|
+
* - fragments follow EMF's real default grammar: "//@books.2" (single
|
|
83
|
+
* root, positional), "/" (single root, target is the root itself),
|
|
84
|
+
* or the bare ID-attribute value when the EClass has one - see
|
|
85
|
+
* emf-fragment.ts
|
|
86
|
+
* - polymorphism on a cross-document reference: xsi:type on the href
|
|
87
|
+
* child element
|
|
88
|
+
* - polymorphism on a same-document reference: NOTES.md flags this as
|
|
89
|
+
* a best-effort reconstruction, not independently verified against a
|
|
90
|
+
* real Eclipse-produced file - see there before relying on it for
|
|
91
|
+
* interop specifically in the polymorphic + same-document + intra-doc
|
|
92
|
+
* case
|
|
93
|
+
*
|
|
94
|
+
* v1 scope: single-root documents only (see NOTES.md for the multi-root
|
|
95
|
+
* <xmi:XMI> wrapper form, not yet implemented). Many-valued EAttributes
|
|
96
|
+
* are written as repeated child elements - also flagged in NOTES.md as a
|
|
97
|
+
* judgment call, not verified against EMF's own default for that specific
|
|
98
|
+
* case.
|
|
99
|
+
*/
|
|
100
|
+
declare class XmiSerializer implements EObjectSerializer {
|
|
101
|
+
computeFragment(target: EObject, roots: EObject[]): string;
|
|
102
|
+
resolveFragment(fragment: string, roots: EObject[]): EObject | undefined;
|
|
103
|
+
serialize(roots: EObject[], resource: Resource): Promise<Uint8Array>;
|
|
104
|
+
/**
|
|
105
|
+
* Root-element only (not a full-tree walk), all declared xmlns:* values (not filtered to ones
|
|
106
|
+
* some xsi:type actually uses) - the simpler of the two real tradeoffs this method has for XMI,
|
|
107
|
+
* chosen deliberately: this project's own writer always declares every namespace a document
|
|
108
|
+
* needs on the root element (see NamespaceCollector/the ['xmlns:xmi', ...] block above), so this
|
|
109
|
+
* is exactly correct for anything this project produces. A third-party or hand-edited XMI file
|
|
110
|
+
* that declares a namespace deeper in the tree (legal XML, just non-standard practice for this
|
|
111
|
+
* kind of document) would have that namespace missed here - an accepted, documented limitation,
|
|
112
|
+
* not an oversight.
|
|
113
|
+
*/
|
|
114
|
+
peekReferencedNsURIs(content: Uint8Array): Promise<string[]>;
|
|
115
|
+
deserialize(content: Uint8Array, resource: Resource): Promise<EObject[]>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Parses `text` into a real DOM Document, using whichever DOMParser is
|
|
120
|
+
* actually available: the browser's native one (feature-detected, no
|
|
121
|
+
* import needed), or @xmldom/xmldom's, dynamically imported only when
|
|
122
|
+
* running in Node - this is the pattern designed back when @typemf/core's
|
|
123
|
+
* own ecore/ parser was first discussed, applied for real here since XMI
|
|
124
|
+
* needs genuine XML parsing (unlike @typemf/json, which only ever needed
|
|
125
|
+
* JSON.parse - universal in both environments, nothing to dispatch on).
|
|
126
|
+
*
|
|
127
|
+
* Deliberately does not attempt DTD/external-entity resolution in either
|
|
128
|
+
* path - @xmldom/xmldom does not fetch external resources at all, and
|
|
129
|
+
* nothing here changes that, which is the property that matters for
|
|
130
|
+
* loading a .xmi/.ecore file from a source you don't fully trust (a
|
|
131
|
+
* workspace file authored by someone else, a third-party plugin's model).
|
|
132
|
+
*/
|
|
133
|
+
declare function parseXmlDocument(text: string): Promise<Document>;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Writing XML is built as direct string generation rather than through a
|
|
137
|
+
* DOM Document, deliberately asymmetric with the parse side: constructing
|
|
138
|
+
* a spec-correct Document identically across native DOMParser and
|
|
139
|
+
* @xmldom/xmldom is real complexity (they don't expose identical
|
|
140
|
+
* `document.implementation.createDocument`/XMLSerializer behaviour), while
|
|
141
|
+
* generating well-formed XML text with correct escaping is simple enough
|
|
142
|
+
* to not need it. Reading genuinely needs a real parser (arbitrary,
|
|
143
|
+
* possibly untrusted input); writing only needs to escape values we
|
|
144
|
+
* already control.
|
|
145
|
+
*/
|
|
146
|
+
declare function escapeAttributeValue(value: string): string;
|
|
147
|
+
declare function escapeText(value: string): string;
|
|
148
|
+
|
|
149
|
+
export { NamespaceCollector, XmiResourceFactory, XmiSerializer, allAttributesOf, allStructuralFeaturesOf, computeEmfFragment, escapeAttributeValue, escapeText, parseXmlDocument, registerXmiFormat, resolveEmfFragment };
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { EClass, EAttribute, EStructuralFeature, EObject, EPackage, ResourceFactory, URI, Resource, ResourceFactoryRegistry, EObjectSerializer } from '@typemf/core';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Own + inherited structural features/attributes, computed manually by
|
|
5
|
+
* walking getEStructuralFeatures() (own-only) up through getESuperTypes()
|
|
6
|
+
* (single-level, direct supertypes) - not eClass.getEAllStructuralFeatures()/
|
|
7
|
+
* getEAllAttributes() directly. Shared with xmi-serializer.ts, which has
|
|
8
|
+
* the full explanation in its own doc comment: those methods are empty
|
|
9
|
+
* on self-hosted, generated classifier metaclasses (one of the "~14
|
|
10
|
+
* traversal-based derived features" that are stored fields on generated
|
|
11
|
+
* classes, never auto-computed, since Pass 5/recomputeAllLists() is
|
|
12
|
+
* deliberately skipped for them - see the generator's own NOTES.md).
|
|
13
|
+
* getEStructuralFeatures() and getESuperTypes() are both real, populated
|
|
14
|
+
* fields regardless of whether the metamodel in use is self-hosted or
|
|
15
|
+
* hand-written, so this works correctly either way.
|
|
16
|
+
*/
|
|
17
|
+
declare function allStructuralFeaturesOf(eClass: EClass): EStructuralFeature[];
|
|
18
|
+
declare function allAttributesOf(eClass: EClass): EAttribute[];
|
|
19
|
+
/**
|
|
20
|
+
* EMF's actual default fragment grammar - deliberately a separate
|
|
21
|
+
* implementation from @typemf/core's computeFragment()/resolveFragment(),
|
|
22
|
+
* not a shared refactor of it, per the decision to keep @typemf/xmi
|
|
23
|
+
* self-contained. The underlying idea (walk containment, address by
|
|
24
|
+
* feature+index, prefer an ID attribute when present) is the same one;
|
|
25
|
+
* only the string grammar differs, to match what real EMF actually
|
|
26
|
+
* produces and expects:
|
|
27
|
+
*
|
|
28
|
+
* - single-root document, target is a descendant: "//@feature.index"
|
|
29
|
+
* (many-valued) or "//@feature" (single-valued), one such segment per
|
|
30
|
+
* containment step, joined by "/"
|
|
31
|
+
* - single-root document, target IS the root: "/"
|
|
32
|
+
* - multi-root document: same, but prefixed with "/<rootIndex>" instead
|
|
33
|
+
* of the bare "/"
|
|
34
|
+
* - an object whose EClass has an ID attribute set: the ID value itself,
|
|
35
|
+
* with no leading "/" at all (e.g. "myBookId123")
|
|
36
|
+
*
|
|
37
|
+
* Positional forms are fragile to reordering, same as @typemf/core's - an
|
|
38
|
+
* ID attribute is preferred whenever the target EClass has one.
|
|
39
|
+
*/
|
|
40
|
+
declare function computeEmfFragment(target: EObject, roots: EObject[]): string;
|
|
41
|
+
/** The inverse of computeEmfFragment(). */
|
|
42
|
+
declare function resolveEmfFragment(fragment: string, roots: EObject[]): EObject | undefined;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Collects the xmlns:prefix declarations a document needs while writing
|
|
46
|
+
* it, mutated as writeObject() walks the graph. Deliberately not shared
|
|
47
|
+
* with @typemf/json's NamespaceTable - a different package, a different
|
|
48
|
+
* attribute convention (xmlns:X here vs. a $namespaces JSON object there),
|
|
49
|
+
* and the two packages shouldn't depend on each other regardless.
|
|
50
|
+
*/
|
|
51
|
+
declare class NamespaceCollector {
|
|
52
|
+
private readonly prefixToUri;
|
|
53
|
+
private readonly uriToPrefix;
|
|
54
|
+
prefixFor(pkg: EPackage): string;
|
|
55
|
+
entries(): Array<[string, string]>;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
declare class XmiResourceFactory implements ResourceFactory {
|
|
59
|
+
createResource(uri: URI): Resource;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Registers for both "xmi" and "ecore" - a .ecore file is itself an
|
|
63
|
+
* ordinary XMI instance document (its root just happens to be an
|
|
64
|
+
* EPackage), read by this exact same XmiSerializer with no separate
|
|
65
|
+
* parsing logic. Interpreting one correctly still requires a meta-schema
|
|
66
|
+
* (a real, registered EPackage describing what "EClass"/"EAttribute"/etc.
|
|
67
|
+
* mean) to already be in the EPackageRegistry before parsing - see
|
|
68
|
+
* @typemf/core's ecore/ for that piece, and NOTES.md for the full
|
|
69
|
+
* bootstrapping story.
|
|
70
|
+
*/
|
|
71
|
+
declare function registerXmiFormat(registry: ResourceFactoryRegistry): void;
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Targets real EMF's actual default XMI conventions (verified against
|
|
75
|
+
* documented Java EMF behaviour, not assumed) so files this produces open
|
|
76
|
+
* in real Eclipse-based tooling and vice versa:
|
|
77
|
+
*
|
|
78
|
+
* - a reference (any cardinality) becomes an XML ATTRIBUTE when its
|
|
79
|
+
* target is in the SAME document, and a nested <feature href="..."/>
|
|
80
|
+
* CHILD ELEMENT when the target is in a DIFFERENT document - the
|
|
81
|
+
* dispatch is by document locality, not by containment or cardinality
|
|
82
|
+
* - fragments follow EMF's real default grammar: "//@books.2" (single
|
|
83
|
+
* root, positional), "/" (single root, target is the root itself),
|
|
84
|
+
* or the bare ID-attribute value when the EClass has one - see
|
|
85
|
+
* emf-fragment.ts
|
|
86
|
+
* - polymorphism on a cross-document reference: xsi:type on the href
|
|
87
|
+
* child element
|
|
88
|
+
* - polymorphism on a same-document reference: NOTES.md flags this as
|
|
89
|
+
* a best-effort reconstruction, not independently verified against a
|
|
90
|
+
* real Eclipse-produced file - see there before relying on it for
|
|
91
|
+
* interop specifically in the polymorphic + same-document + intra-doc
|
|
92
|
+
* case
|
|
93
|
+
*
|
|
94
|
+
* v1 scope: single-root documents only (see NOTES.md for the multi-root
|
|
95
|
+
* <xmi:XMI> wrapper form, not yet implemented). Many-valued EAttributes
|
|
96
|
+
* are written as repeated child elements - also flagged in NOTES.md as a
|
|
97
|
+
* judgment call, not verified against EMF's own default for that specific
|
|
98
|
+
* case.
|
|
99
|
+
*/
|
|
100
|
+
declare class XmiSerializer implements EObjectSerializer {
|
|
101
|
+
computeFragment(target: EObject, roots: EObject[]): string;
|
|
102
|
+
resolveFragment(fragment: string, roots: EObject[]): EObject | undefined;
|
|
103
|
+
serialize(roots: EObject[], resource: Resource): Promise<Uint8Array>;
|
|
104
|
+
/**
|
|
105
|
+
* Root-element only (not a full-tree walk), all declared xmlns:* values (not filtered to ones
|
|
106
|
+
* some xsi:type actually uses) - the simpler of the two real tradeoffs this method has for XMI,
|
|
107
|
+
* chosen deliberately: this project's own writer always declares every namespace a document
|
|
108
|
+
* needs on the root element (see NamespaceCollector/the ['xmlns:xmi', ...] block above), so this
|
|
109
|
+
* is exactly correct for anything this project produces. A third-party or hand-edited XMI file
|
|
110
|
+
* that declares a namespace deeper in the tree (legal XML, just non-standard practice for this
|
|
111
|
+
* kind of document) would have that namespace missed here - an accepted, documented limitation,
|
|
112
|
+
* not an oversight.
|
|
113
|
+
*/
|
|
114
|
+
peekReferencedNsURIs(content: Uint8Array): Promise<string[]>;
|
|
115
|
+
deserialize(content: Uint8Array, resource: Resource): Promise<EObject[]>;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Parses `text` into a real DOM Document, using whichever DOMParser is
|
|
120
|
+
* actually available: the browser's native one (feature-detected, no
|
|
121
|
+
* import needed), or @xmldom/xmldom's, dynamically imported only when
|
|
122
|
+
* running in Node - this is the pattern designed back when @typemf/core's
|
|
123
|
+
* own ecore/ parser was first discussed, applied for real here since XMI
|
|
124
|
+
* needs genuine XML parsing (unlike @typemf/json, which only ever needed
|
|
125
|
+
* JSON.parse - universal in both environments, nothing to dispatch on).
|
|
126
|
+
*
|
|
127
|
+
* Deliberately does not attempt DTD/external-entity resolution in either
|
|
128
|
+
* path - @xmldom/xmldom does not fetch external resources at all, and
|
|
129
|
+
* nothing here changes that, which is the property that matters for
|
|
130
|
+
* loading a .xmi/.ecore file from a source you don't fully trust (a
|
|
131
|
+
* workspace file authored by someone else, a third-party plugin's model).
|
|
132
|
+
*/
|
|
133
|
+
declare function parseXmlDocument(text: string): Promise<Document>;
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Writing XML is built as direct string generation rather than through a
|
|
137
|
+
* DOM Document, deliberately asymmetric with the parse side: constructing
|
|
138
|
+
* a spec-correct Document identically across native DOMParser and
|
|
139
|
+
* @xmldom/xmldom is real complexity (they don't expose identical
|
|
140
|
+
* `document.implementation.createDocument`/XMLSerializer behaviour), while
|
|
141
|
+
* generating well-formed XML text with correct escaping is simple enough
|
|
142
|
+
* to not need it. Reading genuinely needs a real parser (arbitrary,
|
|
143
|
+
* possibly untrusted input); writing only needs to escape values we
|
|
144
|
+
* already control.
|
|
145
|
+
*/
|
|
146
|
+
declare function escapeAttributeValue(value: string): string;
|
|
147
|
+
declare function escapeText(value: string): string;
|
|
148
|
+
|
|
149
|
+
export { NamespaceCollector, XmiResourceFactory, XmiSerializer, allAttributesOf, allStructuralFeaturesOf, computeEmfFragment, escapeAttributeValue, escapeText, parseXmlDocument, registerXmiFormat, resolveEmfFragment };
|