@tanstack/db-ivm 0.1.22 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/d2.cjs +12 -2
- package/dist/cjs/d2.cjs.map +1 -1
- package/dist/cjs/hashing/hash.cjs +75 -121
- package/dist/cjs/hashing/hash.cjs.map +1 -1
- package/dist/cjs/hashing/temporal.cjs +18 -0
- package/dist/cjs/hashing/temporal.cjs.map +1 -0
- package/dist/cjs/hashing/temporal.d.cts +5 -0
- package/dist/cjs/multiset.cjs +47 -92
- package/dist/cjs/multiset.cjs.map +1 -1
- package/dist/cjs/multiset.d.cts +16 -0
- package/dist/cjs/operators/groupBy.cjs +2 -2
- package/dist/cjs/operators/groupBy.cjs.map +1 -1
- package/dist/cjs/utils.cjs +15 -61
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/cjs/utils.d.cts +3 -32
- package/dist/esm/d2.js +12 -2
- package/dist/esm/d2.js.map +1 -1
- package/dist/esm/hashing/hash.js +75 -121
- package/dist/esm/hashing/hash.js.map +1 -1
- package/dist/esm/hashing/temporal.d.ts +5 -0
- package/dist/esm/hashing/temporal.js +18 -0
- package/dist/esm/hashing/temporal.js.map +1 -0
- package/dist/esm/multiset.d.ts +16 -0
- package/dist/esm/multiset.js +48 -93
- package/dist/esm/multiset.js.map +1 -1
- package/dist/esm/operators/groupBy.js +2 -2
- package/dist/esm/operators/groupBy.js.map +1 -1
- package/dist/esm/utils.d.ts +3 -32
- package/dist/esm/utils.js +15 -61
- package/dist/esm/utils.js.map +1 -1
- package/package.json +1 -1
- package/src/d2.ts +15 -2
- package/src/hashing/hash.ts +118 -172
- package/src/hashing/temporal.ts +25 -0
- package/src/multiset.ts +73 -132
- package/src/operators/groupBy.ts +8 -2
- package/src/utils.ts +20 -76
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"multiset.cjs","sources":["../../src/multiset.ts"],"sourcesContent":["import {\n DefaultMap,\n chunkedArrayPush,\n globalObjectIdGenerator,\n} from './utils.js'\nimport { hash } from './hashing/index.js'\n\nexport type MultiSetArray<T> = Array<[T, number]>\nexport type KeyedData<T> = [key: string, value: T]\n\n/**\n * A multiset of data.\n */\nexport class MultiSet<T> {\n #inner: MultiSetArray<T>\n\n constructor(data: MultiSetArray<T> = []) {\n this.#inner = data\n }\n\n toString(indent = false): string {\n return `MultiSet(${JSON.stringify(this.#inner, null, indent ? 2 : undefined)})`\n }\n\n toJSON(): string {\n return JSON.stringify(Array.from(this.getInner()))\n }\n\n static fromJSON<U>(json: string): MultiSet<U> {\n return new MultiSet(JSON.parse(json))\n }\n\n /**\n * Apply a function to all records in the collection.\n */\n map<U>(f: (data: T) => U): MultiSet<U> {\n return new MultiSet(\n this.#inner.map(([data, multiplicity]) => [f(data), multiplicity]),\n )\n }\n\n /**\n * Filter out records for which a function f(record) evaluates to False.\n */\n filter(f: (data: T) => boolean): MultiSet<T> {\n return new MultiSet(this.#inner.filter(([data, _]) => f(data)))\n }\n\n /**\n * Negate all multiplicities in the collection.\n */\n negate(): MultiSet<T> {\n return new MultiSet(\n this.#inner.map(([data, multiplicity]) => [data, -multiplicity]),\n )\n }\n\n /**\n * Concatenate two collections together.\n */\n concat(other: MultiSet<T>): MultiSet<T> {\n const out: MultiSetArray<T> = []\n chunkedArrayPush(out, this.#inner)\n chunkedArrayPush(out, other.getInner())\n return new MultiSet(out)\n }\n\n /**\n * Produce as output a collection that is logically equivalent to the input\n * but which combines identical instances of the same record into one\n * (record, multiplicity) pair.\n */\n consolidate(): MultiSet<T> {\n // Check if this looks like a keyed multiset (first item is a tuple of length 2)\n if (this.#inner.length > 0) {\n const firstItem = this.#inner[0]?.[0]\n if (Array.isArray(firstItem) && firstItem.length === 2) {\n return this.#consolidateKeyed()\n }\n }\n\n // Fall back to original method for unkeyed data\n return this.#consolidateUnkeyed()\n }\n\n /**\n * Private method for consolidating keyed multisets where keys are strings/numbers\n * and values are compared by reference equality.\n *\n * This method provides significant performance improvements over the hash-based approach\n * by using WeakMap for object reference tracking and avoiding expensive serialization.\n *\n * Special handling for join operations: When values are tuples of length 2 (common in joins),\n * we unpack them and compare each element individually to maintain proper equality semantics.\n */\n #consolidateKeyed(): MultiSet<T> {\n const consolidated = new Map<string, number>()\n const values = new Map<string, T>()\n\n // Use global object ID generator for consistent reference equality\n\n /**\n * Special handler for tuples (arrays of length 2) commonly produced by join operations.\n * Unpacks the tuple and generates an ID based on both elements to ensure proper\n * consolidation of join results like ['A', null] and [null, 'X'].\n */\n const getTupleId = (tuple: Array<any>): string => {\n if (tuple.length !== 2) {\n throw new Error(`Expected tuple of length 2`)\n }\n const [first, second] = tuple\n return `${globalObjectIdGenerator.getStringId(first)}|${globalObjectIdGenerator.getStringId(second)}`\n }\n\n // Process each item in the multiset\n for (const [data, multiplicity] of this.#inner) {\n // Verify this is still a keyed item (should be [key, value] pair)\n if (!Array.isArray(data) || data.length !== 2) {\n // Found non-keyed item, fall back to unkeyed consolidation\n return this.#consolidateUnkeyed()\n }\n\n const [key, value] = data\n\n // Verify key is string or number as expected for keyed multisets\n if (typeof key !== `string` && typeof key !== `number`) {\n // Found non-string/number key, fall back to unkeyed consolidation\n return this.#consolidateUnkeyed()\n }\n\n // Generate value ID with special handling for join tuples\n let valueId: string\n if (Array.isArray(value) && value.length === 2) {\n // Special case: value is a tuple from join operations\n valueId = getTupleId(value)\n } else {\n // Regular case: use reference/value equality\n valueId = globalObjectIdGenerator.getStringId(value)\n }\n\n // Create composite key and consolidate\n const compositeKey = key + `|` + valueId\n consolidated.set(\n compositeKey,\n (consolidated.get(compositeKey) || 0) + multiplicity,\n )\n\n // Store the original data for the first occurrence\n if (!values.has(compositeKey)) {\n values.set(compositeKey, data as T)\n }\n }\n\n // Build result array, filtering out zero multiplicities\n const result: MultiSetArray<T> = []\n for (const [compositeKey, multiplicity] of consolidated) {\n if (multiplicity !== 0) {\n result.push([values.get(compositeKey)!, multiplicity])\n }\n }\n\n return new MultiSet(result)\n }\n\n /**\n * Private method for consolidating unkeyed multisets using the original approach.\n */\n #consolidateUnkeyed(): MultiSet<T> {\n const consolidated = new DefaultMap<string | number, number>(() => 0)\n const values = new Map<string, any>()\n\n let hasString = false\n let hasNumber = false\n let hasOther = false\n for (const [data, _] of this.#inner) {\n if (typeof data === `string`) {\n hasString = true\n } else if (typeof data === `number`) {\n hasNumber = true\n } else {\n hasOther = true\n break\n }\n }\n\n const requireJson = hasOther || (hasString && hasNumber)\n\n for (const [data, multiplicity] of this.#inner) {\n const key = requireJson ? hash(data) : (data as string | number)\n if (requireJson && !values.has(key as string)) {\n values.set(key as string, data)\n }\n consolidated.update(key, (count) => count + multiplicity)\n }\n\n const result: MultiSetArray<T> = []\n for (const [key, multiplicity] of consolidated.entries()) {\n if (multiplicity !== 0) {\n const parsedKey = requireJson ? values.get(key as string) : key\n result.push([parsedKey as T, multiplicity])\n }\n }\n\n return new MultiSet(result)\n }\n\n extend(other: MultiSet<T> | MultiSetArray<T>): void {\n const otherArray = other instanceof MultiSet ? other.getInner() : other\n chunkedArrayPush(this.#inner, otherArray)\n }\n\n add(item: T, multiplicity: number): void {\n if (multiplicity !== 0) {\n this.#inner.push([item, multiplicity])\n }\n }\n\n getInner(): MultiSetArray<T> {\n return this.#inner\n }\n}\n"],"names":["chunkedArrayPush","globalObjectIdGenerator","DefaultMap","hash"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"multiset.cjs","sources":["../../src/multiset.ts"],"sourcesContent":["import { chunkedArrayPush, getStringId } from './utils.js'\nimport { hash } from './hashing/index.js'\n\nexport type MultiSetArray<T> = Array<[T, number]>\nexport type KeyedData<T> = [key: string, value: T]\n\n/**\n * A multiset of data.\n */\nexport class MultiSet<T> {\n #inner: MultiSetArray<T>\n\n constructor(data: MultiSetArray<T> = []) {\n this.#inner = data\n }\n\n toString(indent = false): string {\n return `MultiSet(${JSON.stringify(this.#inner, null, indent ? 2 : undefined)})`\n }\n\n toJSON(): string {\n return JSON.stringify(Array.from(this.getInner()))\n }\n\n static fromJSON<U>(json: string): MultiSet<U> {\n return new MultiSet(JSON.parse(json))\n }\n\n /**\n * Apply a function to all records in the collection.\n */\n map<U>(f: (data: T) => U): MultiSet<U> {\n return new MultiSet(\n this.#inner.map(([data, multiplicity]) => [f(data), multiplicity]),\n )\n }\n\n /**\n * Filter out records for which a function f(record) evaluates to False.\n */\n filter(f: (data: T) => boolean): MultiSet<T> {\n return new MultiSet(this.#inner.filter(([data, _]) => f(data)))\n }\n\n /**\n * Negate all multiplicities in the collection.\n */\n negate(): MultiSet<T> {\n return new MultiSet(\n this.#inner.map(([data, multiplicity]) => [data, -multiplicity]),\n )\n }\n\n /**\n * Concatenate two collections together.\n */\n concat(other: MultiSet<T>): MultiSet<T> {\n const out: MultiSetArray<T> = []\n chunkedArrayPush(out, this.#inner)\n chunkedArrayPush(out, other.getInner())\n return new MultiSet(out)\n }\n\n /**\n * Produce as output a collection that is logically equivalent to the input\n * but which combines identical instances of the same record into one\n * (record, multiplicity) pair.\n *\n * Record identity depends on the shape of the whole multiset:\n *\n * - Keyed: every record is a `[key, value]` pair with a string or number\n * key. Keys and primitive values compare by value, and object values by\n * reference. A value that is an array of length 2 is a join tuple, and\n * its two elements compare by the same rule. The keyed identity text\n * has known collisions: https://github.com/TanStack/db/issues/1948\n * - Unkeyed, one primitive type: every record is a string, or every record\n * is a number. Records compare as `Map` keys do, and a `-0` record is\n * returned as `0`.\n * - Otherwise, records compare by structure (`hash`).\n *\n * Each identity keeps its first record in input order with the summed\n * multiplicity. Identities whose sum is zero are dropped. The input is not\n * changed.\n */\n consolidate(): MultiSet<T> {\n const inner = this.#inner\n // Keyed multisets hold [string | number key, value] pairs. Their values are\n // compared by reference, which avoids hashing. Join tuples (values of\n // length 2) are unpacked so ['A', null] and [null, 'X'] stay distinct.\n if (\n inner.every(\n ([data]) =>\n Array.isArray(data) &&\n data.length === 2 &&\n (typeof data[0] === `string` || typeof data[0] === `number`),\n )\n ) {\n return consolidateBy(inner, (data) => {\n const [key, value] = data as [string | number, unknown]\n const valueId =\n Array.isArray(value) && value.length === 2\n ? `${getStringId(value[0])}|${getStringId(value[1])}`\n : getStringId(value)\n return key + `|` + valueId\n })\n }\n\n // Unkeyed data of a single primitive type is its own identity, and the\n // result holds the Map key, so -0 becomes 0. Anything else is compared\n // structurally by hash.\n let primitiveType: string | undefined\n const requireHash = inner.some(([data]) => {\n const type = typeof data\n if (type !== `string` && type !== `number`) return true\n primitiveType ??= type\n return type !== primitiveType\n })\n return requireHash\n ? consolidateBy(inner, hash)\n : consolidateBy(inner, (data) => data, true)\n }\n\n extend(other: MultiSet<T> | MultiSetArray<T>): void {\n const otherArray = other instanceof MultiSet ? other.getInner() : other\n chunkedArrayPush(this.#inner, otherArray)\n }\n\n add(item: T, multiplicity: number): void {\n if (multiplicity !== 0) {\n this.#inner.push([item, multiplicity])\n }\n }\n\n getInner(): MultiSetArray<T> {\n return this.#inner\n }\n}\n\n/**\n * Sums multiplicities of records with the same identity, keeping the first\n * record seen for each identity (or its Map key, which writes -0 as 0, when\n * `keepIdentity` is set) and dropping records whose sum is zero.\n */\nfunction consolidateBy<T>(\n inner: MultiSetArray<T>,\n identityOf: (data: T) => unknown,\n keepIdentity = false,\n): MultiSet<T> {\n const consolidated = new Map<unknown, [T, number]>()\n for (const [data, multiplicity] of inner) {\n const identity = identityOf(data)\n const entry = consolidated.get(identity)\n if (entry) entry[1] += multiplicity\n else consolidated.set(identity, [data, multiplicity])\n }\n const result: MultiSetArray<T> = []\n for (const [identity, [data, multiplicity]] of consolidated) {\n if (multiplicity !== 0)\n result.push([keepIdentity ? (identity as T) : data, multiplicity])\n }\n return new MultiSet(result)\n}\n"],"names":["chunkedArrayPush","getStringId","hash"],"mappings":"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"}
|
package/dist/cjs/multiset.d.cts
CHANGED
|
@@ -29,6 +29,22 @@ export declare class MultiSet<T> {
|
|
|
29
29
|
* Produce as output a collection that is logically equivalent to the input
|
|
30
30
|
* but which combines identical instances of the same record into one
|
|
31
31
|
* (record, multiplicity) pair.
|
|
32
|
+
*
|
|
33
|
+
* Record identity depends on the shape of the whole multiset:
|
|
34
|
+
*
|
|
35
|
+
* - Keyed: every record is a `[key, value]` pair with a string or number
|
|
36
|
+
* key. Keys and primitive values compare by value, and object values by
|
|
37
|
+
* reference. A value that is an array of length 2 is a join tuple, and
|
|
38
|
+
* its two elements compare by the same rule. The keyed identity text
|
|
39
|
+
* has known collisions: https://github.com/TanStack/db/issues/1948
|
|
40
|
+
* - Unkeyed, one primitive type: every record is a string, or every record
|
|
41
|
+
* is a number. Records compare as `Map` keys do, and a `-0` record is
|
|
42
|
+
* returned as `0`.
|
|
43
|
+
* - Otherwise, records compare by structure (`hash`).
|
|
44
|
+
*
|
|
45
|
+
* Each identity keeps its first record in input order with the summed
|
|
46
|
+
* multiplicity. Identities whose sum is zero are dropped. The input is not
|
|
47
|
+
* changed.
|
|
32
48
|
*/
|
|
33
49
|
consolidate(): MultiSet<T>;
|
|
34
50
|
extend(other: MultiSet<T> | MultiSetArray<T>): void;
|
|
@@ -124,7 +124,7 @@ function min(valueExtractor) {
|
|
|
124
124
|
reduce: (values) => {
|
|
125
125
|
let minValue;
|
|
126
126
|
for (const [value, _multiplicity] of values) {
|
|
127
|
-
if (
|
|
127
|
+
if (value !== void 0 && (minValue === void 0 || value < minValue)) {
|
|
128
128
|
minValue = value;
|
|
129
129
|
}
|
|
130
130
|
}
|
|
@@ -139,7 +139,7 @@ function max(valueExtractor) {
|
|
|
139
139
|
reduce: (values) => {
|
|
140
140
|
let maxValue;
|
|
141
141
|
for (const [value, _multiplicity] of values) {
|
|
142
|
-
if (
|
|
142
|
+
if (value !== void 0 && (maxValue === void 0 || value > maxValue)) {
|
|
143
143
|
maxValue = value;
|
|
144
144
|
}
|
|
145
145
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"groupBy.cjs","sources":["../../../src/operators/groupBy.ts"],"sourcesContent":["import { serializeValue } from '../utils.js'\nimport { map } from './map.js'\nimport { reduce } from './reduce.js'\nimport type { IStreamBuilder, KeyValue } from '../types.js'\n\ntype GroupKey = Record<string, unknown>\n\ntype BasicAggregateFunction<T, R, V = unknown> = {\n preMap: (data: T) => V\n reduce: (values: Array<[V, number]>) => V\n postMap?: (result: V) => R\n}\n\ntype PipedAggregateFunction<T, R> = {\n pipe: (stream: IStreamBuilder<T>) => IStreamBuilder<KeyValue<string, R>>\n}\n\ntype AggregateFunction<T, R, V = unknown> =\n | BasicAggregateFunction<T, R, V>\n | PipedAggregateFunction<T, R>\n\ntype ExtractAggregateReturnType<T, A> =\n A extends AggregateFunction<T, infer R, any> ? R : never\n\ntype AggregatesReturnType<T, A> = {\n [K in keyof A]: ExtractAggregateReturnType<T, A[K]>\n}\n\nfunction isPipedAggregateFunction<T, R>(\n aggregate: AggregateFunction<T, R>,\n): aggregate is PipedAggregateFunction<T, R> {\n return `pipe` in aggregate\n}\n\n/**\n * Groups data by key and applies multiple aggregate operations\n * @param keyExtractor Function to extract grouping key from data\n * @param aggregates Object mapping aggregate names to aggregate functions\n */\nexport function groupBy<\n T,\n K extends GroupKey,\n A extends Record<string, AggregateFunction<T, any, any>>,\n>(keyExtractor: (data: T) => K, aggregates: A = {} as A) {\n type ResultType = K & AggregatesReturnType<T, A>\n\n const basicAggregates = Object.fromEntries(\n Object.entries(aggregates).filter(\n ([_, aggregate]) => !isPipedAggregateFunction(aggregate),\n ),\n ) as Record<string, BasicAggregateFunction<T, any, any>>\n\n // @ts-expect-error - TODO: we don't use this yet, but we will\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const pipedAggregates = Object.fromEntries(\n Object.entries(aggregates).filter(([_, aggregate]) =>\n isPipedAggregateFunction(aggregate),\n ),\n ) as Record<string, PipedAggregateFunction<T, any>>\n\n return (\n stream: IStreamBuilder<T>,\n ): IStreamBuilder<KeyValue<string, ResultType>> => {\n // Special key to store the original key object\n const KEY_SENTINEL = Symbol(`original_group_key`)\n\n // First map to extract keys and pre-aggregate values\n const withKeysAndValues = stream.pipe(\n map((data) => {\n const key = keyExtractor(data)\n const keyString = serializeValue(key)\n\n // Create values object with pre-aggregated values\n const values: Record<string | symbol, unknown> = {}\n\n // Store the original key object\n values[KEY_SENTINEL] = key\n\n // Add pre-aggregated values\n for (const [name, aggregate] of Object.entries(basicAggregates)) {\n values[name] = aggregate.preMap(data)\n }\n\n return [keyString, values] as KeyValue<\n string,\n Record<string | symbol, unknown>\n >\n }),\n )\n\n // Then reduce to compute aggregates\n const reduced = withKeysAndValues.pipe(\n reduce((values) => {\n // Calculate total multiplicity to check if the group should exist\n let totalMultiplicity = 0\n for (const [_, multiplicity] of values) {\n totalMultiplicity += multiplicity\n }\n\n // If total multiplicity is 0 or negative, the group should be removed completely\n if (totalMultiplicity <= 0) {\n return []\n }\n\n const result: Record<PropertyKey, unknown> = {}\n\n // Get the original key from first value in group\n const originalKey = values[0]?.[0]?.[KEY_SENTINEL]\n result[KEY_SENTINEL] = originalKey\n\n // Apply each aggregate function\n for (const [name, aggregate] of Object.entries(basicAggregates)) {\n const preValues = values.map(\n ([v, m]) => [v[name], m] as [any, number],\n )\n result[name] = aggregate.reduce(preValues)\n }\n\n return [[result, 1]]\n }),\n )\n\n // Finally map to extract the key and include all values\n return reduced.pipe(\n map(([keyString, values]) => {\n // Extract the original key\n const key = values[KEY_SENTINEL] as K\n\n // Create intermediate result with key values and aggregate results\n const result: Record<string, unknown> = {}\n\n // Add key properties to result\n Object.assign(result, key)\n\n // Apply postMap if provided\n for (const [name, aggregate] of Object.entries(basicAggregates)) {\n if (aggregate.postMap) {\n result[name] = aggregate.postMap(values[name])\n } else {\n result[name] = values[name]\n }\n }\n\n // Return with the string key instead of the object\n return [keyString, result] as KeyValue<string, ResultType>\n }),\n )\n }\n}\n\n/**\n * Creates a sum aggregate function\n */\nexport function sum<T>(\n valueExtractor: (value: T) => number = (v) => v as unknown as number,\n): AggregateFunction<T, number, number> {\n return {\n preMap: (data: T) => valueExtractor(data),\n reduce: (values: Array<[number, number]>) => {\n let total = 0\n for (const [value, multiplicity] of values) {\n total += value * multiplicity\n }\n return total\n },\n }\n}\n\n/**\n * Creates a count aggregate function\n */\nexport function count<T>(\n valueExtractor: (value: T) => any = (v) => v,\n): AggregateFunction<T, number, number> {\n return {\n // Count only not-null values (the `== null` comparison gives true for both null and undefined)\n preMap: (data: T) => (valueExtractor(data) == null ? 0 : 1),\n reduce: (values: Array<[number, number]>) => {\n let totalCount = 0\n for (const [nullMultiplier, multiplicity] of values) {\n totalCount += nullMultiplier * multiplicity\n }\n return totalCount\n },\n }\n}\n\n/**\n * Creates an average aggregate function\n */\nexport function avg<T>(\n valueExtractor: (value: T) => number = (v) => v as unknown as number,\n): AggregateFunction<T, number, { sum: number; count: number }> {\n return {\n preMap: (data: T) => ({\n sum: valueExtractor(data),\n count: 0,\n }),\n reduce: (values: Array<[{ sum: number; count: number }, number]>) => {\n let totalSum = 0\n let totalCount = 0\n for (const [value, multiplicity] of values) {\n totalSum += value.sum * multiplicity\n totalCount += multiplicity\n }\n return {\n sum: totalSum,\n count: totalCount,\n }\n },\n postMap: (result: { sum: number; count: number }) => {\n return result.sum / result.count\n },\n }\n}\n\ntype CanMinMax = number | Date | bigint | string\n\n/**\n * Creates a min aggregate function that computes the minimum value in a group\n * @param valueExtractor Function to extract a comparable value from each data entry\n */\nexport function min<T extends CanMinMax>(): AggregateFunction<\n T,\n T | undefined,\n T | undefined\n>\nexport function min<T, V extends CanMinMax>(\n valueExtractor: (value: T) => V,\n): AggregateFunction<T, V | undefined, V | undefined>\nexport function min<T, V extends CanMinMax>(\n valueExtractor?: (value: T) => V,\n): AggregateFunction<T, V | undefined, V | undefined> {\n const extractor = valueExtractor ?? ((v: T) => v as unknown as V)\n return {\n preMap: (data: T) => extractor(data),\n reduce: (values) => {\n let minValue: V | undefined\n for (const [value, _multiplicity] of values) {\n if (!minValue || (value && value < minValue)) {\n minValue = value\n }\n }\n return minValue\n },\n }\n}\n\n/**\n * Creates a max aggregate function that computes the maximum value in a group\n * @param valueExtractor Function to extract a comparable value from each data entry\n */\nexport function max<T extends CanMinMax>(): AggregateFunction<\n T,\n T | undefined,\n T | undefined\n>\nexport function max<T, V extends CanMinMax>(\n valueExtractor: (value: T) => V,\n): AggregateFunction<T, V | undefined, V | undefined>\nexport function max<T, V extends CanMinMax>(\n valueExtractor?: (value: T) => V,\n): AggregateFunction<T, V | undefined, V | undefined> {\n const extractor = valueExtractor ?? ((v: T) => v as unknown as V)\n return {\n preMap: (data: T) => extractor(data),\n reduce: (values) => {\n let maxValue: V | undefined\n for (const [value, _multiplicity] of values) {\n if (!maxValue || (value && value > maxValue)) {\n maxValue = value\n }\n }\n return maxValue\n },\n }\n}\n\n/**\n * Creates a median aggregate function that computes the middle value in a sorted group\n * If there's an even number of values, returns the average of the two middle values\n * @param valueExtractor Function to extract a numeric value from each data entry\n */\nexport function median<T>(\n valueExtractor: (value: T) => number = (v) => v as unknown as number,\n): AggregateFunction<T, number, Array<number>> {\n return {\n preMap: (data: T) => [valueExtractor(data)],\n reduce: (values: Array<[Array<number>, number]>) => {\n // Flatten all values, taking multiplicity into account\n const allValues: Array<number> = []\n for (const [valueArray, multiplicity] of values) {\n for (const value of valueArray) {\n // Add each value multiple times based on multiplicity\n for (let i = 0; i < multiplicity; i++) {\n allValues.push(value)\n }\n }\n }\n\n // Return empty array if no values\n if (allValues.length === 0) {\n return []\n }\n\n // Sort values\n allValues.sort((a, b) => a - b)\n\n return allValues\n },\n postMap: (result: Array<number>) => {\n if (result.length === 0) return 0\n\n const mid = Math.floor(result.length / 2)\n\n // If even number of values, average the two middle values\n if (result.length % 2 === 0) {\n return (result[mid - 1]! + result[mid]!) / 2\n }\n\n // If odd number of values, return the middle value\n return result[mid]!\n },\n }\n}\n\n/**\n * Creates a mode aggregate function that computes the most frequent value in a group\n * If multiple values have the same highest frequency, returns the first one encountered\n * @param valueExtractor Function to extract a value from each data entry\n */\nexport function mode<T>(\n valueExtractor: (value: T) => number = (v) => v as unknown as number,\n): AggregateFunction<T, number, Map<number, number>> {\n return {\n preMap: (data: T) => {\n const value = valueExtractor(data)\n const frequencyMap = new Map<number, number>()\n frequencyMap.set(value, 1)\n return frequencyMap\n },\n reduce: (values: Array<[Map<number, number>, number]>) => {\n // Combine all frequency maps\n const combinedMap = new Map<number, number>()\n\n for (const [frequencyMap, multiplicity] of values) {\n for (const [value, frequencyCount] of frequencyMap.entries()) {\n const currentCount = combinedMap.get(value) || 0\n combinedMap.set(value, currentCount + frequencyCount * multiplicity)\n }\n }\n\n return combinedMap\n },\n postMap: (result: Map<number, number>) => {\n if (result.size === 0) return 0\n\n let modeValue = 0\n let maxFrequency = 0\n\n for (const [value, frequency] of result.entries()) {\n if (frequency > maxFrequency) {\n maxFrequency = frequency\n modeValue = value\n }\n }\n\n return modeValue\n },\n }\n}\n\nexport const groupByOperators = {\n sum,\n count,\n avg,\n min,\n max,\n median,\n mode,\n}\n"],"names":["map","serializeValue","reduce"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"groupBy.cjs","sources":["../../../src/operators/groupBy.ts"],"sourcesContent":["import { serializeValue } from '../utils.js'\nimport { map } from './map.js'\nimport { reduce } from './reduce.js'\nimport type { IStreamBuilder, KeyValue } from '../types.js'\n\ntype GroupKey = Record<string, unknown>\n\ntype BasicAggregateFunction<T, R, V = unknown> = {\n preMap: (data: T) => V\n reduce: (values: Array<[V, number]>) => V\n postMap?: (result: V) => R\n}\n\ntype PipedAggregateFunction<T, R> = {\n pipe: (stream: IStreamBuilder<T>) => IStreamBuilder<KeyValue<string, R>>\n}\n\ntype AggregateFunction<T, R, V = unknown> =\n | BasicAggregateFunction<T, R, V>\n | PipedAggregateFunction<T, R>\n\ntype ExtractAggregateReturnType<T, A> =\n A extends AggregateFunction<T, infer R, any> ? R : never\n\ntype AggregatesReturnType<T, A> = {\n [K in keyof A]: ExtractAggregateReturnType<T, A[K]>\n}\n\nfunction isPipedAggregateFunction<T, R>(\n aggregate: AggregateFunction<T, R>,\n): aggregate is PipedAggregateFunction<T, R> {\n return `pipe` in aggregate\n}\n\n/**\n * Groups data by key and applies multiple aggregate operations\n * @param keyExtractor Function to extract grouping key from data\n * @param aggregates Object mapping aggregate names to aggregate functions\n */\nexport function groupBy<\n T,\n K extends GroupKey,\n A extends Record<string, AggregateFunction<T, any, any>>,\n>(keyExtractor: (data: T) => K, aggregates: A = {} as A) {\n type ResultType = K & AggregatesReturnType<T, A>\n\n const basicAggregates = Object.fromEntries(\n Object.entries(aggregates).filter(\n ([_, aggregate]) => !isPipedAggregateFunction(aggregate),\n ),\n ) as Record<string, BasicAggregateFunction<T, any, any>>\n\n // @ts-expect-error - TODO: we don't use this yet, but we will\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n const pipedAggregates = Object.fromEntries(\n Object.entries(aggregates).filter(([_, aggregate]) =>\n isPipedAggregateFunction(aggregate),\n ),\n ) as Record<string, PipedAggregateFunction<T, any>>\n\n return (\n stream: IStreamBuilder<T>,\n ): IStreamBuilder<KeyValue<string, ResultType>> => {\n // Special key to store the original key object\n const KEY_SENTINEL = Symbol(`original_group_key`)\n\n // First map to extract keys and pre-aggregate values\n const withKeysAndValues = stream.pipe(\n map((data) => {\n const key = keyExtractor(data)\n const keyString = serializeValue(key)\n\n // Create values object with pre-aggregated values\n const values: Record<string | symbol, unknown> = {}\n\n // Store the original key object\n values[KEY_SENTINEL] = key\n\n // Add pre-aggregated values\n for (const [name, aggregate] of Object.entries(basicAggregates)) {\n values[name] = aggregate.preMap(data)\n }\n\n return [keyString, values] as KeyValue<\n string,\n Record<string | symbol, unknown>\n >\n }),\n )\n\n // Then reduce to compute aggregates\n const reduced = withKeysAndValues.pipe(\n reduce((values) => {\n // Calculate total multiplicity to check if the group should exist\n let totalMultiplicity = 0\n for (const [_, multiplicity] of values) {\n totalMultiplicity += multiplicity\n }\n\n // If total multiplicity is 0 or negative, the group should be removed completely\n if (totalMultiplicity <= 0) {\n return []\n }\n\n const result: Record<PropertyKey, unknown> = {}\n\n // Get the original key from first value in group\n const originalKey = values[0]?.[0]?.[KEY_SENTINEL]\n result[KEY_SENTINEL] = originalKey\n\n // Apply each aggregate function\n for (const [name, aggregate] of Object.entries(basicAggregates)) {\n const preValues = values.map(\n ([v, m]) => [v[name], m] as [any, number],\n )\n result[name] = aggregate.reduce(preValues)\n }\n\n return [[result, 1]]\n }),\n )\n\n // Finally map to extract the key and include all values\n return reduced.pipe(\n map(([keyString, values]) => {\n // Extract the original key\n const key = values[KEY_SENTINEL] as K\n\n // Create intermediate result with key values and aggregate results\n const result: Record<string, unknown> = {}\n\n // Add key properties to result\n Object.assign(result, key)\n\n // Apply postMap if provided\n for (const [name, aggregate] of Object.entries(basicAggregates)) {\n if (aggregate.postMap) {\n result[name] = aggregate.postMap(values[name])\n } else {\n result[name] = values[name]\n }\n }\n\n // Return with the string key instead of the object\n return [keyString, result] as KeyValue<string, ResultType>\n }),\n )\n }\n}\n\n/**\n * Creates a sum aggregate function\n */\nexport function sum<T>(\n valueExtractor: (value: T) => number = (v) => v as unknown as number,\n): AggregateFunction<T, number, number> {\n return {\n preMap: (data: T) => valueExtractor(data),\n reduce: (values: Array<[number, number]>) => {\n let total = 0\n for (const [value, multiplicity] of values) {\n total += value * multiplicity\n }\n return total\n },\n }\n}\n\n/**\n * Creates a count aggregate function\n */\nexport function count<T>(\n valueExtractor: (value: T) => any = (v) => v,\n): AggregateFunction<T, number, number> {\n return {\n // Count only not-null values (the `== null` comparison gives true for both null and undefined)\n preMap: (data: T) => (valueExtractor(data) == null ? 0 : 1),\n reduce: (values: Array<[number, number]>) => {\n let totalCount = 0\n for (const [nullMultiplier, multiplicity] of values) {\n totalCount += nullMultiplier * multiplicity\n }\n return totalCount\n },\n }\n}\n\n/**\n * Creates an average aggregate function\n */\nexport function avg<T>(\n valueExtractor: (value: T) => number = (v) => v as unknown as number,\n): AggregateFunction<T, number, { sum: number; count: number }> {\n return {\n preMap: (data: T) => ({\n sum: valueExtractor(data),\n count: 0,\n }),\n reduce: (values: Array<[{ sum: number; count: number }, number]>) => {\n let totalSum = 0\n let totalCount = 0\n for (const [value, multiplicity] of values) {\n totalSum += value.sum * multiplicity\n totalCount += multiplicity\n }\n return {\n sum: totalSum,\n count: totalCount,\n }\n },\n postMap: (result: { sum: number; count: number }) => {\n return result.sum / result.count\n },\n }\n}\n\ntype CanMinMax = number | Date | bigint | string\n\n/**\n * Creates a min aggregate function that computes the minimum value in a group\n * @param valueExtractor Function to extract a comparable value from each data entry\n */\nexport function min<T extends CanMinMax>(): AggregateFunction<\n T,\n T | undefined,\n T | undefined\n>\nexport function min<T, V extends CanMinMax>(\n valueExtractor: (value: T) => V,\n): AggregateFunction<T, V | undefined, V | undefined>\nexport function min<T, V extends CanMinMax>(\n valueExtractor?: (value: T) => V,\n): AggregateFunction<T, V | undefined, V | undefined> {\n const extractor = valueExtractor ?? ((v: T) => v as unknown as V)\n return {\n preMap: (data: T) => extractor(data),\n reduce: (values) => {\n let minValue: V | undefined\n for (const [value, _multiplicity] of values) {\n if (\n value !== undefined &&\n (minValue === undefined || value < minValue)\n ) {\n minValue = value\n }\n }\n return minValue\n },\n }\n}\n\n/**\n * Creates a max aggregate function that computes the maximum value in a group\n * @param valueExtractor Function to extract a comparable value from each data entry\n */\nexport function max<T extends CanMinMax>(): AggregateFunction<\n T,\n T | undefined,\n T | undefined\n>\nexport function max<T, V extends CanMinMax>(\n valueExtractor: (value: T) => V,\n): AggregateFunction<T, V | undefined, V | undefined>\nexport function max<T, V extends CanMinMax>(\n valueExtractor?: (value: T) => V,\n): AggregateFunction<T, V | undefined, V | undefined> {\n const extractor = valueExtractor ?? ((v: T) => v as unknown as V)\n return {\n preMap: (data: T) => extractor(data),\n reduce: (values) => {\n let maxValue: V | undefined\n for (const [value, _multiplicity] of values) {\n if (\n value !== undefined &&\n (maxValue === undefined || value > maxValue)\n ) {\n maxValue = value\n }\n }\n return maxValue\n },\n }\n}\n\n/**\n * Creates a median aggregate function that computes the middle value in a sorted group\n * If there's an even number of values, returns the average of the two middle values\n * @param valueExtractor Function to extract a numeric value from each data entry\n */\nexport function median<T>(\n valueExtractor: (value: T) => number = (v) => v as unknown as number,\n): AggregateFunction<T, number, Array<number>> {\n return {\n preMap: (data: T) => [valueExtractor(data)],\n reduce: (values: Array<[Array<number>, number]>) => {\n // Flatten all values, taking multiplicity into account\n const allValues: Array<number> = []\n for (const [valueArray, multiplicity] of values) {\n for (const value of valueArray) {\n // Add each value multiple times based on multiplicity\n for (let i = 0; i < multiplicity; i++) {\n allValues.push(value)\n }\n }\n }\n\n // Return empty array if no values\n if (allValues.length === 0) {\n return []\n }\n\n // Sort values\n allValues.sort((a, b) => a - b)\n\n return allValues\n },\n postMap: (result: Array<number>) => {\n if (result.length === 0) return 0\n\n const mid = Math.floor(result.length / 2)\n\n // If even number of values, average the two middle values\n if (result.length % 2 === 0) {\n return (result[mid - 1]! + result[mid]!) / 2\n }\n\n // If odd number of values, return the middle value\n return result[mid]!\n },\n }\n}\n\n/**\n * Creates a mode aggregate function that computes the most frequent value in a group\n * If multiple values have the same highest frequency, returns the first one encountered\n * @param valueExtractor Function to extract a value from each data entry\n */\nexport function mode<T>(\n valueExtractor: (value: T) => number = (v) => v as unknown as number,\n): AggregateFunction<T, number, Map<number, number>> {\n return {\n preMap: (data: T) => {\n const value = valueExtractor(data)\n const frequencyMap = new Map<number, number>()\n frequencyMap.set(value, 1)\n return frequencyMap\n },\n reduce: (values: Array<[Map<number, number>, number]>) => {\n // Combine all frequency maps\n const combinedMap = new Map<number, number>()\n\n for (const [frequencyMap, multiplicity] of values) {\n for (const [value, frequencyCount] of frequencyMap.entries()) {\n const currentCount = combinedMap.get(value) || 0\n combinedMap.set(value, currentCount + frequencyCount * multiplicity)\n }\n }\n\n return combinedMap\n },\n postMap: (result: Map<number, number>) => {\n if (result.size === 0) return 0\n\n let modeValue = 0\n let maxFrequency = 0\n\n for (const [value, frequency] of result.entries()) {\n if (frequency > maxFrequency) {\n maxFrequency = frequency\n modeValue = value\n }\n }\n\n return modeValue\n },\n }\n}\n\nexport const groupByOperators = {\n sum,\n count,\n avg,\n min,\n max,\n median,\n mode,\n}\n"],"names":["map","serializeValue","reduce"],"mappings":"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"}
|
package/dist/cjs/utils.cjs
CHANGED
|
@@ -1,26 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
3
|
-
|
|
4
|
-
constructor(defaultValue, entries) {
|
|
5
|
-
super(entries);
|
|
6
|
-
this.defaultValue = defaultValue;
|
|
7
|
-
}
|
|
8
|
-
get(key) {
|
|
9
|
-
if (!this.has(key)) {
|
|
10
|
-
return this.defaultValue();
|
|
11
|
-
}
|
|
12
|
-
return super.get(key);
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Update the value for a key using a function.
|
|
16
|
-
*/
|
|
17
|
-
update(key, updater) {
|
|
18
|
-
const value = this.get(key);
|
|
19
|
-
const newValue = updater(value);
|
|
20
|
-
this.set(key, newValue);
|
|
21
|
-
return newValue;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
3
|
+
const temporal = require("./hashing/temporal.cjs");
|
|
24
4
|
const chunkSize = 3e4;
|
|
25
5
|
function chunkedArrayPush(array, other) {
|
|
26
6
|
if (other.length <= chunkSize) {
|
|
@@ -48,43 +28,16 @@ function binarySearch(array, value, comparator) {
|
|
|
48
28
|
}
|
|
49
29
|
return low;
|
|
50
30
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
*/
|
|
61
|
-
getId(value) {
|
|
62
|
-
if (typeof value !== `object` || value === null) {
|
|
63
|
-
const str = String(value);
|
|
64
|
-
let hashValue = 0;
|
|
65
|
-
for (let i = 0; i < str.length; i++) {
|
|
66
|
-
const char = str.charCodeAt(i);
|
|
67
|
-
hashValue = (hashValue << 5) - hashValue + char;
|
|
68
|
-
hashValue = hashValue & hashValue;
|
|
69
|
-
}
|
|
70
|
-
return hashValue;
|
|
71
|
-
}
|
|
72
|
-
if (!this.objectIds.has(value)) {
|
|
73
|
-
this.objectIds.set(value, this.nextId++);
|
|
74
|
-
}
|
|
75
|
-
return this.objectIds.get(value);
|
|
76
|
-
}
|
|
77
|
-
/**
|
|
78
|
-
* Get a string representation of the ID for use in composite keys.
|
|
79
|
-
*/
|
|
80
|
-
getStringId(value) {
|
|
81
|
-
if (value === null) return `null`;
|
|
82
|
-
if (value === void 0) return `undefined`;
|
|
83
|
-
if (typeof value !== `object`) return `str_${String(value)}`;
|
|
84
|
-
return `obj_${this.getId(value)}`;
|
|
85
|
-
}
|
|
31
|
+
const objectIds = /* @__PURE__ */ new WeakMap();
|
|
32
|
+
let nextObjectId = 0;
|
|
33
|
+
function getStringId(value) {
|
|
34
|
+
if (value === null) return `null`;
|
|
35
|
+
if (value === void 0) return `undefined`;
|
|
36
|
+
if (typeof value !== `object`) return `str_${String(value)}`;
|
|
37
|
+
let id = objectIds.get(value);
|
|
38
|
+
if (id === void 0) objectIds.set(value, id = nextObjectId++);
|
|
39
|
+
return `obj_${id}`;
|
|
86
40
|
}
|
|
87
|
-
const globalObjectIdGenerator = new ObjectIdGenerator();
|
|
88
41
|
function diffHalfOpen(a, b) {
|
|
89
42
|
const [a1, a2] = a;
|
|
90
43
|
const [b1, b2] = b;
|
|
@@ -142,7 +95,7 @@ function isJsonSafeStructuralValue(value, ancestors) {
|
|
|
142
95
|
return false;
|
|
143
96
|
}
|
|
144
97
|
return withAcyclicValue(value, ancestors, () => {
|
|
145
|
-
if (value instanceof Date || value instanceof RegExp || value instanceof Uint8Array || value instanceof Map || value instanceof Set) {
|
|
98
|
+
if (value instanceof Date || value instanceof RegExp || value instanceof Uint8Array || temporal.isTemporal(value) || value instanceof Map || value instanceof Set) {
|
|
146
99
|
return false;
|
|
147
100
|
}
|
|
148
101
|
return Array.isArray(value) ? value.every((item) => isJsonSafeStructuralValue(item, ancestors)) : Object.keys(value).every(
|
|
@@ -199,6 +152,9 @@ function toCanonicalValue(value, ancestors) {
|
|
|
199
152
|
if (value instanceof Uint8Array) {
|
|
200
153
|
return [`bytes`, Array.from(value)];
|
|
201
154
|
}
|
|
155
|
+
if (temporal.isTemporal(value)) {
|
|
156
|
+
return [`temporal`, value[Symbol.toStringTag], value.toString()];
|
|
157
|
+
}
|
|
202
158
|
if (Array.isArray(value)) {
|
|
203
159
|
return [`array`, value.map((item) => toCanonicalValue(item, ancestors))];
|
|
204
160
|
}
|
|
@@ -249,12 +205,10 @@ function withAcyclicValue(value, ancestors, encode) {
|
|
|
249
205
|
ancestors.delete(value);
|
|
250
206
|
}
|
|
251
207
|
}
|
|
252
|
-
exports.DefaultMap = DefaultMap;
|
|
253
|
-
exports.ObjectIdGenerator = ObjectIdGenerator;
|
|
254
208
|
exports.binarySearch = binarySearch;
|
|
255
209
|
exports.chunkedArrayPush = chunkedArrayPush;
|
|
256
210
|
exports.compareKeys = compareKeys;
|
|
257
211
|
exports.diffHalfOpen = diffHalfOpen;
|
|
258
|
-
exports.
|
|
212
|
+
exports.getStringId = getStringId;
|
|
259
213
|
exports.serializeValue = serializeValue;
|
|
260
214
|
//# sourceMappingURL=utils.cjs.map
|
package/dist/cjs/utils.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.cjs","sources":["../../src/utils.ts"],"sourcesContent":["/**\n * Simple assertion function for runtime checks.\n * Throws an error if the condition is false.\n */\nexport function assert(\n condition: unknown,\n message?: string,\n): asserts condition {\n if (!condition) {\n throw new Error(message || `Assertion failed`)\n }\n}\n\n/**\n * A map that returns a default value for keys that are not present.\n */\nexport class DefaultMap<K, V> extends Map<K, V> {\n constructor(\n private defaultValue: () => V,\n entries?: Iterable<[K, V]>,\n ) {\n super(entries)\n }\n\n get(key: K): V {\n if (!this.has(key)) {\n // this.set(key, this.defaultValue())\n return this.defaultValue()\n }\n return super.get(key)!\n }\n\n /**\n * Update the value for a key using a function.\n */\n update(key: K, updater: (value: V) => V): V {\n const value = this.get(key)\n const newValue = updater(value)\n this.set(key, newValue)\n return newValue\n }\n}\n\n// JS engines have various limits on how many args can be passed to a function\n// with a spread operator, so we need to split the operation into chunks\n// 32767 is the max for Chrome 14, all others are higher\n// TODO: investigate the performance of this and other approaches\nconst chunkSize = 30000\nexport function chunkedArrayPush(array: Array<unknown>, other: Array<unknown>) {\n if (other.length <= chunkSize) {\n array.push(...other)\n } else {\n for (let i = 0; i < other.length; i += chunkSize) {\n const chunk = other.slice(i, i + chunkSize)\n array.push(...chunk)\n }\n }\n}\n\nexport function binarySearch<T>(\n array: Array<T>,\n value: T,\n comparator: (a: T, b: T) => number,\n): number {\n let low = 0\n let high = array.length\n while (low < high) {\n const mid = Math.floor((low + high) / 2)\n const comparison = comparator(array[mid]!, value)\n if (comparison < 0) {\n low = mid + 1\n } else if (comparison > 0) {\n high = mid\n } else {\n return mid\n }\n }\n return low\n}\n\n/**\n * Utility for generating unique IDs for objects and values.\n * Uses WeakMap for object reference tracking and consistent hashing for primitives.\n */\nexport class ObjectIdGenerator {\n private objectIds = new WeakMap<object, number>()\n private nextId = 0\n\n /**\n * Get a unique identifier for any value.\n * - Objects: Uses WeakMap for reference-based identity\n * - Primitives: Uses consistent string-based hashing\n */\n getId(value: any): number {\n // For primitives, use a simple hash of their string representation\n if (typeof value !== `object` || value === null) {\n const str = String(value)\n let hashValue = 0\n for (let i = 0; i < str.length; i++) {\n const char = str.charCodeAt(i)\n hashValue = (hashValue << 5) - hashValue + char\n hashValue = hashValue & hashValue // Convert to 32-bit integer\n }\n return hashValue\n }\n\n // For objects, use WeakMap to assign unique IDs\n if (!this.objectIds.has(value)) {\n this.objectIds.set(value, this.nextId++)\n }\n return this.objectIds.get(value)!\n }\n\n /**\n * Get a string representation of the ID for use in composite keys.\n */\n getStringId(value: any): string {\n if (value === null) return `null`\n if (value === undefined) return `undefined`\n if (typeof value !== `object`) return `str_${String(value)}`\n\n return `obj_${this.getId(value)}`\n }\n}\n\n/**\n * Global instance for cases where a shared object ID space is needed.\n */\nexport const globalObjectIdGenerator = new ObjectIdGenerator()\n\nexport function* concatIterable<T>(\n ...iterables: Array<Iterable<T>>\n): Iterable<T> {\n for (const iterable of iterables) {\n yield* iterable\n }\n}\n\nexport function* mapIterable<T, U>(\n it: Iterable<T>,\n fn: (t: T) => U,\n): Iterable<U> {\n for (const t of it) {\n yield fn(t)\n }\n}\n\nexport type HRange = [number, number] // half-open [start, end[ i.e. end is exclusive\n\n/**\n * Computes the difference between two half-open ranges.\n * @param a - The first half-open range\n * @param b - The second half-open range\n * @returns The difference between the two ranges\n */\nexport function diffHalfOpen(a: HRange, b: HRange) {\n const [a1, a2] = a\n const [b1, b2] = b\n\n // A \\ B can be up to two segments (left and right of the overlap)\n const onlyInA: Array<number> = [\n ...range(a1, Math.min(a2, b1)), // left side of A outside B\n ...range(Math.max(a1, b2), a2), // right side of A outside B\n ]\n\n // B \\ A similarly\n const onlyInB: Array<number> = [\n ...range(b1, Math.min(b2, a1)),\n ...range(Math.max(b1, a2), b2),\n ]\n\n return { onlyInA, onlyInB }\n}\n\nfunction range(start: number, end: number): Array<number> {\n const out: Array<number> = []\n for (let i = start; i < end; i++) out.push(i)\n return out\n}\n\n/**\n * Compares two keys (string | number) in a consistent, deterministic way.\n * Handles mixed types by ordering strings before numbers.\n */\nexport function compareKeys(a: string | number, b: string | number): number {\n // Same type: compare directly\n if (typeof a === typeof b) {\n if (typeof a === `number` && typeof b === `number`) {\n const aIsNaN = Number.isNaN(a)\n const bIsNaN = Number.isNaN(b)\n if (aIsNaN || bIsNaN) {\n if (aIsNaN && bIsNaN) return 0\n return aIsNaN ? 1 : -1\n }\n }\n if (a < b) return -1\n if (a > b) return 1\n return 0\n }\n // Different types: strings come before numbers\n return typeof a === `string` ? -1 : 1\n}\n\ntype CanonicalValue =\n | readonly [`undefined`]\n | readonly [`null`]\n | readonly [`boolean`, boolean]\n | readonly [`number`, number | `NaN` | `Infinity` | `-Infinity`]\n | readonly [`bigint`, string]\n | readonly [`string`, string]\n | readonly [`date`, number | `Invalid`]\n | readonly [`regexp`, string, string]\n | readonly [`bytes`, Array<number>]\n | readonly [`array`, Array<CanonicalValue>]\n | readonly [`map`, Array<readonly [CanonicalValue, CanonicalValue]>]\n | readonly [`set`, Array<CanonicalValue>]\n | readonly [`object`, Array<readonly [string, CanonicalValue]>]\n\n/**\n * Serializes a supported query value into one canonical key.\n *\n * JSON's native encoding is not suitable for relation keys: it merges BigInt\n * with strings when a replacer is used, merges NaN with null, drops undefined,\n * and depends on object insertion order. Ordinary JSON values keep their\n * established wire form. Values that need richer types use a reserved prefix\n * plus a structural, type-tagged encoding.\n */\nexport function serializeValue(value: unknown): string {\n if (isJsonSafeStructuralValue(value, new Set())) {\n return JSON.stringify(toStableJsonValue(value))\n }\n\n return `~${JSON.stringify(toCanonicalValue(value, new Set()))}`\n}\n\ntype JsonValue =\n | null\n | boolean\n | number\n | string\n | Array<JsonValue>\n | { [key: string]: JsonValue }\n\nfunction isJsonSafeStructuralValue(\n value: unknown,\n ancestors: Set<object>,\n): boolean {\n if (value === null) return true\n\n switch (typeof value) {\n case `boolean`:\n case `string`:\n return true\n case `number`:\n return Number.isFinite(value)\n case `undefined`:\n case `bigint`:\n case `symbol`:\n case `function`:\n return false\n }\n\n return withAcyclicValue(value, ancestors, () => {\n if (\n value instanceof Date ||\n value instanceof RegExp ||\n value instanceof Uint8Array ||\n value instanceof Map ||\n value instanceof Set\n ) {\n return false\n }\n\n return Array.isArray(value)\n ? value.every((item) => isJsonSafeStructuralValue(item, ancestors))\n : Object.keys(value).every((key) =>\n isJsonSafeStructuralValue(\n (value as Record<string, unknown>)[key],\n ancestors,\n ),\n )\n })\n}\n\nfunction toStableJsonValue(value: unknown): JsonValue {\n if (\n value === null ||\n typeof value === `boolean` ||\n typeof value === `number` ||\n typeof value === `string`\n ) {\n return value\n }\n\n if (Array.isArray(value)) {\n return value.map(toStableJsonValue)\n }\n\n return Object.fromEntries(\n Object.keys(value as object)\n .sort()\n .map((key) => [\n key,\n toStableJsonValue((value as Record<string, unknown>)[key]),\n ]),\n )\n}\n\nfunction toCanonicalValue(\n value: unknown,\n ancestors: Set<object>,\n): CanonicalValue {\n if (value === undefined) return [`undefined`]\n if (value === null) return [`null`]\n\n switch (typeof value) {\n case `boolean`:\n return [`boolean`, value]\n case `number`:\n if (Number.isNaN(value)) return [`number`, `NaN`]\n if (value === Infinity) return [`number`, `Infinity`]\n if (value === -Infinity) return [`number`, `-Infinity`]\n return [`number`, value === 0 ? 0 : value]\n case `bigint`:\n return [`bigint`, value.toString()]\n case `string`:\n return [`string`, value]\n case `symbol`:\n case `function`:\n throw new TypeError(\n `Cannot serialize ${typeof value} as a structural relation key`,\n )\n }\n\n return withAcyclicValue(value, ancestors, () => {\n if (value instanceof Date) {\n const timestamp = value.getTime()\n return Number.isNaN(timestamp)\n ? ([`date`, `Invalid`] as const)\n : ([`date`, timestamp] as const)\n }\n\n if (value instanceof RegExp) {\n return [`regexp`, value.source, value.flags]\n }\n\n if (value instanceof Uint8Array) {\n return [`bytes`, Array.from(value)]\n }\n\n if (Array.isArray(value)) {\n return [`array`, value.map((item) => toCanonicalValue(item, ancestors))]\n }\n\n if (value instanceof Map) {\n const entries = [...value.entries()].map(\n ([key, entryValue]) =>\n [\n toCanonicalValue(key, ancestors),\n toCanonicalValue(entryValue, ancestors),\n ] as const,\n )\n entries.sort((left, right) =>\n compareSerializedValues(JSON.stringify(left), JSON.stringify(right)),\n )\n return [`map`, entries]\n }\n\n if (value instanceof Set) {\n const entries = [...value].map((entry) =>\n toCanonicalValue(entry, ancestors),\n )\n entries.sort((left, right) =>\n compareSerializedValues(JSON.stringify(left), JSON.stringify(right)),\n )\n return [`set`, entries]\n }\n\n const entries = Object.keys(value)\n .sort()\n .map(\n (key) =>\n [\n key,\n toCanonicalValue(\n (value as Record<string, unknown>)[key],\n ancestors,\n ),\n ] as const,\n )\n return [`object`, entries]\n })\n}\n\nfunction compareSerializedValues(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0\n}\n\nfunction withAcyclicValue<T>(\n value: object,\n ancestors: Set<object>,\n encode: () => T,\n): T {\n if (ancestors.has(value)) {\n throw new TypeError(`Cannot serialize a cyclic structural relation key`)\n }\n\n ancestors.add(value)\n try {\n return encode()\n } finally {\n ancestors.delete(value)\n }\n}\n"],"names":["entries"],"mappings":";;AAgBO,MAAM,mBAAyB,IAAU;AAAA,EAC9C,YACU,cACR,SACA;AACA,UAAM,OAAO;AAHL,SAAA,eAAA;AAAA,EAIV;AAAA,EAEA,IAAI,KAAW;AACb,QAAI,CAAC,KAAK,IAAI,GAAG,GAAG;AAElB,aAAO,KAAK,aAAA;AAAA,IACd;AACA,WAAO,MAAM,IAAI,GAAG;AAAA,EACtB;AAAA;AAAA;AAAA;AAAA,EAKA,OAAO,KAAQ,SAA6B;AAC1C,UAAM,QAAQ,KAAK,IAAI,GAAG;AAC1B,UAAM,WAAW,QAAQ,KAAK;AAC9B,SAAK,IAAI,KAAK,QAAQ;AACtB,WAAO;AAAA,EACT;AACF;AAMA,MAAM,YAAY;AACX,SAAS,iBAAiB,OAAuB,OAAuB;AAC7E,MAAI,MAAM,UAAU,WAAW;AAC7B,UAAM,KAAK,GAAG,KAAK;AAAA,EACrB,OAAO;AACL,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,WAAW;AAChD,YAAM,QAAQ,MAAM,MAAM,GAAG,IAAI,SAAS;AAC1C,YAAM,KAAK,GAAG,KAAK;AAAA,IACrB;AAAA,EACF;AACF;AAEO,SAAS,aACd,OACA,OACA,YACQ;AACR,MAAI,MAAM;AACV,MAAI,OAAO,MAAM;AACjB,SAAO,MAAM,MAAM;AACjB,UAAM,MAAM,KAAK,OAAO,MAAM,QAAQ,CAAC;AACvC,UAAM,aAAa,WAAW,MAAM,GAAG,GAAI,KAAK;AAChD,QAAI,aAAa,GAAG;AAClB,YAAM,MAAM;AAAA,IACd,WAAW,aAAa,GAAG;AACzB,aAAO;AAAA,IACT,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAMO,MAAM,kBAAkB;AAAA,EAAxB,cAAA;AACL,SAAQ,gCAAgB,QAAA;AACxB,SAAQ,SAAS;AAAA,EAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOjB,MAAM,OAAoB;AAExB,QAAI,OAAO,UAAU,YAAY,UAAU,MAAM;AAC/C,YAAM,MAAM,OAAO,KAAK;AACxB,UAAI,YAAY;AAChB,eAAS,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;AACnC,cAAM,OAAO,IAAI,WAAW,CAAC;AAC7B,qBAAa,aAAa,KAAK,YAAY;AAC3C,oBAAY,YAAY;AAAA,MAC1B;AACA,aAAO;AAAA,IACT;AAGA,QAAI,CAAC,KAAK,UAAU,IAAI,KAAK,GAAG;AAC9B,WAAK,UAAU,IAAI,OAAO,KAAK,QAAQ;AAAA,IACzC;AACA,WAAO,KAAK,UAAU,IAAI,KAAK;AAAA,EACjC;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,OAAoB;AAC9B,QAAI,UAAU,KAAM,QAAO;AAC3B,QAAI,UAAU,OAAW,QAAO;AAChC,QAAI,OAAO,UAAU,iBAAiB,OAAO,OAAO,KAAK,CAAC;AAE1D,WAAO,OAAO,KAAK,MAAM,KAAK,CAAC;AAAA,EACjC;AACF;AAKO,MAAM,0BAA0B,IAAI,kBAAA;AA2BpC,SAAS,aAAa,GAAW,GAAW;AACjD,QAAM,CAAC,IAAI,EAAE,IAAI;AACjB,QAAM,CAAC,IAAI,EAAE,IAAI;AAGjB,QAAM,UAAyB;AAAA,IAC7B,GAAG,MAAM,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;AAAA;AAAA,IAC7B,GAAG,MAAM,KAAK,IAAI,IAAI,EAAE,GAAG,EAAE;AAAA;AAAA,EAAA;AAI/B,QAAM,UAAyB;AAAA,IAC7B,GAAG,MAAM,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;AAAA,IAC7B,GAAG,MAAM,KAAK,IAAI,IAAI,EAAE,GAAG,EAAE;AAAA,EAAA;AAG/B,SAAO,EAAE,SAAS,QAAA;AACpB;AAEA,SAAS,MAAM,OAAe,KAA4B;AACxD,QAAM,MAAqB,CAAA;AAC3B,WAAS,IAAI,OAAO,IAAI,KAAK,IAAK,KAAI,KAAK,CAAC;AAC5C,SAAO;AACT;AAMO,SAAS,YAAY,GAAoB,GAA4B;AAE1E,MAAI,OAAO,MAAM,OAAO,GAAG;AACzB,QAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;AAClD,YAAM,SAAS,OAAO,MAAM,CAAC;AAC7B,YAAM,SAAS,OAAO,MAAM,CAAC;AAC7B,UAAI,UAAU,QAAQ;AACpB,YAAI,UAAU,OAAQ,QAAO;AAC7B,eAAO,SAAS,IAAI;AAAA,MACtB;AAAA,IACF;AACA,QAAI,IAAI,EAAG,QAAO;AAClB,QAAI,IAAI,EAAG,QAAO;AAClB,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,MAAM,WAAW,KAAK;AACtC;AA0BO,SAAS,eAAe,OAAwB;AACrD,MAAI,0BAA0B,OAAO,oBAAI,IAAA,CAAK,GAAG;AAC/C,WAAO,KAAK,UAAU,kBAAkB,KAAK,CAAC;AAAA,EAChD;AAEA,SAAO,IAAI,KAAK,UAAU,iBAAiB,OAAO,oBAAI,KAAK,CAAC,CAAC;AAC/D;AAUA,SAAS,0BACP,OACA,WACS;AACT,MAAI,UAAU,KAAM,QAAO;AAE3B,UAAQ,OAAO,OAAA;AAAA,IACb,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,OAAO,SAAS,KAAK;AAAA,IAC9B,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,EAAA;AAGX,SAAO,iBAAiB,OAAO,WAAW,MAAM;AAC9C,QACE,iBAAiB,QACjB,iBAAiB,UACjB,iBAAiB,cACjB,iBAAiB,OACjB,iBAAiB,KACjB;AACA,aAAO;AAAA,IACT;AAEA,WAAO,MAAM,QAAQ,KAAK,IACtB,MAAM,MAAM,CAAC,SAAS,0BAA0B,MAAM,SAAS,CAAC,IAChE,OAAO,KAAK,KAAK,EAAE;AAAA,MAAM,CAAC,QACxB;AAAA,QACG,MAAkC,GAAG;AAAA,QACtC;AAAA,MAAA;AAAA,IACF;AAAA,EAER,CAAC;AACH;AAEA,SAAS,kBAAkB,OAA2B;AACpD,MACE,UAAU,QACV,OAAO,UAAU,aACjB,OAAO,UAAU,YACjB,OAAO,UAAU,UACjB;AACA,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,iBAAiB;AAAA,EACpC;AAEA,SAAO,OAAO;AAAA,IACZ,OAAO,KAAK,KAAe,EACxB,OACA,IAAI,CAAC,QAAQ;AAAA,MACZ;AAAA,MACA,kBAAmB,MAAkC,GAAG,CAAC;AAAA,IAAA,CAC1D;AAAA,EAAA;AAEP;AAEA,SAAS,iBACP,OACA,WACgB;AAChB,MAAI,UAAU,OAAW,QAAO,CAAC,WAAW;AAC5C,MAAI,UAAU,KAAM,QAAO,CAAC,MAAM;AAElC,UAAQ,OAAO,OAAA;AAAA,IACb,KAAK;AACH,aAAO,CAAC,WAAW,KAAK;AAAA,IAC1B,KAAK;AACH,UAAI,OAAO,MAAM,KAAK,EAAG,QAAO,CAAC,UAAU,KAAK;AAChD,UAAI,UAAU,SAAU,QAAO,CAAC,UAAU,UAAU;AACpD,UAAI,UAAU,UAAW,QAAO,CAAC,UAAU,WAAW;AACtD,aAAO,CAAC,UAAU,UAAU,IAAI,IAAI,KAAK;AAAA,IAC3C,KAAK;AACH,aAAO,CAAC,UAAU,MAAM,SAAA,CAAU;AAAA,IACpC,KAAK;AACH,aAAO,CAAC,UAAU,KAAK;AAAA,IACzB,KAAK;AAAA,IACL,KAAK;AACH,YAAM,IAAI;AAAA,QACR,oBAAoB,OAAO,KAAK;AAAA,MAAA;AAAA,EAClC;AAGJ,SAAO,iBAAiB,OAAO,WAAW,MAAM;AAC9C,QAAI,iBAAiB,MAAM;AACzB,YAAM,YAAY,MAAM,QAAA;AACxB,aAAO,OAAO,MAAM,SAAS,IACxB,CAAC,QAAQ,SAAS,IAClB,CAAC,QAAQ,SAAS;AAAA,IACzB;AAEA,QAAI,iBAAiB,QAAQ;AAC3B,aAAO,CAAC,UAAU,MAAM,QAAQ,MAAM,KAAK;AAAA,IAC7C;AAEA,QAAI,iBAAiB,YAAY;AAC/B,aAAO,CAAC,SAAS,MAAM,KAAK,KAAK,CAAC;AAAA,IACpC;AAEA,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,aAAO,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,iBAAiB,MAAM,SAAS,CAAC,CAAC;AAAA,IACzE;AAEA,QAAI,iBAAiB,KAAK;AACxB,YAAMA,WAAU,CAAC,GAAG,MAAM,QAAA,CAAS,EAAE;AAAA,QACnC,CAAC,CAAC,KAAK,UAAU,MACf;AAAA,UACE,iBAAiB,KAAK,SAAS;AAAA,UAC/B,iBAAiB,YAAY,SAAS;AAAA,QAAA;AAAA,MACxC;AAEJA,eAAQ;AAAA,QAAK,CAAC,MAAM,UAClB,wBAAwB,KAAK,UAAU,IAAI,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA,MAAA;AAErE,aAAO,CAAC,OAAOA,QAAO;AAAA,IACxB;AAEA,QAAI,iBAAiB,KAAK;AACxB,YAAMA,WAAU,CAAC,GAAG,KAAK,EAAE;AAAA,QAAI,CAAC,UAC9B,iBAAiB,OAAO,SAAS;AAAA,MAAA;AAEnCA,eAAQ;AAAA,QAAK,CAAC,MAAM,UAClB,wBAAwB,KAAK,UAAU,IAAI,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA,MAAA;AAErE,aAAO,CAAC,OAAOA,QAAO;AAAA,IACxB;AAEA,UAAM,UAAU,OAAO,KAAK,KAAK,EAC9B,OACA;AAAA,MACC,CAAC,QACC;AAAA,QACE;AAAA,QACA;AAAA,UACG,MAAkC,GAAG;AAAA,UACtC;AAAA,QAAA;AAAA,MACF;AAAA,IACF;AAEN,WAAO,CAAC,UAAU,OAAO;AAAA,EAC3B,CAAC;AACH;AAEA,SAAS,wBAAwB,MAAc,OAAuB;AACpE,SAAO,OAAO,QAAQ,KAAK,OAAO,QAAQ,IAAI;AAChD;AAEA,SAAS,iBACP,OACA,WACA,QACG;AACH,MAAI,UAAU,IAAI,KAAK,GAAG;AACxB,UAAM,IAAI,UAAU,mDAAmD;AAAA,EACzE;AAEA,YAAU,IAAI,KAAK;AACnB,MAAI;AACF,WAAO,OAAA;AAAA,EACT,UAAA;AACE,cAAU,OAAO,KAAK;AAAA,EACxB;AACF;;;;;;;;;"}
|
|
1
|
+
{"version":3,"file":"utils.cjs","sources":["../../src/utils.ts"],"sourcesContent":["import { isTemporal } from './hashing/temporal.js'\n\n/**\n * Simple assertion function for runtime checks.\n * Throws an error if the condition is false.\n */\nexport function assert(\n condition: unknown,\n message?: string,\n): asserts condition {\n if (!condition) {\n throw new Error(message || `Assertion failed`)\n }\n}\n\n// JS engines have various limits on how many args can be passed to a function\n// with a spread operator, so we need to split the operation into chunks\n// 32767 is the max for Chrome 14, all others are higher\n// TODO: investigate the performance of this and other approaches\nconst chunkSize = 30000\nexport function chunkedArrayPush(array: Array<unknown>, other: Array<unknown>) {\n if (other.length <= chunkSize) {\n array.push(...other)\n } else {\n for (let i = 0; i < other.length; i += chunkSize) {\n const chunk = other.slice(i, i + chunkSize)\n array.push(...chunk)\n }\n }\n}\n\nexport function binarySearch<T>(\n array: Array<T>,\n value: T,\n comparator: (a: T, b: T) => number,\n): number {\n let low = 0\n let high = array.length\n while (low < high) {\n const mid = Math.floor((low + high) / 2)\n const comparison = comparator(array[mid]!, value)\n if (comparison < 0) {\n low = mid + 1\n } else if (comparison > 0) {\n high = mid\n } else {\n return mid\n }\n }\n return low\n}\n\nconst objectIds = new WeakMap<object, number>()\nlet nextObjectId = 0\n\n/**\n * A string identity for composite keys: primitives by value, objects by\n * reference.\n */\nexport function getStringId(value: unknown): string {\n if (value === null) return `null`\n if (value === undefined) return `undefined`\n if (typeof value !== `object`) return `str_${String(value)}`\n let id = objectIds.get(value)\n if (id === undefined) objectIds.set(value, (id = nextObjectId++))\n return `obj_${id}`\n}\n\nexport function* concatIterable<T>(\n ...iterables: Array<Iterable<T>>\n): Iterable<T> {\n for (const iterable of iterables) {\n yield* iterable\n }\n}\n\nexport function* mapIterable<T, U>(\n it: Iterable<T>,\n fn: (t: T) => U,\n): Iterable<U> {\n for (const t of it) {\n yield fn(t)\n }\n}\n\nexport type HRange = [number, number] // half-open [start, end[ i.e. end is exclusive\n\n/**\n * Computes the difference between two half-open ranges.\n * @param a - The first half-open range\n * @param b - The second half-open range\n * @returns The difference between the two ranges\n */\nexport function diffHalfOpen(a: HRange, b: HRange) {\n const [a1, a2] = a\n const [b1, b2] = b\n\n // A \\ B can be up to two segments (left and right of the overlap)\n const onlyInA: Array<number> = [\n ...range(a1, Math.min(a2, b1)), // left side of A outside B\n ...range(Math.max(a1, b2), a2), // right side of A outside B\n ]\n\n // B \\ A similarly\n const onlyInB: Array<number> = [\n ...range(b1, Math.min(b2, a1)),\n ...range(Math.max(b1, a2), b2),\n ]\n\n return { onlyInA, onlyInB }\n}\n\nfunction range(start: number, end: number): Array<number> {\n const out: Array<number> = []\n for (let i = start; i < end; i++) out.push(i)\n return out\n}\n\n/**\n * Compares two keys (string | number) in a consistent, deterministic way.\n * Handles mixed types by ordering strings before numbers.\n */\nexport function compareKeys(a: string | number, b: string | number): number {\n // Same type: compare directly\n if (typeof a === typeof b) {\n if (typeof a === `number` && typeof b === `number`) {\n const aIsNaN = Number.isNaN(a)\n const bIsNaN = Number.isNaN(b)\n if (aIsNaN || bIsNaN) {\n if (aIsNaN && bIsNaN) return 0\n return aIsNaN ? 1 : -1\n }\n }\n if (a < b) return -1\n if (a > b) return 1\n return 0\n }\n // Different types: strings come before numbers\n return typeof a === `string` ? -1 : 1\n}\n\ntype CanonicalValue =\n | readonly [`undefined`]\n | readonly [`null`]\n | readonly [`boolean`, boolean]\n | readonly [`number`, number | `NaN` | `Infinity` | `-Infinity`]\n | readonly [`bigint`, string]\n | readonly [`string`, string]\n | readonly [`date`, number | `Invalid`]\n | readonly [`temporal`, string, string]\n | readonly [`regexp`, string, string]\n | readonly [`bytes`, Array<number>]\n | readonly [`array`, Array<CanonicalValue>]\n | readonly [`map`, Array<readonly [CanonicalValue, CanonicalValue]>]\n | readonly [`set`, Array<CanonicalValue>]\n | readonly [`object`, Array<readonly [string, CanonicalValue]>]\n\n/**\n * Serializes a supported query value into one canonical key.\n *\n * JSON's native encoding is not suitable for relation keys: it merges BigInt\n * with strings when a replacer is used, merges NaN with null, drops undefined,\n * and depends on object insertion order. Ordinary JSON values keep their\n * established wire form. Values that need richer types use a reserved prefix\n * plus a structural, type-tagged encoding.\n */\nexport function serializeValue(value: unknown): string {\n if (isJsonSafeStructuralValue(value, new Set())) {\n return JSON.stringify(toStableJsonValue(value))\n }\n\n return `~${JSON.stringify(toCanonicalValue(value, new Set()))}`\n}\n\ntype JsonValue =\n | null\n | boolean\n | number\n | string\n | Array<JsonValue>\n | { [key: string]: JsonValue }\n\nfunction isJsonSafeStructuralValue(\n value: unknown,\n ancestors: Set<object>,\n): boolean {\n if (value === null) return true\n\n switch (typeof value) {\n case `boolean`:\n case `string`:\n return true\n case `number`:\n return Number.isFinite(value)\n case `undefined`:\n case `bigint`:\n case `symbol`:\n case `function`:\n return false\n }\n\n return withAcyclicValue(value, ancestors, () => {\n if (\n value instanceof Date ||\n value instanceof RegExp ||\n value instanceof Uint8Array ||\n isTemporal(value) ||\n value instanceof Map ||\n value instanceof Set\n ) {\n return false\n }\n\n return Array.isArray(value)\n ? value.every((item) => isJsonSafeStructuralValue(item, ancestors))\n : Object.keys(value).every((key) =>\n isJsonSafeStructuralValue(\n (value as Record<string, unknown>)[key],\n ancestors,\n ),\n )\n })\n}\n\nfunction toStableJsonValue(value: unknown): JsonValue {\n if (\n value === null ||\n typeof value === `boolean` ||\n typeof value === `number` ||\n typeof value === `string`\n ) {\n return value\n }\n\n if (Array.isArray(value)) {\n return value.map(toStableJsonValue)\n }\n\n return Object.fromEntries(\n Object.keys(value as object)\n .sort()\n .map((key) => [\n key,\n toStableJsonValue((value as Record<string, unknown>)[key]),\n ]),\n )\n}\n\nfunction toCanonicalValue(\n value: unknown,\n ancestors: Set<object>,\n): CanonicalValue {\n if (value === undefined) return [`undefined`]\n if (value === null) return [`null`]\n\n switch (typeof value) {\n case `boolean`:\n return [`boolean`, value]\n case `number`:\n if (Number.isNaN(value)) return [`number`, `NaN`]\n if (value === Infinity) return [`number`, `Infinity`]\n if (value === -Infinity) return [`number`, `-Infinity`]\n return [`number`, value === 0 ? 0 : value]\n case `bigint`:\n return [`bigint`, value.toString()]\n case `string`:\n return [`string`, value]\n case `symbol`:\n case `function`:\n throw new TypeError(\n `Cannot serialize ${typeof value} as a structural relation key`,\n )\n }\n\n return withAcyclicValue(value, ancestors, () => {\n if (value instanceof Date) {\n const timestamp = value.getTime()\n return Number.isNaN(timestamp)\n ? ([`date`, `Invalid`] as const)\n : ([`date`, timestamp] as const)\n }\n\n if (value instanceof RegExp) {\n return [`regexp`, value.source, value.flags]\n }\n\n if (value instanceof Uint8Array) {\n return [`bytes`, Array.from(value)]\n }\n\n if (isTemporal(value)) {\n return [`temporal`, value[Symbol.toStringTag], value.toString()]\n }\n\n if (Array.isArray(value)) {\n return [`array`, value.map((item) => toCanonicalValue(item, ancestors))]\n }\n\n if (value instanceof Map) {\n const entries = [...value.entries()].map(\n ([key, entryValue]) =>\n [\n toCanonicalValue(key, ancestors),\n toCanonicalValue(entryValue, ancestors),\n ] as const,\n )\n entries.sort((left, right) =>\n compareSerializedValues(JSON.stringify(left), JSON.stringify(right)),\n )\n return [`map`, entries]\n }\n\n if (value instanceof Set) {\n const entries = [...value].map((entry) =>\n toCanonicalValue(entry, ancestors),\n )\n entries.sort((left, right) =>\n compareSerializedValues(JSON.stringify(left), JSON.stringify(right)),\n )\n return [`set`, entries]\n }\n\n const entries = Object.keys(value)\n .sort()\n .map(\n (key) =>\n [\n key,\n toCanonicalValue(\n (value as Record<string, unknown>)[key],\n ancestors,\n ),\n ] as const,\n )\n return [`object`, entries]\n })\n}\n\nfunction compareSerializedValues(left: string, right: string): number {\n return left < right ? -1 : left > right ? 1 : 0\n}\n\nfunction withAcyclicValue<T>(\n value: object,\n ancestors: Set<object>,\n encode: () => T,\n): T {\n if (ancestors.has(value)) {\n throw new TypeError(`Cannot serialize a cyclic structural relation key`)\n }\n\n ancestors.add(value)\n try {\n return encode()\n } finally {\n ancestors.delete(value)\n }\n}\n"],"names":["isTemporal","entries"],"mappings":";;;AAmBA,MAAM,YAAY;AACX,SAAS,iBAAiB,OAAuB,OAAuB;AAC7E,MAAI,MAAM,UAAU,WAAW;AAC7B,UAAM,KAAK,GAAG,KAAK;AAAA,EACrB,OAAO;AACL,aAAS,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,WAAW;AAChD,YAAM,QAAQ,MAAM,MAAM,GAAG,IAAI,SAAS;AAC1C,YAAM,KAAK,GAAG,KAAK;AAAA,IACrB;AAAA,EACF;AACF;AAEO,SAAS,aACd,OACA,OACA,YACQ;AACR,MAAI,MAAM;AACV,MAAI,OAAO,MAAM;AACjB,SAAO,MAAM,MAAM;AACjB,UAAM,MAAM,KAAK,OAAO,MAAM,QAAQ,CAAC;AACvC,UAAM,aAAa,WAAW,MAAM,GAAG,GAAI,KAAK;AAChD,QAAI,aAAa,GAAG;AAClB,YAAM,MAAM;AAAA,IACd,WAAW,aAAa,GAAG;AACzB,aAAO;AAAA,IACT,OAAO;AACL,aAAO;AAAA,IACT;AAAA,EACF;AACA,SAAO;AACT;AAEA,MAAM,gCAAgB,QAAA;AACtB,IAAI,eAAe;AAMZ,SAAS,YAAY,OAAwB;AAClD,MAAI,UAAU,KAAM,QAAO;AAC3B,MAAI,UAAU,OAAW,QAAO;AAChC,MAAI,OAAO,UAAU,iBAAiB,OAAO,OAAO,KAAK,CAAC;AAC1D,MAAI,KAAK,UAAU,IAAI,KAAK;AAC5B,MAAI,OAAO,OAAW,WAAU,IAAI,OAAQ,KAAK,cAAe;AAChE,SAAO,OAAO,EAAE;AAClB;AA2BO,SAAS,aAAa,GAAW,GAAW;AACjD,QAAM,CAAC,IAAI,EAAE,IAAI;AACjB,QAAM,CAAC,IAAI,EAAE,IAAI;AAGjB,QAAM,UAAyB;AAAA,IAC7B,GAAG,MAAM,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;AAAA;AAAA,IAC7B,GAAG,MAAM,KAAK,IAAI,IAAI,EAAE,GAAG,EAAE;AAAA;AAAA,EAAA;AAI/B,QAAM,UAAyB;AAAA,IAC7B,GAAG,MAAM,IAAI,KAAK,IAAI,IAAI,EAAE,CAAC;AAAA,IAC7B,GAAG,MAAM,KAAK,IAAI,IAAI,EAAE,GAAG,EAAE;AAAA,EAAA;AAG/B,SAAO,EAAE,SAAS,QAAA;AACpB;AAEA,SAAS,MAAM,OAAe,KAA4B;AACxD,QAAM,MAAqB,CAAA;AAC3B,WAAS,IAAI,OAAO,IAAI,KAAK,IAAK,KAAI,KAAK,CAAC;AAC5C,SAAO;AACT;AAMO,SAAS,YAAY,GAAoB,GAA4B;AAE1E,MAAI,OAAO,MAAM,OAAO,GAAG;AACzB,QAAI,OAAO,MAAM,YAAY,OAAO,MAAM,UAAU;AAClD,YAAM,SAAS,OAAO,MAAM,CAAC;AAC7B,YAAM,SAAS,OAAO,MAAM,CAAC;AAC7B,UAAI,UAAU,QAAQ;AACpB,YAAI,UAAU,OAAQ,QAAO;AAC7B,eAAO,SAAS,IAAI;AAAA,MACtB;AAAA,IACF;AACA,QAAI,IAAI,EAAG,QAAO;AAClB,QAAI,IAAI,EAAG,QAAO;AAClB,WAAO;AAAA,EACT;AAEA,SAAO,OAAO,MAAM,WAAW,KAAK;AACtC;AA2BO,SAAS,eAAe,OAAwB;AACrD,MAAI,0BAA0B,OAAO,oBAAI,IAAA,CAAK,GAAG;AAC/C,WAAO,KAAK,UAAU,kBAAkB,KAAK,CAAC;AAAA,EAChD;AAEA,SAAO,IAAI,KAAK,UAAU,iBAAiB,OAAO,oBAAI,KAAK,CAAC,CAAC;AAC/D;AAUA,SAAS,0BACP,OACA,WACS;AACT,MAAI,UAAU,KAAM,QAAO;AAE3B,UAAQ,OAAO,OAAA;AAAA,IACb,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,IACT,KAAK;AACH,aAAO,OAAO,SAAS,KAAK;AAAA,IAC9B,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AACH,aAAO;AAAA,EAAA;AAGX,SAAO,iBAAiB,OAAO,WAAW,MAAM;AAC9C,QACE,iBAAiB,QACjB,iBAAiB,UACjB,iBAAiB,cACjBA,SAAAA,WAAW,KAAK,KAChB,iBAAiB,OACjB,iBAAiB,KACjB;AACA,aAAO;AAAA,IACT;AAEA,WAAO,MAAM,QAAQ,KAAK,IACtB,MAAM,MAAM,CAAC,SAAS,0BAA0B,MAAM,SAAS,CAAC,IAChE,OAAO,KAAK,KAAK,EAAE;AAAA,MAAM,CAAC,QACxB;AAAA,QACG,MAAkC,GAAG;AAAA,QACtC;AAAA,MAAA;AAAA,IACF;AAAA,EAER,CAAC;AACH;AAEA,SAAS,kBAAkB,OAA2B;AACpD,MACE,UAAU,QACV,OAAO,UAAU,aACjB,OAAO,UAAU,YACjB,OAAO,UAAU,UACjB;AACA,WAAO;AAAA,EACT;AAEA,MAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,WAAO,MAAM,IAAI,iBAAiB;AAAA,EACpC;AAEA,SAAO,OAAO;AAAA,IACZ,OAAO,KAAK,KAAe,EACxB,OACA,IAAI,CAAC,QAAQ;AAAA,MACZ;AAAA,MACA,kBAAmB,MAAkC,GAAG,CAAC;AAAA,IAAA,CAC1D;AAAA,EAAA;AAEP;AAEA,SAAS,iBACP,OACA,WACgB;AAChB,MAAI,UAAU,OAAW,QAAO,CAAC,WAAW;AAC5C,MAAI,UAAU,KAAM,QAAO,CAAC,MAAM;AAElC,UAAQ,OAAO,OAAA;AAAA,IACb,KAAK;AACH,aAAO,CAAC,WAAW,KAAK;AAAA,IAC1B,KAAK;AACH,UAAI,OAAO,MAAM,KAAK,EAAG,QAAO,CAAC,UAAU,KAAK;AAChD,UAAI,UAAU,SAAU,QAAO,CAAC,UAAU,UAAU;AACpD,UAAI,UAAU,UAAW,QAAO,CAAC,UAAU,WAAW;AACtD,aAAO,CAAC,UAAU,UAAU,IAAI,IAAI,KAAK;AAAA,IAC3C,KAAK;AACH,aAAO,CAAC,UAAU,MAAM,SAAA,CAAU;AAAA,IACpC,KAAK;AACH,aAAO,CAAC,UAAU,KAAK;AAAA,IACzB,KAAK;AAAA,IACL,KAAK;AACH,YAAM,IAAI;AAAA,QACR,oBAAoB,OAAO,KAAK;AAAA,MAAA;AAAA,EAClC;AAGJ,SAAO,iBAAiB,OAAO,WAAW,MAAM;AAC9C,QAAI,iBAAiB,MAAM;AACzB,YAAM,YAAY,MAAM,QAAA;AACxB,aAAO,OAAO,MAAM,SAAS,IACxB,CAAC,QAAQ,SAAS,IAClB,CAAC,QAAQ,SAAS;AAAA,IACzB;AAEA,QAAI,iBAAiB,QAAQ;AAC3B,aAAO,CAAC,UAAU,MAAM,QAAQ,MAAM,KAAK;AAAA,IAC7C;AAEA,QAAI,iBAAiB,YAAY;AAC/B,aAAO,CAAC,SAAS,MAAM,KAAK,KAAK,CAAC;AAAA,IACpC;AAEA,QAAIA,SAAAA,WAAW,KAAK,GAAG;AACrB,aAAO,CAAC,YAAY,MAAM,OAAO,WAAW,GAAG,MAAM,UAAU;AAAA,IACjE;AAEA,QAAI,MAAM,QAAQ,KAAK,GAAG;AACxB,aAAO,CAAC,SAAS,MAAM,IAAI,CAAC,SAAS,iBAAiB,MAAM,SAAS,CAAC,CAAC;AAAA,IACzE;AAEA,QAAI,iBAAiB,KAAK;AACxB,YAAMC,WAAU,CAAC,GAAG,MAAM,QAAA,CAAS,EAAE;AAAA,QACnC,CAAC,CAAC,KAAK,UAAU,MACf;AAAA,UACE,iBAAiB,KAAK,SAAS;AAAA,UAC/B,iBAAiB,YAAY,SAAS;AAAA,QAAA;AAAA,MACxC;AAEJA,eAAQ;AAAA,QAAK,CAAC,MAAM,UAClB,wBAAwB,KAAK,UAAU,IAAI,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA,MAAA;AAErE,aAAO,CAAC,OAAOA,QAAO;AAAA,IACxB;AAEA,QAAI,iBAAiB,KAAK;AACxB,YAAMA,WAAU,CAAC,GAAG,KAAK,EAAE;AAAA,QAAI,CAAC,UAC9B,iBAAiB,OAAO,SAAS;AAAA,MAAA;AAEnCA,eAAQ;AAAA,QAAK,CAAC,MAAM,UAClB,wBAAwB,KAAK,UAAU,IAAI,GAAG,KAAK,UAAU,KAAK,CAAC;AAAA,MAAA;AAErE,aAAO,CAAC,OAAOA,QAAO;AAAA,IACxB;AAEA,UAAM,UAAU,OAAO,KAAK,KAAK,EAC9B,OACA;AAAA,MACC,CAAC,QACC;AAAA,QACE;AAAA,QACA;AAAA,UACG,MAAkC,GAAG;AAAA,UACtC;AAAA,QAAA;AAAA,MACF;AAAA,IACF;AAEN,WAAO,CAAC,UAAU,OAAO;AAAA,EAC3B,CAAC;AACH;AAEA,SAAS,wBAAwB,MAAc,OAAuB;AACpE,SAAO,OAAO,QAAQ,KAAK,OAAO,QAAQ,IAAI;AAChD;AAEA,SAAS,iBACP,OACA,WACA,QACG;AACH,MAAI,UAAU,IAAI,KAAK,GAAG;AACxB,UAAM,IAAI,UAAU,mDAAmD;AAAA,EACzE;AAEA,YAAU,IAAI,KAAK;AACnB,MAAI;AACF,WAAO,OAAA;AAAA,EACT,UAAA;AACE,cAAU,OAAO,KAAK;AAAA,EACxB;AACF;;;;;;;"}
|
package/dist/cjs/utils.d.cts
CHANGED
|
@@ -3,42 +3,13 @@
|
|
|
3
3
|
* Throws an error if the condition is false.
|
|
4
4
|
*/
|
|
5
5
|
export declare function assert(condition: unknown, message?: string): asserts condition;
|
|
6
|
-
/**
|
|
7
|
-
* A map that returns a default value for keys that are not present.
|
|
8
|
-
*/
|
|
9
|
-
export declare class DefaultMap<K, V> extends Map<K, V> {
|
|
10
|
-
private defaultValue;
|
|
11
|
-
constructor(defaultValue: () => V, entries?: Iterable<[K, V]>);
|
|
12
|
-
get(key: K): V;
|
|
13
|
-
/**
|
|
14
|
-
* Update the value for a key using a function.
|
|
15
|
-
*/
|
|
16
|
-
update(key: K, updater: (value: V) => V): V;
|
|
17
|
-
}
|
|
18
6
|
export declare function chunkedArrayPush(array: Array<unknown>, other: Array<unknown>): void;
|
|
19
7
|
export declare function binarySearch<T>(array: Array<T>, value: T, comparator: (a: T, b: T) => number): number;
|
|
20
8
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*/
|
|
24
|
-
export declare class ObjectIdGenerator {
|
|
25
|
-
private objectIds;
|
|
26
|
-
private nextId;
|
|
27
|
-
/**
|
|
28
|
-
* Get a unique identifier for any value.
|
|
29
|
-
* - Objects: Uses WeakMap for reference-based identity
|
|
30
|
-
* - Primitives: Uses consistent string-based hashing
|
|
31
|
-
*/
|
|
32
|
-
getId(value: any): number;
|
|
33
|
-
/**
|
|
34
|
-
* Get a string representation of the ID for use in composite keys.
|
|
35
|
-
*/
|
|
36
|
-
getStringId(value: any): string;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Global instance for cases where a shared object ID space is needed.
|
|
9
|
+
* A string identity for composite keys: primitives by value, objects by
|
|
10
|
+
* reference.
|
|
40
11
|
*/
|
|
41
|
-
export declare
|
|
12
|
+
export declare function getStringId(value: unknown): string;
|
|
42
13
|
export declare function concatIterable<T>(...iterables: Array<Iterable<T>>): Iterable<T>;
|
|
43
14
|
export declare function mapIterable<T, U>(it: Iterable<T>, fn: (t: T) => U): Iterable<U>;
|
|
44
15
|
export type HRange = [number, number];
|
package/dist/esm/d2.js
CHANGED
|
@@ -40,8 +40,18 @@ class D2 {
|
|
|
40
40
|
return this.#operators.some((op) => op.hasPendingWork());
|
|
41
41
|
}
|
|
42
42
|
run() {
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
if (!this.#finalized) {
|
|
44
|
+
throw new Error(`Graph not finalized`);
|
|
45
|
+
}
|
|
46
|
+
let ran = true;
|
|
47
|
+
while (ran) {
|
|
48
|
+
ran = false;
|
|
49
|
+
for (const op of this.#operators) {
|
|
50
|
+
if (op.hasPendingWork()) {
|
|
51
|
+
op.run();
|
|
52
|
+
ran = true;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
45
55
|
}
|
|
46
56
|
}
|
|
47
57
|
}
|
package/dist/esm/d2.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"d2.js","sources":["../../src/d2.ts"],"sourcesContent":["import { DifferenceStreamWriter } from './graph.js'\nimport type {\n BinaryOperator,\n DifferenceStreamReader,\n UnaryOperator,\n} from './graph.js'\nimport type { MultiSet, MultiSetArray } from './multiset.js'\nimport type { ID2, IStreamBuilder, PipedOperator } from './types.js'\n\nexport class D2 implements ID2 {\n #operators: Array<UnaryOperator<any> | BinaryOperator<any>> = []\n #nextOperatorId = 0\n #finalized = false\n\n constructor() {}\n\n #checkNotFinalized(): void {\n if (this.#finalized) {\n throw new Error(`Graph already finalized`)\n }\n }\n\n getNextOperatorId(): number {\n this.#checkNotFinalized()\n return this.#nextOperatorId++\n }\n\n newInput<T>(): RootStreamBuilder<T> {\n this.#checkNotFinalized()\n const writer = new DifferenceStreamWriter<T>()\n // Use the root stream builder that exposes the sendData and sendFrontier methods\n const streamBuilder = new RootStreamBuilder<T>(this, writer)\n return streamBuilder\n }\n\n addOperator(operator: UnaryOperator<any> | BinaryOperator<any>): void {\n this.#checkNotFinalized()\n this.#operators.push(operator)\n }\n\n finalize() {\n this.#checkNotFinalized()\n this.#finalized = true\n }\n\n step(): void {\n if (!this.#finalized) {\n throw new Error(`Graph not finalized`)\n }\n for (const op of this.#operators) {\n op.run()\n }\n }\n\n pendingWork(): boolean {\n return this.#operators.some((op) => op.hasPendingWork())\n }\n\n run(): void {\n while (this.pendingWork()) {\n this.step()\n }\n }\n}\n\nexport class StreamBuilder<T> implements IStreamBuilder<T> {\n #graph: ID2\n #writer: DifferenceStreamWriter<T>\n\n constructor(graph: ID2, writer: DifferenceStreamWriter<T>) {\n this.#graph = graph\n this.#writer = writer\n }\n\n connectReader(): DifferenceStreamReader<T> {\n return this.#writer.newReader()\n }\n\n get writer(): DifferenceStreamWriter<T> {\n return this.#writer\n }\n\n get graph(): ID2 {\n return this.#graph\n }\n\n // Don't judge, this is the only way to type this function.\n // rxjs has very similar code to type its pipe function\n // https://github.com/ReactiveX/rxjs/blob/master/packages/rxjs/src/internal/util/pipe.ts\n // We go to 20 operators deep, because surly that's enough for anyone...\n // A user can always split the pipe into multiple pipes to get around this.\n pipe<O>(o1: PipedOperator<T, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, T19>, o19: PipedOperator<T19, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, T19>, o19: PipedOperator<T19, T20>, o20: PipedOperator<T20, O>): IStreamBuilder<O>\n\n pipe(...operators: Array<PipedOperator<any, any>>): IStreamBuilder<any> {\n return operators.reduce((stream, operator) => {\n return operator(stream)\n }, this as IStreamBuilder<any>)\n }\n}\n\nexport class RootStreamBuilder<T> extends StreamBuilder<T> {\n sendData(collection: MultiSet<T> | MultiSetArray<T>): void {\n this.writer.sendData(collection)\n }\n}\n"],"names":[],"mappings":";AASO,MAAM,GAAkB;AAAA,EAC7B,aAA8D,CAAA;AAAA,EAC9D,kBAAkB;AAAA,EAClB,aAAa;AAAA,EAEb,cAAc;AAAA,EAAC;AAAA,EAEf,qBAA2B;AACzB,QAAI,KAAK,YAAY;AACnB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAAA,EACF;AAAA,EAEA,oBAA4B;AAC1B,SAAK,mBAAA;AACL,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAoC;AAClC,SAAK,mBAAA;AACL,UAAM,SAAS,IAAI,uBAAA;AAEnB,UAAM,gBAAgB,IAAI,kBAAqB,MAAM,MAAM;AAC3D,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,UAA0D;AACpE,SAAK,mBAAA;AACL,SAAK,WAAW,KAAK,QAAQ;AAAA,EAC/B;AAAA,EAEA,WAAW;AACT,SAAK,mBAAA;AACL,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,OAAa;AACX,QAAI,CAAC,KAAK,YAAY;AACpB,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACvC;AACA,eAAW,MAAM,KAAK,YAAY;AAChC,SAAG,IAAA;AAAA,IACL;AAAA,EACF;AAAA,EAEA,cAAuB;AACrB,WAAO,KAAK,WAAW,KAAK,CAAC,OAAO,GAAG,gBAAgB;AAAA,EACzD;AAAA,EAEA,MAAY;AACV,WAAO,KAAK,eAAe;AACzB,WAAK,KAAA;AAAA,IACP;AAAA,EACF;AACF;AAEO,MAAM,cAA8C;AAAA,EACzD;AAAA,EACA;AAAA,EAEA,YAAY,OAAY,QAAmC;AACzD,SAAK,SAAS;AACd,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,gBAA2C;AACzC,WAAO,KAAK,QAAQ,UAAA;AAAA,EACtB;AAAA,EAEA,IAAI,SAAoC;AACtC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,QAAa;AACf,WAAO,KAAK;AAAA,EACd;AAAA,EA+CA,QAAQ,WAAgE;AACtE,WAAO,UAAU,OAAO,CAAC,QAAQ,aAAa;AAC5C,aAAO,SAAS,MAAM;AAAA,IACxB,GAAG,IAA2B;AAAA,EAChC;AACF;AAEO,MAAM,0BAA6B,cAAiB;AAAA,EACzD,SAAS,YAAkD;AACzD,SAAK,OAAO,SAAS,UAAU;AAAA,EACjC;AACF;"}
|
|
1
|
+
{"version":3,"file":"d2.js","sources":["../../src/d2.ts"],"sourcesContent":["import { DifferenceStreamWriter } from './graph.js'\nimport type {\n BinaryOperator,\n DifferenceStreamReader,\n UnaryOperator,\n} from './graph.js'\nimport type { MultiSet, MultiSetArray } from './multiset.js'\nimport type { ID2, IStreamBuilder, PipedOperator } from './types.js'\n\nexport class D2 implements ID2 {\n #operators: Array<UnaryOperator<any> | BinaryOperator<any>> = []\n #nextOperatorId = 0\n #finalized = false\n\n constructor() {}\n\n #checkNotFinalized(): void {\n if (this.#finalized) {\n throw new Error(`Graph already finalized`)\n }\n }\n\n getNextOperatorId(): number {\n this.#checkNotFinalized()\n return this.#nextOperatorId++\n }\n\n newInput<T>(): RootStreamBuilder<T> {\n this.#checkNotFinalized()\n const writer = new DifferenceStreamWriter<T>()\n // Use the root stream builder that exposes the sendData and sendFrontier methods\n const streamBuilder = new RootStreamBuilder<T>(this, writer)\n return streamBuilder\n }\n\n addOperator(operator: UnaryOperator<any> | BinaryOperator<any>): void {\n this.#checkNotFinalized()\n this.#operators.push(operator)\n }\n\n finalize() {\n this.#checkNotFinalized()\n this.#finalized = true\n }\n\n step(): void {\n if (!this.#finalized) {\n throw new Error(`Graph not finalized`)\n }\n for (const op of this.#operators) {\n op.run()\n }\n }\n\n pendingWork(): boolean {\n return this.#operators.some((op) => op.hasPendingWork())\n }\n\n run(): void {\n if (!this.#finalized) {\n throw new Error(`Graph not finalized`)\n }\n\n // A later registered operator can send work to an earlier one. Continue\n // until a complete pass runs none.\n let ran = true\n while (ran) {\n ran = false\n for (const op of this.#operators) {\n if (op.hasPendingWork()) {\n op.run()\n ran = true\n }\n }\n }\n }\n}\n\nexport class StreamBuilder<T> implements IStreamBuilder<T> {\n #graph: ID2\n #writer: DifferenceStreamWriter<T>\n\n constructor(graph: ID2, writer: DifferenceStreamWriter<T>) {\n this.#graph = graph\n this.#writer = writer\n }\n\n connectReader(): DifferenceStreamReader<T> {\n return this.#writer.newReader()\n }\n\n get writer(): DifferenceStreamWriter<T> {\n return this.#writer\n }\n\n get graph(): ID2 {\n return this.#graph\n }\n\n // Don't judge, this is the only way to type this function.\n // rxjs has very similar code to type its pipe function\n // https://github.com/ReactiveX/rxjs/blob/master/packages/rxjs/src/internal/util/pipe.ts\n // We go to 20 operators deep, because surly that's enough for anyone...\n // A user can always split the pipe into multiple pipes to get around this.\n pipe<O>(o1: PipedOperator<T, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, T19>, o19: PipedOperator<T19, O>): IStreamBuilder<O>\n // prettier-ignore\n pipe<T2, T3, T4, T5, T6, T7, T8, T9, T10, T11, T12, T13, T14, T15, T16, T17, T18, T19, T20, O>(o1: PipedOperator<T, T2>, o2: PipedOperator<T2, T3>, o3: PipedOperator<T3, T4>, o4: PipedOperator<T4, T5>, o5: PipedOperator<T5, T6>, o6: PipedOperator<T6, T7>, o7: PipedOperator<T7, T8>, o8: PipedOperator<T8, T9>, o9: PipedOperator<T9, T10>, o10: PipedOperator<T10, T11>, o11: PipedOperator<T11, T12>, o12: PipedOperator<T12, T13>, o13: PipedOperator<T13, T14>, o14: PipedOperator<T14, T15>, o15: PipedOperator<T15, T16>, o16: PipedOperator<T16, T17>, o17: PipedOperator<T17, T18>, o18: PipedOperator<T18, T19>, o19: PipedOperator<T19, T20>, o20: PipedOperator<T20, O>): IStreamBuilder<O>\n\n pipe(...operators: Array<PipedOperator<any, any>>): IStreamBuilder<any> {\n return operators.reduce((stream, operator) => {\n return operator(stream)\n }, this as IStreamBuilder<any>)\n }\n}\n\nexport class RootStreamBuilder<T> extends StreamBuilder<T> {\n sendData(collection: MultiSet<T> | MultiSetArray<T>): void {\n this.writer.sendData(collection)\n }\n}\n"],"names":[],"mappings":";AASO,MAAM,GAAkB;AAAA,EAC7B,aAA8D,CAAA;AAAA,EAC9D,kBAAkB;AAAA,EAClB,aAAa;AAAA,EAEb,cAAc;AAAA,EAAC;AAAA,EAEf,qBAA2B;AACzB,QAAI,KAAK,YAAY;AACnB,YAAM,IAAI,MAAM,yBAAyB;AAAA,IAC3C;AAAA,EACF;AAAA,EAEA,oBAA4B;AAC1B,SAAK,mBAAA;AACL,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,WAAoC;AAClC,SAAK,mBAAA;AACL,UAAM,SAAS,IAAI,uBAAA;AAEnB,UAAM,gBAAgB,IAAI,kBAAqB,MAAM,MAAM;AAC3D,WAAO;AAAA,EACT;AAAA,EAEA,YAAY,UAA0D;AACpE,SAAK,mBAAA;AACL,SAAK,WAAW,KAAK,QAAQ;AAAA,EAC/B;AAAA,EAEA,WAAW;AACT,SAAK,mBAAA;AACL,SAAK,aAAa;AAAA,EACpB;AAAA,EAEA,OAAa;AACX,QAAI,CAAC,KAAK,YAAY;AACpB,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACvC;AACA,eAAW,MAAM,KAAK,YAAY;AAChC,SAAG,IAAA;AAAA,IACL;AAAA,EACF;AAAA,EAEA,cAAuB;AACrB,WAAO,KAAK,WAAW,KAAK,CAAC,OAAO,GAAG,gBAAgB;AAAA,EACzD;AAAA,EAEA,MAAY;AACV,QAAI,CAAC,KAAK,YAAY;AACpB,YAAM,IAAI,MAAM,qBAAqB;AAAA,IACvC;AAIA,QAAI,MAAM;AACV,WAAO,KAAK;AACV,YAAM;AACN,iBAAW,MAAM,KAAK,YAAY;AAChC,YAAI,GAAG,kBAAkB;AACvB,aAAG,IAAA;AACH,gBAAM;AAAA,QACR;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;AAEO,MAAM,cAA8C;AAAA,EACzD;AAAA,EACA;AAAA,EAEA,YAAY,OAAY,QAAmC;AACzD,SAAK,SAAS;AACd,SAAK,UAAU;AAAA,EACjB;AAAA,EAEA,gBAA2C;AACzC,WAAO,KAAK,QAAQ,UAAA;AAAA,EACtB;AAAA,EAEA,IAAI,SAAoC;AACtC,WAAO,KAAK;AAAA,EACd;AAAA,EAEA,IAAI,QAAa;AACf,WAAO,KAAK;AAAA,EACd;AAAA,EA+CA,QAAQ,WAAgE;AACtE,WAAO,UAAU,OAAO,CAAC,QAAQ,aAAa;AAC5C,aAAO,SAAS,MAAM;AAAA,IACxB,GAAG,IAA2B;AAAA,EAChC;AACF;AAEO,MAAM,0BAA6B,cAAiB;AAAA,EACzD,SAAS,YAAkD;AACzD,SAAK,OAAO,SAAS,UAAU;AAAA,EACjC;AACF;"}
|