linked-rolls 0.29.0 → 0.31.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
@@ -40,8 +40,24 @@ Files written by linked-rolls 0.1 load unchanged: `importJsonLd`
40
40
  recognises their shapes and brings them to the current format, in
41
41
  which versions and conditions carry a typology key beside their type,
42
42
  the keeper and the production metadata are nodes with a name and
43
- authority links, and the roll names its reproducing system. Exports
44
- are always in the current format.
43
+ authority links, and the roll names its reproducing system. A date
44
+ written as a value of its own is read as the day the event falls
45
+ within. Exports are always in the current format.
46
+
47
+ ## How a date is stated
48
+
49
+ A date is the time-span of the event rather than a value on it.
50
+ `within` gives the day it falls within, and `after` and `before` give
51
+ the bounds where nobody can give the day. A bound the edition does not
52
+ know is left out, so `after` alone says "not before". In RDF the three
53
+ become `P82 at some time within`, `P82a begin of the begin` and `P82b
54
+ end of the end` on the `E52 Time-Span` the event has. `assignDate`,
55
+ `notBefore` and `notAfter` build the three shapes, and `dateOf`,
56
+ `earliestOf` and `latestOf` read them.
57
+
58
+ Note that `before` and `after` mean something else on a perforation,
59
+ where they order two onsets. The context defines them for a date
60
+ within the date itself, so the two never meet.
45
61
 
46
62
  ## What a version derives from
47
63
 
@@ -144,16 +144,20 @@ export type ObjectAssumption<O extends object> = Assumption & O;
144
144
  */
145
145
  export type ActorAssignment = ObjectAssumption<Person>;
146
146
  /**
147
- * A date value wrapped as an assumption, so that the date
148
- * can be annotated with a belief about its certainty and source.
147
+ * When something happened, as far as the edition can state it: the day
148
+ * it falls `within`, or the bounds it lies between. A bound nobody can
149
+ * give is left out, so `after` alone says "not before".
150
+ * @see crm:E52 Time-Span
149
151
  */
150
- export type DateAssignment = ValueAssumption<Date> & {
151
- /**
152
- * The datatype of the value. Written on export so that
153
- * RDF reads the value as a date rather than a string.
154
- */
155
- '@type'?: 'xsd:date';
156
- };
152
+ export type DateAssignment = Assumption & ({
153
+ within: Date;
154
+ } | {
155
+ after: Date;
156
+ before?: Date;
157
+ } | {
158
+ before: Date;
159
+ after?: Date;
160
+ });
157
161
  export declare function valueOf<ValueT>(assumption: ValueAssumption<ValueT>): ValueT;
158
162
  export declare function valuesOf<ValueT>(assumptions: ValueAssumption<ValueT>[]): ValueT[];
159
163
  export declare function idOf(assumption: ReferenceAssumption): string;
@@ -161,3 +165,15 @@ export declare function idsOf(assumptions: ReferenceAssumption[]): string[];
161
165
  export declare function assignValue<ValueT>(value: ValueT): ValueAssumption<ValueT>;
162
166
  export declare function assignReference(id: string): ReferenceAssumption;
163
167
  export declare function assignObject<O extends object>(obj: O): ObjectAssumption<O>;
168
+ /** A date the edition states: the day the event falls within. */
169
+ export declare const assignDate: (within: Date) => DateAssignment;
170
+ /** A date the edition can only bound from below, as in "not before 1924". */
171
+ export declare const notBefore: (after: Date) => DateAssignment;
172
+ /** A date the edition can only bound from above. */
173
+ export declare const notAfter: (before: Date) => DateAssignment;
174
+ /** The day the event falls within, where the edition states one. */
175
+ export declare const dateOf: (assignment: DateAssignment) => Date | undefined;
176
+ /** The earliest the event can have happened, as far as the edition states it. */
177
+ export declare const earliestOf: (assignment: DateAssignment) => Date | undefined;
178
+ /** The latest the event can have happened, as far as the edition states it. */
179
+ export declare const latestOf: (assignment: DateAssignment) => Date | undefined;
package/lib/Assumption.js CHANGED
@@ -41,3 +41,15 @@ export function assignObject(obj) {
41
41
  ...obj
42
42
  };
43
43
  }
44
+ /** A date the edition states: the day the event falls within. */
45
+ export const assignDate = (within) => ({ within });
46
+ /** A date the edition can only bound from below, as in "not before 1924". */
47
+ export const notBefore = (after) => ({ after });
48
+ /** A date the edition can only bound from above. */
49
+ export const notAfter = (before) => ({ before });
50
+ /** The day the event falls within, where the edition states one. */
51
+ export const dateOf = (assignment) => 'within' in assignment ? assignment.within : undefined;
52
+ /** The earliest the event can have happened, as far as the edition states it. */
53
+ export const earliestOf = (assignment) => 'within' in assignment ? assignment.within : assignment.after;
54
+ /** The latest the event can have happened, as far as the edition states it. */
55
+ export const latestOf = (assignment) => 'within' in assignment ? assignment.within : assignment.before;
package/lib/Edition.d.ts CHANGED
@@ -68,7 +68,7 @@ export interface RecordingEvent {
68
68
  * The recording date of the roll. This is a date
69
69
  * assignment so that we can state e.g. the catalogue
70
70
  * or the roll label which indicates the date of the recording.
71
- * @see dcterms:date
71
+ * @see crm:P4 has time-span
72
72
  */
73
73
  date: DateAssignment;
74
74
  /**
@@ -98,8 +98,7 @@ export interface FeatureSource extends WithNote {
98
98
  instrument?: ObjectAssumption<Instrument>;
99
99
  /**
100
100
  * When the capture took place.
101
- * @format date
102
- * @see dcterms:date
101
+ * @see crm:P4 has time-span
103
102
  */
104
103
  date?: DateAssignment;
105
104
  }
package/lib/RollCopy.d.ts CHANGED
@@ -90,7 +90,7 @@ export interface ProductionEvent {
90
90
  paper?: Concept;
91
91
  /**
92
92
  * The date of production, if known.
93
- * @see dcterms:date
93
+ * @see crm:P4 has time-span
94
94
  */
95
95
  date?: DateAssignment;
96
96
  /**
@@ -124,7 +124,7 @@ export type Modification = Partial<{
124
124
  actor: ActorAssignment;
125
125
  /**
126
126
  * When the modification took place.
127
- * @see dcterms:date
127
+ * @see crm:P4 has time-span
128
128
  */
129
129
  date: DateAssignment;
130
130
  }> & ({
@@ -274,8 +274,10 @@ export interface RollCopy extends WithType<'RollCopy'>, WithId {
274
274
  */
275
275
  conditions: RollConditionAssignment[];
276
276
  /**
277
- * A short siglum to identify the copy, e.g. "W1" or "S2". A copy
278
- * without one is named by its keeper.
277
+ * A short siglum to identify the copy, e.g. "St1" or "Wi1": letters
278
+ * for the collection the copy was read in, and a number counting the
279
+ * copies of the roll held there. A copy without one is named by its
280
+ * keeper.
279
281
  * @see reo:siglum
280
282
  */
281
283
  siglum?: string;
package/lib/Version.d.ts CHANGED
@@ -47,7 +47,7 @@ export interface VersionCreation {
47
47
  actor?: ActorAssignment;
48
48
  /**
49
49
  * When it took place.
50
- * @see dcterms:date
50
+ * @see crm:P4 has time-span
51
51
  */
52
52
  date?: DateAssignment;
53
53
  /**
@@ -66,7 +66,11 @@ export interface VersionCreation {
66
66
  */
67
67
  export interface Version extends WithId, WithType<'Version'> {
68
68
  /**
69
- * A short siglum to identify the version, e.g. "A", "B1", "B2_rev", etc.
69
+ * A short siglum to identify the version, e.g. "A" or "B2". A siglum
70
+ * is given once and not changed: its letter names the line the version
71
+ * was first recognised in, its number counts the versions admitted to
72
+ * that line. Where later work places the version elsewhere in the
73
+ * stemma, `basedOn` changes and the siglum stays.
70
74
  * @see reo:siglum
71
75
  */
72
76
  siglum: string;
package/lib/asJsonLd.js CHANGED
@@ -35,9 +35,6 @@ const asJsonLdEntity = (obj) => {
35
35
  result[key] = value;
36
36
  }
37
37
  }
38
- if ('@value' in obj && obj['@value'] instanceof Date) {
39
- result['@type'] = 'xsd:date';
40
- }
41
38
  return result;
42
39
  };
43
40
  /**
package/lib/editionOps.js CHANGED
@@ -637,6 +637,19 @@ export const removeVersion = (view, versionId) => {
637
637
  };
638
638
  /** Takes the symbols out of the version's own insertions, and the edits that had nothing else. */
639
639
  export const removeSymbols = (versionId, symbolIds) => onVersion(versionId, version => dropInsertions(version, new Set(symbolIds)));
640
+ /**
641
+ * The siglum for a version derived from this one: the line the siglum
642
+ * names, and the next number free in it. A number already given stays
643
+ * given, whatever the stemma does later.
644
+ */
645
+ const nextInLine = (versions, siglum) => {
646
+ const [line] = siglum.match(/^\D*/) ?? [siglum];
647
+ const numbers = versions.flatMap(other => {
648
+ const match = other.siglum.match(/^(\D*)(\d*)$/);
649
+ return match && match[1] === line ? [Number(match[2] || 1)] : [];
650
+ });
651
+ return `${line}${Math.max(1, ...numbers) + 1}`;
652
+ };
640
653
  /** Moves the edits into a new version based on this one. */
641
654
  export const deriveVersion = (versionId, editIds) => onVersion(versionId, (version, draft) => {
642
655
  const chosen = new Set(editIds);
@@ -646,7 +659,7 @@ export const deriveVersion = (versionId, editIds) => onVersion(versionId, (versi
646
659
  draft.versions.push({
647
660
  type: 'Version',
648
661
  id: v4(),
649
- siglum: `${version.siglum}_derived`,
662
+ siglum: nextInLine(draft.versions, version.siglum),
650
663
  system: stateOf(version).system,
651
664
  basedOn: [assignReference(versionId)],
652
665
  edits: moved,
@@ -1,8 +1,5 @@
1
1
  import { migrate } from "./migrate";
2
- const isDate = (value) => {
3
- const datePattern = /^\d{4}-\d{1,2}-\d{1,2}$/;
4
- return datePattern.test(value);
5
- };
2
+ import { isDateString } from "./utils";
6
3
  export const importDate = (str) => {
7
4
  const [y, m, d] = str.split('-').map(s => parseInt(s, 10));
8
5
  if ([y, m, d].some(n => isNaN(n))) {
@@ -13,7 +10,7 @@ export const importDate = (str) => {
13
10
  /** A value as the edition holds it: a date read, an entity converted, anything else as it stands. */
14
11
  const fromJsonLdValue = (value) => {
15
12
  if (typeof value === 'string')
16
- return isDate(value) ? importDate(value) : value;
13
+ return isDateString(value) ? importDate(value) : value;
17
14
  if (Array.isArray(value))
18
15
  return value.map(fromJsonLdValue);
19
16
  if (value !== null && typeof value === 'object')
package/lib/migrate.js CHANGED
@@ -3,6 +3,7 @@ import { rollConditions } from "./RollCopy";
3
3
  import { systemIdIn, systemOf, translationBetween } from "./TrackerBar";
4
4
  import { trackerBars } from "./systems";
5
5
  import { welteT100 } from "./systems/welteT100/bar";
6
+ import { isDateString } from "./utils";
6
7
  const retiredVersionTypes = new Set(['edition', 'unicum']);
7
8
  const conditionTypeValues = new Set([...rollConditions, ...Object.values(conditions).flat()]);
8
9
  const renamedKeys = {
@@ -94,8 +95,19 @@ const withoutEmptyKeeper = (node) => {
94
95
  const { keeper: _unnamed, ...rest } = node;
95
96
  return rest;
96
97
  };
98
+ /**
99
+ * A date was a value of its own before it was the time-span the event
100
+ * falls within. The datatype goes with it: the context now types each
101
+ * bound, and a `@type` left on the node would read as a class.
102
+ */
103
+ const withTimeSpanDates = (node) => {
104
+ if (!isDateString(node['@value']))
105
+ return node;
106
+ const { '@value': within, '@type': _typed, ...rest } = node;
107
+ return { ...rest, within };
108
+ };
97
109
  const migrateNode = (node) => [withRenamedKeys, withTypology, withoutVersionType, withReferences, withKeeper, withoutEmptyKeeper, withProductionNodes, withScale,
98
- withDerivationList, withReadingKind]
110
+ withDerivationList, withReadingKind, withTimeSpanDates]
99
111
  .reduce((result, step) => step(result), node);
100
112
  /** The items each walked, or the very same list where the walk changed none. */
101
113
  const walked = (items) => {
package/lib/schema.json CHANGED
@@ -256,43 +256,111 @@
256
256
  "ontology": "crm:E55 Type"
257
257
  },
258
258
  "DateAssignment": {
259
- "description": "A date value wrapped as an assumption, so that the date can be annotated with a belief about its certainty and source.",
260
- "properties": {
261
- "@annotation": {
262
- "description": "An optional annotation expressing a belief about this assumption. Uses the JSON-LD-star `@annotation` mechanism to attach epistemic metadata (certainty and reasons) to any triple.",
259
+ "anyOf": [
260
+ {
263
261
  "properties": {
264
- "belief": {
265
- "$ref": "#/definitions/Belief",
266
- "description": "The belief held about the annotated statement.",
267
- "ontology": "crminf:J4i is subject of"
262
+ "@annotation": {
263
+ "description": "An optional annotation expressing a belief about this assumption. Uses the JSON-LD-star `@annotation` mechanism to attach epistemic metadata (certainty and reasons) to any triple.",
264
+ "properties": {
265
+ "belief": {
266
+ "$ref": "#/definitions/Belief",
267
+ "description": "The belief held about the annotated statement.",
268
+ "ontology": "crminf:J4i is subject of"
269
+ },
270
+ "@id": {
271
+ "description": "A unique identifier for this object.",
272
+ "type": "string"
273
+ }
274
+ },
275
+ "required": [
276
+ "belief",
277
+ "@id"
278
+ ],
279
+ "type": "object"
268
280
  },
269
- "@id": {
270
- "description": "A unique identifier for this object.",
281
+ "within": {
282
+ "format": "date",
271
283
  "type": "string"
272
284
  }
273
285
  },
274
286
  "required": [
275
- "belief",
276
- "@id"
287
+ "within"
277
288
  ],
278
289
  "type": "object"
279
290
  },
280
- "@type": {
281
- "const": "xsd:date",
282
- "description": "The datatype of the value. Written on export so that RDF reads the value as a date rather than a string.",
283
- "type": "string",
284
- "ontology": "rdf:type"
291
+ {
292
+ "properties": {
293
+ "@annotation": {
294
+ "description": "An optional annotation expressing a belief about this assumption. Uses the JSON-LD-star `@annotation` mechanism to attach epistemic metadata (certainty and reasons) to any triple.",
295
+ "properties": {
296
+ "belief": {
297
+ "$ref": "#/definitions/Belief",
298
+ "description": "The belief held about the annotated statement.",
299
+ "ontology": "crminf:J4i is subject of"
300
+ },
301
+ "@id": {
302
+ "description": "A unique identifier for this object.",
303
+ "type": "string"
304
+ }
305
+ },
306
+ "required": [
307
+ "belief",
308
+ "@id"
309
+ ],
310
+ "type": "object"
311
+ },
312
+ "after": {
313
+ "format": "date",
314
+ "type": "string"
315
+ },
316
+ "before": {
317
+ "format": "date",
318
+ "type": "string"
319
+ }
320
+ },
321
+ "required": [
322
+ "after"
323
+ ],
324
+ "type": "object"
285
325
  },
286
- "@value": {
287
- "description": "The assumed value.",
288
- "format": "date",
289
- "type": "string"
326
+ {
327
+ "properties": {
328
+ "@annotation": {
329
+ "description": "An optional annotation expressing a belief about this assumption. Uses the JSON-LD-star `@annotation` mechanism to attach epistemic metadata (certainty and reasons) to any triple.",
330
+ "properties": {
331
+ "belief": {
332
+ "$ref": "#/definitions/Belief",
333
+ "description": "The belief held about the annotated statement.",
334
+ "ontology": "crminf:J4i is subject of"
335
+ },
336
+ "@id": {
337
+ "description": "A unique identifier for this object.",
338
+ "type": "string"
339
+ }
340
+ },
341
+ "required": [
342
+ "belief",
343
+ "@id"
344
+ ],
345
+ "type": "object"
346
+ },
347
+ "after": {
348
+ "format": "date",
349
+ "type": "string"
350
+ },
351
+ "before": {
352
+ "format": "date",
353
+ "type": "string"
354
+ }
355
+ },
356
+ "required": [
357
+ "before"
358
+ ],
359
+ "type": "object"
290
360
  }
291
- },
292
- "required": [
293
- "@value"
294
361
  ],
295
- "type": "object"
362
+ "description": "When something happened, as far as the edition can state it: the day it falls `within`, or the bounds it lies between. A bound nobody can give is left out, so `after` alone says \"not before\".",
363
+ "ontology": "crm:E52 Time-Span"
296
364
  },
297
365
  "Derivation": {
298
366
  "description": "A derivation names the version another one was derived from, together with the tolerance the two were collated at. How precisely the copies put a symbol depends on what they are and on how their features were obtained, so the tolerance can differ from derivation to derivation.",
@@ -638,8 +706,7 @@
638
706
  "date": {
639
707
  "$ref": "#/definitions/DateAssignment",
640
708
  "description": "When the capture took place.",
641
- "format": "date",
642
- "ontology": "dcterms:date"
709
+ "ontology": "crm:P4 has time-span"
643
710
  },
644
711
  "device": {
645
712
  "$ref": "#/definitions/Concept",
@@ -1074,7 +1141,7 @@
1074
1141
  "date": {
1075
1142
  "$ref": "#/definitions/DateAssignment",
1076
1143
  "description": "When the modification took place.",
1077
- "ontology": "dcterms:date"
1144
+ "ontology": "crm:P4 has time-span"
1078
1145
  },
1079
1146
  "purpose": {
1080
1147
  "enum": [
@@ -1113,7 +1180,7 @@
1113
1180
  "date": {
1114
1181
  "$ref": "#/definitions/DateAssignment",
1115
1182
  "description": "When the modification took place.",
1116
- "ontology": "dcterms:date"
1183
+ "ontology": "crm:P4 has time-span"
1117
1184
  },
1118
1185
  "purpose": {
1119
1186
  "const": "delabeling",
@@ -1951,7 +2018,7 @@
1951
2018
  "date": {
1952
2019
  "$ref": "#/definitions/DateAssignment",
1953
2020
  "description": "The date of production, if known.",
1954
- "ontology": "dcterms:date"
2021
+ "ontology": "crm:P4 has time-span"
1955
2022
  },
1956
2023
  "paper": {
1957
2024
  "$ref": "#/definitions/Concept",
@@ -2001,7 +2068,7 @@
2001
2068
  "date": {
2002
2069
  "$ref": "#/definitions/DateAssignment",
2003
2070
  "description": "The recording date of the roll. This is a date assignment so that we can state e.g. the catalogue or the roll label which indicates the date of the recording.",
2004
- "ontology": "dcterms:date"
2071
+ "ontology": "crm:P4 has time-span"
2005
2072
  },
2006
2073
  "place": {
2007
2074
  "$ref": "#/definitions/Place",
@@ -2271,7 +2338,7 @@
2271
2338
  "ontology": "crm:P138i has representation"
2272
2339
  },
2273
2340
  "siglum": {
2274
- "description": "A short siglum to identify the copy, e.g. \"W1\" or \"S2\". A copy without one is named by its keeper.",
2341
+ "description": "A short siglum to identify the copy, e.g. \"St1\" or \"Wi1\": letters for the collection the copy was read in, and a number counting the copies of the roll held there. A copy without one is named by its keeper.",
2275
2342
  "type": "string",
2276
2343
  "ontology": "reo:siglum"
2277
2344
  },
@@ -2446,7 +2513,7 @@
2446
2513
  "type": "array"
2447
2514
  },
2448
2515
  "siglum": {
2449
- "description": "A short siglum to identify the version, e.g. \"A\", \"B1\", \"B2_rev\", etc.",
2516
+ "description": "A short siglum to identify the version, e.g. \"A\" or \"B2\". A siglum is given once and not changed: its letter names the line the version was first recognised in, its number counts the versions admitted to that line. Where later work places the version elsewhere in the stemma, `basedOn` changes and the siglum stays.",
2450
2517
  "type": "string",
2451
2518
  "ontology": "reo:siglum"
2452
2519
  },
@@ -2487,7 +2554,7 @@
2487
2554
  "date": {
2488
2555
  "$ref": "#/definitions/DateAssignment",
2489
2556
  "description": "When it took place.",
2490
- "ontology": "dcterms:date"
2557
+ "ontology": "crm:P4 has time-span"
2491
2558
  },
2492
2559
  "procedure": {
2493
2560
  "$ref": "#/definitions/Concept",
@@ -103,8 +103,21 @@
103
103
  },
104
104
  "place": "crm:P7_took_place_at",
105
105
  "date": {
106
- "@id": "dcterms:date",
107
- "@type": "xsd:date"
106
+ "@id": "crm:P4_has_time-span",
107
+ "@context": {
108
+ "within": {
109
+ "@id": "crm:P82_at_some_time_within",
110
+ "@type": "xsd:date"
111
+ },
112
+ "after": {
113
+ "@id": "crm:P82a_begin_of_the_begin",
114
+ "@type": "xsd:date"
115
+ },
116
+ "before": {
117
+ "@id": "crm:P82b_end_of_the_end",
118
+ "@type": "xsd:date"
119
+ }
120
+ }
108
121
  },
109
122
  "created": "lrmoo:R17_created",
110
123
  "copies": "reo:witness",
@@ -148,7 +161,11 @@
148
161
  "value": "crm:P90_has_value",
149
162
  "measuredBy": "crmdig:L23_used_software_or_firmware",
150
163
  "software": "rdfs:label",
151
- "version": "owl:versionInfo"
164
+ "version": "owl:versionInfo",
165
+ "date": {
166
+ "@id": "dcterms:date",
167
+ "@type": "xsd:date"
168
+ }
152
169
  }
153
170
  },
154
171
  "production": {
package/lib/utils.d.ts CHANGED
@@ -12,6 +12,8 @@ export type WithId = {
12
12
  */
13
13
  readonly id: string;
14
14
  };
15
+ /** Whether a value is a date as the format writes one, `YYYY-MM-DD`. */
16
+ export declare const isDateString: (value: unknown) => value is string;
15
17
  export type WithNote = {
16
18
  /**
17
19
  * A free-text note providing additional context.
package/lib/utils.js CHANGED
@@ -1 +1,2 @@
1
- export {};
1
+ /** Whether a value is a date as the format writes one, `YYYY-MM-DD`. */
2
+ export const isDateString = (value) => typeof value === 'string' && /^\d{4}-\d{1,2}-\d{1,2}$/.test(value);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linked-rolls",
3
- "version": "0.29.0",
3
+ "version": "0.31.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": {