@oada/list-lib
Advanced tools
Comparing version 2.1.0 to 2.1.1
174
lib/index.js
@@ -25,2 +25,3 @@ "use strict"; | ||
const jsonpath_plus_1 = require("jsonpath-plus"); | ||
const p_queue_1 = __importDefault(require("p-queue")); | ||
const debug_1 = __importDefault(require("debug")); | ||
@@ -256,3 +257,3 @@ const Options_1 = require("./Options"); | ||
await (__classPrivateFieldGet(this, _onAddItem) && __classPrivateFieldGet(this, _onAddItem).call(this, item, id)); | ||
__classPrivateFieldGet(this, _meta).setHandled(id, { onAddItem: { rev: _rev + '' } }); | ||
await __classPrivateFieldGet(this, _meta).setHandled(id, { onAddItem: { rev: _rev + '' } }); | ||
} | ||
@@ -268,3 +269,3 @@ } | ||
await (__classPrivateFieldGet(this, _onItem) && __classPrivateFieldGet(this, _onItem).call(this, item, id)); | ||
__classPrivateFieldGet(this, _meta).setHandled(id, { onItem: { rev: _rev + '' } }); | ||
await __classPrivateFieldGet(this, _meta).setHandled(id, { onItem: { rev: _rev + '' } }); | ||
} | ||
@@ -279,6 +280,6 @@ } | ||
info(`Detected change to item ${id} in ${path}, rev ${rev}`); | ||
const { _rev } = change; | ||
const { _rev } = change.body; | ||
try { | ||
await (__classPrivateFieldGet(this, _onChangeItem) && __classPrivateFieldGet(this, _onChangeItem).call(this, change, id)); | ||
__classPrivateFieldGet(this, _meta).setHandled(id, { onChangeItem: { rev: _rev + '' } }); | ||
await __classPrivateFieldGet(this, _meta).setHandled(id, { onChangeItem: { rev: _rev + '' } }); | ||
} | ||
@@ -294,3 +295,3 @@ finally { | ||
await __classPrivateFieldGet(this, _onItem).call(this, item, id); | ||
__classPrivateFieldGet(this, _meta).setHandled(id, { onItem: { rev: _rev + '' } }); | ||
await __classPrivateFieldGet(this, _meta).setHandled(id, { onItem: { rev: _rev + '' } }); | ||
} | ||
@@ -344,3 +345,3 @@ } | ||
// Mark for delete? | ||
__classPrivateFieldGet(this, _meta).setHandled(id, undefined); | ||
await __classPrivateFieldGet(this, _meta).setHandled(id, undefined); | ||
} | ||
@@ -401,3 +402,6 @@ } | ||
// Mark handled for all callbacks? | ||
__classPrivateFieldGet(this, _meta).setHandled(id, { onAddItem: { rev }, onItem: { rev } }); | ||
await __classPrivateFieldGet(this, _meta).setHandled(id, { | ||
onAddItem: { rev }, | ||
onItem: { rev }, | ||
}); | ||
break; | ||
@@ -449,80 +453,94 @@ default: | ||
} | ||
// Queue to handle changes in order | ||
const changeQueue = new p_queue_1.default({ concurrency: 1 }); | ||
__classPrivateFieldSet(this, _id_1, await conn.watch({ | ||
path, | ||
rev: __classPrivateFieldGet(this, _resume) ? __classPrivateFieldGet(this, _meta).rev : rev, | ||
watchCallback: async ({ type, path: changePath, body, ...ctx }) => { | ||
if (body === null && type === 'delete' && changePath === '') { | ||
// The list itself was deleted | ||
warn(`Detected delete of list ${path}`); | ||
await __classPrivateFieldGet(this, _onDeleteList).call(this); | ||
return; | ||
} | ||
const rev = body._rev; | ||
trace(`Received change to ${changePath}: %O`, { type, body, ...ctx }); | ||
if (!changePath && __classPrivateFieldGet(this, _resume)) { | ||
trace(`Received change to root of list, updating handled rev in our _meta records`); | ||
__classPrivateFieldGet(this, _meta).rev = rev; | ||
} | ||
let itemsFound = !!changePath; | ||
let listChange = body; | ||
try { | ||
// The actual change was to a descendant of the list | ||
if (changePath) { | ||
// In order to decide if this change was to the list or to an item, need to check if itemsPath | ||
// matches the changePath: if it does, it is to an item. If it doesn't, it's probably to the list. | ||
// Reconstruct change to list? | ||
const changeObj = {}; | ||
let isListChange = false; | ||
if (this.itemsPath) { | ||
json_pointer_1.default.set(changeObj, changePath, true); // just put true here for now to check if path matches | ||
const pathmatches = jsonpath_plus_1.JSONPath({ | ||
resultType: 'pointer', | ||
path: this.itemsPath, | ||
json: changeObj, | ||
preventEval: true, | ||
}); | ||
if (pathmatches && pathmatches.length === 0) { | ||
// if it does not match, this must be above the items | ||
isListChange = true; | ||
trace('Have a write to the list under itemsPath rather than to any of the items'); | ||
type: 'tree', | ||
watchCallback: (changes) => changeQueue.add(async () => { | ||
var _a; | ||
// Get root change? | ||
const rootChange = changes[0]; | ||
// TODO: Better way than just looping through them all? | ||
await bluebird_1.default.each(changes, async ({ type, path: changePath, body, ...ctx }) => { | ||
if (body === null && type === 'delete' && changePath === '') { | ||
// The list itself was deleted | ||
warn(`Detected delete of list ${path}`); | ||
await __classPrivateFieldGet(this, _onDeleteList).call(this); | ||
return; | ||
} | ||
const rev = body._rev; | ||
trace(`Received change to ${changePath}: %O`, { | ||
type, | ||
body, | ||
...ctx, | ||
}); | ||
let itemsFound = !!changePath; | ||
let listChange = body; | ||
try { | ||
// The actual change was to a descendant of the list | ||
if (changePath) { | ||
// In order to decide if this change was to the list or to an item, need to check if itemsPath | ||
// matches the changePath: if it does, it is to an item. If it doesn't, it's probably to the list. | ||
// Reconstruct change to list? | ||
const changeObj = {}; | ||
let isListChange = false; | ||
if (this.itemsPath) { | ||
json_pointer_1.default.set(changeObj, changePath, true); // just put true here for now to check if path matches | ||
const pathmatches = jsonpath_plus_1.JSONPath({ | ||
resultType: 'pointer', | ||
path: this.itemsPath, | ||
json: changeObj, | ||
preventEval: true, | ||
}); | ||
if (pathmatches && pathmatches.length === 0) { | ||
// if it does not match, this must be above the items | ||
isListChange = true; | ||
trace('Have a write to the list under itemsPath rather than to any of the items'); | ||
} | ||
} | ||
// now put the actual change body in place of the true | ||
json_pointer_1.default.set(changeObj, changePath, body); | ||
// Find items involved in the change | ||
const itemsChanged = getListItems(changeObj, this.itemsPath); | ||
// The change was to items of the list (or their descendants) | ||
if (!isListChange && itemsChanged.length >= 1) { | ||
return bluebird_1.default.map(itemsChanged, (item) => { | ||
const body = json_pointer_1.default.get(changeObj, item); | ||
// Make change start at item instead of the list | ||
const path = changePath.slice(item.length); | ||
const change = { | ||
...ctx, | ||
type, | ||
path, | ||
body, | ||
}; | ||
// Check that it is a resource change? | ||
if (!body._rev) { | ||
warn(`Ignoring unexpected (as in the body does not have a _rev) change: %O`, change); | ||
return; | ||
} | ||
return this.handleItemChange(item, change); | ||
}); | ||
} | ||
else { | ||
// The change is between the list and items | ||
// (multiple link levels) | ||
listChange = changeObj; | ||
} | ||
} | ||
// now put the actual change body in place of the true | ||
json_pointer_1.default.set(changeObj, changePath, body); | ||
// Find items involved in the change | ||
const itemsChanged = getListItems(changeObj, this.itemsPath); | ||
// The change was to items of the list (or their descendants) | ||
if (!isListChange && itemsChanged.length >= 1) { | ||
return bluebird_1.default.map(itemsChanged, (item) => { | ||
const body = json_pointer_1.default.get(changeObj, item); | ||
// Make change start at item instead of the list | ||
const path = changePath.slice(item.length); | ||
const change = { | ||
...ctx, | ||
type, | ||
path, | ||
body, | ||
}; | ||
// Check that it is a resource change? | ||
if (!body._rev) { | ||
warn(`Ignoring unexpected (as in the body does not have a _rev) change: %O`, change); | ||
return; | ||
} | ||
return this.handleItemChange(item, change); | ||
}); | ||
} | ||
else { | ||
// The change is between the list and items (multiple link levels) | ||
listChange = changeObj; | ||
} | ||
trace(`Change was to the list itself because changePath is empty, calling handleListChange`); | ||
// The change was to the list itself | ||
itemsFound = | ||
(await this.handleListChange(listChange, type)) || itemsFound; | ||
} | ||
trace(`Change was to the list itself because changePath is empty, calling handleListChange`); | ||
// The change was to the list itself | ||
itemsFound = | ||
(await this.handleListChange(listChange, type)) || itemsFound; | ||
catch (err) { | ||
error(`Error processing change at ${path}, rev ${rev}: %O`, err); | ||
} | ||
}); | ||
if (__classPrivateFieldGet(this, _resume)) { | ||
trace(`Received change to root of list, updating handled rev in our _meta records`); | ||
__classPrivateFieldGet(this, _meta).rev = ((_a = rootChange.body) === null || _a === void 0 ? void 0 : _a._rev) + ''; | ||
} | ||
catch (err) { | ||
error(`Error processing change at ${path}, rev ${rev}: %O`, err); | ||
} | ||
}, | ||
}), | ||
})); | ||
@@ -529,0 +547,0 @@ } |
@@ -18,3 +18,3 @@ "use strict"; | ||
}; | ||
var _rev, _conn, _path, _tree; | ||
var _rev, _conn, _path, _tree, _timeout, _wait; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
@@ -42,2 +42,4 @@ exports.Metadata = void 0; | ||
_tree.set(this, void 0); | ||
_timeout.set(this, void 0); | ||
_wait.set(this, void 0); | ||
__classPrivateFieldSet(this, _conn, conn); | ||
@@ -54,2 +56,13 @@ __classPrivateFieldSet(this, _path, path_1.join(path, '_meta', Metadata.META_KEY, name)); | ||
} | ||
__classPrivateFieldSet(this, _wait, Promise.resolve()); | ||
// TODO: Use timeouts for all updates? | ||
__classPrivateFieldSet(this, _timeout, setTimeout(async () => { | ||
await __classPrivateFieldGet(this, _wait); | ||
__classPrivateFieldSet(this, _wait, __classPrivateFieldGet(this, _conn).put({ | ||
path: __classPrivateFieldGet(this, _path), | ||
// TODO: Figure out why tree here causes If-Match error? | ||
//tree: this.#tree, | ||
data: { rev: __classPrivateFieldGet(this, _rev) }, | ||
})); | ||
}, 100)); | ||
} | ||
@@ -62,8 +75,3 @@ get rev() { | ||
__classPrivateFieldSet(this, _rev, rev); | ||
//this.#updated = true; | ||
__classPrivateFieldGet(this, _conn).put({ | ||
path: __classPrivateFieldGet(this, _path), | ||
tree: __classPrivateFieldGet(this, _tree), | ||
data: { rev }, | ||
}); | ||
__classPrivateFieldGet(this, _timeout).refresh(); | ||
} | ||
@@ -87,3 +95,3 @@ /** | ||
// Unset info? | ||
await __classPrivateFieldGet(this, _conn).delete({ path: path_1.join(__classPrivateFieldGet(this, _path), 'handled', 'path') }); | ||
await __classPrivateFieldGet(this, _conn).delete({ path: path_1.join(__classPrivateFieldGet(this, _path), 'handled', path) }); | ||
} | ||
@@ -108,8 +116,2 @@ //this.#updated = true; | ||
} | ||
toJSON() { | ||
return { | ||
rev: this.rev, | ||
handled: this.handled, | ||
}; | ||
} | ||
/** | ||
@@ -137,3 +139,3 @@ * Initialize the connection to the meta resource | ||
exports.Metadata = Metadata; | ||
_rev = new WeakMap(), _conn = new WeakMap(), _path = new WeakMap(), _tree = new WeakMap(); | ||
_rev = new WeakMap(), _conn = new WeakMap(), _path = new WeakMap(), _tree = new WeakMap(), _timeout = new WeakMap(), _wait = new WeakMap(); | ||
/** | ||
@@ -140,0 +142,0 @@ * @todo: Where in _meta to keep stuff? |
{ | ||
"name": "@oada/list-lib", | ||
"version": "2.1.0", | ||
"version": "2.1.1", | ||
"description": "Library for processing items in an OADA list", | ||
@@ -57,3 +57,3 @@ "main": "lib/index.js", | ||
"dependencies": { | ||
"@oada/client": "^2.2.1", | ||
"@oada/client": "^2.3.0", | ||
"@oada/types": "^1.2.0", | ||
@@ -64,4 +64,5 @@ "bluebird": "^3.7.2", | ||
"json-pointer": "^0.6.0", | ||
"jsonpath-plus": "^5.0.2" | ||
"jsonpath-plus": "^5.0.2", | ||
"p-queue": "^6.6.2" | ||
} | ||
} |
193
src/index.ts
@@ -6,2 +6,3 @@ import { join } from 'path'; | ||
import { JSONPath } from 'jsonpath-plus'; | ||
import PQueue from 'p-queue'; | ||
import debug from 'debug'; | ||
@@ -390,3 +391,3 @@ | ||
await (this.#onAddItem && this.#onAddItem(item, id)); | ||
this.#meta.setHandled(id, { onAddItem: { rev: _rev + '' } }); | ||
await this.#meta.setHandled(id, { onAddItem: { rev: _rev + '' } }); | ||
} | ||
@@ -403,3 +404,3 @@ } finally { | ||
await (this.#onItem && this.#onItem(item, id)); | ||
this.#meta.setHandled(id, { onItem: { rev: _rev + '' } }); | ||
await this.#meta.setHandled(id, { onItem: { rev: _rev + '' } }); | ||
} | ||
@@ -417,6 +418,6 @@ } | ||
const { _rev } = change; | ||
const { _rev } = change.body; | ||
try { | ||
await (this.#onChangeItem && this.#onChangeItem(change, id)); | ||
this.#meta.setHandled(id, { onChangeItem: { rev: _rev + '' } }); | ||
await this.#meta.setHandled(id, { onChangeItem: { rev: _rev + '' } }); | ||
} finally { | ||
@@ -432,3 +433,3 @@ if (this.#onItem) { | ||
await this.#onItem(item, id); | ||
this.#meta.setHandled(id, { onItem: { rev: _rev + '' } }); | ||
await this.#meta.setHandled(id, { onItem: { rev: _rev + '' } }); | ||
} | ||
@@ -492,3 +493,3 @@ } | ||
// Mark for delete? | ||
this.#meta.setHandled(id, undefined); | ||
await this.#meta.setHandled(id, undefined); | ||
} | ||
@@ -557,3 +558,6 @@ } else { | ||
// Mark handled for all callbacks? | ||
this.#meta.setHandled(id, { onAddItem: { rev }, onItem: { rev } }); | ||
await this.#meta.setHandled(id, { | ||
onAddItem: { rev }, | ||
onItem: { rev }, | ||
}); | ||
break; | ||
@@ -608,94 +612,115 @@ default: | ||
} | ||
// Queue to handle changes in order | ||
const changeQueue = new PQueue({ concurrency: 1 }); | ||
this.#id = await conn.watch({ | ||
path, | ||
rev: this.#resume ? this.#meta.rev : rev, | ||
watchCallback: async ({ type, path: changePath, body, ...ctx }) => { | ||
if (body === null && type === 'delete' && changePath === '') { | ||
// The list itself was deleted | ||
warn(`Detected delete of list ${path}`); | ||
type: 'tree', | ||
watchCallback: (changes) => | ||
changeQueue.add(async () => { | ||
// Get root change? | ||
const rootChange = changes[0]; | ||
await this.#onDeleteList(); | ||
return; | ||
} | ||
// TODO: Better way than just looping through them all? | ||
await Bluebird.each( | ||
changes, | ||
async ({ type, path: changePath, body, ...ctx }) => { | ||
if (body === null && type === 'delete' && changePath === '') { | ||
// The list itself was deleted | ||
warn(`Detected delete of list ${path}`); | ||
const rev = (body as Change['body'])._rev as string; | ||
await this.#onDeleteList(); | ||
return; | ||
} | ||
trace(`Received change to ${changePath}: %O`, { type, body, ...ctx }); | ||
const rev = (body as Change['body'])._rev as string; | ||
if (!changePath && this.#resume) { | ||
trace( | ||
`Received change to root of list, updating handled rev in our _meta records` | ||
); | ||
this.#meta!.rev = rev; | ||
} | ||
trace(`Received change to ${changePath}: %O`, { | ||
type, | ||
body, | ||
...ctx, | ||
}); | ||
let itemsFound = !!changePath; | ||
let listChange = body as DeepPartial<List>; | ||
try { | ||
// The actual change was to a descendant of the list | ||
if (changePath) { | ||
// In order to decide if this change was to the list or to an item, need to check if itemsPath | ||
// matches the changePath: if it does, it is to an item. If it doesn't, it's probably to the list. | ||
let itemsFound = !!changePath; | ||
let listChange = body as DeepPartial<List>; | ||
try { | ||
// The actual change was to a descendant of the list | ||
if (changePath) { | ||
// In order to decide if this change was to the list or to an item, need to check if itemsPath | ||
// matches the changePath: if it does, it is to an item. If it doesn't, it's probably to the list. | ||
// Reconstruct change to list? | ||
const changeObj = {}; | ||
let isListChange = false; | ||
if (this.itemsPath) { | ||
pointer.set(changeObj, changePath, true); // just put true here for now to check if path matches | ||
const pathmatches = JSONPath({ | ||
resultType: 'pointer', | ||
path: this.itemsPath, | ||
json: changeObj, | ||
preventEval: true, | ||
}); | ||
if (pathmatches && pathmatches.length === 0) { | ||
// if it does not match, this must be above the items | ||
isListChange = true; | ||
// Reconstruct change to list? | ||
const changeObj = {}; | ||
let isListChange = false; | ||
if (this.itemsPath) { | ||
pointer.set(changeObj, changePath, true); // just put true here for now to check if path matches | ||
const pathmatches = JSONPath({ | ||
resultType: 'pointer', | ||
path: this.itemsPath, | ||
json: changeObj, | ||
preventEval: true, | ||
}); | ||
if (pathmatches && pathmatches.length === 0) { | ||
// if it does not match, this must be above the items | ||
isListChange = true; | ||
trace( | ||
'Have a write to the list under itemsPath rather than to any of the items' | ||
); | ||
} | ||
} | ||
// now put the actual change body in place of the true | ||
pointer.set(changeObj, changePath, body); | ||
// Find items involved in the change | ||
const itemsChanged = getListItems(changeObj, this.itemsPath); | ||
// The change was to items of the list (or their descendants) | ||
if (!isListChange && itemsChanged.length >= 1) { | ||
return Bluebird.map(itemsChanged, (item) => { | ||
const body = pointer.get(changeObj, item); | ||
// Make change start at item instead of the list | ||
const path = changePath.slice(item.length); | ||
const change: Change = { | ||
...ctx, | ||
type, | ||
path, | ||
body, | ||
}; | ||
// Check that it is a resource change? | ||
if (!body._rev) { | ||
warn( | ||
`Ignoring unexpected (as in the body does not have a _rev) change: %O`, | ||
change | ||
); | ||
return; | ||
} | ||
return this.handleItemChange(item, change); | ||
}); | ||
} else { | ||
// The change is between the list and items | ||
// (multiple link levels) | ||
listChange = changeObj; | ||
} | ||
} | ||
trace( | ||
'Have a write to the list under itemsPath rather than to any of the items' | ||
`Change was to the list itself because changePath is empty, calling handleListChange` | ||
); | ||
// The change was to the list itself | ||
itemsFound = | ||
(await this.handleListChange(listChange, type)) || itemsFound; | ||
} catch (err: unknown) { | ||
error( | ||
`Error processing change at ${path}, rev ${rev}: %O`, | ||
err | ||
); | ||
} | ||
} | ||
); | ||
// now put the actual change body in place of the true | ||
pointer.set(changeObj, changePath, body); | ||
// Find items involved in the change | ||
const itemsChanged = getListItems(changeObj, this.itemsPath); | ||
// The change was to items of the list (or their descendants) | ||
if (!isListChange && itemsChanged.length >= 1) { | ||
return Bluebird.map(itemsChanged, (item) => { | ||
const body = pointer.get(changeObj, item); | ||
// Make change start at item instead of the list | ||
const path = changePath.slice(item.length); | ||
const change: Change = { | ||
...ctx, | ||
type, | ||
path, | ||
body, | ||
}; | ||
// Check that it is a resource change? | ||
if (!body._rev) { | ||
warn( | ||
`Ignoring unexpected (as in the body does not have a _rev) change: %O`, | ||
change | ||
); | ||
return; | ||
} | ||
return this.handleItemChange(item, change); | ||
}); | ||
} else { | ||
// The change is between the list and items (multiple link levels) | ||
listChange = changeObj; | ||
} | ||
if (this.#resume) { | ||
trace( | ||
`Received change to root of list, updating handled rev in our _meta records` | ||
); | ||
this.#meta.rev = (rootChange.body as Resource)?._rev + ''; | ||
} | ||
trace( | ||
`Change was to the list itself because changePath is empty, calling handleListChange` | ||
); | ||
// The change was to the list itself | ||
itemsFound = | ||
(await this.handleListChange(listChange, type)) || itemsFound; | ||
} catch (err: unknown) { | ||
error(`Error processing change at ${path}, rev ${rev}: %O`, err); | ||
} | ||
}, | ||
}), | ||
}); | ||
@@ -702,0 +727,0 @@ } |
@@ -59,2 +59,4 @@ import { join } from 'path'; | ||
#tree?: object; | ||
#timeout; | ||
#wait: Promise<unknown>; | ||
@@ -67,8 +69,3 @@ get rev(): string { | ||
this.#rev = rev; | ||
//this.#updated = true; | ||
this.#conn.put({ | ||
path: this.#path, | ||
tree: this.#tree, | ||
data: { rev }, | ||
}); | ||
this.#timeout.refresh(); | ||
} | ||
@@ -92,3 +89,3 @@ | ||
// Unset info? | ||
await this.#conn.delete({ path: join(this.#path, 'handled', 'path') }); | ||
await this.#conn.delete({ path: join(this.#path, 'handled', path) }); | ||
} | ||
@@ -114,9 +111,2 @@ //this.#updated = true; | ||
toJSON(): object { | ||
return { | ||
rev: this.rev, | ||
handled: this.handled, | ||
}; | ||
} | ||
constructor({ | ||
@@ -150,2 +140,13 @@ conn, | ||
} | ||
this.#wait = Promise.resolve(); | ||
// TODO: Use timeouts for all updates? | ||
this.#timeout = setTimeout(async () => { | ||
await this.#wait; | ||
this.#wait = this.#conn.put({ | ||
path: this.#path, | ||
// TODO: Figure out why tree here causes If-Match error? | ||
//tree: this.#tree, | ||
data: { rev: this.#rev }, | ||
}); | ||
}, 100); | ||
} | ||
@@ -152,0 +153,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
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
120174
2629
8
+ Addedp-queue@^6.6.2
Updated@oada/client@^2.3.0