@hotmeshio/long-tail 0.24.2 → 0.25.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/escalations/accumulate.js +8 -4
- package/build/api/escalations/metadata.js +4 -3
- package/build/api/escalations/related.d.ts +1 -1
- package/build/api/escalations/related.js +28 -18
- package/build/services/escalation/accumulate.d.ts +3 -1
- package/build/services/escalation/facet-sql.js +4 -2
- package/build/services/escalation/index.d.ts +1 -0
- package/build/services/escalation/index.js +4 -1
- package/build/services/escalation/related-extremes.d.ts +17 -0
- package/build/services/escalation/related-extremes.js +65 -0
- package/build/services/escalation/related.d.ts +4 -0
- package/build/services/escalation/related.js +2 -0
- package/build/services/escalation/resolver-validation.js +7 -0
- package/build/services/escalation/roster.d.ts +13 -0
- package/build/services/escalation/roster.js +37 -0
- package/build/services/escalation/submit-guard.d.ts +3 -0
- package/build/services/escalation/submit-guard.js +21 -2
- package/build/services/mcp/server-tools.js +1 -1
- package/build/shared/form-validation/display-only-widgets.d.ts +1 -0
- package/build/shared/form-validation/display-only-widgets.js +1 -0
- package/build/shared/form-validation/index.d.ts +1 -0
- package/build/shared/form-validation/index.js +1 -0
- package/build/shared/form-validation/validate-resolver-payload.d.ts +1 -0
- package/build/shared/form-validation/validate-resolver-payload.js +2 -1
- package/build/shared/form-validation/x-lt-help.d.ts +1 -1
- package/build/shared/form-validation/x-lt-help.js +2 -1
- package/build/shared/form-validation/x-lt-roster.d.ts +92 -0
- package/build/shared/form-validation/x-lt-roster.js +234 -0
- package/build/shared/form-validation/x-lt-show-if.d.ts +8 -0
- package/build/shared/form-validation/x-lt-show-if.js +13 -0
- package/build/shared/form-validation/x-lt-submit-guard.d.ts +14 -1
- package/build/shared/form-validation/x-lt-submit-guard.js +4 -1
- package/build/shared/list-schema/aggregate.d.ts +19 -0
- package/build/shared/list-schema/aggregate.js +66 -0
- package/build/shared/list-schema/index.d.ts +2 -0
- package/build/shared/list-schema/index.js +7 -1
- package/build/shared/list-schema/types.d.ts +15 -2
- package/build/shared/list-schema/types.js +6 -1
- package/build/shared/list-schema/validate.js +28 -2
- package/build/tsconfig.tsbuildinfo +1 -1
- package/dashboard/dist/assets/{AdminDashboard-B3mkMIOT.js → AdminDashboard-DWVRAu63.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-B3mkMIOT.js.map → AdminDashboard-DWVRAu63.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-CQj2B678.js → AgentConfigPage-Exjtjbvp.js} +6 -6
- package/dashboard/dist/assets/{AgentConfigPage-CQj2B678.js.map → AgentConfigPage-Exjtjbvp.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-CzrD8JJI.js → AgentDetailPage-nDbZo6RB.js} +3 -3
- package/dashboard/dist/assets/{AgentDetailPage-CzrD8JJI.js.map → AgentDetailPage-nDbZo6RB.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-jg2OFwyW.js → AgentsPage-Dsk9iHps.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-jg2OFwyW.js.map → AgentsPage-Dsk9iHps.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-D1mCqj8Z.js +2 -0
- package/dashboard/dist/assets/{AvailableEscalationsPage-IS28XBRe.js.map → AvailableEscalationsPage-D1mCqj8Z.js.map} +1 -1
- package/dashboard/dist/assets/{BotPicker-DvEJFpwS.js → BotPicker-u5760MWZ.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-DvEJFpwS.js.map → BotPicker-u5760MWZ.js.map} +1 -1
- package/dashboard/dist/assets/{BulkAssignModal-BoQCi-YC.js → BulkAssignModal-CtISXXmW.js} +2 -2
- package/dashboard/dist/assets/{BulkAssignModal-BoQCi-YC.js.map → BulkAssignModal-CtISXXmW.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-kwlrcb_t.js → CapabilitiesPage-BKIlrQfU.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-kwlrcb_t.js.map → CapabilitiesPage-BKIlrQfU.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-UMIV4KIj.js → CollapsibleSection-DlBVTFqc.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-UMIV4KIj.js.map → CollapsibleSection-DlBVTFqc.js.map} +1 -1
- package/dashboard/dist/assets/{ConfirmDeleteModal-C5puwMg7.js → ConfirmDeleteModal-Pabkchg_.js} +2 -2
- package/dashboard/dist/assets/{ConfirmDeleteModal-C5puwMg7.js.map → ConfirmDeleteModal-Pabkchg_.js.map} +1 -1
- package/dashboard/dist/assets/{ConnectedAppsPage-drZa65kP.js → ConnectedAppsPage-DG2YZGN3.js} +2 -2
- package/dashboard/dist/assets/{ConnectedAppsPage-drZa65kP.js.map → ConnectedAppsPage-DG2YZGN3.js.map} +1 -1
- package/dashboard/dist/assets/CountdownTimer-5t8S2qMi.js +2 -0
- package/dashboard/dist/assets/CountdownTimer-5t8S2qMi.js.map +1 -0
- package/dashboard/dist/assets/{CredentialsPage-CaFQ6vLb.js → CredentialsPage-CPVbYQow.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-CaFQ6vLb.js.map → CredentialsPage-CPVbYQow.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-Co5j7tzW.js → CronLabel-C7UbTrO-.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-Co5j7tzW.js.map → CronLabel-C7UbTrO-.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-B1Tdfddc.js → DropZone-DCZpORx-.js} +2 -2
- package/dashboard/dist/assets/{DropZone-B1Tdfddc.js.map → DropZone-DCZpORx-.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-WJyE9G0T.js → ElapsedCell-Yz7-LbtE.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-WJyE9G0T.js.map → ElapsedCell-Yz7-LbtE.js.map} +1 -1
- package/dashboard/dist/assets/{EntityTimelinePanel-CDFFfX9d.js → EntityTimelinePanel-xgPRdKEq.js} +2 -2
- package/dashboard/dist/assets/{EntityTimelinePanel-CDFFfX9d.js.map → EntityTimelinePanel-xgPRdKEq.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationListSchemaPage-CMrUlaKI.js → EscalationListSchemaPage-CqCd-y6B.js} +3 -3
- package/dashboard/dist/assets/{EscalationListSchemaPage-CMrUlaKI.js.map → EscalationListSchemaPage-CqCd-y6B.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationSchemaPage-Dx2Lyfrz.js → EscalationSchemaPage-D8YVQNph.js} +3 -3
- package/dashboard/dist/assets/{EscalationSchemaPage-Dx2Lyfrz.js.map → EscalationSchemaPage-D8YVQNph.js.map} +1 -1
- package/dashboard/dist/assets/EscalationTimeline-D6jrDcBb.js +2 -0
- package/dashboard/dist/assets/{EscalationTimeline-d3nvshOk.js.map → EscalationTimeline-D6jrDcBb.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationTitleSelect-DWQieOM9.js → EscalationTitleSelect-CGGT-QTu.js} +2 -2
- package/dashboard/dist/assets/{EscalationTitleSelect-DWQieOM9.js.map → EscalationTitleSelect-CGGT-QTu.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-BX8Cpi0X.js → EscalationsOverview-B7O4ttP4.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-BX8Cpi0X.js.map → EscalationsOverview-B7O4ttP4.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-D1-ITVXS.js → EventTable-DJniXnhe.js} +2 -2
- package/dashboard/dist/assets/{EventTable-D1-ITVXS.js.map → EventTable-DJniXnhe.js.map} +1 -1
- package/dashboard/dist/assets/{FilterBar-ABC9ctin.js → FilterBar-f-LEKcHI.js} +2 -2
- package/dashboard/dist/assets/{FilterBar-ABC9ctin.js.map → FilterBar-f-LEKcHI.js.map} +1 -1
- package/dashboard/dist/assets/{FormattedValue-k27T4bG7.js → FormattedValue-CUtv1g3k.js} +2 -2
- package/dashboard/dist/assets/{FormattedValue-k27T4bG7.js.map → FormattedValue-CUtv1g3k.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-DKUErxWc.js → GraphInvokePage-CR0MtYv8.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-DKUErxWc.js.map → GraphInvokePage-CR0MtYv8.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-C6u9dIpR.js → HomePage-HVw7T5ER.js} +2 -2
- package/dashboard/dist/assets/{HomePage-C6u9dIpR.js.map → HomePage-HVw7T5ER.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-gA5rFzkz.js → ListToolbar-CTGK5BXW.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-gA5rFzkz.js.map → ListToolbar-CTGK5BXW.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-BOPo3J_i.js → McpOverview-B2bbKKu2.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-BOPo3J_i.js.map → McpOverview-B2bbKKu2.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-BRVVi0Zh.js → McpQueryDetailPage-DgLxwwJy.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-BRVVi0Zh.js.map → McpQueryDetailPage-DgLxwwJy.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-Dmbb6SAc.js → McpQueryPage-CSkczeJP.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-Dmbb6SAc.js.map → McpQueryPage-CSkczeJP.js.map} +1 -1
- package/dashboard/dist/assets/McpRunDetailPage-CrUsDPRD.js +2 -0
- package/dashboard/dist/assets/{McpRunDetailPage-bMXNO-6V.js.map → McpRunDetailPage-CrUsDPRD.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-Bi9AMWja.js → McpRunsPage-twxFdTtJ.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-Bi9AMWja.js.map → McpRunsPage-twxFdTtJ.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-DwM5pNA4.js → NamespacePill-CSZT2fA4.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-DwM5pNA4.js.map → NamespacePill-CSZT2fA4.js.map} +1 -1
- package/dashboard/dist/assets/{OperationsPage-CvR74ZUJ.js → OperationsPage-gOVMrL_a.js} +3 -3
- package/dashboard/dist/assets/{OperationsPage-CvR74ZUJ.js.map → OperationsPage-gOVMrL_a.js.map} +1 -1
- package/dashboard/dist/assets/OperatorDashboard-BoeZIyup.js +2 -0
- package/dashboard/dist/assets/{OperatorDashboard-DctmZw10.js.map → OperatorDashboard-BoeZIyup.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-CMd3f4bz.js → PageHeader-C4z1ExF7.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-CMd3f4bz.js.map → PageHeader-C4z1ExF7.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-DHj8Mqox.js → PageHeaderWithStats-B_f_Sz_Q.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-DHj8Mqox.js.map → PageHeaderWithStats-B_f_Sz_Q.js.map} +1 -1
- package/dashboard/dist/assets/{PersonaDetailPage-Ubqm2rQH.js → PersonaDetailPage-CDktk1mi.js} +2 -2
- package/dashboard/dist/assets/{PersonaDetailPage-Ubqm2rQH.js.map → PersonaDetailPage-CDktk1mi.js.map} +1 -1
- package/dashboard/dist/assets/{PersonasPage-BpFL2rpo.js → PersonasPage-Bs097x07.js} +2 -2
- package/dashboard/dist/assets/{PersonasPage-BpFL2rpo.js.map → PersonasPage-Bs097x07.js.map} +1 -1
- package/dashboard/dist/assets/PortalPage-DjfjerpF.js +2 -0
- package/dashboard/dist/assets/{PortalPage-Dfc_Q02R.js.map → PortalPage-DjfjerpF.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-liJspvQh.js → ProcessDetailPage-B30RatjD.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-liJspvQh.js.map → ProcessDetailPage-B30RatjD.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-C3OM5ZUy.js → ProcessesListPage-DrvjzgWT.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-C3OM5ZUy.js.map → ProcessesListPage-DrvjzgWT.js.map} +1 -1
- package/dashboard/dist/assets/ResolverForm-Bl8Pibyz.js +8 -0
- package/dashboard/dist/assets/ResolverForm-Bl8Pibyz.js.map +1 -0
- package/dashboard/dist/assets/{RoleDetailPage-CrlYCh-R.js → RoleDetailPage-ClYgn-3d.js} +3 -3
- package/dashboard/dist/assets/{RoleDetailPage-CrlYCh-R.js.map → RoleDetailPage-ClYgn-3d.js.map} +1 -1
- package/dashboard/dist/assets/{RolePill-Di2CAHuD.js → RolePill-Bw_n7EpR.js} +2 -2
- package/dashboard/dist/assets/{RolePill-Di2CAHuD.js.map → RolePill-Bw_n7EpR.js.map} +1 -1
- package/dashboard/dist/assets/{RolesPage-BaazL_pt.js → RolesPage-B_3qWsvQ.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-BaazL_pt.js.map → RolesPage-B_3qWsvQ.js.map} +1 -1
- package/dashboard/dist/assets/{RunAsSelector-DxuV0fa1.js → RunAsSelector-CbJT50Hp.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-DxuV0fa1.js.map → RunAsSelector-CbJT50Hp.js.map} +1 -1
- package/dashboard/dist/assets/{ScanCodesPage-C0mvmYTq.js → ScanCodesPage-CIIDuh8w.js} +2 -2
- package/dashboard/dist/assets/{ScanCodesPage-C0mvmYTq.js.map → ScanCodesPage-CIIDuh8w.js.map} +1 -1
- package/dashboard/dist/assets/{ScanSchemeDetailPage-CiJgt5dZ.js → ScanSchemeDetailPage-Bd3P3eyh.js} +2 -2
- package/dashboard/dist/assets/{ScanSchemeDetailPage-CiJgt5dZ.js.map → ScanSchemeDetailPage-Bd3P3eyh.js.map} +1 -1
- package/dashboard/dist/assets/ScanStationPage-BiO34nov.js +2 -0
- package/dashboard/dist/assets/ScanStationPage-BiO34nov.js.map +1 -0
- package/dashboard/dist/assets/{ScopeBadge-CSItPVOH.js → ScopeBadge-CKgfwTy3.js} +2 -2
- package/dashboard/dist/assets/{ScopeBadge-CSItPVOH.js.map → ScopeBadge-CKgfwTy3.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-CqbjVY6T.js → StickyPagination-BNOuYZ1m.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-CqbjVY6T.js.map → StickyPagination-BNOuYZ1m.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-wX3i0aqs.js → StreamMessageDetail-ALVUnfF1.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-wX3i0aqs.js.map → StreamMessageDetail-ALVUnfF1.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-C50PsFoV.js → SwimlaneTimeline-B5b8-xqI.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-C50PsFoV.js.map → SwimlaneTimeline-B5b8-xqI.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-CAgdLN_b.js → TagInput-B6fMAqxd.js} +2 -2
- package/dashboard/dist/assets/{TagInput-CAgdLN_b.js.map → TagInput-B6fMAqxd.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-bbNSv4Ae.js → TaskDetailPage-CoV3WcTA.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-bbNSv4Ae.js.map → TaskDetailPage-CoV3WcTA.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-h0oorVMe.js → TaskQueuePill-BnDp_DE6.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-h0oorVMe.js.map → TaskQueuePill-BnDp_DE6.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-CDWnm_d9.js → TasksListPage-CkMmOYOE.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-CDWnm_d9.js.map → TasksListPage-CkMmOYOE.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-BLjqlfoX.js → TimeAgo-DVkFgECX.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-BLjqlfoX.js.map → TimeAgo-DVkFgECX.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-CnW0ENb5.js → TimestampCell-CfTc6Txy.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-CnW0ENb5.js.map → TimestampCell-CfTc6Txy.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-CQCsVdzD.js → ToolPill-BRZWo4l8.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-CQCsVdzD.js.map → ToolPill-BRZWo4l8.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-DkMgCB-k.js → ToolTestPanel-kvqZqM0A.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-DkMgCB-k.js.map → ToolTestPanel-kvqZqM0A.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-g3pljKKX.js → TopicDetailPage-CjkAAyy8.js} +4 -4
- package/dashboard/dist/assets/{TopicDetailPage-g3pljKKX.js.map → TopicDetailPage-CjkAAyy8.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-CVcCYdy-.js → TopicsPage-CJZnv50y.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-CVcCYdy-.js.map → TopicsPage-CJZnv50y.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-D6zGIK2i.js → UserName-CPJPZnN1.js} +2 -2
- package/dashboard/dist/assets/{UserName-D6zGIK2i.js.map → UserName-CPJPZnN1.js.map} +1 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-46UV5pJd.js +2 -0
- package/dashboard/dist/assets/{WorkflowExecutionPage-B8g81FCJ.js.map → WorkflowExecutionPage-46UV5pJd.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill-Dg7BDTyM.js → WorkflowPill-Cly9Ln_3.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-Dg7BDTyM.js.map → WorkflowPill-Cly9Ln_3.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-nxT3bv6W.js → WorkflowsDashboard-DtxYdQbQ.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-nxT3bv6W.js.map → WorkflowsDashboard-DtxYdQbQ.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-grI-KAI2.js → WorkflowsOverview-Cg4LSfck.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-grI-KAI2.js.map → WorkflowsOverview-Cg4LSfck.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-BoZ-hfBE.js → YamlWorkflowDetailPage-Del-RCgi.js} +12 -12
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-BoZ-hfBE.js.map → YamlWorkflowDetailPage-Del-RCgi.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-Bf04rwRv.js → YamlWorkflowsPage-BF6Jtx24.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-Bf04rwRv.js.map → YamlWorkflowsPage-BF6Jtx24.js.map} +1 -1
- package/dashboard/dist/assets/{agents-Dphpdi8l.js → agents-wL-3dMOX.js} +2 -2
- package/dashboard/dist/assets/{agents-Dphpdi8l.js.map → agents-wL-3dMOX.js.map} +1 -1
- package/dashboard/dist/assets/{bots-DIFE298x.js → bots-D72EsztP.js} +2 -2
- package/dashboard/dist/assets/{bots-DIFE298x.js.map → bots-D72EsztP.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-D7Y7LClY.js → capabilities-DXFo6bRb.js} +2 -2
- package/dashboard/dist/assets/{capabilities-D7Y7LClY.js.map → capabilities-DXFo6bRb.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-D1TGcYsS.js → controlplane-gQdOgVtH.js} +2 -2
- package/dashboard/dist/assets/{controlplane-D1TGcYsS.js.map → controlplane-gQdOgVtH.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-CSE5wfWQ.js +2 -0
- package/dashboard/dist/assets/escalation-columns-CSE5wfWQ.js.map +1 -0
- package/dashboard/dist/assets/escalation-items-BIgBWbaw.js +2 -0
- package/dashboard/dist/assets/escalation-items-BIgBWbaw.js.map +1 -0
- package/dashboard/dist/assets/{index-CeCtqujG.js → index-BaCDL5fh.js} +38 -38
- package/dashboard/dist/assets/index-BaCDL5fh.js.map +1 -0
- package/dashboard/dist/assets/{index-Dl_8hbYI.js → index-Btm8FtTQ.js} +2 -2
- package/dashboard/dist/assets/{index-Dl_8hbYI.js.map → index-Btm8FtTQ.js.map} +1 -1
- package/dashboard/dist/assets/index-BvH8T-jb.js +2 -0
- package/dashboard/dist/assets/{index-CxdjT1Yk.js.map → index-BvH8T-jb.js.map} +1 -1
- package/dashboard/dist/assets/{index-HLu98Sqh.js → index-C-QQ5rKF.js} +2 -2
- package/dashboard/dist/assets/{index-HLu98Sqh.js.map → index-C-QQ5rKF.js.map} +1 -1
- package/dashboard/dist/assets/{index-DpKOr311.js → index-C1iuZ4Q5.js} +2 -2
- package/dashboard/dist/assets/{index-DpKOr311.js.map → index-C1iuZ4Q5.js.map} +1 -1
- package/dashboard/dist/assets/{index-ChV7pBjM.js → index-CDxOQ84B.js} +2 -2
- package/dashboard/dist/assets/{index-ChV7pBjM.js.map → index-CDxOQ84B.js.map} +1 -1
- package/dashboard/dist/assets/index-CED9cLE2.js +3 -0
- package/dashboard/dist/assets/index-CED9cLE2.js.map +1 -0
- package/dashboard/dist/assets/index-CnU7Jjz3.css +1 -0
- package/dashboard/dist/assets/{index-DAvi5_2T.js → index-CptTEhWp.js} +2 -2
- package/dashboard/dist/assets/{index-DAvi5_2T.js.map → index-CptTEhWp.js.map} +1 -1
- package/dashboard/dist/assets/{index-ag2pCAPu.js → index-Cu0xdX2t.js} +2 -2
- package/dashboard/dist/assets/{index-ag2pCAPu.js.map → index-Cu0xdX2t.js.map} +1 -1
- package/dashboard/dist/assets/index-D1lfq5iZ.js +13 -0
- package/dashboard/dist/assets/{index-gkxPN7Vx.js.map → index-D1lfq5iZ.js.map} +1 -1
- package/dashboard/dist/assets/{index-Dy1wWu2e.js → index-czgLyfSo.js} +2 -2
- package/dashboard/dist/assets/{index-Dy1wWu2e.js.map → index-czgLyfSo.js.map} +1 -1
- package/dashboard/dist/assets/{index-DN-kX7hL.js → index-h1Useskt.js} +2 -2
- package/dashboard/dist/assets/{index-DN-kX7hL.js.map → index-h1Useskt.js.map} +1 -1
- package/dashboard/dist/assets/{index-BzYcUPdD.js → index-ujGmmwl6.js} +2 -2
- package/dashboard/dist/assets/{index-BzYcUPdD.js.map → index-ujGmmwl6.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-DnMs-SOJ.js → knowledge-150lsM68.js} +2 -2
- package/dashboard/dist/assets/{knowledge-DnMs-SOJ.js.map → knowledge-150lsM68.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-CwW-K3r4.js → mcp-CRtY4v5W.js} +2 -2
- package/dashboard/dist/assets/{mcp-CwW-K3r4.js.map → mcp-CRtY4v5W.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-BZt5e8_Q.js → mcp-query-CDsSMaYq.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-BZt5e8_Q.js.map → mcp-query-CDsSMaYq.js.map} +1 -1
- package/dashboard/dist/assets/{personas-UA-Xn60f.js → personas-C2Wm4ng1.js} +2 -2
- package/dashboard/dist/assets/{personas-UA-Xn60f.js.map → personas-C2Wm4ng1.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-Cu3PARum.js → pipelines-BH0F9-jf.js} +2 -2
- package/dashboard/dist/assets/{pipelines-Cu3PARum.js.map → pipelines-BH0F9-jf.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-BO6h8GJE.js → tasks-BkUVpaOa.js} +2 -2
- package/dashboard/dist/assets/{tasks-BO6h8GJE.js.map → tasks-BkUVpaOa.js.map} +1 -1
- package/dashboard/dist/assets/{topics-CWjCR-17.js → topics-Crl8HKHq.js} +2 -2
- package/dashboard/dist/assets/{topics-CWjCR-17.js.map → topics-Crl8HKHq.js.map} +1 -1
- package/dashboard/dist/assets/{useDebouncedValue-6t0jx1RB.js → useDebouncedValue-Dk3r5xo-.js} +2 -2
- package/dashboard/dist/assets/{useDebouncedValue-6t0jx1RB.js.map → useDebouncedValue-Dk3r5xo-.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-CqpoQx6D.js → useNamespace-BXuyHKAs.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-CqpoQx6D.js.map → useNamespace-BXuyHKAs.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-hmCQQwzB.js → useYamlActivityEvents-DSlkjQfb.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-hmCQQwzB.js.map → useYamlActivityEvents-DSlkjQfb.js.map} +1 -1
- package/dashboard/dist/assets/{vendor-icons-CeVtUKyP.js → vendor-icons-C_DQEeBy.js} +2 -2
- package/dashboard/dist/assets/{vendor-icons-CeVtUKyP.js.map → vendor-icons-C_DQEeBy.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-ziUdZhKY.js → yaml-workflows-B230v5Za.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-ziUdZhKY.js.map → yaml-workflows-B230v5Za.js.map} +1 -1
- package/dashboard/dist/index.html +3 -3
- package/docs/faceted-routing.md +14 -1
- package/docs/hitl/x-lt-embed.md +4 -1
- package/docs/hitl/x-lt-list-schema.md +19 -2
- package/docs/hitl/x-lt-roster.md +112 -0
- package/docs/hitl/x-lt-show-if.md +15 -0
- package/docs/hitl/x-lt-widget.md +1 -0
- package/docs/hitl-guide.md +3 -1
- package/docs/scan-codes.md +4 -0
- package/docs/schema-enforcement.md +5 -0
- package/package.json +2 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-IS28XBRe.js +0 -2
- package/dashboard/dist/assets/CountdownTimer-LG03dVgR.js +0 -2
- package/dashboard/dist/assets/CountdownTimer-LG03dVgR.js.map +0 -1
- package/dashboard/dist/assets/EscalationTimeline-d3nvshOk.js +0 -2
- package/dashboard/dist/assets/McpRunDetailPage-bMXNO-6V.js +0 -2
- package/dashboard/dist/assets/OperatorDashboard-DctmZw10.js +0 -2
- package/dashboard/dist/assets/PortalPage-Dfc_Q02R.js +0 -2
- package/dashboard/dist/assets/ResolverForm-B1_x8Blv.js +0 -8
- package/dashboard/dist/assets/ResolverForm-B1_x8Blv.js.map +0 -1
- package/dashboard/dist/assets/ScanStationPage-DzGaXc40.js +0 -2
- package/dashboard/dist/assets/ScanStationPage-DzGaXc40.js.map +0 -1
- package/dashboard/dist/assets/WorkflowExecutionPage-B8g81FCJ.js +0 -2
- package/dashboard/dist/assets/escalation-columns-cwZE6xyo.js +0 -2
- package/dashboard/dist/assets/escalation-columns-cwZE6xyo.js.map +0 -1
- package/dashboard/dist/assets/index-CeCtqujG.js.map +0 -1
- package/dashboard/dist/assets/index-CniRa65p.js +0 -3
- package/dashboard/dist/assets/index-CniRa65p.js.map +0 -1
- package/dashboard/dist/assets/index-CxdjT1Yk.js +0 -2
- package/dashboard/dist/assets/index-gkxPN7Vx.js +0 -13
- package/dashboard/dist/assets/index-m9A3UYst.css +0 -1
- package/dashboard/dist/assets/validate-BIRn4a6d.js +0 -2
- package/dashboard/dist/assets/validate-BIRn4a6d.js.map +0 -1
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { type HelpTokenContext } from './x-lt-help';
|
|
2
|
+
export declare const X_LT_ROSTER = "x-lt-roster";
|
|
3
|
+
export declare const ROSTER_WIDGET = "roster";
|
|
4
|
+
/** The context domain the counts are read from: `roster.<field>.outstanding`. */
|
|
5
|
+
export declare const ROSTER_DOMAIN = "roster";
|
|
6
|
+
/** The most member rows a query set reads. */
|
|
7
|
+
export declare const ROSTER_QUERY_LIMIT = 200;
|
|
8
|
+
/** The `in` source naming the host row's held keys. */
|
|
9
|
+
export declare const ROSTER_ITEMS_SOURCE = "items";
|
|
10
|
+
/** `query.status` value that reads every status (the default). */
|
|
11
|
+
export declare const ROSTER_ANY_STATUS = "any";
|
|
12
|
+
export declare const ROSTER_STATES: {
|
|
13
|
+
readonly WAITING: "waiting";
|
|
14
|
+
readonly IN: "in";
|
|
15
|
+
readonly FLAGGED: "flagged";
|
|
16
|
+
};
|
|
17
|
+
export type RosterState = (typeof ROSTER_STATES)[keyof typeof ROSTER_STATES];
|
|
18
|
+
export interface RosterQuery {
|
|
19
|
+
role?: string;
|
|
20
|
+
roles?: string[];
|
|
21
|
+
facets?: Record<string, string>;
|
|
22
|
+
/** A status to narrow to; omitted or `any` reads every status. */
|
|
23
|
+
status?: string;
|
|
24
|
+
}
|
|
25
|
+
export type RosterExpected = {
|
|
26
|
+
query: RosterQuery;
|
|
27
|
+
} | {
|
|
28
|
+
list: string;
|
|
29
|
+
} | {
|
|
30
|
+
batch: true;
|
|
31
|
+
labels?: string;
|
|
32
|
+
};
|
|
33
|
+
export interface RosterFlagRule {
|
|
34
|
+
/** An x-lt-showIf condition over the member row's own context. */
|
|
35
|
+
when: string | string[];
|
|
36
|
+
label: string;
|
|
37
|
+
}
|
|
38
|
+
export interface RosterDef {
|
|
39
|
+
expected: RosterExpected;
|
|
40
|
+
key?: string;
|
|
41
|
+
in?: string[];
|
|
42
|
+
flagged?: RosterFlagRule[];
|
|
43
|
+
}
|
|
44
|
+
/** One member row's token context (the row's own domains). */
|
|
45
|
+
export type RosterRow = HelpTokenContext & {
|
|
46
|
+
escalation: Record<string, unknown>;
|
|
47
|
+
};
|
|
48
|
+
export interface RosterEntry {
|
|
49
|
+
key: string;
|
|
50
|
+
state: RosterState;
|
|
51
|
+
/** The matching flag rule's label, when flagged. */
|
|
52
|
+
flag?: string;
|
|
53
|
+
/** The member row, for a query set. */
|
|
54
|
+
row?: RosterRow;
|
|
55
|
+
/** The per-key label value from a batch's `labels` map. */
|
|
56
|
+
label?: unknown;
|
|
57
|
+
}
|
|
58
|
+
export interface RosterCounts {
|
|
59
|
+
expected: number;
|
|
60
|
+
in: number;
|
|
61
|
+
flagged: number;
|
|
62
|
+
waiting: number;
|
|
63
|
+
/** Entries neither in nor flagged: the count a roster guard waits on. */
|
|
64
|
+
outstanding: number;
|
|
65
|
+
}
|
|
66
|
+
export interface RosterResult {
|
|
67
|
+
entries: RosterEntry[];
|
|
68
|
+
counts: RosterCounts;
|
|
69
|
+
}
|
|
70
|
+
/** The field's roster declaration, or undefined when it declares none or an unusable one. */
|
|
71
|
+
export declare function readRosterDef(fieldSchema: Record<string, unknown> | null | undefined): RosterDef | undefined;
|
|
72
|
+
/** Every roster field of a form schema, by field name. */
|
|
73
|
+
export declare function readRosterDefs(schema: Record<string, unknown> | null | undefined): Record<string, RosterDef>;
|
|
74
|
+
/** The roster's member-row query, or null when its set is not a query. */
|
|
75
|
+
export declare function rosterQueryOf(def: RosterDef): RosterQuery | null;
|
|
76
|
+
/** The query's concrete params: facets interpolated, `any` status dropped. Null when the scope is empty. */
|
|
77
|
+
export declare function resolveRosterQuery(query: RosterQuery, ctx: HelpTokenContext): {
|
|
78
|
+
roles?: string[];
|
|
79
|
+
facets: Record<string, string>;
|
|
80
|
+
status?: string;
|
|
81
|
+
limit: number;
|
|
82
|
+
} | null;
|
|
83
|
+
/** A member row's token context, from a row whose JSON columns are strings or objects. */
|
|
84
|
+
export declare function rosterRowContext(row: Record<string, unknown>): RosterRow;
|
|
85
|
+
/**
|
|
86
|
+
* Every expected entry with its state, and the counts. `rows` are the
|
|
87
|
+
* member rows a query set read (ignored for list and batch sets). `ctx` is
|
|
88
|
+
* the host row's context with the live form values under `resolver`.
|
|
89
|
+
*/
|
|
90
|
+
export declare function computeRoster(def: RosterDef, ctx: HelpTokenContext, rows?: RosterRow[]): RosterResult;
|
|
91
|
+
/** The header line: "3 of 5 in · 1 Missing". */
|
|
92
|
+
export declare function rosterHeadline(result: RosterResult): string;
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ROSTER_STATES = exports.ROSTER_ANY_STATUS = exports.ROSTER_ITEMS_SOURCE = exports.ROSTER_QUERY_LIMIT = exports.ROSTER_DOMAIN = exports.ROSTER_WIDGET = exports.X_LT_ROSTER = void 0;
|
|
4
|
+
exports.readRosterDef = readRosterDef;
|
|
5
|
+
exports.readRosterDefs = readRosterDefs;
|
|
6
|
+
exports.rosterQueryOf = rosterQueryOf;
|
|
7
|
+
exports.resolveRosterQuery = resolveRosterQuery;
|
|
8
|
+
exports.rosterRowContext = rosterRowContext;
|
|
9
|
+
exports.computeRoster = computeRoster;
|
|
10
|
+
exports.rosterHeadline = rosterHeadline;
|
|
11
|
+
/**
|
|
12
|
+
* x-lt-roster: every member a container row expects, each with its state.
|
|
13
|
+
* A form field declares `x-lt-widget: "roster"` and an `x-lt-roster` block:
|
|
14
|
+
*
|
|
15
|
+
* "x-lt-roster": {
|
|
16
|
+
* "expected": { "query": { "roles": ["packing"], "facets": { "box": "{{escalation.id}}" } } },
|
|
17
|
+
* "key": "{{metadata.orderId}}",
|
|
18
|
+
* "in": ["items", "resolver.bags"],
|
|
19
|
+
* "flagged": [{ "when": "resolver.outcome=missing", "label": "Missing" }]
|
|
20
|
+
* }
|
|
21
|
+
*
|
|
22
|
+
* The expected set is one of:
|
|
23
|
+
* - `query`: member rows matched by role(s) and facets, any status unless
|
|
24
|
+
* `status` narrows it. `key` names each row's entry (default its id).
|
|
25
|
+
* - `list`: a `domain.path` naming an array of keys on the host row.
|
|
26
|
+
* - `batch`: the host row's batch keys; `labels` names a per-key map.
|
|
27
|
+
*
|
|
28
|
+
* An entry is `in` when its key is held by the host row (`items`: the
|
|
29
|
+
* accumulated keys, or a batch's filled keys) or appears in a named context
|
|
30
|
+
* value (`resolver.<field>`, a string or an array). It is `flagged` when its
|
|
31
|
+
* member row matches a `flagged` rule, and `waiting` otherwise. In wins over
|
|
32
|
+
* flagged, so a member flagged on its own row and then added reads in.
|
|
33
|
+
*
|
|
34
|
+
* Isomorphic: the dashboard renders the widget and the form's `roster.*`
|
|
35
|
+
* context from it, and the API layer computes the same counts to evaluate
|
|
36
|
+
* showIf and the roster submit guard at resolve.
|
|
37
|
+
*/
|
|
38
|
+
const x_lt_bind_1 = require("./x-lt-bind");
|
|
39
|
+
const ctx_path_1 = require("./ctx-path");
|
|
40
|
+
const x_lt_help_1 = require("./x-lt-help");
|
|
41
|
+
const x_lt_show_if_1 = require("./x-lt-show-if");
|
|
42
|
+
exports.X_LT_ROSTER = 'x-lt-roster';
|
|
43
|
+
exports.ROSTER_WIDGET = 'roster';
|
|
44
|
+
/** The context domain the counts are read from: `roster.<field>.outstanding`. */
|
|
45
|
+
exports.ROSTER_DOMAIN = 'roster';
|
|
46
|
+
/** The most member rows a query set reads. */
|
|
47
|
+
exports.ROSTER_QUERY_LIMIT = 200;
|
|
48
|
+
/** The `in` source naming the host row's held keys. */
|
|
49
|
+
exports.ROSTER_ITEMS_SOURCE = 'items';
|
|
50
|
+
/** `query.status` value that reads every status (the default). */
|
|
51
|
+
exports.ROSTER_ANY_STATUS = 'any';
|
|
52
|
+
exports.ROSTER_STATES = { WAITING: 'waiting', IN: 'in', FLAGGED: 'flagged' };
|
|
53
|
+
const DEFAULT_KEY = '{{escalation.id}}';
|
|
54
|
+
const MISSING = '—';
|
|
55
|
+
const BATCH_KEYS = 'batch_keys';
|
|
56
|
+
const BATCH_PENDING = 'batch_pending';
|
|
57
|
+
const ACCUMULATE_KEYS = 'accumulate_keys';
|
|
58
|
+
function isRecord(v) {
|
|
59
|
+
return !!v && typeof v === 'object' && !Array.isArray(v);
|
|
60
|
+
}
|
|
61
|
+
function strings(v) {
|
|
62
|
+
if (typeof v === 'string')
|
|
63
|
+
return v ? [v] : [];
|
|
64
|
+
if (!Array.isArray(v))
|
|
65
|
+
return [];
|
|
66
|
+
return v.filter((s) => typeof s === 'string' && s.length > 0);
|
|
67
|
+
}
|
|
68
|
+
/** The field's roster declaration, or undefined when it declares none or an unusable one. */
|
|
69
|
+
function readRosterDef(fieldSchema) {
|
|
70
|
+
const raw = fieldSchema?.[exports.X_LT_ROSTER];
|
|
71
|
+
if (!isRecord(raw) || !isRecord(raw.expected))
|
|
72
|
+
return undefined;
|
|
73
|
+
const e = raw.expected;
|
|
74
|
+
const hasSource = isRecord(e.query) || typeof e.list === 'string' || e.batch === true;
|
|
75
|
+
return hasSource ? raw : undefined;
|
|
76
|
+
}
|
|
77
|
+
/** Every roster field of a form schema, by field name. */
|
|
78
|
+
function readRosterDefs(schema) {
|
|
79
|
+
const properties = isRecord(schema?.properties) ? schema.properties : {};
|
|
80
|
+
const defs = {};
|
|
81
|
+
for (const [field, def] of Object.entries(properties)) {
|
|
82
|
+
const roster = isRecord(def) ? readRosterDef(def) : undefined;
|
|
83
|
+
if (roster)
|
|
84
|
+
defs[field] = roster;
|
|
85
|
+
}
|
|
86
|
+
return defs;
|
|
87
|
+
}
|
|
88
|
+
/** The roster's member-row query, or null when its set is not a query. */
|
|
89
|
+
function rosterQueryOf(def) {
|
|
90
|
+
return 'query' in def.expected && isRecord(def.expected.query) ? def.expected.query : null;
|
|
91
|
+
}
|
|
92
|
+
/** The query's concrete params: facets interpolated, `any` status dropped. Null when the scope is empty. */
|
|
93
|
+
function resolveRosterQuery(query, ctx) {
|
|
94
|
+
const facets = {};
|
|
95
|
+
for (const [k, v] of Object.entries(query.facets ?? {})) {
|
|
96
|
+
if (typeof v !== 'string')
|
|
97
|
+
continue;
|
|
98
|
+
const resolved = (0, x_lt_help_1.interpolateHelp)(v, ctx);
|
|
99
|
+
if (!resolved || resolved === MISSING)
|
|
100
|
+
return null;
|
|
101
|
+
facets[k] = resolved;
|
|
102
|
+
}
|
|
103
|
+
const roles = [...(query.role ? [query.role] : []), ...strings(query.roles)];
|
|
104
|
+
if (!roles.length && !Object.keys(facets).length)
|
|
105
|
+
return null;
|
|
106
|
+
const status = query.status && query.status !== exports.ROSTER_ANY_STATUS ? query.status : undefined;
|
|
107
|
+
return { ...(roles.length ? { roles } : {}), facets, ...(status ? { status } : {}), limit: exports.ROSTER_QUERY_LIMIT };
|
|
108
|
+
}
|
|
109
|
+
function parseJson(v) {
|
|
110
|
+
if (isRecord(v))
|
|
111
|
+
return v;
|
|
112
|
+
if (typeof v !== 'string' || !v)
|
|
113
|
+
return null;
|
|
114
|
+
try {
|
|
115
|
+
const parsed = JSON.parse(v);
|
|
116
|
+
return isRecord(parsed) ? parsed : null;
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** A member row's token context, from a row whose JSON columns are strings or objects. */
|
|
123
|
+
function rosterRowContext(row) {
|
|
124
|
+
return {
|
|
125
|
+
escalation: row,
|
|
126
|
+
metadata: isRecord(row.metadata) ? row.metadata : parseJson(row.metadata),
|
|
127
|
+
envelope: parseJson(row.envelope),
|
|
128
|
+
payload: parseJson(row.escalation_payload),
|
|
129
|
+
resolver: parseJson(row.resolver_payload),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
/** The keys the host row holds: its accumulated keys, or a batch's filled keys. */
|
|
133
|
+
function heldKeys(ctx) {
|
|
134
|
+
const metadata = ctx.metadata ?? {};
|
|
135
|
+
const accumulated = metadata[ACCUMULATE_KEYS];
|
|
136
|
+
if (Array.isArray(accumulated))
|
|
137
|
+
return new Set(strings(accumulated));
|
|
138
|
+
const envelopeItems = (0, x_lt_bind_1.getDeep)(ctx.envelope, 'accumulate_items');
|
|
139
|
+
if (isRecord(envelopeItems))
|
|
140
|
+
return new Set(Object.keys(envelopeItems));
|
|
141
|
+
if (Array.isArray(metadata[BATCH_PENDING])) {
|
|
142
|
+
const pending = new Set(strings(metadata[BATCH_PENDING]));
|
|
143
|
+
return new Set(strings(metadata[BATCH_KEYS]).filter((k) => !pending.has(k)));
|
|
144
|
+
}
|
|
145
|
+
return new Set();
|
|
146
|
+
}
|
|
147
|
+
function arrivedKeys(def, ctx) {
|
|
148
|
+
const arrived = new Set();
|
|
149
|
+
for (const source of def.in ?? [exports.ROSTER_ITEMS_SOURCE]) {
|
|
150
|
+
const keys = source === exports.ROSTER_ITEMS_SOURCE
|
|
151
|
+
? [...heldKeys(ctx)]
|
|
152
|
+
: strings((0, ctx_path_1.resolveCtxPath)(source, ctx));
|
|
153
|
+
keys.forEach((k) => arrived.add(k));
|
|
154
|
+
}
|
|
155
|
+
return arrived;
|
|
156
|
+
}
|
|
157
|
+
function flagFor(def, row) {
|
|
158
|
+
if (!row)
|
|
159
|
+
return undefined;
|
|
160
|
+
for (const rule of def.flagged ?? []) {
|
|
161
|
+
const conditions = strings(rule.when);
|
|
162
|
+
if (!conditions.length || typeof rule.label !== 'string')
|
|
163
|
+
continue;
|
|
164
|
+
if ((0, x_lt_show_if_1.evaluateShowIf)(conditions, row))
|
|
165
|
+
return rule.label;
|
|
166
|
+
}
|
|
167
|
+
return undefined;
|
|
168
|
+
}
|
|
169
|
+
/** The expected keys in order, each with its member row (query) or label (batch). */
|
|
170
|
+
function expectedEntries(def, ctx, rows) {
|
|
171
|
+
const seen = new Set();
|
|
172
|
+
const out = [];
|
|
173
|
+
const push = (key, extra = {}) => {
|
|
174
|
+
if (!key || key === MISSING || seen.has(key))
|
|
175
|
+
return;
|
|
176
|
+
seen.add(key);
|
|
177
|
+
out.push({ key, ...extra });
|
|
178
|
+
};
|
|
179
|
+
const e = def.expected;
|
|
180
|
+
if ('query' in e) {
|
|
181
|
+
const template = def.key ?? DEFAULT_KEY;
|
|
182
|
+
for (const row of rows)
|
|
183
|
+
push((0, x_lt_help_1.interpolateHelp)(template, row), { row });
|
|
184
|
+
}
|
|
185
|
+
else if ('list' in e) {
|
|
186
|
+
strings((0, ctx_path_1.resolveCtxPath)(e.list, ctx)).forEach((k) => push(k));
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
const labels = e.labels ? (0, ctx_path_1.resolveCtxPath)(e.labels, ctx) : undefined;
|
|
190
|
+
for (const key of strings(ctx.metadata?.[BATCH_KEYS])) {
|
|
191
|
+
push(key, isRecord(labels) && key in labels ? { label: labels[key] } : {});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
return out;
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Every expected entry with its state, and the counts. `rows` are the
|
|
198
|
+
* member rows a query set read (ignored for list and batch sets). `ctx` is
|
|
199
|
+
* the host row's context with the live form values under `resolver`.
|
|
200
|
+
*/
|
|
201
|
+
function computeRoster(def, ctx, rows = []) {
|
|
202
|
+
const arrived = arrivedKeys(def, ctx);
|
|
203
|
+
const entries = expectedEntries(def, ctx, rows).map((e) => {
|
|
204
|
+
if (arrived.has(e.key))
|
|
205
|
+
return { ...e, state: exports.ROSTER_STATES.IN };
|
|
206
|
+
const flag = flagFor(def, e.row);
|
|
207
|
+
return flag ? { ...e, state: exports.ROSTER_STATES.FLAGGED, flag } : { ...e, state: exports.ROSTER_STATES.WAITING };
|
|
208
|
+
});
|
|
209
|
+
const count = (s) => entries.filter((e) => e.state === s).length;
|
|
210
|
+
const waiting = count(exports.ROSTER_STATES.WAITING);
|
|
211
|
+
return {
|
|
212
|
+
entries,
|
|
213
|
+
counts: {
|
|
214
|
+
expected: entries.length,
|
|
215
|
+
in: count(exports.ROSTER_STATES.IN),
|
|
216
|
+
flagged: count(exports.ROSTER_STATES.FLAGGED),
|
|
217
|
+
waiting,
|
|
218
|
+
outstanding: waiting,
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
/** The header line: "3 of 5 in · 1 Missing". */
|
|
223
|
+
function rosterHeadline(result) {
|
|
224
|
+
const { counts } = result;
|
|
225
|
+
const parts = [`${counts.in} of ${counts.expected} in`];
|
|
226
|
+
const byFlag = new Map();
|
|
227
|
+
for (const e of result.entries) {
|
|
228
|
+
if (e.state === exports.ROSTER_STATES.FLAGGED && e.flag)
|
|
229
|
+
byFlag.set(e.flag, (byFlag.get(e.flag) ?? 0) + 1);
|
|
230
|
+
}
|
|
231
|
+
for (const [flag, n] of byFlag)
|
|
232
|
+
parts.push(`${n} ${flag}`);
|
|
233
|
+
return parts.join(' · ');
|
|
234
|
+
}
|
|
@@ -11,3 +11,11 @@ export declare function evaluateShowIf(condition: unknown, ctx: ShowIfContext |
|
|
|
11
11
|
* (null, undefined, '', false, or 0). Render and validation share this test.
|
|
12
12
|
*/
|
|
13
13
|
export declare function isHiddenIfEmpty(fieldSchema: Record<string, unknown> | undefined, value: unknown): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* `x-lt-hidden: true`: the field never renders. A value set by the caller
|
|
16
|
+
* (a workflow default, an API submitter) passes through to the payload and
|
|
17
|
+
* is checked against the field's type and enum; the field is never required,
|
|
18
|
+
* since no one on the form can answer it.
|
|
19
|
+
*/
|
|
20
|
+
export declare const X_LT_HIDDEN = "x-lt-hidden";
|
|
21
|
+
export declare function isHiddenField(fieldSchema: Record<string, unknown> | undefined): boolean;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.X_LT_HIDDEN = void 0;
|
|
3
4
|
exports.evaluateShowIf = evaluateShowIf;
|
|
4
5
|
exports.isHiddenIfEmpty = isHiddenIfEmpty;
|
|
6
|
+
exports.isHiddenField = isHiddenField;
|
|
5
7
|
/**
|
|
6
8
|
* x-lt-showIf — conditional field visibility. A form_schema property may
|
|
7
9
|
* declare `x-lt-showIf: "domain.path"` to show the field only when the value
|
|
@@ -21,6 +23,7 @@ exports.isHiddenIfEmpty = isHiddenIfEmpty;
|
|
|
21
23
|
* envelope — the workflow-sent input envelope
|
|
22
24
|
* escalation — top-level escalation row fields
|
|
23
25
|
* resolver — the submitted resolver payload
|
|
26
|
+
* roster (each roster field's counts: `roster.orders.outstanding=0`)
|
|
24
27
|
*
|
|
25
28
|
* A missing or non-string condition always shows the field (opt-in, safe default).
|
|
26
29
|
*/
|
|
@@ -95,3 +98,13 @@ function isHiddenIfEmpty(fieldSchema, value) {
|
|
|
95
98
|
return false;
|
|
96
99
|
return value === null || value === undefined || value === '' || value === false || value === 0;
|
|
97
100
|
}
|
|
101
|
+
/**
|
|
102
|
+
* `x-lt-hidden: true`: the field never renders. A value set by the caller
|
|
103
|
+
* (a workflow default, an API submitter) passes through to the payload and
|
|
104
|
+
* is checked against the field's type and enum; the field is never required,
|
|
105
|
+
* since no one on the form can answer it.
|
|
106
|
+
*/
|
|
107
|
+
exports.X_LT_HIDDEN = 'x-lt-hidden';
|
|
108
|
+
function isHiddenField(fieldSchema) {
|
|
109
|
+
return fieldSchema?.[exports.X_LT_HIDDEN] === true;
|
|
110
|
+
}
|
|
@@ -23,11 +23,23 @@
|
|
|
23
23
|
* `autoResolveWhenEmpty` closes the loop: the moment the query is confirmed
|
|
24
24
|
* empty, the claimed parent auto-submits — checked on page-load and after each
|
|
25
25
|
* inline child-resolve — so a set of escalations completes with no extra click.
|
|
26
|
+
*
|
|
27
|
+
* A guard may name a roster field instead of a query: the resolve waits while
|
|
28
|
+
* that roster has outstanding entries (x-lt-roster.ts), so the list the person
|
|
29
|
+
* works from and the rule that holds the submit are one computation.
|
|
30
|
+
*
|
|
31
|
+
* "x-lt-submit-guard": { "roster": "orders",
|
|
32
|
+
* "message": "{{count}} orders are still unaccounted for" }
|
|
33
|
+
*
|
|
34
|
+
* `{{count}}` is the outstanding count. A roster guard never auto-submits.
|
|
26
35
|
*/
|
|
27
36
|
import type { EmbedQuery } from './x-lt-query';
|
|
28
37
|
export declare const X_LT_SUBMIT_GUARD = "x-lt-submit-guard";
|
|
29
38
|
export interface SubmitGuardDef {
|
|
30
|
-
query
|
|
39
|
+
/** The embedded query that must be empty; absent for a roster guard. */
|
|
40
|
+
query?: EmbedQuery;
|
|
41
|
+
/** A roster field whose outstanding entries hold the resolve. */
|
|
42
|
+
roster?: string;
|
|
31
43
|
/** The gate condition; defaults to true (the only supported mode today). */
|
|
32
44
|
mustBeEmpty?: boolean;
|
|
33
45
|
/** Shown beside the disabled submit. `{{count}}` and `{{domain.path}}` tokens interpolate. */
|
|
@@ -36,6 +48,7 @@ export interface SubmitGuardDef {
|
|
|
36
48
|
autoResolveWhenEmpty?: boolean;
|
|
37
49
|
}
|
|
38
50
|
export declare const SUBMIT_GUARD_DEFAULT_MESSAGE = "{{count}} related items are still pending";
|
|
51
|
+
export declare const ROSTER_GUARD_DEFAULT_MESSAGE = "{{count}} expected items are still outstanding";
|
|
39
52
|
/** The schema's submit-guard, or undefined when it declares none. */
|
|
40
53
|
export declare function readSubmitGuard(schema: Record<string, unknown> | null | undefined): SubmitGuardDef | undefined;
|
|
41
54
|
/**
|
|
@@ -1,16 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.SUBMIT_GUARD_DEFAULT_MESSAGE = exports.X_LT_SUBMIT_GUARD = void 0;
|
|
3
|
+
exports.ROSTER_GUARD_DEFAULT_MESSAGE = exports.SUBMIT_GUARD_DEFAULT_MESSAGE = exports.X_LT_SUBMIT_GUARD = void 0;
|
|
4
4
|
exports.readSubmitGuard = readSubmitGuard;
|
|
5
5
|
exports.guardBlocks = guardBlocks;
|
|
6
6
|
exports.X_LT_SUBMIT_GUARD = 'x-lt-submit-guard';
|
|
7
7
|
exports.SUBMIT_GUARD_DEFAULT_MESSAGE = '{{count}} related items are still pending';
|
|
8
|
+
exports.ROSTER_GUARD_DEFAULT_MESSAGE = '{{count}} expected items are still outstanding';
|
|
8
9
|
/** The schema's submit-guard, or undefined when it declares none. */
|
|
9
10
|
function readSubmitGuard(schema) {
|
|
10
11
|
const raw = schema?.[exports.X_LT_SUBMIT_GUARD];
|
|
11
12
|
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
12
13
|
return undefined;
|
|
13
14
|
const guard = raw;
|
|
15
|
+
if (typeof guard.roster === 'string' && guard.roster)
|
|
16
|
+
return guard;
|
|
14
17
|
if (!guard.query || typeof guard.query !== 'object')
|
|
15
18
|
return undefined;
|
|
16
19
|
return guard;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { RELATED_AGGREGATES, type RelatedAggregate, type RelatedColumnDecl } from './types';
|
|
2
|
+
export type RelatedExtreme = typeof RELATED_AGGREGATES.MIN | typeof RELATED_AGGREGATES.MAX;
|
|
3
|
+
/** True for the aggregates that pick one value rather than count. */
|
|
4
|
+
export declare function isExtremeAggregate(aggregate: unknown): aggregate is RelatedExtreme;
|
|
5
|
+
/**
|
|
6
|
+
* The stored path a min/max `value` template reads: `payload.<path>` for an
|
|
7
|
+
* items set, `metadata.<key>` for a query set. Null when the template does
|
|
8
|
+
* not fit the set's source.
|
|
9
|
+
*/
|
|
10
|
+
export declare function relatedValuePath(source: string, value: unknown): string | null;
|
|
11
|
+
/** The key a column's per-host aggregates are stored under in its set's result. */
|
|
12
|
+
export declare function relatedAggregateKey(col: Pick<RelatedColumnDecl, 'aggregate' | 'splitBy' | 'value'>, source: string): string;
|
|
13
|
+
/** A count column's display text, or null when the column declares none for this count. */
|
|
14
|
+
export declare function relatedCountText(display: Record<string, string> | undefined, count: number): string | null;
|
|
15
|
+
/**
|
|
16
|
+
* The least or greatest of several values: numbers when every value is a
|
|
17
|
+
* number, otherwise text in code-unit order (ISO instants order as text).
|
|
18
|
+
*/
|
|
19
|
+
export declare function foldExtreme(fold: RelatedAggregate, values: Array<string | number>): string | number | undefined;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.isExtremeAggregate = isExtremeAggregate;
|
|
4
|
+
exports.relatedValuePath = relatedValuePath;
|
|
5
|
+
exports.relatedAggregateKey = relatedAggregateKey;
|
|
6
|
+
exports.relatedCountText = relatedCountText;
|
|
7
|
+
exports.foldExtreme = foldExtreme;
|
|
8
|
+
const types_1 = require("./types");
|
|
9
|
+
const VALUE_TOKEN = /^\{\{\s*([^{}]+?)\s*\}\}$/;
|
|
10
|
+
const ITEM_PREFIX = 'item.';
|
|
11
|
+
const PATH_SEGMENT = /^[a-zA-Z0-9_]+$/;
|
|
12
|
+
const METADATA_KEY = /^[a-zA-Z0-9_]+$/;
|
|
13
|
+
const COUNT_TOKEN = /\{n\}/g;
|
|
14
|
+
/** True for the aggregates that pick one value rather than count. */
|
|
15
|
+
function isExtremeAggregate(aggregate) {
|
|
16
|
+
return aggregate === types_1.RELATED_AGGREGATES.MIN || aggregate === types_1.RELATED_AGGREGATES.MAX;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The stored path a min/max `value` template reads: `payload.<path>` for an
|
|
20
|
+
* items set, `metadata.<key>` for a query set. Null when the template does
|
|
21
|
+
* not fit the set's source.
|
|
22
|
+
*/
|
|
23
|
+
function relatedValuePath(source, value) {
|
|
24
|
+
if (typeof value !== 'string')
|
|
25
|
+
return null;
|
|
26
|
+
const token = value.match(VALUE_TOKEN)?.[1];
|
|
27
|
+
if (!token)
|
|
28
|
+
return null;
|
|
29
|
+
if (source === types_1.RELATED_SOURCES.ITEMS) {
|
|
30
|
+
if (!token.startsWith(`${ITEM_PREFIX}${types_1.SPLIT_PATHS.PAYLOAD}`))
|
|
31
|
+
return null;
|
|
32
|
+
const path = token.slice(ITEM_PREFIX.length + types_1.SPLIT_PATHS.PAYLOAD.length);
|
|
33
|
+
return path && path.split('.').every((s) => PATH_SEGMENT.test(s)) ? `${types_1.SPLIT_PATHS.PAYLOAD}${path}` : null;
|
|
34
|
+
}
|
|
35
|
+
if (!token.startsWith(types_1.SPLIT_PATHS.METADATA))
|
|
36
|
+
return null;
|
|
37
|
+
const key = token.slice(types_1.SPLIT_PATHS.METADATA.length);
|
|
38
|
+
return METADATA_KEY.test(key) ? token : null;
|
|
39
|
+
}
|
|
40
|
+
/** The key a column's per-host aggregates are stored under in its set's result. */
|
|
41
|
+
function relatedAggregateKey(col, source) {
|
|
42
|
+
if (isExtremeAggregate(col.aggregate))
|
|
43
|
+
return `${col.aggregate}:${relatedValuePath(source, col.value) ?? ''}`;
|
|
44
|
+
return col.splitBy ?? '';
|
|
45
|
+
}
|
|
46
|
+
/** A count column's display text, or null when the column declares none for this count. */
|
|
47
|
+
function relatedCountText(display, count) {
|
|
48
|
+
if (!display)
|
|
49
|
+
return null;
|
|
50
|
+
const text = display[String(count)] ?? display[types_1.RELATED_DISPLAY_DEFAULT];
|
|
51
|
+
return text === undefined ? null : text.replace(COUNT_TOKEN, String(count));
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* The least or greatest of several values: numbers when every value is a
|
|
55
|
+
* number, otherwise text in code-unit order (ISO instants order as text).
|
|
56
|
+
*/
|
|
57
|
+
function foldExtreme(fold, values) {
|
|
58
|
+
if (!values.length)
|
|
59
|
+
return undefined;
|
|
60
|
+
const pickMin = fold === types_1.RELATED_AGGREGATES.MIN;
|
|
61
|
+
if (values.every((v) => typeof v === 'number')) {
|
|
62
|
+
return pickMin ? Math.min(...values) : Math.max(...values);
|
|
63
|
+
}
|
|
64
|
+
const texts = values.map(String).sort((a, b) => (a < b ? -1 : a > b ? 1 : 0));
|
|
65
|
+
return pickMin ? texts[0] : texts[texts.length - 1];
|
|
66
|
+
}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
export { METADATA_KEY, readListSchemaRelations, listSchemaRelationsError, splitPathError } from './validate';
|
|
3
3
|
export { rowTemplateContext, resolveTemplateValue, resolveRelatedFacets } from './template';
|
|
4
|
+
export type { RelatedExtreme } from './aggregate';
|
|
5
|
+
export { isExtremeAggregate, relatedValuePath, relatedAggregateKey, relatedCountText, foldExtreme } from './aggregate';
|
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.resolveRelatedFacets = exports.resolveTemplateValue = exports.rowTemplateContext = exports.splitPathError = exports.listSchemaRelationsError = exports.readListSchemaRelations = exports.METADATA_KEY = void 0;
|
|
17
|
+
exports.foldExtreme = exports.relatedCountText = exports.relatedAggregateKey = exports.relatedValuePath = exports.isExtremeAggregate = exports.resolveRelatedFacets = exports.resolveTemplateValue = exports.rowTemplateContext = exports.splitPathError = exports.listSchemaRelationsError = exports.readListSchemaRelations = exports.METADATA_KEY = void 0;
|
|
18
18
|
__exportStar(require("./types"), exports);
|
|
19
19
|
var validate_1 = require("./validate");
|
|
20
20
|
Object.defineProperty(exports, "METADATA_KEY", { enumerable: true, get: function () { return validate_1.METADATA_KEY; } });
|
|
@@ -25,3 +25,9 @@ var template_1 = require("./template");
|
|
|
25
25
|
Object.defineProperty(exports, "rowTemplateContext", { enumerable: true, get: function () { return template_1.rowTemplateContext; } });
|
|
26
26
|
Object.defineProperty(exports, "resolveTemplateValue", { enumerable: true, get: function () { return template_1.resolveTemplateValue; } });
|
|
27
27
|
Object.defineProperty(exports, "resolveRelatedFacets", { enumerable: true, get: function () { return template_1.resolveRelatedFacets; } });
|
|
28
|
+
var aggregate_1 = require("./aggregate");
|
|
29
|
+
Object.defineProperty(exports, "isExtremeAggregate", { enumerable: true, get: function () { return aggregate_1.isExtremeAggregate; } });
|
|
30
|
+
Object.defineProperty(exports, "relatedValuePath", { enumerable: true, get: function () { return aggregate_1.relatedValuePath; } });
|
|
31
|
+
Object.defineProperty(exports, "relatedAggregateKey", { enumerable: true, get: function () { return aggregate_1.relatedAggregateKey; } });
|
|
32
|
+
Object.defineProperty(exports, "relatedCountText", { enumerable: true, get: function () { return aggregate_1.relatedCountText; } });
|
|
33
|
+
Object.defineProperty(exports, "foldExtreme", { enumerable: true, get: function () { return aggregate_1.foldExtreme; } });
|
|
@@ -23,6 +23,9 @@ export declare const RELATED_SOURCES: {
|
|
|
23
23
|
export type RelatedSource = (typeof RELATED_SOURCES)[keyof typeof RELATED_SOURCES];
|
|
24
24
|
export declare const RELATED_AGGREGATES: {
|
|
25
25
|
readonly COUNT: "count";
|
|
26
|
+
/** The least item or row value: numbers as numbers, everything else as text. */
|
|
27
|
+
readonly MIN: "min";
|
|
28
|
+
readonly MAX: "max";
|
|
26
29
|
};
|
|
27
30
|
export type RelatedAggregate = (typeof RELATED_AGGREGATES)[keyof typeof RELATED_AGGREGATES];
|
|
28
31
|
/** How a grouped table folds a plain column across a group's rows. */
|
|
@@ -73,10 +76,18 @@ export interface RelatedSetDecl {
|
|
|
73
76
|
source: RelatedSource;
|
|
74
77
|
query?: RelatedQueryDecl;
|
|
75
78
|
}
|
|
79
|
+
/** A count column's text keyed by count; `default` covers the rest, `{n}` reads the count. */
|
|
80
|
+
export declare const RELATED_DISPLAY_DEFAULT = "default";
|
|
76
81
|
export interface RelatedColumnDecl {
|
|
77
82
|
label: string;
|
|
78
83
|
related: string;
|
|
79
84
|
aggregate?: RelatedAggregate;
|
|
85
|
+
/** min/max: `{{item.payload.<path>}}` on an items set, `{{metadata.<key>}}` on a query set. */
|
|
86
|
+
value?: string;
|
|
87
|
+
/** min/max: how the value renders, as on any facet-table column. */
|
|
88
|
+
format?: string;
|
|
89
|
+
/** count: text by count, e.g. `{ "0": "Ready", "default": "Waiting on {n}" }`. */
|
|
90
|
+
display?: Record<string, string>;
|
|
80
91
|
splitBy?: string;
|
|
81
92
|
labels?: Record<string, string>;
|
|
82
93
|
priority?: 1 | 2 | 3;
|
|
@@ -103,9 +114,11 @@ export interface ListSchemaRelations {
|
|
|
103
114
|
/** The metadata key a facet-table groups by, when declared. */
|
|
104
115
|
groupKey: string | null;
|
|
105
116
|
}
|
|
106
|
-
/** Per-host aggregate for one (set, splitBy) pair. */
|
|
117
|
+
/** Per-host aggregate for one (set, splitBy) pair, or one (set, min/max, value) triple. */
|
|
107
118
|
export interface RelatedHostAggregate {
|
|
108
119
|
count: number;
|
|
120
|
+
/** min/max: the extreme value among `count` values; absent when none carries one. */
|
|
121
|
+
value?: string | number;
|
|
109
122
|
/** Split value to count; a null value is keyed by the empty string. */
|
|
110
123
|
splits?: Record<string, number>;
|
|
111
124
|
/** Items whose reciprocal row the viewer cannot read. */
|
|
@@ -125,7 +138,7 @@ export interface RelatedSetResult {
|
|
|
125
138
|
roleLabels?: Record<string, string>;
|
|
126
139
|
/** Query sets: declared roles outside the viewer's full read scope. */
|
|
127
140
|
restricted?: string[];
|
|
128
|
-
/** Keyed by splitBy
|
|
141
|
+
/** Keyed by relatedAggregateKey (the splitBy, '' when unsplit, or `min:<path>`), then host id. */
|
|
129
142
|
aggregates: Record<string, Record<string, RelatedHostAggregate>>;
|
|
130
143
|
}
|
|
131
144
|
export interface RelatedAggregatesResponse {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* values are `{{domain.path}}` templates over the host row
|
|
12
12
|
*/
|
|
13
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
-
exports.FACET_TABLE_LAYOUT = exports.RELATED_LIMITS = exports.SPLIT_PATHS = exports.GROUP_FOLDS = exports.RELATED_AGGREGATES = exports.RELATED_SOURCES = exports.LIST_SCHEMA_KEYS = void 0;
|
|
14
|
+
exports.RELATED_DISPLAY_DEFAULT = exports.FACET_TABLE_LAYOUT = exports.RELATED_LIMITS = exports.SPLIT_PATHS = exports.GROUP_FOLDS = exports.RELATED_AGGREGATES = exports.RELATED_SOURCES = exports.LIST_SCHEMA_KEYS = void 0;
|
|
15
15
|
exports.LIST_SCHEMA_KEYS = {
|
|
16
16
|
RELATED: 'x-lt-related',
|
|
17
17
|
COLUMNS: 'x-lt-columns',
|
|
@@ -25,6 +25,9 @@ exports.RELATED_SOURCES = {
|
|
|
25
25
|
};
|
|
26
26
|
exports.RELATED_AGGREGATES = {
|
|
27
27
|
COUNT: 'count',
|
|
28
|
+
/** The least item or row value: numbers as numbers, everything else as text. */
|
|
29
|
+
MIN: 'min',
|
|
30
|
+
MAX: 'max',
|
|
28
31
|
};
|
|
29
32
|
/** How a grouped table folds a plain column across a group's rows. */
|
|
30
33
|
exports.GROUP_FOLDS = {
|
|
@@ -53,3 +56,5 @@ exports.RELATED_LIMITS = {
|
|
|
53
56
|
};
|
|
54
57
|
/** The table layout that renders related columns, row details, and groups. */
|
|
55
58
|
exports.FACET_TABLE_LAYOUT = 'facet-table';
|
|
59
|
+
/** A count column's text keyed by count; `default` covers the rest, `{n}` reads the count. */
|
|
60
|
+
exports.RELATED_DISPLAY_DEFAULT = 'default';
|
|
@@ -5,10 +5,12 @@ exports.splitPathError = splitPathError;
|
|
|
5
5
|
exports.readListSchemaRelations = readListSchemaRelations;
|
|
6
6
|
exports.listSchemaRelationsError = listSchemaRelationsError;
|
|
7
7
|
const types_1 = require("./types");
|
|
8
|
+
const aggregate_1 = require("./aggregate");
|
|
8
9
|
/** A metadata key: interpolated as a JSON path, so strictly validated. */
|
|
9
10
|
exports.METADATA_KEY = /^[a-zA-Z0-9_]+$/;
|
|
10
11
|
const SET_NAME = /^[a-zA-Z][a-zA-Z0-9_-]{0,47}$/;
|
|
11
12
|
const PATH_SEGMENT = /^[a-zA-Z0-9_]+$/;
|
|
13
|
+
const COUNT_KEY = /^(0|[1-9][0-9]*)$/;
|
|
12
14
|
class ListSchemaError extends Error {
|
|
13
15
|
}
|
|
14
16
|
function fail(message) {
|
|
@@ -106,8 +108,32 @@ function readRelatedColumn(raw, sets, at) {
|
|
|
106
108
|
const name = raw.related;
|
|
107
109
|
if (!isNonEmptyString(name) || !sets[name])
|
|
108
110
|
fail(`${at}.related must name a set declared in x-lt-related`);
|
|
109
|
-
if (raw.aggregate !== undefined && raw.aggregate !== types_1.RELATED_AGGREGATES.COUNT)
|
|
110
|
-
fail(`${at}.aggregate must be "count"`);
|
|
111
|
+
if (raw.aggregate !== undefined && raw.aggregate !== types_1.RELATED_AGGREGATES.COUNT && !(0, aggregate_1.isExtremeAggregate)(raw.aggregate)) {
|
|
112
|
+
fail(`${at}.aggregate must be "count", "min" or "max"`);
|
|
113
|
+
}
|
|
114
|
+
const source = sets[name].source;
|
|
115
|
+
if ((0, aggregate_1.isExtremeAggregate)(raw.aggregate)) {
|
|
116
|
+
if (!(0, aggregate_1.relatedValuePath)(source, raw.value)) {
|
|
117
|
+
fail(source === types_1.RELATED_SOURCES.ITEMS
|
|
118
|
+
? `${at}.value must be "{{item.payload.<path>}}" on an items set`
|
|
119
|
+
: `${at}.value must be "{{metadata.<key>}}" on a query set`);
|
|
120
|
+
}
|
|
121
|
+
if (raw.splitBy !== undefined)
|
|
122
|
+
fail(`${at}: a ${raw.aggregate} column takes no splitBy`);
|
|
123
|
+
if (raw.display !== undefined)
|
|
124
|
+
fail(`${at}: display applies to a count column`);
|
|
125
|
+
}
|
|
126
|
+
else if (raw.value !== undefined) {
|
|
127
|
+
fail(`${at}.value applies to a min or max column`);
|
|
128
|
+
}
|
|
129
|
+
if (raw.format !== undefined && typeof raw.format !== 'string')
|
|
130
|
+
fail(`${at}.format must be a format name`);
|
|
131
|
+
if (raw.display !== undefined) {
|
|
132
|
+
if (!isObj(raw.display) || Object.keys(raw.display).length === 0
|
|
133
|
+
|| !Object.entries(raw.display).every(([k, v]) => (k === types_1.RELATED_DISPLAY_DEFAULT || COUNT_KEY.test(k)) && typeof v === 'string')) {
|
|
134
|
+
fail(`${at}.display must map counts (or "default") to display text`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
111
137
|
if (raw.splitBy !== undefined) {
|
|
112
138
|
if (!isNonEmptyString(raw.splitBy))
|
|
113
139
|
fail(`${at}.splitBy must be a path`);
|