linked-rolls 0.57.0 → 0.59.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.
Files changed (130) hide show
  1. package/README.md +54 -367
  2. package/lib/{constraints.d.ts → analysis/constraints.d.ts} +1 -1
  3. package/lib/{constraints.js → analysis/constraints.js} +8 -7
  4. package/lib/analysis/ownPaper.d.ts +26 -0
  5. package/lib/analysis/ownPaper.js +41 -0
  6. package/lib/{reservations.d.ts → analysis/reservations.d.ts} +3 -3
  7. package/lib/{reservations.js → analysis/reservations.js} +3 -3
  8. package/lib/{sigla.d.ts → analysis/sigla.d.ts} +2 -2
  9. package/lib/{sigla.js → analysis/sigla.js} +4 -4
  10. package/lib/{witnesses.d.ts → analysis/witnesses.d.ts} +2 -2
  11. package/lib/{witnesses.js → analysis/witnesses.js} +2 -2
  12. package/lib/{Collation.d.ts → collation/Collation.d.ts} +4 -4
  13. package/lib/{Collation.js → collation/Collation.js} +4 -4
  14. package/lib/{alignment.d.ts → collation/alignment.d.ts} +4 -4
  15. package/lib/{alignment.js → collation/alignment.js} +10 -16
  16. package/lib/{scatter.d.ts → collation/scatter.d.ts} +4 -4
  17. package/lib/{scatter.js → collation/scatter.js} +3 -3
  18. package/lib/{statistics.d.ts → collation/statistics.d.ts} +1 -1
  19. package/lib/{statistics.js → collation/statistics.js} +2 -2
  20. package/lib/{substitution.d.ts → collation/substitution.d.ts} +9 -24
  21. package/lib/{substitution.js → collation/substitution.js} +13 -37
  22. package/lib/emulation/Emulation.d.ts +64 -0
  23. package/lib/emulation/Emulation.js +282 -0
  24. package/lib/emulation/negotiation.d.ts +14 -0
  25. package/lib/emulation/negotiation.js +20 -0
  26. package/lib/index.d.ts +43 -35
  27. package/lib/index.js +36 -35
  28. package/lib/{asJsonLd.d.ts → io/asJsonLd.d.ts} +1 -1
  29. package/lib/{asJsonLd.js → io/asJsonLd.js} +3 -3
  30. package/lib/{context.d.ts → io/context.d.ts} +4 -4
  31. package/lib/{context.js → io/context.js} +4 -4
  32. package/lib/{importJsonLd.d.ts → io/importJsonLd.d.ts} +1 -1
  33. package/lib/io/importJsonLd.js +128 -0
  34. package/lib/{migrate.js → io/migrate.js} +59 -55
  35. package/lib/{Agent.d.ts → model/Agent.d.ts} +1 -1
  36. package/lib/{Assumption.d.ts → model/Assumption.d.ts} +1 -1
  37. package/lib/{Edit.d.ts → model/Edit.d.ts} +1 -1
  38. package/lib/{Edition.d.ts → model/Edition.d.ts} +2 -2
  39. package/lib/{Feature.d.ts → model/Feature.d.ts} +1 -1
  40. package/lib/{FeatureSource.d.ts → model/FeatureSource.d.ts} +1 -1
  41. package/lib/{Perforator.d.ts → model/Perforator.d.ts} +1 -1
  42. package/lib/{RollCopy.d.ts → model/RollCopy.d.ts} +3 -10
  43. package/lib/{RollCopy.js → model/RollCopy.js} +3 -5
  44. package/lib/{Symbol.d.ts → model/Symbol.d.ts} +1 -1
  45. package/lib/{Version.d.ts → model/Version.d.ts} +10 -2
  46. package/lib/{Version.js → model/Version.js} +24 -1
  47. package/lib/{vocabulary.js → model/vocabulary.js} +2 -2
  48. package/lib/ops/beliefs.d.ts +9 -0
  49. package/lib/ops/beliefs.js +31 -0
  50. package/lib/ops/collation.d.ts +62 -0
  51. package/lib/ops/collation.js +228 -0
  52. package/lib/ops/commands.d.ts +9 -0
  53. package/lib/ops/commands.js +29 -0
  54. package/lib/ops/copies.d.ts +74 -0
  55. package/lib/ops/copies.js +135 -0
  56. package/lib/ops/draft.d.ts +59 -0
  57. package/lib/ops/draft.js +81 -0
  58. package/lib/ops/edits.d.ts +10 -0
  59. package/lib/ops/edits.js +82 -0
  60. package/lib/ops/features.d.ts +84 -0
  61. package/lib/ops/features.js +296 -0
  62. package/lib/ops/forget.d.ts +31 -0
  63. package/lib/ops/forget.js +110 -0
  64. package/lib/ops/immutable.d.ts +16 -0
  65. package/lib/ops/immutable.js +46 -0
  66. package/lib/ops/index.d.ts +13 -0
  67. package/lib/ops/index.js +7 -0
  68. package/lib/ops/versions.d.ts +33 -0
  69. package/lib/ops/versions.js +95 -0
  70. package/lib/readers/AtonParser.js +5 -7
  71. package/lib/readers/phillipsEroll.d.ts +3 -3
  72. package/lib/readers/phillipsEroll.js +4 -5
  73. package/lib/readers/spencerBar.d.ts +2 -2
  74. package/lib/readers/spencerBar.js +2 -3
  75. package/lib/readers/stanfordAton.d.ts +3 -3
  76. package/lib/readers/stanfordAton.js +2 -3
  77. package/lib/schema.json +0 -13
  78. package/lib/{ReproducingSystem.d.ts → systems/ReproducingSystem.d.ts} +3 -3
  79. package/lib/{TrackerBar.d.ts → systems/TrackerBar.d.ts} +48 -8
  80. package/lib/{TrackerBar.js → systems/TrackerBar.js} +11 -2
  81. package/lib/systems/index.d.ts +8 -2
  82. package/lib/systems/index.js +7 -1
  83. package/lib/systems/welte.d.ts +100 -0
  84. package/lib/systems/welte.js +119 -0
  85. package/lib/systems/welteLicensee/bar.d.ts +1 -1
  86. package/lib/systems/welteLicensee/bar.js +5 -2
  87. package/lib/systems/welteLicensee/system.d.ts +1 -1
  88. package/lib/systems/welteLicensee/system.js +1 -1
  89. package/lib/systems/welteT100/bar.d.ts +27 -1
  90. package/lib/systems/welteT100/bar.js +44 -2
  91. package/lib/systems/welteT100/system.d.ts +6 -39
  92. package/lib/systems/welteT100/system.js +19 -128
  93. package/lib/systems/welteT98/bar.d.ts +1 -1
  94. package/lib/systems/welteT98/bar.js +18 -2
  95. package/lib/systems/welteT98/system.d.ts +20 -38
  96. package/lib/systems/welteT98/system.js +20 -121
  97. package/lib/validate.d.ts +1 -1
  98. package/lib/{EditionView.d.ts → view/EditionView.d.ts} +17 -41
  99. package/lib/{EditionView.js → view/EditionView.js} +26 -75
  100. package/package.json +7 -2
  101. package/lib/Emulation.d.ts +0 -36
  102. package/lib/Emulation.js +0 -250
  103. package/lib/editionOps.d.ts +0 -260
  104. package/lib/editionOps.js +0 -1079
  105. package/lib/importJsonLd.js +0 -57
  106. /package/lib/{migrate.d.ts → io/migrate.d.ts} +0 -0
  107. /package/lib/{Agent.js → model/Agent.js} +0 -0
  108. /package/lib/{Assumption.js → model/Assumption.js} +0 -0
  109. /package/lib/{ConditionState.d.ts → model/ConditionState.d.ts} +0 -0
  110. /package/lib/{ConditionState.js → model/ConditionState.js} +0 -0
  111. /package/lib/{Edit.js → model/Edit.js} +0 -0
  112. /package/lib/{Edition.js → model/Edition.js} +0 -0
  113. /package/lib/{Feature.js → model/Feature.js} +0 -0
  114. /package/lib/{FeatureSource.js → model/FeatureSource.js} +0 -0
  115. /package/lib/{Perforator.js → model/Perforator.js} +0 -0
  116. /package/lib/{Quantity.d.ts → model/Quantity.d.ts} +0 -0
  117. /package/lib/{Quantity.js → model/Quantity.js} +0 -0
  118. /package/lib/{Symbol.js → model/Symbol.js} +0 -0
  119. /package/lib/{TrackCalibration.d.ts → model/TrackCalibration.d.ts} +0 -0
  120. /package/lib/{TrackCalibration.js → model/TrackCalibration.js} +0 -0
  121. /package/lib/{notes.d.ts → model/notes.d.ts} +0 -0
  122. /package/lib/{notes.js → model/notes.js} +0 -0
  123. /package/lib/{procedures.d.ts → model/procedures.d.ts} +0 -0
  124. /package/lib/{procedures.js → model/procedures.js} +0 -0
  125. /package/lib/{vocabulary.d.ts → model/vocabulary.d.ts} +0 -0
  126. /package/lib/{sorted.d.ts → shared/sorted.d.ts} +0 -0
  127. /package/lib/{sorted.js → shared/sorted.js} +0 -0
  128. /package/lib/{utils.d.ts → shared/utils.d.ts} +0 -0
  129. /package/lib/{utils.js → shared/utils.js} +0 -0
  130. /package/lib/{ReproducingSystem.js → systems/ReproducingSystem.js} +0 -0
@@ -0,0 +1,296 @@
1
+ import { v4 } from "uuid";
2
+ import { featuresByAct, featuresOf, statesNothing } from "../model/RollCopy.js";
3
+ import { idOf } from "../model/Assumption.js";
4
+ import { conditions as conditionsAllowed, featuresBorneBy, isPatch, withBorneFeatures } from "../model/Feature.js";
5
+ import { mm } from "../model/Quantity.js";
6
+ import { onCopy, onFeature, stateOf } from "./draft.js";
7
+ import { without, mapped, pruned, replacing, isRecord } from "./immutable.js";
8
+ import { renameReferences, forgetFeatures } from "./forget.js";
9
+ /**
10
+ * Whether the feature's own kind allows a condition of this kind. The
11
+ * type narrowed to is the weaker statement, holding of a condition any
12
+ * kind of feature may be in.
13
+ */
14
+ const allows = (feature, condition) => {
15
+ const allowed = conditionsAllowed[feature.type];
16
+ return allowed.includes(condition.conditionType);
17
+ };
18
+ /**
19
+ * States the condition of the feature, in place of any earlier
20
+ * statement. Throws where the kind of feature is in no such condition;
21
+ * `conditions` says which conditions each kind of feature may be in.
22
+ */
23
+ export const stateFeatureCondition = (copyId, featureId, condition) => onFeature(copyId, featureId, feature => {
24
+ if (!allows(feature, condition)) {
25
+ throw new Error(`A ${feature.type} is in no '${condition.conditionType}' condition`);
26
+ }
27
+ feature.condition = condition;
28
+ });
29
+ /**
30
+ * The ids of the features named and of everything they bear: a feature
31
+ * of a patch stands nowhere once the patch is gone.
32
+ */
33
+ const goneWith = (features, named) => features.flatMap(feature => named.has(feature.id)
34
+ ? withBorneFeatures(feature).map(borne => borne.id)
35
+ : goneWith(featuresBorneBy(feature), named));
36
+ /** The feature with the named ones gone from what it bears, or the very same one where it bears none of them. */
37
+ const withoutBorne = (feature, named) => {
38
+ if (!isPatch(feature) || !feature.features)
39
+ return feature;
40
+ const borne = withoutFeatures(feature.features, named);
41
+ return borne === feature.features ? feature : { ...feature, features: borne };
42
+ };
43
+ /** The features without those named, wherever they lie, and without whatever those bore. */
44
+ const withoutFeatures = (features, named) => mapped(without(features, feature => named.has(feature.id)), feature => withoutBorne(feature, named));
45
+ /** The act with its features rewritten, or the very same one where the rewriting left them. */
46
+ const rewritingAct = (rewrite) => (modification) => {
47
+ if (modification.type === 'Alteration')
48
+ return replacing(modification, 'produced', rewrite(modification.produced));
49
+ if (modification.type === 'Attachment')
50
+ return replacing(modification, 'added', rewrite(modification.added));
51
+ return modification;
52
+ };
53
+ /**
54
+ * Writes the rewriting onto the features of every act of the copy, and
55
+ * takes out an act it left with nothing to state.
56
+ */
57
+ const rewriteFeatures = (copy, rewrite) => {
58
+ const state = stateOf(copy);
59
+ const produced = state.production?.produced;
60
+ if (produced && copy.production) {
61
+ const rewritten = rewrite(produced);
62
+ if (rewritten !== produced)
63
+ copy.production.produced = rewritten;
64
+ }
65
+ copy.modifications = pruned(state.modifications, rewritingAct(rewrite), statesNothing);
66
+ };
67
+ /** The features of the act that brought the named feature about, where that act produces features. */
68
+ const producedBeside = (copy, featureId) => {
69
+ const names = (features) => features.some(feature => feature.id === featureId);
70
+ const punched = copy.production?.produced;
71
+ if (punched && names(punched))
72
+ return punched;
73
+ const act = copy.modifications.find(act => act.type === 'Alteration' && names(act.produced));
74
+ return act?.type === 'Alteration' ? act.produced : undefined;
75
+ };
76
+ /** The patches of the attachment that glued the named one on. */
77
+ const gluedBeside = (copy, patchId) => {
78
+ const act = copy.modifications.find(act => act.type === 'Attachment' && act.added.some(patch => patch.id === patchId));
79
+ return act?.type === 'Attachment' ? act.added : undefined;
80
+ };
81
+ /** The list the copy states a new feature in, making the act it belongs to where there is none. */
82
+ const actFor = (copy, patch, act) => {
83
+ const beside = act.beside !== undefined
84
+ ? (patch ? gluedBeside(copy, act.beside) : producedBeside(copy, act.beside))
85
+ : undefined;
86
+ if (beside)
87
+ return beside;
88
+ if (patch) {
89
+ const added = [];
90
+ copy.modifications.push({ type: 'Attachment', added, ...(act.purpose && { purpose: act.purpose }) });
91
+ return added;
92
+ }
93
+ if (act.punched) {
94
+ if (!copy.production)
95
+ copy.production = {};
96
+ if (!copy.production.produced)
97
+ copy.production.produced = [];
98
+ return copy.production.produced;
99
+ }
100
+ const produced = [];
101
+ copy.modifications.push({ type: 'Alteration', produced, ...(act.purpose && { purpose: act.purpose }) });
102
+ return produced;
103
+ };
104
+ /**
105
+ * States that the copy bears the feature, in the act that brought it
106
+ * about: a patch was glued on by an attachment, anything else was
107
+ * produced, by the punching or by a later act. Which act, `FeatureAct`
108
+ * says; a `beside` that names nothing the copy bears is passed over and
109
+ * the feature goes into an act of its own.
110
+ */
111
+ export const addFeature = (copyId, feature, act = {}) => onCopy(copyId, copy => {
112
+ actFor(copy, isPatch(feature), act).push(feature);
113
+ });
114
+ /**
115
+ * States that the patch bears the feature. What a patch bears came onto
116
+ * the copy with the patch, so it belongs to no act of its own and
117
+ * states no place: it stands where the patch stands.
118
+ */
119
+ export const addBorneFeature = (copyId, patchId, feature) => onCopy(copyId, copy => {
120
+ const patch = featuresOf(copy).flatMap(withBorneFeatures).find(borne => borne.id === patchId);
121
+ if (!patch || !isPatch(patch))
122
+ return;
123
+ if (!patch.features)
124
+ patch.features = [];
125
+ patch.features.push(feature);
126
+ });
127
+ /**
128
+ * Takes the features off the copy, and out of the versions with what
129
+ * only they carried. A feature may be named wherever the copy bears it:
130
+ * a patch goes with everything glued onto it, and a feature of a patch
131
+ * may be taken back on its own, the patch staying where it is. An act
132
+ * left having produced or added nothing goes as well.
133
+ */
134
+ export const removeFeatures = (copyId, featureIds) => onCopy(copyId, (copy, draft) => {
135
+ const named = new Set(featureIds);
136
+ const features = featuresOf(stateOf(copy));
137
+ rewriteFeatures(copy, (stated) => withoutFeatures(stated, named));
138
+ forgetFeatures(draft, new Set(goneWith(features, named)));
139
+ });
140
+ /**
141
+ * Whether two records of the edition state the same, the identity of a
142
+ * statement left out: two transcriptions reading the same word say the
143
+ * same, whichever ids they were given.
144
+ */
145
+ const sayTheSame = (a, b) => {
146
+ if (a === b)
147
+ return true;
148
+ if (!isRecord(a) || !isRecord(b) || Array.isArray(a) !== Array.isArray(b))
149
+ return false;
150
+ const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
151
+ keys.delete('id');
152
+ return [...keys].every(key => sayTheSame(a[key], b[key]));
153
+ };
154
+ /**
155
+ * What a feature states beyond its identity, its place along the roll,
156
+ * its depiction and its condition. What a patch bears counts by
157
+ * identity: two patches bear the same only where they bear the very
158
+ * same features, so that no merge takes a feature of a patch away.
159
+ */
160
+ const nature = (feature) => {
161
+ const { id, horizontal, depiction, condition, ...rest } = feature;
162
+ return isPatch(feature) && feature.features
163
+ ? { ...rest, features: feature.features.map(borne => borne.id) }
164
+ : rest;
165
+ };
166
+ const conditionsOf = (features) => features.flatMap(feature => feature.condition ? [feature.condition] : []);
167
+ /**
168
+ * What stands in the way of reading the features as one, or nothing
169
+ * where they may be merged. Features may be merged where they differ
170
+ * in nothing but their place along the roll, their depiction and the
171
+ * condition at most one of them states. How far apart they lie is not
172
+ * asked: whether a gap is a bridge of the perforator, a tear or two
173
+ * perforations of their own is the editor's reading.
174
+ */
175
+ export const mergeObstacle = (features) => {
176
+ if (features.length < 2)
177
+ return 'fewer-than-two';
178
+ const [first, ...rest] = features;
179
+ if (rest.some(feature => feature.type !== first.type))
180
+ return 'different-types';
181
+ if (rest.some(feature => !sayTheSame(feature.vertical, first.vertical)))
182
+ return 'different-tracks';
183
+ if (rest.some(feature => !sayTheSame(nature(feature), nature(first))))
184
+ return 'unlike-features';
185
+ const conditions = conditionsOf(features);
186
+ if (conditions.some(condition => !sayTheSame(condition, conditions[0])))
187
+ return 'differing-conditions';
188
+ return undefined;
189
+ };
190
+ /** The acts of the copy that state any of the named features. */
191
+ const actsHolding = (copy, named) => featuresByAct(copy).filter(features => features.some(feature => named.has(feature.id)));
192
+ /**
193
+ * What stands in the way of reading the named features of the copy as
194
+ * one, which is the whole of what `mergeFeatures` asks: whether one act
195
+ * brought them all about, and what the features themselves say. An id
196
+ * the copy does not bear at a place of its own is passed over.
197
+ */
198
+ const obstacleIn = (copy, featureIds) => {
199
+ const named = new Set(featureIds);
200
+ const acts = actsHolding(copy, named);
201
+ return acts.length > 1
202
+ ? 'different-acts'
203
+ : mergeObstacle((acts[0] ?? []).filter(feature => named.has(feature.id)));
204
+ };
205
+ /**
206
+ * What stands in the way of reading the features the ids name as one,
207
+ * as `mergeFeatures` will find it. `mergeObstacle` asks only what the
208
+ * features themselves say, which cannot reach `different-acts`: one
209
+ * feature is the work of one act, and where the features stand is the
210
+ * edition's business rather than theirs.
211
+ */
212
+ export const mergeObstacleIn = (view, featureIds) => {
213
+ const copy = featureIds.map(id => view.copyOf(id)).find(copy => copy !== undefined);
214
+ return copy ? obstacleIn(copy, featureIds) : 'fewer-than-two';
215
+ };
216
+ /** The items with the replacement in the place of the first it stands for, the others dropped. */
217
+ const standingFor = (items, stands, replacement) => {
218
+ const first = items.findIndex(stands);
219
+ if (first < 0)
220
+ return items;
221
+ return items.flatMap((item, i) => i === first ? [replacement] : stands(item) ? [] : [item]);
222
+ };
223
+ const spanning = (features) => ({
224
+ unit: 'mm',
225
+ from: mm(Math.min(...features.map(feature => feature.horizontal.from))),
226
+ to: mm(Math.max(...features.map(feature => feature.horizontal.to)))
227
+ });
228
+ /**
229
+ * The feature the merged ones give way to. It spans them all and is in
230
+ * every other respect the one of them that states a condition, the
231
+ * others being alike in all but their place. The depiction goes: a
232
+ * region of the scan showing one part depicts no more than that part.
233
+ */
234
+ const mergedFrom = (features, id) => {
235
+ const stating = features.find(feature => feature.condition) ?? features[0];
236
+ const merged = { ...stating, id, horizontal: spanning(features) };
237
+ delete merged.depiction;
238
+ return merged;
239
+ };
240
+ /** The symbol carried by the merged feature where it named one of those it replaces, and naming it once. */
241
+ const carriedByMerged = (replaced, mergedId) => (symbol) => {
242
+ const names = (carrier) => replaced.has(idOf(carrier));
243
+ const first = symbol.carriers.find(names);
244
+ if (!first)
245
+ return symbol;
246
+ return { ...symbol, carriers: standingFor(symbol.carriers, names, { ...first, id: mergedId }) };
247
+ };
248
+ /** Points the versions at the merged feature wherever they name one it replaces. */
249
+ const carryOver = (draft, replaced, mergedId) => {
250
+ const recarry = carriedByMerged(replaced, mergedId);
251
+ const recarrying = (edit) => {
252
+ const insert = edit.insert && mapped(edit.insert, recarry);
253
+ return insert === edit.insert ? edit : { ...edit, insert };
254
+ };
255
+ const versions = stateOf(draft.versions);
256
+ draft.versions.forEach((version, i) => {
257
+ const edits = versions[i].edits;
258
+ if (edits)
259
+ version.edits = mapped(edits, recarrying);
260
+ });
261
+ };
262
+ /**
263
+ * Replaces the features of the copy with a single one covering them
264
+ * all, where a scan has split what the editor reads as one feature.
265
+ * The merged feature takes the place of the first it replaces and
266
+ * spans from the first to the last, any gap between them included.
267
+ * What the replaced features carried is carried by the merged one, and
268
+ * whatever else named them, a modification or a comprehension, names
269
+ * the merged one in their stead.
270
+ *
271
+ * Only the features the copy bears itself are merged. A feature of a
272
+ * patch states no place along the roll of its own, so there is nothing
273
+ * for a merge to span, and an id naming one is passed over as an id the
274
+ * copy does not bear is. Features of two different acts are not merged
275
+ * either: one feature is the work of one act.
276
+ *
277
+ * Throws where the features cannot stand for one; `mergeObstacle`
278
+ * says beforehand whether they can.
279
+ */
280
+ export const mergeFeatures = (copyId, featureIds) => onCopy(copyId, (copy, draft) => {
281
+ const state = stateOf(copy);
282
+ const obstacle = obstacleIn(state, featureIds);
283
+ if (obstacle) {
284
+ throw new Error(`The features of copy ${copyId} cannot be merged: ${obstacle}`);
285
+ }
286
+ const named = new Set(featureIds);
287
+ const toMerge = (actsHolding(state, named)[0] ?? []).filter(feature => named.has(feature.id));
288
+ const replaced = new Set(toMerge.map(feature => feature.id));
289
+ const mergedId = v4();
290
+ rewriteFeatures(copy, (stated) => {
291
+ const stands = (feature) => replaced.has(feature.id);
292
+ return stated.some(stands) ? standingFor(stated, stands, mergedFrom(stated.filter(stands), mergedId)) : stated;
293
+ });
294
+ carryOver(draft, replaced, mergedId);
295
+ renameReferences(draft, ids => standingFor(ids, id => replaced.has(id), mergedId));
296
+ });
@@ -0,0 +1,31 @@
1
+ /** Striking features from the edition, and rewriting the ids by which the edition names them outside the versions. */
2
+ import { Draft } from "immer";
3
+ import { Edition } from "../model/Edition.js";
4
+ import { AnySymbol } from "../model/Symbol.js";
5
+ import { RollCopy } from "../model/RollCopy.js";
6
+ import { Ids } from "./draft.js";
7
+ /** The ids of every feature the copy bears, those a patch bears among them. */
8
+ export declare const featureIdsOf: (copy: RollCopy) => Ids;
9
+ /**
10
+ * A symbol every carrier of which lies among the features loses its
11
+ * evidence with them. A symbol without carriers, such as a label,
12
+ * stands on its own.
13
+ */
14
+ export declare const carriedOnlyOn: (features: Ids) => (symbol: AnySymbol) => boolean;
15
+ /** The ids a statement names, as the rewriting leaves them. */
16
+ type Rename = (ids: string[]) => string[];
17
+ /**
18
+ * Rewrites the ids by which the edition names features and symbols
19
+ * outside the versions. A belief is annotatable anywhere, so the whole
20
+ * edition is read; what the rewriting leaves alone is left the very
21
+ * object it was.
22
+ */
23
+ export declare const renameReferences: (draft: Draft<Edition>, rename: Rename) => void;
24
+ /**
25
+ * Strikes the features from the edition: their carriers go, a symbol
26
+ * that had no other carrier goes with them, an edit left exchanging
27
+ * nothing goes as well, and so does every reference the versions, the
28
+ * modifications of the copies and the comprehensions made to what went.
29
+ */
30
+ export declare const forgetFeatures: (draft: Draft<Edition>, features: Ids) => void;
31
+ export {};
@@ -0,0 +1,110 @@
1
+ import { isCommand, placementRelations } from "../model/Symbol.js";
2
+ import { editsOf } from "../model/Version.js";
3
+ import { featuresOf, statesNothing } from "../model/RollCopy.js";
4
+ import { idOf } from "../model/Assumption.js";
5
+ import { withBorneFeatures } from "../model/Feature.js";
6
+ import { stateOf, droppedIds, insertedIn, edited } from "./draft.js";
7
+ import { without, mapped, pruned, replacing, deeply, writeInto } from "./immutable.js";
8
+ /** The ids of every feature the copy bears, those a patch bears among them. */
9
+ export const featureIdsOf = (copy) => new Set(featuresOf(copy).flatMap(withBorneFeatures).map(feature => feature.id));
10
+ /**
11
+ * A symbol every carrier of which lies among the features loses its
12
+ * evidence with them. A symbol without carriers, such as a label,
13
+ * stands on its own.
14
+ */
15
+ export const carriedOnlyOn = (features) => (symbol) => symbol.carriers.length > 0 && symbol.carriers.every(carrier => features.has(idOf(carrier)));
16
+ const references = [...placementRelations, 'pairedWith'];
17
+ /** The command without its references to the dropped symbols, or the very same one where it makes none. */
18
+ const forgettingReferences = (command, dropped) => {
19
+ const stale = references.filter(relation => {
20
+ const reference = command[relation];
21
+ return reference !== undefined && dropped.has(idOf(reference));
22
+ });
23
+ if (stale.length === 0)
24
+ return command;
25
+ const kept = { ...command };
26
+ stale.forEach(relation => { delete kept[relation]; });
27
+ return kept;
28
+ };
29
+ /** The symbol without the features among its carriers, and without references to what went with them. */
30
+ const forgettingCarriers = (features, dropped) => (symbol) => {
31
+ const carriers = without(symbol.carriers, carrier => features.has(idOf(carrier)));
32
+ const relieved = carriers === symbol.carriers ? symbol : { ...symbol, carriers };
33
+ return isCommand(relieved) ? forgettingReferences(relieved, dropped) : relieved;
34
+ };
35
+ /** The edit without the dropped symbols and the features, or the very same edit where it had none of them. */
36
+ const forgettingFeatures = (features, dropped) => {
37
+ const forgetCarriers = forgettingCarriers(features, dropped);
38
+ return (edit) => {
39
+ const insert = edit.insert && mapped(without(edit.insert, symbol => dropped.has(symbol.id)), forgetCarriers);
40
+ const deleted = edit.delete && without(edit.delete, id => dropped.has(id));
41
+ if (insert === edit.insert && deleted === edit.delete)
42
+ return edit;
43
+ return { ...edit, ...(insert && { insert }), ...(deleted && { delete: deleted }) };
44
+ };
45
+ };
46
+ const isCopy = (record) => 'type' in record && record.type === 'RollCopy';
47
+ const isBelief = (record) => 'type' in record && record.type === 'belief';
48
+ const isComprehension = (reason) => reason.type === 'meaningComprehension';
49
+ const comprehendsNothing = (reason) => isComprehension(reason) && reason.comprehends.length === 0;
50
+ /**
51
+ * The modification with what it names rewritten, or the very same one
52
+ * where that leaves it as it was. Only a removal names anything by id:
53
+ * what an act produced or glued on it states itself.
54
+ */
55
+ const renamingMembers = (rename) => (modification) => modification.type === 'Removal'
56
+ ? replacing(modification, 'removed', rename(modification.removed))
57
+ : modification;
58
+ /** The reason with what a comprehension comprehends rewritten; any other reason names nothing of the kind. */
59
+ const renamingComprehended = (rename) => (reason) => isComprehension(reason) ? replacing(reason, 'comprehends', rename(reason.comprehends)) : reason;
60
+ /**
61
+ * The record with the ids it names rewritten: what a copy's removals
62
+ * removed, and what the comprehensions among a belief's reasons
63
+ * comprehend. A removal or a comprehension the rewriting emptied goes
64
+ * with what it named; one that named nothing before stays, as an edit
65
+ * that was empty before it does.
66
+ */
67
+ const renaming = (rename) => (record) => {
68
+ if (isCopy(record)) {
69
+ return replacing(record, 'modifications', pruned(record.modifications, renamingMembers(rename), statesNothing));
70
+ }
71
+ if (isBelief(record)) {
72
+ return replacing(record, 'reasons', pruned(record.reasons, renamingComprehended(rename), comprehendsNothing));
73
+ }
74
+ return record;
75
+ };
76
+ /**
77
+ * Rewrites the ids by which the edition names features and symbols
78
+ * outside the versions. A belief is annotatable anywhere, so the whole
79
+ * edition is read; what the rewriting leaves alone is left the very
80
+ * object it was.
81
+ */
82
+ export const renameReferences = (draft, rename) => {
83
+ const before = stateOf(draft);
84
+ const after = deeply(renaming(rename))(before);
85
+ if (after !== before)
86
+ writeInto(draft, before, after);
87
+ };
88
+ /**
89
+ * Strikes the features from the edition: their carriers go, a symbol
90
+ * that had no other carrier goes with them, an edit left exchanging
91
+ * nothing goes as well, and so does every reference the versions, the
92
+ * modifications of the copies and the comprehensions made to what went.
93
+ */
94
+ export const forgetFeatures = (draft, features) => {
95
+ const versions = stateOf(draft.versions);
96
+ const dropped = new Set(insertedIn(versions).filter(carriedOnlyOn(features)).map(symbol => symbol.id));
97
+ const forget = forgettingFeatures(features, dropped);
98
+ const edits = versions.map(version => version.edits && edited(version.edits, forget));
99
+ const gone = new Set([
100
+ ...features,
101
+ ...dropped,
102
+ ...versions.flatMap((version, i) => droppedIds(editsOf(version), edits[i] ?? []))
103
+ ]);
104
+ renameReferences(draft, ids => without(ids, id => gone.has(id)));
105
+ draft.versions.forEach((version, i) => {
106
+ const stated = edits[i];
107
+ if (stated)
108
+ version.edits = stated;
109
+ });
110
+ };
@@ -0,0 +1,16 @@
1
+ /** Rewritings that return the very object they were given wherever they change nothing, so that an immer draft is left untouched there. */
2
+ /** The items that do not match, or the very same array when none does, so that a draft stays untouched. */
3
+ export declare const without: <T>(items: T[], matches: (item: T) => boolean) => T[];
4
+ /** The items each changed, or the very same array when the change left every one as it was. */
5
+ export declare const mapped: <T>(items: T[], change: (item: T) => T) => T[];
6
+ /** The items each changed, less those the change emptied; the very same array where it changed none. */
7
+ export declare const pruned: <T>(items: T[], change: (item: T) => T, emptied: (item: T) => boolean) => T[];
8
+ /** The record with the field replaced, or the very same record where that is what stood there. */
9
+ export declare const replacing: <T extends object, K extends keyof T>(record: T, key: K, value: T[K]) => T;
10
+ /** A record of the edition, as the walk over it sees one. */
11
+ export type Node = Record<string, unknown>;
12
+ export declare const isRecord: (value: unknown) => value is Node;
13
+ /** The value with the change applied to every record within it and then to itself, innermost first. */
14
+ export declare const deeply: (change: (record: object) => object) => (value: unknown) => unknown;
15
+ /** Writes onto the draft what the rewriting changed, as deep as the change reaches. */
16
+ export declare const writeInto: (draft: Node, before: Node, after: Node) => void;
@@ -0,0 +1,46 @@
1
+ /** Rewritings that return the very object they were given wherever they change nothing, so that an immer draft is left untouched there. */
2
+ /** The items that do not match, or the very same array when none does, so that a draft stays untouched. */
3
+ export const without = (items, matches) => items.some(matches) ? items.filter(item => !matches(item)) : items;
4
+ /** The items each changed, or the very same array when the change left every one as it was. */
5
+ export const mapped = (items, change) => {
6
+ const changed = items.map(change);
7
+ return changed.every((item, i) => item === items[i]) ? items : changed;
8
+ };
9
+ /** The items each changed, less those the change emptied; the very same array where it changed none. */
10
+ export const pruned = (items, change, emptied) => {
11
+ const changed = mapped(items, change);
12
+ return changed === items ? items : changed.filter((item, i) => item === items[i] || !emptied(item));
13
+ };
14
+ /** The record with the field replaced, or the very same record where that is what stood there. */
15
+ export const replacing = (record, key, value) => record[key] === value ? record : { ...record, [key]: value };
16
+ export const isRecord = (value) => typeof value === 'object' && value !== null;
17
+ /** The record's values each changed, or the very same record where the change left every one as it was. */
18
+ const withValues = (record, change) => {
19
+ const entries = Object.entries(record);
20
+ const changed = entries.map(([key, value]) => [key, change(value)]);
21
+ return changed.every(([, value], i) => value === entries[i][1]) ? record : Object.fromEntries(changed);
22
+ };
23
+ /** The value with the change applied to every record within it and then to itself, innermost first. */
24
+ export const deeply = (change) => {
25
+ const changed = (value) => Array.isArray(value) ? mapped(value, changed)
26
+ : isRecord(value) ? change(withValues(value, changed))
27
+ : value;
28
+ return changed;
29
+ };
30
+ /** Whether the two stand alike, so that what differs within them can be written where it lies. */
31
+ const alike = (before, after) => {
32
+ if (!isRecord(before) || !isRecord(after))
33
+ return false;
34
+ if (Array.isArray(before))
35
+ return Array.isArray(after) && before.length === after.length;
36
+ return !Array.isArray(after);
37
+ };
38
+ /** Writes onto the draft what the rewriting changed, as deep as the change reaches. */
39
+ export const writeInto = (draft, before, after) => Object.entries(after).forEach(([key, value]) => {
40
+ if (value === before[key])
41
+ return;
42
+ if (alike(before[key], value))
43
+ writeInto(draft[key], before[key], value);
44
+ else
45
+ draft[key] = value;
46
+ });
@@ -0,0 +1,13 @@
1
+ /**
2
+ * The operations on an edition, each written onto an immer draft of it.
3
+ * They are grouped by what they change under `ops/`; this module names
4
+ * the ones the library offers.
5
+ */
6
+ export type { EditionOp } from "./draft.js";
7
+ export { createVersion, addCopy, alignCopy, unalignCopy, shortenCopy, unshortenCopy, stateSource, clearSource, nameCopy, stateCarriage, clearCarriage, addGeneralCondition, symbolsCarriedOnlyBy, removeCopy } from "./copies.js";
8
+ export { stateFeatureCondition, addFeature, addBorneFeature, removeFeatures, mergeObstacle, mergeObstacleIn, mergeFeatures, type FeatureAct, type MergeObstacle } from "./features.js";
9
+ export { detachVersion, removeVersion, removeSymbols, deriveVersion, stateDerivation, clearDerivation } from "./versions.js";
10
+ export { connectVersions, collateSymbols, separateReadings } from "./collation.js";
11
+ export { mergeEdits, splitEdit } from "./edits.js";
12
+ export { placeCommand, unplaceCommand, pairCommands, unpairCommand } from "./commands.js";
13
+ export { createBelief, clearBelief, setCertainty, addReason, removeReason } from "./beliefs.js";
@@ -0,0 +1,7 @@
1
+ export { createVersion, addCopy, alignCopy, unalignCopy, shortenCopy, unshortenCopy, stateSource, clearSource, nameCopy, stateCarriage, clearCarriage, addGeneralCondition, symbolsCarriedOnlyBy, removeCopy } from "./copies.js";
2
+ export { stateFeatureCondition, addFeature, addBorneFeature, removeFeatures, mergeObstacle, mergeObstacleIn, mergeFeatures } from "./features.js";
3
+ export { detachVersion, removeVersion, removeSymbols, deriveVersion, stateDerivation, clearDerivation } from "./versions.js";
4
+ export { connectVersions, collateSymbols, separateReadings } from "./collation.js";
5
+ export { mergeEdits, splitEdit } from "./edits.js";
6
+ export { placeCommand, unplaceCommand, pairCommands, unpairCommand } from "./commands.js";
7
+ export { createBelief, clearBelief, setCertainty, addReason, removeReason } from "./beliefs.js";
@@ -0,0 +1,33 @@
1
+ import { EditionView } from "../view/EditionView.js";
2
+ import { Derivation, Version } from "../model/Version.js";
3
+ import { Belief } from "../model/Assumption.js";
4
+ import { EditionOp } from "./draft.js";
5
+ /** The derivations the version states beside its principal one, less any naming the parent. */
6
+ export declare const hypothesesBeside: (version: Readonly<Version>, parentId: string) => Derivation[];
7
+ /**
8
+ * Makes the version stand on its own: what it inherited becomes its
9
+ * own insertions, and its derivations go, the hypotheses among them,
10
+ * with the motivations that belonged to them.
11
+ */
12
+ export declare const detachVersion: (given: EditionView, versionId: string) => EditionOp;
13
+ /**
14
+ * Takes the version out. Whatever read its text against it comes to
15
+ * stand on its own, a hypothesis that something derives from it goes,
16
+ * and so does a copy's statement that it carries the version.
17
+ */
18
+ export declare const removeVersion: (given: EditionView, versionId: string) => EditionOp;
19
+ /** Takes the symbols out of the version's own insertions, and the edits that had nothing else. */
20
+ export declare const removeSymbols: (versionId: string, symbolIds: readonly string[]) => EditionOp;
21
+ /** Moves the edits into a new version based on this one. */
22
+ export declare const deriveVersion: (versionId: string, editIds: readonly string[]) => EditionOp;
23
+ /**
24
+ * States that the version may also derive from the parent, beside what
25
+ * it derives from already: a hypothesis, such as a contamination, under
26
+ * the belief given. The text stays read against the principal derivation
27
+ * unless the belief holds this one more certain. A version derives from
28
+ * itself, twice from one parent, or from one of its own descendants in
29
+ * no statement.
30
+ */
31
+ export declare const stateDerivation: (versionId: string, parentId: string, belief?: Belief) => EditionOp;
32
+ /** Takes back the hypothesis that the version derives from the parent; the principal derivation goes with `detachVersion`. */
33
+ export declare const clearDerivation: (versionId: string, parentId: string) => EditionOp;
@@ -0,0 +1,95 @@
1
+ import { v4 } from "uuid";
2
+ import { derivesFrom, editsOf, principalDerivationOf } from "../model/Version.js";
3
+ import { assignReference, idOf } from "../model/Assumption.js";
4
+ import { onVersion, stateOf, insertion, dropInsertions, referenceHeld, withoutReferences, reading } from "./draft.js";
5
+ import { without } from "./immutable.js";
6
+ import { dropStatements } from "./copies.js";
7
+ /** Whether the version's text is read against the parent, its principal derivation naming it. */
8
+ const readsAgainst = (version, parentId) => {
9
+ const principal = principalDerivationOf(version);
10
+ return principal !== undefined && idOf(principal) === parentId;
11
+ };
12
+ /** The derivations the version states beside its principal one, less any naming the parent. */
13
+ export const hypothesesBeside = (version, parentId) => {
14
+ const principal = principalDerivationOf(version);
15
+ return (version.basedOn ?? []).filter(derivation => derivation !== principal && idOf(derivation) !== parentId);
16
+ };
17
+ /** Takes out the derivations that match, and the list itself where none is left. */
18
+ const dropDerivations = (version, matches) => {
19
+ const derivations = stateOf(version).basedOn;
20
+ if (!derivations?.some(matches))
21
+ return;
22
+ const kept = withoutReferences(derivations, matches);
23
+ if (kept)
24
+ version.basedOn = kept;
25
+ else
26
+ delete version.basedOn;
27
+ };
28
+ /**
29
+ * Makes the version stand on its own: what it inherited becomes its
30
+ * own insertions, and its derivations go, the hypotheses among them,
31
+ * with the motivations that belonged to them.
32
+ */
33
+ export const detachVersion = (given, versionId) => reading(given, view => {
34
+ const edits = view.snapshot(versionId).map(insertion);
35
+ return onVersion(versionId, version => {
36
+ version.edits = edits;
37
+ delete version.basedOn;
38
+ version.motivations = [];
39
+ });
40
+ });
41
+ /**
42
+ * Takes the version out. Whatever read its text against it comes to
43
+ * stand on its own, a hypothesis that something derives from it goes,
44
+ * and so does a copy's statement that it carries the version.
45
+ */
46
+ export const removeVersion = (given, versionId) => reading(given, view => {
47
+ const detachments = view.edition.versions
48
+ .filter(version => readsAgainst(version, versionId))
49
+ .map(version => detachVersion(view, version.id));
50
+ const namesIt = (reference) => idOf(reference) === versionId;
51
+ return draft => {
52
+ detachments.forEach(detach => detach(draft));
53
+ draft.versions.forEach(version => dropDerivations(version, namesIt));
54
+ draft.copies.forEach(copy => dropStatements(copy, namesIt));
55
+ draft.versions = without(draft.versions, version => version.id === versionId);
56
+ };
57
+ });
58
+ /** Takes the symbols out of the version's own insertions, and the edits that had nothing else. */
59
+ export const removeSymbols = (versionId, symbolIds) => onVersion(versionId, version => dropInsertions(version, new Set(symbolIds)));
60
+ /** Moves the edits into a new version based on this one. */
61
+ export const deriveVersion = (versionId, editIds) => onVersion(versionId, (version, draft) => {
62
+ const chosen = new Set(editIds);
63
+ const moved = editsOf(version).filter(edit => chosen.has(edit.id));
64
+ if (version.edits)
65
+ version.edits = without(version.edits, edit => chosen.has(edit.id));
66
+ draft.versions.push({
67
+ id: v4(),
68
+ system: stateOf(version).system,
69
+ basedOn: [assignReference(versionId)],
70
+ edits: moved,
71
+ motivations: []
72
+ });
73
+ });
74
+ /**
75
+ * States that the version may also derive from the parent, beside what
76
+ * it derives from already: a hypothesis, such as a contamination, under
77
+ * the belief given. The text stays read against the principal derivation
78
+ * unless the belief holds this one more certain. A version derives from
79
+ * itself, twice from one parent, or from one of its own descendants in
80
+ * no statement.
81
+ */
82
+ export const stateDerivation = (versionId, parentId, belief) => onVersion(versionId, (version, draft) => {
83
+ const derivations = stateOf(version).basedOn ?? [];
84
+ if (parentId === versionId || derivations.some(derivation => idOf(derivation) === parentId))
85
+ return;
86
+ if (derivesFrom(stateOf(draft.versions), parentId, versionId))
87
+ return;
88
+ version.basedOn = [...derivations, referenceHeld(parentId, belief)];
89
+ });
90
+ /** Takes back the hypothesis that the version derives from the parent; the principal derivation goes with `detachVersion`. */
91
+ export const clearDerivation = (versionId, parentId) => onVersion(versionId, version => {
92
+ if (readsAgainst(stateOf(version), parentId))
93
+ return;
94
+ dropDerivations(version, derivation => idOf(derivation) === parentId);
95
+ });