@moxn/kb-migrate 0.4.22 → 0.4.23

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.
@@ -9,7 +9,7 @@
9
9
  */
10
10
  import type { ExtractedDocument } from '../types.js';
11
11
  import { MigrationSource, type SourceConfig } from './base.js';
12
- import type { NotionPage } from './notion-api.js';
12
+ import type { NotionPage, NotionDatabase } from './notion-api.js';
13
13
  import { type PagePathMap } from './notion-blocks.js';
14
14
  import { type ParsedDatabaseSchema } from './notion-databases.js';
15
15
  export interface NotionSourceConfig extends SourceConfig {
@@ -26,6 +26,7 @@ export interface PageTreeNode {
26
26
  children: PageTreeNode[];
27
27
  kbPath: string;
28
28
  isDatabaseEntry: boolean;
29
+ isDatabaseNode?: boolean;
29
30
  parentDatabaseId?: string;
30
31
  }
31
32
  /** Info yielded to the migration runner for database import (post-document pass). */
@@ -83,6 +84,7 @@ export declare class NotionSource extends MigrationSource<NotionSourceConfig> {
83
84
  export interface BuildPageTreeOptions {
84
85
  rootPageId?: string;
85
86
  maxDepth?: number;
87
+ databases?: NotionDatabase[];
86
88
  }
87
89
  /** Result of building the page tree. */
88
90
  export interface PageTreeResult {
@@ -84,12 +84,12 @@ export class NotionSource extends MigrationSource {
84
84
  console.log('Discovering databases...');
85
85
  const allNotionDatabases = await this.client.searchDatabases();
86
86
  console.log(` Found ${allNotionDatabases.length} databases.`);
87
- // 4. Build page tree
88
- this.buildPageTreeInternal(allNotionPages);
87
+ // 4. Build page tree (pass databases so they become tree nodes for nesting entries)
88
+ this.buildPageTreeInternal(allNotionPages, allNotionDatabases);
89
89
  // 5. Process databases — identify entries and build schemas
90
90
  await this.processDatabases(allNotionDatabases);
91
- // 6. Count and validate
92
- this._documentCount = this.allPages.length;
91
+ // 6. Count and validate (exclude synthetic database container nodes)
92
+ this._documentCount = this.allPages.filter((n) => !n.isDatabaseNode).length;
93
93
  // Add database-only entries (pages that are in databases but not in page tree)
94
94
  for (const dbInfo of this.databases) {
95
95
  for (const entry of dbInfo.entries) {
@@ -141,6 +141,9 @@ export class NotionSource extends MigrationSource {
141
141
  // unless they also appear in the page tree (child_page)
142
142
  if (node.isDatabaseEntry)
143
143
  continue;
144
+ // Skip synthetic database container nodes — they're for nesting, not documents
145
+ if (node.isDatabaseNode)
146
+ continue;
144
147
  const doc = await this.extractPage(node);
145
148
  if (doc)
146
149
  yield doc;
@@ -171,10 +174,11 @@ export class NotionSource extends MigrationSource {
171
174
  // ============================================
172
175
  // Tree building (delegates to standalone function)
173
176
  // ============================================
174
- buildPageTreeInternal(pages) {
177
+ buildPageTreeInternal(pages, databases) {
175
178
  const result = buildPageTree(pages, {
176
179
  rootPageId: this.config.rootPageId,
177
180
  maxDepth: this.config.maxDepth,
181
+ databases,
178
182
  });
179
183
  this.pageTree = result.tree;
180
184
  this.allPages = result.allPages;
@@ -332,6 +336,7 @@ export function buildPageTree(pages, options) {
332
336
  // Build lookup maps
333
337
  const pageById = new Map();
334
338
  const childrenByParent = new Map();
339
+ const syntheticDatabaseIds = new Set();
335
340
  for (const page of pages) {
336
341
  const nid = normalizeId(page.id);
337
342
  pageById.set(nid, page);
@@ -344,6 +349,39 @@ export function buildPageTree(pages, options) {
344
349
  childrenByParent.get(npid).push(page);
345
350
  }
346
351
  }
352
+ // Inject synthetic page nodes for databases so entries nest under them
353
+ if (options?.databases?.length) {
354
+ for (const db of options.databases) {
355
+ const dbNid = normalizeId(db.id);
356
+ // Skip if a real page already has this ID
357
+ if (pageById.has(dbNid))
358
+ continue;
359
+ // Create synthetic NotionPage wrapper
360
+ const syntheticPage = {
361
+ id: db.id,
362
+ object: 'page',
363
+ parent: db.parent,
364
+ created_time: db.created_time,
365
+ last_edited_time: db.last_edited_time,
366
+ created_by: { id: '', object: 'user' },
367
+ last_edited_by: { id: '', object: 'user' },
368
+ url: '',
369
+ properties: {
370
+ title: { id: 'title', type: 'title', title: db.title },
371
+ },
372
+ };
373
+ pageById.set(dbNid, syntheticPage);
374
+ // Add to childrenByParent
375
+ const parentId = getParentPageId(syntheticPage);
376
+ if (parentId) {
377
+ const npid = normalizeId(parentId);
378
+ if (!childrenByParent.has(npid))
379
+ childrenByParent.set(npid, []);
380
+ childrenByParent.get(npid).push(syntheticPage);
381
+ }
382
+ syntheticDatabaseIds.add(dbNid);
383
+ }
384
+ }
347
385
  // Identify database entries
348
386
  for (const page of pages) {
349
387
  if (page.parent.type === 'database_id' && page.parent.database_id) {
@@ -377,6 +415,19 @@ export function buildPageTree(pages, options) {
377
415
  }
378
416
  }
379
417
  }
418
+ // Also add synthetic database nodes whose parent is workspace or not in the tree
419
+ for (const dbNid of syntheticDatabaseIds) {
420
+ const synth = pageById.get(dbNid);
421
+ if (synth.parent.type === 'workspace') {
422
+ rootPages.push(synth);
423
+ }
424
+ else {
425
+ const parentId = getParentPageId(synth);
426
+ if (parentId && !pageById.has(normalizeId(parentId))) {
427
+ rootPages.push(synth);
428
+ }
429
+ }
430
+ }
380
431
  }
381
432
  // Build tree recursively
382
433
  const buildNode = (page, parentPath, depth, siblingSlugCounts) => {
@@ -400,6 +451,7 @@ export function buildPageTree(pages, options) {
400
451
  slug,
401
452
  kbPath,
402
453
  isDatabaseEntry,
454
+ isDatabaseNode: syntheticDatabaseIds.has(nid) || undefined,
403
455
  parentDatabaseId: page.parent.type === 'database_id' ? page.parent.database_id : undefined,
404
456
  children: [],
405
457
  };
@@ -438,6 +490,8 @@ export function buildPageTree(pages, options) {
438
490
  function getParentPageId(page) {
439
491
  if (page.parent.type === 'page_id')
440
492
  return page.parent.page_id ?? null;
493
+ if (page.parent.type === 'database_id')
494
+ return page.parent.database_id ?? null;
441
495
  if (page.parent.type === 'block_id')
442
496
  return page.parent.block_id ?? null;
443
497
  return null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@moxn/kb-migrate",
3
- "version": "0.4.22",
3
+ "version": "0.4.23",
4
4
  "description": "Migration tool for importing documents into Moxn Knowledge Base from local files, Notion, Google Docs, and more",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",