@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
package/example/index.ts DELETED
@@ -1,316 +0,0 @@
1
- /**
2
- * Bmg.js Example - Relational Algebra Operations (TypeScript)
3
- *
4
- * This example demonstrates chaining various relational operators
5
- * using the classic suppliers/parts/shipments dataset with full type safety.
6
- */
7
-
8
- import { Bmg, Relation } from '@enspirit/bmg-js';
9
-
10
- // =============================================================================
11
- // Type Definitions
12
- // =============================================================================
13
-
14
- interface Supplier {
15
- sid: string;
16
- name: string;
17
- status: number;
18
- city: string;
19
- }
20
-
21
- interface Part {
22
- pid: string;
23
- pname: string;
24
- color: string;
25
- weight: number;
26
- city: string;
27
- }
28
-
29
- interface Shipment {
30
- sid: string;
31
- pid: string;
32
- qty: number;
33
- }
34
-
35
- // =============================================================================
36
- // Sample Data: Suppliers, Parts, and Shipments
37
- // =============================================================================
38
-
39
- const suppliers = Bmg<Supplier>([
40
- { sid: 'S1', name: 'Smith', status: 20, city: 'London' },
41
- { sid: 'S2', name: 'Jones', status: 10, city: 'Paris' },
42
- { sid: 'S3', name: 'Blake', status: 30, city: 'Paris' },
43
- { sid: 'S4', name: 'Clark', status: 20, city: 'London' },
44
- { sid: 'S5', name: 'Adams', status: 30, city: 'Athens' },
45
- ]);
46
-
47
- const parts = Bmg<Part>([
48
- { pid: 'P1', pname: 'Nut', color: 'Red', weight: 12, city: 'London' },
49
- { pid: 'P2', pname: 'Bolt', color: 'Green', weight: 17, city: 'Paris' },
50
- { pid: 'P3', pname: 'Screw', color: 'Blue', weight: 17, city: 'Oslo' },
51
- { pid: 'P4', pname: 'Screw', color: 'Red', weight: 14, city: 'London' },
52
- { pid: 'P5', pname: 'Cam', color: 'Blue', weight: 12, city: 'Paris' },
53
- { pid: 'P6', pname: 'Cog', color: 'Red', weight: 19, city: 'London' },
54
- ]);
55
-
56
- const shipments = Bmg<Shipment>([
57
- { sid: 'S1', pid: 'P1', qty: 300 },
58
- { sid: 'S1', pid: 'P2', qty: 200 },
59
- { sid: 'S1', pid: 'P3', qty: 400 },
60
- { sid: 'S1', pid: 'P4', qty: 200 },
61
- { sid: 'S1', pid: 'P5', qty: 100 },
62
- { sid: 'S1', pid: 'P6', qty: 100 },
63
- { sid: 'S2', pid: 'P1', qty: 300 },
64
- { sid: 'S2', pid: 'P2', qty: 400 },
65
- { sid: 'S3', pid: 'P2', qty: 200 },
66
- { sid: 'S4', pid: 'P2', qty: 200 },
67
- { sid: 'S4', pid: 'P4', qty: 300 },
68
- { sid: 'S4', pid: 'P5', qty: 400 },
69
- ]);
70
-
71
- // Helper to print results
72
- const printRelation = <T>(title: string, relation: Relation<T>): void => {
73
- console.log(`\n${'='.repeat(60)}`);
74
- console.log(title);
75
- console.log('='.repeat(60));
76
- console.log(relation.toArray());
77
- };
78
-
79
- // =============================================================================
80
- // Example 1: Basic Operations - restrict, project, rename
81
- // =============================================================================
82
-
83
- console.log('\n*** EXAMPLE 1: Basic Operations ***');
84
-
85
- // Find suppliers in Paris, showing only their id and name
86
- // Type: Relation<{ sid: string; name: string }>
87
- const parisSuppliers = suppliers
88
- .restrict({ city: 'Paris' })
89
- .project(['sid', 'name']);
90
-
91
- printRelation('Suppliers in Paris (id and name only)', parisSuppliers);
92
-
93
- // Rename attributes for clarity
94
- // Type: Relation<{ supplierId: string; supplierName: string; city: string }>
95
- const renamedSuppliers = suppliers
96
- .project(['sid', 'name', 'city'])
97
- .rename({ sid: 'supplierId', name: 'supplierName' });
98
-
99
- printRelation('Suppliers with renamed attributes', renamedSuppliers);
100
-
101
- // =============================================================================
102
- // Example 2: Extending Relations - extend, constants, prefix
103
- // =============================================================================
104
-
105
- console.log('\n*** EXAMPLE 2: Extending Relations ***');
106
-
107
- // Add computed attributes
108
- // Type: Relation<Part & { weightInKg: number; description: string }>
109
- const partsWithMetrics = parts
110
- .extend({
111
- weightInKg: (t: Part) => t.weight / 1000,
112
- description: (t: Part) => `${t.color} ${t.pname}`,
113
- })
114
- .project(['pid', 'description', 'weight', 'weightInKg']);
115
-
116
- printRelation('Parts with computed metrics', partsWithMetrics);
117
-
118
- // Add constant values
119
- const taggedParts = parts
120
- .restrict({ color: 'Red' })
121
- .constants({ category: 'Primary', inStock: true })
122
- .project(['pid', 'pname', 'category', 'inStock']);
123
-
124
- printRelation('Red parts with constant tags', taggedParts);
125
-
126
- // =============================================================================
127
- // Example 3: Set Operations - union, minus, intersect
128
- // =============================================================================
129
-
130
- console.log('\n*** EXAMPLE 3: Set Operations ***');
131
-
132
- const partCities = parts.project(['city']);
133
-
134
- // Cities where we have either suppliers or parts
135
- const allCities = suppliers.project(['city']).union(partCities);
136
- printRelation('All cities (suppliers OR parts)', allCities);
137
-
138
- // Cities where we have both suppliers and parts
139
- const commonCities = suppliers.project(['city']).intersect(partCities);
140
- printRelation('Common cities (suppliers AND parts)', commonCities);
141
-
142
- // Cities with parts but no suppliers
143
- const partOnlyCities = partCities.minus(suppliers.project(['city']));
144
- printRelation('Cities with parts but no suppliers', partOnlyCities);
145
-
146
- // =============================================================================
147
- // Example 4: Join Operations - join, left_join, matching
148
- // =============================================================================
149
-
150
- console.log('\n*** EXAMPLE 4: Join Operations ***');
151
-
152
- // Natural join: suppliers and parts in the same city
153
- const supplierPartsInSameCity = suppliers
154
- .project(['sid', 'name', 'city'])
155
- .join(parts.project(['pid', 'pname', 'city']));
156
-
157
- printRelation('Suppliers and parts in the same city', supplierPartsInSameCity);
158
-
159
- // Full shipment details with supplier and part info
160
- const fullShipments = shipments
161
- .join(suppliers.project(['sid', 'name']), ['sid'])
162
- .join(parts.project(['pid', 'pname', 'color']), ['pid'])
163
- .project(['name', 'pname', 'color', 'qty']);
164
-
165
- printRelation('Full shipment details', fullShipments);
166
-
167
- // Left join: all suppliers with their shipments (if any)
168
- const suppliersWithShipments = suppliers
169
- .project(['sid', 'name'])
170
- .left_join(
171
- shipments.summarize(['sid'], { totalQty: { op: 'sum', attr: 'qty' } }),
172
- ['sid']
173
- );
174
-
175
- printRelation('All suppliers with total shipped qty', suppliersWithShipments);
176
-
177
- // Matching: suppliers who have shipped something
178
- const activeSuppliers = suppliers.matching(shipments, ['sid']);
179
- printRelation('Suppliers who have made shipments', activeSuppliers);
180
-
181
- // Not matching: suppliers who haven't shipped anything
182
- const inactiveSuppliers = suppliers.not_matching(shipments, ['sid']);
183
- printRelation('Suppliers with no shipments', inactiveSuppliers);
184
-
185
- // =============================================================================
186
- // Example 5: Aggregation - summarize
187
- // =============================================================================
188
-
189
- console.log('\n*** EXAMPLE 5: Aggregation ***');
190
-
191
- // Count suppliers per city
192
- const suppliersPerCity = suppliers.summarize(['city'], {
193
- supplierCount: 'count',
194
- avgStatus: { op: 'avg', attr: 'status' },
195
- });
196
-
197
- printRelation('Suppliers per city with average status', suppliersPerCity);
198
-
199
- // Shipment statistics per supplier
200
- const shipmentStats = shipments
201
- .join(suppliers.project(['sid', 'name']), ['sid'])
202
- .summarize(['name'], {
203
- shipmentCount: 'count',
204
- totalQty: { op: 'sum', attr: 'qty' },
205
- avgQty: { op: 'avg', attr: 'qty' },
206
- minQty: { op: 'min', attr: 'qty' },
207
- maxQty: { op: 'max', attr: 'qty' },
208
- });
209
-
210
- printRelation('Shipment statistics per supplier', shipmentStats);
211
-
212
- // Grand total
213
- const grandTotal = shipments.summarize([], {
214
- totalShipments: 'count',
215
- totalQuantity: { op: 'sum', attr: 'qty' },
216
- });
217
-
218
- printRelation('Grand total', grandTotal);
219
-
220
- // =============================================================================
221
- // Example 6: Grouping and Nesting - group, image, wrap
222
- // =============================================================================
223
-
224
- console.log('\n*** EXAMPLE 6: Grouping and Nesting ***');
225
-
226
- // Group parts: nest pid and pname into a 'items' relation, grouped by color
227
- const partsByColor = parts
228
- .project(['color', 'pid', 'pname'])
229
- .group(['pid', 'pname'], 'items');
230
-
231
- printRelation('Parts grouped by color (nested items)', partsByColor);
232
-
233
- // Show expanded view of one group
234
- const redParts = partsByColor.restrict({ color: 'Red' }).one();
235
- console.log('Red parts items:', redParts.items.toArray());
236
-
237
- // Image: for each supplier, get their shipped parts
238
- const supplierParts = suppliers
239
- .project(['sid', 'name'])
240
- .image(shipments.project(['sid', 'pid', 'qty']), 'shipments', ['sid']);
241
-
242
- printRelation('Each supplier with their shipments', supplierParts);
243
-
244
- // Wrap: combine part attributes into a nested object
245
- const wrappedParts = parts.wrap(['color', 'weight'], 'specs');
246
-
247
- printRelation('Parts with wrapped specs', wrappedParts);
248
-
249
- // =============================================================================
250
- // Example 7: Complex Chained Query
251
- // =============================================================================
252
-
253
- console.log('\n*** EXAMPLE 7: Complex Chained Query ***');
254
-
255
- // Find supplier quantities by part color
256
- const supplierQtyByColor = shipments
257
- .join(parts.project(['pid', 'color']), ['pid'])
258
- .join(suppliers.project(['sid', 'name']), ['sid'])
259
- .summarize(['color', 'name'], {
260
- totalQty: { op: 'sum', attr: 'qty' },
261
- })
262
- .project(['color', 'name', 'totalQty']);
263
-
264
- printRelation('Supplier quantities by part color', supplierQtyByColor);
265
-
266
- // =============================================================================
267
- // Example 8: Data Transformation - transform, allbut
268
- // =============================================================================
269
-
270
- console.log('\n*** EXAMPLE 8: Data Transformation ***');
271
-
272
- // Transform all string values to uppercase
273
- const uppercasedSuppliers = suppliers.transform({
274
- name: (v: unknown) => (v as string).toUpperCase(),
275
- city: (v: unknown) => (v as string).toUpperCase(),
276
- });
277
-
278
- printRelation('Suppliers with uppercase names and cities', uppercasedSuppliers);
279
-
280
- // Allbut: select all attributes except some
281
- // Type: Relation<{ pid: string; pname: string; color: string; weight: number }>
282
- const partsWithoutLocation = parts.allbut(['city']);
283
-
284
- printRelation('Parts without city attribute', partsWithoutLocation);
285
-
286
- // =============================================================================
287
- // Example 9: Using .one() to extract a single tuple
288
- // =============================================================================
289
-
290
- console.log('\n*** EXAMPLE 9: Extracting Single Tuples ***');
291
-
292
- // Get a specific supplier - returns typed Supplier
293
- const smith: Supplier = suppliers.restrict({ sid: 'S1' }).one();
294
- console.log('\nSupplier S1 (Smith):', smith);
295
-
296
- // Get the total quantity shipped
297
- const total = shipments.summarize([], { total: { op: 'sum', attr: 'qty' } }).one();
298
- console.log('Total quantity shipped:', total.total);
299
-
300
- // =============================================================================
301
- // Example 10: Checking Relation Properties
302
- // =============================================================================
303
-
304
- console.log('\n*** EXAMPLE 10: Relation Properties ***');
305
-
306
- // Check if two relations are equal
307
- const parisSuppliers1 = suppliers.restrict({ city: 'Paris' });
308
- const parisSuppliers2 = suppliers.restrict((t: Supplier) => t.city === 'Paris');
309
-
310
- console.log('\nAre the two Paris supplier queries equal?', parisSuppliers1.isEqual(parisSuppliers2));
311
-
312
- // Check if something is a relation
313
- console.log('Is suppliers a relation?', Bmg.isRelation(suppliers));
314
- console.log('Is an array a relation?', Bmg.isRelation([{ a: 1 }]));
315
-
316
- console.log('\n*** ALL EXAMPLES COMPLETED ***\n');
@@ -1,16 +0,0 @@
1
- {
2
- "name": "bmg-example",
3
- "type": "module",
4
- "version": "1.0.0",
5
- "description": "Examples demonstrating Bmg.js relational algebra operations",
6
- "scripts": {
7
- "start": "tsx index.ts"
8
- },
9
- "dependencies": {
10
- "@enspirit/bmg-js": "file:.."
11
- },
12
- "devDependencies": {
13
- "tsx": "^4.7.0",
14
- "typescript": "^5.3.0"
15
- }
16
- }
@@ -1,11 +0,0 @@
1
- {
2
- "compilerOptions": {
3
- "target": "ES2020",
4
- "module": "ESNext",
5
- "moduleResolution": "bundler",
6
- "strict": true,
7
- "esModuleInterop": true,
8
- "skipLibCheck": true
9
- },
10
- "include": ["*.ts"]
11
- }
@@ -1,77 +0,0 @@
1
- /**
2
- * Utility types for type transformations in relational operators.
3
- * These enable compile-time tracking of how operators transform tuple types.
4
- */
5
-
6
- import type { Tuple } from './types';
7
-
8
- // ============================================================================
9
- // Rename Types
10
- // ============================================================================
11
-
12
- /** Map from old attribute names to new attribute names */
13
- export type RenameMap<T> = { [K in keyof T]?: string };
14
-
15
- /** Transform tuple type by renaming keys according to RenameMap */
16
- export type Renamed<T, R extends RenameMap<T>> = {
17
- [K in keyof T as K extends keyof R ? (R[K] extends string ? R[K] : K) : K]: T[K];
18
- };
19
-
20
- // ============================================================================
21
- // Prefix/Suffix Types
22
- // ============================================================================
23
-
24
- /** Prefix all keys except those in Except */
25
- export type Prefixed<T, P extends string, Except extends keyof T = never> = {
26
- [K in keyof T as K extends Except ? K : `${P}${K & string}`]: T[K];
27
- };
28
-
29
- /** Suffix all keys except those in Except */
30
- export type Suffixed<T, S extends string, Except extends keyof T = never> = {
31
- [K in keyof T as K extends Except ? K : `${K & string}${S}`]: T[K];
32
- };
33
-
34
- // ============================================================================
35
- // Join Types
36
- // ============================================================================
37
-
38
- /** Extract common keys between two tuple types */
39
- export type CommonKeys<L, R> = Extract<keyof L, keyof R>;
40
-
41
- /** Result of inner join: L & R with R's common keys removed */
42
- export type Joined<L, R> = L & Omit<R, CommonKeys<L, R>>;
43
-
44
- /** Result of left join: L & optional R attributes (common keys removed) */
45
- export type LeftJoined<L, R> = L & Partial<Omit<R, CommonKeys<L, R>>>;
46
-
47
- // ============================================================================
48
- // Wrap/Unwrap Types
49
- // ============================================================================
50
-
51
- /** Result of wrap: remove wrapped attrs, add nested object */
52
- export type Wrapped<T, K extends keyof T, As extends string> =
53
- Omit<T, K> & Record<As, Pick<T, K>>;
54
-
55
- /** Result of unwrap: remove object attr, spread its properties */
56
- export type Unwrapped<T, K extends keyof T> =
57
- T[K] extends Record<string, unknown> ? Omit<T, K> & T[K] : Omit<T, K>;
58
-
59
- // Note: Ungrouped is defined in types.ts since it references Relation
60
-
61
- // ============================================================================
62
- // Aggregator Result Types
63
- // ============================================================================
64
-
65
- /** Infer result type from aggregator specification */
66
- export type AggregatorResult<A> =
67
- A extends 'count' ? number :
68
- A extends { op: 'count' } ? number :
69
- A extends { op: 'sum' | 'avg' | 'min' | 'max' } ? number | null :
70
- A extends { op: 'collect' } ? unknown[] :
71
- A extends (tuples: Tuple[]) => infer R ? R :
72
- unknown;
73
-
74
- /** Map aggregator definitions to their result types */
75
- export type AggregatorResults<Aggs extends Record<string, unknown>> = {
76
- [K in keyof Aggs]: AggregatorResult<Aggs[K]>;
77
- };
package/tests/bmg.test.ts DELETED
@@ -1,16 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { Bmg } from 'src';
3
-
4
- describe('BMG.js', () => {
5
- it('exists', () => {
6
- expect(Bmg).not.toBe(undefined);
7
- });
8
-
9
- it('allows building relations', () => {
10
- const r = Bmg([
11
- {sid: 'S1', name: 'Smith'},
12
- {sid: 'S2', name: 'Jones'},
13
- ])
14
- expect(Bmg.isRelation(r)).toBeTruthy()
15
- })
16
- });
package/tests/fixtures.ts DELETED
@@ -1,9 +0,0 @@
1
- import { Bmg } from 'src'
2
-
3
- export const SUPPLIERS = Bmg([
4
- {sid: 'S1', name: 'Smith', status: 20, city: 'London' },
5
- {sid: 'S2', name: 'Jones', status: 10, city: 'Paris' },
6
- {sid: 'S3', name: 'Blake', status: 30, city: 'Paris' },
7
- {sid: 'S4', name: 'Clark', status: 20, city: 'London' },
8
- {sid: 'S5', name: 'Adams', status: 30, city: 'Athens' },
9
- ])
@@ -1,51 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { Bmg } from 'src';
3
- import { SUPPLIERS } from 'tests/fixtures';
4
- import { allbut, isEqual } from 'src/operators';
5
-
6
- describe('.allbut', () => {
7
-
8
- it('removes specified attributes', () => {
9
- const result = SUPPLIERS.allbut(['status', 'city']);
10
- const expected = Bmg([
11
- { sid: 'S1', name: 'Smith' },
12
- { sid: 'S2', name: 'Jones' },
13
- { sid: 'S3', name: 'Blake' },
14
- { sid: 'S4', name: 'Clark' },
15
- { sid: 'S5', name: 'Adams' },
16
- ]);
17
- expect(result.isEqual(expected)).to.be.true;
18
- })
19
-
20
- it('removes duplicates (set semantics)', () => {
21
- // Keeping only status should yield 3 unique values, not 5 tuples
22
- const result = SUPPLIERS.allbut(['sid', 'name', 'city']);
23
- const expected = Bmg([
24
- { status: 20 },
25
- { status: 10 },
26
- { status: 30 },
27
- ]);
28
- expect(result.isEqual(expected)).to.be.true;
29
- })
30
-
31
- it('keeps all attributes when excluding none', () => {
32
- const result = SUPPLIERS.allbut([]);
33
- expect(result.isEqual(SUPPLIERS)).to.be.true;
34
- })
35
-
36
- it('ignores non-existent attributes', () => {
37
- // @ts-expect-error - testing runtime behavior with invalid attribute
38
- const result = SUPPLIERS.allbut(['nonexistent']);
39
- expect(result.isEqual(SUPPLIERS)).to.be.true;
40
- })
41
-
42
- ///
43
-
44
- it('can be used standalone', () => {
45
- const input = SUPPLIERS.toArray();
46
- const res = allbut(input, ['status', 'city']);
47
- const expected = SUPPLIERS.allbut(['status', 'city']);
48
- expect(isEqual(res, expected)).to.be.true;
49
- })
50
-
51
- });
@@ -1,82 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
- import { Bmg } from 'src';
3
- import { autowrap , isEqual } from 'src/operators';
4
-
5
- describe('.autowrap', () => {
6
-
7
- it('wraps attributes based on underscore separator by default', () => {
8
- const flat = Bmg([
9
- { id: 1, address_street: '123 Main', address_city: 'NYC' },
10
- ]);
11
- const result = flat.autowrap();
12
- const tuple = result.one();
13
- expect(tuple.id).to.eql(1);
14
- expect(tuple.address).to.eql({ street: '123 Main', city: 'NYC' });
15
- })
16
-
17
- it('allows custom separator', () => {
18
- const flat = Bmg([
19
- { id: 1, 'address.street': '123 Main', 'address.city': 'NYC' },
20
- ]);
21
- const result = flat.autowrap({ separator: '.' });
22
- const tuple = result.one();
23
- expect(tuple.id).to.eql(1);
24
- expect(tuple.address).to.eql({ street: '123 Main', city: 'NYC' });
25
- })
26
-
27
- it('handles attributes without separator', () => {
28
- const flat = Bmg([
29
- { id: 1, name: 'John' },
30
- ]);
31
- const result = flat.autowrap();
32
- const tuple = result.one();
33
- expect(tuple).to.eql({ id: 1, name: 'John' });
34
- })
35
-
36
- it('handles multiple prefixes', () => {
37
- const flat = Bmg([
38
- { id: 1, home_city: 'NYC', work_city: 'Boston' },
39
- ]);
40
- const result = flat.autowrap();
41
- const tuple = result.one();
42
- expect(tuple.id).to.eql(1);
43
- expect(tuple.home).to.eql({ city: 'NYC' });
44
- expect(tuple.work).to.eql({ city: 'Boston' });
45
- })
46
-
47
- it('handles nested separators (one level only)', () => {
48
- const flat = Bmg([
49
- { id: 1, address_home_city: 'NYC' },
50
- ]);
51
- const result = flat.autowrap();
52
- const tuple = result.one();
53
- expect(tuple.id).to.eql(1);
54
- // Only first level split: address -> { home_city: 'NYC' }
55
- expect(tuple.address).to.eql({ home_city: 'NYC' });
56
- })
57
-
58
- it('autowraps multiple tuples correctly', () => {
59
- const flat = Bmg([
60
- { id: 1, address_street: '123 Main', address_city: 'NYC' },
61
- { id: 2, address_street: '456 Oak', address_city: 'Boston' },
62
- { id: 3, address_street: '789 Pine', address_city: 'Chicago' },
63
- ]);
64
- const result = flat.autowrap();
65
- const expected = Bmg([
66
- { id: 1, address: { street: '123 Main', city: 'NYC' } },
67
- { id: 2, address: { street: '456 Oak', city: 'Boston' } },
68
- { id: 3, address: { street: '789 Pine', city: 'Chicago' } },
69
- ]);
70
- expect(result.isEqual(expected)).to.be.true;
71
- })
72
-
73
- ///
74
-
75
- it('can be used standalone', () => {
76
- const input = Bmg([{ id: 1, addr_city: 'NYC' }]);
77
- const res = autowrap(input.toArray());
78
- const expected = input.autowrap();
79
- expect(isEqual(res, expected)).to.be.true;
80
- })
81
-
82
- });
@@ -1,37 +0,0 @@
1
- import { describe, it, expect } from 'vitest';
2
-
3
- import { SUPPLIERS } from 'tests/fixtures';
4
- import { constants , isEqual } from 'src/operators';
5
-
6
- describe('.constants', () => {
7
-
8
- it('adds constant attributes to all tuples', () => {
9
- const withType = SUPPLIERS.constants({type: 'supplier'});
10
- const smith = withType.restrict({sid: 'S1'}).one();
11
- expect(smith.type).to.eql('supplier');
12
- expect(smith.name).to.eql('Smith');
13
- })
14
-
15
- it('can add multiple constants', () => {
16
- const result = SUPPLIERS.constants({type: 'supplier', active: true});
17
- const jones = result.restrict({sid: 'S2'}).one();
18
- expect(jones.type).to.eql('supplier');
19
- expect(jones.active).to.eql(true);
20
- })
21
-
22
- it('overwrites existing attributes', () => {
23
- const result = SUPPLIERS.constants({city: 'Unknown'});
24
- const smith = result.restrict({sid: 'S1'}).one();
25
- expect(smith.city).to.eql('Unknown');
26
- })
27
-
28
- ///
29
-
30
- it('can be used standalone', () => {
31
- const input = SUPPLIERS.toArray();
32
- const res = constants(input, { type: 'supplier' });
33
- const expected = SUPPLIERS.constants({ type: 'supplier' });
34
- expect(isEqual(res, expected)).to.be.true;
35
- })
36
-
37
- });