@internetarchive/metadata-service 0.0.1-alpha.1

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 (125) hide show
  1. package/.editorconfig +29 -0
  2. package/.eslintrc.js +14 -0
  3. package/.github/workflows/ci.yml +26 -0
  4. package/LICENSE +661 -0
  5. package/README.md +64 -0
  6. package/demo/app-root.ts +80 -0
  7. package/demo/index.html +27 -0
  8. package/dist/demo/app-root.d.ts +11 -0
  9. package/dist/demo/app-root.js +71 -0
  10. package/dist/demo/app-root.js.map +1 -0
  11. package/dist/index.d.ts +18 -0
  12. package/dist/index.js +17 -0
  13. package/dist/index.js.map +1 -0
  14. package/dist/src/backend/default-metadata-backend.d.ts +27 -0
  15. package/dist/src/backend/default-metadata-backend.js +101 -0
  16. package/dist/src/backend/default-metadata-backend.js.map +1 -0
  17. package/dist/src/backend/metadata-backend-interface.d.ts +20 -0
  18. package/dist/src/backend/metadata-backend-interface.js +2 -0
  19. package/dist/src/backend/metadata-backend-interface.js.map +1 -0
  20. package/dist/src/metadata-service-error.d.ts +11 -0
  21. package/dist/src/metadata-service-error.js +18 -0
  22. package/dist/src/metadata-service-error.js.map +1 -0
  23. package/dist/src/metadata-service-interface.d.ts +44 -0
  24. package/dist/src/metadata-service-interface.js +2 -0
  25. package/dist/src/metadata-service-interface.js.map +1 -0
  26. package/dist/src/metadata-service.d.ts +18 -0
  27. package/dist/src/metadata-service.js +43 -0
  28. package/dist/src/metadata-service.js.map +1 -0
  29. package/dist/src/models/file.d.ts +29 -0
  30. package/dist/src/models/file.js +95 -0
  31. package/dist/src/models/file.js.map +1 -0
  32. package/dist/src/models/metadata-fields/field-types/boolean.d.ts +5 -0
  33. package/dist/src/models/metadata-fields/field-types/boolean.js +8 -0
  34. package/dist/src/models/metadata-fields/field-types/boolean.js.map +1 -0
  35. package/dist/src/models/metadata-fields/field-types/byte.d.ts +13 -0
  36. package/dist/src/models/metadata-fields/field-types/byte.js +16 -0
  37. package/dist/src/models/metadata-fields/field-types/byte.js.map +1 -0
  38. package/dist/src/models/metadata-fields/field-types/date.d.ts +5 -0
  39. package/dist/src/models/metadata-fields/field-types/date.js +8 -0
  40. package/dist/src/models/metadata-fields/field-types/date.js.map +1 -0
  41. package/dist/src/models/metadata-fields/field-types/duration.d.ts +14 -0
  42. package/dist/src/models/metadata-fields/field-types/duration.js +17 -0
  43. package/dist/src/models/metadata-fields/field-types/duration.js.map +1 -0
  44. package/dist/src/models/metadata-fields/field-types/list.d.ts +25 -0
  45. package/dist/src/models/metadata-fields/field-types/list.js +34 -0
  46. package/dist/src/models/metadata-fields/field-types/list.js.map +1 -0
  47. package/dist/src/models/metadata-fields/field-types/mediatype.d.ts +5 -0
  48. package/dist/src/models/metadata-fields/field-types/mediatype.js +8 -0
  49. package/dist/src/models/metadata-fields/field-types/mediatype.js.map +1 -0
  50. package/dist/src/models/metadata-fields/field-types/number.d.ts +5 -0
  51. package/dist/src/models/metadata-fields/field-types/number.js +8 -0
  52. package/dist/src/models/metadata-fields/field-types/number.js.map +1 -0
  53. package/dist/src/models/metadata-fields/field-types/page-progression.d.ts +5 -0
  54. package/dist/src/models/metadata-fields/field-types/page-progression.js +8 -0
  55. package/dist/src/models/metadata-fields/field-types/page-progression.js.map +1 -0
  56. package/dist/src/models/metadata-fields/field-types/string.d.ts +5 -0
  57. package/dist/src/models/metadata-fields/field-types/string.js +8 -0
  58. package/dist/src/models/metadata-fields/field-types/string.js.map +1 -0
  59. package/dist/src/models/metadata-fields/metadata-field.d.ts +70 -0
  60. package/dist/src/models/metadata-fields/metadata-field.js +67 -0
  61. package/dist/src/models/metadata-fields/metadata-field.js.map +1 -0
  62. package/dist/src/models/metadata.d.ts +168 -0
  63. package/dist/src/models/metadata.js +618 -0
  64. package/dist/src/models/metadata.js.map +1 -0
  65. package/dist/src/models/review.d.ts +10 -0
  66. package/dist/src/models/review.js +43 -0
  67. package/dist/src/models/review.js.map +1 -0
  68. package/dist/src/models/speech-music-asr-entry.d.ts +10 -0
  69. package/dist/src/models/speech-music-asr-entry.js +2 -0
  70. package/dist/src/models/speech-music-asr-entry.js.map +1 -0
  71. package/dist/src/responses/metadata-response.d.ts +28 -0
  72. package/dist/src/responses/metadata-response.js +31 -0
  73. package/dist/src/responses/metadata-response.js.map +1 -0
  74. package/dist/test/default-metadata-backend.test.d.ts +1 -0
  75. package/dist/test/default-metadata-backend.test.js +91 -0
  76. package/dist/test/default-metadata-backend.test.js.map +1 -0
  77. package/dist/test/metadata-service.test.d.ts +1 -0
  78. package/dist/test/metadata-service.test.js +99 -0
  79. package/dist/test/metadata-service.test.js.map +1 -0
  80. package/dist/test/mock-response-generator.d.ts +3 -0
  81. package/dist/test/mock-response-generator.js +62 -0
  82. package/dist/test/mock-response-generator.js.map +1 -0
  83. package/dist/test/models/file.test.d.ts +1 -0
  84. package/dist/test/models/file.test.js +9 -0
  85. package/dist/test/models/file.test.js.map +1 -0
  86. package/dist/test/models/metadata-fields/list-field.test.d.ts +1 -0
  87. package/dist/test/models/metadata-fields/list-field.test.js +52 -0
  88. package/dist/test/models/metadata-fields/list-field.test.js.map +1 -0
  89. package/dist/test/models/metadata-fields/metadata-field.test.d.ts +1 -0
  90. package/dist/test/models/metadata-fields/metadata-field.test.js +121 -0
  91. package/dist/test/models/metadata-fields/metadata-field.test.js.map +1 -0
  92. package/dist/test/models/metadata.test.d.ts +1 -0
  93. package/dist/test/models/metadata.test.js +10 -0
  94. package/dist/test/models/metadata.test.js.map +1 -0
  95. package/index.ts +32 -0
  96. package/package.json +77 -0
  97. package/src/backend/default-metadata-backend.ts +141 -0
  98. package/src/backend/metadata-backend-interface.ts +25 -0
  99. package/src/metadata-service-error.ts +21 -0
  100. package/src/metadata-service-interface.ts +51 -0
  101. package/src/metadata-service.ts +63 -0
  102. package/src/models/file.ts +105 -0
  103. package/src/models/metadata-fields/field-types/boolean.ts +8 -0
  104. package/src/models/metadata-fields/field-types/byte.ts +16 -0
  105. package/src/models/metadata-fields/field-types/date.ts +8 -0
  106. package/src/models/metadata-fields/field-types/duration.ts +17 -0
  107. package/src/models/metadata-fields/field-types/list.ts +44 -0
  108. package/src/models/metadata-fields/field-types/mediatype.ts +8 -0
  109. package/src/models/metadata-fields/field-types/number.ts +8 -0
  110. package/src/models/metadata-fields/field-types/page-progression.ts +14 -0
  111. package/src/models/metadata-fields/field-types/string.ts +8 -0
  112. package/src/models/metadata-fields/metadata-field.ts +108 -0
  113. package/src/models/metadata.ts +457 -0
  114. package/src/models/review.ts +41 -0
  115. package/src/models/speech-music-asr-entry.ts +10 -0
  116. package/src/responses/metadata-response.ts +61 -0
  117. package/test/default-metadata-backend.test.ts +101 -0
  118. package/test/metadata-service.test.ts +150 -0
  119. package/test/mock-response-generator.ts +62 -0
  120. package/test/models/file.test.ts +10 -0
  121. package/test/models/metadata-fields/list-field.test.ts +64 -0
  122. package/test/models/metadata-fields/metadata-field.test.ts +151 -0
  123. package/test/models/metadata.test.ts +11 -0
  124. package/tsconfig.json +22 -0
  125. package/web-test-runner.config.mjs +7 -0
@@ -0,0 +1,105 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import { Memoize } from 'typescript-memoize';
3
+ import {
4
+ Byte,
5
+ ByteParser,
6
+ Duration,
7
+ DurationParser,
8
+ NumberParser,
9
+ } from '@internetarchive/field-parsers';
10
+
11
+ /**
12
+ * This represents an Internet Archive File
13
+ *
14
+ * @export
15
+ * @class File
16
+ */
17
+ export class File {
18
+ rawValue: Record<string, any>;
19
+
20
+ get name(): string {
21
+ return this.rawValue.name;
22
+ }
23
+
24
+ get source(): string {
25
+ return this.rawValue.source;
26
+ }
27
+
28
+ get btih(): string {
29
+ return this.rawValue.btih;
30
+ }
31
+
32
+ get md5(): string {
33
+ return this.rawValue.md5;
34
+ }
35
+
36
+ get format(): string {
37
+ return this.rawValue.format;
38
+ }
39
+
40
+ get mtime(): string {
41
+ return this.rawValue.mtime;
42
+ }
43
+
44
+ get crc32(): string {
45
+ return this.rawValue.crc32;
46
+ }
47
+
48
+ get sha1(): string {
49
+ return this.rawValue.sha1;
50
+ }
51
+
52
+ get original(): string | undefined {
53
+ return this.rawValue.original;
54
+ }
55
+
56
+ @Memoize() get size(): Byte | undefined {
57
+ return this.rawValue.size
58
+ ? ByteParser.shared.parseValue(this.rawValue.size)
59
+ : undefined;
60
+ }
61
+
62
+ get title(): string | undefined {
63
+ return this.rawValue.title;
64
+ }
65
+
66
+ @Memoize() get length(): Duration | undefined {
67
+ return this.rawValue.length
68
+ ? DurationParser.shared.parseValue(this.rawValue.length)
69
+ : undefined;
70
+ }
71
+
72
+ @Memoize() get height(): number | undefined {
73
+ return this.rawValue.height
74
+ ? NumberParser.shared.parseValue(this.rawValue.height)
75
+ : undefined;
76
+ }
77
+
78
+ @Memoize() get width(): number | undefined {
79
+ return this.rawValue.width
80
+ ? NumberParser.shared.parseValue(this.rawValue.width)
81
+ : undefined;
82
+ }
83
+
84
+ @Memoize() get track(): number | undefined {
85
+ return this.rawValue.track
86
+ ? NumberParser.shared.parseValue(this.rawValue.track)
87
+ : undefined;
88
+ }
89
+
90
+ get external_identifier(): string | string[] | undefined {
91
+ return this.rawValue.external_identifier;
92
+ }
93
+
94
+ get creator(): string | undefined {
95
+ return this.rawValue.creator;
96
+ }
97
+
98
+ get album(): string | undefined {
99
+ return this.rawValue.album;
100
+ }
101
+
102
+ constructor(json: Record<string, any>) {
103
+ this.rawValue = json;
104
+ }
105
+ }
@@ -0,0 +1,8 @@
1
+ import { BooleanParser } from '@internetarchive/field-parsers';
2
+ import { MetadataField, MetadataRawValue } from '../metadata-field';
3
+
4
+ export class BooleanField extends MetadataField<boolean, BooleanParser> {
5
+ constructor(rawValue: MetadataRawValue) {
6
+ super(BooleanParser.shared, rawValue);
7
+ }
8
+ }
@@ -0,0 +1,16 @@
1
+ import { Byte, ByteParser } from '@internetarchive/field-parsers';
2
+ import { MetadataField, MetadataRawValue } from '../metadata-field';
3
+
4
+ /**
5
+ * ByteField is a unit-specific number field that
6
+ * returns a value in bytes
7
+ *
8
+ * @export
9
+ * @class ByteField
10
+ * @extends {MetadataField<Byte, ByteParser>}
11
+ */
12
+ export class ByteField extends MetadataField<Byte, ByteParser> {
13
+ constructor(rawValue: MetadataRawValue) {
14
+ super(ByteParser.shared, rawValue);
15
+ }
16
+ }
@@ -0,0 +1,8 @@
1
+ import { DateParser } from '@internetarchive/field-parsers';
2
+ import { MetadataField, MetadataRawValue } from '../metadata-field';
3
+
4
+ export class DateField extends MetadataField<Date, DateParser> {
5
+ constructor(rawValue: MetadataRawValue) {
6
+ super(DateParser.shared, rawValue);
7
+ }
8
+ }
@@ -0,0 +1,17 @@
1
+ import { Duration, DurationParser } from '@internetarchive/field-parsers';
2
+ import { MetadataField, MetadataRawValue } from '../metadata-field';
3
+
4
+ /**
5
+ * The DurationField parses different duration formats
6
+ * and returns a `Duration`, which is a number in seconds
7
+ * with decimals.
8
+ *
9
+ * @export
10
+ * @class DurationField
11
+ * @extends {MetadataField<Duration, DurationParser>}
12
+ */
13
+ export class DurationField extends MetadataField<Duration, DurationParser> {
14
+ constructor(rawValue: MetadataRawValue) {
15
+ super(DurationParser.shared, rawValue);
16
+ }
17
+ }
@@ -0,0 +1,44 @@
1
+ import {
2
+ FieldParserInterface,
3
+ ListParser,
4
+ NumberParser,
5
+ StringParser,
6
+ } from '@internetarchive/field-parsers';
7
+ import { MetadataField, MetadataRawValue } from '../metadata-field';
8
+
9
+ /**
10
+ * The ListField handles parsing of a list of values.
11
+ *
12
+ * Certain fields in the metadata, like `subject` typically have a
13
+ * comma or semicolon-separated list of values. The `ListField`
14
+ * parses the list values independently and aggregates them into
15
+ * the main `.values` array.
16
+ */
17
+ export class ListField<
18
+ T,
19
+ FieldParserInterfaceType extends FieldParserInterface<T[]>
20
+ > extends MetadataField<T, FieldParserInterfaceType> {
21
+ constructor(rawValue: MetadataRawValue, parser: FieldParserInterfaceType) {
22
+ super(parser, rawValue);
23
+ }
24
+ }
25
+
26
+ /**
27
+ * The StringListField handles parsing of a list of strings.
28
+ */
29
+ export class StringListField extends ListField<string, ListParser<string>> {
30
+ constructor(rawValue: MetadataRawValue) {
31
+ const parser = new ListParser<string>(StringParser.shared);
32
+ super(rawValue, parser);
33
+ }
34
+ }
35
+
36
+ /**
37
+ * The NumberListField handles parsing of a list of numbers.
38
+ */
39
+ export class NumberListField extends ListField<number, ListParser<number>> {
40
+ constructor(rawValue: MetadataRawValue) {
41
+ const parser = new ListParser<number>(NumberParser.shared);
42
+ super(rawValue, parser);
43
+ }
44
+ }
@@ -0,0 +1,8 @@
1
+ import { MediaType, MediaTypeParser } from '@internetarchive/field-parsers';
2
+ import { MetadataField, MetadataRawValue } from '../metadata-field';
3
+
4
+ export class MediaTypeField extends MetadataField<MediaType, MediaTypeParser> {
5
+ constructor(rawValue: MetadataRawValue) {
6
+ super(MediaTypeParser.shared, rawValue);
7
+ }
8
+ }
@@ -0,0 +1,8 @@
1
+ import { NumberParser } from '@internetarchive/field-parsers';
2
+ import { MetadataField, MetadataRawValue } from '../metadata-field';
3
+
4
+ export class NumberField extends MetadataField<number, NumberParser> {
5
+ constructor(rawValue: MetadataRawValue) {
6
+ super(NumberParser.shared, rawValue);
7
+ }
8
+ }
@@ -0,0 +1,14 @@
1
+ import {
2
+ PageProgression,
3
+ PageProgressionParser,
4
+ } from '@internetarchive/field-parsers';
5
+ import { MetadataField, MetadataRawValue } from '../metadata-field';
6
+
7
+ export class PageProgressionField extends MetadataField<
8
+ PageProgression,
9
+ PageProgressionParser
10
+ > {
11
+ constructor(rawValue: MetadataRawValue) {
12
+ super(PageProgressionParser.shared, rawValue);
13
+ }
14
+ }
@@ -0,0 +1,8 @@
1
+ import { StringParser } from '@internetarchive/field-parsers';
2
+ import { MetadataField, MetadataRawValue } from '../metadata-field';
3
+
4
+ export class StringField extends MetadataField<string, StringParser> {
5
+ constructor(rawValue: MetadataRawValue) {
6
+ super(StringParser.shared, rawValue);
7
+ }
8
+ }
@@ -0,0 +1,108 @@
1
+ import { Memoize } from 'typescript-memoize';
2
+ import { FieldParserInterface } from '@internetarchive/field-parsers';
3
+
4
+ /**
5
+ * The MetadataRawValue is all of the possible raw types we can get for a field.
6
+ *
7
+ * This allows the parsers to know if they can handle the raw value or not and
8
+ * how to handle it if they can.
9
+ */
10
+ export type MetadataRawValue = string | string[] | number | boolean;
11
+
12
+ export interface MetadataFieldInterface<T> {
13
+ /**
14
+ * The raw value received from the API response
15
+ *
16
+ * @type {MetadataRawValue}
17
+ * @memberof MetadataField
18
+ */
19
+ rawValue?: MetadataRawValue;
20
+
21
+ /**
22
+ * The first value if there are multiple or the only value if there is one
23
+ *
24
+ * @readonly
25
+ * @type {(T | undefined)}
26
+ * @memberof MetadataField
27
+ */
28
+ value?: T;
29
+
30
+ /**
31
+ * The array of all values for the field.
32
+ *
33
+ * Many fields only contain a single value and
34
+ * can be accessed via the `.value` getter
35
+ *
36
+ * @type {T[]}
37
+ * @memberof MetadataField
38
+ */
39
+ values: T[];
40
+ }
41
+
42
+ /**
43
+ * The MetadataField is responsible for three things:
44
+ * 1. Take in some raw data (strings, arrays, numbers, etc)
45
+ * 2. Normalize the input to an array of the input,
46
+ * ie. [string, string], [number, number], [Date, Date], etc
47
+ * 3. Cast the values to their expected `Type`
48
+ *
49
+ * This class gets instiated with a `Type` and a parser of that type. For instance, the
50
+ * `DateField` is a subclass of `MetadataField` with a `Type` of `Date` and a `DateParser`.
51
+ *
52
+ * When using a `DateField`, you can pass it a string date and it will cast it to a javascript Date,
53
+ * ie:
54
+ *
55
+ * ```
56
+ * const dateField = new DateField('2020-02-13')
57
+ * dateField.value = Date(2020-02-13) // native javascript Date object
58
+ * dateField.values = [Date(2020-02-13)] // the normalized array of values
59
+ * dateField.rawValue = '2020-02-13' // the raw string that was passed in
60
+ * ```
61
+ *
62
+ * @class MetadataField
63
+ * @template Type The type of metadata this is (string, number, Date, etc)
64
+ * @template FieldParserInterfaceType The parser for that type (StringParser, NumberParser, etc)
65
+ */
66
+ export class MetadataField<
67
+ Type,
68
+ FieldParserInterfaceType extends FieldParserInterface<Type | Type[]>
69
+ > implements MetadataFieldInterface<Type>
70
+ {
71
+ /** @inheritdoc */
72
+ rawValue?: MetadataRawValue;
73
+
74
+ /** @inheritdoc */
75
+ @Memoize() get values(): Type[] {
76
+ const values = this.parseRawValue();
77
+ return values;
78
+ }
79
+
80
+ /** @inheritdoc */
81
+ @Memoize() get value(): Type | undefined {
82
+ return this.values[0];
83
+ }
84
+
85
+ constructor(parser: FieldParserInterfaceType, rawValue?: MetadataRawValue) {
86
+ this.parser = parser;
87
+ this.rawValue = rawValue;
88
+ }
89
+
90
+ private parser: FieldParserInterfaceType;
91
+
92
+ private parseRawValue(): Type[] {
93
+ if (this.rawValue === undefined) return [];
94
+ const rawValues = Array.isArray(this.rawValue)
95
+ ? this.rawValue
96
+ : [this.rawValue];
97
+ const values: Type[] = [];
98
+ rawValues.forEach(value => {
99
+ const parsed = this.parser.parseValue(value);
100
+ if (Array.isArray(parsed)) {
101
+ values.push(...parsed);
102
+ } else {
103
+ if (parsed !== undefined) values.push(parsed);
104
+ }
105
+ });
106
+ return values;
107
+ }
108
+ }