From 6c3079786f0b06073d4227a9fe3815645933e70a Mon Sep 17 00:00:00 2001 From: Matt Hill Date: Mon, 3 Oct 2022 10:56:50 -0600 Subject: [PATCH] handle escaping and honor root pointer (#52) * handle escaping and honor root pointer * Update client/lib/json-patch-lib.ts * Update client/lib/json-patch-lib.ts Co-authored-by: Lucy C <12953208+elvece@users.noreply.github.com> --- client/lib/json-patch-lib.ts | 34 ++++++++++++++++++++++++++++------ client/lib/patch-db.ts | 9 +++++---- 2 files changed, 33 insertions(+), 10 deletions(-) diff --git a/client/lib/json-patch-lib.ts b/client/lib/json-patch-lib.ts index 8748b89..7330260 100644 --- a/client/lib/json-patch-lib.ts +++ b/client/lib/json-patch-lib.ts @@ -25,12 +25,12 @@ export type Operation = export function getValueByPointer>( data: T, - pointer: string, + path: string, ): any { - if (pointer === '/') return data + if (!path) return data try { - return jsonPathToKeyArray(pointer).reduce((acc, next) => acc[next], data) + return arrayFromPath(path).reduce((acc, next) => acc[next], data) } catch (e) { return undefined } @@ -40,11 +40,33 @@ export function applyOperation( doc: DBCache>, { path, op, value }: Operation & { value?: T }, ) { - doc.data = recursiveApply(doc.data, jsonPathToKeyArray(path), op, value) + doc.data = recursiveApply(doc.data, arrayFromPath(path), op, value) } -export function jsonPathToKeyArray(path: string): string[] { - return path.split('/').slice(1) +export function arrayFromPath(path: string): string[] { + return path + .split('/') + .slice(1) + .map(p => + // order matters, always replace "~1" first + p.replace(new RegExp('~1', 'g'), '/').replace(new RegExp('~0', 'g'), '~'), + ) +} + +export function pathFromArray(args: Array): string { + if (!args.length) return '' + + return ( + '/' + + args + .map(a => + String(a) + // do not change order, "~" needs to be replaced first + .replace(new RegExp('~', 'g'), '~0') + .replace(new RegExp('/', 'g'), '~1'), + ) + .join('/') + ) } function recursiveApply | any[]>( diff --git a/client/lib/patch-db.ts b/client/lib/patch-db.ts index 636522a..75bb3dc 100644 --- a/client/lib/patch-db.ts +++ b/client/lib/patch-db.ts @@ -10,8 +10,9 @@ import { } from 'rxjs' import { applyOperation, + arrayFromPath, getValueByPointer, - jsonPathToKeyArray, + pathFromArray, } from './json-patch-lib' export class PatchDB { @@ -133,12 +134,12 @@ export class PatchDB { filter(({ sequence }) => !!sequence), take(1), switchMap(({ data }) => { - const path = args.length ? `/${args.join('/')}` : '' + const path = pathFromArray(args) if (!this.watchedNodes[path]) { const value = getValueByPointer(data, path) this.watchedNodes[path] = { subject: new BehaviorSubject(value), - pathArr: jsonPathToKeyArray(path), + pathArr: arrayFromPath(path), } } return this.watchedNodes[path].subject @@ -183,7 +184,7 @@ export class PatchDB { } private updateWatchedNodes(revisionPath: string, data: T): void { - const r = jsonPathToKeyArray(revisionPath) + const r = arrayFromPath(revisionPath) Object.entries(this.watchedNodes).forEach(([path, { pathArr }]) => { if (startsWith(pathArr, r) || startsWith(r, pathArr)) { this.updateWatchedNode(path, data)