forked from akai/readest
The TypeScript types in `src/types/opds.ts` declared fresh
`Symbol('content')` / `Symbol('summary')` instances. foliate-js's
`opds.js` declared its own distinct ones, and since Symbols are unique
per call, `metadata[SYMBOL.CONTENT]` always returned undefined — even
though the parser had written the value under a same-named Symbol.
This broke silently in 0.11.1 after foliate-js #14 stopped also setting
a plain `content: string` fallback. For OPDS 1.x feeds (e.g. CWA) the
book description lives in `<entry><summary>`, which foliate-js exposes
only via `[SYMBOL.CONTENT]` — so the description vanished.
Re-export the SYMBOL from foliate-js so consumers read the same Symbol
identities the parser writes.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { getFeed, getPublication } from 'foliate-js/opds.js';
|
||||
import type { OPDSFeed, OPDSPublication } from '@/types/opds';
|
||||
import { SYMBOL, type OPDSFeed, type OPDSPublication } from '@/types/opds';
|
||||
|
||||
const MIME_XML = 'application/xml';
|
||||
|
||||
@@ -222,6 +222,33 @@ describe('OPDS feed parsing', () => {
|
||||
expect(pub.metadata.updated).toBe('2026-01-15T10:30:00.000Z');
|
||||
});
|
||||
|
||||
// Regression test for https://github.com/readest/readest/issues/4156
|
||||
// CWA (and other OPDS 1.x servers) place the book description in
|
||||
// <entry><summary>. foliate-js attaches it under SYMBOL.CONTENT, so the
|
||||
// SYMBOL exported from @/types/opds must be the same Symbol instance the
|
||||
// parser writes — otherwise PublicationView reads undefined and the
|
||||
// description disappears from the book details page.
|
||||
it('should expose <summary> via SYMBOL.CONTENT for OPDS 1.x feeds', () => {
|
||||
const opds1Feed = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
<title>CWA Library</title>
|
||||
<entry>
|
||||
<title>A Book With A Summary</title>
|
||||
<summary>A short blurb describing the book, set by the OPDS server in <summary>.</summary>
|
||||
<link rel="http://opds-spec.org/acquisition"
|
||||
href="book.epub" type="application/epub+zip"/>
|
||||
</entry>
|
||||
</feed>`;
|
||||
const doc = parseXML(opds1Feed);
|
||||
const feed = getFeed(doc) as OPDSFeed;
|
||||
|
||||
expect(feed.publications).toHaveLength(1);
|
||||
const metadata = feed.publications![0]!.metadata;
|
||||
const content = metadata[SYMBOL.CONTENT];
|
||||
expect(content).toBeDefined();
|
||||
expect(content!.value).toContain('A short blurb describing the book');
|
||||
});
|
||||
|
||||
it('should handle entries without id or updated gracefully', () => {
|
||||
const minimalFeed = `<?xml version="1.0" encoding="UTF-8"?>
|
||||
<feed xmlns="http://www.w3.org/2005/Atom">
|
||||
|
||||
@@ -1,3 +1,10 @@
|
||||
// SYMBOL must be re-exported from foliate-js so consumers read the same Symbol
|
||||
// instances that the parser writes onto publication metadata. Declaring fresh
|
||||
// `Symbol('content')` calls here would produce different identities, and
|
||||
// `metadata[SYMBOL.CONTENT]` would silently return undefined — losing the book
|
||||
// description for OPDS 1.x feeds where it lives in <summary>.
|
||||
import { SYMBOL as FOLIATE_SYMBOL } from 'foliate-js/opds.js';
|
||||
|
||||
export const REL = {
|
||||
ACQ: 'http://opds-spec.org/acquisition',
|
||||
FACET: 'http://opds-spec.org/facet',
|
||||
@@ -11,13 +18,7 @@ export const REL = {
|
||||
STREAM: 'http://vaemendis.net/opds-pse/stream',
|
||||
} as const;
|
||||
|
||||
const SUMMARY = Symbol('summary');
|
||||
const CONTENT = Symbol('content');
|
||||
|
||||
export const SYMBOL = {
|
||||
SUMMARY,
|
||||
CONTENT,
|
||||
} as const;
|
||||
export const SYMBOL = FOLIATE_SYMBOL as { SUMMARY: symbol; CONTENT: symbol };
|
||||
|
||||
export interface OPDSCatalog {
|
||||
id: string;
|
||||
|
||||
Reference in New Issue
Block a user