Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
197 changes: 44 additions & 153 deletions js/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 2 additions & 3 deletions js/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "object-basin",
"version": "2.1.0",
"version": "2.2.0",
"description": "JavaScript/TypeScript library to stream updates to an object.",
"license": "MIT",
"repository": {
Expand Down Expand Up @@ -29,11 +29,10 @@
},
"dependencies": {
"fast-json-patch": "^3.1.1",
"jsonpath": "^1.1.1"
"jsonpath-plus": "^10.4.0"
},
"devDependencies": {
"@types/chai": "^4.3.4",
"@types/jsonpath": "^0.2.0",
"@types/mocha": "^10.0.1",
"chai": "^4.3.7",
"mocha": "^10.2.0",
Expand Down
45 changes: 36 additions & 9 deletions js/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import jsonpatch, { Operation } from 'fast-json-patch'
import jp from 'jsonpath'
import { JSONPath } from 'jsonpath-plus'

// Export to help dependencies because this is used in our interface.
export { Operation } from 'fast-json-patch'
Expand Down Expand Up @@ -101,13 +101,12 @@ export class Basin<T> {
delete cursor.d
}

const expressions = jp.parse(cursor.jsonPath!)
for (const expression of expressions) {
if (expression.expression.type !== 'root') {
this._keys.set(label, expression.expression.value)
break
}
if (!cursor.jsonPath!.startsWith('$')) {
cursor.jsonPath = '$.' + cursor.jsonPath
}

const pathArray = JSONPath.toPathArray(cursor.jsonPath!)
this._keys.set(label, pathArray[1])
}

/**
Expand All @@ -123,9 +122,9 @@ export class Basin<T> {
const jsonPath = cursor.jsonPath!
if (typeof position !== 'number') {
// Set the value.
jp.value(this.items, jsonPath, value)
jpValue(this.items, jsonPath, value)
} else {
jp.apply(this.items, jsonPath, (currentValue: string) => {
jpApply(this.items, jsonPath, (currentValue: string) => {
if (Array.isArray(currentValue)) {
if (cursor.deleteCount !== undefined) {
// Delete
Expand Down Expand Up @@ -156,4 +155,32 @@ export class Basin<T> {
const key = this._keys.get(cursorLabel)!
return this.items[key]
}
}

function jpValue(obj: any, path: string, value: any): void {
const results: any[] = JSONPath({ path, json: obj, resultType: 'all' })
if (results.length > 0) {
results[0].parent[results[0].parentProperty] = value
} else {
// Path doesn't exist yet — create intermediate objects and set the value.
const pathArray = JSONPath.toPathArray(path)
let current = obj
for (let i = 1; i < pathArray.length - 1; i++) {
const key = pathArray[i]
if (current[key] === undefined) {
current[key] = {}
}
current = current[key]
}
if (pathArray.length > 1) {
current[pathArray[pathArray.length - 1]] = value
}
}
}

function jpApply(obj: any, path: string, fn: (value: any) => any): void {
const results: any[] = JSONPath({ path, json: obj, resultType: 'all' })
for (const result of results) {
result.parent[result.parentProperty] = fn(result.value)
}
}