@restforgejs/platform 4.2.8 → 4.3.2

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 (325) hide show
  1. package/SECURITY.md +83 -4
  2. package/bin/sdf-tools.exe +0 -0
  3. package/build-info.json +2 -2
  4. package/cli/consumer-deploy.js +1 -1
  5. package/cli/consumer.js +1 -1
  6. package/generators/cli/dashboard/create.js +4 -1
  7. package/generators/cli/endpoint/create.js +1 -1
  8. package/generators/cli/key/generate.js +2 -1
  9. package/generators/cli/key/revoke.js +2 -1
  10. package/generators/cli/payload/diff.js +3 -2
  11. package/generators/cli/payload/generate.js +3 -2
  12. package/generators/cli/payload/sync.js +3 -2
  13. package/generators/cli/payload/validate.js +3 -2
  14. package/generators/cli/processor/create.js +14 -3
  15. package/generators/cli/project/delete.js +2 -1
  16. package/generators/cli/query/validate.js +3 -2
  17. package/generators/cli/schema/apply.js +3 -2
  18. package/generators/cli/schema/describe.js +3 -2
  19. package/generators/cli/schema/diff.js +3 -2
  20. package/generators/cli/schema/introspect.js +3 -2
  21. package/generators/cli/schema/list.js +3 -2
  22. package/generators/cli/schema/migrate.js +3 -2
  23. package/generators/lib/migration/audit-table-runner.js +213 -215
  24. package/generators/lib/payload/payload-runner.js +1 -1
  25. package/generators/lib/templates/dashboard-catalog.js +1 -437
  26. package/generators/lib/templates/db-connection-env.js +1 -212
  27. package/generators/lib/templates/dbschema-catalog.js +1 -489
  28. package/generators/lib/templates/field-validation-catalog.js +1 -531
  29. package/generators/lib/templates/mysql-template.js +1 -3863
  30. package/generators/lib/templates/oracle-template.js +1 -3915
  31. package/generators/lib/templates/postgres-template.js +1 -5838
  32. package/generators/lib/templates/query-declarative-catalog.js +1 -199
  33. package/generators/lib/templates/sqlite-template.js +1 -3440
  34. package/generators/lib/utils/env-manager.js +6 -0
  35. package/generators/lib/utils/path-validator.js +71 -0
  36. package/generators/lib/validators/payload-validator.js +1 -2
  37. package/integrity-manifest.json +28 -10
  38. package/package.json +11 -3
  39. package/scripts/verify-integrity.js +1 -1
  40. package/server.js +1 -1
  41. package/src/components/handlers/adjust_handler.js +1 -1
  42. package/src/components/handlers/audit_handler.js +1 -1
  43. package/src/components/handlers/delete_handler.js +1 -1
  44. package/src/components/handlers/export_handler.js +1 -1
  45. package/src/components/handlers/import_handler.js +1 -1
  46. package/src/components/handlers/insert_handler.js +1 -1
  47. package/src/components/handlers/update_handler.js +1 -1
  48. package/src/components/handlers/upload_handler.js +1 -1
  49. package/src/components/handlers/workflow_handler.js +1 -1
  50. package/src/components/integrations/webhook.js +1 -1
  51. package/src/consumers/baseConsumer.js +1 -1
  52. package/src/consumers/declarativeMapper.js +1 -1
  53. package/src/consumers/handlers/apiHandler.js +1 -1
  54. package/src/consumers/handlers/consoleHandler.js +1 -1
  55. package/src/consumers/handlers/databaseHandler.js +1 -1
  56. package/src/consumers/handlers/index.js +1 -1
  57. package/src/consumers/handlers/kafkaHandler.js +1 -1
  58. package/src/consumers/index.js +1 -1
  59. package/src/consumers/messageTransformer.js +1 -1
  60. package/src/consumers/validator.js +1 -1
  61. package/src/core/db/dialect/base-dialect.js +1 -1
  62. package/src/core/db/dialect/index.js +1 -1
  63. package/src/core/db/dialect/mysql-dialect.js +1 -1
  64. package/src/core/db/dialect/oracle-dialect.js +1 -1
  65. package/src/core/db/dialect/postgres-dialect.js +1 -1
  66. package/src/core/db/dialect/sqlite-dialect.js +1 -1
  67. package/src/core/db/flatten-helper.js +1 -1
  68. package/src/core/db/query-builder-error.js +1 -1
  69. package/src/core/db/query-builder.js +1 -1
  70. package/src/core/db/relation-helper.js +1 -1
  71. package/src/core/handlers/delete_handler.js +1 -1
  72. package/src/core/handlers/insert_handler.js +1 -1
  73. package/src/core/handlers/update_handler.js +1 -1
  74. package/src/core/models/base-model.js +1 -1
  75. package/src/core/utils/cache-manager.js +1 -1
  76. package/src/core/utils/component-engine.js +1 -1
  77. package/src/core/utils/context-builder.js +1 -1
  78. package/src/core/utils/datetime-formatter.js +1 -1
  79. package/src/core/utils/datetime-parser.js +1 -1
  80. package/src/core/utils/db.js +1 -1
  81. package/src/core/utils/logger.js +1 -1
  82. package/src/core/utils/payload-loader.js +1 -1
  83. package/src/core/utils/security-checks.js +1 -1
  84. package/src/middleware/body-options.js +1 -1
  85. package/src/middleware/cors.js +1 -1
  86. package/src/middleware/idempotency.js +1 -1
  87. package/src/middleware/rate-limiter.js +1 -1
  88. package/src/middleware/request-logger.js +1 -1
  89. package/src/middleware/security-headers.js +1 -1
  90. package/src/models/base-model-mysql.js +1 -1
  91. package/src/models/base-model-oracle.js +1 -1
  92. package/src/models/base-model-sqlite.js +1 -1
  93. package/src/models/base-model.js +1 -1
  94. package/src/pro/caching/redis-client.js +1 -1
  95. package/src/pro/caching/redis-helper.js +1 -1
  96. package/src/pro/consumers/baseConsumer.js +1 -1
  97. package/src/pro/consumers/declarativeMapper.js +1 -1
  98. package/src/pro/consumers/handlers/apiHandler.js +1 -1
  99. package/src/pro/consumers/handlers/consoleHandler.js +1 -1
  100. package/src/pro/consumers/handlers/databaseHandler.js +1 -1
  101. package/src/pro/consumers/handlers/index.js +1 -1
  102. package/src/pro/consumers/handlers/kafkaHandler.js +1 -1
  103. package/src/pro/consumers/index.js +1 -1
  104. package/src/pro/consumers/messageTransformer.js +1 -1
  105. package/src/pro/consumers/validator.js +1 -1
  106. package/src/pro/database/base-model-mysql.js +1 -1
  107. package/src/pro/database/base-model-oracle.js +1 -1
  108. package/src/pro/database/base-model-sqlite.js +1 -1
  109. package/src/pro/database/db-mysql.js +1 -1
  110. package/src/pro/database/db-oracle.js +1 -1
  111. package/src/pro/database/db-sqlite.js +1 -1
  112. package/src/pro/excel/excel-generator.js +1 -1
  113. package/src/pro/excel/excel-parser.js +1 -1
  114. package/src/pro/excel/export-service.js +1 -1
  115. package/src/pro/excel/export_handler.js +1 -1
  116. package/src/pro/excel/import-service.js +1 -1
  117. package/src/pro/excel/import-validator.js +1 -1
  118. package/src/pro/excel/import_handler.js +1 -1
  119. package/src/pro/excel/upsert-builder.js +1 -1
  120. package/src/pro/idgen/idgen-routes.js +1 -1
  121. package/src/pro/integrations/lookup-resolver.js +1 -1
  122. package/src/pro/integrations/upload-handler-v2.js +1 -1
  123. package/src/pro/integrations/upload-handler.js +1 -1
  124. package/src/pro/integrations/webhook.js +1 -1
  125. package/src/pro/locking/lock-routes.js +1 -1
  126. package/src/pro/locking/resource-lock-manager.js +1 -1
  127. package/src/pro/messaging/kafkaConsumerService.js +1 -1
  128. package/src/pro/messaging/kafkaService.js +1 -1
  129. package/src/pro/messaging/messagehubService.js +1 -1
  130. package/src/pro/messaging/rabbitmqService.js +1 -1
  131. package/src/pro/scheduler/job-manager.js +1 -1
  132. package/src/pro/scheduler/job-routes.js +1 -1
  133. package/src/pro/scheduler/job-validator.js +1 -1
  134. package/src/pro/storage/base-storage-provider.js +1 -1
  135. package/src/pro/storage/file-metadata-helper.js +1 -1
  136. package/src/pro/storage/index.js +1 -1
  137. package/src/pro/storage/local-storage-provider.js +1 -1
  138. package/src/pro/storage/s3-storage-provider.js +1 -1
  139. package/src/pro/storage/upload-cleanup-job.js +1 -1
  140. package/src/pro/storage/upload-cleanup-scheduler.js +1 -1
  141. package/src/pro/storage/upload-pending-tracker.js +1 -1
  142. package/src/pro/websocket/broadcast-helper.js +1 -1
  143. package/src/pro/websocket/index.js +1 -1
  144. package/src/pro/websocket/livesync-server.js +1 -1
  145. package/src/pro/websocket/ws-broadcaster.js +1 -1
  146. package/src/services/export-service.js +1 -1
  147. package/src/services/import-service.js +1 -1
  148. package/src/services/kafkaConsumerService.js +1 -1
  149. package/src/services/kafkaService.js +1 -1
  150. package/src/services/messagehubService.js +1 -1
  151. package/src/services/rabbitmqService.js +1 -1
  152. package/src/utils/cache-invalidation-registry.js +1 -1
  153. package/src/utils/cache-manager.js +1 -1
  154. package/src/utils/component-engine.js +1 -1
  155. package/src/utils/config-extractor.js +1 -1
  156. package/src/utils/consumerLogger.js +1 -1
  157. package/src/utils/context-builder.js +1 -1
  158. package/src/utils/dashboard-helpers.js +1 -1
  159. package/src/utils/dateHelper.js +1 -1
  160. package/src/utils/datetime-formatter.js +1 -1
  161. package/src/utils/datetime-parser.js +1 -1
  162. package/src/utils/db-bootstrap.js +1 -1
  163. package/src/utils/db-mysql.js +1 -1
  164. package/src/utils/db-oracle.js +1 -1
  165. package/src/utils/db-sqlite.js +1 -1
  166. package/src/utils/db.js +1 -1
  167. package/src/utils/demo-generator.js +1 -1
  168. package/src/utils/excel-generator.js +1 -1
  169. package/src/utils/excel-parser.js +1 -1
  170. package/src/utils/file-watcher.js +1 -1
  171. package/src/utils/id-generator.js +1 -1
  172. package/src/utils/idempotency-manager.js +1 -1
  173. package/src/utils/import-validator.js +1 -1
  174. package/src/utils/license-client.js +1 -1
  175. package/src/utils/lock-manager.js +1 -1
  176. package/src/utils/logger.js +1 -1
  177. package/src/utils/lookup-resolver.js +1 -1
  178. package/src/utils/payload-loader.js +1 -1
  179. package/src/utils/processor-response.js +1 -1
  180. package/src/utils/rabbitmq.js +1 -1
  181. package/src/utils/redis-client.js +1 -1
  182. package/src/utils/redis-helper.js +1 -1
  183. package/src/utils/request-scope.js +1 -1
  184. package/src/utils/security-checks.js +1 -1
  185. package/src/utils/service-resolver.js +1 -1
  186. package/src/utils/shutdown-coordinator.js +1 -1
  187. package/src/utils/trusted-keys.js +1 -1
  188. package/src/utils/upload-handler.js +1 -1
  189. package/src/utils/upsert-builder.js +1 -1
  190. package/src/utils/workflow-hook-executor.js +1 -1
  191. package/generators/metadata/global.json +0 -58
  192. package/generators/metadata/test-mysql-workbench.json +0 -118
  193. package/generators/metadata/test-mysql.json +0 -56
  194. package/generators/metadata/test-oracle-workbench.json +0 -118
  195. package/generators/metadata/test-oracle.json +0 -56
  196. package/generators/metadata/test-pg-workbench.json +0 -118
  197. package/generators/metadata/test-pg.json +0 -56
  198. package/generators/scripts/obfuscate-source.js +0 -356
  199. package/generators/scripts/validate-catalog.js +0 -430
  200. package/generators/scripts/validate-dbschema-catalog.js +0 -708
  201. package/generators/tests/baseline/mysql/mini_inventory_item/src/models/mini-inventory/item.js +0 -944
  202. package/generators/tests/baseline/mysql/mini_inventory_item/src/modules/mini-inventory/item.js +0 -740
  203. package/generators/tests/baseline/mysql/mini_inventory_item/src/modules/mini-inventory.js +0 -336
  204. package/generators/tests/baseline/oracle/mini_inventory_item/src/models/mini-inventory/item.js +0 -1002
  205. package/generators/tests/baseline/oracle/mini_inventory_item/src/modules/mini-inventory/item.js +0 -740
  206. package/generators/tests/baseline/oracle/mini_inventory_item/src/modules/mini-inventory.js +0 -336
  207. package/generators/tests/baseline/postgres/mini_inventory_item/src/models/mini-inventory/item.js +0 -1333
  208. package/generators/tests/baseline/postgres/mini_inventory_item/src/modules/mini-inventory/item.js +0 -1173
  209. package/generators/tests/baseline/postgres/mini_inventory_item/src/modules/mini-inventory.js +0 -496
  210. package/generators/tests/fixtures/payloads/custom-sensitive.json +0 -27
  211. package/generators/tests/fixtures/payloads/dynamic-search-optout.json +0 -23
  212. package/generators/tests/fixtures/payloads/login-with-password.json +0 -22
  213. package/generators/tests/fixtures/payloads/order-process.json +0 -52
  214. package/generators/tests/fixtures/payloads/with-inline-sql.json +0 -26
  215. package/generators/tests/integration-tahap4b/README.md +0 -145
  216. package/generators/tests/integration-tahap4b/run-concurrent.js +0 -77
  217. package/generators/tests/integration-tahap4b/seed.sql +0 -53
  218. package/generators/tests/integration-tahap4b/verify.sql +0 -110
  219. package/generators/tests/unit/cli/create-dashboard.test.js +0 -505
  220. package/generators/tests/unit/cli/create-processor.test.js +0 -319
  221. package/generators/tests/unit/cli/dispatch-dashboard.test.js +0 -149
  222. package/generators/tests/unit/lib/dashboard-generator.test.js +0 -895
  223. package/generators/tests/unit/lib/dashboard-validator.test.js +0 -354
  224. package/generators/tests/unit/lib/dbschema-kit/apply-executor.test.js +0 -437
  225. package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-introspect.test.js +0 -393
  226. package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-generate-ddl.test.js +0 -104
  227. package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-init.test.js +0 -119
  228. package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-list.test.js +0 -48
  229. package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-migrate.test.js +0 -175
  230. package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-validate.test.js +0 -102
  231. package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-models.test.js +0 -43
  232. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/all-schemas-listing.js +0 -84
  233. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/connection-error.js +0 -13
  234. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/empty.js +0 -12
  235. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/multi-schema.js +0 -124
  236. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/single-schema-inventory.js +0 -64
  237. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/two-tables.js +0 -66
  238. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/migrate-stubs/connection-error.js +0 -9
  239. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/migrate-stubs/partial.js +0 -29
  240. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/migrate-stubs/rollback.js +0 -26
  241. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/migrate-stubs/success.js +0 -43
  242. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/multi-schema/audit/events.js +0 -18
  243. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/multi-schema/inventory/products.js +0 -9
  244. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/multi-schema/users.js +0 -8
  245. package/generators/tests/unit/lib/dbschema-kit/connection.test.js +0 -112
  246. package/generators/tests/unit/lib/dbschema-kit/ddl-generator.test.js +0 -205
  247. package/generators/tests/unit/lib/dbschema-kit/define-model.test.js +0 -56
  248. package/generators/tests/unit/lib/dbschema-kit/dialect/index.test.js +0 -46
  249. package/generators/tests/unit/lib/dbschema-kit/dialect/mysql.test.js +0 -126
  250. package/generators/tests/unit/lib/dbschema-kit/dialect/oracle.test.js +0 -126
  251. package/generators/tests/unit/lib/dbschema-kit/dialect/postgres.test.js +0 -131
  252. package/generators/tests/unit/lib/dbschema-kit/dialect/sqlite.test.js +0 -126
  253. package/generators/tests/unit/lib/dbschema-kit/driver-loader.test.js +0 -93
  254. package/generators/tests/unit/lib/dbschema-kit/emitters/create-index.test.js +0 -173
  255. package/generators/tests/unit/lib/dbschema-kit/emitters/create-table.test.js +0 -376
  256. package/generators/tests/unit/lib/dbschema-kit/emitters/drop-table.test.js +0 -78
  257. package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/invalid-dialect.env +0 -6
  258. package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/missing-dialect.env +0 -5
  259. package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/missing-host.env +0 -5
  260. package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/oracle-valid.env +0 -6
  261. package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/postgres-valid.env +0 -7
  262. package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/sqlite-valid.env +0 -2
  263. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/category.js +0 -11
  264. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/item_product.js +0 -11
  265. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/stock_inbound.js +0 -24
  266. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/stock_inbound_item.js +0 -28
  267. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/supplier.js +0 -9
  268. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/warehouse.js +0 -9
  269. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-invalid/orphan.js +0 -17
  270. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/master/category.js +0 -11
  271. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/master/item_product.js +0 -11
  272. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/master/supplier.js +0 -9
  273. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/master/warehouse.js +0 -9
  274. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/transactions/stock_inbound.js +0 -24
  275. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/transactions/stock_inbound_item.js +0 -28
  276. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/multi-schema/audit/events.js +0 -18
  277. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/multi-schema/inventory/products.js +0 -9
  278. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/multi-schema/public/users.js +0 -9
  279. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/duplicate-subfolder/extra/category.js +0 -8
  280. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/duplicate-subfolder/master/category.js +0 -8
  281. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/duplicate-tablename/bar.js +0 -8
  282. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/duplicate-tablename/foo.js +0 -8
  283. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/empty-folder/README.md +0 -1
  284. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/invalid-export/plain.js +0 -3
  285. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/invalid-schema/bad.js +0 -6
  286. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/legacy-pattern/legacy.js +0 -12
  287. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/multi-schema-distinct/audit/products.js +0 -9
  288. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/multi-schema-distinct/inventory/products.js +0 -9
  289. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/multi-schema-duplicate/a/products.js +0 -8
  290. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/multi-schema-duplicate/b/products.js +0 -8
  291. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/nested-deep/a/b/c/deep_table.js +0 -8
  292. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/.hidden/ignored.js +0 -7
  293. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/master/category.js +0 -8
  294. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/master/supplier.js +0 -8
  295. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/transactions/stock_inbound.js +0 -8
  296. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/transactions/stock_inbound_item.js +0 -8
  297. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/valid-multiple/category.js +0 -8
  298. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/valid-multiple/item_product.js +0 -9
  299. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/valid-single/category.js +0 -8
  300. package/generators/tests/unit/lib/dbschema-kit/integration.test.js +0 -217
  301. package/generators/tests/unit/lib/dbschema-kit/introspect-mapper.test.js +0 -403
  302. package/generators/tests/unit/lib/dbschema-kit/ir-builder.test.js +0 -390
  303. package/generators/tests/unit/lib/dbschema-kit/loader.test.js +0 -128
  304. package/generators/tests/unit/lib/dbschema-kit/naming.test.js +0 -170
  305. package/generators/tests/unit/lib/dbschema-kit/parser/shorthand-parser.test.js +0 -237
  306. package/generators/tests/unit/lib/dbschema-kit/schema-printer.test.js +0 -251
  307. package/generators/tests/unit/lib/dbschema-kit/statement-modifier.test.js +0 -105
  308. package/generators/tests/unit/lib/dbschema-kit/statement-splitter.test.js +0 -165
  309. package/generators/tests/unit/lib/dbschema-kit/topological-sort.test.js +0 -135
  310. package/generators/tests/unit/lib/dbschema-kit/validator/check-compatibility-validator.test.js +0 -373
  311. package/generators/tests/unit/lib/dbschema-kit/validator/circular-relation-validator.test.js +0 -454
  312. package/generators/tests/unit/lib/dbschema-kit/validator/cross-model-validator.test.js +0 -512
  313. package/generators/tests/unit/lib/dbschema-kit/validator/enhanced-validate-integration.test.js +0 -390
  314. package/generators/tests/unit/lib/dbschema-kit/validator/naming-convention-validator.test.js +0 -306
  315. package/generators/tests/unit/lib/dbschema-kit/validator/schema-validator.test.js +0 -443
  316. package/generators/tests/unit/lib/dbschema-kit/validator/type-compatibility-validator.test.js +0 -440
  317. package/generators/tests/unit/lib/dbschema-kit/validator/validator-reporter.test.js +0 -172
  318. package/generators/tests/unit/lib/metadata-manager-dashboard.test.js +0 -256
  319. package/generators/tests/unit/lib/payload-validator-fieldpolicy.test.js +0 -240
  320. package/generators/tests/unit/lib/processor-validation-generator.test.js +0 -300
  321. package/generators/tests/unit/lib/sensitive-field-masker.test.js +0 -170
  322. package/generators/tests/unit/lib/sql-table-extractor.test.js +0 -119
  323. package/scripts/generate-integrity-manifest.js +0 -124
  324. package/scripts/snapshot-cli-contracts.js +0 -194
  325. package/scripts/verify-publish.js +0 -56
@@ -1,437 +1 @@
1
- /**
2
- * Authoritative source for dashboard-catalog content.
3
- * Used by:
4
- * - cli/dashboard-catalog.js (CLI introspection command)
5
- * - (future) MCP server tool 'codegen_get_dashboard_catalog'
6
- *
7
- * SINGLE SOURCE OF TRUTH: Modify only this file when adding/changing
8
- * dashboard payload schema, widget spec, param spec, scalar collapse rules,
9
- * naming/URL/file/placeholder conventions. Keep in sync with:
10
- * - feat-dashboard.md spec
11
- * - dashboard-validator.js implementation
12
- *
13
- * Reference document (human-readable): feat-dashboard.md
14
- * Public documentation URL: https://restforge.dev/docs/server/query-data/dashboard
15
- *
16
- * @module lib/templates/dashboard-catalog
17
- */
18
-
19
- // ============================================================================
20
- // SHARED CONSTANTS (single source — referenced multiple places below)
21
- // ============================================================================
22
-
23
- const FORBIDDEN_FRONTEND_FIELDS = ['widgetType', 'layout', 'title', 'subtitle', 'color'];
24
- const ALLOWED_PARAM_TYPES = ['string', 'number', 'boolean', 'date'];
25
-
26
- const FRONTEND_CONCERN_REASONS = {
27
- widgetType: 'Visual variant (donut, bar, pie, area) is a frontend rendering concern (separation of concerns).',
28
- layout: 'Layout is a frontend rendering concern.',
29
- title: 'UI label is a frontend rendering concern.',
30
- subtitle: 'UI label is a frontend rendering concern.',
31
- color: 'Visual color is a frontend rendering concern.'
32
- };
33
-
34
- // ============================================================================
35
- // PAYLOAD SHAPE
36
- // ============================================================================
37
-
38
- const PAYLOAD_SHAPE = {
39
- discriminator: {
40
- field: 'widgets',
41
- presentMeans: 'dashboard payload',
42
- absentMeans: 'Not a dashboard payload (likely CRUD with tableName, or invalid)',
43
- conflictsWith: 'tableName',
44
- conflictRationale: "A payload with both 'widgets' and 'tableName' is rejected by DashboardValidator. Pick one shape."
45
- },
46
- topLevelAllowed: [
47
- {
48
- name: 'widgets',
49
- type: 'array',
50
- required: true,
51
- minItems: 1,
52
- description: 'List of widget definitions. Order is informational only (response keys are by widget id, not array index).'
53
- },
54
- {
55
- name: 'params',
56
- type: 'object',
57
- required: false,
58
- description: 'Parameter contract for the dashboard. Each key is a param name; values describe type/required/default. Placeholders inside widget SQL must reference declared param names.'
59
- },
60
- {
61
- name: 'cache',
62
- type: 'object',
63
- required: false,
64
- description: "Optional cache configuration. See cacheSpec for details."
65
- }
66
- ],
67
- topLevelForbidden: [
68
- {
69
- name: 'tableName',
70
- category: 'shape-conflict',
71
- reason: "Reserved for CRUD payloads. A dashboard payload must declare 'widgets' instead."
72
- },
73
- ...FORBIDDEN_FRONTEND_FIELDS.map(name => ({
74
- name,
75
- category: 'frontend-concern',
76
- reason: FRONTEND_CONCERN_REASONS[name]
77
- }))
78
- ]
79
- };
80
-
81
- // ============================================================================
82
- // WIDGET SPEC
83
- // ============================================================================
84
-
85
- const WIDGET_SPEC = {
86
- requiredFields: [
87
- {
88
- name: 'id',
89
- type: 'string',
90
- constraint: 'non-empty, unique across widgets in the same payload',
91
- description: 'Widget identifier; used as the response key in the dashboard envelope.'
92
- }
93
- ],
94
- exclusiveQueryFields: {
95
- rule: "A widget MUST declare exactly one of: 'query' OR 'queries'. Both or neither is rejected.",
96
- options: [
97
- {
98
- name: 'query',
99
- type: 'string',
100
- format: 'file:relative/path/to/query.sql',
101
- description: 'Single SQL query for the widget.',
102
- responseShape: 'Always { items: [...] } regardless of SQL result shape.'
103
- },
104
- {
105
- name: 'queries',
106
- type: 'object',
107
- format: 'key→file:relative/path/to/query.sql',
108
- minKeys: 1,
109
- description: 'Multi-SQL widget. Each key becomes a key in the response object.',
110
- responseShape: 'Per-key based on scalarCollapseRules below.'
111
- }
112
- ]
113
- },
114
- forbiddenFields: FORBIDDEN_FRONTEND_FIELDS
115
- };
116
-
117
- // ============================================================================
118
- // PARAM SPEC
119
- // ============================================================================
120
-
121
- const PARAM_SPEC = {
122
- container: "top-level 'params' object",
123
- keyConvention: 'Param name must match the placeholder regex `[a-zA-Z_][a-zA-Z0-9_]*` (alphanumeric + underscore, must start with letter or underscore).',
124
- perEntryFields: [
125
- {
126
- name: 'type',
127
- required: true,
128
- allowedValues: ALLOWED_PARAM_TYPES,
129
- description: 'Param data type. Validates request body and shapes runtime parameter binding.'
130
- },
131
- {
132
- name: 'required',
133
- required: false,
134
- type: 'boolean',
135
- default: false,
136
- description: 'When true, the request body MUST include this param (otherwise 400).'
137
- },
138
- {
139
- name: 'default',
140
- required: false,
141
- type: "any (must be compatible with declared 'type')",
142
- description: "Default value applied when the request omits this param. Validator does NOT strictly type-check default; runtime is responsible for compatibility."
143
- }
144
- ]
145
- };
146
-
147
- // ============================================================================
148
- // SCALAR COLLAPSE RULES
149
- // ============================================================================
150
-
151
- const SCALAR_COLLAPSE_RULES = [
152
- {
153
- appliesTo: 'widget.query (singular)',
154
- rule: 'Always wrap as { items: [...] } regardless of SQL result shape.',
155
- exampleSqlShape: 'any (1 row × 1 col, N rows × M cols, etc.)',
156
- exampleResponse: '"shopping_categories": { "items": [{ "name": "Lands" }, { "name": "Houses" }] }'
157
- },
158
- {
159
- appliesTo: 'widget.queries.<key> with SQL returning 1 row × 1 column',
160
- rule: 'Collapse to scalar primitive (the value of the single column).',
161
- exampleSqlShape: "1 row × 1 col, output column 'value'",
162
- exampleResponse: '"value": "69700"'
163
- },
164
- {
165
- appliesTo: 'widget.queries.<key> with SQL returning 1 row × multiple columns',
166
- rule: 'Collapse to object whose keys are SQL column names (lowercased).',
167
- exampleSqlShape: "1 row × 2 cols, output columns 'direction', 'pct'",
168
- exampleResponse: '"trend": { "direction": "up", "pct": "2.2" }'
169
- },
170
- {
171
- appliesTo: 'widget.queries.<key> with SQL returning N rows',
172
- rule: 'Return as array of objects (no collapse).',
173
- exampleSqlShape: 'N rows × M cols',
174
- exampleResponse: '"items": [{ "label": "Shoes", "value": "7660" }, ...]'
175
- }
176
- ];
177
-
178
- // ============================================================================
179
- // COMMON WIDGET PATTERNS
180
- // ============================================================================
181
-
182
- const COMMON_WIDGET_PATTERNS = [
183
- {
184
- id: 'metric_donut_breakdown',
185
- name: 'Metric + Donut Breakdown',
186
- useCase: "Headline metric with trend chip and breakdown across categories. Suitable for widgets like 'Expected Earnings' that show total value, percentage change, and per-category contribution.",
187
- payloadShape: {
188
- id: '<widget_id>',
189
- queries: {
190
- value: 'file:query/<path>/value.sql',
191
- trend: 'file:query/<path>/trend.sql',
192
- items: 'file:query/<path>/breakdown.sql'
193
- }
194
- },
195
- sqlShapesPerKey: [
196
- {
197
- key: 'value',
198
- shape: '1 row × 1 column',
199
- outputColumns: ['value'],
200
- collapseRule: 'scalar primitive'
201
- },
202
- {
203
- key: 'trend',
204
- shape: '1 row × 2 columns',
205
- outputColumns: ['direction', 'pct'],
206
- collapseRule: 'object'
207
- },
208
- {
209
- key: 'items',
210
- shape: 'N rows × 2 columns',
211
- outputColumns: ['label', 'value'],
212
- collapseRule: 'array of objects'
213
- }
214
- ],
215
- responseShape: {
216
- value: '"69700"',
217
- trend: '{ "direction": "up", "pct": "2.2" }',
218
- items: '[{ "label": "Shoes", "value": "7660" }, { "label": "Gaming", "value": "2820" }, { "label": "Others", "value": "45257" }]'
219
- },
220
- referenceWidgetId: 'expected_earnings',
221
- socNotes: "Frontend determines donut/pie variant, color per category, and label order. If per-category percentage is needed for the donut arc, frontend computes it from items[i].value / sum(items[*].value). No need to send 'pct' from backend unless the figure is a stable business calculation independent of visual rendering."
222
- },
223
- {
224
- id: 'metric_sparkline',
225
- name: 'Metric + Sparkline',
226
- useCase: "Headline metric with trend chip and sparkline mini-chart for short windows (7 days, 12 months, etc.). Suitable for widgets like 'Average Daily Sales'.",
227
- payloadShape: {
228
- id: '<widget_id>',
229
- queries: {
230
- value: 'file:query/<path>/value.sql',
231
- trend: 'file:query/<path>/trend.sql',
232
- points: 'file:query/<path>/points.sql'
233
- }
234
- },
235
- sqlShapesPerKey: [
236
- {
237
- key: 'value',
238
- shape: '1 row × 1 column',
239
- outputColumns: ['value'],
240
- collapseRule: 'scalar primitive'
241
- },
242
- {
243
- key: 'trend',
244
- shape: '1 row × 2 columns',
245
- outputColumns: ['direction', 'pct'],
246
- collapseRule: 'object'
247
- },
248
- {
249
- key: 'points',
250
- shape: 'N rows × 2 columns',
251
- outputColumns: ['period', 'value'],
252
- collapseRule: 'array of objects'
253
- }
254
- ],
255
- responseShape: {
256
- value: '"2420"',
257
- trend: '{ "direction": "up", "pct": "2.6" }',
258
- points: '[{ "period": "2026-04-24", "value": "1850" }, ... ]'
259
- },
260
- referenceWidgetId: 'avg_daily_sales',
261
- socNotes: "Sparkline libraries (ApexCharts, Chartist, etc.) typically need a plain number array. Frontend maps points.map(p => p.value). The 'period' field stays for tooltip and gap-resilience against missing days. Use generate_series in SQL to ensure consistent row count even for days with no transactions."
262
- },
263
- {
264
- id: 'metric_progress_to_goal',
265
- name: 'Metric + Progress to Goal',
266
- useCase: "Headline metric with trend chip and progress bar against a period target. Suitable for widgets like 'Orders This Month'.",
267
- payloadShape: {
268
- id: '<widget_id>',
269
- queries: {
270
- value: 'file:query/<path>/current.sql',
271
- trend: 'file:query/<path>/trend.sql',
272
- target: 'file:query/<path>/target.sql'
273
- }
274
- },
275
- sqlShapesPerKey: [
276
- {
277
- key: 'value',
278
- shape: '1 row × 1 column',
279
- outputColumns: ['value (or current)'],
280
- collapseRule: 'scalar primitive'
281
- },
282
- {
283
- key: 'trend',
284
- shape: '1 row × 2 columns',
285
- outputColumns: ['direction', 'pct'],
286
- collapseRule: 'object'
287
- },
288
- {
289
- key: 'target',
290
- shape: '1 row × 1 column',
291
- outputColumns: ['target'],
292
- collapseRule: 'scalar primitive'
293
- }
294
- ],
295
- responseShape: {
296
- value: '"1836"',
297
- trend: '{ "direction": "down", "pct": "2.2" }',
298
- target: '"2884"'
299
- },
300
- referenceWidgetId: 'orders_this_month',
301
- socNotes: "Frontend computes to_goal = target - value and pct = round(value / target * 100) for the progress bar. Visual width is presentational and must NOT live in the backend payload. If progress involves complex business rules (e.g. exclude weekends, prorated workdays), use a single multi-column query so 'pct' is a stable business fact rather than visual width."
302
- }
303
- ];
304
-
305
- // ============================================================================
306
- // NAMING CONVENTION
307
- // ============================================================================
308
-
309
- const NAMING_CONVENTION = {
310
- dashboardName: {
311
- constraint: "MUST start with 'dash-' prefix",
312
- minLength: 6,
313
- maxLength: 50,
314
- regex: '^dash-[a-zA-Z0-9_-]+$',
315
- examples: ['dash-sales', 'dash-inbound', 'dash-author-stats'],
316
- rationale: 'The prefix becomes part of the URL segment. The reserved scheme keeps dashboard endpoints visually distinct from CRUD endpoints in the URL space and allows future routing differentiation.'
317
- }
318
- };
319
-
320
- // ============================================================================
321
- // URL PATTERN
322
- // ============================================================================
323
-
324
- const URL_PATTERN = {
325
- method: 'POST',
326
- path: '/api/{project}/{name}/dashboard',
327
- exampleFull: 'POST /api/mini-inventory/dash-inbound/dashboard',
328
- requestBodyShape: {
329
- params: 'object — values for declared params (validated against params contract; missing required → 400, type mismatch → 400)',
330
- widgets: 'array<string>, optional — subset of widget IDs to execute. Omit to execute all declared widgets.'
331
- },
332
- responseShape: {
333
- envelope: '{ success: boolean, data: { <widgetId>: <perWidgetResponse>, ... } }',
334
- perWidgetResponse: "Determined by scalarCollapseRules. Failed widgets produce { error: '...' } block with top-level success still true (one widget failure does NOT fail the dashboard)."
335
- }
336
- };
337
-
338
- // ============================================================================
339
- // FILE REFERENCE CONVENTION
340
- // ============================================================================
341
-
342
- const FILE_REFERENCE_CONVENTION = {
343
- format: 'file:relative/path/to/query.sql',
344
- pathRelativeTo: 'payload JSON file location',
345
- fileExtensionPolicy: 'free; .sql recommended for editor highlight',
346
- resolvedAt: 'generation time (NOT runtime)',
347
- embedStrategy: 'SQL file content is embedded as JavaScript template literal inside the generated module file. Runtime performs zero disk I/O per request — all SQL is in memory after module load.',
348
- implication: "Updating an SQL file requires regenerating the dashboard module ('codegen_create_dashboard') for changes to take effect."
349
- };
350
-
351
- // ============================================================================
352
- // PLACEHOLDER CONVENTION
353
- // ============================================================================
354
-
355
- const PLACEHOLDER_CONVENTION = {
356
- format: ':paramName',
357
- regex: '(?<!:):([a-zA-Z_][a-zA-Z0-9_]*)',
358
- regexNotes: "Negative lookbehind prevents matching '::' (Postgres cast syntax) as a placeholder.",
359
- scanScope: "All widget SQL — both 'query' (singular) and every 'queries.<key>'.",
360
- constraint: 'Every placeholder used in SQL MUST be declared in \'params\'. Validator throws Error with message format: "Widget \'<id>\' query \'<label>\' uses undeclared placeholder \':<token>\' (declare in \'params\')".',
361
- exampleSql: 'SELECT * FROM stock_inbound WHERE EXTRACT(YEAR FROM inbound_date) = :year',
362
- exampleParamDeclaration: '{ "params": { "year": { "type": "number", "required": true } } }'
363
- };
364
-
365
- // ============================================================================
366
- // CACHE SPEC
367
- // ============================================================================
368
-
369
- const CACHE_SPEC = {
370
- container: "top-level 'cache' object",
371
- optional: true,
372
- rationale: "Dashboard endpoint may opt-in to Redis-based cache. Pattern follows processor cache (see feat-cache.md). Cache scope is the full response envelope; one cache entry per (params + widgets[] subset) combination.",
373
- fields: [
374
- {
375
- name: 'enabled',
376
- type: 'boolean',
377
- required: true,
378
- description: 'Toggle cache feature for this dashboard.'
379
- },
380
- {
381
- name: 'ttl',
382
- type: 'number',
383
- required: false,
384
- constraint: '>= 0 (seconds)',
385
- default: 'inherits CACHE_TTL env',
386
- description: 'Time-to-live in seconds. 0 effectively disables cache for this entry.'
387
- },
388
- {
389
- name: 'invalidates',
390
- type: 'array<string>',
391
- required: false,
392
- default: '[]',
393
- description: 'List of CRUD table names that, when written, will trigger invalidation of this dashboard cache.'
394
- }
395
- ],
396
- validation: {
397
- sqlCrossReference: 'When cache.enabled === true and invalidates is non-empty: validator extracts table candidates from widget SQL (regex FROM/JOIN), cross-references with metadata/{project}.json (endpoints[*].tableName where type === "module"), and asserts equality of expected vs declared sets. Mismatches are reported per category (missing, extra, unmatched).',
398
- errorOn: [
399
- 'Table appears in SQL AND in metadata project, but missing from invalidates (cache stale risk)',
400
- 'Table declared in invalidates, but not detected in any widget SQL (typo or dead entry)'
401
- ],
402
- warningOn: [
403
- 'Table detected in SQL, but not registered as CRUD endpoint in metadata project (likely a view, CTE alias, or cross-project table — cascade will not fire)'
404
- ]
405
- }
406
- };
407
-
408
- // ============================================================================
409
- // FINAL CATALOG
410
- // ============================================================================
411
-
412
- const DOCUMENTATION_URL = 'https://restforge.dev/docs/server/query-data/dashboard';
413
-
414
- const DASHBOARD_CATALOG = {
415
- schemaVersion: '1.0',
416
- source: 'dashboard-catalog',
417
- summary: {
418
- totalAllowedTopLevelFields: PAYLOAD_SHAPE.topLevelAllowed.length,
419
- totalForbiddenFrontendFields: FORBIDDEN_FRONTEND_FIELDS.length,
420
- totalParamTypes: ALLOWED_PARAM_TYPES.length,
421
- totalScalarCollapseRules: SCALAR_COLLAPSE_RULES.length,
422
- totalCommonWidgetPatterns: COMMON_WIDGET_PATTERNS.length
423
- },
424
- payloadShape: PAYLOAD_SHAPE,
425
- widgetSpec: WIDGET_SPEC,
426
- paramSpec: PARAM_SPEC,
427
- scalarCollapseRules: SCALAR_COLLAPSE_RULES,
428
- commonWidgetPatterns: COMMON_WIDGET_PATTERNS,
429
- namingConvention: NAMING_CONVENTION,
430
- urlPattern: URL_PATTERN,
431
- fileReferenceConvention: FILE_REFERENCE_CONVENTION,
432
- placeholderConvention: PLACEHOLDER_CONVENTION,
433
- cacheSpec: CACHE_SPEC,
434
- documentationUrl: DOCUMENTATION_URL
435
- };
436
-
437
- module.exports = { DASHBOARD_CATALOG };
1
+ function a0_0x163f(_0x2fff76,_0x3dad32){_0x2fff76=_0x2fff76-0x160;const _0x38bfad=a0_0x38bf();let _0x163fb2=_0x38bfad[_0x2fff76];if(a0_0x163f['SAJaCv']===undefined){var _0xe116b1=function(_0x1825a5){const _0x2aec6a='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x2b8fe5='',_0x2d0f21='';for(let _0x53cfad=0x0,_0x13283a,_0xeaa967,_0x5b14df=0x0;_0xeaa967=_0x1825a5['charAt'](_0x5b14df++);~_0xeaa967&&(_0x13283a=_0x53cfad%0x4?_0x13283a*0x40+_0xeaa967:_0xeaa967,_0x53cfad++%0x4)?_0x2b8fe5+=String['fromCharCode'](0xff&_0x13283a>>(-0x2*_0x53cfad&0x6)):0x0){_0xeaa967=_0x2aec6a['indexOf'](_0xeaa967);}for(let _0x571693=0x0,_0x279c6a=_0x2b8fe5['length'];_0x571693<_0x279c6a;_0x571693++){_0x2d0f21+='%'+('00'+_0x2b8fe5['charCodeAt'](_0x571693)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x2d0f21);};a0_0x163f['pKrfaV']=_0xe116b1,a0_0x163f['kVtkIC']={},a0_0x163f['SAJaCv']=!![];}const _0x2e82a7=_0x38bfad[0x0],_0x3ae501=_0x2fff76+_0x2e82a7,_0x217380=a0_0x163f['kVtkIC'][_0x3ae501];return!_0x217380?(_0x163fb2=a0_0x163f['pKrfaV'](_0x163fb2),a0_0x163f['kVtkIC'][_0x3ae501]=_0x163fb2):_0x163fb2=_0x217380,_0x163fb2;}function a0_0x38bf(){const _0x444f17=['Cgf5Bg9Hzcbku09oigzPBguGBg9JyxrPB24','rgfZAgjVyxjKigvUzhbVAw50ig1HEsbVChqTAw4GDg8GuMvKAxmTyMfZzwqGy2fJAguUifbHDhrLCM4GzM9SBg93CYbWCM9JzxnZB3iGy2fJAguGkhnLzsbMzwf0lwnHy2HLlM1Kks4Gq2fJAguGC2nVCguGAxmGDgHLigz1BgWGCMvZCg9UC2uGzw52zwXVCgu7ig9UzsbJywnOzsbLBNrYEsbWzxiGkhbHCMfTCYaRihDPzgDLDhnBxsbZDwjZzxqPignVBwjPBMf0Aw9UlG','w3SGiNbLCMLVzci6iciYmdi2lta0lti0iIWGiNzHBhvLiJOGiJe4ntaIih0Sic4UlIbD','mti1nZa5mMvUvxLRDa','twv0CMLJicSGuhjVz3jLC3mGDg8Gr29HBa','yxjYyxKGB2yGB2jQzwn0CW','msbYB3CGW5CGmIbJB2X1Bw5Z','A2v54OAszMLSztPYzwXHDgL2zs9WyxrOl3rVl3f1zxj5lNnXBa','l2fWAs97ChjVAMvJDh0VE25HBwv9l2rHC2HIB2fYza','DgfYz2v0','yxjYyxK8C3rYAw5NpIWGB3b0Aw9UywWG4Ocuihn1yNnLDcbVzIb3AwrNzxqGsurZihrVigv4zwn1DguUie9TAxqGDg8GzxHLy3v0zsbHBgWGzgvJBgfYzwqGD2LKz2v0CY4','zgvMyxvSDa','pJ0GmcaOC2vJB25KCYK','yM9VBgvHBG','mti0ntyYowPwwvnMEa','zw5HyMXLza','zMLSztPXDwvYEs88Cgf0Ad4VDhjLBMqUC3fS','v2LKz2v0igLKzw50AwzPzxi7ihvZzwqGyxmGDgHLihjLC3bVBNnLigTLEsbPBIb0AguGzgfZAgjVyxjKigvUDMvSB3bLlG','yxzNx2rHAwX5x3nHBgvZ','zMLSztPXDwvYEs88Cgf0Ad4VCg9PBNrZlNnXBa','sgvHzgXPBMuGBwv0CMLJihDPDgGGDhjLBMqGy2HPCcbHBMqGChjVz3jLC3mGyMfYigfNywLUC3qGysbWzxjPB2qGDgfYz2v0lIbtDwL0ywjSzsbMB3iGD2LKz2v0CYbSAwTLicDpCMrLCNmGvgHPCYbnB250AcCU','ugfYyw0Gzgf0ysb0ExbLlIbwywXPzgf0zxmGCMvXDwvZDcbIB2r5igfUzcbZAgfWzxmGCNvUDgLTzsbWyxjHBwv0zxiGyMLUzgLUzY4','mJa3u0HlyxPI','Bgf5B3v0','EYaIzgLYzwn0Aw9UiJOGiMrVD24IlcaICgn0iJOGiJiUmIiGFq','Cg9PBNrZ','C2nHBgfYihbYAw1PDgL2zq','uMvZzxj2zwqGzM9YiensvuqGCgf5Bg9HzhmUieeGzgfZAgjVyxjKihbHEwXVywqGBxvZDcbKzwnSyxjLicD3AwrNzxrZjYbPBNn0zwfKlG','rNjVBNrLBMqGy29TChv0zxmGDg9Fz29HBca9ihrHCMDLDcaTihzHBhvLigfUzcbWy3qGpsbYB3vUzcH2ywX1zsaVihrHCMDLDcaQideWmcKGzM9YihrOzsbWCM9NCMvZCYbIyxiUifzPC3vHBcb3Awr0AcbPCYbWCMvZzw50yxrPB25HBcbHBMqGBxvZDcbot1qGBgL2zsbPBIb0AguGyMfJA2vUzcbWyxLSB2fKlIbjzIbWCM9NCMvZCYbPBNzVBhzLCYbJB21WBgv4igj1C2LUzxnZihj1BgvZicHLlMCUigv4y2X1zguGD2vLA2vUzhmSihbYB3jHDgvKihDVCMTKyxLZksWGDxnLigeGC2LUz2XLig11BhrPlwnVBhvTBIbXDwvYEsbZBYaNCgn0jYbPCYbHihn0ywjSzsbIDxnPBMvZCYbMywn0ihjHDgHLCIb0AgfUihzPC3vHBcb3Awr0Ac4','ue9tvcaVyxbPl21PBMKTAw52zw50B3j5l2rHC2GTAw5IB3vUzc9KyxnOyM9HCMq','q29SBgfWC2uGDg8GC2nHBgfYihbYAw1PDgL2zsaODgHLihzHBhvLig9MihrOzsbZAw5NBguGy29SDw1Uks4','zNjLztSGlNnXBcbYzwnVBw1LBMrLzcbMB3iGzwrPDg9YigHPz2HSAwDODa','C2HHCguTy29UzMXPy3q','C3rYAw5N','BgvUz3rO','zMLSztPYzwXHDgL2zs9WyxrOl3rVl3f1zxj5lNnXBa','CxvLCMLLCW','vuKGBgfIzwWGAxmGysbMCM9UDgvUzcbYzw5KzxjPBMCGy29Uy2vYBI4','tIbYB3DZimoxidiGy29SDw1UCW','ugfYyw1LDgvYignVBNrYywn0igzVCIb0AguGzgfZAgjVyxjKlIbfywnOigTLEsbPCYbHihbHCMfTig5HBwu7ihzHBhvLCYbKzxnJCMLIzsb0ExbLl3jLCxvPCMvKl2rLzMf1BhqUifbSywnLAg9SzgvYCYbPBNnPzguGD2LKz2v0ifnrtcbTDxn0ihjLzMvYzw5JzsbKzwnSyxjLzcbWyxjHBsbUyw1LCY4','u1fmigzPBguGy29UDgvUDcbPCYbLBwjLzgrLzcbHCYbkyxzHu2nYAxb0ihrLBxbSyxrLigXPDgvYywWGAw5ZAwrLihrOzsbNzw5LCMf0zwqGBw9KDwXLigzPBguUifj1BNrPBwuGCgvYzM9YBxmGEMvYBYbKAxnRieKVtYbWzxiGCMvXDwvZDcdIGjqGywXSifnrtcbPCYbPBIbTzw1VCNKGywz0zxiGBw9KDwXLigXVywqU','u3bHCMTSAw5LigXPyNjHCMLLCYaOqxbLEenOyxj0CYWGq2HHCNrPC3qSigv0yY4Pihr5CgLJywXSEsbUzwvKigeGCgXHAw4GBNvTyMvYigfYCMf5lIbgCM9UDgvUzcbTyxbZihbVAw50CY5TyxaOCca9pIbWlNzHBhvLks4GvgHLicDWzxjPB2qNigzPzwXKihn0yxLZigzVCIb0B29SDgLWigfUzcbNyxaTCMvZAwXPzw5JzsbHz2fPBNn0ig1PC3nPBMCGzgf5CY4GvxnLigDLBMvYyxrLx3nLCMLLCYbPBIbtuuWGDg8Gzw5ZDxjLignVBNnPC3rLBNqGCM93ignVDw50igv2zw4GzM9YigrHExmGD2L0AcbUBYb0CMfUC2fJDgLVBNmU','Dg9WlwXLDMvSicDWyxjHBxmNig9IAMvJDa','DhjLBMq','zgLYzwn0Aw9U','iNzHBhvLiJOGiJy5nZaWiG','mJKWotKXmMrqEu1UBa','CgvYAw9K','zgfZAgjVyxjKihbHEwXVywq','ndC3otC1mgr6DfnRsG','vMLZDwfSignVBg9YigLZigeGzNjVBNrLBMqGCMvUzgvYAw5NignVBMnLCM4U','D2LKz2v0CW','Cgn0','C3vIDgL0Bgu','B2jQzwn0','yxjYyxK8C3rYAw5NpG','B3jKzxjZx3rOAxnFBw9UDgG','zgf0zq','v2HLBIb0CNvLlcb0AguGCMvXDwvZDcbIB2r5ie1vu1qGAw5JBhvKzsb0AgLZihbHCMfTicHVDgHLCNDPC2uGndaWks4','ntiWnZG1nLLfy2P5rW','CxvLCNK','EYaIzgLYzwn0Aw9UiJOGiNvWiIWGiNbJDci6iciYlJyIih0','u0vmrunuicOGrLjptsbZDg9JA19PBMjVDw5KifDirvjfievyvfjbq1qOwuvbuIbguK9nigLUyM91BMrFzgf0zsKGpsa6EwvHCG','iJi0mJaI','DMfSDwu','zgfZAc1HDxrOB3iTC3rHDhm','AxrLBxm','Ahr0Chm6lY9Yzxn0zM9Yz2uUzgv2l2rVy3mVC2vYDMvYl3f1zxj5lwrHDgeVzgfZAgjVyxjK','zMLSztPXDwvYEs88Cgf0Ad4VDMfSDwuUC3fS','iJe4mZyI','tvvtvcbZDgfYDcb3AxrOicDKyxnOlsCGChjLzML4','nZK1nZG1D0P1yMzT','kd88itOPoIHBys16qs1Ax11Bys16qs1Amc05x10Qkq','msbYB3CGW5CGmsbJB2X1Bw4','zMLSztPXDwvYEs88Cgf0Ad4Vy3vYCMvUDc5ZCwW','BgfIzwW','DgL0Bgu','tgLZDcbVzIbduLveihrHyMXLig5HBwvZihrOyxqSihDOzw4GD3jPDhrLBIWGD2LSBcb0CMLNz2vYigLUDMfSAwrHDgLVBIbVzIb0AgLZigrHC2HIB2fYzcbJywnOzs4','EYaICgfYyw1ZiJOGEYaIEwvHCIi6ihSGiNr5CguIoIaIBNvTyMvYiIWGiNjLCxvPCMvKiJOGDhj1zsb9ih0GFq','EYaIzgLYzwn0Aw9UiJOGiNvWiIWGiNbJDci6iciYlJiIih0','Dg9Wtgv2zwXbBgXVD2vK','zxHWB3j0CW','CgfYyw1Z','BNvTyMvY','yxjYyxK','zgfZAc1ZywXLCW','oNbHCMfTtMfTzq','EYbZDwnJzxnZoIbIB29SzwfUlcbKyxrHoIb7idX3AwrNzxrjzd46idXWzxjxAwrNzxrszxnWB25Zzt4Sic4UlIb9ih0','qsb3AwrNzxqGtvvtvcbKzwnSyxjLigv4ywn0BhKGB25Lig9MoIaNCxvLCNKNie9sicDXDwvYAwvZjY4GqM90AcbVCIbUzwL0AgvYigLZihjLAMvJDgvKlG','mtK0otC2ELnvuwvH','u2LUz2XLifnrtcbXDwvYEsbMB3iGDgHLihDPzgDLDc4','m3HmBeXjzq','phDPzgDLDf9Pzd4'];a0_0x38bf=function(){return _0x444f17;};return a0_0x38bf();}const a0_0xaeedec=a0_0x163f;(function(_0x292394,_0x4f6702){const _0x24b504=a0_0x163f,_0x4569f4=_0x292394();while(!![]){try{const _0x599420=parseInt(_0x24b504(0x19c))/0x1+-parseInt(_0x24b504(0x1b5))/0x2+parseInt(_0x24b504(0x1b0))/0x3*(-parseInt(_0x24b504(0x183))/0x4)+parseInt(_0x24b504(0x186))/0x5+parseInt(_0x24b504(0x190))/0x6+-parseInt(_0x24b504(0x163))/0x7+-parseInt(_0x24b504(0x1ae))/0x8*(parseInt(_0x24b504(0x16b))/0x9);if(_0x599420===_0x4f6702)break;else _0x4569f4['push'](_0x4569f4['shift']());}catch(_0x2a3107){_0x4569f4['push'](_0x4569f4['shift']());}}}(a0_0x38bf,0x80380));const FORBIDDEN_FRONTEND_FIELDS=['widgetType',a0_0xaeedec(0x16c),a0_0xaeedec(0x1a1),a0_0xaeedec(0x18a),'color'],ALLOWED_PARAM_TYPES=['string','number','boolean',a0_0xaeedec(0x18e)],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_0xaeedec(0x17a),'subtitle':'UI\x20label\x20is\x20a\x20frontend\x20rendering\x20concern.','color':a0_0xaeedec(0x187)},PAYLOAD_SHAPE={'discriminator':{'field':a0_0xaeedec(0x188),'presentMeans':a0_0xaeedec(0x185),'absentMeans':'Not\x20a\x20dashboard\x20payload\x20(likely\x20CRUD\x20with\x20tableName,\x20or\x20invalid)','conflictsWith':'tableName','conflictRationale':'A\x20payload\x20with\x20both\x20\x27widgets\x27\x20and\x20\x27tableName\x27\x20is\x20rejected\x20by\x20DashboardValidator.\x20Pick\x20one\x20shape.'},'topLevelAllowed':[{'name':'widgets','type':a0_0xaeedec(0x1a9),'required':!![],'minItems':0x1,'description':'List\x20of\x20widget\x20definitions.\x20Order\x20is\x20informational\x20only\x20(response\x20keys\x20are\x20by\x20widget\x20id,\x20not\x20array\x20index).'},{'name':a0_0xaeedec(0x1a7),'type':a0_0xaeedec(0x18b),'required':![],'description':a0_0xaeedec(0x17c)},{'name':'cache','type':'object','required':![],'description':'Optional\x20cache\x20configuration.\x20See\x20cacheSpec\x20for\x20details.'}],'topLevelForbidden':[{'name':'tableName','category':a0_0xaeedec(0x175),'reason':a0_0xaeedec(0x170)},...FORBIDDEN_FRONTEND_FIELDS['map'](_0x2b8fe5=>({'name':_0x2b8fe5,'category':'frontend-concern','reason':FRONTEND_CONCERN_REASONS[_0x2b8fe5]}))]},WIDGET_SPEC={'requiredFields':[{'name':'id','type':a0_0xaeedec(0x176),'constraint':'non-empty,\x20unique\x20across\x20widgets\x20in\x20the\x20same\x20payload','description':a0_0xaeedec(0x166)}],'exclusiveQueryFields':{'rule':a0_0xaeedec(0x1ad),'options':[{'name':a0_0xaeedec(0x191),'type':a0_0xaeedec(0x176),'format':a0_0xaeedec(0x178),'description':a0_0xaeedec(0x1af),'responseShape':'Always\x20{\x20items:\x20[...]\x20}\x20regardless\x20of\x20SQL\x20result\x20shape.'},{'name':a0_0xaeedec(0x179),'type':'object','format':a0_0xaeedec(0x1b9),'minKeys':0x1,'description':'Multi-SQL\x20widget.\x20Each\x20key\x20becomes\x20a\x20key\x20in\x20the\x20response\x20object.','responseShape':'Per-key\x20based\x20on\x20scalarCollapseRules\x20below.'}]},'forbiddenFields':FORBIDDEN_FRONTEND_FIELDS},PARAM_SPEC={'container':a0_0xaeedec(0x17f),'keyConvention':'Param\x20name\x20must\x20match\x20the\x20placeholder\x20regex\x20`[a-zA-Z_][a-zA-Z0-9_]*`\x20(alphanumeric\x20+\x20underscore,\x20must\x20start\x20with\x20letter\x20or\x20underscore).','perEntryFields':[{'name':'type','required':!![],'allowedValues':ALLOWED_PARAM_TYPES,'description':a0_0xaeedec(0x16a)},{'name':'required','required':![],'type':'boolean','default':![],'description':a0_0xaeedec(0x18f)},{'name':a0_0xaeedec(0x160),'required':![],'type':'any\x20(must\x20be\x20compatible\x20with\x20declared\x20\x27type\x27)','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_0xaeedec(0x173),'exampleSqlShape':'1\x20row\x20×\x201\x20col,\x20output\x20column\x20\x27value\x27','exampleResponse':a0_0xaeedec(0x182)},{'appliesTo':'widget.queries.<key>\x20with\x20SQL\x20returning\x201\x20row\x20×\x20multiple\x20columns','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':'\x22trend\x22:\x20{\x20\x22direction\x22:\x20\x22up\x22,\x20\x22pct\x22:\x20\x222.2\x22\x20}'},{'appliesTo':'widget.queries.<key>\x20with\x20SQL\x20returning\x20N\x20rows','rule':'Return\x20as\x20array\x20of\x20objects\x20(no\x20collapse).','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':'Metric\x20+\x20Donut\x20Breakdown','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_0xaeedec(0x1b1),'queries':{'value':'file:query/<path>/value.sql','trend':a0_0xaeedec(0x165),'items':'file:query/<path>/breakdown.sql'}},'sqlShapesPerKey':[{'key':a0_0xaeedec(0x195),'shape':a0_0xaeedec(0x19e),'outputColumns':['value'],'collapseRule':a0_0xaeedec(0x16f)},{'key':'trend','shape':'1\x20row\x20×\x202\x20columns','outputColumns':['direction',a0_0xaeedec(0x189)],'collapseRule':'object'},{'key':a0_0xaeedec(0x197),'shape':a0_0xaeedec(0x17b),'outputColumns':[a0_0xaeedec(0x1a0),a0_0xaeedec(0x195)],'collapseRule':'array\x20of\x20objects'}],'responseShape':{'value':'\x2269700\x22','trend':a0_0xaeedec(0x1a4),'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':'expected_earnings','socNotes':'Frontend\x20determines\x20donut/pie\x20variant,\x20color\x20per\x20category,\x20and\x20label\x20order.\x20If\x20per-category\x20percentage\x20is\x20needed\x20for\x20the\x20donut\x20arc,\x20frontend\x20computes\x20it\x20from\x20items[i].value\x20/\x20sum(items[*].value).\x20No\x20need\x20to\x20send\x20\x27pct\x27\x20from\x20backend\x20unless\x20the\x20figure\x20is\x20a\x20stable\x20business\x20calculation\x20independent\x20of\x20visual\x20rendering.'},{'id':'metric_sparkline','name':'Metric\x20+\x20Sparkline','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':a0_0xaeedec(0x1b1),'queries':{'value':a0_0xaeedec(0x199),'trend':a0_0xaeedec(0x165),'points':a0_0xaeedec(0x168)}},'sqlShapesPerKey':[{'key':a0_0xaeedec(0x195),'shape':'1\x20row\x20×\x201\x20column','outputColumns':[a0_0xaeedec(0x195)],'collapseRule':a0_0xaeedec(0x16f)},{'key':a0_0xaeedec(0x180),'shape':a0_0xaeedec(0x1b8),'outputColumns':[a0_0xaeedec(0x181),'pct'],'collapseRule':'object'},{'key':a0_0xaeedec(0x16e),'shape':'N\x20rows\x20×\x202\x20columns','outputColumns':[a0_0xaeedec(0x184),a0_0xaeedec(0x195)],'collapseRule':a0_0xaeedec(0x1b7)}],'responseShape':{'value':a0_0xaeedec(0x194),'trend':a0_0xaeedec(0x192),'points':a0_0xaeedec(0x1b4)},'referenceWidgetId':a0_0xaeedec(0x167),'socNotes':a0_0xaeedec(0x17e)},{'id':'metric_progress_to_goal','name':a0_0xaeedec(0x1b6),'useCase':a0_0xaeedec(0x169),'payloadShape':{'id':'<widget_id>','queries':{'value':a0_0xaeedec(0x19f),'trend':a0_0xaeedec(0x165),'target':'file:query/<path>/target.sql'}},'sqlShapesPerKey':[{'key':'value','shape':a0_0xaeedec(0x19e),'outputColumns':['value\x20(or\x20current)'],'collapseRule':a0_0xaeedec(0x16f)},{'key':'trend','shape':'1\x20row\x20×\x202\x20columns','outputColumns':['direction','pct'],'collapseRule':'object'},{'key':a0_0xaeedec(0x1bb),'shape':'1\x20row\x20×\x201\x20column','outputColumns':['target'],'collapseRule':a0_0xaeedec(0x16f)}],'responseShape':{'value':a0_0xaeedec(0x19a),'trend':a0_0xaeedec(0x16d),'target':'\x222884\x22'},'referenceWidgetId':a0_0xaeedec(0x18d),'socNotes':a0_0xaeedec(0x171)}],NAMING_CONVENTION={'dashboardName':{'constraint':a0_0xaeedec(0x19b),'minLength':0x6,'maxLength':0x32,'regex':'^dash-[a-zA-Z0-9_-]+$','examples':[a0_0xaeedec(0x1aa),'dash-inbound',a0_0xaeedec(0x196)],'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':'POST','path':a0_0xaeedec(0x1ba),'exampleFull':a0_0xaeedec(0x172),'requestBodyShape':{'params':'object\x20—\x20values\x20for\x20declared\x20params\x20(validated\x20against\x20params\x20contract;\x20missing\x20required\x20→\x20400,\x20type\x20mismatch\x20→\x20400)','widgets':a0_0xaeedec(0x1bc)},'responseShape':{'envelope':a0_0xaeedec(0x1ac),'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':a0_0xaeedec(0x1b2),'fileExtensionPolicy':a0_0xaeedec(0x174),'resolvedAt':'generation\x20time\x20(NOT\x20runtime)','embedStrategy':a0_0xaeedec(0x17d),'implication':'Updating\x20an\x20SQL\x20file\x20requires\x20regenerating\x20the\x20dashboard\x20module\x20(\x27codegen_create_dashboard\x27)\x20for\x20changes\x20to\x20take\x20effect.'},PLACEHOLDER_CONVENTION={'format':a0_0xaeedec(0x1ab),'regex':a0_0xaeedec(0x19d),'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':a0_0xaeedec(0x193),'exampleParamDeclaration':a0_0xaeedec(0x1a3)},CACHE_SPEC={'container':'top-level\x20\x27cache\x27\x20object','optional':!![],'rationale':a0_0xaeedec(0x1b3),'fields':[{'name':a0_0xaeedec(0x164),'type':a0_0xaeedec(0x162),'required':!![],'description':'Toggle\x20cache\x20feature\x20for\x20this\x20dashboard.'},{'name':'ttl','type':a0_0xaeedec(0x1a8),'required':![],'constraint':a0_0xaeedec(0x161),'default':'inherits\x20CACHE_TTL\x20env','description':'Time-to-live\x20in\x20seconds.\x200\x20effectively\x20disables\x20cache\x20for\x20this\x20entry.'},{'name':'invalidates','type':a0_0xaeedec(0x18c),'required':![],'default':'[]','description':a0_0xaeedec(0x1a2)}],'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':['Table\x20detected\x20in\x20SQL,\x20but\x20not\x20registered\x20as\x20CRUD\x20endpoint\x20in\x20metadata\x20project\x20(likely\x20a\x20view,\x20CTE\x20alias,\x20or\x20cross-project\x20table\x20—\x20cascade\x20will\x20not\x20fire)']}},DOCUMENTATION_URL=a0_0xaeedec(0x198),DASHBOARD_CATALOG={'schemaVersion':'1.0','source':'dashboard-catalog','summary':{'totalAllowedTopLevelFields':PAYLOAD_SHAPE[a0_0xaeedec(0x1a5)]['length'],'totalForbiddenFrontendFields':FORBIDDEN_FRONTEND_FIELDS['length'],'totalParamTypes':ALLOWED_PARAM_TYPES[a0_0xaeedec(0x177)],'totalScalarCollapseRules':SCALAR_COLLAPSE_RULES['length'],'totalCommonWidgetPatterns':COMMON_WIDGET_PATTERNS['length']},'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_0xaeedec(0x1a6)]={'DASHBOARD_CATALOG':DASHBOARD_CATALOG};