admindb 2.2.1 → 2.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +368 -364
- package/dist/app.js +25 -16
- package/dist/auth/config.d.ts +1 -1
- package/dist/auth/config.js +18 -30
- package/dist/auth/crypto.js +11 -21
- package/dist/auth/middleware.js +9 -16
- package/dist/auth/routes.js +9 -15
- package/dist/cli/args.d.ts +1 -1
- package/dist/cli/args.js +20 -29
- package/dist/cli/config.d.ts +1 -1
- package/dist/cli/config.js +15 -18
- package/dist/cli/runner.js +13 -13
- package/dist/data/chain.d.ts +1 -1
- package/dist/data/chain.js +40 -617
- package/dist/data/datasets.d.ts +1 -1
- package/dist/data/datasets.js +43 -1
- package/dist/data/detector.js +45 -21
- package/dist/data/engine.d.ts +1 -1
- package/dist/data/engine.js +483 -142
- package/dist/data/index.d.ts +1 -1
- package/dist/data/index.js +8 -0
- package/dist/data/prng.d.ts +1 -0
- package/dist/data/prng.js +73 -0
- package/dist/data/registry.d.ts +1 -0
- package/dist/data/registry.js +805 -0
- package/dist/data/schema-graph.d.ts +1 -0
- package/dist/data/schema-graph.js +210 -0
- package/dist/data/strategies.d.ts +1 -1
- package/dist/data/strategies.js +44 -283
- package/dist/data/templates.d.ts +1 -0
- package/dist/data/templates.js +134 -0
- package/dist/data/types.d.ts +1 -1
- package/dist/data/validator.d.ts +1 -0
- package/dist/data/validator.js +160 -0
- package/dist/db/database.js +9 -14
- package/dist/db/export.js +32 -55
- package/dist/db/filters.d.ts +1 -1
- package/dist/db/filters.js +15 -38
- package/dist/db/introspection.js +4 -7
- package/dist/db/manager.d.ts +1 -1
- package/dist/db/manager.js +48 -53
- package/dist/db/migrations.js +7 -14
- package/dist/db/postgres.js +30 -78
- package/dist/index.d.ts +1 -1
- package/dist/public/css/app.css +1 -1
- package/dist/public/js/browse.js +1 -1
- package/dist/public/js/inspector.js +1 -1
- package/dist/public/js/query.js +1 -1
- package/dist/public/js/seed.js +1 -1
- package/dist/routes/api/erd.js +3 -8
- package/dist/routes/api/helpers.d.ts +1 -1
- package/dist/routes/api/helpers.js +75 -25
- package/dist/routes/api/import-export.js +1 -2
- package/dist/routes/api/query.js +2 -6
- package/dist/routes/api/rows.js +4 -8
- package/dist/routes/api/seed.js +35 -21
- package/dist/routes/api/tables.js +15 -30
- package/dist/routes/databases.js +8 -26
- package/dist/routes/pages.js +67 -18
- package/dist/serverless.js +31 -44
- package/dist/sql/generator.d.ts +1 -1
- package/dist/sql/generator.js +35 -109
- package/dist/types/api.d.ts +1 -1
- package/dist/utils/common.d.ts +1 -1
- package/dist/utils/common.js +45 -85
- package/dist/utils/csv.d.ts +1 -1
- package/dist/utils/csv.js +11 -17
- package/dist/utils/datatype.js +46 -60
- package/dist/utils/icons.js +8 -8
- package/dist/views/layouts/main.hbs +1 -87
- package/dist/views/pages/databases.hbs +1 -233
- package/dist/views/pages/designer.hbs +1 -89
- package/dist/views/pages/erd.hbs +1 -1241
- package/dist/views/pages/error.hbs +1 -10
- package/dist/views/pages/form.hbs +1 -59
- package/dist/views/pages/home.hbs +1 -220
- package/dist/views/pages/info.hbs +1 -107
- package/dist/views/pages/login.hbs +1 -98
- package/dist/views/pages/query.hbs +1 -129
- package/dist/views/pages/schema.hbs +1 -298
- package/dist/views/pages/seed-select.hbs +251 -0
- package/dist/views/pages/seed.hbs +256 -414
- package/dist/views/pages/table.hbs +1 -327
- package/dist/views/partials/navbar.hbs +1 -83
- package/dist/views/partials/sidebar.hbs +1 -129
- package/docs/API.md +149 -149
- package/docs/EXAMPLES.md +579 -579
- package/docs/SECURITY.md +265 -265
- package/package.json +80 -79
package/dist/sql/generator.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.DESIGNER_TYPES = exports.POSTGRES_DESIGNER_TYPES = exports.SQLITE_DESIGNER_TYPES = void 0;
|
|
4
|
-
exports.getDesignerTypes = getDesignerTypes;
|
|
5
|
-
exports.quoteIdentifier = quoteIdentifier;
|
|
6
|
-
exports.escapeString = escapeString;
|
|
3
|
+
exports.escapeString = exports.quoteIdentifier = exports.getDesignerTypes = exports.DESIGNER_TYPES = exports.POSTGRES_DESIGNER_TYPES = exports.SQLITE_DESIGNER_TYPES = void 0;
|
|
7
4
|
exports.sqlValue = sqlValue;
|
|
8
5
|
exports.mapColumnType = mapColumnType;
|
|
9
6
|
exports.formatSqlDefault = formatSqlDefault;
|
|
@@ -18,79 +15,18 @@ exports.generateCreateIndex = generateCreateIndex;
|
|
|
18
15
|
exports.generateDropIndex = generateDropIndex;
|
|
19
16
|
exports.generateInsert = generateInsert;
|
|
20
17
|
exports.generateUpdate = generateUpdate;
|
|
21
|
-
exports.SQLITE_DESIGNER_TYPES = [
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
'BOOLEAN',
|
|
27
|
-
'DATE',
|
|
28
|
-
'DATETIME',
|
|
29
|
-
'NUMERIC',
|
|
30
|
-
'VARCHAR(255)',
|
|
31
|
-
'JSON',
|
|
32
|
-
];
|
|
33
|
-
exports.POSTGRES_DESIGNER_TYPES = [
|
|
34
|
-
'TEXT',
|
|
35
|
-
'VARCHAR(255)',
|
|
36
|
-
'INTEGER',
|
|
37
|
-
'BIGINT',
|
|
38
|
-
'SMALLINT',
|
|
39
|
-
'SERIAL',
|
|
40
|
-
'BIGSERIAL',
|
|
41
|
-
'REAL',
|
|
42
|
-
'DECIMAL(10,2)',
|
|
43
|
-
'NUMERIC',
|
|
44
|
-
'BOOLEAN',
|
|
45
|
-
'DATE',
|
|
46
|
-
'DATETIME',
|
|
47
|
-
'TIMESTAMP',
|
|
48
|
-
'TIMESTAMPTZ',
|
|
49
|
-
'TIME',
|
|
50
|
-
'INTERVAL',
|
|
51
|
-
'JSON',
|
|
52
|
-
'JSONB',
|
|
53
|
-
'UUID',
|
|
54
|
-
'BYTEA',
|
|
55
|
-
'INET',
|
|
56
|
-
];
|
|
57
|
-
exports.DESIGNER_TYPES = [
|
|
58
|
-
'TEXT',
|
|
59
|
-
'VARCHAR(255)',
|
|
60
|
-
'INTEGER',
|
|
61
|
-
'BIGINT',
|
|
62
|
-
'SMALLINT',
|
|
63
|
-
'SERIAL',
|
|
64
|
-
'BIGSERIAL',
|
|
65
|
-
'REAL',
|
|
66
|
-
'DECIMAL(10,2)',
|
|
67
|
-
'NUMERIC',
|
|
68
|
-
'BOOLEAN',
|
|
69
|
-
'DATE',
|
|
70
|
-
'DATETIME',
|
|
71
|
-
'TIMESTAMP',
|
|
72
|
-
'TIMESTAMPTZ',
|
|
73
|
-
'TIME',
|
|
74
|
-
'INTERVAL',
|
|
75
|
-
'JSON',
|
|
76
|
-
'JSONB',
|
|
77
|
-
'UUID',
|
|
78
|
-
'BYTEA',
|
|
79
|
-
'BLOB',
|
|
80
|
-
'INET',
|
|
81
|
-
];
|
|
82
|
-
function getDesignerTypes(dialect = 'sqlite') {
|
|
83
|
-
return dialect === 'postgres' ? exports.POSTGRES_DESIGNER_TYPES : exports.SQLITE_DESIGNER_TYPES;
|
|
84
|
-
}
|
|
18
|
+
exports.SQLITE_DESIGNER_TYPES = ['TEXT', 'INTEGER', 'REAL', 'BLOB', 'BOOLEAN', 'DATE', 'DATETIME', 'NUMERIC', 'VARCHAR(255)', 'JSON',];
|
|
19
|
+
exports.POSTGRES_DESIGNER_TYPES = ['TEXT', 'VARCHAR(255)', 'INTEGER', 'BIGINT', 'SMALLINT', 'SERIAL', 'BIGSERIAL', 'REAL', 'DECIMAL(10,2)', 'NUMERIC', 'BOOLEAN', 'DATE', 'DATETIME', 'TIMESTAMP', 'TIMESTAMPTZ', 'TIME', 'INTERVAL', 'JSON', 'JSONB', 'UUID', 'BYTEA', 'INET',];
|
|
20
|
+
exports.DESIGNER_TYPES = ['TEXT', 'VARCHAR(255)', 'INTEGER', 'BIGINT', 'SMALLINT', 'SERIAL', 'BIGSERIAL', 'REAL', 'DECIMAL(10,2)', 'NUMERIC', 'BOOLEAN', 'DATE', 'DATETIME', 'TIMESTAMP', 'TIMESTAMPTZ', 'TIME', 'INTERVAL', 'JSON', 'JSONB', 'UUID', 'BYTEA', 'BLOB', 'INET',];
|
|
21
|
+
const getDesignerTypes = (dialect = 'sqlite') => dialect === 'postgres' ? exports.POSTGRES_DESIGNER_TYPES : exports.SQLITE_DESIGNER_TYPES;
|
|
22
|
+
exports.getDesignerTypes = getDesignerTypes;
|
|
85
23
|
const IDENTIFIER_RE = /^[A-Za-z_][A-Za-z0-9_$]*$/;
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return `'${String(value).replace(/'/g, "''")}'`;
|
|
91
|
-
}
|
|
24
|
+
const quoteIdentifier = (identifier) => `"${String(identifier).replace(/"/g, '""')}"`;
|
|
25
|
+
exports.quoteIdentifier = quoteIdentifier;
|
|
26
|
+
const escapeString = (value) => `'${String(value).replace(/'/g, "''")}'`;
|
|
27
|
+
exports.escapeString = escapeString;
|
|
92
28
|
function sqlValue(value) {
|
|
93
|
-
if (value
|
|
29
|
+
if (value == null)
|
|
94
30
|
return 'NULL';
|
|
95
31
|
if (typeof value === 'number')
|
|
96
32
|
return Number.isFinite(value) ? String(value) : 'NULL';
|
|
@@ -101,8 +37,8 @@ function sqlValue(value) {
|
|
|
101
37
|
if (value instanceof Uint8Array)
|
|
102
38
|
return `X'${Buffer.from(value).toString('hex')}'`;
|
|
103
39
|
if (value instanceof Date)
|
|
104
|
-
return escapeString(value.toISOString());
|
|
105
|
-
return escapeString(String(value));
|
|
40
|
+
return (0, exports.escapeString)(value.toISOString());
|
|
41
|
+
return (0, exports.escapeString)(String(value));
|
|
106
42
|
}
|
|
107
43
|
function mapColumnType(type) {
|
|
108
44
|
const t = String(type ?? '').trim().toUpperCase();
|
|
@@ -151,12 +87,10 @@ function mapColumnType(type) {
|
|
|
151
87
|
case 'TIMESTAMPTZ':
|
|
152
88
|
case 'TIMESTAMP WITHOUT TIME ZONE':
|
|
153
89
|
case 'TIMESTAMP WITH TIME ZONE':
|
|
154
|
-
return { sqlType: t };
|
|
155
90
|
case 'TIME':
|
|
156
91
|
case 'TIMETZ':
|
|
157
92
|
case 'TIME WITHOUT TIME ZONE':
|
|
158
93
|
case 'TIME WITH TIME ZONE':
|
|
159
|
-
return { sqlType: t };
|
|
160
94
|
case 'INTERVAL':
|
|
161
95
|
case 'JSON':
|
|
162
96
|
case 'JSONB':
|
|
@@ -164,14 +98,10 @@ function mapColumnType(type) {
|
|
|
164
98
|
case 'INET':
|
|
165
99
|
case 'CIDR':
|
|
166
100
|
case 'MACADDR':
|
|
167
|
-
case 'SERIAL':
|
|
168
|
-
case 'BIGSERIAL':
|
|
169
|
-
case 'SMALLSERIAL':
|
|
170
101
|
return { sqlType: t };
|
|
171
102
|
default:
|
|
172
|
-
if (/^[A-Za-z0-9_(),\s\[\]]+$/.test(t))
|
|
103
|
+
if (/^[A-Za-z0-9_(),\s\[\]]+$/.test(t))
|
|
173
104
|
return { sqlType: t };
|
|
174
|
-
}
|
|
175
105
|
throw new Error(`Unsupported column type: "${type}". Supported types include: ${exports.DESIGNER_TYPES.join(', ')} or standard SQL types.`);
|
|
176
106
|
}
|
|
177
107
|
}
|
|
@@ -194,15 +124,13 @@ function formatSqlDefault(raw, colType) {
|
|
|
194
124
|
return s;
|
|
195
125
|
if (/^(true|false)$/i.test(s))
|
|
196
126
|
return s.toUpperCase() === 'TRUE' ? '1' : '0';
|
|
197
|
-
if (/^'.*'$/s.test(s) || /^".*"$/s.test(s))
|
|
198
|
-
return s;
|
|
199
|
-
if (/^[a-zA-Z_]\w*\s*\(.*\)$/s.test(s))
|
|
127
|
+
if (/^'.*'$/s.test(s) || /^".*"$/s.test(s) || /^[a-zA-Z_]\w*\s*\(.*\)$/s.test(s))
|
|
200
128
|
return s;
|
|
201
129
|
if (/^CURRENT_TIMESTAMP|CURRENT_DATE|CURRENT_TIME$/i.test(s))
|
|
202
130
|
return s.toUpperCase();
|
|
203
131
|
const t = (colType || '').toUpperCase();
|
|
204
132
|
if (t.includes('TEXT') || t.includes('CHAR') || t.includes('CLOB') || t.includes('VARCHAR') || t.includes('STRING')) {
|
|
205
|
-
return escapeString(s);
|
|
133
|
+
return (0, exports.escapeString)(s);
|
|
206
134
|
}
|
|
207
135
|
return s;
|
|
208
136
|
}
|
|
@@ -218,7 +146,7 @@ function renderColumnDef(col, opts = {}) {
|
|
|
218
146
|
throw new Error(`Cannot add "${colName}" with UNIQUE: ALTER TABLE ADD COLUMN does not support UNIQUE constraints.`);
|
|
219
147
|
}
|
|
220
148
|
}
|
|
221
|
-
let def = `${quoteIdentifier(colName)} ${mapped.sqlType}`;
|
|
149
|
+
let def = `${(0, exports.quoteIdentifier)(colName)} ${mapped.sqlType}`;
|
|
222
150
|
if (!opts.forAddColumn && col.primaryKey)
|
|
223
151
|
def += ' PRIMARY KEY';
|
|
224
152
|
if (col.notNull)
|
|
@@ -227,8 +155,9 @@ function renderColumnDef(col, opts = {}) {
|
|
|
227
155
|
def += ' UNIQUE';
|
|
228
156
|
const rawDefault = formatSqlDefault(String(col.defaultValue ?? ''), col.type);
|
|
229
157
|
const dflt = rawDefault || mapped.defaultAuto || '';
|
|
230
|
-
if (dflt)
|
|
158
|
+
if (dflt) {
|
|
231
159
|
def += ` DEFAULT ${dflt}`;
|
|
160
|
+
}
|
|
232
161
|
else if (opts.forAddColumn && col.notNull) {
|
|
233
162
|
throw new Error(`Cannot add "${colName}" as NOT NULL without a default value.`);
|
|
234
163
|
}
|
|
@@ -236,7 +165,7 @@ function renderColumnDef(col, opts = {}) {
|
|
|
236
165
|
if (opts.forAddColumn && (col.notNull || dflt)) {
|
|
237
166
|
throw new Error(`Cannot add "${colName}" as a foreign key with NOT NULL or a non-NULL default.`);
|
|
238
167
|
}
|
|
239
|
-
def += ` REFERENCES ${quoteIdentifier(col.foreignKey.table)}(${quoteIdentifier(col.foreignKey.column)})`;
|
|
168
|
+
def += ` REFERENCES ${(0, exports.quoteIdentifier)(col.foreignKey.table)}(${(0, exports.quoteIdentifier)(col.foreignKey.column)})`;
|
|
240
169
|
}
|
|
241
170
|
return def;
|
|
242
171
|
}
|
|
@@ -256,38 +185,37 @@ function generateCreateTable(tableName, columns) {
|
|
|
256
185
|
throw new Error(`Duplicate column name: "${colName}".`);
|
|
257
186
|
seen.add(colName);
|
|
258
187
|
if (col.primaryKey)
|
|
259
|
-
pkCount
|
|
188
|
+
pkCount++;
|
|
260
189
|
parts.push(renderColumnDef(col));
|
|
261
190
|
}
|
|
262
191
|
if (pkCount > 1)
|
|
263
192
|
throw new Error('At most one primary key column is allowed.');
|
|
264
|
-
return `CREATE TABLE ${quoteIdentifier(table)} (\n ${parts.join(',\n ')}\n);`;
|
|
193
|
+
return `CREATE TABLE ${(0, exports.quoteIdentifier)(table)} (\n ${parts.join(',\n ')}\n);`;
|
|
265
194
|
}
|
|
266
195
|
function generateAddColumn(tableName, col) {
|
|
267
196
|
const table = String(tableName ?? '').trim();
|
|
268
197
|
validateIdentifier('table', table);
|
|
269
|
-
|
|
270
|
-
return `ALTER TABLE ${quoteIdentifier(table)} ADD COLUMN ${def};`;
|
|
198
|
+
return `ALTER TABLE ${(0, exports.quoteIdentifier)(table)} ADD COLUMN ${renderColumnDef(col, { forAddColumn: true })};`;
|
|
271
199
|
}
|
|
272
200
|
function generateRenameTable(tableName, newName) {
|
|
273
201
|
validateIdentifier('table', tableName);
|
|
274
202
|
validateIdentifier('table', newName);
|
|
275
|
-
return `ALTER TABLE ${quoteIdentifier(tableName)} RENAME TO ${quoteIdentifier(newName)};`;
|
|
203
|
+
return `ALTER TABLE ${(0, exports.quoteIdentifier)(tableName)} RENAME TO ${(0, exports.quoteIdentifier)(newName)};`;
|
|
276
204
|
}
|
|
277
205
|
function generateRenameColumn(tableName, oldName, newName) {
|
|
278
206
|
validateIdentifier('table', tableName);
|
|
279
207
|
validateIdentifier('column', oldName);
|
|
280
208
|
validateIdentifier('column', newName);
|
|
281
|
-
return `ALTER TABLE ${quoteIdentifier(tableName)} RENAME COLUMN ${quoteIdentifier(oldName)} TO ${quoteIdentifier(newName)};`;
|
|
209
|
+
return `ALTER TABLE ${(0, exports.quoteIdentifier)(tableName)} RENAME COLUMN ${(0, exports.quoteIdentifier)(oldName)} TO ${(0, exports.quoteIdentifier)(newName)};`;
|
|
282
210
|
}
|
|
283
211
|
function generateDropColumn(tableName, column) {
|
|
284
212
|
validateIdentifier('table', tableName);
|
|
285
213
|
validateIdentifier('column', column);
|
|
286
|
-
return `ALTER TABLE ${quoteIdentifier(tableName)} DROP COLUMN ${quoteIdentifier(column)};`;
|
|
214
|
+
return `ALTER TABLE ${(0, exports.quoteIdentifier)(tableName)} DROP COLUMN ${(0, exports.quoteIdentifier)(column)};`;
|
|
287
215
|
}
|
|
288
216
|
function generateDropTable(tableName) {
|
|
289
217
|
validateIdentifier('table', tableName);
|
|
290
|
-
return `DROP TABLE ${quoteIdentifier(tableName)};`;
|
|
218
|
+
return `DROP TABLE ${(0, exports.quoteIdentifier)(tableName)};`;
|
|
291
219
|
}
|
|
292
220
|
function generateCreateIndex(tableName, index) {
|
|
293
221
|
validateIdentifier('table', tableName);
|
|
@@ -297,23 +225,21 @@ function generateCreateIndex(tableName, index) {
|
|
|
297
225
|
for (const c of cols)
|
|
298
226
|
validateIdentifier('column', c);
|
|
299
227
|
const unique = index.unique ? 'UNIQUE ' : '';
|
|
300
|
-
|
|
301
|
-
if (!name)
|
|
302
|
-
name = `idx_${tableName}_${cols.join('_')}`;
|
|
228
|
+
const name = String(index?.name ?? '').trim() || `idx_${tableName}_${cols.join('_')}`;
|
|
303
229
|
validateIdentifier('index', name);
|
|
304
|
-
return `CREATE ${unique}INDEX ${quoteIdentifier(name)} ON ${quoteIdentifier(tableName)} (${cols.map(quoteIdentifier).join(', ')});`;
|
|
230
|
+
return `CREATE ${unique}INDEX ${(0, exports.quoteIdentifier)(name)} ON ${(0, exports.quoteIdentifier)(tableName)} (${cols.map(exports.quoteIdentifier).join(', ')});`;
|
|
305
231
|
}
|
|
306
232
|
function generateDropIndex(indexName) {
|
|
307
233
|
validateIdentifier('index', indexName);
|
|
308
|
-
return `DROP INDEX ${quoteIdentifier(indexName)};`;
|
|
234
|
+
return `DROP INDEX ${(0, exports.quoteIdentifier)(indexName)};`;
|
|
309
235
|
}
|
|
310
236
|
function generateInsert(table, fields) {
|
|
311
|
-
const cols = fields.map((f) => quoteIdentifier(f.column));
|
|
237
|
+
const cols = fields.map((f) => (0, exports.quoteIdentifier)(f.column));
|
|
312
238
|
const vals = fields.map((f) => sqlValue(f.value));
|
|
313
|
-
return `INSERT INTO ${quoteIdentifier(table)} (${cols.join(', ')}) VALUES (${vals.join(', ')});`;
|
|
239
|
+
return `INSERT INTO ${(0, exports.quoteIdentifier)(table)} (${cols.join(', ')}) VALUES (${vals.join(', ')});`;
|
|
314
240
|
}
|
|
315
241
|
function generateUpdate(table, fields, where) {
|
|
316
|
-
const sets = fields.map((f) => `${quoteIdentifier(f.column)} = ${sqlValue(f.value)}`);
|
|
317
|
-
const conds = where.map((w) => `${quoteIdentifier(w.column)} = ${sqlValue(w.value)}`);
|
|
318
|
-
return `UPDATE ${quoteIdentifier(table)} SET ${sets.join(', ')} WHERE ${conds.join(' AND ')};`;
|
|
242
|
+
const sets = fields.map((f) => `${(0, exports.quoteIdentifier)(f.column)} = ${sqlValue(f.value)}`);
|
|
243
|
+
const conds = where.map((w) => `${(0, exports.quoteIdentifier)(w.column)} = ${sqlValue(w.value)}`);
|
|
244
|
+
return `UPDATE ${(0, exports.quoteIdentifier)(table)} SET ${sets.join(', ')} WHERE ${conds.join(' AND ')};`;
|
|
319
245
|
}
|
package/dist/types/api.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import type {ColumnInfo,ForeignKeyInfo,IndexInfo,SavedQuery,SchemaInfo,TableInfoData,TableListItem,WhereClause,RowFilters} from '../db/database';import type {DatabaseEntry} from '../db/manager';import type {ColumnDef,IndexDef} from '../sql/generator';import type {ColumnGeneratorConfig,ColumnPlan,GeneratorStrategyId,StrategyDescriptor,StrategyCategory} from '../data/index';export type {ColumnInfo,ForeignKeyInfo,IndexInfo,SavedQuery,SchemaInfo,TableInfoData,TableListItem,WhereClause,RowFilters,ColumnDef,IndexDef,ColumnGeneratorConfig,ColumnPlan,GeneratorStrategyId,StrategyDescriptor,StrategyCategory
|
|
1
|
+
import type {ColumnInfo,ForeignKeyInfo,IndexInfo,SavedQuery,SchemaInfo,TableInfoData,TableListItem,WhereClause,RowFilters} from '../db/database';import type {DatabaseEntry} from '../db/manager';import type {ColumnDef,IndexDef} from '../sql/generator';import type {ColumnGeneratorConfig,ColumnPlan,GeneratorStrategyId,StrategyDescriptor,StrategyCategory} from '../data/index';export type {ColumnInfo,ForeignKeyInfo,IndexInfo,SavedQuery,SchemaInfo,TableInfoData,TableListItem,WhereClause,RowFilters,ColumnDef,IndexDef,ColumnGeneratorConfig,ColumnPlan,GeneratorStrategyId,StrategyDescriptor,StrategyCategory};export interface ApiSuccessResponse<T=unknown>{success:true;data:T;}export interface ApiErrorResponse<T=unknown>{success:false;error:string;data?:T;}export type ApiResponse<T=unknown>=ApiSuccessResponse<T>|ApiErrorResponse<T>;export type ListTablesResponseData=TableListItem[];export type ListTablesResponse=ApiResponse<ListTablesResponseData>;export type TableInfoResponseData=TableInfoData;export type TableInfoResponse=ApiResponse<TableInfoResponseData>;export type FkOptionsResponseData=Record<string,{value:unknown;label:string;}[]>;export type FkOptionsResponse=ApiResponse<FkOptionsResponseData>;export interface GetRowsResponseData{rows:Record<string,unknown>[];count:number;page:number;limit:number;filters:RowFilters;}export type GetRowsResponse=ApiResponse<GetRowsResponseData>;export type GetRowResponseData=Record<string,unknown>;export type GetRowResponse=ApiResponse<GetRowResponseData>;export interface RowReferenceGroup{table:string; from :string;to:string;value:unknown;count:number;columns:string[];rows:Record<string,unknown>[];}export interface GetRowReferencesResponseData{references:RowReferenceGroup[];}export type GetRowReferencesResponse=ApiResponse<GetRowReferencesResponseData>;export interface InsertRowRequestBody{values:Record<string,unknown>;}export interface InsertRowResponseData{message:string;rowId?:number|bigint;sql:string;}export type InsertRowResponse=ApiResponse<InsertRowResponseData>;export interface GenerateInsertRequestBody{values:Record<string,unknown>;}export interface GenerateInsertResponseData{sql:string;}export type GenerateInsertResponse=ApiResponse<GenerateInsertResponseData>;export interface ImportCsvRequestBody{csv:string;}export interface ImportCsvResponseData{message:string;inserted?:number;skipped?:number;}export type ImportCsvResponse=ApiResponse<ImportCsvResponseData>;export interface UpdateRowRequestBody{values:Record<string,unknown>;nulls?:string[];}export interface UpdateRowResponseData{message:string;sql:string;}export type UpdateRowResponse=ApiResponse<UpdateRowResponseData>;export interface GenerateUpdateRequestBody{values:Record<string,unknown>;nulls?:string[];}export interface GenerateUpdateResponseData{sql:string;}export type GenerateUpdateResponse=ApiResponse<GenerateUpdateResponseData>;export interface DeleteRowResponseData{message:string;}export type DeleteRowResponse=ApiResponse<DeleteRowResponseData>;export interface BlobMetaResponseData{isNull:boolean;mime?:string;ext?:string;isImage?:boolean;isText?:boolean;isPdf?:boolean;isAudio?:boolean;isVideo?:boolean;size:number;sizeFormatted:string;hexDump?:{lines:{offset:string;hex:string;ascii:string;}[];totalBytes:number;truncated:boolean;};textPreview?:string|null;}export type BlobMetaResponse=ApiResponse<BlobMetaResponseData>;export interface UpdateBlobRequestBody{data:string;format?:'base64'|'hex'|'text';}export interface UpdateBlobResponseData{message:string;size:number;}export type UpdateBlobResponse=ApiResponse<UpdateBlobResponseData>;export interface TableDdlResponseData{table:string;ddl:string;}export type TableDdlResponse=ApiResponse<TableDdlResponseData>;export interface BulkImpactRequestBody{ids:(string|number)[];}export interface BulkImpactReference{table:string; from :string;to:string;count:number;}export interface BulkImpactResponseData{selected:number;references:BulkImpactReference[];total:number;}export type BulkImpactResponse=ApiResponse<BulkImpactResponseData>;export interface BulkDeleteRequestBody{ids:(string|number)[];confirmImpact?:boolean;}export interface BulkDeleteResponseData{message:string;deleted:number;}export type BulkDeleteResponse=ApiResponse<BulkDeleteResponseData>;export interface BulkExportRequestBody{ids:(string|number)[];format?:'csv'|'json';}export interface BulkExportResponseData{format:string;filename:string;content:string;rowCount:number;}export type BulkExportResponse=ApiResponse<BulkExportResponseData>;export interface BulkUpdateItem{id:string|number;values?:Record<string,unknown>;nulls?:string[];}export interface BulkUpdateRequestBody{updates:BulkUpdateItem[];}export interface BulkUpdateResponseData{message:string;updated:number;}export type BulkUpdateResponse=ApiResponse<BulkUpdateResponseData>;export interface SeedConfigResponseData{table:string;columns:ColumnGeneratorConfig[];maxRows:number;}export type SeedConfigResponse=ApiResponse<SeedConfigResponseData>;export interface SeedGenerateRequestBody{count?:number;plan?:Record<string,ColumnPlan>;}export interface SeedGenerateResponseData{table:string;count:number;sql:string;previewRows?:Record<string,unknown>[];warnings:string[];}export type SeedGenerateResponse=ApiResponse<SeedGenerateResponseData>;export interface SeedInsertRequestBody{count?:number;plan?:Record<string,ColumnPlan>;truncate?:boolean;}export interface SeedInsertResponseData{message:string;inserted:number;skipped:number;warnings:string[];elapsedMs?:number;}export type SeedInsertResponse=ApiResponse<SeedInsertResponseData>;export interface CreateTableRequestBody{name:string;columns:ColumnDef[];}export interface CreateTableResponseData{message:string;sql:string;}export type CreateTableResponse=ApiResponse<CreateTableResponseData>;export interface GenerateCreateTableRequestBody{name:string;columns:ColumnDef[];}export interface GenerateCreateTableResponseData{sql:string;}export type GenerateCreateTableResponse=ApiResponse<GenerateCreateTableResponseData>;export type GetSchemaResponseData=SchemaInfo;export type GetSchemaResponse=ApiResponse<GetSchemaResponseData>;export interface RenameTableRequestBody{name:string;}export interface RenameTableResponseData{message:string;sql:string;table:string;}export type RenameTableResponse=ApiResponse<RenameTableResponseData>;export interface AddColumnRequestBody{column:ColumnDef;}export interface AddColumnResponseData{message:string;sql:string;}export type AddColumnResponse=ApiResponse<AddColumnResponseData>;export interface RenameColumnRequestBody{name:string;}export interface RenameColumnResponseData{message:string;sql:string;}export type RenameColumnResponse=ApiResponse<RenameColumnResponseData>;export interface DropColumnResponseData{message:string;sql:string;}export type DropColumnResponse=ApiResponse<DropColumnResponseData>;export interface DropTableResponseData{message:string;sql:string;}export type DropTableResponse=ApiResponse<DropTableResponseData>;export interface CreateIndexRequestBody{name?:string;columns:string[];unique?:boolean;}export interface CreateIndexResponseData{message:string;sql:string;}export type CreateIndexResponse=ApiResponse<CreateIndexResponseData>;export interface DropIndexResponseData{message:string;sql:string;}export type DropIndexResponse=ApiResponse<DropIndexResponseData>;export interface ExecuteQueryRequestBody{sql:string;}export interface QueryCountResult{kind:'count';count:number;message:string;}export interface QuerySelectResult{kind:'select';columns:string[];rows:Record<string,unknown>[];}export interface QueryWriteResult{kind:'write';changes:number|null;message:string;}export type ExecuteQueryResponseData=QueryCountResult|QuerySelectResult|QueryWriteResult;export type ExecuteQueryResponse=ApiResponse<ExecuteQueryResponseData>;export interface ExportQueryRequestBody{sql:string;format?:'csv'|'json';}export type ListSavedQueriesResponseData=SavedQuery[];export type ListSavedQueriesResponse=ApiResponse<ListSavedQueriesResponseData>;export interface SaveQueryRequestBody{name:string;sql:string;}export interface SaveQueryResponseData{message:string;}export type SaveQueryResponse=ApiResponse<SaveQueryResponseData>;export interface DeleteSavedQueryResponseData{message:string;}export type DeleteSavedQueryResponse=ApiResponse<DeleteSavedQueryResponseData>;export interface DatabaseRow extends DatabaseEntry{sizeLabel:string;modifiedLabel:string;tables:number;}export type ListDatabasesResponseData=DatabaseRow[];export type ListDatabasesResponse=ApiResponse<ListDatabasesResponseData>;export interface CreateDatabaseRequestBody{name:string;}export interface CreateDatabaseResponseData{id:string;message:string;}export type CreateDatabaseResponse=ApiResponse<CreateDatabaseResponseData>;export interface OpenDatabaseRequestBody{path:string;}export interface OpenDatabaseResponseData{id:string;message:string;}export type OpenDatabaseResponse=ApiResponse<OpenDatabaseResponseData>;export interface DeleteDatabaseResponseData{message:string;}export type DeleteDatabaseResponse=ApiResponse<DeleteDatabaseResponseData>;export interface FsEntry{name:string;path:string;isDir:boolean;isDb:boolean;size:number;}export interface ListFsResponseData{path:string;name:string;parent:string|null;entries:FsEntry[];}export type ListFsResponse=ApiResponse<ListFsResponseData>;
|
package/dist/utils/common.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare
|
|
1
|
+
export declare const errorMessage:(err:unknown)=>string;export declare const isPostgresConnectionString:(target:unknown)=>boolean;export declare function sanitizeConnectionString(conn:string):string;export declare function isPathWithinRoot(root:string,p:string):boolean;export type FilterOp='eq'|'neq'|'gt'|'gte'|'lt'|'lte'|'like'|'prefix'|'between'|'null'|'notnull';export interface FilterCondition{op:FilterOp;value?:string;max?:string;}export type FilterValue=string|FilterCondition|FilterCondition[];export interface RowFilters{[column:string]:FilterValue;}export declare function parseFilters(raw:unknown):RowFilters;export declare function filtersToQS(filters:RowFilters|undefined):string;export declare const isInternalTable:(name:string)=>boolean;export declare const encodePk:(values:unknown[])=>string;export declare const decodePk:(encoded:string)=>string[];export declare function normalizeCell(value:unknown):unknown;export declare function normalizeRow(row:Record<string,unknown>):Record<string,unknown>;export declare function coerceFormValue(raw:unknown,columnType:string,dialect?:'sqlite'|'postgres'):unknown;export declare function formatBytes(bytes:number):string;export declare const truncate:(str:string,maxLength:number)=>string;
|
package/dist/utils/common.js
CHANGED
|
@@ -3,31 +3,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.errorMessage =
|
|
7
|
-
exports.isPostgresConnectionString = isPostgresConnectionString;
|
|
6
|
+
exports.truncate = exports.decodePk = exports.encodePk = exports.isInternalTable = exports.isPostgresConnectionString = exports.errorMessage = void 0;
|
|
8
7
|
exports.sanitizeConnectionString = sanitizeConnectionString;
|
|
9
8
|
exports.isPathWithinRoot = isPathWithinRoot;
|
|
10
9
|
exports.parseFilters = parseFilters;
|
|
11
10
|
exports.filtersToQS = filtersToQS;
|
|
12
|
-
exports.isInternalTable = isInternalTable;
|
|
13
|
-
exports.encodePk = encodePk;
|
|
14
|
-
exports.decodePk = decodePk;
|
|
15
11
|
exports.normalizeCell = normalizeCell;
|
|
16
12
|
exports.normalizeRow = normalizeRow;
|
|
17
13
|
exports.coerceFormValue = coerceFormValue;
|
|
18
14
|
exports.formatBytes = formatBytes;
|
|
19
|
-
exports.truncate = truncate;
|
|
20
15
|
const node_fs_1 = require("node:fs");
|
|
21
16
|
const node_path_1 = __importDefault(require("node:path"));
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
}
|
|
27
|
-
function isPostgresConnectionString(target) {
|
|
28
|
-
return (typeof target === 'string' &&
|
|
29
|
-
(target.startsWith('postgres://') || target.startsWith('postgresql://')));
|
|
30
|
-
}
|
|
17
|
+
const errorMessage = (err) => (err instanceof Error ? err.message : String(err));
|
|
18
|
+
exports.errorMessage = errorMessage;
|
|
19
|
+
const isPostgresConnectionString = (target) => typeof target === 'string' && /^(postgres|postgresql):\/\//.test(target);
|
|
20
|
+
exports.isPostgresConnectionString = isPostgresConnectionString;
|
|
31
21
|
function sanitizeConnectionString(conn) {
|
|
32
22
|
if (!conn || typeof conn !== 'string')
|
|
33
23
|
return '';
|
|
@@ -42,25 +32,19 @@ function sanitizeConnectionString(conn) {
|
|
|
42
32
|
return conn.replace(/:([^:@]+)@/, ':****@');
|
|
43
33
|
}
|
|
44
34
|
function isPathWithinRoot(root, p) {
|
|
45
|
-
if (!root || !p || typeof root !== 'string' || typeof p !== 'string')
|
|
35
|
+
if (!root || !p || typeof root !== 'string' || typeof p !== 'string' || p.includes('\0') || root.includes('\0')) {
|
|
46
36
|
return false;
|
|
47
|
-
if (p.includes('\0') || root.includes('\0'))
|
|
48
|
-
return false;
|
|
49
|
-
const absRoot = node_path_1.default.resolve(root);
|
|
50
|
-
const absPath = node_path_1.default.resolve(p);
|
|
51
|
-
let realRoot = absRoot;
|
|
52
|
-
let realPath = absPath;
|
|
53
|
-
try {
|
|
54
|
-
if ((0, node_fs_1.existsSync)(absRoot))
|
|
55
|
-
realRoot = (0, node_fs_1.realpathSync)(absRoot);
|
|
56
|
-
}
|
|
57
|
-
catch { }
|
|
58
|
-
try {
|
|
59
|
-
if ((0, node_fs_1.existsSync)(absPath))
|
|
60
|
-
realPath = (0, node_fs_1.realpathSync)(absPath);
|
|
61
37
|
}
|
|
62
|
-
|
|
63
|
-
|
|
38
|
+
const resolveReal = (target) => {
|
|
39
|
+
const abs = node_path_1.default.resolve(target);
|
|
40
|
+
try {
|
|
41
|
+
if ((0, node_fs_1.existsSync)(abs))
|
|
42
|
+
return (0, node_fs_1.realpathSync)(abs);
|
|
43
|
+
}
|
|
44
|
+
catch { }
|
|
45
|
+
return abs;
|
|
46
|
+
};
|
|
47
|
+
const rel = node_path_1.default.relative(resolveReal(root), resolveReal(p));
|
|
64
48
|
return rel === '' || (!rel.startsWith('..') && !node_path_1.default.isAbsolute(rel));
|
|
65
49
|
}
|
|
66
50
|
function parseFilters(raw) {
|
|
@@ -104,52 +88,41 @@ function filtersToQS(filters) {
|
|
|
104
88
|
if (Array.isArray(v)) {
|
|
105
89
|
if (v.length)
|
|
106
90
|
out[k] = v;
|
|
107
|
-
continue;
|
|
108
91
|
}
|
|
109
|
-
if (typeof v === 'object') {
|
|
92
|
+
else if (typeof v === 'object') {
|
|
110
93
|
if (Object.keys(v).length)
|
|
111
94
|
out[k] = v;
|
|
112
|
-
continue;
|
|
113
95
|
}
|
|
114
|
-
if (String(v).trim() !== '')
|
|
96
|
+
else if (String(v).trim() !== '') {
|
|
115
97
|
out[k] = String(v).trim();
|
|
116
|
-
}
|
|
117
|
-
const keys = Object.keys(out);
|
|
118
|
-
if (!keys.length)
|
|
119
|
-
return '';
|
|
120
|
-
return 'f=' + encodeURIComponent(JSON.stringify(out));
|
|
121
|
-
}
|
|
122
|
-
function isInternalTable(name) {
|
|
123
|
-
return name.startsWith('_');
|
|
124
|
-
}
|
|
125
|
-
function encodePk(values) {
|
|
126
|
-
return values.map((v) => encodeURIComponent(v == null ? '' : String(v))).join(',');
|
|
127
|
-
}
|
|
128
|
-
function decodePk(encoded) {
|
|
129
|
-
return String(encoded)
|
|
130
|
-
.split(',')
|
|
131
|
-
.map((s) => {
|
|
132
|
-
try {
|
|
133
|
-
return decodeURIComponent(s);
|
|
134
|
-
}
|
|
135
|
-
catch {
|
|
136
|
-
return s;
|
|
137
98
|
}
|
|
138
|
-
}
|
|
99
|
+
}
|
|
100
|
+
return Object.keys(out).length ? `f=${encodeURIComponent(JSON.stringify(out))}` : '';
|
|
139
101
|
}
|
|
102
|
+
const isInternalTable = (name) => name.startsWith('_');
|
|
103
|
+
exports.isInternalTable = isInternalTable;
|
|
104
|
+
const encodePk = (values) => values.map((v) => encodeURIComponent(v == null ? '' : String(v))).join(',');
|
|
105
|
+
exports.encodePk = encodePk;
|
|
106
|
+
const decodePk = (encoded) => String(encoded)
|
|
107
|
+
.split(',')
|
|
108
|
+
.map((s) => {
|
|
109
|
+
try {
|
|
110
|
+
return decodeURIComponent(s);
|
|
111
|
+
}
|
|
112
|
+
catch {
|
|
113
|
+
return s;
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
exports.decodePk = decodePk;
|
|
140
117
|
function normalizeCell(value) {
|
|
141
118
|
if (value instanceof Uint8Array || Buffer.isBuffer(value)) {
|
|
142
119
|
return `0x${Buffer.from(value).toString('hex')}`;
|
|
143
120
|
}
|
|
144
|
-
if (value instanceof Date)
|
|
121
|
+
if (value instanceof Date)
|
|
145
122
|
return value.toISOString();
|
|
146
|
-
|
|
147
|
-
if (typeof value === 'object' && value !== null) {
|
|
123
|
+
if (typeof value === 'object' && value !== null)
|
|
148
124
|
return JSON.stringify(value);
|
|
149
|
-
|
|
150
|
-
if (value === undefined)
|
|
151
|
-
return null;
|
|
152
|
-
return value;
|
|
125
|
+
return value ?? null;
|
|
153
126
|
}
|
|
154
127
|
function normalizeRow(row) {
|
|
155
128
|
const out = {};
|
|
@@ -158,9 +131,9 @@ function normalizeRow(row) {
|
|
|
158
131
|
return out;
|
|
159
132
|
}
|
|
160
133
|
function coerceFormValue(raw, columnType, dialect = 'sqlite') {
|
|
161
|
-
const type = (columnType || '').toUpperCase();
|
|
162
134
|
if (raw === undefined || raw === null)
|
|
163
135
|
return null;
|
|
136
|
+
const type = (columnType || '').toUpperCase();
|
|
164
137
|
if (type === 'BOOLEAN' || type === 'BOOL') {
|
|
165
138
|
const s = String(raw).trim().toLowerCase();
|
|
166
139
|
if (s === '')
|
|
@@ -185,30 +158,20 @@ function coerceFormValue(raw, columnType, dialect = 'sqlite') {
|
|
|
185
158
|
}
|
|
186
159
|
if (type === 'BLOB' || type === 'BYTEA') {
|
|
187
160
|
const s = String(raw);
|
|
188
|
-
|
|
189
|
-
return Buffer.from(s.slice(2), 'hex');
|
|
190
|
-
if (/^\\x[0-9a-f]*$/i.test(s))
|
|
191
|
-
return Buffer.from(s.slice(2), 'hex');
|
|
192
|
-
return Buffer.from(s, 'utf8');
|
|
161
|
+
return /^(0x|\\x)[0-9a-f]*$/i.test(s) ? Buffer.from(s.slice(2), 'hex') : Buffer.from(s, 'utf8');
|
|
193
162
|
}
|
|
194
163
|
if (type === 'JSON' || type === 'JSONB') {
|
|
195
164
|
const s = String(raw).trim();
|
|
196
|
-
|
|
197
|
-
return null;
|
|
198
|
-
return s;
|
|
165
|
+
return s === '' ? null : s;
|
|
199
166
|
}
|
|
200
167
|
if (type.endsWith('[]') || type.startsWith('_') || type.includes('ARRAY')) {
|
|
201
168
|
if (Array.isArray(raw))
|
|
202
169
|
return raw;
|
|
203
170
|
const s = String(raw).trim();
|
|
204
|
-
|
|
205
|
-
return null;
|
|
206
|
-
return s;
|
|
171
|
+
return s === '' ? null : s;
|
|
207
172
|
}
|
|
208
173
|
const s = String(raw);
|
|
209
|
-
|
|
210
|
-
return null;
|
|
211
|
-
return s;
|
|
174
|
+
return s === '' ? null : s;
|
|
212
175
|
}
|
|
213
176
|
function formatBytes(bytes) {
|
|
214
177
|
if (bytes === 0)
|
|
@@ -218,8 +181,5 @@ function formatBytes(bytes) {
|
|
|
218
181
|
const i = Math.floor(Math.log(bytes) / Math.log(k));
|
|
219
182
|
return `${parseFloat((bytes / Math.pow(k, i)).toFixed(1))} ${sizes[i]}`;
|
|
220
183
|
}
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
return str;
|
|
224
|
-
return `${str.slice(0, maxLength)}…`;
|
|
225
|
-
}
|
|
184
|
+
const truncate = (str, maxLength) => !str || str.length <= maxLength ? str : `${str.slice(0, maxLength)}…`;
|
|
185
|
+
exports.truncate = truncate;
|
package/dist/utils/csv.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare function csvEscape(value:unknown):string;export declare
|
|
1
|
+
export declare function csvEscape(value:unknown):string;export declare const toCsv:(rows:Record<string,unknown>[],columns:string[])=>string;export declare const toJson:(rows:Record<string,unknown>[])=>string;export declare function parseCsv(text:string):string[][];
|
package/dist/utils/csv.js
CHANGED
|
@@ -1,23 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.toJson = exports.toCsv = void 0;
|
|
3
4
|
exports.csvEscape = csvEscape;
|
|
4
|
-
exports.toCsv = toCsv;
|
|
5
|
-
exports.toJson = toJson;
|
|
6
5
|
exports.parseCsv = parseCsv;
|
|
7
6
|
function csvEscape(value) {
|
|
8
|
-
const s = value
|
|
9
|
-
|
|
10
|
-
return `"${s.replace(/"/g, '""')}"`;
|
|
11
|
-
return s;
|
|
12
|
-
}
|
|
13
|
-
function toCsv(rows, columns) {
|
|
14
|
-
const header = columns.map(csvEscape).join(',');
|
|
15
|
-
const body = rows.map((row) => columns.map((c) => csvEscape(row[c])).join(','));
|
|
16
|
-
return [header, ...body].join('\r\n');
|
|
17
|
-
}
|
|
18
|
-
function toJson(rows) {
|
|
19
|
-
return JSON.stringify(rows, null, 2);
|
|
7
|
+
const s = value == null ? '' : String(value);
|
|
8
|
+
return /[",\r\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s;
|
|
20
9
|
}
|
|
10
|
+
const toCsv = (rows, columns) => [
|
|
11
|
+
columns.map(csvEscape).join(','),
|
|
12
|
+
...rows.map((row) => columns.map((c) => csvEscape(row[c])).join(',')),
|
|
13
|
+
].join('\r\n');
|
|
14
|
+
exports.toCsv = toCsv;
|
|
15
|
+
const toJson = (rows) => JSON.stringify(rows, null, 2);
|
|
16
|
+
exports.toJson = toJson;
|
|
21
17
|
function parseCsv(text) {
|
|
22
18
|
const rows = [];
|
|
23
19
|
let row = [];
|
|
@@ -54,9 +50,7 @@ function parseCsv(text) {
|
|
|
54
50
|
rows.push(row);
|
|
55
51
|
row = [];
|
|
56
52
|
}
|
|
57
|
-
else if (ch
|
|
58
|
-
}
|
|
59
|
-
else {
|
|
53
|
+
else if (ch !== '\r') {
|
|
60
54
|
field += ch;
|
|
61
55
|
}
|
|
62
56
|
}
|