@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,281 @@
1
+ // Auto-generated from types.ts
2
+ // Run 'npm run generate:lib-definitions' to regenerate
3
+
4
+ export const LIB_DEFINITIONS = `
5
+ /**
6
+ * Type definitions for Bmg.js relational algebra library.
7
+ *
8
+ * This file defines all types needed for type-safe relational operations:
9
+ * - Base types (AttrName, Tuple)
10
+ * - Utility types for transformations (Renamed, Prefixed, Joined, etc.)
11
+ * - The Relation interface with all operators
12
+ * - Helper types for predicates, extensions, aggregators, etc.
13
+ */
14
+
15
+ // ============================================================================
16
+ // Base Types
17
+ // ============================================================================
18
+
19
+ /** Attribute name in a tuple */
20
+ type AttrName = string
21
+
22
+ /** A tuple is a record mapping attribute names to values */
23
+ type Tuple = Record<AttrName, unknown>
24
+
25
+ // ============================================================================
26
+ // Rename Types
27
+ // ============================================================================
28
+
29
+ /** Map from old attribute names to new attribute names */
30
+ type RenameMap<T> = { [K in keyof T]?: string };
31
+
32
+ /** Transform tuple type by renaming keys according to RenameMap */
33
+ type Renamed<T, R extends RenameMap<T>> = {
34
+ [K in keyof T as K extends keyof R ? (R[K] extends string ? R[K] : K) : K]: T[K];
35
+ };
36
+
37
+ // ============================================================================
38
+ // Prefix/Suffix Types
39
+ // ============================================================================
40
+
41
+ /** Prefix all keys except those in Except */
42
+ type Prefixed<T, P extends string, Except extends keyof T = never> = {
43
+ [K in keyof T as K extends Except ? K : \`\${P}\${K & string}\`]: T[K];
44
+ };
45
+
46
+ /** Suffix all keys except those in Except */
47
+ type Suffixed<T, S extends string, Except extends keyof T = never> = {
48
+ [K in keyof T as K extends Except ? K : \`\${K & string}\${S}\`]: T[K];
49
+ };
50
+
51
+ // ============================================================================
52
+ // Join Types
53
+ // ============================================================================
54
+
55
+ /** Extract common keys between two tuple types */
56
+ type CommonKeys<L, R> = Extract<keyof L, keyof R>;
57
+
58
+ /** Result of inner join: L & R with R's common keys removed */
59
+ type Joined<L, R> = L & Omit<R, CommonKeys<L, R>>;
60
+
61
+ /** Result of left join: L & optional R attributes (common keys removed) */
62
+ type LeftJoined<L, R> = L & Partial<Omit<R, CommonKeys<L, R>>>;
63
+
64
+ /**
65
+ * Typed join keys for array form: keys must exist on BOTH operands.
66
+ * Example: suppliers.join(parts, ['city']) - 'city' must be a key of both.
67
+ */
68
+ type TypedJoinKeysArray<L, R> = (keyof L & keyof R & string)[];
69
+
70
+ /**
71
+ * Typed join keys for object form: maps left keys to right keys.
72
+ * Example: suppliers.join(parts, { sid: 'supplier_id' })
73
+ * - Left key (sid) must exist on L
74
+ * - Right key (supplier_id) must exist on R
75
+ */
76
+ type TypedJoinKeysObject<L, R> = { [K in keyof L & string]?: keyof R & string };
77
+
78
+ // ============================================================================
79
+ // Wrap/Unwrap Types
80
+ // ============================================================================
81
+
82
+ /** Result of wrap: remove wrapped attrs, add nested object */
83
+ type Wrapped<T, K extends keyof T, As extends string> =
84
+ Omit<T, K> & Record<As, Pick<T, K>>;
85
+
86
+ /** Result of unwrap: remove object attr, spread its properties */
87
+ type Unwrapped<T, K extends keyof T> =
88
+ T[K] extends Record<string, unknown> ? Omit<T, K> & T[K] : Omit<T, K>;
89
+
90
+ /** Result of ungroup: remove relation attr, flatten its tuple type */
91
+ type Ungrouped<T, K extends keyof T> =
92
+ T[K] extends Relation<infer N> ? Omit<T, K> & N : Omit<T, K>;
93
+
94
+ // ============================================================================
95
+ // Aggregator Types
96
+ // ============================================================================
97
+
98
+ type AggregatorName = 'count' | 'sum' | 'min' | 'max' | 'avg' | 'collect'
99
+ type AggregatorSpec = { op: AggregatorName, attr: AttrName }
100
+ type AggregatorFunc = (tuples: Tuple[]) => unknown
101
+ type Aggregator = AggregatorName | AggregatorSpec | AggregatorFunc
102
+ type Aggregators = Record<AttrName, Aggregator>
103
+
104
+ /** Infer result type from aggregator specification */
105
+ type AggregatorResult<A> =
106
+ A extends 'count' ? number :
107
+ A extends { op: 'count' } ? number :
108
+ A extends { op: 'sum' | 'avg' | 'min' | 'max' } ? number | null :
109
+ A extends { op: 'collect' } ? unknown[] :
110
+ A extends (tuples: Tuple[]) => infer R ? R :
111
+ unknown;
112
+
113
+ /** Map aggregator definitions to their result types */
114
+ type AggregatorResults<Aggs extends Record<string, unknown>> = {
115
+ [K in keyof Aggs]: AggregatorResult<Aggs[K]>;
116
+ };
117
+
118
+ // ============================================================================
119
+ // Predicate Types
120
+ // ============================================================================
121
+
122
+ /** Predicate function that receives a typed tuple */
123
+ type TypedPredicateFunc<T> = (t: T) => boolean
124
+
125
+ /** Predicate: either a partial tuple for equality matching, or a function */
126
+ type TypedPredicate<T> = Partial<T> | TypedPredicateFunc<T>
127
+
128
+ // Legacy predicate types (for backwards compatibility with standalone operators)
129
+ type PredicateFunc = ((t: Tuple) => any)
130
+ type Predicate = Tuple | PredicateFunc
131
+
132
+ // ============================================================================
133
+ // Extension Types
134
+ // ============================================================================
135
+
136
+ /** Extension function that receives a typed tuple */
137
+ type TypedExtensionFunc<T, R> = (tuple: T) => R
138
+
139
+ /** Extension definition: function returning value, or attribute name to copy */
140
+ type TypedExtension<T, E extends Record<string, unknown>> = {
141
+ [K in keyof E]: TypedExtensionFunc<T, E[K]> | keyof T;
142
+ }
143
+
144
+ type ExtensionFunc = (tuple: Tuple) => unknown
145
+ type Extension = Record<AttrName, ExtensionFunc | AttrName>
146
+
147
+ // ============================================================================
148
+ // Other Helper Types
149
+ // ============================================================================
150
+
151
+ interface PrefixOptions {
152
+ except?: AttrName[]
153
+ }
154
+
155
+ interface SuffixOptions {
156
+ except?: AttrName[]
157
+ }
158
+
159
+ interface AutowrapOptions {
160
+ separator?: string
161
+ }
162
+
163
+ type Renaming = RenamingObj | RenamingFunc
164
+ type RenamingFunc = (attr: AttrName) => AttrName
165
+ type RenamingObj = Record<AttrName, AttrName>
166
+
167
+ type JoinKeys = AttrName[] | Record<AttrName, AttrName>
168
+
169
+ type TransformFunc = (value: unknown) => unknown
170
+ type Transformation = TransformFunc | TransformFunc[] | Record<AttrName, TransformFunc | TransformFunc[]>
171
+
172
+ // ============================================================================
173
+ // Relation Interface
174
+ // ============================================================================
175
+
176
+ /**
177
+ * Relation interface with generic type parameter for tuple type.
178
+ * Default parameter \`Tuple\` ensures backwards compatibility.
179
+ *
180
+ * @typeParam T - The tuple type for this relation. Defaults to \`Tuple\` (Record<string, unknown>).
181
+ *
182
+ * @example
183
+ * // Untyped usage (backwards compatible)
184
+ * const r = Bmg([{ id: 1 }]); // Relation<Tuple>
185
+ *
186
+ * @example
187
+ * // Typed usage with full type safety
188
+ * interface Person { id: number; name: string }
189
+ * const r = Bmg<Person>([{ id: 1, name: 'Alice' }]);
190
+ * r.project(['id']); // Relation<{ id: number }>
191
+ */
192
+ interface Relation<T = Tuple> {
193
+ // === Type-preserving operators ===
194
+
195
+ restrict(p: TypedPredicate<T>): Relation<T>
196
+ where(p: TypedPredicate<T>): Relation<T>
197
+ exclude(p: TypedPredicate<T>): Relation<T>
198
+
199
+ // === Projection operators ===
200
+
201
+ project<K extends keyof T>(attrs: K[]): Relation<Pick<T, K>>
202
+ allbut<K extends keyof T>(attrs: K[]): Relation<Omit<T, K>>
203
+
204
+ // === Extension operators ===
205
+
206
+ extend<E extends Record<string, unknown>>(e: TypedExtension<T, E>): Relation<T & E>
207
+ constants<C extends Tuple>(consts: C): Relation<T & C>
208
+
209
+ // === Rename operators ===
210
+
211
+ rename<R extends RenameMap<T>>(r: R): Relation<Renamed<T, R>>
212
+ prefix<P extends string, Ex extends keyof T = never>(pfx: P, options?: { except?: Ex[] }): Relation<Prefixed<T, P, Ex>>
213
+ suffix<S extends string, Ex extends keyof T = never>(sfx: S, options?: { except?: Ex[] }): Relation<Suffixed<T, S, Ex>>
214
+
215
+ // === Set operators (require same type) ===
216
+
217
+ union(right: RelationOperand<T>): Relation<T>
218
+ minus(right: RelationOperand<T>): Relation<T>
219
+ intersect(right: RelationOperand<T>): Relation<T>
220
+
221
+ // === Semi-join operators (preserve left type) ===
222
+
223
+ matching<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<T>
224
+ not_matching<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<T>
225
+
226
+ // === Join operators ===
227
+
228
+ join<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<Joined<T, R>>
229
+ left_join<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<LeftJoined<T, R>>
230
+ cross_product<R>(right: RelationOperand<R>): Relation<T & R>
231
+ cross_join<R>(right: RelationOperand<R>): Relation<T & R>
232
+
233
+ // === Nesting operators ===
234
+
235
+ 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>>>>
236
+ group<K extends keyof T, As extends string>(attrs: K[], as: As): Relation<Omit<T, K> & Record<As, Relation<Pick<T, K>>>>
237
+ ungroup<K extends keyof T>(attr: K): Relation<Ungrouped<T, K>>
238
+ wrap<K extends keyof T, As extends string>(attrs: K[], as: As): Relation<Wrapped<T, K, As>>
239
+ unwrap<K extends keyof T>(attr: K): Relation<Unwrapped<T, K>>
240
+
241
+ // === Aggregation ===
242
+
243
+ summarize<By extends keyof T, Aggs extends Aggregators>(by: By[], aggs: Aggs): Relation<Pick<T, By> & AggregatorResults<Aggs>>
244
+
245
+ // === Transform ===
246
+
247
+ transform(t: Transformation): Relation<T>
248
+
249
+ // === Dynamic (loses type precision) ===
250
+
251
+ autowrap(options?: AutowrapOptions): Relation<Tuple>
252
+
253
+ // === Non-relational ===
254
+
255
+ one(): T
256
+ yByX<Y extends keyof T, X extends keyof T>(y: Y, x: X): Record<T[X] & PropertyKey, T[Y]>
257
+ toArray(): T[]
258
+ isEqual(right: any): boolean
259
+ }
260
+
261
+ // ============================================================================
262
+ // Operands
263
+ // ============================================================================
264
+
265
+ type RelationOperand<T = Tuple> = Relation<T> | T[]
266
+
267
+ interface OperationalOperand<T = Tuple> {
268
+ tuples(): Iterable<T>
269
+ output(tuples: T[]): RelationOperand<T>
270
+ }
271
+
272
+ // ============================================================================
273
+ // Bmg Function Declaration
274
+ // ============================================================================
275
+
276
+ /** Create a relation from an array of tuples */
277
+ declare function Bmg<T>(tuples: T[]): Relation<T>
278
+ declare namespace Bmg {
279
+ function isRelation(op: unknown): boolean
280
+ }
281
+ `
package/src/types.ts CHANGED
@@ -1,36 +1,118 @@
1
- import type {
2
- RenameMap, Renamed,
3
- Prefixed, Suffixed,
4
- Joined, LeftJoined,
5
- Wrapped, Unwrapped,
6
- AggregatorResults
7
- } from './utility-types';
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
+
11
+ // ============================================================================
12
+ // Base Types
13
+ // ============================================================================
14
+
15
+ /** Attribute name in a tuple */
16
+ export type AttrName = string
17
+
18
+ /** A tuple is a record mapping attribute names to values */
19
+ export type Tuple = Record<AttrName, unknown>
8
20
 
9
21
  // ============================================================================
10
- // Group/Ungroup Types (defined here due to Relation dependency)
22
+ // Rename Types
11
23
  // ============================================================================
12
24
 
25
+ /** Map from old attribute names to new attribute names */
26
+ export type RenameMap<T> = { [K in keyof T]?: string };
27
+
28
+ /** Transform tuple type by renaming keys according to RenameMap */
29
+ export type Renamed<T, R extends RenameMap<T>> = {
30
+ [K in keyof T as K extends keyof R ? (R[K] extends string ? R[K] : K) : K]: T[K];
31
+ };
32
+
33
+ // ============================================================================
34
+ // Prefix/Suffix Types
35
+ // ============================================================================
36
+
37
+ /** Prefix all keys except those in Except */
38
+ export type Prefixed<T, P extends string, Except extends keyof T = never> = {
39
+ [K in keyof T as K extends Except ? K : `${P}${K & string}`]: T[K];
40
+ };
41
+
42
+ /** Suffix all keys except those in Except */
43
+ export type Suffixed<T, S extends string, Except extends keyof T = never> = {
44
+ [K in keyof T as K extends Except ? K : `${K & string}${S}`]: T[K];
45
+ };
46
+
47
+ // ============================================================================
48
+ // Join Types
49
+ // ============================================================================
50
+
51
+ /** Extract common keys between two tuple types */
52
+ export type CommonKeys<L, R> = Extract<keyof L, keyof R>;
53
+
54
+ /** Result of inner join: L & R with R's common keys removed */
55
+ export type Joined<L, R> = L & Omit<R, CommonKeys<L, R>>;
56
+
57
+ /** Result of left join: L & optional R attributes (common keys removed) */
58
+ export type LeftJoined<L, R> = L & Partial<Omit<R, CommonKeys<L, R>>>;
59
+
60
+ /**
61
+ * Typed join keys for array form: keys must exist on BOTH operands.
62
+ * Example: suppliers.join(parts, ['city']) - 'city' must be a key of both.
63
+ */
64
+ export type TypedJoinKeysArray<L, R> = (keyof L & keyof R & string)[];
65
+
66
+ /**
67
+ * Typed join keys for object form: maps left keys to right keys.
68
+ * Example: suppliers.join(parts, { sid: 'supplier_id' })
69
+ * - Left key (sid) must exist on L
70
+ * - Right key (supplier_id) must exist on R
71
+ */
72
+ export type TypedJoinKeysObject<L, R> = { [K in keyof L & string]?: keyof R & string };
73
+
74
+ // ============================================================================
75
+ // Wrap/Unwrap Types
76
+ // ============================================================================
77
+
78
+ /** Result of wrap: remove wrapped attrs, add nested object */
79
+ export type Wrapped<T, K extends keyof T, As extends string> =
80
+ Omit<T, K> & Record<As, Pick<T, K>>;
81
+
82
+ /** Result of unwrap: remove object attr, spread its properties */
83
+ export type Unwrapped<T, K extends keyof T> =
84
+ T[K] extends Record<string, unknown> ? Omit<T, K> & T[K] : Omit<T, K>;
85
+
13
86
  /** Result of ungroup: remove relation attr, flatten its tuple type */
14
87
  export type Ungrouped<T, K extends keyof T> =
15
88
  T[K] extends Relation<infer N> ? Omit<T, K> & N : Omit<T, K>;
16
89
 
17
- export type AttrName = string
18
- export type Tuple = Record<AttrName, unknown>
90
+ // ============================================================================
91
+ // Aggregator Types
92
+ // ============================================================================
19
93
 
20
- export interface PrefixOptions {
21
- except?: AttrName[]
22
- }
94
+ export type AggregatorName = 'count' | 'sum' | 'min' | 'max' | 'avg' | 'collect'
95
+ export type AggregatorSpec = { op: AggregatorName, attr: AttrName }
96
+ export type AggregatorFunc = (tuples: Tuple[]) => unknown
97
+ export type Aggregator = AggregatorName | AggregatorSpec | AggregatorFunc
98
+ export type Aggregators = Record<AttrName, Aggregator>
23
99
 
24
- export interface SuffixOptions {
25
- except?: AttrName[]
26
- }
100
+ /** Infer result type from aggregator specification */
101
+ export type AggregatorResult<A> =
102
+ A extends 'count' ? number :
103
+ A extends { op: 'count' } ? number :
104
+ A extends { op: 'sum' | 'avg' | 'min' | 'max' } ? number | null :
105
+ A extends { op: 'collect' } ? unknown[] :
106
+ A extends (tuples: Tuple[]) => infer R ? R :
107
+ unknown;
27
108
 
28
- export interface AutowrapOptions {
29
- separator?: string
30
- }
109
+ /** Map aggregator definitions to their result types */
110
+ export type AggregatorResults<Aggs extends Record<string, unknown>> = {
111
+ [K in keyof Aggs]: AggregatorResult<Aggs[K]>;
112
+ };
31
113
 
32
114
  // ============================================================================
33
- // Typed Predicates
115
+ // Predicate Types
34
116
  // ============================================================================
35
117
 
36
118
  /** Predicate function that receives a typed tuple */
@@ -39,8 +121,12 @@ export type TypedPredicateFunc<T> = (t: T) => boolean
39
121
  /** Predicate: either a partial tuple for equality matching, or a function */
40
122
  export type TypedPredicate<T> = Partial<T> | TypedPredicateFunc<T>
41
123
 
124
+ // Legacy predicate types (for backwards compatibility with standalone operators)
125
+ export type PredicateFunc = ((t: Tuple) => any)
126
+ export type Predicate = Tuple | PredicateFunc
127
+
42
128
  // ============================================================================
43
- // Typed Extensions
129
+ // Extension Types
44
130
  // ============================================================================
45
131
 
46
132
  /** Extension function that receives a typed tuple */
@@ -51,8 +137,36 @@ export type TypedExtension<T, E extends Record<string, unknown>> = {
51
137
  [K in keyof E]: TypedExtensionFunc<T, E[K]> | keyof T;
52
138
  }
53
139
 
140
+ export type ExtensionFunc = (tuple: Tuple) => unknown
141
+ export type Extension = Record<AttrName, ExtensionFunc | AttrName>
142
+
54
143
  // ============================================================================
55
- // Generic Relation Interface
144
+ // Other Helper Types
145
+ // ============================================================================
146
+
147
+ export interface PrefixOptions {
148
+ except?: AttrName[]
149
+ }
150
+
151
+ export interface SuffixOptions {
152
+ except?: AttrName[]
153
+ }
154
+
155
+ export interface AutowrapOptions {
156
+ separator?: string
157
+ }
158
+
159
+ export type Renaming = RenamingObj | RenamingFunc
160
+ export type RenamingFunc = (attr: AttrName) => AttrName
161
+ export type RenamingObj = Record<AttrName, AttrName>
162
+
163
+ export type JoinKeys = AttrName[] | Record<AttrName, AttrName>
164
+
165
+ export type TransformFunc = (value: unknown) => unknown
166
+ export type Transformation = TransformFunc | TransformFunc[] | Record<AttrName, TransformFunc | TransformFunc[]>
167
+
168
+ // ============================================================================
169
+ // Relation Interface
56
170
  // ============================================================================
57
171
 
58
172
  /**
@@ -102,19 +216,19 @@ export interface Relation<T = Tuple> {
102
216
 
103
217
  // === Semi-join operators (preserve left type) ===
104
218
 
105
- matching<R>(right: RelationOperand<R>, keys?: JoinKeys): Relation<T>
106
- not_matching<R>(right: RelationOperand<R>, keys?: JoinKeys): Relation<T>
219
+ matching<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<T>
220
+ not_matching<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<T>
107
221
 
108
222
  // === Join operators ===
109
223
 
110
- join<R>(right: RelationOperand<R>, keys?: JoinKeys): Relation<Joined<T, R>>
111
- left_join<R>(right: RelationOperand<R>, keys?: JoinKeys): Relation<LeftJoined<T, R>>
224
+ join<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<Joined<T, R>>
225
+ left_join<R>(right: RelationOperand<R>, keys?: TypedJoinKeysArray<T, R> | TypedJoinKeysObject<T, R>): Relation<LeftJoined<T, R>>
112
226
  cross_product<R>(right: RelationOperand<R>): Relation<T & R>
113
227
  cross_join<R>(right: RelationOperand<R>): Relation<T & R>
114
228
 
115
229
  // === Nesting operators ===
116
230
 
117
- image<R, As extends string>(right: RelationOperand<R>, as: As, keys?: JoinKeys): Relation<T & Record<As, Relation<Omit<R, keyof T & keyof R>>>>
231
+ 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>>>>
118
232
  group<K extends keyof T, As extends string>(attrs: K[], as: As): Relation<Omit<T, K> & Record<As, Relation<Pick<T, K>>>>
119
233
  ungroup<K extends keyof T>(attr: K): Relation<Ungrouped<T, K>>
120
234
  wrap<K extends keyof T, As extends string>(attrs: K[], as: As): Relation<Wrapped<T, K, As>>
@@ -141,7 +255,7 @@ export interface Relation<T = Tuple> {
141
255
  }
142
256
 
143
257
  // ============================================================================
144
- // Operands and Helpers
258
+ // Operands
145
259
  // ============================================================================
146
260
 
147
261
  export type RelationOperand<T = Tuple> = Relation<T> | T[]
@@ -150,29 +264,3 @@ export interface OperationalOperand<T = Tuple> {
150
264
  tuples(): Iterable<T>
151
265
  output(tuples: T[]): RelationOperand<T>
152
266
  }
153
-
154
- // Legacy predicate types (for backwards compatibility with standalone operators)
155
- export type PredicateFunc = ((t: Tuple) => any)
156
- export type Predicate = Tuple | PredicateFunc
157
-
158
- export type Renaming = RenamingObj | RenamingFunc
159
- export type RenamingFunc = (attr: AttrName) => AttrName
160
- export type RenamingObj = Record<AttrName, AttrName>
161
-
162
- export type ExtensionFunc = (tuple: Tuple) => unknown
163
- export type Extension = Record<AttrName, ExtensionFunc | AttrName>
164
-
165
- export type JoinKeys = AttrName[] | Record<AttrName, AttrName>
166
-
167
- export type AggregatorName = 'count' | 'sum' | 'min' | 'max' | 'avg' | 'collect'
168
- export type AggregatorSpec = { op: AggregatorName, attr: AttrName }
169
- export type AggregatorFunc = (tuples: Tuple[]) => unknown
170
- export type Aggregator = AggregatorName | AggregatorSpec | AggregatorFunc
171
- export type Aggregators = Record<AttrName, Aggregator>
172
-
173
- export type TransformFunc = (value: unknown) => unknown
174
- export type Transformation = TransformFunc | TransformFunc[] | Record<AttrName, TransformFunc | TransformFunc[]>
175
-
176
- // Re-export utility types for convenience
177
- export type { RenameMap, Renamed, Prefixed, Suffixed, Joined, LeftJoined, Wrapped, Unwrapped } from './utility-types';
178
- // Ungrouped is defined in this file (not utility-types) due to Relation dependency
@@ -1,3 +0,0 @@
1
- # Git configuration (used for commits inside the container)
2
- GIT_USER_NAME=Your Name
3
- GIT_USER_EMAIL=your.email@example.com
@@ -1,36 +0,0 @@
1
- FROM alpine:latest
2
-
3
- # Install packages: base tools, node, ruby, postgresql client
4
- RUN apk update && apk add --no-cache \
5
- bash \
6
- curl \
7
- git \
8
- build-base \
9
- nodejs \
10
- npm \
11
- vim \
12
- sudo
13
-
14
- # Create non-root user with sudo access
15
- RUN addgroup -S bmg && adduser -S bmg -G bmg \
16
- && echo "bmg ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers
17
-
18
- # Install Claude Code CLI globally
19
- RUN npm install -g @anthropic-ai/claude-code
20
-
21
- # Set up working directory
22
- WORKDIR /workspace
23
-
24
- # Copy and set up entrypoint
25
- COPY entrypoint.sh /entrypoint.sh
26
- RUN chmod +x /entrypoint.sh
27
-
28
- # Create .claude directory for history persistence
29
- RUN mkdir -p /home/bmg/.claude && chown -R bmg:bmg /home/bmg/.claude
30
-
31
- # Switch to non-root user
32
- USER bmg
33
-
34
- # Use entrypoint to configure git and run command
35
- ENTRYPOINT ["/entrypoint.sh"]
36
- CMD ["bash"]
@@ -1,63 +0,0 @@
1
- # Hacking on Bmg with Claude Code
2
-
3
- This Docker setup provides a sandboxed environment with all tools needed to hack on Bmg:
4
- - Node.js
5
- - Claude Code CLI
6
-
7
- ## Prerequisites
8
-
9
- 1. Docker and Docker Compose installed
10
- 2. A Claude Code account
11
-
12
- ## Getting Started
13
-
14
- ```bash
15
- cd .claude/safe-setup
16
-
17
- # Create your .env file from template
18
- cp .env.example .env
19
-
20
- # Edit .env to set your git identity
21
- # GIT_USER_NAME=Your Name
22
- # GIT_USER_EMAIL=your.email@example.com
23
-
24
- # Build and start containers
25
- make up
26
-
27
- # Enter the dev environment
28
- make shell
29
- ```
30
-
31
- ## Persistent Data
32
-
33
- - **Claude history**: Stored in a named Docker volume (`claude-history`) that persists across container restarts
34
- - **Git configuration**: Automatically set from `GIT_USER_NAME` and `GIT_USER_EMAIL` environment variables
35
-
36
- ## Inside the Container
37
-
38
- You're now user `bmg` in `/workspace` (the Bmg project root).
39
-
40
- ```bash
41
- # Install project dependencies
42
- npm install
43
-
44
- # Run tests
45
- npm run test
46
-
47
- # Use Claude Code
48
- claude
49
- ```
50
-
51
- PostgreSQL is pre-configured via environment variables. Just run `psql` to connect.
52
-
53
- ## Commands
54
-
55
- | Command | Description |
56
- |---------|-------------|
57
- | `make up` | Build and start containers |
58
- | `make down` | Stop containers |
59
- | `make shell` | Enter dev container |
60
- | `make restart` | Restart everything |
61
- | `make logs` | Follow container logs |
62
- | `make status` | Show container status |
63
- | `make clean` | Remove containers and images |
@@ -1,22 +0,0 @@
1
- .PHONY: up down shell restart clean logs status
2
-
3
- up:
4
- docker compose up -d --build
5
- @echo "Containers started. Use 'make shell' to access dev environment"
6
-
7
- down:
8
- docker compose down
9
-
10
- shell:
11
- docker compose exec dev bash
12
-
13
- restart: down up
14
-
15
- clean: down
16
- docker compose down --rmi all --volumes
17
-
18
- logs:
19
- docker compose logs -f
20
-
21
- status:
22
- @docker compose ps
@@ -1,18 +0,0 @@
1
- services:
2
-
3
- dev:
4
- build:
5
- context: .
6
- dockerfile: Dockerfile.claude
7
- container_name: claude
8
- volumes:
9
- - ../..:/workspace
10
- - claude-history:/home/bmg/.claude
11
- environment:
12
- - GIT_USER_NAME=${GIT_USER_NAME:-}
13
- - GIT_USER_EMAIL=${GIT_USER_EMAIL:-}
14
- stdin_open: true
15
- tty: true
16
-
17
- volumes:
18
- claude-history: