@mason-data/cli 0.7.0 → 0.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +90 -71
- package/dist/src/api/error.js +3 -0
- package/dist/src/api/postgres/catalog.js +222 -13
- package/dist/src/api/postgres/encode.js +3 -1
- package/dist/src/api/postgres/explain.js +3 -2
- package/dist/src/api/postgres/protocol.js +39 -23
- package/dist/src/api/postgres/server.js +1173 -207
- package/dist/src/api/postgres/translate.js +81 -9
- package/dist/src/api/request-log.js +18 -5
- package/dist/src/cdc/poll.js +5 -5
- package/dist/src/cli/background.js +29 -6
- package/dist/src/cli/cancel.js +75 -0
- package/dist/src/cli/command.js +1 -0
- package/dist/src/cli/commands.js +622 -183
- package/dist/src/cli/control.js +59 -0
- package/dist/src/cli/demo.js +19 -10
- package/dist/src/cli/dev.js +351 -111
- package/dist/src/cli/doctor.js +20 -10
- package/dist/src/cli/guides.js +79 -31
- package/dist/src/cli/init.js +119 -58
- package/dist/src/cli/machine.js +12 -11
- package/dist/src/cli/main.js +3 -3
- package/dist/src/cli/mcp.js +29 -20
- package/dist/src/cli/metrics.js +79 -0
- package/dist/src/cli/pg-tls.js +13 -3
- package/dist/src/cli/pg-url.js +4 -0
- package/dist/src/cli/pin.js +13 -12
- package/dist/src/cli/preflight.js +8 -7
- package/dist/src/cli/project.js +43 -4
- package/dist/src/cli/readiness.js +38 -4
- package/dist/src/cli/registry.js +53 -34
- package/dist/src/cli/replication-checks.js +7 -5
- package/dist/src/cli/replication-mode.js +105 -26
- package/dist/src/cli/request-filter.js +134 -0
- package/dist/src/cli/roles.js +584 -0
- package/dist/src/cli/run-server.js +483 -41
- package/dist/src/cli/runs.js +1 -1
- package/dist/src/cli/security.js +10 -8
- package/dist/src/cli/serve.js +144 -42
- package/dist/src/cli/share-run.js +286 -0
- package/dist/src/cli/share.js +286 -0
- package/dist/src/cli/source-edit.js +65 -13
- package/dist/src/cli/sources.js +287 -38
- package/dist/src/cli/supervise.js +12 -1
- package/dist/src/cli/sync.js +55 -25
- package/dist/src/cli/tunnel.js +200 -0
- package/dist/src/cli/ui.js +9 -6
- package/dist/src/cli/usage.js +128 -0
- package/dist/src/cli/user.js +496 -40
- package/dist/src/cli/version.js +9 -7
- package/dist/src/demo/seed.js +3 -0
- package/dist/src/duck/cancel.js +10 -1
- package/dist/src/duck/events.js +134 -0
- package/dist/src/duck/executor-worker.js +18 -9
- package/dist/src/duck/executor.js +43 -30
- package/dist/src/duck/explain.js +56 -46
- package/dist/src/duck/extensions.js +160 -0
- package/dist/src/duck/functions.js +13 -14
- package/dist/src/duck/gaps.js +17 -8
- package/dist/src/duck/grants.js +176 -0
- package/dist/src/duck/history.js +123 -0
- package/dist/src/duck/kept.js +6 -6
- package/dist/src/duck/plan.js +102 -25
- package/dist/src/duck/publication.js +6 -6
- package/dist/src/duck/requests.js +265 -93
- package/dist/src/duck/rewrite.js +25 -5
- package/dist/src/duck/row-filter.js +649 -0
- package/dist/src/duck/store.js +156 -120
- package/dist/src/duck/stream.js +10 -5
- package/dist/src/duck/sync.js +835 -268
- package/dist/src/duck/types.js +6 -5
- package/dist/src/duck/usage.js +324 -0
- package/dist/src/duck/users.js +64 -15
- package/dist/src/duck/verified.js +3 -0
- package/dist/src/duck/views.js +2 -3
- package/dist/src/lib/database-name.js +62 -0
- package/dist/src/lib/dotenv.js +17 -4
- package/dist/src/lib/log-buffer.js +68 -0
- package/dist/src/lib/pg-numeric.js +5 -2
- package/dist/src/lib/project-version.js +23 -23
- package/dist/src/lib/table-sync.js +267 -0
- package/dist/src/web/app.css +706 -66
- package/dist/src/web/app.js +327 -410
- package/dist/src/web/charts.js +229 -0
- package/dist/src/web/index.html +82 -18
- package/dist/src/web/pages/queries.js +192 -0
- package/dist/src/web/pages/status.js +446 -0
- package/dist/src/web/pages/syncs.js +155 -0
- package/dist/src/web/pages/tables.js +434 -0
- package/dist/src/web/pages/usage.js +397 -0
- package/dist/src/web/pages/users.js +218 -0
- package/dist/src/web/shared.js +568 -0
- package/dist/src/web/vendor/README.md +25 -0
- package/dist/src/web/vendor/geist-1.7.2/Geist-Variable.woff2 +0 -0
- package/dist/src/web/vendor/geist-1.7.2/GeistMono-Variable.woff2 +0 -0
- package/dist/src/web/vendor/geist-1.7.2/LICENSE.txt +92 -0
- package/dist/src/web/vendor/lucide-1.56.0/LICENSE.txt +43 -0
- package/dist/src/web/vendor/lucide-1.56.0/chart-column.mjs +29 -0
- package/dist/src/web/vendor/lucide-1.56.0/layout-dashboard.mjs +29 -0
- package/dist/src/web/vendor/lucide-1.56.0/logs.mjs +34 -0
- package/dist/src/web/vendor/lucide-1.56.0/menu.mjs +28 -0
- package/dist/src/web/vendor/lucide-1.56.0/square-terminal.mjs +28 -0
- package/dist/src/web/vendor/lucide-1.56.0/table.mjs +29 -0
- package/dist/src/web/vendor/lucide-1.56.0/users.mjs +29 -0
- package/dist/src/web/vendor/lucide-1.56.0/x.mjs +27 -0
- package/dist/src/web/vendor/uplot-1.6.32/LICENSE.txt +21 -0
- package/dist/src/web/vendor/uplot-1.6.32/uPlot.esm.js +6140 -0
- package/dist/src/web/vendor/uplot-1.6.32/uPlot.min.css +1 -0
- package/dist/src/web/vendor/zilla-slab-5.3.0/LICENSE.txt +93 -0
- package/dist/src/web/vendor/zilla-slab-5.3.0/zilla-slab-latin-700-normal.woff2 +0 -0
- package/package.json +10 -3
package/dist/src/cli/control.js
CHANGED
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* THE COMMANDS' LINE TO A RUN: `mason status`, `mason tables`, `mason add` and `mason wait` ask the run's
|
|
3
|
+
* command server (src/cli/run-server.ts) — with the
|
|
4
|
+
* run's token as `Authorization: Bearer` (src/cli/security.ts), read from the project's .mason/config.json (src/cli/runs.ts).
|
|
5
|
+
*/
|
|
6
|
+
import * as http from "node:http";
|
|
1
7
|
/** The run does not answer: a failure with the reason as `why` and mason logs as the way out (`failure` is what a command
|
|
2
8
|
* hands src/cli/command.ts failed). */
|
|
3
9
|
export class RunUnreachable extends Error {
|
|
@@ -34,3 +40,56 @@ export async function callRun(run, method, route, body, timeoutMs = 30_000) {
|
|
|
34
40
|
}
|
|
35
41
|
return { status: res.status, body: parsed };
|
|
36
42
|
}
|
|
43
|
+
/**
|
|
44
|
+
* ONE REQUEST HELD OPEN (src/cli/share.ts): the run answers a line of JSON and keeps the answer open, so the end of the
|
|
45
|
+
* connection is the end of what was asked, however this process ends. On node:http, not fetch: fetch gives up on a body
|
|
46
|
+
* silent for five minutes, and here silence is the normal state. A refusal is answered whole, as `callRun` returns it.
|
|
47
|
+
*/
|
|
48
|
+
export function holdRun(run, route, body, timeoutMs = 30_000) {
|
|
49
|
+
return new Promise((resolve, reject) => {
|
|
50
|
+
const unreachable = (why) => new RunUnreachable(`Mason for ${run.name} (pid ${run.pid}) does not answer at ${run.control.url}`, why);
|
|
51
|
+
const text = JSON.stringify(body);
|
|
52
|
+
const req = http.request(`${run.control.url}${route}`, { method: "POST", headers: { Authorization: `Bearer ${run.control.token}`, "Content-Type": "application/json", "Content-Length": Buffer.byteLength(text) } }, (res) => {
|
|
53
|
+
let got = "", first = null, said = null;
|
|
54
|
+
let finish = () => undefined;
|
|
55
|
+
const ended = new Promise((r) => { finish = r; });
|
|
56
|
+
res.setEncoding("utf8");
|
|
57
|
+
res.on("data", (chunk) => {
|
|
58
|
+
got += chunk;
|
|
59
|
+
if (res.statusCode !== 200)
|
|
60
|
+
return;
|
|
61
|
+
for (let at = got.indexOf("\n"); at >= 0; at = got.indexOf("\n")) {
|
|
62
|
+
const line = JSON.parse(got.slice(0, at));
|
|
63
|
+
got = got.slice(at + 1);
|
|
64
|
+
if (first === null) {
|
|
65
|
+
first = line;
|
|
66
|
+
clearTimeout(timer);
|
|
67
|
+
resolve({ first, ended, close: () => req.destroy() });
|
|
68
|
+
}
|
|
69
|
+
else if (typeof line.ended === "string")
|
|
70
|
+
said = line.ended;
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
res.on("close", () => {
|
|
74
|
+
clearTimeout(timer);
|
|
75
|
+
if (res.statusCode === 200) {
|
|
76
|
+
if (first === null)
|
|
77
|
+
reject(unreachable("it closed the answer before it said anything"));
|
|
78
|
+
finish(said);
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let parsed;
|
|
82
|
+
try {
|
|
83
|
+
parsed = JSON.parse(got);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
parsed = { error: got.trim() || `HTTP ${res.statusCode}` };
|
|
87
|
+
}
|
|
88
|
+
resolve({ status: res.statusCode ?? 0, body: parsed });
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
const timer = setTimeout(() => { req.destroy(); reject(unreachable(`no answer in ${timeoutMs / 1000} s`)); }, timeoutMs);
|
|
92
|
+
req.on("error", (e) => { clearTimeout(timer); reject(unreachable(e.code ?? e.message)); });
|
|
93
|
+
req.end(text);
|
|
94
|
+
});
|
|
95
|
+
}
|
package/dist/src/cli/demo.js
CHANGED
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
*
|
|
10
10
|
* - THE PORT LIVES IN THE URL ALONE: `mason init demo` picks one free and writes it into `.env`; `mason start` reads
|
|
11
11
|
* it back from the resolved URL (`demoPort`), never from a second record that could disagree with it.
|
|
12
|
-
* - POLL ONLY: the demo database has no change log (no WAL sender), so
|
|
13
|
-
* starts (`demoChangesRefusal`).
|
|
12
|
+
* - POLL ONLY: the demo database has no change log (no WAL sender), so a table whose `sync:` is `replication` is
|
|
13
|
+
* refused before anything starts (`demoChangesRefusal`).
|
|
14
14
|
* - A WRITER: every MASON_DEMO_CHANGE_MS (`demoSettings`) the run makes one round of changes (`demoChanges`), so the
|
|
15
15
|
* copy is seen keeping current.
|
|
16
16
|
*
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import * as path from "node:path";
|
|
21
21
|
import { parseSource, isLocalHost } from "./preflight.js";
|
|
22
|
-
import {
|
|
22
|
+
import { entryName, replicatedEntries } from "./sources.js";
|
|
23
23
|
import { heldText, portHolder } from "./ports.js";
|
|
24
24
|
import { DEMO_DIR, STATE_DIR } from "./project.js";
|
|
25
25
|
import { DEMO_DATABASE, DEMO_USER, demoUrl } from "../demo/seed.js";
|
|
@@ -30,7 +30,7 @@ export const DEMO_PORT = 5434;
|
|
|
30
30
|
/** The demo database's tables (the seed's own list), the ones `mason init demo` adds to the sources file and the one it
|
|
31
31
|
* leaves for the person to add, and its URL (src/demo/seed.ts: no PGlite is loaded by it, so `mason init demo` and
|
|
32
32
|
* `demoPort` never load the database). */
|
|
33
|
-
export { DEMO_ADDED_TABLES, DEMO_TABLES, DEMO_TABLE_TO_ADD, demoUrl } from "../demo/seed.js";
|
|
33
|
+
export { DEMO_ADDED_TABLES, DEMO_SYNC, DEMO_TABLES, DEMO_TABLE_TO_ADD, demoUrl } from "../demo/seed.js";
|
|
34
34
|
/** How often the run changes the demo database by default (MASON_DEMO_CHANGE_MS). */
|
|
35
35
|
export const DEMO_CHANGE_MS = 5000;
|
|
36
36
|
const DEMO_CHANGE_MIN_MS = 100;
|
|
@@ -69,14 +69,23 @@ export function demoPort(url, from) {
|
|
|
69
69
|
return { problem: `${from} was changed: the demo database is ${demoUrl(s.port)}; ${wayOut}` };
|
|
70
70
|
return { port: s.port };
|
|
71
71
|
}
|
|
72
|
-
/** The refusal of logical replication for a demo project (
|
|
73
|
-
*
|
|
74
|
-
|
|
75
|
-
|
|
72
|
+
/** The refusal of logical replication for a demo project (a table whose sync: is replication in its sources file,
|
|
73
|
+
* `shown`, or the mode an older Mason recorded in the config, edited to it): before anything starts, with the way out;
|
|
74
|
+
* null for any other project, or a demo one polled. */
|
|
75
|
+
export function demoChangesRefusal(cfg, file = null, shown = "") {
|
|
76
|
+
if (cfg.demo !== true)
|
|
76
77
|
return null;
|
|
77
|
-
|
|
78
|
+
const streamed = file === null ? [] : replicatedEntries(file).map((e) => entryName(e.table));
|
|
79
|
+
if (streamed.length) {
|
|
80
|
+
return { message: `the demo database is read by polling only: sync: replication does not apply to it (${streamed.join(", ")})`, code: "refused",
|
|
81
|
+
why: "the demo database has no change log for logical replication to read; Mason polls it with SELECTs",
|
|
82
|
+
fix: `write sync: poll 5s for ${streamed.length === 1 ? "it" : "them"} in ${shown}, then mason start`, next: [["mason start", "the demo database and Mason, polled"]] };
|
|
83
|
+
}
|
|
84
|
+
if (cfg.changes !== "replication")
|
|
85
|
+
return null;
|
|
86
|
+
return { message: `the demo database is read by polling only: "changes": "replication" in ${STATE_DIR}/config.json does not apply to it`, code: "refused",
|
|
78
87
|
why: "the demo database has no change log for logical replication to read; mason start polls it with SELECTs, every few seconds",
|
|
79
|
-
fix:
|
|
88
|
+
fix: `remove "changes": "replication" from ${STATE_DIR}/config.json, then mason start`,
|
|
80
89
|
next: [["mason start", "the demo database and Mason, polled"]] };
|
|
81
90
|
}
|
|
82
91
|
/** The step's done line: where the demo database listens, and whether it was seeded now or kept from the last run. */
|