@enspirit/bmg-js 1.0.2 → 1.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (194) hide show
  1. package/README.md +3 -2
  2. package/dist/AsyncRelation/Base.d.ts +47 -0
  3. package/dist/AsyncRelation/index.d.ts +25 -0
  4. package/dist/Relation/Memory.d.ts +2 -1
  5. package/dist/Relation/index.d.ts +1 -1
  6. package/dist/async/Relation/Base.d.ts +47 -0
  7. package/dist/async/Relation/index.d.ts +25 -0
  8. package/dist/async/operators/_helpers.d.ts +16 -0
  9. package/dist/async/operators/autowrap.d.ts +7 -0
  10. package/dist/async/operators/constants.d.ts +6 -0
  11. package/dist/async/operators/cross_product.d.ts +9 -0
  12. package/dist/async/operators/extend.d.ts +7 -0
  13. package/dist/async/operators/group.d.ts +7 -0
  14. package/dist/async/operators/image.d.ts +8 -0
  15. package/dist/async/operators/index.d.ts +28 -0
  16. package/dist/async/operators/intersect.d.ts +7 -0
  17. package/dist/async/operators/isEqual.d.ts +7 -0
  18. package/dist/async/operators/join.d.ts +7 -0
  19. package/dist/async/operators/left_join.d.ts +8 -0
  20. package/dist/async/operators/matching.d.ts +7 -0
  21. package/dist/async/operators/minus.d.ts +7 -0
  22. package/dist/async/operators/not_matching.d.ts +7 -0
  23. package/dist/async/operators/one.d.ts +6 -0
  24. package/dist/async/operators/prefix.d.ts +6 -0
  25. package/dist/async/operators/project.d.ts +10 -0
  26. package/dist/async/operators/rename.d.ts +6 -0
  27. package/dist/async/operators/restrict.d.ts +14 -0
  28. package/dist/async/operators/suffix.d.ts +6 -0
  29. package/dist/async/operators/summarize.d.ts +8 -0
  30. package/dist/async/operators/toArray.d.ts +5 -0
  31. package/dist/async/operators/transform.d.ts +9 -0
  32. package/dist/async/operators/ungroup.d.ts +7 -0
  33. package/dist/async/operators/union.d.ts +6 -0
  34. package/dist/async/operators/unwrap.d.ts +6 -0
  35. package/dist/async/operators/wrap.d.ts +6 -0
  36. package/dist/async/operators/yByX.d.ts +7 -0
  37. package/dist/async/types.d.ts +58 -0
  38. package/dist/async-operators/_helpers.d.ts +16 -0
  39. package/dist/async-operators/autowrap.d.ts +7 -0
  40. package/dist/async-operators/constants.d.ts +6 -0
  41. package/dist/async-operators/cross_product.d.ts +9 -0
  42. package/dist/async-operators/extend.d.ts +7 -0
  43. package/dist/async-operators/group.d.ts +7 -0
  44. package/dist/async-operators/image.d.ts +8 -0
  45. package/dist/async-operators/index.d.ts +28 -0
  46. package/dist/async-operators/intersect.d.ts +7 -0
  47. package/dist/async-operators/isEqual.d.ts +7 -0
  48. package/dist/async-operators/join.d.ts +7 -0
  49. package/dist/async-operators/left_join.d.ts +8 -0
  50. package/dist/async-operators/matching.d.ts +7 -0
  51. package/dist/async-operators/minus.d.ts +7 -0
  52. package/dist/async-operators/not_matching.d.ts +7 -0
  53. package/dist/async-operators/one.d.ts +6 -0
  54. package/dist/async-operators/prefix.d.ts +6 -0
  55. package/dist/async-operators/project.d.ts +10 -0
  56. package/dist/async-operators/rename.d.ts +6 -0
  57. package/dist/async-operators/restrict.d.ts +14 -0
  58. package/dist/async-operators/suffix.d.ts +6 -0
  59. package/dist/async-operators/summarize.d.ts +8 -0
  60. package/dist/async-operators/toArray.d.ts +5 -0
  61. package/dist/async-operators/transform.d.ts +9 -0
  62. package/dist/async-operators/ungroup.d.ts +7 -0
  63. package/dist/async-operators/union.d.ts +6 -0
  64. package/dist/async-operators/unwrap.d.ts +6 -0
  65. package/dist/async-operators/wrap.d.ts +6 -0
  66. package/dist/async-operators/yByX.d.ts +7 -0
  67. package/dist/async-types.d.ts +58 -0
  68. package/dist/async.d.ts +4 -0
  69. package/dist/bmg.cjs +1 -1
  70. package/dist/bmg.cjs.map +1 -1
  71. package/dist/bmg.modern.js +1 -1
  72. package/dist/bmg.modern.js.map +1 -1
  73. package/dist/bmg.module.js +1 -1
  74. package/dist/bmg.module.js.map +1 -1
  75. package/dist/bmg.umd.js +1 -1
  76. package/dist/bmg.umd.js.map +1 -1
  77. package/dist/index.d.ts +15 -3
  78. package/dist/lib-definitions.d.ts +1 -1
  79. package/dist/operators/index.d.ts +1 -30
  80. package/dist/operators/isEqual.d.ts +1 -2
  81. package/dist/operators/isRelation.d.ts +2 -1
  82. package/dist/sync/Relation/Memory.d.ts +46 -0
  83. package/dist/sync/Relation/index.d.ts +1 -0
  84. package/dist/sync/operators/_helpers.d.ts +142 -0
  85. package/dist/sync/operators/allbut.d.ts +2 -0
  86. package/dist/sync/operators/autowrap.d.ts +2 -0
  87. package/dist/sync/operators/constants.d.ts +2 -0
  88. package/dist/sync/operators/cross_product.d.ts +3 -0
  89. package/dist/sync/operators/exclude.d.ts +2 -0
  90. package/dist/sync/operators/extend.d.ts +2 -0
  91. package/dist/sync/operators/group.d.ts +2 -0
  92. package/dist/sync/operators/image.d.ts +2 -0
  93. package/dist/sync/operators/index.d.ts +30 -0
  94. package/dist/sync/operators/intersect.d.ts +2 -0
  95. package/dist/sync/operators/isEqual.d.ts +1 -0
  96. package/dist/sync/operators/isRelation.d.ts +2 -0
  97. package/dist/sync/operators/join.d.ts +2 -0
  98. package/dist/sync/operators/left_join.d.ts +2 -0
  99. package/dist/sync/operators/matching.d.ts +2 -0
  100. package/dist/sync/operators/minus.d.ts +2 -0
  101. package/dist/sync/operators/not_matching.d.ts +2 -0
  102. package/dist/sync/operators/one.d.ts +2 -0
  103. package/dist/sync/operators/prefix.d.ts +2 -0
  104. package/dist/sync/operators/project.d.ts +2 -0
  105. package/dist/sync/operators/rename.d.ts +2 -0
  106. package/dist/sync/operators/restrict.d.ts +2 -0
  107. package/dist/sync/operators/suffix.d.ts +2 -0
  108. package/dist/sync/operators/summarize.d.ts +2 -0
  109. package/dist/sync/operators/transform.d.ts +2 -0
  110. package/dist/sync/operators/ungroup.d.ts +2 -0
  111. package/dist/sync/operators/union.d.ts +2 -0
  112. package/dist/sync/operators/unwrap.d.ts +2 -0
  113. package/dist/sync/operators/wrap.d.ts +2 -0
  114. package/dist/sync/operators/yByX.d.ts +2 -0
  115. package/dist/types.d.ts +7 -0
  116. package/dist/writer/Text.d.ts +68 -0
  117. package/dist/writer/index.d.ts +1 -0
  118. package/package.json +1 -1
  119. package/src/Relation/index.ts +2 -1
  120. package/src/async/Relation/Base.ts +245 -0
  121. package/src/async/Relation/index.ts +31 -0
  122. package/src/async/operators/_helpers.ts +60 -0
  123. package/src/async/operators/autowrap.ts +31 -0
  124. package/src/async/operators/constants.ts +26 -0
  125. package/src/async/operators/cross_product.ts +39 -0
  126. package/src/async/operators/extend.ts +36 -0
  127. package/src/async/operators/group.ts +61 -0
  128. package/src/async/operators/image.ts +42 -0
  129. package/src/async/operators/index.ts +28 -0
  130. package/src/async/operators/intersect.ts +28 -0
  131. package/src/async/operators/isEqual.ts +39 -0
  132. package/src/async/operators/join.ts +39 -0
  133. package/src/async/operators/left_join.ts +55 -0
  134. package/src/async/operators/matching.ts +39 -0
  135. package/src/async/operators/minus.ts +28 -0
  136. package/src/async/operators/not_matching.ts +39 -0
  137. package/src/async/operators/one.ts +25 -0
  138. package/src/async/operators/prefix.ts +15 -0
  139. package/src/async/operators/project.ts +64 -0
  140. package/src/async/operators/rename.ts +33 -0
  141. package/src/async/operators/restrict.ts +61 -0
  142. package/src/async/operators/suffix.ts +15 -0
  143. package/src/async/operators/summarize.ts +90 -0
  144. package/src/async/operators/toArray.ts +18 -0
  145. package/src/async/operators/transform.ts +43 -0
  146. package/src/async/operators/ungroup.ts +43 -0
  147. package/src/async/operators/union.ts +29 -0
  148. package/src/async/operators/unwrap.ts +31 -0
  149. package/src/async/operators/wrap.ts +32 -0
  150. package/src/async/operators/yByX.ts +19 -0
  151. package/src/async/types.ts +86 -0
  152. package/src/async.ts +4 -0
  153. package/src/index.ts +17 -3
  154. package/src/lib-definitions.ts +11 -0
  155. package/src/operators/index.ts +2 -31
  156. package/src/{Relation → sync/Relation}/Memory.ts +9 -1
  157. package/src/sync/Relation/index.ts +1 -0
  158. package/src/{operators → sync/operators}/_helpers.ts +1 -1
  159. package/src/{operators → sync/operators}/allbut.ts +1 -1
  160. package/src/{operators → sync/operators}/autowrap.ts +1 -1
  161. package/src/{operators → sync/operators}/constants.ts +1 -1
  162. package/src/{operators → sync/operators}/cross_product.ts +1 -1
  163. package/src/{operators → sync/operators}/exclude.ts +2 -2
  164. package/src/{operators → sync/operators}/extend.ts +1 -1
  165. package/src/{operators → sync/operators}/group.ts +2 -2
  166. package/src/{operators → sync/operators}/image.ts +2 -2
  167. package/src/sync/operators/index.ts +31 -0
  168. package/src/{operators → sync/operators}/intersect.ts +1 -1
  169. package/src/{operators → sync/operators}/isEqual.ts +1 -2
  170. package/src/sync/operators/isRelation.ts +6 -0
  171. package/src/{operators → sync/operators}/join.ts +1 -1
  172. package/src/{operators → sync/operators}/left_join.ts +1 -1
  173. package/src/{operators → sync/operators}/matching.ts +1 -1
  174. package/src/{operators → sync/operators}/minus.ts +1 -1
  175. package/src/{operators → sync/operators}/not_matching.ts +1 -1
  176. package/src/{operators → sync/operators}/one.ts +1 -1
  177. package/src/{operators → sync/operators}/prefix.ts +1 -1
  178. package/src/{operators → sync/operators}/project.ts +1 -1
  179. package/src/{operators → sync/operators}/rename.ts +1 -1
  180. package/src/{operators → sync/operators}/restrict.ts +2 -2
  181. package/src/{operators → sync/operators}/suffix.ts +1 -1
  182. package/src/{operators → sync/operators}/summarize.ts +1 -1
  183. package/src/{operators → sync/operators}/transform.ts +1 -1
  184. package/src/{operators → sync/operators}/ungroup.ts +1 -1
  185. package/src/{operators → sync/operators}/union.ts +1 -1
  186. package/src/{operators → sync/operators}/unwrap.ts +1 -1
  187. package/src/sync/operators/where.ts +1 -0
  188. package/src/{operators → sync/operators}/wrap.ts +1 -1
  189. package/src/{operators → sync/operators}/yByX.ts +1 -1
  190. package/src/types.ts +11 -0
  191. package/src/writer/Text.ts +415 -0
  192. package/src/writer/index.ts +1 -0
  193. package/src/operators/isRelation.ts +0 -5
  194. /package/{src/operators/where.ts → dist/sync/operators/where.d.ts} +0 -0
@@ -0,0 +1,415 @@
1
+ import { Relation, Tuple } from '../types';
2
+ import { isRelation } from '../sync/operators/isRelation';
3
+
4
+ /**
5
+ * Characters used for table borders
6
+ */
7
+ export interface BorderChars {
8
+ topLeft: string;
9
+ topCenter: string;
10
+ topRight: string;
11
+ midLeft: string;
12
+ midCenter: string;
13
+ midRight: string;
14
+ bottomLeft: string;
15
+ bottomCenter: string;
16
+ bottomRight: string;
17
+ horizontal: string;
18
+ vertical: string;
19
+ }
20
+
21
+ /** ASCII border style (default) - maximum compatibility */
22
+ export const ASCII_BORDER: BorderChars = {
23
+ topLeft: '+',
24
+ topCenter: '+',
25
+ topRight: '+',
26
+ midLeft: '+',
27
+ midCenter: '+',
28
+ midRight: '+',
29
+ bottomLeft: '+',
30
+ bottomCenter: '+',
31
+ bottomRight: '+',
32
+ horizontal: '-',
33
+ vertical: '|',
34
+ };
35
+
36
+ /** Unicode single-line border style */
37
+ export const SINGLE_BORDER: BorderChars = {
38
+ topLeft: '┌',
39
+ topCenter: '┬',
40
+ topRight: '┐',
41
+ midLeft: '├',
42
+ midCenter: '┼',
43
+ midRight: '┤',
44
+ bottomLeft: '└',
45
+ bottomCenter: '┴',
46
+ bottomRight: '┘',
47
+ horizontal: '─',
48
+ vertical: '│',
49
+ };
50
+
51
+ /** Unicode double-line border style */
52
+ export const DOUBLE_BORDER: BorderChars = {
53
+ topLeft: '╔',
54
+ topCenter: '╦',
55
+ topRight: '╗',
56
+ midLeft: '╠',
57
+ midCenter: '╬',
58
+ midRight: '╣',
59
+ bottomLeft: '╚',
60
+ bottomCenter: '╩',
61
+ bottomRight: '╝',
62
+ horizontal: '═',
63
+ vertical: '║',
64
+ };
65
+
66
+ /** Unicode rounded corners border style */
67
+ export const ROUNDED_BORDER: BorderChars = {
68
+ topLeft: '╭',
69
+ topCenter: '┬',
70
+ topRight: '╮',
71
+ midLeft: '├',
72
+ midCenter: '┼',
73
+ midRight: '┤',
74
+ bottomLeft: '╰',
75
+ bottomCenter: '┴',
76
+ bottomRight: '╯',
77
+ horizontal: '─',
78
+ vertical: '│',
79
+ };
80
+
81
+ /** Border style preset names */
82
+ export type BorderStyleName = 'ascii' | 'single' | 'double' | 'rounded';
83
+
84
+ const BORDER_PRESETS: Record<BorderStyleName, BorderChars> = {
85
+ ascii: ASCII_BORDER,
86
+ single: SINGLE_BORDER,
87
+ double: DOUBLE_BORDER,
88
+ rounded: ROUNDED_BORDER,
89
+ };
90
+
91
+ /** Resolve border option to BorderChars */
92
+ const resolveBorder = (
93
+ border: BorderStyleName | BorderChars | undefined
94
+ ): BorderChars => {
95
+ if (border === undefined) return ASCII_BORDER;
96
+ if (typeof border === 'string') return BORDER_PRESETS[border];
97
+ return border;
98
+ };
99
+
100
+ /**
101
+ * Options for text rendering
102
+ */
103
+ export interface TextOptions {
104
+ /** Format string for floating point numbers (default: '%.3f' style) */
105
+ floatPrecision?: number;
106
+ /** Maximum width to trim output at */
107
+ trimAt?: number;
108
+ /** Border style: preset name or custom BorderChars object */
109
+ border?: BorderStyleName | BorderChars;
110
+ }
111
+
112
+ // Type guards
113
+ const isTupleLike = (t: unknown): t is Tuple =>
114
+ t !== null && typeof t === 'object' && !Array.isArray(t);
115
+
116
+ const isRelationLike = (r: unknown): r is Relation | Tuple[] =>
117
+ isRelation(r) || (Array.isArray(r) && r.every(isTupleLike));
118
+
119
+ /**
120
+ * Utility function to get max of two nullable numbers
121
+ */
122
+ const max = (x: number | null, y: number | null): number => {
123
+ if (x === null) return y ?? 0;
124
+ if (y === null) return x;
125
+ return x > y ? x : y;
126
+ };
127
+
128
+ /**
129
+ * Represents a single cell in the table
130
+ */
131
+ class Cell {
132
+ private value: unknown;
133
+ private renderer: TextWriter;
134
+ private _textRendering: string | null = null;
135
+ private _minWidth: number | null = null;
136
+
137
+ constructor(renderer: TextWriter, value: unknown) {
138
+ this.renderer = renderer;
139
+ this.value = value;
140
+ }
141
+
142
+ get minWidth(): number {
143
+ if (this._minWidth === null) {
144
+ this._minWidth = this.renderingLines().reduce(
145
+ (maxLen, line) => max(maxLen, line.length),
146
+ 0
147
+ );
148
+ }
149
+ return this._minWidth;
150
+ }
151
+
152
+ renderingLines(size?: number): string[] {
153
+ if (size === undefined) {
154
+ return this.textRendering.split('\n');
155
+ } else if (typeof this.value === 'number') {
156
+ // Right-align numbers
157
+ return this.renderingLines().map(l => l.padStart(size));
158
+ } else {
159
+ // Left-align other values
160
+ return this.renderingLines().map(l => l.padEnd(size));
161
+ }
162
+ }
163
+
164
+ get textRendering(): string {
165
+ if (this._textRendering === null) {
166
+ this._textRendering = this.computeTextRendering();
167
+ }
168
+ return this._textRendering;
169
+ }
170
+
171
+ private computeTextRendering(): string {
172
+ const value = this.value;
173
+
174
+ if (value === null) {
175
+ return '[null]';
176
+ }
177
+ if (value === undefined) {
178
+ return '[undefined]';
179
+ }
180
+ if (typeof value === 'symbol') {
181
+ return value.toString();
182
+ }
183
+ if (typeof value === 'number') {
184
+ if (Number.isInteger(value)) {
185
+ return String(value);
186
+ }
187
+ const precision = this.renderer.options.floatPrecision ?? 3;
188
+ return value.toFixed(precision);
189
+ }
190
+ if (isRelationLike(value)) {
191
+ return this.renderer.render(value, '');
192
+ }
193
+ if (Array.isArray(value)) {
194
+ return this.arrayRendering(value);
195
+ }
196
+ if (value instanceof Date) {
197
+ return value.toISOString();
198
+ }
199
+ if (typeof value === 'object') {
200
+ return JSON.stringify(value);
201
+ }
202
+ return String(value);
203
+ }
204
+
205
+ private arrayRendering(value: unknown[]): string {
206
+ if (value.length > 0 && isTupleLike(value[0])) {
207
+ return this.renderer.render(value as Tuple[], '');
208
+ }
209
+ if (value.length === 0) {
210
+ return '[]';
211
+ }
212
+ const values = value.map(x => {
213
+ const cell = new Cell(this.renderer, x);
214
+ return cell.textRendering;
215
+ });
216
+ const totalLength = values.reduce((sum, s) => sum + s.length, 0);
217
+ if (totalLength < 20) {
218
+ return '[' + values.join(', ') + ']';
219
+ } else {
220
+ return '[' + values.join(',\n ') + ']';
221
+ }
222
+ }
223
+ }
224
+
225
+ /**
226
+ * Represents a row of cells in the table
227
+ */
228
+ class Row {
229
+ private cells: Cell[];
230
+
231
+ constructor(renderer: TextWriter, values: unknown[]) {
232
+ this.cells = values.map(v => new Cell(renderer, v));
233
+ }
234
+
235
+ minWidths(): number[] {
236
+ return this.cells.map(cell => cell.minWidth);
237
+ }
238
+
239
+ renderingLines(sizes: number[], border: BorderChars): string[] {
240
+ const v = border.vertical;
241
+ let nbLines = 0;
242
+ const byCell = this.cells.map((cell, i) => {
243
+ const lines = cell.renderingLines(sizes[i]);
244
+ nbLines = max(nbLines, lines.length);
245
+ return lines;
246
+ });
247
+
248
+ const grid: string[] = [];
249
+ for (let lineI = 0; lineI < nbLines; lineI++) {
250
+ const lineContent = byCell
251
+ .map((cellLines, i) => cellLines[lineI] ?? ' '.repeat(sizes[i]))
252
+ .join(` ${v} `);
253
+ grid.push(`${v} ` + lineContent + ` ${v}`);
254
+ }
255
+
256
+ return grid.length === 0 ? [`${v} ${v}`] : grid;
257
+ }
258
+ }
259
+
260
+ /**
261
+ * Represents the entire table structure
262
+ */
263
+ class Table {
264
+ private renderer: TextWriter;
265
+ private border: BorderChars;
266
+ private header: Row;
267
+ private rows: Row[];
268
+ private _sizes: number[] | null = null;
269
+
270
+ constructor(renderer: TextWriter, records: unknown[][], attributes: string[]) {
271
+ this.renderer = renderer;
272
+ this.border = resolveBorder(renderer.options.border);
273
+ this.header = new Row(renderer, attributes);
274
+ this.rows = records.map(r => new Row(renderer, r));
275
+ }
276
+
277
+ get sizes(): number[] {
278
+ if (this._sizes === null) {
279
+ const headerWidths = this.header.minWidths();
280
+ this._sizes = this.rows.reduce((memo, row) => {
281
+ const rowWidths = row.minWidths();
282
+ return memo.map((w, i) => max(w, rowWidths[i] ?? 0));
283
+ }, headerWidths);
284
+ }
285
+ return this._sizes;
286
+ }
287
+
288
+ private makeSep(left: string, center: string, right: string): string {
289
+ const h = this.border.horizontal;
290
+ return left + h + this.sizes.map(s => h.repeat(s)).join(h + center + h) + h + right;
291
+ }
292
+
293
+ get topSep(): string {
294
+ return this.makeSep(this.border.topLeft, this.border.topCenter, this.border.topRight);
295
+ }
296
+
297
+ get midSep(): string {
298
+ return this.makeSep(this.border.midLeft, this.border.midCenter, this.border.midRight);
299
+ }
300
+
301
+ get bottomSep(): string {
302
+ return this.makeSep(this.border.bottomLeft, this.border.bottomCenter, this.border.bottomRight);
303
+ }
304
+
305
+ *eachLine(): Generator<string> {
306
+ const trimAt = this.renderer.options.trimAt;
307
+
308
+ if (trimAt !== undefined) {
309
+ for (const line of this.eachLineRaw()) {
310
+ yield line.substring(0, trimAt);
311
+ }
312
+ } else {
313
+ yield* this.eachLineRaw();
314
+ }
315
+ }
316
+
317
+ private *eachLineRaw(): Generator<string> {
318
+ yield this.topSep;
319
+ yield this.header.renderingLines(this.sizes, this.border)[0];
320
+ yield this.midSep;
321
+ for (const row of this.rows) {
322
+ for (const line of row.renderingLines(this.sizes, this.border)) {
323
+ yield line;
324
+ }
325
+ }
326
+ yield this.bottomSep;
327
+ }
328
+
329
+ *[Symbol.iterator](): Generator<string> {
330
+ for (const line of this.eachLine()) {
331
+ yield line.trimEnd() + '\n';
332
+ }
333
+ }
334
+
335
+ toString(): string {
336
+ let result = '';
337
+ for (const line of this) {
338
+ result += line;
339
+ }
340
+ return result;
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Text writer for rendering relations as ASCII tables
346
+ */
347
+ export class TextWriter {
348
+ readonly options: TextOptions;
349
+
350
+ constructor(options: TextOptions = {}) {
351
+ this.options = options;
352
+ }
353
+
354
+ /**
355
+ * Renders a relation or tuple array to an ASCII table string
356
+ */
357
+ render(input: Relation | Tuple[] | Tuple, output: string = ''): string {
358
+ for (const line of this.eachLine(input)) {
359
+ output += line;
360
+ }
361
+ return output;
362
+ }
363
+
364
+ *eachLine(input: Relation | Tuple[] | Tuple): Generator<string> {
365
+ // Handle single tuple
366
+ let tuples: Tuple[];
367
+ if (isTupleLike(input) && !isRelation(input)) {
368
+ tuples = [input];
369
+ } else if (isRelation(input)) {
370
+ tuples = input.toArray();
371
+ } else {
372
+ tuples = input;
373
+ }
374
+
375
+ // Collect all attribute names across all tuples
376
+ const attrs = tuples.reduce((memo: string[], tuple) => {
377
+ for (const key of Object.keys(tuple)) {
378
+ if (!memo.includes(key)) {
379
+ memo.push(key);
380
+ }
381
+ }
382
+ return memo;
383
+ }, []);
384
+
385
+ // Build records array (values in attribute order)
386
+ const records = tuples.map(t => attrs.map(a => t[a]));
387
+
388
+ const table = new Table(this, records, attrs);
389
+ yield* table;
390
+ }
391
+ }
392
+
393
+ /**
394
+ * Renders a relation as an ASCII table string
395
+ *
396
+ * @param operand - The relation or tuple array to render
397
+ * @param options - Text rendering options
398
+ * @returns ASCII table string representation
399
+ *
400
+ * @example
401
+ * const r = Bmg([{ id: 1, name: 'Alice' }, { id: 2, name: 'Bob' }]);
402
+ * console.log(toText(r));
403
+ * // +----+-------+
404
+ * // | id | name |
405
+ * // +----+-------+
406
+ * // | 1 | Alice |
407
+ * // | 2 | Bob |
408
+ * // +----+-------+
409
+ */
410
+ export const toText = (
411
+ operand: Relation | Tuple[] | Tuple,
412
+ options: TextOptions = {}
413
+ ): string => {
414
+ return new TextWriter(options).render(operand);
415
+ };
@@ -0,0 +1 @@
1
+ export * from './Text';
@@ -1,5 +0,0 @@
1
- import { MemoryRelation } from "@/Relation";
2
-
3
- export const isRelation = (op) => {
4
- return op != null && op.constructor === MemoryRelation;
5
- }