@restforgejs/platform 4.3.8 → 5.0.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.
- package/bin/sdf-tools.exe +0 -0
- package/build-info.json +2 -2
- package/cli/consumer-deploy.js +1 -1
- package/cli/consumer.js +1 -1
- package/generators/cli/payload/migrate.js +96 -96
- package/generators/lib/dbschema-kit/apply-engine.js +211 -46
- package/generators/lib/dbschema-kit/diff-engine.js +14 -2
- package/generators/lib/dbschema-kit/emitters/alter-table.js +96 -2
- package/generators/lib/dbschema-kit/introspect-mapper.js +9 -0
- package/generators/lib/migrate/backend-payload-migrator.js +221 -221
- package/generators/lib/migrate/field-type-resolver.js +319 -319
- package/generators/lib/migrate/label-generator.js +38 -38
- package/generators/lib/migrate/migrate-runner.js +187 -187
- package/generators/lib/migrate/naming.js +43 -43
- package/generators/lib/migrate/sql-parser.js +124 -124
- 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/integrity-manifest.json +18 -18
- package/node_modules/brace-expansion/index.js +1 -1
- package/node_modules/brace-expansion/package.json +1 -1
- package/node_modules/dayjs/CHANGELOG.md +7 -0
- package/node_modules/dayjs/README.md +12 -10
- package/node_modules/dayjs/dayjs.min.js +1 -1
- package/node_modules/dayjs/esm/constant.js +1 -1
- package/node_modules/dayjs/esm/plugin/duration/index.js +5 -4
- package/node_modules/dayjs/locale.json +1 -1
- package/node_modules/dayjs/package.json +2 -2
- package/node_modules/dayjs/plugin/duration.js +1 -1
- package/node_modules/tmp/lib/tmp.js +37 -7
- package/node_modules/tmp/package.json +4 -16
- 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
|
@@ -1,38 +1,38 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Port dari packages/designer/src/migrators/label_generator.rs.
|
|
5
|
-
* Generator label field dari snake_case ke human-readable format.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
const { snakeToTitle } = require('./naming');
|
|
9
|
-
|
|
10
|
-
const LABEL_OVERRIDES = {
|
|
11
|
-
is_active: 'Status',
|
|
12
|
-
uom: 'UOM',
|
|
13
|
-
sku: 'SKU',
|
|
14
|
-
email: 'Email',
|
|
15
|
-
phone: 'Phone',
|
|
16
|
-
url: 'URL',
|
|
17
|
-
ip: 'IP',
|
|
18
|
-
id: 'ID'
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
function generateLabel(fieldName, isForeignKey) {
|
|
22
|
-
const name = String(fieldName || '');
|
|
23
|
-
|
|
24
|
-
if (Object.prototype.hasOwnProperty.call(LABEL_OVERRIDES, name)) {
|
|
25
|
-
return LABEL_OVERRIDES[name];
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
if (isForeignKey && name.endsWith('_id')) {
|
|
29
|
-
const stripped = name.slice(0, -3);
|
|
30
|
-
if (stripped.length > 0) {
|
|
31
|
-
return snakeToTitle(stripped);
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return snakeToTitle(name);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
module.exports = { generateLabel };
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Port dari packages/designer/src/migrators/label_generator.rs.
|
|
5
|
+
* Generator label field dari snake_case ke human-readable format.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const { snakeToTitle } = require('./naming');
|
|
9
|
+
|
|
10
|
+
const LABEL_OVERRIDES = {
|
|
11
|
+
is_active: 'Status',
|
|
12
|
+
uom: 'UOM',
|
|
13
|
+
sku: 'SKU',
|
|
14
|
+
email: 'Email',
|
|
15
|
+
phone: 'Phone',
|
|
16
|
+
url: 'URL',
|
|
17
|
+
ip: 'IP',
|
|
18
|
+
id: 'ID'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function generateLabel(fieldName, isForeignKey) {
|
|
22
|
+
const name = String(fieldName || '');
|
|
23
|
+
|
|
24
|
+
if (Object.prototype.hasOwnProperty.call(LABEL_OVERRIDES, name)) {
|
|
25
|
+
return LABEL_OVERRIDES[name];
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (isForeignKey && name.endsWith('_id')) {
|
|
29
|
+
const stripped = name.slice(0, -3);
|
|
30
|
+
if (stripped.length > 0) {
|
|
31
|
+
return snakeToTitle(stripped);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
return snakeToTitle(name);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
module.exports = { generateLabel };
|
|
@@ -1,187 +1,187 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Migrate Runner — orchestrator untuk command `payload migrate`.
|
|
5
|
-
*
|
|
6
|
-
* Tanggung jawab:
|
|
7
|
-
* 1. Resolve file config (.env) via cascade lookup standar platform
|
|
8
|
-
* 2. Baca SERVER_ADDRESS dan SERVER_PORT dari config → konstruksi apiBaseUrl
|
|
9
|
-
* dengan format `http://{host}:{port}/api/{project}`
|
|
10
|
-
* 3. Load file payload backend (RDF) dari --name (relative ke cwd/payload/
|
|
11
|
-
* atau absolute)
|
|
12
|
-
* 4. Jalankan migrasi RDF → UDF via BackendPayloadMigrator
|
|
13
|
-
* 5. Tulis output JSON ke directory --output (file name = basename tanpa ext +
|
|
14
|
-
* suffix `-app.json`)
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
const fs = require('fs');
|
|
18
|
-
const path = require('path');
|
|
19
|
-
|
|
20
|
-
const { resolveConfig, printDefaultConfigWarning } = require('../utils/config-resolver');
|
|
21
|
-
const { readEnvFile } = require('../utils/env-manager');
|
|
22
|
-
const { migrate } = require('./backend-payload-migrator');
|
|
23
|
-
|
|
24
|
-
const DEFAULT_APP_NAME = 'My Application';
|
|
25
|
-
const DEFAULT_APP_CODE_FALLBACK = 'my-app';
|
|
26
|
-
const DEFAULT_PLUGIN = 'vanilla-js-basic';
|
|
27
|
-
const DEFAULT_HOST = '127.0.0.1';
|
|
28
|
-
const DEFAULT_BACKEND_PORT = 3000;
|
|
29
|
-
const DEFAULT_FRONTEND_PORT = 8000;
|
|
30
|
-
|
|
31
|
-
function resolveInputPath(nameArg, cwd) {
|
|
32
|
-
if (path.isAbsolute(nameArg)) return nameArg;
|
|
33
|
-
const direct = path.resolve(cwd, nameArg);
|
|
34
|
-
if (fs.existsSync(direct)) return direct;
|
|
35
|
-
const inPayload = path.resolve(cwd, 'payload', nameArg);
|
|
36
|
-
if (fs.existsSync(inPayload)) return inPayload;
|
|
37
|
-
// Fallback: return cwd-resolved path so caller dapat tampilkan error path
|
|
38
|
-
return direct;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function buildOutputPath(outputArg, inputBaseName, cwd) {
|
|
42
|
-
const targetFileName = inputBaseName;
|
|
43
|
-
|
|
44
|
-
if (!outputArg) {
|
|
45
|
-
const defaultDir = path.resolve(cwd, 'frontend', 'payload');
|
|
46
|
-
return path.join(defaultDir, targetFileName);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
const abs = path.isAbsolute(outputArg) ? outputArg : path.resolve(cwd, outputArg);
|
|
50
|
-
|
|
51
|
-
// Treat as file path bila berakhiran .json (case insensitive)
|
|
52
|
-
if (/\.json$/i.test(abs)) return abs;
|
|
53
|
-
|
|
54
|
-
// Else treat as directory
|
|
55
|
-
return path.join(abs, targetFileName);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function readServerConfig(envFilePath) {
|
|
59
|
-
const { data } = readEnvFile(envFilePath);
|
|
60
|
-
const host = (typeof data.SERVER_ADDRESS === 'string' && data.SERVER_ADDRESS.length > 0)
|
|
61
|
-
? data.SERVER_ADDRESS
|
|
62
|
-
: DEFAULT_HOST;
|
|
63
|
-
const portRaw = (typeof data.SERVER_PORT === 'string' && data.SERVER_PORT.length > 0)
|
|
64
|
-
? data.SERVER_PORT
|
|
65
|
-
: '';
|
|
66
|
-
const portNum = parseInt(portRaw, 10);
|
|
67
|
-
const port = Number.isFinite(portNum) && portNum > 0 ? portNum : DEFAULT_BACKEND_PORT;
|
|
68
|
-
return { host, port };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function buildApiBaseUrl(host, port, project) {
|
|
72
|
-
const projectSegment = project ? `/${project}` : '';
|
|
73
|
-
return `http://${host}:${port}/api${projectSegment}`;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
async function run(args) {
|
|
77
|
-
const cwd = process.cwd();
|
|
78
|
-
|
|
79
|
-
if (!args.name || typeof args.name !== 'string') {
|
|
80
|
-
const err = new Error('Flag --name is required (backend payload file name, e.g. visitors.json)');
|
|
81
|
-
err.exitCode = 2;
|
|
82
|
-
throw err;
|
|
83
|
-
}
|
|
84
|
-
if (!args.project || typeof args.project !== 'string') {
|
|
85
|
-
const err = new Error('Flag --project is required (project name used in apiBaseUrl)');
|
|
86
|
-
err.exitCode = 2;
|
|
87
|
-
throw err;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const resolved = resolveConfig(args.config, cwd);
|
|
91
|
-
if (!resolved) {
|
|
92
|
-
const err = new Error('Flag --config is required, or set a default config via `restforge config set-default`');
|
|
93
|
-
err.exitCode = 2;
|
|
94
|
-
throw err;
|
|
95
|
-
}
|
|
96
|
-
if (resolved.source === 'default') {
|
|
97
|
-
printDefaultConfigWarning(resolved.defaultName);
|
|
98
|
-
}
|
|
99
|
-
if (!fs.existsSync(resolved.path)) {
|
|
100
|
-
const err = new Error(`Config file not found: ${resolved.path}`);
|
|
101
|
-
err.exitCode = 3;
|
|
102
|
-
throw err;
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const { host, port: backendPort } = readServerConfig(resolved.path);
|
|
106
|
-
const project = args.project;
|
|
107
|
-
const apiBaseUrl = buildApiBaseUrl(host, backendPort, project);
|
|
108
|
-
|
|
109
|
-
const frontendPortArg = (typeof args.port === 'number' && Number.isFinite(args.port) && args.port > 0)
|
|
110
|
-
? args.port
|
|
111
|
-
: DEFAULT_FRONTEND_PORT;
|
|
112
|
-
|
|
113
|
-
const appName = args.appName || DEFAULT_APP_NAME;
|
|
114
|
-
const appCode = args.appCode || project || DEFAULT_APP_CODE_FALLBACK;
|
|
115
|
-
const plugin = args.plugin || DEFAULT_PLUGIN;
|
|
116
|
-
|
|
117
|
-
const inputPath = resolveInputPath(args.name, cwd);
|
|
118
|
-
if (!fs.existsSync(inputPath)) {
|
|
119
|
-
const err = new Error(`Input payload file not found: ${inputPath}`);
|
|
120
|
-
err.exitCode = 3;
|
|
121
|
-
throw err;
|
|
122
|
-
}
|
|
123
|
-
let backendPayload;
|
|
124
|
-
try {
|
|
125
|
-
const content = fs.readFileSync(inputPath, 'utf8');
|
|
126
|
-
backendPayload = JSON.parse(content);
|
|
127
|
-
} catch (e) {
|
|
128
|
-
const err = new Error(`Failed to parse JSON from ${inputPath}: ${e.message}`);
|
|
129
|
-
err.exitCode = 3;
|
|
130
|
-
throw err;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const inputBaseName = path.basename(inputPath);
|
|
134
|
-
const outputPath = buildOutputPath(args.output, inputBaseName, cwd);
|
|
135
|
-
|
|
136
|
-
if (fs.existsSync(outputPath) && !args.overwrite) {
|
|
137
|
-
const err = new Error(`Output file already exists: ${outputPath}. Use --overwrite to replace.`);
|
|
138
|
-
err.exitCode = 1;
|
|
139
|
-
throw err;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
const result = migrate([backendPayload], appName, appCode, plugin, apiBaseUrl, frontendPortArg);
|
|
143
|
-
if (!result.success) {
|
|
144
|
-
const msg = (result.errors && result.errors.length > 0)
|
|
145
|
-
? result.errors.join('; ')
|
|
146
|
-
: 'Migration failed without details';
|
|
147
|
-
const err = new Error(msg);
|
|
148
|
-
err.exitCode = 1;
|
|
149
|
-
throw err;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
const outputDir = path.dirname(outputPath);
|
|
153
|
-
if (!fs.existsSync(outputDir)) {
|
|
154
|
-
fs.mkdirSync(outputDir, { recursive: true });
|
|
155
|
-
}
|
|
156
|
-
fs.writeFileSync(outputPath, JSON.stringify(result.payload, null, 2), 'utf8');
|
|
157
|
-
|
|
158
|
-
const stdout = process.stdout;
|
|
159
|
-
stdout.write('============================================================\n');
|
|
160
|
-
stdout.write('PAYLOAD MIGRATE - RDF (backend) -> UDF (frontend)\n');
|
|
161
|
-
stdout.write('============================================================\n\n');
|
|
162
|
-
stdout.write(` Input : ${inputPath}\n`);
|
|
163
|
-
stdout.write(` Output : ${outputPath}\n`);
|
|
164
|
-
stdout.write(` Project : ${project}\n`);
|
|
165
|
-
stdout.write(` apiBaseUrl : ${apiBaseUrl}\n`);
|
|
166
|
-
stdout.write(` Backend port : ${backendPort}\n`);
|
|
167
|
-
stdout.write(` Frontend port: ${frontendPortArg}\n`);
|
|
168
|
-
stdout.write(` Pages : ${result.pageResults.length}\n\n`);
|
|
169
|
-
for (const pr of result.pageResults) {
|
|
170
|
-
stdout.write(` [OK] ${pr.pageId}: ${pr.fieldCount} field(s), ${pr.tableColCount} table column(s)\n`);
|
|
171
|
-
}
|
|
172
|
-
if (result.warnings && result.warnings.length > 0) {
|
|
173
|
-
stdout.write('\n Warnings:\n');
|
|
174
|
-
for (const w of result.warnings) {
|
|
175
|
-
stdout.write(` - ${w}\n`);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
stdout.write('\n Migration completed successfully.\n');
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
module.exports = {
|
|
182
|
-
run,
|
|
183
|
-
resolveInputPath,
|
|
184
|
-
buildOutputPath,
|
|
185
|
-
readServerConfig,
|
|
186
|
-
buildApiBaseUrl
|
|
187
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Migrate Runner — orchestrator untuk command `payload migrate`.
|
|
5
|
+
*
|
|
6
|
+
* Tanggung jawab:
|
|
7
|
+
* 1. Resolve file config (.env) via cascade lookup standar platform
|
|
8
|
+
* 2. Baca SERVER_ADDRESS dan SERVER_PORT dari config → konstruksi apiBaseUrl
|
|
9
|
+
* dengan format `http://{host}:{port}/api/{project}`
|
|
10
|
+
* 3. Load file payload backend (RDF) dari --name (relative ke cwd/payload/
|
|
11
|
+
* atau absolute)
|
|
12
|
+
* 4. Jalankan migrasi RDF → UDF via BackendPayloadMigrator
|
|
13
|
+
* 5. Tulis output JSON ke directory --output (file name = basename tanpa ext +
|
|
14
|
+
* suffix `-app.json`)
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
const fs = require('fs');
|
|
18
|
+
const path = require('path');
|
|
19
|
+
|
|
20
|
+
const { resolveConfig, printDefaultConfigWarning } = require('../utils/config-resolver');
|
|
21
|
+
const { readEnvFile } = require('../utils/env-manager');
|
|
22
|
+
const { migrate } = require('./backend-payload-migrator');
|
|
23
|
+
|
|
24
|
+
const DEFAULT_APP_NAME = 'My Application';
|
|
25
|
+
const DEFAULT_APP_CODE_FALLBACK = 'my-app';
|
|
26
|
+
const DEFAULT_PLUGIN = 'vanilla-js-basic';
|
|
27
|
+
const DEFAULT_HOST = '127.0.0.1';
|
|
28
|
+
const DEFAULT_BACKEND_PORT = 3000;
|
|
29
|
+
const DEFAULT_FRONTEND_PORT = 8000;
|
|
30
|
+
|
|
31
|
+
function resolveInputPath(nameArg, cwd) {
|
|
32
|
+
if (path.isAbsolute(nameArg)) return nameArg;
|
|
33
|
+
const direct = path.resolve(cwd, nameArg);
|
|
34
|
+
if (fs.existsSync(direct)) return direct;
|
|
35
|
+
const inPayload = path.resolve(cwd, 'payload', nameArg);
|
|
36
|
+
if (fs.existsSync(inPayload)) return inPayload;
|
|
37
|
+
// Fallback: return cwd-resolved path so caller dapat tampilkan error path
|
|
38
|
+
return direct;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function buildOutputPath(outputArg, inputBaseName, cwd) {
|
|
42
|
+
const targetFileName = inputBaseName;
|
|
43
|
+
|
|
44
|
+
if (!outputArg) {
|
|
45
|
+
const defaultDir = path.resolve(cwd, 'frontend', 'payload');
|
|
46
|
+
return path.join(defaultDir, targetFileName);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const abs = path.isAbsolute(outputArg) ? outputArg : path.resolve(cwd, outputArg);
|
|
50
|
+
|
|
51
|
+
// Treat as file path bila berakhiran .json (case insensitive)
|
|
52
|
+
if (/\.json$/i.test(abs)) return abs;
|
|
53
|
+
|
|
54
|
+
// Else treat as directory
|
|
55
|
+
return path.join(abs, targetFileName);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function readServerConfig(envFilePath) {
|
|
59
|
+
const { data } = readEnvFile(envFilePath);
|
|
60
|
+
const host = (typeof data.SERVER_ADDRESS === 'string' && data.SERVER_ADDRESS.length > 0)
|
|
61
|
+
? data.SERVER_ADDRESS
|
|
62
|
+
: DEFAULT_HOST;
|
|
63
|
+
const portRaw = (typeof data.SERVER_PORT === 'string' && data.SERVER_PORT.length > 0)
|
|
64
|
+
? data.SERVER_PORT
|
|
65
|
+
: '';
|
|
66
|
+
const portNum = parseInt(portRaw, 10);
|
|
67
|
+
const port = Number.isFinite(portNum) && portNum > 0 ? portNum : DEFAULT_BACKEND_PORT;
|
|
68
|
+
return { host, port };
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function buildApiBaseUrl(host, port, project) {
|
|
72
|
+
const projectSegment = project ? `/${project}` : '';
|
|
73
|
+
return `http://${host}:${port}/api${projectSegment}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function run(args) {
|
|
77
|
+
const cwd = process.cwd();
|
|
78
|
+
|
|
79
|
+
if (!args.name || typeof args.name !== 'string') {
|
|
80
|
+
const err = new Error('Flag --name is required (backend payload file name, e.g. visitors.json)');
|
|
81
|
+
err.exitCode = 2;
|
|
82
|
+
throw err;
|
|
83
|
+
}
|
|
84
|
+
if (!args.project || typeof args.project !== 'string') {
|
|
85
|
+
const err = new Error('Flag --project is required (project name used in apiBaseUrl)');
|
|
86
|
+
err.exitCode = 2;
|
|
87
|
+
throw err;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const resolved = resolveConfig(args.config, cwd);
|
|
91
|
+
if (!resolved) {
|
|
92
|
+
const err = new Error('Flag --config is required, or set a default config via `restforge config set-default`');
|
|
93
|
+
err.exitCode = 2;
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
if (resolved.source === 'default') {
|
|
97
|
+
printDefaultConfigWarning(resolved.defaultName);
|
|
98
|
+
}
|
|
99
|
+
if (!fs.existsSync(resolved.path)) {
|
|
100
|
+
const err = new Error(`Config file not found: ${resolved.path}`);
|
|
101
|
+
err.exitCode = 3;
|
|
102
|
+
throw err;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
const { host, port: backendPort } = readServerConfig(resolved.path);
|
|
106
|
+
const project = args.project;
|
|
107
|
+
const apiBaseUrl = buildApiBaseUrl(host, backendPort, project);
|
|
108
|
+
|
|
109
|
+
const frontendPortArg = (typeof args.port === 'number' && Number.isFinite(args.port) && args.port > 0)
|
|
110
|
+
? args.port
|
|
111
|
+
: DEFAULT_FRONTEND_PORT;
|
|
112
|
+
|
|
113
|
+
const appName = args.appName || DEFAULT_APP_NAME;
|
|
114
|
+
const appCode = args.appCode || project || DEFAULT_APP_CODE_FALLBACK;
|
|
115
|
+
const plugin = args.plugin || DEFAULT_PLUGIN;
|
|
116
|
+
|
|
117
|
+
const inputPath = resolveInputPath(args.name, cwd);
|
|
118
|
+
if (!fs.existsSync(inputPath)) {
|
|
119
|
+
const err = new Error(`Input payload file not found: ${inputPath}`);
|
|
120
|
+
err.exitCode = 3;
|
|
121
|
+
throw err;
|
|
122
|
+
}
|
|
123
|
+
let backendPayload;
|
|
124
|
+
try {
|
|
125
|
+
const content = fs.readFileSync(inputPath, 'utf8');
|
|
126
|
+
backendPayload = JSON.parse(content);
|
|
127
|
+
} catch (e) {
|
|
128
|
+
const err = new Error(`Failed to parse JSON from ${inputPath}: ${e.message}`);
|
|
129
|
+
err.exitCode = 3;
|
|
130
|
+
throw err;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const inputBaseName = path.basename(inputPath);
|
|
134
|
+
const outputPath = buildOutputPath(args.output, inputBaseName, cwd);
|
|
135
|
+
|
|
136
|
+
if (fs.existsSync(outputPath) && !args.overwrite) {
|
|
137
|
+
const err = new Error(`Output file already exists: ${outputPath}. Use --overwrite to replace.`);
|
|
138
|
+
err.exitCode = 1;
|
|
139
|
+
throw err;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const result = migrate([backendPayload], appName, appCode, plugin, apiBaseUrl, frontendPortArg);
|
|
143
|
+
if (!result.success) {
|
|
144
|
+
const msg = (result.errors && result.errors.length > 0)
|
|
145
|
+
? result.errors.join('; ')
|
|
146
|
+
: 'Migration failed without details';
|
|
147
|
+
const err = new Error(msg);
|
|
148
|
+
err.exitCode = 1;
|
|
149
|
+
throw err;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const outputDir = path.dirname(outputPath);
|
|
153
|
+
if (!fs.existsSync(outputDir)) {
|
|
154
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
155
|
+
}
|
|
156
|
+
fs.writeFileSync(outputPath, JSON.stringify(result.payload, null, 2), 'utf8');
|
|
157
|
+
|
|
158
|
+
const stdout = process.stdout;
|
|
159
|
+
stdout.write('============================================================\n');
|
|
160
|
+
stdout.write('PAYLOAD MIGRATE - RDF (backend) -> UDF (frontend)\n');
|
|
161
|
+
stdout.write('============================================================\n\n');
|
|
162
|
+
stdout.write(` Input : ${inputPath}\n`);
|
|
163
|
+
stdout.write(` Output : ${outputPath}\n`);
|
|
164
|
+
stdout.write(` Project : ${project}\n`);
|
|
165
|
+
stdout.write(` apiBaseUrl : ${apiBaseUrl}\n`);
|
|
166
|
+
stdout.write(` Backend port : ${backendPort}\n`);
|
|
167
|
+
stdout.write(` Frontend port: ${frontendPortArg}\n`);
|
|
168
|
+
stdout.write(` Pages : ${result.pageResults.length}\n\n`);
|
|
169
|
+
for (const pr of result.pageResults) {
|
|
170
|
+
stdout.write(` [OK] ${pr.pageId}: ${pr.fieldCount} field(s), ${pr.tableColCount} table column(s)\n`);
|
|
171
|
+
}
|
|
172
|
+
if (result.warnings && result.warnings.length > 0) {
|
|
173
|
+
stdout.write('\n Warnings:\n');
|
|
174
|
+
for (const w of result.warnings) {
|
|
175
|
+
stdout.write(` - ${w}\n`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
stdout.write('\n Migration completed successfully.\n');
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
module.exports = {
|
|
182
|
+
run,
|
|
183
|
+
resolveInputPath,
|
|
184
|
+
buildOutputPath,
|
|
185
|
+
readServerConfig,
|
|
186
|
+
buildApiBaseUrl
|
|
187
|
+
};
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Port dari packages/designer/src/utils/naming.rs.
|
|
5
|
-
* Konversi naming convention: snake_case -> kebab-case / camelCase / PascalCase / Title Case.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
function capitalize(s) {
|
|
9
|
-
if (!s || s.length === 0) return '';
|
|
10
|
-
return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
function snakeToKebab(name) {
|
|
14
|
-
return String(name || '').replace(/_/g, '-');
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function snakeToCamel(name) {
|
|
18
|
-
const parts = String(name || '').split('_');
|
|
19
|
-
if (parts.length === 0) return '';
|
|
20
|
-
const first = parts.shift();
|
|
21
|
-
return first + parts.map(capitalize).join('');
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
function snakeToPascal(name) {
|
|
25
|
-
return String(name || '').split('_').map(capitalize).join('');
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
function snakeToTitle(name) {
|
|
29
|
-
return String(name || '').split('_').map(capitalize).join(' ');
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function toClassName(appCode) {
|
|
33
|
-
return String(appCode || '').replace(/_/g, '-').split('-').map(capitalize).join('');
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
module.exports = {
|
|
37
|
-
capitalize,
|
|
38
|
-
snakeToKebab,
|
|
39
|
-
snakeToCamel,
|
|
40
|
-
snakeToPascal,
|
|
41
|
-
snakeToTitle,
|
|
42
|
-
toClassName
|
|
43
|
-
};
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Port dari packages/designer/src/utils/naming.rs.
|
|
5
|
+
* Konversi naming convention: snake_case -> kebab-case / camelCase / PascalCase / Title Case.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
function capitalize(s) {
|
|
9
|
+
if (!s || s.length === 0) return '';
|
|
10
|
+
return s.charAt(0).toUpperCase() + s.slice(1).toLowerCase();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function snakeToKebab(name) {
|
|
14
|
+
return String(name || '').replace(/_/g, '-');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function snakeToCamel(name) {
|
|
18
|
+
const parts = String(name || '').split('_');
|
|
19
|
+
if (parts.length === 0) return '';
|
|
20
|
+
const first = parts.shift();
|
|
21
|
+
return first + parts.map(capitalize).join('');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function snakeToPascal(name) {
|
|
25
|
+
return String(name || '').split('_').map(capitalize).join('');
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function snakeToTitle(name) {
|
|
29
|
+
return String(name || '').split('_').map(capitalize).join(' ');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function toClassName(appCode) {
|
|
33
|
+
return String(appCode || '').replace(/_/g, '-').split('-').map(capitalize).join('');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
module.exports = {
|
|
37
|
+
capitalize,
|
|
38
|
+
snakeToKebab,
|
|
39
|
+
snakeToCamel,
|
|
40
|
+
snakeToPascal,
|
|
41
|
+
snakeToTitle,
|
|
42
|
+
toClassName
|
|
43
|
+
};
|