@cocreate/crdt
Advanced tools
Comparing version 1.18.28 to 1.19.0
@@ -7,4 +7,4 @@ module.exports = { | ||
{ | ||
"collection": "files", | ||
"document": { | ||
"array": "files", | ||
"object": { | ||
"_id": "6019e6794eea0817df303b10", | ||
@@ -11,0 +11,0 @@ "name": "index.html", |
{ | ||
"name": "@cocreate/crdt", | ||
"version": "1.18.28", | ||
"version": "1.19.0", | ||
"description": "CoCreate crdt", | ||
"author": "CoCreate LLC", | ||
"license": "SEE LICENSE", | ||
"license": "AGPL-3.0", | ||
"publishConfig": { | ||
@@ -12,3 +12,3 @@ "access": "public" | ||
"start": "npx webpack --config webpack.config.js", | ||
"build": "NODE_ENV=production npx webpack --config webpack.config.js", | ||
"build": "npx webpack --mode=production --config webpack.config.js", | ||
"dev": "npx webpack --config webpack.config.js --watch", | ||
@@ -44,7 +44,7 @@ "postinstall": "node -e \"const { execSync } = require('child_process'); try { execSync('coc --version', { stdio: 'ignore' }); } catch (error) { try { execSync('npm install -g @cocreate/cli', { stdio: 'inherit' }); console.log('Installed \"@cocreate/cli\" globally.'); } catch (error) { console.error('Failed to install \"@cocreate/cli\" globally:', error); } }\"" | ||
"dependencies": { | ||
"@cocreate/crud-client": "^1.21.23", | ||
"@cocreate/local-storage": "^1.7.20", | ||
"@cocreate/message-client": "^1.3.28", | ||
"@cocreate/utils": "^1.21.15" | ||
"@cocreate/crud-client": "^1.21.24", | ||
"@cocreate/local-storage": "^1.7.21", | ||
"@cocreate/message-client": "^1.3.29", | ||
"@cocreate/utils": "^1.21.16" | ||
} | ||
} |
765
src/index.js
@@ -0,1 +1,23 @@ | ||
/******************************************************************************** | ||
* Copyright (C) 2023 CoCreate and Contributors. | ||
* | ||
* This program is free software: you can redistribute it and/or modify | ||
* it under the terms of the GNU Affero General Public License as published | ||
* by the Free Software Foundation, either version 3 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU Affero General Public License for more details. | ||
* | ||
* You should have received a copy of the GNU Affero General Public License | ||
* along with this program. If not, see <https://www.gnu.org/licenses/>. | ||
********************************************************************************/ | ||
// Commercial Licensing Information: | ||
// For commercial use of this software without the copyleft provisions of the AGPLv3, | ||
// you must obtain a commercial license from CoCreate LLC. | ||
// For details, visit <https://cocreate.app/licenses/> or contact us at sales@cocreate.app. | ||
/*globals config, atob, btoa, localStorage, CustomEvent*/ | ||
@@ -12,264 +34,266 @@ import crud from '@cocreate/crud-client'; | ||
function init(data){ | ||
getText(data).then(value => { | ||
data.value = value; | ||
data.start = 0; | ||
data['clientId'] = clientId; | ||
localChange(data); | ||
}); | ||
function init(data) { | ||
getText(data).then(value => { | ||
data.value = value; | ||
data.start = 0; | ||
data['clientId'] = clientId; | ||
localChange(data); | ||
}); | ||
} | ||
async function getDoc(data) { | ||
try { | ||
if (['_id', 'organization_id'].includes(data.name)) | ||
return | ||
let docName = getDocName(data); | ||
let doc = docs.get(docName); | ||
if (!doc) { | ||
docs.set(docName, new Map()); | ||
doc = docs.get(docName); | ||
} | ||
if (!doc.has('changeLog')) { | ||
let changeLog = []; | ||
doc.set('undoLog', new Map()) | ||
doc.set('redoLog', new Map()) | ||
if (data.read != 'false') { | ||
if (!data.newDocument) { | ||
let response = await crud.readDocument({ | ||
collection: "crdt-transactions", | ||
filter: { | ||
query: [{ | ||
name: 'docName', | ||
operator: "$eq", | ||
value: docName | ||
}] | ||
} | ||
}); | ||
if (response.document && response.document[0] && response.document[0].changeLog) { | ||
changeLog = response.document[0].changeLog; | ||
} | ||
} | ||
doc.set('changeLog', changeLog); | ||
await generateText(data, true); | ||
} | ||
} | ||
else if (!doc.has('text')){ | ||
await generateText(data, false); | ||
} | ||
return doc; | ||
} | ||
catch (e) { | ||
console.log('Invalid param', e); | ||
} | ||
try { | ||
if (['_id', 'organization_id'].includes(data.key)) | ||
return | ||
let docName = getDocName(data); | ||
let doc = docs.get(docName); | ||
if (!doc) { | ||
docs.set(docName, new Map()); | ||
doc = docs.get(docName); | ||
} | ||
if (!doc.has('changeLog')) { | ||
let changeLog = []; | ||
doc.set('undoLog', new Map()) | ||
doc.set('redoLog', new Map()) | ||
if (data.read != 'false') { | ||
if (!data.newObject) { | ||
let response = await crud.send({ | ||
method: "read.object", | ||
array: "crdt-transactions", | ||
filter: { | ||
query: [{ | ||
key: 'docName', | ||
operator: "$eq", | ||
value: docName | ||
}] | ||
} | ||
}); | ||
if (response.object && response.object[0] && response.object[0].changeLog) { | ||
changeLog = response.object[0].changeLog; | ||
} | ||
} | ||
doc.set('changeLog', changeLog); | ||
await generateText(data, true); | ||
} | ||
} | ||
else if (!doc.has('text')) { | ||
await generateText(data, false); | ||
} | ||
return doc; | ||
} | ||
catch (e) { | ||
console.log('Invalid param', e); | ||
} | ||
} | ||
String.prototype.customSplice = function (index, absIndex, string) { | ||
return this.slice(0, index) + string+ this.slice(index + Math.abs(absIndex)); | ||
return this.slice(0, index) + string + this.slice(index + Math.abs(absIndex)); | ||
}; | ||
async function generateText(data, flag) { | ||
try { | ||
let doc = docs.get(getDocName(data)) | ||
try { | ||
let doc = docs.get(getDocName(data)) | ||
let string = ''; | ||
let changeLog = doc.get('changeLog'); | ||
for (let change of changeLog) { | ||
if (change || change !== null ) { | ||
string = string.customSplice(change.start, change.length, change.value); | ||
} | ||
} | ||
if (string === '' && data.read !== 'false') { | ||
string = await checkDb(data, flag); | ||
} | ||
doc.set('text', string); | ||
return; | ||
} | ||
catch (e) { | ||
console.error(e); | ||
} | ||
let string = ''; | ||
let changeLog = doc.get('changeLog'); | ||
for (let change of changeLog) { | ||
if (change || change !== null) { | ||
string = string.customSplice(change.start, change.length, change.value); | ||
} | ||
} | ||
if (string === '' && data.read !== 'false') { | ||
string = await checkDb(data, flag); | ||
} | ||
doc.set('text', string); | ||
return; | ||
} | ||
catch (e) { | ||
console.error(e); | ||
} | ||
} | ||
async function checkDb(data, flag) { | ||
let { collection, document_id, name } = data; | ||
if (checkedDb.get(`${collection}${document_id}${name}`)) return; | ||
checkedDb.set(`${collection}${document_id}${name}`, true); | ||
let { array, object, key } = data; | ||
if (checkedDb.get(`${array}${object}${key}`)) return; | ||
checkedDb.set(`${array}${object}${key}`, true); | ||
let string = '' | ||
if (data.newDocument) | ||
string = data.newDocument | ||
else { | ||
let response = await crud.readDocument({ collection, document: {_id: document_id, name}}); | ||
string = crud.getValueFromObject(response.document[0], name); | ||
} | ||
if (string && typeof string !== 'string') | ||
string = "" | ||
if (string && typeof string === 'string' && flag != false) { | ||
data.value = string; | ||
data.start = 0; | ||
data.clientId = clientId; | ||
insertChange(data); | ||
} | ||
return string || ''; | ||
let string = '' | ||
if (data.newObject) | ||
string = data.newObject | ||
else { | ||
let response = await crud.send({ method: 'read.object', array, object: { _id: object, key } }); | ||
string = crud.getValueFromObject(response.object[0], key); | ||
} | ||
if (string && typeof string !== 'string') | ||
string = "" | ||
if (string && typeof string === 'string' && flag != false) { | ||
data.value = string; | ||
data.start = 0; | ||
data.clientId = clientId; | ||
insertChange(data); | ||
} | ||
return string || ''; | ||
} | ||
function insertChange(data, flag) { | ||
let docName = getDocName(data); | ||
let doc = docs.get(docName); | ||
let changeLog = doc.get('changeLog'); | ||
if (!changeLog) | ||
return | ||
let docName = getDocName(data); | ||
let doc = docs.get(docName); | ||
let changeLog = doc.get('changeLog'); | ||
if (!changeLog) | ||
return | ||
let type = 'insert'; | ||
if (data.start == undefined) return; | ||
if (!data.value) | ||
type = 'delete'; | ||
let change = { | ||
datetime: data.datetime || new Date().toISOString(), | ||
value: data.value || '', | ||
start: data.start, | ||
end: data.end, | ||
length: data.length || 0, | ||
clientId: data.clientId || clientId, | ||
user_id: data.user_id || localStorage.getItem("user_id"), | ||
type | ||
}; | ||
let lastChange = changeLog[changeLog.length - 1]; | ||
if (lastChange && change.datetime && lastChange.datetime){ | ||
if (change.datetime < lastChange.datetime){ | ||
// TODO: insert change at index position and update start and end postions for every element after insert | ||
console.log('requires changeLog rebuild'); | ||
} | ||
if (flag != 'replace') { | ||
if (lastChange && change.start == lastChange.start) { | ||
let date1 = new Date(lastChange.datetime); | ||
let date2 = new Date(change.datetime); | ||
let diff = date2.getTime() - date1.getTime(); | ||
if (diff < 500) { | ||
if (change.value.length == 1) { | ||
change.start = lastChange.start + lastChange.value.length; | ||
data.start = change.start; | ||
} | ||
if (change.length == 1) { | ||
change.start = lastChange.start - lastChange.length; | ||
data.start = change.start; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
if (!change || change == null ) { | ||
console.log('null change') | ||
return | ||
} | ||
let string = doc.get('text') || ''; | ||
if (change.length > 0) | ||
change.removedValue = string.substring(change.start, change.length); | ||
if (flag == 'replace') { | ||
// TODO get current string and create new changeLog array then push new change | ||
changeLog = [change]; | ||
doc.set('changeLog', changeLog) | ||
} else { | ||
changeLog.push(change); | ||
} | ||
let type = 'insert'; | ||
if (data.start == undefined) return; | ||
if (!data.value) | ||
type = 'delete'; | ||
doc.set('text', string.customSplice(change.start, change.length, change.value)); | ||
string = doc.get('text'); | ||
if (!data.clientId){ | ||
data['datetime'] = change.datetime; | ||
data['clientId'] = change.clientId; | ||
broadcastChange(data); | ||
localChange(data, string); | ||
} | ||
else | ||
localChange(data, string); | ||
if (data.clientId == clientId && data.save != "false") | ||
persistChange(data); | ||
let change = { | ||
datetime: data.datetime || new Date().toISOString(), | ||
value: data.value || '', | ||
start: data.start, | ||
end: data.end, | ||
length: data.length || 0, | ||
clientId: data.clientId || clientId, | ||
user_id: data.user_id || localStorage.getItem("user_id"), | ||
type | ||
}; | ||
let lastChange = changeLog[changeLog.length - 1]; | ||
if (lastChange && change.datetime && lastChange.datetime) { | ||
if (change.datetime < lastChange.datetime) { | ||
// TODO: insert change at index position and update start and end postions for every element after insert | ||
console.log('requires changeLog rebuild'); | ||
} | ||
if (flag != 'replace') { | ||
if (lastChange && change.start == lastChange.start) { | ||
let date1 = new Date(lastChange.datetime); | ||
let date2 = new Date(change.datetime); | ||
let diff = date2.getTime() - date1.getTime(); | ||
if (diff < 500) { | ||
if (change.value.length == 1) { | ||
change.start = lastChange.start + lastChange.value.length; | ||
data.start = change.start; | ||
} | ||
if (change.length == 1) { | ||
change.start = lastChange.start - lastChange.length; | ||
data.start = change.start; | ||
} | ||
} | ||
} | ||
} | ||
} | ||
if (!change || change == null) { | ||
console.log('null change') | ||
return | ||
} | ||
let string = doc.get('text') || ''; | ||
if (change.length > 0) | ||
change.removedValue = string.substring(change.start, change.length); | ||
if (flag == 'replace') { | ||
// TODO get current string and create new changeLog array then push new change | ||
changeLog = [change]; | ||
doc.set('changeLog', changeLog) | ||
} else { | ||
changeLog.push(change); | ||
} | ||
doc.set('text', string.customSplice(change.start, change.length, change.value)); | ||
string = doc.get('text'); | ||
if (!data.clientId) { | ||
data['datetime'] = change.datetime; | ||
data['clientId'] = change.clientId; | ||
broadcastChange(data); | ||
localChange(data, string); | ||
} | ||
else | ||
localChange(data, string); | ||
if (data.clientId == clientId && data.save != "false") | ||
persistChange(data); | ||
} | ||
function broadcastChange(data){ | ||
message.send({ | ||
room: "", | ||
broadcastSender: 'false', | ||
broadcastBrowser: 'once', | ||
message: "crdt", | ||
data | ||
}); | ||
function broadcastChange(data) { | ||
message.send({ | ||
room: "", | ||
broadcastSender: 'false', | ||
broadcastBrowser: 'once', | ||
message: "crdt", | ||
data | ||
}); | ||
} | ||
function localChange(data, string) { | ||
const localChange = new CustomEvent('cocreate-crdt-update', { | ||
detail: { ...data, string }, | ||
}); | ||
window.dispatchEvent(localChange); | ||
const localChange = new CustomEvent('cocreate-crdt-update', { | ||
detail: { ...data, string }, | ||
}); | ||
window.dispatchEvent(localChange); | ||
} | ||
function persistChange(data) { | ||
let docName = getDocName(data); | ||
let doc = docs.get(docName); | ||
let changeLog = doc.get('changeLog'); | ||
let text = doc.get('text'); | ||
let Data = { | ||
collection: 'crdt-transactions', | ||
document: { | ||
_id: data.document_id, | ||
docName, | ||
changeLog, | ||
text, | ||
crud: { | ||
collection: data.collection, | ||
document_id: data.document_id, | ||
name: data.name | ||
} | ||
}, | ||
upsert: true, | ||
namespace: data.namespace, | ||
room: data.room, | ||
broadcast: data.broadcast, | ||
broadcastSender: data.broadcastSender, | ||
metadata: 'crdt-change' | ||
} | ||
let docName = getDocName(data); | ||
let doc = docs.get(docName); | ||
let changeLog = doc.get('changeLog'); | ||
let text = doc.get('text'); | ||
let Data = { | ||
method: 'update.object', | ||
array: 'crdt-transactions', | ||
object: { | ||
_id: data.object, | ||
docName, | ||
changeLog, | ||
text, | ||
crud: { | ||
array: data.array, | ||
object: data.object, | ||
key: data.key | ||
} | ||
}, | ||
upsert: true, | ||
namespace: data.namespace, | ||
room: data.room, | ||
broadcast: data.broadcast, | ||
broadcastSender: data.broadcastSender, | ||
metadata: 'crdt-change' | ||
} | ||
crud.updateDocument(Data); | ||
crud.send(Data); | ||
} | ||
message.listen('crdt', function(response) { | ||
let data = response.data | ||
let docName = getDocName(data); | ||
let doc = docs.get(docName); | ||
message.listen('crdt', function (response) { | ||
let data = response.data | ||
let docName = getDocName(data); | ||
let doc = docs.get(docName); | ||
if (doc){ | ||
if (data.clientId !== clientId){ | ||
insertChange(data); | ||
} | ||
} | ||
if (doc) { | ||
if (data.clientId !== clientId) { | ||
insertChange(data); | ||
} | ||
} | ||
}); | ||
crud.listen('sync', function(data) { | ||
if (data.collection.includes('crdt-transactions')) { | ||
if (data.document && data.document[0]) { | ||
let Data = data.document[0]; | ||
let docName = Data.docName; | ||
let doc = docs.get(docName); | ||
if (doc && Data.crud) { | ||
Data.crud.value = Data.text | ||
Data.crud.start = 0 | ||
Data.crud.length = doc.get('text').length | ||
crud.listen('sync', function (data) { | ||
if (data.array.includes('crdt-transactions')) { | ||
if (data.object && data.object[0]) { | ||
let Data = data.object[0]; | ||
let docName = Data.docName; | ||
let doc = docs.get(docName); | ||
if (doc && Data.crud) { | ||
Data.crud.value = Data.text | ||
Data.crud.start = 0 | ||
Data.crud.length = doc.get('text').length | ||
doc.set('changeLog', Data.changeLog) | ||
doc.set('text', Data.text) | ||
// TODO: compare modified dates to check if arrays need to merged and orderd by date or if we just use server | ||
localChange(Data.crud, Data.text) | ||
console.log('crdtSync') | ||
} | ||
} | ||
} | ||
doc.set('changeLog', Data.changeLog) | ||
doc.set('text', Data.text) | ||
// TODO: compare modified dates to check if arrays need to merged and orderd by date or if we just use server | ||
localChange(Data.crud, Data.text) | ||
console.log('crdtSync') | ||
} | ||
} | ||
} | ||
}); | ||
@@ -280,22 +304,22 @@ | ||
crdt.getText({ | ||
collection: 'modules', | ||
document_id: '5e4802ce3ed96d38e71fc7e5', | ||
name: 'name' | ||
array: 'modules', | ||
object: '5e4802ce3ed96d38e71fc7e5', | ||
key: 'name' | ||
}) | ||
*/ | ||
async function getText(data) { | ||
try { | ||
let doc = await getDoc(data); | ||
if (doc) { | ||
let value = doc.get('text') | ||
return value; | ||
} | ||
else { | ||
console.log('undefined') | ||
} | ||
} | ||
catch (e) { | ||
console.error(e); | ||
return ""; | ||
} | ||
try { | ||
let doc = await getDoc(data); | ||
if (doc) { | ||
let value = doc.get('text') | ||
return value; | ||
} | ||
else { | ||
console.log('undefined') | ||
} | ||
} | ||
catch (e) { | ||
console.error(e); | ||
return ""; | ||
} | ||
} | ||
@@ -306,28 +330,28 @@ | ||
crdt.replaceText({ | ||
collection: "module", | ||
document_id: "", | ||
name: "", | ||
value: "", | ||
crud: true | false, | ||
element: dom_object, | ||
metadata: "xxxx" | ||
array: "module", | ||
object: "", | ||
key: "", | ||
value: "", | ||
crud: true | false, | ||
element: dom_object, | ||
metadata: "xxxx" | ||
}) | ||
*/ | ||
async function replaceText(data) { | ||
try { | ||
let doc = await getDoc(data); | ||
if (doc) { | ||
let oldValue = await getText(data); | ||
if (oldValue) | ||
data.length = oldValue.length; | ||
else | ||
data.length = 0; | ||
try { | ||
let doc = await getDoc(data); | ||
if (doc) { | ||
let oldValue = await getText(data); | ||
if (oldValue) | ||
data.length = oldValue.length; | ||
else | ||
data.length = 0; | ||
data.start = 0; | ||
updateText(data, 'replace'); | ||
} | ||
} | ||
catch (e) { | ||
console.error(e); | ||
} | ||
data.start = 0; | ||
updateText(data, 'replace'); | ||
} | ||
} | ||
catch (e) { | ||
console.error(e); | ||
} | ||
} | ||
@@ -337,120 +361,121 @@ | ||
crdt.updateText({ | ||
collection: 'module_activities', | ||
document_id: '5e4802ce3ed96d38e71fc7e5', | ||
name: 'name', | ||
value: 'T', | ||
start: '8', | ||
attributes: {bold: true} | ||
length: 2, // length is used to define charcters that will be deleted | ||
array: 'module_activities', | ||
object: '5e4802ce3ed96d38e71fc7e5', | ||
key: 'name', | ||
value: 'T', | ||
start: '8', | ||
attributes: {bold: true} | ||
length: 2, // length is used to define charcters that will be deleted | ||
}) | ||
*/ | ||
async function updateText(data, flag) { | ||
let doc = await getDoc(data); | ||
if (doc) { | ||
insertChange(data, flag); | ||
if (data.crud != 'false' && data.save != 'false') { | ||
let wholestring = await getText(data); | ||
crud.updateDocument({ | ||
collection: data.collection, | ||
document: { | ||
_id: data.document_id, | ||
[data.name]: wholestring | ||
}, | ||
upsert: data.upsert, | ||
namespace: data.namespace, | ||
room: data.room, | ||
broadcast: data.broadcast, | ||
broadcastSender: data.broadcastSender, | ||
metadata: 'crdt-updateDocument' | ||
}); | ||
} | ||
} | ||
let doc = await getDoc(data); | ||
if (doc) { | ||
insertChange(data, flag); | ||
if (data.crud != 'false' && data.save != 'false') { | ||
let wholestring = await getText(data); | ||
crud.send({ | ||
method: 'update.object', | ||
array: data.array, | ||
object: { | ||
_id: data.object, | ||
[data.key]: wholestring | ||
}, | ||
upsert: data.upsert, | ||
namespace: data.namespace, | ||
room: data.room, | ||
broadcast: data.broadcast, | ||
broadcastSender: data.broadcastSender, | ||
metadata: 'crdt-updateobject' | ||
}); | ||
} | ||
} | ||
} | ||
function createChange(data, change){ | ||
if (change.value && change.length == 0) { | ||
change.length = change.value.length | ||
change.removedValue = change.value | ||
change.value = ''; | ||
change.type = 'delete'; | ||
} else { | ||
change.value = change.removedValue | ||
change.length = 0; | ||
change.type = 'insert'; | ||
} | ||
data = {...data, ...change}; | ||
delete data.clientId | ||
delete data.datetime | ||
delete change.clientId | ||
delete change.datetime | ||
return {data, change} | ||
function createChange(data, change) { | ||
if (change.value && change.length == 0) { | ||
change.length = change.value.length | ||
change.removedValue = change.value | ||
change.value = ''; | ||
change.type = 'delete'; | ||
} else { | ||
change.value = change.removedValue | ||
change.length = 0; | ||
change.type = 'insert'; | ||
} | ||
data = { ...data, ...change }; | ||
delete data.clientId | ||
delete data.datetime | ||
delete change.clientId | ||
delete change.datetime | ||
return { data, change } | ||
} | ||
function undoText(data){ | ||
let docName = getDocName(data); | ||
let doc = docs.get(docName); | ||
let changeLog = doc.get('changeLog'); | ||
let undoLog = doc.get('undoLog') | ||
function undoText(data) { | ||
let docName = getDocName(data); | ||
let doc = docs.get(docName); | ||
let changeLog = doc.get('changeLog'); | ||
let undoLog = doc.get('undoLog') | ||
for (let index = changeLog.length - 1; index >= 0; index--) { | ||
let change = Object.assign({}, changeLog[index]); | ||
if (change && change.clientId == clientId){ | ||
let log = undoLog.get(index) | ||
if (!log) { | ||
if (log != 'undo') { | ||
undoLog.set(index, 'undo') | ||
change.index = changeLog.length += 1; | ||
let updated = createChange(data, change); | ||
undoLog.set(updated.change.index, updated.change) | ||
updateText(updated.data) | ||
return | ||
} | ||
} | ||
} | ||
} | ||
for (let index = changeLog.length - 1; index >= 0; index--) { | ||
let change = Object.assign({}, changeLog[index]); | ||
if (change && change.clientId == clientId) { | ||
let log = undoLog.get(index) | ||
if (!log) { | ||
if (log != 'undo') { | ||
undoLog.set(index, 'undo') | ||
change.index = changeLog.length += 1; | ||
let updated = createChange(data, change); | ||
undoLog.set(updated.change.index, updated.change) | ||
updateText(updated.data) | ||
return | ||
} | ||
} | ||
} | ||
} | ||
} | ||
function redoText(data){ | ||
let docName = getDocName(data); | ||
let doc = docs.get(docName); | ||
let redoLog = doc.get('redoLog') | ||
let undoLog = Array.from(doc.get('undoLog').values()); | ||
function redoText(data) { | ||
let docName = getDocName(data); | ||
let doc = docs.get(docName); | ||
let redoLog = doc.get('redoLog') | ||
let undoLog = Array.from(doc.get('undoLog').values()); | ||
for (let index = undoLog.length - 1; index >= 0; index--) { | ||
let change = Object.assign({}, undoLog[index]); | ||
if (change && change != 'undo') { | ||
let log = redoLog.get(change.index) | ||
for (let index = undoLog.length - 1; index >= 0; index--) { | ||
let change = Object.assign({}, undoLog[index]); | ||
if (change && change != 'undo') { | ||
let log = redoLog.get(change.index) | ||
if (!log) { | ||
if (log != 'redo') { | ||
let updated = createChange(data, change); | ||
redoLog.set(updated.change.index, updated.change) | ||
updateText(updated.data) | ||
return | ||
} | ||
} | ||
} | ||
} | ||
if (!log) { | ||
if (log != 'redo') { | ||
let updated = createChange(data, change); | ||
redoLog.set(updated.change.index, updated.change) | ||
updateText(updated.data) | ||
return | ||
} | ||
} | ||
} | ||
} | ||
} | ||
async function viewVersion(data) { | ||
try { | ||
let docName = getDocName(data); | ||
let doc = docs.get(docName); | ||
let changeLog = doc.get('changeLog'); | ||
let string = ''; | ||
try { | ||
let docName = getDocName(data); | ||
let doc = docs.get(docName); | ||
let changeLog = doc.get('changeLog'); | ||
let string = ''; | ||
let log = changeLog.slice(0, data.version) | ||
for (let change of log) { | ||
if (change || change !== null ) { | ||
string = string.customSplice(change.start, change.length, change.value); | ||
} | ||
} | ||
return { ...data, ...string } | ||
} | ||
catch (e) { | ||
console.error(e); | ||
} | ||
let log = changeLog.slice(0, data.version) | ||
for (let change of log) { | ||
if (change || change !== null) { | ||
string = string.customSplice(change.start, change.length, change.value); | ||
} | ||
} | ||
return { ...data, ...string } | ||
} | ||
catch (e) { | ||
console.error(e); | ||
} | ||
} | ||
@@ -472,5 +497,5 @@ | ||
function getDocName(data) { | ||
return `${data.collection}${data.document_id}${data.name}`; | ||
return `${data.array}${data.object}${data.key}`; | ||
} | ||
export default { init, getText, updateText, replaceText, undoText, redoText, viewVersion }; |
const path = require("path") | ||
const TerserPlugin = require("terser-webpack-plugin") | ||
const MiniCssExtractPlugin = require("mini-css-extract-plugin") | ||
let isProduction = process.env.NODE_ENV === "production" | ||
const { CleanWebpackPlugin } = require("clean-webpack-plugin") | ||
module.exports = { | ||
entry: { | ||
"CoCreate-crdt": "./src/index.js", | ||
}, | ||
output: { | ||
path: path.resolve(__dirname, "dist"), | ||
filename: isProduction ? "[name].min.js" : "[name].js", | ||
libraryTarget: "umd", | ||
libraryExport: "default", | ||
library: ["CoCreate", "crdt"], | ||
globalObject: "this", | ||
}, | ||
module.exports = (env, argv) => { | ||
let isProduction = false | ||
if (argv.mode === 'production') | ||
isProduction = true | ||
plugins: [ | ||
new CleanWebpackPlugin(), | ||
new MiniCssExtractPlugin({ | ||
filename: "[name].css", | ||
}), | ||
], | ||
// Default mode for Webpack is production. | ||
// Depending on mode Webpack will apply different things | ||
// on final bundle. For now we don't need production's JavaScript | ||
// minifying and other thing so let's set mode to development | ||
mode: isProduction ? "production" : "development", | ||
module: { | ||
rules: [ | ||
{ | ||
test: /.js$/, | ||
exclude: /(node_modules)/, | ||
use: { | ||
loader: "babel-loader", | ||
options: { | ||
plugins: ["@babel/plugin-transform-modules-commonjs"], | ||
}, | ||
const config = { | ||
entry: { | ||
"CoCreate-crdt": "./src/index.js", | ||
}, | ||
}, | ||
{ | ||
test: /.css$/i, | ||
use: [ | ||
{ loader: "style-loader", options: { injectType: "linkTag" } }, | ||
"file-loader", | ||
output: { | ||
path: path.resolve(__dirname, "dist"), | ||
filename: isProduction ? "[name].min.js" : "[name].js", | ||
libraryTarget: "umd", | ||
libraryExport: "default", | ||
library: ["CoCreate", "crdt"], | ||
globalObject: "this", | ||
}, | ||
plugins: [ | ||
new CleanWebpackPlugin(), | ||
new MiniCssExtractPlugin({ | ||
filename: "[name].css", | ||
}), | ||
], | ||
}, | ||
], | ||
}, | ||
// Default mode for Webpack is production. | ||
// Depending on mode Webpack will apply different things | ||
// on final bundle. For now we don't need production's JavaScript | ||
// minifying and other thing so let's set mode to development | ||
mode: isProduction ? "production" : "development", | ||
module: { | ||
rules: [ | ||
{ | ||
test: /.js$/, | ||
exclude: /(node_modules)/, | ||
use: { | ||
loader: "babel-loader", | ||
options: { | ||
plugins: ["@babel/plugin-transform-modules-commonjs"], | ||
}, | ||
}, | ||
}, | ||
{ | ||
test: /.css$/i, | ||
use: [ | ||
{ loader: "style-loader", options: { injectType: "linkTag" } }, | ||
"file-loader", | ||
], | ||
}, | ||
], | ||
}, | ||
// add source map | ||
...(isProduction ? {} : { devtool: "eval-source-map" }), | ||
// add source map | ||
...(isProduction ? {} : { devtool: "eval-source-map" }), | ||
optimization: { | ||
minimize: true, | ||
minimizer: [ | ||
new TerserPlugin({ | ||
extractComments: true, | ||
// cache: true, | ||
parallel: true, | ||
// sourceMap: true, // Must be set to true if using source-maps in production | ||
terserOptions: { | ||
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions | ||
// extractComments: 'all', | ||
compress: { | ||
drop_console: true, | ||
}, | ||
optimization: { | ||
minimize: true, | ||
minimizer: [ | ||
new TerserPlugin({ | ||
extractComments: true, | ||
// cache: true, | ||
parallel: true, | ||
// sourceMap: true, // Must be set to true if using source-maps in production | ||
terserOptions: { | ||
// https://github.com/webpack-contrib/terser-webpack-plugin#terseroptions | ||
// extractComments: 'all', | ||
compress: { | ||
drop_console: true, | ||
}, | ||
}, | ||
}), | ||
], | ||
splitChunks: { | ||
chunks: "all", | ||
minSize: 200, | ||
// maxSize: 99999, | ||
//minChunks: 1, | ||
cacheGroups: { | ||
defaultVendors: false, | ||
}, | ||
}, | ||
}, | ||
}), | ||
], | ||
splitChunks: { | ||
chunks: "all", | ||
minSize: 200, | ||
// maxSize: 99999, | ||
//minChunks: 1, | ||
cacheGroups: { | ||
defaultVendors: false, | ||
}, | ||
}, | ||
}, | ||
} | ||
} | ||
return config | ||
} |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Misc. License Issues
License(Experimental) A package's licensing information has fine-grained problems.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
157912
0
572
0