@hotmeshio/long-tail 0.10.1 → 0.10.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -0
- package/build/api/bot-accounts.d.ts +4 -0
- package/build/api/bot-accounts.js +3 -1
- package/build/api/escalations/metadata.js +21 -1
- package/build/api/escalations/resolve.d.ts +8 -0
- package/build/api/escalations/resolve.js +82 -3
- package/build/api/me.d.ts +16 -0
- package/build/api/me.js +79 -0
- package/build/api/roles.d.ts +8 -0
- package/build/api/roles.js +14 -0
- package/build/bin/ltc.js +10 -1
- package/build/lib/cli/client.d.ts +4 -1
- package/build/lib/cli/client.js +4 -2
- package/build/lib/db/schemas/022_user_preferences.sql +18 -0
- package/build/lib/db/schemas/023_role_enforce_schema.sql +18 -0
- package/build/lib/typed-resolution.d.ts +35 -0
- package/build/lib/typed-resolution.js +37 -0
- package/build/modules/config.d.ts +1 -0
- package/build/modules/config.js +5 -0
- package/build/routes/bot-accounts.js +3 -1
- package/build/routes/index.js +2 -0
- package/build/routes/me.d.ts +2 -0
- package/build/routes/me.js +57 -0
- package/build/sdk/index.d.ts +10 -0
- package/build/sdk/index.js +25 -0
- package/build/services/escalation/crud.d.ts +18 -6
- package/build/services/escalation/crud.js +18 -2
- package/build/services/escalation/index.d.ts +1 -0
- package/build/services/escalation/index.js +1 -0
- package/build/services/escalation/resolver-validation.d.ts +28 -0
- package/build/services/escalation/resolver-validation.js +95 -0
- package/build/services/escalation/sql.d.ts +12 -2
- package/build/services/escalation/sql.js +22 -2
- package/build/services/iam/bots.d.ts +4 -1
- package/build/services/iam/bots.js +5 -3
- package/build/services/iam/sql.d.ts +2 -2
- package/build/services/iam/sql.js +15 -1
- package/build/services/role/enforcement-cache.d.ts +21 -0
- package/build/services/role/enforcement-cache.js +0 -0
- package/build/services/role/index.js +6 -0
- package/build/services/role/sql.d.ts +15 -2
- package/build/services/role/sql.js +21 -2
- package/build/services/role/types.d.ts +25 -0
- package/build/services/user/index.d.ts +1 -0
- package/build/services/user/index.js +5 -1
- package/build/services/user/preferences.d.ts +15 -0
- package/build/services/user/preferences.js +38 -0
- package/build/shared/form-validation/field-validator.d.ts +45 -0
- package/build/shared/form-validation/field-validator.js +214 -0
- package/build/shared/form-validation/index.d.ts +11 -0
- package/build/shared/form-validation/index.js +27 -0
- package/build/shared/form-validation/validate-resolver-payload.d.ts +41 -0
- package/build/shared/form-validation/validate-resolver-payload.js +62 -0
- package/build/shared/form-validation/x-lt-bind.d.ts +41 -0
- package/build/shared/form-validation/x-lt-bind.js +110 -0
- package/build/shared/form-validation/x-lt-help.d.ts +21 -0
- package/build/shared/form-validation/x-lt-help.js +91 -0
- package/build/shared/form-validation/x-lt-show-if.d.ts +7 -0
- package/build/shared/form-validation/x-lt-show-if.js +84 -0
- package/build/system/mcp-servers/admin/schemas.d.ts +29 -0
- package/build/system/mcp-servers/admin/schemas.js +7 -0
- package/build/system/mcp-servers/admin/users.js +8 -3
- package/build/system/mcp-servers/human-queue-schemas.d.ts +4 -4
- package/build/system/mcp-servers/human-queue-schemas.js +4 -1
- package/build/system/mcp-servers/human-queue.js +41 -2
- package/build/system/seed/tool-manifests-admin.d.ts +167 -0
- package/build/system/seed/tool-manifests-admin.js +1 -1
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/sdk.d.ts +6 -1
- package/build/types/validation.d.ts +43 -0
- package/build/types/validation.js +28 -0
- package/dashboard/dist/assets/{AdminDashboard-D7Amh-EJ.js → AdminDashboard-DQLGybZ9.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-D7Amh-EJ.js.map → AdminDashboard-DQLGybZ9.js.map} +1 -1
- package/dashboard/dist/assets/AgentConfigPage-4JgMpuYJ.js +16 -0
- package/dashboard/dist/assets/AgentConfigPage-4JgMpuYJ.js.map +1 -0
- package/dashboard/dist/assets/{AgentDetailPage-Dy3Qvlfk.js → AgentDetailPage-DCgKokgX.js} +3 -3
- package/dashboard/dist/assets/{AgentDetailPage-Dy3Qvlfk.js.map → AgentDetailPage-DCgKokgX.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-D7WePZ0o.js → AgentsPage-BK_8VKZO.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-D7WePZ0o.js.map → AgentsPage-BK_8VKZO.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-Dp1rsIHC.js +2 -0
- package/dashboard/dist/assets/AvailableEscalationsPage-Dp1rsIHC.js.map +1 -0
- package/dashboard/dist/assets/BotPicker-DvUnYEJk.js +2 -0
- package/dashboard/dist/assets/BotPicker-DvUnYEJk.js.map +1 -0
- package/dashboard/dist/assets/{CapabilitiesPage-3VTLEPGO.js → CapabilitiesPage-CJ5ldY8w.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-3VTLEPGO.js.map → CapabilitiesPage-CJ5ldY8w.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-BYgeZz9M.js → CollapsibleSection-BXOpfs3z.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-BYgeZz9M.js.map → CollapsibleSection-BXOpfs3z.js.map} +1 -1
- package/dashboard/dist/assets/CountdownTimer-D20FNwS-.js +2 -0
- package/dashboard/dist/assets/CountdownTimer-D20FNwS-.js.map +1 -0
- package/dashboard/dist/assets/{CredentialsPage-CaH3J3SE.js → CredentialsPage-vZ2psNnd.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-CaH3J3SE.js.map → CredentialsPage-vZ2psNnd.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-Bma_4AC2.js → CronLabel-KRkqNSYs.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-Bma_4AC2.js.map → CronLabel-KRkqNSYs.js.map} +1 -1
- package/dashboard/dist/assets/{CustomDurationPicker-BxyjEX27.js → CustomDurationPicker-C-vGsrFl.js} +2 -2
- package/dashboard/dist/assets/CustomDurationPicker-C-vGsrFl.js.map +1 -0
- package/dashboard/dist/assets/{DropZone-zxH-9B8c.js → DropZone-C3NdMtwK.js} +2 -2
- package/dashboard/dist/assets/{DropZone-zxH-9B8c.js.map → DropZone-C3NdMtwK.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-Dr1cEwoi.js → ElapsedCell-DNH-XHOS.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-Dr1cEwoi.js.map → ElapsedCell-DNH-XHOS.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationListSchemaPage-CLdsR-hK.js → EscalationListSchemaPage-BIW1qxjC.js} +3 -3
- package/dashboard/dist/assets/{EscalationListSchemaPage-CLdsR-hK.js.map → EscalationListSchemaPage-BIW1qxjC.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationSchemaPage-DnEnvolM.js → EscalationSchemaPage-Fj2IwQXQ.js} +2 -2
- package/dashboard/dist/assets/{EscalationSchemaPage-DnEnvolM.js.map → EscalationSchemaPage-Fj2IwQXQ.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-BH8cxgLI.js → EscalationsOverview-BVUx0vZH.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-BH8cxgLI.js.map → EscalationsOverview-BVUx0vZH.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-p-xsfYYg.js → EventTable-Dm4zZaRl.js} +2 -2
- package/dashboard/dist/assets/{EventTable-p-xsfYYg.js.map → EventTable-Dm4zZaRl.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-Cf9c_LJ-.js → GraphInvokePage-D4f074qh.js} +2 -2
- package/dashboard/dist/assets/GraphInvokePage-D4f074qh.js.map +1 -0
- package/dashboard/dist/assets/HomePage-DQ0bb_M_.js +2 -0
- package/dashboard/dist/assets/HomePage-DQ0bb_M_.js.map +1 -0
- package/dashboard/dist/assets/ListToolbar-CItVkLQ-.js +2 -0
- package/dashboard/dist/assets/{ListToolbar-CDlol8XJ.js.map → ListToolbar-CItVkLQ-.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-ChZ36p9t.js → McpOverview-ByYVDldF.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-ChZ36p9t.js.map → McpOverview-ByYVDldF.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-X4j_jm3r.js → McpQueryDetailPage-DYuIdJiH.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-X4j_jm3r.js.map → McpQueryDetailPage-DYuIdJiH.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-C30HsQfZ.js → McpQueryPage-C9C4mPC0.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-C30HsQfZ.js.map → McpQueryPage-C9C4mPC0.js.map} +1 -1
- package/dashboard/dist/assets/McpRunDetailPage-CWHKhx8n.js +2 -0
- package/dashboard/dist/assets/McpRunDetailPage-CWHKhx8n.js.map +1 -0
- package/dashboard/dist/assets/{McpRunsPage-BHZHCwMM.js → McpRunsPage-DMamEjjr.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-BHZHCwMM.js.map → McpRunsPage-DMamEjjr.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-BQNMfY0m.js → NamespacePill-B3YOa1xo.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-BQNMfY0m.js.map → NamespacePill-B3YOa1xo.js.map} +1 -1
- package/dashboard/dist/assets/OperationsPage-CVPDugQz.js +2 -0
- package/dashboard/dist/assets/OperationsPage-CVPDugQz.js.map +1 -0
- package/dashboard/dist/assets/{OperatorDashboard-BGJrxLMU.js → OperatorDashboard-CK-4GREp.js} +2 -2
- package/dashboard/dist/assets/{OperatorDashboard-BGJrxLMU.js.map → OperatorDashboard-CK-4GREp.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-BIY1YvKR.js → PageHeader-poyTWcUA.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-BIY1YvKR.js.map → PageHeader-poyTWcUA.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-TUyglVW0.js → PageHeaderWithStats-CAmspbl9.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-TUyglVW0.js.map → PageHeaderWithStats-CAmspbl9.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-Bcp3GKFo.js → ProcessDetailPage-BBCwv1bQ.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-Bcp3GKFo.js.map → ProcessDetailPage-BBCwv1bQ.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-DPrvuLoU.js → ProcessesListPage-m70xmTZx.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-DPrvuLoU.js.map → ProcessesListPage-m70xmTZx.js.map} +1 -1
- package/dashboard/dist/assets/RoleDetailPage-l865Cx3n.js +8 -0
- package/dashboard/dist/assets/RoleDetailPage-l865Cx3n.js.map +1 -0
- package/dashboard/dist/assets/RolePill-C0SWz9X7.js +2 -0
- package/dashboard/dist/assets/RolePill-C0SWz9X7.js.map +1 -0
- package/dashboard/dist/assets/RolesPage-Wzfjz7Wc.js +2 -0
- package/dashboard/dist/assets/RolesPage-Wzfjz7Wc.js.map +1 -0
- package/dashboard/dist/assets/RunAsSelector-DlnIolTf.js +2 -0
- package/dashboard/dist/assets/{RunAsSelector-Z2Yunqdf.js.map → RunAsSelector-DlnIolTf.js.map} +1 -1
- package/dashboard/dist/assets/{SlidePanel-DK4sWXmp.js → SlidePanel-Bm9B6zIA.js} +2 -2
- package/dashboard/dist/assets/{SlidePanel-DK4sWXmp.js.map → SlidePanel-Bm9B6zIA.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-CeLsnCIS.js → StickyPagination-BHF5fTt5.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-CeLsnCIS.js.map → StickyPagination-BHF5fTt5.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-DWs7XkmH.js → StreamMessageDetail-Dgp1lzay.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-DWs7XkmH.js.map → StreamMessageDetail-Dgp1lzay.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-Dxl9a51q.js → SwimlaneTimeline-BlptZ8lr.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-Dxl9a51q.js.map → SwimlaneTimeline-BlptZ8lr.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-CK-JhBG2.js → TagInput-CM3AV5Ky.js} +2 -2
- package/dashboard/dist/assets/{TagInput-CK-JhBG2.js.map → TagInput-CM3AV5Ky.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-DVr31zDE.js → TaskDetailPage-f5K9dVPG.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-DVr31zDE.js.map → TaskDetailPage-f5K9dVPG.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-C_Vo2Clc.js → TaskQueuePill-BZYmqdcv.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-C_Vo2Clc.js.map → TaskQueuePill-BZYmqdcv.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-DNGp5lfi.js → TasksListPage-BPyPG6UY.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-DNGp5lfi.js.map → TasksListPage-BPyPG6UY.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-j2Q4w4lY.js → TimeAgo-CBaGbueK.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-j2Q4w4lY.js.map → TimeAgo-CBaGbueK.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-BLEfob2C.js → TimestampCell-Z5kDrR9U.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-BLEfob2C.js.map → TimestampCell-Z5kDrR9U.js.map} +1 -1
- package/dashboard/dist/assets/ToolPill-D_-m3lUO.js +2 -0
- package/dashboard/dist/assets/{ToolPill-BCi101Jh.js.map → ToolPill-D_-m3lUO.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-ZJZT_mDP.js → ToolTestPanel-DFrP_aMx.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-ZJZT_mDP.js.map → ToolTestPanel-DFrP_aMx.js.map} +1 -1
- package/dashboard/dist/assets/TopicDetailPage-Cj9qXnTW.js +9 -0
- package/dashboard/dist/assets/TopicDetailPage-Cj9qXnTW.js.map +1 -0
- package/dashboard/dist/assets/{TopicsPage-Ctb_85Tw.js → TopicsPage-Dyq6niOv.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-Ctb_85Tw.js.map → TopicsPage-Dyq6niOv.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-CkFpUqVK.js → UserName-DOX2P-8M.js} +2 -2
- package/dashboard/dist/assets/{UserName-CkFpUqVK.js.map → UserName-DOX2P-8M.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-Cb_dSIoq.js +2 -0
- package/dashboard/dist/assets/WorkflowExecutionPage-Cb_dSIoq.js.map +1 -0
- package/dashboard/dist/assets/WorkflowPill-CyYYPB7b.js +2 -0
- package/dashboard/dist/assets/{WorkflowPill-DHxrAkp3.js.map → WorkflowPill-CyYYPB7b.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-DOyZbqzf.js → WorkflowsDashboard-DJpMO5IY.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-DOyZbqzf.js.map → WorkflowsDashboard-DJpMO5IY.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-CRyHvUBx.js → WorkflowsOverview-CCC_KaXz.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-CRyHvUBx.js.map → WorkflowsOverview-CCC_KaXz.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DRBmQXY6.js → YamlWorkflowDetailPage-C3ntUyoZ.js} +11 -11
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DRBmQXY6.js.map → YamlWorkflowDetailPage-C3ntUyoZ.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-HnI-9eBW.js → YamlWorkflowsPage-ju-9JDXg.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-HnI-9eBW.js.map → YamlWorkflowsPage-ju-9JDXg.js.map} +1 -1
- package/dashboard/dist/assets/{agents-BA2CvQO9.js → agents-gs9fea8r.js} +2 -2
- package/dashboard/dist/assets/{agents-BA2CvQO9.js.map → agents-gs9fea8r.js.map} +1 -1
- package/dashboard/dist/assets/bots-DYxAHrob.js +2 -0
- package/dashboard/dist/assets/bots-DYxAHrob.js.map +1 -0
- package/dashboard/dist/assets/{capabilities-BYBKLRRI.js → capabilities-BJoxz6DJ.js} +2 -2
- package/dashboard/dist/assets/{capabilities-BYBKLRRI.js.map → capabilities-BJoxz6DJ.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-H0Ljhb-1.js → controlplane-BeDZgDDf.js} +2 -2
- package/dashboard/dist/assets/{controlplane-H0Ljhb-1.js.map → controlplane-BeDZgDDf.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-CZ4fl0IE.js +2 -0
- package/dashboard/dist/assets/escalation-columns-CZ4fl0IE.js.map +1 -0
- package/dashboard/dist/assets/index-BMM3Xug7.js +2 -0
- package/dashboard/dist/assets/index-BMM3Xug7.js.map +1 -0
- package/dashboard/dist/assets/{index-BXrm76Hm.js → index-BMXkxgnK.js} +2 -2
- package/dashboard/dist/assets/{index-BXrm76Hm.js.map → index-BMXkxgnK.js.map} +1 -1
- package/dashboard/dist/assets/{index-CWkAN6wc.js → index-Bj1Mjjiv.js} +2 -2
- package/dashboard/dist/assets/{index-CWkAN6wc.js.map → index-Bj1Mjjiv.js.map} +1 -1
- package/dashboard/dist/assets/index-BqVb13tE.js +5 -0
- package/dashboard/dist/assets/index-BqVb13tE.js.map +1 -0
- package/dashboard/dist/assets/{index-YWvrMcqh.js → index-CGrtjUdj.js} +2 -2
- package/dashboard/dist/assets/{index-YWvrMcqh.js.map → index-CGrtjUdj.js.map} +1 -1
- package/dashboard/dist/assets/{index-DSjih1iX.js → index-CQ8cd4JZ.js} +2 -2
- package/dashboard/dist/assets/{index-DSjih1iX.js.map → index-CQ8cd4JZ.js.map} +1 -1
- package/dashboard/dist/assets/{index-D1Q8WfGN.js → index-CruL35yU.js} +2 -2
- package/dashboard/dist/assets/{index-D1Q8WfGN.js.map → index-CruL35yU.js.map} +1 -1
- package/dashboard/dist/assets/index-DAM1Dm1F.js +2 -0
- package/dashboard/dist/assets/index-DAM1Dm1F.js.map +1 -0
- package/dashboard/dist/assets/{index-CP6D_Gtw.js → index-DZ7PWRg1.js} +2 -2
- package/dashboard/dist/assets/{index-CP6D_Gtw.js.map → index-DZ7PWRg1.js.map} +1 -1
- package/dashboard/dist/assets/{index-BI-9M7z3.js → index-DfbrPOP3.js} +2 -2
- package/dashboard/dist/assets/{index-BI-9M7z3.js.map → index-DfbrPOP3.js.map} +1 -1
- package/dashboard/dist/assets/index-DwuM7Fka.js +5 -0
- package/dashboard/dist/assets/index-DwuM7Fka.js.map +1 -0
- package/dashboard/dist/assets/index-DycfrrTK.js +63 -0
- package/dashboard/dist/assets/index-DycfrrTK.js.map +1 -0
- package/dashboard/dist/assets/{index-Dx6-4esw.js → index-tbvwCdvQ.js} +2 -2
- package/dashboard/dist/assets/{index-Dx6-4esw.js.map → index-tbvwCdvQ.js.map} +1 -1
- package/dashboard/dist/assets/index-zP2ncT4m.css +1 -0
- package/dashboard/dist/assets/{knowledge-BVbElRHf.js → knowledge-DZnI1Fdk.js} +2 -2
- package/dashboard/dist/assets/{knowledge-BVbElRHf.js.map → knowledge-DZnI1Fdk.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-CstDdowJ.js → mcp-BB_m6UyI.js} +2 -2
- package/dashboard/dist/assets/{mcp-CstDdowJ.js.map → mcp-BB_m6UyI.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-DHUs0N8H.js → mcp-query-BTSWn0de.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-DHUs0N8H.js.map → mcp-query-BTSWn0de.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-BDmAzE3U.js → pipelines-CvGQLyUz.js} +2 -2
- package/dashboard/dist/assets/{pipelines-BDmAzE3U.js.map → pipelines-CvGQLyUz.js.map} +1 -1
- package/dashboard/dist/assets/task-queues-7-sHKPsN.js +2 -0
- package/dashboard/dist/assets/task-queues-7-sHKPsN.js.map +1 -0
- package/dashboard/dist/assets/{tasks--qZocuG-.js → tasks-BbgOhCVm.js} +2 -2
- package/dashboard/dist/assets/{tasks--qZocuG-.js.map → tasks-BbgOhCVm.js.map} +1 -1
- package/dashboard/dist/assets/{topics-P3jkjMsx.js → topics-D9usuYHv.js} +2 -2
- package/dashboard/dist/assets/{topics-P3jkjMsx.js.map → topics-D9usuYHv.js.map} +1 -1
- package/dashboard/dist/assets/{useEventHooks-CqH8Pasj.js → useEventHooks-DxnqYVPg.js} +2 -2
- package/dashboard/dist/assets/{useEventHooks-CqH8Pasj.js.map → useEventHooks-DxnqYVPg.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-aI3tPfo3.js → useNamespace-CBAn0o5z.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-aI3tPfo3.js.map → useNamespace-CBAn0o5z.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-CPVyuW8i.js → useYamlActivityEvents-blOlKGDO.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-CPVyuW8i.js.map → useYamlActivityEvents-blOlKGDO.js.map} +1 -1
- package/dashboard/dist/assets/{users-BrcmIH7O.js → users-CYVJHgNM.js} +2 -2
- package/dashboard/dist/assets/{users-BrcmIH7O.js.map → users-CYVJHgNM.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-Qk9qLjF_.js → vendor-icons-B98-XxNc.js} +147 -132
- package/dashboard/dist/assets/vendor-icons-B98-XxNc.js.map +1 -0
- package/dashboard/dist/assets/{workflows-DPhX5L5C.js → workflows-csQAJF0G.js} +2 -2
- package/dashboard/dist/assets/{workflows-DPhX5L5C.js.map → workflows-csQAJF0G.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-KZI2cufF.js → yaml-workflows-CctsTDby.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-KZI2cufF.js.map → yaml-workflows-CctsTDby.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/api/http/escalations.md +16 -1
- package/docs/api/http/roles.md +4 -0
- package/docs/api/http/users.md +39 -0
- package/docs/api/sdk/roles.md +5 -0
- package/docs/api/sdk/users.md +39 -0
- package/docs/hitl/pinned-views.md +45 -0
- package/docs/hitl/x-lt-list-schema.md +37 -2
- package/docs/hitl/x-lt-show-if.md +11 -0
- package/docs/hitl/x-lt-widget.md +22 -0
- package/docs/hitl-guide.md +9 -5
- package/docs/schema-enforcement.md +149 -0
- package/package.json +1 -1
- package/dashboard/dist/assets/AgentConfigPage-CW2udbSO.js +0 -16
- package/dashboard/dist/assets/AgentConfigPage-CW2udbSO.js.map +0 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-Dx51E6s_.js +0 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-Dx51E6s_.js.map +0 -1
- package/dashboard/dist/assets/BotPicker-KebTxTDi.js +0 -2
- package/dashboard/dist/assets/BotPicker-KebTxTDi.js.map +0 -1
- package/dashboard/dist/assets/CountdownTimer-DFqEYmiF.js +0 -2
- package/dashboard/dist/assets/CountdownTimer-DFqEYmiF.js.map +0 -1
- package/dashboard/dist/assets/CustomDurationPicker-BxyjEX27.js.map +0 -1
- package/dashboard/dist/assets/GraphInvokePage-Cf9c_LJ-.js.map +0 -1
- package/dashboard/dist/assets/HomePage-BdCtKXAV.js +0 -2
- package/dashboard/dist/assets/HomePage-BdCtKXAV.js.map +0 -1
- package/dashboard/dist/assets/ListToolbar-CDlol8XJ.js +0 -2
- package/dashboard/dist/assets/McpRunDetailPage-18d7Iqfy.js +0 -2
- package/dashboard/dist/assets/McpRunDetailPage-18d7Iqfy.js.map +0 -1
- package/dashboard/dist/assets/OperationsPage-GMojwL3q.js +0 -2
- package/dashboard/dist/assets/OperationsPage-GMojwL3q.js.map +0 -1
- package/dashboard/dist/assets/RoleDetailPage-Ws3umQKQ.js +0 -8
- package/dashboard/dist/assets/RoleDetailPage-Ws3umQKQ.js.map +0 -1
- package/dashboard/dist/assets/RolePill-4LO_CuO3.js +0 -2
- package/dashboard/dist/assets/RolePill-4LO_CuO3.js.map +0 -1
- package/dashboard/dist/assets/RolesPage-D3NNU92h.js +0 -2
- package/dashboard/dist/assets/RolesPage-D3NNU92h.js.map +0 -1
- package/dashboard/dist/assets/RunAsSelector-Z2Yunqdf.js +0 -2
- package/dashboard/dist/assets/ToolPill-BCi101Jh.js +0 -2
- package/dashboard/dist/assets/TopicDetailPage-ynUkO1A8.js +0 -9
- package/dashboard/dist/assets/TopicDetailPage-ynUkO1A8.js.map +0 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-CSw2aQJC.js +0 -2
- package/dashboard/dist/assets/WorkflowExecutionPage-CSw2aQJC.js.map +0 -1
- package/dashboard/dist/assets/WorkflowPill-DHxrAkp3.js +0 -2
- package/dashboard/dist/assets/bots-DHHnoo8x.js +0 -2
- package/dashboard/dist/assets/bots-DHHnoo8x.js.map +0 -1
- package/dashboard/dist/assets/escalation-columns-BV3Ztn7D.js +0 -2
- package/dashboard/dist/assets/escalation-columns-BV3Ztn7D.js.map +0 -1
- package/dashboard/dist/assets/index-B4FaugP1.js +0 -2
- package/dashboard/dist/assets/index-B4FaugP1.js.map +0 -1
- package/dashboard/dist/assets/index-BeFjLCIQ.js +0 -5
- package/dashboard/dist/assets/index-BeFjLCIQ.js.map +0 -1
- package/dashboard/dist/assets/index-BrEcQQ1p.css +0 -1
- package/dashboard/dist/assets/index-CVMA5PVQ.js +0 -2
- package/dashboard/dist/assets/index-CVMA5PVQ.js.map +0 -1
- package/dashboard/dist/assets/index-O2wWcCLo.js +0 -5
- package/dashboard/dist/assets/index-O2wWcCLo.js.map +0 -1
- package/dashboard/dist/assets/index-iX8HLLB9.js +0 -63
- package/dashboard/dist/assets/index-iX8HLLB9.js.map +0 -1
- package/dashboard/dist/assets/vendor-icons-Qk9qLjF_.js.map +0 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
// ─── Escalation chain queries ───────────────────────────────────────────────
|
|
3
3
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
-
exports.COUNT_ACTIVE_ESCALATION_REFS = exports.COUNT_WORKFLOW_REFS = exports.COUNT_CHAIN_REFS = exports.COUNT_USER_ROLE_REFS = exports.LIST_ROLES_WITH_DETAILS = exports.GET_ROLE_LIST_SCHEMA_CURRENT = exports.GET_ROLE_LIST_SCHEMA_VERSION = exports.LIST_ROLE_LIST_SCHEMA_VERSIONS = exports.GET_ROLE_SCHEMA_CURRENT = exports.GET_ROLE_SCHEMA_VERSION = exports.LIST_ROLE_SCHEMA_VERSIONS = exports.GET_ROLE_UPSTREAMS = exports.GET_ROLE_METADATA_SCHEMA = exports.GET_ROLE_FORM_SCHEMA = exports.UPDATE_ROLE_METADATA = exports.DELETE_ROLE = exports.LIST_ROLES = exports.ENSURE_ROLE_EXISTS = exports.CHECK_ESCALATION_CHAIN_EXISTS = exports.DELETE_ESCALATION_CHAINS_BY_SOURCE = exports.DELETE_ESCALATION_CHAIN = exports.ADD_ESCALATION_CHAIN = exports.INSERT_ESCALATION_CHAIN = exports.GET_ALL_ESCALATION_CHAINS = exports.GET_ESCALATION_TARGETS = void 0;
|
|
4
|
+
exports.COUNT_ACTIVE_ESCALATION_REFS = exports.COUNT_WORKFLOW_REFS = exports.COUNT_CHAIN_REFS = exports.COUNT_USER_ROLE_REFS = exports.LIST_ROLES_WITH_DETAILS = exports.GET_ROLE_LIST_SCHEMA_CURRENT = exports.GET_ROLE_LIST_SCHEMA_VERSION = exports.LIST_ROLE_LIST_SCHEMA_VERSIONS = exports.GET_ROLE_SCHEMA_CURRENT = exports.GET_ROLE_SCHEMA_VERSION = exports.LIST_ROLE_SCHEMA_VERSIONS = exports.GET_ROLE_UPSTREAMS = exports.GET_ROLE_METADATA_SCHEMA = exports.LIST_ENFORCING_ROLES = exports.GET_ROLE_FORM_SCHEMA = exports.UPDATE_ROLE_METADATA = exports.DELETE_ROLE = exports.LIST_ROLES = exports.ENSURE_ROLE_EXISTS = exports.CHECK_ESCALATION_CHAIN_EXISTS = exports.DELETE_ESCALATION_CHAINS_BY_SOURCE = exports.DELETE_ESCALATION_CHAIN = exports.ADD_ESCALATION_CHAIN = exports.INSERT_ESCALATION_CHAIN = exports.GET_ALL_ESCALATION_CHAINS = exports.GET_ESCALATION_TARGETS = void 0;
|
|
5
5
|
exports.GET_ESCALATION_TARGETS = `
|
|
6
6
|
SELECT target_role
|
|
7
7
|
FROM lt_config_role_escalations
|
|
@@ -73,6 +73,13 @@ exports.DELETE_ROLE = `
|
|
|
73
73
|
* of the form/metadata pair: its own current_list_schema_version and its own
|
|
74
74
|
* snapshot table (lt_role_list_schemas), so a list-view edit never advances the
|
|
75
75
|
* resolve form's version. It shares $26 as the change summary.
|
|
76
|
+
*
|
|
77
|
+
* default_pins ($31 = provided sentinel, $32 = value) is unversioned config:
|
|
78
|
+
* the pinned-view seeds a role hands its members.
|
|
79
|
+
*
|
|
80
|
+
* enforce_schema ($33 = provided sentinel, $34 = value) is unversioned config:
|
|
81
|
+
* the server-side resolver-validation opt-in. The service layer invalidates
|
|
82
|
+
* the enforcement cache after this statement commits.
|
|
76
83
|
*/
|
|
77
84
|
exports.UPDATE_ROLE_METADATA = `
|
|
78
85
|
WITH updated AS (
|
|
@@ -91,6 +98,8 @@ exports.UPDATE_ROLE_METADATA = `
|
|
|
91
98
|
= CASE WHEN $22::boolean THEN $23::numeric ELSE priority_threshold_minutes END,
|
|
92
99
|
priority_facet = CASE WHEN $24::boolean THEN $25 ELSE priority_facet END,
|
|
93
100
|
list_schema = CASE WHEN $29::boolean THEN $30::jsonb ELSE list_schema END,
|
|
101
|
+
default_pins = CASE WHEN $31::boolean THEN $32::jsonb ELSE default_pins END,
|
|
102
|
+
enforce_schema = CASE WHEN $33::boolean THEN COALESCE($34::boolean, false) ELSE enforce_schema END,
|
|
94
103
|
current_schema_version = CASE
|
|
95
104
|
WHEN ($6::boolean AND $7::jsonb IS DISTINCT FROM form_schema)
|
|
96
105
|
OR ($8::boolean AND $9::jsonb IS DISTINCT FROM metadata_schema)
|
|
@@ -105,7 +114,8 @@ exports.UPDATE_ROLE_METADATA = `
|
|
|
105
114
|
role, title, description, form_schema, metadata_schema, properties,
|
|
106
115
|
ops_visible, parent_role, sla_minutes, target_per_hour, worker_count,
|
|
107
116
|
priority_threshold_minutes, priority_facet,
|
|
108
|
-
current_schema_version, list_schema, current_list_schema_version
|
|
117
|
+
current_schema_version, list_schema, current_list_schema_version,
|
|
118
|
+
default_pins, enforce_schema
|
|
109
119
|
), snapshot AS (
|
|
110
120
|
INSERT INTO lt_role_schemas (role, version, form_schema, metadata_schema, change_summary)
|
|
111
121
|
SELECT role, current_schema_version, form_schema, metadata_schema, $26
|
|
@@ -130,6 +140,13 @@ exports.UPDATE_ROLE_METADATA = `
|
|
|
130
140
|
SELECT * FROM updated`;
|
|
131
141
|
exports.GET_ROLE_FORM_SCHEMA = `
|
|
132
142
|
SELECT form_schema FROM lt_roles WHERE role = $1`;
|
|
143
|
+
/**
|
|
144
|
+
* The set of roles with server-side resolver validation turned on. Small and
|
|
145
|
+
* slow-changing — the enforcement cache holds it under a short TTL so the
|
|
146
|
+
* resolve hot path costs zero SQL when no role (or no involved role) enforces.
|
|
147
|
+
*/
|
|
148
|
+
exports.LIST_ENFORCING_ROLES = `
|
|
149
|
+
SELECT role FROM lt_roles WHERE enforce_schema`;
|
|
133
150
|
exports.GET_ROLE_METADATA_SCHEMA = `
|
|
134
151
|
SELECT metadata_schema FROM lt_roles WHERE role = $1`;
|
|
135
152
|
exports.GET_ROLE_UPSTREAMS = `
|
|
@@ -239,6 +256,8 @@ exports.LIST_ROLES_WITH_DETAILS = `
|
|
|
239
256
|
r.current_schema_version,
|
|
240
257
|
r.list_schema,
|
|
241
258
|
r.current_list_schema_version,
|
|
259
|
+
r.default_pins,
|
|
260
|
+
r.enforce_schema,
|
|
242
261
|
COALESCE(up.ups, '{}') AS upstream_roles,
|
|
243
262
|
COALESCE(uc.cnt, 0) AS user_count,
|
|
244
263
|
COALESCE(cc.cnt, 0) AS chain_count,
|
|
@@ -54,6 +54,24 @@ export interface RoleDetail {
|
|
|
54
54
|
list_schema: Record<string, any> | null;
|
|
55
55
|
/** Version of the live list_schema; null until the role first carries one. */
|
|
56
56
|
current_list_schema_version: number | null;
|
|
57
|
+
/**
|
|
58
|
+
* When true, every resolve surface validates the submitted resolverPayload
|
|
59
|
+
* against the escalation's resolved form schema (same pass the dashboard
|
|
60
|
+
* runs) and rejects violations with 422 before any state changes. Opt-in
|
|
61
|
+
* per role; false leaves resolution behavior unchanged.
|
|
62
|
+
*/
|
|
63
|
+
enforce_schema: boolean;
|
|
64
|
+
/**
|
|
65
|
+
* Pinned-view seeds this role hands its members: [{ label, url, badge? }].
|
|
66
|
+
* On first render, a member without an own pin of the same label sees these
|
|
67
|
+
* in their Pinned nav section (marked role-provided); users promote, hide,
|
|
68
|
+
* or reorder them through their own preferences. URLs only — never data.
|
|
69
|
+
*/
|
|
70
|
+
default_pins: {
|
|
71
|
+
label: string;
|
|
72
|
+
url: string;
|
|
73
|
+
badge?: boolean;
|
|
74
|
+
}[] | null;
|
|
57
75
|
/**
|
|
58
76
|
* Roles this station draws input from that live in OTHER sequences.
|
|
59
77
|
* parent_role is the single "prior step" placing the role in one sequence;
|
|
@@ -109,6 +127,11 @@ export interface UpdateRoleInput {
|
|
|
109
127
|
metadata_schema?: Record<string, any> | null;
|
|
110
128
|
/** JSON contract that richly formats this role's escalation list page. */
|
|
111
129
|
list_schema?: Record<string, any> | null;
|
|
130
|
+
default_pins?: {
|
|
131
|
+
label: string;
|
|
132
|
+
url: string;
|
|
133
|
+
badge?: boolean;
|
|
134
|
+
}[] | null;
|
|
112
135
|
properties?: Record<string, any> | null;
|
|
113
136
|
ops_visible?: boolean;
|
|
114
137
|
parent_role?: string | null;
|
|
@@ -117,6 +140,8 @@ export interface UpdateRoleInput {
|
|
|
117
140
|
worker_count?: number | null;
|
|
118
141
|
priority_threshold_minutes?: number | null;
|
|
119
142
|
priority_facet?: string | null;
|
|
143
|
+
/** Turn server-side resolver schema validation on/off for this role. */
|
|
144
|
+
enforce_schema?: boolean;
|
|
120
145
|
/**
|
|
121
146
|
* Replace the upstream-input set (omitted = preserve; null or [] = clear).
|
|
122
147
|
* Every entry must name an existing role other than this one.
|
|
@@ -5,3 +5,4 @@ export { isSuperAdmin, isGroupAdmin, canManageRole, hasGlobalEscalationAccess, h
|
|
|
5
5
|
export { READ_SCOPES, WRITE_SCOPES, DEFAULT_READ_SCOPE, DEFAULT_WRITE_SCOPE, isValidReadScope, isValidWriteScope, isValidScopePair, effectiveScope, } from './scope';
|
|
6
6
|
export { verifyPassword } from './auth';
|
|
7
7
|
export { seedAdmin } from './seed-admin';
|
|
8
|
+
export { getPreferences, patchPreferences, PREFERENCES_MAX_BYTES } from './preferences';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.seedAdmin = exports.verifyPassword = exports.effectiveScope = exports.isValidScopePair = exports.isValidWriteScope = exports.isValidReadScope = exports.DEFAULT_WRITE_SCOPE = exports.DEFAULT_READ_SCOPE = exports.WRITE_SCOPES = exports.READ_SCOPES = exports.getRoleWriteScope = exports.getRoleScope = exports.hasRolesAsAdmin = exports.hasGlobalEscalationAccess = exports.canManageRole = exports.isGroupAdmin = exports.isSuperAdmin = exports.hasRoleType = exports.hasRole = exports.getUserRoles = exports.removeUserRole = exports.addUserRole = exports.isValidRoleType = exports.listUsers = exports.deleteUser = exports.updateUser = exports.getUserByEmail = exports.getUserByExternalId = exports.getUser = exports.createUser = exports.VALID_ROLE_TYPES = void 0;
|
|
3
|
+
exports.PREFERENCES_MAX_BYTES = exports.patchPreferences = exports.getPreferences = exports.seedAdmin = exports.verifyPassword = exports.effectiveScope = exports.isValidScopePair = exports.isValidWriteScope = exports.isValidReadScope = exports.DEFAULT_WRITE_SCOPE = exports.DEFAULT_READ_SCOPE = exports.WRITE_SCOPES = exports.READ_SCOPES = exports.getRoleWriteScope = exports.getRoleScope = exports.hasRolesAsAdmin = exports.hasGlobalEscalationAccess = exports.canManageRole = exports.isGroupAdmin = exports.isSuperAdmin = exports.hasRoleType = exports.hasRole = exports.getUserRoles = exports.removeUserRole = exports.addUserRole = exports.isValidRoleType = exports.listUsers = exports.deleteUser = exports.updateUser = exports.getUserByEmail = exports.getUserByExternalId = exports.getUser = exports.createUser = exports.VALID_ROLE_TYPES = void 0;
|
|
4
4
|
var types_1 = require("./types");
|
|
5
5
|
Object.defineProperty(exports, "VALID_ROLE_TYPES", { enumerable: true, get: function () { return types_1.VALID_ROLE_TYPES; } });
|
|
6
6
|
var crud_1 = require("./crud");
|
|
@@ -39,3 +39,7 @@ var auth_1 = require("./auth");
|
|
|
39
39
|
Object.defineProperty(exports, "verifyPassword", { enumerable: true, get: function () { return auth_1.verifyPassword; } });
|
|
40
40
|
var seed_admin_1 = require("./seed-admin");
|
|
41
41
|
Object.defineProperty(exports, "seedAdmin", { enumerable: true, get: function () { return seed_admin_1.seedAdmin; } });
|
|
42
|
+
var preferences_1 = require("./preferences");
|
|
43
|
+
Object.defineProperty(exports, "getPreferences", { enumerable: true, get: function () { return preferences_1.getPreferences; } });
|
|
44
|
+
Object.defineProperty(exports, "patchPreferences", { enumerable: true, get: function () { return preferences_1.patchPreferences; } });
|
|
45
|
+
Object.defineProperty(exports, "PREFERENCES_MAX_BYTES", { enumerable: true, get: function () { return preferences_1.PREFERENCES_MAX_BYTES; } });
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-user preferences — a generic JSON store for presentation state (pinned
|
|
3
|
+
* views are the first tenant, not the schema). Preferences carry URLs and UI
|
|
4
|
+
* choices only, never data and never authorization.
|
|
5
|
+
*/
|
|
6
|
+
/** Server-side ceiling on the stored document (the API rejects beyond it). */
|
|
7
|
+
export declare const PREFERENCES_MAX_BYTES: number;
|
|
8
|
+
export declare function getPreferences(userId: string): Promise<Record<string, unknown>>;
|
|
9
|
+
/**
|
|
10
|
+
* Shallow-merge `patch` into the user's preferences in ONE guarded UPDATE:
|
|
11
|
+
* top-level keys overwrite, a `null` value deletes its key, and the statement
|
|
12
|
+
* refuses to commit a document over the size cap (returns null → 413 at the
|
|
13
|
+
* API). No read-then-write — concurrent patches serialize on the row.
|
|
14
|
+
*/
|
|
15
|
+
export declare function patchPreferences(userId: string, patch: Record<string, unknown>): Promise<Record<string, unknown> | null>;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PREFERENCES_MAX_BYTES = void 0;
|
|
4
|
+
exports.getPreferences = getPreferences;
|
|
5
|
+
exports.patchPreferences = patchPreferences;
|
|
6
|
+
const db_1 = require("../../lib/db");
|
|
7
|
+
/**
|
|
8
|
+
* Per-user preferences — a generic JSON store for presentation state (pinned
|
|
9
|
+
* views are the first tenant, not the schema). Preferences carry URLs and UI
|
|
10
|
+
* choices only, never data and never authorization.
|
|
11
|
+
*/
|
|
12
|
+
/** Server-side ceiling on the stored document (the API rejects beyond it). */
|
|
13
|
+
exports.PREFERENCES_MAX_BYTES = 32 * 1024;
|
|
14
|
+
async function getPreferences(userId) {
|
|
15
|
+
const { rows } = await (0, db_1.getPool)().query('SELECT preferences FROM lt_users WHERE id = $1', [userId]);
|
|
16
|
+
return rows[0]?.preferences ?? {};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Shallow-merge `patch` into the user's preferences in ONE guarded UPDATE:
|
|
20
|
+
* top-level keys overwrite, a `null` value deletes its key, and the statement
|
|
21
|
+
* refuses to commit a document over the size cap (returns null → 413 at the
|
|
22
|
+
* API). No read-then-write — concurrent patches serialize on the row.
|
|
23
|
+
*/
|
|
24
|
+
async function patchPreferences(userId, patch) {
|
|
25
|
+
const deletions = Object.keys(patch).filter((k) => patch[k] === null);
|
|
26
|
+
const additions = {};
|
|
27
|
+
for (const [k, v] of Object.entries(patch)) {
|
|
28
|
+
if (v !== null)
|
|
29
|
+
additions[k] = v;
|
|
30
|
+
}
|
|
31
|
+
const { rows } = await (0, db_1.getPool)().query(`UPDATE lt_users u
|
|
32
|
+
SET preferences = ((COALESCE(u.preferences, '{}'::jsonb) || $2::jsonb) - $3::text[]),
|
|
33
|
+
updated_at = NOW()
|
|
34
|
+
WHERE u.id = $1
|
|
35
|
+
AND length(((COALESCE(u.preferences, '{}'::jsonb) || $2::jsonb) - $3::text[])::text) <= $4
|
|
36
|
+
RETURNING u.preferences`, [userId, JSON.stringify(additions), deletions, exports.PREFERENCES_MAX_BYTES]);
|
|
37
|
+
return rows[0]?.preferences ?? null;
|
|
38
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
export interface FieldError {
|
|
2
|
+
field: string;
|
|
3
|
+
message: string;
|
|
4
|
+
}
|
|
5
|
+
interface ChecklistItem {
|
|
6
|
+
id: string;
|
|
7
|
+
label: string;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Resolve a checklist widget's item definitions from its `x-lt-source`
|
|
12
|
+
* "domain.path" against the escalation context. Missing domain/path or a
|
|
13
|
+
* non-array value yields an empty list.
|
|
14
|
+
*/
|
|
15
|
+
export declare function resolveChecklistItems(sourcePath: unknown, ctx: Record<string, unknown> | undefined): ChecklistItem[];
|
|
16
|
+
/**
|
|
17
|
+
* The checklist completion guard (`x-lt-require-all`): every rendered item must
|
|
18
|
+
* be checked, except items whose definition carries `required: false` (the
|
|
19
|
+
* explicit opt-out). Vacuous when the source resolves to no items — never
|
|
20
|
+
* blocks on zero. Returns the panel message, or undefined when satisfied.
|
|
21
|
+
* Stricter than (and composes with) the field-level required-array entry,
|
|
22
|
+
* which remains the at-least-one check.
|
|
23
|
+
*/
|
|
24
|
+
export declare function validateRequireAll(value: unknown, fieldSchema: Record<string, unknown> | undefined, ctx?: Record<string, unknown>): string | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* Runtime type check against the field's declared JSON Schema `type`. Guards
|
|
27
|
+
* the payload contract for API-first callers (a form widget already produces
|
|
28
|
+
* the right primitive; a script or agent submitting `"1"` for a number field
|
|
29
|
+
* does not). Absent/empty values pass — presence is the required check's job.
|
|
30
|
+
*/
|
|
31
|
+
export declare function validateFieldType(value: unknown, fieldSchema: Record<string, unknown> | undefined): string | undefined;
|
|
32
|
+
/**
|
|
33
|
+
* Per-field JSON Schema validation — subset used by the resolver form.
|
|
34
|
+
*
|
|
35
|
+
* Returns a human-readable error string when the value fails a constraint,
|
|
36
|
+
* or undefined when the value is valid. Callers are responsible for the
|
|
37
|
+
* required check (empty-when-required) before calling this for constraint checks.
|
|
38
|
+
*
|
|
39
|
+
* `ctx` is optional context (ShowIfContext-shaped) used to resolve dynamic
|
|
40
|
+
* constraint paths like `x-lt-minimum: "envelope.min_score"`.
|
|
41
|
+
*/
|
|
42
|
+
export declare function validateFieldConstraints(value: unknown, fieldSchema: Record<string, unknown> | undefined, ctx?: Record<string, unknown>): string | undefined;
|
|
43
|
+
/** Full field error including required check then constraints. */
|
|
44
|
+
export declare function validateField(value: unknown, fieldSchema: Record<string, unknown> | undefined, isRequired: boolean, isTouched: boolean, ctx?: Record<string, unknown>): string | undefined;
|
|
45
|
+
export {};
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.resolveChecklistItems = resolveChecklistItems;
|
|
4
|
+
exports.validateRequireAll = validateRequireAll;
|
|
5
|
+
exports.validateFieldType = validateFieldType;
|
|
6
|
+
exports.validateFieldConstraints = validateFieldConstraints;
|
|
7
|
+
exports.validateField = validateField;
|
|
8
|
+
/**
|
|
9
|
+
* Resolve a checklist widget's item definitions from its `x-lt-source`
|
|
10
|
+
* "domain.path" against the escalation context. Missing domain/path or a
|
|
11
|
+
* non-array value yields an empty list.
|
|
12
|
+
*/
|
|
13
|
+
function resolveChecklistItems(sourcePath, ctx) {
|
|
14
|
+
if (typeof sourcePath !== 'string' || !ctx)
|
|
15
|
+
return [];
|
|
16
|
+
const dot = sourcePath.indexOf('.');
|
|
17
|
+
if (dot === -1)
|
|
18
|
+
return [];
|
|
19
|
+
const domainObj = ctx[sourcePath.slice(0, dot)];
|
|
20
|
+
if (!domainObj || typeof domainObj !== 'object')
|
|
21
|
+
return [];
|
|
22
|
+
let cur = domainObj;
|
|
23
|
+
for (const p of sourcePath.slice(dot + 1).split('.')) {
|
|
24
|
+
cur = cur[p];
|
|
25
|
+
if (cur === undefined)
|
|
26
|
+
return [];
|
|
27
|
+
}
|
|
28
|
+
return Array.isArray(cur) ? cur : [];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* The checklist completion guard (`x-lt-require-all`): every rendered item must
|
|
32
|
+
* be checked, except items whose definition carries `required: false` (the
|
|
33
|
+
* explicit opt-out). Vacuous when the source resolves to no items — never
|
|
34
|
+
* blocks on zero. Returns the panel message, or undefined when satisfied.
|
|
35
|
+
* Stricter than (and composes with) the field-level required-array entry,
|
|
36
|
+
* which remains the at-least-one check.
|
|
37
|
+
*/
|
|
38
|
+
function validateRequireAll(value, fieldSchema, ctx) {
|
|
39
|
+
if (fieldSchema?.['x-lt-widget'] !== 'checklist')
|
|
40
|
+
return undefined;
|
|
41
|
+
if (fieldSchema['x-lt-require-all'] !== true)
|
|
42
|
+
return undefined;
|
|
43
|
+
const items = resolveChecklistItems(fieldSchema['x-lt-source'], ctx);
|
|
44
|
+
const mandatory = items.filter((i) => i.required !== false);
|
|
45
|
+
if (mandatory.length === 0)
|
|
46
|
+
return undefined;
|
|
47
|
+
const state = (typeof value === 'object' && value !== null && !Array.isArray(value))
|
|
48
|
+
? value
|
|
49
|
+
: {};
|
|
50
|
+
const incomplete = mandatory.filter((i) => !state[i.id]).length;
|
|
51
|
+
if (incomplete === 0)
|
|
52
|
+
return undefined;
|
|
53
|
+
return `${incomplete} of ${mandatory.length} checks incomplete`;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Runtime type check against the field's declared JSON Schema `type`. Guards
|
|
57
|
+
* the payload contract for API-first callers (a form widget already produces
|
|
58
|
+
* the right primitive; a script or agent submitting `"1"` for a number field
|
|
59
|
+
* does not). Absent/empty values pass — presence is the required check's job.
|
|
60
|
+
*/
|
|
61
|
+
function validateFieldType(value, fieldSchema) {
|
|
62
|
+
const declared = fieldSchema?.type;
|
|
63
|
+
if (typeof declared !== 'string')
|
|
64
|
+
return undefined;
|
|
65
|
+
if (value === undefined || value === null || value === '')
|
|
66
|
+
return undefined;
|
|
67
|
+
switch (declared) {
|
|
68
|
+
case 'string':
|
|
69
|
+
return typeof value === 'string' ? undefined : 'Expected text';
|
|
70
|
+
case 'number':
|
|
71
|
+
return typeof value === 'number' && Number.isFinite(value) ? undefined : 'Expected a number';
|
|
72
|
+
case 'integer':
|
|
73
|
+
return typeof value === 'number' && Number.isInteger(value) ? undefined : 'Expected a whole number';
|
|
74
|
+
case 'boolean':
|
|
75
|
+
return typeof value === 'boolean' ? undefined : 'Expected true or false';
|
|
76
|
+
case 'array':
|
|
77
|
+
return Array.isArray(value) ? undefined : 'Expected a list';
|
|
78
|
+
case 'object':
|
|
79
|
+
return typeof value === 'object' && !Array.isArray(value) ? undefined : 'Expected an object';
|
|
80
|
+
default:
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Per-field JSON Schema validation — subset used by the resolver form.
|
|
86
|
+
*
|
|
87
|
+
* Returns a human-readable error string when the value fails a constraint,
|
|
88
|
+
* or undefined when the value is valid. Callers are responsible for the
|
|
89
|
+
* required check (empty-when-required) before calling this for constraint checks.
|
|
90
|
+
*
|
|
91
|
+
* `ctx` is optional context (ShowIfContext-shaped) used to resolve dynamic
|
|
92
|
+
* constraint paths like `x-lt-minimum: "envelope.min_score"`.
|
|
93
|
+
*/
|
|
94
|
+
function validateFieldConstraints(value, fieldSchema, ctx) {
|
|
95
|
+
if (!fieldSchema)
|
|
96
|
+
return undefined;
|
|
97
|
+
// Enum membership — the form's select widget constrains choices; API-first
|
|
98
|
+
// callers are unconstrained, so the schema's enum is the contract.
|
|
99
|
+
const allowed = fieldSchema.enum;
|
|
100
|
+
if (Array.isArray(allowed) && allowed.length > 0
|
|
101
|
+
&& value !== undefined && value !== null && value !== ''
|
|
102
|
+
&& !allowed.includes(value)) {
|
|
103
|
+
return `Must be one of: ${allowed.map((v) => String(v)).join(', ')}`;
|
|
104
|
+
}
|
|
105
|
+
if (typeof value === 'string') {
|
|
106
|
+
const s = value.trim();
|
|
107
|
+
if (s === '')
|
|
108
|
+
return undefined; // empty string: required check is the caller's job
|
|
109
|
+
const minLength = resolveNumericConstraint(fieldSchema.minLength, fieldSchema['x-lt-min-length'], ctx);
|
|
110
|
+
if (minLength !== undefined && s.length < minLength) {
|
|
111
|
+
return `Minimum ${minLength} character${minLength === 1 ? '' : 's'}`;
|
|
112
|
+
}
|
|
113
|
+
const maxLength = resolveNumericConstraint(fieldSchema.maxLength, fieldSchema['x-lt-max-length'], ctx);
|
|
114
|
+
if (maxLength !== undefined && s.length > maxLength) {
|
|
115
|
+
return `Maximum ${maxLength} characters (${s.length} entered)`;
|
|
116
|
+
}
|
|
117
|
+
const pattern = fieldSchema.pattern;
|
|
118
|
+
if (pattern) {
|
|
119
|
+
try {
|
|
120
|
+
if (!new RegExp(pattern).test(s)) {
|
|
121
|
+
const patternError = fieldSchema['x-lt-pattern-error'];
|
|
122
|
+
return patternError ?? 'Invalid format';
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
// Malformed pattern in schema — silently skip
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
const fmt = fieldSchema.format;
|
|
130
|
+
if (fmt === 'email' && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(s)) {
|
|
131
|
+
return 'Enter a valid email address';
|
|
132
|
+
}
|
|
133
|
+
if (fmt === 'uri' && !/^https?:\/\/.+/.test(s)) {
|
|
134
|
+
return 'Enter a valid URL';
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (typeof value === 'number') {
|
|
138
|
+
const minimum = resolveNumericConstraint(fieldSchema.minimum, fieldSchema['x-lt-minimum'], ctx);
|
|
139
|
+
const exclusiveMinimum = resolveNumericConstraint(fieldSchema.exclusiveMinimum, undefined, ctx);
|
|
140
|
+
const maximum = resolveNumericConstraint(fieldSchema.maximum, fieldSchema['x-lt-maximum'], ctx);
|
|
141
|
+
const exclusiveMaximum = resolveNumericConstraint(fieldSchema.exclusiveMaximum, undefined, ctx);
|
|
142
|
+
if (minimum !== undefined && value < minimum) {
|
|
143
|
+
return `Minimum value is ${minimum}`;
|
|
144
|
+
}
|
|
145
|
+
if (exclusiveMinimum !== undefined && value <= exclusiveMinimum) {
|
|
146
|
+
return `Must be greater than ${exclusiveMinimum}`;
|
|
147
|
+
}
|
|
148
|
+
if (maximum !== undefined && value > maximum) {
|
|
149
|
+
return `Maximum value is ${maximum}`;
|
|
150
|
+
}
|
|
151
|
+
if (exclusiveMaximum !== undefined && value >= exclusiveMaximum) {
|
|
152
|
+
return `Must be less than ${exclusiveMaximum}`;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
return undefined;
|
|
156
|
+
}
|
|
157
|
+
function resolveNumericConstraint(staticVal, dynamicPath, ctx) {
|
|
158
|
+
if (typeof staticVal === 'number')
|
|
159
|
+
return staticVal;
|
|
160
|
+
if (typeof dynamicPath === 'string' && ctx) {
|
|
161
|
+
const dot = dynamicPath.indexOf('.');
|
|
162
|
+
if (dot !== -1) {
|
|
163
|
+
const domain = dynamicPath.slice(0, dot);
|
|
164
|
+
const path = dynamicPath.slice(dot + 1);
|
|
165
|
+
const domainObj = ctx[domain];
|
|
166
|
+
if (domainObj && typeof domainObj === 'object') {
|
|
167
|
+
const parts = path.split('.');
|
|
168
|
+
let cur = domainObj;
|
|
169
|
+
for (const p of parts) {
|
|
170
|
+
cur = cur[p];
|
|
171
|
+
if (cur === undefined)
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
if (typeof cur === 'number')
|
|
175
|
+
return cur;
|
|
176
|
+
if (typeof cur === 'string') {
|
|
177
|
+
const n = Number(cur);
|
|
178
|
+
if (!Number.isNaN(n))
|
|
179
|
+
return n;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
/** Full field error including required check then constraints. */
|
|
187
|
+
function validateField(value, fieldSchema, isRequired, isTouched, ctx) {
|
|
188
|
+
if (!isTouched)
|
|
189
|
+
return undefined;
|
|
190
|
+
if (isRequired) {
|
|
191
|
+
if (value === undefined || value === null)
|
|
192
|
+
return 'Required';
|
|
193
|
+
if (typeof value === 'boolean' && !value)
|
|
194
|
+
return 'Required';
|
|
195
|
+
if (typeof value === 'string' && value.trim() === '')
|
|
196
|
+
return 'Required';
|
|
197
|
+
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
|
|
198
|
+
const vals = Object.values(value);
|
|
199
|
+
if (vals.length === 0 || vals.every((v) => !v))
|
|
200
|
+
return 'Required';
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
// Declared-type check runs before constraints: a mis-typed value would make
|
|
204
|
+
// the numeric/string constraint branches silently inapplicable.
|
|
205
|
+
const typeError = validateFieldType(value, fieldSchema);
|
|
206
|
+
if (typeError)
|
|
207
|
+
return typeError;
|
|
208
|
+
// Checklist completion — the stricter guard runs regardless of the
|
|
209
|
+
// required-array entry (which stays at-least-one when present).
|
|
210
|
+
const requireAll = validateRequireAll(value, fieldSchema, ctx);
|
|
211
|
+
if (requireAll)
|
|
212
|
+
return requireAll;
|
|
213
|
+
return validateFieldConstraints(value, fieldSchema, ctx);
|
|
214
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Isomorphic form-validation library for the x-lt-* enriched form_schema
|
|
3
|
+
* vocabulary. The dashboard renders and pre-validates resolver forms with it;
|
|
4
|
+
* the API layer enforces the same contract on every resolve surface. Pure
|
|
5
|
+
* functions only — no React, no DOM, no database.
|
|
6
|
+
*/
|
|
7
|
+
export * from './x-lt-bind';
|
|
8
|
+
export * from './x-lt-help';
|
|
9
|
+
export * from './x-lt-show-if';
|
|
10
|
+
export * from './field-validator';
|
|
11
|
+
export * from './validate-resolver-payload';
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
14
|
+
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
/**
|
|
18
|
+
* Isomorphic form-validation library for the x-lt-* enriched form_schema
|
|
19
|
+
* vocabulary. The dashboard renders and pre-validates resolver forms with it;
|
|
20
|
+
* the API layer enforces the same contract on every resolve surface. Pure
|
|
21
|
+
* functions only — no React, no DOM, no database.
|
|
22
|
+
*/
|
|
23
|
+
__exportStar(require("./x-lt-bind"), exports);
|
|
24
|
+
__exportStar(require("./x-lt-help"), exports);
|
|
25
|
+
__exportStar(require("./x-lt-show-if"), exports);
|
|
26
|
+
__exportStar(require("./field-validator"), exports);
|
|
27
|
+
__exportStar(require("./validate-resolver-payload"), exports);
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The full pre-submission validation pass over a resolver form schema — the
|
|
3
|
+
* same pass on both sides of the wire. The dashboard runs it on the flat form
|
|
4
|
+
* values before submitting; the API layer runs it on the submitted (bound,
|
|
5
|
+
* nested) payload by inverting x-lt-bind first. Because both entry points
|
|
6
|
+
* funnel into one loop, a payload that passes the client panel passes the
|
|
7
|
+
* server gate, and a 422's violation list is exactly what the panel shows.
|
|
8
|
+
*
|
|
9
|
+
* Semantics per field:
|
|
10
|
+
* - x-lt-showIf-hidden fields are skipped entirely (a field the submitter
|
|
11
|
+
* cannot see never blocks submission)
|
|
12
|
+
* - required (schema.required membership), with checklist/empty-object rules
|
|
13
|
+
* - declared-type check (string/number/integer/boolean/array/object)
|
|
14
|
+
* - enum membership
|
|
15
|
+
* - x-lt-require-all against the checklist items resolved from context
|
|
16
|
+
* - static + dynamic bounds (minimum/x-lt-minimum, lengths, patterns)
|
|
17
|
+
*
|
|
18
|
+
* The showIf/`resolver.*` domain is always the FLAT form representation, so a
|
|
19
|
+
* condition like `resolver.approved` reads the same on both sides.
|
|
20
|
+
*/
|
|
21
|
+
import { type FieldError } from './field-validator';
|
|
22
|
+
import { type ShowIfContext } from './x-lt-show-if';
|
|
23
|
+
export type { FieldError } from './field-validator';
|
|
24
|
+
/**
|
|
25
|
+
* The escalation-surface context the pass evaluates showIf conditions and
|
|
26
|
+
* dynamic constraints against. `resolver` is supplied by the pass itself
|
|
27
|
+
* (the flat form values under validation) — callers provide the rest.
|
|
28
|
+
*/
|
|
29
|
+
export type ResolverValidationContext = Omit<ShowIfContext, 'resolver'>;
|
|
30
|
+
/**
|
|
31
|
+
* Validate FLAT form values against the form schema. This is the dashboard's
|
|
32
|
+
* entry point (the values the user edits, keyed by field name).
|
|
33
|
+
*/
|
|
34
|
+
export declare function validateResolverForm(schema: Record<string, unknown> | null | undefined, formValues: Record<string, unknown>, ctx?: ResolverValidationContext | null): FieldError[];
|
|
35
|
+
/**
|
|
36
|
+
* Validate a SUBMITTED resolver payload (the bound, nested shape the workflow
|
|
37
|
+
* consumes) against the form schema. This is the API layer's entry point: the
|
|
38
|
+
* payload is inverted through each field's x-lt-bind path back to the flat
|
|
39
|
+
* form representation, then run through the same pass the client uses.
|
|
40
|
+
*/
|
|
41
|
+
export declare function validateResolverPayload(schema: Record<string, unknown> | null | undefined, payload: Record<string, unknown>, ctx?: ResolverValidationContext | null): FieldError[];
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateResolverForm = validateResolverForm;
|
|
4
|
+
exports.validateResolverPayload = validateResolverPayload;
|
|
5
|
+
/**
|
|
6
|
+
* The full pre-submission validation pass over a resolver form schema — the
|
|
7
|
+
* same pass on both sides of the wire. The dashboard runs it on the flat form
|
|
8
|
+
* values before submitting; the API layer runs it on the submitted (bound,
|
|
9
|
+
* nested) payload by inverting x-lt-bind first. Because both entry points
|
|
10
|
+
* funnel into one loop, a payload that passes the client panel passes the
|
|
11
|
+
* server gate, and a 422's violation list is exactly what the panel shows.
|
|
12
|
+
*
|
|
13
|
+
* Semantics per field:
|
|
14
|
+
* - x-lt-showIf-hidden fields are skipped entirely (a field the submitter
|
|
15
|
+
* cannot see never blocks submission)
|
|
16
|
+
* - required (schema.required membership), with checklist/empty-object rules
|
|
17
|
+
* - declared-type check (string/number/integer/boolean/array/object)
|
|
18
|
+
* - enum membership
|
|
19
|
+
* - x-lt-require-all against the checklist items resolved from context
|
|
20
|
+
* - static + dynamic bounds (minimum/x-lt-minimum, lengths, patterns)
|
|
21
|
+
*
|
|
22
|
+
* The showIf/`resolver.*` domain is always the FLAT form representation, so a
|
|
23
|
+
* condition like `resolver.approved` reads the same on both sides.
|
|
24
|
+
*/
|
|
25
|
+
const field_validator_1 = require("./field-validator");
|
|
26
|
+
const x_lt_show_if_1 = require("./x-lt-show-if");
|
|
27
|
+
const x_lt_bind_1 = require("./x-lt-bind");
|
|
28
|
+
/**
|
|
29
|
+
* Validate FLAT form values against the form schema. This is the dashboard's
|
|
30
|
+
* entry point (the values the user edits, keyed by field name).
|
|
31
|
+
*/
|
|
32
|
+
function validateResolverForm(schema, formValues, ctx) {
|
|
33
|
+
if (!schema)
|
|
34
|
+
return [];
|
|
35
|
+
const properties = (schema.properties ?? {});
|
|
36
|
+
const required = new Set(schema.required ?? []);
|
|
37
|
+
const liveCtx = {
|
|
38
|
+
...(ctx ?? {}),
|
|
39
|
+
resolver: formValues,
|
|
40
|
+
};
|
|
41
|
+
const errors = [];
|
|
42
|
+
for (const [field, fieldSchema] of Object.entries(properties)) {
|
|
43
|
+
if (!(0, x_lt_show_if_1.evaluateShowIf)(fieldSchema['x-lt-showIf'], liveCtx))
|
|
44
|
+
continue;
|
|
45
|
+
const err = (0, field_validator_1.validateField)(formValues[field], fieldSchema, required.has(field), true, liveCtx);
|
|
46
|
+
if (err)
|
|
47
|
+
errors.push({ field, message: err });
|
|
48
|
+
}
|
|
49
|
+
return errors;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Validate a SUBMITTED resolver payload (the bound, nested shape the workflow
|
|
53
|
+
* consumes) against the form schema. This is the API layer's entry point: the
|
|
54
|
+
* payload is inverted through each field's x-lt-bind path back to the flat
|
|
55
|
+
* form representation, then run through the same pass the client uses.
|
|
56
|
+
*/
|
|
57
|
+
function validateResolverPayload(schema, payload, ctx) {
|
|
58
|
+
if (!schema)
|
|
59
|
+
return [];
|
|
60
|
+
const formValues = (0, x_lt_bind_1.mapPayloadToForm)(payload, schema);
|
|
61
|
+
return validateResolverForm(schema, formValues, ctx);
|
|
62
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* x-lt-bind — maps the escalation FORM (flat field values the user edits) to and
|
|
3
|
+
* from the PAYLOAD (the nested JSON the workflow consumes). A form_schema property
|
|
4
|
+
* may declare `x-lt-bind: "a.b[0].c"`, the path its value occupies in the payload;
|
|
5
|
+
* a property with no bind sits at its own name at the root (1:1). This module is
|
|
6
|
+
* isomorphic — the dashboard renders forms with it, and the API layer inverts a
|
|
7
|
+
* submitted payload through it to validate against the form contract. One
|
|
8
|
+
* implementation, both sides.
|
|
9
|
+
*
|
|
10
|
+
* mapPayloadToForm — payload → flat form values, to PREFILL/display the form
|
|
11
|
+
* (server-side: to validate a submitted payload)
|
|
12
|
+
* mapFormToPayload — flat form values → payload, the shape submitted/stored
|
|
13
|
+
*/
|
|
14
|
+
type PathSeg = {
|
|
15
|
+
key: string;
|
|
16
|
+
} | {
|
|
17
|
+
index: number;
|
|
18
|
+
};
|
|
19
|
+
/** Parse a bind path — dot keys with optional `[n]` indices. Rejects pollution keys. */
|
|
20
|
+
export declare function parsePath(path: string): PathSeg[];
|
|
21
|
+
/** Set `value` at `path` inside `root`, creating intermediate objects/arrays. */
|
|
22
|
+
export declare function setDeep(root: Record<string, any>, path: string, value: unknown): void;
|
|
23
|
+
/** Read the value at `path` inside `root` (undefined if any segment is missing). */
|
|
24
|
+
export declare function getDeep(root: unknown, path: string): unknown;
|
|
25
|
+
/** The bind path for a form property (its `x-lt-bind`, or the property name). */
|
|
26
|
+
export declare function bindPathFor(propName: string, propDef: Record<string, any> | undefined): string;
|
|
27
|
+
/**
|
|
28
|
+
* Assemble the payload from the form's flat values. A field absent from
|
|
29
|
+
* `formValues` is omitted (so clearing/deleting a field drops it from the
|
|
30
|
+
* payload). Unknown keys not declared in the schema are dropped. With no schema
|
|
31
|
+
* properties, values pass through unchanged.
|
|
32
|
+
*/
|
|
33
|
+
export declare function mapFormToPayload(formValues: Record<string, any>, formSchema: Record<string, any> | null | undefined): Record<string, any>;
|
|
34
|
+
/**
|
|
35
|
+
* Reverse of {@link mapFormToPayload}: pull each form field's value out of the
|
|
36
|
+
* payload via its bind path, to prefill/display the form. A field whose bound
|
|
37
|
+
* path is missing from the payload is omitted (the caller falls back to the
|
|
38
|
+
* schema default).
|
|
39
|
+
*/
|
|
40
|
+
export declare function mapPayloadToForm(payload: Record<string, any> | null | undefined, formSchema: Record<string, any> | null | undefined): Record<string, any>;
|
|
41
|
+
export {};
|