mobx-keystone
Version:
A MobX powered state management solution based on data trees with first class support for TypeScript, snapshots, patches and much more
109 lines (97 loc) • 2.49 kB
text/typescript
import type { Path } from "../parent/pathTypes"
import { failure } from "../utils"
import type { Patch } from "./Patch"
export type JsonPatch =
| JsonPatchAddOperation<any>
| JsonPatchRemoveOperation
| JsonPatchReplaceOperation<any>
export interface JsonPatchBaseOperation {
path: string
}
export interface JsonPatchAddOperation<T> extends JsonPatchBaseOperation {
op: "add"
value: T
}
export interface JsonPatchRemoveOperation extends JsonPatchBaseOperation {
op: "remove"
}
export interface JsonPatchReplaceOperation<T> extends JsonPatchBaseOperation {
op: "replace"
value: T
}
/**
* Escapes a json pointer path.
*
* @param path The raw pointer
* @return the Escaped path
*/
function escapePathComponent(path: string | number): string {
if (typeof path === "number") {
return String(path)
}
if (!(path.includes("/") || path.includes("~"))) {
return path
}
return path.replace(/~/g, "~0").replace(/\//g, "~1")
}
/**
* Unescapes a json pointer path.
*
* @param path The escaped pointer
* @return The unescaped path
*/
function unescapePathComponent(path: string): string {
return path.replace(/~1/g, "/").replace(/~0/g, "~")
}
/**
* Converts a path into a JSON pointer.
*
* @param path Path to convert.
* @returns Converted JSON pointer.
*/
export function pathToJsonPointer(path: Path): string {
if (path.length <= 0) {
return ""
}
return "/" + path.map(escapePathComponent).join("/")
}
/**
* Converts a JSON pointer into a path.
*
* @param jsonPointer JSON pointer to convert.
* @returns Converted path.
*/
export function jsonPointerToPath(jsonPointer: string): Path {
if (jsonPointer === "") {
return []
}
if (!jsonPointer.startsWith("/")) {
throw failure("a JSON pointer must start with '/' or be empty")
}
jsonPointer = jsonPointer.slice(1)
return jsonPointer.split("/").map(unescapePathComponent)
}
/**
* Convert a patch into a JSON patch.
*
* @param patch A patch.
* @returns A JSON patch.
*/
export function patchToJsonPatch(patch: Patch): JsonPatch {
return {
...patch,
path: pathToJsonPointer(patch.path),
}
}
/**
* Converts a JSON patch into a patch.
*
* @param jsonPatch A JSON patch.
* @returns A patch.
*/
export function jsonPatchToPatch(jsonPatch: JsonPatch): Patch {
return {
...jsonPatch,
path: jsonPointerToPath(jsonPatch.path),
}
}