@soulbatical/tetra-core 0.1.0
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.
Potentially problematic release.
This version of @soulbatical/tetra-core might be problematic. Click here for more details.
- package/dist/core/SupabaseUserClient.d.ts +14 -0
- package/dist/core/SupabaseUserClient.d.ts.map +1 -0
- package/dist/core/SupabaseUserClient.js +49 -0
- package/dist/core/SupabaseUserClient.js.map +1 -0
- package/dist/core/adminDb.d.ts +21 -0
- package/dist/core/adminDb.d.ts.map +1 -0
- package/dist/core/adminDb.js +43 -0
- package/dist/core/adminDb.js.map +1 -0
- package/dist/core/publicDb.d.ts +19 -0
- package/dist/core/publicDb.d.ts.map +1 -0
- package/dist/core/publicDb.js +29 -0
- package/dist/core/publicDb.js.map +1 -0
- package/dist/core/superadminDb.d.ts +29 -0
- package/dist/core/superadminDb.d.ts.map +1 -0
- package/dist/core/superadminDb.js +53 -0
- package/dist/core/superadminDb.js.map +1 -0
- package/dist/core/systemDb.d.ts +34 -0
- package/dist/core/systemDb.d.ts.map +1 -0
- package/dist/core/systemDb.js +64 -0
- package/dist/core/systemDb.js.map +1 -0
- package/dist/core/userDb.d.ts +22 -0
- package/dist/core/userDb.d.ts.map +1 -0
- package/dist/core/userDb.js +36 -0
- package/dist/core/userDb.js.map +1 -0
- package/dist/core/webhookDb.d.ts +16 -0
- package/dist/core/webhookDb.d.ts.map +1 -0
- package/dist/core/webhookDb.js +26 -0
- package/dist/core/webhookDb.js.map +1 -0
- package/dist/frontend/components/FeatureFilters.d.ts +81 -0
- package/dist/frontend/components/FeatureFilters.d.ts.map +1 -0
- package/dist/frontend/components/FeatureFilters.js +257 -0
- package/dist/frontend/components/FeatureFilters.js.map +1 -0
- package/dist/frontend/components/FeatureTable.d.ts +96 -0
- package/dist/frontend/components/FeatureTable.d.ts.map +1 -0
- package/dist/frontend/components/FeatureTable.js +279 -0
- package/dist/frontend/components/FeatureTable.js.map +1 -0
- package/dist/frontend/hooks/useFeature.d.ts +108 -0
- package/dist/frontend/hooks/useFeature.d.ts.map +1 -0
- package/dist/frontend/hooks/useFeature.js +354 -0
- package/dist/frontend/hooks/useFeature.js.map +1 -0
- package/dist/frontend/index.d.ts +17 -0
- package/dist/frontend/index.d.ts.map +1 -0
- package/dist/frontend/index.js +14 -0
- package/dist/frontend/index.js.map +1 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/dist/middleware/authMiddleware.d.ts +137 -0
- package/dist/middleware/authMiddleware.d.ts.map +1 -0
- package/dist/middleware/authMiddleware.js +292 -0
- package/dist/middleware/authMiddleware.js.map +1 -0
- package/dist/middleware/autoRegisterValidators.d.ts +31 -0
- package/dist/middleware/autoRegisterValidators.d.ts.map +1 -0
- package/dist/middleware/autoRegisterValidators.js +125 -0
- package/dist/middleware/autoRegisterValidators.js.map +1 -0
- package/dist/middleware/validateQueryParams.d.ts +62 -0
- package/dist/middleware/validateQueryParams.d.ts.map +1 -0
- package/dist/middleware/validateQueryParams.js +321 -0
- package/dist/middleware/validateQueryParams.js.map +1 -0
- package/dist/shared/controllers/BaseMutationController.d.ts +79 -0
- package/dist/shared/controllers/BaseMutationController.d.ts.map +1 -0
- package/dist/shared/controllers/BaseMutationController.js +241 -0
- package/dist/shared/controllers/BaseMutationController.js.map +1 -0
- package/dist/shared/controllers/BaseQueryController.d.ts +72 -0
- package/dist/shared/controllers/BaseQueryController.d.ts.map +1 -0
- package/dist/shared/controllers/BaseQueryController.js +375 -0
- package/dist/shared/controllers/BaseQueryController.js.map +1 -0
- package/dist/shared/controllers/FilterConfigController.d.ts +37 -0
- package/dist/shared/controllers/FilterConfigController.d.ts.map +1 -0
- package/dist/shared/controllers/FilterConfigController.js +94 -0
- package/dist/shared/controllers/FilterConfigController.js.map +1 -0
- package/dist/shared/controllers/index.d.ts +5 -0
- package/dist/shared/controllers/index.d.ts.map +1 -0
- package/dist/shared/controllers/index.js +4 -0
- package/dist/shared/controllers/index.js.map +1 -0
- package/dist/shared/controllers/types.d.ts +57 -0
- package/dist/shared/controllers/types.d.ts.map +1 -0
- package/dist/shared/controllers/types.js +2 -0
- package/dist/shared/controllers/types.js.map +1 -0
- package/dist/shared/factories/BatchRouteFactory.d.ts +33 -0
- package/dist/shared/factories/BatchRouteFactory.d.ts.map +1 -0
- package/dist/shared/factories/BatchRouteFactory.js +54 -0
- package/dist/shared/factories/BatchRouteFactory.js.map +1 -0
- package/dist/shared/factories/MutationRouteFactory.d.ts +27 -0
- package/dist/shared/factories/MutationRouteFactory.d.ts.map +1 -0
- package/dist/shared/factories/MutationRouteFactory.js +39 -0
- package/dist/shared/factories/MutationRouteFactory.js.map +1 -0
- package/dist/shared/factories/PhaseRouteFactory.d.ts +33 -0
- package/dist/shared/factories/PhaseRouteFactory.d.ts.map +1 -0
- package/dist/shared/factories/PhaseRouteFactory.js +67 -0
- package/dist/shared/factories/PhaseRouteFactory.js.map +1 -0
- package/dist/shared/factories/QueryRouteFactory.d.ts +37 -0
- package/dist/shared/factories/QueryRouteFactory.d.ts.map +1 -0
- package/dist/shared/factories/QueryRouteFactory.js +244 -0
- package/dist/shared/factories/QueryRouteFactory.js.map +1 -0
- package/dist/shared/factories/QueryServiceFactory.d.ts +282 -0
- package/dist/shared/factories/QueryServiceFactory.d.ts.map +1 -0
- package/dist/shared/factories/QueryServiceFactory.js +277 -0
- package/dist/shared/factories/QueryServiceFactory.js.map +1 -0
- package/dist/shared/factories/index.d.ts +8 -0
- package/dist/shared/factories/index.d.ts.map +1 -0
- package/dist/shared/factories/index.js +6 -0
- package/dist/shared/factories/index.js.map +1 -0
- package/dist/shared/factories/types.d.ts +98 -0
- package/dist/shared/factories/types.d.ts.map +1 -0
- package/dist/shared/factories/types.js +8 -0
- package/dist/shared/factories/types.js.map +1 -0
- package/dist/shared/ownership/types.d.ts +84 -0
- package/dist/shared/ownership/types.d.ts.map +1 -0
- package/dist/shared/ownership/types.js +8 -0
- package/dist/shared/ownership/types.js.map +1 -0
- package/dist/shared/rfc7807ErrorResponse.d.ts +54 -0
- package/dist/shared/rfc7807ErrorResponse.d.ts.map +1 -0
- package/dist/shared/rfc7807ErrorResponse.js +98 -0
- package/dist/shared/rfc7807ErrorResponse.js.map +1 -0
- package/dist/shared/services/BaseCronService.d.ts +171 -0
- package/dist/shared/services/BaseCronService.d.ts.map +1 -0
- package/dist/shared/services/BaseCronService.js +444 -0
- package/dist/shared/services/BaseCronService.js.map +1 -0
- package/dist/shared/services/BasePhaseService.d.ts +170 -0
- package/dist/shared/services/BasePhaseService.d.ts.map +1 -0
- package/dist/shared/services/BasePhaseService.js +409 -0
- package/dist/shared/services/BasePhaseService.js.map +1 -0
- package/dist/shared/services/CronRegistry.d.ts +67 -0
- package/dist/shared/services/CronRegistry.d.ts.map +1 -0
- package/dist/shared/services/CronRegistry.js +68 -0
- package/dist/shared/services/CronRegistry.js.map +1 -0
- package/dist/shared/services/SimpleRPCQueryService.d.ts +111 -0
- package/dist/shared/services/SimpleRPCQueryService.d.ts.map +1 -0
- package/dist/shared/services/SimpleRPCQueryService.js +265 -0
- package/dist/shared/services/SimpleRPCQueryService.js.map +1 -0
- package/dist/shared/types/feature-config.d.ts +611 -0
- package/dist/shared/types/feature-config.d.ts.map +1 -0
- package/dist/shared/types/feature-config.js +85 -0
- package/dist/shared/types/feature-config.js.map +1 -0
- package/dist/shared/types/query-config.d.ts +41 -0
- package/dist/shared/types/query-config.d.ts.map +1 -0
- package/dist/shared/types/query-config.js +6 -0
- package/dist/shared/types/query-config.js.map +1 -0
- package/dist/shared/utils/config-driven-filters.d.ts +215 -0
- package/dist/shared/utils/config-driven-filters.d.ts.map +1 -0
- package/dist/shared/utils/config-driven-filters.js +451 -0
- package/dist/shared/utils/config-driven-filters.js.map +1 -0
- package/dist/shared/utils/controllerErrorHandler.d.ts +44 -0
- package/dist/shared/utils/controllerErrorHandler.d.ts.map +1 -0
- package/dist/shared/utils/controllerErrorHandler.js +126 -0
- package/dist/shared/utils/controllerErrorHandler.js.map +1 -0
- package/dist/shared/utils/parseInclude.d.ts +14 -0
- package/dist/shared/utils/parseInclude.d.ts.map +1 -0
- package/dist/shared/utils/parseInclude.js +28 -0
- package/dist/shared/utils/parseInclude.js.map +1 -0
- package/dist/shared/utils/queryHelpers.d.ts +62 -0
- package/dist/shared/utils/queryHelpers.d.ts.map +1 -0
- package/dist/shared/utils/queryHelpers.js +61 -0
- package/dist/shared/utils/queryHelpers.js.map +1 -0
- package/dist/shared/utils/response-mapper.d.ts +42 -0
- package/dist/shared/utils/response-mapper.d.ts.map +1 -0
- package/dist/shared/utils/response-mapper.js +176 -0
- package/dist/shared/utils/response-mapper.js.map +1 -0
- package/dist/shared/utils/responseBuilder.d.ts +103 -0
- package/dist/shared/utils/responseBuilder.d.ts.map +1 -0
- package/dist/shared/utils/responseBuilder.js +131 -0
- package/dist/shared/utils/responseBuilder.js.map +1 -0
- package/dist/shared/validators/featureConfigValidator.d.ts +23 -0
- package/dist/shared/validators/featureConfigValidator.d.ts.map +1 -0
- package/dist/shared/validators/featureConfigValidator.js +143 -0
- package/dist/shared/validators/featureConfigValidator.js.map +1 -0
- package/dist/shared/validators/organizationValidator.d.ts +53 -0
- package/dist/shared/validators/organizationValidator.d.ts.map +1 -0
- package/dist/shared/validators/organizationValidator.js +69 -0
- package/dist/shared/validators/organizationValidator.js.map +1 -0
- package/dist/shared/validators/uuidValidator.d.ts +57 -0
- package/dist/shared/validators/uuidValidator.d.ts.map +1 -0
- package/dist/shared/validators/uuidValidator.js +77 -0
- package/dist/shared/validators/uuidValidator.js.map +1 -0
- package/dist/utils/logger.d.ts +40 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +47 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +80 -0
|
@@ -0,0 +1,409 @@
|
|
|
1
|
+
import { createLogger } from '../../utils/logger.js';
|
|
2
|
+
const logger = createLogger('service:phase:base');
|
|
3
|
+
/**
|
|
4
|
+
* Abstract base class for all Phase Services
|
|
5
|
+
* Provides 95% of batch update logic shared across:
|
|
6
|
+
* - OrderPhaseService
|
|
7
|
+
* - BookPhaseService (via BookPhaseCacheService)
|
|
8
|
+
* - ProductionPhaseService
|
|
9
|
+
* - ProductionItemPhaseService
|
|
10
|
+
* - OrderItemPhaseService
|
|
11
|
+
*
|
|
12
|
+
* Pattern:
|
|
13
|
+
* 1. Batch fetch entities using RPC (200 per chunk)
|
|
14
|
+
* 2. Calculate phases in batch (avoiding N+1)
|
|
15
|
+
* 3. Batch update (100 per chunk with Promise.allSettled)
|
|
16
|
+
* 4. Progress logging & error tracking
|
|
17
|
+
*/
|
|
18
|
+
export class BasePhaseService {
|
|
19
|
+
supabase;
|
|
20
|
+
config; // FeatureConfig
|
|
21
|
+
constructor(supabase) {
|
|
22
|
+
this.supabase = supabase;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Get the RPC function name for batch fetching (v3.0 - results pattern)
|
|
26
|
+
* Uses config.resultsRpcName automatically
|
|
27
|
+
*
|
|
28
|
+
* @deprecated Override getFeatureConfig() instead - this method is auto-generated
|
|
29
|
+
*/
|
|
30
|
+
getRpcFunctionName() {
|
|
31
|
+
if (!this.config) {
|
|
32
|
+
this.config = this.getFeatureConfig();
|
|
33
|
+
}
|
|
34
|
+
if (!this.config?.resultsRpcName) {
|
|
35
|
+
throw new Error(`Missing resultsRpcName in feature config for ${this.getTableName()}. ` +
|
|
36
|
+
`Add 'resultsRpcName: "get_${this.getTableName()}_results"' to the config.`);
|
|
37
|
+
}
|
|
38
|
+
return this.config.resultsRpcName;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Get the batch size for updates (default: 100)
|
|
42
|
+
* Override if needed (e.g., Productions uses 20)
|
|
43
|
+
*/
|
|
44
|
+
getUpdateBatchSize() {
|
|
45
|
+
return 100;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Prepare update data from entity and phases
|
|
49
|
+
* Override if entity needs custom fields (e.g., phase_conflict_details)
|
|
50
|
+
*/
|
|
51
|
+
prepareUpdateData(entity, phases, now) {
|
|
52
|
+
return {
|
|
53
|
+
current_phase: phases[0],
|
|
54
|
+
phase_updated_at: now
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* CONCRETE METHODS - Shared 95% logic
|
|
59
|
+
*/
|
|
60
|
+
/**
|
|
61
|
+
* Batch fetch entities using RPC
|
|
62
|
+
* Handles chunking if > 200 IDs
|
|
63
|
+
*/
|
|
64
|
+
async batchFetchEntities(ids) {
|
|
65
|
+
if (ids.length === 0)
|
|
66
|
+
return [];
|
|
67
|
+
const SAFE_BATCH_SIZE = 200;
|
|
68
|
+
const entities = [];
|
|
69
|
+
const rpcFunction = this.getRpcFunctionName();
|
|
70
|
+
const entityType = this.getEntityTypeName();
|
|
71
|
+
if (ids.length > SAFE_BATCH_SIZE) {
|
|
72
|
+
logger.info(`📦 Processing ${ids.length} ${entityType} in batches of ${SAFE_BATCH_SIZE}...`);
|
|
73
|
+
for (let i = 0; i < ids.length; i += SAFE_BATCH_SIZE) {
|
|
74
|
+
const batchIds = ids.slice(i, i + SAFE_BATCH_SIZE);
|
|
75
|
+
const batchNumber = Math.floor(i / SAFE_BATCH_SIZE) + 1;
|
|
76
|
+
const totalBatches = Math.ceil(ids.length / SAFE_BATCH_SIZE);
|
|
77
|
+
logger.info(`📦 Fetching batch ${batchNumber}/${totalBatches} (${batchIds.length} ${entityType})...`);
|
|
78
|
+
// Modern pattern: get_*_results with p_ids parameter
|
|
79
|
+
// No p_org_id for system-wide cron jobs (NULL = all orgs)
|
|
80
|
+
const { data, error } = await this.supabase
|
|
81
|
+
.rpc(rpcFunction, {
|
|
82
|
+
p_org_id: null, // NULL = system-wide (all organizations)
|
|
83
|
+
p_ids: batchIds,
|
|
84
|
+
p_limit: 10000 // High limit to fetch all requested IDs
|
|
85
|
+
});
|
|
86
|
+
if (error) {
|
|
87
|
+
logger.error({ error, batchNumber }, `❌ Batch ${batchNumber} failed`);
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
else if (data && data.length > 0) {
|
|
91
|
+
// Extract data from rows (get_*_results returns {data: jsonb, total_count: bigint}[])
|
|
92
|
+
const extractedData = data.map((row) => row.data);
|
|
93
|
+
entities.push(...extractedData);
|
|
94
|
+
logger.info(`✅ Batch ${batchNumber} successful: ${extractedData.length} ${entityType} fetched`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
// Small enough to fetch in one go
|
|
100
|
+
const { data, error } = await this.supabase
|
|
101
|
+
.rpc(rpcFunction, {
|
|
102
|
+
p_org_id: null, // NULL = system-wide (all organizations)
|
|
103
|
+
p_ids: ids,
|
|
104
|
+
p_limit: 10000 // High limit to fetch all requested IDs
|
|
105
|
+
});
|
|
106
|
+
if (error) {
|
|
107
|
+
logger.error({ error }, `Error fetching ${entityType} for batch update`);
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
// Extract data from rows (get_*_results returns {data: jsonb, total_count: bigint}[])
|
|
111
|
+
if (data && data.length > 0) {
|
|
112
|
+
const extractedData = data.map((row) => row.data);
|
|
113
|
+
entities.push(...extractedData);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return entities;
|
|
117
|
+
}
|
|
118
|
+
// REMOVED: getRpcParameterName() - No longer needed
|
|
119
|
+
// Modern pattern uses standardized p_ids parameter in get_*_results functions
|
|
120
|
+
/**
|
|
121
|
+
* Generic method to batch fetch related entities via RPC
|
|
122
|
+
* Handles error logging and groups results by key
|
|
123
|
+
*
|
|
124
|
+
* @param rpcName - Name of the RPC function to call
|
|
125
|
+
* @param params - Parameters to pass to the RPC function
|
|
126
|
+
* @param groupByKey - Key to group results by (e.g., 'orderid', 'productionid')
|
|
127
|
+
* @returns Map of grouped results by the specified key
|
|
128
|
+
*
|
|
129
|
+
* @example
|
|
130
|
+
* ```typescript
|
|
131
|
+
* const orderItemsMap = await this.batchFetchRelated<OrderItem>(
|
|
132
|
+
* 'get_orderitems_for_orders',
|
|
133
|
+
* { order_ids: orderIds },
|
|
134
|
+
* 'orderid'
|
|
135
|
+
* );
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
async batchFetchRelated(rpcName, params, groupByKey) {
|
|
139
|
+
// Return empty map if no params or all params are empty
|
|
140
|
+
if (!params || Object.values(params).every(v => !v || (Array.isArray(v) && v.length === 0))) {
|
|
141
|
+
return new Map();
|
|
142
|
+
}
|
|
143
|
+
try {
|
|
144
|
+
const { data, error } = await this.supabase.rpc(rpcName, params);
|
|
145
|
+
if (error) {
|
|
146
|
+
logger.error({ error, rpcName, params }, `Failed to batch fetch related entities via ${rpcName}`);
|
|
147
|
+
return new Map();
|
|
148
|
+
}
|
|
149
|
+
return this.groupByKey(data || [], groupByKey);
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
logger.error({ error, rpcName, params }, `Exception during batch fetch via ${rpcName}`);
|
|
153
|
+
return new Map();
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Helper method to group array of items by a specific key
|
|
158
|
+
*
|
|
159
|
+
* @param items - Array of items to group
|
|
160
|
+
* @param key - Property name to group by
|
|
161
|
+
* @returns Map where key is the grouping value and value is array of items
|
|
162
|
+
*
|
|
163
|
+
* @example
|
|
164
|
+
* ```typescript
|
|
165
|
+
* const itemsByOrder = this.groupByKey<OrderItem>(orderItems, 'orderid');
|
|
166
|
+
* ```
|
|
167
|
+
*/
|
|
168
|
+
groupByKey(items, key) {
|
|
169
|
+
const map = new Map();
|
|
170
|
+
for (const item of items) {
|
|
171
|
+
const keyValue = item[key];
|
|
172
|
+
if (!map.has(keyValue)) {
|
|
173
|
+
map.set(keyValue, []);
|
|
174
|
+
}
|
|
175
|
+
map.get(keyValue).push(item);
|
|
176
|
+
}
|
|
177
|
+
return map;
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Batch update entities with phases
|
|
181
|
+
* Uses Promise.allSettled for parallel updates with error tracking
|
|
182
|
+
*/
|
|
183
|
+
async batchUpdateEntities(updateData, results) {
|
|
184
|
+
if (updateData.length === 0)
|
|
185
|
+
return;
|
|
186
|
+
const BATCH_SIZE = this.getUpdateBatchSize();
|
|
187
|
+
const now = new Date().toISOString();
|
|
188
|
+
const tableName = this.getTableName();
|
|
189
|
+
const entityType = this.getEntityTypeName();
|
|
190
|
+
logger.info(`📝 Updating ${updateData.length} ${entityType} in batches of ${BATCH_SIZE}...`);
|
|
191
|
+
for (let i = 0; i < updateData.length; i += BATCH_SIZE) {
|
|
192
|
+
const batch = updateData.slice(i, i + BATCH_SIZE);
|
|
193
|
+
// Use Promise.allSettled to update all entities in batch concurrently
|
|
194
|
+
const batchResults = await Promise.allSettled(batch.map(update => {
|
|
195
|
+
// Prepare update data using overridable method
|
|
196
|
+
const updateFields = this.prepareUpdateData(update, [update.current_phase], now);
|
|
197
|
+
return this.supabase
|
|
198
|
+
.from(tableName)
|
|
199
|
+
.update(updateFields)
|
|
200
|
+
.eq('id', update.id);
|
|
201
|
+
}));
|
|
202
|
+
// Process results
|
|
203
|
+
batchResults.forEach((result, idx) => {
|
|
204
|
+
const update = batch[idx];
|
|
205
|
+
if (result.status === 'fulfilled' && !result.value.error) {
|
|
206
|
+
results.success++;
|
|
207
|
+
}
|
|
208
|
+
else {
|
|
209
|
+
results.failed++;
|
|
210
|
+
const error = result.status === 'rejected'
|
|
211
|
+
? result.reason
|
|
212
|
+
: result.value.error;
|
|
213
|
+
// Better error formatting
|
|
214
|
+
let errorMessage = 'Unknown error';
|
|
215
|
+
if (typeof error === 'string') {
|
|
216
|
+
errorMessage = error;
|
|
217
|
+
}
|
|
218
|
+
else if (error?.message) {
|
|
219
|
+
errorMessage = error.message;
|
|
220
|
+
}
|
|
221
|
+
else if (error?.code) {
|
|
222
|
+
errorMessage = `Error code: ${error.code}${error.details ? ' - ' + error.details : ''}`;
|
|
223
|
+
}
|
|
224
|
+
else if (error) {
|
|
225
|
+
errorMessage = JSON.stringify(error);
|
|
226
|
+
}
|
|
227
|
+
results.errors.push({
|
|
228
|
+
entityId: update.id,
|
|
229
|
+
error: errorMessage
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
});
|
|
233
|
+
// Log progress
|
|
234
|
+
const processed = Math.min(i + BATCH_SIZE, updateData.length);
|
|
235
|
+
const percentage = Math.round((processed / updateData.length) * 100);
|
|
236
|
+
logger.info(`📝 Updated ${processed}/${updateData.length} ${entityType} (${percentage}%)`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Main batch update method
|
|
241
|
+
* This is the SINGLE SOURCE OF TRUTH for batch phase updates
|
|
242
|
+
*
|
|
243
|
+
* Pattern:
|
|
244
|
+
* 1. Batch fetch entities (200 per chunk)
|
|
245
|
+
* 2. Calculate phases in batch (avoiding N+1)
|
|
246
|
+
* 3. Prepare update data
|
|
247
|
+
* 4. Batch update (100 per chunk with error tracking)
|
|
248
|
+
*/
|
|
249
|
+
async updateCachedPhasesBatch(ids) {
|
|
250
|
+
const results = {
|
|
251
|
+
success: 0,
|
|
252
|
+
failed: 0,
|
|
253
|
+
errors: [],
|
|
254
|
+
changedIds: [] // Track which IDs actually changed
|
|
255
|
+
};
|
|
256
|
+
if (ids.length === 0) {
|
|
257
|
+
return results;
|
|
258
|
+
}
|
|
259
|
+
const entityType = this.getEntityTypeName();
|
|
260
|
+
logger.info(`🚀 Starting REAL batch phase update for ${ids.length} ${entityType}`);
|
|
261
|
+
const startTime = Date.now();
|
|
262
|
+
try {
|
|
263
|
+
// STEP 1: Batch fetch all entities using RPC
|
|
264
|
+
const entities = await this.batchFetchEntities(ids);
|
|
265
|
+
if (entities.length === 0) {
|
|
266
|
+
logger.warn(`No ${entityType} found for batch update`);
|
|
267
|
+
results.failed = ids.length;
|
|
268
|
+
ids.forEach(id => results.errors.push({ entityId: id, error: 'Entity not found' }));
|
|
269
|
+
return results;
|
|
270
|
+
}
|
|
271
|
+
// STEP 2: Calculate phases for ALL entities in ONE batch call (efficient!)
|
|
272
|
+
const allPhasesMap = await this.determineAllPhasesForEntities(entities);
|
|
273
|
+
// STEP 3: Prepare update data
|
|
274
|
+
const updateData = [];
|
|
275
|
+
for (const entity of entities) {
|
|
276
|
+
const entityId = entity.id;
|
|
277
|
+
const phases = allPhasesMap.get(entityId) || [];
|
|
278
|
+
if (phases.length === 0) {
|
|
279
|
+
results.failed++;
|
|
280
|
+
results.errors.push({ entityId, error: 'No phases determined' });
|
|
281
|
+
continue;
|
|
282
|
+
}
|
|
283
|
+
const newPhase = phases[0];
|
|
284
|
+
const previousPhase = entity.current_phase;
|
|
285
|
+
updateData.push({
|
|
286
|
+
id: entityId,
|
|
287
|
+
current_phase: newPhase,
|
|
288
|
+
previous_phase: previousPhase,
|
|
289
|
+
phase_details: {
|
|
290
|
+
all_phases: phases,
|
|
291
|
+
timestamp: new Date().toISOString()
|
|
292
|
+
}
|
|
293
|
+
});
|
|
294
|
+
// Track if phase actually changed
|
|
295
|
+
if (newPhase !== previousPhase) {
|
|
296
|
+
results.changedIds.push(entityId);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
// STEP 4: Batch update all entities
|
|
300
|
+
await this.batchUpdateEntities(updateData, results);
|
|
301
|
+
const duration = Math.round((Date.now() - startTime) / 1000);
|
|
302
|
+
logger.info({
|
|
303
|
+
[`total_${entityType.replace(/ /g, '_')}`]: ids.length,
|
|
304
|
+
success: results.success,
|
|
305
|
+
failed: results.failed,
|
|
306
|
+
changed: results.changedIds.length,
|
|
307
|
+
unchanged: results.success - results.changedIds.length,
|
|
308
|
+
duration
|
|
309
|
+
}, `✅ Batch phase update completed in ${duration}s`);
|
|
310
|
+
}
|
|
311
|
+
catch (error) {
|
|
312
|
+
// Better error logging
|
|
313
|
+
const errorDetails = error instanceof Error
|
|
314
|
+
? { message: error.message, stack: error.stack, name: error.name }
|
|
315
|
+
: { error: String(error) };
|
|
316
|
+
logger.error({
|
|
317
|
+
error: errorDetails,
|
|
318
|
+
entityCount: ids.length,
|
|
319
|
+
successSoFar: results.success,
|
|
320
|
+
failedSoFar: results.failed
|
|
321
|
+
}, `❌ Critical error in batch phase update for ${entityType}`);
|
|
322
|
+
// Mark remaining as failed
|
|
323
|
+
const remainingIds = ids.slice(results.success + results.failed);
|
|
324
|
+
results.failed += remainingIds.length;
|
|
325
|
+
// Better error message
|
|
326
|
+
let errorMessage = 'Unknown error';
|
|
327
|
+
if (error instanceof Error) {
|
|
328
|
+
errorMessage = error.message;
|
|
329
|
+
}
|
|
330
|
+
else if (typeof error === 'string') {
|
|
331
|
+
errorMessage = error;
|
|
332
|
+
}
|
|
333
|
+
else if (error) {
|
|
334
|
+
errorMessage = JSON.stringify(error);
|
|
335
|
+
}
|
|
336
|
+
remainingIds.forEach(id => {
|
|
337
|
+
results.errors.push({
|
|
338
|
+
entityId: id,
|
|
339
|
+
error: errorMessage
|
|
340
|
+
});
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
return results;
|
|
344
|
+
}
|
|
345
|
+
/**
|
|
346
|
+
* Update cached phase for a single entity
|
|
347
|
+
* Default implementation that delegates to batch method
|
|
348
|
+
*
|
|
349
|
+
* Services can override this if they need custom behavior (e.g., extra metadata)
|
|
350
|
+
*
|
|
351
|
+
* @param entityId - ID of entity to update
|
|
352
|
+
* @returns UNIFORM PhaseUpdateResult with all phase information
|
|
353
|
+
*/
|
|
354
|
+
async updateCachedPhase(entityId) {
|
|
355
|
+
const entityType = this.getEntityTypeName();
|
|
356
|
+
logger.debug({ entityId }, `Updating cached phase for single ${entityType.slice(0, -1)}`);
|
|
357
|
+
try {
|
|
358
|
+
// Get current phase before update
|
|
359
|
+
const entitiesBefore = await this.batchFetchEntities([entityId]);
|
|
360
|
+
const previousPhase = entitiesBefore[0] ? entitiesBefore[0].current_phase : undefined;
|
|
361
|
+
// Use batch method for consistency and code reuse
|
|
362
|
+
const results = await this.updateCachedPhasesBatch([entityId]);
|
|
363
|
+
if (results.success > 0) {
|
|
364
|
+
// Success - get the updated entity with all phase info
|
|
365
|
+
const entitiesAfter = await this.batchFetchEntities([entityId]);
|
|
366
|
+
const entity = entitiesAfter[0];
|
|
367
|
+
if (!entity) {
|
|
368
|
+
return {
|
|
369
|
+
success: false,
|
|
370
|
+
error: 'Entity not found after update'
|
|
371
|
+
};
|
|
372
|
+
}
|
|
373
|
+
const currentPhase = entity.current_phase;
|
|
374
|
+
const phaseDetails = entity.phase_conflict_details;
|
|
375
|
+
const allPhases = phaseDetails?.all_phases || (currentPhase ? [currentPhase] : []);
|
|
376
|
+
const hasConflict = phaseDetails?.phases?.length > 1 || false;
|
|
377
|
+
const changed = results.changedIds?.includes(entityId) || false;
|
|
378
|
+
return {
|
|
379
|
+
success: true,
|
|
380
|
+
phase: currentPhase,
|
|
381
|
+
previousPhase,
|
|
382
|
+
allPhases,
|
|
383
|
+
hasConflict,
|
|
384
|
+
metadata: {
|
|
385
|
+
changed,
|
|
386
|
+
timestamp: new Date().toISOString()
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
else {
|
|
391
|
+
// Failed - return error from batch results
|
|
392
|
+
const error = results.errors.find(e => e.entityId === entityId);
|
|
393
|
+
return {
|
|
394
|
+
success: false,
|
|
395
|
+
previousPhase,
|
|
396
|
+
error: error?.error || 'Update failed'
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
logger.error({ error, entityId }, `Error updating cached phase for ${entityType.slice(0, -1)}`);
|
|
402
|
+
return {
|
|
403
|
+
success: false,
|
|
404
|
+
error: error instanceof Error ? error.message : 'Unknown error'
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
//# sourceMappingURL=BasePhaseService.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BasePhaseService.js","sourceRoot":"","sources":["../../../src/shared/services/BasePhaseService.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Configuration for a cron service
|
|
3
|
+
* Used to register cron services for auto-discovery
|
|
4
|
+
*/
|
|
5
|
+
export interface CronServiceConfig {
|
|
6
|
+
/** Unique identifier (e.g., 'order-item-phases') */
|
|
7
|
+
id: string;
|
|
8
|
+
/** Display name (e.g., 'OrderItem Phases') */
|
|
9
|
+
name: string;
|
|
10
|
+
/** Human-readable description of what this cron job does */
|
|
11
|
+
description: string;
|
|
12
|
+
/** Human-readable schedule (e.g., 'Every 30 min (:06, :36)') */
|
|
13
|
+
schedule: string;
|
|
14
|
+
/** API endpoint for manual triggering */
|
|
15
|
+
endpoint: string;
|
|
16
|
+
/** Dynamic import function that returns the service class */
|
|
17
|
+
serviceImport: () => Promise<any>;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Registry for all cron services in the application
|
|
21
|
+
*
|
|
22
|
+
* Provides:
|
|
23
|
+
* - Auto-discovery of cron services
|
|
24
|
+
* - Centralized configuration
|
|
25
|
+
* - Type-safe service registration
|
|
26
|
+
*
|
|
27
|
+
* Usage:
|
|
28
|
+
* 1. Export cronConfig from your cron service file
|
|
29
|
+
* 2. Register it: CronRegistry.register(cronConfig)
|
|
30
|
+
* 3. Access via CronRegistry.getAll() or CronRegistry.get(id)
|
|
31
|
+
*/
|
|
32
|
+
export declare class CronRegistry {
|
|
33
|
+
private static services;
|
|
34
|
+
/**
|
|
35
|
+
* Register a cron service
|
|
36
|
+
* @param config - Cron service configuration
|
|
37
|
+
* @throws Error if service with same ID already registered
|
|
38
|
+
*/
|
|
39
|
+
static register(config: CronServiceConfig): void;
|
|
40
|
+
/**
|
|
41
|
+
* Get all registered cron services
|
|
42
|
+
* @returns Array of all registered cron service configs
|
|
43
|
+
*/
|
|
44
|
+
static getAll(): CronServiceConfig[];
|
|
45
|
+
/**
|
|
46
|
+
* Get a specific cron service by ID
|
|
47
|
+
* @param id - Service ID
|
|
48
|
+
* @returns Service config or undefined if not found
|
|
49
|
+
*/
|
|
50
|
+
static get(id: string): CronServiceConfig | undefined;
|
|
51
|
+
/**
|
|
52
|
+
* Check if a cron service is registered
|
|
53
|
+
* @param id - Service ID
|
|
54
|
+
* @returns True if service is registered
|
|
55
|
+
*/
|
|
56
|
+
static has(id: string): boolean;
|
|
57
|
+
/**
|
|
58
|
+
* Get count of registered services
|
|
59
|
+
* @returns Number of registered services
|
|
60
|
+
*/
|
|
61
|
+
static count(): number;
|
|
62
|
+
/**
|
|
63
|
+
* Clear all registered services (for testing)
|
|
64
|
+
*/
|
|
65
|
+
static clear(): void;
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=CronRegistry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CronRegistry.d.ts","sourceRoot":"","sources":["../../../src/shared/services/CronRegistry.ts"],"names":[],"mappings":"AAIA;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,oDAAoD;IACpD,EAAE,EAAE,MAAM,CAAC;IAEX,8CAA8C;IAC9C,IAAI,EAAE,MAAM,CAAC;IAEb,4DAA4D;IAC5D,WAAW,EAAE,MAAM,CAAC;IAEpB,gEAAgE;IAChE,QAAQ,EAAE,MAAM,CAAC;IAEjB,yCAAyC;IACzC,QAAQ,EAAE,MAAM,CAAC;IAEjB,6DAA6D;IAC7D,aAAa,EAAE,MAAM,OAAO,CAAC,GAAG,CAAC,CAAC;CACnC;AAED;;;;;;;;;;;;GAYG;AACH,qBAAa,YAAY;IACvB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAwC;IAE/D;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,iBAAiB,GAAG,IAAI;IAShD;;;OAGG;IACH,MAAM,CAAC,MAAM,IAAI,iBAAiB,EAAE;IAIpC;;;;OAIG;IACH,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,iBAAiB,GAAG,SAAS;IAIrD;;;;OAIG;IACH,MAAM,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO;IAI/B;;;OAGG;IACH,MAAM,CAAC,KAAK,IAAI,MAAM;IAItB;;OAEG;IACH,MAAM,CAAC,KAAK,IAAI,IAAI;CAIrB"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { createLogger } from '../../utils/logger.js';
|
|
2
|
+
const logger = createLogger('system:cron:registry');
|
|
3
|
+
/**
|
|
4
|
+
* Registry for all cron services in the application
|
|
5
|
+
*
|
|
6
|
+
* Provides:
|
|
7
|
+
* - Auto-discovery of cron services
|
|
8
|
+
* - Centralized configuration
|
|
9
|
+
* - Type-safe service registration
|
|
10
|
+
*
|
|
11
|
+
* Usage:
|
|
12
|
+
* 1. Export cronConfig from your cron service file
|
|
13
|
+
* 2. Register it: CronRegistry.register(cronConfig)
|
|
14
|
+
* 3. Access via CronRegistry.getAll() or CronRegistry.get(id)
|
|
15
|
+
*/
|
|
16
|
+
export class CronRegistry {
|
|
17
|
+
static services = new Map();
|
|
18
|
+
/**
|
|
19
|
+
* Register a cron service
|
|
20
|
+
* @param config - Cron service configuration
|
|
21
|
+
* @throws Error if service with same ID already registered
|
|
22
|
+
*/
|
|
23
|
+
static register(config) {
|
|
24
|
+
if (this.services.has(config.id)) {
|
|
25
|
+
logger.warn({ serviceId: config.id }, '⚠️ Cron service already registered, overwriting');
|
|
26
|
+
}
|
|
27
|
+
this.services.set(config.id, config);
|
|
28
|
+
logger.debug({ serviceId: config.id, name: config.name }, '✅ Cron service registered');
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Get all registered cron services
|
|
32
|
+
* @returns Array of all registered cron service configs
|
|
33
|
+
*/
|
|
34
|
+
static getAll() {
|
|
35
|
+
return Array.from(this.services.values());
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Get a specific cron service by ID
|
|
39
|
+
* @param id - Service ID
|
|
40
|
+
* @returns Service config or undefined if not found
|
|
41
|
+
*/
|
|
42
|
+
static get(id) {
|
|
43
|
+
return this.services.get(id);
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Check if a cron service is registered
|
|
47
|
+
* @param id - Service ID
|
|
48
|
+
* @returns True if service is registered
|
|
49
|
+
*/
|
|
50
|
+
static has(id) {
|
|
51
|
+
return this.services.has(id);
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get count of registered services
|
|
55
|
+
* @returns Number of registered services
|
|
56
|
+
*/
|
|
57
|
+
static count() {
|
|
58
|
+
return this.services.size;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Clear all registered services (for testing)
|
|
62
|
+
*/
|
|
63
|
+
static clear() {
|
|
64
|
+
this.services.clear();
|
|
65
|
+
logger.debug('🧹 All cron services unregistered');
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=CronRegistry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"CronRegistry.js","sourceRoot":"","sources":["../../../src/shared/services/CronRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAErD,MAAM,MAAM,GAAG,YAAY,CAAC,sBAAsB,CAAC,CAAC;AA0BpD;;;;;;;;;;;;GAYG;AACH,MAAM,OAAO,YAAY;IACf,MAAM,CAAC,QAAQ,GAAG,IAAI,GAAG,EAA6B,CAAC;IAE/D;;;;OAIG;IACH,MAAM,CAAC,QAAQ,CAAC,MAAyB;QACvC,IAAI,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,EAAE,CAAC;YACjC,MAAM,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,EAAE,EAAE,kDAAkD,CAAC,CAAC;QAC5F,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,EAAE,MAAM,CAAC,CAAC;QACrC,MAAM,CAAC,KAAK,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,EAAE,2BAA2B,CAAC,CAAC;IACzF,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,MAAM;QACX,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,GAAG,CAAC,EAAU;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED;;;;OAIG;IACH,MAAM,CAAC,GAAG,CAAC,EAAU;QACnB,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;IAC/B,CAAC;IAED;;;OAGG;IACH,MAAM,CAAC,KAAK;QACV,OAAO,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;IAC5B,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,KAAK;QACV,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACtB,MAAM,CAAC,KAAK,CAAC,mCAAmC,CAAC,CAAC;IACpD,CAAC"}
|