/*
Hyrrokkin - a library for building and running executable graphs
MIT License - Copyright (C) 2022-2025 Visual Topology Ltd
Permission is hereby granted, free of charge, to any person obtaining a copy of this software
and associated documentation files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or
substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
var hyrrokkin_engine = hyrrokkin_engine || {};
/**
* values stored in a collection can be of any type
*
* @typedef AnyValue
*/
/**
* A function which transforms a value
*
* @callback transformFunction
*
* @param {AnyValue} value the value to be transformed
*
* @return {AnyValue} a transformed value
*/
/**
* An async function which transforms a value
*
* @callback asyncTransformFunction
*
* @param {AnyValue} value the value to be transformed
*
* @return {Promise<AnyValue>} a transformed value
*/
hyrrokkin_engine.ValueCollectionIterator = class {
constructor(collection) {
this.collection = collection;
this.position = 0;
}
async next() {
if (this.position < this.collection.size()) {
let value = await this.collection.get(this.position);
this.position += 1;
return {"value": value};
}
return {"done":true};
}
}
/**
* Implement a collection of values.
*
* Call hyrrokkin_engine.ValueCollection.create_from_collections or hyrrokkin_engine.ValueCollection.create_source
* static methods to create an appropriate instance of this class.
*
* @type {hyrrokkin_engine.ValueCollection}
*
*/
hyrrokkin_engine.ValueCollection = class {
constructor() {
this.is_closed = false;
}
/**
* Create a collection by concatenating one or more collections
*
* @param {hyrrokkin_engine.ValueCollection[]} source_collections the collections to concatenate
* @param {transformFunction|asyncTransformFunction} transform_fn an optional function to transform values from the source collections into this collection
*
* @return {hyrrokkin_engine.ValueCollection}
*/
static
create_from_collections(source_collections, transform_fn) {
return new hyrrokkin_engine.TransformCollection(source_collections, transform_fn);
}
/**
* Create a collection based on a value store
*
* @param {hyrrokkin_engine.ValueStore} value_store an object implementing the ValueStore interface which is used to store values held in the collection
*
* @return {hyrrokkin_engine.ValueCollection} A new ValueCollection instance
*/
static
create_from_store(value_store) {
return new hyrrokkin_engine.StoreCollection(value_store);
}
/**
* Open an async iterator over this collection
*
* @return {hyrrokkin_engine.ValueCollectionIterator} an async iterator
*
* @throws {hrrokkin_engine.ValueCollectionClosedError} if the collection has been closed
*/
[Symbol.asyncIterator]() {
if (this.is_closed) {
throw new hyrrokkin_engine.hyrrokkin_engine.ValueCollectionClosedError();
} else {
return new hyrrokkin_engine.ValueCollectionIterator(this);
}
}
/**
* Get the size of this collection
*
* @return {int} the number of items in the collection
*
* @throws {hrrokkin_engine.ValueCollectionClosedError} if the collection has been closed
*/
size() {
}
/**
* Retrieve a value from the collection
*
* @param {int} index the index into the collection
* @return {Promise<*>} a promise that resolves to the returned value
*
* @throws {hrrokkin_engine.ValueCollectionClosedError} if the collection has been closed
* @throws {hrrokkin_engine.IndexError} if the index is out of range
*/
async get(index) {
}
/**
* Close the collection, freeing up any resources.
*
* @throws {hrrokkin_engine.ValueCollectionClosedError} if the collection has already been closed
*/
close() {
if (this.is_closed) {
throw new hyrrokkin_engine.hyrrokkin_engine.ValueCollectionClosedError();
}
this.is_closed = true;
}
}
/**
* An exception that is thrown when attempting to access a collection that has been closed
*
* @type {hyrrokkin_engine.ValueCollectionClosedError}
*/
hyrrokkin_engine.ValueCollectionClosedError = class extends Error {
constructor() {
super("Attempting to access a value from a ValueCollection that has been closed")
}
}
/**
* An exception that is thrown when attempting to access an item from a collection with an index that is out of range
*
* @type {hyrrokkin_engine.IndexError}
*/
hyrrokkin_engine.IndexError = class extends Error {
constructor(index, limit) {
super(`Index ${index} is out of range 0..${limit}`)
}
}
hyrrokkin_engine.ValueStore = class {
/**
* Retrieve a value from the store at a particular index
*
* @param index an index into the store
* @return {Promise<*>} a promise that resolves to the returned value
*
* @throws {hyrrokkin_engine.IndexError} if the index is out of range
*/
async get(index) {
}
/**
* Gets the number of values held in this store
*
* @return {int} the number of values
*/
get length() {
}
/**
* Close this store, freeing any resources
*/
close() {
}
}
hyrrokkin_engine.InMemoryValueStore = class extends hyrrokkin_engine.ValueStore {
/**
* A simple implementation of the ValueStore interface using an in-memory array of values
*
* @param {AnyValue[]} values an array of values
*/
constructor(values) {
super();
this.values = values;
}
async get(index) {
if (index >= 0 && index < this.length) {
return this.values[index];
}
throw new hyrrokkin_engine.IndexError(index, this.length);
}
get length() {
return this.values.length;
}
close() {
this.values = [];
}
}
hyrrokkin_engine.StoreCollection = class extends hyrrokkin_engine.ValueCollection {
constructor(value_store) {
super();
this.value_store = value_store;
}
size() {
if (this.is_closed) {
throw new hyrrokkin_engine.hyrrokkin_engine.ValueCollectionClosedError();
}
return this.value_store.length;
}
async get(pos) {
if (this.is_closed) {
throw new hyrrokkin_engine.hyrrokkin_engine.ValueCollectionClosedError();
}
return await this.value_store.get(pos);
}
close() {
super.close();
this.value_store.close();
}
}
hyrrokkin_engine.TransformCollection = class extends hyrrokkin_engine.ValueCollection {
constructor(source_collections, transform_fn) {
super();
this.source_collections = source_collections;
this.transform_fn = transform_fn;
}
size() {
let total_sz = 0;
for(let idx=0; idx<this.source_collections.length; idx++) {
total_sz += this.source_collections[idx].size();
}
return total_sz;
}
async get(pos) {
let index = pos;
if (this.is_closed) {
throw new hyrrokkin_engine.hyrrokkin_engine.ValueCollectionClosedError();
}
for(let idx=0; idx<this.source_collections.length; idx++) {
let collection = this.source_collections[idx];
let sz = collection.size();
if (pos < sz) {
let value = await collection.get(pos);
if (this.transform_fn) {
value = await this.transform_fn(value);
}
return value;
} else {
pos -= sz;
}
}
throw new hyrrokkin_engine.IndexError(index, this.size());
}
}