@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,47 @@
1
+ import { defineConfig } from 'vite';
2
+ import react from '@vitejs/plugin-react-swc';
3
+ import path from 'node:path';
4
+
5
+ // Vite config for BFSI starter. Notable:
6
+ // - SWC for fast transpile
7
+ // - Path alias '@/' → 'src/'
8
+ // - Server hardening headers (CSP set at deploy edge; dev runs without)
9
+ // - HMR exposed only on localhost
10
+ export default defineConfig({
11
+ plugins: [react()],
12
+ resolve: {
13
+ alias: {
14
+ '@': path.resolve(__dirname, './src'),
15
+ },
16
+ },
17
+ server: {
18
+ port: 5173,
19
+ strictPort: true,
20
+ host: 'localhost',
21
+ headers: {
22
+ 'X-Content-Type-Options': 'nosniff',
23
+ 'X-Frame-Options': 'DENY',
24
+ 'Referrer-Policy': 'strict-origin-when-cross-origin',
25
+ 'Permissions-Policy': 'geolocation=(), camera=(), microphone=()',
26
+ },
27
+ },
28
+ build: {
29
+ target: 'es2022',
30
+ sourcemap: true,
31
+ rollupOptions: {
32
+ output: {
33
+ manualChunks: {
34
+ react: ['react', 'react-dom', 'react-router-dom'],
35
+ forms: ['react-hook-form', 'zod', '@hookform/resolvers'],
36
+ i18n: ['react-i18next', 'i18next'],
37
+ },
38
+ },
39
+ },
40
+ },
41
+ test: {
42
+ environment: 'jsdom',
43
+ setupFiles: ['./tests/setup.ts'],
44
+ globals: true,
45
+ include: ['src/**/*.test.{ts,tsx}', 'tests/**/*.test.{ts,tsx}'],
46
+ },
47
+ });
@@ -0,0 +1,103 @@
1
+ ---
2
+ name: axios-auth
3
+ description: Configure the project's axios instance, response interceptor, and RTK Query baseQuery. Sets the auth token ONCE at login via setAuthToken() (not per-request), with response interceptor for notifications + 401 redirect. Use when the user mentions axios, API client, baseQuery, set token, login flow, 401 handling, response interceptor, error notification.
4
+ ---
5
+
6
+ # Axios + Auth Pattern
7
+
8
+ The HTTP layer is three files under `src/axiosconfig/` plus the auth-token helper from `@<scope>/core/http`. Tokens are set ONCE at login on the axios instance — not injected per-request via interceptor.
9
+
10
+ ## File map
11
+
12
+ | File | Role |
13
+ | ---------------------------------- | ----------------------------------------------------------------------------------------------- |
14
+ | `src/axiosconfig/axiosInstance.ts` | Single shared `AxiosInstance` from `createAxios()` |
15
+ | `src/axiosconfig/interceptor.ts` | Response interceptor: notifications + 401 redirect. Side-effect import from `axiosInstance.ts`. |
16
+ | `src/axiosconfig/baseQuery.ts` | `axiosBaseQuery()` for RTK Query — wraps the instance |
17
+ | `@<scope>/core/http` | Exports `createAxios`, `setAuthToken`, `clearAuthToken`, `ApiError` |
18
+
19
+ ## Workflow
20
+
21
+ ### Step 1 — Set token at login (the canonical place)
22
+
23
+ Inside your login mutation's success path:
24
+
25
+ ```ts
26
+ import { setAuthToken } from '@<scope>/core/http';
27
+ import axiosInstance from '@/axiosconfig/axiosInstance';
28
+
29
+ // After login API returns { token, ... }:
30
+ setAuthToken(axiosInstance, response.token);
31
+ ```
32
+
33
+ `setAuthToken` writes to `instance.defaults.headers.common.Authorization` (or whatever `authHeaderName` was set). Every subsequent request carries the header automatically — no per-request interceptor needed.
34
+
35
+ ### Step 2 — Clear on logout / 401
36
+
37
+ `createAxios()` already wires this: when a response is 401, it auto-calls `clearAuthToken(instance)` then invokes the `onUnauthorized` callback (which redirects to `/login` in the scaffolded template).
38
+
39
+ Manual logout flow:
40
+
41
+ ```ts
42
+ import { clearAuthToken } from '@<scope>/core/http';
43
+ import axiosInstance from '@/axiosconfig/axiosInstance';
44
+
45
+ clearAuthToken(axiosInstance);
46
+ // then dispatch any session-cleanup action and navigate
47
+ ```
48
+
49
+ ### Step 3 — Wire notification dispatch in the interceptor (optional)
50
+
51
+ The scaffolded `interceptor.ts` has commented-out placeholders for notification dispatch. When you set up a Notification slice, uncomment + adapt:
52
+
53
+ ```ts
54
+ import store from '@/redux/store';
55
+ import { setNotification } from '@/shared/Notification/slice';
56
+
57
+ // inside the response error handler:
58
+ store.dispatch(setNotification({ type: 'error', message: extractMessage(err) }));
59
+ ```
60
+
61
+ See [`references/notification-wiring.md`](references/notification-wiring.md) for the full pattern.
62
+
63
+ ### Step 4 — Use `axiosBaseQuery` in feature APIs
64
+
65
+ ```ts
66
+ import { createApi } from '@reduxjs/toolkit/query/react';
67
+ import axiosBaseQuery from '@/axiosconfig/baseQuery';
68
+
69
+ export const fooApi = createApi({
70
+ reducerPath: 'fooApi',
71
+ baseQuery: axiosBaseQuery(),
72
+ tagTypes: ['Foo'],
73
+ endpoints: (builder) => ({
74
+ /* ... */
75
+ }),
76
+ });
77
+ ```
78
+
79
+ Each endpoint can opt-in to success/error notifications via `showSuccessNotification` / `showFailureNotification` flags on the query object.
80
+
81
+ ## Conventions enforced
82
+
83
+ - ❌ NEVER inject token via a `request.use` interceptor — use `setAuthToken` at login.
84
+ - ❌ NEVER read token from `localStorage` on every request.
85
+ - ❌ NEVER hardcode `Authorization: Bearer ...` anywhere.
86
+ - ✅ One axios instance per app — exported as the default from `axiosInstance.ts`.
87
+ - ✅ Side-effect import of `'./interceptor.js'` in `axiosInstance.ts` so response interceptors are always registered.
88
+ - ✅ All RTK Query APIs use `axiosBaseQuery()` (never the default `fetchBaseQuery`).
89
+
90
+ ## Quick reference checklist
91
+
92
+ When adding API auth:
93
+
94
+ - [ ] Login mutation calls `setAuthToken(axiosInstance, token)` on success
95
+ - [ ] Logout / 401 path calls `clearAuthToken(axiosInstance)`
96
+ - [ ] Token never appears in `localStorage` (memory only, set on the instance)
97
+ - [ ] `onUnauthorized` callback in `axiosInstance.ts` redirects to `/login`
98
+
99
+ ## References
100
+
101
+ - [`references/full-code-walkthrough.md`](references/full-code-walkthrough.md) — annotated walk through all three files in the scaffolded project
102
+ - [`references/notification-wiring.md`](references/notification-wiring.md) — how to wire `showSuccessNotification` / `showFailureNotification` flags to a Notification slice
103
+ - [`references/error-shape.md`](references/error-shape.md) — backend error contract (`ApiError` kinds, field-error format)
@@ -0,0 +1,84 @@
1
+ # Error shape contract
2
+
3
+ The HTTP layer normalises errors so feature code can pattern-match instead of digging through raw axios errors.
4
+
5
+ ## `ApiError` (from `@<scope>/core/http`)
6
+
7
+ ```ts
8
+ class ApiError extends Error {
9
+ readonly kind: ApiErrorKind;
10
+ readonly status?: number;
11
+ readonly ref?: string;
12
+ readonly fieldErrors?: Record<string, string>;
13
+ }
14
+
15
+ type ApiErrorKind =
16
+ | 'network' // no response, offline, connection refused
17
+ | 'timeout' // request exceeded timeout
18
+ | 'unauthorized' // 401
19
+ | 'forbidden' // 403
20
+ | 'not_found' // 404
21
+ | 'conflict' // 409
22
+ | 'validation' // 422 — fieldErrors populated
23
+ | 'rate_limited' // 429
24
+ | 'server_error' // 5xx
25
+ | 'cancelled' // user cancelled / abort signal
26
+ | 'unknown';
27
+ ```
28
+
29
+ `createAxios` attaches an error-mapping interceptor that converts every axios error into `ApiError`. Inside `axiosBaseQuery`, you'll catch this — re-throw is fine; RTK Query stores it in `endpoint.error`.
30
+
31
+ ## Backend error envelope
32
+
33
+ The starter's `axiosBaseQuery` expects backends to return errors as:
34
+
35
+ ```json
36
+ {
37
+ "errors": [{ "detail": "Email is already taken" }]
38
+ }
39
+ ```
40
+
41
+ Or for field-level (422):
42
+
43
+ ```json
44
+ {
45
+ "errors": {
46
+ "email": ["is already taken"],
47
+ "password": ["is too short"]
48
+ }
49
+ }
50
+ ```
51
+
52
+ If your backend uses a different shape, override `extractMessage()` in `interceptor.ts` and adjust `axiosBaseQuery`'s error branch.
53
+
54
+ ## Surfacing errors in containers
55
+
56
+ ```tsx
57
+ import type { ApiError } from '@<scope>/core/http';
58
+
59
+ const [createFoo, { error, isError }] = useCreateFooMutation();
60
+
61
+ if (isError && error) {
62
+ const e = error as ApiError;
63
+ if (e.kind === 'validation' && e.fieldErrors) {
64
+ // Set field-level errors on RHF:
65
+ for (const [field, msg] of Object.entries(e.fieldErrors)) {
66
+ form.setError(field as keyof FormValues, { message: msg });
67
+ }
68
+ } else if (e.kind === 'unauthorized') {
69
+ // Already handled by axios's onUnauthorized — no action needed.
70
+ } else {
71
+ // Generic toast — interceptor handles this if showFailureNotification was true.
72
+ }
73
+ }
74
+ ```
75
+
76
+ ## NEVER expose to UI
77
+
78
+ Per the `bfsi-error-message` reference skill in the toolkit:
79
+
80
+ - ❌ `error.message` from raw axios — leaks "Network Error" / stack info
81
+ - ❌ `error.response.data.errors` raw — may contain SQL fragments, DB IDs
82
+ - ❌ HTTP status codes as user copy ("Error 500")
83
+
84
+ Use the safe-error mapping (`toSafeView` from `@<scope>/core/compliance`) to convert `ApiError.kind` into a user-facing toast title + description + ref code.
@@ -0,0 +1,146 @@
1
+ # Axios + Auth — full code walkthrough
2
+
3
+ Annotated tour of the three files the scaffolder lays down.
4
+
5
+ ## `src/axiosconfig/axiosInstance.ts`
6
+
7
+ ```ts
8
+ import { createAxios } from '@<scope>/core/http';
9
+ import { env } from '../env.js';
10
+ import './interceptor.js'; // side-effect: registers response interceptor
11
+
12
+ const axiosInstance = createAxios({
13
+ baseURL: env.VITE_API_BASE_URL,
14
+ timeoutMs: env.VITE_API_TIMEOUT_MS,
15
+ authHeaderName: env.VITE_AUTH_HEADER_NAME, // default 'Authorization'
16
+ snakeCaseBackend: false, // flip true for Rails/Python
17
+ onUnauthorized: () => {
18
+ if (typeof window !== 'undefined') {
19
+ window.location.href = '/login';
20
+ }
21
+ },
22
+ });
23
+
24
+ export default axiosInstance;
25
+ ```
26
+
27
+ Key points:
28
+
29
+ - `createAxios` (from core) attaches the BFSI-grade interceptors: request-IDs, idempotency keys, error mapping to typed `ApiError`.
30
+ - The `onUnauthorized` callback fires AFTER `clearAuthToken` has already wiped the token off the instance.
31
+ - The side-effect `import './interceptor.js'` is critical — it registers the response interceptor when `axiosInstance` is first imported.
32
+ - `snakeCaseBackend: true` enables automatic snake↔camel transformation on bodies and responses.
33
+
34
+ ## `src/axiosconfig/interceptor.ts`
35
+
36
+ ```ts
37
+ import type { AxiosError, AxiosResponse } from 'axios';
38
+ import axiosInstance from './axiosInstance.js';
39
+ // import store from '../redux/store.js';
40
+ // import { setNotification } from '../shared/Notification/slice.js';
41
+
42
+ export interface ApiErrorShape {
43
+ config?: { url?: string };
44
+ response?: {
45
+ status?: number;
46
+ data?: {
47
+ errors?: Array<{ detail?: string; details?: string }> | Record<string, string[]>;
48
+ message?: string;
49
+ };
50
+ };
51
+ }
52
+
53
+ axiosInstance.interceptors.response.use(
54
+ (response: AxiosResponse) => response,
55
+ (error: AxiosError) => {
56
+ const err = error as unknown as ApiErrorShape;
57
+ const status = err.response?.status;
58
+
59
+ // Wire to your Notification slice when ready:
60
+ // store.dispatch(setNotification({ type: 'error', message: extractMessage(err) }));
61
+
62
+ if (status === 401) {
63
+ // createAxios's onUnauthorized has already fired (cleared token + navigated).
64
+ // Add any additional auth-cleanup here.
65
+ }
66
+
67
+ return Promise.reject(err);
68
+ },
69
+ );
70
+ ```
71
+
72
+ Notes:
73
+
74
+ - This file ONLY adds the response side. Request-side concerns (auth header, X-Request-Id, Idempotency-Key) are owned by `createAxios`'s built-in interceptors.
75
+ - 401 is double-handled: once in `createAxios` (clears token) and once here (extra cleanup if needed).
76
+ - The notification dispatch is commented-out scaffolding; wire it when you create the Notification slice.
77
+
78
+ ## `src/axiosconfig/baseQuery.ts`
79
+
80
+ ```ts
81
+ import type { AxiosRequestConfig, AxiosResponse, ResponseType } from 'axios';
82
+ import type { BaseQueryFn } from '@reduxjs/toolkit/query';
83
+ import axiosInstance from './axiosInstance.js';
84
+ import type { ApiErrorShape } from './interceptor.js';
85
+
86
+ const GET = 'GET' as const;
87
+
88
+ const axiosBaseQuery =
89
+ (): BaseQueryFn<
90
+ {
91
+ url: string;
92
+ method?: AxiosRequestConfig['method'];
93
+ data?: AxiosRequestConfig['data'];
94
+ params?: AxiosRequestConfig['params'];
95
+ headers?: AxiosRequestConfig['headers'];
96
+ showSuccessNotification?: boolean;
97
+ showFailureNotification?: boolean;
98
+ responseType?: ResponseType;
99
+ },
100
+ unknown,
101
+ unknown
102
+ > =>
103
+ async ({ url, method, data, params, headers, responseType }) => {
104
+ try {
105
+ const requestConfig: AxiosRequestConfig =
106
+ method === GET
107
+ ? { url, method, params: params ?? data, headers }
108
+ : { url, method, data, params, headers };
109
+
110
+ if (responseType) requestConfig.responseType = responseType;
111
+
112
+ const result: AxiosResponse = await axiosInstance(requestConfig);
113
+ return { data: result.data };
114
+ } catch (axiosError) {
115
+ const error = axiosError as ApiErrorShape;
116
+ return {
117
+ error: {
118
+ status: error.response?.status,
119
+ data: error.response?.data?.errors,
120
+ },
121
+ };
122
+ }
123
+ };
124
+
125
+ export default axiosBaseQuery;
126
+ ```
127
+
128
+ Notes:
129
+
130
+ - Endpoints pass `data` for both GET and non-GET — `axiosBaseQuery` routes it correctly (`params` for GET, `data` for others).
131
+ - `showSuccessNotification` / `showFailureNotification` are flags that get inspected here. Wiring goes to the Notification slice once that's set up.
132
+ - Returns the RTK Query `{ data }` / `{ error }` shape. Error.data is the raw `errors` payload — features format it for display.
133
+
134
+ ## Order of imports matters
135
+
136
+ ```
137
+ axiosInstance.ts ─── imports → interceptor.ts (side-effect)
138
+
139
+ ▼ (default export)
140
+ baseQuery.ts ─── imports → axiosInstance (same singleton)
141
+
142
+ ▼ (default export)
143
+ features/<X>/api.ts ─── createApi({ baseQuery: axiosBaseQuery() })
144
+ ```
145
+
146
+ Anyone calling `axiosInstance` (directly or via baseQuery) gets a fully-interceptor-wired instance because of the side-effect import chain.
@@ -0,0 +1,141 @@
1
+ # Notification wiring
2
+
3
+ `axiosBaseQuery` and `interceptor.ts` both have commented-out hooks for dispatching toast notifications. This is how you wire them once you have a Notification slice.
4
+
5
+ ## 1. Create the slice
6
+
7
+ `src/shared/Notification/slice.ts`:
8
+
9
+ ```ts
10
+ import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
11
+
12
+ export type NotificationType = 'success' | 'error' | 'info' | 'warning';
13
+
14
+ interface NotificationState {
15
+ type: NotificationType | null;
16
+ message: string;
17
+ }
18
+
19
+ const initialState: NotificationState = { type: null, message: '' };
20
+
21
+ const notificationSlice = createSlice({
22
+ name: 'notification',
23
+ initialState,
24
+ reducers: {
25
+ setNotification: (
26
+ state,
27
+ action: PayloadAction<{ type: NotificationType; message: string }>,
28
+ ) => {
29
+ state.type = action.payload.type;
30
+ state.message = action.payload.message;
31
+ },
32
+ clearNotification: (state) => {
33
+ state.type = null;
34
+ state.message = '';
35
+ },
36
+ },
37
+ });
38
+
39
+ export const { setNotification, clearNotification } = notificationSlice.actions;
40
+ export const notificationReducer = notificationSlice.reducer;
41
+ ```
42
+
43
+ Register it in `src/redux/rootReducer.ts`:
44
+
45
+ ```ts
46
+ import { notificationReducer } from '@/shared/Notification/slice';
47
+
48
+ const rootReducer = combineReducers({
49
+ notification: notificationReducer,
50
+ // ...
51
+ });
52
+ ```
53
+
54
+ ## 2. Wire into the response interceptor
55
+
56
+ Uncomment in `src/axiosconfig/interceptor.ts`:
57
+
58
+ ```ts
59
+ import store from '../redux/store.js';
60
+ import { setNotification } from '../shared/Notification/slice.js';
61
+
62
+ // inside the response error handler:
63
+ store.dispatch(
64
+ setNotification({
65
+ type: 'error',
66
+ message: extractMessage(err),
67
+ }),
68
+ );
69
+ ```
70
+
71
+ A typical `extractMessage`:
72
+
73
+ ```ts
74
+ function extractMessage(err: ApiErrorShape): string {
75
+ const errors = err.response?.data?.errors;
76
+ if (Array.isArray(errors) && errors[0]) {
77
+ return errors[0].detail ?? errors[0].details ?? 'Request failed';
78
+ }
79
+ if (errors && typeof errors === 'object') {
80
+ const firstKey = Object.keys(errors)[0];
81
+ const firstVal = firstKey ? errors[firstKey]?.[0] : undefined;
82
+ return firstVal ?? 'Validation failed';
83
+ }
84
+ return err.response?.data?.message ?? 'Request failed';
85
+ }
86
+ ```
87
+
88
+ ## 3. Wire into `axiosBaseQuery`
89
+
90
+ Each endpoint sets `showSuccessNotification` / `showFailureNotification`. Adapt `baseQuery.ts`:
91
+
92
+ ```ts
93
+ import store from '../redux/store.js';
94
+ import { setNotification } from '../shared/Notification/slice.js';
95
+
96
+ // success branch:
97
+ if (showSuccessNotification) {
98
+ store.dispatch(setNotification({ type: 'success', message: result.data?.message }));
99
+ }
100
+
101
+ // failure branch:
102
+ if (showFailureNotification) {
103
+ store.dispatch(setNotification({ type: 'error', message: extractMessage(error) }));
104
+ }
105
+ ```
106
+
107
+ ## 4. Render the notifications
108
+
109
+ A small `<Notification />` component listens to the slice and renders toast UI (shadcn `<Toast>`, Sonner, etc.):
110
+
111
+ ```tsx
112
+ import { useEffect } from 'react';
113
+ import { useAppSelector, useAppDispatch } from '@/redux/reduxHooks';
114
+ import { clearNotification } from './slice';
115
+ import { toast } from 'sonner';
116
+
117
+ export function Notification() {
118
+ const { type, message } = useAppSelector((s) => s.notification);
119
+ const dispatch = useAppDispatch();
120
+
121
+ useEffect(() => {
122
+ if (!type) return;
123
+ toast[type](message);
124
+ const t = setTimeout(() => dispatch(clearNotification()), 3000);
125
+ return () => clearTimeout(t);
126
+ }, [type, message, dispatch]);
127
+
128
+ return null;
129
+ }
130
+ ```
131
+
132
+ Mount it once in `App.tsx`:
133
+
134
+ ```tsx
135
+ <Provider store={store}>
136
+ <Notification />
137
+ <BrowserRouter>
138
+ <AppRoutes />
139
+ </BrowserRouter>
140
+ </Provider>
141
+ ```
@@ -0,0 +1,112 @@
1
+ ---
2
+ name: constants-organization
3
+ description: Where each kind of constant lives in the project and how to add a new one. Covers URL endpoint constants, HTTP method constants, route paths, RTK Query tag types, validation regex patterns, and app-wide constants. Use when adding a new endpoint URL, route path, tag type, validation regex, or any other shared constant.
4
+ ---
5
+
6
+ # Constants Organisation
7
+
8
+ Constants are split across files by purpose. Each kind has one home — don't scatter.
9
+
10
+ ## File map
11
+
12
+ ```
13
+ src/utils/constants/
14
+ ├── apiConstants.ts HTTP method names, API version, base URL helpers
15
+ ├── appConstants.ts App-wide enums (ERROR / SUCCESS, storage keys, etc.)
16
+ ├── urlConstants.ts Every API endpoint URL string
17
+ ├── routeConstants.ts Every router path
18
+ ├── tagTypes.ts RTK Query cache tag type names
19
+ └── regexConstants.ts Reusable validation regex
20
+ ```
21
+
22
+ ## When to use which
23
+
24
+ | Adding … | Goes in | Naming convention |
25
+ | ---------------------------- | ------------------- | ---------------------------------------------- |
26
+ | A backend endpoint URL | `urlConstants.ts` | `<FEATURE>_URLS.<ACTION>` or `<FEATURE>_URL` |
27
+ | A frontend route path | `routeConstants.ts` | `ROUTES.<feature>.<view>` or `<FEATURE>_ROUTE` |
28
+ | An HTTP method (GET/POST/…) | `apiConstants.ts` | `GET`, `POST` constants |
29
+ | An RTK Query tag type | `tagTypes.ts` | `<Feature>Tag` literal in array |
30
+ | A validation regex | `regexConstants.ts` | `<FIELD>_REGEX` |
31
+ | A storage key | `appConstants.ts` | `<PURPOSE>_KEY` |
32
+ | An enum value used > 1 place | `appConstants.ts` | `<NAME>` (UPPER_SNAKE) |
33
+
34
+ ## Workflow — adding a new endpoint
35
+
36
+ 1. Open `src/utils/constants/urlConstants.ts`.
37
+ 2. Add to the appropriate feature block (or create one):
38
+ ```ts
39
+ export const KYC_URLS = {
40
+ LIST: '/kyc',
41
+ DETAIL: (id: string) => `/kyc/${id}`,
42
+ SUBMIT: '/kyc/submit',
43
+ } as const;
44
+ ```
45
+ 3. Reference from the feature's `api.ts`:
46
+ ```ts
47
+ import { KYC_URLS } from '@/utils/constants/urlConstants';
48
+ ```
49
+ 4. If this endpoint introduces a new tag type, add it to `tagTypes.ts` and to the feature API's `tagTypes` array.
50
+
51
+ See [`references/example-files.md`](references/example-files.md) for full file templates.
52
+
53
+ ## Workflow — adding a new route
54
+
55
+ 1. Open `src/utils/constants/routeConstants.ts`.
56
+ 2. Add the path:
57
+ ```ts
58
+ export const ROUTES = {
59
+ kyc: {
60
+ list: '/kyc',
61
+ detail: '/kyc/:id',
62
+ submit: '/kyc/submit',
63
+ },
64
+ } as const;
65
+ ```
66
+ 3. Wire it in `src/routes/index.tsx`:
67
+ ```tsx
68
+ <Route
69
+ path={ROUTES.kyc.list}
70
+ element={
71
+ <ProtectedRoute permission="kyc.view">
72
+ <KycList />
73
+ </ProtectedRoute>
74
+ }
75
+ />
76
+ ```
77
+
78
+ ## Workflow — adding a validation regex
79
+
80
+ Always centralise — never inline a regex in a Zod schema.
81
+
82
+ 1. Open `src/utils/constants/regexConstants.ts`.
83
+ 2. Add with documentation:
84
+ ```ts
85
+ // PAN: 5 letters + 4 digits + 1 letter
86
+ export const PAN_REGEX = /^[A-Z]{5}[0-9]{4}[A-Z]$/;
87
+ ```
88
+ 3. Use in Zod:
89
+
90
+ ```ts
91
+ import { PAN_REGEX } from '@/utils/constants/regexConstants';
92
+
93
+ const schema = z.object({
94
+ pan: z.string().regex(PAN_REGEX, { message: 'Invalid PAN' }),
95
+ });
96
+ ```
97
+
98
+ The starter's `@<scope>/core/pii` already exports `PII_PATTERNS` with common BFSI regexes (PAN, Aadhaar, IFSC, mobile, email, etc.). Re-export those from `regexConstants.ts` rather than redefining.
99
+
100
+ ## Conventions enforced
101
+
102
+ - ❌ NEVER hardcode a URL string in `api.ts` — always reference `urlConstants.ts`.
103
+ - ❌ NEVER inline a regex in component code or schema — always via `regexConstants.ts`.
104
+ - ❌ NEVER use a magic string for tag types — always via `tagTypes.ts`.
105
+ - ✅ Group by feature within each constants file (e.g. `KYC_URLS`, `LOAN_URLS`).
106
+ - ✅ Use `as const` on the exported objects so TypeScript infers literal types.
107
+ - ✅ Functions for dynamic paths (`DETAIL: (id) => ...`), strings for static ones.
108
+
109
+ ## References
110
+
111
+ - [`references/example-files.md`](references/example-files.md) — full templates for each constants file
112
+ - [`references/tag-types-catalog.md`](references/tag-types-catalog.md) — RTK Query tag type naming + when each is invalidated