@kavo/mikroorm 0.6.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 +132 -0
- package/dist/error-mapping.d.ts +32 -0
- package/dist/error-mapping.d.ts.map +1 -0
- package/dist/error-mapping.js +51 -0
- package/dist/error-mapping.js.map +1 -0
- package/dist/filter-translator.d.ts +49 -0
- package/dist/filter-translator.d.ts.map +1 -0
- package/dist/filter-translator.js +109 -0
- package/dist/filter-translator.js.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/infrastructure.d.ts +35 -0
- package/dist/infrastructure.d.ts.map +1 -0
- package/dist/infrastructure.js +49 -0
- package/dist/infrastructure.js.map +1 -0
- package/dist/metadata.d.ts +15 -0
- package/dist/metadata.d.ts.map +1 -0
- package/dist/metadata.js +202 -0
- package/dist/metadata.js.map +1 -0
- package/dist/mikro-orm-repository-adapter.d.ts +110 -0
- package/dist/mikro-orm-repository-adapter.d.ts.map +1 -0
- package/dist/mikro-orm-repository-adapter.js +428 -0
- package/dist/mikro-orm-repository-adapter.js.map +1 -0
- package/dist/plain-entity.d.ts +31 -0
- package/dist/plain-entity.d.ts.map +1 -0
- package/dist/plain-entity.js +36 -0
- package/dist/plain-entity.js.map +1 -0
- package/package.json +41 -0
package/README.md
ADDED
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
# @kavo/mikroorm
|
|
2
|
+
|
|
3
|
+
MikroORM adapter for Kavo: implements `RepositoryAdapter`
|
|
4
|
+
(`EntityReader` + `EntityWriter`) from `@kavo/core` over a MikroORM
|
|
5
|
+
`EntityManager`. `TransactionManager` is not implemented — see the
|
|
6
|
+
`@remarks` on that interface in `@kavo/core`.
|
|
7
|
+
|
|
8
|
+
**May depend on:** `@kavo/core`, `@mikro-orm/core` (peer). **Never on:**
|
|
9
|
+
`@kavo/nest` or any framework.
|
|
10
|
+
|
|
11
|
+
Fully implemented: CRUD, filtering/sorting/pagination (including across
|
|
12
|
+
relation paths), soft delete/restore/purge, and nested relation includes
|
|
13
|
+
(via `populate`). MikroORM declares no delete-marker column the way
|
|
14
|
+
TypeORM's `@DeleteDateColumn` does, so the seam reports none — but read the
|
|
15
|
+
soft-delete note under "Known limitations" before assuming that means soft
|
|
16
|
+
delete is off until you configure it.
|
|
17
|
+
|
|
18
|
+
## Usage
|
|
19
|
+
|
|
20
|
+
Like `@kavo/typeorm` and unlike `@kavo/prisma`, there is nothing to declare
|
|
21
|
+
twice: a MikroORM entity is a real runtime class carrying its own metadata,
|
|
22
|
+
so the class you pass to `createCrud` _is_ the identity core needs — no
|
|
23
|
+
marker classes, no `entities` list beyond the one MikroORM already has.
|
|
24
|
+
|
|
25
|
+
```ts
|
|
26
|
+
import { Collection, Entity, ManyToOne, OneToMany, PrimaryKey, Property, MikroORM } from "@mikro-orm/core";
|
|
27
|
+
import { defineConfig } from "@mikro-orm/postgresql";
|
|
28
|
+
import { createMikroOrmKavo } from "@kavo/mikroorm";
|
|
29
|
+
|
|
30
|
+
@Entity()
|
|
31
|
+
class Author {
|
|
32
|
+
@PrimaryKey({ type: "number" })
|
|
33
|
+
id!: number;
|
|
34
|
+
|
|
35
|
+
@Property({ type: "string" })
|
|
36
|
+
name!: string;
|
|
37
|
+
|
|
38
|
+
@OneToMany(() => Book, (book) => book.author)
|
|
39
|
+
books = new Collection<Book>(this);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const orm = await MikroORM.init(defineConfig({ dbName: "app", entities: [Author, Book] }));
|
|
43
|
+
const kavo = createMikroOrmKavo(orm, { caseInsensitiveFilters: true });
|
|
44
|
+
|
|
45
|
+
const authors = kavo.createCrud(Author);
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`createInfrastructure`/`createMikroOrmKavo` take the `MikroORM` instance
|
|
49
|
+
itself rather than an `EntityManager`: every adapter operation calls
|
|
50
|
+
`orm.em.fork()` to get its own manager, which is what keeps one request's
|
|
51
|
+
identity map out of the next one's.
|
|
52
|
+
|
|
53
|
+
## `caseInsensitiveFilters`
|
|
54
|
+
|
|
55
|
+
`ILIKE` maps to MikroORM's `$ilike`, which **only PostgreSQL supports** —
|
|
56
|
+
SQLite, MySQL, and MongoDB pass the token through to the driver and fail
|
|
57
|
+
with a syntax error. MikroORM's `Platform` exposes nothing to detect this
|
|
58
|
+
from, so it is a declared setting, defaulting to `false` (the value that
|
|
59
|
+
works everywhere). Turn it on for PostgreSQL.
|
|
60
|
+
|
|
61
|
+
With it off, `ILIKE` translates exactly like `LIKE`. On SQLite that is not
|
|
62
|
+
even a loss — SQLite's own `LIKE` is already ASCII case-insensitive.
|
|
63
|
+
|
|
64
|
+
## Known limitations
|
|
65
|
+
|
|
66
|
+
- **`LIKE` escapes are driver-dependent.** MikroORM has no way to attach an
|
|
67
|
+
`ESCAPE` clause to a `LIKE`, so the query grammar's `\` escape for a
|
|
68
|
+
literal `%`/`_` is honored only by drivers that default to backslash
|
|
69
|
+
(PostgreSQL, MySQL). On SQLite, which has no default escape character,
|
|
70
|
+
`filter[name][like]=100\%` matches the literal text `100\` followed by
|
|
71
|
+
anything, rather than the string `100%`.
|
|
72
|
+
- **Soft-deleted related rows are pruned in memory.** MikroORM's
|
|
73
|
+
`populateWhere` cannot express per-level scoping: nesting a child condition
|
|
74
|
+
makes it a predicate on the _parent_, so a parent with no surviving
|
|
75
|
+
children disappears instead of coming back empty. The adapter populates
|
|
76
|
+
everything and prunes the loaded tree, which is correct at any depth but
|
|
77
|
+
does fetch soft-deleted related rows before discarding them. Soft-deleted
|
|
78
|
+
_roots_ are still excluded in SQL.
|
|
79
|
+
- **`IncludeNode.strategy` is ignored.** MikroORM resolves `populate` with
|
|
80
|
+
its own queries and applies `limit`/`offset` to the root either way, so
|
|
81
|
+
the join/batch distinction has nothing to control here — and MikroORM's
|
|
82
|
+
`strategy` option is per-query rather than per-relation, so it could not
|
|
83
|
+
express a mixed include tree anyway. Same posture as `@kavo/prisma`.
|
|
84
|
+
- **MikroORM's own property options win at the boundary.** Rows are
|
|
85
|
+
converted with `wrap(entity).toObject()`, so a custom `serializer` runs
|
|
86
|
+
before core ever sees the row.
|
|
87
|
+
- **The soft-delete marker is writable, and it is on by default.** Nothing
|
|
88
|
+
in a MikroORM entity declares a delete column, so the adapter cannot mark
|
|
89
|
+
it generated — but `softDelete` defaults to
|
|
90
|
+
`{ field: "deletedAt", strategy: "auto" }` and core matches that _name_
|
|
91
|
+
against your columns, so any entity with a `deletedAt` property is
|
|
92
|
+
soft-deletable with no config at all. The marker then sits in the derived
|
|
93
|
+
writable projection: a plain `PATCH` of it soft-deletes a row, bypassing
|
|
94
|
+
`deleteOne`'s already-deleted check and even
|
|
95
|
+
`operations: { deleteOne: false }`. It cannot _revive_ one — writes are
|
|
96
|
+
scoped to the live set, so a soft-deleted row 404s on `PUT`/`PATCH`. This
|
|
97
|
+
is the same hole `@kavo/prisma` and `@kavo/mongoose` have (only
|
|
98
|
+
`@kavo/typeorm` escapes it, because `@DeleteDateColumn` is detectable and
|
|
99
|
+
therefore markable), and the fix belongs in core. Until then, register an
|
|
100
|
+
explicit `update`/`patch` DTO that omits the marker.
|
|
101
|
+
- **A non-auto-increment primary key is client-writable.**
|
|
102
|
+
`@PrimaryKey() id: string = v4()` carries none of MikroORM's generated
|
|
103
|
+
flags, so a `PATCH` can rewrite a row's identity. A numeric `@PrimaryKey()`
|
|
104
|
+
is auto-increment and safe. Name the write DTOs explicitly for any entity
|
|
105
|
+
with a caller-assigned key.
|
|
106
|
+
- **`hidden` and `lazy` properties are dropped from Kavo entirely.** Not
|
|
107
|
+
merely hidden from responses — excluding them from the metadata seam is
|
|
108
|
+
what keeps them off the default filter/sort allowlists, where an invisible
|
|
109
|
+
but filterable column is a blind extraction oracle. The trade is the one
|
|
110
|
+
`@kavo/mongoose` makes for `select: false`: Kavo does not manage such a
|
|
111
|
+
property at all, so write it through a custom operation or the ORM.
|
|
112
|
+
- **A MikroORM `@Filter` is applied on top of Kavo's scoping.** Kavo owns
|
|
113
|
+
soft-delete scoping through `softDelete.field`; a default-on MikroORM
|
|
114
|
+
soft-delete filter would AND a second predicate onto every query and
|
|
115
|
+
quietly defeat `withDeleted`. Use one or the other, not both.
|
|
116
|
+
- **No transactions.** The `TransactionManager` seam is unbuilt across every
|
|
117
|
+
Kavo adapter today.
|
|
118
|
+
- **Composite primary keys are refused.** `buildEntityMetadata` raises
|
|
119
|
+
`KAVO_CONFIG_INVALID` for an entity with more than one `@PrimaryKey`.
|
|
120
|
+
|
|
121
|
+
## Soft delete and unique indexes
|
|
122
|
+
|
|
123
|
+
A soft-deleted row still occupies its unique indexes, so re-creating "the
|
|
124
|
+
same" row after a soft delete raises a 409 conflict — the honest answer,
|
|
125
|
+
since the value _is_ taken. The fix is a partial unique index scoped to
|
|
126
|
+
live rows:
|
|
127
|
+
|
|
128
|
+
```sql
|
|
129
|
+
CREATE UNIQUE INDEX author_email_live ON author (email) WHERE deleted_at IS NULL;
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
Full design notes: [`docs/internals/architecture/17-mikroorm-adapter.md`](../../../docs/internals/architecture/17-mikroorm-adapter.md).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ErrorContext } from "@kavo/core";
|
|
2
|
+
import { KavoException } from "@kavo/core";
|
|
3
|
+
/**
|
|
4
|
+
* The error-mapping table: driver error → Kavo exception.
|
|
5
|
+
*
|
|
6
|
+
* | MikroORM condition | Exception |
|
|
7
|
+
* | ------------------------------------------- | ---------------------------------- |
|
|
8
|
+
* | `UniqueConstraintViolationException` | ConflictException |
|
|
9
|
+
* | `ForeignKeyConstraintViolationException` | ConflictException |
|
|
10
|
+
* | `DeadlockException` / `LockWaitTimeoutException` | TransactionException (retryable) |
|
|
11
|
+
* | anything else | PersistenceException with `cause` |
|
|
12
|
+
*
|
|
13
|
+
* The same four rows `@kavo/typeorm`'s table has, reached differently:
|
|
14
|
+
* MikroORM normalizes each driver's native error into its own exception
|
|
15
|
+
* hierarchy before it surfaces, so this matches on those classes rather than
|
|
16
|
+
* on Postgres SQLSTATEs, MySQL errnos, and SQLite extended codes the way the
|
|
17
|
+
* TypeORM adapter must. Every driver MikroORM supports is covered by that
|
|
18
|
+
* normalization, and anything it does not recognize falls through to
|
|
19
|
+
* `PersistenceException` — the original error always travels as `cause`,
|
|
20
|
+
* never swallowed.
|
|
21
|
+
*
|
|
22
|
+
* **Soft delete and unique indexes.** A soft-deleted row still occupies its
|
|
23
|
+
* unique indexes, so re-creating "the same" row after a soft delete raises a
|
|
24
|
+
* unique violation — mapped here to a 409 like any other conflict, which is
|
|
25
|
+
* the honest answer: the value *is* taken. Kavo never rewrites indexes; the
|
|
26
|
+
* standard fix is a partial/filtered unique index on the live rows only,
|
|
27
|
+
* e.g. in Postgres
|
|
28
|
+
* `CREATE UNIQUE INDEX … ON author (email) WHERE deleted_at IS NULL`
|
|
29
|
+
* (SQLite supports the same form; MySQL needs a generated column).
|
|
30
|
+
*/
|
|
31
|
+
export declare function mapDriverError(error: unknown, context: ErrorContext): KavoException;
|
|
32
|
+
//# 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,EAAqB,aAAa,EAA8C,MAAM,YAAY,CAAC;AAQ1G;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,YAAY,GAAG,aAAa,CAsBnF"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { ConflictException, KavoException, PersistenceException, TransactionException } from "@kavo/core";
|
|
2
|
+
import { DeadlockException, ForeignKeyConstraintViolationException, LockWaitTimeoutException, UniqueConstraintViolationException, } from "@mikro-orm/core";
|
|
3
|
+
/**
|
|
4
|
+
* The error-mapping table: driver error → Kavo exception.
|
|
5
|
+
*
|
|
6
|
+
* | MikroORM condition | Exception |
|
|
7
|
+
* | ------------------------------------------- | ---------------------------------- |
|
|
8
|
+
* | `UniqueConstraintViolationException` | ConflictException |
|
|
9
|
+
* | `ForeignKeyConstraintViolationException` | ConflictException |
|
|
10
|
+
* | `DeadlockException` / `LockWaitTimeoutException` | TransactionException (retryable) |
|
|
11
|
+
* | anything else | PersistenceException with `cause` |
|
|
12
|
+
*
|
|
13
|
+
* The same four rows `@kavo/typeorm`'s table has, reached differently:
|
|
14
|
+
* MikroORM normalizes each driver's native error into its own exception
|
|
15
|
+
* hierarchy before it surfaces, so this matches on those classes rather than
|
|
16
|
+
* on Postgres SQLSTATEs, MySQL errnos, and SQLite extended codes the way the
|
|
17
|
+
* TypeORM adapter must. Every driver MikroORM supports is covered by that
|
|
18
|
+
* normalization, and anything it does not recognize falls through to
|
|
19
|
+
* `PersistenceException` — the original error always travels as `cause`,
|
|
20
|
+
* never swallowed.
|
|
21
|
+
*
|
|
22
|
+
* **Soft delete and unique indexes.** A soft-deleted row still occupies its
|
|
23
|
+
* unique indexes, so re-creating "the same" row after a soft delete raises a
|
|
24
|
+
* unique violation — mapped here to a 409 like any other conflict, which is
|
|
25
|
+
* the honest answer: the value *is* taken. Kavo never rewrites indexes; the
|
|
26
|
+
* standard fix is a partial/filtered unique index on the live rows only,
|
|
27
|
+
* e.g. in Postgres
|
|
28
|
+
* `CREATE UNIQUE INDEX … ON author (email) WHERE deleted_at IS NULL`
|
|
29
|
+
* (SQLite supports the same form; MySQL needs a generated column).
|
|
30
|
+
*/
|
|
31
|
+
export function mapDriverError(error, context) {
|
|
32
|
+
if (error instanceof KavoException)
|
|
33
|
+
return error;
|
|
34
|
+
const entity = context.entityName ?? "entity";
|
|
35
|
+
if (error instanceof UniqueConstraintViolationException || error instanceof ForeignKeyConstraintViolationException) {
|
|
36
|
+
return new ConflictException({
|
|
37
|
+
messageParams: { entity },
|
|
38
|
+
context,
|
|
39
|
+
cause: error,
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
if (error instanceof DeadlockException || error instanceof LockWaitTimeoutException) {
|
|
43
|
+
return new TransactionException({ retryable: true, context, cause: error });
|
|
44
|
+
}
|
|
45
|
+
return new PersistenceException({
|
|
46
|
+
messageParams: { operation: context.operation ?? "unknown" },
|
|
47
|
+
context,
|
|
48
|
+
cause: error,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//# 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,EAAE,iBAAiB,EAAE,aAAa,EAAE,oBAAoB,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAC;AAC1G,OAAO,EACL,iBAAiB,EACjB,sCAAsC,EACtC,wBAAwB,EACxB,kCAAkC,GACnC,MAAM,iBAAiB,CAAC;AAEzB;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;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;IAE9C,IAAI,KAAK,YAAY,kCAAkC,IAAI,KAAK,YAAY,sCAAsC,EAAE,CAAC;QACnH,OAAO,IAAI,iBAAiB,CAAC;YAC3B,aAAa,EAAE,EAAE,MAAM,EAAE;YACzB,OAAO;YACP,KAAK,EAAE,KAAK;SACb,CAAC,CAAC;IACL,CAAC;IAED,IAAI,KAAK,YAAY,iBAAiB,IAAI,KAAK,YAAY,wBAAwB,EAAE,CAAC;QACpF,OAAO,IAAI,oBAAoB,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAC;IAC9E,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,49 @@
|
|
|
1
|
+
import type { Filter } from "@kavo/core";
|
|
2
|
+
/**
|
|
3
|
+
* A MikroORM `FilterQuery`, built structurally as a plain object. MikroORM's
|
|
4
|
+
* own `FilterQuery<T>` is entity-generic and deliberately not used here: the
|
|
5
|
+
* translator works from core's AST, which is not tied to a MikroORM entity
|
|
6
|
+
* type, and a plain object is what lets this be unit-tested without a
|
|
7
|
+
* database.
|
|
8
|
+
*/
|
|
9
|
+
export type MikroWhere = Record<string, unknown>;
|
|
10
|
+
export interface FilterTranslatorOptions {
|
|
11
|
+
/**
|
|
12
|
+
* The entity's primary-key property. Used only to spell a contradiction
|
|
13
|
+
* for the degenerate empty-group cases — see {@link matchesNothing}.
|
|
14
|
+
*/
|
|
15
|
+
readonly idField: string;
|
|
16
|
+
/**
|
|
17
|
+
* Whether the target driver supports MikroORM's `$ilike` operator.
|
|
18
|
+
* PostgreSQL does; SQLite, MySQL, and MongoDB do not — MikroORM passes
|
|
19
|
+
* `ilike` straight through to SQL, so on those drivers it is a syntax
|
|
20
|
+
* error rather than a degraded match. A MikroORM `Platform` exposes
|
|
21
|
+
* nothing to detect this from, so it is a caller-declared setting (see
|
|
22
|
+
* `MikroOrmInfrastructureOptions.caseInsensitiveFilters`), the same
|
|
23
|
+
* posture `@kavo/prisma` takes for `mode: "insensitive"`.
|
|
24
|
+
*
|
|
25
|
+
* Unlike `@kavo/prisma` this defaults to **`false`**, because the two
|
|
26
|
+
* failure modes are not symmetric: declaring it off on PostgreSQL costs
|
|
27
|
+
* case-insensitivity, while leaving it on anywhere else makes every
|
|
28
|
+
* `ILIKE` query throw. On SQLite the default is not even a loss —
|
|
29
|
+
* SQLite's own `LIKE` is already ASCII case-insensitive.
|
|
30
|
+
*/
|
|
31
|
+
readonly caseInsensitiveFilters: boolean;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Filter AST → MikroORM `FilterQuery`.
|
|
35
|
+
*
|
|
36
|
+
* Like `@kavo/prisma`'s translator and unlike `@kavo/typeorm`'s, this needs
|
|
37
|
+
* no query-builder state — no join aliases, no parameter numbering. MikroORM
|
|
38
|
+
* nests relation paths natively (`{ author: { name: { $eq: "Ada" } } }`) and
|
|
39
|
+
* adds the join itself, so a relation-path field nests the same translation
|
|
40
|
+
* one level deeper instead of registering a join.
|
|
41
|
+
*
|
|
42
|
+
* Every comparison is wrapped in an explicit operator (`{ $eq: v }`, never
|
|
43
|
+
* the bare `{ field: v }` shorthand). Core coerces filter values to scalars
|
|
44
|
+
* upstream, so this is defence in depth rather than the only guard — but an
|
|
45
|
+
* object arriving through the shorthand would be spliced in as *operators*,
|
|
46
|
+
* and the boundary's job is to be safe on its own terms.
|
|
47
|
+
*/
|
|
48
|
+
export declare function translateFilter<Entity>(filter: Filter<Entity>, options: FilterTranslatorOptions): MikroWhere | undefined;
|
|
49
|
+
//# 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;;;;;;GAMG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAEjD,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB;;;;;;;;;;;;;;OAcG;IACH,QAAQ,CAAC,sBAAsB,EAAE,OAAO,CAAC;CAC1C;AAgBD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,eAAe,CAAC,MAAM,EACpC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC,EACtB,OAAO,EAAE,uBAAuB,GAC/B,UAAU,GAAG,SAAS,CAIxB"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { assertNever } from "@kavo/core";
|
|
2
|
+
/**
|
|
3
|
+
* A predicate that matches no row.
|
|
4
|
+
*
|
|
5
|
+
* MikroORM rejects an empty `$and`/`$or` outright, and `$not: {}` negates
|
|
6
|
+
* *no condition at all*, which it renders as a match-everything rather than
|
|
7
|
+
* a match-nothing — the opposite of what these cases need. An empty `$in`
|
|
8
|
+
* is the one spelling MikroORM turns into a genuine contradiction on every
|
|
9
|
+
* driver, which is why this is expressed against the primary key rather
|
|
10
|
+
* than with a bare connective.
|
|
11
|
+
*/
|
|
12
|
+
function matchesNothing(idField) {
|
|
13
|
+
return { [idField]: { $in: [] } };
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Filter AST → MikroORM `FilterQuery`.
|
|
17
|
+
*
|
|
18
|
+
* Like `@kavo/prisma`'s translator and unlike `@kavo/typeorm`'s, this needs
|
|
19
|
+
* no query-builder state — no join aliases, no parameter numbering. MikroORM
|
|
20
|
+
* nests relation paths natively (`{ author: { name: { $eq: "Ada" } } }`) and
|
|
21
|
+
* adds the join itself, so a relation-path field nests the same translation
|
|
22
|
+
* one level deeper instead of registering a join.
|
|
23
|
+
*
|
|
24
|
+
* Every comparison is wrapped in an explicit operator (`{ $eq: v }`, never
|
|
25
|
+
* the bare `{ field: v }` shorthand). Core coerces filter values to scalars
|
|
26
|
+
* upstream, so this is defence in depth rather than the only guard — but an
|
|
27
|
+
* object arriving through the shorthand would be spliced in as *operators*,
|
|
28
|
+
* and the boundary's job is to be safe on its own terms.
|
|
29
|
+
*/
|
|
30
|
+
export function translateFilter(filter, options) {
|
|
31
|
+
const root = filter.root;
|
|
32
|
+
if (root === null)
|
|
33
|
+
return undefined;
|
|
34
|
+
return translateExpression(root, options);
|
|
35
|
+
}
|
|
36
|
+
function translateExpression(expression, options) {
|
|
37
|
+
if (expression.kind === "condition") {
|
|
38
|
+
return translateCondition(expression, options);
|
|
39
|
+
}
|
|
40
|
+
const children = expression.children.map((child) => translateExpression(child, options));
|
|
41
|
+
if (expression.operator === "NOT") {
|
|
42
|
+
// Core's parser gives a NOT group exactly one child. With no child there
|
|
43
|
+
// is nothing to negate, and "not (anything)" matches nothing.
|
|
44
|
+
return children.length === 0 ? matchesNothing(options.idField) : { $not: children[0] };
|
|
45
|
+
}
|
|
46
|
+
// The parser enforces arity, so the empty cases only guard hand-built ASTs
|
|
47
|
+
// passed programmatically — but MikroORM rejects an empty `$and`/`$or`, so
|
|
48
|
+
// the identity for each connective is spelled explicitly.
|
|
49
|
+
if (children.length === 0) {
|
|
50
|
+
return expression.operator === "OR" ? matchesNothing(options.idField) : {};
|
|
51
|
+
}
|
|
52
|
+
return expression.operator === "OR" ? { $or: children } : { $and: children };
|
|
53
|
+
}
|
|
54
|
+
/** Nest `condition.field` (`"author.name"`) into MikroORM's native relation-path shape. */
|
|
55
|
+
function nest(field, leaf) {
|
|
56
|
+
return field.split(".").reduceRight((inner, segment) => ({ [segment]: inner }), leaf);
|
|
57
|
+
}
|
|
58
|
+
function translateCondition(condition, options) {
|
|
59
|
+
const field = condition.field;
|
|
60
|
+
const value = condition.value;
|
|
61
|
+
switch (condition.operator) {
|
|
62
|
+
case "EQ":
|
|
63
|
+
return nest(field, { $eq: value });
|
|
64
|
+
case "NE":
|
|
65
|
+
// `$ne: null` is MikroORM's `IS NOT NULL`, so a null value needs no
|
|
66
|
+
// separate branch the way the SQL-string translator's does.
|
|
67
|
+
return nest(field, { $ne: value });
|
|
68
|
+
case "GT":
|
|
69
|
+
return nest(field, { $gt: value });
|
|
70
|
+
case "GTE":
|
|
71
|
+
return nest(field, { $gte: value });
|
|
72
|
+
case "LT":
|
|
73
|
+
return nest(field, { $lt: value });
|
|
74
|
+
case "LTE":
|
|
75
|
+
return nest(field, { $lte: value });
|
|
76
|
+
case "IN":
|
|
77
|
+
// MikroORM renders an empty `$in` as a contradiction rather than
|
|
78
|
+
// emitting invalid `IN ()`, and an empty `$nin` as a tautology — the
|
|
79
|
+
// same answers `@kavo/typeorm` has to spell out by hand because it
|
|
80
|
+
// writes the SQL itself.
|
|
81
|
+
return nest(field, { $in: value });
|
|
82
|
+
case "NOT_IN":
|
|
83
|
+
return nest(field, { $nin: value });
|
|
84
|
+
case "LIKE":
|
|
85
|
+
// The pattern reaches SQL `LIKE` verbatim. MikroORM has no way to
|
|
86
|
+
// attach an `ESCAPE` clause, so the grammar's `\` escape for a literal
|
|
87
|
+
// `%`/`_` is honored only by drivers that default to backslash
|
|
88
|
+
// (PostgreSQL, MySQL) — not by SQLite, which has no default escape
|
|
89
|
+
// character. See doc 17, "Adapter-specific caveats".
|
|
90
|
+
return nest(field, { $like: value });
|
|
91
|
+
case "ILIKE":
|
|
92
|
+
return nest(field, options.caseInsensitiveFilters ? { $ilike: value } : { $like: value });
|
|
93
|
+
case "BETWEEN": {
|
|
94
|
+
const [low, high] = value;
|
|
95
|
+
return nest(field, { $gte: low, $lte: high });
|
|
96
|
+
}
|
|
97
|
+
case "IS_NULL":
|
|
98
|
+
return nest(field, { $eq: null });
|
|
99
|
+
case "IS_NOT_NULL":
|
|
100
|
+
return nest(field, { $ne: null });
|
|
101
|
+
default:
|
|
102
|
+
// Every AST operator must translate to a predicate. Falling through
|
|
103
|
+
// silently would drop the condition and widen the result set, so the
|
|
104
|
+
// union is proven total here at build time — the same guarantee the
|
|
105
|
+
// other adapters' translators give.
|
|
106
|
+
return assertNever(condition.operator, "filter operator");
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
//# 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,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @kavo/mikroorm — MikroORM adapter for Kavo.
|
|
3
|
+
*
|
|
4
|
+
* Implements `@kavo/core`'s `RepositoryAdapter` over a MikroORM
|
|
5
|
+
* `EntityManager` and feeds core's entity-metadata seam from MikroORM's own
|
|
6
|
+
* `MetadataStorage`. `@mikro-orm/core` is a peerDependency; `@kavo/core`
|
|
7
|
+
* never imports it.
|
|
8
|
+
*/
|
|
9
|
+
export { MikroOrmRepositoryAdapter } from "./mikro-orm-repository-adapter.js";
|
|
10
|
+
export { translateFilter, type FilterTranslatorOptions, type MikroWhere } from "./filter-translator.js";
|
|
11
|
+
export { buildEntityMetadata } from "./metadata.js";
|
|
12
|
+
export { mapDriverError } from "./error-mapping.js";
|
|
13
|
+
export { toPlain, toPlainAll } from "./plain-entity.js";
|
|
14
|
+
export { createInfrastructure, createMikroOrmKavo, type MikroOrmInfrastructureOptions } from "./infrastructure.js";
|
|
15
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAE,KAAK,uBAAuB,EAAE,KAAK,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACxG,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAE,KAAK,6BAA6B,EAAE,MAAM,qBAAqB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @kavo/mikroorm — MikroORM adapter for Kavo.
|
|
3
|
+
*
|
|
4
|
+
* Implements `@kavo/core`'s `RepositoryAdapter` over a MikroORM
|
|
5
|
+
* `EntityManager` and feeds core's entity-metadata seam from MikroORM's own
|
|
6
|
+
* `MetadataStorage`. `@mikro-orm/core` is a peerDependency; `@kavo/core`
|
|
7
|
+
* never imports it.
|
|
8
|
+
*/
|
|
9
|
+
export { MikroOrmRepositoryAdapter } from "./mikro-orm-repository-adapter.js";
|
|
10
|
+
export { translateFilter } from "./filter-translator.js";
|
|
11
|
+
export { buildEntityMetadata } from "./metadata.js";
|
|
12
|
+
export { mapDriverError } from "./error-mapping.js";
|
|
13
|
+
export { toPlain, toPlainAll } from "./plain-entity.js";
|
|
14
|
+
export { createInfrastructure, createMikroOrmKavo } from "./infrastructure.js";
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AACH,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,eAAe,EAAiD,MAAM,wBAAwB,CAAC;AACxG,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AACxD,OAAO,EAAE,oBAAoB,EAAE,kBAAkB,EAAsC,MAAM,qBAAqB,CAAC"}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { KavoInfrastructure, KavoInstance, KavoOptions } from "@kavo/core";
|
|
2
|
+
import type { MikroORM } from "@mikro-orm/core";
|
|
3
|
+
/**
|
|
4
|
+
* What MikroORM needs beyond the ORM instance. Nothing is required — a
|
|
5
|
+
* MikroORM entity is a real runtime class carrying its own metadata, so the
|
|
6
|
+
* ORM instance alone supplies both halves of core's infrastructure seam,
|
|
7
|
+
* exactly as a TypeORM `DataSource` does and unlike `@kavo/prisma`, which
|
|
8
|
+
* needs caller-declared marker classes (ADR-0017).
|
|
9
|
+
*/
|
|
10
|
+
export interface MikroOrmInfrastructureOptions {
|
|
11
|
+
/**
|
|
12
|
+
* Whether the driver supports MikroORM's `$ilike` operator — PostgreSQL
|
|
13
|
+
* does; SQLite, MySQL, and MongoDB reject it as a syntax error. Defaults
|
|
14
|
+
* to `false`, the setting that works on every driver; turn it on for
|
|
15
|
+
* PostgreSQL to make `ILIKE` genuinely case-insensitive. See
|
|
16
|
+
* `FilterTranslatorOptions`.
|
|
17
|
+
*/
|
|
18
|
+
readonly caseInsensitiveFilters?: boolean;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* The MikroORM implementation of core's infrastructure seam: metadata and
|
|
22
|
+
* adapters derived from one `MikroORM` instance, cached per entity (metadata
|
|
23
|
+
* derivation and adapter construction are bootstrap work, not per-request
|
|
24
|
+
* work) — same shape as `@kavo/typeorm`'s `createInfrastructure`.
|
|
25
|
+
*
|
|
26
|
+
* The instance is held rather than an `EntityManager`, because every adapter
|
|
27
|
+
* operation forks its own manager from it; see `MikroOrmRepositoryAdapter`.
|
|
28
|
+
*/
|
|
29
|
+
export declare function createInfrastructure(orm: MikroORM, options?: MikroOrmInfrastructureOptions): KavoInfrastructure;
|
|
30
|
+
/**
|
|
31
|
+
* Sugar for the common case: a Kavo root instance wired to one MikroORM
|
|
32
|
+
* instance, so `kavo.createCrud(Author)` is genuinely zero-config.
|
|
33
|
+
*/
|
|
34
|
+
export declare function createMikroOrmKavo(orm: MikroORM, options?: MikroOrmInfrastructureOptions & Omit<KavoOptions, "infrastructure">): KavoInstance;
|
|
35
|
+
//# 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,EAGV,kBAAkB,EAClB,YAAY,EACZ,WAAW,EAEZ,MAAM,YAAY,CAAC;AAEpB,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAIhD;;;;;;GAMG;AACH,MAAM,WAAW,6BAA6B;IAC5C;;;;;;OAMG;IACH,QAAQ,CAAC,sBAAsB,CAAC,EAAE,OAAO,CAAC;CAC3C;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,QAAQ,EAAE,OAAO,GAAE,6BAAkC,GAAG,kBAAkB,CA0BnH;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,QAAQ,EACb,OAAO,GAAE,6BAA6B,GAAG,IAAI,CAAC,WAAW,EAAE,gBAAgB,CAAM,GAChF,YAAY,CAMd"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { createKavo } from "@kavo/core";
|
|
2
|
+
import { buildEntityMetadata } from "./metadata.js";
|
|
3
|
+
import { MikroOrmRepositoryAdapter } from "./mikro-orm-repository-adapter.js";
|
|
4
|
+
/**
|
|
5
|
+
* The MikroORM implementation of core's infrastructure seam: metadata and
|
|
6
|
+
* adapters derived from one `MikroORM` instance, cached per entity (metadata
|
|
7
|
+
* derivation and adapter construction are bootstrap work, not per-request
|
|
8
|
+
* work) — same shape as `@kavo/typeorm`'s `createInfrastructure`.
|
|
9
|
+
*
|
|
10
|
+
* The instance is held rather than an `EntityManager`, because every adapter
|
|
11
|
+
* operation forks its own manager from it; see `MikroOrmRepositoryAdapter`.
|
|
12
|
+
*/
|
|
13
|
+
export function createInfrastructure(orm, options = {}) {
|
|
14
|
+
const metadataCache = new Map();
|
|
15
|
+
const adapterCache = new Map();
|
|
16
|
+
function metadataFor(entity) {
|
|
17
|
+
let metadata = metadataCache.get(entity);
|
|
18
|
+
if (metadata === undefined) {
|
|
19
|
+
metadata = buildEntityMetadata(orm, entity);
|
|
20
|
+
metadataCache.set(entity, metadata);
|
|
21
|
+
}
|
|
22
|
+
return metadata;
|
|
23
|
+
}
|
|
24
|
+
return {
|
|
25
|
+
metadataFor,
|
|
26
|
+
adapterFor(entity) {
|
|
27
|
+
let adapter = adapterCache.get(entity);
|
|
28
|
+
if (adapter === undefined) {
|
|
29
|
+
adapter = new MikroOrmRepositoryAdapter(orm, metadataFor(entity), {
|
|
30
|
+
caseInsensitiveFilters: options.caseInsensitiveFilters ?? false,
|
|
31
|
+
});
|
|
32
|
+
adapterCache.set(entity, adapter);
|
|
33
|
+
}
|
|
34
|
+
return adapter;
|
|
35
|
+
},
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Sugar for the common case: a Kavo root instance wired to one MikroORM
|
|
40
|
+
* instance, so `kavo.createCrud(Author)` is genuinely zero-config.
|
|
41
|
+
*/
|
|
42
|
+
export function createMikroOrmKavo(orm, options = {}) {
|
|
43
|
+
const { caseInsensitiveFilters, ...kavoOptions } = options;
|
|
44
|
+
return createKavo({
|
|
45
|
+
...kavoOptions,
|
|
46
|
+
infrastructure: createInfrastructure(orm, { caseInsensitiveFilters }),
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
//# 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;AACpD,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAoB9E;;;;;;;;GAQG;AACH,MAAM,UAAU,oBAAoB,CAAC,GAAa,EAAE,UAAyC,EAAE;IAC7F,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,GAAG,EAAE,MAAM,CAAmB,CAAC;YAC9D,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,yBAAyB,CAAC,GAAG,EAAE,WAAW,CAAC,MAAM,CAAC,EAAE;oBAChE,sBAAsB,EAAE,OAAO,CAAC,sBAAsB,IAAI,KAAK;iBAChE,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;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAChC,GAAa,EACb,UAA+E,EAAE;IAEjF,MAAM,EAAE,sBAAsB,EAAE,GAAG,WAAW,EAAE,GAAG,OAAO,CAAC;IAC3D,OAAO,UAAU,CAAC;QAChB,GAAG,WAAW;QACd,cAAc,EAAE,oBAAoB,CAAC,GAAG,EAAE,EAAE,sBAAsB,EAAE,CAAC;KACtE,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { ClassRef, EntityMetadata } from "@kavo/core";
|
|
2
|
+
import type { MikroORM } from "@mikro-orm/core";
|
|
3
|
+
/**
|
|
4
|
+
* Build the core `EntityMetadata` for one entity from MikroORM's own
|
|
5
|
+
* `MetadataStorage`: the adapter feeds core's metadata seam; core never sees
|
|
6
|
+
* MikroORM types.
|
|
7
|
+
*
|
|
8
|
+
* Unlike `@kavo/prisma`, no marker class is needed (ADR-0017 exists because
|
|
9
|
+
* Prisma erases its models at compile time) — a MikroORM entity is a real
|
|
10
|
+
* runtime class that already carries its own metadata, so the class the
|
|
11
|
+
* caller passes to `createCrud` *is* the identity, exactly as in
|
|
12
|
+
* `@kavo/typeorm`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildEntityMetadata<Entity extends object>(orm: MikroORM, entity: ClassRef<Entity>): EntityMetadata<Entity>;
|
|
15
|
+
//# 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,EAAkB,QAAQ,EAAE,MAAM,iBAAiB,CAAC;AAyGhE;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,SAAS,MAAM,EACvD,GAAG,EAAE,QAAQ,EACb,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,GACvB,cAAc,CAAC,MAAM,CAAC,CAmGxB"}
|