@gopherium/react-auth 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.
@@ -5,6 +5,7 @@ declare const userSchema: z.ZodObject<{
5
5
  email: z.ZodString;
6
6
  name: z.ZodString;
7
7
  disabled: z.ZodBoolean;
8
+ rank: z.ZodDefault<z.ZodString>;
8
9
  created_at: z.ZodCoercedDate<unknown>;
9
10
  }, z.core.$strip>;
10
11
  export type User = z.infer<typeof userSchema>;
@@ -12,6 +13,7 @@ export interface NewUser {
12
13
  email: string;
13
14
  name: string;
14
15
  password: string;
16
+ rank?: string;
15
17
  }
16
18
  /**
17
19
  * EmailTakenError is thrown when a new user's email is already in use.
@@ -24,6 +26,22 @@ export declare class EmailTakenError extends Error {
24
26
  */
25
27
  export declare class ValidationError extends Error {
26
28
  }
29
+ /**
30
+ * RankRefusedError is thrown when the actor holds no rank the server admits.
31
+ */
32
+ export declare class RankRefusedError extends Error {
33
+ }
34
+ /**
35
+ * SelfRankError is thrown when an account changes its own rank.
36
+ */
37
+ export declare class SelfRankError extends Error {
38
+ }
39
+ /**
40
+ * LastPrivilegedError is thrown when a write would leave no enabled
41
+ * account holding a privileged rank.
42
+ */
43
+ export declare class LastPrivilegedError extends Error {
44
+ }
27
45
  /**
28
46
  * Returns every user account.
29
47
  * @param signal - Aborts the in-flight request.
@@ -42,4 +60,10 @@ export declare function createUser(input: NewUser): Promise<User>;
42
60
  * @param disabled - Whether the account should be disabled.
43
61
  */
44
62
  export declare function setUserDisabled(id: string, disabled: boolean): Promise<void>;
63
+ /**
64
+ * Sets the rank an account holds.
65
+ * @param id - The identifier of the account to update.
66
+ * @param rank - The rank the account is to hold.
67
+ */
68
+ export declare function setUserRank(id: string, rank: string): Promise<void>;
45
69
  export {};
@@ -10,6 +10,7 @@ const userSchema = z.object({
10
10
  email: z.string(),
11
11
  name: z.string(),
12
12
  disabled: z.boolean(),
13
+ rank: z.string().default(''),
13
14
  created_at: z.coerce.date(),
14
15
  });
15
16
  /**
@@ -23,7 +24,23 @@ export class EmailTakenError extends Error {
23
24
  */
24
25
  export class ValidationError extends Error {
25
26
  }
26
- const errorSchema = z.object({ error: z.string() });
27
+ /**
28
+ * RankRefusedError is thrown when the actor holds no rank the server admits.
29
+ */
30
+ export class RankRefusedError extends Error {
31
+ }
32
+ /**
33
+ * SelfRankError is thrown when an account changes its own rank.
34
+ */
35
+ export class SelfRankError extends Error {
36
+ }
37
+ /**
38
+ * LastPrivilegedError is thrown when a write would leave no enabled
39
+ * account holding a privileged rank.
40
+ */
41
+ export class LastPrivilegedError extends Error {
42
+ }
43
+ const errorSchema = z.object({ error: z.string(), code: z.string().optional() });
27
44
  /**
28
45
  * Extracts the backend's explanation from a failed response body.
29
46
  * @param response - The failed HTTP response.
@@ -96,6 +113,49 @@ async function restSetUserDisabled(id, disabled) {
96
113
  throw new Error(`updating user failed with status ${response.status}`);
97
114
  }
98
115
  }
116
+ /**
117
+ * Writes the rank an account holds over REST.
118
+ * @param id - The identifier of the account to update.
119
+ * @param rank - The rank the account is to hold.
120
+ */
121
+ async function restSetUserRank(id, rank) {
122
+ const response = await fetch(`/api/users/${id}/rank`, {
123
+ method: 'PUT',
124
+ headers: { 'Content-Type': 'application/json' },
125
+ body: JSON.stringify({ rank }),
126
+ });
127
+ if (response.ok) {
128
+ return;
129
+ }
130
+ if (response.status === 401) {
131
+ throw new UnauthorizedError('session expired');
132
+ }
133
+ const refusal = await refusalOf(response);
134
+ if (refusal.code === 'rank_insufficient') {
135
+ throw new RankRefusedError(refusal.message);
136
+ }
137
+ if (refusal.code === 'self_rank_refused') {
138
+ throw new SelfRankError(refusal.message);
139
+ }
140
+ if (refusal.code === 'last_privileged_refused') {
141
+ throw new LastPrivilegedError(refusal.message);
142
+ }
143
+ throw new Error(`updating rank failed with status ${response.status}`);
144
+ }
145
+ /**
146
+ * Returns the code and message a refused response carries.
147
+ * @param response - The refused HTTP response.
148
+ * @returns The refusal code, if any, and its message.
149
+ */
150
+ async function refusalOf(response) {
151
+ try {
152
+ const held = errorSchema.parse(await response.json());
153
+ return { code: held.code, message: held.error };
154
+ }
155
+ catch {
156
+ return { message: __('the account could not be updated', DOMAIN) };
157
+ }
158
+ }
99
159
  /**
100
160
  * Returns every user account.
101
161
  * @param signal - Aborts the in-flight request.
@@ -120,3 +180,11 @@ export async function createUser(input) {
120
180
  export async function setUserDisabled(id, disabled) {
121
181
  await resolveTransport('setUserDisabled', restSetUserDisabled)(id, disabled);
122
182
  }
183
+ /**
184
+ * Sets the rank an account holds.
185
+ * @param id - The identifier of the account to update.
186
+ * @param rank - The rank the account is to hold.
187
+ */
188
+ export async function setUserRank(id, rank) {
189
+ await resolveTransport('setUserRank', restSetUserRank)(id, rank);
190
+ }
package/dist/api.d.ts CHANGED
@@ -3,6 +3,7 @@ declare const userSchema: z.ZodObject<{
3
3
  id: z.ZodString;
4
4
  email: z.ZodString;
5
5
  name: z.ZodString;
6
+ rank: z.ZodDefault<z.ZodString>;
6
7
  }, z.core.$strip>;
7
8
  export type User = z.infer<typeof userSchema>;
8
9
  /**
package/dist/api.js CHANGED
@@ -5,6 +5,7 @@ const userSchema = z.object({
5
5
  id: z.string(),
6
6
  email: z.string(),
7
7
  name: z.string(),
8
+ rank: z.string().default(''),
8
9
  });
9
10
  /**
10
11
  * InvalidCredentialsError is thrown when the backend rejects a login.
@@ -1 +1 @@
1
- { "": { "lang": "es-ES", "plural-forms": "nplurals=2; plural=(n != 1);" }, "Actions": ["Acciones"], "Active": ["Activo"], "Create user": ["Crear usuario"], "Disable": ["Desactivar"], "Disable %s": ["Desactivar a %s"], "Disabled": ["Desactivado"], "Enable": ["Activar"], "Enable %s": ["Activar a %s"], "Invalid email or password.": ["Correo o contraseña no válidos."], "Loading users…": ["Cargando usuarios…"], "Loading…": ["Cargando…"], "Log in": ["Iniciar sesión"], "Log out": ["Cerrar sesión"], "Login failed, please try again.": ["No se pudo iniciar sesión, inténtalo de nuevo."], "Logout failed, please try again.": ["No se pudo cerrar sesión, inténtalo de nuevo."], "Password": ["Contraseña"], "Something went wrong.": ["Algo ha ido mal."], "That email is already in use.": ["Ese correo ya está en uso."], "The user could not be created.": ["No se pudo crear el usuario."], "Too many attempts. Please wait a minute and try again.": ["Demasiados intentos. Espera un minuto e inténtalo de nuevo."], "Update failed.": ["No se pudo actualizar."], "Users could not be loaded.": ["No se pudieron cargar los usuarios."], "admin section\u0004Users": ["Usuarios"], "button\u0004New user": ["Nuevo usuario"], "column\u0004Email": ["Correo"], "column\u0004Name": ["Nombre"], "column\u0004Status": ["Estado"], "field label\u0004Email": ["Correo"], "field label\u0004Name": ["Nombre"], "invalid user details": ["datos de usuario no válidos"], "page heading\u0004New user": ["Nuevo usuario"], "page heading\u0004Users": ["Usuarios"] }
1
+ { "": { "lang": "es-ES", "plural-forms": "nplurals=2; plural=(n != 1);" }, "Actions": ["Acciones"], "Active": ["Activo"], "Create user": ["Crear usuario"], "Disable": ["Desactivar"], "Disable %s": ["Desactivar a %s"], "Disabled": ["Desactivado"], "Enable": ["Activar"], "Enable %s": ["Activar a %s"], "Invalid email or password.": ["Correo o contraseña no válidos."], "Loading users…": ["Cargando usuarios…"], "Loading…": ["Cargando…"], "Log in": ["Iniciar sesión"], "Log out": ["Cerrar sesión"], "Login failed, please try again.": ["No se pudo iniciar sesión, inténtalo de nuevo."], "Logout failed, please try again.": ["No se pudo cerrar sesión, inténtalo de nuevo."], "Password": ["Contraseña"], "Something went wrong.": ["Algo ha ido mal."], "That email is already in use.": ["Ese correo ya está en uso."], "The user could not be created.": ["No se pudo crear el usuario."], "Too many attempts. Please wait a minute and try again.": ["Demasiados intentos. Espera un minuto e inténtalo de nuevo."], "Update failed.": ["No se pudo actualizar."], "Users could not be loaded.": ["No se pudieron cargar los usuarios."], "admin section\u0004Users": ["Usuarios"], "button\u0004New user": ["Nuevo usuario"], "column\u0004Email": ["Correo"], "column\u0004Name": ["Nombre"], "column\u0004Status": ["Estado"], "field label\u0004Email": ["Correo"], "field label\u0004Name": ["Nombre"], "invalid user details": ["datos de usuario no válidos"], "page heading\u0004New user": ["Nuevo usuario"], "page heading\u0004Users": ["Usuarios"], "the account could not be updated": ["no se ha podido actualizar la cuenta"] }
@@ -1 +1 @@
1
- {"":{"lang":"es-ES","plural-forms":"nplurals=2; plural=(n != 1);"},"Actions":["Acciones"],"Active":["Activo"],"Create user":["Crear usuario"],"Disable":["Desactivar"],"Disable %s":["Desactivar a %s"],"Disabled":["Desactivado"],"Enable":["Activar"],"Enable %s":["Activar a %s"],"Invalid email or password.":["Correo o contraseña no válidos."],"Loading users…":["Cargando usuarios…"],"Loading…":["Cargando…"],"Log in":["Iniciar sesión"],"Log out":["Cerrar sesión"],"Login failed, please try again.":["No se pudo iniciar sesión, inténtalo de nuevo."],"Logout failed, please try again.":["No se pudo cerrar sesión, inténtalo de nuevo."],"Password":["Contraseña"],"Something went wrong.":["Algo ha ido mal."],"That email is already in use.":["Ese correo ya está en uso."],"The user could not be created.":["No se pudo crear el usuario."],"Too many attempts. Please wait a minute and try again.":["Demasiados intentos. Espera un minuto e inténtalo de nuevo."],"Update failed.":["No se pudo actualizar."],"Users could not be loaded.":["No se pudieron cargar los usuarios."],"admin section\u0004Users":["Usuarios"],"button\u0004New user":["Nuevo usuario"],"column\u0004Email":["Correo"],"column\u0004Name":["Nombre"],"column\u0004Status":["Estado"],"field label\u0004Email":["Correo"],"field label\u0004Name":["Nombre"],"invalid user details":["datos de usuario no válidos"],"page heading\u0004New user":["Nuevo usuario"],"page heading\u0004Users":["Usuarios"]}
1
+ {"":{"lang":"es-ES","plural-forms":"nplurals=2; plural=(n != 1);"},"Actions":["Acciones"],"Active":["Activo"],"Create user":["Crear usuario"],"Disable":["Desactivar"],"Disable %s":["Desactivar a %s"],"Disabled":["Desactivado"],"Enable":["Activar"],"Enable %s":["Activar a %s"],"Invalid email or password.":["Correo o contraseña no válidos."],"Loading users…":["Cargando usuarios…"],"Loading…":["Cargando…"],"Log in":["Iniciar sesión"],"Log out":["Cerrar sesión"],"Login failed, please try again.":["No se pudo iniciar sesión, inténtalo de nuevo."],"Logout failed, please try again.":["No se pudo cerrar sesión, inténtalo de nuevo."],"Password":["Contraseña"],"Something went wrong.":["Algo ha ido mal."],"That email is already in use.":["Ese correo ya está en uso."],"The user could not be created.":["No se pudo crear el usuario."],"Too many attempts. Please wait a minute and try again.":["Demasiados intentos. Espera un minuto e inténtalo de nuevo."],"Update failed.":["No se pudo actualizar."],"Users could not be loaded.":["No se pudieron cargar los usuarios."],"admin section\u0004Users":["Usuarios"],"button\u0004New user":["Nuevo usuario"],"column\u0004Email":["Correo"],"column\u0004Name":["Nombre"],"column\u0004Status":["Estado"],"field label\u0004Email":["Correo"],"field label\u0004Name":["Nombre"],"invalid user details":["datos de usuario no válidos"],"page heading\u0004New user":["Nuevo usuario"],"page heading\u0004Users":["Usuarios"],"the account could not be updated":["no se ha podido actualizar la cuenta"]}
package/dist/session.d.ts CHANGED
@@ -10,6 +10,7 @@ export declare function useSession(): import("@tanstack/react-query").UseQueryRe
10
10
  id: string;
11
11
  email: string;
12
12
  name: string;
13
+ rank: string;
13
14
  } | null>, Error>;
14
15
  /**
15
16
  * Ends the current session and drops all cached data belonging to the
@@ -11,6 +11,17 @@ export declare function installTestEnvironment(): void;
11
11
  * defaultUser is the canned account tests sign in as.
12
12
  */
13
13
  export declare const defaultUser: User;
14
+ /**
15
+ * Returns a canned account holding the given rank, one identifier per rank.
16
+ * @param rank - The rank the account holds.
17
+ * @returns The account.
18
+ */
19
+ export declare function rankedUser(rank: string): User;
20
+ /**
21
+ * Accepts a rank change.
22
+ * @returns The msw handler.
23
+ */
24
+ export declare function rankOk(): import("msw").HttpHandler;
14
25
  /**
15
26
  * Seeds the cached session so components render signed in without a fetch.
16
27
  * @param client - The query client under test.
@@ -25,7 +25,34 @@ export const defaultUser = {
25
25
  id: '0198b2f0-0000-7000-8000-000000000001',
26
26
  email: 'grace@example.com',
27
27
  name: 'Grace Hopper',
28
+ rank: '',
28
29
  };
30
+ /**
31
+ * Returns a canned account holding the given rank, one identifier per rank.
32
+ * @param rank - The rank the account holds.
33
+ * @returns The account.
34
+ */
35
+ export function rankedUser(rank) {
36
+ let held = 2166136261;
37
+ for (const letter of rank) {
38
+ held ^= letter.charCodeAt(0);
39
+ held = Math.imul(held, 16777619);
40
+ }
41
+ held >>>= 0;
42
+ return {
43
+ id: `0198b2f0-0000-7000-8000-${String(held).padStart(12, '0')}`,
44
+ email: `${rank}@example.com`,
45
+ name: `Maria Perez the ${rank}`,
46
+ rank,
47
+ };
48
+ }
49
+ /**
50
+ * Accepts a rank change.
51
+ * @returns The msw handler.
52
+ */
53
+ export function rankOk() {
54
+ return http.put('/api/users/:id/rank', () => new HttpResponse(null, { status: 204 }));
55
+ }
29
56
  /**
30
57
  * Seeds the cached session so components render signed in without a fetch.
31
58
  * @param client - The query client under test.
@@ -14,6 +14,7 @@ export interface AuthTransport {
14
14
  fetchUsers(signal?: AbortSignal): Promise<Account[]>;
15
15
  createUser(input: NewUser): Promise<Account>;
16
16
  setUserDisabled(id: string, disabled: boolean): Promise<void>;
17
+ setUserRank(id: string, rank: string): Promise<void>;
17
18
  }
18
19
  /**
19
20
  * Replaces backend calls with the given implementations, keeping the REST
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gopherium/react-auth",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Session authentication for React apps backed by gouncer's authkit.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",