@cda/adapter-xma 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +192 -0
- package/dist/diagnostics/diagnostics.d.ts +20 -0
- package/dist/diagnostics/diagnostics.d.ts.map +1 -0
- package/dist/diagnostics/diagnostics.js +19 -0
- package/dist/diagnostics/diagnostics.js.map +1 -0
- package/dist/diagnostics/errors.d.ts +13 -0
- package/dist/diagnostics/errors.d.ts.map +1 -0
- package/dist/diagnostics/errors.js +20 -0
- package/dist/diagnostics/errors.js.map +1 -0
- package/dist/geometry/bendpoints.d.ts +33 -0
- package/dist/geometry/bendpoints.d.ts.map +1 -0
- package/dist/geometry/bendpoints.js +44 -0
- package/dist/geometry/bendpoints.js.map +1 -0
- package/dist/geometry/geometry.d.ts +36 -0
- package/dist/geometry/geometry.d.ts.map +1 -0
- package/dist/geometry/geometry.js +41 -0
- package/dist/geometry/geometry.js.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure/id-allocator.d.ts +34 -0
- package/dist/infrastructure/id-allocator.d.ts.map +1 -0
- package/dist/infrastructure/id-allocator.js +54 -0
- package/dist/infrastructure/id-allocator.js.map +1 -0
- package/dist/infrastructure/xml-writer.d.ts +20 -0
- package/dist/infrastructure/xml-writer.d.ts.map +1 -0
- package/dist/infrastructure/xml-writer.js +54 -0
- package/dist/infrastructure/xml-writer.js.map +1 -0
- package/dist/mapping/element-mapping.d.ts +31 -0
- package/dist/mapping/element-mapping.d.ts.map +1 -0
- package/dist/mapping/element-mapping.js +112 -0
- package/dist/mapping/element-mapping.js.map +1 -0
- package/dist/mapping/relationship-mapping.d.ts +21 -0
- package/dist/mapping/relationship-mapping.d.ts.map +1 -0
- package/dist/mapping/relationship-mapping.js +45 -0
- package/dist/mapping/relationship-mapping.js.map +1 -0
- package/dist/mapping/scheme-mapping.d.ts +38 -0
- package/dist/mapping/scheme-mapping.d.ts.map +1 -0
- package/dist/mapping/scheme-mapping.js +39 -0
- package/dist/mapping/scheme-mapping.js.map +1 -0
- package/dist/mapping/visual-mapping.d.ts +24 -0
- package/dist/mapping/visual-mapping.d.ts.map +1 -0
- package/dist/mapping/visual-mapping.js +39 -0
- package/dist/mapping/visual-mapping.js.map +1 -0
- package/dist/serializer/document-writer.d.ts +19 -0
- package/dist/serializer/document-writer.d.ts.map +1 -0
- package/dist/serializer/document-writer.js +126 -0
- package/dist/serializer/document-writer.js.map +1 -0
- package/dist/serializer/graphical-writer.d.ts +25 -0
- package/dist/serializer/graphical-writer.d.ts.map +1 -0
- package/dist/serializer/graphical-writer.js +287 -0
- package/dist/serializer/graphical-writer.js.map +1 -0
- package/dist/serializer/profile-values.d.ts +8 -0
- package/dist/serializer/profile-values.d.ts.map +1 -0
- package/dist/serializer/profile-values.js +25 -0
- package/dist/serializer/profile-values.js.map +1 -0
- package/dist/serializer/relationship-writer.d.ts +15 -0
- package/dist/serializer/relationship-writer.d.ts.map +1 -0
- package/dist/serializer/relationship-writer.js +83 -0
- package/dist/serializer/relationship-writer.js.map +1 -0
- package/dist/serializer/rtf.d.ts +9 -0
- package/dist/serializer/rtf.d.ts.map +1 -0
- package/dist/serializer/rtf.js +50 -0
- package/dist/serializer/rtf.js.map +1 -0
- package/dist/serializer/semantic-writer.d.ts +36 -0
- package/dist/serializer/semantic-writer.d.ts.map +1 -0
- package/dist/serializer/semantic-writer.js +91 -0
- package/dist/serializer/semantic-writer.js.map +1 -0
- package/dist/serializer/serialize-xma.d.ts +29 -0
- package/dist/serializer/serialize-xma.d.ts.map +1 -0
- package/dist/serializer/serialize-xma.js +80 -0
- package/dist/serializer/serialize-xma.js.map +1 -0
- package/dist/serializer/view-writer.d.ts +26 -0
- package/dist/serializer/view-writer.d.ts.map +1 -0
- package/dist/serializer/view-writer.js +147 -0
- package/dist/serializer/view-writer.js.map +1 -0
- package/package.json +58 -0
package/README.md
ADDED
|
@@ -0,0 +1,192 @@
|
|
|
1
|
+
# @cda/adapter-xma
|
|
2
|
+
|
|
3
|
+
An XMA output adapter for the [Continuous-Driven Architecture (CDA)](https://github.com/Continuous-DrivenArchitecture) ecosystem.
|
|
4
|
+
|
|
5
|
+
`@cda/adapter-xma` converts the typed `ArchiModel` produced by
|
|
6
|
+
[`@cda/archi-semantic-core`](https://github.com/Continuous-DrivenArchitecture/archi-semantic-core)
|
|
7
|
+
into an XMA document, preserving both **semantics** (elements, relationships,
|
|
8
|
+
names, documentation) and **presentation intent** (views, node geometry,
|
|
9
|
+
default visual styling, graphical relationships, bendpoints, Notes, Groups).
|
|
10
|
+
|
|
11
|
+
```
|
|
12
|
+
.archimate
|
|
13
|
+
│
|
|
14
|
+
▼
|
|
15
|
+
@cda/archi-semantic-core (parseArchiModel)
|
|
16
|
+
│
|
|
17
|
+
▼
|
|
18
|
+
ArchiModel
|
|
19
|
+
│
|
|
20
|
+
▼
|
|
21
|
+
@cda/adapter-xma (serializeXma)
|
|
22
|
+
│
|
|
23
|
+
▼
|
|
24
|
+
.xma
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This package is a **pure output-format adapter**. It does not re-parse
|
|
28
|
+
`.archimate` XML, does not duplicate `archi-semantic-core`'s parsing logic,
|
|
29
|
+
and does not use the ArchiMate Open Exchange format as an intermediate
|
|
30
|
+
representation (doing so would lose native presentation fidelity). It
|
|
31
|
+
consumes `archi-semantic-core`'s public `ArchiModel` contract only.
|
|
32
|
+
|
|
33
|
+
The runtime is dependency-light and avoids Node-only APIs (`fs`, `path`,
|
|
34
|
+
`process`), so it can run in a browser — e.g. a future client-side CDA
|
|
35
|
+
converter.
|
|
36
|
+
|
|
37
|
+
## Installation
|
|
38
|
+
|
|
39
|
+
```
|
|
40
|
+
npm install @cda/archi-semantic-core @cda/adapter-xma
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Usage
|
|
44
|
+
|
|
45
|
+
```ts
|
|
46
|
+
import { parseArchiModel } from '@cda/archi-semantic-core';
|
|
47
|
+
import { serializeXma } from '@cda/adapter-xma';
|
|
48
|
+
|
|
49
|
+
const model = parseArchiModel(archimateXml);
|
|
50
|
+
|
|
51
|
+
const xma = serializeXma(model, {
|
|
52
|
+
language: 'en',
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
`serializeXma` is **strict by default**: if the model contains a construct
|
|
57
|
+
that would cause semantic or presentation loss (an unmapped element type, an
|
|
58
|
+
unconfirmed relationship, more than one view, incomplete geometry, ...) it
|
|
59
|
+
throws `XmaSerializationError` carrying a structured diagnostic for every
|
|
60
|
+
problem found, rather than silently dropping content or fabricating an
|
|
61
|
+
approximate result.
|
|
62
|
+
|
|
63
|
+
To preview what is and isn't supported without throwing:
|
|
64
|
+
|
|
65
|
+
```ts
|
|
66
|
+
import { inspectXmaSupport } from '@cda/adapter-xma';
|
|
67
|
+
|
|
68
|
+
const diagnostics = inspectXmaSupport(model);
|
|
69
|
+
for (const d of diagnostics) {
|
|
70
|
+
console.log(`[${d.severity}] ${d.code}: ${d.message}`);
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### Options
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
interface XmaSerializeOptions {
|
|
78
|
+
/** Applied to every xml:lang and the default MM_Language. Default: 'en'. Never inferred from element names. */
|
|
79
|
+
language?: string;
|
|
80
|
+
/** The root MM_ModelPackage's display name. Default: a neutral placeholder — no machine/user identity is ever fabricated. */
|
|
81
|
+
packageName?: string;
|
|
82
|
+
/** The model's display name. Default: `model.metadata.name`. */
|
|
83
|
+
modelName?: string;
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Support matrix
|
|
88
|
+
|
|
89
|
+
This is **initial, evidence-backed XMA serialization support** — not a
|
|
90
|
+
complete, universal XMA converter. Everything below was confirmed against
|
|
91
|
+
two reference `.archimate`/`.xma` fixture pairs (see
|
|
92
|
+
[`tests/fixtures/README.md`](tests/fixtures/README.md)); nothing here is
|
|
93
|
+
extrapolated beyond that evidence.
|
|
94
|
+
|
|
95
|
+
### Supported
|
|
96
|
+
|
|
97
|
+
- All 60 confirmed ArchiMate element type mappings (Strategy, Business,
|
|
98
|
+
Application, Technology, Physical, Motivation, Implementation & Migration,
|
|
99
|
+
Composite), with confirmed scheme/collection placement and default icon
|
|
100
|
+
decoration presence.
|
|
101
|
+
- Element and view names, and documentation (as RTF profile values).
|
|
102
|
+
- A single ArchiMate view per model: node geometry (exact ×3 scale),
|
|
103
|
+
default per-category fill colors, line color, opacity, and font.
|
|
104
|
+
- Notes and Groups (as `ArchiMate:ViewGraphic`), including Group
|
|
105
|
+
documentation.
|
|
106
|
+
- Three confirmed semantic relationship mappings and their graphical
|
|
107
|
+
representation:
|
|
108
|
+
- `AssignmentRelationship` `BusinessActor` → `BusinessProcess`
|
|
109
|
+
- `ServingRelationship` `ApplicationService` → `BusinessProcess` (XMA
|
|
110
|
+
calls this `...Use`, not `...Serving`)
|
|
111
|
+
- `FlowRelationship` `BusinessProcess` → `BusinessProcess`
|
|
112
|
+
- Manually routed connection bendpoints (source/target-relative offsets,
|
|
113
|
+
cross-checked against each other).
|
|
114
|
+
- A configurable output language, applied consistently — never inferred
|
|
115
|
+
from element names.
|
|
116
|
+
- Direct, lossless mapping of explicit Archi `fillColor`/`lineColor`
|
|
117
|
+
(hex → RGB) and `fontName` overrides, where the source field maps onto
|
|
118
|
+
the exact same structural XMA slot.
|
|
119
|
+
|
|
120
|
+
### Not yet guaranteed
|
|
121
|
+
|
|
122
|
+
- Relationship type/source/target combinations beyond the three confirmed
|
|
123
|
+
above (reported as a diagnostic, not silently dropped or guessed).
|
|
124
|
+
- More than one view per model (reported as a diagnostic).
|
|
125
|
+
- Nested diagram objects, `DiagramModelReference` ("insert view as
|
|
126
|
+
reference"), and purely visual (non-semantic) connections.
|
|
127
|
+
- Junctions, profiles/specializations, and arbitrary model properties.
|
|
128
|
+
- Explicit font size, bold/italic, line width, font color, or connector
|
|
129
|
+
line color overrides (reported as diagnostics; the confirmed defaults are
|
|
130
|
+
used instead).
|
|
131
|
+
- Manual connector anchor metadata (`mm_fromx`/`mm_fromy`/`mm_tox`/`mm_toy`)
|
|
132
|
+
— only one fixture exhibited these attributes, not enough evidence to
|
|
133
|
+
derive a general formula or confirm they're required for import. They are
|
|
134
|
+
deliberately omitted rather than guessed; see
|
|
135
|
+
`src/serializer/graphical-writer.ts`.
|
|
136
|
+
|
|
137
|
+
## Architecture
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
src/
|
|
141
|
+
index.ts public API
|
|
142
|
+
|
|
143
|
+
serializer/ XmlElement tree construction
|
|
144
|
+
serialize-xma.ts orchestrator: plan -> diagnostics/throw -> render
|
|
145
|
+
document-writer.ts MM_Document skeleton, scheme/folder nesting
|
|
146
|
+
semantic-writer.ts per-scheme element collections
|
|
147
|
+
relationship-writer.ts semantic relationship elements
|
|
148
|
+
view-writer.ts AllView: ViewGraphics + RefObjects, validation
|
|
149
|
+
graphical-writer.ts Canvas / MM_Node / MM_DirectedRel
|
|
150
|
+
profile-values.ts MM_ProfileValues (nm/doc) builder
|
|
151
|
+
rtf.ts dependency-free RTF escaping
|
|
152
|
+
|
|
153
|
+
mapping/ immutable mapping DATA (never switch statements)
|
|
154
|
+
element-mapping.ts 60 confirmed Archi type -> XMA type/scheme/collection
|
|
155
|
+
relationship-mapping.ts 3 confirmed (type, source, target) -> XMA relationship
|
|
156
|
+
scheme-mapping.ts scheme container / folder-nesting metadata
|
|
157
|
+
visual-mapping.ts default fill/line colors, font, opacity
|
|
158
|
+
|
|
159
|
+
geometry/
|
|
160
|
+
geometry.ts the ×3 scale rule
|
|
161
|
+
bendpoints.ts bendpoint offset -> absolute point resolution
|
|
162
|
+
|
|
163
|
+
infrastructure/
|
|
164
|
+
id-allocator.ts deterministic numeric XMA id allocation
|
|
165
|
+
xml-writer.ts typed XmlElement tree -> deterministic XML string
|
|
166
|
+
|
|
167
|
+
diagnostics/
|
|
168
|
+
diagnostics.ts XmaDiagnostic, DiagnosticCollector
|
|
169
|
+
errors.ts XmaSerializationError
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
Every generated XMA id is a deterministic, strictly-increasing integer
|
|
173
|
+
driven by traversal order over the (order-preserving) `ArchiModel` — never
|
|
174
|
+
`Math.random()`, `Date.now()`, or a UUID. Archi's own string ids are never
|
|
175
|
+
reused as XMA ids.
|
|
176
|
+
|
|
177
|
+
Output is fully deterministic: the same `ArchiModel` and options always
|
|
178
|
+
produce byte-identical XML (no timestamps, host names, or environment
|
|
179
|
+
values are ever embedded).
|
|
180
|
+
|
|
181
|
+
## Development
|
|
182
|
+
|
|
183
|
+
```
|
|
184
|
+
npm install
|
|
185
|
+
npm run typecheck
|
|
186
|
+
npm run build
|
|
187
|
+
npm test
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## License
|
|
191
|
+
|
|
192
|
+
MIT
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
export type XmaDiagnosticSeverity = 'error' | 'warning';
|
|
2
|
+
export interface XmaDiagnostic {
|
|
3
|
+
/** Stable machine-readable code, e.g. "unsupported-element-type". */
|
|
4
|
+
code: string;
|
|
5
|
+
severity: XmaDiagnosticSeverity;
|
|
6
|
+
message: string;
|
|
7
|
+
/** Id of the offending source entity (Archi element/relationship/view/diagram id), when applicable. */
|
|
8
|
+
entityId?: string;
|
|
9
|
+
/** Kind of the offending source entity, e.g. "ArchiElement", "ArchiRelationship". */
|
|
10
|
+
entityType?: string;
|
|
11
|
+
}
|
|
12
|
+
/** Accumulates diagnostics during a single serialization pass. */
|
|
13
|
+
export declare class DiagnosticCollector {
|
|
14
|
+
private readonly items;
|
|
15
|
+
error(diagnostic: Omit<XmaDiagnostic, 'severity'>): void;
|
|
16
|
+
warning(diagnostic: Omit<XmaDiagnostic, 'severity'>): void;
|
|
17
|
+
get all(): XmaDiagnostic[];
|
|
18
|
+
get hasErrors(): boolean;
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=diagnostics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.d.ts","sourceRoot":"","sources":["../../src/diagnostics/diagnostics.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,qBAAqB,GAAG,OAAO,GAAG,SAAS,CAAC;AAExD,MAAM,WAAW,aAAa;IAC5B,qEAAqE;IACrE,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,qBAAqB,CAAC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,uGAAuG;IACvG,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,kEAAkE;AAClE,qBAAa,mBAAmB;IAC9B,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAuB;IAE7C,KAAK,CAAC,UAAU,EAAE,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,GAAG,IAAI;IAIxD,OAAO,CAAC,UAAU,EAAE,IAAI,CAAC,aAAa,EAAE,UAAU,CAAC,GAAG,IAAI;IAI1D,IAAI,GAAG,IAAI,aAAa,EAAE,CAEzB;IAED,IAAI,SAAS,IAAI,OAAO,CAEvB;CACF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/** Accumulates diagnostics during a single serialization pass. */
|
|
2
|
+
export class DiagnosticCollector {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.items = [];
|
|
5
|
+
}
|
|
6
|
+
error(diagnostic) {
|
|
7
|
+
this.items.push({ ...diagnostic, severity: 'error' });
|
|
8
|
+
}
|
|
9
|
+
warning(diagnostic) {
|
|
10
|
+
this.items.push({ ...diagnostic, severity: 'warning' });
|
|
11
|
+
}
|
|
12
|
+
get all() {
|
|
13
|
+
return [...this.items];
|
|
14
|
+
}
|
|
15
|
+
get hasErrors() {
|
|
16
|
+
return this.items.some((d) => d.severity === 'error');
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=diagnostics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"diagnostics.js","sourceRoot":"","sources":["../../src/diagnostics/diagnostics.ts"],"names":[],"mappings":"AAaA,kEAAkE;AAClE,MAAM,OAAO,mBAAmB;IAAhC;QACmB,UAAK,GAAoB,EAAE,CAAC;IAiB/C,CAAC;IAfC,KAAK,CAAC,UAA2C;QAC/C,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,UAAU,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,OAAO,CAAC,UAA2C;QACjD,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,GAAG,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,GAAG;QACL,OAAO,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC;IACzB,CAAC;IAED,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC;IACxD,CAAC;CACF"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { XmaDiagnostic } from './diagnostics.js';
|
|
2
|
+
/**
|
|
3
|
+
* Thrown by `serializeXma` whenever serializing would silently lose
|
|
4
|
+
* semantics or presentation intent. Carries the full structured diagnostic
|
|
5
|
+
* list (not just the first failure) so callers can report everything wrong
|
|
6
|
+
* in one pass — see `inspectXmaSupport` to obtain the same list without
|
|
7
|
+
* throwing.
|
|
8
|
+
*/
|
|
9
|
+
export declare class XmaSerializationError extends Error {
|
|
10
|
+
readonly diagnostics: XmaDiagnostic[];
|
|
11
|
+
constructor(diagnostics: XmaDiagnostic[]);
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../../src/diagnostics/errors.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEtD;;;;;;GAMG;AACH,qBAAa,qBAAsB,SAAQ,KAAK;IAC9C,QAAQ,CAAC,WAAW,EAAE,aAAa,EAAE,CAAC;gBAE1B,WAAW,EAAE,aAAa,EAAE;CAYzC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Thrown by `serializeXma` whenever serializing would silently lose
|
|
3
|
+
* semantics or presentation intent. Carries the full structured diagnostic
|
|
4
|
+
* list (not just the first failure) so callers can report everything wrong
|
|
5
|
+
* in one pass — see `inspectXmaSupport` to obtain the same list without
|
|
6
|
+
* throwing.
|
|
7
|
+
*/
|
|
8
|
+
export class XmaSerializationError extends Error {
|
|
9
|
+
constructor(diagnostics) {
|
|
10
|
+
const errorCount = diagnostics.filter((d) => d.severity === 'error').length;
|
|
11
|
+
super(`XMA serialization failed: ${errorCount} unsupported construct(s) would cause semantic or presentation loss. ` +
|
|
12
|
+
diagnostics
|
|
13
|
+
.filter((d) => d.severity === 'error')
|
|
14
|
+
.map((d) => `[${d.code}] ${d.message}`)
|
|
15
|
+
.join('; '));
|
|
16
|
+
this.name = 'XmaSerializationError';
|
|
17
|
+
this.diagnostics = diagnostics;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/diagnostics/errors.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,OAAO,qBAAsB,SAAQ,KAAK;IAG9C,YAAY,WAA4B;QACtC,MAAM,UAAU,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;QAC5E,KAAK,CACH,6BAA6B,UAAU,uEAAuE;YAC5G,WAAW;iBACR,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC;iBACrC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,CAAC,CAAC,OAAO,EAAE,CAAC;iBACtC,IAAI,CAAC,IAAI,CAAC,CAChB,CAAC;QACF,IAAI,CAAC,IAAI,GAAG,uBAAuB,CAAC;QACpC,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;CACF"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import type { Point, Rect } from './geometry.js';
|
|
2
|
+
export type BendpointAgreement = 'both' | 'source-only' | 'target-only';
|
|
3
|
+
export interface BendpointResolution {
|
|
4
|
+
/** Resolved absolute point, in Archi (pre-scale) coordinate space. */
|
|
5
|
+
point: Point;
|
|
6
|
+
agreement: BendpointAgreement;
|
|
7
|
+
/** Present only when both source- and target-relative forms were available but disagreed materially. */
|
|
8
|
+
mismatch?: {
|
|
9
|
+
fromSource: Point;
|
|
10
|
+
fromTarget: Point;
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Resolves a single Archi bendpoint (stored as offsets from the source and
|
|
15
|
+
* target diagram-object centers) to an absolute point in Archi coordinate
|
|
16
|
+
* space.
|
|
17
|
+
*
|
|
18
|
+
* Confirmed fixture: source bounds x=48,y=180,w=120,h=55 (center 108,207),
|
|
19
|
+
* target bounds x=204,y=276,w=120,h=55 (center 264,303), bendpoint
|
|
20
|
+
* startX=-12,startY=93,endX=-168,endY=-3 resolves to absolute (96, 300) via
|
|
21
|
+
* either offset.
|
|
22
|
+
*
|
|
23
|
+
* When both offsets are present, they are cross-checked against each other;
|
|
24
|
+
* a material disagreement is reported via `mismatch` rather than silently
|
|
25
|
+
* resolved, so the caller can surface a diagnostic instead of guessing.
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveBendpoint(bendpoint: {
|
|
28
|
+
startX: number | null;
|
|
29
|
+
startY: number | null;
|
|
30
|
+
endX: number | null;
|
|
31
|
+
endY: number | null;
|
|
32
|
+
}, sourceBounds: Rect, targetBounds: Rect): BendpointResolution | null;
|
|
33
|
+
//# sourceMappingURL=bendpoints.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bendpoints.d.ts","sourceRoot":"","sources":["../../src/geometry/bendpoints.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAC;AAKjD,MAAM,MAAM,kBAAkB,GAAG,MAAM,GAAG,aAAa,GAAG,aAAa,CAAC;AAExE,MAAM,WAAW,mBAAmB;IAClC,sEAAsE;IACtE,KAAK,EAAE,KAAK,CAAC;IACb,SAAS,EAAE,kBAAkB,CAAC;IAC9B,wGAAwG;IACxG,QAAQ,CAAC,EAAE;QACT,UAAU,EAAE,KAAK,CAAC;QAClB,UAAU,EAAE,KAAK,CAAC;KACnB,CAAC;CACH;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE;IAAE,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,EACrG,YAAY,EAAE,IAAI,EAClB,YAAY,EAAE,IAAI,GACjB,mBAAmB,GAAG,IAAI,CAgC5B"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { centerOf } from './geometry.js';
|
|
2
|
+
const AGREEMENT_EPSILON = 0.01;
|
|
3
|
+
/**
|
|
4
|
+
* Resolves a single Archi bendpoint (stored as offsets from the source and
|
|
5
|
+
* target diagram-object centers) to an absolute point in Archi coordinate
|
|
6
|
+
* space.
|
|
7
|
+
*
|
|
8
|
+
* Confirmed fixture: source bounds x=48,y=180,w=120,h=55 (center 108,207),
|
|
9
|
+
* target bounds x=204,y=276,w=120,h=55 (center 264,303), bendpoint
|
|
10
|
+
* startX=-12,startY=93,endX=-168,endY=-3 resolves to absolute (96, 300) via
|
|
11
|
+
* either offset.
|
|
12
|
+
*
|
|
13
|
+
* When both offsets are present, they are cross-checked against each other;
|
|
14
|
+
* a material disagreement is reported via `mismatch` rather than silently
|
|
15
|
+
* resolved, so the caller can surface a diagnostic instead of guessing.
|
|
16
|
+
*/
|
|
17
|
+
export function resolveBendpoint(bendpoint, sourceBounds, targetBounds) {
|
|
18
|
+
const hasSourceOffset = bendpoint.startX !== null && bendpoint.startY !== null;
|
|
19
|
+
const hasTargetOffset = bendpoint.endX !== null && bendpoint.endY !== null;
|
|
20
|
+
if (!hasSourceOffset && !hasTargetOffset) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
const sourceCenter = centerOf(sourceBounds);
|
|
24
|
+
const targetCenter = centerOf(targetBounds);
|
|
25
|
+
const fromSource = hasSourceOffset
|
|
26
|
+
? { x: sourceCenter.x + bendpoint.startX, y: sourceCenter.y + bendpoint.startY }
|
|
27
|
+
: null;
|
|
28
|
+
const fromTarget = hasTargetOffset
|
|
29
|
+
? { x: targetCenter.x + bendpoint.endX, y: targetCenter.y + bendpoint.endY }
|
|
30
|
+
: null;
|
|
31
|
+
if (fromSource && fromTarget) {
|
|
32
|
+
const deltaX = Math.abs(fromSource.x - fromTarget.x);
|
|
33
|
+
const deltaY = Math.abs(fromSource.y - fromTarget.y);
|
|
34
|
+
if (deltaX > AGREEMENT_EPSILON || deltaY > AGREEMENT_EPSILON) {
|
|
35
|
+
return { point: fromSource, agreement: 'both', mismatch: { fromSource, fromTarget } };
|
|
36
|
+
}
|
|
37
|
+
return { point: fromSource, agreement: 'both' };
|
|
38
|
+
}
|
|
39
|
+
if (fromSource) {
|
|
40
|
+
return { point: fromSource, agreement: 'source-only' };
|
|
41
|
+
}
|
|
42
|
+
return { point: fromTarget, agreement: 'target-only' };
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=bendpoints.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bendpoints.js","sourceRoot":"","sources":["../../src/geometry/bendpoints.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAEzC,MAAM,iBAAiB,GAAG,IAAI,CAAC;AAe/B;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,gBAAgB,CAC9B,SAAqG,EACrG,YAAkB,EAClB,YAAkB;IAElB,MAAM,eAAe,GAAG,SAAS,CAAC,MAAM,KAAK,IAAI,IAAI,SAAS,CAAC,MAAM,KAAK,IAAI,CAAC;IAC/E,MAAM,eAAe,GAAG,SAAS,CAAC,IAAI,KAAK,IAAI,IAAI,SAAS,CAAC,IAAI,KAAK,IAAI,CAAC;IAE3E,IAAI,CAAC,eAAe,IAAI,CAAC,eAAe,EAAE,CAAC;QACzC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,MAAM,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC;IAC5C,MAAM,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC,CAAC;IAE5C,MAAM,UAAU,GAAiB,eAAe;QAC9C,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC,GAAI,SAAS,CAAC,MAAiB,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC,GAAI,SAAS,CAAC,MAAiB,EAAE;QACxG,CAAC,CAAC,IAAI,CAAC;IACT,MAAM,UAAU,GAAiB,eAAe;QAC9C,CAAC,CAAC,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC,GAAI,SAAS,CAAC,IAAe,EAAE,CAAC,EAAE,YAAY,CAAC,CAAC,GAAI,SAAS,CAAC,IAAe,EAAE;QACpG,CAAC,CAAC,IAAI,CAAC;IAET,IAAI,UAAU,IAAI,UAAU,EAAE,CAAC;QAC7B,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QACrD,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QACrD,IAAI,MAAM,GAAG,iBAAiB,IAAI,MAAM,GAAG,iBAAiB,EAAE,CAAC;YAC7D,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,UAAU,EAAE,UAAU,EAAE,EAAE,CAAC;QACxF,CAAC;QACD,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAClD,CAAC;IAED,IAAI,UAAU,EAAE,CAAC;QACf,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;IACzD,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,UAAmB,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC;AAClE,CAAC"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confirmed across all catalogue shapes and the relationship fixture: XMA
|
|
3
|
+
* geometry is Archi geometry uniformly scaled by 3 (x, y, width, height,
|
|
4
|
+
* and bendpoint coordinates alike).
|
|
5
|
+
*/
|
|
6
|
+
export declare const XMA_SCALE = 3;
|
|
7
|
+
export interface Rect {
|
|
8
|
+
x: number;
|
|
9
|
+
y: number;
|
|
10
|
+
width: number;
|
|
11
|
+
height: number;
|
|
12
|
+
}
|
|
13
|
+
export declare function scaleValue(archiValue: number): number;
|
|
14
|
+
export declare function scaleRect(bounds: Rect): Rect;
|
|
15
|
+
export interface Point {
|
|
16
|
+
x: number;
|
|
17
|
+
y: number;
|
|
18
|
+
}
|
|
19
|
+
export declare function scalePoint(point: Point): Point;
|
|
20
|
+
/**
|
|
21
|
+
* Rect center, floored to a whole pixel — confirmed against the proven
|
|
22
|
+
* bendpoint fixture: a 55px-tall shape's center is Archi's own `180 + 55/2`
|
|
23
|
+
* truncated to `207`, not `207.5`. Using the exact fraction here would
|
|
24
|
+
* throw off every bendpoint resolved against an odd-height/width shape.
|
|
25
|
+
*/
|
|
26
|
+
export declare function centerOf(bounds: Rect): Point;
|
|
27
|
+
interface NullableBounds {
|
|
28
|
+
x: number | null;
|
|
29
|
+
y: number | null;
|
|
30
|
+
width: number | null;
|
|
31
|
+
height: number | null;
|
|
32
|
+
}
|
|
33
|
+
/** Type guard: true when every bounds field is set, narrowing to a usable `Rect`. */
|
|
34
|
+
export declare function hasCompleteBounds(bounds: NullableBounds | null): bounds is Rect;
|
|
35
|
+
export {};
|
|
36
|
+
//# sourceMappingURL=geometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../../src/geometry/geometry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,eAAO,MAAM,SAAS,IAAI,CAAC;AAE3B,MAAM,WAAW,IAAI;IACnB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAgB,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAErD;AAED,wBAAgB,SAAS,CAAC,MAAM,EAAE,IAAI,GAAG,IAAI,CAO5C;AAED,MAAM,WAAW,KAAK;IACpB,CAAC,EAAE,MAAM,CAAC;IACV,CAAC,EAAE,MAAM,CAAC;CACX;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,CAE9C;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,MAAM,EAAE,IAAI,GAAG,KAAK,CAK5C;AAED,UAAU,cAAc;IACtB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;CACvB;AAED,qFAAqF;AACrF,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,GAAG,MAAM,IAAI,IAAI,CAQ/E"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Confirmed across all catalogue shapes and the relationship fixture: XMA
|
|
3
|
+
* geometry is Archi geometry uniformly scaled by 3 (x, y, width, height,
|
|
4
|
+
* and bendpoint coordinates alike).
|
|
5
|
+
*/
|
|
6
|
+
export const XMA_SCALE = 3;
|
|
7
|
+
export function scaleValue(archiValue) {
|
|
8
|
+
return archiValue * XMA_SCALE;
|
|
9
|
+
}
|
|
10
|
+
export function scaleRect(bounds) {
|
|
11
|
+
return {
|
|
12
|
+
x: scaleValue(bounds.x),
|
|
13
|
+
y: scaleValue(bounds.y),
|
|
14
|
+
width: scaleValue(bounds.width),
|
|
15
|
+
height: scaleValue(bounds.height),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
export function scalePoint(point) {
|
|
19
|
+
return { x: scaleValue(point.x), y: scaleValue(point.y) };
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Rect center, floored to a whole pixel — confirmed against the proven
|
|
23
|
+
* bendpoint fixture: a 55px-tall shape's center is Archi's own `180 + 55/2`
|
|
24
|
+
* truncated to `207`, not `207.5`. Using the exact fraction here would
|
|
25
|
+
* throw off every bendpoint resolved against an odd-height/width shape.
|
|
26
|
+
*/
|
|
27
|
+
export function centerOf(bounds) {
|
|
28
|
+
return {
|
|
29
|
+
x: Math.floor(bounds.x + bounds.width / 2),
|
|
30
|
+
y: Math.floor(bounds.y + bounds.height / 2),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
/** Type guard: true when every bounds field is set, narrowing to a usable `Rect`. */
|
|
34
|
+
export function hasCompleteBounds(bounds) {
|
|
35
|
+
return (bounds !== null &&
|
|
36
|
+
bounds.x !== null &&
|
|
37
|
+
bounds.y !== null &&
|
|
38
|
+
bounds.width !== null &&
|
|
39
|
+
bounds.height !== null);
|
|
40
|
+
}
|
|
41
|
+
//# sourceMappingURL=geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.js","sourceRoot":"","sources":["../../src/geometry/geometry.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,MAAM,CAAC,MAAM,SAAS,GAAG,CAAC,CAAC;AAS3B,MAAM,UAAU,UAAU,CAAC,UAAkB;IAC3C,OAAO,UAAU,GAAG,SAAS,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,MAAY;IACpC,OAAO;QACL,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QACvB,CAAC,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;QACvB,KAAK,EAAE,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC;QAC/B,MAAM,EAAE,UAAU,CAAC,MAAM,CAAC,MAAM,CAAC;KAClC,CAAC;AACJ,CAAC;AAOD,MAAM,UAAU,UAAU,CAAC,KAAY;IACrC,OAAO,EAAE,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,UAAU,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;AAC5D,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,QAAQ,CAAC,MAAY;IACnC,OAAO;QACL,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,KAAK,GAAG,CAAC,CAAC;QAC1C,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;KAC5C,CAAC;AACJ,CAAC;AASD,qFAAqF;AACrF,MAAM,UAAU,iBAAiB,CAAC,MAA6B;IAC7D,OAAO,CACL,MAAM,KAAK,IAAI;QACf,MAAM,CAAC,CAAC,KAAK,IAAI;QACjB,MAAM,CAAC,CAAC,KAAK,IAAI;QACjB,MAAM,CAAC,KAAK,KAAK,IAAI;QACrB,MAAM,CAAC,MAAM,KAAK,IAAI,CACvB,CAAC;AACJ,CAAC"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { serializeXma, inspectXmaSupport } from './serializer/serialize-xma.js';
|
|
2
|
+
export type { XmaSerializeOptions } from './serializer/serialize-xma.js';
|
|
3
|
+
export { XmaSerializationError } from './diagnostics/errors.js';
|
|
4
|
+
export type { XmaDiagnostic, XmaDiagnosticSeverity } from './diagnostics/diagnostics.js';
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAChF,YAAY,EAAE,mBAAmB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAChE,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,8BAA8B,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,iBAAiB,EAAE,MAAM,+BAA+B,CAAC;AAEhF,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic numeric XMA id allocator.
|
|
3
|
+
*
|
|
4
|
+
* XMA reference documents use small sequential integer ids. We do not (and
|
|
5
|
+
* cannot) reproduce Archi's string ids — instead we hand out ids in strictly
|
|
6
|
+
* increasing order, starting from 1, driven entirely by serializer traversal
|
|
7
|
+
* order. As long as the traversal order over a given `ArchiModel` is stable
|
|
8
|
+
* (it is: every `ArchiModel` collection preserves source document order),
|
|
9
|
+
* the same input always yields the same ids.
|
|
10
|
+
*
|
|
11
|
+
* Never uses Math.random(), Date.now(), or any other non-deterministic or
|
|
12
|
+
* environment-specific source.
|
|
13
|
+
*/
|
|
14
|
+
export declare class XmaIdAllocator {
|
|
15
|
+
private counter;
|
|
16
|
+
next(): number;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A registry mapping arbitrary string keys (Archi ids, or synthetic
|
|
20
|
+
* composite keys) to allocated XMA numeric ids, with reference-consistent
|
|
21
|
+
* reuse: asking for the same key twice returns the same id.
|
|
22
|
+
*/
|
|
23
|
+
export declare class XmaIdRegistry {
|
|
24
|
+
private readonly allocator;
|
|
25
|
+
private readonly ids;
|
|
26
|
+
constructor(allocator: XmaIdAllocator);
|
|
27
|
+
/** Allocates (or returns the previously allocated) id for `key`. */
|
|
28
|
+
idFor(key: string): number;
|
|
29
|
+
/** Allocates a fresh id not tied to any lookup key (e.g. structural scaffolding elements). */
|
|
30
|
+
fresh(): number;
|
|
31
|
+
has(key: string): boolean;
|
|
32
|
+
get(key: string): number | undefined;
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=id-allocator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id-allocator.d.ts","sourceRoot":"","sources":["../../src/infrastructure/id-allocator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,OAAO,CAAK;IAEpB,IAAI,IAAI,MAAM;CAIf;AAED;;;;GAIG;AACH,qBAAa,aAAa;IAGZ,OAAO,CAAC,QAAQ,CAAC,SAAS;IAFtC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAA6B;gBAEpB,SAAS,EAAE,cAAc;IAEtD,oEAAoE;IACpE,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM;IAU1B,8FAA8F;IAC9F,KAAK,IAAI,MAAM;IAIf,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAIzB,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;CAGrC"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Deterministic numeric XMA id allocator.
|
|
3
|
+
*
|
|
4
|
+
* XMA reference documents use small sequential integer ids. We do not (and
|
|
5
|
+
* cannot) reproduce Archi's string ids — instead we hand out ids in strictly
|
|
6
|
+
* increasing order, starting from 1, driven entirely by serializer traversal
|
|
7
|
+
* order. As long as the traversal order over a given `ArchiModel` is stable
|
|
8
|
+
* (it is: every `ArchiModel` collection preserves source document order),
|
|
9
|
+
* the same input always yields the same ids.
|
|
10
|
+
*
|
|
11
|
+
* Never uses Math.random(), Date.now(), or any other non-deterministic or
|
|
12
|
+
* environment-specific source.
|
|
13
|
+
*/
|
|
14
|
+
export class XmaIdAllocator {
|
|
15
|
+
constructor() {
|
|
16
|
+
this.counter = 0;
|
|
17
|
+
}
|
|
18
|
+
next() {
|
|
19
|
+
this.counter += 1;
|
|
20
|
+
return this.counter;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* A registry mapping arbitrary string keys (Archi ids, or synthetic
|
|
25
|
+
* composite keys) to allocated XMA numeric ids, with reference-consistent
|
|
26
|
+
* reuse: asking for the same key twice returns the same id.
|
|
27
|
+
*/
|
|
28
|
+
export class XmaIdRegistry {
|
|
29
|
+
constructor(allocator) {
|
|
30
|
+
this.allocator = allocator;
|
|
31
|
+
this.ids = new Map();
|
|
32
|
+
}
|
|
33
|
+
/** Allocates (or returns the previously allocated) id for `key`. */
|
|
34
|
+
idFor(key) {
|
|
35
|
+
const existing = this.ids.get(key);
|
|
36
|
+
if (existing !== undefined) {
|
|
37
|
+
return existing;
|
|
38
|
+
}
|
|
39
|
+
const id = this.allocator.next();
|
|
40
|
+
this.ids.set(key, id);
|
|
41
|
+
return id;
|
|
42
|
+
}
|
|
43
|
+
/** Allocates a fresh id not tied to any lookup key (e.g. structural scaffolding elements). */
|
|
44
|
+
fresh() {
|
|
45
|
+
return this.allocator.next();
|
|
46
|
+
}
|
|
47
|
+
has(key) {
|
|
48
|
+
return this.ids.has(key);
|
|
49
|
+
}
|
|
50
|
+
get(key) {
|
|
51
|
+
return this.ids.get(key);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=id-allocator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"id-allocator.js","sourceRoot":"","sources":["../../src/infrastructure/id-allocator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,cAAc;IAA3B;QACU,YAAO,GAAG,CAAC,CAAC;IAMtB,CAAC;IAJC,IAAI;QACF,IAAI,CAAC,OAAO,IAAI,CAAC,CAAC;QAClB,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,aAAa;IAGxB,YAA6B,SAAyB;QAAzB,cAAS,GAAT,SAAS,CAAgB;QAFrC,QAAG,GAAG,IAAI,GAAG,EAAkB,CAAC;IAEQ,CAAC;IAE1D,oEAAoE;IACpE,KAAK,CAAC,GAAW;QACf,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;QACnC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,OAAO,QAAQ,CAAC;QAClB,CAAC;QACD,MAAM,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QACjC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;QACtB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,8FAA8F;IAC9F,KAAK;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;IAC/B,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;IAED,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IAC3B,CAAC;CACF"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal typed XML element tree + deterministic serializer.
|
|
3
|
+
*
|
|
4
|
+
* Business logic builds `XmlElement` trees (attribute order and child order
|
|
5
|
+
* are explicit arrays, controlled entirely by the caller) and this module
|
|
6
|
+
* turns them into text. No implicit ordering, no host/timestamp-dependent
|
|
7
|
+
* output, no third-party XML dependency — keeps the runtime browser-safe.
|
|
8
|
+
*/
|
|
9
|
+
export interface XmlElement {
|
|
10
|
+
tag: string;
|
|
11
|
+
attrs?: Array<[string, string]>;
|
|
12
|
+
children?: XmlElement[];
|
|
13
|
+
/** Leaf text content. Mutually exclusive with `children` in practice. */
|
|
14
|
+
text?: string;
|
|
15
|
+
}
|
|
16
|
+
export declare function element(tag: string, attrs?: Array<[string, string]>, children?: XmlElement[]): XmlElement;
|
|
17
|
+
export declare function textElement(tag: string, text: string, attrs?: Array<[string, string]>): XmlElement;
|
|
18
|
+
/** Renders a root element to a complete, deterministic XML document string. */
|
|
19
|
+
export declare function renderXmlDocument(root: XmlElement): string;
|
|
20
|
+
//# sourceMappingURL=xml-writer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"xml-writer.d.ts","sourceRoot":"","sources":["../../src/infrastructure/xml-writer.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,MAAM,WAAW,UAAU;IACzB,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IAChC,QAAQ,CAAC,EAAE,UAAU,EAAE,CAAC;IACxB,yEAAyE;IACzE,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,wBAAgB,OAAO,CACrB,GAAG,EAAE,MAAM,EACX,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,EAC/B,QAAQ,CAAC,EAAE,UAAU,EAAE,GACtB,UAAU,CAEZ;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,KAAK,CAAC,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,GAAG,UAAU,CAElG;AAsCD,+EAA+E;AAC/E,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,UAAU,GAAG,MAAM,CAI1D"}
|