@axium/client 0.10.0 → 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.
package/assets/styles.css CHANGED
@@ -101,6 +101,53 @@ pre {
101
101
  border-radius: 0.5em;
102
102
  }
103
103
 
104
+ dialog {
105
+ border-radius: 1em;
106
+ background: var(--bg-menu);
107
+ border: 1px solid #8888;
108
+ padding: 1em;
109
+ max-width: calc(100% - 2em);
110
+ word-wrap: normal;
111
+
112
+ form {
113
+ max-width: 100%;
114
+ }
115
+ }
116
+
117
+ dialog::backdrop {
118
+ background: #0003;
119
+ }
120
+
121
+ dialog[open] {
122
+ animation: zoom 0.25s cubic-bezier(0.35, 1.55, 0.65, 1);
123
+ }
124
+
125
+ @keyframes zoom {
126
+ from {
127
+ transform: scale(0.95);
128
+ }
129
+ to {
130
+ transform: scale(1);
131
+ }
132
+ }
133
+
134
+ dialog[open]::backdrop {
135
+ animation: fade 0.25s ease-out;
136
+ }
137
+
138
+ @keyframes fade {
139
+ from {
140
+ opacity: 0;
141
+ }
142
+ to {
143
+ opacity: 1;
144
+ }
145
+ }
146
+
147
+ dialog form {
148
+ display: contents;
149
+ }
150
+
104
151
  .error {
105
152
  padding: 1em;
106
153
  border-radius: 0.5em;
@@ -208,6 +255,9 @@ h6 {
208
255
  }
209
256
 
210
257
  @media (width < 700px) {
258
+ dialog {
259
+ }
260
+
211
261
  .mobile-hide {
212
262
  display: none !important;
213
263
  }
@@ -14,8 +14,10 @@ export declare function session(): {
14
14
  debug: boolean;
15
15
  };
16
16
  roles: string[];
17
+ tags: string[];
17
18
  registeredAt: Date;
18
19
  isAdmin: boolean;
20
+ isSuspended: boolean;
19
21
  emailVerified?: Date | null | undefined;
20
22
  image?: string | null | undefined;
21
23
  };
package/dist/config.d.ts CHANGED
@@ -14,14 +14,16 @@ export declare const ClientConfig: z.ZodObject<{
14
14
  id: z.ZodUUID;
15
15
  name: z.ZodString;
16
16
  email: z.ZodEmail;
17
- emailVerified: z.ZodOptional<z.ZodNullable<z.ZodDate>>;
17
+ emailVerified: z.ZodOptional<z.ZodNullable<z.ZodCoercedDate<unknown>>>;
18
18
  image: z.ZodOptional<z.ZodNullable<z.ZodURL>>;
19
- preferences: z.ZodObject<{
19
+ preferences: z.ZodLazy<z.ZodObject<{
20
20
  debug: z.ZodDefault<z.ZodBoolean>;
21
- }, z.core.$strip>;
21
+ }, z.core.$strip>>;
22
22
  roles: z.ZodArray<z.ZodString>;
23
+ tags: z.ZodArray<z.ZodString>;
23
24
  registeredAt: z.ZodCoercedDate<unknown>;
24
25
  isAdmin: z.ZodBoolean;
26
+ isSuspended: z.ZodBoolean;
25
27
  }, z.core.$strip>;
26
28
  }, z.core.$strip>;
27
29
  apps: z.ZodArray<z.ZodObject<{
@@ -1,7 +1,8 @@
1
- import type { $API, APIParameters, Endpoint, RequestBody } from '@axium/core/api';
1
+ import type { APIParameters, APIValues, Endpoint, RequestBody } from '@axium/core/api';
2
+ import { $API } from '@axium/core/api';
2
3
  import type { RequestMethod } from '@axium/core/requests';
3
4
  export declare let token: string | null;
4
5
  export declare function setToken(value: string | null): void;
5
6
  export declare let prefix: string;
6
7
  export declare function setPrefix(value: string): void;
7
- export declare function fetchAPI<const M extends RequestMethod, const E extends Endpoint>(method: M, endpoint: E, data?: RequestBody<M, E>, ...params: APIParameters<E>): Promise<M extends keyof $API[E] ? ($API[E][M] extends [unknown, infer R] ? R : $API[E][M]) : unknown>;
8
+ export declare function fetchAPI<const E extends Endpoint, const M extends keyof $API[E] & RequestMethod>(method: M, endpoint: E, data?: RequestBody<M, E>, ...params: APIParameters<E>): Promise<M extends keyof APIValues[E] ? (APIValues[E][M] extends readonly [unknown, infer R] ? R : APIValues[E][M]) : unknown>;
package/dist/requests.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { $API } from '@axium/core/api';
2
+ import { prettifyError } from 'zod';
1
3
  export let token = null;
2
4
  export function setToken(value) {
3
5
  token = value;
@@ -14,6 +16,14 @@ export async function fetchAPI(method, endpoint, data, ...params) {
14
16
  Accept: 'application/json',
15
17
  },
16
18
  };
19
+ const schema = $API[endpoint]?.[method];
20
+ if (schema && Array.isArray(schema))
21
+ try {
22
+ data = schema[0].parse(data);
23
+ }
24
+ catch (e) {
25
+ throw prettifyError(e);
26
+ }
17
27
  if (method !== 'GET' && method !== 'HEAD')
18
28
  options.body = JSON.stringify(data);
19
29
  const search = method != 'GET' || typeof data != 'object' || data == null || !Object.keys(data).length
@@ -39,5 +49,13 @@ export async function fetchAPI(method, endpoint, data, ...params) {
39
49
  const json = await response.json().catch(() => ({ message: 'Unknown server error (invalid JSON response)' }));
40
50
  if (!response.ok)
41
51
  throw new Error(json.message);
42
- return json;
52
+ if (!schema)
53
+ return json;
54
+ const Output = Array.isArray(schema) ? schema[1] : schema;
55
+ try {
56
+ return Output.parse(json);
57
+ }
58
+ catch (e) {
59
+ throw prettifyError(e);
60
+ }
43
61
  }
package/dist/user.d.ts CHANGED
@@ -19,7 +19,11 @@ export declare function updateUser(userId: string, data: Record<string, FormData
19
19
  export declare function fullUserInfo(userId: string): Promise<User & {
20
20
  sessions: Session[];
21
21
  }>;
22
- export declare function deleteUser(userId: string): Promise<User>;
22
+ /**
23
+ * @param userId The UUID of the user to delete
24
+ * @param deletingId The UUID of the user performing the deletion (for authentication). Defaults to userId.
25
+ */
26
+ export declare function deleteUser(userId: string, deletingId?: string): Promise<User>;
23
27
  export declare function emailVerificationEnabled(userId: string): Promise<boolean>;
24
28
  export declare function sendVerificationEmail(userId: string): Promise<Verification>;
25
29
  export declare function verifyEmail(userId: string, token: string): Promise<void>;
package/dist/user.js CHANGED
@@ -1,4 +1,3 @@
1
- import { UserChangeable } from '@axium/core';
2
1
  import { startAuthentication, startRegistration } from '@simplewebauthn/browser';
3
2
  import * as z from 'zod';
4
3
  import { fetchAPI } from './requests.js';
@@ -23,38 +22,20 @@ export async function loginByEmail(email) {
23
22
  return await login(userId);
24
23
  }
25
24
  export async function getCurrentSession() {
26
- const result = await fetchAPI('GET', 'session');
27
- result.created = new Date(result.created);
28
- result.expires = new Date(result.expires);
29
- return result;
25
+ return await fetchAPI('GET', 'session');
30
26
  }
31
27
  export async function getSessions(userId) {
32
28
  _checkId(userId);
33
- const result = await fetchAPI('GET', 'users/:id/sessions', {}, userId);
34
- for (const session of result) {
35
- session.created = new Date(session.created);
36
- session.expires = new Date(session.expires);
37
- }
38
- return result;
29
+ return await fetchAPI('GET', 'users/:id/sessions', {}, userId);
39
30
  }
40
31
  export async function logout(userId, ...sessionId) {
41
32
  _checkId(userId);
42
- const result = await fetchAPI('DELETE', 'users/:id/sessions', { id: sessionId }, userId);
43
- for (const session of result) {
44
- session.created = new Date(session.created);
45
- session.expires = new Date(session.expires);
46
- }
47
- return result;
33
+ return await fetchAPI('DELETE', 'users/:id/sessions', { id: sessionId }, userId);
48
34
  }
49
35
  export async function logoutAll(userId) {
50
36
  _checkId(userId);
51
37
  await elevate(userId);
52
- const result = await fetchAPI('DELETE', 'users/:id/sessions', { confirm_all: true }, userId);
53
- for (const session of result) {
54
- session.created = new Date(session.created);
55
- session.expires = new Date(session.expires);
56
- }
57
- return result;
38
+ return await fetchAPI('DELETE', 'users/:id/sessions', { confirm_all: true }, userId);
58
39
  }
59
40
  export async function logoutCurrentSession() {
60
41
  return await fetchAPI('DELETE', 'session');
@@ -80,59 +61,43 @@ function _checkId(userId) {
80
61
  }
81
62
  export async function userInfo(userId) {
82
63
  _checkId(userId);
83
- const user = await fetchAPI('GET', 'users/:id', {}, userId);
84
- user.registeredAt = new Date(user.registeredAt);
85
- user.emailVerified = user.emailVerified ? new Date(user.emailVerified) : null;
86
- return user;
64
+ return await fetchAPI('GET', 'users/:id', {}, userId);
87
65
  }
88
66
  export async function updateUser(userId, data) {
89
67
  _checkId(userId);
90
- const body = await UserChangeable.parseAsync(data).catch(e => {
91
- throw e instanceof z.core.$ZodError ? z.prettifyError(e) : e;
92
- });
93
- const result = await fetchAPI('PATCH', 'users/:id', body, userId);
94
- result.registeredAt = new Date(result.registeredAt);
95
- if (result.emailVerified)
96
- result.emailVerified = new Date(result.emailVerified);
97
- return result;
68
+ return await fetchAPI('PATCH', 'users/:id', data, userId);
98
69
  }
99
70
  export async function fullUserInfo(userId) {
100
71
  _checkId(userId);
101
- const result = await fetchAPI('GET', 'users/:id/full', {}, userId);
102
- result.registeredAt = new Date(result.registeredAt);
103
- result.emailVerified = new Date(result.emailVerified);
104
- return result;
72
+ return await fetchAPI('GET', 'users/:id/full', {}, userId);
105
73
  }
106
- export async function deleteUser(userId) {
74
+ /**
75
+ * @param userId The UUID of the user to delete
76
+ * @param deletingId The UUID of the user performing the deletion (for authentication). Defaults to userId.
77
+ */
78
+ export async function deleteUser(userId, deletingId = userId) {
107
79
  _checkId(userId);
108
- const options = await fetchAPI('OPTIONS', 'users/:id/auth', { type: 'action' }, userId);
80
+ const options = await fetchAPI('OPTIONS', 'users/:id/auth', { type: 'action' }, deletingId);
109
81
  const response = await startAuthentication({ optionsJSON: options });
110
- await fetchAPI('POST', 'users/:id/auth', response, userId);
111
- const result = await fetchAPI('DELETE', 'users/:id', response, userId);
112
- result.registeredAt = new Date(result.registeredAt);
113
- result.emailVerified = new Date(result.emailVerified);
114
- return result;
82
+ await fetchAPI('POST', 'users/:id/auth', response, deletingId);
83
+ return await fetchAPI('DELETE', 'users/:id', response, userId);
115
84
  }
116
85
  export async function emailVerificationEnabled(userId) {
117
86
  _checkId(userId);
118
- const { enabled } = await fetchAPI('OPTIONS', 'users/:id/verify_email', {}, userId);
87
+ const { enabled } = await fetchAPI('OPTIONS', 'users/:id/verify/email', {}, userId);
119
88
  return enabled;
120
89
  }
121
90
  export async function sendVerificationEmail(userId) {
122
91
  _checkId(userId);
123
- return await fetchAPI('GET', 'users/:id/verify_email', {}, userId);
92
+ return await fetchAPI('GET', 'users/:id/verify/email', {}, userId);
124
93
  }
125
94
  export async function verifyEmail(userId, token) {
126
95
  _checkId(userId);
127
- await fetchAPI('POST', 'users/:id/verify_email', { token }, userId);
96
+ await fetchAPI('POST', 'users/:id/verify/email', { token }, userId);
128
97
  }
129
98
  export async function getPasskeys(userId) {
130
99
  _checkId(userId);
131
- const result = await fetchAPI('GET', 'users/:id/passkeys', {}, userId);
132
- for (const passkey of result) {
133
- passkey.createdAt = new Date(passkey.createdAt);
134
- }
135
- return result;
100
+ return await fetchAPI('GET', 'users/:id/passkeys', {}, userId);
136
101
  }
137
102
  /**
138
103
  * Create a new passkey for an existing user.
@@ -1,5 +1,4 @@
1
1
  <script lang="ts">
2
- import Dialog from './Dialog.svelte';
3
2
  import type { HTMLDialogAttributes } from 'svelte/elements';
4
3
 
5
4
  let {
@@ -59,7 +58,7 @@
59
58
  <button type="submit" class={['submit', submitDanger && 'danger']}>{submitText}</button>
60
59
  {/snippet}
61
60
 
62
- <Dialog bind:dialog {onclose} {...rest}>
61
+ <dialog bind:this={dialog} {onclose} {...rest}>
63
62
  {@render header?.()}
64
63
  <form {onsubmit} class="main" method="dialog">
65
64
  {#if error}
@@ -76,7 +75,7 @@
76
75
  {/if}
77
76
  </form>
78
77
  {@render footer?.()}
79
- </Dialog>
78
+ </dialog>
80
79
 
81
80
  <style>
82
81
  .actions {
@@ -0,0 +1,21 @@
1
+ <script lang="ts">
2
+ import ClipboardCopy from './ClipboardCopy.svelte';
3
+
4
+ const { url }: { url: string } = $props();
5
+
6
+ const href = $derived(new URL(url, location.origin).href);
7
+ </script>
8
+
9
+ <pre class="URLText"><span>{href}</span><ClipboardCopy value={href} --size="16px" /></pre>
10
+
11
+ <style>
12
+ .URLText {
13
+ display: flex;
14
+ align-items: center;
15
+ justify-content: space-between;
16
+
17
+ span {
18
+ overflow-x: scroll;
19
+ }
20
+ }
21
+ </style>
package/lib/index.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  export { default as AccessControlDialog } from './AccessControlDialog.svelte';
2
2
  export { default as AppMenu } from './AppMenu.svelte';
3
3
  export { default as ClipboardCopy } from './ClipboardCopy.svelte';
4
- export { default as Dialog } from './Dialog.svelte';
5
4
  export { default as FormDialog } from './FormDialog.svelte';
6
5
  export { default as Icon } from './Icon.svelte';
7
6
  export { default as Login } from './Login.svelte';
@@ -14,6 +13,7 @@ export { default as Register } from './Register.svelte';
14
13
  export { default as SessionList } from './SessionList.svelte';
15
14
  export { default as Toast } from './Toast.svelte';
16
15
  export { default as Upload } from './Upload.svelte';
16
+ export { default as URLText } from './URLText.svelte';
17
17
  export { default as UserCard } from './UserCard.svelte';
18
18
  export { default as UserMenu } from './UserMenu.svelte';
19
19
  export { default as Version } from './Version.svelte';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@axium/client",
3
- "version": "0.10.0",
3
+ "version": "0.11.0",
4
4
  "author": "James Prevett <jp@jamespre.dev>",
5
5
  "funding": {
6
6
  "type": "individual",
@@ -40,7 +40,7 @@
40
40
  "build": "tsc"
41
41
  },
42
42
  "peerDependencies": {
43
- "@axium/core": ">=0.16.0",
43
+ "@axium/core": ">=0.17.0",
44
44
  "utilium": "^2.3.8",
45
45
  "zod": "^4.0.5",
46
46
  "svelte": "^5.36.0"
package/lib/Dialog.svelte DELETED
@@ -1,53 +0,0 @@
1
- <script lang="ts">
2
- import type { HTMLDialogAttributes } from 'svelte/elements';
3
-
4
- let { children, dialog = $bindable(), ...rest }: { children(): any; dialog?: HTMLDialogElement } & HTMLDialogAttributes = $props();
5
- </script>
6
-
7
- <dialog bind:this={dialog} {...rest}>
8
- {@render children()}
9
- </dialog>
10
-
11
- <!-- svelte-ignore css_unused_selector -->
12
- <style>
13
- dialog {
14
- border-radius: 1em;
15
- background: var(--bg-menu);
16
- border: 1px solid #8888;
17
- padding: 1em;
18
-
19
- form {
20
- display: contents;
21
- }
22
- }
23
-
24
- dialog::backdrop {
25
- background: #0003;
26
- }
27
-
28
- dialog[open] {
29
- animation: zoom 0.25s cubic-bezier(0.35, 1.55, 0.65, 1);
30
- }
31
-
32
- @keyframes zoom {
33
- from {
34
- transform: scale(0.95);
35
- }
36
- to {
37
- transform: scale(1);
38
- }
39
- }
40
-
41
- dialog[open]::backdrop {
42
- animation: fade 0.25s ease-out;
43
- }
44
-
45
- @keyframes fade {
46
- from {
47
- opacity: 0;
48
- }
49
- to {
50
- opacity: 1;
51
- }
52
- }
53
- </style>