create-varity-app 2.0.0-beta.25 → 2.0.0-beta.26
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/README.md +1 -1
- package/dist/create.js +13 -8
- package/package.json +1 -1
- package/template/.env.example +1 -1
- package/template/KNOWN_ISSUES.md +5 -5
- package/template/README.md +2 -2
- package/template/next.config.js +1 -0
- package/template/npmrc +1 -0
- package/template/package.json +5 -1
- package/template/src/app/dashboard/layout.tsx +16 -16
- package/template/src/app/dashboard/settings/page.tsx +10 -10
- package/template/src/app/layout.tsx +6 -3
- package/template/src/app/login/page.tsx +18 -18
- package/template/src/components/providers.tsx +1 -1
- package/template/src/lib/hooks.ts +7 -7
- package/template/tsconfig.tsbuildinfo +1 -0
package/README.md
CHANGED
|
@@ -77,7 +77,7 @@ Or deploy from your AI editor with the [Varity MCP server](https://www.npmjs.com
|
|
|
77
77
|
|
|
78
78
|
---
|
|
79
79
|
|
|
80
|
-
**Part of the [Varity SDK](https://github.com/varity-labs/varity-sdk)
|
|
80
|
+
**Part of the [Varity SDK](https://github.com/varity-labs/varity-sdk).** Deploy any app, AI agent, or LLM in 60 seconds. 60-80% cheaper than AWS.
|
|
81
81
|
|
|
82
82
|
[Documentation](https://docs.varity.so) | [GitHub](https://github.com/varity-labs/varity-sdk) | [Discord](https://discord.gg/7vWsdwa2Bg)
|
|
83
83
|
|
package/dist/create.js
CHANGED
|
@@ -8,9 +8,9 @@ import { getInstallCommand } from "./utils.js";
|
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = path.dirname(__filename);
|
|
10
10
|
const WORKSPACE_DEPS = {
|
|
11
|
-
"@varity-labs/sdk": "2.0.0-beta.
|
|
12
|
-
"@varity-labs/types": "2.0.0-beta.
|
|
13
|
-
"@varity-labs/ui-kit": "2.0.0-beta.
|
|
11
|
+
"@varity-labs/sdk": "2.0.0-beta.14",
|
|
12
|
+
"@varity-labs/types": "2.0.0-beta.8",
|
|
13
|
+
"@varity-labs/ui-kit": "2.0.0-beta.15",
|
|
14
14
|
};
|
|
15
15
|
const EXCLUDED_FILES = new Set([
|
|
16
16
|
".env.local",
|
|
@@ -74,11 +74,16 @@ export async function createApp(projectName, packageManager) {
|
|
|
74
74
|
return true;
|
|
75
75
|
},
|
|
76
76
|
});
|
|
77
|
-
// Rename
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
77
|
+
// Rename dotfiles back (npm strips them from tarballs)
|
|
78
|
+
for (const [plain, dot] of [
|
|
79
|
+
["gitignore", ".gitignore"],
|
|
80
|
+
["npmrc", ".npmrc"],
|
|
81
|
+
]) {
|
|
82
|
+
const src = path.join(targetDir, plain);
|
|
83
|
+
const dst = path.join(targetDir, dot);
|
|
84
|
+
if (fs.existsSync(src) && !fs.existsSync(dst)) {
|
|
85
|
+
await fs.rename(src, dst);
|
|
86
|
+
}
|
|
82
87
|
}
|
|
83
88
|
// Rewrite package.json
|
|
84
89
|
const pkgPath = path.join(targetDir, "package.json");
|
package/package.json
CHANGED
package/template/.env.example
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# You never need to manually set these values.
|
|
8
8
|
|
|
9
9
|
# Auth (optional — dev credentials used automatically when blank)
|
|
10
|
-
|
|
10
|
+
NEXT_PUBLIC_VARITY_AUTH_APP_ID=
|
|
11
11
|
|
|
12
12
|
# Database (optional — dev database used automatically when blank)
|
|
13
13
|
NEXT_PUBLIC_VARITY_APP_TOKEN=
|
package/template/KNOWN_ISSUES.md
CHANGED
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
| Feature | Status | Notes |
|
|
12
12
|
|---------|--------|-------|
|
|
13
13
|
| Landing Page | Working | 6 sections, scroll animations, social proof |
|
|
14
|
-
| Auth (Login) | Working | Email/Google
|
|
14
|
+
| Auth (Login) | Working | Email/Google, zero-config dev credentials |
|
|
15
15
|
| Dashboard | Working | KPI cards, checklist, activity feed |
|
|
16
16
|
| Projects/Tasks/Team CRUD | Working | Full create, read, update, delete with validation |
|
|
17
17
|
| Settings | Working | 4 tabs with backend persistence via DB Proxy |
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
|
|
23
23
|
## Known Issues
|
|
24
24
|
|
|
25
|
-
### 1. Auth
|
|
26
|
-
The template uses `
|
|
25
|
+
### 1. Auth Provider Configuration
|
|
26
|
+
The template uses `useAuth()` and `NEXT_PUBLIC_VARITY_AUTH_APP_ID`. Auth works correctly out of the box — no action required.
|
|
27
27
|
|
|
28
28
|
### 2. Payments Section is Placeholder
|
|
29
29
|
Settings > Billing shows mock UI. Credit card payment integration (on/off ramp) is coming soon. Wire your own billing provider (Stripe, etc.) if needed now.
|
|
@@ -32,13 +32,13 @@ Settings > Billing shows mock UI. Credit card payment integration (on/off ramp)
|
|
|
32
32
|
All pages are statically exported. No SSR, API routes, or middleware. Do not use dynamic routes (`[id]` patterns) -- use client-side state for detail views instead.
|
|
33
33
|
|
|
34
34
|
### 4. Navigation Flash
|
|
35
|
-
Brief "Initializing Dashboard" screen when navigating between pages. Caused by `
|
|
35
|
+
Brief "Initializing Dashboard" screen when navigating between pages. Caused by `ReadyGate` re-checking auth state. Resolves in under 1 second.
|
|
36
36
|
|
|
37
37
|
### 5. Team Email Invites Are Local Only
|
|
38
38
|
No SMTP integration. Team members are added to the database but no invitation email is sent. Integrate your own email service if needed.
|
|
39
39
|
|
|
40
40
|
### 6. Sessions and Password Are Auth-Provider Managed
|
|
41
|
-
Settings > Security shows mock session data. Password changes and profile photos are managed by the auth provider
|
|
41
|
+
Settings > Security shows mock session data. Password changes and profile photos are managed by the auth provider, not the template.
|
|
42
42
|
|
|
43
43
|
## Environment
|
|
44
44
|
|
package/template/README.md
CHANGED
|
@@ -67,7 +67,7 @@ This template works immediately with **zero setup**:
|
|
|
67
67
|
|
|
68
68
|
### Instant Database
|
|
69
69
|
- ✅ Create, read, update, delete data
|
|
70
|
-
- ✅ Dev
|
|
70
|
+
- ✅ Dev key built-in
|
|
71
71
|
- ✅ Production-ready proxy
|
|
72
72
|
- ❌ No credentials needed
|
|
73
73
|
|
|
@@ -216,7 +216,7 @@ The database collection is created automatically on first use — no migrations
|
|
|
216
216
|
| Variable | Required | Notes |
|
|
217
217
|
|----------|----------|-------|
|
|
218
218
|
| `NEXT_PUBLIC_VARITY_AUTH_ID` | No | Auth provider (auto-configured) |
|
|
219
|
-
| `NEXT_PUBLIC_VARITY_APP_TOKEN` | No | Database
|
|
219
|
+
| `NEXT_PUBLIC_VARITY_APP_TOKEN` | No | Database key (auto-configured) |
|
|
220
220
|
| `NEXT_PUBLIC_VARITY_APP_ID` | No | App ID (auto-configured) |
|
|
221
221
|
|
|
222
222
|
## Deployment
|
package/template/next.config.js
CHANGED
package/template/npmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
legacy-peer-deps=true
|
package/template/package.json
CHANGED
|
@@ -26,6 +26,10 @@
|
|
|
26
26
|
"react": "^18.3.0",
|
|
27
27
|
"react-dom": "^18.3.0"
|
|
28
28
|
},
|
|
29
|
+
"overrides": {
|
|
30
|
+
"postcss": "$postcss",
|
|
31
|
+
"typescript": "^5.0.0"
|
|
32
|
+
},
|
|
29
33
|
"devDependencies": {
|
|
30
34
|
"@playwright/test": "^1.58.2",
|
|
31
35
|
"@types/node": "^20.0.0",
|
|
@@ -33,7 +37,7 @@
|
|
|
33
37
|
"@types/react-dom": "^18.3.0",
|
|
34
38
|
"autoprefixer": "^10.0.0",
|
|
35
39
|
"husky": "^9.1.7",
|
|
36
|
-
"postcss": "^8.
|
|
40
|
+
"postcss": "^8.4.31",
|
|
37
41
|
"tailwindcss": "^3.4.0",
|
|
38
42
|
"typescript": "^5.0.0"
|
|
39
43
|
}
|
|
@@ -7,22 +7,22 @@ import { useProjects, useTasks, useTeam } from '@/lib/hooks';
|
|
|
7
7
|
import { CommandPalette } from '@varity-labs/ui-kit';
|
|
8
8
|
import { Menu, X } from 'lucide-react';
|
|
9
9
|
|
|
10
|
-
// Defensively import
|
|
10
|
+
// Defensively import UI-Kit components at runtime (not statically) so the
|
|
11
11
|
// dashboard renders gracefully even if @varity-labs/ui-kit hasn't been installed
|
|
12
12
|
// yet (e.g. during local scaffolding before `npm install` completes).
|
|
13
13
|
// This is intentional — it is NOT a sign that something is broken.
|
|
14
14
|
// See KNOWN_ISSUES.md for details on this pattern.
|
|
15
15
|
let DashboardLayout: any = null;
|
|
16
|
-
let
|
|
17
|
-
let
|
|
18
|
-
let
|
|
16
|
+
let ProtectedRouteComponent: any = null;
|
|
17
|
+
let AuthProviderComponent: any = null;
|
|
18
|
+
let useAuthHook: any = null;
|
|
19
19
|
|
|
20
20
|
try {
|
|
21
21
|
const uiKit = require('@varity-labs/ui-kit');
|
|
22
22
|
DashboardLayout = uiKit.DashboardLayout;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
ProtectedRouteComponent = uiKit.ProtectedRoute;
|
|
24
|
+
AuthProviderComponent = uiKit.AuthProvider;
|
|
25
|
+
useAuthHook = uiKit.useAuth;
|
|
26
26
|
} catch {
|
|
27
27
|
// ui-kit not installed or not yet available — DashboardShell fallback renders below
|
|
28
28
|
}
|
|
@@ -117,8 +117,8 @@ function MobileNav({
|
|
|
117
117
|
|
|
118
118
|
function DashboardShell({ children }: { children: React.ReactNode }) {
|
|
119
119
|
// eslint-disable-next-line react-hooks/rules-of-hooks -- conditional on require() success, stable across renders
|
|
120
|
-
const
|
|
121
|
-
const { user, logout } =
|
|
120
|
+
const auth = useAuthHook ? useAuthHook() : { user: null, logout: async () => {} };
|
|
121
|
+
const { user, logout } = auth;
|
|
122
122
|
const pathname = usePathname();
|
|
123
123
|
const router = useRouter();
|
|
124
124
|
const isMobile = useIsMobile();
|
|
@@ -283,21 +283,21 @@ export default function DashboardRootLayout({
|
|
|
283
283
|
}: {
|
|
284
284
|
children: React.ReactNode;
|
|
285
285
|
}) {
|
|
286
|
-
// Always wrap in
|
|
287
|
-
if (!
|
|
286
|
+
// Always wrap in AuthProvider + ProtectedRoute - uses dev credentials automatically when env vars are empty
|
|
287
|
+
if (!ProtectedRouteComponent || !AuthProviderComponent) {
|
|
288
288
|
// Fallback if ui-kit package isn't installed
|
|
289
289
|
return <DashboardShell>{children}</DashboardShell>;
|
|
290
290
|
}
|
|
291
291
|
|
|
292
292
|
return (
|
|
293
|
-
<
|
|
294
|
-
appId={process.env.
|
|
293
|
+
<AuthProviderComponent
|
|
294
|
+
appId={process.env.NEXT_PUBLIC_VARITY_AUTH_APP_ID}
|
|
295
295
|
loginMethods={['email', 'google']}
|
|
296
296
|
appearance={{ theme: 'light', accentColor: '#2563EB', logo: '/logo.svg' }}
|
|
297
297
|
>
|
|
298
|
-
<
|
|
298
|
+
<ProtectedRouteComponent fallback={<RedirectToLogin />}>
|
|
299
299
|
<DashboardShell>{children}</DashboardShell>
|
|
300
|
-
</
|
|
301
|
-
</
|
|
300
|
+
</ProtectedRouteComponent>
|
|
301
|
+
</AuthProviderComponent>
|
|
302
302
|
);
|
|
303
303
|
}
|
|
@@ -37,15 +37,15 @@ import {
|
|
|
37
37
|
Key,
|
|
38
38
|
} from 'lucide-react';
|
|
39
39
|
|
|
40
|
-
const
|
|
40
|
+
const authAppId = process.env.NEXT_PUBLIC_VARITY_AUTH_APP_ID;
|
|
41
41
|
|
|
42
|
-
let
|
|
43
|
-
let
|
|
42
|
+
let UserProfileComponent: any = null;
|
|
43
|
+
let useAuthHook: any = null;
|
|
44
44
|
|
|
45
45
|
try {
|
|
46
46
|
const uiKit = require('@varity-labs/ui-kit');
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
UserProfileComponent = uiKit.UserProfile;
|
|
48
|
+
useAuthHook = uiKit.useAuth;
|
|
49
49
|
} catch {}
|
|
50
50
|
|
|
51
51
|
const TABS = [
|
|
@@ -79,7 +79,7 @@ export default function SettingsPage() {
|
|
|
79
79
|
const { settings, loading, update: updateSettings } = useUserSettings();
|
|
80
80
|
const toast = useToast();
|
|
81
81
|
// eslint-disable-next-line react-hooks/rules-of-hooks -- conditional on require() + env var, stable across renders
|
|
82
|
-
const
|
|
82
|
+
const auth = authAppId && useAuthHook ? useAuthHook() : { logout: async () => {} };
|
|
83
83
|
|
|
84
84
|
const [activeTab, setActiveTab] = useState<TabId>('general');
|
|
85
85
|
|
|
@@ -112,7 +112,7 @@ export default function SettingsPage() {
|
|
|
112
112
|
},
|
|
113
113
|
]);
|
|
114
114
|
|
|
115
|
-
// Sync profile form when
|
|
115
|
+
// Sync profile form when auth data arrives asynchronously
|
|
116
116
|
useEffect(() => {
|
|
117
117
|
if (name || email) {
|
|
118
118
|
setProfileForm({ name: name || '', email: email || '' });
|
|
@@ -557,13 +557,13 @@ export default function SettingsPage() {
|
|
|
557
557
|
</div>
|
|
558
558
|
|
|
559
559
|
{/* Account Settings */}
|
|
560
|
-
{
|
|
560
|
+
{authAppId && UserProfileComponent && (
|
|
561
561
|
<div className="rounded-xl border border-gray-200 bg-white p-6 shadow-sm">
|
|
562
562
|
<h2 className="mb-4 text-lg font-semibold text-gray-900 flex items-center gap-2">
|
|
563
563
|
<Key className="h-5 w-5" />
|
|
564
564
|
Account Settings
|
|
565
565
|
</h2>
|
|
566
|
-
<
|
|
566
|
+
<UserProfileComponent showLogoutButton={false} />
|
|
567
567
|
</div>
|
|
568
568
|
)}
|
|
569
569
|
|
|
@@ -577,7 +577,7 @@ export default function SettingsPage() {
|
|
|
577
577
|
These actions are permanent and cannot be undone.
|
|
578
578
|
</p>
|
|
579
579
|
<div className="flex flex-wrap gap-3">
|
|
580
|
-
<Button variant="danger" onClick={() =>
|
|
580
|
+
<Button variant="danger" onClick={() => auth.logout()}>
|
|
581
581
|
<LogOut className="h-4 w-4" />
|
|
582
582
|
Sign Out
|
|
583
583
|
</Button>
|
|
@@ -1,19 +1,22 @@
|
|
|
1
1
|
import type { Metadata } from 'next';
|
|
2
|
+
import { APP_NAME } from '@/lib/constants';
|
|
2
3
|
import { Providers } from '@/components/providers';
|
|
3
4
|
import './globals.css';
|
|
4
5
|
|
|
6
|
+
const appTitle = `${APP_NAME} - Dashboard`;
|
|
7
|
+
|
|
5
8
|
export const metadata: Metadata = {
|
|
6
|
-
title:
|
|
9
|
+
title: appTitle,
|
|
7
10
|
description: 'Manage projects, track tasks, and collaborate with your team.',
|
|
8
11
|
metadataBase: new URL('https://example.com'),
|
|
9
12
|
openGraph: {
|
|
10
|
-
title:
|
|
13
|
+
title: appTitle,
|
|
11
14
|
description: 'Manage projects, track tasks, and collaborate with your team.',
|
|
12
15
|
type: 'website',
|
|
13
16
|
},
|
|
14
17
|
twitter: {
|
|
15
18
|
card: 'summary',
|
|
16
|
-
title:
|
|
19
|
+
title: appTitle,
|
|
17
20
|
description: 'Manage projects, track tasks, and collaborate with your team.',
|
|
18
21
|
},
|
|
19
22
|
};
|
|
@@ -6,29 +6,29 @@ import Link from 'next/link';
|
|
|
6
6
|
import { CheckCircle } from 'lucide-react';
|
|
7
7
|
import { APP_NAME } from '@/lib/constants';
|
|
8
8
|
|
|
9
|
-
let
|
|
10
|
-
let
|
|
9
|
+
let AuthProviderComponent: any = null;
|
|
10
|
+
let useAuthHook: (() => { authenticated: boolean; ready: boolean; login: () => void }) | null = null;
|
|
11
11
|
|
|
12
12
|
try {
|
|
13
13
|
const uiKit = require('@varity-labs/ui-kit');
|
|
14
|
-
|
|
15
|
-
|
|
14
|
+
AuthProviderComponent = uiKit.AuthProvider;
|
|
15
|
+
useAuthHook = uiKit.useAuth;
|
|
16
16
|
} catch {}
|
|
17
17
|
|
|
18
18
|
function LoginContent() {
|
|
19
19
|
const router = useRouter();
|
|
20
20
|
// eslint-disable-next-line react-hooks/rules-of-hooks -- conditional on require() success, stable across renders
|
|
21
|
-
const
|
|
21
|
+
const auth = useAuthHook ? useAuthHook() : null;
|
|
22
22
|
|
|
23
23
|
useEffect(() => {
|
|
24
|
-
if (
|
|
24
|
+
if (auth?.authenticated) {
|
|
25
25
|
router.push('/dashboard');
|
|
26
26
|
}
|
|
27
|
-
}, [
|
|
27
|
+
}, [auth?.authenticated, router]);
|
|
28
28
|
|
|
29
29
|
const handleLogin = () => {
|
|
30
|
-
if (
|
|
31
|
-
|
|
30
|
+
if (auth?.login) {
|
|
31
|
+
auth.login();
|
|
32
32
|
}
|
|
33
33
|
};
|
|
34
34
|
|
|
@@ -49,15 +49,15 @@ function LoginContent() {
|
|
|
49
49
|
</div>
|
|
50
50
|
|
|
51
51
|
<div className="rounded-xl border border-gray-200 bg-white p-8 shadow-sm">
|
|
52
|
-
{
|
|
52
|
+
{auth ? (
|
|
53
53
|
<button
|
|
54
54
|
onClick={handleLogin}
|
|
55
|
-
disabled={!
|
|
55
|
+
disabled={!auth.ready || auth.authenticated}
|
|
56
56
|
className="w-full px-6 py-3 bg-primary-600 hover:bg-primary-700 disabled:bg-gray-300 disabled:cursor-not-allowed text-white font-medium rounded-lg transition-colors shadow-sm"
|
|
57
57
|
>
|
|
58
|
-
{!
|
|
58
|
+
{!auth.ready
|
|
59
59
|
? 'Loading...'
|
|
60
|
-
:
|
|
60
|
+
: auth.authenticated
|
|
61
61
|
? 'Already Signed In'
|
|
62
62
|
: 'Sign In with Email or Social'}
|
|
63
63
|
</button>
|
|
@@ -79,16 +79,16 @@ function LoginContent() {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
export default function LoginPage() {
|
|
82
|
-
// Always wrap in
|
|
83
|
-
if (
|
|
82
|
+
// Always wrap in AuthProvider - it uses dev credentials automatically when no appId is provided
|
|
83
|
+
if (AuthProviderComponent) {
|
|
84
84
|
return (
|
|
85
|
-
<
|
|
86
|
-
appId={process.env.
|
|
85
|
+
<AuthProviderComponent
|
|
86
|
+
appId={process.env.NEXT_PUBLIC_VARITY_AUTH_APP_ID}
|
|
87
87
|
loginMethods={['email', 'google']}
|
|
88
88
|
appearance={{ theme: 'light', accentColor: '#2563EB', logo: '/logo.svg' }}
|
|
89
89
|
>
|
|
90
90
|
<LoginContent />
|
|
91
|
-
</
|
|
91
|
+
</AuthProviderComponent>
|
|
92
92
|
);
|
|
93
93
|
}
|
|
94
94
|
|
|
@@ -5,7 +5,7 @@ import { ToastProvider } from '@varity-labs/ui-kit';
|
|
|
5
5
|
|
|
6
6
|
export function Providers({ children }: { children: ReactNode }) {
|
|
7
7
|
// Only ToastProvider at the global level.
|
|
8
|
-
//
|
|
8
|
+
// AuthProvider is added in dashboard/layout.tsx and login/page.tsx —
|
|
9
9
|
// the landing page loads instantly without any auth dependency.
|
|
10
10
|
return <ToastProvider>{children}</ToastProvider>;
|
|
11
11
|
}
|
|
@@ -4,18 +4,18 @@ import { useState, useEffect, useCallback } from 'react';
|
|
|
4
4
|
import { projects, tasks, teamMembers, userSettings } from './database';
|
|
5
5
|
import type { Project, Task, TeamMember, UserSettings } from '../types';
|
|
6
6
|
|
|
7
|
-
let
|
|
7
|
+
let useAuthHook: any = null;
|
|
8
8
|
try {
|
|
9
9
|
const uiKit = require('@varity-labs/ui-kit');
|
|
10
|
-
|
|
10
|
+
useAuthHook = uiKit.useAuth;
|
|
11
11
|
} catch {}
|
|
12
12
|
|
|
13
13
|
export function useCurrentUser() {
|
|
14
14
|
// eslint-disable-next-line react-hooks/rules-of-hooks -- conditional on require() success, stable across renders
|
|
15
|
-
const
|
|
15
|
+
const auth = useAuthHook ? useAuthHook() : { user: null, authenticated: false, logout: async () => {} };
|
|
16
16
|
|
|
17
|
-
// Extract email from any
|
|
18
|
-
const user =
|
|
17
|
+
// Extract email from any auth method (email, Google, GitHub, etc.)
|
|
18
|
+
const user = auth.user;
|
|
19
19
|
const email =
|
|
20
20
|
user?.email?.address ||
|
|
21
21
|
user?.google?.email ||
|
|
@@ -30,8 +30,8 @@ export function useCurrentUser() {
|
|
|
30
30
|
id: user?.id || 'dev-user-id',
|
|
31
31
|
email,
|
|
32
32
|
name,
|
|
33
|
-
authenticated:
|
|
34
|
-
logout:
|
|
33
|
+
authenticated: auth.authenticated,
|
|
34
|
+
logout: auth.logout,
|
|
35
35
|
};
|
|
36
36
|
}
|
|
37
37
|
|