@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.
- package/build-info.json +2 -2
- package/cli/consumer-deploy.js +1 -1
- package/cli/consumer.js +1 -1
- package/generators/cli/catalog/dbschema.js +2 -1
- package/generators/cli/endpoint/list.js +264 -0
- package/generators/cli/fast-track.js +395 -37
- package/generators/cli/processor/create.js +7 -7
- package/generators/cli/processor/list.js +229 -0
- package/generators/lib/dbschema-kit/apply-executor.js +20 -0
- package/generators/lib/generators/dashboard-generator.js +5 -5
- package/generators/lib/generators/processor-validation-generator.js +4 -1
- package/generators/lib/migrate/field-type-resolver.js +23 -0
- package/generators/lib/payload/payload-runner.js +80 -3
- package/generators/lib/templates/dashboard-catalog.js +1 -1
- package/generators/lib/templates/db-connection-env.js +1 -1
- package/generators/lib/templates/dbschema-catalog.js +1 -1
- package/generators/lib/templates/field-validation-catalog.js +1 -1
- package/generators/lib/templates/mysql-template.js +1 -1
- package/generators/lib/templates/oracle-template.js +1 -1
- package/generators/lib/templates/postgres-template.js +1 -1
- package/generators/lib/templates/query-declarative-catalog.js +1 -1
- package/generators/lib/templates/sqlite-template.js +1 -1
- package/integrity-manifest.json +18 -18
- package/package.json +1 -1
- package/scripts/check-install.js +8 -8
- package/scripts/verify-integrity.js +1 -1
- package/server.js +1 -1
- package/src/components/handlers/adjust_handler.js +1 -1
- package/src/components/handlers/audit_handler.js +1 -1
- package/src/components/handlers/delete_handler.js +1 -1
- package/src/components/handlers/export_handler.js +1 -1
- package/src/components/handlers/import_handler.js +1 -1
- package/src/components/handlers/insert_handler.js +1 -1
- package/src/components/handlers/update_handler.js +1 -1
- package/src/components/handlers/upload_handler.js +1 -1
- package/src/components/handlers/workflow_handler.js +1 -1
- package/src/components/integrations/webhook.js +1 -1
- package/src/consumers/baseConsumer.js +1 -1
- package/src/consumers/declarativeMapper.js +1 -1
- package/src/consumers/handlers/apiHandler.js +1 -1
- package/src/consumers/handlers/consoleHandler.js +1 -1
- package/src/consumers/handlers/databaseHandler.js +1 -1
- package/src/consumers/handlers/index.js +1 -1
- package/src/consumers/handlers/kafkaHandler.js +1 -1
- package/src/consumers/index.js +1 -1
- package/src/consumers/messageTransformer.js +1 -1
- package/src/consumers/validator.js +1 -1
- package/src/core/db/dialect/base-dialect.js +1 -1
- package/src/core/db/dialect/index.js +1 -1
- package/src/core/db/dialect/mysql-dialect.js +1 -1
- package/src/core/db/dialect/oracle-dialect.js +1 -1
- package/src/core/db/dialect/postgres-dialect.js +1 -1
- package/src/core/db/dialect/sqlite-dialect.js +1 -1
- package/src/core/db/flatten-helper.js +1 -1
- package/src/core/db/query-builder-error.js +1 -1
- package/src/core/db/query-builder.js +1 -1
- package/src/core/db/relation-helper.js +1 -1
- package/src/core/handlers/delete_handler.js +1 -1
- package/src/core/handlers/insert_handler.js +1 -1
- package/src/core/handlers/update_handler.js +1 -1
- package/src/core/models/base-model.js +1 -1
- package/src/core/utils/cache-manager.js +1 -1
- package/src/core/utils/component-engine.js +1 -1
- package/src/core/utils/context-builder.js +1 -1
- package/src/core/utils/datetime-formatter.js +1 -1
- package/src/core/utils/datetime-parser.js +1 -1
- package/src/core/utils/db.js +1 -1
- package/src/core/utils/logger.js +1 -1
- package/src/core/utils/payload-loader.js +1 -1
- package/src/core/utils/security-checks.js +1 -1
- package/src/middleware/body-options.js +1 -1
- package/src/middleware/cors.js +1 -1
- package/src/middleware/idempotency.js +1 -1
- package/src/middleware/rate-limiter.js +1 -1
- package/src/middleware/request-logger.js +1 -1
- package/src/middleware/security-headers.js +1 -1
- package/src/models/base-model-mysql.js +1 -1
- package/src/models/base-model-oracle.js +1 -1
- package/src/models/base-model-sqlite.js +1 -1
- package/src/models/base-model.js +1 -1
- package/src/pro/caching/redis-client.js +1 -1
- package/src/pro/caching/redis-helper.js +1 -1
- package/src/pro/consumers/baseConsumer.js +1 -1
- package/src/pro/consumers/declarativeMapper.js +1 -1
- package/src/pro/consumers/handlers/apiHandler.js +1 -1
- package/src/pro/consumers/handlers/consoleHandler.js +1 -1
- package/src/pro/consumers/handlers/databaseHandler.js +1 -1
- package/src/pro/consumers/handlers/index.js +1 -1
- package/src/pro/consumers/handlers/kafkaHandler.js +1 -1
- package/src/pro/consumers/index.js +1 -1
- package/src/pro/consumers/messageTransformer.js +1 -1
- package/src/pro/consumers/validator.js +1 -1
- package/src/pro/database/base-model-mysql.js +1 -1
- package/src/pro/database/base-model-oracle.js +1 -1
- package/src/pro/database/base-model-sqlite.js +1 -1
- package/src/pro/database/db-mysql.js +1 -1
- package/src/pro/database/db-oracle.js +1 -1
- package/src/pro/database/db-sqlite.js +1 -1
- package/src/pro/excel/excel-generator.js +1 -1
- package/src/pro/excel/excel-parser.js +1 -1
- package/src/pro/excel/export-service.js +1 -1
- package/src/pro/excel/export_handler.js +1 -1
- package/src/pro/excel/import-service.js +1 -1
- package/src/pro/excel/import-validator.js +1 -1
- package/src/pro/excel/import_handler.js +1 -1
- package/src/pro/excel/upsert-builder.js +1 -1
- package/src/pro/idgen/idgen-routes.js +1 -1
- package/src/pro/integrations/lookup-resolver.js +1 -1
- package/src/pro/integrations/upload-handler-v2.js +1 -1
- package/src/pro/integrations/upload-handler.js +1 -1
- package/src/pro/integrations/webhook.js +1 -1
- package/src/pro/locking/lock-routes.js +1 -1
- package/src/pro/locking/resource-lock-manager.js +1 -1
- package/src/pro/messaging/kafkaConsumerService.js +1 -1
- package/src/pro/messaging/kafkaService.js +1 -1
- package/src/pro/messaging/messagehubService.js +1 -1
- package/src/pro/messaging/rabbitmqService.js +1 -1
- package/src/pro/scheduler/job-manager.js +1 -1
- package/src/pro/scheduler/job-routes.js +1 -1
- package/src/pro/scheduler/job-validator.js +1 -1
- package/src/pro/storage/base-storage-provider.js +1 -1
- package/src/pro/storage/file-metadata-helper.js +1 -1
- package/src/pro/storage/index.js +1 -1
- package/src/pro/storage/local-storage-provider.js +1 -1
- package/src/pro/storage/s3-storage-provider.js +1 -1
- package/src/pro/storage/upload-cleanup-job.js +1 -1
- package/src/pro/storage/upload-cleanup-scheduler.js +1 -1
- package/src/pro/storage/upload-pending-tracker.js +1 -1
- package/src/pro/websocket/broadcast-helper.js +1 -1
- package/src/pro/websocket/index.js +1 -1
- package/src/pro/websocket/livesync-server.js +1 -1
- package/src/pro/websocket/ws-broadcaster.js +1 -1
- package/src/services/export-service.js +1 -1
- package/src/services/import-service.js +1 -1
- package/src/services/kafkaConsumerService.js +1 -1
- package/src/services/kafkaService.js +1 -1
- package/src/services/messagehubService.js +1 -1
- package/src/services/rabbitmqService.js +1 -1
- package/src/utils/cache-invalidation-registry.js +1 -1
- package/src/utils/cache-manager.js +1 -1
- package/src/utils/component-engine.js +1 -1
- package/src/utils/config-extractor.js +1 -1
- package/src/utils/consumerLogger.js +1 -1
- package/src/utils/context-builder.js +1 -1
- package/src/utils/dashboard-helpers.js +1 -1
- package/src/utils/dateHelper.js +1 -1
- package/src/utils/datetime-formatter.js +1 -1
- package/src/utils/datetime-parser.js +1 -1
- package/src/utils/db-bootstrap.js +1 -1
- package/src/utils/db-mysql.js +1 -1
- package/src/utils/db-oracle.js +1 -1
- package/src/utils/db-sqlite.js +1 -1
- package/src/utils/db.js +1 -1
- package/src/utils/demo-generator.js +1 -1
- package/src/utils/excel-generator.js +1 -1
- package/src/utils/excel-parser.js +1 -1
- package/src/utils/file-watcher.js +1 -1
- package/src/utils/id-generator.js +1 -1
- package/src/utils/idempotency-manager.js +1 -1
- package/src/utils/import-validator.js +1 -1
- package/src/utils/license-client.js +1 -1
- package/src/utils/lock-manager.js +1 -1
- package/src/utils/logger.js +1 -1
- package/src/utils/lookup-resolver.js +1 -1
- package/src/utils/payload-loader.js +1 -1
- package/src/utils/processor-response.js +1 -1
- package/src/utils/rabbitmq.js +1 -1
- package/src/utils/redis-client.js +1 -1
- package/src/utils/redis-helper.js +1 -1
- package/src/utils/request-scope.js +1 -1
- package/src/utils/security-checks.js +1 -1
- package/src/utils/service-resolver.js +1 -1
- package/src/utils/shutdown-coordinator.js +1 -1
- package/src/utils/soft-delete-dashboard-guard.js +1 -1
- package/src/utils/sql-table-extractor.js +1 -1
- package/src/utils/trusted-keys.js +1 -1
- package/src/utils/upload-handler.js +1 -1
- package/src/utils/upsert-builder.js +1 -1
- package/src/utils/workflow-hook-executor.js +1 -1
|
@@ -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
|
|
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
|
|
384
|
-
* - bila cacheConfig.ttl null
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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();}
|