@mikro-orm/core 7.0.0-dev.106 → 7.0.0-dev.108

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.
@@ -63,7 +63,7 @@ export class FileCacheAdapter {
63
63
  let path = typeof this.options.combined === 'string'
64
64
  ? this.options.combined
65
65
  : './metadata.json';
66
- path = Utils.normalizePath(this.options.cacheDir, path);
66
+ path = fs.normalizePath(this.options.cacheDir, path);
67
67
  this.options.combined = path; // override in the options, so we can log it from the CLI in `cache:generate` command
68
68
  writeFileSync(path, JSON.stringify(this.cache, null, this.pretty ? 2 : undefined));
69
69
  return path;
@@ -73,7 +73,7 @@ export class FileCacheAdapter {
73
73
  return `${this.options.cacheDir}/${name}.json`;
74
74
  }
75
75
  getHash(origin) {
76
- origin = Utils.absolutePath(origin, this.baseDir);
76
+ origin = fs.absolutePath(origin, this.baseDir);
77
77
  if (!existsSync(origin)) {
78
78
  return null;
79
79
  }
@@ -153,15 +153,19 @@ export class MetadataDiscovery {
153
153
  const { entities, entitiesTs, baseDir } = this.config.getAll();
154
154
  const targets = (preferTs && entitiesTs.length > 0) ? entitiesTs : entities;
155
155
  const processed = [];
156
+ const paths = [];
156
157
  for (const entity of targets) {
157
158
  if (typeof entity === 'string') {
158
- const { discoverEntities } = await import('@mikro-orm/core/file-discovery');
159
- processed.push(...await discoverEntities(entity, { baseDir }));
159
+ paths.push(entity);
160
160
  }
161
161
  else {
162
162
  processed.push(entity);
163
163
  }
164
164
  }
165
+ if (paths.length > 0) {
166
+ const { discoverEntities } = await import('@mikro-orm/core/file-discovery');
167
+ processed.push(...await discoverEntities(paths, { baseDir }));
168
+ }
165
169
  return this.discoverReferences(processed);
166
170
  }
167
171
  discoverMissingTargets() {
@@ -253,7 +257,7 @@ export class MetadataDiscovery {
253
257
  const path = entity[MetadataStorage.PATH_SYMBOL];
254
258
  if (path) {
255
259
  const meta = Utils.copy(MetadataStorage.getMetadata(entity.name, path), false);
256
- meta.path = Utils.relativePath(path, this.config.get('baseDir'));
260
+ meta.path = path;
257
261
  this.metadata.set(entity.name, meta);
258
262
  }
259
263
  const exists = this.metadata.has(entity.name);
@@ -279,7 +283,7 @@ export class MetadataDiscovery {
279
283
  const path = meta.path;
280
284
  this.logger.log('discovery', `- processing entity ${colors.cyan(meta.className)}${colors.grey(path ? ` (${path})` : '')}`);
281
285
  const root = this.getRootEntity(meta);
282
- schema.meta.path = Utils.relativePath(meta.path, this.config.get('baseDir'));
286
+ schema.meta.path = meta.path;
283
287
  const cache = this.metadataProvider.getCachedMetadata(meta, root);
284
288
  if (cache) {
285
289
  this.logger.log('discovery', `- using cached metadata for entity ${colors.cyan(meta.className)}`);
@@ -4,8 +4,8 @@ import { Utils } from '../utils/Utils.js';
4
4
  import { MetadataStorage } from './MetadataStorage.js';
5
5
  import { EntitySchema } from './EntitySchema.js';
6
6
  async function getEntityClassOrSchema(filepath, allTargets, baseDir) {
7
- const path = Utils.normalizePath(baseDir, filepath);
8
- const exports = await Utils.dynamicImport(path);
7
+ const path = fs.normalizePath(baseDir, filepath);
8
+ const exports = await fs.dynamicImport(path);
9
9
  const targets = Object.values(exports);
10
10
  // ignore class implementations that are linked from an EntitySchema
11
11
  for (const item of targets) {
@@ -25,9 +25,9 @@ async function getEntityClassOrSchema(filepath, allTargets, baseDir) {
25
25
  }
26
26
  }
27
27
  export async function discoverEntities(paths, options) {
28
- paths = Utils.asArray(paths).map(path => Utils.normalizePath(path));
29
- const baseDir = options?.baseDir ?? process.cwd();
30
- const files = fs.glob(paths, Utils.normalizePath(baseDir));
28
+ paths = Utils.asArray(paths).map(path => fs.normalizePath(path));
29
+ const baseDir = fs.absolutePath(options?.baseDir ?? process.cwd());
30
+ const files = fs.glob(paths, fs.normalizePath(baseDir));
31
31
  const found = new Map();
32
32
  for (const filepath of files) {
33
33
  const filename = basename(filepath);
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@mikro-orm/core",
3
3
  "type": "module",
4
- "version": "7.0.0-dev.106",
4
+ "version": "7.0.0-dev.108",
5
5
  "description": "TypeScript ORM for Node.js based on Data Mapper, Unit of Work and Identity Map patterns. Supports MongoDB, MySQL, PostgreSQL and SQLite databases as well as usage with vanilla JavaScript.",
6
6
  "exports": {
7
7
  "./package.json": "./package.json",
@@ -1,4 +1,3 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
2
1
  import { Collection } from '../entity/Collection.js';
3
2
  import { EntityHelper } from '../entity/EntityHelper.js';
4
3
  import { helper } from '../entity/wrap.js';
@@ -13,8 +12,9 @@ import { Cascade, DeferMode, EventType, LockMode, ReferenceKind } from '../enums
13
12
  import { OptimisticLockError, ValidationError } from '../errors.js';
14
13
  import { TransactionEventBroadcaster } from '../events/TransactionEventBroadcaster.js';
15
14
  import { IdentityMap } from './IdentityMap.js';
15
+ import { createAsyncContext } from '../utils/AsyncContext.js';
16
16
  // to deal with validation for flush inside flush hooks and `Promise.all`
17
- const insideFlush = new AsyncLocalStorage();
17
+ const insideFlush = createAsyncContext();
18
18
  export class UnitOfWork {
19
19
  em;
20
20
  /** map of references to managed entities */
@@ -0,0 +1,6 @@
1
+ export interface AsyncContext<T> {
2
+ getStore(): T | undefined;
3
+ run<R>(store: T, callback: () => R): R;
4
+ enterWith(store: T): void;
5
+ }
6
+ export declare function createAsyncContext<T>(): AsyncContext<T>;
@@ -0,0 +1,42 @@
1
+ function getNodeAsyncContext() {
2
+ const mod = globalThis.process?.getBuiltinModule?.('node:async_hooks');
3
+ /* v8 ignore next */
4
+ if (!mod?.AsyncLocalStorage) {
5
+ throw new Error('AsyncLocalStorage not available');
6
+ }
7
+ return new mod.AsyncLocalStorage();
8
+ }
9
+ /* v8 ignore next */
10
+ function createFallbackAsyncContext() {
11
+ let store;
12
+ // eslint-disable-next-line no-console
13
+ console.warn('AsyncLocalStorage not available');
14
+ return {
15
+ getStore: () => store,
16
+ enterWith: value => store = value,
17
+ run: (value, cb) => {
18
+ const prev = store;
19
+ store = value;
20
+ try {
21
+ return cb();
22
+ }
23
+ finally {
24
+ store = prev;
25
+ }
26
+ },
27
+ };
28
+ }
29
+ export function createAsyncContext() {
30
+ /* v8 ignore next */
31
+ const ALS = globalThis.AsyncLocalStorage;
32
+ /* v8 ignore next */
33
+ if (typeof ALS === 'function' && ALS.prototype?.run) {
34
+ return new ALS();
35
+ }
36
+ /* v8 ignore else */
37
+ if (globalThis.process?.versions?.node) {
38
+ return getNodeAsyncContext();
39
+ }
40
+ /* v8 ignore next */
41
+ return createFallbackAsyncContext();
42
+ }
@@ -135,10 +135,9 @@ export class Configuration {
135
135
  extensions = new Map();
136
136
  constructor(options, validate = true) {
137
137
  if (options.dynamicImportProvider) {
138
- Utils.dynamicImportProvider = options.dynamicImportProvider;
138
+ globalThis.dynamicImportProvider = options.dynamicImportProvider;
139
139
  }
140
140
  this.options = Utils.mergeConfig({}, DEFAULTS, options);
141
- this.options.baseDir = Utils.absolutePath(this.options.baseDir);
142
141
  if (validate) {
143
142
  this.validateOptions();
144
143
  }
@@ -1,10 +1,10 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
2
1
  import { Utils } from './Utils.js';
2
+ import { createAsyncContext } from './AsyncContext.js';
3
3
  export class RawQueryFragment {
4
4
  sql;
5
5
  params;
6
6
  static #rawQueryCache = new Map();
7
- static #storage = new AsyncLocalStorage();
7
+ static #storage = createAsyncContext();
8
8
  static #index = 0n;
9
9
  static cloneRegistry;
10
10
  #used = 0;
@@ -1,10 +1,10 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
1
+ import { createAsyncContext } from './AsyncContext.js';
2
2
  /**
3
3
  * Uses `AsyncLocalStorage` to create async context that holds the current EM fork.
4
4
  */
5
5
  export class RequestContext {
6
6
  map;
7
- static storage = new AsyncLocalStorage();
7
+ static storage = createAsyncContext();
8
8
  static counter = 1;
9
9
  id = RequestContext.counter++;
10
10
  constructor(map) {
@@ -1,7 +1,7 @@
1
- import { AsyncLocalStorage } from 'node:async_hooks';
1
+ import { createAsyncContext } from './AsyncContext.js';
2
2
  export class TransactionContext {
3
3
  em;
4
- static storage = new AsyncLocalStorage();
4
+ static storage = createAsyncContext();
5
5
  id;
6
6
  constructor(em) {
7
7
  this.em = em;
package/utils/Utils.d.ts CHANGED
@@ -14,7 +14,6 @@ export declare function parseJsonSafe<T = unknown>(value: unknown): T;
14
14
  export declare class Utils {
15
15
  #private;
16
16
  static readonly PK_SEPARATOR = "~~~";
17
- static dynamicImportProvider: (id: string) => Promise<any>;
18
17
  /**
19
18
  * Checks if the argument is instance of `Object`. Returns false for arrays.
20
19
  */
@@ -129,25 +128,6 @@ export declare class Utils {
129
128
  */
130
129
  static runSerial<T = any, U = any>(items: Iterable<U>, cb: (item: U) => Promise<T>): Promise<T[]>;
131
130
  static isCollection<T extends object, O extends object = object>(item: any): item is Collection<T, O>;
132
- static fileURLToPath(url: string | URL): string;
133
- /**
134
- * Resolves and normalizes a series of path parts relative to each preceding part.
135
- * If any part is a `file:` URL, it is converted to a local path. If any part is an
136
- * absolute path, it replaces preceding paths (similar to `path.resolve` in NodeJS).
137
- * Trailing directory separators are removed, and all directory separators are converted
138
- * to POSIX-style separators (`/`).
139
- */
140
- static normalizePath(...parts: string[]): string;
141
- /**
142
- * Determines the relative path between two paths. If either path is a `file:` URL,
143
- * it is converted to a local path.
144
- */
145
- static relativePath(path: string, relativeTo: string): string;
146
- /**
147
- * Computes the absolute path to for the given path relative to the provided base directory.
148
- * If either `path` or `baseDir` are `file:` URLs, they are converted to local paths.
149
- */
150
- static absolutePath(path: string, baseDir?: string): string;
151
131
  static hash(data: string, length?: number): string;
152
132
  static runIfNotEmpty(clause: () => any, data: any): void;
153
133
  static defaultValue<T extends Dictionary>(prop: T, option: keyof T, defaultValue: any): void;
@@ -161,7 +141,6 @@ export declare class Utils {
161
141
  static flatten<T>(arrays: T[][]): T[];
162
142
  static isOperator(key: PropertyKey, includeGroupOperators?: boolean): boolean;
163
143
  static hasNestedKey(object: unknown, key: string): boolean;
164
- static dynamicImport<T = any>(id: string): Promise<T>;
165
144
  static getORMVersion(): string;
166
145
  static createFunction(context: Map<string, any>, code: string): any;
167
146
  static callCompiledFunction<T extends unknown[], R>(fn: (...args: T) => R, ...args: T): R;
package/utils/Utils.js CHANGED
@@ -1,5 +1,3 @@
1
- import { isAbsolute, normalize, relative } from 'node:path';
2
- import { fileURLToPath, pathToFileURL } from 'node:url';
3
1
  import { clone } from './clone.js';
4
2
  import { GroupOperator, PlainObject, QueryOperator, ReferenceKind } from '../enums.js';
5
3
  import { helper } from '../entity/wrap.js';
@@ -125,9 +123,7 @@ export function parseJsonSafe(value) {
125
123
  }
126
124
  export class Utils {
127
125
  static PK_SEPARATOR = '~~~';
128
- static #ORM_VERSION = '7.0.0-dev.106';
129
- /* v8 ignore next */
130
- static dynamicImportProvider = (id) => import(id);
126
+ static #ORM_VERSION = '7.0.0-dev.108';
131
127
  /**
132
128
  * Checks if the argument is instance of `Object`. Returns false for arrays.
133
129
  */
@@ -589,64 +585,6 @@ export class Utils {
589
585
  static isCollection(item) {
590
586
  return item?.__collection;
591
587
  }
592
- static fileURLToPath(url) {
593
- // expose `fileURLToPath` on Utils so that it can be properly mocked in tests
594
- return fileURLToPath(url);
595
- }
596
- /**
597
- * Resolves and normalizes a series of path parts relative to each preceding part.
598
- * If any part is a `file:` URL, it is converted to a local path. If any part is an
599
- * absolute path, it replaces preceding paths (similar to `path.resolve` in NodeJS).
600
- * Trailing directory separators are removed, and all directory separators are converted
601
- * to POSIX-style separators (`/`).
602
- */
603
- static normalizePath(...parts) {
604
- let start = 0;
605
- for (let i = 0; i < parts.length; i++) {
606
- const part = parts[i];
607
- if (isAbsolute(part)) {
608
- start = i;
609
- }
610
- else if (part.startsWith('file:')) {
611
- start = i;
612
- parts[i] = Utils.fileURLToPath(part);
613
- }
614
- }
615
- if (start > 0) {
616
- parts = parts.slice(start);
617
- }
618
- let path = parts.join('/').replace(/\\/g, '/').replace(/\/$/, '');
619
- path = normalize(path).replace(/\\/g, '/');
620
- return (path.match(/^[/.]|[a-zA-Z]:/) || path.startsWith('!')) ? path : './' + path;
621
- }
622
- /**
623
- * Determines the relative path between two paths. If either path is a `file:` URL,
624
- * it is converted to a local path.
625
- */
626
- static relativePath(path, relativeTo) {
627
- if (!path) {
628
- return path;
629
- }
630
- path = Utils.normalizePath(path);
631
- if (path.startsWith('.')) {
632
- return path;
633
- }
634
- path = relative(Utils.normalizePath(relativeTo), path);
635
- return Utils.normalizePath(path);
636
- }
637
- /**
638
- * Computes the absolute path to for the given path relative to the provided base directory.
639
- * If either `path` or `baseDir` are `file:` URLs, they are converted to local paths.
640
- */
641
- static absolutePath(path, baseDir = process.cwd()) {
642
- if (!path) {
643
- return Utils.normalizePath(baseDir);
644
- }
645
- if (!isAbsolute(path) && !path.startsWith('file://')) {
646
- path = baseDir + '/' + path;
647
- }
648
- return Utils.normalizePath(path);
649
- }
650
588
  // FNV-1a 64-bit
651
589
  static hash(data, length) {
652
590
  let h1 = 0xcbf29ce484222325n;
@@ -723,11 +661,6 @@ export class Utils {
723
661
  }
724
662
  return false;
725
663
  }
726
- static async dynamicImport(id) {
727
- /* v8 ignore next */
728
- const specifier = id.startsWith('file://') ? id : pathToFileURL(id).href;
729
- return this.dynamicImportProvider(specifier);
730
- }
731
664
  static getORMVersion() {
732
665
  return this.#ORM_VERSION;
733
666
  }
@@ -4,9 +4,29 @@ export declare const fs: {
4
4
  ensureDir(path: string): void;
5
5
  readJSONSync<T = Dictionary>(path: string): T;
6
6
  glob(input: string | string[], cwd?: string): string[];
7
+ resolveGlob(input: string | string[], cwd?: string): string[];
7
8
  getPackageConfig<T extends Dictionary>(basePath?: string): T;
8
9
  getORMPackages(): Set<string>;
9
10
  getORMPackageVersion(name: string): string | undefined;
10
11
  checkPackageVersion(): void;
12
+ /**
13
+ * Resolves and normalizes a series of path parts relative to each preceding part.
14
+ * If any part is a `file:` URL, it is converted to a local path. If any part is an
15
+ * absolute path, it replaces preceding paths (similar to `path.resolve` in NodeJS).
16
+ * Trailing directory separators are removed, and all directory separators are converted
17
+ * to POSIX-style separators (`/`).
18
+ */
19
+ normalizePath(...parts: string[]): string;
20
+ /**
21
+ * Determines the relative path between two paths. If either path is a `file:` URL,
22
+ * it is converted to a local path.
23
+ */
24
+ relativePath(path: string, relativeTo: string): string;
25
+ /**
26
+ * Computes the absolute path to for the given path relative to the provided base directory.
27
+ * If either `path` or `baseDir` are `file:` URLs, they are converted to local paths.
28
+ */
29
+ absolutePath(path: string, baseDir?: string): string;
30
+ dynamicImport<T = any>(id: string): Promise<T>;
11
31
  };
12
32
  export * from '../cache/FileCacheAdapter.js';
package/utils/fs-utils.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { existsSync, globSync, mkdirSync, readFileSync, realpathSync, statSync } from 'node:fs';
2
- import { join } from 'node:path';
3
- import { fileURLToPath } from 'node:url';
2
+ import { isAbsolute, join, normalize, relative } from 'node:path';
3
+ import { fileURLToPath, pathToFileURL } from 'node:url';
4
4
  import { Utils } from './Utils.js';
5
5
  import { colors } from '../logging/colors.js';
6
6
  export const fs = {
@@ -20,13 +20,34 @@ export const fs = {
20
20
  return JSON.parse(file.toString());
21
21
  },
22
22
  glob(input, cwd) {
23
+ const patterns = Array.isArray(input) ? input : [input];
24
+ const positive = [];
25
+ const negative = [];
26
+ for (const p of patterns) {
27
+ if (p.startsWith('!')) {
28
+ negative.push(p.slice(1));
29
+ }
30
+ else {
31
+ positive.push(p);
32
+ }
33
+ }
34
+ const included = new Set(this.resolveGlob(positive, cwd));
35
+ if (included.size > 0 && negative.length > 0) {
36
+ const excluded = this.resolveGlob(negative, cwd);
37
+ for (const file of excluded) {
38
+ included.delete(file);
39
+ }
40
+ }
41
+ return [...included];
42
+ },
43
+ resolveGlob(input, cwd) {
23
44
  if (Array.isArray(input)) {
24
- return input.flatMap(paths => this.glob(paths, cwd));
45
+ return input.flatMap(paths => this.resolveGlob(paths, cwd));
25
46
  }
26
47
  const hasGlobChars = /[*?[\]]/.test(input);
27
48
  if (!hasGlobChars) {
28
49
  try {
29
- const s = statSync(cwd ? Utils.normalizePath(cwd, input) : input);
50
+ const s = statSync(cwd ? this.normalizePath(cwd, input) : input);
30
51
  if (s.isDirectory()) {
31
52
  const files = globSync(join(input, '**'), { cwd, withFileTypes: true });
32
53
  return files.filter(f => f.isFile()).map(f => join(f.parentPath, f.name));
@@ -93,5 +114,65 @@ export const fs = {
93
114
  }
94
115
  }
95
116
  },
117
+ /**
118
+ * Resolves and normalizes a series of path parts relative to each preceding part.
119
+ * If any part is a `file:` URL, it is converted to a local path. If any part is an
120
+ * absolute path, it replaces preceding paths (similar to `path.resolve` in NodeJS).
121
+ * Trailing directory separators are removed, and all directory separators are converted
122
+ * to POSIX-style separators (`/`).
123
+ */
124
+ normalizePath(...parts) {
125
+ let start = 0;
126
+ for (let i = 0; i < parts.length; i++) {
127
+ const part = parts[i];
128
+ if (isAbsolute(part)) {
129
+ start = i;
130
+ }
131
+ else if (part.startsWith('file:')) {
132
+ start = i;
133
+ parts[i] = fileURLToPath(part);
134
+ }
135
+ }
136
+ if (start > 0) {
137
+ parts = parts.slice(start);
138
+ }
139
+ let path = parts.join('/').replace(/\\/g, '/').replace(/\/$/, '');
140
+ path = normalize(path).replace(/\\/g, '/');
141
+ return (path.match(/^[/.]|[a-zA-Z]:/) || path.startsWith('!')) ? path : './' + path;
142
+ },
143
+ /**
144
+ * Determines the relative path between two paths. If either path is a `file:` URL,
145
+ * it is converted to a local path.
146
+ */
147
+ relativePath(path, relativeTo) {
148
+ if (!path) {
149
+ return path;
150
+ }
151
+ path = this.normalizePath(path);
152
+ if (path.startsWith('.')) {
153
+ return path;
154
+ }
155
+ path = relative(this.normalizePath(relativeTo), path);
156
+ return this.normalizePath(path);
157
+ },
158
+ /**
159
+ * Computes the absolute path to for the given path relative to the provided base directory.
160
+ * If either `path` or `baseDir` are `file:` URLs, they are converted to local paths.
161
+ */
162
+ absolutePath(path, baseDir = process.cwd()) {
163
+ if (!path) {
164
+ return this.normalizePath(baseDir);
165
+ }
166
+ if (!isAbsolute(path) && !path.startsWith('file://')) {
167
+ path = baseDir + '/' + path;
168
+ }
169
+ return this.normalizePath(path);
170
+ },
171
+ async dynamicImport(id) {
172
+ /* v8 ignore next */
173
+ const specifier = id.startsWith('file://') ? id : pathToFileURL(id).href;
174
+ const dynamicImportProvider = globalThis.dynamicImportProvider ?? ((id) => import(id));
175
+ return dynamicImportProvider(specifier);
176
+ },
96
177
  };
97
178
  export * from '../cache/FileCacheAdapter.js';