@enspirit/bmg-js 1.0.0 → 1.0.2

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 (103) hide show
  1. package/LICENSE.md +21 -0
  2. package/dist/Relation/Memory.d.ts +45 -0
  3. package/dist/Relation/index.d.ts +1 -0
  4. package/dist/bmg.cjs +2 -0
  5. package/dist/bmg.cjs.map +1 -0
  6. package/dist/bmg.modern.js +2 -0
  7. package/dist/bmg.modern.js.map +1 -0
  8. package/dist/bmg.module.js +2 -0
  9. package/dist/bmg.module.js.map +1 -0
  10. package/dist/bmg.umd.js +2 -0
  11. package/dist/bmg.umd.js.map +1 -0
  12. package/dist/index.d.ts +27 -0
  13. package/dist/lib-definitions.d.ts +1 -0
  14. package/dist/operators/_helpers.d.ts +142 -0
  15. package/dist/operators/allbut.d.ts +2 -0
  16. package/dist/operators/autowrap.d.ts +2 -0
  17. package/dist/operators/constants.d.ts +2 -0
  18. package/dist/operators/cross_product.d.ts +3 -0
  19. package/dist/operators/exclude.d.ts +2 -0
  20. package/dist/operators/extend.d.ts +2 -0
  21. package/dist/operators/group.d.ts +2 -0
  22. package/dist/operators/image.d.ts +2 -0
  23. package/dist/operators/index.d.ts +30 -0
  24. package/dist/operators/intersect.d.ts +2 -0
  25. package/dist/operators/isEqual.d.ts +2 -0
  26. package/dist/operators/isRelation.d.ts +1 -0
  27. package/dist/operators/join.d.ts +2 -0
  28. package/dist/operators/left_join.d.ts +2 -0
  29. package/dist/operators/matching.d.ts +2 -0
  30. package/dist/operators/minus.d.ts +2 -0
  31. package/dist/operators/not_matching.d.ts +2 -0
  32. package/dist/operators/one.d.ts +2 -0
  33. package/dist/operators/prefix.d.ts +2 -0
  34. package/dist/operators/project.d.ts +2 -0
  35. package/dist/operators/rename.d.ts +2 -0
  36. package/dist/operators/restrict.d.ts +2 -0
  37. package/dist/operators/suffix.d.ts +2 -0
  38. package/dist/operators/summarize.d.ts +2 -0
  39. package/dist/operators/transform.d.ts +2 -0
  40. package/dist/operators/ungroup.d.ts +2 -0
  41. package/dist/operators/union.d.ts +2 -0
  42. package/dist/operators/unwrap.d.ts +2 -0
  43. package/dist/operators/where.d.ts +1 -0
  44. package/dist/operators/wrap.d.ts +2 -0
  45. package/dist/operators/yByX.d.ts +2 -0
  46. package/dist/support/toPredicateFunc.d.ts +2 -0
  47. package/dist/types.d.ts +162 -0
  48. package/package.json +20 -6
  49. package/src/Relation/Memory.ts +13 -12
  50. package/src/index.ts +1 -1
  51. package/src/lib-definitions.ts +281 -0
  52. package/src/types.ts +142 -54
  53. package/.claude/safe-setup/.env.example +0 -3
  54. package/.claude/safe-setup/Dockerfile.claude +0 -36
  55. package/.claude/safe-setup/HACKING.md +0 -63
  56. package/.claude/safe-setup/Makefile +0 -22
  57. package/.claude/safe-setup/docker-compose.yml +0 -18
  58. package/.claude/safe-setup/entrypoint.sh +0 -13
  59. package/.claude/settings.local.json +0 -9
  60. package/.claude/typescript-annotations.md +0 -273
  61. package/.github/workflows/test.yml +0 -26
  62. package/CLAUDE.md +0 -48
  63. package/Makefile +0 -2
  64. package/example/README.md +0 -22
  65. package/example/index.ts +0 -316
  66. package/example/package.json +0 -16
  67. package/example/tsconfig.json +0 -11
  68. package/src/utility-types.ts +0 -77
  69. package/tests/bmg.test.ts +0 -16
  70. package/tests/fixtures.ts +0 -9
  71. package/tests/operators/allbut.test.ts +0 -51
  72. package/tests/operators/autowrap.test.ts +0 -82
  73. package/tests/operators/constants.test.ts +0 -37
  74. package/tests/operators/cross_product.test.ts +0 -90
  75. package/tests/operators/exclude.test.ts +0 -43
  76. package/tests/operators/extend.test.ts +0 -45
  77. package/tests/operators/group.test.ts +0 -69
  78. package/tests/operators/image.test.ts +0 -152
  79. package/tests/operators/intersect.test.ts +0 -53
  80. package/tests/operators/isEqual.test.ts +0 -111
  81. package/tests/operators/join.test.ts +0 -116
  82. package/tests/operators/left_join.test.ts +0 -116
  83. package/tests/operators/matching.test.ts +0 -91
  84. package/tests/operators/minus.test.ts +0 -47
  85. package/tests/operators/not_matching.test.ts +0 -104
  86. package/tests/operators/one.test.ts +0 -19
  87. package/tests/operators/prefix.test.ts +0 -37
  88. package/tests/operators/project.test.ts +0 -48
  89. package/tests/operators/rename.test.ts +0 -39
  90. package/tests/operators/restrict.test.ts +0 -27
  91. package/tests/operators/suffix.test.ts +0 -37
  92. package/tests/operators/summarize.test.ts +0 -109
  93. package/tests/operators/transform.test.ts +0 -94
  94. package/tests/operators/ungroup.test.ts +0 -67
  95. package/tests/operators/union.test.ts +0 -51
  96. package/tests/operators/unwrap.test.ts +0 -50
  97. package/tests/operators/where.test.ts +0 -33
  98. package/tests/operators/wrap.test.ts +0 -54
  99. package/tests/operators/yByX.test.ts +0 -32
  100. package/tests/types/relation.test.ts +0 -296
  101. package/tsconfig.json +0 -37
  102. package/tsconfig.node.json +0 -9
  103. package/vitest.config.ts +0 -15
@@ -0,0 +1,142 @@
1
+ import { OperationalOperand, RelationOperand, Renaming, RenamingFunc, Tuple, JoinKeys, AttrName } from "@/types";
2
+ /**
3
+ * Generates a unique string key for a tuple, used for equality comparison and deduplication.
4
+ * Handles nested relations by converting them to sorted tuple keys.
5
+ *
6
+ * @example
7
+ * tupleKey({ name: 'Alice', age: 30 })
8
+ * // => '[["age",30],["name","Alice"]]'
9
+ *
10
+ * @example
11
+ * tupleKey({ id: 1, items: Bmg([{ x: 1 }, { x: 2 }]) })
12
+ * // => '[["id",1],["items",[...]]]' (nested relation converted to sorted keys)
13
+ */
14
+ export declare const tupleKey: (tuple: Tuple) => string;
15
+ /**
16
+ * Removes duplicate tuples from an array, preserving order of first occurrence.
17
+ * Uses tupleKey() for equality comparison.
18
+ *
19
+ * @example
20
+ * deduplicate([
21
+ * { id: 1, name: 'Alice' },
22
+ * { id: 2, name: 'Bob' },
23
+ * { id: 1, name: 'Alice' }, // duplicate
24
+ * ])
25
+ * // => [{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]
26
+ */
27
+ export declare const deduplicate: (tuples: Tuple[]) => Tuple[];
28
+ /**
29
+ * Converts a RelationOperand (Relation or Tuple[]) to an OperationalOperand
30
+ * that provides a uniform interface for iteration and output.
31
+ *
32
+ * @example
33
+ * // With array input, output remains array
34
+ * const op = toOperationalOperand([{ id: 1 }]);
35
+ * [...op.tuples()]; // => [{ id: 1 }]
36
+ * op.output([{ id: 2 }]); // => [{ id: 2 }]
37
+ *
38
+ * @example
39
+ * // With Relation input, output is a new Relation
40
+ * const op = toOperationalOperand(Bmg([{ id: 1 }]));
41
+ * [...op.tuples()]; // => [{ id: 1 }]
42
+ * op.output([{ id: 2 }]); // => Bmg([{ id: 2 }])
43
+ */
44
+ export declare const toOperationalOperand: (operand: RelationOperand) => OperationalOperand;
45
+ /**
46
+ * Converts a Renaming (object or function) to a RenamingFunc.
47
+ *
48
+ * @example
49
+ * // Object renaming
50
+ * const fn = toRenamingFunc({ name: 'fullName', age: 'years' });
51
+ * fn('name'); // => 'fullName'
52
+ * fn('age'); // => 'years'
53
+ * fn('other'); // => 'other' (unchanged)
54
+ *
55
+ * @example
56
+ * // Function renaming (passed through)
57
+ * const fn = toRenamingFunc(attr => attr.toUpperCase());
58
+ * fn('name'); // => 'NAME'
59
+ */
60
+ export declare const toRenamingFunc: (renaming: Renaming) => RenamingFunc;
61
+ export declare const error: (msg: string) => never;
62
+ /**
63
+ * Finds attribute names that exist in both left and right tuple arrays.
64
+ * Used for natural joins when no explicit keys are provided.
65
+ *
66
+ * @example
67
+ * const left = [{ id: 1, name: 'Alice', city: 'NYC' }];
68
+ * const right = [{ city: 'NYC', country: 'USA' }];
69
+ * getCommonAttrs(left, right);
70
+ * // => ['city']
71
+ *
72
+ * @example
73
+ * const left = [{ a: 1, b: 2 }];
74
+ * const right = [{ b: 2, c: 3 }];
75
+ * getCommonAttrs(left, right);
76
+ * // => ['b']
77
+ */
78
+ export declare const getCommonAttrs: (left: Tuple[], right: Tuple[]) => AttrName[];
79
+ /**
80
+ * Normalizes JoinKeys to a Record<AttrName, AttrName> mapping left attrs to right attrs.
81
+ *
82
+ * @example
83
+ * // undefined => use common attributes
84
+ * normalizeKeys(undefined, [{ id: 1, city: 'NYC' }], [{ city: 'NYC' }]);
85
+ * // => { city: 'city' }
86
+ *
87
+ * @example
88
+ * // Array of common attribute names
89
+ * normalizeKeys(['city', 'country'], leftTuples, rightTuples);
90
+ * // => { city: 'city', country: 'country' }
91
+ *
92
+ * @example
93
+ * // Object mapping left attr to right attr
94
+ * normalizeKeys({ city: 'location' }, leftTuples, rightTuples);
95
+ * // => { city: 'location' }
96
+ */
97
+ export declare const normalizeKeys: (keys: JoinKeys | undefined, leftTuples: Tuple[], rightTuples: Tuple[]) => Record<AttrName, AttrName>;
98
+ /**
99
+ * Checks if two tuples match on the specified key mapping.
100
+ *
101
+ * @example
102
+ * const keyMap = { city: 'location' };
103
+ * tuplesMatch({ id: 1, city: 'NYC' }, { location: 'NYC', pop: 8 }, keyMap);
104
+ * // => true (left.city === right.location)
105
+ *
106
+ * @example
107
+ * const keyMap = { city: 'city' };
108
+ * tuplesMatch({ city: 'NYC' }, { city: 'LA' }, keyMap);
109
+ * // => false
110
+ */
111
+ export declare const tuplesMatch: (left: Tuple, right: Tuple, keyMap: Record<AttrName, AttrName>) => boolean;
112
+ /**
113
+ * Creates a string key from a tuple's join attributes for fast Set-based lookups.
114
+ * Used by matching/not_matching for efficient semi-join operations.
115
+ *
116
+ * @example
117
+ * const keyMap = { first: 'fname', last: 'lname' };
118
+ *
119
+ * // Left side uses left attr names (keys of keyMap)
120
+ * matchKey({ id: 1, first: 'John', last: 'Doe' }, keyMap, 'left');
121
+ * // => '"John"|"Doe"'
122
+ *
123
+ * // Right side uses right attr names (values of keyMap)
124
+ * matchKey({ fname: 'John', lname: 'Doe', age: 30 }, keyMap, 'right');
125
+ * // => '"John"|"Doe"'
126
+ */
127
+ export declare const matchKey: (tuple: Tuple, keyMap: Record<AttrName, AttrName>, side: 'left' | 'right') => string;
128
+ /**
129
+ * Removes join key attributes from a right tuple when merging.
130
+ * Used to avoid duplicate columns in join results.
131
+ *
132
+ * @example
133
+ * const keyMap = { city: 'location' };
134
+ * projectOutKeys({ location: 'NYC', country: 'USA', pop: 8 }, keyMap);
135
+ * // => { country: 'USA', pop: 8 } (location removed)
136
+ *
137
+ * @example
138
+ * const keyMap = { a: 'a', b: 'b' };
139
+ * projectOutKeys({ a: 1, b: 2, c: 3 }, keyMap);
140
+ * // => { c: 3 } (a and b removed)
141
+ */
142
+ export declare const projectOutKeys: (tuple: Tuple, keyMap: Record<AttrName, AttrName>) => Tuple;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, AttrName } from "../types";
2
+ export declare const allbut: (operand: RelationOperand, attrs: AttrName[]) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { AutowrapOptions, RelationOperand } from "../types";
2
+ export declare const autowrap: (operand: RelationOperand, options?: AutowrapOptions) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, Tuple } from "../types";
2
+ export declare const constants: (operand: RelationOperand, consts: Tuple) => RelationOperand;
@@ -0,0 +1,3 @@
1
+ import { RelationOperand } from "../types";
2
+ export declare const cross_product: (left: RelationOperand, right: RelationOperand) => RelationOperand;
3
+ export { cross_product as cross_join };
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, Predicate } from "../types";
2
+ export declare const exclude: (operand: RelationOperand, p: Predicate) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, Extension } from "../types";
2
+ export declare const extend: (operand: RelationOperand, extension: Extension) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, AttrName } from "../types";
2
+ export declare const group: (operand: RelationOperand, attrs: AttrName[], as: AttrName) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, JoinKeys, AttrName } from "../types";
2
+ export declare const image: (left: RelationOperand, right: RelationOperand, as: AttrName, keys?: JoinKeys) => RelationOperand;
@@ -0,0 +1,30 @@
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
+ export * from './isRelation';
28
+ export * from './isEqual';
29
+ export * from './one';
30
+ export * from './yByX';
@@ -0,0 +1,2 @@
1
+ import { RelationOperand } from "../types";
2
+ export declare const intersect: (left: RelationOperand, right: RelationOperand) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand } from "../types";
2
+ export declare const isEqual: (left: RelationOperand, right: RelationOperand) => boolean;
@@ -0,0 +1 @@
1
+ export declare const isRelation: (op: any) => boolean;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, JoinKeys } from "../types";
2
+ export declare const join: (left: RelationOperand, right: RelationOperand, keys?: JoinKeys) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, JoinKeys } from "../types";
2
+ export declare const left_join: (left: RelationOperand, right: RelationOperand, keys?: JoinKeys) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, JoinKeys } from "../types";
2
+ export declare const matching: (left: RelationOperand, right: RelationOperand, keys?: JoinKeys) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand } from "../types";
2
+ export declare const minus: (left: RelationOperand, right: RelationOperand) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, JoinKeys } from "../types";
2
+ export declare const not_matching: (left: RelationOperand, right: RelationOperand, keys?: JoinKeys) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, Tuple } from "../types";
2
+ export declare const one: (operand: RelationOperand) => Tuple;
@@ -0,0 +1,2 @@
1
+ import { PrefixOptions, RelationOperand } from "../types";
2
+ export declare const prefix: (operand: RelationOperand, pfx: string, options?: PrefixOptions) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, AttrName } from "../types";
2
+ export declare const project: (operand: RelationOperand, attrs: AttrName[]) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, Renaming } from "../types";
2
+ export declare const rename: (operand: RelationOperand, renaming: Renaming) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, Predicate } from "../types";
2
+ export declare const restrict: (operand: RelationOperand, p: Predicate) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, SuffixOptions } from "../types";
2
+ export declare const suffix: (operand: RelationOperand, sfx: string, options?: SuffixOptions) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, AttrName, Aggregators } from "../types";
2
+ export declare const summarize: (operand: RelationOperand, by: AttrName[], aggs: Aggregators) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, Transformation } from "../types";
2
+ export declare const transform: (operand: RelationOperand, transformation: Transformation) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, AttrName } from "../types";
2
+ export declare const ungroup: (operand: RelationOperand, attr: AttrName) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand } from "../types";
2
+ export declare const union: (left: RelationOperand, right: RelationOperand) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, AttrName } from "../types";
2
+ export declare const unwrap: (operand: RelationOperand, attr: AttrName) => RelationOperand;
@@ -0,0 +1 @@
1
+ export { restrict as where } from './restrict';
@@ -0,0 +1,2 @@
1
+ import { RelationOperand, AttrName } from "../types";
2
+ export declare const wrap: (operand: RelationOperand, attrs: AttrName[], as: AttrName) => RelationOperand;
@@ -0,0 +1,2 @@
1
+ import { AttrName, RelationOperand, Tuple } from "../types";
2
+ export declare const yByX: (operand: RelationOperand, y: AttrName, x: AttrName) => Tuple;
@@ -0,0 +1,2 @@
1
+ import { Predicate, PredicateFunc } from '../types';
2
+ export declare const toPredicateFunc: (p: Predicate) => PredicateFunc;
@@ -0,0 +1,162 @@
1
+ /**
2
+ * Type definitions for Bmg.js relational algebra library.
3
+ *
4
+ * This file defines all types needed for type-safe relational operations:
5
+ * - Base types (AttrName, Tuple)
6
+ * - Utility types for transformations (Renamed, Prefixed, Joined, etc.)
7
+ * - The Relation interface with all operators
8
+ * - Helper types for predicates, extensions, aggregators, etc.
9
+ */
10
+ /** Attribute name in a tuple */
11
+ export type AttrName = string;
12
+ /** A tuple is a record mapping attribute names to values */
13
+ export type Tuple = Record<AttrName, unknown>;
14
+ /** Map from old attribute names to new attribute names */
15
+ export type RenameMap<T> = {
16
+ [K in keyof T]?: string;
17
+ };
18
+ /** Transform tuple type by renaming keys according to RenameMap */
19
+ export type Renamed<T, R extends RenameMap<T>> = {
20
+ [K in keyof T as K extends keyof R ? (R[K] extends string ? R[K] : K) : K]: T[K];
21
+ };
22
+ /** Prefix all keys except those in Except */
23
+ export type Prefixed<T, P extends string, Except extends keyof T = never> = {
24
+ [K in keyof T as K extends Except ? K : `${P}${K & string}`]: T[K];
25
+ };
26
+ /** Suffix all keys except those in Except */
27
+ export type Suffixed<T, S extends string, Except extends keyof T = never> = {
28
+ [K in keyof T as K extends Except ? K : `${K & string}${S}`]: T[K];
29
+ };
30
+ /** Extract common keys between two tuple types */
31
+ export type CommonKeys<L, R> = Extract<keyof L, keyof R>;
32
+ /** Result of inner join: L & R with R's common keys removed */
33
+ export type Joined<L, R> = L & Omit<R, CommonKeys<L, R>>;
34
+ /** Result of left join: L & optional R attributes (common keys removed) */
35
+ export type LeftJoined<L, R> = L & Partial<Omit<R, CommonKeys<L, R>>>;
36
+ /**
37
+ * Typed join keys for array form: keys must exist on BOTH operands.
38
+ * Example: suppliers.join(parts, ['city']) - 'city' must be a key of both.
39
+ */
40
+ export type TypedJoinKeysArray<L, R> = (keyof L & keyof R & string)[];
41
+ /**
42
+ * Typed join keys for object form: maps left keys to right keys.
43
+ * Example: suppliers.join(parts, { sid: 'supplier_id' })
44
+ * - Left key (sid) must exist on L
45
+ * - Right key (supplier_id) must exist on R
46
+ */
47
+ export type TypedJoinKeysObject<L, R> = {
48
+ [K in keyof L & string]?: keyof R & string;
49
+ };
50
+ /** Result of wrap: remove wrapped attrs, add nested object */
51
+ export type Wrapped<T, K extends keyof T, As extends string> = Omit<T, K> & Record<As, Pick<T, K>>;
52
+ /** Result of unwrap: remove object attr, spread its properties */
53
+ export type Unwrapped<T, K extends keyof T> = T[K] extends Record<string, unknown> ? Omit<T, K> & T[K] : Omit<T, K>;
54
+ /** Result of ungroup: remove relation attr, flatten its tuple type */
55
+ export type Ungrouped<T, K extends keyof T> = T[K] extends Relation<infer N> ? Omit<T, K> & N : Omit<T, K>;
56
+ export type AggregatorName = 'count' | 'sum' | 'min' | 'max' | 'avg' | 'collect';
57
+ export type AggregatorSpec = {
58
+ op: AggregatorName;
59
+ attr: AttrName;
60
+ };
61
+ export type AggregatorFunc = (tuples: Tuple[]) => unknown;
62
+ export type Aggregator = AggregatorName | AggregatorSpec | AggregatorFunc;
63
+ export type Aggregators = Record<AttrName, Aggregator>;
64
+ /** Infer result type from aggregator specification */
65
+ export type AggregatorResult<A> = A extends 'count' ? number : A extends {
66
+ op: 'count';
67
+ } ? number : A extends {
68
+ op: 'sum' | 'avg' | 'min' | 'max';
69
+ } ? number | null : A extends {
70
+ op: 'collect';
71
+ } ? unknown[] : A extends (tuples: Tuple[]) => infer R ? R : unknown;
72
+ /** Map aggregator definitions to their result types */
73
+ export type AggregatorResults<Aggs extends Record<string, unknown>> = {
74
+ [K in keyof Aggs]: AggregatorResult<Aggs[K]>;
75
+ };
76
+ /** Predicate function that receives a typed tuple */
77
+ export type TypedPredicateFunc<T> = (t: T) => boolean;
78
+ /** Predicate: either a partial tuple for equality matching, or a function */
79
+ export type TypedPredicate<T> = Partial<T> | TypedPredicateFunc<T>;
80
+ export type PredicateFunc = ((t: Tuple) => any);
81
+ export type Predicate = Tuple | PredicateFunc;
82
+ /** Extension function that receives a typed tuple */
83
+ export type TypedExtensionFunc<T, R> = (tuple: T) => R;
84
+ /** Extension definition: function returning value, or attribute name to copy */
85
+ export type TypedExtension<T, E extends Record<string, unknown>> = {
86
+ [K in keyof E]: TypedExtensionFunc<T, E[K]> | keyof T;
87
+ };
88
+ export type ExtensionFunc = (tuple: Tuple) => unknown;
89
+ export type Extension = Record<AttrName, ExtensionFunc | AttrName>;
90
+ export interface PrefixOptions {
91
+ except?: AttrName[];
92
+ }
93
+ export interface SuffixOptions {
94
+ except?: AttrName[];
95
+ }
96
+ export interface AutowrapOptions {
97
+ separator?: string;
98
+ }
99
+ export type Renaming = RenamingObj | RenamingFunc;
100
+ export type RenamingFunc = (attr: AttrName) => AttrName;
101
+ export type RenamingObj = Record<AttrName, AttrName>;
102
+ export type JoinKeys = AttrName[] | Record<AttrName, AttrName>;
103
+ export type TransformFunc = (value: unknown) => unknown;
104
+ export type Transformation = TransformFunc | TransformFunc[] | Record<AttrName, TransformFunc | TransformFunc[]>;
105
+ /**
106
+ * Relation interface with generic type parameter for tuple type.
107
+ * Default parameter `Tuple` ensures backwards compatibility.
108
+ *
109
+ * @typeParam T - The tuple type for this relation. Defaults to `Tuple` (Record<string, unknown>).
110
+ *
111
+ * @example
112
+ * // Untyped usage (backwards compatible)
113
+ * const r = Bmg([{ id: 1 }]); // Relation<Tuple>
114
+ *
115
+ * @example
116
+ * // Typed usage with full type safety
117
+ * interface Person { id: number; name: string }
118
+ * const r = Bmg<Person>([{ id: 1, name: 'Alice' }]);
119
+ * r.project(['id']); // Relation<{ id: number }>
120
+ */
121
+ export interface Relation<T = Tuple> {
122
+ restrict(p: TypedPredicate<T>): Relation<T>;
123
+ where(p: TypedPredicate<T>): Relation<T>;
124
+ exclude(p: TypedPredicate<T>): Relation<T>;
125
+ project<K extends keyof T>(attrs: K[]): Relation<Pick<T, K>>;
126
+ allbut<K extends keyof T>(attrs: K[]): Relation<Omit<T, K>>;
127
+ extend<E extends Record<string, unknown>>(e: TypedExtension<T, E>): Relation<T & E>;
128
+ constants<C extends Tuple>(consts: C): Relation<T & C>;
129
+ rename<R extends RenameMap<T>>(r: R): Relation<Renamed<T, R>>;
130
+ prefix<P extends string, Ex extends keyof T = never>(pfx: P, options?: {
131
+ except?: Ex[];
132
+ }): Relation<Prefixed<T, P, Ex>>;
133
+ suffix<S extends string, Ex extends keyof T = never>(sfx: S, options?: {
134
+ except?: Ex[];
135
+ }): Relation<Suffixed<T, S, Ex>>;
136
+ union(right: RelationOperand<T>): Relation<T>;
137
+ minus(right: RelationOperand<T>): Relation<T>;
138
+ intersect(right: RelationOperand<T>): Relation<T>;
139
+ matching<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<T>;
140
+ not_matching<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<T>;
141
+ join<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<Joined<T, R>>;
142
+ left_join<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<LeftJoined<T, R>>;
143
+ cross_product<R>(right: RelationOperand<R>): Relation<T & R>;
144
+ cross_join<R>(right: RelationOperand<R>): Relation<T & R>;
145
+ image<R, As extends string>(right: RelationOperand<R>, as: As, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<T & Record<As, Relation<Omit<R, keyof T & keyof R>>>>;
146
+ group<K extends keyof T, As extends string>(attrs: K[], as: As): Relation<Omit<T, K> & Record<As, Relation<Pick<T, K>>>>;
147
+ ungroup<K extends keyof T>(attr: K): Relation<Ungrouped<T, K>>;
148
+ wrap<K extends keyof T, As extends string>(attrs: K[], as: As): Relation<Wrapped<T, K, As>>;
149
+ unwrap<K extends keyof T>(attr: K): Relation<Unwrapped<T, K>>;
150
+ summarize<By extends keyof T, Aggs extends Aggregators>(by: By[], aggs: Aggs): Relation<Pick<T, By> & AggregatorResults<Aggs>>;
151
+ transform(t: Transformation): Relation<T>;
152
+ autowrap(options?: AutowrapOptions): Relation<Tuple>;
153
+ one(): T;
154
+ yByX<Y extends keyof T, X extends keyof T>(y: Y, x: X): Record<T[X] & PropertyKey, T[Y]>;
155
+ toArray(): T[];
156
+ isEqual(right: any): boolean;
157
+ }
158
+ export type RelationOperand<T = Tuple> = Relation<T> | T[];
159
+ export interface OperationalOperand<T = Tuple> {
160
+ tuples(): Iterable<T>;
161
+ output(tuples: T[]): RelationOperand<T>;
162
+ }
package/package.json CHANGED
@@ -1,9 +1,21 @@
1
1
  {
2
2
  "name": "@enspirit/bmg-js",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Relational algebra for JavaScript/TypeScript - work with arrays of objects using powerful set operations",
5
- "keywords": ["relational", "algebra", "relation", "set", "query", "data", "functional"],
5
+ "keywords": [
6
+ "relational",
7
+ "algebra",
8
+ "relation",
9
+ "set",
10
+ "query",
11
+ "data",
12
+ "functional"
13
+ ],
6
14
  "license": "MIT",
15
+ "files": [
16
+ "dist",
17
+ "src"
18
+ ],
7
19
  "repository": {
8
20
  "type": "git",
9
21
  "url": "https://github.com/enspirit/bmg.js"
@@ -11,24 +23,26 @@
11
23
  "type": "module",
12
24
  "source": "src/index.ts",
13
25
  "exports": {
14
- "types": "./dist/src/index.d.ts",
26
+ "types": "./dist/index.d.ts",
15
27
  "require": "./dist/bmg.cjs",
16
28
  "default": "./dist/bmg.modern.js"
17
29
  },
18
30
  "main": "./dist/bmg.cjs",
19
31
  "module": "./dist/bmg.module.js",
20
- "types": "./dist/src/index.d.ts",
32
+ "types": "./dist/index.d.ts",
21
33
  "unpkg": "./dist/bmg.umd.js",
22
34
  "scripts": {
23
- "build": "microbundle",
35
+ "generate:lib-definitions": "npx tsx scripts/generate-lib-definitions.ts",
36
+ "build": "npm run generate:lib-definitions && microbundle --no-generateTypes && tsc -p tsconfig.build.json",
24
37
  "dev": "microbundle watch",
25
38
  "test": "vitest run",
26
39
  "test:watch": "vitest"
27
40
  },
28
41
  "devDependencies": {
29
42
  "microbundle": "^0.15.1",
43
+ "tsx": "^4.19.0",
30
44
  "vite-tsconfig-paths": "^5.1.4",
31
- "vitest": "^1.4.0"
45
+ "vitest": "^4.0.16"
32
46
  },
33
47
  "packageManager": "pnpm@8.11.0+sha1.01bf39424f1008f29012bbae851d2acb8a20a0d0"
34
48
  }
@@ -32,7 +32,6 @@ import {
32
32
  import type {
33
33
  AttrName,
34
34
  AutowrapOptions,
35
- JoinKeys,
36
35
  Relation,
37
36
  RelationOperand,
38
37
  Transformation,
@@ -48,8 +47,10 @@ import type {
48
47
  Wrapped,
49
48
  Unwrapped,
50
49
  Ungrouped,
50
+ AggregatorResults,
51
+ TypedJoinKeysArray,
52
+ TypedJoinKeysObject,
51
53
  } from "../types";
52
- import type { AggregatorResults } from "../utility-types";
53
54
 
54
55
  /**
55
56
  * In-memory implementation of the Relation interface.
@@ -126,22 +127,22 @@ export class MemoryRelation<T = Tuple> implements Relation<T> {
126
127
 
127
128
  // === Semi-join operators ===
128
129
 
129
- matching<R>(right: RelationOperand<R>, keys?: JoinKeys): Relation<T> {
130
- return matching(this as any, right as any, keys) as unknown as Relation<T>;
130
+ matching<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<T> {
131
+ return matching(this as any, right as any, keys as any) as unknown as Relation<T>;
131
132
  }
132
133
 
133
- not_matching<R>(right: RelationOperand<R>, keys?: JoinKeys): Relation<T> {
134
- return not_matching(this as any, right as any, keys) as unknown as Relation<T>;
134
+ not_matching<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<T> {
135
+ return not_matching(this as any, right as any, keys as any) as unknown as Relation<T>;
135
136
  }
136
137
 
137
138
  // === Join operators ===
138
139
 
139
- join<R>(right: RelationOperand<R>, keys?: JoinKeys): Relation<Joined<T, R>> {
140
- return join(this as any, right as any, keys) as unknown as Relation<Joined<T, R>>;
140
+ join<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<Joined<T, R>> {
141
+ return join(this as any, right as any, keys as any) as unknown as Relation<Joined<T, R>>;
141
142
  }
142
143
 
143
- left_join<R>(right: RelationOperand<R>, keys?: JoinKeys): Relation<LeftJoined<T, R>> {
144
- return left_join(this as any, right as any, keys) as unknown as Relation<LeftJoined<T, R>>;
144
+ left_join<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<LeftJoined<T, R>> {
145
+ return left_join(this as any, right as any, keys as any) as unknown as Relation<LeftJoined<T, R>>;
145
146
  }
146
147
 
147
148
  cross_product<R>(right: RelationOperand<R>): Relation<T & R> {
@@ -154,8 +155,8 @@ export class MemoryRelation<T = Tuple> implements Relation<T> {
154
155
 
155
156
  // === Nesting operators ===
156
157
 
157
- image<R, As extends string>(right: RelationOperand<R>, as: As, keys?: JoinKeys): Relation<T & Record<As, Relation<Omit<R, keyof T & keyof R>>>> {
158
- return image(this as any, right as any, as, keys) as unknown as Relation<T & Record<As, Relation<Omit<R, keyof T & keyof R>>>>;
158
+ image<R, As extends string>(right: RelationOperand<R>, as: As, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<T & Record<As, Relation<Omit<R, keyof T & keyof R>>>> {
159
+ return image(this as any, right as any, as, keys as any) as unknown as Relation<T & Record<As, Relation<Omit<R, keyof T & keyof R>>>>;
159
160
  }
160
161
 
161
162
  group<K extends keyof T, As extends string>(attrs: K[], as: As): Relation<Omit<T, K> & Record<As, Relation<Pick<T, K>>>> {
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  export * from './operators';
2
2
  export * from './types';
3
- export * from './utility-types';
3
+ export { LIB_DEFINITIONS } from './lib-definitions';
4
4
 
5
5
  import { MemoryRelation } from './Relation';
6
6
  import { isRelation } from './operators';