@open-mercato/core 0.7.1-develop.7130.1.fef2396fd8 → 0.7.1-develop.7135.1.19bf969756

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 (242) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/helpers/integration/communicationChannelsFixtures.js.map +2 -2
  3. package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js +5 -0
  4. package/dist/modules/audit_logs/api/audit-logs/actions/redo/route.js.map +2 -2
  5. package/dist/modules/auth/api/profile/route.js +5 -0
  6. package/dist/modules/auth/api/profile/route.js.map +2 -2
  7. package/dist/modules/communication_channels/api/post/test-seed/route.js +40 -2
  8. package/dist/modules/communication_channels/api/post/test-seed/route.js.map +2 -2
  9. package/dist/modules/communication_channels/lib/access-control.js +6 -1
  10. package/dist/modules/communication_channels/lib/access-control.js.map +2 -2
  11. package/dist/modules/communication_channels/lib/system-user.js +8 -3
  12. package/dist/modules/communication_channels/lib/system-user.js.map +2 -2
  13. package/dist/modules/customers/api/activities/route.js +13 -0
  14. package/dist/modules/customers/api/activities/route.js.map +2 -2
  15. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js +9 -0
  16. package/dist/modules/customers/api/dictionaries/[kind]/[id]/route.js.map +2 -2
  17. package/dist/modules/customers/api/dictionaries/[kind]/route.js +5 -0
  18. package/dist/modules/customers/api/dictionaries/[kind]/route.js.map +2 -2
  19. package/dist/modules/customers/api/dictionaries/kind-settings/route.js +5 -0
  20. package/dist/modules/customers/api/dictionaries/kind-settings/route.js.map +2 -2
  21. package/dist/modules/customers/api/interactions/cancel/route.js +5 -0
  22. package/dist/modules/customers/api/interactions/cancel/route.js.map +2 -2
  23. package/dist/modules/customers/api/interactions/complete/route.js +5 -0
  24. package/dist/modules/customers/api/interactions/complete/route.js.map +2 -2
  25. package/dist/modules/customers/api/labels/assign/route.js +5 -0
  26. package/dist/modules/customers/api/labels/assign/route.js.map +2 -2
  27. package/dist/modules/customers/api/labels/route.js +5 -0
  28. package/dist/modules/customers/api/labels/route.js.map +2 -2
  29. package/dist/modules/customers/api/labels/unassign/route.js +5 -0
  30. package/dist/modules/customers/api/labels/unassign/route.js.map +2 -2
  31. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js +9 -0
  32. package/dist/modules/customers/api/people/[id]/companies/[linkId]/route.js.map +2 -2
  33. package/dist/modules/customers/api/people/[id]/companies/route.js +5 -0
  34. package/dist/modules/customers/api/people/[id]/companies/route.js.map +2 -2
  35. package/dist/modules/customers/api/pipeline-stages/reorder/route.js +5 -0
  36. package/dist/modules/customers/api/pipeline-stages/reorder/route.js.map +2 -2
  37. package/dist/modules/customers/api/pipeline-stages/route.js +13 -0
  38. package/dist/modules/customers/api/pipeline-stages/route.js.map +2 -2
  39. package/dist/modules/customers/api/pipelines/route.js +13 -0
  40. package/dist/modules/customers/api/pipelines/route.js.map +2 -2
  41. package/dist/modules/customers/api/settings/address-format/route.js +5 -0
  42. package/dist/modules/customers/api/settings/address-format/route.js.map +2 -2
  43. package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js +5 -0
  44. package/dist/modules/customers/api/settings/dictionary-sort-modes/route.js.map +2 -2
  45. package/dist/modules/customers/api/settings/stuck-threshold/route.js +5 -0
  46. package/dist/modules/customers/api/settings/stuck-threshold/route.js.map +2 -2
  47. package/dist/modules/customers/api/tags/assign/route.js +5 -0
  48. package/dist/modules/customers/api/tags/assign/route.js.map +2 -2
  49. package/dist/modules/customers/api/tags/unassign/route.js +5 -0
  50. package/dist/modules/customers/api/tags/unassign/route.js.map +2 -2
  51. package/dist/modules/customers/api/todos/route.js +13 -0
  52. package/dist/modules/customers/api/todos/route.js.map +2 -2
  53. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js +9 -0
  54. package/dist/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.js.map +2 -2
  55. package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js +5 -0
  56. package/dist/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.js.map +2 -2
  57. package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js +5 -0
  58. package/dist/modules/dictionaries/api/[dictionaryId]/entries/route.js.map +2 -2
  59. package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js +5 -0
  60. package/dist/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.js.map +2 -2
  61. package/dist/modules/directory/api/organization-branding/route.js +5 -0
  62. package/dist/modules/directory/api/organization-branding/route.js.map +2 -2
  63. package/dist/modules/eudr/api/plots/import/route.js.map +2 -2
  64. package/dist/modules/eudr/api/product-mappings/suggestions/apply/route.js.map +2 -2
  65. package/dist/modules/feature_toggles/api/overrides/route.js +5 -0
  66. package/dist/modules/feature_toggles/api/overrides/route.js.map +2 -2
  67. package/dist/modules/messages/api/[id]/actions/[actionId]/route.js +5 -0
  68. package/dist/modules/messages/api/[id]/actions/[actionId]/route.js.map +2 -2
  69. package/dist/modules/messages/api/[id]/conversation/archive/route.js +5 -0
  70. package/dist/modules/messages/api/[id]/conversation/archive/route.js.map +2 -2
  71. package/dist/modules/messages/api/[id]/conversation/read/route.js +5 -0
  72. package/dist/modules/messages/api/[id]/conversation/read/route.js.map +2 -2
  73. package/dist/modules/messages/api/[id]/conversation/route.js +5 -0
  74. package/dist/modules/messages/api/[id]/conversation/route.js.map +2 -2
  75. package/dist/modules/messages/api/[id]/forward/route.js +5 -0
  76. package/dist/modules/messages/api/[id]/forward/route.js.map +2 -2
  77. package/dist/modules/messages/api/[id]/reply/route.js +5 -0
  78. package/dist/modules/messages/api/[id]/reply/route.js.map +2 -2
  79. package/dist/modules/messages/api/[id]/route.js +9 -0
  80. package/dist/modules/messages/api/[id]/route.js.map +2 -2
  81. package/dist/modules/messages/api/token/[token]/route.js +5 -0
  82. package/dist/modules/messages/api/token/[token]/route.js.map +2 -2
  83. package/dist/modules/resources/api/resources/tags/assign/route.js +5 -0
  84. package/dist/modules/resources/api/resources/tags/assign/route.js.map +2 -2
  85. package/dist/modules/resources/api/resources/tags/unassign/route.js +5 -0
  86. package/dist/modules/resources/api/resources/tags/unassign/route.js.map +2 -2
  87. package/dist/modules/sales/api/quotes/accept/route.js +5 -0
  88. package/dist/modules/sales/api/quotes/accept/route.js.map +2 -2
  89. package/dist/modules/sales/api/quotes/convert/route.js +5 -0
  90. package/dist/modules/sales/api/quotes/convert/route.js.map +2 -2
  91. package/dist/modules/sales/api/settings/document-numbers/route.js +5 -0
  92. package/dist/modules/sales/api/settings/document-numbers/route.js.map +2 -2
  93. package/dist/modules/sales/api/settings/order-editing/route.js +5 -0
  94. package/dist/modules/sales/api/settings/order-editing/route.js.map +2 -2
  95. package/dist/modules/staff/api/leave-requests/accept/route.js +5 -0
  96. package/dist/modules/staff/api/leave-requests/accept/route.js.map +2 -2
  97. package/dist/modules/staff/api/leave-requests/reject/route.js +5 -0
  98. package/dist/modules/staff/api/leave-requests/reject/route.js.map +2 -2
  99. package/dist/modules/staff/api/team-members/self/route.js +5 -0
  100. package/dist/modules/staff/api/team-members/self/route.js.map +2 -2
  101. package/dist/modules/staff/api/team-members/tags/assign/route.js +5 -0
  102. package/dist/modules/staff/api/team-members/tags/assign/route.js.map +2 -2
  103. package/dist/modules/staff/api/team-members/tags/unassign/route.js +5 -0
  104. package/dist/modules/staff/api/team-members/tags/unassign/route.js.map +2 -2
  105. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js +5 -0
  106. package/dist/modules/staff/api/timesheets/time-entries/start-timer/route.js.map +2 -2
  107. package/dist/modules/translations/api/[entityType]/[entityId]/route.js +9 -0
  108. package/dist/modules/translations/api/[entityType]/[entityId]/route.js.map +2 -2
  109. package/dist/modules/warranty_claims/api/ai/assess/route.js +25 -18
  110. package/dist/modules/warranty_claims/api/ai/assess/route.js.map +2 -2
  111. package/dist/modules/warranty_claims/api/assign/route.js +5 -0
  112. package/dist/modules/warranty_claims/api/assign/route.js.map +2 -2
  113. package/dist/modules/warranty_claims/api/credit-memo/route.js +16 -12
  114. package/dist/modules/warranty_claims/api/credit-memo/route.js.map +2 -2
  115. package/dist/modules/warranty_claims/api/events/route.js +6 -1
  116. package/dist/modules/warranty_claims/api/events/route.js.map +2 -2
  117. package/dist/modules/warranty_claims/api/portal/attachments/route.js +24 -27
  118. package/dist/modules/warranty_claims/api/portal/attachments/route.js.map +2 -2
  119. package/dist/modules/warranty_claims/api/portal/claims/[id]/route.js +8 -10
  120. package/dist/modules/warranty_claims/api/portal/claims/[id]/route.js.map +2 -2
  121. package/dist/modules/warranty_claims/api/portal/claims/[id]/shared.js +4 -9
  122. package/dist/modules/warranty_claims/api/portal/claims/[id]/shared.js.map +2 -2
  123. package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js +8 -3
  124. package/dist/modules/warranty_claims/api/portal/claims/[id]/submit/route.js.map +2 -2
  125. package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js +8 -3
  126. package/dist/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.js.map +2 -2
  127. package/dist/modules/warranty_claims/api/portal/claims/route.js +9 -9
  128. package/dist/modules/warranty_claims/api/portal/claims/route.js.map +2 -2
  129. package/dist/modules/warranty_claims/api/portal/events/route.js +17 -13
  130. package/dist/modules/warranty_claims/api/portal/events/route.js.map +2 -2
  131. package/dist/modules/warranty_claims/api/receiving/route.js +19 -15
  132. package/dist/modules/warranty_claims/api/receiving/route.js.map +2 -2
  133. package/dist/modules/warranty_claims/api/registrations/route.js +9 -4
  134. package/dist/modules/warranty_claims/api/registrations/route.js.map +2 -2
  135. package/dist/modules/warranty_claims/api/replacement-order/route.js +16 -12
  136. package/dist/modules/warranty_claims/api/replacement-order/route.js.map +2 -2
  137. package/dist/modules/warranty_claims/api/return-label/route.js +25 -19
  138. package/dist/modules/warranty_claims/api/return-label/route.js.map +2 -2
  139. package/dist/modules/warranty_claims/api/risk/route.js +2 -1
  140. package/dist/modules/warranty_claims/api/risk/route.js.map +2 -2
  141. package/dist/modules/warranty_claims/api/sales-return/route.js +16 -12
  142. package/dist/modules/warranty_claims/api/sales-return/route.js.map +2 -2
  143. package/dist/modules/warranty_claims/api/settings-general/route.js +5 -0
  144. package/dist/modules/warranty_claims/api/settings-general/route.js.map +2 -2
  145. package/dist/modules/warranty_claims/api/submit/route.js +5 -0
  146. package/dist/modules/warranty_claims/api/submit/route.js.map +2 -2
  147. package/dist/modules/warranty_claims/api/transition/route.js +5 -0
  148. package/dist/modules/warranty_claims/api/transition/route.js.map +2 -2
  149. package/dist/modules/warranty_claims/api/troubleshooting-guides/route.js +22 -1
  150. package/dist/modules/warranty_claims/api/troubleshooting-guides/route.js.map +2 -2
  151. package/dist/modules/warranty_claims/api/vendor-recovery/route.js +5 -0
  152. package/dist/modules/warranty_claims/api/vendor-recovery/route.js.map +2 -2
  153. package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js +2 -1
  154. package/dist/modules/warranty_claims/api/vendor-recovery-suggestions/route.js.map +2 -2
  155. package/dist/modules/warranty_claims/commands/shared.js +2 -2
  156. package/dist/modules/warranty_claims/commands/shared.js.map +2 -2
  157. package/dist/modules/warranty_claims/lib/portalAuthGuard.js +31 -0
  158. package/dist/modules/warranty_claims/lib/portalAuthGuard.js.map +7 -0
  159. package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js +5 -0
  160. package/dist/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.js.map +2 -2
  161. package/package.json +7 -7
  162. package/src/helpers/integration/communicationChannelsFixtures.ts +13 -0
  163. package/src/modules/audit_logs/api/audit-logs/actions/redo/route.ts +5 -0
  164. package/src/modules/auth/api/profile/route.ts +5 -0
  165. package/src/modules/communication_channels/api/post/test-seed/route.ts +47 -2
  166. package/src/modules/communication_channels/lib/access-control.ts +28 -0
  167. package/src/modules/communication_channels/lib/system-user.ts +40 -22
  168. package/src/modules/customers/api/activities/route.ts +13 -0
  169. package/src/modules/customers/api/dictionaries/[kind]/[id]/route.ts +9 -0
  170. package/src/modules/customers/api/dictionaries/[kind]/route.ts +5 -0
  171. package/src/modules/customers/api/dictionaries/kind-settings/route.ts +5 -0
  172. package/src/modules/customers/api/interactions/cancel/route.ts +5 -0
  173. package/src/modules/customers/api/interactions/complete/route.ts +5 -0
  174. package/src/modules/customers/api/labels/assign/route.ts +5 -0
  175. package/src/modules/customers/api/labels/route.ts +5 -0
  176. package/src/modules/customers/api/labels/unassign/route.ts +5 -0
  177. package/src/modules/customers/api/people/[id]/companies/[linkId]/route.ts +9 -0
  178. package/src/modules/customers/api/people/[id]/companies/route.ts +5 -0
  179. package/src/modules/customers/api/pipeline-stages/reorder/route.ts +5 -0
  180. package/src/modules/customers/api/pipeline-stages/route.ts +13 -0
  181. package/src/modules/customers/api/pipelines/route.ts +13 -0
  182. package/src/modules/customers/api/settings/address-format/route.ts +5 -0
  183. package/src/modules/customers/api/settings/dictionary-sort-modes/route.ts +5 -0
  184. package/src/modules/customers/api/settings/stuck-threshold/route.ts +5 -0
  185. package/src/modules/customers/api/tags/assign/route.ts +5 -0
  186. package/src/modules/customers/api/tags/unassign/route.ts +5 -0
  187. package/src/modules/customers/api/todos/route.ts +13 -0
  188. package/src/modules/dictionaries/api/[dictionaryId]/entries/[entryId]/route.ts +9 -0
  189. package/src/modules/dictionaries/api/[dictionaryId]/entries/reorder/route.ts +5 -0
  190. package/src/modules/dictionaries/api/[dictionaryId]/entries/route.ts +5 -0
  191. package/src/modules/dictionaries/api/[dictionaryId]/entries/set-default/route.ts +5 -0
  192. package/src/modules/directory/api/organization-branding/route.ts +5 -0
  193. package/src/modules/eudr/api/plots/import/route.ts +7 -0
  194. package/src/modules/eudr/api/product-mappings/suggestions/apply/route.ts +7 -0
  195. package/src/modules/feature_toggles/api/overrides/route.ts +5 -0
  196. package/src/modules/messages/api/[id]/actions/[actionId]/route.ts +5 -0
  197. package/src/modules/messages/api/[id]/conversation/archive/route.ts +5 -0
  198. package/src/modules/messages/api/[id]/conversation/read/route.ts +5 -0
  199. package/src/modules/messages/api/[id]/conversation/route.ts +5 -0
  200. package/src/modules/messages/api/[id]/forward/route.ts +5 -0
  201. package/src/modules/messages/api/[id]/reply/route.ts +5 -0
  202. package/src/modules/messages/api/[id]/route.ts +9 -0
  203. package/src/modules/messages/api/token/[token]/route.ts +5 -0
  204. package/src/modules/resources/api/resources/tags/assign/route.ts +5 -0
  205. package/src/modules/resources/api/resources/tags/unassign/route.ts +5 -0
  206. package/src/modules/sales/api/quotes/accept/route.ts +5 -0
  207. package/src/modules/sales/api/quotes/convert/route.ts +5 -0
  208. package/src/modules/sales/api/settings/document-numbers/route.ts +5 -0
  209. package/src/modules/sales/api/settings/order-editing/route.ts +5 -0
  210. package/src/modules/staff/api/leave-requests/accept/route.ts +5 -0
  211. package/src/modules/staff/api/leave-requests/reject/route.ts +5 -0
  212. package/src/modules/staff/api/team-members/self/route.ts +5 -0
  213. package/src/modules/staff/api/team-members/tags/assign/route.ts +5 -0
  214. package/src/modules/staff/api/team-members/tags/unassign/route.ts +5 -0
  215. package/src/modules/staff/api/timesheets/time-entries/start-timer/route.ts +5 -0
  216. package/src/modules/translations/api/[entityType]/[entityId]/route.ts +9 -0
  217. package/src/modules/warranty_claims/api/ai/assess/route.ts +28 -17
  218. package/src/modules/warranty_claims/api/assign/route.ts +5 -0
  219. package/src/modules/warranty_claims/api/credit-memo/route.ts +19 -13
  220. package/src/modules/warranty_claims/api/events/route.ts +6 -1
  221. package/src/modules/warranty_claims/api/portal/attachments/route.ts +29 -27
  222. package/src/modules/warranty_claims/api/portal/claims/[id]/route.ts +9 -10
  223. package/src/modules/warranty_claims/api/portal/claims/[id]/shared.ts +5 -9
  224. package/src/modules/warranty_claims/api/portal/claims/[id]/submit/route.ts +8 -3
  225. package/src/modules/warranty_claims/api/portal/claims/[id]/withdraw/route.ts +8 -3
  226. package/src/modules/warranty_claims/api/portal/claims/route.ts +10 -9
  227. package/src/modules/warranty_claims/api/portal/events/route.ts +18 -13
  228. package/src/modules/warranty_claims/api/receiving/route.ts +22 -15
  229. package/src/modules/warranty_claims/api/registrations/route.ts +9 -3
  230. package/src/modules/warranty_claims/api/replacement-order/route.ts +19 -13
  231. package/src/modules/warranty_claims/api/return-label/route.ts +26 -17
  232. package/src/modules/warranty_claims/api/risk/route.ts +3 -1
  233. package/src/modules/warranty_claims/api/sales-return/route.ts +19 -13
  234. package/src/modules/warranty_claims/api/settings-general/route.ts +5 -0
  235. package/src/modules/warranty_claims/api/submit/route.ts +5 -0
  236. package/src/modules/warranty_claims/api/transition/route.ts +5 -0
  237. package/src/modules/warranty_claims/api/troubleshooting-guides/route.ts +23 -1
  238. package/src/modules/warranty_claims/api/vendor-recovery/route.ts +5 -0
  239. package/src/modules/warranty_claims/api/vendor-recovery-suggestions/route.ts +3 -1
  240. package/src/modules/warranty_claims/commands/shared.ts +7 -1
  241. package/src/modules/warranty_claims/lib/portalAuthGuard.ts +30 -0
  242. package/src/modules/wms/api/sales-orders/[salesOrderId]/warehouse-assignment/route.ts +5 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/staff/api/leave-requests/accept/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { staffLeaveRequestDecisionSchema, type StaffLeaveRequestDecisionInput } from '../../../data/validators'\nimport {\n resolveUserFeatures,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../guards'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.leave_requests.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, translate }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, translate } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const input = parseScopedCommandInput(staffLeaveRequestDecisionSchema, body, ctx, translate)\n\n const auth = ctx.auth\n const tenantId = auth?.tenantId ?? ''\n const organizationId = ctx.selectedOrganizationId ?? null\n const guardResult = await runStaffMutationGuards(\n ctx.container,\n {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.leave_request',\n resourceId: input.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n },\n resolveUserFeatures(auth),\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<StaffLeaveRequestDecisionInput, { requestId: string }>(\n 'staff.leave-requests.accept',\n { input, ctx },\n )\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.leave_request',\n resourceId: result?.requestId ?? input.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n const response = NextResponse.json({ ok: true, id: result?.requestId ?? null }, { status: 200 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'staff.leave_request',\n resourceId: logEntry.resourceId ?? result?.requestId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.leave-requests.accept failed', { err })\n return NextResponse.json({ error: translate('staff.leaveRequests.errors.accept', 'Failed to approve leave request.') }, { status: 400 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Approve leave request',\n methods: {\n POST: {\n summary: 'Approve leave request',\n description: 'Approves a leave request and adds unavailability rules for the staff member.',\n requestBody: {\n contentType: 'application/json',\n schema: staffLeaveRequestDecisionSchema,\n },\n responses: [\n { status: 200, description: 'Leave request approved', schema: z.object({ ok: z.literal(true), id: z.string().uuid().nullable() }) },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
- "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,8BAA8B;AACvC,SAAS,0BAA0B;AACnC,SAAS,0CAA0C;AACnD,SAAS,kCAAkC;AAE3C,SAAS,eAAe,uBAAuB;AAC/C,SAAS,2BAA2B;AACpC,SAAS,+BAA+B;AAExC,SAAS,uCAA4E;AACrF;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,oBAAoB;AAE7B,MAAM,SAAS,aAAa,OAAO;AAE5B,MAAM,WAAW;AAAA,EACtB,MAAM,EAAE,aAAa,MAAM,iBAAiB,CAAC,6BAA6B,EAAE;AAC9E;AAEA,eAAe,aACb,KACgG;AAChG,QAAM,YAAY,MAAM,uBAAuB;AAC/C,QAAM,OAAO,MAAM,mBAAmB,GAAG;AACzC,QAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,MAAI,CAAC,KAAM,OAAM,IAAI,cAAc,KAAK,EAAE,OAAO,UAAU,6BAA6B,cAAc,EAAE,CAAC;AACzG,QAAM,QAAQ,MAAM,mCAAmC,EAAE,WAAW,MAAM,SAAS,IAAI,CAAC;AACxF,QAAM,MAA6B;AAAA,IACjC;AAAA,IACA;AAAA,IACA,mBAAmB;AAAA,IACnB,wBAAwB,OAAO,cAAc,KAAK,SAAS;AAAA,IAC3D,iBAAiB,OAAO,cAAc,KAAK,QAAQ,CAAC,KAAK,KAAK,IAAI;AAAA,IAClE,SAAS;AAAA,EACX;AACA,SAAO,EAAE,KAAK,UAAU;AAC1B;AAEA,eAAsB,KAAK,KAAc;AACvC,MAAI;AACF,UAAM,EAAE,KAAK,UAAU,IAAI,MAAM,aAAa,GAAG;AACjD,UAAM,OAAO,MAAM,IAAI,KAAK,EAAE,MAAM,OAAO,CAAC,EAAE;AAC9C,UAAM,QAAQ,wBAAwB,iCAAiC,MAAM,KAAK,SAAS;AAE3F,UAAM,OAAO,IAAI;AACjB,UAAM,WAAW,MAAM,YAAY;AACnC,UAAM,iBAAiB,IAAI,0BAA0B;AACrD,UAAM,cAAc,MAAM;AAAA,MACxB,IAAI;AAAA,MACJ;AAAA,QACE;AAAA,QACA;AAAA,QACA,QAAQ,MAAM,OAAO;AAAA,QACrB,cAAc;AAAA,QACd,YAAY,MAAM;AAAA,QAClB,WAAW;AAAA,QACX,eAAe,IAAI;AAAA,QACnB,gBAAgB,IAAI;AAAA,QACpB,iBAAiB;AAAA,MACnB;AAAA,MACA,oBAAoB,IAAI;AAAA,IAC1B;AACA,QAAI,CAAC,YAAY,IAAI;AACnB,aAAO,aAAa;AAAA,QAClB,YAAY,aAAa,EAAE,OAAO,6BAA6B;AAAA,QAC/D,EAAE,QAAQ,YAAY,eAAe,IAAI;AAAA,MAC3C;AAAA,IACF;AAEA,UAAM,aAAc,IAAI,UAAU,QAAQ,YAAY;AACtD,UAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,WAAW;AAAA,MAC5C;AAAA,MACA,EAAE,OAAO,IAAI;AAAA,IACf;AAEA,QAAI,YAAY,sBAAsB,QAAQ;AAC5C,YAAM,kCAAkC,YAAY,uBAAuB;AAAA,QACzE;AAAA,QACA;AAAA,QACA,QAAQ,MAAM,OAAO;AAAA,QACrB,cAAc;AAAA,QACd,YAAY,QAAQ,aAAa,MAAM;AAAA,QACvC,WAAW;AAAA,QACX,eAAe,IAAI;AAAA,QACnB,gBAAgB,IAAI;AAAA,MACtB,CAAC;AAAA,IACH;AAEA,UAAM,WAAW,aAAa,KAAK,EAAE,IAAI,MAAM,IAAI,QAAQ,aAAa,KAAK,GAAG,EAAE,QAAQ,IAAI,CAAC;AAC/F,QAAI,UAAU,aAAa,UAAU,MAAM,UAAU,WAAW;AAC9D,eAAS,QAAQ;AAAA,QACf;AAAA,QACA,2BAA2B;AAAA,UACzB,IAAI,SAAS;AAAA,UACb,WAAW,SAAS;AAAA,UACpB,WAAW,SAAS;AAAA,UACpB,aAAa,SAAS,eAAe;AAAA,UACrC,cAAc,SAAS,gBAAgB;AAAA,UACvC,YAAY,SAAS,cAAc,QAAQ,aAAa;AAAA,UACxD,YAAY,SAAS,qBAAqB,OAAO,SAAS,UAAU,YAAY,IAAI;AAAA,QACtF,CAAC;AAAA,MACH;AAAA,IACF;AACA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,UAAM,EAAE,UAAU,IAAI,MAAM,oBAAoB;AAChD,WAAO,MAAM,sCAAsC,EAAE,IAAI,CAAC;AAC1D,WAAO,aAAa,KAAK,EAAE,OAAO,UAAU,qCAAqC,kCAAkC,EAAE,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EACzI;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,SAAS;AAAA,IACP,MAAM;AAAA,MACJ,SAAS;AAAA,MACT,aAAa;AAAA,MACb,aAAa;AAAA,QACX,aAAa;AAAA,QACb,QAAQ;AAAA,MACV;AAAA,MACA,WAAW;AAAA,QACT,EAAE,QAAQ,KAAK,aAAa,0BAA0B,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,IAAI,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,EAAE;AAAA,QAClI,EAAE,QAAQ,KAAK,aAAa,mBAAmB,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE;AAAA,QACvF,EAAE,QAAQ,KAAK,aAAa,gBAAgB,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE;AAAA,QACpF,EAAE,QAAQ,KAAK,aAAa,aAAa,QAAQ,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE;AAAA,MACnF;AAAA,IACF;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { staffLeaveRequestDecisionSchema, type StaffLeaveRequestDecisionInput } from '../../../data/validators'\nimport {\n resolveUserFeatures,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../guards'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.leave_requests.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, translate }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, translate } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const input = parseScopedCommandInput(staffLeaveRequestDecisionSchema, body, ctx, translate)\n\n const auth = ctx.auth\n const tenantId = auth?.tenantId ?? ''\n const organizationId = ctx.selectedOrganizationId ?? null\n const guardResult = await runStaffMutationGuards(\n ctx.container,\n {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.leave_request',\n resourceId: input.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n },\n resolveUserFeatures(auth),\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<StaffLeaveRequestDecisionInput, { requestId: string }>(\n 'staff.leave-requests.accept',\n { input, ctx },\n )\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.leave_request',\n resourceId: result?.requestId ?? input.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n const response = NextResponse.json({ ok: true, id: result?.requestId ?? null }, { status: 200 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'staff.leave_request',\n resourceId: logEntry.resourceId ?? result?.requestId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.leave-requests.accept failed', { err })\n return NextResponse.json({ error: translate('staff.leaveRequests.errors.accept', 'Failed to approve leave request.') }, { status: 400 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Approve leave request',\n methods: {\n POST: {\n summary: 'Approve leave request',\n description: 'Approves a leave request and adds unavailability rules for the staff member.',\n requestBody: {\n contentType: 'application/json',\n schema: staffLeaveRequestDecisionSchema,\n },\n responses: [\n { status: 200, description: 'Leave request approved', schema: z.object({ ok: z.literal(true), id: z.string().uuid().nullable() }) },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -14,6 +14,7 @@ import {
14
14
  runStaffMutationGuards
15
15
  } from "../../guards.js";
16
16
  import { createLogger } from "@open-mercato/shared/lib/logger";
17
+ import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
17
18
  const logger = createLogger("staff");
18
19
  const metadata = {
19
20
  POST: { requireAuth: true, requireFeatures: ["staff.leave_requests.manage"] }
@@ -100,6 +101,10 @@ async function POST(req) {
100
101
  if (isCrudHttpError(err)) {
101
102
  return NextResponse.json(err.body, { status: err.status });
102
103
  }
104
+ const interceptorRejection = getCommandInterceptorHttpRejection(err);
105
+ if (interceptorRejection) {
106
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
107
+ }
103
108
  const { translate } = await resolveTranslations();
104
109
  logger.error("staff.leave-requests.reject failed", { err });
105
110
  return NextResponse.json({ error: translate("staff.leaveRequests.errors.reject", "Failed to reject leave request.") }, { status: 400 });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/staff/api/leave-requests/reject/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { staffLeaveRequestDecisionSchema, type StaffLeaveRequestDecisionInput } from '../../../data/validators'\nimport {\n resolveUserFeatures,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../guards'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.leave_requests.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, translate }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, translate } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const input = parseScopedCommandInput(staffLeaveRequestDecisionSchema, body, ctx, translate)\n\n const auth = ctx.auth\n const tenantId = auth?.tenantId ?? ''\n const organizationId = ctx.selectedOrganizationId ?? null\n const guardResult = await runStaffMutationGuards(\n ctx.container,\n {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.leave_request',\n resourceId: input.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n },\n resolveUserFeatures(auth),\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<StaffLeaveRequestDecisionInput, { requestId: string }>(\n 'staff.leave-requests.reject',\n { input, ctx },\n )\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.leave_request',\n resourceId: result?.requestId ?? input.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n const response = NextResponse.json({ ok: true, id: result?.requestId ?? null }, { status: 200 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'staff.leave_request',\n resourceId: logEntry.resourceId ?? result?.requestId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.leave-requests.reject failed', { err })\n return NextResponse.json({ error: translate('staff.leaveRequests.errors.reject', 'Failed to reject leave request.') }, { status: 400 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Reject leave request',\n methods: {\n POST: {\n summary: 'Reject leave request',\n description: 'Rejects a leave request with an optional comment.',\n requestBody: {\n contentType: 'application/json',\n schema: staffLeaveRequestDecisionSchema,\n },\n responses: [\n { status: 200, description: 'Leave request rejected', schema: z.object({ ok: z.literal(true), id: z.string().uuid().nullable() }) },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { staffLeaveRequestDecisionSchema, type StaffLeaveRequestDecisionInput } from '../../../data/validators'\nimport {\n resolveUserFeatures,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../guards'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.leave_requests.manage'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, translate }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, translate } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const input = parseScopedCommandInput(staffLeaveRequestDecisionSchema, body, ctx, translate)\n\n const auth = ctx.auth\n const tenantId = auth?.tenantId ?? ''\n const organizationId = ctx.selectedOrganizationId ?? null\n const guardResult = await runStaffMutationGuards(\n ctx.container,\n {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.leave_request',\n resourceId: input.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n },\n resolveUserFeatures(auth),\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<StaffLeaveRequestDecisionInput, { requestId: string }>(\n 'staff.leave-requests.reject',\n { input, ctx },\n )\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.leave_request',\n resourceId: result?.requestId ?? input.id,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n const response = NextResponse.json({ ok: true, id: result?.requestId ?? null }, { status: 200 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'staff.leave_request',\n resourceId: logEntry.resourceId ?? result?.requestId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.leave-requests.reject failed', { err })\n return NextResponse.json({ error: translate('staff.leaveRequests.errors.reject', 'Failed to reject leave request.') }, { status: 400 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Reject leave request',\n methods: {\n POST: {\n summary: 'Reject leave request',\n description: 'Rejects a leave request with an optional comment.',\n requestBody: {\n contentType: 'application/json',\n schema: staffLeaveRequestDecisionSchema,\n },\n responses: [\n { status: 200, description: 'Leave request rejected', schema: z.object({ ok: z.literal(true), id: z.string().uuid().nullable() }) },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -18,6 +18,7 @@ import {
18
18
  runStaffMutationGuards
19
19
  } from "../../guards.js";
20
20
  import { createLogger } from "@open-mercato/shared/lib/logger";
21
+ import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
21
22
  const logger = createLogger("staff");
22
23
  const metadata = {
23
24
  GET: { requireAuth: true, requireFeatures: ["staff.leave_requests.send"] },
@@ -167,6 +168,10 @@ async function POST(req) {
167
168
  if (isCrudHttpError(err)) {
168
169
  return NextResponse.json(err.body, { status: err.status });
169
170
  }
171
+ const interceptorRejection = getCommandInterceptorHttpRejection(err);
172
+ if (interceptorRejection) {
173
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
174
+ }
170
175
  const { translate } = await resolveTranslations();
171
176
  logger.error("staff.teamMembers.self.create failed", { err });
172
177
  return NextResponse.json({ error: translate("staff.teamMembers.form.errors.create", "Failed to create team member.") }, { status: 400 });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/staff/api/team-members/self/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { StaffTeamMember } from '../../../data/entities'\nimport {\n staffTeamMemberSelfCreateSchema,\n type StaffTeamMemberSelfCreateInput,\n type StaffTeamMemberCreateInput,\n} from '../../../data/validators'\nimport {\n resolveUserFeatures,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../guards'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['staff.leave_requests.send'] },\n POST: { requireAuth: true, requireFeatures: ['staff.leave_requests.send'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, translate }\n}\n\nconst selfMemberResponseSchema = z.object({\n member: z\n .object({\n id: z.string().uuid(),\n displayName: z.string(),\n userId: z.string().uuid().nullable(),\n teamId: z.string().uuid().nullable(),\n availabilityRuleSetId: z.string().uuid().nullable(),\n })\n .nullable(),\n})\n\nexport async function GET(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const auth = ctx.auth\n if (!auth?.sub) return NextResponse.json({ member: null })\n const em = (ctx.container.resolve('em') as any)\n const member = await findOneWithDecryption(\n em,\n StaffTeamMember,\n { userId: auth.sub, deletedAt: null },\n undefined,\n { tenantId: auth.tenantId ?? null, organizationId: ctx.selectedOrganizationId ?? null },\n )\n if (!member) return NextResponse.json({ member: null })\n return NextResponse.json({\n member: {\n id: member.id,\n displayName: member.displayName,\n userId: member.userId ?? null,\n teamId: member.teamId ?? null,\n availabilityRuleSetId: member.availabilityRuleSetId ?? null,\n },\n })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n logger.error('staff.teamMembers.self.load failed', { err })\n return NextResponse.json({ member: null })\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, translate } = await buildContext(req)\n const auth = ctx.auth\n if (!auth?.sub) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const body = await req.json().catch(() => ({}))\n const parsed = parseScopedCommandInput(staffTeamMemberSelfCreateSchema, body, ctx, translate)\n const em = (ctx.container.resolve('em') as any)\n const existing = await findOneWithDecryption(\n em,\n StaffTeamMember,\n { userId: auth.sub, deletedAt: null },\n undefined,\n { tenantId: auth.tenantId ?? null, organizationId: ctx.selectedOrganizationId ?? null },\n )\n if (existing) {\n throw new CrudHttpError(409, { error: translate('staff.teamMembers.self.exists', 'Team member profile already exists.') })\n }\n\n const tenantId = auth.tenantId ?? ''\n const organizationId = ctx.selectedOrganizationId ?? null\n const guardResult = await runStaffMutationGuards(\n ctx.container,\n {\n tenantId,\n organizationId,\n userId: auth.sub,\n resourceKind: 'staff.team_member',\n resourceId: auth.sub,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: parsed,\n },\n resolveUserFeatures(auth),\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const selfInput: StaffTeamMemberCreateInput = {\n ...parsed,\n userId: auth.sub,\n teamId: null,\n roleIds: [],\n tags: [],\n availabilityRuleSetId: null,\n isActive: true,\n }\n const { result, logEntry } = await commandBus.execute<StaffTeamMemberCreateInput, { memberId: string }>(\n 'staff.team-members.create',\n {\n input: selfInput,\n ctx,\n },\n )\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth.sub,\n resourceKind: 'staff.team_member',\n resourceId: result?.memberId ?? auth.sub,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n const response = NextResponse.json({ id: result?.memberId ?? null }, { status: 201 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'staff.team_member',\n resourceId: logEntry.resourceId ?? result?.memberId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.teamMembers.self.create failed', { err })\n return NextResponse.json({ error: translate('staff.teamMembers.form.errors.create', 'Failed to create team member.') }, { status: 400 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Self team member profile',\n methods: {\n GET: {\n summary: 'Get current user team member profile',\n description: 'Returns the staff team member linked to the current user, if any.',\n responses: [\n { status: 200, description: 'Team member profile', schema: selfMemberResponseSchema },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n ],\n },\n POST: {\n summary: 'Create current user team member profile',\n description: 'Creates a team member profile for the signed-in user.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTeamMemberSelfCreateSchema,\n },\n responses: [\n { status: 201, description: 'Team member created', schema: z.object({ id: z.string().uuid().nullable() }) },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 409, description: 'Already exists', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { StaffTeamMember } from '../../../data/entities'\nimport {\n staffTeamMemberSelfCreateSchema,\n type StaffTeamMemberSelfCreateInput,\n type StaffTeamMemberCreateInput,\n} from '../../../data/validators'\nimport {\n resolveUserFeatures,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../guards'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['staff.leave_requests.send'] },\n POST: { requireAuth: true, requireFeatures: ['staff.leave_requests.send'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, translate }\n}\n\nconst selfMemberResponseSchema = z.object({\n member: z\n .object({\n id: z.string().uuid(),\n displayName: z.string(),\n userId: z.string().uuid().nullable(),\n teamId: z.string().uuid().nullable(),\n availabilityRuleSetId: z.string().uuid().nullable(),\n })\n .nullable(),\n})\n\nexport async function GET(req: Request) {\n try {\n const { ctx } = await buildContext(req)\n const auth = ctx.auth\n if (!auth?.sub) return NextResponse.json({ member: null })\n const em = (ctx.container.resolve('em') as any)\n const member = await findOneWithDecryption(\n em,\n StaffTeamMember,\n { userId: auth.sub, deletedAt: null },\n undefined,\n { tenantId: auth.tenantId ?? null, organizationId: ctx.selectedOrganizationId ?? null },\n )\n if (!member) return NextResponse.json({ member: null })\n return NextResponse.json({\n member: {\n id: member.id,\n displayName: member.displayName,\n userId: member.userId ?? null,\n teamId: member.teamId ?? null,\n availabilityRuleSetId: member.availabilityRuleSetId ?? null,\n },\n })\n } catch (err) {\n if (isCrudHttpError(err)) return NextResponse.json(err.body, { status: err.status })\n logger.error('staff.teamMembers.self.load failed', { err })\n return NextResponse.json({ member: null })\n }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, translate } = await buildContext(req)\n const auth = ctx.auth\n if (!auth?.sub) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const body = await req.json().catch(() => ({}))\n const parsed = parseScopedCommandInput(staffTeamMemberSelfCreateSchema, body, ctx, translate)\n const em = (ctx.container.resolve('em') as any)\n const existing = await findOneWithDecryption(\n em,\n StaffTeamMember,\n { userId: auth.sub, deletedAt: null },\n undefined,\n { tenantId: auth.tenantId ?? null, organizationId: ctx.selectedOrganizationId ?? null },\n )\n if (existing) {\n throw new CrudHttpError(409, { error: translate('staff.teamMembers.self.exists', 'Team member profile already exists.') })\n }\n\n const tenantId = auth.tenantId ?? ''\n const organizationId = ctx.selectedOrganizationId ?? null\n const guardResult = await runStaffMutationGuards(\n ctx.container,\n {\n tenantId,\n organizationId,\n userId: auth.sub,\n resourceKind: 'staff.team_member',\n resourceId: auth.sub,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: parsed,\n },\n resolveUserFeatures(auth),\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const selfInput: StaffTeamMemberCreateInput = {\n ...parsed,\n userId: auth.sub,\n teamId: null,\n roleIds: [],\n tags: [],\n availabilityRuleSetId: null,\n isActive: true,\n }\n const { result, logEntry } = await commandBus.execute<StaffTeamMemberCreateInput, { memberId: string }>(\n 'staff.team-members.create',\n {\n input: selfInput,\n ctx,\n },\n )\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth.sub,\n resourceKind: 'staff.team_member',\n resourceId: result?.memberId ?? auth.sub,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n const response = NextResponse.json({ id: result?.memberId ?? null }, { status: 201 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'staff.team_member',\n resourceId: logEntry.resourceId ?? result?.memberId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.teamMembers.self.create failed', { err })\n return NextResponse.json({ error: translate('staff.teamMembers.form.errors.create', 'Failed to create team member.') }, { status: 400 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Self team member profile',\n methods: {\n GET: {\n summary: 'Get current user team member profile',\n description: 'Returns the staff team member linked to the current user, if any.',\n responses: [\n { status: 200, description: 'Team member profile', schema: selfMemberResponseSchema },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n ],\n },\n POST: {\n summary: 'Create current user team member profile',\n description: 'Creates a team member profile for the signed-in user.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTeamMemberSelfCreateSchema,\n },\n responses: [\n { status: 201, description: 'Team member created', schema: z.object({ id: z.string().uuid().nullable() }) },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 409, description: 'Already exists', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -16,6 +16,7 @@ import {
16
16
  runStaffMutationGuards
17
17
  } from "../../../guards.js";
18
18
  import { createLogger } from "@open-mercato/shared/lib/logger";
19
+ import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
19
20
  const logger = createLogger("staff");
20
21
  const metadata = {
21
22
  POST: { requireAuth: true, requireFeatures: ["staff.manage_team"] }
@@ -102,6 +103,10 @@ async function POST(req) {
102
103
  if (isCrudHttpError(err)) {
103
104
  return NextResponse.json(err.body, { status: err.status });
104
105
  }
106
+ const interceptorRejection = getCommandInterceptorHttpRejection(err);
107
+ if (interceptorRejection) {
108
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
109
+ }
105
110
  const { translate } = await resolveTranslations();
106
111
  logger.error("staff.teamMembers.tags.assign failed", { err });
107
112
  return NextResponse.json({ error: translate("staff.teamMembers.tags.updateError", "Failed to update tags.") }, { status: 400 });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/staff/api/team-members/tags/assign/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n staffTeamMemberTagAssignmentSchema,\n type StaffTeamMemberTagAssignmentInput,\n} from '../../../../data/validators'\nimport {\n resolveUserFeatures,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../../guards'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.manage_team'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, translate }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, translate } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const input = parseScopedCommandInput(staffTeamMemberTagAssignmentSchema, body, ctx, translate)\n\n const auth = ctx.auth\n const tenantId = auth?.tenantId ?? ''\n const organizationId = ctx.selectedOrganizationId ?? null\n const guardResult = await runStaffMutationGuards(\n ctx.container,\n {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.team_member',\n resourceId: input.memberId,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n },\n resolveUserFeatures(auth),\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<StaffTeamMemberTagAssignmentInput, { memberId: string }>(\n 'staff.team-members.tags.assign',\n { input, ctx },\n )\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.team_member',\n resourceId: result?.memberId ?? input.memberId,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n const response = NextResponse.json({ id: result?.memberId ?? null }, { status: 201 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'staff.teamMemberTagAssignment',\n resourceId: logEntry.resourceId ?? result?.memberId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.teamMembers.tags.assign failed', { err })\n return NextResponse.json({ error: translate('staff.teamMembers.tags.updateError', 'Failed to update tags.') }, { status: 400 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Assign team member tag',\n methods: {\n POST: {\n summary: 'Assign team member tag',\n description: 'Assigns a tag to a staff team member.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTeamMemberTagAssignmentSchema,\n },\n responses: [\n { status: 201, description: 'Tag assignment created', schema: z.object({ id: z.string().uuid().nullable() }) },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n staffTeamMemberTagAssignmentSchema,\n type StaffTeamMemberTagAssignmentInput,\n} from '../../../../data/validators'\nimport {\n resolveUserFeatures,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../../guards'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.manage_team'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, translate }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, translate } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const input = parseScopedCommandInput(staffTeamMemberTagAssignmentSchema, body, ctx, translate)\n\n const auth = ctx.auth\n const tenantId = auth?.tenantId ?? ''\n const organizationId = ctx.selectedOrganizationId ?? null\n const guardResult = await runStaffMutationGuards(\n ctx.container,\n {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.team_member',\n resourceId: input.memberId,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n },\n resolveUserFeatures(auth),\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<StaffTeamMemberTagAssignmentInput, { memberId: string }>(\n 'staff.team-members.tags.assign',\n { input, ctx },\n )\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.team_member',\n resourceId: result?.memberId ?? input.memberId,\n operation: 'create',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n const response = NextResponse.json({ id: result?.memberId ?? null }, { status: 201 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'staff.teamMemberTagAssignment',\n resourceId: logEntry.resourceId ?? result?.memberId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.teamMembers.tags.assign failed', { err })\n return NextResponse.json({ error: translate('staff.teamMembers.tags.updateError', 'Failed to update tags.') }, { status: 400 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Assign team member tag',\n methods: {\n POST: {\n summary: 'Assign team member tag',\n description: 'Assigns a tag to a staff team member.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTeamMemberTagAssignmentSchema,\n },\n responses: [\n { status: 201, description: 'Tag assignment created', schema: z.object({ id: z.string().uuid().nullable() }) },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -16,6 +16,7 @@ import {
16
16
  runStaffMutationGuards
17
17
  } from "../../../guards.js";
18
18
  import { createLogger } from "@open-mercato/shared/lib/logger";
19
+ import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
19
20
  const logger = createLogger("staff");
20
21
  const metadata = {
21
22
  POST: { requireAuth: true, requireFeatures: ["staff.manage_team"] }
@@ -102,6 +103,10 @@ async function POST(req) {
102
103
  if (isCrudHttpError(err)) {
103
104
  return NextResponse.json(err.body, { status: err.status });
104
105
  }
106
+ const interceptorRejection = getCommandInterceptorHttpRejection(err);
107
+ if (interceptorRejection) {
108
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
109
+ }
105
110
  const { translate } = await resolveTranslations();
106
111
  logger.error("staff.teamMembers.tags.unassign failed", { err });
107
112
  return NextResponse.json({ error: translate("staff.teamMembers.tags.updateError", "Failed to update tags.") }, { status: 400 });
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/staff/api/team-members/tags/unassign/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n staffTeamMemberTagAssignmentSchema,\n type StaffTeamMemberTagAssignmentInput,\n} from '../../../../data/validators'\nimport {\n resolveUserFeatures,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../../guards'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.manage_team'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, translate }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, translate } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const input = parseScopedCommandInput(staffTeamMemberTagAssignmentSchema, body, ctx, translate)\n\n const auth = ctx.auth\n const tenantId = auth?.tenantId ?? ''\n const organizationId = ctx.selectedOrganizationId ?? null\n const guardResult = await runStaffMutationGuards(\n ctx.container,\n {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.team_member',\n resourceId: input.memberId,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n },\n resolveUserFeatures(auth),\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<StaffTeamMemberTagAssignmentInput, { memberId: string }>(\n 'staff.team-members.tags.unassign',\n { input, ctx },\n )\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.team_member',\n resourceId: result?.memberId ?? input.memberId,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n const response = NextResponse.json({ id: result?.memberId ?? null })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'staff.teamMemberTagAssignment',\n resourceId: logEntry.resourceId ?? result?.memberId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.teamMembers.tags.unassign failed', { err })\n return NextResponse.json({ error: translate('staff.teamMembers.tags.updateError', 'Failed to update tags.') }, { status: 400 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Unassign team member tag',\n methods: {\n POST: {\n summary: 'Unassign team member tag',\n description: 'Removes a tag from a staff team member.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTeamMemberTagAssignmentSchema,\n },\n responses: [\n { status: 200, description: 'Tag assignment removed', schema: z.object({ id: z.string().uuid().nullable() }) },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport {\n staffTeamMemberTagAssignmentSchema,\n type StaffTeamMemberTagAssignmentInput,\n} from '../../../../data/validators'\nimport {\n resolveUserFeatures,\n runStaffMutationGuardAfterSuccess,\n runStaffMutationGuards,\n} from '../../../guards'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.manage_team'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, translate }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, translate } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const input = parseScopedCommandInput(staffTeamMemberTagAssignmentSchema, body, ctx, translate)\n\n const auth = ctx.auth\n const tenantId = auth?.tenantId ?? ''\n const organizationId = ctx.selectedOrganizationId ?? null\n const guardResult = await runStaffMutationGuards(\n ctx.container,\n {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.team_member',\n resourceId: input.memberId,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: input,\n },\n resolveUserFeatures(auth),\n )\n if (!guardResult.ok) {\n return NextResponse.json(\n guardResult.errorBody ?? { error: 'Operation blocked by guard' },\n { status: guardResult.errorStatus ?? 422 },\n )\n }\n\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<StaffTeamMemberTagAssignmentInput, { memberId: string }>(\n 'staff.team-members.tags.unassign',\n { input, ctx },\n )\n\n if (guardResult.afterSuccessCallbacks.length) {\n await runStaffMutationGuardAfterSuccess(guardResult.afterSuccessCallbacks, {\n tenantId,\n organizationId,\n userId: auth?.sub ?? '',\n resourceKind: 'staff.team_member',\n resourceId: result?.memberId ?? input.memberId,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n })\n }\n\n const response = NextResponse.json({ id: result?.memberId ?? null })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'staff.teamMemberTagAssignment',\n resourceId: logEntry.resourceId ?? result?.memberId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.teamMembers.tags.unassign failed', { err })\n return NextResponse.json({ error: translate('staff.teamMembers.tags.updateError', 'Failed to update tags.') }, { status: 400 })\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Unassign team member tag',\n methods: {\n POST: {\n summary: 'Unassign team member tag',\n description: 'Removes a tag from a staff team member.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTeamMemberTagAssignmentSchema,\n },\n responses: [\n { status: 200, description: 'Tag assignment removed', schema: z.object({ id: z.string().uuid().nullable() }) },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -9,6 +9,7 @@ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
9
9
  import { parseScopedCommandInput } from "@open-mercato/shared/lib/api/scoped";
10
10
  import { staffTimeEntryStartTimerSchema } from "../../../../data/validators.js";
11
11
  import { createLogger } from "@open-mercato/shared/lib/logger";
12
+ import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
12
13
  const logger = createLogger("staff");
13
14
  const metadata = {
14
15
  POST: { requireAuth: true, requireFeatures: ["staff.timesheets.manage_own"] }
@@ -59,6 +60,10 @@ async function POST(req) {
59
60
  if (isCrudHttpError(err)) {
60
61
  return NextResponse.json(err.body, { status: err.status });
61
62
  }
63
+ const interceptorRejection = getCommandInterceptorHttpRejection(err);
64
+ if (interceptorRejection) {
65
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
66
+ }
62
67
  const { translate } = await resolveTranslations();
63
68
  logger.error("staff.timesheets.time-entries.start-timer failed", { err });
64
69
  return NextResponse.json(
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/staff/api/timesheets/time-entries/start-timer/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { staffTimeEntryStartTimerSchema, type StaffTimeEntryStartTimerInput } from '../../../../data/validators'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, translate }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, translate } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const input = parseScopedCommandInput(staffTimeEntryStartTimerSchema, body, ctx, translate)\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<StaffTimeEntryStartTimerInput, { timeEntryId: string }>(\n 'staff.timesheets.time_entries.start_timer',\n { input, ctx },\n )\n const response = NextResponse.json({ ok: true, id: result?.timeEntryId ?? null }, { status: 201 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'staff.timesheets.time_entry',\n resourceId: logEntry.resourceId ?? result?.timeEntryId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.timesheets.time-entries.start-timer failed', { err })\n return NextResponse.json(\n { error: translate('staff.timesheets.errors.timerStart', 'Failed to start timer.') },\n { status: 400 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Start a timesheet timer',\n methods: {\n POST: {\n summary: 'Start a timesheet timer',\n description:\n 'Atomically creates a timer-sourced time entry and starts it (sets startedAt and creates the initial work segment) in a single transaction, so a partial failure cannot leave an orphaned, unstarted entry.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTimeEntryStartTimerSchema,\n },\n responses: [\n {\n status: 201,\n description: 'Timer started',\n schema: z.object({ ok: z.literal(true), id: z.string().uuid().nullable() }),\n },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n {\n status: 409,\n description: 'Another timer is already running for this staff member',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 422,\n description: 'Referenced time project not found or out of scope',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport { getAuthFromRequest } from '@open-mercato/shared/lib/auth/server'\nimport { resolveOrganizationScopeForRequest } from '@open-mercato/core/modules/directory/utils/organizationScope'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport type { CommandRuntimeContext, CommandBus } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { parseScopedCommandInput } from '@open-mercato/shared/lib/api/scoped'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { staffTimeEntryStartTimerSchema, type StaffTimeEntryStartTimerInput } from '../../../../data/validators'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\n\nconst logger = createLogger('staff')\n\nexport const metadata = {\n POST: { requireAuth: true, requireFeatures: ['staff.timesheets.manage_own'] },\n}\n\nasync function buildContext(\n req: Request\n): Promise<{ ctx: CommandRuntimeContext; translate: (key: string, fallback?: string) => string }> {\n const container = await createRequestContainer()\n const auth = await getAuthFromRequest(req)\n const { translate } = await resolveTranslations()\n if (!auth) throw new CrudHttpError(401, { error: translate('staff.errors.unauthorized', 'Unauthorized') })\n const scope = await resolveOrganizationScopeForRequest({ container, auth, request: req })\n const ctx: CommandRuntimeContext = {\n container,\n auth,\n organizationScope: scope,\n selectedOrganizationId: scope?.selectedId ?? auth.orgId ?? null,\n organizationIds: scope?.filterIds ?? (auth.orgId ? [auth.orgId] : null),\n request: req,\n }\n return { ctx, translate }\n}\n\nexport async function POST(req: Request) {\n try {\n const { ctx, translate } = await buildContext(req)\n const body = await req.json().catch(() => ({}))\n const input = parseScopedCommandInput(staffTimeEntryStartTimerSchema, body, ctx, translate)\n const commandBus = (ctx.container.resolve('commandBus') as CommandBus)\n const { result, logEntry } = await commandBus.execute<StaffTimeEntryStartTimerInput, { timeEntryId: string }>(\n 'staff.timesheets.time_entries.start_timer',\n { input, ctx },\n )\n const response = NextResponse.json({ ok: true, id: result?.timeEntryId ?? null }, { status: 201 })\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'staff.timesheets.time_entry',\n resourceId: logEntry.resourceId ?? result?.timeEntryId ?? null,\n executedAt: logEntry.createdAt instanceof Date ? logEntry.createdAt.toISOString() : undefined,\n }),\n )\n }\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n const { translate } = await resolveTranslations()\n logger.error('staff.timesheets.time-entries.start-timer failed', { err })\n return NextResponse.json(\n { error: translate('staff.timesheets.errors.timerStart', 'Failed to start timer.') },\n { status: 400 },\n )\n }\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Staff',\n summary: 'Start a timesheet timer',\n methods: {\n POST: {\n summary: 'Start a timesheet timer',\n description:\n 'Atomically creates a timer-sourced time entry and starts it (sets startedAt and creates the initial work segment) in a single transaction, so a partial failure cannot leave an orphaned, unstarted entry.',\n requestBody: {\n contentType: 'application/json',\n schema: staffTimeEntryStartTimerSchema,\n },\n responses: [\n {\n status: 201,\n description: 'Timer started',\n schema: z.object({ ok: z.literal(true), id: z.string().uuid().nullable() }),\n },\n { status: 400, description: 'Invalid payload', schema: z.object({ error: z.string() }) },\n { status: 401, description: 'Unauthorized', schema: z.object({ error: z.string() }) },\n { status: 403, description: 'Forbidden', schema: z.object({ error: z.string() }) },\n {\n status: 409,\n description: 'Another timer is already running for this staff member',\n schema: z.object({ error: z.string() }),\n },\n {\n status: 422,\n description: 'Referenced time project not found or out of scope',\n schema: z.object({ error: z.string() }),\n },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -11,6 +11,7 @@ import {
11
11
  import { serializeOperationMetadata } from "@open-mercato/shared/lib/commands/operationMetadata";
12
12
  import { enforceCommandOptimisticLockWithGuards } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
13
13
  import { createLogger } from "@open-mercato/shared/lib/logger";
14
+ import { getCommandInterceptorHttpRejection } from "@open-mercato/shared/lib/commands/errors";
14
15
  const logger = createLogger("translations").child({ component: "entity-translations" });
15
16
  const TRANSLATION_RESOURCE_KIND = "translations.translation";
16
17
  async function loadTranslationRowVersion(db, entityType, entityId, tenantId, organizationId) {
@@ -156,6 +157,10 @@ async function PUT(req, ctx) {
156
157
  if (isCrudHttpError(err)) {
157
158
  return NextResponse.json(err.body, { status: err.status });
158
159
  }
160
+ const interceptorRejection = getCommandInterceptorHttpRejection(err);
161
+ if (interceptorRejection) {
162
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
163
+ }
159
164
  if (err instanceof z.ZodError) {
160
165
  return NextResponse.json({ error: "Validation failed", details: err.issues }, { status: 400 });
161
166
  }
@@ -244,6 +249,10 @@ async function DELETE(req, ctx) {
244
249
  if (isCrudHttpError(err)) {
245
250
  return NextResponse.json(err.body, { status: err.status });
246
251
  }
252
+ const interceptorRejection = getCommandInterceptorHttpRejection(err);
253
+ if (interceptorRejection) {
254
+ return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status });
255
+ }
247
256
  if (err instanceof z.ZodError) {
248
257
  return NextResponse.json({ error: "Invalid parameters", details: err.issues }, { status: 400 });
249
258
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/translations/api/%5BentityType%5D/%5BentityId%5D/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { sql } from 'kysely'\nimport { resolveTranslationsRouteContext, requireTranslationFeatures, resolveTranslationsActorId } from '@open-mercato/core/modules/translations/api/context'\nimport { translationBodySchema, entityTypeParamSchema, entityIdParamSchema } from '@open-mercato/core/modules/translations/data/validators'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\n\nconst logger = createLogger('translations').child({ component: 'entity-translations' })\n\nconst TRANSLATION_RESOURCE_KIND = 'translations.translation'\n\n/**\n * Load the existing translation row's primary key + `updated_at` (the row's own\n * version), scoped to tenant/org. Returns `null` when no row exists yet.\n *\n * Optimistic locking enforces against the TRANSLATION ROW'S OWN version, not the\n * host entity's: the host's EAV `entityType` (`module:entity`) cannot be cleanly\n * mapped to a registered optimistic-lock reader key (those are derived from the\n * host module's ORM entity name / events config, e.g. `CatalogProduct` \u2192\n * `catalog.product`, which does not equal `canonicalizeResourceTag('catalog:catalog_product')`),\n * so there is no reliable server-side path to resolve the host's current version\n * for an arbitrary `entityType`. Guarding the translation row's own `updated_at`\n * closes the no-lock hole with real server-side enforcement and no cross-module\n * coupling (mirrors the hand-written `auth.role_acl` route).\n */\nasync function loadTranslationRowVersion(\n db: any,\n entityType: string,\n entityId: string,\n tenantId: string,\n organizationId: string | null,\n): Promise<{ id: string; updatedAt: Date | string | null } | null> {\n const row = await db\n .selectFrom('entity_translations')\n .select(['id', 'updated_at'])\n .where('entity_type', '=', entityType)\n .where('entity_id', '=', entityId)\n .where(sql<boolean>`tenant_id is not distinct from ${tenantId}`)\n .where(sql<boolean>`organization_id is not distinct from ${organizationId}`)\n .executeTakeFirst() as { id: string; updated_at: Date | string | null } | undefined\n if (!row) return null\n return { id: row.id, updatedAt: row.updated_at ?? null }\n}\n\nconst paramsSchema = z.object({\n entityType: entityTypeParamSchema,\n entityId: entityIdParamSchema,\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['translations.view'] },\n PUT: { requireAuth: true, requireFeatures: ['translations.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['translations.manage'] },\n}\n\nexport async function GET(req: Request, ctx: { params?: { entityType?: string; entityId?: string } }) {\n try {\n const context = await resolveTranslationsRouteContext(req)\n await requireTranslationFeatures(context, ['translations.view'])\n const { entityType, entityId } = paramsSchema.parse({\n entityType: ctx.params?.entityType,\n entityId: ctx.params?.entityId,\n })\n\n const row = await (context.db as any)\n .selectFrom('entity_translations')\n .selectAll()\n .where('entity_type', '=', entityType)\n .where('entity_id', '=', entityId)\n .where(sql<boolean>`tenant_id is not distinct from ${context.tenantId}`)\n .where(sql<boolean>`organization_id is not distinct from ${context.organizationId}`)\n .executeTakeFirst() as Record<string, any> | undefined\n\n if (!row) {\n return NextResponse.json({ error: 'Not found' }, { status: 404 })\n }\n\n return NextResponse.json({\n entityType: row.entity_type,\n entityId: row.entity_id,\n translations: row.translations,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Invalid parameters', details: err.issues }, { status: 400 })\n }\n logger.error('Failed to load translations', { err })\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n\nexport async function PUT(req: Request, ctx: { params?: { entityType?: string; entityId?: string } }) {\n try {\n const context = await resolveTranslationsRouteContext(req)\n await requireTranslationFeatures(context, ['translations.manage'])\n const { entityType, entityId } = paramsSchema.parse({\n entityType: ctx.params?.entityType,\n entityId: ctx.params?.entityId,\n })\n\n const rawText = await req.text()\n let rawBody: unknown = {}\n if (rawText.trim().length > 0) {\n try {\n rawBody = JSON.parse(rawText)\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n }\n const translations = translationBodySchema.parse(rawBody)\n\n const guardUserId = resolveTranslationsActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'translations.translation',\n resourceId: `${entityType}:${entityId}`,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { entityType, entityId, translations },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n // Optimistic lock: refuse a stale standalone PUT so a translation save that\n // started from an out-of-date view cannot silently clobber a concurrent edit.\n // Strictly additive \u2014 no expected-version header \u2192 no-op. Skipped when no row\n // exists yet (first save has no prior version to conflict with).\n const existingVersion = await loadTranslationRowVersion(\n context.db as any,\n entityType,\n entityId,\n context.tenantId,\n context.organizationId,\n )\n if (existingVersion) {\n await enforceCommandOptimisticLockWithGuards(context.container, {\n resourceKind: TRANSLATION_RESOURCE_KIND,\n resourceId: existingVersion.id,\n current: existingVersion.updatedAt,\n request: req,\n })\n }\n\n const commandBus = context.container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<\n { entityType: string; entityId: string; translations: typeof translations; organizationId: string | null; tenantId: string },\n { rowId: string }\n >('translations.translation.save', {\n input: {\n entityType,\n entityId,\n translations,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n },\n ctx: context.commandCtx,\n })\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'translations.translation',\n resourceId: `${entityType}:${entityId}`,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const row = await (context.db as any)\n .selectFrom('entity_translations')\n .selectAll()\n .where('id', '=', result.rowId)\n .executeTakeFirst() as Record<string, any>\n\n const response = NextResponse.json({\n entityType: row.entity_type,\n entityId: row.entity_id,\n translations: row.translations,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n })\n\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'translations.translation',\n resourceId: logEntry.resourceId ?? result.rowId,\n executedAt: logEntry.createdAt instanceof Date\n ? logEntry.createdAt.toISOString()\n : typeof logEntry.createdAt === 'string'\n ? logEntry.createdAt\n : new Date().toISOString(),\n }),\n )\n }\n\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 })\n }\n logger.error('Failed to save translations', { err })\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params?: { entityType?: string; entityId?: string } }) {\n try {\n const context = await resolveTranslationsRouteContext(req)\n await requireTranslationFeatures(context, ['translations.manage'])\n const { entityType, entityId } = paramsSchema.parse({\n entityType: ctx.params?.entityType,\n entityId: ctx.params?.entityId,\n })\n\n const guardUserId = resolveTranslationsActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'translations.translation',\n resourceId: `${entityType}:${entityId}`,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: null,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n // Optimistic lock: refuse a stale standalone DELETE (same hole as PUT).\n // Additive \u2014 no header \u2192 no-op; skipped when no row exists.\n const existingVersion = await loadTranslationRowVersion(\n context.db as any,\n entityType,\n entityId,\n context.tenantId,\n context.organizationId,\n )\n if (existingVersion) {\n await enforceCommandOptimisticLockWithGuards(context.container, {\n resourceKind: TRANSLATION_RESOURCE_KIND,\n resourceId: existingVersion.id,\n current: existingVersion.updatedAt,\n request: req,\n })\n }\n\n const commandBus = context.container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute<\n { entityType: string; entityId: string; organizationId: string | null; tenantId: string },\n { deleted: boolean }\n >('translations.translation.delete', {\n input: {\n entityType,\n entityId,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n },\n ctx: context.commandCtx,\n })\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'translations.translation',\n resourceId: `${entityType}:${entityId}`,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const response = new NextResponse(null, { status: 204 })\n\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'translations.translation',\n resourceId: logEntry.resourceId ?? null,\n executedAt: logEntry.createdAt instanceof Date\n ? logEntry.createdAt.toISOString()\n : typeof logEntry.createdAt === 'string'\n ? logEntry.createdAt\n : new Date().toISOString(),\n }),\n )\n }\n\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Invalid parameters', details: err.issues }, { status: 400 })\n }\n logger.error('Failed to delete translations', { err })\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n\nconst translationsTag = 'Translations'\n\nconst getDoc: OpenApiMethodDoc = {\n summary: 'Get entity translations',\n description: 'Returns the full translation record for a single entity.',\n tags: [translationsTag],\n responses: [\n { status: 200, description: 'Translation record found.' },\n ],\n errors: [\n { status: 401, description: 'Authentication required' },\n { status: 404, description: 'No translations found for this entity' },\n ],\n}\n\nconst putDoc: OpenApiMethodDoc = {\n summary: 'Create or update entity translations',\n description: 'Full replacement of translations JSONB for an entity.',\n tags: [translationsTag],\n responses: [\n { status: 200, description: 'Translations saved.' },\n ],\n errors: [\n { status: 400, description: 'Validation failed' },\n { status: 401, description: 'Authentication required' },\n ],\n}\n\nconst deleteDoc: OpenApiMethodDoc = {\n summary: 'Delete entity translations',\n description: 'Removes all translations for an entity.',\n tags: [translationsTag],\n responses: [\n { status: 204, description: 'Translations deleted.' },\n ],\n errors: [\n { status: 401, description: 'Authentication required' },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: translationsTag,\n summary: 'Entity translation resource',\n pathParams: paramsSchema,\n methods: {\n GET: getDoc,\n PUT: putDoc,\n DELETE: deleteDoc,\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport { sql } from 'kysely'\nimport { resolveTranslationsRouteContext, requireTranslationFeatures, resolveTranslationsActorId } from '@open-mercato/core/modules/translations/api/context'\nimport { translationBodySchema, entityTypeParamSchema, entityIdParamSchema } from '@open-mercato/core/modules/translations/data/validators'\nimport { CrudHttpError, isCrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport {\n runCrudMutationGuardAfterSuccess,\n validateCrudMutationGuard,\n} from '@open-mercato/shared/lib/crud/mutation-guard'\nimport { CommandBus } from '@open-mercato/shared/lib/commands'\nimport { serializeOperationMetadata } from '@open-mercato/shared/lib/commands/operationMetadata'\nimport { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport type { OpenApiMethodDoc, OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport { getCommandInterceptorHttpRejection } from '@open-mercato/shared/lib/commands/errors'\n\nconst logger = createLogger('translations').child({ component: 'entity-translations' })\n\nconst TRANSLATION_RESOURCE_KIND = 'translations.translation'\n\n/**\n * Load the existing translation row's primary key + `updated_at` (the row's own\n * version), scoped to tenant/org. Returns `null` when no row exists yet.\n *\n * Optimistic locking enforces against the TRANSLATION ROW'S OWN version, not the\n * host entity's: the host's EAV `entityType` (`module:entity`) cannot be cleanly\n * mapped to a registered optimistic-lock reader key (those are derived from the\n * host module's ORM entity name / events config, e.g. `CatalogProduct` \u2192\n * `catalog.product`, which does not equal `canonicalizeResourceTag('catalog:catalog_product')`),\n * so there is no reliable server-side path to resolve the host's current version\n * for an arbitrary `entityType`. Guarding the translation row's own `updated_at`\n * closes the no-lock hole with real server-side enforcement and no cross-module\n * coupling (mirrors the hand-written `auth.role_acl` route).\n */\nasync function loadTranslationRowVersion(\n db: any,\n entityType: string,\n entityId: string,\n tenantId: string,\n organizationId: string | null,\n): Promise<{ id: string; updatedAt: Date | string | null } | null> {\n const row = await db\n .selectFrom('entity_translations')\n .select(['id', 'updated_at'])\n .where('entity_type', '=', entityType)\n .where('entity_id', '=', entityId)\n .where(sql<boolean>`tenant_id is not distinct from ${tenantId}`)\n .where(sql<boolean>`organization_id is not distinct from ${organizationId}`)\n .executeTakeFirst() as { id: string; updated_at: Date | string | null } | undefined\n if (!row) return null\n return { id: row.id, updatedAt: row.updated_at ?? null }\n}\n\nconst paramsSchema = z.object({\n entityType: entityTypeParamSchema,\n entityId: entityIdParamSchema,\n})\n\nexport const metadata = {\n GET: { requireAuth: true, requireFeatures: ['translations.view'] },\n PUT: { requireAuth: true, requireFeatures: ['translations.manage'] },\n DELETE: { requireAuth: true, requireFeatures: ['translations.manage'] },\n}\n\nexport async function GET(req: Request, ctx: { params?: { entityType?: string; entityId?: string } }) {\n try {\n const context = await resolveTranslationsRouteContext(req)\n await requireTranslationFeatures(context, ['translations.view'])\n const { entityType, entityId } = paramsSchema.parse({\n entityType: ctx.params?.entityType,\n entityId: ctx.params?.entityId,\n })\n\n const row = await (context.db as any)\n .selectFrom('entity_translations')\n .selectAll()\n .where('entity_type', '=', entityType)\n .where('entity_id', '=', entityId)\n .where(sql<boolean>`tenant_id is not distinct from ${context.tenantId}`)\n .where(sql<boolean>`organization_id is not distinct from ${context.organizationId}`)\n .executeTakeFirst() as Record<string, any> | undefined\n\n if (!row) {\n return NextResponse.json({ error: 'Not found' }, { status: 404 })\n }\n\n return NextResponse.json({\n entityType: row.entity_type,\n entityId: row.entity_id,\n translations: row.translations,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n })\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Invalid parameters', details: err.issues }, { status: 400 })\n }\n logger.error('Failed to load translations', { err })\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n\nexport async function PUT(req: Request, ctx: { params?: { entityType?: string; entityId?: string } }) {\n try {\n const context = await resolveTranslationsRouteContext(req)\n await requireTranslationFeatures(context, ['translations.manage'])\n const { entityType, entityId } = paramsSchema.parse({\n entityType: ctx.params?.entityType,\n entityId: ctx.params?.entityId,\n })\n\n const rawText = await req.text()\n let rawBody: unknown = {}\n if (rawText.trim().length > 0) {\n try {\n rawBody = JSON.parse(rawText)\n } catch {\n return NextResponse.json({ error: 'Invalid JSON body' }, { status: 400 })\n }\n }\n const translations = translationBodySchema.parse(rawBody)\n\n const guardUserId = resolveTranslationsActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'translations.translation',\n resourceId: `${entityType}:${entityId}`,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: { entityType, entityId, translations },\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n // Optimistic lock: refuse a stale standalone PUT so a translation save that\n // started from an out-of-date view cannot silently clobber a concurrent edit.\n // Strictly additive \u2014 no expected-version header \u2192 no-op. Skipped when no row\n // exists yet (first save has no prior version to conflict with).\n const existingVersion = await loadTranslationRowVersion(\n context.db as any,\n entityType,\n entityId,\n context.tenantId,\n context.organizationId,\n )\n if (existingVersion) {\n await enforceCommandOptimisticLockWithGuards(context.container, {\n resourceKind: TRANSLATION_RESOURCE_KIND,\n resourceId: existingVersion.id,\n current: existingVersion.updatedAt,\n request: req,\n })\n }\n\n const commandBus = context.container.resolve('commandBus') as CommandBus\n const { result, logEntry } = await commandBus.execute<\n { entityType: string; entityId: string; translations: typeof translations; organizationId: string | null; tenantId: string },\n { rowId: string }\n >('translations.translation.save', {\n input: {\n entityType,\n entityId,\n translations,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n },\n ctx: context.commandCtx,\n })\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'translations.translation',\n resourceId: `${entityType}:${entityId}`,\n operation: 'update',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const row = await (context.db as any)\n .selectFrom('entity_translations')\n .selectAll()\n .where('id', '=', result.rowId)\n .executeTakeFirst() as Record<string, any>\n\n const response = NextResponse.json({\n entityType: row.entity_type,\n entityId: row.entity_id,\n translations: row.translations,\n createdAt: row.created_at,\n updatedAt: row.updated_at,\n })\n\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'translations.translation',\n resourceId: logEntry.resourceId ?? result.rowId,\n executedAt: logEntry.createdAt instanceof Date\n ? logEntry.createdAt.toISOString()\n : typeof logEntry.createdAt === 'string'\n ? logEntry.createdAt\n : new Date().toISOString(),\n }),\n )\n }\n\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Validation failed', details: err.issues }, { status: 400 })\n }\n logger.error('Failed to save translations', { err })\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n\nexport async function DELETE(req: Request, ctx: { params?: { entityType?: string; entityId?: string } }) {\n try {\n const context = await resolveTranslationsRouteContext(req)\n await requireTranslationFeatures(context, ['translations.manage'])\n const { entityType, entityId } = paramsSchema.parse({\n entityType: ctx.params?.entityType,\n entityId: ctx.params?.entityId,\n })\n\n const guardUserId = resolveTranslationsActorId(context.auth)\n const guardResult = await validateCrudMutationGuard(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'translations.translation',\n resourceId: `${entityType}:${entityId}`,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n mutationPayload: null,\n })\n if (guardResult && !guardResult.ok) {\n return NextResponse.json(guardResult.body, { status: guardResult.status })\n }\n\n // Optimistic lock: refuse a stale standalone DELETE (same hole as PUT).\n // Additive \u2014 no header \u2192 no-op; skipped when no row exists.\n const existingVersion = await loadTranslationRowVersion(\n context.db as any,\n entityType,\n entityId,\n context.tenantId,\n context.organizationId,\n )\n if (existingVersion) {\n await enforceCommandOptimisticLockWithGuards(context.container, {\n resourceKind: TRANSLATION_RESOURCE_KIND,\n resourceId: existingVersion.id,\n current: existingVersion.updatedAt,\n request: req,\n })\n }\n\n const commandBus = context.container.resolve('commandBus') as CommandBus\n const { logEntry } = await commandBus.execute<\n { entityType: string; entityId: string; organizationId: string | null; tenantId: string },\n { deleted: boolean }\n >('translations.translation.delete', {\n input: {\n entityType,\n entityId,\n organizationId: context.organizationId,\n tenantId: context.tenantId,\n },\n ctx: context.commandCtx,\n })\n\n if (guardResult?.ok && guardResult.shouldRunAfterSuccess) {\n await runCrudMutationGuardAfterSuccess(context.container, {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userId: guardUserId,\n resourceKind: 'translations.translation',\n resourceId: `${entityType}:${entityId}`,\n operation: 'delete',\n requestMethod: req.method,\n requestHeaders: req.headers,\n metadata: guardResult.metadata ?? null,\n })\n }\n\n const response = new NextResponse(null, { status: 204 })\n\n if (logEntry?.undoToken && logEntry?.id && logEntry?.commandId) {\n response.headers.set(\n 'x-om-operation',\n serializeOperationMetadata({\n id: logEntry.id,\n undoToken: logEntry.undoToken,\n commandId: logEntry.commandId,\n actionLabel: logEntry.actionLabel ?? null,\n resourceKind: logEntry.resourceKind ?? 'translations.translation',\n resourceId: logEntry.resourceId ?? null,\n executedAt: logEntry.createdAt instanceof Date\n ? logEntry.createdAt.toISOString()\n : typeof logEntry.createdAt === 'string'\n ? logEntry.createdAt\n : new Date().toISOString(),\n }),\n )\n }\n\n return response\n } catch (err) {\n if (isCrudHttpError(err)) {\n return NextResponse.json(err.body, { status: err.status })\n }\n const interceptorRejection = getCommandInterceptorHttpRejection(err)\n if (interceptorRejection) {\n return NextResponse.json(interceptorRejection.body, { status: interceptorRejection.status })\n }\n if (err instanceof z.ZodError) {\n return NextResponse.json({ error: 'Invalid parameters', details: err.issues }, { status: 400 })\n }\n logger.error('Failed to delete translations', { err })\n return NextResponse.json({ error: 'Internal server error' }, { status: 500 })\n }\n}\n\nconst translationsTag = 'Translations'\n\nconst getDoc: OpenApiMethodDoc = {\n summary: 'Get entity translations',\n description: 'Returns the full translation record for a single entity.',\n tags: [translationsTag],\n responses: [\n { status: 200, description: 'Translation record found.' },\n ],\n errors: [\n { status: 401, description: 'Authentication required' },\n { status: 404, description: 'No translations found for this entity' },\n ],\n}\n\nconst putDoc: OpenApiMethodDoc = {\n summary: 'Create or update entity translations',\n description: 'Full replacement of translations JSONB for an entity.',\n tags: [translationsTag],\n responses: [\n { status: 200, description: 'Translations saved.' },\n ],\n errors: [\n { status: 400, description: 'Validation failed' },\n { status: 401, description: 'Authentication required' },\n ],\n}\n\nconst deleteDoc: OpenApiMethodDoc = {\n summary: 'Delete entity translations',\n description: 'Removes all translations for an entity.',\n tags: [translationsTag],\n responses: [\n { status: 204, description: 'Translations deleted.' },\n ],\n errors: [\n { status: 401, description: 'Authentication required' },\n ],\n}\n\nexport const openApi: OpenApiRouteDoc = {\n tag: translationsTag,\n summary: 'Entity translation resource',\n pathParams: paramsSchema,\n methods: {\n GET: getDoc,\n PUT: putDoc,\n DELETE: deleteDoc,\n },\n}\n"],
5
+ "mappings": "AAAA,SAAS,oBAAoB;AAC7B,SAAS,SAAS;AAClB,SAAS,WAAW;AACpB,SAAS,iCAAiC,4BAA4B,kCAAkC;AACxG,SAAS,uBAAuB,uBAAuB,2BAA2B;AAClF,SAAwB,uBAAuB;AAC/C;AAAA,EACE;AAAA,EACA;AAAA,OACK;AAEP,SAAS,kCAAkC;AAC3C,SAAS,8CAA8C;AAEvD,SAAS,oBAAoB;AAC7B,SAAS,0CAA0C;AAEnD,MAAM,SAAS,aAAa,cAAc,EAAE,MAAM,EAAE,WAAW,sBAAsB,CAAC;AAEtF,MAAM,4BAA4B;AAgBlC,eAAe,0BACb,IACA,YACA,UACA,UACA,gBACiE;AACjE,QAAM,MAAM,MAAM,GACf,WAAW,qBAAqB,EAChC,OAAO,CAAC,MAAM,YAAY,CAAC,EAC3B,MAAM,eAAe,KAAK,UAAU,EACpC,MAAM,aAAa,KAAK,QAAQ,EAChC,MAAM,qCAA8C,QAAQ,EAAE,EAC9D,MAAM,2CAAoD,cAAc,EAAE,EAC1E,iBAAiB;AACpB,MAAI,CAAC,IAAK,QAAO;AACjB,SAAO,EAAE,IAAI,IAAI,IAAI,WAAW,IAAI,cAAc,KAAK;AACzD;AAEA,MAAM,eAAe,EAAE,OAAO;AAAA,EAC5B,YAAY;AAAA,EACZ,UAAU;AACZ,CAAC;AAEM,MAAM,WAAW;AAAA,EACtB,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,mBAAmB,EAAE;AAAA,EACjE,KAAK,EAAE,aAAa,MAAM,iBAAiB,CAAC,qBAAqB,EAAE;AAAA,EACnE,QAAQ,EAAE,aAAa,MAAM,iBAAiB,CAAC,qBAAqB,EAAE;AACxE;AAEA,eAAsB,IAAI,KAAc,KAA8D;AACpG,MAAI;AACF,UAAM,UAAU,MAAM,gCAAgC,GAAG;AACzD,UAAM,2BAA2B,SAAS,CAAC,mBAAmB,CAAC;AAC/D,UAAM,EAAE,YAAY,SAAS,IAAI,aAAa,MAAM;AAAA,MAClD,YAAY,IAAI,QAAQ;AAAA,MACxB,UAAU,IAAI,QAAQ;AAAA,IACxB,CAAC;AAED,UAAM,MAAM,MAAO,QAAQ,GACxB,WAAW,qBAAqB,EAChC,UAAU,EACV,MAAM,eAAe,KAAK,UAAU,EACpC,MAAM,aAAa,KAAK,QAAQ,EAChC,MAAM,qCAA8C,QAAQ,QAAQ,EAAE,EACtE,MAAM,2CAAoD,QAAQ,cAAc,EAAE,EAClF,iBAAiB;AAEpB,QAAI,CAAC,KAAK;AACR,aAAO,aAAa,KAAK,EAAE,OAAO,YAAY,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAClE;AAEA,WAAO,aAAa,KAAK;AAAA,MACvB,YAAY,IAAI;AAAA,MAChB,UAAU,IAAI;AAAA,MACd,cAAc,IAAI;AAAA,MAClB,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,IACjB,CAAC;AAAA,EACH,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,QAAI,eAAe,EAAE,UAAU;AAC7B,aAAO,aAAa,KAAK,EAAE,OAAO,sBAAsB,SAAS,IAAI,OAAO,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAChG;AACA,WAAO,MAAM,+BAA+B,EAAE,IAAI,CAAC;AACnD,WAAO,aAAa,KAAK,EAAE,OAAO,wBAAwB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC9E;AACF;AAEA,eAAsB,IAAI,KAAc,KAA8D;AACpG,MAAI;AACF,UAAM,UAAU,MAAM,gCAAgC,GAAG;AACzD,UAAM,2BAA2B,SAAS,CAAC,qBAAqB,CAAC;AACjE,UAAM,EAAE,YAAY,SAAS,IAAI,aAAa,MAAM;AAAA,MAClD,YAAY,IAAI,QAAQ;AAAA,MACxB,UAAU,IAAI,QAAQ;AAAA,IACxB,CAAC;AAED,UAAM,UAAU,MAAM,IAAI,KAAK;AAC/B,QAAI,UAAmB,CAAC;AACxB,QAAI,QAAQ,KAAK,EAAE,SAAS,GAAG;AAC7B,UAAI;AACF,kBAAU,KAAK,MAAM,OAAO;AAAA,MAC9B,QAAQ;AACN,eAAO,aAAa,KAAK,EAAE,OAAO,oBAAoB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,MAC1E;AAAA,IACF;AACA,UAAM,eAAe,sBAAsB,MAAM,OAAO;AAExD,UAAM,cAAc,2BAA2B,QAAQ,IAAI;AAC3D,UAAM,cAAc,MAAM,0BAA0B,QAAQ,WAAW;AAAA,MACrE,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY,GAAG,UAAU,IAAI,QAAQ;AAAA,MACrC,WAAW;AAAA,MACX,eAAe,IAAI;AAAA,MACnB,gBAAgB,IAAI;AAAA,MACpB,iBAAiB,EAAE,YAAY,UAAU,aAAa;AAAA,IACxD,CAAC;AACD,QAAI,eAAe,CAAC,YAAY,IAAI;AAClC,aAAO,aAAa,KAAK,YAAY,MAAM,EAAE,QAAQ,YAAY,OAAO,CAAC;AAAA,IAC3E;AAMA,UAAM,kBAAkB,MAAM;AAAA,MAC5B,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AACA,QAAI,iBAAiB;AACnB,YAAM,uCAAuC,QAAQ,WAAW;AAAA,QAC9D,cAAc;AAAA,QACd,YAAY,gBAAgB;AAAA,QAC5B,SAAS,gBAAgB;AAAA,QACzB,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAEA,UAAM,aAAa,QAAQ,UAAU,QAAQ,YAAY;AACzD,UAAM,EAAE,QAAQ,SAAS,IAAI,MAAM,WAAW,QAG5C,iCAAiC;AAAA,MACjC,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA,gBAAgB,QAAQ;AAAA,QACxB,UAAU,QAAQ;AAAA,MACpB;AAAA,MACA,KAAK,QAAQ;AAAA,IACf,CAAC;AAED,QAAI,aAAa,MAAM,YAAY,uBAAuB;AACxD,YAAM,iCAAiC,QAAQ,WAAW;AAAA,QACxD,UAAU,QAAQ;AAAA,QAClB,gBAAgB,QAAQ;AAAA,QACxB,QAAQ;AAAA,QACR,cAAc;AAAA,QACd,YAAY,GAAG,UAAU,IAAI,QAAQ;AAAA,QACrC,WAAW;AAAA,QACX,eAAe,IAAI;AAAA,QACnB,gBAAgB,IAAI;AAAA,QACpB,UAAU,YAAY,YAAY;AAAA,MACpC,CAAC;AAAA,IACH;AAEA,UAAM,MAAM,MAAO,QAAQ,GACxB,WAAW,qBAAqB,EAChC,UAAU,EACV,MAAM,MAAM,KAAK,OAAO,KAAK,EAC7B,iBAAiB;AAEpB,UAAM,WAAW,aAAa,KAAK;AAAA,MACjC,YAAY,IAAI;AAAA,MAChB,UAAU,IAAI;AAAA,MACd,cAAc,IAAI;AAAA,MAClB,WAAW,IAAI;AAAA,MACf,WAAW,IAAI;AAAA,IACjB,CAAC;AAED,QAAI,UAAU,aAAa,UAAU,MAAM,UAAU,WAAW;AAC9D,eAAS,QAAQ;AAAA,QACf;AAAA,QACA,2BAA2B;AAAA,UACzB,IAAI,SAAS;AAAA,UACb,WAAW,SAAS;AAAA,UACpB,WAAW,SAAS;AAAA,UACpB,aAAa,SAAS,eAAe;AAAA,UACrC,cAAc,SAAS,gBAAgB;AAAA,UACvC,YAAY,SAAS,cAAc,OAAO;AAAA,UAC1C,YAAY,SAAS,qBAAqB,OACtC,SAAS,UAAU,YAAY,IAC/B,OAAO,SAAS,cAAc,WAC5B,SAAS,aACT,oBAAI,KAAK,GAAE,YAAY;AAAA,QAC/B,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,UAAM,uBAAuB,mCAAmC,GAAG;AACnE,QAAI,sBAAsB;AACxB,aAAO,aAAa,KAAK,qBAAqB,MAAM,EAAE,QAAQ,qBAAqB,OAAO,CAAC;AAAA,IAC7F;AACA,QAAI,eAAe,EAAE,UAAU;AAC7B,aAAO,aAAa,KAAK,EAAE,OAAO,qBAAqB,SAAS,IAAI,OAAO,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAC/F;AACA,WAAO,MAAM,+BAA+B,EAAE,IAAI,CAAC;AACnD,WAAO,aAAa,KAAK,EAAE,OAAO,wBAAwB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC9E;AACF;AAEA,eAAsB,OAAO,KAAc,KAA8D;AACvG,MAAI;AACF,UAAM,UAAU,MAAM,gCAAgC,GAAG;AACzD,UAAM,2BAA2B,SAAS,CAAC,qBAAqB,CAAC;AACjE,UAAM,EAAE,YAAY,SAAS,IAAI,aAAa,MAAM;AAAA,MAClD,YAAY,IAAI,QAAQ;AAAA,MACxB,UAAU,IAAI,QAAQ;AAAA,IACxB,CAAC;AAED,UAAM,cAAc,2BAA2B,QAAQ,IAAI;AAC3D,UAAM,cAAc,MAAM,0BAA0B,QAAQ,WAAW;AAAA,MACrE,UAAU,QAAQ;AAAA,MAClB,gBAAgB,QAAQ;AAAA,MACxB,QAAQ;AAAA,MACR,cAAc;AAAA,MACd,YAAY,GAAG,UAAU,IAAI,QAAQ;AAAA,MACrC,WAAW;AAAA,MACX,eAAe,IAAI;AAAA,MACnB,gBAAgB,IAAI;AAAA,MACpB,iBAAiB;AAAA,IACnB,CAAC;AACD,QAAI,eAAe,CAAC,YAAY,IAAI;AAClC,aAAO,aAAa,KAAK,YAAY,MAAM,EAAE,QAAQ,YAAY,OAAO,CAAC;AAAA,IAC3E;AAIA,UAAM,kBAAkB,MAAM;AAAA,MAC5B,QAAQ;AAAA,MACR;AAAA,MACA;AAAA,MACA,QAAQ;AAAA,MACR,QAAQ;AAAA,IACV;AACA,QAAI,iBAAiB;AACnB,YAAM,uCAAuC,QAAQ,WAAW;AAAA,QAC9D,cAAc;AAAA,QACd,YAAY,gBAAgB;AAAA,QAC5B,SAAS,gBAAgB;AAAA,QACzB,SAAS;AAAA,MACX,CAAC;AAAA,IACH;AAEA,UAAM,aAAa,QAAQ,UAAU,QAAQ,YAAY;AACzD,UAAM,EAAE,SAAS,IAAI,MAAM,WAAW,QAGpC,mCAAmC;AAAA,MACnC,OAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA,gBAAgB,QAAQ;AAAA,QACxB,UAAU,QAAQ;AAAA,MACpB;AAAA,MACA,KAAK,QAAQ;AAAA,IACf,CAAC;AAED,QAAI,aAAa,MAAM,YAAY,uBAAuB;AACxD,YAAM,iCAAiC,QAAQ,WAAW;AAAA,QACxD,UAAU,QAAQ;AAAA,QAClB,gBAAgB,QAAQ;AAAA,QACxB,QAAQ;AAAA,QACR,cAAc;AAAA,QACd,YAAY,GAAG,UAAU,IAAI,QAAQ;AAAA,QACrC,WAAW;AAAA,QACX,eAAe,IAAI;AAAA,QACnB,gBAAgB,IAAI;AAAA,QACpB,UAAU,YAAY,YAAY;AAAA,MACpC,CAAC;AAAA,IACH;AAEA,UAAM,WAAW,IAAI,aAAa,MAAM,EAAE,QAAQ,IAAI,CAAC;AAEvD,QAAI,UAAU,aAAa,UAAU,MAAM,UAAU,WAAW;AAC9D,eAAS,QAAQ;AAAA,QACf;AAAA,QACA,2BAA2B;AAAA,UACzB,IAAI,SAAS;AAAA,UACb,WAAW,SAAS;AAAA,UACpB,WAAW,SAAS;AAAA,UACpB,aAAa,SAAS,eAAe;AAAA,UACrC,cAAc,SAAS,gBAAgB;AAAA,UACvC,YAAY,SAAS,cAAc;AAAA,UACnC,YAAY,SAAS,qBAAqB,OACtC,SAAS,UAAU,YAAY,IAC/B,OAAO,SAAS,cAAc,WAC5B,SAAS,aACT,oBAAI,KAAK,GAAE,YAAY;AAAA,QAC/B,CAAC;AAAA,MACH;AAAA,IACF;AAEA,WAAO;AAAA,EACT,SAAS,KAAK;AACZ,QAAI,gBAAgB,GAAG,GAAG;AACxB,aAAO,aAAa,KAAK,IAAI,MAAM,EAAE,QAAQ,IAAI,OAAO,CAAC;AAAA,IAC3D;AACA,UAAM,uBAAuB,mCAAmC,GAAG;AACnE,QAAI,sBAAsB;AACxB,aAAO,aAAa,KAAK,qBAAqB,MAAM,EAAE,QAAQ,qBAAqB,OAAO,CAAC;AAAA,IAC7F;AACA,QAAI,eAAe,EAAE,UAAU;AAC7B,aAAO,aAAa,KAAK,EAAE,OAAO,sBAAsB,SAAS,IAAI,OAAO,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,IAChG;AACA,WAAO,MAAM,iCAAiC,EAAE,IAAI,CAAC;AACrD,WAAO,aAAa,KAAK,EAAE,OAAO,wBAAwB,GAAG,EAAE,QAAQ,IAAI,CAAC;AAAA,EAC9E;AACF;AAEA,MAAM,kBAAkB;AAExB,MAAM,SAA2B;AAAA,EAC/B,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,eAAe;AAAA,EACtB,WAAW;AAAA,IACT,EAAE,QAAQ,KAAK,aAAa,4BAA4B;AAAA,EAC1D;AAAA,EACA,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,0BAA0B;AAAA,IACtD,EAAE,QAAQ,KAAK,aAAa,wCAAwC;AAAA,EACtE;AACF;AAEA,MAAM,SAA2B;AAAA,EAC/B,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,eAAe;AAAA,EACtB,WAAW;AAAA,IACT,EAAE,QAAQ,KAAK,aAAa,sBAAsB;AAAA,EACpD;AAAA,EACA,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,oBAAoB;AAAA,IAChD,EAAE,QAAQ,KAAK,aAAa,0BAA0B;AAAA,EACxD;AACF;AAEA,MAAM,YAA8B;AAAA,EAClC,SAAS;AAAA,EACT,aAAa;AAAA,EACb,MAAM,CAAC,eAAe;AAAA,EACtB,WAAW;AAAA,IACT,EAAE,QAAQ,KAAK,aAAa,wBAAwB;AAAA,EACtD;AAAA,EACA,QAAQ;AAAA,IACN,EAAE,QAAQ,KAAK,aAAa,0BAA0B;AAAA,EACxD;AACF;AAEO,MAAM,UAA2B;AAAA,EACtC,KAAK;AAAA,EACL,SAAS;AAAA,EACT,YAAY;AAAA,EACZ,SAAS;AAAA,IACP,KAAK;AAAA,IACL,KAAK;AAAA,IACL,QAAQ;AAAA,EACV;AACF;",
6
6
  "names": []
7
7
  }