linked-rolls 0.63.0 → 0.65.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 +13 -5
- package/lib/analysis/constraints.d.ts +2 -2
- package/lib/analysis/constraints.js +38 -32
- package/lib/analysis/ownPaper.d.ts +3 -3
- package/lib/analysis/ownPaper.js +3 -3
- package/lib/analysis/reservations.d.ts +2 -2
- package/lib/analysis/reservations.js +6 -6
- package/lib/analysis/sigla.d.ts +4 -5
- package/lib/analysis/sigla.js +4 -4
- package/lib/analysis/stemma.d.ts +22 -0
- package/lib/analysis/stemma.js +38 -0
- package/lib/analysis/text.d.ts +31 -0
- package/lib/analysis/text.js +62 -0
- package/lib/analysis/witnesses.d.ts +5 -5
- package/lib/analysis/witnesses.js +27 -22
- package/lib/collation/alignment.js +55 -20
- package/lib/collation/scatter.d.ts +6 -6
- package/lib/collation/scatter.js +15 -13
- package/lib/emulation/Emulation.d.ts +7 -7
- package/lib/emulation/Emulation.js +29 -27
- package/lib/emulation/negotiation.d.ts +2 -2
- package/lib/emulation/negotiation.js +3 -2
- package/lib/index.d.ts +4 -1
- package/lib/index.js +4 -1
- package/lib/io/asJsonLd.js +43 -38
- package/lib/io/importJsonLd.js +11 -1
- package/lib/io/migrate.js +20 -26
- package/lib/lookup/lookup.d.ts +32 -0
- package/lib/lookup/lookup.js +40 -0
- package/lib/lookup/paths.d.ts +11 -0
- package/lib/lookup/paths.js +78 -0
- package/lib/lookup/perState.d.ts +32 -0
- package/lib/lookup/perState.js +63 -0
- package/lib/model/Edit.d.ts +4 -0
- package/lib/model/Edition.d.ts +0 -13
- package/lib/model/Version.d.ts +7 -1
- package/lib/ops/beliefs.d.ts +1 -1
- package/lib/ops/beliefs.js +1 -1
- package/lib/ops/collation.d.ts +3 -4
- package/lib/ops/collation.js +38 -32
- package/lib/ops/commands.d.ts +4 -5
- package/lib/ops/commands.js +10 -10
- package/lib/ops/copies.js +11 -3
- package/lib/ops/draft.d.ts +12 -12
- package/lib/ops/draft.js +11 -13
- package/lib/ops/edits.d.ts +1 -2
- package/lib/ops/edits.js +13 -10
- package/lib/ops/features.d.ts +2 -2
- package/lib/ops/features.js +3 -2
- package/lib/ops/immutable.d.ts +7 -1
- package/lib/ops/immutable.js +22 -8
- package/lib/ops/index.d.ts +1 -1
- package/lib/ops/index.js +6 -0
- package/lib/ops/versions.d.ts +2 -3
- package/lib/ops/versions.js +6 -5
- package/lib/readers/phillipsEroll.js +1 -8
- package/lib/readers/stanfordAton.js +2 -6
- package/lib/schema.json +2 -33
- package/lib/systems/welte.d.ts +17 -0
- package/lib/systems/welte.js +17 -1
- package/lib/systems/welteT100/system.d.ts +1 -16
- package/lib/systems/welteT100/system.js +3 -17
- package/lib/systems/welteT98/system.d.ts +1 -12
- package/lib/systems/welteT98/system.js +3 -13
- package/package.json +1 -1
- package/lib/view/EditionView.d.ts +0 -103
- package/lib/view/EditionView.js +0 -329
package/lib/io/migrate.js
CHANGED
|
@@ -2,6 +2,7 @@ import { systemIdIn, systemOf, translationBetween } from "../systems/TrackerBar.
|
|
|
2
2
|
import { trackerBars } from "../systems/index.js";
|
|
3
3
|
import { welteT100 } from "../systems/welteT100/bar.js";
|
|
4
4
|
import { isDateString } from "../shared/utils.js";
|
|
5
|
+
import { mapped, withValues } from "../ops/immutable.js";
|
|
5
6
|
const retiredVersionTypes = new Set(['edition', 'unicum']);
|
|
6
7
|
/**
|
|
7
8
|
* The vocabulary the steps below were written against, as it stood in
|
|
@@ -37,10 +38,10 @@ const referenceKeys = ['alignedWith', 'pairedWith', 'basedOn'];
|
|
|
37
38
|
* The five type terms that were capitalised while the rest of the
|
|
38
39
|
* vocabulary was not, under the keys that carried them then.
|
|
39
40
|
*/
|
|
40
|
-
const lowerCasedTerms = {
|
|
41
|
+
const lowerCasedTerms = Object.entries({
|
|
41
42
|
method: { Print: 'print', Handwriting: 'handwriting', Stamp: 'stamp' },
|
|
42
43
|
material: { Paper: 'paper', Tape: 'tape' }
|
|
43
|
-
};
|
|
44
|
+
});
|
|
44
45
|
const named = (name) => ({ name, sameAs: [] });
|
|
45
46
|
const withRenamedKeys = (node) => Object.keys(node).some(key => Object.hasOwn(renamedKeys, key))
|
|
46
47
|
? Object.fromEntries(Object.entries(node).map(([key, value]) => [renamedKeys[key] ?? key, value]))
|
|
@@ -82,7 +83,7 @@ const withoutVersionSiglum = (node) => {
|
|
|
82
83
|
const { siglum: _labelled, ...rest } = node;
|
|
83
84
|
return rest;
|
|
84
85
|
};
|
|
85
|
-
const withLowerCaseTerms = (node) =>
|
|
86
|
+
const withLowerCaseTerms = (node) => lowerCasedTerms.reduce((result, [key, terms]) => {
|
|
86
87
|
const lowered = terms[result[key]];
|
|
87
88
|
return lowered ? { ...result, [key]: lowered } : result;
|
|
88
89
|
}, node);
|
|
@@ -450,26 +451,16 @@ const withTimeSpanDates = (node) => {
|
|
|
450
451
|
const { '@value': within, '@type': _typed, ...rest } = node;
|
|
451
452
|
return { ...rest, within };
|
|
452
453
|
};
|
|
453
|
-
const
|
|
454
|
+
const nodeSteps = [withRenamedKeys, withRenamedEditType, withTypology, withRenamedType, withoutVersionType, withoutVersionSiglum, withLowerCaseTerms, withSplitMethod, withReferences, withKeeper, withoutEmptyKeeper,
|
|
454
455
|
withPerforator, withProductionNodes, withTypedPerforator, withScale, withoutOps, withDerivationList, withReadingKind, withTimeSpanDates,
|
|
455
|
-
withoutFeatureKind, withBorneFeaturesNamed, withFeaturesInActs, withOwnPlaces, withDriveOfStaggering, withoutPattern, withoutBearings, withoutEntailedType]
|
|
456
|
-
|
|
457
|
-
/** The
|
|
458
|
-
const walked = (items) => {
|
|
459
|
-
const result = items.map(walk);
|
|
460
|
-
return result.every((item, i) => item === items[i]) ? items : result;
|
|
461
|
-
};
|
|
462
|
-
/** The node with each child walked, or the very same node where the walk changed none. */
|
|
463
|
-
const withWalkedChildren = (node) => {
|
|
464
|
-
const entries = Object.entries(node);
|
|
465
|
-
const result = entries.map(([key, child]) => [key, walk(child)]);
|
|
466
|
-
return result.every(([, child], i) => child === entries[i][1]) ? node : Object.fromEntries(result);
|
|
467
|
-
};
|
|
456
|
+
withoutFeatureKind, withBorneFeaturesNamed, withFeaturesInActs, withOwnPlaces, withDriveOfStaggering, withoutPattern, withoutBearings, withoutEntailedType];
|
|
457
|
+
const migrateNode = (node) => nodeSteps.reduce((result, step) => step(result), node);
|
|
458
|
+
/** The value with every node in it migrated, a node before its children; the very same value where nothing changed. */
|
|
468
459
|
const walk = (value) => {
|
|
469
460
|
if (Array.isArray(value))
|
|
470
|
-
return
|
|
461
|
+
return mapped(value, walk);
|
|
471
462
|
if (value && typeof value === 'object')
|
|
472
|
-
return
|
|
463
|
+
return withValues(migrateNode(value), walk);
|
|
473
464
|
return value;
|
|
474
465
|
};
|
|
475
466
|
/** The production of a copy, under whichever of its two names it carries. */
|
|
@@ -576,18 +567,21 @@ const statesNoTolerance = (version) => isSingleDerivation(version.basedOn) && !v
|
|
|
576
567
|
* The collation tolerance was the edition's before it was stated on
|
|
577
568
|
* each derivation. An edition written then collated every version at
|
|
578
569
|
* that one value, so it is written onto every derivation that gives
|
|
579
|
-
* none of its own.
|
|
570
|
+
* none of its own, and the edition states it no longer.
|
|
580
571
|
*/
|
|
581
572
|
const withDerivationTolerance = (edition) => {
|
|
582
|
-
|
|
583
|
-
const versions = Array.isArray(edition.versions) ? edition.versions : [];
|
|
584
|
-
if (!collationTolerance || !versions.some(statesNoTolerance))
|
|
573
|
+
if (!edition.creation || !Object.hasOwn(edition.creation, 'collationTolerance'))
|
|
585
574
|
return edition;
|
|
575
|
+
const { collationTolerance, ...creation } = edition.creation;
|
|
576
|
+
const versions = listAt(edition, 'versions');
|
|
586
577
|
return {
|
|
587
578
|
...edition,
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
: version)
|
|
579
|
+
creation,
|
|
580
|
+
...(collationTolerance && versions && {
|
|
581
|
+
versions: versions.map(version => statesNoTolerance(version)
|
|
582
|
+
? { ...version, basedOn: { ...version.basedOn, collationTolerance } }
|
|
583
|
+
: version)
|
|
584
|
+
})
|
|
591
585
|
};
|
|
592
586
|
};
|
|
593
587
|
/** Release 0.58.0 stated a revision of the format, which the format does not state. */
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Finding what an edition holds by its id. Each kind of entity has an
|
|
3
|
+
* index of its own, built from where the edition holds it when first
|
|
4
|
+
* asked for and kept for as long as the state is (`perState`). The index
|
|
5
|
+
* an entity is found in says what it is, so a lookup needs neither a
|
|
6
|
+
* type discriminator nor a cast.
|
|
7
|
+
*/
|
|
8
|
+
import { Edition } from "../model/Edition.js";
|
|
9
|
+
import { NestedFeature } from "../model/Feature.js";
|
|
10
|
+
import { AnySymbol } from "../model/Symbol.js";
|
|
11
|
+
import { Version } from "../model/Version.js";
|
|
12
|
+
import { Modification, ProductionEvent, RollCopy } from "../model/RollCopy.js";
|
|
13
|
+
export declare const versionIn: (edition: Pick<Edition, 'versions'>, id: string) => Readonly<Version> | undefined;
|
|
14
|
+
export declare const copyIn: (edition: Pick<Edition, 'copies'>, id: string) => Readonly<RollCopy> | undefined;
|
|
15
|
+
/** A symbol any version inserts. */
|
|
16
|
+
export declare const symbolIn: (edition: Pick<Edition, 'versions'>, id: string) => Readonly<AnySymbol> | undefined;
|
|
17
|
+
/** The symbols under the ids, leaving out an id that names none. */
|
|
18
|
+
export declare const symbolsIn: (edition: Pick<Edition, 'versions'>, ids: readonly string[]) => Readonly<AnySymbol>[];
|
|
19
|
+
/** A feature or a patch on any copy, one a patch bears included. */
|
|
20
|
+
export declare const featureIn: (edition: Pick<Edition, 'copies'>, id: string) => Readonly<NestedFeature> | undefined;
|
|
21
|
+
/** The features and patches under the ids, leaving out an id that names none. */
|
|
22
|
+
export declare const featuresIn: (edition: Pick<Edition, 'copies'>, ids: readonly string[]) => Readonly<NestedFeature>[];
|
|
23
|
+
/** The copy a feature sits on, a patch and everything it bears included. */
|
|
24
|
+
export declare const copyOfFeature: (edition: Pick<Edition, 'copies'>, featureId: string) => Readonly<RollCopy> | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* The act a copy states a feature in: the production event that
|
|
27
|
+
* punched it, or the modification that brought it about. A feature
|
|
28
|
+
* a patch bears came onto the copy with the patch, so the act is
|
|
29
|
+
* the one that glued the patch on. Two features stand in one act
|
|
30
|
+
* where this returns the very same object.
|
|
31
|
+
*/
|
|
32
|
+
export declare const actOf: (edition: Pick<Edition, 'copies'>, featureId: string) => Readonly<ProductionEvent | Modification> | undefined;
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { withBorneFeatures } from "../model/Feature.js";
|
|
2
|
+
import { insertedBy } from "../model/Version.js";
|
|
3
|
+
import { featuresMadeBy, featuresOf } from "../model/RollCopy.js";
|
|
4
|
+
import { perState } from "./perState.js";
|
|
5
|
+
const byIdIn = (entities) => new Map(entities.map(entity => [entity.id, entity]));
|
|
6
|
+
const versionsById = perState((edition) => byIdIn(edition.versions));
|
|
7
|
+
const copiesById = perState((edition) => byIdIn(edition.copies));
|
|
8
|
+
const symbolsById = perState((edition) => byIdIn(edition.versions.flatMap(insertedBy)));
|
|
9
|
+
/** Every feature and patch on the copies, those a patch bears included. */
|
|
10
|
+
const featuresOnCopies = (edition) => edition.copies.flatMap(copy => featuresOf(copy).flatMap(withBorneFeatures).map(feature => ({ feature, copy })));
|
|
11
|
+
const featuresById = perState((edition) => new Map(featuresOnCopies(edition).map(({ feature }) => [feature.id, feature])));
|
|
12
|
+
const copiesByFeature = perState((edition) => new Map(featuresOnCopies(edition).map(({ feature, copy }) => [feature.id, copy])));
|
|
13
|
+
export const versionIn = (edition, id) => versionsById(edition).get(id);
|
|
14
|
+
export const copyIn = (edition, id) => copiesById(edition).get(id);
|
|
15
|
+
/** A symbol any version inserts. */
|
|
16
|
+
export const symbolIn = (edition, id) => symbolsById(edition).get(id);
|
|
17
|
+
/** The symbols under the ids, leaving out an id that names none. */
|
|
18
|
+
export const symbolsIn = (edition, ids) => ids.flatMap(id => symbolIn(edition, id) ?? []);
|
|
19
|
+
/** A feature or a patch on any copy, one a patch bears included. */
|
|
20
|
+
export const featureIn = (edition, id) => featuresById(edition).get(id);
|
|
21
|
+
/** The features and patches under the ids, leaving out an id that names none. */
|
|
22
|
+
export const featuresIn = (edition, ids) => ids.flatMap(id => featureIn(edition, id) ?? []);
|
|
23
|
+
/** The copy a feature sits on, a patch and everything it bears included. */
|
|
24
|
+
export const copyOfFeature = (edition, featureId) => copiesByFeature(edition).get(featureId);
|
|
25
|
+
/**
|
|
26
|
+
* The act a copy states a feature in: the production event that
|
|
27
|
+
* punched it, or the modification that brought it about. A feature
|
|
28
|
+
* a patch bears came onto the copy with the patch, so the act is
|
|
29
|
+
* the one that glued the patch on. Two features stand in one act
|
|
30
|
+
* where this returns the very same object.
|
|
31
|
+
*/
|
|
32
|
+
export const actOf = (edition, featureId) => {
|
|
33
|
+
const copy = copyOfFeature(edition, featureId);
|
|
34
|
+
if (!copy)
|
|
35
|
+
return undefined;
|
|
36
|
+
const states = (features) => features.flatMap(withBorneFeatures).some(feature => feature.id === featureId);
|
|
37
|
+
if (states(copy.production?.produced ?? []))
|
|
38
|
+
return copy.production;
|
|
39
|
+
return copy.modifications.find(act => states(featuresMadeBy(act)));
|
|
40
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where in an edition each id stands and where it is referenced, as
|
|
3
|
+
* paths an operation can follow into a draft of it.
|
|
4
|
+
*/
|
|
5
|
+
import { Edition } from "../model/Edition.js";
|
|
6
|
+
export type Path = (string | number)[];
|
|
7
|
+
export declare const getAt: <T>(path: Path, obj: unknown) => T | undefined;
|
|
8
|
+
/** Where the entity with the id stands in the edition, or nothing where it holds none. */
|
|
9
|
+
export declare const pathIn: (edition: Edition, id: string) => Path | undefined;
|
|
10
|
+
/** Where the edition references the id. */
|
|
11
|
+
export declare const linksTo: (edition: Edition, id: string) => Path[];
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { perState } from "./perState.js";
|
|
2
|
+
export const getAt = (path, obj) => {
|
|
3
|
+
let node = obj;
|
|
4
|
+
for (const key of path) {
|
|
5
|
+
if (node == null)
|
|
6
|
+
return undefined;
|
|
7
|
+
node = node[key];
|
|
8
|
+
}
|
|
9
|
+
return node;
|
|
10
|
+
};
|
|
11
|
+
/** Keys under which an object names others by id, singly or in a list. */
|
|
12
|
+
const referenceKeys = ['delete', 'comprehends', 'motivation', 'premises', 'used'];
|
|
13
|
+
const isObject = (v) => v !== null && typeof v === "object";
|
|
14
|
+
/** What a reference may state besides the id: a belief about it, and the tolerance a derivation was collated at. */
|
|
15
|
+
const referenceOwnKeys = new Set(['id', '@annotation', 'collationTolerance']);
|
|
16
|
+
/** An object that names another by its id and says nothing of its own beyond that. */
|
|
17
|
+
const isReferenceOnly = (keys) => keys.every(key => referenceOwnKeys.has(key));
|
|
18
|
+
const laidOut = (trail) => {
|
|
19
|
+
const path = [];
|
|
20
|
+
for (let link = trail; link !== null; link = link.up)
|
|
21
|
+
path.push(link.key);
|
|
22
|
+
return path.reverse();
|
|
23
|
+
};
|
|
24
|
+
/** Walks the edition and records where each id stands and where it is referenced. */
|
|
25
|
+
const placesIn = perState((edition) => {
|
|
26
|
+
const paths = new Map();
|
|
27
|
+
const links = new Map();
|
|
28
|
+
const visited = new WeakSet();
|
|
29
|
+
const link = (id, trail) => {
|
|
30
|
+
const trails = links.get(id);
|
|
31
|
+
if (trails)
|
|
32
|
+
trails.push(trail);
|
|
33
|
+
else
|
|
34
|
+
links.set(id, [trail]);
|
|
35
|
+
};
|
|
36
|
+
const index = (record, keys, trail) => {
|
|
37
|
+
if (typeof record.id !== "string")
|
|
38
|
+
return;
|
|
39
|
+
if (isReferenceOnly(keys))
|
|
40
|
+
link(record.id, { key: 'id', up: trail });
|
|
41
|
+
else if (!paths.has(record.id)) {
|
|
42
|
+
paths.set(record.id, trail);
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
const linkReferences = (record, trail) => referenceKeys.forEach(key => {
|
|
46
|
+
const ref = record[key];
|
|
47
|
+
if (typeof ref === "string")
|
|
48
|
+
link(ref, { key, up: trail });
|
|
49
|
+
else if (Array.isArray(ref))
|
|
50
|
+
ref.forEach((r, i) => {
|
|
51
|
+
if (typeof r === "string")
|
|
52
|
+
link(r, { key: i, up: { key, up: trail } });
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
const traverse = (node, trail) => {
|
|
56
|
+
if (!isObject(node) || visited.has(node))
|
|
57
|
+
return;
|
|
58
|
+
visited.add(node);
|
|
59
|
+
if (Array.isArray(node)) {
|
|
60
|
+
node.forEach((item, i) => traverse(item, { key: i, up: trail }));
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
const record = node;
|
|
64
|
+
const keys = Object.keys(record);
|
|
65
|
+
index(record, keys, trail);
|
|
66
|
+
linkReferences(record, trail);
|
|
67
|
+
keys.forEach(key => traverse(record[key], { key, up: trail }));
|
|
68
|
+
};
|
|
69
|
+
traverse(edition, null);
|
|
70
|
+
return { paths, links };
|
|
71
|
+
});
|
|
72
|
+
/** Where the entity with the id stands in the edition, or nothing where it holds none. */
|
|
73
|
+
export const pathIn = (edition, id) => {
|
|
74
|
+
const trail = placesIn(edition).paths.get(id);
|
|
75
|
+
return trail === undefined ? undefined : laidOut(trail);
|
|
76
|
+
};
|
|
77
|
+
/** Where the edition references the id. */
|
|
78
|
+
export const linksTo = (edition, id) => (placesIn(edition).links.get(id) ?? []).map(laidOut);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Draft } from "immer";
|
|
2
|
+
/**
|
|
3
|
+
* The draft as it stands, as plain data: the state it was drafted from
|
|
4
|
+
* while nothing has changed it, and a copy of it as it now stands once
|
|
5
|
+
* something has.
|
|
6
|
+
*/
|
|
7
|
+
export declare const takenOf: <T extends object>(draft: Draft<T>) => T;
|
|
8
|
+
/**
|
|
9
|
+
* The edition as a state that no longer changes. It is frozen, deeply
|
|
10
|
+
* and once, as immer freezes what it produces, since what is worked out
|
|
11
|
+
* from a state is kept for as long as the state is: changed in place
|
|
12
|
+
* afterwards, it would leave that behind unnoticed, and frozen, the
|
|
13
|
+
* change throws instead. An edition immer produced is frozen already,
|
|
14
|
+
* and freezing stops at whatever is. A copy taken of a draft is left as
|
|
15
|
+
* it is (`takenOf`).
|
|
16
|
+
*
|
|
17
|
+
* A draft is no such state, since the next write changes it. It is read
|
|
18
|
+
* as it stands by taking `stateOf(draft)` first, once for all that is
|
|
19
|
+
* asked of it; asked of directly, it would have to be copied for every
|
|
20
|
+
* question.
|
|
21
|
+
*/
|
|
22
|
+
export declare const settled: <T extends object>(edition: T) => T;
|
|
23
|
+
/**
|
|
24
|
+
* What is worked out from an edition, once for each state of it.
|
|
25
|
+
*
|
|
26
|
+
* Every change to an edition yields a new object, immer's produce
|
|
27
|
+
* included, so the state an object stands for never changes and what
|
|
28
|
+
* was worked out from it holds for as long as the object is around.
|
|
29
|
+
* Nothing is worked out before it is first asked for, and nothing is
|
|
30
|
+
* kept once the state is gone.
|
|
31
|
+
*/
|
|
32
|
+
export declare const perState: <E extends object, T>(work: (edition: E) => T) => ((edition: E) => T);
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { current, freeze, isDraft, original } from "immer";
|
|
2
|
+
/**
|
|
3
|
+
* Copies taken of a draft as it stood, for an operation to read. Such a
|
|
4
|
+
* copy is the operation's alone and is not frozen: what the operation
|
|
5
|
+
* reads from it may go into the draft, and a later operation on the same
|
|
6
|
+
* draft may go on to change it there, as it may anything written there.
|
|
7
|
+
*/
|
|
8
|
+
const takenOfDrafts = new WeakSet();
|
|
9
|
+
/**
|
|
10
|
+
* The draft as it stands, as plain data: the state it was drafted from
|
|
11
|
+
* while nothing has changed it, and a copy of it as it now stands once
|
|
12
|
+
* something has.
|
|
13
|
+
*/
|
|
14
|
+
export const takenOf = (draft) => {
|
|
15
|
+
const state = current(draft);
|
|
16
|
+
if (state !== original(draft))
|
|
17
|
+
takenOfDrafts.add(state);
|
|
18
|
+
return state;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* The edition as a state that no longer changes. It is frozen, deeply
|
|
22
|
+
* and once, as immer freezes what it produces, since what is worked out
|
|
23
|
+
* from a state is kept for as long as the state is: changed in place
|
|
24
|
+
* afterwards, it would leave that behind unnoticed, and frozen, the
|
|
25
|
+
* change throws instead. An edition immer produced is frozen already,
|
|
26
|
+
* and freezing stops at whatever is. A copy taken of a draft is left as
|
|
27
|
+
* it is (`takenOf`).
|
|
28
|
+
*
|
|
29
|
+
* A draft is no such state, since the next write changes it. It is read
|
|
30
|
+
* as it stands by taking `stateOf(draft)` first, once for all that is
|
|
31
|
+
* asked of it; asked of directly, it would have to be copied for every
|
|
32
|
+
* question.
|
|
33
|
+
*/
|
|
34
|
+
export const settled = (edition) => {
|
|
35
|
+
if (isDraft(edition)) {
|
|
36
|
+
throw new Error('An edition is asked about as a state, not as a draft: take stateOf(draft) first.');
|
|
37
|
+
}
|
|
38
|
+
return takenOfDrafts.has(edition) ? edition : freeze(edition, true);
|
|
39
|
+
};
|
|
40
|
+
/**
|
|
41
|
+
* What is worked out from an edition, once for each state of it.
|
|
42
|
+
*
|
|
43
|
+
* Every change to an edition yields a new object, immer's produce
|
|
44
|
+
* included, so the state an object stands for never changes and what
|
|
45
|
+
* was worked out from it holds for as long as the object is around.
|
|
46
|
+
* Nothing is worked out before it is first asked for, and nothing is
|
|
47
|
+
* kept once the state is gone.
|
|
48
|
+
*/
|
|
49
|
+
export const perState = (work) => {
|
|
50
|
+
const known = new WeakMap();
|
|
51
|
+
return edition => {
|
|
52
|
+
// A state known already was settled when it became known, and a
|
|
53
|
+
// draft never is, so only what is asked of for the first time
|
|
54
|
+
// needs settling.
|
|
55
|
+
const kept = known.get(edition);
|
|
56
|
+
if (kept)
|
|
57
|
+
return kept.value;
|
|
58
|
+
const state = settled(edition);
|
|
59
|
+
const entry = { value: work(state) };
|
|
60
|
+
known.set(state, entry);
|
|
61
|
+
return entry.value;
|
|
62
|
+
};
|
|
63
|
+
};
|
package/lib/model/Edit.d.ts
CHANGED
|
@@ -23,6 +23,10 @@ export type EditType = typeof editTypes[number];
|
|
|
23
23
|
* If an edit is the interpretation of a metamark,
|
|
24
24
|
* such as a pencil mark, this should be made explicit
|
|
25
25
|
* using a meaning comprehension on the `@annotation` field.
|
|
26
|
+
* An edit states no date. That it ended no later than the act that
|
|
27
|
+
* made a feature carrying a symbol it inserts, a reasoner derives with
|
|
28
|
+
* the axioms of reo.ttl; which of those dates bounds it closest is for
|
|
29
|
+
* a query to pick.
|
|
26
30
|
* @see reo:Edit
|
|
27
31
|
*/
|
|
28
32
|
export interface Edit extends WithId, Assumption {
|
package/lib/model/Edition.d.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { RollCopy } from "./RollCopy.js";
|
|
2
2
|
import { Version } from "./Version.js";
|
|
3
|
-
import { CollationTolerance } from "../collation/Collation.js";
|
|
4
3
|
import { DateAssignment, ObjectAssumption } from "./Assumption.js";
|
|
5
4
|
import { Editor, Person, Place } from "./Agent.js";
|
|
6
5
|
import { RollTempo } from "../systems/ReproducingSystem.js";
|
|
@@ -28,18 +27,6 @@ export interface EditionCreation {
|
|
|
28
27
|
* @see dcterms:date
|
|
29
28
|
*/
|
|
30
29
|
publicationDate: Date;
|
|
31
|
-
/**
|
|
32
|
-
* The tolerance every version of the edition was collated at,
|
|
33
|
-
* before each derivation stated its own. How precisely two copies
|
|
34
|
-
* place a symbol depends on which two they are, so the tolerance
|
|
35
|
-
* belongs to the derivation, `Derivation.collationTolerance`, and
|
|
36
|
-
* migration writes this one onto every derivation that gives none.
|
|
37
|
-
* Nothing else reads it. A window's offset is the displacement
|
|
38
|
-
* between two particular copies and so can never be stated here.
|
|
39
|
-
* Not exported to RDF.
|
|
40
|
-
* @deprecated
|
|
41
|
-
*/
|
|
42
|
-
collationTolerance?: CollationTolerance;
|
|
43
30
|
}
|
|
44
31
|
/**
|
|
45
32
|
* Describes the event of recording and documents
|
package/lib/model/Version.d.ts
CHANGED
|
@@ -93,7 +93,13 @@ export interface Version extends WithId {
|
|
|
93
93
|
basedOn?: Derivation[];
|
|
94
94
|
/**
|
|
95
95
|
* The act that made this version, where it is known: who carried it
|
|
96
|
-
* out, when, and by what rule.
|
|
96
|
+
* out, when, and by what rule. The export states the act for every
|
|
97
|
+
* version, stating nothing of it where nothing is known, so that the
|
|
98
|
+
* axioms of reo.ttl can place it in time: it started after the
|
|
99
|
+
* recording and the creation of every version it derives from had
|
|
100
|
+
* started, and ended no later than the punching of any copy carrying
|
|
101
|
+
* a symbol it added. A date stated here stands beside those; which of
|
|
102
|
+
* them bounds the act closest is for a query to pick.
|
|
97
103
|
* @see lrmoo:R17i was created by
|
|
98
104
|
*/
|
|
99
105
|
creation?: VersionCreation;
|
package/lib/ops/beliefs.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Path } from "../
|
|
1
|
+
import { Path } from "../lookup/paths.js";
|
|
2
2
|
import { AnyArgumentation, Certainty } from "../model/Assumption.js";
|
|
3
3
|
import { EditionOp } from "./draft.js";
|
|
4
4
|
/** Annotates the assumption at the path with a belief held true, for reasons to be added. */
|
package/lib/ops/beliefs.js
CHANGED
package/lib/ops/collation.d.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { EditionView } from "../view/EditionView.js";
|
|
2
1
|
import { CollationTolerance } from "../collation/Collation.js";
|
|
3
2
|
import { ObjectAssumption } from "../model/Assumption.js";
|
|
4
3
|
import { EditionOp } from "./draft.js";
|
|
@@ -28,13 +27,13 @@ import { EditionOp } from "./draft.js";
|
|
|
28
27
|
* statement, since the stemma would loop; asked for that, nothing
|
|
29
28
|
* changes.
|
|
30
29
|
*/
|
|
31
|
-
export declare const connectVersions: (
|
|
30
|
+
export declare const connectVersions: (childId: string, parentId: string, tolerance?: ObjectAssumption<CollationTolerance>) => EditionOp;
|
|
32
31
|
/**
|
|
33
32
|
* Folds the version's own symbols into those it inherits and collates
|
|
34
33
|
* with: the carriers pass over, and the insertions go. Collates at the
|
|
35
34
|
* tolerance of the derivation, where the caller names none.
|
|
36
35
|
*/
|
|
37
|
-
export declare const collateSymbols: (
|
|
36
|
+
export declare const collateSymbols: (versionId: string, symbolIds: readonly string[], tolerance?: CollationTolerance) => EditionOp;
|
|
38
37
|
/**
|
|
39
38
|
* Takes the named copies' reading of a symbol back out of the symbol it
|
|
40
39
|
* was collated into: their carriers pass to a new symbol of the
|
|
@@ -59,4 +58,4 @@ export declare const collateSymbols: (given: EditionView, versionId: string, sym
|
|
|
59
58
|
* keeps its identifier and the edit that speaks for it. Named symbols
|
|
60
59
|
* narrow the act to those; naming none separates the whole reading.
|
|
61
60
|
*/
|
|
62
|
-
export declare const separateReadings: (
|
|
61
|
+
export declare const separateReadings: (versionId: string, copies: ReadonlySet<string>, symbolIds?: readonly string[]) => EditionOp;
|
package/lib/ops/collation.js
CHANGED
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
import { v4 } from "uuid";
|
|
2
|
-
import { getAt } from "../
|
|
2
|
+
import { getAt, pathIn } from "../lookup/paths.js";
|
|
3
3
|
import { collationsOf, defaultCollationTolerance, isCollationsOwn, unchecked } from "../collation/Collation.js";
|
|
4
4
|
import { collationToleranceOf, derivesFrom, editsOf, insertedBy, principalDerivationOf } from "../model/Version.js";
|
|
5
5
|
import { substitutionsBetween } from "../collation/substitution.js";
|
|
6
6
|
import { trackerBarOf } from "../systems/index.js";
|
|
7
7
|
import { assignReference, idOf } from "../model/Assumption.js";
|
|
8
|
-
import { noChange, onVersion,
|
|
8
|
+
import { noChange, onVersion, insertion, deletion, asUnchecked, declaring, dropInsertions, reading } from "./draft.js";
|
|
9
9
|
import { hypothesesBeside } from "./versions.js";
|
|
10
|
+
import { placeOf, snapshotOf } from "../analysis/text.js";
|
|
11
|
+
import { copyOfFeature, versionIn } from "../lookup/lookup.js";
|
|
10
12
|
/** The carriers of each collated symbol pass to its counterpart. */
|
|
11
|
-
const handOverCarriers = (
|
|
12
|
-
const path =
|
|
13
|
+
const handOverCarriers = (edition, draft, collations) => collations.forEach(({ symbol, counterpart }) => {
|
|
14
|
+
const path = pathIn(edition, counterpart.id);
|
|
13
15
|
const target = path && getAt(path, draft);
|
|
14
16
|
target?.carriers.push(...symbol.carriers);
|
|
15
17
|
});
|
|
@@ -59,10 +61,10 @@ const byExchange = (edits) => new Map(edits.map(edit => [exchangeKey((edit.inser
|
|
|
59
61
|
* statement, since the stemma would loop; asked for that, nothing
|
|
60
62
|
* changes.
|
|
61
63
|
*/
|
|
62
|
-
export const connectVersions = (
|
|
63
|
-
if (childId === parentId || derivesFrom(
|
|
64
|
+
export const connectVersions = (childId, parentId, tolerance = defaultCollationTolerance) => reading(edition => {
|
|
65
|
+
if (childId === parentId || derivesFrom(edition.versions, parentId, childId))
|
|
64
66
|
return noChange;
|
|
65
|
-
const child =
|
|
67
|
+
const child = versionIn(edition, childId);
|
|
66
68
|
const stated = child ? editsOf(child) : [];
|
|
67
69
|
const established = stated.filter(edit => !isCollationsOwn(edit));
|
|
68
70
|
const spokenFor = spokenForBy(established);
|
|
@@ -71,12 +73,12 @@ export const connectVersions = (given, childId, parentId, tolerance = defaultCol
|
|
|
71
73
|
// through: it is neither the child's own symbol nor one it lacks.
|
|
72
74
|
// Without that, connecting a pair already connected would collate
|
|
73
75
|
// the inherited symbols with themselves and double their carriers.
|
|
74
|
-
const handedDown =
|
|
75
|
-
const shown = new Set(
|
|
76
|
+
const handedDown = snapshotOf(edition, parentId).filter(unspoken);
|
|
77
|
+
const shown = new Set(snapshotOf(edition, childId).map(symbol => symbol.id));
|
|
76
78
|
const inheritedIds = new Set(handedDown.map(symbol => symbol.id));
|
|
77
79
|
const inherited = handedDown.filter(symbol => !shown.has(symbol.id));
|
|
78
|
-
const own =
|
|
79
|
-
const locate = (symbol) =>
|
|
80
|
+
const own = snapshotOf(edition, childId).filter(symbol => unspoken(symbol) && !inheritedIds.has(symbol.id));
|
|
81
|
+
const locate = (symbol) => placeOf(edition, symbol);
|
|
80
82
|
const collations = collationsOf(own, inherited, locate, tolerance);
|
|
81
83
|
const collated = new Set(collations.map(({ symbol }) => symbol.id));
|
|
82
84
|
const matched = new Set(collations.map(({ counterpart }) => counterpart.id));
|
|
@@ -87,7 +89,7 @@ export const connectVersions = (given, childId, parentId, tolerance = defaultCol
|
|
|
87
89
|
* apart would make the apparatus a list of unexplained losses beside
|
|
88
90
|
* a list of unexplained gains.
|
|
89
91
|
*/
|
|
90
|
-
const substituted = differ(child,
|
|
92
|
+
const substituted = differ(child, versionIn(edition, parentId))
|
|
91
93
|
? substitutionsBetween(own.filter(symbol => !collated.has(symbol.id)), inherited.filter(symbol => !matched.has(symbol.id)), locate, tolerance)
|
|
92
94
|
: [];
|
|
93
95
|
const paired = new Set(substituted.flatMap(({ replaced, by }) => [...by, ...replaced].map(symbol => symbol.id)));
|
|
@@ -112,14 +114,18 @@ export const connectVersions = (given, childId, parentId, tolerance = defaultCol
|
|
|
112
114
|
.filter(symbol => !matched.has(symbol.id) && !paired.has(symbol.id))
|
|
113
115
|
.map(symbol => asUnchecked(deletion(symbol.id)))
|
|
114
116
|
];
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
117
|
+
// Read off the child as it stands, since once its edits are written a
|
|
118
|
+
// reading of it would copy them all.
|
|
119
|
+
const motivations = declaring(child?.motivations ?? [], edits);
|
|
120
|
+
const basedOn = [
|
|
121
|
+
{ ...assignReference(parentId), collationTolerance: tolerance },
|
|
122
|
+
...(child ? hypothesesBeside(child, parentId) : [])
|
|
123
|
+
];
|
|
124
|
+
return onVersion(childId, (version, draft) => {
|
|
125
|
+
handOverCarriers(edition, draft, collations);
|
|
126
|
+
version.edits = edits;
|
|
127
|
+
version.motivations = motivations;
|
|
128
|
+
version.basedOn = basedOn;
|
|
123
129
|
});
|
|
124
130
|
});
|
|
125
131
|
/**
|
|
@@ -127,17 +133,17 @@ export const connectVersions = (given, childId, parentId, tolerance = defaultCol
|
|
|
127
133
|
* with: the carriers pass over, and the insertions go. Collates at the
|
|
128
134
|
* tolerance of the derivation, where the caller names none.
|
|
129
135
|
*/
|
|
130
|
-
export const collateSymbols = (
|
|
131
|
-
const version =
|
|
136
|
+
export const collateSymbols = (versionId, symbolIds, tolerance) => reading(edition => {
|
|
137
|
+
const version = versionIn(edition, versionId);
|
|
132
138
|
const principal = version && principalDerivationOf(version);
|
|
133
139
|
if (!version || !principal)
|
|
134
140
|
return noChange;
|
|
135
141
|
const chosen = new Set(symbolIds);
|
|
136
|
-
const own =
|
|
137
|
-
const collations = collationsOf(own,
|
|
142
|
+
const own = insertedBy(version).filter(symbol => chosen.has(symbol.id));
|
|
143
|
+
const collations = collationsOf(own, snapshotOf(edition, idOf(principal)), symbol => placeOf(edition, symbol), tolerance ?? collationToleranceOf(principal));
|
|
138
144
|
const collated = new Set(collations.map(({ symbol }) => symbol.id));
|
|
139
145
|
return onVersion(versionId, (version, draft) => {
|
|
140
|
-
handOverCarriers(
|
|
146
|
+
handOverCarriers(edition, draft, collations);
|
|
141
147
|
dropInsertions(version, collated);
|
|
142
148
|
});
|
|
143
149
|
});
|
|
@@ -163,9 +169,9 @@ const readingOf = (symbol, carriers) => {
|
|
|
163
169
|
* where the two versions do not disagree and there is nothing to take
|
|
164
170
|
* apart.
|
|
165
171
|
*/
|
|
166
|
-
const sharedWith = (
|
|
172
|
+
const sharedWith = (edition, symbol, copies) => {
|
|
167
173
|
const onNamedCopy = (carrier) => {
|
|
168
|
-
const copy =
|
|
174
|
+
const copy = copyOfFeature(edition, idOf(carrier));
|
|
169
175
|
return copy !== undefined && copies.has(copy.id);
|
|
170
176
|
};
|
|
171
177
|
const mine = symbol.carriers.filter(onNamedCopy);
|
|
@@ -196,14 +202,14 @@ const sharedWith = (view, symbol, copies) => {
|
|
|
196
202
|
* keeps its identifier and the edit that speaks for it. Named symbols
|
|
197
203
|
* narrow the act to those; naming none separates the whole reading.
|
|
198
204
|
*/
|
|
199
|
-
export const separateReadings = (
|
|
200
|
-
const version =
|
|
205
|
+
export const separateReadings = (versionId, copies, symbolIds) => reading(edition => {
|
|
206
|
+
const version = versionIn(edition, versionId);
|
|
201
207
|
if (!version)
|
|
202
208
|
return noChange;
|
|
203
209
|
const chosen = symbolIds && new Set(symbolIds);
|
|
204
|
-
const shared =
|
|
210
|
+
const shared = snapshotOf(edition, versionId)
|
|
205
211
|
.filter(symbol => chosen === undefined || chosen.has(symbol.id))
|
|
206
|
-
.flatMap(symbol => sharedWith(
|
|
212
|
+
.flatMap(symbol => sharedWith(edition, symbol, copies));
|
|
207
213
|
if (shared.length === 0)
|
|
208
214
|
return noChange;
|
|
209
215
|
const inserted = new Set(insertedBy(version).map(symbol => symbol.id));
|
|
@@ -218,7 +224,7 @@ export const separateReadings = (given, versionId, copies, symbolIds) => reading
|
|
|
218
224
|
}));
|
|
219
225
|
return onVersion(versionId, (version, draft) => {
|
|
220
226
|
shared.forEach(({ symbol, theirs }) => {
|
|
221
|
-
const path =
|
|
227
|
+
const path = pathIn(edition, symbol.id);
|
|
222
228
|
const target = path && getAt(path, draft);
|
|
223
229
|
if (target)
|
|
224
230
|
target.carriers = theirs;
|
package/lib/ops/commands.d.ts
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import { EditionView } from "../view/EditionView.js";
|
|
2
1
|
import { PlacementRelation } from "../model/Symbol.js";
|
|
3
2
|
import { EditionOp } from "./draft.js";
|
|
4
3
|
/** States how the follower is placed relative to the reference, in place of any earlier statement. */
|
|
5
|
-
export declare const placeCommand: (
|
|
6
|
-
export declare const unplaceCommand: (
|
|
4
|
+
export declare const placeCommand: (followerId: string, referenceId: string, relation: PlacementRelation) => EditionOp;
|
|
5
|
+
export declare const unplaceCommand: (followerId: string) => EditionOp;
|
|
7
6
|
/** The pair is stated on `statingId` only, as the format asks. */
|
|
8
|
-
export declare const pairCommands: (
|
|
9
|
-
export declare const unpairCommand: (
|
|
7
|
+
export declare const pairCommands: (statingId: string, partnerId: string) => EditionOp;
|
|
8
|
+
export declare const unpairCommand: (statingId: string) => EditionOp;
|