@restforgejs/platform 5.1.16 → 5.1.21

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 (179) hide show
  1. package/build-info.json +2 -2
  2. package/cli/consumer-deploy.js +1 -1
  3. package/cli/consumer.js +1 -1
  4. package/generators/cli/catalog/dbschema.js +2 -1
  5. package/generators/cli/endpoint/list.js +264 -0
  6. package/generators/cli/fast-track.js +395 -37
  7. package/generators/cli/processor/create.js +7 -7
  8. package/generators/cli/processor/list.js +229 -0
  9. package/generators/lib/dbschema-kit/apply-executor.js +20 -0
  10. package/generators/lib/generators/dashboard-generator.js +5 -5
  11. package/generators/lib/generators/processor-validation-generator.js +4 -1
  12. package/generators/lib/migrate/field-type-resolver.js +23 -0
  13. package/generators/lib/payload/payload-runner.js +80 -3
  14. package/generators/lib/templates/dashboard-catalog.js +1 -1
  15. package/generators/lib/templates/db-connection-env.js +1 -1
  16. package/generators/lib/templates/dbschema-catalog.js +1 -1
  17. package/generators/lib/templates/field-validation-catalog.js +1 -1
  18. package/generators/lib/templates/mysql-template.js +1 -1
  19. package/generators/lib/templates/oracle-template.js +1 -1
  20. package/generators/lib/templates/postgres-template.js +1 -1
  21. package/generators/lib/templates/query-declarative-catalog.js +1 -1
  22. package/generators/lib/templates/sqlite-template.js +1 -1
  23. package/integrity-manifest.json +18 -18
  24. package/package.json +1 -1
  25. package/scripts/check-install.js +8 -8
  26. package/scripts/verify-integrity.js +1 -1
  27. package/server.js +1 -1
  28. package/src/components/handlers/adjust_handler.js +1 -1
  29. package/src/components/handlers/audit_handler.js +1 -1
  30. package/src/components/handlers/delete_handler.js +1 -1
  31. package/src/components/handlers/export_handler.js +1 -1
  32. package/src/components/handlers/import_handler.js +1 -1
  33. package/src/components/handlers/insert_handler.js +1 -1
  34. package/src/components/handlers/update_handler.js +1 -1
  35. package/src/components/handlers/upload_handler.js +1 -1
  36. package/src/components/handlers/workflow_handler.js +1 -1
  37. package/src/components/integrations/webhook.js +1 -1
  38. package/src/consumers/baseConsumer.js +1 -1
  39. package/src/consumers/declarativeMapper.js +1 -1
  40. package/src/consumers/handlers/apiHandler.js +1 -1
  41. package/src/consumers/handlers/consoleHandler.js +1 -1
  42. package/src/consumers/handlers/databaseHandler.js +1 -1
  43. package/src/consumers/handlers/index.js +1 -1
  44. package/src/consumers/handlers/kafkaHandler.js +1 -1
  45. package/src/consumers/index.js +1 -1
  46. package/src/consumers/messageTransformer.js +1 -1
  47. package/src/consumers/validator.js +1 -1
  48. package/src/core/db/dialect/base-dialect.js +1 -1
  49. package/src/core/db/dialect/index.js +1 -1
  50. package/src/core/db/dialect/mysql-dialect.js +1 -1
  51. package/src/core/db/dialect/oracle-dialect.js +1 -1
  52. package/src/core/db/dialect/postgres-dialect.js +1 -1
  53. package/src/core/db/dialect/sqlite-dialect.js +1 -1
  54. package/src/core/db/flatten-helper.js +1 -1
  55. package/src/core/db/query-builder-error.js +1 -1
  56. package/src/core/db/query-builder.js +1 -1
  57. package/src/core/db/relation-helper.js +1 -1
  58. package/src/core/handlers/delete_handler.js +1 -1
  59. package/src/core/handlers/insert_handler.js +1 -1
  60. package/src/core/handlers/update_handler.js +1 -1
  61. package/src/core/models/base-model.js +1 -1
  62. package/src/core/utils/cache-manager.js +1 -1
  63. package/src/core/utils/component-engine.js +1 -1
  64. package/src/core/utils/context-builder.js +1 -1
  65. package/src/core/utils/datetime-formatter.js +1 -1
  66. package/src/core/utils/datetime-parser.js +1 -1
  67. package/src/core/utils/db.js +1 -1
  68. package/src/core/utils/logger.js +1 -1
  69. package/src/core/utils/payload-loader.js +1 -1
  70. package/src/core/utils/security-checks.js +1 -1
  71. package/src/middleware/body-options.js +1 -1
  72. package/src/middleware/cors.js +1 -1
  73. package/src/middleware/idempotency.js +1 -1
  74. package/src/middleware/rate-limiter.js +1 -1
  75. package/src/middleware/request-logger.js +1 -1
  76. package/src/middleware/security-headers.js +1 -1
  77. package/src/models/base-model-mysql.js +1 -1
  78. package/src/models/base-model-oracle.js +1 -1
  79. package/src/models/base-model-sqlite.js +1 -1
  80. package/src/models/base-model.js +1 -1
  81. package/src/pro/caching/redis-client.js +1 -1
  82. package/src/pro/caching/redis-helper.js +1 -1
  83. package/src/pro/consumers/baseConsumer.js +1 -1
  84. package/src/pro/consumers/declarativeMapper.js +1 -1
  85. package/src/pro/consumers/handlers/apiHandler.js +1 -1
  86. package/src/pro/consumers/handlers/consoleHandler.js +1 -1
  87. package/src/pro/consumers/handlers/databaseHandler.js +1 -1
  88. package/src/pro/consumers/handlers/index.js +1 -1
  89. package/src/pro/consumers/handlers/kafkaHandler.js +1 -1
  90. package/src/pro/consumers/index.js +1 -1
  91. package/src/pro/consumers/messageTransformer.js +1 -1
  92. package/src/pro/consumers/validator.js +1 -1
  93. package/src/pro/database/base-model-mysql.js +1 -1
  94. package/src/pro/database/base-model-oracle.js +1 -1
  95. package/src/pro/database/base-model-sqlite.js +1 -1
  96. package/src/pro/database/db-mysql.js +1 -1
  97. package/src/pro/database/db-oracle.js +1 -1
  98. package/src/pro/database/db-sqlite.js +1 -1
  99. package/src/pro/excel/excel-generator.js +1 -1
  100. package/src/pro/excel/excel-parser.js +1 -1
  101. package/src/pro/excel/export-service.js +1 -1
  102. package/src/pro/excel/export_handler.js +1 -1
  103. package/src/pro/excel/import-service.js +1 -1
  104. package/src/pro/excel/import-validator.js +1 -1
  105. package/src/pro/excel/import_handler.js +1 -1
  106. package/src/pro/excel/upsert-builder.js +1 -1
  107. package/src/pro/idgen/idgen-routes.js +1 -1
  108. package/src/pro/integrations/lookup-resolver.js +1 -1
  109. package/src/pro/integrations/upload-handler-v2.js +1 -1
  110. package/src/pro/integrations/upload-handler.js +1 -1
  111. package/src/pro/integrations/webhook.js +1 -1
  112. package/src/pro/locking/lock-routes.js +1 -1
  113. package/src/pro/locking/resource-lock-manager.js +1 -1
  114. package/src/pro/messaging/kafkaConsumerService.js +1 -1
  115. package/src/pro/messaging/kafkaService.js +1 -1
  116. package/src/pro/messaging/messagehubService.js +1 -1
  117. package/src/pro/messaging/rabbitmqService.js +1 -1
  118. package/src/pro/scheduler/job-manager.js +1 -1
  119. package/src/pro/scheduler/job-routes.js +1 -1
  120. package/src/pro/scheduler/job-validator.js +1 -1
  121. package/src/pro/storage/base-storage-provider.js +1 -1
  122. package/src/pro/storage/file-metadata-helper.js +1 -1
  123. package/src/pro/storage/index.js +1 -1
  124. package/src/pro/storage/local-storage-provider.js +1 -1
  125. package/src/pro/storage/s3-storage-provider.js +1 -1
  126. package/src/pro/storage/upload-cleanup-job.js +1 -1
  127. package/src/pro/storage/upload-cleanup-scheduler.js +1 -1
  128. package/src/pro/storage/upload-pending-tracker.js +1 -1
  129. package/src/pro/websocket/broadcast-helper.js +1 -1
  130. package/src/pro/websocket/index.js +1 -1
  131. package/src/pro/websocket/livesync-server.js +1 -1
  132. package/src/pro/websocket/ws-broadcaster.js +1 -1
  133. package/src/services/export-service.js +1 -1
  134. package/src/services/import-service.js +1 -1
  135. package/src/services/kafkaConsumerService.js +1 -1
  136. package/src/services/kafkaService.js +1 -1
  137. package/src/services/messagehubService.js +1 -1
  138. package/src/services/rabbitmqService.js +1 -1
  139. package/src/utils/cache-invalidation-registry.js +1 -1
  140. package/src/utils/cache-manager.js +1 -1
  141. package/src/utils/component-engine.js +1 -1
  142. package/src/utils/config-extractor.js +1 -1
  143. package/src/utils/consumerLogger.js +1 -1
  144. package/src/utils/context-builder.js +1 -1
  145. package/src/utils/dashboard-helpers.js +1 -1
  146. package/src/utils/dateHelper.js +1 -1
  147. package/src/utils/datetime-formatter.js +1 -1
  148. package/src/utils/datetime-parser.js +1 -1
  149. package/src/utils/db-bootstrap.js +1 -1
  150. package/src/utils/db-mysql.js +1 -1
  151. package/src/utils/db-oracle.js +1 -1
  152. package/src/utils/db-sqlite.js +1 -1
  153. package/src/utils/db.js +1 -1
  154. package/src/utils/demo-generator.js +1 -1
  155. package/src/utils/excel-generator.js +1 -1
  156. package/src/utils/excel-parser.js +1 -1
  157. package/src/utils/file-watcher.js +1 -1
  158. package/src/utils/id-generator.js +1 -1
  159. package/src/utils/idempotency-manager.js +1 -1
  160. package/src/utils/import-validator.js +1 -1
  161. package/src/utils/license-client.js +1 -1
  162. package/src/utils/lock-manager.js +1 -1
  163. package/src/utils/logger.js +1 -1
  164. package/src/utils/lookup-resolver.js +1 -1
  165. package/src/utils/payload-loader.js +1 -1
  166. package/src/utils/processor-response.js +1 -1
  167. package/src/utils/rabbitmq.js +1 -1
  168. package/src/utils/redis-client.js +1 -1
  169. package/src/utils/redis-helper.js +1 -1
  170. package/src/utils/request-scope.js +1 -1
  171. package/src/utils/security-checks.js +1 -1
  172. package/src/utils/service-resolver.js +1 -1
  173. package/src/utils/shutdown-coordinator.js +1 -1
  174. package/src/utils/soft-delete-dashboard-guard.js +1 -1
  175. package/src/utils/sql-table-extractor.js +1 -1
  176. package/src/utils/trusted-keys.js +1 -1
  177. package/src/utils/upload-handler.js +1 -1
  178. package/src/utils/upsert-builder.js +1 -1
  179. package/src/utils/workflow-hook-executor.js +1 -1
@@ -0,0 +1,229 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Contract: processor list
5
+ *
6
+ * Menampilkan daftar processor dari workbench metadata di working directory
7
+ * (`metadata/<project>-workbench.json` map `processors`, output dari
8
+ * `processor create`). File backup (`.backup.` / `.corrupt.`) di-skip.
9
+ *
10
+ * Kolom:
11
+ * - Processor Name : key di `workbench.processors`
12
+ * - Payload : nama file `payload/<processor>.json` bila ada, `-` bila tidak
13
+ * - Processor : daftar function berformat `nama (METHOD)`; di-wrap
14
+ * maksimal 4 function per baris, tanpa pemotongan
15
+ *
16
+ * Output default berupa table human-readable (`--format=text`). Output JSON
17
+ * (untuk konsumsi AI agent / mcp-server) tersedia via `--format=json`.
18
+ */
19
+
20
+ const fs = require('fs');
21
+ const path = require('path');
22
+ const ConfigReader = require('../../lib/config/config-reader');
23
+
24
+ /**
25
+ * Baca semua entry processor dari file workbench metadata di working directory.
26
+ * Read-only: file yang corrupt di-skip dengan warning, tidak ditulis ulang.
27
+ *
28
+ * @param {string} workingDir
29
+ * @returns {{ project: string, name: string, payload: string|null, processor: { name: string, method: string }[] }[]}
30
+ */
31
+ function readProcessorEntries(workingDir) {
32
+ const metadataDir = path.join(workingDir, 'metadata');
33
+ if (!fs.existsSync(metadataDir)) {
34
+ return [];
35
+ }
36
+
37
+ const files = fs.readdirSync(metadataDir).filter(name =>
38
+ name.endsWith('-workbench.json') &&
39
+ !name.includes('.backup.') &&
40
+ !name.includes('.corrupt.')
41
+ );
42
+
43
+ const entries = [];
44
+
45
+ for (const file of files) {
46
+ let metadata;
47
+ try {
48
+ metadata = JSON.parse(fs.readFileSync(path.join(metadataDir, file), 'utf8'));
49
+ } catch (error) {
50
+ console.warn(`Warning: skipping unreadable metadata file: ${file} (${error.message})`);
51
+ continue;
52
+ }
53
+
54
+ if (!metadata || typeof metadata.processors !== 'object' || metadata.processors === null) {
55
+ continue;
56
+ }
57
+
58
+ const project = metadata.projectName || path.basename(file, '-workbench.json');
59
+
60
+ for (const [processorName, processorData] of Object.entries(metadata.processors)) {
61
+ const payloadFile = `${processorName}.json`;
62
+ const hasPayload = fs.existsSync(path.join(workingDir, 'payload', payloadFile));
63
+
64
+ const procs = processorData && Array.isArray(processorData.processor)
65
+ ? processorData.processor.map(proc => ({
66
+ name: proc.name,
67
+ method: proc.method
68
+ }))
69
+ : [];
70
+
71
+ entries.push({
72
+ project,
73
+ name: processorName,
74
+ payload: hasPayload ? payloadFile : null,
75
+ processor: procs
76
+ });
77
+ }
78
+ }
79
+
80
+ entries.sort((a, b) => a.name.localeCompare(b.name));
81
+ return entries;
82
+ }
83
+
84
+ /**
85
+ * Konversi pattern glob sederhana (`*` = karakter apa pun) menjadi RegExp
86
+ * case-insensitive yang match keseluruhan nama. Tanpa wildcard berarti
87
+ * exact match.
88
+ * @param {string} pattern
89
+ * @returns {RegExp}
90
+ */
91
+ function patternToRegExp(pattern) {
92
+ const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, '\\$&');
93
+ return new RegExp('^' + escaped.replace(/\*/g, '.*') + '$', 'i');
94
+ }
95
+
96
+ /**
97
+ * Potong string dengan suffix '..' bila melebihi lebar kolom (pola `project list`).
98
+ * @param {string} value
99
+ * @param {number} width
100
+ * @returns {string}
101
+ */
102
+ function truncate(value, width) {
103
+ return value.length > width - 2
104
+ ? value.substring(0, width - 4) + '..'
105
+ : value;
106
+ }
107
+
108
+ /**
109
+ * Render daftar processor ke format table human-readable.
110
+ * @param {{ name: string, payload: string|null, processor: { name: string, method: string }[] }[]} entries
111
+ * @returns {string}
112
+ */
113
+ function renderHumanTable(entries) {
114
+ const lines = [];
115
+
116
+ const colName = 24;
117
+ const colPayload = 28;
118
+ const colProcessor = 44;
119
+
120
+ lines.push('');
121
+ lines.push('Registered Processors:');
122
+ lines.push('');
123
+ lines.push(
124
+ 'Processor Name'.padEnd(colName) +
125
+ 'Payload'.padEnd(colPayload) +
126
+ 'Processor'.padEnd(colProcessor)
127
+ );
128
+ lines.push('-'.repeat(colName + colPayload + colProcessor));
129
+
130
+ const PROCS_PER_LINE = 4;
131
+
132
+ for (const entry of entries) {
133
+ // Wrap function per 4 item; baris pertama menyatu dengan kolom lain,
134
+ // baris lanjutan di-indent sejajar kolom Processor.
135
+ const labels = entry.processor.map(proc =>
136
+ proc.method ? `${proc.name} (${proc.method})` : proc.name
137
+ );
138
+
139
+ const procLines = [];
140
+ if (labels.length === 0) {
141
+ procLines.push('-');
142
+ } else {
143
+ for (let i = 0; i < labels.length; i += PROCS_PER_LINE) {
144
+ const chunk = labels.slice(i, i + PROCS_PER_LINE).join(', ');
145
+ const isLast = i + PROCS_PER_LINE >= labels.length;
146
+ procLines.push(isLast ? chunk : chunk + ',');
147
+ }
148
+ }
149
+
150
+ lines.push(
151
+ truncate(entry.name, colName).padEnd(colName) +
152
+ truncate(entry.payload || '-', colPayload).padEnd(colPayload) +
153
+ procLines[0]
154
+ );
155
+ for (const continuation of procLines.slice(1)) {
156
+ lines.push(' '.repeat(colName + colPayload) + continuation);
157
+ }
158
+ }
159
+
160
+ lines.push('');
161
+ lines.push(`Total: ${entries.length} processor(s)`);
162
+ lines.push('');
163
+
164
+ return lines.join('\n');
165
+ }
166
+
167
+ module.exports = {
168
+ resource: 'processor',
169
+ verb: 'list',
170
+ description: 'Menampilkan daftar processor dari workbench metadata di working directory',
171
+ category: 'management',
172
+ flags: {
173
+ format: {
174
+ type: 'string',
175
+ required: false,
176
+ default: 'text',
177
+ description: 'Format output: `text` (table human-readable) atau `json` (untuk AI agent / mcp-server)'
178
+ },
179
+ name: {
180
+ type: 'string',
181
+ required: false,
182
+ default: null,
183
+ description: 'Filter nama processor, case-insensitive. Wildcard `*` = karakter apa pun (mis. `*order*`); tanpa wildcard berarti exact match'
184
+ }
185
+ },
186
+ examples: [
187
+ 'npx restforge processor list',
188
+ 'npx restforge processor list --format=json',
189
+ 'npx restforge processor list --name=*order*'
190
+ ],
191
+ async handler(args) {
192
+ const workingDir = ConfigReader.getWorkingDirectory();
193
+ const allEntries = readProcessorEntries(workingDir);
194
+ const format = (args.format || 'text').toLowerCase();
195
+
196
+ const entries = args.name
197
+ ? allEntries.filter(e => patternToRegExp(args.name).test(e.name))
198
+ : allEntries;
199
+
200
+ if (format === 'json') {
201
+ const summary = {
202
+ totalProcessors: entries.length,
203
+ projects: [...new Set(entries.map(e => e.project))].sort()
204
+ };
205
+ if (args.name) {
206
+ summary.namePattern = args.name;
207
+ }
208
+ const output = {
209
+ schemaVersion: '1.0',
210
+ source: 'processor-list',
211
+ summary,
212
+ processors: entries
213
+ };
214
+ process.stdout.write(JSON.stringify(output, null, 2) + '\n');
215
+ return;
216
+ }
217
+
218
+ if (allEntries.length === 0) {
219
+ console.log('');
220
+ console.log('No processors found in this project.');
221
+ console.log('');
222
+ console.log('Use "npx restforge processor create" to create a new processor.');
223
+ console.log('');
224
+ return;
225
+ }
226
+
227
+ process.stdout.write(renderHumanTable(entries) + '\n');
228
+ }
229
+ };
@@ -1,5 +1,8 @@
1
1
  'use strict';
2
2
 
3
+ const fs = require('fs');
4
+ const path = require('path');
5
+
3
6
  const { loadDriver } = require('./driver-loader');
4
7
 
5
8
  const VALID_DIALECTS = ['postgres', 'mysql', 'oracle', 'sqlite'];
@@ -90,8 +93,25 @@ async function connectOracle(driver, config) {
90
93
  };
91
94
  }
92
95
 
96
+ /**
97
+ * Pastikan folder parent dari path file SQLite ada. Skip untuk database
98
+ * in-memory (:memory:) dan path tanpa folder (file di cwd). Idempoten:
99
+ * mkdir recursive tidak error bila folder sudah ada.
100
+ */
101
+ function ensureSqliteParentDir(file) {
102
+ if (!file || file === ':memory:') return;
103
+ const dir = path.dirname(file);
104
+ if (!dir || dir === '.' || dir === path.dirname(dir)) return;
105
+ fs.mkdirSync(dir, { recursive: true });
106
+ }
107
+
93
108
  async function connectSqlite(driver, config) {
94
109
  const SqliteCtor = driver;
110
+ // SQLite membuat file database otomatis, tetapi TIDAK membuat folder
111
+ // parent-nya. Bila path mengandung folder yang belum ada (mis.
112
+ // ./data/app.db dengan ./data belum dibuat), buka koneksi gagal dengan
113
+ // SQLITE_CANTOPEN. Buat folder parent lebih dulu (skip untuk :memory:).
114
+ ensureSqliteParentDir(config.file);
95
115
  const db = new SqliteCtor(config.file);
96
116
  return {
97
117
  exec: async (sql) => db.exec(sql),
@@ -60,7 +60,7 @@ class DashboardGenerator {
60
60
 
61
61
  /**
62
62
  * Tulis file module dashboard ke src/modules/{project}/dash-{name}.js.
63
- * SQL di-embed langsung sebagai template literal — tidak ada SQL file copy.
63
+ * SQL di-embed langsung sebagai template literal tidak ada SQL file copy.
64
64
  *
65
65
  * @param {string} projectName
66
66
  * @param {string} dashboardName
@@ -141,8 +141,8 @@ class DashboardGenerator {
141
141
  /**
142
142
  * Baca file SQL dari disk (saat generation time), trim trailing whitespace,
143
143
  * lalu escape karakter yang berbahaya di template literal JS:
144
- * - backtick (`) → \`
145
- * - dollar-brace (${) → \${
144
+ * - backtick (`) \`
145
+ * - dollar-brace (${) \${
146
146
  *
147
147
  * Comment SQL dibiarkan apa adanya (defensive: bisa berisi info dialect /
148
148
  * output columns yang berguna saat audit).
@@ -380,8 +380,8 @@ module.exports = router;
380
380
  /**
381
381
  * Render block cache set yang disisip setelah loop normalisasi (lowercaseKeysDeep
382
382
  * + stringifyNumericDeep) dan sebelum `res.json(response)`. TTL literal:
383
- * - bila cacheConfig.ttl numeric → emit angka literal
384
- * - bila cacheConfig.ttl null → emit `null` agar `cacheManager.set`
383
+ * - bila cacheConfig.ttl numeric emit angka literal
384
+ * - bila cacheConfig.ttl null emit `null` agar `cacheManager.set`
385
385
  * fallback ke env CACHE_TTL.
386
386
  */
387
387
  static _renderCacheSetBlock(cacheConfig) {
@@ -21,7 +21,7 @@ const SOURCES = ['body', 'params', 'headers'];
21
21
 
22
22
  // Tipe yang didukung oleh validator (harus sinkron dengan validatePayloadV2).
23
23
  const VALID_TYPES = new Set([
24
- 'string', 'number', 'integer', 'boolean',
24
+ 'string', 'text', 'number', 'integer', 'boolean',
25
25
  'uuid', 'date', 'datetime', 'array', 'object'
26
26
  ]);
27
27
 
@@ -126,6 +126,9 @@ function buildFollowUpChecks(inputField, messagePrefix, fieldDef) {
126
126
  if (type && VALID_TYPES.has(type)) {
127
127
  switch (type) {
128
128
  case 'string':
129
+ case 'text':
130
+ // text divalidasi identik dengan string (unbounded string); pemetaan UI
131
+ // (→ textarea) bukan urusan lapisan validasi ini.
129
132
  checks.push({
130
133
  condition: `typeof input['${safeField}'] !== 'string'`,
131
134
  message: `${messagePrefix} must be a string`
@@ -311,6 +311,29 @@ class FieldTypeResolver {
311
311
  };
312
312
  }
313
313
 
314
+ // Rule 8.5: Textarea type-driven (tipe `text` first-class dari RDF).
315
+ // Tipe `text` bersifat UNBOUNDED (phase-01 sengaja tidak menurunkan maxLength),
316
+ // jadi `maxlength` HANYA disertakan bila constraints.maxLength ada nilainya.
317
+ // Tidak memaksa default cap agar konsisten dengan semantik unbounded.
318
+ if (valType === 'text') {
319
+ const extra = { rows: 3 };
320
+ if (typeof constraints.maxLength === 'number') {
321
+ extra.maxlength = constraints.maxLength;
322
+ }
323
+ return {
324
+ name: fieldName,
325
+ label,
326
+ fieldType: 'textarea',
327
+ skip: false,
328
+ required,
329
+ inTable,
330
+ tableOrder,
331
+ tableField: null,
332
+ defaultValue: extractDefault(constraints),
333
+ extra
334
+ };
335
+ }
336
+
314
337
  // Rule 9: Default → text
315
338
  const maxlen = (typeof constraints.maxLength === 'number')
316
339
  ? constraints.maxLength
@@ -774,6 +774,61 @@ async function deriveSoftDeleteFkChecks(db, tableName) {
774
774
  return checks;
775
775
  }
776
776
 
777
+ /**
778
+ * Turunkan constraint `enum` RDF dari CHECK enum SDF (`checks` dengan op 'in'),
779
+ * secara in-place pada `payload.fieldValidation` (pola `applySoftDeleteDerivation`).
780
+ *
781
+ * Untuk tiap IR check `{ field, op: 'in', value: [...] }` pada model tabel, set
782
+ * `constraints.enum = value` pada entry fieldValidation string yang namanya cocok.
783
+ * Sumber = SDF IR, BUKAN introspeksi clause DB: deterministik dan agnostik dialect
784
+ * (PostgreSQL menulis ulang `IN (...)` menjadi `= ANY (ARRAY[...])`, sehingga clause
785
+ * DB tidak andal). Hilir: `payload migrate` (field-type-resolver Rule 5) merender
786
+ * field string ber-`enum` menjadi UDF `select` dengan dataSource static (combo box),
787
+ * paralel dengan boolean -> checkbox.
788
+ *
789
+ * Guard ketat agar baseline tabel tanpa enum tetap byte-identik:
790
+ * - Map SDF null (schema-path absen / gagal load) -> no-op
791
+ * - tabel tidak ada di SDF / bare-name ambigu -> no-op
792
+ * - check bukan `op:'in'` valid atau value kosong -> di-skip
793
+ * - field tidak punya entry fieldValidation string -> di-skip (lihat catatan)
794
+ * - `constraints.enum` sudah ada -> dipertahankan (tidak override)
795
+ *
796
+ * Catatan: field string nullable tanpa constraint lain di-skip dari fieldValidation
797
+ * oleh `generateFieldValidation` (anti-bloat), sehingga enum-nya tidak terpasang.
798
+ * Field enum pada praktiknya notnull (mis. `jabatan`), sehingga entry-nya selalu ada.
799
+ *
800
+ * @param {Object} payload - payloadData yang dimutasi
801
+ * @param {Map<string, Object>|null} models - Map model SDF (hasil loadSchemaMapGraceful)
802
+ * @param {string} tableName - nama tabel target
803
+ * @returns {void}
804
+ */
805
+ function applyCheckEnumDerivation(payload, models, tableName) {
806
+ if (!models) return;
807
+ if (!Array.isArray(payload.fieldValidation)) return;
808
+
809
+ let ir;
810
+ try {
811
+ ir = findModelByTable(models, tableName);
812
+ } catch (err) {
813
+ return;
814
+ }
815
+ if (!ir || !Array.isArray(ir.checks)) return;
816
+
817
+ for (const check of ir.checks) {
818
+ if (!check || check._invalid || check.op !== 'in') continue;
819
+ if (typeof check.field !== 'string') continue;
820
+ if (!Array.isArray(check.value) || check.value.length === 0) continue;
821
+
822
+ const fv = payload.fieldValidation.find(
823
+ (f) => f && f.name === check.field && f.type === 'string'
824
+ );
825
+ if (!fv || !fv.constraints || typeof fv.constraints !== 'object') continue;
826
+ if (Array.isArray(fv.constraints.enum)) continue;
827
+
828
+ fv.constraints.enum = check.value.slice();
829
+ }
830
+ }
831
+
777
832
  // ============================================================================
778
833
  // FK-SD-4 REGISTRY DERIVATION (SDF scan -> RDF) — Fase 1.5 FK-aware soft-delete
779
834
  // ============================================================================
@@ -1504,9 +1559,18 @@ class PayloadGenerator {
1504
1559
 
1505
1560
  if (isPrimaryKey) continue;
1506
1561
 
1562
+ // Diskriminator TEXT vs VARCHAR/CHAR (deterministik lintas dialect):
1563
+ // PostgreSQL: TEXT -> data_type/udt_name = 'text'
1564
+ // MySQL: TEXT -> data_type/udt_name = 'text'
1565
+ // SQLite: TEXT -> data_type/udt_name = 'text'
1566
+ // Oracle: CLOB/NCLOB -> dinormalisasi ke 'text' oleh getDetailedColumnInfo()
1567
+ // VARCHAR/CHAR ('character varying'/'varchar'/'character'/'char'/'bpchar')
1568
+ // TIDAK match diskriminator ini, sehingga tetap type:'string'.
1569
+ const isTextType = dataType === 'text' || udtName === 'text';
1570
+
1507
1571
  const entry = {
1508
1572
  name: fieldName,
1509
- type: 'string',
1573
+ type: isTextType ? 'text' : 'string',
1510
1574
  constraints: {}
1511
1575
  };
1512
1576
 
@@ -1514,7 +1578,9 @@ class PayloadGenerator {
1514
1578
  entry.constraints.required = true;
1515
1579
  }
1516
1580
 
1517
- if (col.character_maximum_length) {
1581
+ // text bersifat unbounded: tidak menurunkan maxLength. VARCHAR/CHAR tetap
1582
+ // derive maxLength dari character_maximum_length seperti sebelumnya.
1583
+ if (!isTextType && col.character_maximum_length) {
1518
1584
  entry.constraints.maxLength = col.character_maximum_length;
1519
1585
  }
1520
1586
 
@@ -1522,7 +1588,10 @@ class PayloadGenerator {
1522
1588
  entry.constraints.unique = true;
1523
1589
  }
1524
1590
 
1525
- if (Object.keys(entry.constraints).length === 0) {
1591
+ // Skip entry tanpa constraint agar payload tidak bloat — KECUALI type:'text'.
1592
+ // Untuk text, tipe itu sendiri adalah sinyal "unbounded/long text" first-class
1593
+ // yang harus bertahan di payload meski tidak ada constraint lain.
1594
+ if (!isTextType && Object.keys(entry.constraints).length === 0) {
1526
1595
  continue;
1527
1596
  }
1528
1597
 
@@ -1845,6 +1914,13 @@ class PayloadGenerator {
1845
1914
  // graceful). Tidak menambah query DB saat runtime (derivasi hanya saat generate).
1846
1915
  const fkRegistryModels = loadSchemaMapGraceful(args['schema-path']);
1847
1916
 
1917
+ // Enum derivation (CHECK `in` SDF -> constraints.enum RDF): bila tabel punya
1918
+ // CHECK enum di SDF, turunkan daftar nilai ke fieldValidation string sehingga
1919
+ // hilir `payload migrate` merendernya sebagai UDF select static (combo box).
1920
+ // Guarded + graceful (Map null / tabel tak ada / field non-string -> no-op),
1921
+ // sehingga baseline tabel tanpa enum tetap byte-identik.
1922
+ applyCheckEnumDerivation(payloadData, fkRegistryModels, payloadData.tableName);
1923
+
1848
1924
  if (hasSoftDeleteColumns) {
1849
1925
  const sdfSoftDelete = resolveSoftDeleteForTable(payloadData.tableName, args['schema-path']);
1850
1926
  applySoftDeleteDerivation(payloadData, sdfSoftDelete);
@@ -2756,6 +2832,7 @@ module.exports = {
2756
2832
  isDefaultSearchableColumn,
2757
2833
  applyIsActiveDefaultScope,
2758
2834
  applySoftDeleteDerivation,
2835
+ applyCheckEnumDerivation,
2759
2836
  resolveSoftDeleteForTable,
2760
2837
  deriveSoftDeleteFkChecks,
2761
2838
  isParentSoftDelete,
@@ -1 +1 @@
1
- function a0_0x81fe(){const _0x4db9ed=['zgvMyxvSDa','CMvXDwLYzwq','ndmWzxnQsuTx','kd88itOPoIHBys16qs1Ax11Bys16qs1Amc05x10Qkq','l2fWAs97ChjVAMvJDh0VE25HBwv9l2rHC2HIB2fYza','mJm2nJm3wKzQsu5N','w3SGiMXHyMvSiJOGiLnOB2vZiIWGiNzHBhvLiJOGiJC2nJaIih0SihSGiMXHyMvSiJOGiKDHBwLUzYiSicj2ywX1zsi6iciYodiWiIb9lcb7icjSywjLBci6icjpDgHLCNmIlcaIDMfSDwuIoIaInduYntCIih1D','vxbKyxrPBMCGyw4Gu1fmigzPBguGCMvXDwLYzxmGCMvNzw5LCMf0Aw5NihrOzsbKyxnOyM9HCMqGBw9KDwXLicGNy29KzwDLBL9JCMvHDgvFzgfZAgjVyxjKjYKGzM9YignOyw5NzxmGDg8GDgfRzsbLzMzLy3qU','nZG4mJGWmeXLzKzRCq','vgfIBguGzgv0zwn0zwqGAw4Gu1fmlcbIDxqGBM90ihjLz2LZDgvYzwqGyxmGq1jvrcbLBMrWB2LUDcbPBIbTzxrHzgf0ysbWCM9Qzwn0icHSAwTLBhKGysb2Awv3lcbdveuGywXPyxmSig9YignYB3nZlxbYB2PLy3qGDgfIBguG4OcuignHC2nHzguGD2LSBcbUB3qGzMLYzsK','tgLZDcbVzIb3AwrNzxqGzgvMAw5PDgLVBNmUie9YzgvYigLZigLUzM9YBwf0Aw9UywWGB25SEsaOCMvZCg9UC2uGA2v5CYbHCMuGyNKGD2LKz2v0igLKlcbUB3qGyxjYyxKGAw5KzxGPlG','uMvZzxj2zwqGzM9YiensvuqGCgf5Bg9HzhmUieeGzgfZAgjVyxjKihbHEwXVywqGBxvZDcbKzwnSyxjLicD3AwrNzxrZjYbPBNn0zwfKlG','tgLZDcbVzIbduLveihrHyMXLig5HBwvZihrOyxqSihDOzw4GD3jPDhrLBIWGD2LSBcb0CMLNz2vYigLUDMfSAwrHDgLVBIbVzIb0AgLZigrHC2HIB2fYzcbJywnOzs4','msbYB3CGW5CGmIbJB2X1Bw5Z','Dg9WlwXLDMvSicDJywnOzsCGB2jQzwn0','zgf0zq','Cg9PBNrZ','msbYB3CGW5CGmsbJB2WSig91Dhb1DcbJB2X1Bw4Gj3zHBhvLjW','C3rYAw5N','zgfZAc1ZywXLCW','D2LKz2v0CW','nuvJuK5osa','iJi0mJaI','v2LKz2v0igLKzw50AwzPzxi7ihvZzwqGyxmGDgHLihjLC3bVBNnLigTLEsbPBIb0AguGzgfZAgjVyxjKigvUDMvSB3bLlG','DMfSDwu','DMfSDwuGkg9Yign1CNjLBNqP','qsbWyxLSB2fKihDPDgGGyM90AcaND2LKz2v0CYCGyw5KicD0ywjSzu5HBwuNigLZihjLAMvJDgvKigj5ierHC2HIB2fYzfzHBgLKyxrVCI4GugLJAYbVBMuGC2HHCguU','twv0CMLJicSGrg9UDxqGqNjLywTKB3DU','Dg9Wtgv2zwXbBgXVD2vK','iNrYzw5KiJOGEYaIzgLYzwn0Aw9UiJOGiNvWiIWGiNbJDci6iciYlJiIih0','u3bHCMTSAw5LigXPyNjHCMLLCYaOqxbLEenOyxj0CYWGq2HHCNrPC3qSigv0yY4Pihr5CgLJywXSEsbUzwvKigeGCgXHAw4GBNvTyMvYigfYCMf5lIbgCM9UDgvUzcbTyxbZihbVAw50CY5TyxaOCca9pIbWlNzHBhvLks4GvgHLicDWzxjPB2qNigzPzwXKihn0yxLZigzVCIb0B29SDgLWigfUzcbNyxaTCMvZAwXPzw5JzsbHz2fPBNn0ig1PC3nPBMCGzgf5CY4GvxnLigDLBMvYyxrLx3nLCMLLCYbPBIbtuuWGDg8Gzw5ZDxjLignVBNnPC3rLBNqGCM93ignVDw50igv2zw4GzM9YigrHExmGD2L0AcbUBYb0CMfUC2fJDgLVBNmU','twv0CMLJicSGuhjVz3jLC3mGDg8Gr29HBa','tvvtvcbZDgfYDcb3AxrOicDKyxnOlsCGChjLzML4','zxHWzwn0zwrFzwfYBMLUz3m','DhLWzq','zgfZAgjVyxjKlwnHDgfSB2C','ue9tva','EYaIzgLYzwn0Aw9UiJOGiNvWiIWGiNbJDci6iciYlJyIih0','DgfYz2v0','v2HLBIbJywnOzs5LBMfIBgvKid09psb0CNvLigfUzcbPBNzHBgLKyxrLCYbPCYbUB24Tzw1WDhK6ihzHBgLKyxrVCIbLEhrYywn0CYb0ywjSzsbJyw5KAwrHDgvZigzYB20GD2LKz2v0ifnrtcaOCMvNzxGGrLjpts9kt0LoksWGy3jVC3mTCMvMzxjLBMnLCYb3AxrOig1LDgfKyxrHl3TWCM9Qzwn0Fs5QC29UicHLBMrWB2LUDhnBkL0UDgfIBgvoyw1LihDOzxjLihr5CguGpt09icjTB2r1BguIksWGyw5KigfZC2vYDhmGzxf1ywXPDhKGB2yGzxHWzwn0zwqGDNmGzgvJBgfYzwqGC2v0CY4GtwLZBwf0y2HLCYbHCMuGCMvWB3j0zwqGCgvYignHDgvNB3j5icHTAxnZAw5NlcbLEhrYysWGDw5TyxrJAgvKks4','zgfZAc1PBMjVDw5K','DhjLBMq','mti3ndiXmKr3vvv6sa','qsb3AwrNzxqGtvvtvcbKzwnSyxjLigv4ywn0BhKGB25Lig9MoIaNCxvLCNKNie9sicDXDwvYAwvZjY4GqM90AcbVCIbUzwL0AgvYigLZihjLAMvJDgvKlG','y2fJAgu','DgfIBgvoyw1L','zgfZAgjVyxjKihbHEwXVywq','vgHLihbYzwzPEcbIzwnVBwvZihbHCNqGB2yGDgHLifvstcbZzwDTzw50lIbuAguGCMvZzxj2zwqGC2nOzw1LigTLzxbZigrHC2HIB2fYzcbLBMrWB2LUDhmGDMLZDwfSBhKGzgLZDgLUy3qGzNjVBsbduLveigvUzhbVAw50CYbPBIb0AguGvvjmihnWywnLigfUzcbHBgXVD3mGzNv0DxjLihjVDxrPBMCGzgLMzMvYzw50Awf0Aw9UlG','vgfIBguGzgvJBgfYzwqGAw4GAw52ywXPzgf0zxmSigj1DcbUB3qGzgv0zwn0zwqGAw4Gyw55ihDPzgDLDcbtuuWGkhr5Cg8GB3iGzgvHzcbLBNrYEsK','nZbLq1vdsuq','zMLSztPXDwvYEs88Cgf0Ad4VDgfYz2v0lNnXBa','xMrHC2GTw2eTEKeTwJaTov8TxsSK','BNvTyMvY','oNbHCMfTtMfTzq','vMLZDwfSignVBg9YigLZigeGzNjVBNrLBMqGCMvUzgvYAw5NignVBMnLCM4U','EYaIzgLYzwn0Aw9UiJOGiNvWiIWGiNbJDci6iciYlJiIih0','D2LKz2v0lNf1zxjPzxmUpgTLEt4GD2L0AcbtuuWGCMv0DxjUAw5Nie4GCM93CW','CgvYAw9K','phDPzgDLDf9Pzd4','zxHWB3j0CW','AxrLBxm','ugfYyw0GBMfTzsbTDxn0ig1HDgnOihrOzsbWBgfJzwHVBgrLCIbYzwDLEcbGw2eTEKeTwL9Dw2eTEKeTwJaTov9DkMaGkgfSCgHHBNvTzxjPyYaRihvUzgvYC2nVCMuSig11C3qGC3rHCNqGD2L0AcbSzxr0zxiGB3iGDw5KzxjZy29YzsKU','oe9Iuu1KrG','t3b0Aw9UywWGy2fJAguGy29UzMLNDxjHDgLVBI4Gu2vLignHy2HLu3bLyYbMB3iGzgv0ywLSCY4','DgL0Bgu','msbYB3CGW5CGmsbJB2X1Bw4','iNzHBhvLiJOGiJy5nZaWiG','Cgn0','zMLSztPXDwvYEs88Cgf0Ad4VyNjLywTKB3DUlNnXBa','v2HLBIb0CNvLlcb0AguGCMvXDwvZDcbIB2r5ie1vu1qGAw5JBhvKzsb0AgLZihbHCMfTicHVDgHLCNDPC2uGndaWks4','sgvHzgXPBMuGBwv0CMLJihDPDgGGDhjLBMqGy2HPCcbHBMqGC3bHCMTSAw5Lig1PBMKTy2HHCNqGzM9YihnOB3j0ihDPBMrVD3mGkdCGzgf5CYWGmtiGBw9UDgHZlcbLDgmUks4Gu3vPDgfIBguGzM9YihDPzgDLDhmGBgLRzsaNqxzLCMfNzsbeywLSEsbtywXLCYCU','C3vIDgL0Bgu','C2nHBgfYihbYAw1PDgL2zq','mJzpyvzwBKK','yw55icHTDxn0igjLignVBxbHDgLIBguGD2L0AcbKzwnSyxjLzcaNDhLWzsCP','zNjLztSGlNnXBcbYzwnVBw1LBMrLzcbMB3iGzwrPDg9YigHPz2HSAwDODa','nZa1nJvvtg9Sz2q','B3jKzxjZx3rOAxnFBw9UDgG','EYaICgfYyw1ZiJOGEYaIEwvHCIi6ihSGiNr5CguIoIaIBNvTyMvYiIWGiNjLCxvPCMvKiJOGDhj1zsb9ih0GFq','ndm5mdHuseTuDfy','CxvLCNK','ugfYyw0Gzgf0ysb0ExbLlIbwywXPzgf0zxmGCMvXDwvZDcbIB2r5igfUzcbZAgfWzxmGCNvUDgLTzsbWyxjHBwv0zxiGyMLUzgLUzY4','BgvUz3rO','rgfZAgjVyxjKigvUzhbVAw50ig1HEsbVChqTAw4GDg8GuMvKAxmTyMfZzwqGy2fJAguUifbHDhrLCM4GzM9SBg93CYbWCM9JzxnZB3iGy2fJAguGkhnLzsbMzwf0lwnHy2HLlM1Kks4Gq2fJAguGC2nVCguGAxmGDgHLigz1BgWGCMvZCg9UC2uGzw52zwXVCgu7ig9UzsbJywnOzsbLBNrYEsbWzxiGkhbHCMfTCYaRihDPzgDLDhnBxsbZDwjZzxqPignVBwjPBMf0Aw9UlG','tIbYB3DZimoxie0Gy29SCW','rxzLCNKGCgXHy2vOB2XKzxiGDxnLzcbPBIbtuuWGtvvtvcbIzsbKzwnSyxjLzcbPBIaNCgfYyw1ZjY4GvMfSAwrHDg9YihrOCM93CYbfCNjVCIb3AxrOig1LC3nHz2uGzM9YBwf0oIaIv2LKz2v0icC8Awq+jYbXDwvYEsaNpgXHyMvSpICGDxnLCYb1BMrLy2XHCMvKihbSywnLAg9SzgvYicC6phrVA2vUpICGkgrLy2XHCMuGAw4Gj3bHCMfTCYCPiI4','zgLYzwn0Aw9U','q29SBgfWC2uGDg8GC2nHBgfYihbYAw1PDgL2zsaODgHLihzHBhvLig9MihrOzsbZAw5NBguGy29SDw1Uks4','ntGZmZa4CNvhAxL3','rNjVBNrLBMqGzgv0zxjTAw5LCYbKB251Dc9WAwuGDMfYAwfUDcWGy29SB3iGCgvYignHDgvNB3j5lcbHBMqGBgfIzwWGB3jKzxiUieLMihbLCI1JyxrLz29YEsbWzxjJzw50ywDLigLZig5LzwrLzcbMB3iGDgHLigrVBNv0igfYyYWGzNjVBNrLBMqGy29TChv0zxmGAxqGzNjVBsbPDgvTC1TPxs52ywX1zsaVihn1BsHPDgvTC1SQxs52ywX1zsKUie5Vig5LzwqGDg8GC2vUzcaNCgn0jYbMCM9TigjHy2TLBMqGDw5SzxnZihrOzsbMAwD1CMuGAxmGysbZDgfIBguGyNvZAw5LC3mGy2fSy3vSyxrPB24GAw5KzxbLBMrLBNqGB2yGDMLZDwfSihjLBMrLCMLUzY4','zNjVBNrLBMqTy29Uy2vYBG','zw5HyMXLza','B2jQzwn0','D2LKz2v0lNf1zxjPzxmUpgTLEt4GD2L0AcbtuuWGCMv0DxjUAw5NideGCM93imoxig11BhrPCgXLignVBhvTBNm','yxjYyxK','mZm3mdLpu0X3q2q'];a0_0x81fe=function(){return _0x4db9ed;};return a0_0x81fe();}const a0_0x5d8009=a0_0x48a4;(function(_0x27e103,_0x388d31){const _0xbd7787=a0_0x48a4,_0x2cefd8=_0x27e103();while(!![]){try{const _0x551d61=-parseInt(_0xbd7787(0x195))/0x1*(-parseInt(_0xbd7787(0x17f))/0x2)+-parseInt(_0xbd7787(0x18e))/0x3+parseInt(_0xbd7787(0x160))/0x4*(parseInt(_0xbd7787(0x14b))/0x5)+parseInt(_0xbd7787(0x185))/0x6*(parseInt(_0xbd7787(0x167))/0x7)+parseInt(_0xbd7787(0x174))/0x8*(parseInt(_0xbd7787(0x19b))/0x9)+-parseInt(_0xbd7787(0x198))/0xa*(-parseInt(_0xbd7787(0x182))/0xb)+-parseInt(_0xbd7787(0x13e))/0xc;if(_0x551d61===_0x388d31)break;else _0x2cefd8['push'](_0x2cefd8['shift']());}catch(_0x572860){_0x2cefd8['push'](_0x2cefd8['shift']());}}}(a0_0x81fe,0x448b0));function a0_0x48a4(_0xb4416f,_0x562a37){_0xb4416f=_0xb4416f-0x13e;const _0x81febf=a0_0x81fe();let _0x48a46a=_0x81febf[_0xb4416f];if(a0_0x48a4['HFnGMm']===undefined){var _0xfe9f1c=function(_0x5021a5){const _0x2f4c26='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x430828='',_0x132928='';for(let _0x54e612=0x0,_0x1f2daa,_0x4abcdc,_0x35e705=0x0;_0x4abcdc=_0x5021a5['charAt'](_0x35e705++);~_0x4abcdc&&(_0x1f2daa=_0x54e612%0x4?_0x1f2daa*0x40+_0x4abcdc:_0x4abcdc,_0x54e612++%0x4)?_0x430828+=String['fromCharCode'](0xff&_0x1f2daa>>(-0x2*_0x54e612&0x6)):0x0){_0x4abcdc=_0x2f4c26['indexOf'](_0x4abcdc);}for(let _0x31ebcd=0x0,_0x471c7e=_0x430828['length'];_0x31ebcd<_0x471c7e;_0x31ebcd++){_0x132928+='%'+('00'+_0x430828['charCodeAt'](_0x31ebcd)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x132928);};a0_0x48a4['WnbCFp']=_0xfe9f1c,a0_0x48a4['UcsvjU']={},a0_0x48a4['HFnGMm']=!![];}const _0x12c601=_0x81febf[0x0],_0x4b769d=_0xb4416f+_0x12c601,_0x51e24a=a0_0x48a4['UcsvjU'][_0x4b769d];return!_0x51e24a?(_0x48a46a=a0_0x48a4['WnbCFp'](_0x48a46a),a0_0x48a4['UcsvjU'][_0x4b769d]=_0x48a46a):_0x48a46a=_0x51e24a,_0x48a46a;}const FORBIDDEN_FRONTEND_FIELDS=['widgetType','layout',a0_0x5d8009(0x176),a0_0x5d8009(0x17d),'color'],ALLOWED_PARAM_TYPES=[a0_0x5d8009(0x148),'number','boolean',a0_0x5d8009(0x145)],FRONTEND_CONCERN_REASONS={'widgetType':'Visual\x20variant\x20(donut,\x20bar,\x20pie,\x20area)\x20is\x20a\x20frontend\x20rendering\x20concern\x20(separation\x20of\x20concerns).','layout':'Layout\x20is\x20a\x20frontend\x20rendering\x20concern.','title':'UI\x20label\x20is\x20a\x20frontend\x20rendering\x20concern.','subtitle':'UI\x20label\x20is\x20a\x20frontend\x20rendering\x20concern.','color':a0_0x5d8009(0x16c)},PAYLOAD_SHAPE={'discriminator':{'field':'widgets','presentMeans':a0_0x5d8009(0x164),'absentMeans':'Not\x20a\x20dashboard\x20payload\x20(likely\x20CRUD\x20with\x20tableName,\x20or\x20invalid)','conflictsWith':a0_0x5d8009(0x163),'conflictRationale':a0_0x5d8009(0x150)},'topLevelAllowed':[{'name':a0_0x5d8009(0x14a),'type':a0_0x5d8009(0x194),'required':!![],'minItems':0x1,'description':a0_0x5d8009(0x140)},{'name':'params','type':a0_0x5d8009(0x192),'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_0x5d8009(0x162),'type':'object','required':![],'description':a0_0x5d8009(0x175)}],'topLevelForbidden':[{'name':a0_0x5d8009(0x163),'category':'shape-conflict','reason':a0_0x5d8009(0x141)},...FORBIDDEN_FRONTEND_FIELDS['map'](_0x430828=>({'name':_0x430828,'category':a0_0x5d8009(0x190),'reason':FRONTEND_CONCERN_REASONS[_0x430828]}))]},WIDGET_SPEC={'requiredFields':[{'name':'id','type':'string','constraint':'non-empty,\x20unique\x20across\x20widgets\x20in\x20the\x20same\x20payload','description':a0_0x5d8009(0x14d)}],'exclusiveQueryFields':{'rule':a0_0x5d8009(0x161),'options':[{'name':a0_0x5d8009(0x186),'type':a0_0x5d8009(0x148),'format':'file:relative/path/to/query.sql','description':'Single\x20SQL\x20query\x20for\x20the\x20widget.','responseShape':'Always\x20{\x20items:\x20[...]\x20}\x20regardless\x20of\x20SQL\x20result\x20shape.'},{'name':'queries','type':a0_0x5d8009(0x192),'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':'top-level\x20\x27params\x27\x20object','keyConvention':a0_0x5d8009(0x173),'perEntryFields':[{'name':a0_0x5d8009(0x158),'required':!![],'allowedValues':ALLOWED_PARAM_TYPES,'description':a0_0x5d8009(0x187)},{'name':a0_0x5d8009(0x197),'required':![],'type':'boolean','default':![],'description':a0_0x5d8009(0x17b)},{'name':a0_0x5d8009(0x196),'required':![],'type':a0_0x5d8009(0x180),'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':'Always\x20wrap\x20as\x20{\x20items:\x20[...]\x20}\x20regardless\x20of\x20SQL\x20result\x20shape.','exampleSqlShape':'any\x20(1\x20row\x20×\x201\x20col,\x20N\x20rows\x20×\x20M\x20cols,\x20etc.)','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':'widget.queries.<key>\x20with\x20SQL\x20returning\x201\x20row\x20×\x201\x20column','rule':a0_0x5d8009(0x18d),'exampleSqlShape':a0_0x5d8009(0x147),'exampleResponse':a0_0x5d8009(0x178)},{'appliesTo':a0_0x5d8009(0x193),'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_0x5d8009(0x153)},{'appliesTo':a0_0x5d8009(0x16e),'rule':'Return\x20as\x20array\x20of\x20objects\x20(no\x20collapse).','exampleSqlShape':a0_0x5d8009(0x18a),'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_0x5d8009(0x151),'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_0x5d8009(0x170),'queries':{'value':'file:query/<path>/value.sql','trend':'file:query/<path>/trend.sql','items':a0_0x5d8009(0x17a)}},'sqlShapesPerKey':[{'key':a0_0x5d8009(0x14e),'shape':'1\x20row\x20×\x201\x20column','outputColumns':['value'],'collapseRule':a0_0x5d8009(0x17e)},{'key':'trend','shape':a0_0x5d8009(0x143),'outputColumns':[a0_0x5d8009(0x18c),a0_0x5d8009(0x179)],'collapseRule':a0_0x5d8009(0x192)},{'key':a0_0x5d8009(0x172),'shape':'N\x20rows\x20×\x202\x20columns','outputColumns':['label','value'],'collapseRule':'array\x20of\x20objects'}],'responseShape':{'value':'\x2269700\x22','trend':a0_0x5d8009(0x16d),'items':a0_0x5d8009(0x19c)},'referenceWidgetId':a0_0x5d8009(0x157),'socNotes':a0_0x5d8009(0x18f)},{'id':'metric_sparkline','name':'Metric\x20+\x20Sparkline','useCase':a0_0x5d8009(0x17c),'payloadShape':{'id':'<widget_id>','queries':{'value':'file:query/<path>/value.sql','trend':'file:query/<path>/trend.sql','points':'file:query/<path>/points.sql'}},'sqlShapesPerKey':[{'key':a0_0x5d8009(0x14e),'shape':a0_0x5d8009(0x177),'outputColumns':[a0_0x5d8009(0x14e)],'collapseRule':'scalar\x20primitive'},{'key':a0_0x5d8009(0x15f),'shape':a0_0x5d8009(0x143),'outputColumns':[a0_0x5d8009(0x18c),a0_0x5d8009(0x179)],'collapseRule':'object'},{'key':a0_0x5d8009(0x146),'shape':'N\x20rows\x20×\x202\x20columns','outputColumns':[a0_0x5d8009(0x16f),a0_0x5d8009(0x14e)],'collapseRule':'array\x20of\x20objects'}],'responseShape':{'value':a0_0x5d8009(0x14c),'trend':a0_0x5d8009(0x15b),'points':'[{\x20\x22period\x22:\x20\x222026-04-24\x22,\x20\x22value\x22:\x20\x221850\x22\x20},\x20...\x20]'},'referenceWidgetId':'avg_daily_sales','socNotes':a0_0x5d8009(0x154)},{'id':'metric_progress_to_goal','name':a0_0x5d8009(0x155),'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':'file:query/<path>/current.sql','trend':'file:query/<path>/trend.sql','target':a0_0x5d8009(0x168)}},'sqlShapesPerKey':[{'key':'value','shape':'1\x20row\x20×\x201\x20column','outputColumns':[a0_0x5d8009(0x14f)],'collapseRule':a0_0x5d8009(0x17e)},{'key':a0_0x5d8009(0x15f),'shape':'1\x20row\x20×\x202\x20columns','outputColumns':['direction','pct'],'collapseRule':a0_0x5d8009(0x192)},{'key':'target','shape':'1\x20row\x20×\x201\x20column','outputColumns':[a0_0x5d8009(0x15c)],'collapseRule':'scalar\x20primitive'}],'responseShape':{'value':'\x221836\x22','trend':'{\x20\x22direction\x22:\x20\x22down\x22,\x20\x22pct\x22:\x20\x222.2\x22\x20}','target':'\x222884\x22'},'referenceWidgetId':a0_0x5d8009(0x183),'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':a0_0x5d8009(0x156),'minLength':0x6,'maxLength':0x32,'regex':a0_0x5d8009(0x169),'examples':[a0_0x5d8009(0x149),a0_0x5d8009(0x15e),'dash-author-stats'],'rationale':a0_0x5d8009(0x165)}},URL_PATTERN={'method':a0_0x5d8009(0x15a),'path':a0_0x5d8009(0x19a),'exampleFull':'POST\x20/api/mini-inventory/dash-inbound/dashboard','requestBodyShape':{'params':'object\x20—\x20values\x20for\x20declared\x20params\x20(validated\x20against\x20params\x20contract;\x20missing\x20required\x20→\x20400,\x20type\x20mismatch\x20→\x20400)','widgets':'array<string>,\x20optional\x20—\x20subset\x20of\x20widget\x20IDs\x20to\x20execute.\x20Omit\x20to\x20execute\x20all\x20declared\x20widgets.'},'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':'file:relative/path/to/query.sql','pathRelativeTo':'payload\x20JSON\x20file\x20location','fileExtensionPolicy':a0_0x5d8009(0x181),'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':a0_0x5d8009(0x19d)},PLACEHOLDER_CONVENTION={'format':a0_0x5d8009(0x16b),'regex':a0_0x5d8009(0x199),'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_0x5d8009(0x18b),'exampleSql':'SELECT\x20*\x20FROM\x20stock_inbound\x20WHERE\x20EXTRACT(YEAR\x20FROM\x20inbound_date)\x20=\x20:year','exampleParamDeclaration':a0_0x5d8009(0x184)},CACHE_SPEC={'container':a0_0x5d8009(0x144),'optional':!![],'rationale':a0_0x5d8009(0x189),'fields':[{'name':a0_0x5d8009(0x191),'type':'boolean','required':!![],'description':'Toggle\x20cache\x20feature\x20for\x20this\x20dashboard.'},{'name':'ttl','type':a0_0x5d8009(0x16a),'required':![],'constraint':'>=\x200\x20(seconds)','default':'inherits\x20CACHE_TTL\x20env','description':'Time-to-live\x20in\x20seconds.\x200\x20effectively\x20disables\x20cache\x20for\x20this\x20entry.'},{'name':'invalidates','type':'array<string>','required':![],'default':'[]','description':a0_0x5d8009(0x142)}],'validation':{'sqlCrossReference':a0_0x5d8009(0x15d),'errorOn':['Table\x20appears\x20in\x20SQL\x20AND\x20in\x20metadata\x20project,\x20but\x20missing\x20from\x20invalidates\x20(cache\x20stale\x20risk)',a0_0x5d8009(0x166)],'warningOn':[a0_0x5d8009(0x13f)]}},DOCUMENTATION_URL='https://restforge.dev/docs/server/query-data/dashboard',DASHBOARD_CATALOG={'schemaVersion':'1.0','source':a0_0x5d8009(0x159),'summary':{'totalAllowedTopLevelFields':PAYLOAD_SHAPE[a0_0x5d8009(0x152)]['length'],'totalForbiddenFrontendFields':FORBIDDEN_FRONTEND_FIELDS[a0_0x5d8009(0x188)],'totalParamTypes':ALLOWED_PARAM_TYPES['length'],'totalScalarCollapseRules':SCALAR_COLLAPSE_RULES['length'],'totalCommonWidgetPatterns':COMMON_WIDGET_PATTERNS[a0_0x5d8009(0x188)]},'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_0x5d8009(0x171)]={'DASHBOARD_CATALOG':DASHBOARD_CATALOG};
1
+ const a0_0x5de898=a0_0x1e66;(function(_0x121d3a,_0x483816){const _0x36c52d=a0_0x1e66,_0x730bda=_0x121d3a();while(!![]){try{const _0x332d3f=-parseInt(_0x36c52d(0x178))/0x1*(-parseInt(_0x36c52d(0x15a))/0x2)+parseInt(_0x36c52d(0x173))/0x3*(parseInt(_0x36c52d(0x164))/0x4)+-parseInt(_0x36c52d(0x12a))/0x5+-parseInt(_0x36c52d(0x13e))/0x6*(parseInt(_0x36c52d(0x16b))/0x7)+parseInt(_0x36c52d(0x11a))/0x8+-parseInt(_0x36c52d(0x174))/0x9*(parseInt(_0x36c52d(0x13a))/0xa)+-parseInt(_0x36c52d(0x177))/0xb;if(_0x332d3f===_0x483816)break;else _0x730bda['push'](_0x730bda['shift']());}catch(_0x1e288c){_0x730bda['push'](_0x730bda['shift']());}}}(a0_0x2228,0xcfc52));function a0_0x2228(){const _0x316c52=['yxzNx2rHAwX5x3nHBgvZ','Cgn0','q29SBgfWC2uGDg8GB2jQzwn0ihDOB3nLigTLExmGyxjLifnrtcbJB2X1Bw4GBMfTzxmGkgXVD2vYy2fZzwqPlG','Aw52ywXPzgf0zxm','zgfZAc1PBMjVDw5K','uMvZzxj2zwqGzM9YiensvuqGCgf5Bg9HzhmUieeGzgfZAgjVyxjKihbHEwXVywqGBxvZDcbKzwnSyxjLicD3AwrNzxrZjYbPBNn0zwfKlG','ue9tva','msbYB3CGW5CGmIbJB2XZlcbVDxrWDxqGy29SDw1UCYaNzgLYzwn0Aw9UjYWGj3bJDcC','q29SBgfWC2uGDg8GC2nHBgfYihbYAw1PDgL2zsaODgHLihzHBhvLig9MihrOzsbZAw5NBguGy29SDw1Uks4','DgfIBgvoyw1L','nZGXmZK5mgT1r1zJsG','iNzHBhvLiJOGiJy5nZaWiG','uMv0DxjUigfZigfYCMf5ig9Mig9IAMvJDhmGkg5VignVBgXHChnLks4','DMfSDwu','mtm2mtrls1DqsMW','oNbHCMfTtMfTzq','iNrYzw5KiJOGEYaIzgLYzwn0Aw9UiJOGiNvWiIWGiNbJDci6iciYlJiIih0','Dg9WlwXLDMvSicDWyxjHBxmNig9IAMvJDa','DhLWzq','msbYB3CGW5CGmIbJB2X1Bw5Z','v2HLBIb0CNvLlcb0AguGCMvXDwvZDcbIB2r5ie1vu1qGAw5JBhvKzsb0AgLZihbHCMfTicHVDgHLCNDPC2uGndaWks4','BgvUz3rO','zgvMyxvSDa','phDPzgDLDf9Pzd4','B2jQzwn0','zgfZAc1HDxrOB3iTC3rHDhm','ugvYlwTLEsbIyxnLzcbVBIbZy2fSyxjdB2XSyxbZzvj1BgvZigjLBg93lG','qsbWyxLSB2fKihDPDgGGyM90AcaND2LKz2v0CYCGyw5KicD0ywjSzu5HBwuNigLZihjLAMvJDgvKigj5ierHC2HIB2fYzfzHBgLKyxrVCI4GugLJAYbVBMuGC2HHCguU','iNnOB3bWAw5Nx2nHDgvNB3jPzxmIoIb7icjPDgvTCYi6ifT7icjUyw1LiJOGiKXHBMrZiIb9lcb7icjUyw1LiJOGiKHVDxnLCYiGFv0GFq','zgf0zq','tIbYB3DZimoxidiGy29SDw1UCW','rgv0zxjTAw5LzcbIEsbZy2fSyxjdB2XSyxbZzvj1BgvZlIbgywLSzwqGD2LKz2v0CYbWCM9KDwnLihSGzxjYB3i6icCUlI4Nih0GyMXVy2SGD2L0Acb0B3aTBgv2zwWGC3vJy2vZCYbZDgLSBcb0CNvLicHVBMuGD2LKz2v0igzHAwX1CMuGzg9LCYbot1qGzMfPBcb0AguGzgfZAgjVyxjKks4','twv0CMLJicSGrg9UDxqGqNjLywTKB3DU','vgfIBguGzgv0zwn0zwqGAw4Gu1fmlcbIDxqGBM90ihjLz2LZDgvYzwqGyxmGq1jvrcbLBMrWB2LUDcbPBIbTzxrHzgf0ysbWCM9Qzwn0icHSAwTLBhKGysb2Awv3lcbdveuGywXPyxmSig9YignYB3nZlxbYB2PLy3qGDgfIBguG4OcuignHC2nHzguGD2LSBcbUB3qGzMLYzsK','zMLSztPXDwvYEs88Cgf0Ad4VyNjLywTKB3DUlNnXBa','pJ0GmcaOC2vJB25KCYK','tvvtvcbZDgfYDcb3AxrOicDKyxnOlsCGChjLzML4','CxvLCMLLCW','yxjYyxK8C3rYAw5NpG','tgLZDcbVzIb3AwrNzxqGzgvMAw5PDgLVBNmUie9YzgvYigLZigLUzM9YBwf0Aw9UywWGB25SEsaOCMvZCg9UC2uGA2v5CYbHCMuGyNKGD2LKz2v0igLKlcbUB3qGyxjYyxKGAw5KzxGPlG','zgfZAgjVyxjKihbHEwXVywq','D2LKz2v0lNf1zxjPzxmUpgTLEt4GD2L0AcbtuuWGCMv0DxjUAw5NideGCM93imoxig11BhrPCgXLignVBhvTBNm','mJy0ote0renjqNfN','CxvLCNK','C2nHBgfYihbYAw1PDgL2zq','Bgf5B3v0','BNvTyMvY','DgfYz2v0','zMLSztPYzwXHDgL2zs9WyxrOl3rVl3f1zxj5lNnXBa','C3rYAw5N','Dg9Wtgv2zwXbBgXVD2vK','CMvXDwLYzwq','neDxDKLhzG','Cgf5Bg9Hzcbku09oigzPBguGBg9JyxrPB24','B3jKzxjZx3rOAxnFBw9UDgG','D2LKz2v0vhLWzq','vMLZDwfSignVBg9YigLZigeGzNjVBNrLBMqGCMvUzgvYAw5NignVBMnLCM4U','ugfYyw0GBMfTzsbTDxn0ig1HDgnOihrOzsbWBgfJzwHVBgrLCIbYzwDLEcbGw2eTEKeTwL9Dw2eTEKeTwJaTov9DkMaGkgfSCgHHBNvTzxjPyYaRihvUzgvYC2nVCMuSig11C3qGC3rHCNqGD2L0AcbSzxr0zxiGB3iGDw5KzxjZy29YzsKU','CgvYAw9K','ndCXmuPqq3nurW','DhrS','kd88itOPoIHBys16qs1Ax11Bys16qs1Amc05x10Qkq','BgfIzwW','zMLSztPXDwvYEs88Cgf0Ad4VDMfSDwuUC3fS','Cg9PBNrZ','zxHWzwn0zwrFzwfYBMLUz3m','zgfZAc1ZywXLCW','ndy5nJiXnuLmsg9dDG','owX4ExPjyq','iJy5nZaWiG','txvSDgKTu1fmihDPzgDLDc4GrwfJAcbRzxKGyMvJB21LCYbHigTLEsbPBIb0AguGCMvZCg9UC2uGB2jQzwn0lG','mtGYmZu0n2nAChvqzW','oxzpCuj0sq','otiWmZiYnfbPq3HQqG','C3vIDgL0Bgu','DhjLBMq','zMLSztPXDwvYEs88Cgf0Ad4VDhjLBMqUC3fS','xMrHC2GTw2eTEKeTwJaTov8TxsSK','twv0CMLJicSGu3bHCMTSAw5L','rNjVBNrLBMqGzgv0zxjTAw5LCYbKB251Dc9WAwuGDMfYAwfUDcWGy29SB3iGCgvYignHDgvNB3j5lcbHBMqGBgfIzwWGB3jKzxiUieLMihbLCI1JyxrLz29YEsbWzxjJzw50ywDLigLZig5LzwrLzcbMB3iGDgHLigrVBNv0igfYyYWGzNjVBNrLBMqGy29TChv0zxmGAxqGzNjVBsbPDgvTC1TPxs52ywX1zsaVihn1BsHPDgvTC1SQxs52ywX1zsKUie5Vig5LzwqGDg8GC2vUzcaNCgn0jYbMCM9TigjHy2TLBMqGDw5SzxnZihrOzsbMAwD1CMuGAxmGysbZDgfIBguGyNvZAw5LC3mGy2fSy3vSyxrPB24GAw5KzxbLBMrLBNqGB2yGDMLZDwfSihjLBMrLCMLUzY4','D2LKz2v0lNf1zxj5icHZAw5NDwXHCIK','rNjVBNrLBMqGy29TChv0zxmGDg9Fz29HBca9ihrHCMDLDcaTihzHBhvLigfUzcbWy3qGpsbYB3vUzcH2ywX1zsaVihrHCMDLDcaQideWmcKGzM9YihrOzsbWCM9NCMvZCYbIyxiUifzPC3vHBcb3Awr0AcbPCYbWCMvZzw50yxrPB25HBcbHBMqGBxvZDcbot1qGBgL2zsbPBIb0AguGyMfJA2vUzcbWyxLSB2fKlIbjzIbWCM9NCMvZCYbPBNzVBhzLCYbJB21WBgv4igj1C2LUzxnZihj1BgvZicHLlMCUigv4y2X1zguGD2vLA2vUzhmSihbYB3jHDgvKihDVCMTKyxLZksWGDxnLigeGC2LUz2XLig11BhrPlwnVBhvTBIbXDwvYEsbZBYaNCgn0jYbPCYbHihn0ywjSzsbIDxnPBMvZCYbMywn0ihjHDgHLCIb0AgfUihzPC3vHBcb3Awr0Ac4','yM9VBgvHBG','vuKGBgfIzwWGAxmGysbMCM9UDgvUzcbYzw5KzxjPBMCGy29Uy2vYBI4','y2fJAgu','EYaIzgLYzwn0Aw9UiJOGiNvWiIWGiNbJDci6iciYlJiIih0','tM90igeGzgfZAgjVyxjKihbHEwXVywqGkgXPA2vSEsbduLveihDPDgGGDgfIBgvoyw1LlcbVCIbPBNzHBgLKkq','zgLYzwn0Aw9U','yxjYyxK8C3rYAw5NpIWGB3b0Aw9UywWG4Ocuihn1yNnLDcbVzIb3AwrNzxqGsurZihrVigv4zwn1DguUie9TAxqGDg8GzxHLy3v0zsbHBgWGzgvJBgfYzwqGD2LKz2v0CY4','mJKXmZqXme12DvPIra','yw55icGXihjVDYddLYaXignVBcWGtIbYB3DZimoxie0Gy29SCYWGzxrJlIK','tgLZDcbVzIbduLveihrHyMXLig5HBwvZihrOyxqSihDOzw4GD3jPDhrLBIWGD2LSBcb0CMLNz2vYigLUDMfSAwrHDgLVBIbVzIb0AgLZigrHC2HIB2fYzcbJywnOzs4','rgvMyxvSDcb2ywX1zsbHChbSAwvKihDOzw4GDgHLihjLCxvLC3qGB21PDhmGDgHPCYbWyxjHBs4GvMfSAwrHDg9YigrVzxmGtK9uihn0CMLJDgX5ihr5CguTy2HLy2SGzgvMyxvSDdSGCNvUDgLTzsbPCYbYzxnWB25ZAwjSzsbMB3iGy29TCgf0AwjPBgL0Es4','zMLSztPXDwvYEs88Cgf0Ad4VCg9PBNrZlNnXBa','u2LUz2XLifnrtcbXDwvYEsbMB3iGDgHLihDPzgDLDc4'];a0_0x2228=function(){return _0x316c52;};return a0_0x2228();}function a0_0x1e66(_0x50e4aa,_0x4c5bb5){_0x50e4aa=_0x50e4aa-0x11a;const _0x22288c=a0_0x2228();let _0x1e6611=_0x22288c[_0x50e4aa];if(a0_0x1e66['hlRPct']===undefined){var _0x4e4b19=function(_0x5861d8){const _0x193e1='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x536459='',_0x4f9545='';for(let _0x51152b=0x0,_0x5b8a22,_0x1f5db5,_0x4e9850=0x0;_0x1f5db5=_0x5861d8['charAt'](_0x4e9850++);~_0x1f5db5&&(_0x5b8a22=_0x51152b%0x4?_0x5b8a22*0x40+_0x1f5db5:_0x1f5db5,_0x51152b++%0x4)?_0x536459+=String['fromCharCode'](0xff&_0x5b8a22>>(-0x2*_0x51152b&0x6)):0x0){_0x1f5db5=_0x193e1['indexOf'](_0x1f5db5);}for(let _0x4fea0e=0x0,_0x19cca9=_0x536459['length'];_0x4fea0e<_0x19cca9;_0x4fea0e++){_0x4f9545+='%'+('00'+_0x536459['charCodeAt'](_0x4fea0e)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x4f9545);};a0_0x1e66['stZBnT']=_0x4e4b19,a0_0x1e66['SobHJN']={},a0_0x1e66['hlRPct']=!![];}const _0x34549c=_0x22288c[0x0],_0x5aba68=_0x50e4aa+_0x34549c,_0x27c4c8=a0_0x1e66['SobHJN'][_0x5aba68];return!_0x27c4c8?(_0x1e6611=a0_0x1e66['stZBnT'](_0x1e6611),a0_0x1e66['SobHJN'][_0x5aba68]=_0x1e6611):_0x1e6611=_0x27c4c8,_0x1e6611;}const FORBIDDEN_FRONTEND_FIELDS=[a0_0x5de898(0x167),a0_0x5de898(0x15d),'title',a0_0x5de898(0x11b),'color'],ALLOWED_PARAM_TYPES=['string','number',a0_0x5de898(0x123),a0_0x5de898(0x14d)],FRONTEND_CONCERN_REASONS={'widgetType':'Visual\x20variant\x20(donut,\x20bar,\x20pie,\x20area)\x20is\x20a\x20frontend\x20rendering\x20concern\x20(separation\x20of\x20concerns).','layout':'Layout\x20is\x20a\x20frontend\x20rendering\x20concern.','title':a0_0x5de898(0x124),'subtitle':'UI\x20label\x20is\x20a\x20frontend\x20rendering\x20concern.','color':a0_0x5de898(0x168)},PAYLOAD_SHAPE={'discriminator':{'field':'widgets','presentMeans':a0_0x5de898(0x158),'absentMeans':a0_0x5de898(0x127),'conflictsWith':a0_0x5de898(0x139),'conflictRationale':a0_0x5de898(0x14b)},'topLevelAllowed':[{'name':'widgets','type':'array','required':!![],'minItems':0x1,'description':a0_0x5de898(0x157)},{'name':'params','type':'object','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_0x5de898(0x125),'type':'object','required':![],'description':'Optional\x20cache\x20configuration.\x20See\x20cacheSpec\x20for\x20details.'}],'topLevelForbidden':[{'name':a0_0x5de898(0x139),'category':'shape-conflict','reason':a0_0x5de898(0x135)},...FORBIDDEN_FRONTEND_FIELDS['map'](_0x536459=>({'name':_0x536459,'category':'frontend-concern','reason':FRONTEND_CONCERN_REASONS[_0x536459]}))]},WIDGET_SPEC={'requiredFields':[{'name':'id','type':a0_0x5de898(0x161),'constraint':'non-empty,\x20unique\x20across\x20widgets\x20in\x20the\x20same\x20payload','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_0x5de898(0x15b),'type':a0_0x5de898(0x161),'format':a0_0x5de898(0x160),'description':a0_0x5de898(0x12f),'responseShape':'Always\x20{\x20items:\x20[...]\x20}\x20regardless\x20of\x20SQL\x20result\x20shape.'},{'name':a0_0x5de898(0x155),'type':'object','format':'key→file:relative/path/to/query.sql','minKeys':0x1,'description':a0_0x5de898(0x176),'responseShape':a0_0x5de898(0x14a)}]},'forbiddenFields':FORBIDDEN_FRONTEND_FIELDS},PARAM_SPEC={'container':a0_0x5de898(0x141),'keyConvention':a0_0x5de898(0x169),'perEntryFields':[{'name':a0_0x5de898(0x142),'required':!![],'allowedValues':ALLOWED_PARAM_TYPES,'description':'Param\x20data\x20type.\x20Validates\x20request\x20body\x20and\x20shapes\x20runtime\x20parameter\x20binding.'},{'name':a0_0x5de898(0x163),'required':![],'type':a0_0x5de898(0x123),'default':![],'description':a0_0x5de898(0x144)},{'name':a0_0x5de898(0x146),'required':![],'type':'any\x20(must\x20be\x20compatible\x20with\x20declared\x20\x27type\x27)','description':a0_0x5de898(0x12d)}]},SCALAR_COLLAPSE_RULES=[{'appliesTo':a0_0x5de898(0x121),'rule':'Always\x20wrap\x20as\x20{\x20items:\x20[...]\x20}\x20regardless\x20of\x20SQL\x20result\x20shape.','exampleSqlShape':a0_0x5de898(0x12b),'exampleResponse':a0_0x5de898(0x14c)},{'appliesTo':'widget.queries.<key>\x20with\x20SQL\x20returning\x201\x20row\x20×\x201\x20column','rule':a0_0x5de898(0x138),'exampleSqlShape':'1\x20row\x20×\x201\x20col,\x20output\x20column\x20\x27value\x27','exampleResponse':a0_0x5de898(0x13b)},{'appliesTo':a0_0x5de898(0x159),'rule':a0_0x5de898(0x132),'exampleSqlShape':a0_0x5de898(0x137),'exampleResponse':a0_0x5de898(0x140)},{'appliesTo':'widget.queries.<key>\x20with\x20SQL\x20returning\x20N\x20rows','rule':a0_0x5de898(0x13c),'exampleSqlShape':'N\x20rows\x20×\x20M\x20cols','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_0x5de898(0x150),'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_0x5de898(0x147),'queries':{'value':'file:query/<path>/value.sql','trend':'file:query/<path>/trend.sql','items':a0_0x5de898(0x152)}},'sqlShapesPerKey':[{'key':'value','shape':'1\x20row\x20×\x201\x20column','outputColumns':[a0_0x5de898(0x13d)],'collapseRule':a0_0x5de898(0x15c)},{'key':'trend','shape':'1\x20row\x20×\x202\x20columns','outputColumns':[a0_0x5de898(0x128),a0_0x5de898(0x131)],'collapseRule':a0_0x5de898(0x148)},{'key':'items','shape':'N\x20rows\x20×\x202\x20columns','outputColumns':[a0_0x5de898(0x16e),'value'],'collapseRule':'array\x20of\x20objects'}],'responseShape':{'value':a0_0x5de898(0x175),'trend':a0_0x5de898(0x126),'items':'[{\x20\x22label\x22:\x20\x22Shoes\x22,\x20\x22value\x22:\x20\x227660\x22\x20},\x20{\x20\x22label\x22:\x20\x22Gaming\x22,\x20\x22value\x22:\x20\x222820\x22\x20},\x20{\x20\x22label\x22:\x20\x22Others\x22,\x20\x22value\x22:\x20\x2245257\x22\x20}]'},'referenceWidgetId':a0_0x5de898(0x171),'socNotes':a0_0x5de898(0x120)},{'id':'metric_sparkline','name':a0_0x5de898(0x11f),'useCase':'Headline\x20metric\x20with\x20trend\x20chip\x20and\x20sparkline\x20mini-chart\x20for\x20short\x20windows\x20(7\x20days,\x2012\x20months,\x20etc.).\x20Suitable\x20for\x20widgets\x20like\x20\x27Average\x20Daily\x20Sales\x27.','payloadShape':{'id':'<widget_id>','queries':{'value':a0_0x5de898(0x16f),'trend':a0_0x5de898(0x11d),'points':a0_0x5de898(0x12e)}},'sqlShapesPerKey':[{'key':a0_0x5de898(0x13d),'shape':'1\x20row\x20×\x201\x20column','outputColumns':[a0_0x5de898(0x13d)],'collapseRule':'scalar\x20primitive'},{'key':'trend','shape':'1\x20row\x20×\x202\x20columns','outputColumns':[a0_0x5de898(0x128),'pct'],'collapseRule':a0_0x5de898(0x148)},{'key':a0_0x5de898(0x170),'shape':a0_0x5de898(0x14e),'outputColumns':[a0_0x5de898(0x16a),'value'],'collapseRule':'array\x20of\x20objects'}],'responseShape':{'value':'\x222420\x22','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':a0_0x5de898(0x130),'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':'Metric\x20+\x20Progress\x20to\x20Goal','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':'file:query/<path>/current.sql','trend':'file:query/<path>/trend.sql','target':'file:query/<path>/target.sql'}},'sqlShapesPerKey':[{'key':'value','shape':'1\x20row\x20×\x201\x20column','outputColumns':['value\x20(or\x20current)'],'collapseRule':a0_0x5de898(0x15c)},{'key':a0_0x5de898(0x11c),'shape':a0_0x5de898(0x143),'outputColumns':['direction','pct'],'collapseRule':a0_0x5de898(0x148)},{'key':a0_0x5de898(0x15f),'shape':'1\x20row\x20×\x201\x20column','outputColumns':['target'],'collapseRule':'scalar\x20primitive'}],'responseShape':{'value':'\x221836\x22','trend':'{\x20\x22direction\x22:\x20\x22down\x22,\x20\x22pct\x22:\x20\x222.2\x22\x20}','target':'\x222884\x22'},'referenceWidgetId':a0_0x5de898(0x166),'socNotes':a0_0x5de898(0x122)}],NAMING_CONVENTION={'dashboardName':{'constraint':a0_0x5de898(0x154),'minLength':0x6,'maxLength':0x32,'regex':a0_0x5de898(0x11e),'examples':[a0_0x5de898(0x172),a0_0x5de898(0x134),a0_0x5de898(0x149)],'rationale':'The\x20prefix\x20becomes\x20part\x20of\x20the\x20URL\x20segment.\x20The\x20reserved\x20scheme\x20keeps\x20dashboard\x20endpoints\x20visually\x20distinct\x20from\x20CRUD\x20endpoints\x20in\x20the\x20URL\x20space\x20and\x20allows\x20future\x20routing\x20differentiation.'}},URL_PATTERN={'method':a0_0x5de898(0x136),'path':'/api/{project}/{name}/dashboard','exampleFull':'POST\x20/api/mini-inventory/dash-inbound/dashboard','requestBodyShape':{'params':'object\x20—\x20values\x20for\x20declared\x20params\x20(validated\x20against\x20params\x20contract;\x20missing\x20required\x20→\x20400,\x20type\x20mismatch\x20→\x20400)','widgets':a0_0x5de898(0x129)},'responseShape':{'envelope':'{\x20success:\x20boolean,\x20data:\x20{\x20<widgetId>:\x20<perWidgetResponse>,\x20...\x20}\x20}','perWidgetResponse':a0_0x5de898(0x14f)}},FILE_REFERENCE_CONVENTION={'format':a0_0x5de898(0x160),'pathRelativeTo':a0_0x5de898(0x165),'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':a0_0x5de898(0x13f),'regex':a0_0x5de898(0x16d),'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':'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':'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':'top-level\x20\x27cache\x27\x20object','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':'Toggle\x20cache\x20feature\x20for\x20this\x20dashboard.'},{'name':a0_0x5de898(0x16c),'type':a0_0x5de898(0x15e),'required':![],'constraint':a0_0x5de898(0x153),'default':'inherits\x20CACHE_TTL\x20env','description':'Time-to-live\x20in\x20seconds.\x200\x20effectively\x20disables\x20cache\x20for\x20this\x20entry.'},{'name':a0_0x5de898(0x133),'type':a0_0x5de898(0x156),'required':![],'default':'[]','description':a0_0x5de898(0x12c)}],'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_0x5de898(0x151)]}},DOCUMENTATION_URL='https://restforge.dev/docs/server/query-data/dashboard',DASHBOARD_CATALOG={'schemaVersion':'1.0','source':'dashboard-catalog','summary':{'totalAllowedTopLevelFields':PAYLOAD_SHAPE[a0_0x5de898(0x162)]['length'],'totalForbiddenFrontendFields':FORBIDDEN_FRONTEND_FIELDS[a0_0x5de898(0x145)],'totalParamTypes':ALLOWED_PARAM_TYPES['length'],'totalScalarCollapseRules':SCALAR_COLLAPSE_RULES['length'],'totalCommonWidgetPatterns':COMMON_WIDGET_PATTERNS[a0_0x5de898(0x145)]},'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['exports']={'DASHBOARD_CATALOG':DASHBOARD_CATALOG};
@@ -1 +1 @@
1
- const a0_0x2f54bc=a0_0x232a;function a0_0x24c8(){const _0x48f2d0=['mZr5tuXHuMm','C3rYAw5N','Aw5JBhvKzxm','mta0ndDXv0D0u2G','m1vTEg5sqW','ndm1nta0Dgv0wxHY','u0vsvKvsx0ferfjfu1m','DhjPBq','Au1gvMS','n2vUqLHzDG','otC5mdaWD2fbvePu','rejFtKfnrq','uhLfBwC','t1PeBgq','ChvZAa','mZC0ntu4nhPeB0zVAG','nJu3mZe1mhjgvxrPAW','nZK1otm2sLfvDLDw','BgvUz3rO','rejFvvnfuG','C2XPy2u','rejFue9sva','u0vsvKvsx1bpuLq','yM9VBgvHBG','DgvZDa','C3bSAxq','teLdru5trq','ntG1mJG0sNnPr0ru'];a0_0x24c8=function(){return _0x48f2d0;};return a0_0x24c8();}(function(_0x3371cb,_0x101ee4){const _0x2bd334=a0_0x232a,_0x5b114e=_0x3371cb();while(!![]){try{const _0x3c6808=-parseInt(_0x2bd334(0x14a))/0x1*(-parseInt(_0x2bd334(0x163))/0x2)+-parseInt(_0x2bd334(0x14b))/0x3*(parseInt(_0x2bd334(0x162))/0x4)+parseInt(_0x2bd334(0x151))/0x5+-parseInt(_0x2bd334(0x14c))/0x6+-parseInt(_0x2bd334(0x150))/0x7*(parseInt(_0x2bd334(0x158))/0x8)+-parseInt(_0x2bd334(0x156))/0x9+parseInt(_0x2bd334(0x157))/0xa;if(_0x3c6808===_0x101ee4)break;else _0x5b114e['push'](_0x5b114e['shift']());}catch(_0x3f2aa0){_0x5b114e['push'](_0x5b114e['shift']());}}}(a0_0x24c8,0x484cd));function a0_0x232a(_0x1d1dff,_0x7b7c32){_0x1d1dff=_0x1d1dff-0x14a;const _0x24c856=a0_0x24c8();let _0x232a62=_0x24c856[_0x1d1dff];if(a0_0x232a['LiPClw']===undefined){var _0xb04ee=function(_0x7ac3b1){const _0x183a88='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x29cb80='',_0x8c1b81='';for(let _0x14b592=0x0,_0x2e9f33,_0x115198,_0x30fffd=0x0;_0x115198=_0x7ac3b1['charAt'](_0x30fffd++);~_0x115198&&(_0x2e9f33=_0x14b592%0x4?_0x2e9f33*0x40+_0x115198:_0x115198,_0x14b592++%0x4)?_0x29cb80+=String['fromCharCode'](0xff&_0x2e9f33>>(-0x2*_0x14b592&0x6)):0x0){_0x115198=_0x183a88['indexOf'](_0x115198);}for(let _0x1a8b25=0x0,_0x2d1327=_0x29cb80['length'];_0x1a8b25<_0x2d1327;_0x1a8b25++){_0x8c1b81+='%'+('00'+_0x29cb80['charCodeAt'](_0x1a8b25)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x8c1b81);};a0_0x232a['fijkLe']=_0xb04ee,a0_0x232a['zZeMZk']={},a0_0x232a['LiPClw']=!![];}const _0x282a07=_0x24c856[0x0],_0x130724=_0x1d1dff+_0x282a07,_0xf54f25=a0_0x232a['zZeMZk'][_0x130724];return!_0xf54f25?(_0x232a62=a0_0x232a['fijkLe'](_0x232a62),a0_0x232a['zZeMZk'][_0x130724]=_0x232a62):_0x232a62=_0xf54f25,_0x232a62;}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_0x2f54bc(0x161),a0_0x2f54bc(0x14d),a0_0x2f54bc(0x15d),'DB_TYPE','DB_HOST',a0_0x2f54bc(0x15c),a0_0x2f54bc(0x15a),'DB_PASSWORD',a0_0x2f54bc(0x152)]);function parseTemplateAsSchema(_0x527337){const _0x2eac7d=a0_0x2f54bc,_0x3502ef={'PyEmg':function(_0x55fe71,_0x53dc54){return _0x55fe71===_0x53dc54;},'iMFVk':function(_0x3dca85,_0x897187){return _0x3dca85>_0x897187;},'pdktj':function(_0x239cfb,_0x3c2ec9){return _0x239cfb<_0x3c2ec9;},'OZDld':function(_0x4f16d4,_0x1f8449){return _0x4f16d4+_0x1f8449;},'cuvYP':'false','WVTFq':_0x2eac7d(0x15e)},_0x2dc9cd=_0x527337||DB_CONNECTION_ENV_TEMPLATE,_0x59511d=_0x2dc9cd[_0x2eac7d(0x160)]('\x0a'),_0x5bd789=[];let _0x44ddef=null,_0x18a396=[];for(const _0xc25aeb of _0x59511d){const _0x3c6bfa=_0xc25aeb['trim']();if(_0x3502ef[_0x2eac7d(0x153)](_0x3c6bfa,'')){_0x18a396=[];continue;}if(_0x3c6bfa['startsWith']('#')){const _0x307cb1=_0x3c6bfa[_0x2eac7d(0x15b)](0x1)['trim'](),_0x3caf62=_0x3502ef[_0x2eac7d(0x14f)](_0x307cb1[_0x2eac7d(0x159)],0x0)&&_0x3502ef['pdktj'](_0x307cb1['length'],0x3c)&&!_0x307cb1[_0x2eac7d(0x165)](':')&&!/^[A-Z_]+=/[_0x2eac7d(0x15f)](_0x307cb1)&&/^[A-Z]/[_0x2eac7d(0x15f)](_0x307cb1);_0x3caf62&&_0x18a396['length']===0x0?_0x44ddef=_0x307cb1:_0x18a396[_0x2eac7d(0x155)](_0x307cb1);continue;}const _0x4344dc=_0xc25aeb['indexOf']('=');if(_0x4344dc>0x0){const _0x427cad=_0xc25aeb['slice'](0x0,_0x4344dc)[_0x2eac7d(0x14e)](),_0x4dabed=_0xc25aeb[_0x2eac7d(0x15b)](_0x3502ef[_0x2eac7d(0x154)](_0x4344dc,0x1));let _0x55ea11=_0x2eac7d(0x164);if(_0x4dabed==='true'||_0x4dabed===_0x3502ef['cuvYP'])_0x55ea11=_0x3502ef['WVTFq'];else/^-?\d+$/[_0x2eac7d(0x15f)](_0x4dabed)&&(_0x55ea11='integer');_0x5bd789['push']({'name':_0x427cad,'section':_0x44ddef,'type':_0x55ea11,'default':_0x4dabed,'description':_0x18a396['join']('\x20')||null,'required':REQUIRED_KEYS['has'](_0x427cad)}),_0x18a396=[];}}return _0x5bd789;}module['exports']={'DB_CONNECTION_ENV_TEMPLATE':DB_CONNECTION_ENV_TEMPLATE,'REQUIRED_KEYS':REQUIRED_KEYS,'parseTemplateAsSchema':parseTemplateAsSchema};
1
+ const a0_0x1ca974=a0_0x1a45;(function(_0x3bb2b6,_0x40ce35){const _0x4e5937=a0_0x1a45,_0x54c5d1=_0x3bb2b6();while(!![]){try{const _0xfed8b3=-parseInt(_0x4e5937(0xe1))/0x1+-parseInt(_0x4e5937(0xe7))/0x2*(-parseInt(_0x4e5937(0xec))/0x3)+parseInt(_0x4e5937(0xe8))/0x4+parseInt(_0x4e5937(0xe6))/0x5+parseInt(_0x4e5937(0xdc))/0x6+parseInt(_0x4e5937(0xdf))/0x7+-parseInt(_0x4e5937(0xe5))/0x8*(parseInt(_0x4e5937(0xe4))/0x9);if(_0xfed8b3===_0x40ce35)break;else _0x54c5d1['push'](_0x54c5d1['shift']());}catch(_0x53834a){_0x54c5d1['push'](_0x54c5d1['shift']());}}}(a0_0x5438,0xf2842));const DB_CONNECTION_ENV_TEMPLATE=a0_0x1ca974(0xe3),REQUIRED_KEYS=new Set(['LICENSE','SERVER_ADDRESS',a0_0x1ca974(0xef),'DB_TYPE',a0_0x1ca974(0xde),'DB_PORT',a0_0x1ca974(0xe9),a0_0x1ca974(0xee),a0_0x1ca974(0xf3)]);function parseTemplateAsSchema(_0x502976){const _0x4ed414=a0_0x1ca974,_0x3112ae={'VEzUw':function(_0x252fe2,_0x4066b6){return _0x252fe2||_0x4066b6;},'luljH':function(_0x5e7315,_0x52d514){return _0x5e7315<_0x52d514;},'lIMHG':function(_0x54ba81,_0xe4df1f){return _0x54ba81+_0xe4df1f;},'eYKaR':'boolean'},_0x9c0a04=_0x3112ae[_0x4ed414(0xeb)](_0x502976,DB_CONNECTION_ENV_TEMPLATE),_0x5698db=_0x9c0a04['split']('\x0a'),_0xcdc87c=[];let _0x566bd2=null,_0x3dec62=[];for(const _0x53f254 of _0x5698db){const _0x3304a6=_0x53f254['trim']();if(_0x3304a6===''){_0x3dec62=[];continue;}if(_0x3304a6[_0x4ed414(0xf1)]('#')){const _0x504caf=_0x3304a6['slice'](0x1)['trim'](),_0x1e4bfd=_0x504caf[_0x4ed414(0xea)]>0x0&&_0x3112ae[_0x4ed414(0xda)](_0x504caf['length'],0x3c)&&!_0x504caf[_0x4ed414(0xf0)](':')&&!/^[A-Z_]+=/[_0x4ed414(0xdb)](_0x504caf)&&/^[A-Z]/[_0x4ed414(0xdb)](_0x504caf);_0x1e4bfd&&_0x3dec62[_0x4ed414(0xea)]===0x0?_0x566bd2=_0x504caf:_0x3dec62['push'](_0x504caf);continue;}const _0x3698dd=_0x53f254[_0x4ed414(0xdd)]('=');if(_0x3698dd>0x0){const _0x2cf7f0=_0x53f254['slice'](0x0,_0x3698dd)['trim'](),_0x466787=_0x53f254['slice'](_0x3112ae[_0x4ed414(0xe2)](_0x3698dd,0x1));let _0x201ffc='string';if(_0x466787===_0x4ed414(0xf2)||_0x466787==='false')_0x201ffc=_0x3112ae['eYKaR'];else/^-?\d+$/[_0x4ed414(0xdb)](_0x466787)&&(_0x201ffc=_0x4ed414(0xed));_0xcdc87c[_0x4ed414(0xd9)]({'name':_0x2cf7f0,'section':_0x566bd2,'type':_0x201ffc,'default':_0x466787,'description':_0x3dec62[_0x4ed414(0xe0)]('\x20')||null,'required':REQUIRED_KEYS['has'](_0x2cf7f0)}),_0x3dec62=[];}}return _0xcdc87c;}function a0_0x1a45(_0x4eec12,_0xf8039d){_0x4eec12=_0x4eec12-0xd9;const _0x543881=a0_0x5438();let _0x1a45c2=_0x543881[_0x4eec12];if(a0_0x1a45['rRhrJJ']===undefined){var _0x57f837=function(_0x5abd97){const _0x1ed190='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x1bbec0='',_0x11f9d6='';for(let _0x24091b=0x0,_0x1b6ccb,_0x332b14,_0xb0040b=0x0;_0x332b14=_0x5abd97['charAt'](_0xb0040b++);~_0x332b14&&(_0x1b6ccb=_0x24091b%0x4?_0x1b6ccb*0x40+_0x332b14:_0x332b14,_0x24091b++%0x4)?_0x1bbec0+=String['fromCharCode'](0xff&_0x1b6ccb>>(-0x2*_0x24091b&0x6)):0x0){_0x332b14=_0x1ed190['indexOf'](_0x332b14);}for(let _0x28975c=0x0,_0x3d5f71=_0x1bbec0['length'];_0x28975c<_0x3d5f71;_0x28975c++){_0x11f9d6+='%'+('00'+_0x1bbec0['charCodeAt'](_0x28975c)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x11f9d6);};a0_0x1a45['wiNEmt']=_0x57f837,a0_0x1a45['LZiliZ']={},a0_0x1a45['rRhrJJ']=!![];}const _0x24af8b=_0x543881[0x0],_0x30c540=_0x4eec12+_0x24af8b,_0x40adf5=a0_0x1a45['LZiliZ'][_0x30c540];return!_0x40adf5?(_0x1a45c2=a0_0x1a45['wiNEmt'](_0x1a45c2),a0_0x1a45['LZiliZ'][_0x30c540]=_0x1a45c2):_0x1a45c2=_0x40adf5,_0x1a45c2;}module['exports']={'DB_CONNECTION_ENV_TEMPLATE':DB_CONNECTION_ENV_TEMPLATE,'REQUIRED_KEYS':REQUIRED_KEYS,'parseTemplateAsSchema':parseTemplateAsSchema};function a0_0x5438(){const _0x50f67e=['BhvSAKG','DgvZDa','mtm2mZu0mMLqExvYAq','Aw5KzxHpzG','rejFse9tva','mtm3oti3m1LXBMP6rq','AM9PBG','odu1nZa0weLkAvLN','BeLnseC','iYbmAwnLBNnLcKXjq0vou0u9wfHywc1ywfHylvHywfGTwfHywaOkiYbtzxj2zxiku0vsvKvsx0ferfjfu1m9mti3lJaUmc4XcLnfuLzfuL9qt1juptmWmdakcImGtgL2zsbtEw5JicHxzwjtB2nRzxqPienVBMzPz3vYyxrPB24kiYbot1rfoIbmsvzfx1nztKnFru5bqKXfrd10CNvLihjLCxvPCMvZigfUiefqssblzxKGkeTfwt0UlI4PihrVigf1DgHLBNrPy2f0zsbxzwjtB2nRzxqGy2XPzw50CWPmsvzfx1nztKnFru5bqKXfrd1MywXZzqPmsvzfx1nztKnFue9svd0ZmdmZcGOJifjLzgLZienVBMzPz3vYyxrPB24kuKvesvnFse9tvd1SB2nHBgHVC3qkuKvesvnFue9svd02mZGWcLjfreLtx1bbu1nxt1jepqPsrurju19eqJ0WcGOJiev4Cg9YDcbdB25MAwD1CMf0Aw9UcKvyue9svf9gsuXfx0vyueLswt0ZnJaWmdaWcKvyue9svf9dsfvos19tsvPfpteWmdakcImGs2fMA2eGq29UzMLNDxjHDgLVBGPlquzlqv9ftKfctevepwzHBhnLcImGqNjVA2vYigXPC3qGkgnVBw1HlxnLCgfYyxrLzcbMB3iGBxvSDgLWBguGyNjVA2vYCZOGyNjVA2vYmtO5mdKYlgjYB2TLCJi6ota5mIXICM9RzxiZoJKWotiPcKTbrKTbx0jst0TfuLm9Bg9JywXOB3n0oJKWotikiYbdBgLLBNqGsuqGkg9WDgLVBMfSlcbKzwzHDwX0oIbYzxn0zM9Yz2uTE3bYB2PLy3r9lxbYB2r1y2vYic8GlwnVBNn1BwvYkqOJieTbrKTbx0nmsuvovf9jrd0ks0fgs0fFq09otKvdveLptL9usu1ft1vuptmWmdaks0fgs0fFuKvrvuvtvf9usu1ft1vupti1mdaWcKTbrKTbx1rpueLdx1bbvfrfuK49E21VzhvSzx0UE2vUzhbVAw50Fs5LDMvUDhmks0fgs0fFvevoqu5ux0LepwrLzMf1Bhqks0fgs0fFu0vtu0LptL9usu1ft1vuptmWmdaWcKTbrKTbx0HfqvjuqKvbvf9jtLrfuLzbtd0ZmdaWcKTbrKTbx01bwf9cwvrfu19qrvjFuefsveLusu9opteWndG1nZyks0fgs0fFqvvut19dt01nsvq9zMfSC2uks0fgs0fFqvvut19dt01nsvrFsu5urvjwquW9ntaWmaPlquzlqv9srvrswv9bvfrftvbuuZ0ZcKTbrKTbx1jfvfjzx0rftefzpteWmdaks0fgs0fFuKvuuLLFtufyx0rftefzptmWmdaWcKTbrKTbx1nttd1MywXZzqPlquzlqv9mt0DFtevwruW9Aw5MBWOJifnbu0WGqxv0AgvUDgLJyxrPB24Gkg9WDgLVBMfSlcb1BMnVBw1LBNqGAwyGDgHLigjYB2TLCIbYzxf1AxjLCYbHDxrOzw50AwnHDgLVBIKkiYbtDxbWB3j0zwqGBwvJAgfUAxnTCZOGCgXHAw4SihnJCMfTlxnOys0YntySihnJCMfTlxnOys01mtikiYblquzlqv9tqvnmx01fq0HbtKLttt1WBgfPBGOJieTbrKTbx1nbu0XFvvnfuK5btuu9cImGs0fgs0fFu0fttf9qqvntv09srd0kcImGrgf0ywjHC2uGq29UzMLNDxjHDgLVBGOJifn1ChbVCNrLzdOGCg9ZDgDYzxnXBcWGBxLZCwWSig9YywnSzsWGC3fSAxrLcKrcx1rzueu9Cg9ZDgDYzxnXBaPeqL9it1nupteYnY4WlJaUmqPeqL9qt1juptu0mZikrejFvvnfuJ1WB3n0z3jLCWPeqL9qqvntv09srd15B3vYx3bHC3n3B3jKx2HLCMukrejFtKfnrt15B3vYx2rHDgfIyxnLx25HBwukiYbgB3iGu1fmAxrLoIbZzxqGrejFvfLqrt1ZCwXPDguGyw5Kiercx05btuu9lI9KyxrHl215yxbWlMrIcImGrejFse9tvcWGrejFue9svcWGrejFvvnfuIWGrejFueftu1DpuKqGyxjLigLNBM9YzwqGzM9YifnrtgL0zqOkiYbmB2DNAw5NienVBMzPz3vYyxrPB24kte9hx0XfvKvmpwrLyNvNcKXpr19ut19gsuXfpxrYDwukcImGu1fmieXVz2DPBMCku1fmx0Xpr19ftKfctevepwzHBhnLcLnrtf9mt0DFtevwruW9zgvIDwCku1fmx0Xpr19qqvjbtvm9zMfSC2uku1fmx0Xpr19tte9xx1riuKvtse9mrd0XmdaWcGOJienHy2HLienVBMzPz3vYyxrPB24kq0fdsevFru5bqKXfrd1MywXZzqPdqunirv9uveW9mZaWcGOJiePVyIbty2HLzhvSzxiksK9cx0voqujmruq9zMfSC2uksK9cx0nptKnvuLjftKnzptuksK9cx1jfvevoveLptL9it1vsuZ03mGPkt0jFrKfjtevex1jfvevoveLptL9it1vsuZ0XnJGksK9cx1nivvret1Dox1rjtuvpvvq9mtaWmdaksK9cx1nuquXmrurFsu5urvjwquW9mZaWmdaksK9cx01bwf9tvefmtevex0npvu5uptikcImGrgLZDhjPyNv0zwqGtg9JAYbdB25MAwD1CMf0Aw9UcKXpq0TFreLtvfjjqLvururFru5bqKXfrd1MywXZzqPmt0nlx0rju1rssujvvevex1rutd0XmaPmt0nlx1jfu09vuKnfx01bwf9uveW9nJaWcKXpq0TFreLtvfjjqLvururFuKvuuLK9mWPmt0nlx0rju1rssujvvevex1jfvfjzx0rftefzpteWmaPmt0nlx0rju1rssujvvevex1nuuKfuruDzpxjLAMvJDaOkiYbjrcbhzw5LCMf0B3iGq29UzMLNDxjHDgLVBGPjreDftL9ftKfctevepwzHBhnLcKLer0vox0Leru1FvfrmptyWmaPjreDftL9dt1vovevsx1rutf9nt05useXzpti3nJq4mdaksurhru5Fq09vtLrfuL9uveXFrefjtfK9mtCYodaWcKLer0vox0rfrKfvtfrFtufyx1jfvfjzpteWcKLer0vox0rfrKfvtfrFueLox0rjr0LuuZ02cKLer0vox0rfrKfvtfrFu0vssufmx1bbvfrfuK49wfHywc1ywfHylvHywfGTwfHywaPjreDftL9eruzbvuXux0nprevFuefuvevstJ05otK5ltK5otKksurhru5FquXmt1DFuKvtrvq9zMfSC2uk','mty0ntjqDKTyD3q','odKXmLL3rhjtzG','ntG4mdm1DKTKrfDZ','mtqXodGWmg9PywvVrG','nZy5odK1nMjOB2nHwa','rejFvvnfuG','BgvUz3rO','vKv6vxC','nNPmDhroAq','Aw50zwDLCG','rejFueftu1DpuKq','u0vsvKvsx1bpuLq','Aw5JBhvKzxm','C3rHCNrZv2L0Aa','Dhj1zq','rejFtKfnrq','ChvZAa'];a0_0x5438=function(){return _0x50f67e;};return a0_0x5438();}