@tk_wfl/o2n-core 0.1.2

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 (53) hide show
  1. package/dist/converter.d.ts +48 -0
  2. package/dist/converter.d.ts.map +1 -0
  3. package/dist/converter.js +264 -0
  4. package/dist/converter.js.map +1 -0
  5. package/dist/credentials.d.ts +13 -0
  6. package/dist/credentials.d.ts.map +1 -0
  7. package/dist/credentials.js +33 -0
  8. package/dist/credentials.js.map +1 -0
  9. package/dist/index.d.ts +13 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +13 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/migrator.d.ts +16 -0
  14. package/dist/migrator.d.ts.map +1 -0
  15. package/dist/migrator.js +402 -0
  16. package/dist/migrator.js.map +1 -0
  17. package/dist/notion-blocks.d.ts +23 -0
  18. package/dist/notion-blocks.d.ts.map +1 -0
  19. package/dist/notion-blocks.js +91 -0
  20. package/dist/notion-blocks.js.map +1 -0
  21. package/dist/notion-client.d.ts +161 -0
  22. package/dist/notion-client.d.ts.map +1 -0
  23. package/dist/notion-client.js +187 -0
  24. package/dist/notion-client.js.map +1 -0
  25. package/dist/notion-db.d.ts +15 -0
  26. package/dist/notion-db.d.ts.map +1 -0
  27. package/dist/notion-db.js +88 -0
  28. package/dist/notion-db.js.map +1 -0
  29. package/dist/planner.d.ts +16 -0
  30. package/dist/planner.d.ts.map +1 -0
  31. package/dist/planner.js +60 -0
  32. package/dist/planner.js.map +1 -0
  33. package/dist/property-mapping.d.ts +17 -0
  34. package/dist/property-mapping.d.ts.map +1 -0
  35. package/dist/property-mapping.js +68 -0
  36. package/dist/property-mapping.js.map +1 -0
  37. package/dist/report.d.ts +6 -0
  38. package/dist/report.d.ts.map +1 -0
  39. package/dist/report.js +51 -0
  40. package/dist/report.js.map +1 -0
  41. package/dist/scanner.d.ts +14 -0
  42. package/dist/scanner.d.ts.map +1 -0
  43. package/dist/scanner.js +202 -0
  44. package/dist/scanner.js.map +1 -0
  45. package/dist/state.d.ts +39 -0
  46. package/dist/state.d.ts.map +1 -0
  47. package/dist/state.js +99 -0
  48. package/dist/state.js.map +1 -0
  49. package/dist/types.d.ts +126 -0
  50. package/dist/types.d.ts.map +1 -0
  51. package/dist/types.js +3 -0
  52. package/dist/types.js.map +1 -0
  53. package/package.json +48 -0
@@ -0,0 +1,68 @@
1
+ const DATE_RE = /^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}(:\d{2})?(Z|[+-]\d{2}:?\d{2})?)?$/;
2
+ const URL_RE = /^https?:\/\//i;
3
+ /**
4
+ * §7.2 型推論規則: YAML値 → Notionプロパティ型
5
+ */
6
+ export function inferPropertyType(key, sampleValues) {
7
+ if (key === 'title')
8
+ return 'title';
9
+ if (key === 'tags')
10
+ return 'multi_select';
11
+ if (/^(created|updated)/i.test(key))
12
+ return 'date';
13
+ const nonNull = sampleValues.filter((v) => v !== undefined && v !== null);
14
+ if (nonNull.length === 0)
15
+ return 'rich_text';
16
+ // gray-matter(js-yaml)は無引用の YYYY-MM-DD 等をJSのDate型にパースするため先に判定する
17
+ const allDateInstance = nonNull.every((v) => v instanceof Date);
18
+ if (allDateInstance)
19
+ return 'date';
20
+ const allBoolean = nonNull.every((v) => typeof v === 'boolean');
21
+ if (allBoolean)
22
+ return 'checkbox';
23
+ const allNumber = nonNull.every((v) => typeof v === 'number');
24
+ if (allNumber)
25
+ return 'number';
26
+ const allArrayOfString = nonNull.every((v) => Array.isArray(v) && v.every((x) => typeof x === 'string'));
27
+ if (allArrayOfString)
28
+ return 'multi_select';
29
+ const allString = nonNull.every((v) => typeof v === 'string');
30
+ if (allString) {
31
+ const allDate = nonNull.every((v) => DATE_RE.test(v));
32
+ if (allDate)
33
+ return 'date';
34
+ const allUrl = nonNull.every((v) => URL_RE.test(v));
35
+ if (allUrl)
36
+ return 'url';
37
+ return 'rich_text';
38
+ }
39
+ // ネストしたobject等 → JSON文字列化してrich_text(+レポートは呼び出し側で記録)
40
+ return 'rich_text';
41
+ }
42
+ export function buildFrontmatterMappingsForFolder(notesFrontmatter) {
43
+ const keyValues = new Map();
44
+ for (const fm of notesFrontmatter) {
45
+ for (const [k, v] of Object.entries(fm)) {
46
+ const list = keyValues.get(k) ?? [];
47
+ list.push(v);
48
+ keyValues.set(k, list);
49
+ }
50
+ }
51
+ return [...keyValues.entries()].map(([key, values]) => ({
52
+ key,
53
+ notionPropertyType: inferPropertyType(key, values),
54
+ }));
55
+ }
56
+ /** rich_text値が2000文字を超える場合の切り詰め。全文は本文冒頭メタcalloutへ退避+レポート対象 */
57
+ export function truncateRichText(value, maxLen = 2000) {
58
+ if (value.length <= maxLen)
59
+ return { text: value, truncated: false };
60
+ return { text: value.slice(0, maxLen), truncated: true };
61
+ }
62
+ /** multi_selectの選択肢が100を超える場合、超過分をrich_textに退避 */
63
+ export function splitMultiSelectOverflow(values, maxOptions = 100) {
64
+ if (values.length <= maxOptions)
65
+ return { kept: values, overflow: [] };
66
+ return { kept: values.slice(0, maxOptions), overflow: values.slice(maxOptions) };
67
+ }
68
+ //# sourceMappingURL=property-mapping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"property-mapping.js","sourceRoot":"","sources":["../src/property-mapping.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { MigrationReport, ReportEntry, StateFile } from './types.js';
2
+ export declare function reportPath(vaultPath: string): string;
3
+ export declare function buildReport(state: StateFile, entries: ReportEntry[]): MigrationReport;
4
+ export declare function renderReportMarkdown(report: MigrationReport, state: StateFile): string;
5
+ export declare function writeReport(vaultPath: string, report: MigrationReport, state: StateFile): Promise<void>;
6
+ //# sourceMappingURL=report.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.d.ts","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE1E,wBAAgB,UAAU,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEpD;AAUD,wBAAgB,WAAW,CAAC,KAAK,EAAE,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,GAAG,eAAe,CAGrF;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,GAAG,MAAM,CA6BtF;AAED,wBAAsB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAI7G"}
package/dist/report.js ADDED
@@ -0,0 +1,51 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { stateDir } from './state.js';
4
+ export function reportPath(vaultPath) {
5
+ return path.join(stateDir(vaultPath), 'report.md');
6
+ }
7
+ const CATEGORY_LABEL = {
8
+ skipped: 'スキップ',
9
+ unresolved_link: '未解決リンク',
10
+ oversized_file: 'サイズ超過ファイル',
11
+ downgraded: '降格変換',
12
+ warning: '警告',
13
+ };
14
+ export function buildReport(state, entries) {
15
+ const successCount = Object.values(state.notes).filter((n) => n.status === 'done').length;
16
+ return { successCount, entries };
17
+ }
18
+ export function renderReportMarkdown(report, state) {
19
+ const lines = [];
20
+ lines.push('# Migration Report', '');
21
+ lines.push(`- 成功: ${report.successCount}件`);
22
+ const failed = Object.entries(state.notes).filter(([, n]) => n.status === 'failed');
23
+ const skipped = Object.entries(state.notes).filter(([, n]) => n.status === 'skipped');
24
+ lines.push(`- 失敗: ${failed.length}件`);
25
+ lines.push(`- スキップ: ${skipped.length}件`);
26
+ lines.push('');
27
+ for (const category of Object.keys(CATEGORY_LABEL)) {
28
+ const items = report.entries.filter((e) => e.category === category);
29
+ if (items.length === 0)
30
+ continue;
31
+ lines.push(`## ${CATEGORY_LABEL[category]} (${items.length}件)`, '');
32
+ for (const item of items) {
33
+ lines.push(`- \`${item.path}\`: ${item.message}`);
34
+ }
35
+ lines.push('');
36
+ }
37
+ if (failed.length > 0) {
38
+ lines.push('## 失敗ノート', '');
39
+ for (const [notePath, s] of failed) {
40
+ lines.push(`- \`${notePath}\`: ${s.error ?? '不明なエラー'}`);
41
+ }
42
+ lines.push('');
43
+ }
44
+ return lines.join('\n');
45
+ }
46
+ export async function writeReport(vaultPath, report, state) {
47
+ const dir = stateDir(vaultPath);
48
+ await fs.mkdir(dir, { recursive: true });
49
+ await fs.writeFile(reportPath(vaultPath), renderReportMarkdown(report, state), 'utf-8');
50
+ }
51
+ //# sourceMappingURL=report.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"report.js","sourceRoot":"","sources":["../src/report.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { NoteResolutionWarning, VaultInventory } from './types.js';
2
+ /**
3
+ * ファイル名(basename、拡張子有無どちらでも)→ 候補パス一覧のインデックスを作る
4
+ */
5
+ export declare function buildNameIndex(paths: string[]): Map<string, string[]>;
6
+ /**
7
+ * Obsidianの挙動に合わせたノート名解決: ファイル名一致→曖昧ならパス近接→なお曖昧なら警告
8
+ */
9
+ export declare function resolveByFilename(target: string, sourcePath: string, nameIndex: Map<string, string[]>): {
10
+ resolved: string | null;
11
+ warning?: NoteResolutionWarning;
12
+ };
13
+ export declare function scanVault(vaultPath: string): Promise<VaultInventory>;
14
+ //# sourceMappingURL=scanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.d.ts","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAGV,qBAAqB,EAErB,cAAc,EAEf,MAAM,YAAY,CAAC;AAkFpB;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAYrE;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,MAAM,EAAE,MAAM,EACd,UAAU,EAAE,MAAM,EAClB,SAAS,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,GAC/B;IAAE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,CAAC,EAAE,qBAAqB,CAAA;CAAE,CAoC9D;AAED,wBAAsB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,CAAC,CAqF1E"}
@@ -0,0 +1,202 @@
1
+ import { promises as fs } from 'node:fs';
2
+ import path from 'node:path';
3
+ import matter from 'gray-matter';
4
+ const EXCLUDED_DIRS = new Set(['.obsidian', '.trash', '.o2n']);
5
+ const ATTACHMENT_EXTENSIONS = new Set([
6
+ 'png', 'jpg', 'jpeg', 'gif', 'svg', 'webp', 'bmp',
7
+ 'pdf',
8
+ 'mp3', 'wav', 'm4a', 'ogg', 'flac',
9
+ 'mp4', 'mov', 'webm', 'mkv',
10
+ ]);
11
+ const NON_CONVERTIBLE_EXTENSIONS = new Set(['canvas']);
12
+ function toPosix(p) {
13
+ return p.split(path.sep).join('/');
14
+ }
15
+ async function walk(dir, root, out) {
16
+ const entries = await fs.readdir(dir, { withFileTypes: true });
17
+ for (const entry of entries) {
18
+ if (entry.isDirectory()) {
19
+ if (EXCLUDED_DIRS.has(entry.name))
20
+ continue;
21
+ await walk(path.join(dir, entry.name), root, out);
22
+ }
23
+ else if (entry.isFile()) {
24
+ out.push(toPosix(path.relative(root, path.join(dir, entry.name))));
25
+ }
26
+ }
27
+ }
28
+ const WIKILINK_RE = /(!?)\[\[([^\]|#]+)(?:#(\^?[^\]|]+))?(?:\|([^\]]+))?\]\]/g;
29
+ function parseWikiLinks(content) {
30
+ const results = [];
31
+ for (const m of content.matchAll(WIKILINK_RE)) {
32
+ const [raw, bang, targetRaw, anchor, alias] = m;
33
+ const isEmbed = bang === '!';
34
+ const target = (targetRaw ?? '').trim();
35
+ let heading;
36
+ let blockId;
37
+ if (anchor) {
38
+ if (anchor.startsWith('^')) {
39
+ blockId = anchor.slice(1).trim();
40
+ }
41
+ else {
42
+ heading = anchor.trim();
43
+ }
44
+ }
45
+ results.push({
46
+ isEmbed,
47
+ target,
48
+ heading,
49
+ blockId,
50
+ alias: alias?.trim(),
51
+ raw,
52
+ });
53
+ }
54
+ return results;
55
+ }
56
+ function dirSegments(relPath) {
57
+ const dir = path.posix.dirname(relPath);
58
+ return dir === '.' ? [] : dir.split('/');
59
+ }
60
+ function pathDistance(a, b) {
61
+ const aDirs = dirSegments(a);
62
+ const bDirs = dirSegments(b);
63
+ let common = 0;
64
+ while (common < aDirs.length && common < bDirs.length && aDirs[common] === bDirs[common]) {
65
+ common += 1;
66
+ }
67
+ return (aDirs.length - common) + (bDirs.length - common);
68
+ }
69
+ /**
70
+ * ファイル名(basename、拡張子有無どちらでも)→ 候補パス一覧のインデックスを作る
71
+ */
72
+ export function buildNameIndex(paths) {
73
+ const index = new Map();
74
+ for (const p of paths) {
75
+ const base = path.posix.basename(p);
76
+ const baseNoExt = base.replace(/\.[^.]+$/, '');
77
+ for (const key of [base, baseNoExt]) {
78
+ const list = index.get(key) ?? [];
79
+ list.push(p);
80
+ index.set(key, list);
81
+ }
82
+ }
83
+ return index;
84
+ }
85
+ /**
86
+ * Obsidianの挙動に合わせたノート名解決: ファイル名一致→曖昧ならパス近接→なお曖昧なら警告
87
+ */
88
+ export function resolveByFilename(target, sourcePath, nameIndex) {
89
+ const key = path.posix.basename(target);
90
+ const candidates = [...new Set(nameIndex.get(key) ?? [])];
91
+ if (candidates.length === 0) {
92
+ return {
93
+ resolved: null,
94
+ warning: { sourcePath, linkText: target, reason: 'not_found' },
95
+ };
96
+ }
97
+ if (candidates.length === 1) {
98
+ return { resolved: candidates[0] ?? null };
99
+ }
100
+ // 複数候補: パス近接(ディレクトリ距離が最小、かつ一意)を優先
101
+ let best = [];
102
+ let bestDistance = Infinity;
103
+ for (const c of candidates) {
104
+ const d = pathDistance(sourcePath, c);
105
+ if (d < bestDistance) {
106
+ bestDistance = d;
107
+ best = [c];
108
+ }
109
+ else if (d === bestDistance) {
110
+ best.push(c);
111
+ }
112
+ }
113
+ if (best.length === 1) {
114
+ return { resolved: best[0] ?? null };
115
+ }
116
+ return {
117
+ resolved: best[0] ?? null,
118
+ warning: {
119
+ sourcePath,
120
+ linkText: target,
121
+ reason: 'ambiguous',
122
+ candidates: best,
123
+ },
124
+ };
125
+ }
126
+ export async function scanVault(vaultPath) {
127
+ const allFiles = [];
128
+ await walk(vaultPath, vaultPath, allFiles);
129
+ const mdPaths = allFiles.filter((p) => p.endsWith('.md'));
130
+ const nameIndex = buildNameIndex(mdPaths);
131
+ const notes = [];
132
+ const wikiLinks = [];
133
+ const attachments = [];
134
+ const warnings = [];
135
+ const skipped = [];
136
+ const folderTree = {};
137
+ const frontmatterKeyStats = {};
138
+ for (const relPath of allFiles) {
139
+ const ext = relPath.split('.').pop()?.toLowerCase() ?? '';
140
+ if (NON_CONVERTIBLE_EXTENSIONS.has(ext)) {
141
+ skipped.push({ path: relPath, reason: `非対応ファイル形式 (.${ext}) はv1では変換されません` });
142
+ }
143
+ }
144
+ for (const relPath of mdPaths) {
145
+ const absPath = path.join(vaultPath, relPath);
146
+ const raw = await fs.readFile(absPath, 'utf-8');
147
+ const stat = await fs.stat(absPath);
148
+ const parsed = matter(raw);
149
+ notes.push({
150
+ path: relPath,
151
+ frontmatter: parsed.data ?? {},
152
+ content: parsed.content,
153
+ sizeBytes: stat.size,
154
+ });
155
+ for (const key of Object.keys(parsed.data ?? {})) {
156
+ frontmatterKeyStats[key] = (frontmatterKeyStats[key] ?? 0) + 1;
157
+ }
158
+ const dir = path.posix.dirname(relPath);
159
+ const dirKey = dir === '.' ? '' : dir;
160
+ folderTree[dirKey] = folderTree[dirKey] ?? [];
161
+ folderTree[dirKey].push(relPath);
162
+ for (const link of parseWikiLinks(parsed.content)) {
163
+ const linkExt = link.target.includes('.')
164
+ ? link.target.split('.').pop().toLowerCase()
165
+ : '';
166
+ const isAttachment = link.isEmbed && ATTACHMENT_EXTENSIONS.has(linkExt);
167
+ if (isAttachment) {
168
+ const attachmentIndex = buildNameIndex(allFiles.filter((p) => !p.endsWith('.md')));
169
+ const { resolved, warning } = resolveByFilename(link.target, relPath, attachmentIndex);
170
+ if (warning)
171
+ warnings.push(warning);
172
+ attachments.push({
173
+ sourcePath: relPath,
174
+ targetPath: resolved,
175
+ raw: link.raw,
176
+ extension: linkExt,
177
+ });
178
+ continue;
179
+ }
180
+ wikiLinks.push({
181
+ sourcePath: relPath,
182
+ target: link.target,
183
+ heading: link.heading,
184
+ blockId: link.blockId,
185
+ alias: link.alias,
186
+ isEmbed: link.isEmbed,
187
+ raw: link.raw,
188
+ });
189
+ }
190
+ }
191
+ return {
192
+ vaultPath,
193
+ notes,
194
+ attachments,
195
+ wikiLinks,
196
+ skipped,
197
+ warnings,
198
+ folderTree,
199
+ frontmatterKeyStats,
200
+ };
201
+ }
202
+ //# sourceMappingURL=scanner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scanner.js","sourceRoot":"","sources":["../src/scanner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { FileState, FolderState, NoteState, StateFile } from './types.js';
2
+ export declare function contentHash(content: string): string;
3
+ export declare function stateDir(vaultPath: string): string;
4
+ export declare function statePath(vaultPath: string): string;
5
+ /**
6
+ * .o2n/state.json の読み書きを担うストア。
7
+ * F5: 「API成功のたびに書き込む(クラッシュ耐性)」を満たすため、
8
+ * 更新のたびに即ディスクへ書き込む(tmpファイル→renameでアトミックに)。
9
+ * 書き込みはPromiseチェーンで直列化し、並行呼び出しでも壊れないようにする。
10
+ */
11
+ export declare class StateStore {
12
+ private readonly vaultPath;
13
+ private readonly data;
14
+ /**
15
+ * dry-run用: trueの場合、実在のstate.jsonは読み込む(正確な進捗表示のため)が、
16
+ * 一切ディスクに書き込まない。dry-runの結果が本実行の「作成済み」判定を
17
+ * 汚染してしまう不具合を防ぐため。
18
+ */
19
+ private readonly readOnly;
20
+ private writeChain;
21
+ private constructor();
22
+ static load(vaultPath: string, parentPageId: string, opts?: {
23
+ readOnly?: boolean;
24
+ }): Promise<StateStore>;
25
+ get snapshot(): StateFile;
26
+ getNote(notePath: string): NoteState | undefined;
27
+ setNote(notePath: string, state: NoteState): Promise<void>;
28
+ getFile(filePath: string): FileState | undefined;
29
+ setFile(filePath: string, state: FileState): Promise<void>;
30
+ getFolder(folderPath: string): FolderState | undefined;
31
+ setFolder(folderPath: string, state: FolderState): Promise<void>;
32
+ private persist;
33
+ private writeNow;
34
+ }
35
+ /**
36
+ * resume用の冪等性判定: 既にcreated以降で、本文ハッシュが一致すれば再作成不要
37
+ */
38
+ export declare function isNoteUpToDate(state: NoteState | undefined, currentHash: string): boolean;
39
+ //# sourceMappingURL=state.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.d.ts","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAE/E,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED,wBAAgB,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAElD;AAED,wBAAgB,SAAS,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEnD;AAED;;;;;GAKG;AACH,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,IAAI;IACrB;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAV3B,OAAO,CAAC,UAAU,CAAoC;IAEtD,OAAO;WAWM,IAAI,CAAC,SAAS,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,IAAI,GAAE;QAAE,QAAQ,CAAC,EAAE,OAAO,CAAA;KAAO,GAAG,OAAO,CAAC,UAAU,CAAC;IAelH,IAAI,QAAQ,IAAI,SAAS,CAExB;IAED,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAIhD,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAK1D,OAAO,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,GAAG,SAAS;IAIhD,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAK1D,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;IAItD,SAAS,CAAC,UAAU,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhE,OAAO,CAAC,OAAO;YAMD,QAAQ;CAOvB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,SAAS,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAIzF"}
package/dist/state.js ADDED
@@ -0,0 +1,99 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { promises as fs } from 'node:fs';
3
+ import path from 'node:path';
4
+ export function contentHash(content) {
5
+ return 'sha256:' + createHash('sha256').update(content, 'utf-8').digest('hex');
6
+ }
7
+ export function stateDir(vaultPath) {
8
+ return path.join(vaultPath, '.o2n');
9
+ }
10
+ export function statePath(vaultPath) {
11
+ return path.join(stateDir(vaultPath), 'state.json');
12
+ }
13
+ /**
14
+ * .o2n/state.json の読み書きを担うストア。
15
+ * F5: 「API成功のたびに書き込む(クラッシュ耐性)」を満たすため、
16
+ * 更新のたびに即ディスクへ書き込む(tmpファイル→renameでアトミックに)。
17
+ * 書き込みはPromiseチェーンで直列化し、並行呼び出しでも壊れないようにする。
18
+ */
19
+ export class StateStore {
20
+ vaultPath;
21
+ data;
22
+ readOnly;
23
+ writeChain = Promise.resolve();
24
+ constructor(vaultPath, data,
25
+ /**
26
+ * dry-run用: trueの場合、実在のstate.jsonは読み込む(正確な進捗表示のため)が、
27
+ * 一切ディスクに書き込まない。dry-runの結果が本実行の「作成済み」判定を
28
+ * 汚染してしまう不具合を防ぐため。
29
+ */
30
+ readOnly) {
31
+ this.vaultPath = vaultPath;
32
+ this.data = data;
33
+ this.readOnly = readOnly;
34
+ }
35
+ static async load(vaultPath, parentPageId, opts = {}) {
36
+ const readOnly = opts.readOnly ?? false;
37
+ const p = statePath(vaultPath);
38
+ try {
39
+ const raw = await fs.readFile(p, 'utf-8');
40
+ const data = JSON.parse(raw);
41
+ data.folders = data.folders ?? {};
42
+ return new StateStore(vaultPath, data, readOnly);
43
+ }
44
+ catch (err) {
45
+ if (err.code !== 'ENOENT')
46
+ throw err;
47
+ const fresh = { version: 1, parentPageId, notes: {}, files: {}, folders: {} };
48
+ return new StateStore(vaultPath, fresh, readOnly);
49
+ }
50
+ }
51
+ get snapshot() {
52
+ return this.data;
53
+ }
54
+ getNote(notePath) {
55
+ return this.data.notes[notePath];
56
+ }
57
+ setNote(notePath, state) {
58
+ this.data.notes[notePath] = state;
59
+ return this.persist();
60
+ }
61
+ getFile(filePath) {
62
+ return this.data.files[filePath];
63
+ }
64
+ setFile(filePath, state) {
65
+ this.data.files[filePath] = state;
66
+ return this.persist();
67
+ }
68
+ getFolder(folderPath) {
69
+ return this.data.folders[folderPath];
70
+ }
71
+ setFolder(folderPath, state) {
72
+ this.data.folders[folderPath] = state;
73
+ return this.persist();
74
+ }
75
+ persist() {
76
+ if (this.readOnly)
77
+ return Promise.resolve();
78
+ this.writeChain = this.writeChain.then(() => this.writeNow());
79
+ return this.writeChain;
80
+ }
81
+ async writeNow() {
82
+ const dir = stateDir(this.vaultPath);
83
+ await fs.mkdir(dir, { recursive: true });
84
+ const tmp = statePath(this.vaultPath) + `.tmp-${process.pid}`;
85
+ await fs.writeFile(tmp, JSON.stringify(this.data, null, 2), 'utf-8');
86
+ await fs.rename(tmp, statePath(this.vaultPath));
87
+ }
88
+ }
89
+ /**
90
+ * resume用の冪等性判定: 既にcreated以降で、本文ハッシュが一致すれば再作成不要
91
+ */
92
+ export function isNoteUpToDate(state, currentHash) {
93
+ if (!state)
94
+ return false;
95
+ if (state.status === 'pending' || state.status === 'failed')
96
+ return false;
97
+ return state.contentHash === currentHash;
98
+ }
99
+ //# sourceMappingURL=state.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"state.js","sourceRoot":"","sources":["../src/state.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,126 @@
1
+ export interface WikiLink {
2
+ /** リンク元ノートの相対パス */
3
+ sourcePath: string;
4
+ /** [[ノート]] や [[ノート|表示名]] の "ノート" 部分(生の表記) */
5
+ target: string;
6
+ /** 見出しアンカー [[ノート#見出し]] */
7
+ heading?: string;
8
+ /** ブロック参照 [[ノート#^id]] */
9
+ blockId?: string;
10
+ /** [[ノート|表示名]] の表示名 */
11
+ alias?: string;
12
+ /** ![[...]] の埋め込みか */
13
+ isEmbed: boolean;
14
+ /** 生のマッチ文字列(本文中の置換に使う) */
15
+ raw: string;
16
+ }
17
+ export interface AttachmentRef {
18
+ /** 参照元ノートの相対パス */
19
+ sourcePath: string;
20
+ /** 添付ファイルの相対パス(vaultルートから解決済み。未解決ならnull) */
21
+ targetPath: string | null;
22
+ /** リンク内の生表記 */
23
+ raw: string;
24
+ /** 拡張子 */
25
+ extension: string;
26
+ }
27
+ export interface NoteResolutionWarning {
28
+ sourcePath: string;
29
+ linkText: string;
30
+ reason: 'ambiguous' | 'not_found';
31
+ candidates?: string[];
32
+ }
33
+ export interface NoteRecord {
34
+ /** vaultルートからの相対パス(POSIX区切り) */
35
+ path: string;
36
+ /** frontmatter(gray-matterのdata) */
37
+ frontmatter: Record<string, unknown>;
38
+ /** frontmatterを除いた本文 */
39
+ content: string;
40
+ /** ファイルサイズ(バイト) */
41
+ sizeBytes: number;
42
+ }
43
+ export interface SkippedFile {
44
+ path: string;
45
+ reason: string;
46
+ }
47
+ export interface VaultInventory {
48
+ vaultPath: string;
49
+ notes: NoteRecord[];
50
+ attachments: AttachmentRef[];
51
+ wikiLinks: WikiLink[];
52
+ skipped: SkippedFile[];
53
+ warnings: NoteResolutionWarning[];
54
+ /** フォルダパス→直下ノートパス一覧 */
55
+ folderTree: Record<string, string[]>;
56
+ /** frontmatterキーの出現回数 */
57
+ frontmatterKeyStats: Record<string, number>;
58
+ }
59
+ export type FolderMode = 'page_tree' | 'database';
60
+ export interface FolderPlan {
61
+ folderPath: string;
62
+ mode: FolderMode;
63
+ /** database モード時の提案理由(自動提案の場合) */
64
+ suggestionReason?: string;
65
+ }
66
+ export interface FrontmatterMapping {
67
+ key: string;
68
+ notionPropertyType: 'title' | 'rich_text' | 'number' | 'checkbox' | 'date' | 'multi_select' | 'url';
69
+ }
70
+ export interface MigrationPlan {
71
+ version: 1;
72
+ vaultPath: string;
73
+ parentPageId: string;
74
+ folders: FolderPlan[];
75
+ frontmatterMappings: Record<string, FrontmatterMapping[]>;
76
+ skipList: string[];
77
+ }
78
+ export type NoteStatus = 'pending' | 'created' | 'linked' | 'attached' | 'done' | 'failed' | 'skipped';
79
+ export interface NoteState {
80
+ status: NoteStatus;
81
+ pageId?: string;
82
+ pageUrl?: string;
83
+ contentHash?: string;
84
+ error?: string;
85
+ }
86
+ export type FileStatus = 'pending' | 'uploaded' | 'attached' | 'failed' | 'skipped';
87
+ export interface FileState {
88
+ status: FileStatus;
89
+ fileUploadId?: string;
90
+ error?: string;
91
+ }
92
+ export type FolderStatus = 'pending' | 'created' | 'failed';
93
+ export interface FolderState {
94
+ status: FolderStatus;
95
+ kind: 'page' | 'database';
96
+ notionId: string;
97
+ /** databaseの場合のdata source id(§16-4: 正確な構造は要検証) */
98
+ dataSourceId?: string;
99
+ error?: string;
100
+ }
101
+ export interface StateFile {
102
+ version: 1;
103
+ parentPageId: string;
104
+ notes: Record<string, NoteState>;
105
+ files: Record<string, FileState>;
106
+ /**
107
+ * 仕様書§5 F5のstate.jsonスキーマ例には無いキー。
108
+ * page_treeモードのフォルダ=親ページ、databaseモードのフォルダ=DBのID管理に必要なため追加。
109
+ * 差分はdocs/questions.mdに記録。
110
+ */
111
+ folders: Record<string, FolderState>;
112
+ }
113
+ export interface ReportEntry {
114
+ category: 'skipped' | 'unresolved_link' | 'oversized_file' | 'downgraded' | 'warning';
115
+ path: string;
116
+ message: string;
117
+ }
118
+ export interface MigrationReport {
119
+ successCount: number;
120
+ entries: ReportEntry[];
121
+ }
122
+ export interface ConversionResult {
123
+ markdown: string;
124
+ entries: ReportEntry[];
125
+ }
126
+ //# sourceMappingURL=types.d.ts.map