@enspirit/bmg-js 1.0.2 → 1.1.0

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.
Files changed (194) hide show
  1. package/README.md +3 -2
  2. package/dist/AsyncRelation/Base.d.ts +47 -0
  3. package/dist/AsyncRelation/index.d.ts +25 -0
  4. package/dist/Relation/Memory.d.ts +2 -1
  5. package/dist/Relation/index.d.ts +1 -1
  6. package/dist/async/Relation/Base.d.ts +47 -0
  7. package/dist/async/Relation/index.d.ts +25 -0
  8. package/dist/async/operators/_helpers.d.ts +16 -0
  9. package/dist/async/operators/autowrap.d.ts +7 -0
  10. package/dist/async/operators/constants.d.ts +6 -0
  11. package/dist/async/operators/cross_product.d.ts +9 -0
  12. package/dist/async/operators/extend.d.ts +7 -0
  13. package/dist/async/operators/group.d.ts +7 -0
  14. package/dist/async/operators/image.d.ts +8 -0
  15. package/dist/async/operators/index.d.ts +28 -0
  16. package/dist/async/operators/intersect.d.ts +7 -0
  17. package/dist/async/operators/isEqual.d.ts +7 -0
  18. package/dist/async/operators/join.d.ts +7 -0
  19. package/dist/async/operators/left_join.d.ts +8 -0
  20. package/dist/async/operators/matching.d.ts +7 -0
  21. package/dist/async/operators/minus.d.ts +7 -0
  22. package/dist/async/operators/not_matching.d.ts +7 -0
  23. package/dist/async/operators/one.d.ts +6 -0
  24. package/dist/async/operators/prefix.d.ts +6 -0
  25. package/dist/async/operators/project.d.ts +10 -0
  26. package/dist/async/operators/rename.d.ts +6 -0
  27. package/dist/async/operators/restrict.d.ts +14 -0
  28. package/dist/async/operators/suffix.d.ts +6 -0
  29. package/dist/async/operators/summarize.d.ts +8 -0
  30. package/dist/async/operators/toArray.d.ts +5 -0
  31. package/dist/async/operators/transform.d.ts +9 -0
  32. package/dist/async/operators/ungroup.d.ts +7 -0
  33. package/dist/async/operators/union.d.ts +6 -0
  34. package/dist/async/operators/unwrap.d.ts +6 -0
  35. package/dist/async/operators/wrap.d.ts +6 -0
  36. package/dist/async/operators/yByX.d.ts +7 -0
  37. package/dist/async/types.d.ts +58 -0
  38. package/dist/async-operators/_helpers.d.ts +16 -0
  39. package/dist/async-operators/autowrap.d.ts +7 -0
  40. package/dist/async-operators/constants.d.ts +6 -0
  41. package/dist/async-operators/cross_product.d.ts +9 -0
  42. package/dist/async-operators/extend.d.ts +7 -0
  43. package/dist/async-operators/group.d.ts +7 -0
  44. package/dist/async-operators/image.d.ts +8 -0
  45. package/dist/async-operators/index.d.ts +28 -0
  46. package/dist/async-operators/intersect.d.ts +7 -0
  47. package/dist/async-operators/isEqual.d.ts +7 -0
  48. package/dist/async-operators/join.d.ts +7 -0
  49. package/dist/async-operators/left_join.d.ts +8 -0
  50. package/dist/async-operators/matching.d.ts +7 -0
  51. package/dist/async-operators/minus.d.ts +7 -0
  52. package/dist/async-operators/not_matching.d.ts +7 -0
  53. package/dist/async-operators/one.d.ts +6 -0
  54. package/dist/async-operators/prefix.d.ts +6 -0
  55. package/dist/async-operators/project.d.ts +10 -0
  56. package/dist/async-operators/rename.d.ts +6 -0
  57. package/dist/async-operators/restrict.d.ts +14 -0
  58. package/dist/async-operators/suffix.d.ts +6 -0
  59. package/dist/async-operators/summarize.d.ts +8 -0
  60. package/dist/async-operators/toArray.d.ts +5 -0
  61. package/dist/async-operators/transform.d.ts +9 -0
  62. package/dist/async-operators/ungroup.d.ts +7 -0
  63. package/dist/async-operators/union.d.ts +6 -0
  64. package/dist/async-operators/unwrap.d.ts +6 -0
  65. package/dist/async-operators/wrap.d.ts +6 -0
  66. package/dist/async-operators/yByX.d.ts +7 -0
  67. package/dist/async-types.d.ts +58 -0
  68. package/dist/async.d.ts +4 -0
  69. package/dist/bmg.cjs +1 -1
  70. package/dist/bmg.cjs.map +1 -1
  71. package/dist/bmg.modern.js +1 -1
  72. package/dist/bmg.modern.js.map +1 -1
  73. package/dist/bmg.module.js +1 -1
  74. package/dist/bmg.module.js.map +1 -1
  75. package/dist/bmg.umd.js +1 -1
  76. package/dist/bmg.umd.js.map +1 -1
  77. package/dist/index.d.ts +14 -3
  78. package/dist/lib-definitions.d.ts +1 -1
  79. package/dist/operators/index.d.ts +1 -30
  80. package/dist/operators/isEqual.d.ts +1 -2
  81. package/dist/operators/isRelation.d.ts +2 -1
  82. package/dist/sync/Relation/Memory.d.ts +46 -0
  83. package/dist/sync/Relation/index.d.ts +1 -0
  84. package/dist/sync/operators/_helpers.d.ts +142 -0
  85. package/dist/sync/operators/allbut.d.ts +2 -0
  86. package/dist/sync/operators/autowrap.d.ts +2 -0
  87. package/dist/sync/operators/constants.d.ts +2 -0
  88. package/dist/sync/operators/cross_product.d.ts +3 -0
  89. package/dist/sync/operators/exclude.d.ts +2 -0
  90. package/dist/sync/operators/extend.d.ts +2 -0
  91. package/dist/sync/operators/group.d.ts +2 -0
  92. package/dist/sync/operators/image.d.ts +2 -0
  93. package/dist/sync/operators/index.d.ts +30 -0
  94. package/dist/sync/operators/intersect.d.ts +2 -0
  95. package/dist/sync/operators/isEqual.d.ts +1 -0
  96. package/dist/sync/operators/isRelation.d.ts +2 -0
  97. package/dist/sync/operators/join.d.ts +2 -0
  98. package/dist/sync/operators/left_join.d.ts +2 -0
  99. package/dist/sync/operators/matching.d.ts +2 -0
  100. package/dist/sync/operators/minus.d.ts +2 -0
  101. package/dist/sync/operators/not_matching.d.ts +2 -0
  102. package/dist/sync/operators/one.d.ts +2 -0
  103. package/dist/sync/operators/prefix.d.ts +2 -0
  104. package/dist/sync/operators/project.d.ts +2 -0
  105. package/dist/sync/operators/rename.d.ts +2 -0
  106. package/dist/sync/operators/restrict.d.ts +2 -0
  107. package/dist/sync/operators/suffix.d.ts +2 -0
  108. package/dist/sync/operators/summarize.d.ts +2 -0
  109. package/dist/sync/operators/transform.d.ts +2 -0
  110. package/dist/sync/operators/ungroup.d.ts +2 -0
  111. package/dist/sync/operators/union.d.ts +2 -0
  112. package/dist/sync/operators/unwrap.d.ts +2 -0
  113. package/dist/sync/operators/wrap.d.ts +2 -0
  114. package/dist/sync/operators/yByX.d.ts +2 -0
  115. package/dist/types.d.ts +7 -0
  116. package/dist/writer/Text.d.ts +40 -0
  117. package/dist/writer/index.d.ts +1 -0
  118. package/package.json +1 -1
  119. package/src/Relation/index.ts +2 -1
  120. package/src/async/Relation/Base.ts +245 -0
  121. package/src/async/Relation/index.ts +31 -0
  122. package/src/async/operators/_helpers.ts +60 -0
  123. package/src/async/operators/autowrap.ts +31 -0
  124. package/src/async/operators/constants.ts +26 -0
  125. package/src/async/operators/cross_product.ts +39 -0
  126. package/src/async/operators/extend.ts +36 -0
  127. package/src/async/operators/group.ts +61 -0
  128. package/src/async/operators/image.ts +42 -0
  129. package/src/async/operators/index.ts +28 -0
  130. package/src/async/operators/intersect.ts +28 -0
  131. package/src/async/operators/isEqual.ts +39 -0
  132. package/src/async/operators/join.ts +39 -0
  133. package/src/async/operators/left_join.ts +55 -0
  134. package/src/async/operators/matching.ts +39 -0
  135. package/src/async/operators/minus.ts +28 -0
  136. package/src/async/operators/not_matching.ts +39 -0
  137. package/src/async/operators/one.ts +25 -0
  138. package/src/async/operators/prefix.ts +15 -0
  139. package/src/async/operators/project.ts +64 -0
  140. package/src/async/operators/rename.ts +33 -0
  141. package/src/async/operators/restrict.ts +61 -0
  142. package/src/async/operators/suffix.ts +15 -0
  143. package/src/async/operators/summarize.ts +90 -0
  144. package/src/async/operators/toArray.ts +18 -0
  145. package/src/async/operators/transform.ts +43 -0
  146. package/src/async/operators/ungroup.ts +43 -0
  147. package/src/async/operators/union.ts +29 -0
  148. package/src/async/operators/unwrap.ts +31 -0
  149. package/src/async/operators/wrap.ts +32 -0
  150. package/src/async/operators/yByX.ts +19 -0
  151. package/src/async/types.ts +86 -0
  152. package/src/async.ts +4 -0
  153. package/src/index.ts +16 -3
  154. package/src/lib-definitions.ts +11 -0
  155. package/src/operators/index.ts +2 -31
  156. package/src/{Relation → sync/Relation}/Memory.ts +9 -1
  157. package/src/sync/Relation/index.ts +1 -0
  158. package/src/{operators → sync/operators}/_helpers.ts +1 -1
  159. package/src/{operators → sync/operators}/allbut.ts +1 -1
  160. package/src/{operators → sync/operators}/autowrap.ts +1 -1
  161. package/src/{operators → sync/operators}/constants.ts +1 -1
  162. package/src/{operators → sync/operators}/cross_product.ts +1 -1
  163. package/src/{operators → sync/operators}/exclude.ts +2 -2
  164. package/src/{operators → sync/operators}/extend.ts +1 -1
  165. package/src/{operators → sync/operators}/group.ts +2 -2
  166. package/src/{operators → sync/operators}/image.ts +2 -2
  167. package/src/sync/operators/index.ts +31 -0
  168. package/src/{operators → sync/operators}/intersect.ts +1 -1
  169. package/src/{operators → sync/operators}/isEqual.ts +1 -2
  170. package/src/sync/operators/isRelation.ts +6 -0
  171. package/src/{operators → sync/operators}/join.ts +1 -1
  172. package/src/{operators → sync/operators}/left_join.ts +1 -1
  173. package/src/{operators → sync/operators}/matching.ts +1 -1
  174. package/src/{operators → sync/operators}/minus.ts +1 -1
  175. package/src/{operators → sync/operators}/not_matching.ts +1 -1
  176. package/src/{operators → sync/operators}/one.ts +1 -1
  177. package/src/{operators → sync/operators}/prefix.ts +1 -1
  178. package/src/{operators → sync/operators}/project.ts +1 -1
  179. package/src/{operators → sync/operators}/rename.ts +1 -1
  180. package/src/{operators → sync/operators}/restrict.ts +2 -2
  181. package/src/{operators → sync/operators}/suffix.ts +1 -1
  182. package/src/{operators → sync/operators}/summarize.ts +1 -1
  183. package/src/{operators → sync/operators}/transform.ts +1 -1
  184. package/src/{operators → sync/operators}/ungroup.ts +1 -1
  185. package/src/{operators → sync/operators}/union.ts +1 -1
  186. package/src/{operators → sync/operators}/unwrap.ts +1 -1
  187. package/src/sync/operators/where.ts +1 -0
  188. package/src/{operators → sync/operators}/wrap.ts +1 -1
  189. package/src/{operators → sync/operators}/yByX.ts +1 -1
  190. package/src/types.ts +11 -0
  191. package/src/writer/Text.ts +305 -0
  192. package/src/writer/index.ts +1 -0
  193. package/src/operators/isRelation.ts +0 -5
  194. /package/{src/operators/where.ts → dist/sync/operators/where.d.ts} +0 -0
@@ -0,0 +1,64 @@
1
+ import type { AsyncRelationOperand } from '../types';
2
+ import type { Tuple, AttrName } from '../../types';
3
+ import { toAsyncOperationalOperand, deduplicateAsync } from './_helpers';
4
+
5
+ /**
6
+ * Async generator that projects tuples to specified attributes.
7
+ */
8
+ async function* projectGen<T>(
9
+ source: AsyncIterable<T>,
10
+ attrs: AttrName[]
11
+ ): AsyncGenerator<Tuple> {
12
+ for await (const tuple of source) {
13
+ const t = tuple as Tuple;
14
+ const projected = attrs.reduce((memo, attr) => {
15
+ if (attr in t) {
16
+ memo[attr] = t[attr];
17
+ }
18
+ return memo;
19
+ }, {} as Tuple);
20
+ yield projected;
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Projects tuples to specified attributes with deduplication.
26
+ */
27
+ export const project = <T>(
28
+ operand: AsyncRelationOperand<T>,
29
+ attrs: AttrName[]
30
+ ): AsyncIterable<Tuple> => {
31
+ const op = toAsyncOperationalOperand(operand);
32
+ return deduplicateAsync(projectGen(op.tuples(), attrs));
33
+ };
34
+
35
+ /**
36
+ * Async generator that projects out (removes) specified attributes.
37
+ */
38
+ async function* allbutGen<T>(
39
+ source: AsyncIterable<T>,
40
+ attrs: AttrName[]
41
+ ): AsyncGenerator<Tuple> {
42
+ const excludeSet = new Set(attrs);
43
+ for await (const tuple of source) {
44
+ const t = tuple as Tuple;
45
+ const projected: Tuple = {};
46
+ for (const [attr, value] of Object.entries(t)) {
47
+ if (!excludeSet.has(attr)) {
48
+ projected[attr] = value;
49
+ }
50
+ }
51
+ yield projected;
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Projects out (removes) specified attributes with deduplication.
57
+ */
58
+ export const allbut = <T>(
59
+ operand: AsyncRelationOperand<T>,
60
+ attrs: AttrName[]
61
+ ): AsyncIterable<Tuple> => {
62
+ const op = toAsyncOperationalOperand(operand);
63
+ return deduplicateAsync(allbutGen(op.tuples(), attrs));
64
+ };
@@ -0,0 +1,33 @@
1
+ import type { AsyncRelationOperand } from '../types';
2
+ import type { Renaming, Tuple } from '../../types';
3
+ import { toAsyncOperationalOperand } from './_helpers';
4
+ import { toRenamingFunc } from '../../sync/operators/_helpers';
5
+
6
+ /**
7
+ * Async generator that renames attributes in each tuple.
8
+ */
9
+ async function* renameGen<T>(
10
+ source: AsyncIterable<T>,
11
+ renaming: Renaming
12
+ ): AsyncGenerator<Tuple> {
13
+ const renamingFunc = toRenamingFunc(renaming);
14
+ for await (const tuple of source) {
15
+ const t = tuple as Tuple;
16
+ const renamed = Object.keys(t).reduce((memo, attr) => {
17
+ memo[renamingFunc(attr)] = t[attr];
18
+ return memo;
19
+ }, {} as Tuple);
20
+ yield renamed;
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Renames attributes in each tuple.
26
+ */
27
+ export const rename = <T>(
28
+ operand: AsyncRelationOperand<T>,
29
+ renaming: Renaming
30
+ ): AsyncIterable<Tuple> => {
31
+ const op = toAsyncOperationalOperand(operand);
32
+ return renameGen(op.tuples(), renaming);
33
+ };
@@ -0,0 +1,61 @@
1
+ import type { AsyncRelationOperand } from '../types';
2
+ import type { Predicate } from '../../types';
3
+ import { toPredicateFunc } from '../../support/toPredicateFunc';
4
+ import { toAsyncOperationalOperand } from './_helpers';
5
+
6
+ /**
7
+ * Async generator that filters tuples based on a predicate.
8
+ */
9
+ async function* restrictGen<T>(
10
+ source: AsyncIterable<T>,
11
+ predicate: (t: T) => boolean
12
+ ): AsyncGenerator<T> {
13
+ for await (const tuple of source) {
14
+ if (predicate(tuple)) {
15
+ yield tuple;
16
+ }
17
+ }
18
+ }
19
+
20
+ /**
21
+ * Filters tuples that match the predicate.
22
+ */
23
+ export const restrict = <T>(
24
+ operand: AsyncRelationOperand<T>,
25
+ p: Predicate
26
+ ): AsyncIterable<T> => {
27
+ const op = toAsyncOperationalOperand(operand);
28
+ const f = toPredicateFunc(p) as (t: T) => boolean;
29
+ return restrictGen(op.tuples(), f);
30
+ };
31
+
32
+ /**
33
+ * Alias for restrict.
34
+ */
35
+ export const where = restrict;
36
+
37
+ /**
38
+ * Async generator that excludes tuples based on a predicate.
39
+ */
40
+ async function* excludeGen<T>(
41
+ source: AsyncIterable<T>,
42
+ predicate: (t: T) => boolean
43
+ ): AsyncGenerator<T> {
44
+ for await (const tuple of source) {
45
+ if (!predicate(tuple)) {
46
+ yield tuple;
47
+ }
48
+ }
49
+ }
50
+
51
+ /**
52
+ * Filters tuples that do NOT match the predicate.
53
+ */
54
+ export const exclude = <T>(
55
+ operand: AsyncRelationOperand<T>,
56
+ p: Predicate
57
+ ): AsyncIterable<T> => {
58
+ const op = toAsyncOperationalOperand(operand);
59
+ const f = toPredicateFunc(p) as (t: T) => boolean;
60
+ return excludeGen(op.tuples(), f);
61
+ };
@@ -0,0 +1,15 @@
1
+ import type { AsyncRelationOperand } from '../types';
2
+ import type { SuffixOptions, Tuple } from '../../types';
3
+ import { rename } from './rename';
4
+
5
+ /**
6
+ * Suffixes all attribute names (except those in options.except).
7
+ */
8
+ export const suffix = <T>(
9
+ operand: AsyncRelationOperand<T>,
10
+ sfx: string,
11
+ options?: SuffixOptions
12
+ ): AsyncIterable<Tuple> => {
13
+ const except = new Set(options?.except ?? []);
14
+ return rename(operand, (attr) => except.has(attr) ? attr : `${attr}${sfx}`);
15
+ };
@@ -0,0 +1,90 @@
1
+ import type { AsyncRelationOperand } from '../types';
2
+ import type { AttrName, Tuple, Aggregator, Aggregators } from '../../types';
3
+
4
+ const groupKey = (tuple: Tuple, by: AttrName[]): string => {
5
+ const keyParts = by.map(attr => JSON.stringify(tuple[attr]));
6
+ return keyParts.join('|');
7
+ };
8
+
9
+ const pickAttrs = (tuple: Tuple, attrs: AttrName[]): Tuple => {
10
+ return attrs.reduce((acc, attr) => {
11
+ acc[attr] = tuple[attr];
12
+ return acc;
13
+ }, {} as Tuple);
14
+ };
15
+
16
+ const applyAggregator = (tuples: Tuple[], agg: Aggregator): unknown => {
17
+ if (typeof agg === 'function') {
18
+ return agg(tuples);
19
+ }
20
+
21
+ const spec = typeof agg === 'string' ? { op: agg, attr: '' } : agg;
22
+ const { op, attr } = spec;
23
+
24
+ switch (op) {
25
+ case 'count':
26
+ return tuples.length;
27
+
28
+ case 'sum': {
29
+ return tuples.reduce((sum, t) => sum + (Number(t[attr]) || 0), 0);
30
+ }
31
+
32
+ case 'min': {
33
+ const values = tuples.map(t => t[attr]).filter(v => v !== undefined && v !== null);
34
+ return values.length > 0 ? Math.min(...values.map(Number)) : null;
35
+ }
36
+
37
+ case 'max': {
38
+ const values = tuples.map(t => t[attr]).filter(v => v !== undefined && v !== null);
39
+ return values.length > 0 ? Math.max(...values.map(Number)) : null;
40
+ }
41
+
42
+ case 'avg': {
43
+ const values = tuples.map(t => Number(t[attr])).filter(v => !isNaN(v));
44
+ return values.length > 0 ? values.reduce((a, b) => a + b, 0) / values.length : null;
45
+ }
46
+
47
+ case 'collect': {
48
+ return tuples.map(t => t[attr]);
49
+ }
50
+
51
+ default:
52
+ throw new Error(`Unknown aggregator: ${op}`);
53
+ }
54
+ };
55
+
56
+ /**
57
+ * Aggregates tuples by grouping attributes.
58
+ * Supports count, sum, min, max, avg, collect, and custom functions.
59
+ * Materializes all tuples to perform grouping.
60
+ */
61
+ export async function* summarize<T>(
62
+ operand: AsyncRelationOperand<T>,
63
+ by: AttrName[],
64
+ aggs: Aggregators
65
+ ): AsyncIterable<Tuple> {
66
+ // Materialize all tuples
67
+ const tuples: Tuple[] = [];
68
+ for await (const tuple of operand) {
69
+ tuples.push(tuple as Tuple);
70
+ }
71
+
72
+ // Group tuples
73
+ const groups = new Map<string, Tuple[]>();
74
+ for (const tuple of tuples) {
75
+ const key = groupKey(tuple, by);
76
+ if (!groups.has(key)) {
77
+ groups.set(key, []);
78
+ }
79
+ groups.get(key)!.push(tuple);
80
+ }
81
+
82
+ // Apply aggregators to each group
83
+ for (const groupTuples of groups.values()) {
84
+ const row: Tuple = pickAttrs(groupTuples[0], by);
85
+ for (const [resultAttr, agg] of Object.entries(aggs)) {
86
+ row[resultAttr] = applyAggregator(groupTuples, agg);
87
+ }
88
+ yield row;
89
+ }
90
+ }
@@ -0,0 +1,18 @@
1
+ import type { AsyncRelationOperand } from '../types';
2
+ import { toAsyncOperationalOperand } from './_helpers';
3
+
4
+ /**
5
+ * Collects all tuples from the async relation into an array.
6
+ */
7
+ export const toArray = async <T>(
8
+ operand: AsyncRelationOperand<T>
9
+ ): Promise<T[]> => {
10
+ const op = toAsyncOperationalOperand(operand);
11
+ const result: T[] = [];
12
+
13
+ for await (const tuple of op.tuples()) {
14
+ result.push(tuple);
15
+ }
16
+
17
+ return result;
18
+ };
@@ -0,0 +1,43 @@
1
+ import type { AsyncRelationOperand } from '../types';
2
+ import type { Transformation, Tuple } from '../../types';
3
+
4
+ /**
5
+ * Applies transformation functions to attribute values.
6
+ * - Single function: applies to all values
7
+ * - Array of functions: chains them in order
8
+ * - Object with attr keys: applies per-attribute transformers
9
+ */
10
+ export async function* transform<T>(
11
+ operand: AsyncRelationOperand<T>,
12
+ transformation: Transformation
13
+ ): AsyncIterable<Tuple> {
14
+ for await (const tuple of operand) {
15
+ const transformed: Tuple = {};
16
+
17
+ for (const [attr, value] of Object.entries(tuple as Tuple)) {
18
+ transformed[attr] = applyTransformation(value, attr, transformation);
19
+ }
20
+
21
+ yield transformed;
22
+ }
23
+ }
24
+
25
+ const applyTransformation = (value: unknown, attr: string, transformation: Transformation): unknown => {
26
+ if (typeof transformation === 'function') {
27
+ // Single function - apply to all values
28
+ return transformation(value);
29
+ } else if (Array.isArray(transformation)) {
30
+ // Array of functions - chain them
31
+ return transformation.reduce((v, fn) => fn(v), value);
32
+ } else {
33
+ // Object with attr-specific transformers
34
+ const fn = transformation[attr];
35
+ if (fn) {
36
+ if (Array.isArray(fn)) {
37
+ return fn.reduce((v, f) => f(v), value);
38
+ }
39
+ return fn(value);
40
+ }
41
+ return value;
42
+ }
43
+ };
@@ -0,0 +1,43 @@
1
+ import type { AsyncRelationOperand } from '../types';
2
+ import type { AttrName, Tuple, Relation } from '../../types';
3
+ import { isRelation } from '../../sync/operators/isRelation';
4
+
5
+ const toTupleArray = (value: unknown): Tuple[] => {
6
+ if (isRelation(value)) {
7
+ return (value as Relation).toArray();
8
+ }
9
+ if (Array.isArray(value)) {
10
+ return value;
11
+ }
12
+ throw new Error(`Value is not a relation or array`);
13
+ };
14
+
15
+ /**
16
+ * Flattens a nested relation attribute back into parent tuples.
17
+ * Materializes all tuples to perform ungrouping.
18
+ */
19
+ export async function* ungroup<T>(
20
+ operand: AsyncRelationOperand<T>,
21
+ attr: AttrName
22
+ ): AsyncIterable<Tuple> {
23
+ for await (const tuple of operand) {
24
+ const t = tuple as Tuple;
25
+ const nested = toTupleArray(t[attr]);
26
+
27
+ // Get base attributes (all except the grouped one)
28
+ const base: Tuple = {};
29
+ for (const [key, value] of Object.entries(t)) {
30
+ if (key !== attr) {
31
+ base[key] = value;
32
+ }
33
+ }
34
+
35
+ // Flatten each nested tuple
36
+ for (const nestedTuple of nested) {
37
+ yield {
38
+ ...base,
39
+ ...nestedTuple
40
+ };
41
+ }
42
+ }
43
+ }
@@ -0,0 +1,29 @@
1
+ import type { AsyncRelationOperand } from '../types';
2
+ import type { Tuple } from '../../types';
3
+ import { tupleKey } from '../../sync/operators/_helpers';
4
+
5
+ /**
6
+ * Combines tuples from two relations, removing duplicates.
7
+ */
8
+ export async function* union<T>(
9
+ left: AsyncRelationOperand<T>,
10
+ right: AsyncRelationOperand<T>
11
+ ): AsyncIterable<Tuple> {
12
+ const seen = new Set<string>();
13
+
14
+ for await (const tuple of left) {
15
+ const key = tupleKey(tuple as Tuple);
16
+ if (!seen.has(key)) {
17
+ seen.add(key);
18
+ yield tuple as Tuple;
19
+ }
20
+ }
21
+
22
+ for await (const tuple of right) {
23
+ const key = tupleKey(tuple as Tuple);
24
+ if (!seen.has(key)) {
25
+ seen.add(key);
26
+ yield tuple as Tuple;
27
+ }
28
+ }
29
+ }
@@ -0,0 +1,31 @@
1
+ import type { AsyncRelationOperand } from '../types';
2
+ import type { AttrName, Tuple } from '../../types';
3
+
4
+ /**
5
+ * Unwraps a nested object attribute back into the parent tuple.
6
+ */
7
+ export async function* unwrap<T>(
8
+ operand: AsyncRelationOperand<T>,
9
+ attr: AttrName
10
+ ): AsyncIterable<Tuple> {
11
+ for await (const tuple of operand) {
12
+ const t = tuple as Tuple;
13
+ const wrapped = t[attr] as Tuple;
14
+
15
+ if (typeof wrapped !== 'object' || wrapped === null || Array.isArray(wrapped)) {
16
+ throw new Error(`Attribute '${attr}' is not a tuple (object)`);
17
+ }
18
+
19
+ const unwrapped: Tuple = {};
20
+ for (const [key, value] of Object.entries(t)) {
21
+ if (key !== attr) {
22
+ unwrapped[key] = value;
23
+ }
24
+ }
25
+
26
+ yield {
27
+ ...unwrapped,
28
+ ...wrapped
29
+ };
30
+ }
31
+ }
@@ -0,0 +1,32 @@
1
+ import type { AsyncRelationOperand } from '../types';
2
+ import type { AttrName, Tuple } from '../../types';
3
+
4
+ /**
5
+ * Wraps specified attributes into a nested object.
6
+ */
7
+ export async function* wrap<T>(
8
+ operand: AsyncRelationOperand<T>,
9
+ attrs: AttrName[],
10
+ as: AttrName
11
+ ): AsyncIterable<Tuple> {
12
+ const wrappedSet = new Set(attrs);
13
+
14
+ for await (const tuple of operand) {
15
+ const t = tuple as Tuple;
16
+ const wrapped: Tuple = {};
17
+ const remaining: Tuple = {};
18
+
19
+ for (const [key, value] of Object.entries(t)) {
20
+ if (wrappedSet.has(key)) {
21
+ wrapped[key] = value;
22
+ } else {
23
+ remaining[key] = value;
24
+ }
25
+ }
26
+
27
+ yield {
28
+ ...remaining,
29
+ [as]: wrapped
30
+ };
31
+ }
32
+ }
@@ -0,0 +1,19 @@
1
+ import type { AsyncRelationOperand } from '../types';
2
+ import type { AttrName, Tuple } from '../../types';
3
+
4
+ /**
5
+ * Builds a key-value map from two attributes.
6
+ * Terminal operation - returns a Promise.
7
+ */
8
+ export const yByX = async <T>(
9
+ operand: AsyncRelationOperand<T>,
10
+ y: AttrName,
11
+ x: AttrName
12
+ ): Promise<Tuple> => {
13
+ const hash: Tuple = {};
14
+ for await (const tuple of operand) {
15
+ const t = tuple as Tuple;
16
+ hash[`${t[x]}`] = t[y];
17
+ }
18
+ return hash;
19
+ };
@@ -0,0 +1,86 @@
1
+ import type { Tuple, TypedPredicate, Relation, TypedExtension, RenameMap, Renamed, Prefixed, Suffixed, Transformation, JoinKeys, AttrName, Aggregators, AutowrapOptions, TextOptions } from '../types';
2
+
3
+ /**
4
+ * Async version of the Relation interface.
5
+ * Operations are lazy - they build a pipeline that executes only when
6
+ * a terminal method (one(), toArray()) is awaited.
7
+ *
8
+ * @typeParam T - The tuple type for this relation.
9
+ */
10
+ export interface AsyncRelation<T = Tuple> {
11
+ // === Type-preserving operators ===
12
+
13
+ restrict(p: TypedPredicate<T>): AsyncRelation<T>
14
+ where(p: TypedPredicate<T>): AsyncRelation<T>
15
+ exclude(p: TypedPredicate<T>): AsyncRelation<T>
16
+
17
+ // === Projection operators ===
18
+
19
+ project<K extends keyof T>(attrs: K[]): AsyncRelation<Pick<T, K>>
20
+ allbut<K extends keyof T>(attrs: K[]): AsyncRelation<Omit<T, K>>
21
+
22
+ // === Extension operators ===
23
+
24
+ extend<E extends Record<string, unknown>>(e: TypedExtension<T, E>): AsyncRelation<T & E>
25
+ constants<C extends Tuple>(consts: C): AsyncRelation<T & C>
26
+
27
+ // === Rename operators ===
28
+
29
+ rename<R extends RenameMap<T>>(r: R): AsyncRelation<Renamed<T, R>>
30
+ prefix<P extends string, Ex extends keyof T = never>(pfx: P, options?: { except?: Ex[] }): AsyncRelation<Prefixed<T, P, Ex>>
31
+ suffix<S extends string, Ex extends keyof T = never>(sfx: S, options?: { except?: Ex[] }): AsyncRelation<Suffixed<T, S, Ex>>
32
+ transform(t: Transformation): AsyncRelation<T>
33
+
34
+ // === Set operations ===
35
+
36
+ union(other: AsyncRelationOperand<T>): AsyncRelation<T>
37
+ minus(other: AsyncRelationOperand<T>): AsyncRelation<T>
38
+ intersect(other: AsyncRelationOperand<T>): AsyncRelation<T>
39
+
40
+ // === Semi-joins ===
41
+
42
+ matching<U = Tuple>(other: AsyncRelationOperand<U>, keys?: JoinKeys): AsyncRelation<T>
43
+ not_matching<U = Tuple>(other: AsyncRelationOperand<U>, keys?: JoinKeys): AsyncRelation<T>
44
+
45
+ // === Joins ===
46
+
47
+ join<U = Tuple>(other: AsyncRelationOperand<U>, keys?: JoinKeys): AsyncRelation<T & U>
48
+ left_join<U = Tuple>(other: AsyncRelationOperand<U>, keys?: JoinKeys): AsyncRelation<T & Partial<U>>
49
+ cross_product<U = Tuple>(other: AsyncRelationOperand<U>): AsyncRelation<T & U>
50
+ cross_join<U = Tuple>(other: AsyncRelationOperand<U>): AsyncRelation<T & U>
51
+
52
+ // === Nesting/Grouping ===
53
+
54
+ group<K extends keyof T>(attrs: K[], as: AttrName): AsyncRelation<Omit<T, K> & Record<string, Relation<Pick<T, K>>>>
55
+ ungroup(attr: AttrName): AsyncRelation<Tuple>
56
+ wrap<K extends keyof T>(attrs: K[], as: AttrName): AsyncRelation<Omit<T, K> & Record<string, Pick<T, K>>>
57
+ unwrap(attr: AttrName): AsyncRelation<Tuple>
58
+ image<U = Tuple>(other: AsyncRelationOperand<U>, as: AttrName, keys?: JoinKeys): AsyncRelation<T & Record<string, Relation<U>>>
59
+ summarize<K extends keyof T>(by: K[], aggs: Aggregators): AsyncRelation<Pick<T, K> & Tuple>
60
+ autowrap(options?: AutowrapOptions): AsyncRelation<Tuple>
61
+
62
+ // === Terminal operations (return Promises) ===
63
+
64
+ one(): Promise<T>
65
+ toArray(): Promise<T[]>
66
+ toRelation(): Promise<Relation<T>>
67
+ yByX(y: AttrName, x: AttrName): Promise<Tuple>
68
+ toText(options?: TextOptions): Promise<string>
69
+
70
+ // === Async iteration ===
71
+
72
+ [Symbol.asyncIterator](): AsyncIterator<T>
73
+ }
74
+
75
+ /**
76
+ * What can be passed as an operand to async operators.
77
+ */
78
+ export type AsyncRelationOperand<T = Tuple> = AsyncRelation<T> | AsyncIterable<T>
79
+
80
+ /**
81
+ * Async version of OperationalOperand for internal use by operators.
82
+ */
83
+ export interface AsyncOperationalOperand<T = Tuple> {
84
+ tuples(): AsyncIterable<T>
85
+ output(tuples: AsyncIterable<T>): AsyncRelationOperand<T>
86
+ }
package/src/async.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './async/types';
2
+ export * as asyncOps from './async/operators';
3
+ export { AsyncBmg, BaseAsyncRelation } from './async/Relation';
4
+ export { toText, TextWriter } from './writer';
package/src/index.ts CHANGED
@@ -1,9 +1,10 @@
1
- export * from './operators';
1
+ export * from './sync/operators';
2
2
  export * from './types';
3
3
  export { LIB_DEFINITIONS } from './lib-definitions';
4
+ export { toText } from './writer';
4
5
 
5
- import { MemoryRelation } from './Relation';
6
- import { isRelation } from './operators';
6
+ import { MemoryRelation } from './sync/Relation';
7
+ import { isRelation } from './sync/operators';
7
8
 
8
9
  /**
9
10
  * Creates a new in-memory relation from an array of tuples.
@@ -29,3 +30,15 @@ export function Bmg<T>(tuples: T[]): MemoryRelation<T> {
29
30
  }
30
31
 
31
32
  Bmg.isRelation = isRelation;
33
+
34
+ /**
35
+ * DEE - The relation with no attributes and exactly one tuple.
36
+ * This is the identity element for natural join.
37
+ */
38
+ export const DEE = new MemoryRelation<Record<string, never>>([{}]);
39
+
40
+ /**
41
+ * DUM - The relation with no attributes and no tuples.
42
+ * This is the identity element for union.
43
+ */
44
+ export const DUM = new MemoryRelation<Record<string, never>>([]);
@@ -152,6 +152,13 @@ interface PrefixOptions {
152
152
  except?: AttrName[]
153
153
  }
154
154
 
155
+ interface TextOptions {
156
+ /** Precision for floating point numbers (default: 3) */
157
+ floatPrecision?: number;
158
+ /** Maximum width to trim output at */
159
+ trimAt?: number;
160
+ }
161
+
155
162
  interface SuffixOptions {
156
163
  except?: AttrName[]
157
164
  }
@@ -256,6 +263,10 @@ interface Relation<T = Tuple> {
256
263
  yByX<Y extends keyof T, X extends keyof T>(y: Y, x: X): Record<T[X] & PropertyKey, T[Y]>
257
264
  toArray(): T[]
258
265
  isEqual(right: any): boolean
266
+
267
+ // === Display ===
268
+
269
+ toText(options?: TextOptions): string
259
270
  }
260
271
 
261
272
  // ============================================================================
@@ -1,31 +1,2 @@
1
- export * from './restrict'
2
- export * from './where'
3
- export * from './exclude'
4
- export * from './constants'
5
- export * from './rename'
6
- export * from './prefix'
7
- export * from './suffix'
8
- export * from './project'
9
- export * from './allbut'
10
- export * from './extend'
11
- export * from './union'
12
- export * from './minus'
13
- export * from './intersect'
14
- export * from './matching'
15
- export * from './not_matching'
16
- export * from './join'
17
- export * from './left_join'
18
- export * from './cross_product'
19
- export * from './image'
20
- export * from './summarize'
21
- export * from './group'
22
- export * from './ungroup'
23
- export * from './wrap'
24
- export * from './unwrap'
25
- export * from './autowrap'
26
- export * from './transform'
27
-
28
- export * from './isRelation'
29
- export * from './isEqual'
30
- export * from './one'
31
- export * from './yByX'
1
+ // Re-export sync operators for backward compatibility
2
+ export * from '../sync/operators';