@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,335 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: database-migrations
|
|
3
|
+
description: |
|
|
4
|
+
PostgreSQL migration patterns with RLS, Better Auth integration, and TIMESTAMPTZ.
|
|
5
|
+
Covers main tables, meta tables, child entities, and sample data.
|
|
6
|
+
Use this skill when creating migrations, validating SQL, or generating sample data.
|
|
7
|
+
allowed-tools: Read, Glob, Grep, Bash(python:*)
|
|
8
|
+
version: 1.0.0
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
# Database Migrations Skill
|
|
12
|
+
|
|
13
|
+
Patterns and tools for PostgreSQL database migrations with RLS, Better Auth, and strict conventions.
|
|
14
|
+
|
|
15
|
+
## Architecture Overview
|
|
16
|
+
|
|
17
|
+
```
|
|
18
|
+
core/migrations/
|
|
19
|
+
├── 001_better_auth_and_functions.sql # Base auth (DO NOT MODIFY)
|
|
20
|
+
├── 002_auth_tables.sql # Auth tables (DO NOT MODIFY)
|
|
21
|
+
├── XXX_[entity]_table.sql # Main entity table
|
|
22
|
+
├── XXX_[entity]_metas.sql # Meta table (optional)
|
|
23
|
+
├── XXX_[entity]_sample_data.sql # Sample data (optional)
|
|
24
|
+
└── XXX_[parent]_[child]_table.sql # Child entities
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
> **📍 Context-Aware Paths:** Paths shown assume monorepo development. In consumer projects,
|
|
28
|
+
> create migrations in `contents/themes/{theme}/migrations/` instead (use sequence 1001+). Core is read-only.
|
|
29
|
+
> See `core-theme-responsibilities` skill for complete rules.
|
|
30
|
+
|
|
31
|
+
## When to Use This Skill
|
|
32
|
+
|
|
33
|
+
- Creating new entity migrations
|
|
34
|
+
- Adding meta tables for entities
|
|
35
|
+
- Creating child entity relationships
|
|
36
|
+
- Validating migration conventions
|
|
37
|
+
- Generating sample data
|
|
38
|
+
|
|
39
|
+
## Core Principles
|
|
40
|
+
|
|
41
|
+
### 1. Better Auth Integration
|
|
42
|
+
|
|
43
|
+
**Better Auth uses TEXT for IDs, not UUID type:**
|
|
44
|
+
|
|
45
|
+
```sql
|
|
46
|
+
-- ✅ CORRECT
|
|
47
|
+
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
48
|
+
"userId" TEXT NOT NULL REFERENCES public."users"(id) ON DELETE CASCADE,
|
|
49
|
+
|
|
50
|
+
-- ❌ WRONG
|
|
51
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Use existing functions (NEVER redefine):**
|
|
55
|
+
- `get_auth_user_id()` - Get current authenticated user ID
|
|
56
|
+
- `set_updated_at()` - Auto-update timestamp trigger
|
|
57
|
+
|
|
58
|
+
### 2. Field Ordering (MANDATORY)
|
|
59
|
+
|
|
60
|
+
Fields MUST follow this exact order:
|
|
61
|
+
|
|
62
|
+
```sql
|
|
63
|
+
CREATE TABLE IF NOT EXISTS public."EntityName" (
|
|
64
|
+
-- 1. Primary Key (always first)
|
|
65
|
+
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
66
|
+
|
|
67
|
+
-- 2. Relational Fields (foreign keys)
|
|
68
|
+
"userId" TEXT NOT NULL REFERENCES public."users"(id) ON DELETE CASCADE,
|
|
69
|
+
"teamId" TEXT REFERENCES public."teams"(id) ON DELETE CASCADE,
|
|
70
|
+
|
|
71
|
+
-- 3. Entity-specific Fields (business logic)
|
|
72
|
+
title TEXT NOT NULL,
|
|
73
|
+
description TEXT,
|
|
74
|
+
content JSONB,
|
|
75
|
+
priority INTEGER DEFAULT 0,
|
|
76
|
+
|
|
77
|
+
-- 4. System Fields (always last)
|
|
78
|
+
status TEXT DEFAULT 'draft',
|
|
79
|
+
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
80
|
+
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
81
|
+
);
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. TIMESTAMPTZ Requirement (CRITICAL)
|
|
85
|
+
|
|
86
|
+
**ALL timestamps MUST use `TIMESTAMPTZ`, never plain `TIMESTAMP`:**
|
|
87
|
+
|
|
88
|
+
```sql
|
|
89
|
+
-- ✅ CORRECT
|
|
90
|
+
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
91
|
+
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
92
|
+
"expiresAt" TIMESTAMPTZ,
|
|
93
|
+
|
|
94
|
+
-- ❌ FORBIDDEN
|
|
95
|
+
"createdAt" TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
### 4. CASCADE Rules
|
|
99
|
+
|
|
100
|
+
```sql
|
|
101
|
+
-- ✅ Main entity tables - ALWAYS use CASCADE
|
|
102
|
+
DROP TABLE IF EXISTS public."EntityName" CASCADE;
|
|
103
|
+
|
|
104
|
+
-- ✅ Meta tables - NO DROP (removed by parent CASCADE)
|
|
105
|
+
CREATE TABLE IF NOT EXISTS public."EntityName_metas" (...);
|
|
106
|
+
|
|
107
|
+
-- ✅ Foreign Keys - Use CASCADE for parent-child
|
|
108
|
+
REFERENCES public."Parent"(id) ON DELETE CASCADE
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
## RLS Patterns (4 Cases)
|
|
112
|
+
|
|
113
|
+
### Case 1: Private to Owner
|
|
114
|
+
```sql
|
|
115
|
+
CREATE POLICY "Entity owner can do all"
|
|
116
|
+
ON public."Entity"
|
|
117
|
+
FOR ALL TO authenticated
|
|
118
|
+
USING ("userId" = public.get_auth_user_id())
|
|
119
|
+
WITH CHECK ("userId" = public.get_auth_user_id());
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Case 2: Team-Based Access
|
|
123
|
+
```sql
|
|
124
|
+
CREATE POLICY "Entity team can do all"
|
|
125
|
+
ON public."Entity"
|
|
126
|
+
FOR ALL TO authenticated
|
|
127
|
+
USING (
|
|
128
|
+
"teamId" IN (
|
|
129
|
+
SELECT "teamId" FROM public."members"
|
|
130
|
+
WHERE "userId" = public.get_auth_user_id()
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
WITH CHECK (...);
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Case 3: Shared Among Authenticated
|
|
137
|
+
```sql
|
|
138
|
+
CREATE POLICY "Entity any auth can do all"
|
|
139
|
+
ON public."Entity"
|
|
140
|
+
FOR ALL TO authenticated
|
|
141
|
+
USING (true)
|
|
142
|
+
WITH CHECK (true);
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### Case 4: Public Read with Auth Write
|
|
146
|
+
```sql
|
|
147
|
+
-- Anonymous can read published
|
|
148
|
+
CREATE POLICY "Entity public can select"
|
|
149
|
+
ON public."Entity"
|
|
150
|
+
FOR SELECT TO anon
|
|
151
|
+
USING (published = TRUE);
|
|
152
|
+
|
|
153
|
+
-- Authenticated can manage all
|
|
154
|
+
CREATE POLICY "Entity auth can do all"
|
|
155
|
+
ON public."Entity"
|
|
156
|
+
FOR ALL TO authenticated
|
|
157
|
+
USING (true)
|
|
158
|
+
WITH CHECK (true);
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Meta Tables Pattern
|
|
162
|
+
|
|
163
|
+
**CRITICAL: Always use `"entityId"` for foreign key (never `"postId"`, `"userId"`, etc.)**
|
|
164
|
+
|
|
165
|
+
```sql
|
|
166
|
+
CREATE TABLE IF NOT EXISTS public."EntityName_metas" (
|
|
167
|
+
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
168
|
+
"entityId" TEXT NOT NULL REFERENCES public."EntityName"(id) ON DELETE CASCADE,
|
|
169
|
+
"metaKey" TEXT NOT NULL,
|
|
170
|
+
"metaValue" JSONB NOT NULL DEFAULT '{}'::jsonb,
|
|
171
|
+
"dataType" TEXT,
|
|
172
|
+
"isPublic" BOOLEAN NOT NULL DEFAULT FALSE,
|
|
173
|
+
"isSearchable" BOOLEAN NOT NULL DEFAULT FALSE,
|
|
174
|
+
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
175
|
+
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
176
|
+
CONSTRAINT entity_metas_unique_key UNIQUE ("entityId", "metaKey")
|
|
177
|
+
);
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Child Entities Pattern
|
|
181
|
+
|
|
182
|
+
**CRITICAL: Always use `"parentId"` for foreign key (never `"clientId"`, `"orderId"`, etc.)**
|
|
183
|
+
|
|
184
|
+
```sql
|
|
185
|
+
CREATE TABLE IF NOT EXISTS public."parent_children" (
|
|
186
|
+
id TEXT PRIMARY KEY DEFAULT gen_random_uuid()::text,
|
|
187
|
+
"parentId" TEXT NOT NULL REFERENCES public."parent"(id) ON DELETE CASCADE,
|
|
188
|
+
|
|
189
|
+
-- Child-specific fields (NO userId - inherited via parent)
|
|
190
|
+
name TEXT NOT NULL,
|
|
191
|
+
description TEXT,
|
|
192
|
+
|
|
193
|
+
-- System fields
|
|
194
|
+
"createdAt" TIMESTAMPTZ NOT NULL DEFAULT now(),
|
|
195
|
+
"updatedAt" TIMESTAMPTZ NOT NULL DEFAULT now()
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
-- RLS inherits parent access
|
|
199
|
+
CREATE POLICY "Child inherit parent access"
|
|
200
|
+
ON public."parent_children"
|
|
201
|
+
FOR ALL TO authenticated
|
|
202
|
+
USING (
|
|
203
|
+
EXISTS (
|
|
204
|
+
SELECT 1 FROM public."parent" p
|
|
205
|
+
WHERE p.id = "parentId"
|
|
206
|
+
AND p."userId" = public.get_auth_user_id()
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
WITH CHECK (...);
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## Scripts
|
|
213
|
+
|
|
214
|
+
### Validate Migration Conventions
|
|
215
|
+
```bash
|
|
216
|
+
# Validate a specific migration file
|
|
217
|
+
python3 .claude/skills/database-migrations/scripts/validate-migration.py \
|
|
218
|
+
--file core/migrations/017_scheduled_actions_table.sql
|
|
219
|
+
|
|
220
|
+
# Validate all migrations
|
|
221
|
+
python3 .claude/skills/database-migrations/scripts/validate-migration.py \
|
|
222
|
+
--path core/migrations/
|
|
223
|
+
|
|
224
|
+
# Strict mode (exit with error if issues found)
|
|
225
|
+
python3 .claude/skills/database-migrations/scripts/validate-migration.py \
|
|
226
|
+
--path core/migrations/ \
|
|
227
|
+
--strict
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### Generate Sample Data
|
|
231
|
+
```bash
|
|
232
|
+
# Generate sample data for an entity
|
|
233
|
+
python3 .claude/skills/database-migrations/scripts/generate-sample-data.py \
|
|
234
|
+
--entity posts \
|
|
235
|
+
--count 20
|
|
236
|
+
|
|
237
|
+
# With custom team and user IDs
|
|
238
|
+
python3 .claude/skills/database-migrations/scripts/generate-sample-data.py \
|
|
239
|
+
--entity tasks \
|
|
240
|
+
--count 10 \
|
|
241
|
+
--user-id "user-sample-1" \
|
|
242
|
+
--team-id "team-tmt-001"
|
|
243
|
+
|
|
244
|
+
# Preview without writing
|
|
245
|
+
python3 .claude/skills/database-migrations/scripts/generate-sample-data.py \
|
|
246
|
+
--entity products \
|
|
247
|
+
--dry-run
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
## Required Indexes
|
|
251
|
+
|
|
252
|
+
```sql
|
|
253
|
+
-- ============================================
|
|
254
|
+
-- INDEXES
|
|
255
|
+
-- ============================================
|
|
256
|
+
-- Primary relationships
|
|
257
|
+
CREATE INDEX IF NOT EXISTS idx_entity_user_id ON public."entity"("userId");
|
|
258
|
+
CREATE INDEX IF NOT EXISTS idx_entity_team_id ON public."entity"("teamId");
|
|
259
|
+
|
|
260
|
+
-- Common query patterns
|
|
261
|
+
CREATE INDEX IF NOT EXISTS idx_entity_status ON public."entity"(status);
|
|
262
|
+
CREATE INDEX IF NOT EXISTS idx_entity_created_at ON public."entity"("createdAt" DESC);
|
|
263
|
+
|
|
264
|
+
-- Conditional indexes
|
|
265
|
+
CREATE INDEX IF NOT EXISTS idx_entity_published ON public."entity"(published) WHERE published = TRUE;
|
|
266
|
+
|
|
267
|
+
-- JSONB indexes
|
|
268
|
+
CREATE INDEX IF NOT EXISTS idx_entity_payload_gin ON public."entity" USING GIN (payload);
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
## Trigger Pattern
|
|
272
|
+
|
|
273
|
+
**Always use existing Better Auth function:**
|
|
274
|
+
|
|
275
|
+
```sql
|
|
276
|
+
-- ============================================
|
|
277
|
+
-- TRIGGER updatedAt (uses Better Auth function)
|
|
278
|
+
-- ============================================
|
|
279
|
+
DROP TRIGGER IF EXISTS entity_set_updated_at ON public."entity";
|
|
280
|
+
CREATE TRIGGER entity_set_updated_at
|
|
281
|
+
BEFORE UPDATE ON public."entity"
|
|
282
|
+
FOR EACH ROW EXECUTE FUNCTION public.set_updated_at();
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
## Naming Conventions
|
|
286
|
+
|
|
287
|
+
| Convention | Example |
|
|
288
|
+
|------------|---------|
|
|
289
|
+
| Table names | camelCase: `"scheduledActions"` |
|
|
290
|
+
| Column names | camelCase: `"createdAt"`, `"userId"` |
|
|
291
|
+
| Index names | snake_case: `idx_entity_user_id` |
|
|
292
|
+
| Policy names | "Entity action description" |
|
|
293
|
+
| Constraint names | snake_case: `entity_metas_unique_key` |
|
|
294
|
+
|
|
295
|
+
## Anti-Patterns
|
|
296
|
+
|
|
297
|
+
```sql
|
|
298
|
+
-- ❌ NEVER: Plain TIMESTAMP
|
|
299
|
+
"createdAt" TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
|
300
|
+
|
|
301
|
+
-- ❌ NEVER: UUID type (Better Auth uses TEXT)
|
|
302
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid()
|
|
303
|
+
|
|
304
|
+
-- ❌ NEVER: Redefine auth functions
|
|
305
|
+
CREATE OR REPLACE FUNCTION public.get_auth_user_id()...
|
|
306
|
+
|
|
307
|
+
-- ❌ NEVER: Wrong meta FK naming
|
|
308
|
+
"postId" TEXT NOT NULL REFERENCES public."posts"(id)
|
|
309
|
+
-- ✅ CORRECT: "entityId"
|
|
310
|
+
|
|
311
|
+
-- ❌ NEVER: Wrong child FK naming
|
|
312
|
+
"clientId" TEXT NOT NULL REFERENCES public."clients"(id)
|
|
313
|
+
-- ✅ CORRECT: "parentId"
|
|
314
|
+
|
|
315
|
+
-- ❌ NEVER: Business logic in DB triggers
|
|
316
|
+
CREATE FUNCTION calculate_order_totals()...
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
## Checklist
|
|
320
|
+
|
|
321
|
+
Before finalizing a migration:
|
|
322
|
+
|
|
323
|
+
- [ ] Uses TEXT for all ID fields (not UUID type)
|
|
324
|
+
- [ ] References `public."users"` for user relationships
|
|
325
|
+
- [ ] Uses `get_auth_user_id()` in RLS (not redefined)
|
|
326
|
+
- [ ] Uses `set_updated_at()` in triggers (not redefined)
|
|
327
|
+
- [ ] ALL timestamps use `TIMESTAMPTZ` (not `TIMESTAMP`)
|
|
328
|
+
- [ ] Uses `now()` (not `CURRENT_TIMESTAMP`)
|
|
329
|
+
- [ ] Fields follow strict ordering (id → FK → business → system)
|
|
330
|
+
- [ ] Main tables use `DROP ... CASCADE`
|
|
331
|
+
- [ ] Meta tables use `"entityId"` (not entity-specific name)
|
|
332
|
+
- [ ] Child tables use `"parentId"` (not parent-specific name)
|
|
333
|
+
- [ ] Appropriate RLS policies for access pattern
|
|
334
|
+
- [ ] Required indexes created
|
|
335
|
+
- [ ] Sample data uses `ON CONFLICT` clause
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Generate Sample Data Script
|
|
4
|
+
|
|
5
|
+
Generates SQL INSERT statements for sample/test data.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
python generate-sample-data.py --entity ENTITY [--count COUNT]
|
|
9
|
+
|
|
10
|
+
Options:
|
|
11
|
+
--entity ENTITY Entity name (e.g., posts, tasks, products)
|
|
12
|
+
--count COUNT Number of records to generate (default: 20)
|
|
13
|
+
--user-id ID User ID for ownership (default: user-sample-1)
|
|
14
|
+
--team-id ID Team ID for team context (default: team-tmt-001)
|
|
15
|
+
--dry-run Preview without writing to file
|
|
16
|
+
--output FILE Output file path (default: core/migrations/XXX_entity_sample_data.sql)
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
import os
|
|
20
|
+
import sys
|
|
21
|
+
import re
|
|
22
|
+
import random
|
|
23
|
+
import argparse
|
|
24
|
+
from datetime import datetime, timedelta
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
from typing import List, Dict
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def to_pascal_case(name: str) -> str:
|
|
30
|
+
"""Convert kebab-case/snake_case to PascalCase."""
|
|
31
|
+
return ''.join(x.title() for x in re.split(r'[-_]', name))
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def to_camel_case(name: str) -> str:
|
|
35
|
+
"""Convert kebab-case/snake_case to camelCase."""
|
|
36
|
+
parts = re.split(r'[-_]', name)
|
|
37
|
+
return parts[0].lower() + ''.join(x.title() for x in parts[1:])
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def get_next_migration_number(migrations_path: Path) -> str:
|
|
41
|
+
"""Get the next available migration number."""
|
|
42
|
+
if not migrations_path.exists():
|
|
43
|
+
return "020"
|
|
44
|
+
|
|
45
|
+
existing = list(migrations_path.glob("*.sql"))
|
|
46
|
+
if not existing:
|
|
47
|
+
return "020"
|
|
48
|
+
|
|
49
|
+
numbers = []
|
|
50
|
+
for f in existing:
|
|
51
|
+
match = re.match(r'^(\d+)_', f.name)
|
|
52
|
+
if match:
|
|
53
|
+
numbers.append(int(match.group(1)))
|
|
54
|
+
|
|
55
|
+
if numbers:
|
|
56
|
+
return str(max(numbers) + 1).zfill(3)
|
|
57
|
+
return "020"
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def generate_sample_titles(entity: str, count: int) -> List[str]:
|
|
61
|
+
"""Generate sample titles based on entity type."""
|
|
62
|
+
templates = {
|
|
63
|
+
'posts': [
|
|
64
|
+
"Introduction to {topic}",
|
|
65
|
+
"Understanding {topic}",
|
|
66
|
+
"A Guide to {topic}",
|
|
67
|
+
"Best Practices for {topic}",
|
|
68
|
+
"Deep Dive into {topic}",
|
|
69
|
+
"Mastering {topic}",
|
|
70
|
+
"The Complete {topic} Guide",
|
|
71
|
+
"Getting Started with {topic}",
|
|
72
|
+
"{topic} Explained",
|
|
73
|
+
"Why {topic} Matters",
|
|
74
|
+
],
|
|
75
|
+
'tasks': [
|
|
76
|
+
"Review {topic} implementation",
|
|
77
|
+
"Fix {topic} issue",
|
|
78
|
+
"Update {topic} documentation",
|
|
79
|
+
"Test {topic} feature",
|
|
80
|
+
"Refactor {topic} code",
|
|
81
|
+
"Deploy {topic} to production",
|
|
82
|
+
"Configure {topic} settings",
|
|
83
|
+
"Analyze {topic} metrics",
|
|
84
|
+
"Optimize {topic} performance",
|
|
85
|
+
"Design {topic} system",
|
|
86
|
+
],
|
|
87
|
+
'products': [
|
|
88
|
+
"Premium {topic} Package",
|
|
89
|
+
"Professional {topic} Suite",
|
|
90
|
+
"Enterprise {topic} Solution",
|
|
91
|
+
"Basic {topic} Plan",
|
|
92
|
+
"Advanced {topic} Tools",
|
|
93
|
+
"Essential {topic} Kit",
|
|
94
|
+
"Ultimate {topic} Bundle",
|
|
95
|
+
"Starter {topic} Pack",
|
|
96
|
+
"{topic} Pro Edition",
|
|
97
|
+
"{topic} Deluxe Version",
|
|
98
|
+
],
|
|
99
|
+
'default': [
|
|
100
|
+
"Sample {topic} Item",
|
|
101
|
+
"Test {topic} Record",
|
|
102
|
+
"{topic} Example",
|
|
103
|
+
"Demo {topic} Entry",
|
|
104
|
+
"New {topic} Data",
|
|
105
|
+
]
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
topics = [
|
|
109
|
+
"React", "TypeScript", "Next.js", "PostgreSQL", "Authentication",
|
|
110
|
+
"API Design", "Testing", "CI/CD", "Docker", "Kubernetes",
|
|
111
|
+
"Performance", "Security", "Caching", "Database", "Frontend",
|
|
112
|
+
"Backend", "DevOps", "Monitoring", "Logging", "Analytics"
|
|
113
|
+
]
|
|
114
|
+
|
|
115
|
+
template_list = templates.get(entity, templates['default'])
|
|
116
|
+
titles = []
|
|
117
|
+
|
|
118
|
+
for i in range(count):
|
|
119
|
+
template = template_list[i % len(template_list)]
|
|
120
|
+
topic = topics[i % len(topics)]
|
|
121
|
+
titles.append(template.format(topic=topic))
|
|
122
|
+
|
|
123
|
+
return titles
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def generate_slugs(titles: List[str]) -> List[str]:
|
|
127
|
+
"""Generate URL slugs from titles."""
|
|
128
|
+
slugs = []
|
|
129
|
+
for title in titles:
|
|
130
|
+
slug = title.lower()
|
|
131
|
+
slug = re.sub(r'[^a-z0-9\s-]', '', slug)
|
|
132
|
+
slug = re.sub(r'\s+', '-', slug)
|
|
133
|
+
slug = re.sub(r'-+', '-', slug)
|
|
134
|
+
slugs.append(slug.strip('-'))
|
|
135
|
+
return slugs
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
def generate_sample_data(
|
|
139
|
+
entity: str,
|
|
140
|
+
count: int,
|
|
141
|
+
user_id: str,
|
|
142
|
+
team_id: str
|
|
143
|
+
) -> str:
|
|
144
|
+
"""Generate SQL INSERT statements for sample data."""
|
|
145
|
+
|
|
146
|
+
table_name = to_camel_case(entity)
|
|
147
|
+
titles = generate_sample_titles(entity, count)
|
|
148
|
+
slugs = generate_slugs(titles)
|
|
149
|
+
|
|
150
|
+
# Status options
|
|
151
|
+
statuses = ['draft', 'active', 'published', 'archived']
|
|
152
|
+
|
|
153
|
+
# Build SQL
|
|
154
|
+
lines = [
|
|
155
|
+
f"-- Migration: XXX_{entity}_sample_data.sql",
|
|
156
|
+
f"-- Description: Sample data for {entity}",
|
|
157
|
+
f"-- Date: {datetime.now().strftime('%Y-%m-%d')}",
|
|
158
|
+
"",
|
|
159
|
+
"-- ============================================",
|
|
160
|
+
f"-- SAMPLE {entity.upper()}",
|
|
161
|
+
"-- ============================================",
|
|
162
|
+
f'INSERT INTO public."{table_name}" (',
|
|
163
|
+
" id,",
|
|
164
|
+
' "userId",',
|
|
165
|
+
]
|
|
166
|
+
|
|
167
|
+
# Add teamId if provided
|
|
168
|
+
if team_id:
|
|
169
|
+
lines.append(' "teamId",')
|
|
170
|
+
|
|
171
|
+
lines.extend([
|
|
172
|
+
" title,",
|
|
173
|
+
" slug,",
|
|
174
|
+
" description,",
|
|
175
|
+
" status,",
|
|
176
|
+
' "createdAt",',
|
|
177
|
+
' "updatedAt"',
|
|
178
|
+
") VALUES",
|
|
179
|
+
])
|
|
180
|
+
|
|
181
|
+
# Generate records
|
|
182
|
+
values = []
|
|
183
|
+
base_date = datetime.now() - timedelta(days=30)
|
|
184
|
+
|
|
185
|
+
for i in range(count):
|
|
186
|
+
record_id = f'{entity[:4]}-sample-{i + 1}'
|
|
187
|
+
created_at = base_date + timedelta(days=i, hours=random.randint(0, 23))
|
|
188
|
+
updated_at = created_at + timedelta(days=random.randint(0, 5))
|
|
189
|
+
status = statuses[i % len(statuses)]
|
|
190
|
+
description = f"Sample description for {entity} #{i + 1}. This is test data for development."
|
|
191
|
+
|
|
192
|
+
value_parts = [
|
|
193
|
+
f" '{record_id}'",
|
|
194
|
+
f" '{user_id}'",
|
|
195
|
+
]
|
|
196
|
+
|
|
197
|
+
if team_id:
|
|
198
|
+
value_parts.append(f" '{team_id}'")
|
|
199
|
+
|
|
200
|
+
value_parts.extend([
|
|
201
|
+
f" '{titles[i]}'",
|
|
202
|
+
f" '{slugs[i]}'",
|
|
203
|
+
f" '{description}'",
|
|
204
|
+
f" '{status}'",
|
|
205
|
+
f" '{created_at.strftime('%Y-%m-%d %H:%M:%S')}'::timestamptz",
|
|
206
|
+
f" '{updated_at.strftime('%Y-%m-%d %H:%M:%S')}'::timestamptz",
|
|
207
|
+
])
|
|
208
|
+
|
|
209
|
+
values.append(" (\n" + ",\n".join(value_parts) + "\n )")
|
|
210
|
+
|
|
211
|
+
lines.append(",\n".join(values))
|
|
212
|
+
lines.append("ON CONFLICT (id) DO NOTHING;")
|
|
213
|
+
|
|
214
|
+
return "\n".join(lines)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def main():
|
|
218
|
+
parser = argparse.ArgumentParser(description='Generate sample data')
|
|
219
|
+
parser.add_argument('--entity', required=True, help='Entity name')
|
|
220
|
+
parser.add_argument('--count', type=int, default=20, help='Number of records')
|
|
221
|
+
parser.add_argument('--user-id', default='user-sample-1', help='User ID')
|
|
222
|
+
parser.add_argument('--team-id', default='team-tmt-001', help='Team ID')
|
|
223
|
+
parser.add_argument('--dry-run', action='store_true', help='Preview without writing')
|
|
224
|
+
parser.add_argument('--output', help='Output file path')
|
|
225
|
+
|
|
226
|
+
args = parser.parse_args()
|
|
227
|
+
|
|
228
|
+
entity = args.entity.lower()
|
|
229
|
+
|
|
230
|
+
print(f"\n{'=' * 60}")
|
|
231
|
+
print("GENERATING SAMPLE DATA")
|
|
232
|
+
print(f"{'=' * 60}")
|
|
233
|
+
print(f"Entity: {entity}")
|
|
234
|
+
print(f"Count: {args.count}")
|
|
235
|
+
print(f"User ID: {args.user_id}")
|
|
236
|
+
print(f"Team ID: {args.team_id}")
|
|
237
|
+
print(f"{'=' * 60}\n")
|
|
238
|
+
|
|
239
|
+
# Generate SQL
|
|
240
|
+
sql_content = generate_sample_data(
|
|
241
|
+
entity,
|
|
242
|
+
args.count,
|
|
243
|
+
args.user_id,
|
|
244
|
+
args.team_id
|
|
245
|
+
)
|
|
246
|
+
|
|
247
|
+
if args.dry_run:
|
|
248
|
+
print("DRY RUN - Generated SQL:\n")
|
|
249
|
+
print("-" * 60)
|
|
250
|
+
print(sql_content)
|
|
251
|
+
print("-" * 60)
|
|
252
|
+
print("\nRun without --dry-run to write to file.")
|
|
253
|
+
return 0
|
|
254
|
+
|
|
255
|
+
# Determine output path
|
|
256
|
+
if args.output:
|
|
257
|
+
output_path = Path(args.output)
|
|
258
|
+
else:
|
|
259
|
+
migrations_path = Path('core/migrations')
|
|
260
|
+
next_num = get_next_migration_number(migrations_path)
|
|
261
|
+
output_path = migrations_path / f"{next_num}_{entity}_sample_data.sql"
|
|
262
|
+
|
|
263
|
+
# Write file
|
|
264
|
+
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
265
|
+
|
|
266
|
+
with open(output_path, 'w', encoding='utf-8') as f:
|
|
267
|
+
f.write(sql_content)
|
|
268
|
+
f.write('\n')
|
|
269
|
+
|
|
270
|
+
print(f"Sample data written to: {output_path}")
|
|
271
|
+
print(f"Records generated: {args.count}")
|
|
272
|
+
print(f"\n{'=' * 60}")
|
|
273
|
+
print("NEXT STEPS:")
|
|
274
|
+
print("=" * 60)
|
|
275
|
+
print(f"1. Review generated file: {output_path}")
|
|
276
|
+
print("2. Adjust fields as needed for your entity schema")
|
|
277
|
+
print("3. Run migration: pnpm db:migrate")
|
|
278
|
+
print("=" * 60 + "\n")
|
|
279
|
+
|
|
280
|
+
return 0
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
if __name__ == '__main__':
|
|
284
|
+
sys.exit(main())
|