Remove all Django-specific naming from mizan-react
Renamed: DjangoError → MizanError DjangoHTTPClient → MizanHTTPClient DjangoFormState → MizanFormState DjangoFormsetState → MizanFormsetState createDjangoCSRClient → createMizanCSRClient createDjangoSSRClient → createMizanSSRClient ensureDjangoSession → ensureMizanSession useDjangoCSRClient → useMizanCSRClient TDjangoMessage → TServerMessage Made CSRF configurable: configureCsrf(cookieName, headerName) — defaults to Django conventions but works with any backend that uses CSRF tokens. Cookie name and header name are no longer hardcoded. All old names preserved as deprecated aliases in index.ts exports for backwards compatibility. Removed dead RouterAdapter re-export (file moved to legacy/). 33 React tests pass. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -8,22 +8,22 @@
|
||||
*
|
||||
* ### Client-Side (CSR)
|
||||
* ```ts
|
||||
* import { createDjangoCSRClient, Auth } from 'mizan/client'
|
||||
* import { createMizanCSRClient, Auth } from 'mizan/client'
|
||||
*
|
||||
* // Session-based (cookies + CSRF)
|
||||
* const client = createDjangoCSRClient(Auth.SESSION)
|
||||
* const client = createMizanCSRClient(Auth.SESSION)
|
||||
*
|
||||
* // JWT-based (Bearer token)
|
||||
* const client = createDjangoCSRClient(Auth.JWT, { getAccessToken })
|
||||
* const client = createMizanCSRClient(Auth.JWT, { getAccessToken })
|
||||
*
|
||||
* const user = await client.json('GET', '/api/accounts/me/')
|
||||
* ```
|
||||
*
|
||||
* ### Server-Side (SSR)
|
||||
* ```ts
|
||||
* import { createDjangoSSRClient } from 'mizan/client'
|
||||
* import { createMizanSSRClient } from 'mizan/client'
|
||||
*
|
||||
* const client = createDjangoSSRClient({
|
||||
* const client = createMizanSSRClient({
|
||||
* cookies: await cookies() // Next.js cookies()
|
||||
* })
|
||||
*
|
||||
@@ -76,7 +76,7 @@ export type SSRCookies = CookieGetter | {
|
||||
/**
|
||||
* The core HTTP client interface for Django requests.
|
||||
*/
|
||||
export interface DjangoHTTPClient {
|
||||
export interface MizanHTTPClient {
|
||||
/**
|
||||
* Make an HTTP request, returning the raw Response.
|
||||
*/
|
||||
@@ -148,7 +148,7 @@ export class HttpError extends Error {
|
||||
// Internal Utilities
|
||||
// =============================================================================
|
||||
|
||||
import { getCSRFToken } from '../utils'
|
||||
import { getCSRFToken, getCsrfHeaderName, getCsrfCookieName } from '../utils'
|
||||
|
||||
interface RequestBuild {
|
||||
request: RequestInit
|
||||
@@ -218,24 +218,24 @@ async function buildHttpError(resp: Response, url: URL | string): Promise<HttpEr
|
||||
*
|
||||
* @param auth - Authentication strategy (Auth.SESSION or Auth.JWT)
|
||||
* @param config - Client configuration
|
||||
* @returns DjangoHTTPClient
|
||||
* @returns MizanHTTPClient
|
||||
*
|
||||
* @example
|
||||
* // Session-based
|
||||
* const client = createDjangoCSRClient(Auth.SESSION)
|
||||
* const client = createMizanCSRClient(Auth.SESSION)
|
||||
*
|
||||
* @example
|
||||
* // JWT-based
|
||||
* const client = createDjangoCSRClient(Auth.JWT, {
|
||||
* const client = createMizanCSRClient(Auth.JWT, {
|
||||
* getAccessToken: async () => localStorage.getItem('token')
|
||||
* })
|
||||
*/
|
||||
export function createDjangoCSRClient(auth: Auth.SESSION, config?: CSRClientConfig): DjangoHTTPClient
|
||||
export function createDjangoCSRClient(auth: Auth.JWT, config: JWTClientConfig): DjangoHTTPClient
|
||||
export function createDjangoCSRClient(
|
||||
export function createMizanCSRClient(auth: Auth.SESSION, config?: CSRClientConfig): MizanHTTPClient
|
||||
export function createMizanCSRClient(auth: Auth.JWT, config: JWTClientConfig): MizanHTTPClient
|
||||
export function createMizanCSRClient(
|
||||
auth: Auth,
|
||||
config?: CSRClientConfig | JWTClientConfig
|
||||
): DjangoHTTPClient {
|
||||
): MizanHTTPClient {
|
||||
if (!config?.baseUrl) {
|
||||
throw new Error(
|
||||
'baseUrl is required. Pass it via config or use MizanProvider which provides it automatically.'
|
||||
@@ -254,7 +254,7 @@ export function createDjangoCSRClient(
|
||||
return {}
|
||||
}
|
||||
// Session auth uses CSRF
|
||||
return { 'X-CSRFToken': getCSRFToken() ?? '' }
|
||||
return { [getCsrfHeaderName()]: getCSRFToken() ?? '' }
|
||||
}
|
||||
|
||||
function resolveUrl(path: string): string {
|
||||
@@ -318,7 +318,7 @@ function isCookieGetter(cookies: SSRCookies): cookies is CookieGetter {
|
||||
function extractCookies(cookies: SSRCookies): { csrf: string; cookieHeader: string } {
|
||||
if (isCookieGetter(cookies)) {
|
||||
return {
|
||||
csrf: cookies.get('csrftoken')?.value ?? '',
|
||||
csrf: cookies.get(getCsrfCookieName())?.value ?? '',
|
||||
cookieHeader: cookies.getAll().map(c => `${c.name}=${c.value}`).join('; ')
|
||||
}
|
||||
}
|
||||
@@ -330,13 +330,13 @@ function extractCookies(cookies: SSRCookies): { csrf: string; cookieHeader: stri
|
||||
* Used in SSR contexts (Next.js server components, server actions, etc.)
|
||||
*
|
||||
* @param config - SSR client configuration with cookies
|
||||
* @returns DjangoHTTPClient
|
||||
* @returns MizanHTTPClient
|
||||
*
|
||||
* @example
|
||||
* // Next.js server component
|
||||
* import { cookies } from 'next/headers'
|
||||
*
|
||||
* const client = createDjangoSSRClient({ cookies: await cookies() })
|
||||
* const client = createMizanSSRClient({ cookies: await cookies() })
|
||||
*/
|
||||
// Re-export auth types for non-React usage
|
||||
export type {
|
||||
@@ -348,10 +348,7 @@ export type {
|
||||
JWTState,
|
||||
} from './types'
|
||||
|
||||
// Re-export RouterAdapter for libraries that extend it
|
||||
export type { RouterAdapter } from './RouterContext'
|
||||
|
||||
export function createDjangoSSRClient(config: SSRClientConfig): DjangoHTTPClient {
|
||||
export function createMizanSSRClient(config: SSRClientConfig): MizanHTTPClient {
|
||||
const baseUrl = getInternalBackendUrl(config.baseUrl)
|
||||
const { csrf, cookieHeader } = extractCookies(config.cookies)
|
||||
|
||||
@@ -361,7 +358,7 @@ export function createDjangoSSRClient(config: SSRClientConfig): DjangoHTTPClient
|
||||
|
||||
const requestHeaders: Record<string, string> = {
|
||||
'Accept': 'application/json',
|
||||
'X-CSRFToken': csrf,
|
||||
[getCsrfHeaderName()]: csrf,
|
||||
'Cookie': cookieHeader,
|
||||
...headers,
|
||||
}
|
||||
@@ -390,7 +387,7 @@ export function createDjangoSSRClient(config: SSRClientConfig): DjangoHTTPClient
|
||||
|
||||
const requestHeaders: Record<string, string> = {
|
||||
'Accept': 'application/json',
|
||||
'X-CSRFToken': csrf,
|
||||
[getCsrfHeaderName()]: csrf,
|
||||
'Cookie': cookieHeader,
|
||||
...headers,
|
||||
}
|
||||
@@ -454,12 +451,12 @@ interface SessionInitResponse {
|
||||
* @example
|
||||
* // In layout.tsx
|
||||
* const cookieStore = await cookies()
|
||||
* const session = await ensureDjangoSession({ cookies: cookieStore })
|
||||
* const client = createDjangoSSRClient({
|
||||
* const session = await ensureMizanSession({ cookies: cookieStore })
|
||||
* const client = createMizanSSRClient({
|
||||
* cookies: { csrf: session.csrf, cookieHeader: session.cookieHeader }
|
||||
* })
|
||||
*/
|
||||
export async function ensureDjangoSession(config: SSRClientConfig): Promise<{
|
||||
export async function ensureMizanSession(config: SSRClientConfig): Promise<{
|
||||
csrf: string
|
||||
cookieHeader: string
|
||||
}> {
|
||||
@@ -510,7 +507,7 @@ export async function ensureDjangoSession(config: SSRClientConfig): Promise<{
|
||||
|
||||
// Re-export error types from the canonical location
|
||||
export type { FunctionErrorResponse } from '../errors'
|
||||
import { DjangoError, type FunctionErrorResponse } from '../errors'
|
||||
import { MizanError, type FunctionErrorResponse } from '../errors'
|
||||
|
||||
/**
|
||||
* Success response from a server function
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
import { useMemo } from 'react'
|
||||
import { useJWT } from '../jwt/JWTContext'
|
||||
import {
|
||||
createDjangoCSRClient,
|
||||
createMizanCSRClient,
|
||||
Auth,
|
||||
type DjangoHTTPClient,
|
||||
type MizanHTTPClient,
|
||||
type CSRClientConfig,
|
||||
} from './index'
|
||||
|
||||
@@ -22,19 +22,19 @@ export type * from './types'
|
||||
*
|
||||
* @param auth - Authentication strategy (Auth.SESSION or Auth.JWT)
|
||||
* @param config - Optional client configuration
|
||||
* @returns DjangoHTTPClient
|
||||
* @returns MizanHTTPClient
|
||||
*
|
||||
* @example
|
||||
* // Session-based
|
||||
* const client = useDjangoCSRClient(Auth.SESSION)
|
||||
* const client = useMizanCSRClient(Auth.SESSION)
|
||||
* const user = await client.json('GET', '/api/accounts/me/')
|
||||
*
|
||||
* @example
|
||||
* // JWT-based (requires JWTContext from mizan/jwt)
|
||||
* const client = useDjangoCSRClient(Auth.JWT)
|
||||
* const client = useMizanCSRClient(Auth.JWT)
|
||||
* const user = await client.json('GET', '/api/accounts/me/')
|
||||
*/
|
||||
export function useDjangoCSRClient(auth: Auth, config?: CSRClientConfig): DjangoHTTPClient {
|
||||
export function useMizanCSRClient(auth: Auth, config?: CSRClientConfig): MizanHTTPClient {
|
||||
// Always call useJWT (React hooks must be unconditional)
|
||||
// Returns null when outside JWTContext
|
||||
const jwtContext = useJWT()
|
||||
@@ -43,16 +43,16 @@ export function useDjangoCSRClient(auth: Auth, config?: CSRClientConfig): Django
|
||||
if (auth === Auth.JWT) {
|
||||
if (!jwtContext?.getAccessToken) {
|
||||
throw new Error(
|
||||
'useDjangoCSRClient(Auth.JWT) requires JWTContext from mizan/jwt. ' +
|
||||
'useMizanCSRClient(Auth.JWT) requires JWTContext from mizan/jwt. ' +
|
||||
'Wrap your component in JWTContext to use JWT authentication.'
|
||||
)
|
||||
}
|
||||
return createDjangoCSRClient(Auth.JWT, {
|
||||
return createMizanCSRClient(Auth.JWT, {
|
||||
...config,
|
||||
getAccessToken: jwtContext.getAccessToken,
|
||||
})
|
||||
}
|
||||
|
||||
return createDjangoCSRClient(Auth.SESSION, config)
|
||||
return createMizanCSRClient(Auth.SESSION, config)
|
||||
}, [auth, config, jwtContext?.getAccessToken])
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user