@suss/contract-prisma 0.33.0 → 0.34.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -19
- package/dist/index.d.cts +17 -6
- package/dist/index.d.ts +17 -6
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,43 +1,43 @@
|
|
|
1
1
|
# @suss/contract-prisma
|
|
2
2
|
|
|
3
|
-
Part of [suss](https://github.com/nimbuscloud-ai/suss), which reads both sides of every call in a repository and
|
|
3
|
+
Part of [suss](https://github.com/nimbuscloud-ai/suss), which reads both sides of every call in a repository and reports where the two disagree.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
This package builds suss `BehavioralSummary[]` from a [Prisma](https://www.prisma.io/) schema. A `schema.prisma` file declares every table an application has and every column on it, and the code reads and writes against that. This reader turns the schema into storage boundaries, so a query that touches a column nobody declared becomes a finding.
|
|
6
6
|
|
|
7
7
|
## What this package reads
|
|
8
8
|
|
|
9
|
-
`@suss/contract-prisma` parses `schema.prisma` with [`@mrleebo/prisma-ast`](https://github.com/MrLeebo/prisma-ast), a parser that does not pull in the Prisma runtime
|
|
9
|
+
`@suss/contract-prisma` parses `schema.prisma` with [`@mrleebo/prisma-ast`](https://github.com/MrLeebo/prisma-ast), a parser that does not pull in the Prisma runtime. Reading a schema never needs a generated client or a database connection.
|
|
10
10
|
|
|
11
|
-
The `datasource` block decides whether anything comes out. A `provider` of `postgresql`, `postgres`, `mysql
|
|
11
|
+
The `datasource` block decides whether anything comes out. A `provider` of `postgresql`, `postgres`, `mysql` or `sqlite` sets the storage system on every boundary. Any other provider, such as MongoDB, returns an empty array, since document storage is a different kind of boundary.
|
|
12
12
|
|
|
13
13
|
## What it produces
|
|
14
14
|
|
|
15
|
-
One `library`-kind summary per `model` and per `view`, with a storage boundary binding: `recognition: "prisma"`, the storage system from the datasource, a scope, and the model name as the container. The checker's storage pass pairs
|
|
15
|
+
One `library`-kind summary per `model` and per `view`, with a storage boundary binding: `recognition: "prisma"`, the storage system from the datasource, a scope, and the model name as the container. The checker's storage pass pairs these with the `interaction(class: "storage-access")` effects the adapters find in code. In a monorepo with several Prisma schemas, pass a different `scope` for each schema so their pairings stay separate. The default is `"default"`.
|
|
16
16
|
|
|
17
17
|
The behavior is in `metadata.storageContract`:
|
|
18
18
|
|
|
19
|
-
- `fieldSet: "exhaustive"`. A Prisma model declares every column its table has, so a column the code touches
|
|
19
|
+
- `fieldSet: "exhaustive"`. A Prisma model declares every column its table has, so a column the code touches that is missing from this list is unknown, and not merely unlisted.
|
|
20
20
|
- `fields`, one entry per column, with the Prisma type, whether the field is optional (`nullable`), and `primary` / `unique` from the `@id` and `@unique` field attributes. Scalar fields (`Int`, `BigInt`, `Float`, `Decimal`, `String`, `Boolean`, `DateTime`, `Json`, `Bytes`) and enum fields are columns.
|
|
21
|
-
- Relation fields
|
|
22
|
-
- A many-to-many relation with no explicit join model gets a `joinContainer` entry instead,
|
|
23
|
-
- A model with any relation also gets a derived `_count` entry,
|
|
24
|
-
- `indexes`, from `@@index([...])` (not unique), `@@unique([...])
|
|
25
|
-
- `physicalTable`, when `@@map("...")` gives the table a SQL name different from the model name. That
|
|
21
|
+
- Relation fields appear too, marked `derived`, because the Prisma client accepts them in an `include` or a `select` even though no column of that name exists. Leaving them out of a contract marked exhaustive would report working code as reading an undeclared field. When the model stores the foreign key, the columns listed in `@relation(fields: [...])` are included as `relationKey`. Prisma only allows that argument on one side of a relation, so the other side, and every implicit many-to-many, has no key.
|
|
22
|
+
- A many-to-many relation with no explicit join model gets a `joinContainer` entry instead, which gives the table Prisma manages for it. See below.
|
|
23
|
+
- A model with any relation also gets a derived `_count` entry, since the client exposes relation counts under that name.
|
|
24
|
+
- `indexes`, from `@@index([...])` (not unique), `@@unique([...])` and `@@id([...])` (both unique).
|
|
25
|
+
- `physicalTable`, when `@@map("...")` gives the table a SQL name different from the model name. That lets it pair with code that uses SQL names directly, such as Drizzle's `pgTable("users")` or a raw query.
|
|
26
26
|
|
|
27
|
-
These summaries have no inputs and no transitions. A table is a
|
|
27
|
+
These summaries have no inputs and no transitions. A table is a set of columns the code has to agree with, and it does not return anything the way a function does. Confidence is `declared` at `high`, since the schema states all of this explicitly.
|
|
28
28
|
|
|
29
29
|
### Implicit many-to-many join tables
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
When two list fields point at each other and neither side declares `@relation(fields: [...])`, that is Prisma's implicit many-to-many. Prisma creates and manages the join table itself, and the schema never declares a model for it. This reader produces a summary for that table too, with the boundary Prisma would create. The container is `_Name` when the fields have `@relation("Name")`, and otherwise `_FirstToSecond`, with the two model names in alphabetical order. The table has two columns, `A` and `B`, keyed to whichever model sorts first and second. Each side of the relation gets `joinContainer` set to that table's container name, so a `connect`, `disconnect` or `set` through the field counts as a write there and is not lost.
|
|
32
32
|
|
|
33
|
-
An explicit join model, the kind written out by hand to
|
|
33
|
+
An explicit join model, the kind written out by hand to add extra columns, already has a boundary through the ordinary model path above, and none of this applies to it.
|
|
34
34
|
|
|
35
35
|
## What it does not read
|
|
36
36
|
|
|
37
|
-
- **Non-relational providers.** A MongoDB schema comes back empty
|
|
38
|
-
- **Composite types and unsupported field types.** A field whose type is
|
|
37
|
+
- **Non-relational providers.** A MongoDB schema comes back empty. Document storage is separate work.
|
|
38
|
+
- **Composite types and unsupported field types.** A field whose type is not a scalar, an enum or another model is skipped, so the reader never guesses. That covers `Unsupported(...)`, a Mongo composite type, and a typo.
|
|
39
39
|
- **Array scalar fields.** `String[]` is dropped along with relation lists, because the walk treats every array field as a relation array.
|
|
40
|
-
- **Native type attributes.** `@db.VarChar(255)` and
|
|
40
|
+
- **Native type attributes.** `@db.VarChar(255)` and similar are not recorded. The field's Prisma type is what comes through.
|
|
41
41
|
- **`generator` blocks, the datasource URL, and relation cardinality** as behavior of their own.
|
|
42
42
|
|
|
43
43
|
## Worked example
|
|
@@ -66,7 +66,7 @@ suss check summaries/db.json summaries/app.json
|
|
|
66
66
|
|
|
67
67
|
The `User` summary declares `id` (primary), `email` (unique), a nullable `name`, the derived `posts` relation, a derived `_count`, an index on `email`, and `physicalTable: "users"`.
|
|
68
68
|
|
|
69
|
-
Or
|
|
69
|
+
Or from code:
|
|
70
70
|
|
|
71
71
|
```ts
|
|
72
72
|
import { prismaSchemaFileToSummaries } from "@suss/contract-prisma";
|
|
@@ -78,7 +78,7 @@ const summaries = prismaSchemaFileToSummaries("prisma/schema.prisma", {
|
|
|
78
78
|
|
|
79
79
|
## Where it fits in suss
|
|
80
80
|
|
|
81
|
-
|
|
81
|
+
The package depends only on `@suss/behavioral-ir`, for the IR types it produces, and `@mrleebo/prisma-ast`, for parsing. It does not extract from source code and does not use the language adapters. The Prisma pattern pack finds the query call sites that this contract pairs with.
|
|
82
82
|
|
|
83
83
|
## More
|
|
84
84
|
|
package/dist/index.d.cts
CHANGED
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
import { BehavioralSummary } from '@suss/behavioral-ir';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Reads a Prisma schema into one storage summary per model and view, plus
|
|
5
|
+
* one per implicit many-to-many join table. The checker's storage pass
|
|
6
|
+
* pairs these with the `storage-access` interactions found in code.
|
|
7
|
+
*
|
|
8
|
+
* `@mrleebo/prisma-ast` parses the schema, so no Prisma runtime or
|
|
9
|
+
* generated client is needed. A schema whose datasource is not relational,
|
|
10
|
+
* such as MongoDB, comes back empty. The README lists what else is left out.
|
|
11
|
+
*/
|
|
12
|
+
|
|
3
13
|
interface PrismaSchemaToSummariesOptions {
|
|
4
|
-
/**
|
|
14
|
+
/** The path recorded on each summary, in place of the default. */
|
|
5
15
|
source?: string;
|
|
6
16
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* distinct values per schema so pairings stay separate.
|
|
17
|
+
* Defaults to `"default"`. With several Prisma schemas in one repo, give
|
|
18
|
+
* each its own scope so their pairings stay separate.
|
|
10
19
|
*/
|
|
11
20
|
scope?: string;
|
|
12
21
|
}
|
|
13
22
|
/**
|
|
14
|
-
*
|
|
23
|
+
* Converts Prisma schema text into summaries. Returns an empty array when
|
|
24
|
+
* the datasource is not relational.
|
|
15
25
|
*/
|
|
16
26
|
declare function prismaSchemaToSummaries(source: string, options?: PrismaSchemaToSummariesOptions): BehavioralSummary[];
|
|
17
27
|
/**
|
|
18
|
-
*
|
|
28
|
+
* Reads a Prisma schema file and converts it. Throws when the file does
|
|
29
|
+
* not exist.
|
|
19
30
|
*/
|
|
20
31
|
declare function prismaSchemaFileToSummaries(schemaPath: string, options?: PrismaSchemaToSummariesOptions): BehavioralSummary[];
|
|
21
32
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,21 +1,32 @@
|
|
|
1
1
|
import { BehavioralSummary } from '@suss/behavioral-ir';
|
|
2
2
|
|
|
3
|
+
/**
|
|
4
|
+
* Reads a Prisma schema into one storage summary per model and view, plus
|
|
5
|
+
* one per implicit many-to-many join table. The checker's storage pass
|
|
6
|
+
* pairs these with the `storage-access` interactions found in code.
|
|
7
|
+
*
|
|
8
|
+
* `@mrleebo/prisma-ast` parses the schema, so no Prisma runtime or
|
|
9
|
+
* generated client is needed. A schema whose datasource is not relational,
|
|
10
|
+
* such as MongoDB, comes back empty. The README lists what else is left out.
|
|
11
|
+
*/
|
|
12
|
+
|
|
3
13
|
interface PrismaSchemaToSummariesOptions {
|
|
4
|
-
/**
|
|
14
|
+
/** The path recorded on each summary, in place of the default. */
|
|
5
15
|
source?: string;
|
|
6
16
|
/**
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
* distinct values per schema so pairings stay separate.
|
|
17
|
+
* Defaults to `"default"`. With several Prisma schemas in one repo, give
|
|
18
|
+
* each its own scope so their pairings stay separate.
|
|
10
19
|
*/
|
|
11
20
|
scope?: string;
|
|
12
21
|
}
|
|
13
22
|
/**
|
|
14
|
-
*
|
|
23
|
+
* Converts Prisma schema text into summaries. Returns an empty array when
|
|
24
|
+
* the datasource is not relational.
|
|
15
25
|
*/
|
|
16
26
|
declare function prismaSchemaToSummaries(source: string, options?: PrismaSchemaToSummariesOptions): BehavioralSummary[];
|
|
17
27
|
/**
|
|
18
|
-
*
|
|
28
|
+
* Reads a Prisma schema file and converts it. Throws when the file does
|
|
29
|
+
* not exist.
|
|
19
30
|
*/
|
|
20
31
|
declare function prismaSchemaFileToSummaries(schemaPath: string, options?: PrismaSchemaToSummariesOptions): BehavioralSummary[];
|
|
21
32
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suss/contract-prisma",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.34.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Turn a Prisma schema into a suss contract and check it against the queries your code runs on those tables.",
|
|
6
6
|
"keywords": [
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
],
|
|
15
15
|
"license": "Apache-2.0",
|
|
16
16
|
"author": "Nimbus Cloud AI LLC",
|
|
17
|
-
"homepage": "https://
|
|
17
|
+
"homepage": "https://suss.sh",
|
|
18
18
|
"repository": {
|
|
19
19
|
"type": "git",
|
|
20
20
|
"url": "git+https://github.com/nimbuscloud-ai/suss.git",
|
|
@@ -48,7 +48,7 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@mrleebo/prisma-ast": "^0.15.0",
|
|
51
|
-
"@suss/behavioral-ir": "0.
|
|
51
|
+
"@suss/behavioral-ir": "0.34.0"
|
|
52
52
|
},
|
|
53
53
|
"devDependencies": {
|
|
54
54
|
"tsup": "*",
|