@hotmeshio/long-tail 0.30.4 → 0.31.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (413) hide show
  1. package/build/api/escalations/index.d.ts +1 -0
  2. package/build/api/escalations/index.js +5 -2
  3. package/build/api/escalations/roster-action.d.ts +11 -0
  4. package/build/api/escalations/roster-action.js +47 -30
  5. package/build/api/escalations/roster-scan.d.ts +26 -0
  6. package/build/api/escalations/roster-scan.js +114 -0
  7. package/build/api/labels/errors.d.ts +3 -0
  8. package/build/api/labels/errors.js +15 -0
  9. package/build/api/labels/index.d.ts +2 -0
  10. package/build/api/labels/index.js +17 -0
  11. package/build/api/labels/issue.d.ts +54 -0
  12. package/build/api/labels/issue.js +144 -0
  13. package/build/api/labels/templates.d.ts +29 -0
  14. package/build/api/labels/templates.js +98 -0
  15. package/build/api/scan-codes/context.d.ts +9 -1
  16. package/build/api/scan-codes/context.js +16 -1
  17. package/build/api/scan-codes/execute.js +6 -1
  18. package/build/api/scan-codes/fill-labels.d.ts +8 -0
  19. package/build/api/scan-codes/fill-labels.js +23 -2
  20. package/build/api/scan-codes/hold.js +2 -0
  21. package/build/api/scan-codes/located.d.ts +10 -0
  22. package/build/api/scan-codes/located.js +74 -0
  23. package/build/api/scan-codes/open.d.ts +9 -0
  24. package/build/api/scan-codes/open.js +99 -0
  25. package/build/api/scan-codes/present.js +1 -0
  26. package/build/api/scan-codes/verb-fill.js +1 -1
  27. package/build/api/users.d.ts +8 -4
  28. package/build/api/users.js +51 -13
  29. package/build/index.d.ts +1 -0
  30. package/build/index.js +4 -2
  31. package/build/lib/db/schemas/044_label_sheets.sql +65 -0
  32. package/build/lib/db/schemas/045_label_job_delivery.sql +14 -0
  33. package/build/lib/labels/transports.d.ts +23 -0
  34. package/build/lib/labels/transports.js +39 -0
  35. package/build/modules/auth.d.ts +2 -0
  36. package/build/modules/auth.js +3 -1
  37. package/build/modules/capabilities.d.ts +5 -1
  38. package/build/modules/capabilities.js +38 -8
  39. package/build/modules/config.d.ts +10 -0
  40. package/build/modules/config.js +10 -0
  41. package/build/routes/escalations/resolve.js +18 -0
  42. package/build/routes/index.js +2 -0
  43. package/build/routes/labels.d.ts +2 -0
  44. package/build/routes/labels.js +135 -0
  45. package/build/routes/users.js +1 -1
  46. package/build/services/labels/content.d.ts +13 -0
  47. package/build/services/labels/content.js +81 -0
  48. package/build/services/labels/errors.d.ts +5 -0
  49. package/build/services/labels/errors.js +12 -0
  50. package/build/services/labels/index.d.ts +11 -0
  51. package/build/services/labels/index.js +52 -0
  52. package/build/services/labels/issue.d.ts +38 -0
  53. package/build/services/labels/issue.js +181 -0
  54. package/build/services/labels/jobs.d.ts +37 -0
  55. package/build/services/labels/jobs.js +75 -0
  56. package/build/services/labels/media.d.ts +11 -0
  57. package/build/services/labels/media.js +44 -0
  58. package/build/services/labels/reissue.d.ts +13 -0
  59. package/build/services/labels/reissue.js +65 -0
  60. package/build/services/labels/render-pdf.d.ts +11 -0
  61. package/build/services/labels/render-pdf.js +138 -0
  62. package/build/services/labels/targets.d.ts +18 -0
  63. package/build/services/labels/targets.js +216 -0
  64. package/build/services/labels/templates/index.d.ts +3 -0
  65. package/build/services/labels/templates/index.js +18 -0
  66. package/build/services/labels/templates/read.d.ts +4 -0
  67. package/build/services/labels/templates/read.js +36 -0
  68. package/build/services/labels/templates/sql.d.ts +8 -0
  69. package/build/services/labels/templates/sql.js +50 -0
  70. package/build/services/labels/templates/validate.d.ts +19 -0
  71. package/build/services/labels/templates/validate.js +93 -0
  72. package/build/services/labels/templates/write.d.ts +16 -0
  73. package/build/services/labels/templates/write.js +75 -0
  74. package/build/services/labels/token.d.ts +9 -0
  75. package/build/services/labels/token.js +28 -0
  76. package/build/services/labels/verify.d.ts +4 -0
  77. package/build/services/labels/verify.js +38 -0
  78. package/build/services/role/index.d.ts +2 -0
  79. package/build/services/role/index.js +7 -0
  80. package/build/services/role/scan-done.js +4 -1
  81. package/build/services/role/sql.d.ts +1 -0
  82. package/build/services/role/sql.js +3 -1
  83. package/build/services/role/types.d.ts +2 -0
  84. package/build/services/scan-code/index.d.ts +1 -1
  85. package/build/services/scan-code/index.js +2 -1
  86. package/build/services/scan-code/parse.d.ts +5 -1
  87. package/build/services/scan-code/parse.js +8 -2
  88. package/build/services/scan-code/validate-station.d.ts +13 -0
  89. package/build/services/scan-code/validate-station.js +81 -0
  90. package/build/services/scan-code/validate-subject.js +33 -6
  91. package/build/services/scan-code/validate.d.ts +3 -0
  92. package/build/services/scan-code/validate.js +22 -18
  93. package/build/services/user/crud.d.ts +13 -1
  94. package/build/services/user/crud.js +42 -11
  95. package/build/services/user/index.d.ts +3 -2
  96. package/build/services/user/index.js +7 -1
  97. package/build/services/user/redact.d.ts +17 -0
  98. package/build/services/user/redact.js +32 -0
  99. package/build/services/user/sql.d.ts +8 -15
  100. package/build/services/user/sql.js +27 -3
  101. package/build/services/user/types.d.ts +21 -1
  102. package/build/shared/labels/compose.d.ts +12 -0
  103. package/build/shared/labels/compose.js +43 -0
  104. package/build/shared/labels/index.d.ts +9 -0
  105. package/build/shared/labels/index.js +54 -0
  106. package/build/shared/labels/label-content.d.ts +17 -0
  107. package/build/shared/labels/label-content.js +137 -0
  108. package/build/shared/labels/layout.d.ts +23 -0
  109. package/build/shared/labels/layout.js +81 -0
  110. package/build/shared/labels/media.d.ts +22 -0
  111. package/build/shared/labels/media.js +116 -0
  112. package/build/shared/labels/render-svg.d.ts +10 -0
  113. package/build/shared/labels/render-svg.js +58 -0
  114. package/build/shared/labels/render-zpl.d.ts +9 -0
  115. package/build/shared/labels/render-zpl.js +63 -0
  116. package/build/shared/labels/symbol.d.ts +18 -0
  117. package/build/shared/labels/symbol.js +74 -0
  118. package/build/shared/labels/tokens.d.ts +18 -0
  119. package/build/shared/labels/tokens.js +43 -0
  120. package/build/shared/labels/types.d.ts +105 -0
  121. package/build/shared/labels/types.js +11 -0
  122. package/build/start/adapters.js +16 -0
  123. package/build/start/workers.js +18 -2
  124. package/build/system/mcp-servers/admin/index.js +2 -0
  125. package/build/system/mcp-servers/admin/labels-schemas.d.ts +192 -0
  126. package/build/system/mcp-servers/admin/labels-schemas.js +46 -0
  127. package/build/system/mcp-servers/admin/labels.d.ts +9 -0
  128. package/build/system/mcp-servers/admin/labels.js +101 -0
  129. package/build/system/mcp-servers/admin/schemas.d.ts +491 -24
  130. package/build/system/mcp-servers/admin/schemas.js +32 -4
  131. package/build/system/mcp-servers/admin/users.js +1 -1
  132. package/build/system/seed/tool-manifests-admin.js +5 -0
  133. package/build/tsconfig.tsbuildinfo +1 -1
  134. package/build/types/capability.d.ts +2 -1
  135. package/build/types/index.d.ts +3 -2
  136. package/build/types/labels/index.d.ts +4 -0
  137. package/build/types/labels/index.js +2 -0
  138. package/build/types/labels/job.d.ts +51 -0
  139. package/build/types/labels/job.js +2 -0
  140. package/build/types/labels/source.d.ts +44 -0
  141. package/build/types/labels/source.js +2 -0
  142. package/build/types/labels/template.d.ts +46 -0
  143. package/build/types/labels/template.js +2 -0
  144. package/build/types/labels/transport.d.ts +24 -0
  145. package/build/types/labels/transport.js +2 -0
  146. package/build/types/scan-choice.d.ts +2 -4
  147. package/build/types/scan-code/constants.d.ts +1 -0
  148. package/build/types/scan-code/constants.js +1 -0
  149. package/build/types/scan-code/response.d.ts +22 -4
  150. package/build/types/scan-code/rule.d.ts +66 -3
  151. package/build/types/startup.d.ts +37 -0
  152. package/build/types/user.d.ts +2 -0
  153. package/dashboard/dist/assets/{AdminDashboard-VQ1fhhJP.js → AdminDashboard-BafKaq50.js} +2 -2
  154. package/dashboard/dist/assets/{AdminDashboard-VQ1fhhJP.js.map → AdminDashboard-BafKaq50.js.map} +1 -1
  155. package/dashboard/dist/assets/{AgentConfigPage-BOe_W2Ij.js → AgentConfigPage-CFHf31DT.js} +6 -6
  156. package/dashboard/dist/assets/{AgentConfigPage-BOe_W2Ij.js.map → AgentConfigPage-CFHf31DT.js.map} +1 -1
  157. package/dashboard/dist/assets/{AgentDetailPage-5ov5D9V2.js → AgentDetailPage-CEvz5rJr.js} +3 -3
  158. package/dashboard/dist/assets/{AgentDetailPage-5ov5D9V2.js.map → AgentDetailPage-CEvz5rJr.js.map} +1 -1
  159. package/dashboard/dist/assets/{AgentsPage-DDFMKHhI.js → AgentsPage-DjQaio0O.js} +2 -2
  160. package/dashboard/dist/assets/{AgentsPage-DDFMKHhI.js.map → AgentsPage-DjQaio0O.js.map} +1 -1
  161. package/dashboard/dist/assets/AvailableEscalationsPage-UB03VTJx.js +2 -0
  162. package/dashboard/dist/assets/AvailableEscalationsPage-UB03VTJx.js.map +1 -0
  163. package/dashboard/dist/assets/{BotPicker-Dqpo36dM.js → BotPicker-C7MNWOF7.js} +2 -2
  164. package/dashboard/dist/assets/{BotPicker-Dqpo36dM.js.map → BotPicker-C7MNWOF7.js.map} +1 -1
  165. package/dashboard/dist/assets/BulkAssignModal-h5fX-_cV.js +2 -0
  166. package/dashboard/dist/assets/{BulkAssignModal-CmLVxkz-.js.map → BulkAssignModal-h5fX-_cV.js.map} +1 -1
  167. package/dashboard/dist/assets/{CapabilitiesPage-Dfkip6_b.js → CapabilitiesPage-BIzDl-ZT.js} +2 -2
  168. package/dashboard/dist/assets/{CapabilitiesPage-Dfkip6_b.js.map → CapabilitiesPage-BIzDl-ZT.js.map} +1 -1
  169. package/dashboard/dist/assets/{CollapsibleSection-CYNYMYSa.js → CollapsibleSection-B5TivtEg.js} +2 -2
  170. package/dashboard/dist/assets/{CollapsibleSection-CYNYMYSa.js.map → CollapsibleSection-B5TivtEg.js.map} +1 -1
  171. package/dashboard/dist/assets/{ConfirmDeleteModal-CpJNP-8w.js → ConfirmDeleteModal-CB4nKePb.js} +2 -2
  172. package/dashboard/dist/assets/{ConfirmDeleteModal-CpJNP-8w.js.map → ConfirmDeleteModal-CB4nKePb.js.map} +1 -1
  173. package/dashboard/dist/assets/{ConnectedAppsPage-DXIp2Dpl.js → ConnectedAppsPage-B3S5gVWw.js} +2 -2
  174. package/dashboard/dist/assets/{ConnectedAppsPage-DXIp2Dpl.js.map → ConnectedAppsPage-B3S5gVWw.js.map} +1 -1
  175. package/dashboard/dist/assets/{CountdownTimer-CuPY7zkb.js → CountdownTimer-BHVIq3kA.js} +2 -2
  176. package/dashboard/dist/assets/{CountdownTimer-CuPY7zkb.js.map → CountdownTimer-BHVIq3kA.js.map} +1 -1
  177. package/dashboard/dist/assets/CredentialConfirmModal-w0AGmCIe.js +2 -0
  178. package/dashboard/dist/assets/CredentialConfirmModal-w0AGmCIe.js.map +1 -0
  179. package/dashboard/dist/assets/{CredentialsPage-CtrnkHcj.js → CredentialsPage-BtCGBJrT.js} +2 -2
  180. package/dashboard/dist/assets/{CredentialsPage-CtrnkHcj.js.map → CredentialsPage-BtCGBJrT.js.map} +1 -1
  181. package/dashboard/dist/assets/{CronLabel-DVjTw5Ri.js → CronLabel-BG1rZK-5.js} +2 -2
  182. package/dashboard/dist/assets/{CronLabel-DVjTw5Ri.js.map → CronLabel-BG1rZK-5.js.map} +1 -1
  183. package/dashboard/dist/assets/{DropZone-DefYTYQy.js → DropZone-D8ZVdEb5.js} +2 -2
  184. package/dashboard/dist/assets/{DropZone-DefYTYQy.js.map → DropZone-D8ZVdEb5.js.map} +1 -1
  185. package/dashboard/dist/assets/{ElapsedCell-gY_mGMTF.js → ElapsedCell-l4m8Yvfv.js} +2 -2
  186. package/dashboard/dist/assets/{ElapsedCell-gY_mGMTF.js.map → ElapsedCell-l4m8Yvfv.js.map} +1 -1
  187. package/dashboard/dist/assets/{EntityTimelinePanel-DJN_ZEQa.js → EntityTimelinePanel-BOHlIaxF.js} +2 -2
  188. package/dashboard/dist/assets/{EntityTimelinePanel-DJN_ZEQa.js.map → EntityTimelinePanel-BOHlIaxF.js.map} +1 -1
  189. package/dashboard/dist/assets/{EscalationListSchemaPage-A4KpTdJA.js → EscalationListSchemaPage-BotlwlX3.js} +3 -3
  190. package/dashboard/dist/assets/{EscalationListSchemaPage-A4KpTdJA.js.map → EscalationListSchemaPage-BotlwlX3.js.map} +1 -1
  191. package/dashboard/dist/assets/{EscalationSchemaPage-nfCn42hT.js → EscalationSchemaPage-CfZSVOSD.js} +3 -3
  192. package/dashboard/dist/assets/{EscalationSchemaPage-nfCn42hT.js.map → EscalationSchemaPage-CfZSVOSD.js.map} +1 -1
  193. package/dashboard/dist/assets/{EscalationTimeline-CLvSr1SD.js → EscalationTimeline-CCKpuRBU.js} +2 -2
  194. package/dashboard/dist/assets/{EscalationTimeline-CLvSr1SD.js.map → EscalationTimeline-CCKpuRBU.js.map} +1 -1
  195. package/dashboard/dist/assets/{EscalationTitleSelect-CT7mYHkb.js → EscalationTitleSelect-cpTzPHiW.js} +2 -2
  196. package/dashboard/dist/assets/{EscalationTitleSelect-CT7mYHkb.js.map → EscalationTitleSelect-cpTzPHiW.js.map} +1 -1
  197. package/dashboard/dist/assets/{EscalationsOverview-CKaGRh-E.js → EscalationsOverview-DS-nK0qV.js} +2 -2
  198. package/dashboard/dist/assets/{EscalationsOverview-CKaGRh-E.js.map → EscalationsOverview-DS-nK0qV.js.map} +1 -1
  199. package/dashboard/dist/assets/{EventTable-2ywJsOLy.js → EventTable-UfebhJf0.js} +2 -2
  200. package/dashboard/dist/assets/{EventTable-2ywJsOLy.js.map → EventTable-UfebhJf0.js.map} +1 -1
  201. package/dashboard/dist/assets/{FilterBar-Dy0b7wwf.js → FilterBar-newA5l-X.js} +2 -2
  202. package/dashboard/dist/assets/{FilterBar-Dy0b7wwf.js.map → FilterBar-newA5l-X.js.map} +1 -1
  203. package/dashboard/dist/assets/{GraphInvokePage-yOVpraAy.js → GraphInvokePage-6SCDyQcK.js} +2 -2
  204. package/dashboard/dist/assets/{GraphInvokePage-yOVpraAy.js.map → GraphInvokePage-6SCDyQcK.js.map} +1 -1
  205. package/dashboard/dist/assets/HomePage-BEc-XdKj.js +2 -0
  206. package/dashboard/dist/assets/{HomePage-67xXjKjN.js.map → HomePage-BEc-XdKj.js.map} +1 -1
  207. package/dashboard/dist/assets/LabelJobsPage-BO50zXT5.js +2 -0
  208. package/dashboard/dist/assets/LabelJobsPage-BO50zXT5.js.map +1 -0
  209. package/dashboard/dist/assets/{LinePage-CMsYtukW.js → LinePage-NYxJQ-ex.js} +3 -3
  210. package/dashboard/dist/assets/{LinePage-CMsYtukW.js.map → LinePage-NYxJQ-ex.js.map} +1 -1
  211. package/dashboard/dist/assets/{LinesPage-pwL46mOS.js → LinesPage-RbWTaQRh.js} +3 -3
  212. package/dashboard/dist/assets/{LinesPage-pwL46mOS.js.map → LinesPage-RbWTaQRh.js.map} +1 -1
  213. package/dashboard/dist/assets/{ListToolbar-CJmvGy2B.js → ListToolbar-B-E9-toh.js} +2 -2
  214. package/dashboard/dist/assets/{ListToolbar-CJmvGy2B.js.map → ListToolbar-B-E9-toh.js.map} +1 -1
  215. package/dashboard/dist/assets/{McpOverview-DA9LLvNG.js → McpOverview-Shcf9QhZ.js} +2 -2
  216. package/dashboard/dist/assets/{McpOverview-DA9LLvNG.js.map → McpOverview-Shcf9QhZ.js.map} +1 -1
  217. package/dashboard/dist/assets/{McpQueryDetailPage-N7qLmfE8.js → McpQueryDetailPage-D8kmjy2v.js} +3 -3
  218. package/dashboard/dist/assets/{McpQueryDetailPage-N7qLmfE8.js.map → McpQueryDetailPage-D8kmjy2v.js.map} +1 -1
  219. package/dashboard/dist/assets/{McpQueryPage-CpsP3w7x.js → McpQueryPage-ck-o5m8A.js} +2 -2
  220. package/dashboard/dist/assets/{McpQueryPage-CpsP3w7x.js.map → McpQueryPage-ck-o5m8A.js.map} +1 -1
  221. package/dashboard/dist/assets/McpRunDetailPage-BJKkM9-3.js +2 -0
  222. package/dashboard/dist/assets/{McpRunDetailPage-qHRQ1pEY.js.map → McpRunDetailPage-BJKkM9-3.js.map} +1 -1
  223. package/dashboard/dist/assets/{McpRunsPage-BRDDYpTM.js → McpRunsPage-DqhHfD5G.js} +2 -2
  224. package/dashboard/dist/assets/{McpRunsPage-BRDDYpTM.js.map → McpRunsPage-DqhHfD5G.js.map} +1 -1
  225. package/dashboard/dist/assets/{NamespacePill-CeSfHH-j.js → NamespacePill--uIaGtrN.js} +2 -2
  226. package/dashboard/dist/assets/{NamespacePill-CeSfHH-j.js.map → NamespacePill--uIaGtrN.js.map} +1 -1
  227. package/dashboard/dist/assets/{OperationsPage-zUaab-CL.js → OperationsPage-D6B9Vv__.js} +3 -3
  228. package/dashboard/dist/assets/{OperationsPage-zUaab-CL.js.map → OperationsPage-D6B9Vv__.js.map} +1 -1
  229. package/dashboard/dist/assets/OperatorDashboard-CIH1cxjQ.js +2 -0
  230. package/dashboard/dist/assets/{OperatorDashboard-el5BquXN.js.map → OperatorDashboard-CIH1cxjQ.js.map} +1 -1
  231. package/dashboard/dist/assets/{PageHeader-DiGWAQlR.js → PageHeader-DClJ33fV.js} +2 -2
  232. package/dashboard/dist/assets/{PageHeader-DiGWAQlR.js.map → PageHeader-DClJ33fV.js.map} +1 -1
  233. package/dashboard/dist/assets/{PageHeaderWithStats-DwnKQ06j.js → PageHeaderWithStats-C-TrbKQL.js} +2 -2
  234. package/dashboard/dist/assets/{PageHeaderWithStats-DwnKQ06j.js.map → PageHeaderWithStats-C-TrbKQL.js.map} +1 -1
  235. package/dashboard/dist/assets/PersonaDetailPage-DjvfyIph.js +2 -0
  236. package/dashboard/dist/assets/{PersonaDetailPage-B_tb8gfl.js.map → PersonaDetailPage-DjvfyIph.js.map} +1 -1
  237. package/dashboard/dist/assets/{PersonasPage-BPI7mqM8.js → PersonasPage-D7ryA8Vc.js} +2 -2
  238. package/dashboard/dist/assets/{PersonasPage-BPI7mqM8.js.map → PersonasPage-D7ryA8Vc.js.map} +1 -1
  239. package/dashboard/dist/assets/{PortalPage-e_rBb1do.js → PortalPage-D3ybr0mo.js} +2 -2
  240. package/dashboard/dist/assets/{PortalPage-e_rBb1do.js.map → PortalPage-D3ybr0mo.js.map} +1 -1
  241. package/dashboard/dist/assets/PrintLabelsPanel-D0HF5x8b.js +2 -0
  242. package/dashboard/dist/assets/PrintLabelsPanel-D0HF5x8b.js.map +1 -0
  243. package/dashboard/dist/assets/{ProcessDetailPage-D35RX1bB.js → ProcessDetailPage-0xS9EdHe.js} +2 -2
  244. package/dashboard/dist/assets/{ProcessDetailPage-D35RX1bB.js.map → ProcessDetailPage-0xS9EdHe.js.map} +1 -1
  245. package/dashboard/dist/assets/{ProcessesListPage-CT81miI5.js → ProcessesListPage-93SMj1Fd.js} +2 -2
  246. package/dashboard/dist/assets/{ProcessesListPage-CT81miI5.js.map → ProcessesListPage-93SMj1Fd.js.map} +1 -1
  247. package/dashboard/dist/assets/{ResolverForm-BxJkoKAO.js → ResolverForm-DfogHDzl.js} +5 -5
  248. package/dashboard/dist/assets/{ResolverForm-BxJkoKAO.js.map → ResolverForm-DfogHDzl.js.map} +1 -1
  249. package/dashboard/dist/assets/RoleDetailPage-DlUOJWSB.js +8 -0
  250. package/dashboard/dist/assets/{RoleDetailPage-DtU7vsJm.js.map → RoleDetailPage-DlUOJWSB.js.map} +1 -1
  251. package/dashboard/dist/assets/{RolePill-D3hkAdKf.js → RolePill-BjD1Qw5y.js} +2 -2
  252. package/dashboard/dist/assets/{RolePill-D3hkAdKf.js.map → RolePill-BjD1Qw5y.js.map} +1 -1
  253. package/dashboard/dist/assets/RolesPage-DqhHSvaw.js +2 -0
  254. package/dashboard/dist/assets/{RolesPage-BE7RWt9q.js.map → RolesPage-DqhHSvaw.js.map} +1 -1
  255. package/dashboard/dist/assets/{RosterWidget-BJMx0a8Z.js → RosterWidget-BrIq61HR.js} +2 -2
  256. package/dashboard/dist/assets/RosterWidget-BrIq61HR.js.map +1 -0
  257. package/dashboard/dist/assets/RunAsSelector-D6zQrCNr.js +2 -0
  258. package/dashboard/dist/assets/{RunAsSelector-Co6s9bqA.js.map → RunAsSelector-D6zQrCNr.js.map} +1 -1
  259. package/dashboard/dist/assets/ScanCodesPage-B2S9DR2f.js +2 -0
  260. package/dashboard/dist/assets/{ScanCodesPage-C2TETaj5.js.map → ScanCodesPage-B2S9DR2f.js.map} +1 -1
  261. package/dashboard/dist/assets/ScanSchemeDetailPage-gpxKN-pL.js +5 -0
  262. package/dashboard/dist/assets/ScanSchemeDetailPage-gpxKN-pL.js.map +1 -0
  263. package/dashboard/dist/assets/ScanStationPage-B-UTgXsG.js +2 -0
  264. package/dashboard/dist/assets/ScanStationPage-B-UTgXsG.js.map +1 -0
  265. package/dashboard/dist/assets/{SchemeMatchFields-9H7SmbED.js → SchemeMatchFields-BK24aa7K.js} +2 -2
  266. package/dashboard/dist/assets/{SchemeMatchFields-9H7SmbED.js.map → SchemeMatchFields-BK24aa7K.js.map} +1 -1
  267. package/dashboard/dist/assets/{ScopeBadge-DGz79rmG.js → ScopeBadge-ofLQ6HW-.js} +2 -2
  268. package/dashboard/dist/assets/{ScopeBadge-DGz79rmG.js.map → ScopeBadge-ofLQ6HW-.js.map} +1 -1
  269. package/dashboard/dist/assets/{StickyPagination-miUdVAKw.js → StickyPagination-CgVv7vc_.js} +2 -2
  270. package/dashboard/dist/assets/{StickyPagination-miUdVAKw.js.map → StickyPagination-CgVv7vc_.js.map} +1 -1
  271. package/dashboard/dist/assets/{StreamMessageDetail-D5Rk7pn7.js → StreamMessageDetail-B9sxDVw4.js} +2 -2
  272. package/dashboard/dist/assets/{StreamMessageDetail-D5Rk7pn7.js.map → StreamMessageDetail-B9sxDVw4.js.map} +1 -1
  273. package/dashboard/dist/assets/{SwimlaneTimeline-B1QY1ZzH.js → SwimlaneTimeline-DQc0CYSL.js} +2 -2
  274. package/dashboard/dist/assets/{SwimlaneTimeline-B1QY1ZzH.js.map → SwimlaneTimeline-DQc0CYSL.js.map} +1 -1
  275. package/dashboard/dist/assets/{TagInput-DaIdhv3h.js → TagInput-47qVYzke.js} +2 -2
  276. package/dashboard/dist/assets/{TagInput-DaIdhv3h.js.map → TagInput-47qVYzke.js.map} +1 -1
  277. package/dashboard/dist/assets/{TaskDetailPage-DKUM37o8.js → TaskDetailPage-D6VOsA1f.js} +2 -2
  278. package/dashboard/dist/assets/{TaskDetailPage-DKUM37o8.js.map → TaskDetailPage-D6VOsA1f.js.map} +1 -1
  279. package/dashboard/dist/assets/{TaskQueuePill-sUA1nZe-.js → TaskQueuePill-C5KmdXAl.js} +2 -2
  280. package/dashboard/dist/assets/{TaskQueuePill-sUA1nZe-.js.map → TaskQueuePill-C5KmdXAl.js.map} +1 -1
  281. package/dashboard/dist/assets/{TasksListPage-CUulwvm-.js → TasksListPage-DG8hvO8G.js} +2 -2
  282. package/dashboard/dist/assets/{TasksListPage-CUulwvm-.js.map → TasksListPage-DG8hvO8G.js.map} +1 -1
  283. package/dashboard/dist/assets/{TimeAgo-CHTelacB.js → TimeAgo-CL02EhFi.js} +2 -2
  284. package/dashboard/dist/assets/{TimeAgo-CHTelacB.js.map → TimeAgo-CL02EhFi.js.map} +1 -1
  285. package/dashboard/dist/assets/{TimestampCell-NanLWLKo.js → TimestampCell-BE7oKVJg.js} +2 -2
  286. package/dashboard/dist/assets/{TimestampCell-NanLWLKo.js.map → TimestampCell-BE7oKVJg.js.map} +1 -1
  287. package/dashboard/dist/assets/{ToolPill-CpBAFdEX.js → ToolPill-Cp9Drk1s.js} +2 -2
  288. package/dashboard/dist/assets/{ToolPill-CpBAFdEX.js.map → ToolPill-Cp9Drk1s.js.map} +1 -1
  289. package/dashboard/dist/assets/{ToolTestPanel-CF1CHzgz.js → ToolTestPanel-CRecLh6S.js} +2 -2
  290. package/dashboard/dist/assets/{ToolTestPanel-CF1CHzgz.js.map → ToolTestPanel-CRecLh6S.js.map} +1 -1
  291. package/dashboard/dist/assets/{TopicDetailPage-CGO8XV_a.js → TopicDetailPage-QzkinTfN.js} +3 -3
  292. package/dashboard/dist/assets/{TopicDetailPage-CGO8XV_a.js.map → TopicDetailPage-QzkinTfN.js.map} +1 -1
  293. package/dashboard/dist/assets/{TopicsPage-Bt8gOKYr.js → TopicsPage-D8dW6J65.js} +2 -2
  294. package/dashboard/dist/assets/{TopicsPage-Bt8gOKYr.js.map → TopicsPage-D8dW6J65.js.map} +1 -1
  295. package/dashboard/dist/assets/{UserName-DKXnK7DD.js → UserName-Dw3R_zrl.js} +2 -2
  296. package/dashboard/dist/assets/{UserName-DKXnK7DD.js.map → UserName-Dw3R_zrl.js.map} +1 -1
  297. package/dashboard/dist/assets/WorkflowExecutionPage-BTocv8or.js +2 -0
  298. package/dashboard/dist/assets/{WorkflowExecutionPage-Crf7AIFc.js.map → WorkflowExecutionPage-BTocv8or.js.map} +1 -1
  299. package/dashboard/dist/assets/{WorkflowPill-CKHip3hH.js → WorkflowPill-BiLYq__w.js} +2 -2
  300. package/dashboard/dist/assets/{WorkflowPill-CKHip3hH.js.map → WorkflowPill-BiLYq__w.js.map} +1 -1
  301. package/dashboard/dist/assets/WorkflowsDashboard-CIPEt3et.js +2 -0
  302. package/dashboard/dist/assets/{WorkflowsDashboard--isjt06C.js.map → WorkflowsDashboard-CIPEt3et.js.map} +1 -1
  303. package/dashboard/dist/assets/{WorkflowsOverview-D7Oqf4-V.js → WorkflowsOverview-Bj4MJIE-.js} +2 -2
  304. package/dashboard/dist/assets/{WorkflowsOverview-D7Oqf4-V.js.map → WorkflowsOverview-Bj4MJIE-.js.map} +1 -1
  305. package/dashboard/dist/assets/{YamlWorkflowDetailPage-B0wxgrGP.js → YamlWorkflowDetailPage-DMEk5k9-.js} +2 -2
  306. package/dashboard/dist/assets/{YamlWorkflowDetailPage-B0wxgrGP.js.map → YamlWorkflowDetailPage-DMEk5k9-.js.map} +1 -1
  307. package/dashboard/dist/assets/{YamlWorkflowsPage-BVdLnm6K.js → YamlWorkflowsPage-F2yyyoiB.js} +2 -2
  308. package/dashboard/dist/assets/{YamlWorkflowsPage-BVdLnm6K.js.map → YamlWorkflowsPage-F2yyyoiB.js.map} +1 -1
  309. package/dashboard/dist/assets/{agents-CHjbjHXA.js → agents-mSgxEPpI.js} +2 -2
  310. package/dashboard/dist/assets/{agents-CHjbjHXA.js.map → agents-mSgxEPpI.js.map} +1 -1
  311. package/dashboard/dist/assets/{bots-WrUhEPWs.js → bots-p4Hp0CV6.js} +2 -2
  312. package/dashboard/dist/assets/{bots-WrUhEPWs.js.map → bots-p4Hp0CV6.js.map} +1 -1
  313. package/dashboard/dist/assets/{capabilities-CunMmkj8.js → capabilities-D_-I_SXR.js} +2 -2
  314. package/dashboard/dist/assets/{capabilities-CunMmkj8.js.map → capabilities-D_-I_SXR.js.map} +1 -1
  315. package/dashboard/dist/assets/{controlplane-D6IZIZoh.js → controlplane-DjdoQnRU.js} +2 -2
  316. package/dashboard/dist/assets/{controlplane-D6IZIZoh.js.map → controlplane-DjdoQnRU.js.map} +1 -1
  317. package/dashboard/dist/assets/{escalation-columns-Bvo6JI4Z.js → escalation-columns-R_IEVQjQ.js} +2 -2
  318. package/dashboard/dist/assets/{escalation-columns-Bvo6JI4Z.js.map → escalation-columns-R_IEVQjQ.js.map} +1 -1
  319. package/dashboard/dist/assets/{escalation-items-DFGRwo49.js → escalation-items-BGZlJMpk.js} +2 -2
  320. package/dashboard/dist/assets/{escalation-items-DFGRwo49.js.map → escalation-items-BGZlJMpk.js.map} +1 -1
  321. package/dashboard/dist/assets/{index-OuKQ_0eB.js → index-BEMdD58x.js} +2 -2
  322. package/dashboard/dist/assets/{index-OuKQ_0eB.js.map → index-BEMdD58x.js.map} +1 -1
  323. package/dashboard/dist/assets/index-BUmz0NMg.css +1 -0
  324. package/dashboard/dist/assets/{index-C7EIu1mG.js → index-BuCEhhnl.js} +2 -2
  325. package/dashboard/dist/assets/{index-C7EIu1mG.js.map → index-BuCEhhnl.js.map} +1 -1
  326. package/dashboard/dist/assets/{index-Bo8QU31k.js → index-BxbQmeqh.js} +2 -2
  327. package/dashboard/dist/assets/{index-Bo8QU31k.js.map → index-BxbQmeqh.js.map} +1 -1
  328. package/dashboard/dist/assets/index-C5osluV3.js +2 -0
  329. package/dashboard/dist/assets/{index-DRK7M1_9.js.map → index-C5osluV3.js.map} +1 -1
  330. package/dashboard/dist/assets/{index-ygxyAKcc.js → index-C_dAJUzh.js} +27 -27
  331. package/dashboard/dist/assets/index-C_dAJUzh.js.map +1 -0
  332. package/dashboard/dist/assets/{index-DRw0FXdX.js → index-D-FYj9jT.js} +2 -2
  333. package/dashboard/dist/assets/{index-DRw0FXdX.js.map → index-D-FYj9jT.js.map} +1 -1
  334. package/dashboard/dist/assets/{index-B8qrHwp9.js → index-D6K_IgCz.js} +2 -2
  335. package/dashboard/dist/assets/{index-B8qrHwp9.js.map → index-D6K_IgCz.js.map} +1 -1
  336. package/dashboard/dist/assets/index-D7L_VoBD.js +2 -0
  337. package/dashboard/dist/assets/index-D7L_VoBD.js.map +1 -0
  338. package/dashboard/dist/assets/{index-BZFSXEkr.js → index-D9RAN9sA.js} +2 -2
  339. package/dashboard/dist/assets/{index-BZFSXEkr.js.map → index-D9RAN9sA.js.map} +1 -1
  340. package/dashboard/dist/assets/{index-DCJ483vt.js → index-DUYAGxwc.js} +2 -2
  341. package/dashboard/dist/assets/{index-DCJ483vt.js.map → index-DUYAGxwc.js.map} +1 -1
  342. package/dashboard/dist/assets/{index-boT612Jo.js → index-Dk2KKo0z.js} +2 -2
  343. package/dashboard/dist/assets/{index-boT612Jo.js.map → index-Dk2KKo0z.js.map} +1 -1
  344. package/dashboard/dist/assets/{index-CpCchhQv.js → index-DlJkZ12b.js} +2 -2
  345. package/dashboard/dist/assets/{index-CpCchhQv.js.map → index-DlJkZ12b.js.map} +1 -1
  346. package/dashboard/dist/assets/index-FiNThtNU.js +3 -0
  347. package/dashboard/dist/assets/index-FiNThtNU.js.map +1 -0
  348. package/dashboard/dist/assets/{knowledge-BubJBTwg.js → knowledge-DSsPTqkK.js} +2 -2
  349. package/dashboard/dist/assets/{knowledge-BubJBTwg.js.map → knowledge-DSsPTqkK.js.map} +1 -1
  350. package/dashboard/dist/assets/{machines-DSm5NFgs.js → machines-DSgGtqqr.js} +2 -2
  351. package/dashboard/dist/assets/{machines-DSm5NFgs.js.map → machines-DSgGtqqr.js.map} +1 -1
  352. package/dashboard/dist/assets/{mcp-D6MckqOV.js → mcp-Cg-_uLw8.js} +2 -2
  353. package/dashboard/dist/assets/{mcp-D6MckqOV.js.map → mcp-Cg-_uLw8.js.map} +1 -1
  354. package/dashboard/dist/assets/{mcp-query-VyCdOag6.js → mcp-query-DQombEK_.js} +2 -2
  355. package/dashboard/dist/assets/{mcp-query-VyCdOag6.js.map → mcp-query-DQombEK_.js.map} +1 -1
  356. package/dashboard/dist/assets/{personas-CyJc10xe.js → personas-DYrcoVBu.js} +2 -2
  357. package/dashboard/dist/assets/{personas-CyJc10xe.js.map → personas-DYrcoVBu.js.map} +1 -1
  358. package/dashboard/dist/assets/{pipelines-B2inDtx3.js → pipelines-wdb4xsey.js} +2 -2
  359. package/dashboard/dist/assets/{pipelines-B2inDtx3.js.map → pipelines-wdb4xsey.js.map} +1 -1
  360. package/dashboard/dist/assets/{tasks-DOP4d3M6.js → tasks-Bil7gvm9.js} +2 -2
  361. package/dashboard/dist/assets/{tasks-DOP4d3M6.js.map → tasks-Bil7gvm9.js.map} +1 -1
  362. package/dashboard/dist/assets/{topics-CNOjSWpX.js → topics-wBqaVhRR.js} +2 -2
  363. package/dashboard/dist/assets/{topics-CNOjSWpX.js.map → topics-wBqaVhRR.js.map} +1 -1
  364. package/dashboard/dist/assets/{useDebouncedValue-CgO0TGMd.js → useDebouncedValue-CwrY8TpV.js} +2 -2
  365. package/dashboard/dist/assets/{useDebouncedValue-CgO0TGMd.js.map → useDebouncedValue-CwrY8TpV.js.map} +1 -1
  366. package/dashboard/dist/assets/{useNamespace-BWHvUKtK.js → useNamespace-Sod_xApz.js} +2 -2
  367. package/dashboard/dist/assets/{useNamespace-BWHvUKtK.js.map → useNamespace-Sod_xApz.js.map} +1 -1
  368. package/dashboard/dist/assets/{useYamlActivityEvents-BZQ__ZGD.js → useYamlActivityEvents-CKLInV3C.js} +2 -2
  369. package/dashboard/dist/assets/{useYamlActivityEvents-BZQ__ZGD.js.map → useYamlActivityEvents-CKLInV3C.js.map} +1 -1
  370. package/dashboard/dist/assets/{validate-CAin6WM_.js → validate-CYhW1xMJ.js} +2 -2
  371. package/dashboard/dist/assets/{validate-CAin6WM_.js.map → validate-CYhW1xMJ.js.map} +1 -1
  372. package/dashboard/dist/assets/{vendor-icons-en0_uSRD.js → vendor-icons-ed7mYQw4.js} +2 -2
  373. package/dashboard/dist/assets/{vendor-icons-en0_uSRD.js.map → vendor-icons-ed7mYQw4.js.map} +1 -1
  374. package/dashboard/dist/assets/{yaml-workflows-Dyln3od-.js → yaml-workflows-C20qHo3L.js} +2 -2
  375. package/dashboard/dist/assets/{yaml-workflows-Dyln3od-.js.map → yaml-workflows-C20qHo3L.js.map} +1 -1
  376. package/dashboard/dist/index.html +3 -3
  377. package/docs/api/http/escalations.md +15 -0
  378. package/docs/api/http/labels.md +115 -0
  379. package/docs/api/http/users.md +2 -0
  380. package/docs/api/mcp/admin.md +76 -1
  381. package/docs/code-owned-configuration.md +1 -1
  382. package/docs/dashboard.md +2 -2
  383. package/docs/hitl/x-lt-roster.md +2 -0
  384. package/docs/hitl/x-lt-scan.md +12 -0
  385. package/docs/iam.md +12 -0
  386. package/docs/label-sheets.md +267 -0
  387. package/docs/scan-codes.md +93 -9
  388. package/package.json +4 -1
  389. package/dashboard/dist/assets/AvailableEscalationsPage-GFn8Sgg1.js +0 -2
  390. package/dashboard/dist/assets/AvailableEscalationsPage-GFn8Sgg1.js.map +0 -1
  391. package/dashboard/dist/assets/BulkAssignModal-CmLVxkz-.js +0 -2
  392. package/dashboard/dist/assets/HomePage-67xXjKjN.js +0 -2
  393. package/dashboard/dist/assets/McpRunDetailPage-qHRQ1pEY.js +0 -2
  394. package/dashboard/dist/assets/OperatorDashboard-el5BquXN.js +0 -2
  395. package/dashboard/dist/assets/PersonaDetailPage-B_tb8gfl.js +0 -2
  396. package/dashboard/dist/assets/RoleDetailPage-DtU7vsJm.js +0 -8
  397. package/dashboard/dist/assets/RolesPage-BE7RWt9q.js +0 -2
  398. package/dashboard/dist/assets/RosterWidget-BJMx0a8Z.js.map +0 -1
  399. package/dashboard/dist/assets/RunAsSelector-Co6s9bqA.js +0 -2
  400. package/dashboard/dist/assets/ScanCodesPage-C2TETaj5.js +0 -2
  401. package/dashboard/dist/assets/ScanSchemeDetailPage-C2UgU8l-.js +0 -5
  402. package/dashboard/dist/assets/ScanSchemeDetailPage-C2UgU8l-.js.map +0 -1
  403. package/dashboard/dist/assets/ScanStationPage-nWYz-OD3.js +0 -2
  404. package/dashboard/dist/assets/ScanStationPage-nWYz-OD3.js.map +0 -1
  405. package/dashboard/dist/assets/WorkflowExecutionPage-Crf7AIFc.js +0 -2
  406. package/dashboard/dist/assets/WorkflowsDashboard--isjt06C.js +0 -2
  407. package/dashboard/dist/assets/index-B95LbYhW.js +0 -2
  408. package/dashboard/dist/assets/index-B95LbYhW.js.map +0 -1
  409. package/dashboard/dist/assets/index-BmzscsXK.css +0 -1
  410. package/dashboard/dist/assets/index-DRK7M1_9.js +0 -2
  411. package/dashboard/dist/assets/index-P-DlgAWj.js +0 -3
  412. package/dashboard/dist/assets/index-P-DlgAWj.js.map +0 -1
  413. package/dashboard/dist/assets/index-ygxyAKcc.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import type { LTUserRecord, LTRoleType, LTUserStatus } from '../../types';
2
- import type { CreateUserInput, UpdateUserInput, UserPropertyOps } from './types';
2
+ import type { CreateUserInput, PropertyPatchRecord, UpdateUserInput, UserPropertyOps } from './types';
3
3
  export declare function attachRoles(user: any): Promise<LTUserRecord>;
4
4
  export declare function createUser(input: CreateUserInput): Promise<LTUserRecord>;
5
5
  export declare function getUser(id: string): Promise<LTUserRecord | null>;
@@ -56,6 +56,18 @@ export declare function getIdentityPropertyKeys(): Promise<string[]>;
56
56
  * A tripped guard raises UserPropertyConflictError (409).
57
57
  */
58
58
  export declare function patchUserProperties(id: string, ops: UserPropertyOps): Promise<LTUserRecord | null>;
59
+ /**
60
+ * The identity keys a patch touches: set, removed, or renamed from or to.
61
+ */
62
+ export declare function identityKeysTouched(ops: UserPropertyOps): Promise<string[]>;
63
+ /**
64
+ * patchUserProperties, with an lt_label_jobs row written by the same
65
+ * statement. Returns the user and the ledger row id; null for an unknown user.
66
+ */
67
+ export declare function patchUserPropertiesRecorded(id: string, ops: UserPropertyOps, record: PropertyPatchRecord): Promise<{
68
+ user: LTUserRecord;
69
+ jobId: string;
70
+ } | null>;
59
71
  export declare function deleteUser(id: string): Promise<boolean>;
60
72
  export declare function listUsers(filters: {
61
73
  role?: string;
@@ -14,6 +14,8 @@ exports.getUserByMetadataValue = getUserByMetadataValue;
14
14
  exports.updateUser = updateUser;
15
15
  exports.getIdentityPropertyKeys = getIdentityPropertyKeys;
16
16
  exports.patchUserProperties = patchUserProperties;
17
+ exports.identityKeysTouched = identityKeysTouched;
18
+ exports.patchUserPropertiesRecorded = patchUserPropertiesRecorded;
17
19
  exports.deleteUser = deleteUser;
18
20
  exports.listUsers = listUsers;
19
21
  const bcryptjs_1 = __importDefault(require("bcryptjs"));
@@ -159,8 +161,12 @@ async function updateUser(id, input) {
159
161
  values.push(await bcryptjs_1.default.hash(input.password, 10));
160
162
  }
161
163
  if (input.metadata !== undefined) {
162
- sets.push(`metadata = $${idx++}`);
163
- values.push(JSON.stringify(input.metadata));
164
+ // Identity bindings the replacement leaves out are kept, read in the same statement.
165
+ const keysIdx = idx++;
166
+ const metadataIdx = idx++;
167
+ sets.push(`metadata = COALESCE((SELECT jsonb_object_agg(k, lt_users.metadata -> k) FROM unnest($${keysIdx}::text[]) AS k ` +
168
+ `WHERE COALESCE(lt_users.metadata, '{}'::jsonb) ? k), '{}'::jsonb) || $${metadataIdx}::jsonb`);
169
+ values.push(await getIdentityPropertyKeys(), JSON.stringify(input.metadata));
164
170
  }
165
171
  if (sets.length === 0) {
166
172
  return getUser(id);
@@ -215,6 +221,29 @@ async function getIdentityPropertyKeys() {
215
221
  * A tripped guard raises UserPropertyConflictError (409).
216
222
  */
217
223
  async function patchUserProperties(id, ops) {
224
+ const patched = await runPropertyPatch(id, ops, null);
225
+ return patched?.user ?? null;
226
+ }
227
+ /**
228
+ * The identity keys a patch touches: set, removed, or renamed from or to.
229
+ */
230
+ async function identityKeysTouched(ops) {
231
+ const keys = new Set([
232
+ ...Object.keys(ops.set ?? {}),
233
+ ...(ops.remove ?? []),
234
+ ...Object.entries(ops.rename ?? {}).flat(),
235
+ ]);
236
+ return (await getIdentityPropertyKeys()).filter((k) => keys.has(k));
237
+ }
238
+ /**
239
+ * patchUserProperties, with an lt_label_jobs row written by the same
240
+ * statement. Returns the user and the ledger row id; null for an unknown user.
241
+ */
242
+ async function patchUserPropertiesRecorded(id, ops, record) {
243
+ const patched = await runPropertyPatch(id, ops, record);
244
+ return patched ? { user: patched.user, jobId: patched.jobId } : null;
245
+ }
246
+ async function runPropertyPatch(id, ops, record) {
218
247
  const set = ops.set ?? {};
219
248
  const remove = ops.remove ?? [];
220
249
  const rename = ops.rename ?? {};
@@ -258,18 +287,20 @@ async function patchUserProperties(id, ops) {
258
287
  const removeAll = [...new Set([...remove, ...renameEntries.map(([prev]) => prev)])];
259
288
  const renamePairs = renameEntries.map(([prev, next]) => ({ prev, next }));
260
289
  const pool = (0, db_1.getPool)();
261
- const { rows } = await pool.query(sql_1.PATCH_USER_PROPERTIES, [
262
- id,
263
- removeAll,
264
- JSON.stringify(renamePairs),
265
- JSON.stringify(set),
266
- JSON.stringify(guard),
267
- ]);
268
- if (rows[0])
269
- return attachRoles(rows[0]);
290
+ const params = [id, removeAll, JSON.stringify(renamePairs), JSON.stringify(set), JSON.stringify(guard)];
291
+ const { rows } = record
292
+ ? await pool.query(sql_1.PATCH_USER_PROPERTIES_RECORDED, [...params, JSON.stringify(record)])
293
+ : await pool.query(sql_1.PATCH_USER_PROPERTIES, params);
294
+ if (rows[0]) {
295
+ const { ledger_id: jobId = null, ...row } = rows[0];
296
+ return { user: await attachRoles(row), jobId };
297
+ }
270
298
  const exists = await pool.query(sql_1.VERIFY_USER_BY_ID, [id]);
271
299
  if (!exists.rows[0])
272
300
  return null;
301
+ if (record?.require_active && exists.rows[0].status !== 'active') {
302
+ throw new UserPropertyValidationError('identity bindings are issued to active users only');
303
+ }
273
304
  // The user exists, so the identity guard is what blocked the write. Name
274
305
  // the first guarded key for the caller; the row was not touched.
275
306
  throw new UserPropertyConflictError(Object.keys(guard)[0] ?? 'identity binding');
@@ -1,5 +1,6 @@
1
- export { CreateUserInput, UpdateUserInput, UserPropertyOps, VALID_ROLE_TYPES } from './types';
2
- export { createUser, getUser, getUserNames, UserNameRecord, getUserByExternalId, getUserByEmail, getUserByMetadataValue, updateUser, patchUserProperties, getIdentityPropertyKeys, UserPropertyConflictError, UserPropertyValidationError, deleteUser, listUsers, } from './crud';
1
+ export { CreateUserInput, PropertyPatchRecord, UpdateUserInput, UserPropertyOps, VALID_ROLE_TYPES } from './types';
2
+ export { redactIdentityKeys, setIdentityKeyProtection, identityKeysProtected } from './redact';
3
+ export { createUser, getUser, getUserNames, UserNameRecord, getUserByExternalId, getUserByEmail, getUserByMetadataValue, updateUser, patchUserProperties, patchUserPropertiesRecorded, identityKeysTouched, getIdentityPropertyKeys, UserPropertyConflictError, UserPropertyValidationError, deleteUser, listUsers, } from './crud';
3
4
  export { isValidRoleType, addUserRole, grantRoleIfAbsent, removeUserRole, getUserRoles, hasRole, hasRoleType, } from './roles';
4
5
  export { isSuperAdmin, isGroupAdmin, canManageRole, hasGlobalEscalationAccess, hasRolesAsAdmin, getRoleScope, getRoleWriteScope } from './rbac';
5
6
  export { READ_SCOPES, WRITE_SCOPES, DEFAULT_READ_SCOPE, DEFAULT_WRITE_SCOPE, isValidReadScope, isValidWriteScope, isValidScopePair, effectiveScope, } from './scope';
@@ -1,8 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.PREFERENCES_MAX_BYTES = exports.patchPreferences = exports.getPreferences = exports.seedAdmin = exports.verifyPassword = exports.effectiveScope = exports.isValidScopePair = exports.isValidWriteScope = exports.isValidReadScope = exports.DEFAULT_WRITE_SCOPE = exports.DEFAULT_READ_SCOPE = exports.WRITE_SCOPES = exports.READ_SCOPES = exports.getRoleWriteScope = exports.getRoleScope = exports.hasRolesAsAdmin = exports.hasGlobalEscalationAccess = exports.canManageRole = exports.isGroupAdmin = exports.isSuperAdmin = exports.hasRoleType = exports.hasRole = exports.getUserRoles = exports.removeUserRole = exports.grantRoleIfAbsent = exports.addUserRole = exports.isValidRoleType = exports.listUsers = exports.deleteUser = exports.UserPropertyValidationError = exports.UserPropertyConflictError = exports.getIdentityPropertyKeys = exports.patchUserProperties = exports.updateUser = exports.getUserByMetadataValue = exports.getUserByEmail = exports.getUserByExternalId = exports.getUserNames = exports.getUser = exports.createUser = exports.VALID_ROLE_TYPES = void 0;
3
+ exports.PREFERENCES_MAX_BYTES = exports.patchPreferences = exports.getPreferences = exports.seedAdmin = exports.verifyPassword = exports.effectiveScope = exports.isValidScopePair = exports.isValidWriteScope = exports.isValidReadScope = exports.DEFAULT_WRITE_SCOPE = exports.DEFAULT_READ_SCOPE = exports.WRITE_SCOPES = exports.READ_SCOPES = exports.getRoleWriteScope = exports.getRoleScope = exports.hasRolesAsAdmin = exports.hasGlobalEscalationAccess = exports.canManageRole = exports.isGroupAdmin = exports.isSuperAdmin = exports.hasRoleType = exports.hasRole = exports.getUserRoles = exports.removeUserRole = exports.grantRoleIfAbsent = exports.addUserRole = exports.isValidRoleType = exports.listUsers = exports.deleteUser = exports.UserPropertyValidationError = exports.UserPropertyConflictError = exports.getIdentityPropertyKeys = exports.identityKeysTouched = exports.patchUserPropertiesRecorded = exports.patchUserProperties = exports.updateUser = exports.getUserByMetadataValue = exports.getUserByEmail = exports.getUserByExternalId = exports.getUserNames = exports.getUser = exports.createUser = exports.identityKeysProtected = exports.setIdentityKeyProtection = exports.redactIdentityKeys = exports.VALID_ROLE_TYPES = void 0;
4
4
  var types_1 = require("./types");
5
5
  Object.defineProperty(exports, "VALID_ROLE_TYPES", { enumerable: true, get: function () { return types_1.VALID_ROLE_TYPES; } });
6
+ var redact_1 = require("./redact");
7
+ Object.defineProperty(exports, "redactIdentityKeys", { enumerable: true, get: function () { return redact_1.redactIdentityKeys; } });
8
+ Object.defineProperty(exports, "setIdentityKeyProtection", { enumerable: true, get: function () { return redact_1.setIdentityKeyProtection; } });
9
+ Object.defineProperty(exports, "identityKeysProtected", { enumerable: true, get: function () { return redact_1.identityKeysProtected; } });
6
10
  var crud_1 = require("./crud");
7
11
  Object.defineProperty(exports, "createUser", { enumerable: true, get: function () { return crud_1.createUser; } });
8
12
  Object.defineProperty(exports, "getUser", { enumerable: true, get: function () { return crud_1.getUser; } });
@@ -12,6 +16,8 @@ Object.defineProperty(exports, "getUserByEmail", { enumerable: true, get: functi
12
16
  Object.defineProperty(exports, "getUserByMetadataValue", { enumerable: true, get: function () { return crud_1.getUserByMetadataValue; } });
13
17
  Object.defineProperty(exports, "updateUser", { enumerable: true, get: function () { return crud_1.updateUser; } });
14
18
  Object.defineProperty(exports, "patchUserProperties", { enumerable: true, get: function () { return crud_1.patchUserProperties; } });
19
+ Object.defineProperty(exports, "patchUserPropertiesRecorded", { enumerable: true, get: function () { return crud_1.patchUserPropertiesRecorded; } });
20
+ Object.defineProperty(exports, "identityKeysTouched", { enumerable: true, get: function () { return crud_1.identityKeysTouched; } });
15
21
  Object.defineProperty(exports, "getIdentityPropertyKeys", { enumerable: true, get: function () { return crud_1.getIdentityPropertyKeys; } });
16
22
  Object.defineProperty(exports, "UserPropertyConflictError", { enumerable: true, get: function () { return crud_1.UserPropertyConflictError; } });
17
23
  Object.defineProperty(exports, "UserPropertyValidationError", { enumerable: true, get: function () { return crud_1.UserPropertyValidationError; } });
@@ -0,0 +1,17 @@
1
+ import type { LTUserRecord } from '../../types';
2
+ /**
3
+ * Whether identity keys are protected: left out of user reads, refused in
4
+ * wholesale metadata writes, and written only with credentialIssuer. On by
5
+ * default; start({ labels: { protectIdentityKeys: false } }) turns it off.
6
+ */
7
+ export declare function setIdentityKeyProtection(on: boolean): void;
8
+ export declare function identityKeysProtected(): boolean;
9
+ /**
10
+ * A user record as a reader sees it. `identity_bound` names the identity keys
11
+ * the user carries; while protection is on, their values (badge tokens and
12
+ * other printed credentials) leave `metadata`, and printing a credential is
13
+ * the only path that reveals one.
14
+ */
15
+ export declare function redactIdentityKeys<T extends Pick<LTUserRecord, 'metadata'>>(user: T, identityKeys: string[]): T & {
16
+ identity_bound: string[];
17
+ };
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.setIdentityKeyProtection = setIdentityKeyProtection;
4
+ exports.identityKeysProtected = identityKeysProtected;
5
+ exports.redactIdentityKeys = redactIdentityKeys;
6
+ const config_1 = require("../../modules/config");
7
+ let protectIdentityKeys = config_1.config.LT_PROTECT_IDENTITY_KEYS;
8
+ /**
9
+ * Whether identity keys are protected: left out of user reads, refused in
10
+ * wholesale metadata writes, and written only with credentialIssuer. On by
11
+ * default; start({ labels: { protectIdentityKeys: false } }) turns it off.
12
+ */
13
+ function setIdentityKeyProtection(on) {
14
+ protectIdentityKeys = on;
15
+ }
16
+ function identityKeysProtected() {
17
+ return protectIdentityKeys;
18
+ }
19
+ /**
20
+ * A user record as a reader sees it. `identity_bound` names the identity keys
21
+ * the user carries; while protection is on, their values (badge tokens and
22
+ * other printed credentials) leave `metadata`, and printing a credential is
23
+ * the only path that reveals one.
24
+ */
25
+ function redactIdentityKeys(user, identityKeys) {
26
+ const metadata = user.metadata ? { ...user.metadata } : user.metadata;
27
+ const bound = identityKeys.filter((key) => !!metadata && key in metadata);
28
+ if (metadata && protectIdentityKeys)
29
+ for (const key of bound)
30
+ delete metadata[key];
31
+ return { ...user, metadata, identity_bound: bound };
32
+ }
@@ -33,23 +33,16 @@ export declare const GET_NAMES_BY_IDS = "SELECT id, display_name, external_id, e
33
33
  */
34
34
  export declare const GET_USERS_BY_METADATA_VALUE: string;
35
35
  /** Check if a user exists by id. Lightweight — returns only the id column. */
36
- export declare const VERIFY_USER_BY_ID = "SELECT id FROM lt_users WHERE id = $1 LIMIT 1";
36
+ export declare const VERIFY_USER_BY_ID = "SELECT id, status FROM lt_users WHERE id = $1 LIMIT 1";
37
+ export declare const PATCH_USER_PROPERTIES = "\n UPDATE lt_users SET metadata = (\n (COALESCE(metadata, '{}'::jsonb) - $2::text[])\n || COALESCE((\n SELECT jsonb_object_agg(r.next, COALESCE(lt_users.metadata, '{}'::jsonb) -> r.prev)\n FROM jsonb_to_recordset($3::jsonb) AS r(prev text, next text)\n WHERE COALESCE(lt_users.metadata, '{}'::jsonb) ? r.prev\n ), '{}'::jsonb)\n || $4::jsonb\n )\n WHERE id = $1\n AND NOT EXISTS (\n SELECT 1\n FROM lt_users u2, jsonb_each_text($5::jsonb) AS g(k, v)\n WHERE u2.id <> $1 AND u2.status = 'active' AND u2.metadata ->> g.k = g.v\n )\n RETURNING *";
37
38
  /**
38
- * Atomic per-key patch of the user's properties dictionary — ONE statement,
39
- * never read-merge-write. Composition (later wins on key collision):
40
- *
41
- * base minus ($2: removed keys ∪ rename old-names)
42
- * || renames ($3: [{prev,next}] — each carries the OLD value, skipped when
43
- * the source key is absent)
44
- * || sets ($4: typed JSON values)
45
- *
46
- * $5 is the identity-binding guard: {key: value} pairs that must not already
47
- * belong to ANOTHER active user (the write-side counterpart of the
48
- * resolver's ambiguity throw in GET_USERS_BY_METADATA_VALUE). Zero rows
49
- * back means the user is missing OR the guard tripped — the caller
50
- * distinguishes with VERIFY_USER_BY_ID.
39
+ * The same patch, recorded in lt_label_jobs in the same statement: the ledger
40
+ * row exists exactly when the patch landed. $6 is the ledger row as JSON
41
+ * (action, requested_by, source, and for a reissue the template and media);
42
+ * it never carries the identity value. With `require_active`, only an active
43
+ * user is patched, checked in the same statement.
51
44
  */
52
- export declare const PATCH_USER_PROPERTIES = "\n UPDATE lt_users SET metadata = (\n (COALESCE(metadata, '{}'::jsonb) - $2::text[])\n || COALESCE((\n SELECT jsonb_object_agg(r.next, COALESCE(lt_users.metadata, '{}'::jsonb) -> r.prev)\n FROM jsonb_to_recordset($3::jsonb) AS r(prev text, next text)\n WHERE COALESCE(lt_users.metadata, '{}'::jsonb) ? r.prev\n ), '{}'::jsonb)\n || $4::jsonb\n )\n WHERE id = $1\n AND NOT EXISTS (\n SELECT 1\n FROM lt_users u2, jsonb_each_text($5::jsonb) AS g(k, v)\n WHERE u2.id <> $1 AND u2.status = 'active' AND u2.metadata ->> g.k = g.v\n )\n RETURNING *";
45
+ export declare const PATCH_USER_PROPERTIES_RECORDED: string;
53
46
  /** Fetch user + roles in a single query. Returns one row per role (or one row with nulls if no roles). */
54
47
  export declare const GET_USER_WITH_ROLES = "SELECT u.id, u.external_id, u.display_name, u.status, u.metadata,\n r.role, r.type AS role_type, r.read_scope, r.write_scope\n FROM lt_users u\n LEFT JOIN lt_user_roles r ON r.user_id = u.id\n WHERE u.external_id = $1\n ORDER BY r.created_at";
55
48
  export declare const DELETE_USER_BY_ID = "DELETE FROM lt_users WHERE id = $1";
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  // ─── Shared queries (used by multiple modules) ──────────────────────────────
3
3
  Object.defineProperty(exports, "__esModule", { value: true });
4
- exports.IS_GROUP_ADMIN = exports.GET_ROLE_SCOPE = exports.HAS_ROLE_TYPE = exports.HAS_ROLE = exports.DELETE_USER_ROLE = exports.INSERT_USER_ROLE_IF_ABSENT = exports.UPSERT_USER_ROLE = exports.GET_ROLES_BY_USER_IDS = exports.DELETE_USER_BY_ID = exports.GET_USER_WITH_ROLES = exports.PATCH_USER_PROPERTIES = exports.VERIFY_USER_BY_ID = exports.GET_USERS_BY_METADATA_VALUE = exports.GET_NAMES_BY_IDS = exports.GET_USER_BY_ID = exports.GET_USER_BY_EMAIL = exports.CREATE_USER_WITH_ROLES = exports.GET_USER_FOR_AUTH = exports.GET_USER_BY_EXTERNAL_ID = exports.GET_ROLES_BY_USER_ID = exports.ENSURE_ROLE_EXISTS = exports.USER_SAFE_COLUMNS_U = exports.USER_SAFE_COLUMNS = void 0;
4
+ exports.IS_GROUP_ADMIN = exports.GET_ROLE_SCOPE = exports.HAS_ROLE_TYPE = exports.HAS_ROLE = exports.DELETE_USER_ROLE = exports.INSERT_USER_ROLE_IF_ABSENT = exports.UPSERT_USER_ROLE = exports.GET_ROLES_BY_USER_IDS = exports.DELETE_USER_BY_ID = exports.GET_USER_WITH_ROLES = exports.PATCH_USER_PROPERTIES_RECORDED = exports.PATCH_USER_PROPERTIES = exports.VERIFY_USER_BY_ID = exports.GET_USERS_BY_METADATA_VALUE = exports.GET_NAMES_BY_IDS = exports.GET_USER_BY_ID = exports.GET_USER_BY_EMAIL = exports.CREATE_USER_WITH_ROLES = exports.GET_USER_FOR_AUTH = exports.GET_USER_BY_EXTERNAL_ID = exports.GET_ROLES_BY_USER_ID = exports.ENSURE_ROLE_EXISTS = exports.USER_SAFE_COLUMNS_U = exports.USER_SAFE_COLUMNS = void 0;
5
5
  /**
6
6
  * Every lt_users column a client may see. password_hash and oauth_provider_id
7
7
  * are credentials and never appear here — no user read path selects them (the
@@ -61,7 +61,7 @@ exports.GET_NAMES_BY_IDS = 'SELECT id, display_name, external_id, email FROM lt_
61
61
  */
62
62
  exports.GET_USERS_BY_METADATA_VALUE = `SELECT ${exports.USER_SAFE_COLUMNS} FROM lt_users WHERE metadata->>$1 = $2 AND status = 'active' LIMIT 2`;
63
63
  /** Check if a user exists by id. Lightweight — returns only the id column. */
64
- exports.VERIFY_USER_BY_ID = 'SELECT id FROM lt_users WHERE id = $1 LIMIT 1';
64
+ exports.VERIFY_USER_BY_ID = 'SELECT id, status FROM lt_users WHERE id = $1 LIMIT 1';
65
65
  /**
66
66
  * Atomic per-key patch of the user's properties dictionary — ONE statement,
67
67
  * never read-merge-write. Composition (later wins on key collision):
@@ -77,7 +77,7 @@ exports.VERIFY_USER_BY_ID = 'SELECT id FROM lt_users WHERE id = $1 LIMIT 1';
77
77
  * back means the user is missing OR the guard tripped — the caller
78
78
  * distinguishes with VERIFY_USER_BY_ID.
79
79
  */
80
- exports.PATCH_USER_PROPERTIES = `
80
+ const PATCH_USER_PROPERTIES_UPDATE = `
81
81
  UPDATE lt_users SET metadata = (
82
82
  (COALESCE(metadata, '{}'::jsonb) - $2::text[])
83
83
  || COALESCE((
@@ -94,6 +94,30 @@ exports.PATCH_USER_PROPERTIES = `
94
94
  WHERE u2.id <> $1 AND u2.status = 'active' AND u2.metadata ->> g.k = g.v
95
95
  )
96
96
  RETURNING *`;
97
+ exports.PATCH_USER_PROPERTIES = PATCH_USER_PROPERTIES_UPDATE;
98
+ /**
99
+ * The same patch, recorded in lt_label_jobs in the same statement: the ledger
100
+ * row exists exactly when the patch landed. $6 is the ledger row as JSON
101
+ * (action, requested_by, source, and for a reissue the template and media);
102
+ * it never carries the identity value. With `require_active`, only an active
103
+ * user is patched, checked in the same statement.
104
+ */
105
+ exports.PATCH_USER_PROPERTIES_RECORDED = `
106
+ WITH patched AS (${PATCH_USER_PROPERTIES_UPDATE.replace('WHERE id = $1', "WHERE id = $1 AND (($6::jsonb->>'require_active')::boolean IS NOT TRUE OR status = 'active')")}),
107
+ recorded AS (
108
+ INSERT INTO lt_label_jobs (
109
+ action, requested_by, sensitive, source, subject_user_ids, label_count,
110
+ template_name, template_revision, template_snapshot, scheme_version, scheme_kind, media, output
111
+ )
112
+ SELECT $6::jsonb->>'action', ($6::jsonb->>'requested_by')::uuid, true,
113
+ COALESCE($6::jsonb->'source', '{}'::jsonb), ARRAY[patched.id],
114
+ COALESCE(($6::jsonb->>'label_count')::int, 0),
115
+ $6::jsonb->>'template_name', ($6::jsonb->>'template_revision')::int, $6::jsonb->'template_snapshot',
116
+ ($6::jsonb->>'scheme_version')::int, $6::jsonb->>'scheme_kind', $6::jsonb->'media', $6::jsonb->>'output'
117
+ FROM patched
118
+ RETURNING id
119
+ )
120
+ SELECT patched.*, (SELECT id FROM recorded) AS ledger_id FROM patched`;
97
121
  /** Fetch user + roles in a single query. Returns one row per role (or one row with nulls if no roles). */
98
122
  exports.GET_USER_WITH_ROLES = `SELECT u.id, u.external_id, u.display_name, u.status, u.metadata,
99
123
  r.role, r.type AS role_type, r.read_scope, r.write_scope
@@ -24,7 +24,10 @@ export interface UpdateUserInput {
24
24
  display_name?: string;
25
25
  password?: string;
26
26
  status?: LTUserStatus;
27
- /** Wholesale REPLACE of the properties dictionary — for per-key edits use patchUserProperties. */
27
+ /**
28
+ * Wholesale REPLACE of the properties dictionary — for per-key edits use
29
+ * patchUserProperties. Identity bindings absent from the replacement are kept.
30
+ */
28
31
  metadata?: Record<string, any>;
29
32
  }
30
33
  /**
@@ -37,3 +40,20 @@ export interface UserPropertyOps {
37
40
  remove?: string[];
38
41
  rename?: Record<string, string>;
39
42
  }
43
+ /** The lt_label_jobs row a recorded property patch writes beside the patch. */
44
+ export interface PropertyPatchRecord {
45
+ action: 'bind' | 'reissue';
46
+ requested_by: string;
47
+ /** Patch only an active user; checked in the patch statement. */
48
+ require_active?: boolean;
49
+ /** Which identity keys changed; never their values. */
50
+ source: Record<string, unknown>;
51
+ label_count?: number;
52
+ template_name?: string;
53
+ template_revision?: number;
54
+ template_snapshot?: unknown;
55
+ scheme_version?: number;
56
+ scheme_kind?: string;
57
+ media?: unknown;
58
+ output?: string;
59
+ }
@@ -0,0 +1,12 @@
1
+ import type { ParseableScheme } from '../scan-code/parse';
2
+ /** A code that cannot be composed under its scheme. */
3
+ export declare class ScanComposeError extends Error {
4
+ constructor(message: string);
5
+ }
6
+ /** Whether the scheme's codes are minted by the engine (and so printable from a template). */
7
+ export declare function isMintableScheme(scheme: Pick<ParseableScheme, 'encoding'>): boolean;
8
+ /**
9
+ * The code a scan of `target` under `category` reads. Throws ScanComposeError
10
+ * when the scheme only reads codes (gtin, pattern) or the target does not fit.
11
+ */
12
+ export declare function composeScanCode(scheme: Pick<ParseableScheme, 'version' | 'encoding' | 'delimiter' | 'target_length'>, category: string, target: string): string;
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ // The inverse of parseScanCode for the encodings the engine mints.
3
+ Object.defineProperty(exports, "__esModule", { value: true });
4
+ exports.ScanComposeError = void 0;
5
+ exports.isMintableScheme = isMintableScheme;
6
+ exports.composeScanCode = composeScanCode;
7
+ /** A code that cannot be composed under its scheme. */
8
+ class ScanComposeError extends Error {
9
+ constructor(message) {
10
+ super(message);
11
+ this.name = 'ScanComposeError';
12
+ }
13
+ }
14
+ exports.ScanComposeError = ScanComposeError;
15
+ const PRINTABLE_ASCII = /^[\x21-\x7e]+$/;
16
+ /** Whether the scheme's codes are minted by the engine (and so printable from a template). */
17
+ function isMintableScheme(scheme) {
18
+ return scheme.encoding === 'fixed' || scheme.encoding === 'delimited';
19
+ }
20
+ /**
21
+ * The code a scan of `target` under `category` reads. Throws ScanComposeError
22
+ * when the scheme only reads codes (gtin, pattern) or the target does not fit.
23
+ */
24
+ function composeScanCode(scheme, category, target) {
25
+ if (!/^[0-9]$/.test(category)) {
26
+ throw new ScanComposeError(`category must be a single digit, got "${category}"`);
27
+ }
28
+ const version = String(scheme.version);
29
+ if (scheme.encoding === 'delimited') {
30
+ if (!target || !PRINTABLE_ASCII.test(target)) {
31
+ throw new ScanComposeError(`target "${target}" must be printable ASCII with no spaces`);
32
+ }
33
+ return `${version}${scheme.delimiter}${category}${scheme.delimiter}${target}`;
34
+ }
35
+ if (scheme.encoding === 'fixed') {
36
+ const length = scheme.target_length ?? 0;
37
+ if (!new RegExp(`^[0-9]{${length}}$`).test(target)) {
38
+ throw new ScanComposeError(`target "${target}" must be exactly ${length} digits`);
39
+ }
40
+ return `${version}${category}${target}`;
41
+ }
42
+ throw new ScanComposeError(`scheme ${version} reads ${scheme.encoding} codes; the engine does not mint them`);
43
+ }
@@ -0,0 +1,9 @@
1
+ export * from './types';
2
+ export { composeScanCode, isMintableScheme, ScanComposeError } from './compose';
3
+ export { renderLabelText, listLabelTokens, unknownLabelTokens, userTokenKeys, type LabelTokenContext } from './tokens';
4
+ export { LABEL_MEDIA, DEFAULT_LABEL_MEDIA_ID, LabelMediaError, assertValidMedia, resolveMedia, slotOrigin, slotsPerPage, } from './media';
5
+ export { encodeSymbol, quietZoneModules, SymbolEncodeError, type EncodedSymbol } from './symbol';
6
+ export { layoutLabelContent, GLYPH_ADVANCE, type LabelContentOptions } from './label-content';
7
+ export { buildLabelSheet, cutMarkSegments, LabelLayoutError, MAX_COPIES, type LabelSheetInput } from './layout';
8
+ export { renderSheetSvg, escapeXml, type SvgRenderOptions } from './render-svg';
9
+ export { renderSheetZpl, zplField, ZplRenderError } from './render-zpl';
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.ZplRenderError = exports.zplField = exports.renderSheetZpl = exports.escapeXml = exports.renderSheetSvg = exports.MAX_COPIES = exports.LabelLayoutError = exports.cutMarkSegments = exports.buildLabelSheet = exports.GLYPH_ADVANCE = exports.layoutLabelContent = exports.SymbolEncodeError = exports.quietZoneModules = exports.encodeSymbol = exports.slotsPerPage = exports.slotOrigin = exports.resolveMedia = exports.assertValidMedia = exports.LabelMediaError = exports.DEFAULT_LABEL_MEDIA_ID = exports.LABEL_MEDIA = exports.userTokenKeys = exports.unknownLabelTokens = exports.listLabelTokens = exports.renderLabelText = exports.ScanComposeError = exports.isMintableScheme = exports.composeScanCode = void 0;
18
+ __exportStar(require("./types"), exports);
19
+ var compose_1 = require("./compose");
20
+ Object.defineProperty(exports, "composeScanCode", { enumerable: true, get: function () { return compose_1.composeScanCode; } });
21
+ Object.defineProperty(exports, "isMintableScheme", { enumerable: true, get: function () { return compose_1.isMintableScheme; } });
22
+ Object.defineProperty(exports, "ScanComposeError", { enumerable: true, get: function () { return compose_1.ScanComposeError; } });
23
+ var tokens_1 = require("./tokens");
24
+ Object.defineProperty(exports, "renderLabelText", { enumerable: true, get: function () { return tokens_1.renderLabelText; } });
25
+ Object.defineProperty(exports, "listLabelTokens", { enumerable: true, get: function () { return tokens_1.listLabelTokens; } });
26
+ Object.defineProperty(exports, "unknownLabelTokens", { enumerable: true, get: function () { return tokens_1.unknownLabelTokens; } });
27
+ Object.defineProperty(exports, "userTokenKeys", { enumerable: true, get: function () { return tokens_1.userTokenKeys; } });
28
+ var media_1 = require("./media");
29
+ Object.defineProperty(exports, "LABEL_MEDIA", { enumerable: true, get: function () { return media_1.LABEL_MEDIA; } });
30
+ Object.defineProperty(exports, "DEFAULT_LABEL_MEDIA_ID", { enumerable: true, get: function () { return media_1.DEFAULT_LABEL_MEDIA_ID; } });
31
+ Object.defineProperty(exports, "LabelMediaError", { enumerable: true, get: function () { return media_1.LabelMediaError; } });
32
+ Object.defineProperty(exports, "assertValidMedia", { enumerable: true, get: function () { return media_1.assertValidMedia; } });
33
+ Object.defineProperty(exports, "resolveMedia", { enumerable: true, get: function () { return media_1.resolveMedia; } });
34
+ Object.defineProperty(exports, "slotOrigin", { enumerable: true, get: function () { return media_1.slotOrigin; } });
35
+ Object.defineProperty(exports, "slotsPerPage", { enumerable: true, get: function () { return media_1.slotsPerPage; } });
36
+ var symbol_1 = require("./symbol");
37
+ Object.defineProperty(exports, "encodeSymbol", { enumerable: true, get: function () { return symbol_1.encodeSymbol; } });
38
+ Object.defineProperty(exports, "quietZoneModules", { enumerable: true, get: function () { return symbol_1.quietZoneModules; } });
39
+ Object.defineProperty(exports, "SymbolEncodeError", { enumerable: true, get: function () { return symbol_1.SymbolEncodeError; } });
40
+ var label_content_1 = require("./label-content");
41
+ Object.defineProperty(exports, "layoutLabelContent", { enumerable: true, get: function () { return label_content_1.layoutLabelContent; } });
42
+ Object.defineProperty(exports, "GLYPH_ADVANCE", { enumerable: true, get: function () { return label_content_1.GLYPH_ADVANCE; } });
43
+ var layout_1 = require("./layout");
44
+ Object.defineProperty(exports, "buildLabelSheet", { enumerable: true, get: function () { return layout_1.buildLabelSheet; } });
45
+ Object.defineProperty(exports, "cutMarkSegments", { enumerable: true, get: function () { return layout_1.cutMarkSegments; } });
46
+ Object.defineProperty(exports, "LabelLayoutError", { enumerable: true, get: function () { return layout_1.LabelLayoutError; } });
47
+ Object.defineProperty(exports, "MAX_COPIES", { enumerable: true, get: function () { return layout_1.MAX_COPIES; } });
48
+ var render_svg_1 = require("./render-svg");
49
+ Object.defineProperty(exports, "renderSheetSvg", { enumerable: true, get: function () { return render_svg_1.renderSheetSvg; } });
50
+ Object.defineProperty(exports, "escapeXml", { enumerable: true, get: function () { return render_svg_1.escapeXml; } });
51
+ var render_zpl_1 = require("./render-zpl");
52
+ Object.defineProperty(exports, "renderSheetZpl", { enumerable: true, get: function () { return render_zpl_1.renderSheetZpl; } });
53
+ Object.defineProperty(exports, "zplField", { enumerable: true, get: function () { return render_zpl_1.zplField; } });
54
+ Object.defineProperty(exports, "ZplRenderError", { enumerable: true, get: function () { return render_zpl_1.ZplRenderError; } });
@@ -0,0 +1,17 @@
1
+ import type { LabelContent, LabelElement, LabelSymbology, LabelWarningKind } from './types';
2
+ export interface LabelContentOptions {
3
+ symbology: LabelSymbology;
4
+ humanReadable: boolean;
5
+ minModuleMm: number;
6
+ }
7
+ export interface LabelContentResult {
8
+ elements: LabelElement[];
9
+ warnings: {
10
+ kind: LabelWarningKind;
11
+ detail: string;
12
+ }[];
13
+ }
14
+ /** Average glyph advance as a fraction of the font size, for fitting text without font metrics. */
15
+ export declare const GLYPH_ADVANCE = 0.56;
16
+ /** Lay out one label's content inside a `width` × `height` box. */
17
+ export declare function layoutLabelContent(content: LabelContent, width: number, height: number, opts: LabelContentOptions): LabelContentResult;
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ // Lays out one label: title, lines, then one panel per code (caption band,
3
+ // symbol, readable code). Coordinates are millimetres from the label's corner.
4
+ Object.defineProperty(exports, "__esModule", { value: true });
5
+ exports.GLYPH_ADVANCE = void 0;
6
+ exports.layoutLabelContent = layoutLabelContent;
7
+ const symbol_1 = require("./symbol");
8
+ /** Average glyph advance as a fraction of the font size, for fitting text without font metrics. */
9
+ exports.GLYPH_ADVANCE = 0.56;
10
+ const MIN_TEXT_MM = 1.6;
11
+ const MAX_LINEAR_MODULE_MM = 0.6;
12
+ const MAX_MATRIX_MODULE_MM = 1.4;
13
+ const MAX_BAR_HEIGHT_MM = 30;
14
+ const clamp = (n, lo, hi) => Math.min(hi, Math.max(lo, n));
15
+ /** The largest font size up to `size` at which `text` fits `width`, or null below the floor. */
16
+ function fitText(text, width, size) {
17
+ const fitted = Math.min(size, width / Math.max(1, text.length * exports.GLYPH_ADVANCE));
18
+ return fitted >= MIN_TEXT_MM ? fitted : null;
19
+ }
20
+ /** Text that fits `width` at the floor size, cut with an ellipsis. */
21
+ function truncateTo(text, width) {
22
+ const max = Math.max(1, Math.floor(width / (MIN_TEXT_MM * exports.GLYPH_ADVANCE)));
23
+ return text.length <= max ? text : `${text.slice(0, Math.max(1, max - 1))}…`;
24
+ }
25
+ function textRow(text, box, size, weight, out) {
26
+ let fitted = fitText(text, box.w, size);
27
+ let shown = text;
28
+ if (fitted === null) {
29
+ shown = truncateTo(text, box.w);
30
+ fitted = MIN_TEXT_MM;
31
+ out.warnings.push({ kind: 'overflow', detail: `"${text}" is too long for the label and was shortened` });
32
+ }
33
+ out.elements.push({ kind: 'text', x: box.x, y: box.y, w: box.w, size: fitted, text: shown, weight, align: 'center' });
34
+ return size * 1.2;
35
+ }
36
+ function planPanel(code, box, opts) {
37
+ const elements = [];
38
+ const gap = clamp(box.h * 0.04, 0.4, 1.5);
39
+ let y = box.y;
40
+ if (code.caption) {
41
+ const band = clamp(box.h * 0.2, 2.2, 7);
42
+ const size = Math.min(band * 0.62, box.w / Math.max(1, code.caption.length * exports.GLYPH_ADVANCE));
43
+ if (code.tone === 'solid')
44
+ elements.push({ kind: 'rect', x: box.x, y, w: box.w, h: band, fill: true });
45
+ if (code.tone === 'outline')
46
+ elements.push({ kind: 'rect', x: box.x, y, w: box.w, h: band, fill: false, stroke: clamp(band * 0.08, 0.2, 0.5) });
47
+ elements.push({
48
+ kind: 'text', x: box.x, y: y + (band - size) / 2, w: box.w, size: Math.max(size, MIN_TEXT_MM),
49
+ text: code.caption, weight: 'bold', align: 'center', inverse: code.tone === 'solid',
50
+ });
51
+ y += band + gap;
52
+ }
53
+ const readable = opts.humanReadable ? clamp(box.h * 0.1, 1.6, 3.2) : 0;
54
+ const symbolH = box.y + box.h - y - (readable ? readable + gap : 0);
55
+ const encoded = (0, symbol_1.encodeSymbol)(opts.symbology, code.code);
56
+ const quiet = (0, symbol_1.quietZoneModules)(opts.symbology);
57
+ const linear = opts.symbology === 'code128';
58
+ const moduleMm = linear
59
+ ? Math.min(MAX_LINEAR_MODULE_MM, box.w / (encoded.widthModules + 2 * quiet))
60
+ : Math.min(MAX_MATRIX_MODULE_MM, Math.min(box.w, symbolH) / (encoded.widthModules + 2 * quiet));
61
+ const w = encoded.widthModules * moduleMm;
62
+ const h = linear ? Math.min(symbolH, MAX_BAR_HEIGHT_MM) : encoded.heightModules * moduleMm;
63
+ elements.push({
64
+ kind: 'symbol', x: box.x + (box.w - w) / 2, y, w, h,
65
+ symbology: opts.symbology, data: code.code, moduleMm, path: encoded.path,
66
+ });
67
+ if (readable) {
68
+ elements.push({
69
+ kind: 'text', x: box.x, y: y + h + gap, w: box.w,
70
+ size: Math.max(MIN_TEXT_MM, Math.min(readable * 0.85, box.w / Math.max(1, code.code.length * exports.GLYPH_ADVANCE))),
71
+ text: code.code, weight: 'regular', align: 'center',
72
+ });
73
+ }
74
+ return { moduleMm, elements, quietOk: symbolH > 0 && w + 2 * quiet * moduleMm <= box.w + 1e-6 };
75
+ }
76
+ function planPanels(codes, area, across, opts) {
77
+ const n = codes.length;
78
+ const gap = clamp(Math.min(area.w, area.h) * 0.05, 0.8, 4);
79
+ return codes.map((code, i) => {
80
+ const box = across
81
+ ? { x: area.x + i * ((area.w - gap * (n - 1)) / n + gap), y: area.y, w: (area.w - gap * (n - 1)) / n, h: area.h }
82
+ : { x: area.x, y: area.y + i * ((area.h - gap * (n - 1)) / n + gap), w: area.w, h: (area.h - gap * (n - 1)) / n };
83
+ return planPanel(code, box, opts);
84
+ });
85
+ }
86
+ const smallest = (plans) => Math.min(...plans.map((p) => p.moduleMm));
87
+ /** Lay out one label's content inside a `width` × `height` box. */
88
+ function layoutLabelContent(content, width, height, opts) {
89
+ const out = { elements: [], warnings: [] };
90
+ const pad = clamp(Math.min(width, height) * 0.05, 1, 4);
91
+ const inner = { x: pad, y: pad, w: width - 2 * pad, h: height - 2 * pad };
92
+ let y = inner.y;
93
+ if (content.title) {
94
+ y += textRow(content.title, { ...inner, y }, clamp(inner.h * 0.15, 2.2, 10), 'bold', out);
95
+ }
96
+ const lineSize = clamp(inner.h * 0.09, 1.8, 6);
97
+ for (const line of content.lines.filter(Boolean)) {
98
+ y += textRow(line, { ...inner, y }, lineSize, 'regular', out);
99
+ }
100
+ if (content.codes.length === 0)
101
+ return out;
102
+ const area = { x: inner.x, y: y + (y > inner.y ? pad / 2 : 0), w: inner.w, h: 0 };
103
+ area.h = inner.y + inner.h - area.y;
104
+ let plans;
105
+ try {
106
+ const stacked = planPanels(content.codes, area, false, opts);
107
+ const across = content.codes.length > 1 ? planPanels(content.codes, area, true, opts) : stacked;
108
+ plans = smallest(across) > smallest(stacked) ? across : stacked;
109
+ }
110
+ catch (err) {
111
+ if (!(err instanceof symbol_1.SymbolEncodeError))
112
+ throw err;
113
+ out.warnings.push({ kind: 'unencodable', detail: err.message });
114
+ return out;
115
+ }
116
+ for (const plan of plans)
117
+ out.elements.push(...plan.elements);
118
+ const module = smallest(plans);
119
+ if (module < opts.minModuleMm) {
120
+ out.warnings.push({
121
+ kind: 'scale',
122
+ detail: `bars print at ${module.toFixed(2)} mm, below the ${opts.minModuleMm} mm a scanner reads reliably; use larger labels or fewer codes`,
123
+ });
124
+ }
125
+ if (plans.some((p) => !p.quietOk)) {
126
+ out.warnings.push({ kind: 'quiet_zone', detail: 'a code has too little blank margin around it to scan reliably' });
127
+ }
128
+ if (content.masked) {
129
+ for (const el of out.elements) {
130
+ if (el.kind === 'symbol')
131
+ Object.assign(el, { data: '', path: '', masked: true });
132
+ if (el.kind === 'text' && content.codes.some((c) => c.code === el.text))
133
+ el.text = '•'.repeat(8);
134
+ }
135
+ }
136
+ return out;
137
+ }
@@ -0,0 +1,23 @@
1
+ import type { LabelContent, LabelMedia, LabelSheet, LabelSymbology } from './types';
2
+ export interface LabelSheetInput {
3
+ media: LabelMedia;
4
+ labels: LabelContent[];
5
+ symbology: LabelSymbology;
6
+ humanReadable: boolean;
7
+ /** The first slot to print on page one, counted from 1, so used labels are skipped. */
8
+ startPosition?: number;
9
+ /** Copies of each label, printed together. */
10
+ copies?: number;
11
+ /** One label per page, in the start slot (placards). */
12
+ onePerPage?: boolean;
13
+ cutMarks?: boolean;
14
+ }
15
+ /** A sheet request that cannot be laid out. */
16
+ export declare class LabelLayoutError extends Error {
17
+ constructor(message: string);
18
+ }
19
+ export declare const MAX_COPIES = 50;
20
+ /** Lay out every label of a job onto pages. */
21
+ export declare function buildLabelSheet(input: LabelSheetInput): LabelSheet;
22
+ /** Short tick marks at every label corner, outside the label, for cutting card stock. */
23
+ export declare function cutMarkSegments(media: LabelMedia, length?: number): Array<[number, number, number, number]>;