@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,481 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Scaffold API Endpoint Script
|
|
4
|
+
|
|
5
|
+
Creates the file structure for a new API endpoint following Next.js 15 App Router patterns.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python scaffold-endpoint.py --name ENDPOINT_NAME [--methods METHODS] [--auth AUTH_TYPE]
|
|
9
|
+
|
|
10
|
+
Options:
|
|
11
|
+
--name ENDPOINT_NAME Name of the endpoint (kebab-case)
|
|
12
|
+
--methods METHODS Comma-separated HTTP methods (default: GET,POST)
|
|
13
|
+
--auth AUTH_TYPE Authentication type: required, optional, none (default: required)
|
|
14
|
+
--override Create in (contents)/ folder for custom logic
|
|
15
|
+
--with-id Include [id] route for single resource operations
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import os
|
|
19
|
+
import sys
|
|
20
|
+
import argparse
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from datetime import datetime
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def to_camel_case(name: str) -> str:
|
|
26
|
+
"""Convert kebab-case to camelCase."""
|
|
27
|
+
components = name.split('-')
|
|
28
|
+
return components[0] + ''.join(x.title() for x in components[1:])
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def to_pascal_case(name: str) -> str:
|
|
32
|
+
"""Convert kebab-case to PascalCase."""
|
|
33
|
+
return ''.join(x.title() for x in name.split('-'))
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def to_snake_case(name: str) -> str:
|
|
37
|
+
"""Convert kebab-case to snake_case."""
|
|
38
|
+
return name.replace('-', '_')
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def generate_list_route(name: str, methods: list, auth_type: str) -> str:
|
|
42
|
+
"""Generate list/create route file."""
|
|
43
|
+
pascal = to_pascal_case(name)
|
|
44
|
+
singular = name.rstrip('s')
|
|
45
|
+
methods_str = ', '.join(methods)
|
|
46
|
+
|
|
47
|
+
# Build imports
|
|
48
|
+
imports = [
|
|
49
|
+
"import { NextRequest, NextResponse } from 'next/server'",
|
|
50
|
+
"import { queryWithRLS, mutateWithRLS } from '@/core/lib/db'",
|
|
51
|
+
]
|
|
52
|
+
|
|
53
|
+
helper_imports = [
|
|
54
|
+
"createApiResponse",
|
|
55
|
+
"createApiError",
|
|
56
|
+
"withApiLogging",
|
|
57
|
+
"handleCorsPreflightRequest",
|
|
58
|
+
"addCorsHeaders",
|
|
59
|
+
]
|
|
60
|
+
if 'GET' in methods:
|
|
61
|
+
helper_imports.extend(["createPaginationMeta", "parsePaginationParams"])
|
|
62
|
+
|
|
63
|
+
imports.append(f"import {{ {', '.join(helper_imports)} }} from '@/core/lib/api/helpers'")
|
|
64
|
+
|
|
65
|
+
if auth_type == 'required':
|
|
66
|
+
imports.append("import { authenticateRequest } from '@/core/lib/api/auth/dual-auth'")
|
|
67
|
+
|
|
68
|
+
if 'POST' in methods or 'PATCH' in methods:
|
|
69
|
+
imports.append("import { z } from 'zod'")
|
|
70
|
+
|
|
71
|
+
# Build endpoint comments
|
|
72
|
+
endpoint_lines = []
|
|
73
|
+
for m in methods:
|
|
74
|
+
endpoint_lines.append(f" * {m} /api/v1/{name}")
|
|
75
|
+
|
|
76
|
+
code = f'''/**
|
|
77
|
+
* {pascal} API Route
|
|
78
|
+
*
|
|
79
|
+
* Handles list and create operations for {name}.
|
|
80
|
+
*
|
|
81
|
+
* Endpoints:
|
|
82
|
+
{chr(10).join(endpoint_lines)}
|
|
83
|
+
*/
|
|
84
|
+
|
|
85
|
+
{chr(10).join(imports)}
|
|
86
|
+
'''
|
|
87
|
+
|
|
88
|
+
# Add schema if POST
|
|
89
|
+
if 'POST' in methods:
|
|
90
|
+
code += f'''
|
|
91
|
+
// Validation schema for creating {singular}
|
|
92
|
+
const Create{pascal}Schema = z.object({{
|
|
93
|
+
title: z.string().min(1).max(255),
|
|
94
|
+
description: z.string().optional(),
|
|
95
|
+
// Add more fields as needed
|
|
96
|
+
}})
|
|
97
|
+
'''
|
|
98
|
+
|
|
99
|
+
# OPTIONS handler
|
|
100
|
+
code += '''
|
|
101
|
+
// Handle CORS preflight
|
|
102
|
+
export async function OPTIONS() {
|
|
103
|
+
return handleCorsPreflightRequest()
|
|
104
|
+
}
|
|
105
|
+
'''
|
|
106
|
+
|
|
107
|
+
# GET handler
|
|
108
|
+
if 'GET' in methods:
|
|
109
|
+
code += f'''
|
|
110
|
+
// GET /api/v1/{name} - List {name}
|
|
111
|
+
export const GET = withApiLogging(async (req: NextRequest): Promise<NextResponse> => {{
|
|
112
|
+
try {{
|
|
113
|
+
'''
|
|
114
|
+
if auth_type == 'required':
|
|
115
|
+
code += ''' // Authenticate using dual auth
|
|
116
|
+
const authResult = await authenticateRequest(req)
|
|
117
|
+
|
|
118
|
+
if (!authResult.success) {
|
|
119
|
+
return NextResponse.json(
|
|
120
|
+
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
121
|
+
{ status: 401 }
|
|
122
|
+
)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (authResult.rateLimitResponse) {
|
|
126
|
+
return authResult.rateLimitResponse as NextResponse
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
'''
|
|
130
|
+
code += f''' const {{ page, limit, offset }} = parsePaginationParams(req)
|
|
131
|
+
|
|
132
|
+
// TODO: Implement data fetching with queryWithRLS
|
|
133
|
+
const data: unknown[] = []
|
|
134
|
+
const total = 0
|
|
135
|
+
|
|
136
|
+
const paginationMeta = createPaginationMeta(page, limit, total)
|
|
137
|
+
const response = createApiResponse(data, paginationMeta)
|
|
138
|
+
return addCorsHeaders(response)
|
|
139
|
+
}} catch (error) {{
|
|
140
|
+
console.error('[{name.upper()}] List error:', error)
|
|
141
|
+
const response = createApiError('Failed to fetch {name}', 500)
|
|
142
|
+
return addCorsHeaders(response)
|
|
143
|
+
}}
|
|
144
|
+
}})
|
|
145
|
+
'''
|
|
146
|
+
|
|
147
|
+
# POST handler
|
|
148
|
+
if 'POST' in methods:
|
|
149
|
+
code += f'''
|
|
150
|
+
// POST /api/v1/{name} - Create {singular}
|
|
151
|
+
export const POST = withApiLogging(async (req: NextRequest): Promise<NextResponse> => {{
|
|
152
|
+
try {{
|
|
153
|
+
'''
|
|
154
|
+
if auth_type == 'required':
|
|
155
|
+
code += ''' // Authenticate using dual auth
|
|
156
|
+
const authResult = await authenticateRequest(req)
|
|
157
|
+
|
|
158
|
+
if (!authResult.success) {
|
|
159
|
+
return NextResponse.json(
|
|
160
|
+
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
161
|
+
{ status: 401 }
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
if (authResult.rateLimitResponse) {
|
|
166
|
+
return authResult.rateLimitResponse as NextResponse
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
'''
|
|
170
|
+
code += f''' // Validate input
|
|
171
|
+
const body = Create{pascal}Schema.parse(await req.json())
|
|
172
|
+
|
|
173
|
+
// TODO: Implement creation with mutateWithRLS
|
|
174
|
+
const created = {{ id: 'new-id', ...body }}
|
|
175
|
+
|
|
176
|
+
const response = createApiResponse(created, {{ created: true }}, 201)
|
|
177
|
+
return addCorsHeaders(response)
|
|
178
|
+
}} catch (error) {{
|
|
179
|
+
if (error instanceof z.ZodError) {{
|
|
180
|
+
const response = createApiError('Validation error', 400, error.issues, 'VALIDATION_ERROR')
|
|
181
|
+
return addCorsHeaders(response)
|
|
182
|
+
}}
|
|
183
|
+
console.error('[{name.upper()}] Create error:', error)
|
|
184
|
+
const response = createApiError('Failed to create {singular}', 500)
|
|
185
|
+
return addCorsHeaders(response)
|
|
186
|
+
}}
|
|
187
|
+
}})
|
|
188
|
+
'''
|
|
189
|
+
|
|
190
|
+
return code
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
def generate_id_route(name: str, methods: list, auth_type: str) -> str:
|
|
194
|
+
"""Generate single resource route file."""
|
|
195
|
+
pascal = to_pascal_case(name)
|
|
196
|
+
singular = name.rstrip('s')
|
|
197
|
+
|
|
198
|
+
# Build imports
|
|
199
|
+
imports = [
|
|
200
|
+
"import { NextRequest, NextResponse } from 'next/server'",
|
|
201
|
+
"import { queryWithRLS, mutateWithRLS } from '@/core/lib/db'",
|
|
202
|
+
]
|
|
203
|
+
|
|
204
|
+
helper_imports = [
|
|
205
|
+
"createApiResponse",
|
|
206
|
+
"createApiError",
|
|
207
|
+
"withApiLogging",
|
|
208
|
+
"handleCorsPreflightRequest",
|
|
209
|
+
"addCorsHeaders",
|
|
210
|
+
]
|
|
211
|
+
|
|
212
|
+
imports.append(f"import {{ {', '.join(helper_imports)} }} from '@/core/lib/api/helpers'")
|
|
213
|
+
|
|
214
|
+
if auth_type == 'required':
|
|
215
|
+
imports.append("import { authenticateRequest } from '@/core/lib/api/auth/dual-auth'")
|
|
216
|
+
|
|
217
|
+
if 'PATCH' in methods:
|
|
218
|
+
imports.append("import { z } from 'zod'")
|
|
219
|
+
|
|
220
|
+
code = f'''/**
|
|
221
|
+
* {pascal} Single Resource API Route
|
|
222
|
+
*
|
|
223
|
+
* Handles read, update, and delete operations for a single {singular}.
|
|
224
|
+
*
|
|
225
|
+
* Endpoints:
|
|
226
|
+
* GET /api/v1/{name}/[id]
|
|
227
|
+
* PATCH /api/v1/{name}/[id]
|
|
228
|
+
* DELETE /api/v1/{name}/[id]
|
|
229
|
+
*/
|
|
230
|
+
|
|
231
|
+
{chr(10).join(imports)}
|
|
232
|
+
|
|
233
|
+
interface RouteParams {{
|
|
234
|
+
params: Promise<{{ id: string }}>
|
|
235
|
+
}}
|
|
236
|
+
'''
|
|
237
|
+
|
|
238
|
+
# Add schema if PATCH
|
|
239
|
+
if 'PATCH' in methods:
|
|
240
|
+
code += f'''
|
|
241
|
+
// Validation schema for updating {singular}
|
|
242
|
+
const Update{pascal}Schema = z.object({{
|
|
243
|
+
title: z.string().min(1).max(255).optional(),
|
|
244
|
+
description: z.string().optional(),
|
|
245
|
+
// Add more fields as needed
|
|
246
|
+
}}).partial()
|
|
247
|
+
'''
|
|
248
|
+
|
|
249
|
+
# OPTIONS handler
|
|
250
|
+
code += '''
|
|
251
|
+
// Handle CORS preflight
|
|
252
|
+
export async function OPTIONS() {
|
|
253
|
+
return handleCorsPreflightRequest()
|
|
254
|
+
}
|
|
255
|
+
'''
|
|
256
|
+
|
|
257
|
+
# GET handler
|
|
258
|
+
if 'GET' in methods:
|
|
259
|
+
code += f'''
|
|
260
|
+
// GET /api/v1/{name}/[id] - Get single {singular}
|
|
261
|
+
export const GET = withApiLogging(async (
|
|
262
|
+
req: NextRequest,
|
|
263
|
+
{{ params }}: RouteParams
|
|
264
|
+
): Promise<NextResponse> => {{
|
|
265
|
+
const {{ id }} = await params
|
|
266
|
+
|
|
267
|
+
try {{
|
|
268
|
+
'''
|
|
269
|
+
if auth_type == 'required':
|
|
270
|
+
code += ''' // Authenticate using dual auth
|
|
271
|
+
const authResult = await authenticateRequest(req)
|
|
272
|
+
|
|
273
|
+
if (!authResult.success) {
|
|
274
|
+
return NextResponse.json(
|
|
275
|
+
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
276
|
+
{ status: 401 }
|
|
277
|
+
)
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
if (authResult.rateLimitResponse) {
|
|
281
|
+
return authResult.rateLimitResponse as NextResponse
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
'''
|
|
285
|
+
code += f''' // TODO: Implement fetching by ID with queryWithRLS
|
|
286
|
+
const data = {{ id }}
|
|
287
|
+
|
|
288
|
+
if (!data) {{
|
|
289
|
+
const response = createApiError('{singular.title()} not found', 404)
|
|
290
|
+
return addCorsHeaders(response)
|
|
291
|
+
}}
|
|
292
|
+
|
|
293
|
+
const response = createApiResponse(data)
|
|
294
|
+
return addCorsHeaders(response)
|
|
295
|
+
}} catch (error) {{
|
|
296
|
+
console.error('[{name.upper()}] Get error:', error)
|
|
297
|
+
const response = createApiError('Failed to fetch {singular}', 500)
|
|
298
|
+
return addCorsHeaders(response)
|
|
299
|
+
}}
|
|
300
|
+
}})
|
|
301
|
+
'''
|
|
302
|
+
|
|
303
|
+
# PATCH handler
|
|
304
|
+
if 'PATCH' in methods:
|
|
305
|
+
code += f'''
|
|
306
|
+
// PATCH /api/v1/{name}/[id] - Update {singular}
|
|
307
|
+
export const PATCH = withApiLogging(async (
|
|
308
|
+
req: NextRequest,
|
|
309
|
+
{{ params }}: RouteParams
|
|
310
|
+
): Promise<NextResponse> => {{
|
|
311
|
+
const {{ id }} = await params
|
|
312
|
+
|
|
313
|
+
try {{
|
|
314
|
+
'''
|
|
315
|
+
if auth_type == 'required':
|
|
316
|
+
code += ''' // Authenticate using dual auth
|
|
317
|
+
const authResult = await authenticateRequest(req)
|
|
318
|
+
|
|
319
|
+
if (!authResult.success) {
|
|
320
|
+
return NextResponse.json(
|
|
321
|
+
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
322
|
+
{ status: 401 }
|
|
323
|
+
)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
if (authResult.rateLimitResponse) {
|
|
327
|
+
return authResult.rateLimitResponse as NextResponse
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
'''
|
|
331
|
+
code += f''' // Validate input
|
|
332
|
+
const body = Update{pascal}Schema.parse(await req.json())
|
|
333
|
+
|
|
334
|
+
// TODO: Implement update with mutateWithRLS
|
|
335
|
+
const updated = {{ id, ...body }}
|
|
336
|
+
|
|
337
|
+
const response = createApiResponse(updated)
|
|
338
|
+
return addCorsHeaders(response)
|
|
339
|
+
}} catch (error) {{
|
|
340
|
+
if (error instanceof z.ZodError) {{
|
|
341
|
+
const response = createApiError('Validation error', 400, error.issues, 'VALIDATION_ERROR')
|
|
342
|
+
return addCorsHeaders(response)
|
|
343
|
+
}}
|
|
344
|
+
console.error('[{name.upper()}] Update error:', error)
|
|
345
|
+
const response = createApiError('Failed to update {singular}', 500)
|
|
346
|
+
return addCorsHeaders(response)
|
|
347
|
+
}}
|
|
348
|
+
}})
|
|
349
|
+
'''
|
|
350
|
+
|
|
351
|
+
# DELETE handler
|
|
352
|
+
if 'DELETE' in methods:
|
|
353
|
+
code += f'''
|
|
354
|
+
// DELETE /api/v1/{name}/[id] - Delete {singular}
|
|
355
|
+
export const DELETE = withApiLogging(async (
|
|
356
|
+
req: NextRequest,
|
|
357
|
+
{{ params }}: RouteParams
|
|
358
|
+
): Promise<NextResponse> => {{
|
|
359
|
+
const {{ id }} = await params
|
|
360
|
+
|
|
361
|
+
try {{
|
|
362
|
+
'''
|
|
363
|
+
if auth_type == 'required':
|
|
364
|
+
code += ''' // Authenticate using dual auth
|
|
365
|
+
const authResult = await authenticateRequest(req)
|
|
366
|
+
|
|
367
|
+
if (!authResult.success) {
|
|
368
|
+
return NextResponse.json(
|
|
369
|
+
{ success: false, error: 'Authentication required', code: 'AUTHENTICATION_FAILED' },
|
|
370
|
+
{ status: 401 }
|
|
371
|
+
)
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
if (authResult.rateLimitResponse) {
|
|
375
|
+
return authResult.rateLimitResponse as NextResponse
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
'''
|
|
379
|
+
code += f''' // TODO: Implement delete with mutateWithRLS
|
|
380
|
+
|
|
381
|
+
const response = createApiResponse({{ deleted: true, id }})
|
|
382
|
+
return addCorsHeaders(response)
|
|
383
|
+
}} catch (error) {{
|
|
384
|
+
console.error('[{name.upper()}] Delete error:', error)
|
|
385
|
+
const response = createApiError('Failed to delete {singular}', 500)
|
|
386
|
+
return addCorsHeaders(response)
|
|
387
|
+
}}
|
|
388
|
+
}})
|
|
389
|
+
'''
|
|
390
|
+
|
|
391
|
+
return code
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
def main():
|
|
395
|
+
parser = argparse.ArgumentParser(description='Scaffold API endpoint')
|
|
396
|
+
parser.add_argument('--name', required=True, help='Endpoint name (kebab-case)')
|
|
397
|
+
parser.add_argument('--methods', default='GET,POST', help='HTTP methods (comma-separated)')
|
|
398
|
+
parser.add_argument('--auth', choices=['required', 'optional', 'none'], default='required')
|
|
399
|
+
parser.add_argument('--override', action='store_true', help='Create in (contents)/ folder')
|
|
400
|
+
parser.add_argument('--with-id', action='store_true', help='Include [id] route')
|
|
401
|
+
parser.add_argument('--dry-run', action='store_true', help='Show what would be created')
|
|
402
|
+
|
|
403
|
+
args = parser.parse_args()
|
|
404
|
+
|
|
405
|
+
name = args.name.lower()
|
|
406
|
+
methods = [m.strip().upper() for m in args.methods.split(',')]
|
|
407
|
+
|
|
408
|
+
# Determine base path
|
|
409
|
+
if args.override:
|
|
410
|
+
base_path = Path(f'app/api/v1/(contents)/{name}')
|
|
411
|
+
else:
|
|
412
|
+
base_path = Path(f'app/api/v1/{name}')
|
|
413
|
+
|
|
414
|
+
print(f"\n{'=' * 60}")
|
|
415
|
+
print(f"SCAFFOLDING API ENDPOINT: {name}")
|
|
416
|
+
print(f"{'=' * 60}")
|
|
417
|
+
print(f"Methods: {', '.join(methods)}")
|
|
418
|
+
print(f"Auth: {args.auth}")
|
|
419
|
+
print(f"Override: {args.override}")
|
|
420
|
+
print(f"Path: {base_path}")
|
|
421
|
+
print(f"{'=' * 60}\n")
|
|
422
|
+
|
|
423
|
+
# Files to create
|
|
424
|
+
files = {
|
|
425
|
+
'route.ts': generate_list_route(name, methods, args.auth)
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
# Add [id] route if requested
|
|
429
|
+
if args.with_id:
|
|
430
|
+
id_methods = []
|
|
431
|
+
if 'GET' in methods:
|
|
432
|
+
id_methods.append('GET')
|
|
433
|
+
if 'POST' in methods or 'PUT' in methods or 'PATCH' in methods:
|
|
434
|
+
id_methods.append('PATCH')
|
|
435
|
+
if 'DELETE' in methods:
|
|
436
|
+
id_methods.append('DELETE')
|
|
437
|
+
|
|
438
|
+
files['[id]/route.ts'] = generate_id_route(name, id_methods, args.auth)
|
|
439
|
+
|
|
440
|
+
if args.dry_run:
|
|
441
|
+
print("DRY RUN - Files that would be created:\n")
|
|
442
|
+
for file_path, content in files.items():
|
|
443
|
+
print(f" {base_path / file_path}")
|
|
444
|
+
print(f" Lines: {len(content.splitlines())}")
|
|
445
|
+
print("\n" + "-" * 60)
|
|
446
|
+
print("GENERATED CODE PREVIEW (route.ts):")
|
|
447
|
+
print("-" * 60)
|
|
448
|
+
print(files['route.ts'])
|
|
449
|
+
print("-" * 60)
|
|
450
|
+
print("\nRun without --dry-run to create files.")
|
|
451
|
+
return 0
|
|
452
|
+
|
|
453
|
+
# Create files
|
|
454
|
+
for file_path, content in files.items():
|
|
455
|
+
full_path = base_path / file_path
|
|
456
|
+
full_path.parent.mkdir(parents=True, exist_ok=True)
|
|
457
|
+
|
|
458
|
+
if full_path.exists():
|
|
459
|
+
print(f" SKIP (exists): {full_path}")
|
|
460
|
+
else:
|
|
461
|
+
with open(full_path, 'w', encoding='utf-8') as f:
|
|
462
|
+
f.write(content)
|
|
463
|
+
print(f" CREATED: {full_path}")
|
|
464
|
+
|
|
465
|
+
print(f"\n{'=' * 60}")
|
|
466
|
+
print("NEXT STEPS:")
|
|
467
|
+
print("=" * 60)
|
|
468
|
+
print(f"1. Implement TODO sections in route files")
|
|
469
|
+
print(f"2. Add API scopes to core/lib/api/keys.ts:")
|
|
470
|
+
print(f" '{name}:read': 'Read {name}',")
|
|
471
|
+
print(f" '{name}:write': 'Create/update {name}',")
|
|
472
|
+
print(f" '{name}:delete': 'Delete {name}',")
|
|
473
|
+
print(f"3. Create Cypress tests for the endpoint")
|
|
474
|
+
print(f"4. Test with both API key and session auth")
|
|
475
|
+
print("=" * 60 + "\n")
|
|
476
|
+
|
|
477
|
+
return 0
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
if __name__ == '__main__':
|
|
481
|
+
sys.exit(main())
|