@girardmedia/bootspring 1.2.0 → 2.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +107 -14
- package/bin/bootspring.js +166 -27
- package/cli/agent.js +189 -17
- package/cli/analyze.js +499 -0
- package/cli/audit.js +557 -0
- package/cli/auth.js +495 -38
- package/cli/billing.js +302 -0
- package/cli/build.js +695 -0
- package/cli/business.js +109 -26
- package/cli/checkpoint-utils.js +168 -0
- package/cli/checkpoint.js +639 -0
- package/cli/cloud-sync.js +447 -0
- package/cli/content.js +198 -0
- package/cli/context.js +1 -1
- package/cli/deploy.js +543 -0
- package/cli/fundraise.js +112 -50
- package/cli/github-cmd.js +435 -0
- package/cli/health.js +477 -0
- package/cli/init.js +84 -13
- package/cli/legal.js +107 -95
- package/cli/log.js +2 -2
- package/cli/loop.js +976 -73
- package/cli/manager.js +711 -0
- package/cli/metrics.js +480 -0
- package/cli/monitor.js +812 -0
- package/cli/onboard.js +521 -0
- package/cli/orchestrator.js +12 -24
- package/cli/prd.js +594 -0
- package/cli/preseed-start.js +1483 -0
- package/cli/preseed.js +2302 -0
- package/cli/project.js +436 -0
- package/cli/quality.js +233 -0
- package/cli/security.js +913 -0
- package/cli/seed.js +1441 -5
- package/cli/skill.js +273 -211
- package/cli/suggest.js +989 -0
- package/cli/switch.js +453 -0
- package/cli/visualize.js +527 -0
- package/cli/watch.js +769 -0
- package/cli/workspace.js +607 -0
- package/core/analyze-workflow.js +1134 -0
- package/core/api-client.js +535 -22
- package/core/audit-workflow.js +1350 -0
- package/core/build-orchestrator.js +480 -0
- package/core/build-state.js +577 -0
- package/core/checkpoint-engine.js +408 -0
- package/core/config.js +1109 -26
- package/core/context-loader.js +21 -1
- package/core/deploy-workflow.js +836 -0
- package/core/entitlements.js +93 -22
- package/core/github-sync.js +610 -0
- package/core/index.js +8 -1
- package/core/ingest.js +1111 -0
- package/core/metrics-engine.js +768 -0
- package/core/onboard-workflow.js +1007 -0
- package/core/preseed-workflow.js +934 -0
- package/core/preseed.js +1617 -0
- package/core/project-context.js +325 -0
- package/core/project-state.js +694 -0
- package/core/r2-sync.js +583 -0
- package/core/scaffold.js +525 -7
- package/core/session.js +258 -0
- package/core/task-extractor.js +758 -0
- package/core/telemetry.js +28 -6
- package/core/tier-enforcement.js +737 -0
- package/core/utils.js +38 -14
- package/generators/questionnaire.js +15 -12
- package/generators/sections/ai.js +7 -7
- package/generators/sections/content.js +300 -0
- package/generators/sections/index.js +3 -0
- package/generators/sections/plugins.js +7 -6
- package/generators/templates/build-planning.template.js +596 -0
- package/generators/templates/content.template.js +819 -0
- package/generators/templates/index.js +2 -1
- package/hooks/git-autopilot.js +1250 -0
- package/hooks/index.js +9 -0
- package/intelligence/agent-collab.js +2057 -0
- package/intelligence/auto-suggest.js +634 -0
- package/intelligence/content-gen.js +1589 -0
- package/intelligence/cross-project.js +1647 -0
- package/intelligence/index.js +184 -0
- package/intelligence/learning/insights.json +517 -7
- package/intelligence/learning/pattern-learner.js +1008 -14
- package/intelligence/memory/decision-tracker.js +1431 -31
- package/intelligence/memory/decisions.jsonl +0 -0
- package/intelligence/orchestrator.js +2896 -1
- package/intelligence/prd.js +92 -1
- package/intelligence/recommendation-weights.json +14 -2
- package/intelligence/recommendations.js +463 -9
- package/intelligence/workflow-composer.js +1451 -0
- package/marketplace/index.d.ts +324 -0
- package/marketplace/index.js +1921 -0
- package/mcp/contracts/mcp-contract.v1.json +342 -4
- package/mcp/registry.js +680 -3
- package/mcp/response-formatter.js +23 -0
- package/mcp/tools/assist-tool.js +78 -4
- package/mcp/tools/autopilot-tool.js +408 -0
- package/mcp/tools/content-tool.js +571 -0
- package/mcp/tools/dashboard-tool.js +251 -5
- package/mcp/tools/mvp-tool.js +344 -0
- package/mcp/tools/plugin-tool.js +23 -1
- package/mcp/tools/prd-tool.js +579 -0
- package/mcp/tools/seed-tool.js +447 -0
- package/mcp/tools/skill-tool.js +43 -14
- package/mcp/tools/suggest-tool.js +147 -0
- package/package.json +15 -6
- package/agents/README.md +0 -93
- package/agents/ai-integration-expert/context.md +0 -386
- package/agents/api-expert/context.md +0 -416
- package/agents/architecture-expert/context.md +0 -454
- package/agents/auth-expert/context.md +0 -399
- package/agents/backend-expert/context.md +0 -483
- package/agents/business-strategy-expert/context.md +0 -180
- package/agents/code-review-expert/context.md +0 -365
- package/agents/competitive-analysis-expert/context.md +0 -239
- package/agents/data-modeling-expert/context.md +0 -352
- package/agents/database-expert/context.md +0 -250
- package/agents/devops-expert/context.md +0 -446
- package/agents/email-expert/context.md +0 -379
- package/agents/financial-expert/context.md +0 -213
- package/agents/frontend-expert/context.md +0 -364
- package/agents/fundraising-expert/context.md +0 -257
- package/agents/growth-expert/context.md +0 -249
- package/agents/index.js +0 -140
- package/agents/investor-relations-expert/context.md +0 -266
- package/agents/legal-expert/context.md +0 -284
- package/agents/marketing-expert/context.md +0 -236
- package/agents/monitoring-expert/context.md +0 -362
- package/agents/operations-expert/context.md +0 -279
- package/agents/partnerships-expert/context.md +0 -286
- package/agents/payment-expert/context.md +0 -340
- package/agents/performance-expert/context.md +0 -377
- package/agents/private-equity-expert/context.md +0 -246
- package/agents/railway-expert/context.md +0 -284
- package/agents/research-expert/context.md +0 -245
- package/agents/sales-expert/context.md +0 -241
- package/agents/security-expert/context.md +0 -343
- package/agents/testing-expert/context.md +0 -414
- package/agents/ui-ux-expert/context.md +0 -448
- package/agents/vercel-expert/context.md +0 -426
- package/skills/index.js +0 -787
- package/skills/patterns/README.md +0 -163
- package/skills/patterns/ai/agents.md +0 -281
- package/skills/patterns/ai/claude.md +0 -138
- package/skills/patterns/ai/embeddings.md +0 -150
- package/skills/patterns/ai/rag.md +0 -266
- package/skills/patterns/ai/streaming.md +0 -170
- package/skills/patterns/ai/structured-output.md +0 -162
- package/skills/patterns/ai/tools.md +0 -154
- package/skills/patterns/analytics/tracking.md +0 -220
- package/skills/patterns/api/errors.md +0 -296
- package/skills/patterns/api/graphql.md +0 -440
- package/skills/patterns/api/middleware.md +0 -279
- package/skills/patterns/api/openapi.md +0 -285
- package/skills/patterns/api/rate-limiting.md +0 -231
- package/skills/patterns/api/route-handler.md +0 -217
- package/skills/patterns/api/server-action.md +0 -249
- package/skills/patterns/api/versioning.md +0 -443
- package/skills/patterns/api/webhooks.md +0 -247
- package/skills/patterns/auth/clerk.md +0 -132
- package/skills/patterns/auth/mfa.md +0 -313
- package/skills/patterns/auth/nextauth.md +0 -140
- package/skills/patterns/auth/oauth.md +0 -237
- package/skills/patterns/auth/rbac.md +0 -152
- package/skills/patterns/auth/session-management.md +0 -367
- package/skills/patterns/auth/session.md +0 -120
- package/skills/patterns/database/audit.md +0 -177
- package/skills/patterns/database/migrations.md +0 -177
- package/skills/patterns/database/pagination.md +0 -230
- package/skills/patterns/database/pooling.md +0 -357
- package/skills/patterns/database/prisma.md +0 -180
- package/skills/patterns/database/relations.md +0 -187
- package/skills/patterns/database/seeding.md +0 -246
- package/skills/patterns/database/soft-delete.md +0 -153
- package/skills/patterns/database/transactions.md +0 -162
- package/skills/patterns/deployment/ci-cd.md +0 -231
- package/skills/patterns/deployment/docker.md +0 -188
- package/skills/patterns/deployment/monitoring.md +0 -387
- package/skills/patterns/deployment/vercel.md +0 -160
- package/skills/patterns/email/resend.md +0 -143
- package/skills/patterns/email/templates.md +0 -245
- package/skills/patterns/email/transactional.md +0 -503
- package/skills/patterns/email/verification.md +0 -176
- package/skills/patterns/files/download.md +0 -243
- package/skills/patterns/files/upload.md +0 -239
- package/skills/patterns/i18n/nextintl.md +0 -188
- package/skills/patterns/logging/structured.md +0 -292
- package/skills/patterns/notifications/email-queue.md +0 -248
- package/skills/patterns/notifications/push.md +0 -279
- package/skills/patterns/payments/checkout.md +0 -303
- package/skills/patterns/payments/invoices.md +0 -287
- package/skills/patterns/payments/portal.md +0 -245
- package/skills/patterns/payments/stripe.md +0 -272
- package/skills/patterns/payments/subscriptions.md +0 -300
- package/skills/patterns/payments/usage.md +0 -279
- package/skills/patterns/performance/caching.md +0 -276
- package/skills/patterns/performance/code-splitting.md +0 -233
- package/skills/patterns/performance/edge.md +0 -254
- package/skills/patterns/performance/isr.md +0 -266
- package/skills/patterns/performance/lazy-loading.md +0 -281
- package/skills/patterns/realtime/sse.md +0 -327
- package/skills/patterns/realtime/websockets.md +0 -336
- package/skills/patterns/search/filtering.md +0 -329
- package/skills/patterns/search/fulltext.md +0 -260
- package/skills/patterns/security/audit-logging.md +0 -444
- package/skills/patterns/security/csrf.md +0 -234
- package/skills/patterns/security/headers.md +0 -252
- package/skills/patterns/security/sanitization.md +0 -258
- package/skills/patterns/security/secrets.md +0 -261
- package/skills/patterns/security/validation.md +0 -268
- package/skills/patterns/security/xss.md +0 -229
- package/skills/patterns/seo/metadata.md +0 -252
- package/skills/patterns/state/context.md +0 -349
- package/skills/patterns/state/react-query.md +0 -313
- package/skills/patterns/state/url-state.md +0 -482
- package/skills/patterns/state/zustand.md +0 -262
- package/skills/patterns/testing/api.md +0 -259
- package/skills/patterns/testing/component.md +0 -233
- package/skills/patterns/testing/coverage.md +0 -207
- package/skills/patterns/testing/fixtures.md +0 -225
- package/skills/patterns/testing/integration.md +0 -436
- package/skills/patterns/testing/mocking.md +0 -177
- package/skills/patterns/testing/playwright.md +0 -162
- package/skills/patterns/testing/snapshot.md +0 -175
- package/skills/patterns/testing/vitest.md +0 -307
- package/skills/patterns/ui/accordions.md +0 -395
- package/skills/patterns/ui/cards.md +0 -299
- package/skills/patterns/ui/dropdowns.md +0 -476
- package/skills/patterns/ui/empty-states.md +0 -320
- package/skills/patterns/ui/forms.md +0 -405
- package/skills/patterns/ui/inputs.md +0 -319
- package/skills/patterns/ui/layouts.md +0 -282
- package/skills/patterns/ui/loading.md +0 -291
- package/skills/patterns/ui/modals.md +0 -338
- package/skills/patterns/ui/navigation.md +0 -374
- package/skills/patterns/ui/tables.md +0 -407
- package/skills/patterns/ui/toasts.md +0 -300
- package/skills/patterns/ui/tooltips.md +0 -396
- package/skills/patterns/utils/dates.md +0 -435
- package/skills/patterns/utils/errors.md +0 -451
- package/skills/patterns/utils/formatting.md +0 -345
- package/skills/patterns/utils/validation.md +0 -434
- package/templates/bootspring.config.js +0 -83
- package/templates/business/business-model-canvas.md +0 -246
- package/templates/business/business-plan.md +0 -266
- package/templates/business/competitive-analysis.md +0 -312
- package/templates/fundraising/data-room-checklist.md +0 -300
- package/templates/fundraising/investor-research.md +0 -243
- package/templates/fundraising/pitch-deck-outline.md +0 -253
- package/templates/legal/gdpr-checklist.md +0 -339
- package/templates/legal/privacy-policy.md +0 -285
- package/templates/legal/terms-of-service.md +0 -222
- package/templates/mcp.json +0 -9
|
@@ -1,246 +0,0 @@
|
|
|
1
|
-
# Database Seeding Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for seeding database with test data.
|
|
4
|
-
|
|
5
|
-
## Basic Seed Script
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
// prisma/seed.ts
|
|
9
|
-
import { prisma } from '../src/lib/db'
|
|
10
|
-
import { hash } from 'bcryptjs'
|
|
11
|
-
|
|
12
|
-
async function main() {
|
|
13
|
-
console.log('Seeding database...')
|
|
14
|
-
|
|
15
|
-
// Create admin user
|
|
16
|
-
const adminPassword = await hash('admin123', 12)
|
|
17
|
-
const admin = await prisma.user.upsert({
|
|
18
|
-
where: { email: 'admin@example.com' },
|
|
19
|
-
update: {},
|
|
20
|
-
create: {
|
|
21
|
-
email: 'admin@example.com',
|
|
22
|
-
name: 'Admin User',
|
|
23
|
-
password: adminPassword,
|
|
24
|
-
role: 'ADMIN',
|
|
25
|
-
emailVerified: new Date()
|
|
26
|
-
}
|
|
27
|
-
})
|
|
28
|
-
|
|
29
|
-
console.log('Created admin:', admin.email)
|
|
30
|
-
|
|
31
|
-
// Create categories
|
|
32
|
-
const categories = await Promise.all(
|
|
33
|
-
['Technology', 'Design', 'Business'].map(name =>
|
|
34
|
-
prisma.category.upsert({
|
|
35
|
-
where: { slug: name.toLowerCase() },
|
|
36
|
-
update: {},
|
|
37
|
-
create: { name, slug: name.toLowerCase() }
|
|
38
|
-
})
|
|
39
|
-
)
|
|
40
|
-
)
|
|
41
|
-
|
|
42
|
-
console.log('Created categories:', categories.length)
|
|
43
|
-
|
|
44
|
-
// Create sample posts
|
|
45
|
-
for (let i = 0; i < 10; i++) {
|
|
46
|
-
await prisma.post.create({
|
|
47
|
-
data: {
|
|
48
|
-
title: `Sample Post ${i + 1}`,
|
|
49
|
-
slug: `sample-post-${i + 1}`,
|
|
50
|
-
content: `This is the content for post ${i + 1}`,
|
|
51
|
-
published: true,
|
|
52
|
-
authorId: admin.id,
|
|
53
|
-
categoryId: categories[i % categories.length].id
|
|
54
|
-
}
|
|
55
|
-
})
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
console.log('Created 10 sample posts')
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
main()
|
|
62
|
-
.catch(console.error)
|
|
63
|
-
.finally(() => prisma.$disconnect())
|
|
64
|
-
```
|
|
65
|
-
|
|
66
|
-
## Faker-Based Seeding
|
|
67
|
-
|
|
68
|
-
```typescript
|
|
69
|
-
// prisma/seed.ts
|
|
70
|
-
import { prisma } from '../src/lib/db'
|
|
71
|
-
import { faker } from '@faker-js/faker'
|
|
72
|
-
import { hash } from 'bcryptjs'
|
|
73
|
-
|
|
74
|
-
async function main() {
|
|
75
|
-
// Clear existing data
|
|
76
|
-
await prisma.post.deleteMany()
|
|
77
|
-
await prisma.user.deleteMany()
|
|
78
|
-
|
|
79
|
-
// Create users
|
|
80
|
-
const password = await hash('password123', 12)
|
|
81
|
-
|
|
82
|
-
const users = await Promise.all(
|
|
83
|
-
Array.from({ length: 20 }).map(() =>
|
|
84
|
-
prisma.user.create({
|
|
85
|
-
data: {
|
|
86
|
-
email: faker.internet.email(),
|
|
87
|
-
name: faker.person.fullName(),
|
|
88
|
-
password,
|
|
89
|
-
image: faker.image.avatar(),
|
|
90
|
-
emailVerified: faker.datatype.boolean() ? faker.date.past() : null
|
|
91
|
-
}
|
|
92
|
-
})
|
|
93
|
-
)
|
|
94
|
-
)
|
|
95
|
-
|
|
96
|
-
// Create posts
|
|
97
|
-
const posts = await Promise.all(
|
|
98
|
-
Array.from({ length: 50 }).map(() =>
|
|
99
|
-
prisma.post.create({
|
|
100
|
-
data: {
|
|
101
|
-
title: faker.lorem.sentence(),
|
|
102
|
-
slug: faker.helpers.slugify(faker.lorem.words(3)),
|
|
103
|
-
content: faker.lorem.paragraphs(5),
|
|
104
|
-
excerpt: faker.lorem.paragraph(),
|
|
105
|
-
published: faker.datatype.boolean(),
|
|
106
|
-
authorId: faker.helpers.arrayElement(users).id,
|
|
107
|
-
createdAt: faker.date.past(),
|
|
108
|
-
updatedAt: faker.date.recent()
|
|
109
|
-
}
|
|
110
|
-
})
|
|
111
|
-
)
|
|
112
|
-
)
|
|
113
|
-
|
|
114
|
-
// Create comments
|
|
115
|
-
await Promise.all(
|
|
116
|
-
posts.flatMap(post =>
|
|
117
|
-
Array.from({ length: faker.number.int({ min: 0, max: 10 }) }).map(() =>
|
|
118
|
-
prisma.comment.create({
|
|
119
|
-
data: {
|
|
120
|
-
content: faker.lorem.sentences(),
|
|
121
|
-
postId: post.id,
|
|
122
|
-
userId: faker.helpers.arrayElement(users).id,
|
|
123
|
-
createdAt: faker.date.recent()
|
|
124
|
-
}
|
|
125
|
-
})
|
|
126
|
-
)
|
|
127
|
-
)
|
|
128
|
-
)
|
|
129
|
-
|
|
130
|
-
console.log('Database seeded!')
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
main()
|
|
134
|
-
.catch(console.error)
|
|
135
|
-
.finally(() => prisma.$disconnect())
|
|
136
|
-
```
|
|
137
|
-
|
|
138
|
-
## Environment-Specific Seeds
|
|
139
|
-
|
|
140
|
-
```typescript
|
|
141
|
-
// prisma/seed.ts
|
|
142
|
-
import { prisma } from '../src/lib/db'
|
|
143
|
-
|
|
144
|
-
const ENV = process.env.SEED_ENV || 'development'
|
|
145
|
-
|
|
146
|
-
async function seedDevelopment() {
|
|
147
|
-
// Create test users and data
|
|
148
|
-
await createTestUsers()
|
|
149
|
-
await createSampleContent()
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
async function seedProduction() {
|
|
153
|
-
// Only create essential data
|
|
154
|
-
await createAdminUser()
|
|
155
|
-
await createDefaultCategories()
|
|
156
|
-
await createSystemSettings()
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
async function seedStaging() {
|
|
160
|
-
// Production-like but with some test data
|
|
161
|
-
await seedProduction()
|
|
162
|
-
await createDemoAccounts()
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
async function main() {
|
|
166
|
-
console.log(`Seeding for environment: ${ENV}`)
|
|
167
|
-
|
|
168
|
-
switch (ENV) {
|
|
169
|
-
case 'production':
|
|
170
|
-
await seedProduction()
|
|
171
|
-
break
|
|
172
|
-
case 'staging':
|
|
173
|
-
await seedStaging()
|
|
174
|
-
break
|
|
175
|
-
default:
|
|
176
|
-
await seedDevelopment()
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
main()
|
|
181
|
-
.catch(console.error)
|
|
182
|
-
.finally(() => prisma.$disconnect())
|
|
183
|
-
```
|
|
184
|
-
|
|
185
|
-
## Seed with Relations
|
|
186
|
-
|
|
187
|
-
```typescript
|
|
188
|
-
// prisma/seed.ts
|
|
189
|
-
async function seedWithRelations() {
|
|
190
|
-
// Create team with members
|
|
191
|
-
const team = await prisma.team.create({
|
|
192
|
-
data: {
|
|
193
|
-
name: 'Development Team',
|
|
194
|
-
members: {
|
|
195
|
-
create: [
|
|
196
|
-
{ role: 'OWNER', user: { create: { email: 'owner@example.com', name: 'Owner' } } },
|
|
197
|
-
{ role: 'ADMIN', user: { create: { email: 'admin@example.com', name: 'Admin' } } },
|
|
198
|
-
{ role: 'MEMBER', user: { create: { email: 'member@example.com', name: 'Member' } } }
|
|
199
|
-
]
|
|
200
|
-
},
|
|
201
|
-
projects: {
|
|
202
|
-
create: [
|
|
203
|
-
{
|
|
204
|
-
name: 'Project Alpha',
|
|
205
|
-
tasks: {
|
|
206
|
-
create: [
|
|
207
|
-
{ title: 'Task 1', status: 'TODO' },
|
|
208
|
-
{ title: 'Task 2', status: 'IN_PROGRESS' },
|
|
209
|
-
{ title: 'Task 3', status: 'DONE' }
|
|
210
|
-
]
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
]
|
|
214
|
-
}
|
|
215
|
-
},
|
|
216
|
-
include: {
|
|
217
|
-
members: { include: { user: true } },
|
|
218
|
-
projects: { include: { tasks: true } }
|
|
219
|
-
}
|
|
220
|
-
})
|
|
221
|
-
|
|
222
|
-
console.log('Created team with members and projects:', team.name)
|
|
223
|
-
}
|
|
224
|
-
```
|
|
225
|
-
|
|
226
|
-
## Package.json Scripts
|
|
227
|
-
|
|
228
|
-
```json
|
|
229
|
-
{
|
|
230
|
-
"prisma": {
|
|
231
|
-
"seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts"
|
|
232
|
-
},
|
|
233
|
-
"scripts": {
|
|
234
|
-
"db:seed": "prisma db seed",
|
|
235
|
-
"db:seed:prod": "SEED_ENV=production prisma db seed",
|
|
236
|
-
"db:reset": "prisma migrate reset"
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
```
|
|
240
|
-
|
|
241
|
-
## When to Use
|
|
242
|
-
|
|
243
|
-
- Development setup
|
|
244
|
-
- Testing data
|
|
245
|
-
- Demo environments
|
|
246
|
-
- Initial deployment
|
|
@@ -1,153 +0,0 @@
|
|
|
1
|
-
# Soft Delete Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for implementing soft delete with Prisma.
|
|
4
|
-
|
|
5
|
-
## Schema Setup
|
|
6
|
-
|
|
7
|
-
```prisma
|
|
8
|
-
// schema.prisma
|
|
9
|
-
model Document {
|
|
10
|
-
id String @id @default(cuid())
|
|
11
|
-
title String
|
|
12
|
-
content String?
|
|
13
|
-
deletedAt DateTime? // Soft delete marker
|
|
14
|
-
createdAt DateTime @default(now())
|
|
15
|
-
updatedAt DateTime @updatedAt
|
|
16
|
-
|
|
17
|
-
@@index([deletedAt])
|
|
18
|
-
}
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
## Prisma Client Extension
|
|
22
|
-
|
|
23
|
-
```typescript
|
|
24
|
-
// lib/db.ts
|
|
25
|
-
import { PrismaClient } from '@prisma/client'
|
|
26
|
-
|
|
27
|
-
const prisma = new PrismaClient().$extends({
|
|
28
|
-
query: {
|
|
29
|
-
document: {
|
|
30
|
-
async findMany({ args, query }) {
|
|
31
|
-
args.where = { ...args.where, deletedAt: null }
|
|
32
|
-
return query(args)
|
|
33
|
-
},
|
|
34
|
-
async findFirst({ args, query }) {
|
|
35
|
-
args.where = { ...args.where, deletedAt: null }
|
|
36
|
-
return query(args)
|
|
37
|
-
},
|
|
38
|
-
async findUnique({ args, query }) {
|
|
39
|
-
// findUnique doesn't support compound where, use findFirst
|
|
40
|
-
return query(args)
|
|
41
|
-
},
|
|
42
|
-
async delete({ args }) {
|
|
43
|
-
return prisma.document.update({
|
|
44
|
-
where: args.where,
|
|
45
|
-
data: { deletedAt: new Date() }
|
|
46
|
-
})
|
|
47
|
-
},
|
|
48
|
-
async deleteMany({ args }) {
|
|
49
|
-
return prisma.document.updateMany({
|
|
50
|
-
where: args.where,
|
|
51
|
-
data: { deletedAt: new Date() }
|
|
52
|
-
})
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
export { prisma }
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
## Manual Soft Delete
|
|
62
|
-
|
|
63
|
-
```typescript
|
|
64
|
-
// lib/documents.ts
|
|
65
|
-
import { prisma } from '@/lib/db'
|
|
66
|
-
|
|
67
|
-
export async function softDelete(id: string) {
|
|
68
|
-
return prisma.document.update({
|
|
69
|
-
where: { id },
|
|
70
|
-
data: { deletedAt: new Date() }
|
|
71
|
-
})
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export async function restore(id: string) {
|
|
75
|
-
return prisma.document.update({
|
|
76
|
-
where: { id },
|
|
77
|
-
data: { deletedAt: null }
|
|
78
|
-
})
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export async function hardDelete(id: string) {
|
|
82
|
-
return prisma.document.delete({
|
|
83
|
-
where: { id }
|
|
84
|
-
})
|
|
85
|
-
}
|
|
86
|
-
```
|
|
87
|
-
|
|
88
|
-
## Query Helpers
|
|
89
|
-
|
|
90
|
-
```typescript
|
|
91
|
-
// lib/documents.ts
|
|
92
|
-
// Only active (non-deleted)
|
|
93
|
-
export async function findActive() {
|
|
94
|
-
return prisma.document.findMany({
|
|
95
|
-
where: { deletedAt: null }
|
|
96
|
-
})
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// Only deleted
|
|
100
|
-
export async function findDeleted() {
|
|
101
|
-
return prisma.document.findMany({
|
|
102
|
-
where: { deletedAt: { not: null } }
|
|
103
|
-
})
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
// Include deleted (for admin)
|
|
107
|
-
export async function findAll() {
|
|
108
|
-
return prisma.document.findMany()
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// With deleted flag
|
|
112
|
-
export async function findWithDeletedFlag(id: string) {
|
|
113
|
-
const doc = await prisma.document.findUnique({
|
|
114
|
-
where: { id }
|
|
115
|
-
})
|
|
116
|
-
|
|
117
|
-
return doc ? { ...doc, isDeleted: !!doc.deletedAt } : null
|
|
118
|
-
}
|
|
119
|
-
```
|
|
120
|
-
|
|
121
|
-
## Cascade Soft Delete
|
|
122
|
-
|
|
123
|
-
```typescript
|
|
124
|
-
// lib/documents.ts
|
|
125
|
-
export async function softDeleteWithRelations(userId: string) {
|
|
126
|
-
const now = new Date()
|
|
127
|
-
|
|
128
|
-
return prisma.$transaction([
|
|
129
|
-
// Soft delete user
|
|
130
|
-
prisma.user.update({
|
|
131
|
-
where: { id: userId },
|
|
132
|
-
data: { deletedAt: now }
|
|
133
|
-
}),
|
|
134
|
-
// Soft delete user's posts
|
|
135
|
-
prisma.post.updateMany({
|
|
136
|
-
where: { authorId: userId },
|
|
137
|
-
data: { deletedAt: now }
|
|
138
|
-
}),
|
|
139
|
-
// Soft delete user's comments
|
|
140
|
-
prisma.comment.updateMany({
|
|
141
|
-
where: { authorId: userId },
|
|
142
|
-
data: { deletedAt: now }
|
|
143
|
-
})
|
|
144
|
-
])
|
|
145
|
-
}
|
|
146
|
-
```
|
|
147
|
-
|
|
148
|
-
## When to Use
|
|
149
|
-
|
|
150
|
-
- User data retention requirements
|
|
151
|
-
- Audit trail needs
|
|
152
|
-
- Undo/restore functionality
|
|
153
|
-
- Legal compliance
|
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
# Database Transaction Patterns
|
|
2
|
-
|
|
3
|
-
Patterns for handling database transactions with Prisma.
|
|
4
|
-
|
|
5
|
-
## Basic Transaction
|
|
6
|
-
|
|
7
|
-
```typescript
|
|
8
|
-
// lib/db.ts
|
|
9
|
-
import { prisma } from '@/lib/db'
|
|
10
|
-
|
|
11
|
-
export async function transferCredits(
|
|
12
|
-
fromUserId: string,
|
|
13
|
-
toUserId: string,
|
|
14
|
-
amount: number
|
|
15
|
-
) {
|
|
16
|
-
return prisma.$transaction(async (tx) => {
|
|
17
|
-
// Deduct from sender
|
|
18
|
-
const sender = await tx.user.update({
|
|
19
|
-
where: { id: fromUserId },
|
|
20
|
-
data: { credits: { decrement: amount } }
|
|
21
|
-
})
|
|
22
|
-
|
|
23
|
-
if (sender.credits < 0) {
|
|
24
|
-
throw new Error('Insufficient credits')
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Add to recipient
|
|
28
|
-
await tx.user.update({
|
|
29
|
-
where: { id: toUserId },
|
|
30
|
-
data: { credits: { increment: amount } }
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
// Record transaction
|
|
34
|
-
return tx.transaction.create({
|
|
35
|
-
data: {
|
|
36
|
-
fromUserId,
|
|
37
|
-
toUserId,
|
|
38
|
-
amount,
|
|
39
|
-
type: 'TRANSFER'
|
|
40
|
-
}
|
|
41
|
-
})
|
|
42
|
-
})
|
|
43
|
-
}
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
## Sequential Operations
|
|
47
|
-
|
|
48
|
-
```typescript
|
|
49
|
-
// lib/db.ts
|
|
50
|
-
export async function createOrder(
|
|
51
|
-
userId: string,
|
|
52
|
-
items: { productId: string; quantity: number }[]
|
|
53
|
-
) {
|
|
54
|
-
return prisma.$transaction(async (tx) => {
|
|
55
|
-
// 1. Check inventory
|
|
56
|
-
for (const item of items) {
|
|
57
|
-
const product = await tx.product.findUnique({
|
|
58
|
-
where: { id: item.productId }
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
if (!product || product.stock < item.quantity) {
|
|
62
|
-
throw new Error(`Insufficient stock for ${item.productId}`)
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// 2. Create order
|
|
67
|
-
const order = await tx.order.create({
|
|
68
|
-
data: {
|
|
69
|
-
userId,
|
|
70
|
-
status: 'PENDING',
|
|
71
|
-
items: {
|
|
72
|
-
create: items.map((item) => ({
|
|
73
|
-
productId: item.productId,
|
|
74
|
-
quantity: item.quantity
|
|
75
|
-
}))
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
})
|
|
79
|
-
|
|
80
|
-
// 3. Decrement inventory
|
|
81
|
-
for (const item of items) {
|
|
82
|
-
await tx.product.update({
|
|
83
|
-
where: { id: item.productId },
|
|
84
|
-
data: { stock: { decrement: item.quantity } }
|
|
85
|
-
})
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
return order
|
|
89
|
-
})
|
|
90
|
-
}
|
|
91
|
-
```
|
|
92
|
-
|
|
93
|
-
## Transaction Options
|
|
94
|
-
|
|
95
|
-
```typescript
|
|
96
|
-
// lib/db.ts
|
|
97
|
-
export async function riskyOperation() {
|
|
98
|
-
return prisma.$transaction(
|
|
99
|
-
async (tx) => {
|
|
100
|
-
// Long-running operation
|
|
101
|
-
await tx.heavyComputation.create({ ... })
|
|
102
|
-
},
|
|
103
|
-
{
|
|
104
|
-
maxWait: 5000, // Wait up to 5s for transaction slot
|
|
105
|
-
timeout: 10000, // Allow up to 10s for transaction
|
|
106
|
-
isolationLevel: 'Serializable' // Strictest isolation
|
|
107
|
-
}
|
|
108
|
-
)
|
|
109
|
-
}
|
|
110
|
-
```
|
|
111
|
-
|
|
112
|
-
## Batch Operations
|
|
113
|
-
|
|
114
|
-
```typescript
|
|
115
|
-
// lib/db.ts
|
|
116
|
-
export async function batchUpdate(updates: { id: string; data: any }[]) {
|
|
117
|
-
// Use $transaction for batching multiple operations
|
|
118
|
-
return prisma.$transaction(
|
|
119
|
-
updates.map((update) =>
|
|
120
|
-
prisma.item.update({
|
|
121
|
-
where: { id: update.id },
|
|
122
|
-
data: update.data
|
|
123
|
-
})
|
|
124
|
-
)
|
|
125
|
-
)
|
|
126
|
-
}
|
|
127
|
-
```
|
|
128
|
-
|
|
129
|
-
## Optimistic Locking
|
|
130
|
-
|
|
131
|
-
```typescript
|
|
132
|
-
// lib/db.ts
|
|
133
|
-
export async function updateWithVersion(
|
|
134
|
-
id: string,
|
|
135
|
-
expectedVersion: number,
|
|
136
|
-
data: any
|
|
137
|
-
) {
|
|
138
|
-
const result = await prisma.document.updateMany({
|
|
139
|
-
where: {
|
|
140
|
-
id,
|
|
141
|
-
version: expectedVersion
|
|
142
|
-
},
|
|
143
|
-
data: {
|
|
144
|
-
...data,
|
|
145
|
-
version: { increment: 1 }
|
|
146
|
-
}
|
|
147
|
-
})
|
|
148
|
-
|
|
149
|
-
if (result.count === 0) {
|
|
150
|
-
throw new Error('Concurrent modification detected')
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
return prisma.document.findUnique({ where: { id } })
|
|
154
|
-
}
|
|
155
|
-
```
|
|
156
|
-
|
|
157
|
-
## When to Use
|
|
158
|
-
|
|
159
|
-
- Money/credit transfers
|
|
160
|
-
- Order processing
|
|
161
|
-
- Inventory management
|
|
162
|
-
- Multi-table updates
|