linked-rolls 0.56.0 → 0.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (131) hide show
  1. package/README.md +54 -361
  2. package/lib/{constraints.d.ts → analysis/constraints.d.ts} +1 -1
  3. package/lib/{constraints.js → analysis/constraints.js} +9 -8
  4. package/lib/analysis/ownPaper.d.ts +26 -0
  5. package/lib/analysis/ownPaper.js +41 -0
  6. package/lib/{reservations.d.ts → analysis/reservations.d.ts} +3 -3
  7. package/lib/{reservations.js → analysis/reservations.js} +3 -3
  8. package/lib/{sigla.d.ts → analysis/sigla.d.ts} +2 -2
  9. package/lib/{sigla.js → analysis/sigla.js} +4 -4
  10. package/lib/{witnesses.d.ts → analysis/witnesses.d.ts} +2 -2
  11. package/lib/{witnesses.js → analysis/witnesses.js} +4 -4
  12. package/lib/{Collation.d.ts → collation/Collation.d.ts} +4 -4
  13. package/lib/{Collation.js → collation/Collation.js} +4 -4
  14. package/lib/{alignment.d.ts → collation/alignment.d.ts} +4 -4
  15. package/lib/{alignment.js → collation/alignment.js} +10 -16
  16. package/lib/{scatter.d.ts → collation/scatter.d.ts} +4 -4
  17. package/lib/{scatter.js → collation/scatter.js} +6 -6
  18. package/lib/{statistics.d.ts → collation/statistics.d.ts} +1 -1
  19. package/lib/{statistics.js → collation/statistics.js} +2 -2
  20. package/lib/{substitution.d.ts → collation/substitution.d.ts} +9 -24
  21. package/lib/{substitution.js → collation/substitution.js} +13 -37
  22. package/lib/emulation/Emulation.d.ts +64 -0
  23. package/lib/emulation/Emulation.js +282 -0
  24. package/lib/emulation/negotiation.d.ts +14 -0
  25. package/lib/emulation/negotiation.js +20 -0
  26. package/lib/index.d.ts +43 -35
  27. package/lib/index.js +36 -35
  28. package/lib/{asJsonLd.d.ts → io/asJsonLd.d.ts} +1 -1
  29. package/lib/{asJsonLd.js → io/asJsonLd.js} +22 -51
  30. package/lib/{context.d.ts → io/context.d.ts} +4 -4
  31. package/lib/{context.js → io/context.js} +4 -4
  32. package/lib/{importJsonLd.d.ts → io/importJsonLd.d.ts} +1 -1
  33. package/lib/io/importJsonLd.js +128 -0
  34. package/lib/{migrate.d.ts → io/migrate.d.ts} +9 -0
  35. package/lib/{migrate.js → io/migrate.js} +97 -54
  36. package/lib/{Agent.d.ts → model/Agent.d.ts} +1 -1
  37. package/lib/{Assumption.d.ts → model/Assumption.d.ts} +1 -1
  38. package/lib/{ConditionState.d.ts → model/ConditionState.d.ts} +1 -2
  39. package/lib/{Edit.d.ts → model/Edit.d.ts} +1 -1
  40. package/lib/{Edition.d.ts → model/Edition.d.ts} +2 -2
  41. package/lib/{Feature.d.ts → model/Feature.d.ts} +3 -1
  42. package/lib/{Feature.js → model/Feature.js} +2 -0
  43. package/lib/{FeatureSource.d.ts → model/FeatureSource.d.ts} +1 -1
  44. package/lib/{Perforator.d.ts → model/Perforator.d.ts} +1 -1
  45. package/lib/{RollCopy.d.ts → model/RollCopy.d.ts} +5 -12
  46. package/lib/{RollCopy.js → model/RollCopy.js} +3 -5
  47. package/lib/{Symbol.d.ts → model/Symbol.d.ts} +1 -1
  48. package/lib/{Version.d.ts → model/Version.d.ts} +11 -3
  49. package/lib/{Version.js → model/Version.js} +24 -1
  50. package/lib/{vocabulary.js → model/vocabulary.js} +2 -2
  51. package/lib/ops/beliefs.d.ts +9 -0
  52. package/lib/ops/beliefs.js +31 -0
  53. package/lib/ops/collation.d.ts +62 -0
  54. package/lib/ops/collation.js +228 -0
  55. package/lib/ops/commands.d.ts +9 -0
  56. package/lib/ops/commands.js +29 -0
  57. package/lib/ops/copies.d.ts +74 -0
  58. package/lib/ops/copies.js +135 -0
  59. package/lib/ops/draft.d.ts +59 -0
  60. package/lib/ops/draft.js +81 -0
  61. package/lib/ops/edits.d.ts +10 -0
  62. package/lib/ops/edits.js +82 -0
  63. package/lib/ops/features.d.ts +84 -0
  64. package/lib/ops/features.js +296 -0
  65. package/lib/ops/forget.d.ts +31 -0
  66. package/lib/ops/forget.js +110 -0
  67. package/lib/ops/immutable.d.ts +16 -0
  68. package/lib/ops/immutable.js +46 -0
  69. package/lib/ops/index.d.ts +13 -0
  70. package/lib/ops/index.js +7 -0
  71. package/lib/ops/versions.d.ts +33 -0
  72. package/lib/ops/versions.js +95 -0
  73. package/lib/readers/AtonParser.js +5 -7
  74. package/lib/readers/phillipsEroll.d.ts +3 -3
  75. package/lib/readers/phillipsEroll.js +4 -5
  76. package/lib/readers/spencerBar.d.ts +2 -2
  77. package/lib/readers/spencerBar.js +2 -3
  78. package/lib/readers/stanfordAton.d.ts +3 -3
  79. package/lib/readers/stanfordAton.js +2 -4
  80. package/lib/schema.json +11 -151
  81. package/lib/spec/context.json +2 -12
  82. package/lib/{ReproducingSystem.d.ts → systems/ReproducingSystem.d.ts} +3 -3
  83. package/lib/{TrackerBar.d.ts → systems/TrackerBar.d.ts} +48 -8
  84. package/lib/{TrackerBar.js → systems/TrackerBar.js} +11 -2
  85. package/lib/systems/index.d.ts +8 -2
  86. package/lib/systems/index.js +7 -1
  87. package/lib/systems/welte.d.ts +100 -0
  88. package/lib/systems/welte.js +119 -0
  89. package/lib/systems/welteLicensee/bar.d.ts +1 -1
  90. package/lib/systems/welteLicensee/bar.js +5 -2
  91. package/lib/systems/welteLicensee/system.d.ts +1 -1
  92. package/lib/systems/welteLicensee/system.js +1 -1
  93. package/lib/systems/welteT100/bar.d.ts +27 -1
  94. package/lib/systems/welteT100/bar.js +44 -2
  95. package/lib/systems/welteT100/system.d.ts +6 -39
  96. package/lib/systems/welteT100/system.js +19 -128
  97. package/lib/systems/welteT98/bar.d.ts +1 -1
  98. package/lib/systems/welteT98/bar.js +18 -2
  99. package/lib/systems/welteT98/system.d.ts +20 -38
  100. package/lib/systems/welteT98/system.js +20 -121
  101. package/lib/validate.d.ts +1 -1
  102. package/lib/{EditionView.d.ts → view/EditionView.d.ts} +38 -48
  103. package/lib/{EditionView.js → view/EditionView.js} +90 -96
  104. package/package.json +7 -2
  105. package/lib/Emulation.d.ts +0 -36
  106. package/lib/Emulation.js +0 -250
  107. package/lib/editionOps.d.ts +0 -260
  108. package/lib/editionOps.js +0 -1081
  109. package/lib/importJsonLd.js +0 -62
  110. /package/lib/{Agent.js → model/Agent.js} +0 -0
  111. /package/lib/{Assumption.js → model/Assumption.js} +0 -0
  112. /package/lib/{ConditionState.js → model/ConditionState.js} +0 -0
  113. /package/lib/{Edit.js → model/Edit.js} +0 -0
  114. /package/lib/{Edition.js → model/Edition.js} +0 -0
  115. /package/lib/{FeatureSource.js → model/FeatureSource.js} +0 -0
  116. /package/lib/{Perforator.js → model/Perforator.js} +0 -0
  117. /package/lib/{Quantity.d.ts → model/Quantity.d.ts} +0 -0
  118. /package/lib/{Quantity.js → model/Quantity.js} +0 -0
  119. /package/lib/{Symbol.js → model/Symbol.js} +0 -0
  120. /package/lib/{TrackCalibration.d.ts → model/TrackCalibration.d.ts} +0 -0
  121. /package/lib/{TrackCalibration.js → model/TrackCalibration.js} +0 -0
  122. /package/lib/{notes.d.ts → model/notes.d.ts} +0 -0
  123. /package/lib/{notes.js → model/notes.js} +0 -0
  124. /package/lib/{procedures.d.ts → model/procedures.d.ts} +0 -0
  125. /package/lib/{procedures.js → model/procedures.js} +0 -0
  126. /package/lib/{vocabulary.d.ts → model/vocabulary.d.ts} +0 -0
  127. /package/lib/{sorted.d.ts → shared/sorted.d.ts} +0 -0
  128. /package/lib/{sorted.js → shared/sorted.js} +0 -0
  129. /package/lib/{utils.d.ts → shared/utils.d.ts} +0 -0
  130. /package/lib/{utils.js → shared/utils.js} +0 -0
  131. /package/lib/{ReproducingSystem.js → systems/ReproducingSystem.js} +0 -0
@@ -0,0 +1,128 @@
1
+ import { derivedKeys } from "./asJsonLd.js";
2
+ import { migrate, plainCopyId } from "./migrate.js";
3
+ import { isDateString } from "../shared/utils.js";
4
+ import schema from "../schema.json" with { type: 'json' };
5
+ export const importDate = (str) => {
6
+ const [y, m, d] = str.split('-').map(s => parseInt(s, 10));
7
+ if ([y, m, d].some(n => isNaN(n))) {
8
+ throw new Error(`Invalid date format: "${str}". Expected "YYYY-MM-DD".`);
9
+ }
10
+ return new Date(y, m - 1, d);
11
+ };
12
+ /** The names of the properties the schema holds dates under, wherever they stand in it. */
13
+ const dateKeysIn = (node, keys = new Set()) => {
14
+ if (Array.isArray(node))
15
+ node.forEach(item => dateKeysIn(item, keys));
16
+ else if (node !== null && typeof node === 'object') {
17
+ Object.entries(node).forEach(([key, value]) => {
18
+ if (key === 'properties' && value !== null && typeof value === 'object') {
19
+ Object.entries(value)
20
+ .filter(([, property]) => property?.format === 'date')
21
+ .forEach(([name]) => keys.add(name));
22
+ }
23
+ dateKeysIn(value, keys);
24
+ });
25
+ }
26
+ return keys;
27
+ };
28
+ /**
29
+ * The keys a date is read under. Only these: a string elsewhere that
30
+ * happens to read like a date, such as the transcription of a dated
31
+ * label, is text and stays text.
32
+ */
33
+ const dateKeys = dateKeysIn(schema);
34
+ /** A value as the edition holds it under the key: a date read, an entity converted, anything else as it stands. */
35
+ const fromJsonLdValue = (key, value) => {
36
+ if (typeof value === 'string')
37
+ return dateKeys.has(key) && isDateString(value) ? importDate(value) : value;
38
+ if (Array.isArray(value))
39
+ return value.map(item => fromJsonLdValue(key, item));
40
+ if (value !== null && typeof value === 'object')
41
+ return fromJsonLdEntity(value);
42
+ return value;
43
+ };
44
+ /** A node without what the export derived from it: what an act changed follows from where the act stands. */
45
+ const asStated = (json) => Object.keys(json).some(key => derivedKeys.has(key))
46
+ ? Object.fromEntries(Object.entries(json).filter(([key]) => !derivedKeys.has(key)))
47
+ : json;
48
+ /**
49
+ * An entity with its keywords read as plain keys. The input is left as
50
+ * it is. The `@type` of a value object names the datatype of the value,
51
+ * not a class, and is dropped. So is a context: a version carries one
52
+ * naming its system's vocabulary, and it belongs to the serialisation
53
+ * rather than to the edition, which states the system as data.
54
+ */
55
+ const fromJsonLdEntity = (json) => {
56
+ const { '@type': type, '@id': id, '@context': context, ...rest } = asStated(json);
57
+ const entity = Object.fromEntries(Object.entries(rest).map(([key, value]) => [key, fromJsonLdValue(key, value)]));
58
+ if (type !== undefined && !('@value' in json))
59
+ entity.type = type;
60
+ if (id !== undefined)
61
+ entity.id = id;
62
+ return entity;
63
+ };
64
+ // Every entity is named by its id alone. Documents written before that was so
65
+ // prefixed a copy's with `copy/`, and are read here as though they had not.
66
+ const withPlainCopyIds = (json) => ({
67
+ ...json,
68
+ copies: (json.copies ?? []).map((copy) => ({
69
+ ...copy,
70
+ '@id': typeof copy['@id'] === 'string' ? plainCopyId(copy['@id']) : copy['@id']
71
+ }))
72
+ });
73
+ /** A statement an export quoted rather than stated: an included node whose id is a triple. */
74
+ const isQuotedStatement = (node) => node !== null && typeof node === 'object' && node['@id'] !== null && typeof node['@id'] === 'object';
75
+ /** The reference a quoted statement made, annotated again with the belief the export set beside it. */
76
+ const referenceOf = (statement) => {
77
+ const { '@id': { '@id': subject, ...made }, annotation, ...about } = statement;
78
+ const [key, value] = Object.entries(made)[0];
79
+ const listed = Array.isArray(value);
80
+ return {
81
+ subject,
82
+ key,
83
+ listed,
84
+ reference: {
85
+ ...(listed ? value[0] : value),
86
+ '@annotation': { ...(annotation !== undefined && { '@id': annotation }), ...about }
87
+ }
88
+ };
89
+ };
90
+ /** Whether the object states anything beside its id, which a reference to a node does not. */
91
+ const isNode = (value) => typeof value['@id'] === 'string' && Object.keys(value).some(key => key !== '@id' && key !== '@annotation');
92
+ /** The document with each quoted reference back on the node that makes it. */
93
+ const withReferencesOn = (value, bySubject) => {
94
+ if (Array.isArray(value))
95
+ return value.map(item => withReferencesOn(item, bySubject));
96
+ if (!value || typeof value !== 'object')
97
+ return value;
98
+ const walked = Object.fromEntries(Object.entries(value).map(([key, child]) => [key, withReferencesOn(child, bySubject)]));
99
+ const references = isNode(value)
100
+ ? bySubject.get(value['@id']) ?? []
101
+ : [];
102
+ return references.reduce((node, { key, listed, reference }) => ({ ...node, [key]: listed ? [...(node[key] ?? []), reference] : reference }), walked);
103
+ };
104
+ /**
105
+ * Puts back what an export quoted. A reference the edition doubts goes
106
+ * out as a JSON-LD-star embedded node beside the document, so that RDF
107
+ * does not state it; in the edition it belongs on the node that makes
108
+ * it, under its belief. In a list it comes back after the references
109
+ * that were stated.
110
+ */
111
+ const withQuotedStatementsInPlace = (edition) => {
112
+ const included = Array.isArray(edition['@included']) ? edition['@included'] : [];
113
+ const statements = included.filter(isQuotedStatement);
114
+ if (statements.length === 0)
115
+ return edition;
116
+ const others = included.filter(node => !isQuotedStatement(node));
117
+ const { '@included': _quoted, ...rest } = edition;
118
+ const bySubject = Map.groupBy(statements.map(referenceOf), ({ subject }) => subject);
119
+ return withReferencesOn({ ...rest, ...(others.length > 0 && { '@included': others }) }, bySubject);
120
+ };
121
+ export const importJsonLd = (json) => {
122
+ const { '@context': context, formatVersion: _revision, ...document } = withPlainCopyIds(migrate(withQuotedStatementsInPlace(json)));
123
+ const edition = fromJsonLdEntity(document);
124
+ edition.base = Array.isArray(context)
125
+ ? context.find((c) => c['@base'])?.['@base'] || ''
126
+ : '';
127
+ return edition;
128
+ };
@@ -13,5 +13,14 @@
13
13
  type Json = any;
14
14
  /** A copy's id without the `copy/` that documents of earlier releases put before it. */
15
15
  export declare const plainCopyId: (id: string) => string;
16
+ /**
17
+ * The revision of the format this release writes, stated in every export
18
+ * as `formatVersion`. A document stating it is in the current shape and
19
+ * passes through untouched; one stating none was written before the
20
+ * revisions were numbered and is brought up by recognising its shapes.
21
+ * A step added later is to run on the documents of the revisions before
22
+ * it, and to raise this number.
23
+ */
24
+ export declare const formatVersion = 1;
16
25
  export declare const migrate: (edition: Json) => Json;
17
26
  export {};
@@ -1,11 +1,24 @@
1
- import { conditions, isFeatureType, media, techniques } from "./Feature.js";
2
- import { rollConditions } from "./RollCopy.js";
3
- import { systemIdIn, systemOf, translationBetween } from "./TrackerBar.js";
4
- import { trackerBars } from "./systems/index.js";
5
- import { welteT100 } from "./systems/welteT100/bar.js";
6
- import { isDateString } from "./utils.js";
1
+ import { systemIdIn, systemOf, translationBetween } from "../systems/TrackerBar.js";
2
+ import { trackerBars } from "../systems/index.js";
3
+ import { welteT100 } from "../systems/welteT100/bar.js";
4
+ import { isDateString } from "../shared/utils.js";
7
5
  const retiredVersionTypes = new Set(['edition', 'unicum']);
8
- const conditionTypeValues = new Set([...rollConditions, ...Object.values(conditions).flat()]);
6
+ /**
7
+ * The vocabulary the steps below were written against, as it stood when
8
+ * the format was first given a revision number. It is copied here rather
9
+ * than imported from the model, so that changing today's vocabulary
10
+ * cannot change how an old document is read. The tracker bars are still
11
+ * asked, being the layout of instruments rather than a choice of the
12
+ * format.
13
+ */
14
+ const conditionTypeValues = new Set([
15
+ 'general', 'paper-stretch',
16
+ 'partially-torn', 'missing-perforation', 'illegible', 'faded', 'detaching', 'ripped'
17
+ ]);
18
+ const techniques = ['print', 'handwriting', 'stamp'];
19
+ const media = ['ink', 'pencil', 'crayon'];
20
+ const featureTypes = ['HoleChain', 'Writing', 'Mark', 'Patch'];
21
+ const isFeatureType = (value) => typeof value === 'string' && featureTypes.includes(value);
9
22
  const renamedKeys = {
10
23
  productionEvent: 'production',
11
24
  annotates: 'depiction',
@@ -128,6 +141,38 @@ const withScale = (node) => {
128
141
  const factor = (node.conditions ?? []).find(isPaperStretch)?.factor;
129
142
  return factor === undefined ? node : { ...node, measurements: { ...node.measurements, scale: factor } };
130
143
  };
144
+ /**
145
+ * What each entry of a copy's former `ops` list stands for among its
146
+ * measurements, and the value that does nothing where the list claimed
147
+ * an act whose amount was never recorded.
148
+ */
149
+ const opsInMeasurements = [
150
+ ['shifted', 'shift', { horizontal: 0, vertical: 0 }],
151
+ ['stretched', 'scale', 1],
152
+ ['shortened', 'readerExtension', { length: 0 }]
153
+ ];
154
+ /**
155
+ * A copy listed what had been done to line its features up, which said
156
+ * again what its measurements state; the measurements now say it alone.
157
+ * Where the two disagreed, the list is what the alignment went by, so
158
+ * the measurements are made to say what it said: a measurement it does
159
+ * not list is dropped, and an act it lists without a recorded amount is
160
+ * recorded as one that moved nothing. `withScale` has read the list by
161
+ * the time this runs.
162
+ */
163
+ const withoutOps = (node) => {
164
+ if (!Array.isArray(node.ops))
165
+ return node;
166
+ const { ops, ...rest } = node;
167
+ const measurements = { ...rest.measurements };
168
+ opsInMeasurements.forEach(([op, key, nothing]) => {
169
+ if (!ops.includes(op))
170
+ delete measurements[key];
171
+ else if (measurements[key] === undefined)
172
+ measurements[key] = nothing;
173
+ });
174
+ return { ...rest, measurements };
175
+ };
131
176
  /** A copy's id without the `copy/` that documents of earlier releases put before it. */
132
177
  export const plainCopyId = (id) => id.replace(/^copy\//, '');
133
178
  /**
@@ -156,7 +201,6 @@ const withPerforator = (node) => {
156
201
  perforator: {
157
202
  ...perforator,
158
203
  condition: {
159
- '@type': 'ConditionState',
160
204
  conditionType: 'setting',
161
205
  ...perforator.condition,
162
206
  punchDiameter: diameter
@@ -196,6 +240,22 @@ const withoutPattern = (node) => {
196
240
  const { pattern: _moved, ...rest } = node;
197
241
  return rest;
198
242
  };
243
+ /** A version and a condition state once stated their class, which the key they stand under entails. */
244
+ const withoutEntailedType = (node) => {
245
+ if (node['@type'] !== 'Version' && node['@type'] !== 'ConditionState')
246
+ return node;
247
+ const { '@type': _entailed, ...rest } = node;
248
+ return rest;
249
+ };
250
+ /** An export once stated what a copy or a patch bears, which a reasoner now derives from the acts. */
251
+ const withoutBearings = (node) => {
252
+ if (!Object.hasOwn(node, 'bears') && !Object.hasOwn(node, 'composedOf'))
253
+ return node;
254
+ if (node['@type'] !== 'RollCopy' && node['@type'] !== 'Patch')
255
+ return node;
256
+ const { bears: _bears, composedOf: _composedOf, ...rest } = node;
257
+ return rest;
258
+ };
199
259
  /** A derivation written as a single one, before a version could name several. */
200
260
  const isSingleDerivation = (basedOn) => basedOn !== null && typeof basedOn === 'object' && !Array.isArray(basedOn);
201
261
  const withDerivationList = (node) => isSingleDerivation(node.basedOn) ? { ...node, basedOn: [node.basedOn] } : node;
@@ -312,8 +372,8 @@ const withTimeSpanDates = (node) => {
312
372
  return { ...rest, within };
313
373
  };
314
374
  const migrateNode = (node) => [withRenamedKeys, withTypology, withRenamedType, withoutVersionType, withoutVersionSiglum, withLowerCaseTerms, withSplitMethod, withReferences, withKeeper, withoutEmptyKeeper,
315
- withPerforator, withProductionNodes, withTypedPerforator, withScale, withDerivationList, withReadingKind, withTimeSpanDates,
316
- withoutFeatureKind, withBorneFeaturesNamed, withFeaturesInActs, withDriveOfStaggering, withoutPattern]
375
+ withPerforator, withProductionNodes, withTypedPerforator, withScale, withoutOps, withDerivationList, withReadingKind, withTimeSpanDates,
376
+ withoutFeatureKind, withBorneFeaturesNamed, withFeaturesInActs, withDriveOfStaggering, withoutPattern, withoutBearings, withoutEntailedType]
317
377
  .reduce((result, step) => step(result), node);
318
378
  /** The items each walked, or the very same list where the walk changed none. */
319
379
  const walked = (items) => {
@@ -390,6 +450,13 @@ const onOwnBar = (copy, rollSystem) => {
390
450
  * roll that named no system was a T-100 roll, and the text a copy's
391
451
  * production gave for it is kept as the system's name.
392
452
  */
453
+ /** An export once stated the class of the edition, which R17i was created by entails. */
454
+ const withoutEditionType = (edition) => {
455
+ if (edition['@type'] !== 'Edition')
456
+ return edition;
457
+ const { '@type': _entailed, ...rest } = edition;
458
+ return rest;
459
+ };
393
460
  const withSystems = (edition) => {
394
461
  if (!edition.roll || !namesSystemOnTheRoll(edition))
395
462
  return edition;
@@ -444,51 +511,27 @@ const withDerivationTolerance = (edition) => {
444
511
  : version)
445
512
  };
446
513
  };
447
- /** A statement an export quoted rather than stated: an included node whose id is a triple. */
448
- const isQuotedStatement = (node) => node !== null && typeof node === 'object' && node['@id'] !== null && typeof node['@id'] === 'object';
449
- /** The reference a quoted statement made, annotated again with the belief the export set beside it. */
450
- const referenceOf = (statement) => {
451
- const { '@id': { '@id': subject, ...made }, annotation, ...about } = statement;
452
- const [key, value] = Object.entries(made)[0];
453
- const listed = Array.isArray(value);
454
- return {
455
- subject,
456
- key,
457
- listed,
458
- reference: {
459
- ...(listed ? value[0] : value),
460
- '@annotation': { ...(annotation !== undefined && { '@id': annotation }), ...about }
461
- }
462
- };
463
- };
464
- /** The document with each quoted reference back on the node that makes it, a node being what has a type. */
465
- const withReferencesOn = (value, bySubject) => {
466
- if (Array.isArray(value))
467
- return value.map(item => withReferencesOn(item, bySubject));
468
- if (!value || typeof value !== 'object')
469
- return value;
470
- const walked = Object.fromEntries(Object.entries(value).map(([key, child]) => [key, withReferencesOn(child, bySubject)]));
471
- const references = typeof value['@id'] === 'string' && value['@type'] !== undefined
472
- ? bySubject.get(value['@id']) ?? []
473
- : [];
474
- return references.reduce((node, { key, listed, reference }) => ({ ...node, [key]: listed ? [...(node[key] ?? []), reference] : reference }), walked);
475
- };
514
+ const editionSteps = [withoutEditionType, withSystems, withEditors, withDerivationTolerance];
476
515
  /**
477
- * Puts back what an export quoted. A reference the edition doubts goes
478
- * out as a JSON-LD-star embedded node beside the document, so that RDF
479
- * does not state it; in the edition it belongs on the node that makes
480
- * it, under its belief. In a list it comes back after the references
481
- * that were stated.
516
+ * The revision of the format this release writes, stated in every export
517
+ * as `formatVersion`. A document stating it is in the current shape and
518
+ * passes through untouched; one stating none was written before the
519
+ * revisions were numbered and is brought up by recognising its shapes.
520
+ * A step added later is to run on the documents of the revisions before
521
+ * it, and to raise this number.
482
522
  */
483
- const withQuotedStatementsInPlace = (edition) => {
484
- const included = Array.isArray(edition['@included']) ? edition['@included'] : [];
485
- const statements = included.filter(isQuotedStatement);
486
- if (statements.length === 0)
523
+ export const formatVersion = 1;
524
+ export const migrate = (edition) => {
525
+ const stated = edition?.formatVersion;
526
+ if (stated === formatVersion)
487
527
  return edition;
488
- const others = included.filter(node => !isQuotedStatement(node));
489
- const { '@included': _quoted, ...rest } = edition;
490
- const bySubject = Map.groupBy(statements.map(referenceOf), ({ subject }) => subject);
491
- return withReferencesOn({ ...rest, ...(others.length > 0 && { '@included': others }) }, bySubject);
528
+ if (typeof stated === 'number' && stated > formatVersion) {
529
+ throw new Error(`The document is written in revision ${stated} of the format, which this release, reading up to ${formatVersion}, does not know`);
530
+ }
531
+ const current = walk(editionSteps.reduce((result, step) => step(result), edition));
532
+ // What comes out is in the current revision and says so, so that it
533
+ // passes through untouched when it is migrated again.
534
+ return current !== null && typeof current === 'object' && !Array.isArray(current)
535
+ ? { ...current, formatVersion }
536
+ : current;
492
537
  };
493
- const editionSteps = [withQuotedStatementsInPlace, withSystems, withEditors, withDerivationTolerance];
494
- export const migrate = (edition) => walk(editionSteps.reduce((result, step) => step(result), edition));
@@ -1,4 +1,4 @@
1
- import { WithId } from "./utils.js";
1
+ import { WithId } from "../shared/utils.js";
2
2
  /**
3
3
  * Something with a name and, where one exists, an authority record.
4
4
  */
@@ -1,4 +1,4 @@
1
- import { WithNote, WithId } from "./utils.js";
1
+ import { WithNote, WithId } from "../shared/utils.js";
2
2
  import { Person, WithActor } from "./Agent.js";
3
3
  export declare const certainties: readonly ['true', 'likely', 'possible', 'unlikely', 'false'];
4
4
  /**
@@ -1,11 +1,10 @@
1
- import { WithType } from "./utils.js";
2
1
  /**
3
2
  * Physical condition of a roll or
4
3
  * of a feature on the roll (e.g. a damaged
5
4
  * or unsuccessful perforation).
6
5
  * @see crm:E3 Condition State
7
6
  */
8
- export interface ConditionState<T extends string> extends WithType<'ConditionState'> {
7
+ export interface ConditionState<T extends string> {
9
8
  /**
10
9
  * The kind of condition, from the list the roll or
11
10
  * the kind of feature allows.
@@ -1,6 +1,6 @@
1
1
  import { Assumption } from "./Assumption.js";
2
2
  import { AnySymbol } from "./Symbol.js";
3
- import { WithId } from "./utils.js";
3
+ import { WithId } from "../shared/utils.js";
4
4
  export declare const editTypes: readonly [
5
5
  /**
6
6
  * An additional accent that can only be encoded with
@@ -1,9 +1,9 @@
1
1
  import { RollCopy } from "./RollCopy.js";
2
2
  import { Version } from "./Version.js";
3
- import { CollationTolerance } from "./Collation.js";
3
+ import { CollationTolerance } from "../collation/Collation.js";
4
4
  import { DateAssignment, ObjectAssumption } from "./Assumption.js";
5
5
  import { Editor, Person, Place } from "./Agent.js";
6
- import { RollTempo } from "./ReproducingSystem.js";
6
+ import { RollTempo } from "../systems/ReproducingSystem.js";
7
7
  /**
8
8
  * This type describes the creation of an edition,
9
9
  * i.e. the editor, publisher, and publication date.
@@ -1,7 +1,7 @@
1
1
  import { ObjectAssumption } from "./Assumption.js";
2
2
  import { ConditionState } from "./ConditionState.js";
3
3
  import { Text } from "./Symbol.js";
4
- import { PartialBy, WithId, WithType } from "./utils.js";
4
+ import { PartialBy, WithId, WithType } from "../shared/utils.js";
5
5
  import { Measure, Millimeters, Track } from "./Quantity.js";
6
6
  /**
7
7
  * Describes the horizontal extent of a feature on the roll,
@@ -238,6 +238,8 @@ type Unplaced<T> = T extends FeatureOrPatch ? PartialBy<T, 'horizontal' | 'verti
238
238
  * the feature bearing it standing in one.
239
239
  */
240
240
  export type NestedFeature = Unplaced<FeatureOrPatch>;
241
+ /** Whether the feature states a place of its own, which one a patch bears does not. */
242
+ export declare const isPlaced: (feature: NestedFeature) => feature is FeatureOrPatch;
241
243
  export declare const isRollFeature: (obj: object) => obj is FeatureOrPatch;
242
244
  export declare const isPatch: <T extends NestedFeature>(feature: T) => feature is T & Patch;
243
245
  /** The features a feature bears: a patch those stated as parts of it, any other feature none. */
@@ -8,6 +8,8 @@ export const conditions = {
8
8
  };
9
9
  export const techniques = ['print', 'handwriting', 'stamp'];
10
10
  export const media = ['ink', 'pencil', 'crayon'];
11
+ /** Whether the feature states a place of its own, which one a patch bears does not. */
12
+ export const isPlaced = (feature) => feature.horizontal !== undefined && feature.vertical !== undefined;
11
13
  export const isRollFeature = (obj) => {
12
14
  return 'type' in obj && isFeatureType(obj.type);
13
15
  };
@@ -1,7 +1,7 @@
1
1
  import { ActorAssignment, DateAssignment, ObjectAssumption } from "./Assumption.js";
2
2
  import { Concept, Named } from "./Agent.js";
3
3
  import { ConditionState } from "./ConditionState.js";
4
- import { WithNote } from "./utils.js";
4
+ import { WithNote } from "../shared/utils.js";
5
5
  /**
6
6
  * What a copy's features were read from. The kinds run from the paper
7
7
  * itself, through files in which the roll has already been read into
@@ -1,7 +1,7 @@
1
1
  import { ObjectAssumption } from "./Assumption.js";
2
2
  import { ConditionState } from "./ConditionState.js";
3
3
  import { Measure, Millimeters } from "./Quantity.js";
4
- import { WithId, WithType } from "./utils.js";
4
+ import { WithId, WithType } from "../shared/utils.js";
5
5
  /**
6
6
  * The pitch in the engineering sense: the distance along the roll from
7
7
  * one punch of a chain to the next, which is the slot one firing cut
@@ -1,10 +1,10 @@
1
1
  import { ConditionState } from "./ConditionState.js";
2
2
  import { AnySymbol } from "./Symbol.js";
3
- import { TrackerBar } from "./TrackerBar.js";
3
+ import { TrackerBar } from "../systems/TrackerBar.js";
4
4
  import { TrackCalibration } from "./TrackCalibration.js";
5
5
  import { AnyFeature, FeatureOrPatch, Patch } from "./Feature.js";
6
6
  import { ActorAssignment, DateAssignment, ObjectAssumption, ReferenceAssumption } from "./Assumption.js";
7
- import { WithId, WithType } from "./utils.js";
7
+ import { WithId, WithType } from "../shared/utils.js";
8
8
  import { Agent, Concept } from "./Agent.js";
9
9
  import { FeatureSource } from "./FeatureSource.js";
10
10
  import { Measure, Millimeters, Quantity, Track } from "./Quantity.js";
@@ -128,7 +128,7 @@ export interface ProductionEvent {
128
128
  * at once and states it here, and a feature an editor reads as the
129
129
  * work of a later hand belongs in the act that made it, whichever
130
130
  * kind of feature it is.
131
- * @see crm:P108 has produced
131
+ * @see reo:produced
132
132
  */
133
133
  produced?: AnyFeature[];
134
134
  }
@@ -173,7 +173,7 @@ export type Modification = Partial<{
173
173
  * paper, a circle in pencil, a hole punched by hand. Any kind of
174
174
  * feature may come of a later act, as any kind may come of the
175
175
  * punching.
176
- * @see crm:P108 has produced
176
+ * @see reo:produced
177
177
  */
178
178
  produced: AnyFeature[];
179
179
  /**
@@ -203,12 +203,6 @@ export type Modification = Partial<{
203
203
  * @see reo:RollCopy
204
204
  */
205
205
  export interface RollCopy extends WithType<'RollCopy'>, WithId {
206
- /**
207
- * A list of operations that have been applied to this copy's features
208
- * (e.g. 'shifted', 'stretched') to normalize measurements
209
- * for comparison with other copies. Not exported to RDF.
210
- */
211
- ops: Array<'shifted' | 'stretched' | 'shortened'>;
212
206
  /**
213
207
  * Physical measurements of this roll copy, including
214
208
  * dimensions, hole separation, margins, shift corrections,
@@ -403,8 +397,7 @@ export declare const featuresOf: (copy: Pick<RollCopy, 'production' | 'modificat
403
397
  export declare const isPaperStretch: (condition: RollConditionAssignment) => boolean;
404
398
  /**
405
399
  * The bar a copy is read by, which is the one it was cut for. A copy
406
- * that names no system falls back to the T-100, as every copy was read
407
- * before the systems were told apart; `reservationsAbout` says so.
400
+ * that names no system falls back to `defaultTrackerBar`.
408
401
  */
409
402
  export declare const barOf: (copy: Pick<RollCopy, 'production'>) => TrackerBar;
410
403
  /** The diameter of the punch the copy was cut with, where the edition holds it true or likely. */
@@ -1,6 +1,5 @@
1
1
  import { v4 } from "uuid";
2
- import { welteT100 } from "./systems/welteT100/bar.js";
3
- import { trackerBarOf } from "./systems/index.js";
2
+ import { defaultTrackerBar, trackerBarOf } from "../systems/index.js";
4
3
  import { assignReference, certaintyOf, isAsserted } from "./Assumption.js";
5
4
  import { px, track } from "./Quantity.js";
6
5
  export const rollConditions = [
@@ -43,10 +42,9 @@ export const featuresOf = (copy) => featuresByAct(copy).flat();
43
42
  export const isPaperStretch = (condition) => condition.conditionType === 'paper-stretch';
44
43
  /**
45
44
  * The bar a copy is read by, which is the one it was cut for. A copy
46
- * that names no system falls back to the T-100, as every copy was read
47
- * before the systems were told apart; `reservationsAbout` says so.
45
+ * that names no system falls back to `defaultTrackerBar`.
48
46
  */
49
- export const barOf = (copy) => trackerBarOf(copy.production?.system) ?? welteT100;
47
+ export const barOf = (copy) => trackerBarOf(copy.production?.system) ?? defaultTrackerBar;
50
48
  /** The diameter of the punch the copy was cut with, where the edition holds it true or likely. */
51
49
  export const punchDiameterOf = (copy) => {
52
50
  const diameter = copy.production?.perforator?.condition?.punchDiameter;
@@ -1,5 +1,5 @@
1
1
  import { ReferenceAssumption } from "./Assumption.js";
2
- import { WithId } from "./utils.js";
2
+ import { WithId } from "../shared/utils.js";
3
3
  /**
4
4
  * A symbol is an abstract musical or textual entity carried by one or more
5
5
  * physical features on the roll. Symbols are the result of interpreting
@@ -1,9 +1,9 @@
1
1
  import { Edit } from "./Edit.js";
2
2
  import { Concept } from "./Agent.js";
3
3
  import { ActorAssignment, Belief, Certainty, DateAssignment, ObjectAssumption, ReferenceAssumption } from "./Assumption.js";
4
- import { CollationTolerance } from "./Collation.js";
4
+ import { CollationTolerance } from "../collation/Collation.js";
5
5
  import { AnySymbol } from "./Symbol.js";
6
- import { WithId, WithNote, WithType } from "./utils.js";
6
+ import { WithId, WithNote, WithType } from "../shared/utils.js";
7
7
  /**
8
8
  * A motivation provides a reason or rationale for an editorial change.
9
9
  * Motivations are defined at the version level and referenced by edits.
@@ -71,7 +71,7 @@ export interface VersionCreation {
71
71
  * it describes.
72
72
  * @see lrmoo:F2 Expression
73
73
  */
74
- export interface Version extends WithId, WithType<'Version'> {
74
+ export interface Version extends WithId {
75
75
  /**
76
76
  * The reproducing system this version is coded for. One roll was
77
77
  * often issued for several of them, and a version is a reading in
@@ -131,3 +131,11 @@ export declare const derivationsOf: (version: Readonly<Version>) => {
131
131
  * below another's reads the version's edits against another parent.
132
132
  */
133
133
  export declare const principalDerivationOf: (version: Readonly<Version>) => Readonly<Derivation> | undefined;
134
+ /**
135
+ * Whether the version derives from the other, directly or through the
136
+ * versions between them, by any derivation it states, a hypothesis
137
+ * among them: a hypothesis held more certain becomes the principal one.
138
+ * A version derives from itself in no statement, and a stemma that
139
+ * loops is walked once round.
140
+ */
141
+ export declare const derivesFrom: (versions: readonly Readonly<Version>[], versionId: string, ancestorId: string) => boolean;
@@ -1,5 +1,5 @@
1
1
  import { certainties, certaintyOf, idOf } from "./Assumption.js";
2
- import { defaultCollationTolerance } from "./Collation.js";
2
+ import { defaultCollationTolerance } from "../collation/Collation.js";
3
3
  /** The tolerance the derivation was collated at, or the default where it states none. */
4
4
  export const collationToleranceOf = (derivation) => derivation.collationTolerance ?? defaultCollationTolerance;
5
5
  /** The edits the version states, none where it leaves its text unstated. */
@@ -26,3 +26,26 @@ const rankOf = (derivation) => certainties.indexOf(certaintyOf(derivation));
26
26
  export const principalDerivationOf = (version) => (version.basedOn ?? [])
27
27
  .filter(derivation => rankOf(derivation) <= certainties.indexOf('possible'))
28
28
  .reduce((principal, derivation) => principal === undefined || rankOf(derivation) < rankOf(principal) ? derivation : principal, undefined);
29
+ /**
30
+ * Whether the version derives from the other, directly or through the
31
+ * versions between them, by any derivation it states, a hypothesis
32
+ * among them: a hypothesis held more certain becomes the principal one.
33
+ * A version derives from itself in no statement, and a stemma that
34
+ * loops is walked once round.
35
+ */
36
+ export const derivesFrom = (versions, versionId, ancestorId) => {
37
+ const byId = new Map(versions.map(version => [version.id, version]));
38
+ const seen = new Set();
39
+ const pending = [versionId];
40
+ while (pending.length > 0) {
41
+ const id = pending.pop();
42
+ if (seen.has(id))
43
+ continue;
44
+ seen.add(id);
45
+ const parents = (byId.get(id)?.basedOn ?? []).map(idOf);
46
+ if (parents.includes(ancestorId))
47
+ return true;
48
+ pending.push(...parents);
49
+ }
50
+ return false;
51
+ };
@@ -1,7 +1,7 @@
1
1
  import { drives } from "./Perforator.js";
2
2
  import { procedures } from "./procedures.js";
3
- import { systemOf } from "./TrackerBar.js";
4
- import { trackerBars } from "./systems/index.js";
3
+ import { systemOf } from "../systems/TrackerBar.js";
4
+ import { trackerBars } from "../systems/index.js";
5
5
  /**
6
6
  * The concepts the type vocabulary declares, whatever kind they are:
7
7
  * the reproducing systems, the procedures and the drives of a
@@ -0,0 +1,9 @@
1
+ import { Path } from "../view/EditionView.js";
2
+ import { AnyArgumentation, Certainty } from "../model/Assumption.js";
3
+ import { EditionOp } from "./draft.js";
4
+ /** Annotates the assumption at the path with a belief held true, for reasons to be added. */
5
+ export declare const createBelief: (path: Path) => EditionOp;
6
+ export declare const clearBelief: (path: Path) => EditionOp;
7
+ export declare const setCertainty: (path: Path, certainty: Certainty) => EditionOp;
8
+ export declare const addReason: (path: Path, reason: AnyArgumentation) => EditionOp;
9
+ export declare const removeReason: (path: Path, index: number) => EditionOp;