linked-rolls 0.62.0 → 0.64.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 +26 -7
- package/lib/analysis/constraints.d.ts +2 -2
- package/lib/analysis/constraints.js +30 -21
- package/lib/analysis/ownPaper.d.ts +11 -16
- package/lib/analysis/ownPaper.js +13 -31
- package/lib/analysis/paper.d.ts +129 -0
- package/lib/analysis/paper.js +260 -0
- 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 +22 -20
- package/lib/collation/alignment.d.ts +46 -8
- package/lib/collation/alignment.js +137 -47
- package/lib/collation/scatter.d.ts +4 -4
- 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 +6 -2
- package/lib/index.js +6 -2
- package/lib/io/asJsonLd.js +16 -1
- package/lib/io/importJsonLd.d.ts +5 -0
- package/lib/io/importJsonLd.js +18 -1
- package/lib/io/migrate.js +94 -2
- 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/Assumption.d.ts +21 -9
- package/lib/model/Edit.d.ts +4 -0
- package/lib/model/Edition.d.ts +19 -0
- package/lib/model/Edition.js +9 -1
- package/lib/model/Quantity.d.ts +4 -1
- package/lib/model/Quantity.js +1 -0
- package/lib/model/RollCopy.d.ts +144 -48
- package/lib/model/RollCopy.js +9 -6
- 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 +25 -23
- package/lib/ops/commands.d.ts +4 -5
- package/lib/ops/commands.js +10 -10
- package/lib/ops/copies.d.ts +47 -10
- package/lib/ops/copies.js +92 -26
- 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/index.d.ts +2 -2
- package/lib/ops/index.js +7 -1
- package/lib/ops/versions.d.ts +2 -3
- package/lib/ops/versions.js +6 -5
- package/lib/readers/stanfordAton.js +28 -1
- package/lib/schema.json +185 -26
- package/lib/spec/context.json +33 -1
- package/lib/systems/ReproducingSystem.d.ts +7 -6
- package/package.json +1 -1
- package/lib/view/EditionView.d.ts +0 -103
- package/lib/view/EditionView.js +0 -329
package/lib/io/importJsonLd.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { derivedKeys } from "./asJsonLd.js";
|
|
2
2
|
import { migrate, plainCopyId } from "./migrate.js";
|
|
3
3
|
import { isDateString } from "../shared/utils.js";
|
|
4
|
+
import { putOnAxis } from "../collation/alignment.js";
|
|
4
5
|
import schema from "../schema.json" with { type: 'json' };
|
|
5
6
|
export const importDate = (str) => {
|
|
6
7
|
const [y, m, d] = str.split('-').map(s => parseInt(s, 10));
|
|
@@ -70,6 +71,16 @@ const withPlainCopyIds = (json) => ({
|
|
|
70
71
|
'@id': typeof copy['@id'] === 'string' ? plainCopyId(copy['@id']) : copy['@id']
|
|
71
72
|
}))
|
|
72
73
|
});
|
|
74
|
+
/** The document without the creations an export gave versions that state none. */
|
|
75
|
+
const withoutEmptyCreations = (json) => Array.isArray(json.versions)
|
|
76
|
+
? {
|
|
77
|
+
...json,
|
|
78
|
+
versions: json.versions.map((version) => {
|
|
79
|
+
const { creation, ...rest } = version;
|
|
80
|
+
return creation !== null && typeof creation === 'object' && Object.keys(creation).length === 0 ? rest : version;
|
|
81
|
+
})
|
|
82
|
+
}
|
|
83
|
+
: json;
|
|
73
84
|
/** A statement an export quoted rather than stated: an included node whose id is a triple. */
|
|
74
85
|
const isQuotedStatement = (node) => node !== null && typeof node === 'object' && node['@id'] !== null && typeof node['@id'] === 'object';
|
|
75
86
|
/** The reference a quoted statement made, annotated again with the belief the export set beside it. */
|
|
@@ -118,11 +129,17 @@ const withQuotedStatementsInPlace = (edition) => {
|
|
|
118
129
|
const bySubject = Map.groupBy(statements.map(referenceOf), ({ subject }) => subject);
|
|
119
130
|
return withReferencesOn({ ...rest, ...(others.length > 0 && { '@included': others }) }, bySubject);
|
|
120
131
|
};
|
|
132
|
+
/**
|
|
133
|
+
* Reads an edition. A document holds each copy's features and tears at
|
|
134
|
+
* the copy's own places, as they were read; the edition holds them on
|
|
135
|
+
* its axis, which is where the copy's alignment carries them.
|
|
136
|
+
*/
|
|
121
137
|
export const importJsonLd = (json) => {
|
|
122
|
-
const { '@context': context, ...document } = withPlainCopyIds(migrate(withQuotedStatementsInPlace(json)));
|
|
138
|
+
const { '@context': context, ...document } = withoutEmptyCreations(withPlainCopyIds(migrate(withQuotedStatementsInPlace(json))));
|
|
123
139
|
const edition = fromJsonLdEntity(document);
|
|
124
140
|
edition.base = Array.isArray(context)
|
|
125
141
|
? context.find((c) => c['@base'])?.['@base'] || ''
|
|
126
142
|
: '';
|
|
143
|
+
(edition.copies ?? []).forEach(putOnAxis);
|
|
127
144
|
return edition;
|
|
128
145
|
};
|
package/lib/io/migrate.js
CHANGED
|
@@ -360,6 +360,78 @@ const withFeaturesInActs = (node) => {
|
|
|
360
360
|
modifications: [...acts, ...(loose.length > 0 ? [{ '@type': 'Attachment', added: loose }] : [])]
|
|
361
361
|
};
|
|
362
362
|
};
|
|
363
|
+
/** A place written to the nanometre, as the export writes places it has carried off the axis. */
|
|
364
|
+
const writtenPlace = (place) => Math.round(place * 1e6) / 1e6;
|
|
365
|
+
/** The span with both ends taken through the mapping, the far end only where it has one. */
|
|
366
|
+
const spanThrough = (span, through) => span && typeof span.from === 'number'
|
|
367
|
+
? { ...span, from: through(span.from), ...(typeof span.to === 'number' && { to: through(span.to) }) }
|
|
368
|
+
: span;
|
|
369
|
+
const isLegacyAligned = (copy) => copy?.measurements?.shift !== undefined || copy?.measurements?.scale !== undefined;
|
|
370
|
+
const isLegacyPaperStretch = (condition) => isPaperStretch(condition) && Object.hasOwn(condition, 'factor');
|
|
371
|
+
const conditionTypeOf = (condition) => condition?.conditionType ?? condition?.['@type'];
|
|
372
|
+
/**
|
|
373
|
+
* A copy's features once stood where its alignment had put them, on the
|
|
374
|
+
* edition's axis, with the shift and the scale that had done it recorded
|
|
375
|
+
* beside them. They stand at the copy's own places now, as they were
|
|
376
|
+
* read, and the alignment says how they go onto the axis; so they are
|
|
377
|
+
* taken back by it, and so are the tears, which were measured as the
|
|
378
|
+
* features are. The features are in their acts here, `withFeaturesInActs`
|
|
379
|
+
* having run.
|
|
380
|
+
*
|
|
381
|
+
* The factor of a paper-stretch condition was the alignment's scale
|
|
382
|
+
* stated again, which is how every such condition came about. It is
|
|
383
|
+
* dropped: the scale stays with the alignment, and what it says about the
|
|
384
|
+
* paper is worked out from all the alignments together.
|
|
385
|
+
*/
|
|
386
|
+
const withOwnPlaces = (node) => {
|
|
387
|
+
if (node['@type'] !== 'RollCopy' || !isLegacyAligned(node))
|
|
388
|
+
return node;
|
|
389
|
+
const { shift: legacyShift, scale: legacyScale, ...measurements } = node.measurements;
|
|
390
|
+
const shift = {
|
|
391
|
+
horizontal: legacyShift?.horizontal ?? 0,
|
|
392
|
+
vertical: legacyShift?.vertical ?? 0
|
|
393
|
+
};
|
|
394
|
+
const scale = legacyScale ?? 1;
|
|
395
|
+
const along = (place) => writtenPlace(place / scale - shift.horizontal);
|
|
396
|
+
const across = (position) => position - shift.vertical;
|
|
397
|
+
const ownFeature = (feature) => {
|
|
398
|
+
if (!feature || typeof feature !== 'object' || !feature.horizontal)
|
|
399
|
+
return feature;
|
|
400
|
+
return {
|
|
401
|
+
...feature,
|
|
402
|
+
horizontal: spanThrough(feature.horizontal, along),
|
|
403
|
+
...(feature.vertical?.unit === 'track' && shift.vertical !== 0 && { vertical: spanThrough(feature.vertical, across) })
|
|
404
|
+
};
|
|
405
|
+
};
|
|
406
|
+
const ownFeatures = (features) => Array.isArray(features) ? features.map(ownFeature) : features;
|
|
407
|
+
const production = node.production && {
|
|
408
|
+
...node.production,
|
|
409
|
+
...(Array.isArray(node.production.produced) && { produced: ownFeatures(node.production.produced) })
|
|
410
|
+
};
|
|
411
|
+
const modifications = Array.isArray(node.modifications)
|
|
412
|
+
? node.modifications.map((act) => act && typeof act === 'object'
|
|
413
|
+
? {
|
|
414
|
+
...act,
|
|
415
|
+
...(Array.isArray(act.produced) && { produced: ownFeatures(act.produced) }),
|
|
416
|
+
...(Array.isArray(act.added) && { added: ownFeatures(act.added) })
|
|
417
|
+
}
|
|
418
|
+
: act)
|
|
419
|
+
: node.modifications;
|
|
420
|
+
const conditions = Array.isArray(node.conditions)
|
|
421
|
+
? node.conditions
|
|
422
|
+
.filter((condition) => !isLegacyPaperStretch(condition))
|
|
423
|
+
.map((condition) => conditionTypeOf(condition) === 'torn' && condition.horizontal
|
|
424
|
+
? { ...condition, horizontal: spanThrough(condition.horizontal, along) }
|
|
425
|
+
: condition)
|
|
426
|
+
: node.conditions;
|
|
427
|
+
return {
|
|
428
|
+
...node,
|
|
429
|
+
measurements: { ...measurements, alignment: { shift, scale } },
|
|
430
|
+
...(production && { production }),
|
|
431
|
+
...(modifications && { modifications }),
|
|
432
|
+
...(conditions && { conditions })
|
|
433
|
+
};
|
|
434
|
+
};
|
|
363
435
|
/** A keeper nobody could name was written as an empty one before a copy could leave it out. */
|
|
364
436
|
const withoutEmptyKeeper = (node) => {
|
|
365
437
|
if (node.keeper?.name !== '' || node.keeper.sameAs?.length)
|
|
@@ -380,7 +452,7 @@ const withTimeSpanDates = (node) => {
|
|
|
380
452
|
};
|
|
381
453
|
const migrateNode = (node) => [withRenamedKeys, withRenamedEditType, withTypology, withRenamedType, withoutVersionType, withoutVersionSiglum, withLowerCaseTerms, withSplitMethod, withReferences, withKeeper, withoutEmptyKeeper,
|
|
382
454
|
withPerforator, withProductionNodes, withTypedPerforator, withScale, withoutOps, withDerivationList, withReadingKind, withTimeSpanDates,
|
|
383
|
-
withoutFeatureKind, withBorneFeaturesNamed, withFeaturesInActs, withDriveOfStaggering, withoutPattern, withoutBearings, withoutEntailedType]
|
|
455
|
+
withoutFeatureKind, withBorneFeaturesNamed, withFeaturesInActs, withOwnPlaces, withDriveOfStaggering, withoutPattern, withoutBearings, withoutEntailedType]
|
|
384
456
|
.reduce((result, step) => step(result), node);
|
|
385
457
|
/** The items each walked, or the very same list where the walk changed none. */
|
|
386
458
|
const walked = (items) => {
|
|
@@ -525,5 +597,25 @@ const withoutFormatVersion = (edition) => {
|
|
|
525
597
|
const { formatVersion: _stated, ...rest } = edition;
|
|
526
598
|
return rest;
|
|
527
599
|
};
|
|
528
|
-
|
|
600
|
+
/** Whether the copy states features, in either shape. */
|
|
601
|
+
const hasFeatures = (copy) => [copy?.features, copy?.production?.produced, copy?.productionEvent?.produced]
|
|
602
|
+
.some(features => Array.isArray(features) && features.length > 0);
|
|
603
|
+
const statesLegacyAlignment = (copy) => isLegacyAligned(copy)
|
|
604
|
+
|| (Array.isArray(copy?.ops) && (copy.ops.includes('shifted') || copy.ops.includes('stretched')));
|
|
605
|
+
/**
|
|
606
|
+
* The copy whose millimetres were the edition's axis went unnamed while
|
|
607
|
+
* the copies aligned onto it recorded only what had been done to them.
|
|
608
|
+
* It was the copy nothing had been done to, and of those the first with
|
|
609
|
+
* features, so that is the one named.
|
|
610
|
+
*/
|
|
611
|
+
const withReferenceCopy = (edition) => {
|
|
612
|
+
const copies = listAt(edition, 'copies');
|
|
613
|
+
if (edition.referenceCopy !== undefined || !copies?.some(statesLegacyAlignment))
|
|
614
|
+
return edition;
|
|
615
|
+
const reference = copies.find(copy => !statesLegacyAlignment(copy) && hasFeatures(copy));
|
|
616
|
+
return typeof reference?.['@id'] === 'string'
|
|
617
|
+
? { ...edition, referenceCopy: plainCopyId(reference['@id']) }
|
|
618
|
+
: edition;
|
|
619
|
+
};
|
|
620
|
+
const editionSteps = [withoutFormatVersion, withoutEditionType, withSystems, withEditors, withDerivationTolerance, withReferenceCopy];
|
|
529
621
|
export const migrate = (edition) => walk(editionSteps.reduce((result, step) => step(result), edition));
|
|
@@ -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
|
+
};
|
|
@@ -46,22 +46,35 @@ export interface Inference extends Argumentation<'inference'> {
|
|
|
46
46
|
/**
|
|
47
47
|
* References (by `@id`) to the beliefs
|
|
48
48
|
* from which the conclusion is drawn.
|
|
49
|
+
*
|
|
50
|
+
* A belief the editor holds and publishes elsewhere under an IRI of
|
|
51
|
+
* its own, such as a bound from a catalogue of premises the editor
|
|
52
|
+
* keeps, is named by that IRI. Someone else's belief is not: it is
|
|
53
|
+
* adopted first, and the belief of the edition's own that the
|
|
54
|
+
* adoption concludes is named instead.
|
|
49
55
|
* @see crminf:J1 used as premise
|
|
50
56
|
*/
|
|
51
57
|
premises: string[];
|
|
52
58
|
/**
|
|
53
59
|
* References (by `@id`) to what the inference worked on besides
|
|
54
|
-
* beliefs, such as the versions it compared
|
|
55
|
-
*
|
|
60
|
+
* beliefs, such as the versions it compared, the analysis output it
|
|
61
|
+
* cites, or the document holding a premise named by IRI, in the
|
|
62
|
+
* state it was read in, since a belief published elsewhere may move
|
|
63
|
+
* after the edition has used it.
|
|
56
64
|
* @see crm:P16 used specific object
|
|
57
65
|
*/
|
|
58
66
|
used?: string[];
|
|
59
67
|
}
|
|
60
68
|
/**
|
|
61
|
-
* A belief adoption
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
*
|
|
69
|
+
* A belief adoption takes over someone else's belief without checking it:
|
|
70
|
+
* knowledge through private communication, from the secondary
|
|
71
|
+
* literature, or from another edition. Like every reason, it concludes
|
|
72
|
+
* the belief it is a reason of, which is the edition's own and holds of
|
|
73
|
+
* the edition's statement what the adopted belief held. An inference that
|
|
74
|
+
* relies on it names that belief among its premises.
|
|
75
|
+
*
|
|
76
|
+
* The editor's own beliefs need no adoption, wherever they are
|
|
77
|
+
* published: an inference names them directly (see `Inference`).
|
|
65
78
|
* @see crminf:I7 Belief Adoption
|
|
66
79
|
*/
|
|
67
80
|
export interface BeliefAdoption extends Argumentation<'beliefAdoption'> {
|
|
@@ -76,9 +89,8 @@ export interface BeliefAdoption extends Argumentation<'beliefAdoption'> {
|
|
|
76
89
|
note: string;
|
|
77
90
|
/**
|
|
78
91
|
* The beliefs this adoption takes over, by IRI, where they are
|
|
79
|
-
* published with one.
|
|
80
|
-
*
|
|
81
|
-
* comes from.
|
|
92
|
+
* published with one. They are someone else's, and stay theirs: what
|
|
93
|
+
* the edition holds is the belief the adoption concludes.
|
|
82
94
|
* @see crminf:J6 adopted
|
|
83
95
|
*/
|
|
84
96
|
adopted?: string[];
|
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
|
@@ -140,6 +140,18 @@ export interface Edition {
|
|
|
140
140
|
* @see reo:witness
|
|
141
141
|
*/
|
|
142
142
|
copies: RollCopy[];
|
|
143
|
+
/**
|
|
144
|
+
* The copy whose millimetres are the edition's axis, by id: every
|
|
145
|
+
* place the edition gives along the roll is a place on this copy's
|
|
146
|
+
* paper, counted from where its scan begins, and every other copy is
|
|
147
|
+
* aligned onto it. Choosing it is the one editorial decision the
|
|
148
|
+
* alignment leaves; a copy scanned whole and without tears serves
|
|
149
|
+
* best, since its places are what notes and readers cite. Left out,
|
|
150
|
+
* it is the first copy with features that is not aligned
|
|
151
|
+
* (`referenceCopyOf`).
|
|
152
|
+
* Not exported to RDF.
|
|
153
|
+
*/
|
|
154
|
+
referenceCopy?: string;
|
|
143
155
|
/**
|
|
144
156
|
* The different versions of the roll on which
|
|
145
157
|
* this edition is based.
|
|
@@ -154,3 +166,10 @@ export interface Edition {
|
|
|
154
166
|
tempoAdjustment?: ObjectAssumption<RollTempo>;
|
|
155
167
|
}
|
|
156
168
|
export type EditionMetadata = Pick<Edition, 'base' | 'title' | 'license' | 'creation' | 'roll'>;
|
|
169
|
+
/**
|
|
170
|
+
* The copy whose millimetres are the edition's axis: the one it names,
|
|
171
|
+
* or where it names none or one it no longer has, the first copy with
|
|
172
|
+
* features that is not aligned, which is how an edition stood before it
|
|
173
|
+
* named one.
|
|
174
|
+
*/
|
|
175
|
+
export declare const referenceCopyOf: (edition: Pick<Edition, 'copies' | 'referenceCopy'>) => RollCopy | undefined;
|
package/lib/model/Edition.js
CHANGED
|
@@ -1 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
import { featuresOf } from "./RollCopy.js";
|
|
2
|
+
/**
|
|
3
|
+
* The copy whose millimetres are the edition's axis: the one it names,
|
|
4
|
+
* or where it names none or one it no longer has, the first copy with
|
|
5
|
+
* features that is not aligned, which is how an edition stood before it
|
|
6
|
+
* named one.
|
|
7
|
+
*/
|
|
8
|
+
export const referenceCopyOf = (edition) => edition.copies.find(copy => copy.id === edition.referenceCopy)
|
|
9
|
+
?? edition.copies.find(copy => !copy.measurements.alignment && featuresOf(copy).length > 0);
|
package/lib/model/Quantity.d.ts
CHANGED
|
@@ -11,7 +11,7 @@ declare const unit: unique symbol;
|
|
|
11
11
|
* The units a record may state. A `Measure` is limited to these, since
|
|
12
12
|
* they are what the `unit` field is allowed to say.
|
|
13
13
|
*/
|
|
14
|
-
export type Unit = 'mm' | 'cm' | 'px' | 'track' | 's' | 'ms' | 'ft/min' | 'm/min' | 'px/in' | 'deg';
|
|
14
|
+
export type Unit = 'mm' | 'cm' | 'px' | 'track' | 's' | 'ms' | 'ft/min' | 'm/min' | 'px/in' | 'deg' | 'percent';
|
|
15
15
|
export type Quantity<U extends string> = number & {
|
|
16
16
|
readonly [unit]: U;
|
|
17
17
|
};
|
|
@@ -30,6 +30,8 @@ export type MetersPerMinute = Quantity<'m/min'>;
|
|
|
30
30
|
export type Resolution = Quantity<'px/in'>;
|
|
31
31
|
/** An angle on the roll, turning from the line across the paper towards the end of the roll. */
|
|
32
32
|
export type Degrees = Quantity<'deg'>;
|
|
33
|
+
/** A share of a length, such as how far the paper has stretched: dimensionless, in hundredths. */
|
|
34
|
+
export type Percent = Quantity<'percent'>;
|
|
33
35
|
/** Names a number in a unit. Partially apply it to make a constructor. */
|
|
34
36
|
export declare const quantity: <U extends string>(value: number) => Quantity<U>;
|
|
35
37
|
export declare const mm: (value: number) => Quantity<"mm">;
|
|
@@ -42,6 +44,7 @@ export declare const feetPerMinute: (value: number) => Quantity<"ft/min">;
|
|
|
42
44
|
export declare const metersPerMinute: (value: number) => Quantity<"m/min">;
|
|
43
45
|
export declare const pixelsPerInch: (value: number) => Quantity<"px/in">;
|
|
44
46
|
export declare const degrees: (value: number) => Quantity<"deg">;
|
|
47
|
+
export declare const percent: (value: number) => Quantity<"percent">;
|
|
45
48
|
/**
|
|
46
49
|
* A value together with the unit it was measured in, as a record
|
|
47
50
|
* states it.
|
package/lib/model/Quantity.js
CHANGED
|
@@ -10,6 +10,7 @@ export const feetPerMinute = quantity;
|
|
|
10
10
|
export const metersPerMinute = quantity;
|
|
11
11
|
export const pixelsPerInch = quantity;
|
|
12
12
|
export const degrees = quantity;
|
|
13
|
+
export const percent = quantity;
|
|
13
14
|
// The second operand takes its unit from the first. Inferred from both,
|
|
14
15
|
// TypeScript would unite two units rather than reject them.
|
|
15
16
|
export const add = (a, b) => quantity(a + b);
|