@hotmeshio/long-tail 0.22.1 → 0.22.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.
Files changed (291) hide show
  1. package/build/api/escalations/accumulate.js +38 -19
  2. package/build/api/escalations/resolve-batch.js +18 -11
  3. package/build/api/roles.js +4 -0
  4. package/build/api/scan-codes/badge-policy.d.ts +10 -0
  5. package/build/api/scan-codes/badge-policy.js +72 -0
  6. package/build/api/scan-codes/choice.js +16 -10
  7. package/build/api/scan-codes/context.d.ts +45 -1
  8. package/build/api/scan-codes/context.js +51 -3
  9. package/build/api/scan-codes/execute.js +73 -35
  10. package/build/api/scan-codes/grant.d.ts +27 -0
  11. package/build/api/scan-codes/grant.js +67 -0
  12. package/build/api/scan-codes/hold.d.ts +10 -0
  13. package/build/api/scan-codes/hold.js +98 -0
  14. package/build/api/scan-codes/identity.d.ts +5 -2
  15. package/build/api/scan-codes/identity.js +26 -8
  16. package/build/api/scan-codes/subject.d.ts +15 -0
  17. package/build/api/scan-codes/subject.js +68 -0
  18. package/build/api/scan-codes/verb-accumulate.d.ts +24 -0
  19. package/build/api/scan-codes/verb-accumulate.js +147 -0
  20. package/build/api/scan-codes/verb-fill.d.ts +6 -0
  21. package/build/api/scan-codes/verb-fill.js +152 -0
  22. package/build/api/scan-codes/verbs-subject.d.ts +14 -0
  23. package/build/api/scan-codes/verbs-subject.js +270 -0
  24. package/build/api/scan-codes/verbs.d.ts +1 -21
  25. package/build/api/scan-codes/verbs.js +26 -115
  26. package/build/index.d.ts +1 -0
  27. package/build/index.js +8 -2
  28. package/build/lib/db/schemas/037_scan_gtin_and_subject_grants.sql +27 -0
  29. package/build/routes/escalations/acting.d.ts +5 -0
  30. package/build/routes/escalations/acting.js +8 -1
  31. package/build/routes/scan-codes.js +3 -0
  32. package/build/services/iam/acting-identity.d.ts +29 -4
  33. package/build/services/iam/acting-identity.js +60 -17
  34. package/build/services/iam/ephemeral.d.ts +21 -0
  35. package/build/services/iam/ephemeral.js +32 -1
  36. package/build/services/iam/sql.d.ts +10 -0
  37. package/build/services/iam/sql.js +32 -3
  38. package/build/services/role/badge-grant.d.ts +5 -0
  39. package/build/services/role/badge-grant.js +36 -0
  40. package/build/services/role/seed.js +5 -0
  41. package/build/services/role/types.d.ts +14 -0
  42. package/build/services/role/types.js +5 -0
  43. package/build/services/scan-code/index.d.ts +3 -2
  44. package/build/services/scan-code/index.js +7 -1
  45. package/build/services/scan-code/parse.d.ts +23 -20
  46. package/build/services/scan-code/parse.js +45 -90
  47. package/build/services/scan-code/read.js +1 -0
  48. package/build/services/scan-code/sql.d.ts +3 -3
  49. package/build/services/scan-code/sql.js +11 -8
  50. package/build/services/scan-code/validate-subject.d.ts +7 -0
  51. package/build/services/scan-code/validate-subject.js +158 -0
  52. package/build/services/scan-code/validate.d.ts +7 -0
  53. package/build/services/scan-code/validate.js +94 -18
  54. package/build/services/scan-code/write.d.ts +1 -0
  55. package/build/services/scan-code/write.js +9 -3
  56. package/build/shared/form-validation/field-validator.js +4 -0
  57. package/build/shared/form-validation/index.d.ts +1 -0
  58. package/build/shared/form-validation/index.js +1 -0
  59. package/build/shared/form-validation/x-lt-scan.d.ts +52 -0
  60. package/build/shared/form-validation/x-lt-scan.js +166 -0
  61. package/build/shared/scan-code/gtin.d.ts +5 -0
  62. package/build/shared/scan-code/gtin.js +25 -0
  63. package/build/shared/scan-code/index.d.ts +2 -0
  64. package/build/shared/scan-code/index.js +10 -0
  65. package/build/shared/scan-code/parse.d.ts +46 -0
  66. package/build/shared/scan-code/parse.js +133 -0
  67. package/build/system/mcp-servers/admin/scan-codes.js +10 -4
  68. package/build/system/mcp-servers/admin/schemas.d.ts +754 -23
  69. package/build/system/mcp-servers/admin/schemas.js +57 -4
  70. package/build/system/mcp-servers/human-queue-schemas.d.ts +6 -6
  71. package/build/tsconfig.tsbuildinfo +1 -1
  72. package/build/types/escalation.d.ts +2 -0
  73. package/build/types/escalation.js +3 -1
  74. package/build/types/index.d.ts +2 -2
  75. package/build/types/index.js +8 -1
  76. package/build/types/scan-code/constants.d.ts +182 -0
  77. package/build/types/{scan-code.js → scan-code/constants.js} +72 -11
  78. package/build/types/scan-code/index.d.ts +3 -0
  79. package/build/types/scan-code/index.js +17 -0
  80. package/build/types/scan-code/response.d.ts +140 -0
  81. package/build/types/scan-code/response.js +2 -0
  82. package/build/types/scan-code/rule.d.ts +199 -0
  83. package/build/types/scan-code/rule.js +2 -0
  84. package/build/types/startup.d.ts +3 -1
  85. package/dashboard/dist/assets/{AdminDashboard-BbWve-Ax.js → AdminDashboard-DBW12qQt.js} +2 -2
  86. package/dashboard/dist/assets/{AdminDashboard-BbWve-Ax.js.map → AdminDashboard-DBW12qQt.js.map} +1 -1
  87. package/dashboard/dist/assets/{AgentConfigPage-M-9eTKhP.js → AgentConfigPage-CmHv8U9M.js} +2 -2
  88. package/dashboard/dist/assets/{AgentConfigPage-M-9eTKhP.js.map → AgentConfigPage-CmHv8U9M.js.map} +1 -1
  89. package/dashboard/dist/assets/{AgentDetailPage-CsakSlx5.js → AgentDetailPage-D1scIbz-.js} +3 -3
  90. package/dashboard/dist/assets/{AgentDetailPage-CsakSlx5.js.map → AgentDetailPage-D1scIbz-.js.map} +1 -1
  91. package/dashboard/dist/assets/{AgentsPage-Cj_cRTT1.js → AgentsPage-BA_ai9HJ.js} +2 -2
  92. package/dashboard/dist/assets/{AgentsPage-Cj_cRTT1.js.map → AgentsPage-BA_ai9HJ.js.map} +1 -1
  93. package/dashboard/dist/assets/{AvailableEscalationsPage-BXJmMBM1.js → AvailableEscalationsPage-C2_5aEFW.js} +2 -2
  94. package/dashboard/dist/assets/{AvailableEscalationsPage-BXJmMBM1.js.map → AvailableEscalationsPage-C2_5aEFW.js.map} +1 -1
  95. package/dashboard/dist/assets/{BotPicker-CGB16rYU.js → BotPicker-BZo4Kp4a.js} +2 -2
  96. package/dashboard/dist/assets/{BotPicker-CGB16rYU.js.map → BotPicker-BZo4Kp4a.js.map} +1 -1
  97. package/dashboard/dist/assets/{BulkAssignModal-MjxcU7N1.js → BulkAssignModal-BFhyxY2T.js} +2 -2
  98. package/dashboard/dist/assets/{BulkAssignModal-MjxcU7N1.js.map → BulkAssignModal-BFhyxY2T.js.map} +1 -1
  99. package/dashboard/dist/assets/CapabilitiesPage-CvOX6vAc.js +2 -0
  100. package/dashboard/dist/assets/{CapabilitiesPage-BxadotXn.js.map → CapabilitiesPage-CvOX6vAc.js.map} +1 -1
  101. package/dashboard/dist/assets/{CollapsibleSection-BBNpP-6h.js → CollapsibleSection-GCJsV9Oq.js} +2 -2
  102. package/dashboard/dist/assets/{CollapsibleSection-BBNpP-6h.js.map → CollapsibleSection-GCJsV9Oq.js.map} +1 -1
  103. package/dashboard/dist/assets/{ConfirmDeleteModal-D_kAbT_D.js → ConfirmDeleteModal-CAgYLcef.js} +2 -2
  104. package/dashboard/dist/assets/{ConfirmDeleteModal-D_kAbT_D.js.map → ConfirmDeleteModal-CAgYLcef.js.map} +1 -1
  105. package/dashboard/dist/assets/CountdownTimer-Dik2z0M_.js +2 -0
  106. package/dashboard/dist/assets/{CountdownTimer-CI_JOAvk.js.map → CountdownTimer-Dik2z0M_.js.map} +1 -1
  107. package/dashboard/dist/assets/{CredentialsPage-C9OA1Q2J.js → CredentialsPage-twd2xCOW.js} +2 -2
  108. package/dashboard/dist/assets/{CredentialsPage-C9OA1Q2J.js.map → CredentialsPage-twd2xCOW.js.map} +1 -1
  109. package/dashboard/dist/assets/{CronLabel-sxLLBf2I.js → CronLabel-DdnIuudX.js} +2 -2
  110. package/dashboard/dist/assets/{CronLabel-sxLLBf2I.js.map → CronLabel-DdnIuudX.js.map} +1 -1
  111. package/dashboard/dist/assets/{ElapsedCell-jH9m8Dgl.js → ElapsedCell-DCctVv5R.js} +2 -2
  112. package/dashboard/dist/assets/{ElapsedCell-jH9m8Dgl.js.map → ElapsedCell-DCctVv5R.js.map} +1 -1
  113. package/dashboard/dist/assets/{EntityTimelinePanel-Dl3Nr2kk.js → EntityTimelinePanel-CMnskyWv.js} +2 -2
  114. package/dashboard/dist/assets/{EntityTimelinePanel-Dl3Nr2kk.js.map → EntityTimelinePanel-CMnskyWv.js.map} +1 -1
  115. package/dashboard/dist/assets/{EscalationListSchemaPage-DAK6It5J.js → EscalationListSchemaPage-VfCn6WDb.js} +3 -3
  116. package/dashboard/dist/assets/{EscalationListSchemaPage-DAK6It5J.js.map → EscalationListSchemaPage-VfCn6WDb.js.map} +1 -1
  117. package/dashboard/dist/assets/{EscalationListView-CAHbdxKR.js → EscalationListView-CGQZxcC-.js} +2 -2
  118. package/dashboard/dist/assets/{EscalationListView-CAHbdxKR.js.map → EscalationListView-CGQZxcC-.js.map} +1 -1
  119. package/dashboard/dist/assets/{EscalationSchemaPage-DotfAMCL.js → EscalationSchemaPage-BX0tx-PM.js} +3 -3
  120. package/dashboard/dist/assets/{EscalationSchemaPage-DotfAMCL.js.map → EscalationSchemaPage-BX0tx-PM.js.map} +1 -1
  121. package/dashboard/dist/assets/{EscalationTimeline-DqmFydnU.js → EscalationTimeline-DKoSM56J.js} +2 -2
  122. package/dashboard/dist/assets/{EscalationTimeline-DqmFydnU.js.map → EscalationTimeline-DKoSM56J.js.map} +1 -1
  123. package/dashboard/dist/assets/{EscalationTitleSelect-BpRquguO.js → EscalationTitleSelect-DWGLBOmS.js} +2 -2
  124. package/dashboard/dist/assets/{EscalationTitleSelect-BpRquguO.js.map → EscalationTitleSelect-DWGLBOmS.js.map} +1 -1
  125. package/dashboard/dist/assets/{EscalationsOverview-DtGOqnP4.js → EscalationsOverview-C2XoguaA.js} +2 -2
  126. package/dashboard/dist/assets/{EscalationsOverview-DtGOqnP4.js.map → EscalationsOverview-C2XoguaA.js.map} +1 -1
  127. package/dashboard/dist/assets/{EventTable-DSHqKyX2.js → EventTable-CBUWq7--.js} +2 -2
  128. package/dashboard/dist/assets/{EventTable-DSHqKyX2.js.map → EventTable-CBUWq7--.js.map} +1 -1
  129. package/dashboard/dist/assets/{FilterBar-Dxd0wNSW.js → FilterBar-8IdbleZ9.js} +2 -2
  130. package/dashboard/dist/assets/{FilterBar-Dxd0wNSW.js.map → FilterBar-8IdbleZ9.js.map} +1 -1
  131. package/dashboard/dist/assets/GrantScopeField-BzWGzBfA.js +2 -0
  132. package/dashboard/dist/assets/GrantScopeField-BzWGzBfA.js.map +1 -0
  133. package/dashboard/dist/assets/{GraphInvokePage-ChqqGkro.js → GraphInvokePage-CUvoPzsW.js} +2 -2
  134. package/dashboard/dist/assets/{GraphInvokePage-ChqqGkro.js.map → GraphInvokePage-CUvoPzsW.js.map} +1 -1
  135. package/dashboard/dist/assets/{HomePage-ByKesG8w.js → HomePage-BP7v9GLJ.js} +2 -2
  136. package/dashboard/dist/assets/{HomePage-ByKesG8w.js.map → HomePage-BP7v9GLJ.js.map} +1 -1
  137. package/dashboard/dist/assets/{ListToolbar-Ddz_S2av.js → ListToolbar-Cnacuwoy.js} +2 -2
  138. package/dashboard/dist/assets/{ListToolbar-Ddz_S2av.js.map → ListToolbar-Cnacuwoy.js.map} +1 -1
  139. package/dashboard/dist/assets/{McpOverview-CVe13tEv.js → McpOverview-C2JoNoDA.js} +2 -2
  140. package/dashboard/dist/assets/{McpOverview-CVe13tEv.js.map → McpOverview-C2JoNoDA.js.map} +1 -1
  141. package/dashboard/dist/assets/{McpQueryDetailPage-BQYF2Mjq.js → McpQueryDetailPage-C_nih0w-.js} +2 -2
  142. package/dashboard/dist/assets/{McpQueryDetailPage-BQYF2Mjq.js.map → McpQueryDetailPage-C_nih0w-.js.map} +1 -1
  143. package/dashboard/dist/assets/{McpQueryPage-DZi5lm7s.js → McpQueryPage-U_wNXow2.js} +2 -2
  144. package/dashboard/dist/assets/{McpQueryPage-DZi5lm7s.js.map → McpQueryPage-U_wNXow2.js.map} +1 -1
  145. package/dashboard/dist/assets/{McpRunDetailPage-DreARTE1.js → McpRunDetailPage-DtKoJq6J.js} +2 -2
  146. package/dashboard/dist/assets/{McpRunDetailPage-DreARTE1.js.map → McpRunDetailPage-DtKoJq6J.js.map} +1 -1
  147. package/dashboard/dist/assets/{McpRunsPage-DmO0YVhT.js → McpRunsPage-CnYtfGhf.js} +2 -2
  148. package/dashboard/dist/assets/{McpRunsPage-DmO0YVhT.js.map → McpRunsPage-CnYtfGhf.js.map} +1 -1
  149. package/dashboard/dist/assets/{OperationsPage-DpjCO85T.js → OperationsPage-C7hupIer.js} +2 -2
  150. package/dashboard/dist/assets/{OperationsPage-DpjCO85T.js.map → OperationsPage-C7hupIer.js.map} +1 -1
  151. package/dashboard/dist/assets/{OperatorDashboard-Vv_MEBkv.js → OperatorDashboard-fVuLn2D5.js} +2 -2
  152. package/dashboard/dist/assets/{OperatorDashboard-Vv_MEBkv.js.map → OperatorDashboard-fVuLn2D5.js.map} +1 -1
  153. package/dashboard/dist/assets/{PersonaDetailPage-CW-2uBHY.js → PersonaDetailPage-C-4o8lPw.js} +2 -2
  154. package/dashboard/dist/assets/{PersonaDetailPage-CW-2uBHY.js.map → PersonaDetailPage-C-4o8lPw.js.map} +1 -1
  155. package/dashboard/dist/assets/{PersonasPage-CO6yYQdm.js → PersonasPage-BL1FwLmv.js} +2 -2
  156. package/dashboard/dist/assets/{PersonasPage-CO6yYQdm.js.map → PersonasPage-BL1FwLmv.js.map} +1 -1
  157. package/dashboard/dist/assets/{PortalPage-DcNqhBlg.js → PortalPage-tJG5sgb1.js} +2 -2
  158. package/dashboard/dist/assets/{PortalPage-DcNqhBlg.js.map → PortalPage-tJG5sgb1.js.map} +1 -1
  159. package/dashboard/dist/assets/{ProcessDetailPage-BuipGZcC.js → ProcessDetailPage-Dz_jY4Yt.js} +2 -2
  160. package/dashboard/dist/assets/{ProcessDetailPage-BuipGZcC.js.map → ProcessDetailPage-Dz_jY4Yt.js.map} +1 -1
  161. package/dashboard/dist/assets/{ProcessesListPage-D9DYntvl.js → ProcessesListPage-Cbn9cter.js} +2 -2
  162. package/dashboard/dist/assets/{ProcessesListPage-D9DYntvl.js.map → ProcessesListPage-Cbn9cter.js.map} +1 -1
  163. package/dashboard/dist/assets/ResolverForm-Bk8iXQ6-.js +8 -0
  164. package/dashboard/dist/assets/ResolverForm-Bk8iXQ6-.js.map +1 -0
  165. package/dashboard/dist/assets/RoleDetailPage-owPi6gI9.js +8 -0
  166. package/dashboard/dist/assets/RoleDetailPage-owPi6gI9.js.map +1 -0
  167. package/dashboard/dist/assets/{RolesPage-0QJOBSzF.js → RolesPage-NwmMvZFa.js} +2 -2
  168. package/dashboard/dist/assets/{RolesPage-0QJOBSzF.js.map → RolesPage-NwmMvZFa.js.map} +1 -1
  169. package/dashboard/dist/assets/{RunAsSelector-Dh4lyyRo.js → RunAsSelector-tgEooTyU.js} +2 -2
  170. package/dashboard/dist/assets/{RunAsSelector-Dh4lyyRo.js.map → RunAsSelector-tgEooTyU.js.map} +1 -1
  171. package/dashboard/dist/assets/ScanCodesPage-CUv_8ic6.js +2 -0
  172. package/dashboard/dist/assets/ScanCodesPage-CUv_8ic6.js.map +1 -0
  173. package/dashboard/dist/assets/ScanSchemeDetailPage-YHqbu7oK.js +2 -0
  174. package/dashboard/dist/assets/ScanSchemeDetailPage-YHqbu7oK.js.map +1 -0
  175. package/dashboard/dist/assets/ScanStationPage-B275l6r_.js +2 -0
  176. package/dashboard/dist/assets/ScanStationPage-B275l6r_.js.map +1 -0
  177. package/dashboard/dist/assets/{StickyPagination-BSUyf5ja.js → StickyPagination-CsTI_gvJ.js} +2 -2
  178. package/dashboard/dist/assets/{StickyPagination-BSUyf5ja.js.map → StickyPagination-CsTI_gvJ.js.map} +1 -1
  179. package/dashboard/dist/assets/{StreamMessageDetail--tp7l2Sd.js → StreamMessageDetail-auRlvjTh.js} +2 -2
  180. package/dashboard/dist/assets/{StreamMessageDetail--tp7l2Sd.js.map → StreamMessageDetail-auRlvjTh.js.map} +1 -1
  181. package/dashboard/dist/assets/{SwimlaneTimeline-CxBZ2THh.js → SwimlaneTimeline-BLoVHYAd.js} +2 -2
  182. package/dashboard/dist/assets/{SwimlaneTimeline-CxBZ2THh.js.map → SwimlaneTimeline-BLoVHYAd.js.map} +1 -1
  183. package/dashboard/dist/assets/{TaskDetailPage-BJ1QrmPH.js → TaskDetailPage-BPsZ1bK_.js} +2 -2
  184. package/dashboard/dist/assets/{TaskDetailPage-BJ1QrmPH.js.map → TaskDetailPage-BPsZ1bK_.js.map} +1 -1
  185. package/dashboard/dist/assets/{TasksListPage-5BydGWmt.js → TasksListPage-BNGM74yT.js} +2 -2
  186. package/dashboard/dist/assets/{TasksListPage-5BydGWmt.js.map → TasksListPage-BNGM74yT.js.map} +1 -1
  187. package/dashboard/dist/assets/{TimeAgo-CCnz84Y4.js → TimeAgo-BnAHXGK7.js} +2 -2
  188. package/dashboard/dist/assets/{TimeAgo-CCnz84Y4.js.map → TimeAgo-BnAHXGK7.js.map} +1 -1
  189. package/dashboard/dist/assets/{TimestampCell-D0ru7Lya.js → TimestampCell-J4yae6mc.js} +2 -2
  190. package/dashboard/dist/assets/{TimestampCell-D0ru7Lya.js.map → TimestampCell-J4yae6mc.js.map} +1 -1
  191. package/dashboard/dist/assets/{ToolTestPanel-3Kpofmen.js → ToolTestPanel-BdzzgXDu.js} +2 -2
  192. package/dashboard/dist/assets/{ToolTestPanel-3Kpofmen.js.map → ToolTestPanel-BdzzgXDu.js.map} +1 -1
  193. package/dashboard/dist/assets/{TopicDetailPage-BaBXD_AK.js → TopicDetailPage-DJKg8lb7.js} +2 -2
  194. package/dashboard/dist/assets/{TopicDetailPage-BaBXD_AK.js.map → TopicDetailPage-DJKg8lb7.js.map} +1 -1
  195. package/dashboard/dist/assets/{TopicsPage-Cnm1AAjh.js → TopicsPage-DQV0WxG6.js} +2 -2
  196. package/dashboard/dist/assets/{TopicsPage-Cnm1AAjh.js.map → TopicsPage-DQV0WxG6.js.map} +1 -1
  197. package/dashboard/dist/assets/{UserName-CXsdPiMZ.js → UserName-Ct09nGh5.js} +2 -2
  198. package/dashboard/dist/assets/{UserName-CXsdPiMZ.js.map → UserName-Ct09nGh5.js.map} +1 -1
  199. package/dashboard/dist/assets/{WorkflowExecutionPage-zx3AUJFd.js → WorkflowExecutionPage-B5WJOsee.js} +2 -2
  200. package/dashboard/dist/assets/{WorkflowExecutionPage-zx3AUJFd.js.map → WorkflowExecutionPage-B5WJOsee.js.map} +1 -1
  201. package/dashboard/dist/assets/{WorkflowsDashboard-DfAGK1l7.js → WorkflowsDashboard-Hc87TRgD.js} +2 -2
  202. package/dashboard/dist/assets/{WorkflowsDashboard-DfAGK1l7.js.map → WorkflowsDashboard-Hc87TRgD.js.map} +1 -1
  203. package/dashboard/dist/assets/{WorkflowsOverview-QIYvoa13.js → WorkflowsOverview-CJQbyOj-.js} +2 -2
  204. package/dashboard/dist/assets/{WorkflowsOverview-QIYvoa13.js.map → WorkflowsOverview-CJQbyOj-.js.map} +1 -1
  205. package/dashboard/dist/assets/{YamlWorkflowDetailPage-DGgO3hyB.js → YamlWorkflowDetailPage-BMeED677.js} +8 -8
  206. package/dashboard/dist/assets/{YamlWorkflowDetailPage-DGgO3hyB.js.map → YamlWorkflowDetailPage-BMeED677.js.map} +1 -1
  207. package/dashboard/dist/assets/{YamlWorkflowsPage-CEsHm0B9.js → YamlWorkflowsPage-BMMg0Id6.js} +2 -2
  208. package/dashboard/dist/assets/{YamlWorkflowsPage-CEsHm0B9.js.map → YamlWorkflowsPage-BMMg0Id6.js.map} +1 -1
  209. package/dashboard/dist/assets/{agents-v_HH3Rji.js → agents-CdFkgFvg.js} +2 -2
  210. package/dashboard/dist/assets/{agents-v_HH3Rji.js.map → agents-CdFkgFvg.js.map} +1 -1
  211. package/dashboard/dist/assets/{bots-bwvWYrXz.js → bots-Mj3LHWWg.js} +2 -2
  212. package/dashboard/dist/assets/{bots-bwvWYrXz.js.map → bots-Mj3LHWWg.js.map} +1 -1
  213. package/dashboard/dist/assets/{capabilities-D4vaw_nQ.js → capabilities-Dw1ObK2-.js} +2 -2
  214. package/dashboard/dist/assets/{capabilities-D4vaw_nQ.js.map → capabilities-Dw1ObK2-.js.map} +1 -1
  215. package/dashboard/dist/assets/{controlplane-CiiZ6LJF.js → controlplane-DcNE1APv.js} +2 -2
  216. package/dashboard/dist/assets/{controlplane-CiiZ6LJF.js.map → controlplane-DcNE1APv.js.map} +1 -1
  217. package/dashboard/dist/assets/{escalation-columns-Dv-MB3eC.js → escalation-columns-D364Trpy.js} +2 -2
  218. package/dashboard/dist/assets/{escalation-columns-Dv-MB3eC.js.map → escalation-columns-D364Trpy.js.map} +1 -1
  219. package/dashboard/dist/assets/{index-IJmtbuLh.js → index-B3veJ698.js} +2 -2
  220. package/dashboard/dist/assets/{index-IJmtbuLh.js.map → index-B3veJ698.js.map} +1 -1
  221. package/dashboard/dist/assets/index-BQeHJHYN.js +67 -0
  222. package/dashboard/dist/assets/index-BQeHJHYN.js.map +1 -0
  223. package/dashboard/dist/assets/{index-D2VKBMng.js → index-BRGTmoYY.js} +2 -2
  224. package/dashboard/dist/assets/{index-D2VKBMng.js.map → index-BRGTmoYY.js.map} +1 -1
  225. package/dashboard/dist/assets/{index-C4Lce2ET.js → index-Bd6-KmBE.js} +3 -3
  226. package/dashboard/dist/assets/{index-C4Lce2ET.js.map → index-Bd6-KmBE.js.map} +1 -1
  227. package/dashboard/dist/assets/{index-CcuvGdvf.js → index-BiVce3n0.js} +2 -2
  228. package/dashboard/dist/assets/{index-CcuvGdvf.js.map → index-BiVce3n0.js.map} +1 -1
  229. package/dashboard/dist/assets/{index-D6pBLZFu.js → index-BmNAVd4s.js} +2 -2
  230. package/dashboard/dist/assets/{index-D6pBLZFu.js.map → index-BmNAVd4s.js.map} +1 -1
  231. package/dashboard/dist/assets/{index-CjenRVX_.js → index-Bq5XNEzD.js} +2 -2
  232. package/dashboard/dist/assets/{index-CjenRVX_.js.map → index-Bq5XNEzD.js.map} +1 -1
  233. package/dashboard/dist/assets/{index-CzjUgFt_.js → index-DPStdq4T.js} +2 -2
  234. package/dashboard/dist/assets/{index-CzjUgFt_.js.map → index-DPStdq4T.js.map} +1 -1
  235. package/dashboard/dist/assets/index-DcppjzP5.css +1 -0
  236. package/dashboard/dist/assets/{index-C3WutFpT.js → index-DkcI4V_H.js} +2 -2
  237. package/dashboard/dist/assets/{index-C3WutFpT.js.map → index-DkcI4V_H.js.map} +1 -1
  238. package/dashboard/dist/assets/{index-2tzsxFat.js → index-Dyotfa_k.js} +2 -2
  239. package/dashboard/dist/assets/{index-2tzsxFat.js.map → index-Dyotfa_k.js.map} +1 -1
  240. package/dashboard/dist/assets/{index-C_ISDOWJ.js → index-MDD4JYtu.js} +2 -2
  241. package/dashboard/dist/assets/{index-C_ISDOWJ.js.map → index-MDD4JYtu.js.map} +1 -1
  242. package/dashboard/dist/assets/index-WSm8bgE7.js +3 -0
  243. package/dashboard/dist/assets/index-WSm8bgE7.js.map +1 -0
  244. package/dashboard/dist/assets/{index-BLB77tc8.js → index-eDm6cz0F.js} +2 -2
  245. package/dashboard/dist/assets/{index-BLB77tc8.js.map → index-eDm6cz0F.js.map} +1 -1
  246. package/dashboard/dist/assets/{knowledge-D1YP2NzS.js → knowledge-Bafewjxk.js} +2 -2
  247. package/dashboard/dist/assets/{knowledge-D1YP2NzS.js.map → knowledge-Bafewjxk.js.map} +1 -1
  248. package/dashboard/dist/assets/{mcp-query-D5jUdZwN.js → mcp-query-BzTtIG90.js} +2 -2
  249. package/dashboard/dist/assets/{mcp-query-D5jUdZwN.js.map → mcp-query-BzTtIG90.js.map} +1 -1
  250. package/dashboard/dist/assets/{mcp-Bb5psC61.js → mcp-vLIutbnc.js} +2 -2
  251. package/dashboard/dist/assets/{mcp-Bb5psC61.js.map → mcp-vLIutbnc.js.map} +1 -1
  252. package/dashboard/dist/assets/{personas-CDndbkp4.js → personas-WD40EDS_.js} +2 -2
  253. package/dashboard/dist/assets/{personas-CDndbkp4.js.map → personas-WD40EDS_.js.map} +1 -1
  254. package/dashboard/dist/assets/{pipelines-Dq3kNhKu.js → pipelines-DFwULWs3.js} +2 -2
  255. package/dashboard/dist/assets/{pipelines-Dq3kNhKu.js.map → pipelines-DFwULWs3.js.map} +1 -1
  256. package/dashboard/dist/assets/{tasks-BlpDsWCy.js → tasks-La_-WAUn.js} +2 -2
  257. package/dashboard/dist/assets/{tasks-BlpDsWCy.js.map → tasks-La_-WAUn.js.map} +1 -1
  258. package/dashboard/dist/assets/{topics-C0YaR9fu.js → topics-CzYMlvqm.js} +2 -2
  259. package/dashboard/dist/assets/{topics-C0YaR9fu.js.map → topics-CzYMlvqm.js.map} +1 -1
  260. package/dashboard/dist/assets/{useDebouncedValue-B-KC5ba3.js → useDebouncedValue-C0FQyANW.js} +2 -2
  261. package/dashboard/dist/assets/{useDebouncedValue-B-KC5ba3.js.map → useDebouncedValue-C0FQyANW.js.map} +1 -1
  262. package/dashboard/dist/assets/{useNamespace-Dkr5g2Ac.js → useNamespace-WW_XjAb3.js} +2 -2
  263. package/dashboard/dist/assets/{useNamespace-Dkr5g2Ac.js.map → useNamespace-WW_XjAb3.js.map} +1 -1
  264. package/dashboard/dist/assets/{useYamlActivityEvents-BANBOshA.js → useYamlActivityEvents-B3YxPeFt.js} +2 -2
  265. package/dashboard/dist/assets/{useYamlActivityEvents-BANBOshA.js.map → useYamlActivityEvents-B3YxPeFt.js.map} +1 -1
  266. package/dashboard/dist/assets/{yaml-workflows-BLYoJfmB.js → yaml-workflows-COHOjdhF.js} +2 -2
  267. package/dashboard/dist/assets/{yaml-workflows-BLYoJfmB.js.map → yaml-workflows-COHOjdhF.js.map} +1 -1
  268. package/dashboard/dist/index.html +2 -2
  269. package/docs/dashboard.md +1 -1
  270. package/docs/hitl/x-lt-scan.md +51 -0
  271. package/docs/hitl-guide.md +2 -0
  272. package/docs/scan-codes.md +221 -21
  273. package/package.json +1 -1
  274. package/build/types/scan-code.d.ts +0 -317
  275. package/dashboard/dist/assets/CapabilitiesPage-BxadotXn.js +0 -2
  276. package/dashboard/dist/assets/CountdownTimer-CI_JOAvk.js +0 -2
  277. package/dashboard/dist/assets/ResolverForm-BVG8X0sS.js +0 -8
  278. package/dashboard/dist/assets/ResolverForm-BVG8X0sS.js.map +0 -1
  279. package/dashboard/dist/assets/RoleDetailPage-KCXDqoJz.js +0 -8
  280. package/dashboard/dist/assets/RoleDetailPage-KCXDqoJz.js.map +0 -1
  281. package/dashboard/dist/assets/ScanCodesPage-CUhCz-Yr.js +0 -2
  282. package/dashboard/dist/assets/ScanCodesPage-CUhCz-Yr.js.map +0 -1
  283. package/dashboard/dist/assets/ScanSchemeDetailPage-qzXKNqJK.js +0 -2
  284. package/dashboard/dist/assets/ScanSchemeDetailPage-qzXKNqJK.js.map +0 -1
  285. package/dashboard/dist/assets/ScanStationPage-D8e8cG3t.js +0 -2
  286. package/dashboard/dist/assets/ScanStationPage-D8e8cG3t.js.map +0 -1
  287. package/dashboard/dist/assets/index-CT-FN6CW.js +0 -3
  288. package/dashboard/dist/assets/index-CT-FN6CW.js.map +0 -1
  289. package/dashboard/dist/assets/index-Dh8MJl3L.css +0 -1
  290. package/dashboard/dist/assets/index-N71QQ3J5.js +0 -67
  291. package/dashboard/dist/assets/index-N71QQ3J5.js.map +0 -1
@@ -68,6 +68,15 @@ function isBatch(escalation) {
68
68
  return Array.isArray(escalation.metadata?.[escalation_1.ESCALATION_BATCH_KEYS.PENDING]);
69
69
  }
70
70
  const NO_ACCUMULATOR_FOR_FACET = 'No pending accumulator found for this metadata';
71
+ /** The add's item-key error, or null when the key is usable. */
72
+ function itemKeyError(itemKey) {
73
+ if (!itemKey)
74
+ return { status: 400, error: 'itemKey is required' };
75
+ if (typeof itemKey !== 'string' || itemKey.length > escalation_1.ESCALATION_ITEM_KEY_MAX_LENGTH) {
76
+ return { status: 400, error: `itemKey must be a string of at most ${escalation_1.ESCALATION_ITEM_KEY_MAX_LENGTH} characters` };
77
+ }
78
+ return null;
79
+ }
71
80
  /**
72
81
  * The container a facet names: the highest priority pending row that carries
73
82
  * the facet AND the accumulator declaration, within the given roles (null =
@@ -199,18 +208,22 @@ async function gatePayload(escalation, payload, userId) {
199
208
  async function accumulateItem(input, auth) {
200
209
  try {
201
210
  const { id, itemKey, metadata } = input;
202
- if (!itemKey)
203
- return { status: 400, error: 'itemKey is required' };
211
+ const keyError = itemKeyError(itemKey);
212
+ if (keyError)
213
+ return keyError;
204
214
  const initiator = await resolveActor(input.initiatedBy, auth);
205
215
  if ('error' in initiator)
206
216
  return initiator.error;
207
217
  const escalation = await escalationService.getEscalation(id);
208
218
  if (!escalation)
209
219
  return { status: 404, error: 'Escalation not found' };
210
- if (escalation.status === 'cancelled')
211
- return { status: 409, error: 'Escalation is cancelled' };
212
- if (escalation.status !== 'pending')
213
- return { status: 409, error: 'Escalation not available for accumulation' };
220
+ if (escalation.status === 'cancelled') {
221
+ return { status: 409, error: 'Escalation is cancelled', data: { error: 'Escalation is cancelled', outcome: 'already-cancelled' } };
222
+ }
223
+ if (escalation.status !== 'pending') {
224
+ const error = 'Escalation not available for accumulation';
225
+ return { status: 409, error, data: { error, outcome: `already-${escalation.status}` } };
226
+ }
214
227
  if (!isAccumulator(escalation))
215
228
  return { status: 400, error: 'Escalation is not an accumulator' };
216
229
  if (await (0, helpers_1.assertReadAccess)(auth.userId, escalation)) {
@@ -250,8 +263,9 @@ async function accumulateItemBySignalKey(input, auth) {
250
263
  const { signalKey, itemKey, metadata } = input;
251
264
  if (!signalKey)
252
265
  return { status: 400, error: 'signalKey is required' };
253
- if (!itemKey)
254
- return { status: 400, error: 'itemKey is required' };
266
+ const keyError = itemKeyError(itemKey);
267
+ if (keyError)
268
+ return keyError;
255
269
  const initiator = await resolveActor(input.initiatedBy, auth);
256
270
  if ('error' in initiator)
257
271
  return initiator.error;
@@ -301,8 +315,9 @@ async function accumulateItemByMetadata(input, auth) {
301
315
  const { key, value, itemKey, metadata } = input;
302
316
  if (!key || !value)
303
317
  return { status: 400, error: 'key and value are required' };
304
- if (!itemKey)
305
- return { status: 400, error: 'itemKey is required' };
318
+ const keyError = itemKeyError(itemKey);
319
+ if (keyError)
320
+ return keyError;
306
321
  const initiator = await resolveActor(input.initiatedBy, auth);
307
322
  if ('error' in initiator)
308
323
  return initiator.error;
@@ -574,21 +589,21 @@ function accumulateOutcomeResult(result, itemKey, workflowId) {
574
589
  },
575
590
  };
576
591
  case 'duplicate-item':
577
- return { status: 409, error: 'Item already held', data: { error: 'Item already held', itemKey } };
592
+ return { status: 409, error: 'Item already held', data: { error: 'Item already held', itemKey, outcome: 'duplicate-item' } };
578
593
  case 'full':
579
- return { status: 409, error: 'Accumulator is full', data: { error: 'Accumulator is full', itemKey } };
594
+ return { status: 409, error: 'Accumulator is full', data: { error: 'Accumulator is full', itemKey, outcome: 'full' } };
580
595
  case 'claimed-by-other':
581
- return { status: 409, error: 'Escalation is claimed by another user' };
596
+ return { status: 409, error: 'Escalation is claimed by another user', data: { error: 'Escalation is claimed by another user', outcome: 'claimed-by-other' } };
582
597
  case 'claim-expired':
583
- return { status: 409, error: 'Your claim has expired; re-claim this escalation to add to it' };
598
+ return { status: 409, error: 'Your claim has expired; re-claim this escalation to add to it', data: { error: 'Your claim has expired; re-claim this escalation to add to it', outcome: 'claim-expired' } };
584
599
  case 'not-found':
585
- return { status: 404, error: 'Escalation not found' };
600
+ return { status: 404, error: 'Escalation not found', data: { error: 'Escalation not found', outcome: 'not-found' } };
586
601
  case 'not-accumulator':
587
- return { status: 400, error: 'Escalation is not an accumulator' };
602
+ return { status: 400, error: 'Escalation is not an accumulator', data: { error: 'Escalation is not an accumulator', outcome: 'not-accumulator' } };
588
603
  case 'reciprocal-not-found':
589
- return { status: 404, error: 'Reciprocal escalation not found' };
604
+ return { status: 404, error: 'Reciprocal escalation not found', data: { error: 'Reciprocal escalation not found', outcome: 'reciprocal-not-found' } };
590
605
  case 'reciprocal-not-accumulator':
591
- return { status: 400, error: 'Reciprocal escalation is not an accumulator' };
606
+ return { status: 400, error: 'Reciprocal escalation is not an accumulator', data: { error: 'Reciprocal escalation is not an accumulator', outcome: 'reciprocal-not-accumulator' } };
592
607
  case 'reciprocal-duplicate':
593
608
  return { status: 409, error: 'Reciprocal escalation already holds this container', data: { error: 'Reciprocal escalation already holds this container', outcome: result.outcome } };
594
609
  case 'reciprocal-full':
@@ -596,7 +611,11 @@ function accumulateOutcomeResult(result, itemKey, workflowId) {
596
611
  case 'reciprocal-terminal':
597
612
  return { status: 409, error: 'Reciprocal escalation is no longer pending', data: { error: 'Reciprocal escalation is no longer pending', outcome: result.outcome } };
598
613
  default:
599
- return { status: 409, error: 'Escalation not available for accumulation' };
614
+ return {
615
+ status: 409,
616
+ error: 'Escalation not available for accumulation',
617
+ data: { error: 'Escalation not available for accumulation', outcome: result.outcome },
618
+ };
600
619
  }
601
620
  }
602
621
  function removeOutcomeResult(result, itemKey) {
@@ -70,10 +70,13 @@ async function resolveBatchItem(input, auth) {
70
70
  const escalation = await escalationService.getEscalation(id);
71
71
  if (!escalation)
72
72
  return { status: 404, error: 'Escalation not found' };
73
- if (escalation.status === 'cancelled')
74
- return { status: 409, error: 'Escalation is cancelled' };
75
- if (escalation.status !== 'pending')
76
- return { status: 409, error: 'Escalation not available for resolution' };
73
+ if (escalation.status === 'cancelled') {
74
+ return { status: 409, error: 'Escalation is cancelled', data: { error: 'Escalation is cancelled', outcome: 'already-cancelled' } };
75
+ }
76
+ if (escalation.status !== 'pending') {
77
+ const error = 'Escalation not available for resolution';
78
+ return { status: 409, error, data: { error, outcome: `already-${escalation.status}` } };
79
+ }
77
80
  // Advisory shape gate — cheap, readable rejection. The SDK's guarded
78
81
  // statement re-checks and is the atomic arbiter (outcome 'not-batch').
79
82
  const pending = escalation.metadata?.[escalation_1.ESCALATION_BATCH_KEYS.PENDING];
@@ -228,20 +231,24 @@ function batchOutcomeResult(result, itemKey, workflowId) {
228
231
  return {
229
232
  status: 409,
230
233
  error: 'Batch item already submitted',
231
- data: { error: 'Batch item already submitted', itemKey },
234
+ data: { error: 'Batch item already submitted', itemKey, outcome: 'duplicate-item' },
232
235
  };
233
236
  case 'claimed-by-other':
234
- return { status: 409, error: 'Escalation is claimed by another user' };
237
+ return { status: 409, error: 'Escalation is claimed by another user', data: { error: 'Escalation is claimed by another user', outcome: 'claimed-by-other' } };
235
238
  case 'claim-expired':
236
- return { status: 409, error: 'Your claim has expired — re-claim this escalation to resolve it' };
239
+ return { status: 409, error: 'Your claim has expired — re-claim this escalation to resolve it', data: { error: 'Your claim has expired — re-claim this escalation to resolve it', outcome: 'claim-expired' } };
237
240
  case 'not-found':
238
- return { status: 404, error: 'Escalation not found' };
241
+ return { status: 404, error: 'Escalation not found', data: { error: 'Escalation not found', outcome: 'not-found' } };
239
242
  case 'unknown-item':
240
- return { status: 400, error: 'itemKey is not in the declared batch' };
243
+ return { status: 400, error: 'itemKey is not in the declared batch', data: { error: 'itemKey is not in the declared batch', outcome: 'unknown-item' } };
241
244
  case 'not-batch':
242
- return { status: 400, error: 'Escalation is not a batch' };
245
+ return { status: 400, error: 'Escalation is not a batch', data: { error: 'Escalation is not a batch', outcome: 'not-batch' } };
243
246
  default:
244
247
  // already-resolved / already-cancelled / already-expired
245
- return { status: 409, error: 'Escalation not available for resolution' };
248
+ return {
249
+ status: 409,
250
+ error: 'Escalation not available for resolution',
251
+ data: { error: 'Escalation not available for resolution', outcome: result.outcome },
252
+ };
246
253
  }
247
254
  }
@@ -49,6 +49,7 @@ exports.listRoleListSchemaVersions = listRoleListSchemaVersions;
49
49
  exports.deleteRole = deleteRole;
50
50
  const roleService = __importStar(require("../services/role"));
51
51
  const types_1 = require("../services/role/types");
52
+ const badge_grant_1 = require("../services/role/badge-grant");
52
53
  const facet_sql_1 = require("../services/escalation/facet-sql");
53
54
  /**
54
55
  * List all distinct role names in the system.
@@ -291,6 +292,9 @@ async function updateRole(input) {
291
292
  return { status: 400, error: 'default_pins must be [{ label, url, badge? }] with dashboard-relative urls' };
292
293
  }
293
294
  }
295
+ const grantError = (0, badge_grant_1.badgeGrantError)(input.properties?.[types_1.ROLE_PROPERTY_KEYS.BADGE_GRANT]);
296
+ if (grantError)
297
+ return { status: 400, error: grantError };
294
298
  if (input.portals != null && !isPortalList(input.portals)) {
295
299
  return {
296
300
  status: 400,
@@ -0,0 +1,10 @@
1
+ import { type ScanGrantScope, type ScanScheme } from '../../types';
2
+ export interface GrantPolicy {
3
+ ttlSeconds: number;
4
+ maxUses: number;
5
+ scope: ScanGrantScope;
6
+ /** The station role whose policy applied; absent when the scheme's did. */
7
+ role?: string;
8
+ }
9
+ /** The policy a badge scanned on this device mints under. */
10
+ export declare function stationGrantPolicy(scheme: ScanScheme, stationUserId: string, stationRole?: string): Promise<GrantPolicy>;
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.stationGrantPolicy = stationGrantPolicy;
37
+ const roleService = __importStar(require("../../services/role"));
38
+ const userService = __importStar(require("../../services/user"));
39
+ const badge_grant_1 = require("../../services/role/badge-grant");
40
+ const types_1 = require("../../types");
41
+ function schemePolicy(scheme) {
42
+ return {
43
+ ttlSeconds: scheme.grant_ttl_seconds ?? 0,
44
+ maxUses: scheme.grant_max_uses ?? 0,
45
+ scope: scheme.grant_scope ?? types_1.SCAN_GRANT_SCOPES.ACTION,
46
+ };
47
+ }
48
+ /** The policy a badge scanned on this device mints under. */
49
+ async function stationGrantPolicy(scheme, stationUserId, stationRole) {
50
+ const fallback = schemePolicy(scheme);
51
+ const user = await userService.getUser(stationUserId);
52
+ const memberRoles = (user?.roles ?? []).map((r) => r.role);
53
+ const candidates = stationRole
54
+ ? memberRoles.filter((r) => r === stationRole)
55
+ : memberRoles;
56
+ const declared = [];
57
+ for (const role of candidates) {
58
+ const grant = (0, badge_grant_1.readBadgeGrant)(await roleService.getRoleProperties(role));
59
+ if (grant)
60
+ declared.push({ role, grant });
61
+ }
62
+ const distinct = new Set(declared.map((d) => JSON.stringify(d.grant)));
63
+ if (distinct.size !== 1)
64
+ return fallback;
65
+ const { role, grant } = declared[0];
66
+ return {
67
+ ttlSeconds: grant.ttl_seconds ?? fallback.ttlSeconds,
68
+ maxUses: grant.max_uses ?? fallback.maxUses,
69
+ scope: grant.scope ?? fallback.scope,
70
+ role,
71
+ };
72
+ }
@@ -38,6 +38,7 @@ const scanCodeService = __importStar(require("../../services/scan-code"));
38
38
  const escalationService = __importStar(require("../../services/escalation"));
39
39
  const types_1 = require("../../types");
40
40
  const identity_1 = require("./identity");
41
+ const grant_1 = require("./grant");
41
42
  const context_1 = require("./context");
42
43
  const locate_1 = require("./locate");
43
44
  const verbs_1 = require("./verbs");
@@ -69,19 +70,19 @@ async function executeScanChoice(input, auth) {
69
70
  data: { outcome: types_1.SCAN_OUTCOMES.UNCONFIGURED, error: 'the presented choice is no longer configured' },
70
71
  };
71
72
  }
72
- // 2. The acting grant, exchanged before anything reads or writes.
73
+ // 2. The acting grant, read (not spent) before anything reads or writes.
73
74
  let effectiveAuth = auth;
74
- let acting = false;
75
+ let grant;
75
76
  if (input.actingToken) {
76
- const resolved = await (0, identity_1.resolveActingAuth)(input.actingToken);
77
- if (!resolved.ok) {
77
+ const peeked = await (0, grant_1.peekGrant)(input.actingToken);
78
+ if (!peeked.ok) {
78
79
  return {
79
80
  status: 200,
80
- data: { outcome: types_1.SCAN_OUTCOMES.NOT_PRIMED, notPrimed: rule.notPrimed, error: resolved.error },
81
+ data: { outcome: types_1.SCAN_OUTCOMES.NOT_PRIMED, notPrimed: rule.notPrimed, replayable: true, error: peeked.error },
81
82
  };
82
83
  }
83
- effectiveAuth = resolved.auth;
84
- acting = true;
84
+ effectiveAuth = peeked.auth;
85
+ grant = peeked.grant;
85
86
  }
86
87
  // 3. The row anchors the target: the scheme's facet on the escalation the
87
88
  // screen presented. A missing binding is loud misconfiguration.
@@ -100,7 +101,8 @@ async function executeScanChoice(input, auth) {
100
101
  scannedAt: new Date().toISOString(),
101
102
  auth: effectiveAuth,
102
103
  stationAuth: auth,
103
- acting,
104
+ acting: !!grant,
105
+ grant,
104
106
  };
105
107
  // 4. The identity gate, per the choice.
106
108
  if (choice.requireActingIdentity && !(await (0, identity_1.actingIdentitySatisfied)(step, ctx))) {
@@ -116,8 +118,12 @@ async function executeScanChoice(input, auth) {
116
118
  }
117
119
  // 6. Dispatch through the same atomic executors a direct scan uses.
118
120
  const synthesized = { query: step.query, verb: choice.verb, params: choice.params };
119
- const result = await (0, verbs_1.dispatchChoiceVerb)(synthesized, ctx, presented);
120
- return decorate(result, ctx, input);
121
+ const result = decorate(await (0, verbs_1.dispatchChoiceVerb)(synthesized, ctx, presented), ctx, input);
122
+ // 7. Settle the badge: refund a spend whose act did not land
123
+ const acting = await (0, grant_1.settleGrant)(ctx, result);
124
+ if (acting && result.data)
125
+ result.data = { ...result.data, acting };
126
+ return result;
121
127
  }
122
128
  catch (err) {
123
129
  return { status: 500, error: err.message };
@@ -1,11 +1,34 @@
1
1
  import * as scanCodeService from '../../services/scan-code';
2
2
  import { type LTEscalationRecord, type ParsedScanCode, type ScanExecuteResponse, type ScanRule, type ScanScheme, type ScanStep } from '../../types';
3
3
  import type { LTApiAuth, LTApiResult } from '../../types/sdk';
4
+ /** The held subject, re-derived from the client's pointer on this request. */
5
+ export interface HeldSubject {
6
+ row: LTEscalationRecord;
7
+ parsed: ParsedScanCode;
8
+ scheme: ScanScheme;
9
+ code: string;
10
+ }
11
+ /** The acting grant riding the request, and whether this request spent it. */
12
+ export interface GrantLedger {
13
+ token: string;
14
+ /** Before this request touched it. */
15
+ peeked: {
16
+ remaining: number | null;
17
+ bound: boolean;
18
+ };
19
+ /** Set once this request spends a use; null until then. */
20
+ spent: {
21
+ remaining: number | null;
22
+ bound: boolean;
23
+ } | null;
24
+ }
4
25
  /** Everything a step executor needs about the scan that reached it. */
5
26
  export interface StepContext {
6
27
  scheme: ScanScheme;
7
28
  rule: ScanRule;
8
29
  parsed: ParsedScanCode;
30
+ /** The code as the station scanned it; absent when a choice re-derived the target. */
31
+ rawCode?: string;
9
32
  scannedAt: string;
10
33
  /** The effective actor — the acting (badged) user when a grant rode the request. */
11
34
  auth: LTApiAuth;
@@ -13,9 +36,17 @@ export interface StepContext {
13
36
  stationAuth: LTApiAuth;
14
37
  /** True when `auth` came from an acting-identity grant. */
15
38
  acting: boolean;
39
+ /** The grant behind `acting`; spent only when an act lands. */
40
+ grant?: GrantLedger;
41
+ /** The subject the station holds, when its pointer still checks out. */
42
+ subject?: HeldSubject;
43
+ /** The station sent a subject pointer that no longer checks out. */
44
+ subjectStale?: boolean;
16
45
  }
17
46
  export declare function targetFilter(step: ScanStep, ctx: StepContext): Record<string, any>;
18
47
  export declare function templateContext(ctx: StepContext): scanCodeService.ScanTemplateContext;
48
+ /** The `{subject.x}` bag: the held row's metadata plus its id. */
49
+ export declare function subjectBag(row: LTEscalationRecord): Record<string, unknown>;
19
50
  /**
20
51
  * The metadata of the acting user's single live claim, or undefined when
21
52
  * they hold none or more than one. One scoped list query, read before the
@@ -34,4 +65,17 @@ export declare function interpolatedMetadata(step: ScanStep, ctx: StepContext, t
34
65
  export declare function executed(escalation: LTEscalationRecord | Record<string, any> | undefined, step: Pick<ScanStep, 'verb'>): LTApiResult<ScanExecuteResponse>;
35
66
  export declare function forbidden(error?: string): LTApiResult<ScanExecuteResponse>;
36
67
  export declare function conflict(error?: string): LTApiResult<ScanExecuteResponse>;
37
- export declare function notPrimed(ctx: StepContext): LTApiResult<ScanExecuteResponse>;
68
+ export declare function notPrimed(ctx: StepContext, error?: string): LTApiResult<ScanExecuteResponse>;
69
+ /** Nothing was written; say why, and name what the step expected. */
70
+ export declare function refused(markdown: string, extra?: Partial<ScanExecuteResponse>): LTApiResult<ScanExecuteResponse>;
71
+ /**
72
+ * Interpolates a step's params, or falls through when a `{claim.…}` or
73
+ * `{item.…}` token has nothing to read: a literal token never reaches a row.
74
+ */
75
+ export declare function templated<T>(step: ScanStep, ctx: StepContext, render: (tpl: scanCodeService.ScanTemplateContext) => T, item?: LTEscalationRecord): Promise<T | null>;
76
+ /**
77
+ * Attach the step's `done` copy to a write that landed. `tpl` carries the
78
+ * bags the copy may read (the container a subject step located, a fill's
79
+ * batch); an unreadable token renders empty rather than failing the act.
80
+ */
81
+ export declare function withDone(result: LTApiResult<ScanExecuteResponse>, step: ScanStep, tpl: scanCodeService.ScanTemplateContext): LTApiResult<ScanExecuteResponse>;
@@ -35,6 +35,7 @@ var __importStar = (this && this.__importStar) || (function () {
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.targetFilter = targetFilter;
37
37
  exports.templateContext = templateContext;
38
+ exports.subjectBag = subjectBag;
38
39
  exports.liveClaimMetadata = liveClaimMetadata;
39
40
  exports.stepTemplate = stepTemplate;
40
41
  exports.provenance = provenance;
@@ -43,6 +44,9 @@ exports.executed = executed;
43
44
  exports.forbidden = forbidden;
44
45
  exports.conflict = conflict;
45
46
  exports.notPrimed = notPrimed;
47
+ exports.refused = refused;
48
+ exports.templated = templated;
49
+ exports.withDone = withDone;
46
50
  const scanCodeService = __importStar(require("../../services/scan-code"));
47
51
  const escalationService = __importStar(require("../../services/escalation"));
48
52
  const types_1 = require("../../types");
@@ -50,7 +54,17 @@ function targetFilter(step, ctx) {
50
54
  return { [ctx.scheme.target_facet]: ctx.parsed.target, ...(step.query?.facets ?? {}) };
51
55
  }
52
56
  function templateContext(ctx) {
53
- return { target: ctx.parsed.target, category: ctx.parsed.category, scannedAt: ctx.scannedAt };
57
+ return {
58
+ target: ctx.parsed.target,
59
+ category: ctx.parsed.category,
60
+ scannedAt: ctx.scannedAt,
61
+ code: ctx.parsed.raw ?? ctx.parsed.target,
62
+ ...(ctx.subject ? { subject: subjectBag(ctx.subject.row) } : {}),
63
+ };
64
+ }
65
+ /** The `{subject.x}` bag: the held row's metadata plus its id. */
66
+ function subjectBag(row) {
67
+ return { ...(row.metadata ?? {}), id: row.id };
54
68
  }
55
69
  /**
56
70
  * The metadata of the acting user's single live claim, or undefined when
@@ -102,13 +116,47 @@ function forbidden(error) {
102
116
  function conflict(error) {
103
117
  return { status: 200, data: { outcome: types_1.SCAN_OUTCOMES.CONFLICT, error } };
104
118
  }
105
- function notPrimed(ctx) {
119
+ function notPrimed(ctx, error) {
106
120
  return {
107
121
  status: 200,
108
122
  data: {
109
123
  outcome: types_1.SCAN_OUTCOMES.NOT_PRIMED,
110
124
  notPrimed: ctx.rule.notPrimed,
111
- error: 'an acting identity is required — scan your badge',
125
+ replayable: true,
126
+ error: error ?? 'an acting identity is required — scan your badge',
112
127
  },
113
128
  };
114
129
  }
130
+ /** Nothing was written; say why, and name what the step expected. */
131
+ function refused(markdown, extra = {}) {
132
+ return {
133
+ status: 200,
134
+ data: { outcome: types_1.SCAN_OUTCOMES.REFUSED, refusal: { markdown }, error: markdown, ...extra },
135
+ };
136
+ }
137
+ /**
138
+ * Interpolates a step's params, or falls through when a `{claim.…}` or
139
+ * `{item.…}` token has nothing to read: a literal token never reaches a row.
140
+ */
141
+ async function templated(step, ctx, render, item) {
142
+ const tpl = await stepTemplate(step, ctx, item);
143
+ try {
144
+ return render(tpl);
145
+ }
146
+ catch (err) {
147
+ if (err instanceof scanCodeService.ScanTemplateError)
148
+ return null;
149
+ throw err;
150
+ }
151
+ }
152
+ /**
153
+ * Attach the step's `done` copy to a write that landed. `tpl` carries the
154
+ * bags the copy may read (the container a subject step located, a fill's
155
+ * batch); an unreadable token renders empty rather than failing the act.
156
+ */
157
+ function withDone(result, step, tpl) {
158
+ const data = result.data;
159
+ if (!step.done || !data || data.outcome !== types_1.SCAN_OUTCOMES.EXECUTED || data.already || data.done)
160
+ return result;
161
+ return { ...result, data: { ...data, done: { markdown: scanCodeService.renderScanCopy(step.done.markdown, tpl) } } };
162
+ }
@@ -37,10 +37,15 @@ exports.executeScanCode = executeScanCode;
37
37
  const scanCodeService = __importStar(require("../../services/scan-code"));
38
38
  const identity_1 = require("./identity");
39
39
  const context_1 = require("./context");
40
+ const grant_1 = require("./grant");
41
+ const hold_1 = require("./hold");
40
42
  const locate_1 = require("./locate");
41
43
  const present_1 = require("./present");
44
+ const subject_1 = require("./subject");
45
+ const verb_fill_1 = require("./verb-fill");
42
46
  const verbs_1 = require("./verbs");
43
47
  const types_1 = require("../../types");
48
+ const SUBJECT_STALE_MESSAGE = 'That one moved on. Scan it again.';
44
49
  /**
45
50
  * Execute a raw scan code: parse → load the rule → walk its steps in order.
46
51
  * The first step whose condition matches performs its verb and answers; a
@@ -77,45 +82,57 @@ async function executeScanCode(input, auth) {
77
82
  }
78
83
  // 2a. Identity schemes are the badge layer — they mint, never walk steps.
79
84
  if (scheme.kind === types_1.SCAN_SCHEME_KINDS.IDENTITY) {
80
- return (0, identity_1.executeIdentityScan)(parsed, scheme, rule, input.previousActingToken);
85
+ return (0, identity_1.executeIdentityScan)(parsed, scheme, rule, input.previousActingToken, {
86
+ userId: auth.userId,
87
+ stationRole: typeof input.stationRole === 'string' ? input.stationRole : undefined,
88
+ });
81
89
  }
82
- // 2b. A supplied acting grant is exchanged once, up front — the acting
83
- // user's read scope must govern the locate as much as the mutation. A
84
- // dead grant terminates loudly; silently degrading to the station
85
- // identity would misattribute whatever follows.
86
- let effectiveAuth = auth;
87
- let acting = false;
90
+ // 2b. A supplied acting grant is read up front (not spent): the acting
91
+ // user's read scope governs the locate as much as the mutation. A dead
92
+ // grant terminates loudly; silently degrading to the station identity
93
+ // would misattribute whatever follows.
94
+ const ctx = {
95
+ scheme, rule, parsed, rawCode: input.code.trim(), scannedAt: new Date().toISOString(),
96
+ auth, stationAuth: auth, acting: false,
97
+ };
88
98
  if (input.actingToken) {
89
- const resolved = await (0, identity_1.resolveActingAuth)(input.actingToken);
90
- if (!resolved.ok) {
91
- return {
92
- status: 200,
93
- data: {
94
- outcome: types_1.SCAN_OUTCOMES.NOT_PRIMED,
95
- parsed,
96
- notPrimed: rule.notPrimed,
97
- error: resolved.error,
98
- },
99
- };
99
+ const peeked = await (0, grant_1.peekGrant)(input.actingToken);
100
+ if (!peeked.ok) {
101
+ return { status: 200, data: { ...(0, context_1.notPrimed)(ctx, peeked.error).data, parsed } };
100
102
  }
101
- effectiveAuth = resolved.auth;
102
- acting = true;
103
+ ctx.auth = peeked.auth;
104
+ ctx.acting = true;
105
+ ctx.grant = peeked.grant;
103
106
  }
107
+ // 2c. The held subject, re-derived from the station's pointer
108
+ const held = await (0, subject_1.resolveSubject)(input.subject, schemes, auth);
109
+ if (held?.ok)
110
+ ctx.subject = held.subject;
111
+ else if (held)
112
+ ctx.subjectStale = true;
104
113
  // 3. Walk the steps — first condition match wins
105
- const ctx = {
106
- scheme, rule, parsed, scannedAt: new Date().toISOString(),
107
- auth: effectiveAuth, stationAuth: auth, acting,
108
- };
109
- for (let i = 0; i < rule.steps.length; i++) {
110
- const result = await executeStep(rule.steps[i], i, ctx);
111
- if (result)
112
- return decorate(result, ctx, i);
114
+ let result = null;
115
+ for (let i = 0; i < rule.steps.length && !result; i++) {
116
+ const stepResult = await executeStep(rule.steps[i], ctx);
117
+ if (stepResult)
118
+ result = decorate((0, context_1.withDone)(stepResult, rule.steps[i], (0, context_1.templateContext)(ctx)), ctx, i);
113
119
  }
114
- // 4. Nothing matched — the fallback screen is the answer
115
- return {
116
- status: 200,
117
- data: decorateData({ outcome: types_1.SCAN_OUTCOMES.NO_MATCH_FALLBACK, fallback: rule.fallback }, ctx),
118
- };
120
+ // 4. Nothing matched: a stale subject, or the fallback screen
121
+ if (!result) {
122
+ result = ctx.subjectStale
123
+ ? { status: 200, data: decorateData({ outcome: types_1.SCAN_OUTCOMES.SUBJECT_STALE, clearSubject: true, error: SUBJECT_STALE_MESSAGE }, ctx) }
124
+ : { status: 200, data: decorateData({ outcome: types_1.SCAN_OUTCOMES.NO_MATCH_FALLBACK, fallback: rule.fallback }, ctx) };
125
+ }
126
+ // 5. Settle the badge: refund a spend whose act did not land
127
+ const acting = await (0, grant_1.settleGrant)(ctx, result);
128
+ if (result.data && typeof result.data === 'object' && 'outcome' in result.data) {
129
+ result.data = {
130
+ ...result.data,
131
+ ...(acting ? { acting } : {}),
132
+ ...(ctx.subjectStale ? { clearSubject: true } : {}),
133
+ };
134
+ }
135
+ return result;
119
136
  }
120
137
  catch (err) {
121
138
  return { status: 500, error: err.message };
@@ -139,17 +156,34 @@ function decorateData(data, ctx) {
139
156
  },
140
157
  };
141
158
  }
159
+ /**
160
+ * A subject step runs only while a subject from one of its schemes is held,
161
+ * and, when it declares facets, only while the held row carries them.
162
+ */
163
+ function subjectGateOpen(step, ctx) {
164
+ if (!step.subject)
165
+ return true;
166
+ if (!ctx.subject || !step.subject.schemes.includes(ctx.subject.scheme.version))
167
+ return false;
168
+ const meta = (ctx.subject.row.metadata ?? {});
169
+ return Object.entries(step.subject.facets ?? {}).every(([key, value]) => meta[key] === value);
170
+ }
142
171
  /**
143
172
  * Execute one step. Returns null when the step's condition matched nothing
144
173
  * (fall through) and an LTApiResult for every terminal outcome.
145
174
  */
146
- async function executeStep(step, index, ctx) {
175
+ async function executeStep(step, ctx) {
176
+ if (!subjectGateOpen(step, ctx))
177
+ return null;
147
178
  // The identity pre-condition: the effective actor must be a real acting
148
179
  // identity — a badge grant, or an authenticated user whose own write scope
149
180
  // covers the step. A write-incapable shared account can never self-satisfy,
150
181
  // so the outcome is the rule's "scan your badge" screen, never a silent or
151
182
  // misattributed action.
152
- if (step.requireActingIdentity && !(await (0, identity_1.actingIdentitySatisfied)(step, ctx))) {
183
+ // A subject step checks the pairing first and asks for the badge only when
184
+ // the pairing is right (see identityGate), so a wrong container is named
185
+ // at once.
186
+ if (step.requireActingIdentity && !step.subject && !(await (0, identity_1.actingIdentitySatisfied)(step, ctx))) {
153
187
  return (0, context_1.notPrimed)(ctx);
154
188
  }
155
189
  // Confirm steps and read verbs only locate — mutation happens after the
@@ -160,6 +194,8 @@ async function executeStep(step, index, ctx) {
160
194
  switch (step.verb) {
161
195
  case types_1.SCAN_VERBS.PRESENT:
162
196
  return (0, present_1.presentStep)(step, ctx);
197
+ case types_1.SCAN_VERBS.HOLD:
198
+ return (0, hold_1.holdStep)(step, ctx);
163
199
  case types_1.SCAN_VERBS.CLAIM:
164
200
  case types_1.SCAN_VERBS.CLAIM_SHOW_DETAIL:
165
201
  return (0, verbs_1.claimStep)(step, ctx);
@@ -173,6 +209,8 @@ async function executeStep(step, index, ctx) {
173
209
  return (0, verbs_1.cancelStep)(step, ctx);
174
210
  case types_1.SCAN_VERBS.ACCUMULATE:
175
211
  return (0, verbs_1.accumulateStep)(step, ctx);
212
+ case types_1.SCAN_VERBS.FILL:
213
+ return (0, verb_fill_1.fillStep)(step, ctx);
176
214
  default:
177
215
  throw new Error(`unknown scan verb "${step.verb}"`);
178
216
  }