@vrtmrz/livesync-commonlib 0.1.20 → 0.1.21
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/replication/ReplicatorProvider.d.ts +16 -3
- package/dist/replication/ReplicatorProvider.js +6 -1
- package/dist/replication/index.d.ts +2 -2
- package/dist/replication/index.js +2 -0
- package/dist/replication/trystero/useP2PReplicatorFeature.js +2 -1
- package/dist/services/base/ReplicationService.js +2 -0
- package/dist/services/base/ReplicationService.typedReplication.d.ts +10 -0
- package/dist/services/base/ReplicationService.typedReplication.js +58 -35
- package/package.json +1 -1
|
@@ -68,9 +68,22 @@ export interface UserInitiatedReplicationAuthority {
|
|
|
68
68
|
export type ReplicationInteraction = NoInteraction | UserInitiatedReplicationAuthority;
|
|
69
69
|
/** Authority carried through a replication request and its failure path. */
|
|
70
70
|
export type InteractionAuthority = ReplicationInteraction;
|
|
71
|
-
/**
|
|
71
|
+
/** Stable host presentation choices for routine OneShot progress. */
|
|
72
|
+
export declare const REPLICATION_PROGRESS_PRESENTATIONS: Readonly<{
|
|
73
|
+
readonly QUIET: "quiet";
|
|
74
|
+
readonly NOTICE: "notice";
|
|
75
|
+
}>;
|
|
76
|
+
export type ReplicationProgressPresentation = (typeof REPLICATION_PROGRESS_PRESENTATIONS)[keyof typeof REPLICATION_PROGRESS_PRESENTATIONS];
|
|
77
|
+
/**
|
|
78
|
+
* A request made by an explicitly user-initiated one-shot command.
|
|
79
|
+
*
|
|
80
|
+
* Progress presentation is independent of interaction authority: a quiet
|
|
81
|
+
* command may still open an authorised peer-selection or failure-recovery
|
|
82
|
+
* dialogue when the operation requires a decision.
|
|
83
|
+
*/
|
|
72
84
|
export interface UserInitiatedOneShotRequest {
|
|
73
85
|
readonly trigger: "manual";
|
|
86
|
+
readonly progressPresentation: ReplicationProgressPresentation;
|
|
74
87
|
readonly interaction: InteractionAuthority;
|
|
75
88
|
}
|
|
76
89
|
/** A request made by an automatic finite replication trigger. */
|
|
@@ -193,9 +206,9 @@ export interface ReplicationAttemptFailure {
|
|
|
193
206
|
readonly setting: ObsidianLiveSyncSettings;
|
|
194
207
|
readonly outcome: ReplicationFailed;
|
|
195
208
|
}
|
|
196
|
-
/** Immutable failure source plus
|
|
209
|
+
/** Immutable failure source plus presentation and interaction policy from its original request. */
|
|
197
210
|
export interface ReplicationFailureRequest extends ReplicationAttemptFailure {
|
|
198
|
-
readonly
|
|
211
|
+
readonly progressPresentation: ReplicationProgressPresentation;
|
|
199
212
|
readonly interaction: InteractionAuthority;
|
|
200
213
|
}
|
|
201
214
|
/** Return whether one detached setting still belongs to the admitted publication. */
|
|
@@ -16,6 +16,10 @@ const USER_INITIATED_REPLICATION_AUTHORITY = Object.freeze({
|
|
|
16
16
|
failureRecovery: true
|
|
17
17
|
})
|
|
18
18
|
});
|
|
19
|
+
const REPLICATION_PROGRESS_PRESENTATIONS = Object.freeze({
|
|
20
|
+
QUIET: "quiet",
|
|
21
|
+
NOTICE: "notice"
|
|
22
|
+
});
|
|
19
23
|
const REPLICATION_COMPLETED = Object.freeze({ status: "completed" });
|
|
20
24
|
const REPLICATION_CANCELLED = Object.freeze({ status: "cancelled" });
|
|
21
25
|
const CENTRAL_REMOTE_REPLICATION_READINESS = Object.freeze({
|
|
@@ -74,7 +78,7 @@ function supportedOpenReplicationOneShot() {
|
|
|
74
78
|
const result = await replicator.openReplication(
|
|
75
79
|
setting,
|
|
76
80
|
false,
|
|
77
|
-
request.
|
|
81
|
+
request.progressPresentation === REPLICATION_PROGRESS_PRESENTATIONS.NOTICE,
|
|
78
82
|
false
|
|
79
83
|
);
|
|
80
84
|
return outcomeFromFiniteOpenReplication(result);
|
|
@@ -129,6 +133,7 @@ export {
|
|
|
129
133
|
PEER_REPLICATION_READINESS,
|
|
130
134
|
REPLICATION_CANCELLED,
|
|
131
135
|
REPLICATION_COMPLETED,
|
|
136
|
+
REPLICATION_PROGRESS_PRESENTATIONS,
|
|
132
137
|
USER_INITIATED_REPLICATION_AUTHORITY,
|
|
133
138
|
defineReplicatorProviderDefinitions,
|
|
134
139
|
isActiveReplicatorContextBoundToSetting,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { CAPABILITY_NOT_APPLICABLE, CAPABILITY_NOT_IMPLEMENTED, CENTRAL_REMOTE_REPLICATION_READINESS, NO_INTERACTION, PEER_REPLICATION_READINESS, REPLICATION_CANCELLED, REPLICATION_COMPLETED, USER_INITIATED_REPLICATION_AUTHORITY, defineReplicatorProviderDefinitions, isReplicationCompleted, outcomeFromContinuousOpenReplication, outcomeFromFiniteOpenReplication, replicationBlocked, replicationFailed, supportedOpenReplicationContinuous, supportedOpenReplicationOneShot, supportedOpenReplicationUnattended, supportedStopActiveTransfer, supportedCapability, } from "./ReplicatorProvider.js";
|
|
1
|
+
export { CAPABILITY_NOT_APPLICABLE, CAPABILITY_NOT_IMPLEMENTED, CENTRAL_REMOTE_REPLICATION_READINESS, NO_INTERACTION, PEER_REPLICATION_READINESS, REPLICATION_CANCELLED, REPLICATION_COMPLETED, REPLICATION_PROGRESS_PRESENTATIONS, USER_INITIATED_REPLICATION_AUTHORITY, defineReplicatorProviderDefinitions, isReplicationCompleted, outcomeFromContinuousOpenReplication, outcomeFromFiniteOpenReplication, replicationBlocked, replicationFailed, supportedOpenReplicationContinuous, supportedOpenReplicationOneShot, supportedOpenReplicationUnattended, supportedStopActiveTransfer, supportedCapability, } from "./ReplicatorProvider.js";
|
|
2
2
|
export type { ReplicatorInstance } from "./ReplicatorInstance.js";
|
|
3
|
-
export type { ActiveReplicatorContext, CapabilitySupport, ContinuousReplicationRequest, ContinuousReplicationTrigger, ContinuousRunner, InteractionAuthority, InteractionPermissions, NotApplicableCapability, NotImplementedCapability, NoInteraction, PermittedInteractionPermissions, ReplicationBlockReason, ReplicationAttemptFailure, ReplicationCancelled, ReplicationCompleted, ReplicationFailed, ReplicationInteraction, ReplicationOutcome, ReplicationReadinessRequirements, ReplicationPartial, ReplicationFailureRequest, ReplicatorConfigurationIdentity, ReplicatorProviderDefinition, ReplicatorProviderDefinitionMap, StopActiveTransferRunner, SupportedCapability, UnattendedOneShotRequest, UnattendedReplicationTrigger, UnattendedOneShotRunner, UserInitiatedOneShotRequest, UserInitiatedReplicationAuthority, UserInitiatedOneShotRunner, } from "./ReplicatorProvider.js";
|
|
3
|
+
export type { ActiveReplicatorContext, CapabilitySupport, ContinuousReplicationRequest, ContinuousReplicationTrigger, ContinuousRunner, InteractionAuthority, InteractionPermissions, NotApplicableCapability, NotImplementedCapability, NoInteraction, PermittedInteractionPermissions, ReplicationBlockReason, ReplicationAttemptFailure, ReplicationCancelled, ReplicationCompleted, ReplicationFailed, ReplicationInteraction, ReplicationOutcome, ReplicationProgressPresentation, ReplicationReadinessRequirements, ReplicationPartial, ReplicationFailureRequest, ReplicatorConfigurationIdentity, ReplicatorProviderDefinition, ReplicatorProviderDefinitionMap, StopActiveTransferRunner, SupportedCapability, UnattendedOneShotRequest, UnattendedReplicationTrigger, UnattendedOneShotRunner, UserInitiatedOneShotRequest, UserInitiatedReplicationAuthority, UserInitiatedOneShotRunner, } from "./ReplicatorProvider.js";
|
|
4
4
|
export { CAPABILITY_SUPPORT_KINDS, CAPABILITY_UNAVAILABLE_REASONS } from "./ProviderCapability.js";
|
|
5
5
|
export { NO_REMOTE_RESOURCE_CAPABILITIES, REMOTE_RESOURCE_KINDS } from "./RemoteResource.js";
|
|
6
6
|
export type { ConnectionProbeFactory, PreferredTweakProbe, PreferredTweakProbeFactory, RemoteConnectionProbe, RemoteConnectionProbeOptions, RemoteConnectionProbeResult, RemoteResourceCapabilities, RemoteResourceFactoryMap, RemoteResourceKind, RemoteResourceMap, SecuritySeedResource, SecuritySeedResourceFactory, SynchronisationInformationResource, SynchronisationInformationResourceFactory, } from "./RemoteResource.js";
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
PEER_REPLICATION_READINESS,
|
|
7
7
|
REPLICATION_CANCELLED,
|
|
8
8
|
REPLICATION_COMPLETED,
|
|
9
|
+
REPLICATION_PROGRESS_PRESENTATIONS,
|
|
9
10
|
USER_INITIATED_REPLICATION_AUTHORITY,
|
|
10
11
|
defineReplicatorProviderDefinitions,
|
|
11
12
|
isReplicationCompleted,
|
|
@@ -50,6 +51,7 @@ export {
|
|
|
50
51
|
REMOTE_RESOURCE_KINDS,
|
|
51
52
|
REPLICATION_CANCELLED,
|
|
52
53
|
REPLICATION_COMPLETED,
|
|
54
|
+
REPLICATION_PROGRESS_PRESENTATIONS,
|
|
53
55
|
USER_INITIATED_REPLICATION_AUTHORITY,
|
|
54
56
|
applyCentralRemoteAdministrationMutation,
|
|
55
57
|
centralRemoteAdministrationVerificationFailed,
|
|
@@ -6,6 +6,7 @@ import {
|
|
|
6
6
|
CAPABILITY_NOT_APPLICABLE,
|
|
7
7
|
NO_REMOTE_RESOURCE_CAPABILITIES,
|
|
8
8
|
PEER_REPLICATION_READINESS,
|
|
9
|
+
REPLICATION_PROGRESS_PRESENTATIONS,
|
|
9
10
|
defineReplicatorProviderDefinitions,
|
|
10
11
|
outcomeFromFiniteOpenReplication,
|
|
11
12
|
replicationBlocked,
|
|
@@ -38,7 +39,7 @@ function useP2PReplicatorFeature(host, openReplicationUIFactory, openRebuildUIFa
|
|
|
38
39
|
const result = await instance.openReplication(
|
|
39
40
|
setting,
|
|
40
41
|
false,
|
|
41
|
-
request.
|
|
42
|
+
request.progressPresentation === REPLICATION_PROGRESS_PRESENTATIONS.NOTICE,
|
|
42
43
|
false
|
|
43
44
|
);
|
|
44
45
|
return outcomeFromFiniteOpenReplication(result);
|
|
@@ -11,6 +11,7 @@ import {
|
|
|
11
11
|
CAPABILITY_SUPPORT_KINDS,
|
|
12
12
|
NO_INTERACTION,
|
|
13
13
|
CENTRAL_REMOTE_REPLICATION_READINESS,
|
|
14
|
+
REPLICATION_PROGRESS_PRESENTATIONS,
|
|
14
15
|
USER_INITIATED_REPLICATION_AUTHORITY,
|
|
15
16
|
isActiveReplicatorContextBoundToSetting,
|
|
16
17
|
isReplicationCompleted,
|
|
@@ -196,6 +197,7 @@ class ReplicationService extends ServiceBase {
|
|
|
196
197
|
onReplicationFailed = handlers().bailFirstFailure("onReplicationFailed");
|
|
197
198
|
async replicateUserInitiated(request = {
|
|
198
199
|
trigger: "manual",
|
|
200
|
+
progressPresentation: REPLICATION_PROGRESS_PRESENTATIONS.NOTICE,
|
|
199
201
|
interaction: USER_INITIATED_REPLICATION_AUTHORITY
|
|
200
202
|
}) {
|
|
201
203
|
return await this._typedReplication.runUserInitiated(request);
|
|
@@ -29,6 +29,7 @@ export interface TypedReplicationCoordinatorDependencies {
|
|
|
29
29
|
*/
|
|
30
30
|
export declare class TypedReplicationCoordinator {
|
|
31
31
|
private readonly dependencies;
|
|
32
|
+
private oneShotInProgress;
|
|
32
33
|
constructor(dependencies: TypedReplicationCoordinatorDependencies);
|
|
33
34
|
/**
|
|
34
35
|
* Execute a user-initiated finite role with the request's interaction authority.
|
|
@@ -67,6 +68,15 @@ export declare class TypedReplicationCoordinator {
|
|
|
67
68
|
* not a request to begin new remote work.
|
|
68
69
|
*/
|
|
69
70
|
stopActiveTransfer(): Promise<ReplicationOutcome>;
|
|
71
|
+
/**
|
|
72
|
+
* Admit one complete typed OneShot attempt for this service instance.
|
|
73
|
+
*
|
|
74
|
+
* Ownership is recorded before the operation reaches its first await and is
|
|
75
|
+
* retained through readiness, provider work, activity settlement, and
|
|
76
|
+
* failure handling. Later requests are neither queued nor joined: they
|
|
77
|
+
* settle immediately so repeated UI actions cannot replay after the owner.
|
|
78
|
+
*/
|
|
79
|
+
private runOneShot;
|
|
70
80
|
private finishFiniteAttempt;
|
|
71
81
|
private acquireContext;
|
|
72
82
|
private acquireReadyContext;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
CAPABILITY_SUPPORT_KINDS,
|
|
3
3
|
NO_INTERACTION,
|
|
4
|
+
REPLICATION_PROGRESS_PRESENTATIONS,
|
|
4
5
|
isActiveReplicatorContextBoundToSetting,
|
|
5
6
|
replicationBlocked,
|
|
6
7
|
replicationFailed
|
|
@@ -11,6 +12,7 @@ class TypedReplicationCoordinator {
|
|
|
11
12
|
this.dependencies = dependencies;
|
|
12
13
|
}
|
|
13
14
|
dependencies;
|
|
15
|
+
oneShotInProgress = false;
|
|
14
16
|
/**
|
|
15
17
|
* Execute a user-initiated finite role with the request's interaction authority.
|
|
16
18
|
*
|
|
@@ -20,23 +22,25 @@ class TypedReplicationCoordinator {
|
|
|
20
22
|
* activity has settled.
|
|
21
23
|
*/
|
|
22
24
|
async runUserInitiated(request) {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
25
|
+
return await this.runOneShot(async () => {
|
|
26
|
+
const showProgress = request.progressPresentation === REPLICATION_PROGRESS_PRESENTATIONS.NOTICE;
|
|
27
|
+
const ready = await this.acquireReadyContext(showProgress);
|
|
28
|
+
if ("status" in ready) return ready;
|
|
29
|
+
const capability = ready.provider.userInitiatedOneShot;
|
|
30
|
+
if (capability.kind !== CAPABILITY_SUPPORT_KINDS.SUPPORTED) {
|
|
31
|
+
return this.finishFiniteAttempt(replicationBlocked(capability.reason));
|
|
32
|
+
}
|
|
33
|
+
const settings = Object.freeze(asCopy(this.dependencies.currentSettings()));
|
|
34
|
+
return this.finishFiniteAttempt(
|
|
35
|
+
await this.runAdmittedFiniteActivity(
|
|
36
|
+
ready,
|
|
37
|
+
(context) => capability.run(context.replicator, settings, request),
|
|
38
|
+
settings,
|
|
39
|
+
request.interaction,
|
|
40
|
+
request.progressPresentation
|
|
41
|
+
)
|
|
42
|
+
);
|
|
43
|
+
});
|
|
40
44
|
}
|
|
41
45
|
/**
|
|
42
46
|
* Execute an unattended finite role without granting interaction authority.
|
|
@@ -49,22 +53,24 @@ class TypedReplicationCoordinator {
|
|
|
49
53
|
if (request.interaction.kind !== NO_INTERACTION.kind) {
|
|
50
54
|
return replicationBlocked("interaction-required");
|
|
51
55
|
}
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
56
|
+
return await this.runOneShot(async () => {
|
|
57
|
+
const ready = await this.acquireReadyContext(false);
|
|
58
|
+
if ("status" in ready) return ready;
|
|
59
|
+
const capability = ready.provider.unattendedOneShot;
|
|
60
|
+
if (capability.kind !== CAPABILITY_SUPPORT_KINDS.SUPPORTED) {
|
|
61
|
+
return this.finishFiniteAttempt(replicationBlocked(capability.reason));
|
|
62
|
+
}
|
|
63
|
+
const settings = Object.freeze(asCopy(this.dependencies.currentSettings()));
|
|
64
|
+
return this.finishFiniteAttempt(
|
|
65
|
+
await this.runAdmittedFiniteActivity(
|
|
66
|
+
ready,
|
|
67
|
+
(context) => capability.run(context.replicator, settings, request),
|
|
68
|
+
settings,
|
|
69
|
+
NO_INTERACTION,
|
|
70
|
+
REPLICATION_PROGRESS_PRESENTATIONS.QUIET
|
|
71
|
+
)
|
|
72
|
+
);
|
|
73
|
+
});
|
|
68
74
|
}
|
|
69
75
|
/**
|
|
70
76
|
* Start the provider's continuous role outside finite activity accounting.
|
|
@@ -130,6 +136,23 @@ class TypedReplicationCoordinator {
|
|
|
130
136
|
);
|
|
131
137
|
return admitted ?? replicationBlocked("no-active-replicator");
|
|
132
138
|
}
|
|
139
|
+
/**
|
|
140
|
+
* Admit one complete typed OneShot attempt for this service instance.
|
|
141
|
+
*
|
|
142
|
+
* Ownership is recorded before the operation reaches its first await and is
|
|
143
|
+
* retained through readiness, provider work, activity settlement, and
|
|
144
|
+
* failure handling. Later requests are neither queued nor joined: they
|
|
145
|
+
* settle immediately so repeated UI actions cannot replay after the owner.
|
|
146
|
+
*/
|
|
147
|
+
async runOneShot(run) {
|
|
148
|
+
if (this.oneShotInProgress) return replicationBlocked("replication-in-progress");
|
|
149
|
+
this.oneShotInProgress = true;
|
|
150
|
+
try {
|
|
151
|
+
return await run();
|
|
152
|
+
} finally {
|
|
153
|
+
this.oneShotInProgress = false;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
133
156
|
finishFiniteAttempt(outcome) {
|
|
134
157
|
this.dependencies.recordFiniteAttempt();
|
|
135
158
|
return outcome;
|
|
@@ -164,7 +187,7 @@ class TypedReplicationCoordinator {
|
|
|
164
187
|
* is rejected instead of running work prepared for an older publication.
|
|
165
188
|
* Failure recovery runs only after the reservation has been released.
|
|
166
189
|
*/
|
|
167
|
-
async runAdmittedFiniteActivity(expectedContext, run, setting, interaction,
|
|
190
|
+
async runAdmittedFiniteActivity(expectedContext, run, setting, interaction, progressPresentation) {
|
|
168
191
|
const admitted = await this.dependencies.replicatorService.runWithActiveReplicatorContext((context) => {
|
|
169
192
|
if (context !== expectedContext || !isActiveReplicatorContextBoundToSetting(context, setting)) {
|
|
170
193
|
return replicationBlocked("not-ready");
|
|
@@ -178,7 +201,7 @@ class TypedReplicationCoordinator {
|
|
|
178
201
|
context: expectedContext,
|
|
179
202
|
setting,
|
|
180
203
|
outcome: result,
|
|
181
|
-
|
|
204
|
+
progressPresentation,
|
|
182
205
|
interaction
|
|
183
206
|
})
|
|
184
207
|
);
|
package/package.json
CHANGED