@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.
- package/README.md +3 -2
- package/dist/AsyncRelation/Base.d.ts +47 -0
- package/dist/AsyncRelation/index.d.ts +25 -0
- package/dist/Relation/Memory.d.ts +2 -1
- package/dist/Relation/index.d.ts +1 -1
- package/dist/async/Relation/Base.d.ts +47 -0
- package/dist/async/Relation/index.d.ts +25 -0
- package/dist/async/operators/_helpers.d.ts +16 -0
- package/dist/async/operators/autowrap.d.ts +7 -0
- package/dist/async/operators/constants.d.ts +6 -0
- package/dist/async/operators/cross_product.d.ts +9 -0
- package/dist/async/operators/extend.d.ts +7 -0
- package/dist/async/operators/group.d.ts +7 -0
- package/dist/async/operators/image.d.ts +8 -0
- package/dist/async/operators/index.d.ts +28 -0
- package/dist/async/operators/intersect.d.ts +7 -0
- package/dist/async/operators/isEqual.d.ts +7 -0
- package/dist/async/operators/join.d.ts +7 -0
- package/dist/async/operators/left_join.d.ts +8 -0
- package/dist/async/operators/matching.d.ts +7 -0
- package/dist/async/operators/minus.d.ts +7 -0
- package/dist/async/operators/not_matching.d.ts +7 -0
- package/dist/async/operators/one.d.ts +6 -0
- package/dist/async/operators/prefix.d.ts +6 -0
- package/dist/async/operators/project.d.ts +10 -0
- package/dist/async/operators/rename.d.ts +6 -0
- package/dist/async/operators/restrict.d.ts +14 -0
- package/dist/async/operators/suffix.d.ts +6 -0
- package/dist/async/operators/summarize.d.ts +8 -0
- package/dist/async/operators/toArray.d.ts +5 -0
- package/dist/async/operators/transform.d.ts +9 -0
- package/dist/async/operators/ungroup.d.ts +7 -0
- package/dist/async/operators/union.d.ts +6 -0
- package/dist/async/operators/unwrap.d.ts +6 -0
- package/dist/async/operators/wrap.d.ts +6 -0
- package/dist/async/operators/yByX.d.ts +7 -0
- package/dist/async/types.d.ts +58 -0
- package/dist/async-operators/_helpers.d.ts +16 -0
- package/dist/async-operators/autowrap.d.ts +7 -0
- package/dist/async-operators/constants.d.ts +6 -0
- package/dist/async-operators/cross_product.d.ts +9 -0
- package/dist/async-operators/extend.d.ts +7 -0
- package/dist/async-operators/group.d.ts +7 -0
- package/dist/async-operators/image.d.ts +8 -0
- package/dist/async-operators/index.d.ts +28 -0
- package/dist/async-operators/intersect.d.ts +7 -0
- package/dist/async-operators/isEqual.d.ts +7 -0
- package/dist/async-operators/join.d.ts +7 -0
- package/dist/async-operators/left_join.d.ts +8 -0
- package/dist/async-operators/matching.d.ts +7 -0
- package/dist/async-operators/minus.d.ts +7 -0
- package/dist/async-operators/not_matching.d.ts +7 -0
- package/dist/async-operators/one.d.ts +6 -0
- package/dist/async-operators/prefix.d.ts +6 -0
- package/dist/async-operators/project.d.ts +10 -0
- package/dist/async-operators/rename.d.ts +6 -0
- package/dist/async-operators/restrict.d.ts +14 -0
- package/dist/async-operators/suffix.d.ts +6 -0
- package/dist/async-operators/summarize.d.ts +8 -0
- package/dist/async-operators/toArray.d.ts +5 -0
- package/dist/async-operators/transform.d.ts +9 -0
- package/dist/async-operators/ungroup.d.ts +7 -0
- package/dist/async-operators/union.d.ts +6 -0
- package/dist/async-operators/unwrap.d.ts +6 -0
- package/dist/async-operators/wrap.d.ts +6 -0
- package/dist/async-operators/yByX.d.ts +7 -0
- package/dist/async-types.d.ts +58 -0
- package/dist/async.d.ts +4 -0
- package/dist/bmg.cjs +1 -1
- package/dist/bmg.cjs.map +1 -1
- package/dist/bmg.modern.js +1 -1
- package/dist/bmg.modern.js.map +1 -1
- package/dist/bmg.module.js +1 -1
- package/dist/bmg.module.js.map +1 -1
- package/dist/bmg.umd.js +1 -1
- package/dist/bmg.umd.js.map +1 -1
- package/dist/index.d.ts +15 -3
- package/dist/lib-definitions.d.ts +1 -1
- package/dist/operators/index.d.ts +1 -30
- package/dist/operators/isEqual.d.ts +1 -2
- package/dist/operators/isRelation.d.ts +2 -1
- package/dist/sync/Relation/Memory.d.ts +46 -0
- package/dist/sync/Relation/index.d.ts +1 -0
- package/dist/sync/operators/_helpers.d.ts +142 -0
- package/dist/sync/operators/allbut.d.ts +2 -0
- package/dist/sync/operators/autowrap.d.ts +2 -0
- package/dist/sync/operators/constants.d.ts +2 -0
- package/dist/sync/operators/cross_product.d.ts +3 -0
- package/dist/sync/operators/exclude.d.ts +2 -0
- package/dist/sync/operators/extend.d.ts +2 -0
- package/dist/sync/operators/group.d.ts +2 -0
- package/dist/sync/operators/image.d.ts +2 -0
- package/dist/sync/operators/index.d.ts +30 -0
- package/dist/sync/operators/intersect.d.ts +2 -0
- package/dist/sync/operators/isEqual.d.ts +1 -0
- package/dist/sync/operators/isRelation.d.ts +2 -0
- package/dist/sync/operators/join.d.ts +2 -0
- package/dist/sync/operators/left_join.d.ts +2 -0
- package/dist/sync/operators/matching.d.ts +2 -0
- package/dist/sync/operators/minus.d.ts +2 -0
- package/dist/sync/operators/not_matching.d.ts +2 -0
- package/dist/sync/operators/one.d.ts +2 -0
- package/dist/sync/operators/prefix.d.ts +2 -0
- package/dist/sync/operators/project.d.ts +2 -0
- package/dist/sync/operators/rename.d.ts +2 -0
- package/dist/sync/operators/restrict.d.ts +2 -0
- package/dist/sync/operators/suffix.d.ts +2 -0
- package/dist/sync/operators/summarize.d.ts +2 -0
- package/dist/sync/operators/transform.d.ts +2 -0
- package/dist/sync/operators/ungroup.d.ts +2 -0
- package/dist/sync/operators/union.d.ts +2 -0
- package/dist/sync/operators/unwrap.d.ts +2 -0
- package/dist/sync/operators/wrap.d.ts +2 -0
- package/dist/sync/operators/yByX.d.ts +2 -0
- package/dist/types.d.ts +7 -0
- package/dist/writer/Text.d.ts +68 -0
- package/dist/writer/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/Relation/index.ts +2 -1
- package/src/async/Relation/Base.ts +245 -0
- package/src/async/Relation/index.ts +31 -0
- package/src/async/operators/_helpers.ts +60 -0
- package/src/async/operators/autowrap.ts +31 -0
- package/src/async/operators/constants.ts +26 -0
- package/src/async/operators/cross_product.ts +39 -0
- package/src/async/operators/extend.ts +36 -0
- package/src/async/operators/group.ts +61 -0
- package/src/async/operators/image.ts +42 -0
- package/src/async/operators/index.ts +28 -0
- package/src/async/operators/intersect.ts +28 -0
- package/src/async/operators/isEqual.ts +39 -0
- package/src/async/operators/join.ts +39 -0
- package/src/async/operators/left_join.ts +55 -0
- package/src/async/operators/matching.ts +39 -0
- package/src/async/operators/minus.ts +28 -0
- package/src/async/operators/not_matching.ts +39 -0
- package/src/async/operators/one.ts +25 -0
- package/src/async/operators/prefix.ts +15 -0
- package/src/async/operators/project.ts +64 -0
- package/src/async/operators/rename.ts +33 -0
- package/src/async/operators/restrict.ts +61 -0
- package/src/async/operators/suffix.ts +15 -0
- package/src/async/operators/summarize.ts +90 -0
- package/src/async/operators/toArray.ts +18 -0
- package/src/async/operators/transform.ts +43 -0
- package/src/async/operators/ungroup.ts +43 -0
- package/src/async/operators/union.ts +29 -0
- package/src/async/operators/unwrap.ts +31 -0
- package/src/async/operators/wrap.ts +32 -0
- package/src/async/operators/yByX.ts +19 -0
- package/src/async/types.ts +86 -0
- package/src/async.ts +4 -0
- package/src/index.ts +17 -3
- package/src/lib-definitions.ts +11 -0
- package/src/operators/index.ts +2 -31
- package/src/{Relation → sync/Relation}/Memory.ts +9 -1
- package/src/sync/Relation/index.ts +1 -0
- package/src/{operators → sync/operators}/_helpers.ts +1 -1
- package/src/{operators → sync/operators}/allbut.ts +1 -1
- package/src/{operators → sync/operators}/autowrap.ts +1 -1
- package/src/{operators → sync/operators}/constants.ts +1 -1
- package/src/{operators → sync/operators}/cross_product.ts +1 -1
- package/src/{operators → sync/operators}/exclude.ts +2 -2
- package/src/{operators → sync/operators}/extend.ts +1 -1
- package/src/{operators → sync/operators}/group.ts +2 -2
- package/src/{operators → sync/operators}/image.ts +2 -2
- package/src/sync/operators/index.ts +31 -0
- package/src/{operators → sync/operators}/intersect.ts +1 -1
- package/src/{operators → sync/operators}/isEqual.ts +1 -2
- package/src/sync/operators/isRelation.ts +6 -0
- package/src/{operators → sync/operators}/join.ts +1 -1
- package/src/{operators → sync/operators}/left_join.ts +1 -1
- package/src/{operators → sync/operators}/matching.ts +1 -1
- package/src/{operators → sync/operators}/minus.ts +1 -1
- package/src/{operators → sync/operators}/not_matching.ts +1 -1
- package/src/{operators → sync/operators}/one.ts +1 -1
- package/src/{operators → sync/operators}/prefix.ts +1 -1
- package/src/{operators → sync/operators}/project.ts +1 -1
- package/src/{operators → sync/operators}/rename.ts +1 -1
- package/src/{operators → sync/operators}/restrict.ts +2 -2
- package/src/{operators → sync/operators}/suffix.ts +1 -1
- package/src/{operators → sync/operators}/summarize.ts +1 -1
- package/src/{operators → sync/operators}/transform.ts +1 -1
- package/src/{operators → sync/operators}/ungroup.ts +1 -1
- package/src/{operators → sync/operators}/union.ts +1 -1
- package/src/{operators → sync/operators}/unwrap.ts +1 -1
- package/src/sync/operators/where.ts +1 -0
- package/src/{operators → sync/operators}/wrap.ts +1 -1
- package/src/{operators → sync/operators}/yByX.ts +1 -1
- package/src/types.ts +11 -0
- package/src/writer/Text.ts +415 -0
- package/src/writer/index.ts +1 -0
- package/src/operators/isRelation.ts +0 -5
- /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';
|
|
File without changes
|