@restforgejs/platform 4.3.2 → 4.3.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build-info.json +2 -2
- package/cli/consumer-deploy.js +1 -1
- package/cli/consumer.js +1 -1
- package/generators/cli/dashboard/create.js +0 -1
- package/generators/cli/endpoint/create.js +1 -1
- package/generators/lib/generators/model-generator.js +1 -1
- package/generators/lib/payload/endpoint-schema-validator.js +11 -1
- package/generators/lib/payload/payload-runner.js +3 -3
- package/generators/lib/payload/schema-diff.js +186 -3
- package/generators/lib/templates/dashboard-catalog.js +1 -1
- package/generators/lib/templates/db-connection-env.js +1 -1
- package/generators/lib/templates/dbschema-catalog.js +1 -1
- package/generators/lib/templates/field-validation-catalog.js +1 -1
- package/generators/lib/templates/mysql-template.js +1 -1
- package/generators/lib/templates/oracle-template.js +1 -1
- package/generators/lib/templates/postgres-template.js +1 -1
- package/generators/lib/templates/query-declarative-catalog.js +1 -1
- package/generators/lib/templates/sqlite-template.js +1 -1
- package/generators/lib/utils/conflict-checker.js +9 -57
- package/generators/lib/utils/database-introspector.js +84 -0
- package/generators/lib/utils/file-utils.js +0 -159
- package/generators/lib/utils/payload-processor.js +21 -112
- package/integrity-manifest.json +18 -18
- package/package.json +1 -1
- package/scripts/verify-integrity.js +1 -1
- package/server.js +1 -1
- package/src/components/handlers/adjust_handler.js +1 -1
- package/src/components/handlers/audit_handler.js +1 -1
- package/src/components/handlers/delete_handler.js +1 -1
- package/src/components/handlers/export_handler.js +1 -1
- package/src/components/handlers/import_handler.js +1 -1
- package/src/components/handlers/insert_handler.js +1 -1
- package/src/components/handlers/update_handler.js +1 -1
- package/src/components/handlers/upload_handler.js +1 -1
- package/src/components/handlers/workflow_handler.js +1 -1
- package/src/components/integrations/webhook.js +1 -1
- package/src/consumers/baseConsumer.js +1 -1
- package/src/consumers/declarativeMapper.js +1 -1
- package/src/consumers/handlers/apiHandler.js +1 -1
- package/src/consumers/handlers/consoleHandler.js +1 -1
- package/src/consumers/handlers/databaseHandler.js +1 -1
- package/src/consumers/handlers/index.js +1 -1
- package/src/consumers/handlers/kafkaHandler.js +1 -1
- package/src/consumers/index.js +1 -1
- package/src/consumers/messageTransformer.js +1 -1
- package/src/consumers/validator.js +1 -1
- package/src/core/db/dialect/base-dialect.js +1 -1
- package/src/core/db/dialect/index.js +1 -1
- package/src/core/db/dialect/mysql-dialect.js +1 -1
- package/src/core/db/dialect/oracle-dialect.js +1 -1
- package/src/core/db/dialect/postgres-dialect.js +1 -1
- package/src/core/db/dialect/sqlite-dialect.js +1 -1
- package/src/core/db/flatten-helper.js +1 -1
- package/src/core/db/query-builder-error.js +1 -1
- package/src/core/db/query-builder.js +1 -1
- package/src/core/db/relation-helper.js +1 -1
- package/src/core/handlers/delete_handler.js +1 -1
- package/src/core/handlers/insert_handler.js +1 -1
- package/src/core/handlers/update_handler.js +1 -1
- package/src/core/models/base-model.js +1 -1
- package/src/core/utils/cache-manager.js +1 -1
- package/src/core/utils/component-engine.js +1 -1
- package/src/core/utils/context-builder.js +1 -1
- package/src/core/utils/datetime-formatter.js +1 -1
- package/src/core/utils/datetime-parser.js +1 -1
- package/src/core/utils/db.js +1 -1
- package/src/core/utils/logger.js +1 -1
- package/src/core/utils/payload-loader.js +1 -1
- package/src/core/utils/security-checks.js +1 -1
- package/src/middleware/body-options.js +1 -1
- package/src/middleware/cors.js +1 -1
- package/src/middleware/idempotency.js +1 -1
- package/src/middleware/rate-limiter.js +1 -1
- package/src/middleware/request-logger.js +1 -1
- package/src/middleware/security-headers.js +1 -1
- package/src/models/base-model-mysql.js +1 -1
- package/src/models/base-model-oracle.js +1 -1
- package/src/models/base-model-sqlite.js +1 -1
- package/src/models/base-model.js +1 -1
- package/src/pro/caching/redis-client.js +1 -1
- package/src/pro/caching/redis-helper.js +1 -1
- package/src/pro/consumers/baseConsumer.js +1 -1
- package/src/pro/consumers/declarativeMapper.js +1 -1
- package/src/pro/consumers/handlers/apiHandler.js +1 -1
- package/src/pro/consumers/handlers/consoleHandler.js +1 -1
- package/src/pro/consumers/handlers/databaseHandler.js +1 -1
- package/src/pro/consumers/handlers/index.js +1 -1
- package/src/pro/consumers/handlers/kafkaHandler.js +1 -1
- package/src/pro/consumers/index.js +1 -1
- package/src/pro/consumers/messageTransformer.js +1 -1
- package/src/pro/consumers/validator.js +1 -1
- package/src/pro/database/base-model-mysql.js +1 -1
- package/src/pro/database/base-model-oracle.js +1 -1
- package/src/pro/database/base-model-sqlite.js +1 -1
- package/src/pro/database/db-mysql.js +1 -1
- package/src/pro/database/db-oracle.js +1 -1
- package/src/pro/database/db-sqlite.js +1 -1
- package/src/pro/excel/excel-generator.js +1 -1
- package/src/pro/excel/excel-parser.js +1 -1
- package/src/pro/excel/export-service.js +1 -1
- package/src/pro/excel/export_handler.js +1 -1
- package/src/pro/excel/import-service.js +1 -1
- package/src/pro/excel/import-validator.js +1 -1
- package/src/pro/excel/import_handler.js +1 -1
- package/src/pro/excel/upsert-builder.js +1 -1
- package/src/pro/idgen/idgen-routes.js +1 -1
- package/src/pro/integrations/lookup-resolver.js +1 -1
- package/src/pro/integrations/upload-handler-v2.js +1 -1
- package/src/pro/integrations/upload-handler.js +1 -1
- package/src/pro/integrations/webhook.js +1 -1
- package/src/pro/locking/lock-routes.js +1 -1
- package/src/pro/locking/resource-lock-manager.js +1 -1
- package/src/pro/messaging/kafkaConsumerService.js +1 -1
- package/src/pro/messaging/kafkaService.js +1 -1
- package/src/pro/messaging/messagehubService.js +1 -1
- package/src/pro/messaging/rabbitmqService.js +1 -1
- package/src/pro/scheduler/job-manager.js +1 -1
- package/src/pro/scheduler/job-routes.js +1 -1
- package/src/pro/scheduler/job-validator.js +1 -1
- package/src/pro/storage/base-storage-provider.js +1 -1
- package/src/pro/storage/file-metadata-helper.js +1 -1
- package/src/pro/storage/index.js +1 -1
- package/src/pro/storage/local-storage-provider.js +1 -1
- package/src/pro/storage/s3-storage-provider.js +1 -1
- package/src/pro/storage/upload-cleanup-job.js +1 -1
- package/src/pro/storage/upload-cleanup-scheduler.js +1 -1
- package/src/pro/storage/upload-pending-tracker.js +1 -1
- package/src/pro/websocket/broadcast-helper.js +1 -1
- package/src/pro/websocket/index.js +1 -1
- package/src/pro/websocket/livesync-server.js +1 -1
- package/src/pro/websocket/ws-broadcaster.js +1 -1
- package/src/services/export-service.js +1 -1
- package/src/services/import-service.js +1 -1
- package/src/services/kafkaConsumerService.js +1 -1
- package/src/services/kafkaService.js +1 -1
- package/src/services/messagehubService.js +1 -1
- package/src/services/rabbitmqService.js +1 -1
- package/src/utils/cache-invalidation-registry.js +1 -1
- package/src/utils/cache-manager.js +1 -1
- package/src/utils/component-engine.js +1 -1
- package/src/utils/config-extractor.js +1 -1
- package/src/utils/consumerLogger.js +1 -1
- package/src/utils/context-builder.js +1 -1
- package/src/utils/dashboard-helpers.js +1 -1
- package/src/utils/dateHelper.js +1 -1
- package/src/utils/datetime-formatter.js +1 -1
- package/src/utils/datetime-parser.js +1 -1
- package/src/utils/db-bootstrap.js +1 -1
- package/src/utils/db-mysql.js +1 -1
- package/src/utils/db-oracle.js +1 -1
- package/src/utils/db-sqlite.js +1 -1
- package/src/utils/db.js +1 -1
- package/src/utils/demo-generator.js +1 -1
- package/src/utils/excel-generator.js +1 -1
- package/src/utils/excel-parser.js +1 -1
- package/src/utils/file-watcher.js +1 -1
- package/src/utils/id-generator.js +1 -1
- package/src/utils/idempotency-manager.js +1 -1
- package/src/utils/import-validator.js +1 -1
- package/src/utils/license-client.js +1 -1
- package/src/utils/lock-manager.js +1 -1
- package/src/utils/logger.js +1 -1
- package/src/utils/lookup-resolver.js +1 -1
- package/src/utils/payload-loader.js +1 -1
- package/src/utils/processor-response.js +1 -1
- package/src/utils/rabbitmq.js +1 -1
- package/src/utils/redis-client.js +1 -1
- package/src/utils/redis-helper.js +1 -1
- package/src/utils/request-scope.js +1 -1
- package/src/utils/security-checks.js +1 -1
- package/src/utils/service-resolver.js +1 -1
- package/src/utils/shutdown-coordinator.js +1 -1
- package/src/utils/trusted-keys.js +1 -1
- package/src/utils/upload-handler.js +1 -1
- package/src/utils/upsert-builder.js +1 -1
- package/src/utils/workflow-hook-executor.js +1 -1
|
@@ -78,47 +78,22 @@ class ConflictChecker {
|
|
|
78
78
|
conflicts.hasConflicts = true;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
-
// 3. Check
|
|
82
|
-
const queryDir = path.join(workingDir, 'src', 'models', moduleName, 'query');
|
|
83
|
-
if (this.hasQueryFiles(payload) && fs.existsSync(queryDir)) {
|
|
84
|
-
const fileInfo = FileUtils.getFileInfo(queryDir);
|
|
85
|
-
conflicts.fileConflicts.push({
|
|
86
|
-
type: 'queryDir',
|
|
87
|
-
path: queryDir,
|
|
88
|
-
relativePath: path.relative(workingDir, queryDir),
|
|
89
|
-
fileInfo: fileInfo
|
|
90
|
-
});
|
|
91
|
-
conflicts.hasConflicts = true;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// 4. Check metadata conflicts (optional, tidak urgent)
|
|
95
|
-
if (MetadataManager.endpointExists(moduleName, endpointName)) {
|
|
96
|
-
const endpointMetadata = MetadataManager.getEndpointMetadata(moduleName, endpointName);
|
|
97
|
-
conflicts.metadataConflicts.push({
|
|
98
|
-
type: 'endpoint_metadata',
|
|
99
|
-
endpointName: endpointName,
|
|
100
|
-
metadata: endpointMetadata
|
|
101
|
-
});
|
|
102
|
-
// Metadata conflicts tidak mengaktifkan hasConflicts untuk module
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// 5. Check system-level conflicts
|
|
81
|
+
// 3. Check system-level conflicts
|
|
106
82
|
const systemConflict = this.checkSystemConflicts(moduleName, endpointName);
|
|
107
83
|
if (systemConflict) {
|
|
108
84
|
conflicts.systemConflicts.push(systemConflict);
|
|
109
85
|
conflicts.hasConflicts = true;
|
|
110
86
|
}
|
|
111
87
|
|
|
112
|
-
//
|
|
88
|
+
// 5. Add summary details
|
|
113
89
|
conflicts.details = {
|
|
114
90
|
moduleName: moduleName,
|
|
115
91
|
endpointName: endpointName,
|
|
116
|
-
totalConflicts: conflicts.fileConflicts.length + conflicts.
|
|
92
|
+
totalConflicts: conflicts.fileConflicts.length + conflicts.systemConflicts.length,
|
|
117
93
|
totalFiles: conflicts.fileConflicts.length,
|
|
118
94
|
submoduleFiles: conflicts.fileConflicts.filter(f => f.type === 'submoduleFile').length,
|
|
119
95
|
modelFiles: conflicts.fileConflicts.filter(f => f.type === 'modelFile').length,
|
|
120
96
|
queryDirs: conflicts.fileConflicts.filter(f => f.type === 'queryDir').length,
|
|
121
|
-
metadataFiles: conflicts.metadataConflicts.length,
|
|
122
97
|
systemIssues: conflicts.systemConflicts.length,
|
|
123
98
|
riskLevel: this.assessRiskLevel(conflicts)
|
|
124
99
|
};
|
|
@@ -277,33 +252,21 @@ class ConflictChecker {
|
|
|
277
252
|
}
|
|
278
253
|
}
|
|
279
254
|
|
|
280
|
-
// 3. Check
|
|
281
|
-
if (MetadataManager.endpointExists(moduleName, endpointName)) {
|
|
282
|
-
const endpointMetadata = MetadataManager.getEndpointMetadata(moduleName, endpointName);
|
|
283
|
-
conflicts.metadataConflicts.push({
|
|
284
|
-
type: 'endpoint_metadata',
|
|
285
|
-
endpointName: endpointName,
|
|
286
|
-
metadata: endpointMetadata
|
|
287
|
-
});
|
|
288
|
-
// Metadata conflicts tidak mengaktifkan hasConflicts untuk processor
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// 4. Check system-level conflicts
|
|
255
|
+
// 3. Check system-level conflicts
|
|
292
256
|
const systemConflict = this.checkSystemConflicts(moduleName, endpointName);
|
|
293
257
|
if (systemConflict) {
|
|
294
258
|
conflicts.systemConflicts.push(systemConflict);
|
|
295
259
|
conflicts.hasConflicts = true;
|
|
296
260
|
}
|
|
297
261
|
|
|
298
|
-
//
|
|
262
|
+
// 4. Add summary details
|
|
299
263
|
conflicts.details = {
|
|
300
264
|
moduleName: moduleName,
|
|
301
265
|
endpointName: endpointName,
|
|
302
|
-
totalConflicts: conflicts.fileConflicts.length + conflicts.
|
|
266
|
+
totalConflicts: conflicts.fileConflicts.length + conflicts.systemConflicts.length,
|
|
303
267
|
totalFiles: conflicts.fileConflicts.length,
|
|
304
268
|
endpointFiles: conflicts.fileConflicts.filter(f => f.type === 'endpointFile').length,
|
|
305
269
|
processorFiles: conflicts.fileConflicts.filter(f => f.type === 'processorFile').length,
|
|
306
|
-
metadataFiles: conflicts.metadataConflicts.length,
|
|
307
270
|
systemIssues: conflicts.systemConflicts.length,
|
|
308
271
|
riskLevel: this.assessRiskLevel(conflicts)
|
|
309
272
|
};
|
|
@@ -344,29 +307,18 @@ class ConflictChecker {
|
|
|
344
307
|
}
|
|
345
308
|
}
|
|
346
309
|
|
|
347
|
-
// 2. Check
|
|
348
|
-
if (MetadataManager.endpointExists(moduleName, endpointName)) {
|
|
349
|
-
const endpointMetadata = MetadataManager.getEndpointMetadata(moduleName, endpointName);
|
|
350
|
-
conflicts.metadataConflicts.push({
|
|
351
|
-
type: 'endpoint_metadata',
|
|
352
|
-
endpointName: endpointName,
|
|
353
|
-
metadata: endpointMetadata
|
|
354
|
-
});
|
|
355
|
-
conflicts.hasConflicts = true;
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// 3. Check system conflicts (reserved names, etc)
|
|
310
|
+
// 2. Check system conflicts (reserved names, etc)
|
|
359
311
|
const systemConflict = this.checkSystemConflicts(moduleName, endpointName);
|
|
360
312
|
if (systemConflict) {
|
|
361
313
|
conflicts.systemConflicts.push(systemConflict);
|
|
362
314
|
conflicts.hasConflicts = true;
|
|
363
315
|
}
|
|
364
316
|
|
|
365
|
-
//
|
|
317
|
+
// 3. Collect detailed information
|
|
366
318
|
conflicts.details = {
|
|
367
319
|
moduleName: moduleName,
|
|
368
320
|
endpointName: endpointName,
|
|
369
|
-
totalConflicts: conflicts.fileConflicts.length + conflicts.
|
|
321
|
+
totalConflicts: conflicts.fileConflicts.length + conflicts.systemConflicts.length,
|
|
370
322
|
riskLevel: this.assessRiskLevel(conflicts)
|
|
371
323
|
};
|
|
372
324
|
|
|
@@ -1257,6 +1257,90 @@ class DatabaseIntrospector {
|
|
|
1257
1257
|
}
|
|
1258
1258
|
}
|
|
1259
1259
|
|
|
1260
|
+
/**
|
|
1261
|
+
* Describe kolom output dari arbitrary SELECT query tanpa mengkonsumsi row.
|
|
1262
|
+
* Pendekatan: wrap query dalam subquery `WHERE false LIMIT 0` agar DB engine
|
|
1263
|
+
* tetap resolve column metadata (alias, JOIN, subquery) tanpa execute baris.
|
|
1264
|
+
*
|
|
1265
|
+
* Dipakai oleh schema-diff untuk extract kolom dari viewQuery/datatablesQuery/
|
|
1266
|
+
* exportQuery, sehingga JOIN field (mis. `category_name`) bisa di-treat sebagai
|
|
1267
|
+
* valid column saat drift check terhadap `fieldName`.
|
|
1268
|
+
*
|
|
1269
|
+
* @param {string} sql - SELECT statement
|
|
1270
|
+
* @returns {Promise<{ok: boolean, columns?: string[], error?: {code: string, message: string}}>}
|
|
1271
|
+
*/
|
|
1272
|
+
async describeQueryColumns(sql) {
|
|
1273
|
+
if (!this.pool) {
|
|
1274
|
+
throw new Error('Database not connected');
|
|
1275
|
+
}
|
|
1276
|
+
|
|
1277
|
+
const trimmed = (sql || '').trim().replace(/;+\s*$/, '');
|
|
1278
|
+
if (!trimmed) {
|
|
1279
|
+
return { ok: false, error: { code: 'empty_sql', message: 'SQL query is empty' } };
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
switch (this.dbType) {
|
|
1283
|
+
case 'postgresql': {
|
|
1284
|
+
const wrapped = `SELECT * FROM (${trimmed}) _describe WHERE false`;
|
|
1285
|
+
try {
|
|
1286
|
+
const result = await this.pool.query(wrapped);
|
|
1287
|
+
const columns = (result.fields || []).map(f => String(f.name).toLowerCase());
|
|
1288
|
+
return { ok: true, columns };
|
|
1289
|
+
} catch (err) {
|
|
1290
|
+
return {
|
|
1291
|
+
ok: false,
|
|
1292
|
+
error: {
|
|
1293
|
+
code: err.code || 'unknown',
|
|
1294
|
+
message: err.message
|
|
1295
|
+
}
|
|
1296
|
+
};
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
|
|
1300
|
+
case 'mysql': {
|
|
1301
|
+
const wrapped = `SELECT * FROM (${trimmed}) _describe WHERE 1=0`;
|
|
1302
|
+
try {
|
|
1303
|
+
const [, fields] = await this.pool.query(wrapped);
|
|
1304
|
+
const columns = (fields || []).map(f => String(f.name).toLowerCase());
|
|
1305
|
+
return { ok: true, columns };
|
|
1306
|
+
} catch (err) {
|
|
1307
|
+
return {
|
|
1308
|
+
ok: false,
|
|
1309
|
+
error: {
|
|
1310
|
+
code: err.code || (err.errno ? String(err.errno) : 'unknown'),
|
|
1311
|
+
message: err.sqlMessage || err.message
|
|
1312
|
+
}
|
|
1313
|
+
};
|
|
1314
|
+
}
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
case 'oracle': {
|
|
1318
|
+
const wrapped = `SELECT * FROM (${trimmed}) _describe WHERE 1=0`;
|
|
1319
|
+
const conn = await this.pool.getConnection();
|
|
1320
|
+
try {
|
|
1321
|
+
const result = await conn.execute(wrapped, [], { maxRows: 0 });
|
|
1322
|
+
const columns = (result.metaData || []).map(m => String(m.name).toLowerCase());
|
|
1323
|
+
return { ok: true, columns };
|
|
1324
|
+
} catch (err) {
|
|
1325
|
+
return {
|
|
1326
|
+
ok: false,
|
|
1327
|
+
error: {
|
|
1328
|
+
code: err.errorNum
|
|
1329
|
+
? `ORA-${String(err.errorNum).padStart(5, '0')}`
|
|
1330
|
+
: 'unknown',
|
|
1331
|
+
message: err.message
|
|
1332
|
+
}
|
|
1333
|
+
};
|
|
1334
|
+
} finally {
|
|
1335
|
+
try { await conn.close(); } catch (e) { /* ignore */ }
|
|
1336
|
+
}
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
default:
|
|
1340
|
+
throw new Error(`Unsupported DB_TYPE: ${this.dbType}`);
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1260
1344
|
async validateQuery(sql) {
|
|
1261
1345
|
if (!this.pool) {
|
|
1262
1346
|
throw new Error('Database not connected');
|
|
@@ -210,165 +210,6 @@ class FileUtils {
|
|
|
210
210
|
.toLowerCase();
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
/**
|
|
214
|
-
* Menyalin file SQL dari array file references ke folder model (untuk preserve mode)
|
|
215
|
-
* @param {Array} filesToCopy - Array file references (format: file:path)
|
|
216
|
-
* @param {string} payloadDir - Directory dimana payload berada
|
|
217
|
-
* @param {string} moduleName - Nama module
|
|
218
|
-
* @returns {Array} Array file yang berhasil disalin
|
|
219
|
-
*/
|
|
220
|
-
static copyQueryFiles(filesToCopy, payloadDir, moduleName) {
|
|
221
|
-
const workingDir = ConfigReader.getWorkingDirectory();
|
|
222
|
-
const modelQueryDir = path.join(workingDir, 'src', 'models', moduleName, 'query');
|
|
223
|
-
|
|
224
|
-
// Pastikan direktori query ada
|
|
225
|
-
this.ensureDirectoryExists(modelQueryDir);
|
|
226
|
-
|
|
227
|
-
const copyResults = [];
|
|
228
|
-
|
|
229
|
-
for (const fileReference of filesToCopy) {
|
|
230
|
-
try {
|
|
231
|
-
// Parse file reference
|
|
232
|
-
const relativePath = fileReference.replace('file:', '');
|
|
233
|
-
const sourceFilePath = path.join(payloadDir, relativePath);
|
|
234
|
-
const fileName = path.basename(relativePath);
|
|
235
|
-
const destFilePath = path.join(modelQueryDir, fileName);
|
|
236
|
-
|
|
237
|
-
console.log(`Copying SQL file: ${relativePath} -> query/${fileName}`);
|
|
238
|
-
|
|
239
|
-
// Validasi source file exists
|
|
240
|
-
if (!fs.existsSync(sourceFilePath)) {
|
|
241
|
-
console.error(`Source file not found: ${sourceFilePath}`);
|
|
242
|
-
continue;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// Copy file
|
|
246
|
-
fs.copyFileSync(sourceFilePath, destFilePath);
|
|
247
|
-
|
|
248
|
-
// Validasi hasil copy
|
|
249
|
-
if (!fs.existsSync(destFilePath)) {
|
|
250
|
-
console.error(`Failed to copy file: ${fileName}`);
|
|
251
|
-
continue;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
const destStats = fs.statSync(destFilePath);
|
|
255
|
-
console.log(`SQL file copied: ${fileName} (${destStats.size} bytes)`);
|
|
256
|
-
|
|
257
|
-
copyResults.push({
|
|
258
|
-
source: sourceFilePath,
|
|
259
|
-
dest: destFilePath,
|
|
260
|
-
fileName: fileName,
|
|
261
|
-
size: destStats.size
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
} catch (error) {
|
|
265
|
-
console.error(`Error copying file ${fileReference}:`, error.message);
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
console.log(`Successfully copied ${copyResults.length} of ${filesToCopy.length} SQL files`);
|
|
270
|
-
return copyResults;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/**
|
|
274
|
-
* Menyalin file SQL ke folder model (khusus untuk Node.js mode) - LEGACY METHOD
|
|
275
|
-
* @param {string} moduleName - Nama module
|
|
276
|
-
* @param {string} endpointName - Nama endpoint
|
|
277
|
-
* @param {Object} payload - Payload object
|
|
278
|
-
* @returns {Array} Array file yang berhasil disalin
|
|
279
|
-
*/
|
|
280
|
-
static copyQueryFilesLegacy(moduleName, endpointName, payload) {
|
|
281
|
-
const isPkg = ConfigReader.isPkg;
|
|
282
|
-
|
|
283
|
-
if (isPkg) {
|
|
284
|
-
// Dalam PKG mode, skip file copying karena file sudah di-bundle
|
|
285
|
-
console.log('🎁 PKG mode: Skip copying SQL files (assumed to be bundled)');
|
|
286
|
-
return [];
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
console.log('Processing SQL file copying in Node.js mode...');
|
|
290
|
-
|
|
291
|
-
const workingDir = ConfigReader.getWorkingDirectory();
|
|
292
|
-
const modelQueryDir = path.join(workingDir, 'src', 'models', moduleName, 'query');
|
|
293
|
-
|
|
294
|
-
// Pastikan direktori query ada
|
|
295
|
-
this.ensureDirectoryExists(modelQueryDir);
|
|
296
|
-
|
|
297
|
-
const neededFiles = [];
|
|
298
|
-
|
|
299
|
-
// Proses advancedQueries jika ada
|
|
300
|
-
if (payload.advancedQueries && typeof payload.advancedQueries === 'object') {
|
|
301
|
-
for (const [queryName, querySource] of Object.entries(payload.advancedQueries)) {
|
|
302
|
-
if (typeof querySource === 'string' && querySource.startsWith('file:')) {
|
|
303
|
-
const fileInfo = this.prepareFileForCopy(
|
|
304
|
-
querySource,
|
|
305
|
-
queryName,
|
|
306
|
-
'advancedQuery',
|
|
307
|
-
modelQueryDir
|
|
308
|
-
);
|
|
309
|
-
if (fileInfo) {
|
|
310
|
-
neededFiles.push(fileInfo);
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
// Proses nestedQueries jika ada
|
|
317
|
-
if (payload.nestedQueries && Array.isArray(payload.nestedQueries)) {
|
|
318
|
-
for (const nestedQuery of payload.nestedQueries) {
|
|
319
|
-
if (nestedQuery.query && typeof nestedQuery.query === 'string' && nestedQuery.query.startsWith('file:')) {
|
|
320
|
-
const fileInfo = this.prepareFileForCopy(
|
|
321
|
-
nestedQuery.query,
|
|
322
|
-
nestedQuery.name || 'nested',
|
|
323
|
-
'nestedQuery',
|
|
324
|
-
modelQueryDir
|
|
325
|
-
);
|
|
326
|
-
if (fileInfo) {
|
|
327
|
-
neededFiles.push(fileInfo);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
// Proses datatablesQuery - cek original payload untuk file reference
|
|
334
|
-
if (payload.datatablesQuery && typeof payload.datatablesQuery === 'string' && payload.datatablesQuery.startsWith('file:')) {
|
|
335
|
-
const fileInfo = this.prepareFileForCopy(
|
|
336
|
-
payload.datatablesQuery,
|
|
337
|
-
'datatables',
|
|
338
|
-
'datatablesQuery',
|
|
339
|
-
modelQueryDir
|
|
340
|
-
);
|
|
341
|
-
if (fileInfo) {
|
|
342
|
-
neededFiles.push(fileInfo);
|
|
343
|
-
}
|
|
344
|
-
} else if (payload._originalDataTablesQuery && typeof payload._originalDataTablesQuery === 'string' && payload._originalDataTablesQuery.startsWith('file:')) {
|
|
345
|
-
// Fallback untuk payload yang sudah di-expand tapi masih punya original reference
|
|
346
|
-
const fileInfo = this.prepareFileForCopy(
|
|
347
|
-
payload._originalDataTablesQuery,
|
|
348
|
-
'datatables',
|
|
349
|
-
'datatablesQuery',
|
|
350
|
-
modelQueryDir
|
|
351
|
-
);
|
|
352
|
-
if (fileInfo) {
|
|
353
|
-
neededFiles.push(fileInfo);
|
|
354
|
-
}
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
// Salin semua file yang dibutuhkan
|
|
358
|
-
const copyResult = this.executeBulkFileCopy(neededFiles);
|
|
359
|
-
|
|
360
|
-
console.log(`Total ${copyResult.success} of ${neededFiles.length} SQL files copied successfully`);
|
|
361
|
-
|
|
362
|
-
if (copyResult.failed > 0) {
|
|
363
|
-
console.warn(`${copyResult.failed} files failed to copy`);
|
|
364
|
-
copyResult.errors.forEach(error => {
|
|
365
|
-
console.error(` ${error.file}: ${error.message}`);
|
|
366
|
-
});
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
return copyResult.successFiles;
|
|
370
|
-
}
|
|
371
|
-
|
|
372
213
|
/**
|
|
373
214
|
* Prepare file information untuk copy operation
|
|
374
215
|
* @param {string} querySource - Source query dengan format file:path
|
|
@@ -41,15 +41,7 @@ class PayloadProcessor {
|
|
|
41
41
|
*/
|
|
42
42
|
static processFileReferences(payload, payloadPath, options = {}) {
|
|
43
43
|
const isPkg = ConfigReader.isPkg;
|
|
44
|
-
|
|
45
|
-
// Jika preserveFileReferences = true, skip expansion tapi tetap copy file
|
|
46
|
-
if (options.preserveFileReferences) {
|
|
47
|
-
console.log('Preserving file references mode: Files will be copied but references preserved');
|
|
48
|
-
// Copy file query tetap dilakukan di preserveFileReferences mode (baik JS maupun PKG)
|
|
49
|
-
this.copyQueryFilesForPreserveMode(payload, payloadPath, options);
|
|
50
|
-
return payload;
|
|
51
|
-
}
|
|
52
|
-
|
|
44
|
+
|
|
53
45
|
if (isPkg) {
|
|
54
46
|
console.log('Compiled binary mode: Skipping file reference processing (files assumed bundled)');
|
|
55
47
|
return payload;
|
|
@@ -80,6 +72,26 @@ class PayloadProcessor {
|
|
|
80
72
|
);
|
|
81
73
|
}
|
|
82
74
|
|
|
75
|
+
// Process masterDetail.detailConfig.detailQuery (digunakan oleh readComposite)
|
|
76
|
+
if (
|
|
77
|
+
processedPayload.masterDetail &&
|
|
78
|
+
processedPayload.masterDetail.detailConfig &&
|
|
79
|
+
this.isFileReference(processedPayload.masterDetail.detailConfig.detailQuery)
|
|
80
|
+
) {
|
|
81
|
+
processedPayload.masterDetail = {
|
|
82
|
+
...processedPayload.masterDetail,
|
|
83
|
+
detailConfig: {
|
|
84
|
+
...processedPayload.masterDetail.detailConfig,
|
|
85
|
+
detailQuery: this.loadSqlFile(
|
|
86
|
+
processedPayload.masterDetail.detailConfig.detailQuery,
|
|
87
|
+
payloadDir,
|
|
88
|
+
'masterDetail.detailConfig.detailQuery',
|
|
89
|
+
options
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
|
|
83
95
|
// Process advancedQueries
|
|
84
96
|
if (processedPayload.advancedQueries && typeof processedPayload.advancedQueries === 'object') {
|
|
85
97
|
processedPayload.advancedQueries = this.processAdvancedQueries(
|
|
@@ -880,109 +892,6 @@ class PayloadProcessor {
|
|
|
880
892
|
return count;
|
|
881
893
|
}
|
|
882
894
|
|
|
883
|
-
/**
|
|
884
|
-
* Copy query files untuk preserve mode (baik JS maupun PKG)
|
|
885
|
-
* @param {Object} payload - Payload object
|
|
886
|
-
* @param {string} payloadPath - Path file payload
|
|
887
|
-
* @param {Object} options - Options dengan moduleName dan targetDir
|
|
888
|
-
*/
|
|
889
|
-
static copyQueryFilesForPreserveMode(payload, payloadPath, options = {}) {
|
|
890
|
-
const FileUtils = require('./file-utils');
|
|
891
|
-
const path = require('path');
|
|
892
|
-
|
|
893
|
-
if (!options.moduleName) {
|
|
894
|
-
console.warn('moduleName not available, skipping file copying');
|
|
895
|
-
return;
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
const payloadDir = path.dirname(payloadPath);
|
|
899
|
-
const filesToCopy = this.collectFileReferences(payload);
|
|
900
|
-
|
|
901
|
-
if (filesToCopy.length === 0) {
|
|
902
|
-
console.log('No file references found to copy');
|
|
903
|
-
return;
|
|
904
|
-
}
|
|
905
|
-
|
|
906
|
-
console.log(`Copying ${filesToCopy.length} query files for module: ${options.moduleName}`);
|
|
907
|
-
|
|
908
|
-
// Copy files menggunakan FileUtils
|
|
909
|
-
try {
|
|
910
|
-
FileUtils.copyQueryFiles(filesToCopy, payloadDir, options.moduleName);
|
|
911
|
-
console.log(`Query files copied successfully for ${options.moduleName}`);
|
|
912
|
-
} catch (error) {
|
|
913
|
-
console.error(`Error copying query files: ${error.message}`);
|
|
914
|
-
throw error;
|
|
915
|
-
}
|
|
916
|
-
}
|
|
917
|
-
|
|
918
|
-
/**
|
|
919
|
-
* Collect semua file references dari payload
|
|
920
|
-
* @param {Object} payload - Payload object
|
|
921
|
-
* @returns {Array} Array of file references
|
|
922
|
-
*/
|
|
923
|
-
static collectFileReferences(payload) {
|
|
924
|
-
const fileReferences = [];
|
|
925
|
-
|
|
926
|
-
// Check datatablesQuery
|
|
927
|
-
if (payload.datatablesQuery && this.isFileReference(payload.datatablesQuery)) {
|
|
928
|
-
fileReferences.push(payload.datatablesQuery);
|
|
929
|
-
}
|
|
930
|
-
|
|
931
|
-
// Check viewQuery
|
|
932
|
-
if (payload.viewQuery && this.isFileReference(payload.viewQuery)) {
|
|
933
|
-
fileReferences.push(payload.viewQuery);
|
|
934
|
-
}
|
|
935
|
-
|
|
936
|
-
// Check advancedQueries
|
|
937
|
-
if (payload.advancedQueries && typeof payload.advancedQueries === 'object') {
|
|
938
|
-
for (const query of Object.values(payload.advancedQueries)) {
|
|
939
|
-
if (this.isFileReference(query)) {
|
|
940
|
-
fileReferences.push(query);
|
|
941
|
-
}
|
|
942
|
-
}
|
|
943
|
-
}
|
|
944
|
-
|
|
945
|
-
// Check nestedQueries
|
|
946
|
-
if (payload.nestedQueries && Array.isArray(payload.nestedQueries)) {
|
|
947
|
-
for (const nested of payload.nestedQueries) {
|
|
948
|
-
if (nested.query && this.isFileReference(nested.query)) {
|
|
949
|
-
fileReferences.push(nested.query);
|
|
950
|
-
}
|
|
951
|
-
}
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
// Check masterDetail.detailConfig.detailQuery (digunakan oleh readComposite)
|
|
955
|
-
if (
|
|
956
|
-
payload.masterDetail &&
|
|
957
|
-
payload.masterDetail.detailConfig &&
|
|
958
|
-
typeof payload.masterDetail.detailConfig.detailQuery === 'string' &&
|
|
959
|
-
this.isFileReference(payload.masterDetail.detailConfig.detailQuery)
|
|
960
|
-
) {
|
|
961
|
-
fileReferences.push(payload.masterDetail.detailConfig.detailQuery);
|
|
962
|
-
}
|
|
963
|
-
|
|
964
|
-
// Check customQueries
|
|
965
|
-
if (payload.customQueries && typeof payload.customQueries === 'object') {
|
|
966
|
-
for (const queries of Object.values(payload.customQueries)) {
|
|
967
|
-
if (Array.isArray(queries)) {
|
|
968
|
-
for (const query of queries) {
|
|
969
|
-
if (typeof query === 'object' && query.query && this.isFileReference(query.query)) {
|
|
970
|
-
fileReferences.push(query.query);
|
|
971
|
-
}
|
|
972
|
-
}
|
|
973
|
-
} else if (typeof queries === 'object') {
|
|
974
|
-
for (const subQuery of Object.values(queries)) {
|
|
975
|
-
if (this.isFileReference(subQuery)) {
|
|
976
|
-
fileReferences.push(subQuery);
|
|
977
|
-
}
|
|
978
|
-
}
|
|
979
|
-
}
|
|
980
|
-
}
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
// Remove duplicates
|
|
984
|
-
return [...new Set(fileReferences)];
|
|
985
|
-
}
|
|
986
895
|
}
|
|
987
896
|
|
|
988
897
|
module.exports = PayloadProcessor;
|
package/integrity-manifest.json
CHANGED
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "4.3.
|
|
3
|
-
"generated": "2026-05-
|
|
2
|
+
"version": "4.3.4",
|
|
3
|
+
"generated": "2026-05-24T08:55:59.215Z",
|
|
4
4
|
"generator": "restforge-build-system",
|
|
5
5
|
"algorithm": "sha256",
|
|
6
6
|
"files": {
|
|
7
|
-
"server.js": "
|
|
8
|
-
"src/utils/license-client.js": "
|
|
9
|
-
"src/utils/trusted-keys.js": "
|
|
10
|
-
"cli/consumer.js": "
|
|
11
|
-
"src/utils/security-checks.js": "
|
|
12
|
-
"scripts/verify-integrity.js": "
|
|
7
|
+
"server.js": "a541483503b66baa6e0469b0f8e73f0e70fe88b2d6a57c6994d04c68507a2690",
|
|
8
|
+
"src/utils/license-client.js": "03046d6e1a996f315ae22cec97609f09baef51d02386e489ea129cbf60443cd6",
|
|
9
|
+
"src/utils/trusted-keys.js": "416fc5be0a3bae7ef2182ddc75729be9765925ff82c9537353094983b68b7227",
|
|
10
|
+
"cli/consumer.js": "76513a635092c822144fc30a387386231de168df8955da3726fcdfc271779ea3",
|
|
11
|
+
"src/utils/security-checks.js": "56f14123951dc6a2bba07f2895b2c999d639c3075e5313635173c2900f88f407",
|
|
12
|
+
"scripts/verify-integrity.js": "2ad808831357c4e3b3b6efd178c9e4aeadadab6cd43290c28fa3f4aa9c533a07",
|
|
13
13
|
"bin/restforge-hwinfo-linux": "d9a0071b0acf58a020e738a29837de356f531a5a84d363a2adc50563e3874b8f",
|
|
14
14
|
"bin/restforge-hwinfo-darwin": "d35b15d4e6d7bbdd8317490335ba9602ce5c218519898fa3eabe18cc9c5bb1b6",
|
|
15
15
|
"bin/restforge-hwinfo.exe": "351fa4d33459d30de804ee78deb5eb977b77b688fe1b7b461cdaa214a82f145e",
|
|
16
|
-
"generators/lib/templates/mysql-template.js": "
|
|
17
|
-
"generators/lib/templates/oracle-template.js": "
|
|
18
|
-
"generators/lib/templates/postgres-template.js": "
|
|
19
|
-
"generators/lib/templates/sqlite-template.js": "
|
|
20
|
-
"generators/lib/templates/dashboard-catalog.js": "
|
|
21
|
-
"generators/lib/templates/dbschema-catalog.js": "
|
|
22
|
-
"generators/lib/templates/field-validation-catalog.js": "
|
|
23
|
-
"generators/lib/templates/query-declarative-catalog.js": "
|
|
24
|
-
"generators/lib/templates/db-connection-env.js": "
|
|
16
|
+
"generators/lib/templates/mysql-template.js": "8fd769a6f95221304dce32fa290ab277814d8295714d5ca14ec0db236627c7eb",
|
|
17
|
+
"generators/lib/templates/oracle-template.js": "ed34df2f73cced76a9ac354f7f2634f327caa9c6bf3cfc34e04452ecf750eb8c",
|
|
18
|
+
"generators/lib/templates/postgres-template.js": "222e43e24c3dc1c3689104e4ec9ad66ade134f312990ac358ad5078c93833835",
|
|
19
|
+
"generators/lib/templates/sqlite-template.js": "bc96cfb1bb4a296e909b10e848d7937ac27c81d0bc8ea486337ef06dc1577414",
|
|
20
|
+
"generators/lib/templates/dashboard-catalog.js": "8187cd98803de8dfab3a4ddd90ec25cf19b5d5b6dbe47302b56dbe63e5f259b0",
|
|
21
|
+
"generators/lib/templates/dbschema-catalog.js": "327e8d5244759d628a7ecf04cee961d728834784466801a83b154b7dac374abc",
|
|
22
|
+
"generators/lib/templates/field-validation-catalog.js": "79d2ad274531a307b09c5fc549d90807c97a7eb2b03a179354a29e8218b1fc8e",
|
|
23
|
+
"generators/lib/templates/query-declarative-catalog.js": "488726e976d6a4dd7e0d1e14fb8daea8b666391eb2424e75682a418f62c55b68",
|
|
24
|
+
"generators/lib/templates/db-connection-env.js": "e2cc385a4fa8d7a4332ed47e21509e401d88ddb7104295a6c2e54b17f55f7382"
|
|
25
25
|
},
|
|
26
26
|
"stats": {
|
|
27
27
|
"totalFiles": 18,
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"signature": {
|
|
32
32
|
"algorithm": "ed25519",
|
|
33
33
|
"keyId": "rfs-manifest-2026-05",
|
|
34
|
-
"value": "
|
|
34
|
+
"value": "5c3fc721c306b53950d2cdf0d0407a163eb334ec41b1c954ddc3af428385277dd2bfe6e2da3cffeebee3a82c082fcc18d0c858c743a3e1898684bd4e3d7b700d",
|
|
35
35
|
"scope": "canonical-json(manifest-without-signature)"
|
|
36
36
|
}
|
|
37
37
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@restforgejs/platform",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.4",
|
|
4
4
|
"description": "RESTForge Platform — Schema-driven backend framework and code generator for full-stack Node.js applications. Generates production backend APIs with multi-database support (PostgreSQL, MySQL, Oracle). A platform builder and backend runtime, not an API testing or client tool.",
|
|
5
5
|
"main": "server.js",
|
|
6
6
|
"bin": {
|