Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/identityManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,8 +192,8 @@ describe("identityManager", () => {
expect(readActiveIdentity(paths)).toBeUndefined();
});

it("throws InvalidIdentityNameError, not a raw ZodError, when the confirmed name fails IdentitySchema's own naming rule", async () => {
const prompts = scriptedIdentityPrompts(["create"]);
it("throws InvalidIdentityNameError before any prompt appears, not a raw ZodError after the user confirms, when the name fails IdentitySchema's own naming rule", async () => {
const prompts = scriptedIdentityPrompts([]);
await expect(runIdentityWizard(prompts, paths, "joseph.mearman@exadev.io")).rejects.toThrow(
InvalidIdentityNameError,
);
Expand Down
5 changes: 5 additions & 0 deletions src/identityManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,11 +82,16 @@ export function useIdentity(paths: LayoutPaths, name: string): void {
/**
* The interactive setup wizard for a new identity, offered by the `@<name>` shortcut and `identity use` when the identity doesn't exist yet and stdin is a real terminal.
*
* Validates `name` against `IdentitySchema`'s own naming rule before any prompt appears — offering "Create it now?" for a name that could never validate (an email address, say, whose `@` the shortcut passes through verbatim) just to fail on confirm is a broken interaction, so an invalid name throws `InvalidIdentityNameError` immediately instead.
*
* Confirms the user wants to create the identity, then optionally creates a default configuration profile (reusing `runProfileWizard`), links them, and sets the identity as active. A cancel at any step writes nothing beyond what was already committed — the identity is only created after the first confirm, and the profile wizard's own cancel handling means a profile-only cancellation still leaves the identity usable. Returns `true` when the identity was created and set active; `false` when the user declined at the initial confirm.
*
* Driven entirely by the injected `PromptsPort` so the whole flow is unit-testable with a scripted sequence of answers.
*/
export async function runIdentityWizard(prompts: PromptsPort, paths: LayoutPaths, name: string): Promise<boolean> {
if (!IdentitySchema.safeParse({ name, allowAmbientCredential: false }).success) {
throw new InvalidIdentityNameError(name);
}
const choice = await prompts.select({
message: `No identity named "${name}" exists yet. Create it now?`,
options: [
Expand Down
Loading