@hotmeshio/long-tail 0.25.1 → 0.25.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/build/api/escalations/batch-flag.d.ts +19 -0
- package/build/api/escalations/batch-flag.js +138 -0
- package/build/api/escalations/index.d.ts +2 -0
- package/build/api/escalations/index.js +8 -1
- package/build/api/escalations/roster-action.d.ts +37 -0
- package/build/api/escalations/roster-action.js +179 -0
- package/build/api/scan-codes/choice-roles.d.ts +3 -0
- package/build/api/scan-codes/choice-roles.js +48 -0
- package/build/api/scan-codes/choice.js +8 -0
- package/build/api/scan-codes/fill-labels.d.ts +15 -0
- package/build/api/scan-codes/fill-labels.js +99 -0
- package/build/api/scan-codes/hold.js +1 -0
- package/build/api/scan-codes/present.js +7 -2
- package/build/api/scan-codes/verb-fill.js +22 -5
- package/build/routes/escalations/resolve.js +45 -0
- package/build/sdk/index.d.ts +11 -0
- package/build/sdk/index.js +3 -0
- package/build/services/escalation/batch-flag.d.ts +25 -0
- package/build/services/escalation/batch-flag.js +51 -0
- package/build/services/escalation/index.d.ts +1 -0
- package/build/services/escalation/index.js +1 -0
- package/build/services/escalation/roster.d.ts +3 -1
- package/build/services/escalation/roster.js +6 -1
- package/build/services/scan-code/validate-subject.js +16 -0
- package/build/services/scan-code/validate.js +7 -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/x-lt-actions.d.ts +80 -0
- package/build/shared/form-validation/x-lt-actions.js +133 -0
- package/build/shared/form-validation/x-lt-roster.d.ts +2 -0
- package/build/shared/form-validation/x-lt-roster.js +28 -3
- package/build/tsconfig.tsbuildinfo +1 -1
- package/build/types/escalation.d.ts +19 -0
- package/build/types/escalation.js +4 -0
- package/build/types/index.d.ts +1 -1
- package/build/types/scan-choice.d.ts +9 -0
- package/build/types/scan-code/response.d.ts +2 -0
- package/build/types/scan-code/rule.d.ts +17 -0
- package/dashboard/dist/assets/{AdminDashboard-C_0ShYrm.js → AdminDashboard-B8MqNLiB.js} +2 -2
- package/dashboard/dist/assets/{AdminDashboard-C_0ShYrm.js.map → AdminDashboard-B8MqNLiB.js.map} +1 -1
- package/dashboard/dist/assets/{AgentConfigPage-Cc9dI8lO.js → AgentConfigPage-Yg81Eiuo.js} +2 -2
- package/dashboard/dist/assets/{AgentConfigPage-Cc9dI8lO.js.map → AgentConfigPage-Yg81Eiuo.js.map} +1 -1
- package/dashboard/dist/assets/{AgentDetailPage-Sdmt5j-k.js → AgentDetailPage-ZUGYEONO.js} +3 -3
- package/dashboard/dist/assets/{AgentDetailPage-Sdmt5j-k.js.map → AgentDetailPage-ZUGYEONO.js.map} +1 -1
- package/dashboard/dist/assets/{AgentsPage-BuzXc1mg.js → AgentsPage-CZIWMwak.js} +2 -2
- package/dashboard/dist/assets/{AgentsPage-BuzXc1mg.js.map → AgentsPage-CZIWMwak.js.map} +1 -1
- package/dashboard/dist/assets/AvailableEscalationsPage-BVtpc4xO.js +2 -0
- package/dashboard/dist/assets/{AvailableEscalationsPage-DU84iaBN.js.map → AvailableEscalationsPage-BVtpc4xO.js.map} +1 -1
- package/dashboard/dist/assets/{BotPicker-Ci5PUrkl.js → BotPicker-8bf0yOCT.js} +2 -2
- package/dashboard/dist/assets/{BotPicker-Ci5PUrkl.js.map → BotPicker-8bf0yOCT.js.map} +1 -1
- package/dashboard/dist/assets/{BulkAssignModal-PLx7SLvF.js → BulkAssignModal-B2lr1J6P.js} +2 -2
- package/dashboard/dist/assets/{BulkAssignModal-PLx7SLvF.js.map → BulkAssignModal-B2lr1J6P.js.map} +1 -1
- package/dashboard/dist/assets/{CapabilitiesPage-DuSfgr6i.js → CapabilitiesPage-BQQBR2sj.js} +2 -2
- package/dashboard/dist/assets/{CapabilitiesPage-DuSfgr6i.js.map → CapabilitiesPage-BQQBR2sj.js.map} +1 -1
- package/dashboard/dist/assets/{CollapsibleSection-KOO37qs6.js → CollapsibleSection-DFW8TYyU.js} +2 -2
- package/dashboard/dist/assets/{CollapsibleSection-KOO37qs6.js.map → CollapsibleSection-DFW8TYyU.js.map} +1 -1
- package/dashboard/dist/assets/{ConfirmDeleteModal-CI1eE-Nr.js → ConfirmDeleteModal-DGlOlQAo.js} +2 -2
- package/dashboard/dist/assets/{ConfirmDeleteModal-CI1eE-Nr.js.map → ConfirmDeleteModal-DGlOlQAo.js.map} +1 -1
- package/dashboard/dist/assets/{ConnectedAppsPage-DZgbCC0G.js → ConnectedAppsPage-CIM9gzHj.js} +2 -2
- package/dashboard/dist/assets/{ConnectedAppsPage-DZgbCC0G.js.map → ConnectedAppsPage-CIM9gzHj.js.map} +1 -1
- package/dashboard/dist/assets/{CountdownTimer-C-Ngk8oq.js → CountdownTimer-BD_iWm6S.js} +2 -2
- package/dashboard/dist/assets/{CountdownTimer-C-Ngk8oq.js.map → CountdownTimer-BD_iWm6S.js.map} +1 -1
- package/dashboard/dist/assets/{CredentialsPage-BR7s6eVu.js → CredentialsPage-CTtUkvoD.js} +2 -2
- package/dashboard/dist/assets/{CredentialsPage-BR7s6eVu.js.map → CredentialsPage-CTtUkvoD.js.map} +1 -1
- package/dashboard/dist/assets/{CronLabel-BQ7Y1Nfd.js → CronLabel-rZMptkYQ.js} +2 -2
- package/dashboard/dist/assets/{CronLabel-BQ7Y1Nfd.js.map → CronLabel-rZMptkYQ.js.map} +1 -1
- package/dashboard/dist/assets/{DropZone-DCZpORx-.js → DropZone-u1JGfR9G.js} +2 -2
- package/dashboard/dist/assets/{DropZone-DCZpORx-.js.map → DropZone-u1JGfR9G.js.map} +1 -1
- package/dashboard/dist/assets/{ElapsedCell-BT49mUIc.js → ElapsedCell-CmK9gHC5.js} +2 -2
- package/dashboard/dist/assets/{ElapsedCell-BT49mUIc.js.map → ElapsedCell-CmK9gHC5.js.map} +1 -1
- package/dashboard/dist/assets/{EntityTimelinePanel-BtARWy3R.js → EntityTimelinePanel-Cfg0EMZs.js} +2 -2
- package/dashboard/dist/assets/{EntityTimelinePanel-BtARWy3R.js.map → EntityTimelinePanel-Cfg0EMZs.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationListSchemaPage-EEBsNpTe.js → EscalationListSchemaPage-CPcbC1-9.js} +2 -2
- package/dashboard/dist/assets/{EscalationListSchemaPage-EEBsNpTe.js.map → EscalationListSchemaPage-CPcbC1-9.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationSchemaPage-BHSC46Ow.js → EscalationSchemaPage-BsYdNVN7.js} +3 -3
- package/dashboard/dist/assets/{EscalationSchemaPage-BHSC46Ow.js.map → EscalationSchemaPage-BsYdNVN7.js.map} +1 -1
- package/dashboard/dist/assets/EscalationTimeline-D1enxZDs.js +2 -0
- package/dashboard/dist/assets/{EscalationTimeline-BWTUBxUf.js.map → EscalationTimeline-D1enxZDs.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationTitleSelect-DOsfY4u8.js → EscalationTitleSelect-DwqhnPKX.js} +2 -2
- package/dashboard/dist/assets/{EscalationTitleSelect-DOsfY4u8.js.map → EscalationTitleSelect-DwqhnPKX.js.map} +1 -1
- package/dashboard/dist/assets/{EscalationsOverview-Bl8g53x-.js → EscalationsOverview-ChuKh02E.js} +2 -2
- package/dashboard/dist/assets/{EscalationsOverview-Bl8g53x-.js.map → EscalationsOverview-ChuKh02E.js.map} +1 -1
- package/dashboard/dist/assets/{EventTable-Bjx17W_i.js → EventTable-GqZIKPce.js} +2 -2
- package/dashboard/dist/assets/{EventTable-Bjx17W_i.js.map → EventTable-GqZIKPce.js.map} +1 -1
- package/dashboard/dist/assets/{FilterBar-Dnse0soD.js → FilterBar-CqPFZkVU.js} +2 -2
- package/dashboard/dist/assets/{FilterBar-Dnse0soD.js.map → FilterBar-CqPFZkVU.js.map} +1 -1
- package/dashboard/dist/assets/{GraphInvokePage-HQEvbHTd.js → GraphInvokePage-_fB7mtBU.js} +2 -2
- package/dashboard/dist/assets/{GraphInvokePage-HQEvbHTd.js.map → GraphInvokePage-_fB7mtBU.js.map} +1 -1
- package/dashboard/dist/assets/{HomePage-DizTklVg.js → HomePage-BXA0Cz0F.js} +2 -2
- package/dashboard/dist/assets/{HomePage-DizTklVg.js.map → HomePage-BXA0Cz0F.js.map} +1 -1
- package/dashboard/dist/assets/{ListToolbar-B0qe2QZ5.js → ListToolbar-Bf4DFITI.js} +2 -2
- package/dashboard/dist/assets/{ListToolbar-B0qe2QZ5.js.map → ListToolbar-Bf4DFITI.js.map} +1 -1
- package/dashboard/dist/assets/{McpOverview-DmH-qtX9.js → McpOverview-H61XQRji.js} +2 -2
- package/dashboard/dist/assets/{McpOverview-DmH-qtX9.js.map → McpOverview-H61XQRji.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryDetailPage-B-qtmwLd.js → McpQueryDetailPage-wbwK8WWq.js} +2 -2
- package/dashboard/dist/assets/{McpQueryDetailPage-B-qtmwLd.js.map → McpQueryDetailPage-wbwK8WWq.js.map} +1 -1
- package/dashboard/dist/assets/{McpQueryPage-DX1T-Ugq.js → McpQueryPage-CYvVEk3G.js} +2 -2
- package/dashboard/dist/assets/{McpQueryPage-DX1T-Ugq.js.map → McpQueryPage-CYvVEk3G.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunDetailPage-DNdl10nB.js → McpRunDetailPage-TZBX68i4.js} +2 -2
- package/dashboard/dist/assets/{McpRunDetailPage-DNdl10nB.js.map → McpRunDetailPage-TZBX68i4.js.map} +1 -1
- package/dashboard/dist/assets/{McpRunsPage-DVXVB4AJ.js → McpRunsPage-Cu91Mh52.js} +2 -2
- package/dashboard/dist/assets/{McpRunsPage-DVXVB4AJ.js.map → McpRunsPage-Cu91Mh52.js.map} +1 -1
- package/dashboard/dist/assets/{NamespacePill-CSZT2fA4.js → NamespacePill-C5epAgq6.js} +2 -2
- package/dashboard/dist/assets/{NamespacePill-CSZT2fA4.js.map → NamespacePill-C5epAgq6.js.map} +1 -1
- package/dashboard/dist/assets/{OperationsPage-BxgWQq8v.js → OperationsPage-D-6sKw6y.js} +2 -2
- package/dashboard/dist/assets/{OperationsPage-BxgWQq8v.js.map → OperationsPage-D-6sKw6y.js.map} +1 -1
- package/dashboard/dist/assets/OperatorDashboard-DD8JUU3L.js +2 -0
- package/dashboard/dist/assets/{OperatorDashboard-Cw86vOPg.js.map → OperatorDashboard-DD8JUU3L.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeader-C4z1ExF7.js → PageHeader-BSWIspNB.js} +2 -2
- package/dashboard/dist/assets/{PageHeader-C4z1ExF7.js.map → PageHeader-BSWIspNB.js.map} +1 -1
- package/dashboard/dist/assets/{PageHeaderWithStats-B_f_Sz_Q.js → PageHeaderWithStats-DDjopQ-k.js} +2 -2
- package/dashboard/dist/assets/{PageHeaderWithStats-B_f_Sz_Q.js.map → PageHeaderWithStats-DDjopQ-k.js.map} +1 -1
- package/dashboard/dist/assets/{PersonaDetailPage-BTIJ4A6p.js → PersonaDetailPage-CGri6okV.js} +2 -2
- package/dashboard/dist/assets/{PersonaDetailPage-BTIJ4A6p.js.map → PersonaDetailPage-CGri6okV.js.map} +1 -1
- package/dashboard/dist/assets/{PersonasPage-D11PokPb.js → PersonasPage-DtHW6-Qx.js} +2 -2
- package/dashboard/dist/assets/{PersonasPage-D11PokPb.js.map → PersonasPage-DtHW6-Qx.js.map} +1 -1
- package/dashboard/dist/assets/PortalPage-Dgbk_mzr.js +2 -0
- package/dashboard/dist/assets/{PortalPage-1rXm0cNK.js.map → PortalPage-Dgbk_mzr.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessDetailPage-Cp5-969g.js → ProcessDetailPage-BdkacBrD.js} +2 -2
- package/dashboard/dist/assets/{ProcessDetailPage-Cp5-969g.js.map → ProcessDetailPage-BdkacBrD.js.map} +1 -1
- package/dashboard/dist/assets/{ProcessesListPage-assXQe-x.js → ProcessesListPage-DO5A-OfZ.js} +2 -2
- package/dashboard/dist/assets/{ProcessesListPage-assXQe-x.js.map → ProcessesListPage-DO5A-OfZ.js.map} +1 -1
- package/dashboard/dist/assets/ResolverForm-CqGE5lf-.js +8 -0
- package/dashboard/dist/assets/ResolverForm-CqGE5lf-.js.map +1 -0
- package/dashboard/dist/assets/{RoleDetailPage-c_Qij0Tf.js → RoleDetailPage-DgVp0Hih.js} +2 -2
- package/dashboard/dist/assets/{RoleDetailPage-c_Qij0Tf.js.map → RoleDetailPage-DgVp0Hih.js.map} +1 -1
- package/dashboard/dist/assets/{RolePill-Bw_n7EpR.js → RolePill-CoETDpjF.js} +2 -2
- package/dashboard/dist/assets/{RolePill-Bw_n7EpR.js.map → RolePill-CoETDpjF.js.map} +1 -1
- package/dashboard/dist/assets/{RolesPage-D4oaYgTg.js → RolesPage-BmsWq83e.js} +2 -2
- package/dashboard/dist/assets/{RolesPage-D4oaYgTg.js.map → RolesPage-BmsWq83e.js.map} +1 -1
- package/dashboard/dist/assets/RosterWidget-DR-T4Izu.js +2 -0
- package/dashboard/dist/assets/RosterWidget-DR-T4Izu.js.map +1 -0
- package/dashboard/dist/assets/{RunAsSelector-Fptil_sB.js → RunAsSelector-CyUWGp5P.js} +2 -2
- package/dashboard/dist/assets/{RunAsSelector-Fptil_sB.js.map → RunAsSelector-CyUWGp5P.js.map} +1 -1
- package/dashboard/dist/assets/{ScanCodesPage-BeZA_YJW.js → ScanCodesPage-Cy05JOt4.js} +2 -2
- package/dashboard/dist/assets/{ScanCodesPage-BeZA_YJW.js.map → ScanCodesPage-Cy05JOt4.js.map} +1 -1
- package/dashboard/dist/assets/{ScanSchemeDetailPage-Bxi15xNr.js → ScanSchemeDetailPage-BX1afzOg.js} +2 -2
- package/dashboard/dist/assets/{ScanSchemeDetailPage-Bxi15xNr.js.map → ScanSchemeDetailPage-BX1afzOg.js.map} +1 -1
- package/dashboard/dist/assets/ScanStationPage-DLAB41tb.js +2 -0
- package/dashboard/dist/assets/ScanStationPage-DLAB41tb.js.map +1 -0
- package/dashboard/dist/assets/{ScopeBadge-CKgfwTy3.js → ScopeBadge-CGsTtqFr.js} +2 -2
- package/dashboard/dist/assets/{ScopeBadge-CKgfwTy3.js.map → ScopeBadge-CGsTtqFr.js.map} +1 -1
- package/dashboard/dist/assets/{StickyPagination-C2drZk7w.js → StickyPagination-BzUTnYYE.js} +2 -2
- package/dashboard/dist/assets/{StickyPagination-C2drZk7w.js.map → StickyPagination-BzUTnYYE.js.map} +1 -1
- package/dashboard/dist/assets/{StreamMessageDetail-x-l5653y.js → StreamMessageDetail-D0W1ekgG.js} +2 -2
- package/dashboard/dist/assets/{StreamMessageDetail-x-l5653y.js.map → StreamMessageDetail-D0W1ekgG.js.map} +1 -1
- package/dashboard/dist/assets/{SwimlaneTimeline-Ds_CqLXL.js → SwimlaneTimeline-BPBwmwuO.js} +2 -2
- package/dashboard/dist/assets/{SwimlaneTimeline-Ds_CqLXL.js.map → SwimlaneTimeline-BPBwmwuO.js.map} +1 -1
- package/dashboard/dist/assets/{TagInput-B6fMAqxd.js → TagInput-S7UuVT_M.js} +2 -2
- package/dashboard/dist/assets/{TagInput-B6fMAqxd.js.map → TagInput-S7UuVT_M.js.map} +1 -1
- package/dashboard/dist/assets/{TaskDetailPage-BJGKEnIo.js → TaskDetailPage-VuaIXISy.js} +2 -2
- package/dashboard/dist/assets/{TaskDetailPage-BJGKEnIo.js.map → TaskDetailPage-VuaIXISy.js.map} +1 -1
- package/dashboard/dist/assets/{TaskQueuePill-BnDp_DE6.js → TaskQueuePill-BlHNpYiZ.js} +2 -2
- package/dashboard/dist/assets/{TaskQueuePill-BnDp_DE6.js.map → TaskQueuePill-BlHNpYiZ.js.map} +1 -1
- package/dashboard/dist/assets/{TasksListPage-DyLaZvTT.js → TasksListPage-KaUSoqos.js} +2 -2
- package/dashboard/dist/assets/{TasksListPage-DyLaZvTT.js.map → TasksListPage-KaUSoqos.js.map} +1 -1
- package/dashboard/dist/assets/{TimeAgo-B_Ta97uy.js → TimeAgo-C_E6u0fr.js} +2 -2
- package/dashboard/dist/assets/{TimeAgo-B_Ta97uy.js.map → TimeAgo-C_E6u0fr.js.map} +1 -1
- package/dashboard/dist/assets/{TimestampCell-CiTYBQw-.js → TimestampCell-CUJiAmax.js} +2 -2
- package/dashboard/dist/assets/{TimestampCell-CiTYBQw-.js.map → TimestampCell-CUJiAmax.js.map} +1 -1
- package/dashboard/dist/assets/{ToolPill-BRZWo4l8.js → ToolPill-D40uLM_v.js} +2 -2
- package/dashboard/dist/assets/{ToolPill-BRZWo4l8.js.map → ToolPill-D40uLM_v.js.map} +1 -1
- package/dashboard/dist/assets/{ToolTestPanel-Dw8Zu6xr.js → ToolTestPanel-Ch5nQANh.js} +2 -2
- package/dashboard/dist/assets/{ToolTestPanel-Dw8Zu6xr.js.map → ToolTestPanel-Ch5nQANh.js.map} +1 -1
- package/dashboard/dist/assets/{TopicDetailPage-DPMAWKTN.js → TopicDetailPage-BprToO7w.js} +2 -2
- package/dashboard/dist/assets/{TopicDetailPage-DPMAWKTN.js.map → TopicDetailPage-BprToO7w.js.map} +1 -1
- package/dashboard/dist/assets/{TopicsPage-0Z2lAE0d.js → TopicsPage-8SmCnQ-E.js} +2 -2
- package/dashboard/dist/assets/{TopicsPage-0Z2lAE0d.js.map → TopicsPage-8SmCnQ-E.js.map} +1 -1
- package/dashboard/dist/assets/{UserName-DrAbCpdY.js → UserName-BkJryunF.js} +2 -2
- package/dashboard/dist/assets/{UserName-DrAbCpdY.js.map → UserName-BkJryunF.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowExecutionPage-DQITqX-6.js → WorkflowExecutionPage-B2S5ODie.js} +2 -2
- package/dashboard/dist/assets/{WorkflowExecutionPage-DQITqX-6.js.map → WorkflowExecutionPage-B2S5ODie.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowPill-Cly9Ln_3.js → WorkflowPill-DJLcK_CM.js} +2 -2
- package/dashboard/dist/assets/{WorkflowPill-Cly9Ln_3.js.map → WorkflowPill-DJLcK_CM.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsDashboard-CMJPiR6G.js → WorkflowsDashboard-CABuRCXp.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsDashboard-CMJPiR6G.js.map → WorkflowsDashboard-CABuRCXp.js.map} +1 -1
- package/dashboard/dist/assets/{WorkflowsOverview-DwVMLe1y.js → WorkflowsOverview-BPUHAj8T.js} +2 -2
- package/dashboard/dist/assets/{WorkflowsOverview-DwVMLe1y.js.map → WorkflowsOverview-BPUHAj8T.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-Do6s_wmW.js → YamlWorkflowDetailPage-DZGjT-Tl.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowDetailPage-Do6s_wmW.js.map → YamlWorkflowDetailPage-DZGjT-Tl.js.map} +1 -1
- package/dashboard/dist/assets/{YamlWorkflowsPage-8Sr6rZMZ.js → YamlWorkflowsPage-DIxHURlM.js} +2 -2
- package/dashboard/dist/assets/{YamlWorkflowsPage-8Sr6rZMZ.js.map → YamlWorkflowsPage-DIxHURlM.js.map} +1 -1
- package/dashboard/dist/assets/{agents-Maom-dbf.js → agents-BnDp2rcb.js} +2 -2
- package/dashboard/dist/assets/{agents-Maom-dbf.js.map → agents-BnDp2rcb.js.map} +1 -1
- package/dashboard/dist/assets/{bots-BESWftTA.js → bots-maURYoyr.js} +2 -2
- package/dashboard/dist/assets/{bots-BESWftTA.js.map → bots-maURYoyr.js.map} +1 -1
- package/dashboard/dist/assets/{capabilities-BnvJAmFQ.js → capabilities-5-TakMUh.js} +2 -2
- package/dashboard/dist/assets/{capabilities-BnvJAmFQ.js.map → capabilities-5-TakMUh.js.map} +1 -1
- package/dashboard/dist/assets/{controlplane-Cu7TD1RX.js → controlplane-BXVVTWoh.js} +2 -2
- package/dashboard/dist/assets/{controlplane-Cu7TD1RX.js.map → controlplane-BXVVTWoh.js.map} +1 -1
- package/dashboard/dist/assets/escalation-columns-v8gx8wFb.js +2 -0
- package/dashboard/dist/assets/{escalation-columns-BPSH8qMx.js.map → escalation-columns-v8gx8wFb.js.map} +1 -1
- package/dashboard/dist/assets/escalation-items-OWntjDaH.js +2 -0
- package/dashboard/dist/assets/escalation-items-OWntjDaH.js.map +1 -0
- package/dashboard/dist/assets/{index-BkVarz6E.js → index-BIfY4mR2.js} +2 -2
- package/dashboard/dist/assets/{index-BkVarz6E.js.map → index-BIfY4mR2.js.map} +1 -1
- package/dashboard/dist/assets/{index-WA8py2Yc.js → index-BTm3-qwq.js} +2 -2
- package/dashboard/dist/assets/{index-WA8py2Yc.js.map → index-BTm3-qwq.js.map} +1 -1
- package/dashboard/dist/assets/{index-B4SNhwgt.js → index-CR9rhAvN.js} +2 -2
- package/dashboard/dist/assets/{index-B4SNhwgt.js.map → index-CR9rhAvN.js.map} +1 -1
- package/dashboard/dist/assets/index-CWKs10VQ.js +13 -0
- package/dashboard/dist/assets/{index-qY2KDGkM.js.map → index-CWKs10VQ.js.map} +1 -1
- package/dashboard/dist/assets/index-CvKegIVe.js +3 -0
- package/dashboard/dist/assets/{index-kGSUZCOb.js.map → index-CvKegIVe.js.map} +1 -1
- package/dashboard/dist/assets/{index-BLbZsMRG.js → index-D7peGNN0.js} +10 -10
- package/dashboard/dist/assets/index-D7peGNN0.js.map +1 -0
- package/dashboard/dist/assets/{index-BvI2_dSt.js → index-D_ojXTgO.js} +2 -2
- package/dashboard/dist/assets/{index-BvI2_dSt.js.map → index-D_ojXTgO.js.map} +1 -1
- package/dashboard/dist/assets/index-Dafrbwk_.js +2 -0
- package/dashboard/dist/assets/{index-AJNPDsTI.js.map → index-Dafrbwk_.js.map} +1 -1
- package/dashboard/dist/assets/{index-BpWutGxS.js → index-Dk7nwVpm.js} +2 -2
- package/dashboard/dist/assets/{index-BpWutGxS.js.map → index-Dk7nwVpm.js.map} +1 -1
- package/dashboard/dist/assets/{index-_yHsJJTS.js → index-DvkcyTE2.js} +2 -2
- package/dashboard/dist/assets/{index-_yHsJJTS.js.map → index-DvkcyTE2.js.map} +1 -1
- package/dashboard/dist/assets/{index-BLlg1aVG.js → index-WPVpyoD3.js} +2 -2
- package/dashboard/dist/assets/{index-BLlg1aVG.js.map → index-WPVpyoD3.js.map} +1 -1
- package/dashboard/dist/assets/{index-D5JQweUm.js → index-YGTGxcTF.js} +2 -2
- package/dashboard/dist/assets/{index-D5JQweUm.js.map → index-YGTGxcTF.js.map} +1 -1
- package/dashboard/dist/assets/{index-BG7OpNg7.js → index-vgHzaGmW.js} +2 -2
- package/dashboard/dist/assets/{index-BG7OpNg7.js.map → index-vgHzaGmW.js.map} +1 -1
- package/dashboard/dist/assets/{knowledge-DCFRNG8R.js → knowledge-L_zY1zmy.js} +2 -2
- package/dashboard/dist/assets/{knowledge-DCFRNG8R.js.map → knowledge-L_zY1zmy.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-query-Wt8z6yAL.js → mcp-query-CFbGudkb.js} +2 -2
- package/dashboard/dist/assets/{mcp-query-Wt8z6yAL.js.map → mcp-query-CFbGudkb.js.map} +1 -1
- package/dashboard/dist/assets/{mcp-CZhOocmN.js → mcp-wCWFRA0T.js} +2 -2
- package/dashboard/dist/assets/{mcp-CZhOocmN.js.map → mcp-wCWFRA0T.js.map} +1 -1
- package/dashboard/dist/assets/{personas-D2_foqJm.js → personas-B_VIacZJ.js} +2 -2
- package/dashboard/dist/assets/{personas-D2_foqJm.js.map → personas-B_VIacZJ.js.map} +1 -1
- package/dashboard/dist/assets/{pipelines-Bb159fNz.js → pipelines-Bv0Z8xQA.js} +2 -2
- package/dashboard/dist/assets/{pipelines-Bb159fNz.js.map → pipelines-Bv0Z8xQA.js.map} +1 -1
- package/dashboard/dist/assets/{tasks-xAU6C6tb.js → tasks-DP6yaNZs.js} +2 -2
- package/dashboard/dist/assets/{tasks-xAU6C6tb.js.map → tasks-DP6yaNZs.js.map} +1 -1
- package/dashboard/dist/assets/{topics-DFdqb2Te.js → topics-UBSIcpSR.js} +2 -2
- package/dashboard/dist/assets/{topics-DFdqb2Te.js.map → topics-UBSIcpSR.js.map} +1 -1
- package/dashboard/dist/assets/{useDebouncedValue-CbHmpS4w.js → useDebouncedValue-R62q3Xpx.js} +2 -2
- package/dashboard/dist/assets/{useDebouncedValue-CbHmpS4w.js.map → useDebouncedValue-R62q3Xpx.js.map} +1 -1
- package/dashboard/dist/assets/{useNamespace-B_00X1yb.js → useNamespace-CAaszJDE.js} +2 -2
- package/dashboard/dist/assets/{useNamespace-B_00X1yb.js.map → useNamespace-CAaszJDE.js.map} +1 -1
- package/dashboard/dist/assets/{useYamlActivityEvents-B4F-s9g0.js → useYamlActivityEvents-emCp9U69.js} +2 -2
- package/dashboard/dist/assets/{useYamlActivityEvents-B4F-s9g0.js.map → useYamlActivityEvents-emCp9U69.js.map} +1 -1
- package/dashboard/dist/assets/validate-C7yskdEn.js +2 -0
- package/dashboard/dist/assets/validate-C7yskdEn.js.map +1 -0
- package/dashboard/dist/assets/{vendor-icons-C_DQEeBy.js → vendor-icons-DUYcKTjP.js} +2 -2
- package/dashboard/dist/assets/{vendor-icons-C_DQEeBy.js.map → vendor-icons-DUYcKTjP.js.map} +1 -1
- package/dashboard/dist/assets/{yaml-workflows-DyjbPFVp.js → yaml-workflows-C2sOwz1p.js} +2 -2
- package/dashboard/dist/assets/{yaml-workflows-DyjbPFVp.js.map → yaml-workflows-C2sOwz1p.js.map} +1 -1
- package/dashboard/dist/index.html +2 -2
- package/docs/accumulator.md +1 -1
- package/docs/api/http/escalations.md +33 -0
- package/docs/api/sdk/escalations.md +26 -1
- package/docs/hitl/escalation.md +9 -0
- package/docs/hitl/x-lt-embed.md +2 -0
- package/docs/hitl/x-lt-roster.md +31 -7
- package/docs/scan-codes.md +45 -3
- package/package.json +2 -2
- package/dashboard/dist/assets/AvailableEscalationsPage-DU84iaBN.js +0 -2
- package/dashboard/dist/assets/EscalationTimeline-BWTUBxUf.js +0 -2
- package/dashboard/dist/assets/FormattedValue-DeVrRJy4.js +0 -2
- package/dashboard/dist/assets/FormattedValue-DeVrRJy4.js.map +0 -1
- package/dashboard/dist/assets/OperatorDashboard-Cw86vOPg.js +0 -2
- package/dashboard/dist/assets/PortalPage-1rXm0cNK.js +0 -2
- package/dashboard/dist/assets/ResolverForm-CMzNjmGG.js +0 -8
- package/dashboard/dist/assets/ResolverForm-CMzNjmGG.js.map +0 -1
- package/dashboard/dist/assets/ScanStationPage-t5Bz957t.js +0 -2
- package/dashboard/dist/assets/ScanStationPage-t5Bz957t.js.map +0 -1
- package/dashboard/dist/assets/escalation-columns-BPSH8qMx.js +0 -2
- package/dashboard/dist/assets/escalation-items-uDQTphVW.js +0 -2
- package/dashboard/dist/assets/escalation-items-uDQTphVW.js.map +0 -1
- package/dashboard/dist/assets/index-AJNPDsTI.js +0 -2
- package/dashboard/dist/assets/index-BLbZsMRG.js.map +0 -1
- package/dashboard/dist/assets/index-kGSUZCOb.js +0 -3
- package/dashboard/dist/assets/index-qY2KDGkM.js +0 -13
|
@@ -40,6 +40,7 @@ const resolve_batch_1 = require("../escalations/resolve-batch");
|
|
|
40
40
|
const escalation_1 = require("../../types/escalation");
|
|
41
41
|
const types_1 = require("../../types");
|
|
42
42
|
const context_1 = require("./context");
|
|
43
|
+
const fill_labels_1 = require("./fill-labels");
|
|
43
44
|
const grant_1 = require("./grant");
|
|
44
45
|
const locate_1 = require("./locate");
|
|
45
46
|
const subject_1 = require("./subject");
|
|
@@ -60,6 +61,18 @@ function codeOf(key, separator) {
|
|
|
60
61
|
function keysFor(keys, target, separator) {
|
|
61
62
|
return keys.filter((k) => codeOf(k, separator) === target);
|
|
62
63
|
}
|
|
64
|
+
/** Labeled pending entries read as one line; labels may carry commas. */
|
|
65
|
+
const LABEL_JOIN = '; ';
|
|
66
|
+
/** The `{fill.pending}` and `{fill.next}` values for the open keys. */
|
|
67
|
+
function pendingCopy(pending, separator, labeler) {
|
|
68
|
+
const code = (key) => codeOf(key, separator);
|
|
69
|
+
if (!labeler) {
|
|
70
|
+
const codes = [...new Set(pending.map(code))];
|
|
71
|
+
return { pending: codes, ...(codes.length ? { next: codes[0] } : {}) };
|
|
72
|
+
}
|
|
73
|
+
const labels = (0, fill_labels_1.pendingLabels)(pending, code, labeler);
|
|
74
|
+
return { pending: labels.join(LABEL_JOIN), ...(labels.length ? { next: labels[0] } : {}) };
|
|
75
|
+
}
|
|
63
76
|
function progress(batch, filledNow) {
|
|
64
77
|
const remaining = Math.max(0, batch.pending.length - filledNow);
|
|
65
78
|
return { filled: batch.keys.length - remaining, total: batch.keys.length, remaining };
|
|
@@ -82,10 +95,12 @@ async function fillStep(step, ctx) {
|
|
|
82
95
|
if (!batch)
|
|
83
96
|
return (0, context_1.conflict)('the held row is not a batch');
|
|
84
97
|
// 2. Which open slot this scan fills, or why none
|
|
98
|
+
const labeler = (0, fill_labels_1.entryLabeler)(options, row, ctx.scheme);
|
|
99
|
+
const open = pendingCopy(batch.pending, separator, labeler);
|
|
85
100
|
const tpl = {
|
|
86
101
|
...(0, context_1.templateContext)(ctx),
|
|
87
102
|
fill: {
|
|
88
|
-
|
|
103
|
+
...open,
|
|
89
104
|
total: batch.keys.length,
|
|
90
105
|
filled: batch.keys.length - batch.pending.length,
|
|
91
106
|
},
|
|
@@ -96,9 +111,10 @@ async function fillStep(step, ctx) {
|
|
|
96
111
|
const markdown = declared.length
|
|
97
112
|
? `All ${declared.length} of these are already checked off.`
|
|
98
113
|
: scanCodeService.renderScanCopy(step.refuse?.markdown ?? STOCK_UNEXPECTED, tpl);
|
|
99
|
-
const
|
|
100
|
-
|
|
101
|
-
|
|
114
|
+
const codes = [...new Set(batch.pending.map((k) => codeOf(k, separator)))];
|
|
115
|
+
const expected = labeler
|
|
116
|
+
? (0, fill_labels_1.pendingLabels)(batch.pending, (k) => codeOf(k, separator), labeler)
|
|
117
|
+
: codes.map((code) => (0, fill_labels_1.printedCode)(code, ctx.scheme));
|
|
102
118
|
return (0, context_1.refused)(markdown, {
|
|
103
119
|
refusal: { markdown, expected },
|
|
104
120
|
progress: progress(batch, 0),
|
|
@@ -125,6 +141,7 @@ async function fillStep(step, ctx) {
|
|
|
125
141
|
const data = result.data;
|
|
126
142
|
const done = data.outcome === 'completed';
|
|
127
143
|
const filled = progress(batch, batch.pending.length - (done ? 0 : (data.remaining ?? 0)));
|
|
144
|
+
const next = pendingCopy(batch.pending.filter((k) => k !== itemKey), separator, labeler).next;
|
|
128
145
|
return (0, context_1.withDone)({
|
|
129
146
|
status: 200,
|
|
130
147
|
data: {
|
|
@@ -134,7 +151,7 @@ async function fillStep(step, ctx) {
|
|
|
134
151
|
progress: filled,
|
|
135
152
|
...(done ? { clearSubject: true } : {}),
|
|
136
153
|
},
|
|
137
|
-
}, step, { ...tpl, fill: { ...tpl.fill, ...filled } });
|
|
154
|
+
}, step, { ...tpl, fill: { ...tpl.fill, ...filled, next } });
|
|
138
155
|
}
|
|
139
156
|
const outcome = result.data?.outcome;
|
|
140
157
|
if (outcome === 'duplicate-item')
|
|
@@ -126,6 +126,51 @@ function registerResolveRoutes(router) {
|
|
|
126
126
|
}, auth);
|
|
127
127
|
res.status(result.status).json(result.data ?? { error: result.error });
|
|
128
128
|
});
|
|
129
|
+
/**
|
|
130
|
+
* POST /api/escalations/:id/flag-batch-item
|
|
131
|
+
* Flag ONE pending key of a batch escalation (e.g. `Missing`). The key stays
|
|
132
|
+
* pending and still fills; a wait declared with `wakeOnFlag` ends on the flag.
|
|
133
|
+
* Body: { itemKey: string, label: string, payload?: Record<string, any> }
|
|
134
|
+
*/
|
|
135
|
+
router.post('/:id/flag-batch-item', async (req, res) => {
|
|
136
|
+
const auth = await (0, acting_1.effectiveWorkAuth)(req, res);
|
|
137
|
+
if (!auth)
|
|
138
|
+
return;
|
|
139
|
+
const result = await api.flagBatchItem({ id: req.params.id, itemKey: req.body?.itemKey, label: req.body?.label, payload: req.body?.payload }, auth);
|
|
140
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
141
|
+
});
|
|
142
|
+
/**
|
|
143
|
+
* POST /api/escalations/:id/unflag-batch-item
|
|
144
|
+
* Clear ONE key's flag while the batch escalation is pending.
|
|
145
|
+
* Body: { itemKey: string }
|
|
146
|
+
*/
|
|
147
|
+
router.post('/:id/unflag-batch-item', async (req, res) => {
|
|
148
|
+
const auth = await (0, acting_1.effectiveWorkAuth)(req, res);
|
|
149
|
+
if (!auth)
|
|
150
|
+
return;
|
|
151
|
+
const result = await api.unflagBatchItem({ id: req.params.id, itemKey: req.body?.itemKey }, auth);
|
|
152
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
153
|
+
});
|
|
154
|
+
/**
|
|
155
|
+
* POST /api/escalations/:id/roster-action
|
|
156
|
+
* Run one action a roster entry offers on this row's form. The server
|
|
157
|
+
* re-reads the action from the row's form schema, checks the entry and any
|
|
158
|
+
* role limit, and runs the act as the person acting: the badged person when
|
|
159
|
+
* X-LT-Acting-Token rides the request (spent only when the act runs).
|
|
160
|
+
* Body: { field: string, action: number, entry: string, code?: string }
|
|
161
|
+
*/
|
|
162
|
+
router.post('/:id/roster-action', async (req, res) => {
|
|
163
|
+
const token = req.headers[acting_1.ACTING_TOKEN_HEADER];
|
|
164
|
+
const result = await api.runRosterAction({
|
|
165
|
+
id: req.params.id,
|
|
166
|
+
field: req.body?.field,
|
|
167
|
+
action: req.body?.action,
|
|
168
|
+
entry: req.body?.entry,
|
|
169
|
+
code: req.body?.code,
|
|
170
|
+
actingToken: typeof token === 'string' && token ? token : undefined,
|
|
171
|
+
}, req.auth);
|
|
172
|
+
res.status(result.status).json(result.data ?? { error: result.error });
|
|
173
|
+
});
|
|
129
174
|
/**
|
|
130
175
|
* POST /api/escalations/:id/resolve
|
|
131
176
|
* Resolve a pending escalation with a human-provided payload. Routes by
|
package/build/sdk/index.d.ts
CHANGED
|
@@ -247,6 +247,17 @@ export declare function createClient(options?: LTClientOptions): {
|
|
|
247
247
|
metadata?: Record<string, any>;
|
|
248
248
|
restrictRoles?: string[];
|
|
249
249
|
}, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
|
|
250
|
+
flagBatchItem: (input: {
|
|
251
|
+
id: string;
|
|
252
|
+
itemKey: string;
|
|
253
|
+
label: string;
|
|
254
|
+
payload?: Record<string, any>;
|
|
255
|
+
}, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
|
|
256
|
+
unflagBatchItem: (input: {
|
|
257
|
+
id: string;
|
|
258
|
+
itemKey: string;
|
|
259
|
+
}, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
|
|
260
|
+
runRosterAction: (input: import("../api/escalations/roster-action").RosterActionInput, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
|
|
250
261
|
accumulate: (input: {
|
|
251
262
|
id: string;
|
|
252
263
|
} & import("../api/escalations/accumulate").AccumulateItemInput, auth?: LTApiAuth) => Promise<LTApiResult<any>>;
|
package/build/sdk/index.js
CHANGED
|
@@ -146,6 +146,9 @@ function createClient(options = {}) {
|
|
|
146
146
|
resolveBatchItem: bindAuth(escalationsApi.resolveBatchItem, auth),
|
|
147
147
|
resolveBatchItemBySignalKey: bindAuth(escalationsApi.resolveBatchItemBySignalKey, auth),
|
|
148
148
|
resolveBatchItemByMetadata: bindAuth(escalationsApi.resolveBatchItemByMetadata, auth),
|
|
149
|
+
flagBatchItem: bindAuth(escalationsApi.flagBatchItem, auth),
|
|
150
|
+
unflagBatchItem: bindAuth(escalationsApi.unflagBatchItem, auth),
|
|
151
|
+
runRosterAction: bindAuth(escalationsApi.runRosterAction, auth),
|
|
149
152
|
// Open accumulation: add and remove held items, read the ordered collection.
|
|
150
153
|
accumulate: bindAuth(escalationsApi.accumulateItem, auth),
|
|
151
154
|
accumulateBySignalKey: bindAuth(escalationsApi.accumulateItemBySignalKey, auth),
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { FlagBatchItemOutcome, LTEscalationRecord, UnflagBatchItemOutcome } from '../../types';
|
|
2
|
+
export interface FlagBatchItemInput {
|
|
3
|
+
label: string;
|
|
4
|
+
payload?: Record<string, any>;
|
|
5
|
+
actor?: string;
|
|
6
|
+
metadata?: Record<string, any>;
|
|
7
|
+
resolvedBy?: {
|
|
8
|
+
id: string;
|
|
9
|
+
email?: string;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
export interface BatchFlagOutcome<O extends string> {
|
|
13
|
+
outcome: O;
|
|
14
|
+
/** The post-write row; null on every failure outcome. */
|
|
15
|
+
escalation: LTEscalationRecord | null;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Flag one pending key of a batch escalation in a single SDK statement. The
|
|
19
|
+
* key stays pending. A row whose wait declared `wakeOnFlag` resolves in the
|
|
20
|
+
* same statement and its workflow wakes; that publishes `escalation.resolved`,
|
|
21
|
+
* any other flag `escalation.updated`. Failures publish nothing.
|
|
22
|
+
*/
|
|
23
|
+
export declare function flagBatchItem(id: string, itemKey: string, input: FlagBatchItemInput): Promise<BatchFlagOutcome<FlagBatchItemOutcome>>;
|
|
24
|
+
/** Clear one key's flag while the row is pending; publishes `escalation.updated`. */
|
|
25
|
+
export declare function unflagBatchItem(id: string, itemKey: string): Promise<BatchFlagOutcome<UnflagBatchItemOutcome>>;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.flagBatchItem = flagBatchItem;
|
|
4
|
+
exports.unflagBatchItem = unflagBatchItem;
|
|
5
|
+
const escalation_wire_1 = require("../../lib/events/escalation-wire");
|
|
6
|
+
const uuid_1 = require("../../lib/uuid");
|
|
7
|
+
const client_1 = require("./client");
|
|
8
|
+
const crud_1 = require("./crud");
|
|
9
|
+
const map_1 = require("./map");
|
|
10
|
+
/**
|
|
11
|
+
* Flag one pending key of a batch escalation in a single SDK statement. The
|
|
12
|
+
* key stays pending. A row whose wait declared `wakeOnFlag` resolves in the
|
|
13
|
+
* same statement and its workflow wakes; that publishes `escalation.resolved`,
|
|
14
|
+
* any other flag `escalation.updated`. Failures publish nothing.
|
|
15
|
+
*/
|
|
16
|
+
async function flagBatchItem(id, itemKey, input) {
|
|
17
|
+
if (!(0, uuid_1.isUuid)(id))
|
|
18
|
+
return { outcome: 'not-found', escalation: null };
|
|
19
|
+
const client = await (0, client_1.escalations)();
|
|
20
|
+
const result = await client.flagBatchItem({ id, itemKey, ...input });
|
|
21
|
+
if (!result.ok)
|
|
22
|
+
return { outcome: result.outcome, escalation: null };
|
|
23
|
+
const escalation = (0, map_1.toEscalationRecord)(result.entry);
|
|
24
|
+
publishFlagChange(escalation, itemKey, result.outcome === 'completed');
|
|
25
|
+
return { outcome: result.outcome, escalation };
|
|
26
|
+
}
|
|
27
|
+
/** Clear one key's flag while the row is pending; publishes `escalation.updated`. */
|
|
28
|
+
async function unflagBatchItem(id, itemKey) {
|
|
29
|
+
if (!(0, uuid_1.isUuid)(id))
|
|
30
|
+
return { outcome: 'not-found', escalation: null };
|
|
31
|
+
const client = await (0, client_1.escalations)();
|
|
32
|
+
const result = await client.unflagBatchItem({ id, itemKey });
|
|
33
|
+
if (!result.ok)
|
|
34
|
+
return { outcome: result.outcome, escalation: null };
|
|
35
|
+
const escalation = (0, map_1.toEscalationRecord)(result.entry);
|
|
36
|
+
publishFlagChange(escalation, itemKey, false);
|
|
37
|
+
return { outcome: result.outcome, escalation };
|
|
38
|
+
}
|
|
39
|
+
function publishFlagChange(escalation, itemKey, completed) {
|
|
40
|
+
(0, crud_1.publishEscalationChange)({
|
|
41
|
+
type: completed ? 'escalation.resolved' : 'escalation.updated',
|
|
42
|
+
source: 'service',
|
|
43
|
+
workflowId: escalation.workflow_id || '',
|
|
44
|
+
workflowName: escalation.workflow_type || '',
|
|
45
|
+
taskQueue: escalation.task_queue || '',
|
|
46
|
+
escalationId: escalation.id,
|
|
47
|
+
role: escalation.role,
|
|
48
|
+
status: completed ? 'resolved' : 'pending',
|
|
49
|
+
data: (0, escalation_wire_1.escalationEventData)(escalation, { item_key: itemKey }),
|
|
50
|
+
});
|
|
51
|
+
}
|
|
@@ -20,6 +20,7 @@ __exportStar(require("./crud"), exports);
|
|
|
20
20
|
var claim_asserted_1 = require("./claim-asserted");
|
|
21
21
|
Object.defineProperty(exports, "claimAssertedByMetadata", { enumerable: true, get: function () { return claim_asserted_1.claimAssertedByMetadata; } });
|
|
22
22
|
__exportStar(require("./batch"), exports);
|
|
23
|
+
__exportStar(require("./batch-flag"), exports);
|
|
23
24
|
__exportStar(require("./accumulate"), exports);
|
|
24
25
|
__exportStar(require("./bulk"), exports);
|
|
25
26
|
__exportStar(require("./queries"), exports);
|
|
@@ -8,6 +8,8 @@
|
|
|
8
8
|
* Member rows are read with `global: true`: a resolver who cannot see a
|
|
9
9
|
* member must never be told the roster is complete.
|
|
10
10
|
*/
|
|
11
|
-
import { type RosterCounts, type ShowIfContext } from '../../shared/form-validation';
|
|
11
|
+
import { type RosterCounts, type RosterDef, type RosterResult, type ShowIfContext } from '../../shared/form-validation';
|
|
12
12
|
/** Each roster field's counts, keyed by field name; empty when the schema declares none. */
|
|
13
13
|
export declare function loadRosterCounts(schema: Record<string, unknown> | null | undefined, ctx: ShowIfContext): Promise<Record<string, RosterCounts>>;
|
|
14
|
+
/** One roster's entries and counts, read the way the widget reads them. */
|
|
15
|
+
export declare function loadRoster(def: RosterDef, ctx: ShowIfContext): Promise<RosterResult>;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.loadRosterCounts = loadRosterCounts;
|
|
4
|
+
exports.loadRoster = loadRoster;
|
|
4
5
|
/**
|
|
5
6
|
* Server-side roster counts: the API-first counterpart of the dashboard's
|
|
6
7
|
* roster widget. For each `x-lt-roster` field of a form schema, the member
|
|
@@ -18,10 +19,14 @@ async function loadRosterCounts(schema, ctx) {
|
|
|
18
19
|
const defs = (0, form_validation_1.readRosterDefs)(schema);
|
|
19
20
|
const counts = {};
|
|
20
21
|
for (const [field, def] of Object.entries(defs)) {
|
|
21
|
-
counts[field] = (
|
|
22
|
+
counts[field] = (await loadRoster(def, ctx)).counts;
|
|
22
23
|
}
|
|
23
24
|
return counts;
|
|
24
25
|
}
|
|
26
|
+
/** One roster's entries and counts, read the way the widget reads them. */
|
|
27
|
+
async function loadRoster(def, ctx) {
|
|
28
|
+
return (0, form_validation_1.computeRoster)(def, ctx, await readMemberRows(def, ctx));
|
|
29
|
+
}
|
|
25
30
|
async function readMemberRows(def, ctx) {
|
|
26
31
|
const query = (0, form_validation_1.rosterQueryOf)(def);
|
|
27
32
|
const scoped = query ? (0, form_validation_1.resolveRosterQuery)(query, ctx) : null;
|
|
@@ -10,6 +10,10 @@ function isSchemeList(value) {
|
|
|
10
10
|
function isStringList(value) {
|
|
11
11
|
return Array.isArray(value) && value.length > 0 && value.every((v) => typeof v === 'string' && v);
|
|
12
12
|
}
|
|
13
|
+
/** A form field name, as `hold.show` names one. */
|
|
14
|
+
const HOLD_SHOW_FIELD = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
15
|
+
/** A fill's label map lives on the batch row: `envelope.<path>` or `metadata.<path>`. */
|
|
16
|
+
const FILL_LABELS_PATH = /^(envelope|metadata)\.[A-Za-z0-9_.-]+$/;
|
|
13
17
|
function isPlainObject(value) {
|
|
14
18
|
return !!value && typeof value === 'object' && !Array.isArray(value);
|
|
15
19
|
}
|
|
@@ -165,6 +169,9 @@ function assertValidHold(step, at) {
|
|
|
165
169
|
throw new Error(`${at}: params.hold.expect.prompt must be markdown`);
|
|
166
170
|
}
|
|
167
171
|
}
|
|
172
|
+
if (hold.show !== undefined && (typeof hold.show !== 'string' || !HOLD_SHOW_FIELD.test(hold.show))) {
|
|
173
|
+
throw new Error(`${at}: params.hold.show must name a roster field of the held row's form`);
|
|
174
|
+
}
|
|
168
175
|
}
|
|
169
176
|
function assertValidFill(step, at) {
|
|
170
177
|
const fill = step.params?.fill;
|
|
@@ -180,6 +187,15 @@ function assertValidFill(step, at) {
|
|
|
180
187
|
if (fill.payload !== undefined && !isPlainObject(fill.payload)) {
|
|
181
188
|
throw new Error(`${at}: params.fill.payload must be an object`);
|
|
182
189
|
}
|
|
190
|
+
if (fill.labels !== undefined && (typeof fill.labels !== 'string' || !FILL_LABELS_PATH.test(fill.labels))) {
|
|
191
|
+
throw new Error(`${at}: params.fill.labels must name a map on the row, e.g. "envelope.labels"`);
|
|
192
|
+
}
|
|
193
|
+
if (fill.label !== undefined && (typeof fill.label !== 'string' || !fill.label.trim())) {
|
|
194
|
+
throw new Error(`${at}: params.fill.label must be a non-empty template`);
|
|
195
|
+
}
|
|
196
|
+
if (fill.label !== undefined && fill.labels === undefined) {
|
|
197
|
+
throw new Error(`${at}: params.fill.label requires params.fill.labels`);
|
|
198
|
+
}
|
|
183
199
|
if (step.confirm)
|
|
184
200
|
throw new Error(`${at}: fill cannot carry confirm`);
|
|
185
201
|
}
|
|
@@ -127,6 +127,13 @@ function assertValidChoices(choices, at) {
|
|
|
127
127
|
|| choice.verb === types_1.SCAN_VERBS.FILL) {
|
|
128
128
|
throw new Error(`${cat}: ${choice.verb} cannot be a choice verb`);
|
|
129
129
|
}
|
|
130
|
+
if (choice.roles !== undefined && (!Array.isArray(choice.roles) || choice.roles.length === 0
|
|
131
|
+
|| choice.roles.some((r) => typeof r !== 'string' || !r))) {
|
|
132
|
+
throw new Error(`${cat}: roles must be a non-empty array of role names`);
|
|
133
|
+
}
|
|
134
|
+
if (choice.refuse !== undefined && (typeof choice.refuse !== 'string' || !choice.refuse.trim())) {
|
|
135
|
+
throw new Error(`${cat}: refuse must be non-empty text`);
|
|
136
|
+
}
|
|
130
137
|
if (choice.confirm && !choice.confirm.prompt) {
|
|
131
138
|
throw new Error(`${cat}: confirm requires a prompt`);
|
|
132
139
|
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* x-lt-actions on a roster field: the acts an entry offers, read the same way
|
|
3
|
+
* by the dashboard (which shows them) and the API (which runs them). Each
|
|
4
|
+
* action is exactly one act:
|
|
5
|
+
*
|
|
6
|
+
* { "label": "Missing", "resolverPayload": { "outcome": "missing" } } resolve the member row
|
|
7
|
+
* { "label": "Key in", "fill": { "payload": { "source": "key-in" }, "enterCode": true } }
|
|
8
|
+
* { "label": "Missing", "flag": { "label": "Missing" } } flag the batch key
|
|
9
|
+
* { "label": "Found it", "unflag": true } clear the flag
|
|
10
|
+
*
|
|
11
|
+
* `when` names the entry states that offer it. `roles` limits who may run it:
|
|
12
|
+
* the action shows to everyone and runs only for a person holding one of the
|
|
13
|
+
* roles; `refuse` is what anyone else is told. The server re-reads the action
|
|
14
|
+
* from the row's form schema and decides; the dashboard only displays.
|
|
15
|
+
*/
|
|
16
|
+
import { type HelpTokenContext } from './x-lt-help';
|
|
17
|
+
import { type RosterEntry, type RosterState } from './x-lt-roster';
|
|
18
|
+
export declare const X_LT_ACTIONS = "x-lt-actions";
|
|
19
|
+
export declare const ROSTER_ACTION_KINDS: {
|
|
20
|
+
readonly RESOLVE: "resolve";
|
|
21
|
+
readonly FILL: "fill";
|
|
22
|
+
readonly FLAG: "flag";
|
|
23
|
+
readonly UNFLAG: "unflag";
|
|
24
|
+
};
|
|
25
|
+
export type RosterActionKind = (typeof ROSTER_ACTION_KINDS)[keyof typeof ROSTER_ACTION_KINDS];
|
|
26
|
+
/** The separator of counted batch keys when the roster names none. */
|
|
27
|
+
export declare const ROSTER_KEY_SEPARATOR_DEFAULT = "#";
|
|
28
|
+
/** The payload key a key-in records the entered code under. */
|
|
29
|
+
export declare const ENTERED_CODE_KEY = "enteredCode";
|
|
30
|
+
export interface RosterActionDef {
|
|
31
|
+
label: string;
|
|
32
|
+
/** The entry states that offer it; the default follows the act. */
|
|
33
|
+
when?: RosterState | RosterState[];
|
|
34
|
+
/** A confirmation asked in place before firing; tokens interpolate per entry. */
|
|
35
|
+
confirm?: string;
|
|
36
|
+
/** Resolve the entry's member row with this canned payload (a query set). */
|
|
37
|
+
resolverPayload?: Record<string, unknown>;
|
|
38
|
+
/** Fill the entry's batch key; `enterCode` asks for the code and checks it against the key. */
|
|
39
|
+
fill?: {
|
|
40
|
+
payload?: Record<string, unknown>;
|
|
41
|
+
enterCode?: boolean;
|
|
42
|
+
};
|
|
43
|
+
/** Flag the entry's batch key. */
|
|
44
|
+
flag?: {
|
|
45
|
+
label: string;
|
|
46
|
+
payload?: Record<string, unknown>;
|
|
47
|
+
};
|
|
48
|
+
/** Clear the entry's flag. */
|
|
49
|
+
unflag?: true;
|
|
50
|
+
/** Runs only for a person holding one of these roles. */
|
|
51
|
+
roles?: string[];
|
|
52
|
+
/** What a person without the roles is told. */
|
|
53
|
+
refuse?: string;
|
|
54
|
+
}
|
|
55
|
+
/** The one act an action declares, or null when it declares none or several. */
|
|
56
|
+
export declare function rosterActionKind(action: RosterActionDef): RosterActionKind | null;
|
|
57
|
+
/** The field's actions with a label and exactly one act, in declared order. */
|
|
58
|
+
export declare function readRosterActions(fieldSchema: Record<string, unknown> | null | undefined): RosterActionDef[];
|
|
59
|
+
/**
|
|
60
|
+
* True when the entry offers the action: the entry is in one of its states,
|
|
61
|
+
* and the act fits the entry (a resolve needs a member row; batch acts need
|
|
62
|
+
* a batch set).
|
|
63
|
+
*/
|
|
64
|
+
export declare function offersAction(action: RosterActionDef, entry: RosterEntry, batch: boolean): boolean;
|
|
65
|
+
/** True when a person holding `roles` may run the action. */
|
|
66
|
+
export declare function holdsActionRole(action: Pick<RosterActionDef, 'roles'>, roles: string[], superadmin?: boolean): boolean;
|
|
67
|
+
/** What a person without the action's roles is told. */
|
|
68
|
+
export declare function actionRefusal(action: Pick<RosterActionDef, 'label' | 'roles' | 'refuse'>): string;
|
|
69
|
+
/** Deep-walk a payload template: string leaves interpolate against the context; other types pass through typed. */
|
|
70
|
+
export declare function interpolatePayload(value: unknown, ctx: HelpTokenContext): unknown;
|
|
71
|
+
/** The code a batch key names, without its ordinal suffix. */
|
|
72
|
+
export declare function batchKeyCode(key: string, separator?: string): string;
|
|
73
|
+
/** The batch key's code as its label prints it: a GTIN in its EAN-13 or EAN-8 form. */
|
|
74
|
+
export declare function printedKeyCode(key: string, separator?: string): string;
|
|
75
|
+
/**
|
|
76
|
+
* True when an entered code names the batch key's code. GTINs compare in
|
|
77
|
+
* their 14-digit form, so a UPC-A typed from the label matches its GTIN-14
|
|
78
|
+
* key; anything else compares as trimmed text.
|
|
79
|
+
*/
|
|
80
|
+
export declare function enteredCodeMatches(entered: string, key: string, separator?: string): boolean;
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ENTERED_CODE_KEY = exports.ROSTER_KEY_SEPARATOR_DEFAULT = exports.ROSTER_ACTION_KINDS = exports.X_LT_ACTIONS = void 0;
|
|
4
|
+
exports.rosterActionKind = rosterActionKind;
|
|
5
|
+
exports.readRosterActions = readRosterActions;
|
|
6
|
+
exports.offersAction = offersAction;
|
|
7
|
+
exports.holdsActionRole = holdsActionRole;
|
|
8
|
+
exports.actionRefusal = actionRefusal;
|
|
9
|
+
exports.interpolatePayload = interpolatePayload;
|
|
10
|
+
exports.batchKeyCode = batchKeyCode;
|
|
11
|
+
exports.printedKeyCode = printedKeyCode;
|
|
12
|
+
exports.enteredCodeMatches = enteredCodeMatches;
|
|
13
|
+
/**
|
|
14
|
+
* x-lt-actions on a roster field: the acts an entry offers, read the same way
|
|
15
|
+
* by the dashboard (which shows them) and the API (which runs them). Each
|
|
16
|
+
* action is exactly one act:
|
|
17
|
+
*
|
|
18
|
+
* { "label": "Missing", "resolverPayload": { "outcome": "missing" } } resolve the member row
|
|
19
|
+
* { "label": "Key in", "fill": { "payload": { "source": "key-in" }, "enterCode": true } }
|
|
20
|
+
* { "label": "Missing", "flag": { "label": "Missing" } } flag the batch key
|
|
21
|
+
* { "label": "Found it", "unflag": true } clear the flag
|
|
22
|
+
*
|
|
23
|
+
* `when` names the entry states that offer it. `roles` limits who may run it:
|
|
24
|
+
* the action shows to everyone and runs only for a person holding one of the
|
|
25
|
+
* roles; `refuse` is what anyone else is told. The server re-reads the action
|
|
26
|
+
* from the row's form schema and decides; the dashboard only displays.
|
|
27
|
+
*/
|
|
28
|
+
const x_lt_help_1 = require("./x-lt-help");
|
|
29
|
+
const x_lt_roster_1 = require("./x-lt-roster");
|
|
30
|
+
const gtin_1 = require("../scan-code/gtin");
|
|
31
|
+
exports.X_LT_ACTIONS = 'x-lt-actions';
|
|
32
|
+
exports.ROSTER_ACTION_KINDS = {
|
|
33
|
+
RESOLVE: 'resolve',
|
|
34
|
+
FILL: 'fill',
|
|
35
|
+
FLAG: 'flag',
|
|
36
|
+
UNFLAG: 'unflag',
|
|
37
|
+
};
|
|
38
|
+
/** The separator of counted batch keys when the roster names none. */
|
|
39
|
+
exports.ROSTER_KEY_SEPARATOR_DEFAULT = '#';
|
|
40
|
+
/** The payload key a key-in records the entered code under. */
|
|
41
|
+
exports.ENTERED_CODE_KEY = 'enteredCode';
|
|
42
|
+
const DEFAULT_WHEN = {
|
|
43
|
+
resolve: [x_lt_roster_1.ROSTER_STATES.WAITING],
|
|
44
|
+
fill: [x_lt_roster_1.ROSTER_STATES.WAITING, x_lt_roster_1.ROSTER_STATES.FLAGGED],
|
|
45
|
+
flag: [x_lt_roster_1.ROSTER_STATES.WAITING],
|
|
46
|
+
unflag: [x_lt_roster_1.ROSTER_STATES.FLAGGED],
|
|
47
|
+
};
|
|
48
|
+
function isRecord(v) {
|
|
49
|
+
return !!v && typeof v === 'object' && !Array.isArray(v);
|
|
50
|
+
}
|
|
51
|
+
/** The one act an action declares, or null when it declares none or several. */
|
|
52
|
+
function rosterActionKind(action) {
|
|
53
|
+
const kinds = [];
|
|
54
|
+
if (isRecord(action.resolverPayload))
|
|
55
|
+
kinds.push(exports.ROSTER_ACTION_KINDS.RESOLVE);
|
|
56
|
+
if (isRecord(action.fill))
|
|
57
|
+
kinds.push(exports.ROSTER_ACTION_KINDS.FILL);
|
|
58
|
+
if (isRecord(action.flag) && typeof action.flag.label === 'string' && action.flag.label) {
|
|
59
|
+
kinds.push(exports.ROSTER_ACTION_KINDS.FLAG);
|
|
60
|
+
}
|
|
61
|
+
if (action.unflag === true)
|
|
62
|
+
kinds.push(exports.ROSTER_ACTION_KINDS.UNFLAG);
|
|
63
|
+
return kinds.length === 1 ? kinds[0] : null;
|
|
64
|
+
}
|
|
65
|
+
/** The field's actions with a label and exactly one act, in declared order. */
|
|
66
|
+
function readRosterActions(fieldSchema) {
|
|
67
|
+
const raw = fieldSchema?.[exports.X_LT_ACTIONS];
|
|
68
|
+
if (!Array.isArray(raw))
|
|
69
|
+
return [];
|
|
70
|
+
return raw.filter((a) => isRecord(a) && typeof a.label === 'string' && !!a.label && rosterActionKind(a) !== null);
|
|
71
|
+
}
|
|
72
|
+
/**
|
|
73
|
+
* True when the entry offers the action: the entry is in one of its states,
|
|
74
|
+
* and the act fits the entry (a resolve needs a member row; batch acts need
|
|
75
|
+
* a batch set).
|
|
76
|
+
*/
|
|
77
|
+
function offersAction(action, entry, batch) {
|
|
78
|
+
const kind = rosterActionKind(action);
|
|
79
|
+
if (!kind)
|
|
80
|
+
return false;
|
|
81
|
+
if (kind === exports.ROSTER_ACTION_KINDS.RESOLVE ? !entry.row : !batch)
|
|
82
|
+
return false;
|
|
83
|
+
const when = action.when === undefined ? DEFAULT_WHEN[kind]
|
|
84
|
+
: Array.isArray(action.when) ? action.when : [action.when];
|
|
85
|
+
return when.includes(entry.state);
|
|
86
|
+
}
|
|
87
|
+
/** True when a person holding `roles` may run the action. */
|
|
88
|
+
function holdsActionRole(action, roles, superadmin = false) {
|
|
89
|
+
const required = Array.isArray(action.roles) ? action.roles.filter((r) => typeof r === 'string' && r) : [];
|
|
90
|
+
return required.length === 0 || superadmin || required.some((r) => roles.includes(r));
|
|
91
|
+
}
|
|
92
|
+
/** What a person without the action's roles is told. */
|
|
93
|
+
function actionRefusal(action) {
|
|
94
|
+
if (typeof action.refuse === 'string' && action.refuse.trim())
|
|
95
|
+
return action.refuse;
|
|
96
|
+
const roles = (action.roles ?? []).filter(Boolean);
|
|
97
|
+
return `${action.label} needs ${roles.length > 1 ? 'one of these roles' : 'the role'}: ${roles.join(', ')}.`;
|
|
98
|
+
}
|
|
99
|
+
/** Deep-walk a payload template: string leaves interpolate against the context; other types pass through typed. */
|
|
100
|
+
function interpolatePayload(value, ctx) {
|
|
101
|
+
if (typeof value === 'string')
|
|
102
|
+
return (0, x_lt_help_1.interpolateHelp)(value, ctx);
|
|
103
|
+
if (Array.isArray(value))
|
|
104
|
+
return value.map((v) => interpolatePayload(v, ctx));
|
|
105
|
+
if (isRecord(value)) {
|
|
106
|
+
return Object.fromEntries(Object.entries(value).map(([k, v]) => [k, interpolatePayload(v, ctx)]));
|
|
107
|
+
}
|
|
108
|
+
return value;
|
|
109
|
+
}
|
|
110
|
+
/** The code a batch key names, without its ordinal suffix. */
|
|
111
|
+
function batchKeyCode(key, separator = exports.ROSTER_KEY_SEPARATOR_DEFAULT) {
|
|
112
|
+
const at = key.lastIndexOf(separator);
|
|
113
|
+
return at > 0 && /^[0-9]+$/.test(key.slice(at + 1)) ? key.slice(0, at) : key;
|
|
114
|
+
}
|
|
115
|
+
/** The batch key's code as its label prints it: a GTIN in its EAN-13 or EAN-8 form. */
|
|
116
|
+
function printedKeyCode(key, separator) {
|
|
117
|
+
const code = batchKeyCode(key, separator);
|
|
118
|
+
return (0, gtin_1.isValidGtin)(code) ? (0, gtin_1.displayGtin)((0, gtin_1.normalizeGtin)(code)) : code;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* True when an entered code names the batch key's code. GTINs compare in
|
|
122
|
+
* their 14-digit form, so a UPC-A typed from the label matches its GTIN-14
|
|
123
|
+
* key; anything else compares as trimmed text.
|
|
124
|
+
*/
|
|
125
|
+
function enteredCodeMatches(entered, key, separator) {
|
|
126
|
+
const typed = entered.trim();
|
|
127
|
+
const code = batchKeyCode(key, separator);
|
|
128
|
+
if (!typed)
|
|
129
|
+
return false;
|
|
130
|
+
if ((0, gtin_1.isValidGtin)(typed) && (0, gtin_1.isValidGtin)(code))
|
|
131
|
+
return (0, gtin_1.normalizeGtin)(typed) === (0, gtin_1.normalizeGtin)(code);
|
|
132
|
+
return typed === code;
|
|
133
|
+
}
|
|
@@ -40,6 +40,8 @@ export interface RosterDef {
|
|
|
40
40
|
key?: string;
|
|
41
41
|
in?: string[];
|
|
42
42
|
flagged?: RosterFlagRule[];
|
|
43
|
+
/** The separator of counted keys: the nth occurrence of a code ticks `<code><sep>n`. */
|
|
44
|
+
countedBy?: string;
|
|
43
45
|
}
|
|
44
46
|
/** One member row's token context (the row's own domains). */
|
|
45
47
|
export type RosterRow = HelpTokenContext & {
|
|
@@ -28,9 +28,14 @@ exports.rosterHeadline = rosterHeadline;
|
|
|
28
28
|
* An entry is `in` when its key is held by the host row (`items`: the
|
|
29
29
|
* accumulated keys, or a batch's filled keys) or appears in a named context
|
|
30
30
|
* value (`resolver.<field>`, a string or an array). It is `flagged` when its
|
|
31
|
-
* member row matches a `flagged` rule,
|
|
31
|
+
* member row matches a `flagged` rule (or, on a batch set, when its key
|
|
32
|
+
* carries a flag in `envelope.batch_flags`), and `waiting` otherwise. In wins over
|
|
32
33
|
* flagged, so a member flagged on its own row and then added reads in.
|
|
33
34
|
*
|
|
35
|
+
* `countedBy` names the separator of counted keys (`<code>#1`, `<code>#2`):
|
|
36
|
+
* the nth occurrence of a code in a named context value ticks `<code>#n`,
|
|
37
|
+
* so two scans of one code tick both of its keys.
|
|
38
|
+
*
|
|
34
39
|
* Isomorphic: the dashboard renders the widget and the form's `roster.*`
|
|
35
40
|
* context from it, and the API layer computes the same counts to evaluate
|
|
36
41
|
* showIf and the roster submit guard at resolve.
|
|
@@ -54,6 +59,7 @@ const DEFAULT_KEY = '{{escalation.id}}';
|
|
|
54
59
|
const MISSING = '—';
|
|
55
60
|
const BATCH_KEYS = 'batch_keys';
|
|
56
61
|
const BATCH_PENDING = 'batch_pending';
|
|
62
|
+
const BATCH_FLAGS = 'batch_flags';
|
|
57
63
|
const ACCUMULATE_KEYS = 'accumulate_keys';
|
|
58
64
|
function isRecord(v) {
|
|
59
65
|
return !!v && typeof v === 'object' && !Array.isArray(v);
|
|
@@ -144,16 +150,34 @@ function heldKeys(ctx) {
|
|
|
144
150
|
}
|
|
145
151
|
return new Set();
|
|
146
152
|
}
|
|
153
|
+
/** Each value as given, and with `countedBy` its nth occurrence also as `<value><sep>n`. */
|
|
154
|
+
function countedKeys(values, separator) {
|
|
155
|
+
if (!separator)
|
|
156
|
+
return values;
|
|
157
|
+
const seen = new Map();
|
|
158
|
+
return values.flatMap((v) => {
|
|
159
|
+
const n = (seen.get(v) ?? 0) + 1;
|
|
160
|
+
seen.set(v, n);
|
|
161
|
+
return [v, `${v}${separator}${n}`];
|
|
162
|
+
});
|
|
163
|
+
}
|
|
147
164
|
function arrivedKeys(def, ctx) {
|
|
148
165
|
const arrived = new Set();
|
|
166
|
+
const separator = typeof def.countedBy === 'string' && def.countedBy ? def.countedBy : undefined;
|
|
149
167
|
for (const source of def.in ?? [exports.ROSTER_ITEMS_SOURCE]) {
|
|
150
168
|
const keys = source === exports.ROSTER_ITEMS_SOURCE
|
|
151
169
|
? [...heldKeys(ctx)]
|
|
152
|
-
: strings((0, ctx_path_1.resolveCtxPath)(source, ctx));
|
|
170
|
+
: countedKeys(strings((0, ctx_path_1.resolveCtxPath)(source, ctx)), separator);
|
|
153
171
|
keys.forEach((k) => arrived.add(k));
|
|
154
172
|
}
|
|
155
173
|
return arrived;
|
|
156
174
|
}
|
|
175
|
+
/** A batch key's flag label, from `envelope.batch_flags[key].label`. */
|
|
176
|
+
function batchFlagLabel(ctx, key) {
|
|
177
|
+
const flags = (0, x_lt_bind_1.getDeep)(ctx.envelope, BATCH_FLAGS);
|
|
178
|
+
const flag = isRecord(flags) ? flags[key] : undefined;
|
|
179
|
+
return isRecord(flag) && typeof flag.label === 'string' && flag.label ? flag.label : undefined;
|
|
180
|
+
}
|
|
157
181
|
function flagFor(def, row) {
|
|
158
182
|
if (!row)
|
|
159
183
|
return undefined;
|
|
@@ -219,10 +243,11 @@ function expectedEntries(def, ctx, rows) {
|
|
|
219
243
|
*/
|
|
220
244
|
function computeRoster(def, ctx, rows = []) {
|
|
221
245
|
const arrived = arrivedKeys(def, ctx);
|
|
246
|
+
const batch = 'batch' in def.expected;
|
|
222
247
|
const entries = expectedEntries(def, ctx, rows).map((e) => {
|
|
223
248
|
if (arrived.has(e.key))
|
|
224
249
|
return { ...e, state: exports.ROSTER_STATES.IN };
|
|
225
|
-
const flag = flagFor(def, e.row);
|
|
250
|
+
const flag = batch ? batchFlagLabel(ctx, e.key) : flagFor(def, e.row);
|
|
226
251
|
return flag ? { ...e, state: exports.ROSTER_STATES.FLAGGED, flag } : { ...e, state: exports.ROSTER_STATES.WAITING };
|
|
227
252
|
});
|
|
228
253
|
const count = (s) => entries.filter((e) => e.state === s).length;
|