@restforgejs/platform 5.2.10 → 5.2.12

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 (173) hide show
  1. package/bin/drift-check-linux +0 -0
  2. package/bin/sdf-tools-linux +0 -0
  3. package/build-info.json +2 -2
  4. package/cli/consumer-deploy.js +1 -1
  5. package/cli/consumer.js +1 -1
  6. package/generators/cli/fast-track.js +147 -27
  7. package/generators/cli/schema/init.js +20 -6
  8. package/generators/cli/schema/template.js +24 -9
  9. package/generators/lib/templates/dashboard-catalog.js +1 -1
  10. package/generators/lib/templates/db-connection-env.js +1 -1
  11. package/generators/lib/templates/dbschema-catalog.js +1 -1
  12. package/generators/lib/templates/field-validation-catalog.js +1 -1
  13. package/generators/lib/templates/mysql-template.js +1 -1
  14. package/generators/lib/templates/oracle-template.js +1 -1
  15. package/generators/lib/templates/postgres-template.js +1 -1
  16. package/generators/lib/templates/query-declarative-catalog.js +1 -1
  17. package/generators/lib/templates/sqlite-template.js +1 -1
  18. package/integrity-manifest.json +18 -18
  19. package/package.json +1 -1
  20. package/scripts/verify-integrity.js +1 -1
  21. package/server.js +1 -1
  22. package/src/components/handlers/adjust_handler.js +1 -1
  23. package/src/components/handlers/audit_handler.js +1 -1
  24. package/src/components/handlers/delete_handler.js +1 -1
  25. package/src/components/handlers/export_handler.js +1 -1
  26. package/src/components/handlers/import_handler.js +1 -1
  27. package/src/components/handlers/insert_handler.js +1 -1
  28. package/src/components/handlers/update_handler.js +1 -1
  29. package/src/components/handlers/upload_handler.js +1 -1
  30. package/src/components/handlers/workflow_handler.js +1 -1
  31. package/src/components/integrations/webhook.js +1 -1
  32. package/src/consumers/baseConsumer.js +1 -1
  33. package/src/consumers/declarativeMapper.js +1 -1
  34. package/src/consumers/handlers/apiHandler.js +1 -1
  35. package/src/consumers/handlers/consoleHandler.js +1 -1
  36. package/src/consumers/handlers/databaseHandler.js +1 -1
  37. package/src/consumers/handlers/index.js +1 -1
  38. package/src/consumers/handlers/kafkaHandler.js +1 -1
  39. package/src/consumers/index.js +1 -1
  40. package/src/consumers/messageTransformer.js +1 -1
  41. package/src/consumers/validator.js +1 -1
  42. package/src/core/db/dialect/base-dialect.js +1 -1
  43. package/src/core/db/dialect/index.js +1 -1
  44. package/src/core/db/dialect/mysql-dialect.js +1 -1
  45. package/src/core/db/dialect/oracle-dialect.js +1 -1
  46. package/src/core/db/dialect/postgres-dialect.js +1 -1
  47. package/src/core/db/dialect/sqlite-dialect.js +1 -1
  48. package/src/core/db/flatten-helper.js +1 -1
  49. package/src/core/db/query-builder-error.js +1 -1
  50. package/src/core/db/query-builder.js +1 -1
  51. package/src/core/db/relation-helper.js +1 -1
  52. package/src/core/handlers/delete_handler.js +1 -1
  53. package/src/core/handlers/insert_handler.js +1 -1
  54. package/src/core/handlers/update_handler.js +1 -1
  55. package/src/core/models/base-model.js +1 -1
  56. package/src/core/utils/cache-manager.js +1 -1
  57. package/src/core/utils/component-engine.js +1 -1
  58. package/src/core/utils/context-builder.js +1 -1
  59. package/src/core/utils/datetime-formatter.js +1 -1
  60. package/src/core/utils/datetime-parser.js +1 -1
  61. package/src/core/utils/db.js +1 -1
  62. package/src/core/utils/logger.js +1 -1
  63. package/src/core/utils/payload-loader.js +1 -1
  64. package/src/core/utils/security-checks.js +1 -1
  65. package/src/middleware/body-options.js +1 -1
  66. package/src/middleware/cors.js +1 -1
  67. package/src/middleware/idempotency.js +1 -1
  68. package/src/middleware/rate-limiter.js +1 -1
  69. package/src/middleware/request-logger.js +1 -1
  70. package/src/middleware/security-headers.js +1 -1
  71. package/src/models/base-model-mysql.js +1 -1
  72. package/src/models/base-model-oracle.js +1 -1
  73. package/src/models/base-model-sqlite.js +1 -1
  74. package/src/models/base-model.js +1 -1
  75. package/src/pro/caching/redis-client.js +1 -1
  76. package/src/pro/caching/redis-helper.js +1 -1
  77. package/src/pro/consumers/baseConsumer.js +1 -1
  78. package/src/pro/consumers/declarativeMapper.js +1 -1
  79. package/src/pro/consumers/handlers/apiHandler.js +1 -1
  80. package/src/pro/consumers/handlers/consoleHandler.js +1 -1
  81. package/src/pro/consumers/handlers/databaseHandler.js +1 -1
  82. package/src/pro/consumers/handlers/index.js +1 -1
  83. package/src/pro/consumers/handlers/kafkaHandler.js +1 -1
  84. package/src/pro/consumers/index.js +1 -1
  85. package/src/pro/consumers/messageTransformer.js +1 -1
  86. package/src/pro/consumers/validator.js +1 -1
  87. package/src/pro/database/base-model-mysql.js +1 -1
  88. package/src/pro/database/base-model-oracle.js +1 -1
  89. package/src/pro/database/base-model-sqlite.js +1 -1
  90. package/src/pro/database/db-mysql.js +1 -1
  91. package/src/pro/database/db-oracle.js +1 -1
  92. package/src/pro/database/db-sqlite.js +1 -1
  93. package/src/pro/excel/excel-generator.js +1 -1
  94. package/src/pro/excel/excel-parser.js +1 -1
  95. package/src/pro/excel/export-service.js +1 -1
  96. package/src/pro/excel/export_handler.js +1 -1
  97. package/src/pro/excel/import-service.js +1 -1
  98. package/src/pro/excel/import-validator.js +1 -1
  99. package/src/pro/excel/import_handler.js +1 -1
  100. package/src/pro/excel/upsert-builder.js +1 -1
  101. package/src/pro/idgen/idgen-routes.js +1 -1
  102. package/src/pro/integrations/lookup-resolver.js +1 -1
  103. package/src/pro/integrations/upload-handler-v2.js +1 -1
  104. package/src/pro/integrations/upload-handler.js +1 -1
  105. package/src/pro/integrations/webhook.js +1 -1
  106. package/src/pro/locking/lock-routes.js +1 -1
  107. package/src/pro/locking/resource-lock-manager.js +1 -1
  108. package/src/pro/messaging/kafkaConsumerService.js +1 -1
  109. package/src/pro/messaging/kafkaService.js +1 -1
  110. package/src/pro/messaging/messagehubService.js +1 -1
  111. package/src/pro/messaging/rabbitmqService.js +1 -1
  112. package/src/pro/scheduler/job-manager.js +1 -1
  113. package/src/pro/scheduler/job-routes.js +1 -1
  114. package/src/pro/scheduler/job-validator.js +1 -1
  115. package/src/pro/storage/base-storage-provider.js +1 -1
  116. package/src/pro/storage/file-metadata-helper.js +1 -1
  117. package/src/pro/storage/index.js +1 -1
  118. package/src/pro/storage/local-storage-provider.js +1 -1
  119. package/src/pro/storage/s3-storage-provider.js +1 -1
  120. package/src/pro/storage/upload-cleanup-job.js +1 -1
  121. package/src/pro/storage/upload-cleanup-scheduler.js +1 -1
  122. package/src/pro/storage/upload-pending-tracker.js +1 -1
  123. package/src/pro/websocket/broadcast-helper.js +1 -1
  124. package/src/pro/websocket/index.js +1 -1
  125. package/src/pro/websocket/livesync-server.js +1 -1
  126. package/src/pro/websocket/ws-broadcaster.js +1 -1
  127. package/src/services/export-service.js +1 -1
  128. package/src/services/import-service.js +1 -1
  129. package/src/services/kafkaConsumerService.js +1 -1
  130. package/src/services/kafkaService.js +1 -1
  131. package/src/services/messagehubService.js +1 -1
  132. package/src/services/rabbitmqService.js +1 -1
  133. package/src/utils/cache-invalidation-registry.js +1 -1
  134. package/src/utils/cache-manager.js +1 -1
  135. package/src/utils/component-engine.js +1 -1
  136. package/src/utils/config-extractor.js +1 -1
  137. package/src/utils/consumerLogger.js +1 -1
  138. package/src/utils/context-builder.js +1 -1
  139. package/src/utils/dashboard-helpers.js +1 -1
  140. package/src/utils/dateHelper.js +1 -1
  141. package/src/utils/datetime-formatter.js +1 -1
  142. package/src/utils/datetime-parser.js +1 -1
  143. package/src/utils/db-bootstrap.js +1 -1
  144. package/src/utils/db-mysql.js +1 -1
  145. package/src/utils/db-oracle.js +1 -1
  146. package/src/utils/db-sqlite.js +1 -1
  147. package/src/utils/db.js +1 -1
  148. package/src/utils/demo-generator.js +1 -1
  149. package/src/utils/excel-generator.js +1 -1
  150. package/src/utils/excel-parser.js +1 -1
  151. package/src/utils/file-watcher.js +1 -1
  152. package/src/utils/id-generator.js +1 -1
  153. package/src/utils/idempotency-manager.js +1 -1
  154. package/src/utils/import-validator.js +1 -1
  155. package/src/utils/license-client.js +1 -1
  156. package/src/utils/lock-manager.js +1 -1
  157. package/src/utils/logger.js +1 -1
  158. package/src/utils/lookup-resolver.js +1 -1
  159. package/src/utils/payload-loader.js +1 -1
  160. package/src/utils/processor-response.js +1 -1
  161. package/src/utils/rabbitmq.js +1 -1
  162. package/src/utils/redis-client.js +1 -1
  163. package/src/utils/redis-helper.js +1 -1
  164. package/src/utils/request-scope.js +1 -1
  165. package/src/utils/security-checks.js +1 -1
  166. package/src/utils/service-resolver.js +1 -1
  167. package/src/utils/shutdown-coordinator.js +1 -1
  168. package/src/utils/soft-delete-dashboard-guard.js +1 -1
  169. package/src/utils/sql-table-extractor.js +1 -1
  170. package/src/utils/trusted-keys.js +1 -1
  171. package/src/utils/upload-handler.js +1 -1
  172. package/src/utils/upsert-builder.js +1 -1
  173. package/src/utils/workflow-hook-executor.js +1 -1
@@ -257,8 +257,9 @@ function checkDesigner() {
257
257
  console.log('');
258
258
  console.log('[Preflight]');
259
259
 
260
- // Probe nyata: jalankan `restforge-designer --version`.
261
- const r = spawnSync('cmd', ['/S', '/C', 'restforge-designer --version'], { encoding: 'utf8' });
260
+ // Probe nyata: jalankan `restforge-designer --version`. shell:true memilih
261
+ // cmd.exe (Windows) atau /bin/sh (Linux/macOS) otomatis, termasuk resolusi PATH.
262
+ const r = spawnSync('restforge-designer --version', { shell: true, encoding: 'utf8' });
262
263
  const found = !r.error && r.status === 0;
263
264
  let version = '';
264
265
  if (found && r.stdout) version = r.stdout.trim().split(/\r?\n/)[0];
@@ -654,7 +655,7 @@ const ANSI_RE = /\x1b\[[0-9;]*m/g;
654
655
  * (list hanya membaca metadata plugin built-in).
655
656
  */
656
657
  function runDesignerPluginsList() {
657
- const r = spawnSync('cmd', ['/S', '/C', 'restforge-designer plugins list'], { encoding: 'utf8' });
658
+ const r = spawnSync('restforge-designer plugins list', { shell: true, encoding: 'utf8' });
658
659
  if (r.error || r.status !== 0) return null;
659
660
  return r.stdout || '';
660
661
  }
@@ -865,7 +866,7 @@ function phase(title) {
865
866
  /** Jalankan command CMD inline; hentikan pipeline bila gagal. */
866
867
  function run(cmd, cwd, { allowNonZero = false } = {}) {
867
868
  console.log(`\n#${cmd}`);
868
- const r = spawnSync('cmd', ['/S', '/C', cmd], { cwd, stdio: 'inherit' });
869
+ const r = spawnSync(cmd, { cwd, stdio: 'inherit', shell: true });
869
870
  if (allowNonZero) {
870
871
  if (r.error) console.log(`\n [WARN] ${cmd}\n ${r.error.message}`);
871
872
  return r.status;
@@ -1130,19 +1131,34 @@ function healthHost(serverAddress) {
1130
1131
  return (!serverAddress || serverAddress === '0.0.0.0') ? 'localhost' : serverAddress;
1131
1132
  }
1132
1133
 
1133
- /** Bila port dipakai, hentikan proses lama (Windows) sebelum serve. */
1134
+ /** Bila port dipakai, hentikan proses lama sebelum serve. */
1134
1135
  function freePort(port) {
1135
- const res = spawnSync('cmd', ['/S', '/C', `netstat -ano | findstr :${port}`], { encoding: 'utf8' });
1136
+ const isWin = process.platform === 'win32';
1136
1137
  const pids = new Set();
1137
- for (const line of (res.stdout || '').split(/\r?\n/)) {
1138
- const p = line.trim().split(/\s+/);
1139
- if (p.length >= 5 && /LISTENING/i.test(p[3]) && p[1].endsWith(`:${port}`)) {
1140
- if (/^\d+$/.test(p[4]) && p[4] !== '0') pids.add(p[4]);
1138
+
1139
+ if (isWin) {
1140
+ const res = spawnSync(`netstat -ano | findstr :${port}`, { shell: true, encoding: 'utf8' });
1141
+ for (const line of (res.stdout || '').split(/\r?\n/)) {
1142
+ const p = line.trim().split(/\s+/);
1143
+ if (p.length >= 5 && /LISTENING/i.test(p[3]) && p[1].endsWith(`:${port}`)) {
1144
+ if (/^\d+$/.test(p[4]) && p[4] !== '0') pids.add(p[4]);
1145
+ }
1146
+ }
1147
+ } else {
1148
+ // lsof tidak selalu terinstall (mis. minimal container) - tolerant, skip kalau gagal.
1149
+ const res = spawnSync(`lsof -ti tcp:${port}`, { shell: true, encoding: 'utf8' });
1150
+ for (const line of (res.stdout || '').split(/\r?\n/)) {
1151
+ const pid = line.trim();
1152
+ if (/^\d+$/.test(pid)) pids.add(pid);
1141
1153
  }
1142
1154
  }
1155
+
1143
1156
  if (pids.size === 0) return;
1144
1157
  console.log(` Port ${port} in use (PID ${[...pids].join(', ')}); stopping old process...`);
1145
- for (const pid of pids) spawnSync('cmd', ['/S', '/C', `taskkill /PID ${pid} /F`], { stdio: 'inherit' });
1158
+ for (const pid of pids) {
1159
+ const killCmd = isWin ? `taskkill /PID ${pid} /F` : `kill -9 ${pid}`;
1160
+ spawnSync(killCmd, { shell: true, stdio: 'inherit' });
1161
+ }
1146
1162
  }
1147
1163
 
1148
1164
  /** Pipeline REST API nyata: env -> validate -> migrate -> payload+endpoint. */
@@ -1230,7 +1246,12 @@ function runFrontendPipeline(ctx) {
1230
1246
 
1231
1247
  phase('[F3/3] Generate frontend application');
1232
1248
  // Hapus index.html lama agar landing page diregenerasi sesuai set page terbaru.
1233
- run(`if exist apps\\${ctx.project}\\index.html del /Q apps\\${ctx.project}\\index.html`, frontendDir, { allowNonZero: true });
1249
+ // Pakai fs langsung (bukan shell command) supaya portable Windows/Linux/macOS.
1250
+ try {
1251
+ fs.unlinkSync(path.join(frontendDir, 'apps', ctx.project, 'index.html'));
1252
+ } catch {
1253
+ // File belum ada (first run) - abaikan.
1254
+ }
1234
1255
  run(`restforge-designer generate --payload=payload/${appCode}.json --output=./apps/${ctx.project} ${pluginArg} --overwrite`, frontendDir);
1235
1256
  }
1236
1257
 
@@ -1271,6 +1292,66 @@ function writeServerStartScript(ctx) {
1271
1292
  return file;
1272
1293
  }
1273
1294
 
1295
+ /**
1296
+ * Tulis launcher start frontend mandiri di folder app (frontend/apps/<project>/).
1297
+ * Windows -> frontend-start.bat, Linux/macOS -> frontend-start.sh. Sama seperti
1298
+ * writeServerStartScript, dipakai juga sebagai target `pm2 start` di non-Windows.
1299
+ */
1300
+ function writeFrontendStartScript(ctx) {
1301
+ const serveCmd = `npx serve . -l ${ctx.cfg.WEB_SERVER_PORT}`;
1302
+ const isWin = process.platform === 'win32';
1303
+ const appDir = path.join(ctx.cwd, 'frontend', 'apps', ctx.project);
1304
+ const file = path.join(appDir, isWin ? 'frontend-start.bat' : 'frontend-start.sh');
1305
+ let content;
1306
+ if (isWin) {
1307
+ content = [
1308
+ '@echo off',
1309
+ 'REM Start RESTForge frontend app. Generated by fast-track.',
1310
+ 'cd /d "%~dp0"',
1311
+ `call ${serveCmd}`,
1312
+ ''
1313
+ ].join('\r\n');
1314
+ } else {
1315
+ content = [
1316
+ '#!/usr/bin/env bash',
1317
+ '# Start RESTForge frontend app. Generated by fast-track.',
1318
+ 'set -e',
1319
+ 'cd "$(dirname "$0")"',
1320
+ serveCmd,
1321
+ ''
1322
+ ].join('\n');
1323
+ }
1324
+ fs.writeFileSync(file, content);
1325
+ if (!isWin) {
1326
+ try { fs.chmodSync(file, 0o755); } catch { /* abaikan bila FS tak dukung chmod */ }
1327
+ }
1328
+ return file;
1329
+ }
1330
+
1331
+ /**
1332
+ * pm2 dipakai di Linux/macOS sebagai pengganti "open new window" (konsep
1333
+ * Windows-only) untuk mengelola proses server/frontend: pm2 list/logs/reload/
1334
+ * stop, bukan raw PID. Nama proses dibedakan per-project per-kind agar tidak
1335
+ * collision antar project yang dijalankan bersamaan.
1336
+ */
1337
+ function pm2Name(project, kind) {
1338
+ return `${project}-${kind}`;
1339
+ }
1340
+
1341
+ /**
1342
+ * Start (ulang) script launcher via pm2. Idempotent: hapus instance lama dulu
1343
+ * (silent, tolerant bila belum ada) supaya re-run fast-track tidak gagal
1344
+ * dengan "already launched". Return false bila pm2 tidak tersedia/gagal.
1345
+ */
1346
+ function pm2StartScript(name, scriptPath, cwd) {
1347
+ spawnSync(`pm2 delete ${name} --silent`, { shell: true, stdio: 'ignore' });
1348
+ const r = spawnSync(
1349
+ `pm2 start "${scriptPath}" --name ${name} --cwd "${cwd}" --interpreter bash`,
1350
+ { shell: true, stdio: 'inherit' }
1351
+ );
1352
+ return !r.error && r.status === 0;
1353
+ }
1354
+
1274
1355
  function printFinalSummary(ctx) {
1275
1356
  const parts = [];
1276
1357
  if (ctx.scope.backend) parts.push('REST API generated');
@@ -1287,6 +1368,9 @@ function printFinalSummary(ctx) {
1287
1368
  }
1288
1369
  if (ctx.scope.frontend) {
1289
1370
  console.log(` Frontend : frontend/apps/${ctx.project}/ (start: npx serve . -l ${ctx.cfg.WEB_SERVER_PORT})`);
1371
+ if (ctx.frontendStartFile) {
1372
+ console.log(` Start : ${path.basename(ctx.frontendStartFile)} (start frontend manually)`);
1373
+ }
1290
1374
  }
1291
1375
  console.log(rule('='));
1292
1376
  }
@@ -1304,20 +1388,35 @@ async function startServerNow(ctx) {
1304
1388
  const serveCmd = `npx restforge serve --project=${ctx.project} --config=${ctx.configFlag} --watch`;
1305
1389
  freePort(ctx.cfg.SERVER_PORT);
1306
1390
  const title = `RESTForge Server - ${ctx.project}`;
1307
- console.log(`\n Opening new window: "${title}"`);
1308
- console.log(`#${serveCmd}`);
1309
1391
  // server.js (dispatcher) memaksa NODE_ENV=production untuk proses cli command,
1310
1392
  // sehingga fast-track berjalan production dan secara default akan mewariskannya
1311
1393
  // ke serve -> logger memakai format JSON mentah (verbose). Set development agar
1312
1394
  // runtime server memakai pino-pretty (rapi), konsisten dengan playbook yang
1313
1395
  // menjalankan serve dari plain `node`.
1314
1396
  const serveEnv = { ...process.env, NODE_ENV: 'development' };
1315
- const r = spawnSync('cmd', ['/C', 'start', title, 'cmd', '/k', serveCmd], { cwd: ctx.cwd, stdio: 'inherit', env: serveEnv });
1316
- if (r.error) {
1317
- console.log(` Failed to open server window: ${r.error.message}`);
1318
- return false;
1397
+
1398
+ if (process.platform === 'win32') {
1399
+ console.log(`\n Opening new window: "${title}"`);
1400
+ console.log(`#${serveCmd}`);
1401
+ const r = spawnSync('cmd', ['/C', 'start', title, 'cmd', '/k', serveCmd], { cwd: ctx.cwd, stdio: 'inherit', env: serveEnv });
1402
+ if (r.error) {
1403
+ console.log(` Failed to open server window: ${r.error.message}`);
1404
+ return false;
1405
+ }
1406
+ console.log(' ✓ Server window opened. Keep it open. Stop with Ctrl+C.');
1407
+ } else {
1408
+ // Tidak ada konsep "window baru" di Linux/macOS (terutama session SSH headless) -
1409
+ // pakai pm2 supaya proses bisa dikelola normal (pm2 list/logs/reload/stop).
1410
+ console.log(`\n Starting via pm2: "${title}"`);
1411
+ const name = pm2Name(ctx.project, 'server');
1412
+ const ok = pm2StartScript(name, ctx.serverStartFile, ctx.cwd);
1413
+ if (!ok) {
1414
+ console.log(' Failed to start via pm2. Is pm2 installed? (npm install -g pm2)');
1415
+ return false;
1416
+ }
1417
+ console.log(` ✓ Server started via pm2 as "${name}".`);
1418
+ console.log(` pm2 logs ${name} | pm2 reload ${name} | pm2 stop ${name}`);
1319
1419
  }
1320
- console.log(' ✓ Server window opened. Keep it open. Stop with Ctrl+C.');
1321
1420
 
1322
1421
  // Health check: tunggu runtime benar-benar siap menerima request sebelum
1323
1422
  // lanjut (mis. ke frontend). URL = banner runtime: /api/<project>/health.
@@ -1365,14 +1464,27 @@ async function startFrontendNow(ctx) {
1365
1464
  }
1366
1465
  freePort(webPort);
1367
1466
  const title = `RESTForge Frontend - ${ctx.project}`;
1368
- console.log(`\n Opening new window: "${title}"`);
1369
- console.log(`#${serveCmd}`);
1370
- const r = spawnSync('cmd', ['/C', 'start', title, 'cmd', '/k', serveCmd], { cwd: appDir, stdio: 'inherit' });
1371
- if (r.error) {
1372
- console.log(` Failed to open frontend window: ${r.error.message}`);
1373
- return false;
1467
+
1468
+ if (process.platform === 'win32') {
1469
+ console.log(`\n Opening new window: "${title}"`);
1470
+ console.log(`#${serveCmd}`);
1471
+ const r = spawnSync('cmd', ['/C', 'start', title, 'cmd', '/k', serveCmd], { cwd: appDir, stdio: 'inherit' });
1472
+ if (r.error) {
1473
+ console.log(` Failed to open frontend window: ${r.error.message}`);
1474
+ return false;
1475
+ }
1476
+ console.log(` ✓ Frontend window opened (WEB_SERVER_PORT ${webPort}).`);
1477
+ } else {
1478
+ console.log(`\n Starting via pm2: "${title}"`);
1479
+ const name = pm2Name(ctx.project, 'frontend');
1480
+ const ok = pm2StartScript(name, ctx.frontendStartFile, appDir);
1481
+ if (!ok) {
1482
+ console.log(' Failed to start via pm2. Is pm2 installed? (npm install -g pm2)');
1483
+ return false;
1484
+ }
1485
+ console.log(` ✓ Frontend started via pm2 as "${name}" (WEB_SERVER_PORT ${webPort}).`);
1486
+ console.log(` pm2 logs ${name} | pm2 reload ${name} | pm2 stop ${name}`);
1374
1487
  }
1375
- console.log(` ✓ Frontend window opened (WEB_SERVER_PORT ${webPort}).`);
1376
1488
 
1377
1489
  const url = `http://localhost:${webPort}/index.html`;
1378
1490
  // Tunggu static server (`npx serve`) benar-benar siap sebelum buka browser,
@@ -1387,7 +1499,12 @@ async function startFrontendNow(ctx) {
1387
1499
  if (!ready.ok) {
1388
1500
  console.log(' ⚠ Frontend belum merespons - buka URL di atas manual bila browser tidak otomatis terbuka.');
1389
1501
  }
1390
- const openResult = spawnSync('cmd', ['/C', 'start', '""', url], { stdio: 'ignore' });
1502
+ const openCmd = process.platform === 'win32'
1503
+ ? ['cmd', ['/C', 'start', '""', url]]
1504
+ : process.platform === 'darwin'
1505
+ ? ['open', [url]]
1506
+ : ['xdg-open', [url]];
1507
+ const openResult = spawnSync(openCmd[0], openCmd[1], { stdio: 'ignore' });
1391
1508
  if (openResult.error) {
1392
1509
  console.log(` (Could not open browser automatically: ${openResult.error.message})`);
1393
1510
  }
@@ -1552,6 +1669,9 @@ module.exports = {
1552
1669
  }
1553
1670
  if (ctx.scope.frontend) {
1554
1671
  runFrontendPipeline(ctx);
1672
+ // Launcher start frontend mandiri (sesuai OS), dipakai juga sebagai
1673
+ // target `pm2 start` di non-Windows.
1674
+ ctx.frontendStartFile = writeFrontendStartScript(ctx);
1555
1675
  }
1556
1676
 
1557
1677
  printFinalSummary(ctx);
@@ -21,8 +21,14 @@ const path = require('path');
21
21
  const { spawnSync } = require('child_process');
22
22
 
23
23
  function resolveBinaryPath() {
24
- if (os.platform() !== 'win32') return null;
25
- return path.resolve(__dirname, '..', '..', '..', 'bin', 'sdf-tools.exe');
24
+ const platform = os.platform();
25
+ if (platform === 'win32') {
26
+ return path.resolve(__dirname, '..', '..', '..', 'bin', 'sdf-tools.exe');
27
+ }
28
+ if (platform === 'linux') {
29
+ return path.resolve(__dirname, '..', '..', '..', 'bin', 'sdf-tools-linux');
30
+ }
31
+ return null;
26
32
  }
27
33
 
28
34
  module.exports = {
@@ -51,7 +57,7 @@ module.exports = {
51
57
  const binaryPath = resolveBinaryPath();
52
58
  if (!binaryPath) {
53
59
  const err = new Error(
54
- `schema init hanya tersedia di Windows (sdf-tools.exe). Platform saat ini: ${os.platform()}`
60
+ `schema init tidak didukung di platform ini (sdf-tools). Platform saat ini: ${os.platform()}`
55
61
  );
56
62
  err.exitCode = 3;
57
63
  throw err;
@@ -59,13 +65,21 @@ module.exports = {
59
65
 
60
66
  if (!fs.existsSync(binaryPath)) {
61
67
  const err = new Error(
62
- `sdf-tools.exe tidak ditemukan di ${binaryPath}. ` +
68
+ `Binary sdf-tools tidak ditemukan di ${binaryPath}. ` +
63
69
  'Pastikan binary sudah di-build dan tersedia di folder bin/ package.'
64
70
  );
65
71
  err.exitCode = 3;
66
72
  throw err;
67
73
  }
68
74
 
75
+ if (os.platform() !== 'win32') {
76
+ try {
77
+ fs.chmodSync(binaryPath, 0o755);
78
+ } catch {
79
+ // FS tak dukung chmod (mis. read-only mount) — lanjut, biarkan spawnSync gagal kalau memang tidak executable.
80
+ }
81
+ }
82
+
69
83
  const binaryArgs = [
70
84
  '--table=dummy',
71
85
  '--generate',
@@ -79,14 +93,14 @@ module.exports = {
79
93
  });
80
94
 
81
95
  if (result.error) {
82
- const err = new Error(`Gagal menjalankan sdf-tools.exe: ${result.error.message}`);
96
+ const err = new Error(`Gagal menjalankan sdf-tools: ${result.error.message}`);
83
97
  err.exitCode = 1;
84
98
  throw err;
85
99
  }
86
100
 
87
101
  const status = typeof result.status === 'number' ? result.status : 1;
88
102
  if (status !== 0) {
89
- const err = new Error(`sdf-tools.exe exit code ${status}`);
103
+ const err = new Error(`sdf-tools exit code ${status}`);
90
104
  err.exitCode = status;
91
105
  err.silent = true;
92
106
  throw err;
@@ -9,11 +9,12 @@
9
9
  * bin/. Semua filter dan display flag diteruskan ke binary; help text di sisi
10
10
  * Node mempertahankan kontrak CLI (contract validator + help generator).
11
11
  *
12
- * Binary lookup: <package-root>/bin/sdf-tools.exe relatif terhadap file ini
12
+ * Binary lookup: <package-root>/bin/sdf-tools.exe (Windows) atau
13
+ * <package-root>/bin/sdf-tools-linux (Linux), relatif terhadap file ini
13
14
  * (resolusi sama untuk source workspace dan installation di node_modules).
14
15
  *
15
- * Platform: saat ini hanya Windows (sdf-tools.exe). Pemanggilan di non-Windows
16
- * akan return error eksplisit, bukan crash diam-diam.
16
+ * Platform: Windows dan Linux x86_64. Platform lain (mis. macOS) akan
17
+ * return error eksplisit, bukan crash diam-diam.
17
18
  */
18
19
 
19
20
  const fs = require('fs');
@@ -36,8 +37,14 @@ const BOOLEAN_FLAGS = [
36
37
  ];
37
38
 
38
39
  function resolveBinaryPath() {
39
- if (os.platform() !== 'win32') return null;
40
- return path.resolve(__dirname, '..', '..', '..', 'bin', 'sdf-tools.exe');
40
+ const platform = os.platform();
41
+ if (platform === 'win32') {
42
+ return path.resolve(__dirname, '..', '..', '..', 'bin', 'sdf-tools.exe');
43
+ }
44
+ if (platform === 'linux') {
45
+ return path.resolve(__dirname, '..', '..', '..', 'bin', 'sdf-tools-linux');
46
+ }
47
+ return null;
41
48
  }
42
49
 
43
50
  function buildBinaryArgs(args) {
@@ -185,7 +192,7 @@ module.exports = {
185
192
  const binaryPath = resolveBinaryPath();
186
193
  if (!binaryPath) {
187
194
  const err = new Error(
188
- `schema template hanya tersedia di Windows (sdf-tools.exe). Platform saat ini: ${os.platform()}`
195
+ `schema template tidak didukung di platform ini (sdf-tools). Platform saat ini: ${os.platform()}`
189
196
  );
190
197
  err.exitCode = 3;
191
198
  throw err;
@@ -193,13 +200,21 @@ module.exports = {
193
200
 
194
201
  if (!fs.existsSync(binaryPath)) {
195
202
  const err = new Error(
196
- `sdf-tools.exe tidak ditemukan di ${binaryPath}. ` +
203
+ `Binary sdf-tools tidak ditemukan di ${binaryPath}. ` +
197
204
  'Pastikan binary sudah di-build dan tersedia di folder bin/ package.'
198
205
  );
199
206
  err.exitCode = 3;
200
207
  throw err;
201
208
  }
202
209
 
210
+ if (os.platform() !== 'win32') {
211
+ try {
212
+ fs.chmodSync(binaryPath, 0o755);
213
+ } catch {
214
+ // FS tak dukung chmod (mis. read-only mount) — lanjut, biarkan spawnSync gagal kalau memang tidak executable.
215
+ }
216
+ }
217
+
203
218
  const binaryArgs = buildBinaryArgs(args);
204
219
  const result = spawnSync(binaryPath, binaryArgs, {
205
220
  stdio: 'inherit',
@@ -207,14 +222,14 @@ module.exports = {
207
222
  });
208
223
 
209
224
  if (result.error) {
210
- const err = new Error(`Gagal menjalankan sdf-tools.exe: ${result.error.message}`);
225
+ const err = new Error(`Gagal menjalankan sdf-tools: ${result.error.message}`);
211
226
  err.exitCode = 1;
212
227
  throw err;
213
228
  }
214
229
 
215
230
  const status = typeof result.status === 'number' ? result.status : 1;
216
231
  if (status !== 0) {
217
- const err = new Error(`sdf-tools.exe exit code ${status}`);
232
+ const err = new Error(`sdf-tools exit code ${status}`);
218
233
  err.exitCode = status;
219
234
  err.silent = true;
220
235
  throw err;
@@ -1 +1 @@
1
- const a0_0x20f86c=a0_0x2283;(function(_0x5b5bff,_0x206b7d){const _0x4bdb08=a0_0x2283,_0x46a584=_0x5b5bff();while(!![]){try{const _0x8d6e45=parseInt(_0x4bdb08(0x134))/0x1+parseInt(_0x4bdb08(0x108))/0x2*(parseInt(_0x4bdb08(0x120))/0x3)+parseInt(_0x4bdb08(0x10f))/0x4+-parseInt(_0x4bdb08(0x10a))/0x5+parseInt(_0x4bdb08(0x12e))/0x6*(parseInt(_0x4bdb08(0x125))/0x7)+parseInt(_0x4bdb08(0x166))/0x8*(parseInt(_0x4bdb08(0x136))/0x9)+-parseInt(_0x4bdb08(0x152))/0xa;if(_0x8d6e45===_0x206b7d)break;else _0x46a584['push'](_0x46a584['shift']());}catch(_0x40804b){_0x46a584['push'](_0x46a584['shift']());}}}(a0_0x1538,0x93c66));function a0_0x2283(_0x2e320c,_0x4f958e){_0x2e320c=_0x2e320c-0x106;const _0x1538ac=a0_0x1538();let _0x2283c5=_0x1538ac[_0x2e320c];if(a0_0x2283['MdNWvK']===undefined){var _0xe908e=function(_0x3bd183){const _0x3fbce5='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x14c42a='',_0x358bd1='';for(let _0x35093b=0x0,_0x4efde2,_0x541bd2,_0x2592a6=0x0;_0x541bd2=_0x3bd183['charAt'](_0x2592a6++);~_0x541bd2&&(_0x4efde2=_0x35093b%0x4?_0x4efde2*0x40+_0x541bd2:_0x541bd2,_0x35093b++%0x4)?_0x14c42a+=String['fromCharCode'](0xff&_0x4efde2>>(-0x2*_0x35093b&0x6)):0x0){_0x541bd2=_0x3fbce5['indexOf'](_0x541bd2);}for(let _0x3160f8=0x0,_0x5d7dab=_0x14c42a['length'];_0x3160f8<_0x5d7dab;_0x3160f8++){_0x358bd1+='%'+('00'+_0x14c42a['charCodeAt'](_0x3160f8)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x358bd1);};a0_0x2283['zLtAEQ']=_0xe908e,a0_0x2283['uYIOpr']={},a0_0x2283['MdNWvK']=!![];}const _0x18f5a0=_0x1538ac[0x0],_0x203ecc=_0x2e320c+_0x18f5a0,_0x228efa=a0_0x2283['uYIOpr'][_0x203ecc];return!_0x228efa?(_0x2283c5=a0_0x2283['zLtAEQ'](_0x2283c5),a0_0x2283['uYIOpr'][_0x203ecc]=_0x2283c5):_0x2283c5=_0x228efa,_0x2283c5;}function a0_0x1538(){const _0x43c469=['Dg9WlwXLDMvSicDWyxjHBxmNig9IAMvJDa','zgfZAc1ZywXLCW','nKzQBLj5ta','sgvHzgXPBMuGBwv0CMLJihDPDgGGDhjLBMqGy2HPCcbHBMqGyNjLywTKB3DUigfJCM9ZCYbJyxrLz29YAwvZlIbtDwL0ywjSzsbMB3iGD2LKz2v0CYbSAwTLicDfEhbLy3rLzcbfyxjUAw5NCYCGDgHHDcbZAg93ihrVDgfSihzHBhvLlcbWzxjJzw50ywDLignOyw5NzsWGyw5KihbLCI1JyxrLz29YEsbJB250CMLIDxrPB24U','rgvMyxvSDcb2ywX1zsbHChbSAwvKihDOzw4GDgHLihjLCxvLC3qGB21PDhmGDgHPCYbWyxjHBs4GvMfSAwrHDg9YigrVzxmGtK9uihn0CMLJDgX5ihr5CguTy2HLy2SGzgvMyxvSDdSGCNvUDgLTzsbPCYbYzxnWB25ZAwjSzsbMB3iGy29TCgf0AwjPBgL0Es4','Dg9WlwXLDMvSicDJywnOzsCGB2jQzwn0','tgLZDcbVzIb3AwrNzxqGzgvMAw5PDgLVBNmUie9YzgvYigLZigLUzM9YBwf0Aw9UywWGB25SEsaOCMvZCg9UC2uGA2v5CYbHCMuGyNKGD2LKz2v0igLKlcbUB3qGyxjYyxKGAw5KzxGPlG','phDPzgDLDf9Pzd4','mtm1nZC1yujnuMXe','tIbYB3DZimoxie0Gy29SCW','mta0nJyZndnNAgnVwvO','DgfYz2v0','zxHWzwn0zwrFzwfYBMLUz3m','vgHLihbYzwzPEcbIzwnVBwvZihbHCNqGB2yGDgHLifvstcbZzwDTzw50lIbuAguGCMvZzxj2zwqGC2nOzw1LigTLzxbZigrHC2HIB2fYzcbLBMrWB2LUDhmGDMLZDwfSBhKGzgLZDgLUy3qGzNjVBsbduLveigvUzhbVAw50CYbPBIb0AguGvvjmihnWywnLigfUzcbHBgXVD3mGzNv0DxjLihjVDxrPBMCGzgLMzMvYzw50Awf0Aw9UlG','vuKGBgfIzwWGAxmGysbMCM9UDgvUzcbYzw5KzxjPBMCGy29Uy2vYBI4','B2jQzwn0','u2LUz2XLifnrtcbXDwvYEsbMB3iGDgHLihDPzgDLDc4','zgfZAc1PBMjVDw5K','zgfZAc1HDxrOB3iTC3rHDhm','tIbYB3DZimoxidiGy29SDw1UCW','yxjYyxK8C3rYAw5NpIWGB3b0Aw9UywWG4Ocuihn1yNnLDcbVzIb3AwrNzxqGsurZihrVigv4zwn1DguUie9TAxqGDg8GzxHLy3v0zsbHBgWGzgvJBgfYzwqGD2LKz2v0CY4','zMLSztPXDwvYEs88Cgf0Ad4VDhjLBMqUC3fS','qsbWyxLSB2fKihDPDgGGyM90AcaND2LKz2v0CYCGyw5KicD0ywjSzu5HBwuNigLZihjLAMvJDgvKigj5ierHC2HIB2fYzfzHBgLKyxrVCI4GugLJAYbVBMuGC2HHCguU','vMLZDwfSignVBg9YigLZigeGzNjVBNrLBMqGCMvUzgvYAw5NignVBMnLCM4U','D2LKz2v0lNf1zxjPzxmUpgTLEt4GD2L0AcbtuuWGCMv0DxjUAw5NideGCM93imoxig11BhrPCgXLignVBhvTBNm','ue9tvcaVyxbPl21PBMKTAw52zw50B3j5l2rHC2GTAw5IB3vUzc9KyxnOyM9HCMq','CxvLCNK','Aw5OzxjPDhmGq0fdsevFvfrmigvUDG','vgfIBguGzgv0zwn0zwqGAw4Gu1fmlcbIDxqGBM90ihjLz2LZDgvYzwqGyxmGq1jvrcbLBMrWB2LUDcbPBIbTzxrHzgf0ysbWCM9Qzwn0icHSAwTLBhKGysb2Awv3lcbdveuGywXPyxmSig9YignYB3nZlxbYB2PLy3qGDgfIBguG4OcuignHC2nHzguGD2LSBcbUB3qGzMLYzsK','zMLSztPXDwvYEs88Cgf0Ad4VCg9PBNrZlNnXBa','D2LKz2v0lNf1zxj5icHZAw5NDwXHCIK','zMLSztPYzwXHDgL2zs9WyxrOl3rVl3f1zxj5lNnXBa','BgvUz3rO','ugfYyw0Gzgf0ysb0ExbLlIbwywXPzgf0zxmGCMvXDwvZDcbIB2r5igfUzcbZAgfWzxmGCNvUDgLTzsbWyxjHBwv0zxiGyMLUzgLUzY4','DhjLBMq','zMLSztPXDwvYEs88Cgf0Ad4VDMfSDwuUC3fS','B2jQzwn0iokaLcb2ywX1zxmGzM9YigrLy2XHCMvKihbHCMfTCYaODMfSAwrHDgvKigfNywLUC3qGCgfYyw1ZignVBNrYywn0oYbTAxnZAw5NihjLCxvPCMvKiokgKIa0mdaSihr5CguGBwLZBwf0y2GG4OAsidqWmcK','y2fJAgu','mtmWndy0nJbHqxfKsMK','sgvHzgXPBMuGBwv0CMLJihDPDgGGDhjLBMqGy2HPCcbHBMqGC3bHCMTSAw5Lig1PBMKTy2HHCNqGzM9YihnOB3j0ihDPBMrVD3mGkdCGzgf5CYWGmtiGBw9UDgHZlcbLDgmUks4Gu3vPDgfIBguGzM9YihDPzgDLDhmGBgLRzsaNqxzLCMfNzsbeywLSEsbtywXLCYCU','yM9VBgvHBG','zgLYzwn0Aw9U','q29SBgfWC2uGDg8GB2jQzwn0ihDOB3nLigTLExmGyxjLifnrtcbJB2X1Bw4GBMfTzxmGkgXVD2vYy2fZzwqPlG','yxjYyxK8C3rYAw5NpG','CgvYAw9K','D2LKz2v0CW','msbYB3CGW5CGmsbJB2X1Bw4','DMfSDwu','iJe4mZyI','msbYB3CGW5CGmIbJB2X1Bw5Z','rxzLCNKGCgXHy2vOB2XKzxiGDxnLzcbPBIbtuuWGtvvtvcbIzsbKzwnSyxjLzcbPBIaNCgfYyw1ZjY4GvMfSAwrHDg9YihrOCM93CYbfCNjVCIb3AxrOig1LC3nHz2uGzM9YBwf0oIaIv2LKz2v0icC8Awq+jYbXDwvYEsaNpgXHyMvSpICGDxnLCYb1BMrLy2XHCMvKihbSywnLAg9SzgvYicC6phrVA2vUpICGkgrLy2XHCMuGAw4Gj3bHCMfTCYCPiI4','zMLSztPXDwvYEs88Cgf0Ad4Vy3vYCMvUDc5ZCwW','zxHWB3j0CW','C2nHBgfYihbYAw1PDgL2zq','q29SBgfWC2uGDg8GC2nHBgfYihbYAw1PDgL2zsaODgHLihzHBhvLig9MihrOzsbZAw5NBguGy29SDw1Uks4','BM9UlwvTChr5lcb1BMLXDwuGywnYB3nZihDPzgDLDhmGAw4GDgHLihnHBwuGCgf5Bg9Hza','ugfYyw0GBMfTzsbTDxn0ig1HDgnOihrOzsbWBgfJzwHVBgrLCIbYzwDLEcbGw2eTEKeTwL9Dw2eTEKeTwJaTov9DkMaGkgfSCgHHBNvTzxjPyYaRihvUzgvYC2nVCMuSig11C3qGC3rHCNqGD2L0AcbSzxr0zxiGB3iGDw5KzxjZy29YzsKU','w3SGiMXHyMvSiJOGiLnOB2vZiIWGiNzHBhvLiJOGiJC2nJaIih0SihSGiMXHyMvSiJOGiKDHBwLUzYiSicj2ywX1zsi6iciYodiWiIb9lcb7icjSywjLBci6icjpDgHLCNmIlcaIDMfSDwuIoIaInduYntCIih1D','ofzOvuzQAG','BNvTyMvY','AxrLBxm','DgfIBgvoyw1L','mtzyqMDfChG','B3jKzxjZx3rOAxnFBw9UDgG','ntC1ntK1nwT5vwjVAa','Bgf5B3v0','zNjVBNrLBMqTy29Uy2vYBG','Aw52ywXPzgf0zxm','v2HLBIb0CNvLlcb0AguGCMvXDwvZDcbIB2r5ie1vu1qGAw5JBhvKzsb0AgLZihbHCMfTicHVDgHLCNDPC2uGndaWks4','mZy5odG0ogffuvfXBq','zMLSztPXDwvYEs88Cgf0Ad4VyNjLywTKB3DUlNnXBa','yw55icGXihjVDYddLYaXignVBcWGtIbYB3DZimoxie0Gy29SCYWGzxrJlIK','kd88itOPoIHBys16qs1Ax11Bys16qs1Amc05x10Qkq','vgLTzs10BY1SAxzLigLUihnLy29UzhmUidaGzwzMzwn0AxzLBhKGzgLZywjSzxmGy2fJAguGzM9YihrOAxmGzw50CNKU','zgf0zq','qwX3yxLZihSGAxrLBxm6ifSUlI5Dih0GCMvNyxjKBgvZCYbVzIbtuuWGCMvZDwX0ihnOyxbLlG','DgL0Bgu','xMrHC2GTw2eTEKeTwJaTov8TxsSK','C3rYAw5N','iJi0mJaI','CgfYyw1Z','twv0CMLJicSGuhjVz3jLC3mGDg8Gr29HBa','l2fWAs97ChjVAMvJDh0VE25HBwv9l2rHC2HIB2fYza','Ahr0Chm6lY9Yzxn0zM9Yz2uUzgv2l2rVy3mVC2vYDMvYl3f1zxj5lwrHDgeVzgfZAgjVyxjK','iNzHBhvLiJOGiJy5nZaWiG','vg9Nz2XLignHy2HLigzLyxr1CMuGzM9YihrOAxmGzgfZAgjVyxjKlG','mJK4mdq3CwjVBgnl','DhrS','vMLZDwfSihzHCMLHBNqGkgrVBNv0lcbIyxiSihbPzsWGyxjLysKGAxmGysbMCM9UDgvUzcbYzw5KzxjPBMCGy29Uy2vYBIaOC2vWyxjHDgLVBIbVzIbJB25JzxjUCYKU','D2LKz2v0lNf1zxjPzxmUpgTLEt4GD2L0AcbtuuWGCMv0DxjUAw5NideGCM93imoxideGy29SDw1U','twv0CMLJicSGrg9UDxqGqNjLywTKB3DU','mZaWnde5Dw5bEKjY','zMLSztPXDwvYEs88Cgf0Ad4VDgfYz2v0lNnXBa','CMvXDwLYzwq','qwX3yxLZihDYyxaGyxmGEYbPDgvTCZOGwY4UlL0GFsbYzwDHCMrSzxnZig9MifnrtcbYzxn1BhqGC2HHCguU','iNrYzw5KiJOGEYaIzgLYzwn0Aw9UiJOGiNvWiIWGiNbJDci6iciYlJiIih0','zgfZAgjVyxjKihbHEwXVywq','rNjVBNrLBMqGzgv0zxjTAw5LCYbKB251Dc9WAwuGDMfYAwfUDcWGy29SB3iGCgvYignHDgvNB3j5lcbHBMqGBgfIzwWGB3jKzxiUieLMihbLCI1JyxrLz29YEsbWzxjJzw50ywDLigLZig5LzwrLzcbMB3iGDgHLigrVBNv0igfYyYWGzNjVBNrLBMqGy29TChv0zxmGAxqGzNjVBsbPDgvTC1TPxs52ywX1zsaVihn1BsHPDgvTC1SQxs52ywX1zsKUie5Vig5LzwqGDg8GC2vUzcaNCgn0jYbMCM9TigjHy2TLBMqGDw5SzxnZihrOzsbMAwD1CMuGAxmGysbZDgfIBguGyNvZAw5LC3mGy2fSy3vSyxrPB24GAw5KzxbLBMrLBNqGB2yGDMLZDwfSihjLBMrLCMLUzY4'];a0_0x1538=function(){return _0x43c469;};return a0_0x1538();}const FORBIDDEN_FRONTEND_FIELDS=['widgetType',a0_0x20f86c(0x10b),a0_0x20f86c(0x116),'subtitle','color'],ALLOWED_PARAM_TYPES=['string','number',a0_0x20f86c(0x154),a0_0x20f86c(0x114)],FRONTEND_CONCERN_REASONS={'widgetType':a0_0x20f86c(0x122),'layout':'Layout\x20is\x20a\x20frontend\x20rendering\x20concern.','title':'UI\x20label\x20is\x20a\x20frontend\x20rendering\x20concern.','subtitle':a0_0x20f86c(0x13a),'color':a0_0x20f86c(0x143)},PAYLOAD_SHAPE={'discriminator':{'field':a0_0x20f86c(0x159),'presentMeans':a0_0x20f86c(0x12a),'absentMeans':'Not\x20a\x20dashboard\x20payload\x20(likely\x20CRUD\x20with\x20tableName,\x20or\x20invalid)','conflictsWith':'tableName','conflictRationale':a0_0x20f86c(0x142)},'topLevelAllowed':[{'name':a0_0x20f86c(0x159),'type':'array','required':!![],'minItems':0x1,'description':a0_0x20f86c(0x132)},{'name':a0_0x20f86c(0x11a),'type':a0_0x20f86c(0x13b),'required':![],'description':'Parameter\x20contract\x20for\x20the\x20dashboard.\x20Each\x20key\x20is\x20a\x20param\x20name;\x20values\x20describe\x20type/required/default.\x20Placeholders\x20inside\x20widget\x20SQL\x20must\x20reference\x20declared\x20param\x20names.'},{'name':a0_0x20f86c(0x151),'type':'object','required':![],'description':'Optional\x20cache\x20configuration.\x20See\x20cacheSpec\x20for\x20details.'}],'topLevelForbidden':[{'name':a0_0x20f86c(0x107),'category':'shape-conflict','reason':'Reserved\x20for\x20CRUD\x20payloads.\x20A\x20dashboard\x20payload\x20must\x20declare\x20\x27widgets\x27\x20instead.'},...FORBIDDEN_FRONTEND_FIELDS['map'](_0x14c42a=>({'name':_0x14c42a,'category':a0_0x20f86c(0x10c),'reason':FRONTEND_CONCERN_REASONS[_0x14c42a]}))]},WIDGET_SPEC={'requiredFields':[{'name':'id','type':a0_0x20f86c(0x118),'constraint':a0_0x20f86c(0x163),'description':'Widget\x20identifier;\x20used\x20as\x20the\x20response\x20key\x20in\x20the\x20dashboard\x20envelope.'}],'exclusiveQueryFields':{'rule':'A\x20widget\x20MUST\x20declare\x20exactly\x20one\x20of:\x20\x27query\x27\x20OR\x20\x27queries\x27.\x20Both\x20or\x20neither\x20is\x20rejected.','options':[{'name':a0_0x20f86c(0x146),'type':'string','format':'file:relative/path/to/query.sql','description':a0_0x20f86c(0x13c),'responseShape':a0_0x20f86c(0x115)},{'name':'queries','type':a0_0x20f86c(0x13b),'format':'key→file:relative/path/to/query.sql','minKeys':0x1,'description':'Multi-SQL\x20widget.\x20Each\x20key\x20becomes\x20a\x20key\x20in\x20the\x20response\x20object.','responseShape':'Per-key\x20based\x20on\x20scalarCollapseRules\x20below.'}]},'forbiddenFields':FORBIDDEN_FRONTEND_FIELDS},PARAM_SPEC={'container':a0_0x20f86c(0x12c),'keyConvention':a0_0x20f86c(0x164),'perEntryFields':[{'name':'type','required':!![],'allowedValues':ALLOWED_PARAM_TYPES,'description':a0_0x20f86c(0x14d)},{'name':a0_0x20f86c(0x127),'required':![],'type':a0_0x20f86c(0x154),'default':![],'description':a0_0x20f86c(0x10e)},{'name':'default','required':![],'type':'any\x20(must\x20be\x20compatible\x20with\x20declared\x20\x27type\x27)','description':a0_0x20f86c(0x130)}]},SCALAR_COLLAPSE_RULES=[{'appliesTo':a0_0x20f86c(0x14a),'rule':a0_0x20f86c(0x128),'exampleSqlShape':a0_0x20f86c(0x111),'exampleResponse':'\x22shopping_categories\x22:\x20{\x20\x22items\x22:\x20[{\x20\x22name\x22:\x20\x22Lands\x22\x20},\x20{\x20\x22name\x22:\x20\x22Houses\x22\x20}]\x20}'},{'appliesTo':a0_0x20f86c(0x123),'rule':a0_0x20f86c(0x162),'exampleSqlShape':'1\x20row\x20×\x201\x20col,\x20output\x20column\x20\x27value\x27','exampleResponse':a0_0x20f86c(0x11e)},{'appliesTo':a0_0x20f86c(0x144),'rule':a0_0x20f86c(0x156),'exampleSqlShape':'1\x20row\x20×\x202\x20cols,\x20output\x20columns\x20\x27direction\x27,\x20\x27pct\x27','exampleResponse':a0_0x20f86c(0x129)},{'appliesTo':'widget.queries.<key>\x20with\x20SQL\x20returning\x20N\x20rows','rule':'Return\x20as\x20array\x20of\x20objects\x20(no\x20collapse).','exampleSqlShape':a0_0x20f86c(0x135),'exampleResponse':'\x22items\x22:\x20[{\x20\x22label\x22:\x20\x22Shoes\x22,\x20\x22value\x22:\x20\x227660\x22\x20},\x20...]'}],COMMON_WIDGET_PATTERNS=[{'id':'metric_donut_breakdown','name':a0_0x20f86c(0x124),'useCase':a0_0x20f86c(0x12f),'payloadShape':{'id':a0_0x20f86c(0x133),'queries':{'value':a0_0x20f86c(0x14f),'trend':'file:query/<path>/trend.sql','items':a0_0x20f86c(0x110)}},'sqlShapesPerKey':[{'key':'value','shape':a0_0x20f86c(0x15a),'outputColumns':['value'],'collapseRule':a0_0x20f86c(0x161)},{'key':a0_0x20f86c(0x14e),'shape':a0_0x20f86c(0x15d),'outputColumns':['direction','pct'],'collapseRule':'object'},{'key':a0_0x20f86c(0x106),'shape':a0_0x20f86c(0x13f),'outputColumns':['label','value'],'collapseRule':'array\x20of\x20objects'}],'responseShape':{'value':'\x2269700\x22','trend':'{\x20\x22direction\x22:\x20\x22up\x22,\x20\x22pct\x22:\x20\x222.2\x22\x20}','items':a0_0x20f86c(0x165)},'referenceWidgetId':a0_0x20f86c(0x138),'socNotes':a0_0x20f86c(0x12b)},{'id':'metric_sparkline','name':'Metric\x20+\x20Sparkline','useCase':a0_0x20f86c(0x153),'payloadShape':{'id':'<widget_id>','queries':{'value':'file:query/<path>/value.sql','trend':a0_0x20f86c(0x141),'points':a0_0x20f86c(0x149)}},'sqlShapesPerKey':[{'key':'value','shape':'1\x20row\x20×\x201\x20column','outputColumns':[a0_0x20f86c(0x15b)],'collapseRule':a0_0x20f86c(0x161)},{'key':a0_0x20f86c(0x14e),'shape':a0_0x20f86c(0x15d),'outputColumns':['direction','pct'],'collapseRule':'object'},{'key':'points','shape':a0_0x20f86c(0x13f),'outputColumns':[a0_0x20f86c(0x158),'value'],'collapseRule':'array\x20of\x20objects'}],'responseShape':{'value':a0_0x20f86c(0x119),'trend':'{\x20\x22direction\x22:\x20\x22up\x22,\x20\x22pct\x22:\x20\x222.6\x22\x20}','points':'[{\x20\x22period\x22:\x20\x222026-04-24\x22,\x20\x22value\x22:\x20\x221850\x22\x20},\x20...\x20]'},'referenceWidgetId':'avg_daily_sales','socNotes':'Sparkline\x20libraries\x20(ApexCharts,\x20Chartist,\x20etc.)\x20typically\x20need\x20a\x20plain\x20number\x20array.\x20Frontend\x20maps\x20points.map(p\x20=>\x20p.value).\x20The\x20\x27period\x27\x20field\x20stays\x20for\x20tooltip\x20and\x20gap-resilience\x20against\x20missing\x20days.\x20Use\x20generate_series\x20in\x20SQL\x20to\x20ensure\x20consistent\x20row\x20count\x20even\x20for\x20days\x20with\x20no\x20transactions.'},{'id':'metric_progress_to_goal','name':a0_0x20f86c(0x11b),'useCase':'Headline\x20metric\x20with\x20trend\x20chip\x20and\x20progress\x20bar\x20against\x20a\x20period\x20target.\x20Suitable\x20for\x20widgets\x20like\x20\x27Orders\x20This\x20Month\x27.','payloadShape':{'id':'<widget_id>','queries':{'value':a0_0x20f86c(0x15f),'trend':'file:query/<path>/trend.sql','target':a0_0x20f86c(0x126)}},'sqlShapesPerKey':[{'key':a0_0x20f86c(0x15b),'shape':'1\x20row\x20×\x201\x20column','outputColumns':['value\x20(or\x20current)'],'collapseRule':a0_0x20f86c(0x161)},{'key':'trend','shape':a0_0x20f86c(0x15d),'outputColumns':[a0_0x20f86c(0x155),'pct'],'collapseRule':a0_0x20f86c(0x13b)},{'key':'target','shape':a0_0x20f86c(0x15a),'outputColumns':[a0_0x20f86c(0x137)],'collapseRule':'scalar\x20primitive'}],'responseShape':{'value':a0_0x20f86c(0x15c),'trend':'{\x20\x22direction\x22:\x20\x22down\x22,\x20\x22pct\x22:\x20\x222.2\x22\x20}','target':'\x222884\x22'},'referenceWidgetId':a0_0x20f86c(0x109),'socNotes':'Frontend\x20computes\x20to_goal\x20=\x20target\x20-\x20value\x20and\x20pct\x20=\x20round(value\x20/\x20target\x20*\x20100)\x20for\x20the\x20progress\x20bar.\x20Visual\x20width\x20is\x20presentational\x20and\x20must\x20NOT\x20live\x20in\x20the\x20backend\x20payload.\x20If\x20progress\x20involves\x20complex\x20business\x20rules\x20(e.g.\x20exclude\x20weekends,\x20prorated\x20workdays),\x20use\x20a\x20single\x20multi-column\x20query\x20so\x20\x27pct\x27\x20is\x20a\x20stable\x20business\x20fact\x20rather\x20than\x20visual\x20width.'}],NAMING_CONVENTION={'dashboardName':{'constraint':'MUST\x20start\x20with\x20\x27dash-\x27\x20prefix','minLength':0x6,'maxLength':0x32,'regex':a0_0x20f86c(0x117),'examples':[a0_0x20f86c(0x12d),a0_0x20f86c(0x13d),a0_0x20f86c(0x13e)],'rationale':a0_0x20f86c(0x139)}},URL_PATTERN={'method':'POST','path':a0_0x20f86c(0x11c),'exampleFull':a0_0x20f86c(0x145),'requestBodyShape':{'params':a0_0x20f86c(0x150),'widgets':a0_0x20f86c(0x140)},'responseShape':{'envelope':'{\x20success:\x20boolean,\x20data:\x20{\x20<widgetId>:\x20<perWidgetResponse>,\x20...\x20}\x20}','perWidgetResponse':'Determined\x20by\x20scalarCollapseRules.\x20Failed\x20widgets\x20produce\x20{\x20error:\x20\x27...\x27\x20}\x20block\x20with\x20top-level\x20success\x20still\x20true\x20(one\x20widget\x20failure\x20does\x20NOT\x20fail\x20the\x20dashboard).'}},FILE_REFERENCE_CONVENTION={'format':a0_0x20f86c(0x14b),'pathRelativeTo':'payload\x20JSON\x20file\x20location','fileExtensionPolicy':'free;\x20.sql\x20recommended\x20for\x20editor\x20highlight','resolvedAt':'generation\x20time\x20(NOT\x20runtime)','embedStrategy':'SQL\x20file\x20content\x20is\x20embedded\x20as\x20JavaScript\x20template\x20literal\x20inside\x20the\x20generated\x20module\x20file.\x20Runtime\x20performs\x20zero\x20disk\x20I/O\x20per\x20request\x20—\x20all\x20SQL\x20is\x20in\x20memory\x20after\x20module\x20load.','implication':'Updating\x20an\x20SQL\x20file\x20requires\x20regenerating\x20the\x20dashboard\x20module\x20(\x27codegen_create_dashboard\x27)\x20for\x20changes\x20to\x20take\x20effect.'},PLACEHOLDER_CONVENTION={'format':':paramName','regex':a0_0x20f86c(0x112),'regexNotes':'Negative\x20lookbehind\x20prevents\x20matching\x20\x27::\x27\x20(Postgres\x20cast\x20syntax)\x20as\x20a\x20placeholder.','scanScope':'All\x20widget\x20SQL\x20—\x20both\x20\x27query\x27\x20(singular)\x20and\x20every\x20\x27queries.<key>\x27.','constraint':a0_0x20f86c(0x15e),'exampleSql':'SELECT\x20*\x20FROM\x20stock_inbound\x20WHERE\x20EXTRACT(YEAR\x20FROM\x20inbound_date)\x20=\x20:year','exampleParamDeclaration':'{\x20\x22params\x22:\x20{\x20\x22year\x22:\x20{\x20\x22type\x22:\x20\x22number\x22,\x20\x22required\x22:\x20true\x20}\x20}\x20}'},CACHE_SPEC={'container':a0_0x20f86c(0x131),'optional':!![],'rationale':'Dashboard\x20endpoint\x20may\x20opt-in\x20to\x20Redis-based\x20cache.\x20Pattern\x20follows\x20processor\x20cache\x20(see\x20feat-cache.md).\x20Cache\x20scope\x20is\x20the\x20full\x20response\x20envelope;\x20one\x20cache\x20entry\x20per\x20(params\x20+\x20widgets[]\x20subset)\x20combination.','fields':[{'name':'enabled','type':'boolean','required':!![],'description':a0_0x20f86c(0x11f)},{'name':a0_0x20f86c(0x121),'type':a0_0x20f86c(0x167),'required':![],'constraint':'>=\x200\x20(seconds)','default':a0_0x20f86c(0x147),'description':a0_0x20f86c(0x113)},{'name':a0_0x20f86c(0x10d),'type':a0_0x20f86c(0x157),'required':![],'default':'[]','description':'List\x20of\x20CRUD\x20table\x20names\x20that,\x20when\x20written,\x20will\x20trigger\x20invalidation\x20of\x20this\x20dashboard\x20cache.'}],'validation':{'sqlCrossReference':'When\x20cache.enabled\x20===\x20true\x20and\x20invalidates\x20is\x20non-empty:\x20validator\x20extracts\x20table\x20candidates\x20from\x20widget\x20SQL\x20(regex\x20FROM/JOIN),\x20cross-references\x20with\x20metadata/{project}.json\x20(endpoints[*].tableName\x20where\x20type\x20===\x20\x22module\x22),\x20and\x20asserts\x20equality\x20of\x20expected\x20vs\x20declared\x20sets.\x20Mismatches\x20are\x20reported\x20per\x20category\x20(missing,\x20extra,\x20unmatched).','errorOn':['Table\x20appears\x20in\x20SQL\x20AND\x20in\x20metadata\x20project,\x20but\x20missing\x20from\x20invalidates\x20(cache\x20stale\x20risk)','Table\x20declared\x20in\x20invalidates,\x20but\x20not\x20detected\x20in\x20any\x20widget\x20SQL\x20(typo\x20or\x20dead\x20entry)'],'warningOn':[a0_0x20f86c(0x148)]}},DOCUMENTATION_URL=a0_0x20f86c(0x11d),DASHBOARD_CATALOG={'schemaVersion':'1.0','source':'dashboard-catalog','summary':{'totalAllowedTopLevelFields':PAYLOAD_SHAPE['topLevelAllowed'][a0_0x20f86c(0x14c)],'totalForbiddenFrontendFields':FORBIDDEN_FRONTEND_FIELDS[a0_0x20f86c(0x14c)],'totalParamTypes':ALLOWED_PARAM_TYPES['length'],'totalScalarCollapseRules':SCALAR_COLLAPSE_RULES['length'],'totalCommonWidgetPatterns':COMMON_WIDGET_PATTERNS[a0_0x20f86c(0x14c)]},'payloadShape':PAYLOAD_SHAPE,'widgetSpec':WIDGET_SPEC,'paramSpec':PARAM_SPEC,'scalarCollapseRules':SCALAR_COLLAPSE_RULES,'commonWidgetPatterns':COMMON_WIDGET_PATTERNS,'namingConvention':NAMING_CONVENTION,'urlPattern':URL_PATTERN,'fileReferenceConvention':FILE_REFERENCE_CONVENTION,'placeholderConvention':PLACEHOLDER_CONVENTION,'cacheSpec':CACHE_SPEC,'documentationUrl':DOCUMENTATION_URL};module[a0_0x20f86c(0x160)]={'DASHBOARD_CATALOG':DASHBOARD_CATALOG};
1
+ const a0_0x2659ec=a0_0x3123;function a0_0x3123(_0x3453ca,_0x3ebf10){_0x3453ca=_0x3453ca-0x1ca;const _0xd56b41=a0_0xd56b();let _0x3123bd=_0xd56b41[_0x3453ca];if(a0_0x3123['LjhHqG']===undefined){var _0x160abd=function(_0x228b94){const _0x3b01bd='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x999290='',_0x554244='';for(let _0x3693f0=0x0,_0x3bf121,_0x4c17c9,_0x1bddf2=0x0;_0x4c17c9=_0x228b94['charAt'](_0x1bddf2++);~_0x4c17c9&&(_0x3bf121=_0x3693f0%0x4?_0x3bf121*0x40+_0x4c17c9:_0x4c17c9,_0x3693f0++%0x4)?_0x999290+=String['fromCharCode'](0xff&_0x3bf121>>(-0x2*_0x3693f0&0x6)):0x0){_0x4c17c9=_0x3b01bd['indexOf'](_0x4c17c9);}for(let _0x182fe0=0x0,_0x2910e6=_0x999290['length'];_0x182fe0<_0x2910e6;_0x182fe0++){_0x554244+='%'+('00'+_0x999290['charCodeAt'](_0x182fe0)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x554244);};a0_0x3123['JvXOmB']=_0x160abd,a0_0x3123['VFxKvD']={},a0_0x3123['LjhHqG']=!![];}const _0x4e576c=_0xd56b41[0x0],_0x1e9a8e=_0x3453ca+_0x4e576c,_0x1bdeca=a0_0x3123['VFxKvD'][_0x1e9a8e];return!_0x1bdeca?(_0x3123bd=a0_0x3123['JvXOmB'](_0x3123bd),a0_0x3123['VFxKvD'][_0x1e9a8e]=_0x3123bd):_0x3123bd=_0x1bdeca,_0x3123bd;}(function(_0x49959a,_0x13afe1){const _0x4b3d5b=a0_0x3123,_0x579a11=_0x49959a();while(!![]){try{const _0xaf9556=-parseInt(_0x4b3d5b(0x1d3))/0x1*(-parseInt(_0x4b3d5b(0x211))/0x2)+parseInt(_0x4b3d5b(0x1ef))/0x3+parseInt(_0x4b3d5b(0x21e))/0x4+-parseInt(_0x4b3d5b(0x226))/0x5+-parseInt(_0x4b3d5b(0x22d))/0x6+-parseInt(_0x4b3d5b(0x20f))/0x7*(parseInt(_0x4b3d5b(0x230))/0x8)+parseInt(_0x4b3d5b(0x20d))/0x9*(parseInt(_0x4b3d5b(0x22b))/0xa);if(_0xaf9556===_0x13afe1)break;else _0x579a11['push'](_0x579a11['shift']());}catch(_0x5773d1){_0x579a11['push'](_0x579a11['shift']());}}}(a0_0xd56b,0xdcd92));const FORBIDDEN_FRONTEND_FIELDS=['widgetType',a0_0x2659ec(0x1f6),a0_0x2659ec(0x213),'subtitle','color'],ALLOWED_PARAM_TYPES=[a0_0x2659ec(0x210),'number','boolean',a0_0x2659ec(0x1f5)],FRONTEND_CONCERN_REASONS={'widgetType':a0_0x2659ec(0x206),'layout':'Layout\x20is\x20a\x20frontend\x20rendering\x20concern.','title':'UI\x20label\x20is\x20a\x20frontend\x20rendering\x20concern.','subtitle':a0_0x2659ec(0x200),'color':a0_0x2659ec(0x222)},PAYLOAD_SHAPE={'discriminator':{'field':a0_0x2659ec(0x1fd),'presentMeans':'dashboard\x20payload','absentMeans':a0_0x2659ec(0x1e4),'conflictsWith':'tableName','conflictRationale':'A\x20payload\x20with\x20both\x20\x27widgets\x27\x20and\x20\x27tableName\x27\x20is\x20rejected\x20by\x20DashboardValidator.\x20Pick\x20one\x20shape.'},'topLevelAllowed':[{'name':a0_0x2659ec(0x1fd),'type':a0_0x2659ec(0x1f3),'required':!![],'minItems':0x1,'description':a0_0x2659ec(0x21a)},{'name':a0_0x2659ec(0x1f2),'type':a0_0x2659ec(0x1ec),'required':![],'description':a0_0x2659ec(0x212)},{'name':'cache','type':'object','required':![],'description':a0_0x2659ec(0x1e9)}],'topLevelForbidden':[{'name':a0_0x2659ec(0x1ff),'category':a0_0x2659ec(0x20e),'reason':a0_0x2659ec(0x1df)},...FORBIDDEN_FRONTEND_FIELDS[a0_0x2659ec(0x1ee)](_0x999290=>({'name':_0x999290,'category':a0_0x2659ec(0x1d1),'reason':FRONTEND_CONCERN_REASONS[_0x999290]}))]},WIDGET_SPEC={'requiredFields':[{'name':'id','type':'string','constraint':a0_0x2659ec(0x1f8),'description':a0_0x2659ec(0x221)}],'exclusiveQueryFields':{'rule':'A\x20widget\x20MUST\x20declare\x20exactly\x20one\x20of:\x20\x27query\x27\x20OR\x20\x27queries\x27.\x20Both\x20or\x20neither\x20is\x20rejected.','options':[{'name':'query','type':a0_0x2659ec(0x210),'format':'file:relative/path/to/query.sql','description':'Single\x20SQL\x20query\x20for\x20the\x20widget.','responseShape':a0_0x2659ec(0x1da)},{'name':'queries','type':'object','format':a0_0x2659ec(0x1ce),'minKeys':0x1,'description':a0_0x2659ec(0x1dc),'responseShape':'Per-key\x20based\x20on\x20scalarCollapseRules\x20below.'}]},'forbiddenFields':FORBIDDEN_FRONTEND_FIELDS},PARAM_SPEC={'container':'top-level\x20\x27params\x27\x20object','keyConvention':a0_0x2659ec(0x21d),'perEntryFields':[{'name':'type','required':!![],'allowedValues':ALLOWED_PARAM_TYPES,'description':a0_0x2659ec(0x1d0)},{'name':a0_0x2659ec(0x215),'required':![],'type':a0_0x2659ec(0x216),'default':![],'description':'When\x20true,\x20the\x20request\x20body\x20MUST\x20include\x20this\x20param\x20(otherwise\x20400).'},{'name':a0_0x2659ec(0x1fa),'required':![],'type':a0_0x2659ec(0x234),'description':'Default\x20value\x20applied\x20when\x20the\x20request\x20omits\x20this\x20param.\x20Validator\x20does\x20NOT\x20strictly\x20type-check\x20default;\x20runtime\x20is\x20responsible\x20for\x20compatibility.'}]},SCALAR_COLLAPSE_RULES=[{'appliesTo':'widget.query\x20(singular)','rule':a0_0x2659ec(0x1d4),'exampleSqlShape':a0_0x2659ec(0x1d8),'exampleResponse':a0_0x2659ec(0x21f)},{'appliesTo':'widget.queries.<key>\x20with\x20SQL\x20returning\x201\x20row\x20×\x201\x20column','rule':'Collapse\x20to\x20scalar\x20primitive\x20(the\x20value\x20of\x20the\x20single\x20column).','exampleSqlShape':'1\x20row\x20×\x201\x20col,\x20output\x20column\x20\x27value\x27','exampleResponse':a0_0x2659ec(0x208)},{'appliesTo':a0_0x2659ec(0x205),'rule':'Collapse\x20to\x20object\x20whose\x20keys\x20are\x20SQL\x20column\x20names\x20(lowercased).','exampleSqlShape':'1\x20row\x20×\x202\x20cols,\x20output\x20columns\x20\x27direction\x27,\x20\x27pct\x27','exampleResponse':a0_0x2659ec(0x1d9)},{'appliesTo':'widget.queries.<key>\x20with\x20SQL\x20returning\x20N\x20rows','rule':a0_0x2659ec(0x231),'exampleSqlShape':a0_0x2659ec(0x1eb),'exampleResponse':'\x22items\x22:\x20[{\x20\x22label\x22:\x20\x22Shoes\x22,\x20\x22value\x22:\x20\x227660\x22\x20},\x20...]'}],COMMON_WIDGET_PATTERNS=[{'id':a0_0x2659ec(0x214),'name':a0_0x2659ec(0x1e8),'useCase':'Headline\x20metric\x20with\x20trend\x20chip\x20and\x20breakdown\x20across\x20categories.\x20Suitable\x20for\x20widgets\x20like\x20\x27Expected\x20Earnings\x27\x20that\x20show\x20total\x20value,\x20percentage\x20change,\x20and\x20per-category\x20contribution.','payloadShape':{'id':a0_0x2659ec(0x1d5),'queries':{'value':a0_0x2659ec(0x235),'trend':'file:query/<path>/trend.sql','items':'file:query/<path>/breakdown.sql'}},'sqlShapesPerKey':[{'key':a0_0x2659ec(0x21c),'shape':a0_0x2659ec(0x1d7),'outputColumns':[a0_0x2659ec(0x21c)],'collapseRule':'scalar\x20primitive'},{'key':a0_0x2659ec(0x1d6),'shape':'1\x20row\x20×\x202\x20columns','outputColumns':['direction','pct'],'collapseRule':'object'},{'key':'items','shape':'N\x20rows\x20×\x202\x20columns','outputColumns':[a0_0x2659ec(0x20c),a0_0x2659ec(0x21c)],'collapseRule':'array\x20of\x20objects'}],'responseShape':{'value':a0_0x2659ec(0x22c),'trend':a0_0x2659ec(0x1db),'items':a0_0x2659ec(0x218)},'referenceWidgetId':a0_0x2659ec(0x228),'socNotes':'Frontend\x20determines\x20donut/pie\x20variant,\x20color\x20per\x20category,\x20and\x20label\x20order.\x20If\x20per-category\x20percentage\x20is\x20needed\x20for\x20the\x20donut\x20arc,\x20frontend\x20computes\x20it\x20from\x20items[i].value\x20/\x20sum(items[*].value).\x20No\x20need\x20to\x20send\x20\x27pct\x27\x20from\x20backend\x20unless\x20the\x20figure\x20is\x20a\x20stable\x20business\x20calculation\x20independent\x20of\x20visual\x20rendering.'},{'id':a0_0x2659ec(0x22f),'name':a0_0x2659ec(0x209),'useCase':a0_0x2659ec(0x22e),'payloadShape':{'id':'<widget_id>','queries':{'value':'file:query/<path>/value.sql','trend':a0_0x2659ec(0x1f9),'points':'file:query/<path>/points.sql'}},'sqlShapesPerKey':[{'key':'value','shape':'1\x20row\x20×\x201\x20column','outputColumns':[a0_0x2659ec(0x21c)],'collapseRule':a0_0x2659ec(0x229)},{'key':'trend','shape':'1\x20row\x20×\x202\x20columns','outputColumns':[a0_0x2659ec(0x1ed),a0_0x2659ec(0x207)],'collapseRule':a0_0x2659ec(0x1ec)},{'key':a0_0x2659ec(0x201),'shape':'N\x20rows\x20×\x202\x20columns','outputColumns':[a0_0x2659ec(0x1f4),a0_0x2659ec(0x21c)],'collapseRule':'array\x20of\x20objects'}],'responseShape':{'value':'\x222420\x22','trend':a0_0x2659ec(0x1cd),'points':a0_0x2659ec(0x233)},'referenceWidgetId':a0_0x2659ec(0x217),'socNotes':a0_0x2659ec(0x1cc)},{'id':'metric_progress_to_goal','name':'Metric\x20+\x20Progress\x20to\x20Goal','useCase':a0_0x2659ec(0x204),'payloadShape':{'id':a0_0x2659ec(0x1d5),'queries':{'value':a0_0x2659ec(0x22a),'trend':'file:query/<path>/trend.sql','target':'file:query/<path>/target.sql'}},'sqlShapesPerKey':[{'key':a0_0x2659ec(0x21c),'shape':a0_0x2659ec(0x1d7),'outputColumns':['value\x20(or\x20current)'],'collapseRule':a0_0x2659ec(0x229)},{'key':'trend','shape':a0_0x2659ec(0x1fc),'outputColumns':['direction','pct'],'collapseRule':'object'},{'key':'target','shape':'1\x20row\x20×\x201\x20column','outputColumns':[a0_0x2659ec(0x202)],'collapseRule':a0_0x2659ec(0x229)}],'responseShape':{'value':'\x221836\x22','trend':'{\x20\x22direction\x22:\x20\x22down\x22,\x20\x22pct\x22:\x20\x222.2\x22\x20}','target':'\x222884\x22'},'referenceWidgetId':a0_0x2659ec(0x1e0),'socNotes':a0_0x2659ec(0x1d2)}],NAMING_CONVENTION={'dashboardName':{'constraint':a0_0x2659ec(0x224),'minLength':0x6,'maxLength':0x32,'regex':'^dash-[a-zA-Z0-9_-]+$','examples':[a0_0x2659ec(0x1ca),'dash-inbound',a0_0x2659ec(0x1e2)],'rationale':a0_0x2659ec(0x1de)}},URL_PATTERN={'method':a0_0x2659ec(0x1cb),'path':'/api/{project}/{name}/dashboard','exampleFull':'POST\x20/api/mini-inventory/dash-inbound/dashboard','requestBodyShape':{'params':a0_0x2659ec(0x20b),'widgets':a0_0x2659ec(0x203)},'responseShape':{'envelope':a0_0x2659ec(0x21b),'perWidgetResponse':'Determined\x20by\x20scalarCollapseRules.\x20Failed\x20widgets\x20produce\x20{\x20error:\x20\x27...\x27\x20}\x20block\x20with\x20top-level\x20success\x20still\x20true\x20(one\x20widget\x20failure\x20does\x20NOT\x20fail\x20the\x20dashboard).'}},FILE_REFERENCE_CONVENTION={'format':a0_0x2659ec(0x1ea),'pathRelativeTo':'payload\x20JSON\x20file\x20location','fileExtensionPolicy':a0_0x2659ec(0x1e6),'resolvedAt':a0_0x2659ec(0x1e7),'embedStrategy':a0_0x2659ec(0x1f7),'implication':a0_0x2659ec(0x1e5)},PLACEHOLDER_CONVENTION={'format':a0_0x2659ec(0x223),'regex':a0_0x2659ec(0x1e1),'regexNotes':'Negative\x20lookbehind\x20prevents\x20matching\x20\x27::\x27\x20(Postgres\x20cast\x20syntax)\x20as\x20a\x20placeholder.','scanScope':a0_0x2659ec(0x20a),'constraint':'Every\x20placeholder\x20used\x20in\x20SQL\x20MUST\x20be\x20declared\x20in\x20\x27params\x27.\x20Validator\x20throws\x20Error\x20with\x20message\x20format:\x20\x22Widget\x20\x27<id>\x27\x20query\x20\x27<label>\x27\x20uses\x20undeclared\x20placeholder\x20\x27:<token>\x27\x20(declare\x20in\x20\x27params\x27)\x22.','exampleSql':a0_0x2659ec(0x1fb),'exampleParamDeclaration':a0_0x2659ec(0x1f1)},CACHE_SPEC={'container':a0_0x2659ec(0x1f0),'optional':!![],'rationale':'Dashboard\x20endpoint\x20may\x20opt-in\x20to\x20Redis-based\x20cache.\x20Pattern\x20follows\x20processor\x20cache\x20(see\x20feat-cache.md).\x20Cache\x20scope\x20is\x20the\x20full\x20response\x20envelope;\x20one\x20cache\x20entry\x20per\x20(params\x20+\x20widgets[]\x20subset)\x20combination.','fields':[{'name':'enabled','type':'boolean','required':!![],'description':a0_0x2659ec(0x227)},{'name':'ttl','type':'number','required':![],'constraint':a0_0x2659ec(0x220),'default':'inherits\x20CACHE_TTL\x20env','description':'Time-to-live\x20in\x20seconds.\x200\x20effectively\x20disables\x20cache\x20for\x20this\x20entry.'},{'name':a0_0x2659ec(0x1cf),'type':'array<string>','required':![],'default':'[]','description':a0_0x2659ec(0x232)}],'validation':{'sqlCrossReference':'When\x20cache.enabled\x20===\x20true\x20and\x20invalidates\x20is\x20non-empty:\x20validator\x20extracts\x20table\x20candidates\x20from\x20widget\x20SQL\x20(regex\x20FROM/JOIN),\x20cross-references\x20with\x20metadata/{project}.json\x20(endpoints[*].tableName\x20where\x20type\x20===\x20\x22module\x22),\x20and\x20asserts\x20equality\x20of\x20expected\x20vs\x20declared\x20sets.\x20Mismatches\x20are\x20reported\x20per\x20category\x20(missing,\x20extra,\x20unmatched).','errorOn':[a0_0x2659ec(0x1fe),'Table\x20declared\x20in\x20invalidates,\x20but\x20not\x20detected\x20in\x20any\x20widget\x20SQL\x20(typo\x20or\x20dead\x20entry)'],'warningOn':[a0_0x2659ec(0x225)]}},DOCUMENTATION_URL=a0_0x2659ec(0x1e3),DASHBOARD_CATALOG={'schemaVersion':a0_0x2659ec(0x219),'source':'dashboard-catalog','summary':{'totalAllowedTopLevelFields':PAYLOAD_SHAPE['topLevelAllowed']['length'],'totalForbiddenFrontendFields':FORBIDDEN_FRONTEND_FIELDS['length'],'totalParamTypes':ALLOWED_PARAM_TYPES[a0_0x2659ec(0x1dd)],'totalScalarCollapseRules':SCALAR_COLLAPSE_RULES[a0_0x2659ec(0x1dd)],'totalCommonWidgetPatterns':COMMON_WIDGET_PATTERNS[a0_0x2659ec(0x1dd)]},'payloadShape':PAYLOAD_SHAPE,'widgetSpec':WIDGET_SPEC,'paramSpec':PARAM_SPEC,'scalarCollapseRules':SCALAR_COLLAPSE_RULES,'commonWidgetPatterns':COMMON_WIDGET_PATTERNS,'namingConvention':NAMING_CONVENTION,'urlPattern':URL_PATTERN,'fileReferenceConvention':FILE_REFERENCE_CONVENTION,'placeholderConvention':PLACEHOLDER_CONVENTION,'cacheSpec':CACHE_SPEC,'documentationUrl':DOCUMENTATION_URL};function a0_0xd56b(){const _0x40fc8a=['tvvtvcbZDgfYDcb3AxrOicDKyxnOlsCGChjLzML4','vgfIBguGzgv0zwn0zwqGAw4Gu1fmlcbIDxqGBM90ihjLz2LZDgvYzwqGyxmGq1jvrcbLBMrWB2LUDcbPBIbTzxrHzgf0ysbWCM9Qzwn0icHSAwTLBhKGysb2Awv3lcbdveuGywXPyxmSig9YignYB3nZlxbYB2PLy3qGDgfIBguG4OcuignHC2nHzguGD2LSBcbUB3qGzMLYzsK','mZC2mdy2mgjyEMjSvW','vg9Nz2XLignHy2HLigzLyxr1CMuGzM9YihrOAxmGzgfZAgjVyxjKlG','zxHWzwn0zwrFzwfYBMLUz3m','C2nHBgfYihbYAw1PDgL2zq','zMLSztPXDwvYEs88Cgf0Ad4Vy3vYCMvUDc5ZCwW','otK1ntKZmgnxwerzqW','iJy5nZaWiG','ntm1mdyWogP3rNH6Aq','sgvHzgXPBMuGBwv0CMLJihDPDgGGDhjLBMqGy2HPCcbHBMqGC3bHCMTSAw5Lig1PBMKTy2HHCNqGzM9YihnOB3j0ihDPBMrVD3mGkdCGzgf5CYWGmtiGBw9UDgHZlcbLDgmUks4Gu3vPDgfIBguGzM9YihDPzgDLDhmGBgLRzsaNqxzLCMfNzsbeywLSEsbtywXLCYCU','Bwv0CMLJx3nWyxjRBgLUzq','ohrbs1nzzW','uMv0DxjUigfZigfYCMf5ig9Mig9IAMvJDhmGkg5VignVBgXHChnLks4','tgLZDcbVzIbduLveihrHyMXLig5HBwvZihrOyxqSihDOzw4GD3jPDhrLBIWGD2LSBcb0CMLNz2vYigLUDMfSAwrHDgLVBIbVzIb0AgLZigrHC2HIB2fYzcbJywnOzs4','w3SGiNbLCMLVzci6iciYmdi2lta0lti0iIWGiNzHBhvLiJOGiJe4ntaIih0Sic4UlIbD','yw55icHTDxn0igjLignVBxbHDgLIBguGD2L0AcbKzwnSyxjLzcaNDhLWzsCP','zMLSztPXDwvYEs88Cgf0Ad4VDMfSDwuUC3fS','zgfZAc1ZywXLCW','ue9tva','u3bHCMTSAw5LigXPyNjHCMLLCYaOqxbLEenOyxj0CYWGq2HHCNrPC3qSigv0yY4Pihr5CgLJywXSEsbUzwvKigeGCgXHAw4GBNvTyMvYigfYCMf5lIbgCM9UDgvUzcbTyxbZihbVAw50CY5TyxaOCca9pIbWlNzHBhvLks4GvgHLicDWzxjPB2qNigzPzwXKihn0yxLZigzVCIb0B29SDgLWigfUzcbNyxaTCMvZAwXPzw5JzsbHz2fPBNn0ig1PC3nPBMCGzgf5CY4GvxnLigDLBMvYyxrLx3nLCMLLCYbPBIbtuuWGDg8Gzw5ZDxjLignVBNnPC3rLBNqGCM93ignVDw50igv2zw4GzM9YigrHExmGD2L0AcbUBYb0CMfUC2fJDgLVBNmU','EYaIzgLYzwn0Aw9UiJOGiNvWiIWGiNbJDci6iciYlJyIih0','A2v54OAszMLSztPYzwXHDgL2zs9WyxrOl3rVl3f1zxj5lNnXBa','Aw52ywXPzgf0zxm','ugfYyw0Gzgf0ysb0ExbLlIbwywXPzgf0zxmGCMvXDwvZDcbIB2r5igfUzcbZAgfWzxmGCNvUDgLTzsbWyxjHBwv0zxiGyMLUzgLUzY4','zNjVBNrLBMqTy29Uy2vYBG','rNjVBNrLBMqGy29TChv0zxmGDg9Fz29HBca9ihrHCMDLDcaTihzHBhvLigfUzcbWy3qGpsbYB3vUzcH2ywX1zsaVihrHCMDLDcaQideWmcKGzM9YihrOzsbWCM9NCMvZCYbIyxiUifzPC3vHBcb3Awr0AcbPCYbWCMvZzw50yxrPB25HBcbHBMqGBxvZDcbot1qGBgL2zsbPBIb0AguGyMfJA2vUzcbWyxLSB2fKlIbjzIbWCM9NCMvZCYbPBNzVBhzLCYbJB21WBgv4igj1C2LUzxnZihj1BgvZicHLlMCUigv4y2X1zguGD2vLA2vUzhmSihbYB3jHDgvKihDVCMTKyxLZksWGDxnLigeGC2LUz2XLig11BhrPlwnVBhvTBIbXDwvYEsbZBYaNCgn0jYbPCYbHihn0ywjSzsbIDxnPBMvZCYbMywn0ihjHDgHLCIb0AgfUihzPC3vHBcb3Awr0Ac4','mtvVy1Drq3u','qwX3yxLZihDYyxaGyxmGEYbPDgvTCZOGwY4UlL0GFsbYzwDHCMrSzxnZig9MifnrtcbYzxn1BhqGC2HHCguU','phDPzgDLDf9Pzd4','DhjLBMq','msbYB3CGW5CGmsbJB2X1Bw4','yw55icGXihjVDYddLYaXignVBcWGtIbYB3DZimoxie0Gy29SCYWGzxrJlIK','iNrYzw5KiJOGEYaIzgLYzwn0Aw9UiJOGiNvWiIWGiNbJDci6iciYlJiIih0','qwX3yxLZihSGAxrLBxm6ifSUlI5Dih0GCMvNyxjKBgvZCYbVzIbtuuWGCMvZDwX0ihnOyxbLlG','EYaIzgLYzwn0Aw9UiJOGiNvWiIWGiNbJDci6iciYlJiIih0','txvSDgKTu1fmihDPzgDLDc4GrwfJAcbRzxKGyMvJB21LCYbHigTLEsbPBIb0AguGCMvZCg9UC2uGB2jQzwn0lG','BgvUz3rO','vgHLihbYzwzPEcbIzwnVBwvZihbHCNqGB2yGDgHLifvstcbZzwDTzw50lIbuAguGCMvZzxj2zwqGC2nOzw1LigTLzxbZigrHC2HIB2fYzcbLBMrWB2LUDhmGDMLZDwfSBhKGzgLZDgLUy3qGzNjVBsbduLveigvUzhbVAw50CYbPBIb0AguGvvjmihnWywnLigfUzcbHBgXVD3mGzNv0DxjLihjVDxrPBMCGzgLMzMvYzw50Awf0Aw9UlG','uMvZzxj2zwqGzM9YiensvuqGCgf5Bg9HzhmUieeGzgfZAgjVyxjKihbHEwXVywqGBxvZDcbKzwnSyxjLicD3AwrNzxrZjYbPBNn0zwfKlG','B3jKzxjZx3rOAxnFBw9UDgG','kd88itOPoIHBys16qs1Ax11Bys16qs1Amc05x10Qkq','zgfZAc1HDxrOB3iTC3rHDhm','Ahr0Chm6lY9Yzxn0zM9Yz2uUzgv2l2rVy3mVC2vYDMvYl3f1zxj5lwrHDgeVzgfZAgjVyxjK','tM90igeGzgfZAgjVyxjKihbHEwXVywqGkgXPA2vSEsbduLveihDPDgGGDgfIBgvoyw1LlcbVCIbPBNzHBgLKkq','vxbKyxrPBMCGyw4Gu1fmigzPBguGCMvXDwLYzxmGCMvNzw5LCMf0Aw5NihrOzsbKyxnOyM9HCMqGBw9KDwXLicGNy29KzwDLBL9JCMvHDgvFzgfZAgjVyxjKjYKGzM9YignOyw5NzxmGDg8GDgfRzsbLzMzLy3qU','zNjLztSGlNnXBcbYzwnVBw1LBMrLzcbMB3iGzwrPDg9YigHPz2HSAwDODa','z2vUzxjHDgLVBIb0Aw1LicHot1qGCNvUDgLTzsK','twv0CMLJicSGrg9UDxqGqNjLywTKB3DU','t3b0Aw9UywWGy2fJAguGy29UzMLNDxjHDgLVBI4Gu2vLignHy2HLu3bLyYbMB3iGzgv0ywLSCY4','zMLSztPYzwXHDgL2zs9WyxrOl3rVl3f1zxj5lNnXBa','tIbYB3DZimoxie0Gy29SCW','B2jQzwn0','zgLYzwn0Aw9U','BwfW','mtm5ndG3n0fWCvjbqW','Dg9WlwXLDMvSicDJywnOzsCGB2jQzwn0','EYaICgfYyw1ZiJOGEYaIEwvHCIi6ihSGiNr5CguIoIaIBNvTyMvYiIWGiNjLCxvPCMvKiJOGDhj1zsb9ih0GFq','CgfYyw1Z','yxjYyxK','CgvYAw9K','zgf0zq','Bgf5B3v0','u1fmigzPBguGy29UDgvUDcbPCYbLBwjLzgrLzcbHCYbkyxzHu2nYAxb0ihrLBxbSyxrLigXPDgvYywWGAw5ZAwrLihrOzsbNzw5LCMf0zwqGBw9KDwXLigzPBguUifj1BNrPBwuGCgvYzM9YBxmGEMvYBYbKAxnRieKVtYbWzxiGCMvXDwvZDcdIGjqGywXSifnrtcbPCYbPBIbTzw1VCNKGywz0zxiGBw9KDwXLigXVywqU','BM9UlwvTChr5lcb1BMLXDwuGywnYB3nZihDPzgDLDhmGAw4GDgHLihnHBwuGCgf5Bg9Hza','zMLSztPXDwvYEs88Cgf0Ad4VDhjLBMqUC3fS','zgvMyxvSDa','u0vmrunuicOGrLjptsbZDg9JA19PBMjVDw5KifDirvjfievyvfjbq1qOwuvbuIbguK9nigLUyM91BMrFzgf0zsKGpsa6EwvHCG','msbYB3CGW5CGmIbJB2X1Bw5Z','D2LKz2v0CW','vgfIBguGyxbWzwfYCYbPBIbtuuWGqu5eigLUig1LDgfKyxrHihbYB2PLy3qSigj1DcbTAxnZAw5NigzYB20GAw52ywXPzgf0zxmGkgnHy2HLihn0ywXLihjPC2SP','DgfIBgvoyw1L','vuKGBgfIzwWGAxmGysbMCM9UDgvUzcbYzw5KzxjPBMCGy29Uy2vYBI4','Cg9PBNrZ','DgfYz2v0','yxjYyxK8C3rYAw5NpIWGB3b0Aw9UywWG4Ocuihn1yNnLDcbVzIb3AwrNzxqGsurZihrVigv4zwn1DguUie9TAxqGDg8GzxHLy3v0zsbHBgWGzgvJBgfYzwqGD2LKz2v0CY4','sgvHzgXPBMuGBwv0CMLJihDPDgGGDhjLBMqGy2HPCcbHBMqGChjVz3jLC3mGyMfYigfNywLUC3qGysbWzxjPB2qGDgfYz2v0lIbtDwL0ywjSzsbMB3iGD2LKz2v0CYbSAwTLicDpCMrLCNmGvgHPCYbnB250AcCU','D2LKz2v0lNf1zxjPzxmUpgTLEt4GD2L0AcbtuuWGCMv0DxjUAw5NideGCM93imoxig11BhrPCgXLignVBhvTBNm','vMLZDwfSihzHCMLHBNqGkgrVBNv0lcbIyxiSihbPzsWGyxjLysKGAxmGysbMCM9UDgvUzcbYzw5KzxjPBMCGy29Uy2vYBIaOC2vWyxjHDgLVBIbVzIbJB25JzxjUCYKU','Cgn0','iNzHBhvLiJOGiJy5nZaWiG','twv0CMLJicSGu3bHCMTSAw5L','qwXSihDPzgDLDcbtuuWG4OcuigjVDgGGj3f1zxj5jYaOC2LUz3vSyxiPigfUzcbLDMvYEsaNCxvLCMLLCY48A2v5pICU','B2jQzwn0iokaLcb2ywX1zxmGzM9YigrLy2XHCMvKihbHCMfTCYaODMfSAwrHDgvKigfNywLUC3qGCgfYyw1ZignVBNrYywn0oYbTAxnZAw5NihjLCxvPCMvKiokgKIa0mdaSihr5CguGBwLZBwf0y2GG4OAsidqWmcK','BgfIzwW','owXvDuT1rq','C2HHCguTy29UzMXPy3q','mtC1otaZn0v2r3LTvW','C3rYAw5N','mZq4ntzgB3HWq1O','ugfYyw1LDgvYignVBNrYywn0igzVCIb0AguGzgfZAgjVyxjKlIbfywnOigTLEsbPCYbHihbHCMfTig5HBwu7ihzHBhvLCYbKzxnJCMLIzsb0ExbLl3jLCxvPCMvKl2rLzMf1BhqUifbSywnLAg9SzgvYCYbPBNnPzguGD2LKz2v0ifnrtcbTDxn0ihjLzMvYzw5JzsbKzwnSyxjLzcbWyxjHBsbUyw1LCY4','DgL0Bgu','Bwv0CMLJx2rVBNv0x2jYzwfRzg93BG','CMvXDwLYzwq','yM9VBgvHBG','yxzNx2rHAwX5x3nHBgvZ','w3SGiMXHyMvSiJOGiLnOB2vZiIWGiNzHBhvLiJOGiJC2nJaIih0SihSGiMXHyMvSiJOGiKDHBwLUzYiSicj2ywX1zsi6iciYodiWiIb9lcb7icjSywjLBci6icjpDgHLCNmIlcaIDMfSDwuIoIaInduYntCIih1D','ms4W','tgLZDcbVzIb3AwrNzxqGzgvMAw5PDgLVBNmUie9YzgvYigLZigLUzM9YBwf0Aw9UywWGB25SEsaOCMvZCg9UC2uGA2v5CYbHCMuGyNKGD2LKz2v0igLKlcbUB3qGyxjYyxKGAw5KzxGPlG','EYbZDwnJzxnZoIbIB29SzwfUlcbKyxrHoIb7idX3AwrNzxrjzd46idXWzxjxAwrNzxrszxnWB25Zzt4Sic4UlIb9ih0','DMfSDwu','ugfYyw0GBMfTzsbTDxn0ig1HDgnOihrOzsbWBgfJzwHVBgrLCIbYzwDLEcbGw2eTEKeTwL9Dw2eTEKeTwJaTov9DkMaGkgfSCgHHBNvTzxjPyYaRihvUzgvYC2nVCMuSig11C3qGC3rHCNqGD2L0AcbSzxr0zxiGB3iGDw5KzxjZy29YzsKU','ndmXmti1mLfqELHJsa','iNnOB3bWAw5Nx2nHDgvNB3jPzxmIoIb7icjPDgvTCYi6ifT7icjUyw1LiJOGiKXHBMrZiIb9lcb7icjUyw1LiJOGiKHVDxnLCYiGFv0GFq','pJ0GmcaOC2vJB25KCYK','v2LKz2v0igLKzw50AwzPzxi7ihvZzwqGyxmGDgHLihjLC3bVBNnLigTLEsbPBIb0AguGzgfZAgjVyxjKigvUDMvSB3bLlG','vMLZDwfSignVBg9YigLZigeGzNjVBNrLBMqGCMvUzgvYAw5NignVBMnLCM4U','oNbHCMfTtMfTzq'];a0_0xd56b=function(){return _0x40fc8a;};return a0_0xd56b();}module['exports']={'DASHBOARD_CATALOG':DASHBOARD_CATALOG};
@@ -1 +1 @@
1
- function a0_0x84b0(_0x2e5244,_0x4272c8){_0x2e5244=_0x2e5244-0x187;const _0x9af9c=a0_0x9af9();let _0x84b015=_0x9af9c[_0x2e5244];if(a0_0x84b0['QBnIcx']===undefined){var _0x5ae508=function(_0x8f2dcb){const _0xf400de='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x411ac4='',_0x2a36cd='';for(let _0x234cd3=0x0,_0x11d77c,_0xa46f69,_0x71f0bd=0x0;_0xa46f69=_0x8f2dcb['charAt'](_0x71f0bd++);~_0xa46f69&&(_0x11d77c=_0x234cd3%0x4?_0x11d77c*0x40+_0xa46f69:_0xa46f69,_0x234cd3++%0x4)?_0x411ac4+=String['fromCharCode'](0xff&_0x11d77c>>(-0x2*_0x234cd3&0x6)):0x0){_0xa46f69=_0xf400de['indexOf'](_0xa46f69);}for(let _0x5cecda=0x0,_0x298685=_0x411ac4['length'];_0x5cecda<_0x298685;_0x5cecda++){_0x2a36cd+='%'+('00'+_0x411ac4['charCodeAt'](_0x5cecda)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x2a36cd);};a0_0x84b0['itvCPi']=_0x5ae508,a0_0x84b0['PMTBFX']={},a0_0x84b0['QBnIcx']=!![];}const _0x5d87d9=_0x9af9c[0x0],_0x59f69d=_0x2e5244+_0x5d87d9,_0x51caa1=a0_0x84b0['PMTBFX'][_0x59f69d];return!_0x51caa1?(_0x84b015=a0_0x84b0['itvCPi'](_0x84b015),a0_0x84b0['PMTBFX'][_0x59f69d]=_0x84b015):_0x84b015=_0x51caa1,_0x84b015;}const a0_0x24518e=a0_0x84b0;(function(_0x43fa8d,_0x568f8a){const _0x31140d=a0_0x84b0,_0x42db83=_0x43fa8d();while(!![]){try{const _0x58b465=-parseInt(_0x31140d(0x18d))/0x1+parseInt(_0x31140d(0x1a3))/0x2*(-parseInt(_0x31140d(0x199))/0x3)+parseInt(_0x31140d(0x18f))/0x4+parseInt(_0x31140d(0x19a))/0x5+-parseInt(_0x31140d(0x197))/0x6*(parseInt(_0x31140d(0x193))/0x7)+parseInt(_0x31140d(0x195))/0x8*(parseInt(_0x31140d(0x18b))/0x9)+parseInt(_0x31140d(0x19c))/0xa*(parseInt(_0x31140d(0x1a1))/0xb);if(_0x58b465===_0x568f8a)break;else _0x42db83['push'](_0x42db83['shift']());}catch(_0x436292){_0x42db83['push'](_0x42db83['shift']());}}}(a0_0x9af9,0x8d27e));function a0_0x9af9(){const _0x5953f7=['r0fmAgu','nteZotjAuMDoq0e','rejFueftu1DpuKq','sevSq1u','AgfZ','mtm4otCXDgXkAMLX','C2XPy2u','mty4odaWAejiC3L5','u0vsvKvsx1bpuLq','mtm4AxrVywDn','qLPRuKO','mtqWmw1Kug1Hqq','mtK3otGZnvrWEwznra','DhjPBq','mJmZmZG0nJbqA2DftgS','yM9VBgvHBG','C3bSAxq','BgvUz3rO','rejFue9sva','mtfpvLLpEwi','teLdru5trq','ndm2nLvHD0vPsq','rejFse9tva','DgvZDa','zxHWB3j0CW','yKn3s3m','mtHfENPhtKG','rejFvfLqrq','nZmWnJa3qNnlDgvK'];a0_0x9af9=function(){return _0x5953f7;};return a0_0x9af9();}const DB_CONNECTION_ENV_TEMPLATE='#\x20License\x0aLICENSE=XXXX-XXXX-XXXX-XXXX\x0a\x0a#\x20Server\x0aSERVER_ADDRESS=127.0.0.1\x0aSERVER_PORT=3000\x0a\x0a#\x20Live\x20Sync\x20(WebSocket)\x20Configuration\x0a#\x20NOTE:\x20LIVE_SYNC_ENABLED=true\x20requires\x20an\x20API\x20Key\x20(KEY=...)\x20to\x20authenticate\x20WebSocket\x20clients\x0aLIVE_SYNC_ENABLED=false\x0aLIVE_SYNC_PORT=3033\x0a\x0a#\x20Redis\x20Configuration\x0aREDIS_HOST=localhost\x0aREDIS_PORT=6380\x0aREDIS_PASSWORD=\x0aREDIS_DB=0\x0a\x0a#\x20Export\x20Configuration\x0aEXPORT_FILE_EXPIRY=3600000\x0aEXPORT_CHUNK_SIZE=1000\x0a\x0a#\x20Kafka\x20Configuration\x0aKAFKA_ENABLED=false\x0a#\x20Broker\x20list\x20(comma-separated\x20for\x20multiple\x20brokers:\x20broker1:9092,broker2:9092,broker3:9092)\x0aKAFKA_BROKERS=localhost:9092\x0a#\x20Client\x20ID\x20(optional,\x20default:\x20restforge-{project}-producer\x20/\x20-consumer)\x0a#\x20KAFKA_CLIENT_ID=\x0aKAFKA_CONNECTION_TIMEOUT=3000\x0aKAFKA_REQUEST_TIMEOUT=25000\x0aKAFKA_TOPIC_PATTERN={module}.{endpoint}.events\x0aKAFKA_TENANT_ID=default\x0aKAFKA_SESSION_TIMEOUT=30000\x0aKAFKA_HEARTBEAT_INTERVAL=3000\x0aKAFKA_MAX_BYTES_PER_PARTITION=1048576\x0aKAFKA_AUTO_COMMIT=false\x0aKAFKA_AUTO_COMMIT_INTERVAL=5000\x0aKAFKA_RETRY_ATTEMPTS=3\x0aKAFKA_RETRY_DELAY=1000\x0aKAFKA_RETRY_MAX_DELAY=30000\x0aKAFKA_SSL=false\x0aKAFKA_LOG_LEVEL=info\x0a#\x20SASL\x20Authentication\x20(optional,\x20uncomment\x20if\x20the\x20broker\x20requires\x20authentication)\x0a#\x20Supported\x20mechanisms:\x20plain,\x20scram-sha-256,\x20scram-sha-512\x0a#\x20KAFKA_SASL_MECHANISM=plain\x0a#\x20KAFKA_SASL_USERNAME=\x0a#\x20KAFKA_SASL_PASSWORD=\x0a\x0a#\x20Database\x20Configuration\x0a#\x20Supported:\x20postgresql,\x20mysql,\x20oracle,\x20sqlite\x0aDB_TYPE=postgresql\x0aDB_HOST=127.0.0.1\x0aDB_PORT=5432\x0aDB_USER=postgres\x0aDB_PASSWORD=your_password_here\x0aDB_NAME=your_database_name\x0a#\x20For\x20SQLite:\x20set\x20DB_TYPE=sqlite\x20and\x20DB_NAME=./data/myapp.db\x0a#\x20DB_HOST,\x20DB_PORT,\x20DB_USER,\x20DB_PASSWORD\x20are\x20ignored\x20for\x20SQLite\x0a\x0a#\x20Logging\x20Configuration\x0aLOG_LEVEL=debug\x0aLOG_TO_FILE=true\x0a\x0a#\x20SQL\x20Logging\x0aSQL_LOG_ENABLED=false\x0aSQL_LOG_LEVEL=debug\x0aSQL_LOG_PARAMS=false\x0aSQL_LOG_SLOW_THRESHOLD=1000\x0a\x0a#\x20Cache\x20Configuration\x0aCACHE_ENABLED=false\x0aCACHE_TTL=300\x0a\x0a#\x20Job\x20Scheduler\x0aJOB_ENABLED=false\x0aJOB_CONCURRENCY=5\x0aJOB_RETENTION_HOURS=72\x0aJOB_FAILED_RETENTION_HOURS=168\x0aJOB_SHUTDOWN_TIMEOUT=10000\x0aJOB_STALLED_INTERVAL=30000\x0aJOB_MAX_STALLED_COUNT=2\x0a\x0a#\x20Distributed\x20Lock\x20Configuration\x0aLOCK_DISTRIBUTED_ENABLED=false\x0aLOCK_DISTRIBUTED_TTL=10\x0aLOCK_RESOURCE_MAX_TTL=600\x0aLOCK_DISTRIBUTED_RETRY=3\x0aLOCK_DISTRIBUTED_RETRY_DELAY=100\x0aLOCK_DISTRIBUTED_STRATEGY=reject\x0a\x0a#\x20ID\x20Generator\x20Configuration\x0aIDGEN_ENABLED=false\x0aIDGEN_IDEM_TTL=600\x0aIDGEN_COUNTER_TTL_MONTHLY=2764800\x0aIDGEN_COUNTER_TTL_DAILY=172800\x0aIDGEN_DEFAULT_MAX_RETRY=10\x0aIDGEN_DEFAULT_PIN_DIGITS=6\x0aIDGEN_DEFAULT_SERIAL_PATTERN=XXXX-XXXX-XXXX-XXXX\x0aIDGEN_DEFAULT_CODE_PATTERN=9999-9999\x0aIDGEN_ALLOW_RESET=false\x0a',REQUIRED_KEYS=new Set([a0_0x24518e(0x1a2),'SERVER_ADDRESS',a0_0x24518e(0x196),a0_0x24518e(0x18c),a0_0x24518e(0x187),a0_0x24518e(0x1a0),'DB_USER',a0_0x24518e(0x190),'DB_NAME']);function parseTemplateAsSchema(_0x7882dc){const _0x1384d4=a0_0x24518e,_0x1c0599={'BZkRJ':function(_0x5bfbe1,_0x3c25b0){return _0x5bfbe1||_0x3c25b0;},'RBhjT':function(_0x2566e8,_0x3804a6){return _0x2566e8<_0x3804a6;},'HElCU':function(_0x38313f,_0x37aae6){return _0x38313f===_0x37aae6;},'ftOUx':function(_0x591529,_0x2fede8){return _0x591529+_0x2fede8;},'GALhe':function(_0x4fd577,_0x1ba260){return _0x4fd577===_0x1ba260;},'bCwKs':'integer'},_0x562b59=_0x1c0599[_0x1384d4(0x198)](_0x7882dc,DB_CONNECTION_ENV_TEMPLATE),_0x6d6b92=_0x562b59[_0x1384d4(0x19e)]('\x0a'),_0x11bacf=[];let _0x31ded8=null,_0x2f1a52=[];for(const _0x5d0972 of _0x6d6b92){const _0x1bebe6=_0x5d0972['trim']();if(_0x1bebe6===''){_0x2f1a52=[];continue;}if(_0x1bebe6['startsWith']('#')){const _0x3a42a3=_0x1bebe6['slice'](0x1)[_0x1384d4(0x19b)](),_0x471c76=_0x3a42a3['length']>0x0&&_0x1c0599['RBhjT'](_0x3a42a3[_0x1384d4(0x19f)],0x3c)&&!_0x3a42a3['includes'](':')&&!/^[A-Z_]+=/[_0x1384d4(0x188)](_0x3a42a3)&&/^[A-Z]/['test'](_0x3a42a3);_0x471c76&&_0x1c0599[_0x1384d4(0x191)](_0x2f1a52[_0x1384d4(0x19f)],0x0)?_0x31ded8=_0x3a42a3:_0x2f1a52['push'](_0x3a42a3);continue;}const _0x1c9fe4=_0x5d0972['indexOf']('=');if(_0x1c9fe4>0x0){const _0x38bc75=_0x5d0972[_0x1384d4(0x194)](0x0,_0x1c9fe4)[_0x1384d4(0x19b)](),_0x101595=_0x5d0972['slice'](_0x1c0599['ftOUx'](_0x1c9fe4,0x1));let _0x2e989b='string';if(_0x1c0599[_0x1384d4(0x18e)](_0x101595,'true')||_0x1c0599['GALhe'](_0x101595,'false'))_0x2e989b=_0x1384d4(0x19d);else/^-?\d+$/['test'](_0x101595)&&(_0x2e989b=_0x1c0599[_0x1384d4(0x18a)]);_0x11bacf['push']({'name':_0x38bc75,'section':_0x31ded8,'type':_0x2e989b,'default':_0x101595,'description':_0x2f1a52['join']('\x20')||null,'required':REQUIRED_KEYS[_0x1384d4(0x192)](_0x38bc75)}),_0x2f1a52=[];}}return _0x11bacf;}module[a0_0x24518e(0x189)]={'DB_CONNECTION_ENV_TEMPLATE':DB_CONNECTION_ENV_TEMPLATE,'REQUIRED_KEYS':REQUIRED_KEYS,'parseTemplateAsSchema':parseTemplateAsSchema};
1
+ function a0_0x2877(){const _0x4b30b3=['DhjPBq','AgfZ','mta0uu9pB2fb','mJqWAvvyEKHV','otC4ne5ouujsBW','rejFvfLqrq','Aw50zwDLCG','rejFtKfnrq','C3rHCNrZv2L0Aa','mti2ote4AuvOzxbe','mZCYnJf2r2fVtvm','nZCXnZyWrM1zr2HL','neXyD0fYBa','ntuZnhnuuhjIra','ChvZAa','mtu5EwnbCxDT','C2XPy2u','sePbAg4','rejFvvnfuG','rejFse9tva','mJa3otGXmefxwwDStW','yM9VBgvHBG','teLdru5trq','C3bSAxq','Aw5KzxHpzG','rejFueftu1DpuKq','D0nhC04','sKHjvLq','Dhj1zq','zxHWB3j0CW','ntKYmJi5suPRrMTc','BgvUz3rO'];a0_0x2877=function(){return _0x4b30b3;};return a0_0x2877();}const a0_0x1b76c4=a0_0x4446;(function(_0x60483d,_0x59c50a){const _0x35a29a=a0_0x4446,_0x438d06=_0x60483d();while(!![]){try{const _0xe45e12=parseInt(_0x35a29a(0xc5))/0x1*(parseInt(_0x35a29a(0xc4))/0x2)+-parseInt(_0x35a29a(0xc7))/0x3*(-parseInt(_0x35a29a(0xdc))/0x4)+-parseInt(_0x35a29a(0xc3))/0x5+parseInt(_0x35a29a(0xdb))/0x6*(-parseInt(_0x35a29a(0xe2))/0x7)+-parseInt(_0x35a29a(0xda))/0x8*(-parseInt(_0x35a29a(0xe1))/0x9)+parseInt(_0x35a29a(0xcc))/0xa+-parseInt(_0x35a29a(0xd6))/0xb;if(_0xe45e12===_0x59c50a)break;else _0x438d06['push'](_0x438d06['shift']());}catch(_0x3ba4d8){_0x438d06['push'](_0x438d06['shift']());}}}(a0_0x2877,0x1b136));const DB_CONNECTION_ENV_TEMPLATE='#\x20License\x0aLICENSE=XXXX-XXXX-XXXX-XXXX\x0a\x0a#\x20Server\x0aSERVER_ADDRESS=127.0.0.1\x0aSERVER_PORT=3000\x0a\x0a#\x20Live\x20Sync\x20(WebSocket)\x20Configuration\x0a#\x20NOTE:\x20LIVE_SYNC_ENABLED=true\x20requires\x20an\x20API\x20Key\x20(KEY=...)\x20to\x20authenticate\x20WebSocket\x20clients\x0aLIVE_SYNC_ENABLED=false\x0aLIVE_SYNC_PORT=3033\x0a\x0a#\x20Redis\x20Configuration\x0aREDIS_HOST=localhost\x0aREDIS_PORT=6380\x0aREDIS_PASSWORD=\x0aREDIS_DB=0\x0a\x0a#\x20Export\x20Configuration\x0aEXPORT_FILE_EXPIRY=3600000\x0aEXPORT_CHUNK_SIZE=1000\x0a\x0a#\x20Kafka\x20Configuration\x0aKAFKA_ENABLED=false\x0a#\x20Broker\x20list\x20(comma-separated\x20for\x20multiple\x20brokers:\x20broker1:9092,broker2:9092,broker3:9092)\x0aKAFKA_BROKERS=localhost:9092\x0a#\x20Client\x20ID\x20(optional,\x20default:\x20restforge-{project}-producer\x20/\x20-consumer)\x0a#\x20KAFKA_CLIENT_ID=\x0aKAFKA_CONNECTION_TIMEOUT=3000\x0aKAFKA_REQUEST_TIMEOUT=25000\x0aKAFKA_TOPIC_PATTERN={module}.{endpoint}.events\x0aKAFKA_TENANT_ID=default\x0aKAFKA_SESSION_TIMEOUT=30000\x0aKAFKA_HEARTBEAT_INTERVAL=3000\x0aKAFKA_MAX_BYTES_PER_PARTITION=1048576\x0aKAFKA_AUTO_COMMIT=false\x0aKAFKA_AUTO_COMMIT_INTERVAL=5000\x0aKAFKA_RETRY_ATTEMPTS=3\x0aKAFKA_RETRY_DELAY=1000\x0aKAFKA_RETRY_MAX_DELAY=30000\x0aKAFKA_SSL=false\x0aKAFKA_LOG_LEVEL=info\x0a#\x20SASL\x20Authentication\x20(optional,\x20uncomment\x20if\x20the\x20broker\x20requires\x20authentication)\x0a#\x20Supported\x20mechanisms:\x20plain,\x20scram-sha-256,\x20scram-sha-512\x0a#\x20KAFKA_SASL_MECHANISM=plain\x0a#\x20KAFKA_SASL_USERNAME=\x0a#\x20KAFKA_SASL_PASSWORD=\x0a\x0a#\x20Database\x20Configuration\x0a#\x20Supported:\x20postgresql,\x20mysql,\x20oracle,\x20sqlite\x0aDB_TYPE=postgresql\x0aDB_HOST=127.0.0.1\x0aDB_PORT=5432\x0aDB_USER=postgres\x0aDB_PASSWORD=your_password_here\x0aDB_NAME=your_database_name\x0a#\x20For\x20SQLite:\x20set\x20DB_TYPE=sqlite\x20and\x20DB_NAME=./data/myapp.db\x0a#\x20DB_HOST,\x20DB_PORT,\x20DB_USER,\x20DB_PASSWORD\x20are\x20ignored\x20for\x20SQLite\x0a\x0a#\x20Logging\x20Configuration\x0aLOG_LEVEL=debug\x0aLOG_TO_FILE=true\x0a\x0a#\x20SQL\x20Logging\x0aSQL_LOG_ENABLED=false\x0aSQL_LOG_LEVEL=debug\x0aSQL_LOG_PARAMS=false\x0aSQL_LOG_SLOW_THRESHOLD=1000\x0a\x0a#\x20Cache\x20Configuration\x0aCACHE_ENABLED=false\x0aCACHE_TTL=300\x0a\x0a#\x20Job\x20Scheduler\x0aJOB_ENABLED=false\x0aJOB_CONCURRENCY=5\x0aJOB_RETENTION_HOURS=72\x0aJOB_FAILED_RETENTION_HOURS=168\x0aJOB_SHUTDOWN_TIMEOUT=10000\x0aJOB_STALLED_INTERVAL=30000\x0aJOB_MAX_STALLED_COUNT=2\x0a\x0a#\x20Distributed\x20Lock\x20Configuration\x0aLOCK_DISTRIBUTED_ENABLED=false\x0aLOCK_DISTRIBUTED_TTL=10\x0aLOCK_RESOURCE_MAX_TTL=600\x0aLOCK_DISTRIBUTED_RETRY=3\x0aLOCK_DISTRIBUTED_RETRY_DELAY=100\x0aLOCK_DISTRIBUTED_STRATEGY=reject\x0a\x0a#\x20ID\x20Generator\x20Configuration\x0aIDGEN_ENABLED=false\x0aIDGEN_IDEM_TTL=600\x0aIDGEN_COUNTER_TTL_MONTHLY=2764800\x0aIDGEN_COUNTER_TTL_DAILY=172800\x0aIDGEN_DEFAULT_MAX_RETRY=10\x0aIDGEN_DEFAULT_PIN_DIGITS=6\x0aIDGEN_DEFAULT_SERIAL_PATTERN=XXXX-XXXX-XXXX-XXXX\x0aIDGEN_DEFAULT_CODE_PATTERN=9999-9999\x0aIDGEN_ALLOW_RESET=false\x0a',REQUIRED_KEYS=new Set([a0_0x1b76c4(0xce),'SERVER_ADDRESS','SERVER_PORT',a0_0x1b76c4(0xdd),a0_0x1b76c4(0xcb),'DB_PORT',a0_0x1b76c4(0xca),a0_0x1b76c4(0xd1),a0_0x1b76c4(0xdf)]);function a0_0x4446(_0x232634,_0x41706b){_0x232634=_0x232634-0xc3;const _0x2877a7=a0_0x2877();let _0x444611=_0x2877a7[_0x232634];if(a0_0x4446['rVZLoE']===undefined){var _0x575ce7=function(_0x1d5e48){const _0x31ca05='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x2465c9='',_0x25ce98='';for(let _0x20aed2=0x0,_0x400b80,_0x3a33df,_0x3ac963=0x0;_0x3a33df=_0x1d5e48['charAt'](_0x3ac963++);~_0x3a33df&&(_0x400b80=_0x20aed2%0x4?_0x400b80*0x40+_0x3a33df:_0x3a33df,_0x20aed2++%0x4)?_0x2465c9+=String['fromCharCode'](0xff&_0x400b80>>(-0x2*_0x20aed2&0x6)):0x0){_0x3a33df=_0x31ca05['indexOf'](_0x3a33df);}for(let _0x983e1b=0x0,_0xe09f57=_0x2465c9['length'];_0x983e1b<_0xe09f57;_0x983e1b++){_0x25ce98+='%'+('00'+_0x2465c9['charCodeAt'](_0x983e1b)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x25ce98);};a0_0x4446['DLFRjw']=_0x575ce7,a0_0x4446['bQQxYk']={},a0_0x4446['rVZLoE']=!![];}const _0x1be273=_0x2877a7[0x0],_0x478130=_0x232634+_0x1be273,_0x4bd96b=a0_0x4446['bQQxYk'][_0x478130];return!_0x4bd96b?(_0x444611=a0_0x4446['DLFRjw'](_0x444611),a0_0x4446['bQQxYk'][_0x478130]=_0x444611):_0x444611=_0x4bd96b,_0x444611;}function parseTemplateAsSchema(_0x566ee5){const _0x405f53=a0_0x1b76c4,_0x1e2f92={'HJAhn':function(_0x362179,_0x1e5414){return _0x362179===_0x1e5414;},'JHIVT':'string','wCGsN':_0x405f53(0xd4),'rslgb':_0x405f53(0xcd),'vqpPX':_0x405f53(0xde)},_0x181ad8=_0x566ee5||DB_CONNECTION_ENV_TEMPLATE,_0x148d82=_0x181ad8[_0x405f53(0xcf)]('\x0a'),_0x33ec1b=[];let _0x228082=null,_0x19dab5=[];for(const _0x555e7e of _0x148d82){const _0x26898b=_0x555e7e[_0x405f53(0xd8)]();if(_0x26898b===''){_0x19dab5=[];continue;}if(_0x26898b[_0x405f53(0xe0)]('#')){const _0x179293=_0x26898b[_0x405f53(0xc8)](0x1)['trim'](),_0x54659d=_0x179293[_0x405f53(0xd7)]>0x0&&_0x179293[_0x405f53(0xd7)]<0x3c&&!_0x179293['includes'](':')&&!/^[A-Z_]+=/['test'](_0x179293)&&/^[A-Z]/['test'](_0x179293);_0x54659d&&_0x1e2f92[_0x405f53(0xc9)](_0x19dab5['length'],0x0)?_0x228082=_0x179293:_0x19dab5[_0x405f53(0xc6)](_0x179293);continue;}const _0x47f7f7=_0x555e7e[_0x405f53(0xd0)]('=');if(_0x47f7f7>0x0){const _0x302fc3=_0x555e7e['slice'](0x0,_0x47f7f7)['trim'](),_0x38e72f=_0x555e7e['slice'](_0x47f7f7+0x1);let _0x161f0d=_0x1e2f92[_0x405f53(0xd3)];if(_0x38e72f===_0x1e2f92[_0x405f53(0xd2)]||_0x38e72f==='false')_0x161f0d=_0x1e2f92['rslgb'];else/^-?\d+$/['test'](_0x38e72f)&&(_0x161f0d=_0x1e2f92['vqpPX']);_0x33ec1b['push']({'name':_0x302fc3,'section':_0x228082,'type':_0x161f0d,'default':_0x38e72f,'description':_0x19dab5['join']('\x20')||null,'required':REQUIRED_KEYS[_0x405f53(0xd9)](_0x302fc3)}),_0x19dab5=[];}}return _0x33ec1b;}module[a0_0x1b76c4(0xd5)]={'DB_CONNECTION_ENV_TEMPLATE':DB_CONNECTION_ENV_TEMPLATE,'REQUIRED_KEYS':REQUIRED_KEYS,'parseTemplateAsSchema':parseTemplateAsSchema};