@vrtmrz/livesync-commonlib 0.2.2 → 0.2.3
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 +60 -0
- package/dist/ContentSplitter/ContentSplitterRabinKarp.js +2 -17
- package/dist/ContentSplitter/ContentSplitterV1.js +9 -19
- package/dist/ContentSplitter/ContentSplitterV2.js +9 -19
- package/dist/common/utils.d.ts +33 -3
- package/dist/common/utils.js +12 -44
- package/dist/common/utils.object.js +2 -1
- package/dist/concurrency/keyedPendingWork.d.ts +13 -0
- package/dist/concurrency/keyedPendingWork.js +27 -0
- package/dist/conflicts/conflictCheckQueue.d.ts +45 -0
- package/dist/conflicts/conflictCheckQueue.js +108 -0
- package/dist/conflicts.d.ts +3 -0
- package/dist/conflicts.js +4 -0
- package/dist/diagnostics/integrity.js +11 -12
- package/dist/interfaces/ServiceModule.d.ts +42 -24
- package/dist/interfaces/ServiceModule.js +4 -3
- package/dist/managers/StorageEventManager.d.ts +3 -3
- package/dist/managers/StorageEventManager.js +15 -6
- package/dist/pouchdb/LiveSyncLocalDB.js +1 -0
- package/dist/pouchdb/chunks.d.ts +16 -0
- package/dist/pouchdb/pouchdb-browser.js +2 -84
- package/dist/pouchdb/pouchdb-http.js +2 -84
- package/dist/pouchdb/purgeMulti.d.ts +4 -0
- package/dist/pouchdb/purgeMulti.js +89 -0
- package/dist/replication/journal/JournalSyncCore.js +6 -1
- package/dist/replication/trystero/TrysteroReplicator.js +3 -4
- package/dist/replication/trystero/TrysteroReplicatorP2PServer.js +3 -2
- package/dist/replication/trystero/peerPatterns.d.ts +1 -0
- package/dist/replication/trystero/peerPatterns.js +6 -0
- package/dist/rpc/RpcRoom.d.ts +5 -1
- package/dist/rpc/RpcRoom.js +80 -105
- package/dist/serviceFeatures/offlineScanner.d.ts +4 -4
- package/dist/serviceFeatures/offlineScanner.js +14 -15
- package/dist/serviceFeatures/prepareDatabaseForUse.d.ts +2 -2
- package/dist/serviceFeatures/remoteConfig.d.ts +10 -5
- package/dist/serviceModules/ServiceFileHandlerBase.js +2 -7
- package/dist/services/implements/injectable/InjectableConflictService.d.ts +5 -5
- package/dist/services/lib/HandlerUtils.d.ts +8 -6
- package/dist/services/lib/HandlerUtils.js +14 -0
- package/dist/worker/bgWorker.js +1 -1
- package/docs/conflict-resolution.md +30 -0
- package/docs/development.md +46 -0
- package/docs/proven-in-use.md +63 -0
- package/docs/releasing.md +2 -0
- package/docs/service-feature-composition.md +56 -9
- package/package.json +10 -2
|
@@ -20,29 +20,49 @@ export interface ServiceModules {
|
|
|
20
20
|
*/
|
|
21
21
|
rebuilder: Rebuilder;
|
|
22
22
|
}
|
|
23
|
-
|
|
23
|
+
/**
|
|
24
|
+
* Selects service dependencies from the host's service type.
|
|
25
|
+
* A context is retained when the selected source exposes one. The default
|
|
26
|
+
* source preserves the existing ServiceHub dependency contract.
|
|
27
|
+
*/
|
|
28
|
+
export type RequiredServices<T extends keyof THub, THub = ServiceHub> = Pick<THub, T | Extract<"context", keyof THub>>;
|
|
24
29
|
export type RequiredServiceModules<T extends keyof ServiceModules> = Pick<ServiceModules, T>;
|
|
25
|
-
export type RequiredServicesInterfaces<T extends keyof IServiceHub> =
|
|
30
|
+
export type RequiredServicesInterfaces<T extends keyof IServiceHub> = RequiredServices<T, IServiceHub>;
|
|
26
31
|
export type RequiredServiceModulesInterfaces<T extends keyof ServiceModules> = Pick<ServiceModules, T>;
|
|
27
|
-
|
|
28
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Declares the Services and ServiceModules consumed by a feature.
|
|
34
|
+
* Select a more specific source to retain injectable handlers or an extended
|
|
35
|
+
* host context, or use IServiceHub for operation-only interfaces. Additional
|
|
36
|
+
* collaborators, such as a borrowed LogFunction, belong in the feature's own
|
|
37
|
+
* dependency type. This type neither selects runtime values nor owns them.
|
|
38
|
+
*/
|
|
39
|
+
export type NecessaryServices<T extends keyof THub, U extends keyof ServiceModules, THub = ServiceHub> = {
|
|
40
|
+
services: RequiredServices<T, THub>;
|
|
29
41
|
serviceModules: RequiredServiceModules<U>;
|
|
30
42
|
};
|
|
31
|
-
export type NecessaryServicesInterfaces<T extends keyof IServiceHub, U extends keyof ServiceModules> =
|
|
32
|
-
|
|
33
|
-
serviceModules: RequiredServiceModulesInterfaces<U>;
|
|
34
|
-
};
|
|
35
|
-
export type ServiceFeatureFunction<T extends keyof ServiceHub, U extends keyof ServiceModules, TR> = (host: NecessaryServices<T, U>) => TR;
|
|
43
|
+
export type NecessaryServicesInterfaces<T extends keyof IServiceHub, U extends keyof ServiceModules> = NecessaryServices<T, U, IServiceHub>;
|
|
44
|
+
export type ServiceFeatureFunction<T extends keyof THub, U extends keyof ServiceModules, TR, THub = ServiceHub> = (host: NecessaryServices<T, U, THub>) => TR;
|
|
36
45
|
type ServiceFeatureContext<T> = T & {
|
|
37
46
|
_log: LogFunction;
|
|
38
47
|
};
|
|
39
|
-
export type ServiceFeatureFunctionWithContext<T extends keyof
|
|
48
|
+
export type ServiceFeatureFunctionWithContext<T extends keyof THub, U extends keyof ServiceModules, C, TR, THub = ServiceHub> = (host: NecessaryServices<T, U, THub>, context: ServiceFeatureContext<C>) => TR;
|
|
49
|
+
type AnyServiceFeature = (...args: never[]) => unknown;
|
|
50
|
+
type ServiceFeatureWithArguments<T extends keyof THub, U extends keyof ServiceModules, THub = ServiceHub> = (host: NecessaryServices<T, U, THub>, ...args: never[]) => unknown;
|
|
40
51
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
*
|
|
52
|
+
* Retains a service feature's complete function type and identity.
|
|
53
|
+
* Declare the dependency type on the feature parameter so that required
|
|
54
|
+
* collaborators, concrete service types, and returned views remain explicit.
|
|
55
|
+
* Optional arguments, overloads, generic signatures, and asynchronous results
|
|
56
|
+
* are preserved. Older unannotated callbacks retain their ServiceHub context;
|
|
57
|
+
* their dependency keys cannot be inferred from the callback body.
|
|
58
|
+
*
|
|
59
|
+
* This helper does not invoke the feature, acquire or release its dependencies,
|
|
60
|
+
* or wrap its return value or errors. The host owns composition and disposal.
|
|
61
|
+
* @param featureFunction The feature function to retain.
|
|
62
|
+
* @returns The supplied function without a runtime wrapper.
|
|
44
63
|
* @example
|
|
45
|
-
*
|
|
64
|
+
* type IndexHost = NecessaryServices<"appLifecycle", "storageAccess">;
|
|
65
|
+
* const useIndexFeature = createServiceFeature((host: IndexHost) => {
|
|
46
66
|
* const { appLifecycle } = host.services;
|
|
47
67
|
* const { storageAccess } = host.serviceModules;
|
|
48
68
|
*
|
|
@@ -51,18 +71,16 @@ export type ServiceFeatureFunctionWithContext<T extends keyof ServiceHub, U exte
|
|
|
51
71
|
* return true;
|
|
52
72
|
* });
|
|
53
73
|
* });
|
|
54
|
-
*
|
|
55
|
-
* useIndexFeature({
|
|
56
|
-
* services: { context: serviceHub.context, appLifecycle: serviceHub.appLifecycle },
|
|
57
|
-
* serviceModules: { storageAccess },
|
|
58
|
-
* });
|
|
59
74
|
*/
|
|
75
|
+
export declare function createServiceFeature<TFeature extends ServiceFeatureWithArguments<keyof ServiceHub, keyof ServiceModules>>(featureFunction: TFeature): TFeature;
|
|
76
|
+
export declare function createServiceFeature<TFeature extends AnyServiceFeature>(featureFunction: TFeature): TFeature;
|
|
60
77
|
export declare function createServiceFeature<T extends keyof ServiceHub, U extends keyof ServiceModules, TR>(featureFunction: ServiceFeatureFunction<T, U, TR>): ServiceFeatureFunction<T, U, TR>;
|
|
61
|
-
type ContextFactory<T extends keyof
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
78
|
+
type ContextFactory<T extends keyof THub, U extends keyof ServiceModules, C, THub = ServiceHub> = (host: NecessaryServices<T, U, THub>) => ServiceFeatureContext<C>;
|
|
79
|
+
/** Selects the host type for the existing service-feature builder. */
|
|
80
|
+
export declare function serviceFeature<T extends keyof THub, U extends keyof ServiceModules, THub = ServiceHub>(): {
|
|
81
|
+
create: <TR, TFeature extends (host: NecessaryServices<T, U, THub>, ...args: never[]) => TR = ServiceFeatureFunction<T, U, TR, THub>>(featureFunction: TFeature) => TFeature;
|
|
82
|
+
withContext<C extends object = object>(ContextFactory: ContextFactory<T, U, C, THub>): {
|
|
83
|
+
create: <TR>(featureFunction: ServiceFeatureFunctionWithContext<T, U, C, TR, THub>) => (host: NecessaryServices<T, U, THub>, context: ServiceFeatureContext<C>) => TR;
|
|
66
84
|
};
|
|
67
85
|
};
|
|
68
86
|
export {};
|
|
@@ -2,10 +2,11 @@ function createServiceFeature(featureFunction) {
|
|
|
2
2
|
return featureFunction;
|
|
3
3
|
}
|
|
4
4
|
function serviceFeature() {
|
|
5
|
+
function create(featureFunction) {
|
|
6
|
+
return featureFunction;
|
|
7
|
+
}
|
|
5
8
|
return {
|
|
6
|
-
create
|
|
7
|
-
return featureFunction;
|
|
8
|
-
},
|
|
9
|
+
create,
|
|
9
10
|
withContext(ContextFactory) {
|
|
10
11
|
return {
|
|
11
12
|
create: (featureFunction) => (host, context) => featureFunction(host, ContextFactory(host))
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type FileEventType, type FilePath, type UXFileInfoStub, type UXFolderInfo, type UXInternalFileInfoStub } from "../common/types.js";
|
|
2
2
|
import { type FileEventItem } from "../common/types.js";
|
|
3
|
+
import { type DeferredAction } from "octagonal-wheels/concurrency/deferredAction";
|
|
3
4
|
import type { IStorageAccessManager } from "../interfaces/StorageAccess.js";
|
|
4
5
|
import { type PromiseWithResolvers } from "octagonal-wheels/promises";
|
|
5
6
|
import { StorageEventManager, type FileEvent } from "../interfaces/StorageEventManager.js";
|
|
@@ -8,12 +9,11 @@ import type { SettingService } from "../services/base/SettingService.js";
|
|
|
8
9
|
import type { FileProcessingService } from "../services/base/FileProcessingService.js";
|
|
9
10
|
import { createInstanceLogFunction } from "../services/lib/logUtils.js";
|
|
10
11
|
import type { IStorageEventManagerAdapter } from "./adapters/index.js";
|
|
11
|
-
import { type CompatTimeoutHandle } from "../common/coreEnvFunctions.js";
|
|
12
12
|
type WaitInfo = {
|
|
13
13
|
since: number;
|
|
14
14
|
type: FileEventType;
|
|
15
15
|
canProceed: PromiseWithResolvers<boolean>;
|
|
16
|
-
|
|
16
|
+
deadline: DeferredAction;
|
|
17
17
|
event: FileEventItem;
|
|
18
18
|
};
|
|
19
19
|
declare const TYPE_SENTINEL_FLUSH = "SENTINEL_FLUSH";
|
|
@@ -88,7 +88,7 @@ export declare abstract class StorageEventManagerBase<TAdapter extends IStorageE
|
|
|
88
88
|
enqueue(newItem: FileEventItem): void;
|
|
89
89
|
private _triggerTakeSnapshot;
|
|
90
90
|
triggerTakeSnapshot: import("octagonal-wheels/function").ThrottledFunction<() => void>;
|
|
91
|
-
protected concurrentProcessing: import("octagonal-wheels/concurrency/
|
|
91
|
+
protected concurrentProcessing: import("octagonal-wheels/concurrency/semaphore").SemaphoreObject;
|
|
92
92
|
protected _waitingMap: Map<string, WaitInfo>;
|
|
93
93
|
private pendingOperations;
|
|
94
94
|
private releaseOperations;
|
|
@@ -10,10 +10,17 @@ import { delay, fireAndForget, isRemediationModeActive, throttle } from "../comm
|
|
|
10
10
|
import {} from "../common/types.js";
|
|
11
11
|
import { serialized } from "octagonal-wheels/concurrency/lock";
|
|
12
12
|
import { Semaphore } from "octagonal-wheels/concurrency/semaphore";
|
|
13
|
+
import {
|
|
14
|
+
createDeferredAction
|
|
15
|
+
} from "octagonal-wheels/concurrency/deferredAction";
|
|
13
16
|
import { promiseWithResolvers } from "octagonal-wheels/promises";
|
|
14
17
|
import { StorageEventManager } from "../interfaces/StorageEventManager.js";
|
|
15
18
|
import { createInstanceLogFunction } from "../services/lib/logUtils.js";
|
|
16
19
|
import { compatGlobal } from "../common/coreEnvFunctions.js";
|
|
20
|
+
const scheduleStorageTimeout = (callback, delayMs) => {
|
|
21
|
+
const handle = compatGlobal.setTimeout(callback, delayMs);
|
|
22
|
+
return () => compatGlobal.clearTimeout(handle);
|
|
23
|
+
};
|
|
17
24
|
const TYPE_SENTINEL_FLUSH = "SENTINEL_FLUSH";
|
|
18
25
|
class StorageEventManagerBase extends StorageEventManager {
|
|
19
26
|
_log;
|
|
@@ -311,7 +318,7 @@ class StorageEventManagerBase extends StorageEventManager {
|
|
|
311
318
|
const waitInfo = this._waitingMap.get(key);
|
|
312
319
|
if (waitInfo) {
|
|
313
320
|
waitInfo.canProceed.resolve(true);
|
|
314
|
-
|
|
321
|
+
waitInfo.deadline.dispose();
|
|
315
322
|
this._waitingMap.delete(key);
|
|
316
323
|
}
|
|
317
324
|
this.triggerTakeSnapshot();
|
|
@@ -323,7 +330,7 @@ class StorageEventManagerBase extends StorageEventManager {
|
|
|
323
330
|
const waitInfo = this._waitingMap.get(key);
|
|
324
331
|
if (waitInfo) {
|
|
325
332
|
waitInfo.canProceed.resolve(false);
|
|
326
|
-
|
|
333
|
+
waitInfo.deadline.dispose();
|
|
327
334
|
this._waitingMap.delete(key);
|
|
328
335
|
}
|
|
329
336
|
this.triggerTakeSnapshot();
|
|
@@ -355,16 +362,18 @@ class StorageEventManagerBase extends StorageEventManager {
|
|
|
355
362
|
LOG_LEVEL_DEBUG
|
|
356
363
|
);
|
|
357
364
|
}
|
|
365
|
+
const deadline = createDeferredAction(() => {
|
|
366
|
+
this._log(`Processing ${key}: Batch save timeout reached: ${event.type}`, LOG_LEVEL_DEBUG);
|
|
367
|
+
this._proceedWaiting(key);
|
|
368
|
+
}, scheduleStorageTimeout);
|
|
358
369
|
const waitInfo = {
|
|
359
370
|
since,
|
|
360
371
|
type: event.type,
|
|
361
372
|
event,
|
|
362
373
|
canProceed: resolver,
|
|
363
|
-
|
|
364
|
-
this._log(`Processing ${key}: Batch save timeout reached: ${event.type}`, LOG_LEVEL_DEBUG);
|
|
365
|
-
this._proceedWaiting(key);
|
|
366
|
-
}, nextDelay)
|
|
374
|
+
deadline
|
|
367
375
|
};
|
|
376
|
+
deadline.schedule(nextDelay);
|
|
368
377
|
this._waitingMap.set(key, waitInfo);
|
|
369
378
|
this.triggerTakeSnapshot();
|
|
370
379
|
return waitInfo;
|
|
@@ -185,6 +185,7 @@ class LiveSyncLocalDB {
|
|
|
185
185
|
this._log("Database initialisation was rejected by a required module.", LOG_LEVEL_NOTICE);
|
|
186
186
|
return false;
|
|
187
187
|
}
|
|
188
|
+
manager.changeManager.restartWatch();
|
|
188
189
|
this._log("Opening Database...");
|
|
189
190
|
this._log("Database info", LOG_LEVEL_VERBOSE);
|
|
190
191
|
this._log(JSON.stringify(await openedDatabase.info(), null, 2), LOG_LEVEL_VERBOSE);
|
package/dist/pouchdb/chunks.d.ts
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
1
1
|
import type { CouchDBConnection } from "../common/types.js";
|
|
2
2
|
export declare function purgeUnreferencedChunks(db: PouchDB.Database, dryRun: boolean, connSetting?: CouchDBConnection, performCompact?: boolean): Promise<number>;
|
|
3
|
+
/**
|
|
4
|
+
* Copy chunks missing from the destination, preserving their source revisions.
|
|
5
|
+
*
|
|
6
|
+
* @deprecated Obsolete purge-based cleanup compatibility helper. Retained for
|
|
7
|
+
* existing package consumers; current Self-hosted LiveSync no longer calls it.
|
|
8
|
+
* Garbage Collection V3 propagates ordinary deletion revisions instead.
|
|
9
|
+
*/
|
|
3
10
|
export declare function transferChunks(key: string, label: string, dbFrom: PouchDB.Database, dbTo: PouchDB.Database, items: {
|
|
4
11
|
id: string;
|
|
5
12
|
rev: string;
|
|
6
13
|
}[]): Promise<boolean>;
|
|
14
|
+
/**
|
|
15
|
+
* Complement chunks between databases which were separately purged.
|
|
16
|
+
* @deprecated Obsolete cleanup compatibility wrapper around transferChunks.
|
|
17
|
+
* Current Self-hosted LiveSync uses its standard locked-remote recovery flow.
|
|
18
|
+
*/
|
|
7
19
|
export declare function balanceChunkPurgedDBs(local: PouchDB.Database, remote: PouchDB.Database): Promise<void>;
|
|
20
|
+
/**
|
|
21
|
+
* @deprecated Obsolete transferChunks compatibility wrapper with no callers in
|
|
22
|
+
* the current Commonlib or Self-hosted LiveSync source trees.
|
|
23
|
+
*/
|
|
8
24
|
export declare function fetchAllUsedChunks(local: PouchDB.Database, remote: PouchDB.Database): Promise<void>;
|
|
9
25
|
export declare function purgeChunksLocal(db: PouchDB.Database, docs: {
|
|
10
26
|
id: string;
|
|
@@ -6,91 +6,9 @@ import mapreduce from "pouchdb-mapreduce";
|
|
|
6
6
|
import replication from "pouchdb-replication";
|
|
7
7
|
import find from "pouchdb-find";
|
|
8
8
|
import transform from "transform-pouch";
|
|
9
|
-
import {
|
|
10
|
-
import { adapterFun } from "pouchdb-utils";
|
|
11
|
-
import { createError, MISSING_DOC, UNKNOWN_ERROR } from "pouchdb-errors";
|
|
12
|
-
import { mapAllTasksWithConcurrencyLimit, unwrapTaskResult } from "octagonal-wheels/concurrency/task";
|
|
9
|
+
import { installPurgeMulti } from "./purgeMulti.js";
|
|
13
10
|
PouchDB.plugin(IDBPouch).plugin(INDEXEDDBPouch).plugin(HttpPouch).plugin(mapreduce).plugin(replication).plugin(find).plugin(transform);
|
|
14
|
-
|
|
15
|
-
return db.get("_local/purges").then(function(doc) {
|
|
16
|
-
for (const [docId, rev$$1] of docs) {
|
|
17
|
-
const purgeSeq = doc.purgeSeq + 1;
|
|
18
|
-
doc.purges.push({
|
|
19
|
-
docId,
|
|
20
|
-
rev: rev$$1,
|
|
21
|
-
purgeSeq
|
|
22
|
-
});
|
|
23
|
-
if (doc.purges.length > db.purged_infos_limit) {
|
|
24
|
-
doc.purges.splice(0, doc.purges.length - db.purged_infos_limit);
|
|
25
|
-
}
|
|
26
|
-
doc.purgeSeq = purgeSeq;
|
|
27
|
-
}
|
|
28
|
-
return doc;
|
|
29
|
-
}).catch(function(err) {
|
|
30
|
-
if (err.status !== 404) {
|
|
31
|
-
throw err;
|
|
32
|
-
}
|
|
33
|
-
return {
|
|
34
|
-
_id: "_local/purges",
|
|
35
|
-
purges: docs.map(([docId, rev$$1], idx) => ({
|
|
36
|
-
docId,
|
|
37
|
-
rev: rev$$1,
|
|
38
|
-
purgeSeq: idx
|
|
39
|
-
})),
|
|
40
|
-
purgeSeq: docs.length
|
|
41
|
-
};
|
|
42
|
-
}).then(function(doc) {
|
|
43
|
-
return db.put(doc);
|
|
44
|
-
});
|
|
45
|
-
}
|
|
46
|
-
PouchDB.prototype.purgeMulti = adapterFun(
|
|
47
|
-
"_purgeMulti",
|
|
48
|
-
function(docs, callback) {
|
|
49
|
-
if (typeof this._purge === "undefined") {
|
|
50
|
-
return callback(
|
|
51
|
-
//@ts-ignore: this ts-ignore might be hiding a `this` bug where we don't have "this" conext.
|
|
52
|
-
createError(UNKNOWN_ERROR, "Purge is not implemented in the " + this.adapter + " adapter.")
|
|
53
|
-
);
|
|
54
|
-
}
|
|
55
|
-
const self = this;
|
|
56
|
-
const tasks = docs.map(
|
|
57
|
-
(param) => () => new Promise((res, rej) => {
|
|
58
|
-
const [docId, rev$$1] = param;
|
|
59
|
-
self._getRevisionTree(docId, (error, revs) => {
|
|
60
|
-
if (error) {
|
|
61
|
-
return res([param, error]);
|
|
62
|
-
}
|
|
63
|
-
if (!revs) {
|
|
64
|
-
return res([param, createError(MISSING_DOC)]);
|
|
65
|
-
}
|
|
66
|
-
let path;
|
|
67
|
-
try {
|
|
68
|
-
path = findPathToLeaf(revs, rev$$1);
|
|
69
|
-
} catch (error2) {
|
|
70
|
-
return res([param, error2.message || error2]);
|
|
71
|
-
}
|
|
72
|
-
self._purge(docId, path, (error2, result) => {
|
|
73
|
-
if (error2) {
|
|
74
|
-
return res([param, error2]);
|
|
75
|
-
} else {
|
|
76
|
-
return res([param, result]);
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
})
|
|
81
|
-
);
|
|
82
|
-
(async () => {
|
|
83
|
-
const ret = await mapAllTasksWithConcurrencyLimit(1, tasks);
|
|
84
|
-
const retAll = ret.map((e) => unwrapTaskResult(e));
|
|
85
|
-
await appendPurgeSeqs(
|
|
86
|
-
self,
|
|
87
|
-
retAll.filter((e) => "ok" in e[1]).map((e) => e[0])
|
|
88
|
-
);
|
|
89
|
-
const result = Object.fromEntries(retAll.map((e) => [e[0][0], e[1]]));
|
|
90
|
-
return result;
|
|
91
|
-
})().then((result) => callback(void 0, result)).catch((error) => callback(error));
|
|
92
|
-
}
|
|
93
|
-
);
|
|
11
|
+
installPurgeMulti(PouchDB);
|
|
94
12
|
export {
|
|
95
13
|
PouchDB
|
|
96
14
|
};
|
|
@@ -4,91 +4,9 @@ import mapreduce from "pouchdb-mapreduce";
|
|
|
4
4
|
import replication from "pouchdb-replication";
|
|
5
5
|
import find from "pouchdb-find";
|
|
6
6
|
import transform from "transform-pouch";
|
|
7
|
-
import {
|
|
8
|
-
import { adapterFun } from "pouchdb-utils";
|
|
9
|
-
import { createError, MISSING_DOC, UNKNOWN_ERROR } from "pouchdb-errors";
|
|
10
|
-
import { mapAllTasksWithConcurrencyLimit, unwrapTaskResult } from "octagonal-wheels/concurrency/task";
|
|
7
|
+
import { installPurgeMulti } from "./purgeMulti.js";
|
|
11
8
|
PouchDB.plugin(HttpPouch).plugin(mapreduce).plugin(replication).plugin(find).plugin(transform);
|
|
12
|
-
|
|
13
|
-
return db.get("_local/purges").then(function(doc) {
|
|
14
|
-
for (const [docId, rev$$1] of docs) {
|
|
15
|
-
const purgeSeq = doc.purgeSeq + 1;
|
|
16
|
-
doc.purges.push({
|
|
17
|
-
docId,
|
|
18
|
-
rev: rev$$1,
|
|
19
|
-
purgeSeq
|
|
20
|
-
});
|
|
21
|
-
if (doc.purges.length > db.purged_infos_limit) {
|
|
22
|
-
doc.purges.splice(0, doc.purges.length - db.purged_infos_limit);
|
|
23
|
-
}
|
|
24
|
-
doc.purgeSeq = purgeSeq;
|
|
25
|
-
}
|
|
26
|
-
return doc;
|
|
27
|
-
}).catch(function(err) {
|
|
28
|
-
if (err.status !== 404) {
|
|
29
|
-
throw err;
|
|
30
|
-
}
|
|
31
|
-
return {
|
|
32
|
-
_id: "_local/purges",
|
|
33
|
-
purges: docs.map(([docId, rev$$1], idx) => ({
|
|
34
|
-
docId,
|
|
35
|
-
rev: rev$$1,
|
|
36
|
-
purgeSeq: idx
|
|
37
|
-
})),
|
|
38
|
-
purgeSeq: docs.length
|
|
39
|
-
};
|
|
40
|
-
}).then(function(doc) {
|
|
41
|
-
return db.put(doc);
|
|
42
|
-
});
|
|
43
|
-
}
|
|
44
|
-
PouchDB.prototype.purgeMulti = adapterFun(
|
|
45
|
-
"_purgeMulti",
|
|
46
|
-
function(docs, callback) {
|
|
47
|
-
if (typeof this._purge === "undefined") {
|
|
48
|
-
return callback(
|
|
49
|
-
//@ts-ignore: this ts-ignore might be hiding a `this` bug where we don't have "this" conext.
|
|
50
|
-
createError(UNKNOWN_ERROR, "Purge is not implemented in the " + this.adapter + " adapter.")
|
|
51
|
-
);
|
|
52
|
-
}
|
|
53
|
-
const self = this;
|
|
54
|
-
const tasks = docs.map(
|
|
55
|
-
(param) => () => new Promise((res, rej) => {
|
|
56
|
-
const [docId, rev$$1] = param;
|
|
57
|
-
self._getRevisionTree(docId, (error, revs) => {
|
|
58
|
-
if (error) {
|
|
59
|
-
return res([param, error]);
|
|
60
|
-
}
|
|
61
|
-
if (!revs) {
|
|
62
|
-
return res([param, createError(MISSING_DOC)]);
|
|
63
|
-
}
|
|
64
|
-
let path;
|
|
65
|
-
try {
|
|
66
|
-
path = findPathToLeaf(revs, rev$$1);
|
|
67
|
-
} catch (error2) {
|
|
68
|
-
return res([param, error2.message || error2]);
|
|
69
|
-
}
|
|
70
|
-
self._purge(docId, path, (error2, result) => {
|
|
71
|
-
if (error2) {
|
|
72
|
-
return res([param, error2]);
|
|
73
|
-
} else {
|
|
74
|
-
return res([param, result]);
|
|
75
|
-
}
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
})
|
|
79
|
-
);
|
|
80
|
-
(async () => {
|
|
81
|
-
const ret = await mapAllTasksWithConcurrencyLimit(1, tasks);
|
|
82
|
-
const retAll = ret.map((e) => unwrapTaskResult(e));
|
|
83
|
-
await appendPurgeSeqs(
|
|
84
|
-
self,
|
|
85
|
-
retAll.filter((e) => "ok" in e[1]).map((e) => e[0])
|
|
86
|
-
);
|
|
87
|
-
const result = Object.fromEntries(retAll.map((e) => [e[0][0], e[1]]));
|
|
88
|
-
return result;
|
|
89
|
-
})().then((result) => callback(void 0, result)).catch((error) => callback(error));
|
|
90
|
-
}
|
|
91
|
-
);
|
|
9
|
+
installPurgeMulti(PouchDB);
|
|
92
10
|
export {
|
|
93
11
|
PouchDB
|
|
94
12
|
};
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import { findPathToLeaf } from "pouchdb-merge";
|
|
2
|
+
import { adapterFun } from "pouchdb-utils";
|
|
3
|
+
import { createError, MISSING_DOC, UNKNOWN_ERROR } from "pouchdb-errors";
|
|
4
|
+
import { mapAllTasksWithConcurrencyLimit, unwrapTaskResult } from "octagonal-wheels/concurrency/task";
|
|
5
|
+
function appendPurgeSeqs(db, docs) {
|
|
6
|
+
return db.get("_local/purges").then(function(doc) {
|
|
7
|
+
for (const [docId, rev$$1] of docs) {
|
|
8
|
+
const purgeSeq = doc.purgeSeq + 1;
|
|
9
|
+
doc.purges.push({
|
|
10
|
+
docId,
|
|
11
|
+
rev: rev$$1,
|
|
12
|
+
purgeSeq
|
|
13
|
+
});
|
|
14
|
+
if (doc.purges.length > db.purged_infos_limit) {
|
|
15
|
+
doc.purges.splice(0, doc.purges.length - db.purged_infos_limit);
|
|
16
|
+
}
|
|
17
|
+
doc.purgeSeq = purgeSeq;
|
|
18
|
+
}
|
|
19
|
+
return doc;
|
|
20
|
+
}).catch(function(err) {
|
|
21
|
+
if (err.status !== 404) {
|
|
22
|
+
throw err;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
_id: "_local/purges",
|
|
26
|
+
purges: docs.map(([docId, rev$$1], idx) => ({
|
|
27
|
+
docId,
|
|
28
|
+
rev: rev$$1,
|
|
29
|
+
purgeSeq: idx
|
|
30
|
+
})),
|
|
31
|
+
purgeSeq: docs.length
|
|
32
|
+
};
|
|
33
|
+
}).then(function(doc) {
|
|
34
|
+
return db.put(doc);
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
function installPurgeMulti(PouchDB) {
|
|
38
|
+
PouchDB.prototype.purgeMulti = adapterFun(
|
|
39
|
+
"_purgeMulti",
|
|
40
|
+
function(docs, callback) {
|
|
41
|
+
if (typeof this._purge === "undefined") {
|
|
42
|
+
return callback(
|
|
43
|
+
//@ts-ignore: this ts-ignore might be hiding a `this` bug where we don't have "this" conext.
|
|
44
|
+
createError(UNKNOWN_ERROR, "Purge is not implemented in the " + this.adapter + " adapter.")
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
const self = this;
|
|
48
|
+
const tasks = docs.map(
|
|
49
|
+
(param) => () => new Promise((res, rej) => {
|
|
50
|
+
const [docId, rev$$1] = param;
|
|
51
|
+
self._getRevisionTree(docId, (error, revs) => {
|
|
52
|
+
if (error) {
|
|
53
|
+
return res([param, error]);
|
|
54
|
+
}
|
|
55
|
+
if (!revs) {
|
|
56
|
+
return res([param, createError(MISSING_DOC)]);
|
|
57
|
+
}
|
|
58
|
+
let path;
|
|
59
|
+
try {
|
|
60
|
+
path = findPathToLeaf(revs, rev$$1);
|
|
61
|
+
} catch (error2) {
|
|
62
|
+
return res([param, error2.message || error2]);
|
|
63
|
+
}
|
|
64
|
+
self._purge(docId, path, (error2, result) => {
|
|
65
|
+
if (error2) {
|
|
66
|
+
return res([param, error2]);
|
|
67
|
+
} else {
|
|
68
|
+
return res([param, result]);
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
})
|
|
73
|
+
);
|
|
74
|
+
(async () => {
|
|
75
|
+
const ret = await mapAllTasksWithConcurrencyLimit(1, tasks);
|
|
76
|
+
const retAll = ret.map((e) => unwrapTaskResult(e));
|
|
77
|
+
await appendPurgeSeqs(
|
|
78
|
+
self,
|
|
79
|
+
retAll.filter((e) => "ok" in e[1]).map((e) => e[0])
|
|
80
|
+
);
|
|
81
|
+
const result = Object.fromEntries(retAll.map((e) => [e[0][0], e[1]]));
|
|
82
|
+
return result;
|
|
83
|
+
})().then((result) => callback(void 0, result)).catch((error) => callback(error));
|
|
84
|
+
}
|
|
85
|
+
);
|
|
86
|
+
}
|
|
87
|
+
export {
|
|
88
|
+
installPurgeMulti
|
|
89
|
+
};
|
|
@@ -677,7 +677,12 @@ class JournalSyncCore {
|
|
|
677
677
|
const piece = decompressed.slice(idxFrom, idxTo);
|
|
678
678
|
const strPiece = d.decode(piece);
|
|
679
679
|
if (strPiece.startsWith("~")) {
|
|
680
|
-
const
|
|
680
|
+
const separatorIndex = strPiece.indexOf(UNIT_SPLIT);
|
|
681
|
+
if (separatorIndex === -1) {
|
|
682
|
+
throw new Error("Invalid journal chunk record: missing separator");
|
|
683
|
+
}
|
|
684
|
+
const idPart = strPiece.substring(1, separatorIndex);
|
|
685
|
+
const dataPart = strPiece.substring(separatorIndex + 1);
|
|
681
686
|
result.push({
|
|
682
687
|
_id: idPart,
|
|
683
688
|
data: unescapeNewLineFromString(dataPart),
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parsePeerPatterns } from "./peerPatterns.js";
|
|
1
2
|
import {
|
|
2
3
|
TweakValuesShouldMatchedTemplate,
|
|
3
4
|
omitP2PRuntimeSettings
|
|
@@ -171,12 +172,10 @@ class TrysteroReplicator {
|
|
|
171
172
|
}
|
|
172
173
|
}
|
|
173
174
|
get autoSyncPeers() {
|
|
174
|
-
|
|
175
|
-
return peers;
|
|
175
|
+
return parsePeerPatterns(this.currentSettings.P2P_AutoSyncPeers);
|
|
176
176
|
}
|
|
177
177
|
get autoWatchPeers() {
|
|
178
|
-
|
|
179
|
-
return peers;
|
|
178
|
+
return parsePeerPatterns(this.currentSettings.P2P_AutoWatchPeers);
|
|
180
179
|
}
|
|
181
180
|
async onNewPeer(peer) {
|
|
182
181
|
const peerName = peer.name;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { parsePeerPatterns } from "./peerPatterns.js";
|
|
1
2
|
import { selfId, joinRoom } from "@trystero-p2p/nostr";
|
|
2
3
|
import { AutoAccepting, LOG_LEVEL_INFO, LOG_LEVEL_NOTICE } from "../../common/types.js";
|
|
3
4
|
import { LOG_LEVEL_VERBOSE, Logger } from "../../common/logger.js";
|
|
@@ -281,12 +282,12 @@ You can chose as follows:
|
|
|
281
282
|
}
|
|
282
283
|
_acceptablePeers = new Computed({
|
|
283
284
|
evaluation: (settings) => {
|
|
284
|
-
return `${settings?.P2P_AutoAcceptingPeers ?? ""}
|
|
285
|
+
return parsePeerPatterns(`${settings?.P2P_AutoAcceptingPeers ?? ""}`);
|
|
285
286
|
}
|
|
286
287
|
});
|
|
287
288
|
_shouldDenyPeers = new Computed({
|
|
288
289
|
evaluation: (settings) => {
|
|
289
|
-
return `${settings?.P2P_AutoDenyingPeers ?? ""}
|
|
290
|
+
return parsePeerPatterns(`${settings?.P2P_AutoDenyingPeers ?? ""}`);
|
|
290
291
|
}
|
|
291
292
|
});
|
|
292
293
|
/**
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare function parsePeerPatterns(value: string): RegExp[];
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
function parsePeerPatterns(value) {
|
|
2
|
+
return value.split(",").map((entry) => entry.trim()).filter((entry) => entry.length > 0).map((entry) => entry.startsWith("~") ? new RegExp(entry.substring(1), "i") : new RegExp(`^${entry}$`, "i"));
|
|
3
|
+
}
|
|
4
|
+
export {
|
|
5
|
+
parsePeerPatterns
|
|
6
|
+
};
|
package/dist/rpc/RpcRoom.d.ts
CHANGED
|
@@ -8,7 +8,7 @@ export declare class RpcRoom {
|
|
|
8
8
|
private sessions;
|
|
9
9
|
private outgoingChunkMap;
|
|
10
10
|
private incomingChunkMap;
|
|
11
|
-
private
|
|
11
|
+
private incomingChunkWaits;
|
|
12
12
|
private peerVersion;
|
|
13
13
|
private disposers;
|
|
14
14
|
private closed;
|
|
@@ -32,7 +32,11 @@ export declare class RpcRoom {
|
|
|
32
32
|
private notifyRemoteCancellation;
|
|
33
33
|
private abortInboundCall;
|
|
34
34
|
private sendEnvelope;
|
|
35
|
+
private sendChunk;
|
|
36
|
+
private sendErrorResponse;
|
|
35
37
|
private scheduleMissingAck;
|
|
36
38
|
private onWireMessage;
|
|
37
39
|
private onEnvelopePayload;
|
|
40
|
+
private onRequest;
|
|
41
|
+
private runMethod;
|
|
38
42
|
}
|