@djangocfg/ext-base 1.0.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 (43) hide show
  1. package/README.md +346 -0
  2. package/dist/api.cjs +41 -0
  3. package/dist/api.d.cts +35 -0
  4. package/dist/api.d.ts +35 -0
  5. package/dist/api.js +2 -0
  6. package/dist/auth.cjs +10 -0
  7. package/dist/auth.d.cts +1 -0
  8. package/dist/auth.d.ts +1 -0
  9. package/dist/auth.js +2 -0
  10. package/dist/chunk-3RG5ZIWI.js +8 -0
  11. package/dist/chunk-MECBWZG4.js +44 -0
  12. package/dist/chunk-YQGNYUBX.js +67 -0
  13. package/dist/hooks.cjs +190 -0
  14. package/dist/hooks.d.cts +96 -0
  15. package/dist/hooks.d.ts +96 -0
  16. package/dist/hooks.js +65 -0
  17. package/dist/index.cjs +131 -0
  18. package/dist/index.d.cts +246 -0
  19. package/dist/index.d.ts +246 -0
  20. package/dist/index.js +3 -0
  21. package/package.json +80 -0
  22. package/src/api/createExtensionAPI.ts +63 -0
  23. package/src/api/index.ts +5 -0
  24. package/src/auth/index.ts +13 -0
  25. package/src/config/env.ts +59 -0
  26. package/src/config/index.ts +5 -0
  27. package/src/context/ExtensionProvider.tsx +102 -0
  28. package/src/context/createExtensionContext.tsx +78 -0
  29. package/src/context/index.ts +7 -0
  30. package/src/hooks/index.ts +6 -0
  31. package/src/hooks/useInfinitePagination.ts +117 -0
  32. package/src/hooks/usePagination.ts +155 -0
  33. package/src/hooks.ts +17 -0
  34. package/src/index.ts +21 -0
  35. package/src/logger/createExtensionLogger.ts +61 -0
  36. package/src/logger/index.ts +5 -0
  37. package/src/types/context.ts +93 -0
  38. package/src/types/error.ts +12 -0
  39. package/src/types/index.ts +17 -0
  40. package/src/types/logger.ts +17 -0
  41. package/src/types/pagination.ts +47 -0
  42. package/src/utils/errors.ts +71 -0
  43. package/src/utils/index.ts +10 -0
@@ -0,0 +1,246 @@
1
+ import { ReactNode } from 'react';
2
+ export { createExtensionAPI, getSharedAuthStorage } from './api.cjs';
3
+
4
+ /**
5
+ * Pagination types for extension packages
6
+ */
7
+ interface PaginatedResponse<T> {
8
+ results: T[];
9
+ count: number;
10
+ next?: number | null;
11
+ previous?: number | null;
12
+ next_page?: number | null;
13
+ previous_page?: number | null;
14
+ has_next?: boolean;
15
+ has_previous?: boolean;
16
+ total_pages?: number;
17
+ }
18
+ interface PaginationParams {
19
+ page?: number;
20
+ page_size?: number;
21
+ ordering?: string;
22
+ }
23
+ interface PaginationState {
24
+ page: number;
25
+ pageSize: number;
26
+ totalCount: number;
27
+ totalPages: number;
28
+ hasNext: boolean;
29
+ hasPrevious: boolean;
30
+ }
31
+ interface InfinitePaginationReturn<T> {
32
+ items: T[];
33
+ isLoading: boolean;
34
+ isLoadingMore: boolean;
35
+ error: any;
36
+ hasMore: boolean;
37
+ totalCount: number;
38
+ loadMore: () => void;
39
+ refresh: () => Promise<void>;
40
+ }
41
+ interface InfinitePaginationOptions {
42
+ pageSize?: number;
43
+ revalidateFirstPage?: boolean;
44
+ parallel?: boolean;
45
+ }
46
+
47
+ /**
48
+ * Context and provider types for extension packages
49
+ */
50
+
51
+ interface ExtensionContextOptions {
52
+ revalidateOnFocus?: boolean;
53
+ revalidateOnReconnect?: boolean;
54
+ revalidateIfStale?: boolean;
55
+ }
56
+ interface ExtensionMetadata {
57
+ /**
58
+ * Unique extension name (e.g., 'newsletter', 'payments')
59
+ */
60
+ name: string;
61
+ /**
62
+ * Extension version (semver format recommended)
63
+ */
64
+ version: string;
65
+ /**
66
+ * Extension author name or organization
67
+ */
68
+ author: string;
69
+ /**
70
+ * Extension display name
71
+ */
72
+ displayName?: string;
73
+ /**
74
+ * Extension description
75
+ */
76
+ description?: string;
77
+ /**
78
+ * GitHub repository URL
79
+ * @example 'https://github.com/username/repo'
80
+ */
81
+ githubUrl?: string;
82
+ /**
83
+ * Extension homepage URL
84
+ */
85
+ homepage?: string;
86
+ /**
87
+ * License identifier (e.g., 'MIT', 'Apache-2.0')
88
+ */
89
+ license?: string;
90
+ /**
91
+ * Keywords for search and discovery
92
+ */
93
+ keywords?: string[];
94
+ /**
95
+ * Extension icon URL or emoji
96
+ * @example '📧' or 'https://example.com/icon.png'
97
+ */
98
+ icon?: string;
99
+ /**
100
+ * List of extension dependencies
101
+ * @example ['payments', 'auth']
102
+ */
103
+ dependencies?: string[];
104
+ /**
105
+ * Minimum required DjangoCFG version
106
+ */
107
+ minVersion?: string;
108
+ }
109
+ interface ExtensionProviderProps {
110
+ /**
111
+ * Extension metadata for registration
112
+ */
113
+ metadata: ExtensionMetadata;
114
+ /**
115
+ * SWR configuration options
116
+ */
117
+ options?: ExtensionContextOptions;
118
+ /**
119
+ * Child components
120
+ */
121
+ children: ReactNode;
122
+ }
123
+
124
+ /**
125
+ * Error types for extension packages
126
+ */
127
+ interface ExtensionError {
128
+ message: string;
129
+ code?: string;
130
+ details?: unknown;
131
+ timestamp: string;
132
+ }
133
+ type ErrorHandler = (error: Error | ExtensionError) => void;
134
+
135
+ /**
136
+ * Logger types for extension packages
137
+ */
138
+ interface ExtensionLogger {
139
+ info: (...args: any[]) => void;
140
+ warn: (...args: any[]) => void;
141
+ error: (...args: any[]) => void;
142
+ debug: (...args: any[]) => void;
143
+ success: (...args: any[]) => void;
144
+ }
145
+ interface LoggerOptions {
146
+ tag: string;
147
+ level?: 'debug' | 'info' | 'warn' | 'error';
148
+ enabled?: boolean;
149
+ }
150
+
151
+ /**
152
+ * Environment configuration utilities
153
+ *
154
+ * Safe to use in both client and server contexts.
155
+ * Can be imported from server-safe entry point.
156
+ */
157
+ /**
158
+ * Check if running in development mode
159
+ */
160
+ declare const isDevelopment: boolean;
161
+ /**
162
+ * Check if running in production mode
163
+ */
164
+ declare const isProduction: boolean;
165
+ /**
166
+ * Check if running in test mode
167
+ */
168
+ declare const isTest: boolean;
169
+ /**
170
+ * Check if this is a static build (Next.js export)
171
+ */
172
+ declare const isStaticBuild: boolean;
173
+ /**
174
+ * Check if code is running on client side
175
+ * Safe to use in both client and server contexts
176
+ */
177
+ declare const isClient: boolean;
178
+ /**
179
+ * Check if code is running on server side
180
+ * Safe to use in both client and server contexts
181
+ */
182
+ declare const isServer: boolean;
183
+ /**
184
+ * Get API URL from environment
185
+ * Returns empty string if not set
186
+ */
187
+ declare const getApiUrl: () => string;
188
+ /**
189
+ * Environment configuration object
190
+ */
191
+ declare const env: {
192
+ readonly isDevelopment: boolean;
193
+ readonly isProduction: boolean;
194
+ readonly isTest: boolean;
195
+ readonly isStaticBuild: boolean;
196
+ readonly isClient: boolean;
197
+ readonly isServer: boolean;
198
+ readonly getApiUrl: () => string;
199
+ };
200
+
201
+ /**
202
+ * Error handling utilities for extension packages
203
+ */
204
+
205
+ /**
206
+ * Checks if an error is an ExtensionError
207
+ */
208
+ declare function isExtensionError(error: unknown): error is ExtensionError;
209
+ /**
210
+ * Creates an ExtensionError from any error
211
+ */
212
+ declare function createExtensionError(error: unknown, code?: string, details?: unknown): ExtensionError;
213
+ /**
214
+ * Formats an error message for display
215
+ */
216
+ declare function formatErrorMessage(error: unknown): string;
217
+ /**
218
+ * Safe error handler that logs and optionally calls callback
219
+ */
220
+ declare function handleExtensionError(error: unknown, logger?: {
221
+ error: (...args: any[]) => void;
222
+ }, callback?: (error: ExtensionError) => void): void;
223
+
224
+ /**
225
+ * Logger factory for extension packages
226
+ */
227
+
228
+ /**
229
+ * Creates a tagged logger for an extension
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * // In extension package
234
+ * export const logger = createExtensionLogger({
235
+ * tag: 'ext-newsletter',
236
+ * level: 'info'
237
+ * });
238
+ *
239
+ * // Usage
240
+ * logger.info('Campaign created:', campaignId);
241
+ * logger.error('Failed to send campaign:', error);
242
+ * ```
243
+ */
244
+ declare function createExtensionLogger(options: LoggerOptions): ExtensionLogger;
245
+
246
+ export { type ErrorHandler, type ExtensionContextOptions, type ExtensionError, type ExtensionLogger, type ExtensionMetadata, type ExtensionProviderProps, type InfinitePaginationOptions, type InfinitePaginationReturn, type LoggerOptions, type PaginatedResponse, type PaginationParams, type PaginationState, createExtensionError, createExtensionLogger, env, formatErrorMessage, getApiUrl, handleExtensionError, isClient, isDevelopment, isExtensionError, isProduction, isServer, isStaticBuild, isTest };
@@ -0,0 +1,246 @@
1
+ import { ReactNode } from 'react';
2
+ export { createExtensionAPI, getSharedAuthStorage } from './api.js';
3
+
4
+ /**
5
+ * Pagination types for extension packages
6
+ */
7
+ interface PaginatedResponse<T> {
8
+ results: T[];
9
+ count: number;
10
+ next?: number | null;
11
+ previous?: number | null;
12
+ next_page?: number | null;
13
+ previous_page?: number | null;
14
+ has_next?: boolean;
15
+ has_previous?: boolean;
16
+ total_pages?: number;
17
+ }
18
+ interface PaginationParams {
19
+ page?: number;
20
+ page_size?: number;
21
+ ordering?: string;
22
+ }
23
+ interface PaginationState {
24
+ page: number;
25
+ pageSize: number;
26
+ totalCount: number;
27
+ totalPages: number;
28
+ hasNext: boolean;
29
+ hasPrevious: boolean;
30
+ }
31
+ interface InfinitePaginationReturn<T> {
32
+ items: T[];
33
+ isLoading: boolean;
34
+ isLoadingMore: boolean;
35
+ error: any;
36
+ hasMore: boolean;
37
+ totalCount: number;
38
+ loadMore: () => void;
39
+ refresh: () => Promise<void>;
40
+ }
41
+ interface InfinitePaginationOptions {
42
+ pageSize?: number;
43
+ revalidateFirstPage?: boolean;
44
+ parallel?: boolean;
45
+ }
46
+
47
+ /**
48
+ * Context and provider types for extension packages
49
+ */
50
+
51
+ interface ExtensionContextOptions {
52
+ revalidateOnFocus?: boolean;
53
+ revalidateOnReconnect?: boolean;
54
+ revalidateIfStale?: boolean;
55
+ }
56
+ interface ExtensionMetadata {
57
+ /**
58
+ * Unique extension name (e.g., 'newsletter', 'payments')
59
+ */
60
+ name: string;
61
+ /**
62
+ * Extension version (semver format recommended)
63
+ */
64
+ version: string;
65
+ /**
66
+ * Extension author name or organization
67
+ */
68
+ author: string;
69
+ /**
70
+ * Extension display name
71
+ */
72
+ displayName?: string;
73
+ /**
74
+ * Extension description
75
+ */
76
+ description?: string;
77
+ /**
78
+ * GitHub repository URL
79
+ * @example 'https://github.com/username/repo'
80
+ */
81
+ githubUrl?: string;
82
+ /**
83
+ * Extension homepage URL
84
+ */
85
+ homepage?: string;
86
+ /**
87
+ * License identifier (e.g., 'MIT', 'Apache-2.0')
88
+ */
89
+ license?: string;
90
+ /**
91
+ * Keywords for search and discovery
92
+ */
93
+ keywords?: string[];
94
+ /**
95
+ * Extension icon URL or emoji
96
+ * @example '📧' or 'https://example.com/icon.png'
97
+ */
98
+ icon?: string;
99
+ /**
100
+ * List of extension dependencies
101
+ * @example ['payments', 'auth']
102
+ */
103
+ dependencies?: string[];
104
+ /**
105
+ * Minimum required DjangoCFG version
106
+ */
107
+ minVersion?: string;
108
+ }
109
+ interface ExtensionProviderProps {
110
+ /**
111
+ * Extension metadata for registration
112
+ */
113
+ metadata: ExtensionMetadata;
114
+ /**
115
+ * SWR configuration options
116
+ */
117
+ options?: ExtensionContextOptions;
118
+ /**
119
+ * Child components
120
+ */
121
+ children: ReactNode;
122
+ }
123
+
124
+ /**
125
+ * Error types for extension packages
126
+ */
127
+ interface ExtensionError {
128
+ message: string;
129
+ code?: string;
130
+ details?: unknown;
131
+ timestamp: string;
132
+ }
133
+ type ErrorHandler = (error: Error | ExtensionError) => void;
134
+
135
+ /**
136
+ * Logger types for extension packages
137
+ */
138
+ interface ExtensionLogger {
139
+ info: (...args: any[]) => void;
140
+ warn: (...args: any[]) => void;
141
+ error: (...args: any[]) => void;
142
+ debug: (...args: any[]) => void;
143
+ success: (...args: any[]) => void;
144
+ }
145
+ interface LoggerOptions {
146
+ tag: string;
147
+ level?: 'debug' | 'info' | 'warn' | 'error';
148
+ enabled?: boolean;
149
+ }
150
+
151
+ /**
152
+ * Environment configuration utilities
153
+ *
154
+ * Safe to use in both client and server contexts.
155
+ * Can be imported from server-safe entry point.
156
+ */
157
+ /**
158
+ * Check if running in development mode
159
+ */
160
+ declare const isDevelopment: boolean;
161
+ /**
162
+ * Check if running in production mode
163
+ */
164
+ declare const isProduction: boolean;
165
+ /**
166
+ * Check if running in test mode
167
+ */
168
+ declare const isTest: boolean;
169
+ /**
170
+ * Check if this is a static build (Next.js export)
171
+ */
172
+ declare const isStaticBuild: boolean;
173
+ /**
174
+ * Check if code is running on client side
175
+ * Safe to use in both client and server contexts
176
+ */
177
+ declare const isClient: boolean;
178
+ /**
179
+ * Check if code is running on server side
180
+ * Safe to use in both client and server contexts
181
+ */
182
+ declare const isServer: boolean;
183
+ /**
184
+ * Get API URL from environment
185
+ * Returns empty string if not set
186
+ */
187
+ declare const getApiUrl: () => string;
188
+ /**
189
+ * Environment configuration object
190
+ */
191
+ declare const env: {
192
+ readonly isDevelopment: boolean;
193
+ readonly isProduction: boolean;
194
+ readonly isTest: boolean;
195
+ readonly isStaticBuild: boolean;
196
+ readonly isClient: boolean;
197
+ readonly isServer: boolean;
198
+ readonly getApiUrl: () => string;
199
+ };
200
+
201
+ /**
202
+ * Error handling utilities for extension packages
203
+ */
204
+
205
+ /**
206
+ * Checks if an error is an ExtensionError
207
+ */
208
+ declare function isExtensionError(error: unknown): error is ExtensionError;
209
+ /**
210
+ * Creates an ExtensionError from any error
211
+ */
212
+ declare function createExtensionError(error: unknown, code?: string, details?: unknown): ExtensionError;
213
+ /**
214
+ * Formats an error message for display
215
+ */
216
+ declare function formatErrorMessage(error: unknown): string;
217
+ /**
218
+ * Safe error handler that logs and optionally calls callback
219
+ */
220
+ declare function handleExtensionError(error: unknown, logger?: {
221
+ error: (...args: any[]) => void;
222
+ }, callback?: (error: ExtensionError) => void): void;
223
+
224
+ /**
225
+ * Logger factory for extension packages
226
+ */
227
+
228
+ /**
229
+ * Creates a tagged logger for an extension
230
+ *
231
+ * @example
232
+ * ```ts
233
+ * // In extension package
234
+ * export const logger = createExtensionLogger({
235
+ * tag: 'ext-newsletter',
236
+ * level: 'info'
237
+ * });
238
+ *
239
+ * // Usage
240
+ * logger.info('Campaign created:', campaignId);
241
+ * logger.error('Failed to send campaign:', error);
242
+ * ```
243
+ */
244
+ declare function createExtensionLogger(options: LoggerOptions): ExtensionLogger;
245
+
246
+ export { type ErrorHandler, type ExtensionContextOptions, type ExtensionError, type ExtensionLogger, type ExtensionMetadata, type ExtensionProviderProps, type InfinitePaginationOptions, type InfinitePaginationReturn, type LoggerOptions, type PaginatedResponse, type PaginationParams, type PaginationState, createExtensionError, createExtensionLogger, env, formatErrorMessage, getApiUrl, handleExtensionError, isClient, isDevelopment, isExtensionError, isProduction, isServer, isStaticBuild, isTest };
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { createExtensionError, createExtensionLogger, formatErrorMessage, handleExtensionError, isExtensionError } from './chunk-YQGNYUBX.js';
2
+ export { createExtensionAPI, env, getApiUrl, getSharedAuthStorage, isClient, isDevelopment, isProduction, isServer, isStaticBuild, isTest } from './chunk-MECBWZG4.js';
3
+ import './chunk-3RG5ZIWI.js';
package/package.json ADDED
@@ -0,0 +1,80 @@
1
+ {
2
+ "name": "@djangocfg/ext-base",
3
+ "version": "1.0.0",
4
+ "description": "Base utilities and common code for DjangoCFG extensions",
5
+ "keywords": [
6
+ "django",
7
+ "djangocfg",
8
+ "extension",
9
+ "base",
10
+ "utilities",
11
+ "typescript",
12
+ "react"
13
+ ],
14
+ "author": {
15
+ "name": "DjangoCFG",
16
+ "url": "https://djangocfg.com"
17
+ },
18
+ "homepage": "https://djangocfg.com",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "https://github.com/markolofsen/django-cfg.git",
22
+ "directory": "extensions/base"
23
+ },
24
+ "bugs": {
25
+ "url": "https://github.com/markolofsen/django-cfg/issues"
26
+ },
27
+ "license": "MIT",
28
+ "type": "module",
29
+ "main": "./dist/index.cjs",
30
+ "module": "./dist/index.mjs",
31
+ "types": "./dist/index.d.ts",
32
+ "exports": {
33
+ ".": {
34
+ "types": "./dist/index.d.ts",
35
+ "import": "./dist/index.mjs",
36
+ "require": "./dist/index.cjs"
37
+ },
38
+ "./hooks": {
39
+ "types": "./dist/hooks.d.ts",
40
+ "import": "./dist/hooks.mjs",
41
+ "require": "./dist/hooks.cjs"
42
+ },
43
+ "./auth": {
44
+ "types": "./dist/auth.d.ts",
45
+ "import": "./dist/auth.mjs",
46
+ "require": "./dist/auth.cjs"
47
+ },
48
+ "./api": {
49
+ "types": "./dist/api.d.ts",
50
+ "import": "./dist/api.mjs",
51
+ "require": "./dist/api.cjs"
52
+ }
53
+ },
54
+ "files": [
55
+ "dist",
56
+ "src"
57
+ ],
58
+ "scripts": {
59
+ "build": "tsup",
60
+ "dev": "tsup --watch",
61
+ "check": "tsc --noEmit"
62
+ },
63
+ "peerDependencies": {
64
+ "@djangocfg/api": "^2.1.14",
65
+ "consola": "^3.4.2",
66
+ "react": "^18 || ^19",
67
+ "react-dom": "^18 || ^19",
68
+ "swr": "^2.3.7"
69
+ },
70
+ "devDependencies": {
71
+ "@djangocfg/api": "^2.1.14",
72
+ "@djangocfg/typescript-config": "^2.1.14",
73
+ "@types/node": "^24.7.2",
74
+ "@types/react": "^19.0.0",
75
+ "consola": "^3.4.2",
76
+ "swr": "^2.3.7",
77
+ "tsup": "^8.5.0",
78
+ "typescript": "^5.9.3"
79
+ }
80
+ }
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Factory for creating extension API instances
3
+ *
4
+ * Provides consistent API setup across all extensions with shared authentication
5
+ */
6
+
7
+ import { isStaticBuild, getApiUrl } from '../config';
8
+
9
+ /**
10
+ * Creates an extension API instance with shared authentication storage
11
+ *
12
+ * @param APIClass - The generated API class from your extension
13
+ * @returns Configured API instance with shared auth storage
14
+ *
15
+ * @example
16
+ * ```typescript
17
+ * // In your extension's api/index.ts
18
+ * import { API } from './generated/ext_newsletter';
19
+ * import { createExtensionAPI } from '@djangocfg/ext-base/api';
20
+ *
21
+ * export const apiNewsletter = createExtensionAPI(API);
22
+ * ```
23
+ */
24
+ export function createExtensionAPI<T>(APIClass: new (url: string, options?: any) => T): T {
25
+ // Dynamically import accounts API to get shared storage
26
+ // This avoids circular dependencies and keeps the import lazy
27
+ let storage: any;
28
+
29
+ try {
30
+ // @ts-ignore - dynamic import for shared storage
31
+ const { api: accountsApi } = require('@djangocfg/api');
32
+ storage = (accountsApi as any)._storage;
33
+ } catch (error) {
34
+ // If @djangocfg/api is not available, use undefined storage
35
+ // This allows extensions to work in isolation if needed
36
+ storage = undefined;
37
+ }
38
+
39
+ const apiUrl = isStaticBuild ? '' : getApiUrl();
40
+
41
+ return new APIClass(apiUrl, storage ? { storage } : undefined);
42
+ }
43
+
44
+ /**
45
+ * Get shared authentication storage from accounts API
46
+ *
47
+ * @returns Storage instance or undefined if not available
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * const storage = getSharedAuthStorage();
52
+ * const api = new API(apiUrl, { storage });
53
+ * ```
54
+ */
55
+ export function getSharedAuthStorage(): any | undefined {
56
+ try {
57
+ // @ts-ignore - dynamic import
58
+ const { api: accountsApi } = require('@djangocfg/api');
59
+ return (accountsApi as any)._storage;
60
+ } catch (error) {
61
+ return undefined;
62
+ }
63
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * API utilities for extensions
3
+ */
4
+
5
+ export * from './createExtensionAPI';
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Auth utilities for extension packages
3
+ *
4
+ * Re-exports useAuth from @djangocfg/api for convenience
5
+ */
6
+
7
+ 'use client';
8
+
9
+ // Re-export useAuth for convenience
10
+ // Extensions can import from either:
11
+ // - @djangocfg/api/auth (direct)
12
+ // - @djangocfg/ext-base/auth (convenience re-export)
13
+ export { useAuth, type UserProfile, type AuthContextType } from '@djangocfg/api/auth';