document-drive 1.19.1 → 1.20.1
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 +4 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/src/cache/memory.d.ts +10 -0
- package/dist/src/cache/memory.d.ts.map +1 -0
- package/dist/src/cache/memory.js +26 -0
- package/dist/src/cache/redis.d.ts +14 -0
- package/dist/src/cache/redis.d.ts.map +1 -0
- package/dist/src/cache/redis.js +40 -0
- package/dist/src/cache/types.d.ts +7 -0
- package/dist/src/cache/types.d.ts.map +1 -0
- package/dist/src/cache/types.js +1 -0
- package/dist/src/drive-document-model/constants.d.ts +2 -0
- package/dist/src/drive-document-model/constants.d.ts.map +1 -0
- package/dist/src/drive-document-model/constants.js +1 -0
- package/dist/src/drive-document-model/gen/actions.d.ts +7 -0
- package/dist/src/drive-document-model/gen/actions.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/actions.js +2 -0
- package/dist/src/drive-document-model/gen/constants.d.ts +7 -0
- package/dist/src/drive-document-model/gen/constants.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/constants.js +16 -0
- package/dist/src/drive-document-model/gen/creators.d.ts +3 -0
- package/dist/src/drive-document-model/gen/creators.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/creators.js +2 -0
- package/dist/src/drive-document-model/gen/document-model.d.ts +3 -0
- package/dist/src/drive-document-model/gen/document-model.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/document-model.js +210 -0
- package/dist/src/drive-document-model/gen/drive/actions.d.ts +12 -0
- package/dist/src/drive-document-model/gen/drive/actions.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/drive/actions.js +1 -0
- package/dist/src/drive-document-model/gen/drive/creators.d.ts +11 -0
- package/dist/src/drive-document-model/gen/drive/creators.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/drive/creators.js +10 -0
- package/dist/src/drive-document-model/gen/drive/error.d.ts +2 -0
- package/dist/src/drive-document-model/gen/drive/error.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/drive/error.js +1 -0
- package/dist/src/drive-document-model/gen/drive/object.d.ts +14 -0
- package/dist/src/drive-document-model/gen/drive/object.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/drive/object.js +28 -0
- package/dist/src/drive-document-model/gen/drive/operations.d.ts +14 -0
- package/dist/src/drive-document-model/gen/drive/operations.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/drive/operations.js +1 -0
- package/dist/src/drive-document-model/gen/node/actions.d.ts +11 -0
- package/dist/src/drive-document-model/gen/node/actions.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/node/actions.js +1 -0
- package/dist/src/drive-document-model/gen/node/creators.d.ts +10 -0
- package/dist/src/drive-document-model/gen/node/creators.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/node/creators.js +9 -0
- package/dist/src/drive-document-model/gen/node/error.d.ts +2 -0
- package/dist/src/drive-document-model/gen/node/error.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/node/error.js +1 -0
- package/dist/src/drive-document-model/gen/node/object.d.ts +13 -0
- package/dist/src/drive-document-model/gen/node/object.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/node/object.js +25 -0
- package/dist/src/drive-document-model/gen/node/operations.d.ts +13 -0
- package/dist/src/drive-document-model/gen/node/operations.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/node/operations.js +1 -0
- package/dist/src/drive-document-model/gen/object.d.ts +21 -0
- package/dist/src/drive-document-model/gen/object.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/object.js +28 -0
- package/dist/src/drive-document-model/gen/reducer.d.ts +4 -0
- package/dist/src/drive-document-model/gen/reducer.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/reducer.js +74 -0
- package/dist/src/drive-document-model/gen/schema/types.d.ts +176 -0
- package/dist/src/drive-document-model/gen/schema/types.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/schema/types.js +1 -0
- package/dist/src/drive-document-model/gen/schema/zod.d.ts +87 -0
- package/dist/src/drive-document-model/gen/schema/zod.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/schema/zod.js +203 -0
- package/dist/src/drive-document-model/gen/types.d.ts +9 -0
- package/dist/src/drive-document-model/gen/types.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/types.js +1 -0
- package/dist/src/drive-document-model/gen/utils.d.ts +10 -0
- package/dist/src/drive-document-model/gen/utils.d.ts.map +1 -0
- package/dist/src/drive-document-model/gen/utils.js +27 -0
- package/dist/src/drive-document-model/index.d.ts +2 -0
- package/dist/src/drive-document-model/index.d.ts.map +1 -0
- package/dist/src/drive-document-model/index.js +1 -0
- package/dist/src/drive-document-model/module.d.ts +3 -0
- package/dist/src/drive-document-model/module.d.ts.map +1 -0
- package/dist/src/drive-document-model/module.js +12 -0
- package/dist/src/drive-document-model/src/reducers/drive.d.ts +8 -0
- package/dist/src/drive-document-model/src/reducers/drive.d.ts.map +1 -0
- package/dist/src/drive-document-model/src/reducers/drive.js +37 -0
- package/dist/src/drive-document-model/src/reducers/node.d.ts +8 -0
- package/dist/src/drive-document-model/src/reducers/node.d.ts.map +1 -0
- package/dist/src/drive-document-model/src/reducers/node.js +185 -0
- package/dist/src/drive-document-model/src/utils.d.ts +34 -0
- package/dist/src/drive-document-model/src/utils.d.ts.map +1 -0
- package/dist/src/drive-document-model/src/utils.js +146 -0
- package/dist/src/queue/base.d.ts +43 -0
- package/dist/src/queue/base.d.ts.map +1 -0
- package/dist/src/queue/base.js +241 -0
- package/dist/src/queue/redis.d.ts +28 -0
- package/dist/src/queue/redis.d.ts.map +1 -0
- package/dist/src/queue/redis.js +110 -0
- package/dist/src/queue/types.d.ts +55 -0
- package/dist/src/queue/types.d.ts.map +1 -0
- package/dist/src/queue/types.js +6 -0
- package/dist/src/read-mode/errors.d.ts +12 -0
- package/dist/src/read-mode/errors.d.ts.map +1 -0
- package/dist/src/read-mode/errors.js +17 -0
- package/dist/src/read-mode/server.d.ts +4 -0
- package/dist/src/read-mode/server.d.ts.map +1 -0
- package/dist/src/read-mode/server.js +78 -0
- package/dist/src/read-mode/service.d.ts +18 -0
- package/dist/src/read-mode/service.d.ts.map +1 -0
- package/dist/src/read-mode/service.js +112 -0
- package/dist/src/read-mode/types.d.ts +35 -0
- package/dist/src/read-mode/types.d.ts.map +1 -0
- package/dist/src/read-mode/types.js +1 -0
- package/dist/src/server/base-server.d.ts +112 -0
- package/dist/src/server/base-server.d.ts.map +1 -0
- package/dist/src/server/base-server.js +1280 -0
- package/dist/src/server/builder.d.ts +30 -0
- package/dist/src/server/builder.d.ts.map +1 -0
- package/dist/src/server/builder.js +89 -0
- package/dist/src/server/constants.d.ts +2 -0
- package/dist/src/server/constants.d.ts.map +1 -0
- package/dist/src/server/constants.js +1 -0
- package/dist/src/server/error.d.ts +30 -0
- package/dist/src/server/error.d.ts.map +1 -0
- package/dist/src/server/error.js +47 -0
- package/dist/src/server/event-emitter.d.ts +8 -0
- package/dist/src/server/event-emitter.d.ts.map +1 -0
- package/dist/src/server/event-emitter.js +10 -0
- package/dist/src/server/listener/index.d.ts +2 -0
- package/dist/src/server/listener/index.d.ts.map +1 -0
- package/dist/src/server/listener/index.js +1 -0
- package/dist/src/server/listener/listener-manager.d.ts +27 -0
- package/dist/src/server/listener/listener-manager.d.ts.map +1 -0
- package/dist/src/server/listener/listener-manager.js +401 -0
- package/dist/src/server/listener/transmitter/factory.d.ts +8 -0
- package/dist/src/server/listener/transmitter/factory.d.ts.map +1 -0
- package/dist/src/server/listener/transmitter/factory.js +25 -0
- package/dist/src/server/listener/transmitter/internal.d.ts +34 -0
- package/dist/src/server/listener/transmitter/internal.d.ts.map +1 -0
- package/dist/src/server/listener/transmitter/internal.js +87 -0
- package/dist/src/server/listener/transmitter/pull-responder.d.ts +38 -0
- package/dist/src/server/listener/transmitter/pull-responder.d.ts.map +1 -0
- package/dist/src/server/listener/transmitter/pull-responder.js +256 -0
- package/dist/src/server/listener/transmitter/switchboard-push.d.ts +9 -0
- package/dist/src/server/listener/transmitter/switchboard-push.d.ts.map +1 -0
- package/dist/src/server/listener/transmitter/switchboard-push.js +77 -0
- package/dist/src/server/listener/transmitter/types.d.ts +20 -0
- package/dist/src/server/listener/transmitter/types.d.ts.map +1 -0
- package/dist/src/server/listener/transmitter/types.js +1 -0
- package/dist/src/server/listener/util.d.ts +2 -0
- package/dist/src/server/listener/util.d.ts.map +1 -0
- package/dist/src/server/listener/util.js +22 -0
- package/dist/src/server/sync-manager.d.ts +30 -0
- package/dist/src/server/sync-manager.d.ts.map +1 -0
- package/dist/src/server/sync-manager.js +287 -0
- package/dist/src/server/types.d.ts +308 -0
- package/dist/src/server/types.d.ts.map +1 -0
- package/dist/src/server/types.js +12 -0
- package/dist/src/server/utils.d.ts +8 -0
- package/dist/src/server/utils.d.ts.map +1 -0
- package/dist/src/server/utils.js +47 -0
- package/dist/src/storage/base.d.ts +36 -0
- package/dist/src/storage/base.d.ts.map +1 -0
- package/dist/src/storage/base.js +4 -0
- package/dist/src/storage/browser.d.ts +36 -0
- package/dist/src/storage/browser.d.ts.map +1 -0
- package/dist/src/storage/browser.js +155 -0
- package/dist/src/storage/filesystem.d.ts +33 -0
- package/dist/src/storage/filesystem.d.ts.map +1 -0
- package/dist/src/storage/filesystem.js +197 -0
- package/dist/src/storage/memory.d.ts +33 -0
- package/dist/src/storage/memory.d.ts.map +1 -0
- package/dist/src/storage/memory.js +139 -0
- package/dist/src/storage/prisma.d.ts +67 -0
- package/dist/src/storage/prisma.d.ts.map +1 -0
- package/dist/src/storage/prisma.js +445 -0
- package/dist/src/storage/sequelize.d.ts +32 -0
- package/dist/src/storage/sequelize.d.ts.map +1 -0
- package/dist/src/storage/sequelize.js +373 -0
- package/dist/src/storage/types.d.ts +43 -0
- package/dist/src/storage/types.d.ts.map +1 -0
- package/dist/src/storage/types.js +1 -0
- package/dist/src/utils/default-drives-manager.d.ts +29 -0
- package/dist/src/utils/default-drives-manager.d.ts.map +1 -0
- package/dist/src/utils/default-drives-manager.js +208 -0
- package/dist/src/utils/graphql.d.ts +34 -0
- package/dist/src/utils/graphql.d.ts.map +1 -0
- package/dist/src/utils/graphql.js +183 -0
- package/dist/src/utils/logger.d.ts +27 -0
- package/dist/src/utils/logger.d.ts.map +1 -0
- package/dist/src/utils/logger.js +105 -0
- package/dist/src/utils/migrations.d.ts +4 -0
- package/dist/src/utils/migrations.d.ts.map +1 -0
- package/dist/src/utils/migrations.js +41 -0
- package/dist/src/utils/misc.d.ts +11 -0
- package/dist/src/utils/misc.d.ts.map +1 -0
- package/dist/src/utils/misc.js +43 -0
- package/dist/src/utils/run-asap.d.ts +12 -0
- package/dist/src/utils/run-asap.d.ts.map +1 -0
- package/dist/src/utils/run-asap.js +131 -0
- package/dist/test/document-helpers/utils.d.ts +8 -0
- package/dist/test/document-helpers/utils.d.ts.map +1 -0
- package/dist/test/document-helpers/utils.js +21 -0
- package/dist/test/utils.d.ts +48 -0
- package/dist/test/utils.d.ts.map +1 -0
- package/dist/test/utils.js +132 -0
- package/dist/test/vitest-setup.d.ts +2 -0
- package/dist/test/vitest-setup.d.ts.map +1 -0
- package/dist/test/vitest-setup.js +4 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/vitest.config.d.ts +3 -0
- package/dist/vitest.config.d.ts.map +1 -0
- package/dist/vitest.config.js +20 -0
- package/package.json +20 -38
- package/src/cache/index.ts +0 -2
- package/src/cache/memory.ts +0 -33
- package/src/cache/redis.ts +0 -56
- package/src/cache/types.ts +0 -9
- package/src/index.ts +0 -4
- package/src/queue/base.ts +0 -320
- package/src/queue/index.ts +0 -2
- package/src/queue/redis.ts +0 -144
- package/src/queue/types.ts +0 -79
- package/src/read-mode/errors.ts +0 -19
- package/src/read-mode/index.ts +0 -125
- package/src/read-mode/service.ts +0 -207
- package/src/read-mode/types.ts +0 -108
- package/src/server/error.ts +0 -70
- package/src/server/index.ts +0 -2444
- package/src/server/listener/index.ts +0 -2
- package/src/server/listener/manager.ts +0 -652
- package/src/server/listener/transmitter/index.ts +0 -4
- package/src/server/listener/transmitter/internal.ts +0 -143
- package/src/server/listener/transmitter/pull-responder.ts +0 -462
- package/src/server/listener/transmitter/switchboard-push.ts +0 -125
- package/src/server/listener/transmitter/types.ts +0 -27
- package/src/server/types.ts +0 -596
- package/src/server/utils.ts +0 -82
- package/src/storage/base.ts +0 -81
- package/src/storage/browser.ts +0 -238
- package/src/storage/filesystem.ts +0 -297
- package/src/storage/index.ts +0 -2
- package/src/storage/memory.ts +0 -211
- package/src/storage/prisma.ts +0 -653
- package/src/storage/sequelize.ts +0 -498
- package/src/storage/types.ts +0 -97
- package/src/utils/default-drives-manager.ts +0 -341
- package/src/utils/document-helpers.ts +0 -21
- package/src/utils/graphql.ts +0 -301
- package/src/utils/index.ts +0 -90
- package/src/utils/logger.ts +0 -38
- package/src/utils/migrations.ts +0 -58
- package/src/utils/run-asap.ts +0 -156
|
@@ -1,652 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DocumentDriveDocument,
|
|
3
|
-
ListenerFilter,
|
|
4
|
-
} from "document-model-libs/document-drive";
|
|
5
|
-
import { OperationScope } from "document-model/document";
|
|
6
|
-
import { logger } from "../../utils/logger";
|
|
7
|
-
import { OperationError } from "../error";
|
|
8
|
-
import {
|
|
9
|
-
DefaultListenerManagerOptions,
|
|
10
|
-
DriveUpdateErrorHandler,
|
|
11
|
-
ErrorStatus,
|
|
12
|
-
GetStrandsOptions,
|
|
13
|
-
IBaseDocumentDriveServer,
|
|
14
|
-
IListenerManager,
|
|
15
|
-
Listener,
|
|
16
|
-
ListenerManagerOptions,
|
|
17
|
-
ListenerState,
|
|
18
|
-
ListenerUpdate,
|
|
19
|
-
OperationUpdate,
|
|
20
|
-
StrandUpdate,
|
|
21
|
-
SynchronizationUnit,
|
|
22
|
-
SynchronizationUnitQuery,
|
|
23
|
-
} from "../types";
|
|
24
|
-
import { StrandUpdateSource } from "./transmitter/types";
|
|
25
|
-
|
|
26
|
-
const ENABLE_SYNC_DEBUG = false;
|
|
27
|
-
|
|
28
|
-
function debounce<T extends unknown[], R>(
|
|
29
|
-
func: (...args: T) => Promise<R>,
|
|
30
|
-
delay = 250,
|
|
31
|
-
) {
|
|
32
|
-
let timer: number;
|
|
33
|
-
return (immediate: boolean, ...args: T) => {
|
|
34
|
-
if (timer) {
|
|
35
|
-
clearTimeout(timer);
|
|
36
|
-
}
|
|
37
|
-
return new Promise<R>((resolve, reject) => {
|
|
38
|
-
if (immediate) {
|
|
39
|
-
func(...args)
|
|
40
|
-
.then(resolve)
|
|
41
|
-
.catch(reject);
|
|
42
|
-
} else {
|
|
43
|
-
timer = setTimeout(() => {
|
|
44
|
-
func(...args)
|
|
45
|
-
.then(resolve)
|
|
46
|
-
.catch(reject);
|
|
47
|
-
}, delay) as unknown as number;
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export class ListenerManager implements IListenerManager {
|
|
54
|
-
static LISTENER_UPDATE_DELAY = 250;
|
|
55
|
-
private debugID = `[LM #${Math.floor(Math.random() * 999)}]`;
|
|
56
|
-
protected driveServer: IBaseDocumentDriveServer;
|
|
57
|
-
protected options: ListenerManagerOptions;
|
|
58
|
-
|
|
59
|
-
// driveId -> listenerId -> listenerState
|
|
60
|
-
protected listenerStateByDriveId = new Map<
|
|
61
|
-
string,
|
|
62
|
-
Map<string, ListenerState>
|
|
63
|
-
>();
|
|
64
|
-
|
|
65
|
-
constructor(
|
|
66
|
-
drive: IBaseDocumentDriveServer,
|
|
67
|
-
listenerState = new Map<string, Map<string, ListenerState>>(),
|
|
68
|
-
options: ListenerManagerOptions = DefaultListenerManagerOptions,
|
|
69
|
-
) {
|
|
70
|
-
this.debugLog(`constructor(...)`);
|
|
71
|
-
this.driveServer = drive;
|
|
72
|
-
this.listenerStateByDriveId = listenerState;
|
|
73
|
-
this.options = { ...DefaultListenerManagerOptions, ...options };
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
private debugLog(...data: any[]) {
|
|
77
|
-
if (!ENABLE_SYNC_DEBUG) {
|
|
78
|
-
return;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (data.length > 0 && typeof data[0] === "string") {
|
|
82
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
83
|
-
console.log(`${this.debugID} ${data[0]}`, ...data.slice(1));
|
|
84
|
-
} else {
|
|
85
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
|
|
86
|
-
console.log(this.debugID, ...data);
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
async initialize(handler: DriveUpdateErrorHandler) {
|
|
91
|
-
this.debugLog("initialize(...)");
|
|
92
|
-
// if network connect comes back online
|
|
93
|
-
// then triggers the listeners update
|
|
94
|
-
if (typeof window !== "undefined") {
|
|
95
|
-
window.addEventListener("online", () => {
|
|
96
|
-
this.triggerUpdate(false, { type: "local" }, handler).catch((error) => {
|
|
97
|
-
logger.error("Non handled error updating listeners", error);
|
|
98
|
-
});
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
driveHasListeners(driveId: string) {
|
|
104
|
-
return this.listenerStateByDriveId.has(driveId);
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
async setListener(driveId: string, listener: Listener) {
|
|
108
|
-
this.debugLog(
|
|
109
|
-
`setListener(drive: ${driveId}, listener: ${listener.listenerId})`,
|
|
110
|
-
);
|
|
111
|
-
|
|
112
|
-
// slight code smell -- drive id may not need to be on listener or not passed in
|
|
113
|
-
if (driveId !== listener.driveId) {
|
|
114
|
-
throw new Error("Drive ID mismatch");
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
let existingState;
|
|
118
|
-
try {
|
|
119
|
-
existingState = this.getListenerState(driveId, listener.listenerId);
|
|
120
|
-
} catch {
|
|
121
|
-
existingState = {};
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
// keep existing state if it exists
|
|
125
|
-
this.setListenerState(driveId, listener.listenerId, {
|
|
126
|
-
...existingState,
|
|
127
|
-
block: listener.block,
|
|
128
|
-
driveId: listener.driveId,
|
|
129
|
-
pendingTimeout: "0",
|
|
130
|
-
listener,
|
|
131
|
-
listenerStatus: "CREATED",
|
|
132
|
-
syncUnits: new Map(),
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
this.triggerUpdate(true, { type: "local" });
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
async removeListener(driveId: string, listenerId: string) {
|
|
139
|
-
this.debugLog("setListener()");
|
|
140
|
-
|
|
141
|
-
const driveMap = this.listenerStateByDriveId.get(driveId);
|
|
142
|
-
if (!driveMap) {
|
|
143
|
-
return false;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
return Promise.resolve(driveMap.delete(listenerId));
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
async removeSyncUnits(
|
|
150
|
-
driveId: string,
|
|
151
|
-
syncUnits: Pick<SynchronizationUnit, "syncId">[],
|
|
152
|
-
): Promise<void> {
|
|
153
|
-
const listeners = this.listenerStateByDriveId.get(driveId);
|
|
154
|
-
if (!listeners) {
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
157
|
-
for (const [, listener] of listeners) {
|
|
158
|
-
for (const syncUnit of syncUnits) {
|
|
159
|
-
listener.syncUnits.delete(syncUnit.syncId);
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
return Promise.resolve();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async updateSynchronizationRevisions(
|
|
166
|
-
driveId: string,
|
|
167
|
-
syncUnits: SynchronizationUnit[],
|
|
168
|
-
source: StrandUpdateSource,
|
|
169
|
-
willUpdate?: (listeners: Listener[]) => void,
|
|
170
|
-
onError?: (error: Error, driveId: string, listener: ListenerState) => void,
|
|
171
|
-
forceSync = false,
|
|
172
|
-
) {
|
|
173
|
-
const listenerIdToListenerState = this.listenerStateByDriveId.get(driveId);
|
|
174
|
-
if (!listenerIdToListenerState) {
|
|
175
|
-
return [];
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
const outdatedListeners: Listener[] = [];
|
|
179
|
-
for (const [, listenerState] of listenerIdToListenerState) {
|
|
180
|
-
if (
|
|
181
|
-
outdatedListeners.find(
|
|
182
|
-
(l) => l.listenerId === listenerState.listener.listenerId,
|
|
183
|
-
)
|
|
184
|
-
) {
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
const transmitter = listenerState.listener.transmitter;
|
|
189
|
-
if (!transmitter?.transmit) {
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
|
|
193
|
-
for (const syncUnit of syncUnits) {
|
|
194
|
-
if (!this._checkFilter(listenerState.listener.filter, syncUnit)) {
|
|
195
|
-
continue;
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
const listenerRev = listenerState.syncUnits.get(syncUnit.syncId);
|
|
199
|
-
|
|
200
|
-
if (!listenerRev || listenerRev.listenerRev < syncUnit.revision) {
|
|
201
|
-
outdatedListeners.push(listenerState.listener);
|
|
202
|
-
break;
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
if (outdatedListeners.length) {
|
|
208
|
-
willUpdate?.(outdatedListeners);
|
|
209
|
-
return this.triggerUpdate(forceSync, source, onError);
|
|
210
|
-
}
|
|
211
|
-
return [];
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
async updateListenerRevision(
|
|
215
|
-
listenerId: string,
|
|
216
|
-
driveId: string,
|
|
217
|
-
syncId: string,
|
|
218
|
-
listenerRev: number,
|
|
219
|
-
): Promise<void> {
|
|
220
|
-
const drive = this.listenerStateByDriveId.get(driveId);
|
|
221
|
-
if (!drive) {
|
|
222
|
-
return;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const listener = drive.get(listenerId);
|
|
226
|
-
if (!listener) {
|
|
227
|
-
return;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
const lastUpdated = new Date().toISOString();
|
|
231
|
-
const entry = listener.syncUnits.get(syncId);
|
|
232
|
-
if (entry) {
|
|
233
|
-
entry.listenerRev = listenerRev;
|
|
234
|
-
entry.lastUpdated = lastUpdated;
|
|
235
|
-
} else {
|
|
236
|
-
listener.syncUnits.set(syncId, { listenerRev, lastUpdated });
|
|
237
|
-
}
|
|
238
|
-
|
|
239
|
-
return Promise.resolve();
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
triggerUpdate = debounce(
|
|
243
|
-
this._triggerUpdate.bind(this),
|
|
244
|
-
ListenerManager.LISTENER_UPDATE_DELAY,
|
|
245
|
-
);
|
|
246
|
-
|
|
247
|
-
private async _triggerUpdate(
|
|
248
|
-
source: StrandUpdateSource,
|
|
249
|
-
onError?: (error: Error, driveId: string, listener: ListenerState) => void,
|
|
250
|
-
maxContinues = 500,
|
|
251
|
-
) {
|
|
252
|
-
this.debugLog(
|
|
253
|
-
`_triggerUpdate(source: ${source.type}, maxContinues: ${maxContinues})`,
|
|
254
|
-
this.listenerStateByDriveId,
|
|
255
|
-
);
|
|
256
|
-
|
|
257
|
-
if (maxContinues < 0) {
|
|
258
|
-
throw new Error("Maximum retries exhausted.");
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const listenerUpdates: ListenerUpdate[] = [];
|
|
262
|
-
|
|
263
|
-
for (const [driveId, drive] of this.listenerStateByDriveId) {
|
|
264
|
-
for (const [listenerId, listenerState] of drive) {
|
|
265
|
-
const transmitter = listenerState.listener.transmitter;
|
|
266
|
-
|
|
267
|
-
if (!transmitter?.transmit) {
|
|
268
|
-
this.debugLog(`Transmitter not set on listener: ${listenerId}`);
|
|
269
|
-
continue;
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const syncUnits = await this.getListenerSyncUnits(driveId, listenerId);
|
|
273
|
-
const strandUpdates: StrandUpdate[] = [];
|
|
274
|
-
|
|
275
|
-
this.debugLog("syncUnits", syncUnits);
|
|
276
|
-
|
|
277
|
-
// TODO change to push one after the other, reusing operation data
|
|
278
|
-
const tasks = syncUnits.map((syncUnit) => async () => {
|
|
279
|
-
const unitState = listenerState.syncUnits.get(syncUnit.syncId);
|
|
280
|
-
|
|
281
|
-
if (unitState && unitState.listenerRev >= syncUnit.revision) {
|
|
282
|
-
this.debugLog(
|
|
283
|
-
`Abandoning push for sync unit ${syncUnit.syncId}: already up-to-date (${unitState.listenerRev} >= ${syncUnit.revision})`,
|
|
284
|
-
);
|
|
285
|
-
return;
|
|
286
|
-
} else {
|
|
287
|
-
this.debugLog(
|
|
288
|
-
`Listener out-of-date for sync unit ${syncUnit.syncId}: ${unitState?.listenerRev} < ${syncUnit.revision}`,
|
|
289
|
-
);
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
const opData: OperationUpdate[] = [];
|
|
293
|
-
try {
|
|
294
|
-
const data = await this.driveServer.getOperationData(
|
|
295
|
-
// TODO - join queries, DEAL WITH INVALID SYNC ID ERROR
|
|
296
|
-
driveId,
|
|
297
|
-
syncUnit.syncId,
|
|
298
|
-
{
|
|
299
|
-
fromRevision: unitState?.listenerRev,
|
|
300
|
-
},
|
|
301
|
-
);
|
|
302
|
-
opData.push(...data);
|
|
303
|
-
} catch (e) {
|
|
304
|
-
logger.error(e);
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
if (!opData.length) {
|
|
308
|
-
this.debugLog(
|
|
309
|
-
`Abandoning push for ${syncUnit.syncId}: no operations found`,
|
|
310
|
-
);
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
strandUpdates.push({
|
|
315
|
-
driveId,
|
|
316
|
-
documentId: syncUnit.documentId,
|
|
317
|
-
branch: syncUnit.branch,
|
|
318
|
-
operations: opData,
|
|
319
|
-
scope: syncUnit.scope as OperationScope,
|
|
320
|
-
});
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
if (this.options.sequentialUpdates) {
|
|
324
|
-
this.debugLog(
|
|
325
|
-
`Collecting ${tasks.length} syncUnit strandUpdates in sequence`,
|
|
326
|
-
);
|
|
327
|
-
for (const task of tasks) {
|
|
328
|
-
await task();
|
|
329
|
-
}
|
|
330
|
-
} else {
|
|
331
|
-
this.debugLog(
|
|
332
|
-
`Collecting ${tasks.length} syncUnit strandUpdates in parallel`,
|
|
333
|
-
);
|
|
334
|
-
await Promise.all(tasks.map((task) => task()));
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (strandUpdates.length == 0) {
|
|
338
|
-
this.debugLog(`No strandUpdates needed for listener ${listenerId}`);
|
|
339
|
-
continue;
|
|
340
|
-
}
|
|
341
|
-
|
|
342
|
-
listenerState.pendingTimeout = new Date(
|
|
343
|
-
new Date().getTime() / 1000 + 300,
|
|
344
|
-
).toISOString();
|
|
345
|
-
|
|
346
|
-
listenerState.listenerStatus = "PENDING";
|
|
347
|
-
|
|
348
|
-
// TODO update listeners in parallel, blocking for listeners with block=true
|
|
349
|
-
try {
|
|
350
|
-
this.debugLog(
|
|
351
|
-
`_triggerUpdate(source: ${source.type}) > transmitter.transmit`,
|
|
352
|
-
);
|
|
353
|
-
|
|
354
|
-
const listenerRevisions = await transmitter.transmit(
|
|
355
|
-
strandUpdates,
|
|
356
|
-
source,
|
|
357
|
-
);
|
|
358
|
-
|
|
359
|
-
this.debugLog(
|
|
360
|
-
`_triggerUpdate(source: ${source.type}) > transmission succeeded`,
|
|
361
|
-
listenerRevisions,
|
|
362
|
-
);
|
|
363
|
-
|
|
364
|
-
listenerState.pendingTimeout = "0";
|
|
365
|
-
listenerState.listenerStatus = "PENDING";
|
|
366
|
-
|
|
367
|
-
const lastUpdated = new Date().toISOString();
|
|
368
|
-
let continuationNeeded = false;
|
|
369
|
-
|
|
370
|
-
for (const revision of listenerRevisions) {
|
|
371
|
-
const syncUnit = syncUnits.find(
|
|
372
|
-
(unit) =>
|
|
373
|
-
revision.documentId === unit.documentId &&
|
|
374
|
-
revision.scope === unit.scope &&
|
|
375
|
-
revision.branch === unit.branch,
|
|
376
|
-
);
|
|
377
|
-
|
|
378
|
-
if (syncUnit) {
|
|
379
|
-
listenerState.syncUnits.set(syncUnit.syncId, {
|
|
380
|
-
lastUpdated,
|
|
381
|
-
listenerRev: revision.revision,
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
// Check for revision status vv
|
|
385
|
-
const su = strandUpdates.find(
|
|
386
|
-
(su) =>
|
|
387
|
-
su.driveId === revision.driveId &&
|
|
388
|
-
su.documentId === revision.documentId &&
|
|
389
|
-
su.scope === revision.scope &&
|
|
390
|
-
su.branch === revision.branch,
|
|
391
|
-
);
|
|
392
|
-
|
|
393
|
-
if (su && su.operations.length > 0) {
|
|
394
|
-
const suIndex = su.operations.at(
|
|
395
|
-
su.operations.length - 1,
|
|
396
|
-
)?.index;
|
|
397
|
-
if (suIndex !== revision.revision) {
|
|
398
|
-
this.debugLog(
|
|
399
|
-
`Revision still out-of-date for ${su.documentId}:${su.scope}:${su.branch} ${suIndex} <> ${revision.revision}`,
|
|
400
|
-
);
|
|
401
|
-
continuationNeeded = true;
|
|
402
|
-
} else {
|
|
403
|
-
this.debugLog(
|
|
404
|
-
`Revision match for ${su.documentId}:${su.scope}:${su.branch} ${suIndex}`,
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
} else {
|
|
408
|
-
this.debugLog(
|
|
409
|
-
`Cannot find strand update for (${revision.documentId}:${revision.scope}:${revision.branch} in drive ${revision.driveId})`,
|
|
410
|
-
);
|
|
411
|
-
}
|
|
412
|
-
// Check for revision status ^^
|
|
413
|
-
} else {
|
|
414
|
-
logger.warn(
|
|
415
|
-
`Received revision for untracked unit for listener ${listenerState.listener.listenerId}`,
|
|
416
|
-
revision,
|
|
417
|
-
);
|
|
418
|
-
}
|
|
419
|
-
}
|
|
420
|
-
|
|
421
|
-
for (const revision of listenerRevisions) {
|
|
422
|
-
const error = revision.status === "ERROR";
|
|
423
|
-
if (revision.error?.includes("Missing operations")) {
|
|
424
|
-
continuationNeeded = true;
|
|
425
|
-
} else if (error) {
|
|
426
|
-
throw new OperationError(
|
|
427
|
-
revision.status as ErrorStatus,
|
|
428
|
-
undefined,
|
|
429
|
-
revision.error,
|
|
430
|
-
revision.error,
|
|
431
|
-
);
|
|
432
|
-
}
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
if (!continuationNeeded) {
|
|
436
|
-
listenerUpdates.push({
|
|
437
|
-
listenerId: listenerState.listener.listenerId,
|
|
438
|
-
listenerRevisions,
|
|
439
|
-
});
|
|
440
|
-
} else {
|
|
441
|
-
const updates = await this._triggerUpdate(
|
|
442
|
-
source,
|
|
443
|
-
onError,
|
|
444
|
-
maxContinues - 1,
|
|
445
|
-
);
|
|
446
|
-
listenerUpdates.push(...updates);
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
listenerState.listenerStatus = "SUCCESS";
|
|
450
|
-
} catch (e) {
|
|
451
|
-
// TODO: Handle error based on listener params (blocking, retry, etc)
|
|
452
|
-
onError?.(e as Error, driveId, listenerState);
|
|
453
|
-
listenerState.listenerStatus =
|
|
454
|
-
e instanceof OperationError ? e.status : "ERROR";
|
|
455
|
-
}
|
|
456
|
-
}
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
this.debugLog(
|
|
460
|
-
`Returning listener updates (maxContinues: ${maxContinues})`,
|
|
461
|
-
listenerUpdates,
|
|
462
|
-
);
|
|
463
|
-
|
|
464
|
-
return listenerUpdates;
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
private _checkFilter(filter: ListenerFilter, syncUnit: SynchronizationUnit) {
|
|
468
|
-
const { branch, documentId, scope, documentType } = syncUnit;
|
|
469
|
-
// TODO: Needs to be optimized
|
|
470
|
-
if (
|
|
471
|
-
(!filter.branch ||
|
|
472
|
-
filter.branch.includes(branch) ||
|
|
473
|
-
filter.branch.includes("*")) &&
|
|
474
|
-
(!filter.documentId ||
|
|
475
|
-
filter.documentId.includes(documentId) ||
|
|
476
|
-
filter.documentId.includes("*")) &&
|
|
477
|
-
(!filter.scope ||
|
|
478
|
-
filter.scope.includes(scope) ||
|
|
479
|
-
filter.scope.includes("*")) &&
|
|
480
|
-
(!filter.documentType ||
|
|
481
|
-
filter.documentType.includes(documentType) ||
|
|
482
|
-
filter.documentType.includes("*"))
|
|
483
|
-
) {
|
|
484
|
-
return true;
|
|
485
|
-
}
|
|
486
|
-
return false;
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
getListenerSyncUnits(
|
|
490
|
-
driveId: string,
|
|
491
|
-
listenerId: string,
|
|
492
|
-
loadedDrive?: DocumentDriveDocument,
|
|
493
|
-
) {
|
|
494
|
-
const listener = this.listenerStateByDriveId.get(driveId)?.get(listenerId);
|
|
495
|
-
if (!listener) {
|
|
496
|
-
return [];
|
|
497
|
-
}
|
|
498
|
-
const filter = listener.listener.filter;
|
|
499
|
-
return this.driveServer.getSynchronizationUnits(
|
|
500
|
-
driveId,
|
|
501
|
-
filter.documentId ?? ["*"],
|
|
502
|
-
filter.scope ?? ["*"],
|
|
503
|
-
filter.branch ?? ["*"],
|
|
504
|
-
filter.documentType ?? ["*"],
|
|
505
|
-
loadedDrive,
|
|
506
|
-
);
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
getListenerSyncUnitIds(
|
|
510
|
-
driveId: string,
|
|
511
|
-
listenerId: string,
|
|
512
|
-
): Promise<SynchronizationUnitQuery[]> {
|
|
513
|
-
const listener = this.listenerStateByDriveId.get(driveId)?.get(listenerId);
|
|
514
|
-
if (!listener) {
|
|
515
|
-
return Promise.resolve([]);
|
|
516
|
-
}
|
|
517
|
-
const filter = listener.listener.filter;
|
|
518
|
-
return this.driveServer.getSynchronizationUnitsIds(
|
|
519
|
-
driveId,
|
|
520
|
-
filter.documentId ?? ["*"],
|
|
521
|
-
filter.scope ?? ["*"],
|
|
522
|
-
filter.branch ?? ["*"],
|
|
523
|
-
filter.documentType ?? ["*"],
|
|
524
|
-
);
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
async removeDrive(driveId: string): Promise<void> {
|
|
528
|
-
const listenerIdToListenerState = this.listenerStateByDriveId.get(driveId);
|
|
529
|
-
if (!listenerIdToListenerState) {
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
|
-
|
|
533
|
-
// delete first
|
|
534
|
-
this.listenerStateByDriveId.delete(driveId);
|
|
535
|
-
|
|
536
|
-
for (const [_, listenerState] of listenerIdToListenerState) {
|
|
537
|
-
// guarantee that all disconnects are called
|
|
538
|
-
try {
|
|
539
|
-
await listenerState.listener.transmitter?.disconnect?.();
|
|
540
|
-
} catch (error) {
|
|
541
|
-
logger.error(error);
|
|
542
|
-
}
|
|
543
|
-
}
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
async getStrands(
|
|
547
|
-
driveId: string,
|
|
548
|
-
listenerId: string,
|
|
549
|
-
options?: GetStrandsOptions,
|
|
550
|
-
): Promise<StrandUpdate[]> {
|
|
551
|
-
// this will throw if listenerState is not found
|
|
552
|
-
const listenerState = this.getListenerState(driveId, listenerId);
|
|
553
|
-
|
|
554
|
-
// fetch operations from drive and prepare strands
|
|
555
|
-
const strands: StrandUpdate[] = [];
|
|
556
|
-
|
|
557
|
-
const drive = await this.driveServer.getDrive(driveId);
|
|
558
|
-
const syncUnits = await this.getListenerSyncUnits(
|
|
559
|
-
driveId,
|
|
560
|
-
listenerId,
|
|
561
|
-
drive,
|
|
562
|
-
);
|
|
563
|
-
|
|
564
|
-
const limit = options?.limit; // maximum number of operations to send across all sync units
|
|
565
|
-
let operationsCount = 0; // total amount of operations that have been retrieved
|
|
566
|
-
|
|
567
|
-
const tasks = syncUnits.map((syncUnit) => async () => {
|
|
568
|
-
if (limit && operationsCount >= limit) {
|
|
569
|
-
// break;
|
|
570
|
-
return;
|
|
571
|
-
}
|
|
572
|
-
if (syncUnit.revision < 0) {
|
|
573
|
-
return;
|
|
574
|
-
}
|
|
575
|
-
const entry = listenerState.syncUnits.get(syncUnit.syncId);
|
|
576
|
-
if (entry && entry.listenerRev >= syncUnit.revision) {
|
|
577
|
-
return;
|
|
578
|
-
}
|
|
579
|
-
|
|
580
|
-
const { documentId, driveId, scope, branch } = syncUnit;
|
|
581
|
-
try {
|
|
582
|
-
const operations = await this.driveServer.getOperationData(
|
|
583
|
-
// DEAL WITH INVALID SYNC ID ERROR
|
|
584
|
-
driveId,
|
|
585
|
-
syncUnit.syncId,
|
|
586
|
-
{
|
|
587
|
-
since: options?.since,
|
|
588
|
-
fromRevision: options?.fromRevision ?? entry?.listenerRev,
|
|
589
|
-
limit: limit ? limit - operationsCount : undefined,
|
|
590
|
-
},
|
|
591
|
-
drive,
|
|
592
|
-
);
|
|
593
|
-
|
|
594
|
-
if (!operations.length) {
|
|
595
|
-
return;
|
|
596
|
-
}
|
|
597
|
-
|
|
598
|
-
operationsCount += operations.length;
|
|
599
|
-
|
|
600
|
-
strands.push({
|
|
601
|
-
driveId,
|
|
602
|
-
documentId,
|
|
603
|
-
scope: scope as OperationScope,
|
|
604
|
-
branch,
|
|
605
|
-
operations,
|
|
606
|
-
});
|
|
607
|
-
} catch (error) {
|
|
608
|
-
logger.error(error);
|
|
609
|
-
return;
|
|
610
|
-
}
|
|
611
|
-
});
|
|
612
|
-
|
|
613
|
-
if (this.options.sequentialUpdates) {
|
|
614
|
-
for (const task of tasks) {
|
|
615
|
-
await task();
|
|
616
|
-
}
|
|
617
|
-
} else {
|
|
618
|
-
await Promise.all(tasks.map((task) => task()));
|
|
619
|
-
}
|
|
620
|
-
|
|
621
|
-
return strands;
|
|
622
|
-
}
|
|
623
|
-
|
|
624
|
-
getListenerState(driveId: string, listenerId: string) {
|
|
625
|
-
let listenerStateByListenerId = this.listenerStateByDriveId.get(driveId);
|
|
626
|
-
if (!listenerStateByListenerId) {
|
|
627
|
-
listenerStateByListenerId = new Map();
|
|
628
|
-
this.listenerStateByDriveId.set(driveId, listenerStateByListenerId);
|
|
629
|
-
}
|
|
630
|
-
|
|
631
|
-
const listenerState = listenerStateByListenerId.get(listenerId);
|
|
632
|
-
if (!listenerState) {
|
|
633
|
-
throw new Error("Listener not found");
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
return listenerState;
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
setListenerState(
|
|
640
|
-
driveId: string,
|
|
641
|
-
listenerId: string,
|
|
642
|
-
listenerState: ListenerState,
|
|
643
|
-
) {
|
|
644
|
-
let listenerStateByListenerId = this.listenerStateByDriveId.get(driveId);
|
|
645
|
-
if (!listenerStateByListenerId) {
|
|
646
|
-
listenerStateByListenerId = new Map();
|
|
647
|
-
this.listenerStateByDriveId.set(driveId, listenerStateByListenerId);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
|
-
listenerStateByListenerId.set(listenerId, listenerState);
|
|
651
|
-
}
|
|
652
|
-
}
|