@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,46 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createPrismaFixPolicy — Prisma-backed implementation of the SDK FixPolicy
|
|
3
|
+
* contract introduced by QAP-012 / ADR-0014.
|
|
4
|
+
*
|
|
5
|
+
* QAP-013 (Sprint 2). Per Option A (operator-confirmed during scoping):
|
|
6
|
+
*
|
|
7
|
+
* Pure helpers (matchGlob / isPathAllowed / meetsMinSeverity / DEFAULT_AUTO_FIX_CONFIG)
|
|
8
|
+
* live in @derwinjs/core/src/auto-fix/policy-engine.ts. THIS file wires
|
|
9
|
+
* those helpers + Prisma reads + the QAFixAttemptStore daily-cap query
|
|
10
|
+
* into one factory.
|
|
11
|
+
*
|
|
12
|
+
* Sprint 8 extension note: the Policy schema (packages/db/prisma/schema.prisma)
|
|
13
|
+
* gains explicit auto-fix fields in QAP-080..089 — `enabled`, `mode`,
|
|
14
|
+
* `pathAllowlist`, `pathBlocklist`, `minSeverity`. Until then, this impl
|
|
15
|
+
* reads what the schema currently has (`dailyDispatchLimit` from a
|
|
16
|
+
* CONCURRENCY policy row) and falls back to DEFAULT_AUTO_FIX_CONFIG for the
|
|
17
|
+
* rest. The contract surface is stable; only the storage backing the
|
|
18
|
+
* `getConfig` impl will fill in over time.
|
|
19
|
+
*
|
|
20
|
+
* Tenant isolation: every read scopes by projectId; isAutoFixAllowed reaches
|
|
21
|
+
* into the QAFixAttemptStore (which itself is tenant-scoped per QAP-011).
|
|
22
|
+
* Pattern D from docs/testing.md applies — wrong-project lookups return
|
|
23
|
+
* sensible defaults (no enumeration leak), not an error or someone else's
|
|
24
|
+
* data.
|
|
25
|
+
*/
|
|
26
|
+
import type { PrismaClient } from './prisma.js';
|
|
27
|
+
import { type AutoFixConfig, type FixPolicy, type QAFixAttemptStore } from '@derwinjs/sdk';
|
|
28
|
+
export interface PrismaFixPolicyConfig {
|
|
29
|
+
/** Generated Prisma client. */
|
|
30
|
+
prisma: PrismaClient;
|
|
31
|
+
/**
|
|
32
|
+
* QAFixAttemptStore for the daily-cap query in isAutoFixAllowed. Same
|
|
33
|
+
* instance the dispatcher uses — sharing prevents two parallel store
|
|
34
|
+
* connections per project.
|
|
35
|
+
*/
|
|
36
|
+
attemptStore: QAFixAttemptStore;
|
|
37
|
+
/**
|
|
38
|
+
* Optional override for the platform-wide defaults. Tests use this to pin
|
|
39
|
+
* a specific config without touching the database. Production wiring
|
|
40
|
+
* should leave this undefined; the platform defaults from
|
|
41
|
+
* @derwinjs/core/policy-engine apply.
|
|
42
|
+
*/
|
|
43
|
+
defaultConfig?: AutoFixConfig;
|
|
44
|
+
}
|
|
45
|
+
export declare function createPrismaFixPolicy(config: PrismaFixPolicyConfig): FixPolicy;
|
|
46
|
+
//# sourceMappingURL=fix-policy.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-policy.d.ts","sourceRoot":"","sources":["../src/fix-policy.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EACL,KAAK,aAAa,EAGlB,KAAK,SAAS,EACd,KAAK,iBAAiB,EAEvB,MAAM,eAAe,CAAC;AASvB,MAAM,WAAW,qBAAqB;IACpC,+BAA+B;IAC/B,MAAM,EAAE,YAAY,CAAC;IACrB;;;;OAIG;IACH,YAAY,EAAE,iBAAiB,CAAC;IAChC;;;;;OAKG;IACH,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAID,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,SAAS,CA0H9E"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createPrismaFixPolicy — Prisma-backed implementation of the SDK FixPolicy
|
|
3
|
+
* contract introduced by QAP-012 / ADR-0014.
|
|
4
|
+
*
|
|
5
|
+
* QAP-013 (Sprint 2). Per Option A (operator-confirmed during scoping):
|
|
6
|
+
*
|
|
7
|
+
* Pure helpers (matchGlob / isPathAllowed / meetsMinSeverity / DEFAULT_AUTO_FIX_CONFIG)
|
|
8
|
+
* live in @derwinjs/core/src/auto-fix/policy-engine.ts. THIS file wires
|
|
9
|
+
* those helpers + Prisma reads + the QAFixAttemptStore daily-cap query
|
|
10
|
+
* into one factory.
|
|
11
|
+
*
|
|
12
|
+
* Sprint 8 extension note: the Policy schema (packages/db/prisma/schema.prisma)
|
|
13
|
+
* gains explicit auto-fix fields in QAP-080..089 — `enabled`, `mode`,
|
|
14
|
+
* `pathAllowlist`, `pathBlocklist`, `minSeverity`. Until then, this impl
|
|
15
|
+
* reads what the schema currently has (`dailyDispatchLimit` from a
|
|
16
|
+
* CONCURRENCY policy row) and falls back to DEFAULT_AUTO_FIX_CONFIG for the
|
|
17
|
+
* rest. The contract surface is stable; only the storage backing the
|
|
18
|
+
* `getConfig` impl will fill in over time.
|
|
19
|
+
*
|
|
20
|
+
* Tenant isolation: every read scopes by projectId; isAutoFixAllowed reaches
|
|
21
|
+
* into the QAFixAttemptStore (which itself is tenant-scoped per QAP-011).
|
|
22
|
+
* Pattern D from docs/testing.md applies — wrong-project lookups return
|
|
23
|
+
* sensible defaults (no enumeration leak), not an error or someone else's
|
|
24
|
+
* data.
|
|
25
|
+
*/
|
|
26
|
+
import { DEFAULT_AUTO_FIX_CONFIG, isPathAllowed as pureIsPathAllowed, meetsMinSeverity as pureMeetsMinSeverity, } from '@derwinjs/core';
|
|
27
|
+
// ─── Factory ─────────────────────────────────────────────────────────────
|
|
28
|
+
export function createPrismaFixPolicy(config) {
|
|
29
|
+
const { prisma, attemptStore } = config;
|
|
30
|
+
const defaults = config.defaultConfig ?? DEFAULT_AUTO_FIX_CONFIG;
|
|
31
|
+
return {
|
|
32
|
+
async getConfig(projectId) {
|
|
33
|
+
// Read the CONCURRENCY policy row if it exists. The Policy model
|
|
34
|
+
// (Sprint 1 / QAP-005) currently tracks `dailyDispatchLimit` which
|
|
35
|
+
// maps directly to `maxAttemptsPerDay`. The remaining auto-fix-specific
|
|
36
|
+
// fields land in Sprint 8's Policy expansion; for now they come from
|
|
37
|
+
// defaults.
|
|
38
|
+
//
|
|
39
|
+
// Tenant scope: where { projectId, policyType: 'CONCURRENCY' } —
|
|
40
|
+
// findFirst rather than findUnique because the schema doesn't
|
|
41
|
+
// enforce one-row-per-(project, type) uniqueness yet.
|
|
42
|
+
const concurrencyPolicy = await prisma.policy.findFirst({
|
|
43
|
+
where: { projectId, policyType: 'CONCURRENCY' },
|
|
44
|
+
});
|
|
45
|
+
return {
|
|
46
|
+
...defaults,
|
|
47
|
+
// Override only the field the schema currently models.
|
|
48
|
+
maxAttemptsPerDay: concurrencyPolicy?.dailyDispatchLimit ?? defaults.maxAttemptsPerDay,
|
|
49
|
+
};
|
|
50
|
+
},
|
|
51
|
+
async isAutoFixAllowed(projectId) {
|
|
52
|
+
const cfg = await this.getConfig(projectId);
|
|
53
|
+
// (1) Master enable gate.
|
|
54
|
+
if (!cfg.enabled) {
|
|
55
|
+
return {
|
|
56
|
+
allowed: false,
|
|
57
|
+
reason: `auto-fix disabled for project ${projectId}`,
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
// (2) Daily cap. maxAttemptsPerDay === 0 means "unlimited" (the SDK
|
|
61
|
+
// contract's note); skip the cap check in that case.
|
|
62
|
+
if (cfg.maxAttemptsPerDay > 0) {
|
|
63
|
+
const attemptsToday = await attemptStore.countAttemptsToday({ projectId });
|
|
64
|
+
if (attemptsToday >= cfg.maxAttemptsPerDay) {
|
|
65
|
+
return {
|
|
66
|
+
allowed: false,
|
|
67
|
+
reason: `daily cap reached (${String(attemptsToday)}/${String(cfg.maxAttemptsPerDay)})`,
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return { allowed: true };
|
|
72
|
+
},
|
|
73
|
+
meetsMinSeverity(severity, cfg) {
|
|
74
|
+
// Delegate to the pure helper from @derwinjs/core. Re-exported here
|
|
75
|
+
// through the FixPolicy interface so callers don't need both deps.
|
|
76
|
+
return pureMeetsMinSeverity(severity, cfg);
|
|
77
|
+
},
|
|
78
|
+
isPathAllowed(path, cfg) {
|
|
79
|
+
return pureIsPathAllowed(path, cfg);
|
|
80
|
+
},
|
|
81
|
+
/**
|
|
82
|
+
* Update the project's auto-fix policy. Writes through to the Policy row
|
|
83
|
+
* (CONCURRENCY policyType) for the project, creating it with sensible
|
|
84
|
+
* defaults + operator overrides if absent. The post-write config is
|
|
85
|
+
* returned via getConfig so callers see the same shape they'd see on the
|
|
86
|
+
* next read.
|
|
87
|
+
*
|
|
88
|
+
* Sprint 8 note: the Policy schema does not yet have an `enabled` column
|
|
89
|
+
* (lands in QAP-080..089). Until then, the `enabled` field on the input
|
|
90
|
+
* is type-checked and validated, but silently no-ops at the storage
|
|
91
|
+
* layer. The setConfig return value reflects the post-write state from
|
|
92
|
+
* getConfig, which sources `enabled` from defaults until the schema
|
|
93
|
+
* gains the column.
|
|
94
|
+
*/
|
|
95
|
+
async setConfig(projectId, input) {
|
|
96
|
+
validateUpdateInput(input);
|
|
97
|
+
// Read the current row (or null) to know whether we update or create.
|
|
98
|
+
const existing = await prisma.policy.findFirst({
|
|
99
|
+
where: { projectId, policyType: 'CONCURRENCY' },
|
|
100
|
+
});
|
|
101
|
+
if (existing) {
|
|
102
|
+
// Partial update — only fields the input supplies. `enabled` is
|
|
103
|
+
// accepted at the contract layer but no-ops here until the Sprint 8
|
|
104
|
+
// schema lands.
|
|
105
|
+
await prisma.policy.update({
|
|
106
|
+
where: { id: existing.id },
|
|
107
|
+
data: {
|
|
108
|
+
...(input.maxAttemptsPerDay !== undefined && {
|
|
109
|
+
dailyDispatchLimit: input.maxAttemptsPerDay,
|
|
110
|
+
}),
|
|
111
|
+
updatedBy: input.updatedBy,
|
|
112
|
+
},
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
// Create a new CONCURRENCY policy row with platform defaults +
|
|
117
|
+
// operator-supplied overrides where present.
|
|
118
|
+
await prisma.policy.create({
|
|
119
|
+
data: {
|
|
120
|
+
projectId,
|
|
121
|
+
policyType: 'CONCURRENCY',
|
|
122
|
+
pathRules: [],
|
|
123
|
+
classificationOverrides: {},
|
|
124
|
+
autoMergeTrustThreshold: 70,
|
|
125
|
+
autoMergeMediumThreshold: 85,
|
|
126
|
+
dailyDispatchLimit: input.maxAttemptsPerDay ?? defaults.maxAttemptsPerDay,
|
|
127
|
+
perTicketAttemptLimit: 1,
|
|
128
|
+
freezeWindowsCron: [],
|
|
129
|
+
escalationTriggers: {},
|
|
130
|
+
updatedBy: input.updatedBy,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// Return the post-write config via the existing getConfig path so
|
|
135
|
+
// callers see the same shape as the next read.
|
|
136
|
+
return this.getConfig(projectId);
|
|
137
|
+
},
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
// ─── Validation ───────────────────────────────────────────────────────────
|
|
141
|
+
/**
|
|
142
|
+
* Validate an AutoFixConfigUpdateInput. Throws plain Error with a
|
|
143
|
+
* recognizable 'FixPolicy.setConfig:' prefix — matching the existing
|
|
144
|
+
* FixPolicy convention of not defining a tagged error class. If FixPolicy
|
|
145
|
+
* gains store-style errors later (Sprint 8), this can be migrated.
|
|
146
|
+
*/
|
|
147
|
+
function validateUpdateInput(input) {
|
|
148
|
+
if (typeof input.updatedBy !== 'string' || input.updatedBy.trim() === '') {
|
|
149
|
+
throw new Error('FixPolicy.setConfig: updatedBy is required');
|
|
150
|
+
}
|
|
151
|
+
if (input.maxAttemptsPerDay !== undefined) {
|
|
152
|
+
if (typeof input.maxAttemptsPerDay !== 'number' ||
|
|
153
|
+
!Number.isInteger(input.maxAttemptsPerDay) ||
|
|
154
|
+
input.maxAttemptsPerDay < 0) {
|
|
155
|
+
throw new Error('FixPolicy.setConfig: maxAttemptsPerDay must be a non-negative integer');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (input.enabled !== undefined && typeof input.enabled !== 'boolean') {
|
|
159
|
+
throw new Error('FixPolicy.setConfig: enabled must be a boolean');
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=fix-policy.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fix-policy.js","sourceRoot":"","sources":["../src/fix-policy.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @derwinjs/db — Prisma schema + generated client + storage implementations.
|
|
3
|
+
*
|
|
4
|
+
* Schema lives at `packages/db/prisma/schema.prisma` (added in QAP-005).
|
|
5
|
+
* Models per technical spec §3 plus Sprint 2 additions (crossLinkRefs +
|
|
6
|
+
* dashboardUrl on QATicket per ADR-0008).
|
|
7
|
+
*
|
|
8
|
+
* Project-namespaced — every project-scoped table has projectId FK to Project.
|
|
9
|
+
* pgvector for RAG corpus embeddings (technical spec §14 Q2 / ADR-0007 — switch
|
|
10
|
+
* lands at Sprint 4 / QAP-040..047 with Profile-v0 ingestion).
|
|
11
|
+
*
|
|
12
|
+
* Storage implementations exposed here implement contracts defined in
|
|
13
|
+
* @derwinjs/sdk. configureQAPlatform wires them in based on the consumer's
|
|
14
|
+
* configuration. Today: QATicketStore (QAP-018B). More land per sprint.
|
|
15
|
+
*/
|
|
16
|
+
export declare const PACKAGE_NAME: "@derwinjs/db";
|
|
17
|
+
export { createPrismaQATicketStore, type PrismaQATicketStoreConfig } from './qa-ticket-store.js';
|
|
18
|
+
export { createPrismaQAFixAttemptStore, type PrismaQAFixAttemptStoreConfig, } from './qa-fix-attempt-store.js';
|
|
19
|
+
export { createPrismaQARunStore, type PrismaQARunStoreConfig } from './qa-run-store.js';
|
|
20
|
+
export { createPrismaQAPatternStore, type PrismaQAPatternStoreConfig } from './qa-pattern-store.js';
|
|
21
|
+
export { createPrismaQARevertStore, type PrismaQARevertStoreConfig } from './qa-revert-store.js';
|
|
22
|
+
export { createPrismaQAUniformityStore, type PrismaQAUniformityStoreConfig, } from './qa-uniformity-store.js';
|
|
23
|
+
export { createPrismaAgentFindingsIngestor, type PrismaAgentFindingsIngestorConfig, } from './agent-findings-ingestor.js';
|
|
24
|
+
export { createPrismaLearningHealthReporter, type PrismaLearningHealthReporterConfig, } from './learning-health-reporter.js';
|
|
25
|
+
export { createPrismaCoverageGapReporter, type PrismaCoverageGapReporterConfig, } from './coverage-gap-reporter.js';
|
|
26
|
+
export * from './prisma.js';
|
|
27
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,eAAO,MAAM,YAAY,EAAG,cAAuB,CAAC;AAIpD,OAAO,EAAE,yBAAyB,EAAE,KAAK,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjG,OAAO,EACL,6BAA6B,EAC7B,KAAK,6BAA6B,GACnC,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,sBAAsB,EAAE,KAAK,sBAAsB,EAAE,MAAM,mBAAmB,CAAC;AACxF,OAAO,EAAE,0BAA0B,EAAE,KAAK,0BAA0B,EAAE,MAAM,uBAAuB,CAAC;AACpG,OAAO,EAAE,yBAAyB,EAAE,KAAK,yBAAyB,EAAE,MAAM,sBAAsB,CAAC;AACjG,OAAO,EACL,6BAA6B,EAC7B,KAAK,6BAA6B,GACnC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,iCAAiC,EACjC,KAAK,iCAAiC,GACvC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,kCAAkC,EAClC,KAAK,kCAAkC,GACxC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,+BAA+B,EAC/B,KAAK,+BAA+B,GACrC,MAAM,4BAA4B,CAAC;AA4BpC,cAAc,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @derwinjs/db — Prisma schema + generated client + storage implementations.
|
|
3
|
+
*
|
|
4
|
+
* Schema lives at `packages/db/prisma/schema.prisma` (added in QAP-005).
|
|
5
|
+
* Models per technical spec §3 plus Sprint 2 additions (crossLinkRefs +
|
|
6
|
+
* dashboardUrl on QATicket per ADR-0008).
|
|
7
|
+
*
|
|
8
|
+
* Project-namespaced — every project-scoped table has projectId FK to Project.
|
|
9
|
+
* pgvector for RAG corpus embeddings (technical spec §14 Q2 / ADR-0007 — switch
|
|
10
|
+
* lands at Sprint 4 / QAP-040..047 with Profile-v0 ingestion).
|
|
11
|
+
*
|
|
12
|
+
* Storage implementations exposed here implement contracts defined in
|
|
13
|
+
* @derwinjs/sdk. configureQAPlatform wires them in based on the consumer's
|
|
14
|
+
* configuration. Today: QATicketStore (QAP-018B). More land per sprint.
|
|
15
|
+
*/
|
|
16
|
+
export const PACKAGE_NAME = '@derwinjs/db';
|
|
17
|
+
// ─── Storage implementations ─────────────────────────────────────────────
|
|
18
|
+
export { createPrismaQATicketStore } from './qa-ticket-store.js';
|
|
19
|
+
export { createPrismaQAFixAttemptStore, } from './qa-fix-attempt-store.js';
|
|
20
|
+
export { createPrismaQARunStore } from './qa-run-store.js';
|
|
21
|
+
export { createPrismaQAPatternStore } from './qa-pattern-store.js';
|
|
22
|
+
export { createPrismaQARevertStore } from './qa-revert-store.js';
|
|
23
|
+
export { createPrismaQAUniformityStore, } from './qa-uniformity-store.js';
|
|
24
|
+
export { createPrismaAgentFindingsIngestor, } from './agent-findings-ingestor.js';
|
|
25
|
+
export { createPrismaLearningHealthReporter, } from './learning-health-reporter.js';
|
|
26
|
+
export { createPrismaCoverageGapReporter, } from './coverage-gap-reporter.js';
|
|
27
|
+
// ─── Prisma client re-export ─────────────────────────────────────────────
|
|
28
|
+
//
|
|
29
|
+
// @derwinjs/db ships its OWN generated Prisma client (output: prisma-client/
|
|
30
|
+
// per the schema's generator block). Consumers MUST import PrismaClient
|
|
31
|
+
// from here, NOT from @prisma/client — see src/prisma.ts for the full
|
|
32
|
+
// rationale. Each Derwin consumer (Lifeline, Side Piece, Bolt, ...) has
|
|
33
|
+
// its own @prisma/client generated against its own schema; @derwinjs/db
|
|
34
|
+
// can't piggyback on it without breaking types.
|
|
35
|
+
//
|
|
36
|
+
// What's re-exported (full surface from the generated client):
|
|
37
|
+
// - PrismaClient — instantiate with { datasources: { db: { url } }} and
|
|
38
|
+
// pass to the store factories above.
|
|
39
|
+
// - Prisma — namespace containing PrismaClientKnownRequestError, validators,
|
|
40
|
+
// input/output types per model, etc.
|
|
41
|
+
// - All enums from the schema: ProjectType, ProjectMode, RunTrigger,
|
|
42
|
+
// RunStatus, AttemptStatus, TicketStatus, ReviewBucket, RiskTier,
|
|
43
|
+
// Severity, SurfaceCategory, QAPatternStatus, QAFailureClass,
|
|
44
|
+
// QAUniformityStatus, etc.
|
|
45
|
+
//
|
|
46
|
+
// Consumer pattern:
|
|
47
|
+
// import { PrismaClient, ProjectType } from '@derwinjs/db';
|
|
48
|
+
// const derwinPrisma = new PrismaClient({
|
|
49
|
+
// datasources: { db: { url: process.env.DERWIN_DATABASE_URL } },
|
|
50
|
+
// });
|
|
51
|
+
// await derwinPrisma.project.upsert({ ... });
|
|
52
|
+
export * from './prisma.js';
|
|
53
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,CAAC,MAAM,YAAY,GAAG,cAAuB,CAAC;AAEpD,4EAA4E;AAE5E,OAAO,EAAE,yBAAyB,EAAkC,MAAM,sBAAsB,CAAC;AACjG,OAAO,EACL,6BAA6B,GAE9B,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAE,sBAAsB,EAA+B,MAAM,mBAAmB,CAAC;AACxF,OAAO,EAAE,0BAA0B,EAAmC,MAAM,uBAAuB,CAAC;AACpG,OAAO,EAAE,yBAAyB,EAAkC,MAAM,sBAAsB,CAAC;AACjG,OAAO,EACL,6BAA6B,GAE9B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EACL,iCAAiC,GAElC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EACL,kCAAkC,GAEnC,MAAM,+BAA+B,CAAC;AACvC,OAAO,EACL,+BAA+B,GAEhC,MAAM,4BAA4B,CAAC;AAEpC,4EAA4E;AAC5E,EAAE;AACF,6EAA6E;AAC7E,wEAAwE;AACxE,sEAAsE;AACtE,wEAAwE;AACxE,wEAAwE;AACxE,gDAAgD;AAChD,EAAE;AACF,+DAA+D;AAC/D,0EAA0E;AAC1E,yCAAyC;AACzC,+EAA+E;AAC/E,yCAAyC;AACzC,uEAAuE;AACvE,sEAAsE;AACtE,kEAAkE;AAClE,+BAA+B;AAC/B,EAAE;AACF,oBAAoB;AACpB,8DAA8D;AAC9D,4CAA4C;AAC5C,qEAAqE;AACrE,QAAQ;AACR,gDAAgD;AAEhD,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LearningHealthReporter — Prisma-backed aggregate reader over QAFixAttempt
|
|
3
|
+
* + QAPattern for the dashboard's "Learning Health" widget.
|
|
4
|
+
*
|
|
5
|
+
* QAP-019F (Sprint 2 Group D-3, Commit 3). Implements the
|
|
6
|
+
* LearningHealthReporter contract from @derwinjs/sdk. Migrated from
|
|
7
|
+
* Lifeline's GET /api/qa/learning-health route. No new schema — pure
|
|
8
|
+
* aggregate read.
|
|
9
|
+
*
|
|
10
|
+
* Response field names + null semantics mirror Lifeline so the QAP-019G
|
|
11
|
+
* shim is a 1:1 mapping. Five metrics:
|
|
12
|
+
* - mergedWithoutEditsPercent (humanEditLines === 0 share of merges)
|
|
13
|
+
* - regressionRatePercent (regressionDetected share of merges)
|
|
14
|
+
* - topImprovingClassifications (top-5 QAPatterns by fixSuccessScore,
|
|
15
|
+
* filtered to fixOutcomeSampleSize >= 3)
|
|
16
|
+
* - patternsWithSamples (count of QAPatterns with fixOutcomeSampleSize > 0)
|
|
17
|
+
* - dryRunOnlyFailureRatePercent (PRE_VERIFY_PASSED share of dispatches)
|
|
18
|
+
*
|
|
19
|
+
* **Schema mapping under Option β** (Lifeline → Derwin):
|
|
20
|
+
* - Lifeline `status: 'MERGED'` → Derwin `dispatchStatus IN
|
|
21
|
+
* ('AUTO_MERGED', 'HUMAN_MERGED')` (Lifeline's single MERGED is split
|
|
22
|
+
* in Derwin's AttemptStatus enum).
|
|
23
|
+
* - Lifeline `status: 'DRY_RUN_LOGGED'` → Derwin
|
|
24
|
+
* `dispatchStatus = 'PRE_VERIFY_PASSED'` (the closest equivalent —
|
|
25
|
+
* pre-verify ran successfully but operator never escalated to PR).
|
|
26
|
+
* - Lifeline `outcomeRecordedAt` → Derwin `mergedAt` (timestamp on
|
|
27
|
+
* merged attempts).
|
|
28
|
+
* - Lifeline `QAPatternInsight` → Derwin `QAPattern` (D-2 rename).
|
|
29
|
+
*/
|
|
30
|
+
import type { PrismaClient } from './prisma.js';
|
|
31
|
+
import { type LearningHealthReporter } from '@derwinjs/sdk';
|
|
32
|
+
export interface PrismaLearningHealthReporterConfig {
|
|
33
|
+
/** Generated Prisma client. Pass an instance per process. */
|
|
34
|
+
prisma: PrismaClient;
|
|
35
|
+
}
|
|
36
|
+
export declare function createPrismaLearningHealthReporter(config: PrismaLearningHealthReporterConfig): LearningHealthReporter;
|
|
37
|
+
//# sourceMappingURL=learning-health-reporter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learning-health-reporter.d.ts","sourceRoot":"","sources":["../src/learning-health-reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAKL,KAAK,sBAAsB,EAC5B,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,kCAAkC;IACjD,6DAA6D;IAC7D,MAAM,EAAE,YAAY,CAAC;CACtB;AAUD,wBAAgB,kCAAkC,CAChD,MAAM,EAAE,kCAAkC,GACzC,sBAAsB,CAiGxB"}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LearningHealthReporter — Prisma-backed aggregate reader over QAFixAttempt
|
|
3
|
+
* + QAPattern for the dashboard's "Learning Health" widget.
|
|
4
|
+
*
|
|
5
|
+
* QAP-019F (Sprint 2 Group D-3, Commit 3). Implements the
|
|
6
|
+
* LearningHealthReporter contract from @derwinjs/sdk. Migrated from
|
|
7
|
+
* Lifeline's GET /api/qa/learning-health route. No new schema — pure
|
|
8
|
+
* aggregate read.
|
|
9
|
+
*
|
|
10
|
+
* Response field names + null semantics mirror Lifeline so the QAP-019G
|
|
11
|
+
* shim is a 1:1 mapping. Five metrics:
|
|
12
|
+
* - mergedWithoutEditsPercent (humanEditLines === 0 share of merges)
|
|
13
|
+
* - regressionRatePercent (regressionDetected share of merges)
|
|
14
|
+
* - topImprovingClassifications (top-5 QAPatterns by fixSuccessScore,
|
|
15
|
+
* filtered to fixOutcomeSampleSize >= 3)
|
|
16
|
+
* - patternsWithSamples (count of QAPatterns with fixOutcomeSampleSize > 0)
|
|
17
|
+
* - dryRunOnlyFailureRatePercent (PRE_VERIFY_PASSED share of dispatches)
|
|
18
|
+
*
|
|
19
|
+
* **Schema mapping under Option β** (Lifeline → Derwin):
|
|
20
|
+
* - Lifeline `status: 'MERGED'` → Derwin `dispatchStatus IN
|
|
21
|
+
* ('AUTO_MERGED', 'HUMAN_MERGED')` (Lifeline's single MERGED is split
|
|
22
|
+
* in Derwin's AttemptStatus enum).
|
|
23
|
+
* - Lifeline `status: 'DRY_RUN_LOGGED'` → Derwin
|
|
24
|
+
* `dispatchStatus = 'PRE_VERIFY_PASSED'` (the closest equivalent —
|
|
25
|
+
* pre-verify ran successfully but operator never escalated to PR).
|
|
26
|
+
* - Lifeline `outcomeRecordedAt` → Derwin `mergedAt` (timestamp on
|
|
27
|
+
* merged attempts).
|
|
28
|
+
* - Lifeline `QAPatternInsight` → Derwin `QAPattern` (D-2 rename).
|
|
29
|
+
*/
|
|
30
|
+
import { LearningHealthReporterError, } from '@derwinjs/sdk';
|
|
31
|
+
// ─── Constants (Lifeline parity) ─────────────────────────────────────────
|
|
32
|
+
const DEFAULT_WINDOW_DAYS = 30;
|
|
33
|
+
const TOP_PATTERNS_LIMIT = 5;
|
|
34
|
+
const MIN_SAMPLE_SIZE = 3; // pattern threshold below which trend is too noisy
|
|
35
|
+
// ─── Factory ─────────────────────────────────────────────────────────────
|
|
36
|
+
export function createPrismaLearningHealthReporter(config) {
|
|
37
|
+
const { prisma } = config;
|
|
38
|
+
return {
|
|
39
|
+
async getLearningHealth(filter) {
|
|
40
|
+
validateFilter(filter);
|
|
41
|
+
const windowDays = filter.windowDays ?? DEFAULT_WINDOW_DAYS;
|
|
42
|
+
const windowStart = new Date(Date.now() - windowDays * 24 * 60 * 60 * 1000);
|
|
43
|
+
// ─── 1. Merged-attempt sample (clean-merge + regression rates) ────
|
|
44
|
+
const mergedAttempts = await prisma.qAFixAttempt.findMany({
|
|
45
|
+
where: {
|
|
46
|
+
projectId: filter.projectId,
|
|
47
|
+
dispatchStatus: { in: ['AUTO_MERGED', 'HUMAN_MERGED'] },
|
|
48
|
+
mergedAt: { gte: windowStart },
|
|
49
|
+
},
|
|
50
|
+
select: { humanEditLines: true, regressionDetected: true },
|
|
51
|
+
});
|
|
52
|
+
const sampleSize = mergedAttempts.length;
|
|
53
|
+
let mergedWithoutEditsPercent = null;
|
|
54
|
+
let regressionRatePercent = null;
|
|
55
|
+
if (sampleSize > 0) {
|
|
56
|
+
const cleanMerges = mergedAttempts.filter((a) => a.humanEditLines === 0).length;
|
|
57
|
+
mergedWithoutEditsPercent = Math.round((cleanMerges / sampleSize) * 100);
|
|
58
|
+
const regressions = mergedAttempts.filter((a) => a.regressionDetected).length;
|
|
59
|
+
regressionRatePercent = Math.round((regressions / sampleSize) * 100);
|
|
60
|
+
}
|
|
61
|
+
// ─── 2. Top-improving classifications ──────────────────────────────
|
|
62
|
+
const patterns = await prisma.qAPattern.findMany({
|
|
63
|
+
where: {
|
|
64
|
+
projectId: filter.projectId,
|
|
65
|
+
fixOutcomeSampleSize: { gte: MIN_SAMPLE_SIZE },
|
|
66
|
+
fixSuccessScore: { not: null },
|
|
67
|
+
},
|
|
68
|
+
orderBy: { fixSuccessScore: 'desc' },
|
|
69
|
+
take: TOP_PATTERNS_LIMIT,
|
|
70
|
+
select: {
|
|
71
|
+
classification: true,
|
|
72
|
+
fixSuccessScore: true,
|
|
73
|
+
fixOutcomeSampleSize: true,
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
const topImprovingClassifications = patterns.map((p) => ({
|
|
77
|
+
classification: p.classification,
|
|
78
|
+
// Filter ensures fixSuccessScore !== null, but TS narrows it to
|
|
79
|
+
// number | null because Prisma's type widens. Coerce defensively.
|
|
80
|
+
score: p.fixSuccessScore ?? 0,
|
|
81
|
+
sampleSize: p.fixOutcomeSampleSize,
|
|
82
|
+
}));
|
|
83
|
+
// ─── 3. Patterns with samples (transparency metric) ────────────────
|
|
84
|
+
const patternsWithSamples = await prisma.qAPattern.count({
|
|
85
|
+
where: {
|
|
86
|
+
projectId: filter.projectId,
|
|
87
|
+
fixOutcomeSampleSize: { gt: 0 },
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
// ─── 4. Dry-run-only failure rate ──────────────────────────────────
|
|
91
|
+
//
|
|
92
|
+
// Counts in the same time window. Lifeline parity: numerator counts
|
|
93
|
+
// attempts stuck at PRE_VERIFY_PASSED (Derwin's closest analog to
|
|
94
|
+
// DRY_RUN_LOGGED); denominator counts those plus everything that
|
|
95
|
+
// moved past — PR_OPENED + the merged + REJECTED final states.
|
|
96
|
+
const dryRunCount = await prisma.qAFixAttempt.count({
|
|
97
|
+
where: {
|
|
98
|
+
projectId: filter.projectId,
|
|
99
|
+
dispatchStatus: 'PRE_VERIFY_PASSED',
|
|
100
|
+
attemptedAt: { gte: windowStart },
|
|
101
|
+
},
|
|
102
|
+
});
|
|
103
|
+
const prCount = await prisma.qAFixAttempt.count({
|
|
104
|
+
where: {
|
|
105
|
+
projectId: filter.projectId,
|
|
106
|
+
dispatchStatus: { in: ['PR_OPENED', 'AUTO_MERGED', 'HUMAN_MERGED', 'REJECTED'] },
|
|
107
|
+
attemptedAt: { gte: windowStart },
|
|
108
|
+
},
|
|
109
|
+
});
|
|
110
|
+
const totalDispatches = dryRunCount + prCount;
|
|
111
|
+
let dryRunOnlyFailureRatePercent = null;
|
|
112
|
+
if (totalDispatches > 0) {
|
|
113
|
+
dryRunOnlyFailureRatePercent = Math.round((dryRunCount / totalDispatches) * 100);
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
windowDays,
|
|
117
|
+
sampleSize,
|
|
118
|
+
mergedWithoutEditsPercent,
|
|
119
|
+
regressionRatePercent,
|
|
120
|
+
topImprovingClassifications,
|
|
121
|
+
patternsWithSamples,
|
|
122
|
+
dryRunOnlyFailureRatePercent,
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
// ─── Validation ──────────────────────────────────────────────────────────
|
|
128
|
+
function validateFilter(filter) {
|
|
129
|
+
if (typeof filter.projectId !== 'string' || filter.projectId.length === 0) {
|
|
130
|
+
throw new LearningHealthReporterError('invalid_input', 'LearningHealthReporter: projectId is required');
|
|
131
|
+
}
|
|
132
|
+
if (filter.windowDays !== undefined) {
|
|
133
|
+
if (typeof filter.windowDays !== 'number' || !Number.isFinite(filter.windowDays)) {
|
|
134
|
+
throw new LearningHealthReporterError('invalid_input', 'LearningHealthReporter: windowDays must be a finite number');
|
|
135
|
+
}
|
|
136
|
+
if (filter.windowDays < 1) {
|
|
137
|
+
throw new LearningHealthReporterError('invalid_input', 'LearningHealthReporter: windowDays must be >= 1');
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=learning-health-reporter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learning-health-reporter.js","sourceRoot":"","sources":["../src/learning-health-reporter.ts"],"names":[],"mappings":"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"}
|
package/dist/prisma.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @derwinjs/db internal re-export of the generated Prisma client.
|
|
3
|
+
*
|
|
4
|
+
* The Prisma generator emits to `packages/db/prisma-client/` (see
|
|
5
|
+
* `prisma/schema.prisma`'s `generator client { output = "../prisma-client" }`
|
|
6
|
+
* directive). This file is the single internal entrypoint for that
|
|
7
|
+
* generated module — every store factory inside @derwinjs/db imports
|
|
8
|
+
* `Prisma` and `PrismaClient` from here instead of from `@prisma/client`.
|
|
9
|
+
*
|
|
10
|
+
* **Why a Derwin-owned PrismaClient (not @prisma/client)?**
|
|
11
|
+
*
|
|
12
|
+
* Each Derwin consumer (Lifeline, Side Piece, Bolt, Financial Analyzer,
|
|
13
|
+
* Shackbox, Home Remodeler, Patient Communications Hub) has its own
|
|
14
|
+
* Prisma schema and its own generated client at `node_modules/@prisma/client`
|
|
15
|
+
* for ITS OWN tables. If @derwinjs/db imported `PrismaClient` from
|
|
16
|
+
* `@prisma/client`, consumers' tsc would resolve that to the consumer's
|
|
17
|
+
* client — which doesn't have Derwin's models (qAUniformity, qARun, etc).
|
|
18
|
+
* Type-check would fail; runtime would fail.
|
|
19
|
+
*
|
|
20
|
+
* By owning the generated client inside @derwinjs/db, we get a Derwin-shaped
|
|
21
|
+
* `PrismaClient` that ships in the published tarball alongside the rest
|
|
22
|
+
* of `dist/`. Consumers can safely instantiate `new PrismaClient(...)`
|
|
23
|
+
* pointed at `DERWIN_DATABASE_URL` and pass it to Derwin's store
|
|
24
|
+
* factories without type collisions.
|
|
25
|
+
*
|
|
26
|
+
* Consumers should import `PrismaClient` from `@derwinjs/db` (which
|
|
27
|
+
* re-exports it from this module) — never from `@prisma/client` for
|
|
28
|
+
* Derwin queries.
|
|
29
|
+
*/
|
|
30
|
+
export * from '../prisma-client/index.js';
|
|
31
|
+
//# sourceMappingURL=prisma.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../src/prisma.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,cAAc,2BAA2B,CAAC"}
|
package/dist/prisma.js
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @derwinjs/db internal re-export of the generated Prisma client.
|
|
3
|
+
*
|
|
4
|
+
* The Prisma generator emits to `packages/db/prisma-client/` (see
|
|
5
|
+
* `prisma/schema.prisma`'s `generator client { output = "../prisma-client" }`
|
|
6
|
+
* directive). This file is the single internal entrypoint for that
|
|
7
|
+
* generated module — every store factory inside @derwinjs/db imports
|
|
8
|
+
* `Prisma` and `PrismaClient` from here instead of from `@prisma/client`.
|
|
9
|
+
*
|
|
10
|
+
* **Why a Derwin-owned PrismaClient (not @prisma/client)?**
|
|
11
|
+
*
|
|
12
|
+
* Each Derwin consumer (Lifeline, Side Piece, Bolt, Financial Analyzer,
|
|
13
|
+
* Shackbox, Home Remodeler, Patient Communications Hub) has its own
|
|
14
|
+
* Prisma schema and its own generated client at `node_modules/@prisma/client`
|
|
15
|
+
* for ITS OWN tables. If @derwinjs/db imported `PrismaClient` from
|
|
16
|
+
* `@prisma/client`, consumers' tsc would resolve that to the consumer's
|
|
17
|
+
* client — which doesn't have Derwin's models (qAUniformity, qARun, etc).
|
|
18
|
+
* Type-check would fail; runtime would fail.
|
|
19
|
+
*
|
|
20
|
+
* By owning the generated client inside @derwinjs/db, we get a Derwin-shaped
|
|
21
|
+
* `PrismaClient` that ships in the published tarball alongside the rest
|
|
22
|
+
* of `dist/`. Consumers can safely instantiate `new PrismaClient(...)`
|
|
23
|
+
* pointed at `DERWIN_DATABASE_URL` and pass it to Derwin's store
|
|
24
|
+
* factories without type collisions.
|
|
25
|
+
*
|
|
26
|
+
* Consumers should import `PrismaClient` from `@derwinjs/db` (which
|
|
27
|
+
* re-exports it from this module) — never from `@prisma/client` for
|
|
28
|
+
* Derwin queries.
|
|
29
|
+
*/
|
|
30
|
+
export * from '../prisma-client/index.js';
|
|
31
|
+
//# sourceMappingURL=prisma.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.js","sourceRoot":"","sources":["../src/prisma.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AAEH,cAAc,2BAA2B,CAAC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
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 { type PrismaClient } from './prisma.js';
|
|
22
|
+
import { type QAFixAttemptStore } from '@derwinjs/sdk';
|
|
23
|
+
export interface PrismaQAFixAttemptStoreConfig {
|
|
24
|
+
/** Generated Prisma client. Pass an instance per process. */
|
|
25
|
+
prisma: PrismaClient;
|
|
26
|
+
}
|
|
27
|
+
export declare function createPrismaQAFixAttemptStore(config: PrismaQAFixAttemptStoreConfig): QAFixAttemptStore;
|
|
28
|
+
//# sourceMappingURL=qa-fix-attempt-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"qa-fix-attempt-store.d.ts","sourceRoot":"","sources":["../src/qa-fix-attempt-store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG;AAEH,OAAO,EAAU,KAAK,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAOL,KAAK,iBAAiB,EACvB,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,6BAA6B;IAC5C,6DAA6D;IAC7D,MAAM,EAAE,YAAY,CAAC;CACtB;AAID,wBAAgB,6BAA6B,CAC3C,MAAM,EAAE,6BAA6B,GACpC,iBAAiB,CA0MnB"}
|