Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

libzap

Package Overview
Dependencies
Maintainers
1
Versions
103
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

libzap - npm Package Compare versions

Comparing version 0.0.38 to 0.0.39

lib/util/stringify.d.ts

2

lib/ot/client_test.js

@@ -7,3 +7,3 @@ "use strict";

function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments)).next());
step((generator = generator.apply(thisArg, _arguments || [])).next());
});

@@ -10,0 +10,0 @@ };

@@ -7,3 +7,3 @@ "use strict";

function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments)).next());
step((generator = generator.apply(thisArg, _arguments || [])).next());
});

@@ -10,0 +10,0 @@ };

"use strict";
const assert = require("assert");
const workspace_1 = require("./workspace");
const stringify_1 = require("../util/stringify");
const otTestCases_1 = require("./otTestCases");
function orderedStringify(obj) {
return JSON.stringify(obj, Object.keys(obj).sort());
}
describe("noop", () => {

@@ -15,6 +19,6 @@ [

].forEach(({ op, want }) => {
it(`${JSON.stringify(op)}`, () => assert.equal(workspace_1.noop(op), want));
it(stringify_1.stringify `${op}`, () => assert.equal(workspace_1.noop(op), want));
});
});
describe("compose", () => {
describe("compose:", () => {
const tests = otTestCases_1.composeTests;

@@ -25,6 +29,7 @@ const testCompose = (title, t) => {

if (wantErr) {
assert.throws(() => workspace_1.compose(a, b));
assert.throws(() => workspace_1.compose(a, b), stringify_1.stringify `compose:\n a: ${a}\n b: ${b}`);
}
else {
assert.deepEqual(workspace_1.compose(a, b), want);
let got = workspace_1.compose(a, b);
assert.deepEqual(got, want, stringify_1.stringify `compose:\n a: ${a}\n b: ${b}\n got: ${got}\n want: ${want}`);
}

@@ -36,17 +41,2 @@ });

const opTypes = new Set(Object.keys(Object.assign({}, td.a, td.b, td.want)));
if (opTypes.has("copy")) {
continue;
}
if (opTypes.has("rename")) {
continue;
}
if (opTypes.has("create")) {
continue;
}
if (opTypes.has("delete")) {
continue;
}
if (opTypes.has("truncate")) {
continue;
}
if (opTypes.has("sel")) {

@@ -58,3 +48,3 @@ continue;

});
describe("transform", () => {
describe("transform:", () => {
const tests = otTestCases_1.transformTests;

@@ -69,4 +59,5 @@ const testTransform = (title, t) => {

const { a1: gotA1, b1: gotB1 } = workspace_1.transform(a, b);
assertOpsEqual("a1", gotA1, a1);
assertOpsEqual("b1", gotB1, b1);
const msg = stringify_1.stringify `transform\n a: ${a}\n b: ${b}\n`;
assertOpsEqual("a1", gotA1, a1, msg);
assertOpsEqual("b1", gotB1, b1, msg);
}

@@ -78,20 +69,2 @@ });

const opTypes = new Set(Object.keys(Object.assign({}, td.a, td.b, td.a1, td.b1)));
if (opTypes.has("save")) {
continue;
}
if (opTypes.has("copy")) {
continue;
}
if (opTypes.has("rename")) {
continue;
}
if (opTypes.has("create")) {
continue;
}
if (opTypes.has("delete")) {
continue;
}
if (opTypes.has("truncate")) {
continue;
}
if (opTypes.has("sel")) {

@@ -103,5 +76,5 @@ continue;

});
function assertOpsEqual(label, got, want) {
assert.equal(JSON.stringify(got), JSON.stringify(want), `${label}: got ${JSON.stringify(got)}, want ${JSON.stringify(want)}`);
function assertOpsEqual(label, got, want, msg = "") {
assert.equal(orderedStringify(got), orderedStringify(want), msg + stringify_1.stringify `${label} got: ${got}\n want: ${want}`);
}
//# sourceMappingURL=workspace_test.js.map
import { EditOps } from "./textDoc";
export interface WorkspaceOp {
save?: string[];
copy?: {

@@ -10,2 +9,3 @@ [file: string]: string;

};
save?: string[];
create?: string[];

@@ -39,2 +39,4 @@ delete?: string[];

export declare function from(a: WorkspaceOp): WorkspaceOp;
export declare function opExists(op: WorkspaceOp, path: string): [boolean, boolean];
export declare function opExisted(op: WorkspaceOp, path: string): [boolean, boolean];
export declare function compose(a: WorkspaceOp, b: WorkspaceOp): WorkspaceOp;

@@ -41,0 +43,0 @@ export declare function composeAll(ops: WorkspaceOp[]): WorkspaceOp;

"use strict";
const cloneDeep = require("lodash/cloneDeep");
const includes = require("lodash/includes");
const invert = require("lodash/invert");
const invertBy = require("lodash/invertBy");
const uniq = require("lodash/uniq");
const values = require("lodash/values");
const without = require("lodash/without");
const textDoc_1 = require("./textDoc");
const stringify_1 = require("../util/stringify");
function emptyArray(v) {

@@ -25,5 +33,2 @@ return v === undefined || (typeof v.length === "number" && v.length === 0);

validate(op);
if (emptyArray(op.save)) {
delete op.save;
}
if (emptyMap(op.copy)) {

@@ -35,2 +40,5 @@ delete op.copy;

}
if (emptyArray(op.save)) {
delete op.save;
}
if (emptyArray(op.create)) {

@@ -54,2 +62,14 @@ delete op.create;

}
if (op.save) {
op.save = uniq(op.save).sort();
}
if (op.create) {
op.create = uniq(op.create).sort();
}
if (op.delete) {
op.delete = uniq(op.delete).sort();
}
if (op.truncate) {
op.truncate = uniq(op.truncate).sort();
}
return op;

@@ -102,41 +122,88 @@ }

const op = {};
if (a.save) {
op.save = a.save.slice();
}
if (a.copy) {
op.copy = Object.assign({}, a.copy);
op.copy = op.copy || {};
for (const d of Object.keys(a.copy)) {
const [exists, known] = opExists(op, d);
if (known && exists) {
throw new Error(`copy to: file ${d} file already exists`);
}
op.copy[d] = a.copy[d];
}
}
if (a.rename) {
op.rename = Object.assign({}, a.rename);
return cloneDeep(a);
}
exports.from = from;
function opExists(op, path) {
let [copiedFrom, copiedTo, renamedFrom, renamedTo, created, deleted, truncated, edited, selected, savedTo] = Array(10).fill(false);
if (op.copy) {
copiedFrom = includes(values(op.copy), path);
copiedTo = includes(Object.keys(op.copy), path);
}
if (a.create) {
op.create = a.create.slice();
if (op.rename) {
renamedFrom = includes(Object.keys(op.rename), path);
renamedTo = includes(values(op.rename), path);
}
if (a.delete) {
op.delete = a.delete.slice();
if (op.create) {
created = includes(op.create, path);
}
if (a.truncate) {
op.truncate = a.truncate.slice();
if (op.delete) {
deleted = includes(op.delete, path);
}
if (a.edit) {
op.edit = Object.assign({}, a.edit);
if (op.truncate) {
truncated = includes(op.truncate, path);
}
if (a.sel) {
op.sel = Object.assign({}, a.sel);
if (op.edit) {
edited = includes(Object.keys(op.edit), path);
}
return op;
if (op.sel) {
selected = includes(Object.keys(op.sel), path);
}
if (op.save) {
if (isFilePath(path)) {
const b = fileToBufferPath(path);
savedTo = includes(op.save, b);
}
}
const exists = copiedFrom || copiedTo || renamedTo || created || truncated || edited || selected || savedTo && !(renamedFrom || deleted);
const known = copiedFrom || copiedTo || renamedFrom || renamedTo || created || deleted || truncated || savedTo || edited || selected;
return [exists, known];
}
exports.from = from;
exports.opExists = opExists;
function opExisted(op, path) {
let [copiedFrom, copiedTo, renamedFrom, renamedTo, created, deleted, truncated, edited, selected] = Array(9).fill(false);
if (op.copy) {
copiedFrom = includes(values(op.copy), path);
copiedTo = includes(Object.keys(op.copy), path);
}
if (op.rename) {
renamedFrom = includes(Object.keys(op.rename), path);
renamedTo = includes(values(op.rename), path);
}
if (op.create) {
created = includes(op.create, path);
}
if (op.delete) {
deleted = includes(op.delete, path);
}
if (op.truncate) {
truncated = includes(op.truncate, path);
}
if (op.edit) {
edited = includes(Object.keys(op.edit), path);
}
if (op.sel) {
selected = includes(Object.keys(op.sel), path);
}
const existed = copiedFrom || renamedFrom || deleted || truncated || edited || selected && !(renamedTo || copiedTo || created);
const known = copiedFrom || copiedTo || renamedFrom || renamedTo || created || deleted || truncated || edited || selected;
return [existed, known];
}
exports.opExisted = opExisted;
function compose(a, b) {
validate(a);
validate(b);
if (noop(a)) {
return b;
}
if (noop(b)) {
return a;
}
let op = from(a);
const op = from(a);
const renamedTo = (op.rename ? invert(op.rename) : {});
const composeCopy = (op, b) => {
const ab = op.copy || {};
op.copy = op.copy || {};
if (emptyMap(b.copy)) {

@@ -147,13 +214,117 @@ return;

if (b.copy.hasOwnProperty(d)) {
const s = b.copy[d];
let s = b.copy[d];
let [exists, known] = opExists(op, s);
if (known && !exists) {
throw new Error(`copy from: file ${s} does not exist`);
}
[exists, known] = opExists(op, d);
if (known && exists) {
throw new Error(`copy to: file ${d} file already exists`);
}
if (op.edit && op.edit[s]) {
op.edit[d] = op.edit[s];
}
ab[d] = s;
if (op.copy && op.copy[s]) {
s = op.copy[s];
}
if (op.rename) {
if (renamedTo[s]) {
s = renamedTo[s];
}
}
let created = false;
if (op.create && includes(op.create, s)) {
created = true;
op.create.push(d);
}
if (op.delete && includes(op.delete, d)) {
op.delete = without(op.delete, d);
}
if (op.truncate && includes(op.truncate, s)) {
op.truncate.push(d);
}
if (!created) {
op.copy[d] = s;
}
if (isFilePath(s) && isFilePath(d)) {
const sb = fileToBufferPath(s);
const db = fileToBufferPath(d);
if (op.save && op.save.indexOf(sb) !== -1) {
op.save.push(db);
op.copy[db] = sb;
delete op.copy[d];
}
}
}
}
op.copy = ab;
};
const composeRename = (op, b) => {
op.rename = op.rename || {};
if (emptyMap(b.rename)) {
return;
}
for (let s in b.rename) {
if (b.rename.hasOwnProperty(s)) {
const d = b.rename[s];
let [exists, known] = opExists(op, s);
if (known && !exists) {
throw new Error(`rename from: file ${s} does not exist`);
}
[exists, known] = opExists(op, d);
if (known && exists) {
throw new Error(`rename to: file ${d} file already exists`);
}
const o2 = s;
if (renamedTo[s]) {
const s2 = renamedTo[s];
s = s2;
}
if (op.copy && op.copy[s]) {
op.copy[d] = op.copy[s];
delete op.copy[s];
continue;
}
let created = false;
if (op.create && includes(op.create, s)) {
created = true;
op.create = without(op.create, s);
op.create.push(d);
}
if (op.delete && includes(op.delete, d)) {
op.delete = without(op.delete, d);
}
if (op.truncate && includes(op.truncate, s)) {
op.truncate = without(op.truncate, s);
op.truncate.push(d);
}
if (op.edit && op.edit[o2]) {
op.edit[d] = op.edit[o2];
delete op.edit[o2];
delete op.edit[s];
}
if (op.edit && b.edit && op.edit[b.rename[o2]]) {
op.edit[d] = textDoc_1.composeEdits(op.edit[d], b.edit[b.rename[o2]]);
delete b.edit[b.rename[o2]];
}
if (!created) {
op.rename[s] = d;
renamedTo[d] = s;
}
if (isFilePath(s)) {
const sb = fileToBufferPath(s);
const db = fileToBufferPath(d);
if (op.save && includes(op.save, sb)) {
op.save.push(db);
op.copy = op.copy || {};
op.copy[db] = sb;
op.delete = op.delete || [];
op.delete.push(s);
delete op.rename[s];
}
}
}
}
};
const composeSave = (op, b) => {
const ab = op.save || [];
op.save = op.save || [];
if (emptySave(b)) {

@@ -164,42 +335,175 @@ return;

const d = bufferToFilePath(s);
let save = true;
const [exists, known] = opExists(op, s);
if (known && !exists) {
throw new Error(`save from: file ${s} does not exist`);
}
if (op.edit && op.edit[d]) {
delete op.edit[d];
}
if (op.edit && op.edit[s]) {
op.edit = op.edit || {};
op.edit[d] = op.edit[s];
}
else if (op.edit && op.edit[d]) {
delete op.edit[d];
if (op.delete && includes(op.delete, d)) {
op.delete = without(op.delete, d);
}
if (op.create && op.create.indexOf(d) !== -1) {
op.create = op.create.filter(f => f !== d);
if (op.truncate && includes(op.truncate, d)) {
op.truncate = without(op.truncate, d);
}
if (op.delete && op.delete.indexOf(d) !== -1) {
op.delete = op.delete.filter(f => f !== d);
const truncated = Boolean(op.truncate && includes(op.truncate, s));
if (truncated) {
op.truncate.push(d);
}
if (op.copy && op.copy[s] === d) {
delete op.copy[s];
if (op.edit) {
delete op.edit[s];
if (renamedTo[d]) {
const r = renamedTo[d];
delete op.rename[r];
op.delete = op.delete || [];
op.delete.push(r);
}
if (op.copy && op.copy[s]) {
const cs = op.copy[s];
if (isFilePath(cs) && isBufferPath(s) && stripFileOrBufferPathPrefix(cs) === stripFileOrBufferPathPrefix(s)) {
delete op.copy[s];
op.save = without(op.save, s);
if (op.edit && op.edit[s]) {
op.edit[d] = op.edit[s];
delete op.edit[s];
}
continue;
}
save = false;
}
if (save) {
ab.push(s);
if (op.create && includes(op.create, d)) {
op.create = without(op.create, d);
}
if (!truncated) {
op.save.push(s);
}
}
op.save = ab;
};
const bDelete = new Set(b.delete);
const noDelete = new Set();
const composeCreate = (op, b) => {
op.create = op.create || [];
if (emptyArray(b)) {
return;
}
for (let f of b) {
const [exists, known] = opExists(op, f);
if (known && exists) {
throw new Error(`create: file ${f} already exist`);
}
if (op.delete && includes(op.delete, f)) {
op.delete = without(op.delete, f);
op.truncate = op.truncate || [];
op.truncate.push(f);
continue;
}
let [chained, renamed] = [false, false];
let o = "";
if (op.rename && op.rename[f]) {
renamed = true;
chained = renamed;
o = op.rename[f];
}
if (!chained) {
if (op.copy && op.copy[f]) {
chained = true;
o = op.copy[f];
}
}
if (chained) {
if (op.delete && includes(op.delete, o)) {
continue;
}
if (renamed) {
if (bDelete.has(o)) {
op.truncate = op.truncate || [];
op.truncate.push(f);
}
else {
op.create.push(f);
}
noDelete.add(f);
continue;
}
f = o;
}
op.create.push(f);
}
};
const composeDelete = (op, b) => {
const ab = op.delete || [];
op.delete = op.delete || [];
if (emptyArray(b)) {
return;
}
for (const f of b) {
if (op.edit && op.edit[f]) {
for (let f of b) {
const [exists, known] = opExists(op, f);
if (known && !exists) {
throw new Error(`delete: file ${f} does not exist`);
}
if (op.rename && renamedTo[f]) {
const o = renamedTo[f];
delete op.rename[o];
if (op.create && includes(op.create, f)) {
op.create = without(op.create, f);
op.truncate = op.truncate || [];
op.truncate.push(o);
}
else if (!noDelete.has(o)) {
op.delete.push(o);
}
}
else if (op.create && includes(op.create, f)) {
op.create = without(op.create, f);
}
else if (op.copy && op.copy[f]) {
delete op.copy[f];
}
else {
op.delete.push(f);
}
if (op.rename && op.rename[f]) {
f = op.rename[f];
}
if (op.edit) {
delete op.edit[f];
}
ab.push(f);
if (op.sel) {
delete op.sel[f];
}
if (isFilePath(f)) {
const sb = fileToBufferPath(f);
if (op.save && includes(op.save, sb)) {
op.save = without(op.save, sb);
}
}
}
op.delete = ab;
};
const composeTruncate = (op, b) => {
op.truncate = op.truncate || [];
if (emptyArray(b)) {
return;
}
for (let f of b) {
const [exists, known] = opExists(op, f);
if (known && !exists) {
throw new Error(`truncate: file ${f} does not exist`);
}
if (op.rename && op.rename[f]) {
f = op.rename[f];
}
op.truncate.push(f);
if (op.edit) {
delete op.edit[f];
}
if (op.sel) {
delete op.sel[f];
}
if (isFilePath(f)) {
const sb = fileToBufferPath(f);
if (op.save && includes(op.save, sb)) {
op.save = without(op.save, sb);
}
}
}
};
const composeEdit = (op, b) => {

@@ -209,9 +513,16 @@ if (emptyEdit(b)) {

}
const ab = op.edit || {};
for (const fb in b) {
if (b.hasOwnProperty(fb)) {
ab[fb] = textDoc_1.composeEdits(ab[fb] || [], b[fb]);
op.edit = op.edit || {};
for (const f of Object.keys(b)) {
const [exists, known] = opExists(op, f);
if (known && !exists) {
throw new Error(`edit: file ${f} does not exist`);
}
op.edit[f] = textDoc_1.composeEdits(op.edit[f] || [], b[f]);
if (op.create && includes(op.create, f)) {
const { ret: ret } = textDoc_1.countEdits(op.edit[f]);
if (ret !== 0) {
throw new Error(`newly created file has nonzero retain count ${f}`);
}
}
}
op.edit = ab;
};

@@ -231,10 +542,53 @@ const composeSel = (op, b) => {

composeCopy(op, b);
composeRename(op, b);
composeSave(op, b.save);
composeCreate(op, b.create);
composeDelete(op, b.delete);
composeTruncate(op, b.truncate);
composeEdit(op, b.edit);
composeSel(op, b.sel);
op.head = b.head || a.head;
if (op.copy) {
for (const d of Object.keys(op.copy)) {
const s = op.copy[d];
if (d === s) {
delete op.copy[d];
}
}
}
if (op.rename) {
for (const s of Object.keys(op.rename)) {
const d = op.rename[s];
if (s === d) {
delete op.rename[s];
}
}
}
if (op.truncate) {
const toRemove = [];
for (const f of op.truncate) {
if (op.create && includes(op.create, f)) {
toRemove.push(f);
}
}
for (const r of toRemove) {
op.truncate = without(op.truncate, r);
}
}
if (op.save) {
const toRemove = [];
for (const s of op.save) {
const d = bufferToFilePath(s);
if (op.delete && includes(op.delete, d)) {
toRemove.push(s);
}
}
for (const r of toRemove) {
op.save = without(op.save, r);
}
}
return normalize(op);
}
exports.compose = compose;
;
function composeAll(ops) {

@@ -286,2 +640,170 @@ if (!ops) {

};
const yCopyFrom = invertBy(y.copy || {});
const newFileSrc = {};
const transformCopy = (x, y, z) => {
if (!x.copy) {
x.copy = {};
}
if (!y.copy) {
y.copy = {};
}
for (const d of Object.keys(x.copy)) {
const s = x.copy[d];
if (y.copy[d] && y.copy[d] === s) {
continue;
}
else if (y.copy[d] && y.copy[d] !== s) {
throw new Error(`copy to ${s}: confilict: ${y.copy[d]}`);
}
const [srcExisted, srcKnown] = opExisted(y, s);
if (srcKnown && !srcExisted) {
throw new Error(`copy: ${s} does not exist`);
}
const [destExists, destKnown] = opExists(y, d);
if (y.rename && y.rename[s] === d) {
continue;
}
else if (destExists && destKnown) {
throw new Error(`copy: file ${d} already exists`);
}
z.copy = z.copy || {};
z.copy[d] = s;
newFileSrc[d] = s;
}
};
const transformRename = (x, y, z) => {
if (!x.rename) {
x.rename = {};
}
if (!y.rename) {
y.rename = {};
}
for (const s of Object.keys(x.rename)) {
const d = x.rename[s];
if (y.rename[d] && y.rename[d] === s) {
continue;
}
else if (y.rename[d] && y.rename[d] !== s) {
throw new Error(`rename to: ${s} conflict: ${y.rename[d]}`);
}
const [srcExisted, srcKnown] = opExisted(y, s);
if (!srcExisted && srcKnown) {
throw new Error(`rename from: ${d} does not exist`);
}
if (y.rename[s] && y.rename[s] === d) {
continue;
}
else if (y.rename[s]) {
z.copy = z.copy || {};
z.copy[d] = y.rename[s];
newFileSrc[y.rename[s]] = s;
continue;
}
const [srcExists] = opExists(y, s);
if (srcKnown && !srcExists) {
throw new Error(`rename from: ${s} does not exist`);
}
const [destExisted, destKnown] = opExisted(y, d);
const [destExists, destKnown2] = opExists(y, d);
if (y.copy && y.copy[d] && y.copy[d] === s) {
z.delete = z.delete || [];
z.delete.push(s);
continue;
}
else if (destExisted && destKnown) {
throw new Error(`rename to: ${d} already exists`);
}
else if (destExists && destKnown2) {
throw new Error(`rename to: ${d} already exists`);
}
z.rename = z.rename || {};
z.rename[s] = d;
newFileSrc[d] = s;
}
};
const transformSave = (x, y, z) => {
if (!x.save) {
x.save = [];
}
if (!y.save) {
y.save = [];
}
z.save = z.save || [];
for (const s of x.save) {
if (y.save && includes(y.save, s)) {
continue;
}
z.save.push(s);
}
};
const transformCreate = (x, y, z) => {
if (!x.create) {
x.create = [];
}
if (!y.create) {
y.create = [];
}
z.create = z.create || [];
for (const f of x.create) {
if (y.create && !includes(y.create, f)) {
const [exists, known] = opExists(y, f);
if (known && exists) {
throw new Error(`create: file ${f} already exists`);
}
z.create.push(f);
}
if (y.delete && includes(y.delete, f)) {
throw new Error(`create: conflict: y delete (file ${f})`);
}
}
};
const transformDelete = (x, y, z) => {
if (!x.delete) {
x.delete = [];
}
if (!y.delete) {
y.delete = [];
}
z.delete = z.delete || [];
for (const f of x.delete) {
if (y.delete && !includes(y.delete, f)) {
const [exists, known] = opExists(y, f);
if (known && !exists) {
throw new Error(`delete: file ${f} does not exist`);
}
z.delete.push(f);
}
if (y.create && includes(y.create, f)) {
throw new Error(`delete: conflict: y create (file ${f})`);
}
}
};
const transformTruncate = (x, y, z) => {
if (!x.truncate) {
x.truncate = [];
}
if (!y.truncate) {
y.truncate = [];
}
z.truncate = z.truncate || [];
for (const f of x.truncate) {
const ycreated = Boolean(y.create && includes(y.create, f));
const ydeleted = Boolean(y.delete && includes(y.delete, f));
const ytruncated = includes(y.truncate, f);
const sameTruncateEdit = Boolean(ytruncated && y.edit && x.edit && stringify_1.orderedStringify(y.edit[f]) === stringify_1.orderedStringify(x.edit[f]));
if (y.edit && Object.keys(y.edit[f]).length > 0 && !sameTruncateEdit) {
throw new Error(`truncate: conflict: edit and y truncate (file ${f})`);
}
if (ydeleted) {
throw new Error(`truncate: conflict: y delete (file ${f})`);
}
if (!ytruncated && !ycreated && !ydeleted && !sameTruncateEdit) {
const [exists, known] = opExists(y, f);
if (known && !exists) {
throw new Error(`truncate: ${f} does not exist`);
}
z.truncate.push(f);
}
}
};
const transformEdit = (x, y, z) => {

@@ -294,22 +816,71 @@ if (!x.edit) {

}
const ab = copyHACK(x.edit);
for (const f in x.edit) {
z.edit = z.edit || {};
for (let f in x.edit) {
if (x.edit.hasOwnProperty(f)) {
const xedit = x.edit[f];
const yedit = y.edit[f];
let edit;
if (yedit) {
edit = transformEditOps(copyHACK(x.edit[f]), copyHACK(yedit), primary);
let edit = x.edit[f];
if (y.rename && y.rename[f]) {
const [exists, known] = opExists(y, y.rename[f]);
if (known && !exists) {
throw new Error(`edit: file ${f} does not exist`);
}
}
else {
const [exists, known] = opExists(y, f);
if (known && !exists) {
throw new Error(`edit: file ${f} does not exist`);
}
}
if (yCopyFrom[f]) {
for (const d of yCopyFrom[f]) {
z.edit[d] = transformEditOps(copyHACK(x.edit[f] || []), copyHACK(y.edit[d] || []), primary);
}
}
let yf;
if (newFileSrc[f]) {
yf = newFileSrc[f];
}
else {
yf = f;
}
if (y.rename && y.rename[yf]) {
const d = y.rename[yf];
f = d;
edit = transformEditOps(copyHACK(x.edit[f] || []), copyHACK(y.edit[d] || []), primary);
}
if (y.edit[yf]) {
edit = transformEditOps(copyHACK(edit || []), copyHACK(y.edit[yf]), primary);
}
if (JSON.stringify(xedit) !== JSON.stringify(yedit)) {
if (z.edit[f] && Object.keys(z.edit[f]).length !== 0) {
throw new Error(`copy/rename edit conflict`);
}
if (edit) {
ab[f] = edit;
z.edit[f] = edit;
}
}
else {
delete ab[f];
}
}
for (let f in x.edit) {
if (isBufferPath(f)) {
const d = bufferToFilePath(f);
if (y.save && includes(y.save, f)) {
z.edit[d] = transformEditOps(copyHACK(x.edit[f] || []), copyHACK(y.edit[d] || []), primary);
}
if (x.save && includes(x.save, f)) {
z.edit[d] = y.edit[d];
}
}
}
z.edit = ab;
for (let f in x.edit) {
if (isFilePath(f)) {
const s = fileToBufferPath(f);
if (x.save && includes(x.save, s)) {
z.edit[f] = transformEditOps(copyHACK(x.edit[f] || []), copyHACK(y.edit[s] || []), primary);
const z1 = transformEditOps(copyHACK(z.edit[f] || []), copyHACK(y.edit[f] || []), primary);
z.edit[f] = textDoc_1.composeEdits(copyHACK(y.edit[f] || []), z1);
}
}
}
};

@@ -337,2 +908,8 @@ const transformSel = (x, y, z) => {

const z = {};
transformCopy(x, y, z);
transformRename(x, y, z);
transformSave(x, y, z);
transformCreate(x, y, z);
transformDelete(x, y, z);
transformTruncate(x, y, z);
transformEdit(x, y, z);

@@ -339,0 +916,0 @@ transformSel(x, y, z);

@@ -7,3 +7,3 @@ "use strict";

function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments)).next());
step((generator = generator.apply(thisArg, _arguments || [])).next());
});

@@ -10,0 +10,0 @@ };

@@ -7,3 +7,3 @@ "use strict";

function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments)).next());
step((generator = generator.apply(thisArg, _arguments || [])).next());
});

@@ -10,0 +10,0 @@ };

{
"name": "libzap",
"version": "0.0.38",
"version": "0.0.39",
"description": "JavaScript library for Zap",

@@ -23,2 +23,3 @@ "license": "none",

"diff-match-patch": "^1.0.0",
"lodash": "^4.17.2",
"uuid": "^3.0.1",

@@ -29,2 +30,3 @@ "vscode-jsonrpc": "3.0.1-alpha.7"

"@types/diff-match-patch": "^1.0.31",
"@types/lodash": "4.14.39",
"@types/mocha": "^2.2.32",

@@ -31,0 +33,0 @@ "@types/node": "^6.0.42",

@@ -461,2 +461,5 @@ // Code generated by ot/gen/gen-ts.go; DO NOT EDIT!

"want": {
"copy": {
"#f2": "#f1"
},
"save": [

@@ -466,5 +469,2 @@ "#f1",

],
"copy": {
"#f2": "#f1"
},
"edit": {

@@ -489,8 +489,8 @@ "#f1": [

"want": {
"copy": {
"#f": "/f"
},
"save": [
"#f"
],
"copy": {
"#f": "/f"
}
]
}

@@ -510,9 +510,9 @@ },

"want": {
"copy": {
"#f2": "#f1"
},
"save": [
"#f1",
"#f2"
],
"copy": {
"#f2": "#f1"
}
]
}

@@ -1507,8 +1507,8 @@ },

"a": {
"rename": {
"/f1": "/f2"
},
"save": [
"#f1"
],
"rename": {
"/f1": "/f2"
}
]
},

@@ -2128,8 +2128,8 @@ "b": {

"want": {
"copy": {
"#f2": "#f1"
},
"save": [
"#f2"
],
"copy": {
"#f2": "#f1"
},
"delete": [

@@ -2450,8 +2450,8 @@ "/f1"

"want": {
"copy": {
"/f2": "/f1"
},
"save": [
"#f1"
],
"copy": {
"/f2": "/f1"
}
]
}

@@ -2657,8 +2657,8 @@ },

"want": {
"rename": {
"/f1": "/f2"
},
"save": [
"#f1"
],
"rename": {
"/f1": "/f2"
}
]
}

@@ -2793,16 +2793,16 @@ },

"b": {
"rename": {
"/f1": "/f2"
},
"save": [
"#f1"
],
]
},
"want": {
"rename": {
"/f1": "/f2"
}
},
"want": {
},
"save": [
"#f1"
],
"rename": {
"/f1": "/f2"
},
"edit": {

@@ -3172,3 +3172,3 @@ "#f1": [

"truncate": [
"a"
"/a"
]

@@ -3178,3 +3178,3 @@ },

"truncate": [
"b"
"/b"
]

@@ -3184,4 +3184,4 @@ },

"truncate": [
"a",
"b"
"/a",
"/b"
]

@@ -3188,0 +3188,0 @@ },

import * as assert from "assert";
import { WorkspaceOp, compose, noop, transform } from "./workspace";
import { stringify } from "../util/stringify";
// Most of the test cases used here are generated from the corresponding tests
// in the ot go package. If you want to modify/add a test, do it in
// ot/ot_test_cases.go and run the generate script.
import { composeTests, transformTests } from "./otTestCases";
function orderedStringify(obj: Object): string {
return JSON.stringify(obj, Object.keys(obj).sort());
}
describe("noop", () => {

@@ -16,7 +24,7 @@ ([

] as { op: WorkspaceOp, want: boolean }[]).forEach(({op, want}) => {
it(`${JSON.stringify(op)}`, () => assert.equal(noop(op), want));
it(stringify`${op}`, () => assert.equal(noop(op), want));
});
});
describe("compose", () => {
describe("compose:", () => {
type testCase = { a: WorkspaceOp, b: WorkspaceOp, want: WorkspaceOp, wantErr: boolean | undefined, commutative: boolean | undefined };

@@ -29,5 +37,6 @@ const tests = composeTests as { [key: string]: testCase };

if (wantErr) {
assert.throws(() => compose(a, b));
assert.throws(() => compose(a, b), stringify`compose:\n a: ${a}\n b: ${b}`);
} else {
assert.deepEqual(compose(a, b), want);
let got = compose(a, b);
assert.deepEqual(got, want, stringify`compose:\n a: ${a}\n b: ${b}\n got: ${got}\n want: ${want}`);
}

@@ -42,7 +51,2 @@ });

const opTypes = new Set(Object.keys(Object.assign({}, td.a, td.b, td.want)));
if (opTypes.has("copy")) { continue; }
if (opTypes.has("rename")) { continue; }
if (opTypes.has("create")) { continue; }
if (opTypes.has("delete")) { continue; }
if (opTypes.has("truncate")) { continue; }
if (opTypes.has("sel")) { continue; } // TODO(renfred): Some sel cases are broken

@@ -54,3 +58,3 @@

describe("transform", () => {
describe("transform:", () => {
type testCase = { a: WorkspaceOp, b: WorkspaceOp, a1: WorkspaceOp, b1: WorkspaceOp, wantErr: boolean | undefined };

@@ -66,4 +70,5 @@ const tests = transformTests as { [key: string]: testCase };

const {a1: gotA1, b1: gotB1} = transform(a, b);
assertOpsEqual("a1", gotA1, a1);
assertOpsEqual("b1", gotB1, b1);
const msg = stringify`transform\n a: ${a}\n b: ${b}\n`;
assertOpsEqual("a1", gotA1, a1, msg);
assertOpsEqual("b1", gotB1, b1, msg);
}

@@ -78,8 +83,2 @@ });

const opTypes = new Set(Object.keys(Object.assign({}, td.a, td.b, td.a1, td.b1)));
if (opTypes.has("save")) { continue; }
if (opTypes.has("copy")) { continue; }
if (opTypes.has("rename")) { continue; }
if (opTypes.has("create")) { continue; }
if (opTypes.has("delete")) { continue; }
if (opTypes.has("truncate")) { continue; }
if (opTypes.has("sel")) { continue; } // TODO(renfred): Some sel cases are broken

@@ -91,4 +90,4 @@

function assertOpsEqual(label: string, got: WorkspaceOp, want: WorkspaceOp) {
assert.equal(JSON.stringify(got), JSON.stringify(want), `${label}: got ${JSON.stringify(got)}, want ${JSON.stringify(want)}`);
function assertOpsEqual(label: string, got: WorkspaceOp, want: WorkspaceOp, msg: string = "") {
assert.equal(orderedStringify(got), orderedStringify(want), msg + stringify`${label} got: ${got}\n want: ${want}`);
}

@@ -1,7 +0,16 @@

import { EditOps, composeEdits, transformEdits } from "./textDoc";
import * as cloneDeep from "lodash/cloneDeep";
import * as includes from "lodash/includes";
import * as invert from "lodash/invert";
import * as invertBy from "lodash/invertBy";
import * as uniq from "lodash/uniq";
import * as values from "lodash/values";
import * as without from "lodash/without";
import { EditOps, composeEdits, transformEdits, countEdits } from "./textDoc";
import { orderedStringify } from "../util/stringify";
export interface WorkspaceOp {
save?: string[];
copy?: { [file: string]: string };
rename?: { [file: string]: string };
save?: string[];
create?: string[];

@@ -49,5 +58,5 @@ delete?: string[];

validate(op);
if (emptyArray(op.save)) { delete op.save; }
if (emptyMap(op.copy)) { delete op.copy; }
if (emptyMap(op.rename)) { delete op.rename; }
if (emptyArray(op.save)) { delete op.save; }
if (emptyArray(op.create)) { delete op.create; }

@@ -59,2 +68,8 @@ if (emptyArray(op.delete)) { delete op.delete; }

if (op.head === undefined) { delete op.head; }
if (op.save) { op.save = uniq(op.save).sort(); }
if (op.create) { op.create = uniq(op.create).sort(); }
if (op.delete) { op.delete = uniq(op.delete).sort(); }
if (op.truncate) { op.truncate = uniq(op.truncate).sort(); }
return op;

@@ -110,29 +125,83 @@ }

const op: WorkspaceOp = {};
if (a.save) {
op.save = a.save.slice();
}
if (a.copy) {
op.copy = Object.assign({}, a.copy);
op.copy = op.copy || {};
for (const d of Object.keys(a.copy)) {
const [exists, known] = opExists(op, d);
if (known && exists) {
throw new Error(`copy to: file ${d} file already exists`);
}
op.copy[d] = a.copy[d];
}
}
if (a.rename) {
op.rename = Object.assign({}, a.rename);
return cloneDeep(a);
}
export function opExists(op: WorkspaceOp, path: string): [boolean, boolean] {
let [copiedFrom, copiedTo, renamedFrom, renamedTo, created, deleted, truncated, edited, selected, savedTo] = Array(10).fill(false);
if (op.copy) {
copiedFrom = includes(values(op.copy), path);
copiedTo = includes(Object.keys(op.copy), path);
}
if (a.create) {
op.create = a.create.slice();
if (op.rename) {
renamedFrom = includes(Object.keys(op.rename), path);
renamedTo = includes(values(op.rename), path);
}
if (a.delete) {
op.delete = a.delete.slice();
if (op.create) {
created = includes(op.create, path);
}
if (a.truncate) {
op.truncate = a.truncate.slice();
if (op.delete) {
deleted = includes(op.delete, path);
}
if (a.edit) {
op.edit = Object.assign({}, a.edit);
if (op.truncate) {
truncated = includes(op.truncate, path);
}
if (a.sel) {
op.sel = Object.assign({}, a.sel);
if (op.edit) {
edited = includes(Object.keys(op.edit), path);
}
return op;
if (op.sel) {
selected = includes(Object.keys(op.sel), path);
}
if (op.save) {
if (isFilePath(path)) {
const b = fileToBufferPath(path);
savedTo = includes(op.save, b);
}
}
const exists = copiedFrom || copiedTo || renamedTo || created || truncated || edited || selected || savedTo && !(renamedFrom || deleted);
const known = copiedFrom || copiedTo || renamedFrom || renamedTo || created || deleted || truncated || savedTo || edited || selected;
return [exists, known];
}
export function opExisted(op: WorkspaceOp, path: string): [boolean, boolean] {
let [copiedFrom, copiedTo, renamedFrom, renamedTo, created, deleted, truncated, edited, selected] = Array(9).fill(false);
if (op.copy) {
copiedFrom = includes(values(op.copy), path);
copiedTo = includes(Object.keys(op.copy), path);
}
if (op.rename) {
renamedFrom = includes(Object.keys(op.rename), path);
renamedTo = includes(values(op.rename), path);
}
if (op.create) {
created = includes(op.create, path);
}
if (op.delete) {
deleted = includes(op.delete, path);
}
if (op.truncate) {
truncated = includes(op.truncate, path);
}
if (op.edit) {
edited = includes(Object.keys(op.edit), path);
}
if (op.sel) {
selected = includes(Object.keys(op.sel), path);
}
const existed = copiedFrom || renamedFrom || deleted || truncated || edited || selected && !(renamedTo || copiedTo || created);
const known = copiedFrom || copiedTo || renamedFrom || renamedTo || created || deleted || truncated || edited || selected;
return [existed, known];
}
// TODO(sqs): how do we need to handle Sel? do we need to adjust it based on edits?

@@ -143,69 +212,326 @@

validate(b);
if (noop(a)) { return b; }
if (noop(b)) { return a; }
let op: WorkspaceOp = from(a);
const op: WorkspaceOp = from(a);
const renamedTo = (op.rename ? invert(op.rename) : {}) as { [key: string]: string };
// tslint:disable-next-line no-shadowed-variable
const composeCopy = (op: WorkspaceOp, b: WorkspaceOp): void => {
const ab = op.copy || {};
op.copy = op.copy || {};
if (emptyMap(b.copy)) { return; }
for (const d in b.copy) {
if (b.copy.hasOwnProperty(d)) {
const s = b.copy[d];
let s = b.copy[d];
let [exists, known] = opExists(op, s);
if (known && !exists) {
throw new Error(`copy from: file ${s} does not exist`);
}
[exists, known] = opExists(op, d);
if (known && exists) {
throw new Error(`copy to: file ${d} file already exists`);
}
if (op.edit && op.edit[s]) {
op.edit[d] = op.edit[s];
}
ab[d] = s;
if (op.copy && op.copy[s]) {
s = op.copy[s];
}
if (op.rename) {
if (renamedTo[s]) {
s = renamedTo[s];
}
}
let created = false;
if (op.create && includes(op.create, s)) {
created = true;
op.create.push(d);
}
if (op.delete && includes(op.delete, d)) {
op.delete = without(op.delete, d);
}
if (op.truncate && includes(op.truncate, s)) {
op.truncate.push(d);
}
if (!created) {
op.copy[d] = s;
// TODO(renfred) update copyFrom here
}
if (isFilePath(s) && isFilePath(d)) {
const sb = fileToBufferPath(s);
const db = fileToBufferPath(d);
if (op.save && op.save.indexOf(sb) !== -1) {
op.save.push(db);
op.copy[db] = sb;
delete op.copy[d];
}
}
}
}
op.copy = ab;
};
// tslint:disable-next-line no-shadowed-variable
const composeRename = (op: WorkspaceOp, b: WorkspaceOp): void => {
op.rename = op.rename || {};
if (emptyMap(b.rename)) { return; }
for (let s in b.rename) {
if (b.rename.hasOwnProperty(s)) {
const d = b.rename[s];
let [exists, known] = opExists(op, s);
if (known && !exists) {
throw new Error(`rename from: file ${s} does not exist`);
}
[exists, known] = opExists(op, d);
if (known && exists) {
throw new Error(`rename to: file ${d} file already exists`);
}
const o2 = s;
if (renamedTo[s]) {
const s2 = renamedTo[s];
s = s2;
}
if (op.copy && op.copy[s]) {
op.copy[d] = op.copy[s];
delete op.copy[s];
continue;
}
let created = false;
if (op.create && includes(op.create, s)) {
created = true;
op.create = without(op.create, s);
op.create.push(d);
}
if (op.delete && includes(op.delete, d)) {
op.delete = without(op.delete, d);
}
if (op.truncate && includes(op.truncate, s)) {
op.truncate = without(op.truncate, s);
op.truncate.push(d);
}
if (op.edit && op.edit[o2]) {
op.edit[d] = op.edit[o2];
delete op.edit[o2];
delete op.edit[s];
}
if (op.edit && b.edit && op.edit[b.rename[o2]]) {
op.edit[d] = composeEdits(op.edit[d], b.edit[b.rename[o2]]);
delete b.edit[b.rename[o2]];
}
// TODO selections
if (!created) {
op.rename[s] = d;
renamedTo[d] = s;
}
if (isFilePath(s)) {
const sb = fileToBufferPath(s);
const db = fileToBufferPath(d);
if (op.save && includes(op.save, sb)) {
op.save.push(db);
op.copy = op.copy || {};
op.copy[db] = sb;
op.delete = op.delete || [];
op.delete.push(s);
delete op.rename[s];
}
}
}
}
};
// tslint:disable-next-line no-shadowed-variable
const composeSave = (op: WorkspaceOp, b: FileSaves | undefined): void => {
const ab = op.save || [];
op.save = op.save || [];
if (emptySave(b)) { return; }
for (const s of b) {
const d = bufferToFilePath(s);
let save = true;
if (op.edit && op.edit[s]) {
op.edit = op.edit || {};
op.edit[d] = op.edit![s];
} else if (op.edit && op.edit[d]) {
const [exists, known] = opExists(op, s);
if (known && !exists) {
throw new Error(`save from: file ${s} does not exist`);
}
if (op.edit && op.edit[d]) {
delete op.edit[d];
}
if (op.create && op.create.indexOf(d) !== -1) { op.create = op.create.filter(f => f !== d); }
if (op.delete && op.delete.indexOf(d) !== -1) { op.delete = op.delete.filter(f => f !== d); }
if (op.copy && op.copy[s] === d) {
delete op.copy[s];
if (op.edit) { delete op.edit[s]; }
save = false;
if (op.edit && op.edit[s]) {
op.edit[d] = op.edit[s];
}
if (save) { ab.push(s); }
if (op.delete && includes(op.delete, d)) {
op.delete = without(op.delete, d);
}
if (op.truncate && includes(op.truncate, d)) {
op.truncate = without(op.truncate, d);
}
const truncated = Boolean(op.truncate && includes(op.truncate, s));
if (truncated) {
op.truncate!.push(d);
}
if (renamedTo[d]) {
const r = renamedTo[d];
delete op.rename![r];
op.delete = op.delete || [];
op.delete.push(r);
}
if (op.copy && op.copy[s]) {
const cs = op.copy[s];
if (isFilePath(cs) && isBufferPath(s) && stripFileOrBufferPathPrefix(cs) === stripFileOrBufferPathPrefix(s)) {
delete op.copy[s];
op.save = without(op.save, s);
if (op.edit && op.edit[s]) {
op.edit[d] = op.edit[s];
delete op.edit[s];
}
continue;
}
}
if (op.create && includes(op.create, d)) {
op.create = without(op.create, d);
}
if (!truncated) {
op.save.push(s);
}
}
op.save = ab;
};
const bDelete = new Set<string>(b.delete);
const noDelete = new Set<string>();
// tslint:disable-next-line no-shadowed-variable
const composeCreate = (op: WorkspaceOp, b: string[] | undefined): void => {
op.create = op.create || [];
if (emptyArray(b)) { return; }
for (let f of b) {
const [exists, known] = opExists(op, f);
if (known && exists) {
throw new Error(`create: file ${f} already exist`);
}
if (op.delete && includes(op.delete, f)) {
op.delete = without(op.delete, f);
op.truncate = op.truncate || [];
op.truncate.push(f);
continue;
}
let [chained, renamed] = [false, false];
let o = "";
if (op.rename && op.rename[f]) {
renamed = true;
chained = renamed;
o = op.rename[f];
}
if (!chained) {
if (op.copy && op.copy[f]) {
chained = true;
o = op.copy[f];
}
}
if (chained) {
if (op.delete && includes(op.delete, o)) {
continue;
}
if (renamed) {
if (bDelete.has(o)) {
op.truncate = op.truncate || [];
op.truncate.push(f);
} else {
op.create.push(f);
}
noDelete.add(f);
continue;
}
f = o;
}
op.create.push(f);
}
};
// tslint:disable-next-line no-shadowed-variable
const composeDelete = (op: WorkspaceOp, b: string[] | undefined): void => {
const ab = op.delete || [];
op.delete = op.delete || [];
if (emptyArray(b)) { return; }
for (const f of b) {
if (op.edit && op.edit[f]) { delete op.edit[f]; }
ab.push(f);
for (let f of b) {
const [exists, known] = opExists(op, f);
if (known && !exists) {
throw new Error(`delete: file ${f} does not exist`);
}
if (op.rename && renamedTo[f]) {
const o = renamedTo[f];
delete op.rename[o];
if (op.create && includes(op.create, f)) {
op.create = without(op.create, f);
op.truncate = op.truncate || [];
op.truncate.push(o);
} else if (!noDelete.has(o)) {
op.delete.push(o);
}
} else if (op.create && includes(op.create, f)) {
op.create = without(op.create, f);
} else if (op.copy && op.copy[f]) {
delete op.copy[f];
} else {
op.delete.push(f);
}
if (op.rename && op.rename[f]) {
f = op.rename[f];
}
if (op.edit) { delete op.edit[f]; }
if (op.sel) { delete op.sel[f]; }
if (isFilePath(f)) {
const sb = fileToBufferPath(f);
if (op.save && includes(op.save, sb)) {
op.save = without(op.save, sb);
}
}
}
op.delete = ab;
};
// tslint:disable-next-line no-shadowed-variable
const composeTruncate = (op: WorkspaceOp, b: string[] | undefined): void => {
op.truncate = op.truncate || [];
if (emptyArray(b)) { return; }
for (let f of b) {
const [exists, known] = opExists(op, f);
if (known && !exists) {
throw new Error(`truncate: file ${f} does not exist`);
}
if (op.rename && op.rename[f]) {
f = op.rename[f];
}
op.truncate.push(f);
if (op.edit) { delete op.edit[f]; }
if (op.sel) { delete op.sel[f]; }
if (isFilePath(f)) {
const sb = fileToBufferPath(f);
if (op.save && includes(op.save, sb)) {
op.save = without(op.save, sb);
}
}
}
};
// tslint:disable-next-line no-shadowed-variable
const composeEdit = (op: WorkspaceOp, b: FileEdits | undefined): void => {
if (emptyEdit(b)) { return; }
const ab = op.edit || {};
for (const fb in b) {
if (b.hasOwnProperty(fb)) {
ab[fb] = composeEdits(ab[fb] || [], b[fb]);
op.edit = op.edit || {};
for (const f of Object.keys(b)) {
const [exists, known] = opExists(op, f);
if (known && !exists) {
throw new Error(`edit: file ${f} does not exist`);
}
op.edit[f] = composeEdits(op.edit[f] || [], b[f]);
if (op.create && includes(op.create, f)) {
const {ret: ret} = countEdits(op.edit[f]);
if (ret !== 0) {
throw new Error(`newly created file has nonzero retain count ${f}`);
}
}
}
op.edit = ab;
};

@@ -226,9 +552,53 @@

composeCopy(op, b);
composeRename(op, b);
composeSave(op, b.save);
composeCreate(op, b.create);
composeDelete(op, b.delete);
composeTruncate(op, b.truncate);
composeEdit(op, b.edit);
composeSel(op, b.sel);
op.head = b.head || a.head;
// Simplify.
if (op.copy) {
for (const d of Object.keys(op.copy)) {
const s = op.copy[d];
if (d === s) {
delete op.copy[d];
}
}
}
if (op.rename) {
for (const s of Object.keys(op.rename)) {
const d = op.rename[s];
if (s === d) {
delete op.rename[s];
}
}
}
if (op.truncate) {
const toRemove: string[] = [];
for (const f of op.truncate) {
if (op.create && includes(op.create, f)) {
toRemove.push(f);
}
}
for (const r of toRemove) {
op.truncate = without(op.truncate, r);
}
}
if (op.save) {
const toRemove: string[] = [];
for (const s of op.save) {
const d = bufferToFilePath(s);
if (op.delete && includes(op.delete, d)) {
toRemove.push(s);
}
}
for (const r of toRemove) {
op.save = without(op.save, r);
}
}
return normalize(op);
}
};

@@ -285,3 +655,162 @@ export function composeAll(ops: WorkspaceOp[]): WorkspaceOp {

const yCopyFrom = invertBy(y.copy || {}) as { [key: string]: string[] };
const newFileSrc = {} as { [key: string]: string };
// tslint:disable-next-line no-shadowed-variable
const transformCopy = (x: WorkspaceOp, y: WorkspaceOp, z: WorkspaceOp): void => {
if (!x.copy) { x.copy = {}; }
if (!y.copy) { y.copy = {}; }
for (const d of Object.keys(x.copy)) {
const s = x.copy[d];
if (y.copy[d] && y.copy[d] === s) {
continue;
} else if (y.copy[d] && y.copy[d] !== s) {
throw new Error(`copy to ${s}: confilict: ${y.copy[d]}`);
}
const [srcExisted, srcKnown] = opExisted(y, s);
if (srcKnown && !srcExisted) {
throw new Error(`copy: ${s} does not exist`);
}
const [destExists, destKnown] = opExists(y, d);
if (y.rename && y.rename[s] === d) {
continue;
} else if (destExists && destKnown) {
throw new Error(`copy: file ${d} already exists`);
}
z.copy = z.copy || {};
z.copy[d] = s;
newFileSrc[d] = s;
}
};
// tslint:disable-next-line no-shadowed-variable
const transformRename = (x: WorkspaceOp, y: WorkspaceOp, z: WorkspaceOp): void => {
if (!x.rename) { x.rename = {}; }
if (!y.rename) { y.rename = {}; }
for (const s of Object.keys(x.rename)) {
const d = x.rename[s];
if (y.rename[d] && y.rename[d] === s) {
continue;
} else if (y.rename[d] && y.rename[d] !== s) {
throw new Error(`rename to: ${s} conflict: ${y.rename[d]}`);
}
const [srcExisted, srcKnown] = opExisted(y, s);
if (!srcExisted && srcKnown) {
throw new Error(`rename from: ${d} does not exist`);
}
if (y.rename[s] && y.rename[s] === d) {
continue;
} else if (y.rename[s]) {
z.copy = z.copy || {};
z.copy[d] = y.rename[s];
newFileSrc[y.rename[s]] = s;
continue;
}
const [srcExists] = opExists(y, s);
if (srcKnown && !srcExists) {
throw new Error(`rename from: ${s} does not exist`);
}
const [destExisted, destKnown] = opExisted(y, d);
const [destExists, destKnown2] = opExists(y, d);
if (y.copy && y.copy[d] && y.copy[d] === s) {
z.delete = z.delete || [];
z.delete.push(s);
continue;
} else if (destExisted && destKnown) {
throw new Error(`rename to: ${d} already exists`);
} else if (destExists && destKnown2) {
throw new Error(`rename to: ${d} already exists`);
}
z.rename = z.rename || {};
z.rename[s] = d;
newFileSrc[d] = s;
}
};
// tslint:disable-next-line no-shadowed-variable
const transformSave = (x: WorkspaceOp, y: WorkspaceOp, z: WorkspaceOp): void => {
if (!x.save) { x.save = []; }
if (!y.save) { y.save = []; }
z.save = z.save || [];
for (const s of x.save) {
if (y.save && includes(y.save, s)) {
continue;
}
z.save.push(s);
}
};
// tslint:disable-next-line no-shadowed-variable
const transformCreate = (x: WorkspaceOp, y: WorkspaceOp, z: WorkspaceOp): void => {
if (!x.create) { x.create = []; }
if (!y.create) { y.create = []; }
z.create = z.create || [];
for (const f of x.create) {
if (y.create && !includes(y.create, f)) {
const [exists, known] = opExists(y, f);
if (known && exists) {
throw new Error(`create: file ${f} already exists`);
}
z.create.push(f);
}
if (y.delete && includes(y.delete, f)) {
throw new Error(`create: conflict: y delete (file ${f})`);
}
}
};
// tslint:disable-next-line no-shadowed-variable
const transformDelete = (x: WorkspaceOp, y: WorkspaceOp, z: WorkspaceOp): void => {
if (!x.delete) { x.delete = []; }
if (!y.delete) { y.delete = []; }
z.delete = z.delete || [];
for (const f of x.delete) {
if (y.delete && !includes(y.delete, f)) {
const [exists, known] = opExists(y, f);
if (known && !exists) {
throw new Error(`delete: file ${f} does not exist`);
}
z.delete.push(f);
}
if (y.create && includes(y.create, f)) {
throw new Error(`delete: conflict: y create (file ${f})`);
}
}
};
// tslint:disable-next-line no-shadowed-variable
const transformTruncate = (x: WorkspaceOp, y: WorkspaceOp, z: WorkspaceOp): void => {
if (!x.truncate) { x.truncate = []; }
if (!y.truncate) { y.truncate = []; }
z.truncate = z.truncate || [];
for (const f of x.truncate) {
const ycreated = Boolean(y.create && includes(y.create, f));
const ydeleted = Boolean(y.delete && includes(y.delete, f));
const ytruncated = includes(y.truncate, f);
const sameTruncateEdit = Boolean(ytruncated && y.edit && x.edit && orderedStringify(y.edit[f]) === orderedStringify(x.edit[f]));
if (y.edit && Object.keys(y.edit[f]).length > 0 && !sameTruncateEdit) {
throw new Error(`truncate: conflict: edit and y truncate (file ${f})`);
}
if (ydeleted) {
throw new Error(`truncate: conflict: y delete (file ${f})`);
}
if (!ytruncated && !ycreated && !ydeleted && !sameTruncateEdit) {
const [exists, known] = opExists(y, f);
if (known && !exists) {
throw new Error(`truncate: ${f} does not exist`);
}
z.truncate.push(f);
}
}
};
// tslint:disable-next-line no-shadowed-variable
const transformEdit = (x: WorkspaceOp, y: WorkspaceOp, z: WorkspaceOp): void => {

@@ -291,19 +820,73 @@ if (!x.edit) { x.edit = {}; }

const ab: FileEdits = copyHACK(x.edit);
for (const f in x.edit) {
z.edit = z.edit || {};
for (let f in x.edit) {
if (x.edit.hasOwnProperty(f)) {
const xedit = x.edit[f];
const yedit = y.edit[f];
let edit: EditOps | undefined;
if (yedit) {
edit = transformEditOps(copyHACK(x.edit[f]), copyHACK(yedit), primary);
let edit = x.edit[f];
if (y.rename && y.rename[f]) {
const [exists, known] = opExists(y, y.rename[f]);
if (known && !exists) {
throw new Error(`edit: file ${f} does not exist`);
}
} else {
const [exists, known] = opExists(y, f);
if (known && !exists) {
throw new Error(`edit: file ${f} does not exist`);
}
}
if (JSON.stringify(xedit) !== JSON.stringify(yedit)) {
if (edit) { ab[f] = edit!; }
if (yCopyFrom[f]) {
for (const d of yCopyFrom[f]) {
z.edit[d] = transformEditOps(copyHACK(x.edit[f] || []), copyHACK(y.edit[d] || []), primary);
}
}
let yf: string;
if (newFileSrc[f]) {
yf = newFileSrc[f];
} else {
delete ab[f];
yf = f;
}
if (y.rename && y.rename[yf]) {
const d = y.rename[yf];
f = d;
edit = transformEditOps(copyHACK(x.edit[f] || []), copyHACK(y.edit[d] || []), primary);
}
if (y.edit[yf]) {
edit = transformEditOps(copyHACK(edit || []), copyHACK(y.edit[yf]), primary);
}
if (JSON.stringify(xedit) !== JSON.stringify(yedit)) {
if (z.edit[f] && Object.keys(z.edit[f]).length !== 0) {
throw new Error(`copy/rename edit conflict`);
}
if (edit) { z.edit[f] = edit!; }
}
}
}
z.edit = ab;
for (let f in x.edit) {
if (isBufferPath(f)) {
const d = bufferToFilePath(f);
if (y.save && includes(y.save, f)) {
z.edit[d] = transformEditOps(copyHACK(x.edit[f] || []), copyHACK(y.edit[d] || []), primary);
}
if (x.save && includes(x.save, f)) {
z.edit[d] = y.edit[d];
}
}
}
for (let f in x.edit) {
if (isFilePath(f)) {
const s = fileToBufferPath(f);
if (x.save && includes(x.save, s)) {
z.edit[f] = transformEditOps(copyHACK(x.edit[f] || []), copyHACK(y.edit[s] || []), primary);
const z1 = transformEditOps(copyHACK(z.edit[f] || []), copyHACK(y.edit[f] || []), primary);
z.edit[f] = composeEdits(copyHACK(y.edit[f] || []), z1);
}
}
}
};

@@ -330,2 +913,8 @@

const z: WorkspaceOp = {};
transformCopy(x, y, z);
transformRename(x, y, z);
transformSave(x, y, z);
transformCreate(x, y, z);
transformDelete(x, y, z);
transformTruncate(x, y, z);
transformEdit(x, y, z);

@@ -332,0 +921,0 @@ transformSel(x, y, z);

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

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc