@@ -51,7 +51,7 @@ const groupByArray = (arr, f) => {
5151
5252// https://www.rfc-editor.org/rfc/rfc7231#section-3.1.1
5353const parseMediaType = str => {
54- if ( ! str ) return null
54+ if ( ! str ) return
5555 const [ mediaType , ...ps ] = str . split ( / * ; * / )
5656 return {
5757 mediaType : mediaType . toLowerCase ( ) ,
@@ -72,7 +72,7 @@ export const isOPDSCatalog = str => {
7272
7373// ignore the namespace if it doesn't appear in document at all
7474const useNS = ( doc , ns ) =>
75- doc . lookupNamespaceURI ( null ) === ns || doc . lookupPrefix ( ns ) ? ns : null
75+ doc . lookupNamespaceURI ( null ) === ns || doc . lookupPrefix ( ns ) ? ns : undefined
7676
7777const filterNS = ns => ns
7878 ? name => el => el . namespaceURI === ns && el . localName === name
@@ -92,7 +92,7 @@ const getContent = el => {
9292
9393const getTextContent = el => {
9494 const content = getContent ( el )
95- if ( content ?. type === 'text' ) return content ? .value
95+ if ( content ?. type === 'text' ) return content . value
9696}
9797
9898const getSummary = ( a , b ) => getTextContent ( a ) ?? getTextContent ( b )
@@ -104,13 +104,13 @@ const getDirectChildren = (el, ns, localName, tagName) => {
104104 (
105105 ( node . namespaceURI === ns && node . localName === localName ) ||
106106 ( node . tagName === tagName )
107- )
107+ ) ,
108108 )
109109}
110110
111111const getPrice = link => {
112112 const prices = getDirectChildren ( link , NS . OPDS , 'price' , 'opds:price' )
113- if ( ! prices . length ) return null
113+ if ( ! prices . length ) return
114114 const parsed = prices . map ( price => ( {
115115 currency : price . getAttribute ( 'currencycode' ) ,
116116 value : parseFloat ( price . textContent ) ,
@@ -133,61 +133,45 @@ const getIndirectAcquisition = el => {
133133}
134134
135135const getLink = link => {
136- const obj = {
137- rel : link . getAttribute ( 'rel' ) ?. split ( / + / ) ,
138- href : link . getAttribute ( 'href' ) ,
139- type : link . getAttribute ( 'type' ) ,
140- title : link . getAttribute ( 'title' ) ,
141- properties : { } ,
142- }
143-
144- // --- Prices & Indirect Acquisitions ---
145- const price = getPrice ( link )
146- if ( price ) obj . properties . price = price
147-
148- const indirectAcquisition = getIndirectAcquisition ( link )
149- if ( indirectAcquisition . length ) obj . properties . indirectAcquisition = indirectAcquisition
136+ const relAttr = link . getAttribute ( 'rel' )
137+ const rel = relAttr ? relAttr . split ( / + / ) : undefined
150138
151- // --- Facet Grouping ---
152- const facetGroup = link . getAttributeNS ( NS . OPDS , 'facetGroup' ) || link . getAttribute ( 'opds:facetGroup' )
153- if ( facetGroup ) obj [ FACET_GROUP ] = facetGroup
139+ const isAcquisition = rel ?. some ( r => r . startsWith ( REL . ACQ ) || r === 'preview' )
140+ const isStream = rel ?. includes ( REL . STREAM )
141+ const isFacet = rel ?. includes ( REL . FACET )
154142
155143 // Map OPDS 1.x active facets to OPDS 2.0 "self" link
156144 const activeFacet = link . getAttributeNS ( NS . OPDS , 'activeFacet' ) || link . getAttribute ( 'opds:activeFacet' )
157- if ( activeFacet === 'true' ) {
158- obj . rel = [ obj . rel ?? [ ] ] . flat ( ) . concat ( 'self' )
159- }
145+ const mappedRel = activeFacet === 'true' ? [ rel ?? [ ] ] . flat ( ) . concat ( 'self' ) : rel
160146
161- // --- Pagination / Facet Counters ---
162147 // Maps OPDS 1.x thr:count seamlessly to OPDS 2.0 properties.numberOfItems
163148 const thrCount = link . getAttributeNS ( NS . THR , 'count' ) || link . getAttribute ( 'thr:count' )
149+ // Support for systems that incorrectly use standard `count` for facet hints
164150 const fallbackCount = link . getAttribute ( 'count' )
165- const isStream = obj . rel ?. includes ( REL . STREAM )
166-
167- if ( thrCount != null ) {
168- obj . properties . numberOfItems = Number ( thrCount )
169- } else if ( ! isStream && fallbackCount != null ) {
170- // Support for systems that incorrectly use standard `count` for facet hints
171- obj . properties . numberOfItems = Number ( fallbackCount )
172- }
173151
174152 // --- OPDS-PSE Extensions ---
175- // Kept explicitly inside properties to map to OPDS 2.x standard extension mechanism
176153 const pseCount = link . getAttributeNS ( NS . PSE , 'count' ) || link . getAttribute ( 'pse:count' )
177- if ( pseCount != null ) {
178- obj . properties [ 'pse:count' ] = Number ( pseCount )
179- } else if ( isStream && fallbackCount != null ) {
180- obj . properties [ 'pse:count' ] = Number ( fallbackCount )
181- }
182-
183154 const pseLastRead = link . getAttributeNS ( NS . PSE , 'lastRead' ) || link . getAttribute ( 'pse:lastRead' )
184- if ( pseLastRead != null ) obj . properties [ 'pse:lastRead' ] = Number ( pseLastRead )
185-
186155 const pseLastReadDate = link . getAttributeNS ( NS . PSE , 'lastReadDate' ) || link . getAttribute ( 'pse:lastReadDate' )
187- if ( pseLastReadDate != null ) obj . properties [ 'pse:lastReadDate' ] = pseLastReadDate
188- // ---------------------------
189156
190- // Clean up empty properties
157+ const obj = {
158+ rel : mappedRel ,
159+ href : link . getAttribute ( 'href' ) ,
160+ type : link . getAttribute ( 'type' ) ,
161+ title : link . getAttribute ( 'title' ) ,
162+ // --- Facet Grouping ---
163+ [ FACET_GROUP ] : link . getAttributeNS ( NS . OPDS , 'facetGroup' ) || link . getAttribute ( 'opds:facetGroup' ) ,
164+ properties : {
165+ price : ( isAcquisition || isStream ) ? getPrice ( link ) : undefined ,
166+ indirectAcquisition : ( isAcquisition || isStream ) ? getIndirectAcquisition ( link ) : [ ] ,
167+ // --- Pagination / Facet Counters ---
168+ numberOfItems : thrCount != null ? Number ( thrCount ) : ( isFacet && fallbackCount != null ) ? Number ( fallbackCount ) : undefined ,
169+ 'pse:count' : isStream ? Number ( pseCount || fallbackCount ) || undefined : undefined ,
170+ 'pse:lastRead' : isStream && pseLastRead != null ? Number ( pseLastRead ) : undefined ,
171+ 'pse:lastReadDate' : isStream ? pseLastReadDate : undefined ,
172+ } ,
173+ }
174+
191175 if ( Object . keys ( obj . properties ) . length === 0 ) delete obj . properties
192176
193177 return obj
@@ -219,8 +203,7 @@ export const getPublication = entry => {
219203 author : children . filter ( filter ( 'author' ) ) . map ( getPerson ) ,
220204 contributor : children . filter ( filter ( 'contributor' ) ) . map ( getPerson ) ,
221205 publisher : children . find ( filterDC ( 'publisher' ) ) ?. textContent ,
222- published : ( children . find ( filterDCTERMS ( 'issued' ) )
223- ?? children . find ( filterDC ( 'date' ) ) ) ?. textContent ,
206+ published : ( children . find ( filterDCTERMS ( 'issued' ) ) ?? children . find ( filterDC ( 'date' ) ) ) ?. textContent ,
224207 language : children . find ( filterDC ( 'language' ) ) ?. textContent ,
225208 identifier : children . find ( filterDC ( 'identifier' ) ) ?. textContent ,
226209 subject : children . filter ( filter ( 'category' ) ) . map ( category => ( {
@@ -229,8 +212,7 @@ export const getPublication = entry => {
229212 scheme : category . getAttribute ( 'scheme' ) ,
230213 } ) ) ,
231214 rights : children . find ( filter ( 'rights' ) ) ?. textContent ?? '' ,
232- [ SYMBOL . CONTENT ] : getContent ( children . find ( filter ( 'content' ) )
233- ?? children . find ( filter ( 'summary' ) ) ) ,
215+ [ SYMBOL . CONTENT ] : getContent ( children . find ( filter ( 'content' ) ) ?? children . find ( filter ( 'summary' ) ) ) ,
234216 } ,
235217 links,
236218 images : REL . COVER . concat ( REL . THUMBNAIL )
@@ -249,7 +231,7 @@ export const getFeed = doc => {
249231 const filterFH = filterNS ( NS . FH )
250232 const filterOS = filterNS ( NS . OS )
251233
252- const groupedItems = new Map ( [ [ null , [ ] ] ] )
234+ const groupedItems = new Map ( [ [ undefined , [ ] ] ] )
253235 const groupLinkMap = new Map ( )
254236 for ( const entry of entries ) {
255237 const children = Array . from ( entry . children )
@@ -260,7 +242,7 @@ export const getFeed = doc => {
260242
261243 const groupLinks = linksByRel . get ( REL . GROUP ) ?? linksByRel . get ( 'collection' )
262244 const groupLink = groupLinks ?. length
263- ? groupLinks . find ( link => groupedItems . has ( link . href ) ) ?? groupLinks [ 0 ] : null
245+ ? groupLinks . find ( link => groupedItems . has ( link . href ) ) ?? groupLinks [ 0 ] : undefined
264246 if ( groupLink && ! groupLinkMap . has ( groupLink . href ) )
265247 groupLinkMap . set ( groupLink . href , groupLink )
266248
@@ -272,13 +254,13 @@ export const getFeed = doc => {
272254 children . find ( filter ( 'content' ) ) ) ,
273255 } )
274256
275- const arr = groupedItems . get ( groupLink ?. href ?? null )
257+ const arr = groupedItems . get ( groupLink ?. href )
276258 if ( arr ) arr . push ( item )
277259 else groupedItems . set ( groupLink . href , [ item ] )
278260 }
279261 const [ items , ...groups ] = Array . from ( groupedItems , ( [ key , items ] ) => {
280262 const itemsKey = items [ 0 ] ?. metadata ? 'publications' : 'navigation'
281- if ( key == null ) return { [ itemsKey ] : items }
263+ if ( key === undefined ) return { [ itemsKey ] : items }
282264 const link = groupLinkMap . get ( key )
283265 return {
284266 metadata : {
@@ -290,39 +272,36 @@ export const getFeed = doc => {
290272 }
291273 } )
292274
293- const metadata = {
294- title : children . find ( filter ( 'title' ) ) ?. textContent ,
295- subtitle : children . find ( filter ( 'subtitle' ) ) ?. textContent ,
296- }
297-
298275 // --- OPDS 2.0 Pagination (derived from OpenSearch / RFC 5005) ---
299276 const totalResults = children . find ( filterOS ( 'totalResults' ) ) ?. textContent
300277 const itemsPerPage = children . find ( filterOS ( 'itemsPerPage' ) ) ?. textContent
301278 const startIndex = children . find ( filterOS ( 'startIndex' ) ) ?. textContent
302279
303- if ( totalResults != null ) metadata . numberOfItems = Number ( totalResults )
304- if ( itemsPerPage != null ) metadata . itemsPerPage = Number ( itemsPerPage )
280+ let currentPage
305281 if ( startIndex != null && itemsPerPage != null ) {
306282 const start = Number ( startIndex )
307283 const items = Number ( itemsPerPage )
308284 // Resolves typical 1-based offset to a page number
309- metadata . currentPage = Math . floor ( ( start > 0 ? start - 1 : 0 ) / items ) + 1
285+ currentPage = Math . floor ( ( start > 0 ? start - 1 : 0 ) / items ) + 1
310286 }
311287
312- const isComplete = ! ! children . find ( filterFH ( 'complete' ) )
313- const isArchive = ! ! children . find ( filterFH ( 'archive' ) )
314- // ----------------------------------------------------------------
315-
316288 return {
317- metadata,
289+ metadata : {
290+ title : children . find ( filter ( 'title' ) ) ?. textContent ,
291+ subtitle : children . find ( filter ( 'subtitle' ) ) ?. textContent ,
292+ numberOfItems : totalResults != null ? Number ( totalResults ) : undefined ,
293+ itemsPerPage : itemsPerPage != null ? Number ( itemsPerPage ) : undefined ,
294+ currentPage,
295+ } ,
318296 links,
319- isComplete,
320- isArchive,
297+ isComplete : ! ! children . find ( filterFH ( 'complete' ) ) ,
298+ isArchive : ! ! children . find ( filterFH ( 'archive' ) ) ,
321299 ...items ,
322300 groups,
323301 facets : Array . from (
324302 groupByArray ( linksByRel . get ( REL . FACET ) ?? [ ] , link => link [ FACET_GROUP ] ) ,
325- ( [ facet , links ] ) => ( { metadata : { title : facet } , links } ) ) ,
303+ ( [ facet , links ] ) => ( { metadata : { title : facet } , links } ) ,
304+ ) ,
326305 }
327306}
328307
@@ -332,7 +311,7 @@ export const getSearch = async link => {
332311 metadata : {
333312 title : link . title ,
334313 } ,
335- search : map => replace ( link . href , map . get ( null ) ) ,
314+ search : map => replace ( link . href , map . get ( undefined ) ) ,
336315 params : Array . from ( getVariables ( link . href ) , name => ( { name } ) ) ,
337316 }
338317}
@@ -363,14 +342,14 @@ export const getOpenSearch = doc => {
363342 description : children . find ( filter ( 'Description' ) ) ?. textContent ,
364343 } ,
365344 search : map => template . replace ( regex , ( _ , prefix , param ) => {
366- const namespace = prefix ? $url . lookupNamespaceURI ( prefix ) : null
367- const ns = namespace === defaultNS ? null : namespace
345+ const namespace = prefix ? $url . lookupNamespaceURI ( prefix ) : undefined
346+ const ns = namespace === defaultNS ? undefined : namespace
368347 const val = map . get ( ns ) ?. get ( param )
369348 return encodeURIComponent ( val ? val : ( ! ns ? defaultMap . get ( param ) ?? '' : '' ) )
370349 } ) ,
371350 params : Array . from ( template . matchAll ( regex ) , ( [ , prefix , param , optional ] ) => {
372- const namespace = prefix ? $url . lookupNamespaceURI ( prefix ) : null
373- const ns = namespace === defaultNS ? null : namespace
351+ const namespace = prefix ? $url . lookupNamespaceURI ( prefix ) : undefined
352+ const ns = namespace === defaultNS ? undefined : namespace
374353 return {
375354 ns, name : param ,
376355 required : ! optional ,
0 commit comments