av6-core 1.2.0 → 1.2.2

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/dist/index.d.mts CHANGED
@@ -1,11 +1,11 @@
1
- import * as PrismaNamespace from '@prisma/client';
2
- import { PrismaClient } from '@prisma/client';
3
1
  import winston from 'winston';
4
2
  import { AsyncLocalStorage } from 'async_hooks';
5
3
  import { Readable } from 'stream';
6
4
  import { JsonValue } from '@prisma/client/runtime/library';
7
5
  import ExcelJs from 'exceljs';
8
6
  import { AxiosResponse } from 'axios';
7
+ import * as PrismaNamespace from '@prisma/client';
8
+ import { PrismaClient, Prisma } from '@prisma/client';
9
9
 
10
10
  declare enum ErrorMessageType {
11
11
  INVALID_ID = "Invalid id: %1 Numeric value expected.",
@@ -29,6 +29,8 @@ declare enum ErrorMessageType {
29
29
  EXCEL = "No data found to be downloaded"
30
30
  }
31
31
 
32
+ type TxClient = PrismaClient | Prisma.TransactionClient;
33
+
32
34
  interface EmployeeCache {
33
35
  id: number;
34
36
  name: string;
@@ -102,6 +104,7 @@ type DynamicShortCode = {
102
104
  permission?: string | null;
103
105
  whereClause?: JsonValue | null;
104
106
  selectClause?: JsonValue | null;
107
+ config?: unknown | null;
105
108
  };
106
109
  interface ExportExcel {
107
110
  shortCode: string;
@@ -192,20 +195,13 @@ interface LockUnlockParams {
192
195
  interface LockUnlockRequestRepository<T extends DynamicShortCode> extends LockUnlockParams {
193
196
  shortCodeData: T;
194
197
  }
195
- interface CommonCreateParams {
196
- shortCode: string;
197
- value: string;
198
- description?: string;
198
+ interface CommonCreateRequestRepository {
199
+ shortCodeData: DynamicShortCode;
200
+ body: Record<string, unknown>;
199
201
  }
200
- interface CommonCreateRequestRepository<T extends DynamicShortCode> extends CommonCreateParams {
201
- shortCodeData: T;
202
- }
203
- interface CommonUpdateParams extends CommonCreateParams {
202
+ interface CommonUpdateRequestRepository extends CommonCreateRequestRepository {
204
203
  id: number;
205
204
  }
206
- interface CommonUpdateRequestRepository<T extends DynamicShortCode> extends CommonUpdateParams {
207
- shortCodeData: T;
208
- }
209
205
  interface PaginatedResponse<T> {
210
206
  totalRecords: number;
211
207
  currentPageNumber: number;
@@ -234,6 +230,7 @@ interface CacheAdapter {
234
230
  updateCache(key: string, id: number | string, value: unknown): Promise<void>;
235
231
  createCache(table: string, data: DataType[]): Promise<void>;
236
232
  deleteCache(key: string, id: number | string): Promise<void>;
233
+ addToCache(key: string, id: number | string, data: unknown): Promise<void>;
237
234
  }
238
235
  interface Mapper {
239
236
  dtoMapping: Record<string, DtoMappingFunction>;
@@ -276,7 +273,7 @@ interface Deps {
276
273
  logger: winston.Logger;
277
274
  cacheAdapter: CacheAdapter;
278
275
  requestStorage: AsyncLocalStorage<Store>;
279
- db: PrismaClient;
276
+ db: TxClient;
280
277
  }
281
278
  interface CommonServiceResponse {
282
279
  search: (searchParams: SearchRequestService<DynamicShortCode>) => Promise<unknown>;
@@ -290,8 +287,8 @@ interface CommonServiceResponse {
290
287
  delete: (deleteParams: DeleteRequestRepository<DynamicShortCode>) => Promise<void>;
291
288
  updateStatus: (updateStatusParams: UpdateStatusRequestRepository<DynamicShortCode>) => Promise<unknown>;
292
289
  lockUnlock: (lockUnlockParams: LockUnlockRequestRepository<DynamicShortCode>) => Promise<unknown>;
293
- create: (createParams: CommonCreateRequestRepository<DynamicShortCode>) => Promise<unknown>;
294
- update: (updateParams: CommonUpdateRequestRepository<DynamicShortCode>) => Promise<unknown>;
290
+ create: (createParams: CommonCreateRequestRepository) => Promise<unknown>;
291
+ update: (updateParams: CommonUpdateRequestRepository) => Promise<unknown>;
295
292
  fetchImageStream: (imageBaseUrl: string, fileName: string) => Promise<AxiosResponse<Readable>>;
296
293
  }
297
294
 
@@ -434,14 +431,16 @@ declare class NotificationEmitter {
434
431
  interface AuditContext {
435
432
  userId: number | null;
436
433
  traceId: string | null;
437
- ccId: number | null;
434
+ level1Id: number | null;
435
+ level2Id: number | null;
438
436
  module: "OPD" | "PROCEDURE" | "GENERAL_BILL" | "PHARMACY" | "LAB" | "IPD";
439
437
  }
440
438
  type AuditContextProvider = () => AuditContext;
441
439
  interface AuditLogPayload {
442
440
  message: string;
443
441
  type?: "INFO" | "CREATE" | "UPDATE" | "DELETE" | "ERROR";
444
- ccId?: number | null;
442
+ level1Id?: number | null;
443
+ level2Id?: number | null;
445
444
  extra?: Record<string, any>;
446
445
  }
447
446
 
@@ -472,4 +471,4 @@ declare class AuditProxy {
472
471
  createAuditedService<T extends object>(serviceName: string, service: T): T;
473
472
  }
474
473
 
475
- export { type AuditContext, type AuditContextProvider, AuditCore, type AuditLogPayload, AuditLogger, AuditProxy, type CacheAdapter, type CalculationRes, type ColValue, type CommonCreateParams, type CommonCreateRequestRepository, type CommonExcelRequest, type CommonFilterRequest, type CommonFilterWithDate, type CommonServiceResponse, type CommonUpdateParams, type CommonUpdateRequestRepository, type Config, type Context, type CreateUINConfigRequest, type DataType, type DeleteParams, type DeleteRequestRepository, type Deps, type DropdownRequest, type DropdownRequestService, type DynamicShortCode, type EmitPayload, type EmployeeCache, type ExcelConfig, type ExportExcel, type ExportExcelRequestService, type FetchRequest, type FetchRequestRepository, type FixedSearchRequest, type FixedSearchRequestService, type Helpers, type ImportExcel, type ImportExcelRequestService, type LockUnlockParams, type LockUnlockRequestRepository, type Mapper, type NewFixedSearchRequest, type NewFixedSearchRequestService, type NewSearchRequest, NotificationEmitter, type PaginatedResponse, type Recipient, type SearchRequest, type SearchRequestService, type ServiceCacheAdapter, type Store, type ToggleActive, type UINConfigDTO, type UINPreviewRequest, type UINSegment, type UINSegmentType, type UIN_RESET_POLICY, type UinDeps, type UpdateStatusRequestRepository, type UpdateUINConfigRequest, type ValidationErrorItem, commonService, customOmit, findDifferences, fromTimestampToSqlDatetime, getDynamicValue, getPattern, interpolate, objectTo2DArray, toNumberOrNull, toUINConfigDTO, uinConfigService, type updateStatusParams };
474
+ export { type AuditContext, type AuditContextProvider, AuditCore, type AuditLogPayload, AuditLogger, AuditProxy, type CacheAdapter, type CalculationRes, type ColValue, type CommonCreateRequestRepository, type CommonExcelRequest, type CommonFilterRequest, type CommonFilterWithDate, type CommonServiceResponse, type CommonUpdateRequestRepository, type Config, type Context, type CreateUINConfigRequest, type DataType, type DeleteParams, type DeleteRequestRepository, type Deps, type DropdownRequest, type DropdownRequestService, type DynamicShortCode, type EmitPayload, type EmployeeCache, type ExcelConfig, type ExportExcel, type ExportExcelRequestService, type FetchRequest, type FetchRequestRepository, type FixedSearchRequest, type FixedSearchRequestService, type Helpers, type ImportExcel, type ImportExcelRequestService, type LockUnlockParams, type LockUnlockRequestRepository, type Mapper, type NewFixedSearchRequest, type NewFixedSearchRequestService, type NewSearchRequest, NotificationEmitter, type PaginatedResponse, type Recipient, type SearchRequest, type SearchRequestService, type ServiceCacheAdapter, type Store, type ToggleActive, type UINConfigDTO, type UINPreviewRequest, type UINSegment, type UINSegmentType, type UIN_RESET_POLICY, type UinDeps, type UpdateStatusRequestRepository, type UpdateUINConfigRequest, type ValidationErrorItem, commonService, customOmit, findDifferences, fromTimestampToSqlDatetime, getDynamicValue, getPattern, interpolate, objectTo2DArray, toNumberOrNull, toUINConfigDTO, uinConfigService, type updateStatusParams };
package/dist/index.d.ts CHANGED
@@ -1,11 +1,11 @@
1
- import * as PrismaNamespace from '@prisma/client';
2
- import { PrismaClient } from '@prisma/client';
3
1
  import winston from 'winston';
4
2
  import { AsyncLocalStorage } from 'async_hooks';
5
3
  import { Readable } from 'stream';
6
4
  import { JsonValue } from '@prisma/client/runtime/library';
7
5
  import ExcelJs from 'exceljs';
8
6
  import { AxiosResponse } from 'axios';
7
+ import * as PrismaNamespace from '@prisma/client';
8
+ import { PrismaClient, Prisma } from '@prisma/client';
9
9
 
10
10
  declare enum ErrorMessageType {
11
11
  INVALID_ID = "Invalid id: %1 Numeric value expected.",
@@ -29,6 +29,8 @@ declare enum ErrorMessageType {
29
29
  EXCEL = "No data found to be downloaded"
30
30
  }
31
31
 
32
+ type TxClient = PrismaClient | Prisma.TransactionClient;
33
+
32
34
  interface EmployeeCache {
33
35
  id: number;
34
36
  name: string;
@@ -102,6 +104,7 @@ type DynamicShortCode = {
102
104
  permission?: string | null;
103
105
  whereClause?: JsonValue | null;
104
106
  selectClause?: JsonValue | null;
107
+ config?: unknown | null;
105
108
  };
106
109
  interface ExportExcel {
107
110
  shortCode: string;
@@ -192,20 +195,13 @@ interface LockUnlockParams {
192
195
  interface LockUnlockRequestRepository<T extends DynamicShortCode> extends LockUnlockParams {
193
196
  shortCodeData: T;
194
197
  }
195
- interface CommonCreateParams {
196
- shortCode: string;
197
- value: string;
198
- description?: string;
198
+ interface CommonCreateRequestRepository {
199
+ shortCodeData: DynamicShortCode;
200
+ body: Record<string, unknown>;
199
201
  }
200
- interface CommonCreateRequestRepository<T extends DynamicShortCode> extends CommonCreateParams {
201
- shortCodeData: T;
202
- }
203
- interface CommonUpdateParams extends CommonCreateParams {
202
+ interface CommonUpdateRequestRepository extends CommonCreateRequestRepository {
204
203
  id: number;
205
204
  }
206
- interface CommonUpdateRequestRepository<T extends DynamicShortCode> extends CommonUpdateParams {
207
- shortCodeData: T;
208
- }
209
205
  interface PaginatedResponse<T> {
210
206
  totalRecords: number;
211
207
  currentPageNumber: number;
@@ -234,6 +230,7 @@ interface CacheAdapter {
234
230
  updateCache(key: string, id: number | string, value: unknown): Promise<void>;
235
231
  createCache(table: string, data: DataType[]): Promise<void>;
236
232
  deleteCache(key: string, id: number | string): Promise<void>;
233
+ addToCache(key: string, id: number | string, data: unknown): Promise<void>;
237
234
  }
238
235
  interface Mapper {
239
236
  dtoMapping: Record<string, DtoMappingFunction>;
@@ -276,7 +273,7 @@ interface Deps {
276
273
  logger: winston.Logger;
277
274
  cacheAdapter: CacheAdapter;
278
275
  requestStorage: AsyncLocalStorage<Store>;
279
- db: PrismaClient;
276
+ db: TxClient;
280
277
  }
281
278
  interface CommonServiceResponse {
282
279
  search: (searchParams: SearchRequestService<DynamicShortCode>) => Promise<unknown>;
@@ -290,8 +287,8 @@ interface CommonServiceResponse {
290
287
  delete: (deleteParams: DeleteRequestRepository<DynamicShortCode>) => Promise<void>;
291
288
  updateStatus: (updateStatusParams: UpdateStatusRequestRepository<DynamicShortCode>) => Promise<unknown>;
292
289
  lockUnlock: (lockUnlockParams: LockUnlockRequestRepository<DynamicShortCode>) => Promise<unknown>;
293
- create: (createParams: CommonCreateRequestRepository<DynamicShortCode>) => Promise<unknown>;
294
- update: (updateParams: CommonUpdateRequestRepository<DynamicShortCode>) => Promise<unknown>;
290
+ create: (createParams: CommonCreateRequestRepository) => Promise<unknown>;
291
+ update: (updateParams: CommonUpdateRequestRepository) => Promise<unknown>;
295
292
  fetchImageStream: (imageBaseUrl: string, fileName: string) => Promise<AxiosResponse<Readable>>;
296
293
  }
297
294
 
@@ -434,14 +431,16 @@ declare class NotificationEmitter {
434
431
  interface AuditContext {
435
432
  userId: number | null;
436
433
  traceId: string | null;
437
- ccId: number | null;
434
+ level1Id: number | null;
435
+ level2Id: number | null;
438
436
  module: "OPD" | "PROCEDURE" | "GENERAL_BILL" | "PHARMACY" | "LAB" | "IPD";
439
437
  }
440
438
  type AuditContextProvider = () => AuditContext;
441
439
  interface AuditLogPayload {
442
440
  message: string;
443
441
  type?: "INFO" | "CREATE" | "UPDATE" | "DELETE" | "ERROR";
444
- ccId?: number | null;
442
+ level1Id?: number | null;
443
+ level2Id?: number | null;
445
444
  extra?: Record<string, any>;
446
445
  }
447
446
 
@@ -472,4 +471,4 @@ declare class AuditProxy {
472
471
  createAuditedService<T extends object>(serviceName: string, service: T): T;
473
472
  }
474
473
 
475
- export { type AuditContext, type AuditContextProvider, AuditCore, type AuditLogPayload, AuditLogger, AuditProxy, type CacheAdapter, type CalculationRes, type ColValue, type CommonCreateParams, type CommonCreateRequestRepository, type CommonExcelRequest, type CommonFilterRequest, type CommonFilterWithDate, type CommonServiceResponse, type CommonUpdateParams, type CommonUpdateRequestRepository, type Config, type Context, type CreateUINConfigRequest, type DataType, type DeleteParams, type DeleteRequestRepository, type Deps, type DropdownRequest, type DropdownRequestService, type DynamicShortCode, type EmitPayload, type EmployeeCache, type ExcelConfig, type ExportExcel, type ExportExcelRequestService, type FetchRequest, type FetchRequestRepository, type FixedSearchRequest, type FixedSearchRequestService, type Helpers, type ImportExcel, type ImportExcelRequestService, type LockUnlockParams, type LockUnlockRequestRepository, type Mapper, type NewFixedSearchRequest, type NewFixedSearchRequestService, type NewSearchRequest, NotificationEmitter, type PaginatedResponse, type Recipient, type SearchRequest, type SearchRequestService, type ServiceCacheAdapter, type Store, type ToggleActive, type UINConfigDTO, type UINPreviewRequest, type UINSegment, type UINSegmentType, type UIN_RESET_POLICY, type UinDeps, type UpdateStatusRequestRepository, type UpdateUINConfigRequest, type ValidationErrorItem, commonService, customOmit, findDifferences, fromTimestampToSqlDatetime, getDynamicValue, getPattern, interpolate, objectTo2DArray, toNumberOrNull, toUINConfigDTO, uinConfigService, type updateStatusParams };
474
+ export { type AuditContext, type AuditContextProvider, AuditCore, type AuditLogPayload, AuditLogger, AuditProxy, type CacheAdapter, type CalculationRes, type ColValue, type CommonCreateRequestRepository, type CommonExcelRequest, type CommonFilterRequest, type CommonFilterWithDate, type CommonServiceResponse, type CommonUpdateRequestRepository, type Config, type Context, type CreateUINConfigRequest, type DataType, type DeleteParams, type DeleteRequestRepository, type Deps, type DropdownRequest, type DropdownRequestService, type DynamicShortCode, type EmitPayload, type EmployeeCache, type ExcelConfig, type ExportExcel, type ExportExcelRequestService, type FetchRequest, type FetchRequestRepository, type FixedSearchRequest, type FixedSearchRequestService, type Helpers, type ImportExcel, type ImportExcelRequestService, type LockUnlockParams, type LockUnlockRequestRepository, type Mapper, type NewFixedSearchRequest, type NewFixedSearchRequestService, type NewSearchRequest, NotificationEmitter, type PaginatedResponse, type Recipient, type SearchRequest, type SearchRequestService, type ServiceCacheAdapter, type Store, type ToggleActive, type UINConfigDTO, type UINPreviewRequest, type UINSegment, type UINSegmentType, type UIN_RESET_POLICY, type UinDeps, type UpdateStatusRequestRepository, type UpdateUINConfigRequest, type ValidationErrorItem, commonService, customOmit, findDifferences, fromTimestampToSqlDatetime, getDynamicValue, getPattern, interpolate, objectTo2DArray, toNumberOrNull, toUINConfigDTO, uinConfigService, type updateStatusParams };
package/dist/index.js CHANGED
@@ -458,39 +458,6 @@ var commonRepository = (serviceDeps) => {
458
458
  logger.info("exiting::commonLock::repository");
459
459
  return updatedRecord;
460
460
  },
461
- async commonCreate({ shortCodeData, value, description }) {
462
- logger.info("entering::commonCreate::repository");
463
- const tableName = shortCodeData.tableName;
464
- const model = db[tableName];
465
- if (!model) {
466
- throw new ErrorHandler(400, generateErrorMessage("INVALID_TABLE"));
467
- }
468
- const results = model.create({
469
- data: {
470
- value,
471
- description
472
- }
473
- });
474
- logger.info("exiting::commonCreate::repository");
475
- return results;
476
- },
477
- async commonUpdate({ id, shortCodeData, value, description }) {
478
- logger.info("entering::commonUpdate::repository");
479
- const tableName = shortCodeData.tableName;
480
- const model = db[tableName];
481
- if (!model) {
482
- throw new ErrorHandler(400, generateErrorMessage("INVALID_TABLE"));
483
- }
484
- const results = model.update({
485
- where: { id },
486
- data: {
487
- value,
488
- description
489
- }
490
- });
491
- logger.info("exiting::commonUpdate::repository");
492
- return results;
493
- },
494
461
  async generateRootCondition({
495
462
  fixedSearch,
496
463
  fixedNotSearch
@@ -818,6 +785,382 @@ var commonRepository = (serviceDeps) => {
818
785
  };
819
786
  };
820
787
 
788
+ // src/utils/dynamicCreateUpdate.utils.ts
789
+ var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
790
+ var getByPath = (root, path) => {
791
+ if (!path) return void 0;
792
+ const parts = path.split(".");
793
+ let cur = root;
794
+ for (const p of parts) {
795
+ if (!isRecord(cur)) return void 0;
796
+ cur = cur[p];
797
+ }
798
+ return cur;
799
+ };
800
+ var omitUndefined = (obj) => {
801
+ const out = {};
802
+ for (const [k, v] of Object.entries(obj)) {
803
+ if (v !== void 0) out[k] = v;
804
+ }
805
+ return out;
806
+ };
807
+
808
+ // src/utils/dynamicPrismaBuilder.utils.ts
809
+ var resolveSrc = (f, env) => {
810
+ const src = f.src ?? `body.${f.name}`;
811
+ if (src.startsWith("body.")) return getByPath(env.body, src.slice(5));
812
+ if (src.startsWith("vars.")) return getByPath(env.ctx.vars ?? {}, src.slice(5));
813
+ if (src.startsWith("ctx.")) return getByPath(env.ctx, src.slice(4));
814
+ return void 0;
815
+ };
816
+ var setAudit = (cfg, op, out, ctx) => {
817
+ const createdByField = cfg.audit?.createdByField ?? "createdBy";
818
+ const updatedByField = cfg.audit?.updatedByField ?? "updatedBy";
819
+ if (op === "create" && ctx.userId != null) out[createdByField] = ctx.userId;
820
+ if (ctx.userId != null) out[updatedByField] = ctx.userId;
821
+ };
822
+ var buildScalarData = (cfg, op, env) => {
823
+ const out = {};
824
+ for (const f of cfg.fields) {
825
+ const dbKey = f.db ?? f.name;
826
+ const presence = f.presence[op];
827
+ if (presence === "forbidden") continue;
828
+ if (dbKey === cfg.primaryKey) continue;
829
+ const value = resolveSrc(f, env);
830
+ if (value === void 0) continue;
831
+ if (value === null && !f.allowNull) continue;
832
+ out[dbKey] = value;
833
+ }
834
+ setAudit(cfg, op, out, env.ctx);
835
+ return out;
836
+ };
837
+ var buildRelationWrite = (rel, op, env) => {
838
+ const raw = env.body[rel.name];
839
+ if (raw === void 0) return void 0;
840
+ const writeCfg = op === "create" ? rel.write.create : rel.write.update;
841
+ const itemPrimaryKey = writeCfg.itemPrimaryKey ?? "id";
842
+ const toItemData = (item, itemOp) => {
843
+ const itemBody = isRecord(item) ? item : {};
844
+ const itemOut = {};
845
+ for (const f of rel.items.fields) {
846
+ const dbKey = f.db ?? f.name;
847
+ const presence = f.presence[itemOp];
848
+ if (presence === "forbidden") continue;
849
+ if (dbKey === itemPrimaryKey) continue;
850
+ const v = itemBody[f.name];
851
+ if (v === void 0) continue;
852
+ if (v === null && !f.allowNull) continue;
853
+ itemOut[dbKey] = v;
854
+ }
855
+ if (rel.items.audit?.createdBy?.startsWith("ctx.") && env.ctx.userId != null) {
856
+ itemOut["createdBy"] = env.ctx.userId;
857
+ }
858
+ if (rel.items.audit?.updatedBy?.startsWith("ctx.") && env.ctx.userId != null) {
859
+ itemOut["updatedBy"] = env.ctx.userId;
860
+ }
861
+ return itemOut;
862
+ };
863
+ if (rel.kind === "one") {
864
+ const itemData = toItemData(raw, op);
865
+ if (op === "create") return { create: itemData };
866
+ if (writeCfg.strategy === "replace") return { delete: true, create: itemData };
867
+ if (writeCfg.strategy === "upsert")
868
+ return { upsert: { create: itemData, update: omitUndefined(itemData), where: {} } };
869
+ return { create: itemData };
870
+ }
871
+ const arr = Array.isArray(raw) ? raw : [];
872
+ const itemsCreate = arr.map((i) => toItemData(i, op));
873
+ if (op === "create") return { create: itemsCreate };
874
+ if (writeCfg.strategy === "replace") return { deleteMany: {}, create: itemsCreate };
875
+ if (writeCfg.strategy === "upsert") {
876
+ const incomingIds = [];
877
+ const upsertItems = [];
878
+ const createOnly = [];
879
+ for (const item of arr) {
880
+ const itemRec = isRecord(item) ? item : {};
881
+ const pk = itemRec[itemPrimaryKey];
882
+ const dataCreate = toItemData(itemRec, "create");
883
+ const dataUpdate = toItemData(itemRec, "update");
884
+ if (pk === void 0 || pk === null || pk === "") {
885
+ createOnly.push(dataCreate);
886
+ continue;
887
+ }
888
+ const pkNum = typeof pk === "string" ? Number(pk) : pk;
889
+ if (Number.isFinite(pkNum)) incomingIds.push(pkNum);
890
+ upsertItems.push({
891
+ where: { [itemPrimaryKey]: pkNum },
892
+ create: dataCreate,
893
+ update: omitUndefined(dataUpdate)
894
+ });
895
+ }
896
+ const payload = {};
897
+ if (upsertItems.length) payload.upsert = upsertItems;
898
+ if (createOnly.length) payload.create = createOnly;
899
+ const cfgAny = writeCfg;
900
+ const now = /* @__PURE__ */ new Date();
901
+ if (cfgAny.softDeleteMissing) {
902
+ const whereExtra = cfgAny.softDelete?.whereExtra ?? { isActive: true };
903
+ const data = {
904
+ ...cfgAny.softDelete?.data ?? { isActive: false }
905
+ };
906
+ if (data.deletedAt === "now") data.deletedAt = now;
907
+ if (data.deletedBy === "ctx.userId") data.deletedBy = env.ctx.userId ?? void 0;
908
+ if (incomingIds.length > 0) {
909
+ payload.updateMany = [
910
+ {
911
+ where: {
912
+ ...whereExtra,
913
+ createdAt: { lt: now },
914
+ [itemPrimaryKey]: { notIn: incomingIds }
915
+ },
916
+ data
917
+ }
918
+ ];
919
+ }
920
+ }
921
+ return payload;
922
+ }
923
+ return { create: itemsCreate };
924
+ };
925
+ var buildPrismaData = (cfg, op, validatedBody, ctx) => {
926
+ const env = { body: validatedBody, ctx };
927
+ const out = buildScalarData(cfg, op, env);
928
+ for (const rel of cfg.relations ?? []) {
929
+ const write = buildRelationWrite(rel, op, env);
930
+ if (write) out[rel.name] = write;
931
+ }
932
+ return out;
933
+ };
934
+
935
+ // src/repository/commonCreateUpdate.repository.ts
936
+ var commonCreateUpdateRepository = (serviceDeps) => {
937
+ const { mappingExport, mappingImport, dtoMapping } = serviceDeps.mapper;
938
+ const db = serviceDeps.db;
939
+ const { generateErrorMessage, ErrorHandler } = serviceDeps.helpers;
940
+ const logger = serviceDeps.logger;
941
+ const createCache = serviceDeps.cacheAdapter.createCache;
942
+ const requestStorage = serviceDeps.requestStorage;
943
+ return {
944
+ // async commonCreate({
945
+ // pageNo = 1,
946
+ // pageSize = 10,
947
+ // searchText,
948
+ // sortBy,
949
+ // sortDir,
950
+ // searchColumns,
951
+ // shortCodeData,
952
+ // includes,
953
+ // }: SearchRequestService<DynamicShortCode>) {
954
+ // logger.info("entering::commonSearch::repository");
955
+ // const tableName = shortCodeData.tableName;
956
+ // const whereClause: { OR?: Array<Record<string, unknown>> } = {};
957
+ // if (searchText && searchColumns.length > 0) {
958
+ // // Build an array of sub-objects
959
+ // whereClause.OR = searchColumns.map((column: string) => {
960
+ // if (column.includes(".")) {
961
+ // const keys = column.split(".");
962
+ // return buildNestedCondition(keys, searchText);
963
+ // }
964
+ // return { [column]: { contains: searchText } };
965
+ // });
966
+ // // Then transform them
967
+ // whereClause.OR = transformData(whereClause.OR);
968
+ // }
969
+ // const orderByClause: Record<string, "asc" | "desc"> = {};
970
+ // if (sortBy && sortDir) {
971
+ // orderByClause[sortBy] = sortDir.toLowerCase() as "asc" | "desc";
972
+ // }
973
+ // const skip = (pageNo - 1) * pageSize;
974
+ // const take = pageSize;
975
+ // const model = db[tableName];
976
+ // if (!model) {
977
+ // throw new ErrorHandler(400, "Invalid mapping table name");
978
+ // }
979
+ // const query: QueryType = {
980
+ // where: whereClause,
981
+ // orderBy: orderByClause,
982
+ // skip,
983
+ // take,
984
+ // };
985
+ // if (includes) {
986
+ // query.include = includes;
987
+ // }
988
+ // const results = await model.findMany(query);
989
+ // const totalRecords = await model.count({ where: whereClause });
990
+ // const totalPages = Math.ceil(totalRecords / pageSize);
991
+ // const DTOClass = dtoMapping[shortCodeData.shortCode];
992
+ // if (shortCodeData.isDTO && DTOClass) {
993
+ // let DTOs = [];
994
+ // if (shortCodeData.isSingleDto === undefined || shortCodeData.isSingleDto === true) {
995
+ // DTOs = await Promise.all(results.map((record: unknown) => DTOClass(record)));
996
+ // } else {
997
+ // DTOs = await DTOClass(results);
998
+ // }
999
+ // return {
1000
+ // data: DTOs,
1001
+ // totalRecords,
1002
+ // currentPageNumber: pageNo,
1003
+ // pageSize,
1004
+ // lastPageNumber: totalPages,
1005
+ // };
1006
+ // }
1007
+ // logger.info("exiting::commonSearch::repository");
1008
+ // return {
1009
+ // data: results,
1010
+ // totalRecords,
1011
+ // currentPageNumber: pageNo,
1012
+ // pageSize,
1013
+ // lastPageNumber: totalPages,
1014
+ // };
1015
+ // },
1016
+ getDelegate(tableName) {
1017
+ const d = db[tableName];
1018
+ if (!d || typeof d !== "object") throw new ErrorHandler(400, "Invalid mapping table name");
1019
+ const del = d;
1020
+ if (typeof del.create !== "function" || typeof del.update !== "function" || typeof del.findFirst !== "function") {
1021
+ throw new ErrorHandler(400, "Invalid prisma delegate for tableName");
1022
+ }
1023
+ return del;
1024
+ },
1025
+ resolveUniqueWhere(u, vars, body, ctx) {
1026
+ const out = {};
1027
+ for (const c of u.where) {
1028
+ if (c.src.startsWith("vars.")) out[c.field] = (vars ?? {})[c.src.slice(5)];
1029
+ else if (c.src.startsWith("body.")) out[c.field] = (body ?? {})[c.src.slice(5)];
1030
+ else if (c.src.startsWith("ctx.")) out[c.field] = ctx[c.src.slice(4)];
1031
+ }
1032
+ return omitUndefined(out);
1033
+ },
1034
+ async runUniqueChecks(delegate, cfg, op, pkField, pkValue, body, ctx) {
1035
+ for (const u of cfg.uniques ?? []) {
1036
+ if (!u.op.includes(op)) continue;
1037
+ const where = this.resolveUniqueWhere(u, ctx.vars ?? {}, body, ctx);
1038
+ if (op === "update" && pkValue != null) {
1039
+ where["NOT"] = { [pkField]: pkValue };
1040
+ }
1041
+ if (!Object.keys(where).length) continue;
1042
+ const query = { where, select: { [pkField]: true } };
1043
+ console.log(query);
1044
+ const existing = await delegate.findFirst({ where, select: { [pkField]: true } });
1045
+ if (existing) throw new ErrorHandler(400, u.message);
1046
+ }
1047
+ },
1048
+ async commonCreate({ body, ctx, shortCodeData }) {
1049
+ if (!shortCodeData.config) throw new ErrorHandler(400, "ShortCode config missing");
1050
+ const cfg = shortCodeData.config;
1051
+ const delegate = this.getDelegate(shortCodeData.tableName);
1052
+ await this.runUniqueChecks(delegate, cfg, "create", cfg.primaryKey, void 0, body, ctx);
1053
+ const data = buildPrismaData(cfg, "create", body, ctx);
1054
+ const include = cfg.response?.include;
1055
+ const select = cfg.response?.select ?? void 0;
1056
+ return delegate.create({ data, include, select });
1057
+ },
1058
+ async commonUpdate({ body, ctx, id, shortCodeData }) {
1059
+ if (!shortCodeData.config) throw new ErrorHandler(400, "ShortCode config missing");
1060
+ const cfg = shortCodeData.config;
1061
+ const pk = cfg.primaryKey;
1062
+ if (cfg.operations.update.requirePrimaryKey && (id === void 0 || id === null || isNaN(Number(id)))) {
1063
+ throw new ErrorHandler(400, "Missing id for update");
1064
+ }
1065
+ const delegate = this.getDelegate(shortCodeData.tableName);
1066
+ await this.runUniqueChecks(delegate, cfg, "update", pk, id, body, ctx);
1067
+ const data = buildPrismaData(cfg, "update", body, ctx);
1068
+ const include = cfg.response?.include;
1069
+ const select = cfg.response?.select ?? void 0;
1070
+ return delegate.update({
1071
+ where: { [pk]: id },
1072
+ data,
1073
+ include,
1074
+ select
1075
+ });
1076
+ }
1077
+ };
1078
+ };
1079
+
1080
+ // src/utils/dynamicJoiBuilder.utils.ts
1081
+ var import_joi = __toESM(require("joi"));
1082
+ var buildScalar = (f) => {
1083
+ const rules = f.rules ?? {};
1084
+ let s;
1085
+ switch (f.type) {
1086
+ case "string": {
1087
+ let j = import_joi.default.string();
1088
+ if (rules.trim) j = j.trim();
1089
+ if (typeof rules.min === "number") j = j.min(rules.min);
1090
+ if (typeof rules.max === "number") j = j.max(rules.max);
1091
+ if (rules.regex) j = j.pattern(new RegExp(rules.regex));
1092
+ s = j;
1093
+ break;
1094
+ }
1095
+ case "number": {
1096
+ let j = import_joi.default.number();
1097
+ if (rules.integer) j = j.integer();
1098
+ if (typeof rules.min === "number") j = j.min(rules.min);
1099
+ if (typeof rules.max === "number") j = j.max(rules.max);
1100
+ s = j;
1101
+ break;
1102
+ }
1103
+ case "boolean":
1104
+ s = import_joi.default.boolean();
1105
+ break;
1106
+ case "date": {
1107
+ let j = import_joi.default.date();
1108
+ if (rules.iso) j = j.iso();
1109
+ s = j;
1110
+ break;
1111
+ }
1112
+ case "enum": {
1113
+ const values = rules.values ?? [];
1114
+ s = import_joi.default.string().valid(...values);
1115
+ break;
1116
+ }
1117
+ case "object":
1118
+ s = import_joi.default.object(rules.schema ?? {});
1119
+ break;
1120
+ case "array":
1121
+ s = import_joi.default.array();
1122
+ break;
1123
+ case "json":
1124
+ default:
1125
+ s = import_joi.default.any();
1126
+ }
1127
+ if (f.allowNull) s = s.allow(null);
1128
+ if (f.default !== void 0) s = s.default(f.default);
1129
+ if (f.messages) s = s.messages(f.messages);
1130
+ return s;
1131
+ };
1132
+ var applyPresence = (schema, presence) => {
1133
+ if (presence === "required") return schema.required();
1134
+ if (presence === "forbidden") return schema.forbidden();
1135
+ return schema.optional();
1136
+ };
1137
+ var buildRelationSchema = (rel, op) => {
1138
+ const itemShape = {};
1139
+ for (const f of rel.items.fields) {
1140
+ const scalar = buildScalar(f);
1141
+ itemShape[f.name] = applyPresence(scalar, f.presence[op]);
1142
+ }
1143
+ const itemSchema = import_joi.default.object(itemShape);
1144
+ const base = rel.kind === "many" ? import_joi.default.array().items(itemSchema) : itemSchema;
1145
+ return applyPresence(base, rel.presence[op]);
1146
+ };
1147
+ var buildJoiSchemaForOp = (cfg, op) => {
1148
+ const shape = {};
1149
+ for (const f of cfg.fields) {
1150
+ const scalar = buildScalar(f);
1151
+ shape[f.name] = applyPresence(scalar, f.presence[op]);
1152
+ }
1153
+ for (const rel of cfg.relations ?? []) {
1154
+ shape[rel.name] = buildRelationSchema(rel, op);
1155
+ }
1156
+ return import_joi.default.object(shape).prefs({
1157
+ abortEarly: cfg.validation.abortEarly,
1158
+ allowUnknown: cfg.validation.allowUnknown,
1159
+ stripUnknown: cfg.validation.stripUnknown,
1160
+ convert: cfg.validation.convert
1161
+ });
1162
+ };
1163
+
821
1164
  // src/utils/helper.utils.ts
822
1165
  function customOmit(obj, keys) {
823
1166
  const rest = { ...obj };
@@ -944,11 +1287,13 @@ var import_exceljs = __toESM(require("exceljs"));
944
1287
  var commonService = (serviceDeps) => {
945
1288
  const generateErrorMessage = serviceDeps.helpers.generateErrorMessage;
946
1289
  const ErrorHandler = serviceDeps.helpers.ErrorHandler;
947
- const { deleteCache, getCacheById, updateCache, createCache } = serviceDeps.cacheAdapter;
1290
+ const { deleteCache, getCacheById, updateCache, createCache, addToCache } = serviceDeps.cacheAdapter;
948
1291
  const logger = serviceDeps.logger;
949
1292
  const dtoMapping = serviceDeps.mapper.dtoMapping;
950
1293
  const { REDIS_PREFIX, CACHE_KEY_NAME } = serviceDeps.config;
951
1294
  const commonRepositoryFactory = commonRepository(serviceDeps);
1295
+ const commonCreateUpdateRepositoryFactory = commonCreateUpdateRepository(serviceDeps);
1296
+ const requestStorage = serviceDeps.requestStorage;
952
1297
  return {
953
1298
  async search(searchParams) {
954
1299
  logger.info("entering::search::service");
@@ -1188,18 +1533,55 @@ var commonService = (serviceDeps) => {
1188
1533
  },
1189
1534
  async create(createParams) {
1190
1535
  logger.info("entering::create::service");
1536
+ const store = requestStorage.getStore();
1537
+ const currentUser = store?.user?.id ?? null;
1191
1538
  const shortCodeData = createParams.shortCodeData;
1192
- const createResult = await commonRepositoryFactory.commonCreate({ ...createParams, shortCodeData });
1539
+ const cfg = shortCodeData.config;
1540
+ const ctx = { userId: currentUser ?? void 0, vars: {} };
1541
+ const schema = buildJoiSchemaForOp(cfg, "create");
1542
+ const validated = await schema.validateAsync(createParams.body);
1543
+ const createResult = await commonCreateUpdateRepositoryFactory.commonCreate({
1544
+ body: validated,
1545
+ ctx,
1546
+ shortCodeData
1547
+ });
1193
1548
  if (shortCodeData.isCacheable) {
1194
- await createCache(`${REDIS_PREFIX}${CACHE_KEY_NAME}:${shortCodeData.tableName}:all`, createResult);
1549
+ await addToCache(
1550
+ `${REDIS_PREFIX}${CACHE_KEY_NAME}:${shortCodeData.tableName}:all`,
1551
+ createResult[cfg.primaryKey],
1552
+ createResult
1553
+ );
1554
+ }
1555
+ if (shortCodeData.isDTO && dtoMapping[shortCodeData.shortCode]) {
1556
+ let dtoResult;
1557
+ if (shortCodeData.isSingleDto === void 0 || shortCodeData.isSingleDto === true) {
1558
+ dtoResult = await dtoMapping[shortCodeData.shortCode](createResult);
1559
+ } else {
1560
+ dtoResult = (await dtoMapping[shortCodeData.shortCode]([createResult]))?.[0];
1561
+ }
1562
+ if (!dtoResult) {
1563
+ throw new ErrorHandler(404, generateErrorMessage("NOT_FOUND", `${shortCodeData.tableName}`));
1564
+ }
1565
+ return dtoResult;
1195
1566
  }
1196
1567
  logger.info("exiting::create::service");
1197
1568
  return createResult;
1198
1569
  },
1199
1570
  async update(updateParams) {
1200
1571
  logger.info("entering::update::service");
1572
+ const store = requestStorage.getStore();
1573
+ const currentUser = store?.user?.id ?? null;
1201
1574
  const shortCodeData = updateParams.shortCodeData;
1202
- const updateResult = await commonRepositoryFactory.commonUpdate({ ...updateParams, shortCodeData });
1575
+ const cfg = shortCodeData.config;
1576
+ const ctx = { userId: currentUser ?? void 0, vars: {} };
1577
+ const schema = buildJoiSchemaForOp(cfg, "update");
1578
+ const validated = await schema.validateAsync(updateParams.body);
1579
+ const updateResult = await commonCreateUpdateRepositoryFactory.commonUpdate({
1580
+ body: validated,
1581
+ ctx,
1582
+ shortCodeData,
1583
+ id: updateParams.id
1584
+ });
1203
1585
  if (shortCodeData.isCacheable) {
1204
1586
  await updateCache(
1205
1587
  `${REDIS_PREFIX}${CACHE_KEY_NAME}:${shortCodeData.tableName}:all`,
@@ -1207,6 +1589,18 @@ var commonService = (serviceDeps) => {
1207
1589
  updateResult
1208
1590
  );
1209
1591
  }
1592
+ if (shortCodeData.isDTO && dtoMapping[shortCodeData.shortCode]) {
1593
+ let dtoResult;
1594
+ if (shortCodeData.isSingleDto === void 0 || shortCodeData.isSingleDto === true) {
1595
+ dtoResult = await dtoMapping[shortCodeData.shortCode](updateResult);
1596
+ } else {
1597
+ dtoResult = (await dtoMapping[shortCodeData.shortCode]([updateResult]))?.[0];
1598
+ }
1599
+ if (!dtoResult) {
1600
+ throw new ErrorHandler(404, generateErrorMessage("NOT_FOUND", `${shortCodeData.tableName}`));
1601
+ }
1602
+ return dtoResult;
1603
+ }
1210
1604
  logger.info("exiting::update::service");
1211
1605
  return updateResult;
1212
1606
  },
@@ -2126,7 +2520,7 @@ var AuditLogger = class {
2126
2520
  async logIfEnabled(params) {
2127
2521
  const { service, method, payload } = params;
2128
2522
  const prisma = this.core.getPrisma();
2129
- const { userId, traceId, ccId: ctxCcId, module: module2 } = this.core.getContext();
2523
+ const { userId, traceId, level1Id: ctxLvl1Id, level2Id: ctxLvl2Id, module: module2 } = this.core.getContext();
2130
2524
  try {
2131
2525
  const config = await prisma.auditConfig.findFirst({
2132
2526
  where: {
@@ -2141,14 +2535,16 @@ var AuditLogger = class {
2141
2535
  }
2142
2536
  const message = payload.type === "ERROR" ? payload.message : config.message;
2143
2537
  const type = payload.type ?? "INFO";
2144
- const ccId = payload.ccId ?? ctxCcId ?? 0;
2538
+ const level1Id = payload.level1Id ?? ctxLvl1Id ?? 0;
2539
+ const level2Id = payload.level2Id ?? ctxLvl2Id ?? 0;
2145
2540
  await prisma.commonAudit.create({
2146
2541
  data: {
2147
2542
  service,
2148
2543
  module: module2,
2149
2544
  type,
2150
2545
  message,
2151
- ccId,
2546
+ level1Id,
2547
+ level2Id,
2152
2548
  methodName: method,
2153
2549
  traceId,
2154
2550
  createdBy: userId ?? void 0
package/dist/index.mjs CHANGED
@@ -408,39 +408,6 @@ var commonRepository = (serviceDeps) => {
408
408
  logger.info("exiting::commonLock::repository");
409
409
  return updatedRecord;
410
410
  },
411
- async commonCreate({ shortCodeData, value, description }) {
412
- logger.info("entering::commonCreate::repository");
413
- const tableName = shortCodeData.tableName;
414
- const model = db[tableName];
415
- if (!model) {
416
- throw new ErrorHandler(400, generateErrorMessage("INVALID_TABLE"));
417
- }
418
- const results = model.create({
419
- data: {
420
- value,
421
- description
422
- }
423
- });
424
- logger.info("exiting::commonCreate::repository");
425
- return results;
426
- },
427
- async commonUpdate({ id, shortCodeData, value, description }) {
428
- logger.info("entering::commonUpdate::repository");
429
- const tableName = shortCodeData.tableName;
430
- const model = db[tableName];
431
- if (!model) {
432
- throw new ErrorHandler(400, generateErrorMessage("INVALID_TABLE"));
433
- }
434
- const results = model.update({
435
- where: { id },
436
- data: {
437
- value,
438
- description
439
- }
440
- });
441
- logger.info("exiting::commonUpdate::repository");
442
- return results;
443
- },
444
411
  async generateRootCondition({
445
412
  fixedSearch,
446
413
  fixedNotSearch
@@ -768,6 +735,382 @@ var commonRepository = (serviceDeps) => {
768
735
  };
769
736
  };
770
737
 
738
+ // src/utils/dynamicCreateUpdate.utils.ts
739
+ var isRecord = (v) => typeof v === "object" && v !== null && !Array.isArray(v);
740
+ var getByPath = (root, path) => {
741
+ if (!path) return void 0;
742
+ const parts = path.split(".");
743
+ let cur = root;
744
+ for (const p of parts) {
745
+ if (!isRecord(cur)) return void 0;
746
+ cur = cur[p];
747
+ }
748
+ return cur;
749
+ };
750
+ var omitUndefined = (obj) => {
751
+ const out = {};
752
+ for (const [k, v] of Object.entries(obj)) {
753
+ if (v !== void 0) out[k] = v;
754
+ }
755
+ return out;
756
+ };
757
+
758
+ // src/utils/dynamicPrismaBuilder.utils.ts
759
+ var resolveSrc = (f, env) => {
760
+ const src = f.src ?? `body.${f.name}`;
761
+ if (src.startsWith("body.")) return getByPath(env.body, src.slice(5));
762
+ if (src.startsWith("vars.")) return getByPath(env.ctx.vars ?? {}, src.slice(5));
763
+ if (src.startsWith("ctx.")) return getByPath(env.ctx, src.slice(4));
764
+ return void 0;
765
+ };
766
+ var setAudit = (cfg, op, out, ctx) => {
767
+ const createdByField = cfg.audit?.createdByField ?? "createdBy";
768
+ const updatedByField = cfg.audit?.updatedByField ?? "updatedBy";
769
+ if (op === "create" && ctx.userId != null) out[createdByField] = ctx.userId;
770
+ if (ctx.userId != null) out[updatedByField] = ctx.userId;
771
+ };
772
+ var buildScalarData = (cfg, op, env) => {
773
+ const out = {};
774
+ for (const f of cfg.fields) {
775
+ const dbKey = f.db ?? f.name;
776
+ const presence = f.presence[op];
777
+ if (presence === "forbidden") continue;
778
+ if (dbKey === cfg.primaryKey) continue;
779
+ const value = resolveSrc(f, env);
780
+ if (value === void 0) continue;
781
+ if (value === null && !f.allowNull) continue;
782
+ out[dbKey] = value;
783
+ }
784
+ setAudit(cfg, op, out, env.ctx);
785
+ return out;
786
+ };
787
+ var buildRelationWrite = (rel, op, env) => {
788
+ const raw = env.body[rel.name];
789
+ if (raw === void 0) return void 0;
790
+ const writeCfg = op === "create" ? rel.write.create : rel.write.update;
791
+ const itemPrimaryKey = writeCfg.itemPrimaryKey ?? "id";
792
+ const toItemData = (item, itemOp) => {
793
+ const itemBody = isRecord(item) ? item : {};
794
+ const itemOut = {};
795
+ for (const f of rel.items.fields) {
796
+ const dbKey = f.db ?? f.name;
797
+ const presence = f.presence[itemOp];
798
+ if (presence === "forbidden") continue;
799
+ if (dbKey === itemPrimaryKey) continue;
800
+ const v = itemBody[f.name];
801
+ if (v === void 0) continue;
802
+ if (v === null && !f.allowNull) continue;
803
+ itemOut[dbKey] = v;
804
+ }
805
+ if (rel.items.audit?.createdBy?.startsWith("ctx.") && env.ctx.userId != null) {
806
+ itemOut["createdBy"] = env.ctx.userId;
807
+ }
808
+ if (rel.items.audit?.updatedBy?.startsWith("ctx.") && env.ctx.userId != null) {
809
+ itemOut["updatedBy"] = env.ctx.userId;
810
+ }
811
+ return itemOut;
812
+ };
813
+ if (rel.kind === "one") {
814
+ const itemData = toItemData(raw, op);
815
+ if (op === "create") return { create: itemData };
816
+ if (writeCfg.strategy === "replace") return { delete: true, create: itemData };
817
+ if (writeCfg.strategy === "upsert")
818
+ return { upsert: { create: itemData, update: omitUndefined(itemData), where: {} } };
819
+ return { create: itemData };
820
+ }
821
+ const arr = Array.isArray(raw) ? raw : [];
822
+ const itemsCreate = arr.map((i) => toItemData(i, op));
823
+ if (op === "create") return { create: itemsCreate };
824
+ if (writeCfg.strategy === "replace") return { deleteMany: {}, create: itemsCreate };
825
+ if (writeCfg.strategy === "upsert") {
826
+ const incomingIds = [];
827
+ const upsertItems = [];
828
+ const createOnly = [];
829
+ for (const item of arr) {
830
+ const itemRec = isRecord(item) ? item : {};
831
+ const pk = itemRec[itemPrimaryKey];
832
+ const dataCreate = toItemData(itemRec, "create");
833
+ const dataUpdate = toItemData(itemRec, "update");
834
+ if (pk === void 0 || pk === null || pk === "") {
835
+ createOnly.push(dataCreate);
836
+ continue;
837
+ }
838
+ const pkNum = typeof pk === "string" ? Number(pk) : pk;
839
+ if (Number.isFinite(pkNum)) incomingIds.push(pkNum);
840
+ upsertItems.push({
841
+ where: { [itemPrimaryKey]: pkNum },
842
+ create: dataCreate,
843
+ update: omitUndefined(dataUpdate)
844
+ });
845
+ }
846
+ const payload = {};
847
+ if (upsertItems.length) payload.upsert = upsertItems;
848
+ if (createOnly.length) payload.create = createOnly;
849
+ const cfgAny = writeCfg;
850
+ const now = /* @__PURE__ */ new Date();
851
+ if (cfgAny.softDeleteMissing) {
852
+ const whereExtra = cfgAny.softDelete?.whereExtra ?? { isActive: true };
853
+ const data = {
854
+ ...cfgAny.softDelete?.data ?? { isActive: false }
855
+ };
856
+ if (data.deletedAt === "now") data.deletedAt = now;
857
+ if (data.deletedBy === "ctx.userId") data.deletedBy = env.ctx.userId ?? void 0;
858
+ if (incomingIds.length > 0) {
859
+ payload.updateMany = [
860
+ {
861
+ where: {
862
+ ...whereExtra,
863
+ createdAt: { lt: now },
864
+ [itemPrimaryKey]: { notIn: incomingIds }
865
+ },
866
+ data
867
+ }
868
+ ];
869
+ }
870
+ }
871
+ return payload;
872
+ }
873
+ return { create: itemsCreate };
874
+ };
875
+ var buildPrismaData = (cfg, op, validatedBody, ctx) => {
876
+ const env = { body: validatedBody, ctx };
877
+ const out = buildScalarData(cfg, op, env);
878
+ for (const rel of cfg.relations ?? []) {
879
+ const write = buildRelationWrite(rel, op, env);
880
+ if (write) out[rel.name] = write;
881
+ }
882
+ return out;
883
+ };
884
+
885
+ // src/repository/commonCreateUpdate.repository.ts
886
+ var commonCreateUpdateRepository = (serviceDeps) => {
887
+ const { mappingExport, mappingImport, dtoMapping } = serviceDeps.mapper;
888
+ const db = serviceDeps.db;
889
+ const { generateErrorMessage, ErrorHandler } = serviceDeps.helpers;
890
+ const logger = serviceDeps.logger;
891
+ const createCache = serviceDeps.cacheAdapter.createCache;
892
+ const requestStorage = serviceDeps.requestStorage;
893
+ return {
894
+ // async commonCreate({
895
+ // pageNo = 1,
896
+ // pageSize = 10,
897
+ // searchText,
898
+ // sortBy,
899
+ // sortDir,
900
+ // searchColumns,
901
+ // shortCodeData,
902
+ // includes,
903
+ // }: SearchRequestService<DynamicShortCode>) {
904
+ // logger.info("entering::commonSearch::repository");
905
+ // const tableName = shortCodeData.tableName;
906
+ // const whereClause: { OR?: Array<Record<string, unknown>> } = {};
907
+ // if (searchText && searchColumns.length > 0) {
908
+ // // Build an array of sub-objects
909
+ // whereClause.OR = searchColumns.map((column: string) => {
910
+ // if (column.includes(".")) {
911
+ // const keys = column.split(".");
912
+ // return buildNestedCondition(keys, searchText);
913
+ // }
914
+ // return { [column]: { contains: searchText } };
915
+ // });
916
+ // // Then transform them
917
+ // whereClause.OR = transformData(whereClause.OR);
918
+ // }
919
+ // const orderByClause: Record<string, "asc" | "desc"> = {};
920
+ // if (sortBy && sortDir) {
921
+ // orderByClause[sortBy] = sortDir.toLowerCase() as "asc" | "desc";
922
+ // }
923
+ // const skip = (pageNo - 1) * pageSize;
924
+ // const take = pageSize;
925
+ // const model = db[tableName];
926
+ // if (!model) {
927
+ // throw new ErrorHandler(400, "Invalid mapping table name");
928
+ // }
929
+ // const query: QueryType = {
930
+ // where: whereClause,
931
+ // orderBy: orderByClause,
932
+ // skip,
933
+ // take,
934
+ // };
935
+ // if (includes) {
936
+ // query.include = includes;
937
+ // }
938
+ // const results = await model.findMany(query);
939
+ // const totalRecords = await model.count({ where: whereClause });
940
+ // const totalPages = Math.ceil(totalRecords / pageSize);
941
+ // const DTOClass = dtoMapping[shortCodeData.shortCode];
942
+ // if (shortCodeData.isDTO && DTOClass) {
943
+ // let DTOs = [];
944
+ // if (shortCodeData.isSingleDto === undefined || shortCodeData.isSingleDto === true) {
945
+ // DTOs = await Promise.all(results.map((record: unknown) => DTOClass(record)));
946
+ // } else {
947
+ // DTOs = await DTOClass(results);
948
+ // }
949
+ // return {
950
+ // data: DTOs,
951
+ // totalRecords,
952
+ // currentPageNumber: pageNo,
953
+ // pageSize,
954
+ // lastPageNumber: totalPages,
955
+ // };
956
+ // }
957
+ // logger.info("exiting::commonSearch::repository");
958
+ // return {
959
+ // data: results,
960
+ // totalRecords,
961
+ // currentPageNumber: pageNo,
962
+ // pageSize,
963
+ // lastPageNumber: totalPages,
964
+ // };
965
+ // },
966
+ getDelegate(tableName) {
967
+ const d = db[tableName];
968
+ if (!d || typeof d !== "object") throw new ErrorHandler(400, "Invalid mapping table name");
969
+ const del = d;
970
+ if (typeof del.create !== "function" || typeof del.update !== "function" || typeof del.findFirst !== "function") {
971
+ throw new ErrorHandler(400, "Invalid prisma delegate for tableName");
972
+ }
973
+ return del;
974
+ },
975
+ resolveUniqueWhere(u, vars, body, ctx) {
976
+ const out = {};
977
+ for (const c of u.where) {
978
+ if (c.src.startsWith("vars.")) out[c.field] = (vars ?? {})[c.src.slice(5)];
979
+ else if (c.src.startsWith("body.")) out[c.field] = (body ?? {})[c.src.slice(5)];
980
+ else if (c.src.startsWith("ctx.")) out[c.field] = ctx[c.src.slice(4)];
981
+ }
982
+ return omitUndefined(out);
983
+ },
984
+ async runUniqueChecks(delegate, cfg, op, pkField, pkValue, body, ctx) {
985
+ for (const u of cfg.uniques ?? []) {
986
+ if (!u.op.includes(op)) continue;
987
+ const where = this.resolveUniqueWhere(u, ctx.vars ?? {}, body, ctx);
988
+ if (op === "update" && pkValue != null) {
989
+ where["NOT"] = { [pkField]: pkValue };
990
+ }
991
+ if (!Object.keys(where).length) continue;
992
+ const query = { where, select: { [pkField]: true } };
993
+ console.log(query);
994
+ const existing = await delegate.findFirst({ where, select: { [pkField]: true } });
995
+ if (existing) throw new ErrorHandler(400, u.message);
996
+ }
997
+ },
998
+ async commonCreate({ body, ctx, shortCodeData }) {
999
+ if (!shortCodeData.config) throw new ErrorHandler(400, "ShortCode config missing");
1000
+ const cfg = shortCodeData.config;
1001
+ const delegate = this.getDelegate(shortCodeData.tableName);
1002
+ await this.runUniqueChecks(delegate, cfg, "create", cfg.primaryKey, void 0, body, ctx);
1003
+ const data = buildPrismaData(cfg, "create", body, ctx);
1004
+ const include = cfg.response?.include;
1005
+ const select = cfg.response?.select ?? void 0;
1006
+ return delegate.create({ data, include, select });
1007
+ },
1008
+ async commonUpdate({ body, ctx, id, shortCodeData }) {
1009
+ if (!shortCodeData.config) throw new ErrorHandler(400, "ShortCode config missing");
1010
+ const cfg = shortCodeData.config;
1011
+ const pk = cfg.primaryKey;
1012
+ if (cfg.operations.update.requirePrimaryKey && (id === void 0 || id === null || isNaN(Number(id)))) {
1013
+ throw new ErrorHandler(400, "Missing id for update");
1014
+ }
1015
+ const delegate = this.getDelegate(shortCodeData.tableName);
1016
+ await this.runUniqueChecks(delegate, cfg, "update", pk, id, body, ctx);
1017
+ const data = buildPrismaData(cfg, "update", body, ctx);
1018
+ const include = cfg.response?.include;
1019
+ const select = cfg.response?.select ?? void 0;
1020
+ return delegate.update({
1021
+ where: { [pk]: id },
1022
+ data,
1023
+ include,
1024
+ select
1025
+ });
1026
+ }
1027
+ };
1028
+ };
1029
+
1030
+ // src/utils/dynamicJoiBuilder.utils.ts
1031
+ import Joi from "joi";
1032
+ var buildScalar = (f) => {
1033
+ const rules = f.rules ?? {};
1034
+ let s;
1035
+ switch (f.type) {
1036
+ case "string": {
1037
+ let j = Joi.string();
1038
+ if (rules.trim) j = j.trim();
1039
+ if (typeof rules.min === "number") j = j.min(rules.min);
1040
+ if (typeof rules.max === "number") j = j.max(rules.max);
1041
+ if (rules.regex) j = j.pattern(new RegExp(rules.regex));
1042
+ s = j;
1043
+ break;
1044
+ }
1045
+ case "number": {
1046
+ let j = Joi.number();
1047
+ if (rules.integer) j = j.integer();
1048
+ if (typeof rules.min === "number") j = j.min(rules.min);
1049
+ if (typeof rules.max === "number") j = j.max(rules.max);
1050
+ s = j;
1051
+ break;
1052
+ }
1053
+ case "boolean":
1054
+ s = Joi.boolean();
1055
+ break;
1056
+ case "date": {
1057
+ let j = Joi.date();
1058
+ if (rules.iso) j = j.iso();
1059
+ s = j;
1060
+ break;
1061
+ }
1062
+ case "enum": {
1063
+ const values = rules.values ?? [];
1064
+ s = Joi.string().valid(...values);
1065
+ break;
1066
+ }
1067
+ case "object":
1068
+ s = Joi.object(rules.schema ?? {});
1069
+ break;
1070
+ case "array":
1071
+ s = Joi.array();
1072
+ break;
1073
+ case "json":
1074
+ default:
1075
+ s = Joi.any();
1076
+ }
1077
+ if (f.allowNull) s = s.allow(null);
1078
+ if (f.default !== void 0) s = s.default(f.default);
1079
+ if (f.messages) s = s.messages(f.messages);
1080
+ return s;
1081
+ };
1082
+ var applyPresence = (schema, presence) => {
1083
+ if (presence === "required") return schema.required();
1084
+ if (presence === "forbidden") return schema.forbidden();
1085
+ return schema.optional();
1086
+ };
1087
+ var buildRelationSchema = (rel, op) => {
1088
+ const itemShape = {};
1089
+ for (const f of rel.items.fields) {
1090
+ const scalar = buildScalar(f);
1091
+ itemShape[f.name] = applyPresence(scalar, f.presence[op]);
1092
+ }
1093
+ const itemSchema = Joi.object(itemShape);
1094
+ const base = rel.kind === "many" ? Joi.array().items(itemSchema) : itemSchema;
1095
+ return applyPresence(base, rel.presence[op]);
1096
+ };
1097
+ var buildJoiSchemaForOp = (cfg, op) => {
1098
+ const shape = {};
1099
+ for (const f of cfg.fields) {
1100
+ const scalar = buildScalar(f);
1101
+ shape[f.name] = applyPresence(scalar, f.presence[op]);
1102
+ }
1103
+ for (const rel of cfg.relations ?? []) {
1104
+ shape[rel.name] = buildRelationSchema(rel, op);
1105
+ }
1106
+ return Joi.object(shape).prefs({
1107
+ abortEarly: cfg.validation.abortEarly,
1108
+ allowUnknown: cfg.validation.allowUnknown,
1109
+ stripUnknown: cfg.validation.stripUnknown,
1110
+ convert: cfg.validation.convert
1111
+ });
1112
+ };
1113
+
771
1114
  // src/utils/helper.utils.ts
772
1115
  function customOmit(obj, keys) {
773
1116
  const rest = { ...obj };
@@ -894,11 +1237,13 @@ import ExcelJs from "exceljs";
894
1237
  var commonService = (serviceDeps) => {
895
1238
  const generateErrorMessage = serviceDeps.helpers.generateErrorMessage;
896
1239
  const ErrorHandler = serviceDeps.helpers.ErrorHandler;
897
- const { deleteCache, getCacheById, updateCache, createCache } = serviceDeps.cacheAdapter;
1240
+ const { deleteCache, getCacheById, updateCache, createCache, addToCache } = serviceDeps.cacheAdapter;
898
1241
  const logger = serviceDeps.logger;
899
1242
  const dtoMapping = serviceDeps.mapper.dtoMapping;
900
1243
  const { REDIS_PREFIX, CACHE_KEY_NAME } = serviceDeps.config;
901
1244
  const commonRepositoryFactory = commonRepository(serviceDeps);
1245
+ const commonCreateUpdateRepositoryFactory = commonCreateUpdateRepository(serviceDeps);
1246
+ const requestStorage = serviceDeps.requestStorage;
902
1247
  return {
903
1248
  async search(searchParams) {
904
1249
  logger.info("entering::search::service");
@@ -1138,18 +1483,55 @@ var commonService = (serviceDeps) => {
1138
1483
  },
1139
1484
  async create(createParams) {
1140
1485
  logger.info("entering::create::service");
1486
+ const store = requestStorage.getStore();
1487
+ const currentUser = store?.user?.id ?? null;
1141
1488
  const shortCodeData = createParams.shortCodeData;
1142
- const createResult = await commonRepositoryFactory.commonCreate({ ...createParams, shortCodeData });
1489
+ const cfg = shortCodeData.config;
1490
+ const ctx = { userId: currentUser ?? void 0, vars: {} };
1491
+ const schema = buildJoiSchemaForOp(cfg, "create");
1492
+ const validated = await schema.validateAsync(createParams.body);
1493
+ const createResult = await commonCreateUpdateRepositoryFactory.commonCreate({
1494
+ body: validated,
1495
+ ctx,
1496
+ shortCodeData
1497
+ });
1143
1498
  if (shortCodeData.isCacheable) {
1144
- await createCache(`${REDIS_PREFIX}${CACHE_KEY_NAME}:${shortCodeData.tableName}:all`, createResult);
1499
+ await addToCache(
1500
+ `${REDIS_PREFIX}${CACHE_KEY_NAME}:${shortCodeData.tableName}:all`,
1501
+ createResult[cfg.primaryKey],
1502
+ createResult
1503
+ );
1504
+ }
1505
+ if (shortCodeData.isDTO && dtoMapping[shortCodeData.shortCode]) {
1506
+ let dtoResult;
1507
+ if (shortCodeData.isSingleDto === void 0 || shortCodeData.isSingleDto === true) {
1508
+ dtoResult = await dtoMapping[shortCodeData.shortCode](createResult);
1509
+ } else {
1510
+ dtoResult = (await dtoMapping[shortCodeData.shortCode]([createResult]))?.[0];
1511
+ }
1512
+ if (!dtoResult) {
1513
+ throw new ErrorHandler(404, generateErrorMessage("NOT_FOUND", `${shortCodeData.tableName}`));
1514
+ }
1515
+ return dtoResult;
1145
1516
  }
1146
1517
  logger.info("exiting::create::service");
1147
1518
  return createResult;
1148
1519
  },
1149
1520
  async update(updateParams) {
1150
1521
  logger.info("entering::update::service");
1522
+ const store = requestStorage.getStore();
1523
+ const currentUser = store?.user?.id ?? null;
1151
1524
  const shortCodeData = updateParams.shortCodeData;
1152
- const updateResult = await commonRepositoryFactory.commonUpdate({ ...updateParams, shortCodeData });
1525
+ const cfg = shortCodeData.config;
1526
+ const ctx = { userId: currentUser ?? void 0, vars: {} };
1527
+ const schema = buildJoiSchemaForOp(cfg, "update");
1528
+ const validated = await schema.validateAsync(updateParams.body);
1529
+ const updateResult = await commonCreateUpdateRepositoryFactory.commonUpdate({
1530
+ body: validated,
1531
+ ctx,
1532
+ shortCodeData,
1533
+ id: updateParams.id
1534
+ });
1153
1535
  if (shortCodeData.isCacheable) {
1154
1536
  await updateCache(
1155
1537
  `${REDIS_PREFIX}${CACHE_KEY_NAME}:${shortCodeData.tableName}:all`,
@@ -1157,6 +1539,18 @@ var commonService = (serviceDeps) => {
1157
1539
  updateResult
1158
1540
  );
1159
1541
  }
1542
+ if (shortCodeData.isDTO && dtoMapping[shortCodeData.shortCode]) {
1543
+ let dtoResult;
1544
+ if (shortCodeData.isSingleDto === void 0 || shortCodeData.isSingleDto === true) {
1545
+ dtoResult = await dtoMapping[shortCodeData.shortCode](updateResult);
1546
+ } else {
1547
+ dtoResult = (await dtoMapping[shortCodeData.shortCode]([updateResult]))?.[0];
1548
+ }
1549
+ if (!dtoResult) {
1550
+ throw new ErrorHandler(404, generateErrorMessage("NOT_FOUND", `${shortCodeData.tableName}`));
1551
+ }
1552
+ return dtoResult;
1553
+ }
1160
1554
  logger.info("exiting::update::service");
1161
1555
  return updateResult;
1162
1556
  },
@@ -2076,7 +2470,7 @@ var AuditLogger = class {
2076
2470
  async logIfEnabled(params) {
2077
2471
  const { service, method, payload } = params;
2078
2472
  const prisma = this.core.getPrisma();
2079
- const { userId, traceId, ccId: ctxCcId, module } = this.core.getContext();
2473
+ const { userId, traceId, level1Id: ctxLvl1Id, level2Id: ctxLvl2Id, module } = this.core.getContext();
2080
2474
  try {
2081
2475
  const config = await prisma.auditConfig.findFirst({
2082
2476
  where: {
@@ -2091,14 +2485,16 @@ var AuditLogger = class {
2091
2485
  }
2092
2486
  const message = payload.type === "ERROR" ? payload.message : config.message;
2093
2487
  const type = payload.type ?? "INFO";
2094
- const ccId = payload.ccId ?? ctxCcId ?? 0;
2488
+ const level1Id = payload.level1Id ?? ctxLvl1Id ?? 0;
2489
+ const level2Id = payload.level2Id ?? ctxLvl2Id ?? 0;
2095
2490
  await prisma.commonAudit.create({
2096
2491
  data: {
2097
2492
  service,
2098
2493
  module,
2099
2494
  type,
2100
2495
  message,
2101
- ccId,
2496
+ level1Id,
2497
+ level2Id,
2102
2498
  methodName: method,
2103
2499
  traceId,
2104
2500
  createdBy: userId ?? void 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "av6-core",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -21,12 +21,13 @@
21
21
  },
22
22
  "dependencies": {
23
23
  "@prisma/client": "^6.19.0",
24
- "prisma": "^6.19.0",
25
24
  "axios": "^1.11.0",
26
25
  "exceljs": "^4.4.0",
27
26
  "handlebars": "^4.7.8",
27
+ "joi": "^17.13.3",
28
28
  "node-cron": "^4.2.1",
29
29
  "nodemailer": "^7.0.10",
30
- "prettier": "^3.6.2"
30
+ "prettier": "^3.6.2",
31
+ "prisma": "^6.19.0"
31
32
  }
32
33
  }