@barekey/cli 0.4.0 → 0.5.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 +53 -12
- package/bun.lock +9 -3
- package/dist/auth-provider.js +7 -4
- package/dist/command-utils.js +6 -6
- package/dist/commands/audit.d.ts +2 -0
- package/dist/commands/audit.js +47 -0
- package/dist/commands/auth.js +22 -7
- package/dist/commands/billing.d.ts +2 -0
- package/dist/commands/billing.js +62 -0
- package/dist/commands/env.js +157 -125
- package/dist/commands/init.d.ts +2 -0
- package/dist/commands/init.js +32 -0
- package/dist/commands/org.d.ts +2 -0
- package/dist/commands/org.js +85 -0
- package/dist/commands/project.d.ts +2 -0
- package/dist/commands/project.js +99 -0
- package/dist/commands/stage.d.ts +2 -0
- package/dist/commands/stage.js +125 -0
- package/dist/commands/target-prompts.d.ts +184 -0
- package/dist/commands/target-prompts.js +312 -0
- package/dist/commands/typegen.d.ts +2 -2
- package/dist/commands/typegen.js +57 -32
- package/dist/constants.d.ts +1 -1
- package/dist/constants.js +1 -1
- package/dist/context/session-id.d.ts +11 -0
- package/dist/context/session-id.js +14 -0
- package/dist/contracts/index.d.ts +491 -0
- package/dist/contracts/index.js +307 -0
- package/dist/credentials-store.js +70 -11
- package/dist/http.d.ts +34 -0
- package/dist/http.js +56 -2
- package/dist/index.js +12 -0
- package/dist/runtime-config.js +14 -26
- package/dist/typegen/core.d.ts +45 -0
- package/dist/typegen/core.js +219 -0
- package/dist/types.d.ts +5 -3
- package/package.json +2 -2
- package/src/auth-provider.ts +8 -5
- package/src/command-utils.ts +6 -6
- package/src/commands/audit.ts +63 -0
- package/src/commands/auth.ts +32 -37
- package/src/commands/billing.ts +73 -0
- package/src/commands/env.ts +211 -218
- package/src/commands/init.ts +47 -0
- package/src/commands/org.ts +104 -0
- package/src/commands/project.ts +130 -0
- package/src/commands/stage.ts +167 -0
- package/src/commands/target-prompts.ts +357 -0
- package/src/commands/typegen.ts +71 -45
- package/src/constants.ts +1 -1
- package/src/context/session-id.ts +14 -0
- package/src/contracts/index.ts +370 -0
- package/src/credentials-store.ts +86 -12
- package/src/http.ts +78 -2
- package/src/index.ts +12 -0
- package/src/runtime-config.ts +19 -32
- package/src/typegen/core.ts +311 -0
- package/src/types.ts +5 -3
- package/test/command-utils.test.ts +47 -0
- package/test/credentials-store.test.ts +40 -0
package/src/runtime-config.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
import { access, readFile } from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { Either, Schema } from "effect";
|
|
4
|
+
|
|
5
|
+
import { RuntimeConfigSchema } from "./contracts/index.js";
|
|
3
6
|
|
|
4
7
|
export type BarekeyRuntimeConfig = {
|
|
5
8
|
org?: string;
|
|
@@ -48,42 +51,26 @@ export async function loadRuntimeConfig(): Promise<BarekeyRuntimeConfigResult |
|
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
const raw = await readFile(configPath, "utf8");
|
|
51
|
-
const parsed = JSON.parse(raw) as
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
54
|
+
const parsed = JSON.parse(raw) as unknown;
|
|
55
|
+
const decoded = Schema.decodeUnknownEither(RuntimeConfigSchema)(parsed);
|
|
56
|
+
if (Either.isLeft(decoded)) {
|
|
57
|
+
throw new Error(`Invalid barekey.json at ${configPath}.`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const config = decoded.right.config;
|
|
61
|
+
const mode = config?.mode ?? "centralized";
|
|
62
|
+
const typegen =
|
|
63
|
+
mode === "standalone" ? "minimal" : (config?.typegen ?? decoded.right.typegen ?? "semantic");
|
|
64
|
+
|
|
56
65
|
return {
|
|
57
66
|
path: configPath,
|
|
58
67
|
config: {
|
|
59
|
-
org:
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
: typeof parsed.org === "string"
|
|
63
|
-
? parsed.org.trim()
|
|
64
|
-
: undefined,
|
|
65
|
-
project: typeof parsed.project === "string" ? parsed.project.trim() : undefined,
|
|
66
|
-
environment:
|
|
67
|
-
typeof parsed.environment === "string"
|
|
68
|
-
? parsed.environment.trim()
|
|
69
|
-
: typeof parsed.stage === "string"
|
|
70
|
-
? parsed.stage.trim()
|
|
71
|
-
: undefined,
|
|
68
|
+
org: (decoded.right.organization ?? decoded.right.org ?? undefined)?.trim() || undefined,
|
|
69
|
+
project: decoded.right.project?.trim() || undefined,
|
|
70
|
+
environment: (decoded.right.environment ?? decoded.right.stage ?? undefined)?.trim() || undefined,
|
|
72
71
|
config: {
|
|
73
|
-
mode
|
|
74
|
-
|
|
75
|
-
? config.mode
|
|
76
|
-
: "centralized",
|
|
77
|
-
typegen:
|
|
78
|
-
(config?.mode === "centralized" || config?.mode === "standalone"
|
|
79
|
-
? config.mode
|
|
80
|
-
: "centralized") === "standalone"
|
|
81
|
-
? "minimal"
|
|
82
|
-
: config?.typegen === "semantic" || config?.typegen === "minimal"
|
|
83
|
-
? config.typegen
|
|
84
|
-
: parsed.typegen === "semantic" || parsed.typegen === "minimal"
|
|
85
|
-
? parsed.typegen
|
|
86
|
-
: "semantic",
|
|
72
|
+
mode,
|
|
73
|
+
typegen,
|
|
87
74
|
},
|
|
88
75
|
},
|
|
89
76
|
};
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import { access, readFile, rename, unlink, writeFile } from "node:fs/promises";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { Schema } from "effect";
|
|
6
|
+
|
|
7
|
+
import { TypegenManifestSchema, type TypegenManifest } from "../contracts/index.js";
|
|
8
|
+
|
|
9
|
+
export type CliTypegenMode = "semantic" | "minimal";
|
|
10
|
+
export type CliRuntimeMode = "centralized" | "standalone";
|
|
11
|
+
|
|
12
|
+
export type CliTypegenResult = {
|
|
13
|
+
written: boolean;
|
|
14
|
+
path: string;
|
|
15
|
+
serverPath: string;
|
|
16
|
+
publicPath: string;
|
|
17
|
+
manifestVersion: string;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
type TypegenIdentity = {
|
|
21
|
+
baseUrl: string | null;
|
|
22
|
+
orgSlug: string;
|
|
23
|
+
projectSlug: string;
|
|
24
|
+
stageSlug: string;
|
|
25
|
+
typegenMode: CliTypegenMode;
|
|
26
|
+
runtimeMode: CliRuntimeMode;
|
|
27
|
+
localEnvRoot: string | null;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const MANIFEST_VERSION_PATTERN = /\/\* barekey-manifest-version: ([^\n]+) \*\//;
|
|
31
|
+
|
|
32
|
+
const TypegenMetadataSchema = Schema.Struct({
|
|
33
|
+
version: Schema.Number,
|
|
34
|
+
last: Schema.String,
|
|
35
|
+
baseUrl: Schema.NullOr(Schema.String),
|
|
36
|
+
orgSlug: Schema.NullOr(Schema.String),
|
|
37
|
+
projectSlug: Schema.NullOr(Schema.String),
|
|
38
|
+
stageSlug: Schema.NullOr(Schema.String),
|
|
39
|
+
typegenMode: Schema.NullOr(Schema.Literal("semantic", "minimal")),
|
|
40
|
+
runtimeMode: Schema.NullOr(Schema.Literal("centralized", "standalone")),
|
|
41
|
+
localEnvRoot: Schema.NullOr(Schema.String),
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
type InstalledSdkTypegenTarget = {
|
|
45
|
+
packageRoot: string;
|
|
46
|
+
serverGeneratedTypesPath: string;
|
|
47
|
+
publicGeneratedTypesPath: string;
|
|
48
|
+
typegenMetadataPath: string;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
function renderLinearMilestones(
|
|
52
|
+
milestones: ReadonlyArray<{ at: string; percentage: number }>,
|
|
53
|
+
): string {
|
|
54
|
+
if (milestones.length === 0) {
|
|
55
|
+
return "readonly []";
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return `readonly [${milestones
|
|
59
|
+
.map(
|
|
60
|
+
(milestone) =>
|
|
61
|
+
`readonly [${JSON.stringify(milestone.at)}, ${String(milestone.percentage)}]`,
|
|
62
|
+
)
|
|
63
|
+
.join(", ")}]`;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function renderRolloutMetadataType(row: TypegenManifest["variables"][number]): string {
|
|
67
|
+
const renderedMilestones = renderLinearMilestones(row.rolloutMilestones ?? []);
|
|
68
|
+
if (row.rolloutFunction === "step") {
|
|
69
|
+
return `Step<${renderedMilestones}>`;
|
|
70
|
+
}
|
|
71
|
+
if (row.rolloutFunction === "ease_in_out") {
|
|
72
|
+
return `EaseInOut<${renderedMilestones}>`;
|
|
73
|
+
}
|
|
74
|
+
return `Linear<${renderedMilestones}>`;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function renderVariableType(
|
|
78
|
+
row: TypegenManifest["variables"][number],
|
|
79
|
+
mode: CliTypegenMode,
|
|
80
|
+
): string {
|
|
81
|
+
if (mode === "minimal") {
|
|
82
|
+
return row.typeScriptType;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const descriptor = `{ Type: ${row.typeScriptType}; Kind: ${JSON.stringify(row.kind)}; Visibility: ${JSON.stringify(row.visibility)}; Rollout: ${
|
|
86
|
+
row.kind === "rollout" ? renderRolloutMetadataType(row) : "never"
|
|
87
|
+
} }`;
|
|
88
|
+
|
|
89
|
+
return `Env<${descriptor}>`;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function buildGeneratedTypesContents(
|
|
93
|
+
manifest: TypegenManifest,
|
|
94
|
+
input: {
|
|
95
|
+
mode: CliTypegenMode;
|
|
96
|
+
typeModulePath: "./dist/types.js" | "./dist/public-types.js";
|
|
97
|
+
declaredModulePath: "./dist/types.js" | "./dist/public-types.js";
|
|
98
|
+
interfaceName: "BarekeyGeneratedTypeMap" | "BarekeyPublicGeneratedTypeMap";
|
|
99
|
+
include: (row: TypegenManifest["variables"][number]) => boolean;
|
|
100
|
+
},
|
|
101
|
+
): string {
|
|
102
|
+
const mapLines = manifest.variables
|
|
103
|
+
.slice()
|
|
104
|
+
.filter(input.include)
|
|
105
|
+
.sort((left, right) => left.name.localeCompare(right.name))
|
|
106
|
+
.map((row) => ` ${JSON.stringify(row.name)}: ${renderVariableType(row, input.mode)};`)
|
|
107
|
+
.join("\n");
|
|
108
|
+
|
|
109
|
+
const importLine =
|
|
110
|
+
input.mode === "semantic"
|
|
111
|
+
? `import type { EaseInOut, Env, Linear, Step } from "${input.typeModulePath}";\n\n`
|
|
112
|
+
: "";
|
|
113
|
+
|
|
114
|
+
return `/* eslint-disable */\n/* This file is generated by barekey typegen. */\n/* barekey-manifest-version: ${manifest.manifestVersion} */\n\n${importLine}declare module "${input.declaredModulePath}" {\n interface ${input.interfaceName} {\n${mapLines.length > 0 ? mapLines : ""}\n }\n}\n\nexport {};\n`;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function readManifestVersion(contents: string | null): string | null {
|
|
118
|
+
return contents?.match(MANIFEST_VERSION_PATTERN)?.[1]?.trim() ?? null;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
async function readTextFile(filePath: string): Promise<string | null> {
|
|
122
|
+
try {
|
|
123
|
+
return await readFile(filePath, "utf8");
|
|
124
|
+
} catch (error) {
|
|
125
|
+
const nodeError = error as NodeJS.ErrnoException | null;
|
|
126
|
+
if (nodeError?.code === "ENOENT") {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
throw error;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function writeTextFileAtomic(filePath: string, value: string): Promise<void> {
|
|
134
|
+
const temporaryPath = `${filePath}.${process.pid}.${Date.now()}.tmp`;
|
|
135
|
+
try {
|
|
136
|
+
await writeFile(temporaryPath, value, "utf8");
|
|
137
|
+
await rename(temporaryPath, filePath);
|
|
138
|
+
} catch (error) {
|
|
139
|
+
try {
|
|
140
|
+
await unlink(temporaryPath);
|
|
141
|
+
} catch {
|
|
142
|
+
// Best-effort cleanup only.
|
|
143
|
+
}
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function resolveInstalledSdkTypegenTarget(): Promise<InstalledSdkTypegenTarget | null> {
|
|
149
|
+
const require = createRequire(path.join(process.cwd(), "__barekey_typegen__.cjs"));
|
|
150
|
+
|
|
151
|
+
let resolvedEntrypoint: string;
|
|
152
|
+
try {
|
|
153
|
+
resolvedEntrypoint = require.resolve("@barekey/sdk/package.json");
|
|
154
|
+
} catch {
|
|
155
|
+
return null;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
let current = path.dirname(resolvedEntrypoint);
|
|
159
|
+
while (true) {
|
|
160
|
+
const candidatePackageJson = path.join(current, "package.json");
|
|
161
|
+
const rawPackageJson = await readTextFile(candidatePackageJson);
|
|
162
|
+
if (rawPackageJson !== null) {
|
|
163
|
+
try {
|
|
164
|
+
const parsed = JSON.parse(rawPackageJson) as { name?: unknown };
|
|
165
|
+
if (parsed.name === "@barekey/sdk") {
|
|
166
|
+
const serverGeneratedTypesPath = path.join(current, "generated.server.d.ts");
|
|
167
|
+
const publicGeneratedTypesPath = path.join(current, "generated.public.d.ts");
|
|
168
|
+
await access(serverGeneratedTypesPath);
|
|
169
|
+
await access(publicGeneratedTypesPath);
|
|
170
|
+
return {
|
|
171
|
+
packageRoot: current,
|
|
172
|
+
serverGeneratedTypesPath,
|
|
173
|
+
publicGeneratedTypesPath,
|
|
174
|
+
typegenMetadataPath: path.join(current, "typegen.json"),
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
} catch {
|
|
178
|
+
// Keep walking upward.
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const parent = path.dirname(current);
|
|
183
|
+
if (parent === current) {
|
|
184
|
+
return null;
|
|
185
|
+
}
|
|
186
|
+
current = parent;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function buildTypegenMetadataContents(lastGeneratedAt: Date, identity: TypegenIdentity): string {
|
|
191
|
+
return `${JSON.stringify(
|
|
192
|
+
{
|
|
193
|
+
version: 1,
|
|
194
|
+
last: lastGeneratedAt.toISOString(),
|
|
195
|
+
baseUrl: identity.baseUrl,
|
|
196
|
+
orgSlug: identity.orgSlug,
|
|
197
|
+
projectSlug: identity.projectSlug,
|
|
198
|
+
stageSlug: identity.stageSlug,
|
|
199
|
+
typegenMode: identity.typegenMode,
|
|
200
|
+
runtimeMode: identity.runtimeMode,
|
|
201
|
+
localEnvRoot: identity.localEnvRoot,
|
|
202
|
+
},
|
|
203
|
+
null,
|
|
204
|
+
2,
|
|
205
|
+
)}\n`;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Renders the generated SDK declaration files for one manifest.
|
|
210
|
+
*
|
|
211
|
+
* @param manifest The decoded typegen manifest.
|
|
212
|
+
* @param mode The requested typegen mode.
|
|
213
|
+
* @returns The rendered server and public declaration contents.
|
|
214
|
+
* @remarks The CLI owns this rendering so it no longer depends on SDK runtime helpers.
|
|
215
|
+
* @lastModified 2026-03-19
|
|
216
|
+
* @author GPT-5.4
|
|
217
|
+
*/
|
|
218
|
+
export function renderGeneratedTypesForManifest(
|
|
219
|
+
manifest: TypegenManifest,
|
|
220
|
+
mode: CliTypegenMode,
|
|
221
|
+
): {
|
|
222
|
+
serverContents: string;
|
|
223
|
+
publicContents: string;
|
|
224
|
+
} {
|
|
225
|
+
const decodedManifest = Schema.decodeUnknownSync(TypegenManifestSchema)(manifest);
|
|
226
|
+
|
|
227
|
+
return {
|
|
228
|
+
serverContents: buildGeneratedTypesContents(decodedManifest, {
|
|
229
|
+
mode,
|
|
230
|
+
typeModulePath: "./dist/types.js",
|
|
231
|
+
declaredModulePath: "./dist/types.js",
|
|
232
|
+
interfaceName: "BarekeyGeneratedTypeMap",
|
|
233
|
+
include: () => true,
|
|
234
|
+
}),
|
|
235
|
+
publicContents: buildGeneratedTypesContents(decodedManifest, {
|
|
236
|
+
mode,
|
|
237
|
+
typeModulePath: "./dist/public-types.js",
|
|
238
|
+
declaredModulePath: "./dist/public-types.js",
|
|
239
|
+
interfaceName: "BarekeyPublicGeneratedTypeMap",
|
|
240
|
+
include: (row) => row.visibility === "public",
|
|
241
|
+
}),
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Writes generated SDK declaration files into the installed `@barekey/sdk` package.
|
|
247
|
+
*
|
|
248
|
+
* @param manifest The decoded typegen manifest.
|
|
249
|
+
* @param identity The manifest identity used for metadata freshness.
|
|
250
|
+
* @returns The typegen write result.
|
|
251
|
+
* @remarks This preserves the existing generated file locations without importing the SDK package at runtime.
|
|
252
|
+
* @lastModified 2026-03-19
|
|
253
|
+
* @author GPT-5.4
|
|
254
|
+
*/
|
|
255
|
+
export async function writeInstalledSdkGeneratedTypes(
|
|
256
|
+
manifest: TypegenManifest,
|
|
257
|
+
identity: TypegenIdentity,
|
|
258
|
+
): Promise<CliTypegenResult> {
|
|
259
|
+
const target = await resolveInstalledSdkTypegenTarget();
|
|
260
|
+
if (target === null) {
|
|
261
|
+
return {
|
|
262
|
+
written: false,
|
|
263
|
+
path: "",
|
|
264
|
+
serverPath: "",
|
|
265
|
+
publicPath: "",
|
|
266
|
+
manifestVersion: manifest.manifestVersion,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const [existingServerContents, existingPublicContents] = await Promise.all([
|
|
271
|
+
readTextFile(target.serverGeneratedTypesPath),
|
|
272
|
+
readTextFile(target.publicGeneratedTypesPath),
|
|
273
|
+
]);
|
|
274
|
+
const rendered = renderGeneratedTypesForManifest(manifest, identity.typegenMode);
|
|
275
|
+
|
|
276
|
+
if (
|
|
277
|
+
existingServerContents === rendered.serverContents &&
|
|
278
|
+
existingPublicContents === rendered.publicContents &&
|
|
279
|
+
readManifestVersion(existingServerContents) === manifest.manifestVersion &&
|
|
280
|
+
readManifestVersion(existingPublicContents) === manifest.manifestVersion
|
|
281
|
+
) {
|
|
282
|
+
await writeTextFileAtomic(
|
|
283
|
+
target.typegenMetadataPath,
|
|
284
|
+
buildTypegenMetadataContents(new Date(), identity),
|
|
285
|
+
);
|
|
286
|
+
return {
|
|
287
|
+
written: false,
|
|
288
|
+
path: target.serverGeneratedTypesPath,
|
|
289
|
+
serverPath: target.serverGeneratedTypesPath,
|
|
290
|
+
publicPath: target.publicGeneratedTypesPath,
|
|
291
|
+
manifestVersion: manifest.manifestVersion,
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
await Promise.all([
|
|
296
|
+
writeTextFileAtomic(target.serverGeneratedTypesPath, rendered.serverContents),
|
|
297
|
+
writeTextFileAtomic(target.publicGeneratedTypesPath, rendered.publicContents),
|
|
298
|
+
]);
|
|
299
|
+
await writeTextFileAtomic(
|
|
300
|
+
target.typegenMetadataPath,
|
|
301
|
+
buildTypegenMetadataContents(new Date(), identity),
|
|
302
|
+
);
|
|
303
|
+
|
|
304
|
+
return {
|
|
305
|
+
written: true,
|
|
306
|
+
path: target.serverGeneratedTypesPath,
|
|
307
|
+
serverPath: target.serverGeneratedTypesPath,
|
|
308
|
+
publicPath: target.publicGeneratedTypesPath,
|
|
309
|
+
manifestVersion: manifest.manifestVersion,
|
|
310
|
+
};
|
|
311
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -4,11 +4,13 @@ export type CliCredentials = {
|
|
|
4
4
|
accessTokenExpiresAtMs: number;
|
|
5
5
|
refreshTokenExpiresAtMs: number;
|
|
6
6
|
clerkUserId: string;
|
|
7
|
-
orgId: string;
|
|
8
|
-
orgSlug: string;
|
|
7
|
+
orgId: string | null;
|
|
8
|
+
orgSlug: string | null;
|
|
9
|
+
lastOrgId?: string | null;
|
|
10
|
+
lastOrgSlug?: string | null;
|
|
9
11
|
};
|
|
10
12
|
|
|
11
13
|
export type CliConfig = {
|
|
12
14
|
baseUrl: string;
|
|
13
|
-
|
|
15
|
+
activeSessionId: string;
|
|
14
16
|
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { mkdtemp } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
|
|
7
|
+
import { resolveTarget } from "../src/command-utils";
|
|
8
|
+
|
|
9
|
+
async function withWorkingDirectory<TValue>(
|
|
10
|
+
nextCwd: string,
|
|
11
|
+
fn: () => Promise<TValue>,
|
|
12
|
+
): Promise<TValue> {
|
|
13
|
+
const previousCwd = process.cwd();
|
|
14
|
+
process.chdir(nextCwd);
|
|
15
|
+
try {
|
|
16
|
+
return await fn();
|
|
17
|
+
} finally {
|
|
18
|
+
process.chdir(previousCwd);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
describe("resolveTarget", () => {
|
|
23
|
+
test("does not fall back to stored org auth metadata when local repo context is missing", async () => {
|
|
24
|
+
const tempDir = await mkdtemp(path.join(tmpdir(), "barekey-cli-target-"));
|
|
25
|
+
|
|
26
|
+
await withWorkingDirectory(tempDir, async () => {
|
|
27
|
+
await expect(
|
|
28
|
+
resolveTarget(
|
|
29
|
+
{},
|
|
30
|
+
{
|
|
31
|
+
baseUrl: "https://api.barekey.dev",
|
|
32
|
+
accountId: "user_123",
|
|
33
|
+
credentials: {
|
|
34
|
+
accessToken: "token",
|
|
35
|
+
refreshToken: "refresh",
|
|
36
|
+
accessTokenExpiresAtMs: Date.now() + 60_000,
|
|
37
|
+
refreshTokenExpiresAtMs: Date.now() + 120_000,
|
|
38
|
+
clerkUserId: "user_123",
|
|
39
|
+
orgId: "org_123",
|
|
40
|
+
orgSlug: "should-not-be-used",
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
),
|
|
44
|
+
).rejects.toThrow("Run barekey init or pass --org/--project/--stage.");
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
});
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { mkdtemp, mkdir, writeFile } from "node:fs/promises";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
|
|
5
|
+
import { describe, expect, test } from "bun:test";
|
|
6
|
+
|
|
7
|
+
describe("loadConfig", () => {
|
|
8
|
+
test("migrates the legacy activeAccountId shape into activeSessionId", async () => {
|
|
9
|
+
const tempHome = await mkdtemp(path.join(tmpdir(), "barekey-cli-home-"));
|
|
10
|
+
const configDir = path.join(tempHome, ".config", "barekey");
|
|
11
|
+
await mkdir(configDir, {
|
|
12
|
+
recursive: true,
|
|
13
|
+
});
|
|
14
|
+
await writeFile(
|
|
15
|
+
path.join(configDir, "config.json"),
|
|
16
|
+
JSON.stringify({
|
|
17
|
+
baseUrl: "https://api.barekey.dev",
|
|
18
|
+
activeAccountId: "user_legacy",
|
|
19
|
+
}),
|
|
20
|
+
"utf8",
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const previousHome = process.env.HOME;
|
|
24
|
+
process.env.HOME = tempHome;
|
|
25
|
+
|
|
26
|
+
try {
|
|
27
|
+
const module = await import(`../src/credentials-store.ts?case=${Date.now()}`);
|
|
28
|
+
await expect(module.loadConfig()).resolves.toEqual({
|
|
29
|
+
baseUrl: "https://api.barekey.dev",
|
|
30
|
+
activeSessionId: "user_legacy",
|
|
31
|
+
});
|
|
32
|
+
} finally {
|
|
33
|
+
if (previousHome === undefined) {
|
|
34
|
+
delete process.env.HOME;
|
|
35
|
+
} else {
|
|
36
|
+
process.env.HOME = previousHome;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
});
|