@restforgejs/platform 4.1.1 → 4.2.8

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.
Files changed (178) hide show
  1. package/bin/sdf-tools.exe +0 -0
  2. package/build-info.json +2 -2
  3. package/cli/consumer-deploy.js +1 -1
  4. package/cli/consumer.js +1 -1
  5. package/generators/cli/endpoint/create.js +42 -3
  6. package/generators/cli/schema/apply.js +525 -0
  7. package/generators/cli/schema/diff.js +321 -0
  8. package/generators/cli/schema/generate-ddl.js +7 -10
  9. package/generators/cli/schema/init.js +95 -172
  10. package/generators/cli/schema/migrate.js +10 -16
  11. package/generators/cli/schema/models.js +8 -12
  12. package/generators/cli/schema/template.js +222 -0
  13. package/generators/cli/schema/validate.js +8 -12
  14. package/generators/cli-entry.js +17 -2
  15. package/generators/lib/dbschema-kit/apply-engine.js +582 -0
  16. package/generators/lib/dbschema-kit/diff-engine.js +703 -0
  17. package/generators/lib/dbschema-kit/diff-reporter.js +272 -0
  18. package/generators/lib/dbschema-kit/emitters/alter-table.js +275 -0
  19. package/generators/lib/payload/endpoint-schema-validator.js +171 -0
  20. package/generators/lib/payload/payload-runner.js +137 -220
  21. package/generators/lib/payload/schema-diff.js +277 -0
  22. package/generators/lib/utils/audit-columns.js +181 -0
  23. package/generators/lib/utils/cli-output.js +17 -0
  24. package/generators/lib/utils/database-introspector.js +16 -13
  25. package/integrity-manifest.json +8 -8
  26. package/package.json +1 -1
  27. package/scripts/verify-integrity.js +1 -1
  28. package/server.js +1 -1
  29. package/src/components/handlers/adjust_handler.js +1 -1
  30. package/src/components/handlers/audit_handler.js +1 -1
  31. package/src/components/handlers/delete_handler.js +1 -1
  32. package/src/components/handlers/export_handler.js +1 -1
  33. package/src/components/handlers/import_handler.js +1 -1
  34. package/src/components/handlers/insert_handler.js +1 -1
  35. package/src/components/handlers/update_handler.js +1 -1
  36. package/src/components/handlers/upload_handler.js +1 -1
  37. package/src/components/handlers/workflow_handler.js +1 -1
  38. package/src/components/integrations/webhook.js +1 -1
  39. package/src/consumers/baseConsumer.js +1 -1
  40. package/src/consumers/declarativeMapper.js +1 -1
  41. package/src/consumers/handlers/apiHandler.js +1 -1
  42. package/src/consumers/handlers/consoleHandler.js +1 -1
  43. package/src/consumers/handlers/databaseHandler.js +1 -1
  44. package/src/consumers/handlers/index.js +1 -1
  45. package/src/consumers/handlers/kafkaHandler.js +1 -1
  46. package/src/consumers/index.js +1 -1
  47. package/src/consumers/messageTransformer.js +1 -1
  48. package/src/consumers/validator.js +1 -1
  49. package/src/core/db/dialect/base-dialect.js +1 -1
  50. package/src/core/db/dialect/index.js +1 -1
  51. package/src/core/db/dialect/mysql-dialect.js +1 -1
  52. package/src/core/db/dialect/oracle-dialect.js +1 -1
  53. package/src/core/db/dialect/postgres-dialect.js +1 -1
  54. package/src/core/db/dialect/sqlite-dialect.js +1 -1
  55. package/src/core/db/flatten-helper.js +1 -1
  56. package/src/core/db/query-builder-error.js +1 -1
  57. package/src/core/db/query-builder.js +1 -1
  58. package/src/core/db/relation-helper.js +1 -1
  59. package/src/core/handlers/delete_handler.js +1 -1
  60. package/src/core/handlers/insert_handler.js +1 -1
  61. package/src/core/handlers/update_handler.js +1 -1
  62. package/src/core/models/base-model.js +1 -1
  63. package/src/core/utils/cache-manager.js +1 -1
  64. package/src/core/utils/component-engine.js +1 -1
  65. package/src/core/utils/context-builder.js +1 -1
  66. package/src/core/utils/datetime-formatter.js +1 -1
  67. package/src/core/utils/datetime-parser.js +1 -1
  68. package/src/core/utils/db.js +1 -1
  69. package/src/core/utils/logger.js +1 -1
  70. package/src/core/utils/payload-loader.js +1 -1
  71. package/src/core/utils/security-checks.js +1 -1
  72. package/src/middleware/body-options.js +1 -1
  73. package/src/middleware/cors.js +1 -1
  74. package/src/middleware/idempotency.js +1 -1
  75. package/src/middleware/rate-limiter.js +1 -1
  76. package/src/middleware/request-logger.js +1 -1
  77. package/src/middleware/security-headers.js +1 -1
  78. package/src/models/base-model-mysql.js +1 -1
  79. package/src/models/base-model-oracle.js +1 -1
  80. package/src/models/base-model-sqlite.js +1 -1
  81. package/src/models/base-model.js +1 -1
  82. package/src/pro/caching/redis-client.js +1 -1
  83. package/src/pro/caching/redis-helper.js +1 -1
  84. package/src/pro/consumers/baseConsumer.js +1 -1
  85. package/src/pro/consumers/declarativeMapper.js +1 -1
  86. package/src/pro/consumers/handlers/apiHandler.js +1 -1
  87. package/src/pro/consumers/handlers/consoleHandler.js +1 -1
  88. package/src/pro/consumers/handlers/databaseHandler.js +1 -1
  89. package/src/pro/consumers/handlers/index.js +1 -1
  90. package/src/pro/consumers/handlers/kafkaHandler.js +1 -1
  91. package/src/pro/consumers/index.js +1 -1
  92. package/src/pro/consumers/messageTransformer.js +1 -1
  93. package/src/pro/consumers/validator.js +1 -1
  94. package/src/pro/database/base-model-mysql.js +1 -1
  95. package/src/pro/database/base-model-oracle.js +1 -1
  96. package/src/pro/database/base-model-sqlite.js +1 -1
  97. package/src/pro/database/db-mysql.js +1 -1
  98. package/src/pro/database/db-oracle.js +1 -1
  99. package/src/pro/database/db-sqlite.js +1 -1
  100. package/src/pro/excel/excel-generator.js +1 -1
  101. package/src/pro/excel/excel-parser.js +1 -1
  102. package/src/pro/excel/export-service.js +1 -1
  103. package/src/pro/excel/export_handler.js +1 -1
  104. package/src/pro/excel/import-service.js +1 -1
  105. package/src/pro/excel/import-validator.js +1 -1
  106. package/src/pro/excel/import_handler.js +1 -1
  107. package/src/pro/excel/upsert-builder.js +1 -1
  108. package/src/pro/idgen/idgen-routes.js +1 -1
  109. package/src/pro/integrations/lookup-resolver.js +1 -1
  110. package/src/pro/integrations/upload-handler-v2.js +1 -1
  111. package/src/pro/integrations/upload-handler.js +1 -1
  112. package/src/pro/integrations/webhook.js +1 -1
  113. package/src/pro/locking/lock-routes.js +1 -1
  114. package/src/pro/locking/resource-lock-manager.js +1 -1
  115. package/src/pro/messaging/kafkaConsumerService.js +1 -1
  116. package/src/pro/messaging/kafkaService.js +1 -1
  117. package/src/pro/messaging/messagehubService.js +1 -1
  118. package/src/pro/messaging/rabbitmqService.js +1 -1
  119. package/src/pro/scheduler/job-manager.js +1 -1
  120. package/src/pro/scheduler/job-routes.js +1 -1
  121. package/src/pro/scheduler/job-validator.js +1 -1
  122. package/src/pro/storage/base-storage-provider.js +1 -1
  123. package/src/pro/storage/file-metadata-helper.js +1 -1
  124. package/src/pro/storage/index.js +1 -1
  125. package/src/pro/storage/local-storage-provider.js +1 -1
  126. package/src/pro/storage/s3-storage-provider.js +1 -1
  127. package/src/pro/storage/upload-cleanup-job.js +1 -1
  128. package/src/pro/storage/upload-cleanup-scheduler.js +1 -1
  129. package/src/pro/storage/upload-pending-tracker.js +1 -1
  130. package/src/pro/websocket/broadcast-helper.js +1 -1
  131. package/src/pro/websocket/index.js +1 -1
  132. package/src/pro/websocket/livesync-server.js +1 -1
  133. package/src/pro/websocket/ws-broadcaster.js +1 -1
  134. package/src/services/export-service.js +1 -1
  135. package/src/services/import-service.js +1 -1
  136. package/src/services/kafkaConsumerService.js +1 -1
  137. package/src/services/kafkaService.js +1 -1
  138. package/src/services/messagehubService.js +1 -1
  139. package/src/services/rabbitmqService.js +1 -1
  140. package/src/utils/cache-invalidation-registry.js +1 -1
  141. package/src/utils/cache-manager.js +1 -1
  142. package/src/utils/component-engine.js +1 -1
  143. package/src/utils/config-extractor.js +1 -1
  144. package/src/utils/consumerLogger.js +1 -1
  145. package/src/utils/context-builder.js +1 -1
  146. package/src/utils/dashboard-helpers.js +1 -1
  147. package/src/utils/dateHelper.js +1 -1
  148. package/src/utils/datetime-formatter.js +1 -1
  149. package/src/utils/datetime-parser.js +1 -1
  150. package/src/utils/db-bootstrap.js +1 -1
  151. package/src/utils/db-mysql.js +1 -1
  152. package/src/utils/db-oracle.js +1 -1
  153. package/src/utils/db-sqlite.js +1 -1
  154. package/src/utils/db.js +1 -1
  155. package/src/utils/demo-generator.js +1 -1
  156. package/src/utils/excel-generator.js +1 -1
  157. package/src/utils/excel-parser.js +1 -1
  158. package/src/utils/file-watcher.js +1 -1
  159. package/src/utils/id-generator.js +1 -1
  160. package/src/utils/idempotency-manager.js +1 -1
  161. package/src/utils/import-validator.js +1 -1
  162. package/src/utils/license-client.js +1 -1
  163. package/src/utils/lock-manager.js +1 -1
  164. package/src/utils/logger.js +1 -1
  165. package/src/utils/lookup-resolver.js +1 -1
  166. package/src/utils/payload-loader.js +1 -1
  167. package/src/utils/processor-response.js +1 -1
  168. package/src/utils/rabbitmq.js +1 -1
  169. package/src/utils/redis-client.js +1 -1
  170. package/src/utils/redis-helper.js +1 -1
  171. package/src/utils/request-scope.js +1 -1
  172. package/src/utils/security-checks.js +1 -1
  173. package/src/utils/service-resolver.js +1 -1
  174. package/src/utils/shutdown-coordinator.js +1 -1
  175. package/src/utils/trusted-keys.js +1 -1
  176. package/src/utils/upload-handler.js +1 -1
  177. package/src/utils/upsert-builder.js +1 -1
  178. package/src/utils/workflow-hook-executor.js +1 -1
@@ -22,8 +22,6 @@ const { splitStatements } = require('../../lib/dbschema-kit/statement-splitter')
22
22
  const { applyIfNotExistsModifier } = require('../../lib/dbschema-kit/statement-modifier');
23
23
  const { resolveConfig, printDefaultConfigWarning } = require('../../lib/utils/config-resolver');
24
24
 
25
- const DEFAULT_PATH = './schema';
26
-
27
25
  // Mapping dialect (internal naming di dbschema-kit) ke dbType (naming yang
28
26
  // dipakai db-bootstrap helper). Hanya postgres dan mysql yang relevan untuk
29
27
  // flow "create database if missing".
@@ -82,6 +80,11 @@ module.exports = {
82
80
  description: 'Apply schema definition ke database (load → validate → generate DDL → execute). Destruktif.',
83
81
  category: 'management',
84
82
  flags: {
83
+ path: {
84
+ type: 'string',
85
+ required: true,
86
+ description: 'Path file atau folder schema (mis. ./schema atau ./schema/users.js)'
87
+ },
85
88
  config: {
86
89
  type: 'string',
87
90
  required: true,
@@ -112,18 +115,10 @@ module.exports = {
112
115
  description: 'Khusus postgres/mysql. Otomatis buat database jika belum ada (skip konfirmasi interaktif). Berguna untuk mode non-interaktif (CI/CD).'
113
116
  }
114
117
  },
115
- positional: [
116
- {
117
- name: 'path',
118
- type: 'string',
119
- required: false,
120
- description: 'Path file atau folder schema (default: ./schema)'
121
- }
122
- ],
123
118
  examples: [
124
- 'npx restforge schema migrate ./schema --config=db.env --dry-run',
125
- 'npx restforge schema migrate ./schema --config=db.env',
126
- 'npx restforge schema migrate ./schema --config=db.env --auto-create-db'
119
+ 'npx restforge schema migrate --path=./schema --config=db.env --dry-run',
120
+ 'npx restforge schema migrate --path=./schema --config=db.env',
121
+ 'npx restforge schema migrate --path=./schema --config=db.env --auto-create-db'
127
122
  ],
128
123
  async handler(args) {
129
124
  const resolved = resolveConfig(args.config, process.cwd());
@@ -147,7 +142,7 @@ module.exports = {
147
142
  throw err;
148
143
  }
149
144
 
150
- const schemaPathArg = args.path || DEFAULT_PATH;
145
+ const schemaPathArg = args.path;
151
146
  const absPath = path.resolve(process.cwd(), schemaPathArg);
152
147
 
153
148
  if (!fs.existsSync(absPath)) {
@@ -258,8 +253,7 @@ module.exports = {
258
253
  }
259
254
 
260
255
  if (testErr && isDbNotExistsError(targetDbType, testErr)) {
261
- const schemaArg = args.path || DEFAULT_PATH;
262
- const hintParts = ['npx restforge schema migrate', schemaArg, `--config=${args.config}`];
256
+ const hintParts = ['npx restforge schema migrate', `--path=${args.path}`, `--config=${args.config}`];
263
257
  if (args.drop === true) hintParts.push('--drop=true');
264
258
  if (args['auto-create-db']) hintParts.push('--auto-create-db');
265
259
  const commandHint = hintParts.join(' ');
@@ -14,8 +14,6 @@
14
14
  const path = require('path');
15
15
  const { loadSchemaPath } = require('../../lib/dbschema-kit/loader');
16
16
 
17
- const DEFAULT_PATH = './schema';
18
-
19
17
  const COL_SCHEMA = 12;
20
18
  const COL_TABLE = 25;
21
19
  const COL_FIELDS = 8;
@@ -71,21 +69,19 @@ module.exports = {
71
69
  verb: 'models',
72
70
  description: 'Menampilkan daftar schema models dengan ringkasan struktur (fields, primary key, indexes, uniques, relations)',
73
71
  category: 'introspection',
74
- flags: {},
75
- positional: [
76
- {
77
- name: 'path',
72
+ flags: {
73
+ path: {
78
74
  type: 'string',
79
- required: false,
80
- description: 'Path folder schema (default: ./schema)'
75
+ required: true,
76
+ description: 'Path file atau folder schema (mis. ./schema atau ./schema/users.js)'
81
77
  }
82
- ],
78
+ },
83
79
  examples: [
84
- 'npx restforge schema models',
85
- 'npx restforge schema models ./schema'
80
+ 'npx restforge schema models --path=./schema',
81
+ 'npx restforge schema models --path=./custom-schema'
86
82
  ],
87
83
  async handler(args) {
88
- const schemaPath = args.path || DEFAULT_PATH;
84
+ const schemaPath = args.path;
89
85
  const absPath = path.resolve(process.cwd(), schemaPath);
90
86
 
91
87
  let models;
@@ -0,0 +1,222 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Contract: schema template
5
+ *
6
+ * Browse, preview, dan generate template schema dari koleksi RestForge Schema
7
+ * Reference (85 template, 30 domain, 33 section). Verb ini bertindak sebagai
8
+ * wrapper terhadap binary native `sdf-tools.exe` yang di-ship melalui folder
9
+ * bin/. Semua filter dan display flag diteruskan ke binary; help text di sisi
10
+ * Node mempertahankan kontrak CLI (contract validator + help generator).
11
+ *
12
+ * Binary lookup: <package-root>/bin/sdf-tools.exe relatif terhadap file ini
13
+ * (resolusi sama untuk source workspace dan installation di node_modules).
14
+ *
15
+ * Platform: saat ini hanya Windows (sdf-tools.exe). Pemanggilan di non-Windows
16
+ * akan return error eksplisit, bukan crash diam-diam.
17
+ */
18
+
19
+ const fs = require('fs');
20
+ const os = require('os');
21
+ const path = require('path');
22
+ const { spawnSync } = require('child_process');
23
+
24
+ const VALUE_FLAGS = ['domain', 'table', 'category', 'pattern', 'section', 'lang', 'path', 'format'];
25
+ const BOOLEAN_FLAGS = [
26
+ 'has-sdf',
27
+ 'no-sdf',
28
+ 'show',
29
+ 'example',
30
+ 'generate',
31
+ 'force',
32
+ 'list-domains',
33
+ 'list-categories',
34
+ 'list-sections',
35
+ 'stats'
36
+ ];
37
+
38
+ function resolveBinaryPath() {
39
+ if (os.platform() !== 'win32') return null;
40
+ return path.resolve(__dirname, '..', '..', '..', 'bin', 'sdf-tools.exe');
41
+ }
42
+
43
+ function buildBinaryArgs(args) {
44
+ const out = [];
45
+ for (const name of VALUE_FLAGS) {
46
+ const value = args[name];
47
+ if (value === undefined || value === null || value === '') continue;
48
+ out.push(`--${name}=${value}`);
49
+ }
50
+ for (const name of BOOLEAN_FLAGS) {
51
+ if (args[name] === true) {
52
+ out.push(`--${name}`);
53
+ }
54
+ }
55
+ return out;
56
+ }
57
+
58
+ module.exports = {
59
+ resource: 'schema',
60
+ verb: 'template',
61
+ description: 'Browse, preview, dan generate template schema dari koleksi RestForge Schema Reference',
62
+ category: 'utility',
63
+ flags: {
64
+ domain: {
65
+ type: 'string',
66
+ required: false,
67
+ default: null,
68
+ description: 'Filter berdasarkan domain (csv, mis. erp atau erp,finance)'
69
+ },
70
+ table: {
71
+ type: 'string',
72
+ required: false,
73
+ default: null,
74
+ description: 'Filter nama tabel (wildcard glob: sales*, *_invoice)'
75
+ },
76
+ category: {
77
+ type: 'string',
78
+ required: false,
79
+ default: null,
80
+ description: 'Filter category: master-data atau transactional'
81
+ },
82
+ pattern: {
83
+ type: 'string',
84
+ required: false,
85
+ default: null,
86
+ description: 'Filter pattern: single-table atau master-detail'
87
+ },
88
+ section: {
89
+ type: 'string',
90
+ required: false,
91
+ default: null,
92
+ description: 'Filter section code (lihat --list-sections)'
93
+ },
94
+ 'has-sdf': {
95
+ type: 'boolean',
96
+ required: false,
97
+ default: false,
98
+ description: 'Filter hanya template yang sudah punya versi SDF'
99
+ },
100
+ 'no-sdf': {
101
+ type: 'boolean',
102
+ required: false,
103
+ default: false,
104
+ description: 'Filter hanya template yang belum punya SDF (gap analysis)'
105
+ },
106
+ show: {
107
+ type: 'boolean',
108
+ required: false,
109
+ default: false,
110
+ description: 'Cetak schema template (perlu --table=<nama_spesifik>)'
111
+ },
112
+ example: {
113
+ type: 'boolean',
114
+ required: false,
115
+ default: false,
116
+ description: 'Sertakan section CONTOH DATA (kombinasi dengan --show)'
117
+ },
118
+ lang: {
119
+ type: 'string',
120
+ required: false,
121
+ default: null,
122
+ description: 'Format schema: sdf (default) atau sql'
123
+ },
124
+ generate: {
125
+ type: 'boolean',
126
+ required: false,
127
+ default: false,
128
+ description: 'Generate template ke filesystem (perlu --table dan --path)'
129
+ },
130
+ path: {
131
+ type: 'string',
132
+ required: false,
133
+ default: null,
134
+ description: 'Path destination untuk --generate (direktori atau file)'
135
+ },
136
+ force: {
137
+ type: 'boolean',
138
+ required: false,
139
+ default: false,
140
+ description: 'Overwrite file destination existing saat --generate'
141
+ },
142
+ 'list-domains': {
143
+ type: 'boolean',
144
+ required: false,
145
+ default: false,
146
+ description: 'List semua domain aplikasi yang tersedia'
147
+ },
148
+ 'list-categories': {
149
+ type: 'boolean',
150
+ required: false,
151
+ default: false,
152
+ description: 'List semua category template'
153
+ },
154
+ 'list-sections': {
155
+ type: 'boolean',
156
+ required: false,
157
+ default: false,
158
+ description: 'List semua section beserta category-nya'
159
+ },
160
+ stats: {
161
+ type: 'boolean',
162
+ required: false,
163
+ default: false,
164
+ description: 'Tampilkan statistik koleksi (per category, pattern, domain, section)'
165
+ },
166
+ format: {
167
+ type: 'string',
168
+ required: false,
169
+ default: null,
170
+ description: 'Format output: table (default), plain, atau json'
171
+ }
172
+ },
173
+ examples: [
174
+ 'npx restforge schema template',
175
+ 'npx restforge schema template --domain=erp',
176
+ 'npx restforge schema template --domain=erp,inventory --category=master-data',
177
+ 'npx restforge schema template --table=sales_order --show',
178
+ 'npx restforge schema template --table=sales_order --show --lang=sql',
179
+ 'npx restforge schema template --table=sales_order --generate --path=./schema --lang=sdf',
180
+ 'npx restforge schema template --stats',
181
+ 'npx restforge schema template --list-domains'
182
+ ],
183
+ async handler(args) {
184
+ const binaryPath = resolveBinaryPath();
185
+ if (!binaryPath) {
186
+ const err = new Error(
187
+ `schema template hanya tersedia di Windows (sdf-tools.exe). Platform saat ini: ${os.platform()}`
188
+ );
189
+ err.exitCode = 3;
190
+ throw err;
191
+ }
192
+
193
+ if (!fs.existsSync(binaryPath)) {
194
+ const err = new Error(
195
+ `sdf-tools.exe tidak ditemukan di ${binaryPath}. ` +
196
+ 'Pastikan binary sudah di-build dan tersedia di folder bin/ package.'
197
+ );
198
+ err.exitCode = 3;
199
+ throw err;
200
+ }
201
+
202
+ const binaryArgs = buildBinaryArgs(args);
203
+ const result = spawnSync(binaryPath, binaryArgs, {
204
+ stdio: 'inherit',
205
+ windowsHide: true
206
+ });
207
+
208
+ if (result.error) {
209
+ const err = new Error(`Gagal menjalankan sdf-tools.exe: ${result.error.message}`);
210
+ err.exitCode = 1;
211
+ throw err;
212
+ }
213
+
214
+ const status = typeof result.status === 'number' ? result.status : 1;
215
+ if (status !== 0) {
216
+ const err = new Error(`sdf-tools.exe exit code ${status}`);
217
+ err.exitCode = status;
218
+ err.silent = true;
219
+ throw err;
220
+ }
221
+ }
222
+ };
@@ -22,8 +22,6 @@ const { validateCheckCompatibility } = require('../../lib/dbschema-kit/validator
22
22
  const { reportHuman, getExitCode } = require('../../lib/dbschema-kit/validator/validator-reporter');
23
23
  const { defineModel } = require('../../lib/dbschema-kit/define-model');
24
24
 
25
- const DEFAULT_PATH = './schema';
26
-
27
25
  function listSchemaFiles(absPath) {
28
26
  const fs = require('fs');
29
27
  const stat = fs.statSync(absPath);
@@ -67,21 +65,19 @@ module.exports = {
67
65
  verb: 'validate',
68
66
  description: 'Memvalidasi file schema definition (single-model dan cross-model)',
69
67
  category: 'introspection',
70
- flags: {},
71
- positional: [
72
- {
73
- name: 'path',
68
+ flags: {
69
+ path: {
74
70
  type: 'string',
75
- required: false,
76
- description: 'Path file atau folder schema (default: ./schema)'
71
+ required: true,
72
+ description: 'Path file atau folder schema (mis. ./schema atau ./schema/users.js)'
77
73
  }
78
- ],
74
+ },
79
75
  examples: [
80
- 'npx restforge schema validate',
81
- 'npx restforge schema validate ./my-schema'
76
+ 'npx restforge schema validate --path=./schema',
77
+ 'npx restforge schema validate --path=./my-schema'
82
78
  ],
83
79
  async handler(args) {
84
- const schemaPath = args.path || DEFAULT_PATH;
80
+ const schemaPath = args.path;
85
81
  const absPath = path.resolve(process.cwd(), schemaPath);
86
82
 
87
83
  let models;
@@ -162,11 +162,26 @@ async function dispatch(argv, options = {}) {
162
162
  await contract.handler(parseResult.args);
163
163
  return 0;
164
164
  } catch (handlerErr) {
165
- const message = handlerErr && handlerErr.message ? handlerErr.message : String(handlerErr);
166
- writeLine(stderr, `Error: ${message}`);
165
+ // Handler dapat men-set property `silent: true` untuk men-suppress
166
+ // pesan "Error: ..." (mis. payload validate/diff/sync yang sudah
167
+ // print verdict lengkap dan tidak perlu redundant error message).
168
+ // DEBUG mode tetap tampilkan stack untuk troubleshooting.
169
+ if (!handlerErr || handlerErr.silent !== true) {
170
+ const message = handlerErr && handlerErr.message ? handlerErr.message : String(handlerErr);
171
+ writeLine(stderr, `Error: ${message}`);
172
+ }
167
173
  if (process.env.DEBUG && handlerErr && handlerErr.stack) {
168
174
  writeLine(stderr, handlerErr.stack);
169
175
  }
176
+ // Handler dapat men-set property `exitCode` pada Error untuk override
177
+ // default exit code 1. Dipakai oleh validasi schema di `endpoint create`
178
+ // yang membedakan drift (1) vs usage (2) vs connection (3).
179
+ const customCode = handlerErr && Number.isInteger(handlerErr.exitCode)
180
+ ? handlerErr.exitCode
181
+ : null;
182
+ if (customCode !== null && customCode >= 0 && customCode <= 255) {
183
+ return customCode;
184
+ }
170
185
  return 1;
171
186
  }
172
187
  }