Skip to content

feat: IIIF manifest metadata resolver and getMapMeta iiif - #147

Merged
kochizufan merged 6 commits into
train/2026-12from
dec26/m13-t3-iiif-metadata
Oct 28, 2026
Merged

kochizufan merged 6 commits into
train/2026-12from
dec26/m13-t3-iiif-metadata

Conversation

@kochizufan

@kochizufan kochizufan commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Summary

  • src/iiif/manifest.ts (new): pure functions to read a IIIF Presentation 2/3 manifest without fetching it and without touching the DOM: listIiifCanvases, proposeIiifBindings, resolveIiifMetadata, compareIiifCopy, iiifRightsToLicense, the IIIF_BINDABLE_KEYS table (metadata key → IIIF property) and IIIF_METADATA_DISPLAY_DEFAULT ("hidden"). It is not exported from src/index; authoring tools import it from @maplat/core/src/iiif/manifest.
  • sanitizeHtml(dirty, { profile: "iiif" }) and sanitizeIiifHtml: a narrower allowlist kept in sanitize-allowlist.json (a b br i p small span sub sup, href only, http(s) links get target="_blank" and rel="noopener noreferrer"). sanitizeIiifHtml needs window (DOMPurify), so call it in the browser / renderer. The default sanitizeHtml behaviour is unchanged. @maplat/core/common exports sanitizeIiifHtml, the MapMetaIiif type and the isIiifMetadataEntry guard.
  • getMapMeta returns an optional iiif entry (manifest, canvas, image, copiedAt, display, metadata) when the map data has a iiif section. display comes from the app setting iiifMetadata ("all" / "hidden", default "hidden"). proposeIiifBindings proposes only title, attr, license and description, and only when the manifest has the property. Malformed copiedMetadata entries are dropped with one warning per map. The viewer never fetches the manifest.
  • Demo (Niigata): copied NDL metadata, the full table vs. the default hidden display, a try-out of the resolver / comparison functions, and sanitizing of malicious values. README (en/ja), API docs and CHANGELOG updated.

Test plan

  • pnpm run typecheck
  • pnpm exec vitest run (51 files, 972 passed, 1 skipped, after rebasing onto the current base)
  • node_modules/.bin/eslint src spec (0 errors)
  • playwright test with the CI config (76 passed, 1 skipped, after the rebase); e2e/iiif-image.spec.ts covers the metadata table and that no manifest is requested
  • rebased onto the current base; dist/ rebuilt (pnpm run build leaves git status --short dist empty)
  • node scripts/verify-demo-content.js

🤖 Generated with Claude Code

kochizufan and others added 6 commits October 29, 2026 00:46
Add src/iiif/manifest.ts, a set of pure functions (no network, no DOM)
that read a IIIF Presentation 2/3 manifest and pick the values to copy
into a map's metadata keys: listIiifCanvases, proposeIiifBindings,
resolveIiifMetadata, compareIiifCopy, iiifRightsToLicense, and the
IIIF_BINDABLE_KEYS table. HTML values go through a sanitizer passed in
by the caller.

Add a "iiif" profile to sanitizeHtml (tags/attributes from
sanitize-allowlist.json, links get target/rel, href limited to http(s))
and export sanitizeIiifHtml.

getMapMeta now returns an optional `iiif` entry (manifest, canvas,
image, copiedAt, display, metadata) when the map data has a `iiif`
section. The viewer never fetches the manifest.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Niigata map now carries metadata copied from the NDL manifest
(produced by resolveIiifMetadata with sanitizeIiifHtml), and a second
entry of the same map shows the default "hidden" display. The demo
section shows the copied values, the full metadata table, a try-out of
the resolver and comparison functions, and how malicious values are
sanitized. The e2e checks the table, the display setting and that no
manifest is requested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tadata entries

- @maplat/core/common now exports sanitizeIiifHtml, the MapMetaIiif type
  and the isIiifMetadataEntry guard.
- When a map is loaded, iiif.copiedMetadata entries that are not
  { from: "manifest" | "canvas", label: Localized, value: Localized }
  are dropped, with one console.warn per map, so getMapMeta().iiif.metadata
  only contains well-formed entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- proposeIiifBindings proposes only title, attr, license and description,
  and only when the chosen source has the property. Data attribution and
  data license are no longer filled from the image's values by default.
- resolveIiifMetadata tells "missing" from "unreadable" in its warnings,
  and no longer recurses into nested arrays (deeply nested or cyclic
  values no longer throw).
- isIiifMetadataEntry rejects array labels and values.
- Test that the iiif forbidContents list matches DOMPurify's default
  minus thead.
- Docs: sanitizeIiifHtml, MapMetaIiif and isIiifMetadataEntry are
  available from @maplat/core/common.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@kochizufan
kochizufan force-pushed the dec26/m13-t3-iiif-metadata branch from c3f59bc to 5d7c84d Compare October 28, 2026 15:52
@kochizufan
kochizufan marked this pull request as ready for review October 28, 2026 16:56
@kochizufan
kochizufan merged commit 4bb4457 into train/2026-12 Oct 28, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant