@derwinjs/db 0.1.0
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 +25 -0
- package/README.md +79 -0
- package/dist/agent-findings-ingestor.d.ts +40 -0
- package/dist/agent-findings-ingestor.d.ts.map +1 -0
- package/dist/agent-findings-ingestor.js +154 -0
- package/dist/agent-findings-ingestor.js.map +1 -0
- package/dist/classification-trust-store.d.ts +31 -0
- package/dist/classification-trust-store.d.ts.map +1 -0
- package/dist/classification-trust-store.js +154 -0
- package/dist/classification-trust-store.js.map +1 -0
- package/dist/coverage-gap-reporter.d.ts +35 -0
- package/dist/coverage-gap-reporter.d.ts.map +1 -0
- package/dist/coverage-gap-reporter.js +84 -0
- package/dist/coverage-gap-reporter.js.map +1 -0
- package/dist/fix-policy.d.ts +46 -0
- package/dist/fix-policy.d.ts.map +1 -0
- package/dist/fix-policy.js +162 -0
- package/dist/fix-policy.js.map +1 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +53 -0
- package/dist/index.js.map +1 -0
- package/dist/learning-health-reporter.d.ts +37 -0
- package/dist/learning-health-reporter.d.ts.map +1 -0
- package/dist/learning-health-reporter.js +141 -0
- package/dist/learning-health-reporter.js.map +1 -0
- package/dist/prisma.d.ts +31 -0
- package/dist/prisma.d.ts.map +1 -0
- package/dist/prisma.js +31 -0
- package/dist/prisma.js.map +1 -0
- package/dist/qa-fix-attempt-store.d.ts +28 -0
- package/dist/qa-fix-attempt-store.d.ts.map +1 -0
- package/dist/qa-fix-attempt-store.js +258 -0
- package/dist/qa-fix-attempt-store.js.map +1 -0
- package/dist/qa-pattern-store.d.ts +32 -0
- package/dist/qa-pattern-store.d.ts.map +1 -0
- package/dist/qa-pattern-store.js +123 -0
- package/dist/qa-pattern-store.js.map +1 -0
- package/dist/qa-revert-store.d.ts +24 -0
- package/dist/qa-revert-store.d.ts.map +1 -0
- package/dist/qa-revert-store.js +139 -0
- package/dist/qa-revert-store.js.map +1 -0
- package/dist/qa-run-store.d.ts +46 -0
- package/dist/qa-run-store.d.ts.map +1 -0
- package/dist/qa-run-store.js +201 -0
- package/dist/qa-run-store.js.map +1 -0
- package/dist/qa-ticket-store.d.ts +35 -0
- package/dist/qa-ticket-store.d.ts.map +1 -0
- package/dist/qa-ticket-store.js +293 -0
- package/dist/qa-ticket-store.js.map +1 -0
- package/dist/qa-uniformity-store.d.ts +41 -0
- package/dist/qa-uniformity-store.d.ts.map +1 -0
- package/dist/qa-uniformity-store.js +288 -0
- package/dist/qa-uniformity-store.js.map +1 -0
- package/dist/scripts/smoke-auto-fix.d.ts +37 -0
- package/dist/scripts/smoke-auto-fix.d.ts.map +1 -0
- package/dist/scripts/smoke-auto-fix.js +270 -0
- package/dist/scripts/smoke-auto-fix.js.map +1 -0
- package/dist/scripts/smoke-learning-loop.d.ts +21 -0
- package/dist/scripts/smoke-learning-loop.d.ts.map +1 -0
- package/dist/scripts/smoke-learning-loop.js +375 -0
- package/dist/scripts/smoke-learning-loop.js.map +1 -0
- package/dist/scripts/smoke-orchestration.d.ts +35 -0
- package/dist/scripts/smoke-orchestration.d.ts.map +1 -0
- package/dist/scripts/smoke-orchestration.js +215 -0
- package/dist/scripts/smoke-orchestration.js.map +1 -0
- package/dist/scripts/smoke-qa-ticket-store.d.ts +18 -0
- package/dist/scripts/smoke-qa-ticket-store.d.ts.map +1 -0
- package/dist/scripts/smoke-qa-ticket-store.js +233 -0
- package/dist/scripts/smoke-qa-ticket-store.js.map +1 -0
- package/package.json +69 -0
- package/prisma/migrations/20260501165631_init/migration.sql +407 -0
- package/prisma/migrations/20260503051425_0002_qap018b_qaticket_crosslink_fields/migration.sql +6 -0
- package/prisma/migrations/20260504231316_add_project_repofullname_and_webhooksecret/migration.sql +12 -0
- package/prisma/migrations/20260504232851_add_qaticket_resolvedby/migration.sql +2 -0
- package/prisma/migrations/20260505042646_add_qapattern_qarevert/migration.sql +77 -0
- package/prisma/migrations/20260505055826_add_qauniformity_and_agent_trigger/migration.sql +35 -0
- package/prisma/migrations/migration_lock.toml +3 -0
- package/prisma/schema.prisma +748 -0
- package/prisma/seed.ts +181 -0
- package/prisma-client/default.d.ts +1 -0
- package/prisma-client/default.js +1 -0
- package/prisma-client/edge.d.ts +1 -0
- package/prisma-client/edge.js +631 -0
- package/prisma-client/index-browser.js +615 -0
- package/prisma-client/index.d.ts +34509 -0
- package/prisma-client/index.js +660 -0
- package/prisma-client/libquery_engine-darwin-arm64.dylib.node +0 -0
- package/prisma-client/libquery_engine-linux-arm64-openssl-3.0.x.so.node +0 -0
- package/prisma-client/libquery_engine-rhel-openssl-3.0.x.so.node +0 -0
- package/prisma-client/package.json +97 -0
- package/prisma-client/runtime/edge-esm.js +31 -0
- package/prisma-client/runtime/edge.js +31 -0
- package/prisma-client/runtime/index-browser.d.ts +365 -0
- package/prisma-client/runtime/index-browser.js +13 -0
- package/prisma-client/runtime/library.d.ts +3403 -0
- package/prisma-client/runtime/library.js +143 -0
- package/prisma-client/runtime/react-native.js +80 -0
- package/prisma-client/runtime/wasm.js +32 -0
- package/prisma-client/schema.prisma +748 -0
- package/prisma-client/wasm.d.ts +1 -0
- package/prisma-client/wasm.js +615 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QAFixAttemptStore — Prisma implementation against Derwin's QAFixAttempt table.
|
|
3
|
+
*
|
|
4
|
+
* QAP-011 (Sprint 2). Implements the QAFixAttemptStore contract from
|
|
5
|
+
* @derwinjs/sdk (see packages/sdk/src/types/qa-fix-attempt-store.ts) against
|
|
6
|
+
* the @derwinjs/db Prisma client. Mirrors createPrismaQATicketStore in
|
|
7
|
+
* structure: a factory taking `{ prisma }` so callers inject the client
|
|
8
|
+
* (per the architecture pivot's DI seam — @derwinjs/db owns persistence; the
|
|
9
|
+
* orchestrator wires it in via configureQAPlatform()).
|
|
10
|
+
*
|
|
11
|
+
* Tenant isolation: every method scopes by projectId in the WHERE clause.
|
|
12
|
+
* App-layer guards in Sprint 2 ahead of the full Supabase RLS migration
|
|
13
|
+
* (Sprint 3 / QAP-024). The negative path (wrong-project read) returns
|
|
14
|
+
* null / throws not_found rather than the wrong tenant's row — defense in
|
|
15
|
+
* depth against tenant enumeration.
|
|
16
|
+
*
|
|
17
|
+
* Composes with QATicketStore: a ticket id is the qaTicketId FK; the store
|
|
18
|
+
* does NOT validate the ticket exists pre-insert (Prisma's P2003 surfaces
|
|
19
|
+
* FK violations consistently).
|
|
20
|
+
*/
|
|
21
|
+
import { Prisma } from './prisma.js';
|
|
22
|
+
import { QAFixAttemptStoreError, isTerminalAttemptStatus, } from '@derwinjs/sdk';
|
|
23
|
+
// ─── Factory ─────────────────────────────────────────────────────────────
|
|
24
|
+
export function createPrismaQAFixAttemptStore(config) {
|
|
25
|
+
const { prisma } = config;
|
|
26
|
+
return {
|
|
27
|
+
async createAttempt(input) {
|
|
28
|
+
validateCreateInput(input);
|
|
29
|
+
let created;
|
|
30
|
+
try {
|
|
31
|
+
created = await prisma.qAFixAttempt.create({
|
|
32
|
+
data: {
|
|
33
|
+
qaTicketId: input.qaTicketId,
|
|
34
|
+
projectId: input.projectId,
|
|
35
|
+
attemptNumber: input.attemptNumber,
|
|
36
|
+
promptText: input.promptText,
|
|
37
|
+
diff: input.diff ?? null,
|
|
38
|
+
diffSizeBytes: input.diffSizeBytes ?? null,
|
|
39
|
+
modelName: input.modelName ?? null,
|
|
40
|
+
modelVersion: input.modelVersion ?? null,
|
|
41
|
+
inputTokens: input.inputTokens ?? null,
|
|
42
|
+
outputTokens: input.outputTokens ?? null,
|
|
43
|
+
costCents: input.costCents ?? null,
|
|
44
|
+
preVerifyResult: input.preVerifyResult === undefined
|
|
45
|
+
? Prisma.JsonNull
|
|
46
|
+
: input.preVerifyResult,
|
|
47
|
+
preVerifyPassed: input.preVerifyPassed ?? null,
|
|
48
|
+
branchName: input.branchName ?? null,
|
|
49
|
+
prUrl: input.prUrl ?? null,
|
|
50
|
+
prNumber: input.prNumber ?? null,
|
|
51
|
+
postVerifyResult: input.postVerifyResult === undefined
|
|
52
|
+
? Prisma.JsonNull
|
|
53
|
+
: input.postVerifyResult,
|
|
54
|
+
dispatchStatus: input.dispatchStatus ?? 'AUTHORING',
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
catch (e) {
|
|
59
|
+
if (e instanceof Prisma.PrismaClientKnownRequestError && e.code === 'P2003') {
|
|
60
|
+
throw new QAFixAttemptStoreError('fk_violation', 'Unknown qaTicketId or projectId — foreign key violation', {
|
|
61
|
+
qaTicketId: input.qaTicketId,
|
|
62
|
+
projectId: input.projectId,
|
|
63
|
+
prismaCode: e.code,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
throw e;
|
|
67
|
+
}
|
|
68
|
+
const attempt = mapAttempt(created);
|
|
69
|
+
return {
|
|
70
|
+
attempt,
|
|
71
|
+
ref: { id: attempt.id, projectId: attempt.projectId },
|
|
72
|
+
};
|
|
73
|
+
},
|
|
74
|
+
async updateAttempt(ref, patch) {
|
|
75
|
+
// Tenant-scope guard via findFirst — projectId mismatch reports
|
|
76
|
+
// not_found rather than letting Prisma's update succeed by id alone.
|
|
77
|
+
const existing = await prisma.qAFixAttempt.findFirst({
|
|
78
|
+
where: { id: ref.id, projectId: ref.projectId },
|
|
79
|
+
});
|
|
80
|
+
if (!existing) {
|
|
81
|
+
throw new QAFixAttemptStoreError('not_found', `Attempt ${ref.id} not found in project ${ref.projectId}`);
|
|
82
|
+
}
|
|
83
|
+
const data = {};
|
|
84
|
+
if (patch.dispatchStatus !== undefined)
|
|
85
|
+
data.dispatchStatus = patch.dispatchStatus;
|
|
86
|
+
if (patch.diff !== undefined)
|
|
87
|
+
data.diff = patch.diff;
|
|
88
|
+
if (patch.diffSizeBytes !== undefined)
|
|
89
|
+
data.diffSizeBytes = patch.diffSizeBytes;
|
|
90
|
+
if (patch.modelName !== undefined)
|
|
91
|
+
data.modelName = patch.modelName;
|
|
92
|
+
if (patch.modelVersion !== undefined)
|
|
93
|
+
data.modelVersion = patch.modelVersion;
|
|
94
|
+
if (patch.inputTokens !== undefined)
|
|
95
|
+
data.inputTokens = patch.inputTokens;
|
|
96
|
+
if (patch.outputTokens !== undefined)
|
|
97
|
+
data.outputTokens = patch.outputTokens;
|
|
98
|
+
if (patch.costCents !== undefined)
|
|
99
|
+
data.costCents = patch.costCents;
|
|
100
|
+
if (patch.preVerifyResult !== undefined) {
|
|
101
|
+
data.preVerifyResult = patch.preVerifyResult;
|
|
102
|
+
}
|
|
103
|
+
if (patch.preVerifyPassed !== undefined)
|
|
104
|
+
data.preVerifyPassed = patch.preVerifyPassed;
|
|
105
|
+
if (patch.branchName !== undefined)
|
|
106
|
+
data.branchName = patch.branchName;
|
|
107
|
+
if (patch.prUrl !== undefined)
|
|
108
|
+
data.prUrl = patch.prUrl;
|
|
109
|
+
if (patch.prNumber !== undefined)
|
|
110
|
+
data.prNumber = patch.prNumber;
|
|
111
|
+
if (patch.mergedAt !== undefined)
|
|
112
|
+
data.mergedAt = patch.mergedAt;
|
|
113
|
+
if (patch.postVerifyResult !== undefined) {
|
|
114
|
+
data.postVerifyResult = patch.postVerifyResult;
|
|
115
|
+
}
|
|
116
|
+
if (patch.regressionDetected !== undefined) {
|
|
117
|
+
data.regressionDetected = patch.regressionDetected;
|
|
118
|
+
}
|
|
119
|
+
if (patch.autoRevertedAt !== undefined)
|
|
120
|
+
data.autoRevertedAt = patch.autoRevertedAt;
|
|
121
|
+
if (patch.humanEditLines !== undefined)
|
|
122
|
+
data.humanEditLines = patch.humanEditLines;
|
|
123
|
+
if (patch.fixSuccessScore !== undefined)
|
|
124
|
+
data.fixSuccessScore = patch.fixSuccessScore;
|
|
125
|
+
if (patch.closedAt !== undefined)
|
|
126
|
+
data.closedAt = patch.closedAt;
|
|
127
|
+
// Side effect: terminal dispatchStatus on a not-yet-closed row →
|
|
128
|
+
// closedAt set to now. Mirrors QATicketStore's implicit-close branch.
|
|
129
|
+
const willCloseImplicitly = existing.closedAt === null &&
|
|
130
|
+
patch.closedAt === undefined &&
|
|
131
|
+
patch.dispatchStatus !== undefined &&
|
|
132
|
+
isTerminalAttemptStatus(patch.dispatchStatus);
|
|
133
|
+
if (willCloseImplicitly) {
|
|
134
|
+
data.closedAt = new Date();
|
|
135
|
+
}
|
|
136
|
+
const updated = await prisma.qAFixAttempt.update({
|
|
137
|
+
where: { id: ref.id },
|
|
138
|
+
data,
|
|
139
|
+
});
|
|
140
|
+
return mapAttempt(updated);
|
|
141
|
+
},
|
|
142
|
+
async getAttempt(ref) {
|
|
143
|
+
const row = await prisma.qAFixAttempt.findFirst({
|
|
144
|
+
where: { id: ref.id, projectId: ref.projectId },
|
|
145
|
+
});
|
|
146
|
+
return row ? mapAttempt(row) : null;
|
|
147
|
+
},
|
|
148
|
+
async listAttempts(filter) {
|
|
149
|
+
if (!filter.projectId) {
|
|
150
|
+
throw new QAFixAttemptStoreError('invalid_input', 'projectId is required for listAttempts — there is no cross-tenant list query');
|
|
151
|
+
}
|
|
152
|
+
const where = {
|
|
153
|
+
projectId: filter.projectId,
|
|
154
|
+
};
|
|
155
|
+
if (filter.qaTicketId !== undefined) {
|
|
156
|
+
where.qaTicketId = filter.qaTicketId;
|
|
157
|
+
}
|
|
158
|
+
if (filter.dispatchStatus !== undefined) {
|
|
159
|
+
where.dispatchStatus = Array.isArray(filter.dispatchStatus)
|
|
160
|
+
? { in: filter.dispatchStatus }
|
|
161
|
+
: filter.dispatchStatus;
|
|
162
|
+
}
|
|
163
|
+
const limit = Math.min(Math.max(filter.limit ?? 50, 1), 200);
|
|
164
|
+
const findArgs = {
|
|
165
|
+
where,
|
|
166
|
+
orderBy: { attemptedAt: 'desc' },
|
|
167
|
+
take: limit + 1, // +1 to detect hasMore
|
|
168
|
+
};
|
|
169
|
+
if (filter.cursor) {
|
|
170
|
+
findArgs.cursor = { id: filter.cursor };
|
|
171
|
+
findArgs.skip = 1;
|
|
172
|
+
}
|
|
173
|
+
const rows = await prisma.qAFixAttempt.findMany(findArgs);
|
|
174
|
+
const hasMore = rows.length > limit;
|
|
175
|
+
const visibleRows = hasMore ? rows.slice(0, limit) : rows;
|
|
176
|
+
const attempts = visibleRows.map(mapAttempt);
|
|
177
|
+
const lastAttempt = attempts[attempts.length - 1];
|
|
178
|
+
const nextCursor = hasMore && lastAttempt ? lastAttempt.id : null;
|
|
179
|
+
const totalEstimate = await prisma.qAFixAttempt.count({ where });
|
|
180
|
+
return { attempts, nextCursor, totalEstimate };
|
|
181
|
+
},
|
|
182
|
+
async findAttemptByPRNumber(filter) {
|
|
183
|
+
if (!filter.projectId) {
|
|
184
|
+
throw new QAFixAttemptStoreError('invalid_input', 'projectId is required for findAttemptByPRNumber');
|
|
185
|
+
}
|
|
186
|
+
const row = await prisma.qAFixAttempt.findFirst({
|
|
187
|
+
where: { projectId: filter.projectId, prNumber: filter.prNumber },
|
|
188
|
+
orderBy: { attemptedAt: 'desc' },
|
|
189
|
+
});
|
|
190
|
+
return row ? mapAttempt(row) : null;
|
|
191
|
+
},
|
|
192
|
+
async countAttemptsToday(filter) {
|
|
193
|
+
if (!filter.projectId) {
|
|
194
|
+
throw new QAFixAttemptStoreError('invalid_input', 'projectId is required for countAttemptsToday');
|
|
195
|
+
}
|
|
196
|
+
const startOfDay = new Date();
|
|
197
|
+
startOfDay.setHours(0, 0, 0, 0);
|
|
198
|
+
return prisma.qAFixAttempt.count({
|
|
199
|
+
where: {
|
|
200
|
+
projectId: filter.projectId,
|
|
201
|
+
attemptedAt: { gte: startOfDay },
|
|
202
|
+
},
|
|
203
|
+
});
|
|
204
|
+
},
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
208
|
+
/**
|
|
209
|
+
* Convert a Prisma QAFixAttempt row to the SDK value type. Json columns are
|
|
210
|
+
* narrowed via cast — Prisma's JsonValue is intentionally loose, so the
|
|
211
|
+
* caller's preVerifyResult / postVerifyResult shape isn't enforced at the
|
|
212
|
+
* type level (it comes through as `unknown`).
|
|
213
|
+
*/
|
|
214
|
+
function mapAttempt(row) {
|
|
215
|
+
return {
|
|
216
|
+
id: row.id,
|
|
217
|
+
qaTicketId: row.qaTicketId,
|
|
218
|
+
projectId: row.projectId,
|
|
219
|
+
attemptNumber: row.attemptNumber,
|
|
220
|
+
promptText: row.promptText,
|
|
221
|
+
diff: row.diff,
|
|
222
|
+
diffSizeBytes: row.diffSizeBytes,
|
|
223
|
+
modelName: row.modelName,
|
|
224
|
+
modelVersion: row.modelVersion,
|
|
225
|
+
inputTokens: row.inputTokens,
|
|
226
|
+
outputTokens: row.outputTokens,
|
|
227
|
+
costCents: row.costCents,
|
|
228
|
+
preVerifyResult: row.preVerifyResult,
|
|
229
|
+
preVerifyPassed: row.preVerifyPassed,
|
|
230
|
+
branchName: row.branchName,
|
|
231
|
+
prUrl: row.prUrl,
|
|
232
|
+
prNumber: row.prNumber,
|
|
233
|
+
dispatchStatus: row.dispatchStatus,
|
|
234
|
+
mergedAt: row.mergedAt,
|
|
235
|
+
postVerifyResult: row.postVerifyResult,
|
|
236
|
+
regressionDetected: row.regressionDetected,
|
|
237
|
+
autoRevertedAt: row.autoRevertedAt,
|
|
238
|
+
humanEditLines: row.humanEditLines,
|
|
239
|
+
fixSuccessScore: row.fixSuccessScore,
|
|
240
|
+
attemptedAt: row.attemptedAt,
|
|
241
|
+
closedAt: row.closedAt,
|
|
242
|
+
};
|
|
243
|
+
}
|
|
244
|
+
function validateCreateInput(input) {
|
|
245
|
+
if (!input.projectId) {
|
|
246
|
+
throw new QAFixAttemptStoreError('invalid_input', 'projectId is required');
|
|
247
|
+
}
|
|
248
|
+
if (!input.qaTicketId) {
|
|
249
|
+
throw new QAFixAttemptStoreError('invalid_input', 'qaTicketId is required');
|
|
250
|
+
}
|
|
251
|
+
if (!Number.isInteger(input.attemptNumber) || input.attemptNumber < 1) {
|
|
252
|
+
throw new QAFixAttemptStoreError('invalid_input', 'attemptNumber must be a positive integer (1..N)', { attemptNumber: input.attemptNumber });
|
|
253
|
+
}
|
|
254
|
+
if (!input.promptText.trim()) {
|
|
255
|
+
throw new QAFixAttemptStoreError('invalid_input', 'promptText is required');
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
//# sourceMappingURL=qa-fix-attempt-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-fix-attempt-store.js","sourceRoot":"","sources":["../src/qa-fix-attempt-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAE,MAAM,EAAqB,MAAM,aAAa,CAAC;AACxD,OAAO,EACL,sBAAsB,EACtB,uBAAuB,GAMxB,MAAM,eAAe,CAAC;AASvB,4EAA4E;AAE5E,MAAM,UAAU,6BAA6B,CAC3C,MAAqC;IAErC,MAAM,EAAE,MAAM,EAAE,GAAG,MAAM,CAAC;IAE1B,OAAO;QACL,KAAK,CAAC,aAAa,CAAC,KAAK;YACvB,mBAAmB,CAAC,KAAK,CAAC,CAAC;YAE3B,IAAI,OAAO,CAAC;YACZ,IAAI,CAAC;gBACH,OAAO,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;oBACzC,IAAI,EAAE;wBACJ,UAAU,EAAE,KAAK,CAAC,UAAU;wBAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;wBAC1B,aAAa,EAAE,KAAK,CAAC,aAAa;wBAClC,UAAU,EAAE,KAAK,CAAC,UAAU;wBAC5B,IAAI,EAAE,KAAK,CAAC,IAAI,IAAI,IAAI;wBACxB,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,IAAI;wBAC1C,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI;wBAClC,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;wBACxC,WAAW,EAAE,KAAK,CAAC,WAAW,IAAI,IAAI;wBACtC,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;wBACxC,SAAS,EAAE,KAAK,CAAC,SAAS,IAAI,IAAI;wBAClC,eAAe,EACb,KAAK,CAAC,eAAe,KAAK,SAAS;4BACjC,CAAC,CAAC,MAAM,CAAC,QAAQ;4BACjB,CAAC,CAAE,KAAK,CAAC,eAAyC;wBACtD,eAAe,EAAE,KAAK,CAAC,eAAe,IAAI,IAAI;wBAC9C,UAAU,EAAE,KAAK,CAAC,UAAU,IAAI,IAAI;wBACpC,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,IAAI;wBAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ,IAAI,IAAI;wBAChC,gBAAgB,EACd,KAAK,CAAC,gBAAgB,KAAK,SAAS;4BAClC,CAAC,CAAC,MAAM,CAAC,QAAQ;4BACjB,CAAC,CAAE,KAAK,CAAC,gBAA0C;wBACvD,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,WAAW;qBACpD;iBACF,CAAC,CAAC;YACL,CAAC;YAAC,OAAO,CAAC,EAAE,CAAC;gBACX,IAAI,CAAC,YAAY,MAAM,CAAC,6BAA6B,IAAI,CAAC,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;oBAC5E,MAAM,IAAI,sBAAsB,CAC9B,cAAc,EACd,yDAAyD,EACzD;wBACE,UAAU,EAAE,KAAK,CAAC,UAAU;wBAC5B,SAAS,EAAE,KAAK,CAAC,SAAS;wBAC1B,UAAU,EAAE,CAAC,CAAC,IAAI;qBACnB,CACF,CAAC;gBACJ,CAAC;gBACD,MAAM,CAAC,CAAC;YACV,CAAC;YAED,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,CAAC;YACpC,OAAO;gBACL,OAAO;gBACP,GAAG,EAAE,EAAE,EAAE,EAAE,OAAO,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE;aACtD,CAAC;QACJ,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,GAAG,EAAE,KAAK;YAC5B,gEAAgE;YAChE,qEAAqE;YACrE,MAAM,QAAQ,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC;gBACnD,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE;aAChD,CAAC,CAAC;YACH,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,IAAI,sBAAsB,CAC9B,WAAW,EACX,WAAW,GAAG,CAAC,EAAE,yBAAyB,GAAG,CAAC,SAAS,EAAE,CAC1D,CAAC;YACJ,CAAC;YAED,MAAM,IAAI,GAAmC,EAAE,CAAC;YAEhD,IAAI,KAAK,CAAC,cAAc,KAAK,SAAS;gBAAE,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC;YACnF,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS;gBAAE,IAAI,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC;YACrD,IAAI,KAAK,CAAC,aAAa,KAAK,SAAS;gBAAE,IAAI,CAAC,aAAa,GAAG,KAAK,CAAC,aAAa,CAAC;YAChF,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS;gBAAE,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;YACpE,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS;gBAAE,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;YAC7E,IAAI,KAAK,CAAC,WAAW,KAAK,SAAS;gBAAE,IAAI,CAAC,WAAW,GAAG,KAAK,CAAC,WAAW,CAAC;YAC1E,IAAI,KAAK,CAAC,YAAY,KAAK,SAAS;gBAAE,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC,YAAY,CAAC;YAC7E,IAAI,KAAK,CAAC,SAAS,KAAK,SAAS;gBAAE,IAAI,CAAC,SAAS,GAAG,KAAK,CAAC,SAAS,CAAC;YACpE,IAAI,KAAK,CAAC,eAAe,KAAK,SAAS,EAAE,CAAC;gBACxC,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,eAAwC,CAAC;YACxE,CAAC;YACD,IAAI,KAAK,CAAC,eAAe,KAAK,SAAS;gBAAE,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,eAAe,CAAC;YACtF,IAAI,KAAK,CAAC,UAAU,KAAK,SAAS;gBAAE,IAAI,CAAC,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;YACvE,IAAI,KAAK,CAAC,KAAK,KAAK,SAAS;gBAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;YACxD,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS;gBAAE,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;YACjE,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS;gBAAE,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;YACjE,IAAI,KAAK,CAAC,gBAAgB,KAAK,SAAS,EAAE,CAAC;gBACzC,IAAI,CAAC,gBAAgB,GAAG,KAAK,CAAC,gBAAyC,CAAC;YAC1E,CAAC;YACD,IAAI,KAAK,CAAC,kBAAkB,KAAK,SAAS,EAAE,CAAC;gBAC3C,IAAI,CAAC,kBAAkB,GAAG,KAAK,CAAC,kBAAkB,CAAC;YACrD,CAAC;YACD,IAAI,KAAK,CAAC,cAAc,KAAK,SAAS;gBAAE,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC;YACnF,IAAI,KAAK,CAAC,cAAc,KAAK,SAAS;gBAAE,IAAI,CAAC,cAAc,GAAG,KAAK,CAAC,cAAc,CAAC;YACnF,IAAI,KAAK,CAAC,eAAe,KAAK,SAAS;gBAAE,IAAI,CAAC,eAAe,GAAG,KAAK,CAAC,eAAe,CAAC;YACtF,IAAI,KAAK,CAAC,QAAQ,KAAK,SAAS;gBAAE,IAAI,CAAC,QAAQ,GAAG,KAAK,CAAC,QAAQ,CAAC;YAEjE,iEAAiE;YACjE,sEAAsE;YACtE,MAAM,mBAAmB,GACvB,QAAQ,CAAC,QAAQ,KAAK,IAAI;gBAC1B,KAAK,CAAC,QAAQ,KAAK,SAAS;gBAC5B,KAAK,CAAC,cAAc,KAAK,SAAS;gBAClC,uBAAuB,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;YAChD,IAAI,mBAAmB,EAAE,CAAC;gBACxB,IAAI,CAAC,QAAQ,GAAG,IAAI,IAAI,EAAE,CAAC;YAC7B,CAAC;YAED,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,MAAM,CAAC;gBAC/C,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE;gBACrB,IAAI;aACL,CAAC,CAAC;YACH,OAAO,UAAU,CAAC,OAAO,CAAC,CAAC;QAC7B,CAAC;QAED,KAAK,CAAC,UAAU,CAAC,GAAG;YAClB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC;gBAC9C,KAAK,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,EAAE,EAAE,SAAS,EAAE,GAAG,CAAC,SAAS,EAAE;aAChD,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACtC,CAAC;QAED,KAAK,CAAC,YAAY,CAAC,MAAM;YACvB,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;gBACtB,MAAM,IAAI,sBAAsB,CAC9B,eAAe,EACf,8EAA8E,CAC/E,CAAC;YACJ,CAAC;YAED,MAAM,KAAK,GAAkC;gBAC3C,SAAS,EAAE,MAAM,CAAC,SAAS;aAC5B,CAAC;YAEF,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,EAAE,CAAC;gBACpC,KAAK,CAAC,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC;YACvC,CAAC;YACD,IAAI,MAAM,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;gBACxC,KAAK,CAAC,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC;oBACzD,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,cAAc,EAAE;oBAC/B,CAAC,CAAC,MAAM,CAAC,cAAc,CAAC;YAC5B,CAAC;YAED,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;YAE7D,MAAM,QAAQ,GAAoC;gBAChD,KAAK;gBACL,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE;gBAChC,IAAI,EAAE,KAAK,GAAG,CAAC,EAAE,uBAAuB;aACzC,CAAC;YACF,IAAI,MAAM,CAAC,MAAM,EAAE,CAAC;gBAClB,QAAQ,CAAC,MAAM,GAAG,EAAE,EAAE,EAAE,MAAM,CAAC,MAAM,EAAE,CAAC;gBACxC,QAAQ,CAAC,IAAI,GAAG,CAAC,CAAC;YACpB,CAAC;YAED,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;YAC1D,MAAM,OAAO,GAAG,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;YACpC,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;YAC1D,MAAM,QAAQ,GAAG,WAAW,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YAC7C,MAAM,WAAW,GAAG,QAAQ,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;YAClD,MAAM,UAAU,GAAG,OAAO,IAAI,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;YAElE,MAAM,aAAa,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,CAAC,CAAC;YAEjE,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,aAAa,EAAE,CAAC;QACjD,CAAC;QAED,KAAK,CAAC,qBAAqB,CAAC,MAAmC;YAC7D,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;gBACtB,MAAM,IAAI,sBAAsB,CAC9B,eAAe,EACf,iDAAiD,CAClD,CAAC;YACJ,CAAC;YACD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC;gBAC9C,KAAK,EAAE,EAAE,SAAS,EAAE,MAAM,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE;gBACjE,OAAO,EAAE,EAAE,WAAW,EAAE,MAAM,EAAE;aACjC,CAAC,CAAC;YACH,OAAO,GAAG,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC;QACtC,CAAC;QAED,KAAK,CAAC,kBAAkB,CAAC,MAAgC;YACvD,IAAI,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;gBACtB,MAAM,IAAI,sBAAsB,CAC9B,eAAe,EACf,8CAA8C,CAC/C,CAAC;YACJ,CAAC;YACD,MAAM,UAAU,GAAG,IAAI,IAAI,EAAE,CAAC;YAC9B,UAAU,CAAC,QAAQ,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;YAChC,OAAO,MAAM,CAAC,YAAY,CAAC,KAAK,CAAC;gBAC/B,KAAK,EAAE;oBACL,SAAS,EAAE,MAAM,CAAC,SAAS;oBAC3B,WAAW,EAAE,EAAE,GAAG,EAAE,UAAU,EAAE;iBACjC;aACF,CAAC,CAAC;QACL,CAAC;KACF,CAAC;AACJ,CAAC;AAED,4EAA4E;AAE5E;;;;;GAKG;AACH,SAAS,UAAU,CAAC,GAAyD;IAC3E,OAAO;QACL,EAAE,EAAE,GAAG,CAAC,EAAE;QACV,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,aAAa,EAAE,GAAG,CAAC,aAAa;QAChC,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,YAAY,EAAE,GAAG,CAAC,YAAY;QAC9B,SAAS,EAAE,GAAG,CAAC,SAAS;QACxB,eAAe,EAAE,GAAG,CAAC,eAAe;QACpC,eAAe,EAAE,GAAG,CAAC,eAAe;QACpC,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,KAAK,EAAE,GAAG,CAAC,KAAK;QAChB,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,gBAAgB,EAAE,GAAG,CAAC,gBAAgB;QACtC,kBAAkB,EAAE,GAAG,CAAC,kBAAkB;QAC1C,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,eAAe,EAAE,GAAG,CAAC,eAAe;QACpC,WAAW,EAAE,GAAG,CAAC,WAAW;QAC5B,QAAQ,EAAE,GAAG,CAAC,QAAQ;KACvB,CAAC;AACJ,CAAC;AAED,SAAS,mBAAmB,CAAC,KAA8B;IACzD,IAAI,CAAC,KAAK,CAAC,SAAS,EAAE,CAAC;QACrB,MAAM,IAAI,sBAAsB,CAAC,eAAe,EAAE,uBAAuB,CAAC,CAAC;IAC7E,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,UAAU,EAAE,CAAC;QACtB,MAAM,IAAI,sBAAsB,CAAC,eAAe,EAAE,wBAAwB,CAAC,CAAC;IAC9E,CAAC;IACD,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,KAAK,CAAC,aAAa,GAAG,CAAC,EAAE,CAAC;QACtE,MAAM,IAAI,sBAAsB,CAC9B,eAAe,EACf,iDAAiD,EACjD,EAAE,aAAa,EAAE,KAAK,CAAC,aAAa,EAAE,CACvC,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,EAAE,EAAE,CAAC;QAC7B,MAAM,IAAI,sBAAsB,CAAC,eAAe,EAAE,wBAAwB,CAAC,CAAC;IAC9E,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QAPatternStore — Prisma implementation against Derwin's QAPattern table.
|
|
3
|
+
*
|
|
4
|
+
* QAP-019D (Sprint 2 Group D-2, Commit 1 foundation). Implements the
|
|
5
|
+
* QAPatternStore contract from @derwinjs/sdk against the @derwinjs/db Prisma
|
|
6
|
+
* client. Mirrors createPrismaQARunStore in structure: a factory taking
|
|
7
|
+
* `{ prisma }` so callers inject the client (per the architecture pivot's
|
|
8
|
+
* DI seam — @derwinjs/db owns persistence; the orchestrator wires it in via
|
|
9
|
+
* configureQAPlatform()).
|
|
10
|
+
*
|
|
11
|
+
* Three methods:
|
|
12
|
+
* - listTopPatterns: top OPEN by occurrenceCount + lastSeenAt DESC,
|
|
13
|
+
* limit defaulting to 10 and capped at 50 (Lifeline parity per
|
|
14
|
+
* Group D-2 plan decision #5).
|
|
15
|
+
* - getPattern: single read, tenant-scoped.
|
|
16
|
+
* - updatePatternStatus: operator-driven status flip with side-effect
|
|
17
|
+
* timestamps (resolvedAt / archivedAt set on transition; cleared on
|
|
18
|
+
* re-open).
|
|
19
|
+
*
|
|
20
|
+
* Tenant isolation: every method scopes by projectId in the WHERE clause.
|
|
21
|
+
* App-layer guards in Sprint 2 ahead of Supabase RLS migration in Sprint 3
|
|
22
|
+
* (QAP-024). Wrong-project reads return null; wrong-project writes throw
|
|
23
|
+
* not_found. Defense-in-depth against tenant enumeration.
|
|
24
|
+
*/
|
|
25
|
+
import type { PrismaClient } from './prisma.js';
|
|
26
|
+
import { type QAPatternStore } from '@derwinjs/sdk';
|
|
27
|
+
export interface PrismaQAPatternStoreConfig {
|
|
28
|
+
/** Generated Prisma client. Pass an instance per process. */
|
|
29
|
+
prisma: PrismaClient;
|
|
30
|
+
}
|
|
31
|
+
export declare function createPrismaQAPatternStore(config: PrismaQAPatternStoreConfig): QAPatternStore;
|
|
32
|
+
//# sourceMappingURL=qa-pattern-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-pattern-store.d.ts","sourceRoot":"","sources":["../src/qa-pattern-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAOL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,0BAA0B;IACzC,6DAA6D;IAC7D,MAAM,EAAE,YAAY,CAAC;CACtB;AASD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,0BAA0B,GAAG,cAAc,CAmE7F"}
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QAPatternStore — Prisma implementation against Derwin's QAPattern table.
|
|
3
|
+
*
|
|
4
|
+
* QAP-019D (Sprint 2 Group D-2, Commit 1 foundation). Implements the
|
|
5
|
+
* QAPatternStore contract from @derwinjs/sdk against the @derwinjs/db Prisma
|
|
6
|
+
* client. Mirrors createPrismaQARunStore in structure: a factory taking
|
|
7
|
+
* `{ prisma }` so callers inject the client (per the architecture pivot's
|
|
8
|
+
* DI seam — @derwinjs/db owns persistence; the orchestrator wires it in via
|
|
9
|
+
* configureQAPlatform()).
|
|
10
|
+
*
|
|
11
|
+
* Three methods:
|
|
12
|
+
* - listTopPatterns: top OPEN by occurrenceCount + lastSeenAt DESC,
|
|
13
|
+
* limit defaulting to 10 and capped at 50 (Lifeline parity per
|
|
14
|
+
* Group D-2 plan decision #5).
|
|
15
|
+
* - getPattern: single read, tenant-scoped.
|
|
16
|
+
* - updatePatternStatus: operator-driven status flip with side-effect
|
|
17
|
+
* timestamps (resolvedAt / archivedAt set on transition; cleared on
|
|
18
|
+
* re-open).
|
|
19
|
+
*
|
|
20
|
+
* Tenant isolation: every method scopes by projectId in the WHERE clause.
|
|
21
|
+
* App-layer guards in Sprint 2 ahead of Supabase RLS migration in Sprint 3
|
|
22
|
+
* (QAP-024). Wrong-project reads return null; wrong-project writes throw
|
|
23
|
+
* not_found. Defense-in-depth against tenant enumeration.
|
|
24
|
+
*/
|
|
25
|
+
import { QAPatternStoreError, } from '@derwinjs/sdk';
|
|
26
|
+
// ─── Constants (Lifeline parity per Decision 5) ──────────────────────────
|
|
27
|
+
const DEFAULT_LIMIT = 10;
|
|
28
|
+
const MAX_LIMIT = 50;
|
|
29
|
+
// ─── Factory ─────────────────────────────────────────────────────────────
|
|
30
|
+
export function createPrismaQAPatternStore(config) {
|
|
31
|
+
const { prisma } = config;
|
|
32
|
+
return {
|
|
33
|
+
async listTopPatterns(filter) {
|
|
34
|
+
validateProjectId(filter.projectId);
|
|
35
|
+
const limit = clampLimit(filter.limit);
|
|
36
|
+
const rows = await prisma.qAPattern.findMany({
|
|
37
|
+
where: { projectId: filter.projectId, status: 'OPEN' },
|
|
38
|
+
orderBy: [{ occurrenceCount: 'desc' }, { lastSeenAt: 'desc' }],
|
|
39
|
+
take: limit,
|
|
40
|
+
});
|
|
41
|
+
return rows.map(mapPattern);
|
|
42
|
+
},
|
|
43
|
+
async getPattern(ref) {
|
|
44
|
+
const row = await prisma.qAPattern.findFirst({
|
|
45
|
+
where: { id: ref.id, projectId: ref.projectId },
|
|
46
|
+
});
|
|
47
|
+
if (row === null)
|
|
48
|
+
return null;
|
|
49
|
+
return mapPattern(row);
|
|
50
|
+
},
|
|
51
|
+
async updatePatternStatus(ref, status) {
|
|
52
|
+
// Tenant-check first via findFirst (defense-in-depth — even though
|
|
53
|
+
// the update's WHERE clause filters by projectId, surfacing the
|
|
54
|
+
// not_found early gives callers a typed error).
|
|
55
|
+
const existing = await prisma.qAPattern.findFirst({
|
|
56
|
+
where: { id: ref.id, projectId: ref.projectId },
|
|
57
|
+
});
|
|
58
|
+
if (existing === null) {
|
|
59
|
+
throw new QAPatternStoreError('not_found', `QAPattern ${ref.id} not found in project ${ref.projectId}`, { id: ref.id, projectId: ref.projectId });
|
|
60
|
+
}
|
|
61
|
+
// Side-effect timestamps:
|
|
62
|
+
// - status → RESOLVED: set resolvedAt = now() if unset
|
|
63
|
+
// - status → ARCHIVED: set archivedAt = now() if unset
|
|
64
|
+
// - status → OPEN: clear both (re-open path)
|
|
65
|
+
const data = { status };
|
|
66
|
+
const now = new Date();
|
|
67
|
+
if (status === 'RESOLVED' && existing.resolvedAt === null) {
|
|
68
|
+
data.resolvedAt = now;
|
|
69
|
+
}
|
|
70
|
+
else if (status === 'ARCHIVED' && existing.archivedAt === null) {
|
|
71
|
+
data.archivedAt = now;
|
|
72
|
+
}
|
|
73
|
+
else if (status === 'OPEN') {
|
|
74
|
+
data.resolvedAt = null;
|
|
75
|
+
data.archivedAt = null;
|
|
76
|
+
}
|
|
77
|
+
const updated = await prisma.qAPattern.update({
|
|
78
|
+
where: { id: ref.id },
|
|
79
|
+
data,
|
|
80
|
+
});
|
|
81
|
+
return mapPattern(updated);
|
|
82
|
+
},
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
// ─── Validation ──────────────────────────────────────────────────────────
|
|
86
|
+
function validateProjectId(projectId) {
|
|
87
|
+
if (typeof projectId !== 'string' || projectId.length === 0) {
|
|
88
|
+
throw new QAPatternStoreError('invalid_input', 'QAPatternStore: projectId is required');
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function clampLimit(limit) {
|
|
92
|
+
if (limit === undefined)
|
|
93
|
+
return DEFAULT_LIMIT;
|
|
94
|
+
if (limit < 1)
|
|
95
|
+
return 1;
|
|
96
|
+
if (limit > MAX_LIMIT)
|
|
97
|
+
return MAX_LIMIT;
|
|
98
|
+
return limit;
|
|
99
|
+
}
|
|
100
|
+
function mapPattern(row) {
|
|
101
|
+
return {
|
|
102
|
+
id: row.id,
|
|
103
|
+
projectId: row.projectId,
|
|
104
|
+
signature: row.signature,
|
|
105
|
+
classification: row.classification,
|
|
106
|
+
firstSeenAt: row.firstSeenAt,
|
|
107
|
+
lastSeenAt: row.lastSeenAt,
|
|
108
|
+
occurrenceCount: row.occurrenceCount,
|
|
109
|
+
affectedTickets: [...row.affectedTickets],
|
|
110
|
+
trendDirection: row.trendDirection,
|
|
111
|
+
status: row.status,
|
|
112
|
+
resolvedAt: row.resolvedAt,
|
|
113
|
+
promotedToLintAt: row.promotedToLintAt,
|
|
114
|
+
promotedLintPR: row.promotedLintPR,
|
|
115
|
+
archivedAt: row.archivedAt,
|
|
116
|
+
fixSuccessScore: row.fixSuccessScore,
|
|
117
|
+
fixOutcomeSampleSize: row.fixOutcomeSampleSize,
|
|
118
|
+
lastWeightedAt: row.lastWeightedAt,
|
|
119
|
+
createdAt: row.createdAt,
|
|
120
|
+
updatedAt: row.updatedAt,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
//# sourceMappingURL=qa-pattern-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-pattern-store.js","sourceRoot":"","sources":["../src/qa-pattern-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QARevertStore — Prisma implementation against Derwin's QARevert table.
|
|
3
|
+
*
|
|
4
|
+
* QAP-019E (Sprint 2 Group D-2, Commit 1 foundation). Implements the
|
|
5
|
+
* QARevertStore contract from @derwinjs/sdk against the @derwinjs/db Prisma
|
|
6
|
+
* client. Mirrors createPrismaQAPatternStore in structure.
|
|
7
|
+
*
|
|
8
|
+
* Three methods:
|
|
9
|
+
* - listReverts: tenant-scoped, default `restoredAt: null` (Lifeline
|
|
10
|
+
* parity); pass `includeRestored: true` for full history.
|
|
11
|
+
* - createRevert: insert with FK violation → fk_violation error mapping.
|
|
12
|
+
* - restoreRevert: tenant-checked status flip + audit-trail timestamps.
|
|
13
|
+
*
|
|
14
|
+
* Tenant isolation: every method scopes by projectId. Cross-tenant reads
|
|
15
|
+
* return empty / null; cross-tenant writes throw not_found.
|
|
16
|
+
*/
|
|
17
|
+
import { type PrismaClient } from './prisma.js';
|
|
18
|
+
import { type QARevertStore } from '@derwinjs/sdk';
|
|
19
|
+
export interface PrismaQARevertStoreConfig {
|
|
20
|
+
/** Generated Prisma client. Pass an instance per process. */
|
|
21
|
+
prisma: PrismaClient;
|
|
22
|
+
}
|
|
23
|
+
export declare function createPrismaQARevertStore(config: PrismaQARevertStoreConfig): QARevertStore;
|
|
24
|
+
//# sourceMappingURL=qa-revert-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-revert-store.d.ts","sourceRoot":"","sources":["../src/qa-revert-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAOL,KAAK,aAAa,EACnB,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,yBAAyB;IACxC,6DAA6D;IAC7D,MAAM,EAAE,YAAY,CAAC;CACtB;AAID,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,yBAAyB,GAAG,aAAa,CAyF1F"}
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* QARevertStore — Prisma implementation against Derwin's QARevert table.
|
|
3
|
+
*
|
|
4
|
+
* QAP-019E (Sprint 2 Group D-2, Commit 1 foundation). Implements the
|
|
5
|
+
* QARevertStore contract from @derwinjs/sdk against the @derwinjs/db Prisma
|
|
6
|
+
* client. Mirrors createPrismaQAPatternStore in structure.
|
|
7
|
+
*
|
|
8
|
+
* Three methods:
|
|
9
|
+
* - listReverts: tenant-scoped, default `restoredAt: null` (Lifeline
|
|
10
|
+
* parity); pass `includeRestored: true` for full history.
|
|
11
|
+
* - createRevert: insert with FK violation → fk_violation error mapping.
|
|
12
|
+
* - restoreRevert: tenant-checked status flip + audit-trail timestamps.
|
|
13
|
+
*
|
|
14
|
+
* Tenant isolation: every method scopes by projectId. Cross-tenant reads
|
|
15
|
+
* return empty / null; cross-tenant writes throw not_found.
|
|
16
|
+
*/
|
|
17
|
+
import { Prisma } from './prisma.js';
|
|
18
|
+
import { QARevertStoreError, } from '@derwinjs/sdk';
|
|
19
|
+
// ─── Factory ─────────────────────────────────────────────────────────────
|
|
20
|
+
export function createPrismaQARevertStore(config) {
|
|
21
|
+
const { prisma } = config;
|
|
22
|
+
return {
|
|
23
|
+
async listReverts(filter) {
|
|
24
|
+
validateProjectId(filter.projectId);
|
|
25
|
+
const where = { projectId: filter.projectId };
|
|
26
|
+
// Lifeline parity: default is un-restored only. Pass true to get full
|
|
27
|
+
// history including restored rows.
|
|
28
|
+
if (!filter.includeRestored) {
|
|
29
|
+
where.restoredAt = null;
|
|
30
|
+
}
|
|
31
|
+
const rows = await prisma.qARevert.findMany({
|
|
32
|
+
where,
|
|
33
|
+
orderBy: { revertedAt: 'desc' },
|
|
34
|
+
take: filter.limit,
|
|
35
|
+
});
|
|
36
|
+
return rows.map(mapRevert);
|
|
37
|
+
},
|
|
38
|
+
async createRevert(input) {
|
|
39
|
+
validateCreateInput(input);
|
|
40
|
+
let created;
|
|
41
|
+
try {
|
|
42
|
+
created = await prisma.qARevert.create({
|
|
43
|
+
data: {
|
|
44
|
+
projectId: input.projectId,
|
|
45
|
+
ticketId: input.ticketId,
|
|
46
|
+
revertedFromSha: input.revertedFromSha,
|
|
47
|
+
revertedToSha: input.revertedToSha ?? null,
|
|
48
|
+
revertedBy: input.revertedBy,
|
|
49
|
+
reason: input.reason,
|
|
50
|
+
qaRunId: input.qaRunId ?? null,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
catch (err) {
|
|
55
|
+
// P2003 surfaces as fk_violation — distinguish unknown projectId /
|
|
56
|
+
// ticketId / qaRunId from a generic DB error so the route handler
|
|
57
|
+
// returns 400 vs 500.
|
|
58
|
+
if (err instanceof Prisma.PrismaClientKnownRequestError && err.code === 'P2003') {
|
|
59
|
+
throw new QARevertStoreError('fk_violation', `QARevert FK violation: ${err.message}`, {
|
|
60
|
+
projectId: input.projectId,
|
|
61
|
+
ticketId: input.ticketId,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
return mapRevert(created);
|
|
67
|
+
},
|
|
68
|
+
async restoreRevert(ref, input) {
|
|
69
|
+
validateRestoreInput(input);
|
|
70
|
+
// Tenant-check first via findFirst (defense-in-depth).
|
|
71
|
+
const existing = await prisma.qARevert.findFirst({
|
|
72
|
+
where: { id: ref.id, projectId: ref.projectId },
|
|
73
|
+
});
|
|
74
|
+
if (existing === null) {
|
|
75
|
+
throw new QARevertStoreError('not_found', `QARevert ${ref.id} not found in project ${ref.projectId}`, { id: ref.id, projectId: ref.projectId });
|
|
76
|
+
}
|
|
77
|
+
// Idempotent re-restore: if already restored, write-through with the
|
|
78
|
+
// new values. The dashboard may fire the same PATCH on a refresh;
|
|
79
|
+
// we don't error on that.
|
|
80
|
+
const updated = await prisma.qARevert.update({
|
|
81
|
+
where: { id: ref.id },
|
|
82
|
+
data: {
|
|
83
|
+
restoredAt: existing.restoredAt ?? new Date(),
|
|
84
|
+
restoredBy: input.restoredBy,
|
|
85
|
+
restoredBySha: input.restoredBySha,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
return mapRevert(updated);
|
|
89
|
+
},
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
// ─── Validation ──────────────────────────────────────────────────────────
|
|
93
|
+
function validateProjectId(projectId) {
|
|
94
|
+
if (typeof projectId !== 'string' || projectId.length === 0) {
|
|
95
|
+
throw new QARevertStoreError('invalid_input', 'QARevertStore: projectId is required');
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
function validateCreateInput(input) {
|
|
99
|
+
validateProjectId(input.projectId);
|
|
100
|
+
if (typeof input.ticketId !== 'string' || input.ticketId.length === 0) {
|
|
101
|
+
throw new QARevertStoreError('invalid_input', 'QARevertStore.createRevert: ticketId is required');
|
|
102
|
+
}
|
|
103
|
+
if (typeof input.revertedFromSha !== 'string' || input.revertedFromSha.length === 0) {
|
|
104
|
+
throw new QARevertStoreError('invalid_input', 'QARevertStore.createRevert: revertedFromSha is required');
|
|
105
|
+
}
|
|
106
|
+
if (typeof input.revertedBy !== 'string' || input.revertedBy.length === 0) {
|
|
107
|
+
throw new QARevertStoreError('invalid_input', 'QARevertStore.createRevert: revertedBy is required');
|
|
108
|
+
}
|
|
109
|
+
if (typeof input.reason !== 'string' || input.reason.length === 0) {
|
|
110
|
+
throw new QARevertStoreError('invalid_input', 'QARevertStore.createRevert: reason is required');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function validateRestoreInput(input) {
|
|
114
|
+
if (typeof input.restoredBy !== 'string' || input.restoredBy.length === 0) {
|
|
115
|
+
throw new QARevertStoreError('invalid_input', 'QARevertStore.restoreRevert: restoredBy is required');
|
|
116
|
+
}
|
|
117
|
+
if (typeof input.restoredBySha !== 'string' || input.restoredBySha.length === 0) {
|
|
118
|
+
throw new QARevertStoreError('invalid_input', 'QARevertStore.restoreRevert: restoredBySha is required');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function mapRevert(row) {
|
|
122
|
+
return {
|
|
123
|
+
id: row.id,
|
|
124
|
+
projectId: row.projectId,
|
|
125
|
+
ticketId: row.ticketId,
|
|
126
|
+
revertedFromSha: row.revertedFromSha,
|
|
127
|
+
revertedToSha: row.revertedToSha,
|
|
128
|
+
revertedAt: row.revertedAt,
|
|
129
|
+
revertedBy: row.revertedBy,
|
|
130
|
+
reason: row.reason,
|
|
131
|
+
qaRunId: row.qaRunId,
|
|
132
|
+
restoredAt: row.restoredAt,
|
|
133
|
+
restoredBy: row.restoredBy,
|
|
134
|
+
restoredBySha: row.restoredBySha,
|
|
135
|
+
createdAt: row.createdAt,
|
|
136
|
+
updatedAt: row.updatedAt,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
//# sourceMappingURL=qa-revert-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-revert-store.js","sourceRoot":"","sources":["../src/qa-revert-store.ts"],"names":[],"mappings":"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"}
|