@djangocfg/ext-base 1.0.3 → 1.0.5
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.
- package/README.md +134 -8
- package/dist/api.cjs +40 -0
- package/dist/api.d.cts +35 -0
- package/dist/api.d.ts +35 -0
- package/dist/api.js +2 -0
- package/dist/auth.cjs +10 -0
- package/dist/auth.d.cts +1 -0
- package/dist/auth.d.ts +1 -0
- package/dist/auth.js +2 -0
- package/dist/chunk-2CHOCDMY.js +237 -0
- package/dist/chunk-3RG5ZIWI.js +8 -0
- package/dist/chunk-NXFE5CDE.js +140 -0
- package/dist/cli.mjs +530 -0
- package/dist/hooks.cjs +437 -0
- package/dist/hooks.d.cts +97 -0
- package/dist/hooks.d.ts +97 -0
- package/dist/hooks.js +95 -0
- package/dist/index.cjs +345 -0
- package/dist/index.d.cts +363 -0
- package/dist/index.d.ts +363 -0
- package/dist/index.js +3 -0
- package/package.json +6 -4
- package/src/cli/index.ts +281 -15
- package/src/context/ExtensionProvider.tsx +67 -4
- package/src/extensionConfig.ts +77 -28
- package/templates/extension-template/README.md.template +30 -0
- package/templates/extension-template/package.json.template +2 -1
- package/templates/extension-template/playground/.gitignore.template +34 -0
- package/templates/extension-template/playground/CLAUDE.md +35 -0
- package/templates/extension-template/playground/README.md.template +76 -0
- package/templates/extension-template/playground/app/globals.css.template +19 -0
- package/templates/extension-template/playground/app/layout.tsx.template +32 -0
- package/templates/extension-template/playground/app/page.tsx.template +44 -0
- package/templates/extension-template/playground/next.config.ts.template +62 -0
- package/templates/extension-template/playground/package.json.template +36 -0
- package/templates/extension-template/playground/postcss.config.js.template +5 -0
- package/templates/extension-template/playground/tsconfig.json.template +18 -0
- package/templates/extension-template/src/contexts/__PROVIDER_NAME__Context.tsx +1 -1
- package/templates/extension-template/src/contexts/__PROVIDER_NAME__ExtensionProvider.tsx +1 -0
- package/templates/extension-template/src/index.ts +12 -4
- package/templates/extension-template/src/utils/withSmartProvider.tsx +70 -0
package/README.md
CHANGED
|
@@ -39,6 +39,15 @@ The package includes a CLI tool for creating new DjangoCFG extensions:
|
|
|
39
39
|
# Create a new extension with interactive wizard
|
|
40
40
|
djangocfg-ext create
|
|
41
41
|
|
|
42
|
+
# Quick test extension (auto-cleanup + unique name)
|
|
43
|
+
djangocfg-ext test
|
|
44
|
+
|
|
45
|
+
# List all extensions in workspace
|
|
46
|
+
djangocfg-ext list
|
|
47
|
+
|
|
48
|
+
# Show extension info
|
|
49
|
+
djangocfg-ext info <extension-name>
|
|
50
|
+
|
|
42
51
|
# Show help
|
|
43
52
|
djangocfg-ext help
|
|
44
53
|
```
|
|
@@ -50,6 +59,9 @@ The CLI will guide you through creating a new extension with proper structure an
|
|
|
50
59
|
- Automatic npm registry validation to prevent duplicate package names
|
|
51
60
|
- Support for both scoped (`@my-org/my-extension`) and unscoped (`my-extension`) package names
|
|
52
61
|
- Template generation with proper placeholder replacement
|
|
62
|
+
- **Playground environment** with Next.js 15 for rapid development
|
|
63
|
+
- Auto-dependency installation and type checking
|
|
64
|
+
- Smart workspace detection for monorepos
|
|
53
65
|
|
|
54
66
|
## Quick Start
|
|
55
67
|
|
|
@@ -115,7 +127,7 @@ export function MyExtensionProvider({ children }) {
|
|
|
115
127
|
### 3. Use in your app
|
|
116
128
|
|
|
117
129
|
```typescript
|
|
118
|
-
import { MyExtensionProvider } from '@your-org/my-extension
|
|
130
|
+
import { MyExtensionProvider } from '@your-org/my-extension';
|
|
119
131
|
|
|
120
132
|
export default function RootLayout({ children }) {
|
|
121
133
|
return (
|
|
@@ -126,6 +138,94 @@ export default function RootLayout({ children }) {
|
|
|
126
138
|
}
|
|
127
139
|
```
|
|
128
140
|
|
|
141
|
+
## Development Workflow
|
|
142
|
+
|
|
143
|
+
Every extension created with the CLI includes a **playground** - a Next.js 15 development environment for rapid testing and iteration.
|
|
144
|
+
|
|
145
|
+
### Starting the Playground
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
cd your-extension
|
|
149
|
+
pnpm dev:playground
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
This command:
|
|
153
|
+
1. ✅ Automatically builds your extension
|
|
154
|
+
2. ✅ Starts Next.js dev server on port 3333
|
|
155
|
+
3. ✅ Opens browser automatically
|
|
156
|
+
4. ✅ Hot-reloads on source changes
|
|
157
|
+
|
|
158
|
+
### Playground Features
|
|
159
|
+
|
|
160
|
+
- **Next.js 15 App Router** with React 19
|
|
161
|
+
- **Tailwind CSS v4** pre-configured
|
|
162
|
+
- **BaseApp** wrapper with theme, auth, and SWR
|
|
163
|
+
- **Auto-build** extension before starting (via `predev` script)
|
|
164
|
+
- **Auto-open browser** when dev server is ready
|
|
165
|
+
- **Hot reload** for instant feedback
|
|
166
|
+
|
|
167
|
+
### Building for Production
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
# Build extension
|
|
171
|
+
pnpm build
|
|
172
|
+
|
|
173
|
+
# Build playground
|
|
174
|
+
pnpm build:playground
|
|
175
|
+
|
|
176
|
+
# Type check
|
|
177
|
+
pnpm check
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Smart Provider Pattern
|
|
181
|
+
|
|
182
|
+
Extensions support a **Smart Provider Pattern** that allows components to work both standalone and with manual provider wrapping.
|
|
183
|
+
|
|
184
|
+
### Using withSmartProvider
|
|
185
|
+
|
|
186
|
+
Create self-contained components that automatically wrap themselves with the provider when needed:
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
import { withSmartProvider } from '@your-org/my-extension';
|
|
190
|
+
|
|
191
|
+
// Your component
|
|
192
|
+
function MyComponent() {
|
|
193
|
+
const { data } = useMyExtension();
|
|
194
|
+
return <div>{data}</div>;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Wrap with smart provider
|
|
198
|
+
export const MySmartComponent = withSmartProvider(MyComponent);
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
**Usage:**
|
|
202
|
+
|
|
203
|
+
```typescript
|
|
204
|
+
// Works standalone (auto-wrapped)
|
|
205
|
+
<MySmartComponent />
|
|
206
|
+
|
|
207
|
+
// Also works with manual provider (shares context)
|
|
208
|
+
<MyExtensionProvider>
|
|
209
|
+
<MySmartComponent />
|
|
210
|
+
<MySmartComponent />
|
|
211
|
+
</MyExtensionProvider>
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
**Benefits:**
|
|
215
|
+
- ✅ Components work out-of-the-box without provider boilerplate
|
|
216
|
+
- ✅ Multiple components can still share context when manually wrapped
|
|
217
|
+
- ✅ Best of both worlds - simplicity and flexibility
|
|
218
|
+
|
|
219
|
+
**When to use:**
|
|
220
|
+
- Library components that should "just work"
|
|
221
|
+
- Components that might be used in isolation
|
|
222
|
+
- Reducing boilerplate for simple use cases
|
|
223
|
+
|
|
224
|
+
**When to use manual provider:**
|
|
225
|
+
- Multiple components need to share state
|
|
226
|
+
- Performance optimization (single provider instance)
|
|
227
|
+
- Explicit context boundaries
|
|
228
|
+
|
|
129
229
|
## Core Features
|
|
130
230
|
|
|
131
231
|
### Extension Config Helper
|
|
@@ -279,8 +379,17 @@ function MyComponent() {
|
|
|
279
379
|
| `@djangocfg/ext-base/api` | API utilities (createExtensionAPI, getSharedAuthStorage) | Server & client components |
|
|
280
380
|
|
|
281
381
|
**For your own extensions:**
|
|
282
|
-
|
|
283
|
-
|
|
382
|
+
|
|
383
|
+
Extension templates automatically export:
|
|
384
|
+
- `extensionConfig` - Extension metadata
|
|
385
|
+
- `[Name]Provider` - Main provider component (wraps ExtensionProvider)
|
|
386
|
+
- `use[Name]` - Context hook (required provider)
|
|
387
|
+
- `use[Name]Optional` - Context hook (optional provider)
|
|
388
|
+
- `withSmartProvider` - HOC for self-contained components
|
|
389
|
+
|
|
390
|
+
**Server-safe imports:**
|
|
391
|
+
- Use `@your-org/ext-name/config` to import extension metadata without loading React components
|
|
392
|
+
- This is recommended for server components and build tools
|
|
284
393
|
|
|
285
394
|
## Extension Categories
|
|
286
395
|
|
|
@@ -342,17 +451,34 @@ import type {
|
|
|
342
451
|
|
|
343
452
|
## Best Practices
|
|
344
453
|
|
|
454
|
+
### Configuration & Metadata
|
|
345
455
|
- Use `createExtensionConfig` helper to maintain Single Source of Truth from package.json
|
|
346
|
-
- Always wrap your extension with `ExtensionProvider` for proper registration
|
|
347
456
|
- Use Lucide icon names (not emoji) for `icon` field
|
|
348
|
-
- **Include a `preview.png` file** in your extension root (1200x630px recommended) - it will be automatically served via unpkg
|
|
349
457
|
- Include comprehensive `features` list for marketplace visibility
|
|
350
458
|
- Provide code `examples` with proper syntax highlighting
|
|
351
|
-
-
|
|
352
|
-
- Use `createExtensionLogger` with consistent tags for structured logging
|
|
353
|
-
- Separate client-only code using `/hooks` entry point
|
|
459
|
+
- **Include a `preview.png` file** in your extension root (1200x630px recommended)
|
|
354
460
|
- List `preview.png` in your package.json `files` array for npm publication
|
|
355
461
|
|
|
462
|
+
### Development
|
|
463
|
+
- Use the **playground** for rapid development (`pnpm dev:playground`)
|
|
464
|
+
- Test your components in isolation before integrating
|
|
465
|
+
- Run type checks before building (`pnpm check`)
|
|
466
|
+
- Use `createExtensionLogger` with consistent tags for structured logging
|
|
467
|
+
|
|
468
|
+
### Architecture
|
|
469
|
+
- Always wrap your extension with `ExtensionProvider` for proper registration
|
|
470
|
+
- Export main provider as `[Name]Provider` (not `[Name]ExtensionProvider`)
|
|
471
|
+
- Use `withSmartProvider` for components that should work standalone
|
|
472
|
+
- Prefer manual provider wrapping when components need shared state
|
|
473
|
+
- Use provided pagination hooks for consistent data fetching
|
|
474
|
+
- Separate client-only code appropriately
|
|
475
|
+
|
|
476
|
+
### Publishing
|
|
477
|
+
- Test with `pnpm build` before publishing
|
|
478
|
+
- Verify preview image is accessible via unpkg
|
|
479
|
+
- Ensure all peer dependencies are correctly listed
|
|
480
|
+
- Include clear usage examples in README
|
|
481
|
+
|
|
356
482
|
## License
|
|
357
483
|
|
|
358
484
|
MIT
|
package/dist/api.cjs
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
4
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
5
|
+
}) : x)(function(x) {
|
|
6
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
7
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
// src/config.ts
|
|
11
|
+
process.env.NODE_ENV === "development";
|
|
12
|
+
process.env.NODE_ENV === "production";
|
|
13
|
+
var isStaticBuild = process.env.STATIC_BUILD === "true";
|
|
14
|
+
function getApiUrl() {
|
|
15
|
+
return process.env.NEXT_PUBLIC_API_URL || "/api";
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// src/api/createExtensionAPI.ts
|
|
19
|
+
function createExtensionAPI(APIClass) {
|
|
20
|
+
let storage;
|
|
21
|
+
try {
|
|
22
|
+
const { api: accountsApi } = __require("@djangocfg/api");
|
|
23
|
+
storage = accountsApi._storage;
|
|
24
|
+
} catch (error) {
|
|
25
|
+
storage = void 0;
|
|
26
|
+
}
|
|
27
|
+
const apiUrl = isStaticBuild ? "" : getApiUrl();
|
|
28
|
+
return new APIClass(apiUrl, storage ? { storage } : void 0);
|
|
29
|
+
}
|
|
30
|
+
function getSharedAuthStorage() {
|
|
31
|
+
try {
|
|
32
|
+
const { api: accountsApi } = __require("@djangocfg/api");
|
|
33
|
+
return accountsApi._storage;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
return void 0;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
exports.createExtensionAPI = createExtensionAPI;
|
|
40
|
+
exports.getSharedAuthStorage = getSharedAuthStorage;
|
package/dist/api.d.cts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory for creating extension API instances
|
|
3
|
+
*
|
|
4
|
+
* Provides consistent API setup across all extensions with shared authentication
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Creates an extension API instance with shared authentication storage
|
|
8
|
+
*
|
|
9
|
+
* @param APIClass - The generated API class from your extension
|
|
10
|
+
* @returns Configured API instance with shared auth storage
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // In your extension's api/index.ts
|
|
15
|
+
* import { API } from './generated/ext_newsletter';
|
|
16
|
+
* import { createExtensionAPI } from '@djangocfg/ext-base/api';
|
|
17
|
+
*
|
|
18
|
+
* export const apiNewsletter = createExtensionAPI(API);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
declare function createExtensionAPI<T>(APIClass: new (url: string, options?: any) => T): T;
|
|
22
|
+
/**
|
|
23
|
+
* Get shared authentication storage from accounts API
|
|
24
|
+
*
|
|
25
|
+
* @returns Storage instance or undefined if not available
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const storage = getSharedAuthStorage();
|
|
30
|
+
* const api = new API(apiUrl, { storage });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare function getSharedAuthStorage(): any | undefined;
|
|
34
|
+
|
|
35
|
+
export { createExtensionAPI, getSharedAuthStorage };
|
package/dist/api.d.ts
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Factory for creating extension API instances
|
|
3
|
+
*
|
|
4
|
+
* Provides consistent API setup across all extensions with shared authentication
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Creates an extension API instance with shared authentication storage
|
|
8
|
+
*
|
|
9
|
+
* @param APIClass - The generated API class from your extension
|
|
10
|
+
* @returns Configured API instance with shared auth storage
|
|
11
|
+
*
|
|
12
|
+
* @example
|
|
13
|
+
* ```typescript
|
|
14
|
+
* // In your extension's api/index.ts
|
|
15
|
+
* import { API } from './generated/ext_newsletter';
|
|
16
|
+
* import { createExtensionAPI } from '@djangocfg/ext-base/api';
|
|
17
|
+
*
|
|
18
|
+
* export const apiNewsletter = createExtensionAPI(API);
|
|
19
|
+
* ```
|
|
20
|
+
*/
|
|
21
|
+
declare function createExtensionAPI<T>(APIClass: new (url: string, options?: any) => T): T;
|
|
22
|
+
/**
|
|
23
|
+
* Get shared authentication storage from accounts API
|
|
24
|
+
*
|
|
25
|
+
* @returns Storage instance or undefined if not available
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const storage = getSharedAuthStorage();
|
|
30
|
+
* const api = new API(apiUrl, { storage });
|
|
31
|
+
* ```
|
|
32
|
+
*/
|
|
33
|
+
declare function getSharedAuthStorage(): any | undefined;
|
|
34
|
+
|
|
35
|
+
export { createExtensionAPI, getSharedAuthStorage };
|
package/dist/api.js
ADDED
package/dist/auth.cjs
ADDED
package/dist/auth.d.cts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AuthContextType, UserProfile, useAuth } from '@djangocfg/api/auth';
|
package/dist/auth.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { AuthContextType, UserProfile, useAuth } from '@djangocfg/api/auth';
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
import { package_default } from './chunk-NXFE5CDE.js';
|
|
2
|
+
import { createConsola } from 'consola';
|
|
3
|
+
|
|
4
|
+
// src/types/context.ts
|
|
5
|
+
var EXTENSION_CATEGORIES = [
|
|
6
|
+
{ title: "Forms", value: "forms" },
|
|
7
|
+
{ title: "Payments", value: "payments" },
|
|
8
|
+
{ title: "Content", value: "content" },
|
|
9
|
+
{ title: "Support", value: "support" },
|
|
10
|
+
{ title: "Utilities", value: "utilities" },
|
|
11
|
+
{ title: "Analytics", value: "analytics" },
|
|
12
|
+
{ title: "Security", value: "security" },
|
|
13
|
+
{ title: "Integration", value: "integration" },
|
|
14
|
+
{ title: "Other", value: "other" }
|
|
15
|
+
];
|
|
16
|
+
|
|
17
|
+
// src/utils/createExtensionConfig.ts
|
|
18
|
+
function replaceWorkspaceDeps(deps) {
|
|
19
|
+
if (!deps) return void 0;
|
|
20
|
+
const result = {};
|
|
21
|
+
for (const [key, value] of Object.entries(deps)) {
|
|
22
|
+
result[key] = value === "workspace:*" ? "latest" : value;
|
|
23
|
+
}
|
|
24
|
+
return result;
|
|
25
|
+
}
|
|
26
|
+
function createExtensionConfig(packageJson, config) {
|
|
27
|
+
const author = typeof packageJson.author === "string" ? packageJson.author : packageJson.author?.name || "Unknown";
|
|
28
|
+
const githubUrl = typeof packageJson.repository === "string" ? packageJson.repository : packageJson.repository?.url;
|
|
29
|
+
const packageNameWithoutScope = packageJson.name.split("/").pop() || packageJson.name;
|
|
30
|
+
const downloadUrl = `https://registry.npmjs.org/${packageJson.name}/-/${packageNameWithoutScope}-${packageJson.version}.tgz`;
|
|
31
|
+
const marketplaceId = packageJson.name.replace("@", "").replace("/", "-");
|
|
32
|
+
return {
|
|
33
|
+
// From package.json
|
|
34
|
+
name: config.name,
|
|
35
|
+
version: packageJson.version,
|
|
36
|
+
author,
|
|
37
|
+
description: packageJson.description,
|
|
38
|
+
keywords: packageJson.keywords,
|
|
39
|
+
license: packageJson.license,
|
|
40
|
+
homepage: packageJson.homepage,
|
|
41
|
+
githubUrl,
|
|
42
|
+
packageDependencies: replaceWorkspaceDeps(packageJson.dependencies),
|
|
43
|
+
packagePeerDependencies: replaceWorkspaceDeps(packageJson.peerDependencies),
|
|
44
|
+
packageDevDependencies: replaceWorkspaceDeps(packageJson.devDependencies),
|
|
45
|
+
// From manual config
|
|
46
|
+
displayName: config.displayName,
|
|
47
|
+
category: config.category,
|
|
48
|
+
features: config.features,
|
|
49
|
+
examples: config.examples,
|
|
50
|
+
minVersion: config.minVersion,
|
|
51
|
+
githubStars: config.githubStars,
|
|
52
|
+
// Auto-generated
|
|
53
|
+
npmUrl: `https://www.npmjs.com/package/${packageJson.name}`,
|
|
54
|
+
marketplaceId,
|
|
55
|
+
// Only generate marketplace URL for official @djangocfg extensions
|
|
56
|
+
marketplaceUrl: packageJson.name.startsWith("@djangocfg/ext-") ? `https://hub.djangocfg.com/extensions/${marketplaceId}` : void 0,
|
|
57
|
+
installCommand: `pnpm add ${packageJson.name}`,
|
|
58
|
+
downloadUrl,
|
|
59
|
+
tags: packageJson.keywords,
|
|
60
|
+
preview: `https://unpkg.com/${packageJson.name}@latest/preview.png`
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/extensionConfig.ts
|
|
65
|
+
var extensionConfig = createExtensionConfig(package_default, {
|
|
66
|
+
name: "base",
|
|
67
|
+
displayName: "Extension Base",
|
|
68
|
+
category: "utilities",
|
|
69
|
+
features: [
|
|
70
|
+
"CLI for creating extensions",
|
|
71
|
+
"Next.js 15 playground for rapid development",
|
|
72
|
+
"Smart Provider Pattern for flexible component usage",
|
|
73
|
+
"Extension context and provider system",
|
|
74
|
+
"Pagination hooks (standard & infinite scroll)",
|
|
75
|
+
"API client utilities with auth integration",
|
|
76
|
+
"Type-safe context creation helpers",
|
|
77
|
+
"Logging system with structured output",
|
|
78
|
+
"Environment detection (dev, prod, static)",
|
|
79
|
+
"TypeScript types and utilities"
|
|
80
|
+
],
|
|
81
|
+
minVersion: "2.0.0",
|
|
82
|
+
examples: [
|
|
83
|
+
{
|
|
84
|
+
title: "Create Extension with CLI",
|
|
85
|
+
description: "Interactive wizard with playground environment",
|
|
86
|
+
code: `# Create extension with interactive wizard
|
|
87
|
+
pnpm dlx @djangocfg/ext-base create
|
|
88
|
+
|
|
89
|
+
# Quick test extension (auto-cleanup)
|
|
90
|
+
pnpm dlx @djangocfg/ext-base test
|
|
91
|
+
|
|
92
|
+
# List all extensions
|
|
93
|
+
pnpm dlx @djangocfg/ext-base list`,
|
|
94
|
+
language: "bash"
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
title: "Development with Playground",
|
|
98
|
+
description: "Start Next.js playground for rapid testing",
|
|
99
|
+
code: `cd your-extension
|
|
100
|
+
|
|
101
|
+
# Start playground (auto-builds extension + opens browser)
|
|
102
|
+
pnpm dev:playground
|
|
103
|
+
|
|
104
|
+
# Build for production
|
|
105
|
+
pnpm build
|
|
106
|
+
|
|
107
|
+
# Type check
|
|
108
|
+
pnpm check`,
|
|
109
|
+
language: "bash"
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
title: "Smart Provider Pattern",
|
|
113
|
+
description: "Components that work standalone or with shared context",
|
|
114
|
+
code: `import { withSmartProvider } from '@your-org/my-extension';
|
|
115
|
+
|
|
116
|
+
// Your component
|
|
117
|
+
function MyComponent() {
|
|
118
|
+
const { data } = useMyExtension();
|
|
119
|
+
return <div>{data}</div>;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Wrap with smart provider
|
|
123
|
+
export const MySmartComponent = withSmartProvider(MyComponent);
|
|
124
|
+
|
|
125
|
+
// Usage 1: Standalone (auto-wrapped)
|
|
126
|
+
<MySmartComponent />
|
|
127
|
+
|
|
128
|
+
// Usage 2: Manual provider (shared context)
|
|
129
|
+
<MyExtensionProvider>
|
|
130
|
+
<MySmartComponent />
|
|
131
|
+
<MySmartComponent />
|
|
132
|
+
</MyExtensionProvider>`,
|
|
133
|
+
language: "tsx"
|
|
134
|
+
},
|
|
135
|
+
{
|
|
136
|
+
title: "Extension Provider",
|
|
137
|
+
description: "Register and wrap your extension",
|
|
138
|
+
code: `import { ExtensionProvider } from '@djangocfg/ext-base/hooks';
|
|
139
|
+
import { extensionConfig } from './config';
|
|
140
|
+
|
|
141
|
+
export function MyExtensionProvider({ children }) {
|
|
142
|
+
return (
|
|
143
|
+
<ExtensionProvider metadata={extensionConfig}>
|
|
144
|
+
{children}
|
|
145
|
+
</ExtensionProvider>
|
|
146
|
+
);
|
|
147
|
+
}`,
|
|
148
|
+
language: "tsx"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
title: "Pagination Hooks",
|
|
152
|
+
description: "Built-in hooks for standard and infinite scroll",
|
|
153
|
+
code: `import { usePagination, useInfinitePagination } from '@djangocfg/ext-base/hooks';
|
|
154
|
+
|
|
155
|
+
// Standard pagination
|
|
156
|
+
const { items, page, totalPages, nextPage, prevPage } = usePagination({
|
|
157
|
+
keyPrefix: 'articles',
|
|
158
|
+
fetcher: async (page, pageSize) => api.articles.list({ page, page_size: pageSize }),
|
|
159
|
+
pageSize: 20,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Infinite scroll
|
|
163
|
+
const { items, isLoading, hasMore, loadMore } = useInfinitePagination({
|
|
164
|
+
keyPrefix: 'articles',
|
|
165
|
+
fetcher: async (page, pageSize) => api.articles.list({ page, page_size: pageSize }),
|
|
166
|
+
pageSize: 20,
|
|
167
|
+
});`,
|
|
168
|
+
language: "tsx"
|
|
169
|
+
}
|
|
170
|
+
]
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// src/utils/errors.ts
|
|
174
|
+
function isExtensionError(error) {
|
|
175
|
+
return typeof error === "object" && error !== null && "message" in error && "timestamp" in error;
|
|
176
|
+
}
|
|
177
|
+
function createExtensionError(error, code, details) {
|
|
178
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
179
|
+
return {
|
|
180
|
+
message,
|
|
181
|
+
code,
|
|
182
|
+
details,
|
|
183
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString()
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function formatErrorMessage(error) {
|
|
187
|
+
if (isExtensionError(error)) {
|
|
188
|
+
return error.code ? `[${error.code}] ${error.message}` : error.message;
|
|
189
|
+
}
|
|
190
|
+
if (error instanceof Error) {
|
|
191
|
+
return error.message;
|
|
192
|
+
}
|
|
193
|
+
return String(error);
|
|
194
|
+
}
|
|
195
|
+
function handleExtensionError(error, logger, callback) {
|
|
196
|
+
const extensionError = isExtensionError(error) ? error : createExtensionError(error);
|
|
197
|
+
if (logger) {
|
|
198
|
+
logger.error("Extension error:", extensionError);
|
|
199
|
+
}
|
|
200
|
+
if (callback) {
|
|
201
|
+
callback(extensionError);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
var isDevelopment = process.env.NODE_ENV === "development";
|
|
205
|
+
var LEVEL_MAP = {
|
|
206
|
+
debug: 4,
|
|
207
|
+
info: 3,
|
|
208
|
+
warn: 2,
|
|
209
|
+
error: 1
|
|
210
|
+
};
|
|
211
|
+
function createExtensionLogger(options) {
|
|
212
|
+
const { tag, level = "info", enabled = true } = options;
|
|
213
|
+
if (!enabled) {
|
|
214
|
+
const noop = () => {
|
|
215
|
+
};
|
|
216
|
+
return {
|
|
217
|
+
info: noop,
|
|
218
|
+
warn: noop,
|
|
219
|
+
error: noop,
|
|
220
|
+
debug: noop,
|
|
221
|
+
success: noop
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
const logLevel = isDevelopment ? LEVEL_MAP[level] : LEVEL_MAP.error;
|
|
225
|
+
const consola = createConsola({
|
|
226
|
+
level: logLevel
|
|
227
|
+
}).withTag(tag);
|
|
228
|
+
return {
|
|
229
|
+
info: (...args) => consola.info(...args),
|
|
230
|
+
warn: (...args) => consola.warn(...args),
|
|
231
|
+
error: (...args) => consola.error(...args),
|
|
232
|
+
debug: (...args) => consola.debug(...args),
|
|
233
|
+
success: (...args) => consola.success(...args)
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export { EXTENSION_CATEGORIES, createExtensionConfig, createExtensionError, createExtensionLogger, extensionConfig, formatErrorMessage, handleExtensionError, isExtensionError };
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
2
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
3
|
+
}) : x)(function(x) {
|
|
4
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
5
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
6
|
+
});
|
|
7
|
+
|
|
8
|
+
export { __require };
|