forge-sql-orm 1.0.23 → 1.0.25

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.
@@ -3,23 +3,30 @@ export * from "@mikro-orm/mysql";
3
3
  import { sql } from "@forge/sql";
4
4
  import moment from "moment";
5
5
  export * from "@mikro-orm/entity-generator";
6
- const transformValue = (value) => {
6
+ const wrapIfNeeded = (data, wrap) => {
7
+ return wrap ? `'${data}'` : data;
8
+ };
9
+ const transformValue = (value, wrapValue = false) => {
7
10
  switch (value.type) {
8
11
  case "text":
9
12
  case "string":
10
- return `'${value.value}'`;
13
+ return wrapIfNeeded(`${value.value}`, wrapValue);
11
14
  case "datetime":
12
- return `'${moment(value.value).format("YYYY-MM-DDTHH:mm:ss.SSS")}'`;
15
+ return wrapIfNeeded(`${moment(value.value).format("YYYY-MM-DDTHH:mm:ss.SSS")}`, wrapValue);
13
16
  case "date":
14
- return `'${moment(value.value).format("YYYY-MM-DD")}'`;
17
+ return wrapIfNeeded(`${moment(value.value).format("YYYY-MM-DD")}`, wrapValue);
15
18
  case "time":
16
- return `'${moment(value.value).format("HH:mm:ss.SSS")}'`;
19
+ return wrapIfNeeded(`${moment(value.value).format("HH:mm:ss.SSS")}`, wrapValue);
17
20
  default:
18
21
  return value.value;
19
22
  }
20
23
  };
21
24
  const parseDateTime = (value, format) => {
22
- return moment(value, format).toDate();
25
+ const m = moment(value, format, true);
26
+ if (!m.isValid()) {
27
+ return moment(value).toDate();
28
+ }
29
+ return m.toDate();
23
30
  };
24
31
  class ForgeSQLCrudOperations {
25
32
  forgeOperations;
@@ -29,57 +36,86 @@ class ForgeSQLCrudOperations {
29
36
  this.options = options;
30
37
  }
31
38
  /**
32
- * Generates an SQL insert query with values.
39
+ * Generates an SQL INSERT statement for the provided models.
40
+ * If a version field exists in the schema, its value is set accordingly.
41
+ *
33
42
  * @param schema - The entity schema.
34
43
  * @param models - The list of entities to insert.
35
44
  * @param updateIfExists - Whether to update the row if it already exists.
36
- * @returns An object containing the SQL query, fields, and values.
45
+ * @returns An object containing the SQL query, column names, and values.
37
46
  */
38
47
  async generateInsertScript(schema, models, updateIfExists) {
39
- const fieldNames = /* @__PURE__ */ new Set();
40
- const fieldValueMaps = [];
48
+ const columnNames = /* @__PURE__ */ new Set();
49
+ const modelFieldValues = [];
41
50
  models.forEach((model) => {
42
- const fieldValueMap = {};
43
- schema.meta.props.forEach((p) => {
44
- const modelValue = model[p.name];
45
- if (p.kind === "scalar" && modelValue !== void 0) {
46
- fieldNames.add(p.fieldNames[0] || p.name);
47
- fieldValueMap[p.fieldNames[0]] = {
48
- type: p.type,
49
- value: modelValue
50
- };
51
+ const fieldValues = {};
52
+ schema.meta.props.forEach((prop) => {
53
+ const value = model[prop.name];
54
+ if (prop.kind === "scalar" && value !== void 0) {
55
+ const columnName = this.getRealFieldNameFromSchema(prop);
56
+ columnNames.add(columnName);
57
+ fieldValues[columnName] = { type: prop.type, value };
51
58
  }
52
59
  });
53
- fieldValueMaps.push(fieldValueMap);
60
+ modelFieldValues.push(fieldValues);
54
61
  });
55
- const fields = Array.from(fieldNames);
56
- const values = fieldValueMaps.flatMap(
57
- (fieldValueMap) => fields.map(
58
- (f) => fieldValueMap[f] || {
62
+ const versionField = this.getVersionField(schema);
63
+ if (versionField) {
64
+ modelFieldValues.forEach((mv) => {
65
+ const versionRealName = this.getRealFieldNameFromSchema(versionField);
66
+ if (mv[versionRealName]) {
67
+ mv[versionRealName].value = transformValue(
68
+ { value: this.createVersionField(versionField), type: versionField.name },
69
+ true
70
+ );
71
+ } else {
72
+ mv[versionRealName] = {
73
+ type: versionField.type,
74
+ value: transformValue(
75
+ { value: this.createVersionField(versionField), type: versionField.name },
76
+ true
77
+ )
78
+ };
79
+ columnNames.add(versionField.name);
80
+ }
81
+ });
82
+ }
83
+ const columns = Array.from(columnNames);
84
+ const values = modelFieldValues.flatMap(
85
+ (fieldValueMap) => columns.map(
86
+ (column) => fieldValueMap[column] || {
59
87
  type: "string",
60
88
  value: null
61
89
  }
62
90
  )
63
91
  );
92
+ const insertValues = modelFieldValues.map((fieldValueMap) => {
93
+ const rowValues = columns.map(
94
+ (column) => transformValue(
95
+ fieldValueMap[column] || { type: "string", value: null },
96
+ true
97
+ )
98
+ ).join(",");
99
+ return `(${rowValues})`;
100
+ }).join(", ");
101
+ const insertEmptyValues = modelFieldValues.map(() => {
102
+ const rowValues = columns.map(
103
+ () => "?"
104
+ ).join(",");
105
+ return `(${rowValues})`;
106
+ }).join(", ");
107
+ const updateClause = updateIfExists ? ` ON DUPLICATE KEY UPDATE ${columns.map((col) => `${col} = VALUES(${col})`).join(",")}` : "";
64
108
  return {
65
- sql: `INSERT INTO ${schema.meta.collection} (${fields.join(",")}) VALUES ${fieldValueMaps.map(
66
- (fieldValueMap) => `(${fields.map(
67
- (f) => transformValue(
68
- fieldValueMap[f] || {
69
- type: "string",
70
- value: null
71
- }
72
- )
73
- ).join(",")})`
74
- ).join(
75
- ", "
76
- )} ${updateIfExists ? `ON DUPLICATE KEY UPDATE ${fields.map((f) => `${f} = VALUES(${f})`).join(",")}` : ""}`,
77
- fields,
109
+ sql: `INSERT INTO ${schema.meta.collection} (${columns.join(",")}) VALUES ${insertValues}${updateClause}`,
110
+ query: `INSERT INTO ${schema.meta.collection} (${columns.join(",")}) VALUES ${insertEmptyValues}${updateClause}`,
111
+ fields: columns,
78
112
  values
79
113
  };
80
114
  }
81
115
  /**
82
116
  * Inserts records into the database.
117
+ * If a version field exists in the schema, versioning is applied.
118
+ *
83
119
  * @param schema - The entity schema.
84
120
  * @param models - The list of entities to insert.
85
121
  * @param updateIfExists - Whether to update the row if it already exists.
@@ -89,27 +125,29 @@ class ForgeSQLCrudOperations {
89
125
  if (!models || models.length === 0) return 0;
90
126
  const query = await this.generateInsertScript(schema, models, updateIfExists);
91
127
  if (this.options?.logRawSqlQuery) {
92
- console.debug("INSERT SQL: " + query.sql);
128
+ console.debug("INSERT SQL: " + query.query);
93
129
  }
94
130
  const sqlStatement = sql.prepare(query.sql);
95
- const updateQueryResponseResult = await sqlStatement.execute();
96
- return updateQueryResponseResult.rows.insertId;
131
+ const result = await sqlStatement.execute();
132
+ return result.rows.insertId;
97
133
  }
98
134
  /**
99
- * Retrieves the primary keys for the given entity schema.
135
+ * Retrieves the primary key properties from the entity schema.
136
+ *
100
137
  * @param schema - The entity schema.
101
138
  * @returns An array of primary key properties.
102
139
  * @throws If no primary keys are found.
103
140
  */
104
141
  getPrimaryKeys(schema) {
105
- const primaryKeys = schema.meta.props.filter((p) => p.primary);
142
+ const primaryKeys = schema.meta.props.filter((prop) => prop.primary);
106
143
  if (!primaryKeys.length) {
107
144
  throw new Error(`No primary keys found for schema: ${schema.meta.className}`);
108
145
  }
109
146
  return primaryKeys;
110
147
  }
111
148
  /**
112
- * Deletes a record by its ID.
149
+ * Deletes a record by its primary key.
150
+ *
113
151
  * @param id - The ID of the record to delete.
114
152
  * @param schema - The entity schema.
115
153
  * @returns The number of rows affected.
@@ -125,33 +163,303 @@ class ForgeSQLCrudOperations {
125
163
  queryBuilder.andWhere({ [primaryKey.name]: { $eq: id } });
126
164
  const query = queryBuilder.getFormattedQuery();
127
165
  if (this.options?.logRawSqlQuery) {
128
- console.debug("DELETE SQL: " + query);
166
+ console.debug("DELETE SQL: " + queryBuilder.getQuery());
129
167
  }
130
168
  const sqlStatement = sql.prepare(query);
131
- const updateQueryResponseResult = await sqlStatement.execute();
132
- return updateQueryResponseResult.rows.affectedRows;
169
+ const result = await sqlStatement.execute();
170
+ return result.rows.affectedRows;
133
171
  }
134
172
  /**
135
- * Updates a record by its ID.
173
+ * Retrieves the version field from the entity schema.
174
+ * The version field must be of type datetime, integer, or decimal, not a primary key, and not nullable.
175
+ *
176
+ * @param schema - The entity schema.
177
+ * @returns The version field property if it exists.
178
+ */
179
+ getVersionField(schema) {
180
+ if (this.options.disableOptimisticLocking) {
181
+ return void 0;
182
+ }
183
+ return schema.meta.props.filter((prop) => prop.version).filter((prop) => {
184
+ const validType = prop.type === "datetime" || prop.type === "integer" || prop.type === "decimal";
185
+ if (!validType) {
186
+ console.warn(
187
+ `Version field "${prop.name}" in table ${schema.meta.tableName} must be datetime, integer, or decimal, but is "${prop.type}"`
188
+ );
189
+ }
190
+ return validType;
191
+ }).filter((prop) => {
192
+ if (prop.primary) {
193
+ console.warn(
194
+ `Version field "${prop.name}" in table ${schema.meta.tableName} cannot be a primary key`
195
+ );
196
+ return false;
197
+ }
198
+ return true;
199
+ }).find((prop) => {
200
+ if (prop.nullable) {
201
+ console.warn(
202
+ `Version field "${prop.name}" in table ${schema.meta.tableName} should not be nullable`
203
+ );
204
+ return false;
205
+ }
206
+ return true;
207
+ });
208
+ }
209
+ /**
210
+ * Increments the version field of an entity.
211
+ * For datetime types, sets the current date; for numeric types, increments by 1.
212
+ *
213
+ * @param versionField - The version field property.
214
+ * @param updateModel - The entity to update.
215
+ */
216
+ incrementVersionField(versionField, updateModel) {
217
+ const key = versionField.name;
218
+ switch (versionField.type) {
219
+ case "datetime": {
220
+ updateModel[key] = /* @__PURE__ */ new Date();
221
+ break;
222
+ }
223
+ case "decimal":
224
+ case "integer": {
225
+ updateModel[key] = updateModel[key] + 1;
226
+ break;
227
+ }
228
+ default:
229
+ throw new Error(`Unsupported version field type: ${versionField.type}`);
230
+ }
231
+ }
232
+ /**
233
+ * Creates the initial version field value for an entity.
234
+ * For datetime types, returns the current date; for numeric types, returns 0.
235
+ *
236
+ * @param versionField - The version field property.
237
+ */
238
+ createVersionField(versionField) {
239
+ switch (versionField.type) {
240
+ case "datetime": {
241
+ return /* @__PURE__ */ new Date();
242
+ }
243
+ case "decimal":
244
+ case "integer": {
245
+ return 0;
246
+ }
247
+ default:
248
+ throw new Error(`Unsupported version field type: ${versionField.type}`);
249
+ }
250
+ }
251
+ /**
252
+ * Updates a record by its primary key using the provided entity data.
253
+ *
136
254
  * @param entity - The entity with updated values.
137
255
  * @param schema - The entity schema.
138
- * @throws If the primary key value is missing in the entity.
139
256
  */
140
257
  async updateById(entity, schema) {
258
+ const fields = schema.meta.props.filter((prop) => prop.kind === "scalar").map((prop) => prop.name);
259
+ await this.updateFieldById(entity, fields, schema);
260
+ }
261
+ /**
262
+ * Updates specified fields of records based on provided conditions.
263
+ * If the "where" parameter is not provided, the WHERE clause is built from the entity fields
264
+ * that are not included in the list of fields to update.
265
+ *
266
+ * @param entity - The object containing values to update and potential criteria for filtering.
267
+ * @param fields - Array of field names to update.
268
+ * @param schema - The entity schema.
269
+ * @param where - Optional filtering conditions for the WHERE clause.
270
+ * @returns The number of affected rows.
271
+ * @throws If no filtering criteria are provided (either via "where" or from the remaining entity fields).
272
+ */
273
+ async updateFields(entity, fields, schema, where) {
274
+ const updateData = this.filterEntityFields(entity, fields);
275
+ const updateModel = this.modifyModel(updateData, schema);
276
+ let queryBuilder = this.forgeOperations.createQueryBuilder(schema.meta.class).getKnexQuery();
277
+ queryBuilder.update(updateModel);
278
+ if (where) {
279
+ queryBuilder.where(where);
280
+ } else {
281
+ const filterCriteria = Object.keys(entity).filter((key) => !fields.includes(key)).reduce((criteria, key) => {
282
+ if (entity[key] !== void 0) {
283
+ criteria[key] = entity[key];
284
+ }
285
+ return criteria;
286
+ }, {});
287
+ if (Object.keys(filterCriteria).length === 0) {
288
+ throw new Error(
289
+ "Filtering criteria (WHERE clause) must be provided either via the 'where' parameter or through non-updated entity fields"
290
+ );
291
+ }
292
+ queryBuilder.where(filterCriteria);
293
+ }
294
+ if (this.options?.logRawSqlQuery) {
295
+ console.debug("UPDATE SQL (updateFields): " + queryBuilder.toSQL().sql);
296
+ }
297
+ const sqlQuery = queryBuilder.toQuery();
298
+ const updateQueryResponse = await this.forgeOperations.fetch().executeRawUpdateSQL(sqlQuery);
299
+ return updateQueryResponse.affectedRows;
300
+ }
301
+ /**
302
+ * Updates specific fields of a record identified by its primary key.
303
+ * If a version field exists in the schema, versioning is applied.
304
+ *
305
+ * @param entity - The entity with updated values.
306
+ * @param fields - The list of field names to update.
307
+ * @param schema - The entity schema.
308
+ * @throws If the primary key is not included in the update fields.
309
+ */
310
+ async updateFieldById(entity, fields, schema) {
141
311
  const primaryKeys = this.getPrimaryKeys(schema);
142
- const queryBuilder = this.forgeOperations.createQueryBuilder(schema.meta.class).update(entity);
143
312
  primaryKeys.forEach((pk) => {
144
- const value = entity[pk.name];
145
- if (value === null || value === void 0) {
146
- throw new Error(`Primary Key ${pk.name} must exist in the model`);
313
+ if (!fields.includes(pk.name)) {
314
+ throw new Error("Update fields must include primary key: " + pk.name);
147
315
  }
148
- queryBuilder.andWhere({ [pk.name]: { $eq: value } });
149
316
  });
150
- const query = queryBuilder.getFormattedQuery();
317
+ const updatedEntity = this.filterEntityFields(entity, fields);
318
+ let queryBuilder = this.forgeOperations.createQueryBuilder(schema.meta.class).getKnexQuery();
319
+ const versionField = this.getVersionField(schema);
320
+ const useVersion = Boolean(versionField);
321
+ let updateModel = { ...updatedEntity };
322
+ if (useVersion && versionField) {
323
+ let oldModel = entity;
324
+ if (entity[versionField.name] === void 0 || entity[versionField.name] === null) {
325
+ oldModel = await this.getOldModel(primaryKeys, entity, schema, versionField);
326
+ }
327
+ const primaryFieldNames = primaryKeys.map((pk) => pk.name);
328
+ const fieldsToRetain = primaryFieldNames.concat(versionField.name);
329
+ const fromEntries = Object.fromEntries(fieldsToRetain.map((key) => [key, oldModel[key]]));
330
+ updateModel = { ...updatedEntity, ...fromEntries };
331
+ this.incrementVersionField(versionField, updateModel);
332
+ updateModel = this.modifyModel(updateModel, schema);
333
+ queryBuilder.update(updateModel);
334
+ if (oldModel[versionField.name] !== void 0 || oldModel[versionField.name] !== null && this.isValid(oldModel[versionField.name])) {
335
+ queryBuilder.andWhere(this.optWhere(oldModel, versionField));
336
+ }
337
+ } else {
338
+ updateModel = this.modifyModel(updatedEntity, schema);
339
+ queryBuilder.update(updateModel);
340
+ }
341
+ this.addPrimaryWhere(queryBuilder, primaryKeys, updateModel);
342
+ const sqlQuery = queryBuilder.toQuery();
151
343
  if (this.options?.logRawSqlQuery) {
152
- console.debug("UPDATE SQL: " + query);
344
+ console.debug("UPDATE SQL: " + queryBuilder.toSQL().sql);
345
+ }
346
+ const updateQueryResponse = await this.forgeOperations.fetch().executeRawUpdateSQL(sqlQuery);
347
+ if (versionField && !updateQueryResponse.affectedRows) {
348
+ throw new Error(
349
+ "Optimistic locking failed: the record with primary key(s) " + primaryKeys.map((p) => updateModel[p.name]).join(", ") + " has been modified by another process."
350
+ );
153
351
  }
154
- await this.forgeOperations.fetch().executeRawUpdateSQL(query);
352
+ }
353
+ /**
354
+ * Constructs an optional WHERE clause for the version field.
355
+ *
356
+ * @param updateModel - The model containing the current version field value.
357
+ * @param versionField - The version field property.
358
+ * @returns A filter query for the version field.
359
+ */
360
+ optWhere(updateModel, versionField) {
361
+ const currentVersionValue = transformValue(
362
+ { value: updateModel[versionField.name], type: versionField.type },
363
+ false
364
+ );
365
+ return { [versionField.name]: currentVersionValue };
366
+ }
367
+ /**
368
+ * Retrieves the current state of a record from the database.
369
+ *
370
+ * @param primaryKeys - The primary key properties.
371
+ * @param entity - The entity with updated values.
372
+ * @param schema - The entity schema.
373
+ * @param versionField - The version field property.
374
+ * @returns The existing record from the database.
375
+ * @throws If the record does not exist or if multiple records are found.
376
+ */
377
+ async getOldModel(primaryKeys, entity, schema, versionField) {
378
+ const primaryFieldNames = primaryKeys.map((pk) => pk.name);
379
+ const fieldsToSelect = primaryFieldNames.concat(versionField.name);
380
+ const queryBuilder = this.forgeOperations.createQueryBuilder(schema).select(fieldsToSelect);
381
+ this.addPrimaryWhere(queryBuilder, primaryKeys, entity);
382
+ const formattedQuery = queryBuilder.getFormattedQuery();
383
+ const models = await this.forgeOperations.fetch().executeSchemaSQL(formattedQuery, schema);
384
+ if (!models || models.length === 0) {
385
+ throw new Error(`Cannot modify record because it does not exist in table ${schema.meta.tableName}`);
386
+ }
387
+ if (models.length > 1) {
388
+ throw new Error(
389
+ `Cannot modify record because multiple rows with the same ID were found in table ${schema.meta.tableName}. Please verify the table metadata.`
390
+ );
391
+ }
392
+ return models[0];
393
+ }
394
+ /**
395
+ * Adds primary key conditions to the query builder.
396
+ *
397
+ * @param queryBuilder - The Knex query builder instance.
398
+ * @param primaryKeys - The primary key properties.
399
+ * @param entity - The entity containing primary key values.
400
+ * @throws If any primary key value is missing.
401
+ */
402
+ addPrimaryWhere(queryBuilder, primaryKeys, entity) {
403
+ primaryKeys.forEach((pk) => {
404
+ const fieldName = this.getRealFieldNameFromSchema(pk);
405
+ const value = entity[fieldName];
406
+ if (value === null || value === void 0) {
407
+ throw new Error(`Primary key ${fieldName} must exist in the model`);
408
+ }
409
+ queryBuilder.andWhere({ [fieldName]: value });
410
+ });
411
+ }
412
+ /**
413
+ * Filters the entity to include only the specified fields.
414
+ *
415
+ * @param entity - The original entity.
416
+ * @param fields - The list of fields to retain.
417
+ * @returns A partial entity object containing only the specified fields.
418
+ */
419
+ filterEntityFields = (entity, fields) => fields.reduce((result, field) => {
420
+ if (field in entity) {
421
+ result[field] = entity[field];
422
+ }
423
+ return result;
424
+ }, {});
425
+ /**
426
+ * Transforms and modifies the updated entity model based on the schema.
427
+ *
428
+ * @param updatedEntity - The updated entity.
429
+ * @param schema - The entity schema.
430
+ * @returns The modified entity.
431
+ */
432
+ modifyModel(updatedEntity, schema) {
433
+ const modifiedModel = {};
434
+ schema.meta.props.filter((p) => p.kind === "scalar").forEach((p) => {
435
+ const value = updatedEntity[p.name];
436
+ if (value !== void 0 && value !== null) {
437
+ const fieldName = this.getRealFieldNameFromSchema(p);
438
+ modifiedModel[fieldName] = transformValue({ value, type: p.type }, false);
439
+ }
440
+ });
441
+ return modifiedModel;
442
+ }
443
+ /**
444
+ * Returns the real field name from the entity property based on the schema.
445
+ *
446
+ * @param p - The entity property.
447
+ * @returns The real field name.
448
+ */
449
+ getRealFieldNameFromSchema(p) {
450
+ return p.fieldNames && p.fieldNames.length ? p.fieldNames[0] : p.name;
451
+ }
452
+ /**
453
+ * Validates the provided value.
454
+ *
455
+ * @param value - The value to validate.
456
+ * @returns True if the value is valid, false otherwise.
457
+ */
458
+ isValid(value) {
459
+ if (value instanceof Date) {
460
+ return !isNaN(value.getTime());
461
+ }
462
+ return true;
155
463
  }
156
464
  }
157
465
  class ForgeSQLSelectOperations {
@@ -159,6 +467,16 @@ class ForgeSQLSelectOperations {
159
467
  constructor(options) {
160
468
  this.options = options;
161
469
  }
470
+ async executeSchemaSQLOnlyOne(query, schema) {
471
+ const results = await this.executeSchemaSQL(query, schema);
472
+ if (!results || results.length === 0) {
473
+ return void 0;
474
+ }
475
+ if (results.length > 1) {
476
+ throw new Error("Expected 1 record but returned " + results.length);
477
+ }
478
+ return results[0];
479
+ }
162
480
  /**
163
481
  * Executes a schema-based SQL query and maps the result to the entity schema.
164
482
  * @param query - The SQL query to execute.
@@ -210,13 +528,14 @@ class ForgeSQLSelectOperations {
210
528
  /**
211
529
  * Executes a raw SQL update query.
212
530
  * @param query - The raw SQL update query.
531
+ * @param params - sql parameters.
213
532
  * @returns The update response containing affected rows.
214
533
  */
215
- async executeRawUpdateSQL(query) {
216
- if (this.options.logRawSqlQuery) {
217
- console.debug("Executing update SQL: " + query);
218
- }
534
+ async executeRawUpdateSQL(query, params) {
219
535
  const sqlStatement = sql.prepare(query);
536
+ if (params) {
537
+ sqlStatement.bindParams(params);
538
+ }
220
539
  const updateQueryResponseResults = await sqlStatement.execute();
221
540
  return updateQueryResponseResults.rows;
222
541
  }
@@ -253,7 +572,7 @@ class ForgeSQLORMImpl {
253
572
  preferTs: false,
254
573
  debug: false
255
574
  });
256
- const newOptions = options ?? { logRawSqlQuery: false };
575
+ const newOptions = options ?? { logRawSqlQuery: false, disableOptimisticLocking: false };
257
576
  this.crudOperations = new ForgeSQLCrudOperations(this, newOptions);
258
577
  this.fetchOperations = new ForgeSQLSelectOperations(newOptions);
259
578
  } catch (error) {
@@ -264,11 +583,12 @@ class ForgeSQLORMImpl {
264
583
  /**
265
584
  * Returns the singleton instance of ForgeSQLORMImpl.
266
585
  * @param entities - List of entities (required only on first initialization).
586
+ * @param options - Options for configuring ForgeSQL ORM behavior.
267
587
  * @returns The singleton instance of ForgeSQLORMImpl.
268
588
  */
269
- static getInstance(entities) {
589
+ static getInstance(entities, options) {
270
590
  if (!ForgeSQLORMImpl.instance) {
271
- ForgeSQLORMImpl.instance = new ForgeSQLORMImpl(entities);
591
+ ForgeSQLORMImpl.instance = new ForgeSQLORMImpl(entities, options);
272
592
  }
273
593
  return ForgeSQLORMImpl.instance;
274
594
  }
@@ -297,7 +617,7 @@ class ForgeSQLORMImpl {
297
617
  return this.mikroORM.em.createQueryBuilder(entityName, alias, void 0, loggerContext);
298
618
  }
299
619
  /**
300
- * Provides access to the underlying Knex instance for executing raw queries and building complex query parts.
620
+ * Provides access to the underlying Knex instance for building complex query parts.
301
621
  * enabling advanced query customization and performance tuning.
302
622
  * @returns The Knex instance, which can be used for query building.
303
623
  */
@@ -307,8 +627,8 @@ class ForgeSQLORMImpl {
307
627
  }
308
628
  class ForgeSQLORM {
309
629
  ormInstance;
310
- constructor(entities) {
311
- this.ormInstance = ForgeSQLORMImpl.getInstance(entities);
630
+ constructor(entities, options) {
631
+ this.ormInstance = ForgeSQLORMImpl.getInstance(entities, options);
312
632
  }
313
633
  /**
314
634
  * Proxies the `crud` method from `ForgeSQLORMImpl`.