function deserialize(canvas, data, dontClear) {
	if (data.v !== v) { alert('Wrong format'); return null; }
	if (!dontClear) { canvasClear(canvas); }

	/** @type {Map<ShapeData, ShapeElement>} */
	const shapeDataToElem = new Map();

	/** @param {ShapeData} shapeData */
	function shapeEnsure(shapeData) {
		let shapeEl = shapeDataToElem.get(shapeData);
		if (!shapeEl) {
			shapeEl = canvas[CanvasSmbl].shapeMap[shapeData.type].create(shapeData);
			canvas.append(shapeEl);
			shapeDataToElem.set(shapeData, shapeEl);
		}
		return shapeEl;
	}

	/** @param {number?} index */
	const shapeByIndex = index => shapeEnsure(/** @type {ShapeData} */(data.s[index]));

	/** @type {PathElement[]} */
	const paths = [];
	for (const shape of data.s) {
		switch (shape.type) {
			// path
			case 0: {
				/** @param {PathEndSerialized} pathEnd */
				const pathDeserialize = pathEnd => pathEnd.p
					? { data: pathEnd.p }
					: { shape: { shapeEl: shapeByIndex(pathEnd.s), connectorKey: pathEnd.k } };

				const path = canvas[CanvasSmbl].shapeMap[0].create({
					styles: /** @type {PathSerialized} */(shape).c,
					s: pathDeserialize(/** @type {PathSerialized} */(shape).s),
					e: pathDeserialize(/** @type {PathSerialized} */(shape).e)
				});
				paths.push(path);
				canvas.append(path);
				break;
			}
			default: shapeEnsure(/** @type {ShapeData} */(shape)); break;
		}
	}

	return [...shapeDataToElem.values(), ...paths];
}
@param {CanvasElement} canvas
@param {DiagramSerialized} data
@param {Boolean=} dontClear