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:
2026-04-07 03:55:24 -04:00
parent 27c30d7e50
commit 1c6d9075ad
12 changed files with 214 additions and 186 deletions

View File

@@ -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

View File

@@ -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])
}