@restforgejs/platform 4.1.1 → 4.3.1

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 (340) 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 +43 -4
  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 +526 -0
  18. package/generators/cli/schema/describe.js +3 -2
  19. package/generators/cli/schema/diff.js +322 -0
  20. package/generators/cli/schema/generate-ddl.js +7 -10
  21. package/generators/cli/schema/init.js +95 -172
  22. package/generators/cli/schema/introspect.js +3 -2
  23. package/generators/cli/schema/list.js +3 -2
  24. package/generators/cli/schema/migrate.js +13 -18
  25. package/generators/cli/schema/models.js +8 -12
  26. package/generators/cli/schema/template.js +222 -0
  27. package/generators/cli/schema/validate.js +8 -12
  28. package/generators/cli-entry.js +17 -2
  29. package/generators/lib/dbschema-kit/apply-engine.js +582 -0
  30. package/generators/lib/dbschema-kit/diff-engine.js +703 -0
  31. package/generators/lib/dbschema-kit/diff-reporter.js +272 -0
  32. package/generators/lib/dbschema-kit/emitters/alter-table.js +275 -0
  33. package/generators/lib/migration/audit-table-runner.js +213 -215
  34. package/generators/lib/payload/endpoint-schema-validator.js +171 -0
  35. package/generators/lib/payload/payload-runner.js +137 -220
  36. package/generators/lib/payload/schema-diff.js +277 -0
  37. package/generators/lib/templates/dashboard-catalog.js +1 -437
  38. package/generators/lib/templates/db-connection-env.js +1 -212
  39. package/generators/lib/templates/dbschema-catalog.js +1 -489
  40. package/generators/lib/templates/field-validation-catalog.js +1 -531
  41. package/generators/lib/templates/mysql-template.js +1 -3863
  42. package/generators/lib/templates/oracle-template.js +1 -3915
  43. package/generators/lib/templates/postgres-template.js +1 -5838
  44. package/generators/lib/templates/query-declarative-catalog.js +1 -199
  45. package/generators/lib/templates/sqlite-template.js +1 -3440
  46. package/generators/lib/utils/audit-columns.js +181 -0
  47. package/generators/lib/utils/cli-output.js +17 -0
  48. package/generators/lib/utils/database-introspector.js +16 -13
  49. package/generators/lib/utils/env-manager.js +6 -0
  50. package/generators/lib/utils/path-validator.js +71 -0
  51. package/generators/lib/validators/payload-validator.js +1 -2
  52. package/integrity-manifest.json +28 -10
  53. package/package.json +11 -3
  54. package/scripts/verify-integrity.js +1 -1
  55. package/server.js +1 -1
  56. package/src/components/handlers/adjust_handler.js +1 -1
  57. package/src/components/handlers/audit_handler.js +1 -1
  58. package/src/components/handlers/delete_handler.js +1 -1
  59. package/src/components/handlers/export_handler.js +1 -1
  60. package/src/components/handlers/import_handler.js +1 -1
  61. package/src/components/handlers/insert_handler.js +1 -1
  62. package/src/components/handlers/update_handler.js +1 -1
  63. package/src/components/handlers/upload_handler.js +1 -1
  64. package/src/components/handlers/workflow_handler.js +1 -1
  65. package/src/components/integrations/webhook.js +1 -1
  66. package/src/consumers/baseConsumer.js +1 -1
  67. package/src/consumers/declarativeMapper.js +1 -1
  68. package/src/consumers/handlers/apiHandler.js +1 -1
  69. package/src/consumers/handlers/consoleHandler.js +1 -1
  70. package/src/consumers/handlers/databaseHandler.js +1 -1
  71. package/src/consumers/handlers/index.js +1 -1
  72. package/src/consumers/handlers/kafkaHandler.js +1 -1
  73. package/src/consumers/index.js +1 -1
  74. package/src/consumers/messageTransformer.js +1 -1
  75. package/src/consumers/validator.js +1 -1
  76. package/src/core/db/dialect/base-dialect.js +1 -1
  77. package/src/core/db/dialect/index.js +1 -1
  78. package/src/core/db/dialect/mysql-dialect.js +1 -1
  79. package/src/core/db/dialect/oracle-dialect.js +1 -1
  80. package/src/core/db/dialect/postgres-dialect.js +1 -1
  81. package/src/core/db/dialect/sqlite-dialect.js +1 -1
  82. package/src/core/db/flatten-helper.js +1 -1
  83. package/src/core/db/query-builder-error.js +1 -1
  84. package/src/core/db/query-builder.js +1 -1
  85. package/src/core/db/relation-helper.js +1 -1
  86. package/src/core/handlers/delete_handler.js +1 -1
  87. package/src/core/handlers/insert_handler.js +1 -1
  88. package/src/core/handlers/update_handler.js +1 -1
  89. package/src/core/models/base-model.js +1 -1
  90. package/src/core/utils/cache-manager.js +1 -1
  91. package/src/core/utils/component-engine.js +1 -1
  92. package/src/core/utils/context-builder.js +1 -1
  93. package/src/core/utils/datetime-formatter.js +1 -1
  94. package/src/core/utils/datetime-parser.js +1 -1
  95. package/src/core/utils/db.js +1 -1
  96. package/src/core/utils/logger.js +1 -1
  97. package/src/core/utils/payload-loader.js +1 -1
  98. package/src/core/utils/security-checks.js +1 -1
  99. package/src/middleware/body-options.js +1 -1
  100. package/src/middleware/cors.js +1 -1
  101. package/src/middleware/idempotency.js +1 -1
  102. package/src/middleware/rate-limiter.js +1 -1
  103. package/src/middleware/request-logger.js +1 -1
  104. package/src/middleware/security-headers.js +1 -1
  105. package/src/models/base-model-mysql.js +1 -1
  106. package/src/models/base-model-oracle.js +1 -1
  107. package/src/models/base-model-sqlite.js +1 -1
  108. package/src/models/base-model.js +1 -1
  109. package/src/pro/caching/redis-client.js +1 -1
  110. package/src/pro/caching/redis-helper.js +1 -1
  111. package/src/pro/consumers/baseConsumer.js +1 -1
  112. package/src/pro/consumers/declarativeMapper.js +1 -1
  113. package/src/pro/consumers/handlers/apiHandler.js +1 -1
  114. package/src/pro/consumers/handlers/consoleHandler.js +1 -1
  115. package/src/pro/consumers/handlers/databaseHandler.js +1 -1
  116. package/src/pro/consumers/handlers/index.js +1 -1
  117. package/src/pro/consumers/handlers/kafkaHandler.js +1 -1
  118. package/src/pro/consumers/index.js +1 -1
  119. package/src/pro/consumers/messageTransformer.js +1 -1
  120. package/src/pro/consumers/validator.js +1 -1
  121. package/src/pro/database/base-model-mysql.js +1 -1
  122. package/src/pro/database/base-model-oracle.js +1 -1
  123. package/src/pro/database/base-model-sqlite.js +1 -1
  124. package/src/pro/database/db-mysql.js +1 -1
  125. package/src/pro/database/db-oracle.js +1 -1
  126. package/src/pro/database/db-sqlite.js +1 -1
  127. package/src/pro/excel/excel-generator.js +1 -1
  128. package/src/pro/excel/excel-parser.js +1 -1
  129. package/src/pro/excel/export-service.js +1 -1
  130. package/src/pro/excel/export_handler.js +1 -1
  131. package/src/pro/excel/import-service.js +1 -1
  132. package/src/pro/excel/import-validator.js +1 -1
  133. package/src/pro/excel/import_handler.js +1 -1
  134. package/src/pro/excel/upsert-builder.js +1 -1
  135. package/src/pro/idgen/idgen-routes.js +1 -1
  136. package/src/pro/integrations/lookup-resolver.js +1 -1
  137. package/src/pro/integrations/upload-handler-v2.js +1 -1
  138. package/src/pro/integrations/upload-handler.js +1 -1
  139. package/src/pro/integrations/webhook.js +1 -1
  140. package/src/pro/locking/lock-routes.js +1 -1
  141. package/src/pro/locking/resource-lock-manager.js +1 -1
  142. package/src/pro/messaging/kafkaConsumerService.js +1 -1
  143. package/src/pro/messaging/kafkaService.js +1 -1
  144. package/src/pro/messaging/messagehubService.js +1 -1
  145. package/src/pro/messaging/rabbitmqService.js +1 -1
  146. package/src/pro/scheduler/job-manager.js +1 -1
  147. package/src/pro/scheduler/job-routes.js +1 -1
  148. package/src/pro/scheduler/job-validator.js +1 -1
  149. package/src/pro/storage/base-storage-provider.js +1 -1
  150. package/src/pro/storage/file-metadata-helper.js +1 -1
  151. package/src/pro/storage/index.js +1 -1
  152. package/src/pro/storage/local-storage-provider.js +1 -1
  153. package/src/pro/storage/s3-storage-provider.js +1 -1
  154. package/src/pro/storage/upload-cleanup-job.js +1 -1
  155. package/src/pro/storage/upload-cleanup-scheduler.js +1 -1
  156. package/src/pro/storage/upload-pending-tracker.js +1 -1
  157. package/src/pro/websocket/broadcast-helper.js +1 -1
  158. package/src/pro/websocket/index.js +1 -1
  159. package/src/pro/websocket/livesync-server.js +1 -1
  160. package/src/pro/websocket/ws-broadcaster.js +1 -1
  161. package/src/services/export-service.js +1 -1
  162. package/src/services/import-service.js +1 -1
  163. package/src/services/kafkaConsumerService.js +1 -1
  164. package/src/services/kafkaService.js +1 -1
  165. package/src/services/messagehubService.js +1 -1
  166. package/src/services/rabbitmqService.js +1 -1
  167. package/src/utils/cache-invalidation-registry.js +1 -1
  168. package/src/utils/cache-manager.js +1 -1
  169. package/src/utils/component-engine.js +1 -1
  170. package/src/utils/config-extractor.js +1 -1
  171. package/src/utils/consumerLogger.js +1 -1
  172. package/src/utils/context-builder.js +1 -1
  173. package/src/utils/dashboard-helpers.js +1 -1
  174. package/src/utils/dateHelper.js +1 -1
  175. package/src/utils/datetime-formatter.js +1 -1
  176. package/src/utils/datetime-parser.js +1 -1
  177. package/src/utils/db-bootstrap.js +1 -1
  178. package/src/utils/db-mysql.js +1 -1
  179. package/src/utils/db-oracle.js +1 -1
  180. package/src/utils/db-sqlite.js +1 -1
  181. package/src/utils/db.js +1 -1
  182. package/src/utils/demo-generator.js +1 -1
  183. package/src/utils/excel-generator.js +1 -1
  184. package/src/utils/excel-parser.js +1 -1
  185. package/src/utils/file-watcher.js +1 -1
  186. package/src/utils/id-generator.js +1 -1
  187. package/src/utils/idempotency-manager.js +1 -1
  188. package/src/utils/import-validator.js +1 -1
  189. package/src/utils/license-client.js +1 -1
  190. package/src/utils/lock-manager.js +1 -1
  191. package/src/utils/logger.js +1 -1
  192. package/src/utils/lookup-resolver.js +1 -1
  193. package/src/utils/payload-loader.js +1 -1
  194. package/src/utils/processor-response.js +1 -1
  195. package/src/utils/rabbitmq.js +1 -1
  196. package/src/utils/redis-client.js +1 -1
  197. package/src/utils/redis-helper.js +1 -1
  198. package/src/utils/request-scope.js +1 -1
  199. package/src/utils/security-checks.js +1 -1
  200. package/src/utils/service-resolver.js +1 -1
  201. package/src/utils/shutdown-coordinator.js +1 -1
  202. package/src/utils/trusted-keys.js +1 -1
  203. package/src/utils/upload-handler.js +1 -1
  204. package/src/utils/upsert-builder.js +1 -1
  205. package/src/utils/workflow-hook-executor.js +1 -1
  206. package/generators/metadata/global.json +0 -58
  207. package/generators/metadata/test-mysql-workbench.json +0 -118
  208. package/generators/metadata/test-mysql.json +0 -56
  209. package/generators/metadata/test-oracle-workbench.json +0 -118
  210. package/generators/metadata/test-oracle.json +0 -56
  211. package/generators/metadata/test-pg-workbench.json +0 -118
  212. package/generators/metadata/test-pg.json +0 -56
  213. package/generators/scripts/obfuscate-source.js +0 -356
  214. package/generators/scripts/validate-catalog.js +0 -430
  215. package/generators/scripts/validate-dbschema-catalog.js +0 -708
  216. package/generators/tests/baseline/mysql/mini_inventory_item/src/models/mini-inventory/item.js +0 -944
  217. package/generators/tests/baseline/mysql/mini_inventory_item/src/modules/mini-inventory/item.js +0 -740
  218. package/generators/tests/baseline/mysql/mini_inventory_item/src/modules/mini-inventory.js +0 -336
  219. package/generators/tests/baseline/oracle/mini_inventory_item/src/models/mini-inventory/item.js +0 -1002
  220. package/generators/tests/baseline/oracle/mini_inventory_item/src/modules/mini-inventory/item.js +0 -740
  221. package/generators/tests/baseline/oracle/mini_inventory_item/src/modules/mini-inventory.js +0 -336
  222. package/generators/tests/baseline/postgres/mini_inventory_item/src/models/mini-inventory/item.js +0 -1333
  223. package/generators/tests/baseline/postgres/mini_inventory_item/src/modules/mini-inventory/item.js +0 -1173
  224. package/generators/tests/baseline/postgres/mini_inventory_item/src/modules/mini-inventory.js +0 -496
  225. package/generators/tests/fixtures/payloads/custom-sensitive.json +0 -27
  226. package/generators/tests/fixtures/payloads/dynamic-search-optout.json +0 -23
  227. package/generators/tests/fixtures/payloads/login-with-password.json +0 -22
  228. package/generators/tests/fixtures/payloads/order-process.json +0 -52
  229. package/generators/tests/fixtures/payloads/with-inline-sql.json +0 -26
  230. package/generators/tests/integration-tahap4b/README.md +0 -145
  231. package/generators/tests/integration-tahap4b/run-concurrent.js +0 -77
  232. package/generators/tests/integration-tahap4b/seed.sql +0 -53
  233. package/generators/tests/integration-tahap4b/verify.sql +0 -110
  234. package/generators/tests/unit/cli/create-dashboard.test.js +0 -505
  235. package/generators/tests/unit/cli/create-processor.test.js +0 -319
  236. package/generators/tests/unit/cli/dispatch-dashboard.test.js +0 -149
  237. package/generators/tests/unit/lib/dashboard-generator.test.js +0 -895
  238. package/generators/tests/unit/lib/dashboard-validator.test.js +0 -354
  239. package/generators/tests/unit/lib/dbschema-kit/apply-executor.test.js +0 -437
  240. package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-introspect.test.js +0 -393
  241. package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-generate-ddl.test.js +0 -104
  242. package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-init.test.js +0 -119
  243. package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-list.test.js +0 -48
  244. package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-migrate.test.js +0 -175
  245. package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-kit-validate.test.js +0 -102
  246. package/generators/tests/unit/lib/dbschema-kit/cli/dbschema-models.test.js +0 -43
  247. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/all-schemas-listing.js +0 -84
  248. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/connection-error.js +0 -13
  249. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/empty.js +0 -12
  250. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/multi-schema.js +0 -124
  251. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/single-schema-inventory.js +0 -64
  252. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/introspect-stubs/two-tables.js +0 -66
  253. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/migrate-stubs/connection-error.js +0 -9
  254. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/migrate-stubs/partial.js +0 -29
  255. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/migrate-stubs/rollback.js +0 -26
  256. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/migrate-stubs/success.js +0 -43
  257. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/multi-schema/audit/events.js +0 -18
  258. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/multi-schema/inventory/products.js +0 -9
  259. package/generators/tests/unit/lib/dbschema-kit/cli/fixtures/multi-schema/users.js +0 -8
  260. package/generators/tests/unit/lib/dbschema-kit/connection.test.js +0 -112
  261. package/generators/tests/unit/lib/dbschema-kit/ddl-generator.test.js +0 -205
  262. package/generators/tests/unit/lib/dbschema-kit/define-model.test.js +0 -56
  263. package/generators/tests/unit/lib/dbschema-kit/dialect/index.test.js +0 -46
  264. package/generators/tests/unit/lib/dbschema-kit/dialect/mysql.test.js +0 -126
  265. package/generators/tests/unit/lib/dbschema-kit/dialect/oracle.test.js +0 -126
  266. package/generators/tests/unit/lib/dbschema-kit/dialect/postgres.test.js +0 -131
  267. package/generators/tests/unit/lib/dbschema-kit/dialect/sqlite.test.js +0 -126
  268. package/generators/tests/unit/lib/dbschema-kit/driver-loader.test.js +0 -93
  269. package/generators/tests/unit/lib/dbschema-kit/emitters/create-index.test.js +0 -173
  270. package/generators/tests/unit/lib/dbschema-kit/emitters/create-table.test.js +0 -376
  271. package/generators/tests/unit/lib/dbschema-kit/emitters/drop-table.test.js +0 -78
  272. package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/invalid-dialect.env +0 -6
  273. package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/missing-dialect.env +0 -5
  274. package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/missing-host.env +0 -5
  275. package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/oracle-valid.env +0 -6
  276. package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/postgres-valid.env +0 -7
  277. package/generators/tests/unit/lib/dbschema-kit/fixtures/connection/sqlite-valid.env +0 -2
  278. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/category.js +0 -11
  279. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/item_product.js +0 -11
  280. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/stock_inbound.js +0 -24
  281. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/stock_inbound_item.js +0 -28
  282. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/supplier.js +0 -9
  283. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory/warehouse.js +0 -9
  284. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-invalid/orphan.js +0 -17
  285. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/master/category.js +0 -11
  286. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/master/item_product.js +0 -11
  287. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/master/supplier.js +0 -9
  288. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/master/warehouse.js +0 -9
  289. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/transactions/stock_inbound.js +0 -24
  290. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/mini-inventory-multifolder/transactions/stock_inbound_item.js +0 -28
  291. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/multi-schema/audit/events.js +0 -18
  292. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/multi-schema/inventory/products.js +0 -9
  293. package/generators/tests/unit/lib/dbschema-kit/fixtures/integration/multi-schema/public/users.js +0 -9
  294. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/duplicate-subfolder/extra/category.js +0 -8
  295. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/duplicate-subfolder/master/category.js +0 -8
  296. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/duplicate-tablename/bar.js +0 -8
  297. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/duplicate-tablename/foo.js +0 -8
  298. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/empty-folder/README.md +0 -1
  299. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/invalid-export/plain.js +0 -3
  300. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/invalid-schema/bad.js +0 -6
  301. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/legacy-pattern/legacy.js +0 -12
  302. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/multi-schema-distinct/audit/products.js +0 -9
  303. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/multi-schema-distinct/inventory/products.js +0 -9
  304. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/multi-schema-duplicate/a/products.js +0 -8
  305. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/multi-schema-duplicate/b/products.js +0 -8
  306. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/nested-deep/a/b/c/deep_table.js +0 -8
  307. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/.hidden/ignored.js +0 -7
  308. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/master/category.js +0 -8
  309. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/master/supplier.js +0 -8
  310. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/transactions/stock_inbound.js +0 -8
  311. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/recursive-multi-folder/transactions/stock_inbound_item.js +0 -8
  312. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/valid-multiple/category.js +0 -8
  313. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/valid-multiple/item_product.js +0 -9
  314. package/generators/tests/unit/lib/dbschema-kit/fixtures/loader/valid-single/category.js +0 -8
  315. package/generators/tests/unit/lib/dbschema-kit/integration.test.js +0 -217
  316. package/generators/tests/unit/lib/dbschema-kit/introspect-mapper.test.js +0 -403
  317. package/generators/tests/unit/lib/dbschema-kit/ir-builder.test.js +0 -390
  318. package/generators/tests/unit/lib/dbschema-kit/loader.test.js +0 -128
  319. package/generators/tests/unit/lib/dbschema-kit/naming.test.js +0 -170
  320. package/generators/tests/unit/lib/dbschema-kit/parser/shorthand-parser.test.js +0 -237
  321. package/generators/tests/unit/lib/dbschema-kit/schema-printer.test.js +0 -251
  322. package/generators/tests/unit/lib/dbschema-kit/statement-modifier.test.js +0 -105
  323. package/generators/tests/unit/lib/dbschema-kit/statement-splitter.test.js +0 -165
  324. package/generators/tests/unit/lib/dbschema-kit/topological-sort.test.js +0 -135
  325. package/generators/tests/unit/lib/dbschema-kit/validator/check-compatibility-validator.test.js +0 -373
  326. package/generators/tests/unit/lib/dbschema-kit/validator/circular-relation-validator.test.js +0 -454
  327. package/generators/tests/unit/lib/dbschema-kit/validator/cross-model-validator.test.js +0 -512
  328. package/generators/tests/unit/lib/dbschema-kit/validator/enhanced-validate-integration.test.js +0 -390
  329. package/generators/tests/unit/lib/dbschema-kit/validator/naming-convention-validator.test.js +0 -306
  330. package/generators/tests/unit/lib/dbschema-kit/validator/schema-validator.test.js +0 -443
  331. package/generators/tests/unit/lib/dbschema-kit/validator/type-compatibility-validator.test.js +0 -440
  332. package/generators/tests/unit/lib/dbschema-kit/validator/validator-reporter.test.js +0 -172
  333. package/generators/tests/unit/lib/metadata-manager-dashboard.test.js +0 -256
  334. package/generators/tests/unit/lib/payload-validator-fieldpolicy.test.js +0 -240
  335. package/generators/tests/unit/lib/processor-validation-generator.test.js +0 -300
  336. package/generators/tests/unit/lib/sensitive-field-masker.test.js +0 -170
  337. package/generators/tests/unit/lib/sql-table-extractor.test.js +0 -119
  338. package/scripts/generate-integrity-manifest.js +0 -124
  339. package/scripts/snapshot-cli-contracts.js +0 -194
  340. 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
+ const a0_0x1bcb89=a0_0x9c64;(function(_0x2b1cb8,_0x330c46){const _0x5d0276=a0_0x9c64,_0x5698ef=_0x2b1cb8();while(!![]){try{const _0x213706=-parseInt(_0x5d0276(0x11d))/0x1+-parseInt(_0x5d0276(0xce))/0x2*(-parseInt(_0x5d0276(0xd4))/0x3)+-parseInt(_0x5d0276(0xe2))/0x4+parseInt(_0x5d0276(0x105))/0x5*(-parseInt(_0x5d0276(0x104))/0x6)+parseInt(_0x5d0276(0xf2))/0x7*(-parseInt(_0x5d0276(0xef))/0x8)+-parseInt(_0x5d0276(0xf4))/0x9*(parseInt(_0x5d0276(0x10a))/0xa)+parseInt(_0x5d0276(0xe9))/0xb;if(_0x213706===_0x330c46)break;else _0x5698ef['push'](_0x5698ef['shift']());}catch(_0x560f52){_0x5698ef['push'](_0x5698ef['shift']());}}}(a0_0x4227,0x5950d));const FORBIDDEN_FRONTEND_FIELDS=['widgetType','layout',a0_0x1bcb89(0xf9),a0_0x1bcb89(0x101),'color'],ALLOWED_PARAM_TYPES=[a0_0x1bcb89(0x102),'number',a0_0x1bcb89(0x11e),'date'],FRONTEND_CONCERN_REASONS={'widgetType':a0_0x1bcb89(0xfa),'layout':a0_0x1bcb89(0x108),'title':'UI\x20label\x20is\x20a\x20frontend\x20rendering\x20concern.','subtitle':a0_0x1bcb89(0xe5),'color':'Visual\x20color\x20is\x20a\x20frontend\x20rendering\x20concern.'},PAYLOAD_SHAPE={'discriminator':{'field':'widgets','presentMeans':'dashboard\x20payload','absentMeans':a0_0x1bcb89(0xd7),'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_0x1bcb89(0xe0),'required':!![],'minItems':0x1,'description':a0_0x1bcb89(0x103)},{'name':a0_0x1bcb89(0xc8),'type':a0_0x1bcb89(0xde),'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':'cache','type':'object','required':![],'description':'Optional\x20cache\x20configuration.\x20See\x20cacheSpec\x20for\x20details.'}],'topLevelForbidden':[{'name':a0_0x1bcb89(0xe1),'category':a0_0x1bcb89(0x11f),'reason':'Reserved\x20for\x20CRUD\x20payloads.\x20A\x20dashboard\x20payload\x20must\x20declare\x20\x27widgets\x27\x20instead.'},...FORBIDDEN_FRONTEND_FIELDS['map'](_0x255ffd=>({'name':_0x255ffd,'category':a0_0x1bcb89(0xf6),'reason':FRONTEND_CONCERN_REASONS[_0x255ffd]}))]},WIDGET_SPEC={'requiredFields':[{'name':'id','type':'string','constraint':'non-empty,\x20unique\x20across\x20widgets\x20in\x20the\x20same\x20payload','description':'Widget\x20identifier;\x20used\x20as\x20the\x20response\x20key\x20in\x20the\x20dashboard\x20envelope.'}],'exclusiveQueryFields':{'rule':a0_0x1bcb89(0xdd),'options':[{'name':'query','type':'string','format':a0_0x1bcb89(0xf3),'description':a0_0x1bcb89(0xc4),'responseShape':'Always\x20{\x20items:\x20[...]\x20}\x20regardless\x20of\x20SQL\x20result\x20shape.'},{'name':'queries','type':'object','format':'key→file:relative/path/to/query.sql','minKeys':0x1,'description':'Multi-SQL\x20widget.\x20Each\x20key\x20becomes\x20a\x20key\x20in\x20the\x20response\x20object.','responseShape':a0_0x1bcb89(0xdb)}]},'forbiddenFields':FORBIDDEN_FRONTEND_FIELDS},PARAM_SPEC={'container':a0_0x1bcb89(0xeb),'keyConvention':a0_0x1bcb89(0xd2),'perEntryFields':[{'name':a0_0x1bcb89(0xc7),'required':!![],'allowedValues':ALLOWED_PARAM_TYPES,'description':'Param\x20data\x20type.\x20Validates\x20request\x20body\x20and\x20shapes\x20runtime\x20parameter\x20binding.'},{'name':'required','required':![],'type':'boolean','default':![],'description':a0_0x1bcb89(0xd6)},{'name':a0_0x1bcb89(0x126),'required':![],'type':a0_0x1bcb89(0xff),'description':a0_0x1bcb89(0x10c)}]},SCALAR_COLLAPSE_RULES=[{'appliesTo':'widget.query\x20(singular)','rule':a0_0x1bcb89(0x10d),'exampleSqlShape':a0_0x1bcb89(0x124),'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_0x1bcb89(0x118),'exampleSqlShape':'1\x20row\x20×\x201\x20col,\x20output\x20column\x20\x27value\x27','exampleResponse':'\x22value\x22:\x20\x2269700\x22'},{'appliesTo':a0_0x1bcb89(0xed),'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':a0_0x1bcb89(0xdc),'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_0x1bcb89(0x119),'queries':{'value':'file:query/<path>/value.sql','trend':a0_0x1bcb89(0xc6),'items':a0_0x1bcb89(0x125)}},'sqlShapesPerKey':[{'key':'value','shape':a0_0x1bcb89(0x116),'outputColumns':['value'],'collapseRule':a0_0x1bcb89(0x100)},{'key':'trend','shape':a0_0x1bcb89(0xe6),'outputColumns':[a0_0x1bcb89(0x117),a0_0x1bcb89(0xec)],'collapseRule':'object'},{'key':'items','shape':a0_0x1bcb89(0xcb),'outputColumns':[a0_0x1bcb89(0xe4),'value'],'collapseRule':a0_0x1bcb89(0xcf)}],'responseShape':{'value':'\x2269700\x22','trend':'{\x20\x22direction\x22:\x20\x22up\x22,\x20\x22pct\x22:\x20\x222.2\x22\x20}','items':a0_0x1bcb89(0xfb)},'referenceWidgetId':'expected_earnings','socNotes':a0_0x1bcb89(0x115)},{'id':'metric_sparkline','name':a0_0x1bcb89(0xca),'useCase':a0_0x1bcb89(0x109),'payloadShape':{'id':a0_0x1bcb89(0x119),'queries':{'value':a0_0x1bcb89(0xf1),'trend':'file:query/<path>/trend.sql','points':a0_0x1bcb89(0xee)}},'sqlShapesPerKey':[{'key':a0_0x1bcb89(0x111),'shape':'1\x20row\x20×\x201\x20column','outputColumns':['value'],'collapseRule':'scalar\x20primitive'},{'key':a0_0x1bcb89(0xd1),'shape':'1\x20row\x20×\x202\x20columns','outputColumns':[a0_0x1bcb89(0x117),a0_0x1bcb89(0xec)],'collapseRule':'object'},{'key':'points','shape':'N\x20rows\x20×\x202\x20columns','outputColumns':[a0_0x1bcb89(0xea),'value'],'collapseRule':a0_0x1bcb89(0xcf)}],'responseShape':{'value':'\x222420\x22','trend':a0_0x1bcb89(0xd0),'points':'[{\x20\x22period\x22:\x20\x222026-04-24\x22,\x20\x22value\x22:\x20\x221850\x22\x20},\x20...\x20]'},'referenceWidgetId':'avg_daily_sales','socNotes':a0_0x1bcb89(0x106)},{'id':a0_0x1bcb89(0xe3),'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':a0_0x1bcb89(0xcc),'trend':'file:query/<path>/trend.sql','target':a0_0x1bcb89(0xf7)}},'sqlShapesPerKey':[{'key':'value','shape':a0_0x1bcb89(0x116),'outputColumns':['value\x20(or\x20current)'],'collapseRule':a0_0x1bcb89(0x100)},{'key':'trend','shape':'1\x20row\x20×\x202\x20columns','outputColumns':['direction','pct'],'collapseRule':'object'},{'key':a0_0x1bcb89(0xd5),'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':'orders_this_month','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_0x1bcb89(0x11c),'minLength':0x6,'maxLength':0x32,'regex':'^dash-[a-zA-Z0-9_-]+$','examples':[a0_0x1bcb89(0x10e),a0_0x1bcb89(0xe7),a0_0x1bcb89(0x114)],'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_0x1bcb89(0x10f),'path':a0_0x1bcb89(0xf0),'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_0x1bcb89(0x123)},'responseShape':{'envelope':'{\x20success:\x20boolean,\x20data:\x20{\x20<widgetId>:\x20<perWidgetResponse>,\x20...\x20}\x20}','perWidgetResponse':'Determined\x20by\x20scalarCollapseRules.\x20Failed\x20widgets\x20produce\x20{\x20error:\x20\x27...\x27\x20}\x20block\x20with\x20top-level\x20success\x20still\x20true\x20(one\x20widget\x20failure\x20does\x20NOT\x20fail\x20the\x20dashboard).'}},FILE_REFERENCE_CONVENTION={'format':a0_0x1bcb89(0xf3),'pathRelativeTo':a0_0x1bcb89(0xd3),'fileExtensionPolicy':a0_0x1bcb89(0x121),'resolvedAt':a0_0x1bcb89(0xfc),'embedStrategy':'SQL\x20file\x20content\x20is\x20embedded\x20as\x20JavaScript\x20template\x20literal\x20inside\x20the\x20generated\x20module\x20file.\x20Runtime\x20performs\x20zero\x20disk\x20I/O\x20per\x20request\x20—\x20all\x20SQL\x20is\x20in\x20memory\x20after\x20module\x20load.','implication':'Updating\x20an\x20SQL\x20file\x20requires\x20regenerating\x20the\x20dashboard\x20module\x20(\x27codegen_create_dashboard\x27)\x20for\x20changes\x20to\x20take\x20effect.'},PLACEHOLDER_CONVENTION={'format':':paramName','regex':a0_0x1bcb89(0xc5),'regexNotes':a0_0x1bcb89(0x110),'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':a0_0x1bcb89(0xcd)},CACHE_SPEC={'container':a0_0x1bcb89(0xc9),'optional':!![],'rationale':a0_0x1bcb89(0x11a),'fields':[{'name':a0_0x1bcb89(0xe8),'type':'boolean','required':!![],'description':a0_0x1bcb89(0xd9)},{'name':a0_0x1bcb89(0xf5),'type':'number','required':![],'constraint':'>=\x200\x20(seconds)','default':a0_0x1bcb89(0xd8),'description':'Time-to-live\x20in\x20seconds.\x200\x20effectively\x20disables\x20cache\x20for\x20this\x20entry.'},{'name':a0_0x1bcb89(0xdf),'type':a0_0x1bcb89(0x11b),'required':![],'default':'[]','description':'List\x20of\x20CRUD\x20table\x20names\x20that,\x20when\x20written,\x20will\x20trigger\x20invalidation\x20of\x20this\x20dashboard\x20cache.'}],'validation':{'sqlCrossReference':a0_0x1bcb89(0x107),'errorOn':[a0_0x1bcb89(0x113),a0_0x1bcb89(0x112)],'warningOn':[a0_0x1bcb89(0xfd)]}},DOCUMENTATION_URL=a0_0x1bcb89(0xfe),DASHBOARD_CATALOG={'schemaVersion':a0_0x1bcb89(0xf8),'source':a0_0x1bcb89(0x120),'summary':{'totalAllowedTopLevelFields':PAYLOAD_SHAPE[a0_0x1bcb89(0x122)][a0_0x1bcb89(0x10b)],'totalForbiddenFrontendFields':FORBIDDEN_FRONTEND_FIELDS[a0_0x1bcb89(0x10b)],'totalParamTypes':ALLOWED_PARAM_TYPES['length'],'totalScalarCollapseRules':SCALAR_COLLAPSE_RULES['length'],'totalCommonWidgetPatterns':COMMON_WIDGET_PATTERNS[a0_0x1bcb89(0x10b)]},'payloadShape':PAYLOAD_SHAPE,'widgetSpec':WIDGET_SPEC,'paramSpec':PARAM_SPEC,'scalarCollapseRules':SCALAR_COLLAPSE_RULES,'commonWidgetPatterns':COMMON_WIDGET_PATTERNS,'namingConvention':NAMING_CONVENTION,'urlPattern':URL_PATTERN,'fileReferenceConvention':FILE_REFERENCE_CONVENTION,'placeholderConvention':PLACEHOLDER_CONVENTION,'cacheSpec':CACHE_SPEC,'documentationUrl':DOCUMENTATION_URL};function a0_0x9c64(_0x945cb2,_0x590855){_0x945cb2=_0x945cb2-0xc4;const _0x422707=a0_0x4227();let _0x9c644e=_0x422707[_0x945cb2];if(a0_0x9c64['azAKdO']===undefined){var _0x1cd830=function(_0x2c8fcc){const _0x9f058d='abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/=';let _0x255ffd='',_0x1c2d06='';for(let _0x156103=0x0,_0x3e1e61,_0x1bfae2,_0x23ee0b=0x0;_0x1bfae2=_0x2c8fcc['charAt'](_0x23ee0b++);~_0x1bfae2&&(_0x3e1e61=_0x156103%0x4?_0x3e1e61*0x40+_0x1bfae2:_0x1bfae2,_0x156103++%0x4)?_0x255ffd+=String['fromCharCode'](0xff&_0x3e1e61>>(-0x2*_0x156103&0x6)):0x0){_0x1bfae2=_0x9f058d['indexOf'](_0x1bfae2);}for(let _0x1a8298=0x0,_0x25bc32=_0x255ffd['length'];_0x1a8298<_0x25bc32;_0x1a8298++){_0x1c2d06+='%'+('00'+_0x255ffd['charCodeAt'](_0x1a8298)['toString'](0x10))['slice'](-0x2);}return decodeURIComponent(_0x1c2d06);};a0_0x9c64['uDhYJh']=_0x1cd830,a0_0x9c64['NEBtEo']={},a0_0x9c64['azAKdO']=!![];}const _0x60f226=_0x422707[0x0],_0x58e23a=_0x945cb2+_0x60f226,_0x5973fb=a0_0x9c64['NEBtEo'][_0x58e23a];return!_0x5973fb?(_0x9c644e=a0_0x9c64['uDhYJh'](_0x9c644e),a0_0x9c64['NEBtEo'][_0x58e23a]=_0x9c644e):_0x9c644e=_0x5973fb,_0x9c644e;}module[a0_0x1bcb89(0xda)]={'DASHBOARD_CATALOG':DASHBOARD_CATALOG};function a0_0x4227(){const _0x3b0eca=['yxjYyxK8C3rYAw5NpG','tvvtvcbZDgfYDcb3AxrOicDKyxnOlsCGChjLzML4','ndaZnZqYC3zXv0fI','yM9VBgvHBG','C2HHCguTy29UzMXPy3q','zgfZAgjVyxjKlwnHDgfSB2C','zNjLztSGlNnXBcbYzwnVBw1LBMrLzcbMB3iGzwrPDg9YigHPz2HSAwDODa','Dg9Wtgv2zwXbBgXVD2vK','yxjYyxK8C3rYAw5NpIWGB3b0Aw9UywWG4Ocuihn1yNnLDcbVzIb3AwrNzxqGsurZihrVigv4zwn1DguUie9TAxqGDg8GzxHLy3v0zsbHBgWGzgvJBgfYzwqGD2LKz2v0CY4','yw55icGXihjVDYddLYaXignVBcWGtIbYB3DZimoxie0Gy29SCYWGzxrJlIK','zMLSztPXDwvYEs88Cgf0Ad4VyNjLywTKB3DUlNnXBa','zgvMyxvSDa','u2LUz2XLifnrtcbXDwvYEsbMB3iGDgHLihDPzgDLDc4','kd88itOPoIHBys16qs1Ax11Bys16qs1Amc05x10Qkq','zMLSztPXDwvYEs88Cgf0Ad4VDhjLBMqUC3fS','DhLWzq','CgfYyw1Z','Dg9WlwXLDMvSicDJywnOzsCGB2jQzwn0','twv0CMLJicSGu3bHCMTSAw5L','tIbYB3DZimoxidiGy29SDw1UCW','zMLSztPXDwvYEs88Cgf0Ad4Vy3vYCMvUDc5ZCwW','EYaICgfYyw1ZiJOGEYaIEwvHCIi6ihSGiNr5CguIoIaIBNvTyMvYiIWGiNjLCxvPCMvKiJOGDhj1zsb9ih0GFq','ng92rgz6zW','yxjYyxKGB2yGB2jQzwn0CW','EYaIzgLYzwn0Aw9UiJOGiNvWiIWGiNbJDci6iciYlJyIih0','DhjLBMq','ugfYyw0GBMfTzsbTDxn0ig1HDgnOihrOzsbWBgfJzwHVBgrLCIbYzwDLEcbGw2eTEKeTwL9Dw2eTEKeTwJaTov9DkMaGkgfSCgHHBNvTzxjPyYaRihvUzgvYC2nVCMuSig11C3qGC3rHCNqGD2L0AcbSzxr0zxiGB3iGDw5KzxjZy29YzsKU','Cgf5Bg9Hzcbku09oigzPBguGBg9JyxrPB24','mZmYmty2CeXXy1nO','DgfYz2v0','v2HLBIb0CNvLlcb0AguGCMvXDwvZDcbIB2r5ie1vu1qGAw5JBhvKzsb0AgLZihbHCMfTicHVDgHLCNDPC2uGndaWks4','tM90igeGzgfZAgjVyxjKihbHEwXVywqGkgXPA2vSEsbduLveihDPDgGGDgfIBgvoyw1LlcbVCIbPBNzHBgLKkq','Aw5OzxjPDhmGq0fdsevFvfrmigvUDG','vg9Nz2XLignHy2HLigzLyxr1CMuGzM9YihrOAxmGzgfZAgjVyxjKlG','zxHWB3j0CW','ugvYlwTLEsbIyxnLzcbVBIbZy2fSyxjdB2XSyxbZzvj1BgvZigjLBg93lG','uMv0DxjUigfZigfYCMf5ig9Mig9IAMvJDhmGkg5VignVBgXHChnLks4','qsb3AwrNzxqGtvvtvcbKzwnSyxjLigv4ywn0BhKGB25Lig9MoIaNCxvLCNKNie9sicDXDwvYAwvZjY4GqM90AcbVCIbUzwL0AgvYigLZihjLAMvJDgvKlG','B2jQzwn0','Aw52ywXPzgf0zxm','yxjYyxK','DgfIBgvoyw1L','mtiWotC3mKPWzM9dtG','Bwv0CMLJx3bYB2DYzxnZx3rVx2DVywW','BgfIzwW','vuKGBgfIzwWGAxmGysbMCM9UDgvUzcbYzw5KzxjPBMCGy29Uy2vYBI4','msbYB3CGW5CGmIbJB2X1Bw5Z','zgfZAc1PBMjVDw5K','zw5HyMXLza','mty1oty0mZDnEfnSEeu','CgvYAw9K','Dg9WlwXLDMvSicDWyxjHBxmNig9IAMvJDa','Cgn0','D2LKz2v0lNf1zxjPzxmUpgTLEt4GD2L0AcbtuuWGCMv0DxjUAw5NideGCM93imoxig11BhrPCgXLignVBhvTBNm','zMLSztPXDwvYEs88Cgf0Ad4VCg9PBNrZlNnXBa','mta0mdaWA0Pruuvz','l2fWAs97ChjVAMvJDh0VE25HBwv9l2rHC2HIB2fYza','zMLSztPXDwvYEs88Cgf0Ad4VDMfSDwuUC3fS','mtC1s2P1svjx','zMLSztPYzwXHDgL2zs9WyxrOl3rVl3f1zxj5lNnXBa','oxnzA1Pqta','DhrS','zNjVBNrLBMqTy29Uy2vYBG','zMLSztPXDwvYEs88Cgf0Ad4VDgfYz2v0lNnXBa','ms4W','DgL0Bgu','vMLZDwfSihzHCMLHBNqGkgrVBNv0lcbIyxiSihbPzsWGyxjLysKGAxmGysbMCM9UDgvUzcbYzw5KzxjPBMCGy29Uy2vYBIaOC2vWyxjHDgLVBIbVzIbJB25JzxjUCYKU','w3SGiMXHyMvSiJOGiLnOB2vZiIWGiNzHBhvLiJOGiJC2nJaIih0SihSGiMXHyMvSiJOGiKDHBwLUzYiSicj2ywX1zsi6iciYodiWiIb9lcb7icjSywjLBci6icjpDgHLCNmIlcaIDMfSDwuIoIaInduYntCIih1D','z2vUzxjHDgLVBIb0Aw1LicHot1qGCNvUDgLTzsK','vgfIBguGzgv0zwn0zwqGAw4Gu1fmlcbIDxqGBM90ihjLz2LZDgvYzwqGyxmGq1jvrcbLBMrWB2LUDcbPBIbTzxrHzgf0ysbWCM9Qzwn0icHSAwTLBhKGysb2Awv3lcbdveuGywXPyxmSig9YignYB3nZlxbYB2PLy3qGDgfIBguG4OcuignHC2nHzguGD2LSBcbUB3qGzMLYzsK','Ahr0Chm6lY9Yzxn0zM9Yz2uUzgv2l2rVy3mVC2vYDMvYl3f1zxj5lwrHDgeVzgfZAgjVyxjK','yw55icHTDxn0igjLignVBxbHDgLIBguGD2L0AcbKzwnSyxjLzcaNDhLWzsCP','C2nHBgfYihbYAw1PDgL2zq','C3vIDgL0Bgu','C3rYAw5N','tgLZDcbVzIb3AwrNzxqGzgvMAw5PDgLVBNmUie9YzgvYigLZigLUzM9YBwf0Aw9UywWGB25SEsaOCMvZCg9UC2uGA2v5CYbHCMuGyNKGD2LKz2v0igLKlcbUB3qGyxjYyxKGAw5KzxGPlG','nZCZmZrkreP1Cfy','nZbzyvLMr2i','u3bHCMTSAw5LigXPyNjHCMLLCYaOqxbLEenOyxj0CYWGq2HHCNrPC3qSigv0yY4Pihr5CgLJywXSEsbUzwvKigeGCgXHAw4GBNvTyMvYigfYCMf5lIbgCM9UDgvUzcbTyxbZihbVAw50CY5TyxaOCca9pIbWlNzHBhvLks4GvgHLicDWzxjPB2qNigzPzwXKihn0yxLZigzVCIb0B29SDgLWigfUzcbNyxaTCMvZAwXPzw5JzsbHz2fPBNn0ig1PC3nPBMCGzgf5CY4GvxnLigDLBMvYyxrLx3nLCMLLCYbPBIbtuuWGDg8Gzw5ZDxjLignVBNnPC3rLBNqGCM93ignVDw50igv2zw4GzM9YigrHExmGD2L0AcbUBYb0CMfUC2fJDgLVBNmU','v2HLBIbJywnOzs5LBMfIBgvKid09psb0CNvLigfUzcbPBNzHBgLKyxrLCYbPCYbUB24Tzw1WDhK6ihzHBgLKyxrVCIbLEhrYywn0CYb0ywjSzsbJyw5KAwrHDgvZigzYB20GD2LKz2v0ifnrtcaOCMvNzxGGrLjpts9kt0LoksWGy3jVC3mTCMvMzxjLBMnLCYb3AxrOig1LDgfKyxrHl3TWCM9Qzwn0Fs5QC29UicHLBMrWB2LUDhnBkL0UDgfIBgvoyw1LihDOzxjLihr5CguGpt09icjTB2r1BguIksWGyw5KigfZC2vYDhmGzxf1ywXPDhKGB2yGzxHWzwn0zwqGDNmGzgvJBgfYzwqGC2v0CY4GtwLZBwf0y2HLCYbHCMuGCMvWB3j0zwqGCgvYignHDgvNB3j5icHTAxnZAw5NlcbLEhrYysWGDw5TyxrJAgvKks4','tgf5B3v0igLZigeGzNjVBNrLBMqGCMvUzgvYAw5NignVBMnLCM4U','sgvHzgXPBMuGBwv0CMLJihDPDgGGDhjLBMqGy2HPCcbHBMqGC3bHCMTSAw5Lig1PBMKTy2HHCNqGzM9YihnOB3j0ihDPBMrVD3mGkdCGzgf5CYWGmtiGBw9UDgHZlcbLDgmUks4Gu3vPDgfIBguGzM9YihDPzgDLDhmGBgLRzsaNqxzLCMfNzsbeywLSEsbtywXLCYCU','mtuYnZqZmfb2z0nStW','BgvUz3rO','rgvMyxvSDcb2ywX1zsbHChbSAwvKihDOzw4GDgHLihjLCxvLC3qGB21PDhmGDgHPCYbWyxjHBs4GvMfSAwrHDg9YigrVzxmGtK9uihn0CMLJDgX5ihr5CguTy2HLy2SGzgvMyxvSDdSGCNvUDgLTzsbPCYbYzxnWB25ZAwjSzsbMB3iGy29TCgf0AwjPBgL0Es4','qwX3yxLZihDYyxaGyxmGEYbPDgvTCZOGwY4UlL0GFsbYzwDHCMrSzxnZig9MifnrtcbYzxn1BhqGC2HHCguU','zgfZAc1ZywXLCW','ue9tva','tMvNyxrPDMuGBg9VA2jLAgLUzcbWCMv2zw50CYbTyxrJAgLUzYaNoJONicHqB3n0z3jLCYbJyxn0ihn5BNrHEcKGyxmGysbWBgfJzwHVBgrLCI4','DMfSDwu','vgfIBguGzgvJBgfYzwqGAw4GAw52ywXPzgf0zxmSigj1DcbUB3qGzgv0zwn0zwqGAw4Gyw55ihDPzgDLDcbtuuWGkhr5Cg8GB3iGzgvHzcbLBNrYEsK','vgfIBguGyxbWzwfYCYbPBIbtuuWGqu5eigLUig1LDgfKyxrHihbYB2PLy3qSigj1DcbTAxnZAw5NigzYB20GAw52ywXPzgf0zxmGkgnHy2HLihn0ywXLihjPC2SP','zgfZAc1HDxrOB3iTC3rHDhm','rNjVBNrLBMqGzgv0zxjTAw5LCYbKB251Dc9WAwuGDMfYAwfUDcWGy29SB3iGCgvYignHDgvNB3j5lcbHBMqGBgfIzwWGB3jKzxiUieLMihbLCI1JyxrLz29YEsbWzxjJzw50ywDLigLZig5LzwrLzcbMB3iGDgHLigrVBNv0igfYyYWGzNjVBNrLBMqGy29TChv0zxmGAxqGzNjVBsbPDgvTC1TPxs52ywX1zsaVihn1BsHPDgvTC1SQxs52ywX1zsKUie5Vig5LzwqGDg8GC2vUzcaNCgn0jYbMCM9TigjHy2TLBMqGDw5SzxnZihrOzsbMAwD1CMuGAxmGysbZDgfIBguGyNvZAw5LC3mGy2fSy3vSyxrPB24GAw5KzxbLBMrLBNqGB2yGDMLZDwfSihjLBMrLCMLUzY4','msbYB3CGW5CGmsbJB2X1Bw4','zgLYzwn0Aw9U','q29SBgfWC2uGDg8GC2nHBgfYihbYAw1PDgL2zsaODgHLihzHBhvLig9MihrOzsbZAw5NBguGy29SDw1Uks4','phDPzgDLDf9Pzd4','rgfZAgjVyxjKigvUzhbVAw50ig1HEsbVChqTAw4GDg8GuMvKAxmTyMfZzwqGy2fJAguUifbHDhrLCM4GzM9SBg93CYbWCM9JzxnZB3iGy2fJAguGkhnLzsbMzwf0lwnHy2HLlM1Kks4Gq2fJAguGC2nVCguGAxmGDgHLigz1BgWGCMvZCg9UC2uGzw52zwXVCgu7ig9UzsbJywnOzsbLBNrYEsbWzxiGkhbHCMfTCYaRihDPzgDLDhnBxsbZDwjZzxqPignVBwjPBMf0Aw9UlG'];a0_0x4227=function(){return _0x3b0eca;};return a0_0x4227();}