document-drive 1.0.0-alpha.92 → 1.0.0-alpha.94

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document-drive",
3
- "version": "1.0.0-alpha.92",
3
+ "version": "1.0.0-alpha.94",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "module": "./src/index.ts",
@@ -46,6 +46,7 @@
46
46
  "sqlite3": "^5.1.7"
47
47
  },
48
48
  "dependencies": {
49
+ "change-case": "^5.4.4",
49
50
  "exponential-backoff": "^3.1.1",
50
51
  "graphql": "^16.9.0",
51
52
  "graphql-request": "^6.1.0",
@@ -82,7 +83,8 @@
82
83
  "sqlite3": "^5.1.7",
83
84
  "typescript": "^5.5.3",
84
85
  "vitest": "^2.0.5",
85
- "webdriverio": "^9.0.9"
86
+ "webdriverio": "^9.0.9",
87
+ "vitest-fetch-mock": "^0.3.0"
86
88
  },
87
89
  "packageManager": "pnpm@9.1.4+sha256.30a1801ac4e723779efed13a21f4c39f9eb6c9fbb4ced101bce06b422593d7c9"
88
90
  }
@@ -0,0 +1,19 @@
1
+ export abstract class ReadDriveError extends Error {}
2
+
3
+ export class ReadDriveNotFoundError extends ReadDriveError {
4
+ constructor(driveId: string) {
5
+ super(`Read drive ${driveId} not found.`);
6
+ }
7
+ }
8
+
9
+ export class ReadDriveSlugNotFoundError extends ReadDriveError {
10
+ constructor(slug: string) {
11
+ super(`Read drive with slug ${slug} not found.`);
12
+ }
13
+ }
14
+
15
+ export class ReadDocumentNotFoundError extends ReadDriveError {
16
+ constructor(drive: string, id: string) {
17
+ super(`Document with id ${id} not found on read drive ${drive}.`);
18
+ }
19
+ }
@@ -0,0 +1,128 @@
1
+ import { Document } from 'document-model/document';
2
+ import { DocumentDriveServerConstructor, RemoteDriveOptions } from '../server';
3
+ import { logger } from '../utils/logger';
4
+ import { ReadDriveSlugNotFoundError } from './errors';
5
+ import { ReadModeService } from './service';
6
+ import {
7
+ IReadModeDriveServer,
8
+ IReadModeDriveService,
9
+ ReadDrive,
10
+ ReadDriveOptions,
11
+ ReadDrivesListener,
12
+ ReadModeDriveServerMixin
13
+ } from './types';
14
+
15
+ export * from './errors';
16
+ export * from './types';
17
+
18
+ export function ReadModeServer<TBase extends DocumentDriveServerConstructor>(
19
+ Base: TBase
20
+ ): ReadModeDriveServerMixin {
21
+ return class ReadMode extends Base implements IReadModeDriveServer {
22
+ #readModeStorage: IReadModeDriveService;
23
+ #listeners = new Set<ReadDrivesListener>();
24
+
25
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
26
+ constructor(...args: any[]) {
27
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
28
+ super(...args);
29
+
30
+ this.#readModeStorage = new ReadModeService(
31
+ this.getDocumentModel.bind(this)
32
+ );
33
+
34
+ this.#buildDrives()
35
+ .then(drives => {
36
+ if (drives.length) {
37
+ this.#notifyListeners(drives, 'add');
38
+ }
39
+ })
40
+ .catch(logger.error);
41
+ }
42
+
43
+ async #buildDrives() {
44
+ const driveIds = await this.getReadDrives();
45
+ const drives = (
46
+ await Promise.all(
47
+ driveIds.map(driveId => this.getReadDrive(driveId))
48
+ )
49
+ ).filter(drive => !(drive instanceof Error)) as ReadDrive[];
50
+ return drives;
51
+ }
52
+
53
+ #notifyListeners(drives: ReadDrive[], operation: 'add' | 'delete') {
54
+ this.#listeners.forEach(listener => listener(drives, operation));
55
+ }
56
+
57
+ getReadDrives(): Promise<string[]> {
58
+ return this.#readModeStorage.getReadDrives();
59
+ }
60
+
61
+ getReadDrive(id: string) {
62
+ return this.#readModeStorage.getReadDrive(id);
63
+ }
64
+
65
+ getReadDriveBySlug(
66
+ slug: string
67
+ ): Promise<ReadDrive | ReadDriveSlugNotFoundError> {
68
+ return this.#readModeStorage.getReadDriveBySlug(slug);
69
+ }
70
+
71
+ getReadDriveContext(id: string) {
72
+ return this.#readModeStorage.getReadDriveContext(id);
73
+ }
74
+
75
+ async addReadDrive(url: string, options?: ReadDriveOptions) {
76
+ await this.#readModeStorage.addReadDrive(url, options);
77
+ this.#notifyListeners(await this.#buildDrives(), 'add');
78
+ }
79
+
80
+ fetchDrive(id: string) {
81
+ return this.#readModeStorage.fetchDrive(id);
82
+ }
83
+
84
+ fetchDocument<D extends Document>(
85
+ driveId: string,
86
+ documentId: string,
87
+ documentType: string
88
+ ) {
89
+ return this.#readModeStorage.fetchDocument<D>(
90
+ driveId,
91
+ documentId,
92
+ documentType
93
+ );
94
+ }
95
+
96
+ async deleteReadDrive(id: string) {
97
+ const error = await this.#readModeStorage.deleteReadDrive(id);
98
+ if (error) {
99
+ return error;
100
+ }
101
+
102
+ this.#notifyListeners(await this.#buildDrives(), 'delete');
103
+ }
104
+
105
+ async migrateReadDrive(id: string, options: RemoteDriveOptions) {
106
+ const result = await this.getReadDriveContext(id);
107
+ if (result instanceof Error) {
108
+ return result;
109
+ }
110
+
111
+ const { url, ...readOptions } = result;
112
+ try {
113
+ const newDrive = await this.addRemoteDrive(url, options);
114
+ return newDrive;
115
+ } catch (error) {
116
+ // if an error is thrown, then add the read drive again
117
+ logger.error(error);
118
+ await this.addReadDrive(result.url, readOptions);
119
+ throw error;
120
+ }
121
+ }
122
+
123
+ onReadDrivesUpdate(listener: ReadDrivesListener) {
124
+ this.#listeners.add(listener);
125
+ return Promise.resolve(() => this.#listeners.delete(listener));
126
+ }
127
+ };
128
+ }
@@ -0,0 +1,206 @@
1
+ import type { DocumentDriveDocument } from 'document-model-libs/document-drive';
2
+ import * as DocumentDrive from 'document-model-libs/document-drive';
3
+ import { Document, DocumentModel } from 'document-model/document';
4
+ import { GraphQLError } from 'graphql';
5
+ import { DocumentModelNotFoundError } from '../server/error';
6
+ import { fetchDocument, requestPublicDrive } from '../utils/graphql';
7
+ import {
8
+ ReadDocumentNotFoundError,
9
+ ReadDriveError,
10
+ ReadDriveNotFoundError,
11
+ ReadDriveSlugNotFoundError
12
+ } from './errors';
13
+ import {
14
+ GetDocumentModel,
15
+ InferDocumentLocalState,
16
+ InferDocumentOperation,
17
+ InferDocumentState,
18
+ IReadModeDriveService,
19
+ ReadDrive,
20
+ ReadDriveContext,
21
+ ReadDriveOptions
22
+ } from './types';
23
+
24
+ export class ReadModeService implements IReadModeDriveService {
25
+ #getDocumentModel: GetDocumentModel;
26
+ #drives = new Map<
27
+ string,
28
+ { drive: Omit<ReadDrive, 'readContext'>; context: ReadDriveContext }
29
+ >();
30
+
31
+ constructor(getDocumentModel: GetDocumentModel) {
32
+ this.#getDocumentModel = getDocumentModel;
33
+ }
34
+
35
+ #parseGraphQLErrors(
36
+ errors: GraphQLError[],
37
+ driveId: string,
38
+ documentId?: string
39
+ ) {
40
+ for (const error of errors) {
41
+ if (error.message === `Drive with id ${driveId} not found`) {
42
+ return new ReadDriveNotFoundError(driveId);
43
+ } else if (
44
+ documentId &&
45
+ error.message === `Document with id ${documentId} not found`
46
+ ) {
47
+ return new ReadDocumentNotFoundError(driveId, documentId);
48
+ }
49
+ }
50
+ const firstError = errors.at(0);
51
+ if (firstError) {
52
+ return firstError;
53
+ }
54
+ }
55
+
56
+ async #fetchDrive(id: string, url: string) {
57
+ const { errors, document } = await fetchDocument<DocumentDriveDocument>(
58
+ url,
59
+ id,
60
+ DocumentDrive
61
+ );
62
+ const error = errors ? this.#parseGraphQLErrors(errors, id) : undefined;
63
+ return error || document;
64
+ }
65
+
66
+ async fetchDrive(id: string): Promise<ReadDrive | ReadDriveNotFoundError> {
67
+ const drive = this.#drives.get(id);
68
+ if (!drive) {
69
+ return new ReadDriveNotFoundError(id);
70
+ }
71
+ const document = await this.fetchDocument<DocumentDriveDocument>(
72
+ id,
73
+ id,
74
+ DocumentDrive.documentModel.id
75
+ );
76
+ if (document instanceof Error) {
77
+ return document;
78
+ }
79
+ const result = { ...document, readContext: drive.context };
80
+ drive.drive = result;
81
+ return result;
82
+ }
83
+
84
+ async fetchDocument<D extends Document>(
85
+ driveId: string,
86
+ documentId: string,
87
+ documentType: DocumentModel<
88
+ InferDocumentState<D>,
89
+ InferDocumentOperation<D>,
90
+ InferDocumentLocalState<D>
91
+ >['documentModel']['id']
92
+ ): Promise<
93
+ | Document<
94
+ InferDocumentState<D>,
95
+ InferDocumentOperation<D>,
96
+ InferDocumentLocalState<D>
97
+ >
98
+ | DocumentModelNotFoundError
99
+ | ReadDriveNotFoundError
100
+ | ReadDocumentNotFoundError
101
+ > {
102
+ const drive = this.#drives.get(driveId);
103
+ if (!drive) {
104
+ return new ReadDriveNotFoundError(driveId);
105
+ }
106
+
107
+ let documentModel:
108
+ | DocumentModel<
109
+ InferDocumentState<D>,
110
+ InferDocumentOperation<D>,
111
+ InferDocumentLocalState<D>
112
+ >
113
+ | undefined = undefined;
114
+ try {
115
+ documentModel = this.#getDocumentModel(
116
+ documentType
117
+ ) as unknown as DocumentModel<
118
+ InferDocumentState<D>,
119
+ InferDocumentOperation<D>,
120
+ InferDocumentLocalState<D>
121
+ >;
122
+ } catch (error) {
123
+ return new DocumentModelNotFoundError(documentType, error);
124
+ }
125
+
126
+ const { url } = drive.context;
127
+ const { errors, document } = await fetchDocument<D>(
128
+ url,
129
+ documentId,
130
+ documentModel
131
+ );
132
+
133
+ if (errors) {
134
+ const error = this.#parseGraphQLErrors(errors, driveId, documentId);
135
+ if (error instanceof ReadDriveError) {
136
+ return error;
137
+ } else if (error) {
138
+ throw error;
139
+ }
140
+ }
141
+
142
+ if (!document) {
143
+ return new ReadDocumentNotFoundError(driveId, documentId);
144
+ }
145
+
146
+ return document;
147
+ }
148
+
149
+ async addReadDrive(url: string, options?: ReadDriveOptions): Promise<void> {
150
+ const { id } =
151
+ options?.expectedDriveInfo ?? (await requestPublicDrive(url));
152
+
153
+ const result = await this.#fetchDrive(id, url);
154
+ if (result instanceof Error) {
155
+ throw result;
156
+ } else if (!result) {
157
+ throw new Error(`Drive "${id}" not found at ${url}`);
158
+ }
159
+ this.#drives.set(id, {
160
+ drive: result as unknown as ReadDrive,
161
+ context: {
162
+ ...options,
163
+ url
164
+ }
165
+ });
166
+ }
167
+
168
+ async getReadDrives(): Promise<string[]> {
169
+ return Promise.resolve([...this.#drives.keys()]);
170
+ }
171
+
172
+ async getReadDrive(id: string) {
173
+ const result = this.#drives.get(id);
174
+ return Promise.resolve(
175
+ result
176
+ ? { ...result.drive, readContext: result.context }
177
+ : new ReadDriveNotFoundError(id)
178
+ );
179
+ }
180
+
181
+ async getReadDriveBySlug(
182
+ slug: string
183
+ ): Promise<ReadDrive | ReadDriveSlugNotFoundError> {
184
+ const readDrive = [...this.#drives.values()].find(
185
+ ({ drive }) => drive.state.global.slug === slug
186
+ );
187
+ return Promise.resolve(
188
+ readDrive
189
+ ? { ...readDrive.drive, readContext: readDrive.context }
190
+ : new ReadDriveSlugNotFoundError(slug)
191
+ );
192
+ }
193
+
194
+ getReadDriveContext(id: string) {
195
+ return Promise.resolve(
196
+ this.#drives.get(id)?.context ?? new ReadDriveNotFoundError(id)
197
+ );
198
+ }
199
+
200
+ deleteReadDrive(id: string): Promise<ReadDriveNotFoundError | undefined> {
201
+ const deleted = this.#drives.delete(id);
202
+ return Promise.resolve(
203
+ deleted ? undefined : new ReadDriveNotFoundError(id)
204
+ );
205
+ }
206
+ }
@@ -0,0 +1,108 @@
1
+ import {
2
+ DocumentDriveDocument,
3
+ ListenerFilter
4
+ } from 'document-model-libs/document-drive';
5
+ import { Action, Document, DocumentModel } from 'document-model/document';
6
+ import { DocumentDriveServerMixin, RemoteDriveOptions } from '../server';
7
+ import { DocumentModelNotFoundError } from '../server/error';
8
+ import { DriveInfo } from '../utils/graphql';
9
+ import {
10
+ ReadDocumentNotFoundError,
11
+ ReadDriveNotFoundError,
12
+ ReadDriveSlugNotFoundError
13
+ } from './errors';
14
+
15
+ // TODO: move these types to the document-model package
16
+ export type InferDocumentState<D extends Document> =
17
+ D extends Document<infer S> ? S : never;
18
+
19
+ export type InferDocumentOperation<D extends Document> =
20
+ D extends Document<unknown, infer A> ? A : never;
21
+
22
+ export type InferDocumentLocalState<D extends Document> =
23
+ D extends Document<unknown, Action, infer L> ? L : never;
24
+
25
+ export type InferDocumentGenerics<D extends Document> = {
26
+ state: InferDocumentState<D>;
27
+ action: InferDocumentOperation<D>;
28
+ logger: InferDocumentLocalState<D>;
29
+ };
30
+
31
+ export type ReadModeDriveServerMixin =
32
+ DocumentDriveServerMixin<IReadModeDriveServer>;
33
+
34
+ export type ReadDrivesListener = (
35
+ drives: ReadDrive[],
36
+ operation: 'add' | 'delete'
37
+ ) => void;
38
+
39
+ export type ReadDrivesListenerUnsubscribe = () => void;
40
+
41
+ export interface IReadModeDriveServer extends IReadModeDriveService {
42
+ migrateReadDrive(
43
+ id: string,
44
+ options: RemoteDriveOptions
45
+ ): Promise<DocumentDriveDocument | ReadDriveNotFoundError>;
46
+ onReadDrivesUpdate(
47
+ listener: ReadDrivesListener
48
+ ): Promise<ReadDrivesListenerUnsubscribe>; // TODO: make DriveEvents extensible and reuse event emitter
49
+ }
50
+
51
+ export type ReadDriveOptions = {
52
+ expectedDriveInfo?: DriveInfo;
53
+ filter?: ListenerFilter;
54
+ };
55
+
56
+ export type ReadDriveContext = {
57
+ url: string;
58
+ } & ReadDriveOptions;
59
+
60
+ export type ReadDrive = DocumentDriveDocument & {
61
+ readContext: ReadDriveContext;
62
+ };
63
+
64
+ export type IsDocument<D extends Document> =
65
+ (<G>() => G extends D ? 1 : 2) extends <G>() => G extends Document ? 1 : 2
66
+ ? true
67
+ : false;
68
+
69
+ export interface IReadModeDriveService {
70
+ addReadDrive(url: string, options?: ReadDriveOptions): Promise<void>;
71
+
72
+ getReadDrives(): Promise<string[]>;
73
+
74
+ getReadDriveBySlug(
75
+ slug: string
76
+ ): Promise<ReadDrive | ReadDriveSlugNotFoundError>;
77
+
78
+ getReadDrive(id: string): Promise<ReadDrive | ReadDriveNotFoundError>;
79
+
80
+ getReadDriveContext(
81
+ id: string
82
+ ): Promise<ReadDriveContext | ReadDriveNotFoundError>;
83
+
84
+ fetchDrive(id: string): Promise<ReadDrive | ReadDriveNotFoundError>;
85
+
86
+ fetchDocument<D extends Document>(
87
+ driveId: string,
88
+ documentId: string,
89
+ documentType: DocumentModel<
90
+ InferDocumentState<D>,
91
+ InferDocumentOperation<D>,
92
+ InferDocumentLocalState<D>
93
+ >['documentModel']['id']
94
+ ): Promise<
95
+ | Document<
96
+ InferDocumentState<D>,
97
+ InferDocumentOperation<D>,
98
+ InferDocumentLocalState<D>
99
+ >
100
+ | DocumentModelNotFoundError
101
+ | ReadDriveNotFoundError
102
+ | ReadDocumentNotFoundError
103
+ >;
104
+
105
+ deleteReadDrive(id: string): Promise<ReadDriveNotFoundError | undefined>;
106
+ }
107
+
108
+ export type GetDocumentModel = (documentType: string) => DocumentModel;
@@ -1,6 +1,14 @@
1
1
  import type { Operation } from 'document-model/document';
2
2
  import type { ErrorStatus } from './types';
3
3
 
4
+ export class DocumentModelNotFoundError extends Error {
5
+ constructor(
6
+ public id: string,
7
+ cause?: unknown
8
+ ) {
9
+ super(`Document model "${id}" not found`, { cause });
10
+ }
11
+ }
4
12
  export class OperationError extends Error {
5
13
  status: ErrorStatus;
6
14
  operation: Operation | undefined;
@@ -35,6 +35,7 @@ import {
35
35
  Job,
36
36
  OperationJob
37
37
  } from '../queue/types';
38
+ import { ReadModeServer } from '../read-mode';
38
39
  import { MemoryStorage } from '../storage/memory';
39
40
  import type {
40
41
  DocumentDriveStorage,
@@ -77,15 +78,17 @@ import {
77
78
  StrandUpdateSource
78
79
  } from './listener/transmitter';
79
80
  import {
81
+ AbstractDocumentDriveServer,
80
82
  AddOperationOptions,
81
- BaseDocumentDriveServer,
82
83
  DefaultListenerManagerOptions,
83
84
  DocumentDriveServerOptions,
84
85
  DriveEvents,
85
86
  GetDocumentOptions,
86
87
  GetStrandsOptions,
88
+ IBaseDocumentDriveServer,
87
89
  IOperationResult,
88
90
  ListenerState,
91
+ RemoteDriveAccessLevel,
89
92
  RemoteDriveOptions,
90
93
  StrandUpdate,
91
94
  SynchronizationUnitQuery,
@@ -102,8 +105,14 @@ import { filterOperationsByRevision } from './utils';
102
105
  export * from './listener';
103
106
  export type * from './types';
104
107
 
108
+ export * from '../read-mode';
109
+
105
110
  export const PULL_DRIVE_INTERVAL = 5000;
106
- export class DocumentDriveServer extends BaseDocumentDriveServer {
111
+
112
+ export class BaseDocumentDriveServer
113
+ extends AbstractDocumentDriveServer
114
+ implements IBaseDocumentDriveServer
115
+ {
107
116
  private emitter = createNanoEvents<DriveEvents>();
108
117
  private cache: ICache;
109
118
  private documentModels: DocumentModel[];
@@ -131,11 +140,10 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
131
140
  ) {
132
141
  super();
133
142
  this.options = {
134
- defaultRemoteDrives: [],
135
- removeOldRemoteDrives: {
136
- strategy: 'preserve-all'
137
- },
138
143
  ...options,
144
+ defaultDrives: {
145
+ ...options?.defaultDrives
146
+ },
139
147
  listenerManager: {
140
148
  ...DefaultListenerManagerOptions,
141
149
  ...options?.listenerManager
@@ -180,6 +188,13 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
180
188
  return this.defaultDrivesManager.getDefaultRemoteDrives();
181
189
  }
182
190
 
191
+ setDefaultDriveAccessLevel(url: string, level: RemoteDriveAccessLevel) {
192
+ return this.defaultDrivesManager.setDefaultDriveAccessLevel(url, level);
193
+ }
194
+ setAllDefaultDrivesAccessLevel(level: RemoteDriveAccessLevel) {
195
+ return this.defaultDrivesManager.setAllDefaultDrivesAccessLevel(level);
196
+ }
197
+
183
198
  private getOperationSource(source: StrandUpdateSource) {
184
199
  return source.type === 'local' ? 'push' : 'pull';
185
200
  }
@@ -899,7 +914,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
899
914
  }));
900
915
  }
901
916
 
902
- private _getDocumentModel(documentType: string) {
917
+ protected getDocumentModel(documentType: string) {
903
918
  const documentModel = this.documentModels.find(
904
919
  model => model.documentModel.id === documentType
905
920
  );
@@ -1093,7 +1108,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
1093
1108
  // if no document was provided then create a new one
1094
1109
  const document =
1095
1110
  input.document ??
1096
- this._getDocumentModel(input.documentType).utils.createDocument();
1111
+ this.getDocumentModel(input.documentType).utils.createDocument();
1097
1112
 
1098
1113
  // stores document information
1099
1114
  const documentStorage: DocumentStorage = {
@@ -1331,7 +1346,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
1331
1346
  return documentStorage as T;
1332
1347
  }
1333
1348
 
1334
- const documentModel = this._getDocumentModel(
1349
+ const documentModel = this.getDocumentModel(
1335
1350
  documentStorage.documentType
1336
1351
  );
1337
1352
 
@@ -1369,7 +1384,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
1369
1384
  operation: Operation,
1370
1385
  skipHashValidation = false
1371
1386
  ) {
1372
- const documentModel = this._getDocumentModel(document.documentType);
1387
+ const documentModel = this.getDocumentModel(document.documentType);
1373
1388
 
1374
1389
  const signalResults: SignalResult[] = [];
1375
1390
  let newDocument = document;
@@ -2205,7 +2220,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
2205
2220
  actions: (T | BaseAction)[]
2206
2221
  ): Operation<T | BaseAction>[] {
2207
2222
  const operations: Operation<T | BaseAction>[] = [];
2208
- const { reducer } = this._getDocumentModel(document.documentType);
2223
+ const { reducer } = this.getDocumentModel(document.documentType);
2209
2224
  for (const action of actions) {
2210
2225
  document = reducer(document, action);
2211
2226
  const operation = document.operations[action.scope].slice().pop();
@@ -2369,3 +2384,5 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
2369
2384
  return this.emitter.emit(event, ...args);
2370
2385
  }
2371
2386
  }
2387
+
2388
+ export const DocumentDriveServer = ReadModeServer(BaseDocumentDriveServer);
@@ -1,7 +1,7 @@
1
1
  import { Document, OperationScope } from 'document-model/document';
2
2
  import { logger } from '../../../utils/logger';
3
3
  import {
4
- BaseDocumentDriveServer,
4
+ IBaseDocumentDriveServer,
5
5
  Listener,
6
6
  ListenerRevision,
7
7
  OperationUpdate,
@@ -28,11 +28,11 @@ export type InternalTransmitterUpdate<
28
28
  };
29
29
 
30
30
  export class InternalTransmitter implements ITransmitter {
31
- private drive: BaseDocumentDriveServer;
31
+ private drive: IBaseDocumentDriveServer;
32
32
  private listener: Listener;
33
33
  private receiver: IReceiver | undefined;
34
34
 
35
- constructor(listener: Listener, drive: BaseDocumentDriveServer) {
35
+ constructor(listener: Listener, drive: IBaseDocumentDriveServer) {
36
36
  this.listener = listener;
37
37
  this.drive = drive;
38
38
  }
@@ -6,8 +6,8 @@ import { gql, requestGraphql } from '../../../utils/graphql';
6
6
  import { logger as defaultLogger } from '../../../utils/logger';
7
7
  import { OperationError } from '../../error';
8
8
  import {
9
- BaseDocumentDriveServer,
10
9
  GetStrandsOptions,
10
+ IBaseDocumentDriveServer,
11
11
  IOperationResult,
12
12
  Listener,
13
13
  ListenerRevision,
@@ -46,13 +46,13 @@ export interface IPullResponderTransmitter extends ITransmitter {
46
46
  }
47
47
 
48
48
  export class PullResponderTransmitter implements IPullResponderTransmitter {
49
- private drive: BaseDocumentDriveServer;
49
+ private drive: IBaseDocumentDriveServer;
50
50
  private listener: Listener;
51
51
  private manager: ListenerManager;
52
52
 
53
53
  constructor(
54
54
  listener: Listener,
55
- drive: BaseDocumentDriveServer,
55
+ drive: IBaseDocumentDriveServer,
56
56
  manager: ListenerManager
57
57
  ) {
58
58
  this.listener = listener;
@@ -68,6 +68,11 @@ export class PullResponderTransmitter implements IPullResponderTransmitter {
68
68
  );
69
69
  }
70
70
 
71
+ disconnect(): Promise<void> {
72
+ // TODO remove listener from switchboard
73
+ return Promise.resolve();
74
+ }
75
+
71
76
  async processAcknowledge(
72
77
  driveId: string,
73
78
  listenerId: string,