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.
- package/README.md +62 -26
- package/lib/collation/Collation.d.ts +12 -42
- package/lib/collation/Collation.js +15 -48
- package/lib/filiation/adoption.d.ts +23 -4
- package/lib/filiation/adoption.js +81 -23
- package/lib/filiation/edits.d.ts +35 -19
- package/lib/filiation/edits.js +68 -34
- package/lib/filiation/index.d.ts +2 -1
- package/lib/filiation/index.js +2 -1
- package/lib/filiation/record.d.ts +49 -0
- package/lib/filiation/record.js +49 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +1 -0
- package/lib/io/asJsonLd.js +5 -1
- package/lib/io/importJsonLd.js +42 -4
- package/lib/io/migrate.d.ts +7 -0
- package/lib/io/migrate.js +196 -2
- package/lib/model/Agent.d.ts +3 -1
- package/lib/model/Assumption.d.ts +8 -1
- package/lib/model/Assumption.js +2 -1
- package/lib/model/Edit.d.ts +76 -17
- package/lib/model/Edit.js +52 -3
- package/lib/model/Edition.d.ts +32 -3
- package/lib/model/RollCopy.js +1 -1
- package/lib/model/Version.d.ts +3 -13
- package/lib/model/Version.js +7 -2
- package/lib/ops/collation.d.ts +8 -4
- package/lib/ops/collation.js +40 -23
- package/lib/ops/copies.js +1 -2
- package/lib/ops/draft.d.ts +1 -9
- package/lib/ops/draft.js +0 -12
- package/lib/ops/edits.d.ts +20 -3
- package/lib/ops/edits.js +36 -15
- package/lib/ops/index.d.ts +1 -1
- package/lib/ops/index.js +1 -1
- package/lib/ops/versions.d.ts +3 -2
- package/lib/ops/versions.js +4 -5
- package/lib/schema.json +60 -64
- package/lib/software.d.ts +12 -0
- package/lib/software.js +10 -0
- package/lib/spec/context.json +8 -5
- package/package.json +1 -1
package/lib/filiation/edits.js
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The edits of a version, proposed from what it inserts and deletes:
|
|
3
|
-
* which of its changes belong together, and what
|
|
4
|
-
*
|
|
3
|
+
* which of its changes belong together, and what shape each group
|
|
4
|
+
* takes, from which the motivation a program can give it follows. Why
|
|
5
|
+
* a musical change was made is left to the editor.
|
|
5
6
|
*/
|
|
6
7
|
import { v5 } from "uuid";
|
|
8
|
+
import { motivationProposedFor, musicalChange, technicalChange } from "../model/Edit.js";
|
|
7
9
|
import { deletedBy, editsOf, insertedBy } from "../model/Version.js";
|
|
8
10
|
import { distance, mm, subtract } from "../model/Quantity.js";
|
|
9
11
|
import { trackerBarOf } from "../systems/index.js";
|
|
10
12
|
import { operationOf, substitutionsBetween } from "../collation/substitution.js";
|
|
11
|
-
import { isCollationsOwn
|
|
13
|
+
import { isCollationsOwn } from "../collation/Collation.js";
|
|
12
14
|
import { predecessorOf } from "../analysis/stemma.js";
|
|
13
15
|
import { placeOf, snapshotOf } from "../analysis/text.js";
|
|
14
16
|
import { symbolsIn, versionIn } from "../lookup/lookup.js";
|
|
15
17
|
import { groupBy } from "../shared/utils.js";
|
|
16
|
-
import {
|
|
18
|
+
import { reading } from "../ops/draft.js";
|
|
17
19
|
import { ineffectiveAmong } from "./presence.js";
|
|
18
20
|
import { reads } from "./readings.js";
|
|
21
|
+
import { editRules, madeWith } from "./record.js";
|
|
19
22
|
/**
|
|
20
23
|
* How far a perforation may be put from where it stood for the two to be
|
|
21
24
|
* one move. WM 225 moves commands by up to about 60 mm.
|
|
@@ -278,8 +281,8 @@ const rowsAmong = (groups, within) => {
|
|
|
278
281
|
};
|
|
279
282
|
/** Whether the bar spells an accent with exactly these commands. */
|
|
280
283
|
const spellsAccent = (bar, types) => (bar?.accents ?? []).some(accent => sameSequence(types, accent));
|
|
281
|
-
/**
|
|
282
|
-
const
|
|
284
|
+
/** The shape a group of changes takes, where it is one the program knows. */
|
|
285
|
+
const shapeOfGroup = (group, bar, accentsAt, options) => {
|
|
283
286
|
const inserted = inOrder(ofSide(group.changes, 'insert'));
|
|
284
287
|
const deleted = inOrder(ofSide(group.changes, 'delete'));
|
|
285
288
|
if (group.grouping === 'substitution' || group.changes.some(change => change.foreign))
|
|
@@ -343,8 +346,8 @@ const settingsOf = (options) => ({
|
|
|
343
346
|
* punched across three lines or more at one place are one edit, as a
|
|
344
347
|
* cancel row is.
|
|
345
348
|
*
|
|
346
|
-
* The
|
|
347
|
-
* an additional accent, as is a note set off from its chord by a few
|
|
349
|
+
* The shape is read off the group: a pair the bar spells an accent with
|
|
350
|
+
* is an additional accent, as is a note set off from its chord by a few
|
|
348
351
|
* millimetres where the version puts an accent's On; a perforation put
|
|
349
352
|
* elsewhere is a shift, or where it begins about where the other did,
|
|
350
353
|
* shortened or prolonged; a note struck again is shortened, two tied
|
|
@@ -427,11 +430,11 @@ export const proposeEdits = (edition, versionId, options = {}) => {
|
|
|
427
430
|
const proposed = [...substituted, ...recoded, ...lines, ...rows]
|
|
428
431
|
.sort((a, b) => byPlace(inOrder(a.changes)[0], inOrder(b.changes)[0]))
|
|
429
432
|
.map((group) => {
|
|
430
|
-
const
|
|
433
|
+
const shape = shapeOfGroup(group, bar, accentsAt, settings);
|
|
431
434
|
return {
|
|
432
435
|
insert: inOrder(ofSide(group.changes, 'insert')).map(change => change.symbol.id),
|
|
433
436
|
delete: inOrder(ofSide(group.changes, 'delete')).map(change => change.symbol.id),
|
|
434
|
-
...(
|
|
437
|
+
...(shape && { shape }),
|
|
435
438
|
grouping: group.grouping
|
|
436
439
|
};
|
|
437
440
|
});
|
|
@@ -441,60 +444,87 @@ export const proposeEdits = (edition, versionId, options = {}) => {
|
|
|
441
444
|
const EDITS = '3c9e0f62-1d4b-4a7e-9b55-8e2f7a6d1c04';
|
|
442
445
|
const exchangeKey = (inserted, deleted) => `${[...inserted].sort().join(',')}/${[...deleted].sort().join(',')}`;
|
|
443
446
|
/**
|
|
444
|
-
*
|
|
445
|
-
*
|
|
447
|
+
* The motivation a program states for each change the version makes,
|
|
448
|
+
* by the id of the symbol it inserts or deletes: the one the shape of
|
|
449
|
+
* the proposed edit it belongs to calls for (`motivationProposedFor`),
|
|
450
|
+
* musical or technical. A version that derives from none makes no
|
|
451
|
+
* change, and its symbols are given none.
|
|
446
452
|
* @category Filiation
|
|
447
453
|
*/
|
|
448
|
-
export const
|
|
454
|
+
export const proposedMotivations = (edition, versionId, options = {}) => {
|
|
455
|
+
const motivations = new Map();
|
|
456
|
+
if (!predecessorOf(edition, versionId))
|
|
457
|
+
return motivations;
|
|
458
|
+
for (const edit of proposeEdits(edition, versionId, options)) {
|
|
459
|
+
const motivation = motivationProposedFor(edit.shape);
|
|
460
|
+
for (const id of [...edit.insert, ...edit.delete])
|
|
461
|
+
motivations.set(id, motivation);
|
|
462
|
+
}
|
|
463
|
+
return motivations;
|
|
464
|
+
};
|
|
465
|
+
/**
|
|
466
|
+
* The motivation a program states for an edit making these changes:
|
|
467
|
+
* the generic musical one where any of them is a musical change, or one
|
|
468
|
+
* the proposal does not see, and the technical one where all are
|
|
469
|
+
* technical.
|
|
470
|
+
*/
|
|
471
|
+
export const motivationOfChanges = (motivations, symbolIds) => symbolIds.some(id => (motivations.get(id) ?? musicalChange) === musicalChange) ? musicalChange : technicalChange;
|
|
449
472
|
/**
|
|
450
473
|
* The edits the version comes to state when the proposal is adopted:
|
|
451
474
|
* those it keeps, and those put in place of the rest.
|
|
452
475
|
*/
|
|
453
476
|
const adoptedEditsOf = (edition, version, options) => {
|
|
454
477
|
const edits = editsOf(version);
|
|
455
|
-
const held = edits.filter(edit => !
|
|
478
|
+
const held = edits.filter(edit => !isCollationsOwn(edit));
|
|
456
479
|
const spoken = new Set(held.flatMap(edit => [...(edit.insert ?? []).map(symbol => symbol.id), ...(edit.delete ?? [])]));
|
|
457
480
|
const symbolOf = new Map(insertedBy(version).map(symbol => [symbol.id, symbol]));
|
|
458
481
|
const stated = new Map(edits.map(edit => [exchangeKey((edit.insert ?? []).map(symbol => symbol.id), edit.delete ?? []), edit]));
|
|
459
|
-
const made = (inserted, deleted,
|
|
482
|
+
const made = (inserted, deleted, motivation) => {
|
|
460
483
|
const key = exchangeKey(inserted, deleted);
|
|
461
484
|
const same = stated.get(key);
|
|
485
|
+
// An edit over the same changes stays, with the kind of change it states, which the editor may have said.
|
|
462
486
|
if (same)
|
|
463
|
-
return same.
|
|
487
|
+
return same.motivation === undefined ? { ...same, motivation } : same;
|
|
464
488
|
return {
|
|
465
489
|
type: 'edit',
|
|
466
490
|
id: v5(`edit|${version.id}|${key}`, EDITS),
|
|
467
|
-
|
|
468
|
-
motivation: unchecked,
|
|
491
|
+
motivation,
|
|
469
492
|
...(inserted.length > 0 && { insert: inserted.map(id => symbolOf.get(id)) }),
|
|
470
493
|
...(deleted.length > 0 && { delete: [...deleted] })
|
|
471
494
|
};
|
|
472
495
|
};
|
|
473
496
|
const proposed = proposeEdits(edition, version.id, options).flatMap(edit => {
|
|
497
|
+
const motivation = motivationProposedFor(edit.shape);
|
|
474
498
|
const free = (ids) => ids.filter(id => !spoken.has(id));
|
|
475
499
|
const [inserted, deleted] = [free(edit.insert), free(edit.delete)];
|
|
476
500
|
if (inserted.length === edit.insert.length && deleted.length === edit.delete.length) {
|
|
477
|
-
return [made(inserted, deleted,
|
|
501
|
+
return [made(inserted, deleted, motivation)];
|
|
478
502
|
}
|
|
479
503
|
// What an edit that stays leaves of a proposed one stands symbol by symbol.
|
|
480
|
-
return [...inserted.map(id => made([id], [])), ...deleted.map(id => made([], [id]))];
|
|
504
|
+
return [...inserted.map(id => made([id], [], motivation)), ...deleted.map(id => made([], [id], motivation))];
|
|
481
505
|
});
|
|
482
|
-
return
|
|
506
|
+
return { held, proposed };
|
|
483
507
|
};
|
|
484
508
|
/**
|
|
485
|
-
* Puts the proposed edits (`proposeEdits`) in place of
|
|
486
|
-
*
|
|
487
|
-
*
|
|
488
|
-
*
|
|
489
|
-
*
|
|
490
|
-
*
|
|
491
|
-
*
|
|
492
|
-
*
|
|
493
|
-
*
|
|
494
|
-
*
|
|
509
|
+
* Puts the proposed edits (`proposeEdits`) in place of the program's
|
|
510
|
+
* edits of the versions, every version but the root where none are
|
|
511
|
+
* named: those that say nothing of why they were made, or only that they
|
|
512
|
+
* are musical or technical changes (`isCollationsOwn`). An edit with a
|
|
513
|
+
* motivation of the editor's or held under a belief stays as it is, and
|
|
514
|
+
* what a proposed edit shares with it stands symbol by symbol beside it.
|
|
515
|
+
* An edit stated already over the very same symbols as a proposed one
|
|
516
|
+
* is kept with its id and the kind of change it states, which the
|
|
517
|
+
* editor may have said otherwise than the program read it; the others
|
|
518
|
+
* are new, with ids that adopting again gives again. An edit that
|
|
519
|
+
* states no kind has the motivation its shape calls for
|
|
520
|
+
* (`motivationProposedFor`): the generic musical one for a musical
|
|
521
|
+
* change, whose reason is left to the editor, and the technical one for
|
|
522
|
+
* a technical change.
|
|
495
523
|
*
|
|
496
524
|
* Applied on the same draft after `adoptStemma`, it proposes the edits of
|
|
497
|
-
* the stemma adopted, as one undo step.
|
|
525
|
+
* the stemma adopted, as one undo step. The edition's creation comes to
|
|
526
|
+
* name the rule that keeps an edit at one place among its principles,
|
|
527
|
+
* and this release among its software.
|
|
498
528
|
* @category Operations
|
|
499
529
|
*/
|
|
500
530
|
export const adoptEdits = (versionIds, options = {}) => reading(edition => {
|
|
@@ -502,12 +532,14 @@ export const adoptEdits = (versionIds, options = {}) => reading(edition => {
|
|
|
502
532
|
.map(id => versionIn(edition, id))
|
|
503
533
|
.filter((version) => version !== undefined && predecessorOf(edition, version.id) !== undefined);
|
|
504
534
|
const adopted = new Map(targets.flatMap(version => {
|
|
505
|
-
const
|
|
535
|
+
const { held, proposed } = adoptedEditsOf(edition, version, options);
|
|
536
|
+
const edits = [...held, ...proposed];
|
|
506
537
|
const stated = editsOf(version);
|
|
507
538
|
if (edits.length === stated.length && edits.every((edit, index) => edit === stated[index]))
|
|
508
539
|
return [];
|
|
509
|
-
return [[version.id, { ...version, edits
|
|
540
|
+
return [[version.id, { ...version, edits }]];
|
|
510
541
|
}));
|
|
542
|
+
const creation = madeWith(edition.creation, [editRules.onePlace]);
|
|
511
543
|
// Each version is put in place whole: one that an operation before put
|
|
512
544
|
// on the same draft, as adopting a stemma does, is no draft to write to.
|
|
513
545
|
return draft => {
|
|
@@ -516,5 +548,7 @@ export const adoptEdits = (versionIds, options = {}) => reading(edition => {
|
|
|
516
548
|
if (changed)
|
|
517
549
|
draft.versions[index] = changed;
|
|
518
550
|
});
|
|
551
|
+
if (adopted.size > 0)
|
|
552
|
+
draft.creation = creation;
|
|
519
553
|
};
|
|
520
554
|
});
|
package/lib/filiation/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export { defaultCosts, type Costs, type Scored, type Support } from "./search.js
|
|
|
9
9
|
export { transfersOf, systemAbove } from "./systems.js";
|
|
10
10
|
export { proposeStemma, defaultMovedWithin, type FiliationOptions, type ProposedVersion, type StemmaProposal, type Unpinned } from "./proposal.js";
|
|
11
11
|
export { adoptionOf, adoptStemma, type Adoption } from "./adoption.js";
|
|
12
|
+
export { stemmaRules, editRules } from "./record.js";
|
|
12
13
|
export { stemmaChanges, type VersionChange } from "./changes.js";
|
|
13
14
|
export { sharedTexts, differencesBetween, defaultSameText, type SharedText, type KeptApart } from "./texts.js";
|
|
14
|
-
export { proposeEdits, adoptEdits,
|
|
15
|
+
export { proposeEdits, adoptEdits, proposedMotivations, defaultShiftWithin, defaultPairWithin, defaultResizeWithin, defaultSamePlaceWithin, type EditOptions, type EditGrouping, type ProposedEdit } from "./edits.js";
|
package/lib/filiation/index.js
CHANGED
|
@@ -9,6 +9,7 @@ export { defaultCosts } from "./search.js";
|
|
|
9
9
|
export { transfersOf, systemAbove } from "./systems.js";
|
|
10
10
|
export { proposeStemma, defaultMovedWithin } from "./proposal.js";
|
|
11
11
|
export { adoptionOf, adoptStemma } from "./adoption.js";
|
|
12
|
+
export { stemmaRules, editRules } from "./record.js";
|
|
12
13
|
export { stemmaChanges } from "./changes.js";
|
|
13
14
|
export { sharedTexts, differencesBetween, defaultSameText } from "./texts.js";
|
|
14
|
-
export { proposeEdits, adoptEdits,
|
|
15
|
+
export { proposeEdits, adoptEdits, proposedMotivations, defaultShiftWithin, defaultPairWithin, defaultResizeWithin, defaultSamePlaceWithin } from "./edits.js";
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What adopting a proposal records of how it was found: the published
|
|
3
|
+
* rules the proposal follows, among the principles of the edition, and
|
|
4
|
+
* the release of this library, among the software it was made with.
|
|
5
|
+
*/
|
|
6
|
+
import { EditionCreation } from "../model/Edition.js";
|
|
7
|
+
/**
|
|
8
|
+
* The published rules the proposal of a stemma follows, by IRI, each
|
|
9
|
+
* under the cost or the step that carries it out. They are the
|
|
10
|
+
* editorial principles of the edition of WM 225, which are suggested
|
|
11
|
+
* for the filiation of piano rolls at large.
|
|
12
|
+
* @category Filiation
|
|
13
|
+
*/
|
|
14
|
+
export declare const stemmaRules: {
|
|
15
|
+
/** An addition at the same place is not made twice independently, so a reading arising costs ten times one lost. */
|
|
16
|
+
readonly sharedAddition: 'https://welte225.org/rules#shared-addition';
|
|
17
|
+
/** Redundancies are cleaned up in two branches independently, so losing one counts for less. */
|
|
18
|
+
readonly independentCleanup: 'https://welte225.org/rules#independent-cleanup';
|
|
19
|
+
/** A re-cut for another system was an editorial pass of its own, so what it lacks counts for less. */
|
|
20
|
+
readonly recutLoss: 'https://welte225.org/rules#recut-loss';
|
|
21
|
+
/** A copy may fail to show a reading of the version it carries. */
|
|
22
|
+
readonly copyDeviation: 'https://welte225.org/rules#copy-deviation';
|
|
23
|
+
/** A lost state is posited only where shared additions require it. */
|
|
24
|
+
readonly lostStates: 'https://welte225.org/rules#lost-states';
|
|
25
|
+
/** Copies of one system that differ in a few readings carry one text. */
|
|
26
|
+
readonly oneText: 'https://welte225.org/rules#one-text';
|
|
27
|
+
/** Later versions are the more refined, which orients the root. */
|
|
28
|
+
readonly refinement: 'https://welte225.org/rules#refinement';
|
|
29
|
+
/** A version derives from one exemplar, so a stemma is a tree. */
|
|
30
|
+
readonly noContamination: 'https://welte225.org/rules#no-contamination';
|
|
31
|
+
};
|
|
32
|
+
/**
|
|
33
|
+
* The published rules the proposal of edits follows, by IRI.
|
|
34
|
+
* @category Filiation
|
|
35
|
+
*/
|
|
36
|
+
export declare const editRules: {
|
|
37
|
+
/** An edit stays at one place: it runs across no command both texts keep. */
|
|
38
|
+
readonly onePlace: 'https://welte225.org/rules#one-place';
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* The edition's creation as adopting a proposal leaves it. Its
|
|
42
|
+
* principles come to name the rules the proposal follows, in place of
|
|
43
|
+
* those given to be replaced, which are the rules of the same kind that
|
|
44
|
+
* it no longer follows; the principles the editor named stay. Its
|
|
45
|
+
* software comes to name this release of the library, in place of any
|
|
46
|
+
* other release of it, so that the release named finds again what the
|
|
47
|
+
* edition states.
|
|
48
|
+
*/
|
|
49
|
+
export declare const madeWith: (creation: Readonly<EditionCreation>, rules: readonly string[], replacing?: readonly string[]) => EditionCreation;
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { linkedRolls } from "../software.js";
|
|
2
|
+
/**
|
|
3
|
+
* The published rules the proposal of a stemma follows, by IRI, each
|
|
4
|
+
* under the cost or the step that carries it out. They are the
|
|
5
|
+
* editorial principles of the edition of WM 225, which are suggested
|
|
6
|
+
* for the filiation of piano rolls at large.
|
|
7
|
+
* @category Filiation
|
|
8
|
+
*/
|
|
9
|
+
export const stemmaRules = {
|
|
10
|
+
/** An addition at the same place is not made twice independently, so a reading arising costs ten times one lost. */
|
|
11
|
+
sharedAddition: 'https://welte225.org/rules#shared-addition',
|
|
12
|
+
/** Redundancies are cleaned up in two branches independently, so losing one counts for less. */
|
|
13
|
+
independentCleanup: 'https://welte225.org/rules#independent-cleanup',
|
|
14
|
+
/** A re-cut for another system was an editorial pass of its own, so what it lacks counts for less. */
|
|
15
|
+
recutLoss: 'https://welte225.org/rules#recut-loss',
|
|
16
|
+
/** A copy may fail to show a reading of the version it carries. */
|
|
17
|
+
copyDeviation: 'https://welte225.org/rules#copy-deviation',
|
|
18
|
+
/** A lost state is posited only where shared additions require it. */
|
|
19
|
+
lostStates: 'https://welte225.org/rules#lost-states',
|
|
20
|
+
/** Copies of one system that differ in a few readings carry one text. */
|
|
21
|
+
oneText: 'https://welte225.org/rules#one-text',
|
|
22
|
+
/** Later versions are the more refined, which orients the root. */
|
|
23
|
+
refinement: 'https://welte225.org/rules#refinement',
|
|
24
|
+
/** A version derives from one exemplar, so a stemma is a tree. */
|
|
25
|
+
noContamination: 'https://welte225.org/rules#no-contamination'
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* The published rules the proposal of edits follows, by IRI.
|
|
29
|
+
* @category Filiation
|
|
30
|
+
*/
|
|
31
|
+
export const editRules = {
|
|
32
|
+
/** An edit stays at one place: it runs across no command both texts keep. */
|
|
33
|
+
onePlace: 'https://welte225.org/rules#one-place'
|
|
34
|
+
};
|
|
35
|
+
/**
|
|
36
|
+
* The edition's creation as adopting a proposal leaves it. Its
|
|
37
|
+
* principles come to name the rules the proposal follows, in place of
|
|
38
|
+
* those given to be replaced, which are the rules of the same kind that
|
|
39
|
+
* it no longer follows; the principles the editor named stay. Its
|
|
40
|
+
* software comes to name this release of the library, in place of any
|
|
41
|
+
* other release of it, so that the release named finds again what the
|
|
42
|
+
* edition states.
|
|
43
|
+
*/
|
|
44
|
+
export const madeWith = (creation, rules, replacing = []) => {
|
|
45
|
+
const replaced = new Set(replacing);
|
|
46
|
+
const principles = [...new Set([...(creation.principles ?? []).filter(rule => !replaced.has(rule)), ...rules])];
|
|
47
|
+
const software = [...(creation.software ?? []).filter(program => program.name !== linkedRolls.name), linkedRolls];
|
|
48
|
+
return { ...creation, principles, software };
|
|
49
|
+
};
|
package/lib/index.d.ts
CHANGED
|
@@ -61,6 +61,7 @@ export * from './analysis/witnesses.js';
|
|
|
61
61
|
export { alignFeatures, ALIGNMENT_METHOD, fromAxis, ownFeaturesOf, toAxis, tooShortToShorten, type AlignmentResult } from './collation/alignment.js';
|
|
62
62
|
export * from './collation/statistics.js';
|
|
63
63
|
export * from './model/Edition.js';
|
|
64
|
+
export { linkedRolls } from './software.js';
|
|
64
65
|
export * from './lookup/lookup.js';
|
|
65
66
|
export * from './lookup/paths.js';
|
|
66
67
|
export * from './analysis/stemma.js';
|
package/lib/index.js
CHANGED
|
@@ -24,6 +24,7 @@ export * from './analysis/witnesses.js';
|
|
|
24
24
|
export { alignFeatures, ALIGNMENT_METHOD, fromAxis, ownFeaturesOf, toAxis, tooShortToShorten } from './collation/alignment.js';
|
|
25
25
|
export * from './collation/statistics.js';
|
|
26
26
|
export * from './model/Edition.js';
|
|
27
|
+
export { linkedRolls } from './software.js';
|
|
27
28
|
export * from './lookup/lookup.js';
|
|
28
29
|
export * from './lookup/paths.js';
|
|
29
30
|
export * from './analysis/stemma.js';
|
package/lib/io/asJsonLd.js
CHANGED
|
@@ -114,7 +114,11 @@ const isDoubtedReference = (value) => isRecord(value)
|
|
|
114
114
|
*/
|
|
115
115
|
const quote = (subject, key, reference, listed) => {
|
|
116
116
|
const { '@annotation': { '@id': annotation, ...about }, ...object } = reference;
|
|
117
|
-
return {
|
|
117
|
+
return {
|
|
118
|
+
'@id': { '@id': subject, [key]: listed ? [object] : object },
|
|
119
|
+
annotation,
|
|
120
|
+
...about
|
|
121
|
+
};
|
|
118
122
|
};
|
|
119
123
|
/** The value a node states under a key, less the doubted references, which go to the quoted. */
|
|
120
124
|
const unquoted = (subject, key, value, quoted) => {
|
package/lib/io/importJsonLd.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { derivedKeys } from "./asJsonLd.js";
|
|
2
|
-
import { migrate, plainCopyId } from "./migrate.js";
|
|
2
|
+
import { migrate, plainCopyId, uncheckedMotivation } from "./migrate.js";
|
|
3
3
|
import { isDateString } from "../shared/utils.js";
|
|
4
4
|
import { putOnAxis } from "../collation/alignment.js";
|
|
5
|
+
import { editsOf } from "../model/Version.js";
|
|
6
|
+
import { motivationOfChanges, proposedMotivations } from "../filiation/edits.js";
|
|
5
7
|
import schema from "../schema.json" with { type: 'json' };
|
|
6
8
|
export const importDate = (str) => {
|
|
7
9
|
const [y, m, d] = str.split('-').map(s => parseInt(s, 10));
|
|
@@ -83,9 +85,21 @@ const withoutEmptyCreations = (json) => Array.isArray(json.versions)
|
|
|
83
85
|
: json;
|
|
84
86
|
/** A statement an export quoted rather than stated: an included node whose id is a triple. */
|
|
85
87
|
const isQuotedStatement = (node) => node !== null && typeof node === 'object' && node['@id'] !== null && typeof node['@id'] === 'object';
|
|
86
|
-
/**
|
|
88
|
+
/**
|
|
89
|
+
* A belief named by its id alone, as a quoted statement of a document
|
|
90
|
+
* written before 0.76 named the belief of the proposition set it
|
|
91
|
+
* belonged to, which was stated with the set (`migrate`).
|
|
92
|
+
*/
|
|
93
|
+
const isNamedBelief = (belief) => belief !== null && typeof belief === 'object' && Object.keys(belief).every(key => key === '@id');
|
|
94
|
+
/**
|
|
95
|
+
* The reference a quoted statement made, annotated again with the belief
|
|
96
|
+
* the export set beside it, or where it belonged to a proposition set,
|
|
97
|
+
* naming the set again for `migrate` to resolve.
|
|
98
|
+
*/
|
|
87
99
|
const referenceOf = (statement) => {
|
|
88
|
-
const { '@id': { '@id': subject, ...made }, annotation, ...
|
|
100
|
+
const { '@id': { '@id': subject, ...made }, annotation, ...stated } = statement;
|
|
101
|
+
const { belief, ...rest } = stated;
|
|
102
|
+
const about = isNamedBelief(belief) ? rest : stated;
|
|
89
103
|
const [key, value] = Object.entries(made)[0];
|
|
90
104
|
const listed = Array.isArray(value);
|
|
91
105
|
return {
|
|
@@ -142,5 +156,29 @@ export const importJsonLd = (json) => {
|
|
|
142
156
|
? context.find((c) => c['@base'])?.['@base'] || ''
|
|
143
157
|
: '';
|
|
144
158
|
(edition.copies ?? []).forEach(putOnAxis);
|
|
145
|
-
return edition;
|
|
159
|
+
return withKindsReadOffShapes(edition);
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Gives an edit that a document written before 0.76 left unchecked the
|
|
163
|
+
* motivation its shape calls for, read in the edition as it stands: the
|
|
164
|
+
* generic musical one or the technical one (`migrate`). The
|
|
165
|
+
* shapes are read off a copy, since what reads an edition settles it
|
|
166
|
+
* for good, and the edition an import returns is the caller's to change.
|
|
167
|
+
*/
|
|
168
|
+
const withKindsReadOffShapes = (edition) => {
|
|
169
|
+
if (!(edition.versions ?? []).some(version => editsOf(version).some(edit => edit.motivation === uncheckedMotivation)))
|
|
170
|
+
return edition;
|
|
171
|
+
const probe = structuredClone(edition);
|
|
172
|
+
const versions = edition.versions.map(version => {
|
|
173
|
+
if (!editsOf(version).some(edit => edit.motivation === uncheckedMotivation))
|
|
174
|
+
return version;
|
|
175
|
+
const motivations = proposedMotivations(probe, version.id);
|
|
176
|
+
return {
|
|
177
|
+
...version,
|
|
178
|
+
edits: editsOf(version).map(edit => edit.motivation === uncheckedMotivation
|
|
179
|
+
? { ...edit, motivation: motivationOfChanges(motivations, [...(edit.insert ?? []).map(symbol => symbol.id), ...(edit.delete ?? [])]) }
|
|
180
|
+
: edit)
|
|
181
|
+
};
|
|
182
|
+
});
|
|
183
|
+
return { ...edition, versions };
|
|
146
184
|
};
|
package/lib/io/migrate.d.ts
CHANGED
|
@@ -13,6 +13,13 @@
|
|
|
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 motivation an edit of a version that derives from another is left
|
|
18
|
+
* with where only the edition can tell what kind of change it is: one
|
|
19
|
+
* that states neither a kind nor a reason, as a collation once left
|
|
20
|
+
* them. `importJsonLd` reads its kind off its shape (`proposedMotivations`).
|
|
21
|
+
*/
|
|
22
|
+
export declare const uncheckedMotivation = "unchecked";
|
|
16
23
|
/**
|
|
17
24
|
* Brings the JSON of an edition written by an earlier release of the
|
|
18
25
|
* format up to the current shape. A document that is current already
|