@etus/bhono-app 0.1.5 → 0.1.7
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/index.js +0 -0
- package/package.json +7 -2
- package/templates/base/.claude/commands/check-skill-rules.md +112 -29
- package/templates/base/.claude/commands/linear/implement-issue.md +383 -55
- package/templates/base/.claude/commands/ship.md +77 -13
- package/templates/base/.claude/hooks/package-lock.json +0 -419
- package/templates/base/.claude/hooks/skill-activation-prompt.ts +185 -113
- package/templates/base/.claude/hooks/skill-tool-guard.sh +6 -0
- package/templates/base/.claude/hooks/skill-tool-guard.ts +198 -0
- package/templates/base/.claude/scripts/validate-skill-rules.sh +55 -32
- package/templates/base/.claude/settings.json +18 -11
- package/templates/base/.claude/skills/skill-rules.json +326 -173
- package/templates/base/.env.example +3 -0
- package/templates/base/CLAUDE.md +5 -5
- package/templates/base/README.md +40 -27
- package/templates/base/config/eslint.config.js +1 -0
- package/templates/base/config/wrangler.json +16 -17
- package/templates/base/docs/SETUP-GUIDE.md +566 -0
- package/templates/base/docs/app_spec.txt +13 -10
- package/templates/base/docs/architecture/README.md +162 -5
- package/templates/base/docs/architecture/api-catalog.md +575 -0
- package/templates/base/docs/architecture/c4-component.md +309 -0
- package/templates/base/docs/architecture/c4-container.md +183 -0
- package/templates/base/docs/architecture/c4-context.md +106 -0
- package/templates/base/docs/architecture/data-requirements.md +4 -3
- package/templates/base/docs/architecture/db-bootstrap.md +39 -0
- package/templates/base/docs/architecture/dependencies.md +327 -0
- package/templates/base/docs/architecture/drizzle-migration-plan.md +125 -0
- package/templates/base/docs/architecture/erd.md +1 -1
- package/templates/base/docs/architecture/sql-standards.md +100 -0
- package/templates/base/docs/architecture/tech-debt.md +184 -0
- package/templates/base/docs/testing.md +36 -29
- package/templates/base/package.json +26 -20
- package/templates/base/schema.sql +84 -0
- package/templates/base/scripts/capture-prod-session.ts +2 -2
- package/templates/base/scripts/init.sh +244 -59
- package/templates/base/scripts/sync-template.sh +104 -0
- package/templates/base/src/client/hooks/use-auth.ts +5 -0
- package/templates/base/src/client/routes/_authenticated/dashboard.tsx +1 -1
- package/templates/base/src/client/routes/index.tsx +1 -1
- package/templates/base/src/server/db/client.ts +3 -5
- package/templates/base/src/server/db/records.ts +81 -0
- package/templates/base/src/server/db/seed.ts +3 -2
- package/templates/base/src/server/db/sql.ts +116 -0
- package/templates/base/src/server/index.ts +17 -2
- package/templates/base/src/server/lib/audit.ts +74 -26
- package/templates/base/src/server/lib/audited-db.ts +219 -109
- package/templates/base/src/server/lib/transaction.ts +10 -16
- package/templates/base/src/server/middleware/account.ts +9 -16
- package/templates/base/src/server/middleware/auth.ts +102 -38
- package/templates/base/src/server/middleware/rate-limit.ts +8 -6
- package/templates/base/src/server/routes/accounts/handlers.ts +18 -6
- package/templates/base/src/server/routes/audits/handlers.ts +3 -1
- package/templates/base/src/server/routes/auth/handlers.ts +15 -10
- package/templates/base/src/server/routes/auth/test-login.ts +99 -45
- package/templates/base/src/server/routes/health/handlers.ts +4 -4
- package/templates/base/src/server/routes/index.ts +9 -0
- package/templates/base/src/server/routes/invitations/handlers.ts +9 -6
- package/templates/base/src/server/routes/openapi.ts +1 -1
- package/templates/base/src/server/routes/users/handlers.ts +21 -14
- package/templates/base/src/server/services/accounts.ts +242 -217
- package/templates/base/src/server/services/audits.ts +114 -61
- package/templates/base/src/server/services/auth.ts +310 -180
- package/templates/base/src/server/services/invitations.ts +282 -222
- package/templates/base/src/server/services/users.ts +383 -293
- package/templates/base/src/server/types/index.ts +1 -2
- package/templates/base/src/shared/types/api.ts +66 -198
- package/templates/base/tests/e2e/auth.setup.ts +1 -1
- package/templates/base/{src/server/__tests__/fixtures.ts → tests/fixtures/server.ts} +3 -3
- package/templates/base/{src/client/__tests__/setup-browser.ts → tests/helpers/client-setup-browser.ts} +2 -2
- package/templates/base/{src/client/__tests__/setup.ts → tests/helpers/client-setup.ts} +1 -1
- package/templates/base/{src/client/__tests__/test-utils.tsx → tests/helpers/client-test-utils.tsx} +2 -2
- package/templates/base/{src/server/__tests__/setup.ts → tests/helpers/server.ts} +9 -9
- package/templates/base/tests/integration/accounts/crud.test.ts +2 -11
- package/templates/base/tests/integration/audits/list.test.ts +2 -11
- package/templates/base/tests/integration/auth/auth-service.test.ts +1 -10
- package/templates/base/tests/integration/auth/invitation-token.test.ts +2 -11
- package/templates/base/tests/integration/auth/logout.test.ts +2 -11
- package/templates/base/tests/integration/auth/oauth.test.ts +23 -42
- package/templates/base/tests/integration/auth/refresh-token.test.ts +1 -9
- package/templates/base/tests/integration/auth/session-expiry.test.ts +1 -9
- package/templates/base/tests/integration/auth/session.test.ts +2 -11
- package/templates/base/tests/integration/auth/super-admin.test.ts +1 -9
- package/templates/base/tests/integration/authorization/analytics-role.test.ts +2 -11
- package/templates/base/tests/integration/authorization/billing-role.test.ts +2 -11
- package/templates/base/tests/integration/authorization/guards-roles.test.ts +1 -9
- package/templates/base/tests/integration/authorization/multi-tenancy.test.ts +2 -11
- package/templates/base/tests/integration/authorization/roles.test.ts +2 -11
- package/templates/base/tests/integration/config/production-behavior.test.ts +2 -11
- package/templates/base/tests/integration/health/health.test.ts +25 -44
- package/templates/base/tests/integration/invitations/crud.test.ts +2 -11
- package/templates/base/tests/integration/invitations/email.test.ts +1 -9
- package/templates/base/tests/integration/middleware/auth.test.ts +3 -12
- package/templates/base/tests/integration/middleware/request-logger.test.ts +1 -9
- package/templates/base/tests/integration/performance/response-times.test.ts +1 -9
- package/templates/base/tests/integration/security/cookie-security.test.ts +2 -11
- package/templates/base/tests/integration/security/csrf-protection.test.ts +2 -11
- package/templates/base/tests/integration/security/log-sanitization.test.ts +1 -9
- package/templates/base/tests/integration/security/rate-limiting.test.ts +1 -9
- package/templates/base/tests/integration/security/sql-injection.test.ts +7 -18
- package/templates/base/tests/integration/security/xss-prevention.test.ts +2 -11
- package/templates/base/tests/integration/setup.ts +13 -90
- package/templates/base/tests/integration/smoke.test.ts +3 -2
- package/templates/base/tests/integration/storage/upload.test.ts +2 -11
- package/templates/base/tests/integration/storage/validation.test.ts +2 -11
- package/templates/base/tests/integration/users/crud.test.ts +2 -11
- package/templates/base/tests/integration/users/list.test.ts +2 -11
- package/templates/base/tests/integration/vitest.config.ts +2 -9
- package/templates/base/{src/server/__tests__ → tests}/mocks/db.ts +1 -1
- package/templates/base/{src/server/__tests__ → tests}/mocks/index.ts +1 -1
- package/templates/base/{src/server/__tests__ → tests}/mocks/kv.ts +1 -1
- package/templates/base/{src/server/__tests__ → tests}/mocks/r2.ts +1 -1
- package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/sidebar.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/avatar.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client/components/ui}/button.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/card.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/dialog.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/input.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/loading-skeleton.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/skeleton.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/sonner.test.tsx +1 -1
- package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/tabs.test.tsx +1 -1
- package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/account.test.tsx +1 -1
- package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/integrations.test.tsx +1 -1
- package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/settings.test.tsx +1 -1
- package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/team.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/authenticated-layout.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/dashboard.test.tsx +1 -1
- package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/invite.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/login.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/navigation.test.tsx +1 -1
- package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/root-layout.test.tsx +1 -1
- package/templates/base/{src/server/auth/__tests__ → tests/unit/server/auth}/guards.test.ts +2 -2
- package/templates/base/{src → tests/unit}/server/auth/permissions.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/auth/roles.test.ts +1 -1
- package/templates/base/tests/unit/server/db/sql.test.ts +68 -0
- package/templates/base/{src → tests/unit}/server/env.test.ts +1 -1
- package/templates/base/tests/unit/server/lib/audited-db.test.ts +78 -0
- package/templates/base/{src → tests/unit}/server/lib/email.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/errors.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/oauth.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/pagination.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/password.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/providers.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/r2-storage.test.ts +2 -2
- package/templates/base/{src → tests/unit}/server/lib/session.test.ts +2 -2
- package/templates/base/{src → tests/unit}/server/lib/tokens.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/lib/transaction.test.ts +5 -14
- package/templates/base/{src → tests/unit}/server/middleware/account.test.ts +16 -24
- package/templates/base/tests/unit/server/middleware/auth.test.ts +647 -0
- package/templates/base/{src → tests/unit}/server/middleware/cors.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/middleware/error-handler.test.ts +2 -2
- package/templates/base/{src → tests/unit}/server/middleware/rate-limit.test.ts +3 -2
- package/templates/base/{src → tests/unit}/server/middleware/request-context.test.ts +1 -1
- package/templates/base/{src → tests/unit}/server/middleware/request-logger.test.ts +1 -1
- package/templates/base/{src/server/__tests__/mocks/__tests__ → tests/unit/server/mocks}/db.test.ts +1 -1
- package/templates/base/{src/server/__tests__/mocks/__tests__ → tests/unit/server/mocks}/kv.test.ts +1 -1
- package/templates/base/{src/server/__tests__/mocks/__tests__ → tests/unit/server/mocks}/r2.test.ts +1 -1
- package/templates/base/{src/server/routes/accounts/__tests__ → tests/unit/server/routes/accounts}/handlers.test.ts +12 -12
- package/templates/base/{src/server/routes/audits/__tests__ → tests/unit/server/routes/audits}/handlers.test.ts +11 -11
- package/templates/base/{src/server/routes/auth/__tests__ → tests/unit/server/routes/auth}/handlers.test.ts +124 -13
- package/templates/base/{src/server/routes/health/__tests__ → tests/unit/server/routes/health}/handlers.test.ts +27 -23
- package/templates/base/{src/server/routes/invitations/__tests__ → tests/unit/server/routes/invitations}/handlers.test.ts +14 -17
- package/templates/base/{src/server/routes/storage/__tests__ → tests/unit/server/routes/storage}/handlers.test.ts +6 -6
- package/templates/base/{src/server/routes/users/__tests__ → tests/unit/server/routes/users}/handlers.test.ts +81 -17
- package/templates/base/tests/unit/server/services/accounts.test.ts +406 -0
- package/templates/base/tests/unit/server/services/audits.test.ts +360 -0
- package/templates/base/tests/unit/server/services/auth.test.ts +656 -0
- package/templates/base/tests/unit/server/services/invitations.test.ts +343 -0
- package/templates/base/tests/unit/server/services/users.test.ts +706 -0
- package/templates/base/{src/shared/schemas/__tests__ → tests/unit/shared}/schemas.test.ts +1 -1
- package/templates/base/tsconfig.json +2 -1
- package/templates/base/vite.config.ts +3 -1
- package/templates/base/vitest.config.browser.ts +3 -2
- package/templates/base/vitest.config.frontend.ts +3 -2
- package/templates/base/vitest.config.ts +7 -14
- package/templates/base/.claude/settings.local.json +0 -11
- package/templates/base/.github/workflows/test.yml +0 -127
- package/templates/base/auth-setup-error.png +0 -0
- package/templates/base/config/drizzle.config.ts +0 -10
- package/templates/base/pnpm-lock.yaml +0 -8175
- package/templates/base/src/server/db/schema/accounts.ts +0 -20
- package/templates/base/src/server/db/schema/audit-logs.ts +0 -26
- package/templates/base/src/server/db/schema/index.ts +0 -7
- package/templates/base/src/server/db/schema/invitations.ts +0 -30
- package/templates/base/src/server/db/schema/refresh-tokens.ts +0 -22
- package/templates/base/src/server/db/schema/user-accounts.ts +0 -25
- package/templates/base/src/server/db/schema/users.ts +0 -33
- package/templates/base/src/server/lib/audited-db.test.ts +0 -107
- package/templates/base/src/server/lib/schema-helpers.ts +0 -16
- package/templates/base/src/server/middleware/auth.test.ts +0 -345
- package/templates/base/src/server/services/__tests__/accounts.test.ts +0 -764
- package/templates/base/src/server/services/__tests__/audits.test.ts +0 -235
- package/templates/base/src/server/services/__tests__/auth.test.ts +0 -765
- package/templates/base/src/server/services/__tests__/invitations.test.ts +0 -704
- package/templates/base/src/server/services/__tests__/users.test.ts +0 -755
- package/templates/base/tests/e2e/_auth/.gitkeep +0 -0
- package/templates/base/tests/integration/lib/schema-helpers.test.ts +0 -129
- package/templates/base/tsconfig.tsbuildinfo +0 -1
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-can-be-collapsed-by-default-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-expands-when-collapsed-and-expand-button-is-clicked-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-handles-logout-button-click-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-handles-navigation-clicks-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-hides-navigation-labels-when-collapsed-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-highlights-active-route-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-renders-sidebar-navigation-items-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-shows-keyboard-shortcut-hint-when-expanded-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-shows-user-info-when-authenticated-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/__screenshots__/sidebar.test.tsx/Sidebar-shows-user-initials-in-avatar-fallback-1.png +0 -0
- /package/templates/base/{src/client/components/__tests__ → tests/unit/client/components}/error-boundary.test.tsx +0 -0
- /package/templates/base/{src/client/components/ui/__tests__ → tests/unit/client/components/ui}/error-fallback.test.tsx +0 -0
- /package/templates/base/{src/client/hooks/__tests__ → tests/unit/client/hooks}/use-auth.test.tsx +0 -0
- /package/templates/base/{src/client/hooks/__tests__ → tests/unit/client/hooks}/use-theme.test.tsx +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-dashboard-stats-cards-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-quick-action-cards-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-recent-activity-section-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-user-first-name-in-welcome-message-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-display-user-information-in-sidebar-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-render-dashboard-when-authenticated-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-authenticated-should-show-navigation-sidebar-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/dashboard.test.tsx/Dashboard-Page-when-unauthenticated-should-redirect-to-login-when-not-authenticated-1.png +0 -0
- /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-display-accept-invitation-button-1.png +0 -0
- /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-display-decline-button-linking-to-homepage-1.png +0 -0
- /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-display-invitation-details--email--workspace--role--1.png +0 -0
- /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-have-a-logo-link-to-homepage-1.png +0 -0
- /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-render-invitation-page-with-inviter-name-and-workspace-1.png +0 -0
- /package/templates/base/{src/client/routes/__tests__ → tests/unit/client/routes}/__screenshots__/invite.test.tsx/Invite-Token-Page-pending-invitation-state-should-show-terms-of-service-and-privacy-policy-links-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-display-Google-OAuth-login-button-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-have-a-link-back-to-home-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-render-login-content-without-waiting-for-authentication-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-render-login-page-at--login-route-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-show-Terms-of-Service-and-Privacy-Policy-links-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/login.test.tsx/Login-Page-should-trigger-OAuth-flow-when-clicking-login-button-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-404-handling-should-display-404-text-on-not-found-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-404-handling-should-have-navigation-options-on-404-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-404-handling-should-render-404-page-for-unknown-routes-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-account-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-integrations-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-settings-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-authenticated-navigation-should-navigate-from-dashboard-to-team-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-home-page-navigation-should-display-navigation-links-on-home-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-home-page-navigation-should-have-correct-link-destinations-on-home-page-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-allow-access-to-home-page-without-authentication-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-allow-access-to-login-page-without-authentication-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-redirect-unauthenticated-users-from-dashboard-to-login-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-redirect-unauthenticated-users-from-settings-to-login-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/__screenshots__/navigation.test.tsx/Navigation-unauthenticated-navigation-should-redirect-unauthenticated-users-from-team-to-login-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-render-Active-Sessions-section-with-session-cards-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-render-page-with-correct-title--Account--1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-show-API-Access-section-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-show-Connected-Accounts-section-with-Google-connected-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-show-Danger-Zone-section-with-delete-button-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/account.test.tsx/Account-Page-should-show-Security-section-with-Two-Factor-Authentication-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-API-documentation-section-should-display-API-documentation-link-section-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-Create-Webhook-Dialog-should-have-Add-Webhook-trigger-button-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-category-filters-should-display-all-category-buttons-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-display-all-integration-cards-with-names-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-display-category-badges-on-cards-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-show-Configure-button-for-connected-integrations-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-show-Connect-button-for-not-connected-integrations-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-integration-cards-should-show-Connected-badge-for-connected-integrations-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-Available-Integrations-section-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-Webhooks-section-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-connected-count-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-page-description-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-display-search-input-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-page-rendering-should-render-with-correct-title--Integrations--1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-search-functionality-should-filter-integrations-based-on-search-query-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-search-functionality-should-search-by-description-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-search-functionality-should-show-no-results-message-when-search-has-no-matches-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-existing-webhook-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-webhook-events-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-webhook-last-delivery-info-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-display-webhook-success-status-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/integrations.test.tsx/Integrations-Page-webhooks-section-should-have-Add-Webhook-button-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Account-tab-should-display-connected-accounts-section-with-Google-provider-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Account-tab-should-display-sessions-and-danger-zone-sections-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-display-all-notification-toggle-options-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-display-email-notifications-section-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-display-toggle-switches-for-notification-options-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Notifications-tab-should-have-toggles-checked-by-default-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display--Save-Changes--button-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-personal-information-form-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-profile-picture-section-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-user-email-in-disabled-email-input-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-user-initials-in-avatar-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-Profile-tab-should-display-user-name-in-the-name-input-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-page-rendering-should-display-all-three-tabs-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-page-rendering-should-display-page-description-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-page-rendering-should-render-with-correct-title--Settings--1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-tab-navigation-should-show-Profile-tab-as-default-active-tab-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-tab-navigation-should-switch-to-Account-tab-when-clicked-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/settings.test.tsx/Settings-Page-tab-navigation-should-switch-to-Notifications-tab-when-clicked-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-display-Active-Members-section-with-member-count-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-display-Pending-Invitations-section-with-invitation-details-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-have-invite-member-button-that-can-be-clicked-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-render-page-with-correct-title-and-description-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-render-search-input-that-filters-team-members-1.png +0 -0
- /package/templates/base/{src/client/routes/_authenticated/__tests__ → tests/unit/client/routes/_authenticated}/__screenshots__/team.test.tsx/Team-Page-should-show-current-user-with---you---indicator-and-role-badge-1.png +0 -0
- /package/templates/base/{src/client/__tests__ → tests/unit/client}/routes/error-components.test.tsx +0 -0
|
@@ -0,0 +1,566 @@
|
|
|
1
|
+
# BHono App - Guia de Setup Completo
|
|
2
|
+
|
|
3
|
+
## Setup Rápido (Recomendado)
|
|
4
|
+
|
|
5
|
+
O script `init.sh` foi atualizado para resolver automaticamente todos os problemas documentados neste guia.
|
|
6
|
+
|
|
7
|
+
### Para Projetos Novos
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
# Criar projeto
|
|
11
|
+
npm init bhono-app@latest meu-projeto -- --yes
|
|
12
|
+
cd meu-projeto
|
|
13
|
+
|
|
14
|
+
# Setup completo com credenciais Google
|
|
15
|
+
./scripts/init.sh --port 8787 \
|
|
16
|
+
--google-id "seu-client-id.apps.googleusercontent.com" \
|
|
17
|
+
--google-secret "GOCSPX-xxx"
|
|
18
|
+
|
|
19
|
+
# Ou setup básico (configure credenciais depois)
|
|
20
|
+
./scripts/init.sh --port 8787
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Para Projetos Existentes
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Setup completo
|
|
27
|
+
./scripts/init.sh --port 8787 \
|
|
28
|
+
--google-id "seu-client-id" \
|
|
29
|
+
--google-secret "seu-secret"
|
|
30
|
+
|
|
31
|
+
# Setup sem provisionar recursos Cloudflare
|
|
32
|
+
./scripts/init.sh --no-provision --port 8787
|
|
33
|
+
|
|
34
|
+
# Setup sem iniciar o servidor
|
|
35
|
+
./scripts/init.sh --skip-dev
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Múltiplas Contas Cloudflare
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
CLOUDFLARE_ACCOUNT_ID=seu-account-id ./scripts/init.sh
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### O que o init.sh faz automaticamente:
|
|
45
|
+
|
|
46
|
+
1. ✅ Corrige nome do projeto em `package.json`, `etus.config.json`, `wrangler.json`
|
|
47
|
+
2. ✅ Configura `vite.config.ts` (configPath + porta)
|
|
48
|
+
3. ✅ Cria `config/.dev.vars` com todas as variáveis necessárias
|
|
49
|
+
4. ✅ Adiciona `config/.dev.vars` ao `.gitignore`
|
|
50
|
+
5. ✅ Aumenta rate limit de auth para desenvolvimento (100 req/min)
|
|
51
|
+
6. ✅ Corrige caminhos no `drizzle.config.ts`
|
|
52
|
+
7. ✅ Sincroniza bancos SQLite (resolve problema de hash do plugin)
|
|
53
|
+
8. ✅ Provisiona recursos Cloudflare (D1, KV, R2)
|
|
54
|
+
9. ✅ Aplica schema e seed no banco de dados
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Resumo dos Problemas Encontrados e Soluções
|
|
59
|
+
|
|
60
|
+
### 1. Criação do Projeto com Prompts Interativos
|
|
61
|
+
|
|
62
|
+
**Problema:** O comando `npm init bhono-app@latest .` falha em ambientes não-TTY (como Claude Code ou CI/CD) porque usa prompts interativos.
|
|
63
|
+
|
|
64
|
+
**Solução:** Usar a flag `--yes` para pular os prompts:
|
|
65
|
+
```bash
|
|
66
|
+
npm init bhono-app@latest . -- --yes
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
### 2. Nome do Projeto Incorreto
|
|
72
|
+
|
|
73
|
+
**Problema:** Quando usado com `.` como diretório, o nome do projeto fica como "." em vez do nome da pasta, causando:
|
|
74
|
+
- `wrangler.json` com nomes inválidos (`.-db`, `.-storage`)
|
|
75
|
+
- `package.json` com `"name": "."`
|
|
76
|
+
- Erros de validação do Cloudflare
|
|
77
|
+
|
|
78
|
+
**Solução:** Após criar o projeto, corrigir manualmente:
|
|
79
|
+
|
|
80
|
+
**package.json:**
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"name": "nome-do-projeto"
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
**config/wrangler.json:**
|
|
88
|
+
- Substituir todas ocorrências de `"."` e `".-"` pelo nome correto do projeto
|
|
89
|
+
- Corrigir: `name`, `database_name`, `bucket_name`, URLs em `vars`
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
### 3. Múltiplas Contas Cloudflare
|
|
94
|
+
|
|
95
|
+
**Problema:** Se você tem múltiplas contas Cloudflare, o wrangler falha com:
|
|
96
|
+
```
|
|
97
|
+
More than one account available but unable to select one in non-interactive mode
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
**Solução:** Definir a variável de ambiente `CLOUDFLARE_ACCOUNT_ID`:
|
|
101
|
+
```bash
|
|
102
|
+
CLOUDFLARE_ACCOUNT_ID=seu-account-id ./scripts/init.sh
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Ou adicionar `account_id` no `wrangler.json`:
|
|
106
|
+
```json
|
|
107
|
+
{
|
|
108
|
+
"account_id": "seu-account-id"
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
### 4. Cloudflare Vite Plugin não Encontra wrangler.json
|
|
115
|
+
|
|
116
|
+
**Problema:** O plugin `@cloudflare/vite-plugin` não encontra o `wrangler.json` quando está em `config/`.
|
|
117
|
+
|
|
118
|
+
**Solução:** Configurar o caminho no `vite.config.ts`:
|
|
119
|
+
```typescript
|
|
120
|
+
cloudflare({
|
|
121
|
+
configPath: './config/wrangler.json',
|
|
122
|
+
}),
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
### 5. Variáveis de Ambiente não Carregadas
|
|
128
|
+
|
|
129
|
+
**Problema:** O arquivo `.dev.vars` precisa estar no mesmo diretório que o `wrangler.json` para ser carregado.
|
|
130
|
+
|
|
131
|
+
**Solução:** Criar `config/.dev.vars` com todas as variáveis necessárias:
|
|
132
|
+
```env
|
|
133
|
+
# Environment
|
|
134
|
+
ENVIRONMENT=development
|
|
135
|
+
APP_URL=http://localhost:8787
|
|
136
|
+
|
|
137
|
+
# JWT Configuration
|
|
138
|
+
JWT_SECRET=super-secret-jwt-key-with-at-least-32-characters-for-security
|
|
139
|
+
JWT_EXPIRY_MINUTES=15
|
|
140
|
+
|
|
141
|
+
# Google OAuth
|
|
142
|
+
GOOGLE_CLIENT_ID=seu-client-id
|
|
143
|
+
GOOGLE_CLIENT_SECRET=seu-client-secret
|
|
144
|
+
GOOGLE_REDIRECT_URI=http://localhost:8787/auth/callback
|
|
145
|
+
|
|
146
|
+
# Refresh Token
|
|
147
|
+
REFRESH_TOKEN_EXPIRY_DAYS=30
|
|
148
|
+
|
|
149
|
+
# SendGrid
|
|
150
|
+
SENDGRID_API_KEY=your-sendgrid-api-key
|
|
151
|
+
SENDGRID_FROM_EMAIL=noreply@example.com
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Importante:** `JWT_SECRET` deve ter pelo menos 32 caracteres.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
### 6. Banco de Dados com Hash Diferente
|
|
159
|
+
|
|
160
|
+
**Problema:** O Cloudflare Vite Plugin cria o banco SQLite com um hash baseado nas configurações, não no `database_id` do wrangler.json. Isso causa erro "no such table: users".
|
|
161
|
+
|
|
162
|
+
**Solução:** Após executar `pnpm db:push`, identificar o arquivo correto e copiar:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# Encontrar todos os arquivos sqlite
|
|
166
|
+
find .wrangler -name "*.sqlite"
|
|
167
|
+
|
|
168
|
+
# Copiar o banco com dados para o banco que o plugin usa
|
|
169
|
+
cp .wrangler/state/v3/d1/miniflare-D1DatabaseObject/ID_CORRETO.sqlite \
|
|
170
|
+
.wrangler/state/v3/d1/miniflare-D1DatabaseObject/HASH_DO_PLUGIN.sqlite
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**Melhor solução:** Atualizar `config/drizzle.config.ts` para usar o caminho correto do banco usado pelo plugin.
|
|
174
|
+
|
|
175
|
+
---
|
|
176
|
+
|
|
177
|
+
### 7. Drizzle Config com Caminhos Relativos Incorretos
|
|
178
|
+
|
|
179
|
+
**Problema:** O `drizzle.config.ts` usa caminhos relativos que não funcionam quando executado da raiz.
|
|
180
|
+
|
|
181
|
+
**Solução:** Usar caminhos relativos à raiz do projeto:
|
|
182
|
+
```typescript
|
|
183
|
+
export default defineConfig({
|
|
184
|
+
schema: './src/server/db/schema/index.ts',
|
|
185
|
+
out: './src/server/db/migrations',
|
|
186
|
+
dialect: 'sqlite',
|
|
187
|
+
dbCredentials: {
|
|
188
|
+
url: './.wrangler/state/v3/d1/miniflare-D1DatabaseObject/SEU_DB_ID.sqlite',
|
|
189
|
+
},
|
|
190
|
+
})
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
195
|
+
### 8. Rate Limit Muito Restritivo
|
|
196
|
+
|
|
197
|
+
**Problema:** O rate limit de auth (10 req/min) é atingido rapidamente porque o frontend chama `/auth/me` múltiplas vezes para verificar sessão.
|
|
198
|
+
|
|
199
|
+
**Solução:** Aumentar o limite em `src/server/middleware/rate-limit.ts`:
|
|
200
|
+
```typescript
|
|
201
|
+
export function authRateLimit() {
|
|
202
|
+
return rateLimit({
|
|
203
|
+
windowMs: 60000,
|
|
204
|
+
max: 100, // Aumentado de 10 para 100
|
|
205
|
+
message: 'Too many authentication attempts, please try again later',
|
|
206
|
+
})
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
### 9. Porta do Servidor
|
|
213
|
+
|
|
214
|
+
**Problema:** Por padrão o Vite usa porta 5173, não a porta configurada no wrangler.json.
|
|
215
|
+
|
|
216
|
+
**Solução:** Configurar a porta no `vite.config.ts`:
|
|
217
|
+
```typescript
|
|
218
|
+
export default defineConfig({
|
|
219
|
+
server: {
|
|
220
|
+
port: 8787,
|
|
221
|
+
},
|
|
222
|
+
// ...
|
|
223
|
+
})
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
---
|
|
227
|
+
|
|
228
|
+
### 10. Redirect após Login vai para Produção
|
|
229
|
+
|
|
230
|
+
**Problema:** Após login OAuth, o redirect vai para a URL de produção em vez de localhost.
|
|
231
|
+
|
|
232
|
+
**Solução:** Adicionar `APP_URL` no `config/.dev.vars`:
|
|
233
|
+
```env
|
|
234
|
+
APP_URL=http://localhost:8787
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Checklist de Setup Rápido
|
|
240
|
+
|
|
241
|
+
Com o `init.sh` atualizado, a maioria destes itens são automáticos:
|
|
242
|
+
|
|
243
|
+
- [x] ~~Criar projeto com `--yes`~~ (use `npm init bhono-app@latest nome -- --yes`)
|
|
244
|
+
- [x] ~~Corrigir nome em arquivos de config~~ (**automático via init.sh**)
|
|
245
|
+
- [x] ~~Criar `config/.dev.vars`~~ (**automático via init.sh**)
|
|
246
|
+
- [x] ~~Adicionar `config/.dev.vars` ao `.gitignore`~~ (**automático via init.sh**)
|
|
247
|
+
- [x] ~~Configurar `vite.config.ts`~~ (**automático via init.sh**)
|
|
248
|
+
- [x] ~~Executar `pnpm db:push` e `pnpm db:seed`~~ (**automático via init.sh**)
|
|
249
|
+
- [x] ~~Sincronizar bancos sqlite~~ (**automático via init.sh**)
|
|
250
|
+
- [x] ~~Aumentar rate limit de auth~~ (**automático via init.sh**)
|
|
251
|
+
- [ ] **Configurar Google OAuth redirect URI no console** (manual)
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## Variáveis de Ambiente Necessárias
|
|
256
|
+
|
|
257
|
+
| Variável | Descrição | Obrigatório |
|
|
258
|
+
|----------|-----------|-------------|
|
|
259
|
+
| `ENVIRONMENT` | development/staging/production | Sim |
|
|
260
|
+
| `APP_URL` | URL da aplicação | Sim |
|
|
261
|
+
| `JWT_SECRET` | Chave para tokens (min 32 chars) | Sim |
|
|
262
|
+
| `JWT_EXPIRY_MINUTES` | Expiração do JWT | Sim |
|
|
263
|
+
| `GOOGLE_CLIENT_ID` | ID do OAuth Google | Sim |
|
|
264
|
+
| `GOOGLE_CLIENT_SECRET` | Secret do OAuth Google | Sim |
|
|
265
|
+
| `GOOGLE_REDIRECT_URI` | URI de callback | Sim |
|
|
266
|
+
| `REFRESH_TOKEN_EXPIRY_DAYS` | Dias para refresh token | Sim |
|
|
267
|
+
| `SENDGRID_API_KEY` | API key do SendGrid | Opcional* |
|
|
268
|
+
| `SENDGRID_FROM_EMAIL` | Email remetente | Opcional* |
|
|
269
|
+
|
|
270
|
+
*Necessário para envio de convites por email.
|
|
271
|
+
|
|
272
|
+
---
|
|
273
|
+
|
|
274
|
+
## Google OAuth Setup
|
|
275
|
+
|
|
276
|
+
1. Acesse [Google Cloud Console](https://console.cloud.google.com/)
|
|
277
|
+
2. Crie um novo projeto ou selecione existente
|
|
278
|
+
3. Vá em APIs & Services > Credentials
|
|
279
|
+
4. Crie OAuth 2.0 Client ID (Web application)
|
|
280
|
+
5. Adicione os URIs de redirecionamento autorizados:
|
|
281
|
+
- `http://localhost:8787/auth/callback` (desenvolvimento)
|
|
282
|
+
- `https://seu-app.workers.dev/auth/callback` (produção)
|
|
283
|
+
6. Copie Client ID e Client Secret para `.dev.vars`
|
|
284
|
+
|
|
285
|
+
---
|
|
286
|
+
|
|
287
|
+
## Lacunas Adicionais Descobertas
|
|
288
|
+
|
|
289
|
+
### 11. Arquivo etus.config.json não Corrigido
|
|
290
|
+
|
|
291
|
+
**Problema:** O arquivo `etus.config.json` também fica com nome "." após a criação do projeto.
|
|
292
|
+
|
|
293
|
+
**Solução:** Corrigir manualmente:
|
|
294
|
+
```json
|
|
295
|
+
{
|
|
296
|
+
"name": "nome-do-projeto",
|
|
297
|
+
"domain": "nome-do-projeto.com",
|
|
298
|
+
"modules": [],
|
|
299
|
+
"providers": {
|
|
300
|
+
"auth": "google",
|
|
301
|
+
"email": "sendgrid"
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
```
|
|
305
|
+
|
|
306
|
+
---
|
|
307
|
+
|
|
308
|
+
### 12. Dois Arquivos .dev.vars (Raiz e config/)
|
|
309
|
+
|
|
310
|
+
**Problema:** O projeto pode ter dois arquivos `.dev.vars`:
|
|
311
|
+
- `.dev.vars` na raiz (usado pelo `scripts/init.sh`)
|
|
312
|
+
- `config/.dev.vars` (usado pelo Cloudflare Vite Plugin)
|
|
313
|
+
|
|
314
|
+
**Solução:** Manter ambos sincronizados com as mesmas variáveis, ou usar apenas `config/.dev.vars` e criar um symlink:
|
|
315
|
+
```bash
|
|
316
|
+
ln -sf config/.dev.vars .dev.vars
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
---
|
|
320
|
+
|
|
321
|
+
### 13. Como Descobrir Qual Banco SQLite o Plugin Usa
|
|
322
|
+
|
|
323
|
+
**Problema:** O Cloudflare Vite Plugin usa um hash diferente do `database_id` para o arquivo SQLite. Difícil saber qual arquivo atualizar.
|
|
324
|
+
|
|
325
|
+
**Solução:** Verificar qual arquivo foi modificado mais recentemente ou é maior:
|
|
326
|
+
```bash
|
|
327
|
+
# Listar todos os bancos com tamanho e data
|
|
328
|
+
find .wrangler -name "*.sqlite" -not -name "*-shm" -not -name "*-wal" -exec ls -la {} \;
|
|
329
|
+
|
|
330
|
+
# O maior arquivo geralmente é o que tem dados
|
|
331
|
+
# Ou verificar qual foi modificado após fazer uma operação
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
**Dica:** Após descobrir o hash correto, atualize `config/drizzle.config.ts` para apontar para ele.
|
|
335
|
+
|
|
336
|
+
---
|
|
337
|
+
|
|
338
|
+
### 14. Atualizar .gitignore para config/.dev.vars
|
|
339
|
+
|
|
340
|
+
**Problema:** O arquivo `config/.dev.vars` contém credenciais sensíveis mas pode não estar no `.gitignore`.
|
|
341
|
+
|
|
342
|
+
**Solução:** Adicionar ao `.gitignore`:
|
|
343
|
+
```bash
|
|
344
|
+
echo "config/.dev.vars" >> .gitignore
|
|
345
|
+
```
|
|
346
|
+
|
|
347
|
+
---
|
|
348
|
+
|
|
349
|
+
### 15. KV Namespace para Sessions
|
|
350
|
+
|
|
351
|
+
**Informação:** O projeto usa KV Namespace para armazenar sessões. Em desenvolvimento local, o miniflare cria um arquivo SQLite para simular o KV:
|
|
352
|
+
```
|
|
353
|
+
.wrangler/state/v3/kv/miniflare-KVNamespaceObject/*.sqlite
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
Este arquivo é gerenciado automaticamente e não precisa de intervenção manual.
|
|
357
|
+
|
|
358
|
+
---
|
|
359
|
+
|
|
360
|
+
## Arquivos que Precisam ser Corrigidos (Lista Completa)
|
|
361
|
+
|
|
362
|
+
> **Nota:** Todos estes arquivos são corrigidos automaticamente pelo `init.sh` atualizado.
|
|
363
|
+
|
|
364
|
+
| Arquivo | O que corrigir | Status |
|
|
365
|
+
|---------|----------------|--------|
|
|
366
|
+
| `package.json` | `"name": "."` → `"name": "nome-projeto"` | ✅ Automático |
|
|
367
|
+
| `config/wrangler.json` | `name`, `database_name`, `bucket_name`, URLs | ✅ Automático |
|
|
368
|
+
| `etus.config.json` | `"name": "."` → `"name": "nome-projeto"` | ✅ Automático |
|
|
369
|
+
| `vite.config.ts` | Adicionar `configPath` e `server.port` | ✅ Automático |
|
|
370
|
+
| `config/drizzle.config.ts` | Corrigir caminhos e apontar para banco correto | ✅ Automático |
|
|
371
|
+
| `config/.dev.vars` | Criar com todas as variáveis | ✅ Automático |
|
|
372
|
+
| `.gitignore` | Adicionar `config/.dev.vars` (segurança!) | ✅ Automático |
|
|
373
|
+
| `src/server/middleware/rate-limit.ts` | Aumentar `max` de 10 para 100 | ✅ Automático |
|
|
374
|
+
|
|
375
|
+
---
|
|
376
|
+
|
|
377
|
+
## Comandos Úteis
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
# Verificar tabelas no banco
|
|
381
|
+
sqlite3 .wrangler/state/v3/d1/miniflare-D1DatabaseObject/*.sqlite ".tables"
|
|
382
|
+
|
|
383
|
+
# Ver estrutura de uma tabela
|
|
384
|
+
sqlite3 .wrangler/state/v3/d1/miniflare-D1DatabaseObject/*.sqlite ".schema users"
|
|
385
|
+
|
|
386
|
+
# Executar query
|
|
387
|
+
sqlite3 .wrangler/state/v3/d1/miniflare-D1DatabaseObject/*.sqlite "SELECT * FROM users;"
|
|
388
|
+
|
|
389
|
+
# Aplicar seed manualmente
|
|
390
|
+
sqlite3 ARQUIVO.sqlite < seed.sql
|
|
391
|
+
|
|
392
|
+
# Listar todos os bancos sqlite
|
|
393
|
+
find .wrangler -name "*.sqlite" -not -name "*-shm" -not -name "*-wal"
|
|
394
|
+
|
|
395
|
+
# Verificar qual banco tem tabelas
|
|
396
|
+
for f in $(find .wrangler -name "*.sqlite" -not -name "*-shm" -not -name "*-wal"); do
|
|
397
|
+
echo "=== $f ==="
|
|
398
|
+
sqlite3 "$f" ".tables"
|
|
399
|
+
done
|
|
400
|
+
```
|
|
401
|
+
|
|
402
|
+
---
|
|
403
|
+
|
|
404
|
+
## Testes
|
|
405
|
+
|
|
406
|
+
```bash
|
|
407
|
+
# Rodar todos os testes
|
|
408
|
+
pnpm test
|
|
409
|
+
|
|
410
|
+
# Testes unitários do servidor
|
|
411
|
+
pnpm test:unit:server
|
|
412
|
+
|
|
413
|
+
# Testes unitários do cliente
|
|
414
|
+
pnpm test:unit:client
|
|
415
|
+
|
|
416
|
+
# Testes de integração
|
|
417
|
+
pnpm test:integration
|
|
418
|
+
|
|
419
|
+
# Testes E2E (requer servidor rodando)
|
|
420
|
+
pnpm test:e2e
|
|
421
|
+
|
|
422
|
+
# Testes E2E com UI
|
|
423
|
+
pnpm test:e2e:ui
|
|
424
|
+
```
|
|
425
|
+
|
|
426
|
+
---
|
|
427
|
+
|
|
428
|
+
## Deploy para Produção
|
|
429
|
+
|
|
430
|
+
### 1. Provisionar Recursos Cloudflare
|
|
431
|
+
|
|
432
|
+
```bash
|
|
433
|
+
# Login no Cloudflare
|
|
434
|
+
pnpm exec wrangler login
|
|
435
|
+
|
|
436
|
+
# Criar D1 Database
|
|
437
|
+
pnpm exec wrangler d1 create nome-projeto-db
|
|
438
|
+
|
|
439
|
+
# Criar KV Namespace
|
|
440
|
+
pnpm exec wrangler kv namespace create nome-projeto-sessions
|
|
441
|
+
|
|
442
|
+
# Criar R2 Bucket
|
|
443
|
+
pnpm exec wrangler r2 bucket create nome-projeto-storage
|
|
444
|
+
```
|
|
445
|
+
|
|
446
|
+
### 2. Atualizar wrangler.json com IDs
|
|
447
|
+
|
|
448
|
+
Após criar os recursos, atualize `config/wrangler.json` com os IDs retornados.
|
|
449
|
+
|
|
450
|
+
### 3. Configurar Secrets
|
|
451
|
+
|
|
452
|
+
```bash
|
|
453
|
+
# Configurar variáveis secretas
|
|
454
|
+
pnpm exec wrangler secret put JWT_SECRET --config config/wrangler.json
|
|
455
|
+
pnpm exec wrangler secret put GOOGLE_CLIENT_ID --config config/wrangler.json
|
|
456
|
+
pnpm exec wrangler secret put GOOGLE_CLIENT_SECRET --config config/wrangler.json
|
|
457
|
+
pnpm exec wrangler secret put SENDGRID_API_KEY --config config/wrangler.json
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### 4. Aplicar Schema no D1 Remoto
|
|
461
|
+
|
|
462
|
+
```bash
|
|
463
|
+
pnpm exec wrangler d1 execute nome-projeto-db --remote --file=seed.sql --config config/wrangler.json
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
### 5. Deploy
|
|
467
|
+
|
|
468
|
+
```bash
|
|
469
|
+
pnpm run deploy
|
|
470
|
+
```
|
|
471
|
+
|
|
472
|
+
---
|
|
473
|
+
|
|
474
|
+
## Troubleshooting
|
|
475
|
+
|
|
476
|
+
### Erro: INTERNAL_ERROR 500
|
|
477
|
+
|
|
478
|
+
```json
|
|
479
|
+
{"error":{"code":"INTERNAL_ERROR","message":"Internal server error","status":500}}
|
|
480
|
+
```
|
|
481
|
+
|
|
482
|
+
**Causas possíveis:**
|
|
483
|
+
|
|
484
|
+
1. **JWT_SECRET muito curto**
|
|
485
|
+
- O JWT_SECRET deve ter pelo menos 32 caracteres
|
|
486
|
+
- Verifique `config/.dev.vars`:
|
|
487
|
+
```env
|
|
488
|
+
JWT_SECRET=super-secret-jwt-key-with-at-least-32-characters-for-security
|
|
489
|
+
```
|
|
490
|
+
|
|
491
|
+
2. **Variáveis de ambiente não carregadas**
|
|
492
|
+
- O Cloudflare Vite Plugin lê de `config/.dev.vars`, não da raiz
|
|
493
|
+
- Certifique-se que `config/.dev.vars` existe e tem todas as variáveis
|
|
494
|
+
|
|
495
|
+
3. **Banco de dados sem tabelas**
|
|
496
|
+
- Execute: `pnpm db:push && pnpm db:seed`
|
|
497
|
+
- Verifique com: `sqlite3 .wrangler/state/v3/d1/miniflare-D1DatabaseObject/*.sqlite ".tables"`
|
|
498
|
+
|
|
499
|
+
4. **configPath não configurado no vite.config.ts**
|
|
500
|
+
- Adicione ao plugin cloudflare:
|
|
501
|
+
```typescript
|
|
502
|
+
cloudflare({
|
|
503
|
+
configPath: './config/wrangler.json',
|
|
504
|
+
})
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
**Diagnóstico rápido:**
|
|
508
|
+
```bash
|
|
509
|
+
# Ver logs detalhados
|
|
510
|
+
pnpm dev 2>&1 | head -50
|
|
511
|
+
|
|
512
|
+
# Verificar se config/.dev.vars existe
|
|
513
|
+
cat config/.dev.vars
|
|
514
|
+
|
|
515
|
+
# Verificar se banco tem tabelas
|
|
516
|
+
find .wrangler -name "*.sqlite" -exec sqlite3 {} ".tables" \;
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
---
|
|
520
|
+
|
|
521
|
+
### Erro: "no such table: users"
|
|
522
|
+
- Verifique se `pnpm db:push` foi executado
|
|
523
|
+
- Verifique se o drizzle.config.ts aponta para o banco correto
|
|
524
|
+
- Copie o banco com dados para o arquivo que o plugin usa
|
|
525
|
+
|
|
526
|
+
### Erro: "JWT_SECRET must be at least 32 characters"
|
|
527
|
+
- Atualize `JWT_SECRET` no `config/.dev.vars` com pelo menos 32 caracteres
|
|
528
|
+
|
|
529
|
+
### Erro: "Rate limit exceeded"
|
|
530
|
+
- Reinicie o servidor para limpar o rate limiter
|
|
531
|
+
- Ou aumente o limite em `src/server/middleware/rate-limit.ts`
|
|
532
|
+
|
|
533
|
+
### Redirect vai para produção após login
|
|
534
|
+
- Verifique se `APP_URL=http://localhost:8787` está em `config/.dev.vars`
|
|
535
|
+
|
|
536
|
+
### Frontend carrega mas API dá 404
|
|
537
|
+
- Verifique se `configPath` foi adicionado ao cloudflare plugin no vite.config.ts
|
|
538
|
+
|
|
539
|
+
### Múltiplas contas Cloudflare
|
|
540
|
+
- Defina `CLOUDFLARE_ACCOUNT_ID` antes de executar comandos wrangler
|
|
541
|
+
|
|
542
|
+
---
|
|
543
|
+
|
|
544
|
+
## Referência do init.sh
|
|
545
|
+
|
|
546
|
+
```
|
|
547
|
+
BHono - Development Environment Setup
|
|
548
|
+
|
|
549
|
+
Usage: ./scripts/init.sh [OPTIONS]
|
|
550
|
+
|
|
551
|
+
Options:
|
|
552
|
+
--port PORT Set dev server port (default: 8787)
|
|
553
|
+
--google-id ID Set Google OAuth Client ID
|
|
554
|
+
--google-secret SEC Set Google OAuth Client Secret
|
|
555
|
+
--no-provision Skip Cloudflare resource provisioning
|
|
556
|
+
--skip-dev Don't start dev server after setup
|
|
557
|
+
--skip-seed Skip database seeding
|
|
558
|
+
--update Update dependencies
|
|
559
|
+
--help, -h Show this help message
|
|
560
|
+
|
|
561
|
+
Examples:
|
|
562
|
+
./scripts/init.sh
|
|
563
|
+
./scripts/init.sh --port 3000
|
|
564
|
+
./scripts/init.sh --port 8787 --google-id 'xxx.apps.googleusercontent.com' --google-secret 'GOCSPX-xxx'
|
|
565
|
+
CLOUDFLARE_ACCOUNT_ID=xxx ./scripts/init.sh
|
|
566
|
+
```
|
|
@@ -42,12 +42,12 @@
|
|
|
42
42
|
|
|
43
43
|
<backend>
|
|
44
44
|
<framework>Hono.js 4.6</framework>
|
|
45
|
-
<orm>
|
|
45
|
+
<orm>SQL (D1/SQLite)</orm>
|
|
46
46
|
<validation>Zod</validation>
|
|
47
47
|
<documentation>OpenAPI 3.0 / Swagger UI</documentation>
|
|
48
48
|
<description>
|
|
49
49
|
Type-safe API development with automatic schema generation.
|
|
50
|
-
|
|
50
|
+
SQL-first access with Cloudflare D1 (SQLite under the hood).
|
|
51
51
|
OpenAPI spec and TypeScript types can be generated locally (api:spec, api:types).
|
|
52
52
|
</description>
|
|
53
53
|
</backend>
|
|
@@ -120,7 +120,7 @@
|
|
|
120
120
|
→ Fetch API
|
|
121
121
|
→ Hono Routes
|
|
122
122
|
→ Services
|
|
123
|
-
→
|
|
123
|
+
→ SQL (D1/SQLite)
|
|
124
124
|
→ D1 Database
|
|
125
125
|
</data_flow>
|
|
126
126
|
</architecture>
|
|
@@ -769,9 +769,11 @@
|
|
|
769
769
|
npm run build # Build for production
|
|
770
770
|
npm run deploy # Deploy to Cloudflare Workers
|
|
771
771
|
|
|
772
|
-
npm run db:
|
|
773
|
-
npm run db:
|
|
774
|
-
npm run db:seed #
|
|
772
|
+
npm run db:schema:local # Apply schema.sql locally
|
|
773
|
+
npm run db:schema:remote # Apply schema.sql to production (optional)
|
|
774
|
+
npm run db:seed # Generate seed.sql
|
|
775
|
+
npm run db:seed:local # Generate + seed locally
|
|
776
|
+
npm run db:reset:local # Apply schema.sql + seed (local)
|
|
775
777
|
|
|
776
778
|
npm run test:unit:server # Backend unit tests
|
|
777
779
|
npm run test:unit:client # Frontend tests
|
|
@@ -813,8 +815,8 @@
|
|
|
813
815
|
│ ├── services/ # Business logic
|
|
814
816
|
│ ├── middleware/ # Auth, CORS, logging
|
|
815
817
|
│ ├── db/
|
|
816
|
-
│ │ ├──
|
|
817
|
-
│ │ └──
|
|
818
|
+
│ │ ├── records.ts # DB record types for SQL mapping
|
|
819
|
+
│ │ └── seed.ts # Seed generator (outputs seed.sql)
|
|
818
820
|
│ ├── lib/ # Utilities
|
|
819
821
|
│ └── auth/ # RBAC guards
|
|
820
822
|
│
|
|
@@ -836,11 +838,12 @@
|
|
|
836
838
|
<key_files>
|
|
837
839
|
src/server/index.ts # Hono app entry
|
|
838
840
|
src/server/routes/index.ts # API router
|
|
839
|
-
src/server/db/
|
|
841
|
+
src/server/db/records.ts # Database record types
|
|
840
842
|
src/client/routes/__root.tsx # React app root
|
|
841
843
|
src/client/routes/_authenticated.tsx # Auth layout
|
|
842
844
|
wrangler.json # Cloudflare config
|
|
843
|
-
|
|
845
|
+
schema.sql # SQL schema (source of truth)
|
|
846
|
+
seed.sql # Generated seed data (output)
|
|
844
847
|
playwright.config.ts # E2E test config
|
|
845
848
|
vitest.config.ts # Unit test config
|
|
846
849
|
</key_files>
|