Socket
Socket
Sign inDemoInstall

@oada/list-lib

Package Overview
Dependencies
Maintainers
8
Versions
60
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@oada/list-lib - npm Package Compare versions

Comparing version 1.1.1 to 2.0.0

23

lib/index.d.ts

@@ -0,1 +1,3 @@

import type { Resource } from '@oada/types/oada/resource';
import type { Link } from '@oada/types/oada/link/v1';
import type V2Changes from '@oada/types/oada/change/v2';

@@ -14,2 +16,17 @@ import { Options, ItemState } from './Options';

/**
* Type for the lists we can watch
*/
export declare type List = Resource & {
[key: string]: Link | List;
};
declare module 'jsonpath-plus' {
interface JSONPathCallable {
(options: JSONPathOptions & {
resultType: 'path' | 'pointer' | 'parentProperty';
wrap?: true;
}): string[];
(path: JSONPathOptions['path'], json: JSONPathOptions['json'], callback?: JSONPathOptions['callback'], otherTypeCallback?: JSONPathOptions['otherTypeCallback']): any[];
}
}
/**
* The main class of this library.

@@ -29,2 +46,6 @@ * Watches an OADA list and calls various callbacks when appropriate.

/**
* The JSON Path for the list items
*/
readonly itemsPath: string;
/**
* The OADA Tree for the List being watched

@@ -60,3 +81,3 @@ * @see path

};
constructor({ path, tree, name, resume, conn, persistInterval, assertItem, onAddItem, onChangeItem, onItem, onRemoveItem, onNewList, onDeleteList, getItemState, }: Options<Item>);
constructor({ path, itemsPath, tree, name, resume, conn, persistInterval, assertItem, onAddItem, onChangeItem, onItem, onRemoveItem, onNewList, onDeleteList, getItemState, }: Options<Item>);
/**

@@ -63,0 +84,0 @@ * Force library to recheck all current list items

124

lib/index.js

@@ -20,5 +20,6 @@ "use strict";

Object.defineProperty(exports, "__esModule", { value: true });
exports.ListWatch = exports.ItemState = void 0;
exports.ListWatch = exports.pathFromTree = exports.ItemState = void 0;
const bluebird_1 = __importDefault(require("bluebird"));
const json_pointer_1 = __importDefault(require("json-pointer"));
const jsonpath_plus_1 = require("jsonpath-plus");
const debug_1 = __importDefault(require("debug"));

@@ -53,3 +54,42 @@ const Options_1 = require("./Options");

}
function getListItems(list, path) {
const pointers = jsonpath_plus_1.JSONPath({
resultType: 'pointer',
path,
json: list,
preventEval: true,
}).filter(
// Don't follow underscore keys
(p) => !/\/_/.test(p));
return pointers;
}
/**
* Generates an equivalent JSON Path from an OADA Tree object
*
* @internal
* @experimental trees with multiple "paths" (excluing *)
*/
function pathFromTree(tree, root = '') {
let path = '$.*';
let outPath = '$';
const json = json_pointer_1.default.get(tree, root);
while (true) {
// Get set of non underscore keys
const keys = [
...new Set(jsonpath_plus_1.JSONPath({
resultType: 'parentProperty',
path,
json,
}).filter((k) => !k.startsWith('_'))),
];
if (keys.length === 0) {
break;
}
outPath += '.' + (keys.length === 1 ? keys[0] : `[${keys.join(',')}]`);
path += '.*';
}
return outPath;
}
exports.pathFromTree = pathFromTree;
/**
* The main class of this library.

@@ -63,3 +103,3 @@ * Watches an OADA list and calls various callbacks when appropriate.

class ListWatch {
constructor({ path, tree, name, resume = false, conn, persistInterval = 1000,
constructor({ path, itemsPath, tree, name, resume = false, conn, persistInterval = 1000,
// If no assert given, assume all items valid

@@ -99,2 +139,15 @@ assertItem = () => { }, onAddItem, onChangeItem, onItem, onRemoveItem, onNewList, onDeleteList = async () => {

__classPrivateFieldSet(this, _getItemState, getItemState);
if (itemsPath) {
this.itemsPath = itemsPath;
}
else {
if (tree) {
// Asume items are at the leaves of tree
this.itemsPath = pathFromTree(tree, path);
}
else {
// Assume flat list
this.itemsPath = '$.*';
}
}
if (onNewList) {

@@ -140,3 +193,4 @@ __classPrivateFieldSet(this, _onNewList, onNewList);

const { data: list } = (await conn.get({ path }));
const items = Object.keys(list).filter((k) => !k.match(/^_/));
//const items = Object.keys(list).filter((k) => !k.match(/^_/));
const items = getListItems(list, this.itemsPath);
//const { rev } = this.#meta;

@@ -252,3 +306,4 @@ await bluebird_1.default.map(items, async (id) => {

// Ignore _ keys of OADA
const items = Object.keys(list).filter((k) => !k.match(/^_/));
//const items = Object.keys(list).filter((k) => !k.match(/^_/));
const items = getListItems(list, this.itemsPath);
switch (type) {

@@ -258,3 +313,3 @@ case 'merge':

try {
const lchange = list[id];
const lchange = json_pointer_1.default.get(list, id);
// If there is an _id this is a new link in the list right?

@@ -281,3 +336,3 @@ if (lchange._id) {

try {
const lchange = list[id];
const lchange = json_pointer_1.default.get(list, id);
if (lchange === null) {

@@ -334,3 +389,3 @@ info(`Detected removal of item ${id} from ${path}, rev ${rev}`);

const change = {
resource_id: list[id]._id,
resource_id: json_pointer_1.default.get(list, id)._id,
path: '',

@@ -380,4 +435,5 @@ // TODO: what is the type the change??

if (currentItemsNew) {
const { data: list } = (await conn.get({ path }));
const items = Object.keys(list).filter((k) => !k.match(/^_/));
const { data: list } = (await conn.get({ path, tree }));
//const items = Object.keys(list).filter((k) => !k.match(/^_/));
const items = getListItems(list, this.itemsPath);
// ask for states of pre-existing items

@@ -401,24 +457,42 @@ const states = await __classPrivateFieldGet(this, _onNewList).call(this, items);

const rev = body._rev;
const [id, ...rest] = json_pointer_1.default.parse(changePath);
trace(`Received change to ${path}, rev ${rev}`);
let itemsFound = !!id;
let itemsFound = !!changePath;
let listChange = body;
try {
// The actual change was to an item in the list (or a descendant)
if (id) {
// Make change start at item instead of the list
const change = {
...ctx,
type,
path: json_pointer_1.default.compile(rest),
body: body,
};
await this.handleItemChange(id, change);
return;
// The actual change was to a descendant of the list
if (changePath) {
// Reconstruct change to list?
const changeObj = {};
json_pointer_1.default.set(changeObj, changePath, body);
// Find items involved in the change
const itemsChanged = jsonpath_plus_1.JSONPath({
path: this.itemsPath,
json: changeObj,
resultType: 'pointer',
});
// The change was to items of the list (or their descendants)
if (itemsChanged.length >= 1) {
return bluebird_1.default.map(itemsChanged, (item) => {
// Make change start at item instead of the list
const path = changePath.slice(item.length);
const change = {
...ctx,
type,
path,
body: json_pointer_1.default.get(changeObj, item),
};
return this.handleItemChange(item, change);
});
}
else {
// The change is between the list and items (multiple link levels)
listChange = changeObj;
}
}
// The change was to the list itself
const list = body;
itemsFound = (await this.handleListChange(list, type)) || itemsFound;
itemsFound =
(await this.handleListChange(listChange, type)) || itemsFound;
}
catch (err) {
error(`Error processing change for ${id} at ${path}, rev ${rev}: %O`, err);
error(`Error processing change at ${path}, rev ${rev}: %O`, err);
}

@@ -425,0 +499,0 @@ finally {

@@ -13,2 +13,72 @@ "use strict";

const delay = 11;
ava_1.default('it should create JSON Path from simple OADA tree', (t) => {
const tree = {
bookmarks: {
_type: 'application/vnd.oada.bookmarks.1+json',
_rev: 0,
thing: {
_type: 'application/json',
_rev: 0,
abc: {
'*': {
_type: 'application/json',
_rev: 0,
},
},
},
},
};
const path = _1.pathFromTree(tree);
t.is(path, '$.bookmarks.thing.abc.*');
});
ava_1.default('it should create JSON Path from OADA tree and root', (t) => {
const tree = {
bookmarks: {
_type: 'application/vnd.oada.bookmarks.1+json',
_rev: 0,
thing: {
_type: 'application/json',
_rev: 0,
abc: {
'*': {
_type: 'application/json',
_rev: 0,
},
},
},
},
};
const path = _1.pathFromTree(tree, '/bookmarks/thing');
t.is(path, '$.abc.*');
});
ava_1.default('it should create JSON Path from two path OADA tree', (t) => {
const tree = {
bookmarks: {
_type: 'application/vnd.oada.bookmarks.1+json',
_rev: 0,
thing1: {
_type: 'application/json',
_rev: 0,
abc: {
'*': {
_type: 'application/json',
_rev: 0,
},
},
},
thing2: {
_type: 'application/json',
_rev: 0,
abc: {
'*': {
_type: 'application/json',
_rev: 0,
},
},
},
},
};
const path = _1.pathFromTree(tree);
t.is(path, '$.bookmarks.[thing1,thing2].abc.*');
});
ava_1.default('it should WATCH given path', async (t) => {

@@ -15,0 +85,0 @@ var _a, _b, _c;

@@ -21,2 +21,11 @@ import type { TypeAssert } from '@oada/types';

/**
* JSON Path to retrieve items from list.
*
* @default $.*
* @experimental
* currently requires `tree` if there are multiple links to traverse
* @see tree
*/
itemsPath?: string;
/**
* OADA Tree for the path

@@ -23,0 +32,0 @@ *

{
"name": "@oada/list-lib",
"version": "1.1.1",
"version": "2.0.0",
"description": "Library for processing items in an OADA list",

@@ -60,2 +60,3 @@ "main": "lib/index.js",

"json-pointer": "^0.6.0",
"jsonpath-plus": "^4.0.0",
"jsonschema8": "^1.1.0",

@@ -62,0 +63,0 @@ "object-assign-deep": "^0.4.0"

@@ -11,3 +11,3 @@ import test from 'ava';

import { ListWatch } from './';
import { ListWatch, Tree, pathFromTree } from './';

@@ -18,2 +18,81 @@ const name = 'oada-list-lib-test';

test('it should create JSON Path from simple OADA tree', (t) => {
const tree: Tree = {
bookmarks: {
_type: 'application/vnd.oada.bookmarks.1+json',
_rev: 0,
thing: {
_type: 'application/json',
_rev: 0,
abc: {
'*': {
_type: 'application/json',
_rev: 0,
},
},
},
},
};
const path = pathFromTree(tree);
t.is(path, '$.bookmarks.thing.abc.*');
});
test('it should create JSON Path from OADA tree and root', (t) => {
const tree: Tree = {
bookmarks: {
_type: 'application/vnd.oada.bookmarks.1+json',
_rev: 0,
thing: {
_type: 'application/json',
_rev: 0,
abc: {
'*': {
_type: 'application/json',
_rev: 0,
},
},
},
},
};
const path = pathFromTree(tree, '/bookmarks/thing');
t.is(path, '$.abc.*');
});
test('it should create JSON Path from two path OADA tree', (t) => {
const tree: Tree = {
bookmarks: {
_type: 'application/vnd.oada.bookmarks.1+json',
_rev: 0,
thing1: {
_type: 'application/json',
_rev: 0,
abc: {
'*': {
_type: 'application/json',
_rev: 0,
},
},
},
thing2: {
_type: 'application/json',
_rev: 0,
abc: {
'*': {
_type: 'application/json',
_rev: 0,
},
},
},
},
};
const path = pathFromTree(tree);
t.is(path, '$.bookmarks.[thing1,thing2].abc.*');
});
test('it should WATCH given path', async (t) => {

@@ -20,0 +99,0 @@ const conn = createStub();

import Bluebird from 'bluebird';
import pointer from 'json-pointer';
import { JSONPath } from 'jsonpath-plus';
import debug from 'debug';

@@ -7,3 +8,3 @@

import type { Resource } from '@oada/types/oada/resource';
import type { List, Link } from '@oada/types/oada/link/v1';
import type { Link } from '@oada/types/oada/link/v1';
import type V2Changes from '@oada/types/oada/change/v2';

@@ -75,2 +76,89 @@ import type { SocketResponse } from '@oada/client/dist/websocket';

/**
* Type for the lists we can watch
*/
export type List = Resource & {
[key: string]: Link | List;
};
declare module 'jsonpath-plus' {
interface JSONPathCallable {
(
options: JSONPathOptions & {
resultType: 'path' | 'pointer' | 'parentProperty';
wrap?: true;
}
): string[];
(
path: JSONPathOptions['path'],
json: JSONPathOptions['json'],
callback?: JSONPathOptions['callback'],
otherTypeCallback?: JSONPathOptions['otherTypeCallback']
): any[];
}
}
function getListItems(list: List, path: string) {
const pointers = JSONPath({
resultType: 'pointer',
path,
json: list,
preventEval: true,
}).filter(
// Don't follow underscore keys
(p) => !/\/_/.test(p)
);
return pointers;
}
/**
* OADA Tree
*
* @internal
*/
export type Tree = {
_type?: string;
_rev?: number;
} & (
| {
[key: string]: Tree;
}
| {}
);
/**
* Generates an equivalent JSON Path from an OADA Tree object
*
* @internal
* @experimental trees with multiple "paths" (excluing *)
*/
export function pathFromTree(tree: Tree, root = ''): string {
let path = '$.*';
let outPath = '$';
const json = pointer.get(tree, root);
while (true) {
// Get set of non underscore keys
const keys = [
...new Set(
JSONPath({
resultType: 'parentProperty',
path,
json,
}).filter((k) => !k.startsWith('_'))
),
];
if (keys.length === 0) {
break;
}
outPath += '.' + (keys.length === 1 ? keys[0] : `[${keys.join(',')}]`);
path += '.*';
}
return outPath;
}
/**
* The main class of this library.

@@ -89,2 +177,6 @@ * Watches an OADA list and calls various callbacks when appropriate.

/**
* The JSON Path for the list items
*/
public readonly itemsPath;
/**
* The OADA Tree for the List being watched

@@ -135,2 +227,3 @@ * @see path

path,
itemsPath,
tree,

@@ -170,2 +263,14 @@ name,

if (itemsPath) {
this.itemsPath = itemsPath;
} else {
if (tree) {
// Asume items are at the leaves of tree
this.itemsPath = pathFromTree(tree, path);
} else {
// Assume flat list
this.itemsPath = '$.*';
}
}
if (onNewList) {

@@ -213,3 +318,4 @@ this.#onNewList = onNewList;

const { data: list } = (await conn.get({ path })) as GetResponse<List>;
const items = Object.keys(list).filter((k) => !k.match(/^_/));
//const items = Object.keys(list).filter((k) => !k.match(/^_/));
const items = getListItems(list, this.itemsPath);

@@ -340,3 +446,4 @@ //const { rev } = this.#meta;

// Ignore _ keys of OADA
const items = Object.keys(list).filter((k) => !k.match(/^_/));
//const items = Object.keys(list).filter((k) => !k.match(/^_/));
const items = getListItems(list as List, this.itemsPath);

@@ -347,3 +454,3 @@ switch (type) {

try {
const lchange = list[id] as Partial<Link>;
const lchange = pointer.get(list, id) as Partial<Link>;

@@ -373,3 +480,3 @@ // If there is an _id this is a new link in the list right?

try {
const lchange = list[id];
const lchange = pointer.get(list, id);

@@ -434,3 +541,3 @@ if (lchange === null) {

const change: Change = {
resource_id: list[id]._id,
resource_id: pointer.get(list, id)._id,
path: '',

@@ -481,4 +588,7 @@ // TODO: what is the type the change??

if (currentItemsNew) {
const { data: list } = (await conn.get({ path })) as GetResponse<List>;
const items = Object.keys(list).filter((k) => !k.match(/^_/));
const { data: list } = (await conn.get({ path, tree })) as GetResponse<
List
>;
//const items = Object.keys(list).filter((k) => !k.match(/^_/));
const items = getListItems(list, this.itemsPath);

@@ -506,30 +616,41 @@ // ask for states of pre-existing items

const rev = (body as Change['body'])._rev as string;
const [id, ...rest] = pointer.parse(changePath);
trace(`Received change to ${path}, rev ${rev}`);
let itemsFound = !!id;
let itemsFound = !!changePath;
let listChange = body as DeepPartial<List>;
try {
// The actual change was to an item in the list (or a descendant)
if (id) {
// Make change start at item instead of the list
const change: Change = {
...ctx,
type,
path: pointer.compile(rest),
body: body as {},
};
await this.handleItemChange(id, change);
return;
// The actual change was to a descendant of the list
if (changePath) {
// Reconstruct change to list?
const changeObj = {};
pointer.set(changeObj, changePath, body);
// Find items involved in the change
const itemsChanged = JSONPath({
path: this.itemsPath,
json: changeObj,
resultType: 'pointer',
});
// The change was to items of the list (or their descendants)
if (itemsChanged.length >= 1) {
return Bluebird.map(itemsChanged, (item) => {
// Make change start at item instead of the list
const path = changePath.slice(item.length);
const change: Change = {
...ctx,
type,
path,
body: pointer.get(changeObj, item),
};
return this.handleItemChange(item, change);
});
} else {
// The change is between the list and items (multiple link levels)
listChange = changeObj;
}
}
// The change was to the list itself
const list = body as DeepPartial<List>;
itemsFound = (await this.handleListChange(list, type)) || itemsFound;
itemsFound =
(await this.handleListChange(listChange, type)) || itemsFound;
} catch (err: unknown) {
error(
`Error processing change for ${id} at ${path}, rev ${rev}: %O`,
err
);
error(`Error processing change at ${path}, rev ${rev}: %O`, err);
} finally {

@@ -536,0 +657,0 @@ // Need this check to prevent infinite loop

@@ -26,2 +26,11 @@ import type { TypeAssert } from '@oada/types';

/**
* JSON Path to retrieve items from list.
*
* @default $.*
* @experimental
* currently requires `tree` if there are multiple links to traverse
* @see tree
*/
itemsPath?: string;
/**
* OADA Tree for the path

@@ -28,0 +37,0 @@ *

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc