create-nevela 0.3.0 → 0.4.2

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 (68) hide show
  1. package/README.md +3 -1
  2. package/banner.mjs +50 -0
  3. package/index.mjs +144 -27
  4. package/net.mjs +55 -0
  5. package/nevela.mjs +8 -0
  6. package/package.json +5 -2
  7. package/packagist.mjs +14 -21
  8. package/selfupdate.mjs +9 -12
  9. package/template/laravel/composer.json +2 -1
  10. package/template/laravel/config/nevela.php +66 -1
  11. package/template/laravel/database/migrations/2026_10_05_000000_create_nevela_auth_tables.php +66 -0
  12. package/template/laravel/src/Auth/Account.php +140 -0
  13. package/template/laravel/src/Auth/AuthMail.php +77 -0
  14. package/template/laravel/src/Auth/Challenges.php +135 -0
  15. package/template/laravel/src/Auth/Passkeys.php +206 -0
  16. package/template/laravel/src/Auth/Totp.php +106 -0
  17. package/template/laravel/src/Auth/Web.php +75 -0
  18. package/template/laravel/src/Console/GenerateCommand.php +1 -0
  19. package/template/laravel/src/Console/UpdateCommand.php +56 -4
  20. package/template/laravel/src/Generator/ResourceGenerator.php +60 -0
  21. package/template/laravel/src/Http/Auth/AccountController.php +252 -0
  22. package/template/laravel/src/Http/Auth/Answers.php +33 -0
  23. package/template/laravel/src/Http/Auth/SecurityController.php +188 -0
  24. package/template/laravel/src/Http/Auth/SignInController.php +302 -0
  25. package/template/laravel/src/Http/TokenController.php +19 -37
  26. package/template/laravel/src/Http/UploadController.php +24 -12
  27. package/template/laravel/src/Models/Passkey.php +38 -0
  28. package/template/laravel/src/Models/TwoFactor.php +64 -0
  29. package/template/laravel/src/Nevela.php +1 -1
  30. package/template/laravel/src/NevelaServiceProvider.php +72 -4
  31. package/template/web/app/api/[...path]/route.ts +5 -1
  32. package/template/web/app/api/auth/[...action]/route.ts +196 -0
  33. package/template/web/app/auth-actions.ts +4 -43
  34. package/template/web/app/dashboard/account/layout.tsx +29 -0
  35. package/template/web/app/dashboard/account/page.tsx +19 -0
  36. package/template/web/app/dashboard/account/password/page.tsx +10 -0
  37. package/template/web/app/dashboard/account/security/page.tsx +19 -0
  38. package/template/web/app/dashboard/account/sessions/page.tsx +9 -0
  39. package/template/web/app/dashboard/layout.tsx +2 -1
  40. package/template/web/app/forgot-password/page.tsx +22 -0
  41. package/template/web/app/reset-password/page.tsx +31 -0
  42. package/template/web/app/sign-in/page.tsx +34 -4
  43. package/template/web/app/sign-up/page.tsx +43 -0
  44. package/template/web/app/two-factor/page.tsx +34 -0
  45. package/template/web/app/verify-email/page.tsx +33 -0
  46. package/template/web/components/account/account-nav.tsx +39 -0
  47. package/template/web/components/account/sections.tsx +573 -0
  48. package/template/web/components/auth/otp-input.tsx +95 -0
  49. package/template/web/components/auth/password-field.tsx +116 -0
  50. package/template/web/components/auth/password-forms.tsx +76 -0
  51. package/template/web/components/auth/provider-icons.tsx +39 -0
  52. package/template/web/components/auth/sign-in-flow.tsx +307 -0
  53. package/template/web/components/auth/sign-up-form.tsx +93 -0
  54. package/template/web/components/auth/social-buttons.tsx +56 -0
  55. package/template/web/components/auth/two-factor-form.tsx +144 -0
  56. package/template/web/components/auth/use-resend.ts +44 -0
  57. package/template/web/components/auth/verify-email-form.tsx +147 -0
  58. package/template/web/components/dashboard/dashboard-sidebar.tsx +2 -2
  59. package/template/web/components/dashboard/user-menu.tsx +33 -1
  60. package/template/web/lib/auth-client.ts +279 -0
  61. package/template/web/lib/auth-config.ts +43 -0
  62. package/template/web/lib/laravel.ts +26 -1
  63. package/template/web/lib/password-rules.ts +167 -0
  64. package/template/web/lib/second-step.ts +35 -0
  65. package/template/web/lib/session.ts +12 -4
  66. package/template/web/package.json +3 -2
  67. package/tool.mjs +45 -0
  68. package/template/web/components/auth/sign-in-form.tsx +0 -50
package/tool.mjs CHANGED
@@ -54,5 +54,50 @@ export function globalInstall(dir, exists = fs.existsSync) {
54
54
  return { manager: 'npm', command: INSTALL.npm };
55
55
  }
56
56
 
57
+ /**
58
+ * Every folder on the PATH that holds a nevela command, in the order the shell looks.
59
+ *
60
+ * More than one means it was installed more than once, say with npm and with pnpm. The
61
+ * shell runs the first and never mentions the rest, so updating one of the others changes
62
+ * nothing you can see: `npm install -g create-nevela@latest` succeeds and `nevela` is
63
+ * still the old one.
64
+ *
65
+ * @param {Record<string, string | undefined>} env
66
+ * @param {object} [system] Replaceable in tests
67
+ */
68
+ export function copiesOnPath(env = process.env, { exists = fs.existsSync, real = fs.realpathSync, windows = process.platform === 'win32' } = {}) {
69
+ const names = windows ? ['nevela.cmd', 'nevela.exe', 'nevela.ps1', 'nevela'] : ['nevela'];
70
+ const separator = windows ? '\\' : '/';
71
+ const found = [];
72
+ const seen = new Set();
73
+ for (const entry of String(env.PATH ?? env.Path ?? '').split(windows ? ';' : ':')) {
74
+ const dir = entry.trim().replace(/^"|"$/g, '').replace(/[\\/]+$/, '');
75
+ if (!dir) continue;
76
+ const file = names.map((name) => `${dir}${separator}${name}`).find((candidate) => exists(candidate));
77
+ if (!file) continue;
78
+ // The same folder listed twice, or two names for one folder (/bin and /usr/bin), is one copy.
79
+ let target = file;
80
+ try {
81
+ target = real(file);
82
+ } catch {
83
+ // Left as it is.
84
+ }
85
+ const key = windows ? target.toLowerCase() : target;
86
+ if (seen.has(key)) continue;
87
+ seen.add(key);
88
+ found.push(dir);
89
+ }
90
+ return found;
91
+ }
92
+
93
+ /** What removes the copy whose command is in `dir`, judged by the folder. */
94
+ export function removeCommand(dir) {
95
+ const where = dir.replace(/\\/g, '/').toLowerCase();
96
+ if (/\/pnpm(\/|$)/.test(where)) return 'pnpm remove -g create-nevela';
97
+ if (/\/\.bun\//.test(where)) return 'bun remove -g create-nevela';
98
+ if (/\/yarn\/|\/\.yarn\//.test(where)) return 'yarn global remove create-nevela';
99
+ return 'npm uninstall -g create-nevela';
100
+ }
101
+
57
102
  /** What to type to get the `nevela` command, for someone who doesn't have it yet. */
58
103
  export const INSTALL_COMMAND = INSTALL.npm.join(' ').replace('@latest', '');
@@ -1,50 +0,0 @@
1
- "use client";
2
-
3
- import { useState } from "react";
4
- import { signInAction } from "@/app/auth-actions";
5
- import { AuthInput, AuthLabel, FormMessage, PrimaryButton } from "./ui";
6
-
7
- /** Email and password, checked by Laravel. */
8
- export function SignInForm({ next, initialError }: { next: string; initialError?: string | null }) {
9
- const [email, setEmail] = useState("");
10
- const [password, setPassword] = useState("");
11
- const [error, setError] = useState<string | null>(initialError ?? null);
12
- const [pending, setPending] = useState(false);
13
-
14
- const submit = async (event: React.FormEvent) => {
15
- event.preventDefault();
16
- setPending(true);
17
- setError(null);
18
- const result = await signInAction(email, password);
19
- if (result.ok) {
20
- window.location.href = next;
21
- return;
22
- }
23
- setError(result.error);
24
- setPending(false);
25
- };
26
-
27
- return (
28
- <form onSubmit={submit} className="flex flex-col gap-4">
29
- {error && <FormMessage tone="error">{error}</FormMessage>}
30
- <div className="flex flex-col gap-1.5">
31
- <AuthLabel htmlFor="email">Email</AuthLabel>
32
- <AuthInput id="email" type="email" autoComplete="email" required autoFocus value={email} onChange={(event) => setEmail(event.target.value)} />
33
- </div>
34
- <div className="flex flex-col gap-1.5">
35
- <AuthLabel htmlFor="password">Password</AuthLabel>
36
- <AuthInput
37
- id="password"
38
- type="password"
39
- autoComplete="current-password"
40
- required
41
- value={password}
42
- onChange={(event) => setPassword(event.target.value)}
43
- />
44
- </div>
45
- <PrimaryButton type="submit" pending={pending}>
46
- Sign in
47
- </PrimaryButton>
48
- </form>
49
- );
50
- }