@@ -15,6 +15,18 @@ export function escapeCql(value: string): string {
1515 return value . replace ( / \\ / g, '\\\\' ) . replace ( / " / g, '\\"' )
1616}
1717
18+ /**
19+ * Keeps only content that is still current in Confluence. The v2
20+ * `/spaces/{id}/pages` endpoint includes `archived` pages by default and CQL has
21+ * no status filter, so without this guard archived pages stay in every listing,
22+ * keep getting upserted, and never fall out via deletion reconciliation (which
23+ * removes only documents absent from the listing). Items with no status field
24+ * are kept — only an explicit non-current status excludes a result.
25+ */
26+ export function isCurrentContent ( item : Record < string , unknown > ) : boolean {
27+ return item . status == null || item . status === 'current'
28+ }
29+
1830/**
1931 * Builds a CQL clause restricting content to the given space keys.
2032 * Single key uses `space = "X"`; multiple keys use `space in ("X","Y")`.
@@ -272,7 +284,7 @@ export const confluenceConnector: ConnectorConfig = {
272284 }
273285 }
274286
275- if ( ! page ) return null
287+ if ( ! page || ! isCurrentContent ( page ) ) return null
276288 const body = page . body as Record < string , unknown > | undefined
277289 const view = body ?. view as Record < string , unknown > | undefined
278290 const rawContent = ( view ?. value as string ) || ''
@@ -381,6 +393,12 @@ async function listDocumentsV2(
381393) : Promise < ExternalDocumentList > {
382394 const queryParams = new URLSearchParams ( )
383395 queryParams . append ( 'limit' , '250' )
396+ /**
397+ * Restrict to current content: the pages endpoint defaults to
398+ * `current,archived`, so archived pages would otherwise stay in the listing
399+ * forever and never be purged by deletion reconciliation.
400+ */
401+ queryParams . append ( 'status' , 'current' )
384402 if ( cursor ) {
385403 queryParams . append ( 'cursor' , cursor )
386404 }
@@ -410,13 +428,15 @@ async function listDocumentsV2(
410428 const data = await response . json ( )
411429 const results = data . results || [ ]
412430
413- const documents : ExternalDocument [ ] = results . map ( ( page : Record < string , unknown > ) => {
414- const links = page . _links as Record < string , string > | undefined
415- return pageToStub ( page , {
416- spaceId : page . spaceId ,
417- sourceUrl : links ?. webui ? `https://${ domain } /wiki${ links . webui } ` : undefined ,
431+ const documents : ExternalDocument [ ] = ( results as Record < string , unknown > [ ] )
432+ . filter ( isCurrentContent )
433+ . map ( ( page ) => {
434+ const links = page . _links as Record < string , string > | undefined
435+ return pageToStub ( page , {
436+ spaceId : page . spaceId ,
437+ sourceUrl : links ?. webui ? `https://${ domain } /wiki${ links . webui } ` : undefined ,
438+ } )
418439 } )
419- } )
420440
421441 let nextCursor : string | undefined
422442 const nextLink = ( data . _links as Record < string , string > ) ?. next
@@ -594,9 +614,9 @@ async function listDocumentsViaCql(
594614 const data = await response . json ( )
595615 const results = data . results || [ ]
596616
597- const documents : ExternalDocument [ ] = results . map ( ( item : Record < string , unknown > ) =>
598- cqlResultToStub ( item , domain )
599- )
617+ const documents : ExternalDocument [ ] = ( results as Record < string , unknown > [ ] )
618+ . filter ( isCurrentContent )
619+ . map ( ( item ) => cqlResultToStub ( item , domain ) )
600620
601621 const totalFetched = ( ( syncContext ?. totalDocsFetched as number ) ?? 0 ) + documents . length
602622 if ( syncContext ) syncContext . totalDocsFetched = totalFetched
0 commit comments