@hyper.authio/cli 0.0.0-stage → 0.11.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.
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Why the CLI stopped. Each one is a sentence the person can act on; the code is stable for scripts and agents.
3
+ */
4
+ type AuthioCliErrorCode = "ticket_missing" | "ticket_invalid" | "verification_missing" | "verification_invalid" | "api_unreachable" | "api_error" | "rate_limited" | "plan_version_unsupported" | "plan_path_invalid" | "project_not_found" | "stack_not_detected" | "pages_router_unsupported" | "nextauth_v4_unsupported" | "confirmation_required" | "invalid_arguments";
5
+ /**
6
+ * An error the CLI raises on purpose: printed as its message, never as a stack, and mapped to exit code 1.
7
+ */
8
+ declare class AuthioCliError extends Error {
9
+ /** What went wrong, stable across versions. */
10
+ readonly code: AuthioCliErrorCode;
11
+ /** The process exit code the CLI ends with. */
12
+ readonly exitCode: number;
13
+ constructor(code: AuthioCliErrorCode, message: string, options?: {
14
+ cause?: unknown;
15
+ exitCode?: number;
16
+ });
17
+ }
18
+
19
+ /** The API address the CLI talks to when `--api` is not given: the shared Authio platform. */
20
+ declare const DEFAULT_API = "https://authio.api.hyperio.tech";
21
+ /** The command line, read. */
22
+ interface CliArguments {
23
+ /** `init`, `verify`, `help` or `version`. */
24
+ command: "init" | "verify" | "help" | "version";
25
+ ticket: string | undefined;
26
+ verification: string | undefined;
27
+ api: string | undefined;
28
+ cwd: string | undefined;
29
+ dryRun: boolean;
30
+ yes: boolean;
31
+ wait: boolean;
32
+ }
33
+ /**
34
+ * Reads `process.argv.slice(2)`. Accepts `--name value` and `--name=value`; refuses anything it does not know, so a
35
+ * typo never silently turns into the default.
36
+ */
37
+ declare function parseArguments(argv: readonly string[]): CliArguments;
38
+ /** The API address with no trailing slash, refusing anything that is not http(s). */
39
+ declare function apiAddress(api: string | undefined): string;
40
+
41
+ /**
42
+ * Everything the commands do outside their own logic: the network, the terminal and running a program. Injected, so
43
+ * every decision is testable without either.
44
+ */
45
+ interface CliIo {
46
+ /** The `fetch` to call Authio with. */
47
+ fetch: typeof fetch;
48
+ /** Prints one line. */
49
+ print(line: string): void;
50
+ /** Asks a yes/no question; `defaultYes` is the answer to an empty line. */
51
+ confirm(question: string, defaultYes: boolean): Promise<boolean>;
52
+ /** Asks for a value without echoing what is typed. */
53
+ promptSecret(question: string): Promise<string>;
54
+ /** Runs a program in `cwd`, inheriting the terminal; resolves with its exit code. */
55
+ run(command: string, args: readonly string[], cwd: string): Promise<number>;
56
+ /** Whether a person can answer questions (stdin and stdout are a terminal). */
57
+ isInteractive: boolean;
58
+ /** The clock, in milliseconds. */
59
+ now(): number;
60
+ /** Waits. */
61
+ sleep(milliseconds: number): Promise<void>;
62
+ }
63
+ /**
64
+ * The command line `cmd.exe` runs for a program and its arguments, each argument in double quotes. Windows needs a shell
65
+ * to start `npm.cmd` and friends, and handing `spawn` one string (rather than arguments with `shell: true`) is what
66
+ * Node accepts without DEP0190. Inside quotes `cmd.exe` keeps `^`, so `pkg@^6.0.2` keeps its range.
67
+ */
68
+ declare function windowsCommandLine(command: string, args: readonly string[]): string;
69
+ /** The real terminal, network and process. */
70
+ declare function createTerminalIo(): CliIo;
71
+
72
+ /** The package managers the CLI installs with, named after their lockfile. */
73
+ type PackageManager = "npm" | "pnpm" | "yarn" | "bun";
74
+ /** What the CLI read from the project, without the network. */
75
+ interface DetectedProject {
76
+ /** The project folder. */
77
+ root: string;
78
+ /** The major of the `next` dependency. */
79
+ nextMajor: number;
80
+ /** Whether the app lives under `src/` (`src/app`), where Next.js then expects `auth.ts` and `proxy.ts` too. */
81
+ srcDir: boolean;
82
+ /** The package manager, from the lockfile; `npm` when there is none. */
83
+ packageManager: PackageManager;
84
+ /** The port the `dev` script serves on: `-p N`/`--port N`, else 3000. */
85
+ devPort: number;
86
+ }
87
+ /**
88
+ * Reads the project the plan is applied to and stops, before any network call, when it is not one the plan fits: no
89
+ * `package.json`, no `next`, the pages router only, or NextAuth v4 already installed.
90
+ */
91
+ declare function detectProject(root: string): DetectedProject;
92
+
93
+ /** The plan versions this CLI knows how to apply. */
94
+ declare const SUPPORTED_PLAN_VERSIONS: readonly number[];
95
+ /** A package the plan installs. */
96
+ interface IntegrationPackage {
97
+ name: string;
98
+ version: string;
99
+ }
100
+ /** A file the plan writes; `source` files go under `src/` when the project has it. */
101
+ interface IntegrationFile {
102
+ path: string;
103
+ root: "source" | "project";
104
+ content: string;
105
+ }
106
+ /** An environment variable: its value comes in the plan, is typed by the person, or is generated on the machine. */
107
+ interface IntegrationEnvironmentVariable {
108
+ name: string;
109
+ value?: string | null;
110
+ source: "plan" | "secret" | "generate";
111
+ comment?: string | null;
112
+ }
113
+ /** The integration plan Authio hands out for one client and one stack. It never carries the client secret. */
114
+ interface IntegrationPlan {
115
+ stack: string;
116
+ planVersion: number;
117
+ issuer: string;
118
+ realm: string;
119
+ client: string;
120
+ clientActive: boolean;
121
+ packages: IntegrationPackage[];
122
+ files: IntegrationFile[];
123
+ environment: IntegrationEnvironmentVariable[];
124
+ redirectUris: string[];
125
+ callbackPath: string;
126
+ }
127
+ /** One file of the plan, placed in the project. */
128
+ interface PlannedFile {
129
+ /** The path relative to the project, with forward slashes. */
130
+ path: string;
131
+ /** The absolute path. */
132
+ target: string;
133
+ content: string;
134
+ /** `create` when the file does not exist; `merge` when it does and is left alone for the person to merge. */
135
+ action: "create" | "merge";
136
+ }
137
+ /** What applying the plan would do to this project. */
138
+ interface PlannedChanges {
139
+ files: PlannedFile[];
140
+ /** The variables `.env.local` does not have yet; the ones it has are kept. */
141
+ environment: IntegrationEnvironmentVariable[];
142
+ /** The variables `.env.local` already has, kept as they are. */
143
+ keptEnvironment: string[];
144
+ /** The redirect the app will use locally. */
145
+ localRedirect: string;
146
+ /** Whether that redirect is registered on the client. */
147
+ redirectRegistered: boolean;
148
+ }
149
+ /**
150
+ * Reads the plan as the API sends it. The API writes enum members by name (`Source`, `Plan`), the way every Authio
151
+ * response does, and leaves empty lists as `null`; the CLI compares in lower case and always has lists, so both
152
+ * spellings of a value apply the same.
153
+ */
154
+ declare function normalizePlan(plan: IntegrationPlan): IntegrationPlan;
155
+ /** Refuses a plan this CLI does not know how to apply, before anything is written. */
156
+ declare function assertPlanVersion(plan: IntegrationPlan): void;
157
+ /** The keys an env file already defines. */
158
+ declare function readEnvKeys(path: string): Set<string>;
159
+ /** The variable Auth.js builds its own addresses from: in `.env.local`, where the app runs on this machine. */
160
+ declare const LOCAL_ORIGIN_VARIABLE = "AUTH_URL";
161
+ /** The address the app is served from on this machine, from the `dev` script's port. */
162
+ declare function localOrigin(project: DetectedProject): string;
163
+ /**
164
+ * Works out, without writing, what applying the plan does to this project.
165
+ *
166
+ * `.env.local` is the development environment, so `AUTH_URL` there is the local address the `dev` script serves on,
167
+ * not the address the plan derives from the client's first redirect (often the production one): with that, Auth.js would
168
+ * send every sign-in on this machine back to the deployed app.
169
+ */
170
+ declare function planChanges(project: DetectedProject, plan: IntegrationPlan): PlannedChanges;
171
+
172
+ /** The paths the CLI writes that must never be committed. */
173
+ declare const IGNORED_PATHS: readonly [".env.local", ".authio/"];
174
+ /** The paths of {@link IGNORED_PATHS} the `.gitignore` at `path` does not cover yet. */
175
+ declare function uncoveredPaths(path: string): string[];
176
+
177
+ /** What redeeming a code answers: the plan, and the code that later confirms the first sign-in. */
178
+ interface IntegrationRedemption {
179
+ plan: IntegrationPlan;
180
+ verification: string;
181
+ verificationExpiresAt: string;
182
+ }
183
+ /** Whether the client has had a successful sign-in since the code was redeemed. */
184
+ interface IntegrationVerification {
185
+ signedIn: boolean;
186
+ firstSignInAt: string | null;
187
+ }
188
+ /** Exchanges the one-time code for the plan. The code is used up, whatever happens next. */
189
+ declare function redeemTicket(fetcher: typeof fetch, api: string, ticket: string, nextMajor: number): Promise<IntegrationRedemption>;
190
+ /** Asks whether the first sign-in has arrived. The verification code may be used until it expires. */
191
+ declare function checkVerification(fetcher: typeof fetch, api: string, verification: string): Promise<IntegrationVerification>;
192
+
193
+ /** The options of `authio init`. */
194
+ interface InitOptions {
195
+ /** The one-time code from the client's guide. */
196
+ ticket: string | undefined;
197
+ /** The API address, already normalised. */
198
+ api: string;
199
+ /** The project folder. */
200
+ cwd: string;
201
+ /** Show the plan without writing, installing or asking for secrets. */
202
+ dryRun: boolean;
203
+ /** Answer yes to every question. */
204
+ yes: boolean;
205
+ }
206
+ /** What `authio init` did. */
207
+ interface InitResult {
208
+ /** 0 when the plan was applied (or shown, with `dryRun`), 2 when the person declined. */
209
+ exitCode: number;
210
+ /** The files created, relative to the project. */
211
+ created: string[];
212
+ /** The files that already existed and are left for the person to merge. */
213
+ toMerge: string[];
214
+ /** The variables added to `.env.local`. */
215
+ addedEnvironment: string[];
216
+ /** The `secret` variables written empty, for the person to fill. */
217
+ emptySecrets: string[];
218
+ /** The plan, as redeemed. */
219
+ plan: IntegrationPlan | undefined;
220
+ }
221
+ /** The file `verify` reads the verification from. */
222
+ declare const VERIFICATION_FILE: string;
223
+ /** The command that installs `specs` with `manager`. */
224
+ declare function installCommand(manager: PackageManager, specs: readonly string[]): [string, string[]];
225
+ /**
226
+ * `authio init`: reads the project, exchanges the code for the plan and applies it, never overwriting a file, never
227
+ * committing, asking before writing and for each secret in a hidden prompt.
228
+ */
229
+ declare function runInit(options: InitOptions, io: CliIo): Promise<InitResult>;
230
+
231
+ /** The options of `authio verify`. */
232
+ interface VerifyOptions {
233
+ /** The verification code; when absent, the one `init` saved in `.authio/verification`. */
234
+ verification: string | undefined;
235
+ /** The API address given on the command line, already normalised; the saved one wins when it is absent. */
236
+ api: string | undefined;
237
+ /** The address used when neither is given. */
238
+ defaultApi: string;
239
+ /** The project folder. */
240
+ cwd: string;
241
+ /** Keep checking until the first sign-in arrives or the verification expires. */
242
+ wait: boolean;
243
+ }
244
+ /** What `authio verify` found. */
245
+ interface VerifyResult {
246
+ /** 0 when the first sign-in arrived, 2 when it has not yet. */
247
+ exitCode: number;
248
+ firstSignInAt: string | null;
249
+ }
250
+ /** How often `--wait` asks again. */
251
+ declare const VERIFY_INTERVAL_MS = 3000;
252
+ /**
253
+ * `authio verify`: asks Authio whether the client has had a successful sign-in since `init` redeemed the code. With
254
+ * `wait`, asks every three seconds until it has or the verification expires.
255
+ */
256
+ declare function runVerify(options: VerifyOptions, io: CliIo): Promise<VerifyResult>;
257
+
258
+ /**
259
+ * Runs the CLI with `argv` (without the node and script paths) and answers the exit code. Errors the CLI raises on
260
+ * purpose are printed as their message; anything else is printed with its stack, since it is a bug.
261
+ */
262
+ declare function main(argv: readonly string[], io: CliIo, environment: Record<string, string | undefined>, version: string): Promise<number>;
263
+
264
+ export { AuthioCliError, type AuthioCliErrorCode, type CliArguments, type CliIo, DEFAULT_API, type DetectedProject, IGNORED_PATHS, type InitOptions, type InitResult, type IntegrationEnvironmentVariable, type IntegrationFile, type IntegrationPackage, type IntegrationPlan, type IntegrationRedemption, type IntegrationVerification, LOCAL_ORIGIN_VARIABLE, type PackageManager, type PlannedChanges, type PlannedFile, SUPPORTED_PLAN_VERSIONS, VERIFICATION_FILE, VERIFY_INTERVAL_MS, type VerifyOptions, type VerifyResult, apiAddress, assertPlanVersion, checkVerification, createTerminalIo, detectProject, installCommand, localOrigin, main, normalizePlan, parseArguments, planChanges, readEnvKeys, redeemTicket, runInit, runVerify, uncoveredPaths, windowsCommandLine };