Note: Related problem - 886. Possible Bipartition

With BFS:

var isBipartite = function (graph) {
	const adjList = graph,
		n = graph.length,
		visited = new Array(n).fill(-1),
		parent = new Array(n).fill(-1),
		distance = new Array(n).fill(-1)

	for (let v = 0; v < n; v++) {
		if (visited[v] === -1) {
			if (!bfs(v)) return false
		}
	}

	return true

	// -----------------------------------------------
	function bfs(source) {
		const queue = []
		queue.push(source)

		visited[source] = 1
		distance[source] = 0

		while (queue.length) {
			const node = queue.shift()
			for (const neighbor of adjList[node]) {
				if (visited[neighbor] == -1) {
					visited[neighbor] = 1
					parent[neighbor] = node
					distance[neighbor] = distance[node] + 1
					queue.push(neighbor)
				} else {
					// cycle
					if (parent[node] !== neighbor) {
						// not bipartite (odd length cycle found)
						// cross edges found in same layer are NOT bipartite
						if (distance[node] == distance[neighbor]) return false
					}
				}
			}
		}
		return true
	}
}

With DFS:

var isBipartite = function (graph) {
	const adjList = graph,
		n = graph.length,
		visited = new Array(n).fill(-1),
		parent = new Array(n).fill(-1),
		color = new Array(n).fill(-1)

	for (let v = 0; v < n; v++) {
		if (visited[v] === -1) {
			if (!dfs(v)) return false
		}
	}

	return true

	// -----------------------------------------------
	function dfs(source) {
		visited[source] = 1

		if (parent[source] === -1) color[source] = 0
		else color[source] = 1 - color[parent[source]]

		for (const neighbor of adjList[source]) {
			if (visited[neighbor] === -1) {
				parent[neighbor] = source
				if (!dfs(neighbor)) return false
			} else {
				if (color[source] === color[neighbor]) return false
			}
		}
		return true
	}
}

// Tests
console.log(
	isBipartite([
		[1, 2, 3],
		[0, 2],
		[0, 1, 3],
		[0, 2],
	])
)
console.log(
	isBipartite([
		[1, 2, 3],
		[0, 2],
		[0, 1, 3],
		[0, 2],
	])
)