@fleetless/contracts 5.2.0 → 6.0.0-next.1
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/CHANGELOG.md +99 -0
- package/artifacts/openapi.json +1975 -415
- package/artifacts/routes.json +1697 -251
- package/artifacts/schema/accept-team-invite-request.schema.json +13 -7
- package/artifacts/schema/app-auth-config.schema.json +108 -4
- package/artifacts/schema/app-hosted-pages.schema.json +33 -0
- package/artifacts/schema/app-invitation.schema.json +2 -2
- package/artifacts/schema/app-mail-template-list-response.schema.json +4 -3
- package/artifacts/schema/app-mail-template.schema.json +3 -2
- package/artifacts/schema/app-sign-in-methods.schema.json +19 -0
- package/artifacts/schema/app-user-list-response.schema.json +36 -0
- package/artifacts/schema/app-user.schema.json +36 -0
- package/artifacts/schema/auth-me-response.schema.json +27 -0
- package/artifacts/schema/auth-ok.schema.json +13 -1
- package/artifacts/schema/client-accept-invitation-request.schema.json +3 -4
- package/artifacts/schema/client-identity.schema.json +13 -1
- package/artifacts/schema/client-login-code-request.schema.json +24 -0
- package/artifacts/schema/client-login-code-verify-request.schema.json +30 -0
- package/artifacts/schema/client-provider-list-response.schema.json +22 -2
- package/artifacts/schema/client-register-request.schema.json +3 -4
- package/artifacts/schema/client-sign-in-result.schema.json +55 -0
- package/artifacts/schema/client-two-factor-disable-request.schema.json +15 -0
- package/artifacts/schema/client-two-factor-setup-confirm-request.schema.json +20 -0
- package/artifacts/schema/client-two-factor-setup-confirm-response.schema.json +49 -0
- package/artifacts/schema/client-two-factor-setup-request.schema.json +12 -0
- package/artifacts/schema/client-two-factor-verify-request.schema.json +25 -0
- package/artifacts/schema/create-app-invitation-request.schema.json +1 -1
- package/artifacts/schema/create-passkey-request.schema.json +25 -0
- package/artifacts/schema/create-passkey-response.schema.json +84 -0
- package/artifacts/schema/developer-passkey.schema.json +56 -0
- package/artifacts/schema/developer-two-factor.schema.json +105 -0
- package/artifacts/schema/fleetless-user-list-response.schema.json +22 -0
- package/artifacts/schema/fleetless-user.schema.json +22 -0
- package/artifacts/schema/invalid-code-details.schema.json +16 -0
- package/artifacts/schema/org.schema.json +5 -0
- package/artifacts/schema/patch-org-request.schema.json +5 -3
- package/artifacts/schema/patch-org-response.schema.json +5 -0
- package/artifacts/schema/put-app-auth-look-request.schema.json +22 -0
- package/artifacts/schema/put-app-auth-mcp-request.schema.json +1 -14
- package/artifacts/schema/put-app-auth-sign-in-request.schema.json +39 -0
- package/artifacts/schema/put-app-auth-urls-request.schema.json +31 -4
- package/artifacts/schema/recovery-codes-response.schema.json +20 -0
- package/artifacts/schema/rename-passkey-request.schema.json +16 -0
- package/artifacts/schema/totp-confirm-request.schema.json +15 -0
- package/artifacts/schema/totp-confirm-response.schema.json +27 -0
- package/artifacts/schema/two-factor-challenge.schema.json +24 -0
- package/artifacts/schema/two-factor-setup-response.schema.json +21 -0
- package/artifacts/schema/webauthn-options-response.schema.json +18 -0
- package/dist/app-users.d.ts +154 -34
- package/dist/app-users.js +177 -45
- package/dist/client-auth.d.ts +133 -24
- package/dist/client-auth.js +139 -28
- package/dist/config.d.ts +2 -2
- package/dist/errors.d.ts +10 -1
- package/dist/errors.js +34 -5
- package/dist/identity.d.ts +172 -123
- package/dist/identity.js +189 -101
- package/dist/index.d.ts +9 -9
- package/dist/index.js +11 -7
- package/dist/protocol.d.ts +1 -1
- package/dist/realtime.d.ts +1 -0
- package/dist/rest.d.ts +2 -2
- package/dist/rest.js +17 -28
- package/dist/routes.d.ts +19 -0
- package/dist/routes.js +497 -183
- package/package.json +1 -1
- package/artifacts/schema/developer-login-request.schema.json +0 -19
- package/artifacts/schema/password-reset-confirm.schema.json +0 -19
- package/artifacts/schema/password-reset-request.schema.json +0 -14
- package/artifacts/schema/sign-up-request.schema.json +0 -26
- package/artifacts/schema/sign-up-response.schema.json +0 -127
package/dist/client-auth.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
1
|
// SPDX-License-Identifier: Apache-2.0
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import { appIdentifier } from './apps.js';
|
|
4
|
-
import { APP_USER_DISPLAY_NAME_MAX, providerSlug } from './app-users.js';
|
|
5
|
-
import { password } from './identity.js';
|
|
4
|
+
import { APP_USER_DISPLAY_NAME_MAX, appSignInMethods, providerSlug } from './app-users.js';
|
|
5
|
+
import { loginCode, password, recoveryCode, recoveryCodesList, sessionTokens, totpCode } from './identity.js';
|
|
6
6
|
/**
|
|
7
7
|
* **The client auth API: the whole of what an app user's browser talks to.**
|
|
8
8
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
9
|
+
* The developer's own UI owns every screen it wants to — login, emailed
|
|
10
|
+
* code, second factor, registration, verification, invitation acceptance,
|
|
11
11
|
* password reset, the provider buttons, the MCP consent — and calls these
|
|
12
|
-
* routes as JSON.
|
|
13
|
-
*
|
|
12
|
+
* routes as JSON. **Where the app sets no URL, a Fleetless-hosted page on the
|
|
13
|
+
* auth portal stands in** (`appAuthConfig.hosted_pages`) and calls the same
|
|
14
|
+
* policy: one set of rules, whichever page drives it.
|
|
14
15
|
*
|
|
15
16
|
* Everything here is public: `app_identifier` travels in the body (in the
|
|
16
17
|
* query for a GET), CORS is answered only for the app's `allowed_origins`, and
|
|
@@ -27,12 +28,19 @@ import { password } from './identity.js';
|
|
|
27
28
|
* the SDK and the cloud cannot drift into disagreeing about it.
|
|
28
29
|
*
|
|
29
30
|
* **The enumeration discipline is deliberate, not a preference:**
|
|
30
|
-
* `register`, `resend-verification
|
|
31
|
-
* policy-allowed request whether or not the address exists,
|
|
32
|
-
* the identical `invalid_credentials` for a wrong
|
|
33
|
-
* and a `pending_verification` one. Policy
|
|
34
|
-
* `registration_closed
|
|
35
|
-
*
|
|
31
|
+
* `register`, `resend-verification`, `password/reset` and `login/code` answer
|
|
32
|
+
* `202` for every policy-allowed request whether or not the address exists,
|
|
33
|
+
* and `login` answers the identical `invalid_credentials` for a wrong
|
|
34
|
+
* password, a `blocked` account and a `pending_verification` one. Policy
|
|
35
|
+
* refusals are honest — `registration_closed`, `domain_not_allowed` and
|
|
36
|
+
* `method_not_allowed` say what they are, because none reveals whether a
|
|
37
|
+
* *person* exists.
|
|
38
|
+
*
|
|
39
|
+
* **Every step that signs somebody in answers a `clientSignInResult`**:
|
|
40
|
+
* session tokens, or a two-factor challenge when the person has a confirmed
|
|
41
|
+
* authenticator or the app requires one. No path yields a session without the
|
|
42
|
+
* second factor then — except a sign-in through an identity provider, which
|
|
43
|
+
* owns that sign-in and answers tokens as before.
|
|
36
44
|
*/
|
|
37
45
|
/* ------------------------------------------------------ password login -- */
|
|
38
46
|
export const clientLoginRequest = z.object({
|
|
@@ -70,6 +78,102 @@ export const clientLogoutRequest = z.object({
|
|
|
70
78
|
description: 'Any refresh token of the session to end. The whole token family is revoked server-side, so a token stolen before this call stops working too — clearing a client-side store is a gesture, not a revocation. The answer is `204`: a token the server does not recognise gets it too, since that is the end state being asked for.',
|
|
71
79
|
}),
|
|
72
80
|
});
|
|
81
|
+
/* -------------------------------------------------- emailed sign-in code -- */
|
|
82
|
+
/**
|
|
83
|
+
* **Asking for a sign-in code.** A six-digit code, mailed, valid ten minutes;
|
|
84
|
+
* a new request expires the previous code for the same address. The answer is
|
|
85
|
+
* `202` whether or not the address names an account — a decoy, like
|
|
86
|
+
* `resend-verification` — so this is no enumeration oracle.
|
|
87
|
+
*/
|
|
88
|
+
export const clientLoginCodeRequest = z
|
|
89
|
+
.object({
|
|
90
|
+
app_identifier: appIdentifier.meta({ description: 'The app to sign in to. An identifier no app carries is `404 not_found`; the address is never the subject of a refusal.' }),
|
|
91
|
+
email: z.email().meta({
|
|
92
|
+
description: 'The address to mail the code to, trimmed and compared case-insensitively. `202` whether or not it names an account of this app.',
|
|
93
|
+
}),
|
|
94
|
+
})
|
|
95
|
+
.strict();
|
|
96
|
+
/** **Spending the code.** Five wrong attempts spend it; so does its tenth minute. */
|
|
97
|
+
export const clientLoginCodeVerifyRequest = z
|
|
98
|
+
.object({
|
|
99
|
+
app_identifier: appIdentifier.meta({ description: 'The app the code was requested for.' }),
|
|
100
|
+
email: z.email().meta({ description: 'The address the code was mailed to, as typed when it was requested; trimmed and compared case-insensitively.' }),
|
|
101
|
+
code: loginCode.meta({ description: 'The six digits from the mail, exactly — leading zeros included, no spaces.' }),
|
|
102
|
+
})
|
|
103
|
+
.strict();
|
|
104
|
+
/* ---------------------------------------------------------- two-factor -- */
|
|
105
|
+
/**
|
|
106
|
+
* **A sign-in that needs its second factor first.** No session exists yet:
|
|
107
|
+
* the `challenge` is a short-lived handle, five minutes, that the next call
|
|
108
|
+
* spends.
|
|
109
|
+
*
|
|
110
|
+
* - `two_factor_required` — the person has an authenticator; send a code or
|
|
111
|
+
* a recovery code to `POST /api/client/two-factor/verify`.
|
|
112
|
+
* - `two_factor_setup_required` — the app requires two-factor and the person
|
|
113
|
+
* has none; set one up with `POST /api/client/two-factor/setup` and
|
|
114
|
+
* `…/setup/confirm`, which answers the session.
|
|
115
|
+
*/
|
|
116
|
+
export const twoFactorChallenge = z.object({
|
|
117
|
+
status: z.enum(['two_factor_required', 'two_factor_setup_required']).meta({
|
|
118
|
+
description: '`two_factor_required`: ask for the authenticator code. `two_factor_setup_required`: the app requires two-factor and the person has none yet, so set one up before any session exists.',
|
|
119
|
+
}),
|
|
120
|
+
challenge: z.string().min(1).meta({
|
|
121
|
+
description: 'The handle the next step spends. Valid five minutes; afterwards it answers `410 token_spent` and the sign-in starts over.',
|
|
122
|
+
}),
|
|
123
|
+
});
|
|
124
|
+
/**
|
|
125
|
+
* **What every session-minting sign-in step answers**: password login, code
|
|
126
|
+
* verify, and spending an invitation, verification or reset token. Session
|
|
127
|
+
* tokens when the sign-in is complete; a `twoFactorChallenge` when a second
|
|
128
|
+
* factor comes first. Tell them apart by `status`, which only the challenge
|
|
129
|
+
* carries.
|
|
130
|
+
*/
|
|
131
|
+
export const clientSignInResult = z.union([sessionTokens, twoFactorChallenge]);
|
|
132
|
+
/** **Answering a `two_factor_required` challenge**, with the authenticator's code or one recovery code — exactly one of the two. */
|
|
133
|
+
export const clientTwoFactorVerifyRequest = z
|
|
134
|
+
.object({
|
|
135
|
+
challenge: z.string().min(1).meta({ description: 'The challenge the sign-in step answered.' }),
|
|
136
|
+
code: totpCode.optional().meta({ description: 'The six-digit code the authenticator shows now. A code already accepted once is refused, so a replay of a seen code does not sign anybody in.' }),
|
|
137
|
+
recovery_code: recoveryCode.optional().meta({ description: 'One of the ten recovery codes, `xxxxx-xxxxx`, in either case. Spent by its use.' }),
|
|
138
|
+
})
|
|
139
|
+
.strict()
|
|
140
|
+
.refine((b) => (b.code === undefined) !== (b.recovery_code === undefined), { message: 'Send exactly one of code and recovery_code.' });
|
|
141
|
+
/**
|
|
142
|
+
* **Starting an authenticator setup.** Either during sign-in, with the
|
|
143
|
+
* `two_factor_setup_required` challenge in the body, or from the app's own
|
|
144
|
+
* account settings, with the app user's bearer and no challenge.
|
|
145
|
+
*/
|
|
146
|
+
export const clientTwoFactorSetupRequest = z
|
|
147
|
+
.object({
|
|
148
|
+
challenge: z.string().min(1).optional().meta({
|
|
149
|
+
description: 'The `two_factor_setup_required` challenge, during sign-in. Absent when the call carries the app user\'s bearer instead.',
|
|
150
|
+
}),
|
|
151
|
+
})
|
|
152
|
+
.strict();
|
|
153
|
+
/** **Confirming the setup** with a code from the new authenticator. Only then is it on. */
|
|
154
|
+
export const clientTwoFactorSetupConfirmRequest = z
|
|
155
|
+
.object({
|
|
156
|
+
challenge: z.string().min(1).optional().meta({ description: 'The same challenge as at `setup`, during sign-in; absent with a bearer.' }),
|
|
157
|
+
code: totpCode.meta({ description: 'A code the new authenticator shows now. It proves the secret was copied correctly before anything depends on it.' }),
|
|
158
|
+
})
|
|
159
|
+
.strict();
|
|
160
|
+
/**
|
|
161
|
+
* **The confirmed setup**: the ten recovery codes, shown this once, and a
|
|
162
|
+
* session — during sign-in the first one, from account settings a fresh one,
|
|
163
|
+
* since every other session of the account ends.
|
|
164
|
+
*/
|
|
165
|
+
export const clientTwoFactorSetupConfirmResponse = z.object({
|
|
166
|
+
recovery_codes: recoveryCodesList.meta({
|
|
167
|
+
description: 'The ten single-use recovery codes, lowercase, shown once. Any earlier set is void.',
|
|
168
|
+
}),
|
|
169
|
+
session: sessionTokens.meta({ description: 'The session the sign-in was waiting for, or a fresh one for the account settings.' }),
|
|
170
|
+
});
|
|
171
|
+
/** **Turning the authenticator off**, from the app's own account settings. A current code proves the person still holds it. */
|
|
172
|
+
export const clientTwoFactorDisableRequest = z
|
|
173
|
+
.object({
|
|
174
|
+
code: totpCode.meta({ description: 'A code the authenticator shows now.' }),
|
|
175
|
+
})
|
|
176
|
+
.strict();
|
|
73
177
|
/* ---------------------------------------------- registration and mails -- */
|
|
74
178
|
/**
|
|
75
179
|
* **Self-registration** — and the account it creates cannot log in yet.
|
|
@@ -98,8 +202,8 @@ export const clientRegisterRequest = z
|
|
|
98
202
|
email: z.email().meta({
|
|
99
203
|
description: 'The address to register. Unique per app, case-insensitively. An address this app already knows still answers `202`, without a mail — the answer may not say whether an account exists.',
|
|
100
204
|
}),
|
|
101
|
-
password: password.meta({
|
|
102
|
-
description: 'The password for the new account
|
|
205
|
+
password: password.optional().meta({
|
|
206
|
+
description: 'The password for the new account, at least 12 characters. **Required while the app\'s password method is on, refused while it is off** — both as `400 validation_error` naming `password`. An email-code-only app registers people without one.',
|
|
103
207
|
}),
|
|
104
208
|
display_name: z.string().min(1).max(APP_USER_DISPLAY_NAME_MAX).nullable().optional().meta({
|
|
105
209
|
description: 'An optional human name for the account. The developer\'s own UI decides whether to ask for it.',
|
|
@@ -124,12 +228,10 @@ export const clientResendVerificationRequest = z
|
|
|
124
228
|
})
|
|
125
229
|
.strict();
|
|
126
230
|
/**
|
|
127
|
-
* Asking for a reset link **as an app user
|
|
128
|
-
*
|
|
129
|
-
*
|
|
130
|
-
*
|
|
131
|
-
* and resolves alone; an app user's is unique only within their app, so the
|
|
132
|
-
* pair is what names them.
|
|
231
|
+
* Asking for a reset link **as an app user** — the only password reset
|
|
232
|
+
* there is: Fleetless users hold no password. An app user's address is
|
|
233
|
+
* unique only within their app, so the pair of app and address is what names
|
|
234
|
+
* them.
|
|
133
235
|
*
|
|
134
236
|
* The response is identical for a known and an unknown pair — otherwise this
|
|
135
237
|
* becomes the enumeration oracle the rest of the family is carefully built not
|
|
@@ -170,7 +272,9 @@ export const clientAcceptInvitationRequest = z
|
|
|
170
272
|
token: z.string().min(1).meta({
|
|
171
273
|
description: 'The opaque token from the invitation link, valid seven days. Unknown, expired, revoked and already-accepted all answer `410 token_spent`.',
|
|
172
274
|
}),
|
|
173
|
-
password: password.meta({
|
|
275
|
+
password: password.optional().meta({
|
|
276
|
+
description: 'The password the new account will use, at least 12 characters. **Required while the app\'s password method is on, refused while it is off** — both as `400 validation_error` naming `password`. An email-code-only app accepts invitations without one.',
|
|
277
|
+
}),
|
|
174
278
|
display_name: z.string().min(1).max(APP_USER_DISPLAY_NAME_MAX).nullable().optional().meta({
|
|
175
279
|
description: 'An optional name, overriding whatever the invitation pre-filled.',
|
|
176
280
|
}),
|
|
@@ -210,8 +314,8 @@ export const clientProviderListQuery = z
|
|
|
210
314
|
})
|
|
211
315
|
.meta({ description: 'The one parameter of the public provider listing.' });
|
|
212
316
|
/**
|
|
213
|
-
* What the developer's login page needs to draw its provider buttons
|
|
214
|
-
* **nothing more**. This route is public and unauthenticated: the issuer, the
|
|
317
|
+
* What the developer's login page needs to draw its provider buttons and its
|
|
318
|
+
* password or code fields, and **nothing more**. This route is public and unauthenticated: the issuer, the
|
|
215
319
|
* client id, the scopes and the linking policy are all management-side facts
|
|
216
320
|
* that would tell a stranger how the app's federation is configured.
|
|
217
321
|
*
|
|
@@ -225,7 +329,10 @@ export const clientProviderListResponse = z.object({
|
|
|
225
329
|
name: z.string().meta({ description: 'What to write on the button, as the developer configured it.' }),
|
|
226
330
|
}))
|
|
227
331
|
.meta({
|
|
228
|
-
description: 'The app\'s **enabled** providers, slug and display name only. An app with none answers an empty array, which is the state of an app that offers password
|
|
332
|
+
description: 'The app\'s **enabled** providers, slug and display name only. An app with none answers an empty array, which is the state of an app that offers password or code sign-in alone.',
|
|
333
|
+
}),
|
|
334
|
+
sign_in_methods: appSignInMethods.meta({
|
|
335
|
+
description: 'Which of password and emailed code the app accepts, so its sign-in page draws the right fields without guessing. The same value the developer set; public, like the provider buttons.',
|
|
229
336
|
}),
|
|
230
337
|
});
|
|
231
338
|
/**
|
|
@@ -353,10 +460,11 @@ export const clientOidcErrorCode = z.enum([
|
|
|
353
460
|
]);
|
|
354
461
|
/* ------------------------------------------------ MCP, delegated login -- */
|
|
355
462
|
/**
|
|
356
|
-
* **A pending MCP authorization, as the app's own consent screen reads it
|
|
357
|
-
*
|
|
358
|
-
* app
|
|
359
|
-
*
|
|
463
|
+
* **A pending MCP authorization, as the app's own consent screen reads it.**
|
|
464
|
+
* `authorize` redirects to the app's `mcp_login_url` with an interaction id,
|
|
465
|
+
* the app authenticates the user with its normal UI, shows this, and approves
|
|
466
|
+
* or denies through the API. An app with no `mcp_login_url` gets the hosted
|
|
467
|
+
* MCP sign-in instead, which reads and decides the same interaction.
|
|
360
468
|
*
|
|
361
469
|
* `client_name_verified` is `z.literal(false)`, and that is the whole point of
|
|
362
470
|
* the field. The name comes from an **unauthenticated** dynamic registration —
|
|
@@ -482,4 +590,7 @@ export const clientIdentity = z.object({
|
|
|
482
590
|
email: z.email().nullable().meta({
|
|
483
591
|
description: 'The address of the Fleetless user or app user behind this session, and `null` for a server key, which is not a person.',
|
|
484
592
|
}),
|
|
593
|
+
two_factor_enabled: z.boolean().nullable().meta({
|
|
594
|
+
description: 'Whether the app user has a confirmed authenticator, so the app\'s account settings can offer to turn it on or off. `null` unless `kind` is `app_user`.',
|
|
595
|
+
}),
|
|
485
596
|
});
|
package/dist/config.d.ts
CHANGED
|
@@ -648,9 +648,9 @@ export declare const LOW_BANDWIDTH_DEFAULTS: {
|
|
|
648
648
|
*/
|
|
649
649
|
export declare const lowBandwidthSection: z.ZodObject<{
|
|
650
650
|
mode: z.ZodOptional<z.ZodEnum<{
|
|
651
|
+
off: "off";
|
|
651
652
|
auto: "auto";
|
|
652
653
|
on: "on";
|
|
653
|
-
off: "off";
|
|
654
654
|
}>>;
|
|
655
655
|
enter_lag_ms: z.ZodOptional<z.ZodNumber>;
|
|
656
656
|
enter_after_s: z.ZodOptional<z.ZodNumber>;
|
|
@@ -858,9 +858,9 @@ export declare const robotConfigDoc: z.ZodObject<{
|
|
|
858
858
|
}, z.core.$strict>>>;
|
|
859
859
|
low_bandwidth: z.ZodOptional<z.ZodObject<{
|
|
860
860
|
mode: z.ZodOptional<z.ZodEnum<{
|
|
861
|
+
off: "off";
|
|
861
862
|
auto: "auto";
|
|
862
863
|
on: "on";
|
|
863
|
-
off: "off";
|
|
864
864
|
}>>;
|
|
865
865
|
enter_lag_ms: z.ZodOptional<z.ZodNumber>;
|
|
866
866
|
enter_after_s: z.ZodOptional<z.ZodNumber>;
|
package/dist/errors.d.ts
CHANGED
|
@@ -63,10 +63,19 @@ export declare const cancelRejectedDetails: z.ZodObject<{
|
|
|
63
63
|
}, z.core.$strip>>;
|
|
64
64
|
}, z.core.$strip>;
|
|
65
65
|
export type CancelRejectedDetails = z.infer<typeof cancelRejectedDetails>;
|
|
66
|
+
/**
|
|
67
|
+
* The `details` of an `invalid_code` refusal: how many wrong codes are left
|
|
68
|
+
* before the code is spent. Pinned for the reason `parameterInvalidDetails`
|
|
69
|
+
* is — a sign-in page parses it instead of reading the shape from prose.
|
|
70
|
+
*/
|
|
71
|
+
export declare const invalidCodeDetails: z.ZodObject<{
|
|
72
|
+
attempts_left: z.ZodNumber;
|
|
73
|
+
}, z.core.$strip>;
|
|
74
|
+
export type InvalidCodeDetails = z.infer<typeof invalidCodeDetails>;
|
|
66
75
|
/**
|
|
67
76
|
* The codes in use today. The wire deliberately allows any string — this
|
|
68
77
|
* list is the shared vocabulary, not a closed set, so a new refusal never
|
|
69
78
|
* needs a contracts release before it can be reported honestly.
|
|
70
79
|
*/
|
|
71
|
-
export declare const ERROR_CODES: readonly ["not_found", "validation_error", "bad_request", "unknown_datapoint", "invalid_token", "protocol_mismatch", "bridge_too_old", "invalid_frame", "duplicate_slug", "reserved_slug", "unknown_slug", "unknown_field_path", "unknown_type", "unknown_topic", "invalid_rate", "invalid_range", "config_conflict", "no_data", "robot_offline", "bridge_timeout", "unauthorized", "forbidden", "invalid_credentials", "token_expired", "token_revoked", "email_taken", "identifier_taken", "weak_password", "account_blocked", "busy", "parameter_invalid", "cancel_rejected", "job_lost", "job_unknown_to_bridge", "action_server_lost", "action_failed", "goal_rejected", "goal_send_failed", "result_failed", "goal_uncontrollable", "bridge_disconnected", "config_changed", "publisher_busy", "unknown_command", "not_subscribable", "camera_offline", "no_snapshot_yet", "live_unavailable", "wrong_kind", "not_recorded", "not_aggregatable", "quota_exceeded", "credential_in_use", "goal_timeout", "robot_in_use", "robot_deletion_partial", "job_queue_full", "invalid_uuid", "rate_limited", "tier_required", "token_spent", "service_timeout", "asset_missing", "dynamic_registration_disabled", "client_limit_reached", "idp_unavailable", "mcp_disabled", "tool_not_available", "capability_required", "last_owner", "target_state_conflict", "signup_closed", "draft_not_a_document", "internal_error", "not_cancellable", "unsupported_media_type", "wrong_browser", "invalid_yaml", "unstorable_yaml", "registration_closed", "domain_not_allowed", "email_unverified", "origin_not_allowed", "template_invalid", "provider_disabled", "provider_misconfigured", "invalid_redirect_uri", "interaction_expired"];
|
|
80
|
+
export declare const ERROR_CODES: readonly ["not_found", "validation_error", "bad_request", "unknown_datapoint", "invalid_token", "protocol_mismatch", "bridge_too_old", "invalid_frame", "duplicate_slug", "reserved_slug", "unknown_slug", "unknown_field_path", "unknown_type", "unknown_topic", "invalid_rate", "invalid_range", "config_conflict", "no_data", "robot_offline", "bridge_timeout", "unauthorized", "forbidden", "invalid_credentials", "token_expired", "token_revoked", "email_taken", "identifier_taken", "weak_password", "account_blocked", "busy", "parameter_invalid", "cancel_rejected", "job_lost", "job_unknown_to_bridge", "action_server_lost", "action_failed", "goal_rejected", "goal_send_failed", "result_failed", "goal_uncontrollable", "bridge_disconnected", "config_changed", "publisher_busy", "unknown_command", "not_subscribable", "camera_offline", "no_snapshot_yet", "live_unavailable", "wrong_kind", "not_recorded", "not_aggregatable", "quota_exceeded", "credential_in_use", "goal_timeout", "robot_in_use", "robot_deletion_partial", "job_queue_full", "invalid_uuid", "rate_limited", "tier_required", "token_spent", "service_timeout", "asset_missing", "dynamic_registration_disabled", "client_limit_reached", "idp_unavailable", "mcp_disabled", "tool_not_available", "capability_required", "last_owner", "target_state_conflict", "signup_closed", "draft_not_a_document", "internal_error", "not_cancellable", "unsupported_media_type", "wrong_browser", "invalid_yaml", "unstorable_yaml", "registration_closed", "domain_not_allowed", "email_unverified", "origin_not_allowed", "template_invalid", "provider_disabled", "provider_misconfigured", "invalid_redirect_uri", "interaction_expired", "invalid_code", "method_not_allowed"];
|
|
72
81
|
export type ErrorCode = (typeof ERROR_CODES)[number];
|
package/dist/errors.js
CHANGED
|
@@ -53,6 +53,16 @@ export const parameterInvalidDetails = z.object({
|
|
|
53
53
|
export const cancelRejectedDetails = z.object({
|
|
54
54
|
goals: z.array(bridgeCancelResultEntry).min(1),
|
|
55
55
|
});
|
|
56
|
+
/**
|
|
57
|
+
* The `details` of an `invalid_code` refusal: how many wrong codes are left
|
|
58
|
+
* before the code is spent. Pinned for the reason `parameterInvalidDetails`
|
|
59
|
+
* is — a sign-in page parses it instead of reading the shape from prose.
|
|
60
|
+
*/
|
|
61
|
+
export const invalidCodeDetails = z.object({
|
|
62
|
+
attempts_left: z.number().int().min(0).meta({
|
|
63
|
+
description: 'How many more wrong codes this code or challenge takes before it is spent. `0` means the next attempt answers `410 token_spent`.',
|
|
64
|
+
}),
|
|
65
|
+
});
|
|
56
66
|
/**
|
|
57
67
|
* The codes in use today. The wire deliberately allows any string — this
|
|
58
68
|
* list is the shared vocabulary, not a closed set, so a new refusal never
|
|
@@ -390,6 +400,10 @@ export const ERROR_CODES = [
|
|
|
390
400
|
* Deliberately one code for both: distinguishing them tells a stranger
|
|
391
401
|
* whether a token ever existed, and the recovery is identical either way —
|
|
392
402
|
* ask for a new link.
|
|
403
|
+
*
|
|
404
|
+
* The same code answers an emailed sign-in code that is spent, expired or
|
|
405
|
+
* out of attempts, and a two-factor challenge past its five minutes: the
|
|
406
|
+
* recovery is the same — ask for a new code, or start the sign-in again.
|
|
393
407
|
*/
|
|
394
408
|
'token_spent',
|
|
395
409
|
// The command path, continued.
|
|
@@ -597,11 +611,9 @@ export const ERROR_CODES = [
|
|
|
597
611
|
'target_state_conflict',
|
|
598
612
|
// 2026-09-04 — the public site (closed beta).
|
|
599
613
|
/**
|
|
600
|
-
* `403` from
|
|
601
|
-
* `
|
|
602
|
-
*
|
|
603
|
-
* waiting list. Produced by cloud `routes/auth.ts` and
|
|
604
|
-
* `routes/console-oauth.ts` in the same release.
|
|
614
|
+
* `403` from the portal's sign-up pages while `SIGNUP_MODE=closed`. Not
|
|
615
|
+
* `forbidden`: nothing about the caller is refused, the door is closed for
|
|
616
|
+
* everyone. The message names the waiting list.
|
|
605
617
|
*/
|
|
606
618
|
'signup_closed',
|
|
607
619
|
// Emitted by the cloud, catalogued late.
|
|
@@ -834,4 +846,21 @@ export const ERROR_CODES = [
|
|
|
834
846
|
* advice for the state a person is actually in.
|
|
835
847
|
*/
|
|
836
848
|
'interaction_expired',
|
|
849
|
+
// 2026-09-30 — email codes and two-factor.
|
|
850
|
+
/**
|
|
851
|
+
* `400`: a six-digit code, an authenticator code or a recovery code that
|
|
852
|
+
* does not match. `details` is `invalidCodeDetails`: how many attempts the
|
|
853
|
+
* emailed code has left, so the page can say so. At zero the code is spent
|
|
854
|
+
* and the next answer is `410 token_spent`. Distinct from
|
|
855
|
+
* `invalid_credentials`, which is about a password and says nothing about
|
|
856
|
+
* attempts.
|
|
857
|
+
*/
|
|
858
|
+
'invalid_code',
|
|
859
|
+
/**
|
|
860
|
+
* `403`: the app does not offer this sign-in method — a password login on a
|
|
861
|
+
* code-only app, a code request on a password-only one, or a password where
|
|
862
|
+
* the app takes none. It names the app's policy, never a person, so it is
|
|
863
|
+
* no enumeration oracle.
|
|
864
|
+
*/
|
|
865
|
+
'method_not_allowed',
|
|
837
866
|
];
|