@kavo/prisma 0.5.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 +64 -0
- package/dist/datamodel.d.ts +48 -0
- package/dist/datamodel.d.ts.map +1 -0
- package/dist/datamodel.js +2 -0
- package/dist/datamodel.js.map +1 -0
- package/dist/error-mapping.d.ts +25 -0
- package/dist/error-mapping.d.ts.map +1 -0
- package/dist/error-mapping.js +52 -0
- package/dist/error-mapping.js.map +1 -0
- package/dist/filter-translator.d.ts +27 -0
- package/dist/filter-translator.d.ts.map +1 -0
- package/dist/filter-translator.js +94 -0
- package/dist/filter-translator.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure.d.ts +43 -0
- package/dist/infrastructure.d.ts.map +1 -0
- package/dist/infrastructure.js +48 -0
- package/dist/infrastructure.js.map +1 -0
- package/dist/metadata.d.ts +17 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +113 -0
- package/dist/metadata.js.map +1 -0
- package/dist/prisma-client-like.d.ts +23 -0
- package/dist/prisma-client-like.d.ts.map +1 -0
- package/dist/prisma-client-like.js +5 -0
- package/dist/prisma-client-like.js.map +1 -0
- package/dist/prisma-repository-adapter.d.ts +61 -0
- package/dist/prisma-repository-adapter.d.ts.map +1 -0
- package/dist/prisma-repository-adapter.js +267 -0
- package/dist/prisma-repository-adapter.js.map +1 -0
- package/package.json +40 -0
package/README.md
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# @kavo/prisma
|
|
2
|
+
|
|
3
|
+
Prisma adapter for Kavo: implements `RepositoryAdapter`
|
|
4
|
+
(`EntityReader` + `EntityWriter`) from `@kavo/core` over a Prisma Client
|
|
5
|
+
model delegate. `TransactionManager` is not implemented — see the
|
|
6
|
+
`@remarks` on that interface in `@kavo/core`.
|
|
7
|
+
|
|
8
|
+
**May depend on:** `@kavo/core`, `@prisma/client` (peer). **Never on:**
|
|
9
|
+
`@kavo/nest` or any framework.
|
|
10
|
+
|
|
11
|
+
Fully implemented: CRUD, filtering/sorting/pagination, soft delete
|
|
12
|
+
(explicit `softDelete.field` only — Prisma declares no delete-marker
|
|
13
|
+
column the way TypeORM's `@DeleteDateColumn` does), and relation
|
|
14
|
+
includes all run through this adapter.
|
|
15
|
+
|
|
16
|
+
## Usage
|
|
17
|
+
|
|
18
|
+
Prisma generates no runtime class for a model, so each entity needs a
|
|
19
|
+
caller-declared **marker class** as its `ClassRef` identity, matched by
|
|
20
|
+
name to the schema (`class Author {}` ↔ `model Author { … }`). See
|
|
21
|
+
`docs/adr/0017-prisma-marker-classes-and-entity-registry.md` for why.
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import { PrismaClient, Prisma } from "@prisma/client";
|
|
25
|
+
import { createPrismaKavo } from "@kavo/prisma";
|
|
26
|
+
|
|
27
|
+
class Author {
|
|
28
|
+
id!: number;
|
|
29
|
+
email!: string;
|
|
30
|
+
name!: string;
|
|
31
|
+
}
|
|
32
|
+
class Book {
|
|
33
|
+
id!: number;
|
|
34
|
+
title!: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const prisma = new PrismaClient();
|
|
38
|
+
const kavo = createPrismaKavo(prisma, {
|
|
39
|
+
datamodel: Prisma.dmmf.datamodel,
|
|
40
|
+
entities: [Author, Book],
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const authors = kavo.createCrud(Author);
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Set `caseInsensitiveFilters: false` when the connector isn't Postgres or
|
|
47
|
+
MongoDB — Prisma's `mode: "insensitive"` (used to translate the `ilike`
|
|
48
|
+
filter operator) is rejected outright by MySQL, SQLite, and SQL Server.
|
|
49
|
+
|
|
50
|
+
## Relation writes: explicit scalar foreign keys only
|
|
51
|
+
|
|
52
|
+
Per ADR-0014 ("associate by id, not deep writes"), a relation is set by
|
|
53
|
+
writing its scalar foreign-key field directly (`{ authorId: 5 }`), the
|
|
54
|
+
same contract `@kavo/typeorm` exposes. This requires the Prisma schema to
|
|
55
|
+
declare that foreign key as an explicit scalar field on the relation
|
|
56
|
+
(`authorId Int?` alongside `author Author? @relation(fields: [authorId],
|
|
57
|
+
references: [id])`) — the [documented best
|
|
58
|
+
practice](https://www.prisma.io/docs/orm/prisma-schema/data-model/relations)
|
|
59
|
+
for any 1:1/1:n relation. An **implicit many-to-many** relation (no
|
|
60
|
+
scalar field on either side, Prisma manages the join table itself) has no
|
|
61
|
+
foreign key for a DTO to expose, so it cannot be associated through the
|
|
62
|
+
normal write path — a custom operation handler reaching for the raw
|
|
63
|
+
Prisma Client is the escape hatch, same as any write shape Kavo doesn't
|
|
64
|
+
model directly.
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The subset of Prisma's DMMF (`Prisma.dmmf.datamodel`) this adapter reads.
|
|
3
|
+
* Modeled locally rather than imported from `@prisma/client` or
|
|
4
|
+
* `@prisma/generator-helper`, for the same reason as {@link
|
|
5
|
+
* PrismaClientLike}: `Prisma.dmmf` is a stable, documented runtime export
|
|
6
|
+
* (the mechanism Prisma's own generator ecosystem — `zod-prisma`,
|
|
7
|
+
* `prisma-erd-generator`, and others — relies on to read a schema without
|
|
8
|
+
* a generated client), but its *type* only resolves post-generation. A
|
|
9
|
+
* duck-typed subset keeps `@kavo/prisma` buildable without one, and the
|
|
10
|
+
* real `Prisma.dmmf.datamodel` a consumer passes in satisfies this
|
|
11
|
+
* structurally.
|
|
12
|
+
*/
|
|
13
|
+
export interface PrismaDatamodel {
|
|
14
|
+
readonly models: readonly PrismaModel[];
|
|
15
|
+
readonly enums: readonly PrismaEnum[];
|
|
16
|
+
}
|
|
17
|
+
export interface PrismaModel {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
readonly fields: readonly PrismaField[];
|
|
20
|
+
}
|
|
21
|
+
export interface PrismaEnum {
|
|
22
|
+
readonly name: string;
|
|
23
|
+
readonly values: readonly {
|
|
24
|
+
readonly name: string;
|
|
25
|
+
}[];
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* One field of a DMMF model. `kind: "object"` is a relation edge (Prisma's
|
|
29
|
+
* term for it, not core's `FieldKind`); every other kind is a scalar
|
|
30
|
+
* column. `default` is a literal (`"active"`, `42`) for an ordinary
|
|
31
|
+
* column default, or an object (`{ name: "autoincrement", args: [] }`,
|
|
32
|
+
* `{ name: "now", args: [] }`) for a function-style default Prisma or the
|
|
33
|
+
* database computes at write time — that shape distinction is what
|
|
34
|
+
* separates a merely-defaulted column from a generated one, mirroring
|
|
35
|
+
* `@kavo/typeorm`'s `isGenerated || isCreateDate || …` union.
|
|
36
|
+
*/
|
|
37
|
+
export interface PrismaField {
|
|
38
|
+
readonly name: string;
|
|
39
|
+
readonly kind: "scalar" | "object" | "enum" | "unsupported";
|
|
40
|
+
readonly type: string;
|
|
41
|
+
readonly isId: boolean;
|
|
42
|
+
readonly isList: boolean;
|
|
43
|
+
readonly isRequired: boolean;
|
|
44
|
+
readonly isUpdatedAt?: boolean;
|
|
45
|
+
readonly hasDefaultValue: boolean;
|
|
46
|
+
readonly default?: unknown;
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=datamodel.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"datamodel.d.ts","sourceRoot":"","sources":["../src/datamodel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;IACxC,QAAQ,CAAC,KAAK,EAAE,SAAS,UAAU,EAAE,CAAC;CACvC;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS,WAAW,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACvD;AAED;;;;;;;;;GASG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,GAAG,aAAa,CAAC;IAC5D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,OAAO,CAAC;IAC7B,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC;IAC/B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAC;IAClC,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAC;CAC5B"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"datamodel.js","sourceRoot":"","sources":["../src/datamodel.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { ErrorContext } from "@kavo/core";
|
|
2
|
+
import { KavoException } from "@kavo/core";
|
|
3
|
+
/**
|
|
4
|
+
* The error-mapping table: Prisma driver error → Kavo exception.
|
|
5
|
+
*
|
|
6
|
+
* | Prisma code | Exception |
|
|
7
|
+
* | ----------------------------------------- | ---------------------------------- |
|
|
8
|
+
* | P2002 unique constraint failed | ConflictException |
|
|
9
|
+
* | P2003 / P2014 foreign-key / relation violation | ConflictException |
|
|
10
|
+
* | P2025 record required for write not found | NotFoundException |
|
|
11
|
+
* | P2034 transaction write conflict/deadlock | TransactionException (retryable) |
|
|
12
|
+
* | anything else | PersistenceException with `cause` |
|
|
13
|
+
*
|
|
14
|
+
* Every code Prisma reports is driver-agnostic already (Prisma normalizes
|
|
15
|
+
* Postgres/MySQL/SQLite/… errors into its own `P####` catalog), so unlike
|
|
16
|
+
* `@kavo/typeorm`'s table this one needs no per-database code lists.
|
|
17
|
+
*
|
|
18
|
+
* **Soft delete and unique indexes.** Same caveat as `@kavo/typeorm`: a
|
|
19
|
+
* soft-deleted row still occupies its unique indexes, so re-creating "the
|
|
20
|
+
* same" row after a soft delete raises P2002 — mapped here to a 409 like
|
|
21
|
+
* any other conflict. Kavo never rewrites indexes; the fix is a partial
|
|
22
|
+
* unique index scoped to live rows.
|
|
23
|
+
*/
|
|
24
|
+
export declare function mapDriverError(error: unknown, context: ErrorContext): KavoException;
|
|
25
|
+
//# sourceMappingURL=error-mapping.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-mapping.d.ts","sourceRoot":"","sources":["../src/error-mapping.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAEL,aAAa,EAId,MAAM,YAAY,CAAC;AAoBpB;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,GAAG,aAAa,CAwBnF"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { ConflictException, KavoException, NotFoundException, PersistenceException, TransactionException, } from "@kavo/core";
|
|
2
|
+
function isPrismaKnownRequestError(error) {
|
|
3
|
+
return (error instanceof Error &&
|
|
4
|
+
error.constructor.name === "PrismaClientKnownRequestError" &&
|
|
5
|
+
typeof error.code === "string");
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* The error-mapping table: Prisma driver error → Kavo exception.
|
|
9
|
+
*
|
|
10
|
+
* | Prisma code | Exception |
|
|
11
|
+
* | ----------------------------------------- | ---------------------------------- |
|
|
12
|
+
* | P2002 unique constraint failed | ConflictException |
|
|
13
|
+
* | P2003 / P2014 foreign-key / relation violation | ConflictException |
|
|
14
|
+
* | P2025 record required for write not found | NotFoundException |
|
|
15
|
+
* | P2034 transaction write conflict/deadlock | TransactionException (retryable) |
|
|
16
|
+
* | anything else | PersistenceException with `cause` |
|
|
17
|
+
*
|
|
18
|
+
* Every code Prisma reports is driver-agnostic already (Prisma normalizes
|
|
19
|
+
* Postgres/MySQL/SQLite/… errors into its own `P####` catalog), so unlike
|
|
20
|
+
* `@kavo/typeorm`'s table this one needs no per-database code lists.
|
|
21
|
+
*
|
|
22
|
+
* **Soft delete and unique indexes.** Same caveat as `@kavo/typeorm`: a
|
|
23
|
+
* soft-deleted row still occupies its unique indexes, so re-creating "the
|
|
24
|
+
* same" row after a soft delete raises P2002 — mapped here to a 409 like
|
|
25
|
+
* any other conflict. Kavo never rewrites indexes; the fix is a partial
|
|
26
|
+
* unique index scoped to live rows.
|
|
27
|
+
*/
|
|
28
|
+
export function mapDriverError(error, context) {
|
|
29
|
+
if (error instanceof KavoException)
|
|
30
|
+
return error;
|
|
31
|
+
const entity = context.entityName ?? "entity";
|
|
32
|
+
if (isPrismaKnownRequestError(error)) {
|
|
33
|
+
switch (error.code) {
|
|
34
|
+
case "P2002":
|
|
35
|
+
case "P2003":
|
|
36
|
+
case "P2014":
|
|
37
|
+
return new ConflictException({ messageParams: { entity }, context, cause: error });
|
|
38
|
+
case "P2025":
|
|
39
|
+
return new NotFoundException({ messageParams: { entity, id: "" }, context, cause: error });
|
|
40
|
+
case "P2034":
|
|
41
|
+
return new TransactionException({ retryable: true, context, cause: error });
|
|
42
|
+
default:
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return new PersistenceException({
|
|
47
|
+
messageParams: { operation: context.operation ?? "unknown" },
|
|
48
|
+
context,
|
|
49
|
+
cause: error,
|
|
50
|
+
});
|
|
51
|
+
}
|
|
52
|
+
//# sourceMappingURL=error-mapping.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"error-mapping.js","sourceRoot":"","sources":["../src/error-mapping.ts"],"names":[],"mappings":"AACA,OAAO,EACL,iBAAiB,EACjB,aAAa,EACb,iBAAiB,EACjB,oBAAoB,EACpB,oBAAoB,GACrB,MAAM,YAAY,CAAC;AAYpB,SAAS,yBAAyB,CAAC,KAAc;IAC/C,OAAO,CACL,KAAK,YAAY,KAAK;QACtB,KAAK,CAAC,WAAW,CAAC,IAAI,KAAK,+BAA+B;QAC1D,OAAQ,KAA4B,CAAC,IAAI,KAAK,QAAQ,CACvD,CAAC;AACJ,CAAC;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,MAAM,UAAU,cAAc,CAAC,KAAc,EAAE,OAAqB;IAClE,IAAI,KAAK,YAAY,aAAa;QAAE,OAAO,KAAK,CAAC;IAEjD,MAAM,MAAM,GAAG,OAAO,CAAC,UAAU,IAAI,QAAQ,CAAC;IAC9C,IAAI,yBAAyB,CAAC,KAAK,CAAC,EAAE,CAAC;QACrC,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;YACnB,KAAK,OAAO,CAAC;YACb,KAAK,OAAO,CAAC;YACb,KAAK,OAAO;gBACV,OAAO,IAAI,iBAAiB,CAAC,EAAE,aAAa,EAAE,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YACrF,KAAK,OAAO;gBACV,OAAO,IAAI,iBAAiB,CAAC,EAAE,aAAa,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAC7F,KAAK,OAAO;gBACV,OAAO,IAAI,oBAAoB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;YAC9E;gBACE,MAAM;QACV,CAAC;IACH,CAAC;IAED,OAAO,IAAI,oBAAoB,CAAC;QAC9B,aAAa,EAAE,EAAE,SAAS,EAAE,OAAO,CAAC,SAAS,IAAI,SAAS,EAAE;QAC5D,OAAO;QACP,KAAK,EAAE,KAAK;KACb,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Filter } from "@kavo/core";
|
|
2
|
+
/** A Prisma `where` input, built structurally — no generated type is required to build one. */
|
|
3
|
+
export type PrismaWhere = Record<string, unknown>;
|
|
4
|
+
/**
|
|
5
|
+
* Whether the target connector supports Prisma's `mode: "insensitive"`
|
|
6
|
+
* string filter — Postgres and MongoDB do; MySQL, SQLite, and SQL Server
|
|
7
|
+
* reject the argument outright. There is no reliable way to detect this
|
|
8
|
+
* from the DMMF at runtime, so it is an explicit, caller-declared setting
|
|
9
|
+
* (see `PrismaInfrastructureOptions.caseInsensitiveFilters`) rather than a
|
|
10
|
+
* guess. On an unsupported connector, `ILIKE` degrades to the same
|
|
11
|
+
* translation as `LIKE` — on SQLite in particular this is not a loss of
|
|
12
|
+
* behavior, since SQLite's own `LIKE` is already ASCII case-insensitive.
|
|
13
|
+
*/
|
|
14
|
+
export interface FilterTranslatorOptions {
|
|
15
|
+
readonly caseInsensitiveFilters: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Filter AST → Prisma `where` object.
|
|
19
|
+
*
|
|
20
|
+
* Unlike `@kavo/typeorm`'s translator, this needs no query-builder state
|
|
21
|
+
* (no join aliases, no parameter numbering): Prisma's `where` already
|
|
22
|
+
* nests relation paths natively (`{ author: { name: { equals: "Ada" } } }`),
|
|
23
|
+
* so a relation-path field (`author.name`) simply nests the same
|
|
24
|
+
* translation one level deeper instead of adding a join.
|
|
25
|
+
*/
|
|
26
|
+
export declare function translateFilter<Entity>(filter: Filter<Entity>, options: FilterTranslatorOptions): PrismaWhere | undefined;
|
|
27
|
+
//# sourceMappingURL=filter-translator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-translator.d.ts","sourceRoot":"","sources":["../src/filter-translator.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAmD,MAAM,YAAY,CAAC;AAG1F,+FAA+F;AAC/F,MAAM,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAElD;;;;;;;;;GASG;AACH,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;CAC1C;AAED;;;;;;;;GAQG;AACH,wBAAgB,eAAe,CAAC,MAAM,EACpC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EACtB,OAAO,EAAE,uBAAuB,GAC/B,WAAW,GAAG,SAAS,CAIzB"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { assertNever } from "@kavo/core";
|
|
2
|
+
/**
|
|
3
|
+
* Filter AST → Prisma `where` object.
|
|
4
|
+
*
|
|
5
|
+
* Unlike `@kavo/typeorm`'s translator, this needs no query-builder state
|
|
6
|
+
* (no join aliases, no parameter numbering): Prisma's `where` already
|
|
7
|
+
* nests relation paths natively (`{ author: { name: { equals: "Ada" } } }`),
|
|
8
|
+
* so a relation-path field (`author.name`) simply nests the same
|
|
9
|
+
* translation one level deeper instead of adding a join.
|
|
10
|
+
*/
|
|
11
|
+
export function translateFilter(filter, options) {
|
|
12
|
+
const root = filter.root;
|
|
13
|
+
if (root === null)
|
|
14
|
+
return undefined;
|
|
15
|
+
return translateExpression(root, options);
|
|
16
|
+
}
|
|
17
|
+
function translateExpression(expression, options) {
|
|
18
|
+
if (expression.kind === "condition") {
|
|
19
|
+
return translateCondition(expression, options);
|
|
20
|
+
}
|
|
21
|
+
if (expression.operator === "NOT") {
|
|
22
|
+
const child = expression.children[0];
|
|
23
|
+
return { NOT: child === undefined ? {} : translateExpression(child, options) };
|
|
24
|
+
}
|
|
25
|
+
const key = expression.operator === "OR" ? "OR" : "AND";
|
|
26
|
+
return { [key]: expression.children.map((child) => translateExpression(child, options)) };
|
|
27
|
+
}
|
|
28
|
+
/** Nest `condition.field` (`"author.name"`) into Prisma's native relation-path shape. */
|
|
29
|
+
function nest(field, leaf) {
|
|
30
|
+
const segments = field.split(".");
|
|
31
|
+
return segments.reduceRight((inner, segment) => ({ [segment]: inner }), leaf);
|
|
32
|
+
}
|
|
33
|
+
function translateCondition(condition, options) {
|
|
34
|
+
const field = condition.field;
|
|
35
|
+
const value = condition.value;
|
|
36
|
+
switch (condition.operator) {
|
|
37
|
+
case "EQ":
|
|
38
|
+
return nest(field, { equals: value });
|
|
39
|
+
case "NE":
|
|
40
|
+
return nest(field, { not: value });
|
|
41
|
+
case "GT":
|
|
42
|
+
return nest(field, { gt: value });
|
|
43
|
+
case "GTE":
|
|
44
|
+
return nest(field, { gte: value });
|
|
45
|
+
case "LT":
|
|
46
|
+
return nest(field, { lt: value });
|
|
47
|
+
case "LTE":
|
|
48
|
+
return nest(field, { lte: value });
|
|
49
|
+
case "IN":
|
|
50
|
+
return nest(field, { in: value });
|
|
51
|
+
case "NOT_IN":
|
|
52
|
+
return nest(field, { notIn: value });
|
|
53
|
+
case "LIKE":
|
|
54
|
+
return nest(field, likeToPrismaStringFilter(value));
|
|
55
|
+
case "ILIKE":
|
|
56
|
+
return nest(field, {
|
|
57
|
+
...likeToPrismaStringFilter(value),
|
|
58
|
+
...(options.caseInsensitiveFilters && { mode: "insensitive" }),
|
|
59
|
+
});
|
|
60
|
+
case "BETWEEN": {
|
|
61
|
+
const [low, high] = value;
|
|
62
|
+
return nest(field, { gte: low, lte: high });
|
|
63
|
+
}
|
|
64
|
+
case "IS_NULL":
|
|
65
|
+
return nest(field, { equals: null });
|
|
66
|
+
case "IS_NOT_NULL":
|
|
67
|
+
return nest(field, { not: null });
|
|
68
|
+
default:
|
|
69
|
+
// Every AST operator must translate to a predicate — proven total
|
|
70
|
+
// here at build time, same guarantee as `@kavo/typeorm`'s translator.
|
|
71
|
+
return assertNever(condition.operator, "filter operator");
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* `LIKE`/`ILIKE` carry SQL wildcard syntax (`%prefix%`, `A%`, `%suffix`)
|
|
76
|
+
* from the parser; Prisma has no raw pattern operator, only
|
|
77
|
+
* `contains`/`startsWith`/`endsWith`/`equals`. The wildcard *position*
|
|
78
|
+
* decides which one — `A%` is a prefix match (`startsWith`), not "contains
|
|
79
|
+
* A somewhere", so the two ends are checked independently rather than
|
|
80
|
+
* always falling back to `contains`.
|
|
81
|
+
*/
|
|
82
|
+
function likeToPrismaStringFilter(pattern) {
|
|
83
|
+
const leading = pattern.startsWith("%");
|
|
84
|
+
const trailing = pattern.endsWith("%") && pattern.length > 1;
|
|
85
|
+
const stripped = pattern.slice(leading ? 1 : 0, trailing ? -1 : undefined);
|
|
86
|
+
if (leading && trailing)
|
|
87
|
+
return { contains: stripped };
|
|
88
|
+
if (trailing)
|
|
89
|
+
return { startsWith: stripped };
|
|
90
|
+
if (leading)
|
|
91
|
+
return { endsWith: stripped };
|
|
92
|
+
return { equals: stripped };
|
|
93
|
+
}
|
|
94
|
+
//# sourceMappingURL=filter-translator.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter-translator.js","sourceRoot":"","sources":["../src/filter-translator.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export { buildEntityMetadata } from "./metadata.js";
|
|
2
|
+
export { mapDriverError } from "./error-mapping.js";
|
|
3
|
+
export { translateFilter, type PrismaWhere } from "./filter-translator.js";
|
|
4
|
+
export { PrismaRepositoryAdapter } from "./prisma-repository-adapter.js";
|
|
5
|
+
export { createPrismaInfrastructure, createPrismaKavo, type PrismaInfrastructureOptions } from "./infrastructure.js";
|
|
6
|
+
export { delegateName, type PrismaClientLike, type PrismaModelDelegate } from "./prisma-client-like.js";
|
|
7
|
+
export type { PrismaDatamodel, PrismaEnum, PrismaField, PrismaModel } from "./datamodel.js";
|
|
8
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAE,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,gBAAgB,EAAE,KAAK,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AACrH,OAAO,EAAE,YAAY,EAAE,KAAK,gBAAgB,EAAE,KAAK,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AACxG,YAAY,EAAE,eAAe,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export { buildEntityMetadata } from "./metadata.js";
|
|
2
|
+
export { mapDriverError } from "./error-mapping.js";
|
|
3
|
+
export { translateFilter } from "./filter-translator.js";
|
|
4
|
+
export { PrismaRepositoryAdapter } from "./prisma-repository-adapter.js";
|
|
5
|
+
export { createPrismaInfrastructure, createPrismaKavo } from "./infrastructure.js";
|
|
6
|
+
export { delegateName } from "./prisma-client-like.js";
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,eAAe,EAAoB,MAAM,wBAAwB,CAAC;AAC3E,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AACzE,OAAO,EAAE,0BAA0B,EAAE,gBAAgB,EAAoC,MAAM,qBAAqB,CAAC;AACrH,OAAO,EAAE,YAAY,EAAmD,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import type { ClassRef, CrudInfrastructure, KavoInstance, KavoOptions } from "@kavo/core";
|
|
2
|
+
import type { PrismaDatamodel } from "./datamodel.js";
|
|
3
|
+
import type { PrismaClientLike } from "./prisma-client-like.js";
|
|
4
|
+
/**
|
|
5
|
+
* What Prisma needs beyond the client instance, mirroring what TypeORM's
|
|
6
|
+
* `DataSource` supplies for free:
|
|
7
|
+
*
|
|
8
|
+
* - `datamodel` — Prisma's DMMF (`Prisma.dmmf.datamodel` from the
|
|
9
|
+
* generated client), the metadata seam's data source.
|
|
10
|
+
* - `entities` — every marker class this Kavo root will call `createCrud`
|
|
11
|
+
* with. Prisma models have no runtime class the way TypeORM's
|
|
12
|
+
* `@Entity()` classes do, so each entity needs a caller-declared class
|
|
13
|
+
* purely as `ClassRef` identity (`class Author {}` ↔ `model Author`,
|
|
14
|
+
* matched by name) — and a relation's target model name can only resolve
|
|
15
|
+
* back to *its* marker class if every marker class was registered up
|
|
16
|
+
* front. See `docs/adr/0017-prisma-marker-classes-and-entity-registry.md`.
|
|
17
|
+
*/
|
|
18
|
+
export interface PrismaInfrastructureOptions {
|
|
19
|
+
readonly datamodel: PrismaDatamodel;
|
|
20
|
+
readonly entities: readonly ClassRef[];
|
|
21
|
+
/**
|
|
22
|
+
* Whether the connector supports Prisma's `mode: "insensitive"` string
|
|
23
|
+
* filter (Postgres, MongoDB) — `false` for MySQL, SQLite, SQL Server,
|
|
24
|
+
* where Prisma rejects the argument outright. Defaults to `true`
|
|
25
|
+
* (Postgres is the common case); set explicitly for any other connector.
|
|
26
|
+
* See `FilterTranslatorOptions`.
|
|
27
|
+
*/
|
|
28
|
+
readonly caseInsensitiveFilters?: boolean;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The Prisma implementation of core's infrastructure seam: metadata and
|
|
32
|
+
* adapters derived from one Prisma Client, cached per entity (metadata
|
|
33
|
+
* derivation and adapter construction are bootstrap work, not per-request
|
|
34
|
+
* work) — same shape as `@kavo/typeorm`'s `createTypeOrmInfrastructure`.
|
|
35
|
+
*/
|
|
36
|
+
export declare function createPrismaInfrastructure(prismaClient: PrismaClientLike, options: PrismaInfrastructureOptions): CrudInfrastructure;
|
|
37
|
+
/**
|
|
38
|
+
* Sugar for the common case: a Kavo root instance wired to one Prisma
|
|
39
|
+
* Client, so `kavo.createCrud(Author)` is genuinely zero-config beyond
|
|
40
|
+
* declaring the marker classes once.
|
|
41
|
+
*/
|
|
42
|
+
export declare function createPrismaKavo(prismaClient: PrismaClientLike, options: PrismaInfrastructureOptions & Omit<KavoOptions, "infrastructure">): KavoInstance;
|
|
43
|
+
//# sourceMappingURL=infrastructure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infrastructure.d.ts","sourceRoot":"","sources":["../src/infrastructure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,kBAAkB,EAElB,YAAY,EACZ,WAAW,EAEZ,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEtD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAGhE;;;;;;;;;;;;;GAaG;AACH,MAAM,WAAW,2BAA2B;IAC1C,QAAQ,CAAC,SAAS,EAAE,eAAe,CAAC;IACpC,QAAQ,CAAC,QAAQ,EAAE,SAAS,QAAQ,EAAE,CAAC;IACvC;;;;;;OAMG;IACH,QAAQ,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAC3C;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,YAAY,EAAE,gBAAgB,EAC9B,OAAO,EAAE,2BAA2B,GACnC,kBAAkB,CA2BpB;AAED;;;;GAIG;AACH,wBAAgB,gBAAgB,CAC9B,YAAY,EAAE,gBAAgB,EAC9B,OAAO,EAAE,2BAA2B,GAAG,IAAI,CAAC,WAAW,EAAE,gBAAgB,CAAC,GACzE,YAAY,CAMd"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { createKavo } from "@kavo/core";
|
|
2
|
+
import { buildEntityMetadata } from "./metadata.js";
|
|
3
|
+
import { PrismaRepositoryAdapter } from "./prisma-repository-adapter.js";
|
|
4
|
+
/**
|
|
5
|
+
* The Prisma implementation of core's infrastructure seam: metadata and
|
|
6
|
+
* adapters derived from one Prisma Client, cached per entity (metadata
|
|
7
|
+
* derivation and adapter construction are bootstrap work, not per-request
|
|
8
|
+
* work) — same shape as `@kavo/typeorm`'s `createTypeOrmInfrastructure`.
|
|
9
|
+
*/
|
|
10
|
+
export function createPrismaInfrastructure(prismaClient, options) {
|
|
11
|
+
const byName = new Map(options.entities.map((entity) => [entity.name, entity]));
|
|
12
|
+
const metadataCache = new Map();
|
|
13
|
+
const adapterCache = new Map();
|
|
14
|
+
function metadataFor(entity) {
|
|
15
|
+
let metadata = metadataCache.get(entity);
|
|
16
|
+
if (metadata === undefined) {
|
|
17
|
+
metadata = buildEntityMetadata(options.datamodel, entity, byName);
|
|
18
|
+
metadataCache.set(entity, metadata);
|
|
19
|
+
}
|
|
20
|
+
return metadata;
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
metadataFor,
|
|
24
|
+
adapterFor(entity) {
|
|
25
|
+
let adapter = adapterCache.get(entity);
|
|
26
|
+
if (adapter === undefined) {
|
|
27
|
+
adapter = new PrismaRepositoryAdapter(prismaClient, metadataFor(entity), {
|
|
28
|
+
caseInsensitiveFilters: options.caseInsensitiveFilters ?? true,
|
|
29
|
+
});
|
|
30
|
+
adapterCache.set(entity, adapter);
|
|
31
|
+
}
|
|
32
|
+
return adapter;
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Sugar for the common case: a Kavo root instance wired to one Prisma
|
|
38
|
+
* Client, so `kavo.createCrud(Author)` is genuinely zero-config beyond
|
|
39
|
+
* declaring the marker classes once.
|
|
40
|
+
*/
|
|
41
|
+
export function createPrismaKavo(prismaClient, options) {
|
|
42
|
+
const { datamodel, entities, caseInsensitiveFilters, ...kavoOptions } = options;
|
|
43
|
+
return createKavo({
|
|
44
|
+
...kavoOptions,
|
|
45
|
+
infrastructure: createPrismaInfrastructure(prismaClient, { datamodel, entities, caseInsensitiveFilters }),
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=infrastructure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"infrastructure.js","sourceRoot":"","sources":["../src/infrastructure.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAExC,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAEpD,OAAO,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AA6BzE;;;;;GAKG;AACH,MAAM,UAAU,0BAA0B,CACxC,YAA8B,EAC9B,OAAoC;IAEpC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC;IAChF,MAAM,aAAa,GAAG,IAAI,GAAG,EAA4B,CAAC;IAC1D,MAAM,YAAY,GAAG,IAAI,GAAG,EAA+B,CAAC;IAE5D,SAAS,WAAW,CAAwB,MAAwB;QAClE,IAAI,QAAQ,GAAG,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QACzC,IAAI,QAAQ,KAAK,SAAS,EAAE,CAAC;YAC3B,QAAQ,GAAG,mBAAmB,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;YAClE,aAAa,CAAC,GAAG,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;QACtC,CAAC;QACD,OAAO,QAAkC,CAAC;IAC5C,CAAC;IAED,OAAO;QACL,WAAW;QACX,UAAU,CAAwB,MAAwB;YACxD,IAAI,OAAO,GAAG,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;YACvC,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;gBAC1B,OAAO,GAAG,IAAI,uBAAuB,CAAC,YAAY,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE;oBACvE,sBAAsB,EAAE,OAAO,CAAC,sBAAsB,IAAI,IAAI;iBAC/D,CAAsB,CAAC;gBACxB,YAAY,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACpC,CAAC;YACD,OAAO,OAAoC,CAAC;QAC9C,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,gBAAgB,CAC9B,YAA8B,EAC9B,OAA0E;IAE1E,MAAM,EAAE,SAAS,EAAE,QAAQ,EAAE,sBAAsB,EAAE,GAAG,WAAW,EAAE,GAAG,OAAO,CAAC;IAChF,OAAO,UAAU,CAAC;QAChB,GAAG,WAAW;QACd,cAAc,EAAE,0BAA0B,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,sBAAsB,EAAE,CAAC;KAC1G,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ClassRef, EntityMetadata } from "@kavo/core";
|
|
2
|
+
import type { PrismaDatamodel } from "./datamodel.js";
|
|
3
|
+
/**
|
|
4
|
+
* Build core's `EntityMetadata` for one entity from Prisma's DMMF.
|
|
5
|
+
*
|
|
6
|
+
* Prisma models have no runtime class the way TypeORM's `@Entity()`
|
|
7
|
+
* classes do, so `entity` is a *marker class* the caller declares purely as
|
|
8
|
+
* the `ClassRef` identity `createCrud` requires — matched to a DMMF model
|
|
9
|
+
* by name (`class Author {}` ↔ `model Author { … }`). `entities` is the
|
|
10
|
+
* name→class registry that lets a relation's target model name resolve
|
|
11
|
+
* back to *its* marker class; TypeORM gets this for free from
|
|
12
|
+
* `DataSource#entities`, Prisma has no equivalent, so
|
|
13
|
+
* `createPrismaInfrastructure` collects it explicitly (see
|
|
14
|
+
* `docs/adr/0017-prisma-marker-classes-and-entity-registry.md`).
|
|
15
|
+
*/
|
|
16
|
+
export declare function buildEntityMetadata<Entity extends object>(datamodel: PrismaDatamodel, entity: ClassRef<Entity>, entities: ReadonlyMap<string, ClassRef>): EntityMetadata<Entity>;
|
|
17
|
+
//# sourceMappingURL=metadata.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAgD,MAAM,YAAY,CAAC;AAEzG,OAAO,KAAK,EAAE,eAAe,EAAe,MAAM,gBAAgB,CAAC;AA4CnE;;;;;;;;;;;;GAYG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,SAAS,MAAM,EACvD,SAAS,EAAE,eAAe,EAC1B,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,EACxB,QAAQ,EAAE,WAAW,CAAC,MAAM,EAAE,QAAQ,CAAC,GACtC,cAAc,CAAC,MAAM,CAAC,CAoDxB"}
|
package/dist/metadata.js
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { ConfigurationException } from "@kavo/core";
|
|
2
|
+
/**
|
|
3
|
+
* Prisma scalar type name → core's ORM-independent `FieldKind`. Unrecognized
|
|
4
|
+
* scalar names (a future Prisma type this mapping hasn't caught up to)
|
|
5
|
+
* degrade to `string`, same posture as `@kavo/typeorm`'s fallback.
|
|
6
|
+
*/
|
|
7
|
+
function fieldKindOf(field) {
|
|
8
|
+
if (field.kind === "enum")
|
|
9
|
+
return "enum";
|
|
10
|
+
switch (field.type) {
|
|
11
|
+
case "Int":
|
|
12
|
+
case "Float":
|
|
13
|
+
case "BigInt":
|
|
14
|
+
case "Decimal":
|
|
15
|
+
return "number";
|
|
16
|
+
case "Boolean":
|
|
17
|
+
return "boolean";
|
|
18
|
+
case "DateTime":
|
|
19
|
+
return "date";
|
|
20
|
+
case "Json":
|
|
21
|
+
return "json";
|
|
22
|
+
default:
|
|
23
|
+
return "string";
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* A column is "generated" (excluded from `create`/`update`/`patch` DTO
|
|
28
|
+
* defaults, per `EntityMetadata.fields[].generated`) when Prisma or the
|
|
29
|
+
* database computes its value at write time rather than the caller
|
|
30
|
+
* supplying one: `@updatedAt`, or a function-style `@default(...)`
|
|
31
|
+
* (`autoincrement()`, `now()`, `cuid()`, `uuid()`, `dbgenerated(...)`).
|
|
32
|
+
* A literal default (`@default("active")`, `@default(0)`) is an ordinary
|
|
33
|
+
* writable column with a fallback value, not a generated one — matching
|
|
34
|
+
* `@kavo/typeorm`, where `@Column({ default: "active" })` isn't `generated`
|
|
35
|
+
* either.
|
|
36
|
+
*/
|
|
37
|
+
function isGeneratedField(field) {
|
|
38
|
+
if (field.isUpdatedAt)
|
|
39
|
+
return true;
|
|
40
|
+
if (!field.hasDefaultValue)
|
|
41
|
+
return false;
|
|
42
|
+
const value = field.default;
|
|
43
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Build core's `EntityMetadata` for one entity from Prisma's DMMF.
|
|
47
|
+
*
|
|
48
|
+
* Prisma models have no runtime class the way TypeORM's `@Entity()`
|
|
49
|
+
* classes do, so `entity` is a *marker class* the caller declares purely as
|
|
50
|
+
* the `ClassRef` identity `createCrud` requires — matched to a DMMF model
|
|
51
|
+
* by name (`class Author {}` ↔ `model Author { … }`). `entities` is the
|
|
52
|
+
* name→class registry that lets a relation's target model name resolve
|
|
53
|
+
* back to *its* marker class; TypeORM gets this for free from
|
|
54
|
+
* `DataSource#entities`, Prisma has no equivalent, so
|
|
55
|
+
* `createPrismaInfrastructure` collects it explicitly (see
|
|
56
|
+
* `docs/adr/0017-prisma-marker-classes-and-entity-registry.md`).
|
|
57
|
+
*/
|
|
58
|
+
export function buildEntityMetadata(datamodel, entity, entities) {
|
|
59
|
+
const modelName = entity.name;
|
|
60
|
+
const model = datamodel.models.find((candidate) => candidate.name === modelName);
|
|
61
|
+
if (model === undefined) {
|
|
62
|
+
throw new ConfigurationException(modelName, "entity", `No Prisma model named '${modelName}' in the supplied datamodel — the marker class name must match ` +
|
|
63
|
+
`the schema's model name exactly`);
|
|
64
|
+
}
|
|
65
|
+
const idFields = model.fields.filter((field) => field.isId);
|
|
66
|
+
if (idFields.length !== 1) {
|
|
67
|
+
throw new ConfigurationException(modelName, "id", `Kavo requires exactly one @id field; found ${idFields.length} on model '${modelName}' ` +
|
|
68
|
+
`(composite primary keys are out of scope)`);
|
|
69
|
+
}
|
|
70
|
+
const fields = model.fields
|
|
71
|
+
.filter((field) => field.kind !== "object")
|
|
72
|
+
.map((field) => ({
|
|
73
|
+
name: field.name,
|
|
74
|
+
kind: fieldKindOf(field),
|
|
75
|
+
nullable: field.isList ? false : !field.isRequired,
|
|
76
|
+
generated: isGeneratedField(field),
|
|
77
|
+
...(field.kind === "enum" && {
|
|
78
|
+
enumValues: (datamodel.enums.find((candidate) => candidate.name === field.type)?.values ?? []).map((value) => value.name),
|
|
79
|
+
}),
|
|
80
|
+
}));
|
|
81
|
+
const relations = model.fields
|
|
82
|
+
.filter((field) => field.kind === "object")
|
|
83
|
+
.map((field) => relationDescriptor(field, modelName, entities));
|
|
84
|
+
return {
|
|
85
|
+
entity,
|
|
86
|
+
name: model.name,
|
|
87
|
+
idField: idFields[0].name,
|
|
88
|
+
fields,
|
|
89
|
+
relations,
|
|
90
|
+
// Prisma declares no delete-marker column the way TypeORM's
|
|
91
|
+
// `@DeleteDateColumn` does — soft delete is always explicit
|
|
92
|
+
// `softDelete.field` configuration for this adapter, never
|
|
93
|
+
// auto-detected.
|
|
94
|
+
softDeleteField: null,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function relationDescriptor(field, ownerModelName, entities) {
|
|
98
|
+
return {
|
|
99
|
+
name: field.name,
|
|
100
|
+
target: () => {
|
|
101
|
+
const target = entities.get(field.type);
|
|
102
|
+
if (target === undefined) {
|
|
103
|
+
throw new ConfigurationException(ownerModelName, field.name, `Relation '${field.name}' targets Prisma model '${field.type}', but no marker class for it was ` +
|
|
104
|
+
`registered with createPrismaInfrastructure's 'entities' list`);
|
|
105
|
+
}
|
|
106
|
+
return target;
|
|
107
|
+
},
|
|
108
|
+
cardinality: field.isList ? "many" : "one",
|
|
109
|
+
includable: false,
|
|
110
|
+
strategy: "auto",
|
|
111
|
+
};
|
|
112
|
+
}
|
|
113
|
+
//# sourceMappingURL=metadata.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"metadata.js","sourceRoot":"","sources":["../src/metadata.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The minimal structural shape this adapter needs from a generated Prisma
|
|
3
|
+
* Client — one delegate per model, each with the handful of query methods
|
|
4
|
+
* the adapter calls. Kept structural (not `import type { PrismaClient }
|
|
5
|
+
* from "@prisma/client"`) so `@kavo/prisma`'s own build never needs a
|
|
6
|
+
* generated client: `@prisma/client`'s package entrypoint only resolves
|
|
7
|
+
* *after* `prisma generate` has run against the consumer's schema, and a
|
|
8
|
+
* library package cannot assume that happened. A real generated
|
|
9
|
+
* `PrismaClient` instance satisfies this structurally with no cast.
|
|
10
|
+
*/
|
|
11
|
+
export interface PrismaModelDelegate {
|
|
12
|
+
findFirst(args: Record<string, unknown>): Promise<Record<string, unknown> | null>;
|
|
13
|
+
findMany(args: Record<string, unknown>): Promise<Record<string, unknown>[]>;
|
|
14
|
+
count(args: Record<string, unknown>): Promise<number>;
|
|
15
|
+
create(args: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
16
|
+
update(args: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
17
|
+
delete(args: Record<string, unknown>): Promise<Record<string, unknown>>;
|
|
18
|
+
}
|
|
19
|
+
/** A Prisma Client instance, indexed by its per-model delegate property (`prisma.author`). */
|
|
20
|
+
export type PrismaClientLike = Record<string, PrismaModelDelegate>;
|
|
21
|
+
/** `Author` → `author`: Prisma's own delegate-naming rule (first letter lowercased). */
|
|
22
|
+
export declare function delegateName(modelName: string): string;
|
|
23
|
+
//# sourceMappingURL=prisma-client-like.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma-client-like.d.ts","sourceRoot":"","sources":["../src/prisma-client-like.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AACH,MAAM,WAAW,mBAAmB;IAClC,SAAS,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAAC,CAAC;IAClF,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,CAAC,CAAC;IAC5E,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACtD,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;IACxE,MAAM,CAAC,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC;CACzE;AAED,8FAA8F;AAC9F,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,mBAAmB,CAAC,CAAC;AAEnE,wFAAwF;AACxF,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAEtD"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma-client-like.js","sourceRoot":"","sources":["../src/prisma-client-like.ts"],"names":[],"mappings":"AAsBA,wFAAwF;AACxF,MAAM,UAAU,YAAY,CAAC,SAAiB;IAC5C,OAAO,SAAS,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAChE,CAAC"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import type { CrudContext, EntityId, EntityMetadata, NormalizedQueryContext, RepositoryAdapter } from "@kavo/core";
|
|
2
|
+
import { type FilterTranslatorOptions } from "./filter-translator.js";
|
|
3
|
+
import { type PrismaClientLike } from "./prisma-client-like.js";
|
|
4
|
+
/**
|
|
5
|
+
* `RepositoryAdapter` over a Prisma Client model delegate: CRUD with hard
|
|
6
|
+
* *or* soft delete, restore, purge, filtering, sorting, pagination,
|
|
7
|
+
* optional counting.
|
|
8
|
+
*
|
|
9
|
+
* **No join/batch split.** `@kavo/typeorm` translates `IncludeNode.strategy`
|
|
10
|
+
* because it drives a raw SQL query builder, where a to-many `JOIN`
|
|
11
|
+
* multiplies root rows and a separate batched query is how that's avoided.
|
|
12
|
+
* Prisma's `include` has no such failure mode: it always resolves relations
|
|
13
|
+
* as its own internally-batched queries, to-one or to-many alike, never a
|
|
14
|
+
* row-multiplying join — so a to-many include never disturbs root
|
|
15
|
+
* pagination regardless of which strategy core resolved. This adapter
|
|
16
|
+
* therefore *ignores* `IncludeNode.strategy` and maps every node the same
|
|
17
|
+
* way; there is nothing for the distinction to control here.
|
|
18
|
+
*/
|
|
19
|
+
export declare class PrismaRepositoryAdapter<Entity extends object> implements RepositoryAdapter<Entity> {
|
|
20
|
+
private readonly prismaClient;
|
|
21
|
+
private readonly metadata;
|
|
22
|
+
private readonly filterOptions;
|
|
23
|
+
private readonly delegate;
|
|
24
|
+
private readonly idField;
|
|
25
|
+
constructor(prismaClient: PrismaClientLike, metadata: EntityMetadata<Entity>, filterOptions?: FilterTranslatorOptions);
|
|
26
|
+
findOneById(id: EntityId, query: NormalizedQueryContext<Entity> | null, context: CrudContext<Entity>): Promise<Entity | null>;
|
|
27
|
+
findOne(query: NormalizedQueryContext<Entity>, context: CrudContext<Entity>): Promise<Entity | null>;
|
|
28
|
+
findMany(query: NormalizedQueryContext<Entity>, context: CrudContext<Entity>): Promise<readonly Entity[]>;
|
|
29
|
+
count(query: NormalizedQueryContext<Entity>, context: CrudContext<Entity>): Promise<number>;
|
|
30
|
+
private buildFindArgs;
|
|
31
|
+
/**
|
|
32
|
+
* Translate a validated `IncludeTree` into Prisma's nested `include`
|
|
33
|
+
* clause. Soft-deleted related rows are excluded the same way for a
|
|
34
|
+
* to-one or to-many edge — Prisma accepts a `where` on either — mirroring
|
|
35
|
+
* `@kavo/typeorm`'s exclusion of soft-deleted rows from every include.
|
|
36
|
+
* A root `withDeleted` is the root's own opt-in only; it never widens an
|
|
37
|
+
* included relation (same rule as `@kavo/typeorm`).
|
|
38
|
+
*/
|
|
39
|
+
private buildInclude;
|
|
40
|
+
private scopeToLive;
|
|
41
|
+
/** The resolved strategy, refused when an operation requires soft. */
|
|
42
|
+
private requireSoftDelete;
|
|
43
|
+
private isDeleted;
|
|
44
|
+
private byId;
|
|
45
|
+
create(data: Partial<Entity>, context: CrudContext<Entity>): Promise<Entity>;
|
|
46
|
+
update(id: EntityId, data: Partial<Entity>, context: CrudContext<Entity>): Promise<Entity>;
|
|
47
|
+
patch(id: EntityId, data: Partial<Entity>, context: CrudContext<Entity>): Promise<Entity>;
|
|
48
|
+
/**
|
|
49
|
+
* update and patch share one primitive: the *shape* of `data` differs
|
|
50
|
+
* (full body vs. sparse) because the DTO layer differs, not the
|
|
51
|
+
* persistence mechanics. A soft-deleted row is invisible to writes,
|
|
52
|
+
* exactly as it is to reads — reviving one is `restore`'s job, so
|
|
53
|
+
* existence is checked against the live scope before the write.
|
|
54
|
+
*/
|
|
55
|
+
private writeExisting;
|
|
56
|
+
delete(id: EntityId, context: CrudContext<Entity>): Promise<void>;
|
|
57
|
+
restore(id: EntityId, context: CrudContext<Entity>): Promise<Entity>;
|
|
58
|
+
purge(id: EntityId, context: CrudContext<Entity>): Promise<void>;
|
|
59
|
+
private notFound;
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=prisma-repository-adapter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma-repository-adapter.d.ts","sourceRoot":"","sources":["../src/prisma-repository-adapter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,WAAW,EACX,QAAQ,EACR,cAAc,EAEd,sBAAsB,EACtB,iBAAiB,EAElB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAmB,KAAK,uBAAuB,EAAoB,MAAM,wBAAwB,CAAC;AACzG,OAAO,EAAgB,KAAK,gBAAgB,EAA4B,MAAM,yBAAyB,CAAC;AAExG;;;;;;;;;;;;;;GAcG;AACH,qBAAa,uBAAuB,CAAC,MAAM,SAAS,MAAM,CAAE,YAAW,iBAAiB,CAAC,MAAM,CAAC;IAK5F,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,aAAa;IANhC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAsB;IAC/C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;gBAGd,YAAY,EAAE,gBAAgB,EAC9B,QAAQ,EAAE,cAAc,CAAC,MAAM,CAAC,EAChC,aAAa,GAAE,uBAA0D;IAiBtF,WAAW,CACf,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,sBAAsB,CAAC,MAAM,CAAC,GAAG,IAAI,EAC5C,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAC3B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAWnB,OAAO,CAAC,KAAK,EAAE,sBAAsB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IASpG,QAAQ,CAAC,KAAK,EAAE,sBAAsB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,SAAS,MAAM,EAAE,CAAC;IAazG,KAAK,CAAC,KAAK,EAAE,sBAAsB,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAWjG,OAAO,CAAC,aAAa;IAarB;;;;;;;OAOG;IACH,OAAO,CAAC,YAAY;IAkBpB,OAAO,CAAC,WAAW;IAYnB,sEAAsE;IACtE,OAAO,CAAC,iBAAiB;IAazB,OAAO,CAAC,SAAS;YAIH,IAAI;IAWZ,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAQ5E,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAI1F,KAAK,CAAC,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAI/F;;;;;;OAMG;YACW,aAAa;IAUrB,MAAM,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBjE,OAAO,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAiBpE,KAAK,CAAC,EAAE,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;IAwBtE,OAAO,CAAC,QAAQ;CAMjB"}
|
|
@@ -0,0 +1,267 @@
|
|
|
1
|
+
import { AlreadyDeletedException, ConfigurationException, NotDeletedException, NotFoundException } from "@kavo/core";
|
|
2
|
+
import { mapDriverError } from "./error-mapping.js";
|
|
3
|
+
import { translateFilter } from "./filter-translator.js";
|
|
4
|
+
import { delegateName } from "./prisma-client-like.js";
|
|
5
|
+
/**
|
|
6
|
+
* `RepositoryAdapter` over a Prisma Client model delegate: CRUD with hard
|
|
7
|
+
* *or* soft delete, restore, purge, filtering, sorting, pagination,
|
|
8
|
+
* optional counting.
|
|
9
|
+
*
|
|
10
|
+
* **No join/batch split.** `@kavo/typeorm` translates `IncludeNode.strategy`
|
|
11
|
+
* because it drives a raw SQL query builder, where a to-many `JOIN`
|
|
12
|
+
* multiplies root rows and a separate batched query is how that's avoided.
|
|
13
|
+
* Prisma's `include` has no such failure mode: it always resolves relations
|
|
14
|
+
* as its own internally-batched queries, to-one or to-many alike, never a
|
|
15
|
+
* row-multiplying join — so a to-many include never disturbs root
|
|
16
|
+
* pagination regardless of which strategy core resolved. This adapter
|
|
17
|
+
* therefore *ignores* `IncludeNode.strategy` and maps every node the same
|
|
18
|
+
* way; there is nothing for the distinction to control here.
|
|
19
|
+
*/
|
|
20
|
+
export class PrismaRepositoryAdapter {
|
|
21
|
+
prismaClient;
|
|
22
|
+
metadata;
|
|
23
|
+
filterOptions;
|
|
24
|
+
delegate;
|
|
25
|
+
idField;
|
|
26
|
+
constructor(prismaClient, metadata, filterOptions = { caseInsensitiveFilters: true }) {
|
|
27
|
+
this.prismaClient = prismaClient;
|
|
28
|
+
this.metadata = metadata;
|
|
29
|
+
this.filterOptions = filterOptions;
|
|
30
|
+
const name = delegateName(metadata.name);
|
|
31
|
+
const delegate = prismaClient[name];
|
|
32
|
+
if (delegate === undefined) {
|
|
33
|
+
throw new ConfigurationException(metadata.name, "entity", `Prisma Client has no '${name}' delegate for model '${metadata.name}'`);
|
|
34
|
+
}
|
|
35
|
+
this.delegate = delegate;
|
|
36
|
+
this.idField = metadata.idField;
|
|
37
|
+
}
|
|
38
|
+
// ── Reads ────────────────────────────────────────────────────────────
|
|
39
|
+
async findOneById(id, query, context) {
|
|
40
|
+
try {
|
|
41
|
+
const where = this.scopeToLive({ [this.idField]: id }, context, query?.withDeleted ?? false);
|
|
42
|
+
const include = this.buildInclude(query?.include ?? {});
|
|
43
|
+
const row = await this.delegate.findFirst({ where, ...(include && { include }) });
|
|
44
|
+
return row ?? null;
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
throw mapDriverError(error, errorContext(context));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async findOne(query, context) {
|
|
51
|
+
try {
|
|
52
|
+
const row = await this.delegate.findFirst(this.buildFindArgs(query, context));
|
|
53
|
+
return row ?? null;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
throw mapDriverError(error, errorContext(context));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async findMany(query, context) {
|
|
60
|
+
try {
|
|
61
|
+
const rows = await this.delegate.findMany({
|
|
62
|
+
...this.buildFindArgs(query, context),
|
|
63
|
+
skip: query.pagination.offset,
|
|
64
|
+
take: query.pagination.limit,
|
|
65
|
+
});
|
|
66
|
+
return rows;
|
|
67
|
+
}
|
|
68
|
+
catch (error) {
|
|
69
|
+
throw mapDriverError(error, errorContext(context));
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
async count(query, context) {
|
|
73
|
+
try {
|
|
74
|
+
// A dedicated count — never fetch-then-length: the engine only calls
|
|
75
|
+
// this when `query.count` is true, so `total: null` costs zero queries.
|
|
76
|
+
const where = this.scopeToLive(translateFilter(query.filter, this.filterOptions), context, query.withDeleted);
|
|
77
|
+
return await this.delegate.count({ ...(where && { where }) });
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
throw mapDriverError(error, errorContext(context));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
buildFindArgs(query, context) {
|
|
84
|
+
const where = this.scopeToLive(translateFilter(query.filter, this.filterOptions), context, query.withDeleted);
|
|
85
|
+
const orderBy = query.sort.map((sort) => nestOrderBy(sort.field, sort.direction));
|
|
86
|
+
const include = this.buildInclude(query.include);
|
|
87
|
+
return {
|
|
88
|
+
...(where && { where }),
|
|
89
|
+
...(orderBy.length > 0 && { orderBy }),
|
|
90
|
+
...(include && { include }),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
// ── Relation includes ───────────────────────────────────────────────
|
|
94
|
+
/**
|
|
95
|
+
* Translate a validated `IncludeTree` into Prisma's nested `include`
|
|
96
|
+
* clause. Soft-deleted related rows are excluded the same way for a
|
|
97
|
+
* to-one or to-many edge — Prisma accepts a `where` on either — mirroring
|
|
98
|
+
* `@kavo/typeorm`'s exclusion of soft-deleted rows from every include.
|
|
99
|
+
* A root `withDeleted` is the root's own opt-in only; it never widens an
|
|
100
|
+
* included relation (same rule as `@kavo/typeorm`).
|
|
101
|
+
*/
|
|
102
|
+
buildInclude(tree) {
|
|
103
|
+
const entries = Object.values(tree);
|
|
104
|
+
if (entries.length === 0)
|
|
105
|
+
return undefined;
|
|
106
|
+
const include = {};
|
|
107
|
+
for (const node of entries) {
|
|
108
|
+
const where = node.softDelete.strategy === "soft" ? { [node.softDelete.field]: null } : undefined;
|
|
109
|
+
const children = this.buildInclude(node.children);
|
|
110
|
+
if (where === undefined && children === undefined) {
|
|
111
|
+
include[node.relation.name] = true;
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
include[node.relation.name] = { ...(where && { where }), ...(children && { include: children }) };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return include;
|
|
118
|
+
}
|
|
119
|
+
// ── Soft delete ──────────────────────────────────────────────────────
|
|
120
|
+
scopeToLive(where, context, withDeleted) {
|
|
121
|
+
const softDelete = context.config.softDelete;
|
|
122
|
+
if (softDelete.strategy !== "soft" || withDeleted)
|
|
123
|
+
return where;
|
|
124
|
+
const live = { [softDelete.field]: null };
|
|
125
|
+
if (where === undefined)
|
|
126
|
+
return live;
|
|
127
|
+
return { AND: [where, live] };
|
|
128
|
+
}
|
|
129
|
+
/** The resolved strategy, refused when an operation requires soft. */
|
|
130
|
+
requireSoftDelete(context, operation) {
|
|
131
|
+
const softDelete = context.config.softDelete;
|
|
132
|
+
if (softDelete.strategy !== "soft") {
|
|
133
|
+
throw new ConfigurationException(context.entityName, "softDelete", `'${operation}' requires a soft-deletable entity, but '${context.entityName}' ` +
|
|
134
|
+
`resolves to a hard delete strategy`);
|
|
135
|
+
}
|
|
136
|
+
return softDelete;
|
|
137
|
+
}
|
|
138
|
+
isDeleted(row, field) {
|
|
139
|
+
return row[field] !== null && row[field] !== undefined;
|
|
140
|
+
}
|
|
141
|
+
async byId(id, context, withDeleted) {
|
|
142
|
+
const where = this.scopeToLive({ [this.idField]: id }, context, withDeleted);
|
|
143
|
+
return (await this.delegate.findFirst({ where }));
|
|
144
|
+
}
|
|
145
|
+
// ── Writes ───────────────────────────────────────────────────────────
|
|
146
|
+
async create(data, context) {
|
|
147
|
+
try {
|
|
148
|
+
return (await this.delegate.create({ data }));
|
|
149
|
+
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
throw mapDriverError(error, errorContext(context));
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
async update(id, data, context) {
|
|
155
|
+
return this.writeExisting(id, data, context);
|
|
156
|
+
}
|
|
157
|
+
async patch(id, data, context) {
|
|
158
|
+
return this.writeExisting(id, data, context);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* update and patch share one primitive: the *shape* of `data` differs
|
|
162
|
+
* (full body vs. sparse) because the DTO layer differs, not the
|
|
163
|
+
* persistence mechanics. A soft-deleted row is invisible to writes,
|
|
164
|
+
* exactly as it is to reads — reviving one is `restore`'s job, so
|
|
165
|
+
* existence is checked against the live scope before the write.
|
|
166
|
+
*/
|
|
167
|
+
async writeExisting(id, data, context) {
|
|
168
|
+
try {
|
|
169
|
+
const existing = await this.byId(id, context, false);
|
|
170
|
+
if (existing === null)
|
|
171
|
+
throw this.notFound(id, context);
|
|
172
|
+
return (await this.delegate.update({ where: { [this.idField]: id }, data }));
|
|
173
|
+
}
|
|
174
|
+
catch (error) {
|
|
175
|
+
throw mapDriverError(error, errorContext(context));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
async delete(id, context) {
|
|
179
|
+
const softDelete = context.config.softDelete;
|
|
180
|
+
try {
|
|
181
|
+
if (softDelete.strategy === "hard") {
|
|
182
|
+
const existing = await this.byId(id, context, false);
|
|
183
|
+
if (existing === null)
|
|
184
|
+
throw this.notFound(id, context);
|
|
185
|
+
await this.delegate.delete({ where: { [this.idField]: id } });
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
const { field } = softDelete;
|
|
189
|
+
const existing = await this.byId(id, context, true);
|
|
190
|
+
if (existing === null)
|
|
191
|
+
throw this.notFound(id, context);
|
|
192
|
+
if (this.isDeleted(existing, field)) {
|
|
193
|
+
throw new AlreadyDeletedException({
|
|
194
|
+
messageParams: { entity: context.entityName, id: String(id) },
|
|
195
|
+
context: errorContext(context),
|
|
196
|
+
});
|
|
197
|
+
}
|
|
198
|
+
await this.delegate.update({ where: { [this.idField]: id }, data: { [field]: new Date() } });
|
|
199
|
+
}
|
|
200
|
+
catch (error) {
|
|
201
|
+
throw mapDriverError(error, errorContext(context));
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
async restore(id, context) {
|
|
205
|
+
try {
|
|
206
|
+
const { field } = this.requireSoftDelete(context, "restore");
|
|
207
|
+
const existing = await this.byId(id, context, true);
|
|
208
|
+
if (existing === null)
|
|
209
|
+
throw this.notFound(id, context);
|
|
210
|
+
if (!this.isDeleted(existing, field)) {
|
|
211
|
+
throw new NotDeletedException({
|
|
212
|
+
messageParams: { entity: context.entityName, id: String(id) },
|
|
213
|
+
context: errorContext(context),
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return (await this.delegate.update({ where: { [this.idField]: id }, data: { [field]: null } }));
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
throw mapDriverError(error, errorContext(context));
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
async purge(id, context) {
|
|
223
|
+
const softDelete = context.config.softDelete;
|
|
224
|
+
try {
|
|
225
|
+
if (softDelete.strategy === "soft") {
|
|
226
|
+
// Purge is the second step of a two-step delete: it removes a row
|
|
227
|
+
// that is already soft-deleted, never a live one.
|
|
228
|
+
const existing = await this.byId(id, context, true);
|
|
229
|
+
if (existing === null)
|
|
230
|
+
throw this.notFound(id, context);
|
|
231
|
+
if (!this.isDeleted(existing, softDelete.field)) {
|
|
232
|
+
throw new NotDeletedException({
|
|
233
|
+
messageParams: { entity: context.entityName, id: String(id) },
|
|
234
|
+
context: errorContext(context),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
else {
|
|
239
|
+
const existing = await this.byId(id, context, false);
|
|
240
|
+
if (existing === null)
|
|
241
|
+
throw this.notFound(id, context);
|
|
242
|
+
}
|
|
243
|
+
await this.delegate.delete({ where: { [this.idField]: id } });
|
|
244
|
+
}
|
|
245
|
+
catch (error) {
|
|
246
|
+
throw mapDriverError(error, errorContext(context));
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
notFound(id, context) {
|
|
250
|
+
return new NotFoundException({
|
|
251
|
+
messageParams: { entity: context.entityName, id: String(id) },
|
|
252
|
+
context: errorContext(context),
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
function nestOrderBy(field, direction) {
|
|
257
|
+
const segments = field.split(".");
|
|
258
|
+
return segments.reduceRight((inner, segment, index) => ({ [segment]: index === segments.length - 1 ? direction : inner }), {});
|
|
259
|
+
}
|
|
260
|
+
function errorContext(context) {
|
|
261
|
+
return {
|
|
262
|
+
entityName: context.entityName,
|
|
263
|
+
operation: context.operation,
|
|
264
|
+
correlationId: context.correlationId,
|
|
265
|
+
};
|
|
266
|
+
}
|
|
267
|
+
//# sourceMappingURL=prisma-repository-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma-repository-adapter.js","sourceRoot":"","sources":["../src/prisma-repository-adapter.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kavo/prisma",
|
|
3
|
+
"version": "0.5.0",
|
|
4
|
+
"description": "Kavo Prisma adapter — implements @kavo/core's RepositoryAdapter over Prisma Client.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/kavo-labs/kavo.git",
|
|
9
|
+
"directory": "packages/orms/prisma"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/kavo-labs/kavo/tree/main/packages/orms/prisma#readme",
|
|
12
|
+
"type": "module",
|
|
13
|
+
"sideEffects": false,
|
|
14
|
+
"publishConfig": {
|
|
15
|
+
"access": "public"
|
|
16
|
+
},
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/index.d.ts",
|
|
20
|
+
"default": "./dist/index.js"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"files": [
|
|
24
|
+
"dist"
|
|
25
|
+
],
|
|
26
|
+
"scripts": {
|
|
27
|
+
"build": "tsc -b",
|
|
28
|
+
"generate": "prisma generate --schema prisma/schema.prisma && prisma db push --schema prisma/schema.prisma --skip-generate --accept-data-loss"
|
|
29
|
+
},
|
|
30
|
+
"dependencies": {
|
|
31
|
+
"@kavo/core": "workspace:^"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"@prisma/client": "^5.0.0 || ^6.0.0"
|
|
35
|
+
},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@prisma/client": "^6.19.3",
|
|
38
|
+
"prisma": "^6.19.3"
|
|
39
|
+
}
|
|
40
|
+
}
|