flostep 0.1.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/AGENTS.md +105 -0
- package/LICENSE +21 -0
- package/README.md +112 -0
- package/bin/flostep.js +6 -0
- package/package.json +36 -0
- package/src/api.js +197 -0
- package/src/browser.js +46 -0
- package/src/cli.js +264 -0
- package/src/code.js +151 -0
- package/src/commands/create.js +55 -0
- package/src/commands/delete.js +59 -0
- package/src/commands/folder.js +141 -0
- package/src/commands/init.js +106 -0
- package/src/commands/list.js +56 -0
- package/src/commands/login.js +166 -0
- package/src/commands/logout.js +30 -0
- package/src/commands/move.js +48 -0
- package/src/commands/node.js +87 -0
- package/src/commands/open.js +29 -0
- package/src/commands/share.js +47 -0
- package/src/commands/show.js +30 -0
- package/src/commands/step.js +129 -0
- package/src/commands/syntax.js +17 -0
- package/src/commands/update.js +71 -0
- package/src/commands/whoami.js +39 -0
- package/src/config.js +67 -0
- package/src/errors.js +26 -0
- package/src/instructions.js +121 -0
- package/src/output.js +170 -0
- package/src/source.js +63 -0
- package/src/stdin.js +29 -0
- package/src/target.js +30 -0
- package/src/version.js +17 -0
package/src/source.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Deciding whether what arrived on stdin is a diagram, before it is sent.
|
|
2
|
+
//
|
|
3
|
+
// `create` and `update` used to hand anything to the server and let a 422 come
|
|
4
|
+
// back. That is a slow no, and the text has already left the machine by the
|
|
5
|
+
// time something objects — which matters when the pipe was `cat .env` rather
|
|
6
|
+
// than a flow.
|
|
7
|
+
//
|
|
8
|
+
// The checks here are deliberately weaker than the server's. This is the third
|
|
9
|
+
// copy of the grammar (see code.js), so anything ambiguous goes to the server
|
|
10
|
+
// and lets it decide; only input with no steps at all is refused outright.
|
|
11
|
+
|
|
12
|
+
import { CliError } from "./errors.js";
|
|
13
|
+
import { parseSteps, componentsOf, MAX_STEPS, MAX_COMPONENTS } from "./code.js";
|
|
14
|
+
|
|
15
|
+
// What the parser made of the text: the counts `create` prints, and everything
|
|
16
|
+
// it skipped — the tell for a pipe carrying prose rather than steps.
|
|
17
|
+
export function summarize(code) {
|
|
18
|
+
const steps = parseSteps(code);
|
|
19
|
+
const stepLines = new Set(steps.map((s) => s.lineIndex));
|
|
20
|
+
|
|
21
|
+
const ignored = [];
|
|
22
|
+
code.split("\n").forEach((raw, index) => {
|
|
23
|
+
const line = raw.trim();
|
|
24
|
+
if (!line || line.startsWith("#") || line.startsWith("%%") || stepLines.has(index)) return;
|
|
25
|
+
ignored.push({ line: index + 1, text: line });
|
|
26
|
+
});
|
|
27
|
+
|
|
28
|
+
return { steps: steps.length, components: componentsOf(code), ignored };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Limits mirrored from the server, so oversized input fails here instead of as
|
|
32
|
+
// a 422 after the upload.
|
|
33
|
+
export function diagramFromStdin(raw) {
|
|
34
|
+
const code = raw.replace(/\r\n/g, "\n").trim();
|
|
35
|
+
const summary = summarize(code);
|
|
36
|
+
|
|
37
|
+
if (summary.steps === 0) {
|
|
38
|
+
const first = code.split("\n").find((line) => line.trim()) ?? "";
|
|
39
|
+
throw new CliError("What arrived on stdin has no steps — it doesn't look like a diagram.", {
|
|
40
|
+
hint: `Line 1: ${first.slice(0, 60)}\n Steps look like \`Frontend -> API: POST /login\` — run \`flostep syntax\` for the format.`
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (summary.steps > MAX_STEPS) {
|
|
45
|
+
throw new CliError(`That is ${summary.steps} steps, past the limit of ${MAX_STEPS}.`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (summary.components.length > MAX_COMPONENTS) {
|
|
49
|
+
throw new CliError(
|
|
50
|
+
`That is ${summary.components.length} components, past the limit of ${MAX_COMPONENTS}.`,
|
|
51
|
+
{ hint: "Reusing an existing name references the same box — different spellings make different ones." }
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return { code, summary };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// "4 steps, 3 components" — printed on success, because input that parsed into
|
|
59
|
+
// something absurd ($obj -> method();) is only obvious when counted.
|
|
60
|
+
export function describe(summary) {
|
|
61
|
+
const plural = (n, word) => `${n} ${word}${n === 1 ? "" : "s"}`;
|
|
62
|
+
return `${plural(summary.steps, "step")}, ${plural(summary.components.length, "component")}`;
|
|
63
|
+
}
|
package/src/stdin.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
// Reading steps from a pipe.
|
|
2
|
+
//
|
|
3
|
+
// This is what makes the CLI usable without touching the filesystem at all: an
|
|
4
|
+
// agent asked to "explain this flow" wants a link, not an untracked file in
|
|
5
|
+
// someone else's checkout. Nothing here writes to disk — redirect `show` if you
|
|
6
|
+
// want the steps in a file.
|
|
7
|
+
|
|
8
|
+
import { CliError } from "./errors.js";
|
|
9
|
+
|
|
10
|
+
export async function readStdin({ what }) {
|
|
11
|
+
// A TTY means nothing was piped in — the user typed the command and is now
|
|
12
|
+
// staring at a cursor with no idea the process is waiting on them.
|
|
13
|
+
if (process.stdin.isTTY) {
|
|
14
|
+
throw new CliError(`No steps on stdin.`, {
|
|
15
|
+
hint: `Pipe them in — ${what} — or redirect a file into it: \`flostep create < flow.txt\`.`
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const chunks = [];
|
|
20
|
+
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
21
|
+
const text = Buffer.concat(chunks).toString("utf8").trim();
|
|
22
|
+
|
|
23
|
+
if (!text) {
|
|
24
|
+
throw new CliError("Nothing arrived on stdin.", {
|
|
25
|
+
hint: "Steps look like `Frontend -> API: POST /login` — run `flostep syntax` for the format."
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
return text;
|
|
29
|
+
}
|
package/src/target.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Turning "15" into a diagram id.
|
|
2
|
+
//
|
|
3
|
+
// Every command that acts on one diagram takes an id, and only an id. Diagrams
|
|
4
|
+
// live in the account, not in a checkout: `create` and `update` read steps from
|
|
5
|
+
// a pipe and write nothing to disk, so there is no local file to name and
|
|
6
|
+
// nothing to keep in sync.
|
|
7
|
+
|
|
8
|
+
import { CliError } from "./errors.js";
|
|
9
|
+
|
|
10
|
+
// A bare positive integer. Ids come from `flostep list`, from `create`, or
|
|
11
|
+
// from the user — never from a guess.
|
|
12
|
+
export function looksLikeId(ref) {
|
|
13
|
+
return /^[1-9][0-9]*$/.test(String(ref).trim());
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function resolveTarget(ref) {
|
|
17
|
+
if (!ref) {
|
|
18
|
+
throw new CliError("Which diagram? Pass an id.", {
|
|
19
|
+
hint: "Run `flostep list` to see ids."
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (!looksLikeId(ref)) {
|
|
24
|
+
throw new CliError(`"${ref}" is not a diagram id.`, {
|
|
25
|
+
hint: "Ids are numbers — run `flostep list` to see them."
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return { id: Number(ref) };
|
|
30
|
+
}
|
package/src/version.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// The version, read once from package.json, and the User-Agent built from it.
|
|
2
|
+
//
|
|
3
|
+
// Every request identifies the client and its version. Without that the server
|
|
4
|
+
// can't tell CLI traffic from any other API key, can't count who is on which
|
|
5
|
+
// release, and can never turn away a version with a known bug — and a version
|
|
6
|
+
// published without the header stays anonymous for as long as anyone runs it,
|
|
7
|
+
// which is why it went in before the first publish rather than after.
|
|
8
|
+
|
|
9
|
+
import { readFileSync } from "node:fs";
|
|
10
|
+
import { fileURLToPath } from "node:url";
|
|
11
|
+
import { dirname, join } from "node:path";
|
|
12
|
+
|
|
13
|
+
const here = dirname(fileURLToPath(import.meta.url));
|
|
14
|
+
|
|
15
|
+
export const VERSION = JSON.parse(readFileSync(join(here, "..", "package.json"), "utf8")).version;
|
|
16
|
+
|
|
17
|
+
export const USER_AGENT = `flostep-cli/${VERSION} node/${process.versions.node} ${process.platform}`;
|