1
1
mirror of https://github.com/Fabio286/antares.git synced 2025-06-05 21:59:22 +02:00

Compare commits

...

11 Commits

65 changed files with 232 additions and 172 deletions

View File

@@ -2,6 +2,36 @@
All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
### [0.7.22-beta.1](https://github.com/antares-sql/antares/compare/v0.7.22-beta.0...v0.7.22-beta.1) (2024-02-12)
### Features
* update dutch translation + fix spelling mistake ([30ada13](https://github.com/antares-sql/antares/commit/30ada13663e88f89beb3dd7291010837059585d5))
### Bug Fixes
* some issues related to previous commit ([259d051](https://github.com/antares-sql/antares/commit/259d051a21e334496d3a52b662f1855ba9a9046d))
* unable to edit tables containing SET fields, fixes [#755](https://github.com/antares-sql/antares/issues/755) ([d698f27](https://github.com/antares-sql/antares/commit/d698f2798a2423f86e6d786dd3ab80439b372a08))
### Improvements
* **MySQL:** improvements in connection handling ([876d5ea](https://github.com/antares-sql/antares/commit/876d5ea48185334e9e2fc981c4282a9c42d22b10))
### [0.7.22-beta.0](https://github.com/antares-sql/antares/compare/v0.7.21...v0.7.22-beta.0) (2024-02-04)
### Features
* **UI:** resizable textarea in new/edito note, closes [#747](https://github.com/antares-sql/antares/issues/747) ([1a0c5da](https://github.com/antares-sql/antares/commit/1a0c5da2f14b99d6f5581b2bf6e916d67d097245))
### Improvements
* **UI:** improved notes, fixes [#746](https://github.com/antares-sql/antares/issues/746) ([bb36e98](https://github.com/antares-sql/antares/commit/bb36e98bebc5e1e55735e98d272428df2ab682e8))
### [0.7.21](https://github.com/antares-sql/antares/compare/v0.7.21-beta.1...v0.7.21) (2024-01-13)

View File

@@ -71,17 +71,6 @@ On macOS you can run `.dmg` distribution following [this guide](https://support.
[<img height='56' alt='Download on Flathub' src='https://dl.flathub.org/assets/badges/flathub-badge-en.svg'/>](https://flathub.org/apps/it.fabiodistasio.AntaresSQL) [![Get it from the Snap Store](https://snapcraft.io/static/images/badges/en/snap-store-black.svg)](https://snapcraft.io/antares) [![Get it from AUR](https://raw.githubusercontent.com/antares-sql/antares/master/docs/aur-badge.svg)](https://aur.archlinux.org/packages/antares-sql-bin) [<img src="https://developer.microsoft.com/store/badges/images/English_get-it-from-MS.png" style="height: 56px">](https://www.microsoft.com/p/antares-sql-client/9nhtb9sq51r1?cid=storebadge&ocid=badge&rtc=1&activetab=pivot:overviewtab)
🚀 **[Other Downloads](https://github.com/antares-sql/antares/releases/latest)**
## Coming soon
This is a roadmap with major features will come in near future.
- Database tools.
- Users management (add/edit/delete).
- More context menu shortcuts.
- More keyboard shortcuts.
- Support for other databases.
- Apple Silicon distribution
## Currently supported
### Databases
@@ -90,6 +79,7 @@ This is a roadmap with major features will come in near future.
- [x] PostgreSQL
- [x] SQLite
- [x] Firebird SQL
- [ ] DuckDB
- [ ] SQL Server
- [ ] More...

4
package-lock.json generated
View File

@@ -1,12 +1,12 @@
{
"name": "antares",
"version": "0.7.21",
"version": "0.7.22-beta.1",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "antares",
"version": "0.7.21",
"version": "0.7.22-beta.1",
"hasInstallScript": true,
"license": "MIT",
"dependencies": {

View File

@@ -1,7 +1,7 @@
{
"name": "antares",
"productName": "Antares",
"version": "0.7.21",
"version": "0.7.22-beta.1",
"description": "A modern, fast and productivity driven SQL client with a focus in UX.",
"license": "MIT",
"repository": "https://github.com/antares-sql/antares.git",

View File

@@ -363,7 +363,7 @@ export interface QueryBuilderObject {
offset: number;
join: string[];
update: string[];
insert: {[key: string]: string | boolean | number }[];
insert: Record<string, string | boolean | number>[];
delete: boolean;
}

View File

@@ -13,7 +13,7 @@ export interface ExportOptions {
includeContent: boolean;
includeDropStatement: boolean;
}[];
includes: {[key: string]: boolean};
includes: Record<string, boolean>;
outputFormat: 'sql' | 'sql.zip';
outputFile: string;
sqlInsertAfter: number;

View File

@@ -18,7 +18,7 @@ export interface TableDeleteParams {
primary?: string;
field: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
rows: {[key: string]: any};
rows: Record<string, any>;
}
export type TableFilterOperator = '=' | '!=' | '>' | '<' | '>=' | '<=' | 'IN' | 'NOT IN' | 'LIKE' | 'NOT LIKE' | 'RLIKE' | 'NOT RLIKE' | 'BETWEEN' | 'IS NULL' | 'IS NOT NULL'
@@ -35,17 +35,16 @@ export interface InsertRowsParams {
uid: string;
schema: string;
table: string;
row: {[key: string]: {
group: string;
method: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
params: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any;
length: number;
};
};
row: Record<string, {
group: string;
method: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
params: any;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
value: any;
length: number;
}>;
repeat: number;
fields: {[key: string]: string};
fields: Record<string, string>;
locale: UsableLocale;
}

View File

@@ -41,7 +41,7 @@ export const escapeAndQuote = (val: string, client: ClientCode) => {
const { stringsWrapper: sw } = customizations[client];
// eslint-disable-next-line no-control-regex
const CHARS_TO_ESCAPE = /[\0\b\t\n\r\x1a"'\\]/g;
const CHARS_ESCAPE_MAP: {[key: string]: string} = {
const CHARS_ESCAPE_MAP: Record<string, string> = {
'\0': '\\0',
'\b': '\\b',
'\t': '\\t',
@@ -153,9 +153,9 @@ export const valueToSqlString = (args: {
};
export const jsonToSqlInsert = (args: {
json: { [key: string]: any}[];
json: Record<string, any>[];
client: ClientCode;
fields: { [key: string]: {type: string; datePrecision: number}};
fields: Record<string, {type: string; datePrecision: number}>;
table: string;
options?: {sqlInsertAfter: number; sqlInsertDivider: 'bytes' | 'rows'};
}) => {

View File

@@ -1,4 +1,4 @@
export const shortcutEvents: { [key: string]: { l18n: string; l18nParam?: string | number; context?: 'tab' }} = {
export const shortcutEvents: Record<string, { l18n: string; l18nParam?: string | number; context?: 'tab' }> = {
'run-or-reload': { l18n: 'application.runOrReload', context: 'tab' },
'open-new-tab': { l18n: 'application.openNewTab', context: 'tab' },
'close-tab': { l18n: 'application.closeTab', context: 'tab' },

View File

@@ -6,7 +6,7 @@ import { SslOptions } from 'mysql2';
import { ClientsFactory } from '../libs/ClientsFactory';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
export default (connections: Record<string, antares.Client>) => {
ipcMain.handle('test-connection', async (event, conn: antares.ConnectionParams) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };

View File

@@ -3,7 +3,7 @@ import { ipcMain } from 'electron';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
export default (connections: Record<string, antares.Client>) => {
ipcMain.handle('get-databases', async (event, uid) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };

View File

@@ -3,7 +3,7 @@ import { ipcMain } from 'electron';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
export default (connections: Record<string, antares.Client>) => {
ipcMain.handle('get-function-informations', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };

View File

@@ -13,7 +13,7 @@ import updates from './updates';
import users from './users';
import views from './views';
const connections: {[key: string]: antares.Client} = {};
const connections: Record<string, antares.Client> = {};
export default () => {
connection(connections);

View File

@@ -3,7 +3,7 @@ import { ipcMain } from 'electron';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
export default (connections: Record<string, antares.Client>) => {
ipcMain.handle('get-routine-informations', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };

View File

@@ -3,7 +3,7 @@ import { ipcMain } from 'electron';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
export default (connections: Record<string, antares.Client>) => {
ipcMain.handle('get-scheduler-informations', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };

View File

@@ -6,7 +6,7 @@ import { Worker } from 'worker_threads';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
export default (connections: Record<string, antares.Client>) => {
let exporter: Worker = null;
let importer: Worker = null;

View File

@@ -10,7 +10,7 @@ import * as moment from 'moment';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
export default (connections: Record<string, antares.Client>) => {
ipcMain.handle('get-table-columns', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
@@ -249,7 +249,7 @@ export default (connections: {[key: string]: antares.Client}) => {
if (params.primary) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const idString = params.rows.map((row: {[key: string]: any}) => {
const idString = params.rows.map((row: Record<string, any>) => {
const fieldName = Object.keys(row)[0].includes('.') ? `${params.table}.${params.primary}` : params.primary;
return typeof row[fieldName] === 'string'
@@ -304,10 +304,10 @@ export default (connections: {[key: string]: antares.Client}) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };
try { // TODO: move to client classes
const rows: {[key: string]: string | number | boolean | Date | Buffer}[] = [];
const rows: Record<string, string | number | boolean | Date | Buffer>[] = [];
for (let i = 0; i < +params.repeat; i++) {
const insertObj: {[key: string]: string | number | boolean | Date | Buffer} = {};
const insertObj: Record<string, string | number | boolean | Date | Buffer> = {};
for (const key in params.row) {
const type = params.fields[key];
@@ -367,7 +367,7 @@ export default (connections: {[key: string]: antares.Client}) => {
insertObj[key] = escapedParam;
}
else { // Faker value
const parsedParams: {[key: string]: string | number | boolean | Date | Buffer} = {};
const parsedParams: Record<string, string | number | boolean | Date | Buffer> = {};
let fakeValue;
if (params.locale)
@@ -437,12 +437,12 @@ export default (connections: {[key: string]: antares.Client}) => {
if (description)
query.select(`LEFT(${description}, 20) AS foreign_description`);
const results = await query.run<{[key: string]: string}>();
const results = await query.run<Record<string, string>>();
const parsedResults: {[key: string]: string}[] = [];
const parsedResults: Record<string, string>[] = [];
for (const row of results.rows) {
const remappedRow: {[key: string]: string} = {};
const remappedRow: Record<string, string> = {};
for (const key in row)
remappedRow[key.toLowerCase()] = row[key];// Thanks Firebird -.-

View File

@@ -3,7 +3,7 @@ import { ipcMain } from 'electron';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
export default (connections: Record<string, antares.Client>) => {
ipcMain.handle('get-trigger-informations', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };

View File

@@ -3,7 +3,7 @@ import { ipcMain } from 'electron';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
export default (connections: Record<string, antares.Client>) => {
ipcMain.handle('get-users', async (event, uid) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };

View File

@@ -3,7 +3,7 @@ import { ipcMain } from 'electron';
import { validateSender } from '../libs/misc/validateSender';
export default (connections: {[key: string]: antares.Client}) => {
export default (connections: Record<string, antares.Client>) => {
ipcMain.handle('get-view-informations', async (event, params) => {
if (!validateSender(event.senderFrame)) return { status: 'error', response: 'Unauthorized process' };

View File

@@ -136,7 +136,7 @@ export abstract class BaseClient {
}
// eslint-disable-next-line @typescript-eslint/no-explicit-any
insert (arr: {[key: string]: any}[]) {
insert (arr: Record<string, any>[]) {
this._query.insert = [...this._query.insert, ...arr];
return this;
}

View File

@@ -13,7 +13,7 @@ export class FirebirdSQLClient extends BaseClient {
protected _connection?: firebird.Database | firebird.ConnectionPool;
_params: firebird.Options;
private _types: {[key: number]: string} ={
private _types: Record<number, string> ={
452: 'CHAR', // Array of char
448: 'VARCHAR',
500: 'SMALLINT',

View File

@@ -12,10 +12,11 @@ export class MySQLClient extends BaseClient {
private _connectionsToCommit: Map<string, mysql.Connection | mysql.PoolConnection>;
private _keepaliveTimer: NodeJS.Timer;
private _keepaliveMs: number;
private sqlMode?: string[];
_connection?: mysql.Connection | mysql.Pool;
_params: mysql.ConnectionOptions & {schema: string; ssl?: mysql.SslOptions; ssh?: SSHConfig; readonly: boolean};
private types: {[key: number]: string} = {
private types: Record<number, string> = {
0: 'DECIMAL',
1: 'TINYINT',
2: 'SMALLINT',
@@ -58,6 +59,10 @@ export class MySQLClient extends BaseClient {
this._keepaliveMs = 10*60*1000;
}
private get isPool () {
return 'getConnection' in this._connection;
}
private _getType (field: mysql.FieldPacket & { columnType?: number; columnLength?: number }) {
let name = this.types[field.columnType];
let length = field.columnLength;
@@ -181,9 +186,32 @@ export class MySQLClient extends BaseClient {
async connect () {
if (!this._poolSize)
this._connection = await this.getConnection();
this._connection = await this.getSingleConnection();
else
this._connection = await this.getConnectionPool();
// ANSI_QUOTES check
const [response] = await this._connection.query<mysql.RowDataPacket[]>('SHOW GLOBAL VARIABLES LIKE \'%sql_mode%\'');
this.sqlMode = response[0]?.Value?.split(',');
const hasAnsiQuotes = this.sqlMode.includes('ANSI') || this.sqlMode.includes('ANSI_QUOTES');
if (hasAnsiQuotes)
await this._connection.query(`SET SESSION sql_mode = '${this.sqlMode.filter((m: string) => !['ANSI', 'ANSI_QUOTES'].includes(m)).join(',')}'`);
if (this._params.readonly)
await this._connection.query('SET SESSION TRANSACTION READ ONLY');
if (this._poolSize) {
const hasAnsiQuotes = this.sqlMode.includes('ANSI') || this.sqlMode.includes('ANSI_QUOTES');
this._connection.on('connection', conn => {
if (this._params.readonly)
conn.query('SET SESSION TRANSACTION READ ONLY');
if (hasAnsiQuotes)
conn.query(`SET SESSION sql_mode = '${this.sqlMode.filter((m: string) => !['ANSI', 'ANSI_QUOTES'].includes(m)).join(',')}'`);
});
}
}
destroy () {
@@ -196,7 +224,7 @@ export class MySQLClient extends BaseClient {
}
}
async getConnection () {
async getSingleConnection () {
const dbConfig = await this.getDbConfig();
const connection = await mysql.createConnection({
...dbConfig,
@@ -208,17 +236,6 @@ export class MySQLClient extends BaseClient {
}
});
// ANSI_QUOTES check
const [response] = await connection.query<mysql.RowDataPacket[]>('SHOW GLOBAL VARIABLES LIKE \'%sql_mode%\'');
const sqlMode: string[] = response[0]?.Value?.split(',');
const hasAnsiQuotes = sqlMode.includes('ANSI') || sqlMode.includes('ANSI_QUOTES');
if (this._params.readonly)
await connection.query('SET SESSION TRANSACTION READ ONLY');
if (hasAnsiQuotes)
await connection.query(`SET SESSION sql_mode = '${sqlMode.filter((m: string) => !['ANSI', 'ANSI_QUOTES'].includes(m)).join(',')}'`);
return connection;
}
@@ -227,6 +244,7 @@ export class MySQLClient extends BaseClient {
const connection = mysql.createPool({
...dbConfig,
connectionLimit: this._poolSize,
enableKeepAlive: true,
typeCast: (field, next) => {
if (field.type === 'DATETIME')
return field.string();
@@ -235,25 +253,6 @@ export class MySQLClient extends BaseClient {
}
});
// ANSI_QUOTES check
const [res] = await connection.query<mysql.RowDataPacket[]>('SHOW GLOBAL VARIABLES LIKE \'%sql_mode%\'');
const sqlMode: string[] = res[0]?.Value?.split(',');
const hasAnsiQuotes = sqlMode.includes('ANSI') || sqlMode.includes('ANSI_QUOTES');
if (hasAnsiQuotes)
await connection.query(`SET SESSION sql_mode = '${sqlMode.filter((m: string) => !['ANSI', 'ANSI_QUOTES'].includes(m)).join(',')}'`);
if (this._params.readonly)
await connection.query('SET SESSION TRANSACTION READ ONLY');
connection.on('connection', conn => {
if (this._params.readonly)
conn.query('SET SESSION TRANSACTION READ ONLY');
if (hasAnsiQuotes)
conn.query(`SET SESSION sql_mode = '${sqlMode.filter((m: string) => !['ANSI', 'ANSI_QUOTES'].includes(m)).join(',')}'`);
});
this._keepaliveTimer = setInterval(async () => {
await this.keepAlive();
}, this._keepaliveMs);
@@ -261,6 +260,43 @@ export class MySQLClient extends BaseClient {
return connection;
}
async getConnection (args?: antares.QueryParams, retry?: boolean): Promise<mysql.Pool | mysql.PoolConnection | mysql.Connection> {
let connection;
try {
if (args && !args.autocommit && args.tabUid) { // autocommit OFF
if (this._connectionsToCommit.has(args.tabUid))
connection = this._connectionsToCommit.get(args.tabUid);
else {
connection = await this.getSingleConnection();
await connection.query('SET SESSION autocommit=0');
this._connectionsToCommit.set(args.tabUid, connection);
}
}
else// autocommit ON
connection = this.isPool ? await (this._connection as mysql.Pool).getConnection() : this._connection;
if (args && args.tabUid && this.isPool) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this._runningConnections.set(args.tabUid, (connection as any).connection.connectionId);
}
if (args && args.schema)
await connection.query(`USE \`${args.schema}\``);
return connection;
}
catch (error) {
if (error.code === 'ECONNRESET' && !retry) {
this.destroy();
await this.connect();
return this.getConnection(args, true);
}
else
throw new Error(error.message);
}
}
private async keepAlive () {
try {
const connection = await (this._connection as mysql.Pool).getConnection();
@@ -582,7 +618,7 @@ export class MySQLClient extends BaseClient {
}
})
.filter(Boolean)
.reduce((acc: {[key: string]: { name: string; type: string; length: string; default: string}}, curr) => {
.reduce((acc: Record<string, { name: string; type: string; length: string; default: string}>, curr) => {
acc[curr.name] = curr;
return acc;
}, {});
@@ -591,7 +627,7 @@ export class MySQLClient extends BaseClient {
return rows.map((field) => {
const numLengthMatch = field.COLUMN_TYPE.match(/int\(([^)]+)\)/);
const numLength = numLengthMatch ? +numLengthMatch.pop() : field.NUMERIC_PRECISION || null;
const enumValues = /(enum)/.test(field.COLUMN_TYPE)
const enumValues = /(enum|set)/.test(field.COLUMN_TYPE)
? field.COLUMN_TYPE.match(/\(([^)]+)\)/)[0].slice(1, -1)
: null;
@@ -1648,28 +1684,7 @@ export class MySQLClient extends BaseClient {
.map(q => q.trim())
: [sql];
let connection: mysql.Connection | mysql.Pool | mysql.PoolConnection;
const isPool = 'getConnection' in this._connection;
if (!args.autocommit && args.tabUid) { // autocommit OFF
if (this._connectionsToCommit.has(args.tabUid))
connection = this._connectionsToCommit.get(args.tabUid);
else {
connection = await this.getConnection();
await connection.query('SET SESSION autocommit=0');
this._connectionsToCommit.set(args.tabUid, connection);
}
}
else// autocommit ON
connection = isPool ? await (this._connection as mysql.Pool).getConnection() : this._connection;
if (args.tabUid && isPool) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
this._runningConnections.set(args.tabUid, (connection as any).connection.connectionId);
}
if (args.schema)
await connection.query(`USE \`${args.schema}\``);
const connection = await this.getConnection(args);
for (const query of queries) {
if (!query) continue;
@@ -1729,7 +1744,7 @@ export class MySQLClient extends BaseClient {
});
}
catch (err) {
if (isPool && args.autocommit) {
if (this.isPool && args.autocommit) {
(connection as mysql.PoolConnection).release();
this._runningConnections.delete(args.tabUid);
}
@@ -1741,7 +1756,7 @@ export class MySQLClient extends BaseClient {
keysArr = keysArr ? [...keysArr, ...response] : response;
}
catch (err) {
if (isPool && args.autocommit) {
if (this.isPool && args.autocommit) {
(connection as mysql.PoolConnection).release();
this._runningConnections.delete(args.tabUid);
}
@@ -1759,7 +1774,7 @@ export class MySQLClient extends BaseClient {
keys: keysArr
});
}).catch((err) => {
if (isPool && args.autocommit) {
if (this.isPool && args.autocommit) {
(connection as mysql.PoolConnection).release();
this._runningConnections.delete(args.tabUid);
}
@@ -1776,7 +1791,7 @@ export class MySQLClient extends BaseClient {
});
}
if (isPool && args.autocommit) {
if (this.isPool && args.autocommit) {
(connection as mysql.PoolConnection).release();
this._runningConnections.delete(args.tabUid);
}

View File

@@ -88,8 +88,8 @@ export class PostgreSQLClient extends BaseClient {
private _keepaliveTimer: NodeJS.Timer;
private _keepaliveMs: number;
protected _connection?: pg.Client | pg.Pool;
private types: {[key: string]: string} = {};
private _arrayTypes: {[key: string]: string} = {
private types: Record<string, string> = {};
private _arrayTypes: Record<string, string> = {
_int2: 'SMALLINT',
_int4: 'INTEGER',
_int8: 'BIGINT',
@@ -656,7 +656,7 @@ export class PostgreSQLClient extends BaseClient {
let createSql = '';
const sequences = [];
const columnsSql = [];
const arrayTypes: {[key: string]: string} = {
const arrayTypes: Record<string, string> = {
_int2: 'smallint',
_int4: 'integer',
_int8: 'bigint',

View File

@@ -658,7 +658,7 @@ export class SQLiteClient extends BaseClient {
let queryAllResult: any[];
let affectedRows;
let fields;
const detectedTypes: {[key: string]: string} = {};
const detectedTypes: Record<string, string> = {};
try {
const stmt = connection.prepare(query);

View File

@@ -354,7 +354,7 @@ CREATE TABLE \`${view.Name}\`(
escapeAndQuote (val: string) {
// eslint-disable-next-line no-control-regex
const CHARS_TO_ESCAPE = /[\0\b\t\n\r\x1a"'\\]/g;
const CHARS_ESCAPE_MAP: {[key: string]: string} = {
const CHARS_ESCAPE_MAP: Record<string, string> = {
'\0': '\\0',
'\b': '\\b',
'\t': '\\t',

View File

@@ -59,7 +59,7 @@ SET row_security = off;\n\n\n`;
let createSql = '';
const sequences = [];
const columnsSql = [];
const arrayTypes: {[key: string]: string} = {
const arrayTypes: Record<string, string> = {
_int2: 'smallint',
_int4: 'integer',
_int8: 'bigint',
@@ -440,7 +440,7 @@ SET row_security = off;\n\n\n`;
escapeAndQuote (val: string) {
// eslint-disable-next-line no-control-regex
const CHARS_TO_ESCAPE = /[\0\b\t\n\r\x1a"'\\]/g;
const CHARS_ESCAPE_MAP: {[key: string]: string} = {
const CHARS_ESCAPE_MAP: Record<string, string> = {
'\0': '\\0',
'\b': '\\b',
'\t': '\\t',

View File

@@ -56,8 +56,8 @@ const { t } = useI18n();
const props = defineProps({
size: {
type: String as PropType<'small' | 'medium' | '400' | 'large'>,
validator: (prop: string) => ['small', 'medium', '400', 'large'].includes(prop),
type: String as PropType<'small' | 'medium' | '400' | 'large' | 'resize'>,
validator: (prop: string) => ['small', 'medium', '400', 'large', 'resize'].includes(prop),
default: 'small'
},
hideFooter: {
@@ -88,6 +88,8 @@ const modalSizeClass = computed(() => {
return 'modal-sm';
if (props.size === '400')
return 'modal-400';
if (props.size === 'resize')
return 'modal-resize';
else if (props.size === 'large')
return 'modal-lg';
else return '';
@@ -120,6 +122,12 @@ onBeforeUnmount(() => {
max-width: 400px;
}
.modal-resize .modal-container {
max-width: 95vw;
max-height: 95vh;
width: auto;
}
.modal.modal-sm .modal-container {
padding: 0;
}

View File

@@ -4,7 +4,11 @@
:id="`editor-${id}`"
class="editor"
:class="editorClass"
:style="{height: `${height}px`}"
:style="{
height: `${height}px`,
width: width ? `${width}px` : null,
resize: resizable ? 'both' : 'none'
}"
/>
</div>
</template>
@@ -17,7 +21,7 @@ import 'ace-builds/webpack-resolver';
import { uidGen } from 'common/libs/uidGen';
import { storeToRefs } from 'pinia';
import { onMounted, watch } from 'vue';
import { PropType, onMounted, watch } from 'vue';
import { useSettingsStore } from '@/stores/settings';
@@ -25,10 +29,12 @@ const props = defineProps({
modelValue: String,
mode: { type: String, default: 'text' },
editorClass: { type: String, default: '' },
resizable: { type: Boolean, default: false },
autoFocus: { type: Boolean, default: false },
readOnly: { type: Boolean, default: false },
showLineNumbers: { type: Boolean, default: true },
height: { type: Number, default: 200 }
height: { type: Number, default: 200 },
width: { type: [Number, Boolean] as PropType<number|false>, default: false }
});
const emit = defineEmits(['update:modelValue']);
const settingsStore = useSettingsStore();
@@ -132,8 +138,10 @@ onMounted(() => {
<style lang="scss" scoped>
.editor-wrapper {
.editor {
width: 100%;
}
.editor {
width: 100%;
height: 100%;
max-width: 90vw;
}
}
</style>

View File

@@ -113,7 +113,7 @@ const selectedGroup: Ref<string> = ref('manual');
const selectedMethod: Ref<string> = ref('');
const selectedValue: Ref<string> = ref('');
const debounceTimeout: Ref<NodeJS.Timeout> = ref(null);
const methodParams: Ref<{[key: string]: string}> = ref({});
const methodParams: Ref<Record<string, string>> = ref({});
const enumArray: Ref<string[]> = ref(null);
const fakerGroups = computed(() => {

View File

@@ -73,7 +73,7 @@ const props = defineProps({
const emit = defineEmits(['confirm', 'close']);
const firstInput: Ref<HTMLInputElement[]> = ref(null);
const values: Ref<{[key: string]: string}> = ref({});
const values: Ref<Record<string, string>> = ref({});
const inParameters = computed(() => {
return props.localRoutine.parameters.filter(param => param.context === 'IN');

View File

@@ -327,7 +327,7 @@ const tables: Ref<{
}[]> = ref([]);
const options: Ref<Partial<ExportOptions>> = ref({
schema: selectedSchema.value,
includes: {} as {[key: string]: boolean},
includes: {} as Record<string, boolean>,
outputFormat: 'sql' as 'sql' | 'sql.zip',
sqlInsertAfter: 250,
sqlInsertDivider: 'bytes' as 'bytes' | 'rows'

View File

@@ -142,7 +142,7 @@ const { getSelected: selectedWorkspace } = storeToRefs(workspacesStore);
const { trapRef } = useFocusTrap({ disableAutofocus: true });
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const localRow: Ref<{[key: string]: any}> = ref({});
const localRow: Ref<Record<string, any>> = ref({});
const fieldsToExclude = ref([]);
const nInserts = ref(1);
const isInserting = ref(false);
@@ -225,7 +225,7 @@ const insertRows = async () => {
delete rowToInsert[key];
});
const fieldTypes: {[key: string]: string} = {};
const fieldTypes: Record<string, string> = {};
props.fields.forEach(field => {
fieldTypes[field.name] = field.type;
});
@@ -290,7 +290,7 @@ onMounted(() => {
}
}, 50);
const rowObj: {[key: string]: unknown} = {};
const rowObj: Record<string, unknown> = {};
if (!props.rowToDuplicate) {
// Set default values

View File

@@ -115,7 +115,7 @@
<BaseIcon icon-name="mdiHistory" :size="48" />
</div>
<p class="empty-title h5">
{{ t('database.thereIsNoQueriesYet') }}
{{ t('database.thereAreNoQueriesYet') }}
</p>
</div>
</div>

View File

@@ -1,6 +1,6 @@
<template>
<ConfirmModal
size="medium"
size="resize"
:disable-autofocus="true"
:close-on-confirm="!!localNote.note.length"
:confirm-text="t('general.save')"
@@ -52,6 +52,10 @@
v-model="localNote.note"
:mode="editorMode"
:show-line-numbers="false"
:auto-focus="true"
:height="400"
:width="640"
:resizable="true"
/>
</div>
</form>

View File

@@ -1,6 +1,6 @@
<template>
<ConfirmModal
size="medium"
size="resize"
:disable-autofocus="true"
:close-on-confirm="!!newNote.note.length"
:confirm-text="t('general.save')"
@@ -52,6 +52,10 @@
v-model="newNote.note"
:mode="editorMode"
:show-line-numbers="false"
:auto-focus="true"
:height="400"
:width="640"
:resizable="true"
/>
</div>
</form>

View File

@@ -67,7 +67,7 @@ const props = defineProps({
const emit = defineEmits(['select-row', 'contextmenu', 'stop-refresh']);
const isInlineEditor: Ref<{[key: string]: boolean}> = ref({});
const isInlineEditor: Ref<Record<string, boolean>> = ref({});
const isInfoModal = ref(false);
const editorMode = ref('sql');

View File

@@ -256,9 +256,9 @@ const highlightWord = (string: string) => {
code, pre {
max-width: 100%;
width: 100%;
display: inline-block;
font-size: 100%;
// color: $primary-color;
opacity: 0.8;
font-weight: 600;
white-space: break-spaces;
@@ -342,6 +342,8 @@ const highlightWord = (string: string) => {
<style lang="scss">
.tile-paragraph {
white-space: initial;
word-break: break-word;
user-select: text;
h1, h2, h3, h4, h5, h6, p, li {
margin: 0;

View File

@@ -32,7 +32,7 @@ const { removeNotification } = notificationsStore;
const { notifications } = storeToRefs(notificationsStore);
const { notificationsTimeout } = storeToRefs(settingsStore);
const timeouts: Ref<{[key: string]: NodeJS.Timeout}> = ref({});
const timeouts: Ref<Record<string, NodeJS.Timeout>> = ref({});
const latestNotifications = computed(() => notifications.value.slice(0, 10));

View File

@@ -695,7 +695,7 @@ const openAsPermanentTab = (tab: WorkspaceTab) => {
routine: 'routine-props',
procedure: 'routine-props',
scheduler: 'scheduler-props'
} as {[key: string]: string};
} as Record<string, string>;
newTab({
uid: props.connection.uid,

View File

@@ -572,11 +572,11 @@ const pathSelection = (event: Event & {target: {files: {path: string}[]}}, name:
const { files } = event.target;
if (!files.length) return;
(connection.value as unknown as {[key: string]: string})[name] = files[0].path as string;
(connection.value as unknown as Record<string, string>)[name] = files[0].path as string;
};
const pathClear = (name: keyof ConnectionParams) => {
(connection.value as unknown as {[key: string]: string})[name] = '';
(connection.value as unknown as Record<string, string>)[name] = '';
};
setDefaults();

View File

@@ -569,11 +569,11 @@ const pathSelection = (event: Event & {target: {files: {path: string}[]}}, name:
const { files } = event.target;
if (!files.length) return;
(localConnection.value as unknown as {[key: string]: string})[name] = files[0].path;
(localConnection.value as unknown as Record<string, string>)[name] = files[0].path;
};
const pathClear = (name: keyof ConnectionParams) => {
(localConnection.value as unknown as {[key: string]: string})[name] = '';
(localConnection.value as unknown as Record<string, string>)[name] = '';
};
localConnection.value = JSON.parse(JSON.stringify(props.connection));

View File

@@ -382,7 +382,7 @@ const getFieldsData = async () => {
if (status === 'success') {
const indexesObj = response
.filter((index: TableIndex) => index.type !== 'FOREIGN KEY')
.reduce((acc: {[key: string]: TableIndex[]}, curr: TableIndex) => {
.reduce((acc: Record<string, TableIndex[]>, curr: TableIndex) => {
acc[curr.name] = acc[curr.name] || [];
acc[curr.name].push(curr);
return acc;

View File

@@ -258,7 +258,7 @@ const indexesPanel: Ref<HTMLDivElement> = ref(null);
const foreignProxy = ref([]);
const selectedForeignID = ref('');
const modalInnerHeight = ref(400);
const refFields = ref({} as {[key: string]: TableField[]});
const refFields = ref({} as Record<string, TableField[]>);
const foreignActions = computed(() => props.workspace.customizations.foreignActions);
const selectedForeignObj = computed(() => foreignProxy.value.find(foreign => foreign._antares_id === selectedForeignID.value));

View File

@@ -382,7 +382,7 @@ const isBaseSelectField = computed(() => {
});
const enumArray = computed(() => {
if (props.fields[editingField.value] && props.fields[editingField.value].enumValues)
if (props.fields[editingField.value] && props.fields[editingField.value].enumValues && props.fields[editingField.value].type !== 'SET')
return props.fields[editingField.value].enumValues.replaceAll('\'', '').split(',');
return false;
});

View File

@@ -28,7 +28,7 @@ export function useFilters () {
return `(${num})`;
};
const parseKeys = (keys: {[key: number]: string}[]) => {
const parseKeys = (keys: Record<number, string>[]) => {
const isMacOS = process.platform === 'darwin';
return (keys as string[]).map(k => (
k.split('+')

View File

@@ -232,7 +232,7 @@ export const caES = {
newFunction: 'Nova funció',
newScheduler: 'Nou planificador',
newTriggerFunction: 'Nova funció de disparador',
thereIsNoQueriesYet: 'Encara no hi ha consultes',
thereAreNoQueriesYet: 'Encara no hi ha consultes',
searchForQueries: 'Cerca consultes',
killProcess: 'Mata procés',
exportSchema: 'Exporta esquema',

View File

@@ -233,7 +233,7 @@ export const csCZ = {
newFunction: 'Nová funkce',
newScheduler: 'Nový scheduler',
newTriggerFunction: 'Nová trigger funkce',
thereIsNoQueriesYet: 'Nejsou tu žádné dotazy',
thereAreNoQueriesYet: 'Nejsou tu žádné dotazy',
searchForQueries: 'Hledání dotazů',
killProcess: 'Zabít proces',
exportSchema: 'Exportovat schéma',

View File

@@ -251,7 +251,7 @@ export const enUS = {
newFunction: 'New function',
newScheduler: 'New scheduler',
newTriggerFunction: 'New trigger function',
thereIsNoQueriesYet: 'There is no queries yet',
thereAreNoQueriesYet: 'There are no queries yet',
searchForQueries: 'Search for queries',
killProcess: 'Kill process',
exportSchema: 'Export schema',

View File

@@ -229,7 +229,7 @@ export const frFR = {
newFunction: 'Nouvelle function',
newScheduler: 'Nouveau déclencheur',
newTriggerFunction: 'Nouvelle fonction de déclencheur',
thereIsNoQueriesYet: 'Il n\'y a pas encore de requête',
thereAreNoQueriesYet: 'Il n\'y a pas encore de requête',
searchForQueries: 'Rechercher des requêtes',
killProcess: 'Terminer un processus',
exportSchema: 'Exporter un schéma',

View File

@@ -227,7 +227,7 @@ export const idID = {
newFunction: 'Fungsi baru',
newScheduler: 'Penjadwal baru',
newTriggerFunction: 'Fungsi pemicu baru',
thereIsNoQueriesYet: 'Belum ada kueri',
thereAreNoQueriesYet: 'Belum ada kueri',
searchForQueries: 'Telusuri kueri',
killProcess: 'Membunuh proses',
exportSchema: 'Skema ekspor',

View File

@@ -228,7 +228,7 @@ export const itIT = {
newFunction: 'Nuova funzione',
newScheduler: 'Nuovo scheduler',
newTriggerFunction: 'Nuova funzione di trigger',
thereIsNoQueriesYet: 'Non ci sono ancora query',
thereAreNoQueriesYet: 'Non ci sono ancora query',
searchForQueries: 'Cerca query',
killProcess: 'Uccidi processo',
exportSchema: 'Esporta schema',

View File

@@ -198,7 +198,7 @@ export const jaJP = {
newFunction: '新しい関数',
newScheduler: '新規スケジューラ',
newTriggerFunction: '新しいトリガー機能',
thereIsNoQueriesYet: 'まだ問い合わせはありません',
thereAreNoQueriesYet: 'まだ問い合わせはありません',
searchForQueries: 'クエリの検索',
killProcess: 'プロセスの停止'
},

View File

@@ -228,7 +228,7 @@ export const koKR = {
newFunction: '새 함수',
newScheduler: '새 스케줄러',
newTriggerFunction: '새 트리거 함수',
thereIsNoQueriesYet: '아직 쿼리가 없습니다',
thereAreNoQueriesYet: '아직 쿼리가 없습니다',
searchForQueries: '쿼리 검색',
killProcess: '프로세스 종료',
exportSchema: '스키마 내보내기',

View File

@@ -100,7 +100,7 @@ export const nlNL = {
readOnlyMode: 'Alleen lezen modus',
untrustedConnection: 'Niet vertrouwde verbinding',
allConnections: 'Alle verbindingen',
searchForConnections: 'Search for connections'
searchForConnections: 'Zoek naar verbindingen'
},
database: {
schema: 'Schema',
@@ -233,8 +233,8 @@ export const nlNL = {
newFunction: 'Nieuwe function',
newScheduler: 'Nieuwe scheduler',
newTriggerFunction: 'Nieuwe trigger function',
thereIsNoQueriesYet: 'There is no queries yet',
searchForQueries: 'Search for queries',
thereAreNoQueriesYet: 'Er zijn nog geen queries',
searchForQueries: 'Zoek naar queries',
killProcess: 'Sluit proces af',
exportSchema: 'Exporteer schema',
importSchema: 'Importeer schema',

View File

@@ -231,7 +231,7 @@ export const ptBR = {
newFunction: 'Nova função',
newScheduler: 'Novo agendento',
newTriggerFunction: 'Novo gatinho de função',
thereIsNoQueriesYet: 'Nenhuma consulta ainda',
thereAreNoQueriesYet: 'Nenhuma consulta ainda',
searchForQueries: 'Pesquisar por consultas',
killProcess: 'Matar processo',
exportSchema: 'Exportar banco de dados',

View File

@@ -229,7 +229,7 @@ export const ruRU = {
newFunction: 'Новая функция',
newScheduler: 'Новый планировщик',
newTriggerFunction: 'Новая функция триггера',
thereIsNoQueriesYet: 'Запросы пока отсутствуют',
thereAreNoQueriesYet: 'Запросы пока отсутствуют',
searchForQueries: 'Поиск по запросам',
killProcess: 'Убить процесс',
exportSchema: 'Экспорт схемы',

View File

@@ -1,4 +1,4 @@
export const localesNames: {[key: string]: string} = {
export const localesNames: Record<string, string> = {
'en-US': 'English',
'it-IT': 'Italiano',
'ar-SA': 'العربية',

View File

@@ -238,7 +238,7 @@ export const ukUA = {
newFunction: 'Нова функція',
newScheduler: 'Новий планувальник',
newTriggerFunction: 'Нова функція тригера',
thereIsNoQueriesYet: 'Поки що немає запитів',
thereAreNoQueriesYet: 'Поки що немає запитів',
searchForQueries: 'Пошук запитів',
killProcess: 'Завершити процес',
exportSchema: 'Експорт схеми',

View File

@@ -222,7 +222,7 @@ export const viVN = {
newFunction: 'Chức năng mới',
newScheduler: 'Bộ lập lịch mới',
newTriggerFunction: 'Chức năng kích hoạt mới',
thereIsNoQueriesYet: 'Không có truy vấn nào',
thereAreNoQueriesYet: 'Không có truy vấn nào',
searchForQueries: 'Tìm kiếm truy vấn',
killProcess: 'Huỷ quá trình',
exportSchema: 'Xuất lược đồ',

View File

@@ -236,7 +236,7 @@ export const zhCN = {
newFunction: '新函数',
newScheduler: '新调度器',
newTriggerFunction: '新触发器函数',
thereIsNoQueriesYet: '目前还没有任何查询',
thereAreNoQueriesYet: '目前还没有任何查询',
searchForQueries: '搜索查询',
killProcess: '终止进程',
exportSchema: '导出模式(schema)',

View File

@@ -64,7 +64,7 @@ export default class {
primary?: string;
field: string;
// eslint-disable-next-line @typescript-eslint/no-explicit-any
rows: {[key: string]: any};
rows: Record<string, any>;
}): Promise<IpcResponse> {
return ipcRenderer.invoke('delete-table-rows', unproxify(params));
}
@@ -73,9 +73,9 @@ export default class {
uid: string;
schema: string;
table: string;
row: {[key: string]: string | number | boolean | Date | Buffer};
row: Record<string, string | number | boolean | Date | Buffer>;
repeat: number;
fields: {[key: string]: string};
fields: Record<string, string>;
locale: string;
}): Promise<IpcResponse> {
return ipcRenderer.invoke('insert-table-fake-rows', unproxify(params));

View File

@@ -13,7 +13,7 @@ export interface HistoryRecord {
export const useHistoryStore = defineStore('history', {
state: () => ({
history: persistentStore.get('history', {}) as {[key: string]: HistoryRecord[]},
history: persistentStore.get('history', {}) as Record<string, HistoryRecord[]>,
favorites: persistentStore.get('favorites', {})
}),
getters: {

View File

@@ -86,11 +86,11 @@ export interface Workspace {
arch: string;
os: string;
};
engines?: {[key: string]: string | boolean | number}[];
engines?: Record<string, string | boolean | number>[];
}
const persistentStore = new Store({ name: 'tabs' });
const tabIndex: {[key: string]: number} = {};
const tabIndex: Record<string, number> = {};
export const useWorkspacesStore = defineStore('workspaces', {
state: () => ({