@mason-data/cli 0.7.0 → 0.9.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 (155) hide show
  1. package/README.md +101 -73
  2. package/dist/src/api/error.js +3 -0
  3. package/dist/src/api/postgres/catalog-worker.js +49 -2
  4. package/dist/src/api/postgres/catalog.js +368 -25
  5. package/dist/src/api/postgres/encode.js +3 -1
  6. package/dist/src/api/postgres/explain.js +3 -2
  7. package/dist/src/api/postgres/listener.js +11 -3
  8. package/dist/src/api/postgres/protocol.js +39 -23
  9. package/dist/src/api/postgres/proxy.js +283 -0
  10. package/dist/src/api/postgres/schema.js +6 -0
  11. package/dist/src/api/postgres/server.js +1617 -245
  12. package/dist/src/api/postgres/translate.js +127 -10
  13. package/dist/src/api/request-log.js +18 -5
  14. package/dist/src/cdc/poll.js +5 -5
  15. package/dist/src/cli/background.js +47 -25
  16. package/dist/src/cli/bridge-stdin.js +101 -0
  17. package/dist/src/cli/cancel.js +75 -0
  18. package/dist/src/cli/command.js +170 -11
  19. package/dist/src/cli/commands.js +804 -209
  20. package/dist/src/cli/control.js +59 -0
  21. package/dist/src/cli/credentials.js +182 -0
  22. package/dist/src/cli/demo.js +20 -11
  23. package/dist/src/cli/deploy.js +574 -0
  24. package/dist/src/cli/dev.js +491 -130
  25. package/dist/src/cli/doctor.js +131 -38
  26. package/dist/src/cli/generated-files.js +143 -0
  27. package/dist/src/cli/guides.js +175 -36
  28. package/dist/src/cli/hosted.js +850 -0
  29. package/dist/src/cli/init.js +162 -65
  30. package/dist/src/cli/login.js +289 -0
  31. package/dist/src/cli/machine.js +13 -12
  32. package/dist/src/cli/main.js +11 -5
  33. package/dist/src/cli/mason.cjs +17 -0
  34. package/dist/src/cli/mcp.js +31 -22
  35. package/dist/src/cli/metrics.js +79 -0
  36. package/dist/src/cli/output-gate.js +176 -0
  37. package/dist/src/cli/page-share.js +96 -0
  38. package/dist/src/cli/pg-tls.js +14 -4
  39. package/dist/src/cli/pg-url.js +4 -0
  40. package/dist/src/cli/pin.js +19 -14
  41. package/dist/src/cli/pool-size.cjs +85 -0
  42. package/dist/src/cli/ports.js +2 -0
  43. package/dist/src/cli/preflight.js +138 -46
  44. package/dist/src/cli/project.js +142 -4
  45. package/dist/src/cli/readiness.js +76 -6
  46. package/dist/src/cli/registry.js +65 -38
  47. package/dist/src/cli/replica-hosted.js +665 -0
  48. package/dist/src/cli/replica.js +76 -0
  49. package/dist/src/cli/replication-checks.js +7 -5
  50. package/dist/src/cli/replication-mode.js +105 -26
  51. package/dist/src/cli/request-filter.js +134 -0
  52. package/dist/src/cli/roles.js +585 -0
  53. package/dist/src/cli/run-server.js +637 -48
  54. package/dist/src/cli/runs.js +1 -1
  55. package/dist/src/cli/security.js +67 -18
  56. package/dist/src/cli/serve.js +204 -46
  57. package/dist/src/cli/share-run.js +329 -0
  58. package/dist/src/cli/share.js +286 -0
  59. package/dist/src/cli/source-edit.js +75 -17
  60. package/dist/src/cli/source-role.js +344 -0
  61. package/dist/src/cli/sources.js +306 -47
  62. package/dist/src/cli/style.js +2 -1
  63. package/dist/src/cli/supervise.js +27 -24
  64. package/dist/src/cli/sync.js +55 -25
  65. package/dist/src/cli/token-hosted.js +337 -0
  66. package/dist/src/cli/tunnel.js +200 -0
  67. package/dist/src/cli/ui-ssh.js +460 -0
  68. package/dist/src/cli/ui.js +124 -17
  69. package/dist/src/cli/usage.js +128 -0
  70. package/dist/src/cli/user-hosted.js +299 -0
  71. package/dist/src/cli/user.js +573 -65
  72. package/dist/src/cli/version.js +9 -7
  73. package/dist/src/demo/seed.js +29 -0
  74. package/dist/src/duck/analyze.js +36 -20
  75. package/dist/src/duck/cancel.js +10 -1
  76. package/dist/src/duck/collation.js +266 -0
  77. package/dist/src/duck/constant-key.js +94 -0
  78. package/dist/src/duck/distinct-key.js +283 -26
  79. package/dist/src/duck/events.js +165 -0
  80. package/dist/src/duck/executor-worker.js +20 -11
  81. package/dist/src/duck/executor.js +47 -33
  82. package/dist/src/duck/explain.js +60 -46
  83. package/dist/src/duck/extensions.js +160 -0
  84. package/dist/src/duck/functions.js +461 -63
  85. package/dist/src/duck/gaps.js +48 -21
  86. package/dist/src/duck/generated-plan.js +1070 -0
  87. package/dist/src/duck/generated.js +906 -0
  88. package/dist/src/duck/grants.js +176 -0
  89. package/dist/src/duck/held-types.js +64 -0
  90. package/dist/src/duck/history.js +127 -0
  91. package/dist/src/duck/jsonb-order.js +9 -12
  92. package/dist/src/duck/kept-route.js +6 -5
  93. package/dist/src/duck/kept.js +15 -13
  94. package/dist/src/duck/late.js +1469 -40
  95. package/dist/src/duck/libc-bound.js +341 -0
  96. package/dist/src/duck/libc-order.js +892 -126
  97. package/dist/src/duck/pg-eval-worker.js +7 -1
  98. package/dist/src/duck/pg-eval.js +6 -4
  99. package/dist/src/duck/pgtree.js +774 -23
  100. package/dist/src/duck/plan.js +369 -47
  101. package/dist/src/duck/publication.js +20 -6
  102. package/dist/src/duck/requests.js +269 -93
  103. package/dist/src/duck/resolved.js +117 -2
  104. package/dist/src/duck/rewrite.js +4584 -580
  105. package/dist/src/duck/row-filter.js +655 -0
  106. package/dist/src/duck/statement.js +19 -0
  107. package/dist/src/duck/store.js +439 -177
  108. package/dist/src/duck/stream.js +19 -10
  109. package/dist/src/duck/sync.js +1411 -368
  110. package/dist/src/duck/token.js +4 -1
  111. package/dist/src/duck/types.js +69 -12
  112. package/dist/src/duck/usage.js +326 -0
  113. package/dist/src/duck/users.js +69 -15
  114. package/dist/src/duck/verified.js +161 -1
  115. package/dist/src/duck/views.js +5 -5
  116. package/dist/src/lib/database-name.js +62 -0
  117. package/dist/src/lib/database-url.js +55 -10
  118. package/dist/src/lib/dotenv.js +17 -4
  119. package/dist/src/lib/instant.js +50 -0
  120. package/dist/src/lib/log-buffer.js +77 -0
  121. package/dist/src/lib/log-read.js +196 -0
  122. package/dist/src/lib/pg-numeric.js +390 -49
  123. package/dist/src/lib/project-version.js +23 -23
  124. package/dist/src/lib/secret-mask.js +142 -0
  125. package/dist/src/lib/table-sync.js +282 -0
  126. package/dist/src/web/app.css +706 -66
  127. package/dist/src/web/app.js +331 -409
  128. package/dist/src/web/charts.js +229 -0
  129. package/dist/src/web/index.html +82 -18
  130. package/dist/src/web/pages/queries.js +192 -0
  131. package/dist/src/web/pages/status.js +611 -0
  132. package/dist/src/web/pages/syncs.js +155 -0
  133. package/dist/src/web/pages/tables.js +439 -0
  134. package/dist/src/web/pages/usage.js +397 -0
  135. package/dist/src/web/pages/users.js +219 -0
  136. package/dist/src/web/shared.js +584 -0
  137. package/dist/src/web/vendor/README.md +25 -0
  138. package/dist/src/web/vendor/geist-1.7.2/Geist-Variable.woff2 +0 -0
  139. package/dist/src/web/vendor/geist-1.7.2/GeistMono-Variable.woff2 +0 -0
  140. package/dist/src/web/vendor/geist-1.7.2/LICENSE.txt +92 -0
  141. package/dist/src/web/vendor/lucide-1.56.0/LICENSE.txt +43 -0
  142. package/dist/src/web/vendor/lucide-1.56.0/chart-column.mjs +29 -0
  143. package/dist/src/web/vendor/lucide-1.56.0/layout-dashboard.mjs +29 -0
  144. package/dist/src/web/vendor/lucide-1.56.0/logs.mjs +34 -0
  145. package/dist/src/web/vendor/lucide-1.56.0/menu.mjs +28 -0
  146. package/dist/src/web/vendor/lucide-1.56.0/square-terminal.mjs +28 -0
  147. package/dist/src/web/vendor/lucide-1.56.0/table.mjs +29 -0
  148. package/dist/src/web/vendor/lucide-1.56.0/users.mjs +29 -0
  149. package/dist/src/web/vendor/lucide-1.56.0/x.mjs +27 -0
  150. package/dist/src/web/vendor/uplot-1.6.32/LICENSE.txt +21 -0
  151. package/dist/src/web/vendor/uplot-1.6.32/uPlot.esm.js +6140 -0
  152. package/dist/src/web/vendor/uplot-1.6.32/uPlot.min.css +1 -0
  153. package/dist/src/web/vendor/zilla-slab-5.3.0/LICENSE.txt +93 -0
  154. package/dist/src/web/vendor/zilla-slab-5.3.0/zilla-slab-latin-700-normal.woff2 +0 -0
  155. package/package.json +12 -4
@@ -14,6 +14,10 @@
14
14
  * defined is never in the catalog Postgres, so it never reaches here.
15
15
  */
16
16
  import { exprType } from "./pgtree.js";
17
+ import { HELD_TYPE_INPUT } from "../api/postgres/schema.js";
18
+ import { heldType, isHeldArray } from "./held-types.js";
19
+ /** the key of a type the source made, held as its text (src/duck/held-types.ts) */
20
+ const HELD_KEY = "made";
17
21
  const isNode = (v) => v !== null && v !== undefined && typeof v === "object" && !Array.isArray(v);
18
22
  /** an OID a customer's database made (an extension's type or operator, a table's row type) differs between databases:
19
23
  * marked `u<oid>` here, and named by the catalog before it is a key (`nameUserKeys`: `ext:=(citext,citext)`) */
@@ -42,7 +46,9 @@ export async function nameUserKeys(rows, keys) {
42
46
  const read = async (set, sql) => set.size === 0 ? new Map() : new Map((await rows(sql([...set].join(", ")))).map((r) => [r[0], r[1]]));
43
47
  const proc = await read(oids.proc, (l) => `SELECT oid::text, 'ext:' || oid::regprocedure::text FROM pg_proc WHERE oid IN (${l})`);
44
48
  const op = await read(oids.op, (l) => `SELECT oid::text, 'ext:' || oid::regoperator::text FROM pg_operator WHERE oid IN (${l})`);
45
- const type = await read(oids.type, (l) => `SELECT oid::text, CASE WHEN typtype = 'c' THEN 'rowtype' ELSE 'ext:' || typname END FROM pg_type WHERE oid IN (${l})`);
49
+ // a type the source made that the copy holds as its text is one key, whichever: what is verified over one is the text's
50
+ const type = await read(oids.type, (l) => `SELECT t.oid::text, CASE WHEN t.typtype = 'c' THEN 'rowtype' WHEN t.typinput::text LIKE '%${HELD_TYPE_INPUT}%' THEN '${HELD_KEY}'
51
+ WHEN e.typinput::text LIKE '%${HELD_TYPE_INPUT}%' THEN '${HELD_KEY}[]' ELSE 'ext:' || t.typname END FROM pg_type t LEFT JOIN pg_type e ON e.oid = t.typelem WHERE t.oid IN (${l})`);
46
52
  const name = (kind, p) => { const m = USER.exec(p); return m === null ? p : (kind === "operator" ? op : kind === "type" || kind === "cast" ? type : proc).get(m[1]) ?? p; };
47
53
  return new Set(list.map((k) => {
48
54
  const kind = k.slice(0, k.indexOf(":")), value = k.slice(k.indexOf(":") + 1);
@@ -84,6 +90,9 @@ export function resolvedKeys(tree) {
84
90
  const t = exprType(v);
85
91
  if (t !== null && t.type !== 0)
86
92
  keys.add(`type:${oidKey(t.type)}`);
93
+ // a system column (ctid, xmin, tableoid): a copy has none, whatever its type's being verified says. Never verified
94
+ if (name === "VAR" && Number(v.varattno) < 0)
95
+ keys.add("field:VAR.system");
87
96
  const resolved = RESOLVED[name];
88
97
  if (resolved) {
89
98
  const oid = v[resolved[0]];
@@ -120,6 +129,111 @@ export function resolvedKeys(tree) {
120
129
  visit(tree);
121
130
  return keys;
122
131
  }
132
+ /** Each function, aggregate and window function an analysed tree calls, with the type of each argument it is given, as
133
+ * `<function oid>:<type oid>` (the catalog's OIDs): what a function marked stable only for its arguments' text reads
134
+ * (src/duck/generated-plan.ts `sessionReads`). */
135
+ export function argumentTypes(tree) {
136
+ const out = new Set();
137
+ const visit = (v) => {
138
+ if (Array.isArray(v)) {
139
+ for (const x of v)
140
+ visit(x);
141
+ return;
142
+ }
143
+ if (!isNode(v))
144
+ return;
145
+ const r = RESOLVED[v._node];
146
+ const oid = r === undefined || r[1] === "operator" ? undefined : v[r[0]];
147
+ if (typeof oid === "string" && Array.isArray(v.args)) {
148
+ for (const a of v.args) {
149
+ const e = isNode(a) && a._node === "TARGETENTRY" ? a.expr : a;
150
+ const t = isNode(e) ? exprType(e) : null;
151
+ if (t !== null && t.type !== 0)
152
+ out.add(`${oid}:${String(t.type)}`);
153
+ }
154
+ }
155
+ for (const [k, x] of Object.entries(v))
156
+ if (k !== "_node")
157
+ visit(x);
158
+ };
159
+ visit(tree);
160
+ return [...out];
161
+ }
162
+ /** What a statement does with a value the copy holds as its text (src/duck/held-types.ts): what the text cannot answer
163
+ * as the type would. Each is `operator:<oid>`, `function:<oid>` … as a key; the planner reads an operator's name. */
164
+ export function heldUses(tree) {
165
+ const out = [];
166
+ const typeOf = (x) => {
167
+ if (!isNode(x))
168
+ return null;
169
+ if (x._node === "TARGETENTRY")
170
+ return typeOf(x.expr);
171
+ // a literal is the statement's, not the copy's (`to_tsvector('simple', x)` reads a regconfig)
172
+ if (x._node === "CONST")
173
+ return null;
174
+ // a binary-compatible cast is its value (a cidr read by inet's operators)
175
+ if (x._node === "RELABELTYPE")
176
+ return typeOf(x.arg);
177
+ return exprType(x)?.type ?? null;
178
+ };
179
+ const visit = (v) => {
180
+ if (Array.isArray(v)) {
181
+ for (const x of v)
182
+ visit(x);
183
+ return;
184
+ }
185
+ if (!isNode(v))
186
+ return;
187
+ const resolved = RESOLVED[v._node];
188
+ if (resolved !== undefined && Array.isArray(v.args)) {
189
+ const oid = v[resolved[0]];
190
+ const type = v.args.map(typeOf).find((t) => heldType(t) !== undefined);
191
+ if (type != null && typeof oid === "string")
192
+ out.push({ kind: resolved[1] === "operator" ? "operator" : "function", oid, type });
193
+ }
194
+ if (v._node === "COERCEVIAIO" || v._node === "ARRAYCOERCEEXPR") {
195
+ const from = typeOf(v.arg), to = exprType(v)?.type ?? null;
196
+ // to another type, or a value read as one (a text cast to macaddr), which only Postgres's input function reads
197
+ if (heldType(from) !== undefined && to !== 25 && to !== 1043 && from !== to)
198
+ out.push({ kind: "cast", oid: String(to), type: from });
199
+ else if (heldType(to) !== undefined && from !== to)
200
+ out.push({ kind: "cast", oid: String(to), type: to });
201
+ }
202
+ // a regclass read as its oid: the copy holds its name
203
+ if (v._node === "RELABELTYPE") {
204
+ const from = typeOf(v.arg), to = exprType(v)?.type ?? null;
205
+ if (heldType(from) !== undefined && to !== 869 && heldType(to) === undefined && from !== to)
206
+ out.push({ kind: "cast", oid: String(to), type: from });
207
+ }
208
+ // a "char" read as a text by min and max, under the database's collation: the copy's is a byte's order
209
+ if (v._node === "AGGREF" && Array.isArray(v.args) && v.args.some((a) => isNode(a) && isNode(a.expr) && a.expr.funcid === "946"))
210
+ out.push({ kind: "cast", oid: "25", type: 18 });
211
+ // a point's x, a vector's element: a subscript of a type that is not an array
212
+ if (v._node === "SUBSCRIPTINGREF") {
213
+ const of = typeOf(v.refexpr);
214
+ if (heldType(of) !== undefined && !isHeldArray(of))
215
+ out.push({ kind: "cast", oid: String(of), type: of });
216
+ }
217
+ for (const [k, x] of Object.entries(v))
218
+ if (k !== "_node" && k !== "joinaliasvars")
219
+ visit(x);
220
+ };
221
+ visit(tree);
222
+ return out;
223
+ }
224
+ /** whether a statement tests a value of one of `types` with IS [NOT] NULL */
225
+ export function nullTested(tree, types) {
226
+ const visit = (v) => {
227
+ if (Array.isArray(v))
228
+ return v.some(visit);
229
+ if (!isNode(v))
230
+ return false;
231
+ if (v._node === "NULLTEST" && isNode(v.arg) && types.has(exprType(v.arg)?.type ?? 0))
232
+ return true;
233
+ return Object.entries(v).some(([k, x]) => k !== "_node" && k !== "joinaliasvars" && visit(x));
234
+ };
235
+ return visit(tree);
236
+ }
123
237
  // --------------------------------------------------------------------------------------------- what a key names
124
238
  /** each kind field's values in SQL's words (the enums of Postgres 18's primnodes.h / parsenodes.h, as outfuncs.c writes them) */
125
239
  const VALUES = {
@@ -148,6 +262,7 @@ const VALUES = {
148
262
  "COMMONTABLEEXPR.ctematerialized": { 0: "WITH", 1: "WITH … AS MATERIALIZED", 2: "WITH … AS NOT MATERIALIZED" },
149
263
  };
150
264
  const FLAGS = {
265
+ "VAR.system": "a system column (ctid, xmin, tableoid …), which a synced table does not have",
151
266
  "AGGREF.aggdistinct": "an aggregate of DISTINCT values", "AGGREF.aggorder": "an aggregate's own ORDER BY", "AGGREF.aggfilter": "an aggregate's FILTER (WHERE …)",
152
267
  "AGGREF.aggdirectargs": "an ordered-set aggregate's direct argument", "WINDOWFUNC.aggfilter": "a window aggregate's FILTER (WHERE …)",
153
268
  "QUERY.distinctClause": "SELECT DISTINCT", "QUERY.groupingSets": "GROUPING SETS / ROLLUP / CUBE", "QUERY.havingQual": "HAVING",
@@ -207,7 +322,7 @@ export async function describeKeys(rows, keys) {
207
322
  const proc = await read(procs, (l) => `SELECT oid::text, oid::regprocedure::text FROM pg_proc WHERE oid IN (${l})`);
208
323
  const op = await read(ops, (l) => `SELECT oid::text, oid::regoperator::text FROM pg_operator WHERE oid IN (${l})`);
209
324
  const type = await read(types, (l) => `SELECT oid::text, format_type(oid, NULL) FROM pg_type WHERE oid IN (${l})`);
210
- const typeName = (t) => t === "rowtype" ? "a table's row type" : t.startsWith("ext:") ? `${t.slice(4)} (an extension's)` : type.get(t) ?? `type OID ${t}`;
325
+ const typeName = (t) => t === "rowtype" ? "a table's row type" : t === HELD_KEY ? "a type the source made" : t === `${HELD_KEY}[]` ? "an array of a type the source made" : t.startsWith("ext:") ? `${t.slice(4)} (an extension's)` : type.get(t) ?? `type OID ${t}`;
211
326
  const extName = (v) => v.startsWith("ext:") ? `${v.slice(4)} (an extension's)` : `OID ${v}`;
212
327
  for (const k of list) {
213
328
  const kind = k.slice(0, k.indexOf(":")), value = k.slice(k.indexOf(":") + 1);