ogm-viewer 0.4.0 → 0.5.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/dist/cjs/{lerc-CT61sKYw.js → lerc-Df0hDWkS.js} +6 -6
- package/dist/cjs/loader.cjs.js +1 -1
- package/dist/cjs/{ogm-attributes.ogm-image.ogm-map.ogm-menubar.ogm-metadata.ogm-previews.ogm-settings.ogm-sidebar.ogm-viewer-DKxSOrMP.js → ogm-alerts.ogm-attributes.ogm-image.ogm-map.ogm-menubar.ogm-metadata.ogm-preview.ogm-previews.ogm-settings.ogm-sidebar.ogm-viewer-DMEfXquS.js} +446 -80
- package/dist/cjs/ogm-alerts_11.cjs.entry.js +18 -0
- package/dist/cjs/ogm-viewer.cjs.js +1 -1
- package/dist/collection/assets/icons/exclamation-triangle-fill.svg +3 -0
- package/dist/collection/collection-manifest.json +2 -0
- package/dist/collection/components/ogm-alerts/ogm-alerts.css +51 -0
- package/dist/collection/components/ogm-alerts/ogm-alerts.js +71 -0
- package/dist/collection/components/ogm-alerts/ogm-alerts.test.js +26 -0
- package/dist/collection/components/ogm-image/ogm-image.js +57 -5
- package/dist/collection/components/ogm-map/ogm-map.css +34 -40
- package/dist/collection/components/ogm-map/ogm-map.js +76 -22
- package/dist/collection/components/ogm-menubar/ogm-menubar.test.js +21 -0
- package/dist/collection/components/ogm-preview/ogm-preview.css +16 -0
- package/dist/collection/components/ogm-preview/ogm-preview.js +144 -0
- package/dist/collection/components/ogm-preview/ogm-preview.test.js +37 -0
- package/dist/collection/components/ogm-previews/ogm-previews.css +3 -2
- package/dist/collection/components/ogm-previews/ogm-previews.js +7 -17
- package/dist/collection/components/ogm-previews/ogm-previews.test.js +83 -0
- package/dist/collection/components/ogm-settings/ogm-settings.js +1 -1
- package/dist/collection/components/ogm-sidebar/ogm-sidebar.js +2 -2
- package/dist/collection/components/ogm-viewer/ogm-viewer.js +36 -15
- package/dist/collection/lib/errors.js +137 -0
- package/dist/collection/lib/errors.test.js +96 -0
- package/dist/collection/lib/sources/geojson.js +2 -1
- package/dist/collection/lib/sources/iiif-manifest.js +2 -3
- package/dist/collection/lib/sources/iiif-manifest.test.js +4 -4
- package/dist/esm/{lerc-Bz409klg.js → lerc-CB_Jcu33.js} +1 -1
- package/dist/esm/loader.js +1 -1
- package/dist/esm/{ogm-attributes.ogm-image.ogm-map.ogm-menubar.ogm-metadata.ogm-previews.ogm-settings.ogm-sidebar.ogm-viewer-C9zV3wMe.js → ogm-alerts.ogm-attributes.ogm-image.ogm-map.ogm-menubar.ogm-metadata.ogm-preview.ogm-previews.ogm-settings.ogm-sidebar.ogm-viewer-Dlc6USWB.js} +446 -82
- package/dist/esm/ogm-alerts_11.entry.js +2 -0
- package/dist/esm/ogm-viewer.js +1 -1
- package/dist/ogm-viewer/assets/icons/exclamation-triangle-fill.svg +3 -0
- package/dist/ogm-viewer/ogm-viewer.esm.js +1 -1
- package/dist/ogm-viewer/p-7936ccc9.entry.js +1 -0
- package/dist/ogm-viewer/p-CWtUrqsM.js +2822 -0
- package/dist/ogm-viewer/{p-BrovdLv6.js → p-DqRDI3AC.js} +1 -1
- package/dist/types/components/ogm-alerts/ogm-alerts.d.ts +6 -0
- package/dist/types/components/ogm-alerts/ogm-alerts.test.d.ts +1 -0
- package/dist/types/components/ogm-image/ogm-image.d.ts +5 -0
- package/dist/types/components/ogm-map/ogm-map.d.ts +6 -0
- package/dist/types/components/ogm-preview/ogm-preview.d.ts +13 -0
- package/dist/types/components/ogm-preview/ogm-preview.test.d.ts +1 -0
- package/dist/types/components/ogm-previews/ogm-previews.d.ts +1 -5
- package/dist/types/components/ogm-previews/ogm-previews.test.d.ts +1 -0
- package/dist/types/components/ogm-viewer/ogm-viewer.d.ts +7 -2
- package/dist/types/components.d.ts +52 -0
- package/dist/types/lib/errors.d.ts +13 -0
- package/dist/types/lib/errors.test.d.ts +1 -0
- package/dist/types/lib/sources/source.d.ts +1 -1
- package/package.json +1 -1
- package/dist/cjs/ogm-attributes_9.cjs.entry.js +0 -16
- package/dist/esm/ogm-attributes_9.entry.js +0 -2
- package/dist/ogm-viewer/p-86b8fafc.entry.js +0 -1
- package/dist/ogm-viewer/p-CpCT3zlD.js +0 -2750
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { describe, it, expect, vi, afterEach } from "@stencil/vitest";
|
|
2
|
+
import { HttpError, PreviewError, fetchOrThrow, recordError, referenceError } from "./errors";
|
|
3
|
+
describe('errors', () => {
|
|
4
|
+
afterEach(() => vi.restoreAllMocks());
|
|
5
|
+
describe('HttpError', () => {
|
|
6
|
+
it('captures the status, statusText, and url', () => {
|
|
7
|
+
const error = new HttpError('http://example.com/x', 404, 'Not Found');
|
|
8
|
+
expect(error).toBeInstanceOf(Error);
|
|
9
|
+
expect(error.name).toBe('HttpError');
|
|
10
|
+
expect(error.status).toBe(404);
|
|
11
|
+
expect(error.statusText).toBe('Not Found');
|
|
12
|
+
expect(error.url).toBe('http://example.com/x');
|
|
13
|
+
expect(error.message).toContain('404');
|
|
14
|
+
});
|
|
15
|
+
});
|
|
16
|
+
describe('fetchOrThrow', () => {
|
|
17
|
+
it('returns the response when the status is ok', async () => {
|
|
18
|
+
const response = new Response('{}', { status: 200 });
|
|
19
|
+
vi.spyOn(global, 'fetch').mockResolvedValueOnce(response);
|
|
20
|
+
await expect(fetchOrThrow('http://example.com/ok')).resolves.toBe(response);
|
|
21
|
+
});
|
|
22
|
+
it('throws an HttpError when the status is not ok', async () => {
|
|
23
|
+
vi.spyOn(global, 'fetch').mockResolvedValueOnce(new Response('nope', { status: 500, statusText: 'Server Error' }));
|
|
24
|
+
await expect(fetchOrThrow('http://example.com/bad')).rejects.toMatchObject({ name: 'HttpError', status: 500, url: 'http://example.com/bad' });
|
|
25
|
+
});
|
|
26
|
+
it('propagates a network error (TypeError) unchanged', async () => {
|
|
27
|
+
const networkError = new TypeError('Failed to fetch');
|
|
28
|
+
vi.spyOn(global, 'fetch').mockRejectedValueOnce(networkError);
|
|
29
|
+
await expect(fetchOrThrow('http://example.com/cors')).rejects.toBe(networkError);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
describe('recordError', () => {
|
|
33
|
+
it('always returns a PreviewError (and therefore an Error), carrying a usable message', () => {
|
|
34
|
+
const error = recordError(new Error('boom'), 'http://example.com/r');
|
|
35
|
+
expect(error).toBeInstanceOf(PreviewError);
|
|
36
|
+
expect(error).toBeInstanceOf(Error);
|
|
37
|
+
// Guards the class-field pitfall where redeclaring `message` blanks the value super() sets.
|
|
38
|
+
expect(error.message).toBeTruthy();
|
|
39
|
+
});
|
|
40
|
+
it('classifies an HttpError as an HTTP load error and includes the status', () => {
|
|
41
|
+
const error = recordError(new HttpError('http://example.com/r', 403, 'Forbidden'), 'http://example.com/r');
|
|
42
|
+
expect(error.title).toBe("This record couldn't be loaded");
|
|
43
|
+
expect(error.message).toContain('403');
|
|
44
|
+
expect(error.url).toBe('http://example.com/r');
|
|
45
|
+
});
|
|
46
|
+
it('classifies a SyntaxError as a parse error', () => {
|
|
47
|
+
const error = recordError(new SyntaxError('Unexpected token < in JSON'), 'http://example.com/r');
|
|
48
|
+
expect(error.title).toBe("This record couldn't be parsed");
|
|
49
|
+
expect(error.message).toMatch(/JSON/);
|
|
50
|
+
});
|
|
51
|
+
it('classifies a fetch TypeError as a network/CORS error', () => {
|
|
52
|
+
const error = recordError(new TypeError('Failed to fetch'), 'http://example.com/r');
|
|
53
|
+
expect(error.title).toBe("This record couldn't be reached");
|
|
54
|
+
expect(error.message).toMatch(/CORS/);
|
|
55
|
+
});
|
|
56
|
+
it('falls back to a read error for other failures (e.g. an unsupported metadata version)', () => {
|
|
57
|
+
const error = recordError(new Error('Unsupported metadata version: GeoBlacklight'), 'http://example.com/r');
|
|
58
|
+
expect(error.title).toBe("This record couldn't be read");
|
|
59
|
+
expect(error.message).toContain('Unsupported metadata version');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
62
|
+
describe('referenceError', () => {
|
|
63
|
+
it('names the source in the title and keeps the url', () => {
|
|
64
|
+
const error = referenceError(new TypeError('Failed to fetch'), 'GeoJSON', 'http://example.com/data.json');
|
|
65
|
+
expect(error).toBeInstanceOf(PreviewError);
|
|
66
|
+
expect(error.title).toContain('GeoJSON');
|
|
67
|
+
expect(error.url).toBe('http://example.com/data.json');
|
|
68
|
+
});
|
|
69
|
+
it('classifies an HttpError as an HTTP load error', () => {
|
|
70
|
+
const error = referenceError(new HttpError('http://example.com/wms', 502, 'Bad Gateway'), 'WMS');
|
|
71
|
+
expect(error.title).toBe("The WMS preview couldn't be loaded");
|
|
72
|
+
expect(error.message).toContain('502');
|
|
73
|
+
});
|
|
74
|
+
it('reads a numeric status off a library error object (e.g. MapLibre AjaxError)', () => {
|
|
75
|
+
const error = referenceError({ status: 404, message: 'not found' }, 'XYZ tiles');
|
|
76
|
+
expect(error.title).toBe("The XYZ tiles preview couldn't be loaded");
|
|
77
|
+
expect(error.message).toContain('404');
|
|
78
|
+
});
|
|
79
|
+
it('classifies a fetch TypeError as a network/CORS error', () => {
|
|
80
|
+
const error = referenceError(new TypeError('Failed to fetch'), 'IIIF Image');
|
|
81
|
+
expect(error.title).toBe("The IIIF Image preview couldn't be reached");
|
|
82
|
+
expect(error.message).toMatch(/CORS/);
|
|
83
|
+
});
|
|
84
|
+
it('falls back to a read error for a non-HTTP, non-network failure', () => {
|
|
85
|
+
const error = referenceError(new Error('open-failed'), 'IIIF Image');
|
|
86
|
+
expect(error.title).toBe("The IIIF Image preview couldn't be read");
|
|
87
|
+
expect(error.message).toContain('open-failed');
|
|
88
|
+
});
|
|
89
|
+
it('treats a status-0 error (MapLibre CORS/network block) as a network error, not "HTTP 0"', () => {
|
|
90
|
+
const error = referenceError({ status: 0, message: 'Failed to fetch' }, 'Web Map Service (WMS)');
|
|
91
|
+
expect(error.title).toBe("The Web Map Service (WMS) preview couldn't be reached");
|
|
92
|
+
expect(error.message).toMatch(/CORS/);
|
|
93
|
+
expect(error.message).not.toContain('HTTP 0');
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
});
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import geojsonExtent from "@mapbox/geojson-extent";
|
|
2
2
|
import VectorSource from "./vector";
|
|
3
|
+
import { fetchOrThrow } from "../errors";
|
|
3
4
|
export default class GeoJSONSource extends VectorSource {
|
|
4
5
|
// Data parsed from GeoJSON document
|
|
5
6
|
data;
|
|
6
7
|
// Fetch and memoize data
|
|
7
8
|
async getData() {
|
|
8
9
|
if (!this.data) {
|
|
9
|
-
const resp = await
|
|
10
|
+
const resp = await fetchOrThrow(this.url);
|
|
10
11
|
this.data = await resp.json();
|
|
11
12
|
}
|
|
12
13
|
return this.data;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import IIIFSource from "./iiif";
|
|
2
|
+
import { fetchOrThrow } from "../errors";
|
|
2
3
|
// A manifest containing multiple IIIF image URLs for preview
|
|
3
4
|
export default class IIIFManifestSource extends IIIFSource {
|
|
4
5
|
// The parsed manifest contents
|
|
@@ -21,9 +22,7 @@ export default class IIIFManifestSource extends IIIFSource {
|
|
|
21
22
|
async fetchManifest() {
|
|
22
23
|
if (this.manifest)
|
|
23
24
|
return this.manifest;
|
|
24
|
-
const response = await
|
|
25
|
-
if (!response.ok)
|
|
26
|
-
throw new Error(`Unexpected response fetching IIIF manifest: ${response.statusText}`);
|
|
25
|
+
const response = await fetchOrThrow(this.url);
|
|
27
26
|
const manifest = await response.json();
|
|
28
27
|
this.manifest = manifest;
|
|
29
28
|
return manifest;
|
|
@@ -66,13 +66,13 @@ describe('IIIFManifestSource', () => {
|
|
|
66
66
|
it('should fetch and extract image URLs from a IIIF v2 manifest', async () => {
|
|
67
67
|
const fetchSpy = vi.spyOn(global, 'fetch').mockResolvedValueOnce(new Response(JSON.stringify(v2Manifest)));
|
|
68
68
|
const urls = await createSource().getIIIFImageUrls();
|
|
69
|
-
expect(fetchSpy).
|
|
69
|
+
expect(fetchSpy.mock.calls[0][0]).toBe(MANIFEST_URL);
|
|
70
70
|
expect(urls).toEqual(['http://example.com/image1/info.json']);
|
|
71
71
|
});
|
|
72
72
|
it('should fetch and extract image URLs from a IIIF v3 manifest', async () => {
|
|
73
73
|
const fetchSpy = vi.spyOn(global, 'fetch').mockResolvedValueOnce(new Response(JSON.stringify(v3Manifest)));
|
|
74
74
|
const urls = await createSource().getIIIFImageUrls();
|
|
75
|
-
expect(fetchSpy).
|
|
75
|
+
expect(fetchSpy.mock.calls[0][0]).toBe(MANIFEST_URL);
|
|
76
76
|
expect(urls).toEqual(['http://example.com/image1/info.json']);
|
|
77
77
|
});
|
|
78
78
|
it('should not fetch the manifest again if already cached', async () => {
|
|
@@ -94,9 +94,9 @@ describe('IIIFManifestSource', () => {
|
|
|
94
94
|
vi.spyOn(global, 'fetch').mockRejectedValueOnce(new Error('Network error'));
|
|
95
95
|
await expect(createSource().getIIIFImageUrls()).rejects.toThrow('Network error');
|
|
96
96
|
});
|
|
97
|
-
it('should throw if the manifest response is not ok', async () => {
|
|
97
|
+
it('should throw an HttpError if the manifest response is not ok', async () => {
|
|
98
98
|
vi.spyOn(global, 'fetch').mockResolvedValueOnce(new Response('Not found', { status: 404, statusText: 'Not Found' }));
|
|
99
|
-
await expect(createSource().getIIIFImageUrls()).rejects.
|
|
99
|
+
await expect(createSource().getIIIFImageUrls()).rejects.toMatchObject({ name: 'HttpError', status: 404 });
|
|
100
100
|
});
|
|
101
101
|
it('should throw if the manifest is not valid JSON', async () => {
|
|
102
102
|
vi.spyOn(global, 'fetch').mockResolvedValueOnce(new Response('invalid json'));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { i as inflate_1 } from './pako.esm-KbdoS3Oq.js';
|
|
2
|
-
import { g as getDefaultExportFromCjs, L as LercAddCompression, a as LercParameters } from './ogm-attributes.ogm-image.ogm-map.ogm-menubar.ogm-metadata.ogm-previews.ogm-settings.ogm-sidebar.ogm-viewer-
|
|
2
|
+
import { g as getDefaultExportFromCjs, L as LercAddCompression, a as LercParameters } from './ogm-alerts.ogm-attributes.ogm-image.ogm-map.ogm-menubar.ogm-metadata.ogm-preview.ogm-previews.ogm-settings.ogm-sidebar.ogm-viewer-Dlc6USWB.js';
|
|
3
3
|
import { B as BaseDecoder } from './basedecoder-BK531nfi.js';
|
|
4
4
|
import './index-DTLAlWKQ.js';
|
|
5
5
|
|
package/dist/esm/loader.js
CHANGED
|
@@ -5,7 +5,7 @@ import { g as globalScripts } from './app-globals-DQuL1Twl.js';
|
|
|
5
5
|
const defineCustomElements = async (win, options) => {
|
|
6
6
|
if (typeof window === 'undefined') return undefined;
|
|
7
7
|
await globalScripts();
|
|
8
|
-
return bootstrapLazy([["ogm-
|
|
8
|
+
return bootstrapLazy([["ogm-alerts_11",[[513,"ogm-viewer",{"recordUrl":[1,"record-url"],"theme":[1],"record":[32],"error":[32],"previewOpacity":[32],"sidebarOpen":[32],"loading":[32],"loadRecord":[64]},[[0,"sidebarToggled","toggleSidebar"],[0,"opacityChange","adjustPreviewOpacity"],[0,"mapLoading","setLoadingStarted"],[0,"imageLoading","setLoadingStarted"],[0,"mapIdle","setLoadingFinished"],[0,"imageLoaded","setLoadingFinished"]],{"recordUrl":[{"updateRecord":0}],"record":[{"resetLoading":0}]}],[513,"ogm-previews",{"theme":[1],"record":[16],"previewOpacity":[2,"preview-opacity"],"sidebarPadding":[2,"sidebar-padding"],"sources":[32]},null,{"record":[{"getSources":0}]}],[513,"ogm-sidebar",{"record":[16],"theme":[1],"open":[4]}],[513,"ogm-menubar",{"record":[16],"theme":[1],"loading":[4]}],[513,"ogm-preview",{"theme":[1],"source":[16],"previewOpacity":[2,"preview-opacity"],"sidebarPadding":[2,"sidebar-padding"],"error":[32]},[[0,"previewError","handlePreviewError"]],{"source":[{"resetError":0}]}],[513,"ogm-metadata",{"record":[16],"theme":[1],"fieldNames":[16],"filteredRecord":[32]}],[513,"ogm-settings",{"record":[16]},[[0,"input","handleOpacityChange"]]],[513,"ogm-map",{"source":[16],"theme":[1],"padding":[2],"easeMapTo":[64]},[[16,"featureSelected","handleFeatureSelected"]],{"source":[{"previewSource":0}],"padding":[{"onPaddingChange":0}]}],[513,"ogm-alerts",{"theme":[1],"error":[16]}],[513,"ogm-image",{"source":[16],"theme":[1],"padding":[2]},null,{"source":[{"onSourceChange":0}],"padding":[{"onPaddingChange":0}]}],[513,"ogm-attributes",{"features":[16],"currentIndex":[32]},null,{"features":[{"onFeaturesChange":0}],"currentIndex":[{"onCurrentIndexChange":0}]}]]]], options);
|
|
9
9
|
};
|
|
10
10
|
|
|
11
11
|
export { defineCustomElements };
|