document-drive 0.0.27 → 0.0.29

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.
@@ -1,9 +1,9 @@
1
- import { type Prisma } from '@prisma/client';
1
+ import { PrismaClient, type Prisma } from '@prisma/client';
2
2
  import {
3
3
  DocumentDriveLocalState,
4
4
  DocumentDriveState
5
5
  } from 'document-model-libs/document-drive';
6
- import {
6
+ import type {
7
7
  DocumentHeader,
8
8
  ExtendedState,
9
9
  Operation,
@@ -12,12 +12,14 @@ import {
12
12
  import { DocumentDriveStorage, DocumentStorage, IDriveStorage } from './types';
13
13
 
14
14
  export class PrismaStorage implements IDriveStorage {
15
- private db: Prisma.TransactionClient;
15
+ private db: PrismaClient;
16
16
 
17
- constructor(db: Prisma.TransactionClient) {
17
+ constructor(db: PrismaClient) {
18
18
  this.db = db;
19
19
  }
20
+
20
21
  async createDrive(id: string, drive: DocumentDriveStorage): Promise<void> {
22
+ // drive for all drive documents
21
23
  await this.createDocument('drives', id, drive as DocumentStorage);
22
24
  }
23
25
  async addDriveOperations(
@@ -27,6 +29,7 @@ export class PrismaStorage implements IDriveStorage {
27
29
  ): Promise<void> {
28
30
  await this.addDocumentOperations('drives', id, operations, header);
29
31
  }
32
+
30
33
  async createDocument(
31
34
  drive: string,
32
35
  id: string,
@@ -84,7 +87,8 @@ export class PrismaStorage implements IDriveStorage {
84
87
  timestamp: op.timestamp,
85
88
  type: op.type,
86
89
  scope: op.scope,
87
- branch: 'main'
90
+ branch: 'main',
91
+ skip: op.skip
88
92
  },
89
93
  update: {
90
94
  driveId: drive,
@@ -95,18 +99,17 @@ export class PrismaStorage implements IDriveStorage {
95
99
  timestamp: op.timestamp,
96
100
  type: op.type,
97
101
  scope: op.scope,
98
- branch: 'main'
102
+ branch: 'main',
103
+ skip: op.skip
99
104
  }
100
105
  });
101
106
  })
102
107
  );
103
108
 
104
- await this.db.document.update({
109
+ await this.db.document.updateMany({
105
110
  where: {
106
- id_driveId: {
107
- id,
108
- driveId: 'drives'
109
- }
111
+ id,
112
+ driveId: drive
110
113
  },
111
114
  data: {
112
115
  lastModified: header.lastModified,
@@ -163,6 +166,9 @@ export class PrismaStorage implements IDriveStorage {
163
166
  },
164
167
  include: {
165
168
  operations: {
169
+ orderBy: {
170
+ index: 'asc'
171
+ },
166
172
  include: {
167
173
  attachments: true
168
174
  }
@@ -175,7 +181,6 @@ export class PrismaStorage implements IDriveStorage {
175
181
  }
176
182
 
177
183
  const dbDoc = result;
178
-
179
184
  const doc = {
180
185
  created: dbDoc.created.toISOString(),
181
186
  name: dbDoc.name ? dbDoc.name : '',
@@ -184,11 +189,12 @@ export class PrismaStorage implements IDriveStorage {
184
189
  DocumentDriveState,
185
190
  DocumentDriveLocalState
186
191
  >,
187
- lastModified: dbDoc.lastModified.toISOString(),
192
+ lastModified: new Date(dbDoc.lastModified).toISOString(),
188
193
  operations: {
189
194
  global: dbDoc.operations
190
- .filter(op => op.scope === 'global')
195
+ .filter(op => op.scope === 'global' && !op.clipboard)
191
196
  .map(op => ({
197
+ skip: op.skip,
192
198
  hash: op.hash,
193
199
  index: op.index,
194
200
  timestamp: new Date(op.timestamp).toISOString(),
@@ -198,8 +204,9 @@ export class PrismaStorage implements IDriveStorage {
198
204
  // attachments: fileRegistry
199
205
  })),
200
206
  local: dbDoc.operations
201
- .filter(op => op.scope === 'local')
207
+ .filter(op => op.scope === 'local' && !op.clipboard)
202
208
  .map(op => ({
209
+ skip: op.skip,
203
210
  hash: op.hash,
204
211
  index: op.index,
205
212
  timestamp: new Date(op.timestamp).toISOString(),
@@ -209,19 +216,67 @@ export class PrismaStorage implements IDriveStorage {
209
216
  // attachments: fileRegistry
210
217
  }))
211
218
  },
212
- revision: dbDoc.revision as Required<Record<OperationScope, number>>
219
+ clipboard: dbDoc.operations
220
+ .filter(op => op.clipboard)
221
+ .map(op => ({
222
+ skip: op.skip,
223
+ hash: op.hash,
224
+ index: op.index,
225
+ timestamp: new Date(op.timestamp).toISOString(),
226
+ input: op.input,
227
+ type: op.type,
228
+ scope: op.scope as OperationScope
229
+ // attachments: fileRegistry
230
+ })),
231
+ revision: dbDoc.revision as Record<OperationScope, number>
213
232
  };
214
233
 
215
234
  return doc;
216
235
  }
217
236
 
218
237
  async deleteDocument(drive: string, id: string) {
219
- await this.db.document.deleteMany({
238
+ await this.db.attachment.deleteMany({
220
239
  where: {
221
240
  driveId: drive,
222
- id: id
241
+ documentId: id
223
242
  }
224
243
  });
244
+
245
+ await this.db.operation.deleteMany({
246
+ where: {
247
+ driveId: drive,
248
+ documentId: id
249
+ }
250
+ });
251
+
252
+ await this.db.document.delete({
253
+ where: {
254
+ id_driveId: {
255
+ driveId: drive,
256
+ id: id
257
+ }
258
+ }
259
+ });
260
+
261
+ if (drive === 'drives') {
262
+ await this.db.attachment.deleteMany({
263
+ where: {
264
+ driveId: id
265
+ }
266
+ });
267
+
268
+ await this.db.operation.deleteMany({
269
+ where: {
270
+ driveId: id
271
+ }
272
+ });
273
+
274
+ await this.db.document.deleteMany({
275
+ where: {
276
+ driveId: id
277
+ }
278
+ });
279
+ }
225
280
  }
226
281
 
227
282
  async getDrives() {
@@ -229,15 +284,15 @@ export class PrismaStorage implements IDriveStorage {
229
284
  }
230
285
 
231
286
  async getDrive(id: string) {
232
- return this.getDocument('drives', id) as Promise<DocumentDriveStorage>;
287
+ try {
288
+ const doc = await this.getDocument('drives', id);
289
+ return doc as DocumentDriveStorage;
290
+ } catch (e) {
291
+ throw new Error(`Drive with id ${id} not found`);
292
+ }
233
293
  }
234
294
 
235
295
  async deleteDrive(id: string) {
236
296
  await this.deleteDocument('drives', id);
237
- await this.db.document.deleteMany({
238
- where: {
239
- driveId: id
240
- }
241
- });
242
297
  }
243
298
  }
@@ -27,7 +27,8 @@ export interface IStorage {
27
27
  drive: string,
28
28
  id: string,
29
29
  operations: Operation[],
30
- header: DocumentHeader
30
+ header: DocumentHeader,
31
+ updatedOperations?: Operation[]
31
32
  ): Promise<void>;
32
33
  deleteDocument(drive: string, id: string): Promise<void>;
33
34
  }
@@ -0,0 +1,46 @@
1
+ import request, { GraphQLClient, gql } from 'graphql-request';
2
+
3
+ export { gql } from 'graphql-request';
4
+
5
+ export type DriveInfo = {
6
+ id: string;
7
+ name: string;
8
+ slug: string;
9
+ icon?: string;
10
+ };
11
+
12
+ // replaces fetch so it can be used in Node and Browser envs
13
+ export async function requestGraphql<T>(...args: Parameters<typeof request>) {
14
+ const [url, ...requestArgs] = args;
15
+ const client = new GraphQLClient(url, { fetch });
16
+ return client.request<T>(...requestArgs);
17
+ }
18
+
19
+ export async function requestPublicDrive(url: string): Promise<DriveInfo> {
20
+ let drive: DriveInfo;
21
+ try {
22
+ const result = await requestGraphql<{ drive: DriveInfo }>(
23
+ url,
24
+ gql`
25
+ query getDrive {
26
+ drive {
27
+ id
28
+ name
29
+ icon
30
+ slug
31
+ }
32
+ }
33
+ `
34
+ );
35
+ drive = result.drive;
36
+ } catch (e) {
37
+ console.error(e);
38
+ throw new Error("Couldn't find drive info");
39
+ }
40
+
41
+ if (!drive) {
42
+ throw new Error('Drive not found');
43
+ }
44
+
45
+ return drive;
46
+ }
@@ -0,0 +1,77 @@
1
+ import {
2
+ DocumentDriveDocument,
3
+ documentModel as DocumentDriveModel,
4
+ z
5
+ } from 'document-model-libs/document-drive';
6
+ import {
7
+ Action,
8
+ BaseAction,
9
+ Document,
10
+ DocumentOperations,
11
+ Operation
12
+ } from 'document-model/document';
13
+
14
+ export function isDocumentDrive(
15
+ document: Document
16
+ ): document is DocumentDriveDocument {
17
+ return (
18
+ document.documentType === DocumentDriveModel.id &&
19
+ z.DocumentDriveStateSchema().safeParse(document.state.global).success
20
+ );
21
+ }
22
+
23
+ export function mergeOperations<A extends Action = Action>(
24
+ currentOperations: DocumentOperations<A>,
25
+ newOperations: Operation<A | BaseAction>[]
26
+ ): DocumentOperations<A> {
27
+ return newOperations.reduce((acc, curr) => {
28
+ const operations = acc[curr.scope] ?? [];
29
+ acc[curr.scope] = [...operations, curr] as Operation<A>[];
30
+ return acc;
31
+ }, currentOperations);
32
+ }
33
+
34
+ export function generateUUID(): string {
35
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
36
+ const crypto =
37
+ typeof window !== 'undefined' ? window.crypto : require('crypto');
38
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
39
+ return crypto.randomUUID() as string;
40
+ }
41
+
42
+ export function applyUpdatedOperations<A extends Action = Action>(
43
+ currentOperations: DocumentOperations<A>,
44
+ updatedOperations: Operation<A | BaseAction>[]
45
+ ): DocumentOperations<A> {
46
+ return updatedOperations.reduce(
47
+ (acc, curr) => {
48
+ const operations = acc[curr.scope] ?? [];
49
+ acc[curr.scope] = operations.map(op => {
50
+ return op.index === curr.index ? curr : op;
51
+ });
52
+ return acc;
53
+ },
54
+ { ...currentOperations }
55
+ );
56
+ }
57
+
58
+ export function isNoopUpdate(
59
+ operation: Operation,
60
+ latestOperation?: Operation
61
+ ) {
62
+ if (!latestOperation) {
63
+ return false;
64
+ }
65
+
66
+ const isNoopOp = operation.type === 'NOOP';
67
+ const isNoopLatestOp = latestOperation.type === 'NOOP';
68
+ const isSameIndexOp = operation.index === latestOperation.index;
69
+ const isSkipOpGreaterThanLatestOp = operation.skip > latestOperation.skip;
70
+
71
+ return (
72
+ isNoopOp &&
73
+ isNoopLatestOp &&
74
+ isSameIndexOp &&
75
+ isSkipOpGreaterThanLatestOp
76
+ );
77
+ }
package/src/utils.ts DELETED
@@ -1,32 +0,0 @@
1
- import {
2
- DocumentDriveDocument,
3
- documentModel as DocumentDriveModel,
4
- z
5
- } from 'document-model-libs/document-drive';
6
- import {
7
- Action,
8
- BaseAction,
9
- Document,
10
- DocumentOperations,
11
- Operation
12
- } from 'document-model/document';
13
-
14
- export function isDocumentDrive(
15
- document: Document
16
- ): document is DocumentDriveDocument {
17
- return (
18
- document.documentType === DocumentDriveModel.id &&
19
- z.DocumentDriveStateSchema().safeParse(document.state.global).success
20
- );
21
- }
22
-
23
- export function mergeOperations<A extends Action = Action>(
24
- currentOperations: DocumentOperations<A>,
25
- newOperations: Operation<A | BaseAction>[]
26
- ): DocumentOperations<A> {
27
- return newOperations.reduce((acc, curr) => {
28
- const operations = acc[curr.scope] ?? [];
29
- acc[curr.scope] = [...operations, curr] as Operation<A>[];
30
- return acc;
31
- }, currentOperations);
32
- }