TypeScript types and utility functions for handling Web Annotations.

web-annotation-utils/src/ WebAnnotation.ts
186 lines
4.3 KiB

  1. import type {
  2. OneOrMore,
  3. OneOrMoreIncluding,
  4. ZeroOrMore,
  5. } from './multiplicity-utils.js';
  6. /**
  7. * A Web Annotation object.
  8. *
  9. * This is an interpretation of the Web Annotation Data Model:
  10. * <https://www.w3.org/TR/2017/REC-annotation-model-20170223/>
  11. *
  12. * TODO Deal more systemically with ‘relations’, i.e. values that could be
  13. * either a nested object or a URI referring to such an object.
  14. */
  15. export interface WebAnnotation {
  16. '@context': OneOrMoreIncluding<string, 'http://www.w3.org/ns/anno.jsonld'>;
  17. type: OneOrMoreIncluding<string, 'Annotation'>;
  18. id: string;
  19. target: OneOrMore<Target>;
  20. creator?: OneOrMore<Agent>;
  21. created?: UtcDateTime;
  22. generator?: OneOrMore<Agent>;
  23. generated?: UtcDateTime;
  24. modified?: UtcDateTime;
  25. motivation?: OneOrMore<Motivation>;
  26. audience?: ZeroOrMore<Audience>;
  27. rights?: ZeroOrMore<string>;
  28. canonical?: string;
  29. via?: ZeroOrMore<string>;
  30. body?: BodyChoice | OneOrMore<Body>;
  31. bodyValue?: string;
  32. }
  33. /**
  34. * A slightly stricter type for WebAnnotation, not allowing both a body and bodyValue.
  35. */
  36. export type WebAnnotationStrict = WebAnnotation & (WithBody | WithBodyValue | WithoutBody);
  37. interface WithBody {
  38. body: BodyChoice | OneOrMore<Body>;
  39. bodyValue?: undefined;
  40. }
  41. interface WithBodyValue {
  42. body?: undefined;
  43. bodyValue: string;
  44. }
  45. interface WithoutBody {
  46. body?: undefined;
  47. bodyValue?: undefined;
  48. }
  49. export type Body = string | BodyObject;
  50. export type BodyObject = {
  51. creator?: OneOrMore<Agent>;
  52. created?: UtcDateTime;
  53. modified?: UtcDateTime;
  54. purpose?: OneOrMore<Motivation>;
  55. } & (TextualBody | SpecificResource | ExternalResource);
  56. export type Target = string | SpecificResource | ExternalResource;
  57. export type Agent =
  58. | string
  59. | {
  60. id?: string;
  61. type?: OneOrMore<'Person' | 'Organization' | 'Software'>;
  62. name?: OneOrMore<string>;
  63. nickname?: OneOrMore<string>;
  64. email?: OneOrMore<string>;
  65. email_sha1?: OneOrMore<string>;
  66. homepage?: OneOrMore<string>;
  67. };
  68. export type Audience =
  69. | string
  70. | {
  71. id?: string;
  72. type?: string;
  73. };
  74. export interface BodyChoice {
  75. type: 'Choice';
  76. items: Body[];
  77. }
  78. export interface TextualBody extends Omit<ExternalResource, 'id' | 'type'> {
  79. id?: string;
  80. type: 'TextualBody';
  81. value: string;
  82. }
  83. export interface SpecificResource {
  84. id?: string;
  85. type?: 'SpecificResource';
  86. source: string;
  87. selector?: string | OneOrMore<Selector>;
  88. accessibility?: AccessibilityFeatures;
  89. rights?: ZeroOrMore<string>;
  90. canonical?: string;
  91. via?: ZeroOrMore<string>;
  92. }
  93. export interface Selector {
  94. type?: string;
  95. refinedBy?: Selector;
  96. }
  97. export interface ExternalResource {
  98. id: string;
  99. // XXX type’s value SHOULD be one of these, “but MAY come from other vocabularies”.
  100. type?: OneOrMore<'Dataset' | 'Image' | 'Video' | 'Sound' | 'Text'>;
  101. format?: OneOrMore<string>;
  102. language?: OneOrMore<string>;
  103. processingLanguage?: string;
  104. textDirection?: 'ltr' | 'rtl' | 'auto';
  105. accessibility?: AccessibilityFeatures;
  106. rights?: ZeroOrMore<string>;
  107. canonical?: string;
  108. via?: ZeroOrMore<string>;
  109. }
  110. export type Motivation =
  111. | 'assessing'
  112. | 'bookmarking'
  113. | 'classifying'
  114. | 'commenting'
  115. | 'describing'
  116. | 'editing'
  117. | 'highlighting'
  118. | 'identifying'
  119. | 'linking'
  120. | 'moderating'
  121. | 'questioning'
  122. | 'replying'
  123. | 'tagging';
  124. // “The datetime MUST be a xsd:dateTime with the UTC timezone expressed as "Z".”
  125. type UtcDateTime = `${string}Z`;
  126. declare global {
  127. interface Date {
  128. toISOString(): UtcDateTime;
  129. }
  130. }
  131. // From <https://www.w3.org/2021/a11y-discov-vocab/latest/CG-FINAL-a11y-discov-vocab-20220610.html>
  132. export type AccessibilityFeatures =
  133. | ZeroOrMore<AccessibilityFeature>
  134. | 'none'
  135. | ['none'];
  136. export type AccessibilityFeature =
  137. | 'annotations'
  138. | 'ARIA'
  139. | 'bookmarks'
  140. | 'index'
  141. | 'printPageNumbers'
  142. | 'readingOrder'
  143. | 'structuralNavigation'
  144. | 'tableOfContents'
  145. | 'taggedPDF'
  146. | 'alternativeText'
  147. | 'audioDescription'
  148. | 'captions'
  149. | 'describedMath'
  150. | 'longDescription'
  151. | 'rubyAnnotations'
  152. | 'signLanguage'
  153. | 'transcript'
  154. | 'displayTransformability'
  155. | 'synchronizedAudioText'
  156. | 'timingControl'
  157. | 'unlocked'
  158. | 'ChemML'
  159. | 'latex'
  160. | 'MathML'
  161. | 'ttsMarkup'
  162. | 'highContrastAudio'
  163. | 'highContrastDisplay'
  164. | 'largePrint'
  165. | 'braille'
  166. | 'tactileGraphic'
  167. | 'tactileObject';