@react-vault/create-app 0.1.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 (117) hide show
  1. package/LICENSE +12 -0
  2. package/README.md +16 -0
  3. package/bin/create-app.js +8 -0
  4. package/claude-toolkit/README.md +131 -0
  5. package/claude-toolkit/agents/bfsi-accessibility-auditor.md +132 -0
  6. package/claude-toolkit/agents/bfsi-architect.md +156 -0
  7. package/claude-toolkit/agents/bfsi-code-reviewer.md +137 -0
  8. package/claude-toolkit/agents/bfsi-compliance-auditor.md +161 -0
  9. package/claude-toolkit/agents/bfsi-pii-scanner.md +142 -0
  10. package/claude-toolkit/agents/bfsi-pr-reviewer.md +114 -0
  11. package/claude-toolkit/agents/bfsi-security-reviewer.md +136 -0
  12. package/claude-toolkit/commands/bfsi-audit.md +46 -0
  13. package/claude-toolkit/commands/bfsi-doctor.md +97 -0
  14. package/claude-toolkit/commands/bfsi-review.md +46 -0
  15. package/claude-toolkit/commands/bfsi-scaffold.md +47 -0
  16. package/claude-toolkit/hooks/hooks.json +181 -0
  17. package/claude-toolkit/hooks/scripts/a11y-check.sh +63 -0
  18. package/claude-toolkit/hooks/scripts/audit-prompt.sh +36 -0
  19. package/claude-toolkit/hooks/scripts/block-destructive.sh +41 -0
  20. package/claude-toolkit/hooks/scripts/block-force-push.sh +30 -0
  21. package/claude-toolkit/hooks/scripts/format.sh +42 -0
  22. package/claude-toolkit/hooks/scripts/inject-context.sh +44 -0
  23. package/claude-toolkit/hooks/scripts/lint.sh +45 -0
  24. package/claude-toolkit/hooks/scripts/protect-files.sh +53 -0
  25. package/claude-toolkit/hooks/scripts/save-compliance-context.sh +35 -0
  26. package/claude-toolkit/hooks/scripts/scan-pii.sh +87 -0
  27. package/claude-toolkit/hooks/scripts/scan-secrets.sh +67 -0
  28. package/claude-toolkit/hooks/scripts/verify-clean.sh +50 -0
  29. package/claude-toolkit/package.json +22 -0
  30. package/claude-toolkit/plugin.json +31 -0
  31. package/claude-toolkit/skills/bfsi-api-endpoint/SKILL.md +105 -0
  32. package/claude-toolkit/skills/bfsi-commit/SKILL.md +102 -0
  33. package/claude-toolkit/skills/bfsi-compliance-check/SKILL.md +107 -0
  34. package/claude-toolkit/skills/bfsi-encrypt-helper/SKILL.md +127 -0
  35. package/claude-toolkit/skills/bfsi-error-message/SKILL.md +162 -0
  36. package/claude-toolkit/skills/bfsi-feature/SKILL.md +120 -0
  37. package/claude-toolkit/skills/bfsi-feature/references/architecture.md +69 -0
  38. package/claude-toolkit/skills/bfsi-feature/references/audit-events.md +70 -0
  39. package/claude-toolkit/skills/bfsi-feature/scripts/scaffold.mjs +136 -0
  40. package/claude-toolkit/skills/bfsi-form/SKILL.md +73 -0
  41. package/claude-toolkit/skills/bfsi-form/references/validation-regex.md +50 -0
  42. package/claude-toolkit/skills/bfsi-onboarding/SKILL.md +110 -0
  43. package/claude-toolkit/skills/bfsi-pii-field/SKILL.md +90 -0
  44. package/claude-toolkit/skills/bfsi-test-pattern/SKILL.md +179 -0
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.d.ts.map +1 -0
  47. package/dist/index.js +339 -0
  48. package/dist/index.js.map +1 -0
  49. package/package.json +69 -0
  50. package/templates/_shared/.claude/settings.json +31 -0
  51. package/templates/_shared/.env.local.sample +25 -0
  52. package/templates/_shared/.github/workflows/ci.yml +49 -0
  53. package/templates/_shared/CLAUDE.md +89 -0
  54. package/templates/_shared/README.md +50 -0
  55. package/templates/_shared/index.html +16 -0
  56. package/templates/_shared/package.json +73 -0
  57. package/templates/_shared/postcss.config.cjs +6 -0
  58. package/templates/_shared/src/app/App.tsx +13 -0
  59. package/templates/_shared/src/app/globals.css +64 -0
  60. package/templates/_shared/src/env.ts +33 -0
  61. package/templates/_shared/src/i18n/i18n.ts +18 -0
  62. package/templates/_shared/src/i18n/translations/en.json +54 -0
  63. package/templates/_shared/src/i18n/translations/hi.json +30 -0
  64. package/templates/_shared/src/main.tsx +16 -0
  65. package/templates/_shared/src/routes/ProtectedRoute.tsx +28 -0
  66. package/templates/_shared/src/routes/index.tsx +67 -0
  67. package/templates/_shared/src/shared/ErrorBoundary.tsx +60 -0
  68. package/templates/_shared/tailwind.config.ts +68 -0
  69. package/templates/_shared/tests/setup.ts +7 -0
  70. package/templates/_shared/tsconfig.json +33 -0
  71. package/templates/_shared/tsconfig.node.json +13 -0
  72. package/templates/_shared/vite.config.ts +47 -0
  73. package/templates/rtk-query/.claude/skills/axios-auth/SKILL.md +103 -0
  74. package/templates/rtk-query/.claude/skills/axios-auth/references/error-shape.md +84 -0
  75. package/templates/rtk-query/.claude/skills/axios-auth/references/full-code-walkthrough.md +146 -0
  76. package/templates/rtk-query/.claude/skills/axios-auth/references/notification-wiring.md +141 -0
  77. package/templates/rtk-query/.claude/skills/constants-organization/SKILL.md +112 -0
  78. package/templates/rtk-query/.claude/skills/constants-organization/references/example-files.md +134 -0
  79. package/templates/rtk-query/.claude/skills/constants-organization/references/tag-types-catalog.md +53 -0
  80. package/templates/rtk-query/.claude/skills/redux-store-integration/SKILL.md +159 -0
  81. package/templates/rtk-query/.claude/skills/redux-store-integration/references/localStorage-persistence.md +70 -0
  82. package/templates/rtk-query/.claude/skills/redux-store-integration/references/middleware-patterns.md +82 -0
  83. package/templates/rtk-query/.claude/skills/rtk-query-api/SKILL.md +148 -0
  84. package/templates/rtk-query/.claude/skills/rtk-query-api/references/cache-strategies.md +96 -0
  85. package/templates/rtk-query/.claude/skills/rtk-query-api/references/endpoint-cookbook.md +145 -0
  86. package/templates/rtk-query/.claude/skills/rtk-query-api/references/optimistic-update.md +53 -0
  87. package/templates/rtk-query/README.md +84 -0
  88. package/templates/rtk-query/package.partial.json +7 -0
  89. package/templates/rtk-query/src/app/App.tsx +23 -0
  90. package/templates/rtk-query/src/axiosconfig/axiosInstance.ts +26 -0
  91. package/templates/rtk-query/src/axiosconfig/baseQuery.ts +72 -0
  92. package/templates/rtk-query/src/axiosconfig/interceptor.ts +42 -0
  93. package/templates/rtk-query/src/redux/invalidateCacheMiddleware.ts +20 -0
  94. package/templates/rtk-query/src/redux/reduxHooks.ts +10 -0
  95. package/templates/rtk-query/src/redux/rootReducer.ts +18 -0
  96. package/templates/rtk-query/src/redux/store.ts +36 -0
  97. package/templates/tanstack-query/.claude/skills/axios-auth/SKILL.md +109 -0
  98. package/templates/tanstack-query/.claude/skills/axios-auth/references/error-shape.md +89 -0
  99. package/templates/tanstack-query/.claude/skills/axios-auth/references/full-code-walkthrough.md +121 -0
  100. package/templates/tanstack-query/.claude/skills/axios-auth/references/notification-pattern.md +109 -0
  101. package/templates/tanstack-query/.claude/skills/constants-organization/SKILL.md +144 -0
  102. package/templates/tanstack-query/.claude/skills/constants-organization/references/example-files.md +111 -0
  103. package/templates/tanstack-query/.claude/skills/constants-organization/references/query-key-factories.md +129 -0
  104. package/templates/tanstack-query/.claude/skills/query-client-setup/SKILL.md +165 -0
  105. package/templates/tanstack-query/.claude/skills/query-client-setup/references/devtools.md +67 -0
  106. package/templates/tanstack-query/.claude/skills/query-client-setup/references/global-handlers.md +94 -0
  107. package/templates/tanstack-query/.claude/skills/tanstack-services/SKILL.md +142 -0
  108. package/templates/tanstack-query/.claude/skills/tanstack-services/references/audited-mutation.md +144 -0
  109. package/templates/tanstack-query/.claude/skills/tanstack-services/references/optimistic-update.md +102 -0
  110. package/templates/tanstack-query/.claude/skills/tanstack-services/references/service-cookbook.md +151 -0
  111. package/templates/tanstack-query/README.md +63 -0
  112. package/templates/tanstack-query/package.partial.json +8 -0
  113. package/templates/tanstack-query/src/api/axiosInstance.ts +20 -0
  114. package/templates/tanstack-query/src/api/http.ts +62 -0
  115. package/templates/tanstack-query/src/api/queryClient.ts +28 -0
  116. package/templates/tanstack-query/src/app/App.tsx +20 -0
  117. package/templates/tanstack-query/src/services/example.ts +32 -0
@@ -0,0 +1,50 @@
1
+ # {{projectName}}
2
+
3
+ BFSI React app scaffolded from `@react-vault/create-app`.
4
+
5
+ ## Quick start
6
+
7
+ ```bash
8
+ pnpm install
9
+ pnpm dev # http://localhost:5173
10
+ claude # Claude Code with BFSI toolkit enabled
11
+ ```
12
+
13
+ In Claude Code, run `/bfsi-doctor` first to verify everything is wired.
14
+
15
+ ## Project layout
16
+
17
+ ```
18
+ src/
19
+ ├── app/ App.tsx, providers, globals.css
20
+ ├── routes/ Route config, ProtectedRoute, CanAccess
21
+ ├── features/<Feature>/ One folder per feature (use /bfsi-feature to scaffold)
22
+ ├── shared/ Cross-feature components (ErrorBoundary)
23
+ ├── i18n/ react-i18next setup + en/hi translations
24
+ ├── env.ts Zod-validated env vars
25
+ └── main.tsx Entry point
26
+ ```
27
+
28
+ ## Common commands
29
+
30
+ ```bash
31
+ pnpm dev # dev server (vite)
32
+ pnpm build # production build
33
+ pnpm test # vitest
34
+ pnpm test:e2e # playwright
35
+ pnpm typecheck # tsc --noEmit
36
+ pnpm lint # eslint
37
+ pnpm format # prettier write
38
+ ```
39
+
40
+ ## Claude Code commands
41
+
42
+ ```
43
+ /bfsi-doctor # health check this project
44
+ /bfsi-scaffold feature X # scaffold a new feature
45
+ /bfsi-review # full PR review (security + a11y + perf + tests)
46
+ /bfsi-audit # compliance audit (RBI / PCI / IRDAI / SOC2)
47
+ /bfsi-commit # generate audit-friendly commit message
48
+ ```
49
+
50
+ See `packages/claude-toolkit/README.md` in the starter repo for the full list.
@@ -0,0 +1,16 @@
1
+ <!doctype html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
6
+ <meta name="viewport" content="width=device-width, initial-scale=1.0" />
7
+ <meta name="theme-color" content="#0b1736" />
8
+ <meta name="referrer" content="strict-origin-when-cross-origin" />
9
+ <title>{{projectName}}</title>
10
+ </head>
11
+ <body>
12
+ <div id="root"></div>
13
+ <noscript>JavaScript is required for this application to run.</noscript>
14
+ <script type="module" src="/src/main.tsx"></script>
15
+ </body>
16
+ </html>
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "{{projectName}}",
3
+ "version": "0.1.0",
4
+ "private": true,
5
+ "type": "module",
6
+ "scripts": {
7
+ "dev": "vite",
8
+ "build": "tsc -b && vite build",
9
+ "preview": "vite preview",
10
+ "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0",
11
+ "lint:fix": "eslint . --ext ts,tsx --fix",
12
+ "typecheck": "tsc -b --noEmit",
13
+ "test": "vitest run",
14
+ "test:watch": "vitest",
15
+ "test:e2e": "playwright test",
16
+ "format": "prettier --write \"src/**/*.{ts,tsx,json,md,css}\"",
17
+ "prepare": "husky"
18
+ },
19
+ "dependencies": {
20
+ "@react-vault/core": "0.1.0",
21
+ "@react-vault/ui": "0.1.0",
22
+ "react": "18.3.1",
23
+ "react-dom": "18.3.1",
24
+ "react-router-dom": "6.23.0",
25
+ "react-hook-form": "7.51.4",
26
+ "@hookform/resolvers": "3.3.4",
27
+ "zod": "3.23.6",
28
+ "react-i18next": "14.1.1",
29
+ "i18next": "23.11.3",
30
+ "axios": "1.6.8",
31
+ "date-fns": "3.6.0",
32
+ "lucide-react": "0.378.0",
33
+ "clsx": "2.1.1",
34
+ "tailwind-merge": "2.3.0",
35
+ "class-variance-authority": "0.7.0"
36
+ },
37
+ "devDependencies": {
38
+ "@types/react": "18.3.0",
39
+ "@types/react-dom": "18.3.0",
40
+ "@types/node": "20.11.30",
41
+ "@vitejs/plugin-react-swc": "3.6.0",
42
+ "@testing-library/react": "15.0.6",
43
+ "@testing-library/user-event": "14.5.2",
44
+ "@testing-library/jest-dom": "6.4.5",
45
+ "@playwright/test": "1.43.1",
46
+ "@typescript-eslint/eslint-plugin": "7.8.0",
47
+ "@typescript-eslint/parser": "7.8.0",
48
+ "autoprefixer": "10.4.19",
49
+ "eslint": "8.57.0",
50
+ "eslint-config-prettier": "9.1.0",
51
+ "eslint-plugin-jsx-a11y": "6.8.0",
52
+ "eslint-plugin-react": "7.34.1",
53
+ "eslint-plugin-react-hooks": "4.6.2",
54
+ "eslint-plugin-react-refresh": "0.4.6",
55
+ "husky": "9.0.11",
56
+ "jsdom": "24.0.0",
57
+ "lint-staged": "15.2.2",
58
+ "postcss": "8.4.38",
59
+ "prettier": "3.2.5",
60
+ "tailwindcss": "3.4.3",
61
+ "tailwindcss-animate": "1.0.7",
62
+ "typescript": "5.4.5",
63
+ "vite": "5.2.10",
64
+ "vitest": "1.6.0",
65
+ "@vitest/coverage-v8": "1.6.0"
66
+ },
67
+ "lint-staged": {
68
+ "*.{ts,tsx}": [
69
+ "prettier --write",
70
+ "eslint --fix"
71
+ ]
72
+ }
73
+ }
@@ -0,0 +1,6 @@
1
+ module.exports = {
2
+ plugins: {
3
+ tailwindcss: {},
4
+ autoprefixer: {},
5
+ },
6
+ };
@@ -0,0 +1,13 @@
1
+ import { BrowserRouter } from 'react-router-dom';
2
+ import { ErrorBoundary } from '../shared/ErrorBoundary.js';
3
+ import { AppRoutes } from '../routes/index.js';
4
+
5
+ export function App(): JSX.Element {
6
+ return (
7
+ <ErrorBoundary>
8
+ <BrowserRouter>
9
+ <AppRoutes />
10
+ </BrowserRouter>
11
+ </ErrorBoundary>
12
+ );
13
+ }
@@ -0,0 +1,64 @@
1
+ @tailwind base;
2
+ @tailwind components;
3
+ @tailwind utilities;
4
+
5
+ @layer base {
6
+ :root {
7
+ --background: 0 0% 100%;
8
+ --foreground: 240 10% 3.9%;
9
+ --card: 0 0% 100%;
10
+ --card-foreground: 240 10% 3.9%;
11
+ --popover: 0 0% 100%;
12
+ --popover-foreground: 240 10% 3.9%;
13
+ --primary: 217 91% 35%; /* BFSI navy */
14
+ --primary-foreground: 0 0% 98%;
15
+ --secondary: 240 4.8% 95.9%;
16
+ --secondary-foreground: 240 5.9% 10%;
17
+ --muted: 240 4.8% 95.9%;
18
+ --muted-foreground: 240 3.8% 46.1%;
19
+ --accent: 240 4.8% 95.9%;
20
+ --accent-foreground: 240 5.9% 10%;
21
+ --destructive: 0 84.2% 60.2%;
22
+ --destructive-foreground: 0 0% 98%;
23
+ --border: 240 5.9% 90%;
24
+ --input: 240 5.9% 90%;
25
+ --ring: 217 91% 35%;
26
+ --radius: 0.5rem;
27
+ }
28
+
29
+ .dark {
30
+ --background: 240 10% 3.9%;
31
+ --foreground: 0 0% 98%;
32
+ --card: 240 10% 3.9%;
33
+ --card-foreground: 0 0% 98%;
34
+ --popover: 240 10% 3.9%;
35
+ --popover-foreground: 0 0% 98%;
36
+ --primary: 217 91% 60%;
37
+ --primary-foreground: 240 5.9% 10%;
38
+ --secondary: 240 3.7% 15.9%;
39
+ --secondary-foreground: 0 0% 98%;
40
+ --muted: 240 3.7% 15.9%;
41
+ --muted-foreground: 240 5% 64.9%;
42
+ --accent: 240 3.7% 15.9%;
43
+ --accent-foreground: 0 0% 98%;
44
+ --destructive: 0 62.8% 30.6%;
45
+ --destructive-foreground: 0 0% 98%;
46
+ --border: 240 3.7% 15.9%;
47
+ --input: 240 3.7% 15.9%;
48
+ --ring: 217 91% 60%;
49
+ }
50
+
51
+ * {
52
+ @apply border-border;
53
+ }
54
+
55
+ body {
56
+ @apply bg-background text-foreground;
57
+ font-feature-settings: "rlig" 1, "calt" 1;
58
+ }
59
+
60
+ /* Disable text selection on PII reveal toggle icons */
61
+ [data-testid^="pii-"] button {
62
+ user-select: none;
63
+ }
64
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Env vars — validated at startup via Zod.
3
+ * Throws on app boot if anything required is missing or malformed.
4
+ */
5
+ import { z } from 'zod';
6
+
7
+ const envSchema = z.object({
8
+ VITE_API_BASE_URL: z.string().url(),
9
+ VITE_API_TIMEOUT_MS: z.coerce.number().int().positive().default(30_000),
10
+ VITE_AUTH_HEADER_NAME: z.string().min(1).default('Authorization'),
11
+ VITE_AUDIT_ENDPOINT: z.string().min(1).default('/api/audit'),
12
+ VITE_AUDIT_BATCH_SIZE: z.coerce.number().int().positive().default(20),
13
+ VITE_AUDIT_FLUSH_INTERVAL_MS: z.coerce.number().int().positive().default(5_000),
14
+ VITE_SENTRY_DSN: z.string().optional(),
15
+ VITE_IDLE_TIMEOUT_MS: z.coerce.number().int().positive().default(900_000),
16
+ VITE_SENSITIVE_IDLE_TIMEOUT_MS: z.coerce.number().int().positive().default(300_000),
17
+ VITE_FEATURE_FLAGS_PROVIDER: z.enum(['local', 'growthbook', 'unleash']).default('local'),
18
+ });
19
+
20
+ export type Env = z.infer<typeof envSchema>;
21
+
22
+ function load(): Env {
23
+ const parsed = envSchema.safeParse(import.meta.env);
24
+ if (!parsed.success) {
25
+ const issues = parsed.error.issues
26
+ .map((i) => ` ${i.path.join('.')}: ${i.message}`)
27
+ .join('\n');
28
+ throw new Error(`Invalid environment variables:\n${issues}`);
29
+ }
30
+ return parsed.data;
31
+ }
32
+
33
+ export const env: Env = load();
@@ -0,0 +1,18 @@
1
+ import i18n from 'i18next';
2
+ import { initReactI18next } from 'react-i18next';
3
+ import en from './translations/en.json';
4
+ import hi from './translations/hi.json';
5
+
6
+ void i18n.use(initReactI18next).init({
7
+ resources: {
8
+ en: { translation: en },
9
+ hi: { translation: hi },
10
+ },
11
+ lng: 'en',
12
+ fallbackLng: 'en',
13
+ interpolation: {
14
+ escapeValue: false, // React already escapes
15
+ },
16
+ });
17
+
18
+ export default i18n;
@@ -0,0 +1,54 @@
1
+ {
2
+ "app": {
3
+ "name": "{{projectName}}",
4
+ "loading": "Loading…",
5
+ "empty": "Nothing to show"
6
+ },
7
+ "errors": {
8
+ "network": {
9
+ "title": "Can't reach the server",
10
+ "description": "Check your internet connection and try again."
11
+ },
12
+ "timeout": {
13
+ "title": "Request took too long",
14
+ "description": "Please try again."
15
+ },
16
+ "session_expired": {
17
+ "title": "Session expired",
18
+ "description": "Please sign in again."
19
+ },
20
+ "forbidden": {
21
+ "title": "Not allowed",
22
+ "description": "You don't have access to this. Contact your administrator if you think this is wrong."
23
+ },
24
+ "not_found": {
25
+ "title": "Not found",
26
+ "description": "The page or resource you were looking for doesn't exist."
27
+ },
28
+ "validation": {
29
+ "title": "Check your inputs",
30
+ "description": "Some fields need attention before we can proceed."
31
+ },
32
+ "rate_limited": {
33
+ "title": "Too many attempts",
34
+ "description": "Please wait a moment and try again."
35
+ },
36
+ "server": {
37
+ "title": "Something went wrong on our end",
38
+ "description": "Please try again in a moment. (Ref: {{ref}})"
39
+ },
40
+ "generic": {
41
+ "title": "Something went wrong",
42
+ "description": "Please try again. If it persists, contact support with reference {{ref}}."
43
+ }
44
+ },
45
+ "pii": {
46
+ "reveal": {
47
+ "pan": "Reveal PAN",
48
+ "aadhaar": "Reveal Aadhaar",
49
+ "account": "Reveal account number",
50
+ "mobile": "Reveal mobile",
51
+ "email": "Reveal email"
52
+ }
53
+ }
54
+ }
@@ -0,0 +1,30 @@
1
+ {
2
+ "app": {
3
+ "name": "{{projectName}}",
4
+ "loading": "लोड हो रहा है…",
5
+ "empty": "कुछ नहीं दिखाने को"
6
+ },
7
+ "errors": {
8
+ "network": {
9
+ "title": "सर्वर तक नहीं पहुंच पा रहे",
10
+ "description": "अपना इंटरनेट कनेक्शन जांचें और फिर से प्रयास करें।"
11
+ },
12
+ "session_expired": {
13
+ "title": "सत्र समाप्त हो गया",
14
+ "description": "कृपया फिर से साइन इन करें।"
15
+ },
16
+ "generic": {
17
+ "title": "कुछ गलत हुआ",
18
+ "description": "फिर से प्रयास करें। यदि समस्या बनी रहती है, तो रेफरेंस {{ref}} के साथ समर्थन से संपर्क करें।"
19
+ }
20
+ },
21
+ "pii": {
22
+ "reveal": {
23
+ "pan": "पैन दिखाएं",
24
+ "aadhaar": "आधार दिखाएं",
25
+ "account": "खाता संख्या दिखाएं",
26
+ "mobile": "मोबाइल दिखाएं",
27
+ "email": "ईमेल दिखाएं"
28
+ }
29
+ }
30
+ }
@@ -0,0 +1,16 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import './app/globals.css';
4
+ import { App } from './app/App.js';
5
+ import './i18n/i18n.js';
6
+
7
+ const root = document.getElementById('root');
8
+ if (!root) {
9
+ throw new Error('root element not found');
10
+ }
11
+
12
+ ReactDOM.createRoot(root).render(
13
+ <React.StrictMode>
14
+ <App />
15
+ </React.StrictMode>,
16
+ );
@@ -0,0 +1,28 @@
1
+ import { type ReactNode } from 'react';
2
+ import { Navigate, useLocation } from 'react-router-dom';
3
+
4
+ /**
5
+ * v0.1 stub. Wire up to your auth context once you have it.
6
+ *
7
+ * Expected behaviour:
8
+ * - If not authenticated → redirect to /login (with `from` for return)
9
+ * - If authenticated but lacks `permission` → render 403 + emit audit event
10
+ * - Else → render children
11
+ */
12
+ export interface ProtectedRouteProps {
13
+ permission?: string;
14
+ /** Override idle timeout for this route (in ms). */
15
+ idleTimeoutMs?: number;
16
+ children: ReactNode;
17
+ }
18
+
19
+ export function ProtectedRoute({ permission: _permission, children }: ProtectedRouteProps): JSX.Element {
20
+ const location = useLocation();
21
+ // TODO: wire to actual auth context
22
+ const isAuthenticated = true; // placeholder
23
+ if (!isAuthenticated) {
24
+ return <Navigate to="/login" state={{ from: location }} replace />;
25
+ }
26
+ // TODO: check permission against user's permission set
27
+ return <>{children}</>;
28
+ }
@@ -0,0 +1,67 @@
1
+ import { Routes, Route } from 'react-router-dom';
2
+ import { ProtectedRoute } from './ProtectedRoute.js';
3
+
4
+ export function AppRoutes(): JSX.Element {
5
+ return (
6
+ <Routes>
7
+ <Route path="/" element={<Landing />} />
8
+ <Route path="/login" element={<Login />} />
9
+ <Route
10
+ path="/dashboard"
11
+ element={
12
+ <ProtectedRoute permission="dashboard.view">
13
+ <Dashboard />
14
+ </ProtectedRoute>
15
+ }
16
+ />
17
+ <Route path="*" element={<NotFound />} />
18
+ </Routes>
19
+ );
20
+ }
21
+
22
+ function Landing(): JSX.Element {
23
+ return (
24
+ <main className="container mx-auto py-12">
25
+ <h1 className="text-3xl font-bold tracking-tight">{`{{projectName}}`}</h1>
26
+ <p className="mt-2 text-muted-foreground">
27
+ Scaffolded from @react-vault/create-app. Go to{' '}
28
+ <a className="underline" href="/dashboard">
29
+ /dashboard
30
+ </a>
31
+ .
32
+ </p>
33
+ <p className="mt-4 text-sm text-muted-foreground">
34
+ Run <code className="rounded bg-muted px-1.5 py-0.5">claude</code> in this directory to
35
+ start a Claude Code session with the BFSI toolkit enabled. Then run{' '}
36
+ <code className="rounded bg-muted px-1.5 py-0.5">/bfsi-doctor</code>.
37
+ </p>
38
+ </main>
39
+ );
40
+ }
41
+
42
+ function Login(): JSX.Element {
43
+ return (
44
+ <main className="container mx-auto py-12">
45
+ <h1 className="text-2xl font-semibold">Sign in</h1>
46
+ <p className="mt-2 text-muted-foreground">Login form goes here.</p>
47
+ </main>
48
+ );
49
+ }
50
+
51
+ function Dashboard(): JSX.Element {
52
+ return (
53
+ <main className="container mx-auto py-12">
54
+ <h1 className="text-2xl font-semibold">Dashboard</h1>
55
+ <p className="mt-2 text-muted-foreground">Protected by &lt;ProtectedRoute&gt;.</p>
56
+ </main>
57
+ );
58
+ }
59
+
60
+ function NotFound(): JSX.Element {
61
+ return (
62
+ <main className="container mx-auto py-12">
63
+ <h1 className="text-2xl font-semibold">Not found</h1>
64
+ <p className="mt-2 text-muted-foreground">The page you were looking for doesn't exist.</p>
65
+ </main>
66
+ );
67
+ }
@@ -0,0 +1,60 @@
1
+ import { Component, type ErrorInfo, type ReactNode } from 'react';
2
+ import { generateErrorRef } from '@react-vault/core/audit';
3
+
4
+ interface Props {
5
+ children: ReactNode;
6
+ }
7
+
8
+ interface State {
9
+ errorRef: string | null;
10
+ }
11
+
12
+ /**
13
+ * BFSI error boundary. Catches render errors; shows a generic safe message
14
+ * with a ref code that maps to a full log entry.
15
+ *
16
+ * NEVER expose error.message / error.stack to the UI — that leaks technical
17
+ * detail per the bfsi-error-message skill.
18
+ */
19
+ export class ErrorBoundary extends Component<Props, State> {
20
+ state: State = { errorRef: null };
21
+
22
+ static getDerivedStateFromError(_error: Error): State {
23
+ return { errorRef: generateErrorRef() };
24
+ }
25
+
26
+ componentDidCatch(error: Error, info: ErrorInfo): void {
27
+ // Forward to your observability (PII-scrubbed). Replace with your client.
28
+ // For now, log to console with structure.
29
+ // eslint-disable-next-line no-console
30
+ console.error('[error-boundary]', {
31
+ ref: this.state.errorRef,
32
+ message: error.message,
33
+ stack: error.stack,
34
+ componentStack: info.componentStack,
35
+ });
36
+ }
37
+
38
+ render(): ReactNode {
39
+ if (this.state.errorRef) {
40
+ return (
41
+ <main className="container mx-auto py-12">
42
+ <h1 className="text-2xl font-semibold">Something went wrong</h1>
43
+ <p className="mt-2 text-muted-foreground">
44
+ We're sorry — an unexpected error occurred. If this persists, contact support with this
45
+ reference:
46
+ </p>
47
+ <code className="mt-4 inline-block rounded bg-muted px-2 py-1 font-mono text-sm">
48
+ {this.state.errorRef}
49
+ </code>
50
+ <div className="mt-6">
51
+ <a className="underline" href="/">
52
+ Go back to home
53
+ </a>
54
+ </div>
55
+ </main>
56
+ );
57
+ }
58
+ return this.props.children;
59
+ }
60
+ }
@@ -0,0 +1,68 @@
1
+ import type { Config } from 'tailwindcss';
2
+ import animate from 'tailwindcss-animate';
3
+
4
+ const config: Config = {
5
+ darkMode: ['class'],
6
+ content: [
7
+ './index.html',
8
+ './src/**/*.{ts,tsx}',
9
+ './node_modules/@react-vault/ui/dist/**/*.{js,mjs}',
10
+ ],
11
+ theme: {
12
+ container: {
13
+ center: true,
14
+ padding: '2rem',
15
+ screens: { '2xl': '1400px' },
16
+ },
17
+ extend: {
18
+ colors: {
19
+ // shadcn/ui semantic colors (CSS vars in app/globals.css)
20
+ border: 'hsl(var(--border))',
21
+ input: 'hsl(var(--input))',
22
+ ring: 'hsl(var(--ring))',
23
+ background: 'hsl(var(--background))',
24
+ foreground: 'hsl(var(--foreground))',
25
+ primary: {
26
+ DEFAULT: 'hsl(var(--primary))',
27
+ foreground: 'hsl(var(--primary-foreground))',
28
+ },
29
+ secondary: {
30
+ DEFAULT: 'hsl(var(--secondary))',
31
+ foreground: 'hsl(var(--secondary-foreground))',
32
+ },
33
+ destructive: {
34
+ DEFAULT: 'hsl(var(--destructive))',
35
+ foreground: 'hsl(var(--destructive-foreground))',
36
+ },
37
+ muted: {
38
+ DEFAULT: 'hsl(var(--muted))',
39
+ foreground: 'hsl(var(--muted-foreground))',
40
+ },
41
+ accent: {
42
+ DEFAULT: 'hsl(var(--accent))',
43
+ foreground: 'hsl(var(--accent-foreground))',
44
+ },
45
+ // BFSI status colours
46
+ status: {
47
+ pending: 'hsl(38 92% 50%)',
48
+ approved: 'hsl(142 71% 45%)',
49
+ rejected: 'hsl(0 84% 60%)',
50
+ review: 'hsl(217 91% 60%)',
51
+ expired: 'hsl(240 5% 50%)',
52
+ },
53
+ },
54
+ borderRadius: {
55
+ lg: 'var(--radius)',
56
+ md: 'calc(var(--radius) - 2px)',
57
+ sm: 'calc(var(--radius) - 4px)',
58
+ },
59
+ fontFamily: {
60
+ sans: ['Inter', 'system-ui', 'sans-serif'],
61
+ mono: ['JetBrains Mono', 'ui-monospace', 'monospace'],
62
+ },
63
+ },
64
+ },
65
+ plugins: [animate],
66
+ };
67
+
68
+ export default config;
@@ -0,0 +1,7 @@
1
+ import '@testing-library/jest-dom/vitest';
2
+ import { afterEach } from 'vitest';
3
+ import { cleanup } from '@testing-library/react';
4
+
5
+ afterEach(() => {
6
+ cleanup();
7
+ });
@@ -0,0 +1,33 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "useDefineForClassFields": true,
5
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
6
+ "module": "ESNext",
7
+ "moduleResolution": "Bundler",
8
+ "allowImportingTsExtensions": false,
9
+ "resolveJsonModule": true,
10
+ "isolatedModules": true,
11
+ "esModuleInterop": true,
12
+ "noEmit": true,
13
+ "jsx": "react-jsx",
14
+ "strict": true,
15
+ "noImplicitAny": true,
16
+ "strictNullChecks": true,
17
+ "noUnusedLocals": true,
18
+ "noUnusedParameters": true,
19
+ "noImplicitReturns": true,
20
+ "noFallthroughCasesInSwitch": true,
21
+ "noUncheckedIndexedAccess": true,
22
+ "useUnknownInCatchVariables": true,
23
+ "skipLibCheck": true,
24
+ "forceConsistentCasingInFileNames": true,
25
+ "baseUrl": ".",
26
+ "paths": {
27
+ "@/*": ["./src/*"]
28
+ },
29
+ "types": ["vite/client", "node"]
30
+ },
31
+ "include": ["src", "tests"],
32
+ "references": [{ "path": "./tsconfig.node.json" }]
33
+ }
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "composite": true,
4
+ "skipLibCheck": true,
5
+ "module": "ESNext",
6
+ "moduleResolution": "Bundler",
7
+ "allowSyntheticDefaultImports": true,
8
+ "strict": true,
9
+ "noEmit": true,
10
+ "types": ["node"]
11
+ },
12
+ "include": ["vite.config.ts", "tailwind.config.ts", "postcss.config.cjs", "playwright.config.ts"]
13
+ }