@derwinjs/db 0.7.0 → 0.8.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/dist/budget-gate.d.ts +43 -0
- package/dist/budget-gate.d.ts.map +1 -0
- package/dist/budget-gate.js +110 -0
- package/dist/budget-gate.js.map +1 -0
- package/dist/classification-override-store.d.ts +24 -0
- package/dist/classification-override-store.d.ts.map +1 -0
- package/dist/classification-override-store.js +131 -0
- package/dist/classification-override-store.js.map +1 -0
- package/dist/freeze-window-evaluator.d.ts +62 -0
- package/dist/freeze-window-evaluator.d.ts.map +1 -0
- package/dist/freeze-window-evaluator.js +236 -0
- package/dist/freeze-window-evaluator.js.map +1 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/kill-switch-evaluator.d.ts +68 -0
- package/dist/kill-switch-evaluator.d.ts.map +1 -0
- package/dist/kill-switch-evaluator.js +389 -0
- package/dist/kill-switch-evaluator.js.map +1 -0
- package/dist/path-tier-resolver.d.ts +47 -0
- package/dist/path-tier-resolver.d.ts.map +1 -0
- package/dist/path-tier-resolver.js +177 -0
- package/dist/path-tier-resolver.js.map +1 -0
- package/dist/trust-threshold-config-store.d.ts +20 -0
- package/dist/trust-threshold-config-store.d.ts.map +1 -0
- package/dist/trust-threshold-config-store.js +88 -0
- package/dist/trust-threshold-config-store.js.map +1 -0
- package/package.json +3 -3
- package/prisma/migrations/20260507120000_sprint8_policy_governance/migration.sql +58 -0
- package/prisma/migrations/20260507120100_sprint8_phase2_thresholds_and_freeze/migration.sql +33 -0
- package/prisma/migrations/20260507120200_sprint8_phase3_budget_cap/migration.sql +21 -0
- package/prisma/migrations/20260507120300_sprint8_phase4_kill_switches/migration.sql +74 -0
- package/prisma/schema.prisma +183 -11
- package/prisma-client/edge.js +96 -19
- package/prisma-client/index-browser.js +93 -16
- package/prisma-client/index.d.ts +10997 -3426
- package/prisma-client/index.js +96 -19
- package/prisma-client/package.json +1 -1
- package/prisma-client/schema.prisma +183 -11
- package/prisma-client/wasm.js +93 -16
package/dist/index.d.ts
CHANGED
|
@@ -25,5 +25,11 @@ export { createPrismaLearningHealthReporter, type PrismaLearningHealthReporterCo
|
|
|
25
25
|
export { createPrismaCoverageGapReporter, type PrismaCoverageGapReporterConfig, } from './coverage-gap-reporter.js';
|
|
26
26
|
export { createPrismaRegressionDetector, type PrismaRegressionDetectorConfig, } from './regression-detector.js';
|
|
27
27
|
export { createPrismaDriftDetector, type PrismaDriftDetectorConfig } from './drift-detector.js';
|
|
28
|
+
export { createPrismaPathTierResolver, matchGlob, type PrismaPathTierResolverConfig, } from './path-tier-resolver.js';
|
|
29
|
+
export { createPrismaClassificationOverrideStore, type PrismaClassificationOverrideStoreConfig, } from './classification-override-store.js';
|
|
30
|
+
export { createPrismaTrustThresholdConfigStore, type PrismaTrustThresholdConfigStoreConfig, } from './trust-threshold-config-store.js';
|
|
31
|
+
export { createPrismaFreezeWindowEvaluator, parseCron, mostRecentFire, type PrismaFreezeWindowEvaluatorConfig, } from './freeze-window-evaluator.js';
|
|
32
|
+
export { createPrismaBudgetGate, type PrismaBudgetGateConfig } from './budget-gate.js';
|
|
33
|
+
export { createPrismaKillSwitchEvaluator, DEFAULT_KILL_SWITCH_THRESHOLDS, type PrismaKillSwitchEvaluatorConfig, } from './kill-switch-evaluator.js';
|
|
28
34
|
export * from './prisma.js';
|
|
29
35
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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;AACpC,OAAO,EACL,8BAA8B,EAC9B,KAAK,8BAA8B,GACpC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAE,KAAK,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;
|
|
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;AACpC,OAAO,EACL,8BAA8B,EAC9B,KAAK,8BAA8B,GACpC,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAE,KAAK,yBAAyB,EAAE,MAAM,qBAAqB,CAAC;AAChG,OAAO,EACL,4BAA4B,EAC5B,SAAS,EACT,KAAK,4BAA4B,GAClC,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,uCAAuC,EACvC,KAAK,uCAAuC,GAC7C,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,qCAAqC,EACrC,KAAK,qCAAqC,GAC3C,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,iCAAiC,EACjC,SAAS,EACT,cAAc,EACd,KAAK,iCAAiC,GACvC,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAAE,KAAK,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AACvF,OAAO,EACL,+BAA+B,EAC/B,8BAA8B,EAC9B,KAAK,+BAA+B,GACrC,MAAM,4BAA4B,CAAC;AA4BpC,cAAc,aAAa,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -26,6 +26,12 @@ export { createPrismaLearningHealthReporter, } from './learning-health-reporter.
|
|
|
26
26
|
export { createPrismaCoverageGapReporter, } from './coverage-gap-reporter.js';
|
|
27
27
|
export { createPrismaRegressionDetector, } from './regression-detector.js';
|
|
28
28
|
export { createPrismaDriftDetector } from './drift-detector.js';
|
|
29
|
+
export { createPrismaPathTierResolver, matchGlob, } from './path-tier-resolver.js';
|
|
30
|
+
export { createPrismaClassificationOverrideStore, } from './classification-override-store.js';
|
|
31
|
+
export { createPrismaTrustThresholdConfigStore, } from './trust-threshold-config-store.js';
|
|
32
|
+
export { createPrismaFreezeWindowEvaluator, parseCron, mostRecentFire, } from './freeze-window-evaluator.js';
|
|
33
|
+
export { createPrismaBudgetGate } from './budget-gate.js';
|
|
34
|
+
export { createPrismaKillSwitchEvaluator, DEFAULT_KILL_SWITCH_THRESHOLDS, } from './kill-switch-evaluator.js';
|
|
29
35
|
// ─── Prisma client re-export ─────────────────────────────────────────────
|
|
30
36
|
//
|
|
31
37
|
// @derwinjs/db ships its OWN generated Prisma client (output: prisma-client/
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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;AACpC,OAAO,EACL,8BAA8B,GAE/B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAkC,MAAM,qBAAqB,CAAC;
|
|
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;AACpC,OAAO,EACL,8BAA8B,GAE/B,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAkC,MAAM,qBAAqB,CAAC;AAChG,OAAO,EACL,4BAA4B,EAC5B,SAAS,GAEV,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACL,uCAAuC,GAExC,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EACL,qCAAqC,GAEtC,MAAM,mCAAmC,CAAC;AAC3C,OAAO,EACL,iCAAiC,EACjC,SAAS,EACT,cAAc,GAEf,MAAM,8BAA8B,CAAC;AACtC,OAAO,EAAE,sBAAsB,EAA+B,MAAM,kBAAkB,CAAC;AACvF,OAAO,EACL,+BAA+B,EAC/B,8BAA8B,GAE/B,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,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createPrismaKillSwitchEvaluator — Prisma-backed implementation of the
|
|
3
|
+
* SDK KillSwitchEvaluator contract introduced by QAP-085 / QAP-086 / QAP-087.
|
|
4
|
+
*
|
|
5
|
+
* Sprint 8 Phase 4 (Policy Governance). Three independent triggers that
|
|
6
|
+
* halt auto-fix activity when recent telemetry indicates the platform is
|
|
7
|
+
* misbehaving:
|
|
8
|
+
*
|
|
9
|
+
* 1. SUCCESS_RATE_DROP (QAP-085) — per-(classification, surface):
|
|
10
|
+
* For each (classification, surface) seen in recent MERGED attempts
|
|
11
|
+
* (last 30 days), compute success rate as
|
|
12
|
+
* `(mergedClean + mergedWithEdits) / total`. If `total >= minSample`
|
|
13
|
+
* AND `successRate < threshold`, trip a CLASSIFICATION-scope switch.
|
|
14
|
+
*
|
|
15
|
+
* 2. CONSECUTIVE_FAILURE (QAP-086) — per-project: count attempts in
|
|
16
|
+
* the last `consecutiveFailureWindowHours` for the project ordered
|
|
17
|
+
* by attemptedAt desc. If the FIRST N are all PRE_VERIFY_FAILED,
|
|
18
|
+
* trip a PROJECT-scope switch.
|
|
19
|
+
*
|
|
20
|
+
* 3. REGRESSION_CASCADE (QAP-087) — platform-wide: count attempts
|
|
21
|
+
* across ALL projects where `autoRevertedAt` is within the last
|
|
22
|
+
* `regressionCascadeWindowHours`. If `>=N`, trip a PLATFORM-scope
|
|
23
|
+
* switch (projectId IS NULL).
|
|
24
|
+
*
|
|
25
|
+
* # History semantics
|
|
26
|
+
*
|
|
27
|
+
* Trips are history-preserving — when a trigger evaluates positive on a
|
|
28
|
+
* tuple that already has an engaged row, the existing row is preserved.
|
|
29
|
+
* When it evaluates positive on a tuple with only unblocked rows (or
|
|
30
|
+
* none), a new row is inserted. This means the audit trail shows every
|
|
31
|
+
* trip across time without losing prior unblock provenance.
|
|
32
|
+
*
|
|
33
|
+
* # Tenant isolation
|
|
34
|
+
*
|
|
35
|
+
* Every method scopes by projectId. Pattern D — wrong-project / unknown-
|
|
36
|
+
* project lookups for unblock return `not_found` (defense-in-depth
|
|
37
|
+
* against tenant enumeration). PLATFORM-scope switches have
|
|
38
|
+
* `projectId IS NULL` in storage; getEngaged unions per-project + platform
|
|
39
|
+
* rows so the dispatcher gate sees both.
|
|
40
|
+
*/
|
|
41
|
+
import type { PrismaClient } from './prisma.js';
|
|
42
|
+
import { type KillSwitchEvaluator } from '@derwinjs/sdk';
|
|
43
|
+
export interface PrismaKillSwitchEvaluatorConfig {
|
|
44
|
+
/** Generated Prisma client. Pass an instance per process. */
|
|
45
|
+
prisma: PrismaClient;
|
|
46
|
+
/**
|
|
47
|
+
* Optional clock injection for deterministic window-boundary tests.
|
|
48
|
+
* Tests pass a fixed Date; production callers omit (defaults to
|
|
49
|
+
* `new Date()`).
|
|
50
|
+
*/
|
|
51
|
+
now?: () => Date;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Default thresholds applied when a project's `killSwitchConfig` JSON
|
|
55
|
+
* column is null. Operators tighten / loosen these per-project once the
|
|
56
|
+
* project has trust history. Picked conservatively so the platform errs
|
|
57
|
+
* on the side of halting too eagerly rather than missing a real problem.
|
|
58
|
+
*/
|
|
59
|
+
export declare const DEFAULT_KILL_SWITCH_THRESHOLDS: {
|
|
60
|
+
readonly successRateDropPct: 0.4;
|
|
61
|
+
readonly successRateMinSample: 10;
|
|
62
|
+
readonly consecutiveFailureN: 5;
|
|
63
|
+
readonly consecutiveFailureWindowHours: 24;
|
|
64
|
+
readonly regressionCascadeN: 3;
|
|
65
|
+
readonly regressionCascadeWindowHours: 6;
|
|
66
|
+
};
|
|
67
|
+
export declare function createPrismaKillSwitchEvaluator(config: PrismaKillSwitchEvaluatorConfig): KillSwitchEvaluator;
|
|
68
|
+
//# sourceMappingURL=kill-switch-evaluator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kill-switch-evaluator.d.ts","sourceRoot":"","sources":["../src/kill-switch-evaluator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAuCG;AAEH,OAAO,KAAK,EAAU,YAAY,EAAE,MAAM,aAAa,CAAC;AACxD,OAAO,EAEL,KAAK,mBAAmB,EAIzB,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,+BAA+B;IAC9C,6DAA6D;IAC7D,MAAM,EAAE,YAAY,CAAC;IACrB;;;;OAIG;IACH,GAAG,CAAC,EAAE,MAAM,IAAI,CAAC;CAClB;AAID;;;;;GAKG;AACH,eAAO,MAAM,8BAA8B;;;;;;;CAUjC,CAAC;AAoGX,wBAAgB,+BAA+B,CAC7C,MAAM,EAAE,+BAA+B,GACtC,mBAAmB,CA2TrB"}
|
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createPrismaKillSwitchEvaluator — Prisma-backed implementation of the
|
|
3
|
+
* SDK KillSwitchEvaluator contract introduced by QAP-085 / QAP-086 / QAP-087.
|
|
4
|
+
*
|
|
5
|
+
* Sprint 8 Phase 4 (Policy Governance). Three independent triggers that
|
|
6
|
+
* halt auto-fix activity when recent telemetry indicates the platform is
|
|
7
|
+
* misbehaving:
|
|
8
|
+
*
|
|
9
|
+
* 1. SUCCESS_RATE_DROP (QAP-085) — per-(classification, surface):
|
|
10
|
+
* For each (classification, surface) seen in recent MERGED attempts
|
|
11
|
+
* (last 30 days), compute success rate as
|
|
12
|
+
* `(mergedClean + mergedWithEdits) / total`. If `total >= minSample`
|
|
13
|
+
* AND `successRate < threshold`, trip a CLASSIFICATION-scope switch.
|
|
14
|
+
*
|
|
15
|
+
* 2. CONSECUTIVE_FAILURE (QAP-086) — per-project: count attempts in
|
|
16
|
+
* the last `consecutiveFailureWindowHours` for the project ordered
|
|
17
|
+
* by attemptedAt desc. If the FIRST N are all PRE_VERIFY_FAILED,
|
|
18
|
+
* trip a PROJECT-scope switch.
|
|
19
|
+
*
|
|
20
|
+
* 3. REGRESSION_CASCADE (QAP-087) — platform-wide: count attempts
|
|
21
|
+
* across ALL projects where `autoRevertedAt` is within the last
|
|
22
|
+
* `regressionCascadeWindowHours`. If `>=N`, trip a PLATFORM-scope
|
|
23
|
+
* switch (projectId IS NULL).
|
|
24
|
+
*
|
|
25
|
+
* # History semantics
|
|
26
|
+
*
|
|
27
|
+
* Trips are history-preserving — when a trigger evaluates positive on a
|
|
28
|
+
* tuple that already has an engaged row, the existing row is preserved.
|
|
29
|
+
* When it evaluates positive on a tuple with only unblocked rows (or
|
|
30
|
+
* none), a new row is inserted. This means the audit trail shows every
|
|
31
|
+
* trip across time without losing prior unblock provenance.
|
|
32
|
+
*
|
|
33
|
+
* # Tenant isolation
|
|
34
|
+
*
|
|
35
|
+
* Every method scopes by projectId. Pattern D — wrong-project / unknown-
|
|
36
|
+
* project lookups for unblock return `not_found` (defense-in-depth
|
|
37
|
+
* against tenant enumeration). PLATFORM-scope switches have
|
|
38
|
+
* `projectId IS NULL` in storage; getEngaged unions per-project + platform
|
|
39
|
+
* rows so the dispatcher gate sees both.
|
|
40
|
+
*/
|
|
41
|
+
import { KillSwitchEvaluatorError, } from '@derwinjs/sdk';
|
|
42
|
+
// ─── Defaults ────────────────────────────────────────────────────────────
|
|
43
|
+
/**
|
|
44
|
+
* Default thresholds applied when a project's `killSwitchConfig` JSON
|
|
45
|
+
* column is null. Operators tighten / loosen these per-project once the
|
|
46
|
+
* project has trust history. Picked conservatively so the platform errs
|
|
47
|
+
* on the side of halting too eagerly rather than missing a real problem.
|
|
48
|
+
*/
|
|
49
|
+
export const DEFAULT_KILL_SWITCH_THRESHOLDS = {
|
|
50
|
+
// QAP-085 — SUCCESS_RATE_DROP
|
|
51
|
+
successRateDropPct: 0.4, // < 40% success in last N attempts → trip
|
|
52
|
+
successRateMinSample: 10, // need at least 10 attempts before evaluating
|
|
53
|
+
// QAP-086 — CONSECUTIVE_FAILURE
|
|
54
|
+
consecutiveFailureN: 5, // 5 PRE_VERIFY_FAILED in a row → trip
|
|
55
|
+
consecutiveFailureWindowHours: 24,
|
|
56
|
+
// QAP-087 — REGRESSION_CASCADE
|
|
57
|
+
regressionCascadeN: 3, // 3 new regressions across platform → trip
|
|
58
|
+
regressionCascadeWindowHours: 6,
|
|
59
|
+
};
|
|
60
|
+
/**
|
|
61
|
+
* Window over which SUCCESS_RATE_DROP looks for merged attempts. The
|
|
62
|
+
* trust telemetry roll-up uses a 30-day window everywhere else (see
|
|
63
|
+
* ClassificationTrust.attemptsLast30d), so we mirror it here.
|
|
64
|
+
*/
|
|
65
|
+
const SUCCESS_RATE_LOOKBACK_DAYS = 30;
|
|
66
|
+
const MS_PER_HOUR = 60 * 60 * 1000;
|
|
67
|
+
const MS_PER_DAY = 24 * MS_PER_HOUR;
|
|
68
|
+
// ─── Helpers ─────────────────────────────────────────────────────────────
|
|
69
|
+
/**
|
|
70
|
+
* Merge a project's killSwitchConfig JSON over the defaults. Only known
|
|
71
|
+
* keys are picked up; unknown keys are ignored. Non-finite / wrong-type
|
|
72
|
+
* values fall back to the default, so a malformed JSON column can't break
|
|
73
|
+
* the evaluator.
|
|
74
|
+
*/
|
|
75
|
+
function resolveThresholds(raw) {
|
|
76
|
+
const cfg = raw !== null && typeof raw === 'object' && !Array.isArray(raw)
|
|
77
|
+
? raw
|
|
78
|
+
: {};
|
|
79
|
+
function num(key) {
|
|
80
|
+
const v = cfg[key];
|
|
81
|
+
if (typeof v === 'number' && Number.isFinite(v))
|
|
82
|
+
return v;
|
|
83
|
+
return DEFAULT_KILL_SWITCH_THRESHOLDS[key];
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
successRateDropPct: num('successRateDropPct'),
|
|
87
|
+
successRateMinSample: num('successRateMinSample'),
|
|
88
|
+
consecutiveFailureN: num('consecutiveFailureN'),
|
|
89
|
+
consecutiveFailureWindowHours: num('consecutiveFailureWindowHours'),
|
|
90
|
+
regressionCascadeN: num('regressionCascadeN'),
|
|
91
|
+
regressionCascadeWindowHours: num('regressionCascadeWindowHours'),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// AttemptStatus values that count as "merged" for success-rate purposes.
|
|
95
|
+
const MERGED_STATUSES = ['AUTO_MERGED', 'HUMAN_MERGED'];
|
|
96
|
+
// AttemptStatus values that count as "failed-and-completed" for
|
|
97
|
+
// success-rate purposes (post-merge regressions).
|
|
98
|
+
const REGRESSED_STATUSES = ['REGRESSED_REVERTED'];
|
|
99
|
+
function mapRow(row) {
|
|
100
|
+
// triggerMetrics is stored as JSONB; coerce the Prisma JsonValue back
|
|
101
|
+
// into a plain object for the contract type. Defense-in-depth: if a
|
|
102
|
+
// legacy / direct-DB row has a non-object scalar, surface an empty
|
|
103
|
+
// object rather than letting downstream code crash on `.foo`.
|
|
104
|
+
const metrics = row.triggerMetrics !== null &&
|
|
105
|
+
typeof row.triggerMetrics === 'object' &&
|
|
106
|
+
!Array.isArray(row.triggerMetrics)
|
|
107
|
+
? row.triggerMetrics
|
|
108
|
+
: {};
|
|
109
|
+
return {
|
|
110
|
+
id: row.id,
|
|
111
|
+
projectId: row.projectId,
|
|
112
|
+
classification: row.classification,
|
|
113
|
+
surface: row.surface,
|
|
114
|
+
type: row.type,
|
|
115
|
+
scope: row.scope,
|
|
116
|
+
engaged: row.engaged,
|
|
117
|
+
engagedAt: row.engagedAt,
|
|
118
|
+
unblockedAt: row.unblockedAt,
|
|
119
|
+
unblockedBy: row.unblockedBy,
|
|
120
|
+
reason: row.reason,
|
|
121
|
+
triggerMetrics: metrics,
|
|
122
|
+
createdAt: row.createdAt,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
// ─── Factory ─────────────────────────────────────────────────────────────
|
|
126
|
+
export function createPrismaKillSwitchEvaluator(config) {
|
|
127
|
+
const { prisma } = config;
|
|
128
|
+
const now = config.now ?? (() => new Date());
|
|
129
|
+
/**
|
|
130
|
+
* Insert a new engaged switch row IFF no engaged row already exists for
|
|
131
|
+
* the (projectId, classification, surface, type, scope) tuple. Returns
|
|
132
|
+
* the row regardless (the existing or the newly inserted one) so the
|
|
133
|
+
* caller can include it in the evaluate() return list.
|
|
134
|
+
*/
|
|
135
|
+
async function tripIfNotEngaged(args) {
|
|
136
|
+
// Find an existing engaged row for this tuple. We do NOT use
|
|
137
|
+
// findUnique because (type, scope, classification, surface, projectId)
|
|
138
|
+
// is not a unique constraint by design — we want the history-preserving
|
|
139
|
+
// append-on-retrip semantics.
|
|
140
|
+
const existing = await prisma.killSwitchState.findFirst({
|
|
141
|
+
where: {
|
|
142
|
+
projectId: args.projectId,
|
|
143
|
+
classification: args.classification,
|
|
144
|
+
surface: args.surface,
|
|
145
|
+
type: args.type,
|
|
146
|
+
scope: args.scope,
|
|
147
|
+
engaged: true,
|
|
148
|
+
},
|
|
149
|
+
});
|
|
150
|
+
if (existing !== null)
|
|
151
|
+
return existing;
|
|
152
|
+
const created = await prisma.killSwitchState.create({
|
|
153
|
+
data: {
|
|
154
|
+
projectId: args.projectId,
|
|
155
|
+
classification: args.classification,
|
|
156
|
+
surface: args.surface,
|
|
157
|
+
type: args.type,
|
|
158
|
+
scope: args.scope,
|
|
159
|
+
engaged: true,
|
|
160
|
+
reason: args.reason,
|
|
161
|
+
triggerMetrics: args.triggerMetrics,
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
return created;
|
|
165
|
+
}
|
|
166
|
+
// ── Trigger 1 — QAP-085 SUCCESS_RATE_DROP ──────────────────────────────
|
|
167
|
+
async function evaluateSuccessRateDrop(projectId, thresholds, at) {
|
|
168
|
+
const since = new Date(at.getTime() - SUCCESS_RATE_LOOKBACK_DAYS * MS_PER_DAY);
|
|
169
|
+
// Pull recent terminal-state attempts (merged or regressed-reverted).
|
|
170
|
+
// For each (classification, surface) tuple we compute success rate
|
|
171
|
+
// = merged / (merged + regressed). PRE_VERIFY_FAILED attempts never
|
|
172
|
+
// dispatched, so they are NOT in the denominator — they belong to
|
|
173
|
+
// the CONSECUTIVE_FAILURE trigger instead.
|
|
174
|
+
const attempts = await prisma.qAFixAttempt.findMany({
|
|
175
|
+
where: {
|
|
176
|
+
projectId,
|
|
177
|
+
attemptedAt: { gte: since },
|
|
178
|
+
dispatchStatus: {
|
|
179
|
+
in: [...MERGED_STATUSES, ...REGRESSED_STATUSES],
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
select: {
|
|
183
|
+
dispatchStatus: true,
|
|
184
|
+
ticket: { select: { classification: true, surface: true } },
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
const tallies = new Map();
|
|
188
|
+
for (const a of attempts) {
|
|
189
|
+
const classification = a.ticket.classification;
|
|
190
|
+
const surface = a.ticket.surface;
|
|
191
|
+
if (classification.length === 0)
|
|
192
|
+
continue;
|
|
193
|
+
const key = `${classification}::${surface}`;
|
|
194
|
+
let t = tallies.get(key);
|
|
195
|
+
if (t === undefined) {
|
|
196
|
+
t = { classification, surface, total: 0, merged: 0 };
|
|
197
|
+
tallies.set(key, t);
|
|
198
|
+
}
|
|
199
|
+
t.total += 1;
|
|
200
|
+
if (MERGED_STATUSES.includes(a.dispatchStatus)) {
|
|
201
|
+
t.merged += 1;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const tripped = [];
|
|
205
|
+
for (const t of tallies.values()) {
|
|
206
|
+
if (t.total < thresholds.successRateMinSample)
|
|
207
|
+
continue;
|
|
208
|
+
const rate = t.merged / t.total;
|
|
209
|
+
if (rate >= thresholds.successRateDropPct)
|
|
210
|
+
continue;
|
|
211
|
+
const row = await tripIfNotEngaged({
|
|
212
|
+
projectId,
|
|
213
|
+
classification: t.classification,
|
|
214
|
+
surface: t.surface,
|
|
215
|
+
type: 'SUCCESS_RATE_DROP',
|
|
216
|
+
scope: 'CLASSIFICATION',
|
|
217
|
+
reason: `QAP-085: success rate ${(rate * 100).toFixed(1)}% over ${t.total.toString()} attempts ` +
|
|
218
|
+
`is below threshold ${(thresholds.successRateDropPct * 100).toFixed(0)}% ` +
|
|
219
|
+
`for ${t.classification} on ${t.surface}.`,
|
|
220
|
+
triggerMetrics: {
|
|
221
|
+
classification: t.classification,
|
|
222
|
+
surface: t.surface,
|
|
223
|
+
total: t.total,
|
|
224
|
+
merged: t.merged,
|
|
225
|
+
successRate: rate,
|
|
226
|
+
threshold: thresholds.successRateDropPct,
|
|
227
|
+
windowDays: SUCCESS_RATE_LOOKBACK_DAYS,
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
tripped.push(row);
|
|
231
|
+
}
|
|
232
|
+
return tripped;
|
|
233
|
+
}
|
|
234
|
+
// ── Trigger 2 — QAP-086 CONSECUTIVE_FAILURE ─────────────────────────────
|
|
235
|
+
async function evaluateConsecutiveFailure(projectId, thresholds, at) {
|
|
236
|
+
const since = new Date(at.getTime() - thresholds.consecutiveFailureWindowHours * MS_PER_HOUR);
|
|
237
|
+
// Order by attemptedAt desc so we examine the most recent attempts
|
|
238
|
+
// first. We need at least N attempts in the window; if the first N
|
|
239
|
+
// (ordered by recency desc) are ALL PRE_VERIFY_FAILED, trip.
|
|
240
|
+
const recent = await prisma.qAFixAttempt.findMany({
|
|
241
|
+
where: {
|
|
242
|
+
projectId,
|
|
243
|
+
attemptedAt: { gte: since },
|
|
244
|
+
},
|
|
245
|
+
orderBy: { attemptedAt: 'desc' },
|
|
246
|
+
take: thresholds.consecutiveFailureN,
|
|
247
|
+
select: { id: true, dispatchStatus: true, attemptedAt: true },
|
|
248
|
+
});
|
|
249
|
+
if (recent.length < thresholds.consecutiveFailureN)
|
|
250
|
+
return [];
|
|
251
|
+
const allFailed = recent.every((a) => a.dispatchStatus === 'PRE_VERIFY_FAILED');
|
|
252
|
+
if (!allFailed)
|
|
253
|
+
return [];
|
|
254
|
+
const row = await tripIfNotEngaged({
|
|
255
|
+
projectId,
|
|
256
|
+
classification: null,
|
|
257
|
+
surface: null,
|
|
258
|
+
type: 'CONSECUTIVE_FAILURE',
|
|
259
|
+
scope: 'PROJECT',
|
|
260
|
+
reason: `QAP-086: ${thresholds.consecutiveFailureN.toString()} consecutive PRE_VERIFY_FAILED attempts ` +
|
|
261
|
+
`within ${thresholds.consecutiveFailureWindowHours.toString()}h.`,
|
|
262
|
+
triggerMetrics: {
|
|
263
|
+
consecutiveFailureN: thresholds.consecutiveFailureN,
|
|
264
|
+
windowHours: thresholds.consecutiveFailureWindowHours,
|
|
265
|
+
attemptIds: recent.map((a) => a.id),
|
|
266
|
+
oldestAttemptAt: recent[recent.length - 1]?.attemptedAt ?? null,
|
|
267
|
+
},
|
|
268
|
+
});
|
|
269
|
+
return [row];
|
|
270
|
+
}
|
|
271
|
+
// ── Trigger 3 — QAP-087 REGRESSION_CASCADE ──────────────────────────────
|
|
272
|
+
async function evaluateRegressionCascade(thresholds, at) {
|
|
273
|
+
const since = new Date(at.getTime() - thresholds.regressionCascadeWindowHours * MS_PER_HOUR);
|
|
274
|
+
// Platform-wide — count attempts across ALL projects that were
|
|
275
|
+
// auto-reverted within the window. autoRevertedAt is the field the
|
|
276
|
+
// Sprint 7 RegressionDetector flips when post-verify telemetry shows
|
|
277
|
+
// a regression.
|
|
278
|
+
const reverts = await prisma.qAFixAttempt.findMany({
|
|
279
|
+
where: {
|
|
280
|
+
autoRevertedAt: { gte: since, not: null },
|
|
281
|
+
},
|
|
282
|
+
select: { id: true, projectId: true, autoRevertedAt: true },
|
|
283
|
+
});
|
|
284
|
+
if (reverts.length < thresholds.regressionCascadeN)
|
|
285
|
+
return [];
|
|
286
|
+
const row = await tripIfNotEngaged({
|
|
287
|
+
projectId: null,
|
|
288
|
+
classification: null,
|
|
289
|
+
surface: null,
|
|
290
|
+
type: 'REGRESSION_CASCADE',
|
|
291
|
+
scope: 'PLATFORM',
|
|
292
|
+
reason: `QAP-087: ${reverts.length.toString()} regressions across the platform within ` +
|
|
293
|
+
`${thresholds.regressionCascadeWindowHours.toString()}h ` +
|
|
294
|
+
`(threshold ${thresholds.regressionCascadeN.toString()}).`,
|
|
295
|
+
triggerMetrics: {
|
|
296
|
+
regressionCount: reverts.length,
|
|
297
|
+
threshold: thresholds.regressionCascadeN,
|
|
298
|
+
windowHours: thresholds.regressionCascadeWindowHours,
|
|
299
|
+
attemptIds: reverts.map((r) => r.id),
|
|
300
|
+
affectedProjectIds: Array.from(new Set(reverts.map((r) => r.projectId))),
|
|
301
|
+
},
|
|
302
|
+
});
|
|
303
|
+
return [row];
|
|
304
|
+
}
|
|
305
|
+
return {
|
|
306
|
+
async evaluate(input) {
|
|
307
|
+
if (typeof input.projectId !== 'string' || input.projectId.length === 0) {
|
|
308
|
+
throw new KillSwitchEvaluatorError('invalid_input', 'KillSwitchEvaluator: projectId is required');
|
|
309
|
+
}
|
|
310
|
+
const at = now();
|
|
311
|
+
// Resolve thresholds: project override → defaults. Pattern D —
|
|
312
|
+
// unknown project resolves to defaults; the trigger queries simply
|
|
313
|
+
// return zero rows for an unknown projectId, so no trip.
|
|
314
|
+
const project = await prisma.project.findUnique({
|
|
315
|
+
where: { id: input.projectId },
|
|
316
|
+
select: { killSwitchConfig: true },
|
|
317
|
+
});
|
|
318
|
+
const thresholds = resolveThresholds(project?.killSwitchConfig ?? null);
|
|
319
|
+
// Run all three triggers. They are independent — one tripping
|
|
320
|
+
// doesn't short-circuit the others.
|
|
321
|
+
const tripped = [];
|
|
322
|
+
tripped.push(...(await evaluateSuccessRateDrop(input.projectId, thresholds, at)));
|
|
323
|
+
tripped.push(...(await evaluateConsecutiveFailure(input.projectId, thresholds, at)));
|
|
324
|
+
tripped.push(...(await evaluateRegressionCascade(thresholds, at)));
|
|
325
|
+
// Return the union of (newly-tripped switches) ∪ (already-engaged
|
|
326
|
+
// switches that match this project). The caller (dispatcher) cares
|
|
327
|
+
// about what's CURRENTLY engaged, not strictly about the set we
|
|
328
|
+
// tripped on this evaluate() call.
|
|
329
|
+
const engaged = await this.getEngaged({ projectId: input.projectId });
|
|
330
|
+
// De-duplicate by id — tripped rows for THIS evaluate call may
|
|
331
|
+
// already be in the engaged list from getEngaged.
|
|
332
|
+
const byId = new Map();
|
|
333
|
+
for (const r of tripped)
|
|
334
|
+
byId.set(r.id, mapRow(r));
|
|
335
|
+
for (const e of engaged)
|
|
336
|
+
byId.set(e.id, e);
|
|
337
|
+
return Array.from(byId.values());
|
|
338
|
+
},
|
|
339
|
+
async getEngaged(input) {
|
|
340
|
+
if (typeof input.projectId !== 'string' || input.projectId.length === 0) {
|
|
341
|
+
throw new KillSwitchEvaluatorError('invalid_input', 'KillSwitchEvaluator: projectId is required');
|
|
342
|
+
}
|
|
343
|
+
// Union: (engaged switches scoped to this project) OR
|
|
344
|
+
// (engaged platform-wide switches, projectId IS NULL).
|
|
345
|
+
const rows = await prisma.killSwitchState.findMany({
|
|
346
|
+
where: {
|
|
347
|
+
engaged: true,
|
|
348
|
+
OR: [{ projectId: input.projectId }, { scope: 'PLATFORM' }],
|
|
349
|
+
},
|
|
350
|
+
orderBy: { engagedAt: 'desc' },
|
|
351
|
+
});
|
|
352
|
+
return rows.map(mapRow);
|
|
353
|
+
},
|
|
354
|
+
async unblock(input) {
|
|
355
|
+
if (typeof input.projectId !== 'string' || input.projectId.length === 0) {
|
|
356
|
+
throw new KillSwitchEvaluatorError('invalid_input', 'KillSwitchEvaluator: projectId is required');
|
|
357
|
+
}
|
|
358
|
+
if (typeof input.switchId !== 'string' || input.switchId.length === 0) {
|
|
359
|
+
throw new KillSwitchEvaluatorError('invalid_input', 'KillSwitchEvaluator: switchId is required');
|
|
360
|
+
}
|
|
361
|
+
if (typeof input.unblockedBy !== 'string' || input.unblockedBy.trim().length === 0) {
|
|
362
|
+
throw new KillSwitchEvaluatorError('invalid_input', 'KillSwitchEvaluator: unblockedBy is required');
|
|
363
|
+
}
|
|
364
|
+
// Pattern D — operator can only unblock a switch they have access to:
|
|
365
|
+
// 1. CLASSIFICATION / PROJECT switches must match projectId.
|
|
366
|
+
// 2. PLATFORM switches are unblockable from any project (any
|
|
367
|
+
// operator with platform-admin rights — middleware enforces).
|
|
368
|
+
const existing = await prisma.killSwitchState.findFirst({
|
|
369
|
+
where: {
|
|
370
|
+
id: input.switchId,
|
|
371
|
+
OR: [{ projectId: input.projectId }, { scope: 'PLATFORM' }],
|
|
372
|
+
},
|
|
373
|
+
select: { id: true },
|
|
374
|
+
});
|
|
375
|
+
if (existing === null) {
|
|
376
|
+
throw new KillSwitchEvaluatorError('not_found', `KillSwitch ${input.switchId} not found in project ${input.projectId}`);
|
|
377
|
+
}
|
|
378
|
+
await prisma.killSwitchState.update({
|
|
379
|
+
where: { id: input.switchId },
|
|
380
|
+
data: {
|
|
381
|
+
engaged: false,
|
|
382
|
+
unblockedAt: now(),
|
|
383
|
+
unblockedBy: input.unblockedBy,
|
|
384
|
+
},
|
|
385
|
+
});
|
|
386
|
+
},
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
//# sourceMappingURL=kill-switch-evaluator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kill-switch-evaluator.js","sourceRoot":"","sources":["../src/kill-switch-evaluator.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* createPrismaPathTierResolver — Prisma-backed implementation of the SDK
|
|
3
|
+
* PathTierResolver contract introduced by QAP-080.
|
|
4
|
+
*
|
|
5
|
+
* Sprint 8 (Policy Governance, Phase 1). Resolves a list of paths to the
|
|
6
|
+
* **strictest** RiskTier among all rules that match any of the supplied
|
|
7
|
+
* paths. "Strictest" follows the RiskTier ladder: NEVER > HIGH > MEDIUM >
|
|
8
|
+
* LOW. Falls back to the project's `defaultTier` when no rule matches; if
|
|
9
|
+
* the project itself doesn't exist, falls back to LOW (defense-in-depth
|
|
10
|
+
* against tenant enumeration — wrong-project lookups must not throw or
|
|
11
|
+
* leak whether a Project row exists).
|
|
12
|
+
*
|
|
13
|
+
* No external glob library — `matchGlob` below is a small in-house helper
|
|
14
|
+
* that handles the three glob features we need (`**`, `*`, literals). We
|
|
15
|
+
* deliberately avoid pulling `minimatch` into @derwinjs/db to keep the
|
|
16
|
+
* dependency surface small for Sprint 8; if the Sprint 9 policy editor
|
|
17
|
+
* needs feature parity (negation, brace expansion, `?`), revisit then.
|
|
18
|
+
*
|
|
19
|
+
* Tenant isolation: every read scopes by projectId. setRules wraps DELETE
|
|
20
|
+
* + bulk INSERT in an interactive transaction so partial failures don't
|
|
21
|
+
* leave a half-applied ruleset.
|
|
22
|
+
*/
|
|
23
|
+
import type { PrismaClient } from './prisma.js';
|
|
24
|
+
import { type PathTierResolver } from '@derwinjs/sdk';
|
|
25
|
+
export interface PrismaPathTierResolverConfig {
|
|
26
|
+
/** Generated Prisma client. Pass an instance per process. */
|
|
27
|
+
prisma: PrismaClient;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Minimal glob → path matcher.
|
|
31
|
+
*
|
|
32
|
+
* `**` — matches zero or more path segments (including `/`)
|
|
33
|
+
* `*` — matches zero or more chars within a single segment (no `/`)
|
|
34
|
+
* anything else — literal
|
|
35
|
+
*
|
|
36
|
+
* Implemented by translating the glob to a RegExp. Anchored at both ends.
|
|
37
|
+
* This is intentionally a small subset — we don't support brace expansion,
|
|
38
|
+
* negation, character classes, or `?`. Add them when a real consumer
|
|
39
|
+
* needs them; YAGNI applies in Sprint 8.
|
|
40
|
+
*
|
|
41
|
+
* Throws PathTierResolverError(`invalid_pattern`) on a glob that produces
|
|
42
|
+
* an unparseable RegExp (extremely rare with the supported subset, but
|
|
43
|
+
* the guard exists so the contract's error-code surface is honored).
|
|
44
|
+
*/
|
|
45
|
+
export declare function matchGlob(pattern: string, path: string): boolean;
|
|
46
|
+
export declare function createPrismaPathTierResolver(config: PrismaPathTierResolverConfig): PathTierResolver;
|
|
47
|
+
//# sourceMappingURL=path-tier-resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path-tier-resolver.d.ts","sourceRoot":"","sources":["../src/path-tier-resolver.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAGL,KAAK,gBAAgB,EAGtB,MAAM,eAAe,CAAC;AAIvB,MAAM,WAAW,4BAA4B;IAC3C,6DAA6D;IAC7D,MAAM,EAAE,YAAY,CAAC;CACtB;AA2BD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CA0ChE;AAqBD,wBAAgB,4BAA4B,CAC1C,MAAM,EAAE,4BAA4B,GACnC,gBAAgB,CAuElB"}
|