@shaxpir/duiduidui-models 1.3.2 → 1.3.4

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,129 +0,0 @@
1
- import { Doc } from '@shaxpir/sharedb/lib/client';
2
- import { CachingHasher, CompactDate, CompactDateTime, MultiClock, MultiTime, Time } from "@shaxpir/shaxpir-common";
3
- import { ShareSync, ShareSyncFactory } from '../repo';
4
- import { ArrayView } from './ArrayView';
5
- import { Content, ContentBody, ContentId, ContentMeta, ContentRef } from "./Content";
6
- import { ContentKind } from './ContentKind';
7
- import { TagFilterConfig } from './Device';
8
- import { BatchOperation } from './Operation';
9
- import { Session } from './Session';
10
-
11
- export interface JourneyEntry {
12
- key:string;
13
- at_utc_time:CompactDateTime;
14
- }
15
-
16
- export interface WorkspacePayload {
17
- devices:ContentId[];
18
- sessions:MultiTime[];
19
- journey?:{[key:string]:CompactDateTime};
20
- global_tags?:TagFilterConfig;
21
- }
22
-
23
- export interface WorkspaceBody extends ContentBody {
24
- meta:ContentMeta;
25
- payload:WorkspacePayload;
26
- }
27
-
28
- export class Workspace extends Content {
29
-
30
- private _devicesView:ArrayView<ContentId>;
31
- private _sessionsView:ArrayView<MultiTime>;
32
-
33
- constructor(doc:Doc, shouldAcquire:boolean, shareSync:ShareSync) {
34
- super(doc, shouldAcquire, shareSync);
35
- this._devicesView = new ArrayView<ContentId>(this, [ 'payload', 'devices' ]);
36
- this._sessionsView = new ArrayView<MultiTime>(this, [ 'payload', 'sessions' ]);
37
- }
38
-
39
- public get payload():WorkspacePayload {
40
- return this.doc.data.payload;
41
- }
42
-
43
- public static makeWorkspaceId(userId:ContentId):ContentId {
44
- return CachingHasher.makeMd5Base62Hash(userId + "-" + ContentKind.WORKSPACE) as ContentId;
45
- }
46
-
47
- public static create(
48
- userId:ContentId,
49
- deviceId:ContentId
50
- ):Workspace {
51
- const now = MultiClock.now();
52
- const workspaceId = Workspace.makeWorkspaceId(userId);
53
- return ShareSyncFactory.get().createContent(
54
- {
55
- meta : {
56
- ref : workspaceId,
57
- kind : ContentKind.WORKSPACE,
58
- id : workspaceId,
59
- owner : userId,
60
- created_at : now,
61
- updated_at : now
62
- },
63
- payload : {
64
- devices: [ deviceId ],
65
- journey: {},
66
- sessions: [] as MultiTime[]
67
- }
68
- }
69
- ) as Workspace;
70
- }
71
-
72
- public get sessions():ArrayView<MultiTime> {
73
- this.checkDisposed("Workspace.sessions");
74
- return this._sessionsView;
75
- }
76
-
77
- // NOTE: As the user adds devices, we will always add new devices to the end of the array.
78
- // So the most-recently-added device will always be at the end.
79
- public get devices():ArrayView<ContentId> {
80
- this.checkDisposed("Workspace.devices");
81
- return this._devicesView;
82
- }
83
-
84
- public async acquireSessionsFromDate(
85
- sessionId:ContentId,
86
- localDate:CompactDate
87
- ):Promise<Session[]> {
88
- this.checkDisposed("Workspace.loadSessionsFromDate");
89
- const shareSync = ShareSyncFactory.get();
90
- const sessions:Session[] = [];
91
- for (let i = 0, len = this.sessions.length; i < len; i++) {
92
- const sessionCreatedAt:MultiTime = this.sessions.get(i);
93
- const sessionCreatedLocalDate = Time.dateFrom(sessionCreatedAt.local_time);
94
- if (localDate == sessionCreatedLocalDate) {
95
- const sessionRef:ContentRef = Session.makeSessionRef(sessionId, sessionCreatedAt);
96
- const session:Session = await shareSync.acquire(ContentKind.SESSION, sessionRef) as Session;
97
- sessions.push(session);
98
- }
99
- }
100
- return sessions;
101
- }
102
-
103
- public hasJourneyDate(journeyKey:string):boolean {
104
- this.checkDisposed("Workspace.hasJourneyDate");
105
- return this.payload.hasOwnProperty('journey') && this.payload.journey.hasOwnProperty(journeyKey);
106
- }
107
- public getJourneyDate(journeyKey:string):CompactDateTime {
108
- this.checkDisposed("Workspace.getJourneyDate");
109
- if (this.payload.hasOwnProperty('journey')) {
110
- if (this.payload.journey.hasOwnProperty(journeyKey)) {
111
- return this.payload.journey[journeyKey];
112
- }
113
- }
114
- return null as CompactDateTime;
115
- }
116
- public setJourneyDate(journeyKey:string, journeyDate:CompactDateTime):void {
117
- this.checkDisposed("Workspace.setJourneyDate");
118
- const batch:BatchOperation = new BatchOperation(this);
119
- if (this.payload.hasOwnProperty('journey')) {
120
- batch.setPathValue([ 'payload', 'journey', journeyKey ], journeyDate);
121
- } else {
122
- const journey:any = {};
123
- journey[journeyKey] = journeyDate;
124
- batch.setPathValue([ 'payload', 'journey' ], journey);
125
- }
126
- batch.commit();
127
- }
128
-
129
- }
@@ -1,24 +0,0 @@
1
- // created from 'create-ts-index'
2
-
3
- export * from './ArrayView';
4
- export * from './BayesianScore';
5
- export * from './ChangeModel';
6
- export * from './Content';
7
- export * from './ContentKind';
8
- export * from './Device';
9
- export * from './GeoLocation';
10
- export * from './Hanzi';
11
- export * from './Manifest';
12
- export * from './Media';
13
- export * from './Metric';
14
- export * from './Model';
15
- export * from './Operation';
16
- export * from './Permissions';
17
- export * from './Phrase';
18
- export * from './Profile';
19
- export * from './Progress';
20
- export * from './Review';
21
- export * from './Session';
22
- export * from './Term';
23
- export * from './User';
24
- export * from './Workspace';
@@ -1,25 +0,0 @@
1
- export abstract class ConnectionListener {
2
-
3
- private _onSocketEvent:(eventName:string, details:any) => void;
4
-
5
- public constructor(
6
- onSocketEvent:(eventName:string, details:any) => void
7
- ) {
8
- this._onSocketEvent = onSocketEvent;
9
- }
10
-
11
- public onSocketEvent(eventName:string, details:any):void {
12
- this._onSocketEvent(eventName, details);
13
- }
14
- }
15
-
16
- export class NoOpConnectionListener extends ConnectionListener {
17
-
18
- public constructor() {
19
- super(NoOpConnectionListener.onSocketEvent);
20
- }
21
-
22
- public static onSocketEvent(eventName:string, details:any):void {
23
- // DO NOTHING
24
- }
25
- }
@@ -1,14 +0,0 @@
1
- import * as Json1 from 'ot-json1';
2
-
3
- export * from 'ot-json1';
4
-
5
- // Export a version of ot-json1 that allows various conflicts
6
- export const type = {
7
- ...Json1.type.typeAllowingConflictsPred( (conflict) => {
8
- return (
9
- Json1.type.DROP_COLLISION === conflict.type
10
- || Json1.type.BLACKHOLE === conflict.type
11
- || Json1.type.RM_UNEXPECTED_CONTENT === conflict.type
12
- )
13
- })
14
- };
@@ -1,383 +0,0 @@
1
- import ShareDB, { Connection, Doc } from '@shaxpir/sharedb/lib/client';
2
- import { Base62, CompactDateTime } from '@shaxpir/shaxpir-common';
3
- import ReconnectingWebSocket from 'reconnecting-websocket';
4
- import { Model, Profile } from '../models';
5
- import { Content, ContentBody, ContentId, ContentRef } from '../models/Content';
6
- import { ContentKind } from '../models/ContentKind';
7
- import { Device } from '../models/Device';
8
- import { Manifest, ManifestBody } from '../models/Manifest';
9
- import { User } from '../models/User';
10
- import { Encryption } from '../util/Encryption';
11
- import { ConnectionListener } from './ConnectionListener';
12
-
13
- // Register the ShareDB types
14
- import * as Json1 from './PermissiveJson1';
15
-
16
- const UnicodeText = require('ot-text-unicode');
17
-
18
- ShareDB.types.register(Json1.type);
19
- ShareDB.types.register(UnicodeText.type);
20
-
21
- Json1.type.registerSubtype(UnicodeText.type);
22
-
23
- export enum ShareSyncDisposalStrategy {
24
- UNSUBSCRIBE,
25
- DESTROY,
26
- NOTHING
27
- }
28
-
29
- export interface ShareSyncOptions {
30
- namespace:string;
31
- debug:boolean;
32
- disposalStrategy:ShareSyncDisposalStrategy;
33
- connectionListener:ConnectionListener;
34
- urlProvider:() => Promise<string>;
35
- opErrorCallback:(error?:any) => void;
36
- onReadyCallback?:() => void;
37
- pingInterval?:number;
38
- encryptionKey?:string;
39
- webSocketConstructor?:any;
40
- }
41
-
42
- export class ShareSyncFactory {
43
-
44
- private static INSTANCE:ShareSync = null;
45
-
46
- public static initialize(
47
- encryption:Encryption,
48
- options:ShareSyncOptions
49
- ) {
50
- ShareSyncFactory.INSTANCE = new ShareSync(encryption, options);
51
- }
52
-
53
- public static get():ShareSync {
54
- return ShareSyncFactory.INSTANCE;
55
- }
56
- }
57
-
58
- export class ShareSync {
59
-
60
- private _debug:boolean;
61
- private _disposalStrategy:ShareSyncDisposalStrategy;
62
- private _opErrorCallback:(error?:any) => void;
63
-
64
- private _modelCache:Map<string, Model> = new Map();
65
-
66
- private _encryption:Encryption;
67
- private _hasDurableStore:boolean;
68
- private _useDurableStoreEncryption:boolean;
69
- private _durableStoreEncryptionKey:string;
70
- private _socketIsOpen:boolean;
71
- private _socket:ReconnectingWebSocket;
72
- private _connection:Connection;
73
- private _listener:ConnectionListener;
74
-
75
- public constructor(
76
- encryption:Encryption,
77
- options:ShareSyncOptions
78
- ) {
79
- const shareSync = this;
80
- shareSync._encryption = encryption;
81
- shareSync._debug = options.debug;
82
- shareSync._disposalStrategy = options.disposalStrategy;
83
- shareSync._opErrorCallback = options.opErrorCallback;
84
- shareSync._hasDurableStore = options.namespace && options.namespace.length > 0;
85
- shareSync._useDurableStoreEncryption = options.encryptionKey && options.encryptionKey.length > 0;
86
- shareSync._durableStoreEncryptionKey = options.encryptionKey;
87
-
88
- // Create and configure the socket.
89
- if (options.webSocketConstructor) {
90
- shareSync._socket = new ReconnectingWebSocket(options.urlProvider, [], { WebSocket : options.webSocketConstructor });
91
- } else {
92
- shareSync._socket = new ReconnectingWebSocket(options.urlProvider);
93
- }
94
-
95
- function updateSocketIsOpen() {
96
- shareSync._socketIsOpen = shareSync._socket.readyState === ReconnectingWebSocket.OPEN;
97
- }
98
-
99
- // The listener should forward all events: open, message, close, error
100
- shareSync._listener = options.connectionListener;
101
- shareSync._socket.addEventListener('open', (event:Event) => {
102
- updateSocketIsOpen();
103
- shareSync._listener.onSocketEvent('open', event);
104
- });
105
- shareSync._socket.addEventListener('message', (event:Event) => {
106
- updateSocketIsOpen();
107
- shareSync._listener.onSocketEvent('message', event);
108
- });
109
- shareSync._socket.addEventListener('close', (event:CloseEvent) => {
110
- updateSocketIsOpen();
111
- shareSync._listener.onSocketEvent('close', event);
112
- });
113
- shareSync._socket.addEventListener('error', (event:ErrorEvent) => {
114
- updateSocketIsOpen();
115
- shareSync._listener.onSocketEvent('error', event);
116
- });
117
-
118
- // Create the ShareDB connection from the socket
119
- if (shareSync._hasDurableStore) {
120
- shareSync._connection = new Connection(
121
- shareSync._socket,
122
- {
123
- durableStore : {
124
- namespace : options.namespace,
125
- debug : options.debug,
126
- useEncryption : shareSync._useDurableStoreEncryption,
127
- encryptionCallback : (plaintext:string):string => {
128
- return encryption.encrypt(plaintext, shareSync._durableStoreEncryptionKey);
129
- },
130
- decryptionCallback : (cyphertext:string):string => {
131
- return encryption.decrypt(cyphertext, shareSync._durableStoreEncryptionKey);
132
- },
133
- onReadyCallback : options.onReadyCallback,
134
- opErrorCallback : options.opErrorCallback,
135
- // Use the 'docData.meta.updated_at.utc_time' field (which is universal to all Model subclasses) as the 'version'
136
- // identifier that we use when determining if the doc in the DurableStore is up-to-date. This field won't yet
137
- // exist when the doc is newly created (since the 'doc.data' will be undefined), so in that case, we'll return
138
- // null as the version string, which will work correctly with the versioning semantics of the DurableStore.
139
- extVersionDecoder : (docData:any):string => {
140
- if (
141
- docData &&
142
- docData.meta &&
143
- docData.meta.updated_at &&
144
- docData.meta.updated_at.utc_time
145
- ) {
146
- return docData.meta.updated_at.utc_time;
147
- }
148
- return null;
149
- }
150
- }
151
- }
152
- );
153
- } else {
154
- shareSync._connection = new Connection(shareSync._socket);
155
- }
156
-
157
- shareSync._connection.on('error', (err:any) => {
158
- console.error(`ShareSync connection error: ${err}\n Error code: ${err.code}\n Error message: ${err.message}\n Error stack: ${err.stack}\n`);
159
- });
160
-
161
- // If the client provided a pingInterval option, then set up a periodic ping the
162
- // connection alive. Clients who don't provide this option will send pings.
163
- if (options.pingInterval && typeof(options.pingInterval) === 'number') {
164
- setInterval(() => { shareSync.ping() }, options.pingInterval);
165
- }
166
-
167
- if (!shareSync._hasDurableStore && !!options.onReadyCallback) {
168
- // If there is no DurableStore, then we can call the onReadyCallback now. However,
169
- // we have to use setTimeout to invoke it, because the callback will probably want
170
- // to retrieve the ShareSync instance from the factory, and because the constructor
171
- // hasn't actually finished yet, the INSTANCE pointer hasn't quite been assigned yet.
172
- setTimeout(options.onReadyCallback, 1);
173
- }
174
- }
175
-
176
- public isDebug():boolean {
177
- return this._debug;
178
- }
179
-
180
- public isOnline():boolean {
181
- return this._socketIsOpen;
182
- }
183
-
184
- public ping():void {
185
- try {
186
- (this._connection as any).ping();
187
- } catch (error) {
188
- // Ignore the error. We aren't directly concerned with the connection status.
189
- // We just need to provoke the connection to attempt sending a ping message,
190
- // so that if it fails to send that message, our normal socket-event listeners
191
- // will notice the change in status, and trigger our downstream logic for
192
- // displaying the offline status to the user.
193
- }
194
- }
195
-
196
- public makeContentId():ContentId {
197
- return this._encryption.getRandomString(Base62.CHARS, Content.ID_LENGTH) as ContentId;
198
- }
199
-
200
- public async exists(userId:ContentId, kind:ContentKind, ref:ContentRef):Promise<boolean> {
201
- // Check to see if this item exists in the model cache
202
- const compoundKey = `${kind}/${ref}`;
203
- if (this._modelCache.has(compoundKey)) {
204
- const model = this._modelCache.get(compoundKey);
205
- return model.exists();
206
- }
207
- // Check to see if this item exists in the durable-store inventory
208
- const existsInDurableStore = this._connection.isDocInInventory(kind, ref as string);
209
- if (existsInDurableStore) {
210
- return true;
211
- }
212
- // Check to see if this item is recorded in the manifest.
213
- const manifestId:ContentId = Manifest.makeManifestId(userId);
214
- const manifest = this.load(ContentKind.MANIFEST, manifestId) as Manifest;
215
- await manifest.acquire();
216
- const existsInManifest = manifest.doesContentExist(kind, ref);
217
- manifest.release();
218
- if (existsInManifest) {
219
- return true;
220
- }
221
- // Fetch this model from from the remote ShareDB server.
222
- const model = this.load(kind, ref);
223
- await model.acquire();
224
- const exists = model.exists();
225
- model.release();
226
- return exists;
227
- }
228
-
229
- public createManifest(body:ManifestBody):Manifest {
230
- const shareSync = this;
231
- // Create or retrieve the doc object for this manifest model.
232
- const manifestId = Manifest.makeManifestId(body.meta.owner);
233
- const doc = shareSync._connection.get(ContentKind.MANIFEST, manifestId);
234
- doc.create(
235
- body, Json1.type.uri, (createErr:any) => {
236
- if (createErr) {
237
- console.log(`error creating manifest ${manifestId}: ${createErr}`);
238
- }
239
- }
240
- );
241
- shareSync.isDebug() && console.log(`created manifest ${manifestId}`);
242
- const manifest = shareSync.wrap(ContentKind.MANIFEST, doc, true) as Manifest;
243
- this._modelCache.set(manifest.compoundKey, manifest);
244
- return manifest;
245
- }
246
-
247
- public createContent(body:ContentBody):Content {
248
- const shareSync = this;
249
- // Create or retrieve the doc object for this content model.
250
- const compoundKey = `${body.meta.kind}/${body.meta.ref}`;
251
- const doc = shareSync._connection.get(body.meta.kind, body.meta.ref);
252
- doc.create(
253
- body, Json1.type.uri, (createErr:any) => {
254
- if (createErr) {
255
- console.log(`error creating content ${compoundKey}: ${createErr}`);
256
- }
257
- }
258
- );
259
- shareSync.isDebug() && console.log(`created content ${compoundKey}`);
260
- const content = shareSync.wrap(body.meta.kind, doc, true) as Content;
261
- this._modelCache.set(compoundKey, content);
262
- this.updateManifestForContent(content);
263
- return content;
264
- }
265
-
266
- public async acquire(kind:ContentKind, id:ContentId, timestamp?:CompactDateTime):Promise<Model> {
267
- let model = this.load(kind, id);
268
- if (timestamp) {
269
- model = await model.acquire(timestamp);
270
- } else {
271
- model = await model.acquire();
272
- }
273
- return model;
274
- }
275
-
276
- public load(kind:ContentKind, id:ContentId):Model {
277
- // First, try getting the content from the cache
278
- const compoundKey = `${kind}/${id}`;
279
- if (this._modelCache.has(compoundKey)) {
280
- return this._modelCache.get(compoundKey);
281
- }
282
- // If not in the cache, get the model doc from the connection.
283
- // Then wrap it and put it in the cache for future reference.
284
- const doc = this._connection.get(kind, id);
285
- const model = this.wrap(kind, doc, false);
286
- this._modelCache.set(compoundKey, model);
287
- return model;
288
- }
289
-
290
- public async findAndAcquire(kind:ContentKind, query:any):Promise<Content[]> {
291
- const shareSync = this;
292
- return new Promise((resolve, reject) => {
293
- shareSync._connection.createFetchQuery(kind, query, {}, (error, results) => {
294
- if (error) {
295
- const message = `query error (${error.code}): ${error.message}`;
296
- console.log(message);
297
- reject(message);
298
- return;
299
- }
300
- const contents:Content[] = [];
301
- for (let i = 0; i < results.length; i++) {
302
- const doc:Doc = results[i];
303
- if (doc.type) {
304
- const compoundKey = `${doc.collection}/${doc.id}`;
305
- if (shareSync._modelCache.has(compoundKey)) {
306
- const content = shareSync._modelCache.get(compoundKey) as Content;
307
- content.acquire();
308
- contents.push(content);
309
- } else {
310
- // NOTE: the wrap method calls 'acquire' on the model, so we don't need to do it here.
311
- const content = shareSync.wrap(kind, doc, true) as Content;
312
- shareSync._modelCache.set(compoundKey, content);
313
- contents.push(content);
314
- }
315
- }
316
- }
317
- resolve(contents);
318
- });
319
- });
320
- }
321
-
322
- public async updateManifestForContent(content:Content):Promise<void> {
323
- // TODO: how will we handle manifest updates in a sharing scenario?
324
- // If user A owns a book, and gives B permission to edit that book, and then B makes some
325
- // changes to the book. We want B to be able to update their own manifest, but not the
326
- // manifest belonging to A. But then how will A know that the book has been updated?
327
- this.isDebug() && console.log(`updating manifest for content: ${content.compoundKey}`);
328
- content.acquire();
329
- const manifestId:ContentId = Manifest.makeManifestId(content.owner);
330
- const manifest = this.load(ContentKind.MANIFEST, manifestId) as Manifest;
331
- await manifest.acquire();
332
- manifest.update(content);
333
- manifest.release();
334
- content.release();
335
- }
336
-
337
- public forEachOfflinePendingModel(
338
- callback:(kind:ContentKind, id:ContentId) => void
339
- ):void {
340
- this._connection.forEachPendingDocCollectionId(callback);
341
- }
342
-
343
- public isDocInInventory(kind:ContentKind, ref:ContentRef, minVersion?:CompactDateTime):boolean {
344
- // Check to see if this doc is in the DurableStore inventory. And if so, does it have a `minVersion`
345
- // greater than or equal to the supplied `minVersion`. In all shaxpir models, the `minVersion` is
346
- // extracted from the `meta.updated_at.utc_time` field.
347
- return this._connection.isDocInInventory(kind as string, ref as string, minVersion as string);
348
- }
349
-
350
- public dispose(model:Model):void {
351
- if (this._disposalStrategy == ShareSyncDisposalStrategy.NOTHING) {
352
- return;
353
- }
354
- // Remove the model from the cache
355
- const compoundKey = model.compoundKey;
356
- this._modelCache.delete(compoundKey);
357
- // Dispose of the model's doc object
358
- if (this._disposalStrategy == ShareSyncDisposalStrategy.DESTROY) {
359
- model.disposeAndDestroy();
360
- } else if (this._disposalStrategy == ShareSyncDisposalStrategy.UNSUBSCRIBE) {
361
- model.disposeAndUnsubscribe();
362
- }
363
- }
364
-
365
- public onOperationError(opError:any):void {
366
- this._opErrorCallback && this._opErrorCallback(opError);
367
- }
368
-
369
- private wrap(kind:ContentKind, doc:Doc, shouldAcquire:boolean):Model {
370
- const shareSync = this;
371
- if (kind === ContentKind.MANIFEST) {
372
- return new Manifest(doc, shouldAcquire, shareSync);
373
- } else if (kind === ContentKind.USER) {
374
- return new User(doc, shouldAcquire, shareSync);
375
- } else if (kind === ContentKind.DEVICE) {
376
- return new Device(doc, shouldAcquire, shareSync);
377
- } else if (kind === ContentKind.PROFILE) {
378
- return new Profile(doc, shouldAcquire, shareSync);
379
- }
380
- throw new Error(`can't wrap content ${kind}/${doc.id}`);
381
- }
382
-
383
- }
@@ -1,50 +0,0 @@
1
- import { getPatch, Patch, PatchItem } from "fast-array-diff";
2
-
3
- export type TextEditRetainOp = number;
4
- export type TextEditInsertOp = string;
5
- export interface TextEditDeleteOp {
6
- d:number|String
7
- };
8
-
9
- export type TextEditOp = TextEditRetainOp | TextEditInsertOp | TextEditDeleteOp;
10
-
11
- export class TextEditOps {
12
- public static between(before:string, after:string):TextEditOp[] {
13
- let ops = [] as TextEditOp[];
14
- if (before.length === 0 && after.length === 0) {
15
- return ops;
16
- } else if (before.length === 0) {
17
- ops.push(after);
18
- } else if (after.length === 0) {
19
- ops.push({ d : before });
20
- } else {
21
- // First, we split the string into unicode characters. We use this method of splitting (regex with 'u' flag)
22
- // in order to split on actual unicode characters, rather than merely on bytes. Even the String.charAt()
23
- // function can't be trusted to handle multi-byte characters correctly (e.g., emojis). For more info, read here:
24
- // https://stackoverflow.com/questions/35223206/how-to-split-unicode-string-to-characters-in-javascript
25
- const beforeChars = before.match(/./ug);
26
- const afterChars = after.match(/./ug);
27
- // Create a diff-patch between these two character arrays.
28
- let patch:Patch<string> = getPatch(beforeChars, afterChars);
29
- // console.log(JSON.stringify(patch));
30
- let cursor = 0;
31
- // Iterate through the patch items, and use them to construct OT operations, in the format expected by ShareDB.
32
- for (let i = 0; i < patch.length; i++) {
33
- const item:PatchItem<string> = patch[i];
34
- const chunk = item.items.join('');
35
- if (item.oldPos > cursor) {
36
- const charsToRetain = item.oldPos - cursor;
37
- ops.push(charsToRetain);
38
- cursor += charsToRetain;
39
- }
40
- if (item.type == 'add') {
41
- ops.push(chunk);
42
- } else if (item.type == 'remove') {
43
- ops.push({ d : chunk });
44
- cursor += chunk.length;
45
- }
46
- }
47
- }
48
- return ops;
49
- }
50
- }
package/lib/repo/index.ts DELETED
@@ -1,6 +0,0 @@
1
- // created from 'create-ts-index'
2
-
3
- export * from './ConnectionListener';
4
- export * from './PermissiveJson1';
5
- export * from './ShareSync';
6
- export * from './TextEditOps';
@@ -1,5 +0,0 @@
1
- export interface Encryption {
2
- getRandomString(chars:string, length:number):string;
3
- encrypt(cleartext:string, key:string):string;
4
- decrypt(cyphertext:string, key:string):string;
5
- }