linked-rolls 0.26.0 → 0.28.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 CHANGED
@@ -60,24 +60,40 @@ derivation loads as a list of one.
60
60
 
61
61
  `readFrom` states what a copy's features were read from: the roll
62
62
  itself, a `scan` of it, an `analysis` somebody else measured on a
63
- scan, an `emulation` in which the roll has already been read into
64
- notes and commands, or a `recording` captured while the copy was
65
- played. Beside the kind it holds who carried the capture out, on what
66
- device, when, and a note. What is not known is left out.
63
+ scan, a `reading` of the timed switches of a roll reader, an
64
+ `emulation` in which the roll has already been read into notes and
65
+ commands, or a `recording` of the copy being played on an instrument.
66
+ Beside the kind it holds who carried the capture out, on what device,
67
+ with what software, on which instrument a recorded copy was played,
68
+ when, and a note. What is not known is left out.
67
69
 
68
70
  This says where the numbers of the edition come from and nothing
69
71
  about the state of the paper, which is a condition of the copy. In
70
72
  RDF it is `reo:capture`, an activity typed by what it read from; the
71
73
  file it produced is `crmdig:L11 had output`, the machine it ran on
72
- `crmdig:L12 happened on device`.
74
+ `crmdig:L12 happened on device`, the software `crmdig:L23 used
75
+ software or firmware`, the instrument `crm:P16 used specific object`.
73
76
 
74
77
  Nothing records that a copy is doubtful. `reservationsAbout` works
75
78
  out from what a copy states what the edition cannot vouch for in it:
76
79
  that it names no source, that the making of its source is
77
- undocumented, that its features are somebody else's reading, that its
78
- source bears no physical evidence, that no measuring software is
79
- recorded, that it is not calibrated. A reservation goes away when the
80
- gap it names is filled.
80
+ undocumented, that no software or instrument is named for an
81
+ emulation or a recording, that its features are somebody else's
82
+ reading, that its source bears no physical evidence, that no measuring
83
+ software is recorded, that it is not calibrated, that nobody is known
84
+ to hold it. A reservation goes away when the gap it names is filled.
85
+
86
+ A copy nobody can reach, such as one known only from a recording, has
87
+ no features to carry the symbols of a version. It states instead
88
+ which versions it is held to carry, in `carries`, each under a belief
89
+ that says how certainly and why. `witnessesOf` gathers the copies of a
90
+ version, whether their features carry its symbols or they state that
91
+ they carry it, a statement with the belief it rests on;
92
+ `versionsWitnessedBy` gathers the same from the side of a copy.
93
+ `carriageProblems` reports a statement made beside features that carry
94
+ symbols already, or one naming a version the edition lacks. A copy may
95
+ carry a `siglum`, as a version does; one without is named by its
96
+ keeper.
81
97
 
82
98
  ```ts
83
99
  import { reservationsAbout, stateSource } from 'linked-rolls'
@@ -44,11 +44,18 @@ export interface MeaningComprehension extends Argumentation<'meaningComprehensio
44
44
  */
45
45
  export interface Inference extends Argumentation<'inference'> {
46
46
  /**
47
- * References (by `@id`) to the beliefs or facts
47
+ * References (by `@id`) to the beliefs
48
48
  * from which the conclusion is drawn.
49
49
  * @see crminf:J1 used as premise
50
50
  */
51
51
  premises: string[];
52
+ /**
53
+ * References (by `@id`) to what the inference worked on besides
54
+ * beliefs, such as the versions it compared or the analysis output
55
+ * it cites.
56
+ * @see crm:P16 used specific object
57
+ */
58
+ used?: string[];
52
59
  }
53
60
  /**
54
61
  * A belief adoption adopts someone else's belief. This type is used
@@ -14,7 +14,7 @@ export const getAt = (path, obj) => {
14
14
  return node;
15
15
  };
16
16
  /** Keys under which an object names others by id, singly or in a list. */
17
- const referenceKeys = ['delete', 'comprehends', 'motivation'];
17
+ const referenceKeys = ['delete', 'comprehends', 'motivation', 'premises', 'used'];
18
18
  const isObject = (v) => v !== null && typeof v === "object";
19
19
  /** What a reference may state besides the id: a belief about it, and the tolerance a derivation was collated at. */
20
20
  const referenceOwnKeys = new Set(['id', '@annotation', 'collationTolerance']);
@@ -1,20 +1,22 @@
1
- import { ActorAssignment, DateAssignment } from "./Assumption";
2
- import { Concept } from "./Agent";
1
+ import { ActorAssignment, DateAssignment, ObjectAssumption } from "./Assumption";
2
+ import { Concept, Named } from "./Agent";
3
+ import { ConditionState } from "./ConditionState";
3
4
  import { WithNote } from "./utils";
4
5
  /**
5
6
  * What a copy's features were read from. The kinds run from the paper
6
- * itself to a file in which the roll has already been read into notes
7
- * and commands.
7
+ * itself, through files in which the roll has already been read into
8
+ * notes and commands, to a sound recording of the copy being played.
8
9
  */
9
- export declare const sourceKinds: readonly ['roll', 'scan', 'analysis', 'emulation', 'recording'];
10
+ export declare const sourceKinds: readonly ['roll', 'scan', 'analysis', 'reading', 'emulation', 'recording'];
10
11
  export type SourceKind = typeof sourceKinds[number];
11
12
  /** How each kind of source is referred to in prose. */
12
13
  export declare const sourceLabels: Record<SourceKind, string>;
13
14
  /**
14
- * Whether a source of this kind gives positions that were measured.
15
- * An emulation and a recording give the reading somebody else made:
16
- * the roll has been turned into notes and commands already, and
17
- * turning those back into holes reconstructs them.
15
+ * Whether a source of this kind gives positions that were measured. A
16
+ * roll reader measures when each perforation passes its bar. An
17
+ * emulation and a recording give the reading somebody else made: the
18
+ * roll has been turned into notes and commands already, and turning
19
+ * those back into holes reconstructs them.
18
20
  */
19
21
  export declare const isMeasured: (kind: SourceKind) => boolean;
20
22
  /**
@@ -23,11 +25,38 @@ export declare const isMeasured: (kind: SourceKind) => boolean;
23
25
  * be observed on the roll and on an image of it, and on nothing else.
24
26
  */
25
27
  export declare const bearsPhysicalEvidence: (kind: SourceKind) => boolean;
28
+ /**
29
+ * Software a capture ran, such as the transcriber that read a recording
30
+ * into notes or the emulator that wrote a MIDI file.
31
+ * @see crmdig:D14 Software
32
+ */
33
+ export interface Software {
34
+ /**
35
+ * @see rdfs:label
36
+ */
37
+ name: string;
38
+ /**
39
+ * The release, or the model checkpoint of a transcriber.
40
+ * @see owl:versionInfo
41
+ */
42
+ version?: string;
43
+ }
44
+ /**
45
+ * An instrument a copy was played on to be recorded. How it was
46
+ * regulated, where that is known, is its condition.
47
+ * @see crm:E22 Human-Made Object
48
+ */
49
+ export interface Instrument extends Named {
50
+ /**
51
+ * @see crm:P44 has condition
52
+ */
53
+ condition?: ObjectAssumption<ConditionState<'general'>>;
54
+ }
26
55
  /**
27
56
  * The capture by which a copy's features became data: what they were
28
- * read from, who read them, on what device and when. It states where
29
- * the numbers of the edition come from and says nothing about the
30
- * state of the paper, which is a condition.
57
+ * read from, who read them, on what device, with what software and
58
+ * when. It states where the numbers of the edition come from and says
59
+ * nothing about the state of the paper, which is a condition.
31
60
  *
32
61
  * What is not known is left out. A reader is told about the gap by
33
62
  * `reservationsAbout`, which works out what a copy cannot vouch for
@@ -56,6 +85,17 @@ export interface FeatureSource extends WithNote {
56
85
  * @see crmdig:L12 happened on device
57
86
  */
58
87
  device?: Concept;
88
+ /**
89
+ * The software the capture ran, each with its version.
90
+ * @see crmdig:L23 used software or firmware
91
+ */
92
+ software?: Software[];
93
+ /**
94
+ * The instrument the copy was played on, where the capture is a
95
+ * recording of it.
96
+ * @see crm:P16 used specific object
97
+ */
98
+ instrument?: ObjectAssumption<Instrument>;
59
99
  /**
60
100
  * When the capture took place.
61
101
  * @format date
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * What a copy's features were read from. The kinds run from the paper
3
- * itself to a file in which the roll has already been read into notes
4
- * and commands.
3
+ * itself, through files in which the roll has already been read into
4
+ * notes and commands, to a sound recording of the copy being played.
5
5
  */
6
6
  export const sourceKinds = [
7
7
  'roll',
8
8
  'scan',
9
9
  'analysis',
10
+ 'reading',
10
11
  'emulation',
11
12
  'recording'
12
13
  ];
@@ -15,16 +16,18 @@ export const sourceLabels = {
15
16
  roll: 'the roll itself',
16
17
  scan: 'a scan of the roll',
17
18
  analysis: 'a hole analysis of a scan',
19
+ reading: 'the timed switches of a roll reader',
18
20
  emulation: 'an emulated MIDI file',
19
- recording: 'a MIDI recording of the copy being played'
21
+ recording: 'a sound recording of the copy being played'
20
22
  };
21
- const measured = ['roll', 'scan', 'analysis'];
23
+ const measured = ['roll', 'scan', 'analysis', 'reading'];
22
24
  const physical = ['roll', 'scan'];
23
25
  /**
24
- * Whether a source of this kind gives positions that were measured.
25
- * An emulation and a recording give the reading somebody else made:
26
- * the roll has been turned into notes and commands already, and
27
- * turning those back into holes reconstructs them.
26
+ * Whether a source of this kind gives positions that were measured. A
27
+ * roll reader measures when each perforation passes its bar. An
28
+ * emulation and a recording give the reading somebody else made: the
29
+ * roll has been turned into notes and commands already, and turning
30
+ * those back into holes reconstructs them.
28
31
  */
29
32
  export const isMeasured = (kind) => measured.includes(kind);
30
33
  /**
package/lib/RollCopy.d.ts CHANGED
@@ -3,7 +3,7 @@ import { AnySymbol } from "./Symbol";
3
3
  import { TrackerBar } from "./TrackerBar";
4
4
  import { TrackCalibration } from "./TrackCalibration";
5
5
  import { AnyFeature } from "./Feature";
6
- import { ActorAssignment, DateAssignment, ObjectAssumption } from "./Assumption";
6
+ import { ActorAssignment, DateAssignment, ObjectAssumption, ReferenceAssumption } from "./Assumption";
7
7
  import { WithId, WithType } from "./utils";
8
8
  import { Agent, Concept } from "./Agent";
9
9
  import { FeatureSource } from "./FeatureSource";
@@ -274,10 +274,17 @@ export interface RollCopy extends WithType<'RollCopy'>, WithId {
274
274
  */
275
275
  conditions: RollConditionAssignment[];
276
276
  /**
277
- * The institution or person holding this copy.
277
+ * A short siglum to identify the copy, e.g. "W1" or "S2". A copy
278
+ * without one is named by its keeper.
279
+ * @see reo:siglum
280
+ */
281
+ siglum?: string;
282
+ /**
283
+ * The institution or person holding this copy. Left out where it is
284
+ * not known, as for a copy known only from a recording.
278
285
  * @see crm:P50 has current keeper
279
286
  */
280
- keeper: Agent;
287
+ keeper?: Agent;
281
288
  /**
282
289
  * The physical features found on this copy, with shift
283
290
  * and stretch already applied when `ops` says so.
@@ -300,6 +307,13 @@ export interface RollCopy extends WithType<'RollCopy'>, WithId {
300
307
  * @see reo:capture
301
308
  */
302
309
  readFrom?: FeatureSource;
310
+ /**
311
+ * The versions this copy is held to carry, where its features are not
312
+ * read into symbols, as for a copy known only from a recording. Each
313
+ * statement carries the belief it rests on.
314
+ * @see crm:P128 carries
315
+ */
316
+ carries?: ReferenceAssumption[];
303
317
  }
304
318
  /**
305
319
  * Whether the condition is the stretch or shrinkage of the paper. A
package/lib/Version.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Edit } from "./Edit";
2
2
  import { Concept } from "./Agent";
3
- import { ActorAssignment, Certainty, DateAssignment, ReferenceAssumption } from "./Assumption";
3
+ import { ActorAssignment, Belief, Certainty, DateAssignment, ReferenceAssumption } from "./Assumption";
4
4
  import { CollationTolerance } from "./Collation";
5
5
  import { AnySymbol } from "./Symbol";
6
6
  import { WithId, WithNote, WithType } from "./utils";
@@ -136,10 +136,14 @@ export declare const editsOf: (version: Readonly<Version>) => Edit[];
136
136
  export declare const insertedBy: (version: Readonly<Version>) => AnySymbol[];
137
137
  /** The ids of the symbols the version's edits delete. */
138
138
  export declare const deletedBy: (version: Readonly<Version>) => string[];
139
- /** The parents the version names, each with the certainty its derivation is held with. */
139
+ /**
140
+ * The parents the version names, in the order of `basedOn`, each with the
141
+ * certainty its derivation is held with and the belief it rests on.
142
+ */
140
143
  export declare const derivationsOf: (version: Readonly<Version>) => {
141
144
  parent: string;
142
145
  certainty: Certainty;
146
+ belief?: Belief;
143
147
  }[];
144
148
  /**
145
149
  * The derivation the version's text is read against: the first of those
package/lib/Version.js CHANGED
@@ -19,8 +19,14 @@ export const editsOf = (version) => version.edits ?? [];
19
19
  export const insertedBy = (version) => editsOf(version).flatMap(edit => edit.insert ?? []);
20
20
  /** The ids of the symbols the version's edits delete. */
21
21
  export const deletedBy = (version) => editsOf(version).flatMap(edit => edit.delete ?? []);
22
- /** The parents the version names, each with the certainty its derivation is held with. */
23
- export const derivationsOf = (version) => (version.basedOn ?? []).map(derivation => ({ parent: idOf(derivation), certainty: certaintyOf(derivation) }));
22
+ /**
23
+ * The parents the version names, in the order of `basedOn`, each with the
24
+ * certainty its derivation is held with and the belief it rests on.
25
+ */
26
+ export const derivationsOf = (version) => (version.basedOn ?? []).map(derivation => {
27
+ const belief = derivation['@annotation']?.belief;
28
+ return { parent: idOf(derivation), certainty: certaintyOf(derivation), ...(belief && { belief }) };
29
+ });
24
30
  const rankOf = (derivation) => certainties.indexOf(certaintyOf(derivation));
25
31
  /**
26
32
  * The derivation the version's text is read against: the first of those
@@ -22,6 +22,12 @@ export type EditionOp = (draft: Draft<Edition>) => void;
22
22
  * system the copy was cut for.
23
23
  */
24
24
  export declare const createVersion: (siglum: string, copy: RollCopy) => EditionOp;
25
+ /**
26
+ * Puts the copy into the edition without a version of its own, as for a
27
+ * copy whose features are not read into symbols: one known only from a
28
+ * recording states instead which versions it carries.
29
+ */
30
+ export declare const addCopy: (copy: RollCopy) => EditionOp;
25
31
  /**
26
32
  * Shifts and then scales the copy's features into line with another
27
33
  * copy's, and puts the scale down to what the reading says: the paper,
@@ -38,6 +44,18 @@ export declare const unalignCopy: (copyId: string) => EditionOp;
38
44
  export declare const stateSource: (copyId: string, source: FeatureSource) => EditionOp;
39
45
  /** Takes back the statement, leaving the copy silent about its source again. */
40
46
  export declare const clearSource: (copyId: string) => EditionOp;
47
+ /** Gives the copy the siglum it is referred to by, or takes it away where the siglum given is blank. */
48
+ export declare const nameCopy: (copyId: string, siglum: string) => EditionOp;
49
+ /**
50
+ * States that the copy carries the version, under the belief given. It
51
+ * is meant for a copy whose features are not read into symbols, such as
52
+ * one known only from a recording, and `carriageProblems` reports it
53
+ * where features carry symbols already. A second statement about one
54
+ * version is none.
55
+ */
56
+ export declare const stateCarriage: (copyId: string, versionId: string, belief?: Belief) => EditionOp;
57
+ /** Takes back the copy's statement that it carries the version. */
58
+ export declare const clearCarriage: (copyId: string, versionId: string) => EditionOp;
41
59
  /**
42
60
  * Adds a general condition to the copy, beside whatever is stated of it
43
61
  * already. The other condition a copy may be in, a paper stretch, is
@@ -117,7 +135,8 @@ export declare const collateSymbols: (view: EditionView, versionId: string, symb
117
135
  export declare const detachVersion: (view: EditionView, versionId: string) => EditionOp;
118
136
  /**
119
137
  * Takes the version out. Whatever read its text against it comes to
120
- * stand on its own, and a hypothesis that something derives from it goes.
138
+ * stand on its own, a hypothesis that something derives from it goes,
139
+ * and so does a copy's statement that it carries the version.
121
140
  */
122
141
  export declare const removeVersion: (view: EditionView, versionId: string) => EditionOp;
123
142
  /** Takes the symbols out of the version's own insertions, and the edits that had nothing else. */
package/lib/editionOps.js CHANGED
@@ -88,6 +88,14 @@ export const createVersion = (siglum, copy) => draft => {
88
88
  motivations: []
89
89
  });
90
90
  };
91
+ /**
92
+ * Puts the copy into the edition without a version of its own, as for a
93
+ * copy whose features are not read into symbols: one known only from a
94
+ * recording states instead which versions it carries.
95
+ */
96
+ export const addCopy = (copy) => draft => {
97
+ draft.copies.push(copy);
98
+ };
91
99
  /** States what the scale is put down to, in place of an earlier reading. */
92
100
  const readScale = (copy, reading) => {
93
101
  if (reading.cause === 'paper') {
@@ -127,6 +135,47 @@ export const stateSource = (copyId, source) => onCopy(copyId, copy => {
127
135
  export const clearSource = (copyId) => onCopy(copyId, copy => {
128
136
  copy.readFrom = undefined;
129
137
  });
138
+ /** Gives the copy the siglum it is referred to by, or takes it away where the siglum given is blank. */
139
+ export const nameCopy = (copyId, siglum) => onCopy(copyId, copy => {
140
+ const trimmed = siglum.trim();
141
+ if (trimmed)
142
+ copy.siglum = trimmed;
143
+ else
144
+ delete copy.siglum;
145
+ });
146
+ /** A reference under the belief given, where one is given. */
147
+ const referenceHeld = (id, belief) => ({ ...assignReference(id), ...(belief && { '@annotation': { id: v4(), belief } }) });
148
+ /** The references less those that match, or nothing where none is left. */
149
+ const withoutReferences = (references, matches) => {
150
+ const kept = references.filter(reference => !matches(reference));
151
+ return kept.length > 0 ? kept : undefined;
152
+ };
153
+ /** Takes out the copy's statements that match, and the list itself where none is left. */
154
+ const dropStatements = (copy, matches) => {
155
+ const statements = stateOf(copy).carries;
156
+ if (!statements?.some(matches))
157
+ return;
158
+ const kept = withoutReferences(statements, matches);
159
+ if (kept)
160
+ copy.carries = kept;
161
+ else
162
+ delete copy.carries;
163
+ };
164
+ /**
165
+ * States that the copy carries the version, under the belief given. It
166
+ * is meant for a copy whose features are not read into symbols, such as
167
+ * one known only from a recording, and `carriageProblems` reports it
168
+ * where features carry symbols already. A second statement about one
169
+ * version is none.
170
+ */
171
+ export const stateCarriage = (copyId, versionId, belief) => onCopy(copyId, copy => {
172
+ const statements = stateOf(copy).carries ?? [];
173
+ if (statements.some(statement => idOf(statement) === versionId))
174
+ return;
175
+ copy.carries = [...statements, referenceHeld(versionId, belief)];
176
+ });
177
+ /** Takes back the copy's statement that it carries the version. */
178
+ export const clearCarriage = (copyId, versionId) => onCopy(copyId, copy => dropStatements(copy, statement => idOf(statement) === versionId));
130
179
  /**
131
180
  * Adds a general condition to the copy, beside whatever is stated of it
132
181
  * already. The other condition a copy may be in, a paper stretch, is
@@ -484,8 +533,8 @@ const dropDerivations = (version, matches) => {
484
533
  const derivations = stateOf(version).basedOn;
485
534
  if (!derivations?.some(matches))
486
535
  return;
487
- const kept = derivations.filter(derivation => !matches(derivation));
488
- if (kept.length > 0)
536
+ const kept = withoutReferences(derivations, matches);
537
+ if (kept)
489
538
  version.basedOn = kept;
490
539
  else
491
540
  delete version.basedOn;
@@ -572,15 +621,18 @@ export const detachVersion = (view, versionId) => {
572
621
  };
573
622
  /**
574
623
  * Takes the version out. Whatever read its text against it comes to
575
- * stand on its own, and a hypothesis that something derives from it goes.
624
+ * stand on its own, a hypothesis that something derives from it goes,
625
+ * and so does a copy's statement that it carries the version.
576
626
  */
577
627
  export const removeVersion = (view, versionId) => {
578
628
  const detachments = view.edition.versions
579
629
  .filter(version => readsAgainst(version, versionId))
580
630
  .map(version => detachVersion(view, version.id));
631
+ const namesIt = (reference) => idOf(reference) === versionId;
581
632
  return draft => {
582
633
  detachments.forEach(detach => detach(draft));
583
- draft.versions.forEach(version => dropDerivations(version, derivation => idOf(derivation) === versionId));
634
+ draft.versions.forEach(version => dropDerivations(version, namesIt));
635
+ draft.copies.forEach(copy => dropStatements(copy, namesIt));
584
636
  draft.versions = without(draft.versions, version => version.id === versionId);
585
637
  };
586
638
  };
@@ -614,10 +666,7 @@ export const stateDerivation = (versionId, parentId, belief) => onVersion(versio
614
666
  const derivations = stateOf(version).basedOn ?? [];
615
667
  if (parentId === versionId || derivations.some(derivation => idOf(derivation) === parentId))
616
668
  return;
617
- version.basedOn = [
618
- ...derivations,
619
- { ...assignReference(parentId), ...(belief && { '@annotation': { id: v4(), belief } }) }
620
- ];
669
+ version.basedOn = [...derivations, referenceHeld(parentId, belief)];
621
670
  });
622
671
  /** Takes back the hypothesis that the version derives from the parent; the principal derivation goes with `detachVersion`. */
623
672
  export const clearDerivation = (versionId, parentId) => onVersion(versionId, version => {
package/lib/index.d.ts CHANGED
@@ -18,6 +18,7 @@ export * from './ReproducingSystem';
18
18
  export * from './FeatureSource';
19
19
  export * from './RollCopy';
20
20
  export * from './reservations';
21
+ export * from './witnesses';
21
22
  export * from './alignment';
22
23
  export * from './Edition';
23
24
  export * from './EditionView';
package/lib/index.js CHANGED
@@ -18,6 +18,7 @@ export * from './ReproducingSystem';
18
18
  export * from './FeatureSource';
19
19
  export * from './RollCopy';
20
20
  export * from './reservations';
21
+ export * from './witnesses';
21
22
  export * from './alignment';
22
23
  export * from './Edition';
23
24
  export * from './EditionView';
package/lib/migrate.js CHANGED
@@ -72,7 +72,24 @@ const withScale = (node) => {
72
72
  /** A derivation written as a single one, before a version could name several. */
73
73
  const isSingleDerivation = (basedOn) => basedOn !== null && typeof basedOn === 'object' && !Array.isArray(basedOn);
74
74
  const withDerivationList = (node) => isSingleDerivation(node.basedOn) ? { ...node, basedOn: [node.basedOn] } : node;
75
- const migrateNode = (node) => [withRenamedKeys, withTypology, withReferences, withKeeper, withProductionNodes, withScale, withDerivationList]
75
+ /**
76
+ * A copy read on a roll reader was stated as a recording before a sound
77
+ * recording could be one. A sound recording gives no holes, so a recording
78
+ * with holes is a reading.
79
+ */
80
+ const withReadingKind = (node) => node.readFrom?.kind === 'recording' && Array.isArray(node.features)
81
+ && node.features.some((feature) => feature?.['@type'] === 'Hole')
82
+ ? { ...node, readFrom: { ...node.readFrom, kind: 'reading' } }
83
+ : node;
84
+ /** A keeper nobody could name was written as an empty one before a copy could leave it out. */
85
+ const withoutEmptyKeeper = (node) => {
86
+ if (node.keeper?.name !== '' || node.keeper.sameAs?.length)
87
+ return node;
88
+ const { keeper: _unnamed, ...rest } = node;
89
+ return rest;
90
+ };
91
+ const migrateNode = (node) => [withRenamedKeys, withTypology, withReferences, withKeeper, withoutEmptyKeeper, withProductionNodes, withScale,
92
+ withDerivationList, withReadingKind]
76
93
  .reduce((result, step) => step(result), node);
77
94
  /** The items each walked, or the very same list where the walk changed none. */
78
95
  const walked = (items) => {
@@ -147,13 +147,12 @@ export function readFromPhillipsEroll(buffer, { system = welteT100, controlOffse
147
147
  id: v4(),
148
148
  ops: [],
149
149
  conditions: [],
150
- keeper: { name: '', sameAs: [] },
151
150
  measurements: {},
152
151
  production: { system: systemOf(system) },
153
152
  modifications: [],
154
153
  features,
155
154
  readFrom: {
156
- kind: 'recording',
155
+ kind: 'reading',
157
156
  actor: assignObject({ name: 'Phillips, Peter', sameAs: [] }),
158
157
  note: 'Read on Phillips’s pneumatic roll reader. The places are elapsed time put back onto the paper, and a hole runs as long as its switch stayed open, which is longer than the perforation.'
159
158
  }
@@ -113,7 +113,6 @@ export function readFromSpencerBar(buffer, { rowsPerInch = SPENCER_ROWS_PER_INCH
113
113
  id: v4(),
114
114
  ops: [],
115
115
  conditions: [],
116
- keeper: { name: '', sameAs: [] },
117
116
  measurements: {},
118
117
  production: { system: systemOf(system) },
119
118
  modifications: [],
@@ -157,7 +157,6 @@ export function readFromStanfordAton(atonString, { trackShift, system = welteT10
157
157
  id: v4(),
158
158
  ops: [],
159
159
  conditions: [],
160
- keeper: { name: '', sameAs: [] },
161
160
  production: { system: systemOf(system) },
162
161
  modifications: [],
163
162
  ...((scan ?? stanford) && { scan: scan ?? stanford?.scan }),
@@ -1,6 +1,7 @@
1
1
  import { RollCopy } from "./RollCopy";
2
2
  import { Version } from "./Version";
3
- export declare const reservationTypes: readonly ['source-not-stated', 'source-undocumented', 'features-interpreted', 'no-physical-evidence', 'measurement-undocumented', 'not-calibrated', 'system-unknown'];
3
+ import { EditionView } from "./EditionView";
4
+ export declare const reservationTypes: readonly ['source-not-stated', 'source-undocumented', 'software-not-named', 'instrument-not-named', 'features-interpreted', 'no-physical-evidence', 'measurement-undocumented', 'not-calibrated', 'system-unknown', 'keeper-unknown'];
4
5
  export type ReservationType = typeof reservationTypes[number];
5
6
  /**
6
7
  * Something an edition cannot vouch for in one of its copies or
@@ -20,10 +21,10 @@ export interface Reservation<T extends string = ReservationType> {
20
21
  /**
21
22
  * What the edition cannot vouch for in a copy, in the order the
22
23
  * checks are listed: where its features came from first, then what
23
- * the measurement leaves open.
24
+ * the measurement leaves open, then who holds it.
24
25
  */
25
26
  export declare const reservationsAbout: (copy: RollCopy) => Reservation[];
26
- export declare const versionReservationTypes: readonly ['text-not-stated', 'type-not-stated'];
27
+ export declare const versionReservationTypes: readonly ['text-not-stated', 'type-not-stated', 'witnessed-by-statement-only'];
27
28
  export type VersionReservationType = typeof versionReservationTypes[number];
28
29
  /** What the edition cannot vouch for in a version, in the order the checks are listed. */
29
- export declare const reservationsAboutVersion: (version: Readonly<Version>) => Reservation<VersionReservationType>[];
30
+ export declare const reservationsAboutVersion: (view: EditionView, version: Readonly<Version>) => Reservation<VersionReservationType>[];
@@ -1,14 +1,18 @@
1
1
  import { bearsPhysicalEvidence, isMeasured, sourceLabels } from "./FeatureSource";
2
2
  import { calibrationOf } from "./RollCopy";
3
3
  import { trackerBarOf } from "./systems";
4
+ import { witnessesOf } from "./witnesses";
4
5
  export const reservationTypes = [
5
6
  'source-not-stated',
6
7
  'source-undocumented',
8
+ 'software-not-named',
9
+ 'instrument-not-named',
7
10
  'features-interpreted',
8
11
  'no-physical-evidence',
9
12
  'measurement-undocumented',
10
13
  'not-calibrated',
11
- 'system-unknown'
14
+ 'system-unknown',
15
+ 'keeper-unknown'
12
16
  ];
13
17
  const sourceStated = copy => copy.readFrom ? undefined : {
14
18
  type: 'source-not-stated',
@@ -23,6 +27,20 @@ const sourceDocumented = copy => {
23
27
  note: `The copy names ${sourceLabels[source.kind]} as its source, but records nobody who made it, no device and no date.`
24
28
  };
25
29
  };
30
+ /** An emulation and a recording are turned into notes by software, which says how far to trust them. */
31
+ const softwareNamed = copy => {
32
+ const source = copy.readFrom;
33
+ if (!source || isMeasured(source.kind) || source.software?.length)
34
+ return undefined;
35
+ return {
36
+ type: 'software-not-named',
37
+ note: `The copy names ${sourceLabels[source.kind]} as its source, but not the software that turned it into notes.`
38
+ };
39
+ };
40
+ const instrumentNamed = copy => copy.readFrom?.kind === 'recording' && !copy.readFrom.instrument ? {
41
+ type: 'instrument-not-named',
42
+ note: 'The copy is known from a recording, but neither the instrument it was played on nor its regulation is named.'
43
+ } : undefined;
26
44
  const featuresMeasured = copy => copy.readFrom && !isMeasured(copy.readFrom.kind) ? {
27
45
  type: 'features-interpreted',
28
46
  note: `The features come from ${sourceLabels[copy.readFrom.kind]}, in which the roll has already been read into notes and commands by somebody else.`
@@ -58,33 +76,50 @@ const systemKnown = copy => {
58
76
  note: `The copy names ${system.name || 'a reproducing system'}, which the edition has no tracker bar for, so it is read by the T-100's.`
59
77
  };
60
78
  };
79
+ const keeperKnown = copy => copy.keeper ? undefined : {
80
+ type: 'keeper-unknown',
81
+ note: 'Nobody is known to hold the copy, so its paper cannot be consulted.'
82
+ };
83
+ /** A check on what a copy's features leave open, which has nothing to say of a copy without any. */
84
+ const onFeatures = (check) => copy => copy.features.length > 0 ? check(copy) : undefined;
61
85
  const checks = [
62
86
  sourceStated,
63
87
  sourceDocumented,
64
- featuresMeasured,
65
- physicalEvidence,
66
- measurementDocumented,
67
- calibrated,
68
- systemKnown
88
+ softwareNamed,
89
+ instrumentNamed,
90
+ onFeatures(featuresMeasured),
91
+ onFeatures(physicalEvidence),
92
+ onFeatures(measurementDocumented),
93
+ onFeatures(calibrated),
94
+ onFeatures(systemKnown),
95
+ keeperKnown
69
96
  ];
70
97
  /**
71
98
  * What the edition cannot vouch for in a copy, in the order the
72
99
  * checks are listed: where its features came from first, then what
73
- * the measurement leaves open.
100
+ * the measurement leaves open, then who holds it.
74
101
  */
75
102
  export const reservationsAbout = (copy) => checks.flatMap(check => check(copy) ?? []);
76
103
  export const versionReservationTypes = [
77
104
  'text-not-stated',
78
- 'type-not-stated'
105
+ 'type-not-stated',
106
+ 'witnessed-by-statement-only'
79
107
  ];
80
- const textStated = version => version.edits ? undefined : {
108
+ const textStated = (_view, version) => version.edits ? undefined : {
81
109
  type: 'text-not-stated',
82
110
  note: 'The version does not state its edits, so it reads as the version it derives from.'
83
111
  };
84
- const typeStated = version => version.versionType ? undefined : {
112
+ const typeStated = (_view, version) => version.versionType ? undefined : {
85
113
  type: 'type-not-stated',
86
114
  note: 'The version does not say whether it served as a master for several copies or exists on one only.'
87
115
  };
88
- const versionChecks = [textStated, typeStated];
116
+ const witnessedByFeatures = (view, version) => {
117
+ const witnesses = witnessesOf(view, version.id);
118
+ return witnesses.length > 0 && witnesses.every(({ by }) => by === 'statement') ? {
119
+ type: 'witnessed-by-statement-only',
120
+ note: 'No copy\'s features carry what the version inserts; only copies that state they carry it bear witness to it.'
121
+ } : undefined;
122
+ };
123
+ const versionChecks = [textStated, typeStated, witnessedByFeatures];
89
124
  /** What the edition cannot vouch for in a version, in the order the checks are listed. */
90
- export const reservationsAboutVersion = (version) => versionChecks.flatMap(check => check(version) ?? []);
125
+ export const reservationsAboutVersion = (view, version) => versionChecks.flatMap(check => check(view, version) ?? []);
package/lib/schema.json CHANGED
@@ -628,7 +628,7 @@
628
628
  "ontology": "reo:scope"
629
629
  },
630
630
  "FeatureSource": {
631
- "description": "The capture by which a copy's features became data: what they were read from, who read them, on what device and when. It states where the numbers of the edition come from and says nothing about the state of the paper, which is a condition.\n\nWhat is not known is left out. A reader is told about the gap by `reservationsAbout`, which works out what a copy cannot vouch for from what it states here.",
631
+ "description": "The capture by which a copy's features became data: what they were read from, who read them, on what device, with what software and when. It states where the numbers of the edition come from and says nothing about the state of the paper, which is a condition.\n\nWhat is not known is left out. A reader is told about the gap by `reservationsAbout`, which works out what a copy cannot vouch for from what it states here.",
632
632
  "properties": {
633
633
  "actor": {
634
634
  "$ref": "#/definitions/ActorAssignment",
@@ -646,6 +646,11 @@
646
646
  "description": "The make and model of the scanner, camera or player the capture ran on.",
647
647
  "ontology": "crmdig:L12 happened on device"
648
648
  },
649
+ "instrument": {
650
+ "$ref": "#/definitions/ObjectAssumption%3CInstrument%3E",
651
+ "description": "The instrument the copy was played on, where the capture is a recording of it.",
652
+ "ontology": "crm:P16 used specific object"
653
+ },
649
654
  "kind": {
650
655
  "$ref": "#/definitions/SourceKind",
651
656
  "description": "What the features were read from.",
@@ -660,6 +665,14 @@
660
665
  "description": "The file the capture produced, where it has an address.",
661
666
  "type": "string",
662
667
  "ontology": "crmdig:L11 had output"
668
+ },
669
+ "software": {
670
+ "description": "The software the capture ran, each with its version.",
671
+ "items": {
672
+ "$ref": "#/definitions/Software"
673
+ },
674
+ "type": "array",
675
+ "ontology": "crmdig:L23 used software or firmware"
663
676
  }
664
677
  },
665
678
  "required": [
@@ -823,13 +836,21 @@
823
836
  "ontology": "crm:P3 has note"
824
837
  },
825
838
  "premises": {
826
- "description": "References (by `@id`) to the beliefs or facts from which the conclusion is drawn.",
839
+ "description": "References (by `@id`) to the beliefs from which the conclusion is drawn.",
827
840
  "items": {
828
841
  "type": "string"
829
842
  },
830
843
  "type": "array",
831
844
  "ontology": "crminf:J1 used as premise"
832
845
  },
846
+ "used": {
847
+ "description": "References (by `@id`) to what the inference worked on besides beliefs, such as the versions it compared or the analysis output it cites.",
848
+ "items": {
849
+ "type": "string"
850
+ },
851
+ "type": "array",
852
+ "ontology": "crm:P16 used specific object"
853
+ },
833
854
  "@type": {
834
855
  "const": "inference",
835
856
  "type": "string",
@@ -1312,6 +1333,52 @@
1312
1333
  ],
1313
1334
  "description": "An object assumption wraps a complex object with an optional annotation. Used for structured values (e.g. persons, conditions) whose properties may be uncertain."
1314
1335
  },
1336
+ "ObjectAssumption<ConditionState<\"general\">>": {
1337
+ "description": "An object assumption wraps a complex object with an optional annotation. Used for structured values (e.g. persons, conditions) whose properties may be uncertain.",
1338
+ "properties": {
1339
+ "@annotation": {
1340
+ "description": "An optional annotation expressing a belief about this assumption. Uses the JSON-LD-star `@annotation` mechanism to attach epistemic metadata (certainty and reasons) to any triple.",
1341
+ "properties": {
1342
+ "belief": {
1343
+ "$ref": "#/definitions/Belief",
1344
+ "description": "The belief held about the annotated statement.",
1345
+ "ontology": "crminf:J4i is subject of"
1346
+ },
1347
+ "@id": {
1348
+ "description": "A unique identifier for this object.",
1349
+ "type": "string"
1350
+ }
1351
+ },
1352
+ "required": [
1353
+ "belief",
1354
+ "@id"
1355
+ ],
1356
+ "type": "object"
1357
+ },
1358
+ "conditionType": {
1359
+ "const": "general",
1360
+ "description": "The kind of condition, from the list the roll or the kind of feature allows.",
1361
+ "type": "string",
1362
+ "ontology": "crm:P2 has type"
1363
+ },
1364
+ "description": {
1365
+ "description": "A free-text description of the condition, providing details beyond the type classification.",
1366
+ "type": "string",
1367
+ "ontology": "crm:P3 has note"
1368
+ },
1369
+ "@type": {
1370
+ "const": "ConditionState",
1371
+ "description": "The type discriminator for this object.",
1372
+ "type": "string",
1373
+ "ontology": "rdf:type"
1374
+ }
1375
+ },
1376
+ "required": [
1377
+ "conditionType",
1378
+ "@type"
1379
+ ],
1380
+ "type": "object"
1381
+ },
1315
1382
  "ObjectAssumption<ConditionState<(\"detaching\"|\"ripped\")>>": {
1316
1383
  "description": "An object assumption wraps a complex object with an optional annotation. Used for structured values (e.g. persons, conditions) whose properties may be uncertain.",
1317
1384
  "properties": {
@@ -1502,6 +1569,55 @@
1502
1569
  ],
1503
1570
  "type": "object"
1504
1571
  },
1572
+ "ObjectAssumption<Instrument>": {
1573
+ "description": "An object assumption wraps a complex object with an optional annotation. Used for structured values (e.g. persons, conditions) whose properties may be uncertain.",
1574
+ "properties": {
1575
+ "@annotation": {
1576
+ "description": "An optional annotation expressing a belief about this assumption. Uses the JSON-LD-star `@annotation` mechanism to attach epistemic metadata (certainty and reasons) to any triple.",
1577
+ "properties": {
1578
+ "belief": {
1579
+ "$ref": "#/definitions/Belief",
1580
+ "description": "The belief held about the annotated statement.",
1581
+ "ontology": "crminf:J4i is subject of"
1582
+ },
1583
+ "@id": {
1584
+ "description": "A unique identifier for this object.",
1585
+ "type": "string"
1586
+ }
1587
+ },
1588
+ "required": [
1589
+ "belief",
1590
+ "@id"
1591
+ ],
1592
+ "type": "object"
1593
+ },
1594
+ "condition": {
1595
+ "$ref": "#/definitions/ObjectAssumption%3CConditionState%3C%22general%22%3E%3E",
1596
+ "ontology": "crm:P44 has condition"
1597
+ },
1598
+ "name": {
1599
+ "description": "The name, e.g. \"Grünfeld, Alfred\", \"Wien\", \"M. Welte & Söhne\".",
1600
+ "type": "string",
1601
+ "ontology": "rdfs:label"
1602
+ },
1603
+ "sameAs": {
1604
+ "description": "Authority records for the same thing: GND, Wikidata, geonames or similar.",
1605
+ "examples": [
1606
+ "https://d-nb.info/gnd/116888652"
1607
+ ],
1608
+ "items": {
1609
+ "type": "string"
1610
+ },
1611
+ "type": "array",
1612
+ "ontology": "owl:sameAs"
1613
+ }
1614
+ },
1615
+ "required": [
1616
+ "name",
1617
+ "sameAs"
1618
+ ],
1619
+ "type": "object"
1620
+ },
1505
1621
  "ObjectAssumption<PaperSpeed>": {
1506
1622
  "anyOf": [
1507
1623
  {
@@ -1984,6 +2100,14 @@
1984
2100
  "RollCopy": {
1985
2101
  "description": "A physical copy of a roll, held at a specific location. Each roll copy has its own set of features, measurements, conditions, and modifications. Multiple copies of the same roll may exist across different archives or collections.",
1986
2102
  "properties": {
2103
+ "carries": {
2104
+ "description": "The versions this copy is held to carry, where its features are not read into symbols, as for a copy known only from a recording. Each statement carries the belief it rests on.",
2105
+ "items": {
2106
+ "$ref": "#/definitions/ReferenceAssumption"
2107
+ },
2108
+ "type": "array",
2109
+ "ontology": "crm:P128 carries"
2110
+ },
1987
2111
  "conditions": {
1988
2112
  "description": "Condition assessments of this roll copy (e.g. paper stretch, general wear). Each condition is an assumption annotatable with a belief.",
1989
2113
  "items": {
@@ -2002,7 +2126,7 @@
2002
2126
  },
2003
2127
  "keeper": {
2004
2128
  "$ref": "#/definitions/Agent",
2005
- "description": "The institution or person holding this copy.",
2129
+ "description": "The institution or person holding this copy. Left out where it is not known, as for a copy known only from a recording.",
2006
2130
  "ontology": "crm:P50 has current keeper"
2007
2131
  },
2008
2132
  "measurements": {
@@ -2146,6 +2270,11 @@
2146
2270
  "type": "string",
2147
2271
  "ontology": "crm:P138i has representation"
2148
2272
  },
2273
+ "siglum": {
2274
+ "description": "A short siglum to identify the copy, e.g. \"W1\" or \"S2\". A copy without one is named by its keeper.",
2275
+ "type": "string",
2276
+ "ontology": "reo:siglum"
2277
+ },
2149
2278
  "@id": {
2150
2279
  "description": "A unique identifier for this object.",
2151
2280
  "type": "string"
@@ -2161,7 +2290,6 @@
2161
2290
  "conditions",
2162
2291
  "features",
2163
2292
  "@id",
2164
- "keeper",
2165
2293
  "measurements",
2166
2294
  "modifications",
2167
2295
  "ops",
@@ -2188,11 +2316,31 @@
2188
2316
  ],
2189
2317
  "type": "object"
2190
2318
  },
2319
+ "Software": {
2320
+ "description": "Software a capture ran, such as the transcriber that read a recording into notes or the emulator that wrote a MIDI file.",
2321
+ "properties": {
2322
+ "name": {
2323
+ "type": "string",
2324
+ "ontology": "rdfs:label"
2325
+ },
2326
+ "version": {
2327
+ "description": "The release, or the model checkpoint of a transcriber.",
2328
+ "type": "string",
2329
+ "ontology": "owl:versionInfo"
2330
+ }
2331
+ },
2332
+ "required": [
2333
+ "name"
2334
+ ],
2335
+ "type": "object",
2336
+ "ontology": "crmdig:D14 Software"
2337
+ },
2191
2338
  "SourceKind": {
2192
2339
  "enum": [
2193
2340
  "roll",
2194
2341
  "scan",
2195
2342
  "analysis",
2343
+ "reading",
2196
2344
  "emulation",
2197
2345
  "recording"
2198
2346
  ],
@@ -60,7 +60,13 @@
60
60
  },
61
61
  "inference": {
62
62
  "@id": "crminf:I5_Inference_Making",
63
- "@context": { "note": "crm:P3_has_note" }
63
+ "@context": {
64
+ "note": "crm:P3_has_note",
65
+ "used": {
66
+ "@id": "crm:P16_used_specific_object",
67
+ "@type": "@id"
68
+ }
69
+ }
64
70
  },
65
71
  "beliefAdoption": {
66
72
  "@id": "crminf:I7_Belief_Adoption",
@@ -168,6 +174,7 @@
168
174
  "factor": "rdf:value",
169
175
  "keeper": "crm:P50_has_current_keeper",
170
176
  "features": "crm:P56_bears_feature",
177
+ "carries": "crm:P128_carries",
171
178
  "modifications": "crm:P31i_was_modified_by",
172
179
  "scan": {
173
180
  "@id": "crm:P138i_has_representation",
@@ -190,6 +197,11 @@
190
197
  "@type": "@id"
191
198
  },
192
199
  "device": "crmdig:L12_happened_on_device",
200
+ "software": {
201
+ "@id": "crmdig:L23_used_software_or_firmware",
202
+ "@context": { "version": "owl:versionInfo" }
203
+ },
204
+ "instrument": "crm:P16_used_specific_object",
193
205
  "note": "crm:P3_has_note"
194
206
  }
195
207
  },
@@ -0,0 +1,42 @@
1
+ import { Belief, Certainty } from "./Assumption";
2
+ import { EditionView } from "./EditionView";
3
+ export type WitnessBy = 'carriers' | 'statement';
4
+ /** A copy that bears witness to a version, and how. */
5
+ export interface Witness {
6
+ /** The copy, by id. */
7
+ copy: string;
8
+ /** Whether the copy's features carry what the version inserts, or the copy states that it carries the version. */
9
+ by: WitnessBy;
10
+ /** How certainly a statement is held. Carriage through features is no statement and holds none. */
11
+ certainty?: Certainty;
12
+ /** The belief a statement rests on, where it states one. */
13
+ belief?: Belief;
14
+ }
15
+ /**
16
+ * The copies that bear witness to the version: those whose features carry
17
+ * a symbol the version's own edits insert, and those that state they carry
18
+ * it, with the certainty each statement is held with.
19
+ *
20
+ * What a version inherits is carried by nearly every copy of the roll, so
21
+ * only what it inserts tells its witnesses apart. A version whose edits
22
+ * are not stated is witnessed by statement alone.
23
+ */
24
+ export declare const witnessesOf: (view: EditionView, versionId: string) => Witness[];
25
+ /**
26
+ * The versions the copy bears witness to, each with how, in the order the
27
+ * edition lists its versions.
28
+ */
29
+ export declare const versionsWitnessedBy: (view: EditionView, copyId: string) => (Witness & {
30
+ version: string;
31
+ })[];
32
+ export type CarriageProblem = {
33
+ copy: string;
34
+ version: string;
35
+ problem: 'stated-beside-carriers' | 'version-missing';
36
+ };
37
+ /**
38
+ * Where a copy's statement that it carries a version cannot stand: one
39
+ * made although the copy's features carry symbols, which say by
40
+ * themselves what it carries, and one naming a version the edition lacks.
41
+ */
42
+ export declare const carriageProblems: (view: EditionView) => CarriageProblem[];
@@ -0,0 +1,58 @@
1
+ import { certaintyOf, idOf, idsOf } from "./Assumption";
2
+ import { insertedBy } from "./Version";
3
+ /** The copies whose features carry any of the symbols. */
4
+ const copiesCarrying = (view, symbols) => new Set(symbols
5
+ .flatMap(symbol => idsOf(symbol.carriers))
6
+ .flatMap(id => {
7
+ const copy = view.copyOf(id);
8
+ return copy ? [copy.id] : [];
9
+ }));
10
+ /**
11
+ * The copies that bear witness to the version: those whose features carry
12
+ * a symbol the version's own edits insert, and those that state they carry
13
+ * it, with the certainty each statement is held with.
14
+ *
15
+ * What a version inherits is carried by nearly every copy of the roll, so
16
+ * only what it inserts tells its witnesses apart. A version whose edits
17
+ * are not stated is witnessed by statement alone.
18
+ */
19
+ export const witnessesOf = (view, versionId) => {
20
+ const version = view.get(versionId);
21
+ if (!version)
22
+ return [];
23
+ const carrying = copiesCarrying(view, insertedBy(version));
24
+ const byCarriers = view.edition.copies
25
+ .filter(copy => carrying.has(copy.id))
26
+ .map((copy) => ({ copy: copy.id, by: 'carriers' }));
27
+ const byStatement = view.edition.copies
28
+ .filter(copy => !carrying.has(copy.id))
29
+ .flatMap(copy => (copy.carries ?? [])
30
+ .filter(statement => idOf(statement) === versionId)
31
+ .map((statement) => {
32
+ const belief = statement['@annotation']?.belief;
33
+ return { copy: copy.id, by: 'statement', certainty: certaintyOf(statement), ...(belief && { belief }) };
34
+ }));
35
+ return [...byCarriers, ...byStatement];
36
+ };
37
+ /**
38
+ * The versions the copy bears witness to, each with how, in the order the
39
+ * edition lists its versions.
40
+ */
41
+ export const versionsWitnessedBy = (view, copyId) => view.edition.versions.flatMap(version => witnessesOf(view, version.id)
42
+ .filter(witness => witness.copy === copyId)
43
+ .map(witness => ({ ...witness, version: version.id })));
44
+ /**
45
+ * Where a copy's statement that it carries a version cannot stand: one
46
+ * made although the copy's features carry symbols, which say by
47
+ * themselves what it carries, and one naming a version the edition lacks.
48
+ */
49
+ export const carriageProblems = (view) => {
50
+ const carrying = copiesCarrying(view, view.edition.versions.flatMap(insertedBy));
51
+ return view.edition.copies.flatMap(copy => (copy.carries ?? []).flatMap((statement) => {
52
+ const version = idOf(statement);
53
+ return [
54
+ ...(carrying.has(copy.id) ? [{ copy: copy.id, version, problem: 'stated-beside-carriers' }] : []),
55
+ ...(view.get(version)?.type === 'Version' ? [] : [{ copy: copy.id, version, problem: 'version-missing' }])
56
+ ];
57
+ }));
58
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linked-rolls",
3
- "version": "0.26.0",
3
+ "version": "0.28.0",
4
4
  "description": "Digital editions of piano rolls: import, collation, editorial assumptions, JSON-LD export, and emulation through a reproducing system",
5
5
  "license": "MIT",
6
6
  "repository": {