Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@oada/list-lib

Package Overview
Dependencies
Maintainers
0
Versions
61
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 5.0.4 to 6.0.0

4

dist/ListWatch.d.ts

@@ -57,7 +57,7 @@ /**

* The unique name of this service/watch
*/
*/
readonly name: string;
/**
* The unique name of this service/watch
*/
*/
readonly timeout: number | undefined;

@@ -64,0 +64,0 @@ constructor(options: Options<Item>);

@@ -17,4 +17,2 @@ /**

*/
var _ListWatch_instances, _ListWatch_conn, _ListWatch_watch, _ListWatch_meta, _ListWatch_emitter, _ListWatch_assertItem, _ListWatch_getItem, _ListWatch_emit, _ListWatch_once, _ListWatch_wrapListener, _ListWatch_generate, _ListWatch_initialize, _ListWatch_handleStartingItems, _ListWatch_handleItemChanges, _ListWatch_handleChangeFeed;
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
import { on } from 'node:events';

@@ -24,3 +22,3 @@ import { EventEmitter } from 'eventemitter3';

import debug from 'debug';
import { assertNever, buildChangeObject, changeSym, join, } from './util.js';
import { assertNever, buildChangeObject, changeSym, errorCode, join, } from './util.js';
import { ChangeType, } from './index.js';

@@ -50,2 +48,39 @@ import { Metadata } from './Metadata.js';

export class ListWatch {
/**
* Make ListWatch consider every unknown `Item` new
* @deprecated
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
static AssumeNew = AssumeState.New;
/**
* Make ListWatch consider every unknown `Item` handled
* @deprecated
*/
// eslint-disable-next-line @typescript-eslint/naming-convention
static AssumeHandled = AssumeState.Handled;
/**
* The OADA path of the List being watched
*/
path;
/**
* The OADA tree of the List being watched
*/
tree;
/**
* The JSON Path for the list items
*/
itemsPath;
/**
* The unique name of this service/watch
*/
name;
/**
* The unique name of this service/watch
*/
timeout;
#conn;
#watch;
#meta;
#emitter;
#assertItem;
constructor({ path, itemsPath = '$[?(!@property.match(/^_/))]', tree = { '*': { _type: 'application/json' } }, name = process.env.npm_package_name, resume = true, conn, persistInterval = 1000, timeout,

@@ -55,8 +90,2 @@ // If no assert given, assume all items valid

assertItem = () => { }, onAddItem, onChangeItem, onItem, onRemoveItem, onNewList, }) {
_ListWatch_instances.add(this);
_ListWatch_conn.set(this, void 0);
_ListWatch_watch.set(this, void 0);
_ListWatch_meta.set(this, void 0);
_ListWatch_emitter.set(this, void 0);
_ListWatch_assertItem.set(this, void 0);
this.path = path;

@@ -67,25 +96,25 @@ this.tree = tree;

this.timeout = timeout;
__classPrivateFieldSet(this, _ListWatch_conn, conn, "f");
__classPrivateFieldSet(this, _ListWatch_assertItem, assertItem, "f");
__classPrivateFieldSet(this, _ListWatch_emitter, new EventEmitter(), "f");
this.#conn = conn;
this.#assertItem = assertItem;
this.#emitter = new EventEmitter();
if (onAddItem) {
log.warn('onAddItem is deprecated, use .on(ChangeType.ItemAdded, ...)');
__classPrivateFieldGet(this, _ListWatch_emitter, "f").on(ChangeType.ItemAdded, __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_wrapListener).call(this, ChangeType.ItemAdded, async ({ item, pointer }) => onAddItem(await item, pointer)));
this.#emitter.on(ChangeType.ItemAdded, this.#wrapListener(ChangeType.ItemAdded, async ({ item, pointer }) => onAddItem(await item, pointer)));
}
if (onChangeItem) {
log.warn('onChangeItem is deprecated, use .on(ChangeType.ItemChanged, ...)');
__classPrivateFieldGet(this, _ListWatch_emitter, "f").on(ChangeType.ItemChanged, __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_wrapListener).call(this, ChangeType.ItemChanged, async ({ change, pointer }) => onChangeItem(change, pointer)));
this.#emitter.on(ChangeType.ItemChanged, this.#wrapListener(ChangeType.ItemChanged, async ({ change, pointer }) => onChangeItem(change, pointer)));
}
if (onItem) {
log.warn('onItem is deprecated, use .on(ChangeType.ItemAny, ...)');
__classPrivateFieldGet(this, _ListWatch_emitter, "f").on(ChangeType.ItemAny, __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_wrapListener).call(this, ChangeType.ItemAny, async ({ item, pointer }) => onItem(await item, pointer)));
this.#emitter.on(ChangeType.ItemAny, this.#wrapListener(ChangeType.ItemAny, async ({ item, pointer }) => onItem(await item, pointer)));
}
if (onRemoveItem) {
log.warn('onRemoveItem is deprecated, use .on(ChangeType.ItemRemoved, ...)');
__classPrivateFieldGet(this, _ListWatch_emitter, "f").on(ChangeType.ItemRemoved, __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_wrapListener).call(this, ChangeType.ItemRemoved, async ({ pointer }) => onRemoveItem(pointer)));
this.#emitter.on(ChangeType.ItemRemoved, this.#wrapListener(ChangeType.ItemRemoved, async ({ pointer }) => onRemoveItem(pointer)));
}
// Don't persist metdata if service does not "resume"
__classPrivateFieldSet(this, _ListWatch_meta, resume
this.#meta = resume
? new Metadata({
conn: __classPrivateFieldGet(this, _ListWatch_conn, "f"),
conn: this.#conn,
path,

@@ -95,4 +124,4 @@ name,

})
: undefined, "f");
__classPrivateFieldSet(this, _ListWatch_watch, __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_initialize).call(this, onNewList, timeout), "f");
: undefined;
this.#watch = this.#initialize(onNewList, timeout);
}

@@ -104,7 +133,7 @@ /**

try {
const watch = await __classPrivateFieldGet(this, _ListWatch_watch, "f");
const watch = await this.#watch;
await watch.return?.();
}
finally {
await __classPrivateFieldGet(this, _ListWatch_meta, "f")?.stop();
await this.#meta?.stop();
}

@@ -114,6 +143,6 @@ }

if (listener) {
__classPrivateFieldGet(this, _ListWatch_emitter, "f").on(event, __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_wrapListener).call(this, event, listener));
this.#emitter.on(event, this.#wrapListener(event, listener));
return this;
}
return __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_generate).call(this, event);
return this.#generate(event);
}

@@ -123,273 +152,267 @@ // eslint-disable-next-line @typescript-eslint/promise-function-async

if (listener) {
__classPrivateFieldGet(this, _ListWatch_emitter, "f").once(event, __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_wrapListener).call(this, event, listener));
this.#emitter.once(event, this.#wrapListener(event, listener));
return this;
}
return __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_once).call(this, event);
return this.#once(event);
}
}
_ListWatch_conn = new WeakMap(), _ListWatch_watch = new WeakMap(), _ListWatch_meta = new WeakMap(), _ListWatch_emitter = new WeakMap(), _ListWatch_assertItem = new WeakMap(), _ListWatch_instances = new WeakSet(), _ListWatch_getItem =
/**
* Fetch the contents of the corresponding list item
*/
async function _ListWatch_getItem(itemEvent, timeout) {
// Needed because TS is weird about asserts...
const assertItem = __classPrivateFieldGet(this, _ListWatch_assertItem, "f");
const { data: item } = await __classPrivateFieldGet(this, _ListWatch_conn, "f").get({
path: join(this.path, itemEvent.pointer),
timeout
});
assertItem(item);
return item;
}, _ListWatch_emit =
/**
* Emit our internal events
*/
async function _ListWatch_emit(event, itemEvent) {
// Automagically get the list item when it is accessed
const getItem = __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_getItem).bind(this);
let itemP;
const out = {
get item() {
if (itemP === undefined) {
itemP = getItem(this);
/**
* Fetch the contents of the corresponding list item
*/
async #getItem(itemEvent, timeout) {
// Needed because TS is weird about asserts...
const assertItem = this.#assertItem;
const { data: item } = await this.#conn.get({
path: join(this.path, itemEvent.pointer),
timeout,
});
assertItem(item);
return item;
}
/**
* Emit our internal events
*/
async #emit(event, itemEvent) {
// Automagically get the list item when it is accessed
const getItem = this.#getItem.bind(this);
let itemP;
const out = {
get item() {
if (itemP === undefined) {
itemP = getItem(this);
}
return itemP;
},
...itemEvent,
};
switch (event) {
case ChangeType.ItemChanged: {
log.debug({ itemChange: itemEvent }, 'Detected change to item');
this.#emitter.emit(ChangeType.ItemChanged, out);
this.#emitter.emit(ChangeType.ItemAny, out);
break;
}
return itemP;
},
...itemEvent,
};
switch (event) {
case ChangeType.ItemChanged: {
log.debug({ itemChange: itemEvent }, 'Detected change to item');
__classPrivateFieldGet(this, _ListWatch_emitter, "f").emit(ChangeType.ItemChanged, out);
__classPrivateFieldGet(this, _ListWatch_emitter, "f").emit(ChangeType.ItemAny, out);
break;
case ChangeType.ItemAdded: {
log.debug({ itemChange: itemEvent }, 'Detected new item');
this.#emitter.emit(ChangeType.ItemAdded, out);
this.#emitter.emit(ChangeType.ItemAny, out);
break;
}
case ChangeType.ItemRemoved: {
log.debug({ itemChange: itemEvent }, 'Detected removed item');
this.#emitter.emit(ChangeType.ItemRemoved, out);
break;
}
case ChangeType.ItemAny: {
throw new TypeError('ItemAny is not a valid event');
}
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
default: {
assertNever(event, `Unknown event type ${event}`);
}
}
case ChangeType.ItemAdded: {
log.debug({ itemChange: itemEvent }, 'Detected new item');
__classPrivateFieldGet(this, _ListWatch_emitter, "f").emit(ChangeType.ItemAdded, out);
__classPrivateFieldGet(this, _ListWatch_emitter, "f").emit(ChangeType.ItemAny, out);
break;
}
async #once(event) {
const generator = this.#generate(event);
try {
const { value } = await generator.next();
return value;
}
case ChangeType.ItemRemoved: {
log.debug({ itemChange: itemEvent }, 'Detected removed item');
__classPrivateFieldGet(this, _ListWatch_emitter, "f").emit(ChangeType.ItemRemoved, out);
break;
finally {
await generator.return();
}
case ChangeType.ItemAny: {
throw new TypeError('ItemAny is not a valid event');
}
default: {
assertNever(event, `Unknown event type ${event}`);
}
}
}, _ListWatch_once = async function _ListWatch_once(event) {
const generator = __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_generate).call(this, event);
try {
const { value } = await generator.next();
return value;
#wrapListener(type, listener) {
return async (itemChange) => {
try {
await listener(itemChange);
}
catch (error) {
log.error({ type, listener: listener.name, error }, 'Error in listener');
await this.#meta?.setErrored(itemChange.pointer, itemChange.listRev, error);
}
finally {
if (this.#meta) {
// Update our place in the change feed?
this.#meta.rev = itemChange.listRev;
}
}
};
}
finally {
await generator.return();
async *#generate(type) {
const events = on(this.#emitter, type);
for await (const [event] of events) {
try {
// Generate event
yield [event];
}
catch (error) {
log.error({ type, error }, 'Error in generator');
await this.#meta?.setErrored(event.pointer, event.listRev, error);
}
finally {
if (this.#meta) {
// Update our place in the change feed?
this.#meta.rev = event.listRev;
}
}
}
}
}, _ListWatch_wrapListener = function _ListWatch_wrapListener(type, listener) {
return async (itemChange) => {
/**
* Do async stuff for initializing ourself since constructors are synchronous
*/
async #initialize(assume = AssumeState.New, timeout) {
const { path } = this;
const conn = this.#conn;
log.debug('Ensuring %s exists', path);
try {
await listener(itemChange);
await conn.head({ path, timeout });
}
catch (error) {
log.error({ type, listener: listener.name, error }, 'Error in listener');
await __classPrivateFieldGet(this, _ListWatch_meta, "f")?.setErrored(itemChange.pointer, itemChange.listRev, error);
}
finally {
if (__classPrivateFieldGet(this, _ListWatch_meta, "f")) {
// Update our place in the change feed?
__classPrivateFieldGet(this, _ListWatch_meta, "f").rev = itemChange.listRev;
if (['403', '404'].includes(errorCode(error) ?? '')) {
// Create it
await conn.put({ path, data: {}, timeout });
log.trace('Created %s because it did not exist', path);
}
else {
log.error({ error });
throw error;
}
}
};
}, _ListWatch_generate = async function* _ListWatch_generate(type) {
const events = on(__classPrivateFieldGet(this, _ListWatch_emitter, "f"), type);
for await (const [event] of events) {
try {
// Generate event
yield [event];
}
catch (error) {
log.error({ type, error }, 'Error in generator');
await __classPrivateFieldGet(this, _ListWatch_meta, "f")?.setErrored(event.pointer, event.listRev, error);
}
finally {
if (__classPrivateFieldGet(this, _ListWatch_meta, "f")) {
// Update our place in the change feed?
__classPrivateFieldGet(this, _ListWatch_meta, "f").rev = event.listRev;
const foundMeta = await this.#meta?.init();
log.debug('Resuming watch from rev %s', this.#meta?.rev);
// Setup watch on the path
const { changes } = await conn.watch({
path,
rev: this.#meta?.rev,
type: 'tree',
timeout,
});
if (!foundMeta) {
switch (assume) {
case AssumeState.Handled: {
break;
}
case AssumeState.New: {
await this.#handleStartingItems(timeout);
break;
}
// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
default: {
assertNever(assume);
}
}
}
// eslint-disable-next-line github/no-then
void this.#handleChangeFeed(changes).catch((error) =>
// Forward rejections to EventEmitter
this.#emitter.emit('error', error));
log.info({ this: this }, 'ListWatch initialized');
return changes;
}
}, _ListWatch_initialize =
/**
* Do async stuff for initializing ourself since constructors are synchronous
*/
async function _ListWatch_initialize(assume = AssumeState.New, timeout) {
const { path } = this;
const conn = __classPrivateFieldGet(this, _ListWatch_conn, "f");
log.debug('Ensuring %s exists', path);
try {
await conn.head({ path, timeout });
}
catch (error) {
// @ts-expect-error darn errors
if (error?.status === 403 || error?.status === 404 || error?.code === '403' || error?.code === '404') {
// Create it
await conn.put({ path, data: {}, timeout });
log.trace('Created %s because it did not exist', path);
/**
* Treat all starting list items as new
*
* @todo Remove need for tree GET
*/
async #handleStartingItems(timeout) {
const { path, tree, itemsPath } = this;
const { data: json } = await this.#conn.get({ path, tree, timeout });
if (typeof json !== 'object' ||
json === null ||
Array.isArray(json) ||
Buffer.isBuffer(json)) {
throw new TypeError('Expected JSON');
}
else {
log.error({ error });
throw error;
// eslint-disable-next-line new-cap
const items = JSONPath({
resultType: 'all',
path: itemsPath,
json,
});
const listRev = Number(json._rev);
for await (const { value, pointer } of items) {
const itemChange = {
item: value,
listRev,
pointer,
};
await this.#emit(ChangeType.ItemAdded, itemChange);
}
}
const foundMeta = await __classPrivateFieldGet(this, _ListWatch_meta, "f")?.init();
log.debug('Resuming watch from rev %s', __classPrivateFieldGet(this, _ListWatch_meta, "f")?.rev);
// Setup watch on the path
const { changes } = await conn.watch({
path,
rev: __classPrivateFieldGet(this, _ListWatch_meta, "f")?.rev,
type: 'tree',
timeout,
});
if (!foundMeta) {
switch (assume) {
case AssumeState.Handled: {
break;
/**
* Iterate though chid changes to list items
*/
async #handleItemChanges(changeBody, listRev) {
// eslint-disable-next-line new-cap
const items = JSONPath({
resultType: 'all',
path: this.itemsPath,
json: changeBody,
});
for await (const { value, pointer } of items) {
if (value === undefined) {
// Item was removed from list
const itemChange = {
listRev,
pointer,
};
await this.#emit(ChangeType.ItemRemoved, itemChange);
continue;
}
case AssumeState.New: {
await __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_handleStartingItems).call(this, timeout);
break;
const { [changeSym]: changes } = value;
if (!changes && typeof value === 'object' && '_id' in value) {
// Item was added to list?
const itemChange = {
listRev,
pointer,
};
await this.#emit(ChangeType.ItemAdded, itemChange);
continue;
}
default: {
assertNever(assume);
for await (const change of changes ?? []) {
log.trace({ change }, 'Received change');
const rev = Number(
// @ts-expect-error just do it
change.body?._meta?._rev ?? change.body?._rev);
// ???: Find any children of change
// const changes = [change];
const itemChange = {
rev,
listRev,
pointer,
change: {
...change,
// Adust change path to start at this item
path: change.path.slice(pointer.length),
},
};
// Emit generic item change event
await this.#emit(ChangeType.ItemChanged, itemChange);
}
}
}
// eslint-disable-next-line github/no-then
void __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_handleChangeFeed).call(this, changes).catch((error) =>
// Forward rejections to EventEmitter
__classPrivateFieldGet(this, _ListWatch_emitter, "f").emit('error', error));
log.info({ this: this }, 'ListWatch initialized');
return changes;
}, _ListWatch_handleStartingItems =
/**
* Treat all starting list items as new
*
* @todo Remove need for tree GET
*/
async function _ListWatch_handleStartingItems(timeout) {
const { path, tree, itemsPath } = this;
const { data: json } = await __classPrivateFieldGet(this, _ListWatch_conn, "f").get({ path, tree, timeout });
if (typeof json !== 'object' ||
json === null ||
Array.isArray(json) ||
Buffer.isBuffer(json)) {
throw new TypeError('Expected JSON');
}
// eslint-disable-next-line new-cap
const items = JSONPath({
resultType: 'all',
path: itemsPath,
json,
});
const listRev = Number(json._rev);
for await (const { value, pointer } of items) {
const itemChange = {
item: value,
listRev,
pointer,
};
await __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_emit).call(this, ChangeType.ItemAdded, itemChange);
}
}, _ListWatch_handleItemChanges =
/**
* Iterate though chid changes to list items
*/
async function _ListWatch_handleItemChanges(changeBody, listRev) {
// eslint-disable-next-line new-cap
const items = JSONPath({
resultType: 'all',
path: this.itemsPath,
json: changeBody,
});
for await (const { value, pointer } of items) {
if (value === undefined) {
// Item was removed from list
const itemChange = {
listRev,
pointer,
};
await __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_emit).call(this, ChangeType.ItemRemoved, itemChange);
continue;
}
const { [changeSym]: changes } = value;
if (!changes && typeof value === 'object' && '_id' in value) {
// Item was added to list?
const itemChange = {
listRev,
pointer,
};
await __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_emit).call(this, ChangeType.ItemAdded, itemChange);
continue;
}
for await (const change of changes ?? []) {
log.trace({ change }, 'Received change');
const rev = Number(
async #handleChangeFeed(watch) {
// Iterate through list change feed
for await (const [rootChange, ...children] of watch) {
const listRev = Number(
// @ts-expect-error just do it
change.body?._meta?._rev ?? change.body?._rev);
// ???: Find any children of change
// const changes = [change];
const itemChange = {
rev,
listRev,
pointer,
change: {
...change,
// Adust change path to start at this item
path: change.path.slice(pointer.length),
},
};
// Emit generic item change event
await __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_emit).call(this, ChangeType.ItemChanged, itemChange);
rootChange.body?._meta?._rev ?? rootChange.body?._rev);
if (rootChange.body === null &&
rootChange.type === 'delete' &&
rootChange.path === '') {
// The list itself was deleted
log.warn('Detected delete of list %s, nothing left to watch', rootChange.path);
break;
}
const changeBody = buildChangeObject(rootChange, ...children);
await this.#handleItemChanges(changeBody, listRev);
if (this.#meta) {
log.trace('Received change to root of list, updating handled rev in our _meta records');
this.#meta.rev = rootChange.body?._rev;
}
}
log.fatal('Change feed ended unexpectedly');
throw new Error('Change feed ended');
}
}, _ListWatch_handleChangeFeed = async function _ListWatch_handleChangeFeed(watch) {
// Iterate through list change feed
for await (const [rootChange, ...children] of watch) {
const listRev = Number(
// @ts-expect-error just do it
rootChange.body?._meta?._rev ?? rootChange.body?._rev);
if (rootChange.body === null &&
rootChange.type === 'delete' &&
rootChange.path === '') {
// The list itself was deleted
log.warn('Detected delete of list %s, nothing left to watch', rootChange.path);
break;
}
const changeBody = buildChangeObject(rootChange, ...children);
await __classPrivateFieldGet(this, _ListWatch_instances, "m", _ListWatch_handleItemChanges).call(this, changeBody, listRev);
if (__classPrivateFieldGet(this, _ListWatch_meta, "f")) {
log.trace('Received change to root of list, updating handled rev in our _meta records');
__classPrivateFieldGet(this, _ListWatch_meta, "f").rev = rootChange.body?._rev;
}
}
log.fatal('Change feed ended unexpectedly');
throw new Error('Change feed ended');
};
/**
* Make ListWatch consider every unknown `Item` new
* @deprecated
*/
ListWatch.AssumeNew = AssumeState.New;
/**
* Make ListWatch consider every unknown `Item` handled
* @deprecated
*/
ListWatch.AssumeHandled = AssumeState.Handled;
}
//# sourceMappingURL=ListWatch.js.map

@@ -17,4 +17,2 @@ /**

*/
var _Metadata_instances, _Metadata_rev, _Metadata_revDirty, _Metadata_conn, _Metadata_path, _Metadata_initialized, _Metadata_controller, _Metadata_updates, _Metadata_doUpdate;
import { __classPrivateFieldGet, __classPrivateFieldSet } from "tslib";
import { inspect } from 'node:util';

@@ -25,3 +23,3 @@ import { AbortController } from 'abort-controller';

import { assert as assertResource } from '@oada/types/oada/resource.js';
import { join } from './util.js';
import { errorCode, join } from './util.js';
const log = {

@@ -47,22 +45,21 @@ trace: debug('@oada/list-lib#metadata:trace'),

}
/**
* The rev we left off on
*/
#rev;
#revDirty = false;
// Where to store state
#conn;
#path;
#initialized = false;
#controller;
#updates;
constructor({ conn, path, name, persistInterval, }) {
_Metadata_instances.add(this);
/**
* The rev we left off on
*/
_Metadata_rev.set(this, void 0);
_Metadata_revDirty.set(this, false);
// Where to store state
_Metadata_conn.set(this, void 0);
_Metadata_path.set(this, void 0);
_Metadata_initialized.set(this, false);
_Metadata_controller.set(this, void 0);
_Metadata_updates.set(this, void 0);
__classPrivateFieldSet(this, _Metadata_conn, conn, "f");
__classPrivateFieldSet(this, _Metadata_path, join(path, '_meta', Metadata.META_KEY, name), "f");
__classPrivateFieldSet(this, _Metadata_controller, new AbortController(), "f");
this.#conn = conn;
this.#path = join(path, '_meta', Metadata.META_KEY, name);
this.#controller = new AbortController();
// ??? Use timeouts for all updates?
const revUpdateInterval = setInterval(persistInterval, undefined, {
// @ts-expect-error browser/node difference bs
signal: __classPrivateFieldGet(this, _Metadata_controller, "f").signal,
signal: this.#controller.signal,
});

@@ -72,19 +69,19 @@ const updateRevs = async () => {

for await (const _ of revUpdateInterval) {
await __classPrivateFieldGet(this, _Metadata_instances, "m", _Metadata_doUpdate).call(this);
await this.#doUpdate();
}
}
finally {
await __classPrivateFieldGet(this, _Metadata_instances, "m", _Metadata_doUpdate).call(this);
await this.#doUpdate();
}
};
__classPrivateFieldSet(this, _Metadata_updates, updateRevs(), "f");
this.#updates = updateRevs();
}
async stop() {
__classPrivateFieldGet(this, _Metadata_controller, "f").abort();
await __classPrivateFieldGet(this, _Metadata_updates, "f");
this.#controller.abort();
await this.#updates;
}
async setErrored(pointer, rev, error) {
// Merge with current info
await __classPrivateFieldGet(this, _Metadata_conn, "f")?.put({
path: __classPrivateFieldGet(this, _Metadata_path, "f"),
await this.#conn?.put({
path: this.#path,
data: {

@@ -107,40 +104,67 @@ errors: {

try {
const { data } = await __classPrivateFieldGet(this, _Metadata_conn, "f").get({
path: __classPrivateFieldGet(this, _Metadata_path, "f"),
const { data } = await this.#conn.get({
path: this.#path,
});
assertResource(data);
__classPrivateFieldSet(this, _Metadata_rev, Number(data.rev ?? 0), "f");
this.#rev = Number(data.rev ?? 0);
return true;
}
catch {
// Create our metadata?
log.info('%s does not exist, posting new resource', __classPrivateFieldGet(this, _Metadata_path, "f"));
const { headers: { 'content-location': location }, } = await __classPrivateFieldGet(this, _Metadata_conn, "f").post({
path: '/resources/',
data: {},
contentType: 'application/json',
});
const { headers: { 'x-oada-rev': revHeader }, } = await __classPrivateFieldGet(this, _Metadata_conn, "f").put({
path: __classPrivateFieldGet(this, _Metadata_path, "f"),
data: { _id: location?.slice(1) },
});
const rev = revHeader ? Number(revHeader) : undefined;
__classPrivateFieldSet(this, _Metadata_rev, rev, "f");
await __classPrivateFieldGet(this, _Metadata_conn, "f").put({
path: __classPrivateFieldGet(this, _Metadata_path, "f"),
data: {
rev: rev,
},
});
return false;
catch (error) {
if (errorCode(error) !== '404') {
// Pass other errors causes up
throw new Error('List init error', { cause: error });
}
return await this.#createMeta();
}
finally {
__classPrivateFieldSet(this, _Metadata_initialized, true, "f");
this.#initialized = true;
}
}
/**
* Create our metadata
*/
async #createMeta() {
log.info('%s does not exist, posting new resource', this.#path);
const { headers: { 'content-location': location }, } = await this.#conn.post({
path: '/resources/',
data: {},
contentType: 'application/json',
});
const { headers: { 'x-oada-rev': revHeader }, } = await this.#conn.put({
path: this.#path,
data: { _id: location?.slice(1) },
});
const rev = revHeader ? Number(revHeader) : undefined;
this.#rev = rev;
await this.#conn.put({
path: this.#path,
data: {
rev: rev,
},
});
return false;
}
async #doUpdate() {
if (!(this.#initialized && this.#revDirty)) {
return;
}
log.trace('Recording rev %s', this.#rev);
const data = { rev: this.#rev };
this.#revDirty = false;
try {
await this.#conn.put({
path: this.#path,
data,
});
}
catch (error) {
log.error({ error }, 'Failed to update rev');
this.#revDirty = true;
}
}
get rev() {
return __classPrivateFieldGet(this, _Metadata_rev, "f");
return this.#rev;
}
set rev(rev) {
if (__classPrivateFieldGet(this, _Metadata_rev, "f") === rev) {
if (this.#rev === rev) {
// No need to update

@@ -150,24 +174,6 @@ return;

log.trace('Updating local rev to %d', rev);
__classPrivateFieldSet(this, _Metadata_rev, rev, "f");
__classPrivateFieldSet(this, _Metadata_revDirty, true, "f");
this.#rev = rev;
this.#revDirty = true;
}
}
_Metadata_rev = new WeakMap(), _Metadata_revDirty = new WeakMap(), _Metadata_conn = new WeakMap(), _Metadata_path = new WeakMap(), _Metadata_initialized = new WeakMap(), _Metadata_controller = new WeakMap(), _Metadata_updates = new WeakMap(), _Metadata_instances = new WeakSet(), _Metadata_doUpdate = async function _Metadata_doUpdate() {
if (!(__classPrivateFieldGet(this, _Metadata_initialized, "f") && __classPrivateFieldGet(this, _Metadata_revDirty, "f"))) {
return;
}
log.trace('Recording rev %s', __classPrivateFieldGet(this, _Metadata_rev, "f"));
const data = { rev: __classPrivateFieldGet(this, _Metadata_rev, "f") };
__classPrivateFieldSet(this, _Metadata_revDirty, false, "f");
try {
await __classPrivateFieldGet(this, _Metadata_conn, "f").put({
path: __classPrivateFieldGet(this, _Metadata_path, "f"),
data,
});
}
catch (error) {
log.error({ error }, 'Failed to update rev');
__classPrivateFieldSet(this, _Metadata_revDirty, true, "f");
}
};
//# sourceMappingURL=Metadata.js.map

@@ -17,2 +17,6 @@ /**

*/
export {};
export declare function errorCode(error: {
code?: string;
status?: number;
statusCode?: number;
}): string | undefined;

@@ -73,2 +73,6 @@ /**

}
export function errorCode(error) {
const code = error.code ?? error.status ?? error.statusCode;
return code?.toString();
}
/**

@@ -75,0 +79,0 @@ * @internal

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

@@ -8,3 +8,3 @@ "main": "dist/index.js",

"engines": {
"node": ">=16.0.0"
"node": ">=18.0.0"
},

@@ -36,6 +36,2 @@ "repository": {

],
"prettier": {
"singleQuote": true,
"quoteProps": "consistent"
},
"ava": {

@@ -70,26 +66,26 @@ "failFast": false,

"devDependencies": {
"@ava/typescript": "^4.1.0",
"@oada/client": "^5.0.1",
"@tsconfig/node16": "^16.1.1",
"@ava/typescript": "^5.0.0",
"@oada/client": "^5.1.0",
"@tsconfig/node18": "^18.2.4",
"@types/debug": "^4.1.12",
"@types/node": "^16.18.80",
"@types/node": "^18.19.39",
"@types/object-assign-deep": "^0.4.3",
"@types/sinon": "^17.0.3",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"@typescript-eslint/parser": "^6.21.0",
"@yarnpkg/sdks": "^3.1.0",
"ava": "6.1.1",
"c8": "^9.1.0",
"eslint": "^8.56.0",
"@typescript-eslint/eslint-plugin": "^7.14.1",
"@typescript-eslint/parser": "^7.14.1",
"@yarnpkg/sdks": "^3.1.3",
"ava": "6.1.3",
"c8": "^10.1.2",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.1.0",
"eslint-config-xo": "^0.44.0",
"eslint-config-xo-typescript": "^2.0.0",
"eslint-config-xo": "^0.45.0",
"eslint-config-xo-typescript": "^4.0.0",
"eslint-formatter-pretty": "^6.0.1",
"eslint-import-resolver-node": "^0.3.9",
"eslint-plugin-array-func": "^5.0.1",
"eslint-plugin-ava": "^14.0.0",
"eslint-plugin-ava": "^15.0.1",
"eslint-plugin-escompat": "^3.4.0",
"eslint-plugin-eslint-comments": "^3.2.0",
"eslint-plugin-filenames": "^1.3.2",
"eslint-plugin-github": "^4.10.1",
"eslint-plugin-github": "^5.0.1",
"eslint-plugin-i18n-text": "^1.0.1",

@@ -99,16 +95,16 @@ "eslint-plugin-import": "^2.29.1",

"eslint-plugin-no-only-tests": "^3.1.0",
"eslint-plugin-no-secrets": "^0.8.9",
"eslint-plugin-no-secrets": "^1.0.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-notice": "^0.9.10",
"eslint-plugin-notice": "^1.0.0",
"eslint-plugin-optimize-regex": "^1.2.1",
"eslint-plugin-prettier": "^5.1.3",
"eslint-plugin-promise": "^6.1.1",
"eslint-plugin-regexp": "^2.2.0",
"eslint-plugin-security": "^2.1.0",
"eslint-plugin-sonarjs": "^0.24.0",
"eslint-plugin-unicorn": "^51.0.1",
"prettier": "^3.2.5",
"sinon": "^17.0.1",
"eslint-plugin-promise": "^6.2.0",
"eslint-plugin-regexp": "^2.6.0",
"eslint-plugin-security": "^3.0.1",
"eslint-plugin-sonarjs": "^1.0.3",
"eslint-plugin-unicorn": "^54.0.0",
"prettier": "^3.3.2",
"sinon": "^18.0.0",
"ts-node": "^10.9.2",
"typescript": "5.3.3"
"typescript": "5.5.2"
},

@@ -118,10 +114,10 @@ "dependencies": {

"abort-controller": "^3.0.0",
"debug": "^4.3.4",
"debug": "^4.3.5",
"eventemitter3": "^5.0.1",
"isomorphic-timers-promises": "^1.0.1",
"json-ptr": "^3.1.1",
"jsonpath-plus": "^8.0.0",
"jsonpath-plus": "^9.0.0",
"object-assign-deep": "^0.4.0",
"tslib": "^2.6.2",
"xksuid": "https://github.com/g12i/xksuid.git#fix-crypto-polyfill"
"tslib": "^2.6.3",
"xksuid": "https://github.com/g12i/xksuid.git#commit=22a21eb01331191feb62ea0a4e67d96d2e80f396"
},

@@ -136,5 +132,5 @@ "peerDependencies": {

},
"packageManager": "yarn@4.1.1",
"packageManager": "yarn@4.3.1",
"volta": {
"node": "20.11.0"
"node": "20.15.0"
},

@@ -141,0 +137,0 @@ "resolutions": {

@@ -19,3 +19,3 @@ # @oada/list-lib

```typescript
import { ChangeType, ListWatch } from '@oada/list-lib'
import { ChangeType, ListWatch, AssumeState } from '@oada/list-lib'

@@ -25,3 +25,10 @@ // See type definitions for all supported options

path: '/bookmarks/foo/list',
assertItem: /* assertion function to run on each item handled */,
conn: /* an @oada/client instance */,
name: /* string; key name of the oada-list-lib entry in the _meta doc*/,
resume: /* boolean; whether to track changes using a _meta/oada-list-lib/<name> entry */,
onNewList: /* AssumeState.New || AssumeState.Handled; Whether or not to handle existing
list items on startup. `New` means it will treat the list as new every time
it starts up and will attempt to process each item; `Handled` means it will
not process existing items. */,
})

@@ -28,0 +35,0 @@

@@ -34,2 +34,3 @@ /**

changeSym,
errorCode,
join,

@@ -74,2 +75,3 @@ } from './util.js';

*/
// eslint-disable-next-line @typescript-eslint/naming-convention
static readonly AssumeNew = AssumeState.New;

@@ -80,2 +82,3 @@ /**

*/
// eslint-disable-next-line @typescript-eslint/naming-convention
static readonly AssumeHandled = AssumeState.Handled;

@@ -97,7 +100,7 @@

* The unique name of this service/watch
*/
*/
readonly name;
/**
* The unique name of this service/watch
*/
*/
readonly timeout;

@@ -258,3 +261,3 @@

path: join(this.path, itemEvent.pointer),
timeout
timeout,
});

@@ -315,2 +318,3 @@ assertItem(item);

// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
default: {

@@ -388,6 +392,5 @@ assertNever(event, `Unknown event type ${event}`);

try {
await conn.head({ path, timeout});
await conn.head({ path, timeout });
} catch (error: unknown) {
// @ts-expect-error darn errors
if (error?.status === 403 || error?.status === 404 || error?.code === '403' || error?.code === '404') {
if (['403', '404'].includes(errorCode(error as Error) ?? '')) {
// Create it

@@ -424,2 +427,3 @@ await conn.put({ path, data: {}, timeout });

// eslint-disable-next-line @typescript-eslint/switch-exhaustiveness-check
default: {

@@ -464,3 +468,3 @@ assertNever(assume);

});
const listRev = Number((json as unknown as {_rev: number})._rev);
const listRev = Number((json as unknown as { _rev: number })._rev);
for await (const { value, pointer } of items) {

@@ -467,0 +471,0 @@ const itemChange = {

@@ -28,4 +28,4 @@ /**

import { errorCode, join } from './util.js';
import type { Conn } from './Options.js';
import { join } from './util.js';

@@ -163,29 +163,9 @@ const log = {

return true;
} catch {
// Create our metadata?
log.info('%s does not exist, posting new resource', this.#path);
const {
headers: { 'content-location': location },
} = await this.#conn.post({
path: '/resources/',
data: {},
contentType: 'application/json',
});
const {
headers: { 'x-oada-rev': revHeader },
} = await this.#conn.put({
path: this.#path,
data: { _id: location?.slice(1) },
});
} catch (error: unknown) {
if (errorCode(error as Error) !== '404') {
// Pass other errors causes up
throw new Error('List init error', { cause: error });
}
const rev = revHeader ? Number(revHeader) : undefined;
this.#rev = rev;
await this.#conn.put({
path: this.#path,
data: {
rev: rev!,
},
});
return false;
return await this.#createMeta();
} finally {

@@ -196,2 +176,34 @@ this.#initialized = true;

/**
* Create our metadata
*/
async #createMeta() {
log.info('%s does not exist, posting new resource', this.#path);
const {
headers: { 'content-location': location },
} = await this.#conn.post({
path: '/resources/',
data: {},
contentType: 'application/json',
});
const {
headers: { 'x-oada-rev': revHeader },
} = await this.#conn.put({
path: this.#path,
data: { _id: location?.slice(1) },
});
const rev = revHeader ? Number(revHeader) : undefined;
this.#rev = rev;
await this.#conn.put({
path: this.#path,
data: {
rev: rev!,
},
});
return false;
}
async #doUpdate() {

@@ -198,0 +210,0 @@ if (!(this.#initialized && this.#revDirty)) {

@@ -97,6 +97,5 @@ /**

/**
* Timeout for the watches created in OADAClient
* Timeout for the watches created in OADAClient
*/
timeout?: number;
}

@@ -103,0 +102,0 @@

@@ -20,1 +20,5 @@ /**

}
interface Error {
code?: string;
}

@@ -119,2 +119,11 @@ /**

export function errorCode(error: {
code?: string;
status?: number;
statusCode?: number;
}): string | undefined {
const code = error.code ?? error.status ?? error.statusCode;
return code?.toString();
}
/**

@@ -121,0 +130,0 @@ * @internal

{
"extends": "@tsconfig/node16",
"extends": "@tsconfig/node18",
"ts-node": {

@@ -4,0 +4,0 @@ "files": true

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