@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.
@@ -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
+ }