av6-core 1.3.11 → 1.4.1

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.
@@ -0,0 +1,100 @@
1
+ // #region agent log
2
+ fetch('http://127.0.0.1:7244/ingest/0a9f7972-5d03-4255-b5c9-6164f846fb46',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'debug-check-prisma.ts:1',message:'Starting Prisma client check',data:{timestamp:Date.now()},sessionId:'debug-session',runId:'run1',hypothesisId:'A'})}).catch(()=>{});
3
+ // #endregion
4
+
5
+ import * as fs from 'fs';
6
+ import * as path from 'path';
7
+
8
+ const workspaceRoot = path.resolve(__dirname, '..');
9
+ const nodeModulesPath = path.join(workspaceRoot, 'node_modules');
10
+ const prismaClientPath = path.join(nodeModulesPath, '@prisma', 'client');
11
+ const prismaGeneratedPath = path.join(nodeModulesPath, '.prisma', 'client');
12
+
13
+ // #region agent log
14
+ fetch('http://127.0.0.1:7244/ingest/0a9f7972-5d03-4255-b5c9-6164f846fb46',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'debug-check-prisma.ts:10',message:'Checking paths',data:{workspaceRoot,prismaClientPath,prismaGeneratedPath,prismaClientExists:fs.existsSync(prismaClientPath),prismaGeneratedExists:fs.existsSync(prismaGeneratedPath)},sessionId:'debug-session',runId:'run1',hypothesisId:'A'})}).catch(()=>{});
15
+ // #endregion
16
+
17
+ // Check if @prisma/client package exists
18
+ const prismaClientExists = fs.existsSync(prismaClientPath);
19
+ const prismaGeneratedExists = fs.existsSync(prismaGeneratedPath);
20
+
21
+ // #region agent log
22
+ fetch('http://127.0.0.1:7244/ingest/0a9f7972-5d03-4255-b5c9-6164f846fb46',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'debug-check-prisma.ts:18',message:'Path check results',data:{prismaClientExists,prismaGeneratedExists},sessionId:'debug-session',runId:'run1',hypothesisId:'A'})}).catch(()=>{});
23
+ // #endregion
24
+
25
+ // Try to read package.json from @prisma/client
26
+ let prismaClientPackageJson: any = null;
27
+ if (prismaClientExists) {
28
+ try {
29
+ const packageJsonPath = path.join(prismaClientPath, 'package.json');
30
+ // #region agent log
31
+ fetch('http://127.0.0.1:7244/ingest/0a9f7972-5d03-4255-b5c9-6164f846fb46',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'debug-check-prisma.ts:26',message:'Reading @prisma/client package.json',data:{packageJsonPath,exists:fs.existsSync(packageJsonPath)},sessionId:'debug-session',runId:'run1',hypothesisId:'B'})}).catch(()=>{});
32
+ // #endregion
33
+ if (fs.existsSync(packageJsonPath)) {
34
+ prismaClientPackageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
35
+ // #region agent log
36
+ fetch('http://127.0.0.1:7244/ingest/0a9f7972-5d03-4255-b5c9-6164f846fb46',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'debug-check-prisma.ts:30',message:'@prisma/client package.json content',data:{name:prismaClientPackageJson?.name,version:prismaClientPackageJson?.version,exports:prismaClientPackageJson?.exports},sessionId:'debug-session',runId:'run1',hypothesisId:'B'})}).catch(()=>{});
37
+ // #endregion
38
+ }
39
+ } catch (e) {
40
+ // #region agent log
41
+ fetch('http://127.0.0.1:7244/ingest/0a9f7972-5d03-4255-b5c9-6164f846fb46',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'debug-check-prisma.ts:34',message:'Error reading package.json',data:{error:String(e)},sessionId:'debug-session',runId:'run1',hypothesisId:'B'})}).catch(()=>{});
42
+ // #endregion
43
+ }
44
+ }
45
+
46
+ // Check index.d.ts in @prisma/client
47
+ let indexDtsContent: string | null = null;
48
+ if (prismaClientExists) {
49
+ try {
50
+ const indexDtsPath = path.join(prismaClientPath, 'index.d.ts');
51
+ // #region agent log
52
+ fetch('http://127.0.0.1:7244/ingest/0a9f7972-5d03-4255-b5c9-6164f846fb46',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'debug-check-prisma.ts:43',message:'Reading index.d.ts',data:{indexDtsPath,exists:fs.existsSync(indexDtsPath)},sessionId:'debug-session',runId:'run1',hypothesisId:'C'})}).catch(()=>{});
53
+ // #endregion
54
+ if (fs.existsSync(indexDtsPath)) {
55
+ indexDtsContent = fs.readFileSync(indexDtsPath, 'utf-8');
56
+ // #region agent log
57
+ fetch('http://127.0.0.1:7244/ingest/0a9f7972-5d03-4255-b5c9-6164f846fb46',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'debug-check-prisma.ts:47',message:'index.d.ts content',data:{content:indexDtsContent.substring(0,200)},sessionId:'debug-session',runId:'run1',hypothesisId:'C'})}).catch(()=>{});
58
+ // #endregion
59
+ }
60
+ } catch (e) {
61
+ // #region agent log
62
+ fetch('http://127.0.0.1:7244/ingest/0a9f7972-5d03-4255-b5c9-6164f846fb46',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'debug-check-prisma.ts:51',message:'Error reading index.d.ts',data:{error:String(e)},sessionId:'debug-session',runId:'run1',hypothesisId:'C'})}).catch(()=>{});
63
+ // #endregion
64
+ }
65
+ }
66
+
67
+ // Try to dynamically import PrismaClient
68
+ let prismaClientImportSuccess = false;
69
+ let prismaClientImportError: string | null = null;
70
+ try {
71
+ // #region agent log
72
+ fetch('http://127.0.0.1:7244/ingest/0a9f7972-5d03-4255-b5c9-6164f846fb46',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'debug-check-prisma.ts:60',message:'Attempting dynamic import of PrismaClient',data:{},sessionId:'debug-session',runId:'run1',hypothesisId:'D'})}).catch(()=>{});
73
+ // #endregion
74
+ const prismaModule = require('@prisma/client');
75
+ // #region agent log
76
+ fetch('http://127.0.0.1:7244/ingest/0a9f7972-5d03-4255-b5c9-6164f846fb46',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'debug-check-prisma.ts:63',message:'Module loaded',data:{hasPrismaClient:!!prismaModule.PrismaClient,exports:Object.keys(prismaModule)},sessionId:'debug-session',runId:'run1',hypothesisId:'D'})}).catch(()=>{});
77
+ // #endregion
78
+ if (prismaModule.PrismaClient) {
79
+ prismaClientImportSuccess = true;
80
+ } else {
81
+ prismaClientImportError = 'PrismaClient not found in exports: ' + Object.keys(prismaModule).join(', ');
82
+ }
83
+ } catch (e) {
84
+ prismaClientImportError = String(e);
85
+ // #region agent log
86
+ fetch('http://127.0.0.1:7244/ingest/0a9f7972-5d03-4255-b5c9-6164f846fb46',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'debug-check-prisma.ts:72',message:'Dynamic import failed',data:{error:prismaClientImportError},sessionId:'debug-session',runId:'run1',hypothesisId:'D'})}).catch(()=>{});
87
+ // #endregion
88
+ }
89
+
90
+ // #region agent log
91
+ fetch('http://127.0.0.1:7244/ingest/0a9f7972-5d03-4255-b5c9-6164f846fb46',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({location:'debug-check-prisma.ts:76',message:'Final summary',data:{prismaClientExists,prismaGeneratedExists,prismaClientImportSuccess,prismaClientImportError,hasPackageJson:!!prismaClientPackageJson,hasIndexDts:!!indexDtsContent},sessionId:'debug-session',runId:'run1',hypothesisId:'ALL'})}).catch(()=>{});
92
+ // #endregion
93
+
94
+ console.log('Prisma Client Check Complete');
95
+ console.log('Prisma Client Package Exists:', prismaClientExists);
96
+ console.log('Prisma Generated Client Exists:', prismaGeneratedExists);
97
+ console.log('PrismaClient Import Success:', prismaClientImportSuccess);
98
+ if (prismaClientImportError) {
99
+ console.log('PrismaClient Import Error:', prismaClientImportError);
100
+ }
package/dist/index.d.mts CHANGED
@@ -1,9 +1,9 @@
1
- import winston from 'winston';
2
- import { AsyncLocalStorage } from 'async_hooks';
3
- import { Readable } from 'stream';
4
1
  import { JsonValue } from '@prisma/client/runtime/library';
5
- import ExcelJs from 'exceljs';
2
+ import { AsyncLocalStorage } from 'async_hooks';
6
3
  import { AxiosResponse } from 'axios';
4
+ import ExcelJs from 'exceljs';
5
+ import { Readable } from 'stream';
6
+ import winston from 'winston';
7
7
  import * as PrismaNamespace from '@prisma/client';
8
8
  import { PrismaClient, Prisma } from '@prisma/client';
9
9
 
@@ -31,7 +31,7 @@ declare enum ErrorMessageType {
31
31
 
32
32
  type Presence = "required" | "optional" | "forbidden";
33
33
  type Op = "create" | "update";
34
- type FieldType = "string" | "number" | "boolean" | "date" | "enum" | "object" | "array" | "json";
34
+ type FieldType = "string" | "number" | "boolean" | "date" | "enum" | "object" | "array" | "json" | "file";
35
35
  type SourcePath = `body.${string}` | `vars.${string}` | `ctx.${string}`;
36
36
  interface FieldRules {
37
37
  min?: number;
@@ -46,6 +46,7 @@ interface FieldRules {
46
46
  precisionFrom?: "ctx.decimalPrecision";
47
47
  minDate?: "today" | "now";
48
48
  minDateFieldRef?: string;
49
+ fileKind?: "image" | "document";
49
50
  when?: {
50
51
  ref: string;
51
52
  is: string | number | boolean | (string | number | boolean)[];
@@ -72,6 +73,7 @@ interface FieldConfig {
72
73
  rules?: FieldRules;
73
74
  default?: unknown;
74
75
  allowNull?: boolean;
76
+ allowEmptyString?: boolean;
75
77
  messages?: Record<string, string>;
76
78
  }
77
79
  type RelationStrategy = "create" | "replace" | "upsert";
@@ -134,6 +136,7 @@ interface DynamicCrudConfig {
134
136
  include?: Record<string, unknown>;
135
137
  select?: Record<string, unknown> | null;
136
138
  };
139
+ bulk?: BulkConfig;
137
140
  }
138
141
  interface CrudContext {
139
142
  userId?: number;
@@ -156,6 +159,10 @@ type CrudDelegate = {
156
159
  where: Record<string, unknown>;
157
160
  select?: Record<string, boolean>;
158
161
  }) => Promise<unknown>;
162
+ findMany: (args: {
163
+ where: Record<string, unknown>;
164
+ select?: Record<string, boolean>;
165
+ }) => Promise<unknown>;
159
166
  };
160
167
  interface DynamicCreateInput {
161
168
  shortCodeData: DynamicShortCode;
@@ -189,6 +196,28 @@ type DtoFromMapping<M extends Record<string, string>, Row> = {
189
196
  type DtoNullOnMissing<M extends Record<string, string>, Row extends object> = {
190
197
  [K in keyof M]: Exclude<DtoFromMapping<M, Row>[K], undefined> | null;
191
198
  };
199
+ type BulkOnConflict = "error" | "update" | "skip";
200
+ interface BulkConflictConfig {
201
+ by: Array<{
202
+ field: string;
203
+ src?: SourcePath;
204
+ }>;
205
+ onConflict: BulkOnConflict;
206
+ updateFields?: string[];
207
+ allowRelationUpdate?: boolean;
208
+ }
209
+ interface BulkConfig {
210
+ arrayKey: string;
211
+ conflict: BulkConflictConfig;
212
+ }
213
+ type BulkAtomicResult = {
214
+ total: number;
215
+ created: number;
216
+ updated: number;
217
+ skipped: number;
218
+ createdRows: any[];
219
+ updatedRows: any[];
220
+ };
192
221
 
193
222
  interface EmployeeCache {
194
223
  id: number;
@@ -469,6 +498,7 @@ interface CommonServiceResponse {
469
498
  updateConfigByCode: (input: UpdateConfigByCodeInput) => Promise<void>;
470
499
  update: (updateParams: CommonUpdateRequestRepository) => Promise<unknown>;
471
500
  fetchImageStream: (imageBaseUrl: string, fileName: string) => Promise<AxiosResponse<Readable>>;
501
+ commonBulkUpsert: (bulkUpsertParams: DynamicCreateInput) => Promise<BulkAtomicResult>;
472
502
  }
473
503
 
474
504
  declare const commonService: (serviceDeps: Deps) => CommonServiceResponse;
@@ -650,4 +680,4 @@ declare class AuditProxy<Module extends string = "OPD" | "PROCEDURE" | "GENERAL_
650
680
  createAuditedService<T extends object>(serviceName: string, service: T): T;
651
681
  }
652
682
 
653
- 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 CrudContext, type CrudDelegate, type DataType, type DeepMerge, type DeleteParams, type DeleteRequestRepository, type Deps, type DropdownRequest, type DropdownRequestService, type DtoFromMapping, type DtoNullOnMissing, type DynamicCreateInput, type DynamicCrudConfig, type DynamicShortCode, type DynamicUpdateInput, type EmitPayload, type EmployeeCache, type ExcelConfig, type ExportExcel, type ExportExcelRequestService, type FetchRequest, type FetchRequestRepository, type FieldConfig, type FieldRules, type FieldType, type FixedSearchRequest, type FixedSearchRequestService, type Helpers, type ImportExcel, type ImportExcelRequestService, type LockUnlockParams, type LockUnlockRequestRepository, type Mapper, type MergeAll, type NewFixedSearchRequest, type NewFixedSearchRequestService, type NewSearchRequest, NotificationEmitter, type Op, type PaginatedResponse, type PathToSelectWithSelect, type PathValue, type PathsToSelectWithSelect, type Presence, type Recipient, type RelationConfig, type RelationStrategy, type RelationWriteConfig, type SearchRequest, type SearchRequestService, type ServiceCacheAdapter, type SingleValidationMapping, type SourcePath, type Store, type ToggleActive, type TxClient, type UINConfigDTO, type UINPreviewRequest, type UINSegment, type UINSegmentType, type UIN_RESET_POLICY, type UinDeps, type UnionToIntersection, type UniqueConfig, type UpdateConfigByCodeInput, type UpdateStatusRequestRepository, type UpdateUINConfigRequest, type ValidationErrorItem, commonService, customOmit, findDifferences, fromTimestampToSqlDatetime, getDynamicValue, getPattern, interpolate, objectTo2DArray, toNumberOrNull, toUINConfigDTO, uinConfigService, type updateStatusParams };
683
+ export { type AuditContext, type AuditContextProvider, AuditCore, type AuditLogPayload, AuditLogger, AuditProxy, type BulkAtomicResult, type BulkConfig, type BulkConflictConfig, type BulkOnConflict, 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 CrudContext, type CrudDelegate, type DataType, type DeepMerge, type DeleteParams, type DeleteRequestRepository, type Deps, type DropdownRequest, type DropdownRequestService, type DtoFromMapping, type DtoNullOnMissing, type DynamicCreateInput, type DynamicCrudConfig, type DynamicShortCode, type DynamicUpdateInput, type EmitPayload, type EmployeeCache, type ExcelConfig, type ExportExcel, type ExportExcelRequestService, type FetchRequest, type FetchRequestRepository, type FieldConfig, type FieldRules, type FieldType, type FixedSearchRequest, type FixedSearchRequestService, type Helpers, type ImportExcel, type ImportExcelRequestService, type LockUnlockParams, type LockUnlockRequestRepository, type Mapper, type MergeAll, type NewFixedSearchRequest, type NewFixedSearchRequestService, type NewSearchRequest, NotificationEmitter, type Op, type PaginatedResponse, type PathToSelectWithSelect, type PathValue, type PathsToSelectWithSelect, type Presence, type Recipient, type RelationConfig, type RelationStrategy, type RelationWriteConfig, type SearchRequest, type SearchRequestService, type ServiceCacheAdapter, type SingleValidationMapping, type SourcePath, type Store, type ToggleActive, type TxClient, type UINConfigDTO, type UINPreviewRequest, type UINSegment, type UINSegmentType, type UIN_RESET_POLICY, type UinDeps, type UnionToIntersection, type UniqueConfig, type UpdateConfigByCodeInput, 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,9 +1,9 @@
1
- import winston from 'winston';
2
- import { AsyncLocalStorage } from 'async_hooks';
3
- import { Readable } from 'stream';
4
1
  import { JsonValue } from '@prisma/client/runtime/library';
5
- import ExcelJs from 'exceljs';
2
+ import { AsyncLocalStorage } from 'async_hooks';
6
3
  import { AxiosResponse } from 'axios';
4
+ import ExcelJs from 'exceljs';
5
+ import { Readable } from 'stream';
6
+ import winston from 'winston';
7
7
  import * as PrismaNamespace from '@prisma/client';
8
8
  import { PrismaClient, Prisma } from '@prisma/client';
9
9
 
@@ -31,7 +31,7 @@ declare enum ErrorMessageType {
31
31
 
32
32
  type Presence = "required" | "optional" | "forbidden";
33
33
  type Op = "create" | "update";
34
- type FieldType = "string" | "number" | "boolean" | "date" | "enum" | "object" | "array" | "json";
34
+ type FieldType = "string" | "number" | "boolean" | "date" | "enum" | "object" | "array" | "json" | "file";
35
35
  type SourcePath = `body.${string}` | `vars.${string}` | `ctx.${string}`;
36
36
  interface FieldRules {
37
37
  min?: number;
@@ -46,6 +46,7 @@ interface FieldRules {
46
46
  precisionFrom?: "ctx.decimalPrecision";
47
47
  minDate?: "today" | "now";
48
48
  minDateFieldRef?: string;
49
+ fileKind?: "image" | "document";
49
50
  when?: {
50
51
  ref: string;
51
52
  is: string | number | boolean | (string | number | boolean)[];
@@ -72,6 +73,7 @@ interface FieldConfig {
72
73
  rules?: FieldRules;
73
74
  default?: unknown;
74
75
  allowNull?: boolean;
76
+ allowEmptyString?: boolean;
75
77
  messages?: Record<string, string>;
76
78
  }
77
79
  type RelationStrategy = "create" | "replace" | "upsert";
@@ -134,6 +136,7 @@ interface DynamicCrudConfig {
134
136
  include?: Record<string, unknown>;
135
137
  select?: Record<string, unknown> | null;
136
138
  };
139
+ bulk?: BulkConfig;
137
140
  }
138
141
  interface CrudContext {
139
142
  userId?: number;
@@ -156,6 +159,10 @@ type CrudDelegate = {
156
159
  where: Record<string, unknown>;
157
160
  select?: Record<string, boolean>;
158
161
  }) => Promise<unknown>;
162
+ findMany: (args: {
163
+ where: Record<string, unknown>;
164
+ select?: Record<string, boolean>;
165
+ }) => Promise<unknown>;
159
166
  };
160
167
  interface DynamicCreateInput {
161
168
  shortCodeData: DynamicShortCode;
@@ -189,6 +196,28 @@ type DtoFromMapping<M extends Record<string, string>, Row> = {
189
196
  type DtoNullOnMissing<M extends Record<string, string>, Row extends object> = {
190
197
  [K in keyof M]: Exclude<DtoFromMapping<M, Row>[K], undefined> | null;
191
198
  };
199
+ type BulkOnConflict = "error" | "update" | "skip";
200
+ interface BulkConflictConfig {
201
+ by: Array<{
202
+ field: string;
203
+ src?: SourcePath;
204
+ }>;
205
+ onConflict: BulkOnConflict;
206
+ updateFields?: string[];
207
+ allowRelationUpdate?: boolean;
208
+ }
209
+ interface BulkConfig {
210
+ arrayKey: string;
211
+ conflict: BulkConflictConfig;
212
+ }
213
+ type BulkAtomicResult = {
214
+ total: number;
215
+ created: number;
216
+ updated: number;
217
+ skipped: number;
218
+ createdRows: any[];
219
+ updatedRows: any[];
220
+ };
192
221
 
193
222
  interface EmployeeCache {
194
223
  id: number;
@@ -469,6 +498,7 @@ interface CommonServiceResponse {
469
498
  updateConfigByCode: (input: UpdateConfigByCodeInput) => Promise<void>;
470
499
  update: (updateParams: CommonUpdateRequestRepository) => Promise<unknown>;
471
500
  fetchImageStream: (imageBaseUrl: string, fileName: string) => Promise<AxiosResponse<Readable>>;
501
+ commonBulkUpsert: (bulkUpsertParams: DynamicCreateInput) => Promise<BulkAtomicResult>;
472
502
  }
473
503
 
474
504
  declare const commonService: (serviceDeps: Deps) => CommonServiceResponse;
@@ -650,4 +680,4 @@ declare class AuditProxy<Module extends string = "OPD" | "PROCEDURE" | "GENERAL_
650
680
  createAuditedService<T extends object>(serviceName: string, service: T): T;
651
681
  }
652
682
 
653
- 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 CrudContext, type CrudDelegate, type DataType, type DeepMerge, type DeleteParams, type DeleteRequestRepository, type Deps, type DropdownRequest, type DropdownRequestService, type DtoFromMapping, type DtoNullOnMissing, type DynamicCreateInput, type DynamicCrudConfig, type DynamicShortCode, type DynamicUpdateInput, type EmitPayload, type EmployeeCache, type ExcelConfig, type ExportExcel, type ExportExcelRequestService, type FetchRequest, type FetchRequestRepository, type FieldConfig, type FieldRules, type FieldType, type FixedSearchRequest, type FixedSearchRequestService, type Helpers, type ImportExcel, type ImportExcelRequestService, type LockUnlockParams, type LockUnlockRequestRepository, type Mapper, type MergeAll, type NewFixedSearchRequest, type NewFixedSearchRequestService, type NewSearchRequest, NotificationEmitter, type Op, type PaginatedResponse, type PathToSelectWithSelect, type PathValue, type PathsToSelectWithSelect, type Presence, type Recipient, type RelationConfig, type RelationStrategy, type RelationWriteConfig, type SearchRequest, type SearchRequestService, type ServiceCacheAdapter, type SingleValidationMapping, type SourcePath, type Store, type ToggleActive, type TxClient, type UINConfigDTO, type UINPreviewRequest, type UINSegment, type UINSegmentType, type UIN_RESET_POLICY, type UinDeps, type UnionToIntersection, type UniqueConfig, type UpdateConfigByCodeInput, type UpdateStatusRequestRepository, type UpdateUINConfigRequest, type ValidationErrorItem, commonService, customOmit, findDifferences, fromTimestampToSqlDatetime, getDynamicValue, getPattern, interpolate, objectTo2DArray, toNumberOrNull, toUINConfigDTO, uinConfigService, type updateStatusParams };
683
+ export { type AuditContext, type AuditContextProvider, AuditCore, type AuditLogPayload, AuditLogger, AuditProxy, type BulkAtomicResult, type BulkConfig, type BulkConflictConfig, type BulkOnConflict, 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 CrudContext, type CrudDelegate, type DataType, type DeepMerge, type DeleteParams, type DeleteRequestRepository, type Deps, type DropdownRequest, type DropdownRequestService, type DtoFromMapping, type DtoNullOnMissing, type DynamicCreateInput, type DynamicCrudConfig, type DynamicShortCode, type DynamicUpdateInput, type EmitPayload, type EmployeeCache, type ExcelConfig, type ExportExcel, type ExportExcelRequestService, type FetchRequest, type FetchRequestRepository, type FieldConfig, type FieldRules, type FieldType, type FixedSearchRequest, type FixedSearchRequestService, type Helpers, type ImportExcel, type ImportExcelRequestService, type LockUnlockParams, type LockUnlockRequestRepository, type Mapper, type MergeAll, type NewFixedSearchRequest, type NewFixedSearchRequestService, type NewSearchRequest, NotificationEmitter, type Op, type PaginatedResponse, type PathToSelectWithSelect, type PathValue, type PathsToSelectWithSelect, type Presence, type Recipient, type RelationConfig, type RelationStrategy, type RelationWriteConfig, type SearchRequest, type SearchRequestService, type ServiceCacheAdapter, type SingleValidationMapping, type SourcePath, type Store, type ToggleActive, type TxClient, type UINConfigDTO, type UINPreviewRequest, type UINSegment, type UINSegmentType, type UIN_RESET_POLICY, type UinDeps, type UnionToIntersection, type UniqueConfig, type UpdateConfigByCodeInput, type UpdateStatusRequestRepository, type UpdateUINConfigRequest, type ValidationErrorItem, commonService, customOmit, findDifferences, fromTimestampToSqlDatetime, getDynamicValue, getPattern, interpolate, objectTo2DArray, toNumberOrNull, toUINConfigDTO, uinConfigService, type updateStatusParams };
package/dist/index.js CHANGED
@@ -1050,6 +1050,96 @@ var commonCreateUpdateRepository = (serviceDeps) => {
1050
1050
  include,
1051
1051
  select
1052
1052
  });
1053
+ },
1054
+ resolveBulkWhere(by, row, ctx) {
1055
+ const out = {};
1056
+ for (const c of by) {
1057
+ const src = c.src ?? `body.${c.field}`;
1058
+ if (src.startsWith("body.")) out[c.field] = row[src.slice(5)];
1059
+ else if (src.startsWith("vars.")) out[c.field] = (ctx.vars ?? {})[src.slice(5)];
1060
+ else if (src.startsWith("ctx.")) out[c.field] = ctx[src.slice(4)];
1061
+ }
1062
+ return omitUndefined(out);
1063
+ },
1064
+ async commonBulkUpsertAtomic({ body, ctx, shortCodeData }) {
1065
+ const cfg = shortCodeData.config;
1066
+ const bulk = cfg.bulk;
1067
+ if (!bulk) throw new ErrorHandler(400, "Bulk config missing for this short code");
1068
+ const pk = cfg.primaryKey;
1069
+ const rows = body[bulk.arrayKey];
1070
+ const result = await db.$transaction(async (tx) => {
1071
+ const txModel = tx[shortCodeData.tableName];
1072
+ const include = cfg.response?.include;
1073
+ const select = cfg.response?.select ?? void 0;
1074
+ const conflictBy = bulk.conflict.by;
1075
+ const onConflict = bulk.conflict.onConflict;
1076
+ const makeKeyFromObj = (obj) => conflictBy.map((c) => String(obj[c.field] ?? "")).join("|");
1077
+ const updateFields = bulk.conflict.updateFields?.length ? new Set(bulk.conflict.updateFields) : null;
1078
+ const relationKeysToStrip = bulk.conflict.allowRelationUpdate === true || !cfg.relations?.length ? null : new Set(cfg.relations.map((r) => r.db));
1079
+ const wheres = rows.map((row, i) => {
1080
+ const where = this.resolveBulkWhere(conflictBy, row, ctx);
1081
+ if (!Object.keys(where).length) {
1082
+ throw new ErrorHandler(400, `Row ${i + 1}: Bulk conflict 'by' resolved to empty where`);
1083
+ }
1084
+ return where;
1085
+ });
1086
+ const existingRows = await txModel.findMany({
1087
+ where: { OR: wheres },
1088
+ select: { [pk]: true, ...Object.fromEntries(conflictBy.map((c) => [c.field, true])) }
1089
+ });
1090
+ const existingMap = /* @__PURE__ */ new Map();
1091
+ for (const ex of existingRows) {
1092
+ const key = makeKeyFromObj(ex);
1093
+ const id = ex[pk];
1094
+ if (typeof id === "number") existingMap.set(key, id);
1095
+ }
1096
+ let created = 0;
1097
+ let updated = 0;
1098
+ let skipped = 0;
1099
+ const createdRows = [];
1100
+ const updatedRows = [];
1101
+ for (let i = 0; i < rows.length; i++) {
1102
+ const row = rows[i];
1103
+ const key = makeKeyFromObj(wheres[i]);
1104
+ const existingId = existingMap.get(key);
1105
+ if (!existingId) {
1106
+ const data = buildPrismaData(cfg, "create", row, ctx);
1107
+ const createdRow = await txModel.create({ data, include, select });
1108
+ createdRows.push(createdRow);
1109
+ created++;
1110
+ continue;
1111
+ }
1112
+ if (onConflict === "error") {
1113
+ throw new ErrorHandler(400, `Row ${i + 1}: Record already exists`);
1114
+ }
1115
+ if (onConflict === "skip") {
1116
+ skipped++;
1117
+ continue;
1118
+ }
1119
+ let updateBody = row;
1120
+ if (updateFields) {
1121
+ const filtered = {};
1122
+ for (const k of updateFields) {
1123
+ if (row[k] !== void 0) filtered[k] = row[k];
1124
+ }
1125
+ updateBody = filtered;
1126
+ }
1127
+ if (relationKeysToStrip) {
1128
+ for (const k of relationKeysToStrip) delete updateBody[k];
1129
+ }
1130
+ const updateData = buildPrismaData(cfg, "update", updateBody, ctx);
1131
+ const updatedRow = await txModel.update({
1132
+ where: { [pk]: existingId },
1133
+ data: updateData,
1134
+ include,
1135
+ select
1136
+ });
1137
+ updatedRows.push(updatedRow);
1138
+ updated++;
1139
+ }
1140
+ return { total: rows.length, created, updated, skipped, createdRows, updatedRows };
1141
+ });
1142
+ return result;
1053
1143
  }
1054
1144
  };
1055
1145
  };
@@ -1057,6 +1147,129 @@ var commonCreateUpdateRepository = (serviceDeps) => {
1057
1147
  // src/utils/dynamicJoiBuilder.utils.ts
1058
1148
  var import_joi = __toESM(require("joi"));
1059
1149
  var import_dayjs = __toESM(require("dayjs"));
1150
+
1151
+ // src/utils/helper.utils.ts
1152
+ function customOmit(obj, keys) {
1153
+ const rest = { ...obj };
1154
+ const omitted = {};
1155
+ for (const key of keys) {
1156
+ if (key in obj) {
1157
+ omitted[key] = obj[key];
1158
+ delete rest[key];
1159
+ }
1160
+ }
1161
+ return { rest, omitted };
1162
+ }
1163
+ function getDynamicValue(obj, accessorKey) {
1164
+ if (!accessorKey || obj == null || typeof obj !== "object") {
1165
+ return null;
1166
+ }
1167
+ const keys = accessorKey.split(".");
1168
+ let result = obj;
1169
+ for (const key of keys) {
1170
+ if (result == null || typeof result !== "object" || !(key in result)) {
1171
+ return null;
1172
+ }
1173
+ result = result[key];
1174
+ }
1175
+ return result === void 0 ? null : result;
1176
+ }
1177
+ function objectTo2DArray(obj, maxCols) {
1178
+ if (maxCols < 2) {
1179
+ throw new Error("maxCols must be at least 2");
1180
+ }
1181
+ const rows = [];
1182
+ let currentRow = [];
1183
+ for (const [key, value] of Object.entries(obj)) {
1184
+ const pair = [key, value];
1185
+ if (currentRow.length + pair.length > maxCols) {
1186
+ if (currentRow.length > 0) {
1187
+ rows.push(currentRow);
1188
+ }
1189
+ currentRow = [];
1190
+ }
1191
+ currentRow.push(...pair);
1192
+ }
1193
+ if (currentRow.length > 0) {
1194
+ rows.push(currentRow);
1195
+ }
1196
+ return rows;
1197
+ }
1198
+ function toNumberOrNull(value) {
1199
+ if (typeof value === "number") {
1200
+ return value;
1201
+ }
1202
+ const converted = Number(value);
1203
+ return isNaN(converted) ? null : converted;
1204
+ }
1205
+ var getPattern = {
1206
+ stringBaseNum: /^[1-9][0-9]*$/,
1207
+ licenseTitle: /^(?=.{3,100}$)[A-Za-z0-9][A-Za-z0-9\s\-/&,.()]*$/,
1208
+ categoryName: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=[\]{};'":\\|,.<>/?]).{3,30}$/,
1209
+ namePattern: /^[A-Za-z\s]+$/,
1210
+ skuPattern: /^[A-Z0-9_-]+$/i,
1211
+ SlNoPattern: /^[A-Za-z0-9_-]+$/,
1212
+ nameWithNumPattern: /^[A-Za-z0-9\s]+$/,
1213
+ emailPattern: /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/,
1214
+ // phonePattern:
1215
+ // /^(?:\+?(\d{1,3})[\s.-]?)?(?:\(?(\d{3})\)?[\s.-]?)?(\d{3})[\s.-]?(\d{4})(?:\s*(?:x|ext)\s*(\d+))?$/,
1216
+ phonePattern: /^\d{9}$/,
1217
+ postalCodePattern: /^[0-9]{5}$/,
1218
+ alphanumericPattern: /^[a-zA-Z0-9]+$/,
1219
+ numericPattern: /^[0-9]+$/,
1220
+ datePattern: /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/,
1221
+ timePattern: /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])$/,
1222
+ uuidPattern: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
1223
+ passwordPattern: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/,
1224
+ jsonPattern: /^(\[.+?\]|\{.+?\})$/,
1225
+ ipPattern: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/,
1226
+ macAddressPattern: /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/,
1227
+ hexColorPattern: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/,
1228
+ hexPattern: /^[0-9A-Fa-f]+$/,
1229
+ binaryPattern: /^[01]+$/,
1230
+ base64Pattern: /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/,
1231
+ alphanumericDashPattern: /^[a-zA-Z0-9-]+$/,
1232
+ alphanumericDotPattern: /^[a-zA-Z0-9.]+$/,
1233
+ alphanumericUnderscorePattern: /^[a-zA-Z0-9_]+$/,
1234
+ alphanumericPlusPattern: /^[a-zA-Z0-9+]+$/,
1235
+ alphanumericSlashPattern: /^[a-zA-Z0-9/]+$/,
1236
+ alphanumericColonPattern: /^[a-zA-Z0-9:]+$/,
1237
+ alphanumericQuestionMarkPattern: /^[a-zA-Z0-9?]+$/,
1238
+ alphanumericAtPattern: /^[a-zA-Z0-9@]+$/,
1239
+ alphanumericHashPattern: /^[a-zA-Z0-9#]+$/,
1240
+ alphanumericDollarPattern: /^[a-zA-Z0-9$]+$/,
1241
+ alphanumericPercentPattern: /^[a-zA-Z0-9%]+$/,
1242
+ alphanumericAmpersandPattern: /^[a-zA-Z0-9&]+$/,
1243
+ alphanumericVerticalBarPattern: /^[a-zA-Z0-9|]+$/,
1244
+ alphanumericTildePattern: /^[a-zA-Z0-9~]+$/,
1245
+ alphanumericExclamationPattern: /^[a-zA-Z0-9!]+$/,
1246
+ alphanumericAndPattern: /^[a-zA-Z0-9&]+$/,
1247
+ alphanumericAsteriskPattern: /^[a-zA-Z0-9*]+$/,
1248
+ imagePattern: /^.*\.(jpe?g|png|gif)$/i,
1249
+ videoPattern: /\.(mp4|webm|ogg)$/i,
1250
+ audioPattern: /\.(mp3|wav)$/i,
1251
+ pdfPattern: /\.(pdf)$/i,
1252
+ docPattern: /\.(doc|docx)$/i,
1253
+ xlsPattern: /\.(xls|xlsx)$/i,
1254
+ pptPattern: /\.(ppt|pptx)$/i,
1255
+ zipPattern: /\.(zip)$/i,
1256
+ rarPattern: /\.(rar)$/i,
1257
+ tarPattern: /\.(tar)$/i,
1258
+ gzipPattern: /\.(gz|gzip)$/i,
1259
+ bz2Pattern: /\.(bz2)$/i,
1260
+ isoPattern: /\.(iso)$/i,
1261
+ txtPattern: /\.(txt)$/i,
1262
+ documentPattern: /\.(pdf|doc|docx|xls|xlsx|ppt|pptx|txt)$/i
1263
+ };
1264
+ var interpolate = (template, vars) => {
1265
+ return template.replace(/\{\{\s*([^}]+)\s*\}\}/g, (match, key) => {
1266
+ key = key.trim();
1267
+ return key in vars ? String(vars[key]) : "";
1268
+ });
1269
+ };
1270
+ var fromTimestampToSqlDatetime = (date) => date.replace("T", " ").replace("Z", "");
1271
+
1272
+ // src/utils/dynamicJoiBuilder.utils.ts
1060
1273
  function applyNumberCompare(schema, op, ref) {
1061
1274
  const r = import_joi.default.ref(ref);
1062
1275
  switch (op) {
@@ -1110,9 +1323,9 @@ var buildScalar = (f, ctx, op) => {
1110
1323
  if (typeof rules.min === "number") j = j.min(rules.min);
1111
1324
  if (typeof rules.max === "number") j = j.max(rules.max);
1112
1325
  const p = resolvePrecision(rules, ctx);
1113
- if (typeof p === "number") s = j.precision(p);
1326
+ if (typeof p === "number") j = j.precision(p);
1114
1327
  if (rules.compare?.ref && rules.compare?.op) {
1115
- s = applyNumberCompare(j, rules.compare.op, rules.compare.ref);
1328
+ j = applyNumberCompare(j, rules.compare.op, rules.compare.ref);
1116
1329
  }
1117
1330
  s = j;
1118
1331
  break;
@@ -1141,11 +1354,24 @@ var buildScalar = (f, ctx, op) => {
1141
1354
  case "array":
1142
1355
  s = import_joi.default.array();
1143
1356
  break;
1357
+ case "file": {
1358
+ let j = import_joi.default.string();
1359
+ if (rules.trim) j = j.trim();
1360
+ const kind = rules.fileKind ?? "image";
1361
+ if (kind === "image") {
1362
+ j = j.pattern(getPattern.imagePattern);
1363
+ } else {
1364
+ j = j.pattern(getPattern.documentPattern);
1365
+ }
1366
+ s = j;
1367
+ break;
1368
+ }
1144
1369
  case "json":
1145
1370
  default:
1146
1371
  s = import_joi.default.any();
1147
1372
  }
1148
1373
  if (f.allowNull) s = s.allow(null);
1374
+ if (f.allowEmptyString) s = s.allow("");
1149
1375
  if (f.default !== void 0) s = s.default(f.default);
1150
1376
  if (rules.when?.ref) {
1151
1377
  const isVal = Array.isArray(rules.when.is) ? rules.when.is : [rules.when.is];
@@ -1196,129 +1422,10 @@ var buildJoiSchemaForOp = (cfg, op, ctx) => {
1196
1422
  });
1197
1423
  };
1198
1424
 
1199
- // src/utils/helper.utils.ts
1200
- function customOmit(obj, keys) {
1201
- const rest = { ...obj };
1202
- const omitted = {};
1203
- for (const key of keys) {
1204
- if (key in obj) {
1205
- omitted[key] = obj[key];
1206
- delete rest[key];
1207
- }
1208
- }
1209
- return { rest, omitted };
1210
- }
1211
- function getDynamicValue(obj, accessorKey) {
1212
- if (!accessorKey || obj == null || typeof obj !== "object") {
1213
- return null;
1214
- }
1215
- const keys = accessorKey.split(".");
1216
- let result = obj;
1217
- for (const key of keys) {
1218
- if (result == null || typeof result !== "object" || !(key in result)) {
1219
- return null;
1220
- }
1221
- result = result[key];
1222
- }
1223
- return result === void 0 ? null : result;
1224
- }
1225
- function objectTo2DArray(obj, maxCols) {
1226
- if (maxCols < 2) {
1227
- throw new Error("maxCols must be at least 2");
1228
- }
1229
- const rows = [];
1230
- let currentRow = [];
1231
- for (const [key, value] of Object.entries(obj)) {
1232
- const pair = [key, value];
1233
- if (currentRow.length + pair.length > maxCols) {
1234
- if (currentRow.length > 0) {
1235
- rows.push(currentRow);
1236
- }
1237
- currentRow = [];
1238
- }
1239
- currentRow.push(...pair);
1240
- }
1241
- if (currentRow.length > 0) {
1242
- rows.push(currentRow);
1243
- }
1244
- return rows;
1245
- }
1246
- function toNumberOrNull(value) {
1247
- if (typeof value === "number") {
1248
- return value;
1249
- }
1250
- const converted = Number(value);
1251
- return isNaN(converted) ? null : converted;
1252
- }
1253
- var getPattern = {
1254
- stringBaseNum: /^[1-9][0-9]*$/,
1255
- licenseTitle: /^(?=.{3,100}$)[A-Za-z0-9][A-Za-z0-9\s\-/&,.()]*$/,
1256
- categoryName: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=[\]{};'":\\|,.<>/?]).{3,30}$/,
1257
- namePattern: /^[A-Za-z\s]+$/,
1258
- skuPattern: /^[A-Z0-9_-]+$/i,
1259
- SlNoPattern: /^[A-Za-z0-9_-]+$/,
1260
- nameWithNumPattern: /^[A-Za-z0-9\s]+$/,
1261
- emailPattern: /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/,
1262
- // phonePattern:
1263
- // /^(?:\+?(\d{1,3})[\s.-]?)?(?:\(?(\d{3})\)?[\s.-]?)?(\d{3})[\s.-]?(\d{4})(?:\s*(?:x|ext)\s*(\d+))?$/,
1264
- phonePattern: /^\d{9}$/,
1265
- postalCodePattern: /^[0-9]{5}$/,
1266
- alphanumericPattern: /^[a-zA-Z0-9]+$/,
1267
- numericPattern: /^[0-9]+$/,
1268
- datePattern: /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/,
1269
- timePattern: /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])$/,
1270
- uuidPattern: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
1271
- passwordPattern: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/,
1272
- jsonPattern: /^(\[.+?\]|\{.+?\})$/,
1273
- ipPattern: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/,
1274
- macAddressPattern: /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/,
1275
- hexColorPattern: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/,
1276
- hexPattern: /^[0-9A-Fa-f]+$/,
1277
- binaryPattern: /^[01]+$/,
1278
- base64Pattern: /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/,
1279
- alphanumericDashPattern: /^[a-zA-Z0-9-]+$/,
1280
- alphanumericDotPattern: /^[a-zA-Z0-9.]+$/,
1281
- alphanumericUnderscorePattern: /^[a-zA-Z0-9_]+$/,
1282
- alphanumericPlusPattern: /^[a-zA-Z0-9+]+$/,
1283
- alphanumericSlashPattern: /^[a-zA-Z0-9/]+$/,
1284
- alphanumericColonPattern: /^[a-zA-Z0-9:]+$/,
1285
- alphanumericQuestionMarkPattern: /^[a-zA-Z0-9?]+$/,
1286
- alphanumericAtPattern: /^[a-zA-Z0-9@]+$/,
1287
- alphanumericHashPattern: /^[a-zA-Z0-9#]+$/,
1288
- alphanumericDollarPattern: /^[a-zA-Z0-9$]+$/,
1289
- alphanumericPercentPattern: /^[a-zA-Z0-9%]+$/,
1290
- alphanumericAmpersandPattern: /^[a-zA-Z0-9&]+$/,
1291
- alphanumericVerticalBarPattern: /^[a-zA-Z0-9|]+$/,
1292
- alphanumericTildePattern: /^[a-zA-Z0-9~]+$/,
1293
- alphanumericExclamationPattern: /^[a-zA-Z0-9!]+$/,
1294
- alphanumericAndPattern: /^[a-zA-Z0-9&]+$/,
1295
- alphanumericAsteriskPattern: /^[a-zA-Z0-9*]+$/,
1296
- imagePattern: /^.*\.(jpe?g|png|gif)$/i,
1297
- videoPattern: /\.(mp4|webm|ogg)$/i,
1298
- audioPattern: /\.(mp3|wav)$/i,
1299
- pdfPattern: /\.(pdf)$/i,
1300
- docPattern: /\.(doc|docx)$/i,
1301
- xlsPattern: /\.(xls|xlsx)$/i,
1302
- pptPattern: /\.(ppt|pptx)$/i,
1303
- zipPattern: /\.(zip)$/i,
1304
- rarPattern: /\.(rar)$/i,
1305
- tarPattern: /\.(tar)$/i,
1306
- gzipPattern: /\.(gz|gzip)$/i,
1307
- bz2Pattern: /\.(bz2)$/i,
1308
- isoPattern: /\.(iso)$/i,
1309
- txtPattern: /\.(txt)$/i
1310
- };
1311
- var interpolate = (template, vars) => {
1312
- return template.replace(/\{\{\s*([^}]+)\s*\}\}/g, (match, key) => {
1313
- key = key.trim();
1314
- return key in vars ? String(vars[key]) : "";
1315
- });
1316
- };
1317
- var fromTimestampToSqlDatetime = (date) => date.replace("T", " ").replace("Z", "");
1318
-
1319
1425
  // src/services/common.service.ts
1320
1426
  var import_axios = __toESM(require("axios"));
1321
1427
  var import_exceljs = __toESM(require("exceljs"));
1428
+ var import_joi2 = __toESM(require("joi"));
1322
1429
  var commonService = (serviceDeps) => {
1323
1430
  const generateErrorMessage = serviceDeps.helpers.generateErrorMessage;
1324
1431
  const ErrorHandler = serviceDeps.helpers.ErrorHandler;
@@ -1664,6 +1771,67 @@ var commonService = (serviceDeps) => {
1664
1771
  logger.info("exiting::update::service");
1665
1772
  return updateResult;
1666
1773
  },
1774
+ async commonBulkUpsert(bulkUpsertParams) {
1775
+ logger.info("entering::commonBulkUpsert::service");
1776
+ const store = requestStorage.getStore();
1777
+ const currentUser = store?.user?.id ?? null;
1778
+ const shortCodeData = bulkUpsertParams.shortCodeData;
1779
+ const cfg = shortCodeData.config;
1780
+ if (!cfg) {
1781
+ throw new ErrorHandler(500, `Configuration not found for short code: ${shortCodeData.shortCode}`);
1782
+ }
1783
+ const bulk = cfg.bulk;
1784
+ if (!bulk) throw new ErrorHandler(400, "Bulk config missing for this short code");
1785
+ const ctx = {
1786
+ userId: currentUser ?? void 0,
1787
+ vars: {
1788
+ decimalPrecision: store?.defaultPrecision ?? 2
1789
+ }
1790
+ };
1791
+ const rowSchema = buildJoiSchemaForOp(cfg, "create", {
1792
+ decimalPrecision: ctx.vars?.decimalPrecision ?? 2
1793
+ });
1794
+ const bulkSchema = import_joi2.default.object({
1795
+ [bulk.arrayKey]: import_joi2.default.array().items(rowSchema).min(1).required().messages({
1796
+ "any.required": `"${bulk.arrayKey}" is required`,
1797
+ "array.base": `"${bulk.arrayKey}" must be an array`,
1798
+ "array.min": `"${bulk.arrayKey}" cannot be empty`
1799
+ })
1800
+ }).prefs({
1801
+ abortEarly: false,
1802
+ allowUnknown: cfg.validation.allowUnknown,
1803
+ stripUnknown: cfg.validation.stripUnknown,
1804
+ convert: cfg.validation.convert
1805
+ });
1806
+ const { value: validatedBulk, error } = bulkSchema.validate(bulkUpsertParams.body);
1807
+ if (error) {
1808
+ const messages = error.details.map((d) => d.message.replace(/['"]/g, "")).join(", ");
1809
+ throw new ErrorHandler(400, messages, error.details);
1810
+ }
1811
+ const commonData = await commonCreateUpdateRepositoryFactory.commonBulkUpsertAtomic({
1812
+ body: validatedBulk,
1813
+ shortCodeData,
1814
+ ctx
1815
+ });
1816
+ if (shortCodeData.isCacheable) {
1817
+ for (const row of commonData.createdRows) {
1818
+ await addToCache(
1819
+ `${REDIS_PREFIX}${MASTER_KEY_MODELS.includes(shortCodeData.tableName) ? MASTER_CACHE_KEY_NAME : CACHE_KEY_NAME}:${shortCodeData.tableName}:all`,
1820
+ row[cfg.primaryKey],
1821
+ row
1822
+ );
1823
+ }
1824
+ for (const row of commonData.updatedRows) {
1825
+ await updateCache(
1826
+ `${REDIS_PREFIX}${MASTER_KEY_MODELS.includes(shortCodeData.tableName) ? MASTER_CACHE_KEY_NAME : CACHE_KEY_NAME}:${shortCodeData.tableName}:all`,
1827
+ row.id,
1828
+ row
1829
+ );
1830
+ }
1831
+ }
1832
+ logger.info("exiting::commonBulkUpsert::service");
1833
+ return commonData;
1834
+ },
1667
1835
  async fetchImageStream(imageBaseUrl, fileName) {
1668
1836
  const url = imageBaseUrl + fileName;
1669
1837
  return import_axios.default.get(url, { responseType: "stream" });
package/dist/index.mjs CHANGED
@@ -1000,6 +1000,96 @@ var commonCreateUpdateRepository = (serviceDeps) => {
1000
1000
  include,
1001
1001
  select
1002
1002
  });
1003
+ },
1004
+ resolveBulkWhere(by, row, ctx) {
1005
+ const out = {};
1006
+ for (const c of by) {
1007
+ const src = c.src ?? `body.${c.field}`;
1008
+ if (src.startsWith("body.")) out[c.field] = row[src.slice(5)];
1009
+ else if (src.startsWith("vars.")) out[c.field] = (ctx.vars ?? {})[src.slice(5)];
1010
+ else if (src.startsWith("ctx.")) out[c.field] = ctx[src.slice(4)];
1011
+ }
1012
+ return omitUndefined(out);
1013
+ },
1014
+ async commonBulkUpsertAtomic({ body, ctx, shortCodeData }) {
1015
+ const cfg = shortCodeData.config;
1016
+ const bulk = cfg.bulk;
1017
+ if (!bulk) throw new ErrorHandler(400, "Bulk config missing for this short code");
1018
+ const pk = cfg.primaryKey;
1019
+ const rows = body[bulk.arrayKey];
1020
+ const result = await db.$transaction(async (tx) => {
1021
+ const txModel = tx[shortCodeData.tableName];
1022
+ const include = cfg.response?.include;
1023
+ const select = cfg.response?.select ?? void 0;
1024
+ const conflictBy = bulk.conflict.by;
1025
+ const onConflict = bulk.conflict.onConflict;
1026
+ const makeKeyFromObj = (obj) => conflictBy.map((c) => String(obj[c.field] ?? "")).join("|");
1027
+ const updateFields = bulk.conflict.updateFields?.length ? new Set(bulk.conflict.updateFields) : null;
1028
+ const relationKeysToStrip = bulk.conflict.allowRelationUpdate === true || !cfg.relations?.length ? null : new Set(cfg.relations.map((r) => r.db));
1029
+ const wheres = rows.map((row, i) => {
1030
+ const where = this.resolveBulkWhere(conflictBy, row, ctx);
1031
+ if (!Object.keys(where).length) {
1032
+ throw new ErrorHandler(400, `Row ${i + 1}: Bulk conflict 'by' resolved to empty where`);
1033
+ }
1034
+ return where;
1035
+ });
1036
+ const existingRows = await txModel.findMany({
1037
+ where: { OR: wheres },
1038
+ select: { [pk]: true, ...Object.fromEntries(conflictBy.map((c) => [c.field, true])) }
1039
+ });
1040
+ const existingMap = /* @__PURE__ */ new Map();
1041
+ for (const ex of existingRows) {
1042
+ const key = makeKeyFromObj(ex);
1043
+ const id = ex[pk];
1044
+ if (typeof id === "number") existingMap.set(key, id);
1045
+ }
1046
+ let created = 0;
1047
+ let updated = 0;
1048
+ let skipped = 0;
1049
+ const createdRows = [];
1050
+ const updatedRows = [];
1051
+ for (let i = 0; i < rows.length; i++) {
1052
+ const row = rows[i];
1053
+ const key = makeKeyFromObj(wheres[i]);
1054
+ const existingId = existingMap.get(key);
1055
+ if (!existingId) {
1056
+ const data = buildPrismaData(cfg, "create", row, ctx);
1057
+ const createdRow = await txModel.create({ data, include, select });
1058
+ createdRows.push(createdRow);
1059
+ created++;
1060
+ continue;
1061
+ }
1062
+ if (onConflict === "error") {
1063
+ throw new ErrorHandler(400, `Row ${i + 1}: Record already exists`);
1064
+ }
1065
+ if (onConflict === "skip") {
1066
+ skipped++;
1067
+ continue;
1068
+ }
1069
+ let updateBody = row;
1070
+ if (updateFields) {
1071
+ const filtered = {};
1072
+ for (const k of updateFields) {
1073
+ if (row[k] !== void 0) filtered[k] = row[k];
1074
+ }
1075
+ updateBody = filtered;
1076
+ }
1077
+ if (relationKeysToStrip) {
1078
+ for (const k of relationKeysToStrip) delete updateBody[k];
1079
+ }
1080
+ const updateData = buildPrismaData(cfg, "update", updateBody, ctx);
1081
+ const updatedRow = await txModel.update({
1082
+ where: { [pk]: existingId },
1083
+ data: updateData,
1084
+ include,
1085
+ select
1086
+ });
1087
+ updatedRows.push(updatedRow);
1088
+ updated++;
1089
+ }
1090
+ return { total: rows.length, created, updated, skipped, createdRows, updatedRows };
1091
+ });
1092
+ return result;
1003
1093
  }
1004
1094
  };
1005
1095
  };
@@ -1007,6 +1097,129 @@ var commonCreateUpdateRepository = (serviceDeps) => {
1007
1097
  // src/utils/dynamicJoiBuilder.utils.ts
1008
1098
  import Joi from "joi";
1009
1099
  import dayjs from "dayjs";
1100
+
1101
+ // src/utils/helper.utils.ts
1102
+ function customOmit(obj, keys) {
1103
+ const rest = { ...obj };
1104
+ const omitted = {};
1105
+ for (const key of keys) {
1106
+ if (key in obj) {
1107
+ omitted[key] = obj[key];
1108
+ delete rest[key];
1109
+ }
1110
+ }
1111
+ return { rest, omitted };
1112
+ }
1113
+ function getDynamicValue(obj, accessorKey) {
1114
+ if (!accessorKey || obj == null || typeof obj !== "object") {
1115
+ return null;
1116
+ }
1117
+ const keys = accessorKey.split(".");
1118
+ let result = obj;
1119
+ for (const key of keys) {
1120
+ if (result == null || typeof result !== "object" || !(key in result)) {
1121
+ return null;
1122
+ }
1123
+ result = result[key];
1124
+ }
1125
+ return result === void 0 ? null : result;
1126
+ }
1127
+ function objectTo2DArray(obj, maxCols) {
1128
+ if (maxCols < 2) {
1129
+ throw new Error("maxCols must be at least 2");
1130
+ }
1131
+ const rows = [];
1132
+ let currentRow = [];
1133
+ for (const [key, value] of Object.entries(obj)) {
1134
+ const pair = [key, value];
1135
+ if (currentRow.length + pair.length > maxCols) {
1136
+ if (currentRow.length > 0) {
1137
+ rows.push(currentRow);
1138
+ }
1139
+ currentRow = [];
1140
+ }
1141
+ currentRow.push(...pair);
1142
+ }
1143
+ if (currentRow.length > 0) {
1144
+ rows.push(currentRow);
1145
+ }
1146
+ return rows;
1147
+ }
1148
+ function toNumberOrNull(value) {
1149
+ if (typeof value === "number") {
1150
+ return value;
1151
+ }
1152
+ const converted = Number(value);
1153
+ return isNaN(converted) ? null : converted;
1154
+ }
1155
+ var getPattern = {
1156
+ stringBaseNum: /^[1-9][0-9]*$/,
1157
+ licenseTitle: /^(?=.{3,100}$)[A-Za-z0-9][A-Za-z0-9\s\-/&,.()]*$/,
1158
+ categoryName: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=[\]{};'":\\|,.<>/?]).{3,30}$/,
1159
+ namePattern: /^[A-Za-z\s]+$/,
1160
+ skuPattern: /^[A-Z0-9_-]+$/i,
1161
+ SlNoPattern: /^[A-Za-z0-9_-]+$/,
1162
+ nameWithNumPattern: /^[A-Za-z0-9\s]+$/,
1163
+ emailPattern: /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/,
1164
+ // phonePattern:
1165
+ // /^(?:\+?(\d{1,3})[\s.-]?)?(?:\(?(\d{3})\)?[\s.-]?)?(\d{3})[\s.-]?(\d{4})(?:\s*(?:x|ext)\s*(\d+))?$/,
1166
+ phonePattern: /^\d{9}$/,
1167
+ postalCodePattern: /^[0-9]{5}$/,
1168
+ alphanumericPattern: /^[a-zA-Z0-9]+$/,
1169
+ numericPattern: /^[0-9]+$/,
1170
+ datePattern: /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/,
1171
+ timePattern: /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])$/,
1172
+ uuidPattern: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
1173
+ passwordPattern: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/,
1174
+ jsonPattern: /^(\[.+?\]|\{.+?\})$/,
1175
+ ipPattern: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/,
1176
+ macAddressPattern: /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/,
1177
+ hexColorPattern: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/,
1178
+ hexPattern: /^[0-9A-Fa-f]+$/,
1179
+ binaryPattern: /^[01]+$/,
1180
+ base64Pattern: /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/,
1181
+ alphanumericDashPattern: /^[a-zA-Z0-9-]+$/,
1182
+ alphanumericDotPattern: /^[a-zA-Z0-9.]+$/,
1183
+ alphanumericUnderscorePattern: /^[a-zA-Z0-9_]+$/,
1184
+ alphanumericPlusPattern: /^[a-zA-Z0-9+]+$/,
1185
+ alphanumericSlashPattern: /^[a-zA-Z0-9/]+$/,
1186
+ alphanumericColonPattern: /^[a-zA-Z0-9:]+$/,
1187
+ alphanumericQuestionMarkPattern: /^[a-zA-Z0-9?]+$/,
1188
+ alphanumericAtPattern: /^[a-zA-Z0-9@]+$/,
1189
+ alphanumericHashPattern: /^[a-zA-Z0-9#]+$/,
1190
+ alphanumericDollarPattern: /^[a-zA-Z0-9$]+$/,
1191
+ alphanumericPercentPattern: /^[a-zA-Z0-9%]+$/,
1192
+ alphanumericAmpersandPattern: /^[a-zA-Z0-9&]+$/,
1193
+ alphanumericVerticalBarPattern: /^[a-zA-Z0-9|]+$/,
1194
+ alphanumericTildePattern: /^[a-zA-Z0-9~]+$/,
1195
+ alphanumericExclamationPattern: /^[a-zA-Z0-9!]+$/,
1196
+ alphanumericAndPattern: /^[a-zA-Z0-9&]+$/,
1197
+ alphanumericAsteriskPattern: /^[a-zA-Z0-9*]+$/,
1198
+ imagePattern: /^.*\.(jpe?g|png|gif)$/i,
1199
+ videoPattern: /\.(mp4|webm|ogg)$/i,
1200
+ audioPattern: /\.(mp3|wav)$/i,
1201
+ pdfPattern: /\.(pdf)$/i,
1202
+ docPattern: /\.(doc|docx)$/i,
1203
+ xlsPattern: /\.(xls|xlsx)$/i,
1204
+ pptPattern: /\.(ppt|pptx)$/i,
1205
+ zipPattern: /\.(zip)$/i,
1206
+ rarPattern: /\.(rar)$/i,
1207
+ tarPattern: /\.(tar)$/i,
1208
+ gzipPattern: /\.(gz|gzip)$/i,
1209
+ bz2Pattern: /\.(bz2)$/i,
1210
+ isoPattern: /\.(iso)$/i,
1211
+ txtPattern: /\.(txt)$/i,
1212
+ documentPattern: /\.(pdf|doc|docx|xls|xlsx|ppt|pptx|txt)$/i
1213
+ };
1214
+ var interpolate = (template, vars) => {
1215
+ return template.replace(/\{\{\s*([^}]+)\s*\}\}/g, (match, key) => {
1216
+ key = key.trim();
1217
+ return key in vars ? String(vars[key]) : "";
1218
+ });
1219
+ };
1220
+ var fromTimestampToSqlDatetime = (date) => date.replace("T", " ").replace("Z", "");
1221
+
1222
+ // src/utils/dynamicJoiBuilder.utils.ts
1010
1223
  function applyNumberCompare(schema, op, ref) {
1011
1224
  const r = Joi.ref(ref);
1012
1225
  switch (op) {
@@ -1060,9 +1273,9 @@ var buildScalar = (f, ctx, op) => {
1060
1273
  if (typeof rules.min === "number") j = j.min(rules.min);
1061
1274
  if (typeof rules.max === "number") j = j.max(rules.max);
1062
1275
  const p = resolvePrecision(rules, ctx);
1063
- if (typeof p === "number") s = j.precision(p);
1276
+ if (typeof p === "number") j = j.precision(p);
1064
1277
  if (rules.compare?.ref && rules.compare?.op) {
1065
- s = applyNumberCompare(j, rules.compare.op, rules.compare.ref);
1278
+ j = applyNumberCompare(j, rules.compare.op, rules.compare.ref);
1066
1279
  }
1067
1280
  s = j;
1068
1281
  break;
@@ -1091,11 +1304,24 @@ var buildScalar = (f, ctx, op) => {
1091
1304
  case "array":
1092
1305
  s = Joi.array();
1093
1306
  break;
1307
+ case "file": {
1308
+ let j = Joi.string();
1309
+ if (rules.trim) j = j.trim();
1310
+ const kind = rules.fileKind ?? "image";
1311
+ if (kind === "image") {
1312
+ j = j.pattern(getPattern.imagePattern);
1313
+ } else {
1314
+ j = j.pattern(getPattern.documentPattern);
1315
+ }
1316
+ s = j;
1317
+ break;
1318
+ }
1094
1319
  case "json":
1095
1320
  default:
1096
1321
  s = Joi.any();
1097
1322
  }
1098
1323
  if (f.allowNull) s = s.allow(null);
1324
+ if (f.allowEmptyString) s = s.allow("");
1099
1325
  if (f.default !== void 0) s = s.default(f.default);
1100
1326
  if (rules.when?.ref) {
1101
1327
  const isVal = Array.isArray(rules.when.is) ? rules.when.is : [rules.when.is];
@@ -1146,129 +1372,10 @@ var buildJoiSchemaForOp = (cfg, op, ctx) => {
1146
1372
  });
1147
1373
  };
1148
1374
 
1149
- // src/utils/helper.utils.ts
1150
- function customOmit(obj, keys) {
1151
- const rest = { ...obj };
1152
- const omitted = {};
1153
- for (const key of keys) {
1154
- if (key in obj) {
1155
- omitted[key] = obj[key];
1156
- delete rest[key];
1157
- }
1158
- }
1159
- return { rest, omitted };
1160
- }
1161
- function getDynamicValue(obj, accessorKey) {
1162
- if (!accessorKey || obj == null || typeof obj !== "object") {
1163
- return null;
1164
- }
1165
- const keys = accessorKey.split(".");
1166
- let result = obj;
1167
- for (const key of keys) {
1168
- if (result == null || typeof result !== "object" || !(key in result)) {
1169
- return null;
1170
- }
1171
- result = result[key];
1172
- }
1173
- return result === void 0 ? null : result;
1174
- }
1175
- function objectTo2DArray(obj, maxCols) {
1176
- if (maxCols < 2) {
1177
- throw new Error("maxCols must be at least 2");
1178
- }
1179
- const rows = [];
1180
- let currentRow = [];
1181
- for (const [key, value] of Object.entries(obj)) {
1182
- const pair = [key, value];
1183
- if (currentRow.length + pair.length > maxCols) {
1184
- if (currentRow.length > 0) {
1185
- rows.push(currentRow);
1186
- }
1187
- currentRow = [];
1188
- }
1189
- currentRow.push(...pair);
1190
- }
1191
- if (currentRow.length > 0) {
1192
- rows.push(currentRow);
1193
- }
1194
- return rows;
1195
- }
1196
- function toNumberOrNull(value) {
1197
- if (typeof value === "number") {
1198
- return value;
1199
- }
1200
- const converted = Number(value);
1201
- return isNaN(converted) ? null : converted;
1202
- }
1203
- var getPattern = {
1204
- stringBaseNum: /^[1-9][0-9]*$/,
1205
- licenseTitle: /^(?=.{3,100}$)[A-Za-z0-9][A-Za-z0-9\s\-/&,.()]*$/,
1206
- categoryName: /^(?=.*[A-Za-z])(?=.*\d)(?=.*[!@#$%^&*()_+\-=[\]{};'":\\|,.<>/?]).{3,30}$/,
1207
- namePattern: /^[A-Za-z\s]+$/,
1208
- skuPattern: /^[A-Z0-9_-]+$/i,
1209
- SlNoPattern: /^[A-Za-z0-9_-]+$/,
1210
- nameWithNumPattern: /^[A-Za-z0-9\s]+$/,
1211
- emailPattern: /^[\w-]+(\.[\w-]+)*@([\w-]+\.)+[a-zA-Z]{2,7}$/,
1212
- // phonePattern:
1213
- // /^(?:\+?(\d{1,3})[\s.-]?)?(?:\(?(\d{3})\)?[\s.-]?)?(\d{3})[\s.-]?(\d{4})(?:\s*(?:x|ext)\s*(\d+))?$/,
1214
- phonePattern: /^\d{9}$/,
1215
- postalCodePattern: /^[0-9]{5}$/,
1216
- alphanumericPattern: /^[a-zA-Z0-9]+$/,
1217
- numericPattern: /^[0-9]+$/,
1218
- datePattern: /^(0[1-9]|1[0-2])\/(0[1-9]|1\d|2\d|3[01])\/(19|20)\d{2}$/,
1219
- timePattern: /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])$/,
1220
- uuidPattern: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,
1221
- passwordPattern: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[^a-zA-Z0-9]).{8,}$/,
1222
- jsonPattern: /^(\[.+?\]|\{.+?\})$/,
1223
- ipPattern: /^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$/,
1224
- macAddressPattern: /^([0-9A-Fa-f]{2}[:-]){5}([0-9A-Fa-f]{2})$/,
1225
- hexColorPattern: /^#?([a-f0-9]{6}|[a-f0-9]{3})$/,
1226
- hexPattern: /^[0-9A-Fa-f]+$/,
1227
- binaryPattern: /^[01]+$/,
1228
- base64Pattern: /^([A-Za-z0-9+/]{4})*([A-Za-z0-9+/]{4}|[A-Za-z0-9+/]{3}=|[A-Za-z0-9+/]{2}==)$/,
1229
- alphanumericDashPattern: /^[a-zA-Z0-9-]+$/,
1230
- alphanumericDotPattern: /^[a-zA-Z0-9.]+$/,
1231
- alphanumericUnderscorePattern: /^[a-zA-Z0-9_]+$/,
1232
- alphanumericPlusPattern: /^[a-zA-Z0-9+]+$/,
1233
- alphanumericSlashPattern: /^[a-zA-Z0-9/]+$/,
1234
- alphanumericColonPattern: /^[a-zA-Z0-9:]+$/,
1235
- alphanumericQuestionMarkPattern: /^[a-zA-Z0-9?]+$/,
1236
- alphanumericAtPattern: /^[a-zA-Z0-9@]+$/,
1237
- alphanumericHashPattern: /^[a-zA-Z0-9#]+$/,
1238
- alphanumericDollarPattern: /^[a-zA-Z0-9$]+$/,
1239
- alphanumericPercentPattern: /^[a-zA-Z0-9%]+$/,
1240
- alphanumericAmpersandPattern: /^[a-zA-Z0-9&]+$/,
1241
- alphanumericVerticalBarPattern: /^[a-zA-Z0-9|]+$/,
1242
- alphanumericTildePattern: /^[a-zA-Z0-9~]+$/,
1243
- alphanumericExclamationPattern: /^[a-zA-Z0-9!]+$/,
1244
- alphanumericAndPattern: /^[a-zA-Z0-9&]+$/,
1245
- alphanumericAsteriskPattern: /^[a-zA-Z0-9*]+$/,
1246
- imagePattern: /^.*\.(jpe?g|png|gif)$/i,
1247
- videoPattern: /\.(mp4|webm|ogg)$/i,
1248
- audioPattern: /\.(mp3|wav)$/i,
1249
- pdfPattern: /\.(pdf)$/i,
1250
- docPattern: /\.(doc|docx)$/i,
1251
- xlsPattern: /\.(xls|xlsx)$/i,
1252
- pptPattern: /\.(ppt|pptx)$/i,
1253
- zipPattern: /\.(zip)$/i,
1254
- rarPattern: /\.(rar)$/i,
1255
- tarPattern: /\.(tar)$/i,
1256
- gzipPattern: /\.(gz|gzip)$/i,
1257
- bz2Pattern: /\.(bz2)$/i,
1258
- isoPattern: /\.(iso)$/i,
1259
- txtPattern: /\.(txt)$/i
1260
- };
1261
- var interpolate = (template, vars) => {
1262
- return template.replace(/\{\{\s*([^}]+)\s*\}\}/g, (match, key) => {
1263
- key = key.trim();
1264
- return key in vars ? String(vars[key]) : "";
1265
- });
1266
- };
1267
- var fromTimestampToSqlDatetime = (date) => date.replace("T", " ").replace("Z", "");
1268
-
1269
1375
  // src/services/common.service.ts
1270
1376
  import axios from "axios";
1271
1377
  import ExcelJs from "exceljs";
1378
+ import Joi2 from "joi";
1272
1379
  var commonService = (serviceDeps) => {
1273
1380
  const generateErrorMessage = serviceDeps.helpers.generateErrorMessage;
1274
1381
  const ErrorHandler = serviceDeps.helpers.ErrorHandler;
@@ -1614,6 +1721,67 @@ var commonService = (serviceDeps) => {
1614
1721
  logger.info("exiting::update::service");
1615
1722
  return updateResult;
1616
1723
  },
1724
+ async commonBulkUpsert(bulkUpsertParams) {
1725
+ logger.info("entering::commonBulkUpsert::service");
1726
+ const store = requestStorage.getStore();
1727
+ const currentUser = store?.user?.id ?? null;
1728
+ const shortCodeData = bulkUpsertParams.shortCodeData;
1729
+ const cfg = shortCodeData.config;
1730
+ if (!cfg) {
1731
+ throw new ErrorHandler(500, `Configuration not found for short code: ${shortCodeData.shortCode}`);
1732
+ }
1733
+ const bulk = cfg.bulk;
1734
+ if (!bulk) throw new ErrorHandler(400, "Bulk config missing for this short code");
1735
+ const ctx = {
1736
+ userId: currentUser ?? void 0,
1737
+ vars: {
1738
+ decimalPrecision: store?.defaultPrecision ?? 2
1739
+ }
1740
+ };
1741
+ const rowSchema = buildJoiSchemaForOp(cfg, "create", {
1742
+ decimalPrecision: ctx.vars?.decimalPrecision ?? 2
1743
+ });
1744
+ const bulkSchema = Joi2.object({
1745
+ [bulk.arrayKey]: Joi2.array().items(rowSchema).min(1).required().messages({
1746
+ "any.required": `"${bulk.arrayKey}" is required`,
1747
+ "array.base": `"${bulk.arrayKey}" must be an array`,
1748
+ "array.min": `"${bulk.arrayKey}" cannot be empty`
1749
+ })
1750
+ }).prefs({
1751
+ abortEarly: false,
1752
+ allowUnknown: cfg.validation.allowUnknown,
1753
+ stripUnknown: cfg.validation.stripUnknown,
1754
+ convert: cfg.validation.convert
1755
+ });
1756
+ const { value: validatedBulk, error } = bulkSchema.validate(bulkUpsertParams.body);
1757
+ if (error) {
1758
+ const messages = error.details.map((d) => d.message.replace(/['"]/g, "")).join(", ");
1759
+ throw new ErrorHandler(400, messages, error.details);
1760
+ }
1761
+ const commonData = await commonCreateUpdateRepositoryFactory.commonBulkUpsertAtomic({
1762
+ body: validatedBulk,
1763
+ shortCodeData,
1764
+ ctx
1765
+ });
1766
+ if (shortCodeData.isCacheable) {
1767
+ for (const row of commonData.createdRows) {
1768
+ await addToCache(
1769
+ `${REDIS_PREFIX}${MASTER_KEY_MODELS.includes(shortCodeData.tableName) ? MASTER_CACHE_KEY_NAME : CACHE_KEY_NAME}:${shortCodeData.tableName}:all`,
1770
+ row[cfg.primaryKey],
1771
+ row
1772
+ );
1773
+ }
1774
+ for (const row of commonData.updatedRows) {
1775
+ await updateCache(
1776
+ `${REDIS_PREFIX}${MASTER_KEY_MODELS.includes(shortCodeData.tableName) ? MASTER_CACHE_KEY_NAME : CACHE_KEY_NAME}:${shortCodeData.tableName}:all`,
1777
+ row.id,
1778
+ row
1779
+ );
1780
+ }
1781
+ }
1782
+ logger.info("exiting::commonBulkUpsert::service");
1783
+ return commonData;
1784
+ },
1617
1785
  async fetchImageStream(imageBaseUrl, fileName) {
1618
1786
  const url = imageBaseUrl + fileName;
1619
1787
  return axios.get(url, { responseType: "stream" });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "av6-core",
3
- "version": "1.3.11",
3
+ "version": "1.4.1",
4
4
  "main": "dist/index.js",
5
5
  "module": "dist/index.mjs",
6
6
  "types": "dist/index.d.ts",
@@ -9,18 +9,20 @@
9
9
  "license": "ISC",
10
10
  "scripts": {
11
11
  "build": "npm run format && tsup",
12
+ "p:gen": "prisma generate",
12
13
  "format": "prettier --write **/*.ts"
13
14
  },
14
15
  "devDependencies": {
16
+ "@prisma/client": "^6.19.0",
15
17
  "@types/nodemailer": "^7.0.3",
16
18
  "tsup": "^8.5.0",
17
19
  "typescript": "^5.9.2"
18
20
  },
19
21
  "peerDependencies": {
22
+ "@prisma/client": "^6.19.0",
20
23
  "winston": "^3.17.0"
21
24
  },
22
25
  "dependencies": {
23
- "@prisma/client": "^6.19.0",
24
26
  "axios": "^1.11.0",
25
27
  "exceljs": "^4.4.0",
26
28
  "handlebars": "^4.7.8",