@repo-toolkit/confluence 0.19.0 → 0.21.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (5) hide show
  1. package/README.md +129 -25
  2. package/cli.js +647 -14
  3. package/index.d.ts +150 -1
  4. package/index.js +622 -12
  5. package/package.json +2 -2
package/index.d.ts CHANGED
@@ -18,6 +18,7 @@ declare function isRemoteUrl(src: string): boolean;
18
18
  declare function escapeXmlAttribute(text: string): string;
19
19
  declare function escapeAttachmentFilename(filename: string): string;
20
20
 
21
+ declare const CONFLUENCE_MANAGED_LABEL = "repo-toolkit-confluence";
21
22
  interface ConfluenceClientOptions {
22
23
  baseUrl: string;
23
24
  username: string;
@@ -59,6 +60,23 @@ interface ConfluenceGateway extends AttachmentGateway {
59
60
  getPage(pageId: string): Promise<Page>;
60
61
  createPage(input: CreatePageInput): Promise<Page>;
61
62
  updatePage(input: UpdatePageInput): Promise<Page>;
63
+ getPageDescendants(pageId: string): Promise<PageDescendant[]>;
64
+ getPageLabels(pageId: string): Promise<PageLabel[]>;
65
+ addManagedLabel(pageId: string): Promise<void>;
66
+ deletePage(pageId: string): Promise<void>;
67
+ }
68
+ interface PageDescendant {
69
+ id: string;
70
+ type: string;
71
+ title?: string;
72
+ parentId?: string;
73
+ depth?: number;
74
+ status?: string;
75
+ }
76
+ interface PageLabel {
77
+ name: string;
78
+ prefix: string;
79
+ id?: string;
62
80
  }
63
81
  interface PageBody {
64
82
  representation: 'storage' | 'atlas_doc' | 'wiki' | 'view' | 'export_view';
@@ -145,6 +163,11 @@ declare class ConfluenceClient implements ConfluenceGateway {
145
163
  getPage(pageId: string): Promise<Page>;
146
164
  createPage(input: CreatePageInput): Promise<Page>;
147
165
  updatePage(input: UpdatePageInput): Promise<Page>;
166
+ getPageDescendants(pageId: string): Promise<PageDescendant[]>;
167
+ getPageLabels(pageId: string): Promise<PageLabel[]>;
168
+ addManagedLabel(pageId: string): Promise<void>;
169
+ deletePage(pageId: string): Promise<void>;
170
+ private listAll;
148
171
  getAttachments(pageId: string): Promise<Attachment[]>;
149
172
  uploadAttachment(pageId: string, filePath: string, comment?: string, filename?: string): Promise<Attachment>;
150
173
  updateAttachmentData(pageId: string, attachmentId: string, filePath: string, comment?: string, filename?: string): Promise<Attachment>;
@@ -167,6 +190,11 @@ interface DocTree {
167
190
  declare function readDocTree(root: string, depth?: number): Promise<DocTree>;
168
191
  declare function isMarkdownName(name: string): boolean;
169
192
  declare function titleFromSegment(segment: string): string;
193
+ declare const PAGE_TITLE_STRATEGIES: readonly ["filename-stem", "filename", "sentence-case-parent", "sentence-case-parents", "sentence-case-path"];
194
+ type PageTitleStrategy = (typeof PAGE_TITLE_STRATEGIES)[number];
195
+ declare const DEFAULT_PAGE_TITLE_STRATEGY: PageTitleStrategy;
196
+ declare function resolvePageTitleStrategy(value: unknown): PageTitleStrategy;
197
+ declare function pageTitleFromSegments(segments: readonly string[], strategy: PageTitleStrategy): string;
170
198
 
171
199
  interface RewriteResult {
172
200
  html: string;
@@ -336,10 +364,29 @@ interface ConfluenceSyncOptions {
336
364
  * render/upload work is performed.
337
365
  */
338
366
  skipUnchanged?: boolean;
367
+ /**
368
+ * Leaf page title strategy (default: `filename-stem`). Applied only to
369
+ * Markdown leaf pages; folder-generated parent pages keep their raw
370
+ * directory-segment titles. See {@link PAGE_TITLE_STRATEGIES}.
371
+ */
372
+ pageTitleStrategy?: PageTitleStrategy;
339
373
  /** Render ```html fenced blocks as inline HTML via the Confluence `html` macro instead of a code box (default: false). */
340
374
  renderHtmlBlocks?: boolean;
341
375
  /** Repository URL appended to synced pages as an italic source notice. */
342
376
  repositoryUrl?: string;
377
+ /**
378
+ * Destructive reset (default: false). When true, a real sync first moves
379
+ * every page descendant of `parentPageId` — including manual/unlabeled
380
+ * pages — to the Confluence trash, then recreates the local hierarchy.
381
+ * `parentPageId` itself is never deleted.
382
+ */
383
+ clean?: boolean;
384
+ /**
385
+ * Update the target parent summary (default: true). When true, a successful
386
+ * real sync fetches and merges a deterministic tool-managed region into the
387
+ * parent page body, preserving all external content.
388
+ */
389
+ updateParentPage?: boolean;
343
390
  /**
344
391
  * Dry-run: walk the tree and validate every markdown file and local image
345
392
  * source (same preflight as a real sync) then print the plan, but make no
@@ -370,11 +417,24 @@ interface ConfluenceSyncPlan {
370
417
  dryRun: boolean;
371
418
  renderHtmlBlocks: boolean;
372
419
  repositoryUrl: string;
420
+ /** Destructive reset flag; resolved from {@link ConfluenceSyncOptions.clean} with default false. */
421
+ clean: boolean;
422
+ /** Parent-summary flag; resolved from {@link ConfluenceSyncOptions.updateParentPage} with default true. */
423
+ updateParentPage: boolean;
424
+ /** Validated leaf page title strategy applied during planning. */
425
+ pageTitleStrategy: PageTitleStrategy;
373
426
  }
374
427
  /** A single markdown entry's locally-validated sync plan. */
375
428
  interface LocalSyncEntryPlan {
376
429
  /** The original doc-tree entry this plan covers. */
377
430
  entry: DocEntry;
431
+ /**
432
+ * The resolved Confluence page title for this leaf entry, computed once via
433
+ * {@link pageTitleFromSegments} with the plan's {@link ConfluenceSyncPlan.pageTitleStrategy}.
434
+ * Consumed verbatim by local hierarchy validation, dry-run output, remote
435
+ * lookup, create, and update paths.
436
+ */
437
+ title: string;
378
438
  /** Rendered storage HTML from `markdownToStorage` (placeholder macros intact). */
379
439
  html: string;
380
440
  /** Mermaid blocks parsed from the markdown source. */
@@ -461,8 +521,97 @@ declare class SyncMutationError extends Error {
461
521
  * when a full run completes without error. */
462
522
  interface SyncResult {
463
523
  changes: ReadonlyArray<SyncChange>;
524
+ /** Page ids that received the global ownership marker during this run. */
525
+ labelsAdded: ReadonlyArray<string>;
526
+ /** Page ids trashed by the explicit `clean: true` pre-sync reset. */
527
+ cleanDeletions: ReadonlyArray<string>;
528
+ /** Page ids trashed by the default label-gated prune after a successful sync. */
529
+ pruneDeletions: ReadonlyArray<string>;
530
+ /** Stale labeled pages retained because an unlabeled, non-page, or otherwise
531
+ * retained descendant made deletion unsafe. */
532
+ blocked: ReadonlyArray<string>;
533
+ /** Parent-summary outcome: `updated` when the parent body changed, `unchanged` when already equal, `skipped` when opt-out. */
534
+ parentStatus: 'updated' | 'unchanged' | 'skipped';
535
+ }
536
+ /** A single failed clean/prune deletion. */
537
+ interface ReconciliationFailure {
538
+ pageId: string;
539
+ error: Error;
540
+ }
541
+ /**
542
+ * Structured partial-mutation report for the clean and prune phases. Thrown
543
+ * when trashing a page fails mid-phase. Carries every completed deletion, the
544
+ * failed page, and the planned deletions that were never attempted.
545
+ */
546
+ declare class ReconciliationError extends Error {
547
+ readonly phase: 'clean' | 'prune';
548
+ readonly completed: ReadonlyArray<string>;
549
+ readonly failure: ReconciliationFailure;
550
+ readonly unprocessed: ReadonlyArray<string>;
551
+ constructor(input: {
552
+ phase: 'clean' | 'prune';
553
+ completed: ReadonlyArray<string>;
554
+ failure: ReconciliationFailure;
555
+ unprocessed: ReadonlyArray<string>;
556
+ });
464
557
  }
558
+ declare class ParentSummaryError extends Error {
559
+ readonly phase: 'parent-summary';
560
+ readonly changes: ReadonlyArray<SyncChange>;
561
+ readonly labelsAdded: ReadonlyArray<string>;
562
+ readonly cleanDeletions: ReadonlyArray<string>;
563
+ readonly pruneDeletions: ReadonlyArray<string>;
564
+ readonly blocked: ReadonlyArray<string>;
565
+ readonly failure: ReconciliationFailure;
566
+ constructor(input: {
567
+ changes: ReadonlyArray<SyncChange>;
568
+ labelsAdded: ReadonlyArray<string>;
569
+ cleanDeletions: ReadonlyArray<string>;
570
+ pruneDeletions: ReadonlyArray<string>;
571
+ blocked: ReadonlyArray<string>;
572
+ failure: ReconciliationFailure;
573
+ });
574
+ }
575
+ /** One inventoried descendant of the target page, annotated with ownership. */
576
+ interface RemoteInventoryEntry {
577
+ id: string;
578
+ type: string;
579
+ parentId?: string;
580
+ depth?: number;
581
+ title?: string;
582
+ /** Whether the page carries the exact global ownership marker. */
583
+ labeled: boolean;
584
+ }
585
+ /** Outcome of the pure deletion planners. */
586
+ interface DeletionPlan {
587
+ /** Page ids to trash, ordered deepest-first (children before parents). */
588
+ deletions: ReadonlyArray<string>;
589
+ /** Stale labeled pages retained because deleting them would also remove a
590
+ * retained (unlabeled, non-page, or expected) descendant. */
591
+ blocked: ReadonlyArray<string>;
592
+ }
593
+ /**
594
+ * Pure label-gated stale-page planner. Protects the target page, expected
595
+ * (mapped) ids, unlabeled pages, non-page content, and any stale page with a
596
+ * retained descendant. Throws when the inventory is too incomplete to verify
597
+ * ancestry safely.
598
+ */
599
+ declare function planStalePruning(input: {
600
+ parentPageId: string;
601
+ expectedIds: ReadonlySet<string>;
602
+ inventory: ReadonlyArray<RemoteInventoryEntry>;
603
+ }): DeletionPlan;
604
+ /**
605
+ * Pure explicit-clean planner. Trash every page descendant regardless of
606
+ * label. Fails closed before any deletion when the subtree contains non-page
607
+ * content whose retention cannot be proven, or when the inventory is
608
+ * incomplete.
609
+ */
610
+ declare function planCleanDeletions(input: {
611
+ parentPageId: string;
612
+ inventory: ReadonlyArray<RemoteInventoryEntry>;
613
+ }): DeletionPlan;
465
614
  declare function resolveConfluenceSyncPlan(options?: ConfluenceSyncOptions): ConfluenceSyncPlan;
466
615
  declare function syncConfluenceToDocs(options?: ConfluenceSyncOptions): Promise<SyncResult | void>;
467
616
 
468
- export { type Attachment, type AttachmentGateway, type PreflightResult as AttachmentPreflightResult, ConfluenceApiError, ConfluenceClient, type ConfluenceClientOptions, type ConfluenceGateway, type ConfluenceSyncOptions, type ConfluenceSyncPlan, type CreatePageInput, type DocEntry, type DocTree, INTERACTIVE_FLAG, type LocalSyncEntryPlan, type LocalSyncPlan, LocalSyncValidationAggregateError, type LocalSyncValidationError, type MarkdownConvertOptions, type MarkdownConvertResult, type MermaidBlock, type MermaidPreflightResult, type MermaidRewriteOptions, type MermaidRewriteResult, type Page, type PageBody, type PageVersion, type RewriteOptions, type RewriteResult, type SyncChange, type SyncFailure, SyncMutationError, type SyncResult, type UpdatePageInput, type ValidatedAttachmentSource, escapeAttachmentFilename, escapeXmlAttribute, isAllowedUrl, isMarkdownName, isRemoteUrl, markdownToStorage, preflightImagesToAttachments, preflightMermaidBlocks, readDocTree, resolveConfluenceSyncPlan, resolveConfluenceSyncPlan as resolveSyncPlan, rewriteImagesToAttachments, rewriteMermaidBlocks, syncConfluenceToDocs, titleFromSegment, validateAttachmentSources, validateLocalSync };
617
+ export { type Attachment, type AttachmentGateway, type PreflightResult as AttachmentPreflightResult, CONFLUENCE_MANAGED_LABEL, ConfluenceApiError, ConfluenceClient, type ConfluenceClientOptions, type ConfluenceGateway, type ConfluenceSyncOptions, type ConfluenceSyncPlan, type CreatePageInput, DEFAULT_PAGE_TITLE_STRATEGY, type DeletionPlan, type DocEntry, type DocTree, INTERACTIVE_FLAG, type LocalSyncEntryPlan, type LocalSyncPlan, LocalSyncValidationAggregateError, type LocalSyncValidationError, type MarkdownConvertOptions, type MarkdownConvertResult, type MermaidBlock, type MermaidPreflightResult, type MermaidRewriteOptions, type MermaidRewriteResult, PAGE_TITLE_STRATEGIES, type Page, type PageBody, type PageDescendant, type PageLabel, type PageTitleStrategy, type PageVersion, ParentSummaryError, ReconciliationError, type ReconciliationFailure, type RemoteInventoryEntry, type RewriteOptions, type RewriteResult, type SyncChange, type SyncFailure, SyncMutationError, type SyncResult, type UpdatePageInput, type ValidatedAttachmentSource, escapeAttachmentFilename, escapeXmlAttribute, isAllowedUrl, isMarkdownName, isRemoteUrl, markdownToStorage, pageTitleFromSegments, planCleanDeletions, planStalePruning, preflightImagesToAttachments, preflightMermaidBlocks, readDocTree, resolveConfluenceSyncPlan, resolvePageTitleStrategy, resolveConfluenceSyncPlan as resolveSyncPlan, rewriteImagesToAttachments, rewriteMermaidBlocks, syncConfluenceToDocs, titleFromSegment, validateAttachmentSources, validateLocalSync };