
Security News
The Nightmare Before Deployment
Season’s greetings from Socket, and here’s to a calm end of year: clean dependencies, boring pipelines, no surprises.
@djangocfg/ext-base
Advanced tools
Base utilities and common code for building DjangoCFG extensions.
Part of DjangoCFG — modern Django framework for production-ready SaaS applications.
@djangocfg/ext-base provides the foundation for DjangoCFG extensions with:
Used internally by official extensions (newsletter, payments, support, leads, knowbase) and for building custom extensions.
pnpm add @djangocfg/ext-base
The package includes a CLI tool for creating new DjangoCFG extensions:
# Create a new extension with interactive wizard
djangocfg-ext create
# Quick test extension (auto-cleanup + unique name)
djangocfg-ext test
# List all extensions in workspace
djangocfg-ext list
# Show extension info
djangocfg-ext info <extension-name>
# Show help
djangocfg-ext help
The CLI will guide you through creating a new extension with proper structure and configuration using the createExtensionConfig helper.
Features:
@my-org/my-extension) and unscoped (my-extension) package namesUse the createExtensionConfig helper to automatically pull data from package.json:
// src/config.ts
import { createExtensionConfig } from '@djangocfg/ext-base';
import packageJson from '../package.json';
export const extensionConfig = createExtensionConfig(packageJson, {
name: 'my-extension',
displayName: 'My Extension',
icon: 'Rocket', // Lucide icon name
category: 'utilities',
features: [
'Feature 1',
'Feature 2',
'Feature 3',
],
minVersion: '2.0.0',
examples: [
{
title: 'Basic Usage',
description: 'How to use this extension',
code: `import { MyComponent } from '@your-org/my-extension';`,
language: 'tsx',
},
],
});
This automatically imports from package.json:
// src/contexts/MyExtensionProvider.tsx
'use client';
import { ExtensionProvider } from '@djangocfg/ext-base/hooks';
import { extensionConfig } from '../config';
export function MyExtensionProvider({ children }) {
return (
<ExtensionProvider metadata={extensionConfig}>
{children}
</ExtensionProvider>
);
}
import { MyExtensionProvider } from '@your-org/my-extension';
export default function RootLayout({ children }) {
return (
<MyExtensionProvider>
{children}
</MyExtensionProvider>
);
}
Every extension created with the CLI includes a playground - a Next.js 15 development environment for rapid testing and iteration.
cd your-extension
pnpm dev:playground
This command:
predev script)# Build extension
pnpm build
# Build playground
pnpm build:playground
# Type check
pnpm check
Extensions support a Smart Provider Pattern that allows components to work both standalone and with manual provider wrapping.
Create self-contained components that automatically wrap themselves with the provider when needed:
import { withSmartProvider } from '@your-org/my-extension';
// Your component
function MyComponent() {
const { data } = useMyExtension();
return <div>{data}</div>;
}
// Wrap with smart provider
export const MySmartComponent = withSmartProvider(MyComponent);
Usage:
// Works standalone (auto-wrapped)
<MySmartComponent />
// Also works with manual provider (shares context)
<MyExtensionProvider>
<MySmartComponent />
<MySmartComponent />
</MyExtensionProvider>
Benefits:
When to use:
When to use manual provider:
The createExtensionConfig helper creates a typed extension configuration by combining package.json data with manual metadata:
import { createExtensionConfig, type ExtensionConfigInput } from '@djangocfg/ext-base';
import packageJson from '../package.json';
export const extensionConfig = createExtensionConfig(packageJson, {
// Required fields
name: 'my-extension',
displayName: 'My Extension',
icon: 'Package', // Lucide icon name
category: 'utilities', // 'forms' | 'payments' | 'content' | 'support' | 'utilities' | 'analytics' | 'security' | 'integration' | 'other'
features: ['Feature list for marketplace'],
// Optional fields
minVersion: '2.0.0',
githubStars: 100,
examples: [
{
title: 'Example title',
description: 'Example description',
code: 'import { Component } from "@your-org/my-extension";',
language: 'tsx',
},
],
});
Automatically imported from package.json:
version - Package versionauthor - Author name (from string or object)description - Package descriptionlicense - License typehomepage - Homepage URLgithubUrl - Repository URLkeywords - Keywords arraypeerDependencies - Peer dependencies (workspace:* auto-replaced with latest)packageDependencies - Dependencies from package.json (workspace:* auto-replaced with latest)Auto-generated:
npmUrl - npm package URL (https://www.npmjs.com/package/[name])marketplaceId - URL-safe ID (@ and / replaced with -), e.g. @djangocfg/ext-newsletter → djangocfg-ext-newslettermarketplaceUrl - Marketplace URL (only for official @djangocfg extensions): https://hub.djangocfg.com/extensions/[marketplaceId]installCommand - pnpm install commanddownloadUrl - npm tarball download URLpreview - Preview image URL (https://unpkg.com/[name]@latest/preview.png)tags - Same as keywordsNote: All
workspace:*dependencies are automatically replaced withlatestfor marketplace display.
import { isDevelopment, isProduction, isStaticBuild } from '@djangocfg/ext-base';
if (isDevelopment) {
console.log('Running in development mode');
}
Create extension API instances with automatic configuration:
import { API } from './generated/ext_myextension';
import { createExtensionAPI } from '@djangocfg/ext-base/api';
// Handles API URL, static build detection, and shared auth automatically
export const apiMyExtension = createExtensionAPI(API);
import { usePagination, useInfinitePagination } from '@djangocfg/ext-base/hooks';
// Standard pagination
const { items, page, totalPages, goToPage, nextPage, prevPage } = usePagination({
keyPrefix: 'articles',
fetcher: async (page, pageSize) => {
const response = await api.articles.list({ page, page_size: pageSize });
return response.data;
},
pageSize: 20,
});
// Infinite scroll
const { items, isLoading, hasMore, loadMore } = useInfinitePagination({
keyPrefix: 'articles',
fetcher: async (page, pageSize) => api.articles.list({ page, page_size: pageSize }).then(r => r.data),
pageSize: 20,
});
import { createExtensionContext } from '@djangocfg/ext-base/hooks';
interface MyContextValue {
data: any[];
refresh: () => void;
}
const { Provider, useContext: useMyContext } = createExtensionContext<MyContextValue>({
displayName: 'MyContext',
errorMessage: 'useMyContext must be used within MyProvider',
});
import { createExtensionLogger } from '@djangocfg/ext-base';
const logger = createExtensionLogger({ tag: 'my-extension' });
logger.info('Extension initialized');
logger.error('Operation failed', error);
logger.success('Completed!');
import { useAuth } from '@djangocfg/ext-base/auth';
function MyComponent() {
const { user, isAuthenticated, login, logout } = useAuth();
return isAuthenticated ? (
<div>Welcome, {user?.email}</div>
) : (
<button onClick={login}>Login</button>
);
}
| Export | Description | Usage |
|---|---|---|
@djangocfg/ext-base | Server-safe exports (types, environment, API factory, logger, error handling) | Server & client components |
@djangocfg/ext-base/hooks | Client-only exports (ExtensionProvider, pagination hooks, context factory) | Client components only |
@djangocfg/ext-base/auth | Auth re-exports (useAuth, types) | Client components only |
@djangocfg/ext-base/api | API utilities (createExtensionAPI, getSharedAuthStorage) | Server & client components |
For your own extensions:
Extension templates automatically export:
extensionConfig - Extension metadata[Name]Provider - Main provider component (wraps ExtensionProvider)use[Name] - Context hook (required provider)use[Name]Optional - Context hook (optional provider)withSmartProvider - HOC for self-contained componentsServer-safe imports:
@your-org/ext-name/config to import extension metadata without loading React componentsThe package exports a standardized list of extension categories:
import { EXTENSION_CATEGORIES } from '@djangocfg/ext-base';
// Array of { title: string, value: ExtensionCategory }
EXTENSION_CATEGORIES.forEach(category => {
console.log(category.title, category.value);
// Forms, forms
// Payments, payments
// Content, content
// Support, support
// Utilities, utilities
// Analytics, analytics
// Security, security
// Integration, integration
// Other, other
});
Available categories:
forms - Forms, CRM, Lead Managementpayments - Payment Processing, Billingcontent - Content Management, Marketingsupport - Support, Helpdesk, Ticketsutilities - Tools, Base, Infrastructureanalytics - Analytics, Tracking, Monitoringsecurity - Security, Authentication, Authorizationintegration - Third-party Integrationsother - Other/Uncategorizedimport type {
// Extension configuration
ExtensionMetadata,
ExtensionConfigInput,
ExtensionCategory,
ExtensionExample,
// Provider
ExtensionProviderProps,
// Pagination
PaginatedResponse,
PaginationParams,
PaginationState,
InfinitePaginationReturn,
// Utilities
ExtensionLogger,
ExtensionError,
} from '@djangocfg/ext-base';
createExtensionConfig helper to maintain Single Source of Truth from package.jsonicon fieldfeatures list for marketplace visibilityexamples with proper syntax highlightingpreview.png file in your extension root (1200x630px recommended)preview.png in your package.json files array for npm publicationpnpm dev:playground)pnpm check)createExtensionLogger with consistent tags for structured loggingExtensionProvider for proper registration[Name]Provider (not [Name]ExtensionProvider)withSmartProvider for components that should work standalonepnpm build before publishingMIT
FAQs
Base utilities and common code for DjangoCFG extensions
We found that @djangocfg/ext-base demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Season’s greetings from Socket, and here’s to a calm end of year: clean dependencies, boring pipelines, no surprises.

Research
/Security News
Impostor NuGet package Tracer.Fody.NLog typosquats Tracer.Fody and its author, using homoglyph tricks, and exfiltrates Stratis wallet JSON/passwords to a Russian IP address.

Security News
Deno 2.6 introduces deno audit with a new --socket flag that plugs directly into Socket to bring supply chain security checks into the Deno CLI.