forge-sql-orm 1.0.29 → 1.0.31

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "forge-sql-orm",
3
- "version": "1.0.29",
3
+ "version": "1.0.31",
4
4
  "description": "Mikro-ORM integration for Forge-SQL in Atlassian Forge applications.",
5
5
  "main": "dist/ForgeSQLORM.js",
6
6
  "module": "dist/ForgeSQLORM.mjs",
@@ -29,14 +29,14 @@
29
29
  ],
30
30
  "devDependencies": {
31
31
  "@eslint/js": "^9.22.0",
32
- "@mikro-orm/cli": "^6.4.9",
33
- "inquirer": "^12.4.3",
34
- "@mikro-orm/entity-generator": "^6.4.9",
35
- "@mikro-orm/reflection": "^6.4.9",
32
+ "@mikro-orm/cli": "^6.4.10",
33
+ "inquirer": "^12.5.0",
34
+ "@mikro-orm/entity-generator": "^6.4.10",
35
+ "@mikro-orm/reflection": "^6.4.10",
36
36
  "@typescript-eslint/eslint-plugin": "^8.26.1",
37
37
  "@typescript-eslint/parser": "^8.26.1",
38
- "@vitest/coverage-v8": "^3.0.8",
39
- "@vitest/ui": "^3.0.8",
38
+ "@vitest/coverage-v8": "^3.0.9",
39
+ "@vitest/ui": "^3.0.9",
40
40
  "eslint": "^9.22.0",
41
41
  "eslint-config-prettier": "^10.1.1",
42
42
  "eslint-plugin-import": "^2.31.0",
@@ -52,9 +52,9 @@
52
52
  "tsup": "^8.4.0",
53
53
  "typescript": "^5.8.2",
54
54
  "typescript-eslint": "^8.26.1",
55
- "vite": "^6.2.1",
55
+ "vite": "^6.2.2",
56
56
  "vite-plugin-static-copy": "^2.3.0",
57
- "vitest": "^3.0.8"
57
+ "vitest": "^3.0.9"
58
58
  },
59
59
  "license": "MIT",
60
60
  "author": "Vasyl Zakharchenko",
@@ -71,7 +71,7 @@
71
71
  "build": "npm run clean && vite build && npm run build:types",
72
72
  "build:types": "tsc --emitDeclarationOnly",
73
73
  "build:cli": "vite build --mode cli",
74
- "dependency:fix": "node ./dist-cli/forgeSqlOrmCLI.js patch:mikroorm",
74
+ "dependency:fix": "npm run build:cli && node ./dist-cli/forgeSqlOrmCLI.js patch:mikroorm",
75
75
  "prepublish:npm": "npm run build:cli && npm run build",
76
76
  "publish:npm": "npm publish --access public"
77
77
  },
@@ -84,13 +84,14 @@
84
84
  "README.md"
85
85
  ],
86
86
  "dependencies": {
87
- "@forge/resolver": "^1.6.7",
88
- "@mikro-orm/core": "6.4.9",
89
- "@mikro-orm/mysql": "6.4.9",
90
- "moment": "^2.30.1"
87
+ "@forge/resolver": "^1.6.8",
88
+ "@mikro-orm/core": "6.4.10",
89
+ "@mikro-orm/mysql": "6.4.10",
90
+ "moment": "^2.30.1",
91
+ "uuid": "^11.1.0"
91
92
  },
92
93
  "peerDependencies": {
93
- "@forge/sql": "^2.4.0"
94
+ "@forge/sql": "^2.4.1"
94
95
  },
95
96
  "bin": {
96
97
  "forge-sql-orm": "dist-cli/forgeSqlOrmCLI.js"
@@ -0,0 +1,63 @@
1
+ import { EntitySchema, EntityProperty, EntityKey } from "..";
2
+ import {ComplexQuerySchemaBuilder} from "./ForgeSQLQueryBuilder";
3
+
4
+ export class DynamicEntity {
5
+ [key: string]: unknown;
6
+
7
+ /**
8
+ * Retrieves a schema field by its original entity property.
9
+ * @param field - The entity property to search for.
10
+ * @returns The corresponding schema field or undefined if not found.
11
+ */
12
+ getSchemaBySchemaField<TYPE>(field: EntityProperty): TYPE | undefined {
13
+ return this[field.name] as TYPE | undefined;
14
+ }
15
+
16
+ /**
17
+ * Retrieves a schema field by its alias.
18
+ * @param alias - The alias of the field.
19
+ * @returns The corresponding schema field or undefined if not found.
20
+ */
21
+ getSchemaByAliasField<TYPE>(alias: string): TYPE | undefined {
22
+ return this[alias] as TYPE | undefined;
23
+ }
24
+ }
25
+
26
+ export class EntitySchemaBuilder<T> implements ComplexQuerySchemaBuilder<T> {
27
+ private properties: Record<string, EntityProperty<T>> = {};
28
+
29
+ constructor(private entityClass: new () => T) {}
30
+
31
+ /**
32
+ * Adds a field to the schema definition.
33
+ * @param field - The entity property to add.
34
+ * @param alias - (Optional) Custom alias for the field name.
35
+ * @returns The current instance of EntitySchemaBuilder for method chaining.
36
+ */
37
+ addField<K>(field: Partial<EntityProperty<K>>, alias?: string): this {
38
+ const fieldName = alias || field.name;
39
+ const fieldNameType = fieldName as unknown as EntityKey<T>;
40
+ this.properties[fieldNameType] = { ...field, name: fieldNameType } as unknown as EntityProperty<T>;
41
+ return this;
42
+ }
43
+
44
+ /**
45
+ * Creates and initializes a new EntitySchema based on the added fields.
46
+ * @returns A new EntitySchema<T> instance.
47
+ */
48
+ createSchema(): EntitySchema<T> {
49
+ const es = new EntitySchema<T>({
50
+ class: this.entityClass,
51
+ // @ts-ignore
52
+ properties: this.properties,
53
+ });
54
+ es.init();
55
+ return es;
56
+ }
57
+ }
58
+
59
+ export class DynamicEntitySchemaBuilder extends EntitySchemaBuilder<DynamicEntity> {
60
+ constructor() {
61
+ super(DynamicEntity);
62
+ }
63
+ }
@@ -31,9 +31,12 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
31
31
  entities: (EntityClass<AnyEntity> | EntityClassGroup<AnyEntity> | EntitySchema)[],
32
32
  options?: ForgeSqlOrmOptions,
33
33
  ) {
34
- console.debug("Initializing ForgeSQLORM...");
35
34
 
36
35
  try {
36
+ const newOptions: ForgeSqlOrmOptions = options ?? { logRawSqlQuery: false, disableOptimisticLocking: false };
37
+ if (newOptions.logRawSqlQuery){
38
+ console.debug("Initializing ForgeSQLORM...");
39
+ }
37
40
  this.mikroORM = MikroORM.initSync({
38
41
  dbName: "inmemory",
39
42
  schemaGenerator: {
@@ -53,7 +56,7 @@ class ForgeSQLORMImpl implements ForgeSqlOperation {
53
56
  preferTs: false,
54
57
  debug: false,
55
58
  });
56
- const newOptions: ForgeSqlOrmOptions = options ?? { logRawSqlQuery: false, disableOptimisticLocking: false };
59
+
57
60
  this.crudOperations = new ForgeSQLCrudOperations(this, newOptions);
58
61
  this.fetchOperations = new ForgeSQLSelectOperations(newOptions);
59
62
  } catch (error) {
@@ -3,8 +3,9 @@ import type { EntityName, LoggingOptions, QBFilterQuery } from "..";
3
3
  import type { EntitySchema } from "@mikro-orm/core/metadata/EntitySchema";
4
4
  import type { QueryBuilder } from "@mikro-orm/knex/query";
5
5
  import type { Knex } from "knex";
6
- import { EntityKey } from "@mikro-orm/core";
6
+ import {EntityKey, EntityProperty} from "@mikro-orm/core";
7
7
  import {SqlParameters} from "@forge/sql/out/sql-statement";
8
+ import {DynamicEntity} from "./ComplexQuerySchemaBuilder";
8
9
 
9
10
  /**
10
11
  * Interface representing the main ForgeSQL operations.
@@ -70,6 +71,13 @@ export interface SchemaSqlForgeSql {
70
71
  */
71
72
  executeRawUpdateSQL(query: string, params?: SqlParameters[]): Promise<UpdateQueryResponse>;
72
73
 
74
+ /**
75
+ * Creates a builder for constructing complex query schemas dynamically.
76
+ * This method is useful when working with dynamic entity structures where fields
77
+ * may not be known at compile time.
78
+ * @returns An instance of ComplexQuerySchemaBuilder configured for dynamic entities.
79
+ */
80
+ createComplexQuerySchema():ComplexQuerySchemaBuilder<DynamicEntity>
73
81
  }
74
82
 
75
83
  /**
@@ -171,3 +179,19 @@ export interface QueryBuilderForgeSql {
171
179
  */
172
180
  getKnex(): Knex<any, any[]>;
173
181
  }
182
+
183
+ export interface ComplexQuerySchemaBuilder<T> {
184
+ /**
185
+ * Adds a field from an entity schema to the builder.
186
+ * @param field - The entity property to be added.
187
+ * @param alias - (Optional) Alias for the field name.
188
+ * @returns The updated instance of the builder.
189
+ */
190
+ addField<K>(field: Partial<EntityProperty<K>>, alias?: string): this
191
+
192
+ /**
193
+ * Creates and returns a new entity schema based on the added fields.
194
+ * @returns A new EntitySchema<T> instance.
195
+ */
196
+ createSchema(): EntitySchema<T>;
197
+ }
@@ -1,15 +1,27 @@
1
1
  import { sql, UpdateQueryResponse } from "@forge/sql";
2
2
  import type { EntitySchema } from "@mikro-orm/core/metadata/EntitySchema";
3
3
  import { parseDateTime } from "../utils/sqlUtils";
4
- import { ForgeSqlOrmOptions, SchemaSqlForgeSql } from "./ForgeSQLQueryBuilder";
4
+ import { ComplexQuerySchemaBuilder, ForgeSqlOrmOptions, SchemaSqlForgeSql} from "./ForgeSQLQueryBuilder";
5
5
  import {SqlParameters} from "@forge/sql/out/sql-statement";
6
+ import {DynamicEntity, DynamicEntitySchemaBuilder} from "./ComplexQuerySchemaBuilder";
7
+ import {EntityKey} from "@mikro-orm/core";
6
8
 
7
9
  export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
8
- private readonly options: ForgeSqlOrmOptions;
10
+ private readonly options: ForgeSqlOrmOptions;
9
11
 
10
- constructor(options: ForgeSqlOrmOptions) {
11
- this.options = options;
12
- }
12
+ constructor(options: ForgeSqlOrmOptions) {
13
+ this.options = options;
14
+ }
15
+
16
+ /**
17
+ * Creates a builder for constructing complex query schemas dynamically.
18
+ * This method is useful when working with dynamic entity structures where fields
19
+ * may not be known at compile time.
20
+ * @returns An instance of ComplexQuerySchemaBuilder configured for dynamic entities.
21
+ */
22
+ createComplexQuerySchema(): ComplexQuerySchemaBuilder<DynamicEntity> {
23
+ return new DynamicEntitySchemaBuilder();
24
+ }
13
25
 
14
26
  async executeSchemaSQLOnlyOne<T extends object>(query: string, schema: EntitySchema<T>): Promise<T|undefined> {
15
27
  const results = await this.executeSchemaSQL(query, schema);
@@ -34,7 +46,7 @@ export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
34
46
 
35
47
  return datas.map((r) => {
36
48
  const rawModel = r as Record<string, unknown>;
37
- const newModel: Record<string, unknown> = {};
49
+ const newModel = Object.create(schema.meta.prototype) as T;
38
50
 
39
51
  schema.meta.props
40
52
  .filter((p) => p.kind === "scalar")
@@ -42,22 +54,21 @@ export class ForgeSQLSelectOperations implements SchemaSqlForgeSql {
42
54
  const fieldName = p.name;
43
55
  const fieldNames = p.fieldNames;
44
56
  const rawFieldName = fieldNames && Array.isArray(fieldNames) ? fieldNames[0] : p.name;
45
-
46
57
  switch (p.type) {
47
58
  case "datetime":
48
59
  newModel[fieldName] = parseDateTime(
49
60
  rawModel[rawFieldName] as string,
50
61
  "YYYY-MM-DDTHH:mm:ss.SSS",
51
- );
62
+ ) as unknown as T[EntityKey<T>];
52
63
  break;
53
64
  case "date":
54
- newModel[fieldName] = parseDateTime(rawModel[rawFieldName] as string, "YYYY-MM-DD");
65
+ newModel[fieldName] = parseDateTime(rawModel[rawFieldName] as string, "YYYY-MM-DD") as unknown as T[EntityKey<T>];
55
66
  break;
56
67
  case "time":
57
- newModel[fieldName] = parseDateTime(rawModel[rawFieldName] as string, "HH:mm:ss.SSS");
68
+ newModel[fieldName] = parseDateTime(rawModel[rawFieldName] as string, "HH:mm:ss.SSS") as unknown as T[EntityKey<T>];
58
69
  break;
59
70
  default:
60
- newModel[fieldName] = rawModel[rawFieldName];
71
+ newModel[fieldName] = rawModel[rawFieldName] as unknown as T[EntityKey<T>];
61
72
  }
62
73
  });
63
74
  return newModel as T;
@@ -1,6 +1,6 @@
1
- import { types } from "@mikro-orm/core/types";
2
1
  import moment from "moment";
3
2
  import { AnyString } from "@mikro-orm/core/typings";
3
+ import {types} from "..";
4
4
 
5
5
  const wrapIfNeeded=(data:string, wrap:boolean):string => {
6
6
  return wrap?`'${data}'`:data;