@im-nobsidian/core 0.3.0 → 0.3.1

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.
package/dist/index.d.ts CHANGED
@@ -730,16 +730,33 @@ declare class NotionClient {
730
730
  private fetchDatabaseModern;
731
731
  getDatabaseTitle(databaseId: string): Promise<string>;
732
732
  /**
733
- * 발견된 DB 가 **동기화 가능한지**(접근 가능한 data source 가 있는지) 1회 호출로 판별한다.
733
+ * 발견된 DB 가 **동기화 가능한지**(접근 가능한 data source 가 있는지) 판별한다.
734
734
  * 신 모델(2025-09-03)에서 링크드 DB·미공유 데이터 소스·삭제 DB 는 `data_sources` 가
735
735
  * 비어, 행 조회(`dataSources.query`)가 404 로 실패하고 그 전에 생성된 빈 폴더/`.base` 만
736
736
  * 남긴다. 발견 단계에서 미리 걸러 **빈 폴더/.base 오염 + 매 pull 의 404 노이즈**를 차단한다.
737
737
  * 제목은 함께 반환해 호출처가 추가 조회 없이 폴더명을 잡게 한다.
738
+ *
739
+ * F25: 원본이 같은 공유 범위에 있으면 linked view 컨테이너도 `data_sources` 가 **채워져**
740
+ * 온다(실측 — 워크스페이스 8건). 이때 data source 의 parent database 는 원본이므로,
741
+ * 첫 소스의 parent 가 자신이 아니면 `linkedOriginalDbId` 로 알려 컨테이너가 원본과 같은
742
+ * 행 집합을 이중 pull(폴더 릴레이 재배치 churn)하는 것을 발견 단계에서 차단한다.
738
743
  */
739
744
  getDatabaseSyncability(databaseId: string): Promise<{
740
745
  title: string;
741
746
  queryable: boolean;
747
+ linkedOriginalDbId?: string;
742
748
  }>;
749
+ /**
750
+ * linked database view 컨테이너를 원본 DB 로 해소한다.
751
+ * 컨테이너 자체는 `data_sources` 가 비어 행 조회가 불가능하지만(getDatabaseSyncability
752
+ * queryable=false), Views API 의 뷰 상세에는 원본 `data_source_id` 가 실리고 그 data source
753
+ * 의 parent 가 원본 database 다. 해소 실패(뷰 없음/권한 없음/자기참조)는 null — 호출처가
754
+ * 기존대로 접근 불가 처리한다.
755
+ */
756
+ resolveLinkedDatabase(databaseId: string): Promise<{
757
+ originalDbId: string;
758
+ viewName: string;
759
+ } | null>;
743
760
  getDatabaseSchema(databaseId: string): Promise<Record<string, {
744
761
  id: string;
745
762
  type: string;
@@ -804,7 +821,13 @@ declare class NotionClient {
804
821
  fetchAllChildrenDeep(blockId: string): Promise<BlockObjectResponse[]>;
805
822
  appendChildren(blockId: string, children: unknown[]): Promise<void>;
806
823
  deleteBlock(blockId: string): Promise<void>;
824
+ /** single_part 업로드 상한 (Notion API 규격 20MB) */
825
+ private static readonly SINGLE_PART_MAX_BYTES;
826
+ /** multi_part 파트 크기 — 규격상 마지막 파트 제외 5~20MB, 10MB 고정 사용 */
827
+ private static readonly MULTI_PART_CHUNK_BYTES;
807
828
  uploadFile(fileData: Blob, filename: string, contentType: string): Promise<string>;
829
+ /** 20MB 초과 파일의 multi_part 업로드 — part_number 는 API 규격상 문자열("1"부터) */
830
+ private uploadFileMultiPart;
808
831
  search(params: {
809
832
  query?: string;
810
833
  filter?: {
@@ -1080,7 +1103,19 @@ declare class SyncOrchestrator {
1080
1103
  private buildDiscoveredDbConfig;
1081
1104
  /** 접근 불가 DB denylist 를 상태 메타에서 로드한다(하이픈 정규화). */
1082
1105
  private loadInaccessibleDbIds;
1106
+ /** linked view 컨테이너 → 원본 DB 매핑을 상태 메타에서 로드한다(nohyph → nohyph). */
1107
+ private loadLinkedDbMap;
1083
1108
  private pullDiscoveredDatabases;
1109
+ /**
1110
+ * pull 산출 md 의 인라인 DB placeholder(`**제목** *(Notion DB)*` + 보존 마커)를
1111
+ * `![[<실제 .base 경로>|제목]]` 임베드로 재작성한다(F22) — folder note 본문에서
1112
+ * DB 가 텍스트 한 줄이 아니라 실제 Bases 뷰로 보이게 하는 마지막 조각. linked view
1113
+ * 컨테이너는 linkedMap 을 거쳐 원본 DB 의 .base 로 향한다. .base 경로는 DatabaseSyncer
1114
+ * 가 실제 기록한 경로(baseFileInfo)가 SSOT — 폴더명과 .base 파일명은 새니타이즈 규칙이
1115
+ * 달라(공백→하이픈 vs 공백 보존) localFolder 로 추측하면 깨진 임베드가 된다(E2E 실측
1116
+ * 91/158건). 최종적으로 .base 실존까지 확인해, 없으면 placeholder 를 마커째 보존한다.
1117
+ */
1118
+ private rewriteDbPlaceholderEmbeds;
1084
1119
  private resolveNotionLinks;
1085
1120
  private ensureDbSchema;
1086
1121
  private pushCreate;
@@ -1141,6 +1176,13 @@ declare class SyncOrchestrator {
1141
1176
  private pushUpdatePage;
1142
1177
  private fetchPageMarkdown;
1143
1178
  private collectInlineDbRefs;
1179
+ /**
1180
+ * 이번 라운드에 기록된 DB row md 를 읽어 본문의 인라인 DB 참조를 수집한다.
1181
+ * row 페이지는 DatabaseSyncer 자체 파이프라인으로 변환돼 fetchPageMarkdown 의
1182
+ * _inlineDbRefs 수집을 타지 않는다 — 디스크에 남은 보존 마커가 유일한 발견 신호다.
1183
+ * 부모 Notion 페이지 id 는 state DB 역조회(getByPath)로 얻는다(row upsert 직후라 항상 존재).
1184
+ */
1185
+ private collectRowInlineRefs;
1144
1186
  private resolveNotionIdWikilinks;
1145
1187
  private extractParentId;
1146
1188
  private resolveBlockToPageId;
@@ -1251,6 +1293,16 @@ declare class ImageHandler {
1251
1293
  private downloadFile;
1252
1294
  private isNotionImageUrl;
1253
1295
  private isNotionFileUrl;
1296
+ /**
1297
+ * notion-hosted 파일 URL 하나를 로컬 첨부로 로컬라이즈한다(P3-A).
1298
+ * DB 행의 `files` 속성처럼 본문 밖에 있는 서명 URL 용 — 서명 URL 은 약 1시간 뒤
1299
+ * 만료되어 frontmatter 에 남으면 깨진 링크가 되고, push 로 되밀면 Notion 원본이
1300
+ * external(만료 URL)로 오염된다(실측). notion-hosted 가 아닌 URL(사용자가 넣은
1301
+ * 진짜 외부 링크)은 다운로드 대상이 아니므로 null 을 반환한다.
1302
+ *
1303
+ * @returns 로컬 첨부 경로(`attachments/...`), 다운로드 대상이 아니거나 실패 시 null
1304
+ */
1305
+ localizeNotionFileUrl(url: string, caption: string): Promise<string | null>;
1254
1306
  private guessExtension;
1255
1307
  private guessFileExtension;
1256
1308
  }
@@ -1274,6 +1326,13 @@ declare class FileHandler {
1274
1326
  private readonly sema;
1275
1327
  constructor(vaultFs: VaultFS, notionClient: NotionClient, stateDb: IStateDB, concurrency?: number);
1276
1328
  pushFilesForFolder(folderPageId: string, folderPath: string): Promise<FileUploadResult[]>;
1329
+ /**
1330
+ * 업로드 후보 목록 — 도구 내부 산출물(`.base`/`.notion.json`)은 제외한다.
1331
+ * node 측 VaultFS 는 과거 `.base` 를 워커에서 빼는 식으로 우회했지만, Obsidian
1332
+ * 어댑터는 모든 파일을 반환하므로 플러그인 push 가 사이드카를 Notion 첨부로
1333
+ * 오염시켰다. 소비자인 여기서 일괄 차단해 구현체 간 동작을 통일한다.
1334
+ */
1335
+ private listUploadableFiles;
1277
1336
  pushAllFiles(): Promise<FileUploadResult[]>;
1278
1337
  downloadFileBlock(url: string, filename: string, targetFolder: string): Promise<FileDownloadResult>;
1279
1338
  downloadFileBlocks(blocks: Array<{
@@ -1309,6 +1368,12 @@ interface DatabaseSyncResult {
1309
1368
  * 엉뚱한 행을 골라 정작 바뀐 행의 relation UUID 를 영영 해소하지 못했다(M3). push 경로는 항상 빈 배열.
1310
1369
  */
1311
1370
  writtenPaths: string[];
1371
+ /**
1372
+ * F25: 이 config 가 linked view 컨테이너로 판정된 경우 data source 원본 database id.
1373
+ * 행 처리는 원본 config 에 양보하고 건너뛰었으므로, 호출처(디스커버리 캐시)는 이 config
1374
+ * 를 제거하고 linked 매핑을 기록해야 다음 pull 부터 이중 방문 자체가 사라진다.
1375
+ */
1376
+ linkedOriginalDbId?: string;
1312
1377
  }
1313
1378
  declare class DatabaseSyncer {
1314
1379
  private readonly config;
@@ -1320,10 +1385,28 @@ declare class DatabaseSyncer {
1320
1385
  private readonly propertyMapper;
1321
1386
  private readonly baseFileGenerator;
1322
1387
  private readonly sidecarGenerator;
1388
+ /**
1389
+ * 이번 프로세스가 실제로 기록한 .base 경로/DB 제목 (databaseId nohyph → info).
1390
+ * placeholder 임베드 재작성(F22)의 SSOT — 폴더명(하이픈 새니타이즈)과 .base 파일명
1391
+ * (sanitizeFileName: 공백·점 보존)은 규칙이 달라 localFolder 로 추측한 경로는 깨진
1392
+ * 임베드가 된다. generateBaseFile 은 매 pull 모든 DB 에 대해 실행되므로 pull 종료
1393
+ * 시점에는 성공한 DB 전체가 채워져 있다.
1394
+ */
1395
+ readonly baseFileInfo: Map<string, {
1396
+ basePath: string;
1397
+ title: string;
1398
+ }>;
1323
1399
  constructor(config: Config, stateDb: IStateDB, notionClient: NotionClient, vaultFs: VaultFS, pipeline: ConversionPipeline, imageHandler: ImageHandler);
1324
1400
  pullAll(): Promise<DatabaseSyncResult>;
1325
1401
  pushAll(): Promise<DatabaseSyncResult>;
1326
- pullDatabase(dbConfig: DatabaseSyncConfig): Promise<DatabaseSyncResult>;
1402
+ pullDatabase(dbConfig: DatabaseSyncConfig, opts?: {
1403
+ /**
1404
+ * F25: database id(하이픈 유무 무관) → 그 DB 행이 사는 로컬 폴더. 디스커버리 캐시가
1405
+ * 아는 DB 만 반환하고 모르면 null. linked view 컨테이너 판정 시 원본 폴더를 아는
1406
+ * 경우에만 행 소유를 양보한다 — 원본이 미발견이면 이 컨테이너가 유일한 접근 통로다.
1407
+ */
1408
+ resolveDbFolder?: (databaseId: string) => string | null;
1409
+ }): Promise<DatabaseSyncResult>;
1327
1410
  private pullDatabaseViews;
1328
1411
  private generateBaseFile;
1329
1412
  /**
@@ -1338,6 +1421,15 @@ declare class DatabaseSyncer {
1338
1421
  * 결정적 직렬화라 동일 DB 상태 → 동일 바이트 → 멱등(drift/churn 0).
1339
1422
  */
1340
1423
  private generateSidecar;
1424
+ /**
1425
+ * DB 행 `files` 속성의 notion-hosted 서명 URL 을 로컬 첨부로 로컬라이즈한다(P3-A).
1426
+ * 서명 URL 은 약 1시간 뒤 만료되어 frontmatter 에 남으면 깨진 링크가 되고, push 로
1427
+ * 되밀면 Notion 원본이 만료 URL 의 external 파일로 오염된다(실측). 다운로드 성공분은
1428
+ * `[[attachments/..]]` 위키링크로 대체한다 — Bases 카드 `image:` 는 위키링크 스칼라를
1429
+ * 렌더하므로 갤러리 커버도 유지된다. 원본 파일명은 raw 속성의 `name` 에서 취한다
1430
+ * (extractValue 는 URL 만 남긴다). 사용자가 넣은 진짜 외부 URL 은 그대로 둔다.
1431
+ */
1432
+ private localizeFileProperties;
1341
1433
  private pullDatabasePage;
1342
1434
  private pushDatabase;
1343
1435
  /**