document-drive 1.8.4 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "document-drive",
3
- "version": "1.8.4",
3
+ "version": "1.9.0",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "module": "./src/index.ts",
@@ -33,9 +33,9 @@
33
33
  "nanoevents": "^9.0.0",
34
34
  "sanitize-filename": "^1.6.3",
35
35
  "uuid": "^9.0.1",
36
- "document-model": "2.10.0",
37
- "@powerhousedao/scalars": "1.12.0",
38
- "document-model-libs": "1.120.3"
36
+ "@powerhousedao/scalars": "1.13.0",
37
+ "document-model": "2.11.0",
38
+ "document-model-libs": "1.121.0"
39
39
  },
40
40
  "optionalDependencies": {
41
41
  "@prisma/client": "^5.18.0",
@@ -44,7 +44,7 @@
44
44
  "redis": "^4.6.15",
45
45
  "sequelize": "^6.37.3",
46
46
  "sqlite3": "^5.1.7",
47
- "@powerhousedao/scalars": "1.12.0"
47
+ "@powerhousedao/scalars": "1.13.0"
48
48
  },
49
49
  "devDependencies": {
50
50
  "@prisma/client": "5.17.0",
@@ -66,8 +66,8 @@
66
66
  "nanoevents": "^9.0.0",
67
67
  "sanitize-filename": "^1.6.3",
68
68
  "uuid": "^9.0.1",
69
- "document-model": "2.10.0",
70
- "document-model-libs": "1.120.3"
69
+ "document-model": "2.11.0",
70
+ "document-model-libs": "1.121.0"
71
71
  },
72
72
  "scripts": {
73
73
  "check-types": "tsc --build",
@@ -79,6 +79,6 @@
79
79
  "test:watch": "vitest watch",
80
80
  "clean": "rimraf dist",
81
81
  "clean:node_modules": "rimraf node_modules",
82
- "build": "prisma generate"
82
+ "postinstall": "prisma generate"
83
83
  }
84
84
  }
package/src/index.ts CHANGED
@@ -2,5 +2,3 @@ export * from "./server";
2
2
  export * from "./server/error";
3
3
  export * from "./storage";
4
4
  export * from "./utils";
5
-
6
- export const test = "test";
@@ -102,6 +102,8 @@ export class ListenerManager extends BaseListenerManager {
102
102
  const driveTransmitters = this.transmitters[drive] || {};
103
103
  driveTransmitters[listener.listenerId] = transmitter;
104
104
  this.transmitters[drive] = driveTransmitters;
105
+
106
+ this.triggerUpdate(true, { type: "local" });
105
107
  return Promise.resolve(transmitter);
106
108
  }
107
109
 
@@ -1,30 +1,40 @@
1
- import { Document, OperationScope } from "document-model/document";
1
+ import { Document, Operation, OperationScope } from "document-model/document";
2
2
  import { logger } from "../../../utils/logger";
3
3
  import {
4
4
  IBaseDocumentDriveServer,
5
5
  Listener,
6
6
  ListenerRevision,
7
- OperationUpdate,
8
7
  StrandUpdate,
9
8
  } from "../../types";
10
- import { buildRevisionsFilter } from "../../utils";
11
- import { ITransmitter } from "./types";
9
+ import { ITransmitter, StrandUpdateSource } from "./types";
10
+ import { InferDocumentOperation } from "../../../read-mode/types";
12
11
 
13
- export interface IReceiver {
14
- transmit: (strands: InternalTransmitterUpdate[]) => Promise<void>;
15
- disconnect: () => Promise<void>;
12
+ export interface IReceiver<
13
+ T extends Document = Document,
14
+ S extends OperationScope = OperationScope,
15
+ > {
16
+ onStrands: (strands: InternalTransmitterUpdate<T, S>[]) => Promise<void>;
17
+ onDisconnect: () => Promise<void>;
16
18
  }
17
19
 
20
+ export type InternalOperationUpdate<
21
+ D extends Document = Document,
22
+ S extends OperationScope = OperationScope,
23
+ > = Omit<Operation<InferDocumentOperation<D>>, "scope"> & {
24
+ state: D["state"][S];
25
+ previousState: D["state"][S];
26
+ };
27
+
18
28
  export type InternalTransmitterUpdate<
19
- T extends Document = Document,
29
+ D extends Document = Document,
20
30
  S extends OperationScope = OperationScope,
21
31
  > = {
22
32
  driveId: string;
23
33
  documentId: string;
24
34
  scope: S;
25
35
  branch: string;
26
- operations: OperationUpdate[];
27
- state: T["state"][S];
36
+ operations: InternalOperationUpdate<D, S>[];
37
+ state: D["state"][S];
28
38
  };
29
39
 
30
40
  export interface IInternalTransmitter extends ITransmitter {
@@ -32,52 +42,75 @@ export interface IInternalTransmitter extends ITransmitter {
32
42
  }
33
43
 
34
44
  export class InternalTransmitter implements ITransmitter {
35
- private drive: IBaseDocumentDriveServer;
36
- private listener: Listener;
37
- private receiver: IReceiver | undefined;
45
+ protected drive: IBaseDocumentDriveServer;
46
+ protected listener: Listener;
47
+ protected receiver: IReceiver | undefined;
38
48
 
39
49
  constructor(listener: Listener, drive: IBaseDocumentDriveServer) {
40
50
  this.listener = listener;
41
51
  this.drive = drive;
42
52
  }
43
53
 
54
+ async #buildInternalOperationUpdate(strand: StrandUpdate) {
55
+ const operations: InternalOperationUpdate[] = [];
56
+ const stateByIndex = new Map<number, unknown>();
57
+ const getStateByIndex = async (index: number) => {
58
+ const state = stateByIndex.get(index);
59
+ if (state) {
60
+ return state;
61
+ }
62
+ const document = await this.drive.getDocument(
63
+ strand.driveId,
64
+ strand.documentId,
65
+ {
66
+ revisions: {
67
+ [strand.scope]: index,
68
+ },
69
+ checkHashes: false,
70
+ },
71
+ );
72
+ if (index < 0) {
73
+ stateByIndex.set(index, document.initialState.state[strand.scope]);
74
+ } else {
75
+ stateByIndex.set(index, document.state[strand.scope]);
76
+ }
77
+ return stateByIndex.get(index);
78
+ };
79
+ for (const operation of strand.operations) {
80
+ operations.push({
81
+ ...operation,
82
+ state: await getStateByIndex(operation.index),
83
+ previousState: await getStateByIndex(operation.index - 1),
84
+ });
85
+ }
86
+ return operations;
87
+ }
88
+
44
89
  async transmit(
45
- strands: InternalTransmitterUpdate[],
90
+ strands: StrandUpdate[],
91
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
92
+ _source: StrandUpdateSource,
46
93
  ): Promise<ListenerRevision[]> {
47
94
  if (!this.receiver) {
48
95
  return [];
49
96
  }
50
97
 
51
- const retrievedDocuments = new Map<string, Document>();
52
98
  const updates: InternalTransmitterUpdate[] = [];
53
99
  for (const strand of strands) {
54
- let document = retrievedDocuments.get(
55
- `${strand.driveId}:${strand.documentId}`,
56
- );
57
- if (!document) {
58
- const revisions = buildRevisionsFilter(
59
- strands,
60
- strand.driveId,
61
- strand.documentId,
62
- );
63
- document = await (strand.documentId
64
- ? this.drive.getDocument(strand.driveId, strand.documentId, {
65
- revisions,
66
- })
67
- : this.drive.getDrive(strand.driveId, { revisions }));
68
- retrievedDocuments.set(
69
- `${strand.driveId}:${strand.documentId}`,
70
- document,
71
- );
72
- }
73
- updates.push({ ...strand, state: document.state[strand.scope] });
100
+ const operations = await this.#buildInternalOperationUpdate(strand);
101
+ const state = operations.at(-1)?.state ?? {};
102
+ updates.push({
103
+ ...strand,
104
+ operations,
105
+ state,
106
+ });
74
107
  }
75
108
  try {
76
- await this.receiver.transmit(updates);
109
+ await this.receiver.onStrands(updates);
77
110
  return strands.map(({ operations, ...s }) => ({
78
111
  ...s,
79
112
  status: "SUCCESS",
80
- revision: operations[operations.length - 1]?.index ?? -1,
113
+ revision: operations.at(operations.length - 1)?.index ?? -1,
81
114
  }));
82
115
  } catch (error) {
83
116
  logger.error(error);
@@ -85,7 +118,7 @@ export class InternalTransmitter implements ITransmitter {
85
118
  return strands.map(({ operations, ...s }) => ({
86
119
  ...s,
87
120
  status: "ERROR",
88
- revision: (operations[0]?.index ?? 0) - 1,
121
+ revision: (operations.at(0)?.index ?? 0) - 1,
89
122
  }));
90
123
  }
91
124
  }
@@ -95,6 +128,10 @@ export class InternalTransmitter implements ITransmitter {
95
128
  }
96
129
 
97
130
  async disconnect(): Promise<void> {
98
- await this.receiver?.disconnect();
131
+ await this.receiver?.onDisconnect();
132
+ }
133
+
134
+ getListener(): Listener {
135
+ return this.listener;
99
136
  }
100
137
  }