linked-rolls 0.74.0 → 0.75.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 +41 -11
- package/lib/collation/Collation.d.ts +4 -0
- package/lib/collation/Collation.js +7 -2
- package/lib/filiation/adoption.d.ts +27 -4
- package/lib/filiation/adoption.js +73 -13
- package/lib/filiation/edits.d.ts +12 -1
- package/lib/filiation/edits.js +54 -5
- package/lib/filiation/index.d.ts +1 -0
- package/lib/filiation/index.js +1 -0
- package/lib/filiation/record.d.ts +79 -0
- package/lib/filiation/record.js +120 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/io/asJsonLd.d.ts +5 -1
- package/lib/io/asJsonLd.js +40 -17
- package/lib/io/importJsonLd.js +13 -2
- package/lib/io/migrate.js +30 -1
- package/lib/model/Assumption.d.ts +86 -3
- package/lib/model/Assumption.js +39 -2
- package/lib/model/Edit.d.ts +10 -6
- package/lib/model/Edit.js +4 -2
- package/lib/model/Edition.d.ts +10 -1
- package/lib/model/RollCopy.js +1 -1
- package/lib/model/Version.d.ts +29 -3
- package/lib/model/Version.js +34 -5
- package/lib/ops/edits.d.ts +3 -1
- package/lib/ops/edits.js +20 -7
- package/lib/schema.json +115 -100
- package/lib/software.d.ts +11 -0
- package/lib/software.js +9 -0
- package/lib/spec/context.json +6 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -132,6 +132,24 @@ edits the editor wrote, which hold their versions to what they insert
|
|
|
132
132
|
and delete. Motivations, beliefs and versions known only from a
|
|
133
133
|
recording remain the editor's to state.
|
|
134
134
|
|
|
135
|
+
What the program concluded is recorded as a program's conclusion. A
|
|
136
|
+
search scores whole stemmata, so the derivations and edits it states
|
|
137
|
+
are one proposition set (CRMinf I4) held under one belief. Each of
|
|
138
|
+
them names the set in its annotation, and the set states the belief
|
|
139
|
+
once. The belief, likely unless the editor says otherwise, rests on an
|
|
140
|
+
inference that names this library and its release, the copies it
|
|
141
|
+
collated, the published rules its costs carry out (`stemmaRules`) and,
|
|
142
|
+
in its note, the costs and settings it ran with and the margin of each
|
|
143
|
+
grouping. Who ran it and when are the editor's to give. A derivation or
|
|
144
|
+
an edit the editor holds for reasons of their own stays theirs, outside
|
|
145
|
+
the set.
|
|
146
|
+
|
|
147
|
+
```ts
|
|
148
|
+
const run = { actor: editor, date: { within: new Date() } }
|
|
149
|
+
const adopted = produce(edition, adoptStemma(proposal, run))
|
|
150
|
+
adopted.propositionSets // [{ type: 'propositionSet', id, belief: { certainty: 'likely', reasons: [{ type: 'inference', software: [linkedRolls], … }] } }]
|
|
151
|
+
```
|
|
152
|
+
|
|
135
153
|
The costs are those with which the stemma of WM 225 comes out of its
|
|
136
154
|
six copies. They were found by searching for such values, so they show
|
|
137
155
|
that the stemma is consistent with these principles, not that the
|
|
@@ -151,7 +169,11 @@ edit, a note struck again or two tied; a held command standing for a
|
|
|
151
169
|
latched pair is one recoding; a command the text makes no use of is a
|
|
152
170
|
redundancy added or removed; cancels punched across lines at one place
|
|
153
171
|
are one row. `adoptEdits` puts the proposal in place of the edits a
|
|
154
|
-
collation wrote and nobody has motivated, and keeps
|
|
172
|
+
collation or a program's run wrote and nobody has motivated, and keeps
|
|
173
|
+
the rest. The edits it proposes are a proposition set of their own. The
|
|
174
|
+
inference behind its belief names the beliefs the derivations of the
|
|
175
|
+
versions are held with as its premises, so that edits grouped against
|
|
176
|
+
a proposed stemma rest on it.
|
|
155
177
|
|
|
156
178
|
```ts
|
|
157
179
|
import { adoptEdits, adoptStemma, proposeEdits, proposeStemma } from 'linked-rolls'
|
|
@@ -167,10 +189,12 @@ const adopted = produce(edition, draft => {
|
|
|
167
189
|
|
|
168
190
|
Why an edit was made is not read off its shape, and neither is whether
|
|
169
191
|
a shift corrects an error: what is left to the editor is the
|
|
170
|
-
motivation.
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
192
|
+
motivation. Correcting an error is therefore one of the motivations
|
|
193
|
+
(`correctedError`), not one of the edit types, and a document written
|
|
194
|
+
while it was a type is read with that motivation in its place. On
|
|
195
|
+
WM 225 the proposal reproduces 370 of the 414 edits the editor wrote,
|
|
196
|
+
from what each version changes, and every type the editor gave them.
|
|
197
|
+
From the copies alone, through the proposed stemma, it reproduces 326.
|
|
174
198
|
|
|
175
199
|
## Format
|
|
176
200
|
|
|
@@ -182,7 +206,12 @@ https://pfefferniels.github.io/linked-rolls/, to which the w3id.org
|
|
|
182
206
|
identifiers redirect.
|
|
183
207
|
|
|
184
208
|
Beliefs are written as JSON-LD-star annotations (`@annotation`), so you
|
|
185
|
-
need a processor that supports JSON-LD-star to read them.
|
|
209
|
+
need a processor that supports JSON-LD-star to read them. A statement
|
|
210
|
+
that belongs to a proposition set carries no belief of its own: its
|
|
211
|
+
annotation names the set by its `@id`, which makes the set the reifier
|
|
212
|
+
of every statement that names it, as RDF 1.2 allows. The set is stated
|
|
213
|
+
once, under `propositionSets`, as a `crminf:I4_Proposition_Set` with its
|
|
214
|
+
belief.
|
|
186
215
|
|
|
187
216
|
## Development
|
|
188
217
|
|
|
@@ -199,11 +228,12 @@ TypeScript 7 no longer has the compiler API it reads the source with.
|
|
|
199
228
|
|
|
200
229
|
## Releasing
|
|
201
230
|
|
|
202
|
-
Raise the version in `package.json
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
231
|
+
Raise the version in `package.json`, in `CITATION.cff`, there with the
|
|
232
|
+
day as `date-released`, and in `src/software.ts`, which names the release
|
|
233
|
+
in the reasonings the library records. Then commit, and push a tag
|
|
234
|
+
`v<version>`. The `publish.yml` workflow builds, tests and publishes to
|
|
235
|
+
npm, then makes a GitHub release of the tag, which Zenodo archives with
|
|
236
|
+
a DOI. A test fails while the versions differ.
|
|
207
237
|
|
|
208
238
|
## About
|
|
209
239
|
|
|
@@ -164,6 +164,10 @@ export declare const uncheckedMotivation: {
|
|
|
164
164
|
* A recoding that only inserts or only deletes is the editor's: a
|
|
165
165
|
* collation types no one-sided edit, so one typed so is a reading of
|
|
166
166
|
* the transfer that collating again must not undo.
|
|
167
|
+
*
|
|
168
|
+
* An edit a program proposed and typed is no reading of the editor's
|
|
169
|
+
* either, as long as nobody has motivated it: it says so by belonging
|
|
170
|
+
* to the proposition set the program's run concluded (`adoptEdits`).
|
|
167
171
|
* @category Collation and alignment
|
|
168
172
|
*/
|
|
169
173
|
export declare const isCollationsOwn: (edit: Readonly<Edit>) => boolean;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isHeldForItself, propositionSetOf } from "../model/Assumption.js";
|
|
1
2
|
import { keyOf } from "../systems/TrackerBar.js";
|
|
2
3
|
import { distance, mm } from "../model/Quantity.js";
|
|
3
4
|
import { partitionPoint } from "../shared/sorted.js";
|
|
@@ -153,9 +154,13 @@ export const uncheckedMotivation = {
|
|
|
153
154
|
* A recoding that only inserts or only deletes is the editor's: a
|
|
154
155
|
* collation types no one-sided edit, so one typed so is a reading of
|
|
155
156
|
* the transfer that collating again must not undo.
|
|
157
|
+
*
|
|
158
|
+
* An edit a program proposed and typed is no reading of the editor's
|
|
159
|
+
* either, as long as nobody has motivated it: it says so by belonging
|
|
160
|
+
* to the proposition set the program's run concluded (`adoptEdits`).
|
|
156
161
|
* @category Collation and alignment
|
|
157
162
|
*/
|
|
158
163
|
export const isCollationsOwn = (edit) => (edit.editType === drawnByCollation && !!edit.insert?.length && !!edit.delete?.length)
|
|
159
|
-
|| (edit.editType === undefined
|
|
164
|
+
|| ((edit.editType === undefined || propositionSetOf(edit) !== undefined)
|
|
160
165
|
&& (edit.motivation === undefined || edit.motivation === unchecked)
|
|
161
|
-
&& edit
|
|
166
|
+
&& !isHeldForItself(edit));
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Edition } from "../model/Edition.js";
|
|
2
|
+
import { PropositionSet } from "../model/Assumption.js";
|
|
2
3
|
import { Version } from "../model/Version.js";
|
|
3
4
|
import { EditionOp } from "../ops/draft.js";
|
|
4
5
|
import { StemmaProposal, Unpinned } from "./proposal.js";
|
|
6
|
+
import { Run } from "./record.js";
|
|
5
7
|
/**
|
|
6
8
|
* What adopting a proposal makes of the stemma: the versions it puts in
|
|
7
9
|
* place of the edition's, and what of the edition it leaves out.
|
|
@@ -15,6 +17,12 @@ export interface Adoption {
|
|
|
15
17
|
removed: string[];
|
|
16
18
|
/** The edits the editor wrote that the adoption leaves out, and why. */
|
|
17
19
|
dropped: Unpinned[];
|
|
20
|
+
/**
|
|
21
|
+
* The record of the run: the proposition set the derivations and
|
|
22
|
+
* edits the proposal concluded belong to, held under one belief
|
|
23
|
+
* whose reason is the inference the program drew.
|
|
24
|
+
*/
|
|
25
|
+
propositionSet: PropositionSet;
|
|
18
26
|
}
|
|
19
27
|
/**
|
|
20
28
|
* What adopting the proposal makes of the edition's stemma, worked out
|
|
@@ -35,13 +43,28 @@ export interface Adoption {
|
|
|
35
43
|
* id and whatever it states beside its carriers. A version of the edition
|
|
36
44
|
* nothing stands for goes, unless it states no edits and derives from a
|
|
37
45
|
* version that stays, as a version known only from a recording does.
|
|
46
|
+
*
|
|
47
|
+
* What the program concluded is recorded as one proposition set, since
|
|
48
|
+
* the search scores whole stemmata and concludes the whole: each
|
|
49
|
+
* derivation it states and each edit it makes belongs to the set, which
|
|
50
|
+
* holds them under one belief, likely unless the run says otherwise,
|
|
51
|
+
* concluded by an inference that names this library and its release,
|
|
52
|
+
* the copies it collated, the rules it followed and, in its note, what
|
|
53
|
+
* it was computed with. A derivation or an edit that is the editor's,
|
|
54
|
+
* held under a belief of its own or saying more than a program says,
|
|
55
|
+
* stays the editor's and outside the set. The run gives who ran the
|
|
56
|
+
* program and when (`Run`).
|
|
38
57
|
* @category Filiation
|
|
39
58
|
*/
|
|
40
|
-
export declare const adoptionOf: (edition: Edition, proposal: StemmaProposal) => Adoption;
|
|
59
|
+
export declare const adoptionOf: (edition: Edition, proposal: StemmaProposal, run?: Run) => Adoption;
|
|
41
60
|
/**
|
|
42
61
|
* Puts the proposed stemma in place of the edition's, as `adoptionOf`
|
|
43
|
-
* describes, and takes back a copy's
|
|
44
|
-
* that
|
|
62
|
+
* describes, with the record of the run, and takes back a copy's
|
|
63
|
+
* statement that it carries a version that goes. The record of an
|
|
64
|
+
* earlier run whose conclusions are all replaced goes with them, and
|
|
65
|
+
* one of the very same run, by the same release on the same copies with
|
|
66
|
+
* the same result, stays as it was first recorded. One operation, and
|
|
67
|
+
* so one undo step.
|
|
45
68
|
* @category Operations
|
|
46
69
|
*/
|
|
47
|
-
export declare const adoptStemma: (proposal: StemmaProposal) => EditionOp;
|
|
70
|
+
export declare const adoptStemma: (proposal: StemmaProposal, run?: Run) => EditionOp;
|
|
@@ -1,15 +1,50 @@
|
|
|
1
1
|
/** Taking a proposed stemma into the edition, as one operation. */
|
|
2
2
|
import { v5 } from "uuid";
|
|
3
|
-
import { assignReference, idOf } from "../model/Assumption.js";
|
|
3
|
+
import { assignReference, idOf, isHeldForItself } from "../model/Assumption.js";
|
|
4
4
|
import { derivesFrom, editsOf, insertedBy, principalDerivationOf } from "../model/Version.js";
|
|
5
5
|
import { systemOf } from "../systems/TrackerBar.js";
|
|
6
|
-
import { unchecked } from "../collation/Collation.js";
|
|
6
|
+
import { isCollationsOwn, unchecked } from "../collation/Collation.js";
|
|
7
7
|
import { versionIn } from "../lookup/lookup.js";
|
|
8
8
|
import { declaring, reading } from "../ops/draft.js";
|
|
9
9
|
import { dropStatements } from "../ops/copies.js";
|
|
10
10
|
import { barNamed } from "./systems.js";
|
|
11
|
+
import { inMillimetres, keptSets, memberOf, recorded, stemmaRules } from "./record.js";
|
|
11
12
|
/** The namespace the names of adopted versions and edits are minted in, so that adopting again gives them again. */
|
|
12
13
|
const ADOPTED = '6f1d2c8e-93a4-4f0b-b8a1-2f4c5d7e9a31';
|
|
14
|
+
/** The rules the proposal followed, as far as its costs and settings carry them out. */
|
|
15
|
+
const rulesOf = (proposal) => [
|
|
16
|
+
...(proposal.costs.gain > 1 ? [stemmaRules.sharedAddition] : []),
|
|
17
|
+
...(proposal.costs.redundantLoss < 1 ? [stemmaRules.independentCleanup] : []),
|
|
18
|
+
...(proposal.costs.transferLoss < 1 ? [stemmaRules.recutLoss] : []),
|
|
19
|
+
...(Number.isFinite(proposal.costs.copyDeviation) ? [stemmaRules.copyDeviation] : []),
|
|
20
|
+
...(proposal.costs.lostState > 0 ? [stemmaRules.lostStates] : []),
|
|
21
|
+
...(proposal.sameText >= 0 ? [stemmaRules.oneText] : []),
|
|
22
|
+
stemmaRules.refinement,
|
|
23
|
+
stemmaRules.noContamination
|
|
24
|
+
];
|
|
25
|
+
/** A number as a note gives it: to two places at most. */
|
|
26
|
+
const amount = (value) => String(Math.round(value * 100) / 100);
|
|
27
|
+
/**
|
|
28
|
+
* What the run of `proposeStemma` was, and what it was computed with, in
|
|
29
|
+
* words, so that it can be run again: the copies by siglum, the costs,
|
|
30
|
+
* the settings, and the margin by which the copies decide each grouping.
|
|
31
|
+
*/
|
|
32
|
+
const noteOn = (edition, proposal) => {
|
|
33
|
+
const siglumOf = new Map(edition.copies.map(copy => [copy.id, copy.siglum ?? copy.id]));
|
|
34
|
+
const named = (copies) => copies.map(copy => siglumOf.get(copy) ?? copy).join(', ');
|
|
35
|
+
const { costs } = proposal;
|
|
36
|
+
const supports = proposal.supports.map(support => support.ancestor
|
|
37
|
+
? `${named(support.ancestor)} above ${named(support.copies.filter(copy => !support.ancestor.includes(copy)))} by ${amount(support.margin)}`
|
|
38
|
+
: `${named(support.copies)} together by ${amount(support.margin)}`);
|
|
39
|
+
return [
|
|
40
|
+
`The stemma ${proposal.notation}, the best of ${proposal.scored} that ${named(proposal.copies)} admit, at a cost of ${amount(proposal.cost)}.`,
|
|
41
|
+
`A reading arising costs ${amount(costs.gain)}, a reading lost 1, a redundancy lost ${amount(costs.redundantLoss)}, a reading lost on the way into another system ${amount(costs.transferLoss)}, a copy lacking a reading of its version ${amount(costs.copyDeviation)}, and a lost state ${amount(costs.lostState)}.`,
|
|
42
|
+
`Copies of one system differing in ${proposal.sameText < 0 ? 'any reading' : `no more than ${proposal.sameText} readings`} are taken for one text; a reading of the same kind within ${inMillimetres(proposal.movedWithin)} on the other side of the root for a moved one; and ${inMillimetres(proposal.margin)} beyond a copy's text or a tear for what the copy cannot show.`,
|
|
43
|
+
...(supports.length > 0 ? [`The copies decide its groupings by: ${supports.join('; ')}.`] : [])
|
|
44
|
+
].join(' ');
|
|
45
|
+
};
|
|
46
|
+
/** The set the run of `proposeStemma` concluded, held with the belief the run gives it. */
|
|
47
|
+
const stemmaSetOf = (edition, proposal, run) => recorded('stemma', [proposal.copies, proposal.notation, proposal.costs, proposal.sameText, proposal.movedWithin, proposal.margin], { note: noteOn(edition, proposal), premises: [], used: [...proposal.copies], applies: rulesOf(proposal) }, run);
|
|
13
48
|
const exchangeKey = (inserted, deleted) => `${[...inserted].sort().join(',')}/${[...deleted].sort().join(',')}`;
|
|
14
49
|
/** The carriers of the reading, keeping what the edition states of those it held already. */
|
|
15
50
|
const carriersOf = (reading, stated) => {
|
|
@@ -35,9 +70,21 @@ const carriersOf = (reading, stated) => {
|
|
|
35
70
|
* id and whatever it states beside its carriers. A version of the edition
|
|
36
71
|
* nothing stands for goes, unless it states no edits and derives from a
|
|
37
72
|
* version that stays, as a version known only from a recording does.
|
|
73
|
+
*
|
|
74
|
+
* What the program concluded is recorded as one proposition set, since
|
|
75
|
+
* the search scores whole stemmata and concludes the whole: each
|
|
76
|
+
* derivation it states and each edit it makes belongs to the set, which
|
|
77
|
+
* holds them under one belief, likely unless the run says otherwise,
|
|
78
|
+
* concluded by an inference that names this library and its release,
|
|
79
|
+
* the copies it collated, the rules it followed and, in its note, what
|
|
80
|
+
* it was computed with. A derivation or an edit that is the editor's,
|
|
81
|
+
* held under a belief of its own or saying more than a program says,
|
|
82
|
+
* stays the editor's and outside the set. The run gives who ran the
|
|
83
|
+
* program and when (`Run`).
|
|
38
84
|
* @category Filiation
|
|
39
85
|
*/
|
|
40
|
-
export const adoptionOf = (edition, proposal) => {
|
|
86
|
+
export const adoptionOf = (edition, proposal, run = {}) => {
|
|
87
|
+
const set = stemmaSetOf(edition, proposal, run);
|
|
41
88
|
const readingOf = new Map(proposal.collation.readings.map(reading => [reading.id, reading]));
|
|
42
89
|
const symbolIdOf = (reading) => proposal.symbols[reading] ?? reading;
|
|
43
90
|
const existing = new Map(edition.versions.flatMap(insertedBy).map(symbol => [symbol.id, symbol]));
|
|
@@ -112,16 +159,18 @@ export const adoptionOf = (edition, proposal) => {
|
|
|
112
159
|
const key = exchangeKey(inserted, deleted);
|
|
113
160
|
const same = statedBefore.get(key);
|
|
114
161
|
const insert = inserts.map(reading => symbolOf(reading, proposed.id));
|
|
115
|
-
if (same)
|
|
116
|
-
|
|
117
|
-
|
|
162
|
+
if (same) {
|
|
163
|
+
const taken = { ...same, ...(insert.length > 0 && { insert }), ...(deleted.length > 0 && { delete: deleted }) };
|
|
164
|
+
return isCollationsOwn(same) ? memberOf(set.id, taken) : taken;
|
|
165
|
+
}
|
|
166
|
+
return memberOf(set.id, {
|
|
118
167
|
type: 'edit',
|
|
119
168
|
id: v5(`edit|${id}|${key}`, ADOPTED),
|
|
120
169
|
...(recoding && { editType: 'recoding' }),
|
|
121
170
|
motivation: unchecked,
|
|
122
171
|
...(insert.length > 0 && { insert }),
|
|
123
172
|
...(deleted.length > 0 && { delete: deleted })
|
|
124
|
-
};
|
|
173
|
+
});
|
|
125
174
|
};
|
|
126
175
|
// A recoding an edit of the editor's cuts into is stated reading by reading.
|
|
127
176
|
const whole = proposed.recodings.filter(recoding => ![...recoding.by.map(reading => symbolIdAt(reading, proposed.id)), ...recoding.replaced.map(reading => inheritedAt(reading, proposed.id))]
|
|
@@ -133,11 +182,13 @@ export const adoptionOf = (edition, proposal) => {
|
|
|
133
182
|
...proposed.inserts.filter(reading => !recoded.has(reading)).map(reading => made([reading], [], false)),
|
|
134
183
|
...proposed.deletes.filter(reading => !recoded.has(reading)).map(reading => made([], [reading], false))
|
|
135
184
|
].filter((edit) => edit !== undefined);
|
|
185
|
+
// The derivation is the set's, unless the editor holds it for reasons of their own.
|
|
136
186
|
const parent = proposed.parent ? ids.get(proposed.parent) : undefined;
|
|
137
187
|
const principal = before && principalDerivationOf(before);
|
|
188
|
+
const same = principal && idOf(principal) === parent ? principal : undefined;
|
|
138
189
|
const derivation = parent === undefined
|
|
139
190
|
? undefined
|
|
140
|
-
:
|
|
191
|
+
: same && isHeldForItself(same) ? same : memberOf(set.id, same ?? assignReference(parent));
|
|
141
192
|
const hypotheses = (before?.basedOn ?? [])
|
|
142
193
|
.filter(other => other !== principal && staying.has(idOf(other)) && idOf(other) !== parent);
|
|
143
194
|
const basedOn = [...(derivation ? [derivation] : []), ...hypotheses];
|
|
@@ -171,19 +222,28 @@ export const adoptionOf = (edition, proposal) => {
|
|
|
171
222
|
// The edition's order where a version stays, the new ones after it.
|
|
172
223
|
const rank = new Map(edition.versions.map((version, index) => [version.id, index]));
|
|
173
224
|
const ordered = [...pruned, ...statedOnly].sort((a, b) => (rank.get(a.id) ?? Infinity) - (rank.get(b.id) ?? Infinity));
|
|
174
|
-
return { versions: ordered, ids: Object.fromEntries(ids), removed, dropped: proposal.unpinned };
|
|
225
|
+
return { versions: ordered, ids: Object.fromEntries(ids), removed, dropped: proposal.unpinned, propositionSet: set };
|
|
175
226
|
};
|
|
176
227
|
/**
|
|
177
228
|
* Puts the proposed stemma in place of the edition's, as `adoptionOf`
|
|
178
|
-
* describes, and takes back a copy's
|
|
179
|
-
* that
|
|
229
|
+
* describes, with the record of the run, and takes back a copy's
|
|
230
|
+
* statement that it carries a version that goes. The record of an
|
|
231
|
+
* earlier run whose conclusions are all replaced goes with them, and
|
|
232
|
+
* one of the very same run, by the same release on the same copies with
|
|
233
|
+
* the same result, stays as it was first recorded. One operation, and
|
|
234
|
+
* so one undo step.
|
|
180
235
|
* @category Operations
|
|
181
236
|
*/
|
|
182
|
-
export const adoptStemma = (proposal) => reading(edition => {
|
|
183
|
-
const { versions, removed } = adoptionOf(edition, proposal);
|
|
237
|
+
export const adoptStemma = (proposal, run = {}) => reading(edition => {
|
|
238
|
+
const { versions, removed, propositionSet } = adoptionOf(edition, proposal, run);
|
|
184
239
|
const gone = new Set(removed);
|
|
240
|
+
const sets = keptSets({ ...edition, versions }, [propositionSet]);
|
|
185
241
|
return draft => {
|
|
186
242
|
draft.versions = versions;
|
|
187
243
|
draft.copies.forEach(copy => dropStatements(copy, statement => gone.has(idOf(statement))));
|
|
244
|
+
if (sets)
|
|
245
|
+
draft.propositionSets = sets;
|
|
246
|
+
else
|
|
247
|
+
delete draft.propositionSets;
|
|
188
248
|
};
|
|
189
249
|
});
|
package/lib/filiation/edits.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Edition } from "../model/Edition.js";
|
|
|
2
2
|
import { Edit, EditType } from "../model/Edit.js";
|
|
3
3
|
import { Millimeters } from "../model/Quantity.js";
|
|
4
4
|
import { EditionOp } from "../ops/draft.js";
|
|
5
|
+
import { Run } from "./record.js";
|
|
5
6
|
/**
|
|
6
7
|
* How far a perforation may be put from where it stood for the two to be
|
|
7
8
|
* one move. WM 225 moves commands by up to about 60 mm.
|
|
@@ -129,6 +130,16 @@ export declare const isRegroupable: (edit: Readonly<Edit>) => boolean;
|
|
|
129
130
|
*
|
|
130
131
|
* Applied on the same draft after `adoptStemma`, it proposes the edits of
|
|
131
132
|
* the stemma adopted, as one undo step.
|
|
133
|
+
*
|
|
134
|
+
* The edits it proposes are recorded as one proposition set, held under
|
|
135
|
+
* one belief, likely unless the run says otherwise, whose reason is the
|
|
136
|
+
* inference the program drew: it names this library and its release,
|
|
137
|
+
* the versions it read, the rule that keeps an edit at one place and, in
|
|
138
|
+
* its note, the distances it grouped with, and it rests on the beliefs
|
|
139
|
+
* the derivations of the versions are held with. An edit of the set
|
|
140
|
+
* that nobody has motivated is the program's, typed and all, and a
|
|
141
|
+
* later proposal groups it again. The run gives who ran the program and
|
|
142
|
+
* when (`Run`).
|
|
132
143
|
* @category Operations
|
|
133
144
|
*/
|
|
134
|
-
export declare const adoptEdits: (versionIds?: readonly string[], options?: EditOptions) => EditionOp;
|
|
145
|
+
export declare const adoptEdits: (versionIds?: readonly string[], options?: EditOptions, run?: Run) => EditionOp;
|
package/lib/filiation/edits.js
CHANGED
|
@@ -4,7 +4,8 @@
|
|
|
4
4
|
* group is. Why a change was made is left to the editor.
|
|
5
5
|
*/
|
|
6
6
|
import { v5 } from "uuid";
|
|
7
|
-
import {
|
|
7
|
+
import { beliefIn } from "../model/Assumption.js";
|
|
8
|
+
import { deletedBy, editsOf, insertedBy, principalDerivationOf } from "../model/Version.js";
|
|
8
9
|
import { distance, mm, subtract } from "../model/Quantity.js";
|
|
9
10
|
import { trackerBarOf } from "../systems/index.js";
|
|
10
11
|
import { operationOf, substitutionsBetween } from "../collation/substitution.js";
|
|
@@ -16,6 +17,7 @@ import { groupBy } from "../shared/utils.js";
|
|
|
16
17
|
import { declaring, reading } from "../ops/draft.js";
|
|
17
18
|
import { ineffectiveAmong } from "./presence.js";
|
|
18
19
|
import { reads } from "./readings.js";
|
|
20
|
+
import { editRules, inMillimetres, keptSets, memberOf, recorded } from "./record.js";
|
|
19
21
|
/**
|
|
20
22
|
* How far a perforation may be put from where it stood for the two to be
|
|
21
23
|
* one move. WM 225 moves commands by up to about 60 mm.
|
|
@@ -479,8 +481,17 @@ const adoptedEditsOf = (edition, version, options) => {
|
|
|
479
481
|
// What an edit that stays leaves of a proposed one stands symbol by symbol.
|
|
480
482
|
return [...inserted.map(id => made([id], [])), ...deleted.map(id => made([], [id]))];
|
|
481
483
|
});
|
|
482
|
-
return
|
|
484
|
+
return { held, proposed };
|
|
483
485
|
};
|
|
486
|
+
/**
|
|
487
|
+
* What the run of `proposeEdits` was computed with, in words, so that it
|
|
488
|
+
* can be run again.
|
|
489
|
+
*/
|
|
490
|
+
const noteOn = (settings) => 'The changes of each version against the version it derives from, grouped line by line and never across a command both texts keep. '
|
|
491
|
+
+ `A perforation put elsewhere within ${inMillimetres(settings.shiftWithin)} is one move with the one it replaces, `
|
|
492
|
+
+ `an Off within ${inMillimetres(settings.pairWithin)} of an On one function turned on and off again, `
|
|
493
|
+
+ `a perforation beginning within ${inMillimetres(settings.resizeWithin)} of the one it replaces a shortened or prolonged one, `
|
|
494
|
+
+ `and perforations within ${inMillimetres(settings.samePlaceWithin)} of each other at one place.`;
|
|
484
495
|
/**
|
|
485
496
|
* Puts the proposed edits (`proposeEdits`) in place of those of the
|
|
486
497
|
* versions that a collation or an adoption wrote and nobody has given a
|
|
@@ -495,19 +506,35 @@ const adoptedEditsOf = (edition, version, options) => {
|
|
|
495
506
|
*
|
|
496
507
|
* Applied on the same draft after `adoptStemma`, it proposes the edits of
|
|
497
508
|
* the stemma adopted, as one undo step.
|
|
509
|
+
*
|
|
510
|
+
* The edits it proposes are recorded as one proposition set, held under
|
|
511
|
+
* one belief, likely unless the run says otherwise, whose reason is the
|
|
512
|
+
* inference the program drew: it names this library and its release,
|
|
513
|
+
* the versions it read, the rule that keeps an edit at one place and, in
|
|
514
|
+
* its note, the distances it grouped with, and it rests on the beliefs
|
|
515
|
+
* the derivations of the versions are held with. An edit of the set
|
|
516
|
+
* that nobody has motivated is the program's, typed and all, and a
|
|
517
|
+
* later proposal groups it again. The run gives who ran the program and
|
|
518
|
+
* when (`Run`).
|
|
498
519
|
* @category Operations
|
|
499
520
|
*/
|
|
500
|
-
export const adoptEdits = (versionIds, options = {}) => reading(edition => {
|
|
521
|
+
export const adoptEdits = (versionIds, options = {}, run = {}) => reading(edition => {
|
|
501
522
|
const targets = (versionIds ?? edition.versions.map(version => version.id))
|
|
502
523
|
.map(id => versionIn(edition, id))
|
|
503
524
|
.filter((version) => version !== undefined && predecessorOf(edition, version.id) !== undefined);
|
|
504
|
-
const
|
|
505
|
-
|
|
525
|
+
const regrouped = targets.map(version => ({ version, ...adoptedEditsOf(edition, version, options) }));
|
|
526
|
+
const set = editSetOf(edition, regrouped, options, run);
|
|
527
|
+
const adopted = new Map(regrouped.flatMap(({ version, held, proposed }) => {
|
|
528
|
+
const edits = [...held, ...proposed.map(edit => memberOf(set.id, edit))];
|
|
506
529
|
const stated = editsOf(version);
|
|
507
530
|
if (edits.length === stated.length && edits.every((edit, index) => edit === stated[index]))
|
|
508
531
|
return [];
|
|
509
532
|
return [[version.id, { ...version, edits, motivations: declaring(version.motivations, edits) }]];
|
|
510
533
|
}));
|
|
534
|
+
const sets = keptSets({
|
|
535
|
+
...edition,
|
|
536
|
+
versions: edition.versions.map(version => adopted.get(version.id) ?? version)
|
|
537
|
+
}, [set]);
|
|
511
538
|
// Each version is put in place whole: one that an operation before put
|
|
512
539
|
// on the same draft, as adopting a stemma does, is no draft to write to.
|
|
513
540
|
return draft => {
|
|
@@ -516,5 +543,27 @@ export const adoptEdits = (versionIds, options = {}) => reading(edition => {
|
|
|
516
543
|
if (changed)
|
|
517
544
|
draft.versions[index] = changed;
|
|
518
545
|
});
|
|
546
|
+
if (sets)
|
|
547
|
+
draft.propositionSets = sets;
|
|
548
|
+
else
|
|
549
|
+
delete draft.propositionSets;
|
|
519
550
|
};
|
|
520
551
|
});
|
|
552
|
+
/**
|
|
553
|
+
* The set the run of `proposeEdits` concluded: the edits it proposed for
|
|
554
|
+
* the versions, held with the belief the run gives them. Its premises
|
|
555
|
+
* are the beliefs the derivations of those versions are held with, since
|
|
556
|
+
* what a version changes is read against the version it derives from: a
|
|
557
|
+
* proposed stemma's, where the derivation belongs to one.
|
|
558
|
+
*/
|
|
559
|
+
const editSetOf = (edition, regrouped, options, run) => {
|
|
560
|
+
const settings = settingsOf(options);
|
|
561
|
+
const proposing = regrouped.filter(({ proposed }) => proposed.length > 0);
|
|
562
|
+
const premises = [...new Set(proposing.flatMap(({ version }) => {
|
|
563
|
+
const derivation = principalDerivationOf(version);
|
|
564
|
+
const belief = derivation && beliefIn(edition, derivation);
|
|
565
|
+
return belief ? [belief.id] : [];
|
|
566
|
+
}))];
|
|
567
|
+
const used = [...new Set(proposing.flatMap(({ version }) => [predecessorOf(edition, version.id).id, version.id]))];
|
|
568
|
+
return recorded('edits', [settings, premises, proposing.map(({ version, proposed }) => [version.id, proposed.map(edit => edit.id)])], { note: noteOn(settings), premises, used, applies: [editRules.onePlace] }, run);
|
|
569
|
+
};
|
package/lib/filiation/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { defaultCosts, type Costs, type Scored, type Support } from "./search.js
|
|
|
9
9
|
export { transfersOf, systemAbove } from "./systems.js";
|
|
10
10
|
export { proposeStemma, defaultMovedWithin, type FiliationOptions, type ProposedVersion, type StemmaProposal, type Unpinned } from "./proposal.js";
|
|
11
11
|
export { adoptionOf, adoptStemma, type Adoption } from "./adoption.js";
|
|
12
|
+
export { stemmaRules, editRules, type Run } from "./record.js";
|
|
12
13
|
export { stemmaChanges, type VersionChange } from "./changes.js";
|
|
13
14
|
export { sharedTexts, differencesBetween, defaultSameText, type SharedText, type KeptApart } from "./texts.js";
|
|
14
15
|
export { proposeEdits, adoptEdits, isRegroupable, defaultShiftWithin, defaultPairWithin, defaultResizeWithin, defaultSamePlaceWithin, type EditOptions, type EditGrouping, type ProposedEdit } from "./edits.js";
|
package/lib/filiation/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export { defaultCosts } from "./search.js";
|
|
|
9
9
|
export { transfersOf, systemAbove } from "./systems.js";
|
|
10
10
|
export { proposeStemma, defaultMovedWithin } from "./proposal.js";
|
|
11
11
|
export { adoptionOf, adoptStemma } from "./adoption.js";
|
|
12
|
+
export { stemmaRules, editRules } from "./record.js";
|
|
12
13
|
export { stemmaChanges } from "./changes.js";
|
|
13
14
|
export { sharedTexts, differencesBetween, defaultSameText } from "./texts.js";
|
|
14
15
|
export { proposeEdits, adoptEdits, isRegroupable, defaultShiftWithin, defaultPairWithin, defaultResizeWithin, defaultSamePlaceWithin } from "./edits.js";
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { ActorAssignment, Assumption, Certainty, PropositionSet, TimeSpan } from "../model/Assumption.js";
|
|
2
|
+
import { Edition } from "../model/Edition.js";
|
|
3
|
+
/**
|
|
4
|
+
* The published rules the proposal of a stemma follows, by IRI, each
|
|
5
|
+
* under the cost or the step that carries it out. They are the
|
|
6
|
+
* editorial principles of the edition of WM 225, which are suggested
|
|
7
|
+
* for the filiation of piano rolls at large.
|
|
8
|
+
* @category Filiation
|
|
9
|
+
*/
|
|
10
|
+
export declare const stemmaRules: {
|
|
11
|
+
/** An addition at the same place is not made twice independently, so a reading arising costs ten times one lost. */
|
|
12
|
+
readonly sharedAddition: 'https://welte225.org/rules#shared-addition';
|
|
13
|
+
/** Redundancies are cleaned up in two branches independently, so losing one counts for less. */
|
|
14
|
+
readonly independentCleanup: 'https://welte225.org/rules#independent-cleanup';
|
|
15
|
+
/** A re-cut for another system was an editorial pass of its own, so what it lacks counts for less. */
|
|
16
|
+
readonly recutLoss: 'https://welte225.org/rules#recut-loss';
|
|
17
|
+
/** A copy may fail to show a reading of the version it carries. */
|
|
18
|
+
readonly copyDeviation: 'https://welte225.org/rules#copy-deviation';
|
|
19
|
+
/** A lost state is posited only where shared additions require it. */
|
|
20
|
+
readonly lostStates: 'https://welte225.org/rules#lost-states';
|
|
21
|
+
/** Copies of one system that differ in a few readings carry one text. */
|
|
22
|
+
readonly oneText: 'https://welte225.org/rules#one-text';
|
|
23
|
+
/** Later versions are the more refined, which orients the root. */
|
|
24
|
+
readonly refinement: 'https://welte225.org/rules#refinement';
|
|
25
|
+
/** A version derives from one exemplar, so a stemma is a tree. */
|
|
26
|
+
readonly noContamination: 'https://welte225.org/rules#no-contamination';
|
|
27
|
+
};
|
|
28
|
+
/**
|
|
29
|
+
* The published rules the proposal of edits follows, by IRI.
|
|
30
|
+
* @category Filiation
|
|
31
|
+
*/
|
|
32
|
+
export declare const editRules: {
|
|
33
|
+
/** An edit stays at one place: it runs across no command both texts keep. */
|
|
34
|
+
readonly onePlace: 'https://welte225.org/rules#one-place';
|
|
35
|
+
};
|
|
36
|
+
/**
|
|
37
|
+
* What is known of a run of a program beside what it computed: who ran
|
|
38
|
+
* it, when, how firmly what it concluded is held, and by which
|
|
39
|
+
* published rules. An adoption records the run with it; whatever is left
|
|
40
|
+
* out is not stated.
|
|
41
|
+
* @category Filiation
|
|
42
|
+
*/
|
|
43
|
+
export interface Run {
|
|
44
|
+
/** Who ran the program and so holds what it concluded: the editor. */
|
|
45
|
+
actor?: ActorAssignment;
|
|
46
|
+
/** When it ran. */
|
|
47
|
+
date?: TimeSpan;
|
|
48
|
+
/** How firmly what it concluded is held: likely, where nothing else is said. */
|
|
49
|
+
certainty?: Certainty;
|
|
50
|
+
/** The rules it followed, by IRI, in place of those the library names (`stemmaRules`, `editRules`). */
|
|
51
|
+
applies?: string[];
|
|
52
|
+
}
|
|
53
|
+
/** What a recorded run concluded besides its statements: what the inference says of itself. */
|
|
54
|
+
export interface Concluded {
|
|
55
|
+
/** What the run is, and what it was computed with, in words. */
|
|
56
|
+
note: string;
|
|
57
|
+
premises: string[];
|
|
58
|
+
used: string[];
|
|
59
|
+
applies: string[];
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The proposition set of a run, with its belief and the inference it
|
|
63
|
+
* rests on. Its id follows from what it is given, so that recording the
|
|
64
|
+
* same run again gives the same set.
|
|
65
|
+
*/
|
|
66
|
+
export declare const recorded: (kind: string, identity: unknown, concluded: Concluded, run: Run) => PropositionSet;
|
|
67
|
+
/** The statement as one of the set: its annotation names the set, and it carries no belief of its own. */
|
|
68
|
+
export declare const memberOf: <T extends Assumption>(set: string, statement: T) => T;
|
|
69
|
+
/**
|
|
70
|
+
* The sets the edition keeps, with those given added where it holds none
|
|
71
|
+
* with their id: a set already recorded stays as it was recorded, by
|
|
72
|
+
* whom and when it was first concluded. Kept are the sets a statement
|
|
73
|
+
* belongs to, and those whose belief the reasons of another kept
|
|
74
|
+
* statement or set name as a premise. A set nothing names any longer is
|
|
75
|
+
* the record of a run whose conclusions have all been replaced.
|
|
76
|
+
*/
|
|
77
|
+
export declare const keptSets: (edition: Edition, adding: readonly PropositionSet[]) => PropositionSet[] | undefined;
|
|
78
|
+
/** A distance in millimetres as a note gives it. */
|
|
79
|
+
export declare const inMillimetres: (value: number) => string;
|