@draftlab/auth 0.13.0 → 0.14.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/dist/provider/code.mjs +5 -0
- package/dist/provider/magiclink.mjs +1 -0
- package/dist/provider/passkey.mjs +1 -1
- package/dist/provider/password.mjs +9 -0
- package/dist/ui/code.mjs +15 -8
- package/dist/ui/magiclink.mjs +15 -8
- package/dist/ui/passkey.d.mts +5 -3
- package/dist/ui/passkey.mjs +34 -8
- package/dist/ui/password.d.mts +0 -5
- package/dist/ui/password.mjs +259 -244
- package/dist/util.d.mts +24 -1
- package/dist/util.mjs +24 -1
- package/package.json +1 -1
package/dist/provider/code.mjs
CHANGED
|
@@ -145,6 +145,11 @@ const CodeProvider = (config) => {
|
|
|
145
145
|
...currentState,
|
|
146
146
|
resend: false
|
|
147
147
|
}, formData, { type: "invalid_code" });
|
|
148
|
+
if (!await ctx.get(c, "authorization")) return transition(c, { type: "start" }, formData, {
|
|
149
|
+
type: "invalid_claim",
|
|
150
|
+
key: "session",
|
|
151
|
+
value: "Authentication session expired"
|
|
152
|
+
});
|
|
148
153
|
await ctx.unset(c, "provider");
|
|
149
154
|
return await ctx.success(c, { claims: currentState.claims });
|
|
150
155
|
}
|
|
@@ -74,6 +74,7 @@ const MagicLinkProvider = (config) => {
|
|
|
74
74
|
if (!urlValue || !storedValue) return false;
|
|
75
75
|
return timingSafeCompare(storedValue, urlValue);
|
|
76
76
|
})) return transition(c, { type: "start" }, void 0, { type: "invalid_link" });
|
|
77
|
+
if (!await ctx.get(c, "authorization")) return transition(c, { type: "start" }, void 0, { type: "invalid_link" });
|
|
77
78
|
await ctx.unset(c, "provider");
|
|
78
79
|
return await ctx.success(c, { claims: storedState.claims });
|
|
79
80
|
});
|
|
@@ -171,7 +171,7 @@ const PasskeyProvider = (config) => {
|
|
|
171
171
|
authenticatorSelection: authenticatorSelection ?? {
|
|
172
172
|
residentKey: "preferred",
|
|
173
173
|
userVerification: "preferred",
|
|
174
|
-
authenticatorAttachment: otherDevice ?
|
|
174
|
+
authenticatorAttachment: otherDevice ? void 0 : "platform"
|
|
175
175
|
},
|
|
176
176
|
timeout
|
|
177
177
|
});
|
|
@@ -193,6 +193,14 @@ const PasswordProvider = (config) => {
|
|
|
193
193
|
type: "start",
|
|
194
194
|
redirect: provider.redirect
|
|
195
195
|
}, { type: "invalid_email" });
|
|
196
|
+
if (!await Storage.get(ctx.storage, [
|
|
197
|
+
"email",
|
|
198
|
+
email,
|
|
199
|
+
"password"
|
|
200
|
+
])) return transition({
|
|
201
|
+
type: "start",
|
|
202
|
+
redirect: provider.redirect
|
|
203
|
+
}, { type: "invalid_email" });
|
|
196
204
|
const code = generateCode();
|
|
197
205
|
const context = provider.type === "code" && provider.email === email ? "reset:resend" : "reset";
|
|
198
206
|
await config.sendCode(email, code, context);
|
|
@@ -221,6 +229,7 @@ const PasswordProvider = (config) => {
|
|
|
221
229
|
const password = formData.get("password")?.toString();
|
|
222
230
|
const repeat = formData.get("repeat")?.toString();
|
|
223
231
|
if (!password) return transition(provider, { type: "invalid_password" });
|
|
232
|
+
if (!repeat) return transition(provider, { type: "invalid_password" });
|
|
224
233
|
if (password !== repeat) return transition(provider, { type: "password_mismatch" });
|
|
225
234
|
if (config.validatePassword) {
|
|
226
235
|
let validationError;
|
package/dist/ui/code.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { run } from "../util.mjs";
|
|
1
2
|
import { Layout, renderToHTML } from "./base.mjs";
|
|
2
3
|
import { FormAlert } from "./form.mjs";
|
|
3
4
|
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
@@ -57,10 +58,13 @@ const CodeUI = (options) => {
|
|
|
57
58
|
"data-component": "form",
|
|
58
59
|
method: "post",
|
|
59
60
|
children: [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
61
|
+
run(() => {
|
|
62
|
+
if (success) return /* @__PURE__ */ jsx(FormAlert, {
|
|
63
|
+
message: success.message,
|
|
64
|
+
color: "success"
|
|
65
|
+
});
|
|
66
|
+
return /* @__PURE__ */ jsx(FormAlert, { message: getErrorMessage(error, copy) });
|
|
67
|
+
}),
|
|
64
68
|
/* @__PURE__ */ jsx("input", {
|
|
65
69
|
"data-component": "input",
|
|
66
70
|
type: mode === "email" ? "email" : "tel",
|
|
@@ -97,10 +101,13 @@ const CodeUI = (options) => {
|
|
|
97
101
|
"data-component": "form",
|
|
98
102
|
method: "post",
|
|
99
103
|
children: [
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
+
run(() => {
|
|
105
|
+
if (success) return /* @__PURE__ */ jsx(FormAlert, {
|
|
106
|
+
message: success.message,
|
|
107
|
+
color: "success"
|
|
108
|
+
});
|
|
109
|
+
return /* @__PURE__ */ jsx(FormAlert, { message: getErrorMessage(error, copy) });
|
|
110
|
+
}),
|
|
104
111
|
/* @__PURE__ */ jsx("input", {
|
|
105
112
|
name: "action",
|
|
106
113
|
type: "hidden",
|
package/dist/ui/magiclink.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { run } from "../util.mjs";
|
|
1
2
|
import { Layout, renderToHTML } from "./base.mjs";
|
|
2
3
|
import { FormAlert } from "./form.mjs";
|
|
3
4
|
import { jsx, jsxs } from "preact/jsx-runtime";
|
|
@@ -55,10 +56,13 @@ const MagicLinkUI = (options) => {
|
|
|
55
56
|
"data-component": "form",
|
|
56
57
|
method: "post",
|
|
57
58
|
children: [
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
59
|
+
run(() => {
|
|
60
|
+
if (success) return /* @__PURE__ */ jsx(FormAlert, {
|
|
61
|
+
message: success.message,
|
|
62
|
+
color: "success"
|
|
63
|
+
});
|
|
64
|
+
return /* @__PURE__ */ jsx(FormAlert, { message: getErrorMessage(error, copy) });
|
|
65
|
+
}),
|
|
62
66
|
/* @__PURE__ */ jsx("input", {
|
|
63
67
|
"data-component": "input",
|
|
64
68
|
type: mode === "email" ? "email" : "tel",
|
|
@@ -99,10 +103,13 @@ const MagicLinkUI = (options) => {
|
|
|
99
103
|
"data-component": "title",
|
|
100
104
|
children: "Check your email"
|
|
101
105
|
}),
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
+
run(() => {
|
|
107
|
+
if (success) return /* @__PURE__ */ jsx(FormAlert, {
|
|
108
|
+
message: success.message,
|
|
109
|
+
color: "success"
|
|
110
|
+
});
|
|
111
|
+
return /* @__PURE__ */ jsx(FormAlert, { message: getErrorMessage(error, copy) });
|
|
112
|
+
}),
|
|
106
113
|
/* @__PURE__ */ jsx("p", {
|
|
107
114
|
"data-component": "description",
|
|
108
115
|
children: "Click the link in your email to sign in."
|
package/dist/ui/passkey.d.mts
CHANGED
|
@@ -12,10 +12,12 @@ interface PasskeyUICopy {
|
|
|
12
12
|
readonly register_prompt: string;
|
|
13
13
|
readonly login_prompt: string;
|
|
14
14
|
readonly login: string;
|
|
15
|
-
readonly change_prompt: string;
|
|
16
|
-
readonly code_resend: string;
|
|
17
|
-
readonly code_return: string;
|
|
18
15
|
readonly input_email: string;
|
|
16
|
+
readonly error_register_already_registered: string;
|
|
17
|
+
readonly error_register_cancelled: string;
|
|
18
|
+
readonly error_register_failed: string;
|
|
19
|
+
readonly error_auth_cancelled: string;
|
|
20
|
+
readonly error_auth_failed: string;
|
|
19
21
|
}
|
|
20
22
|
interface PasskeyUIOptions extends Omit<PasskeyProviderConfig, "authorize" | "register" | "copy"> {
|
|
21
23
|
readonly copy?: Partial<PasskeyUICopy>;
|
package/dist/ui/passkey.mjs
CHANGED
|
@@ -10,10 +10,12 @@ const DEFAULT_COPY = {
|
|
|
10
10
|
login_prompt: "Already have an account?",
|
|
11
11
|
login: "Login",
|
|
12
12
|
button_continue: "Continue",
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
input_email: "Email",
|
|
14
|
+
error_register_already_registered: "This device is already registered. Please use the login page or try a different device.",
|
|
15
|
+
error_register_cancelled: "Registration was cancelled or timed out. Please try again.",
|
|
16
|
+
error_register_failed: "Registration failed. Please try again.",
|
|
17
|
+
error_auth_cancelled: "Authentication was cancelled or timed out. Please try again.",
|
|
18
|
+
error_auth_failed: "Authentication failed. Please try again."
|
|
17
19
|
};
|
|
18
20
|
const PasskeyUI = (options) => {
|
|
19
21
|
const { rpName, rpID, origin, userCanRegisterPasskey, authenticatorSelection, attestationType, timeout } = options;
|
|
@@ -57,8 +59,17 @@ const PasskeyUI = (options) => {
|
|
|
57
59
|
// Pass the options to the authenticator and wait for a response
|
|
58
60
|
attResp = await startAuthentication({ optionsJSON });
|
|
59
61
|
} catch (error) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
// Handle WebAuthn errors with friendly messages
|
|
63
|
+
const errorName = error.name;
|
|
64
|
+
if (errorName === "NotAllowedError") {
|
|
65
|
+
message.textContent = "${copy.error_auth_cancelled}";
|
|
66
|
+
} else if (errorName === "InvalidStateError") {
|
|
67
|
+
message.textContent = "${copy.error_auth_failed}";
|
|
68
|
+
} else {
|
|
69
|
+
message.textContent = error.message || "${copy.error_auth_failed}";
|
|
70
|
+
}
|
|
71
|
+
console.error(error);
|
|
72
|
+
return;
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
const verificationResp = await fetch(
|
|
@@ -147,6 +158,7 @@ const PasskeyUI = (options) => {
|
|
|
147
158
|
window.addEventListener("load", async () => {
|
|
148
159
|
const { startRegistration } = SimpleWebAuthnBrowser;
|
|
149
160
|
const registerForm = document.getElementById("registerForm");
|
|
161
|
+
const btnOtherDevice = document.getElementById("btnOtherDevice");
|
|
150
162
|
const message = document.querySelector("[data-slot='message']");
|
|
151
163
|
const origin = window.location.origin;
|
|
152
164
|
const rpID = window.location.hostname;
|
|
@@ -181,8 +193,17 @@ const PasskeyUI = (options) => {
|
|
|
181
193
|
// Pass the options to the authenticator and wait for a response
|
|
182
194
|
attResp = await startRegistration({ optionsJSON });
|
|
183
195
|
} catch (error) {
|
|
184
|
-
|
|
185
|
-
|
|
196
|
+
// Handle WebAuthn errors with friendly messages
|
|
197
|
+
const errorName = error.name;
|
|
198
|
+
if (errorName === "InvalidStateError") {
|
|
199
|
+
message.textContent = "${copy.error_register_already_registered}";
|
|
200
|
+
} else if (errorName === "NotAllowedError") {
|
|
201
|
+
message.textContent = "${copy.error_register_cancelled}";
|
|
202
|
+
} else {
|
|
203
|
+
message.textContent = error.message || "${copy.error_register_failed}";
|
|
204
|
+
}
|
|
205
|
+
console.error(error);
|
|
206
|
+
return;
|
|
186
207
|
}
|
|
187
208
|
|
|
188
209
|
// POST the response to the endpoint that calls
|
|
@@ -232,6 +253,11 @@ const PasskeyUI = (options) => {
|
|
|
232
253
|
e.preventDefault();
|
|
233
254
|
register();
|
|
234
255
|
});
|
|
256
|
+
|
|
257
|
+
btnOtherDevice.addEventListener("click", (e) => {
|
|
258
|
+
e.preventDefault();
|
|
259
|
+
register(true);
|
|
260
|
+
});
|
|
235
261
|
});
|
|
236
262
|
` } }),
|
|
237
263
|
/* @__PURE__ */ jsxs("form", {
|
package/dist/ui/password.d.mts
CHANGED
|
@@ -11,11 +11,6 @@ interface PasswordUICopy {
|
|
|
11
11
|
readonly error_invalid_email: string;
|
|
12
12
|
readonly error_invalid_password: string;
|
|
13
13
|
readonly error_password_mismatch: string;
|
|
14
|
-
readonly error_validation_error: string;
|
|
15
|
-
readonly register_title: string;
|
|
16
|
-
readonly register_description: string;
|
|
17
|
-
readonly login_title: string;
|
|
18
|
-
readonly login_description: string;
|
|
19
14
|
readonly register: string;
|
|
20
15
|
readonly register_prompt: string;
|
|
21
16
|
readonly login_prompt: string;
|
package/dist/ui/password.mjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { run } from "../util.mjs";
|
|
1
2
|
import { Layout, renderToHTML } from "./base.mjs";
|
|
2
3
|
import { FormAlert } from "./form.mjs";
|
|
3
4
|
import { Fragment, jsx, jsxs } from "preact/jsx-runtime";
|
|
@@ -12,11 +13,6 @@ const DEFAULT_COPY = {
|
|
|
12
13
|
error_invalid_email: "Email is not valid.",
|
|
13
14
|
error_invalid_password: "Password is incorrect.",
|
|
14
15
|
error_password_mismatch: "Passwords do not match.",
|
|
15
|
-
error_validation_error: "Password does not meet requirements.",
|
|
16
|
-
register_title: "Welcome to the app",
|
|
17
|
-
register_description: "Sign in with your email",
|
|
18
|
-
login_title: "Welcome to the app",
|
|
19
|
-
login_description: "Sign in with your email",
|
|
20
16
|
register: "Register",
|
|
21
17
|
register_prompt: "Don't have an account?",
|
|
22
18
|
login_prompt: "Already have an account?",
|
|
@@ -105,130 +101,133 @@ const PasswordUI = (options) => {
|
|
|
105
101
|
"password_mismatch",
|
|
106
102
|
"validation_error"
|
|
107
103
|
].includes(error?.type || "");
|
|
108
|
-
return /* @__PURE__ */ jsx(Layout, { children:
|
|
109
|
-
"
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
children: copy.button_continue
|
|
148
|
-
}),
|
|
149
|
-
/* @__PURE__ */ jsx("div", {
|
|
150
|
-
"data-component": "form-footer",
|
|
151
|
-
children: /* @__PURE__ */ jsxs("span", { children: [
|
|
152
|
-
copy.login_prompt,
|
|
153
|
-
" ",
|
|
154
|
-
/* @__PURE__ */ jsx("a", {
|
|
155
|
-
"data-component": "link",
|
|
156
|
-
href: "./authorize",
|
|
157
|
-
children: copy.login
|
|
158
|
-
})
|
|
159
|
-
] })
|
|
160
|
-
})
|
|
161
|
-
]
|
|
162
|
-
}) : /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("form", {
|
|
163
|
-
"data-component": "form",
|
|
164
|
-
method: "post",
|
|
165
|
-
children: [
|
|
166
|
-
/* @__PURE__ */ jsx(FormAlert, { message: getErrorMessage(error) }),
|
|
167
|
-
/* @__PURE__ */ jsx("input", {
|
|
168
|
-
name: "action",
|
|
169
|
-
type: "hidden",
|
|
170
|
-
value: "verify"
|
|
171
|
-
}),
|
|
172
|
-
/* @__PURE__ */ jsx("input", {
|
|
173
|
-
type: "text",
|
|
174
|
-
name: "code",
|
|
175
|
-
placeholder: copy.input_code,
|
|
176
|
-
"aria-label": "6-digit verification code",
|
|
177
|
-
autoComplete: "one-time-code",
|
|
178
|
-
"data-component": "input",
|
|
179
|
-
inputMode: "numeric",
|
|
180
|
-
maxLength: 6,
|
|
181
|
-
minLength: 6,
|
|
182
|
-
pattern: "[0-9]{6}",
|
|
183
|
-
required: true
|
|
184
|
-
}),
|
|
185
|
-
/* @__PURE__ */ jsx("button", {
|
|
186
|
-
"data-component": "button",
|
|
187
|
-
type: "submit",
|
|
188
|
-
children: copy.button_continue
|
|
189
|
-
})
|
|
190
|
-
]
|
|
191
|
-
}), /* @__PURE__ */ jsxs("form", {
|
|
192
|
-
method: "post",
|
|
193
|
-
children: [
|
|
194
|
-
/* @__PURE__ */ jsx("input", {
|
|
195
|
-
name: "action",
|
|
196
|
-
type: "hidden",
|
|
197
|
-
value: "register"
|
|
198
|
-
}),
|
|
199
|
-
/* @__PURE__ */ jsx("input", {
|
|
200
|
-
name: "email",
|
|
201
|
-
type: "hidden",
|
|
202
|
-
value: state.email
|
|
203
|
-
}),
|
|
204
|
-
/* @__PURE__ */ jsx("input", {
|
|
205
|
-
name: "password",
|
|
206
|
-
type: "hidden",
|
|
207
|
-
value: ""
|
|
208
|
-
}),
|
|
209
|
-
/* @__PURE__ */ jsx("input", {
|
|
210
|
-
name: "repeat",
|
|
211
|
-
type: "hidden",
|
|
212
|
-
value: ""
|
|
213
|
-
}),
|
|
214
|
-
/* @__PURE__ */ jsxs("div", {
|
|
215
|
-
"data-component": "form-footer",
|
|
216
|
-
children: [/* @__PURE__ */ jsxs("span", { children: [
|
|
217
|
-
copy.code_return,
|
|
218
|
-
" ",
|
|
219
|
-
/* @__PURE__ */ jsx("a", {
|
|
220
|
-
"data-component": "link",
|
|
221
|
-
href: "./authorize",
|
|
222
|
-
children: copy.login
|
|
223
|
-
})
|
|
224
|
-
] }), /* @__PURE__ */ jsx("button", {
|
|
104
|
+
return /* @__PURE__ */ jsx(Layout, { children: run(() => {
|
|
105
|
+
if (state.type === "start") return /* @__PURE__ */ jsxs("form", {
|
|
106
|
+
"data-component": "form",
|
|
107
|
+
method: "post",
|
|
108
|
+
children: [
|
|
109
|
+
/* @__PURE__ */ jsx(FormAlert, { message: getErrorMessage(error) }),
|
|
110
|
+
/* @__PURE__ */ jsx("input", {
|
|
111
|
+
name: "action",
|
|
112
|
+
type: "hidden",
|
|
113
|
+
value: "register"
|
|
114
|
+
}),
|
|
115
|
+
/* @__PURE__ */ jsx("input", {
|
|
116
|
+
type: "email",
|
|
117
|
+
name: "email",
|
|
118
|
+
placeholder: copy.input_email,
|
|
119
|
+
value: emailError ? "" : form?.get("email")?.toString() || "",
|
|
120
|
+
autoComplete: "email",
|
|
121
|
+
"data-component": "input",
|
|
122
|
+
required: true
|
|
123
|
+
}),
|
|
124
|
+
/* @__PURE__ */ jsx("input", {
|
|
125
|
+
type: "password",
|
|
126
|
+
name: "password",
|
|
127
|
+
placeholder: copy.input_password,
|
|
128
|
+
value: passwordError ? "" : form?.get("password")?.toString() || "",
|
|
129
|
+
autoComplete: "new-password",
|
|
130
|
+
"data-component": "input",
|
|
131
|
+
required: true
|
|
132
|
+
}),
|
|
133
|
+
/* @__PURE__ */ jsx("input", {
|
|
134
|
+
type: "password",
|
|
135
|
+
name: "repeat",
|
|
136
|
+
placeholder: copy.input_repeat,
|
|
137
|
+
autoComplete: "new-password",
|
|
138
|
+
"data-component": "input",
|
|
139
|
+
required: true
|
|
140
|
+
}),
|
|
141
|
+
/* @__PURE__ */ jsx("button", {
|
|
142
|
+
"data-component": "button",
|
|
225
143
|
type: "submit",
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
144
|
+
children: copy.button_continue
|
|
145
|
+
}),
|
|
146
|
+
/* @__PURE__ */ jsx("div", {
|
|
147
|
+
"data-component": "form-footer",
|
|
148
|
+
children: /* @__PURE__ */ jsxs("span", { children: [
|
|
149
|
+
copy.login_prompt,
|
|
150
|
+
" ",
|
|
151
|
+
/* @__PURE__ */ jsx("a", {
|
|
152
|
+
"data-component": "link",
|
|
153
|
+
href: "./authorize",
|
|
154
|
+
children: copy.login
|
|
155
|
+
})
|
|
156
|
+
] })
|
|
157
|
+
})
|
|
158
|
+
]
|
|
159
|
+
});
|
|
160
|
+
return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("form", {
|
|
161
|
+
"data-component": "form",
|
|
162
|
+
method: "post",
|
|
163
|
+
children: [
|
|
164
|
+
/* @__PURE__ */ jsx(FormAlert, { message: getErrorMessage(error) }),
|
|
165
|
+
/* @__PURE__ */ jsx("input", {
|
|
166
|
+
name: "action",
|
|
167
|
+
type: "hidden",
|
|
168
|
+
value: "verify"
|
|
169
|
+
}),
|
|
170
|
+
/* @__PURE__ */ jsx("input", {
|
|
171
|
+
type: "text",
|
|
172
|
+
name: "code",
|
|
173
|
+
placeholder: copy.input_code,
|
|
174
|
+
"aria-label": "6-digit verification code",
|
|
175
|
+
autoComplete: "one-time-code",
|
|
176
|
+
"data-component": "input",
|
|
177
|
+
inputMode: "numeric",
|
|
178
|
+
maxLength: 6,
|
|
179
|
+
minLength: 6,
|
|
180
|
+
pattern: "[0-9]{6}",
|
|
181
|
+
required: true
|
|
182
|
+
}),
|
|
183
|
+
/* @__PURE__ */ jsx("button", {
|
|
184
|
+
"data-component": "button",
|
|
185
|
+
type: "submit",
|
|
186
|
+
children: copy.button_continue
|
|
187
|
+
})
|
|
188
|
+
]
|
|
189
|
+
}), /* @__PURE__ */ jsxs("form", {
|
|
190
|
+
method: "post",
|
|
191
|
+
children: [
|
|
192
|
+
/* @__PURE__ */ jsx("input", {
|
|
193
|
+
name: "action",
|
|
194
|
+
type: "hidden",
|
|
195
|
+
value: "register"
|
|
196
|
+
}),
|
|
197
|
+
/* @__PURE__ */ jsx("input", {
|
|
198
|
+
name: "email",
|
|
199
|
+
type: "hidden",
|
|
200
|
+
value: state.email
|
|
201
|
+
}),
|
|
202
|
+
/* @__PURE__ */ jsx("input", {
|
|
203
|
+
name: "password",
|
|
204
|
+
type: "hidden",
|
|
205
|
+
value: ""
|
|
206
|
+
}),
|
|
207
|
+
/* @__PURE__ */ jsx("input", {
|
|
208
|
+
name: "repeat",
|
|
209
|
+
type: "hidden",
|
|
210
|
+
value: ""
|
|
211
|
+
}),
|
|
212
|
+
/* @__PURE__ */ jsxs("div", {
|
|
213
|
+
"data-component": "form-footer",
|
|
214
|
+
children: [/* @__PURE__ */ jsxs("span", { children: [
|
|
215
|
+
copy.code_return,
|
|
216
|
+
" ",
|
|
217
|
+
/* @__PURE__ */ jsx("a", {
|
|
218
|
+
"data-component": "link",
|
|
219
|
+
href: "./authorize",
|
|
220
|
+
children: copy.login
|
|
221
|
+
})
|
|
222
|
+
] }), /* @__PURE__ */ jsx("button", {
|
|
223
|
+
type: "submit",
|
|
224
|
+
"data-component": "link",
|
|
225
|
+
children: copy.code_resend
|
|
226
|
+
})]
|
|
227
|
+
})
|
|
228
|
+
]
|
|
229
|
+
})] });
|
|
230
|
+
}) });
|
|
232
231
|
};
|
|
233
232
|
/**
|
|
234
233
|
* Renders the password change form based on current state
|
|
@@ -239,124 +238,140 @@ const PasswordUI = (options) => {
|
|
|
239
238
|
"password_mismatch",
|
|
240
239
|
"validation_error"
|
|
241
240
|
].includes(error?.type || "");
|
|
242
|
-
return /* @__PURE__ */ jsx(Layout, { children:
|
|
243
|
-
"
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
241
|
+
return /* @__PURE__ */ jsx(Layout, { children: run(() => {
|
|
242
|
+
if (state.type === "start") return /* @__PURE__ */ jsxs("form", {
|
|
243
|
+
"data-component": "form",
|
|
244
|
+
method: "post",
|
|
245
|
+
children: [
|
|
246
|
+
/* @__PURE__ */ jsx(FormAlert, { message: getErrorMessage(error) }),
|
|
247
|
+
/* @__PURE__ */ jsx("input", {
|
|
248
|
+
name: "action",
|
|
249
|
+
type: "hidden",
|
|
250
|
+
value: "code"
|
|
251
|
+
}),
|
|
252
|
+
/* @__PURE__ */ jsx("input", {
|
|
253
|
+
type: "email",
|
|
254
|
+
name: "email",
|
|
255
|
+
placeholder: copy.input_email,
|
|
256
|
+
value: form?.get("email")?.toString() || "",
|
|
257
|
+
autoComplete: "email",
|
|
258
|
+
"data-component": "input",
|
|
259
|
+
required: true
|
|
260
|
+
}),
|
|
261
|
+
/* @__PURE__ */ jsx("button", {
|
|
262
|
+
"data-component": "button",
|
|
263
|
+
type: "submit",
|
|
264
|
+
children: copy.button_continue
|
|
265
|
+
}),
|
|
266
|
+
/* @__PURE__ */ jsx("div", {
|
|
267
|
+
"data-component": "form-footer",
|
|
268
|
+
children: /* @__PURE__ */ jsxs("span", { children: [
|
|
269
|
+
copy.code_return,
|
|
270
|
+
" ",
|
|
271
|
+
/* @__PURE__ */ jsx("a", {
|
|
272
|
+
"data-component": "link",
|
|
273
|
+
href: "./authorize",
|
|
274
|
+
children: copy.login
|
|
275
|
+
})
|
|
276
|
+
] })
|
|
277
|
+
})
|
|
278
|
+
]
|
|
279
|
+
});
|
|
280
|
+
if (state.type === "code") return /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsxs("form", {
|
|
281
|
+
"data-component": "form",
|
|
282
|
+
method: "post",
|
|
283
|
+
children: [
|
|
284
|
+
/* @__PURE__ */ jsx(FormAlert, { message: getErrorMessage(error) }),
|
|
285
|
+
/* @__PURE__ */ jsx("input", {
|
|
286
|
+
name: "action",
|
|
287
|
+
type: "hidden",
|
|
288
|
+
value: "verify"
|
|
289
|
+
}),
|
|
290
|
+
/* @__PURE__ */ jsx("input", {
|
|
291
|
+
type: "text",
|
|
292
|
+
name: "code",
|
|
293
|
+
placeholder: copy.input_code,
|
|
294
|
+
"aria-label": "6-digit verification code",
|
|
295
|
+
autoComplete: "one-time-code",
|
|
296
|
+
inputMode: "numeric",
|
|
297
|
+
maxLength: 6,
|
|
298
|
+
minLength: 6,
|
|
299
|
+
"data-component": "input",
|
|
300
|
+
pattern: "[0-9]{6}",
|
|
301
|
+
required: true
|
|
302
|
+
}),
|
|
303
|
+
/* @__PURE__ */ jsx("button", {
|
|
304
|
+
"data-component": "button",
|
|
305
|
+
type: "submit",
|
|
306
|
+
children: copy.button_continue
|
|
307
|
+
})
|
|
308
|
+
]
|
|
309
|
+
}), /* @__PURE__ */ jsxs("form", {
|
|
310
|
+
method: "post",
|
|
311
|
+
children: [
|
|
312
|
+
/* @__PURE__ */ jsx("input", {
|
|
313
|
+
name: "action",
|
|
314
|
+
type: "hidden",
|
|
315
|
+
value: "code"
|
|
316
|
+
}),
|
|
317
|
+
/* @__PURE__ */ jsx("input", {
|
|
318
|
+
name: "email",
|
|
319
|
+
type: "hidden",
|
|
320
|
+
value: state.email
|
|
321
|
+
}),
|
|
322
|
+
/* @__PURE__ */ jsxs("div", {
|
|
323
|
+
"data-component": "form-footer",
|
|
324
|
+
children: [/* @__PURE__ */ jsxs("span", { children: [
|
|
325
|
+
copy.code_return,
|
|
326
|
+
" ",
|
|
327
|
+
/* @__PURE__ */ jsx("a", {
|
|
328
|
+
"data-component": "link",
|
|
329
|
+
href: "./authorize",
|
|
330
|
+
children: copy.login
|
|
331
|
+
})
|
|
332
|
+
] }), /* @__PURE__ */ jsx("button", {
|
|
333
|
+
type: "submit",
|
|
315
334
|
"data-component": "link",
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
335
|
+
children: copy.code_resend
|
|
336
|
+
})]
|
|
337
|
+
})
|
|
338
|
+
]
|
|
339
|
+
})] });
|
|
340
|
+
return /* @__PURE__ */ jsxs("form", {
|
|
341
|
+
"data-component": "form",
|
|
342
|
+
method: "post",
|
|
343
|
+
children: [
|
|
344
|
+
/* @__PURE__ */ jsx(FormAlert, { message: getErrorMessage(error) }),
|
|
345
|
+
/* @__PURE__ */ jsx("input", {
|
|
346
|
+
name: "action",
|
|
347
|
+
type: "hidden",
|
|
348
|
+
value: "update"
|
|
349
|
+
}),
|
|
350
|
+
/* @__PURE__ */ jsx("input", {
|
|
351
|
+
type: "password",
|
|
352
|
+
name: "password",
|
|
353
|
+
placeholder: copy.input_password,
|
|
354
|
+
value: passwordError ? "" : form?.get("password")?.toString() || "",
|
|
355
|
+
autoComplete: "new-password",
|
|
356
|
+
"data-component": "input",
|
|
357
|
+
required: true
|
|
358
|
+
}),
|
|
359
|
+
/* @__PURE__ */ jsx("input", {
|
|
360
|
+
type: "password",
|
|
361
|
+
name: "repeat",
|
|
362
|
+
placeholder: copy.input_repeat,
|
|
363
|
+
value: passwordError ? "" : form?.get("repeat")?.toString() || "",
|
|
364
|
+
autoComplete: "new-password",
|
|
365
|
+
"data-component": "input",
|
|
366
|
+
required: true
|
|
367
|
+
}),
|
|
368
|
+
/* @__PURE__ */ jsx("button", {
|
|
369
|
+
"data-component": "button",
|
|
320
370
|
type: "submit",
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
]
|
|
326
|
-
})] }) : /* @__PURE__ */ jsxs("form", {
|
|
327
|
-
"data-component": "form",
|
|
328
|
-
method: "post",
|
|
329
|
-
children: [
|
|
330
|
-
/* @__PURE__ */ jsx(FormAlert, { message: getErrorMessage(error) }),
|
|
331
|
-
/* @__PURE__ */ jsx("input", {
|
|
332
|
-
name: "action",
|
|
333
|
-
type: "hidden",
|
|
334
|
-
value: "update"
|
|
335
|
-
}),
|
|
336
|
-
/* @__PURE__ */ jsx("input", {
|
|
337
|
-
type: "password",
|
|
338
|
-
name: "password",
|
|
339
|
-
placeholder: copy.input_password,
|
|
340
|
-
value: passwordError ? "" : form?.get("password")?.toString() || "",
|
|
341
|
-
autoComplete: "new-password",
|
|
342
|
-
"data-component": "input",
|
|
343
|
-
required: true
|
|
344
|
-
}),
|
|
345
|
-
/* @__PURE__ */ jsx("input", {
|
|
346
|
-
type: "password",
|
|
347
|
-
name: "repeat",
|
|
348
|
-
placeholder: copy.input_repeat,
|
|
349
|
-
value: passwordError ? "" : form?.get("repeat")?.toString() || "",
|
|
350
|
-
autoComplete: "new-password",
|
|
351
|
-
"data-component": "input",
|
|
352
|
-
required: true
|
|
353
|
-
}),
|
|
354
|
-
/* @__PURE__ */ jsx("button", {
|
|
355
|
-
"data-component": "button",
|
|
356
|
-
type: "submit",
|
|
357
|
-
children: copy.button_continue
|
|
358
|
-
})
|
|
359
|
-
]
|
|
371
|
+
children: copy.button_continue
|
|
372
|
+
})
|
|
373
|
+
]
|
|
374
|
+
});
|
|
360
375
|
}) });
|
|
361
376
|
};
|
|
362
377
|
return {
|
package/dist/util.d.mts
CHANGED
|
@@ -68,5 +68,28 @@ declare const isDomainMatch: (a: string, b: string) => boolean;
|
|
|
68
68
|
* ```
|
|
69
69
|
*/
|
|
70
70
|
declare const lazy: <T>(fn: () => T) => (() => T);
|
|
71
|
+
/**
|
|
72
|
+
* Utility function to immediately invoke a function and return its result.
|
|
73
|
+
* Useful for complex conditional rendering logic in JSX/TSX where you want
|
|
74
|
+
* to use if/else statements instead of ternary operators.
|
|
75
|
+
*
|
|
76
|
+
* @template T - The return type of the function
|
|
77
|
+
* @param fn - Function to execute immediately
|
|
78
|
+
* @returns The result of executing the function
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```tsx
|
|
82
|
+
* return (
|
|
83
|
+
* <div>
|
|
84
|
+
* {run(() => {
|
|
85
|
+
* if (state === "loading") return <Spinner />
|
|
86
|
+
* if (state === "error") return <Error />
|
|
87
|
+
* return <Content />
|
|
88
|
+
* })}
|
|
89
|
+
* </div>
|
|
90
|
+
* )
|
|
91
|
+
* ```
|
|
92
|
+
*/
|
|
93
|
+
declare const run: <T>(fn: () => T) => T;
|
|
71
94
|
//#endregion
|
|
72
|
-
export { Prettify, getRelativeUrl, isDomainMatch, lazy };
|
|
95
|
+
export { Prettify, getRelativeUrl, isDomainMatch, lazy, run };
|
package/dist/util.mjs
CHANGED
|
@@ -100,6 +100,29 @@ const lazy = (fn) => {
|
|
|
100
100
|
return value;
|
|
101
101
|
};
|
|
102
102
|
};
|
|
103
|
+
/**
|
|
104
|
+
* Utility function to immediately invoke a function and return its result.
|
|
105
|
+
* Useful for complex conditional rendering logic in JSX/TSX where you want
|
|
106
|
+
* to use if/else statements instead of ternary operators.
|
|
107
|
+
*
|
|
108
|
+
* @template T - The return type of the function
|
|
109
|
+
* @param fn - Function to execute immediately
|
|
110
|
+
* @returns The result of executing the function
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```tsx
|
|
114
|
+
* return (
|
|
115
|
+
* <div>
|
|
116
|
+
* {run(() => {
|
|
117
|
+
* if (state === "loading") return <Spinner />
|
|
118
|
+
* if (state === "error") return <Error />
|
|
119
|
+
* return <Content />
|
|
120
|
+
* })}
|
|
121
|
+
* </div>
|
|
122
|
+
* )
|
|
123
|
+
* ```
|
|
124
|
+
*/
|
|
125
|
+
const run = (fn) => fn();
|
|
103
126
|
|
|
104
127
|
//#endregion
|
|
105
|
-
export { getRelativeUrl, isDomainMatch, lazy };
|
|
128
|
+
export { getRelativeUrl, isDomainMatch, lazy, run };
|