@hotmeshio/long-tail 0.19.1 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/api/workflows/config.d.ts +3 -0
- package/build/api/workflows/config.js +15 -0
- package/build/api/workflows/index.d.ts +1 -0
- package/build/api/workflows/index.js +3 -1
- package/build/api/workflows/input-lookups.d.ts +16 -0
- package/build/api/workflows/input-lookups.js +69 -0
- package/build/api/workflows/input-validation.d.ts +4 -3
- package/build/api/workflows/input-validation.js +7 -4
- package/build/api/workflows/invocable.js +1 -0
- package/build/api/workflows/invocation.js +1 -1
- package/build/lib/db/schemas/035_workflow_input_lookups.sql +9 -0
- package/build/routes/workflows/config.js +10 -0
- package/build/services/config/cache.js +1 -0
- package/build/services/config/read.js +2 -0
- package/build/services/config/sql.d.ts +2 -2
- package/build/services/config/sql.js +6 -5
- package/build/services/config/write.js +10 -1
- package/build/services/knowledge/index.d.ts +1 -0
- package/build/services/knowledge/index.js +1 -0
- package/build/services/knowledge/lookup-refs.d.ts +7 -0
- package/build/services/knowledge/lookup-refs.js +26 -0
- package/build/services/mcp/db-server/schemas.d.ts +2 -2
- package/build/shared/form-validation/field-validator.d.ts +1 -0
- package/build/shared/form-validation/field-validator.js +94 -6
- package/build/shared/form-validation/invoke-context.d.ts +4 -3
- package/build/shared/form-validation/invoke-context.js +5 -4
- package/build/shared/form-validation/x-lt-options.d.ts +3 -3
- package/build/shared/form-validation/x-lt-options.js +19 -5
- package/build/shared/form-validation/x-lt-show-if.d.ts +5 -0
- package/build/shared/form-validation/x-lt-show-if.js +10 -0
- package/build/start/workers.js +10 -0
- package/build/system/mcp-servers/admin/schemas.d.ts +64 -20
- package/build/system/mcp-servers/admin/schemas.js +19 -3
- package/build/system/mcp-servers/admin/workflow-config.js +54 -9
- package/build/system/mcp-servers/admin/workflows.js +9 -7
- package/build/system/mcp-servers/db-query/schemas.d.ts +2 -2
- package/build/system/seed/tool-manifests-admin.d.ts +517 -54
- package/build/system/seed/tool-manifests-admin.js +4 -3
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/config.d.ts +4 -0
- package/build/types/startup.d.ts +6 -0
- package/dashboard/dist/assets/{AdminDashboard-Drs-9TRM.js → AdminDashboard-DplvDBti.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-Drs-9TRM.js.map → AdminDashboard-DplvDBti.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-BDPDZ7-v.js → AgentConfigPage-D8Bf6YxO.js} +2 -2
- package/dashboard/dist/assets/{AgentConfigPage-BDPDZ7-v.js.map → AgentConfigPage-D8Bf6YxO.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-CGBulM4_.js → AgentDetailPage-BN8g0Y2G.js} +2 -2
- package/dashboard/dist/assets/{AgentDetailPage-CGBulM4_.js.map → AgentDetailPage-BN8g0Y2G.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-D77JeViQ.js → AgentsPage-7JJBHku8.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-D77JeViQ.js.map → AgentsPage-7JJBHku8.js.map} +1 -1
- package/dashboard/dist/assets/{AvailableEscalationsPage-CqM_MOMy.js → AvailableEscalationsPage-lz5qwSjg.js} +2 -2
- package/dashboard/dist/assets/{AvailableEscalationsPage-CqM_MOMy.js.map → AvailableEscalationsPage-lz5qwSjg.js.map} +1 -1
- package/dashboard/dist/assets/{BotPicker-CC5vI_Sv.js → BotPicker-CGCnIz8I.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-CC5vI_Sv.js.map → BotPicker-CGCnIz8I.js.map} +1 -1
- package/dashboard/dist/assets/{BulkAssignModal-BXiJnlqt.js → BulkAssignModal-NJBcoj-f.js} +2 -2
- package/dashboard/dist/assets/{BulkAssignModal-BXiJnlqt.js.map → BulkAssignModal-NJBcoj-f.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-D9TC2iKy.js → CapabilitiesPage-F8dpNn9B.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-D9TC2iKy.js.map → CapabilitiesPage-F8dpNn9B.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-D10lGw_u.js → CollapsibleSection-BZW6VpCU.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-D10lGw_u.js.map → CollapsibleSection-BZW6VpCU.js.map} +1 -1
- package/dashboard/dist/assets/{ConfirmDeleteModal-BJdLN6No.js → ConfirmDeleteModal-64adXLdq.js} +2 -2
- package/dashboard/dist/assets/{ConfirmDeleteModal-BJdLN6No.js.map → ConfirmDeleteModal-64adXLdq.js.map} +1 -1
- package/dashboard/dist/assets/{CountdownTimer-D9B-BjQ7.js → CountdownTimer-BxDub9d2.js} +2 -2
- package/dashboard/dist/assets/{CountdownTimer-D9B-BjQ7.js.map → CountdownTimer-BxDub9d2.js.map} +1 -1
- package/dashboard/dist/assets/{CredentialsPage-hniUAFBK.js → CredentialsPage-CnLzopLL.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-hniUAFBK.js.map → CredentialsPage-CnLzopLL.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-v9DCD2FX.js → CronLabel-CAD5UYDT.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-v9DCD2FX.js.map → CronLabel-CAD5UYDT.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-_xvVTK8N.js → ElapsedCell-CwO4ufd0.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-_xvVTK8N.js.map → ElapsedCell-CwO4ufd0.js.map} +1 -1
- package/dashboard/dist/assets/{EntityTimelinePanel-1lT3jkCM.js → EntityTimelinePanel-CkF-oW-m.js} +2 -2
- package/dashboard/dist/assets/{EntityTimelinePanel-1lT3jkCM.js.map → EntityTimelinePanel-CkF-oW-m.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationListSchemaPage-BeQUTUEO.js → EscalationListSchemaPage-h9JO_q5f.js} +2 -2
- package/dashboard/dist/assets/{EscalationListSchemaPage-BeQUTUEO.js.map → EscalationListSchemaPage-h9JO_q5f.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationListView-uuZRMSLB.js → EscalationListView-AHW2xtlr.js} +2 -2
- package/dashboard/dist/assets/{EscalationListView-uuZRMSLB.js.map → EscalationListView-AHW2xtlr.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationSchemaPage-D4VlEwyS.js → EscalationSchemaPage-mJFL5_Se.js} +2 -2
- package/dashboard/dist/assets/{EscalationSchemaPage-D4VlEwyS.js.map → EscalationSchemaPage-mJFL5_Se.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-Bx6Zrzm-.js → EscalationsOverview-DlXUFqWJ.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-Bx6Zrzm-.js.map → EscalationsOverview-DlXUFqWJ.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-KEC3YWBf.js → EventTable-Ike2xchn.js} +2 -2
- package/dashboard/dist/assets/{EventTable-KEC3YWBf.js.map → EventTable-Ike2xchn.js.map} +1 -1
- package/dashboard/dist/assets/{FilterBar-xwN_b5fj.js → FilterBar-DMOR-O1c.js} +2 -2
- package/dashboard/dist/assets/{FilterBar-xwN_b5fj.js.map → FilterBar-DMOR-O1c.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-DV7Y0fe2.js → GraphInvokePage-CKtvWowP.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-DV7Y0fe2.js.map → GraphInvokePage-CKtvWowP.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-G785SWcW.js → HomePage-BQ3Q791Q.js} +2 -2
- package/dashboard/dist/assets/{HomePage-G785SWcW.js.map → HomePage-BQ3Q791Q.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-zPVYPg3W.js → ListToolbar-DdJzj472.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-zPVYPg3W.js.map → ListToolbar-DdJzj472.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-CMGUq8hx.js → McpOverview-BbtrduWn.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-CMGUq8hx.js.map → McpOverview-BbtrduWn.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-CdhLhaKn.js → McpQueryDetailPage-C1L4T-Wt.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-CdhLhaKn.js.map → McpQueryDetailPage-C1L4T-Wt.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-CNSNMagY.js → McpQueryPage-CfJy7HZ7.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-CNSNMagY.js.map → McpQueryPage-CfJy7HZ7.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-DBm8hUaW.js → McpRunDetailPage-CA9yv7he.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-DBm8hUaW.js.map → McpRunDetailPage-CA9yv7he.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-B6rb5QBf.js → McpRunsPage-D7QXjW9z.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-B6rb5QBf.js.map → McpRunsPage-D7QXjW9z.js.map} +1 -1
- package/dashboard/dist/assets/{OperationsPage-Dxk1fAMg.js → OperationsPage-aLHHAP2X.js} +2 -2
- package/dashboard/dist/assets/{OperationsPage-Dxk1fAMg.js.map → OperationsPage-aLHHAP2X.js.map} +1 -1
- package/dashboard/dist/assets/{OperatorDashboard-DBOscDnf.js → OperatorDashboard-sXNDh1Ph.js} +2 -2
- package/dashboard/dist/assets/{OperatorDashboard-DBOscDnf.js.map → OperatorDashboard-sXNDh1Ph.js.map} +1 -1
- package/dashboard/dist/assets/{PersonaDetailPage-BGRdGpeR.js → PersonaDetailPage-NeHD-Gra.js} +2 -2
- package/dashboard/dist/assets/{PersonaDetailPage-BGRdGpeR.js.map → PersonaDetailPage-NeHD-Gra.js.map} +1 -1
- package/dashboard/dist/assets/{PersonasPage-DRzOZcVa.js → PersonasPage-DavKXRWX.js} +2 -2
- package/dashboard/dist/assets/{PersonasPage-DRzOZcVa.js.map → PersonasPage-DavKXRWX.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-BLGjZXpM.js → ProcessDetailPage-DkdKJGe6.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-BLGjZXpM.js.map → ProcessDetailPage-DkdKJGe6.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-5NnysKFC.js → ProcessesListPage-CjwVKe32.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-5NnysKFC.js.map → ProcessesListPage-CjwVKe32.js.map} +1 -1
- package/dashboard/dist/assets/ResolverForm-Bk-wDkN-.js +3 -0
- package/dashboard/dist/assets/ResolverForm-Bk-wDkN-.js.map +1 -0
- package/dashboard/dist/assets/{RoleDetailPage-C_LIoe1j.js → RoleDetailPage-WQt0y5l7.js} +3 -3
- package/dashboard/dist/assets/{RoleDetailPage-C_LIoe1j.js.map → RoleDetailPage-WQt0y5l7.js.map} +1 -1
- package/dashboard/dist/assets/{RolesPage-BmW1LzkU.js → RolesPage-OM9KM8Pg.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-BmW1LzkU.js.map → RolesPage-OM9KM8Pg.js.map} +1 -1
- package/dashboard/dist/assets/{RunAsSelector-BwZTJ4Yj.js → RunAsSelector-fFMt1bGe.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-BwZTJ4Yj.js.map → RunAsSelector-fFMt1bGe.js.map} +1 -1
- package/dashboard/dist/assets/ScanCodesPage-AujH-pPQ.js +2 -0
- package/dashboard/dist/assets/{ScanCodesPage-FiYIYB-m.js.map → ScanCodesPage-AujH-pPQ.js.map} +1 -1
- package/dashboard/dist/assets/{ScanSchemeDetailPage-BiAqtD-a.js → ScanSchemeDetailPage-eKQpmsC_.js} +2 -2
- package/dashboard/dist/assets/{ScanSchemeDetailPage-BiAqtD-a.js.map → ScanSchemeDetailPage-eKQpmsC_.js.map} +1 -1
- package/dashboard/dist/assets/{ScanStationPage-8CdOtbCD.js → ScanStationPage-C1_ppDoZ.js} +2 -2
- package/dashboard/dist/assets/{ScanStationPage-8CdOtbCD.js.map → ScanStationPage-C1_ppDoZ.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-DRuqKhZt.js → StickyPagination-B91Pk9xh.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-DRuqKhZt.js.map → StickyPagination-B91Pk9xh.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-JwlbRiMn.js → StreamMessageDetail-C7SPqaJt.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-JwlbRiMn.js.map → StreamMessageDetail-C7SPqaJt.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-hu3c4bCJ.js → SwimlaneTimeline-BcHR8ylf.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-hu3c4bCJ.js.map → SwimlaneTimeline-BcHR8ylf.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-NqVZF3jW.js → TaskDetailPage-Bqj0I_I0.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-NqVZF3jW.js.map → TaskDetailPage-Bqj0I_I0.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-BQNaSzCn.js → TasksListPage-BB1f79lL.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-BQNaSzCn.js.map → TasksListPage-BB1f79lL.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-Cv35DOiI.js → TimeAgo-BFnODmNe.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-Cv35DOiI.js.map → TimeAgo-BFnODmNe.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-DHUXey9s.js → TimestampCell-BXgLOBsr.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-DHUXey9s.js.map → TimestampCell-BXgLOBsr.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-DvuLFWqQ.js → ToolTestPanel-Bwe3J5lj.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-DvuLFWqQ.js.map → ToolTestPanel-Bwe3J5lj.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-TXX3QNSo.js → TopicDetailPage-DAF07vZ9.js} +2 -2
- package/dashboard/dist/assets/{TopicDetailPage-TXX3QNSo.js.map → TopicDetailPage-DAF07vZ9.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-h-oyTeEJ.js → TopicsPage-C1gxBUap.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-h-oyTeEJ.js.map → TopicsPage-C1gxBUap.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-BceRCeDF.js → UserName-BzXJUE6q.js} +2 -2
- package/dashboard/dist/assets/{UserName-BceRCeDF.js.map → UserName-BzXJUE6q.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-CmHGcaN9.js → WorkflowExecutionPage-SkRU5cRX.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-CmHGcaN9.js.map → WorkflowExecutionPage-SkRU5cRX.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-B0r2lQXk.js → WorkflowsDashboard-CZAqRk1N.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-B0r2lQXk.js.map → WorkflowsDashboard-CZAqRk1N.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-BTMc2PJ_.js → WorkflowsOverview-BoN44VTE.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-BTMc2PJ_.js.map → WorkflowsOverview-BoN44VTE.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DUiCVnZp.js → YamlWorkflowDetailPage-CqjhtdJf.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-DUiCVnZp.js.map → YamlWorkflowDetailPage-CqjhtdJf.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-BAuXfwi0.js → YamlWorkflowsPage-CwnJkzyb.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-BAuXfwi0.js.map → YamlWorkflowsPage-CwnJkzyb.js.map} +1 -1
- package/dashboard/dist/assets/{agents-BvmLCk4h.js → agents-gV0Ys-sY.js} +2 -2
- package/dashboard/dist/assets/{agents-BvmLCk4h.js.map → agents-gV0Ys-sY.js.map} +1 -1
- package/dashboard/dist/assets/{bots-f_tyhl4I.js → bots-B97itB3I.js} +2 -2
- package/dashboard/dist/assets/{bots-f_tyhl4I.js.map → bots-B97itB3I.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-DNo5bqLx.js → capabilities-CR9xZgGd.js} +2 -2
- package/dashboard/dist/assets/{capabilities-DNo5bqLx.js.map → capabilities-CR9xZgGd.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-Dyyj2Irb.js → controlplane-YxvP1KCa.js} +2 -2
- package/dashboard/dist/assets/{controlplane-Dyyj2Irb.js.map → controlplane-YxvP1KCa.js.map} +1 -1
- package/dashboard/dist/assets/{escalation-columns-DIigeTOK.js → escalation-columns-uq-IV2aj.js} +2 -2
- package/dashboard/dist/assets/{escalation-columns-DIigeTOK.js.map → escalation-columns-uq-IV2aj.js.map} +1 -1
- package/dashboard/dist/assets/index-3ezfWLkE.js +6 -0
- package/dashboard/dist/assets/index-3ezfWLkE.js.map +1 -0
- package/dashboard/dist/assets/{index-CsKiRyhy.js → index-B9BBCqLI.js} +2 -2
- package/dashboard/dist/assets/{index-CsKiRyhy.js.map → index-B9BBCqLI.js.map} +1 -1
- package/dashboard/dist/assets/{index-DL83W7Gu.js → index-BQpV-DTG.js} +2 -2
- package/dashboard/dist/assets/{index-DL83W7Gu.js.map → index-BQpV-DTG.js.map} +1 -1
- package/dashboard/dist/assets/index-Bp9KBLT5.js +2 -0
- package/dashboard/dist/assets/{index-D7ESqn22.js.map → index-Bp9KBLT5.js.map} +1 -1
- package/dashboard/dist/assets/{index-DvxxrFBo.js → index-CFbo9FU9.js} +2 -2
- package/dashboard/dist/assets/{index-DvxxrFBo.js.map → index-CFbo9FU9.js.map} +1 -1
- package/dashboard/dist/assets/{index-e2X-s0EQ.js → index-CIpHj4di.js} +2 -2
- package/dashboard/dist/assets/{index-e2X-s0EQ.js.map → index-CIpHj4di.js.map} +1 -1
- package/dashboard/dist/assets/{index-BMlQYOE1.js → index-CIwlVfvV.js} +10 -10
- package/dashboard/dist/assets/index-CIwlVfvV.js.map +1 -0
- package/dashboard/dist/assets/{index-BNZknxFY.js → index-CUZ6LYye.js} +2 -2
- package/dashboard/dist/assets/{index-BNZknxFY.js.map → index-CUZ6LYye.js.map} +1 -1
- package/dashboard/dist/assets/index-Cfvkl2J0.js +13 -0
- package/dashboard/dist/assets/index-Cfvkl2J0.js.map +1 -0
- package/dashboard/dist/assets/{index-DWse7ERF.js → index-DMeaiKgf.js} +2 -2
- package/dashboard/dist/assets/{index-DWse7ERF.js.map → index-DMeaiKgf.js.map} +1 -1
- package/dashboard/dist/assets/{index-C-Xr2V3F.js → index-DwOnWeu8.js} +2 -2
- package/dashboard/dist/assets/{index-C-Xr2V3F.js.map → index-DwOnWeu8.js.map} +1 -1
- package/dashboard/dist/assets/index-SGyrXGp8.js +3 -0
- package/dashboard/dist/assets/{index-CegPUKpc.js.map → index-SGyrXGp8.js.map} +1 -1
- package/dashboard/dist/assets/{index-PcyGjmBm.js → index-ipqwPavs.js} +2 -2
- package/dashboard/dist/assets/{index-PcyGjmBm.js.map → index-ipqwPavs.js.map} +1 -1
- package/dashboard/dist/assets/invoke-context-DrVyUEfs.js +2 -0
- package/dashboard/dist/assets/invoke-context-DrVyUEfs.js.map +1 -0
- package/dashboard/dist/assets/{knowledge-Coo6CKzo.js → knowledge-XX6cW4Ob.js} +2 -2
- package/dashboard/dist/assets/{knowledge-Coo6CKzo.js.map → knowledge-XX6cW4Ob.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-Cz5eFnP_.js → mcp-CH13hNH2.js} +2 -2
- package/dashboard/dist/assets/{mcp-Cz5eFnP_.js.map → mcp-CH13hNH2.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-BtnuO4LF.js → mcp-query-Duydd3JA.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-BtnuO4LF.js.map → mcp-query-Duydd3JA.js.map} +1 -1
- package/dashboard/dist/assets/{personas-C2-LgpHQ.js → personas-mDk4lLQS.js} +2 -2
- package/dashboard/dist/assets/{personas-C2-LgpHQ.js.map → personas-mDk4lLQS.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-TuXQvUA-.js → pipelines-B5GcgCeF.js} +2 -2
- package/dashboard/dist/assets/{pipelines-TuXQvUA-.js.map → pipelines-B5GcgCeF.js.map} +1 -1
- package/dashboard/dist/assets/{resolver-payload-C1dXB3n6.js → resolver-payload-m9AGKZ3a.js} +2 -2
- package/dashboard/dist/assets/{resolver-payload-C1dXB3n6.js.map → resolver-payload-m9AGKZ3a.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-OUzpu__z.js → tasks-CItmNsRp.js} +2 -2
- package/dashboard/dist/assets/{tasks-OUzpu__z.js.map → tasks-CItmNsRp.js.map} +1 -1
- package/dashboard/dist/assets/{topics-B8bdrBaW.js → topics-CJYG7BX5.js} +2 -2
- package/dashboard/dist/assets/{topics-B8bdrBaW.js.map → topics-CJYG7BX5.js.map} +1 -1
- package/dashboard/dist/assets/{useDebouncedValue-Cp5ZOtVE.js → useDebouncedValue-BrwgXcp8.js} +2 -2
- package/dashboard/dist/assets/{useDebouncedValue-Cp5ZOtVE.js.map → useDebouncedValue-BrwgXcp8.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-DtQe3wZ5.js → useNamespace-CHty0HAS.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-DtQe3wZ5.js.map → useNamespace-CHty0HAS.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-CS22c15X.js → useYamlActivityEvents-CoTsia7t.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-CS22c15X.js.map → useYamlActivityEvents-CoTsia7t.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-HQIki3hI.js → yaml-workflows-CiWwx5rM.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-HQIki3hI.js.map → yaml-workflows-CiWwx5rM.js.map} +1 -1
- package/dashboard/dist/index.html +1 -1
- package/docs/api/http/workflows.md +28 -1
- package/docs/api/mcp/admin.md +40 -4
- package/docs/code-owned-configuration.md +1 -1
- package/docs/dashboard.md +1 -1
- package/docs/design-principles.md +6 -5
- package/docs/hitl/form.md +12 -7
- package/docs/hitl/invoke-form.md +6 -3
- package/docs/hitl/lookups.md +21 -0
- package/docs/hitl/x-lt-validation.md +50 -3
- package/docs/hitl/x-lt-widget.md +21 -0
- package/docs/hitl-guide.md +5 -2
- package/package.json +1 -1
- package/dashboard/dist/assets/ResolverForm-Dy0yirtP.js +0 -2
- package/dashboard/dist/assets/ResolverForm-Dy0yirtP.js.map +0 -1
- package/dashboard/dist/assets/ScanCodesPage-FiYIYB-m.js +0 -2
- package/dashboard/dist/assets/index-BMlQYOE1.js.map +0 -1
- package/dashboard/dist/assets/index-CegPUKpc.js +0 -3
- package/dashboard/dist/assets/index-D7ESqn22.js +0 -2
- package/dashboard/dist/assets/index-DPLssnMW.js +0 -6
- package/dashboard/dist/assets/index-DPLssnMW.js.map +0 -1
- package/dashboard/dist/assets/index-DoETPlJP.js +0 -11
- package/dashboard/dist/assets/index-DoETPlJP.js.map +0 -1
- package/dashboard/dist/assets/seed-form-json-DvPsISxD.js +0 -2
- package/dashboard/dist/assets/seed-form-json-DvPsISxD.js.map +0 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type EscalationLookupRef } from '../../types/escalation';
|
|
1
2
|
import type { LTApiResult } from '../../types/sdk';
|
|
2
3
|
/**
|
|
3
4
|
* List all registered workflow configurations.
|
|
@@ -38,6 +39,7 @@ export declare function getWorkflowConfig(input: {
|
|
|
38
39
|
* @param input.envelope_schema — JSON template that pre-fills the invoke form
|
|
39
40
|
* @param input.input_schema — x-lt-* JSON Schema for the rich invoke form; invoke validates data against it
|
|
40
41
|
* @param input.icon — curated icon name (WORKFLOW_ICONS); anything else is refused
|
|
42
|
+
* @param input.input_lookups: versioned knowledge refs the invoke form reads as `lookup.<as ?? key>`; malformed refs are refused
|
|
41
43
|
* @param input.resolver_schema — JSON Schema for resolver payload validation
|
|
42
44
|
* @param input.cron_schedule — cron expression for scheduled execution
|
|
43
45
|
* @returns `{ status: 200, data: <saved config> }`
|
|
@@ -57,6 +59,7 @@ export declare function upsertWorkflowConfig(input: {
|
|
|
57
59
|
envelope_schema?: any;
|
|
58
60
|
input_schema?: any;
|
|
59
61
|
icon?: string | null;
|
|
62
|
+
input_lookups?: EscalationLookupRef[] | null;
|
|
60
63
|
resolver_schema?: any;
|
|
61
64
|
cron_schedule?: string | null;
|
|
62
65
|
read_safe?: boolean;
|
|
@@ -39,6 +39,8 @@ exports.upsertWorkflowConfig = upsertWorkflowConfig;
|
|
|
39
39
|
exports.deleteWorkflowConfig = deleteWorkflowConfig;
|
|
40
40
|
const configService = __importStar(require("../../services/config"));
|
|
41
41
|
const workflow_icons_1 = require("../../types/workflow-icons");
|
|
42
|
+
const escalation_1 = require("../../types/escalation");
|
|
43
|
+
const lookup_refs_1 = require("../../services/knowledge/lookup-refs");
|
|
42
44
|
const cron_1 = require("../../services/cron");
|
|
43
45
|
const ltconfig_1 = require("../../modules/ltconfig");
|
|
44
46
|
/**
|
|
@@ -97,6 +99,7 @@ async function getWorkflowConfig(input) {
|
|
|
97
99
|
* @param input.envelope_schema — JSON template that pre-fills the invoke form
|
|
98
100
|
* @param input.input_schema — x-lt-* JSON Schema for the rich invoke form; invoke validates data against it
|
|
99
101
|
* @param input.icon — curated icon name (WORKFLOW_ICONS); anything else is refused
|
|
102
|
+
* @param input.input_lookups: versioned knowledge refs the invoke form reads as `lookup.<as ?? key>`; malformed refs are refused
|
|
100
103
|
* @param input.resolver_schema — JSON Schema for resolver payload validation
|
|
101
104
|
* @param input.cron_schedule — cron expression for scheduled execution
|
|
102
105
|
* @returns `{ status: 200, data: <saved config> }`
|
|
@@ -106,6 +109,17 @@ async function upsertWorkflowConfig(input) {
|
|
|
106
109
|
if (input.icon && !(0, workflow_icons_1.isWorkflowIcon)(input.icon)) {
|
|
107
110
|
return { status: 400, error: `Unknown workflow icon "${input.icon}"; choose one of WORKFLOW_ICONS` };
|
|
108
111
|
}
|
|
112
|
+
if (input.input_lookups != null) {
|
|
113
|
+
try {
|
|
114
|
+
(0, escalation_1.assertLookupRefs)(input.input_lookups);
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
return { status: 400, error: err.message };
|
|
118
|
+
}
|
|
119
|
+
const missing = await (0, lookup_refs_1.describeMissingLookupRefs)(input.input_lookups);
|
|
120
|
+
if (missing.length)
|
|
121
|
+
return { status: 400, error: missing.join('; ') };
|
|
122
|
+
}
|
|
109
123
|
// Validate cron expression before persisting
|
|
110
124
|
if (input.cron_schedule) {
|
|
111
125
|
const { validateCronSchedule } = await Promise.resolve().then(() => __importStar(require('../../services/cron')));
|
|
@@ -126,6 +140,7 @@ async function upsertWorkflowConfig(input) {
|
|
|
126
140
|
envelope_schema: input.envelope_schema ?? null,
|
|
127
141
|
input_schema: input.input_schema ?? null,
|
|
128
142
|
icon: input.icon ?? null,
|
|
143
|
+
input_lookups: input.input_lookups ?? null,
|
|
129
144
|
resolver_schema: input.resolver_schema ?? null,
|
|
130
145
|
cron_schedule: input.cron_schedule ?? null,
|
|
131
146
|
read_safe: input.read_safe ?? false,
|
|
@@ -3,4 +3,5 @@ export { listWorkers, listDiscoveredWorkflows, getCronStatus, } from './discover
|
|
|
3
3
|
export { listInvocableWorkflows, WORKFLOW_TIERS } from './invocable';
|
|
4
4
|
export type { InvocableWorkflowEntry, WorkflowTier } from './invocable';
|
|
5
5
|
export { checkInvokeInput, inputValidationFailure } from './input-validation';
|
|
6
|
+
export { getWorkflowInputLookups } from './input-lookups';
|
|
6
7
|
export { listWorkflowConfigs, getWorkflowConfig, upsertWorkflowConfig, deleteWorkflowConfig, } from './config';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.deleteWorkflowConfig = exports.upsertWorkflowConfig = exports.getWorkflowConfig = exports.listWorkflowConfigs = exports.inputValidationFailure = exports.checkInvokeInput = exports.WORKFLOW_TIERS = exports.listInvocableWorkflows = exports.getCronStatus = exports.listDiscoveredWorkflows = exports.listWorkers = exports.exportWorkflow = exports.terminateWorkflow = exports.getWorkflowResult = exports.getWorkflowStatus = exports.invokeWorkflow = void 0;
|
|
3
|
+
exports.deleteWorkflowConfig = exports.upsertWorkflowConfig = exports.getWorkflowConfig = exports.listWorkflowConfigs = exports.getWorkflowInputLookups = exports.inputValidationFailure = exports.checkInvokeInput = exports.WORKFLOW_TIERS = exports.listInvocableWorkflows = exports.getCronStatus = exports.listDiscoveredWorkflows = exports.listWorkers = exports.exportWorkflow = exports.terminateWorkflow = exports.getWorkflowResult = exports.getWorkflowStatus = exports.invokeWorkflow = void 0;
|
|
4
4
|
var invocation_1 = require("./invocation");
|
|
5
5
|
Object.defineProperty(exports, "invokeWorkflow", { enumerable: true, get: function () { return invocation_1.invokeWorkflow; } });
|
|
6
6
|
Object.defineProperty(exports, "getWorkflowStatus", { enumerable: true, get: function () { return invocation_1.getWorkflowStatus; } });
|
|
@@ -17,6 +17,8 @@ Object.defineProperty(exports, "WORKFLOW_TIERS", { enumerable: true, get: functi
|
|
|
17
17
|
var input_validation_1 = require("./input-validation");
|
|
18
18
|
Object.defineProperty(exports, "checkInvokeInput", { enumerable: true, get: function () { return input_validation_1.checkInvokeInput; } });
|
|
19
19
|
Object.defineProperty(exports, "inputValidationFailure", { enumerable: true, get: function () { return input_validation_1.inputValidationFailure; } });
|
|
20
|
+
var input_lookups_1 = require("./input-lookups");
|
|
21
|
+
Object.defineProperty(exports, "getWorkflowInputLookups", { enumerable: true, get: function () { return input_lookups_1.getWorkflowInputLookups; } });
|
|
20
22
|
var config_1 = require("./config");
|
|
21
23
|
Object.defineProperty(exports, "listWorkflowConfigs", { enumerable: true, get: function () { return config_1.listWorkflowConfigs; } });
|
|
22
24
|
Object.defineProperty(exports, "getWorkflowConfig", { enumerable: true, get: function () { return config_1.getWorkflowConfig; } });
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { LTApiResult, LTApiAuth } from '../../types/sdk';
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the versioned knowledge lookups pinned on a workflow config.
|
|
4
|
+
*
|
|
5
|
+
* The refs on `input_lookups` ARE the grant: any caller who may invoke the
|
|
6
|
+
* workflow may read exactly the pinned editions it names. A ref whose
|
|
7
|
+
* snapshot does not exist answers with `missing: true`; the batch never
|
|
8
|
+
* fails.
|
|
9
|
+
*
|
|
10
|
+
* @param input.type: workflow type name
|
|
11
|
+
* @param auth: authenticated caller; must pass the invoke predicate
|
|
12
|
+
* @returns `{ status: 200, data: { lookups: [{ domain, key, version, as?, data, missing? }] } }`
|
|
13
|
+
*/
|
|
14
|
+
export declare function getWorkflowInputLookups(input: {
|
|
15
|
+
type: string;
|
|
16
|
+
}, auth: LTApiAuth): Promise<LTApiResult>;
|
|
@@ -0,0 +1,69 @@
|
|
|
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 __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.getWorkflowInputLookups = getWorkflowInputLookups;
|
|
37
|
+
const configService = __importStar(require("../../services/config"));
|
|
38
|
+
const userService = __importStar(require("../../services/user"));
|
|
39
|
+
const invocation_access_1 = require("../../services/invocation-access");
|
|
40
|
+
const knowledge_1 = require("../../services/knowledge");
|
|
41
|
+
/**
|
|
42
|
+
* Resolve the versioned knowledge lookups pinned on a workflow config.
|
|
43
|
+
*
|
|
44
|
+
* The refs on `input_lookups` ARE the grant: any caller who may invoke the
|
|
45
|
+
* workflow may read exactly the pinned editions it names. A ref whose
|
|
46
|
+
* snapshot does not exist answers with `missing: true`; the batch never
|
|
47
|
+
* fails.
|
|
48
|
+
*
|
|
49
|
+
* @param input.type: workflow type name
|
|
50
|
+
* @param auth: authenticated caller; must pass the invoke predicate
|
|
51
|
+
* @returns `{ status: 200, data: { lookups: [{ domain, key, version, as?, data, missing? }] } }`
|
|
52
|
+
*/
|
|
53
|
+
async function getWorkflowInputLookups(input, auth) {
|
|
54
|
+
try {
|
|
55
|
+
const config = await configService.getWorkflowConfig(input.type);
|
|
56
|
+
if (!config) {
|
|
57
|
+
return { status: 404, error: 'Workflow config not found' };
|
|
58
|
+
}
|
|
59
|
+
const user = auth.userId ? await userService.getUser(auth.userId) : null;
|
|
60
|
+
if (!(0, invocation_access_1.canInvokeWorkflow)(config, user?.roles ?? [], auth.role)) {
|
|
61
|
+
return { status: 403, error: 'Not authorized to invoke this workflow' };
|
|
62
|
+
}
|
|
63
|
+
const lookups = await (0, knowledge_1.resolveLookupRefs)(config.input_lookups ?? []);
|
|
64
|
+
return { status: 200, data: { lookups } };
|
|
65
|
+
}
|
|
66
|
+
catch (err) {
|
|
67
|
+
return { status: 500, error: err.message };
|
|
68
|
+
}
|
|
69
|
+
}
|
|
@@ -5,9 +5,10 @@ import type { LTApiResult } from '../../types/sdk';
|
|
|
5
5
|
* The invoke input gate. A workflow that declares input_schema has its
|
|
6
6
|
* submitted data validated by the same shared pass the dashboard form runs,
|
|
7
7
|
* so a payload the form accepts is accepted here and a rejection lists the
|
|
8
|
-
* exact violations the form would show.
|
|
9
|
-
*
|
|
8
|
+
* exact violations the form would show. Pinned input_lookups resolve into
|
|
9
|
+
* the `lookup` domain first. No schema, no gate; a data value that is not
|
|
10
|
+
* an object is left to the service's own 400.
|
|
10
11
|
*/
|
|
11
|
-
export declare function checkInvokeInput(config: Pick<LTWorkflowConfig, 'workflow_type' | 'input_schema'> | null | undefined, data: unknown, metadata: Record<string, unknown> | null | undefined): LTValidationErrorBody | null
|
|
12
|
+
export declare function checkInvokeInput(config: Pick<LTWorkflowConfig, 'workflow_type' | 'input_schema' | 'input_lookups'> | null | undefined, data: unknown, metadata: Record<string, unknown> | null | undefined): Promise<LTValidationErrorBody | null>;
|
|
12
13
|
/** The canonical 422 result; the route serializes `data` as the body. */
|
|
13
14
|
export declare function inputValidationFailure(body: LTValidationErrorBody): LTApiResult;
|
|
@@ -4,6 +4,7 @@ exports.checkInvokeInput = checkInvokeInput;
|
|
|
4
4
|
exports.inputValidationFailure = inputValidationFailure;
|
|
5
5
|
const validate_resolver_payload_1 = require("../../shared/form-validation/validate-resolver-payload");
|
|
6
6
|
const invoke_context_1 = require("../../shared/form-validation/invoke-context");
|
|
7
|
+
const lookup_cache_1 = require("../../services/knowledge/lookup-cache");
|
|
7
8
|
const validation_1 = require("../../types/validation");
|
|
8
9
|
function isPlainObject(value) {
|
|
9
10
|
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
@@ -12,14 +13,16 @@ function isPlainObject(value) {
|
|
|
12
13
|
* The invoke input gate. A workflow that declares input_schema has its
|
|
13
14
|
* submitted data validated by the same shared pass the dashboard form runs,
|
|
14
15
|
* so a payload the form accepts is accepted here and a rejection lists the
|
|
15
|
-
* exact violations the form would show.
|
|
16
|
-
*
|
|
16
|
+
* exact violations the form would show. Pinned input_lookups resolve into
|
|
17
|
+
* the `lookup` domain first. No schema, no gate; a data value that is not
|
|
18
|
+
* an object is left to the service's own 400.
|
|
17
19
|
*/
|
|
18
|
-
function checkInvokeInput(config, data, metadata) {
|
|
20
|
+
async function checkInvokeInput(config, data, metadata) {
|
|
19
21
|
const schema = config?.input_schema;
|
|
20
22
|
if (!schema || !isPlainObject(data))
|
|
21
23
|
return null;
|
|
22
|
-
const
|
|
24
|
+
const lookup = await (0, lookup_cache_1.resolveLookupContext)(config?.input_lookups);
|
|
25
|
+
const violations = (0, validate_resolver_payload_1.validateResolverPayload)(schema, data, (0, invoke_context_1.buildInvokeFormContext)(metadata, lookup));
|
|
23
26
|
if (violations.length === 0)
|
|
24
27
|
return null;
|
|
25
28
|
const n = violations.length;
|
|
@@ -63,7 +63,7 @@ async function invokeWorkflow(input, auth) {
|
|
|
63
63
|
try {
|
|
64
64
|
await (0, workflow_invocation_1.checkInvocationRoles)(input.type, auth.userId, auth.role);
|
|
65
65
|
const config = await configService.getWorkflowConfig(input.type);
|
|
66
|
-
const violation = (0, input_validation_1.checkInvokeInput)(config, input.data, input.metadata);
|
|
66
|
+
const violation = await (0, input_validation_1.checkInvokeInput)(config, input.data, input.metadata);
|
|
67
67
|
if (violation)
|
|
68
68
|
return (0, input_validation_1.inputValidationFailure)(violation);
|
|
69
69
|
const result = await (0, workflow_invocation_1.invokeWorkflow)({
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
-- Migration 035: workflow input lookups
|
|
2
|
+
--
|
|
3
|
+
-- One opt-in column on the workflow registration:
|
|
4
|
+
-- input_lookups: versioned knowledge refs ({ domain, key, version, as? }[])
|
|
5
|
+
-- the invoke form reads under the `lookup.<as ?? key>`
|
|
6
|
+
-- context domain. NULL renders the form with no lookups.
|
|
7
|
+
|
|
8
|
+
ALTER TABLE lt_config_workflows
|
|
9
|
+
ADD COLUMN IF NOT EXISTS input_lookups JSONB;
|
|
@@ -54,6 +54,15 @@ router.get('/:type/config', async (req, res) => {
|
|
|
54
54
|
const result = await api.getWorkflowConfig({ type: req.params.type });
|
|
55
55
|
res.status(result.status).json(result.data ?? { error: result.error });
|
|
56
56
|
});
|
|
57
|
+
/**
|
|
58
|
+
* GET /api/workflows/:type/input-lookups
|
|
59
|
+
* Resolve the versioned knowledge lookups pinned on a workflow config.
|
|
60
|
+
* Open to any caller the invoke gate admits for that workflow.
|
|
61
|
+
*/
|
|
62
|
+
router.get('/:type/input-lookups', async (req, res) => {
|
|
63
|
+
const result = await api.getWorkflowInputLookups({ type: req.params.type }, { userId: req.auth?.userId ?? '', role: req.auth?.role, scopes: req.auth?.scopes });
|
|
64
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
65
|
+
});
|
|
57
66
|
/**
|
|
58
67
|
* PUT /api/workflows/:type/config
|
|
59
68
|
* Create or replace a workflow configuration.
|
|
@@ -75,6 +84,7 @@ router.put('/:type/config', auth_1.requireAdmin, async (req, res) => {
|
|
|
75
84
|
envelope_schema: req.body.envelope_schema,
|
|
76
85
|
input_schema: req.body.input_schema,
|
|
77
86
|
icon: req.body.icon,
|
|
87
|
+
input_lookups: req.body.input_lookups,
|
|
78
88
|
resolver_schema: req.body.resolver_schema,
|
|
79
89
|
cron_schedule: req.body.cron_schedule,
|
|
80
90
|
});
|
|
@@ -18,6 +18,7 @@ async function loadAllConfigs() {
|
|
|
18
18
|
envelopeSchema: c.envelope_schema ?? null,
|
|
19
19
|
inputSchema: c.input_schema ?? null,
|
|
20
20
|
icon: c.icon ?? null,
|
|
21
|
+
inputLookups: c.input_lookups ?? null,
|
|
21
22
|
resolverSchema: c.resolver_schema ?? null,
|
|
22
23
|
cronSchedule: c.cron_schedule ?? null,
|
|
23
24
|
executeAs: c.execute_as ?? null,
|
|
@@ -28,6 +28,7 @@ async function getWorkflowConfig(workflowType) {
|
|
|
28
28
|
envelope_schema: wf.envelope_schema ?? null,
|
|
29
29
|
input_schema: wf.input_schema ?? null,
|
|
30
30
|
icon: wf.icon ?? null,
|
|
31
|
+
input_lookups: wf.input_lookups ?? null,
|
|
31
32
|
resolver_schema: wf.resolver_schema ?? null,
|
|
32
33
|
cron_schedule: wf.cron_schedule ?? null,
|
|
33
34
|
execute_as: wf.execute_as ?? null,
|
|
@@ -68,6 +69,7 @@ async function listWorkflowConfigs() {
|
|
|
68
69
|
envelope_schema: wf.envelope_schema ?? null,
|
|
69
70
|
input_schema: wf.input_schema ?? null,
|
|
70
71
|
icon: wf.icon ?? null,
|
|
72
|
+
input_lookups: wf.input_lookups ?? null,
|
|
71
73
|
resolver_schema: wf.resolver_schema ?? null,
|
|
72
74
|
cron_schedule: wf.cron_schedule ?? null,
|
|
73
75
|
execute_as: wf.execute_as ?? null,
|
|
@@ -5,13 +5,13 @@ export declare const LIST_ALL_WORKFLOWS = "SELECT * FROM lt_config_workflows ORD
|
|
|
5
5
|
export declare const LIST_ALL_ROLES = "SELECT * FROM lt_config_roles ORDER BY workflow_type, role";
|
|
6
6
|
export declare const LIST_ALL_INVOCATION_ROLES = "SELECT * FROM lt_config_invocation_roles ORDER BY workflow_type, role";
|
|
7
7
|
export declare const ENSURE_ROLE_EXISTS = "INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING";
|
|
8
|
-
export declare const UPSERT_WORKFLOW = "INSERT INTO lt_config_workflows\n (workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe, input_schema, icon)\nVALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)\nON CONFLICT (workflow_type) DO UPDATE SET\n invocable = EXCLUDED.invocable,\n task_queue = EXCLUDED.task_queue,\n default_role = EXCLUDED.default_role,\n description = EXCLUDED.description,\n consumes = EXCLUDED.consumes,\n envelope_schema = EXCLUDED.envelope_schema,\n resolver_schema = EXCLUDED.resolver_schema,\n cron_schedule = EXCLUDED.cron_schedule,\n tool_tags = EXCLUDED.tool_tags,\n execute_as = EXCLUDED.execute_as,\n certified = EXCLUDED.certified,\n read_safe = EXCLUDED.read_safe,\n input_schema = EXCLUDED.input_schema,\n icon = EXCLUDED.icon";
|
|
8
|
+
export declare const UPSERT_WORKFLOW = "INSERT INTO lt_config_workflows\n (workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe, input_schema, icon, input_lookups)\nVALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)\nON CONFLICT (workflow_type) DO UPDATE SET\n invocable = EXCLUDED.invocable,\n task_queue = EXCLUDED.task_queue,\n default_role = EXCLUDED.default_role,\n description = EXCLUDED.description,\n consumes = EXCLUDED.consumes,\n envelope_schema = EXCLUDED.envelope_schema,\n resolver_schema = EXCLUDED.resolver_schema,\n cron_schedule = EXCLUDED.cron_schedule,\n tool_tags = EXCLUDED.tool_tags,\n execute_as = EXCLUDED.execute_as,\n certified = EXCLUDED.certified,\n read_safe = EXCLUDED.read_safe,\n input_schema = EXCLUDED.input_schema,\n icon = EXCLUDED.icon,\n input_lookups = EXCLUDED.input_lookups";
|
|
9
9
|
export declare const DELETE_CONFIG_ROLES = "DELETE FROM lt_config_roles WHERE workflow_type = $1";
|
|
10
10
|
export declare const INSERT_CONFIG_ROLE = "INSERT INTO lt_config_roles (workflow_type, role) VALUES ($1, $2)";
|
|
11
11
|
export declare const DELETE_INVOCATION_ROLES = "DELETE FROM lt_config_invocation_roles WHERE workflow_type = $1";
|
|
12
12
|
export declare const INSERT_INVOCATION_ROLE = "INSERT INTO lt_config_invocation_roles (workflow_type, role) VALUES ($1, $2)";
|
|
13
13
|
export declare const DELETE_WORKFLOW = "DELETE FROM lt_config_workflows WHERE workflow_type = $1";
|
|
14
|
-
export declare const SEED_WORKFLOW_CONFIG = "INSERT INTO lt_config_workflows\n (workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe, input_schema, icon)\nVALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)\nON CONFLICT (workflow_type) DO NOTHING";
|
|
14
|
+
export declare const SEED_WORKFLOW_CONFIG = "INSERT INTO lt_config_workflows\n (workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe, input_schema, icon, input_lookups)\nVALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)\nON CONFLICT (workflow_type) DO NOTHING";
|
|
15
15
|
export declare const SEED_CONFIG_ROLE = "INSERT INTO lt_config_roles (workflow_type, role) VALUES ($1, $2)\nON CONFLICT (workflow_type, role) DO NOTHING";
|
|
16
16
|
export declare const SEED_INVOCATION_ROLE = "INSERT INTO lt_config_invocation_roles (workflow_type, role) VALUES ($1, $2)\nON CONFLICT (workflow_type, role) DO NOTHING";
|
|
17
17
|
export declare const GET_PROVIDER_DATA = "SELECT workflow_type, data, completed_at\n FROM lt_tasks\n WHERE origin_id = $1\n AND workflow_type = ANY($2)\n AND status = 'completed'\n ORDER BY completed_at DESC";
|
|
@@ -23,8 +23,8 @@ exports.ENSURE_ROLE_EXISTS = `\
|
|
|
23
23
|
INSERT INTO lt_roles (role) VALUES ($1) ON CONFLICT DO NOTHING`;
|
|
24
24
|
exports.UPSERT_WORKFLOW = `\
|
|
25
25
|
INSERT INTO lt_config_workflows
|
|
26
|
-
(workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe, input_schema, icon)
|
|
27
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
|
|
26
|
+
(workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe, input_schema, icon, input_lookups)
|
|
27
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
|
28
28
|
ON CONFLICT (workflow_type) DO UPDATE SET
|
|
29
29
|
invocable = EXCLUDED.invocable,
|
|
30
30
|
task_queue = EXCLUDED.task_queue,
|
|
@@ -39,7 +39,8 @@ ON CONFLICT (workflow_type) DO UPDATE SET
|
|
|
39
39
|
certified = EXCLUDED.certified,
|
|
40
40
|
read_safe = EXCLUDED.read_safe,
|
|
41
41
|
input_schema = EXCLUDED.input_schema,
|
|
42
|
-
icon = EXCLUDED.icon
|
|
42
|
+
icon = EXCLUDED.icon,
|
|
43
|
+
input_lookups = EXCLUDED.input_lookups`;
|
|
43
44
|
exports.DELETE_CONFIG_ROLES = `\
|
|
44
45
|
DELETE FROM lt_config_roles WHERE workflow_type = $1`;
|
|
45
46
|
exports.INSERT_CONFIG_ROLE = `\
|
|
@@ -55,8 +56,8 @@ DELETE FROM lt_config_workflows WHERE workflow_type = $1`;
|
|
|
55
56
|
// ------------------------------------------------------------------ //
|
|
56
57
|
exports.SEED_WORKFLOW_CONFIG = `\
|
|
57
58
|
INSERT INTO lt_config_workflows
|
|
58
|
-
(workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe, input_schema, icon)
|
|
59
|
-
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)
|
|
59
|
+
(workflow_type, invocable, task_queue, default_role, description, consumes, envelope_schema, resolver_schema, cron_schedule, tool_tags, execute_as, certified, read_safe, input_schema, icon, input_lookups)
|
|
60
|
+
VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15, $16)
|
|
60
61
|
ON CONFLICT (workflow_type) DO NOTHING`;
|
|
61
62
|
exports.SEED_CONFIG_ROLE = `\
|
|
62
63
|
INSERT INTO lt_config_roles (workflow_type, role) VALUES ($1, $2)
|
|
@@ -14,6 +14,10 @@ const sql_1 = require("./sql");
|
|
|
14
14
|
* seed blocks without `certified`): derived from roles/consumes presence —
|
|
15
15
|
* the rule that governed the tier before the flag existed.
|
|
16
16
|
*/
|
|
17
|
+
/** pg binds JS arrays as Postgres arrays; jsonb columns take the JSON text. */
|
|
18
|
+
function toJsonb(value) {
|
|
19
|
+
return value == null ? null : JSON.stringify(value);
|
|
20
|
+
}
|
|
17
21
|
function resolveCertified(config) {
|
|
18
22
|
return (config.certified ??
|
|
19
23
|
((config.roles?.length ?? 0) > 0 || (config.consumes?.length ?? 0) > 0));
|
|
@@ -55,6 +59,7 @@ async function replaceWorkflowConfig(config, certified) {
|
|
|
55
59
|
config.read_safe ?? false,
|
|
56
60
|
config.input_schema ?? null,
|
|
57
61
|
config.icon ?? null,
|
|
62
|
+
toJsonb(config.input_lookups),
|
|
58
63
|
]);
|
|
59
64
|
// Replace roles
|
|
60
65
|
await client.query(sql_1.DELETE_CONFIG_ROLES, [config.workflow_type]);
|
|
@@ -118,6 +123,7 @@ async function seedWorkflowConfig(config) {
|
|
|
118
123
|
config.read_safe ?? false,
|
|
119
124
|
config.input_schema ?? null,
|
|
120
125
|
config.icon ?? null,
|
|
126
|
+
toJsonb(config.input_lookups),
|
|
121
127
|
]);
|
|
122
128
|
const inserted = (rowCount ?? 0) > 0;
|
|
123
129
|
if (inserted) {
|
|
@@ -152,6 +158,8 @@ async function seedWorkflowConfig(config) {
|
|
|
152
158
|
drifts.push('input_schema');
|
|
153
159
|
if ((config.icon ?? null) !== (existing.icon ?? null))
|
|
154
160
|
drifts.push('icon');
|
|
161
|
+
if (JSON.stringify(config.input_lookups ?? null) !== JSON.stringify(existing.input_lookups ?? null))
|
|
162
|
+
drifts.push('input_lookups');
|
|
155
163
|
if (drifts.length) {
|
|
156
164
|
logger_1.loggerRegistry.warn(`[long-tail] config drift: ${config.workflow_type} — ${drifts.join(', ')} differ between code and DB`);
|
|
157
165
|
}
|
|
@@ -191,7 +199,8 @@ async function applyWorkflowConfig(config) {
|
|
|
191
199
|
(0, node_util_1.isDeepStrictEqual)(existing.envelope_schema ?? null, config.envelope_schema ?? null) &&
|
|
192
200
|
(0, node_util_1.isDeepStrictEqual)(existing.resolver_schema ?? null, config.resolver_schema ?? null) &&
|
|
193
201
|
(0, node_util_1.isDeepStrictEqual)(existing.input_schema ?? null, config.input_schema ?? null) &&
|
|
194
|
-
(existing.icon ?? null) === (config.icon ?? null)
|
|
202
|
+
(existing.icon ?? null) === (config.icon ?? null) &&
|
|
203
|
+
(0, node_util_1.isDeepStrictEqual)(existing.input_lookups ?? null, config.input_lookups ?? null);
|
|
195
204
|
if (unchanged)
|
|
196
205
|
return 'unchanged';
|
|
197
206
|
}
|
|
@@ -24,6 +24,7 @@ exports.listKnowledgeVersions = listKnowledgeVersions;
|
|
|
24
24
|
const db_1 = require("../../lib/db");
|
|
25
25
|
const sql_1 = require("./sql");
|
|
26
26
|
__exportStar(require("./lookup-cache"), exports);
|
|
27
|
+
__exportStar(require("./lookup-refs"), exports);
|
|
27
28
|
/** The immutable snapshot at (domain, key, version), uncached full shape. */
|
|
28
29
|
async function getKnowledgeVersion(domain, key, version) {
|
|
29
30
|
const pool = (0, db_1.getPool)();
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { EscalationLookupRef } from '../../types/escalation';
|
|
2
|
+
/**
|
|
3
|
+
* One message per ref whose pinned edition does not exist, naming what does.
|
|
4
|
+
* Shape is the caller's job (assertLookupRefs); this answers the second
|
|
5
|
+
* question a saver has: is there an edition behind the pin.
|
|
6
|
+
*/
|
|
7
|
+
export declare function describeMissingLookupRefs(refs: EscalationLookupRef[]): Promise<string[]>;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.describeMissingLookupRefs = describeMissingLookupRefs;
|
|
4
|
+
const db_1 = require("../../lib/db");
|
|
5
|
+
const lookup_cache_1 = require("./lookup-cache");
|
|
6
|
+
const sql_1 = require("./sql");
|
|
7
|
+
/**
|
|
8
|
+
* One message per ref whose pinned edition does not exist, naming what does.
|
|
9
|
+
* Shape is the caller's job (assertLookupRefs); this answers the second
|
|
10
|
+
* question a saver has: is there an edition behind the pin.
|
|
11
|
+
*/
|
|
12
|
+
async function describeMissingLookupRefs(refs) {
|
|
13
|
+
const problems = [];
|
|
14
|
+
for (const ref of refs) {
|
|
15
|
+
const snapshot = await (0, lookup_cache_1.getKnowledgeSnapshot)(ref.domain, ref.key, ref.version);
|
|
16
|
+
if (snapshot)
|
|
17
|
+
continue;
|
|
18
|
+
const { rows } = await (0, db_1.getPool)().query(sql_1.LIST_KNOWLEDGE_VERSIONS, [ref.domain, ref.key]);
|
|
19
|
+
const versions = rows.map((r) => Number(r.version)).sort((a, b) => a - b);
|
|
20
|
+
const where = versions.length
|
|
21
|
+
? `editions: ${versions.map((v) => `v${v}`).join(', ')}`
|
|
22
|
+
: `no knowledge entry ${ref.domain}/${ref.key}`;
|
|
23
|
+
problems.push(`Lookup ref ${ref.domain}/${ref.key} v${ref.version} names no edition (${where})`);
|
|
24
|
+
}
|
|
25
|
+
return problems;
|
|
26
|
+
}
|
|
@@ -7,12 +7,12 @@ export declare const findTasksSchema: z.ZodObject<{
|
|
|
7
7
|
limit: z.ZodDefault<z.ZodOptional<z.ZodNumber>>;
|
|
8
8
|
}, "strip", z.ZodTypeAny, {
|
|
9
9
|
limit: number;
|
|
10
|
-
status?: "
|
|
10
|
+
status?: "completed" | "pending" | "in_progress" | "needs_intervention" | "failed" | undefined;
|
|
11
11
|
workflow_type?: string | undefined;
|
|
12
12
|
workflow_id?: string | undefined;
|
|
13
13
|
origin_id?: string | undefined;
|
|
14
14
|
}, {
|
|
15
|
-
status?: "
|
|
15
|
+
status?: "completed" | "pending" | "in_progress" | "needs_intervention" | "failed" | undefined;
|
|
16
16
|
workflow_type?: string | undefined;
|
|
17
17
|
workflow_id?: string | undefined;
|
|
18
18
|
origin_id?: string | undefined;
|
|
@@ -38,6 +38,7 @@ export declare function resolveChecklistDefault(fieldSchema: Record<string, unkn
|
|
|
38
38
|
* which remains the at-least-one check.
|
|
39
39
|
*/
|
|
40
40
|
export declare function validateRequireAll(value: unknown, fieldSchema: Record<string, unknown> | undefined, ctx?: Record<string, unknown>): string | undefined;
|
|
41
|
+
export declare const INVALID_JSON = "Invalid JSON";
|
|
41
42
|
/**
|
|
42
43
|
* Runtime type check against the field's declared JSON Schema `type`. Guards
|
|
43
44
|
* the payload contract for API-first callers (a form widget already produces
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.X_LT_DEFAULT_CHECKED = void 0;
|
|
3
|
+
exports.INVALID_JSON = exports.X_LT_DEFAULT_CHECKED = void 0;
|
|
4
4
|
exports.resolveChecklistItems = resolveChecklistItems;
|
|
5
5
|
exports.resolveChecklistDefault = resolveChecklistDefault;
|
|
6
6
|
exports.validateRequireAll = validateRequireAll;
|
|
@@ -87,6 +87,18 @@ function validateRequireAll(value, fieldSchema, ctx) {
|
|
|
87
87
|
return undefined;
|
|
88
88
|
return `${incomplete} of ${mandatory.length} checks incomplete`;
|
|
89
89
|
}
|
|
90
|
+
exports.INVALID_JSON = 'Invalid JSON';
|
|
91
|
+
/** The json editor holds unparseable text as a string at the field's key. */
|
|
92
|
+
function isJsonWidget(fieldSchema) {
|
|
93
|
+
return fieldSchema?.['x-lt-widget'] === 'json';
|
|
94
|
+
}
|
|
95
|
+
/** A list is edited through the json editor or a multi-select; any other list value is display only. */
|
|
96
|
+
function isEditableList(fieldSchema) {
|
|
97
|
+
return isJsonWidget(fieldSchema) || fieldSchema?.[x_lt_options_1.X_LT_OPTIONS] !== undefined;
|
|
98
|
+
}
|
|
99
|
+
function plural(n, noun) {
|
|
100
|
+
return `${n} ${noun}${n === 1 ? '' : 's'}`;
|
|
101
|
+
}
|
|
90
102
|
/**
|
|
91
103
|
* Runtime type check against the field's declared JSON Schema `type`. Guards
|
|
92
104
|
* the payload contract for API-first callers (a form widget already produces
|
|
@@ -109,9 +121,13 @@ function validateFieldType(value, fieldSchema) {
|
|
|
109
121
|
case 'boolean':
|
|
110
122
|
return typeof value === 'boolean' ? undefined : 'Expected true or false';
|
|
111
123
|
case 'array':
|
|
112
|
-
|
|
124
|
+
if (Array.isArray(value))
|
|
125
|
+
return undefined;
|
|
126
|
+
return isJsonWidget(fieldSchema) && typeof value === 'string' ? exports.INVALID_JSON : 'Expected a list';
|
|
113
127
|
case 'object':
|
|
114
|
-
|
|
128
|
+
if (typeof value === 'object' && !Array.isArray(value))
|
|
129
|
+
return undefined;
|
|
130
|
+
return isJsonWidget(fieldSchema) && typeof value === 'string' ? exports.INVALID_JSON : 'Expected an object';
|
|
115
131
|
default:
|
|
116
132
|
return undefined;
|
|
117
133
|
}
|
|
@@ -136,7 +152,7 @@ function validateFieldConstraints(value, fieldSchema, ctx) {
|
|
|
136
152
|
// interpolated list that currently offers nothing fails closed — a stale
|
|
137
153
|
// cascade child value never submits.
|
|
138
154
|
const allowed = (0, x_lt_options_1.resolveFieldOptions)(fieldSchema, ctx);
|
|
139
|
-
if (allowed !== undefined && value !== undefined && value !== null && value !== '') {
|
|
155
|
+
if (allowed !== undefined && value !== undefined && value !== null && value !== '' && !Array.isArray(value)) {
|
|
140
156
|
if (allowed.length === 0) {
|
|
141
157
|
return 'No valid options for this selection';
|
|
142
158
|
}
|
|
@@ -144,6 +160,12 @@ function validateFieldConstraints(value, fieldSchema, ctx) {
|
|
|
144
160
|
return `Must be one of: ${allowed.map((o) => String(o.value)).join(', ')}`;
|
|
145
161
|
}
|
|
146
162
|
}
|
|
163
|
+
if (Array.isArray(value)) {
|
|
164
|
+
return isEditableList(fieldSchema) ? validateListConstraints(value, fieldSchema, allowed, ctx) : undefined;
|
|
165
|
+
}
|
|
166
|
+
if (value !== null && typeof value === 'object') {
|
|
167
|
+
return validateMapConstraints(value, fieldSchema, ctx);
|
|
168
|
+
}
|
|
147
169
|
if (typeof value === 'string') {
|
|
148
170
|
const s = value.trim();
|
|
149
171
|
if (s === '')
|
|
@@ -196,6 +218,65 @@ function validateFieldConstraints(value, fieldSchema, ctx) {
|
|
|
196
218
|
}
|
|
197
219
|
return undefined;
|
|
198
220
|
}
|
|
221
|
+
/**
|
|
222
|
+
* An edited list: every item must belong to the field's `x-lt-options` when
|
|
223
|
+
* declared, the length must sit within `minItems`/`maxItems`, and each item
|
|
224
|
+
* must satisfy `items`. An empty list passes; presence is the required
|
|
225
|
+
* check's job.
|
|
226
|
+
*/
|
|
227
|
+
function validateListConstraints(value, fieldSchema, allowed, ctx) {
|
|
228
|
+
if (value.length === 0)
|
|
229
|
+
return undefined;
|
|
230
|
+
if (allowed !== undefined) {
|
|
231
|
+
if (allowed.length === 0)
|
|
232
|
+
return 'No valid options for this selection';
|
|
233
|
+
if (value.some((v) => !allowed.some((o) => o.value === v))) {
|
|
234
|
+
return `Must be one of: ${allowed.map((o) => String(o.value)).join(', ')}`;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
const minItems = resolveNumericConstraint(fieldSchema.minItems, undefined, ctx);
|
|
238
|
+
if (minItems !== undefined && value.length < minItems)
|
|
239
|
+
return `At least ${plural(minItems, 'item')}`;
|
|
240
|
+
const maxItems = resolveNumericConstraint(fieldSchema.maxItems, undefined, ctx);
|
|
241
|
+
if (maxItems !== undefined && value.length > maxItems)
|
|
242
|
+
return `At most ${plural(maxItems, 'item')}`;
|
|
243
|
+
const items = fieldSchema.items;
|
|
244
|
+
if (items && typeof items === 'object' && !Array.isArray(items)) {
|
|
245
|
+
const itemSchema = items;
|
|
246
|
+
for (let i = 0; i < value.length; i++) {
|
|
247
|
+
const err = validateFieldType(value[i], itemSchema) ?? validateFieldConstraints(value[i], itemSchema, ctx);
|
|
248
|
+
if (err)
|
|
249
|
+
return `Item ${i + 1}: ${err}`;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return undefined;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* A map value: keys must belong to `propertyNames.enum` when declared, and
|
|
256
|
+
* every value must satisfy an object-shaped `additionalProperties`.
|
|
257
|
+
*/
|
|
258
|
+
function validateMapConstraints(value, fieldSchema, ctx) {
|
|
259
|
+
const entries = Object.entries(value);
|
|
260
|
+
if (entries.length === 0)
|
|
261
|
+
return undefined;
|
|
262
|
+
const propertyNames = fieldSchema.propertyNames;
|
|
263
|
+
const allowedKeys = Array.isArray(propertyNames?.enum) ? propertyNames.enum.map(String) : undefined;
|
|
264
|
+
if (allowedKeys) {
|
|
265
|
+
const unknown = entries.find(([k]) => !allowedKeys.includes(k));
|
|
266
|
+
if (unknown)
|
|
267
|
+
return `Unknown key "${unknown[0]}". Allowed: ${allowedKeys.join(', ')}`;
|
|
268
|
+
}
|
|
269
|
+
const ap = fieldSchema.additionalProperties;
|
|
270
|
+
if (ap && typeof ap === 'object' && !Array.isArray(ap)) {
|
|
271
|
+
const valueSchema = ap;
|
|
272
|
+
for (const [k, v] of entries) {
|
|
273
|
+
const err = validateFieldType(v, valueSchema) ?? validateFieldConstraints(v, valueSchema, ctx);
|
|
274
|
+
if (err)
|
|
275
|
+
return `"${k}": ${err}`;
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
return undefined;
|
|
279
|
+
}
|
|
199
280
|
function resolveNumericConstraint(staticVal, dynamicPath, ctx) {
|
|
200
281
|
if (typeof staticVal === 'number')
|
|
201
282
|
return staticVal;
|
|
@@ -232,13 +313,20 @@ function validateField(value, fieldSchema, isRequired, isTouched, ctx) {
|
|
|
232
313
|
if (isRequired) {
|
|
233
314
|
if (value === undefined || value === null)
|
|
234
315
|
return 'Required';
|
|
235
|
-
|
|
316
|
+
// A checkbox must be checked; a boolean with an option list is answered by either value.
|
|
317
|
+
if (typeof value === 'boolean' && !value && (0, x_lt_options_1.resolveFieldOptions)(fieldSchema, ctx) === undefined)
|
|
236
318
|
return 'Required';
|
|
237
319
|
if (typeof value === 'string' && value.trim() === '')
|
|
238
320
|
return 'Required';
|
|
321
|
+
// A picked or edited list needs one item; a plain list value is not checked.
|
|
322
|
+
if (Array.isArray(value) && value.length === 0 && isEditableList(fieldSchema))
|
|
323
|
+
return 'Required';
|
|
239
324
|
if (typeof value === 'object' && !Array.isArray(value) && value !== null) {
|
|
240
325
|
const vals = Object.values(value);
|
|
241
|
-
if (vals.length === 0
|
|
326
|
+
if (vals.length === 0)
|
|
327
|
+
return 'Required';
|
|
328
|
+
// A json map is answered by any key; every other object needs one truthy value.
|
|
329
|
+
if (!isJsonWidget(fieldSchema) && vals.every((v) => !v))
|
|
242
330
|
return 'Required';
|
|
243
331
|
}
|
|
244
332
|
}
|