linked-rolls 0.58.0 → 0.60.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.
@@ -108,19 +108,23 @@ export declare const uncheckedMotivation: {
108
108
  };
109
109
  /**
110
110
  * Whether the edit is one a collation writes by itself: a bare
111
- * insertion or a bare deletion saying nothing further, or an
112
- * equivalence between two systems' spellings for one command.
111
+ * insertion or a bare deletion saying nothing further, or a recoding
112
+ * that exchanges two systems' spellings for one command.
113
113
  *
114
114
  * Collating again rewrites these and keeps the rest. An edit that says
115
115
  * what the change is, or why it was made, or what it rests on, is an
116
116
  * editor's reading of the difference between two texts, and collating
117
117
  * the two again is no reason to discard it.
118
118
  *
119
- * An equivalence counts as the collation's own even where an editor
119
+ * Such an exchange counts as the collation's own even where an editor
120
120
  * added a motivation to it, because it is derived rather than read:
121
121
  * freezing it would leave the transfers, where nearly every edit is
122
122
  * one, unable to be collated again at all. What an editor wrote on it
123
- * is not lost by that, since `connectVersions` keeps an equivalence it
123
+ * is not lost by that, since `connectVersions` keeps an exchange it
124
124
  * draws a second time over the very same symbols.
125
+ *
126
+ * A recoding that only inserts or only deletes is the editor's: a
127
+ * collation types no one-sided edit, so one typed so is a reading of
128
+ * the transfer that collating again must not undo.
125
129
  */
126
130
  export declare const isCollationsOwn: (edit: Readonly<Edit>) => boolean;
@@ -88,7 +88,7 @@ export const collationsOf = (own, inherited, locate, tolerance = defaultCollatio
88
88
  .map(({ symbol: counterpart }) => ({ symbol, counterpart })));
89
89
  };
90
90
  /** The edit type a collation draws by itself, from the two systems' vocabularies rather than off the paper. */
91
- const drawnByCollation = 'replace-with-equivalent';
91
+ const drawnByCollation = 'recoding';
92
92
  /**
93
93
  * The motivation a collation writes on what it makes, saying that
94
94
  * nobody has read it yet.
@@ -112,22 +112,26 @@ export const uncheckedMotivation = {
112
112
  };
113
113
  /**
114
114
  * Whether the edit is one a collation writes by itself: a bare
115
- * insertion or a bare deletion saying nothing further, or an
116
- * equivalence between two systems' spellings for one command.
115
+ * insertion or a bare deletion saying nothing further, or a recoding
116
+ * that exchanges two systems' spellings for one command.
117
117
  *
118
118
  * Collating again rewrites these and keeps the rest. An edit that says
119
119
  * what the change is, or why it was made, or what it rests on, is an
120
120
  * editor's reading of the difference between two texts, and collating
121
121
  * the two again is no reason to discard it.
122
122
  *
123
- * An equivalence counts as the collation's own even where an editor
123
+ * Such an exchange counts as the collation's own even where an editor
124
124
  * added a motivation to it, because it is derived rather than read:
125
125
  * freezing it would leave the transfers, where nearly every edit is
126
126
  * one, unable to be collated again at all. What an editor wrote on it
127
- * is not lost by that, since `connectVersions` keeps an equivalence it
127
+ * is not lost by that, since `connectVersions` keeps an exchange it
128
128
  * draws a second time over the very same symbols.
129
+ *
130
+ * A recoding that only inserts or only deletes is the editor's: a
131
+ * collation types no one-sided edit, so one typed so is a reading of
132
+ * the transfer that collating again must not undo.
129
133
  */
130
- export const isCollationsOwn = (edit) => edit.editType === drawnByCollation
134
+ export const isCollationsOwn = (edit) => (edit.editType === drawnByCollation && !!edit.insert?.length && !!edit.delete?.length)
131
135
  || (edit.editType === undefined
132
136
  && (edit.motivation === undefined || edit.motivation === unchecked)
133
137
  && edit['@annotation'] === undefined);
package/lib/index.d.ts CHANGED
@@ -45,7 +45,7 @@ export * from './analysis/sigla.js';
45
45
  export * from './model/notes.js';
46
46
  export { asJsonLd } from './io/asJsonLd.js';
47
47
  export { importJsonLd } from './io/importJsonLd.js';
48
- export { formatVersion, migrate } from './io/migrate.js';
48
+ export { migrate } from './io/migrate.js';
49
49
  export { readFromStanfordAton, type StanfordAtonOptions } from './readers/stanfordAton.js';
50
50
  export { readFromPhillipsEroll, CONTROL_OFFSET, phillipsSystems, type PhillipsErollOptions } from './readers/phillipsEroll.js';
51
51
  export { readFromSpencerBar, readSpencerAnn, paperSpeedOfSpencerAnn, SPENCER_ROWS_PER_INCH, type SpencerBarOptions } from './readers/spencerBar.js';
package/lib/index.js CHANGED
@@ -38,7 +38,7 @@ export * from './analysis/sigla.js';
38
38
  export * from './model/notes.js';
39
39
  export { asJsonLd } from './io/asJsonLd.js';
40
40
  export { importJsonLd } from './io/importJsonLd.js';
41
- export { formatVersion, migrate } from './io/migrate.js';
41
+ export { migrate } from './io/migrate.js';
42
42
  export { readFromStanfordAton } from './readers/stanfordAton.js';
43
43
  export { readFromPhillipsEroll, CONTROL_OFFSET, phillipsSystems } from './readers/phillipsEroll.js';
44
44
  export { readFromSpencerBar, readSpencerAnn, paperSpeedOfSpencerAnn, SPENCER_ROWS_PER_INCH } from './readers/spencerBar.js';
@@ -1,7 +1,6 @@
1
1
  import { systemIdIn } from "../systems/TrackerBar.js";
2
2
  import { certaintyOf, isAsserted } from "../model/Assumption.js";
3
3
  import context from "../spec/context.json" with { type: 'json' };
4
- import { formatVersion } from "./migrate.js";
5
4
  /**
6
5
  * The keys an export derives from the tree, which no node of the
7
6
  * edition states itself. An import reads them off again.
@@ -164,7 +163,6 @@ export const asJsonLd = (edition) => {
164
163
  }
165
164
  ],
166
165
  '@id': edition.base,
167
- formatVersion,
168
166
  ...rest,
169
167
  ...(quoted.length > 0 && { '@included': quoted })
170
168
  };
@@ -119,7 +119,7 @@ const withQuotedStatementsInPlace = (edition) => {
119
119
  return withReferencesOn({ ...rest, ...(others.length > 0 && { '@included': others }) }, bySubject);
120
120
  };
121
121
  export const importJsonLd = (json) => {
122
- const { '@context': context, formatVersion: _revision, ...document } = withPlainCopyIds(migrate(withQuotedStatementsInPlace(json)));
122
+ const { '@context': context, ...document } = withPlainCopyIds(migrate(withQuotedStatementsInPlace(json)));
123
123
  const edition = fromJsonLdEntity(document);
124
124
  edition.base = Array.isArray(context)
125
125
  ? context.find((c) => c['@base'])?.['@base'] || ''
@@ -13,14 +13,5 @@
13
13
  type Json = any;
14
14
  /** A copy's id without the `copy/` that documents of earlier releases put before it. */
15
15
  export declare const plainCopyId: (id: string) => string;
16
- /**
17
- * The revision of the format this release writes, stated in every export
18
- * as `formatVersion`. A document stating it is in the current shape and
19
- * passes through untouched; one stating none was written before the
20
- * revisions were numbered and is brought up by recognising its shapes.
21
- * A step added later is to run on the documents of the revisions before
22
- * it, and to raise this number.
23
- */
24
- export declare const formatVersion = 1;
25
16
  export declare const migrate: (edition: Json) => Json;
26
17
  export {};
package/lib/io/migrate.js CHANGED
@@ -4,10 +4,10 @@ import { welteT100 } from "../systems/welteT100/bar.js";
4
4
  import { isDateString } from "../shared/utils.js";
5
5
  const retiredVersionTypes = new Set(['edition', 'unicum']);
6
6
  /**
7
- * The vocabulary the steps below were written against, as it stood when
8
- * the format was first given a revision number. It is copied here rather
9
- * than imported from the model, so that changing today's vocabulary
10
- * cannot change how an old document is read. The tracker bars are still
7
+ * The vocabulary the steps below were written against, as it stood in
8
+ * release 0.58.0. It is copied here rather than imported from the model,
9
+ * so that changing today's vocabulary cannot change how an old document
10
+ * is read. The tracker bars are still
11
11
  * asked, being the layout of instruments rather than a choice of the
12
12
  * format.
13
13
  */
@@ -54,6 +54,13 @@ const withTypology = (node) => {
54
54
  }
55
55
  return node;
56
56
  };
57
+ /** Edit types renamed since: an exchange of two systems' spellings is one kind of recoding. */
58
+ const renamedEditTypes = {
59
+ 'replace-with-equivalent': 'recoding'
60
+ };
61
+ const withRenamedEditType = (node) => typeof node.editType === 'string' && Object.hasOwn(renamedEditTypes, node.editType)
62
+ ? { ...node, editType: renamedEditTypes[node.editType] }
63
+ : node;
57
64
  const withRenamedType = (node) => Object.hasOwn(renamedTypes, node['@type']) ? { ...node, '@type': renamedTypes[node['@type']] } : node;
58
65
  /** Whether the feature is of the class under either name, since a node is migrated before its children are. */
59
66
  const isOfType = (type) => (feature) => feature?.['@type'] === type || renamedTypes[feature?.['@type']] === type;
@@ -371,7 +378,7 @@ const withTimeSpanDates = (node) => {
371
378
  const { '@value': within, '@type': _typed, ...rest } = node;
372
379
  return { ...rest, within };
373
380
  };
374
- const migrateNode = (node) => [withRenamedKeys, withTypology, withRenamedType, withoutVersionType, withoutVersionSiglum, withLowerCaseTerms, withSplitMethod, withReferences, withKeeper, withoutEmptyKeeper,
381
+ const migrateNode = (node) => [withRenamedKeys, withRenamedEditType, withTypology, withRenamedType, withoutVersionType, withoutVersionSiglum, withLowerCaseTerms, withSplitMethod, withReferences, withKeeper, withoutEmptyKeeper,
375
382
  withPerforator, withProductionNodes, withTypedPerforator, withScale, withoutOps, withDerivationList, withReadingKind, withTimeSpanDates,
376
383
  withoutFeatureKind, withBorneFeaturesNamed, withFeaturesInActs, withDriveOfStaggering, withoutPattern, withoutBearings, withoutEntailedType]
377
384
  .reduce((result, step) => step(result), node);
@@ -511,27 +518,12 @@ const withDerivationTolerance = (edition) => {
511
518
  : version)
512
519
  };
513
520
  };
514
- const editionSteps = [withoutEditionType, withSystems, withEditors, withDerivationTolerance];
515
- /**
516
- * The revision of the format this release writes, stated in every export
517
- * as `formatVersion`. A document stating it is in the current shape and
518
- * passes through untouched; one stating none was written before the
519
- * revisions were numbered and is brought up by recognising its shapes.
520
- * A step added later is to run on the documents of the revisions before
521
- * it, and to raise this number.
522
- */
523
- export const formatVersion = 1;
524
- export const migrate = (edition) => {
525
- const stated = edition?.formatVersion;
526
- if (stated === formatVersion)
521
+ /** Release 0.58.0 stated a revision of the format, which the format does not state. */
522
+ const withoutFormatVersion = (edition) => {
523
+ if (!Object.hasOwn(edition, 'formatVersion'))
527
524
  return edition;
528
- if (typeof stated === 'number' && stated > formatVersion) {
529
- throw new Error(`The document is written in revision ${stated} of the format, which this release, reading up to ${formatVersion}, does not know`);
530
- }
531
- const current = walk(editionSteps.reduce((result, step) => step(result), edition));
532
- // What comes out is in the current revision and says so, so that it
533
- // passes through untouched when it is migrated again.
534
- return current !== null && typeof current === 'object' && !Array.isArray(current)
535
- ? { ...current, formatVersion }
536
- : current;
525
+ const { formatVersion: _stated, ...rest } = edition;
526
+ return rest;
537
527
  };
528
+ const editionSteps = [withoutFormatVersion, withoutEditionType, withSystems, withEditors, withDerivationTolerance];
529
+ export const migrate = (edition) => walk(editionSteps.reduce((result, step) => step(result), edition));
@@ -7,7 +7,14 @@ export declare const editTypes: readonly [
7
7
  * sforzando on/off due to the short space left between
8
8
  * the notes to be differentiated.
9
9
  */
10
- 'short-dynamic-differentation', 'additional-accent', 'add-redundancy', 'remove-redundancy', 'replace-with-equivalent', 'shift', 'correct-error', 'shorten', 'prolong'];
10
+ 'short-dynamic-differentation', 'additional-accent', 'add-redundancy', 'remove-redundancy',
11
+ /**
12
+ * Carries the text into another reproducing system's coding: a
13
+ * latched pair of one system giving way to the other's held command,
14
+ * a command the other system cannot read struck, or one of its own
15
+ * put in where the first had no word for it.
16
+ */
17
+ 'recoding', 'shift', 'correct-error', 'shorten', 'prolong'];
11
18
  /**
12
19
  * The type of editorial change applied to a symbol or set of symbols.
13
20
  * Classifies the nature of the edit, e.g. whether it corrects an error,
package/lib/model/Edit.js CHANGED
@@ -8,7 +8,13 @@ export const editTypes = [
8
8
  'additional-accent',
9
9
  'add-redundancy',
10
10
  'remove-redundancy',
11
- 'replace-with-equivalent',
11
+ /**
12
+ * Carries the text into another reproducing system's coding: a
13
+ * latched pair of one system giving way to the other's held command,
14
+ * a command the other system cannot read struck, or one of its own
15
+ * put in where the first had no word for it.
16
+ */
17
+ 'recoding',
12
18
  'shift',
13
19
  'correct-error',
14
20
  'shorten',
@@ -98,7 +98,7 @@ export const connectVersions = (given, childId, parentId, tolerance = defaultCol
98
98
  return alreadyStated.get(exchangeKey(inserted, deleted)) ?? {
99
99
  type: 'edit',
100
100
  id: v4(),
101
- editType: 'replace-with-equivalent',
101
+ editType: 'recoding',
102
102
  motivation: unchecked,
103
103
  insert: [...by],
104
104
  delete: deleted
package/lib/ops/edits.js CHANGED
@@ -31,18 +31,24 @@ const guessEditType = (view, versionId, edit) => {
31
31
  const bar = trackerBarOf(view.version(versionId)?.system);
32
32
  const parentBar = trackerBarOf(view.predecessorOf(versionId)?.system);
33
33
  /**
34
- * Where the version is coded for another system than its parent, an
35
- * exchange of expression matter is the transfer being carried out:
36
- * a red ForzandoOn and ForzandoOff pair giving way to one held green
37
- * SforzandoForte says the same thing in the other system's words,
38
- * which is what 'replace-with-equivalent' is for. Calling it a
39
- * corrected error would say the editor made a mistake.
34
+ * Where the version is coded for another system than its parent,
35
+ * expression matter changed is the transfer being carried out: a red
36
+ * ForzandoOn and ForzandoOff pair giving way to one held green
37
+ * SforzandoForte says the same thing in the other system's words, and
38
+ * a red command the green bar cannot read struck, or a green hold put
39
+ * in where the red had no word for it, belongs to the same act. That
40
+ * is a recoding. Calling it a corrected error would say the editor
41
+ * made a mistake. Only an insertion spelling the bar's own accent
42
+ * reads more narrowly, as the accent it spells.
40
43
  */
41
- if (bar && parentBar && bar.id !== parentBar.id && inserted.length > 0 && deleted.length > 0) {
42
- return 'replace-with-equivalent';
43
- }
44
+ const transferred = bar && parentBar && bar.id !== parentBar.id;
45
+ if (transferred && inserted.length > 0 && deleted.length > 0)
46
+ return 'recoding';
44
47
  if (deleted.length === 0 && accentsOn(bar).some(accent => sameSequence(inserted, accent)))
45
48
  return 'additional-accent';
49
+ const onlyExpressions = [...inserts, ...deletes].every(symbol => symbol.type === 'expression');
50
+ if (transferred && onlyExpressions && inserted.length + deleted.length > 0)
51
+ return 'recoding';
46
52
  if (inserted.length > 1 && sameSequence(inserted, deleted))
47
53
  return 'shift';
48
54
  if (inserted.length === 0 && deleted.length === 1)
package/lib/schema.json CHANGED
@@ -516,7 +516,7 @@
516
516
  "additional-accent",
517
517
  "add-redundancy",
518
518
  "remove-redundancy",
519
- "replace-with-equivalent",
519
+ "recoding",
520
520
  "shift",
521
521
  "correct-error",
522
522
  "shorten",
@@ -89,7 +89,6 @@
89
89
  "@type": "xsd:date"
90
90
  },
91
91
  "collationTolerance": null,
92
- "formatVersion": null,
93
92
  "annotation": null,
94
93
  "roll": "lrmoo:R3i_realises",
95
94
  "catalogueNumber": "dcterms:identifier",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linked-rolls",
3
- "version": "0.58.0",
3
+ "version": "0.60.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": {