@adecore/database 0.39.0 → 0.40.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/console/QueryConsole.js +2 -10
- package/dist/console/SchemaPicker.js +0 -1
- package/dist/console/StatementResultView.js +1 -1
- package/dist/ddl/diff.js +1 -1
- package/dist/explorer/tree.js +0 -1
- package/dist/export-formats.d.ts +2 -0
- package/dist/export-formats.js +1 -0
- package/dist/grid/CopyAsMenu.js +0 -1
- package/dist/grid/DataGrid.js +10 -10
- package/dist/grid/column-layout.d.ts +0 -1
- package/dist/grid/column-layout.js +1 -2
- package/dist/grid/column-view.js +3 -2
- package/dist/grid/display.js +1 -2
- package/dist/host/host.js +1 -2
- package/dist/host/validate.d.ts +3 -0
- package/dist/host/validate.js +18 -15
- package/dist/same-list.d.ts +1 -0
- package/dist/same-list.js +1 -0
- package/dist/stored-size.d.ts +1 -0
- package/dist/stored-size.js +10 -0
- package/dist/table/TableToolbar.js +1 -1
- package/dist/table/TableView.js +2 -10
- package/package.json +15 -15
- package/src/DatabaseProvider.tsx +2 -0
- package/src/KeyIcon.tsx +1 -0
- package/src/client/client.ts +13 -0
- package/src/client-context.ts +2 -0
- package/src/connections/ConnectionForm.tsx +11 -0
- package/src/connections/ConnectionManager.tsx +9 -0
- package/src/connections/connection-config.ts +21 -0
- package/src/connections/docker.ts +2 -0
- package/src/connections/use-docker-containers.ts +3 -0
- package/src/console/DestructiveDialog.tsx +1 -0
- package/src/console/QueryConsole.tsx +38 -10
- package/src/console/SchemaPicker.tsx +1 -1
- package/src/console/SqlEditor.tsx +10 -0
- package/src/console/StatementResultView.tsx +7 -2
- package/src/console/TransactionControls.tsx +1 -0
- package/src/console/destructive.ts +11 -0
- package/src/console/editing.ts +10 -0
- package/src/console/history.ts +8 -0
- package/src/console/labels.ts +1 -0
- package/src/console/outcome.ts +1 -0
- package/src/console/result-keys.ts +16 -0
- package/src/console/shortcuts.ts +2 -0
- package/src/ddl/alter-mysql.ts +16 -0
- package/src/ddl/alter-sqlite.ts +21 -0
- package/src/ddl/alter.ts +2 -0
- package/src/ddl/column.ts +11 -0
- package/src/ddl/create.ts +15 -0
- package/src/ddl/dialect.ts +4 -0
- package/src/ddl/diff.ts +22 -2
- package/src/ddl/draft.ts +1 -0
- package/src/ddl/parse.ts +31 -0
- package/src/ddl/types.ts +3 -0
- package/src/ddl/validate.ts +12 -0
- package/src/designer/ColumnsEditor.tsx +3 -0
- package/src/designer/DesignerView.tsx +2 -0
- package/src/designer/ForeignKeysEditor.tsx +4 -0
- package/src/designer/IndexesEditor.tsx +1 -0
- package/src/designer/NamePicker.tsx +2 -0
- package/src/designer/OptionsEditor.tsx +4 -0
- package/src/designer/SqlPreview.tsx +2 -0
- package/src/designer/TableDesigner.tsx +14 -0
- package/src/designer/edit.ts +13 -0
- package/src/designer/plan.ts +3 -0
- package/src/designer/recover.ts +1 -0
- package/src/designer/useReferenceColumns.ts +5 -0
- package/src/explorer/DatabaseExplorer.tsx +40 -0
- package/src/explorer/RowMenu.tsx +2 -0
- package/src/explorer/TableDialog.tsx +6 -0
- package/src/explorer/expansion.ts +8 -0
- package/src/explorer/tree.ts +73 -2
- package/src/explorer/use-explorer-loads.ts +15 -0
- package/src/explorer/use-stored-expansion.ts +4 -0
- package/src/export-formats.ts +3 -0
- package/src/grid/CellEditor.tsx +1 -0
- package/src/grid/CopyAsMenu.tsx +1 -1
- package/src/grid/DataGrid.tsx +93 -23
- package/src/grid/EnumPicker.tsx +6 -0
- package/src/grid/GridHeaderCell.tsx +11 -0
- package/src/grid/GridRowView.tsx +3 -0
- package/src/grid/RecordView.tsx +19 -0
- package/src/grid/ValueDock.tsx +2 -0
- package/src/grid/aggregates.ts +12 -0
- package/src/grid/column-layout.ts +12 -4
- package/src/grid/column-selection.ts +9 -0
- package/src/grid/column-view.ts +6 -2
- package/src/grid/copy-formats.ts +13 -0
- package/src/grid/display.ts +17 -3
- package/src/grid/edit-value.ts +11 -0
- package/src/grid/enum-type.ts +15 -0
- package/src/grid/focused-value.ts +4 -0
- package/src/grid/layout.ts +12 -0
- package/src/grid/navigation.ts +5 -0
- package/src/grid/row-selection.ts +7 -0
- package/src/grid/sort.ts +1 -0
- package/src/grid/whole-cells.ts +8 -0
- package/src/host/host.ts +16 -3
- package/src/host/spawn.ts +2 -0
- package/src/host/validate.ts +37 -15
- package/src/same-list.ts +2 -0
- package/src/sql-split.ts +28 -0
- package/src/sql.ts +20 -0
- package/src/stored-size.ts +10 -0
- package/src/structure/StructureView.tsx +18 -0
- package/src/table/CommandField.tsx +11 -0
- package/src/table/ImportDialog.tsx +4 -0
- package/src/table/TableStatusBar.tsx +1 -0
- package/src/table/TableToolbar.tsx +2 -2
- package/src/table/TableView.tsx +50 -10
- package/src/table/command-field.ts +40 -0
- package/src/table/grid-rows.ts +13 -0
- package/src/table/import-mapping.ts +5 -0
- package/src/table/layout-store.ts +7 -0
- package/src/table/paging.ts +1 -0
- package/src/table/pending.ts +18 -0
- package/src/table/references.ts +4 -0
- package/src/table/useLoaded.ts +3 -0
- package/src/table/useStoredLayout.ts +2 -0
- package/src/table/useTableTransfer.ts +17 -1
- package/src/testing/fake.ts +24 -0
- package/src/use-database-resources.ts +1 -0
- package/src/use-popup-press.ts +3 -0
- package/src/value/ValueEditor.tsx +6 -0
- package/src/value/draft.ts +7 -0
- package/src/value/hex.ts +8 -0
- package/src/value/json.ts +16 -0
- package/src/value/views.ts +14 -0
- package/src/workbench/DatabaseWorkbench.tsx +22 -0
- package/src/workbench/tabs.ts +34 -0
|
@@ -8,16 +8,20 @@ export const wholeValueOf = (cell: Shown): Value | undefined =>
|
|
|
8
8
|
/* What a cell holds as far as it goes: the whole value, or the preview of a long text or a binary value. */
|
|
9
9
|
export const previewValueOf = (cell: Shown): Value => {
|
|
10
10
|
const whole = wholeValueOf(cell);
|
|
11
|
+
|
|
11
12
|
if (whole !== undefined) {
|
|
12
13
|
return whole;
|
|
13
14
|
}
|
|
15
|
+
|
|
14
16
|
if (cell !== null && typeof cell === 'object') {
|
|
15
17
|
if (cell.kind === 'longText') {
|
|
16
18
|
return cell.preview;
|
|
17
19
|
}
|
|
20
|
+
|
|
18
21
|
if (cell.kind === 'binary') {
|
|
19
22
|
return { kind: 'binary', hex: cell.hex };
|
|
20
23
|
}
|
|
21
24
|
}
|
|
25
|
+
|
|
22
26
|
return null;
|
|
23
27
|
};
|
package/src/grid/layout.ts
CHANGED
|
@@ -3,14 +3,19 @@ export const ROW_HEIGHT = 28;
|
|
|
3
3
|
export const HEADER_HEIGHT = 32;
|
|
4
4
|
export const MIN_COLUMN_WIDTH = 64;
|
|
5
5
|
export const MAX_COLUMN_WIDTH = 320;
|
|
6
|
+
|
|
6
7
|
/* How far a drag may widen a column; the estimate stops at `MAX_COLUMN_WIDTH`, a person may go further. */
|
|
7
8
|
export const MAX_RESIZED_WIDTH = 1200;
|
|
9
|
+
|
|
8
10
|
/* How wide fitting a column to its content may make it. */
|
|
9
11
|
export const MAX_FIT_WIDTH = 640;
|
|
12
|
+
|
|
10
13
|
/* The window of drawn rows moves in blocks of this many rows, so a scroll within a block draws nothing. */
|
|
11
14
|
export const ROW_BLOCK = 16;
|
|
15
|
+
|
|
12
16
|
/* Two presses on a resize handle this close are a double click. */
|
|
13
17
|
export const DOUBLE_CLICK_MS = 350;
|
|
18
|
+
|
|
14
19
|
/* What a viewport is taken to be until it is measured, so a first paint already holds rows. */
|
|
15
20
|
export const FALLBACK_VIEWPORT_HEIGHT = 600;
|
|
16
21
|
|
|
@@ -20,6 +25,7 @@ const CELL_PADDING = 24;
|
|
|
20
25
|
const KEY_ICON_ROOM = 18;
|
|
21
26
|
const GUTTER_MIN_WIDTH = 40;
|
|
22
27
|
const SAMPLE_ROWS = 50;
|
|
28
|
+
|
|
23
29
|
/* The arrow and the position number a sorted header carries after its name. */
|
|
24
30
|
const SORT_ROOM = 24;
|
|
25
31
|
|
|
@@ -42,10 +48,12 @@ export const drawnRange = (block: number, height: number, count: number): Visibl
|
|
|
42
48
|
if (count <= 0 || height <= 0) {
|
|
43
49
|
return { start: 0, end: 0 };
|
|
44
50
|
}
|
|
51
|
+
|
|
45
52
|
const page = Math.ceil(height / ROW_HEIGHT);
|
|
46
53
|
const top = Math.max(0, block) * ROW_BLOCK;
|
|
47
54
|
const start = Math.max(0, Math.floor((top - page) / ROW_BLOCK) * ROW_BLOCK);
|
|
48
55
|
const end = Math.min(count, Math.ceil((top + ROW_BLOCK + 2 * page) / ROW_BLOCK) * ROW_BLOCK);
|
|
56
|
+
|
|
49
57
|
return { start: Math.min(start, end), end };
|
|
50
58
|
};
|
|
51
59
|
|
|
@@ -57,18 +65,22 @@ export const scrollToReveal = (start: number, size: number, scroll: number, view
|
|
|
57
65
|
if (start < scroll + leading) {
|
|
58
66
|
return Math.max(0, start - leading);
|
|
59
67
|
}
|
|
68
|
+
|
|
60
69
|
if (start + size > scroll + viewport) {
|
|
61
70
|
return Math.max(0, start + size - viewport);
|
|
62
71
|
}
|
|
72
|
+
|
|
63
73
|
return scroll;
|
|
64
74
|
};
|
|
65
75
|
|
|
66
76
|
/* The left edge of every column, counted from the first one. */
|
|
67
77
|
export const columnOffsets = (widths: readonly number[]): number[] => {
|
|
68
78
|
let offset = 0;
|
|
79
|
+
|
|
69
80
|
return widths.map((width) => {
|
|
70
81
|
const left = offset;
|
|
71
82
|
offset += width;
|
|
83
|
+
|
|
72
84
|
return left;
|
|
73
85
|
});
|
|
74
86
|
};
|
package/src/grid/navigation.ts
CHANGED
|
@@ -30,9 +30,11 @@ export const moveFocus = (from: CellPosition | null, input: NavigationKey, size:
|
|
|
30
30
|
if (size.rows === 0 || size.columns === 0) {
|
|
31
31
|
return null;
|
|
32
32
|
}
|
|
33
|
+
|
|
33
34
|
if (from === null && ENTERING_KEYS.has(input.key)) {
|
|
34
35
|
return { row: 0, column: 0 };
|
|
35
36
|
}
|
|
37
|
+
|
|
36
38
|
const lastRow = size.rows - 1;
|
|
37
39
|
const lastColumn = size.columns - 1;
|
|
38
40
|
const start = from ?? { row: 0, column: 0 };
|
|
@@ -61,11 +63,14 @@ export const moveFocus = (from: CellPosition | null, input: NavigationKey, size:
|
|
|
61
63
|
if (column > 0) {
|
|
62
64
|
return { row, column: column - 1 };
|
|
63
65
|
}
|
|
66
|
+
|
|
64
67
|
return row > 0 ? { row: row - 1, column: lastColumn } : null;
|
|
65
68
|
}
|
|
69
|
+
|
|
66
70
|
if (column < lastColumn) {
|
|
67
71
|
return { row, column: column + 1 };
|
|
68
72
|
}
|
|
73
|
+
|
|
69
74
|
return row < lastRow ? { row: row + 1, column: 0 } : null;
|
|
70
75
|
}
|
|
71
76
|
default:
|
|
@@ -15,21 +15,28 @@ export interface RowClick {
|
|
|
15
15
|
*/
|
|
16
16
|
export const selectRow = (keys: readonly string[], current: ReadonlySet<string>, anchor: number, index: number, click: RowClick): RowSelection => {
|
|
17
17
|
const key = keys[index];
|
|
18
|
+
|
|
18
19
|
if (key === undefined) {
|
|
19
20
|
return { selected: current, anchor };
|
|
20
21
|
}
|
|
22
|
+
|
|
21
23
|
if (click.shiftKey && anchor >= 0 && anchor < keys.length) {
|
|
22
24
|
const from = Math.min(anchor, index);
|
|
23
25
|
const to = Math.max(anchor, index);
|
|
24
26
|
const range = keys.slice(from, to + 1);
|
|
27
|
+
|
|
25
28
|
return { selected: new Set(click.mod ? [...current, ...range] : range), anchor };
|
|
26
29
|
}
|
|
30
|
+
|
|
27
31
|
if (click.mod) {
|
|
28
32
|
const next = new Set(current);
|
|
33
|
+
|
|
29
34
|
if (!next.delete(key)) {
|
|
30
35
|
next.add(key);
|
|
31
36
|
}
|
|
37
|
+
|
|
32
38
|
return { selected: next, anchor: index };
|
|
33
39
|
}
|
|
40
|
+
|
|
34
41
|
return { selected: new Set([key]), anchor: index };
|
|
35
42
|
};
|
package/src/grid/sort.ts
CHANGED
|
@@ -8,5 +8,6 @@ export const sortOnly = (column: string, direction: SortDirection): GridSort[] =
|
|
|
8
8
|
/* The sort of a column and its place among the sorts counted from one, or `null` when it is not sorted. */
|
|
9
9
|
export const sortStateOf = (sorts: readonly GridSort[], column: string): { direction: SortDirection; position: number } | null => {
|
|
10
10
|
const index = sorts.findIndex((sort) => sort.column === column);
|
|
11
|
+
|
|
11
12
|
return index < 0 ? null : { direction: sorts[index]!.direction, position: index + 1 };
|
|
12
13
|
};
|
package/src/grid/whole-cells.ts
CHANGED
|
@@ -24,6 +24,7 @@ const wholeSize = (cell: Shown): number => {
|
|
|
24
24
|
if (cell === null || typeof cell !== 'object' || cell.kind === 'default') {
|
|
25
25
|
return 0;
|
|
26
26
|
}
|
|
27
|
+
|
|
27
28
|
return cell.kind === 'longText' ? cell.length : cell.length * 2;
|
|
28
29
|
};
|
|
29
30
|
|
|
@@ -37,6 +38,7 @@ export const loadWholeCells = async (
|
|
|
37
38
|
): Promise<WholeCells> => {
|
|
38
39
|
const wanted: Wanted[] = [];
|
|
39
40
|
let size = 0;
|
|
41
|
+
|
|
40
42
|
rows.forEach((cells, row) =>
|
|
41
43
|
cells.forEach((cell, column) => {
|
|
42
44
|
if (isPreview(cell)) {
|
|
@@ -45,9 +47,11 @@ export const loadWholeCells = async (
|
|
|
45
47
|
}
|
|
46
48
|
})
|
|
47
49
|
);
|
|
50
|
+
|
|
48
51
|
if (wanted.length === 0) {
|
|
49
52
|
return { rows, previews: 0, tooLarge: false };
|
|
50
53
|
}
|
|
54
|
+
|
|
51
55
|
if (load === undefined || size > WHOLE_COPY_LIMIT) {
|
|
52
56
|
return { rows, previews: wanted.length, tooLarge: load !== undefined };
|
|
53
57
|
}
|
|
@@ -55,11 +59,13 @@ export const loadWholeCells = async (
|
|
|
55
59
|
const filled = rows.map((cells) => [...cells]);
|
|
56
60
|
let previews = 0;
|
|
57
61
|
let next = 0;
|
|
62
|
+
|
|
58
63
|
const work = async (): Promise<void> => {
|
|
59
64
|
while (next < wanted.length) {
|
|
60
65
|
const { row, column } = wanted[next]!;
|
|
61
66
|
next += 1;
|
|
62
67
|
const value = await load(row, column).catch(() => undefined);
|
|
68
|
+
|
|
63
69
|
if (value === undefined) {
|
|
64
70
|
previews += 1;
|
|
65
71
|
} else {
|
|
@@ -67,6 +73,8 @@ export const loadWholeCells = async (
|
|
|
67
73
|
}
|
|
68
74
|
}
|
|
69
75
|
};
|
|
76
|
+
|
|
70
77
|
await Promise.all(Array.from({ length: Math.min(PARALLEL_LOADS, wanted.length) }, work));
|
|
78
|
+
|
|
71
79
|
return { rows: filled, previews, tooLarge: false };
|
|
72
80
|
};
|
package/src/host/host.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { PROTOCOL_VERSION, type DatabaseError, type DatabaseErrorCode, type DatabaseRequest, type DatabaseResponse } from '../protocol/index.ts';
|
|
2
2
|
import type { DatabaseHost, DatabaseHostOptions, HelperProcess } from './types.ts';
|
|
3
|
-
import { parseRequest } from './validate.ts';
|
|
3
|
+
import { isDict, parseRequest } from './validate.ts';
|
|
4
4
|
|
|
5
5
|
const DEFAULT_READY_TIMEOUT_MS = 10000;
|
|
6
6
|
|
|
@@ -49,11 +49,10 @@ const parseLine = (line: string): unknown => {
|
|
|
49
49
|
}
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
const isDict = (value: unknown): value is Record<string, unknown> => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
53
|
-
|
|
54
52
|
/* Settles when every promise has, or after the grace period; a helper that hangs must not hold up an app that quits. */
|
|
55
53
|
const settleWithin = async (promises: readonly Promise<unknown>[]): Promise<void> => {
|
|
56
54
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
55
|
+
|
|
57
56
|
const grace = new Promise<void>((resolve) => {
|
|
58
57
|
timer = setTimeout(resolve, CLOSE_GRACE_MS);
|
|
59
58
|
});
|
|
@@ -127,11 +126,13 @@ export const createDatabaseHost = (options: DatabaseHostOptions): DatabaseHost =
|
|
|
127
126
|
|
|
128
127
|
if (!isDict(message) || message.event !== 'ready') {
|
|
129
128
|
abort(run, 'The helper did not start with a ready line.');
|
|
129
|
+
|
|
130
130
|
return;
|
|
131
131
|
}
|
|
132
132
|
|
|
133
133
|
if (message.protocol !== PROTOCOL_VERSION) {
|
|
134
134
|
abort(run, `The helper speaks protocol ${String(message.protocol)}, this host speaks ${PROTOCOL_VERSION}.`);
|
|
135
|
+
|
|
135
136
|
return;
|
|
136
137
|
}
|
|
137
138
|
|
|
@@ -167,11 +168,14 @@ export const createDatabaseHost = (options: DatabaseHostOptions): DatabaseHost =
|
|
|
167
168
|
}
|
|
168
169
|
|
|
169
170
|
let resolveReady = (): void => {};
|
|
171
|
+
|
|
170
172
|
let rejectReady = (_error: Failure): void => {};
|
|
173
|
+
|
|
171
174
|
const ready = new Promise<void>((resolve, reject) => {
|
|
172
175
|
resolveReady = resolve;
|
|
173
176
|
rejectReady = reject;
|
|
174
177
|
});
|
|
178
|
+
|
|
175
179
|
// A request awaits this; a run nobody waits for must not raise an unhandled rejection.
|
|
176
180
|
ready.catch(() => {});
|
|
177
181
|
|
|
@@ -185,6 +189,7 @@ export const createDatabaseHost = (options: DatabaseHostOptions): DatabaseHost =
|
|
|
185
189
|
onResponse(run, line);
|
|
186
190
|
}
|
|
187
191
|
});
|
|
192
|
+
|
|
188
193
|
child.onExit((code) => retire(run, code === null ? 'The helper exited.' : `The helper exited with code ${code}.`));
|
|
189
194
|
|
|
190
195
|
return run;
|
|
@@ -208,6 +213,7 @@ export const createDatabaseHost = (options: DatabaseHostOptions): DatabaseHost =
|
|
|
208
213
|
/* Sends a message under a helper id of the host's own; the promise never rejects. */
|
|
209
214
|
const dispatch = (run: Run, method: string, params: unknown): { readonly id: string; readonly response: Promise<DatabaseResponse> } => {
|
|
210
215
|
const id = `h${++counter}`;
|
|
216
|
+
|
|
211
217
|
const response = new Promise<DatabaseResponse>((resolve) => {
|
|
212
218
|
run.pending.set(id, resolve);
|
|
213
219
|
|
|
@@ -226,6 +232,7 @@ export const createDatabaseHost = (options: DatabaseHostOptions): DatabaseHost =
|
|
|
226
232
|
|
|
227
233
|
const sessionOf = (request: DatabaseRequest): string | null => {
|
|
228
234
|
const session = (request.params as { readonly session?: unknown }).session;
|
|
235
|
+
|
|
229
236
|
return typeof session === 'string' ? session : null;
|
|
230
237
|
};
|
|
231
238
|
|
|
@@ -292,6 +299,7 @@ export const createDatabaseHost = (options: DatabaseHostOptions): DatabaseHost =
|
|
|
292
299
|
const cancelOwned = async (request: Extract<DatabaseRequest, { method: 'cancel' }>, owner: string): Promise<DatabaseResponse> => {
|
|
293
300
|
const entries = inflight.get(owner);
|
|
294
301
|
const target = request.id === request.params.request ? undefined : entries?.get(request.params.request);
|
|
302
|
+
|
|
295
303
|
const answer = (cancelled: boolean): DatabaseResponse => ({ id: request.id, ok: true, result: { cancelled } });
|
|
296
304
|
|
|
297
305
|
if (target === undefined) {
|
|
@@ -300,6 +308,7 @@ export const createDatabaseHost = (options: DatabaseHostOptions): DatabaseHost =
|
|
|
300
308
|
|
|
301
309
|
if (target.helperId === null) {
|
|
302
310
|
target.cancelled = true;
|
|
311
|
+
|
|
303
312
|
return answer(true);
|
|
304
313
|
}
|
|
305
314
|
|
|
@@ -310,6 +319,7 @@ export const createDatabaseHost = (options: DatabaseHostOptions): DatabaseHost =
|
|
|
310
319
|
}
|
|
311
320
|
|
|
312
321
|
const sent = await dispatch(run, 'cancel', { request: target.helperId }).response;
|
|
322
|
+
|
|
313
323
|
return { ...sent, id: request.id } as DatabaseResponse;
|
|
314
324
|
};
|
|
315
325
|
|
|
@@ -321,6 +331,7 @@ export const createDatabaseHost = (options: DatabaseHostOptions): DatabaseHost =
|
|
|
321
331
|
|
|
322
332
|
// The helper ends a session whose connection to the server was lost.
|
|
323
333
|
const target = sessionOf(request);
|
|
334
|
+
|
|
324
335
|
if (target !== null && !response.ok && response.error.code === 'unknown-session') {
|
|
325
336
|
sessions.delete(target);
|
|
326
337
|
}
|
|
@@ -341,10 +352,12 @@ export const createDatabaseHost = (options: DatabaseHostOptions): DatabaseHost =
|
|
|
341
352
|
|
|
342
353
|
if (entry.released) {
|
|
343
354
|
void dispatch(run, 'close', { session }).response;
|
|
355
|
+
|
|
344
356
|
return failure(request.id, 'cancelled', 'The request was cancelled.');
|
|
345
357
|
}
|
|
346
358
|
|
|
347
359
|
sessions.set(session, owner);
|
|
360
|
+
|
|
348
361
|
return { ...response, id: request.id } as DatabaseResponse;
|
|
349
362
|
};
|
|
350
363
|
|
package/src/host/spawn.ts
CHANGED
|
@@ -95,6 +95,7 @@ export const spawnHelper = (path: string, options: SpawnHelperOptions = {}): Hel
|
|
|
95
95
|
finish(null);
|
|
96
96
|
}
|
|
97
97
|
});
|
|
98
|
+
|
|
98
99
|
child.on('close', (code, signal) => finish(code, signal));
|
|
99
100
|
|
|
100
101
|
return {
|
|
@@ -109,6 +110,7 @@ export const spawnHelper = (path: string, options: SpawnHelperOptions = {}): Hel
|
|
|
109
110
|
onExit(listener): void {
|
|
110
111
|
if (finished) {
|
|
111
112
|
queueMicrotask(() => listener(exitCode, exitSignal));
|
|
113
|
+
|
|
112
114
|
return;
|
|
113
115
|
}
|
|
114
116
|
|
package/src/host/validate.ts
CHANGED
|
@@ -23,7 +23,7 @@ const fail = (message: string): never => {
|
|
|
23
23
|
throw new InvalidRequest(message);
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
const isDict = (value: unknown): value is Dict => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
26
|
+
export const isDict = (value: unknown): value is Dict => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
27
27
|
|
|
28
28
|
const isRequestId = (value: unknown): value is string => typeof value === 'string' && value.length > 0 && value.length <= MAX_ID_LENGTH;
|
|
29
29
|
|
|
@@ -103,6 +103,7 @@ const value = (input: unknown, label: string): void => {
|
|
|
103
103
|
if (!Number.isFinite(input)) {
|
|
104
104
|
fail(`"${label}" must be a finite number.`);
|
|
105
105
|
}
|
|
106
|
+
|
|
106
107
|
return;
|
|
107
108
|
}
|
|
108
109
|
|
|
@@ -120,6 +121,7 @@ const value = (input: unknown, label: string): void => {
|
|
|
120
121
|
const editValue = (input: unknown, label: string): void => {
|
|
121
122
|
if (isDict(input) && input.kind === 'default') {
|
|
122
123
|
shape(input, label, ['kind']);
|
|
124
|
+
|
|
123
125
|
return;
|
|
124
126
|
}
|
|
125
127
|
|
|
@@ -129,6 +131,7 @@ const editValue = (input: unknown, label: string): void => {
|
|
|
129
131
|
const values = (input: unknown, label: string, check: (value: unknown, label: string) => void, atLeastOne: boolean): void => {
|
|
130
132
|
if (!isDict(input)) {
|
|
131
133
|
fail(`"${label}" must be an object.`);
|
|
134
|
+
|
|
132
135
|
return;
|
|
133
136
|
}
|
|
134
137
|
|
|
@@ -146,6 +149,7 @@ const values = (input: unknown, label: string, check: (value: unknown, label: st
|
|
|
146
149
|
const tunnel = (input: unknown, label: string): void => {
|
|
147
150
|
if (!isDict(input)) {
|
|
148
151
|
fail(`"${label}" must be an object.`);
|
|
152
|
+
|
|
149
153
|
return;
|
|
150
154
|
}
|
|
151
155
|
|
|
@@ -155,6 +159,7 @@ const tunnel = (input: unknown, label: string): void => {
|
|
|
155
159
|
optional(config, 'port', label, port);
|
|
156
160
|
optional(config, 'user', label, filledText);
|
|
157
161
|
optional(config, 'identityFile', label, filledText);
|
|
162
|
+
|
|
158
163
|
return;
|
|
159
164
|
}
|
|
160
165
|
|
|
@@ -163,6 +168,7 @@ const tunnel = (input: unknown, label: string): void => {
|
|
|
163
168
|
filledText(config.container, `${label}.container`);
|
|
164
169
|
optional(config, 'port', label, port);
|
|
165
170
|
optional(config, 'context', label, filledText);
|
|
171
|
+
|
|
166
172
|
return;
|
|
167
173
|
}
|
|
168
174
|
|
|
@@ -172,6 +178,7 @@ const tunnel = (input: unknown, label: string): void => {
|
|
|
172
178
|
const connection = (input: unknown, label: string): void => {
|
|
173
179
|
if (!isDict(input)) {
|
|
174
180
|
fail(`"${label}" must be an object.`);
|
|
181
|
+
|
|
175
182
|
return;
|
|
176
183
|
}
|
|
177
184
|
|
|
@@ -180,6 +187,7 @@ const connection = (input: unknown, label: string): void => {
|
|
|
180
187
|
absolutePath(config.path, `${label}.path`);
|
|
181
188
|
optional(config, 'create', label, flag);
|
|
182
189
|
optional(config, 'readOnly', label, flag);
|
|
190
|
+
|
|
183
191
|
return;
|
|
184
192
|
}
|
|
185
193
|
|
|
@@ -197,6 +205,7 @@ const connection = (input: unknown, label: string): void => {
|
|
|
197
205
|
optional(config, 'database', label, anyText);
|
|
198
206
|
optional(config, 'tls', label, oneOf(TLS_MODES));
|
|
199
207
|
optional(config, 'readOnly', label, flag);
|
|
208
|
+
|
|
200
209
|
return;
|
|
201
210
|
}
|
|
202
211
|
|
|
@@ -206,6 +215,7 @@ const connection = (input: unknown, label: string): void => {
|
|
|
206
215
|
const rowChange = (input: unknown, label: string): void => {
|
|
207
216
|
if (!isDict(input)) {
|
|
208
217
|
fail(`"${label}" must be an object.`);
|
|
218
|
+
|
|
209
219
|
return;
|
|
210
220
|
}
|
|
211
221
|
|
|
@@ -227,6 +237,7 @@ const rowChange = (input: unknown, label: string): void => {
|
|
|
227
237
|
const exportSource = (input: unknown, label: string): void => {
|
|
228
238
|
if (!isDict(input)) {
|
|
229
239
|
fail(`"${label}" must be an object.`);
|
|
240
|
+
|
|
230
241
|
return;
|
|
231
242
|
}
|
|
232
243
|
|
|
@@ -236,6 +247,7 @@ const exportSource = (input: unknown, label: string): void => {
|
|
|
236
247
|
filledText(source.table, `${label}.table`);
|
|
237
248
|
optional(source, 'where', label, anyText);
|
|
238
249
|
optional(source, 'orderBy', label, anyText);
|
|
250
|
+
|
|
239
251
|
return;
|
|
240
252
|
}
|
|
241
253
|
|
|
@@ -243,6 +255,7 @@ const exportSource = (input: unknown, label: string): void => {
|
|
|
243
255
|
const source = shape(input, label, ['kind', 'sql'], ['schema']);
|
|
244
256
|
filledText(source.sql, `${label}.sql`);
|
|
245
257
|
optional(source, 'schema', label, filledText);
|
|
258
|
+
|
|
246
259
|
return;
|
|
247
260
|
}
|
|
248
261
|
|
|
@@ -253,6 +266,19 @@ const cellLimit = (params: Dict): void => optional(params, 'cellLimit', 'params'
|
|
|
253
266
|
|
|
254
267
|
const maxBytes = (params: Dict): void => optional(params, 'maxBytes', 'params', (limit, name) => integer(limit, name, 1, MAX_BYTES));
|
|
255
268
|
|
|
269
|
+
const rowLimit = (params: Dict): void => optional(params, 'limit', 'params', (limit, name) => integer(limit, name, 1, MAX_LIMIT));
|
|
270
|
+
|
|
271
|
+
const pageWindow = (params: Dict): void => {
|
|
272
|
+
integer(params.offset, 'params.offset', 0, Number.MAX_SAFE_INTEGER);
|
|
273
|
+
integer(params.limit, 'params.limit', 1, MAX_LIMIT);
|
|
274
|
+
cellLimit(params);
|
|
275
|
+
maxBytes(params);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
const connectionParams = (input: unknown): void => connection(shape(input, 'params', ['connection']).connection, 'params.connection');
|
|
279
|
+
|
|
280
|
+
const sessionParams = (input: unknown): void => filledText(shape(input, 'params', ['session']).session, 'params.session');
|
|
281
|
+
|
|
256
282
|
const table = (params: Dict): void => {
|
|
257
283
|
filledText(params.session, 'params.session');
|
|
258
284
|
filledText(params.schema, 'params.schema');
|
|
@@ -262,10 +288,10 @@ const table = (params: Dict): void => {
|
|
|
262
288
|
const TARGET = ['session', 'schema', 'table'];
|
|
263
289
|
|
|
264
290
|
const PARAMS: { readonly [M in DatabaseMethod]: (input: unknown) => void } = {
|
|
265
|
-
open:
|
|
266
|
-
test:
|
|
267
|
-
close:
|
|
268
|
-
schemas:
|
|
291
|
+
open: connectionParams,
|
|
292
|
+
test: connectionParams,
|
|
293
|
+
close: sessionParams,
|
|
294
|
+
schemas: sessionParams,
|
|
269
295
|
tables: (input) => {
|
|
270
296
|
const params = shape(input, 'params', ['session', 'schema']);
|
|
271
297
|
filledText(params.session, 'params.session');
|
|
@@ -277,10 +303,7 @@ const PARAMS: { readonly [M in DatabaseMethod]: (input: unknown) => void } = {
|
|
|
277
303
|
table(params);
|
|
278
304
|
optional(params, 'where', 'params', anyText);
|
|
279
305
|
optional(params, 'orderBy', 'params', anyText);
|
|
280
|
-
|
|
281
|
-
integer(params.limit, 'params.limit', 1, MAX_LIMIT);
|
|
282
|
-
cellLimit(params);
|
|
283
|
-
maxBytes(params);
|
|
306
|
+
pageWindow(params);
|
|
284
307
|
},
|
|
285
308
|
count: (input) => {
|
|
286
309
|
const params = shape(input, 'params', TARGET, ['where']);
|
|
@@ -299,6 +322,7 @@ const PARAMS: { readonly [M in DatabaseMethod]: (input: unknown) => void } = {
|
|
|
299
322
|
|
|
300
323
|
if (!Array.isArray(params.changes)) {
|
|
301
324
|
fail('"params.changes" must be a list.');
|
|
325
|
+
|
|
302
326
|
return;
|
|
303
327
|
}
|
|
304
328
|
|
|
@@ -309,7 +333,7 @@ const PARAMS: { readonly [M in DatabaseMethod]: (input: unknown) => void } = {
|
|
|
309
333
|
filledText(params.session, 'params.session');
|
|
310
334
|
filledText(params.sql, 'params.sql');
|
|
311
335
|
optional(params, 'schema', 'params', filledText);
|
|
312
|
-
|
|
336
|
+
rowLimit(params);
|
|
313
337
|
cellLimit(params);
|
|
314
338
|
maxBytes(params);
|
|
315
339
|
},
|
|
@@ -318,10 +342,7 @@ const PARAMS: { readonly [M in DatabaseMethod]: (input: unknown) => void } = {
|
|
|
318
342
|
filledText(params.session, 'params.session');
|
|
319
343
|
filledText(params.sql, 'params.sql');
|
|
320
344
|
optional(params, 'schema', 'params', filledText);
|
|
321
|
-
|
|
322
|
-
integer(params.limit, 'params.limit', 1, MAX_LIMIT);
|
|
323
|
-
cellLimit(params);
|
|
324
|
-
maxBytes(params);
|
|
345
|
+
pageWindow(params);
|
|
325
346
|
},
|
|
326
347
|
transaction: (input) => {
|
|
327
348
|
const params = shape(input, 'params', ['session', 'action']);
|
|
@@ -342,7 +363,7 @@ const PARAMS: { readonly [M in DatabaseMethod]: (input: unknown) => void } = {
|
|
|
342
363
|
absolutePath(params.path, 'params.path');
|
|
343
364
|
oneOf(DELIMITED_FORMATS)(params.format, 'params.format');
|
|
344
365
|
flag(params.header, 'params.header');
|
|
345
|
-
|
|
366
|
+
rowLimit(params);
|
|
346
367
|
},
|
|
347
368
|
import: (input) => {
|
|
348
369
|
const params = shape(input, 'params', [...TARGET, 'path', 'format', 'header', 'columns']);
|
|
@@ -353,6 +374,7 @@ const PARAMS: { readonly [M in DatabaseMethod]: (input: unknown) => void } = {
|
|
|
353
374
|
|
|
354
375
|
if (!Array.isArray(params.columns)) {
|
|
355
376
|
fail('"params.columns" must be a list.');
|
|
377
|
+
|
|
356
378
|
return;
|
|
357
379
|
}
|
|
358
380
|
|
package/src/same-list.ts
ADDED