linked-rolls 0.56.0 → 0.57.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 CHANGED
@@ -230,15 +230,21 @@ Three kinds of act rather than two, because the CRM tells them apart.
230
230
  E79 Part Addition asks that what is added be "a separate identifiable
231
231
  whole prior to" the act, which a glued-on label is and a pencil line is
232
232
  not: drawing, writing and punching bring a feature into being and are
233
- productions (E12), which state what they made with P108 has produced.
233
+ productions (E12), which state what they made with `reo:produced`, a
234
+ subproperty of P108 has produced.
234
235
 
235
- The export states what that shape only implies. A copy bears every
236
+ The format states nothing a reasoner can derive. That a copy bears every
236
237
  feature its acts brought about (P56 bears feature) and is composed of
237
238
  the patches glued onto it (P46 is composed of, a patch being an object
238
- rather than a feature); an attachment augments the copy and a removal
239
- diminishes it. The four keys are derived on the way out and read off
240
- again on the way in, so they are no part of the edition, and they are
241
- the one place where the JSON tree and the graph differ in shape.
239
+ rather than a feature) follows from property chains in `reo.ttl`, all
240
+ within OWL 2 RL. The chain for the punching runs over `reo:produced`
241
+ rather than P108, because R28 produced is itself a subproperty of P108
242
+ (LRMoo 1.0, p. 44), and a chain over P108 would have the copy bear
243
+ itself. That an attachment augments the copy and a removal diminishes
244
+ it depends on the class of the act, which no chain can test. These two
245
+ keys are derived on the way out and read off again on the way in, so
246
+ they are no part of the edition, and they are the one place where the
247
+ JSON tree and the graph differ in shape.
242
248
 
243
249
  ## Where a copy's features come from
244
250
 
@@ -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,5 +1,5 @@
1
1
  import { Edition } from "./Edition.js";
2
- import { HorizontalSpan, FeatureOrPatch } from "./Feature.js";
2
+ import { FeatureOrPatch, HorizontalSpan, NestedFeature } from "./Feature.js";
3
3
  import { AnySymbol, Expression, Note } from "./Symbol.js";
4
4
  import { Version } from "./Version.js";
5
5
  import { NegotiatedEvent } from "./ReproducingSystem.js";
@@ -10,10 +10,6 @@ export type Path = (string | number)[];
10
10
  export declare const getAt: <T>(path: Path, obj: unknown) => T | undefined;
11
11
  export declare class EditionView {
12
12
  readonly edition: Edition;
13
- /**
14
- * Map from id to object
15
- */
16
- private readonly byId;
17
13
  /**
18
14
  * Map from id to its path within the edition
19
15
  */
@@ -24,17 +20,35 @@ export declare class EditionView {
24
20
  private readonly links;
25
21
  /** Built when a copy is first asked for, the walk not recording it. */
26
22
  private copiesByFeature?;
23
+ /**
24
+ * One index for each kind of entity, built from where the edition
25
+ * holds it. The index an entity is found in says what it is, so a
26
+ * lookup needs neither a type discriminator nor a cast.
27
+ */
28
+ private readonly versionsById;
29
+ private readonly copiesById;
30
+ private readonly symbolsById;
31
+ private featuresById?;
27
32
  constructor(edition: Edition);
28
33
  atPath<T>(path: Path): T | null;
29
34
  indexObjects(): void;
30
- get<T>(anyId: string): T | undefined;
31
- getAll<T>(anyIds: readonly string[]): T[];
35
+ version(id: string): Readonly<Version> | undefined;
36
+ copy(id: string): Readonly<RollCopy> | undefined;
37
+ symbol(id: string): Readonly<AnySymbol> | undefined;
38
+ /** The symbols under the ids, leaving out an id that names none. */
39
+ symbols(ids: readonly string[]): Readonly<AnySymbol>[];
40
+ /** A feature or a patch on any copy, one a patch bears included. */
41
+ feature(id: string): Readonly<NestedFeature> | undefined;
42
+ /** The features and patches under the ids, leaving out an id that names none. */
43
+ features(ids: readonly string[]): Readonly<NestedFeature>[];
32
44
  getPath(anyId: string): Path | undefined;
33
45
  linksTo(anyId: string): Path[];
34
46
  travelUp(versionId: string, callback: (version: Readonly<Version>) => void): void;
35
47
  /** The version and its ancestors along the principal line, from the version up to the root. */
36
48
  lineageOf(versionId: string): Readonly<Version>[];
37
- carriersOf(symbol: AnySymbol): Readonly<FeatureOrPatch>[];
49
+ carriersOf(symbol: AnySymbol): Readonly<NestedFeature>[];
50
+ /** The carriers that state a place of their own, which a feature on a patch does not. */
51
+ placedCarriersOf(symbol: AnySymbol): Readonly<FeatureOrPatch>[];
38
52
  /** The copy a feature sits on, a patch and everything it bears included. */
39
53
  copyOf(featureId: string): Readonly<RollCopy> | undefined;
40
54
  /**
@@ -1,4 +1,4 @@
1
- import { withBorneFeatures } from "./Feature.js";
1
+ import { isPlaced, withBorneFeatures } from "./Feature.js";
2
2
  import { deletedBy, insertedBy, principalDerivationOf } from "./Version.js";
3
3
  import { systemIdOf } from "./TrackerBar.js";
4
4
  import { featuresMadeBy, featuresOf, isPaperStretch } from "./RollCopy.js";
@@ -15,6 +15,7 @@ export const getAt = (path, obj) => {
15
15
  };
16
16
  /** Keys under which an object names others by id, singly or in a list. */
17
17
  const referenceKeys = ['delete', 'comprehends', 'motivation', 'premises', 'used'];
18
+ const byIdIn = (entities) => new Map(entities.map(entity => [entity.id, entity]));
18
19
  const isObject = (v) => v !== null && typeof v === "object";
19
20
  /** What a reference may state besides the id: a belief about it, and the tolerance a derivation was collated at. */
20
21
  const referenceOwnKeys = new Set(['id', '@annotation', 'collationTolerance']);
@@ -34,15 +35,6 @@ export class EditionView {
34
35
  writable: true,
35
36
  value: void 0
36
37
  });
37
- /**
38
- * Map from id to object
39
- */
40
- Object.defineProperty(this, "byId", {
41
- enumerable: true,
42
- configurable: true,
43
- writable: true,
44
- value: new Map()
45
- });
46
38
  /**
47
39
  * Map from id to its path within the edition
48
40
  */
@@ -68,7 +60,39 @@ export class EditionView {
68
60
  writable: true,
69
61
  value: void 0
70
62
  });
63
+ /**
64
+ * One index for each kind of entity, built from where the edition
65
+ * holds it. The index an entity is found in says what it is, so a
66
+ * lookup needs neither a type discriminator nor a cast.
67
+ */
68
+ Object.defineProperty(this, "versionsById", {
69
+ enumerable: true,
70
+ configurable: true,
71
+ writable: true,
72
+ value: void 0
73
+ });
74
+ Object.defineProperty(this, "copiesById", {
75
+ enumerable: true,
76
+ configurable: true,
77
+ writable: true,
78
+ value: void 0
79
+ });
80
+ Object.defineProperty(this, "symbolsById", {
81
+ enumerable: true,
82
+ configurable: true,
83
+ writable: true,
84
+ value: void 0
85
+ });
86
+ Object.defineProperty(this, "featuresById", {
87
+ enumerable: true,
88
+ configurable: true,
89
+ writable: true,
90
+ value: void 0
91
+ });
71
92
  this.edition = edition;
93
+ this.versionsById = byIdIn(edition.versions);
94
+ this.copiesById = byIdIn(edition.copies);
95
+ this.symbolsById = byIdIn(edition.versions.flatMap(insertedBy));
72
96
  this.indexObjects();
73
97
  }
74
98
  atPath(path) {
@@ -89,8 +113,7 @@ export class EditionView {
89
113
  return;
90
114
  if (isReferenceOnly(keys))
91
115
  link(record.id, { key: 'id', up: trail });
92
- else if (!this.byId.has(record.id)) {
93
- this.byId.set(record.id, record);
116
+ else if (!this.paths.has(record.id)) {
94
117
  this.paths.set(record.id, trail);
95
118
  }
96
119
  };
@@ -120,11 +143,27 @@ export class EditionView {
120
143
  };
121
144
  traverse(this.edition, null);
122
145
  }
123
- get(anyId) {
124
- return this.byId.get(anyId);
146
+ version(id) {
147
+ return this.versionsById.get(id);
148
+ }
149
+ copy(id) {
150
+ return this.copiesById.get(id);
125
151
  }
126
- getAll(anyIds) {
127
- return anyIds.map(id => this.get(id)).filter((v) => !!v);
152
+ symbol(id) {
153
+ return this.symbolsById.get(id);
154
+ }
155
+ /** The symbols under the ids, leaving out an id that names none. */
156
+ symbols(ids) {
157
+ return ids.flatMap(id => this.symbol(id) ?? []);
158
+ }
159
+ /** A feature or a patch on any copy, one a patch bears included. */
160
+ feature(id) {
161
+ this.featuresById ?? (this.featuresById = byIdIn(this.edition.copies.flatMap(copy => featuresOf(copy).flatMap(withBorneFeatures))));
162
+ return this.featuresById.get(id);
163
+ }
164
+ /** The features and patches under the ids, leaving out an id that names none. */
165
+ features(ids) {
166
+ return ids.flatMap(id => this.feature(id) ?? []);
128
167
  }
129
168
  getPath(anyId) {
130
169
  const trail = this.paths.get(anyId);
@@ -134,7 +173,7 @@ export class EditionView {
134
173
  return (this.links.get(anyId) ?? []).map(pathOf);
135
174
  }
136
175
  travelUp(versionId, callback) {
137
- const v = this.get(versionId);
176
+ const v = this.version(versionId);
138
177
  if (!v)
139
178
  return;
140
179
  callback(v);
@@ -150,7 +189,11 @@ export class EditionView {
150
189
  return lineage;
151
190
  }
152
191
  carriersOf(symbol) {
153
- return this.getAll(idsOf(symbol.carriers));
192
+ return this.features(idsOf(symbol.carriers));
193
+ }
194
+ /** The carriers that state a place of their own, which a feature on a patch does not. */
195
+ placedCarriersOf(symbol) {
196
+ return this.carriersOf(symbol).filter(isPlaced);
154
197
  }
155
198
  /** The copy a feature sits on, a patch and everything it bears included. */
156
199
  copyOf(featureId) {
@@ -220,9 +263,9 @@ export class EditionView {
220
263
  }
221
264
  /** The version the given one's text is read against, by its principal derivation. */
222
265
  predecessorOf(versionId) {
223
- const v = this.get(versionId);
266
+ const v = this.version(versionId);
224
267
  const principal = v && principalDerivationOf(v);
225
- return principal && this.get(idOf(principal));
268
+ return principal && this.version(idOf(principal));
226
269
  }
227
270
  /**
228
271
  * Where along the roll the symbol lies, as its carriers put it, or
@@ -238,7 +281,7 @@ export class EditionView {
238
281
  * position a semitone away on either bar.
239
282
  */
240
283
  placeOf(symbol) {
241
- const carriers = this.getAll(idsOf(symbol.carriers));
284
+ const carriers = this.placedCarriersOf(symbol);
242
285
  if (carriers.length === 0)
243
286
  return;
244
287
  return {
@@ -282,7 +325,7 @@ export class EditionView {
282
325
  if (inStack.has(id)) {
283
326
  throw new Error(`Cycle detected involving node '${id}'. Check parentId links.`);
284
327
  }
285
- const node = this.get(id);
328
+ const node = this.version(id);
286
329
  if (!node) {
287
330
  memo.set(id, 0);
288
331
  return 0;
@@ -294,7 +337,7 @@ export class EditionView {
294
337
  if (basedOn === undefined) {
295
338
  gen = 0; // root
296
339
  }
297
- else if (!this.get(basedOn)) {
340
+ else if (!this.version(basedOn)) {
298
341
  // Orphaned parent reference — treat boundary as root
299
342
  gen = 0;
300
343
  }
package/lib/Emulation.js CHANGED
@@ -17,10 +17,10 @@ const propertiesOf = (view, version) => ({
17
17
  });
18
18
  /** The onset a symbol has on each copy carrying it, by the copy's index, as the mean of its chains there. */
19
19
  const onsetsByCopy = (view, symbolId) => {
20
- const symbol = view.get(symbolId);
20
+ const symbol = view.symbol(symbolId);
21
21
  if (!symbol)
22
22
  return new Map();
23
- const onsets = view.carriersOf(symbol).flatMap((carrier) => {
23
+ const onsets = view.placedCarriersOf(symbol).flatMap((carrier) => {
24
24
  const copy = view.getPath(carrier.id)?.[1];
25
25
  return typeof copy === 'number' ? [[copy, carrier.horizontal.from]] : [];
26
26
  });
package/lib/Feature.d.ts CHANGED
@@ -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. */
package/lib/Feature.js CHANGED
@@ -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
  };
package/lib/RollCopy.d.ts CHANGED
@@ -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
  /**
package/lib/Version.d.ts CHANGED
@@ -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
package/lib/asJsonLd.js CHANGED
@@ -5,7 +5,7 @@ import context from "./spec/context.json" with { type: 'json' };
5
5
  * The keys an export derives from the tree, which no node of the
6
6
  * edition states itself. An import reads them off again.
7
7
  */
8
- export const derivedKeys = new Set(['bears', 'composedOf', 'augmented', 'diminished']);
8
+ export const derivedKeys = new Set(['augmented', 'diminished']);
9
9
  export const exportDate = (date) => {
10
10
  const year = date.getFullYear();
11
11
  const month = String(date.getMonth() + 1).padStart(2, "0");
@@ -53,32 +53,12 @@ const asJsonLdEntity = (obj) => {
53
53
  * reading as whichever came last. An embedded context merges with the
54
54
  * active one, so the shared prefixes survive.
55
55
  */
56
- const withSystemContexts = (node) => {
57
- if (Array.isArray(node))
58
- return node.map(withSystemContexts);
59
- if (node === null || typeof node !== 'object')
60
- return node;
61
- const walked = Object.fromEntries(Object.entries(node).map(([key, value]) => [key, withSystemContexts(value)]));
62
- const system = node['@type'] === 'Version'
63
- ? systemIdIn(node.system?.['@id'])
64
- : undefined;
65
- return system
66
- ? { '@context': `https://w3id.org/reo/${system}/context.jsonld`, ...walked }
67
- : walked;
56
+ const withSystemContext = (version) => {
57
+ const system = systemIdIn(version?.system?.['@id']);
58
+ return system ? { '@context': `https://w3id.org/reo/${system}/context.jsonld`, ...version } : version;
68
59
  };
60
+ const withSystemContexts = (edition) => Array.isArray(edition.versions) ? { ...edition, versions: edition.versions.map(withSystemContext) } : edition;
69
61
  const isRecord = (value) => value !== null && typeof value === 'object' && !Array.isArray(value);
70
- const nodesIn = (value) => Array.isArray(value) ? value.filter(isRecord) : [];
71
- /** What the acts of a copy brought onto it: the features they produced and the patches they glued on. */
72
- const madeOn = (copy) => [
73
- ...nodesIn(copy.production?.produced),
74
- ...nodesIn(copy.modifications).flatMap(act => [...nodesIn(act.produced), ...nodesIn(act.added)])
75
- ];
76
- const referencing = (nodes, patches) => nodes.filter(node => (node['@type'] === 'Patch') === patches && typeof node['@id'] === 'string')
77
- .map(node => ({ '@id': node['@id'] }));
78
- const featuresAmong = (nodes) => referencing(nodes, false);
79
- const patchesAmong = (nodes) => referencing(nodes, true);
80
- /** The key, where there is anything to state under it. */
81
- const stating = (key, references) => references.length > 0 ? { [key]: references } : {};
82
62
  /** The act with what it changed named: an attachment augments the copy, a removal diminishes it. */
83
63
  const changing = (copyId) => (act) => {
84
64
  if (typeof copyId !== 'string' || !isRecord(act))
@@ -90,31 +70,21 @@ const changing = (copyId) => (act) => {
90
70
  return act;
91
71
  };
92
72
  /**
93
- * The document with every bearing stated that its tree only implies. A
94
- * copy bears the features its acts brought about and is composed of the
95
- * patches they glued on, a patch bears the features glued onto it in
96
- * turn. Each feature stands in the act that made it and nothing reads a
97
- * bearing off that, so the export states it; a patch is no feature, and
98
- * P56 bears feature takes only features, so a patch is stated as a part.
73
+ * The document with what each act changed stated. Which of P110
74
+ * augmented and P112 diminished applies depends on the class of the
75
+ * act, which no property chain in OWL 2 RL can test, so the export
76
+ * states it. What a copy or a patch bears is left to a reasoner (see
77
+ * the entailments in reo.ttl).
99
78
  */
100
- const withBearings = (value) => {
79
+ const withChanges = (value) => {
101
80
  if (Array.isArray(value))
102
- return value.map(withBearings);
81
+ return value.map(withChanges);
103
82
  if (!isRecord(value))
104
83
  return value;
105
- const node = Object.fromEntries(Object.entries(value).map(([key, child]) => [key, withBearings(child)]));
106
- if (node['@type'] === 'RollCopy') {
107
- const made = madeOn(node);
108
- return {
109
- ...node,
110
- ...(Array.isArray(node.modifications) && { modifications: node.modifications.map(changing(node['@id'])) }),
111
- ...stating('bears', featuresAmong(made)),
112
- ...stating('composedOf', patchesAmong(made))
113
- };
114
- }
115
- if (node['@type'] === 'Patch')
116
- return { ...node, ...stating('bears', featuresAmong(nodesIn(node.features))) };
117
- return node;
84
+ const node = Object.fromEntries(Object.entries(value).map(([key, child]) => [key, withChanges(child)]));
85
+ return node['@type'] === 'RollCopy' && Array.isArray(node.modifications)
86
+ ? { ...node, modifications: node.modifications.map(changing(node['@id'])) }
87
+ : node;
118
88
  };
119
89
  /** Terms the context sets to null, which say nothing when the edition is read as RDF. */
120
90
  const silentTerms = new Set(Object.entries(context['@context'])
@@ -182,7 +152,7 @@ const withDoubtedReferencesQuoted = (value) => {
182
152
  };
183
153
  };
184
154
  export const asJsonLd = (edition) => {
185
- const { node, quoted } = withDoubtedReferencesQuoted(withSystemContexts(withBearings(asJsonLdEntity(edition))));
155
+ const { node, quoted } = withDoubtedReferencesQuoted(withSystemContexts(withChanges(asJsonLdEntity(edition))));
186
156
  // The context is the export's own; one carried in from an import must not override it.
187
157
  const { base, '@context': carried, ...rest } = node;
188
158
  return {
@@ -192,7 +162,6 @@ export const asJsonLd = (edition) => {
192
162
  '@base': edition.base
193
163
  }
194
164
  ],
195
- '@type': "Edition",
196
165
  '@id': edition.base,
197
166
  ...rest,
198
167
  ...(quoted.length > 0 && { '@included': quoted })
@@ -86,7 +86,7 @@ const carriersOffTheirMeaning = (view, version, commands) => {
86
86
  .some(meaning => keyOf(meaning) === keyOf(symbol));
87
87
  };
88
88
  return commands
89
- .filter(symbol => view.carriersOf(symbol).some(carrier => misread(carrier, symbol)))
89
+ .filter(symbol => view.placedCarriersOf(symbol).some(carrier => misread(carrier, symbol)))
90
90
  .map(symbol => ({ version, symbol: symbol.id, problem: 'carrier-on-another-track' }));
91
91
  };
92
92
  /**
package/lib/editionOps.js CHANGED
@@ -90,7 +90,6 @@ export const createVersion = (copy) => draft => {
90
90
  const bar = barOf(copy);
91
91
  draft.copies.push(copy);
92
92
  draft.versions.push({
93
- type: 'Version',
94
93
  id: v4(),
95
94
  system: systemOf(bar),
96
95
  edits: asSymbols(featuresOf(copy), bar).map(insertion),
@@ -707,7 +706,7 @@ const byExchange = (edits) => new Map(edits.map(edit => [exchangeKey((edit.inser
707
706
  * child's symbols and not the parent's with themselves.
708
707
  */
709
708
  export const connectVersions = (view, childId, parentId, tolerance = defaultCollationTolerance) => {
710
- const child = view.get(childId);
709
+ const child = view.version(childId);
711
710
  const stated = child ? editsOf(child) : [];
712
711
  const established = stated.filter(edit => !isCollationsOwn(edit));
713
712
  const spokenFor = spokenForBy(established);
@@ -732,7 +731,7 @@ export const connectVersions = (view, childId, parentId, tolerance = defaultColl
732
731
  * apart would make the apparatus a list of unexplained losses beside
733
732
  * a list of unexplained gains.
734
733
  */
735
- const substituted = differ(child, view.get(parentId))
734
+ const substituted = differ(child, view.version(parentId))
736
735
  ? substitutionsBetween(own.filter(symbol => !collated.has(symbol.id)), inherited.filter(symbol => !matched.has(symbol.id)), locate, tolerance)
737
736
  : [];
738
737
  const paired = new Set(substituted.flatMap(({ replaced, by }) => [...by, ...replaced].map(symbol => symbol.id)));
@@ -773,7 +772,7 @@ export const connectVersions = (view, childId, parentId, tolerance = defaultColl
773
772
  * tolerance of the derivation, where the caller names none.
774
773
  */
775
774
  export const collateSymbols = (view, versionId, symbolIds, tolerance) => {
776
- const version = view.get(versionId);
775
+ const version = view.version(versionId);
777
776
  const principal = version && principalDerivationOf(version);
778
777
  if (!version || !principal)
779
778
  return noChange;
@@ -843,7 +842,7 @@ const sharedWith = (view, symbol, copies) => {
843
842
  * narrow the act to those; naming none separates the whole reading.
844
843
  */
845
844
  export const separateReadings = (view, versionId, copies, symbolIds) => {
846
- const version = view.get(versionId);
845
+ const version = view.version(versionId);
847
846
  if (!version)
848
847
  return noChange;
849
848
  const chosen = symbolIds && new Set(symbolIds);
@@ -911,7 +910,6 @@ export const deriveVersion = (versionId, editIds) => onVersion(versionId, (versi
911
910
  if (version.edits)
912
911
  version.edits = without(version.edits, edit => chosen.has(edit.id));
913
912
  draft.versions.push({
914
- type: 'Version',
915
913
  id: v4(),
916
914
  system: stateOf(version).system,
917
915
  basedOn: [assignReference(versionId)],
@@ -970,10 +968,10 @@ const replacementType = (view, inserted, deleted) => {
970
968
  */
971
969
  const guessEditType = (view, versionId, edit) => {
972
970
  const inserts = edit.insert ?? [];
973
- const deletes = view.getAll(edit.delete ?? []);
971
+ const deletes = view.symbols(edit.delete ?? []);
974
972
  const inserted = expressionTypesOf(inserts);
975
973
  const deleted = expressionTypesOf(deletes);
976
- const bar = trackerBarOf(view.get(versionId)?.system);
974
+ const bar = trackerBarOf(view.version(versionId)?.system);
977
975
  const parentBar = trackerBarOf(view.predecessorOf(versionId)?.system);
978
976
  /**
979
977
  * Where the version is coded for another system than its parent, an
@@ -18,12 +18,7 @@ const fromJsonLdValue = (value) => {
18
18
  return fromJsonLdEntity(value);
19
19
  return value;
20
20
  };
21
- /**
22
- * A node without what the export derived from it. A copy bears the
23
- * features its acts brought about, and the graph has to be told so.
24
- * In the edition the act that made a feature is the one place it
25
- * stands.
26
- */
21
+ /** A node without what the export derived from it: what an act changed follows from where the act stands. */
27
22
  const asStated = (json) => Object.keys(json).some(key => derivedKeys.has(key))
28
23
  ? Object.fromEntries(Object.entries(json).filter(([key]) => !derivedKeys.has(key)))
29
24
  : json;
package/lib/migrate.js CHANGED
@@ -156,7 +156,6 @@ const withPerforator = (node) => {
156
156
  perforator: {
157
157
  ...perforator,
158
158
  condition: {
159
- '@type': 'ConditionState',
160
159
  conditionType: 'setting',
161
160
  ...perforator.condition,
162
161
  punchDiameter: diameter
@@ -196,6 +195,22 @@ const withoutPattern = (node) => {
196
195
  const { pattern: _moved, ...rest } = node;
197
196
  return rest;
198
197
  };
198
+ /** A version and a condition state once stated their class, which the key they stand under entails. */
199
+ const withoutEntailedType = (node) => {
200
+ if (node['@type'] !== 'Version' && node['@type'] !== 'ConditionState')
201
+ return node;
202
+ const { '@type': _entailed, ...rest } = node;
203
+ return rest;
204
+ };
205
+ /** An export once stated what a copy or a patch bears, which a reasoner now derives from the acts. */
206
+ const withoutBearings = (node) => {
207
+ if (!Object.hasOwn(node, 'bears') && !Object.hasOwn(node, 'composedOf'))
208
+ return node;
209
+ if (node['@type'] !== 'RollCopy' && node['@type'] !== 'Patch')
210
+ return node;
211
+ const { bears: _bears, composedOf: _composedOf, ...rest } = node;
212
+ return rest;
213
+ };
199
214
  /** A derivation written as a single one, before a version could name several. */
200
215
  const isSingleDerivation = (basedOn) => basedOn !== null && typeof basedOn === 'object' && !Array.isArray(basedOn);
201
216
  const withDerivationList = (node) => isSingleDerivation(node.basedOn) ? { ...node, basedOn: [node.basedOn] } : node;
@@ -313,7 +328,7 @@ const withTimeSpanDates = (node) => {
313
328
  };
314
329
  const migrateNode = (node) => [withRenamedKeys, withTypology, withRenamedType, withoutVersionType, withoutVersionSiglum, withLowerCaseTerms, withSplitMethod, withReferences, withKeeper, withoutEmptyKeeper,
315
330
  withPerforator, withProductionNodes, withTypedPerforator, withScale, withDerivationList, withReadingKind, withTimeSpanDates,
316
- withoutFeatureKind, withBorneFeaturesNamed, withFeaturesInActs, withDriveOfStaggering, withoutPattern]
331
+ withoutFeatureKind, withBorneFeaturesNamed, withFeaturesInActs, withDriveOfStaggering, withoutPattern, withoutBearings, withoutEntailedType]
317
332
  .reduce((result, step) => step(result), node);
318
333
  /** The items each walked, or the very same list where the walk changed none. */
319
334
  const walked = (items) => {
@@ -390,6 +405,13 @@ const onOwnBar = (copy, rollSystem) => {
390
405
  * roll that named no system was a T-100 roll, and the text a copy's
391
406
  * production gave for it is kept as the system's name.
392
407
  */
408
+ /** An export once stated the class of the edition, which R17i was created by entails. */
409
+ const withoutEditionType = (edition) => {
410
+ if (edition['@type'] !== 'Edition')
411
+ return edition;
412
+ const { '@type': _entailed, ...rest } = edition;
413
+ return rest;
414
+ };
393
415
  const withSystems = (edition) => {
394
416
  if (!edition.roll || !namesSystemOnTheRoll(edition))
395
417
  return edition;
@@ -461,14 +483,16 @@ const referenceOf = (statement) => {
461
483
  }
462
484
  };
463
485
  };
464
- /** The document with each quoted reference back on the node that makes it, a node being what has a type. */
486
+ /** Whether the object states anything beside its id, which a reference to a node does not. */
487
+ const isNode = (value) => typeof value['@id'] === 'string' && Object.keys(value).some(key => key !== '@id' && key !== '@annotation');
488
+ /** The document with each quoted reference back on the node that makes it. */
465
489
  const withReferencesOn = (value, bySubject) => {
466
490
  if (Array.isArray(value))
467
491
  return value.map(item => withReferencesOn(item, bySubject));
468
492
  if (!value || typeof value !== 'object')
469
493
  return value;
470
494
  const walked = Object.fromEntries(Object.entries(value).map(([key, child]) => [key, withReferencesOn(child, bySubject)]));
471
- const references = typeof value['@id'] === 'string' && value['@type'] !== undefined
495
+ const references = isNode(value)
472
496
  ? bySubject.get(value['@id']) ?? []
473
497
  : [];
474
498
  return references.reduce((node, { key, listed, reference }) => ({ ...node, [key]: listed ? [...(node[key] ?? []), reference] : reference }), walked);
@@ -490,5 +514,5 @@ const withQuotedStatementsInPlace = (edition) => {
490
514
  const bySubject = Map.groupBy(statements.map(referenceOf), ({ subject }) => subject);
491
515
  return withReferencesOn({ ...rest, ...(others.length > 0 && { '@included': others }) }, bySubject);
492
516
  };
493
- const editionSteps = [withQuotedStatementsInPlace, withSystems, withEditors, withDerivationTolerance];
517
+ const editionSteps = [withoutEditionType, withQuotedStatementsInPlace, withSystems, withEditors, withDerivationTolerance];
494
518
  export const migrate = (edition) => walk(editionSteps.reduce((result, step) => step(result), edition));
@@ -164,7 +164,6 @@ export function readFromStanfordAton(atonString, { trackShift, system = welteT10
164
164
  type: 'Perforator',
165
165
  id: `perforator_${v4()}`,
166
166
  condition: {
167
- type: 'ConditionState',
168
167
  conditionType: 'setting',
169
168
  punchDiameter: { value: punchDiameter, unit: 'mm' }
170
169
  }
package/lib/scatter.js CHANGED
@@ -37,7 +37,7 @@ const sitsOn = (view, copies) => (feature) => {
37
37
  export const readingsOf = (view, symbols, copies) => {
38
38
  const tested = sitsOn(view, copies);
39
39
  return symbols.flatMap((symbol) => {
40
- const carriers = view.carriersOf(symbol);
40
+ const carriers = view.placedCarriersOf(symbol);
41
41
  const here = carriers.filter(tested);
42
42
  const there = carriers.filter(carrier => !tested(carrier));
43
43
  if (here.length === 0 || there.length === 0)
@@ -220,12 +220,12 @@ const copiesBearing = (view, symbols) => {
220
220
  * stopped saying and an editor has to.
221
221
  */
222
222
  export const sidesOf = (view, versionId) => {
223
- const version = view.get(versionId);
223
+ const version = view.version(versionId);
224
224
  if (!version)
225
225
  return undefined;
226
226
  return {
227
227
  child: copiesBearing(view, insertedBy(version)),
228
- parent: copiesBearing(view, view.getAll(deletedBy(version)))
228
+ parent: copiesBearing(view, view.symbols(deletedBy(version)))
229
229
  };
230
230
  };
231
231
  const inMillimetres = (value) => `${value.toFixed(2)} mm`;
package/lib/schema.json CHANGED
@@ -1221,7 +1221,7 @@
1221
1221
  "$ref": "#/definitions/AnyFeature"
1222
1222
  },
1223
1223
  "type": "array",
1224
- "ontology": "crm:P108 has produced"
1224
+ "ontology": "reo:produced"
1225
1225
  },
1226
1226
  "purpose": {
1227
1227
  "$ref": "#/definitions/ModificationPurpose",
@@ -1566,22 +1566,6 @@
1566
1566
  "description": "The type discriminator for this object.",
1567
1567
  "type": "string",
1568
1568
  "ontology": "rdf:type"
1569
- },
1570
- "bears": {
1571
- "type": "array",
1572
- "items": {
1573
- "type": "object",
1574
- "properties": {
1575
- "@id": {
1576
- "type": "string"
1577
- }
1578
- },
1579
- "required": [
1580
- "@id"
1581
- ]
1582
- },
1583
- "description": "The features the patch bears. Derived on export.",
1584
- "ontology": "crm:P56 bears feature"
1585
1569
  }
1586
1570
  },
1587
1571
  "required": [
@@ -1682,17 +1666,10 @@
1682
1666
  "description": "A free-text description of the condition, providing details beyond the type classification.",
1683
1667
  "type": "string",
1684
1668
  "ontology": "crm:P3 has note"
1685
- },
1686
- "@type": {
1687
- "const": "ConditionState",
1688
- "description": "The type discriminator for this object.",
1689
- "type": "string",
1690
- "ontology": "rdf:type"
1691
1669
  }
1692
1670
  },
1693
1671
  "required": [
1694
- "conditionType",
1695
- "@type"
1672
+ "conditionType"
1696
1673
  ],
1697
1674
  "type": "object"
1698
1675
  },
@@ -1732,18 +1709,11 @@
1732
1709
  "description": "The stretch factor, e.g. 1.02 means the paper has stretched by 2% compared to its original dimensions.",
1733
1710
  "type": "number",
1734
1711
  "ontology": "rdf:value"
1735
- },
1736
- "@type": {
1737
- "const": "ConditionState",
1738
- "description": "The type discriminator for this object.",
1739
- "type": "string",
1740
- "ontology": "rdf:type"
1741
1712
  }
1742
1713
  },
1743
1714
  "required": [
1744
1715
  "conditionType",
1745
- "factor",
1746
- "@type"
1716
+ "factor"
1747
1717
  ],
1748
1718
  "type": "object"
1749
1719
  }
@@ -1986,17 +1956,10 @@
1986
1956
  "description": "A free-text description of the condition, providing details beyond the type classification.",
1987
1957
  "type": "string",
1988
1958
  "ontology": "crm:P3 has note"
1989
- },
1990
- "@type": {
1991
- "const": "ConditionState",
1992
- "description": "The type discriminator for this object.",
1993
- "type": "string",
1994
- "ontology": "rdf:type"
1995
1959
  }
1996
1960
  },
1997
1961
  "required": [
1998
- "conditionType",
1999
- "@type"
1962
+ "conditionType"
2000
1963
  ],
2001
1964
  "type": "object"
2002
1965
  },
@@ -2035,17 +1998,10 @@
2035
1998
  "description": "A free-text description of the condition, providing details beyond the type classification.",
2036
1999
  "type": "string",
2037
2000
  "ontology": "crm:P3 has note"
2038
- },
2039
- "@type": {
2040
- "const": "ConditionState",
2041
- "description": "The type discriminator for this object.",
2042
- "type": "string",
2043
- "ontology": "rdf:type"
2044
2001
  }
2045
2002
  },
2046
2003
  "required": [
2047
- "conditionType",
2048
- "@type"
2004
+ "conditionType"
2049
2005
  ],
2050
2006
  "type": "object"
2051
2007
  },
@@ -2081,17 +2037,10 @@
2081
2037
  "description": "A free-text description of the condition, providing details beyond the type classification.",
2082
2038
  "type": "string",
2083
2039
  "ontology": "crm:P3 has note"
2084
- },
2085
- "@type": {
2086
- "const": "ConditionState",
2087
- "description": "The type discriminator for this object.",
2088
- "type": "string",
2089
- "ontology": "rdf:type"
2090
2040
  }
2091
2041
  },
2092
2042
  "required": [
2093
- "conditionType",
2094
- "@type"
2043
+ "conditionType"
2095
2044
  ],
2096
2045
  "type": "object"
2097
2046
  },
@@ -2127,17 +2076,10 @@
2127
2076
  "description": "A free-text description of the condition, providing details beyond the type classification.",
2128
2077
  "type": "string",
2129
2078
  "ontology": "crm:P3 has note"
2130
- },
2131
- "@type": {
2132
- "const": "ConditionState",
2133
- "description": "The type discriminator for this object.",
2134
- "type": "string",
2135
- "ontology": "rdf:type"
2136
2079
  }
2137
2080
  },
2138
2081
  "required": [
2139
- "conditionType",
2140
- "@type"
2082
+ "conditionType"
2141
2083
  ],
2142
2084
  "type": "object"
2143
2085
  },
@@ -2176,17 +2118,10 @@
2176
2118
  "description": "A free-text description of the condition, providing details beyond the type classification.",
2177
2119
  "type": "string",
2178
2120
  "ontology": "crm:P3 has note"
2179
- },
2180
- "@type": {
2181
- "const": "ConditionState",
2182
- "description": "The type discriminator for this object.",
2183
- "type": "string",
2184
- "ontology": "rdf:type"
2185
2121
  }
2186
2122
  },
2187
2123
  "required": [
2188
- "conditionType",
2189
- "@type"
2124
+ "conditionType"
2190
2125
  ],
2191
2126
  "type": "object"
2192
2127
  },
@@ -2650,22 +2585,6 @@
2650
2585
  "description": "The type discriminator for this object.",
2651
2586
  "type": "string",
2652
2587
  "ontology": "rdf:type"
2653
- },
2654
- "bears": {
2655
- "type": "array",
2656
- "items": {
2657
- "type": "object",
2658
- "properties": {
2659
- "@id": {
2660
- "type": "string"
2661
- }
2662
- },
2663
- "required": [
2664
- "@id"
2665
- ]
2666
- },
2667
- "description": "The features the patch bears. Derived on export.",
2668
- "ontology": "crm:P56 bears feature"
2669
2588
  }
2670
2589
  },
2671
2590
  "required": [
@@ -2734,17 +2653,10 @@
2734
2653
  "$ref": "#/definitions/ObjectAssumption%3CMeasure%3C%22mm%22%3E%3E",
2735
2654
  "description": "The diameter of the round punch. Taken across the roll, it is the one value of the setting that neither the scale along the roll nor the shrinkage of the paper enters.",
2736
2655
  "ontology": "reo:punchDiameter"
2737
- },
2738
- "@type": {
2739
- "const": "ConditionState",
2740
- "description": "The type discriminator for this object.",
2741
- "type": "string",
2742
- "ontology": "rdf:type"
2743
2656
  }
2744
2657
  },
2745
2658
  "required": [
2746
- "conditionType",
2747
- "@type"
2659
+ "conditionType"
2748
2660
  ],
2749
2661
  "type": "object",
2750
2662
  "ontology": "crm:E3 Condition State"
@@ -2813,7 +2725,7 @@
2813
2725
  "$ref": "#/definitions/AnyFeature"
2814
2726
  },
2815
2727
  "type": "array",
2816
- "ontology": "crm:P108 has produced"
2728
+ "ontology": "reo:produced"
2817
2729
  },
2818
2730
  "speed": {
2819
2731
  "$ref": "#/definitions/ObjectAssumption%3CPaperSpeed%3E",
@@ -3152,38 +3064,6 @@
3152
3064
  "description": "The type discriminator for this object.",
3153
3065
  "type": "string",
3154
3066
  "ontology": "rdf:type"
3155
- },
3156
- "bears": {
3157
- "type": "array",
3158
- "items": {
3159
- "type": "object",
3160
- "properties": {
3161
- "@id": {
3162
- "type": "string"
3163
- }
3164
- },
3165
- "required": [
3166
- "@id"
3167
- ]
3168
- },
3169
- "description": "The features the copy bears, whichever of its acts brought them about. Derived on export.",
3170
- "ontology": "crm:P56 bears feature"
3171
- },
3172
- "composedOf": {
3173
- "type": "array",
3174
- "items": {
3175
- "type": "object",
3176
- "properties": {
3177
- "@id": {
3178
- "type": "string"
3179
- }
3180
- },
3181
- "required": [
3182
- "@id"
3183
- ]
3184
- },
3185
- "description": "The patches glued onto the copy. A patch is an object rather than a feature, so it is stated as a part. Derived on export.",
3186
- "ontology": "crm:P46 is composed of"
3187
3067
  }
3188
3068
  },
3189
3069
  "required": [
@@ -3361,19 +3241,12 @@
3361
3241
  "@id": {
3362
3242
  "description": "A unique identifier for this object.",
3363
3243
  "type": "string"
3364
- },
3365
- "@type": {
3366
- "const": "Version",
3367
- "description": "The type discriminator for this object.",
3368
- "type": "string",
3369
- "ontology": "rdf:type"
3370
3244
  }
3371
3245
  },
3372
3246
  "required": [
3373
3247
  "@id",
3374
3248
  "motivations",
3375
- "system",
3376
- "@type"
3249
+ "system"
3377
3250
  ],
3378
3251
  "type": "object",
3379
3252
  "ontology": "lrmoo:F2 Expression"
@@ -11,8 +11,6 @@
11
11
  "crminf": "http://www.cidoc-crm.org/extensions/crminf/",
12
12
  "lrmoo": "http://iflastandards.info/ns/lrm/lrmoo/",
13
13
  "reo": "https://w3id.org/reo/",
14
- "Edition": "lrmoo:F2_Expression",
15
- "Version": "lrmoo:F2_Expression",
16
14
  "RollCopy": "reo:RollCopy",
17
15
  "HoleChain": "reo:HoleChain",
18
16
  "Mark": "reo:Mark",
@@ -22,7 +20,6 @@
22
20
  "Removal": "crm:E80_Part_Removal",
23
21
  "Alteration": "crm:E12_Production",
24
22
  "Perforator": "crm:E22_Human-Made_Object",
25
- "ConditionState": "crm:E3_Condition_State",
26
23
  "note": "reo:Note",
27
24
  "expression": "reo:Expression",
28
25
  "text": {
@@ -280,7 +277,7 @@
280
277
  }
281
278
  },
282
279
  "actor": "crm:P14_carried_out_by",
283
- "produced": "crm:P108_has_produced",
280
+ "produced": "reo:produced",
284
281
  "added": "crm:P111_added",
285
282
  "removed": {
286
283
  "@id": "crm:P113_removed",
@@ -291,14 +288,6 @@
291
288
  "@type": "@vocab",
292
289
  "@context": { "@vocab": "https://w3id.org/reo/type/" }
293
290
  },
294
- "bears": {
295
- "@id": "crm:P56_bears_feature",
296
- "@type": "@id"
297
- },
298
- "composedOf": {
299
- "@id": "crm:P46_is_composed_of",
300
- "@type": "@id"
301
- },
302
291
  "augmented": {
303
292
  "@id": "crm:P110_augmented",
304
293
  "@type": "@id"
package/lib/witnesses.js CHANGED
@@ -70,7 +70,7 @@ const witnessesIn = (carriers, view, versionId) => {
70
70
  * reported as it stands. A version whose edits are not stated inserts
71
71
  * nothing and is witnessed by statement alone.
72
72
  */
73
- export const witnessesOf = (view, versionId) => view.get(versionId) ? witnessesIn(carriersIn(view), view, versionId) : [];
73
+ export const witnessesOf = (view, versionId) => view.version(versionId) ? witnessesIn(carriersIn(view), view, versionId) : [];
74
74
  /**
75
75
  * The versions the copy bears witness to, each with how, in the order the
76
76
  * edition lists its versions.
@@ -92,7 +92,7 @@ export const carriageProblems = (view) => {
92
92
  const version = idOf(statement);
93
93
  return [
94
94
  ...(carrying.has(copy.id) ? [{ copy: copy.id, version, problem: 'stated-beside-carriers' }] : []),
95
- ...(view.get(version)?.type === 'Version' ? [] : [{ copy: copy.id, version, problem: 'version-missing' }])
95
+ ...(view.version(version) ? [] : [{ copy: copy.id, version, problem: 'version-missing' }])
96
96
  ];
97
97
  }));
98
98
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linked-rolls",
3
- "version": "0.56.0",
3
+ "version": "0.57.0",
4
4
  "description": "Digital editions of piano rolls: import, collation, editorial assumptions, JSON-LD export, and emulation through a reproducing system",
5
5
  "license": "MIT",
6
6
  "repository": {