@allmaps/iiif-parser
Version:
Allmaps IIIF parser
83 lines (82 loc) • 2.91 kB
JavaScript
// Image API 2.1.1
// https://iiif.io/api/image/2.1/#image-information
import { z } from 'zod';
import { TilesetSchema, SizeSchema } from './shared.js';
// The following compliance levels URIs are valid in the IIIF Image API 2.1:
// - "http://iiif.io/api/image/2/level0.json"
// - "http://iiif.io/api/image/2/level1.json"
// - "http://iiif.io/api/image/2/level2.json"
//
// However, many IIIF servers use incorrect profile URIs, like these:
// - "http://iiif.io/api/image/2/level0"
// - "https://iiif.io/api/image/2/level0"
// - "http://iiif.io/api/image/2/profiles/level0.json"
//
// This happens very often - @allmaps/iiif-parser should parse them regardless.
// To do this, we use this regular expression:
export const image2ProfileUriRegex = /^https?:\/\/iiif.io\/api\/image\/2.*level(?<level>[012])(.json)?$/;
export const Image2ProfileUri = z.string().regex(image2ProfileUriRegex);
export const Image2ProfileDescriptionSchema = z.object({
formats: z.string().array().optional(),
maxArea: z.number().int().optional(),
maxHeight: z.number().int().optional(),
maxWidth: z.number().int().optional(),
qualities: z.string().array().optional(),
supports: z.string().array().optional()
});
const ValidImage2ProfileArrayItemSchema = z.union([
Image2ProfileUri,
Image2ProfileDescriptionSchema
]);
function isValidImage2ProfileArrayItem(item) {
return item !== undefined;
}
export const Image2ProfileSchema = z
.union([
Image2ProfileUri,
z.array(z
.union([
ValidImage2ProfileArrayItemSchema,
// Catchall for incorrect profiles
z.unknown()
])
.transform((val) => {
const { success, data } = ValidImage2ProfileArrayItemSchema.safeParse(val);
if (success) {
return data;
}
}))
])
.transform((val) => {
if (val && Array.isArray(val)) {
const firstProfile = val[0];
if (typeof firstProfile !== 'string') {
throw new Error('First profile must be a string');
}
return [
firstProfile,
...val.slice(1).filter(isValidImage2ProfileArrayItem)
];
}
return val;
});
export const Image2ContextString = 'http://iiif.io/api/image/2/context.json';
export const Image2Context = z.union([
z.literal(Image2ContextString),
// Invalid, but used by https://iiif.archivelab.org
z.literal('https://iiif.io/api/image/2/context.json'),
z.string().url()
]);
export const Image2Schema = z.object({
'@id': z.string().url(),
'@type': z
.union([z.literal('iiif:Image'), z.literal('ImageService2')])
.optional(),
'@context': Image2Context,
protocol: z.literal('http://iiif.io/api/image'),
width: z.number().int(),
height: z.number().int(),
profile: Image2ProfileSchema,
sizes: SizeSchema.array().optional(),
tiles: TilesetSchema.array().optional()
});