@nextsparkjs/ai-workflow 0.1.0-beta.100
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/LICENSE +21 -0
- package/README.md +115 -0
- package/claude/_docs/workflows-optimizations.md +359 -0
- package/claude/agents/api-tester.md +634 -0
- package/claude/agents/architecture-supervisor.md +1351 -0
- package/claude/agents/backend-developer.md +997 -0
- package/claude/agents/backend-validator.md +417 -0
- package/claude/agents/bdd-docs-writer.md +737 -0
- package/claude/agents/block-developer.md +677 -0
- package/claude/agents/code-reviewer.md +1432 -0
- package/claude/agents/db-developer.md +721 -0
- package/claude/agents/db-validator.md +407 -0
- package/claude/agents/demo-video-generator.md +493 -0
- package/claude/agents/documentation-writer.md +1268 -0
- package/claude/agents/frontend-developer.md +1234 -0
- package/claude/agents/frontend-validator.md +777 -0
- package/claude/agents/functional-validator.md +630 -0
- package/claude/agents/mock-analyst.md +387 -0
- package/claude/agents/product-manager.md +963 -0
- package/claude/agents/qa-automation.md +1762 -0
- package/claude/agents/release-manager.md +634 -0
- package/claude/agents/selectors-translator.md +262 -0
- package/claude/agents/unit-test-writer.md +785 -0
- package/claude/agents/visual-comparator.md +329 -0
- package/claude/agents/workflow-maintainer.md +352 -0
- package/claude/commands/do/README.md +88 -0
- package/claude/commands/do/create-api.md +64 -0
- package/claude/commands/do/create-entity.md +66 -0
- package/claude/commands/do/create-migration.md +64 -0
- package/claude/commands/do/create-plugin.md +56 -0
- package/claude/commands/do/create-theme.md +70 -0
- package/claude/commands/do/mock-data.md +67 -0
- package/claude/commands/do/reset-db.md +71 -0
- package/claude/commands/do/setup-scheduled-action.md +75 -0
- package/claude/commands/do/sync-code-review.md +117 -0
- package/claude/commands/do/update-selectors.md +112 -0
- package/claude/commands/do/use-skills.md +90 -0
- package/claude/commands/do/validate-blocks.md +69 -0
- package/claude/commands/how-to/README.md +261 -0
- package/claude/commands/how-to/add-metadata.md +692 -0
- package/claude/commands/how-to/add-taxonomies.md +806 -0
- package/claude/commands/how-to/add-translations.md +571 -0
- package/claude/commands/how-to/create-api.md +577 -0
- package/claude/commands/how-to/create-block.md +575 -0
- package/claude/commands/how-to/create-child-entities.md +771 -0
- package/claude/commands/how-to/create-entity.md +597 -0
- package/claude/commands/how-to/create-migrations.md +605 -0
- package/claude/commands/how-to/create-plugin.md +654 -0
- package/claude/commands/how-to/customize-app.md +481 -0
- package/claude/commands/how-to/customize-dashboard.md +553 -0
- package/claude/commands/how-to/customize-theme.md +438 -0
- package/claude/commands/how-to/define-features-flows.md +632 -0
- package/claude/commands/how-to/deploy.md +507 -0
- package/claude/commands/how-to/handle-file-uploads.md +746 -0
- package/claude/commands/how-to/implement-search.md +1001 -0
- package/claude/commands/how-to/install-plugins.md +352 -0
- package/claude/commands/how-to/manage-test-coverage.md +984 -0
- package/claude/commands/how-to/run-tests.md +400 -0
- package/claude/commands/how-to/set-app-languages.md +601 -0
- package/claude/commands/how-to/set-plans-and-permissions.md +575 -0
- package/claude/commands/how-to/set-scheduled-actions.md +527 -0
- package/claude/commands/how-to/set-user-roles-and-permissions.md +550 -0
- package/claude/commands/how-to/setup-authentication.md +388 -0
- package/claude/commands/how-to/setup-claude-code.md +440 -0
- package/claude/commands/how-to/setup-database.md +274 -0
- package/claude/commands/how-to/setup-email-providers.md +598 -0
- package/claude/commands/how-to/setup-mobile-dev.md +627 -0
- package/claude/commands/how-to/start.md +500 -0
- package/claude/commands/how-to/use-devtools.md +639 -0
- package/claude/commands/how-to/use-superadmin.md +622 -0
- package/claude/commands/session/README.md +193 -0
- package/claude/commands/session/block-create.md +190 -0
- package/claude/commands/session/block-list.md +203 -0
- package/claude/commands/session/block-update.md +192 -0
- package/claude/commands/session/block-validate.md +218 -0
- package/claude/commands/session/changelog.md +115 -0
- package/claude/commands/session/close.md +225 -0
- package/claude/commands/session/commit.md +174 -0
- package/claude/commands/session/db-entity.md +206 -0
- package/claude/commands/session/db-fix.md +212 -0
- package/claude/commands/session/db-sample.md +206 -0
- package/claude/commands/session/demo.md +178 -0
- package/claude/commands/session/doc-bdd.md +207 -0
- package/claude/commands/session/doc-feature.md +218 -0
- package/claude/commands/session/doc-read.md +225 -0
- package/claude/commands/session/execute.md +204 -0
- package/claude/commands/session/explain.md +202 -0
- package/claude/commands/session/fix-bug.md +210 -0
- package/claude/commands/session/fix-build.md +182 -0
- package/claude/commands/session/fix-test.md +189 -0
- package/claude/commands/session/pending.md +232 -0
- package/claude/commands/session/refine.md +188 -0
- package/claude/commands/session/resume.md +192 -0
- package/claude/commands/session/review.md +192 -0
- package/claude/commands/session/scope-change.md +181 -0
- package/claude/commands/session/start-blocks.md +347 -0
- package/claude/commands/session/start.md +604 -0
- package/claude/commands/session/status.md +169 -0
- package/claude/commands/session/test-fix.md +221 -0
- package/claude/commands/session/test-run.md +203 -0
- package/claude/commands/session/test-write.md +242 -0
- package/claude/commands/session/validate.md +162 -0
- package/claude/config/context.json +40 -0
- package/claude/config/github.json +69 -0
- package/claude/config/github.schema.json +106 -0
- package/claude/config/team.json +46 -0
- package/claude/config/team.schema.json +106 -0
- package/claude/config/workspace.json +43 -0
- package/claude/config/workspace.schema.json +75 -0
- package/claude/skills/README.md +228 -0
- package/claude/skills/accessibility/SKILL.md +573 -0
- package/claude/skills/api-bypass-layers/SKILL.md +550 -0
- package/claude/skills/asana-integration/SKILL.md +499 -0
- package/claude/skills/better-auth/SKILL.md +666 -0
- package/claude/skills/billing-subscriptions/SKILL.md +660 -0
- package/claude/skills/block-decision-matrix/SKILL.md +359 -0
- package/claude/skills/clickup-integration/SKILL.md +434 -0
- package/claude/skills/core-theme-responsibilities/SKILL.md +485 -0
- package/claude/skills/create-plugin/SKILL.md +425 -0
- package/claude/skills/create-theme/SKILL.md +331 -0
- package/claude/skills/cypress-api/SKILL.md +511 -0
- package/claude/skills/cypress-api/scripts/generate-api-controller.py +329 -0
- package/claude/skills/cypress-api/scripts/generate-api-test.py +930 -0
- package/claude/skills/cypress-e2e/SKILL.md +526 -0
- package/claude/skills/cypress-e2e/scripts/extract-selectors.py +383 -0
- package/claude/skills/cypress-e2e/scripts/generate-uat-test.py +788 -0
- package/claude/skills/cypress-selectors/SKILL.md +309 -0
- package/claude/skills/cypress-selectors/scripts/extract-missing.py +243 -0
- package/claude/skills/cypress-selectors/scripts/generate-block-selectors.py +283 -0
- package/claude/skills/cypress-selectors/scripts/validate-selectors.py +145 -0
- package/claude/skills/database-migrations/SKILL.md +335 -0
- package/claude/skills/database-migrations/scripts/generate-sample-data.py +284 -0
- package/claude/skills/database-migrations/scripts/validate-migration.py +323 -0
- package/claude/skills/design-system/SKILL.md +682 -0
- package/claude/skills/documentation/SKILL.md +540 -0
- package/claude/skills/entity-api/SKILL.md +482 -0
- package/claude/skills/entity-system/SKILL.md +635 -0
- package/claude/skills/entity-system/scripts/generate-child-migration.py +298 -0
- package/claude/skills/entity-system/scripts/generate-metas-migration.py +233 -0
- package/claude/skills/entity-system/scripts/generate-migration.py +382 -0
- package/claude/skills/entity-system/scripts/generate-sample-data.py +418 -0
- package/claude/skills/entity-system/scripts/scaffold-entity.py +661 -0
- package/claude/skills/github/SKILL.md +467 -0
- package/claude/skills/i18n-nextintl/SKILL.md +302 -0
- package/claude/skills/i18n-nextintl/scripts/add-translation.py +243 -0
- package/claude/skills/i18n-nextintl/scripts/extract-hardcoded.py +246 -0
- package/claude/skills/i18n-nextintl/scripts/validate-translations.py +260 -0
- package/claude/skills/impact-analysis/SKILL.md +203 -0
- package/claude/skills/jest-unit/SKILL.md +306 -0
- package/claude/skills/jest-unit/references/component-testing.md +371 -0
- package/claude/skills/jest-unit/references/mocking-patterns.md +380 -0
- package/claude/skills/jest-unit/references/service-hook-testing.md +454 -0
- package/claude/skills/jira-integration/SKILL.md +539 -0
- package/claude/skills/media-library/SKILL.md +743 -0
- package/claude/skills/mock-analysis/SKILL.md +276 -0
- package/claude/skills/monorepo-architecture/SKILL.md +162 -0
- package/claude/skills/nextjs-api-development/SKILL.md +364 -0
- package/claude/skills/nextjs-api-development/scripts/generate-crud-tests.py +456 -0
- package/claude/skills/nextjs-api-development/scripts/scaffold-endpoint.py +481 -0
- package/claude/skills/nextjs-api-development/scripts/validate-api.py +283 -0
- package/claude/skills/notion-integration/SKILL.md +641 -0
- package/claude/skills/npm-development-workflow/SKILL.md +480 -0
- package/claude/skills/page-builder-blocks/SKILL.md +530 -0
- package/claude/skills/page-builder-blocks/scripts/scaffold-block.py +444 -0
- package/claude/skills/permissions-system/SKILL.md +619 -0
- package/claude/skills/plugins/SKILL.md +340 -0
- package/claude/skills/plugins/references/plugin-templates.md +414 -0
- package/claude/skills/plugins/references/plugin-testing.md +353 -0
- package/claude/skills/plugins/references/plugin-types.md +198 -0
- package/claude/skills/plugins/scripts/scaffold-plugin.py +443 -0
- package/claude/skills/pom-patterns/SKILL.md +452 -0
- package/claude/skills/pom-patterns/scripts/generate-pom.py +392 -0
- package/claude/skills/rate-limiting/SKILL.md +342 -0
- package/claude/skills/react-best-practices/AGENTS.md +2410 -0
- package/claude/skills/react-best-practices/README.md +123 -0
- package/claude/skills/react-best-practices/SKILL.md +125 -0
- package/claude/skills/react-best-practices/metadata.json +15 -0
- package/claude/skills/react-best-practices/rules/_sections.md +46 -0
- package/claude/skills/react-best-practices/rules/_template.md +28 -0
- package/claude/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/claude/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/claude/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/claude/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/claude/skills/react-best-practices/rules/async-dependencies.md +36 -0
- package/claude/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/claude/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/claude/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/claude/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/claude/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/claude/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/claude/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/claude/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/claude/skills/react-best-practices/rules/client-localstorage-schema.md +71 -0
- package/claude/skills/react-best-practices/rules/client-passive-event-listeners.md +48 -0
- package/claude/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/claude/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/claude/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/claude/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/claude/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/claude/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/claude/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/claude/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/claude/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/claude/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/claude/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/claude/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/claude/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/claude/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/claude/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/claude/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/claude/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/claude/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/claude/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/claude/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/claude/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/claude/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/claude/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/claude/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/claude/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/claude/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/claude/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/claude/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/claude/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/claude/skills/react-best-practices/rules/server-cache-react.md +76 -0
- package/claude/skills/react-best-practices/rules/server-parallel-fetching.md +83 -0
- package/claude/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/claude/skills/react-patterns/SKILL.md +688 -0
- package/claude/skills/registry-system/SKILL.md +331 -0
- package/claude/skills/scheduled-actions/SKILL.md +671 -0
- package/claude/skills/scope-enforcement/SKILL.md +542 -0
- package/claude/skills/scope-enforcement/scripts/validate-scope.py +357 -0
- package/claude/skills/server-actions/SKILL.md +493 -0
- package/claude/skills/service-layer/SKILL.md +587 -0
- package/claude/skills/session-management/SKILL.md +266 -0
- package/claude/skills/session-management/scripts/create-session.py +166 -0
- package/claude/skills/session-management/scripts/iteration-close.sh +105 -0
- package/claude/skills/session-management/scripts/iteration-init.sh +180 -0
- package/claude/skills/session-management/scripts/session-archive.sh +87 -0
- package/claude/skills/session-management/scripts/session-close.sh +133 -0
- package/claude/skills/session-management/scripts/session-init.sh +225 -0
- package/claude/skills/session-management/scripts/session-list.sh +163 -0
- package/claude/skills/session-management/scripts/split-plan.sh +116 -0
- package/claude/skills/shadcn-components/SKILL.md +586 -0
- package/claude/skills/shadcn-theming/SKILL.md +446 -0
- package/claude/skills/suspense-loading/SKILL.md +280 -0
- package/claude/skills/tailwind-theming/SKILL.md +507 -0
- package/claude/skills/tanstack-query/SKILL.md +608 -0
- package/claude/skills/test-coverage/SKILL.md +239 -0
- package/claude/skills/web-design-guidelines/SKILL.md +39 -0
- package/claude/skills/zod-validation/SKILL.md +537 -0
- package/claude/templates/blocks/progress.md +86 -0
- package/claude/templates/iteration/changes.md +61 -0
- package/claude/templates/iteration/progress.md +55 -0
- package/claude/templates/log.md +31 -0
- package/claude/templates/story/context.md +77 -0
- package/claude/templates/story/pendings.md +37 -0
- package/claude/templates/story/plan.md +299 -0
- package/claude/templates/story/requirements.md +109 -0
- package/claude/templates/story/scope.json +10 -0
- package/claude/templates/story/tests.md +91 -0
- package/claude/templates/task/progress.md +58 -0
- package/claude/templates/task/requirements.md +54 -0
- package/claude/workflows/README.md +154 -0
- package/claude/workflows/blocks.md +614 -0
- package/claude/workflows/story.md +1207 -0
- package/claude/workflows/task.md +927 -0
- package/claude/workflows/tweak.md +527 -0
- package/cursor/.gitkeep +0 -0
- package/package.json +35 -0
- package/scripts/postinstall.mjs +198 -0
- package/scripts/setup.mjs +282 -0
- package/scripts/sync.mjs +209 -0
|
@@ -0,0 +1,414 @@
|
|
|
1
|
+
# Plugin Templates Reference
|
|
2
|
+
|
|
3
|
+
Copy-paste templates for plugin components, hooks, and API endpoints.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## ⭐ Plugin Environment Loader (REQUIRED)
|
|
8
|
+
|
|
9
|
+
Every plugin MUST have a `lib/plugin-env.ts` using core's centralized env-loader:
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
// contents/plugins/my-plugin/lib/plugin-env.ts
|
|
13
|
+
import { getPluginEnv } from '@nextsparkjs/core/lib/plugins/env-loader'
|
|
14
|
+
|
|
15
|
+
interface MyPluginEnvConfig {
|
|
16
|
+
MY_PLUGIN_ENABLED?: string
|
|
17
|
+
MY_PLUGIN_DEBUG?: string
|
|
18
|
+
MY_PLUGIN_API_KEY?: string
|
|
19
|
+
MY_PLUGIN_TIMEOUT?: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
class PluginEnvironment {
|
|
23
|
+
private static instance: PluginEnvironment
|
|
24
|
+
private config: MyPluginEnvConfig = {}
|
|
25
|
+
private loaded = false
|
|
26
|
+
|
|
27
|
+
private constructor() {
|
|
28
|
+
this.loadEnvironment()
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
public static getInstance(): PluginEnvironment {
|
|
32
|
+
if (!PluginEnvironment.instance) {
|
|
33
|
+
PluginEnvironment.instance = new PluginEnvironment()
|
|
34
|
+
}
|
|
35
|
+
return PluginEnvironment.instance
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private loadEnvironment(forceReload: boolean = false): void {
|
|
39
|
+
if (this.loaded && !forceReload) return
|
|
40
|
+
|
|
41
|
+
try {
|
|
42
|
+
// Use centralized plugin env loader
|
|
43
|
+
// Priority: Plugin .env > Root .env > Defaults
|
|
44
|
+
const env = getPluginEnv('my-plugin')
|
|
45
|
+
|
|
46
|
+
this.config = {
|
|
47
|
+
MY_PLUGIN_ENABLED: env.MY_PLUGIN_ENABLED || 'true',
|
|
48
|
+
MY_PLUGIN_DEBUG: env.MY_PLUGIN_DEBUG || 'false',
|
|
49
|
+
MY_PLUGIN_API_KEY: env.MY_PLUGIN_API_KEY,
|
|
50
|
+
MY_PLUGIN_TIMEOUT: env.MY_PLUGIN_TIMEOUT || '5000',
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
this.loaded = true
|
|
54
|
+
} catch (error) {
|
|
55
|
+
console.error('[My Plugin] Failed to load environment:', error)
|
|
56
|
+
this.loaded = true
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public getConfig(): MyPluginEnvConfig {
|
|
61
|
+
if (!this.loaded) this.loadEnvironment()
|
|
62
|
+
return this.config
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// Type-safe helper methods
|
|
66
|
+
public isPluginEnabled(): boolean {
|
|
67
|
+
return this.getConfig().MY_PLUGIN_ENABLED !== 'false'
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
public isDebugEnabled(): boolean {
|
|
71
|
+
return this.getConfig().MY_PLUGIN_DEBUG === 'true'
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
public getApiKey(): string | undefined {
|
|
75
|
+
return this.getConfig().MY_PLUGIN_API_KEY
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
public getTimeout(): number {
|
|
79
|
+
return parseInt(this.getConfig().MY_PLUGIN_TIMEOUT || '5000', 10)
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
public reload(): void {
|
|
83
|
+
this.loaded = false
|
|
84
|
+
this.loadEnvironment(true)
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const pluginEnv = PluginEnvironment.getInstance()
|
|
89
|
+
|
|
90
|
+
// Convenience exports
|
|
91
|
+
export const isPluginEnabled = () => pluginEnv.isPluginEnabled()
|
|
92
|
+
export const isDebugEnabled = () => pluginEnv.isDebugEnabled()
|
|
93
|
+
export const getApiKey = () => pluginEnv.getApiKey()
|
|
94
|
+
export const getTimeout = () => pluginEnv.getTimeout()
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Benefits:**
|
|
98
|
+
- ✅ Plugin `.env` takes priority over root `.env`
|
|
99
|
+
- ✅ Automatic fallback to root `.env` for shared variables (API keys, etc.)
|
|
100
|
+
- ✅ Type-safe configuration access
|
|
101
|
+
- ✅ Singleton pattern for performance
|
|
102
|
+
- ✅ Reload support for testing
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## Type Definitions
|
|
107
|
+
|
|
108
|
+
```typescript
|
|
109
|
+
// contents/plugins/my-plugin/types/my-plugin.types.ts
|
|
110
|
+
|
|
111
|
+
// Configuration types
|
|
112
|
+
export interface MyPluginConfig {
|
|
113
|
+
readonly apiKey: string
|
|
114
|
+
readonly timeout: number
|
|
115
|
+
readonly maxRetries: number
|
|
116
|
+
readonly debugMode: boolean
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// Input/Output types
|
|
120
|
+
export interface MyPluginInput {
|
|
121
|
+
readonly data: string
|
|
122
|
+
readonly options?: MyPluginOptions
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export interface MyPluginOutput<T = unknown> {
|
|
126
|
+
readonly success: boolean
|
|
127
|
+
readonly data?: T
|
|
128
|
+
readonly error?: string
|
|
129
|
+
readonly metadata: {
|
|
130
|
+
readonly processingTime: number
|
|
131
|
+
readonly timestamp: string
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
// Options type
|
|
136
|
+
export interface MyPluginOptions {
|
|
137
|
+
readonly timeout?: number
|
|
138
|
+
readonly retryOnError?: boolean
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
|
|
144
|
+
## Component Template
|
|
145
|
+
|
|
146
|
+
```typescript
|
|
147
|
+
// contents/plugins/my-plugin/components/MyWidget.tsx
|
|
148
|
+
'use client'
|
|
149
|
+
|
|
150
|
+
import { useMyPlugin } from '../hooks/useMyPlugin'
|
|
151
|
+
import { Card, CardHeader, CardContent } from '@/core/components/ui/card'
|
|
152
|
+
import { Button } from '@/core/components/ui/button'
|
|
153
|
+
|
|
154
|
+
interface MyWidgetProps {
|
|
155
|
+
readonly title: string
|
|
156
|
+
readonly onAction?: () => void
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
export function MyWidget({ title, onAction }: MyWidgetProps) {
|
|
160
|
+
const { data, isLoading, error } = useMyPlugin()
|
|
161
|
+
|
|
162
|
+
return (
|
|
163
|
+
<Card data-cy="my-plugin-widget">
|
|
164
|
+
<CardHeader>
|
|
165
|
+
<h3>{title}</h3>
|
|
166
|
+
</CardHeader>
|
|
167
|
+
<CardContent>
|
|
168
|
+
{isLoading && <div data-cy="my-plugin-loading">Loading...</div>}
|
|
169
|
+
{error && <div data-cy="my-plugin-error">{error.message}</div>}
|
|
170
|
+
{data && (
|
|
171
|
+
<div data-cy="my-plugin-content">
|
|
172
|
+
{/* Content */}
|
|
173
|
+
</div>
|
|
174
|
+
)}
|
|
175
|
+
<Button
|
|
176
|
+
data-cy="my-plugin-action-btn"
|
|
177
|
+
onClick={onAction}
|
|
178
|
+
>
|
|
179
|
+
Action
|
|
180
|
+
</Button>
|
|
181
|
+
</CardContent>
|
|
182
|
+
</Card>
|
|
183
|
+
)
|
|
184
|
+
}
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
---
|
|
188
|
+
|
|
189
|
+
## Hook Templates
|
|
190
|
+
|
|
191
|
+
### Query Hook
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
// contents/plugins/my-plugin/hooks/useMyPlugin.ts
|
|
195
|
+
'use client'
|
|
196
|
+
|
|
197
|
+
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'
|
|
198
|
+
import type { MyPluginInput, MyPluginOutput } from '../types/my-plugin.types'
|
|
199
|
+
|
|
200
|
+
const QUERY_KEY = ['my-plugin'] as const
|
|
201
|
+
|
|
202
|
+
export function useMyPlugin() {
|
|
203
|
+
return useQuery({
|
|
204
|
+
queryKey: QUERY_KEY,
|
|
205
|
+
queryFn: async () => {
|
|
206
|
+
const response = await fetch('/api/plugin/my-plugin/data')
|
|
207
|
+
if (!response.ok) throw new Error('Failed to fetch')
|
|
208
|
+
return response.json()
|
|
209
|
+
}
|
|
210
|
+
})
|
|
211
|
+
}
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### Mutation Hook
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
export function useMyPluginMutation() {
|
|
218
|
+
const queryClient = useQueryClient()
|
|
219
|
+
|
|
220
|
+
return useMutation({
|
|
221
|
+
mutationFn: async (input: MyPluginInput) => {
|
|
222
|
+
const response = await fetch('/api/plugin/my-plugin/process', {
|
|
223
|
+
method: 'POST',
|
|
224
|
+
headers: { 'Content-Type': 'application/json' },
|
|
225
|
+
body: JSON.stringify(input)
|
|
226
|
+
})
|
|
227
|
+
if (!response.ok) throw new Error('Failed to process')
|
|
228
|
+
return response.json() as Promise<MyPluginOutput>
|
|
229
|
+
},
|
|
230
|
+
onSuccess: () => {
|
|
231
|
+
queryClient.invalidateQueries({ queryKey: QUERY_KEY })
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
}
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## API Endpoint Templates
|
|
240
|
+
|
|
241
|
+
### POST Endpoint with Validation
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
// contents/plugins/my-plugin/api/process/route.ts
|
|
245
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
246
|
+
import { z } from 'zod'
|
|
247
|
+
import { authenticateRequest } from '@/core/lib/auth/authenticateRequest'
|
|
248
|
+
|
|
249
|
+
const ProcessInputSchema = z.object({
|
|
250
|
+
data: z.string().min(1).max(10000),
|
|
251
|
+
options: z.object({
|
|
252
|
+
timeout: z.number().min(1000).max(30000).optional()
|
|
253
|
+
}).optional()
|
|
254
|
+
})
|
|
255
|
+
|
|
256
|
+
export async function POST(request: NextRequest) {
|
|
257
|
+
try {
|
|
258
|
+
// Authentication
|
|
259
|
+
const auth = await authenticateRequest(request)
|
|
260
|
+
if (!auth.isAuthenticated) {
|
|
261
|
+
return NextResponse.json(
|
|
262
|
+
{ success: false, error: 'Unauthorized' },
|
|
263
|
+
{ status: 401 }
|
|
264
|
+
)
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Validation
|
|
268
|
+
const body = await request.json()
|
|
269
|
+
const input = ProcessInputSchema.parse(body)
|
|
270
|
+
|
|
271
|
+
// Process
|
|
272
|
+
const result = await processData(input)
|
|
273
|
+
|
|
274
|
+
return NextResponse.json({
|
|
275
|
+
success: true,
|
|
276
|
+
data: result,
|
|
277
|
+
metadata: {
|
|
278
|
+
processingTime: Date.now(),
|
|
279
|
+
timestamp: new Date().toISOString()
|
|
280
|
+
}
|
|
281
|
+
})
|
|
282
|
+
} catch (error) {
|
|
283
|
+
if (error instanceof z.ZodError) {
|
|
284
|
+
return NextResponse.json(
|
|
285
|
+
{ success: false, error: 'Validation error', details: error.errors },
|
|
286
|
+
{ status: 400 }
|
|
287
|
+
)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
console.error('[My Plugin] Error:', error)
|
|
291
|
+
return NextResponse.json(
|
|
292
|
+
{ success: false, error: 'Internal server error' },
|
|
293
|
+
{ status: 500 }
|
|
294
|
+
)
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### GET Endpoint with Query Params
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// contents/plugins/my-plugin/api/data/route.ts
|
|
303
|
+
import { NextRequest, NextResponse } from 'next/server'
|
|
304
|
+
import { authenticateRequest } from '@/core/lib/auth/authenticateRequest'
|
|
305
|
+
|
|
306
|
+
export async function GET(request: NextRequest) {
|
|
307
|
+
try {
|
|
308
|
+
const auth = await authenticateRequest(request)
|
|
309
|
+
if (!auth.isAuthenticated) {
|
|
310
|
+
return NextResponse.json(
|
|
311
|
+
{ success: false, error: 'Unauthorized' },
|
|
312
|
+
{ status: 401 }
|
|
313
|
+
)
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Parse query params
|
|
317
|
+
const { searchParams } = new URL(request.url)
|
|
318
|
+
const page = parseInt(searchParams.get('page') || '1', 10)
|
|
319
|
+
const limit = parseInt(searchParams.get('limit') || '10', 10)
|
|
320
|
+
|
|
321
|
+
// Fetch data
|
|
322
|
+
const data = await fetchPluginData({ page, limit })
|
|
323
|
+
|
|
324
|
+
return NextResponse.json({
|
|
325
|
+
success: true,
|
|
326
|
+
data,
|
|
327
|
+
pagination: {
|
|
328
|
+
page,
|
|
329
|
+
limit,
|
|
330
|
+
total: data.total
|
|
331
|
+
}
|
|
332
|
+
})
|
|
333
|
+
} catch (error) {
|
|
334
|
+
console.error('[My Plugin] Error:', error)
|
|
335
|
+
return NextResponse.json(
|
|
336
|
+
{ success: false, error: 'Internal server error' },
|
|
337
|
+
{ status: 500 }
|
|
338
|
+
)
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
---
|
|
344
|
+
|
|
345
|
+
## Provider Template
|
|
346
|
+
|
|
347
|
+
```typescript
|
|
348
|
+
// contents/plugins/my-plugin/providers/MyPluginProvider.tsx
|
|
349
|
+
'use client'
|
|
350
|
+
|
|
351
|
+
import { createContext, useContext, useState, ReactNode } from 'react'
|
|
352
|
+
import type { MyPluginConfig } from '../types/my-plugin.types'
|
|
353
|
+
|
|
354
|
+
interface MyPluginContextValue {
|
|
355
|
+
config: MyPluginConfig | null
|
|
356
|
+
isInitialized: boolean
|
|
357
|
+
setConfig: (config: MyPluginConfig) => void
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
const MyPluginContext = createContext<MyPluginContextValue | null>(null)
|
|
361
|
+
|
|
362
|
+
interface MyPluginProviderProps {
|
|
363
|
+
children: ReactNode
|
|
364
|
+
initialConfig?: MyPluginConfig
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
export function MyPluginProvider({
|
|
368
|
+
children,
|
|
369
|
+
initialConfig
|
|
370
|
+
}: MyPluginProviderProps) {
|
|
371
|
+
const [config, setConfig] = useState<MyPluginConfig | null>(initialConfig || null)
|
|
372
|
+
const [isInitialized, setIsInitialized] = useState(!!initialConfig)
|
|
373
|
+
|
|
374
|
+
const value: MyPluginContextValue = {
|
|
375
|
+
config,
|
|
376
|
+
isInitialized,
|
|
377
|
+
setConfig: (newConfig) => {
|
|
378
|
+
setConfig(newConfig)
|
|
379
|
+
setIsInitialized(true)
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
return (
|
|
384
|
+
<MyPluginContext.Provider value={value}>
|
|
385
|
+
{children}
|
|
386
|
+
</MyPluginContext.Provider>
|
|
387
|
+
)
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
export function useMyPluginContext() {
|
|
391
|
+
const context = useContext(MyPluginContext)
|
|
392
|
+
if (!context) {
|
|
393
|
+
throw new Error('useMyPluginContext must be used within MyPluginProvider')
|
|
394
|
+
}
|
|
395
|
+
return context
|
|
396
|
+
}
|
|
397
|
+
```
|
|
398
|
+
|
|
399
|
+
---
|
|
400
|
+
|
|
401
|
+
## data-cy Selector Conventions
|
|
402
|
+
|
|
403
|
+
All plugin components MUST use data-cy selectors:
|
|
404
|
+
|
|
405
|
+
```typescript
|
|
406
|
+
// Naming convention: {plugin}-{component}-{element}
|
|
407
|
+
data-cy="my-plugin-widget" // Container
|
|
408
|
+
data-cy="my-plugin-input" // Input field
|
|
409
|
+
data-cy="my-plugin-action-btn" // Button
|
|
410
|
+
data-cy="my-plugin-loading" // Loading state
|
|
411
|
+
data-cy="my-plugin-error" // Error state
|
|
412
|
+
data-cy="my-plugin-success" // Success state
|
|
413
|
+
data-cy="my-plugin-content" // Content area
|
|
414
|
+
```
|