@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
|
@@ -0,0 +1,428 @@
|
|
|
1
|
+
import { AlreadyDeletedException, ConfigurationException, NotDeletedException, NotFoundException } from "@kavo/core";
|
|
2
|
+
import { wrap } from "@mikro-orm/core";
|
|
3
|
+
import { mapDriverError } from "./error-mapping.js";
|
|
4
|
+
import { translateFilter } from "./filter-translator.js";
|
|
5
|
+
import { toPlain, toPlainAll } from "./plain-entity.js";
|
|
6
|
+
/**
|
|
7
|
+
* `RepositoryAdapter` over a MikroORM `EntityManager`: CRUD with hard *or*
|
|
8
|
+
* soft delete, restore, purge, filtering, sorting, pagination, optional
|
|
9
|
+
* counting, and nested relation includes.
|
|
10
|
+
*
|
|
11
|
+
* Two things are specific to MikroORM and load-bearing:
|
|
12
|
+
*
|
|
13
|
+
* **Every method forks the EntityManager.** MikroORM is a Unit-of-Work ORM:
|
|
14
|
+
* an `EntityManager` owns an identity map that caches every entity it has
|
|
15
|
+
* loaded, and reusing one across requests would serve stale rows and leak
|
|
16
|
+
* one caller's entities into another's. `orm.em` is the *root* manager and
|
|
17
|
+
* is not meant to be queried directly; `orm.em.fork()` gives each operation
|
|
18
|
+
* a clean, isolated one, which is the same scope a request-scoped
|
|
19
|
+
* `RequestContext` would give a hand-written MikroORM application.
|
|
20
|
+
*
|
|
21
|
+
* **`IncludeNode.strategy` is deliberately ignored**, exactly as in
|
|
22
|
+
* `@kavo/prisma` and unlike `@kavo/typeorm`. The TypeORM adapter translates
|
|
23
|
+
* the join/batch split because it drives a raw SQL query builder, where a
|
|
24
|
+
* to-many `JOIN` multiplies root rows and separate batched queries are how
|
|
25
|
+
* that is avoided. MikroORM resolves `populate` with its own queries and
|
|
26
|
+
* applies `limit`/`offset` to the root regardless of the load strategy, so a
|
|
27
|
+
* to-many include never disturbs pagination here. There is nothing left for
|
|
28
|
+
* the distinction to control, and MikroORM's `strategy` option is per-query
|
|
29
|
+
* rather than per-relation anyway — it could not express a mixed tree.
|
|
30
|
+
*/
|
|
31
|
+
export class MikroOrmRepositoryAdapter {
|
|
32
|
+
orm;
|
|
33
|
+
entity;
|
|
34
|
+
idField;
|
|
35
|
+
filterOptions;
|
|
36
|
+
/**
|
|
37
|
+
* Relation property name → the target entity's primary-key property.
|
|
38
|
+
* Resolved lazily: a bidirectional relation's target may not be registered
|
|
39
|
+
* with MikroORM's metadata storage at the time this adapter is built.
|
|
40
|
+
*/
|
|
41
|
+
relationIdFields;
|
|
42
|
+
constructor(orm, metadata, options = {}) {
|
|
43
|
+
this.orm = orm;
|
|
44
|
+
this.entity = metadata.entity;
|
|
45
|
+
this.idField = metadata.idField;
|
|
46
|
+
this.filterOptions = {
|
|
47
|
+
idField: metadata.idField,
|
|
48
|
+
caseInsensitiveFilters: options.caseInsensitiveFilters ?? false,
|
|
49
|
+
};
|
|
50
|
+
const relationIdFields = new Map();
|
|
51
|
+
for (const relation of metadata.relations) {
|
|
52
|
+
relationIdFields.set(relation.name, () => {
|
|
53
|
+
const target = this.orm.getMetadata().getByClassName(relation.target().name, false);
|
|
54
|
+
return target?.primaryKeys[0] ?? "id";
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
this.relationIdFields = relationIdFields;
|
|
58
|
+
}
|
|
59
|
+
/** A clean, isolated `EntityManager` for one operation — see the class doc. */
|
|
60
|
+
fork() {
|
|
61
|
+
return this.orm.em.fork();
|
|
62
|
+
}
|
|
63
|
+
// ── Reads ────────────────────────────────────────────────────────────
|
|
64
|
+
async findOneById(id, query, context) {
|
|
65
|
+
try {
|
|
66
|
+
const include = query?.include ?? {};
|
|
67
|
+
const where = this.scopeToLive({ [this.idField]: id }, context, query?.withDeleted ?? false, query?.onlyDeleted ?? false);
|
|
68
|
+
const row = await this.fork().findOne(this.entity, where, this.populateOptions(include));
|
|
69
|
+
return row === null ? null : pruneIncluded(toPlain(row), include);
|
|
70
|
+
}
|
|
71
|
+
catch (error) {
|
|
72
|
+
throw mapDriverError(error, errorContext(context));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
async findOne(query, context) {
|
|
76
|
+
try {
|
|
77
|
+
// `em.find(…, { limit: 1 })` rather than `em.findOne`: MikroORM's
|
|
78
|
+
// validator rejects `em.findOne` with an empty `where` outright, and an
|
|
79
|
+
// unfiltered query is a legitimate shape here — `findOne`'s contract is
|
|
80
|
+
// "first match of the query, or null", and a query with no filter
|
|
81
|
+
// matches everything. `em.find` accepts it, so routing through it keeps
|
|
82
|
+
// the contract without an empty-where special case. The two are
|
|
83
|
+
// otherwise identical: `findOne` is itself a limit-1 `find`.
|
|
84
|
+
const rows = await this.fork().find(this.entity, this.buildWhere(query, context), {
|
|
85
|
+
...this.buildFindOptions(query),
|
|
86
|
+
limit: 1,
|
|
87
|
+
});
|
|
88
|
+
const row = rows[0];
|
|
89
|
+
return row === undefined ? null : pruneIncluded(toPlain(row), query.include);
|
|
90
|
+
}
|
|
91
|
+
catch (error) {
|
|
92
|
+
throw mapDriverError(error, errorContext(context));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
async findMany(query, context) {
|
|
96
|
+
try {
|
|
97
|
+
const rows = await this.fork().find(this.entity, this.buildWhere(query, context), {
|
|
98
|
+
...this.buildFindOptions(query),
|
|
99
|
+
offset: query.pagination.offset,
|
|
100
|
+
limit: query.pagination.limit,
|
|
101
|
+
});
|
|
102
|
+
return toPlainAll(rows).map((row) => pruneIncluded(row, query.include));
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
throw mapDriverError(error, errorContext(context));
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
async count(query, context) {
|
|
109
|
+
try {
|
|
110
|
+
// A dedicated count query — never fetch-then-length: the engine only
|
|
111
|
+
// calls this when `query.count` is true, so `total: null` costs zero
|
|
112
|
+
// queries. No populate: counting matching roots never needs their
|
|
113
|
+
// relations.
|
|
114
|
+
return await this.fork().count(this.entity, this.buildWhere(query, context));
|
|
115
|
+
}
|
|
116
|
+
catch (error) {
|
|
117
|
+
throw mapDriverError(error, errorContext(context));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
/** The translated filter, narrowed to the request's soft-delete scope. */
|
|
121
|
+
buildWhere(query, context) {
|
|
122
|
+
return this.scopeToLive(translateFilter(query.filter, this.filterOptions), context, query.withDeleted, query.onlyDeleted);
|
|
123
|
+
}
|
|
124
|
+
buildFindOptions(query) {
|
|
125
|
+
const orderBy = query.sort.map((sort) => nestOrderBy(sort.field, sort.direction));
|
|
126
|
+
return {
|
|
127
|
+
...(orderBy.length > 0 && { orderBy }),
|
|
128
|
+
...this.populateOptions(query.include),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
// ── Relation includes ───────────────────────────────────────────────
|
|
132
|
+
/**
|
|
133
|
+
* Translate a validated `IncludeTree` into MikroORM's `populate` paths.
|
|
134
|
+
*
|
|
135
|
+
* Soft-delete scoping of included rows is deliberately **not** done here —
|
|
136
|
+
* see {@link pruneIncluded}. MikroORM's `populateWhere` cannot express it:
|
|
137
|
+
* a nested condition (`{ articles: { deletedAt: null, notes: { deletedAt:
|
|
138
|
+
* null } } }`) is read as a relation-path predicate on the *parent*, so an
|
|
139
|
+
* article with no live notes is dropped from the parent's collection
|
|
140
|
+
* altogether rather than coming back with an empty one. The dotted spelling
|
|
141
|
+
* MikroORM rejects outright, and the parent-only spelling silently leaves
|
|
142
|
+
* every deeper level unscoped.
|
|
143
|
+
*/
|
|
144
|
+
populateOptions(tree) {
|
|
145
|
+
const paths = populatePaths(tree);
|
|
146
|
+
return paths.length === 0 ? {} : { populate: paths };
|
|
147
|
+
}
|
|
148
|
+
// ── Soft delete ──────────────────────────────────────────────────────
|
|
149
|
+
/**
|
|
150
|
+
* Three-way soft-delete scope: exclude deleted rows by default, include
|
|
151
|
+
* both live and deleted with `withDeleted`, or restrict to only deleted
|
|
152
|
+
* with `onlyDeleted` (mutually exclusive — validated upstream).
|
|
153
|
+
*
|
|
154
|
+
* There is one shape to handle rather than TypeORM's two: MikroORM
|
|
155
|
+
* declares no delete-date column of its own, so the marker is always an
|
|
156
|
+
* ordinary property and always needs its predicate spelled out.
|
|
157
|
+
*/
|
|
158
|
+
scopeToLive(where, context, withDeleted, onlyDeleted = false) {
|
|
159
|
+
const softDelete = context.config.softDelete;
|
|
160
|
+
if (softDelete.strategy !== "soft")
|
|
161
|
+
return where ?? {};
|
|
162
|
+
if (onlyDeleted)
|
|
163
|
+
return and(where, { [softDelete.field]: { $ne: null } });
|
|
164
|
+
if (withDeleted)
|
|
165
|
+
return where ?? {};
|
|
166
|
+
return and(where, { [softDelete.field]: { $eq: null } });
|
|
167
|
+
}
|
|
168
|
+
/** The resolved strategy, refused when an operation requires soft. */
|
|
169
|
+
requireSoftDelete(context, operation) {
|
|
170
|
+
const softDelete = context.config.softDelete;
|
|
171
|
+
if (softDelete.strategy !== "soft") {
|
|
172
|
+
throw new ConfigurationException(context.entityName, "softDelete", `'${operation}' requires a soft-deletable entity, but '${context.entityName}' ` +
|
|
173
|
+
`resolves to a hard delete strategy`);
|
|
174
|
+
}
|
|
175
|
+
return softDelete;
|
|
176
|
+
}
|
|
177
|
+
isDeleted(row, field) {
|
|
178
|
+
return row[field] !== null && row[field] !== undefined;
|
|
179
|
+
}
|
|
180
|
+
/** One row by id, as a plain object, under the given soft-delete scope. */
|
|
181
|
+
async byId(id, context, withDeleted) {
|
|
182
|
+
const where = this.scopeToLive({ [this.idField]: id }, context, withDeleted);
|
|
183
|
+
const row = await this.fork().findOne(this.entity, where);
|
|
184
|
+
return row === null ? null : toPlain(row);
|
|
185
|
+
}
|
|
186
|
+
// ── Writes ───────────────────────────────────────────────────────────
|
|
187
|
+
async create(data, context) {
|
|
188
|
+
try {
|
|
189
|
+
const em = this.fork();
|
|
190
|
+
const created = em.create(this.entity, this.toWriteData(data));
|
|
191
|
+
await em.flush();
|
|
192
|
+
return toPlain(created);
|
|
193
|
+
}
|
|
194
|
+
catch (error) {
|
|
195
|
+
throw mapDriverError(error, errorContext(context));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
async update(id, data, context) {
|
|
199
|
+
return this.mergeAndFlush(id, data, context);
|
|
200
|
+
}
|
|
201
|
+
async patch(id, data, context) {
|
|
202
|
+
return this.mergeAndFlush(id, data, context);
|
|
203
|
+
}
|
|
204
|
+
/**
|
|
205
|
+
* update and patch share one load-merge-flush primitive: the *shape* of
|
|
206
|
+
* `data` differs (full body vs. sparse) because the DTO layer differs, not
|
|
207
|
+
* the persistence mechanics.
|
|
208
|
+
*
|
|
209
|
+
* This goes through the Unit of Work rather than `nativeUpdate` — one
|
|
210
|
+
* managed entity, assigned and flushed — so lifecycle hooks,
|
|
211
|
+
* `onUpdate` properties, and relation diffing all behave as they would in
|
|
212
|
+
* a hand-written MikroORM application. The row is loaded first anyway, to
|
|
213
|
+
* turn a missing id into `NotFoundException`, so this costs no extra
|
|
214
|
+
* query.
|
|
215
|
+
*/
|
|
216
|
+
async mergeAndFlush(id, data, context) {
|
|
217
|
+
try {
|
|
218
|
+
const em = this.fork();
|
|
219
|
+
// Scoped to live rows: a soft-deleted row is invisible to updates,
|
|
220
|
+
// exactly as it is to reads. Reviving one is `restore`'s job.
|
|
221
|
+
const where = this.scopeToLive({ [this.idField]: id }, context, false);
|
|
222
|
+
const existing = await em.findOne(this.entity, where);
|
|
223
|
+
if (existing === null)
|
|
224
|
+
throw this.notFound(id, context);
|
|
225
|
+
wrap(existing).assign(this.toWriteData(data), { em });
|
|
226
|
+
await em.flush();
|
|
227
|
+
return toPlain(existing);
|
|
228
|
+
}
|
|
229
|
+
catch (error) {
|
|
230
|
+
throw mapDriverError(error, errorContext(context));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
async delete(id, context) {
|
|
234
|
+
const softDelete = context.config.softDelete;
|
|
235
|
+
try {
|
|
236
|
+
if (softDelete.strategy === "hard") {
|
|
237
|
+
const affected = await this.fork().nativeDelete(this.entity, { [this.idField]: id });
|
|
238
|
+
if (affected === 0)
|
|
239
|
+
throw this.notFound(id, context);
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
const { field } = softDelete;
|
|
243
|
+
const existing = await this.byId(id, context, true);
|
|
244
|
+
if (existing === null)
|
|
245
|
+
throw this.notFound(id, context);
|
|
246
|
+
if (this.isDeleted(existing, field)) {
|
|
247
|
+
throw new AlreadyDeletedException({
|
|
248
|
+
messageParams: { entity: context.entityName, id: String(id) },
|
|
249
|
+
context: errorContext(context),
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
await this.fork().nativeUpdate(this.entity, { [this.idField]: id }, { [field]: new Date() });
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
throw mapDriverError(error, errorContext(context));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
async restore(id, context) {
|
|
259
|
+
try {
|
|
260
|
+
const { field } = this.requireSoftDelete(context, "restore");
|
|
261
|
+
const existing = await this.byId(id, context, true);
|
|
262
|
+
if (existing === null)
|
|
263
|
+
throw this.notFound(id, context);
|
|
264
|
+
if (!this.isDeleted(existing, field)) {
|
|
265
|
+
throw new NotDeletedException({
|
|
266
|
+
messageParams: { entity: context.entityName, id: String(id) },
|
|
267
|
+
context: errorContext(context),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
await this.fork().nativeUpdate(this.entity, { [this.idField]: id }, { [field]: null });
|
|
271
|
+
// Clearing the marker is a single-field write with no relation
|
|
272
|
+
// involvement, so the already-loaded row is corrected in place rather
|
|
273
|
+
// than re-read.
|
|
274
|
+
existing[field] = null;
|
|
275
|
+
return existing;
|
|
276
|
+
}
|
|
277
|
+
catch (error) {
|
|
278
|
+
throw mapDriverError(error, errorContext(context));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async purge(id, context) {
|
|
282
|
+
const softDelete = context.config.softDelete;
|
|
283
|
+
try {
|
|
284
|
+
if (softDelete.strategy === "soft") {
|
|
285
|
+
// Purge is the second step of a two-step delete: it removes a row
|
|
286
|
+
// that is already soft-deleted, never a live one.
|
|
287
|
+
const existing = await this.byId(id, context, true);
|
|
288
|
+
if (existing === null)
|
|
289
|
+
throw this.notFound(id, context);
|
|
290
|
+
if (!this.isDeleted(existing, softDelete.field)) {
|
|
291
|
+
throw new NotDeletedException({
|
|
292
|
+
messageParams: { entity: context.entityName, id: String(id) },
|
|
293
|
+
context: errorContext(context),
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const affected = await this.fork().nativeDelete(this.entity, { [this.idField]: id });
|
|
298
|
+
if (affected === 0)
|
|
299
|
+
throw this.notFound(id, context);
|
|
300
|
+
}
|
|
301
|
+
catch (error) {
|
|
302
|
+
throw mapDriverError(error, errorContext(context));
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Normalize the engine's write payload for MikroORM.
|
|
307
|
+
*
|
|
308
|
+
* Core's default deserializer narrows a relation value to `{ id }` (or an
|
|
309
|
+
* array of them) — association by id, ADR-0014. MikroORM associates by
|
|
310
|
+
* *primary key value*, and would read a nested `{ id }` object as a
|
|
311
|
+
* request to create a new entity, so each relation value is unwrapped to
|
|
312
|
+
* the bare key before it reaches `create`/`assign`.
|
|
313
|
+
*/
|
|
314
|
+
toWriteData(data) {
|
|
315
|
+
const result = { ...data };
|
|
316
|
+
for (const [name, idFieldOf] of this.relationIdFields) {
|
|
317
|
+
if (!(name in result))
|
|
318
|
+
continue;
|
|
319
|
+
result[name] = unwrapAssociation(result[name], idFieldOf());
|
|
320
|
+
}
|
|
321
|
+
return result;
|
|
322
|
+
}
|
|
323
|
+
notFound(id, context) {
|
|
324
|
+
return new NotFoundException({
|
|
325
|
+
messageParams: { entity: context.entityName, id: String(id) },
|
|
326
|
+
context: errorContext(context),
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* `{ id: 5 }` → `5`; arrays element-wise; scalars and `null` unchanged.
|
|
332
|
+
*
|
|
333
|
+
* An object carrying no id becomes `null` rather than being passed through.
|
|
334
|
+
* That mirrors core's own `associate()` exactly, and it matters because core
|
|
335
|
+
* only narrows a relation when the *target* entity is in its catalog — a
|
|
336
|
+
* relation whose target was never `createCrud`-ed arrives here as whatever
|
|
337
|
+
* the client sent. Handing that object to `em.create` would put a nested
|
|
338
|
+
* write one cascade setting away from working, which is precisely what
|
|
339
|
+
* ADR-0014 rules out: relations are associated by id, never deep-written.
|
|
340
|
+
*/
|
|
341
|
+
/**
|
|
342
|
+
* Apply the two include-tree rules core expects of a loaded row: every
|
|
343
|
+
* included relation is **present**, and no soft-deleted related row is in it.
|
|
344
|
+
*
|
|
345
|
+
* Both are done here, in memory, rather than in the query — see
|
|
346
|
+
* `populateOptions` for why `populateWhere` cannot do the second one without
|
|
347
|
+
* silently dropping parents. The cost is that soft-deleted related rows are
|
|
348
|
+
* fetched and then discarded; the alternative spellings are wrong rather than
|
|
349
|
+
* merely slower, so this is the honest trade. Soft-deleted *roots* are still
|
|
350
|
+
* excluded in SQL (`scopeToLive`), which is where the volume is.
|
|
351
|
+
*
|
|
352
|
+
* "Present" matters because core's serializer treats an absent key as "the
|
|
353
|
+
* adapter never hydrated this" and skips it — so an included to-many that
|
|
354
|
+
* matches nothing must be `[]`, not missing. A root `withDeleted` never
|
|
355
|
+
* widens an included relation: this prunes regardless of the root's scope.
|
|
356
|
+
*/
|
|
357
|
+
function pruneIncluded(row, tree) {
|
|
358
|
+
const source = row;
|
|
359
|
+
for (const node of Object.values(tree)) {
|
|
360
|
+
const name = node.relation.name;
|
|
361
|
+
const value = source[name];
|
|
362
|
+
const deleted = (candidate) => {
|
|
363
|
+
if (node.softDelete.strategy !== "soft")
|
|
364
|
+
return false;
|
|
365
|
+
const marker = candidate[node.softDelete.field];
|
|
366
|
+
return marker !== null && marker !== undefined;
|
|
367
|
+
};
|
|
368
|
+
if (Array.isArray(value)) {
|
|
369
|
+
const live = value.filter((child) => !deleted(child));
|
|
370
|
+
for (const child of live)
|
|
371
|
+
pruneIncluded(child, node.children);
|
|
372
|
+
source[name] = live;
|
|
373
|
+
continue;
|
|
374
|
+
}
|
|
375
|
+
// A to-one that was populated is an object; anything else (a bare foreign
|
|
376
|
+
// key from an uninitialized reference, `undefined`, `null`) becomes null.
|
|
377
|
+
if (value !== null && typeof value === "object" && !deleted(value)) {
|
|
378
|
+
pruneIncluded(value, node.children);
|
|
379
|
+
continue;
|
|
380
|
+
}
|
|
381
|
+
source[name] = node.relation.cardinality === "many" ? [] : null;
|
|
382
|
+
}
|
|
383
|
+
return row;
|
|
384
|
+
}
|
|
385
|
+
function unwrapAssociation(value, idField) {
|
|
386
|
+
if (value === null || value === undefined)
|
|
387
|
+
return null;
|
|
388
|
+
if (Array.isArray(value)) {
|
|
389
|
+
// Nulls are filtered, not mapped through, exactly as core's `associate`
|
|
390
|
+
// does: a to-many element carrying no id contributes nothing, and passing
|
|
391
|
+
// `[null]` to `em.create`/`assign` would fail at the driver as a 500
|
|
392
|
+
// instead of being ignored.
|
|
393
|
+
return value.map((element) => unwrapAssociation(element, idField)).filter((element) => element !== null);
|
|
394
|
+
}
|
|
395
|
+
if (typeof value === "object") {
|
|
396
|
+
return value[idField] ?? null;
|
|
397
|
+
}
|
|
398
|
+
return value;
|
|
399
|
+
}
|
|
400
|
+
/** Combine two predicates, keeping `undefined` from degenerating into `{}`. */
|
|
401
|
+
function and(where, extra) {
|
|
402
|
+
return where === undefined ? extra : { $and: [where, extra] };
|
|
403
|
+
}
|
|
404
|
+
/** The include tree flattened to the dotted paths MikroORM's `populate` takes. */
|
|
405
|
+
function populatePaths(tree, prefix = "") {
|
|
406
|
+
const paths = [];
|
|
407
|
+
for (const node of Object.values(tree)) {
|
|
408
|
+
const path = prefix === "" ? node.relation.name : `${prefix}.${node.relation.name}`;
|
|
409
|
+
paths.push(path);
|
|
410
|
+
paths.push(...populatePaths(node.children, path));
|
|
411
|
+
}
|
|
412
|
+
return paths;
|
|
413
|
+
}
|
|
414
|
+
/** `"author.name"` + `"asc"` → `{ author: { name: "asc" } }`. */
|
|
415
|
+
function nestOrderBy(field, direction) {
|
|
416
|
+
const segments = field.split(".");
|
|
417
|
+
return segments.slice(0, -1).reduceRight((inner, segment) => ({ [segment]: inner }), {
|
|
418
|
+
[segments[segments.length - 1]]: direction,
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
function errorContext(context) {
|
|
422
|
+
return {
|
|
423
|
+
entityName: context.entityName,
|
|
424
|
+
operation: context.operation,
|
|
425
|
+
correlationId: context.correlationId,
|
|
426
|
+
};
|
|
427
|
+
}
|
|
428
|
+
//# sourceMappingURL=mikro-orm-repository-adapter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mikro-orm-repository-adapter.js","sourceRoot":"","sources":["../src/mikro-orm-repository-adapter.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MikroORM entity instance → plain object, at the adapter boundary.
|
|
3
|
+
*
|
|
4
|
+
* This conversion is **required**, not a convenience. A MikroORM to-many
|
|
5
|
+
* relation is a `Collection<T>`, not an array, and core's `DefaultSerializer`
|
|
6
|
+
* branches on `Array.isArray` to decide whether an included relation is a
|
|
7
|
+
* list or a single row — a `Collection` would fall down the single-row path
|
|
8
|
+
* and serialize its internal fields instead of its items. `@kavo/mongoose`
|
|
9
|
+
* converts documents at the same seam and for the same reason: core consumes
|
|
10
|
+
* plain data, so the ORM's own row representation stops here.
|
|
11
|
+
*
|
|
12
|
+
* `wrap(entity).toObject()` is what performs it, which has two consequences
|
|
13
|
+
* worth naming because they are behavior, not implementation detail:
|
|
14
|
+
*
|
|
15
|
+
* 1. **Unpopulated relations collapse to their primary key.** A to-one that
|
|
16
|
+
* was not populated comes back as the raw foreign key (`author: 1`) and a
|
|
17
|
+
* to-many as `[]`. That is harmless — core's serializer emits a relation
|
|
18
|
+
* key only for nodes on the request's include tree — and it is why a
|
|
19
|
+
* relation must be `include=`d to appear as an object.
|
|
20
|
+
* 2. **MikroORM's own property options apply.** A custom `serializer` runs
|
|
21
|
+
* before core ever sees the row, and a `@Property({ hidden: true })` is
|
|
22
|
+
* dropped. The hidden case is belt-and-braces rather than the guard:
|
|
23
|
+
* `buildEntityMetadata` excludes such a property from the seam entirely,
|
|
24
|
+
* which is what keeps it off the default filter/sort allowlists too — a
|
|
25
|
+
* column invisible in the body but filterable in the database would be a
|
|
26
|
+
* blind extraction oracle. See doc 17, "Adapter-specific caveats".
|
|
27
|
+
*/
|
|
28
|
+
export declare function toPlain<Entity extends object>(entity: Entity): Entity;
|
|
29
|
+
/** {@link toPlain} over a list, for the `findMany` path. */
|
|
30
|
+
export declare function toPlainAll<Entity extends object>(entities: readonly Entity[]): readonly Entity[];
|
|
31
|
+
//# sourceMappingURL=plain-entity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plain-entity.d.ts","sourceRoot":"","sources":["../src/plain-entity.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,OAAO,CAAC,MAAM,SAAS,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAErE;AAED,4DAA4D;AAC5D,wBAAgB,UAAU,CAAC,MAAM,SAAS,MAAM,EAAE,QAAQ,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,MAAM,EAAE,CAEhG"}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { wrap } from "@mikro-orm/core";
|
|
2
|
+
/**
|
|
3
|
+
* MikroORM entity instance → plain object, at the adapter boundary.
|
|
4
|
+
*
|
|
5
|
+
* This conversion is **required**, not a convenience. A MikroORM to-many
|
|
6
|
+
* relation is a `Collection<T>`, not an array, and core's `DefaultSerializer`
|
|
7
|
+
* branches on `Array.isArray` to decide whether an included relation is a
|
|
8
|
+
* list or a single row — a `Collection` would fall down the single-row path
|
|
9
|
+
* and serialize its internal fields instead of its items. `@kavo/mongoose`
|
|
10
|
+
* converts documents at the same seam and for the same reason: core consumes
|
|
11
|
+
* plain data, so the ORM's own row representation stops here.
|
|
12
|
+
*
|
|
13
|
+
* `wrap(entity).toObject()` is what performs it, which has two consequences
|
|
14
|
+
* worth naming because they are behavior, not implementation detail:
|
|
15
|
+
*
|
|
16
|
+
* 1. **Unpopulated relations collapse to their primary key.** A to-one that
|
|
17
|
+
* was not populated comes back as the raw foreign key (`author: 1`) and a
|
|
18
|
+
* to-many as `[]`. That is harmless — core's serializer emits a relation
|
|
19
|
+
* key only for nodes on the request's include tree — and it is why a
|
|
20
|
+
* relation must be `include=`d to appear as an object.
|
|
21
|
+
* 2. **MikroORM's own property options apply.** A custom `serializer` runs
|
|
22
|
+
* before core ever sees the row, and a `@Property({ hidden: true })` is
|
|
23
|
+
* dropped. The hidden case is belt-and-braces rather than the guard:
|
|
24
|
+
* `buildEntityMetadata` excludes such a property from the seam entirely,
|
|
25
|
+
* which is what keeps it off the default filter/sort allowlists too — a
|
|
26
|
+
* column invisible in the body but filterable in the database would be a
|
|
27
|
+
* blind extraction oracle. See doc 17, "Adapter-specific caveats".
|
|
28
|
+
*/
|
|
29
|
+
export function toPlain(entity) {
|
|
30
|
+
return wrap(entity).toObject();
|
|
31
|
+
}
|
|
32
|
+
/** {@link toPlain} over a list, for the `findMany` path. */
|
|
33
|
+
export function toPlainAll(entities) {
|
|
34
|
+
return entities.map((entity) => toPlain(entity));
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=plain-entity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"plain-entity.js","sourceRoot":"","sources":["../src/plain-entity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,iBAAiB,CAAC;AAEvC;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,MAAM,UAAU,OAAO,CAAwB,MAAc;IAC3D,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAY,CAAC;AAC3C,CAAC;AAED,4DAA4D;AAC5D,MAAM,UAAU,UAAU,CAAwB,QAA2B;IAC3E,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC;AACnD,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kavo/mikroorm",
|
|
3
|
+
"version": "0.6.0",
|
|
4
|
+
"description": "Kavo MikroORM adapter — implements @kavo/core's RepositoryAdapter over a MikroORM EntityManager.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/kavo-labs/kavo.git",
|
|
9
|
+
"directory": "packages/orms/mikroorm"
|
|
10
|
+
},
|
|
11
|
+
"homepage": "https://github.com/kavo-labs/kavo/tree/main/packages/orms/mikroorm#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
|
+
},
|
|
29
|
+
"dependencies": {
|
|
30
|
+
"@kavo/core": "workspace:^"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@mikro-orm/core": "^7.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@mikro-orm/core": "^7.1.9",
|
|
37
|
+
"@mikro-orm/decorators": "^7.1.9",
|
|
38
|
+
"@mikro-orm/sqlite": "^7.1.9",
|
|
39
|
+
"reflect-metadata": "^0.2.2"
|
|
40
|
+
}
|
|
41
|
+
}
|