@cosmicdrift/kumiko-samples 0.341.0 → 0.342.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.
Files changed (29) hide show
  1. package/package.json +1 -1
  2. package/packages/bundled-features/package.json +1 -1
  3. package/packages/bundled-features/src/audit/constants.ts +5 -0
  4. package/packages/bundled-features/src/audit/feature.ts +47 -6
  5. package/packages/bundled-features/src/audit/run-escape-hatch-retention.ts +53 -0
  6. package/packages/bundled-features/src/auth-email-password/bootstrap.ts +18 -2
  7. package/packages/bundled-features/src/auth-email-password/changes.json +12 -0
  8. package/packages/bundled-features/src/auth-email-password/web/auth-form-primitives.tsx +24 -6
  9. package/packages/bundled-features/src/auth-email-password/web/confirm-account-unlock-screen.tsx +12 -18
  10. package/packages/bundled-features/src/auth-email-password/web/forgot-password-screen.tsx +4 -4
  11. package/packages/bundled-features/src/auth-email-password/web/invite-accept-screen.tsx +65 -69
  12. package/packages/bundled-features/src/auth-email-password/web/login-screen.tsx +93 -95
  13. package/packages/bundled-features/src/auth-email-password/web/request-account-unlock-screen.tsx +4 -4
  14. package/packages/bundled-features/src/auth-email-password/web/reset-password-screen.tsx +8 -10
  15. package/packages/bundled-features/src/auth-email-password/web/session-bootstrap-error.tsx +13 -15
  16. package/packages/bundled-features/src/auth-email-password/web/signup-complete-screen.tsx +43 -49
  17. package/packages/bundled-features/src/auth-email-password/web/signup-screen.tsx +4 -4
  18. package/packages/bundled-features/src/auth-email-password/web/verify-email-screen.tsx +12 -18
  19. package/packages/bundled-features/src/auth-mfa/web/mfa-setup-preauth-screen.tsx +83 -85
  20. package/packages/bundled-features/src/auth-mfa/web/mfa-verify-screen.tsx +24 -26
  21. package/packages/bundled-features/src/channel-email/changes.json +6 -0
  22. package/packages/bundled-features/src/channel-email/email-channel.ts +8 -1
  23. package/packages/bundled-features/src/channel-email/pii-guard.ts +6 -5
  24. package/packages/bundled-features/src/channel-email/smtp-transport.ts +27 -1
  25. package/packages/bundled-features/src/channel-email/types.ts +4 -0
  26. package/packages/bundled-features/src/waitlist/changes.json +6 -0
  27. package/packages/bundled-features/src/waitlist/handlers/submit.write.ts +1 -1
  28. package/packages/bundled-features/src/waitlist/options.ts +3 -2
  29. package/samples/apps/use-all-bundled/feature-manifest.json +27 -2
@@ -195,104 +195,102 @@ export function LoginScreen({
195
195
 
196
196
  return (
197
197
  <AuthCard title={effectiveTitle} subtitle={subtitle}>
198
- <div className="p-6 pt-0 flex flex-col gap-4">
199
- <Form onSubmit={onSubmit}>
200
- <Field id="login-email" label={t("auth.login.email")} required>
201
- <Input
202
- kind="email"
203
- id="login-email"
204
- name="login-email"
205
- value={email}
206
- onChange={setEmail}
207
- disabled={submitting}
208
- required
209
- autoComplete="email"
210
- />
211
- </Field>
212
- <Field id="login-password" label={t("auth.login.password")} required>
213
- <Input
214
- kind="password"
215
- id="login-password"
216
- name="login-password"
217
- value={password}
218
- onChange={setPassword}
219
- disabled={submitting}
220
- required
221
- autoComplete="current-password"
222
- />
223
- </Field>
224
- {session.signedOutReason === "session-ended" && (
225
- <Banner variant="info">{t("auth.login.sessionEnded")}</Banner>
226
- )}
227
- {resendStatus.kind === "success" ? (
228
- <Banner variant="info">{t("auth.login.resendSuccess")}</Banner>
229
- ) : error !== null ? (
230
- <Banner variant="error">
231
- <div className="flex flex-col gap-1">
232
- <span>
233
- {(() => {
234
- const { key, params } = reasonToKey(error);
235
- return t(key, params);
236
- })()}
237
- </span>
238
- {error.reason === "email_not_verified" &&
239
- email.trim().length > 0 &&
240
- email === failedLoginEmail && (
241
- <span className="self-start">
242
- <Button
243
- variant="link"
244
- onClick={() => void onResend()}
245
- disabled={resendStatus.kind === "sending"}
246
- >
247
- {resendStatus.kind === "sending"
248
- ? t("auth.login.submitting")
249
- : t("auth.login.resendVerification")}
250
- </Button>
251
- </span>
252
- )}
253
- {error.reason === "account_locked" && unlockAccountHref !== undefined && (
198
+ <Form onSubmit={onSubmit}>
199
+ <Field id="login-email" label={t("auth.login.email")} required>
200
+ <Input
201
+ kind="email"
202
+ id="login-email"
203
+ name="login-email"
204
+ value={email}
205
+ onChange={setEmail}
206
+ disabled={submitting}
207
+ required
208
+ autoComplete="email"
209
+ />
210
+ </Field>
211
+ <Field id="login-password" label={t("auth.login.password")} required>
212
+ <Input
213
+ kind="password"
214
+ id="login-password"
215
+ name="login-password"
216
+ value={password}
217
+ onChange={setPassword}
218
+ disabled={submitting}
219
+ required
220
+ autoComplete="current-password"
221
+ />
222
+ </Field>
223
+ {session.signedOutReason === "session-ended" && (
224
+ <Banner variant="info">{t("auth.login.sessionEnded")}</Banner>
225
+ )}
226
+ {resendStatus.kind === "success" ? (
227
+ <Banner variant="info">{t("auth.login.resendSuccess")}</Banner>
228
+ ) : error !== null ? (
229
+ <Banner variant="error">
230
+ <div className="flex flex-col gap-1">
231
+ <span>
232
+ {(() => {
233
+ const { key, params } = reasonToKey(error);
234
+ return t(key, params);
235
+ })()}
236
+ </span>
237
+ {error.reason === "email_not_verified" &&
238
+ email.trim().length > 0 &&
239
+ email === failedLoginEmail && (
254
240
  <span className="self-start">
255
- <Link href={unlockAccountHref} variant="default">
256
- {t("auth.login.unlockAccount")}
257
- </Link>
241
+ <Button
242
+ variant="link"
243
+ onClick={() => void onResend()}
244
+ disabled={resendStatus.kind === "sending"}
245
+ >
246
+ {resendStatus.kind === "sending"
247
+ ? t("auth.login.submitting")
248
+ : t("auth.login.resendVerification")}
249
+ </Button>
258
250
  </span>
259
251
  )}
260
- {resendStatus.kind === "rateLimited" && (
261
- <span className="text-xs">{t("auth.login.resendRateLimited")}</span>
262
- )}
263
- {resendStatus.kind === "error" && (
264
- <span className="text-xs">{t("auth.login.resendError")}</span>
265
- )}
266
- </div>
267
- </Banner>
268
- ) : null}
269
- <Button type="submit" loading={submitting} disabled={submitting}>
270
- {submitting ? t("auth.login.submitting") : effectiveSubmit}
271
- </Button>
272
- </Form>
273
- {forgotPasswordHref !== undefined && (
274
- <Link href={forgotPasswordHref} variant="muted" className="self-center">
275
- {t("auth.login.forgotPassword")}
276
- </Link>
277
- )}
278
- {signupHref !== undefined && (
279
- <Link href={signupHref} variant="muted" className="self-center">
280
- {t("auth.signup.title")}
281
- </Link>
282
- )}
283
- {legalLinks !== undefined && legalLinks.length > 0 && (
284
- <nav
285
- data-testid="login-legal-links"
286
- className="flex items-center justify-center gap-3 pt-2 border-t border-border/50"
287
- >
288
- {legalLinks.map((link) => (
289
- <Link key={link.href} href={link.href} variant="muted" className="text-xs">
290
- {link.label}
291
- </Link>
292
- ))}
293
- </nav>
294
- )}
295
- </div>
252
+ {error.reason === "account_locked" && unlockAccountHref !== undefined && (
253
+ <span className="self-start">
254
+ <Link href={unlockAccountHref} variant="default">
255
+ {t("auth.login.unlockAccount")}
256
+ </Link>
257
+ </span>
258
+ )}
259
+ {resendStatus.kind === "rateLimited" && (
260
+ <span className="text-xs">{t("auth.login.resendRateLimited")}</span>
261
+ )}
262
+ {resendStatus.kind === "error" && (
263
+ <span className="text-xs">{t("auth.login.resendError")}</span>
264
+ )}
265
+ </div>
266
+ </Banner>
267
+ ) : null}
268
+ <Button type="submit" loading={submitting} disabled={submitting}>
269
+ {submitting ? t("auth.login.submitting") : effectiveSubmit}
270
+ </Button>
271
+ </Form>
272
+ {forgotPasswordHref !== undefined && (
273
+ <Link href={forgotPasswordHref} variant="muted" className="self-center">
274
+ {t("auth.login.forgotPassword")}
275
+ </Link>
276
+ )}
277
+ {signupHref !== undefined && (
278
+ <Link href={signupHref} variant="muted" className="self-center">
279
+ {t("auth.signup.title")}
280
+ </Link>
281
+ )}
282
+ {legalLinks !== undefined && legalLinks.length > 0 && (
283
+ <nav
284
+ data-testid="login-legal-links"
285
+ className="flex items-center justify-center gap-3 pt-2 border-t border-border/50"
286
+ >
287
+ {legalLinks.map((link) => (
288
+ <Link key={link.href} href={link.href} variant="muted" className="text-xs">
289
+ {link.label}
290
+ </Link>
291
+ ))}
292
+ </nav>
293
+ )}
296
294
  </AuthCard>
297
295
  );
298
296
  }
@@ -73,7 +73,7 @@ export function RequestAccountUnlockScreen({
73
73
  return (
74
74
  <AuthCard title={effectiveTitle} subtitle={subtitle}>
75
75
  {done ? (
76
- <div className="p-6 pt-0 flex flex-col gap-4">
76
+ <>
77
77
  <Banner variant="info">
78
78
  <p className="font-medium text-foreground">{t("auth.requestUnlock.successTitle")}</p>
79
79
  <p className="mt-1">{t("auth.requestUnlock.successBody")}</p>
@@ -81,9 +81,9 @@ export function RequestAccountUnlockScreen({
81
81
  <Link href={loginHref} variant="muted">
82
82
  {t("auth.requestUnlock.backToLogin")}
83
83
  </Link>
84
- </div>
84
+ </>
85
85
  ) : (
86
- <div className="p-6 pt-0 flex flex-col gap-4">
86
+ <>
87
87
  <p className="text-sm text-muted-foreground">{t("auth.requestUnlock.intro")}</p>
88
88
  <Form onSubmit={onSubmit}>
89
89
  <Field id="unlock-email" label={t("auth.requestUnlock.email")} required>
@@ -105,7 +105,7 @@ export function RequestAccountUnlockScreen({
105
105
  <Link href={loginHref} variant="muted" className="self-center">
106
106
  {t("auth.requestUnlock.backToLogin")}
107
107
  </Link>
108
- </div>
108
+ </>
109
109
  )}
110
110
  </AuthCard>
111
111
  );
@@ -83,12 +83,10 @@ export function ResetPasswordScreen({
83
83
  if (token === "") {
84
84
  return (
85
85
  <AuthCard title={effectiveTitle}>
86
- <div className="p-6 pt-0 flex flex-col gap-4">
87
- <p className="text-sm text-muted-foreground">{t("auth.resetPassword.missingToken")}</p>
88
- <Link href={loginHref} variant="muted">
89
- {t("auth.resetPassword.goToLogin")}
90
- </Link>
91
- </div>
86
+ <p className="text-sm text-muted-foreground">{t("auth.resetPassword.missingToken")}</p>
87
+ <Link href={loginHref} variant="muted">
88
+ {t("auth.resetPassword.goToLogin")}
89
+ </Link>
92
90
  </AuthCard>
93
91
  );
94
92
  }
@@ -96,7 +94,7 @@ export function ResetPasswordScreen({
96
94
  return (
97
95
  <AuthCard title={effectiveTitle}>
98
96
  {done ? (
99
- <div className="p-6 pt-0 flex flex-col gap-4">
97
+ <>
100
98
  <Banner variant="info">
101
99
  <p className="font-medium text-foreground">{t("auth.resetPassword.successTitle")}</p>
102
100
  <p className="mt-1">{t("auth.resetPassword.successBody")}</p>
@@ -104,9 +102,9 @@ export function ResetPasswordScreen({
104
102
  <Link href={loginHref} variant="button">
105
103
  {t("auth.resetPassword.goToLogin")}
106
104
  </Link>
107
- </div>
105
+ </>
108
106
  ) : (
109
- <div className="p-6 pt-0 flex flex-col gap-4">
107
+ <>
110
108
  <p className="text-sm text-muted-foreground">{t("auth.resetPassword.intro")}</p>
111
109
  <Form onSubmit={onSubmit}>
112
110
  <Field id="reset-new-password" label={t("auth.resetPassword.newPassword")} required>
@@ -142,7 +140,7 @@ export function ResetPasswordScreen({
142
140
  {submitting ? t("auth.resetPassword.submitting") : t("auth.resetPassword.submit")}
143
141
  </Button>
144
142
  </Form>
145
- </div>
143
+ </>
146
144
  )}
147
145
  </AuthCard>
148
146
  );
@@ -44,22 +44,20 @@ export function SessionBootstrapErrorScreen({
44
44
 
45
45
  return (
46
46
  <div data-testid="session-bootstrap-error" data-http-status={failure.httpStatus ?? "network"}>
47
- <AuthCard title={t("auth.sessionBootstrap.errorTitle")}>
48
- <div className="flex flex-col gap-4 px-6 pb-4">
49
- <Banner variant="error">
50
- {failure.httpStatus === 429
51
- ? t("auth.sessionBootstrap.rateLimitedBody")
52
- : t("auth.sessionBootstrap.errorBody")}
53
- </Banner>
54
- <Button variant="primary" onClick={handleRetry} disabled={retrying}>
55
- {retrying ? t("auth.sessionBootstrap.retrying") : t("auth.sessionBootstrap.retry")}
47
+ <AuthCard title={t("auth.sessionBootstrap.errorTitle")} bodyClassName="pb-4">
48
+ <Banner variant="error">
49
+ {failure.httpStatus === 429
50
+ ? t("auth.sessionBootstrap.rateLimitedBody")
51
+ : t("auth.sessionBootstrap.errorBody")}
52
+ </Banner>
53
+ <Button variant="primary" onClick={handleRetry} disabled={retrying}>
54
+ {retrying ? t("auth.sessionBootstrap.retrying") : t("auth.sessionBootstrap.retry")}
55
+ </Button>
56
+ {onSignOut !== undefined && (
57
+ <Button variant="secondary" onClick={() => void onSignOut()} disabled={retrying}>
58
+ {t("auth.sessionBootstrap.signOut")}
56
59
  </Button>
57
- {onSignOut !== undefined && (
58
- <Button variant="secondary" onClick={() => void onSignOut()} disabled={retrying}>
59
- {t("auth.sessionBootstrap.signOut")}
60
- </Button>
61
- )}
62
- </div>
60
+ )}
63
61
  </AuthCard>
64
62
  </div>
65
63
  );
@@ -112,12 +112,10 @@ export function SignupCompleteScreen({
112
112
  if (token === "") {
113
113
  return (
114
114
  <AuthCard title={effectiveTitle}>
115
- <div className="p-6 pt-0 flex flex-col gap-4">
116
- <p className="text-sm text-muted-foreground">{t("auth.signupComplete.missingToken")}</p>
117
- <Link href={loginHref} variant="muted">
118
- {t("auth.signup.haveAccount")}
119
- </Link>
120
- </div>
115
+ <p className="text-sm text-muted-foreground">{t("auth.signupComplete.missingToken")}</p>
116
+ <Link href={loginHref} variant="muted">
117
+ {t("auth.signup.haveAccount")}
118
+ </Link>
121
119
  </AuthCard>
122
120
  );
123
121
  }
@@ -125,57 +123,53 @@ export function SignupCompleteScreen({
125
123
  if (continueHref !== null) {
126
124
  return (
127
125
  <AuthCard title={effectiveTitle}>
128
- <div className="p-6 pt-0 flex flex-col gap-4">
129
- <p className="text-sm text-muted-foreground" role="status">
130
- {t("auth.signupComplete.activated")}
131
- </p>
132
- <Link href={continueHref} variant="button">
133
- {t("auth.signupComplete.continue")}
134
- </Link>
135
- </div>
126
+ <p className="text-sm text-muted-foreground" role="status">
127
+ {t("auth.signupComplete.activated")}
128
+ </p>
129
+ <Link href={continueHref} variant="button">
130
+ {t("auth.signupComplete.continue")}
131
+ </Link>
136
132
  </AuthCard>
137
133
  );
138
134
  }
139
135
 
140
136
  return (
141
137
  <AuthCard title={effectiveTitle}>
142
- <div className="p-6 pt-0 flex flex-col gap-4">
143
- <p className="text-sm text-muted-foreground">{t("auth.signupComplete.intro")}</p>
144
- <Form onSubmit={onSubmit}>
145
- <Field id="signup-password" label={t("auth.signupComplete.password")} required>
146
- <Input
147
- kind="password"
148
- id="signup-password"
149
- name="signup-password"
150
- value={password}
151
- onChange={setPassword}
152
- disabled={submitting}
153
- required
154
- autoComplete="new-password"
155
- />
156
- </Field>
157
- <Field
138
+ <p className="text-sm text-muted-foreground">{t("auth.signupComplete.intro")}</p>
139
+ <Form onSubmit={onSubmit}>
140
+ <Field id="signup-password" label={t("auth.signupComplete.password")} required>
141
+ <Input
142
+ kind="password"
143
+ id="signup-password"
144
+ name="signup-password"
145
+ value={password}
146
+ onChange={setPassword}
147
+ disabled={submitting}
148
+ required
149
+ autoComplete="new-password"
150
+ />
151
+ </Field>
152
+ <Field
153
+ id="signup-confirm-password"
154
+ label={t("auth.signupComplete.confirmPassword")}
155
+ required
156
+ >
157
+ <Input
158
+ kind="password"
158
159
  id="signup-confirm-password"
159
- label={t("auth.signupComplete.confirmPassword")}
160
+ name="signup-confirm-password"
161
+ value={confirmPassword}
162
+ onChange={setConfirmPassword}
163
+ disabled={submitting}
160
164
  required
161
- >
162
- <Input
163
- kind="password"
164
- id="signup-confirm-password"
165
- name="signup-confirm-password"
166
- value={confirmPassword}
167
- onChange={setConfirmPassword}
168
- disabled={submitting}
169
- required
170
- autoComplete="new-password"
171
- />
172
- </Field>
173
- {error !== null && <Banner variant="error">{error}</Banner>}
174
- <Button type="submit" loading={submitting} disabled={submitting}>
175
- {submitting ? t("auth.signupComplete.submitting") : t("auth.signupComplete.submit")}
176
- </Button>
177
- </Form>
178
- </div>
165
+ autoComplete="new-password"
166
+ />
167
+ </Field>
168
+ {error !== null && <Banner variant="error">{error}</Banner>}
169
+ <Button type="submit" loading={submitting} disabled={submitting}>
170
+ {submitting ? t("auth.signupComplete.submitting") : t("auth.signupComplete.submit")}
171
+ </Button>
172
+ </Form>
179
173
  </AuthCard>
180
174
  );
181
175
  }
@@ -85,7 +85,7 @@ export function SignupScreen({
85
85
  return (
86
86
  <AuthCard title={effectiveTitle} subtitle={subtitle}>
87
87
  {done ? (
88
- <div className="p-6 pt-0 flex flex-col gap-4">
88
+ <>
89
89
  <Banner variant="info">
90
90
  <p className="font-medium text-foreground">{t("auth.signup.successTitle")}</p>
91
91
  <p className="mt-1">{t("auth.signup.successBody")}</p>
@@ -96,9 +96,9 @@ export function SignupScreen({
96
96
  <Link href={loginHref} variant="muted">
97
97
  {t("auth.signup.haveAccount")}
98
98
  </Link>
99
- </div>
99
+ </>
100
100
  ) : (
101
- <div className="p-6 pt-0 flex flex-col gap-4">
101
+ <>
102
102
  <p className="text-sm text-muted-foreground">{t("auth.signup.intro")}</p>
103
103
  <Form onSubmit={onSubmit}>
104
104
  <Field id="signup-email" label={t("auth.signup.email")} required>
@@ -121,7 +121,7 @@ export function SignupScreen({
121
121
  <Link href={loginHref} variant="muted" className="self-center">
122
122
  {t("auth.signup.haveAccount")}
123
123
  </Link>
124
- </div>
124
+ </>
125
125
  )}
126
126
  </AuthCard>
127
127
  );
@@ -50,12 +50,10 @@ export function VerifyEmailScreen({
50
50
  if (status === "missing-token") {
51
51
  return (
52
52
  <AuthCard title={title ?? t("auth.verifyEmail.errorTitle")}>
53
- <div className="p-6 pt-0 flex flex-col gap-4">
54
- <p className="text-sm text-muted-foreground">{t("auth.verifyEmail.missingToken")}</p>
55
- <Link href={loginHref} variant="muted">
56
- {t("auth.verifyEmail.goToLogin")}
57
- </Link>
58
- </div>
53
+ <p className="text-sm text-muted-foreground">{t("auth.verifyEmail.missingToken")}</p>
54
+ <Link href={loginHref} variant="muted">
55
+ {t("auth.verifyEmail.goToLogin")}
56
+ </Link>
59
57
  </AuthCard>
60
58
  );
61
59
  }
@@ -75,12 +73,10 @@ export function VerifyEmailScreen({
75
73
  if (status === "success") {
76
74
  return (
77
75
  <AuthCard title={title ?? t("auth.verifyEmail.successTitle")}>
78
- <div className="p-6 pt-0 flex flex-col gap-4">
79
- <p className="text-sm text-muted-foreground">{t("auth.verifyEmail.successBody")}</p>
80
- <Link href={loginHref} variant="button">
81
- {t("auth.verifyEmail.goToLogin")}
82
- </Link>
83
- </div>
76
+ <p className="text-sm text-muted-foreground">{t("auth.verifyEmail.successBody")}</p>
77
+ <Link href={loginHref} variant="button">
78
+ {t("auth.verifyEmail.goToLogin")}
79
+ </Link>
84
80
  </AuthCard>
85
81
  );
86
82
  }
@@ -88,12 +84,10 @@ export function VerifyEmailScreen({
88
84
  // status === "error"
89
85
  return (
90
86
  <AuthCard title={title ?? t("auth.verifyEmail.errorTitle")}>
91
- <div className="p-6 pt-0 flex flex-col gap-4">
92
- <p className="text-sm text-muted-foreground">{t("auth.verifyEmail.errorBody")}</p>
93
- <Link href={loginHref} variant="muted">
94
- {t("auth.verifyEmail.goToLogin")}
95
- </Link>
96
- </div>
87
+ <p className="text-sm text-muted-foreground">{t("auth.verifyEmail.errorBody")}</p>
88
+ <Link href={loginHref} variant="muted">
89
+ {t("auth.verifyEmail.goToLogin")}
90
+ </Link>
97
91
  </AuthCard>
98
92
  );
99
93
  }