@solidxai/core 0.1.6-beta.11 → 0.1.6-beta.12

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.
@@ -1,15 +1,16 @@
1
1
  import { CommonEntity } from "src/entities/common.entity";
2
2
  import { ComputedFieldMetadata } from "src/helpers/solid-registry";
3
- import { IEntityPreComputeFieldProvider } from "src/interfaces";
3
+ import { IEntityPostComputeFieldProvider } from "src/interfaces";
4
4
  import { DataSource } from "typeorm";
5
5
  export interface SequenceNumComputedFieldContext {
6
6
  sequenceName: string;
7
7
  }
8
- export declare class SequenceNumComputedFieldProvider<T extends CommonEntity> implements IEntityPreComputeFieldProvider<T, SequenceNumComputedFieldContext> {
8
+ export declare class SequenceNumComputedFieldProvider<T extends CommonEntity> implements IEntityPostComputeFieldProvider<T, SequenceNumComputedFieldContext> {
9
9
  private readonly dataSource;
10
10
  constructor(dataSource: DataSource);
11
11
  name(): string;
12
12
  help(): string;
13
- preComputeValue(triggerEntity: T, computedFieldMetadata: ComputedFieldMetadata<SequenceNumComputedFieldContext>): Promise<void>;
13
+ private generateSequenceValue;
14
+ postComputeAndSaveValue(triggerEntity: T, computedFieldMetadata: ComputedFieldMetadata<SequenceNumComputedFieldContext>): Promise<void>;
14
15
  }
15
16
  //# sourceMappingURL=sequence-num-computed-field-provider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"sequence-num-computed-field-provider.d.ts","sourceRoot":"","sources":["../../../../src/services/computed-fields/entity/sequence-num-computed-field-provider.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,8BAA8B,EAAE,MAAM,gBAAgB,CAAC;AAChE,OAAO,EAAE,UAAU,EAAgB,MAAM,SAAS,CAAC;AAGnD,MAAM,WAAW,+BAA+B;IAC5C,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,qBAEa,gCAAgC,CAAC,CAAC,SAAS,YAAY,CAAE,YAAW,8BAA8B,CAAC,CAAC,EAAE,+BAA+B,CAAC;IAG3I,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,UAAU;IAG3C,IAAI,IAAI,MAAM;IAId,IAAI,IAAI,MAAM;IAIR,eAAe,CAAC,aAAa,EAAE,CAAC,EAAE,qBAAqB,EAAE,qBAAqB,CAAC,+BAA+B,CAAC;CAuDxH"}
1
+ {"version":3,"file":"sequence-num-computed-field-provider.d.ts","sourceRoot":"","sources":["../../../../src/services/computed-fields/entity/sequence-num-computed-field-provider.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D,OAAO,EAAE,qBAAqB,EAAE,MAAM,4BAA4B,CAAC;AACnE,OAAO,EAAE,+BAA+B,EAAE,MAAM,gBAAgB,CAAC;AACjE,OAAO,EAAE,UAAU,EAAiB,MAAM,SAAS,CAAC;AAGpD,MAAM,WAAW,+BAA+B;IAC5C,YAAY,EAAE,MAAM,CAAC;CACxB;AAED,qBAEa,gCAAgC,CAAC,CAAC,SAAS,YAAY,CAAE,YAAW,+BAA+B,CAAC,CAAC,EAAE,+BAA+B,CAAC;IAG5I,OAAO,CAAC,QAAQ,CAAC,UAAU;gBAAV,UAAU,EAAE,UAAU;IAG3C,IAAI,IAAI,MAAM;IAId,IAAI,IAAI,MAAM;YAIA,qBAAqB;IA2B7B,uBAAuB,CAAC,aAAa,EAAE,CAAC,EAAE,qBAAqB,EAAE,qBAAqB,CAAC,+BAA+B,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;CAahJ"}
@@ -28,13 +28,9 @@ let SequenceNumComputedFieldProvider = class SequenceNumComputedFieldProvider {
28
28
  help() {
29
29
  return "Computed field provider used to create fields whose value is based on some prefix, padding & sequence number.";
30
30
  }
31
- async preComputeValue(triggerEntity, computedFieldMetadata) {
32
- const { sequenceName } = computedFieldMetadata.computedFieldValueProviderCtxt ?? {};
33
- if (!sequenceName) {
34
- throw new Error("sequenceName is required for sequence computation");
35
- }
36
- await this.dataSource.transaction(async (manager) => {
37
- const modelSequenceRepo = manager.getRepository(model_sequence_entity_1.ModelSequence);
31
+ async generateSequenceValue(sequenceName, manager) {
32
+ const run = async (mgr) => {
33
+ const modelSequenceRepo = mgr.getRepository(model_sequence_entity_1.ModelSequence);
38
34
  const modelSequence = await modelSequenceRepo.findOne({
39
35
  where: { sequenceName },
40
36
  lock: { mode: "pessimistic_write" }
@@ -47,19 +43,19 @@ let SequenceNumComputedFieldProvider = class SequenceNumComputedFieldProvider {
47
43
  const prefix = modelSequence.prefix ?? "";
48
44
  const separator = modelSequence.separator ?? "";
49
45
  const sequenceString = `${prefix}${separator}${paddedValue}`;
50
- const entityRepo = manager.getRepository(triggerEntity.constructor);
51
- const existing = await entityRepo.findOne({
52
- where: {
53
- [computedFieldMetadata.fieldName]: sequenceString,
54
- },
55
- });
56
- if (existing) {
57
- throw new Error(`Duplicate Sequence generated: ${sequenceString}`);
58
- }
59
- triggerEntity[computedFieldMetadata.fieldName] = sequenceString;
60
46
  modelSequence.currentValue = nextValue;
61
47
  await modelSequenceRepo.save(modelSequence);
62
- });
48
+ return { sequenceString, currentValue: nextValue };
49
+ };
50
+ return manager ? run(manager) : this.dataSource.transaction(run);
51
+ }
52
+ async postComputeAndSaveValue(triggerEntity, computedFieldMetadata) {
53
+ const { sequenceName } = computedFieldMetadata.computedFieldValueProviderCtxt ?? {};
54
+ if (!sequenceName) {
55
+ throw new Error("sequenceName is required for sequence computation");
56
+ }
57
+ const { sequenceString } = await this.generateSequenceValue(sequenceName);
58
+ await this.dataSource.manager.update(triggerEntity.constructor, triggerEntity.id, { [computedFieldMetadata.fieldName]: sequenceString });
63
59
  }
64
60
  };
65
61
  exports.SequenceNumComputedFieldProvider = SequenceNumComputedFieldProvider;
@@ -1 +1 @@
1
- {"version":3,"file":"sequence-num-computed-field-provider.js","sourceRoot":"","sources":["../../../../src/services/computed-fields/entity/sequence-num-computed-field-provider.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6CAAmD;AACnD,6GAAyF;AAEzF,mFAAmE;AAGnE,qCAAmD;AAS5C,IAAM,gCAAgC,GAAtC,MAAM,gCAAgC;IACzC,YAEqB,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IACvC,CAAC;IAEL,IAAI;QACA,OAAO,kCAAkC,CAAC;IAC9C,CAAC;IAED,IAAI;QACA,OAAO,+GAA+G,CAAC;IAC3H,CAAC;IAED,KAAK,CAAC,eAAe,CAAC,aAAgB,EAAE,qBAA6E;QACjH,MAAM,EAAE,YAAY,EAAE,GAClB,qBAAqB,CAAC,8BAA8B,IAAI,EAAE,CAAC;QAE/D,IAAI,CAAC,YAAY,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE;YAKhD,MAAM,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC,qCAAa,CAAC,CAAA;YAC9D,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC;gBAClD,KAAK,EAAE,EAAE,YAAY,EAAE;gBACvB,IAAI,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE;aACtC,CAAC,CAAC;YAEH,IAAI,CAAC,aAAa,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,+BAA+B,YAAY,EAAE,CAAC,CAAC;YACnE,CAAC;YAGD,MAAM,SAAS,GAAG,aAAa,CAAC,YAAY,GAAG,CAAC,CAAC;YAEjD,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;YAEhF,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,IAAI,EAAE,CAAC;YAC1C,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,IAAI,EAAE,CAAC;YAEhD,MAAM,cAAc,GAAG,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,EAAE,CAAC;YAG7D,MAAM,UAAU,GAAG,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,WAAkB,CAAC,CAAC;YAE3E,MAAM,QAAQ,GAAG,MAAM,UAAU,CAAC,OAAO,CAAC;gBACtC,KAAK,EAAE;oBACH,CAAC,qBAAqB,CAAC,SAAS,CAAC,EAAE,cAAc;iBACpD;aACJ,CAAC,CAAC;YAEH,IAAI,QAAQ,EAAE,CAAC;gBACX,MAAM,IAAI,KAAK,CAAC,iCAAiC,cAAc,EAAE,CAAC,CAAC;YACvE,CAAC;YAGA,aAAqB,CAAC,qBAAqB,CAAC,SAAS,CAAC,GAAG,cAAc,CAAC;YAGzE,aAAa,CAAC,YAAY,GAAG,SAAS,CAAC;YACvC,MAAM,iBAAiB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACP,CAAC;CAEJ,CAAA;AArEY,4EAAgC;2CAAhC,gCAAgC;IAF5C,IAAA,yDAAqB,GAAE;IACvB,IAAA,mBAAU,GAAE;IAGJ,WAAA,IAAA,0BAAgB,GAAE,CAAA;qCACU,oBAAU;GAHlC,gCAAgC,CAqE5C","sourcesContent":["import { Injectable } from \"@nestjs/common\";\nimport { InjectDataSource } from \"@nestjs/typeorm\";\nimport { ComputedFieldProvider } from \"src/decorators/computed-field-provider.decorator\";\nimport { CommonEntity } from \"src/entities/common.entity\";\nimport { ModelSequence } from \"src/entities/model-sequence.entity\";\nimport { ComputedFieldMetadata } from \"src/helpers/solid-registry\";\nimport { IEntityPreComputeFieldProvider } from \"src/interfaces\";\nimport { DataSource, EntityTarget } from \"typeorm\";\n\n\nexport interface SequenceNumComputedFieldContext {\n sequenceName: string; // The separator to use between concatenated values\n}\n\n@ComputedFieldProvider()\n@Injectable()\nexport class SequenceNumComputedFieldProvider<T extends CommonEntity> implements IEntityPreComputeFieldProvider<T, SequenceNumComputedFieldContext> {\n constructor(\n @InjectDataSource()\n private readonly dataSource: DataSource\n ) { }\n\n name(): string {\n return \"SequenceNumComputedFieldProvider\";\n }\n\n help(): string {\n return \"Computed field provider used to create fields whose value is based on some prefix, padding & sequence number.\";\n }\n\n async preComputeValue(triggerEntity: T, computedFieldMetadata: ComputedFieldMetadata<SequenceNumComputedFieldContext>) {\n const { sequenceName } =\n computedFieldMetadata.computedFieldValueProviderCtxt ?? {};\n\n if (!sequenceName) {\n throw new Error(\"sequenceName is required for sequence computation\");\n }\n\n await this.dataSource.transaction(async (manager) => {\n /**\n * 1️⃣ Lock sequence row (prevents race conditions)\n */\n // 1️⃣ Fetch sequence row\n const modelSequenceRepo = manager.getRepository(ModelSequence)\n const modelSequence = await modelSequenceRepo.findOne({\n where: { sequenceName },\n lock: { mode: \"pessimistic_write\" }\n });\n\n if (!modelSequence) {\n throw new Error(`ModelSequence not found for ${sequenceName}`);\n }\n\n // 2️⃣ Generate next sequence value\n const nextValue = modelSequence.currentValue + 1;\n\n const paddedValue = String(nextValue).padStart(modelSequence.padding ?? 5, \"0\");\n\n const prefix = modelSequence.prefix ?? \"\";\n const separator = modelSequence.separator ?? \"\";\n\n const sequenceString = `${prefix}${separator}${paddedValue}`;\n\n // 3️⃣ Duplicate check on TARGET ENTITY (extra safety)\n const entityRepo = manager.getRepository(triggerEntity.constructor as any);\n\n const existing = await entityRepo.findOne({\n where: {\n [computedFieldMetadata.fieldName]: sequenceString,\n },\n });\n\n if (existing) {\n throw new Error(`Duplicate Sequence generated: ${sequenceString}`);\n }\n\n // 4️⃣ set the computed field on the entity\n (triggerEntity as any)[computedFieldMetadata.fieldName] = sequenceString;\n\n // 5️⃣ Persist updated sequence current value\n modelSequence.currentValue = nextValue;\n await modelSequenceRepo.save(modelSequence);\n });\n }\n\n}"]}
1
+ {"version":3,"file":"sequence-num-computed-field-provider.js","sourceRoot":"","sources":["../../../../src/services/computed-fields/entity/sequence-num-computed-field-provider.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;AAAA,2CAA4C;AAC5C,6CAAmD;AACnD,6GAAyF;AAEzF,mFAAmE;AAGnE,qCAAoD;AAS7C,IAAM,gCAAgC,GAAtC,MAAM,gCAAgC;IACzC,YAEqB,UAAsB;QAAtB,eAAU,GAAV,UAAU,CAAY;IACvC,CAAC;IAEL,IAAI;QACA,OAAO,kCAAkC,CAAC;IAC9C,CAAC;IAED,IAAI;QACA,OAAO,+GAA+G,CAAC;IAC3H,CAAC;IAEO,KAAK,CAAC,qBAAqB,CAAC,YAAoB,EAAE,OAAuB;QAC7E,MAAM,GAAG,GAAG,KAAK,EAAE,GAAkB,EAAE,EAAE;YACrC,MAAM,iBAAiB,GAAG,GAAG,CAAC,aAAa,CAAC,qCAAa,CAAC,CAAC;YAC3D,MAAM,aAAa,GAAG,MAAM,iBAAiB,CAAC,OAAO,CAAC;gBAClD,KAAK,EAAE,EAAE,YAAY,EAAE;gBACvB,IAAI,EAAE,EAAE,IAAI,EAAE,mBAAmB,EAAE;aACtC,CAAC,CAAC;YAEH,IAAI,CAAC,aAAa,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,+BAA+B,YAAY,EAAE,CAAC,CAAC;YACnE,CAAC;YAED,MAAM,SAAS,GAAG,aAAa,CAAC,YAAY,GAAG,CAAC,CAAC;YACjD,MAAM,WAAW,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,aAAa,CAAC,OAAO,IAAI,CAAC,EAAE,GAAG,CAAC,CAAC;YAChF,MAAM,MAAM,GAAG,aAAa,CAAC,MAAM,IAAI,EAAE,CAAC;YAC1C,MAAM,SAAS,GAAG,aAAa,CAAC,SAAS,IAAI,EAAE,CAAC;YAChD,MAAM,cAAc,GAAG,GAAG,MAAM,GAAG,SAAS,GAAG,WAAW,EAAE,CAAC;YAE7D,aAAa,CAAC,YAAY,GAAG,SAAS,CAAC;YACvC,MAAM,iBAAiB,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;YAE5C,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,SAAS,EAAE,CAAC;QACvD,CAAC,CAAC;QAEF,OAAO,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IACrE,CAAC;IAED,KAAK,CAAC,uBAAuB,CAAC,aAAgB,EAAE,qBAA6E;QACzH,MAAM,EAAE,YAAY,EAAE,GAAG,qBAAqB,CAAC,8BAA8B,IAAI,EAAE,CAAC;QAEpF,IAAI,CAAC,YAAY,EAAE,CAAC;YAChB,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;QACzE,CAAC;QAED,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,IAAI,CAAC,qBAAqB,CAAC,YAAY,CAAC,CAAC;QAG1E,MAAM,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,MAAM,CAAC,aAAa,CAAC,WAAW,EAAE,aAAa,CAAC,EAAE,EAAE,EAAE,CAAC,qBAAqB,CAAC,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC,CAAC;IAC7I,CAAC;CAEJ,CAAA;AAtDY,4EAAgC;2CAAhC,gCAAgC;IAF5C,IAAA,yDAAqB,GAAE;IACvB,IAAA,mBAAU,GAAE;IAGJ,WAAA,IAAA,0BAAgB,GAAE,CAAA;qCACU,oBAAU;GAHlC,gCAAgC,CAsD5C","sourcesContent":["import { Injectable } from \"@nestjs/common\";\nimport { InjectDataSource } from \"@nestjs/typeorm\";\nimport { ComputedFieldProvider } from \"src/decorators/computed-field-provider.decorator\";\nimport { CommonEntity } from \"src/entities/common.entity\";\nimport { ModelSequence } from \"src/entities/model-sequence.entity\";\nimport { ComputedFieldMetadata } from \"src/helpers/solid-registry\";\nimport { IEntityPostComputeFieldProvider } from \"src/interfaces\";\nimport { DataSource, EntityManager } from \"typeorm\";\n\n\nexport interface SequenceNumComputedFieldContext {\n sequenceName: string; // The separator to use between concatenated values\n}\n\n@ComputedFieldProvider()\n@Injectable()\nexport class SequenceNumComputedFieldProvider<T extends CommonEntity> implements IEntityPostComputeFieldProvider<T, SequenceNumComputedFieldContext> {\n constructor(\n @InjectDataSource()\n private readonly dataSource: DataSource\n ) { }\n\n name(): string {\n return \"SequenceNumComputedFieldProvider\";\n }\n\n help(): string {\n return \"Computed field provider used to create fields whose value is based on some prefix, padding & sequence number.\";\n }\n\n private async generateSequenceValue(sequenceName: string, manager?: EntityManager): Promise<{ sequenceString: string; currentValue: number }> {\n const run = async (mgr: EntityManager) => {\n const modelSequenceRepo = mgr.getRepository(ModelSequence);\n const modelSequence = await modelSequenceRepo.findOne({\n where: { sequenceName },\n lock: { mode: \"pessimistic_write\" }\n });\n\n if (!modelSequence) {\n throw new Error(`ModelSequence not found for ${sequenceName}`);\n }\n\n const nextValue = modelSequence.currentValue + 1;\n const paddedValue = String(nextValue).padStart(modelSequence.padding ?? 5, \"0\");\n const prefix = modelSequence.prefix ?? \"\";\n const separator = modelSequence.separator ?? \"\";\n const sequenceString = `${prefix}${separator}${paddedValue}`;\n\n modelSequence.currentValue = nextValue;\n await modelSequenceRepo.save(modelSequence);\n\n return { sequenceString, currentValue: nextValue };\n };\n\n return manager ? run(manager) : this.dataSource.transaction(run);\n }\n\n async postComputeAndSaveValue(triggerEntity: T, computedFieldMetadata: ComputedFieldMetadata<SequenceNumComputedFieldContext>): Promise<void> {\n const { sequenceName } = computedFieldMetadata.computedFieldValueProviderCtxt ?? {};\n\n if (!sequenceName) {\n throw new Error(\"sequenceName is required for sequence computation\");\n }\n\n const { sequenceString } = await this.generateSequenceValue(sequenceName);\n\n // Update the entity with the computed field value. update is preferred to avoid the post update hooks getting triggered again and causing an infinite loop\n await this.dataSource.manager.update(triggerEntity.constructor, triggerEntity.id, { [computedFieldMetadata.fieldName]: sequenceString });\n }\n\n}"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@solidxai/core",
3
- "version": "0.1.6-beta.11",
3
+ "version": "0.1.6-beta.12",
4
4
  "description": "This module is a NestJS module containing all the required core providers required by a Solid application",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -4,8 +4,8 @@ import { ComputedFieldProvider } from "src/decorators/computed-field-provider.de
4
4
  import { CommonEntity } from "src/entities/common.entity";
5
5
  import { ModelSequence } from "src/entities/model-sequence.entity";
6
6
  import { ComputedFieldMetadata } from "src/helpers/solid-registry";
7
- import { IEntityPreComputeFieldProvider } from "src/interfaces";
8
- import { DataSource, EntityTarget } from "typeorm";
7
+ import { IEntityPostComputeFieldProvider } from "src/interfaces";
8
+ import { DataSource, EntityManager } from "typeorm";
9
9
 
10
10
 
11
11
  export interface SequenceNumComputedFieldContext {
@@ -14,7 +14,7 @@ export interface SequenceNumComputedFieldContext {
14
14
 
15
15
  @ComputedFieldProvider()
16
16
  @Injectable()
17
- export class SequenceNumComputedFieldProvider<T extends CommonEntity> implements IEntityPreComputeFieldProvider<T, SequenceNumComputedFieldContext> {
17
+ export class SequenceNumComputedFieldProvider<T extends CommonEntity> implements IEntityPostComputeFieldProvider<T, SequenceNumComputedFieldContext> {
18
18
  constructor(
19
19
  @InjectDataSource()
20
20
  private readonly dataSource: DataSource
@@ -28,20 +28,9 @@ export class SequenceNumComputedFieldProvider<T extends CommonEntity> implements
28
28
  return "Computed field provider used to create fields whose value is based on some prefix, padding & sequence number.";
29
29
  }
30
30
 
31
- async preComputeValue(triggerEntity: T, computedFieldMetadata: ComputedFieldMetadata<SequenceNumComputedFieldContext>) {
32
- const { sequenceName } =
33
- computedFieldMetadata.computedFieldValueProviderCtxt ?? {};
34
-
35
- if (!sequenceName) {
36
- throw new Error("sequenceName is required for sequence computation");
37
- }
38
-
39
- await this.dataSource.transaction(async (manager) => {
40
- /**
41
- * 1️⃣ Lock sequence row (prevents race conditions)
42
- */
43
- // 1️⃣ Fetch sequence row
44
- const modelSequenceRepo = manager.getRepository(ModelSequence)
31
+ private async generateSequenceValue(sequenceName: string, manager?: EntityManager): Promise<{ sequenceString: string; currentValue: number }> {
32
+ const run = async (mgr: EntityManager) => {
33
+ const modelSequenceRepo = mgr.getRepository(ModelSequence);
45
34
  const modelSequence = await modelSequenceRepo.findOne({
46
35
  where: { sequenceName },
47
36
  lock: { mode: "pessimistic_write" }
@@ -51,36 +40,32 @@ export class SequenceNumComputedFieldProvider<T extends CommonEntity> implements
51
40
  throw new Error(`ModelSequence not found for ${sequenceName}`);
52
41
  }
53
42
 
54
- // 2️⃣ Generate next sequence value
55
43
  const nextValue = modelSequence.currentValue + 1;
56
-
57
44
  const paddedValue = String(nextValue).padStart(modelSequence.padding ?? 5, "0");
58
-
59
45
  const prefix = modelSequence.prefix ?? "";
60
46
  const separator = modelSequence.separator ?? "";
61
-
62
47
  const sequenceString = `${prefix}${separator}${paddedValue}`;
63
48
 
64
- // 3️⃣ Duplicate check on TARGET ENTITY (extra safety)
65
- const entityRepo = manager.getRepository(triggerEntity.constructor as any);
49
+ modelSequence.currentValue = nextValue;
50
+ await modelSequenceRepo.save(modelSequence);
51
+
52
+ return { sequenceString, currentValue: nextValue };
53
+ };
66
54
 
67
- const existing = await entityRepo.findOne({
68
- where: {
69
- [computedFieldMetadata.fieldName]: sequenceString,
70
- },
71
- });
55
+ return manager ? run(manager) : this.dataSource.transaction(run);
56
+ }
72
57
 
73
- if (existing) {
74
- throw new Error(`Duplicate Sequence generated: ${sequenceString}`);
75
- }
58
+ async postComputeAndSaveValue(triggerEntity: T, computedFieldMetadata: ComputedFieldMetadata<SequenceNumComputedFieldContext>): Promise<void> {
59
+ const { sequenceName } = computedFieldMetadata.computedFieldValueProviderCtxt ?? {};
60
+
61
+ if (!sequenceName) {
62
+ throw new Error("sequenceName is required for sequence computation");
63
+ }
76
64
 
77
- // 4️⃣ set the computed field on the entity
78
- (triggerEntity as any)[computedFieldMetadata.fieldName] = sequenceString;
65
+ const { sequenceString } = await this.generateSequenceValue(sequenceName);
79
66
 
80
- // 5️⃣ Persist updated sequence current value
81
- modelSequence.currentValue = nextValue;
82
- await modelSequenceRepo.save(modelSequence);
83
- });
67
+ // Update the entity with the computed field value. update is preferred to avoid the post update hooks getting triggered again and causing an infinite loop
68
+ await this.dataSource.manager.update(triggerEntity.constructor, triggerEntity.id, { [computedFieldMetadata.fieldName]: sequenceString });
84
69
  }
85
70
 
86
71
  }