ixfx
Version:
Bundle of ixfx libraries
1,585 lines (1,584 loc) • 166 kB
TypeScript
import { t as IsEqual } from "./is-equal-BE9SbPVX.js";
import { f as ToString, i as Interval, r as IWithEntries } from "./types-1oz6G7XR.js";
import { A as transformMap, D as sortByValueProperty, E as sortByValue, O as toArray$1, S as mapToObjectTransform, T as some, _ as getOrGenerate, a as addValue$1, b as hasKeyValue, c as deleteByValueCompareMutate, d as findEntryByPredicate, f as findEntryByValue, g as getClosestIntegerKey, h as fromObject, i as addObjectEntriesMutate, j as zipKeyValue, k as toObject, l as filterValues, m as fromIterable, n as GetOrGenerateSync, o as addValueMutate, p as findValue, r as MergeReconcile, s as addValueMutator, t as GetOrGenerate, u as findBySomeKey, v as getOrGenerateSync, w as mergeByKey, x as mapToArray, y as hasAnyValue } from "./maps-DmzuqIRI.js";
import { H as Result } from "./index-DldIQ_ah.js";
import { n as Comparer } from "./comparers-Pedv6tO4.js";
import { n as SimpleEventEmitter } from "./index-DzASKzet.js";
//#region ../packages/collections/src/circular-array.d.ts
interface ICircularArray<V> extends Array<V> {
/**
* Returns true if the array has filled to capacity and is now
* recycling array indexes.
*/
get isFull(): boolean;
/**
* Returns a new Circular with item added
*
* Items are added at `pointer` position, which automatically cycles through available array indexes.
*
* @param value Thing to add
* @returns Circular with item added
*/
add(value: V): ICircularArray<V>;
get length(): number;
/**
* Returns the current add position of array.
*/
get pointer(): number;
}
/**
* A circular array keeps a maximum number of values, overwriting older values as needed. Immutable.
*
* `CircularArray` extends the regular JS array. Only use `add` to change the array if you want
* to keep the `CircularArray` behaviour.
*
* @example Basic functions
* ```js
* let a = new CircularArray(10);
* a = a.add(`hello`); // Because it's immutable, capture the return result of `add`
* a.isFull; // True if circular array is full
* a.pointer; // The current position in array it will write to
* ```
*
* Since it extends the regular JS array, you can access items as usual:
* @example Accessing
* ```js
* let a = new CircularArray(10);
* ... add some stuff ..
* a.forEach(item => // do something with item);
* ```
* @param capacity Maximum capacity before recycling array entries
* @return Circular array
*/
declare class CircularArray<V> extends Array {
#private;
constructor(capacity?: number);
/**
* Add to array
* @param value Thing to add
* @returns
*/
add(value: V): CircularArray<V>;
get pointer(): number;
get isFull(): boolean;
}
//#endregion
//#region ../packages/collections/src/events/types.d.ts
type EventItem = Readonly<{
/**
* Start point, inclusive
*/
start: number;
/**
* End point, exclusive
*/
end: number;
}>;
type EventItemAsDuration = Readonly<{
start: number;
duration: number;
}>;
type IdEventItem = EventItem & Readonly<{
id: string;
}>;
type SplitOptionsRelative = {
percentage: number;
};
type SplitOptionsAbsolute = {
start: number;
};
type SplitOptions = (SplitOptionsRelative | SplitOptionsAbsolute);
type IndexedEventItem = Readonly<{
event: EventItem;
index: number;
}>;
type EventInterval = Readonly<{
a: EventItem;
b: EventItem;
/**
* Interval between start points (B - A)
*/
startInterval: number;
/**
* Interval between end points (B - A)
*/
endInterval: number;
/**
* Interval between end of `a` and start of `b` (B.start - A.end)
* Note, this might be negative if a and b overlap
*/
betweenInterval: number;
indexA: number;
indexB: number;
}>;
type DefragmentOptions = Readonly<{
gap: number;
startAt: number;
}>;
//#endregion
//#region ../packages/collections/src/events/events-fns.d.ts
/**
* Sorts by start, such that 'start' values are ascending.
*
* Returns:
* 0 if A and B are have same start & end.
* positive if B is before A.
* negative if B is after A.
*
* If A and B have the same start point, they are secondarily sorted based on end time, with earlier end time considered "before" later end time.
*
* Use {@link CompareByStartOnly} to ignore end time and consider events equal if they share a `start`.
* @param a
* @param b
*/
declare const CompareByStart: Comparer<EventItem>;
declare const CompareByStartOnly: Comparer<EventItem>;
/**
* Sorts by end, such that 'end' values are ascending.
*
* Returns:
* 0 if A and B are have same start & end.
* Returns positive if B is before A.
* Returns negative if B is after A.
*
* If A and B share the same end, shorter items will come first (ie. those with higher start)
* @param a
* @param b
*/
declare const CompareByEnd: Comparer<EventItem>;
declare const CompareByEndOnly: Comparer<EventItem>;
/**
* Returns a new array of events ordered by their start time (ascending)
* @param events
* @returns Events ordered by start time
*/
declare function sortByStart(events: EventItem[]): EventItem[];
/**
* Returns a new array of events ordered by their end time (ascending)
* @param events
* @returns Events ordered by end time
*/
declare function sortByEnd(events: EventItem[]): EventItem[];
/**
* Yields every item in `sortedEvents` that has the specified `start` value
*
* Return item is a wrapped object consisting of the event as well as its index.
* ```js
* const events = [ { start: 1, end: 2}, { start: 5, end: 10 }, { start: 10, end: 12 }];
* const matched = [...itemsWithStart(events, 5)];
* // matched is [{ event: { start: 5, end: 10 }, index: 1 }]
* ```
* @param sortedEvents Sorted events
* @param start Start position
*/
declare function itemsWithStart(sortedEvents: EventItem[], start: number): Generator<IndexedEventItem>;
/**
* Yields every item in `eventsByEnd` that has the specified `end` value.
* Return item is a wrapped object consisting of the event as well as its index.
*
* The function expects that the input array has been sorted using {@link sortByEnd}, and therefore
* sorted by ascending end value.
*
* ```js
* const events = [ { start: 1, end: 2}, { start: 5, end: 10 }, { start: 10, end: 12 }];
* const matched = [...itemsWithEnd(events, 10)];
* // matched is [{ event: { start: 5, end: 10 }, index: 1 }]
* ```
* @param eventsByEnd Events sorted with {@link sortByEnd}
* @param end End position
*/
declare function itemsWithEnd(eventsByEnd: EventItem[], end: number): Generator<IndexedEventItem>;
/**
* Converts a collection of `IndexedEventItem` back into an array of `EventItem`, placing items at their original index.
*
* ```js
* // Get all items that start at position 5
* const itemsAtPosition = [...itemsWithStart(sortedEvents, 5)];
*
* // Make this into an array:
* const items = arrayFromItems(itemsAtPosition);
* ```
*
* By default, the `index` field is used to construct the array. If `ignoreIndexes` is set to _true_,
* the the returned array is constructed in the order of the input items, ignoring the `index` field. This can be useful if you just want to extract the events from a generator without caring about their original position.
* @param items
* @returns EventItems
*/
declare function arrayFromItems(items: Iterable<IndexedEventItem>, ignoreIndexes?: boolean): EventItem[];
/**
* Yields all events that overlap with `point`.
* By default event end is considered exclusive, meaning that if `point == event.end`, it is not considered overlapping.
* If `endInclusive` is true, event end is considered inclusive, and the aforementioned would be considered ovlerapping.
*
* By default start is inclusive.
*
* @param sortedEvents
* @param point
* @param endInclusive Whether event end is considered inclusive for determining overlap (default:false)
* @param startInclusive Whether event start is considered inclusive for determining overlap (default:true)
*/
declare function overlapping<T extends EventItem>(sortedEvents: T[], point: number, endInclusive?: boolean, startInclusive?: boolean): Generator<IndexedEventItem>;
/**
* Inserts space within `sortedEvents`. It does this by shifting events forward.
*
* If `start` overlaps with existing item(s), `overlappingPolicy` is used:
* - 'ignore': Event duration is not changed
* - 'stretch': Events that overlap are stretched by `length`.
*
* When considering overlap, both end is exclusive and start are considered exclusive.
*
* Eg, if we have the event `{ start: 5, end: 10 }`.
* - insertSpace(data, 5, 1, `ignore`); // Would not be considered overlapping, but event would be shifted to { start: 6, end: 11 }
* - insertSpace(data, 10, 1, `ignore`); // Would not be considered overlapping, event would remain { start: 5, end: 10 }
* - insertSpace(data, 5, 1, `stretch`); // Would be considered overlapping, event shifted to { start: 6, end: 11 }
* - insertSpace(data, 6, 1, `stretch`); // Would be considered overlapping, event would be stretched to { start: 5, end: 11 }
* @param sortedEvents
* @param start
* @param length
* @param overlappingPolicy
*/
declare function insertSpace<T extends EventItem>(sortedEvents: T[], start: number, length: number, overlappingPolicy: `ignore` | `stretch`): T[];
/**
* Punches a hole in `sortedEvents` which overlap `hole`.
* It does this by splitting/trimming events, or removing an event entirely it is fully covered by the hole.
*
* This will never shift events in time.
* @param sortedEvents
* @param hole
*/
declare function holepunch<T extends EventItem>(sortedEvents: T[], hole: EventItem): T[];
/**
* Returns _true_ if `item` has zero duration (start and end are the same), _false_ otherwise.
* ```js
* isEmpty({ start: 1, end: 1 }); // true
* isEmpty({ start: 1, end: 2 }); // false
* ```
* @param item
* @returns _true_ if `item` is empty.
*/
declare function isEmpty$2(item: EventItem): boolean;
/**
* Creates an `EventItem` from an `EventItemAsDuration` by calculating the end as start + duration.
* ```js
* fromDuration({ start: 1, duration: 2 }); // { start: 1, end: 3 }
* ```
*
* Copies additional properties to the return result.
* @param item
* @returns EventItem
*/
declare function fromDuration(item: EventItemAsDuration): EventItem;
/**
* Creats an `EventItemAsDuration` from an `EventItem` by calculating the duration as end - start.
* ```js
* toDuration({ start: 1, end: 3 }); // { start: 1, duration: 2 }
* ```
*
* Copies additional properties to the return result.
* @param item
* @returns EventItemAsDuration
*/
declare function toDuration(item: EventItem): EventItemAsDuration;
/**
* Returns the intervals between pairs of events.
*
* If `sortedEvents` has less than two events, yields nothing
* @param sortedEvents
*/
declare function intervals(sortedEvents: EventItem[]): Generator<EventInterval>;
declare function isValid(item: unknown): Result<EventItem, string>;
declare function isEventItem(item: unknown): item is EventItem;
/**
* Removes `toRemove` from `sortedEvents`.
*
* Consider {@link holepunch} if you want to create an empty hole in the events and maintain overall length of event series.
*
* After removing:
* - 'nothing': Gap is left, other items not affected
* - 'shuffle-following': Events after 'toRemove' are shifted back by duration of `toRemove`, maintaining their spacing after that
* - 'shuffle-leading': Events before 'toRemove' are shifted forward by duration of `toRemove`, maintaining their spacing before that
* - 'slice-following': Events after 'toRemove' are shifted back to start at `toRemoved.start`, maintaining their spacing after that
* - 'slice-leading': Events before 'toRemove' are shifted forward to end at `toRemoved.end`, maintaining their spacing before that
* @param sortedEvents
* @param toRemove
*/
declare function remove$2(sortedEvents: EventItem[], toRemove: EventItem, andThen: `nothing` | `shuffle-following` | `shuffle-leading` | `slice-following` | `slice-leading`): EventItem[];
/**
* Splits `event` into two events by either a percentange of duration or by a specific start position.
*
* If `options` has a `percentage` field, the split point is calculated as `event.start + (event.end - event.start) * percentage`.
* If `options` has a `start` field, the split point is simply that value.
*
* ```js
* splitEvent({ start: 0, end: 10 }, { percentage: 0.5 }); // [{ start: 0, end: 5 }, { start: 5, end: 10 }]
* splitEvent({ start: 0, end: 10 }, { start: 3 }); // [{ start: 0, end: 3 }, { start: 3, end: 10 }]
* ```
*
* Any other properties on `event` are copied to split events.
* @param event Input event
* @param options How to split
* @returns Split event
*/
declare function splitEvent(event: EventItem, options: SplitOptions): [a: EventItem, b: EventItem];
/**
* Applies `fn` to both `start` and `end` fields, returning a new event.
*
* Existing data on `event` is maintained.
*
* ```js
* applyToPosition( { start:1.2, end:2.4 }, v => Math.round(v)); // { start:1, end:2 }
* applyToPosition( { start:1, end:2 }, v => v*2); // { start:2, end:4 }
* ```
*
* Use {@link translate} if you just want to add an amount to start and end, instead of applying a custom function.
* @param event Input event
* @param fn Function to run over start and end
* @returns New event with `fn` applied to start and end
*/
declare function applyToPositions(event: EventItem, fn: (v: number) => number): EventItem;
/**
* Translates an event by adding `amount` to both `start` and `end`, returning a new event.
* ```js
* translate( { start:1, end:2 }, 3); // { start:4, end:5 }
* translate( { start:1, end:2 }, -1); // { start:0, end:1 }
* ```
*
* Existing data on `event` is maintained.
*
* Use {@link applyToPositions} if you want to apply a custom function to the start and end, instead of just adding an amount.
* @param event
* @param amount
* @returns New EventItem
*/
declare function translate<T extends EventItem>(event: T, amount: number): T;
/**
* Returns how `b` overlaps with `a`.
*
* Returns:
* - `none` if `b` does not overlap with `a`
* - `equal` if `b` has the same start and end as `a`
* - `full` if `b` is fully contained within `a` and `a` does not share a start/end
* - `full-border` if `b` is fully contained within `a` and `a` shares a start/end
* - `partial` if `b` overlaps with `a` but is not fully contained within it
*
* ```js
* compareRange({ start:2, end:4 }, { start:0, end:1 }); // 'none'
* compareRange({ start:2, end:4 }, { start: 2, end:4 }); // 'equal'
* compareRange({ start:2, end:4 }, { start: 3, end: 3 }); // 'full'
* compareRange({ start:2, end:4 }, { start: 3, end: 4 }); // 'full-border'
* compareRange({ start:2, end:4 }, { start: 1, end: 3 }); // 'partial'
* ```
* @param a
* @param b
*/
declare function compareRange<T extends EventItem>(a: T, b: T): `none` | `partial` | `full` | `full-border` | `equal`;
/**
* Lays out events end-to-end, removing gaps between them and having the first start at 0.
* Duration of events is maintained.
* @param sortedEvents
*/
declare function defragment(sortedEvents: EventItem[], options?: Partial<DefragmentOptions>): EventItem[];
declare function createFromStarts(starts: number[], duration: number, idPrefix?: string): IdEventItem[];
/**
* Gets the range of `events`: the smallest 'start' and the largest 'end'.
*
* If there are gaps between events, this is still included in the range. Use {@link sumDuration}
* to add up the duration of all events as if they are stacked end-to-end.
* @param events
* @returns Range of events
*/
declare function computeRange(events: EventItem[]): {
start: number;
end: number;
};
/**
* Returns the total duration of all events. Doesn't take into account
* the spacing between events, just sums the duration of each one.
*
* Use {@link computeRange} if you want to calculate the min and max starting points.
* @param events
* @returns Duration
*/
declare function sumDuration(events: EventItem[]): number;
//#endregion
//#region ../packages/collections/src/types.d.ts
/**
* Key-value pairs in an array
* @see {@link ObjectKeys}
*/
type ArrayKeys<K, V> = readonly (readonly [key: K, value: V])[];
/**
* Key-value pairs in object form
* @see {@link ArrayKeys}
*/
type ObjectKeys<K, V> = readonly {
readonly key: K;
readonly value: V;
}[];
declare function isObjectKeys<K, V>(kvs: EitherKey<K, V>): kvs is ObjectKeys<K, V>;
/**
* Type that represents key-values in object or array form
*/
type EitherKey<K, V> = ArrayKeys<K, V> | ObjectKeys<K, V>;
/**
* A table value or _undefined_
*/
type TableValue<V> = V | undefined;
/**
* A row of table values
*/
type TableRow<V> = TableValue<V>[];
//#endregion
//#region ../packages/collections/src/map/imap-base.d.ts
interface IMapBase<K, V> {
/**
* Gets an item by key
* @example
* ```js
* const item = map.get(`hello`);
* ```
* @param key
*/
get(key: K): V | undefined;
/**
* Returns _true_ if map contains key
* @example
* ```js
* if (map.has(`hello`)) ...
* ```
* @param key
*/
has(key: K): boolean;
/**
* Returns _true_ if map is empty
*/
isEmpty(): boolean;
/**
* Iterates over entries (consisting of [key,value])
* @example
* ```js
* for (const [key, value] of map.entries()) {
* // Use key, value...
* }
* ```
*/
entries(): IterableIterator<readonly [K, V]>;
values(): IterableIterator<V>;
}
//#endregion
//#region ../packages/collections/src/map/map.d.ts
/**
* An immutable map. Rather than changing the map, functions like `add` and `delete`
* return a new map reference which must be captured.
*
* Immutable data is useful because as it gets passed around your code, it never
* changes from underneath you. You have what you have.
*
* @example
* ```js
* let m = map(); // Create
* let m2 = m.set(`hello`, `samantha`);
* // m is still empty, only m2 contains a value.
* ```
*
* @typeParam K - Type of map keys. Typically `string`
* @typeParam V - Type of stored values
*/
interface IMapImmutable<K, V> extends IMapBase<K, V> {
/**
* Adds one or more items, returning the changed map.
*
* Can add items in the form of `[key,value]` or `{key, value}`.
* @example These all produce the same result
* ```js
* map.set(`hello`, `samantha`);
* map.add([`hello`, `samantha`]);
* map.add({key: `hello`, value: `samantha`})
* ```
* @param itemsToAdd
*/
add(...itemsToAdd: EitherKey<K, V>): IMapImmutable<K, V>;
/**
* Deletes an item by key, returning the changed map
* @param key
*/
delete(key: K): IMapImmutable<K, V>;
/**
* Returns an empty map
*/
clear(): IMapImmutable<K, V>;
/**
* Sets `key` to be `value`, overwriting anything existing.
* Returns a new map with added key.
* @param key
* @param value
*/
set(key: K, value: V): IMapImmutable<K, V>;
}
/**
* Returns an {@link IMapImmutable}.
* Use {@link Maps.mutable} as a mutable alternatve.
*
* @example Basic usage
* ```js
* // Creating
* let m = map();
* // Add
* m = m.set("name", "sally");
* // Recall
* m.get("name");
* ```
*
* @example Enumerating
* ```js
* for (const [key, value] of map.entries()) {
* console.log(`${key} = ${value}`);
* }
* ```
*
* @example Overview
* ```js
* // Create
* let m = map();
* // Add as array or key & value pair
* m = m.add(["name" , "sally"]);
* m = m.add({ key: "name", value: "sally" });
* // Add using the more typical set
* m = m.set("name", "sally");
* m.get("name"); // "sally";
* m.has("age"); // false
* m.has("name"); // true
* m.isEmpty; // false
* m = m.delete("name");
* m.entries(); // Iterator of key value pairs
* ```
*
* Since it is immutable, `add()`, `delete()` and `clear()` return a new version with change.
*
* @param dataOrMap Optional initial data in the form of an array of `{ key: value }` or `[ key, value ]`
*/
declare const immutable$3: <K, V>(dataOrMap?: ReadonlyMap<K, V> | EitherKey<K, V>) => IMapImmutable<K, V>;
//#endregion
//#region ../packages/collections/src/table.d.ts
/**
* Stores values in a table of rows (vertical) and columns (horizontal)
*/
declare class Table<V> {
#private;
rows: TableRow<V>[];
rowLabels: string[];
colLabels: string[];
/**
* Keep track of widest row
*/
columnMaxLength: number;
/**
* Gets the label for a given column index,
* returning _undefined_ if not found.
*
* Case-sensitive
* @param label Label to seek
* @returns Index of column, or _undefined_ if not found
*/
getColumnLabelIndex(label: string): number | undefined;
/**
* Gets the label for a given row index,
* returning _undefined_ if not found.
*
* Case-sensitive
* @param label Label to seek
* @returns Index of row, or _undefined_ if not found
*/
getRowLabelIndex(label: string): number | undefined;
/**
* Dumps the values of the table to the console
*/
print(): void;
/**
* Return a copy of table as nested array
*
* ```js
* const t = new Table();
* // add stuff
* // ...
* const m = t.asArray();
* for (const row of m) {
* for (const colValue of row) {
* // iterate over all column values for this row
* }
* }
* ```
*
* Alternative: get value at row Y and column X
* ```js
* const value = m[y][x];
* ```
* @returns
*/
asArray(): (V | undefined)[][];
/**
* Return the number of rows
*/
get rowCount(): number;
/**
* Return the maximum number of columns in any row
*/
get columnCount(): number;
/**
* Iterates over the table row-wise, in object format.
* @see {@link rowsWithLabelsArray} to get rows in array format
*/
rowsWithLabelsObject(): Generator<object | undefined, void, unknown>;
/**
* Iterates over each row, including the labels if available
* @see {@link rowsWithLabelsObject} to get rows in object format
*/
rowsWithLabelsArray(): Generator<[label: string | undefined, value: V | undefined][] | undefined, void, unknown>;
/**
* Assign labels to columns
* @param labels
*/
labelColumns(...labels: string[]): void;
/**
* Assign label to a specific column
* First column has an index of 0
* @param columnIndex
* @param label
*/
labelColumn(columnIndex: number, label: string): void;
/**
* Label rows
* @param labels Labels
*/
labelRows(...labels: string[]): void;
/**
* Assign label to a specific row
* First row has an index of 0
* @param rowIndex
* @param label
*/
labelRow(rowIndex: number, label: string): void;
/**
* Adds a new row
* @param data Columns
*/
appendRow(...data: TableValue<V>[]): TableRow<V>;
/**
* Gets a row along with labels, as an array
* @param rowIndex
* @returns
*/
getRowWithLabelsArray(rowIndex: number): [label: string | undefined, value: V | undefined][] | undefined;
/**
* Return a row of objects. Keys use the column labels.
*
* ```js
* const row = table.getRowWithLabelsObject(10);
* // eg:
* // [{ colour: red, size: 10}, { colour: blue, size: 20 }]
* ```
* @param rowIndex
* @returns
*/
getRowWithLabelsObject(rowIndex: number): object | undefined;
/**
* Gets a copy of values at given row, specified by index or label
* @param row
* @returns Returns row or throws an error if label or index not found
*/
row(row: number | string): readonly (V | undefined)[] | undefined;
/**
* Set the value of row,columm.
* Row is created if it doesn't exist, with the other column values being _undefined_
* @param row Index or label
* @param column Column
* @param value Value to set at row,column
*/
set(row: number | string, column: number | string, value: V | undefined): void;
/**
* Gets the value at a specified row and column.
* Throws an error if coordinates are out of range or missing.
* @param row Row index or label
* @param column Column index or label
* @returns
*/
get(row: number | string, column: number | string): TableValue<V>;
/**
* Set all the columns of a row to a specified value.
*
* By default, sets the number of columns corresponding to
* the table's maximum column length. To set an arbitrary
* length of the row, use `length`
* @param row Index or label of row
* @param length How wide the row is. If unset, uses the current maximum width of rows.
* @param value Value to set
*/
setRow(row: number | string, value: V | undefined, length?: number): TableRow<V>;
}
declare namespace directed_graph_d_exports {
export { ConnectOptions$1 as ConnectOptions, DirectedGraph, DistanceCompute, Edge$1 as Edge, Vertex$1 as Vertex, adjacentVertices$1 as adjacentVertices, areAdjacent, bfs, clone, connect$1 as connect, connectTo$1 as connectTo, connectWithEdges$1 as connectWithEdges, createVertex$1 as createVertex, dfs, disconnect, distance, distanceDefault, dumpGraph$1 as dumpGraph, edges, get, getCycles, getOrCreate$1 as getOrCreate, getOrFail, graph$1 as graph, graphFromVertices, hasKey, hasNoOuts, hasOnlyOuts, hasOut, isAcyclic, pathDijkstra, toAdjacencyMatrix$1 as toAdjacencyMatrix, topologicalSort, transitiveReduction, updateGraphVertex$1 as updateGraphVertex, vertexHasOut, vertices };
}
type DistanceCompute = (graph: DirectedGraph, edge: Edge$1) => number;
/**
* Vertex. These are the _nodes_ of the graph. Immutable.
*
* They keep track of all of their outgoing edges, and
* a unique id.
*
* Ids are used for accessing/updating vertices as well as in the
* {@link Edge} type. They must be unique.
*/
type Vertex$1 = Readonly<{
out: readonly Edge$1[];
id: string;
}>;
/**
* Edge. Immutable.
*
* Only encodes the destination vertex. The from
* is known since edges are stored on the from vertex.
*/
type Edge$1 = Readonly<{
/**
* Vertex id edge connects to (ie. destination)
*/
id: string;
/**
* Optional weight of edge
*/
weight?: number;
}>;
/**
* Create a vertex with given id
* @param id
* @returns
*/
declare const createVertex$1: (id: string) => Vertex$1;
/**
* Options for connecting vertices
*/
type ConnectOptions$1 = Readonly<{
/**
* From, or source of connection
*/
from: string;
/**
* To, or destination of connection. Can be multiple vertices for quick use
*/
to: string | string[];
/**
* If true, edges in opposite direction are made as well
*/
bidi?: boolean;
/**
* Weight for this connection (optional)
*/
weight?: number;
}>;
/**
* Directed graph. Immutable.
*
* Consists of {@link Vertex|vertices}, which all have zero or more outgoing {@link Edge|Edges}.
*/
type DirectedGraph = Readonly<{
vertices: IMapImmutable<string, Vertex$1>;
}>;
/**
* Returns _true_ if graph contains `key`.
*
* ```js
* // Same as
* g.vertices.has(key)
* ```
* @param graph
* @param key
* @returns
*/
declare function hasKey(graph: DirectedGraph, key: string): boolean;
/**
* Returns {@link Vertex} under `key`, or _undefined_
* if not found.
*
* ```js
* // Same as
* g.vertices.get(key)
* ```
* @param graph
* @param key
* @returns
*/
declare function get(graph: DirectedGraph, key: string): Vertex$1 | undefined;
/**
* Returns the graph connections as an adjacency matrix
* @param graph
* @returns
*/
declare function toAdjacencyMatrix$1(graph: DirectedGraph): Table<boolean>;
/**
* Return a string representation of the graph for debug inspection
* @param graph
* @returns
*/
declare const dumpGraph$1: (graph: DirectedGraph | Iterable<Vertex$1>) => string;
/**
* Returns the weight of an edge, or 1 if undefined.
* @param graph
* @param edge
* @returns
*/
declare const distance: (graph: DirectedGraph, edge: Edge$1) => number;
/**
* Iterate over all the edges in the graph
* @param graph
*/
declare function edges(graph: DirectedGraph): Generator<Readonly<{
/**
* Vertex id edge connects to (ie. destination)
*/
id: string;
/**
* Optional weight of edge
*/
weight?: number;
}>, void, unknown>;
/**
* Iterate over all the vertices of the graph
* @param graph
*/
declare function vertices(graph: DirectedGraph): Generator<Readonly<{
out: readonly Edge$1[];
id: string;
}>, void, unknown>;
/**
* Iterate over all the vertices connected to `context` vertex
* @param graph Graph
* @param context id or Vertex.
* @returns
*/
declare function adjacentVertices$1(graph: DirectedGraph, context: Vertex$1 | string | undefined): Generator<Readonly<{
out: readonly Edge$1[];
id: string;
}>, void, unknown>;
/**
* Returns _true_ if `vertex` has an outgoing connection to
* the supplied id or vertex.
*
* If `vertex` is undefined, _false_ is returned.
* @param vertex From vertex
* @param outIdOrVertex To vertex
* @returns
*/
declare const vertexHasOut: (vertex: Vertex$1, outIdOrVertex: string | Vertex$1) => boolean;
/**
* Returns _true_ if `vertex` has no outgoing connections
* @param graph
* @param vertex
* @returns
*/
declare const hasNoOuts: (graph: DirectedGraph, vertex: string | Vertex$1) => boolean;
/**
* Returns _true_ if `vertex` only has the given list of vertices.
* Returns _false_ early if the length of the list does not match up with `vertex.out`
* @param graph
* @param vertex
* @param outIdOrVertex
* @returns
*/
declare const hasOnlyOuts: (graph: DirectedGraph, vertex: string | Vertex$1, ...outIdOrVertex: (string | Vertex$1)[]) => boolean;
/**
* Returns _true_ if `vertex` has an outgoing connection to the given vertex.
* @param graph
* @param vertex
* @param outIdOrVertex
* @returns
*/
declare const hasOut: (graph: DirectedGraph, vertex: string | Vertex$1, outIdOrVertex: string | Vertex$1) => boolean;
/**
* Gets a vertex by id, creating it if it does not exist.
* @param graph
* @param id
* @returns
*/
declare const getOrCreate$1: (graph: DirectedGraph, id: string) => Readonly<{
graph: DirectedGraph;
vertex: Vertex$1;
}>;
/**
* Gets a vertex by id, throwing an error if it does not exist
* @param graph
* @param id
* @returns
*/
declare const getOrFail: (graph: DirectedGraph, id: string) => Vertex$1;
/**
* Updates a vertex by returning a mutated graph
* @param graph Graph
* @param vertex Newly changed vertex
* @returns
*/
declare const updateGraphVertex$1: (graph: DirectedGraph, vertex: Vertex$1) => DirectedGraph;
/**
* Default distance computer. Uses `weight` property of edge, or `1` if not found.
* @param graph
* @param edge
* @returns
*/
declare const distanceDefault: (graph: DirectedGraph, edge: Edge$1) => number;
/**
* Returns a mutation of `graph`, with a given edge removed.
*
* If edge was not there, original graph is returned.
* @param graph
* @param from
* @param to
* @returns
*/
declare function disconnect(graph: DirectedGraph, from: string | Vertex$1, to: string | Vertex$1): DirectedGraph;
/**
* Make a connection between two vertices with a given weight.
* It returns the new graph as wll as the created edge.
* @param graph
* @param from
* @param to
* @param weight
* @returns
*/
declare function connectTo$1(graph: DirectedGraph, from: string, to: string, weight?: number): {
graph: DirectedGraph;
edge: Edge$1;
};
/**
* Connect from -> to. Same as {@link connectWithEdges}, but this version just returns the graph.
*
* By default unidirectional, meaning a connection is made only from->to. Use `bidi` option to set a bidirection connection, adding also to->from.
*
* Returns a result of `{ graph, edges }`, where `graph` is the new {@link DirectedGraph} and `edges`
* is an array of {@link Edge Edges}. One for unidirectional, or two for bidirectional.
* @param graph
* @param options
* @returns
*/
declare function connect$1(graph: DirectedGraph, options: ConnectOptions$1): DirectedGraph;
/**
* Connect from -> to. Same as {@link connect} except you get back the edges as well.
*
* By default unidirectional, meaning a connection is made only from->to. Use `bidi` option to set a bidirection connection, adding also to->from.
*
* Returns a result of `{ graph, edges }`, where `graph` is the new {@link DirectedGraph} and `edges`
* is an array of {@link Edge Edges}. One for unidirectional, or two for bidirectional.
* @param graph
* @param options
* @returns
*/
declare function connectWithEdges$1(graph: DirectedGraph, options: ConnectOptions$1): {
graph: DirectedGraph;
edges: Edge$1[];
};
/**
* Returns _true_ if a->b or b->a
* @param graph
* @param a
* @param b
* @returns
*/
declare function areAdjacent(graph: DirectedGraph, a: Vertex$1, b: Vertex$1): true | undefined;
/**
* Iterates over vertices from a starting vertex in an bread-first-search
* @param graph
* @param startIdOrVertex
* @param targetIdOrVertex
* @returns
*/
declare function bfs(graph: DirectedGraph, startIdOrVertex: string | Vertex$1, targetIdOrVertex?: string | Vertex$1): Generator<Readonly<{
out: readonly Edge$1[];
id: string;
}>, void, unknown>;
/**
* Iterates over vertices from a starting vertex in an depth-first-search
* @param graph
* @param startIdOrVertex
*/
declare function dfs(graph: DirectedGraph, startIdOrVertex: string | Vertex$1): Generator<Readonly<{
out: readonly Edge$1[];
id: string;
}>, void, unknown>;
/**
* Compute shortest distance from the source vertex to the rest of the graph.
* @param graph
* @param sourceOrId
* @returns
*/
declare const pathDijkstra: (graph: DirectedGraph, sourceOrId: Vertex$1 | string) => {
distances: Map<string, number>;
previous: Map<string, Readonly<{
out: readonly Edge$1[];
id: string;
}> | null>;
pathTo: (id: string) => Edge$1[];
};
/**
* Clones the graph. Uses shallow clone, because it's all immutable
* @param graph
* @returns
*/
declare const clone: (graph: DirectedGraph) => DirectedGraph;
/**
* Create a graph
* ```js
* let g = graph();
* ```
*
* Can optionally provide initial connections:
* ```js
* let g = graph(
* { from: `a`, to: `b` },
* { from: `b`, to: `c` }
* )
* ```
* @param initialConnections
* @returns
*/
declare const graph$1: (...initialConnections: ConnectOptions$1[]) => DirectedGraph;
/**
* Returns _true_ if the graph contains is acyclic - that is, it has no loops
* @param graph
*/
declare function isAcyclic(graph: DirectedGraph): boolean;
/**
* Topological sort using Kahn's algorithm.
* Returns a new graph that is sorted
* @param graph
*/
declare function topologicalSort(graph: DirectedGraph): DirectedGraph;
/**
* Create a graph from an iterable of vertices
* @param vertices
* @returns
*/
declare function graphFromVertices(vertices: Iterable<Vertex$1>): DirectedGraph;
/**
* Get all the cycles ('strongly-connected-components') within the graph
* [Read more](https://en.wikipedia.org/wiki/Strongly_connected_component)
* @param graph
* @returns
*/
declare function getCycles(graph: DirectedGraph): Vertex$1[][];
/**
* Returns a new graph which is transitively reduced.
* That is, redundant edges are removed
* @param graph
* @returns
*/
declare function transitiveReduction(graph: DirectedGraph): Readonly<{
vertices: IMapImmutable<string, Vertex$1>;
}>;
declare namespace undirected_graph_d_exports {
export { ConnectOptions, Edge, Graph, Vertex, adjacentVertices, connect, connectTo, connectWithEdges, createVertex, dumpGraph, edgesForVertex, getConnection, getOrCreate, graph, hasConnection, toAdjacencyMatrix, updateGraphVertex };
}
type Vertex = Readonly<{
id: string;
}>;
type Edge = Readonly<{
a: string;
b: string;
weight?: number;
}>;
type Graph = Readonly<{
edges: readonly Edge[];
vertices: IMapImmutable<string, Vertex>;
}>;
type ConnectOptions = Readonly<{
a: string;
b: string | string[];
weight?: number;
}>;
declare const createVertex: (id: string) => Vertex;
declare const updateGraphVertex: (graph: Graph, vertex: Vertex) => Graph;
declare const getOrCreate: (graph: Graph, id: string) => Readonly<{
graph: Graph;
vertex: Vertex;
}>;
/**
* Returns _true/false_ if there is a connection between `a` and `b` in `graph`.
* Use {@link getConnection} if you want to the edge.
* @param graph Graph to search
* @param a
* @param b
* @returns _true_ if edge exists
*/
declare const hasConnection: (graph: Graph, a: string | Vertex, b: string | Vertex) => boolean;
/**
* Gets the connection, if it exists between `a` and `b` in `graph`.
* If it doesn't exist, _undefined_ is returned.
* Use {@link hasConnection} for a simple true/false if edge exists.
* @param graph Graph
* @param a
* @param b
* @returns
*/
declare const getConnection: (graph: Graph, a: string | Vertex, b: string | Vertex) => Edge | undefined;
/**
* Connects A with B, returning the changed graph and created edge.
* If the connection already exists, the original graph & edge is returned.
* @param graph
* @param a
* @param b
* @param weight
* @returns
*/
declare function connectTo(graph: Graph, a: string, b: string, weight?: number): {
graph: Graph;
edge: Edge;
};
/**
* Makes a connection between `options.a` and one or more nodes in `options.b`.
* Same as {@link connectWithEdges} but only the {@link Graph} is returned.
*
* ```js
* let g = graph(); // Create an empty graph
* // Make a connection between `red` and `orange`
* g = connect(g, { a: `red`, b: `orange` });
*
* // Make a connection between `red` and `orange as well as `red` and `yellow`.
* g = connect(g, { a: `red`, b: [`orange`, `yellow`] })
* ```
* @param graph Initial graph
* @param options Options
*/
declare function connect(graph: Graph, options: ConnectOptions): Graph;
/**
* Makes a connection between `options.a` and one or more nodes in `options.b`.
* Same as {@link connect} but graph and edges are returned.
*
* ```js
* let g = graph(); // Create an empty graph
*
* // Make a connection between `red` and `orange`
* result = connectWithEdges(g, { a: `red`, b: `orange` });
*
* // Make a connection between `red` and `orange as well as `red` and `yellow`.
* result = connectWithEdges(g, { a: `red`, b: [`orange`, `yellow`] })
* ```
* @param graph Initial graph
* @param options Options
*/
declare function connectWithEdges(graph: Graph, options: ConnectOptions): {
graph: Graph;
edges: Edge[];
};
declare const graph: (...initialConnections: ConnectOptions[]) => Graph;
declare function toAdjacencyMatrix(graph: Graph): Table<boolean>;
/**
* Return a string representation of the graph for debug inspection
* @param graph
* @returns
*/
declare const dumpGraph: (graph: Graph) => string;
/**
* Iterate over all the vertices connectd to `context` vertex
*
* If `context` is _undefined_, returns nothing
* @param graph Graph
* @param context id or Vertex
* @returns
*/
declare function adjacentVertices(graph: Graph, context: Vertex | string | undefined): Generator<Readonly<{
id: string;
}>, void, unknown>;
/**
* Get all the edges for a vertex.
*
* ```js
* // Iterate all edges for vertex with id '0'
* for (const edge of edgesForVertex(graph, '0')) {
* }
* ```
*
* If the vertex has no edges, no values are returned. If the vertex was not found in the graph, an error is thrown.
* @throws Throws an error if `context` was not found, if it's _undefined_ or `graph` is invalid.
* @param graph
* @param context
* @returns
*/
declare function edgesForVertex(graph: Graph, context: Vertex | string | undefined): Generator<Readonly<{
a: string;
b: string;
weight?: number;
}>, void, unknown>;
declare namespace index_d_exports$6 {
export { directed_graph_d_exports as Directed, undirected_graph_d_exports as Undirected };
}
//#endregion
//#region ../packages/collections/src/map/expiring-map.d.ts
/**
* Expiring map options
*/
type Opts = {
/**
* Capacity limit
*/
readonly capacity?: number;
/**
* Policy for evicting items if capacity is reached
*/
readonly evictPolicy?: `none` | `oldestGet` | `oldestSet`;
/**
* Automatic deletion policy.
* none: no automatic deletion (default)
* get/set: interval based on last get/set
* either: if either interval has elapsed
*/
readonly autoDeletePolicy?: `none` | `get` | `set` | `either`;
/**
* Automatic deletion interval
*/
readonly autoDeleteElapsedMs?: number;
};
/**
* Event from the ExpiringMap
*/
type ExpiringMapEvent<K, V> = {
readonly key: K;
readonly value: V;
};
type ExpiringMapEvents<K, V> = {
/**
* Fires when an item is removed due to eviction
* or automatic expiry
*/
readonly expired: ExpiringMapEvent<K, V>;
/**
* Fires when a item with a new key is added
*/
readonly newKey: ExpiringMapEvent<K, V>;
/**
* Fires when an item is manually removed,
* removed due to eviction or automatic expiry
*/
readonly removed: ExpiringMapEvent<K, V>;
};
/**
* Create a ExpiringMap instance
* @param options Options when creating map
* @returns
*/
declare const create$2: <K, V>(options?: Opts) => ExpiringMap<K, V>;
/***
* A map that can have a capacity limit. The elapsed time for each get/set
* operation is maintained allowing for items to be automatically removed.
* `has()` does not affect the last access time.
*
* By default, it uses the `none` eviction policy, meaning that when full
* an error will be thrown if attempting to add new keys.
*
* Eviction policies:
* `oldestGet` removes the item that hasn't been accessed the longest,
* `oldestSet` removes the item that hasn't been updated the longest.
*
* ```js
* const map = new ExpiringMap();
* map.set(`fruit`, `apple`);
*
* // Remove all entries that were set more than 100ms ago
* map.deleteWithElapsed(100, `set`);
* // Remove all entries that were last accessed more than 100ms ago
* map.deleteWithElapsed(100, `get`);
* // Returns the elapsed time since `fruit` was last accessed
* map.elapsedGet(`fruit`);
* // Returns the elapsed time since `fruit` was last set
* map.elapsedSet(`fruit`);
* ```
*
* Last set/get time for a key can be manually reset using {@link touch}.
*
*
* Events:
* * 'expired': when an item is automatically removed.
* * 'removed': when an item is manually or automatically removed due to expiry. Note: does not fire when .clear() is called
* * 'newKey': when a new key is added
*
* ```js
* map.addEventListener(`expired`, evt => {
* const { key, value } = evt;
* });
* ```
* The map can automatically remove items based on elapsed intervals.
*
* @example
* Automatically delete items that haven't been accessed for one second
* ```js
* const map = new ExpiringMap({
* autoDeleteElapsed: 1000,
* autoDeletePolicy: `get`
* });
* ```
*
* @example
* Automatically delete the oldest item if we reach a capacity limit
* ```js
* const map = new ExpiringMap({
* capacity: 5,
* evictPolicy: `oldestSet`
* });
* ```
* @typeParam K - Type of keys
* @typeParam V - Type of values
*/
declare class ExpiringMap<K, V> extends SimpleEventEmitter<ExpiringMapEvents<K, V>> {
#private;
private capacity;
private store;
private evictPolicy;
private autoDeleteElapsedMs;
private autoDeletePolicy;
private autoDeleteTimer;
private disposed;
constructor(opts?: Opts);
dispose(): void;
/**
* Returns the number of keys being stored.
*/
get keyLength(): number;
entries(): IterableIterator<[k: K, v: V]>;
values(): IterableIterator<V>;
keys(): IterableIterator<K>;
/**
* Returns the elapsed time since `key`
* was set. Returns _undefined_ if `key`
* does not exist
*/
elapsedSet(key: K): number | undefined;
/**
* Returns the elapsed time since `key`
* was accessed. Returns _undefined_ if `key`
* does not exist
*/
elapsedGet(key: K): number | undefined;
/**
* Returns true if `key` is stored.
* Does not affect the key's last access time.
* @param key
* @returns
*/
has(key: K): boolean;
/**
* Gets an item from the map by key, returning
* undefined if not present
* @param key Key
* @returns Value, or undefined
*/
get(key: K): V | undefined;
/**
* Deletes the value under `key`, if present.
*
* Returns _true_ if something was removed.
* @param key
* @returns
*/
delete(key: K): boolean;
/**
* Clears the contents of the map.
* Note: does not fire `removed` event
*/
clear(): void;
/**
* Updates the lastSet/lastGet time for a value
* under `key`. If key was not found, nothing happens.
*
* Returns _false_ if key was not found
* @param key
* @returns
*/
touch(key: K): boolean;
private findEvicteeKey;
/**
* Deletes all values where elapsed time has past
* for get/set or either.
* ```js
* // Delete all keys (and associated values) not accessed for a minute
* em.deleteWithElapsed({mins:1}, `get`);
* // Delete things that were set 1s ago
* em.deleteWithElapsed(1000, `set`);
* ```
*
* @param interval Interval
* @param property Basis for deletion 'get','set' or 'either'
* @returns Items removed
*/
deleteWithElapsed(interval: Interval, property: `get` | `set` | `either`): [k: K, v: V][];
/**
* Sets the `key` to be `value`.
*
* If the key already exists, it is updated.
*
* If the map is full, according to its capacity,
* another value is selected for removal.
* @param key
* @param value
* @returns
*/
set(key: K, value: V): void;
}
//#endregion
//#region ../packages/collections/src/map/imap-of.d.ts
interface IMapOf<V> {
/**
* Iterates over all keys
*/
keys(): IterableIterator<string>;
/**
* Iterates over all values stored under `key`
* @param key
*/
valuesFor(key: string): IterableIterator<V>;
/**
* Returns a copy of all values under key as an arry
* @param key
*/
/**
* Iterates over all values, regardless of key.
* Same value may re-appear if it's stored under different keys.
*/
valuesFlat(): IterableIterator<V>;
/**
* Iterates over key-value pairs.
* Unlike a normal map, the same key may appear several times.
*/
entriesFlat(): IterableIterator<readonly [key: string, value: V]>;
entries(): IterableIterator<[key: string, value: V[]]>;
/**
* Iteates over all keys and the count of values therein
*/
keysAndCounts(): IterableIterator<readonly [string, number]>;
/**
* Returns _true_ if `value` is stored under `key`.
*
* @param key Key
* @param value Value
*/
hasKeyValue(key: string, value: V, eq?: IsEqual<V>): boolean;
/**
* Returns _true_ if `key` has any values
* @param key
*/
has(key: string): boolean;
/**
* Returns _true_ if the map is empty
*/
get isEmpty(): boolean;
/**
* Returns the number of values stored under `key`, or _0_ if `key` is not present.
* @param key Key
*/
count(key: string): number;
/**
* Finds the first key where value is stored.
* Note: value could be stored in multiple keys
* @param value Value to seek
* @returns Key, or undefined if value not found
*/
firstKeyByValue(value: V, eq?: IsEqual<V>): string | undefined;
}
//#endregion
//#region ../packages/collections/src/map/imap-of-mutable.d.ts
interface IMapOfMutable<V> extends IMapOf<V> {
/**
* Adds several `values` under the same `key`. Duplicate values are permitted, depending on implementation.
* ```js
* addKeyedValues('colours', 'red', 'green', 'blue')
* ```
* @param key
* @param values
*/
addKeyedValues(key: string, ...values: readonly V[]): void;
/**
* Adds a value, automatically extracting a key via the
* `groupBy` function assigned in the constructor options.
* @param values Adds several values
*/
addValue(...values: readonly V[]): void;
/**
* Clears the map
*/
clear(): void;
/**
* Returns the number of keys
*/
get lengthKeys(): number;
/**
* Deletes all values under `key` that match `value`.
* @param key Key
* @param value Value
*/
deleteKeyValue(key: string, value: V): boolean;
/**
* Delete all occurrences of `value`, regardless of
* key it is stored under.
* Returns _true_ if something was deleted.
* @param value
*/
deleteByValue(value: V): boolean;
/**
* Deletes all values stored under `key`. Returns _true_ if key was found
* @param key
*/
delete(key: string): boolean;
}
//#endregion
//#region ../packages/collections/src/map/map-of-simple-base.d.ts
declare class MapOfSimpleBase<V> {
protected map: Map<string, readonly V[]>;
protected readonly groupBy: (value: V) => string;
protected valueEq: IsEqual<V>;
/**
* Constructor
*
* ```js
* const m = new MapOfSimpleBase();
* m.valuesFor(`apple`); // Iterator over all values stored under key `apple`
* ```
* @param groupBy Creates keys for values when using `addValue`. By default uses JSON.stringify
* @param valueEq Compare values. By default uses JS logic for equality
*/
constructor(groupBy?: (value: V) => string, valueEq?: IsEqual<V>, initial?: Map<string, readonly V[]> | Array<[string, readonly V[]]>);
/**
* Returns the underlying map storage. Do not manipulate.
*/
get getRawMapUnsafe(): Map<string, readonly V[]>;
/**
* Returns _true_ if `key` exists
* @param key
* @returns
*/
has(key: string): boolean;
/**
* Returns _true_ if `value` exists under `key`.
* @param key Key
* @param value Value to seek under `key`
* @returns _True_ if `value` exists under `key`.
*/
hasKeyValue(key: string, value: V): boolean;
/**
* Debug dump of contents
*/
debugString(): string;
/**
* Return number of values stored under `key`.
* Returns 0 if `key` is not found.
* @param key
* @returns
*/
count(key: string): number;
/**
* Returns first key that contains `value`
* @param value
* @param eq
* @returns
*/
firstKeyByValue(value: V, eq?: IsEqual<V>): string | undefined;
/**
* Iterate over all entries
*/
entriesFlat(): IterableIterator<[key: string, value: V]>;
/**
* Iterate over keys and array of values for that ke