Security News
GitHub Removes Malicious Pull Requests Targeting Open Source Repositories
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Format for representing rich text documents and changes.
Operator 表示一个修改。每个修改可能是三种类型:insert
, remove
和 retain
。其中 insert
表示插入,remove
表示删除,retain
表示保留(用来跳过或者修改属性)。
Delta 是一组修改的集合,用来表示对一篇文档的修改。当集合中所有的修改都为 insert
时,此 Delta 即可表示文档本身。如下所示的 Delta 表示一篇内容为“Hello World”的文档:
const doc = new Delta([
new TextOperator({ action: 'insert', data: 'Hello ' }),
new TextOperator({ action: 'insert', data: 'World', attributes: { bold: true } })
]);
其中 Delta 包含两个方法:compose
和 transform
。其中 compose
用来合并两个 Delta:
const doc = new Delta([
new TextOperator({ action: 'insert', data: 'Hello ' }),
new TextOperator({ action: 'insert', data: 'World', attributes: { bold: true } })
]);
const change = new Delta([
new TextOperator({ action: 'retain', data: 6 }),
new TextOperator({ action: 'insert', data: 'Tom' }),
new TextOperator({ action: 'remove', data: 5 })
]);
const updatedDoc = doc.compose(change);
// updatedDoc 的结果为:
new Delta([
new TextOperator({ action: 'insert', data: 'Hello Tom' })
]);
而 transform
则用于操作变基。如对一篇文档,A 先做了修改并提交到服务器,而 B 也在同一时刻对文档做了修改并提交到服务器。此时服务器先收到 A,后收到 B,且 A 和 B 都是对同一版本做的修改。为了合并这两个操作,需要变换 B 为 B' 使得 A.compose(B') === B.compose(A')
,这个变换过程就是通过 transform
实现的,即 A.compose(A.transform(B)) === B.compose(B.transform(A))
。举例而言,还是上面的文档:
const doc = new Delta([
new TextOperator({ action: 'insert', data: 'Hello ' }),
new TextOperator({ action: 'insert', data: 'World', attributes: { bold: true } })
]);
此时 A 进行了操作,在 Hello 后面加个逗号:
const A = new Delta([
new TextOperator({ action: 'retain', data: 5 }),
new TextOperator({ action: 'insert', data: ',' })
]);
同时 B 进行了操作,把 World 换成 Tom:
const B = new Delta([
new TextOperator({ action: 'retain', data: 6 }),
new TextOperator({ action: 'insert', data: 'Tom' }),
new TextOperator({ action: 'remove', data: 5 })
]);
则 A.transform(B)
的结果为:
const AB = new Delta([
new TextOperator({ action: 'retain', data: 7 }),
new TextOperator({ action: 'insert', data: 'Tom' }),
new TextOperator({ action: 'remove', data: 5 })
]);
则 B.transform(A)
的结果为:
const BA = new Delta([
new TextOperator({ action: 'retain', data: 5 }),
new TextOperator({ action: 'insert', data: ',' })
]);
而后,A.compose(AB)
和 B.compose(BA)
都为:
new Delta([
new TextOperator({ action: 'retain', data: 5 }),
new TextOperator({ action: 'insert', data: ',' }),
new TextOperator({ action: 'retain', data: 1 }),
new TextOperator({ action: 'insert', data: 'Tom' }),
new TextOperator({ action: 'remove', data: 5 })
]);
有种特殊情况是,A 和 B 同时在同一位置插入了内容,这时就要确定谁的内容放在前面,为此 transform
接受第二个参数,表示操作优先级。公式为 A.compose(A.transform(B, true)) === B.compose(B.transform(A, false))
。为了统一起见,服务端先收到的 Delta 优先。
npm install richdoc
import { Delta, TableOperator, Operator, Operator, TextOperator } from 'richdoc';
举例来说,对于一篇只有一个 3x3 表格的文档,其中单元格 A1 有“Hello World”几个字,可以表示为:
const doc = new Delta([
new TableOperator({
action: 'insert',
data: {
rows: new Delta([
new Operator({ action: 'insert', data: 3 })
]),
cols: new Delta([
new Operator({ action: 'insert', data: 1 }),
new Operator({ action: 'insert', data: 1, attributes: { width: 50 } }),
new Operator({ action: 'insert', data: 1 })
]),
cells: {
A1: new CellOperator({
action: 'insert',
data: new Delta([
new TextOperator({ action: 'insert', data: 'Hello ' }),
new TextOperator({ action: 'insert', data: 'World', attributes: { bold: true } })
])
})
}
}
})
]);
Delta 可以序列化成字符串方便传输:
import { pack, unpack } from 'richdoc';
const packed = pack(delta);
const unpacked = unpack(packed);
为了方便叙述,这里定义两个伪函数:T(A, B) = A.transform(B)
, A + B = A.compose(B)
。可以得出:A + T(A, B) = B + T(B, A)
。
客户端保存有三个 Delta,A, X 和 Y,其中:
当发生下列情况时,此三个 Delta 会发生变化:
用户对文档进行了修改,产生 Delta E(根据定义,显然 E 是基于 Y 的修改)。客户端此时需要更新 Y,使得 Y <- Y + E。
当客户端要将本地修改 Y 发给服务端时,必须保证 X 为空(见下条情况)。此时客户端需要进行下列操作:
将 Y 发送给服务器
令 X <- Y
设 Y 为空 Delta
客户端收到服务端的确认。
当服务端收到客户端的修改时(即 Y),服务端会向客户端发送 ACK 响应来确认。此时客户端需要进行下列操作:
之后每 500ms 客户端再次将本地修改 Y 提交给服务端,从而形成循环。
当客户端收到服务端发送来的其他客户端的修改 B 时(显然这些修改是基于 A 的),客户端执行如下操作:
除了单元测试外,可以通过执行 npm run evolution 启动演化测试。程序会自动生成随机文档并不断演化文档,通过如下三个公式测试代码的正确性:
a === a.compose(b).compose(a.invert(b))
a.compose(a.transform(b)) === b.compose(b.transform(a))
pack(a) === unpack(a)
FAQs
Format for representing rich text documents and changes
The npm package richdoc receives a total of 8 weekly downloads. As such, richdoc popularity was classified as not popular.
We found that richdoc demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 2 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
GitHub removed 27 malicious pull requests attempting to inject harmful code across multiple open source repositories, in another round of low-effort attacks.
Security News
RubyGems.org has added a new "maintainer" role that allows for publishing new versions of gems. This new permission type is aimed at improving security for gem owners and the service overall.
Security News
Node.js will be enforcing stricter semver-major PR policies a month before major releases to enhance stability and ensure reliable release candidates.