document-drive 1.0.0-alpha.52 → 1.0.0-alpha.53

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.52",
3
+ "version": "1.0.0-alpha.53",
4
4
  "license": "AGPL-3.0-only",
5
5
  "type": "module",
6
6
  "module": "./src/index.ts",
@@ -122,16 +122,16 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
122
122
 
123
123
  const result = await (!strand.documentId
124
124
  ? this.addDriveOperations(
125
- strand.driveId,
126
- operations as Operation<DocumentDriveAction | BaseAction>[],
127
- false
128
- )
125
+ strand.driveId,
126
+ operations as Operation<DocumentDriveAction | BaseAction>[],
127
+ false
128
+ )
129
129
  : this.addOperations(
130
- strand.driveId,
131
- strand.documentId,
132
- operations,
133
- false
134
- ));
130
+ strand.driveId,
131
+ strand.documentId,
132
+ operations,
133
+ false
134
+ ));
135
135
 
136
136
  if (result.status === 'ERROR') {
137
137
  this.updateSyncStatus(strand.driveId, result.status, result.error);
@@ -299,14 +299,14 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
299
299
  const nodeUnits =
300
300
  scope?.length || branch?.length
301
301
  ? node.synchronizationUnits.filter(
302
- unit =>
303
- (!scope?.length ||
304
- scope.includes(unit.scope) ||
305
- scope.includes('*')) &&
306
- (!branch?.length ||
307
- branch.includes(unit.branch) ||
308
- branch.includes('*'))
309
- )
302
+ unit =>
303
+ (!scope?.length ||
304
+ scope.includes(unit.scope) ||
305
+ scope.includes('*')) &&
306
+ (!branch?.length ||
307
+ branch.includes(unit.branch) ||
308
+ branch.includes('*'))
309
+ )
310
310
  : node.synchronizationUnits;
311
311
  if (!nodeUnits.length) {
312
312
  continue;
@@ -494,19 +494,7 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
494
494
  logger.error('Error getting drive from cache', e);
495
495
  }
496
496
  const driveStorage = await this.storage.getDrive(drive);
497
- const documentModel = this._getDocumentModel(driveStorage.documentType);
498
- const document = baseUtils.replayDocument(
499
- driveStorage.initialState,
500
- filterOperationsByRevision(
501
- driveStorage.operations,
502
- options?.revisions
503
- ),
504
- documentModel.reducer,
505
- undefined,
506
- driveStorage,
507
- undefined,
508
- { checkHashes: false }
509
- );
497
+ const document = this._replayDocument(driveStorage, options);
510
498
  if (!isDocumentDrive(document)) {
511
499
  throw new Error(
512
500
  `Document with id ${drive} is not a Document Drive`
@@ -519,6 +507,30 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
519
507
  }
520
508
  }
521
509
 
510
+ async getDriveBySlug(slug: string, options?: GetDocumentOptions) {
511
+ try {
512
+ const document = await this.cache.getDocument('drives', slug);
513
+ if (document && isDocumentDrive(document)) {
514
+ return document;
515
+ }
516
+ } catch (e) {
517
+ logger.error('Error getting drive from cache', e);
518
+ }
519
+
520
+ const driveStorage = await this.storage.getDriveBySlug(slug);
521
+ const document = this._replayDocument(driveStorage, options);
522
+ if (!isDocumentDrive(document)) {
523
+ throw new Error(
524
+ `Document with slug ${slug} is not a Document Drive`
525
+ );
526
+ } else {
527
+ this.cache
528
+ .setDocument('drives', slug, document)
529
+ .catch(logger.error);
530
+ return document;
531
+ }
532
+ }
533
+
522
534
  async getDocument(drive: string, id: string, options?: GetDocumentOptions) {
523
535
  try {
524
536
  const document = await this.cache.getDocument(drive, id);
@@ -664,11 +676,11 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
664
676
  e instanceof OperationError
665
677
  ? e
666
678
  : new OperationError(
667
- 'ERROR',
668
- nextOperation,
669
- (e as Error).message,
670
- (e as Error).cause
671
- );
679
+ 'ERROR',
680
+ nextOperation,
681
+ (e as Error).message,
682
+ (e as Error).cause
683
+ );
672
684
 
673
685
  // TODO: don't break on errors...
674
686
  break;
@@ -925,11 +937,11 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
925
937
  error instanceof OperationError
926
938
  ? error
927
939
  : new OperationError(
928
- 'ERROR',
929
- undefined,
930
- (error as Error).message,
931
- (error as Error).cause
932
- );
940
+ 'ERROR',
941
+ undefined,
942
+ (error as Error).message,
943
+ (error as Error).cause
944
+ );
933
945
 
934
946
  return {
935
947
  status: operationError.status,
@@ -1097,11 +1109,11 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
1097
1109
  error instanceof OperationError
1098
1110
  ? error
1099
1111
  : new OperationError(
1100
- 'ERROR',
1101
- undefined,
1102
- (error as Error).message,
1103
- (error as Error).cause
1104
- );
1112
+ 'ERROR',
1113
+ undefined,
1114
+ (error as Error).message,
1115
+ (error as Error).cause
1116
+ );
1105
1117
 
1106
1118
  return {
1107
1119
  status: operationError.status,
@@ -1268,4 +1280,22 @@ export class DocumentDriveServer extends BaseDocumentDriveServer {
1268
1280
  logger.debug(`Emitting event ${event}`, args);
1269
1281
  return this.emitter.emit(event, ...args);
1270
1282
  }
1283
+
1284
+ private _replayDocument(documentStorage: DocumentStorage, options?: GetDocumentOptions) {
1285
+ const documentModel = this._getDocumentModel(documentStorage.documentType);
1286
+ const document = baseUtils.replayDocument(
1287
+ documentStorage.initialState,
1288
+ filterOperationsByRevision(
1289
+ documentStorage.operations,
1290
+ options?.revisions
1291
+ ),
1292
+ documentModel.reducer,
1293
+ undefined,
1294
+ documentStorage,
1295
+ undefined,
1296
+ { checkHashes: false }
1297
+ );
1298
+
1299
+ return document;
1300
+ }
1271
1301
  }
@@ -111,6 +111,19 @@ export class BrowserStorage implements IDriveStorage {
111
111
  return drive;
112
112
  }
113
113
 
114
+ async getDriveBySlug(slug: string) {
115
+ // get oldes drives first
116
+ const drives = (await this.getDrives()).reverse();
117
+ for (const drive of drives) {
118
+ const driveData = await this.getDrive(drive);
119
+ if (driveData.initialState.state.global.slug === slug) {
120
+ return this.getDrive(drive);
121
+ }
122
+ }
123
+
124
+ throw new Error(`Drive with slug ${slug} not found`);
125
+ }
126
+
114
127
  async createDrive(id: string, drive: DocumentDriveStorage) {
115
128
  const db = await this.db;
116
129
  await db.setItem(this.buildKey(BrowserStorage.DRIVES_KEY, id), drive);
@@ -197,6 +197,18 @@ export class FilesystemStorage implements IDriveStorage {
197
197
  }
198
198
  }
199
199
 
200
+ async getDriveBySlug(slug: string) {
201
+ // get oldes drives first
202
+ const drives = (await this.getDrives()).reverse();
203
+ for (const drive of drives) {
204
+ const { initialState: { state: { global: { slug: driveSlug } } } } = await this.getDrive(drive);
205
+ if (driveSlug === slug) {
206
+ return this.getDrive(drive);
207
+ }
208
+ }
209
+ throw new Error(`Drive with slug ${slug} not found`);
210
+ }
211
+
200
212
  createDrive(id: string, drive: DocumentDriveStorage) {
201
213
  return this.createDocument(FilesystemStorage.DRIVES_DIR, id, drive);
202
214
  }
@@ -11,6 +11,7 @@ import { DocumentDriveStorage, DocumentStorage, IDriveStorage } from './types';
11
11
  export class MemoryStorage implements IDriveStorage {
12
12
  private documents: Record<string, Record<string, DocumentStorage>>;
13
13
  private drives: Record<string, DocumentDriveStorage>;
14
+ private slugToDriveId: Record<string, string> = {};
14
15
 
15
16
  constructor() {
16
17
  this.documents = {};
@@ -116,8 +117,20 @@ export class MemoryStorage implements IDriveStorage {
116
117
  return drive;
117
118
  }
118
119
 
120
+ async getDriveBySlug(slug: string) {
121
+ const driveId = this.slugToDriveId[slug];
122
+ if (!driveId) {
123
+ throw new Error(`Drive with slug ${slug} not found`);
124
+ }
125
+ return this.getDrive(driveId);
126
+ }
127
+
119
128
  async createDrive(id: string, drive: DocumentDriveStorage) {
120
129
  this.drives[id] = drive;
130
+ const { slug } = drive.initialState.state.global;
131
+ if (slug) {
132
+ this.slugToDriveId[slug] = id;
133
+ }
121
134
  }
122
135
 
123
136
  async addDriveOperations(
@@ -17,7 +17,6 @@ import type {
17
17
  import { ConflictOperationError } from '../server/error';
18
18
  import { logger } from '../utils/logger';
19
19
  import { DocumentDriveStorage, DocumentStorage, IDriveStorage } from './types';
20
- import { JitterType } from 'exponential-backoff/dist/options';
21
20
 
22
21
  type Transaction = Omit<
23
22
  PrismaClient<Prisma.PrismaClientOptions, never>,
@@ -79,6 +78,18 @@ export class PrismaStorage implements IDriveStorage {
79
78
  async createDrive(id: string, drive: DocumentDriveStorage): Promise<void> {
80
79
  // drive for all drive documents
81
80
  await this.createDocument('drives', id, drive as DocumentStorage);
81
+ const count = await this.db.drive.upsert({
82
+ where: {
83
+ slug: drive.initialState.state.global.slug ?? id
84
+ },
85
+ create: {
86
+ id: id,
87
+ slug: drive.initialState.state.global.slug ?? id
88
+ },
89
+ update: {
90
+ id
91
+ }
92
+ });
82
93
  }
83
94
  async addDriveOperations(
84
95
  id: string,
@@ -391,6 +402,20 @@ export class PrismaStorage implements IDriveStorage {
391
402
  }
392
403
  }
393
404
 
405
+ async getDriveBySlug(slug: string) {
406
+ const driveEntity = await this.db.drive.findFirst({
407
+ where: {
408
+ slug
409
+ }
410
+ });
411
+
412
+ if (!driveEntity) {
413
+ throw new Error(`Drive with slug ${slug} not found`);
414
+ }
415
+
416
+ return this.getDrive(driveEntity.id);
417
+ }
418
+
394
419
  async deleteDrive(id: string) {
395
420
  await this.db.document.deleteMany({
396
421
  where: {
@@ -20,6 +20,13 @@ export class SequelizeStorage implements IDriveStorage {
20
20
  }
21
21
 
22
22
  public syncModels() {
23
+ const Drive = this.db.define('drive', {
24
+ slug: {
25
+ type: DataTypes.STRING,
26
+ primaryKey: true
27
+ },
28
+ id: DataTypes.STRING,
29
+ })
23
30
  const Document = this.db.define('document', {
24
31
  id: {
25
32
  type: DataTypes.STRING,
@@ -113,6 +120,8 @@ export class SequelizeStorage implements IDriveStorage {
113
120
 
114
121
  async createDrive(id: string, drive: DocumentDriveStorage): Promise<void> {
115
122
  await this.createDocument('drives', id, drive as DocumentStorage);
123
+ const Drive = this.db.models.drive;
124
+ await Drive?.upsert({ id, slug: drive.initialState.state.global.slug });
116
125
  }
117
126
  async addDriveOperations(
118
127
  id: string,
@@ -379,6 +388,25 @@ export class SequelizeStorage implements IDriveStorage {
379
388
  return doc as DocumentDriveStorage;
380
389
  }
381
390
 
391
+ async getDriveBySlug(slug: string) {
392
+ const Drive = this.db.models.drive;
393
+ if (!Drive) {
394
+ throw new Error('Drive model not found');
395
+ }
396
+
397
+ const driveEntity = await Drive.findOne({
398
+ where: {
399
+ slug
400
+ }
401
+ });
402
+
403
+ if (!driveEntity) {
404
+ throw new Error(`Drive with slug ${slug} not found`);
405
+ }
406
+
407
+ return this.getDrive(driveEntity.dataValues.id);
408
+ }
409
+
382
410
  async deleteDrive(id: string) {
383
411
  await this.deleteDocument('drives', id);
384
412
 
@@ -392,5 +420,14 @@ export class SequelizeStorage implements IDriveStorage {
392
420
  driveId: id
393
421
  }
394
422
  });
423
+
424
+ const Drive = this.db.models.drive;
425
+ if (Drive) {
426
+ await Drive.destroy({
427
+ where: {
428
+ id: id
429
+ }
430
+ });
431
+ }
395
432
  }
396
433
  }
@@ -8,6 +8,7 @@ import type {
8
8
  DocumentHeader,
9
9
  Operation
10
10
  } from 'document-model/document';
11
+ import { GetDocumentOptions } from '../server';
11
12
 
12
13
  export type DocumentStorage<D extends Document = Document> = Omit<
13
14
  D,
@@ -45,6 +46,7 @@ export interface IStorage {
45
46
  export interface IDriveStorage extends IStorage {
46
47
  getDrives(): Promise<string[]>;
47
48
  getDrive(id: string): Promise<DocumentDriveStorage>;
49
+ getDriveBySlug(slug: string): Promise<DocumentDriveStorage>;
48
50
  createDrive(id: string, drive: DocumentDriveStorage): Promise<void>;
49
51
  deleteDrive(id: string): Promise<void>;
50
52
  clearStorage?(): Promise<void>;
@@ -98,3 +98,4 @@ export function isNoopUpdate(
98
98
  export function isBefore(dateA: Date | string, dateB: Date | string) {
99
99
  return new Date(dateA) < new Date(dateB);
100
100
  }
101
+