prisma-schema-auditor 1.0.2 → 1.2.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.
Files changed (45) hide show
  1. package/dist/cli.d.ts.map +1 -1
  2. package/dist/cli.js +37 -2
  3. package/dist/cli.js.map +1 -1
  4. package/dist/core/analysis/normalizeChecks/check1nf.js +3 -0
  5. package/dist/core/analysis/normalizeChecks/check1nf.js.map +1 -1
  6. package/dist/core/analysis/normalizeChecks/check2nf.js +4 -1
  7. package/dist/core/analysis/normalizeChecks/check2nf.js.map +1 -1
  8. package/dist/core/analysis/normalizeChecks/check3nf.d.ts.map +1 -1
  9. package/dist/core/analysis/normalizeChecks/check3nf.js +5 -2
  10. package/dist/core/analysis/normalizeChecks/check3nf.js.map +1 -1
  11. package/dist/core/analysis/normalizeChecks/checkFkIndexes.d.ts +9 -0
  12. package/dist/core/analysis/normalizeChecks/checkFkIndexes.d.ts.map +1 -0
  13. package/dist/core/analysis/normalizeChecks/checkFkIndexes.js +45 -0
  14. package/dist/core/analysis/normalizeChecks/checkFkIndexes.js.map +1 -0
  15. package/dist/core/analysis/normalizeChecks/checkSoftDelete.d.ts +10 -0
  16. package/dist/core/analysis/normalizeChecks/checkSoftDelete.d.ts.map +1 -0
  17. package/dist/core/analysis/normalizeChecks/checkSoftDelete.js +56 -0
  18. package/dist/core/analysis/normalizeChecks/checkSoftDelete.js.map +1 -0
  19. package/dist/core/invariants/generate.d.ts +11 -0
  20. package/dist/core/invariants/generate.d.ts.map +1 -0
  21. package/dist/core/invariants/generate.js +58 -0
  22. package/dist/core/invariants/generate.js.map +1 -0
  23. package/dist/core/invariants/parse.d.ts +7 -2
  24. package/dist/core/invariants/parse.d.ts.map +1 -1
  25. package/dist/core/invariants/parse.js +44 -3
  26. package/dist/core/invariants/parse.js.map +1 -1
  27. package/dist/core/invariants/schema.d.ts +9 -0
  28. package/dist/core/invariants/schema.d.ts.map +1 -1
  29. package/dist/core/invariants/schema.js +7 -0
  30. package/dist/core/invariants/schema.js.map +1 -1
  31. package/dist/core/report/reportTypes.d.ts +7 -2
  32. package/dist/core/report/reportTypes.d.ts.map +1 -1
  33. package/dist/core/report/toJson.d.ts +2 -2
  34. package/dist/core/report/toJson.d.ts.map +1 -1
  35. package/dist/core/report/toJson.js +5 -2
  36. package/dist/core/report/toJson.js.map +1 -1
  37. package/dist/core/report/toText.d.ts +2 -2
  38. package/dist/core/report/toText.d.ts.map +1 -1
  39. package/dist/core/report/toText.js +20 -15
  40. package/dist/core/report/toText.js.map +1 -1
  41. package/dist/index.d.ts +13 -1
  42. package/dist/index.d.ts.map +1 -1
  43. package/dist/index.js +55 -4
  44. package/dist/index.js.map +1 -1
  45. package/package.json +1 -1
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAkCA,wBAAsB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAmH3D"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAoCA,wBAAsB,IAAI,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CAuJ3D"}
package/dist/cli.js CHANGED
@@ -3,7 +3,7 @@ import { parseArgs } from 'node:util';
3
3
  import { resolve } from 'node:path';
4
4
  import { existsSync, writeFileSync } from 'node:fs';
5
5
  import { fileURLToPath } from 'node:url';
6
- import { audit } from './index.js';
6
+ import { audit, generateInvariants } from './index.js';
7
7
  import { toJson } from './core/report/toJson.js';
8
8
  import { toText } from './core/report/toText.js';
9
9
  /** Exit codes. */
@@ -22,6 +22,8 @@ Options:
22
22
  --fail-on <severity> Exit 1 if findings at this severity or above: error | warning | info
23
23
  --no-timestamp Omit timestamp from output
24
24
  --pretty Pretty-print JSON output
25
+ --findings-only Omit contract from output (show only findings + metadata)
26
+ --generate-invariants Generate invariants JSON from schema constraints
25
27
  --help Show this help message
26
28
  `);
27
29
  }
@@ -38,6 +40,8 @@ export async function main(argv) {
38
40
  'fail-on': { type: 'string' },
39
41
  'no-timestamp': { type: 'boolean', default: false },
40
42
  pretty: { type: 'boolean', default: false },
43
+ 'findings-only': { type: 'boolean', default: false },
44
+ 'generate-invariants': { type: 'boolean', default: false },
41
45
  help: { type: 'boolean', default: false },
42
46
  },
43
47
  strict: true,
@@ -78,6 +82,8 @@ export async function main(argv) {
78
82
  }
79
83
  const noTimestamp = args.values['no-timestamp'] === true;
80
84
  const pretty = args.values['pretty'] === true;
85
+ const findingsOnly = args.values['findings-only'] === true;
86
+ const formatOptions = { findingsOnly };
81
87
  // Resolve invariants path if provided
82
88
  const invariantsArg = args.values['invariants'];
83
89
  let invariantsPath;
@@ -88,6 +94,35 @@ export async function main(argv) {
88
94
  return EXIT_CLI_ERROR;
89
95
  }
90
96
  }
97
+ // Handle --generate-invariants
98
+ const generateMode = args.values['generate-invariants'] === true;
99
+ if (generateMode && invariantsPath !== undefined) {
100
+ process.stderr.write('Error: --generate-invariants and --invariants cannot be used together.\n');
101
+ return EXIT_CLI_ERROR;
102
+ }
103
+ if (generateMode) {
104
+ let invariantsResult;
105
+ try {
106
+ invariantsResult = await generateInvariants({ schemaPath });
107
+ }
108
+ catch (error) {
109
+ const detail = error instanceof Error ? error.message : '';
110
+ process.stderr.write(`Error: Failed to parse schema.${detail !== '' ? ` ${detail}` : ''}\n`);
111
+ return EXIT_PARSE_ERROR;
112
+ }
113
+ const output = pretty
114
+ ? JSON.stringify(invariantsResult, null, 2)
115
+ : JSON.stringify(invariantsResult);
116
+ const outPath = args.values['out'];
117
+ if (outPath !== undefined) {
118
+ writeFileSync(resolve(outPath), output, 'utf-8');
119
+ }
120
+ else {
121
+ process.stdout.write(output);
122
+ process.stdout.write('\n');
123
+ }
124
+ return EXIT_OK;
125
+ }
91
126
  // Run audit
92
127
  let result;
93
128
  try {
@@ -99,7 +134,7 @@ export async function main(argv) {
99
134
  return EXIT_PARSE_ERROR;
100
135
  }
101
136
  // Format output
102
- const output = outputFormat === 'json' ? toJson(result, pretty) : toText(result);
137
+ const output = outputFormat === 'json' ? toJson(result, pretty, formatOptions) : toText(result, formatOptions);
103
138
  // Write output
104
139
  const outPath = args.values['out'];
105
140
  if (outPath !== undefined) {
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
@@ -35,6 +35,7 @@ function checkListInString(model, findings) {
35
35
  model: model.name,
36
36
  field: field.name,
37
37
  message: `String field "${field.name}" may contain a delimited list of values. Consider normalizing into a separate table.`,
38
+ fix: `Create a separate table for the values in '${field.name}' and use a relation.`,
38
39
  });
39
40
  }
40
41
  }
@@ -69,6 +70,7 @@ function checkRepeatingGroups(model, findings) {
69
70
  model: model.name,
70
71
  field: null,
71
72
  message: `Fields [${fieldNames}] appear to be a repeating group for "${baseName}". Consider normalizing into a separate table.`,
73
+ fix: `Create a separate '${baseName}' table with a one-to-many relation from '${model.name}'.`,
72
74
  });
73
75
  }
74
76
  }
@@ -84,6 +86,7 @@ function checkJsonRelation(model, findings) {
84
86
  model: model.name,
85
87
  field: field.name,
86
88
  message: `Json field "${field.name}" may contain structured data that could be normalized into related tables.`,
89
+ fix: `If '${field.name}' has a consistent structure, extract it into a related model.`,
87
90
  });
88
91
  }
89
92
  }
@@ -1 +1 @@
1
- {"version":3,"file":"check1nf.js","sourceRoot":"","sources":["../../../../src/core/analysis/normalizeChecks/check1nf.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"check1nf.js","sourceRoot":"","sources":["../../../../src/core/analysis/normalizeChecks/check1nf.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,sBAAsB,GAAG,4CAA4C,CAAC;AAE5E;;;GAGG;AACH,MAAM,uBAAuB,GAAG,cAAc,CAAC;AAE/C;;;;;;;GAOG;AACH,MAAM,UAAU,QAAQ,CAAC,QAA4B;IACnD,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpC,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACnC,oBAAoB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QACtC,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;IACrC,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAoB,EAAE,QAAmB;IAClE,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,IAAI,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,EAAE,CAAC;YACvE,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,8BAA8B;gBACpC,QAAQ,EAAE,SAAS;gBACnB,UAAU,EAAE,KAAK;gBACjB,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,OAAO,EAAE,iBAAiB,KAAK,CAAC,IAAI,uFAAuF;gBAC3H,GAAG,EAAE,8CAA8C,KAAK,CAAC,IAAI,uBAAuB;aACrF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,oBAAoB,CAAC,KAAoB,EAAE,QAAmB;IACrE,0DAA0D;IAC1D,MAAM,MAAM,GAAG,IAAI,GAAG,EAA2B,CAAC;IAElD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjC,MAAM,KAAK,GAAG,uBAAuB,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACvD,IAAI,KAAK,EAAE,CAAC,CAAC,CAAC,KAAK,SAAS,EAAE,CAAC;YAC7B,MAAM,QAAQ,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC;YAC1B,MAAM,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;YACtC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;gBAC3B,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACvB,CAAC;iBAAM,CAAC;gBACN,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;IACH,CAAC;IAED,4DAA4D;IAC5D,KAAK,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,MAAM,EAAE,CAAC;QACxC,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;YACvB,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;YAClC,MAAM,WAAW,GAAG,SAAS,KAAK,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,CAAC;YACzF,IAAI,WAAW,EAAE,CAAC;gBAChB,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBACxD,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,+BAA+B;oBACrC,QAAQ,EAAE,SAAS;oBACnB,UAAU,EAAE,KAAK;oBACjB,KAAK,EAAE,KAAK,CAAC,IAAI;oBACjB,KAAK,EAAE,IAAI;oBACX,OAAO,EAAE,WAAW,UAAU,yCAAyC,QAAQ,gDAAgD;oBAC/H,GAAG,EAAE,sBAAsB,QAAQ,6CAA6C,KAAK,CAAC,IAAI,IAAI;iBAC/F,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;AACH,CAAC;AAED,SAAS,iBAAiB,CAAC,KAAoB,EAAE,QAAmB;IAClE,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QACjC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,EAAE,CAAC;YAC1B,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,6BAA6B;gBACnC,QAAQ,EAAE,MAAM;gBAChB,UAAU,EAAE,KAAK;gBACjB,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,OAAO,EAAE,eAAe,KAAK,CAAC,IAAI,6EAA6E;gBAC/G,GAAG,EAAE,OAAO,KAAK,CAAC,IAAI,gEAAgE;aACvF,CAAC,CAAC;QACL,CAAC;IACH,CAAC;AACH,CAAC"}
@@ -38,13 +38,15 @@ function checkPartialDependency(model, pkFields, fds, findings) {
38
38
  const isProperSubset = fkFd.determinant.length < pkFields.length &&
39
39
  fkFd.determinant.every((f) => pkFields.includes(f));
40
40
  if (isProperSubset) {
41
+ const subset = fkFd.determinant.join(', ');
41
42
  findings.push({
42
43
  rule: 'NF2_PARTIAL_DEPENDENCY_SUSPECTED',
43
44
  severity: 'warning',
44
45
  normalForm: '2NF',
45
46
  model: model.name,
46
47
  field: null,
47
- message: `Composite-key model "${model.name}" has FK fields (${fkFd.determinant.join(', ')}) that are a proper subset of the primary key. Non-key attributes may depend on this subset rather than the full key, which would violate 2NF.`,
48
+ message: `Composite-key model "${model.name}" has FK fields (${subset}) that are a proper subset of the primary key. Non-key attributes may depend on this subset rather than the full key, which would violate 2NF.`,
49
+ fix: `Extract fields that depend on (${subset}) into their own model.`,
48
50
  });
49
51
  }
50
52
  }
@@ -74,6 +76,7 @@ function checkJoinTableDuplicatedAttr(model, pkFields, findings) {
74
76
  model: model.name,
75
77
  field: null,
76
78
  message: `Join table "${model.name}" has extra attributes [${extraFields.join(', ')}] beyond its composite key. Consider whether this should be a first-class entity.`,
79
+ fix: `Add a dedicated @id to '${model.name}' and treat it as a first-class entity.`,
77
80
  });
78
81
  }
79
82
  }
@@ -1 +1 @@
1
- {"version":3,"file":"check2nf.js","sourceRoot":"","sources":["../../../../src/core/analysis/normalizeChecks/check2nf.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"check2nf.js","sourceRoot":"","sources":["../../../../src/core/analysis/normalizeChecks/check2nf.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"check3nf.d.ts","sourceRoot":"","sources":["../../../../src/core/analysis/normalizeChecks/check3nf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAC/E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAI3D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,EAAE,SAAS,oBAAoB,EAAE,GACnC,SAAS,OAAO,EAAE,CA8DpB"}
1
+ {"version":3,"file":"check3nf.d.ts","sourceRoot":"","sources":["../../../../src/core/analysis/normalizeChecks/check3nf.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAC/E,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAI3D;;;;;;;;;;;;;GAaG;AACH,wBAAgB,QAAQ,CACtB,QAAQ,EAAE,kBAAkB,EAC5B,GAAG,EAAE,SAAS,oBAAoB,EAAE,GACnC,SAAS,OAAO,EAAE,CAiEpB"}
@@ -42,6 +42,7 @@ export function check3nf(contract, fds) {
42
42
  if (!allFields.includes(dep)) {
43
43
  continue;
44
44
  }
45
+ const det = fd.determinant.join(', ');
45
46
  if (candidateKeyFields.has(dep)) {
46
47
  // Dependent is in a candidate key - BCNF violation only (3NF is satisfied)
47
48
  findings.push({
@@ -50,7 +51,8 @@ export function check3nf(contract, fds) {
50
51
  normalForm: 'BCNF',
51
52
  model: fd.model,
52
53
  field: dep,
53
- message: `FD {${fd.determinant.join(', ')}} → {${dep}}: determinant is not a superkey. BCNF violation (${dep} is part of a candidate key, so 3NF is satisfied).`,
54
+ message: `FD {${det}} → {${dep}}: determinant is not a superkey. BCNF violation (${dep} is part of a candidate key, so 3NF is satisfied).`,
55
+ fix: `Restructure so that (${det}) is a candidate key, or accept this BCNF violation if intended.`,
54
56
  });
55
57
  }
56
58
  else {
@@ -61,7 +63,8 @@ export function check3nf(contract, fds) {
61
63
  normalForm: '3NF',
62
64
  model: fd.model,
63
65
  field: dep,
64
- message: `FD {${fd.determinant.join(', ')}} → {${dep}}: transitive dependency detected. "${dep}" depends on non-key attributes {${fd.determinant.join(', ')}} rather than a candidate key.`,
66
+ message: `FD {${det}} → {${dep}}: transitive dependency detected. "${dep}" depends on non-key attributes {${det}} rather than a candidate key.`,
67
+ fix: `Move '${dep}' to the model keyed by (${det}), or add @@unique([${det}]) to enforce the dependency.`,
65
68
  });
66
69
  }
67
70
  }
@@ -1 +1 @@
1
- {"version":3,"file":"check3nf.js","sourceRoot":"","sources":["../../../../src/core/analysis/normalizeChecks/check3nf.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"check3nf.js","sourceRoot":"","sources":["../../../../src/core/analysis/normalizeChecks/check3nf.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { ConstraintContract, Finding } from '../../report/reportTypes.js';
2
+ /**
3
+ * Check that foreign key fields are covered by a PK or unique constraint prefix.
4
+ *
5
+ * DMMF does not expose @@index(), so we can only check coverage via PK/unique.
6
+ * A FK is "covered" if its fields are a leftmost prefix of any PK or unique constraint.
7
+ */
8
+ export declare function checkFkIndexes(contract: ConstraintContract): readonly Finding[];
9
+ //# sourceMappingURL=checkFkIndexes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkFkIndexes.d.ts","sourceRoot":"","sources":["../../../../src/core/analysis/normalizeChecks/checkFkIndexes.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAE/E;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,QAAQ,EAAE,kBAAkB,GAAG,SAAS,OAAO,EAAE,CAuB/E"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Check that foreign key fields are covered by a PK or unique constraint prefix.
3
+ *
4
+ * DMMF does not expose @@index(), so we can only check coverage via PK/unique.
5
+ * A FK is "covered" if its fields are a leftmost prefix of any PK or unique constraint.
6
+ */
7
+ export function checkFkIndexes(contract) {
8
+ const findings = [];
9
+ for (const model of contract.models) {
10
+ for (const fk of model.foreignKeys) {
11
+ const fkFields = fk.fields;
12
+ const isCovered = isFkCoveredByConstraint(fkFields, model.primaryKey?.fields ?? null, model.uniqueConstraints);
13
+ if (!isCovered) {
14
+ findings.push({
15
+ rule: 'FK_MISSING_INDEX',
16
+ severity: 'info',
17
+ normalForm: 'SCHEMA',
18
+ model: model.name,
19
+ field: fkFields.length === 1 ? (fkFields[0] ?? null) : null,
20
+ message: `Foreign key (${fkFields.join(', ')}) on "${model.name}" referencing "${fk.referencedModel}" is not covered by any PK or unique constraint prefix. Queries joining on this FK may be slow.`,
21
+ fix: `Add @@index([${fkFields.join(', ')}]) to '${model.name}' for faster joins and cascade operations.`,
22
+ });
23
+ }
24
+ }
25
+ }
26
+ return findings;
27
+ }
28
+ function isFkCoveredByConstraint(fkFields, pkFields, uniqueConstraints) {
29
+ const constraintFieldArrays = [
30
+ ...(pkFields !== null ? [pkFields] : []),
31
+ ...uniqueConstraints.map((uq) => uq.fields),
32
+ ];
33
+ return constraintFieldArrays.some((constraintFields) => isLeftmostPrefix(fkFields, constraintFields));
34
+ }
35
+ /**
36
+ * Check if fkFields is a leftmost prefix of constraintFields.
37
+ * e.g. [a] is a prefix of [a, b], [a, b] is a prefix of [a, b, c], [a] is a prefix of [a].
38
+ */
39
+ function isLeftmostPrefix(fkFields, constraintFields) {
40
+ if (fkFields.length > constraintFields.length) {
41
+ return false;
42
+ }
43
+ return fkFields.every((field, i) => constraintFields[i] === field);
44
+ }
45
+ //# sourceMappingURL=checkFkIndexes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkFkIndexes.js","sourceRoot":"","sources":["../../../../src/core/analysis/normalizeChecks/checkFkIndexes.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAAC,QAA4B;IACzD,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpC,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YACnC,MAAM,QAAQ,GAAG,EAAE,CAAC,MAAM,CAAC;YAE3B,MAAM,SAAS,GAAG,uBAAuB,CAAC,QAAQ,EAAE,KAAK,CAAC,UAAU,EAAE,MAAM,IAAI,IAAI,EAAE,KAAK,CAAC,iBAAiB,CAAC,CAAC;YAC/G,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,QAAQ,CAAC,IAAI,CAAC;oBACZ,IAAI,EAAE,kBAAkB;oBACxB,QAAQ,EAAE,MAAM;oBAChB,UAAU,EAAE,QAAQ;oBACpB,KAAK,EAAE,KAAK,CAAC,IAAI;oBACjB,KAAK,EAAE,QAAQ,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI;oBAC3D,OAAO,EAAE,gBAAgB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,KAAK,CAAC,IAAI,kBAAkB,EAAE,CAAC,eAAe,iGAAiG;oBACpM,GAAG,EAAE,gBAAgB,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,KAAK,CAAC,IAAI,4CAA4C;iBACzG,CAAC,CAAC;YACL,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,SAAS,uBAAuB,CAC9B,QAA2B,EAC3B,QAAkC,EAClC,iBAAoE;IAEpE,MAAM,qBAAqB,GAAmC;QAC5D,GAAG,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACxC,GAAG,iBAAiB,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,CAAC;KAC5C,CAAC;IAEF,OAAO,qBAAqB,CAAC,IAAI,CAAC,CAAC,gBAAgB,EAAE,EAAE,CACrD,gBAAgB,CAAC,QAAQ,EAAE,gBAAgB,CAAC,CAC7C,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,gBAAgB,CAAC,QAA2B,EAAE,gBAAmC;IACxF,IAAI,QAAQ,CAAC,MAAM,GAAG,gBAAgB,CAAC,MAAM,EAAE,CAAC;QAC9C,OAAO,KAAK,CAAC;IACf,CAAC;IACD,OAAO,QAAQ,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,CAAC;AACrE,CAAC"}
@@ -0,0 +1,10 @@
1
+ import type { ConstraintContract, Finding } from '../../report/reportTypes.js';
2
+ /**
3
+ * Check for soft-delete consistency issues.
4
+ *
5
+ * For each model with a soft-delete field (deleted_at or deletedAt, DateTime type),
6
+ * every unique constraint should include the soft-delete field. Otherwise, uniqueness
7
+ * is not scoped to active records, and "deleted" rows can conflict with new ones.
8
+ */
9
+ export declare function checkSoftDelete(contract: ConstraintContract): readonly Finding[];
10
+ //# sourceMappingURL=checkSoftDelete.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkSoftDelete.d.ts","sourceRoot":"","sources":["../../../../src/core/analysis/normalizeChecks/checkSoftDelete.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAE/E;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,kBAAkB,GAAG,SAAS,OAAO,EAAE,CAuDhF"}
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Check for soft-delete consistency issues.
3
+ *
4
+ * For each model with a soft-delete field (deleted_at or deletedAt, DateTime type),
5
+ * every unique constraint should include the soft-delete field. Otherwise, uniqueness
6
+ * is not scoped to active records, and "deleted" rows can conflict with new ones.
7
+ */
8
+ export function checkSoftDelete(contract) {
9
+ const findings = [];
10
+ for (const model of contract.models) {
11
+ const softDeleteField = model.fields.find((f) => (f.name === 'deleted_at' || f.name === 'deletedAt') && f.type === 'DateTime');
12
+ // Check unique constraints include soft-delete field
13
+ if (softDeleteField !== undefined) {
14
+ for (const uq of model.uniqueConstraints) {
15
+ if (!uq.fields.includes(softDeleteField.name)) {
16
+ const existingFields = uq.fields.join(', ');
17
+ findings.push({
18
+ rule: 'SOFTDELETE_MISSING_IN_UNIQUE',
19
+ severity: 'warning',
20
+ normalForm: 'SCHEMA',
21
+ model: model.name,
22
+ field: softDeleteField.name,
23
+ message: `Unique constraint (${existingFields}) on "${model.name}" does not include soft-delete field "${softDeleteField.name}". Deleted rows may conflict with active records.`,
24
+ fix: `Add '${softDeleteField.name}' to this unique constraint: @@unique([${existingFields}, ${softDeleteField.name}])`,
25
+ });
26
+ }
27
+ }
28
+ }
29
+ // Check deleted_at / deleted_by pairing
30
+ const deletedByField = model.fields.find((f) => f.name === 'deleted_by' || f.name === 'deletedBy');
31
+ if (softDeleteField !== undefined && deletedByField === undefined) {
32
+ findings.push({
33
+ rule: 'SOFTDELETE_AT_WITHOUT_BY',
34
+ severity: 'info',
35
+ normalForm: 'SCHEMA',
36
+ model: model.name,
37
+ field: softDeleteField.name,
38
+ message: `Model "${model.name}" has "${softDeleteField.name}" but no "deleted_by"/"deletedBy" field to track who performed the soft-delete.`,
39
+ fix: `Add a 'deleted_by' field to "${model.name}" to record the actor.`,
40
+ });
41
+ }
42
+ else if (deletedByField !== undefined && softDeleteField === undefined) {
43
+ findings.push({
44
+ rule: 'SOFTDELETE_BY_WITHOUT_AT',
45
+ severity: 'info',
46
+ normalForm: 'SCHEMA',
47
+ model: model.name,
48
+ field: deletedByField.name,
49
+ message: `Model "${model.name}" has "${deletedByField.name}" but no "deleted_at"/"deletedAt" DateTime field. The soft-delete pattern is incomplete.`,
50
+ fix: `Add a 'deleted_at DateTime?' field to "${model.name}" to complete the soft-delete pattern.`,
51
+ });
52
+ }
53
+ }
54
+ return findings;
55
+ }
56
+ //# sourceMappingURL=checkSoftDelete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkSoftDelete.js","sourceRoot":"","sources":["../../../../src/core/analysis/normalizeChecks/checkSoftDelete.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,UAAU,eAAe,CAAC,QAA4B;IAC1D,MAAM,QAAQ,GAAc,EAAE,CAAC;IAE/B,KAAK,MAAM,KAAK,IAAI,QAAQ,CAAC,MAAM,EAAE,CAAC;QACpC,MAAM,eAAe,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CACvC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,IAAI,CAAC,CAAC,IAAI,KAAK,UAAU,CACpF,CAAC;QAEF,qDAAqD;QACrD,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;YAClC,KAAK,MAAM,EAAE,IAAI,KAAK,CAAC,iBAAiB,EAAE,CAAC;gBACzC,IAAI,CAAC,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC;oBAC9C,MAAM,cAAc,GAAG,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;oBAC5C,QAAQ,CAAC,IAAI,CAAC;wBACZ,IAAI,EAAE,8BAA8B;wBACpC,QAAQ,EAAE,SAAS;wBACnB,UAAU,EAAE,QAAQ;wBACpB,KAAK,EAAE,KAAK,CAAC,IAAI;wBACjB,KAAK,EAAE,eAAe,CAAC,IAAI;wBAC3B,OAAO,EAAE,sBAAsB,cAAc,SAAS,KAAK,CAAC,IAAI,yCAAyC,eAAe,CAAC,IAAI,mDAAmD;wBAChL,GAAG,EAAE,QAAQ,eAAe,CAAC,IAAI,0CAA0C,cAAc,KAAK,eAAe,CAAC,IAAI,IAAI;qBACvH,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;QAED,wCAAwC;QACxC,MAAM,cAAc,GAAG,KAAK,CAAC,MAAM,CAAC,IAAI,CACtC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,YAAY,IAAI,CAAC,CAAC,IAAI,KAAK,WAAW,CACzD,CAAC;QAEF,IAAI,eAAe,KAAK,SAAS,IAAI,cAAc,KAAK,SAAS,EAAE,CAAC;YAClE,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,0BAA0B;gBAChC,QAAQ,EAAE,MAAM;gBAChB,UAAU,EAAE,QAAQ;gBACpB,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,KAAK,EAAE,eAAe,CAAC,IAAI;gBAC3B,OAAO,EAAE,UAAU,KAAK,CAAC,IAAI,UAAU,eAAe,CAAC,IAAI,iFAAiF;gBAC5I,GAAG,EAAE,gCAAgC,KAAK,CAAC,IAAI,wBAAwB;aACxE,CAAC,CAAC;QACL,CAAC;aAAM,IAAI,cAAc,KAAK,SAAS,IAAI,eAAe,KAAK,SAAS,EAAE,CAAC;YACzE,QAAQ,CAAC,IAAI,CAAC;gBACZ,IAAI,EAAE,0BAA0B;gBAChC,QAAQ,EAAE,MAAM;gBAChB,UAAU,EAAE,QAAQ;gBACpB,KAAK,EAAE,KAAK,CAAC,IAAI;gBACjB,KAAK,EAAE,cAAc,CAAC,IAAI;gBAC1B,OAAO,EAAE,UAAU,KAAK,CAAC,IAAI,UAAU,cAAc,CAAC,IAAI,0FAA0F;gBACpJ,GAAG,EAAE,0CAA0C,KAAK,CAAC,IAAI,wCAAwC;aAClG,CAAC,CAAC;QACL,CAAC;IACH,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { ConstraintContract } from '../report/reportTypes.js';
2
+ import type { FunctionalDependency } from '../analysis/inferFds.js';
3
+ import type { InvariantsFile } from './schema.js';
4
+ /**
5
+ * Generate an invariants file from existing schema constraints.
6
+ *
7
+ * Includes only PK and unique FDs (not FK or invariant-sourced),
8
+ * grouped by model with auto-generated notes.
9
+ */
10
+ export declare function generateInvariantsFile(_contract: ConstraintContract, fds: readonly FunctionalDependency[]): InvariantsFile;
11
+ //# sourceMappingURL=generate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.d.ts","sourceRoot":"","sources":["../../../src/core/invariants/generate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD;;;;;GAKG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,kBAAkB,EAC7B,GAAG,EAAE,SAAS,oBAAoB,EAAE,GACnC,cAAc,CAmChB"}
@@ -0,0 +1,58 @@
1
+ /**
2
+ * Generate an invariants file from existing schema constraints.
3
+ *
4
+ * Includes only PK and unique FDs (not FK or invariant-sourced),
5
+ * grouped by model with auto-generated notes.
6
+ */
7
+ export function generateInvariantsFile(_contract, fds) {
8
+ const eligibleFds = fds.filter((fd) => fd.source === 'pk' || fd.source === 'unique');
9
+ // Group by model (sorted alphabetically)
10
+ const byModel = new Map();
11
+ for (const fd of eligibleFds) {
12
+ const existing = byModel.get(fd.model);
13
+ if (existing !== undefined) {
14
+ existing.push(fd);
15
+ }
16
+ else {
17
+ byModel.set(fd.model, [fd]);
18
+ }
19
+ }
20
+ const modelNames = [...byModel.keys()].sort();
21
+ const result = {};
22
+ for (const modelName of modelNames) {
23
+ const modelFds = byModel.get(modelName);
24
+ if (modelFds === undefined) {
25
+ continue;
26
+ }
27
+ const entries = modelFds.map((fd) => ({
28
+ determinant: [...fd.determinant].sort(),
29
+ dependent: [...fd.dependent].sort(),
30
+ note: generateNote(fd),
31
+ rule: generateRule(fd, modelName),
32
+ }));
33
+ if (entries.length > 0) {
34
+ result[modelName] = { functionalDependencies: entries };
35
+ }
36
+ }
37
+ return result;
38
+ }
39
+ function generateRule(fd, modelName) {
40
+ const det = fd.determinant.length === 1
41
+ ? (fd.determinant[0] ?? '')
42
+ : fd.determinant.join(' + ');
43
+ return `Each ${modelName} is uniquely identified by ${det}`;
44
+ }
45
+ function generateNote(fd) {
46
+ if (fd.source === 'pk') {
47
+ if (fd.determinant.length > 1) {
48
+ return `Composite primary key (${fd.determinant.join(', ')})`;
49
+ }
50
+ return 'Primary key determines all fields';
51
+ }
52
+ // source === 'unique'
53
+ if (fd.determinant.length > 1) {
54
+ return `Composite unique constraint on (${fd.determinant.join(', ')})`;
55
+ }
56
+ return `Unique constraint on (${fd.determinant.join(', ')})`;
57
+ }
58
+ //# sourceMappingURL=generate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generate.js","sourceRoot":"","sources":["../../../src/core/invariants/generate.ts"],"names":[],"mappings":"AAIA;;;;;GAKG;AACH,MAAM,UAAU,sBAAsB,CACpC,SAA6B,EAC7B,GAAoC;IAEpC,MAAM,WAAW,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,MAAM,KAAK,IAAI,IAAI,EAAE,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAC;IAErF,yCAAyC;IACzC,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkC,CAAC;IAC1D,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;QAC7B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC;QACvC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,QAAQ,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;QACpB,CAAC;aAAM,CAAC;YACN,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,EAAE,CAAC,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,MAAM,UAAU,GAAG,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9C,MAAM,MAAM,GAA6H,EAAE,CAAC;IAE5I,KAAK,MAAM,SAAS,IAAI,UAAU,EAAE,CAAC;QACnC,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QACxC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,SAAS;QACX,CAAC;QACD,MAAM,OAAO,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC;YACpC,WAAW,EAAE,CAAC,GAAG,EAAE,CAAC,WAAW,CAAC,CAAC,IAAI,EAAE;YACvC,SAAS,EAAE,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,CAAC,IAAI,EAAE;YACnC,IAAI,EAAE,YAAY,CAAC,EAAE,CAAC;YACtB,IAAI,EAAE,YAAY,CAAC,EAAE,EAAE,SAAS,CAAC;SAClC,CAAC,CAAC,CAAC;QAEJ,IAAI,OAAO,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YACvB,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,sBAAsB,EAAE,OAAO,EAAE,CAAC;QAC1D,CAAC;IACH,CAAC;IAED,OAAO,MAAM,CAAC;AAChB,CAAC;AAED,SAAS,YAAY,CAAC,EAAwB,EAAE,SAAiB;IAC/D,MAAM,GAAG,GACP,EAAE,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC;QACzB,CAAC,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC;QAC3B,CAAC,CAAC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACjC,OAAO,QAAQ,SAAS,8BAA8B,GAAG,EAAE,CAAC;AAC9D,CAAC;AAED,SAAS,YAAY,CAAC,EAAwB;IAC5C,IAAI,EAAE,CAAC,MAAM,KAAK,IAAI,EAAE,CAAC;QACvB,IAAI,EAAE,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;YAC9B,OAAO,0BAA0B,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QAChE,CAAC;QACD,OAAO,mCAAmC,CAAC;IAC7C,CAAC;IAED,sBAAsB;IACtB,IAAI,EAAE,CAAC,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC9B,OAAO,mCAAmC,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;IACzE,CAAC;IACD,OAAO,yBAAyB,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AAC/D,CAAC"}
@@ -1,11 +1,16 @@
1
1
  import type { InvariantsFile } from './schema.js';
2
2
  import type { FunctionalDependency } from '../analysis/inferFds.js';
3
3
  import type { ConstraintContract, Finding } from '../report/reportTypes.js';
4
+ /** Result of parsing an invariants file. */
5
+ export interface ParsedInvariants {
6
+ readonly invariants: InvariantsFile;
7
+ readonly suppress: readonly string[];
8
+ }
4
9
  /**
5
10
  * Parse and validate an invariants JSON file.
6
- * Returns the validated invariants or throws on invalid input.
11
+ * Returns the validated invariants and suppress list, or throws on invalid input.
7
12
  */
8
- export declare function parseInvariantsFile(filePath: string): InvariantsFile;
13
+ export declare function parseInvariantsFile(filePath: string): ParsedInvariants;
9
14
  /**
10
15
  * Convert parsed invariants into FunctionalDependency objects.
11
16
  */
@@ -1 +1 @@
1
- {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../../src/core/invariants/parse.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAE5E;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,cAAc,CAIpE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,cAAc,GAAG,SAAS,oBAAoB,EAAE,CAiB3F;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC/C,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,kBAAkB,GAC3B,SAAS,OAAO,EAAE,CAuCpB"}
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../../src/core/invariants/parse.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,yBAAyB,CAAC;AACpE,OAAO,KAAK,EAAE,kBAAkB,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AAE5E,4CAA4C;AAC5C,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,UAAU,EAAE,cAAc,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,SAAS,MAAM,EAAE,CAAC;CACtC;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,gBAAgB,CAkBtE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,cAAc,GAAG,SAAS,oBAAoB,EAAE,CAiB3F;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC/C,UAAU,EAAE,cAAc,EAC1B,QAAQ,EAAE,kBAAkB,GAC3B,SAAS,OAAO,EAAE,CAwEpB"}
@@ -1,13 +1,25 @@
1
1
  import { readFileSync } from 'node:fs';
2
- import { invariantsFileSchema } from './schema.js';
2
+ import { invariantsFileSchema, suppressArraySchema } from './schema.js';
3
3
  /**
4
4
  * Parse and validate an invariants JSON file.
5
- * Returns the validated invariants or throws on invalid input.
5
+ * Returns the validated invariants and suppress list, or throws on invalid input.
6
6
  */
7
7
  export function parseInvariantsFile(filePath) {
8
8
  const content = readFileSync(filePath, 'utf-8');
9
9
  const raw = JSON.parse(content);
10
- return invariantsFileSchema.parse(raw);
10
+ // Extract suppress array before validating the rest as model record
11
+ let suppress = [];
12
+ let modelData = raw;
13
+ if (raw !== null && typeof raw === 'object' && !Array.isArray(raw)) {
14
+ const obj = raw;
15
+ if ('suppress' in obj) {
16
+ suppress = suppressArraySchema.parse(obj['suppress']);
17
+ const { suppress: _suppress, ...rest } = obj;
18
+ modelData = rest;
19
+ }
20
+ }
21
+ const invariants = invariantsFileSchema.parse(modelData);
22
+ return { invariants, suppress };
11
23
  }
12
24
  /**
13
25
  * Convert parsed invariants into FunctionalDependency objects.
@@ -46,15 +58,25 @@ export function validateInvariantsAgainstContract(invariants, contract) {
46
58
  model: modelName,
47
59
  field: null,
48
60
  message: `Invariants reference model "${modelName}" which does not exist in the schema.`,
61
+ fix: `Update the invariants file to remove or rename model '${modelName}'.`,
49
62
  });
50
63
  continue;
51
64
  }
52
65
  const fieldNames = new Set(model.fields.map((f) => f.name));
53
66
  if (modelInvariants.functionalDependencies !== undefined) {
67
+ // Collect constraint field arrays for enforcement check
68
+ const constraintFieldSets = [
69
+ ...(model.primaryKey !== null ? [model.primaryKey.fields] : []),
70
+ ...model.uniqueConstraints.map((uq) => uq.fields),
71
+ ];
54
72
  for (const fd of modelInvariants.functionalDependencies) {
55
73
  const allReferencedFields = new Set([...fd.determinant, ...fd.dependent]);
74
+ let hasUnknownDeterminantField = false;
56
75
  for (const field of allReferencedFields) {
57
76
  if (!fieldNames.has(field)) {
77
+ if (fd.determinant.includes(field)) {
78
+ hasUnknownDeterminantField = true;
79
+ }
58
80
  findings.push({
59
81
  rule: 'INVARIANT_UNKNOWN_FIELD',
60
82
  severity: 'warning',
@@ -62,6 +84,25 @@ export function validateInvariantsAgainstContract(invariants, contract) {
62
84
  model: modelName,
63
85
  field,
64
86
  message: `Invariants reference field "${field}" which does not exist in model "${modelName}".`,
87
+ fix: `Update the invariants file to remove or rename field '${field}' in model '${modelName}'.`,
88
+ });
89
+ }
90
+ }
91
+ // Check if determinant is enforced by any PK or unique constraint.
92
+ // Skip if any determinant field doesn't exist (already reported above).
93
+ if (!hasUnknownDeterminantField) {
94
+ const detSet = new Set(fd.determinant);
95
+ const isEnforced = constraintFieldSets.some((constraintFields) => constraintFields.every((cf) => detSet.has(cf)));
96
+ if (!isEnforced) {
97
+ const det = fd.determinant.join(', ');
98
+ findings.push({
99
+ rule: 'INVARIANT_DETERMINANT_NOT_ENFORCED',
100
+ severity: 'warning',
101
+ normalForm: 'SCHEMA',
102
+ model: modelName,
103
+ field: null,
104
+ message: `Invariant FD {${det}} → {${fd.dependent.join(', ')}} on "${modelName}": determinant is not enforced by any PK or unique constraint.`,
105
+ fix: `Add @@unique([${det}]) to enforce this functional dependency.`,
65
106
  });
66
107
  }
67
108
  }
@@ -1 +1 @@
1
- {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../../src/core/invariants/parse.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../../src/core/invariants/parse.ts"],"names":[],"mappings":"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"}
@@ -5,6 +5,8 @@ import { z } from 'zod/v4';
5
5
  declare const functionalDependencySchema: z.ZodObject<{
6
6
  determinant: z.ZodArray<z.ZodString>;
7
7
  dependent: z.ZodArray<z.ZodString>;
8
+ note: z.ZodOptional<z.ZodString>;
9
+ rule: z.ZodOptional<z.ZodString>;
8
10
  }, z.core.$strip>;
9
11
  /**
10
12
  * Zod schema for the full invariants file.
@@ -14,8 +16,15 @@ export declare const invariantsFileSchema: z.ZodRecord<z.ZodString, z.ZodObject<
14
16
  functionalDependencies: z.ZodOptional<z.ZodArray<z.ZodObject<{
15
17
  determinant: z.ZodArray<z.ZodString>;
16
18
  dependent: z.ZodArray<z.ZodString>;
19
+ note: z.ZodOptional<z.ZodString>;
20
+ rule: z.ZodOptional<z.ZodString>;
17
21
  }, z.core.$strip>>>;
18
22
  }, z.core.$strip>>;
23
+ /**
24
+ * Zod schema for the suppress array.
25
+ * Format: RULE_CODE:ModelName or RULE_CODE:ModelName.fieldName
26
+ */
27
+ export declare const suppressArraySchema: z.ZodArray<z.ZodString>;
19
28
  /** Parsed type for a functional dependency. */
20
29
  export type InvariantFd = z.infer<typeof functionalDependencySchema>;
21
30
  /** Parsed type for the full invariants file. */
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/core/invariants/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B;;GAEG;AACH,QAAA,MAAM,0BAA0B;;;iBAG9B,CAAC;AASH;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;;;;kBAA8C,CAAC;AAEhF,+CAA+C;AAC/C,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAErE,gDAAgD;AAChD,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../../../src/core/invariants/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B;;GAEG;AACH,QAAA,MAAM,0BAA0B;;;;;iBAK9B,CAAC;AASH;;;GAGG;AACH,eAAO,MAAM,oBAAoB;;;;;;;kBAA8C,CAAC;AAEhF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,yBAE/B,CAAC;AAEF,+CAA+C;AAC/C,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,0BAA0B,CAAC,CAAC;AAErE,gDAAgD;AAChD,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC"}
@@ -5,6 +5,8 @@ import { z } from 'zod/v4';
5
5
  const functionalDependencySchema = z.object({
6
6
  determinant: z.array(z.string()).min(1),
7
7
  dependent: z.array(z.string()).min(1),
8
+ note: z.string().optional(),
9
+ rule: z.string().optional(),
8
10
  });
9
11
  /**
10
12
  * Zod schema for model-level invariants.
@@ -17,4 +19,9 @@ const modelInvariantsSchema = z.object({
17
19
  * Top-level keys are model names, values are model invariants.
18
20
  */
19
21
  export const invariantsFileSchema = z.record(z.string(), modelInvariantsSchema);
22
+ /**
23
+ * Zod schema for the suppress array.
24
+ * Format: RULE_CODE:ModelName or RULE_CODE:ModelName.fieldName
25
+ */
26
+ export const suppressArraySchema = z.array(z.string().regex(/^[A-Z][A-Z0-9_]*:[A-Za-z][A-Za-z0-9_]*(\.[A-Za-z][A-Za-z0-9_]*)?$/));
20
27
  //# sourceMappingURL=schema.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/core/invariants/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B;;GAEG;AACH,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;CACtC,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,sBAAsB,EAAE,CAAC,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC,QAAQ,EAAE;CACvE,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC"}
1
+ {"version":3,"file":"schema.js","sourceRoot":"","sources":["../../../src/core/invariants/schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,QAAQ,CAAC;AAE3B;;GAEG;AACH,MAAM,0BAA0B,GAAG,CAAC,CAAC,MAAM,CAAC;IAC1C,WAAW,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACvC,SAAS,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACrC,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC3B,IAAI,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;CAC5B,CAAC,CAAC;AAEH;;GAEG;AACH,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IACrC,sBAAsB,EAAE,CAAC,CAAC,KAAK,CAAC,0BAA0B,CAAC,CAAC,QAAQ,EAAE;CACvE,CAAC,CAAC;AAEH;;;GAGG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,MAAM,EAAE,EAAE,qBAAqB,CAAC,CAAC;AAEhF;;;GAGG;AACH,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,CAAC,KAAK,CACxC,CAAC,CAAC,MAAM,EAAE,CAAC,KAAK,CAAC,mEAAmE,CAAC,CACtF,CAAC"}
@@ -1,9 +1,9 @@
1
1
  /** Severity levels for audit findings. */
2
2
  export type Severity = 'error' | 'warning' | 'info';
3
3
  /** Normal form levels. */
4
- export type NormalForm = '1NF' | '2NF' | '3NF' | 'BCNF';
4
+ export type NormalForm = '1NF' | '2NF' | '3NF' | 'BCNF' | 'SCHEMA';
5
5
  /** Unique finding rule codes. */
6
- export type RuleCode = 'NF1_LIST_IN_STRING_SUSPECTED' | 'NF1_REPEATING_GROUP_SUSPECTED' | 'NF1_JSON_RELATION_SUSPECTED' | 'NF2_PARTIAL_DEPENDENCY_SUSPECTED' | 'NF2_JOIN_TABLE_DUPLICATED_ATTR_SUSPECTED' | 'NF3_VIOLATION' | 'BCNF_VIOLATION' | 'INVARIANT_UNKNOWN_MODEL' | 'INVARIANT_UNKNOWN_FIELD';
6
+ export type RuleCode = 'NF1_LIST_IN_STRING_SUSPECTED' | 'NF1_REPEATING_GROUP_SUSPECTED' | 'NF1_JSON_RELATION_SUSPECTED' | 'NF2_PARTIAL_DEPENDENCY_SUSPECTED' | 'NF2_JOIN_TABLE_DUPLICATED_ATTR_SUSPECTED' | 'NF3_VIOLATION' | 'BCNF_VIOLATION' | 'INVARIANT_UNKNOWN_MODEL' | 'INVARIANT_UNKNOWN_FIELD' | 'INVARIANT_DETERMINANT_NOT_ENFORCED' | 'SOFTDELETE_MISSING_IN_UNIQUE' | 'SOFTDELETE_AT_WITHOUT_BY' | 'SOFTDELETE_BY_WITHOUT_AT' | 'FK_MISSING_INDEX';
7
7
  /** A single normalization finding. */
8
8
  export interface Finding {
9
9
  readonly rule: RuleCode;
@@ -12,6 +12,7 @@ export interface Finding {
12
12
  readonly model: string;
13
13
  readonly field: string | null;
14
14
  readonly message: string;
15
+ readonly fix: string | null;
15
16
  }
16
17
  /** Referential action on a foreign key. */
17
18
  export type ReferentialAction = 'Cascade' | 'Restrict' | 'NoAction' | 'SetNull' | 'SetDefault';
@@ -56,6 +57,10 @@ export interface ConstraintContract {
56
57
  }
57
58
  /** Output format options. */
58
59
  export type OutputFormat = 'json' | 'text';
60
+ /** Options controlling formatter output. */
61
+ export interface FormatOptions {
62
+ readonly findingsOnly?: boolean | undefined;
63
+ }
59
64
  /** The complete audit result. */
60
65
  export interface AuditResult {
61
66
  readonly contract: ConstraintContract;
@@ -1 +1 @@
1
- {"version":3,"file":"reportTypes.d.ts","sourceRoot":"","sources":["../../../src/core/report/reportTypes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reportTypes.d.ts","sourceRoot":"","sources":["../../../src/core/report/reportTypes.ts"],"names":[],"mappings":"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"}
@@ -1,7 +1,7 @@
1
- import type { AuditResult } from './reportTypes.js';
1
+ import type { AuditResult, FormatOptions } from './reportTypes.js';
2
2
  /**
3
3
  * Serialize an AuditResult to a deterministic JSON string.
4
4
  * Keys are sorted for stable diffing.
5
5
  */
6
- export declare function toJson(result: AuditResult, pretty: boolean): string;
6
+ export declare function toJson(result: AuditResult, pretty: boolean, options?: FormatOptions): string;
7
7
  //# sourceMappingURL=toJson.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"toJson.d.ts","sourceRoot":"","sources":["../../../src/core/report/toJson.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;;GAGG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,GAAG,MAAM,CAKnE"}
1
+ {"version":3,"file":"toJson.d.ts","sourceRoot":"","sources":["../../../src/core/report/toJson.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEnE;;;GAGG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAQ5F"}
@@ -2,8 +2,11 @@
2
2
  * Serialize an AuditResult to a deterministic JSON string.
3
3
  * Keys are sorted for stable diffing.
4
4
  */
5
- export function toJson(result, pretty) {
6
- const sorted = sortKeysDeep(result);
5
+ export function toJson(result, pretty, options) {
6
+ const data = options?.findingsOnly === true
7
+ ? { findings: result.findings, metadata: result.metadata }
8
+ : result;
9
+ const sorted = sortKeysDeep(data);
7
10
  return pretty
8
11
  ? JSON.stringify(sorted, null, 2)
9
12
  : JSON.stringify(sorted);
@@ -1 +1 @@
1
- {"version":3,"file":"toJson.js","sourceRoot":"","sources":["../../../src/core/report/toJson.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,MAAmB,EAAE,MAAe;IACzD,MAAM,MAAM,GAAG,YAAY,CAAC,MAAM,CAAC,CAAC;IACpC,OAAO,MAAM;QACX,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACjC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC;AAED,6DAA6D;AAC7D,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACjC,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,GAAG,GAAG,KAAgC,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
1
+ {"version":3,"file":"toJson.js","sourceRoot":"","sources":["../../../src/core/report/toJson.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,UAAU,MAAM,CAAC,MAAmB,EAAE,MAAe,EAAE,OAAuB;IAClF,MAAM,IAAI,GAAG,OAAO,EAAE,YAAY,KAAK,IAAI;QACzC,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE,QAAQ,EAAE,MAAM,CAAC,QAAQ,EAAE;QAC1D,CAAC,CAAC,MAAM,CAAC;IACX,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC;IAClC,OAAO,MAAM;QACX,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;QACjC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;AAC7B,CAAC;AAED,6DAA6D;AAC7D,SAAS,YAAY,CAAC,KAAc;IAClC,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;IACjC,CAAC;IACD,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,MAAM,GAAG,GAAG,KAAgC,CAAC;QAC7C,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;QACrC,MAAM,MAAM,GAA4B,EAAE,CAAC;QAC3C,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,MAAM,CAAC,GAAG,CAAC,GAAG,YAAY,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;QACvC,CAAC;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -1,6 +1,6 @@
1
- import type { AuditResult } from './reportTypes.js';
1
+ import type { AuditResult, FormatOptions } from './reportTypes.js';
2
2
  /**
3
3
  * Format an AuditResult as human-readable text.
4
4
  */
5
- export declare function toText(result: AuditResult): string;
5
+ export declare function toText(result: AuditResult, options?: FormatOptions): string;
6
6
  //# sourceMappingURL=toText.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"toText.d.ts","sourceRoot":"","sources":["../../../src/core/report/toText.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD;;GAEG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,GAAG,MAAM,CA+ClD"}
1
+ {"version":3,"file":"toText.d.ts","sourceRoot":"","sources":["../../../src/core/report/toText.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEnE;;GAEG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,OAAO,CAAC,EAAE,aAAa,GAAG,MAAM,CAoD3E"}
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Format an AuditResult as human-readable text.
3
3
  */
4
- export function toText(result) {
4
+ export function toText(result, options) {
5
5
  const lines = [];
6
6
  lines.push('=== Prisma Schema Audit ===');
7
7
  lines.push('');
@@ -12,22 +12,24 @@ export function toText(result) {
12
12
  lines.push(`Models: ${String(result.metadata.modelCount)}`);
13
13
  lines.push(`Findings: ${String(result.metadata.findingCount)}`);
14
14
  lines.push('');
15
- // Contract summary
16
- lines.push('--- Constraint Contract ---');
17
- for (const model of result.contract.models) {
18
- lines.push(` Model: ${model.name}`);
19
- if (model.primaryKey !== null) {
20
- lines.push(` PK: (${model.primaryKey.fields.join(', ')})`);
21
- }
22
- for (const uq of model.uniqueConstraints) {
23
- const label = uq.name !== null ? ` [${uq.name}]` : '';
24
- lines.push(` Unique${label}: (${uq.fields.join(', ')})`);
25
- }
26
- for (const fk of model.foreignKeys) {
27
- lines.push(` FK: (${fk.fields.join(', ')}) -> ${fk.referencedModel}(${fk.referencedFields.join(', ')}) onDelete=${fk.onDelete} onUpdate=${fk.onUpdate}`);
15
+ // Contract summary (omitted in findings-only mode)
16
+ if (options?.findingsOnly !== true) {
17
+ lines.push('--- Constraint Contract ---');
18
+ for (const model of result.contract.models) {
19
+ lines.push(` Model: ${model.name}`);
20
+ if (model.primaryKey !== null) {
21
+ lines.push(` PK: (${model.primaryKey.fields.join(', ')})`);
22
+ }
23
+ for (const uq of model.uniqueConstraints) {
24
+ const label = uq.name !== null ? ` [${uq.name}]` : '';
25
+ lines.push(` Unique${label}: (${uq.fields.join(', ')})`);
26
+ }
27
+ for (const fk of model.foreignKeys) {
28
+ lines.push(` FK: (${fk.fields.join(', ')}) -> ${fk.referencedModel}(${fk.referencedFields.join(', ')}) onDelete=${fk.onDelete} onUpdate=${fk.onUpdate}`);
29
+ }
28
30
  }
31
+ lines.push('');
29
32
  }
30
- lines.push('');
31
33
  // Findings
32
34
  if (result.findings.length > 0) {
33
35
  lines.push('--- Findings ---');
@@ -35,6 +37,9 @@ export function toText(result) {
35
37
  const field = f.field !== null ? `.${f.field}` : '';
36
38
  lines.push(` [${f.severity.toUpperCase()}] ${f.rule} @ ${f.model}${field}`);
37
39
  lines.push(` ${f.message}`);
40
+ if (f.fix !== null) {
41
+ lines.push(` Fix: ${f.fix}`);
42
+ }
38
43
  }
39
44
  }
40
45
  else {
@@ -1 +1 @@
1
- {"version":3,"file":"toText.js","sourceRoot":"","sources":["../../../src/core/report/toText.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"toText.js","sourceRoot":"","sources":["../../../src/core/report/toText.ts"],"names":[],"mappings":"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"}
package/dist/index.d.ts CHANGED
@@ -1,9 +1,11 @@
1
- export type { AuditResult, AuditMetadata, ConstraintContract, ModelContract, FieldContract, PrimaryKeyConstraint, UniqueConstraint, ForeignKeyConstraint, Finding, RuleCode, Severity, NormalForm, OutputFormat, ReferentialAction, } from './core/report/reportTypes.js';
1
+ export type { AuditResult, AuditMetadata, ConstraintContract, ModelContract, FieldContract, PrimaryKeyConstraint, UniqueConstraint, ForeignKeyConstraint, Finding, RuleCode, Severity, NormalForm, OutputFormat, FormatOptions, ReferentialAction, } from './core/report/reportTypes.js';
2
2
  export type { AuditModel, AuditField, AuditPrimaryKey, AuditUniqueIndex, ParseResult, } from './core/prismaSchema/types.js';
3
3
  export type { FunctionalDependency } from './core/analysis/inferFds.js';
4
4
  export type { CandidateKey } from './core/analysis/computeKeys.js';
5
5
  export type { InvariantsFile, InvariantFd } from './core/invariants/schema.js';
6
+ export type { ParsedInvariants } from './core/invariants/parse.js';
6
7
  import type { AuditResult } from './core/report/reportTypes.js';
8
+ import type { InvariantsFile } from './core/invariants/schema.js';
7
9
  /** Options for the audit function. */
8
10
  export interface AuditOptions {
9
11
  readonly schemaPath: string;
@@ -15,4 +17,14 @@ export interface AuditOptions {
15
17
  * Returns the constraint contract and normalization findings.
16
18
  */
17
19
  export declare function audit(schemaPathOrOptions: string | AuditOptions, noTimestamp?: boolean): Promise<AuditResult>;
20
+ /** Options for generating invariants from a schema. */
21
+ export interface GenerateInvariantsOptions {
22
+ readonly schemaPath: string;
23
+ }
24
+ /**
25
+ * Generate an invariants file from existing schema constraints.
26
+ * Parses the schema, extracts constraints, and produces an InvariantsFile
27
+ * with PK and unique FDs and auto-generated notes.
28
+ */
29
+ export declare function generateInvariants(options: GenerateInvariantsOptions): Promise<InvariantsFile>;
18
30
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,iBAAiB,GAClB,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EACV,UAAU,EACV,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,WAAW,GACZ,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAS/E,OAAO,KAAK,EAAE,WAAW,EAAW,MAAM,8BAA8B,CAAC;AAEzE,sCAAsC;AACtC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC5C;AAED;;;GAGG;AACH,wBAAsB,KAAK,CACzB,mBAAmB,EAAE,MAAM,GAAG,YAAY,EAC1C,WAAW,UAAQ,GAClB,OAAO,CAAC,WAAW,CAAC,CAuCtB"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,YAAY,EACV,WAAW,EACX,aAAa,EACb,kBAAkB,EAClB,aAAa,EACb,aAAa,EACb,oBAAoB,EACpB,gBAAgB,EAChB,oBAAoB,EACpB,OAAO,EACP,QAAQ,EACR,QAAQ,EACR,UAAU,EACV,YAAY,EACZ,aAAa,EACb,iBAAiB,GAClB,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EACV,UAAU,EACV,UAAU,EACV,eAAe,EACf,gBAAgB,EAChB,WAAW,GACZ,MAAM,8BAA8B,CAAC;AAEtC,YAAY,EAAE,oBAAoB,EAAE,MAAM,6BAA6B,CAAC;AACxE,YAAY,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,YAAY,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,6BAA6B,CAAC;AAC/E,YAAY,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAC;AAYnE,OAAO,KAAK,EAAE,WAAW,EAAW,MAAM,8BAA8B,CAAC;AACzE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAElE,sCAAsC;AACtC,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7C,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,GAAG,SAAS,CAAC;CAC5C;AAED;;;GAGG;AACH,wBAAsB,KAAK,CACzB,mBAAmB,EAAE,MAAM,GAAG,YAAY,EAC1C,WAAW,UAAQ,GAClB,OAAO,CAAC,WAAW,CAAC,CA+CtB;AAED,uDAAuD;AACvD,MAAM,WAAW,yBAAyB;IACxC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,OAAO,EAAE,yBAAyB,GAAG,OAAO,CAAC,cAAc,CAAC,CAKpG"}
package/dist/index.js CHANGED
@@ -4,7 +4,10 @@ import { inferFunctionalDependencies } from './core/analysis/inferFds.js';
4
4
  import { check1nf } from './core/analysis/normalizeChecks/check1nf.js';
5
5
  import { check2nf } from './core/analysis/normalizeChecks/check2nf.js';
6
6
  import { check3nf } from './core/analysis/normalizeChecks/check3nf.js';
7
+ import { checkSoftDelete } from './core/analysis/normalizeChecks/checkSoftDelete.js';
8
+ import { checkFkIndexes } from './core/analysis/normalizeChecks/checkFkIndexes.js';
7
9
  import { parseInvariantsFile, invariantsToFds, validateInvariantsAgainstContract } from './core/invariants/parse.js';
10
+ import { generateInvariantsFile } from './core/invariants/generate.js';
8
11
  /**
9
12
  * Run a full audit on a Prisma schema file.
10
13
  * Returns the constraint contract and normalization findings.
@@ -20,18 +23,25 @@ export async function audit(schemaPathOrOptions, noTimestamp = false) {
20
23
  // Merge invariant-declared FDs if provided
21
24
  let allFds = schemaFds;
22
25
  let invariantFindings = [];
26
+ let suppress = [];
23
27
  if (options.invariantsPath !== undefined) {
24
- const invariants = parseInvariantsFile(options.invariantsPath);
25
- const invariantFds = invariantsToFds(invariants);
28
+ const parsedInvariants = parseInvariantsFile(options.invariantsPath);
29
+ const invariantFds = invariantsToFds(parsedInvariants.invariants);
26
30
  allFds = [...schemaFds, ...invariantFds];
27
- invariantFindings = validateInvariantsAgainstContract(invariants, contract);
31
+ invariantFindings = validateInvariantsAgainstContract(parsedInvariants.invariants, contract);
32
+ suppress = parsedInvariants.suppress;
28
33
  }
29
- const findings = [
34
+ const allFindings = [
30
35
  ...check1nf(contract),
31
36
  ...check2nf(contract, allFds),
32
37
  ...check3nf(contract, allFds),
38
+ ...checkSoftDelete(contract),
39
+ ...checkFkIndexes(contract),
33
40
  ...invariantFindings,
34
41
  ];
42
+ const findings = suppress.length > 0
43
+ ? allFindings.filter((f) => !isSuppressed(f, suppress))
44
+ : allFindings;
35
45
  return {
36
46
  contract,
37
47
  findings,
@@ -43,4 +53,45 @@ export async function audit(schemaPathOrOptions, noTimestamp = false) {
43
53
  },
44
54
  };
45
55
  }
56
+ /**
57
+ * Generate an invariants file from existing schema constraints.
58
+ * Parses the schema, extracts constraints, and produces an InvariantsFile
59
+ * with PK and unique FDs and auto-generated notes.
60
+ */
61
+ export async function generateInvariants(options) {
62
+ const parsed = await parseSchema(options.schemaPath);
63
+ const contract = extractContract(parsed);
64
+ const fds = inferFunctionalDependencies(contract);
65
+ return generateInvariantsFile(contract, fds);
66
+ }
67
+ /**
68
+ * Check if a finding is suppressed by a suppress entry.
69
+ * Supports RULE:Model and RULE:Model.field patterns.
70
+ */
71
+ function isSuppressed(finding, suppress) {
72
+ for (const entry of suppress) {
73
+ const colonIdx = entry.indexOf(':');
74
+ const rule = entry.slice(0, colonIdx);
75
+ const target = entry.slice(colonIdx + 1);
76
+ if (rule !== finding.rule) {
77
+ continue;
78
+ }
79
+ const dotIdx = target.indexOf('.');
80
+ if (dotIdx === -1) {
81
+ // RULE:Model — suppress all findings for this model
82
+ if (target === finding.model) {
83
+ return true;
84
+ }
85
+ }
86
+ else {
87
+ // RULE:Model.field — suppress only the specific field
88
+ const model = target.slice(0, dotIdx);
89
+ const field = target.slice(dotIdx + 1);
90
+ if (model === finding.model && field === finding.field) {
91
+ return true;
92
+ }
93
+ }
94
+ }
95
+ return false;
96
+ }
46
97
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6BA,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,EAAE,2BAA2B,EAAE,MAAM,6BAA6B,CAAC;AAC1E,OAAO,EAAE,QAAQ,EAAE,MAAM,6CAA6C,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,6CAA6C,CAAC;AACvE,OAAO,EAAE,QAAQ,EAAE,MAAM,6CAA6C,CAAC;AACvE,OAAO,EAAE,mBAAmB,EAAE,eAAe,EAAE,iCAAiC,EAAE,MAAM,4BAA4B,CAAC;AAUrH;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,KAAK,CACzB,mBAA0C,EAC1C,WAAW,GAAG,KAAK;IAEnB,MAAM,OAAO,GACX,OAAO,mBAAmB,KAAK,QAAQ;QACrC,CAAC,CAAC,EAAE,UAAU,EAAE,mBAAmB,EAAE,WAAW,EAAE;QAClD,CAAC,CAAC,mBAAmB,CAAC;IAE1B,MAAM,mBAAmB,GAAG,OAAO,CAAC,WAAW,KAAK,IAAI,CAAC;IAEzD,MAAM,MAAM,GAAG,MAAM,WAAW,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,eAAe,CAAC,MAAM,CAAC,CAAC;IACzC,MAAM,SAAS,GAAG,2BAA2B,CAAC,QAAQ,CAAC,CAAC;IAExD,2CAA2C;IAC3C,IAAI,MAAM,GAAG,SAAS,CAAC;IACvB,IAAI,iBAAiB,GAAuB,EAAE,CAAC;IAC/C,IAAI,OAAO,CAAC,cAAc,KAAK,SAAS,EAAE,CAAC;QACzC,MAAM,UAAU,GAAG,mBAAmB,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QAC/D,MAAM,YAAY,GAAG,eAAe,CAAC,UAAU,CAAC,CAAC;QACjD,MAAM,GAAG,CAAC,GAAG,SAAS,EAAE,GAAG,YAAY,CAAC,CAAC;QACzC,iBAAiB,GAAG,iCAAiC,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAC9E,CAAC;IAED,MAAM,QAAQ,GAAG;QACf,GAAG,QAAQ,CAAC,QAAQ,CAAC;QACrB,GAAG,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC7B,GAAG,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;QAC7B,GAAG,iBAAiB;KACrB,CAAC;IAEF,OAAO;QACL,QAAQ;QACR,QAAQ;QACR,QAAQ,EAAE;YACR,UAAU,EAAE,OAAO,CAAC,UAAU;YAC9B,SAAS,EAAE,mBAAmB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YAChE,UAAU,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM;YAClC,YAAY,EAAE,QAAQ,CAAC,MAAM;SAC9B;KACF,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "prisma-schema-auditor",
3
- "version": "1.0.2",
3
+ "version": "1.2.0",
4
4
  "description": "Static analysis for Prisma schemas: deterministic constraint contracts + normalization lint findings (1NF/2NF; 3NF+ via invariants)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",