@internetarchive/metadata-service 0.0.0-bc6513-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 (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 +169 -0
  63. package/dist/src/models/metadata.js +627 -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 +463 -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,121 @@
1
+ import { expect } from '@open-wc/testing';
2
+ import { MetadataField } from '../../../src/models/metadata-fields/metadata-field';
3
+ describe('Metadata Field', () => {
4
+ it('can be properly instantiated with single value', () => {
5
+ class MockParser {
6
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
7
+ parseValue(rawValue) {
8
+ return rawValue;
9
+ }
10
+ }
11
+ const parser = new MockParser();
12
+ const metadataField = new MetadataField(parser, 'foo');
13
+ expect(metadataField.rawValue).to.equal('foo');
14
+ expect(metadataField.value).to.equal('foo');
15
+ expect(metadataField.values).to.deep.equal(['foo']);
16
+ });
17
+ it('can be properly instantiated with array value', () => {
18
+ class MockParser {
19
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
20
+ parseValue(rawValue) {
21
+ return rawValue;
22
+ }
23
+ }
24
+ const parser = new MockParser();
25
+ const metadataField = new MetadataField(parser, ['foo', 'bar', 'baz']);
26
+ expect(metadataField.rawValue).to.deep.equal(['foo', 'bar', 'baz']);
27
+ expect(metadataField.value).to.equal('foo');
28
+ expect(metadataField.values).to.deep.equal(['foo', 'bar', 'baz']);
29
+ });
30
+ it('properly casts values to expected parser type for single values', () => {
31
+ class MockFloatParser {
32
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
33
+ parseValue(rawValue) {
34
+ return parseFloat(rawValue);
35
+ }
36
+ }
37
+ const parser = new MockFloatParser();
38
+ const metadataField = new MetadataField(parser, '1.3');
39
+ expect(metadataField.rawValue).to.equal('1.3');
40
+ expect(metadataField.value).to.equal(1.3);
41
+ expect(metadataField.values).to.deep.equal([1.3]);
42
+ });
43
+ it('properly casts values to expected parser type for array values', () => {
44
+ class MockFloatParser {
45
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
+ parseValue(rawValue) {
47
+ return parseFloat(rawValue);
48
+ }
49
+ }
50
+ const parser = new MockFloatParser();
51
+ const metadataField = new MetadataField(parser, ['1.3', '2.4', '4.5']);
52
+ expect(metadataField.rawValue).to.deep.equal(['1.3', '2.4', '4.5']);
53
+ expect(metadataField.value).to.equal(1.3);
54
+ expect(metadataField.values).to.deep.equal([1.3, 2.4, 4.5]);
55
+ });
56
+ it('has no value if a raw value was not passed in', () => {
57
+ class MockFloatParser {
58
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
59
+ parseValue(rawValue) {
60
+ return rawValue;
61
+ }
62
+ }
63
+ const parser = new MockFloatParser();
64
+ const metadataField = new MetadataField(parser);
65
+ expect(metadataField.rawValue).to.equal(undefined);
66
+ expect(metadataField.value).to.equal(undefined);
67
+ expect(metadataField.values).to.deep.equal([]);
68
+ });
69
+ it('does not add value to values array if parsed value is undefined', () => {
70
+ class MockFloatParser {
71
+ parseValue(rawValue) {
72
+ return undefined;
73
+ }
74
+ }
75
+ const parser = new MockFloatParser();
76
+ const metadataField = new MetadataField(parser);
77
+ expect(metadataField.rawValue).to.equal(undefined);
78
+ expect(metadataField.value).to.equal(undefined);
79
+ expect(metadataField.values).to.deep.equal([]);
80
+ });
81
+ it('handles falsy `0` return values properly', () => {
82
+ class MockFloatParser {
83
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
84
+ parseValue(rawValue) {
85
+ return rawValue;
86
+ }
87
+ }
88
+ const parser = new MockFloatParser();
89
+ const metadataField = new MetadataField(parser, 0);
90
+ expect(metadataField.rawValue).to.equal(0);
91
+ expect(metadataField.value).to.equal(0);
92
+ expect(metadataField.values).to.deep.equal([0]);
93
+ });
94
+ it('handles falsy `false` return values properly', () => {
95
+ class MockFloatParser {
96
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
97
+ parseValue(rawValue) {
98
+ return rawValue;
99
+ }
100
+ }
101
+ const parser = new MockFloatParser();
102
+ const metadataField = new MetadataField(parser, false);
103
+ expect(metadataField.rawValue).to.equal(false);
104
+ expect(metadataField.value).to.equal(false);
105
+ expect(metadataField.values).to.deep.equal([false]);
106
+ });
107
+ it('handles falsy empty string return values properly', () => {
108
+ class MockFloatParser {
109
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
110
+ parseValue(rawValue) {
111
+ return rawValue;
112
+ }
113
+ }
114
+ const parser = new MockFloatParser();
115
+ const metadataField = new MetadataField(parser, '');
116
+ expect(metadataField.rawValue).to.equal('');
117
+ expect(metadataField.value).to.equal('');
118
+ expect(metadataField.values).to.deep.equal(['']);
119
+ });
120
+ });
121
+ //# sourceMappingURL=metadata-field.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata-field.test.js","sourceRoot":"","sources":["../../../../test/models/metadata-fields/metadata-field.test.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n FieldParserInterface,\n FieldParserRawValue,\n} from '@internetarchive/field-parsers';\nimport { expect } from '@open-wc/testing';\nimport { MetadataField } from '../../../src/models/metadata-fields/metadata-field';\n\ndescribe('Metadata Field', () => {\n it('can be properly instantiated with single value', () => {\n class MockParser implements FieldParserInterface<string> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n parseValue(rawValue: any): string {\n return rawValue;\n }\n }\n\n const parser = new MockParser();\n const metadataField = new MetadataField(parser, 'foo');\n\n expect(metadataField.rawValue).to.equal('foo');\n expect(metadataField.value).to.equal('foo');\n expect(metadataField.values).to.deep.equal(['foo']);\n });\n\n it('can be properly instantiated with array value', () => {\n class MockParser implements FieldParserInterface<string> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n parseValue(rawValue: any): string {\n return rawValue;\n }\n }\n\n const parser = new MockParser();\n const metadataField = new MetadataField(parser, ['foo', 'bar', 'baz']);\n\n expect(metadataField.rawValue).to.deep.equal(['foo', 'bar', 'baz']);\n expect(metadataField.value).to.equal('foo');\n expect(metadataField.values).to.deep.equal(['foo', 'bar', 'baz']);\n });\n\n it('properly casts values to expected parser type for single values', () => {\n class MockFloatParser implements FieldParserInterface<number> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n parseValue(rawValue: any): number {\n return parseFloat(rawValue);\n }\n }\n\n const parser = new MockFloatParser();\n const metadataField = new MetadataField(parser, '1.3');\n\n expect(metadataField.rawValue).to.equal('1.3');\n expect(metadataField.value).to.equal(1.3);\n expect(metadataField.values).to.deep.equal([1.3]);\n });\n\n it('properly casts values to expected parser type for array values', () => {\n class MockFloatParser implements FieldParserInterface<number> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n parseValue(rawValue: any): number {\n return parseFloat(rawValue);\n }\n }\n\n const parser = new MockFloatParser();\n const metadataField = new MetadataField(parser, ['1.3', '2.4', '4.5']);\n\n expect(metadataField.rawValue).to.deep.equal(['1.3', '2.4', '4.5']);\n expect(metadataField.value).to.equal(1.3);\n expect(metadataField.values).to.deep.equal([1.3, 2.4, 4.5]);\n });\n\n it('has no value if a raw value was not passed in', () => {\n class MockFloatParser implements FieldParserInterface<number> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n parseValue(rawValue: any): number {\n return rawValue;\n }\n }\n\n const parser = new MockFloatParser();\n const metadataField = new MetadataField(parser);\n\n expect(metadataField.rawValue).to.equal(undefined);\n expect(metadataField.value).to.equal(undefined);\n expect(metadataField.values).to.deep.equal([]);\n });\n\n it('does not add value to values array if parsed value is undefined', () => {\n class MockFloatParser implements FieldParserInterface<number> {\n parseValue(rawValue: FieldParserRawValue): number | undefined {\n return undefined;\n }\n }\n\n const parser = new MockFloatParser();\n const metadataField = new MetadataField(parser);\n\n expect(metadataField.rawValue).to.equal(undefined);\n expect(metadataField.value).to.equal(undefined);\n expect(metadataField.values).to.deep.equal([]);\n });\n\n it('handles falsy `0` return values properly', () => {\n class MockFloatParser implements FieldParserInterface<number> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n parseValue(rawValue: any): number {\n return rawValue;\n }\n }\n\n const parser = new MockFloatParser();\n const metadataField = new MetadataField(parser, 0);\n\n expect(metadataField.rawValue).to.equal(0);\n expect(metadataField.value).to.equal(0);\n expect(metadataField.values).to.deep.equal([0]);\n });\n\n it('handles falsy `false` return values properly', () => {\n class MockFloatParser implements FieldParserInterface<number> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n parseValue(rawValue: any): number {\n return rawValue;\n }\n }\n\n const parser = new MockFloatParser();\n const metadataField = new MetadataField(parser, false);\n\n expect(metadataField.rawValue).to.equal(false);\n expect(metadataField.value).to.equal(false);\n expect(metadataField.values).to.deep.equal([false]);\n });\n\n it('handles falsy empty string return values properly', () => {\n class MockFloatParser implements FieldParserInterface<number> {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n parseValue(rawValue: any): number {\n return rawValue;\n }\n }\n\n const parser = new MockFloatParser();\n const metadataField = new MetadataField(parser, '');\n\n expect(metadataField.rawValue).to.equal('');\n expect(metadataField.value).to.equal('');\n expect(metadataField.values).to.deep.equal(['']);\n });\n});\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,10 @@
1
+ import { expect } from '@open-wc/testing';
2
+ import { Metadata } from '../../src/models/metadata';
3
+ describe('Metadata', () => {
4
+ it('properly instantiates metadata with identifier', async () => {
5
+ const json = { identifier: 'foo', collection: 'bar' };
6
+ const metadata = new Metadata(json);
7
+ expect(metadata.identifier).to.equal('foo');
8
+ });
9
+ });
10
+ //# sourceMappingURL=metadata.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.test.js","sourceRoot":"","sources":["../../../test/models/metadata.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAC;AAE1C,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAErD,QAAQ,CAAC,UAAU,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;QAC9D,MAAM,IAAI,GAAG,EAAE,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;QACtD,MAAM,QAAQ,GAAG,IAAI,QAAQ,CAAC,IAAI,CAAC,CAAC;QACpC,MAAM,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["import { expect } from '@open-wc/testing';\n\nimport { Metadata } from '../../src/models/metadata';\n\ndescribe('Metadata', () => {\n it('properly instantiates metadata with identifier', async () => {\n const json = { identifier: 'foo', collection: 'bar' };\n const metadata = new Metadata(json);\n expect(metadata.identifier).to.equal('foo');\n });\n});\n"]}
package/index.ts ADDED
@@ -0,0 +1,32 @@
1
+ export { Metadata } from './src/models/metadata';
2
+ export { File } from './src/models/file';
3
+ export { Review } from './src/models/review';
4
+ export { SpeechMusicASREntry } from './src/models/speech-music-asr-entry';
5
+
6
+ export { DateField } from './src/models/metadata-fields/field-types/date';
7
+
8
+ export { NumberField } from './src/models/metadata-fields/field-types/number';
9
+
10
+ export { StringField } from './src/models/metadata-fields/field-types/string';
11
+
12
+ export { BooleanField } from './src/models/metadata-fields/field-types/boolean';
13
+
14
+ export { ByteField } from './src/models/metadata-fields/field-types/byte';
15
+
16
+ export { DurationField } from './src/models/metadata-fields/field-types/duration';
17
+
18
+ export { PageProgressionField } from './src/models/metadata-fields/field-types/page-progression';
19
+
20
+ export { MediaTypeField } from './src/models/metadata-fields/field-types/mediatype';
21
+
22
+ export {
23
+ MetadataFieldInterface,
24
+ MetadataField,
25
+ } from './src/models/metadata-fields/metadata-field';
26
+
27
+ export { MetadataResponse } from './src/responses/metadata-response';
28
+
29
+ export { DefaultMetadataBackend } from './src/backend/default-metadata-backend';
30
+ export { MetadataServiceInterface } from './src/metadata-service-interface';
31
+ export { MetadataService } from './src/metadata-service';
32
+ export { MetadataServiceError } from './src/metadata-service-error';
package/package.json ADDED
@@ -0,0 +1,77 @@
1
+ {
2
+ "name": "@internetarchive/metadata-service",
3
+ "version": "0.0.0-bc6513-0",
4
+ "description": "A service for fetching metadata about items in the Internet Archive",
5
+ "license": "AGPL-3.0-only",
6
+ "main": "dist/index.js",
7
+ "module": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "scripts": {
13
+ "start": "concurrently --kill-others --names tsc,web-dev-server \"npm run tsc:watch\" \"web-dev-server --app-index demo/index.html --node-resolve --open --watch\"",
14
+ "tsc:watch": "tsc --watch",
15
+ "docs": "typedoc ./index.ts",
16
+ "prepare": "tsc",
17
+ "lint:eslint": "eslint --ext .ts,.html . --ignore-path .gitignore",
18
+ "format:eslint": "eslint --ext .ts,.html . --fix --ignore-path .gitignore",
19
+ "lint:prettier": "prettier \"**/*.js\" \"**/*.ts\" --check --ignore-path .gitignore",
20
+ "format:prettier": "prettier \"**/*.js\" \"**/*.ts\" --write --ignore-path .gitignore",
21
+ "lint": "npm run lint:eslint && npm run lint:prettier",
22
+ "format": "npm run format:eslint && npm run format:prettier",
23
+ "circular": "madge --circular --extensions ts .",
24
+ "test": "tsc && npm run lint && npm run circular && web-test-runner --coverage",
25
+ "test:watch": "web-test-runner --watch",
26
+ "codecov": "npx codecov"
27
+ },
28
+ "devDependencies": {
29
+ "@open-wc/eslint-config": "^4.2.0",
30
+ "@open-wc/testing": "^2.0.0",
31
+ "@types/node": "13.11.1",
32
+ "@typescript-eslint/eslint-plugin": "^4.16.1",
33
+ "@typescript-eslint/parser": "^4.16.1",
34
+ "@web/dev-server": "^0.0.12",
35
+ "@web/test-runner": "^0.13.23",
36
+ "concurrently": "^5.1.0",
37
+ "eslint": "^7.21.0",
38
+ "eslint-config-prettier": "^6.11.0",
39
+ "husky": "^1.0.0",
40
+ "lint-staged": "^10.0.0",
41
+ "lit-element": "^2.4.0",
42
+ "lit-html": "^1.3.0",
43
+ "madge": "^3.12.0",
44
+ "prettier": "^2.0.4",
45
+ "tslib": "^1.14.1",
46
+ "typedoc": "^0.20.29",
47
+ "typescript": "^4.2.2"
48
+ },
49
+ "eslintConfig": {
50
+ "extends": [
51
+ "@open-wc/eslint-config",
52
+ "eslint-config-prettier"
53
+ ]
54
+ },
55
+ "prettier": {
56
+ "singleQuote": true,
57
+ "arrowParens": "avoid"
58
+ },
59
+ "husky": {
60
+ "hooks": {
61
+ "pre-commit": "lint-staged"
62
+ }
63
+ },
64
+ "lint-staged": {
65
+ "*.ts": [
66
+ "eslint --fix",
67
+ "prettier --write",
68
+ "git add"
69
+ ]
70
+ },
71
+ "dependencies": {
72
+ "@internetarchive/field-parsers": "^0.1.3",
73
+ "@internetarchive/result-type": "^0.0.1",
74
+ "decorator-cache-getter": "^1.0.0",
75
+ "typescript-memoize": "^1.1.0"
76
+ }
77
+ }
@@ -0,0 +1,141 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import type { Result } from '@internetarchive/result-type';
3
+ import { MetadataBackendInterface } from './metadata-backend-interface';
4
+ import {
5
+ MetadataServiceError,
6
+ MetadataServiceErrorType,
7
+ } from '../metadata-service-error';
8
+
9
+ /**
10
+ * The DefaultSearchBackend performs a `window.fetch` request to archive.org
11
+ */
12
+ export class DefaultMetadataBackend implements MetadataBackendInterface {
13
+ private baseUrl: string;
14
+
15
+ private includeCredentials: boolean;
16
+
17
+ private requestScope?: string;
18
+
19
+ constructor(options?: {
20
+ baseUrl?: string;
21
+ includeCredentials?: boolean;
22
+ scope?: string;
23
+ }) {
24
+ this.baseUrl = options?.baseUrl ?? 'archive.org';
25
+
26
+ if (options?.includeCredentials !== undefined) {
27
+ this.includeCredentials = options.includeCredentials;
28
+ } else {
29
+ // include credentials if the request is coming from an archive.org domain
30
+ // since credentialed requests are only allowed from archive.org domains
31
+ // due to CORS restrictions, see
32
+ // https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS/Errors/CORSNotSupportingCredentials
33
+ this.includeCredentials =
34
+ window.location.href.match(/^https?:\/\/.*archive\.org(:[0-9]+)?/) !==
35
+ null;
36
+ }
37
+
38
+ if (options?.scope !== undefined) {
39
+ this.requestScope = options.scope;
40
+ } else {
41
+ const currentUrl = new URL(window.location.href);
42
+ const scope = currentUrl.searchParams.get('scope');
43
+ if (scope) {
44
+ this.requestScope = scope;
45
+ }
46
+ }
47
+ }
48
+
49
+ /** @inheritdoc */
50
+ async fetchMetadata(
51
+ identifier: string,
52
+ keypath?: string
53
+ ): Promise<Result<any, MetadataServiceError>> {
54
+ const path = keypath ? `/${keypath}` : '';
55
+ const url = `https://${this.baseUrl}/metadata/${identifier}${path}`;
56
+ // the metadata endpoint doesn't currently support credentialed requests
57
+ // so don't include credentials until that is fixed
58
+ return this.fetchUrl(url, {
59
+ requestOptions: {
60
+ credentials: 'omit',
61
+ },
62
+ });
63
+ }
64
+
65
+ /**
66
+ * Fires a request to the URL (with this backend's options applied) and
67
+ * asynchronously returns a Result object containing either the raw response
68
+ * JSON or a MetadataServiceError.
69
+ */
70
+ protected async fetchUrl(
71
+ url: string,
72
+ options?: {
73
+ requestOptions?: RequestInit;
74
+ }
75
+ ): Promise<Result<any, MetadataServiceError>> {
76
+ const finalUrl = new URL(url);
77
+ if (this.requestScope) {
78
+ finalUrl.searchParams.set('scope', this.requestScope);
79
+ }
80
+
81
+ let response: Response;
82
+ // first try the fetch and return a networkError if it fails
83
+ try {
84
+ const fetchOptions = options?.requestOptions ?? {
85
+ credentials: this.includeCredentials ? 'include' : 'same-origin',
86
+ };
87
+ response = await fetch(finalUrl.href, fetchOptions);
88
+ } catch (err) {
89
+ const message =
90
+ err instanceof Error
91
+ ? err.message
92
+ : typeof err === 'string'
93
+ ? err
94
+ : 'Unknown error';
95
+ return this.getErrorResult(
96
+ MetadataServiceErrorType.networkError,
97
+ message
98
+ );
99
+ }
100
+
101
+ // then try json decoding and return a decodingError if it fails
102
+ try {
103
+ const json = await response.json();
104
+ // the advanced search endpoint doesn't return an HTTP Error 400
105
+ // and instead returns an HTTP 200 with an `error` key in the payload
106
+ const error = json['error'];
107
+ if (error) {
108
+ const forensics = json['forensics'];
109
+ return this.getErrorResult(
110
+ MetadataServiceErrorType.searchEngineError,
111
+ error,
112
+ forensics
113
+ );
114
+ } else {
115
+ // success
116
+ return { success: json };
117
+ }
118
+ } catch (err) {
119
+ const message =
120
+ err instanceof Error
121
+ ? err.message
122
+ : typeof err === 'string'
123
+ ? err
124
+ : 'Unknown error';
125
+ return this.getErrorResult(
126
+ MetadataServiceErrorType.decodingError,
127
+ message
128
+ );
129
+ }
130
+ }
131
+
132
+ private getErrorResult(
133
+ errorType: MetadataServiceErrorType,
134
+ message?: string,
135
+ details?: any
136
+ ): Result<any, MetadataServiceError> {
137
+ const error = new MetadataServiceError(errorType, message, details);
138
+ const result = { error };
139
+ return result;
140
+ }
141
+ }
@@ -0,0 +1,25 @@
1
+ /* eslint-disable @typescript-eslint/no-explicit-any */
2
+ import type { Result } from '@internetarchive/result-type';
3
+ import { MetadataServiceError } from '../metadata-service-error';
4
+
5
+ /**
6
+ * An interface to provide the network layer to the `MetadataService`.
7
+ *
8
+ * Objects implementing this interface are responsible for making calls to the Internet Archive
9
+ * `metadata` endpoint or otherwise providing a similar reponse in JSON format.
10
+ *
11
+ * @export
12
+ * @interface MetadataBackendInterface
13
+ */
14
+ export interface MetadataBackendInterface {
15
+ /**
16
+ * Fetch metadata for a single item with an optional keypath
17
+ *
18
+ * @param identifier
19
+ * @param keypath
20
+ */
21
+ fetchMetadata(
22
+ identifier: string,
23
+ keypath?: string
24
+ ): Promise<Result<any, MetadataServiceError>>;
25
+ }
@@ -0,0 +1,21 @@
1
+ /* eslint-disable @typescript-eslint/explicit-module-boundary-types */
2
+ /* eslint-disable @typescript-eslint/no-explicit-any */
3
+ export enum MetadataServiceErrorType {
4
+ networkError = 'MetadataService.NetworkError',
5
+ itemNotFound = 'MetadataService.ItemNotFound',
6
+ decodingError = 'MetadataService.DecodingError',
7
+ searchEngineError = 'MetadataService.SearchEngineError',
8
+ }
9
+
10
+ export class MetadataServiceError extends Error {
11
+ type: MetadataServiceErrorType;
12
+
13
+ details?: any;
14
+
15
+ constructor(type: MetadataServiceErrorType, message?: string, details?: any) {
16
+ super(message);
17
+ this.name = type;
18
+ this.type = type;
19
+ this.details = details;
20
+ }
21
+ }
@@ -0,0 +1,51 @@
1
+ import type { MetadataResponse } from './responses/metadata-response';
2
+ import type { Result } from '@internetarchive/result-type';
3
+ import type { MetadataServiceError } from './metadata-service-error';
4
+
5
+ export interface MetadataServiceInterface {
6
+ /**
7
+ * Fetch metadata for a given identifier
8
+ *
9
+ * @param {string} identifier
10
+ * @returns {Promise<Result<MetadataResponse, MetadataServiceError>>}
11
+ */
12
+ fetchMetadata(
13
+ identifier: string
14
+ ): Promise<Result<MetadataResponse, MetadataServiceError>>;
15
+
16
+ /**
17
+ * Fetch the metadata value for a given identifier and keypath
18
+ *
19
+ * The response from this request can take any form, object, array, string, etc.
20
+ * depending on the query. You can provide return typing in the response by
21
+ * specifying the type. Note, there is no automatic type conversion since it can be anything.
22
+ *
23
+ * For example:
24
+ *
25
+ * ```ts
26
+ * const collection = await searchService.fetchMetadataValue<string>('goody', 'metadata/collection/0');
27
+ * console.debug('collection:', collection); => 'Goody Collection'
28
+ *
29
+ * const files_count = await searchService.fetchMetadataValue<number>('goody', 'files_count');
30
+ * console.debug('files_count:', files_count); => 12
31
+ * ```
32
+ *
33
+ * Keypath examples:
34
+ *
35
+ * /metadata/:identifier/metadata // returns the entire metadata object
36
+ * /metadata/:identifier/server // returns the server for the given identifier
37
+ * /metadata/:identifier/files_count
38
+ * /metadata/:identifier/files?start=1&count=2 // query for files
39
+ * /metadata/:identifier/metadata/collection // all collections
40
+ * /metadata/:identifier/metadata/collection/0 // first collection
41
+ * /metadata/:identifier/metadata/title
42
+ * /metadata/:identifier/files/0/name // first file name
43
+ *
44
+ * @param identifier
45
+ * @param keypath
46
+ */
47
+ fetchMetadataValue<T>(
48
+ identifier: string,
49
+ keypath: string
50
+ ): Promise<Result<T, MetadataServiceError>>;
51
+ }
@@ -0,0 +1,63 @@
1
+ import { MetadataResponse } from './responses/metadata-response';
2
+ import {
3
+ MetadataServiceError,
4
+ MetadataServiceErrorType,
5
+ } from './metadata-service-error';
6
+ import type { MetadataServiceInterface } from './metadata-service-interface';
7
+ import type { Result } from '@internetarchive/result-type';
8
+ import { DefaultMetadataBackend } from './backend/default-metadata-backend';
9
+ import { MetadataBackendInterface } from './backend/metadata-backend-interface';
10
+
11
+ /**
12
+ * The Metadata Service is responsible for taking the raw response provided by
13
+ * the backend and modeling it as a `MetadataResponse` object.
14
+ */
15
+ export class MetadataService implements MetadataServiceInterface {
16
+ public static default: MetadataServiceInterface = new MetadataService(
17
+ new DefaultMetadataBackend()
18
+ );
19
+
20
+ private backend: MetadataBackendInterface;
21
+
22
+ constructor(backend: MetadataBackendInterface) {
23
+ this.backend = backend;
24
+ }
25
+
26
+ /** @inheritdoc */
27
+ async fetchMetadata(
28
+ identifier: string
29
+ ): Promise<Result<MetadataResponse, MetadataServiceError>> {
30
+ const rawResponse = await this.backend.fetchMetadata(identifier);
31
+ if (rawResponse.error) {
32
+ return rawResponse;
33
+ }
34
+
35
+ if (rawResponse.success?.metadata === undefined) {
36
+ return {
37
+ error: new MetadataServiceError(MetadataServiceErrorType.itemNotFound),
38
+ };
39
+ }
40
+
41
+ const modeledResponse = new MetadataResponse(rawResponse.success);
42
+ return { success: modeledResponse };
43
+ }
44
+
45
+ /** @inheritdoc */
46
+ async fetchMetadataValue<T>(
47
+ identifier: string,
48
+ keypath: string
49
+ ): Promise<Result<T, MetadataServiceError>> {
50
+ const result = await this.backend.fetchMetadata(identifier, keypath);
51
+ if (result.error) {
52
+ return result;
53
+ }
54
+
55
+ if (result.success?.result === undefined) {
56
+ return {
57
+ error: new MetadataServiceError(MetadataServiceErrorType.itemNotFound),
58
+ };
59
+ }
60
+
61
+ return { success: result.success.result };
62
+ }
63
+ }