im-nobsidian 0.14.1 → 0.16.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.
- package/dist/index.js +220 -36
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -134,6 +134,40 @@ function restoredLines(paths, dryRun = false) {
|
|
|
134
134
|
...pathLines(paths)
|
|
135
135
|
];
|
|
136
136
|
}
|
|
137
|
+
function relinkedLines(paths, pending) {
|
|
138
|
+
if (pending) {
|
|
139
|
+
return [
|
|
140
|
+
chalk.cyan(
|
|
141
|
+
"\u21BB \uC0C1\uD0DC \uAE30\uB85D\uC5D0 Notion \uACFC \uC774\uC740 \uB178\uD2B8\uAC00 \uC5C6\uC2B5\uB2C8\uB2E4 \u2014 \uBC1B\uC73C\uBA74 \uAC19\uC740 \uC790\uB9AC\uC5D0 \uC788\uB294 \uB178\uD2B8\uB294 \uC0C8\uB85C \uB9CC\uB4E4\uC9C0 \uC54A\uACE0 \uADF8 \uD398\uC774\uC9C0\uC640 \uB2E4\uC2DC \uC787\uC2B5\uB2C8\uB2E4(\uAE00\uC774 \uB2E4\uB974\uBA74 \uCDA9\uB3CC). \uBBF8\uB9AC \uBCF4\uAE30\uB294 \uADF8 \uB178\uD2B8\uB4E4\uB3C4 \uC0DD\uC131\uC73C\uB85C \uC149\uB2C8\uB2E4"
|
|
142
|
+
)
|
|
143
|
+
];
|
|
144
|
+
}
|
|
145
|
+
if (!paths || paths.length === 0) return [];
|
|
146
|
+
return [
|
|
147
|
+
chalk.cyan(
|
|
148
|
+
`\u21BB \uC0C1\uD0DC \uAE30\uB85D\uC5D0 \uC5C6\uB358 \uB178\uD2B8 ${paths.length}\uAC1C\uB97C \uAC19\uC740 \uC790\uB9AC\uC758 Notion \uD398\uC774\uC9C0\uC640 \uB2E4\uC2DC \uC774\uC5C8\uC2B5\uB2C8\uB2E4 \u2014 \uC0C8\uB85C \uBC1B\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4:`
|
|
149
|
+
),
|
|
150
|
+
...pathLines(paths)
|
|
151
|
+
];
|
|
152
|
+
}
|
|
153
|
+
function mergedLines(paths, then, dryRun = false) {
|
|
154
|
+
if (!paths || paths.length === 0) return [];
|
|
155
|
+
return [
|
|
156
|
+
chalk.cyan(
|
|
157
|
+
`\u21BB \uC591\uCABD\uC774 \uB2E4\uB978 \uC904\uC744 \uACE0\uCE5C \uB178\uD2B8 ${paths.length}\uAC1C\uB97C ${dryRun ? "\uD569\uCE69\uB2C8\uB2E4" : "\uD569\uCCE4\uC2B5\uB2C8\uB2E4"} \u2014 ` + (then === "push" ? "push \uD558\uBA74 \uD569\uCE5C \uAE00\uC744 Notion \uC5D0 \uC62C\uB9BD\uB2C8\uB2E4:" : "\uD569\uCE5C \uAE00\uC744 Notion \uC5D0 \uC62C\uB9BD\uB2C8\uB2E4:")
|
|
158
|
+
),
|
|
159
|
+
...pathLines(paths)
|
|
160
|
+
];
|
|
161
|
+
}
|
|
162
|
+
function plannedConflictLines(paths) {
|
|
163
|
+
if (paths.length === 0) return [];
|
|
164
|
+
return [
|
|
165
|
+
chalk.magenta(
|
|
166
|
+
`\u26A0 \uC591\uCABD\uC744 \uACE0\uCE5C \uB178\uD2B8 ${paths.length}\uAC1C\uB294 \uBC1B\uC73C\uBA74 \uCDA9\uB3CC\uB85C \uB0A8\uC2B5\uB2C8\uB2E4 \u2014 \uBC1B\uC740 \uB4A4 nobsi resolve \uB85C \uACE0\uB974\uC138\uC694:`
|
|
167
|
+
),
|
|
168
|
+
...pathLines(paths)
|
|
169
|
+
];
|
|
170
|
+
}
|
|
137
171
|
function schemaFollowedLines(followed) {
|
|
138
172
|
const lines = [];
|
|
139
173
|
for (const db of followed ?? []) {
|
|
@@ -169,6 +203,9 @@ function schemaEditLines(edits, dryRun = false) {
|
|
|
169
203
|
...edit.renamed.length > 0 ? [`\uC774\uB984 ${edit.renamed.map(([from, to]) => `${from} \u2192 ${to}`).join(", ")}`] : [],
|
|
170
204
|
...edit.retyped.length > 0 ? [`\uD0C0\uC785 ${edit.retyped.map(([name, type]) => `${name} \u2192 ${type}`).join(", ")}`] : [],
|
|
171
205
|
...edit.redefined.length > 0 ? [`\uC124\uC815 ${edit.redefined.join(", ")}`] : [],
|
|
206
|
+
...(edit.renamedOptions ?? []).map(
|
|
207
|
+
([name, from, to]) => `${name} \uC635\uC158 \uC774\uB984 ${from} \u2192 ${to}(\uADF8 \uAC12\uC744 \uAC00\uC9C4 \uD589\uB3C4 \uC62E\uAE41\uB2C8\uB2E4)`
|
|
208
|
+
),
|
|
172
209
|
...(edit.droppedOptions ?? []).map(
|
|
173
210
|
([name, options]) => `${name} \uC635\uC158 \uC9C0\uC6C0 ${options.join(", ")}(\uADF8 \uAC12\uC744 \uAC00\uC9C4 \uD589\uC740 Notion \uC5D0\uC11C \uBE48 \uAC12\uC774 \uB429\uB2C8\uB2E4)`
|
|
174
211
|
)
|
|
@@ -178,6 +215,12 @@ function schemaEditLines(edits, dryRun = false) {
|
|
|
178
215
|
}
|
|
179
216
|
function viewEditLines(edits, dryRun = false) {
|
|
180
217
|
return (edits ?? []).map((edit) => {
|
|
218
|
+
if (edit.action === "created") {
|
|
219
|
+
return `${icons.success} .base \uC5D0\uC11C \uB354\uD55C \uBCF4\uAE30\uB97C Notion \uC5D0 ${dryRun ? "\uB9CC\uB4ED\uB2C8\uB2E4" : "\uB9CC\uB4E4\uC5C8\uC2B5\uB2C8\uB2E4"} \u2014 ${edit.path}: ${edit.view}`;
|
|
220
|
+
}
|
|
221
|
+
if (edit.action === "deleted") {
|
|
222
|
+
return `${icons.success} .base \uC5D0\uC11C \uC9C0\uC6B4 \uBCF4\uAE30\uB97C Notion \uC5D0\uC11C ${dryRun ? "\uC9C0\uC6C1\uB2C8\uB2E4" : "\uC9C0\uC6E0\uC2B5\uB2C8\uB2E4"} \u2014 ${edit.path}: ${edit.view}`;
|
|
223
|
+
}
|
|
181
224
|
const name = edit.from ? `${edit.from} \u2192 ${edit.view}` : edit.view;
|
|
182
225
|
return `${icons.success} .base \uC5D0\uC11C \uACE0\uCE5C \uBCF4\uAE30\uB97C Notion \uC5D0 ${dryRun ? "\uC62C\uB9BD\uB2C8\uB2E4" : "\uC62C\uB838\uC2B5\uB2C8\uB2E4"} \u2014 ${edit.path}: ${name} (${edit.changed.map((field) => VIEW_FIELD_LABELS[field]).join(" \xB7 ")})`;
|
|
183
226
|
});
|
|
@@ -206,7 +249,7 @@ function heldBaseLines(paths) {
|
|
|
206
249
|
if (!paths || paths.length === 0) return [];
|
|
207
250
|
return [
|
|
208
251
|
chalk.yellow(
|
|
209
|
-
`\u26A0 \uBCF4\uAE30\uB97C \uACE0\uCE5C .base ${paths.length}\uAC1C\uB294 \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4 \u2014 push \uD558\uBA74 \uACE0\uCE5C \uBCF4\uAE30 \uC774\uB984 \xB7 \uC5F4 \xB7 \uC815\uB82C \xB7 \uD544\uD130\uB97C Notion \uC5D0 \uC62C\uB9BD\uB2C8\uB2E4:`
|
|
252
|
+
`\u26A0 \uBCF4\uAE30\uB97C \uACE0\uCE5C .base ${paths.length}\uAC1C\uB294 \uB36E\uC5B4\uC4F0\uC9C0 \uC54A\uC558\uC2B5\uB2C8\uB2E4 \u2014 push \uD558\uBA74 \uACE0\uCE5C \uBCF4\uAE30 \uC774\uB984 \xB7 \uC5F4 \xB7 \uC815\uB82C \xB7 \uD544\uD130\uC640 \uB354\uD558\uACE0 \uC9C0\uC6B4 \uBCF4\uAE30\uB97C Notion \uC5D0 \uC62C\uB9BD\uB2C8\uB2E4:`
|
|
210
253
|
),
|
|
211
254
|
...pathLines(paths)
|
|
212
255
|
];
|
|
@@ -232,7 +275,7 @@ function readOnlySkipLines(skips, dryRun = false) {
|
|
|
232
275
|
}
|
|
233
276
|
|
|
234
277
|
// src/utils/vault.ts
|
|
235
|
-
import { existsSync } from "fs";
|
|
278
|
+
import { existsSync, readFileSync } from "fs";
|
|
236
279
|
import { dirname, join, relative } from "path";
|
|
237
280
|
import {
|
|
238
281
|
ConfigManager,
|
|
@@ -241,26 +284,57 @@ import {
|
|
|
241
284
|
matchesPathScope,
|
|
242
285
|
ScopedVaultFS,
|
|
243
286
|
findConnectionFolders,
|
|
244
|
-
OBSIDIAN_CONFIG_DIR
|
|
287
|
+
OBSIDIAN_CONFIG_DIR,
|
|
288
|
+
IGNORE_FILE,
|
|
289
|
+
obsidianOnlyExcludes,
|
|
290
|
+
templateFolders
|
|
245
291
|
} from "@im-nobsidian/core";
|
|
246
292
|
async function nestedConnections(cwd) {
|
|
247
293
|
return (await findConnectionFolders(cwd)).filter((folder) => folder !== "");
|
|
248
294
|
}
|
|
249
295
|
async function openVaultFs(cwd, paths) {
|
|
250
|
-
const base = new NodeVaultFS(cwd,
|
|
296
|
+
const base = new NodeVaultFS(cwd, {
|
|
297
|
+
...paths,
|
|
298
|
+
exclude: [...paths.exclude ?? [], ...obsidianOnlyPatterns(cwd, paths)]
|
|
299
|
+
});
|
|
251
300
|
const nested = await nestedConnections(cwd);
|
|
252
301
|
return nested.length === 0 ? base : new ScopedVaultFS(base, "", ["", ...nested]);
|
|
253
302
|
}
|
|
303
|
+
async function outsideVault(cwd, paths) {
|
|
304
|
+
const root = vaultRoot(cwd);
|
|
305
|
+
if (root === null) return void 0;
|
|
306
|
+
const folder = relative(root, cwd).split("\\").join("/");
|
|
307
|
+
const whole = folder === "" && (paths.include ?? []).length === 0 && (paths.exclude ?? []).length === 0 && paths.excludeObsidianOnly !== true && !existsSync(join(cwd, IGNORE_FILE)) && (await nestedConnections(cwd)).length === 0;
|
|
308
|
+
return whole ? void 0 : { fs: new NodeVaultFS(root, {}, false), folder };
|
|
309
|
+
}
|
|
310
|
+
function obsidianOnlyPatterns(cwd, paths) {
|
|
311
|
+
if (paths.excludeObsidianOnly !== true) return [];
|
|
312
|
+
const root = vaultRoot(cwd);
|
|
313
|
+
if (root === null) return obsidianOnlyExcludes([], "");
|
|
314
|
+
const read = (file) => {
|
|
315
|
+
try {
|
|
316
|
+
return readFileSync(join(root, OBSIDIAN_CONFIG_DIR, file), "utf-8");
|
|
317
|
+
} catch {
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
};
|
|
321
|
+
return obsidianOnlyExcludes(
|
|
322
|
+
templateFolders(read),
|
|
323
|
+
relative(root, cwd).split("\\").join("/").normalize("NFC")
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
function vaultRoot(cwd) {
|
|
327
|
+
for (let dir = cwd; ; dir = dirname(dir)) {
|
|
328
|
+
if (existsSync(join(dir, OBSIDIAN_CONFIG_DIR))) return dir;
|
|
329
|
+
if (dirname(dir) === dir) return null;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
254
332
|
function vaultArgPath(path, platform = process.platform) {
|
|
255
333
|
return platform === "win32" ? path.replace(/\\/g, "/") : path;
|
|
256
334
|
}
|
|
257
335
|
function obsidianConfigDir(cwd) {
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
return relative(cwd, join(dir, OBSIDIAN_CONFIG_DIR)).split("\\").join("/");
|
|
261
|
-
}
|
|
262
|
-
if (dirname(dir) === dir) return OBSIDIAN_CONFIG_DIR;
|
|
263
|
-
}
|
|
336
|
+
const root = vaultRoot(cwd);
|
|
337
|
+
return root === null ? OBSIDIAN_CONFIG_DIR : relative(cwd, join(root, OBSIDIAN_CONFIG_DIR)).split("\\").join("/");
|
|
264
338
|
}
|
|
265
339
|
function outerConnectionTracking(cwd) {
|
|
266
340
|
for (let dir = dirname(cwd); ; dir = dirname(dir)) {
|
|
@@ -403,12 +477,31 @@ function extractPageTitle(page) {
|
|
|
403
477
|
import { Command as Command2 } from "commander";
|
|
404
478
|
import { ConfigManager as ConfigManager3, StateDB as StateDB2, NotionClient as NotionClient2, SyncOrchestrator } from "@im-nobsidian/core";
|
|
405
479
|
|
|
406
|
-
// src/utils/
|
|
480
|
+
// src/utils/log-level.ts
|
|
407
481
|
import { setLogger } from "@im-nobsidian/core";
|
|
482
|
+
var level = "normal";
|
|
483
|
+
var noop = () => {
|
|
484
|
+
};
|
|
485
|
+
function applyLogLevel(next, sink) {
|
|
486
|
+
level = next;
|
|
487
|
+
const out = (fallback) => sink ?? fallback;
|
|
488
|
+
setLogger({
|
|
489
|
+
error: out((message, ...args) => console.error(message, ...args)),
|
|
490
|
+
warn: level === "quiet" ? noop : out((message, ...args) => console.warn(message, ...args)),
|
|
491
|
+
info: level === "verbose" ? out((message, ...args) => console.info(message, ...args)) : noop,
|
|
492
|
+
debug: level === "verbose" ? out((message, ...args) => console.debug(message, ...args)) : noop
|
|
493
|
+
});
|
|
494
|
+
}
|
|
495
|
+
function currentLogLevel() {
|
|
496
|
+
return level;
|
|
497
|
+
}
|
|
498
|
+
function logLevelOf(options) {
|
|
499
|
+
return options.verbose ? "verbose" : options.quiet ? "quiet" : "normal";
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// src/utils/json-output.ts
|
|
408
503
|
function enableJsonMode() {
|
|
409
|
-
|
|
410
|
-
setLogger({ warn: toStderr, error: toStderr, info: toStderr, debug: () => {
|
|
411
|
-
} });
|
|
504
|
+
applyLogLevel(currentLogLevel(), (message, ...args) => console.error(message, ...args));
|
|
412
505
|
}
|
|
413
506
|
function pushDbChangeCount(result) {
|
|
414
507
|
return (result.createdDatabases ?? 0) + (result.renamedProperties?.length ?? 0) + (result.schemaEdits?.length ?? 0) + (result.viewEdits?.length ?? 0);
|
|
@@ -439,6 +532,9 @@ function pullJson(result) {
|
|
|
439
532
|
deleted: result.deleted,
|
|
440
533
|
restored: result.restored,
|
|
441
534
|
restoredPaths: result.restoredPaths ?? [],
|
|
535
|
+
mergedPaths: result.mergedPaths ?? [],
|
|
536
|
+
relinkedPaths: result.relinkedPaths ?? [],
|
|
537
|
+
relinkPending: result.relinkPending ?? false,
|
|
442
538
|
schemaFollowed: result.schemaFollowed ?? [],
|
|
443
539
|
heldSidecars: result.heldSidecars ?? [],
|
|
444
540
|
heldBases: result.heldBases ?? [],
|
|
@@ -463,8 +559,41 @@ function printJson(value) {
|
|
|
463
559
|
`);
|
|
464
560
|
}
|
|
465
561
|
|
|
562
|
+
// src/utils/base-filter-prompt.ts
|
|
563
|
+
import { confirm as confirm2 } from "@inquirer/prompts";
|
|
564
|
+
|
|
565
|
+
// src/utils/interactive.ts
|
|
566
|
+
function isInteractive() {
|
|
567
|
+
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
// src/utils/base-filter-prompt.ts
|
|
571
|
+
async function askUserBaseFilterFixes(orchestrator, options) {
|
|
572
|
+
if (options.dryRun) return;
|
|
573
|
+
const fixes = orchestrator.userBaseFilterFixes();
|
|
574
|
+
if (fixes.length === 0) return;
|
|
575
|
+
console.log(`
|
|
576
|
+
${icons.conflict} \uC9C1\uC811 \uB9CC\uB4E0 .base \uC758 \uD3F4\uB354 \uD544\uD130\uAC00 \uC62E\uAE34 \uD3F4\uB354\uB97C \uAC00\uB9AC\uD0B5\uB2C8\uB2E4:`);
|
|
577
|
+
for (const fix of fixes) console.log(` ${dimText(`\xB7 ${fix.path}: ${fix.from} \u2192 ${fix.to}`)}`);
|
|
578
|
+
let accept;
|
|
579
|
+
if (options.yes) {
|
|
580
|
+
accept = true;
|
|
581
|
+
} else if (!isInteractive()) {
|
|
582
|
+
console.log(
|
|
583
|
+
` ${dimText("\uACE0\uCE58\uB824\uBA74 --yes \uB97C \uBD99\uC5EC \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694 \u2014 \uB2E4\uC74C \uC2E4\uD589\uC5D0\uC11C \uB2E4\uC2DC \uBB3B\uC2B5\uB2C8\uB2E4")}`
|
|
584
|
+
);
|
|
585
|
+
return;
|
|
586
|
+
} else {
|
|
587
|
+
accept = await confirm2({ message: "\uC0C8 \uD3F4\uB354\uB85C \uACE0\uCE60\uAE4C\uC694?", default: true });
|
|
588
|
+
}
|
|
589
|
+
const fixed = await orchestrator.resolveUserBaseFilterFixes(accept);
|
|
590
|
+
console.log(
|
|
591
|
+
accept ? ` ${icons.success} .base ${fixed.length}\uAC1C\uC758 \uD3F4\uB354 \uD544\uD130\uB97C \uACE0\uCCE4\uC2B5\uB2C8\uB2E4` : ` ${dimText("\uADF8\uB300\uB85C \uB461\uB2C8\uB2E4 \u2014 \uB2E4\uC2DC \uBB3B\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4")}`
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
|
|
466
595
|
// src/commands/push.ts
|
|
467
|
-
var pushCommand = new Command2("push").description("\uB85C\uCEEC \uBCC0\uACBD\uC0AC\uD56D\uC744 Notion\uC5D0 \uBC18\uC601").option("--dry-run", "\uC2E4\uC81C \uBC18\uC601 \uC5C6\uC774 \uBCC0\uACBD\uC0AC\uD56D\uB9CC \uD45C\uC2DC").option("-p, --path <paths...>", "\uD2B9\uC815 \uACBD\uB85C\uB9CC push").option("--json", "\uACB0\uACFC\uB97C JSON \uD55C \uC904\uB85C \uCD9C\uB825 (\uC790\uB3D9\uD654\uC6A9 \u2014 \uB85C\uADF8\uB294 stderr)").action(async (options) => {
|
|
596
|
+
var pushCommand = new Command2("push").description("\uB85C\uCEEC \uBCC0\uACBD\uC0AC\uD56D\uC744 Notion\uC5D0 \uBC18\uC601").option("--dry-run", "\uC2E4\uC81C \uBC18\uC601 \uC5C6\uC774 \uBCC0\uACBD\uC0AC\uD56D\uB9CC \uD45C\uC2DC").option("-p, --path <paths...>", "\uD2B9\uC815 \uACBD\uB85C\uB9CC push").option("--json", "\uACB0\uACFC\uB97C JSON \uD55C \uC904\uB85C \uCD9C\uB825 (\uC790\uB3D9\uD654\uC6A9 \u2014 \uB85C\uADF8\uB294 stderr)").option("-y, --yes", "\uC9C1\uC811 \uB9CC\uB4E0 .base \uC758 \uD3F4\uB354 \uD544\uD130\uB97C \uBB3B\uC9C0 \uC54A\uACE0 \uACE0\uCE68 (\uBE44\uB300\uD654\uD615/CI)").action(async (options) => {
|
|
468
597
|
const json = options.json === true;
|
|
469
598
|
if (json) enableJsonMode();
|
|
470
599
|
const cwd = process.cwd();
|
|
@@ -485,6 +614,7 @@ ${header(" Pushing to Notion...")}`);
|
|
|
485
614
|
dryRun: options.dryRun,
|
|
486
615
|
paths: options.path?.map((path) => vaultArgPath(path)),
|
|
487
616
|
onProgress: json ? void 0 : (current, total, item) => {
|
|
617
|
+
if (item.unchanged) return;
|
|
488
618
|
const icon = operationIcon(item.operation);
|
|
489
619
|
const label = operationLabel(item.operation);
|
|
490
620
|
const prog = progress(current, total);
|
|
@@ -533,6 +663,7 @@ ${header(" Pushing to Notion...")}`);
|
|
|
533
663
|
}
|
|
534
664
|
process.exitCode = 1;
|
|
535
665
|
}
|
|
666
|
+
await askUserBaseFilterFixes(orchestrator, options);
|
|
536
667
|
} finally {
|
|
537
668
|
stateDb.close();
|
|
538
669
|
}
|
|
@@ -545,7 +676,7 @@ import {
|
|
|
545
676
|
StateDB as StateDB3,
|
|
546
677
|
NotionClient as NotionClient3,
|
|
547
678
|
SyncOrchestrator as SyncOrchestrator2,
|
|
548
|
-
IGNORE_FILE
|
|
679
|
+
IGNORE_FILE as IGNORE_FILE2
|
|
549
680
|
} from "@im-nobsidian/core";
|
|
550
681
|
import chalk3 from "chalk";
|
|
551
682
|
var pullCommand = new Command3("pull").description("Notion \uBCC0\uACBD\uC0AC\uD56D\uC744 \uB85C\uCEEC\uC5D0 \uBC18\uC601").option("--dry-run", "\uC2E4\uC81C \uBC18\uC601 \uC5C6\uC774 \uBCC0\uACBD\uC0AC\uD56D\uB9CC \uD45C\uC2DC").option("-p, --path <paths...>", "\uD2B9\uC815 \uACBD\uB85C\uB9CC pull").option(
|
|
@@ -556,7 +687,7 @@ var pullCommand = new Command3("pull").description("Notion \uBCC0\uACBD\uC0AC\uD
|
|
|
556
687
|
"--rerender",
|
|
557
688
|
"Notion \uC5D0\uC11C \uBC14\uB00C\uC9C0 \uC54A\uC740 \uBC1B\uC544 \uB454 \uB178\uD2B8\uB3C4 \uC9C0\uAE08 \uBCC0\uD658\uAE30\uB85C \uB2E4\uC2DC \uADF8\uB9BC (\uBCFC\uD2B8\uC5D0\uC11C \uACE0\uCE5C \uB178\uD2B8\uB294 \uADF8\uB300\uB85C)"
|
|
558
689
|
).conflicts("dryRun")
|
|
559
|
-
).option("--json", "\uACB0\uACFC\uB97C JSON \uD55C \uC904\uB85C \uCD9C\uB825 (\uC790\uB3D9\uD654\uC6A9 \u2014 \uB85C\uADF8\uB294 stderr)").action(async (options) => {
|
|
690
|
+
).option("--json", "\uACB0\uACFC\uB97C JSON \uD55C \uC904\uB85C \uCD9C\uB825 (\uC790\uB3D9\uD654\uC6A9 \u2014 \uB85C\uADF8\uB294 stderr)").option("-y, --yes", "\uC9C1\uC811 \uB9CC\uB4E0 .base \uC758 \uD3F4\uB354 \uD544\uD130\uB97C \uBB3B\uC9C0 \uC54A\uACE0 \uACE0\uCE68 (\uBE44\uB300\uD654\uD615/CI)").action(async (options) => {
|
|
560
691
|
const json = options.json === true;
|
|
561
692
|
if (json) enableJsonMode();
|
|
562
693
|
const cwd = process.cwd();
|
|
@@ -566,7 +697,14 @@ var pullCommand = new Command3("pull").description("Notion \uBCC0\uACBD\uC0AC\uD
|
|
|
566
697
|
try {
|
|
567
698
|
const client = NotionClient3.fromConfig(config);
|
|
568
699
|
const vaultFs = await openVaultFs(cwd, config.paths);
|
|
569
|
-
const orchestrator = new SyncOrchestrator2(
|
|
700
|
+
const orchestrator = new SyncOrchestrator2(
|
|
701
|
+
config,
|
|
702
|
+
stateDb,
|
|
703
|
+
client,
|
|
704
|
+
vaultFs,
|
|
705
|
+
void 0,
|
|
706
|
+
await outsideVault(cwd, config.paths)
|
|
707
|
+
);
|
|
570
708
|
if (!json) {
|
|
571
709
|
console.log(`
|
|
572
710
|
${header(" Pulling from Notion...")}`);
|
|
@@ -620,6 +758,12 @@ ${header(" Pulling from Notion...")}`);
|
|
|
620
758
|
for (const line of restoredLines(result.restoredPaths, options.dryRun)) {
|
|
621
759
|
console.log(` ${line}`);
|
|
622
760
|
}
|
|
761
|
+
for (const line of relinkedLines(result.relinkedPaths, result.relinkPending)) {
|
|
762
|
+
console.log(` ${line}`);
|
|
763
|
+
}
|
|
764
|
+
for (const line of mergedLines(result.mergedPaths, "push", options.dryRun)) {
|
|
765
|
+
console.log(` ${line}`);
|
|
766
|
+
}
|
|
623
767
|
for (const move of result.moved ?? []) {
|
|
624
768
|
const verb = move.renamed ? "rename" : "move";
|
|
625
769
|
console.log(
|
|
@@ -629,7 +773,7 @@ ${header(" Pulling from Notion...")}`);
|
|
|
629
773
|
const excluded = result.excluded ?? [];
|
|
630
774
|
if (excluded.length > 0) {
|
|
631
775
|
console.log(
|
|
632
|
-
` ${icons.conflict} ${dimText(`${excluded.length} new in Notion ${options.dryRun ? "would not be" : "not"} pulled \u2014 excluded by paths / ${
|
|
776
|
+
` ${icons.conflict} ${dimText(`${excluded.length} new in Notion ${options.dryRun ? "would not be" : "not"} pulled \u2014 excluded by paths / ${IGNORE_FILE2}:`)}`
|
|
633
777
|
);
|
|
634
778
|
for (const path of excluded.slice(0, LISTED_ITEMS))
|
|
635
779
|
console.log(` ${dimText(`\xB7 ${path}`)}`);
|
|
@@ -648,7 +792,13 @@ ${header(" Pulling from Notion...")}`);
|
|
|
648
792
|
if (result.remoteScan) {
|
|
649
793
|
for (const line of remoteScanLines(result.remoteScan)) console.log(` ${line}`);
|
|
650
794
|
}
|
|
651
|
-
if (
|
|
795
|
+
if (options.dryRun) {
|
|
796
|
+
const planned = plannedConflictLines(
|
|
797
|
+
result.conflicts.map((conflict) => conflict.syncRecord.obsidianPath)
|
|
798
|
+
);
|
|
799
|
+
if (planned.length > 0) console.log("");
|
|
800
|
+
for (const line of planned) console.log(` ${line}`);
|
|
801
|
+
} else if (result.conflicts.length > 0) {
|
|
652
802
|
console.log(
|
|
653
803
|
`
|
|
654
804
|
${icons.conflict} ${chalk3.magenta(`${result.conflicts.length} conflicts`)} \u2014 run ${chalk3.cyan("nobsi resolve")}`
|
|
@@ -661,6 +811,7 @@ ${header(" Pulling from Notion...")}`);
|
|
|
661
811
|
}
|
|
662
812
|
process.exitCode = 1;
|
|
663
813
|
}
|
|
814
|
+
await askUserBaseFilterFixes(orchestrator, options);
|
|
664
815
|
} finally {
|
|
665
816
|
stateDb.close();
|
|
666
817
|
}
|
|
@@ -670,7 +821,7 @@ ${header(" Pulling from Notion...")}`);
|
|
|
670
821
|
import { Command as Command4 } from "commander";
|
|
671
822
|
import { ConfigManager as ConfigManager5, StateDB as StateDB4, NotionClient as NotionClient4, SyncOrchestrator as SyncOrchestrator3 } from "@im-nobsidian/core";
|
|
672
823
|
import chalk4 from "chalk";
|
|
673
|
-
var syncCommand = new Command4("sync").description("\uC591\uBC29\uD5A5 \uB3D9\uAE30\uD654 (Pull \u2192 Push)").option("--dry-run", "\uC2E4\uC81C \uBC18\uC601 \uC5C6\uC774 \uBCC0\uACBD\uC0AC\uD56D\uB9CC \uD45C\uC2DC").option("--json", "\uACB0\uACFC\uB97C JSON \uD55C \uC904\uB85C \uCD9C\uB825 (\uC790\uB3D9\uD654\uC6A9 \u2014 \uB85C\uADF8\uB294 stderr)").action(async (options) => {
|
|
824
|
+
var syncCommand = new Command4("sync").description("\uC591\uBC29\uD5A5 \uB3D9\uAE30\uD654 (Pull \u2192 Push)").option("--dry-run", "\uC2E4\uC81C \uBC18\uC601 \uC5C6\uC774 \uBCC0\uACBD\uC0AC\uD56D\uB9CC \uD45C\uC2DC").option("--json", "\uACB0\uACFC\uB97C JSON \uD55C \uC904\uB85C \uCD9C\uB825 (\uC790\uB3D9\uD654\uC6A9 \u2014 \uB85C\uADF8\uB294 stderr)").option("-y, --yes", "\uC9C1\uC811 \uB9CC\uB4E0 .base \uC758 \uD3F4\uB354 \uD544\uD130\uB97C \uBB3B\uC9C0 \uC54A\uACE0 \uACE0\uCE68 (\uBE44\uB300\uD654\uD615/CI)").action(async (options) => {
|
|
674
825
|
const json = options.json === true;
|
|
675
826
|
if (json) enableJsonMode();
|
|
676
827
|
const log = json ? () => {
|
|
@@ -682,7 +833,14 @@ var syncCommand = new Command4("sync").description("\uC591\uBC29\uD5A5 \uB3D9\uA
|
|
|
682
833
|
try {
|
|
683
834
|
const client = NotionClient4.fromConfig(config);
|
|
684
835
|
const vaultFs = await openVaultFs(cwd, config.paths);
|
|
685
|
-
const orchestrator = new SyncOrchestrator3(
|
|
836
|
+
const orchestrator = new SyncOrchestrator3(
|
|
837
|
+
config,
|
|
838
|
+
stateDb,
|
|
839
|
+
client,
|
|
840
|
+
vaultFs,
|
|
841
|
+
void 0,
|
|
842
|
+
await outsideVault(cwd, config.paths)
|
|
843
|
+
);
|
|
686
844
|
log(`
|
|
687
845
|
${header(" Bidirectional Sync")}`);
|
|
688
846
|
log(` ${separator()}`);
|
|
@@ -745,6 +903,12 @@ ${header(" Bidirectional Sync")}`);
|
|
|
745
903
|
for (const line of restoredLines(pullResult.restoredPaths, options.dryRun)) {
|
|
746
904
|
console.log(` ${line}`);
|
|
747
905
|
}
|
|
906
|
+
for (const line of relinkedLines(pullResult.relinkedPaths, pullResult.relinkPending)) {
|
|
907
|
+
console.log(` ${line}`);
|
|
908
|
+
}
|
|
909
|
+
for (const line of mergedLines(pullResult.mergedPaths, "pushed", options.dryRun)) {
|
|
910
|
+
console.log(` ${line}`);
|
|
911
|
+
}
|
|
748
912
|
for (const line of unsentDeletionLines(pushResult.unsentDeletions, options.dryRun)) {
|
|
749
913
|
console.log(` ${line}`);
|
|
750
914
|
}
|
|
@@ -780,7 +944,13 @@ ${header(" Bidirectional Sync")}`);
|
|
|
780
944
|
if (pullResult.remoteScan) {
|
|
781
945
|
for (const line of remoteScanLines(pullResult.remoteScan)) console.log(` ${line}`);
|
|
782
946
|
}
|
|
783
|
-
if (
|
|
947
|
+
if (options.dryRun) {
|
|
948
|
+
const planned = plannedConflictLines(
|
|
949
|
+
result.conflicts.map((conflict) => conflict.syncRecord.obsidianPath)
|
|
950
|
+
);
|
|
951
|
+
if (planned.length > 0) console.log("");
|
|
952
|
+
for (const line of planned) console.log(` ${line}`);
|
|
953
|
+
} else if (result.conflicts.length > 0) {
|
|
784
954
|
console.log(
|
|
785
955
|
`
|
|
786
956
|
${icons.conflict} ${chalk4.magenta(`${result.conflicts.length} conflicts`)} \u2014 run ${chalk4.cyan("nobsi resolve")}`
|
|
@@ -792,6 +962,7 @@ ${header(" Bidirectional Sync")}`);
|
|
|
792
962
|
for (const f of pushResult.failed) failedItem(f.path, f.error, "\u25B2");
|
|
793
963
|
process.exitCode = 1;
|
|
794
964
|
}
|
|
965
|
+
await askUserBaseFilterFixes(orchestrator, options);
|
|
795
966
|
} finally {
|
|
796
967
|
stateDb.close();
|
|
797
968
|
}
|
|
@@ -804,7 +975,7 @@ import {
|
|
|
804
975
|
StateDB as StateDB5,
|
|
805
976
|
NotionClient as NotionClient5,
|
|
806
977
|
SyncOrchestrator as SyncOrchestrator4,
|
|
807
|
-
IGNORE_FILE as
|
|
978
|
+
IGNORE_FILE as IGNORE_FILE3,
|
|
808
979
|
isRemoteDeletion,
|
|
809
980
|
shortNotionId
|
|
810
981
|
} from "@im-nobsidian/core";
|
|
@@ -979,7 +1150,7 @@ ${header(" Sync Status")}`);
|
|
|
979
1150
|
if (remoteExcluded.length > 0) {
|
|
980
1151
|
console.log(
|
|
981
1152
|
`
|
|
982
|
-
${header(`New in Notion, not pulled (excluded by paths / ${
|
|
1153
|
+
${header(`New in Notion, not pulled (excluded by paths / ${IGNORE_FILE3}): ${remoteExcluded.length}`)}`
|
|
983
1154
|
);
|
|
984
1155
|
for (const path of remoteExcluded.slice(0, LISTED_ITEMS)) {
|
|
985
1156
|
console.log(` ${dimText("\xB7")} ${path}`);
|
|
@@ -1214,7 +1385,7 @@ ${header(" Remote Scan")}`);
|
|
|
1214
1385
|
|
|
1215
1386
|
// src/commands/resolve.ts
|
|
1216
1387
|
import { Command as Command9 } from "commander";
|
|
1217
|
-
import { select, confirm as
|
|
1388
|
+
import { select, confirm as confirm3 } from "@inquirer/prompts";
|
|
1218
1389
|
import {
|
|
1219
1390
|
ConfigManager as ConfigManager10,
|
|
1220
1391
|
StateDB as StateDB9,
|
|
@@ -1225,14 +1396,23 @@ import {
|
|
|
1225
1396
|
} from "@im-nobsidian/core";
|
|
1226
1397
|
|
|
1227
1398
|
// src/utils/state-db-failure.ts
|
|
1228
|
-
import {
|
|
1399
|
+
import {
|
|
1400
|
+
ConnectionBoundaryError,
|
|
1401
|
+
ConnectionScopeError,
|
|
1402
|
+
StateDbUnavailableError,
|
|
1403
|
+
SyncBusyError,
|
|
1404
|
+
UnlinkedVaultError
|
|
1405
|
+
} from "@im-nobsidian/core";
|
|
1229
1406
|
function describeFailure(error) {
|
|
1230
1407
|
const message = error instanceof Error ? error.message : String(error);
|
|
1231
1408
|
if (!(error instanceof StateDbUnavailableError)) return message;
|
|
1232
1409
|
return `${message} \u2014 ${error.guidance("\uBA85\uB839\uC744 \uB2E4\uC2DC \uC2E4\uD589\uD558\uC138\uC694")}`;
|
|
1233
1410
|
}
|
|
1234
|
-
function
|
|
1235
|
-
|
|
1411
|
+
function isUserFailure(error) {
|
|
1412
|
+
return error instanceof StateDbUnavailableError || error instanceof UnlinkedVaultError || error instanceof SyncBusyError || error instanceof ConnectionBoundaryError || error instanceof ConnectionScopeError;
|
|
1413
|
+
}
|
|
1414
|
+
function reportUserFailure(error, proc) {
|
|
1415
|
+
if (!isUserFailure(error)) throw error;
|
|
1236
1416
|
console.error(`\uC624\uB958: ${describeFailure(error)}`);
|
|
1237
1417
|
proc.exitCode = 1;
|
|
1238
1418
|
}
|
|
@@ -1256,9 +1436,6 @@ function choiceName(conflict, choice, uploads) {
|
|
|
1256
1436
|
const deleted = isRemoteDeletion2(conflict);
|
|
1257
1437
|
return (deleted && !uploads ? PULL_ONLY_REMOTE_DELETION_CHOICE_NAMES[choice] : void 0) ?? (deleted ? REMOTE_DELETION_CHOICE_NAMES[choice] : void 0) ?? CHOICE_NAMES[choice] ?? choice;
|
|
1258
1438
|
}
|
|
1259
|
-
function isInteractive() {
|
|
1260
|
-
return process.stdin.isTTY === true && process.stdout.isTTY === true;
|
|
1261
|
-
}
|
|
1262
1439
|
var resolveCommand = new Command9("resolve").description("\uCDA9\uB3CC\uC744 \uD574\uACB0\uD569\uB2C8\uB2E4").option("-s, --strategy <strategy>", `\uC790\uB3D9 \uD574\uACB0 \uC804\uB7B5 (${STRATEGIES.join(" | ")})`).option("-y, --yes", "\uD655\uC778 \uD504\uB86C\uD504\uD2B8 \uC5C6\uC774 \uC9C4\uD589 (\uBE44\uB300\uD654\uD615/CI)").action(async (options) => {
|
|
1263
1440
|
if (options.strategy && !STRATEGIES.includes(options.strategy)) {
|
|
1264
1441
|
console.error(`\uC54C \uC218 \uC5C6\uB294 \uC804\uB7B5 '${options.strategy}' \u2014 \uC0AC\uC6A9 \uAC00\uB2A5: ${STRATEGIES.join(", ")}`);
|
|
@@ -1314,7 +1491,7 @@ ${cleared.size}\uAC74 \uBAA8\uB450 \uC774\uBBF8 \uD574\uC18C\uB41C \uC0C1\uD0DC\
|
|
|
1314
1491
|
process.exitCode = 1;
|
|
1315
1492
|
return;
|
|
1316
1493
|
}
|
|
1317
|
-
const shouldProceed = await
|
|
1494
|
+
const shouldProceed = await confirm3({
|
|
1318
1495
|
message: `${pending.length}\uAC74\uC758 \uCDA9\uB3CC\uC744 '${strategy}' \uC804\uB7B5\uC73C\uB85C \uC77C\uAD04 \uD574\uACB0\uD560\uAE4C\uC694?`
|
|
1319
1496
|
});
|
|
1320
1497
|
if (!shouldProceed) return;
|
|
@@ -1441,7 +1618,14 @@ var watchCommand = new Command10("watch").description("\uD30C\uC77C \uBCC0\uACBD
|
|
|
1441
1618
|
const stateDb = StateDB10.open(configManager.dbPath);
|
|
1442
1619
|
const client = NotionClient10.fromConfig(config);
|
|
1443
1620
|
const vaultFs = await openVaultFs(cwd, config.paths);
|
|
1444
|
-
const orchestrator = new SyncOrchestrator9(
|
|
1621
|
+
const orchestrator = new SyncOrchestrator9(
|
|
1622
|
+
config,
|
|
1623
|
+
stateDb,
|
|
1624
|
+
client,
|
|
1625
|
+
vaultFs,
|
|
1626
|
+
void 0,
|
|
1627
|
+
await outsideVault(cwd, config.paths)
|
|
1628
|
+
);
|
|
1445
1629
|
const service = new WatchSyncService(cwd, orchestrator, {
|
|
1446
1630
|
debounceMs,
|
|
1447
1631
|
onFileChange: (event, path) => {
|
|
@@ -1682,7 +1866,7 @@ function scheduleForcedExit(proc, schedule = setTimeout) {
|
|
|
1682
1866
|
var requireJson = createRequire(import.meta.url);
|
|
1683
1867
|
var { version } = requireJson("../package.json");
|
|
1684
1868
|
var program = new Command13();
|
|
1685
|
-
program.name("im-nobsidian").description("Im-Nobsidian: Obsidian \u2194 Notion \uC591\uBC29\uD5A5 \uB3D9\uAE30\uD654 CLI").version(version).option("--verbose", "\
|
|
1869
|
+
program.name("im-nobsidian").description("Im-Nobsidian: Obsidian \u2194 Notion \uC591\uBC29\uD5A5 \uB3D9\uAE30\uD654 CLI").version(version).option("--verbose", "\uC5D4\uC9C4\uC758 \uC815\uBCF4 \xB7 \uB514\uBC84\uADF8 \uB85C\uADF8\uB3C4 \uCD9C\uB825").option("--quiet", "\uACBD\uACE0 \uB85C\uADF8\uB97C \uC228\uAE30\uACE0 \uC624\uB958 \uB85C\uADF8\uB9CC \uCD9C\uB825").hook("preAction", () => applyLogLevel(logLevelOf(program.opts())));
|
|
1686
1870
|
program.addCommand(initCommand);
|
|
1687
1871
|
program.addCommand(pushCommand);
|
|
1688
1872
|
program.addCommand(pullCommand);
|
|
@@ -1700,7 +1884,7 @@ program.parseAsync().then(
|
|
|
1700
1884
|
scheduleForcedExit(process);
|
|
1701
1885
|
},
|
|
1702
1886
|
(error) => {
|
|
1703
|
-
|
|
1887
|
+
reportUserFailure(error, process);
|
|
1704
1888
|
scheduleForcedExit(process);
|
|
1705
1889
|
}
|
|
1706
1890
|
);
|