Rework OcflStorageService so an object can hold binaries alongside
node.json, and fix several conformance problems in the generated objects.
- saveObject() accepts a map of logical path => source URI. Files whose
sha512 is already in the manifest are referenced from the new version's
state instead of being copied again (forward-delta deduplication); new
copies are fixity-checked after copying.
- File digests are cached by URI, size and mtime in key/value storage so
unchanged files aren't rehashed on every export.
- Unreadable files are skipped and listed in _metadata.missing_files
rather than failing the whole export.
- Fix the manifest overwriting an existing digest's path, which left
content files on disk that the manifest didn't list.
- Fix the 0= conformance declarations, which must contain only the
version string (E007).
- Add a version user, the contentDirectory key, and replace the root
inventory last via rename so a failed export can't advance the head.
Leftover version directories from interrupted exports are removed.
- Object IDs are urn:uuid URIs, and objects are stored using the
0003-hash-and-id-n-tuple-storage-layout extension; the module refuses to
write into a root that declares a different layout.
- Resolve the latest node.json through the inventory, since unchanged
content may live in an earlier version.
- Restrict the view_export route's uuid parameter to a UUID pattern, as
it is now used to build a filesystem path.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>