@lightdash/warehouses 1.79.0 → 1.80.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/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -0
- package/dist/warehouseClientFromCredentials.d.ts +2 -1
- package/dist/warehouseClientFromCredentials.d.ts.map +1 -1
- package/dist/warehouseClientFromCredentials.js +2 -2
- package/dist/warehouseClients/DuckdbWarehouseClient.d.ts +6 -7
- package/dist/warehouseClients/DuckdbWarehouseClient.d.ts.map +1 -1
- package/dist/warehouseClients/DuckdbWarehouseClient.js +128 -14
- package/dist/warehouseClients/DuckdbWarehouseClient.test.js +307 -0
- package/dist/warehouseClients/MotherduckErrorClassifier.d.ts +3 -0
- package/dist/warehouseClients/MotherduckErrorClassifier.d.ts.map +1 -0
- package/dist/warehouseClients/MotherduckErrorClassifier.js +34 -0
- package/dist/warehouseClients/MotherduckErrorClassifier.test.d.ts +2 -0
- package/dist/warehouseClients/MotherduckErrorClassifier.test.d.ts.map +1 -0
- package/dist/warehouseClients/MotherduckErrorClassifier.test.js +31 -0
- package/dist/warehouseClients/MotherduckInstanceCache.catalog.test.d.ts +2 -0
- package/dist/warehouseClients/MotherduckInstanceCache.catalog.test.d.ts.map +1 -0
- package/dist/warehouseClients/MotherduckInstanceCache.catalog.test.js +52 -0
- package/dist/warehouseClients/MotherduckInstanceCache.d.ts +58 -0
- package/dist/warehouseClients/MotherduckInstanceCache.d.ts.map +1 -0
- package/dist/warehouseClients/MotherduckInstanceCache.integration.test.d.ts +2 -0
- package/dist/warehouseClients/MotherduckInstanceCache.integration.test.d.ts.map +1 -0
- package/dist/warehouseClients/MotherduckInstanceCache.integration.test.js +172 -0
- package/dist/warehouseClients/MotherduckInstanceCache.js +316 -0
- package/dist/warehouseClients/MotherduckInstanceCache.security.test.d.ts +2 -0
- package/dist/warehouseClients/MotherduckInstanceCache.security.test.d.ts.map +1 -0
- package/dist/warehouseClients/MotherduckInstanceCache.security.test.js +192 -0
- package/dist/warehouseClients/MotherduckInstanceCache.test.d.ts +2 -0
- package/dist/warehouseClients/MotherduckInstanceCache.test.d.ts.map +1 -0
- package/dist/warehouseClients/MotherduckInstanceCache.test.js +302 -0
- package/package.json +2 -2
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const crypto_1 = require("crypto");
|
|
4
|
+
const MotherduckInstanceCache_1 = require("./MotherduckInstanceCache");
|
|
5
|
+
const createInstanceMock = vi.fn();
|
|
6
|
+
vi.mock('@duckdb/node-api', () => ({
|
|
7
|
+
DuckDBInstance: {
|
|
8
|
+
create: (...args) => createInstanceMock(...args),
|
|
9
|
+
},
|
|
10
|
+
}));
|
|
11
|
+
const createInstance = (connect = vi.fn(async () => ({}))) => ({
|
|
12
|
+
connect,
|
|
13
|
+
closeSync: vi.fn(),
|
|
14
|
+
});
|
|
15
|
+
const configureForTesting = (overrides = {}) => (0, MotherduckInstanceCache_1.configure)({
|
|
16
|
+
idleTtlMs: 1000,
|
|
17
|
+
maxAgeMs: 10000,
|
|
18
|
+
maxEntries: 8,
|
|
19
|
+
...overrides,
|
|
20
|
+
});
|
|
21
|
+
describe('MotherduckInstanceCache', () => {
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
vi.useFakeTimers();
|
|
24
|
+
vi.clearAllMocks();
|
|
25
|
+
(0, MotherduckInstanceCache_1.resetForTesting)();
|
|
26
|
+
configureForTesting();
|
|
27
|
+
});
|
|
28
|
+
afterEach(async () => {
|
|
29
|
+
await (0, MotherduckInstanceCache_1.closeAll)('shutdown');
|
|
30
|
+
vi.useRealTimers();
|
|
31
|
+
});
|
|
32
|
+
it('shares identical connection strings and separates different tokens', async () => {
|
|
33
|
+
createInstanceMock.mockImplementation(async () => createInstance());
|
|
34
|
+
const entryIds = await Promise.all([
|
|
35
|
+
(0, MotherduckInstanceCache_1.withInstance)('md:analytics?motherduck_token=token-a', {}, async (_instance, entryId) => entryId),
|
|
36
|
+
(0, MotherduckInstanceCache_1.withInstance)('md:analytics?motherduck_token=token-a', {}, async (_instance, entryId) => entryId),
|
|
37
|
+
(0, MotherduckInstanceCache_1.withInstance)('md:analytics?motherduck_token=token-b', {}, async (_instance, entryId) => entryId),
|
|
38
|
+
]);
|
|
39
|
+
expect(createInstanceMock).toHaveBeenCalledTimes(2);
|
|
40
|
+
expect(entryIds[0]).toBe(entryIds[1]);
|
|
41
|
+
expect(entryIds[2]).not.toBe(entryIds[0]);
|
|
42
|
+
});
|
|
43
|
+
it('single-flights concurrent creation for an identical connection string', async () => {
|
|
44
|
+
let resolveCreation;
|
|
45
|
+
createInstanceMock.mockImplementation(() => new Promise((resolve) => {
|
|
46
|
+
resolveCreation = resolve;
|
|
47
|
+
}));
|
|
48
|
+
const first = (0, MotherduckInstanceCache_1.withInstance)('md:analytics?motherduck_token=token-a', {}, async (_instance, entryId) => entryId);
|
|
49
|
+
const second = (0, MotherduckInstanceCache_1.withInstance)('md:analytics?motherduck_token=token-a', {}, async (_instance, entryId) => entryId);
|
|
50
|
+
await vi.waitFor(() => expect(createInstanceMock).toHaveBeenCalledOnce());
|
|
51
|
+
resolveCreation?.(createInstance());
|
|
52
|
+
await expect(Promise.all([first, second])).resolves.toEqual([
|
|
53
|
+
expect.any(String),
|
|
54
|
+
expect.any(String),
|
|
55
|
+
]);
|
|
56
|
+
expect(await first).toBe(await second);
|
|
57
|
+
});
|
|
58
|
+
it('keeps creation duration separate from acquisition wait', async () => {
|
|
59
|
+
const events = [];
|
|
60
|
+
let resolveCreation;
|
|
61
|
+
(0, MotherduckInstanceCache_1.setObserver)((event) => events.push(event));
|
|
62
|
+
createInstanceMock.mockImplementation(() => new Promise((resolve) => {
|
|
63
|
+
resolveCreation = resolve;
|
|
64
|
+
}));
|
|
65
|
+
const acquisition = (0, MotherduckInstanceCache_1.withInstance)('md:analytics?motherduck_token=token-a', {}, async () => undefined);
|
|
66
|
+
expect(createInstanceMock).toHaveBeenCalledOnce();
|
|
67
|
+
await vi.advanceTimersByTimeAsync(25);
|
|
68
|
+
resolveCreation?.(createInstance());
|
|
69
|
+
await acquisition;
|
|
70
|
+
expect(events).toContainEqual(expect.objectContaining({
|
|
71
|
+
type: 'acquire',
|
|
72
|
+
result: 'miss',
|
|
73
|
+
waitMs: 0,
|
|
74
|
+
instanceCreateMs: 25,
|
|
75
|
+
}));
|
|
76
|
+
});
|
|
77
|
+
it('closes and replaces a creation that resolves after its connection string is invalidated', async () => {
|
|
78
|
+
const connectionString = 'md:analytics?motherduck_token=token-a&saas_mode=true';
|
|
79
|
+
const invalidatedInstance = createInstance();
|
|
80
|
+
const replacementInstance = createInstance();
|
|
81
|
+
let resolveInvalidatedCreation;
|
|
82
|
+
createInstanceMock
|
|
83
|
+
.mockImplementationOnce(() => new Promise((resolve) => {
|
|
84
|
+
resolveInvalidatedCreation = resolve;
|
|
85
|
+
}))
|
|
86
|
+
.mockResolvedValueOnce(replacementInstance);
|
|
87
|
+
const first = (0, MotherduckInstanceCache_1.withInstance)(connectionString, {}, async (_instance, entryId) => entryId);
|
|
88
|
+
const waiting = (0, MotherduckInstanceCache_1.withInstance)(connectionString, {}, async (_instance, entryId) => entryId);
|
|
89
|
+
await vi.waitFor(() => expect(createInstanceMock).toHaveBeenCalledOnce());
|
|
90
|
+
(0, MotherduckInstanceCache_1.invalidateByConnectionString)(connectionString, 'credentials_updated');
|
|
91
|
+
resolveInvalidatedCreation?.(invalidatedInstance);
|
|
92
|
+
await expect(Promise.all([first, waiting])).resolves.toEqual([
|
|
93
|
+
expect.any(String),
|
|
94
|
+
expect.any(String),
|
|
95
|
+
]);
|
|
96
|
+
expect(await first).toBe(await waiting);
|
|
97
|
+
expect(createInstanceMock).toHaveBeenCalledTimes(2);
|
|
98
|
+
expect(invalidatedInstance.closeSync).toHaveBeenCalledOnce();
|
|
99
|
+
expect(replacementInstance.closeSync).not.toHaveBeenCalled();
|
|
100
|
+
});
|
|
101
|
+
it('invalidates a cached connection string with the requested reason', async () => {
|
|
102
|
+
const connectionString = 'md:analytics?motherduck_token=token-a&saas_mode=true';
|
|
103
|
+
const events = [];
|
|
104
|
+
const firstInstance = createInstance();
|
|
105
|
+
const secondInstance = createInstance();
|
|
106
|
+
(0, MotherduckInstanceCache_1.setObserver)((event) => events.push(event));
|
|
107
|
+
createInstanceMock
|
|
108
|
+
.mockResolvedValueOnce(firstInstance)
|
|
109
|
+
.mockResolvedValueOnce(secondInstance);
|
|
110
|
+
await (0, MotherduckInstanceCache_1.withInstance)(connectionString, {}, async () => undefined);
|
|
111
|
+
(0, MotherduckInstanceCache_1.invalidateByConnectionString)(connectionString, 'credentials_updated');
|
|
112
|
+
await (0, MotherduckInstanceCache_1.withInstance)(connectionString, {}, async () => undefined);
|
|
113
|
+
expect(events).toContainEqual(expect.objectContaining({
|
|
114
|
+
type: 'evict',
|
|
115
|
+
reason: 'credentials_updated',
|
|
116
|
+
}));
|
|
117
|
+
expect(firstInstance.closeSync).toHaveBeenCalledOnce();
|
|
118
|
+
expect(createInstanceMock).toHaveBeenCalledTimes(2);
|
|
119
|
+
});
|
|
120
|
+
it.each([
|
|
121
|
+
{
|
|
122
|
+
bound: 'idle TTL',
|
|
123
|
+
options: { idleTtlMs: 10 },
|
|
124
|
+
elapsedMs: 11,
|
|
125
|
+
reason: 'idle_ttl',
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
bound: 'max age',
|
|
129
|
+
options: { maxAgeMs: 10 },
|
|
130
|
+
elapsedMs: 11,
|
|
131
|
+
reason: 'max_age',
|
|
132
|
+
},
|
|
133
|
+
])('evicts by $bound with the matching reason', async ({ options, elapsedMs, reason }) => {
|
|
134
|
+
const events = [];
|
|
135
|
+
(0, MotherduckInstanceCache_1.setObserver)((event) => events.push(event));
|
|
136
|
+
createInstanceMock.mockImplementation(async () => createInstance());
|
|
137
|
+
configureForTesting(options);
|
|
138
|
+
await (0, MotherduckInstanceCache_1.withInstance)('md:analytics?motherduck_token=token-a', {}, async () => undefined);
|
|
139
|
+
await vi.advanceTimersByTimeAsync(elapsedMs);
|
|
140
|
+
expect(events).toContainEqual(expect.objectContaining({ type: 'evict', reason }));
|
|
141
|
+
});
|
|
142
|
+
it('evicts the least recently used entry at the configured cap', async () => {
|
|
143
|
+
const events = [];
|
|
144
|
+
(0, MotherduckInstanceCache_1.setObserver)((event) => events.push(event));
|
|
145
|
+
createInstanceMock.mockImplementation(async () => createInstance());
|
|
146
|
+
configureForTesting({ maxEntries: 2 });
|
|
147
|
+
await (0, MotherduckInstanceCache_1.withInstance)('md:a?motherduck_token=a', {}, async () => undefined);
|
|
148
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
149
|
+
await (0, MotherduckInstanceCache_1.withInstance)('md:b?motherduck_token=b', {}, async () => undefined);
|
|
150
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
151
|
+
await (0, MotherduckInstanceCache_1.withInstance)('md:a?motherduck_token=a', {}, async () => undefined);
|
|
152
|
+
await vi.advanceTimersByTimeAsync(1);
|
|
153
|
+
await (0, MotherduckInstanceCache_1.withInstance)('md:c?motherduck_token=c', {}, async () => undefined);
|
|
154
|
+
expect(events).toContainEqual(expect.objectContaining({ type: 'evict', reason: 'lru' }));
|
|
155
|
+
await (0, MotherduckInstanceCache_1.withInstance)('md:b?motherduck_token=b', {}, async () => undefined);
|
|
156
|
+
expect(createInstanceMock).toHaveBeenCalledTimes(4);
|
|
157
|
+
});
|
|
158
|
+
it('unlinks an in-flight entry immediately and closes it only after release', async () => {
|
|
159
|
+
const firstInstance = createInstance();
|
|
160
|
+
const secondInstance = createInstance();
|
|
161
|
+
createInstanceMock
|
|
162
|
+
.mockResolvedValueOnce(firstInstance)
|
|
163
|
+
.mockResolvedValueOnce(secondInstance);
|
|
164
|
+
let entryId = '';
|
|
165
|
+
let releaseQuery = () => undefined;
|
|
166
|
+
const queryGate = new Promise((resolve) => {
|
|
167
|
+
releaseQuery = resolve;
|
|
168
|
+
});
|
|
169
|
+
const inFlight = (0, MotherduckInstanceCache_1.withInstance)('md:analytics?motherduck_token=token-a', {}, async (_instance, acquiredEntryId) => {
|
|
170
|
+
entryId = acquiredEntryId;
|
|
171
|
+
await queryGate;
|
|
172
|
+
});
|
|
173
|
+
await vi.waitFor(() => expect(entryId).not.toBe(''));
|
|
174
|
+
const draining = (0, MotherduckInstanceCache_1.invalidate)(entryId, 'stale');
|
|
175
|
+
await (0, MotherduckInstanceCache_1.withInstance)('md:analytics?motherduck_token=token-a', {}, async () => undefined);
|
|
176
|
+
expect(firstInstance.closeSync).not.toHaveBeenCalled();
|
|
177
|
+
expect(createInstanceMock).toHaveBeenCalledTimes(2);
|
|
178
|
+
releaseQuery();
|
|
179
|
+
await Promise.all([inFlight, draining]);
|
|
180
|
+
expect(firstInstance.closeSync).toHaveBeenCalledOnce();
|
|
181
|
+
});
|
|
182
|
+
it('evicts after three consecutive failures with the failures reason', async () => {
|
|
183
|
+
const events = [];
|
|
184
|
+
const instance = createInstance();
|
|
185
|
+
const failure = new Error('query failed');
|
|
186
|
+
(0, MotherduckInstanceCache_1.setObserver)((event) => events.push(event));
|
|
187
|
+
createInstanceMock.mockResolvedValue(instance);
|
|
188
|
+
const fail = () => (0, MotherduckInstanceCache_1.withInstance)('md:analytics?motherduck_token=token-a', {}, async () => {
|
|
189
|
+
throw failure;
|
|
190
|
+
});
|
|
191
|
+
await expect(fail()).rejects.toBe(failure);
|
|
192
|
+
await expect(fail()).rejects.toBe(failure);
|
|
193
|
+
expect(events).not.toContainEqual(expect.objectContaining({ type: 'evict', reason: 'failures' }));
|
|
194
|
+
await expect(fail()).rejects.toBe(failure);
|
|
195
|
+
expect(events).toContainEqual(expect.objectContaining({ type: 'evict', reason: 'failures' }));
|
|
196
|
+
expect(instance.closeSync).toHaveBeenCalledOnce();
|
|
197
|
+
});
|
|
198
|
+
it('resets consecutive failures after a successful callback', async () => {
|
|
199
|
+
const events = [];
|
|
200
|
+
const failure = new Error('query failed');
|
|
201
|
+
(0, MotherduckInstanceCache_1.setObserver)((event) => events.push(event));
|
|
202
|
+
createInstanceMock.mockResolvedValue(createInstance());
|
|
203
|
+
configureForTesting({ maxConsecutiveFailures: 2 });
|
|
204
|
+
const run = (shouldFail) => (0, MotherduckInstanceCache_1.withInstance)('md:analytics?motherduck_token=token-a', {}, async () => {
|
|
205
|
+
if (shouldFail) {
|
|
206
|
+
throw failure;
|
|
207
|
+
}
|
|
208
|
+
return 'ok';
|
|
209
|
+
});
|
|
210
|
+
await expect(run(true)).rejects.toBe(failure);
|
|
211
|
+
await expect(run(false)).resolves.toBe('ok');
|
|
212
|
+
await expect(run(true)).rejects.toBe(failure);
|
|
213
|
+
expect(events).not.toContainEqual(expect.objectContaining({ type: 'evict', reason: 'failures' }));
|
|
214
|
+
expect(createInstanceMock).toHaveBeenCalledOnce();
|
|
215
|
+
});
|
|
216
|
+
it('unlinks on failure without waiting for other in-flight references to drain', async () => {
|
|
217
|
+
const events = [];
|
|
218
|
+
const firstInstance = createInstance();
|
|
219
|
+
const secondInstance = createInstance();
|
|
220
|
+
const failure = new Error('query failed');
|
|
221
|
+
let blockingCallbackStarted = false;
|
|
222
|
+
let releaseBlockingCallback = () => undefined;
|
|
223
|
+
const blockingCallbackGate = new Promise((resolve) => {
|
|
224
|
+
releaseBlockingCallback = resolve;
|
|
225
|
+
});
|
|
226
|
+
(0, MotherduckInstanceCache_1.setObserver)((event) => events.push(event));
|
|
227
|
+
createInstanceMock
|
|
228
|
+
.mockResolvedValueOnce(firstInstance)
|
|
229
|
+
.mockResolvedValueOnce(secondInstance);
|
|
230
|
+
configureForTesting({ maxConsecutiveFailures: 1 });
|
|
231
|
+
const blocking = (0, MotherduckInstanceCache_1.withInstance)('md:analytics?motherduck_token=token-a', {}, async () => {
|
|
232
|
+
blockingCallbackStarted = true;
|
|
233
|
+
await blockingCallbackGate;
|
|
234
|
+
});
|
|
235
|
+
await vi.waitFor(() => expect(blockingCallbackStarted).toBe(true));
|
|
236
|
+
const failing = (0, MotherduckInstanceCache_1.withInstance)('md:analytics?motherduck_token=token-a', {}, async () => {
|
|
237
|
+
throw failure;
|
|
238
|
+
});
|
|
239
|
+
await expect(failing).rejects.toBe(failure);
|
|
240
|
+
expect(firstInstance.closeSync).not.toHaveBeenCalled();
|
|
241
|
+
expect(events).toContainEqual(expect.objectContaining({ type: 'evict', reason: 'failures' }));
|
|
242
|
+
await (0, MotherduckInstanceCache_1.withInstance)('md:analytics?motherduck_token=token-a', {}, async () => undefined);
|
|
243
|
+
expect(createInstanceMock).toHaveBeenCalledTimes(2);
|
|
244
|
+
releaseBlockingCallback();
|
|
245
|
+
await blocking;
|
|
246
|
+
expect(firstInstance.closeSync).toHaveBeenCalledOnce();
|
|
247
|
+
});
|
|
248
|
+
it('reserves newly created entries before concurrent LRU eviction can drain them', async () => {
|
|
249
|
+
const firstInstance = createInstance();
|
|
250
|
+
const secondInstance = createInstance();
|
|
251
|
+
createInstanceMock
|
|
252
|
+
.mockResolvedValueOnce(firstInstance)
|
|
253
|
+
.mockResolvedValueOnce(secondInstance);
|
|
254
|
+
configureForTesting({ maxEntries: 1 });
|
|
255
|
+
let startedCallbacks = 0;
|
|
256
|
+
let releaseQueries = () => undefined;
|
|
257
|
+
const queryGate = new Promise((resolve) => {
|
|
258
|
+
releaseQueries = resolve;
|
|
259
|
+
});
|
|
260
|
+
const first = (0, MotherduckInstanceCache_1.withInstance)('md:first?motherduck_token=first', {}, async () => {
|
|
261
|
+
expect(firstInstance.closeSync).not.toHaveBeenCalled();
|
|
262
|
+
startedCallbacks += 1;
|
|
263
|
+
await queryGate;
|
|
264
|
+
});
|
|
265
|
+
const second = (0, MotherduckInstanceCache_1.withInstance)('md:second?motherduck_token=second', {}, async () => {
|
|
266
|
+
expect(secondInstance.closeSync).not.toHaveBeenCalled();
|
|
267
|
+
startedCallbacks += 1;
|
|
268
|
+
await queryGate;
|
|
269
|
+
});
|
|
270
|
+
await vi.waitFor(() => expect(startedCallbacks).toBe(2));
|
|
271
|
+
expect(firstInstance.closeSync).not.toHaveBeenCalled();
|
|
272
|
+
releaseQueries();
|
|
273
|
+
await Promise.all([first, second]);
|
|
274
|
+
expect(firstInstance.closeSync).toHaveBeenCalledOnce();
|
|
275
|
+
});
|
|
276
|
+
it('never emits the credential-derived digest and isolates observer failures', async () => {
|
|
277
|
+
const connectionString = 'md:analytics?motherduck_token=token-a&saas_mode=true';
|
|
278
|
+
const digest = (0, crypto_1.createHash)('sha256')
|
|
279
|
+
.update(JSON.stringify({ connectionString, v: 1 }))
|
|
280
|
+
.digest('hex');
|
|
281
|
+
const events = [];
|
|
282
|
+
const observer = vi.fn((event) => {
|
|
283
|
+
events.push(event);
|
|
284
|
+
throw new Error('observer failure');
|
|
285
|
+
});
|
|
286
|
+
(0, MotherduckInstanceCache_1.setObserver)(observer);
|
|
287
|
+
createInstanceMock.mockResolvedValue(createInstance());
|
|
288
|
+
await expect((0, MotherduckInstanceCache_1.withInstance)(connectionString, { projectUuid: 'project-a' }, async () => 'ok')).resolves.toBe('ok');
|
|
289
|
+
expect(JSON.stringify(events)).not.toContain(digest);
|
|
290
|
+
expect(JSON.stringify(events)).not.toContain('token-a');
|
|
291
|
+
});
|
|
292
|
+
it('uses an unrefed interval without keepalive queries', () => {
|
|
293
|
+
const setIntervalSpy = vi.spyOn(global, 'setInterval');
|
|
294
|
+
configureForTesting();
|
|
295
|
+
const timer = setIntervalSpy.mock.results.at(-1)?.value;
|
|
296
|
+
if (!timer || typeof timer === 'number') {
|
|
297
|
+
throw new Error('Expected a Node.js interval handle');
|
|
298
|
+
}
|
|
299
|
+
expect(timer.hasRef()).toBe(false);
|
|
300
|
+
expect(createInstanceMock).not.toHaveBeenCalled();
|
|
301
|
+
});
|
|
302
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lightdash/warehouses",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.80.0",
|
|
4
4
|
"description": "Warehouse connectors for Lightdash",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
"snowflake-sdk": "2.3.4",
|
|
30
30
|
"ssh2": "1.14.0",
|
|
31
31
|
"trino-client": "0.2.9",
|
|
32
|
-
"@lightdash/common": "1.
|
|
32
|
+
"@lightdash/common": "1.80.0"
|
|
33
33
|
},
|
|
34
34
|
"devDependencies": {
|
|
35
35
|
"@types/big.js": "6.2.2",
|