linked-rolls 0.74.0 → 0.76.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 (42) hide show
  1. package/README.md +62 -26
  2. package/lib/collation/Collation.d.ts +12 -42
  3. package/lib/collation/Collation.js +15 -48
  4. package/lib/filiation/adoption.d.ts +23 -4
  5. package/lib/filiation/adoption.js +81 -23
  6. package/lib/filiation/edits.d.ts +35 -19
  7. package/lib/filiation/edits.js +68 -34
  8. package/lib/filiation/index.d.ts +2 -1
  9. package/lib/filiation/index.js +2 -1
  10. package/lib/filiation/record.d.ts +49 -0
  11. package/lib/filiation/record.js +49 -0
  12. package/lib/index.d.ts +1 -0
  13. package/lib/index.js +1 -0
  14. package/lib/io/asJsonLd.js +5 -1
  15. package/lib/io/importJsonLd.js +42 -4
  16. package/lib/io/migrate.d.ts +7 -0
  17. package/lib/io/migrate.js +196 -2
  18. package/lib/model/Agent.d.ts +3 -1
  19. package/lib/model/Assumption.d.ts +8 -1
  20. package/lib/model/Assumption.js +2 -1
  21. package/lib/model/Edit.d.ts +76 -17
  22. package/lib/model/Edit.js +52 -3
  23. package/lib/model/Edition.d.ts +32 -3
  24. package/lib/model/RollCopy.js +1 -1
  25. package/lib/model/Version.d.ts +3 -13
  26. package/lib/model/Version.js +7 -2
  27. package/lib/ops/collation.d.ts +8 -4
  28. package/lib/ops/collation.js +40 -23
  29. package/lib/ops/copies.js +1 -2
  30. package/lib/ops/draft.d.ts +1 -9
  31. package/lib/ops/draft.js +0 -12
  32. package/lib/ops/edits.d.ts +20 -3
  33. package/lib/ops/edits.js +36 -15
  34. package/lib/ops/index.d.ts +1 -1
  35. package/lib/ops/index.js +1 -1
  36. package/lib/ops/versions.d.ts +3 -2
  37. package/lib/ops/versions.js +4 -5
  38. package/lib/schema.json +60 -64
  39. package/lib/software.d.ts +12 -0
  40. package/lib/software.js +10 -0
  41. package/lib/spec/context.json +8 -5
  42. package/package.json +1 -1
package/lib/io/migrate.js CHANGED
@@ -4,6 +4,7 @@ import { trackerBars } from "../systems/index.js";
4
4
  import { welteT100 } from "../systems/welteT100/bar.js";
5
5
  import { isDateString } from "../shared/utils.js";
6
6
  import { mapped, withValues } from "../ops/immutable.js";
7
+ import { musicalChange, technicalChange } from "../model/Edit.js";
7
8
  const retiredVersionTypes = new Set(['edition', 'unicum']);
8
9
  /**
9
10
  * The vocabulary the steps below were written against, as it stood in
@@ -63,6 +64,37 @@ const renamedEditTypes = {
63
64
  const withRenamedEditType = (node) => typeof node.editType === 'string' && Object.hasOwn(renamedEditTypes, node.editType)
64
65
  ? { ...node, editType: renamedEditTypes[node.editType] }
65
66
  : node;
67
+ /** The motivation a corrected error is stated with, as `correctedErrorMotivation` was when the kind of edit was retired. */
68
+ const correctedErrorMotivation = {
69
+ '@type': 'motivation',
70
+ '@id': 'corrected-error',
71
+ note: 'Puts right an error of the version it derives from.'
72
+ };
73
+ const isTypedCorrectError = (edit) => edit !== null && typeof edit === 'object' && (edit.editType === 'correct-error' || edit.classification === 'correct-error');
74
+ /**
75
+ * Whether a change corrects an error was once one of the kinds of edit,
76
+ * though it is not read off the shape of a change but is a reason for
77
+ * it. An edit typed so states it as its motivation instead, where it
78
+ * gives none of its own, and the version states what the motivation
79
+ * means; one that gives a motivation of its own keeps that one. Either
80
+ * way it states no kind, its shape being left to be read again. Once
81
+ * motivations are the edition's, a corrected error is a musical change
82
+ * (`withMotivationsOfTheEdition`).
83
+ */
84
+ const withCorrectedErrorsMotivated = (node) => {
85
+ if (!Array.isArray(node.edits) || !node.edits.some(isTypedCorrectError))
86
+ return node;
87
+ const edits = node.edits.map((edit) => {
88
+ if (!isTypedCorrectError(edit))
89
+ return edit;
90
+ const { editType: _kind, classification: _named, ...rest } = edit;
91
+ return rest.motivation === undefined || rest.motivation === 'unchecked' ? { ...rest, motivation: correctedErrorMotivation['@id'] } : rest;
92
+ });
93
+ const motivations = Array.isArray(node.motivations) ? node.motivations : [];
94
+ const declare = edits.some((edit) => edit?.motivation === correctedErrorMotivation['@id'])
95
+ && !motivations.some(motivation => motivation?.['@id'] === correctedErrorMotivation['@id']);
96
+ return { ...node, edits, ...(declare && { motivations: [...motivations, correctedErrorMotivation] }) };
97
+ };
66
98
  const withRenamedType = (node) => Object.hasOwn(renamedTypes, node['@type']) ? { ...node, '@type': renamedTypes[node['@type']] } : node;
67
99
  /** Whether the feature is of the class under either name, since a node is migrated before its children are. */
68
100
  const isOfType = (type) => (feature) => feature?.['@type'] === type || renamedTypes[feature?.['@type']] === type;
@@ -503,7 +535,7 @@ const withMeasuredByInReasons = (node) => {
503
535
  }
504
536
  };
505
537
  };
506
- const nodeSteps = [withRenamedKeys, withRenamedEditType, withTypology, withRenamedType, withoutVersionType, withoutVersionSiglum, withLowerCaseTerms, withSplitMethod, withReferences, withKeeper, withoutEmptyKeeper,
538
+ const nodeSteps = [withRenamedKeys, withRenamedEditType, withCorrectedErrorsMotivated, withTypology, withRenamedType, withoutVersionType, withoutVersionSiglum, withLowerCaseTerms, withSplitMethod, withReferences, withKeeper, withoutEmptyKeeper,
507
539
  withPerforator, withProductionNodes, withTypedPerforator, withScale, withoutOps, withDerivationList, withReadingKind, withTimeSpanDates,
508
540
  withoutFeatureKind, withBorneFeaturesNamed, withFeaturesInActs, withOwnPlaces, withDriveOfStaggering, withoutPattern, withoutBearings, withoutEntailedType,
509
541
  withMeasuredByInReasons];
@@ -677,6 +709,168 @@ const withReferenceCopy = (edition) => {
677
709
  : edition;
678
710
  };
679
711
  const editionSteps = [withoutFormatVersion, withoutTempoAdjustment, withoutEditionType, withSystems, withEditors, withDerivationTolerance, withReferenceCopy];
712
+ /**
713
+ * The motivation an edit of a version that derives from another is left
714
+ * with where only the edition can tell what kind of change it is: one
715
+ * that states neither a kind nor a reason, as a collation once left
716
+ * them. `importJsonLd` reads its kind off its shape (`proposedMotivations`).
717
+ */
718
+ export const uncheckedMotivation = 'unchecked';
719
+ /** The kinds of edit, as they were stated once, whose shape is a musical decision. */
720
+ const musicalEditTypes = new Set(['additional-accent', 'shift', 'shorten', 'prolong']);
721
+ /** The kinds of edit, as they were stated once, whose shape is a technical change. */
722
+ const technicalEditTypes = new Set(['recoding', 'remove-redundancy', 'add-redundancy']);
723
+ const isRecord = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
724
+ /** Whether the version derives from another, so that its edits are changes rather than its text. */
725
+ const derives = (version) => Array.isArray(version?.basedOn) ? version.basedOn.length > 0 : version?.basedOn !== undefined;
726
+ /**
727
+ * The motivation an edit states once motivations say what kind of
728
+ * change it is, from the kind it was given and the reason it gave.
729
+ */
730
+ const motivationOfEdit = (editType, motivation, derived) => {
731
+ // A corrected error put back a perforation in its place, and so made a musical change.
732
+ const stated = motivation === 'corrected-error' ? musicalChange : motivation;
733
+ if (!derived)
734
+ return stated === uncheckedMotivation || stated === musicalChange ? undefined : stated;
735
+ const technical = typeof editType === 'string' && technicalEditTypes.has(editType);
736
+ // A reason given for a technical change explained what the change or the procedure says already.
737
+ if (technical)
738
+ return technicalChange;
739
+ if (stated === undefined || stated === uncheckedMotivation) {
740
+ return typeof editType === 'string' && musicalEditTypes.has(editType) ? musicalChange : uncheckedMotivation;
741
+ }
742
+ return stated;
743
+ };
744
+ /** Whether the document still states what motivations and kinds of edit were before 0.76. */
745
+ const statesMotivationsPerVersion = (versions) => versions.some(version => Array.isArray(version?.motivations)
746
+ || (Array.isArray(version?.edits) && version.edits.some((edit) => isRecord(edit) && (Object.hasOwn(edit, 'editType') || edit.motivation === 'corrected-error'))));
747
+ /**
748
+ * Before 0.76 each version stated the motivations its edits named, and
749
+ * an edit stated its kind beside its reason. The motivations are the
750
+ * edition's now, once each, and the motivation alone says what kind of
751
+ * change an edit is (`kindOfChange`):
752
+ *
753
+ * - A motivation two versions stated under one id with different notes
754
+ * is kept under an id of its own for the second.
755
+ * - The unchecked motivation goes, and so does the declaration of a
756
+ * corrected error: it put a perforation back in its place, and so is
757
+ * a musical change whose reason is not given.
758
+ * - An edit of a version that derives from another, that gave no reason
759
+ * or only that it was unchecked, is a musical change whose reason is
760
+ * not given where its kind was an added accent, a shift, a shortening
761
+ * or a prolongation, and a technical change where it was a recoding or
762
+ * a redundancy removed or added. One that stated no kind is left
763
+ * unchecked, for an import to read its kind off its shape.
764
+ * - A reason given for a recoding or a redundancy goes, since a
765
+ * technical change needs none, and a motivation nothing names any
766
+ * longer goes with it. Every other reason stays, and makes the change
767
+ * a musical one.
768
+ * - The edits of a version that derives from none state its text and
769
+ * keep no motivation but a reason someone gave.
770
+ */
771
+ const withMotivationsOfTheEdition = (edition) => {
772
+ const versions = listAt(edition, 'versions');
773
+ if (!versions || !statesMotivationsPerVersion(versions))
774
+ return edition;
775
+ const notes = new Map((listAt(edition, 'motivations') ?? []).filter(isRecord).map(motivation => [motivation['@id'], motivation]));
776
+ const referencedBefore = new Set();
777
+ const migrated = versions.map((version, index) => {
778
+ if (!isRecord(version))
779
+ return version;
780
+ const renamed = new Map();
781
+ for (const motivation of Array.isArray(version.motivations) ? version.motivations.filter(isRecord) : []) {
782
+ const id = motivation['@id'];
783
+ if (typeof id !== 'string' || id === uncheckedMotivation || id === 'corrected-error')
784
+ continue;
785
+ const known = notes.get(id);
786
+ if (!known)
787
+ notes.set(id, motivation);
788
+ else if (known.note !== motivation.note) {
789
+ const own = `${id}-${index}`;
790
+ renamed.set(id, own);
791
+ notes.set(own, { ...motivation, '@id': own });
792
+ }
793
+ }
794
+ const derived = derives(version);
795
+ const { motivations: _declared, ...rest } = version;
796
+ if (!Array.isArray(version.edits))
797
+ return rest;
798
+ return {
799
+ ...rest,
800
+ edits: version.edits.map((edit) => {
801
+ if (!isRecord(edit))
802
+ return edit;
803
+ const { editType, motivation: given, ...stated } = edit;
804
+ const named = typeof given === 'string' ? renamed.get(given) ?? given : given;
805
+ if (typeof named === 'string')
806
+ referencedBefore.add(named);
807
+ const motivation = motivationOfEdit(editType, named, derived);
808
+ return motivation === undefined ? stated : { ...stated, motivation };
809
+ })
810
+ };
811
+ });
812
+ const referenced = new Set(migrated.flatMap((version) => (Array.isArray(version?.edits) ? version.edits : []).map((edit) => edit?.motivation).filter((id) => typeof id === 'string')));
813
+ const motivations = [...notes.values()].filter(motivation => referenced.has(motivation['@id']) || !referencedBefore.has(motivation['@id']));
814
+ const { motivations: _stated, ...rest } = edition;
815
+ return { ...rest, versions: migrated, ...(motivations.length > 0 && { motivations }) };
816
+ };
817
+ /**
818
+ * Before 0.76 an adopted proposal was recorded as a proposition set: each
819
+ * statement of it named the set in its annotation, and the set held them
820
+ * under one belief whose inference named the release that found them and
821
+ * the rules it followed. The edition states what a program concluded
822
+ * plainly now, and its creation names the rules among its principles and
823
+ * the release among its software. A statement of a set the edition held
824
+ * below likely keeps the doubt, under a belief of its own that is the
825
+ * set's.
826
+ */
827
+ const withoutPropositionSets = (edition) => {
828
+ const sets = listAt(edition, 'propositionSets');
829
+ if (!sets)
830
+ return edition;
831
+ const beliefOf = new Map(sets.filter(isRecord).map(set => [set['@id'], set.belief]));
832
+ const inferences = sets.filter(isRecord).flatMap(set => Array.isArray(set.belief?.reasons) ? set.belief.reasons : []);
833
+ const rules = inferences.flatMap(inference => Array.isArray(inference?.applies) ? inference.applies : []);
834
+ const programs = inferences.flatMap(inference => Array.isArray(inference?.software) ? inference.software : []);
835
+ let held = 0;
836
+ const unset = (value) => {
837
+ if (Array.isArray(value))
838
+ return mapped(value, unset);
839
+ if (!isRecord(value))
840
+ return value;
841
+ const annotation = value['@annotation'];
842
+ const set = isRecord(annotation) && typeof annotation['@id'] === 'string' ? beliefOf.get(annotation['@id']) : undefined;
843
+ if (!set || (isRecord(annotation.belief) && Object.keys(annotation.belief).some(key => key !== '@id')))
844
+ return withValues(value, unset);
845
+ const { '@annotation': _member, ...rest } = value;
846
+ if (set.certainty === 'true' || set.certainty === 'likely')
847
+ return withValues(rest, unset);
848
+ held += 1;
849
+ return withValues({
850
+ ...rest,
851
+ '@annotation': { '@id': `${annotation['@id']}/${held}`, belief: { ...set, '@id': `${set['@id']}/${held}` } }
852
+ }, unset);
853
+ };
854
+ const { propositionSets: _recorded, ...rest } = edition;
855
+ const unsetted = unset(rest);
856
+ const creation = isRecord(unsetted.creation) ? unsetted.creation : undefined;
857
+ if (!creation)
858
+ return unsetted;
859
+ const principles = [...new Set([...(Array.isArray(creation.principles) ? creation.principles : []), ...rules])];
860
+ const known = Array.isArray(creation.software) ? creation.software : [];
861
+ const software = [...known, ...programs.filter((program, index) => !known.some((other) => other?.name === program?.name && other?.version === program?.version)
862
+ && programs.findIndex(other => other?.name === program?.name && other?.version === program?.version) === index)];
863
+ return {
864
+ ...unsetted,
865
+ creation: {
866
+ ...creation,
867
+ ...(principles.length > 0 && { principles }),
868
+ ...(software.length > 0 && { software })
869
+ }
870
+ };
871
+ };
872
+ /** Steps that read the document as the node steps leave it, an old document's kinds of edit renamed. */
873
+ const finalSteps = [withoutPropositionSets, withMotivationsOfTheEdition];
680
874
  /**
681
875
  * Brings the JSON of an edition written by an earlier release of the
682
876
  * format up to the current shape. A document that is current already
@@ -685,4 +879,4 @@ const editionSteps = [withoutFormatVersion, withoutTempoAdjustment, withoutEditi
685
879
  * to look at an old document as JSON.
686
880
  * @category Reading and writing
687
881
  */
688
- export const migrate = (edition) => walk(editionSteps.reduce((result, step) => step(result), edition));
882
+ export const migrate = (edition) => finalSteps.reduce((result, step) => step(result), walk(editionSteps.reduce((result, step) => step(result), edition)));
@@ -107,7 +107,9 @@ export type Concept = (WithId & Partial<Named>) | (Named & Partial<WithId>);
107
107
  /**
108
108
  * Software that captured a copy or measured it: the transcriber that
109
109
  * read a recording into notes, the emulator that wrote a MIDI file, the
110
- * program that found the holes on a scan or aligned two copies.
110
+ * program that found the holes on a scan or aligned two copies. Or the
111
+ * software an edition was made with, such as the release of this
112
+ * library that found its stemma.
111
113
  * @see crmdig:D14 Software
112
114
  * @category Format types
113
115
  */
@@ -21,7 +21,8 @@ export type Certainty = typeof certainties[number];
21
21
  */
22
22
  export declare const isAsserted: (certainty: Certainty) => boolean;
23
23
  /**
24
- * The certainty a statement is held with. One that carries no belief is stated plainly, and so held true.
24
+ * The certainty a statement is held with. One that carries no belief is
25
+ * stated plainly, and so held true.
25
26
  * @category Assumptions and beliefs
26
27
  */
27
28
  export declare const certaintyOf: (assumption: Readonly<Assumption>) => Certainty;
@@ -202,6 +203,12 @@ export interface Assumption {
202
203
  * An optional annotation expressing a belief about this assumption.
203
204
  * Uses the JSON-LD-star `@annotation` mechanism to attach
204
205
  * epistemic metadata (certainty and reasons) to any triple.
206
+ *
207
+ * A statement that carries none is stated plainly, and what a
208
+ * program concluded is stated so too: an edition asserts its stemma
209
+ * and its edits, as a printed edition does, and its creation names
210
+ * the principles and the release they were found by
211
+ * (`EditionCreation`). A belief is kept for what the editor doubts.
205
212
  */
206
213
  '@annotation'?: WithId & {
207
214
  /**
@@ -19,7 +19,8 @@ export const certainties = [
19
19
  */
20
20
  export const isAsserted = (certainty) => certainty === 'true' || certainty === 'likely';
21
21
  /**
22
- * The certainty a statement is held with. One that carries no belief is stated plainly, and so held true.
22
+ * The certainty a statement is held with. One that carries no belief is
23
+ * stated plainly, and so held true.
23
24
  * @category Assumptions and beliefs
24
25
  */
25
26
  export const certaintyOf = (assumption) => assumption['@annotation']?.belief.certainty ?? 'true';
@@ -1,30 +1,92 @@
1
1
  import { Assumption } from "./Assumption.js";
2
2
  import { AnySymbol } from "./Symbol.js";
3
- import { WithId } from "../shared/utils.js";
3
+ import { WithId, WithNote, WithType } from "../shared/utils.js";
4
4
  /**
5
- * The types of editorial change an edit may state.
5
+ * A reason for a musical change, in the editor's words. The edition
6
+ * states its motivations once (`Edition.motivations`), and an edit of
7
+ * any version names the one it was made for. Every change of a version
8
+ * that derives from another states a motivation: a program states
9
+ * whether it is musical or technical (`musicalChange`,
10
+ * `technicalChange`), and the editor puts a reason of their own in
11
+ * place of a musical one. Speculating about why a change was made is
12
+ * kept to the musical changes.
13
+ * @see crm:E33 Linguistic Object
14
+ * @category Format types
15
+ */
16
+ export type Motivation = WithType<'motivation'> & WithId & WithNote;
17
+ /**
18
+ * The motivation of a musical change whose reason is not given yet: a
19
+ * change of the performance, such as an added accent or a perforation
20
+ * put elsewhere. A program states it for a change whose shape a musical
21
+ * decision makes, under the rule that later versions are the more
22
+ * refined; the editor puts a reason of their own in its place.
23
+ * @category Versions and stemma
24
+ */
25
+ export declare const musicalChange = "https://w3id.org/reo/type/motivation/musical";
26
+ /**
27
+ * The motivation of a technical change: one that changes only how the
28
+ * performance is carried, a redundancy cleaned up or added, or a
29
+ * command recoded for another reproducing system. A program states it
30
+ * for a change of such a shape, and the procedure of the version's
31
+ * creation explains it, so that no reason of its own is given.
32
+ * @category Versions and stemma
33
+ */
34
+ export declare const technicalChange = "https://w3id.org/reo/type/motivation/technical";
35
+ /**
36
+ * The kinds of change an edit can be: one that changes the
37
+ * performance, and one that changes only how it is carried.
6
38
  * @category Vocabulary
7
39
  */
8
- export declare const editTypes: readonly ['additional-accent', 'add-redundancy', 'remove-redundancy',
40
+ export declare const changeKinds: readonly ['musical', 'technical'];
41
+ /**
42
+ * The kind of change an edit is (`kindOfChange`).
43
+ * @category Format types
44
+ */
45
+ export type ChangeKind = typeof changeKinds[number];
46
+ /**
47
+ * What kind of change the edit is, as its motivation says: technical
48
+ * if it is `technicalChange`, musical if it is `musicalChange` or a
49
+ * reason of the editor's.
50
+ *
51
+ * An edit without a motivation is of no kind: the edits of a version
52
+ * that derives from none state its text rather than a change.
53
+ * @category Versions and stemma
54
+ */
55
+ export declare const kindOfChange: (edit: Readonly<Pick<Edit, 'motivation'>>) => ChangeKind | undefined;
56
+ /**
57
+ * The shapes a change can take, read off what it inserts and deletes
58
+ * and the systems of the two versions (`shapeOf`, `proposeEdits`). A
59
+ * shape is not stated: it is what a program reads to propose a
60
+ * motivation, and what a view can call a change.
61
+ * @category Vocabulary
62
+ */
63
+ export declare const editShapes: readonly ['additional-accent', 'add-redundancy', 'remove-redundancy',
9
64
  /**
10
65
  * Carries the text into another reproducing system's coding: a
11
66
  * latched pair of one system giving way to the other's held command,
12
67
  * a command the other system cannot read struck, or one of its own
13
68
  * put in where the first had no word for it.
14
69
  */
15
- 'recoding', 'shift', 'correct-error', 'shorten', 'prolong'];
70
+ 'recoding', 'shift', 'shorten', 'prolong'];
16
71
  /**
17
- * The type of editorial change applied to a symbol or set of symbols.
18
- * Classifies the nature of the edit, e.g. whether it corrects an error,
19
- * adds an accent, shifts a note, or shortens/prolongs a perforation.
72
+ * The shape of a change (`editShapes`).
20
73
  * @category Format types
21
74
  */
22
- export type EditType = typeof editTypes[number];
75
+ export type EditShape = typeof editShapes[number];
76
+ /**
77
+ * The motivation a program states for a change of this shape. A
78
+ * recoding and a redundancy removed or added are technical changes
79
+ * (`technicalChange`). Every other change of a later version is taken
80
+ * for a refinement, and so for a musical change whose reason is not
81
+ * given yet (`musicalChange`).
82
+ * @category Versions and stemma
83
+ */
84
+ export declare const motivationProposedFor: (shape: EditShape | undefined) => string;
23
85
  /**
24
86
  * A set of edits transforms a version of a roll into another version.
25
87
  * Edits insert or delete symbols, or both (= replace).
26
- * Edits may be motivated by a given set of reasons, e.g.
27
- * to add an additional accent or to correct an error.
88
+ * What kind of change an edit is follows from its motivation
89
+ * (`kindOfChange`).
28
90
  * If an edit is the interpretation of a metamark,
29
91
  * such as a pencil mark, this should be made explicit
30
92
  * using a meaning comprehension on the `@annotation` field.
@@ -38,13 +100,10 @@ export type EditType = typeof editTypes[number];
38
100
  export interface Edit extends WithId, Assumption {
39
101
  type: 'edit';
40
102
  /**
41
- * The type of editorial change (e.g. 'correct-error', 'additional-accent').
42
- * @see crm:P2 has type
43
- */
44
- editType?: EditType;
45
- /**
46
- * A textual description of the motivation for this edit,
47
- * referencing a motivation defined in the version's motivations list.
103
+ * Why the edit was made: one of the edition's motivations, by id,
104
+ * or one the vocabulary publishes, by IRI (`musicalChange`,
105
+ * `technicalChange`). An edit of a version that derives from none
106
+ * states none.
48
107
  * @see crm:P17 was motivated by
49
108
  */
50
109
  motivation?: string;
package/lib/model/Edit.js CHANGED
@@ -1,8 +1,47 @@
1
1
  /**
2
- * The types of editorial change an edit may state.
2
+ * The motivation of a musical change whose reason is not given yet: a
3
+ * change of the performance, such as an added accent or a perforation
4
+ * put elsewhere. A program states it for a change whose shape a musical
5
+ * decision makes, under the rule that later versions are the more
6
+ * refined; the editor puts a reason of their own in its place.
7
+ * @category Versions and stemma
8
+ */
9
+ export const musicalChange = 'https://w3id.org/reo/type/motivation/musical';
10
+ /**
11
+ * The motivation of a technical change: one that changes only how the
12
+ * performance is carried, a redundancy cleaned up or added, or a
13
+ * command recoded for another reproducing system. A program states it
14
+ * for a change of such a shape, and the procedure of the version's
15
+ * creation explains it, so that no reason of its own is given.
16
+ * @category Versions and stemma
17
+ */
18
+ export const technicalChange = 'https://w3id.org/reo/type/motivation/technical';
19
+ /**
20
+ * The kinds of change an edit can be: one that changes the
21
+ * performance, and one that changes only how it is carried.
3
22
  * @category Vocabulary
4
23
  */
5
- export const editTypes = [
24
+ export const changeKinds = ['musical', 'technical'];
25
+ /**
26
+ * What kind of change the edit is, as its motivation says: technical
27
+ * if it is `technicalChange`, musical if it is `musicalChange` or a
28
+ * reason of the editor's.
29
+ *
30
+ * An edit without a motivation is of no kind: the edits of a version
31
+ * that derives from none state its text rather than a change.
32
+ * @category Versions and stemma
33
+ */
34
+ export const kindOfChange = (edit) => edit.motivation === undefined
35
+ ? undefined
36
+ : edit.motivation === technicalChange ? 'technical' : 'musical';
37
+ /**
38
+ * The shapes a change can take, read off what it inserts and deletes
39
+ * and the systems of the two versions (`shapeOf`, `proposeEdits`). A
40
+ * shape is not stated: it is what a program reads to propose a
41
+ * motivation, and what a view can call a change.
42
+ * @category Vocabulary
43
+ */
44
+ export const editShapes = [
6
45
  'additional-accent',
7
46
  'add-redundancy',
8
47
  'remove-redundancy',
@@ -14,10 +53,20 @@ export const editTypes = [
14
53
  */
15
54
  'recoding',
16
55
  'shift',
17
- 'correct-error',
18
56
  'shorten',
19
57
  'prolong',
20
58
  ];
59
+ /**
60
+ * The motivation a program states for a change of this shape. A
61
+ * recoding and a redundancy removed or added are technical changes
62
+ * (`technicalChange`). Every other change of a later version is taken
63
+ * for a refinement, and so for a musical change whose reason is not
64
+ * given yet (`musicalChange`).
65
+ * @category Versions and stemma
66
+ */
67
+ export const motivationProposedFor = (shape) => shape === 'recoding' || shape === 'remove-redundancy' || shape === 'add-redundancy'
68
+ ? technicalChange
69
+ : musicalChange;
21
70
  /**
22
71
  * Whether the object is an edit.
23
72
  * @category Lookups
@@ -1,10 +1,12 @@
1
1
  import { RollCopy } from "./RollCopy.js";
2
2
  import { Version } from "./Version.js";
3
+ import { Motivation } from "./Edit.js";
3
4
  import { DateAssignment } from "./Assumption.js";
4
- import { Editor, Person, Place } from "./Agent.js";
5
+ import { Editor, Person, Place, Software } from "./Agent.js";
5
6
  /**
6
- * This type describes the creation of an edition,
7
- * i.e. the editor, publisher, and publication date.
7
+ * This type describes the creation of an edition: the editors,
8
+ * publisher and publication date, the editorial principles the edition
9
+ * follows, and the programs it was made with.
8
10
  * @see lrmoo:F28 Expression Creation
9
11
  * @category Format types
10
12
  */
@@ -28,6 +30,25 @@ export interface EditionCreation {
28
30
  * @see dcterms:date
29
31
  */
30
32
  publicationDate: Date;
33
+ /**
34
+ * The editorial principles the edition follows, by IRI: the
35
+ * published rules its stemma and its edits rest on, whether the
36
+ * editor applied them or a program did, such as that the same
37
+ * addition at the same place is not made twice independently
38
+ * (`stemmaRules`, `editRules`). Adopting a proposal names those it
39
+ * follows. An edition that names none leaves it out.
40
+ * @see crm:P33 used specific technique
41
+ */
42
+ principles?: string[];
43
+ /**
44
+ * The programs the edition was made with, each with its release:
45
+ * the release of this library that found its stemma and grouped its
46
+ * edits, which finds them again with the defaults it has. Adopting a
47
+ * proposal names it, and saving the edition does not, so that the
48
+ * release named is the one that found what the edition states.
49
+ * @see crm:P16 used specific object
50
+ */
51
+ software?: Software[];
31
52
  }
32
53
  /**
33
54
  * Describes the event of recording and documents
@@ -159,6 +180,14 @@ export interface Edition {
159
180
  * @see lrmoo:R75 incorporates
160
181
  */
161
182
  versions: Version[];
183
+ /**
184
+ * The editor's motivations, each a reason for musical changes in
185
+ * words, stated once and named by the edits of any version
186
+ * (`Edit.motivation`). The motivations the vocabulary publishes are
187
+ * named by IRI and not stated here (`musicalChange`,
188
+ * `technicalChange`).
189
+ */
190
+ motivations?: Motivation[];
162
191
  }
163
192
  /**
164
193
  * What an edition states about itself rather than about the copies and
@@ -65,7 +65,7 @@ const exportedMeasurementsOf = (copy) => [copy.measurements.dimensions, copy.mea
65
65
  */
66
66
  export const measuringSoftwareOf = (copy) => {
67
67
  const named = exportedMeasurementsOf(copy)
68
- .flatMap(value => value['@annotation']?.belief.reasons ?? [])
68
+ .flatMap(value => value['@annotation']?.belief?.reasons ?? [])
69
69
  .flatMap(reason => reason.type === 'measurement' ? reason.software ?? [] : []);
70
70
  return [...new Map(named.map(software => [`${software.name}\u0000${software.version ?? ''}`, software])).values()];
71
71
  };
@@ -3,14 +3,7 @@ import { Concept } from "./Agent.js";
3
3
  import { ActorAssignment, Belief, Certainty, DateAssignment, ObjectAssumption, ReferenceAssumption } from "./Assumption.js";
4
4
  import { CollationTolerance } from "../collation/Collation.js";
5
5
  import { AnySymbol } from "./Symbol.js";
6
- import { WithId, WithNote, WithType } from "../shared/utils.js";
7
- /**
8
- * A motivation provides a reason or rationale for an editorial change.
9
- * Motivations are defined at the version level and referenced by edits.
10
- * @see crm:E33 Linguistic Object
11
- * @category Format types
12
- */
13
- export type Motivation = WithType<'motivation'> & WithId & WithNote;
6
+ import { WithId } from "../shared/utils.js";
14
7
  /**
15
8
  * A derivation names the version another one was derived from, together
16
9
  * with the tolerance the two were collated at. How precisely the copies
@@ -148,10 +141,6 @@ export interface Version extends WithId {
148
141
  * @see reo:involvedEdit
149
142
  */
150
143
  edits?: Edit[];
151
- /**
152
- * A collection of motivations used in this version's edits.
153
- */
154
- motivations: Motivation[];
155
144
  }
156
145
  /**
157
146
  * The edits the version states, none where it leaves its text unstated.
@@ -170,7 +159,8 @@ export declare const insertedBy: (version: Readonly<Version>) => AnySymbol[];
170
159
  export declare const deletedBy: (version: Readonly<Version>) => string[];
171
160
  /**
172
161
  * The parents the version names, in the order of `basedOn`, each with the
173
- * certainty its derivation is held with and the belief it rests on.
162
+ * certainty its derivation is held with and the belief it rests on, where
163
+ * it carries one.
174
164
  * @category Versions and stemma
175
165
  */
176
166
  export declare const derivationsOf: (version: Readonly<Version>) => {
@@ -22,12 +22,17 @@ export const insertedBy = (version) => editsOf(version).flatMap(edit => edit.ins
22
22
  export const deletedBy = (version) => editsOf(version).flatMap(edit => edit.delete ?? []);
23
23
  /**
24
24
  * The parents the version names, in the order of `basedOn`, each with the
25
- * certainty its derivation is held with and the belief it rests on.
25
+ * certainty its derivation is held with and the belief it rests on, where
26
+ * it carries one.
26
27
  * @category Versions and stemma
27
28
  */
28
29
  export const derivationsOf = (version) => (version.basedOn ?? []).map(derivation => {
29
30
  const belief = derivation['@annotation']?.belief;
30
- return { parent: idOf(derivation), certainty: certaintyOf(derivation), ...(belief && { belief }) };
31
+ return {
32
+ parent: idOf(derivation),
33
+ certainty: belief?.certainty ?? 'true',
34
+ ...(belief && { belief })
35
+ };
31
36
  });
32
37
  const rankOf = (derivation) => certainties.indexOf(certaintyOf(derivation));
33
38
  /**
@@ -9,15 +9,18 @@ import { EditionOp } from "./draft.js";
9
9
  * tolerance it was collated at and becomes the principal one; the
10
10
  * hypotheses the child stated beside its former one stay.
11
11
  *
12
- * The edits the child already stated are rewritten only as far as a
13
- * collation wrote them (`isCollationsOwn`). An editor's reading of the
12
+ * The edits the child already stated are rewritten only as far as they
13
+ * are the program's (`isCollationsOwn`). An editor's reading of the
14
14
  * difference between the two texts stays, and the symbols it speaks for
15
15
  * are left out of the collation, so that connecting the two again
16
16
  * neither doubles them nor silently drops what somebody established by
17
17
  * hand. An equivalence is the collation's own, since it is drawn from
18
18
  * the two systems' vocabularies rather than read off the paper, but one
19
19
  * drawn again over the very same symbols is kept as it stands, with its
20
- * identifier and whatever was written on it.
20
+ * identifier and whatever was written on it. An insertion or deletion
21
+ * drawn anew has the motivation its shape calls for
22
+ * (`motivationProposedFor`): the generic musical one, or the technical
23
+ * one, which an equivalence drawn anew always has.
21
24
  *
22
25
  * The child's own text is what it shows less what the parent hands
23
26
  * down, so that connecting two versions already connected collates the
@@ -40,7 +43,8 @@ export declare const collateSymbols: (versionId: string, symbolIds: readonly str
40
43
  * Takes the named copies' reading of a symbol back out of the symbol it
41
44
  * was collated into: their carriers pass to a new symbol of the
42
45
  * version's own, the remaining copies keep the symbol they had, and the
43
- * version states the exchange.
46
+ * version states the exchange, with the motivation its shape calls for
47
+ * (`motivationProposedFor`) where the version derives from another.
44
48
  *
45
49
  * This is the inverse of the hand-over a collation makes, and it is
46
50
  * what lets a derivation be collated a second time. A collated symbol