@narumitw/pi-sync 0.48.0 → 0.49.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/README.md +1 -0
- package/package.json +2 -2
- package/src/backend-factory.ts +11 -8
- package/src/sync-operations.ts +8 -8
- package/src/sync.ts +57 -30
package/README.md
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@narumitw/pi-sync",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.49.0",
|
|
4
4
|
"description": "Pi extension that syncs Pi configuration through Git, WebDAV, Cloudflare R2, or S3-compatible storage.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"typecheck": "tsc --noEmit"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@narumitw/pi-tui-kit": "^0.
|
|
39
|
+
"@narumitw/pi-tui-kit": "^0.46.0",
|
|
40
40
|
"fast-xml-parser": "^5.10.1",
|
|
41
41
|
"proper-lockfile": "^4.1.2"
|
|
42
42
|
},
|
package/src/backend-factory.ts
CHANGED
|
@@ -1,20 +1,23 @@
|
|
|
1
|
-
import { GitSyncBackend } from "./git-backend.js";
|
|
2
|
-
import { S3SyncBackend } from "./s3-backend.js";
|
|
3
1
|
import type { SyncBackend } from "./sync-backend.js";
|
|
4
2
|
import type { AnySyncConfig } from "./types.js";
|
|
5
|
-
import { WebDavSyncBackend } from "./webdav-backend.js";
|
|
6
3
|
|
|
7
4
|
export type SyncBackendFactory = {
|
|
8
|
-
bivarianceHack(config: AnySyncConfig): SyncBackend
|
|
5
|
+
bivarianceHack(config: AnySyncConfig): SyncBackend | Promise<SyncBackend>;
|
|
9
6
|
}["bivarianceHack"];
|
|
10
7
|
|
|
11
|
-
export const createSyncBackend: SyncBackendFactory = (config) => {
|
|
8
|
+
export const createSyncBackend: SyncBackendFactory = async (config) => {
|
|
12
9
|
switch (config.backend.type) {
|
|
13
|
-
case "s3":
|
|
10
|
+
case "s3": {
|
|
11
|
+
const { S3SyncBackend } = await import("./s3-backend.js");
|
|
14
12
|
return new S3SyncBackend(config.backend);
|
|
15
|
-
|
|
13
|
+
}
|
|
14
|
+
case "webdav": {
|
|
15
|
+
const { WebDavSyncBackend } = await import("./webdav-backend.js");
|
|
16
16
|
return new WebDavSyncBackend(config.backend);
|
|
17
|
-
|
|
17
|
+
}
|
|
18
|
+
case "git": {
|
|
19
|
+
const { GitSyncBackend } = await import("./git-backend.js");
|
|
18
20
|
return new GitSyncBackend(config.backend);
|
|
21
|
+
}
|
|
19
22
|
}
|
|
20
23
|
};
|
package/src/sync-operations.ts
CHANGED
|
@@ -114,7 +114,7 @@ export async function status(
|
|
|
114
114
|
const config = await loadConfig(options.setup);
|
|
115
115
|
throwIfAborted(options.signal);
|
|
116
116
|
ctx.ui.setStatus(STATUS_KEY, `checking ${config.setupName}`);
|
|
117
|
-
const backend = backendFor(config, factory);
|
|
117
|
+
const backend = await backendFor(config, factory);
|
|
118
118
|
const local = await createSnapshot(
|
|
119
119
|
config.snapshotIdentity,
|
|
120
120
|
snapshotOptionsForContext(ctx, config),
|
|
@@ -160,7 +160,7 @@ export async function diff(
|
|
|
160
160
|
const config = await loadConfig(options.setup);
|
|
161
161
|
throwIfAborted(options.signal);
|
|
162
162
|
ctx.ui.setStatus(STATUS_KEY, `checking ${config.setupName}`);
|
|
163
|
-
const backend = backendFor(config, factory);
|
|
163
|
+
const backend = await backendFor(config, factory);
|
|
164
164
|
const local = await createSnapshot(
|
|
165
165
|
config.snapshotIdentity,
|
|
166
166
|
snapshotOptionsForContext(ctx, config),
|
|
@@ -206,7 +206,7 @@ export async function doctor(
|
|
|
206
206
|
try {
|
|
207
207
|
const config = await loadConfig(options.setup);
|
|
208
208
|
throwIfAborted(options.signal);
|
|
209
|
-
backend = backendFor(config, factory);
|
|
209
|
+
backend = await backendFor(config, factory);
|
|
210
210
|
profile = config.snapshotIdentity;
|
|
211
211
|
snapshotOptions = snapshotOptionsForContext(ctx, config);
|
|
212
212
|
messages.push(
|
|
@@ -283,7 +283,7 @@ export async function push(
|
|
|
283
283
|
const config = input?.config ?? (await loadConfig(options.setup));
|
|
284
284
|
throwIfAborted(options.signal);
|
|
285
285
|
ctx.ui.setStatus(STATUS_KEY, `pushing ${config.setupName}`);
|
|
286
|
-
const backend = input?.backend ?? backendFor(config, factory);
|
|
286
|
+
const backend = input?.backend ?? (await backendFor(config, factory));
|
|
287
287
|
const state = input?.state ?? (await readStateForConfig(config));
|
|
288
288
|
throwIfAborted(options.signal);
|
|
289
289
|
const local =
|
|
@@ -415,7 +415,7 @@ export async function pull(
|
|
|
415
415
|
const config = await loadConfig(options.setup);
|
|
416
416
|
throwIfAborted(options.signal);
|
|
417
417
|
ctx.ui.setStatus(STATUS_KEY, `pulling ${config.setupName}`);
|
|
418
|
-
const backend = backendFor(config, factory);
|
|
418
|
+
const backend = await backendFor(config, factory);
|
|
419
419
|
const state = await readStateForConfig(config);
|
|
420
420
|
throwIfAborted(options.signal);
|
|
421
421
|
const local = await createSnapshot(
|
|
@@ -520,7 +520,7 @@ export async function syncBoth(
|
|
|
520
520
|
) {
|
|
521
521
|
const config = await loadConfig(options.setup);
|
|
522
522
|
throwIfAborted(options.signal);
|
|
523
|
-
const backend = backendFor(config, factory);
|
|
523
|
+
const backend = await backendFor(config, factory);
|
|
524
524
|
const state = await readStateForConfig(config);
|
|
525
525
|
throwIfAborted(options.signal);
|
|
526
526
|
const local = await createSnapshot(
|
|
@@ -645,7 +645,7 @@ export async function history(
|
|
|
645
645
|
) {
|
|
646
646
|
const config = await loadConfig(options.setup);
|
|
647
647
|
throwIfAborted(options.signal);
|
|
648
|
-
const backend = backendFor(config, factory);
|
|
648
|
+
const backend = await backendFor(config, factory);
|
|
649
649
|
const snapshots = (await backend.listHistory(options.signal)).slice(-20).reverse();
|
|
650
650
|
throwIfAborted(options.signal);
|
|
651
651
|
if (snapshots.length === 0) {
|
|
@@ -695,7 +695,7 @@ export async function rollback(
|
|
|
695
695
|
|
|
696
696
|
const config = await loadConfig(options.setup);
|
|
697
697
|
throwIfAborted(options.signal);
|
|
698
|
-
const backend = backendFor(config, factory);
|
|
698
|
+
const backend = await backendFor(config, factory);
|
|
699
699
|
if (
|
|
700
700
|
expectedSelection &&
|
|
701
701
|
(backend.identity !== expectedSelection.backendIdentity ||
|
package/src/sync.ts
CHANGED
|
@@ -28,29 +28,30 @@ import {
|
|
|
28
28
|
sessionTokenWarnings,
|
|
29
29
|
syncSessionsWarnings,
|
|
30
30
|
} from "./config.js";
|
|
31
|
-
import { showFileSelection } from "./file-selection.js";
|
|
32
31
|
import { unlock, withLock } from "./lock.js";
|
|
33
|
-
import { showSetupWizard, showSyncManager } from "./manager-ui.js";
|
|
34
32
|
import { SetupPullRequiresUiError, useSyncSetup } from "./setup-switch.js";
|
|
35
33
|
import { createSnapshot } from "./snapshot.js";
|
|
36
34
|
import { recoverSnapshotTransactionsOnStartup } from "./snapshot-transaction.js";
|
|
37
35
|
import { isSyncDecisionRequiredError } from "./sync-decision.js";
|
|
38
36
|
import { errorMessage } from "./sync-format.js";
|
|
39
|
-
import {
|
|
40
|
-
diff,
|
|
41
|
-
doctor,
|
|
42
|
-
history,
|
|
43
|
-
pull,
|
|
44
|
-
push,
|
|
45
|
-
rollback,
|
|
46
|
-
status,
|
|
47
|
-
syncBoth,
|
|
48
|
-
} from "./sync-operations.js";
|
|
49
37
|
import { hasLocalChanges } from "./sync-state.js";
|
|
50
38
|
import type { AnySyncConfig, CommandOptions, SnapshotOptions } from "./types.js";
|
|
51
39
|
|
|
52
40
|
const STATUS_KEY = "sync";
|
|
53
41
|
|
|
42
|
+
type SyncOperations = typeof import("./sync-operations.js");
|
|
43
|
+
let syncOperationsPromise: Promise<SyncOperations> | undefined;
|
|
44
|
+
|
|
45
|
+
function loadSyncOperations(): Promise<SyncOperations> {
|
|
46
|
+
if (!syncOperationsPromise) {
|
|
47
|
+
syncOperationsPromise = import("./sync-operations.js").catch((error) => {
|
|
48
|
+
syncOperationsPromise = undefined;
|
|
49
|
+
throw error;
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
return syncOperationsPromise;
|
|
53
|
+
}
|
|
54
|
+
|
|
54
55
|
const AUTO_SYNC_OPTIONS: CommandOptions = {
|
|
55
56
|
yes: true,
|
|
56
57
|
force: false,
|
|
@@ -130,6 +131,8 @@ async function handleCommand(
|
|
|
130
131
|
) {
|
|
131
132
|
if (!rawArgs.trim()) {
|
|
132
133
|
try {
|
|
134
|
+
const { showSyncManager } = await import("./manager-ui.js");
|
|
135
|
+
if (sessionSignal.aborted) return;
|
|
133
136
|
await showSyncManager(
|
|
134
137
|
ctx,
|
|
135
138
|
(route, signal, onCommit, target) =>
|
|
@@ -174,8 +177,13 @@ async function executeCommand(
|
|
|
174
177
|
await useSyncSetup(
|
|
175
178
|
ctx,
|
|
176
179
|
options.args[0] ?? "",
|
|
177
|
-
(selectedSetup) =>
|
|
178
|
-
|
|
180
|
+
async (selectedSetup) => {
|
|
181
|
+
const operations = await loadSyncOperations();
|
|
182
|
+
throwIfAborted(options.signal);
|
|
183
|
+
return withLock("pull", () =>
|
|
184
|
+
operations.pull(ctx, { ...options, setup: selectedSetup }),
|
|
185
|
+
);
|
|
186
|
+
},
|
|
179
187
|
undefined,
|
|
180
188
|
options.signal,
|
|
181
189
|
);
|
|
@@ -187,34 +195,48 @@ async function executeCommand(
|
|
|
187
195
|
await showConfig(ctx, options);
|
|
188
196
|
return { kind: "completed" };
|
|
189
197
|
case "files":
|
|
190
|
-
await
|
|
198
|
+
await (await import("./file-selection.js")).showFileSelection(
|
|
199
|
+
ctx,
|
|
200
|
+
options.setup,
|
|
201
|
+
options.signal,
|
|
202
|
+
);
|
|
191
203
|
return { kind: "completed" };
|
|
192
204
|
case "status":
|
|
193
|
-
await status(ctx, options);
|
|
205
|
+
await (await loadSyncOperations()).status(ctx, options);
|
|
194
206
|
return { kind: "completed" };
|
|
195
207
|
case "diff":
|
|
196
|
-
await diff(ctx, options);
|
|
208
|
+
await (await loadSyncOperations()).diff(ctx, options);
|
|
197
209
|
return { kind: "completed" };
|
|
198
210
|
case "doctor":
|
|
199
|
-
await doctor(ctx, options);
|
|
211
|
+
await (await loadSyncOperations()).doctor(ctx, options);
|
|
200
212
|
return { kind: "completed" };
|
|
201
213
|
case "push": {
|
|
202
|
-
const
|
|
214
|
+
const operations = await loadSyncOperations();
|
|
215
|
+
throwIfAborted(options.signal);
|
|
216
|
+
const outcome = await withLock("push", () => operations.push(ctx, options));
|
|
203
217
|
return { kind: "completed", ...(outcome ? { outcome } : {}) };
|
|
204
218
|
}
|
|
205
219
|
case "pull": {
|
|
206
|
-
const
|
|
220
|
+
const operations = await loadSyncOperations();
|
|
221
|
+
throwIfAborted(options.signal);
|
|
222
|
+
const outcome = await withLock("pull", () => operations.pull(ctx, options));
|
|
207
223
|
return { kind: "completed", ...(outcome ? { outcome } : {}) };
|
|
208
224
|
}
|
|
209
|
-
case "sync":
|
|
210
|
-
|
|
225
|
+
case "sync": {
|
|
226
|
+
const operations = await loadSyncOperations();
|
|
227
|
+
throwIfAborted(options.signal);
|
|
228
|
+
await withLock("sync", () => operations.syncBoth(ctx, options));
|
|
211
229
|
return { kind: "completed" };
|
|
230
|
+
}
|
|
212
231
|
case "history":
|
|
213
|
-
await history(ctx, options);
|
|
232
|
+
await (await loadSyncOperations()).history(ctx, options);
|
|
214
233
|
return { kind: "completed" };
|
|
215
|
-
case "rollback":
|
|
216
|
-
|
|
234
|
+
case "rollback": {
|
|
235
|
+
const operations = await loadSyncOperations();
|
|
236
|
+
throwIfAborted(options.signal);
|
|
237
|
+
await withLock("rollback", () => operations.rollback(ctx, options));
|
|
217
238
|
return { kind: "completed" };
|
|
239
|
+
}
|
|
218
240
|
case "unlock":
|
|
219
241
|
await unlock(ctx, options);
|
|
220
242
|
return { kind: "completed" };
|
|
@@ -243,9 +265,11 @@ async function autoSync(ctx: ExtensionContext, signal: AbortSignal) {
|
|
|
243
265
|
throwIfAborted(signal);
|
|
244
266
|
await loadConfig();
|
|
245
267
|
throwIfAborted(signal);
|
|
268
|
+
const operations = await loadSyncOperations();
|
|
269
|
+
throwIfAborted(signal);
|
|
246
270
|
await withLock("auto-sync", () => {
|
|
247
271
|
throwIfAborted(signal);
|
|
248
|
-
return syncBoth(ctx, { ...AUTO_SYNC_OPTIONS, signal });
|
|
272
|
+
return operations.syncBoth(ctx, { ...AUTO_SYNC_OPTIONS, signal });
|
|
249
273
|
});
|
|
250
274
|
} catch (error) {
|
|
251
275
|
if (signal.aborted || isMissingConfigError(error)) return;
|
|
@@ -265,6 +289,8 @@ async function autoPushSessions(ctx: ExtensionContext, signal: AbortSignal) {
|
|
|
265
289
|
const config = await loadConfig();
|
|
266
290
|
throwIfAborted(signal);
|
|
267
291
|
if (!config.include.includes("sessions")) return;
|
|
292
|
+
const operations = await loadSyncOperations();
|
|
293
|
+
throwIfAborted(signal);
|
|
268
294
|
await withLock("auto-session-push", async () => {
|
|
269
295
|
throwIfAborted(signal);
|
|
270
296
|
const state = await readStateForConfig(config);
|
|
@@ -275,7 +301,7 @@ async function autoPushSessions(ctx: ExtensionContext, signal: AbortSignal) {
|
|
|
275
301
|
);
|
|
276
302
|
throwIfAborted(signal);
|
|
277
303
|
if (!hasLocalChanges(local, state, config)) return;
|
|
278
|
-
await push(ctx, { ...AUTO_SYNC_OPTIONS, signal }, { config, state, local });
|
|
304
|
+
await operations.push(ctx, { ...AUTO_SYNC_OPTIONS, signal }, { config, state, local });
|
|
279
305
|
});
|
|
280
306
|
} catch (error) {
|
|
281
307
|
if (signal.aborted || isMissingConfigError(error)) return;
|
|
@@ -292,6 +318,8 @@ async function initConfig(ctx: ExtensionCommandContext, signal?: AbortSignal) {
|
|
|
292
318
|
}
|
|
293
319
|
|
|
294
320
|
if (ctx.mode === "tui") {
|
|
321
|
+
const { showSetupWizard } = await import("./manager-ui.js");
|
|
322
|
+
throwIfAborted(signal);
|
|
295
323
|
await showSetupWizard(ctx, signal);
|
|
296
324
|
return;
|
|
297
325
|
}
|
|
@@ -383,8 +411,8 @@ function displayWebDavUrl(value: string | undefined, username: string | undefine
|
|
|
383
411
|
}
|
|
384
412
|
}
|
|
385
413
|
|
|
386
|
-
function throwIfAborted(signal
|
|
387
|
-
if (!signal
|
|
414
|
+
function throwIfAborted(signal?: AbortSignal) {
|
|
415
|
+
if (!signal?.aborted) return;
|
|
388
416
|
throw signal.reason instanceof Error
|
|
389
417
|
? signal.reason
|
|
390
418
|
: new DOMException("The operation was aborted", "AbortError");
|
|
@@ -446,7 +474,6 @@ export {
|
|
|
446
474
|
preflightSnapshotApply,
|
|
447
475
|
protectSnapshotApplyPlan,
|
|
448
476
|
} from "./snapshot-apply.js";
|
|
449
|
-
export { backupLocal } from "./sync-operations.js";
|
|
450
477
|
export {
|
|
451
478
|
canPullRemoteSessionsOnFirstSync,
|
|
452
479
|
canPullRemoteSettingsOnFirstSync,
|