@rslsp1/fa-app-tools 2.0.78 → 2.0.82

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.
@@ -0,0 +1,157 @@
1
+ // src/lib/szenen/versionDag.ts
2
+ var VersionDagError = class extends Error {
3
+ };
4
+ function buildVersionDag(versions) {
5
+ if (versions.length === 0) {
6
+ throw new VersionDagError("Versions-DAG ohne Versionen ist nicht aufbaubar.");
7
+ }
8
+ const elementId = versions[0].element_id;
9
+ const nodes = /* @__PURE__ */ new Map();
10
+ const children = /* @__PURE__ */ new Map();
11
+ for (const version of versions) {
12
+ if (!version.version_id) {
13
+ throw new VersionDagError("Version ohne version_id.");
14
+ }
15
+ if (version.element_id !== elementId) {
16
+ throw new VersionDagError(
17
+ `Version ${version.version_id} gehoert zu Element ${version.element_id}, erwartet ${elementId}. Ein DAG umfasst genau ein Element.`
18
+ );
19
+ }
20
+ if (nodes.has(version.version_id)) {
21
+ throw new VersionDagError(`Version ${version.version_id} kommt doppelt vor.`);
22
+ }
23
+ nodes.set(version.version_id, version);
24
+ if (!children.has(version.version_id)) children.set(version.version_id, []);
25
+ }
26
+ for (const version of versions) {
27
+ const seenParents = /* @__PURE__ */ new Set();
28
+ for (const link of version.parents) {
29
+ if (link.parent_version_id === version.version_id) {
30
+ throw new VersionDagError(`Version ${version.version_id} ist ihr eigener Parent.`);
31
+ }
32
+ if (!nodes.has(link.parent_version_id)) {
33
+ throw new VersionDagError(
34
+ `Version ${version.version_id} verweist auf unbekannten Parent ${link.parent_version_id}. Der Graph waere unvollstaendig.`
35
+ );
36
+ }
37
+ if (seenParents.has(link.parent_version_id)) {
38
+ throw new VersionDagError(
39
+ `Version ${version.version_id} nennt Parent ${link.parent_version_id} doppelt.`
40
+ );
41
+ }
42
+ seenParents.add(link.parent_version_id);
43
+ children.get(link.parent_version_id).push(version.version_id);
44
+ }
45
+ }
46
+ assertAcyclic(nodes);
47
+ const versionIds = versions.map((v) => v.version_id);
48
+ const closure = (start, step) => {
49
+ const seen = /* @__PURE__ */ new Set();
50
+ const stack = [...step(start)];
51
+ while (stack.length > 0) {
52
+ const current = stack.pop();
53
+ if (seen.has(current)) continue;
54
+ seen.add(current);
55
+ stack.push(...step(current));
56
+ }
57
+ return seen;
58
+ };
59
+ const parentIdsOf = (id) => (nodes.get(id)?.parents ?? []).map((p) => p.parent_version_id);
60
+ const childIdsOf = (id) => children.get(id) ?? [];
61
+ return {
62
+ elementId,
63
+ versionIds,
64
+ get: (versionId) => nodes.get(versionId),
65
+ parentsOf: (versionId) => nodes.get(versionId)?.parents ?? [],
66
+ childrenOf: (versionId) => childIdsOf(versionId),
67
+ heads: () => versionIds.filter((id) => childIdsOf(id).length === 0),
68
+ roots: () => versionIds.filter((id) => parentIdsOf(id).length === 0),
69
+ hasFork() {
70
+ return this.heads().length > 1;
71
+ },
72
+ ancestorsOf: (versionId) => closure(versionId, parentIdsOf),
73
+ descendantsOf: (versionId) => closure(versionId, childIdsOf),
74
+ isAncestorOf(candidate, versionId) {
75
+ return this.ancestorsOf(versionId).has(candidate);
76
+ }
77
+ };
78
+ }
79
+ function assertAcyclic(nodes) {
80
+ const OPEN = 1;
81
+ const DONE = 2;
82
+ const state = /* @__PURE__ */ new Map();
83
+ for (const start of nodes.keys()) {
84
+ if (state.get(start) === DONE) continue;
85
+ const stack = [
86
+ { id: start, next: 0, path: [start] }
87
+ ];
88
+ state.set(start, OPEN);
89
+ while (stack.length > 0) {
90
+ const frame = stack[stack.length - 1];
91
+ const parents = nodes.get(frame.id).parents;
92
+ if (frame.next >= parents.length) {
93
+ state.set(frame.id, DONE);
94
+ stack.pop();
95
+ continue;
96
+ }
97
+ const parentId = parents[frame.next++].parent_version_id;
98
+ const parentState = state.get(parentId);
99
+ if (parentState === OPEN) {
100
+ throw new VersionDagError(
101
+ `Zyklus im Versions-DAG: ${[...frame.path, parentId].join(" -> ")}`
102
+ );
103
+ }
104
+ if (parentState !== DONE) {
105
+ state.set(parentId, OPEN);
106
+ stack.push({ id: parentId, next: 0, path: [...frame.path, parentId] });
107
+ }
108
+ }
109
+ }
110
+ }
111
+ function resolvePreferredHead(options) {
112
+ const { dag, workingContextId, inheritanceChain = [], owningContextId, selections = [] } = options;
113
+ const heads = dag.heads();
114
+ const ignoredSelections = [];
115
+ const byContext = /* @__PURE__ */ new Map();
116
+ for (const selection of selections) {
117
+ if (!dag.get(selection.version_id)) {
118
+ ignoredSelections.push(selection);
119
+ continue;
120
+ }
121
+ if (!byContext.has(selection.context_id)) byContext.set(selection.context_id, selection);
122
+ }
123
+ const candidates = [
124
+ { contextId: workingContextId, source: "context" },
125
+ ...inheritanceChain.map((contextId) => ({ contextId, source: "inherited" })),
126
+ ...owningContextId ? [{ contextId: owningContextId, source: "owning" }] : []
127
+ ];
128
+ for (const candidate of candidates) {
129
+ const selection = byContext.get(candidate.contextId);
130
+ if (!selection) continue;
131
+ return {
132
+ resolution: {
133
+ kind: "selected",
134
+ versionId: selection.version_id,
135
+ source: candidate.source,
136
+ contextId: candidate.contextId,
137
+ heads,
138
+ stale: !heads.includes(selection.version_id)
139
+ },
140
+ ignoredSelections
141
+ };
142
+ }
143
+ if (heads.length === 1) {
144
+ return { resolution: { kind: "single", versionId: heads[0], heads }, ignoredSelections };
145
+ }
146
+ return { resolution: { kind: "fork", heads }, ignoredSelections };
147
+ }
148
+ function preferredVersionId(resolution) {
149
+ return resolution.kind === "fork" ? null : resolution.versionId;
150
+ }
151
+
152
+ export {
153
+ VersionDagError,
154
+ buildVersionDag,
155
+ resolvePreferredHead,
156
+ preferredVersionId
157
+ };