@barekey/cli 0.2.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.
- package/AGENTS.md +15 -2
- package/README.md +2 -0
- package/dist/commands/typegen.d.ts +7 -0
- package/dist/commands/typegen.js +79 -5
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/package.json +1 -1
- package/src/commands/typegen.ts +112 -6
- package/src/constants.ts +1 -1
- package/test/typegen.test.ts +38 -1
package/AGENTS.md
CHANGED
|
@@ -10,5 +10,18 @@
|
|
|
10
10
|
|
|
11
11
|
## Releases
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
### CLI update ritual
|
|
14
|
+
|
|
15
|
+
- If CLI work changes the shipped CLI behavior, commands, output, flags, package exports, or published artifacts, bump the CLI version before merging to `master`.
|
|
16
|
+
- Keep CLI versions aligned in both `package.json` and `src/constants.ts`.
|
|
17
|
+
- Use semantic versioning for the CLI bump: `patch` for backward-compatible fixes, `minor` for backward-compatible features, `major` for breaking changes.
|
|
18
|
+
- If the CLI depends on a newly released SDK feature or fix, update `@barekey/sdk` in `package.json`, run `bun install`, and commit the resulting `bun.lock` changes too.
|
|
19
|
+
- Before pushing, run:
|
|
20
|
+
- `bun test test`
|
|
21
|
+
- `bun run typecheck`
|
|
22
|
+
- `bun run build`
|
|
23
|
+
|
|
24
|
+
### SDK handoff from CLI
|
|
25
|
+
|
|
26
|
+
- If work includes merging changes to `/home/sander/barekey/sdk`, make sure the SDK repo is version-bumped first whenever its surface, generated types, runtime behavior, or published artifacts changed.
|
|
27
|
+
- After that SDK merge, update the CLI dependency to the new SDK version if the CLI consumes the changed behavior.
|
package/README.md
CHANGED
|
@@ -25,6 +25,8 @@ barekey env new FEATURE_FLAG true --type boolean --org acme --project api --stag
|
|
|
25
25
|
barekey env set CHECKOUT_COPY original --ab redesign --chance 0.25 --org acme --project api --stage development
|
|
26
26
|
barekey env delete FEATURE_FLAG --yes --org acme --project api --stage development
|
|
27
27
|
barekey env get-many --names DATABASE_URL,FEATURE_FLAG --org acme --project api --stage development
|
|
28
|
+
barekey typegen --org acme --project api --stage development
|
|
29
|
+
barekey typegen --watch --org acme --project api --stage development
|
|
28
30
|
```
|
|
29
31
|
|
|
30
32
|
## Development
|
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
import type { BarekeyTypegenResult } from "@barekey/sdk/server";
|
|
2
2
|
import { Command } from "commander";
|
|
3
3
|
export declare function formatTypegenResultMessage(result: BarekeyTypegenResult): string;
|
|
4
|
+
export declare function formatTypegenWatchStartedMessage(input: {
|
|
5
|
+
organization: string;
|
|
6
|
+
project: string;
|
|
7
|
+
environment: string;
|
|
8
|
+
intervalMs: number;
|
|
9
|
+
}): string;
|
|
10
|
+
export declare function parseTypegenWatchInterval(value: string | undefined): number;
|
|
4
11
|
export declare function registerTypegenCommand(program: Command): void;
|
package/dist/commands/typegen.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BarekeyClient } from "@barekey/sdk/server";
|
|
2
2
|
import pc from "picocolors";
|
|
3
3
|
import { addTargetOptions, requireLocalSession, resolveTarget, } from "../command-utils.js";
|
|
4
|
+
const DEFAULT_TYPEGEN_WATCH_INTERVAL_MS = 5_000;
|
|
4
5
|
export function formatTypegenResultMessage(result) {
|
|
5
6
|
const title = result.written
|
|
6
7
|
? pc.green(pc.bold("Typegen complete"))
|
|
@@ -15,26 +16,99 @@ export function formatTypegenResultMessage(result) {
|
|
|
15
16
|
`${pc.bold("Public types")}: ${result.publicPath}`,
|
|
16
17
|
].join("\n");
|
|
17
18
|
}
|
|
18
|
-
|
|
19
|
+
export function formatTypegenWatchStartedMessage(input) {
|
|
20
|
+
return [
|
|
21
|
+
pc.cyan(pc.bold("Watching Barekey typegen")),
|
|
22
|
+
`Target: ${pc.bold(`${input.organization}/${input.project}@${input.environment}`)}`,
|
|
23
|
+
`Polling every ${pc.bold(`${input.intervalMs}ms`)}. Press ${pc.bold("Ctrl+C")} to stop.`,
|
|
24
|
+
].join("\n");
|
|
25
|
+
}
|
|
26
|
+
export function parseTypegenWatchInterval(value) {
|
|
27
|
+
if (value === undefined) {
|
|
28
|
+
return DEFAULT_TYPEGEN_WATCH_INTERVAL_MS;
|
|
29
|
+
}
|
|
30
|
+
const parsed = Number.parseInt(value, 10);
|
|
31
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
32
|
+
throw new Error("--interval must be a positive integer number of milliseconds.");
|
|
33
|
+
}
|
|
34
|
+
return parsed;
|
|
35
|
+
}
|
|
36
|
+
function sleep(milliseconds) {
|
|
37
|
+
return new Promise((resolve) => {
|
|
38
|
+
setTimeout(resolve, milliseconds);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
async function resolveTypegenClient(options) {
|
|
19
42
|
const local = await requireLocalSession();
|
|
20
43
|
const target = await resolveTarget(options, local);
|
|
21
44
|
const organization = target.orgSlug ?? local.credentials.orgSlug;
|
|
22
45
|
if (!organization || organization.trim().length === 0) {
|
|
23
46
|
throw new Error("Organization slug is required.");
|
|
24
47
|
}
|
|
25
|
-
|
|
48
|
+
return {
|
|
49
|
+
client: new BarekeyClient({
|
|
50
|
+
organization,
|
|
51
|
+
project: target.projectSlug,
|
|
52
|
+
environment: target.stageSlug,
|
|
53
|
+
typegen: false,
|
|
54
|
+
}),
|
|
26
55
|
organization,
|
|
27
56
|
project: target.projectSlug,
|
|
28
57
|
environment: target.stageSlug,
|
|
29
|
-
|
|
30
|
-
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
async function runTypegen(options) {
|
|
61
|
+
const { client } = await resolveTypegenClient(options);
|
|
31
62
|
const result = await client.typegen();
|
|
32
63
|
console.log(formatTypegenResultMessage(result));
|
|
33
64
|
}
|
|
65
|
+
async function runTypegenWatch(options) {
|
|
66
|
+
const intervalMs = parseTypegenWatchInterval(options.interval);
|
|
67
|
+
const { client, organization, project, environment } = await resolveTypegenClient(options);
|
|
68
|
+
console.log(formatTypegenWatchStartedMessage({
|
|
69
|
+
organization,
|
|
70
|
+
project,
|
|
71
|
+
environment,
|
|
72
|
+
intervalMs,
|
|
73
|
+
}));
|
|
74
|
+
let stopped = false;
|
|
75
|
+
const stop = () => {
|
|
76
|
+
stopped = true;
|
|
77
|
+
};
|
|
78
|
+
process.once("SIGINT", stop);
|
|
79
|
+
process.once("SIGTERM", stop);
|
|
80
|
+
try {
|
|
81
|
+
let isFirstRun = true;
|
|
82
|
+
while (!stopped) {
|
|
83
|
+
const result = await client.typegen();
|
|
84
|
+
if (isFirstRun || result.written) {
|
|
85
|
+
if (!isFirstRun) {
|
|
86
|
+
console.log("");
|
|
87
|
+
}
|
|
88
|
+
console.log(formatTypegenResultMessage(result));
|
|
89
|
+
}
|
|
90
|
+
isFirstRun = false;
|
|
91
|
+
if (stopped) {
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
await sleep(intervalMs);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
finally {
|
|
98
|
+
process.removeListener("SIGINT", stop);
|
|
99
|
+
process.removeListener("SIGTERM", stop);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
34
102
|
export function registerTypegenCommand(program) {
|
|
35
103
|
addTargetOptions(program
|
|
36
104
|
.command("typegen")
|
|
37
|
-
.description("Generate Barekey SDK types in the installed @barekey/sdk module")
|
|
105
|
+
.description("Generate Barekey SDK types in the installed @barekey/sdk module")
|
|
106
|
+
.option("--watch", "Keep generated types up to date during development", false)
|
|
107
|
+
.option("--interval <ms>", "Polling interval for --watch in milliseconds")).action(async (options) => {
|
|
108
|
+
if (options.watch) {
|
|
109
|
+
await runTypegenWatch(options);
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
38
112
|
await runTypegen(options);
|
|
39
113
|
});
|
|
40
114
|
}
|
package/dist/constants.d.ts
CHANGED
package/dist/constants.js
CHANGED
package/package.json
CHANGED
package/src/commands/typegen.ts
CHANGED
|
@@ -10,6 +10,8 @@ import {
|
|
|
10
10
|
type EnvTargetOptions,
|
|
11
11
|
} from "../command-utils.js";
|
|
12
12
|
|
|
13
|
+
const DEFAULT_TYPEGEN_WATCH_INTERVAL_MS = 5_000;
|
|
14
|
+
|
|
13
15
|
export function formatTypegenResultMessage(result: BarekeyTypegenResult): string {
|
|
14
16
|
const title = result.written
|
|
15
17
|
? pc.green(pc.bold("Typegen complete"))
|
|
@@ -26,7 +28,44 @@ export function formatTypegenResultMessage(result: BarekeyTypegenResult): string
|
|
|
26
28
|
].join("\n");
|
|
27
29
|
}
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
export function formatTypegenWatchStartedMessage(input: {
|
|
32
|
+
organization: string;
|
|
33
|
+
project: string;
|
|
34
|
+
environment: string;
|
|
35
|
+
intervalMs: number;
|
|
36
|
+
}): string {
|
|
37
|
+
return [
|
|
38
|
+
pc.cyan(pc.bold("Watching Barekey typegen")),
|
|
39
|
+
`Target: ${pc.bold(`${input.organization}/${input.project}@${input.environment}`)}`,
|
|
40
|
+
`Polling every ${pc.bold(`${input.intervalMs}ms`)}. Press ${pc.bold("Ctrl+C")} to stop.`,
|
|
41
|
+
].join("\n");
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function parseTypegenWatchInterval(value: string | undefined): number {
|
|
45
|
+
if (value === undefined) {
|
|
46
|
+
return DEFAULT_TYPEGEN_WATCH_INTERVAL_MS;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const parsed = Number.parseInt(value, 10);
|
|
50
|
+
if (!Number.isFinite(parsed) || parsed <= 0) {
|
|
51
|
+
throw new Error("--interval must be a positive integer number of milliseconds.");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return parsed;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function sleep(milliseconds: number): Promise<void> {
|
|
58
|
+
return new Promise((resolve) => {
|
|
59
|
+
setTimeout(resolve, milliseconds);
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function resolveTypegenClient(options: EnvTargetOptions): Promise<{
|
|
64
|
+
client: BarekeyClient;
|
|
65
|
+
organization: string;
|
|
66
|
+
project: string;
|
|
67
|
+
environment: string;
|
|
68
|
+
}> {
|
|
30
69
|
const local = await requireLocalSession();
|
|
31
70
|
const target = await resolveTarget(options, local);
|
|
32
71
|
const organization = target.orgSlug ?? local.credentials.orgSlug;
|
|
@@ -34,23 +73,90 @@ async function runTypegen(options: EnvTargetOptions): Promise<void> {
|
|
|
34
73
|
throw new Error("Organization slug is required.");
|
|
35
74
|
}
|
|
36
75
|
|
|
37
|
-
|
|
76
|
+
return {
|
|
77
|
+
client: new BarekeyClient({
|
|
78
|
+
organization,
|
|
79
|
+
project: target.projectSlug,
|
|
80
|
+
environment: target.stageSlug,
|
|
81
|
+
typegen: false,
|
|
82
|
+
}),
|
|
38
83
|
organization,
|
|
39
84
|
project: target.projectSlug,
|
|
40
85
|
environment: target.stageSlug,
|
|
41
|
-
|
|
42
|
-
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function runTypegen(options: EnvTargetOptions): Promise<void> {
|
|
90
|
+
const { client } = await resolveTypegenClient(options);
|
|
43
91
|
const result = await client.typegen();
|
|
44
92
|
|
|
45
93
|
console.log(formatTypegenResultMessage(result));
|
|
46
94
|
}
|
|
47
95
|
|
|
96
|
+
async function runTypegenWatch(
|
|
97
|
+
options: EnvTargetOptions & {
|
|
98
|
+
interval?: string;
|
|
99
|
+
},
|
|
100
|
+
): Promise<void> {
|
|
101
|
+
const intervalMs = parseTypegenWatchInterval(options.interval);
|
|
102
|
+
const { client, organization, project, environment } = await resolveTypegenClient(options);
|
|
103
|
+
|
|
104
|
+
console.log(
|
|
105
|
+
formatTypegenWatchStartedMessage({
|
|
106
|
+
organization,
|
|
107
|
+
project,
|
|
108
|
+
environment,
|
|
109
|
+
intervalMs,
|
|
110
|
+
}),
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
let stopped = false;
|
|
114
|
+
const stop = () => {
|
|
115
|
+
stopped = true;
|
|
116
|
+
};
|
|
117
|
+
process.once("SIGINT", stop);
|
|
118
|
+
process.once("SIGTERM", stop);
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
let isFirstRun = true;
|
|
122
|
+
while (!stopped) {
|
|
123
|
+
const result = await client.typegen();
|
|
124
|
+
if (isFirstRun || result.written) {
|
|
125
|
+
if (!isFirstRun) {
|
|
126
|
+
console.log("");
|
|
127
|
+
}
|
|
128
|
+
console.log(formatTypegenResultMessage(result));
|
|
129
|
+
}
|
|
130
|
+
isFirstRun = false;
|
|
131
|
+
|
|
132
|
+
if (stopped) {
|
|
133
|
+
break;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
await sleep(intervalMs);
|
|
137
|
+
}
|
|
138
|
+
} finally {
|
|
139
|
+
process.removeListener("SIGINT", stop);
|
|
140
|
+
process.removeListener("SIGTERM", stop);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
48
144
|
export function registerTypegenCommand(program: Command): void {
|
|
49
145
|
addTargetOptions(
|
|
50
146
|
program
|
|
51
147
|
.command("typegen")
|
|
52
|
-
.description("Generate Barekey SDK types in the installed @barekey/sdk module")
|
|
53
|
-
|
|
148
|
+
.description("Generate Barekey SDK types in the installed @barekey/sdk module")
|
|
149
|
+
.option("--watch", "Keep generated types up to date during development", false)
|
|
150
|
+
.option(
|
|
151
|
+
"--interval <ms>",
|
|
152
|
+
"Polling interval for --watch in milliseconds",
|
|
153
|
+
),
|
|
154
|
+
).action(async (options: EnvTargetOptions & { watch?: boolean; interval?: string }) => {
|
|
155
|
+
if (options.watch) {
|
|
156
|
+
await runTypegenWatch(options);
|
|
157
|
+
return;
|
|
158
|
+
}
|
|
159
|
+
|
|
54
160
|
await runTypegen(options);
|
|
55
161
|
});
|
|
56
162
|
}
|
package/src/constants.ts
CHANGED
package/test/typegen.test.ts
CHANGED
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
|
|
3
|
-
import {
|
|
3
|
+
import {
|
|
4
|
+
formatTypegenResultMessage,
|
|
5
|
+
formatTypegenWatchStartedMessage,
|
|
6
|
+
parseTypegenWatchInterval,
|
|
7
|
+
} from "../src/commands/typegen";
|
|
4
8
|
|
|
5
9
|
describe("formatTypegenResultMessage", () => {
|
|
6
10
|
test("renders a helpful success message when files were updated", () => {
|
|
@@ -34,4 +38,37 @@ describe("formatTypegenResultMessage", () => {
|
|
|
34
38
|
expect(message).toContain("/tmp/generated.server.d.ts");
|
|
35
39
|
expect(message).toContain("/tmp/generated.public.d.ts");
|
|
36
40
|
});
|
|
41
|
+
|
|
42
|
+
test("renders a watch banner with target details", () => {
|
|
43
|
+
const message = formatTypegenWatchStartedMessage({
|
|
44
|
+
organization: "acme",
|
|
45
|
+
project: "web",
|
|
46
|
+
environment: "development",
|
|
47
|
+
intervalMs: 5000,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
expect(message).toContain("Watching Barekey typegen");
|
|
51
|
+
expect(message).toContain("acme/web@development");
|
|
52
|
+
expect(message).toContain("5000ms");
|
|
53
|
+
expect(message).toContain("Ctrl+C");
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
describe("parseTypegenWatchInterval", () => {
|
|
58
|
+
test("defaults to five seconds", () => {
|
|
59
|
+
expect(parseTypegenWatchInterval(undefined)).toBe(5000);
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test("accepts a positive integer number of milliseconds", () => {
|
|
63
|
+
expect(parseTypegenWatchInterval("1500")).toBe(1500);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test("rejects invalid watch intervals", () => {
|
|
67
|
+
expect(() => parseTypegenWatchInterval("0")).toThrow(
|
|
68
|
+
"--interval must be a positive integer number of milliseconds.",
|
|
69
|
+
);
|
|
70
|
+
expect(() => parseTypegenWatchInterval("abc")).toThrow(
|
|
71
|
+
"--interval must be a positive integer number of milliseconds.",
|
|
72
|
+
);
|
|
73
|
+
});
|
|
37
74
|
});
|