prisma-schema-auditor 1.0.1

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 (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +62 -0
  3. package/dist/cli.d.ts +3 -0
  4. package/dist/cli.d.ts.map +1 -0
  5. package/dist/cli.js +132 -0
  6. package/dist/cli.js.map +1 -0
  7. package/dist/core/analysis/computeKeys.d.ts +13 -0
  8. package/dist/core/analysis/computeKeys.d.ts.map +1 -0
  9. package/dist/core/analysis/computeKeys.js +27 -0
  10. package/dist/core/analysis/computeKeys.js.map +1 -0
  11. package/dist/core/analysis/inferFds.d.ts +31 -0
  12. package/dist/core/analysis/inferFds.d.ts.map +1 -0
  13. package/dist/core/analysis/inferFds.js +87 -0
  14. package/dist/core/analysis/inferFds.js.map +1 -0
  15. package/dist/core/analysis/normalizeChecks/check1nf.d.ts +11 -0
  16. package/dist/core/analysis/normalizeChecks/check1nf.d.ts.map +1 -0
  17. package/dist/core/analysis/normalizeChecks/check1nf.js +91 -0
  18. package/dist/core/analysis/normalizeChecks/check1nf.js.map +1 -0
  19. package/dist/core/analysis/normalizeChecks/check2nf.d.ts +13 -0
  20. package/dist/core/analysis/normalizeChecks/check2nf.d.ts.map +1 -0
  21. package/dist/core/analysis/normalizeChecks/check2nf.js +80 -0
  22. package/dist/core/analysis/normalizeChecks/check2nf.js.map +1 -0
  23. package/dist/core/analysis/normalizeChecks/check3nf.d.ts +18 -0
  24. package/dist/core/analysis/normalizeChecks/check3nf.d.ts.map +1 -0
  25. package/dist/core/analysis/normalizeChecks/check3nf.js +71 -0
  26. package/dist/core/analysis/normalizeChecks/check3nf.js.map +1 -0
  27. package/dist/core/invariants/parse.d.ts +19 -0
  28. package/dist/core/invariants/parse.d.ts.map +1 -0
  29. package/dist/core/invariants/parse.js +73 -0
  30. package/dist/core/invariants/parse.js.map +1 -0
  31. package/dist/core/invariants/schema.d.ts +24 -0
  32. package/dist/core/invariants/schema.d.ts.map +1 -0
  33. package/dist/core/invariants/schema.js +20 -0
  34. package/dist/core/invariants/schema.js.map +1 -0
  35. package/dist/core/prismaSchema/contract.d.ts +8 -0
  36. package/dist/core/prismaSchema/contract.d.ts.map +1 -0
  37. package/dist/core/prismaSchema/contract.js +107 -0
  38. package/dist/core/prismaSchema/contract.js.map +1 -0
  39. package/dist/core/prismaSchema/parse.d.ts +8 -0
  40. package/dist/core/prismaSchema/parse.d.ts.map +1 -0
  41. package/dist/core/prismaSchema/parse.js +46 -0
  42. package/dist/core/prismaSchema/parse.js.map +1 -0
  43. package/dist/core/prismaSchema/types.d.ts +39 -0
  44. package/dist/core/prismaSchema/types.d.ts.map +1 -0
  45. package/dist/core/prismaSchema/types.js +2 -0
  46. package/dist/core/prismaSchema/types.js.map +1 -0
  47. package/dist/core/report/reportTypes.d.ts +72 -0
  48. package/dist/core/report/reportTypes.d.ts.map +1 -0
  49. package/dist/core/report/reportTypes.js +2 -0
  50. package/dist/core/report/reportTypes.js.map +1 -0
  51. package/dist/core/report/toJson.d.ts +7 -0
  52. package/dist/core/report/toJson.d.ts.map +1 -0
  53. package/dist/core/report/toJson.js +27 -0
  54. package/dist/core/report/toJson.js.map +1 -0
  55. package/dist/core/report/toText.d.ts +6 -0
  56. package/dist/core/report/toText.d.ts.map +1 -0
  57. package/dist/core/report/toText.js +46 -0
  58. package/dist/core/report/toText.js.map +1 -0
  59. package/dist/index.d.ts +18 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +46 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/util/index.d.ts +5 -0
  64. package/dist/util/index.d.ts.map +1 -0
  65. package/dist/util/index.js +15 -0
  66. package/dist/util/index.js.map +1 -0
  67. package/package.json +84 -0
@@ -0,0 +1,18 @@
1
+ import type { ConstraintContract, Finding } from '../../report/reportTypes.js';
2
+ import type { FunctionalDependency } from '../inferFds.js';
3
+ /**
4
+ * Check for 3NF and BCNF violations using declared functional dependencies
5
+ * (invariant-declared FDs only).
6
+ *
7
+ * 3NF violation: X → A where X is not a superkey AND A is not part of
8
+ * any candidate key (transitive dependency).
9
+ *
10
+ * BCNF violation: X → A where X is not a superkey (regardless of whether
11
+ * A is in a candidate key).
12
+ *
13
+ * Only invariant-sourced FDs are checked, since:
14
+ * - PK/unique FDs are trivially satisfied by key constraints
15
+ * - FK FDs are cross-model references, not intra-model dependencies
16
+ */
17
+ export declare function check3nf(contract: ConstraintContract, fds: readonly FunctionalDependency[]): readonly Finding[];
18
+ //# sourceMappingURL=check3nf.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,71 @@
1
+ import { isSuperkey } from '../inferFds.js';
2
+ import { extractCandidateKeys } from '../computeKeys.js';
3
+ /**
4
+ * Check for 3NF and BCNF violations using declared functional dependencies
5
+ * (invariant-declared FDs only).
6
+ *
7
+ * 3NF violation: X → A where X is not a superkey AND A is not part of
8
+ * any candidate key (transitive dependency).
9
+ *
10
+ * BCNF violation: X → A where X is not a superkey (regardless of whether
11
+ * A is in a candidate key).
12
+ *
13
+ * Only invariant-sourced FDs are checked, since:
14
+ * - PK/unique FDs are trivially satisfied by key constraints
15
+ * - FK FDs are cross-model references, not intra-model dependencies
16
+ */
17
+ export function check3nf(contract, fds) {
18
+ const findings = [];
19
+ // Only check invariant-declared FDs (user-specified intra-model dependencies)
20
+ const checkableFds = fds.filter((fd) => fd.source === 'invariant');
21
+ // For superkey checks, only use intra-model FDs (exclude FK which are cross-model)
22
+ const intraModelFds = fds.filter((fd) => fd.source !== 'fk');
23
+ for (const fd of checkableFds) {
24
+ const model = contract.models.find((m) => m.name === fd.model);
25
+ if (model === undefined) {
26
+ continue;
27
+ }
28
+ const allFields = model.fields.map((f) => f.name);
29
+ // Skip if determinant is already a superkey (using only intra-model FDs)
30
+ if (isSuperkey(fd.determinant, allFields, intraModelFds, fd.model)) {
31
+ continue;
32
+ }
33
+ // Determinant is not a superkey - check each dependent attribute
34
+ const candidateKeys = extractCandidateKeys(contract, fd.model);
35
+ const candidateKeyFields = new Set(candidateKeys.flatMap((k) => [...k.fields]));
36
+ for (const dep of fd.dependent) {
37
+ // Skip self-determination (trivial FDs)
38
+ if (fd.determinant.includes(dep)) {
39
+ continue;
40
+ }
41
+ // Skip if the field doesn't exist in this model's contract
42
+ if (!allFields.includes(dep)) {
43
+ continue;
44
+ }
45
+ if (candidateKeyFields.has(dep)) {
46
+ // Dependent is in a candidate key - BCNF violation only (3NF is satisfied)
47
+ findings.push({
48
+ rule: 'BCNF_VIOLATION',
49
+ severity: 'info',
50
+ normalForm: 'BCNF',
51
+ model: fd.model,
52
+ 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
+ });
55
+ }
56
+ else {
57
+ // Dependent is not in any candidate key - 3NF violation (and BCNF)
58
+ findings.push({
59
+ rule: 'NF3_VIOLATION',
60
+ severity: 'error',
61
+ normalForm: '3NF',
62
+ model: fd.model,
63
+ 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.`,
65
+ });
66
+ }
67
+ }
68
+ }
69
+ return findings;
70
+ }
71
+ //# sourceMappingURL=check3nf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check3nf.js","sourceRoot":"","sources":["../../../../src/core/analysis/normalizeChecks/check3nf.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { InvariantsFile } from './schema.js';
2
+ import type { FunctionalDependency } from '../analysis/inferFds.js';
3
+ import type { ConstraintContract, Finding } from '../report/reportTypes.js';
4
+ /**
5
+ * Parse and validate an invariants JSON file.
6
+ * Returns the validated invariants or throws on invalid input.
7
+ */
8
+ export declare function parseInvariantsFile(filePath: string): InvariantsFile;
9
+ /**
10
+ * Convert parsed invariants into FunctionalDependency objects.
11
+ */
12
+ export declare function invariantsToFds(invariants: InvariantsFile): readonly FunctionalDependency[];
13
+ /**
14
+ * Validate that invariant-declared models and fields actually exist in the
15
+ * constraint contract. Returns findings for any references that don't match,
16
+ * so users get immediate feedback when their invariants file is stale or wrong.
17
+ */
18
+ export declare function validateInvariantsAgainstContract(invariants: InvariantsFile, contract: ConstraintContract): readonly Finding[];
19
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,73 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { invariantsFileSchema } from './schema.js';
3
+ /**
4
+ * Parse and validate an invariants JSON file.
5
+ * Returns the validated invariants or throws on invalid input.
6
+ */
7
+ export function parseInvariantsFile(filePath) {
8
+ const content = readFileSync(filePath, 'utf-8');
9
+ const raw = JSON.parse(content);
10
+ return invariantsFileSchema.parse(raw);
11
+ }
12
+ /**
13
+ * Convert parsed invariants into FunctionalDependency objects.
14
+ */
15
+ export function invariantsToFds(invariants) {
16
+ const fds = [];
17
+ for (const [modelName, modelInvariants] of Object.entries(invariants)) {
18
+ if (modelInvariants.functionalDependencies !== undefined) {
19
+ for (const fd of modelInvariants.functionalDependencies) {
20
+ fds.push({
21
+ determinant: fd.determinant,
22
+ dependent: fd.dependent,
23
+ model: modelName,
24
+ source: 'invariant',
25
+ });
26
+ }
27
+ }
28
+ }
29
+ return fds;
30
+ }
31
+ /**
32
+ * Validate that invariant-declared models and fields actually exist in the
33
+ * constraint contract. Returns findings for any references that don't match,
34
+ * so users get immediate feedback when their invariants file is stale or wrong.
35
+ */
36
+ export function validateInvariantsAgainstContract(invariants, contract) {
37
+ const findings = [];
38
+ const modelMap = new Map(contract.models.map((m) => [m.name, m]));
39
+ for (const [modelName, modelInvariants] of Object.entries(invariants)) {
40
+ const model = modelMap.get(modelName);
41
+ if (model === undefined) {
42
+ findings.push({
43
+ rule: 'INVARIANT_UNKNOWN_MODEL',
44
+ severity: 'warning',
45
+ normalForm: '3NF',
46
+ model: modelName,
47
+ field: null,
48
+ message: `Invariants reference model "${modelName}" which does not exist in the schema.`,
49
+ });
50
+ continue;
51
+ }
52
+ const fieldNames = new Set(model.fields.map((f) => f.name));
53
+ if (modelInvariants.functionalDependencies !== undefined) {
54
+ for (const fd of modelInvariants.functionalDependencies) {
55
+ const allReferencedFields = new Set([...fd.determinant, ...fd.dependent]);
56
+ for (const field of allReferencedFields) {
57
+ if (!fieldNames.has(field)) {
58
+ findings.push({
59
+ rule: 'INVARIANT_UNKNOWN_FIELD',
60
+ severity: 'warning',
61
+ normalForm: '3NF',
62
+ model: modelName,
63
+ field,
64
+ message: `Invariants reference field "${field}" which does not exist in model "${modelName}".`,
65
+ });
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+ return findings;
72
+ }
73
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../../src/core/invariants/parse.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { z } from 'zod/v4';
2
+ /**
3
+ * Zod schema for a single functional dependency declaration in invariants.
4
+ */
5
+ declare const functionalDependencySchema: z.ZodObject<{
6
+ determinant: z.ZodArray<z.ZodString>;
7
+ dependent: z.ZodArray<z.ZodString>;
8
+ }, z.core.$strip>;
9
+ /**
10
+ * Zod schema for the full invariants file.
11
+ * Top-level keys are model names, values are model invariants.
12
+ */
13
+ export declare const invariantsFileSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
14
+ functionalDependencies: z.ZodOptional<z.ZodArray<z.ZodObject<{
15
+ determinant: z.ZodArray<z.ZodString>;
16
+ dependent: z.ZodArray<z.ZodString>;
17
+ }, z.core.$strip>>>;
18
+ }, z.core.$strip>>;
19
+ /** Parsed type for a functional dependency. */
20
+ export type InvariantFd = z.infer<typeof functionalDependencySchema>;
21
+ /** Parsed type for the full invariants file. */
22
+ export type InvariantsFile = z.infer<typeof invariantsFileSchema>;
23
+ export {};
24
+ //# sourceMappingURL=schema.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,20 @@
1
+ import { z } from 'zod/v4';
2
+ /**
3
+ * Zod schema for a single functional dependency declaration in invariants.
4
+ */
5
+ const functionalDependencySchema = z.object({
6
+ determinant: z.array(z.string()).min(1),
7
+ dependent: z.array(z.string()).min(1),
8
+ });
9
+ /**
10
+ * Zod schema for model-level invariants.
11
+ */
12
+ const modelInvariantsSchema = z.object({
13
+ functionalDependencies: z.array(functionalDependencySchema).optional(),
14
+ });
15
+ /**
16
+ * Zod schema for the full invariants file.
17
+ * Top-level keys are model names, values are model invariants.
18
+ */
19
+ export const invariantsFileSchema = z.record(z.string(), modelInvariantsSchema);
20
+ //# sourceMappingURL=schema.js.map
@@ -0,0 +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"}
@@ -0,0 +1,8 @@
1
+ import type { ParseResult } from './types.js';
2
+ import type { ConstraintContract } from '../report/reportTypes.js';
3
+ /**
4
+ * Extract a deterministic constraint contract from parsed schema models.
5
+ * Produces sorted, stable output for diffing.
6
+ */
7
+ export declare function extractContract(parsed: ParseResult): ConstraintContract;
8
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../../../src/core/prismaSchema/contract.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAA0B,WAAW,EAAE,MAAM,YAAY,CAAC;AACtE,OAAO,KAAK,EACV,kBAAkB,EAOnB,MAAM,0BAA0B,CAAC;AAqBlC;;;GAGG;AACH,wBAAgB,eAAe,CAAC,MAAM,EAAE,WAAW,GAAG,kBAAkB,CAGvE"}
@@ -0,0 +1,107 @@
1
+ import { sortBy } from '../../util/index.js';
2
+ const DEFAULT_ON_DELETE = 'Cascade';
3
+ const DEFAULT_ON_UPDATE = 'Cascade';
4
+ const VALID_REFERENTIAL_ACTIONS = new Set([
5
+ 'Cascade',
6
+ 'Restrict',
7
+ 'NoAction',
8
+ 'SetNull',
9
+ 'SetDefault',
10
+ ]);
11
+ function toReferentialAction(value, fallback) {
12
+ if (value !== null && VALID_REFERENTIAL_ACTIONS.has(value)) {
13
+ return value;
14
+ }
15
+ return fallback;
16
+ }
17
+ /**
18
+ * Extract a deterministic constraint contract from parsed schema models.
19
+ * Produces sorted, stable output for diffing.
20
+ */
21
+ export function extractContract(parsed) {
22
+ const models = sortBy([...parsed.models], (m) => m.name).map(extractModelContract);
23
+ return { models };
24
+ }
25
+ function extractModelContract(model) {
26
+ const scalarFields = model.fields.filter((f) => f.kind !== 'object');
27
+ const fields = sortBy([...scalarFields], (f) => f.name).map(extractFieldContract);
28
+ const primaryKey = extractPrimaryKey(model);
29
+ const uniqueConstraints = extractUniqueConstraints(model);
30
+ const foreignKeys = extractForeignKeys(model);
31
+ return {
32
+ name: model.name,
33
+ fields,
34
+ primaryKey,
35
+ uniqueConstraints,
36
+ foreignKeys,
37
+ };
38
+ }
39
+ function extractFieldContract(field) {
40
+ return {
41
+ name: field.name,
42
+ type: field.type,
43
+ isNullable: !field.isRequired,
44
+ hasDefault: field.hasDefaultValue,
45
+ isList: field.isList,
46
+ };
47
+ }
48
+ function extractPrimaryKey(model) {
49
+ // Composite @@id() takes precedence
50
+ if (model.primaryKey !== null) {
51
+ return {
52
+ fields: [...model.primaryKey.fields],
53
+ isComposite: model.primaryKey.fields.length > 1,
54
+ };
55
+ }
56
+ // Single-field @id
57
+ const idField = model.fields.find((f) => f.isId);
58
+ if (idField !== undefined) {
59
+ return {
60
+ fields: [idField.name],
61
+ isComposite: false,
62
+ };
63
+ }
64
+ return null;
65
+ }
66
+ function extractUniqueConstraints(model) {
67
+ const constraints = [];
68
+ // Single-field @unique
69
+ for (const field of model.fields) {
70
+ if (field.isUnique && field.kind !== 'object') {
71
+ constraints.push({
72
+ name: null,
73
+ fields: [field.name],
74
+ isComposite: false,
75
+ });
76
+ }
77
+ }
78
+ // Composite @@unique()
79
+ for (const idx of model.uniqueIndexes) {
80
+ constraints.push({
81
+ name: idx.name,
82
+ fields: [...idx.fields],
83
+ isComposite: idx.fields.length > 1,
84
+ });
85
+ }
86
+ return sortBy(constraints, (c) => c.fields.join(','));
87
+ }
88
+ function extractForeignKeys(model) {
89
+ const fks = [];
90
+ for (const field of model.fields) {
91
+ if (field.kind === 'object' &&
92
+ field.relationFromFields !== null &&
93
+ field.relationFromFields.length > 0 &&
94
+ field.relationToFields !== null &&
95
+ field.relationToFields.length > 0) {
96
+ fks.push({
97
+ fields: [...field.relationFromFields],
98
+ referencedModel: field.type,
99
+ referencedFields: [...field.relationToFields],
100
+ onDelete: toReferentialAction(field.relationOnDelete, DEFAULT_ON_DELETE),
101
+ onUpdate: toReferentialAction(field.relationOnUpdate, DEFAULT_ON_UPDATE),
102
+ });
103
+ }
104
+ }
105
+ return sortBy(fks, (fk) => fk.fields.join(','));
106
+ }
107
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../../../src/core/prismaSchema/contract.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ParseResult } from './types.js';
2
+ /**
3
+ * Parse a Prisma schema file and return an internal representation.
4
+ * Uses getDMMF from @prisma/internals to parse the schema into DMMF,
5
+ * then transforms DMMF models/fields into our AuditModel[] structure.
6
+ */
7
+ export declare function parseSchema(schemaPath: string): Promise<ParseResult>;
8
+ //# sourceMappingURL=parse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.d.ts","sourceRoot":"","sources":["../../../src/core/prismaSchema/parse.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAA4C,WAAW,EAAE,MAAM,YAAY,CAAC;AAExF;;;;GAIG;AACH,wBAAsB,WAAW,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA2C1E"}
@@ -0,0 +1,46 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { getDMMF } from '@prisma/internals';
3
+ /**
4
+ * Parse a Prisma schema file and return an internal representation.
5
+ * Uses getDMMF from @prisma/internals to parse the schema into DMMF,
6
+ * then transforms DMMF models/fields into our AuditModel[] structure.
7
+ */
8
+ export async function parseSchema(schemaPath) {
9
+ const schemaContent = readFileSync(schemaPath, 'utf-8');
10
+ const dmmf = await getDMMF({ datamodel: schemaContent });
11
+ const models = dmmf.datamodel.models.map((model) => {
12
+ const fields = model.fields.map((field) => ({
13
+ name: field.name,
14
+ type: field.type,
15
+ kind: field.kind,
16
+ isList: field.isList,
17
+ isRequired: field.isRequired,
18
+ isId: field.isId,
19
+ isUnique: field.isUnique,
20
+ hasDefaultValue: field.hasDefaultValue,
21
+ relationName: field.relationName ?? null,
22
+ relationFromFields: field.relationFromFields !== undefined && field.relationFromFields.length > 0
23
+ ? field.relationFromFields
24
+ : null,
25
+ relationToFields: field.relationToFields !== undefined && field.relationToFields.length > 0
26
+ ? [...field.relationToFields]
27
+ : null,
28
+ relationOnDelete: field.relationOnDelete ?? null,
29
+ relationOnUpdate: field.relationOnUpdate ?? null,
30
+ documentation: field.documentation ?? null,
31
+ }));
32
+ const uniqueIndexes = model.uniqueIndexes.map((idx) => ({
33
+ name: idx.name,
34
+ fields: idx.fields,
35
+ }));
36
+ return {
37
+ name: model.name,
38
+ fields,
39
+ primaryKey: model.primaryKey !== null ? { fields: model.primaryKey.fields } : null,
40
+ uniqueIndexes,
41
+ documentation: model.documentation ?? null,
42
+ };
43
+ });
44
+ return { models };
45
+ }
46
+ //# sourceMappingURL=parse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parse.js","sourceRoot":"","sources":["../../../src/core/prismaSchema/parse.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACvC,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAG5C;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,UAAkB;IAClD,MAAM,aAAa,GAAG,YAAY,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;IACxD,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,EAAE,SAAS,EAAE,aAAa,EAAE,CAAC,CAAC;IAEzD,MAAM,MAAM,GAAiB,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAC/D,MAAM,MAAM,GAAiB,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;YACxD,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,MAAM,EAAE,KAAK,CAAC,MAAM;YACpB,UAAU,EAAE,KAAK,CAAC,UAAU;YAC5B,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,eAAe,EAAE,KAAK,CAAC,eAAe;YACtC,YAAY,EAAE,KAAK,CAAC,YAAY,IAAI,IAAI;YACxC,kBAAkB,EAChB,KAAK,CAAC,kBAAkB,KAAK,SAAS,IAAI,KAAK,CAAC,kBAAkB,CAAC,MAAM,GAAG,CAAC;gBAC3E,CAAC,CAAC,KAAK,CAAC,kBAAkB;gBAC1B,CAAC,CAAC,IAAI;YACV,gBAAgB,EACd,KAAK,CAAC,gBAAgB,KAAK,SAAS,IAAI,KAAK,CAAC,gBAAgB,CAAC,MAAM,GAAG,CAAC;gBACvE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,gBAAgB,CAAC;gBAC7B,CAAC,CAAC,IAAI;YACV,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,IAAI,IAAI;YAChD,gBAAgB,EAAE,KAAK,CAAC,gBAAgB,IAAI,IAAI;YAChD,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,IAAI;SAC3C,CAAC,CAAC,CAAC;QAEJ,MAAM,aAAa,GAAuB,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC;YAC1E,IAAI,EAAE,GAAG,CAAC,IAAI;YACd,MAAM,EAAE,GAAG,CAAC,MAAM;SACnB,CAAC,CAAC,CAAC;QAEJ,OAAO;YACL,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,MAAM;YACN,UAAU,EAAE,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,IAAI;YAClF,aAAa;YACb,aAAa,EAAE,KAAK,CAAC,aAAa,IAAI,IAAI;SAC3C,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,OAAO,EAAE,MAAM,EAAE,CAAC;AACpB,CAAC"}
@@ -0,0 +1,39 @@
1
+ /** Internal representation of a Prisma model field after DMMF parsing. */
2
+ export interface AuditField {
3
+ readonly name: string;
4
+ readonly type: string;
5
+ readonly kind: 'scalar' | 'object' | 'enum' | 'unsupported';
6
+ readonly isList: boolean;
7
+ readonly isRequired: boolean;
8
+ readonly isId: boolean;
9
+ readonly isUnique: boolean;
10
+ readonly hasDefaultValue: boolean;
11
+ readonly relationName: string | null;
12
+ readonly relationFromFields: readonly string[] | null;
13
+ readonly relationToFields: readonly string[] | null;
14
+ readonly relationOnDelete: string | null;
15
+ readonly relationOnUpdate: string | null;
16
+ readonly documentation: string | null;
17
+ }
18
+ /** Internal representation of a Prisma model after DMMF parsing. */
19
+ export interface AuditModel {
20
+ readonly name: string;
21
+ readonly fields: readonly AuditField[];
22
+ readonly primaryKey: AuditPrimaryKey | null;
23
+ readonly uniqueIndexes: readonly AuditUniqueIndex[];
24
+ readonly documentation: string | null;
25
+ }
26
+ /** Parsed primary key (composite). */
27
+ export interface AuditPrimaryKey {
28
+ readonly fields: readonly string[];
29
+ }
30
+ /** Parsed unique index. */
31
+ export interface AuditUniqueIndex {
32
+ readonly name: string | null;
33
+ readonly fields: readonly string[];
34
+ }
35
+ /** Result of parsing a Prisma schema. */
36
+ export interface ParseResult {
37
+ readonly models: readonly AuditModel[];
38
+ }
39
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/prismaSchema/types.ts"],"names":[],"mappings":"AAAA,0EAA0E;AAC1E,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,aAAa,CAAC;IAC5D,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IACrC,QAAQ,CAAC,kBAAkB,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IACtD,QAAQ,CAAC,gBAAgB,EAAE,SAAS,MAAM,EAAE,GAAG,IAAI,CAAC;IACpD,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IACzC,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED,oEAAoE;AACpE,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;IACvC,QAAQ,CAAC,UAAU,EAAE,eAAe,GAAG,IAAI,CAAC;IAC5C,QAAQ,CAAC,aAAa,EAAE,SAAS,gBAAgB,EAAE,CAAC;IACpD,QAAQ,CAAC,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CACvC;AAED,sCAAsC;AACtC,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,2BAA2B;AAC3B,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC;CACpC;AAED,yCAAyC;AACzC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAC;CACxC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/core/prismaSchema/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,72 @@
1
+ /** Severity levels for audit findings. */
2
+ export type Severity = 'error' | 'warning' | 'info';
3
+ /** Normal form levels. */
4
+ export type NormalForm = '1NF' | '2NF' | '3NF' | 'BCNF';
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';
7
+ /** A single normalization finding. */
8
+ export interface Finding {
9
+ readonly rule: RuleCode;
10
+ readonly severity: Severity;
11
+ readonly normalForm: NormalForm;
12
+ readonly model: string;
13
+ readonly field: string | null;
14
+ readonly message: string;
15
+ }
16
+ /** Referential action on a foreign key. */
17
+ export type ReferentialAction = 'Cascade' | 'Restrict' | 'NoAction' | 'SetNull' | 'SetDefault';
18
+ /** A primary key constraint. */
19
+ export interface PrimaryKeyConstraint {
20
+ readonly fields: readonly string[];
21
+ readonly isComposite: boolean;
22
+ }
23
+ /** A unique constraint. */
24
+ export interface UniqueConstraint {
25
+ readonly name: string | null;
26
+ readonly fields: readonly string[];
27
+ readonly isComposite: boolean;
28
+ }
29
+ /** A foreign key constraint. */
30
+ export interface ForeignKeyConstraint {
31
+ readonly fields: readonly string[];
32
+ readonly referencedModel: string;
33
+ readonly referencedFields: readonly string[];
34
+ readonly onDelete: ReferentialAction;
35
+ readonly onUpdate: ReferentialAction;
36
+ }
37
+ /** Field-level metadata in the contract. */
38
+ export interface FieldContract {
39
+ readonly name: string;
40
+ readonly type: string;
41
+ readonly isNullable: boolean;
42
+ readonly hasDefault: boolean;
43
+ readonly isList: boolean;
44
+ }
45
+ /** Model-level constraint contract. */
46
+ export interface ModelContract {
47
+ readonly name: string;
48
+ readonly fields: readonly FieldContract[];
49
+ readonly primaryKey: PrimaryKeyConstraint | null;
50
+ readonly uniqueConstraints: readonly UniqueConstraint[];
51
+ readonly foreignKeys: readonly ForeignKeyConstraint[];
52
+ }
53
+ /** The full constraint contract for a schema. */
54
+ export interface ConstraintContract {
55
+ readonly models: readonly ModelContract[];
56
+ }
57
+ /** Output format options. */
58
+ export type OutputFormat = 'json' | 'text';
59
+ /** The complete audit result. */
60
+ export interface AuditResult {
61
+ readonly contract: ConstraintContract;
62
+ readonly findings: readonly Finding[];
63
+ readonly metadata: AuditMetadata;
64
+ }
65
+ /** Metadata about the audit run. */
66
+ export interface AuditMetadata {
67
+ readonly schemaPath: string;
68
+ readonly timestamp: string | null;
69
+ readonly modelCount: number;
70
+ readonly findingCount: number;
71
+ }
72
+ //# sourceMappingURL=reportTypes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reportTypes.d.ts","sourceRoot":"","sources":["../../../src/core/report/reportTypes.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=reportTypes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reportTypes.js","sourceRoot":"","sources":["../../../src/core/report/reportTypes.ts"],"names":[],"mappings":""}
@@ -0,0 +1,7 @@
1
+ import type { AuditResult } from './reportTypes.js';
2
+ /**
3
+ * Serialize an AuditResult to a deterministic JSON string.
4
+ * Keys are sorted for stable diffing.
5
+ */
6
+ export declare function toJson(result: AuditResult, pretty: boolean): string;
7
+ //# sourceMappingURL=toJson.d.ts.map
@@ -0,0 +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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Serialize an AuditResult to a deterministic JSON string.
3
+ * Keys are sorted for stable diffing.
4
+ */
5
+ export function toJson(result, pretty) {
6
+ const sorted = sortKeysDeep(result);
7
+ return pretty
8
+ ? JSON.stringify(sorted, null, 2)
9
+ : JSON.stringify(sorted);
10
+ }
11
+ /** Recursively sort object keys for deterministic output. */
12
+ function sortKeysDeep(value) {
13
+ if (Array.isArray(value)) {
14
+ return value.map(sortKeysDeep);
15
+ }
16
+ if (value !== null && typeof value === 'object') {
17
+ const obj = value;
18
+ const keys = Object.keys(obj).sort();
19
+ const sorted = {};
20
+ for (const key of keys) {
21
+ sorted[key] = sortKeysDeep(obj[key]);
22
+ }
23
+ return sorted;
24
+ }
25
+ return value;
26
+ }
27
+ //# sourceMappingURL=toJson.js.map
@@ -0,0 +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"}