@kazurolabs/psql-explorer 0.1.0 → 0.3.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.
Files changed (77) hide show
  1. package/README.md +229 -18
  2. package/RELEASES.md +87 -0
  3. package/dist/cli.js +57 -13
  4. package/dist/commands/configgen.js +25 -25
  5. package/dist/commands/explore.js +86 -4
  6. package/dist/config/load.js +11 -73
  7. package/dist/config/resolve-environment.js +10 -62
  8. package/dist/config/resolvers/index.js +1 -1
  9. package/dist/config/resolvers/prompt.js +2 -2
  10. package/dist/config/resolvers/vault.js +1 -83
  11. package/dist/db/store.js +57 -0
  12. package/dist/environments.js +5 -2
  13. package/dist/explorer/alter.js +493 -0
  14. package/dist/explorer/catalog.js +31 -0
  15. package/dist/explorer/connect-entry.js +29 -18
  16. package/dist/explorer/cypher-parse.js +478 -0
  17. package/dist/explorer/cypher.js +963 -0
  18. package/dist/explorer/ddl.js +502 -0
  19. package/dist/explorer/dependents.js +222 -0
  20. package/dist/explorer/design-load.js +254 -0
  21. package/dist/explorer/design.js +45 -0
  22. package/dist/explorer/edit.js +108 -0
  23. package/dist/explorer/explain.js +24 -0
  24. package/dist/explorer/filter.js +121 -0
  25. package/dist/explorer/graph.js +679 -0
  26. package/dist/explorer/gucs.js +31 -0
  27. package/dist/explorer/profile.js +102 -0
  28. package/dist/explorer/references.js +95 -0
  29. package/dist/explorer/rows.js +22 -8
  30. package/dist/explorer/schema-diff.js +360 -0
  31. package/dist/explorer/search.js +309 -0
  32. package/dist/explorer/security.js +152 -0
  33. package/dist/explorer/seed.js +1376 -0
  34. package/dist/explorer/session.js +17 -0
  35. package/dist/explorer/stats.js +135 -0
  36. package/dist/explorer/timeout.js +13 -0
  37. package/dist/screens/environments.js +6 -3
  38. package/dist/screens/explorer.js +0 -3
  39. package/dist/screens/splash.js +4 -2
  40. package/dist/web/server.js +654 -21
  41. package/package.json +3 -1
  42. package/web/dist/assets/antonio-latin-400-normal-B7iMKlzw.woff +0 -0
  43. package/web/dist/assets/antonio-latin-400-normal-CZjzwNAN.woff2 +0 -0
  44. package/web/dist/assets/antonio-latin-700-normal-BY3mCh47.woff +0 -0
  45. package/web/dist/assets/antonio-latin-700-normal-DSH-lEDG.woff2 +0 -0
  46. package/web/dist/assets/antonio-latin-ext-400-normal-nE82dizb.woff2 +0 -0
  47. package/web/dist/assets/antonio-latin-ext-400-normal-qA9Jb92z.woff +0 -0
  48. package/web/dist/assets/antonio-latin-ext-700-normal-BdSkr58W.woff +0 -0
  49. package/web/dist/assets/antonio-latin-ext-700-normal-D-dvgqjr.woff2 +0 -0
  50. package/web/dist/assets/button-ClA70PLR.js +1 -0
  51. package/web/dist/assets/code-xml-CXkpHeU9.js +1 -0
  52. package/web/dist/assets/compare-view-DKl5l_Jw.js +2 -0
  53. package/web/dist/assets/copy-CCXzeW-s.js +1 -0
  54. package/web/dist/assets/cypher-view-Pd0WFH8F.js +13 -0
  55. package/web/dist/assets/design-view-CIHSmH4u.js +9 -0
  56. package/web/dist/assets/erd-view-CV7mFGAN.js +1 -0
  57. package/web/dist/assets/format-sql-BLj5_RDZ.js +5 -0
  58. package/web/dist/assets/graph-force-yBLyErpS.js +1 -0
  59. package/web/dist/assets/graph-link-OCo_s0zO.js +1 -0
  60. package/web/dist/assets/graph-view-sM1OlByf.js +3 -0
  61. package/web/dist/assets/index-CDcnUacl.js +10 -0
  62. package/web/dist/assets/index-OzJqnJWG.css +2 -0
  63. package/web/dist/assets/key-round-Cv_7QeED.js +1 -0
  64. package/web/dist/assets/loader-circle-BK_GMdvN.js +1 -0
  65. package/web/dist/assets/query-builder-view-ChNFb51g.js +4 -0
  66. package/web/dist/assets/rotate-ccw-CdCaf7Q6.js +1 -0
  67. package/web/dist/assets/route-DO6IZ7of.js +15 -0
  68. package/web/dist/assets/seed-view-DFNB8WHE.js +7 -0
  69. package/web/dist/assets/shield-alert-CMS-3j4U.js +1 -0
  70. package/web/dist/assets/sliders-horizontal-Dg51yc-C.js +1 -0
  71. package/web/dist/assets/sql-tabs-B4jt6bQj.js +2 -0
  72. package/web/dist/assets/table-DfMz2f8M.js +1 -0
  73. package/web/dist/assets/triangle-alert-COGnHCD1.js +1 -0
  74. package/web/dist/assets/zone-D9dXk8tn.js +5 -0
  75. package/web/dist/index.html +9 -2
  76. package/web/dist/assets/index-BMLFCtFY.js +0 -17
  77. package/web/dist/assets/index-CMXJRL9O.css +0 -2
@@ -0,0 +1,121 @@
1
+ // Filters on the rows of a table, as the row grid of the web page sends them.
2
+ import { quoteIdentifier } from '../db/identifiers.js';
3
+ import { escapeLike } from './search.js';
4
+ export const FILTER_OPS = [
5
+ 'eq',
6
+ 'ne',
7
+ 'contains',
8
+ 'gt',
9
+ 'gte',
10
+ 'lt',
11
+ 'lte',
12
+ 'null',
13
+ 'notnull',
14
+ 'in',
15
+ 'between',
16
+ ];
17
+ export const MAX_IN_VALUES = 100;
18
+ export class FilterError extends Error {
19
+ }
20
+ const quoteLiteral = (text) => `'${text.replaceAll("'", "''")}'`;
21
+ /** The values of an `in` list: commas separate them, a backslash escapes a comma or a backslash. */
22
+ export function splitList(text) {
23
+ const values = [];
24
+ let current = '';
25
+ for (let i = 0; i < text.length; i++) {
26
+ const char = text[i];
27
+ if (char === '\\' && i + 1 < text.length)
28
+ current += text[++i];
29
+ else if (char === ',') {
30
+ values.push(current.trim());
31
+ current = '';
32
+ }
33
+ else
34
+ current += char;
35
+ }
36
+ values.push(current.trim());
37
+ return values.filter((value) => value !== '');
38
+ }
39
+ const comparisons = {
40
+ eq: '=',
41
+ ne: '<>',
42
+ gt: '>',
43
+ gte: '>=',
44
+ lt: '<',
45
+ lte: '<=',
46
+ };
47
+ /**
48
+ * The WHERE clause for the filters. Columns must be ones of the table, and values are bound as
49
+ * parameters (or written as literals when `inline` is set, for showing the statement); a value is
50
+ * compared with the column's own type, so Postgres reads `2024-01-01` as a date for a date column.
51
+ */
52
+ export function buildWhere(columns, filters, { inline = false } = {}) {
53
+ const params = [];
54
+ const bind = (value) => {
55
+ if (inline)
56
+ return quoteLiteral(value);
57
+ params.push(value);
58
+ return `$${params.length}`;
59
+ };
60
+ const known = new Set(columns.map((column) => column.name));
61
+ const conditions = filters.map((filter) => {
62
+ if (!known.has(filter.column))
63
+ throw new FilterError(`no column "${filter.column}"`);
64
+ if (!FILTER_OPS.includes(filter.op))
65
+ throw new FilterError(`unknown operator "${filter.op}"`);
66
+ const name = quoteIdentifier(filter.column);
67
+ const { op } = filter;
68
+ if (op === 'null')
69
+ return `${name} IS NULL`;
70
+ if (op === 'notnull')
71
+ return `${name} IS NOT NULL`;
72
+ const value = filter.value ?? '';
73
+ if (op === 'in') {
74
+ const values = splitList(value);
75
+ if (values.length === 0)
76
+ throw new FilterError(`"${filter.column}" in needs a value`);
77
+ if (values.length > MAX_IN_VALUES) {
78
+ throw new FilterError(`in takes at most ${MAX_IN_VALUES} values`);
79
+ }
80
+ return `${name} IN (${values.map(bind).join(', ')})`;
81
+ }
82
+ if (op === 'between') {
83
+ if (value === '' || (filter.value2 ?? '') === '') {
84
+ throw new FilterError(`"${filter.column}" between needs two values`);
85
+ }
86
+ const low = bind(value);
87
+ return `${name} BETWEEN ${low} AND ${bind(filter.value2)}`;
88
+ }
89
+ if (op === 'contains')
90
+ return `${name}::text ILIKE ${bind(`%${escapeLike(value)}%`)}`;
91
+ return `${name} ${comparisons[op]} ${bind(value)}`;
92
+ });
93
+ return {
94
+ sql: conditions.length > 0 ? ` WHERE ${conditions.join(' AND ')}` : '',
95
+ params,
96
+ };
97
+ }
98
+ /** Reads the filters of a request, which are untrusted, as `Filter`s. */
99
+ export function parseFilters(raw) {
100
+ if (raw === undefined || raw === null)
101
+ return [];
102
+ if (!Array.isArray(raw))
103
+ throw new FilterError('filters must be a list');
104
+ if (raw.length > 50)
105
+ throw new FilterError('too many filters');
106
+ return raw.map((item) => {
107
+ const entry = item;
108
+ if (typeof entry?.column !== 'string' || typeof entry.op !== 'string') {
109
+ throw new FilterError('a filter is {column, op, value}');
110
+ }
111
+ const text = (field) => (typeof field === 'string' ? field : undefined);
112
+ const value = text(entry.value);
113
+ const value2 = text(entry.value2);
114
+ return {
115
+ column: entry.column,
116
+ op: entry.op,
117
+ ...(value === undefined ? {} : { value }),
118
+ ...(value2 === undefined ? {} : { value2 }),
119
+ };
120
+ });
121
+ }