@poly-x/next 0.1.0-alpha.6 → 0.1.0-alpha.8

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/server.cjs CHANGED
@@ -165,6 +165,50 @@ function createAuthHandlers(handlersConfig = {}) {
165
165
  });
166
166
  return attempt(newPassword);
167
167
  },
168
+ async forgotPassword(request) {
169
+ const config = resolveNextConfig(handlersConfig);
170
+ let payload;
171
+ try {
172
+ payload = await request.json();
173
+ } catch {
174
+ return Response.json({ status: "invalid_request" }, { status: 400 });
175
+ }
176
+ const email = typeof payload.email === "string" ? payload.email.trim() : "";
177
+ if (!email) return Response.json({ status: "invalid_request" }, { status: 400 });
178
+ try {
179
+ await buildServerAuthClient(config).requestPasswordReset({ email });
180
+ } catch {}
181
+ return Response.json({ status: "accepted" });
182
+ },
183
+ async resetPassword(request) {
184
+ const config = resolveNextConfig(handlersConfig);
185
+ let payload;
186
+ try {
187
+ payload = await request.json();
188
+ } catch {
189
+ return Response.json({ status: "invalid_request" }, { status: 400 });
190
+ }
191
+ const token = typeof payload.token === "string" ? payload.token : "";
192
+ const newPassword = typeof payload.newPassword === "string" ? payload.newPassword : "";
193
+ const confirmPassword = typeof payload.confirmPassword === "string" ? payload.confirmPassword : void 0;
194
+ if (!token) return Response.json({ status: "invalid_request" }, { status: 400 });
195
+ if (newPassword.length < MIN_PASSWORD_LENGTH) return Response.json({
196
+ status: "weak_password",
197
+ minLength: MIN_PASSWORD_LENGTH
198
+ }, { status: 400 });
199
+ if (confirmPassword !== void 0 && newPassword !== confirmPassword) return Response.json({ status: "password_mismatch" }, { status: 400 });
200
+ switch (await buildServerAuthClient(config).resetPassword({
201
+ token,
202
+ newPassword
203
+ })) {
204
+ case "ok": return Response.json({ status: "success" });
205
+ case "weak_password": return Response.json({
206
+ status: "weak_password",
207
+ minLength: MIN_PASSWORD_LENGTH
208
+ }, { status: 400 });
209
+ default: return Response.json({ status: "invalid_or_expired" }, { status: 400 });
210
+ }
211
+ },
168
212
  async callback(request) {
169
213
  const config = resolveNextConfig(handlersConfig);
170
214
  const url = new URL(request.url);
package/dist/server.d.cts CHANGED
@@ -80,10 +80,40 @@ interface AuthHandlers {
80
80
  * The browser never sees the code, the tokens, or the credentials after POST.
81
81
  */
82
82
  authenticate(request: Request): Promise<Response>;
83
+ /**
84
+ * The `<ForgotPassword/>` BFF endpoint: reads `{email}`, adds this app's publishable
85
+ * key, and asks poly-auth to email a reset link server-side. Always relays a uniform
86
+ * `accepted` — it never reveals whether the account exists.
87
+ */
88
+ forgotPassword(request: Request): Promise<Response>;
89
+ /**
90
+ * The `<ResetPassword/>` BFF endpoint: reads `{token, newPassword}` and redeems the
91
+ * opaque single-use token. `success` on completion; `invalid_or_expired` otherwise.
92
+ */
93
+ resetPassword(request: Request): Promise<Response>;
83
94
  callback(request: Request): Promise<Response>;
84
95
  refresh(request: Request): Promise<Response>;
85
96
  signout(request: Request): Promise<Response>;
86
97
  }
98
+ /** The JSON the `forgotPassword` handler returns — always uniform (no enumeration). */
99
+ type ForgotPasswordResult = {
100
+ status: "accepted";
101
+ } | {
102
+ status: "invalid_request";
103
+ };
104
+ /** The JSON the `resetPassword` handler returns — the `<ResetPassword/>` form branches on `status`. */
105
+ type ResetPasswordResult = {
106
+ status: "success";
107
+ } | {
108
+ status: "weak_password";
109
+ minLength: number;
110
+ } | {
111
+ status: "password_mismatch";
112
+ } | {
113
+ status: "invalid_or_expired";
114
+ } | {
115
+ status: "invalid_request";
116
+ };
87
117
  /** The JSON the `authenticate` handler returns — the `<SignIn/>` form branches on `status`. */
88
118
  type AuthenticateResult = {
89
119
  status: "success";
@@ -109,4 +139,4 @@ type AuthenticateResult = {
109
139
  };
110
140
  declare function createAuthHandlers(handlersConfig?: AuthHandlersConfig): AuthHandlers;
111
141
  //#endregion
112
- export { type AuthContext, AuthHandlers, AuthHandlersConfig, AuthenticateResult, type CookieAdapter, type CookieSetOptions, DEFAULT_SESSION_COOKIE, type NextServerConfig, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
142
+ export { type AuthContext, AuthHandlers, AuthHandlersConfig, AuthenticateResult, type CookieAdapter, type CookieSetOptions, DEFAULT_SESSION_COOKIE, ForgotPasswordResult, type NextServerConfig, ResetPasswordResult, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
package/dist/server.d.mts CHANGED
@@ -80,10 +80,40 @@ interface AuthHandlers {
80
80
  * The browser never sees the code, the tokens, or the credentials after POST.
81
81
  */
82
82
  authenticate(request: Request): Promise<Response>;
83
+ /**
84
+ * The `<ForgotPassword/>` BFF endpoint: reads `{email}`, adds this app's publishable
85
+ * key, and asks poly-auth to email a reset link server-side. Always relays a uniform
86
+ * `accepted` — it never reveals whether the account exists.
87
+ */
88
+ forgotPassword(request: Request): Promise<Response>;
89
+ /**
90
+ * The `<ResetPassword/>` BFF endpoint: reads `{token, newPassword}` and redeems the
91
+ * opaque single-use token. `success` on completion; `invalid_or_expired` otherwise.
92
+ */
93
+ resetPassword(request: Request): Promise<Response>;
83
94
  callback(request: Request): Promise<Response>;
84
95
  refresh(request: Request): Promise<Response>;
85
96
  signout(request: Request): Promise<Response>;
86
97
  }
98
+ /** The JSON the `forgotPassword` handler returns — always uniform (no enumeration). */
99
+ type ForgotPasswordResult = {
100
+ status: "accepted";
101
+ } | {
102
+ status: "invalid_request";
103
+ };
104
+ /** The JSON the `resetPassword` handler returns — the `<ResetPassword/>` form branches on `status`. */
105
+ type ResetPasswordResult = {
106
+ status: "success";
107
+ } | {
108
+ status: "weak_password";
109
+ minLength: number;
110
+ } | {
111
+ status: "password_mismatch";
112
+ } | {
113
+ status: "invalid_or_expired";
114
+ } | {
115
+ status: "invalid_request";
116
+ };
87
117
  /** The JSON the `authenticate` handler returns — the `<SignIn/>` form branches on `status`. */
88
118
  type AuthenticateResult = {
89
119
  status: "success";
@@ -109,4 +139,4 @@ type AuthenticateResult = {
109
139
  };
110
140
  declare function createAuthHandlers(handlersConfig?: AuthHandlersConfig): AuthHandlers;
111
141
  //#endregion
112
- export { type AuthContext, AuthHandlers, AuthHandlersConfig, AuthenticateResult, type CookieAdapter, type CookieSetOptions, DEFAULT_SESSION_COOKIE, type NextServerConfig, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
142
+ export { type AuthContext, AuthHandlers, AuthHandlersConfig, AuthenticateResult, type CookieAdapter, type CookieSetOptions, DEFAULT_SESSION_COOKIE, ForgotPasswordResult, type NextServerConfig, ResetPasswordResult, ServerSession, auth, createAuthHandlers, openSession, resolveNextConfig, sealSession };
package/dist/server.mjs CHANGED
@@ -164,6 +164,50 @@ function createAuthHandlers(handlersConfig = {}) {
164
164
  });
165
165
  return attempt(newPassword);
166
166
  },
167
+ async forgotPassword(request) {
168
+ const config = resolveNextConfig(handlersConfig);
169
+ let payload;
170
+ try {
171
+ payload = await request.json();
172
+ } catch {
173
+ return Response.json({ status: "invalid_request" }, { status: 400 });
174
+ }
175
+ const email = typeof payload.email === "string" ? payload.email.trim() : "";
176
+ if (!email) return Response.json({ status: "invalid_request" }, { status: 400 });
177
+ try {
178
+ await buildServerAuthClient(config).requestPasswordReset({ email });
179
+ } catch {}
180
+ return Response.json({ status: "accepted" });
181
+ },
182
+ async resetPassword(request) {
183
+ const config = resolveNextConfig(handlersConfig);
184
+ let payload;
185
+ try {
186
+ payload = await request.json();
187
+ } catch {
188
+ return Response.json({ status: "invalid_request" }, { status: 400 });
189
+ }
190
+ const token = typeof payload.token === "string" ? payload.token : "";
191
+ const newPassword = typeof payload.newPassword === "string" ? payload.newPassword : "";
192
+ const confirmPassword = typeof payload.confirmPassword === "string" ? payload.confirmPassword : void 0;
193
+ if (!token) return Response.json({ status: "invalid_request" }, { status: 400 });
194
+ if (newPassword.length < MIN_PASSWORD_LENGTH) return Response.json({
195
+ status: "weak_password",
196
+ minLength: MIN_PASSWORD_LENGTH
197
+ }, { status: 400 });
198
+ if (confirmPassword !== void 0 && newPassword !== confirmPassword) return Response.json({ status: "password_mismatch" }, { status: 400 });
199
+ switch (await buildServerAuthClient(config).resetPassword({
200
+ token,
201
+ newPassword
202
+ })) {
203
+ case "ok": return Response.json({ status: "success" });
204
+ case "weak_password": return Response.json({
205
+ status: "weak_password",
206
+ minLength: MIN_PASSWORD_LENGTH
207
+ }, { status: 400 });
208
+ default: return Response.json({ status: "invalid_or_expired" }, { status: 400 });
209
+ }
210
+ },
167
211
  async callback(request) {
168
212
  const config = resolveNextConfig(handlersConfig);
169
213
  const url = new URL(request.url);
package/dist/styles.css CHANGED
@@ -339,6 +339,84 @@
339
339
  opacity: 0.85;
340
340
  }
341
341
 
342
+ /* "Forgot password?" link under the sign-in password field. */
343
+ .polyx-signin__forgot {
344
+ display: flex;
345
+ justify-content: flex-end;
346
+ margin-top: -0.5rem;
347
+ }
348
+
349
+ /* --- Recovery confirmation states (ForgotPassword / ResetPassword) ------- */
350
+
351
+ .polyx-signin__header--center {
352
+ align-items: center;
353
+ text-align: center;
354
+ }
355
+
356
+ .polyx-signin__status-icon {
357
+ display: inline-flex;
358
+ align-items: center;
359
+ justify-content: center;
360
+ width: 3rem;
361
+ height: 3rem;
362
+ margin-bottom: 0.25rem;
363
+ border-radius: 999px;
364
+ }
365
+
366
+ .polyx-signin__status-icon svg {
367
+ width: 1.5rem;
368
+ height: 1.5rem;
369
+ }
370
+
371
+ .polyx-signin__status-icon--brand {
372
+ color: var(--polyx-brand);
373
+ background: color-mix(in srgb, var(--polyx-brand) 12%, transparent);
374
+ }
375
+
376
+ .polyx-signin__status-icon--danger {
377
+ color: var(--polyx-danger);
378
+ background: var(--polyx-danger-bg);
379
+ }
380
+
381
+ /* The entered email, emphasized inside the confirmation copy. */
382
+ .polyx-signin__email {
383
+ color: var(--polyx-fg);
384
+ font-weight: 600;
385
+ word-break: break-all;
386
+ }
387
+
388
+ /* Secondary text-link actions under a confirmation screen — quieter than the CTA. */
389
+ .polyx-signin__actions {
390
+ display: flex;
391
+ flex-direction: column;
392
+ align-items: center;
393
+ gap: 0.75rem;
394
+ margin-top: 1.5rem;
395
+ }
396
+
397
+ .polyx-signin__link {
398
+ padding: 0;
399
+ font: inherit;
400
+ font-size: 0.875rem;
401
+ font-weight: 500;
402
+ color: var(--polyx-brand);
403
+ background: none;
404
+ border: none;
405
+ cursor: pointer;
406
+ text-decoration: none;
407
+ }
408
+
409
+ .polyx-signin__link:hover {
410
+ color: var(--polyx-brand-hover);
411
+ text-decoration: underline;
412
+ }
413
+
414
+ .polyx-signin__link:focus-visible {
415
+ outline: none;
416
+ border-radius: 2px;
417
+ box-shadow: 0 0 0 3px var(--polyx-ring);
418
+ }
419
+
342
420
  /* --- Workspace picker ---------------------------------------------------- */
343
421
 
344
422
  .polyx-signin__tenants {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@poly-x/next",
3
- "version": "0.1.0-alpha.6",
3
+ "version": "0.1.0-alpha.8",
4
4
  "description": "PolyX SDK for Next.js App Router - components plus server helpers",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -47,8 +47,8 @@
47
47
  "module": "./dist/index.mjs",
48
48
  "types": "./dist/index.d.cts",
49
49
  "dependencies": {
50
- "@poly-x/core": "0.1.0-alpha.6",
51
- "@poly-x/react": "0.1.0-alpha.6"
50
+ "@poly-x/core": "0.1.0-alpha.8",
51
+ "@poly-x/react": "0.1.0-alpha.8"
52
52
  },
53
53
  "peerDependencies": {
54
54
  "next": ">=14",