@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.
- package/LICENSE.md +21 -0
- package/dist/Relation/Memory.d.ts +45 -0
- package/dist/Relation/index.d.ts +1 -0
- package/dist/bmg.cjs +2 -0
- package/dist/bmg.cjs.map +1 -0
- package/dist/bmg.modern.js +2 -0
- package/dist/bmg.modern.js.map +1 -0
- package/dist/bmg.module.js +2 -0
- package/dist/bmg.module.js.map +1 -0
- package/dist/bmg.umd.js +2 -0
- package/dist/bmg.umd.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/lib-definitions.d.ts +1 -0
- package/dist/operators/_helpers.d.ts +142 -0
- package/dist/operators/allbut.d.ts +2 -0
- package/dist/operators/autowrap.d.ts +2 -0
- package/dist/operators/constants.d.ts +2 -0
- package/dist/operators/cross_product.d.ts +3 -0
- package/dist/operators/exclude.d.ts +2 -0
- package/dist/operators/extend.d.ts +2 -0
- package/dist/operators/group.d.ts +2 -0
- package/dist/operators/image.d.ts +2 -0
- package/dist/operators/index.d.ts +30 -0
- package/dist/operators/intersect.d.ts +2 -0
- package/dist/operators/isEqual.d.ts +2 -0
- package/dist/operators/isRelation.d.ts +1 -0
- package/dist/operators/join.d.ts +2 -0
- package/dist/operators/left_join.d.ts +2 -0
- package/dist/operators/matching.d.ts +2 -0
- package/dist/operators/minus.d.ts +2 -0
- package/dist/operators/not_matching.d.ts +2 -0
- package/dist/operators/one.d.ts +2 -0
- package/dist/operators/prefix.d.ts +2 -0
- package/dist/operators/project.d.ts +2 -0
- package/dist/operators/rename.d.ts +2 -0
- package/dist/operators/restrict.d.ts +2 -0
- package/dist/operators/suffix.d.ts +2 -0
- package/dist/operators/summarize.d.ts +2 -0
- package/dist/operators/transform.d.ts +2 -0
- package/dist/operators/ungroup.d.ts +2 -0
- package/dist/operators/union.d.ts +2 -0
- package/dist/operators/unwrap.d.ts +2 -0
- package/dist/operators/where.d.ts +1 -0
- package/dist/operators/wrap.d.ts +2 -0
- package/dist/operators/yByX.d.ts +2 -0
- package/dist/support/toPredicateFunc.d.ts +2 -0
- package/dist/types.d.ts +162 -0
- package/package.json +20 -6
- package/src/Relation/Memory.ts +13 -12
- package/src/index.ts +1 -1
- package/src/lib-definitions.ts +281 -0
- package/src/types.ts +142 -54
- package/.claude/safe-setup/.env.example +0 -3
- package/.claude/safe-setup/Dockerfile.claude +0 -36
- package/.claude/safe-setup/HACKING.md +0 -63
- package/.claude/safe-setup/Makefile +0 -22
- package/.claude/safe-setup/docker-compose.yml +0 -18
- package/.claude/safe-setup/entrypoint.sh +0 -13
- package/.claude/settings.local.json +0 -9
- package/.claude/typescript-annotations.md +0 -273
- package/.github/workflows/test.yml +0 -26
- package/CLAUDE.md +0 -48
- package/Makefile +0 -2
- package/example/README.md +0 -22
- package/example/index.ts +0 -316
- package/example/package.json +0 -16
- package/example/tsconfig.json +0 -11
- package/src/utility-types.ts +0 -77
- package/tests/bmg.test.ts +0 -16
- package/tests/fixtures.ts +0 -9
- package/tests/operators/allbut.test.ts +0 -51
- package/tests/operators/autowrap.test.ts +0 -82
- package/tests/operators/constants.test.ts +0 -37
- package/tests/operators/cross_product.test.ts +0 -90
- package/tests/operators/exclude.test.ts +0 -43
- package/tests/operators/extend.test.ts +0 -45
- package/tests/operators/group.test.ts +0 -69
- package/tests/operators/image.test.ts +0 -152
- package/tests/operators/intersect.test.ts +0 -53
- package/tests/operators/isEqual.test.ts +0 -111
- package/tests/operators/join.test.ts +0 -116
- package/tests/operators/left_join.test.ts +0 -116
- package/tests/operators/matching.test.ts +0 -91
- package/tests/operators/minus.test.ts +0 -47
- package/tests/operators/not_matching.test.ts +0 -104
- package/tests/operators/one.test.ts +0 -19
- package/tests/operators/prefix.test.ts +0 -37
- package/tests/operators/project.test.ts +0 -48
- package/tests/operators/rename.test.ts +0 -39
- package/tests/operators/restrict.test.ts +0 -27
- package/tests/operators/suffix.test.ts +0 -37
- package/tests/operators/summarize.test.ts +0 -109
- package/tests/operators/transform.test.ts +0 -94
- package/tests/operators/ungroup.test.ts +0 -67
- package/tests/operators/union.test.ts +0 -51
- package/tests/operators/unwrap.test.ts +0 -50
- package/tests/operators/where.test.ts +0 -33
- package/tests/operators/wrap.test.ts +0 -54
- package/tests/operators/yByX.test.ts +0 -32
- package/tests/types/relation.test.ts +0 -296
- package/tsconfig.json +0 -37
- package/tsconfig.node.json +0 -9
- 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
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
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
|
-
//
|
|
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
|
-
|
|
18
|
-
|
|
90
|
+
// ============================================================================
|
|
91
|
+
// Aggregator Types
|
|
92
|
+
// ============================================================================
|
|
19
93
|
|
|
20
|
-
export
|
|
21
|
-
|
|
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
|
-
|
|
25
|
-
|
|
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
|
-
|
|
29
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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?:
|
|
106
|
-
not_matching<R>(right: RelationOperand<R>, keys?:
|
|
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?:
|
|
111
|
-
left_join<R>(right: RelationOperand<R>, keys?:
|
|
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?:
|
|
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
|
|
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,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:
|