@lightdash/warehouses 2.152.0 → 2.153.0
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/dist/.tsbuildinfo +1 -1
- package/dist/warehouseClients/BigqueryWarehouseClient.d.ts +1 -1
- package/dist/warehouseClients/BigqueryWarehouseClient.d.ts.map +1 -1
- package/dist/warehouseClients/BigqueryWarehouseClient.js +68 -16
- package/dist/warehouseClients/BigqueryWarehouseClient.mock.d.ts +54 -4
- package/dist/warehouseClients/BigqueryWarehouseClient.mock.d.ts.map +1 -1
- package/dist/warehouseClients/BigqueryWarehouseClient.mock.js +28 -0
- package/dist/warehouseClients/BigqueryWarehouseClient.test.js +34 -2
- package/package.json +2 -2
|
@@ -28,7 +28,7 @@ export declare const getBigqueryTimestampDomain: (type: string | undefined) => T
|
|
|
28
28
|
type TableSchema = {
|
|
29
29
|
fields: SchemaFields[];
|
|
30
30
|
};
|
|
31
|
-
type SchemaFields = Required<Pick<bigquery.ITableFieldSchema, 'name' | 'type'
|
|
31
|
+
type SchemaFields = Required<Pick<bigquery.ITableFieldSchema, 'name' | 'type'>> & Pick<bigquery.ITableFieldSchema, 'mode' | 'fields'>;
|
|
32
32
|
type BigqueryError = {
|
|
33
33
|
errors: bigquery.IErrorProto[];
|
|
34
34
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BigqueryWarehouseClient.d.ts","sourceRoot":"","sources":["../../src/warehouseClients/BigqueryWarehouseClient.ts"],"names":[],"mappings":"AAAA,OAAO,EACH,QAAQ,EAKR,OAAO,EACP,gBAAgB,EAChB,kBAAkB,EAGlB,iBAAiB,EACpB,MAAM,wBAAwB,CAAC;AAChC,OAAO,QAAQ,MAAM,wCAAwC,CAAC;AAC9D,OAAO,EACH,OAAO,EAGP,eAAe,EACf,eAAe,EACf,6BAA6B,EAE7B,yBAAyB,EAGzB,MAAM,EAEN,eAAe,
|
|
1
|
+
{"version":3,"file":"BigqueryWarehouseClient.d.ts","sourceRoot":"","sources":["../../src/warehouseClients/BigqueryWarehouseClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -47,6 +47,34 @@ const isBigqueryDecimal = (value) => typeof value === 'object' &&
|
|
|
47
47
|
Array.isArray(value.c) &&
|
|
48
48
|
value.c.length > 0 &&
|
|
49
49
|
value.c.every((digit) => typeof digit === 'number');
|
|
50
|
+
const isBigqueryTemporal = (cell) => cell instanceof bigquery_1.BigQueryDate ||
|
|
51
|
+
cell instanceof bigquery_1.BigQueryTimestamp ||
|
|
52
|
+
cell instanceof bigquery_1.BigQueryDatetime ||
|
|
53
|
+
cell instanceof bigquery_1.BigQueryTime;
|
|
54
|
+
const isPlainObject = (value) => typeof value === 'object' &&
|
|
55
|
+
value !== null &&
|
|
56
|
+
Object.getPrototypeOf(value) === Object.prototype;
|
|
57
|
+
// STRUCT and ARRAY values arrive as plain objects/arrays whose leaves are the
|
|
58
|
+
// same SDK wrappers as top-level cells, so leaves are normalised before the
|
|
59
|
+
// whole value is serialised.
|
|
60
|
+
const normaliseNestedValue = (value) => {
|
|
61
|
+
if (Array.isArray(value)) {
|
|
62
|
+
return value.map(normaliseNestedValue);
|
|
63
|
+
}
|
|
64
|
+
if (isBigqueryTemporal(value)) {
|
|
65
|
+
return value.value;
|
|
66
|
+
}
|
|
67
|
+
if (isBigqueryDecimal(value)) {
|
|
68
|
+
return Number(value.toFixed());
|
|
69
|
+
}
|
|
70
|
+
if (isPlainObject(value)) {
|
|
71
|
+
return Object.fromEntries(Object.entries(value).map(([key, nested]) => [
|
|
72
|
+
key,
|
|
73
|
+
normaliseNestedValue(nested),
|
|
74
|
+
]));
|
|
75
|
+
}
|
|
76
|
+
return value;
|
|
77
|
+
};
|
|
50
78
|
const parseCell = (cell) => {
|
|
51
79
|
if (cell === undefined ||
|
|
52
80
|
cell === null ||
|
|
@@ -54,15 +82,15 @@ const parseCell = (cell) => {
|
|
|
54
82
|
typeof cell === 'number') {
|
|
55
83
|
return cell;
|
|
56
84
|
}
|
|
57
|
-
if (cell
|
|
58
|
-
cell instanceof bigquery_1.BigQueryTimestamp ||
|
|
59
|
-
cell instanceof bigquery_1.BigQueryDatetime ||
|
|
60
|
-
cell instanceof bigquery_1.BigQueryTime) {
|
|
85
|
+
if (isBigqueryTemporal(cell)) {
|
|
61
86
|
return new Date(cell.value);
|
|
62
87
|
}
|
|
63
88
|
if (isBigqueryDecimal(cell)) {
|
|
64
89
|
return Number(cell.toFixed());
|
|
65
90
|
}
|
|
91
|
+
if (Array.isArray(cell) || isPlainObject(cell)) {
|
|
92
|
+
return JSON.stringify(normaliseNestedValue(cell));
|
|
93
|
+
}
|
|
66
94
|
return `${cell}`;
|
|
67
95
|
};
|
|
68
96
|
const getBigqueryTimestampDomain = (type) => {
|
|
@@ -101,6 +129,28 @@ const mapFieldType = (type) => {
|
|
|
101
129
|
};
|
|
102
130
|
const isSchemaFields = (rawSchemaFields) => rawSchemaFields.every((field) => field.type && field.name);
|
|
103
131
|
const isTableSchema = (schema) => !!schema && !!schema.fields && isSchemaFields(schema.fields);
|
|
132
|
+
const BIGQUERY_REPEATED_MODE = 'REPEATED';
|
|
133
|
+
const isRecordType = (type) => type === BigqueryFieldType.RECORD || type === BigqueryFieldType.STRUCT;
|
|
134
|
+
/**
|
|
135
|
+
* Walks nested RECORD fields depth-first, emitting every node under its dotted
|
|
136
|
+
* path. A record node is kept alongside its children so the container column
|
|
137
|
+
* still resolves; the shape says whether it is a struct, an array, or both.
|
|
138
|
+
*/
|
|
139
|
+
const flattenSchemaFields = (fields, prefix = '') => fields.flatMap((field) => {
|
|
140
|
+
if (!field.name || !field.type)
|
|
141
|
+
return [];
|
|
142
|
+
const path = prefix ? `${prefix}.${field.name}` : field.name;
|
|
143
|
+
const repeated = field.mode === BIGQUERY_REPEATED_MODE;
|
|
144
|
+
const record = isRecordType(field.type);
|
|
145
|
+
const node = {
|
|
146
|
+
path,
|
|
147
|
+
type: field.type,
|
|
148
|
+
shape: repeated || record ? { repeated, record } : undefined,
|
|
149
|
+
};
|
|
150
|
+
return record
|
|
151
|
+
? [node, ...flattenSchemaFields(field.fields ?? [], path)]
|
|
152
|
+
: [node];
|
|
153
|
+
});
|
|
104
154
|
const parseRow = (row) => Object.fromEntries(Object.entries(row).map(([name, value]) => [name, parseCell(value)]));
|
|
105
155
|
class BigquerySqlBuilder extends WarehouseBaseSqlBuilder_1.default {
|
|
106
156
|
constructor() {
|
|
@@ -408,11 +458,10 @@ class BigqueryWarehouseClient extends WarehouseBaseClient_1.default {
|
|
|
408
458
|
acc[database] = acc[database] || {};
|
|
409
459
|
acc[database][schema] = acc[database][schema] || {};
|
|
410
460
|
acc[database][schema][table] = {};
|
|
411
|
-
tableSchema.fields.forEach(({
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
acc
|
|
415
|
-
(0, common_1.setCatalogTimestampDomain)(acc, database, schema, table, name, (0, exports.getBigqueryTimestampDomain)(type));
|
|
461
|
+
flattenSchemaFields(tableSchema.fields).forEach(({ path, type, shape }) => {
|
|
462
|
+
acc[database][schema][table][path] = mapFieldType(type);
|
|
463
|
+
(0, common_1.setCatalogTimestampDomain)(acc, database, schema, table, path, (0, exports.getBigqueryTimestampDomain)(type));
|
|
464
|
+
(0, common_1.setCatalogNestedColumnShape)(acc, database, schema, table, path, shape);
|
|
416
465
|
});
|
|
417
466
|
}
|
|
418
467
|
return acc;
|
|
@@ -474,17 +523,20 @@ class BigqueryWarehouseClient extends WarehouseBaseClient_1.default {
|
|
|
474
523
|
const dataset = new bigquery_1.Dataset(this.client, schema, {
|
|
475
524
|
projectId: database,
|
|
476
525
|
});
|
|
477
|
-
const
|
|
526
|
+
const [tableCatalog, tableSchema, table, metadataSchema] = await BigqueryWarehouseClient.getTableMetadata(dataset, tableName).catch((e) => {
|
|
478
527
|
this.throwIfGoogleOauthTokenError(e);
|
|
479
528
|
throw e;
|
|
480
529
|
});
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
530
|
+
const flattenedFields = flattenSchemaFields(metadataSchema.fields);
|
|
531
|
+
const catalog = this.parseWarehouseCatalog(flattenedFields.map(({ path, type }) => ({
|
|
532
|
+
table_catalog: tableCatalog,
|
|
533
|
+
table_schema: tableSchema,
|
|
534
|
+
table_name: table,
|
|
535
|
+
column_name: path,
|
|
536
|
+
data_type: type,
|
|
487
537
|
})), mapFieldType, exports.getBigqueryTimestampDomain);
|
|
538
|
+
flattenedFields.forEach(({ path, shape }) => (0, common_1.setCatalogNestedColumnShape)(catalog, tableCatalog, tableSchema, table, path, shape));
|
|
539
|
+
return catalog;
|
|
488
540
|
}
|
|
489
541
|
parseError(error, query = '') {
|
|
490
542
|
// if the error has no reason, return a generic error
|
|
@@ -9,10 +9,35 @@ export declare const getTableResponse: {
|
|
|
9
9
|
};
|
|
10
10
|
getMetadata: import("vitest").Mock<() => {
|
|
11
11
|
schema: {
|
|
12
|
-
fields: {
|
|
12
|
+
fields: ({
|
|
13
13
|
name: string;
|
|
14
14
|
type: BigqueryFieldType;
|
|
15
|
-
|
|
15
|
+
mode?: undefined;
|
|
16
|
+
fields?: undefined;
|
|
17
|
+
} | {
|
|
18
|
+
name: string;
|
|
19
|
+
type: BigqueryFieldType;
|
|
20
|
+
mode: string;
|
|
21
|
+
fields?: undefined;
|
|
22
|
+
} | {
|
|
23
|
+
name: string;
|
|
24
|
+
type: BigqueryFieldType;
|
|
25
|
+
mode: string;
|
|
26
|
+
fields: ({
|
|
27
|
+
mode?: undefined;
|
|
28
|
+
fields?: undefined;
|
|
29
|
+
name: string;
|
|
30
|
+
type: BigqueryFieldType;
|
|
31
|
+
} | {
|
|
32
|
+
name: string;
|
|
33
|
+
type: BigqueryFieldType;
|
|
34
|
+
mode: string;
|
|
35
|
+
fields: {
|
|
36
|
+
name: string;
|
|
37
|
+
type: BigqueryFieldType;
|
|
38
|
+
}[];
|
|
39
|
+
})[];
|
|
40
|
+
})[];
|
|
16
41
|
};
|
|
17
42
|
}[]>;
|
|
18
43
|
};
|
|
@@ -21,10 +46,35 @@ declare const mockStreamRow: () => Readable;
|
|
|
21
46
|
export declare const createJobResponse: {
|
|
22
47
|
getQueryResults: import("vitest").Mock<() => (Record<string, any>[] | {
|
|
23
48
|
schema: {
|
|
24
|
-
fields: {
|
|
49
|
+
fields: ({
|
|
50
|
+
name: string;
|
|
51
|
+
type: BigqueryFieldType;
|
|
52
|
+
mode?: undefined;
|
|
53
|
+
fields?: undefined;
|
|
54
|
+
} | {
|
|
55
|
+
name: string;
|
|
56
|
+
type: BigqueryFieldType;
|
|
57
|
+
mode: string;
|
|
58
|
+
fields?: undefined;
|
|
59
|
+
} | {
|
|
25
60
|
name: string;
|
|
26
61
|
type: BigqueryFieldType;
|
|
27
|
-
|
|
62
|
+
mode: string;
|
|
63
|
+
fields: ({
|
|
64
|
+
mode?: undefined;
|
|
65
|
+
fields?: undefined;
|
|
66
|
+
name: string;
|
|
67
|
+
type: BigqueryFieldType;
|
|
68
|
+
} | {
|
|
69
|
+
name: string;
|
|
70
|
+
type: BigqueryFieldType;
|
|
71
|
+
mode: string;
|
|
72
|
+
fields: {
|
|
73
|
+
name: string;
|
|
74
|
+
type: BigqueryFieldType;
|
|
75
|
+
}[];
|
|
76
|
+
})[];
|
|
77
|
+
})[];
|
|
28
78
|
};
|
|
29
79
|
} | undefined)[]>;
|
|
30
80
|
getQueryResultsStream: import("vitest").Mock<typeof mockStreamRow>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BigqueryWarehouseClient.mock.d.ts","sourceRoot":"","sources":["../../src/warehouseClients/BigqueryWarehouseClient.mock.ts"],"names":[],"mappings":"AAKA,OAAO,EACH,OAAO,EACP,yBAAyB,EAE5B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,eAAO,MAAM,WAAW,EAAE,yBAUzB,CAAC;
|
|
1
|
+
{"version":3,"file":"BigqueryWarehouseClient.mock.d.ts","sourceRoot":"","sources":["../../src/warehouseClients/BigqueryWarehouseClient.mock.ts"],"names":[],"mappings":"AAKA,OAAO,EACH,OAAO,EACP,yBAAyB,EAE5B,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAAE,QAAQ,EAAE,MAAM,QAAQ,CAAC;AAClC,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAE9D,eAAO,MAAM,WAAW,EAAE,yBAUzB,CAAC;AA+DF,eAAO,MAAM,gBAAgB;IACzB,EAAE;IACF,QAAQ;QAAI,SAAS;;IACrB,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CACd,CAAC;AAwBF,eAAO,MAAM,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAgBzC,CAAC;AAEF,QAAA,MAAM,aAAa,gBAOb,CAAC;AAEP,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAK7B,CAAC"}
|
|
@@ -51,6 +51,28 @@ const metadata = {
|
|
|
51
51
|
name: 'myObjectColumn',
|
|
52
52
|
type: BigqueryWarehouseClient_1.BigqueryFieldType.STRUCT,
|
|
53
53
|
},
|
|
54
|
+
{
|
|
55
|
+
name: 'myRepeatedColumn',
|
|
56
|
+
type: BigqueryWarehouseClient_1.BigqueryFieldType.STRING,
|
|
57
|
+
mode: 'REPEATED',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
name: 'myRecordColumn',
|
|
61
|
+
type: BigqueryWarehouseClient_1.BigqueryFieldType.RECORD,
|
|
62
|
+
mode: 'NULLABLE',
|
|
63
|
+
fields: [
|
|
64
|
+
{ name: 'id', type: BigqueryWarehouseClient_1.BigqueryFieldType.INT64 },
|
|
65
|
+
{ name: 'createdAt', type: BigqueryWarehouseClient_1.BigqueryFieldType.TIMESTAMP },
|
|
66
|
+
{
|
|
67
|
+
name: 'tags',
|
|
68
|
+
type: BigqueryWarehouseClient_1.BigqueryFieldType.RECORD,
|
|
69
|
+
mode: 'REPEATED',
|
|
70
|
+
fields: [
|
|
71
|
+
{ name: 'label', type: BigqueryWarehouseClient_1.BigqueryFieldType.STRING },
|
|
72
|
+
],
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
},
|
|
54
76
|
],
|
|
55
77
|
},
|
|
56
78
|
};
|
|
@@ -85,6 +107,12 @@ exports.rows = [
|
|
|
85
107
|
myBooleanColumn: false,
|
|
86
108
|
myArrayColumn: ['1', '2', '3'],
|
|
87
109
|
myObjectColumn: { test: '1', toFixed: () => '123' },
|
|
110
|
+
myRepeatedColumn: ['a', 'b'],
|
|
111
|
+
myRecordColumn: {
|
|
112
|
+
id: 7,
|
|
113
|
+
createdAt: new bigquery_1.BigQueryTimestamp('1990-03-02T08:30:00.010Z'),
|
|
114
|
+
tags: [{ label: 'x' }, { label: 'y' }],
|
|
115
|
+
},
|
|
88
116
|
},
|
|
89
117
|
];
|
|
90
118
|
const mockStreamRow = () => new stream_1.Readable({
|
|
@@ -13,11 +13,18 @@ describe('BigqueryWarehouseClient', () => {
|
|
|
13
13
|
expect(results.fields).toEqual({
|
|
14
14
|
...WarehouseClient_mock_1.expectedFields,
|
|
15
15
|
myBigNumberColumn: { type: 'number' },
|
|
16
|
+
myRepeatedColumn: { type: 'string' },
|
|
17
|
+
myRecordColumn: { type: 'string' },
|
|
16
18
|
});
|
|
17
19
|
expect(results.rows[0]).toEqual({
|
|
18
20
|
...WarehouseClient_mock_1.expectedRow,
|
|
19
21
|
myNumberColumn: 100.25,
|
|
20
22
|
myBigNumberColumn: 200.5,
|
|
23
|
+
// BigQuery serialises nested values as JSON; other clients pass them through
|
|
24
|
+
myArrayColumn: '["1","2","3"]',
|
|
25
|
+
myObjectColumn: '{"test":"1"}',
|
|
26
|
+
myRepeatedColumn: '["a","b"]',
|
|
27
|
+
myRecordColumn: '{"id":7,"createdAt":"1990-03-02T08:30:00.010Z","tags":[{"label":"x"},{"label":"y"}]}',
|
|
21
28
|
});
|
|
22
29
|
expect(warehouse.client.createQueryJob).toHaveBeenCalledTimes(1);
|
|
23
30
|
});
|
|
@@ -46,12 +53,37 @@ describe('BigqueryWarehouseClient', () => {
|
|
|
46
53
|
bigquery_1.Dataset.prototype.table = getTableMock;
|
|
47
54
|
const warehouse = new BigqueryWarehouseClient_1.BigqueryWarehouseClient(BigqueryWarehouseClient_mock_1.credentials);
|
|
48
55
|
const expectedCatalog = structuredClone(WarehouseClient_mock_1.expectedWarehouseSchemaWithAwareTimestamp);
|
|
49
|
-
expectedCatalog.myDatabase.mySchema.myTable
|
|
50
|
-
common_1.DimensionType.NUMBER
|
|
56
|
+
Object.assign(expectedCatalog.myDatabase.mySchema.myTable, {
|
|
57
|
+
myBigNumberColumn: common_1.DimensionType.NUMBER,
|
|
58
|
+
myRepeatedColumn: common_1.DimensionType.STRING,
|
|
59
|
+
myRecordColumn: common_1.DimensionType.STRING,
|
|
60
|
+
'myRecordColumn.id': common_1.DimensionType.NUMBER,
|
|
61
|
+
'myRecordColumn.createdAt': common_1.DimensionType.TIMESTAMP,
|
|
62
|
+
'myRecordColumn.tags': common_1.DimensionType.STRING,
|
|
63
|
+
'myRecordColumn.tags.label': common_1.DimensionType.STRING,
|
|
64
|
+
});
|
|
65
|
+
(0, common_1.setCatalogTimestampDomain)(expectedCatalog, 'myDatabase', 'mySchema', 'myTable', 'myRecordColumn.createdAt', 'aware');
|
|
66
|
+
const nestedShapes = {
|
|
67
|
+
myObjectColumn: { repeated: false, record: true },
|
|
68
|
+
myRepeatedColumn: { repeated: true, record: false },
|
|
69
|
+
myRecordColumn: { repeated: false, record: true },
|
|
70
|
+
'myRecordColumn.tags': { repeated: true, record: true },
|
|
71
|
+
};
|
|
72
|
+
Object.entries(nestedShapes).forEach(([path, shape]) => (0, common_1.setCatalogNestedColumnShape)(expectedCatalog, 'myDatabase', 'mySchema', 'myTable', path, shape));
|
|
51
73
|
expect(await warehouse.getCatalog(WarehouseClient_mock_1.config)).toEqual(expectedCatalog);
|
|
52
74
|
expect(getTableMock).toHaveBeenCalledTimes(1);
|
|
53
75
|
expect(BigqueryWarehouseClient_mock_1.getTableResponse.getMetadata).toHaveBeenCalledTimes(1);
|
|
54
76
|
});
|
|
77
|
+
it('expect getFields to flatten nested columns with the same shapes as getCatalog', async () => {
|
|
78
|
+
bigquery_1.Dataset.prototype.table = vi
|
|
79
|
+
.fn()
|
|
80
|
+
.mockImplementationOnce(() => BigqueryWarehouseClient_mock_1.getTableResponse);
|
|
81
|
+
const warehouse = new BigqueryWarehouseClient_1.BigqueryWarehouseClient(BigqueryWarehouseClient_mock_1.credentials);
|
|
82
|
+
const fields = await warehouse.getFields('myTable', 'mySchema', 'myDatabase');
|
|
83
|
+
expect(fields.myDatabase.mySchema.myTable['myRecordColumn.id']).toBe(common_1.DimensionType.NUMBER);
|
|
84
|
+
expect((0, common_1.getCatalogNestedColumnShape)(fields, 'myDatabase', 'mySchema', 'myTable', 'myRecordColumn.tags')).toEqual({ repeated: true, record: true });
|
|
85
|
+
expect((0, common_1.getCatalogNestedColumnShape)(fields, 'myDatabase', 'mySchema', 'myTable', 'myStringColumn')).toBeUndefined();
|
|
86
|
+
});
|
|
55
87
|
});
|
|
56
88
|
describe('BigqueryWarehouseClient.getDatabases', () => {
|
|
57
89
|
const createDataset = (datasetId) => new bigquery_1.Dataset(new bigquery_1.BigQuery({ projectId: 'test-project' }), datasetId, {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightdash/warehouses",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.153.0",
|
|
4
4
|
"description": "Warehouse connectors for Lightdash",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"snowflake-sdk": "2.3.4",
|
|
29
29
|
"ssh2": "1.14.0",
|
|
30
30
|
"trino-client": "0.2.9",
|
|
31
|
-
"@lightdash/common": "2.
|
|
31
|
+
"@lightdash/common": "2.153.0"
|
|
32
32
|
},
|
|
33
33
|
"devDependencies": {
|
|
34
34
|
"oxlint": "1.75.0",
|