@open-mercato/core 0.9.1-develop.7518.1.343d9198f1 → 0.9.1-develop.7532.1.8eac7e198f

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 (88) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/auth/api/logout.js +6 -1
  3. package/dist/modules/auth/api/logout.js.map +2 -2
  4. package/dist/modules/auth/api/session/refresh.js +6 -1
  5. package/dist/modules/auth/api/session/refresh.js.map +2 -2
  6. package/dist/modules/catalog/ai-tools/mutation-pack.js +10 -5
  7. package/dist/modules/catalog/ai-tools/mutation-pack.js.map +2 -2
  8. package/dist/modules/catalog/ai-tools/types.js.map +2 -2
  9. package/dist/modules/customer_accounts/api/login.js +14 -7
  10. package/dist/modules/customer_accounts/api/login.js.map +2 -2
  11. package/dist/modules/customer_accounts/services/customerUserService.js +10 -10
  12. package/dist/modules/customer_accounts/services/customerUserService.js.map +2 -2
  13. package/dist/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/route.js +1 -1
  14. package/dist/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/route.js.map +1 -1
  15. package/dist/modules/forms/api/form-submissions/[id]/submit/route.js +12 -2
  16. package/dist/modules/forms/api/form-submissions/[id]/submit/route.js.map +2 -2
  17. package/dist/modules/forms/api/form-submissions/route.js +16 -1
  18. package/dist/modules/forms/api/form-submissions/route.js.map +2 -2
  19. package/dist/modules/forms/api/public/submissions/[id]/submit/route.js +6 -1
  20. package/dist/modules/forms/api/public/submissions/[id]/submit/route.js.map +2 -2
  21. package/dist/modules/forms/api/runtime-helpers.js +16 -0
  22. package/dist/modules/forms/api/runtime-helpers.js.map +2 -2
  23. package/dist/modules/forms/data/validators.js.map +2 -2
  24. package/dist/modules/forms/lib/portal-subject-policy.js +17 -0
  25. package/dist/modules/forms/lib/portal-subject-policy.js.map +7 -0
  26. package/dist/modules/forms/lib/runtime-principal.js +27 -10
  27. package/dist/modules/forms/lib/runtime-principal.js.map +2 -2
  28. package/dist/modules/forms/services/distribution-service.js +35 -13
  29. package/dist/modules/forms/services/distribution-service.js.map +2 -2
  30. package/dist/modules/forms/services/submission-service.js +4 -0
  31. package/dist/modules/forms/services/submission-service.js.map +2 -2
  32. package/dist/modules/staff/commands/leave-requests.js +54 -24
  33. package/dist/modules/staff/commands/leave-requests.js.map +2 -2
  34. package/dist/modules/staff/commands/timesheets-entries.js +4 -0
  35. package/dist/modules/staff/commands/timesheets-entries.js.map +2 -2
  36. package/dist/modules/staff/commands/timesheets-tags.js +27 -8
  37. package/dist/modules/staff/commands/timesheets-tags.js.map +2 -2
  38. package/dist/modules/warranty_claims/api/portal/attachments/route.js +19 -7
  39. package/dist/modules/warranty_claims/api/portal/attachments/route.js.map +2 -2
  40. package/dist/modules/warranty_claims/commands/claims.js +1 -1
  41. package/dist/modules/warranty_claims/commands/claims.js.map +2 -2
  42. package/dist/modules/warranty_claims/subscribers/return-shipment-tracking.js +6 -6
  43. package/dist/modules/warranty_claims/subscribers/return-shipment-tracking.js.map +2 -2
  44. package/dist/modules/wms/commands/inventory-actions.js +19 -3
  45. package/dist/modules/wms/commands/inventory-actions.js.map +2 -2
  46. package/dist/modules/workflows/lib/async-activity-wait.js +43 -0
  47. package/dist/modules/workflows/lib/async-activity-wait.js.map +7 -0
  48. package/dist/modules/workflows/lib/parallel-handler.js +20 -14
  49. package/dist/modules/workflows/lib/parallel-handler.js.map +2 -2
  50. package/dist/modules/workflows/lib/workflow-executor.js +21 -23
  51. package/dist/modules/workflows/lib/workflow-executor.js.map +2 -2
  52. package/package.json +7 -7
  53. package/src/modules/auth/api/logout.ts +6 -1
  54. package/src/modules/auth/api/session/refresh.ts +6 -1
  55. package/src/modules/catalog/ai-tools/mutation-pack.ts +14 -5
  56. package/src/modules/catalog/ai-tools/types.ts +1 -0
  57. package/src/modules/customer_accounts/api/login.ts +15 -7
  58. package/src/modules/customer_accounts/services/customerUserService.ts +34 -10
  59. package/src/modules/forms/api/distributions/[distributionId]/invitations/[invitationId]/route.ts +1 -1
  60. package/src/modules/forms/api/form-submissions/[id]/submit/route.ts +16 -3
  61. package/src/modules/forms/api/form-submissions/route.ts +21 -2
  62. package/src/modules/forms/api/public/submissions/[id]/submit/route.ts +7 -0
  63. package/src/modules/forms/api/runtime-helpers.ts +16 -0
  64. package/src/modules/forms/data/validators.ts +2 -1
  65. package/src/modules/forms/i18n/de.json +1 -0
  66. package/src/modules/forms/i18n/en.json +1 -0
  67. package/src/modules/forms/i18n/es.json +1 -0
  68. package/src/modules/forms/i18n/ko.json +1 -0
  69. package/src/modules/forms/i18n/pl.json +1 -0
  70. package/src/modules/forms/lib/portal-subject-policy.ts +42 -0
  71. package/src/modules/forms/lib/runtime-principal.ts +35 -11
  72. package/src/modules/forms/services/distribution-service.ts +35 -13
  73. package/src/modules/forms/services/submission-service.ts +12 -0
  74. package/src/modules/staff/commands/leave-requests.ts +54 -23
  75. package/src/modules/staff/commands/timesheets-entries.ts +4 -0
  76. package/src/modules/staff/commands/timesheets-tags.ts +37 -8
  77. package/src/modules/staff/i18n/de.json +2 -0
  78. package/src/modules/staff/i18n/en.json +2 -0
  79. package/src/modules/staff/i18n/es.json +2 -0
  80. package/src/modules/staff/i18n/ko.json +2 -0
  81. package/src/modules/staff/i18n/pl.json +2 -0
  82. package/src/modules/warranty_claims/api/portal/attachments/route.ts +20 -8
  83. package/src/modules/warranty_claims/commands/claims.ts +1 -1
  84. package/src/modules/warranty_claims/subscribers/return-shipment-tracking.ts +5 -5
  85. package/src/modules/wms/commands/inventory-actions.ts +30 -10
  86. package/src/modules/workflows/lib/async-activity-wait.ts +71 -0
  87. package/src/modules/workflows/lib/parallel-handler.ts +24 -15
  88. package/src/modules/workflows/lib/workflow-executor.ts +24 -27
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/staff/commands/timesheets-tags.ts"],
4
- "sourcesContent": ["/**\n * Tag commands for the consulting suite (T3.4).\n *\n * A tag is org-wide vocabulary \u2014 screen 6 renders it as a card badge, screen 7 as\n * a chip in the task drawer, screens 8 and 10 on the time entry. The tag itself is\n * therefore project-agnostic; only its *assignments* touch project-scoped rows,\n * which is why the access filtering lives on the assignment routes rather than\n * here.\n *\n * Three decisions the callers depend on:\n *\n * 1. `slug` is unique per organization+tenant (partial unique index, live rows\n * only). A collision comes back as a 409 carrying `fieldErrors.slug` so the\n * form marks the field instead of showing a dead 500.\n * 2. **Deleting a tag cascades to its assignments and reports the count.** The\n * alternatives are worse: silently orphaning 200 entry badges hides the blast\n * radius, and refusing until the caller hand-unpicks 200 rows gives them no\n * way forward. The tag is soft-deleted, the junction rows are removed, the\n * response carries `removedTaskAssignments` / `removedEntryAssignments`, and\n * undo puts both the tag and every removed assignment back.\n * 3. **Assignment is idempotent.** Both junctions carry a unique index, so\n * re-sending a tag the target already has is a no-op success, never a unique\n * violation. The already-present ids come back in `alreadyAssignedTagIds` so\n * the UI can tell \"added\" from \"was already there\". Unassigning a tag that is\n * not assigned is the same kind of no-op.\n */\n\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isUniqueViolation } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport {\n StaffTimeEntry,\n StaffTimeEntryTag,\n StaffTimeTag,\n StaffTimeTask,\n StaffTimeTaskTag,\n} from '../data/entities'\nimport {\n staffTimeTagCreateSchema,\n staffTimeEntryTagAssignmentSchema,\n staffTimeTaskTagAssignmentSchema,\n type StaffTimeTagCreateInput,\n type StaffTimeEntryTagAssignmentInput,\n type StaffTimeTaskTagAssignmentInput,\n} from '../data/validators'\nimport {\n applyScopeToWhere,\n commandActorScope,\n commandInputScope,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n scopedStaffSnapshotWhere,\n staffSnapshotScopeFromContext,\n staffSnapshotScopeFromSnapshot,\n type StaffCommandScope,\n type StaffSnapshotScope,\n} from './shared'\n\nexport const staffTimeTagCommandIds = {\n create: 'staff.timesheets.tags.create',\n update: 'staff.timesheets.tags.update',\n delete: 'staff.timesheets.tags.delete',\n assignTask: 'staff.timesheets.tags.assign_task',\n unassignTask: 'staff.timesheets.tags.unassign_task',\n assignEntry: 'staff.timesheets.tags.assign_entry',\n unassignEntry: 'staff.timesheets.tags.unassign_entry',\n} as const\n\nexport const TAG_SLUG_DUPLICATE_CODE = 'time_tag_slug_duplicate'\nexport const TAG_UNKNOWN_IDS_CODE = 'time_tag_unknown_ids'\nexport const TAG_TARGET_LOCKED_CODE = 'time_entry_locked'\n\n/**\n * An assignment snapshot is written into the audit payload so undo can put the\n * junction rows back. A tag used across thousands of rows would bloat the log, so\n * the snapshot stops here and says so; undo then restores what it captured.\n */\nexport const TAG_ASSIGNMENT_SNAPSHOT_LIMIT = 500\n\nconst tagCrudIndexer: CrudIndexerConfig<StaffTimeTag> = {\n entityType: 'staff:staff_time_tag',\n}\n\nconst taskCrudIndexer: CrudIndexerConfig<StaffTimeTask> = {\n entityType: 'staff:staff_time_task',\n}\n\nconst timeEntryCrudIndexer: CrudIndexerConfig<StaffTimeEntry> = {\n entityType: 'staff:staff_time_entry',\n}\n\n// No CRUD event ids are declared for tags in `events.ts`: a tag write is\n// vocabulary maintenance the screens re-read, so these commands queue index and\n// cache side effects only.\n\ntype Translate = (key: string, fallback: string) => string\n\n/**\n * `data/validators.ts` ships the create shape only. The update schema is derived\n * from it rather than restated so the slug/label/color rules cannot drift apart.\n */\nexport const staffTimeTagUpdateSchema = z.object({\n id: z.string().uuid(),\n slug: staffTimeTagCreateSchema.shape.slug.optional(),\n label: staffTimeTagCreateSchema.shape.label.optional(),\n color: staffTimeTagCreateSchema.shape.color,\n})\n\nexport type StaffTimeTagUpdateInput = z.infer<typeof staffTimeTagUpdateSchema>\n\ntype TagSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n slug: string\n label: string\n color: string | null\n deletedAt: string | null\n}\n\ntype TagAssignmentSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n tagId: string\n targetId: string\n}\n\ntype TagUndoPayload = {\n before?: TagSnapshot | null\n after?: TagSnapshot | null\n taskAssignments?: TagAssignmentSnapshot[] | null\n entryAssignments?: TagAssignmentSnapshot[] | null\n assignmentsTruncated?: boolean\n}\n\ntype AssignmentUndoPayload = {\n target: 'task' | 'time_entry'\n targetId: string\n tenantId: string\n organizationId: string\n tagIdsBefore: string[]\n tagIdsAfter: string[]\n added: string[]\n removed: TagAssignmentSnapshot[]\n}\n\nfunction toTagSnapshot(tag: StaffTimeTag): TagSnapshot {\n return {\n id: tag.id,\n tenantId: tag.tenantId,\n organizationId: tag.organizationId,\n slug: tag.slug,\n label: tag.label,\n color: tag.color ?? null,\n deletedAt: tag.deletedAt ? tag.deletedAt.toISOString() : null,\n }\n}\n\nasync function loadTagSnapshot(\n em: EntityManager,\n id: string,\n scope?: StaffSnapshotScope | null,\n): Promise<TagSnapshot | null> {\n const tag = await em.findOne(StaffTimeTag, scopedStaffSnapshotWhere(id, scope))\n return tag ? toTagSnapshot(tag) : null\n}\n\nfunction slugDuplicateError(translate: Translate, slug: string): CrudHttpError {\n const message = translate(\n 'staff.time_tracking.tags.errors.slugDuplicate',\n 'A tag with this slug already exists.',\n )\n return new CrudHttpError(409, {\n code: TAG_SLUG_DUPLICATE_CODE,\n error: message,\n slug,\n fieldErrors: { slug: message },\n })\n}\n\nfunction tagNotFoundError(translate: Translate): CrudHttpError {\n return new CrudHttpError(404, {\n error: translate('staff.time_tracking.tags.errors.notFound', 'Tag not found or not accessible.'),\n })\n}\n\nfunction unknownTagIdsError(translate: Translate, unknownIds: string[]): CrudHttpError {\n const message = translate(\n 'staff.time_tracking.tags.errors.unknownIds',\n 'Some tags do not exist in this organization.',\n )\n return new CrudHttpError(422, {\n code: TAG_UNKNOWN_IDS_CODE,\n error: message,\n unknownIds,\n fieldErrors: { tagIds: message },\n })\n}\n\nfunction targetNotFoundError(translate: Translate, target: 'task' | 'time_entry'): CrudHttpError {\n // Deliberately the same 404 for \"does not exist\" and \"not yours\": tagging must\n // not become a way to probe which task or entry ids exist (screen 17 note 1).\n const message =\n target === 'task'\n ? translate('staff.time_tracking.tags.errors.taskNotFound', 'Task not found or not accessible.')\n : translate('staff.time_tracking.tags.errors.entryNotFound', 'Time entry not found or not accessible.')\n return new CrudHttpError(404, { error: message })\n}\n\n/**\n * Persistence for one junction table. The command bodies below are shared; only\n * these closures know which table and which target column they address, so each\n * ORM call stays fully typed against its own entity.\n */\ntype TagJunctionOps = {\n target: 'task' | 'time_entry'\n resourceKind: string\n loadAssignments: (\n em: EntityManager,\n targetId: string,\n scope: StaffCommandScope,\n ) => Promise<TagAssignmentSnapshot[]>\n loadAssignmentsForSnapshot: (\n em: EntityManager,\n targetId: string,\n scope: StaffSnapshotScope | null,\n ) => Promise<TagAssignmentSnapshot[]>\n loadAssignmentsByTag: (\n em: EntityManager,\n tagId: string,\n scope: StaffCommandScope,\n ) => Promise<TagAssignmentSnapshot[]>\n createAssignment: (em: EntityManager, row: Omit<TagAssignmentSnapshot, 'id'>) => void\n deleteAssignments: (em: EntityManager, ids: string[], scope: StaffCommandScope) => Promise<number>\n deleteAssignmentsByTag: (em: EntityManager, tagId: string, scope: StaffCommandScope) => Promise<number>\n restoreAssignments: (em: EntityManager, rows: TagAssignmentSnapshot[]) => Promise<void>\n}\n\nfunction snapshotScopeWhere(scope?: StaffSnapshotScope | null): { tenantId?: string; organizationId?: string } {\n const where: { tenantId?: string; organizationId?: string } = {}\n if (scope?.tenantId) where.tenantId = scope.tenantId\n if (scope?.organizationId) where.organizationId = scope.organizationId\n return where\n}\n\nconst taskJunctionOps: TagJunctionOps = {\n target: 'task',\n resourceKind: 'staff.timesheets.task_tag',\n loadAssignments: async (em, targetId, scope) => {\n const rows = await em.find(StaffTimeTaskTag, applyScopeToWhere<StaffTimeTaskTag>({ taskId: targetId }, scope))\n return rows.map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n targetId: row.taskId,\n }))\n },\n loadAssignmentsForSnapshot: async (em, targetId, scope) => {\n const rows = await em.find(StaffTimeTaskTag, { taskId: targetId, ...snapshotScopeWhere(scope) })\n return rows.map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n targetId: row.taskId,\n }))\n },\n loadAssignmentsByTag: async (em, tagId, scope) => {\n const rows = await em.find(StaffTimeTaskTag, applyScopeToWhere<StaffTimeTaskTag>({ tagId }, scope), {\n limit: TAG_ASSIGNMENT_SNAPSHOT_LIMIT + 1,\n })\n return rows.map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n targetId: row.taskId,\n }))\n },\n createAssignment: (em, row) => {\n em.persist(\n em.create(StaffTimeTaskTag, {\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n taskId: row.targetId,\n createdAt: new Date(),\n }),\n )\n },\n deleteAssignments: async (em, ids, scope) =>\n em.nativeDelete(StaffTimeTaskTag, applyScopeToWhere<StaffTimeTaskTag>({ id: { $in: ids } }, scope)),\n deleteAssignmentsByTag: async (em, tagId, scope) =>\n em.nativeDelete(StaffTimeTaskTag, applyScopeToWhere<StaffTimeTaskTag>({ tagId }, scope)),\n restoreAssignments: async (em, rows) => {\n for (const row of rows) {\n const existing = await em.findOne(StaffTimeTaskTag, {\n tagId: row.tagId,\n taskId: row.targetId,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n })\n if (existing) continue\n em.persist(\n em.create(StaffTimeTaskTag, {\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n taskId: row.targetId,\n createdAt: new Date(),\n }),\n )\n }\n },\n}\n\nconst entryJunctionOps: TagJunctionOps = {\n target: 'time_entry',\n resourceKind: 'staff.timesheets.entry_tag',\n loadAssignments: async (em, targetId, scope) => {\n const rows = await em.find(\n StaffTimeEntryTag,\n applyScopeToWhere<StaffTimeEntryTag>({ timeEntryId: targetId }, scope),\n )\n return rows.map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n targetId: row.timeEntryId,\n }))\n },\n loadAssignmentsForSnapshot: async (em, targetId, scope) => {\n const rows = await em.find(StaffTimeEntryTag, { timeEntryId: targetId, ...snapshotScopeWhere(scope) })\n return rows.map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n targetId: row.timeEntryId,\n }))\n },\n loadAssignmentsByTag: async (em, tagId, scope) => {\n const rows = await em.find(StaffTimeEntryTag, applyScopeToWhere<StaffTimeEntryTag>({ tagId }, scope), {\n limit: TAG_ASSIGNMENT_SNAPSHOT_LIMIT + 1,\n })\n return rows.map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n targetId: row.timeEntryId,\n }))\n },\n createAssignment: (em, row) => {\n em.persist(\n em.create(StaffTimeEntryTag, {\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n timeEntryId: row.targetId,\n createdAt: new Date(),\n }),\n )\n },\n deleteAssignments: async (em, ids, scope) =>\n em.nativeDelete(StaffTimeEntryTag, applyScopeToWhere<StaffTimeEntryTag>({ id: { $in: ids } }, scope)),\n deleteAssignmentsByTag: async (em, tagId, scope) =>\n em.nativeDelete(StaffTimeEntryTag, applyScopeToWhere<StaffTimeEntryTag>({ tagId }, scope)),\n restoreAssignments: async (em, rows) => {\n for (const row of rows) {\n const existing = await em.findOne(StaffTimeEntryTag, {\n tagId: row.tagId,\n timeEntryId: row.targetId,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n })\n if (existing) continue\n em.persist(\n em.create(StaffTimeEntryTag, {\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n timeEntryId: row.targetId,\n createdAt: new Date(),\n }),\n )\n }\n },\n}\n\nasync function requireKnownTags(\n em: EntityManager,\n tagIds: string[],\n scope: StaffCommandScope,\n translate: Translate,\n): Promise<void> {\n const unique = Array.from(new Set(tagIds))\n if (unique.length === 0) return\n const tags = await em.find(\n StaffTimeTag,\n applyScopeToWhere<StaffTimeTag>({ id: { $in: unique }, deletedAt: null }, scope),\n { fields: ['id'] },\n )\n const known = new Set(tags.map((tag) => tag.id))\n const unknownIds = unique.filter((id) => !known.has(id))\n if (unknownIds.length > 0) throw unknownTagIdsError(translate, unknownIds)\n}\n\nconst createTagCommand: CommandHandler<StaffTimeTagCreateInput, { tagId: string }> = {\n id: staffTimeTagCommandIds.create,\n async prepare(rawInput, ctx) {\n const parsed = staffTimeTagCreateSchema.safeParse(rawInput)\n if (!parsed.success) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const existing = await em.findOne(StaffTimeTag, {\n slug: parsed.data.slug,\n tenantId: parsed.data.tenantId,\n organizationId: parsed.data.organizationId,\n deletedAt: null,\n })\n return { before: { slugTaken: existing != null } }\n },\n async execute(rawInput, ctx) {\n const parsed = staffTimeTagCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n const scope = commandInputScope(ctx, parsed.tenantId, parsed.organizationId)\n const { translate } = await resolveTranslations()\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let created: StaffTimeTag | null = null\n\n try {\n await withAtomicFlush(\n em,\n [\n async () => {\n // The read lives in the same transaction as the insert, so a\n // concurrent create cannot slip a second row past this check; the\n // partial unique index is the backstop and is translated below.\n const duplicate = await em.findOne(\n StaffTimeTag,\n applyScopeToWhere<StaffTimeTag>({ slug: parsed.slug, deletedAt: null }, scope),\n { fields: ['id'] },\n )\n if (duplicate) throw slugDuplicateError(translate, parsed.slug)\n },\n () => {\n created = em.create(StaffTimeTag, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n slug: parsed.slug,\n label: parsed.label,\n color: parsed.color ?? null,\n createdAt: new Date(),\n updatedAt: new Date(),\n deletedAt: null,\n })\n em.persist(created)\n },\n ],\n { transaction: true, label: staffTimeTagCommandIds.create },\n )\n } catch (err) {\n if (isUniqueViolation(err)) throw slugDuplicateError(translate, parsed.slug)\n throw err\n }\n\n const record = created as StaffTimeTag | null\n if (!record) throw tagNotFoundError(translate)\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'created',\n entity: record,\n identifiers: { id: record.id, organizationId: record.organizationId, tenantId: record.tenantId },\n indexer: tagCrudIndexer,\n })\n\n return { tagId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadTagSnapshot(em, result.tagId, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return null\n return { snapshot }\n },\n buildLog: async ({ result, ctx }) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadTagSnapshot(em, result.tagId, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tags.create', 'Create tag'),\n resourceKind: 'staff.timesheets.tag',\n resourceId: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n snapshotAfter: snapshot,\n payload: {\n undo: { after: snapshot } satisfies TagUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TagUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const snapshotScope = staffSnapshotScopeFromSnapshot(after)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tag = await em.findOne(StaffTimeTag, scopedStaffSnapshotWhere(after.id, snapshotScope))\n if (!tag) return\n\n await withAtomicFlush(\n em,\n [\n () => {\n tag.deletedAt = new Date()\n tag.updatedAt = new Date()\n },\n ],\n { transaction: true, label: `${staffTimeTagCommandIds.create}.undo` },\n )\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'deleted',\n entity: tag,\n identifiers: { id: tag.id, organizationId: tag.organizationId, tenantId: tag.tenantId },\n indexer: tagCrudIndexer,\n })\n },\n}\n\nconst updateTagCommand: CommandHandler<StaffTimeTagUpdateInput, { tagId: string }> = {\n id: staffTimeTagCommandIds.update,\n async prepare(rawInput, ctx) {\n const parsed = staffTimeTagUpdateSchema.safeParse(rawInput)\n if (!parsed.success) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadTagSnapshot(em, parsed.data.id, staffSnapshotScopeFromContext(ctx))\n if (!before) return {}\n return { before: { snapshot: before } }\n },\n async execute(rawInput, ctx) {\n const parsed = staffTimeTagUpdateSchema.parse(rawInput)\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n\n const tag = await em.findOne(StaffTimeTag, applyScopeToWhere<StaffTimeTag>({ id: parsed.id, deletedAt: null }, scope))\n if (!tag) throw tagNotFoundError(translate)\n ensureTenantScope(ctx, tag.tenantId)\n ensureOrganizationScope(ctx, tag.organizationId)\n\n try {\n await withAtomicFlush(\n em,\n [\n async () => {\n if (parsed.slug === undefined || parsed.slug === tag.slug) return\n const duplicate = await em.findOne(\n StaffTimeTag,\n applyScopeToWhere<StaffTimeTag>({ slug: parsed.slug, deletedAt: null, id: { $ne: tag.id } }, scope),\n { fields: ['id'] },\n )\n if (duplicate) throw slugDuplicateError(translate, parsed.slug)\n },\n () => {\n if (parsed.slug !== undefined) tag.slug = parsed.slug\n if (parsed.label !== undefined) tag.label = parsed.label\n if (parsed.color !== undefined) tag.color = parsed.color ?? null\n tag.updatedAt = new Date()\n },\n ],\n { transaction: true, label: staffTimeTagCommandIds.update },\n )\n } catch (err) {\n if (isUniqueViolation(err)) throw slugDuplicateError(translate, parsed.slug ?? tag.slug)\n throw err\n }\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'updated',\n entity: tag,\n identifiers: { id: tag.id, organizationId: tag.organizationId, tenantId: tag.tenantId },\n indexer: tagCrudIndexer,\n })\n\n return { tagId: tag.id }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const before = (snapshots.before as { snapshot?: TagSnapshot } | undefined)?.snapshot\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const after = await loadTagSnapshot(em, before.id, staffSnapshotScopeFromSnapshot(before))\n if (!after) return null\n const changes = buildChanges(\n before as unknown as Record<string, unknown>,\n after as unknown as Record<string, unknown>,\n ['slug', 'label', 'color'],\n )\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tags.update', 'Update tag'),\n resourceKind: 'staff.timesheets.tag',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n changes,\n payload: {\n undo: { before, after } satisfies TagUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TagUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const snapshotScope = staffSnapshotScopeFromSnapshot(before)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tag = await em.findOne(StaffTimeTag, scopedStaffSnapshotWhere(before.id, snapshotScope))\n if (!tag) return\n\n await withAtomicFlush(\n em,\n [\n () => {\n tag.slug = before.slug\n tag.label = before.label\n tag.color = before.color ?? null\n tag.updatedAt = new Date()\n },\n ],\n { transaction: true, label: `${staffTimeTagCommandIds.update}.undo` },\n )\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'updated',\n entity: tag,\n identifiers: { id: tag.id, organizationId: tag.organizationId, tenantId: tag.tenantId },\n indexer: tagCrudIndexer,\n })\n },\n}\n\ntype DeleteTagResult = {\n tagId: string\n removedTaskAssignments: number\n removedEntryAssignments: number\n}\n\nconst deleteTagCommand: CommandHandler<{ id?: string }, DeleteTagResult> = {\n id: staffTimeTagCommandIds.delete,\n async prepare(input, ctx) {\n const id = input?.id\n if (!id) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const scope = staffSnapshotScopeFromContext(ctx)\n const before = await loadTagSnapshot(em, id, scope)\n if (!before) return {}\n const readScope: StaffCommandScope = {\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n requireTenant: true,\n requireOrganization: true,\n }\n const taskAssignments = await taskJunctionOps.loadAssignmentsByTag(em, id, readScope)\n const entryAssignments = await entryJunctionOps.loadAssignmentsByTag(em, id, readScope)\n const truncated =\n taskAssignments.length > TAG_ASSIGNMENT_SNAPSHOT_LIMIT || entryAssignments.length > TAG_ASSIGNMENT_SNAPSHOT_LIMIT\n return {\n before: {\n snapshot: before,\n taskAssignments: taskAssignments.slice(0, TAG_ASSIGNMENT_SNAPSHOT_LIMIT),\n entryAssignments: entryAssignments.slice(0, TAG_ASSIGNMENT_SNAPSHOT_LIMIT),\n assignmentsTruncated: truncated,\n },\n }\n },\n async execute(input, ctx) {\n const { translate } = await resolveTranslations()\n const id = input?.id\n if (!id) {\n throw new CrudHttpError(400, {\n error: translate('staff.time_tracking.tags.errors.idRequired', 'Tag id is required.'),\n })\n }\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n const tag = await em.findOne(StaffTimeTag, applyScopeToWhere<StaffTimeTag>({ id, deletedAt: null }, scope))\n if (!tag) throw tagNotFoundError(translate)\n ensureTenantScope(ctx, tag.tenantId)\n ensureOrganizationScope(ctx, tag.organizationId)\n\n let removedTaskAssignments = 0\n let removedEntryAssignments = 0\n\n await withAtomicFlush(\n em,\n [\n async () => {\n // The cascade is deliberate: the badge disappears everywhere it was\n // used, and the counts travel back so the caller sees the reach.\n removedTaskAssignments = await taskJunctionOps.deleteAssignmentsByTag(em, tag.id, scope)\n removedEntryAssignments = await entryJunctionOps.deleteAssignmentsByTag(em, tag.id, scope)\n },\n () => {\n tag.deletedAt = new Date()\n tag.updatedAt = new Date()\n },\n ],\n { transaction: true, label: staffTimeTagCommandIds.delete },\n )\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'deleted',\n entity: tag,\n identifiers: { id: tag.id, organizationId: tag.organizationId, tenantId: tag.tenantId },\n indexer: tagCrudIndexer,\n })\n\n return { tagId: tag.id, removedTaskAssignments, removedEntryAssignments }\n },\n buildLog: async ({ result, snapshots }) => {\n const prepared = snapshots.before as\n | {\n snapshot?: TagSnapshot\n taskAssignments?: TagAssignmentSnapshot[]\n entryAssignments?: TagAssignmentSnapshot[]\n assignmentsTruncated?: boolean\n }\n | undefined\n const before = prepared?.snapshot\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tags.delete', 'Delete tag'),\n resourceKind: 'staff.timesheets.tag',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n changes: {\n removedTaskAssignments: { from: result.removedTaskAssignments, to: 0 },\n removedEntryAssignments: { from: result.removedEntryAssignments, to: 0 },\n },\n payload: {\n undo: {\n before,\n taskAssignments: prepared?.taskAssignments ?? null,\n entryAssignments: prepared?.entryAssignments ?? null,\n assignmentsTruncated: prepared?.assignmentsTruncated ?? false,\n } satisfies TagUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TagUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const snapshotScope = staffSnapshotScopeFromSnapshot(before)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let tag = await em.findOne(StaffTimeTag, scopedStaffSnapshotWhere(before.id, snapshotScope))\n\n await withAtomicFlush(\n em,\n [\n () => {\n if (!tag) {\n tag = em.create(StaffTimeTag, {\n id: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n slug: before.slug,\n label: before.label,\n color: before.color ?? null,\n createdAt: new Date(),\n updatedAt: new Date(),\n deletedAt: null,\n })\n em.persist(tag)\n return\n }\n tag.slug = before.slug\n tag.label = before.label\n tag.color = before.color ?? null\n tag.deletedAt = null\n tag.updatedAt = new Date()\n },\n // Restoring assignments re-reads the junction rows, so it is its own phase.\n () => taskJunctionOps.restoreAssignments(em, payload?.taskAssignments ?? []),\n () => entryJunctionOps.restoreAssignments(em, payload?.entryAssignments ?? []),\n ],\n { transaction: true, label: `${staffTimeTagCommandIds.delete}.undo` },\n )\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'created',\n entity: tag,\n identifiers: { id: before.id, organizationId: before.organizationId, tenantId: before.tenantId },\n indexer: tagCrudIndexer,\n })\n },\n}\n\ntype AssignmentResult = {\n targetId: string\n assignedTagIds: string[]\n alreadyAssignedTagIds: string[]\n tagIds: string[]\n}\n\ntype UnassignmentResult = {\n targetId: string\n removedTagIds: string[]\n notAssignedTagIds: string[]\n tagIds: string[]\n}\n\ntype AssignmentTarget = {\n id: string\n tenantId: string\n organizationId: string\n}\n\nasync function requireTaskTarget(\n em: EntityManager,\n taskId: string,\n scope: StaffCommandScope,\n translate: Translate,\n): Promise<AssignmentTarget> {\n const task = await em.findOne(StaffTimeTask, applyScopeToWhere<StaffTimeTask>({ id: taskId, deletedAt: null }, scope))\n if (!task) throw targetNotFoundError(translate, 'task')\n return { id: task.id, tenantId: task.tenantId, organizationId: task.organizationId }\n}\n\nasync function requireEntryTarget(\n em: EntityManager,\n timeEntryId: string,\n scope: StaffCommandScope,\n translate: Translate,\n): Promise<AssignmentTarget> {\n const entry = await em.findOne(\n StaffTimeEntry,\n applyScopeToWhere<StaffTimeEntry>({ id: timeEntryId, deletedAt: null }, scope),\n )\n if (!entry) throw targetNotFoundError(translate, 'time_entry')\n if (entry.lockedReportId) {\n // An entry frozen in a closed report is read-only, and its tags are part of\n // what the report renders.\n throw new CrudHttpError(409, {\n code: TAG_TARGET_LOCKED_CODE,\n error: translate(\n 'staff.time_tracking.tags.errors.entryLocked',\n 'This time entry is locked in a closed report and its tags cannot be changed.',\n ),\n lockedReportId: entry.lockedReportId,\n })\n }\n return { id: entry.id, tenantId: entry.tenantId, organizationId: entry.organizationId }\n}\n\ntype AssignmentCommandInput = {\n tenantId: string\n organizationId: string\n targetId: string\n tagIds: string[]\n}\n\nfunction refreshTargetSideEffects(ops: TagJunctionOps) {\n return async (ctx: { container: { resolve: (name: string) => unknown } }, target: AssignmentTarget) => {\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n // The target's own index document and cached lists carry its tags, so they\n // are refreshed even though no column on the target row changed.\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: target,\n identifiers: { id: target.id, organizationId: target.organizationId, tenantId: target.tenantId },\n indexer: ops.target === 'task' ? taskCrudIndexer : timeEntryCrudIndexer,\n })\n }\n}\n\nasync function runAssignment(\n ops: TagJunctionOps,\n requireTarget: (\n em: EntityManager,\n targetId: string,\n scope: StaffCommandScope,\n translate: Translate,\n ) => Promise<AssignmentTarget>,\n parsed: AssignmentCommandInput,\n ctx: Parameters<CommandHandler<AssignmentCommandInput, AssignmentResult>['execute']>[1],\n): Promise<AssignmentResult> {\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n const scope = commandInputScope(ctx, parsed.tenantId, parsed.organizationId)\n const { translate } = await resolveTranslations()\n\n const requestedTagIds = Array.from(new Set(parsed.tagIds))\n\n let target: AssignmentTarget | null = null\n let assignedTagIds: string[] = []\n let alreadyAssignedTagIds: string[] = []\n let tagIdsAfter: string[] = []\n\n // Each attempt gets its own fork: a rolled-back unit of work must not carry\n // its persisted-but-uncommitted rows into the retry.\n const attempt = async (): Promise<void> => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await withAtomicFlush(\n em,\n [\n async () => {\n target = await requireTarget(em, parsed.targetId, scope, translate)\n await requireKnownTags(em, requestedTagIds, scope, translate)\n const existing = await ops.loadAssignments(em, parsed.targetId, scope)\n const existingTagIds = new Set(existing.map((row) => row.tagId))\n alreadyAssignedTagIds = requestedTagIds.filter((tagId) => existingTagIds.has(tagId))\n assignedTagIds = requestedTagIds.filter((tagId) => !existingTagIds.has(tagId))\n tagIdsAfter = Array.from(new Set([...existingTagIds, ...requestedTagIds]))\n },\n () => {\n for (const tagId of assignedTagIds) {\n ops.createAssignment(em, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n tagId,\n targetId: parsed.targetId,\n })\n }\n },\n ],\n { transaction: true, label: `${ops.target}.tags.assign` },\n )\n }\n\n try {\n await attempt()\n } catch (err) {\n if (!isUniqueViolation(err)) throw err\n // A concurrent assign won the race. Re-reading turns the collision into the\n // no-op success the second caller asked for; a second violation would mean\n // the row is there either way, which is the requested end state.\n try {\n await attempt()\n } catch (retryErr) {\n if (!isUniqueViolation(retryErr)) throw retryErr\n assignedTagIds = []\n alreadyAssignedTagIds = requestedTagIds\n tagIdsAfter = requestedTagIds\n }\n }\n\n const resolvedTarget = target as AssignmentTarget | null\n if (!resolvedTarget) throw targetNotFoundError(translate, ops.target)\n\n if (assignedTagIds.length > 0) {\n await refreshTargetSideEffects(ops)(ctx, resolvedTarget)\n }\n\n return {\n targetId: parsed.targetId,\n assignedTagIds,\n alreadyAssignedTagIds,\n tagIds: tagIdsAfter,\n }\n}\n\nasync function runUnassignment(\n ops: TagJunctionOps,\n requireTarget: (\n em: EntityManager,\n targetId: string,\n scope: StaffCommandScope,\n translate: Translate,\n ) => Promise<AssignmentTarget>,\n parsed: AssignmentCommandInput,\n ctx: Parameters<CommandHandler<AssignmentCommandInput, UnassignmentResult>['execute']>[1],\n): Promise<UnassignmentResult> {\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n const scope = commandInputScope(ctx, parsed.tenantId, parsed.organizationId)\n const { translate } = await resolveTranslations()\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const requestedTagIds = Array.from(new Set(parsed.tagIds))\n\n let target: AssignmentTarget | null = null\n let removedTagIds: string[] = []\n let notAssignedTagIds: string[] = []\n let tagIdsAfter: string[] = []\n\n await withAtomicFlush(\n em,\n [\n async () => {\n target = await requireTarget(em, parsed.targetId, scope, translate)\n const existing = await ops.loadAssignments(em, parsed.targetId, scope)\n const byTagId = new Map(existing.map((row) => [row.tagId, row]))\n removedTagIds = requestedTagIds.filter((tagId) => byTagId.has(tagId))\n notAssignedTagIds = requestedTagIds.filter((tagId) => !byTagId.has(tagId))\n tagIdsAfter = existing.map((row) => row.tagId).filter((tagId) => !removedTagIds.includes(tagId))\n const removableIds = removedTagIds\n .map((tagId) => byTagId.get(tagId)?.id)\n .filter((value): value is string => typeof value === 'string')\n // Removing a tag that is not assigned is a no-op, not a 404: the caller\n // asked for an end state that already holds.\n if (removableIds.length > 0) await ops.deleteAssignments(em, removableIds, scope)\n },\n ],\n { transaction: true, label: `${ops.target}.tags.unassign` },\n )\n\n const resolvedTarget = target as AssignmentTarget | null\n if (!resolvedTarget) throw targetNotFoundError(translate, ops.target)\n\n if (removedTagIds.length > 0) {\n await refreshTargetSideEffects(ops)(ctx, resolvedTarget)\n }\n\n return {\n targetId: parsed.targetId,\n removedTagIds,\n notAssignedTagIds,\n tagIds: tagIdsAfter,\n }\n}\n\nasync function restoreAssignmentState(\n ops: TagJunctionOps,\n payload: AssignmentUndoPayload,\n ctx: { container: { resolve: (name: string) => unknown } },\n): Promise<void> {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope: StaffCommandScope = {\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n requireTenant: true,\n requireOrganization: true,\n }\n\n await withAtomicFlush(\n em,\n [\n async () => {\n if (payload.added.length > 0) {\n const current = await ops.loadAssignments(em, payload.targetId, scope)\n const removableIds = current\n .filter((row) => payload.added.includes(row.tagId))\n .map((row) => row.id)\n if (removableIds.length > 0) await ops.deleteAssignments(em, removableIds, scope)\n }\n },\n () => ops.restoreAssignments(em, payload.removed),\n ],\n { transaction: true, label: `${ops.target}.tags.undo` },\n )\n}\n\nfunction assignmentLogPayload(\n ops: TagJunctionOps,\n parsed: AssignmentCommandInput,\n result: AssignmentResult,\n): AssignmentUndoPayload {\n return {\n target: ops.target,\n targetId: parsed.targetId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n tagIdsBefore: result.tagIds.filter((tagId) => !result.assignedTagIds.includes(tagId)),\n tagIdsAfter: result.tagIds,\n added: result.assignedTagIds,\n removed: [],\n }\n}\n\nfunction unassignmentLogPayload(\n ops: TagJunctionOps,\n parsed: AssignmentCommandInput,\n result: UnassignmentResult,\n removedRows: TagAssignmentSnapshot[],\n): AssignmentUndoPayload {\n return {\n target: ops.target,\n targetId: parsed.targetId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n tagIdsBefore: [...result.tagIds, ...result.removedTagIds],\n tagIdsAfter: result.tagIds,\n added: [],\n removed: removedRows,\n }\n}\n\nfunction toTaskCommandInput(parsed: StaffTimeTaskTagAssignmentInput): AssignmentCommandInput {\n return {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n targetId: parsed.taskId,\n tagIds: parsed.tagIds,\n }\n}\n\nfunction toEntryCommandInput(parsed: StaffTimeEntryTagAssignmentInput): AssignmentCommandInput {\n return {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n targetId: parsed.timeEntryId,\n tagIds: parsed.tagIds,\n }\n}\n\nconst assignTaskTagsCommand: CommandHandler<StaffTimeTaskTagAssignmentInput, AssignmentResult> = {\n id: staffTimeTagCommandIds.assignTask,\n async prepare(rawInput, ctx) {\n const parsed = staffTimeTaskTagAssignmentSchema.safeParse(rawInput)\n if (!parsed.success) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const rows = await taskJunctionOps.loadAssignmentsForSnapshot(\n em,\n parsed.data.taskId,\n staffSnapshotScopeFromContext(ctx),\n )\n return { before: { tagIds: rows.map((row) => row.tagId) } }\n },\n execute: async (rawInput, ctx) => {\n const parsed = staffTimeTaskTagAssignmentSchema.parse(rawInput)\n return runAssignment(taskJunctionOps, requireTaskTarget, toTaskCommandInput(parsed), ctx)\n },\n buildLog: async ({ input, result, snapshots }) => {\n const parsed = staffTimeTaskTagAssignmentSchema.parse(input)\n const commandInput = toTaskCommandInput(parsed)\n const before = (snapshots.before as { tagIds?: string[] } | undefined)?.tagIds ?? []\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tags.assign_task', 'Assign task tags'),\n resourceKind: taskJunctionOps.resourceKind,\n resourceId: commandInput.targetId,\n tenantId: commandInput.tenantId,\n organizationId: commandInput.organizationId,\n snapshotBefore: { tagIds: before },\n snapshotAfter: { tagIds: result.tagIds },\n changes: { tagIds: { from: before, to: result.tagIds } },\n payload: {\n undo: assignmentLogPayload(taskJunctionOps, commandInput, result),\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AssignmentUndoPayload>(logEntry)\n if (!payload) return\n await restoreAssignmentState(taskJunctionOps, payload, ctx)\n },\n}\n\nconst unassignTaskTagsCommand: CommandHandler<StaffTimeTaskTagAssignmentInput, UnassignmentResult> = {\n id: staffTimeTagCommandIds.unassignTask,\n async prepare(rawInput, ctx) {\n const parsed = staffTimeTaskTagAssignmentSchema.safeParse(rawInput)\n if (!parsed.success) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const rows = await taskJunctionOps.loadAssignmentsForSnapshot(\n em,\n parsed.data.taskId,\n staffSnapshotScopeFromContext(ctx),\n )\n return { before: { rows } }\n },\n execute: async (rawInput, ctx) => {\n const parsed = staffTimeTaskTagAssignmentSchema.parse(rawInput)\n return runUnassignment(taskJunctionOps, requireTaskTarget, toTaskCommandInput(parsed), ctx)\n },\n buildLog: async ({ input, result, snapshots }) => {\n const parsed = staffTimeTaskTagAssignmentSchema.parse(input)\n const commandInput = toTaskCommandInput(parsed)\n const rows = (snapshots.before as { rows?: TagAssignmentSnapshot[] } | undefined)?.rows ?? []\n const removedRows = rows.filter((row) => result.removedTagIds.includes(row.tagId))\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tags.unassign_task', 'Unassign task tags'),\n resourceKind: taskJunctionOps.resourceKind,\n resourceId: commandInput.targetId,\n tenantId: commandInput.tenantId,\n organizationId: commandInput.organizationId,\n snapshotBefore: { tagIds: rows.map((row) => row.tagId) },\n snapshotAfter: { tagIds: result.tagIds },\n changes: { tagIds: { from: rows.map((row) => row.tagId), to: result.tagIds } },\n payload: {\n undo: unassignmentLogPayload(taskJunctionOps, commandInput, result, removedRows),\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AssignmentUndoPayload>(logEntry)\n if (!payload) return\n await restoreAssignmentState(taskJunctionOps, payload, ctx)\n },\n}\n\nconst assignEntryTagsCommand: CommandHandler<StaffTimeEntryTagAssignmentInput, AssignmentResult> = {\n id: staffTimeTagCommandIds.assignEntry,\n async prepare(rawInput, ctx) {\n const parsed = staffTimeEntryTagAssignmentSchema.safeParse(rawInput)\n if (!parsed.success) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const rows = await entryJunctionOps.loadAssignmentsForSnapshot(\n em,\n parsed.data.timeEntryId,\n staffSnapshotScopeFromContext(ctx),\n )\n return { before: { tagIds: rows.map((row) => row.tagId) } }\n },\n execute: async (rawInput, ctx) => {\n const parsed = staffTimeEntryTagAssignmentSchema.parse(rawInput)\n return runAssignment(entryJunctionOps, requireEntryTarget, toEntryCommandInput(parsed), ctx)\n },\n buildLog: async ({ input, result, snapshots }) => {\n const parsed = staffTimeEntryTagAssignmentSchema.parse(input)\n const commandInput = toEntryCommandInput(parsed)\n const before = (snapshots.before as { tagIds?: string[] } | undefined)?.tagIds ?? []\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tags.assign_entry', 'Assign time entry tags'),\n resourceKind: entryJunctionOps.resourceKind,\n resourceId: commandInput.targetId,\n tenantId: commandInput.tenantId,\n organizationId: commandInput.organizationId,\n snapshotBefore: { tagIds: before },\n snapshotAfter: { tagIds: result.tagIds },\n changes: { tagIds: { from: before, to: result.tagIds } },\n payload: {\n undo: assignmentLogPayload(entryJunctionOps, commandInput, result),\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AssignmentUndoPayload>(logEntry)\n if (!payload) return\n await restoreAssignmentState(entryJunctionOps, payload, ctx)\n },\n}\n\nconst unassignEntryTagsCommand: CommandHandler<StaffTimeEntryTagAssignmentInput, UnassignmentResult> = {\n id: staffTimeTagCommandIds.unassignEntry,\n async prepare(rawInput, ctx) {\n const parsed = staffTimeEntryTagAssignmentSchema.safeParse(rawInput)\n if (!parsed.success) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const rows = await entryJunctionOps.loadAssignmentsForSnapshot(\n em,\n parsed.data.timeEntryId,\n staffSnapshotScopeFromContext(ctx),\n )\n return { before: { rows } }\n },\n execute: async (rawInput, ctx) => {\n const parsed = staffTimeEntryTagAssignmentSchema.parse(rawInput)\n return runUnassignment(entryJunctionOps, requireEntryTarget, toEntryCommandInput(parsed), ctx)\n },\n buildLog: async ({ input, result, snapshots }) => {\n const parsed = staffTimeEntryTagAssignmentSchema.parse(input)\n const commandInput = toEntryCommandInput(parsed)\n const rows = (snapshots.before as { rows?: TagAssignmentSnapshot[] } | undefined)?.rows ?? []\n const removedRows = rows.filter((row) => result.removedTagIds.includes(row.tagId))\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tags.unassign_entry', 'Unassign time entry tags'),\n resourceKind: entryJunctionOps.resourceKind,\n resourceId: commandInput.targetId,\n tenantId: commandInput.tenantId,\n organizationId: commandInput.organizationId,\n snapshotBefore: { tagIds: rows.map((row) => row.tagId) },\n snapshotAfter: { tagIds: result.tagIds },\n changes: { tagIds: { from: rows.map((row) => row.tagId), to: result.tagIds } },\n payload: {\n undo: unassignmentLogPayload(entryJunctionOps, commandInput, result, removedRows),\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AssignmentUndoPayload>(logEntry)\n if (!payload) return\n await restoreAssignmentState(entryJunctionOps, payload, ctx)\n },\n}\n\nregisterCommand(createTagCommand)\nregisterCommand(updateTagCommand)\nregisterCommand(deleteTagCommand)\nregisterCommand(assignTaskTagsCommand)\nregisterCommand(unassignTaskTagsCommand)\nregisterCommand(assignEntryTagsCommand)\nregisterCommand(unassignEntryTagsCommand)\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * Tag commands for the consulting suite (T3.4).\n *\n * A tag is org-wide vocabulary \u2014 screen 6 renders it as a card badge, screen 7 as\n * a chip in the task drawer, screens 8 and 10 on the time entry. The tag itself is\n * therefore project-agnostic; only its *assignments* touch project-scoped rows,\n * which is why the access filtering lives on the assignment routes rather than\n * here.\n *\n * Three decisions the callers depend on:\n *\n * 1. `slug` is unique per organization+tenant (partial unique index, live rows\n * only). A collision comes back as a 409 carrying `fieldErrors.slug` so the\n * form marks the field instead of showing a dead 500.\n * 2. **Deleting a tag cascades to its assignments and reports the count.** The\n * alternatives are worse: silently orphaning 200 entry badges hides the blast\n * radius, and refusing until the caller hand-unpicks 200 rows gives them no\n * way forward. The tag is soft-deleted, the junction rows are removed, the\n * response carries `removedTaskAssignments` / `removedEntryAssignments`, and\n * undo puts both the tag and every removed assignment back.\n * 3. **Assignment is idempotent.** Both junctions carry a unique index, so\n * re-sending a tag the target already has is a no-op success, never a unique\n * violation. The already-present ids come back in `alreadyAssignedTagIds` so\n * the UI can tell \"added\" from \"was already there\". Unassigning a tag that is\n * not assigned is the same kind of no-op.\n */\n\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError, isUniqueViolation } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { buildChanges, emitCrudSideEffects, emitCrudUndoSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport type { CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport {\n StaffTimeEntry,\n StaffTimeEntryTag,\n StaffTimeTag,\n StaffTimeTask,\n StaffTimeTaskTag,\n} from '../data/entities'\nimport {\n staffTimeTagCreateSchema,\n staffTimeEntryTagAssignmentSchema,\n staffTimeTaskTagAssignmentSchema,\n type StaffTimeTagCreateInput,\n type StaffTimeEntryTagAssignmentInput,\n type StaffTimeTaskTagAssignmentInput,\n} from '../data/validators'\nimport {\n applyScopeToWhere,\n commandActorScope,\n commandInputScope,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n scopedStaffSnapshotWhere,\n staffSnapshotScopeFromContext,\n staffSnapshotScopeFromSnapshot,\n type StaffCommandScope,\n type StaffSnapshotScope,\n} from './shared'\n\nexport const staffTimeTagCommandIds = {\n create: 'staff.timesheets.tags.create',\n update: 'staff.timesheets.tags.update',\n delete: 'staff.timesheets.tags.delete',\n assignTask: 'staff.timesheets.tags.assign_task',\n unassignTask: 'staff.timesheets.tags.unassign_task',\n assignEntry: 'staff.timesheets.tags.assign_entry',\n unassignEntry: 'staff.timesheets.tags.unassign_entry',\n} as const\n\nexport const TAG_SLUG_DUPLICATE_CODE = 'time_tag_slug_duplicate'\nexport const TAG_UNKNOWN_IDS_CODE = 'time_tag_unknown_ids'\nexport const TAG_TARGET_LOCKED_CODE = 'time_entry_locked'\n\n/**\n * An assignment snapshot is written into the audit payload so undo can put the\n * junction rows back. A tag used across thousands of rows would bloat the log, so\n * the snapshot stops here and says so; undo then restores what it captured.\n */\nexport const TAG_ASSIGNMENT_SNAPSHOT_LIMIT = 500\n\nconst tagCrudIndexer: CrudIndexerConfig<StaffTimeTag> = {\n entityType: 'staff:staff_time_tag',\n}\n\nconst taskCrudIndexer: CrudIndexerConfig<StaffTimeTask> = {\n entityType: 'staff:staff_time_task',\n}\n\nconst timeEntryCrudIndexer: CrudIndexerConfig<StaffTimeEntry> = {\n entityType: 'staff:staff_time_entry',\n}\n\n// No CRUD event ids are declared for tags in `events.ts`: a tag write is\n// vocabulary maintenance the screens re-read, so these commands queue index and\n// cache side effects only.\n\ntype Translate = (key: string, fallback: string) => string\n\n/**\n * `data/validators.ts` ships the create shape only. The update schema is derived\n * from it rather than restated so the slug/label/color rules cannot drift apart.\n */\nexport const staffTimeTagUpdateSchema = z.object({\n id: z.string().uuid(),\n slug: staffTimeTagCreateSchema.shape.slug.optional(),\n label: staffTimeTagCreateSchema.shape.label.optional(),\n color: staffTimeTagCreateSchema.shape.color,\n})\n\nexport type StaffTimeTagUpdateInput = z.infer<typeof staffTimeTagUpdateSchema>\n\ntype TagSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n slug: string\n label: string\n color: string | null\n deletedAt: string | null\n}\n\ntype TagAssignmentSnapshot = {\n id: string\n tenantId: string\n organizationId: string\n tagId: string\n targetId: string\n}\n\ntype TagUndoPayload = {\n before?: TagSnapshot | null\n after?: TagSnapshot | null\n taskAssignments?: TagAssignmentSnapshot[] | null\n entryAssignments?: TagAssignmentSnapshot[] | null\n assignmentsTruncated?: boolean\n}\n\ntype AssignmentUndoPayload = {\n target: 'task' | 'time_entry'\n targetId: string\n tenantId: string\n organizationId: string\n tagIdsBefore: string[]\n tagIdsAfter: string[]\n added: string[]\n removed: TagAssignmentSnapshot[]\n}\n\nfunction toTagSnapshot(tag: StaffTimeTag): TagSnapshot {\n return {\n id: tag.id,\n tenantId: tag.tenantId,\n organizationId: tag.organizationId,\n slug: tag.slug,\n label: tag.label,\n color: tag.color ?? null,\n deletedAt: tag.deletedAt ? tag.deletedAt.toISOString() : null,\n }\n}\n\nasync function loadTagSnapshot(\n em: EntityManager,\n id: string,\n scope?: StaffSnapshotScope | null,\n): Promise<TagSnapshot | null> {\n const tag = await em.findOne(StaffTimeTag, scopedStaffSnapshotWhere(id, scope))\n return tag ? toTagSnapshot(tag) : null\n}\n\nfunction slugDuplicateError(translate: Translate, slug: string): CrudHttpError {\n const message = translate(\n 'staff.time_tracking.tags.errors.slugDuplicate',\n 'A tag with this slug already exists.',\n )\n return new CrudHttpError(409, {\n code: TAG_SLUG_DUPLICATE_CODE,\n error: message,\n slug,\n fieldErrors: { slug: message },\n })\n}\n\nfunction tagNotFoundError(translate: Translate): CrudHttpError {\n return new CrudHttpError(404, {\n error: translate('staff.time_tracking.tags.errors.notFound', 'Tag not found or not accessible.'),\n })\n}\n\nfunction unknownTagIdsError(translate: Translate, unknownIds: string[]): CrudHttpError {\n const message = translate(\n 'staff.time_tracking.tags.errors.unknownIds',\n 'Some tags do not exist in this organization.',\n )\n return new CrudHttpError(422, {\n code: TAG_UNKNOWN_IDS_CODE,\n error: message,\n unknownIds,\n fieldErrors: { tagIds: message },\n })\n}\n\nfunction targetNotFoundError(translate: Translate, target: 'task' | 'time_entry'): CrudHttpError {\n // Deliberately the same 404 for \"does not exist\" and \"not yours\": tagging must\n // not become a way to probe which task or entry ids exist (screen 17 note 1).\n const message =\n target === 'task'\n ? translate('staff.time_tracking.tags.errors.taskNotFound', 'Task not found or not accessible.')\n : translate('staff.time_tracking.tags.errors.entryNotFound', 'Time entry not found or not accessible.')\n return new CrudHttpError(404, { error: message })\n}\n\n/**\n * Persistence for one junction table. The command bodies below are shared; only\n * these closures know which table and which target column they address, so each\n * ORM call stays fully typed against its own entity.\n */\ntype TagJunctionOps = {\n target: 'task' | 'time_entry'\n resourceKind: string\n loadAssignments: (\n em: EntityManager,\n targetId: string,\n scope: StaffCommandScope,\n ) => Promise<TagAssignmentSnapshot[]>\n loadAssignmentsForSnapshot: (\n em: EntityManager,\n targetId: string,\n scope: StaffSnapshotScope | null,\n ) => Promise<TagAssignmentSnapshot[]>\n loadAssignmentsByTag: (\n em: EntityManager,\n tagId: string,\n scope: StaffCommandScope,\n ) => Promise<TagAssignmentSnapshot[]>\n createAssignment: (em: EntityManager, row: Omit<TagAssignmentSnapshot, 'id'>) => void\n deleteAssignments: (em: EntityManager, ids: string[], scope: StaffCommandScope) => Promise<number>\n deleteAssignmentsByTag: (em: EntityManager, tagId: string, scope: StaffCommandScope) => Promise<number>\n restoreAssignments: (em: EntityManager, rows: TagAssignmentSnapshot[]) => Promise<void>\n}\n\nfunction snapshotScopeWhere(scope?: StaffSnapshotScope | null): { tenantId?: string; organizationId?: string } {\n const where: { tenantId?: string; organizationId?: string } = {}\n if (scope?.tenantId) where.tenantId = scope.tenantId\n if (scope?.organizationId) where.organizationId = scope.organizationId\n return where\n}\n\nconst taskJunctionOps: TagJunctionOps = {\n target: 'task',\n resourceKind: 'staff.timesheets.task_tag',\n loadAssignments: async (em, targetId, scope) => {\n const rows = await em.find(StaffTimeTaskTag, applyScopeToWhere<StaffTimeTaskTag>({ taskId: targetId }, scope))\n return rows.map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n targetId: row.taskId,\n }))\n },\n loadAssignmentsForSnapshot: async (em, targetId, scope) => {\n const rows = await em.find(StaffTimeTaskTag, { taskId: targetId, ...snapshotScopeWhere(scope) })\n return rows.map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n targetId: row.taskId,\n }))\n },\n loadAssignmentsByTag: async (em, tagId, scope) => {\n const rows = await em.find(StaffTimeTaskTag, applyScopeToWhere<StaffTimeTaskTag>({ tagId }, scope), {\n limit: TAG_ASSIGNMENT_SNAPSHOT_LIMIT + 1,\n })\n return rows.map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n targetId: row.taskId,\n }))\n },\n createAssignment: (em, row) => {\n em.persist(\n em.create(StaffTimeTaskTag, {\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n taskId: row.targetId,\n createdAt: new Date(),\n }),\n )\n },\n deleteAssignments: async (em, ids, scope) =>\n em.nativeDelete(StaffTimeTaskTag, applyScopeToWhere<StaffTimeTaskTag>({ id: { $in: ids } }, scope)),\n deleteAssignmentsByTag: async (em, tagId, scope) =>\n em.nativeDelete(StaffTimeTaskTag, applyScopeToWhere<StaffTimeTaskTag>({ tagId }, scope)),\n restoreAssignments: async (em, rows) => {\n for (const row of rows) {\n const existing = await em.findOne(StaffTimeTaskTag, {\n tagId: row.tagId,\n taskId: row.targetId,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n })\n if (existing) continue\n em.persist(\n em.create(StaffTimeTaskTag, {\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n taskId: row.targetId,\n createdAt: new Date(),\n }),\n )\n }\n },\n}\n\nconst entryJunctionOps: TagJunctionOps = {\n target: 'time_entry',\n resourceKind: 'staff.timesheets.entry_tag',\n loadAssignments: async (em, targetId, scope) => {\n const rows = await em.find(\n StaffTimeEntryTag,\n applyScopeToWhere<StaffTimeEntryTag>({ timeEntryId: targetId }, scope),\n )\n return rows.map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n targetId: row.timeEntryId,\n }))\n },\n loadAssignmentsForSnapshot: async (em, targetId, scope) => {\n const rows = await em.find(StaffTimeEntryTag, { timeEntryId: targetId, ...snapshotScopeWhere(scope) })\n return rows.map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n targetId: row.timeEntryId,\n }))\n },\n loadAssignmentsByTag: async (em, tagId, scope) => {\n const rows = await em.find(StaffTimeEntryTag, applyScopeToWhere<StaffTimeEntryTag>({ tagId }, scope), {\n limit: TAG_ASSIGNMENT_SNAPSHOT_LIMIT + 1,\n })\n return rows.map((row) => ({\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n targetId: row.timeEntryId,\n }))\n },\n createAssignment: (em, row) => {\n em.persist(\n em.create(StaffTimeEntryTag, {\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n timeEntryId: row.targetId,\n createdAt: new Date(),\n }),\n )\n },\n deleteAssignments: async (em, ids, scope) =>\n em.nativeDelete(StaffTimeEntryTag, applyScopeToWhere<StaffTimeEntryTag>({ id: { $in: ids } }, scope)),\n deleteAssignmentsByTag: async (em, tagId, scope) =>\n em.nativeDelete(StaffTimeEntryTag, applyScopeToWhere<StaffTimeEntryTag>({ tagId }, scope)),\n restoreAssignments: async (em, rows) => {\n for (const row of rows) {\n const existing = await em.findOne(StaffTimeEntryTag, {\n tagId: row.tagId,\n timeEntryId: row.targetId,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n })\n if (existing) continue\n em.persist(\n em.create(StaffTimeEntryTag, {\n id: row.id,\n tenantId: row.tenantId,\n organizationId: row.organizationId,\n tagId: row.tagId,\n timeEntryId: row.targetId,\n createdAt: new Date(),\n }),\n )\n }\n },\n}\n\nasync function requireKnownTags(\n em: EntityManager,\n tagIds: string[],\n scope: StaffCommandScope,\n translate: Translate,\n): Promise<void> {\n const unique = Array.from(new Set(tagIds))\n if (unique.length === 0) return\n const tags = await em.find(\n StaffTimeTag,\n applyScopeToWhere<StaffTimeTag>({ id: { $in: unique }, deletedAt: null }, scope),\n { fields: ['id'] },\n )\n const known = new Set(tags.map((tag) => tag.id))\n const unknownIds = unique.filter((id) => !known.has(id))\n if (unknownIds.length > 0) throw unknownTagIdsError(translate, unknownIds)\n}\n\nconst createTagCommand: CommandHandler<StaffTimeTagCreateInput, { tagId: string }> = {\n id: staffTimeTagCommandIds.create,\n async prepare(rawInput, ctx) {\n const parsed = staffTimeTagCreateSchema.safeParse(rawInput)\n if (!parsed.success) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const existing = await em.findOne(StaffTimeTag, {\n slug: parsed.data.slug,\n tenantId: parsed.data.tenantId,\n organizationId: parsed.data.organizationId,\n deletedAt: null,\n })\n return { before: { slugTaken: existing != null } }\n },\n async execute(rawInput, ctx) {\n const parsed = staffTimeTagCreateSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n const scope = commandInputScope(ctx, parsed.tenantId, parsed.organizationId)\n const { translate } = await resolveTranslations()\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let created: StaffTimeTag | null = null\n\n try {\n await withAtomicFlush(\n em,\n [\n async () => {\n // The read lives in the same transaction as the insert, so a\n // concurrent create cannot slip a second row past this check; the\n // partial unique index is the backstop and is translated below.\n const duplicate = await em.findOne(\n StaffTimeTag,\n applyScopeToWhere<StaffTimeTag>({ slug: parsed.slug, deletedAt: null }, scope),\n { fields: ['id'] },\n )\n if (duplicate) throw slugDuplicateError(translate, parsed.slug)\n },\n () => {\n created = em.create(StaffTimeTag, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n slug: parsed.slug,\n label: parsed.label,\n color: parsed.color ?? null,\n createdAt: new Date(),\n updatedAt: new Date(),\n deletedAt: null,\n })\n em.persist(created)\n },\n ],\n { transaction: true, label: staffTimeTagCommandIds.create },\n )\n } catch (err) {\n if (isUniqueViolation(err)) throw slugDuplicateError(translate, parsed.slug)\n throw err\n }\n\n const record = created as StaffTimeTag | null\n if (!record) throw tagNotFoundError(translate)\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'created',\n entity: record,\n identifiers: { id: record.id, organizationId: record.organizationId, tenantId: record.tenantId },\n indexer: tagCrudIndexer,\n })\n\n return { tagId: record.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadTagSnapshot(em, result.tagId, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return null\n return { snapshot }\n },\n buildLog: async ({ result, ctx }) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const snapshot = await loadTagSnapshot(em, result.tagId, staffSnapshotScopeFromContext(ctx))\n if (!snapshot) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tags.create', 'Create tag'),\n resourceKind: 'staff.timesheets.tag',\n resourceId: snapshot.id,\n tenantId: snapshot.tenantId,\n organizationId: snapshot.organizationId,\n snapshotAfter: snapshot,\n payload: {\n undo: { after: snapshot } satisfies TagUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TagUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const snapshotScope = staffSnapshotScopeFromSnapshot(after)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tag = await em.findOne(StaffTimeTag, scopedStaffSnapshotWhere(after.id, snapshotScope))\n if (!tag) return\n\n await withAtomicFlush(\n em,\n [\n () => {\n tag.deletedAt = new Date()\n tag.updatedAt = new Date()\n },\n ],\n { transaction: true, label: `${staffTimeTagCommandIds.create}.undo` },\n )\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'deleted',\n entity: tag,\n identifiers: { id: tag.id, organizationId: tag.organizationId, tenantId: tag.tenantId },\n indexer: tagCrudIndexer,\n })\n },\n}\n\nconst updateTagCommand: CommandHandler<StaffTimeTagUpdateInput, { tagId: string }> = {\n id: staffTimeTagCommandIds.update,\n async prepare(rawInput, ctx) {\n const parsed = staffTimeTagUpdateSchema.safeParse(rawInput)\n if (!parsed.success) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const before = await loadTagSnapshot(em, parsed.data.id, staffSnapshotScopeFromContext(ctx))\n if (!before) return {}\n return { before: { snapshot: before } }\n },\n async execute(rawInput, ctx) {\n const parsed = staffTimeTagUpdateSchema.parse(rawInput)\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n\n const tag = await em.findOne(StaffTimeTag, applyScopeToWhere<StaffTimeTag>({ id: parsed.id, deletedAt: null }, scope))\n if (!tag) throw tagNotFoundError(translate)\n ensureTenantScope(ctx, tag.tenantId)\n ensureOrganizationScope(ctx, tag.organizationId)\n\n try {\n await withAtomicFlush(\n em,\n [\n async () => {\n if (parsed.slug === undefined || parsed.slug === tag.slug) return\n const duplicate = await em.findOne(\n StaffTimeTag,\n applyScopeToWhere<StaffTimeTag>({ slug: parsed.slug, deletedAt: null, id: { $ne: tag.id } }, scope),\n { fields: ['id'] },\n )\n if (duplicate) throw slugDuplicateError(translate, parsed.slug)\n },\n () => {\n if (parsed.slug !== undefined) tag.slug = parsed.slug\n if (parsed.label !== undefined) tag.label = parsed.label\n if (parsed.color !== undefined) tag.color = parsed.color ?? null\n tag.updatedAt = new Date()\n },\n ],\n { transaction: true, label: staffTimeTagCommandIds.update },\n )\n } catch (err) {\n if (isUniqueViolation(err)) throw slugDuplicateError(translate, parsed.slug ?? tag.slug)\n throw err\n }\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'updated',\n entity: tag,\n identifiers: { id: tag.id, organizationId: tag.organizationId, tenantId: tag.tenantId },\n indexer: tagCrudIndexer,\n })\n\n return { tagId: tag.id }\n },\n buildLog: async ({ snapshots, ctx }) => {\n const before = (snapshots.before as { snapshot?: TagSnapshot } | undefined)?.snapshot\n if (!before) return null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const after = await loadTagSnapshot(em, before.id, staffSnapshotScopeFromSnapshot(before))\n if (!after) return null\n const changes = buildChanges(\n before as unknown as Record<string, unknown>,\n after as unknown as Record<string, unknown>,\n ['slug', 'label', 'color'],\n )\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tags.update', 'Update tag'),\n resourceKind: 'staff.timesheets.tag',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n snapshotAfter: after,\n changes,\n payload: {\n undo: { before, after } satisfies TagUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TagUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const snapshotScope = staffSnapshotScopeFromSnapshot(before)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tag = await em.findOne(StaffTimeTag, scopedStaffSnapshotWhere(before.id, snapshotScope))\n if (!tag) return\n\n await withAtomicFlush(\n em,\n [\n () => {\n tag.slug = before.slug\n tag.label = before.label\n tag.color = before.color ?? null\n tag.updatedAt = new Date()\n },\n ],\n { transaction: true, label: `${staffTimeTagCommandIds.update}.undo` },\n )\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'updated',\n entity: tag,\n identifiers: { id: tag.id, organizationId: tag.organizationId, tenantId: tag.tenantId },\n indexer: tagCrudIndexer,\n })\n },\n}\n\ntype DeleteTagResult = {\n tagId: string\n removedTaskAssignments: number\n removedEntryAssignments: number\n}\n\nconst deleteTagCommand: CommandHandler<{ id?: string }, DeleteTagResult> = {\n id: staffTimeTagCommandIds.delete,\n async prepare(input, ctx) {\n const id = input?.id\n if (!id) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const scope = staffSnapshotScopeFromContext(ctx)\n const before = await loadTagSnapshot(em, id, scope)\n if (!before) return {}\n const readScope: StaffCommandScope = {\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n requireTenant: true,\n requireOrganization: true,\n }\n const taskAssignments = await taskJunctionOps.loadAssignmentsByTag(em, id, readScope)\n const entryAssignments = await entryJunctionOps.loadAssignmentsByTag(em, id, readScope)\n const truncated =\n taskAssignments.length > TAG_ASSIGNMENT_SNAPSHOT_LIMIT || entryAssignments.length > TAG_ASSIGNMENT_SNAPSHOT_LIMIT\n return {\n before: {\n snapshot: before,\n taskAssignments: taskAssignments.slice(0, TAG_ASSIGNMENT_SNAPSHOT_LIMIT),\n entryAssignments: entryAssignments.slice(0, TAG_ASSIGNMENT_SNAPSHOT_LIMIT),\n assignmentsTruncated: truncated,\n },\n }\n },\n async execute(input, ctx) {\n const { translate } = await resolveTranslations()\n const id = input?.id\n if (!id) {\n throw new CrudHttpError(400, {\n error: translate('staff.time_tracking.tags.errors.idRequired', 'Tag id is required.'),\n })\n }\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope = commandActorScope(ctx)\n const tag = await em.findOne(StaffTimeTag, applyScopeToWhere<StaffTimeTag>({ id, deletedAt: null }, scope))\n if (!tag) throw tagNotFoundError(translate)\n ensureTenantScope(ctx, tag.tenantId)\n ensureOrganizationScope(ctx, tag.organizationId)\n\n let removedTaskAssignments = 0\n let removedEntryAssignments = 0\n\n await withAtomicFlush(\n em,\n [\n async () => {\n // The cascade is deliberate: the badge disappears everywhere it was\n // used, and the counts travel back so the caller sees the reach.\n removedTaskAssignments = await taskJunctionOps.deleteAssignmentsByTag(em, tag.id, scope)\n removedEntryAssignments = await entryJunctionOps.deleteAssignmentsByTag(em, tag.id, scope)\n },\n () => {\n tag.deletedAt = new Date()\n tag.updatedAt = new Date()\n },\n ],\n { transaction: true, label: staffTimeTagCommandIds.delete },\n )\n\n await emitCrudSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'deleted',\n entity: tag,\n identifiers: { id: tag.id, organizationId: tag.organizationId, tenantId: tag.tenantId },\n indexer: tagCrudIndexer,\n })\n\n return { tagId: tag.id, removedTaskAssignments, removedEntryAssignments }\n },\n buildLog: async ({ result, snapshots }) => {\n const prepared = snapshots.before as\n | {\n snapshot?: TagSnapshot\n taskAssignments?: TagAssignmentSnapshot[]\n entryAssignments?: TagAssignmentSnapshot[]\n assignmentsTruncated?: boolean\n }\n | undefined\n const before = prepared?.snapshot\n if (!before) return null\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tags.delete', 'Delete tag'),\n resourceKind: 'staff.timesheets.tag',\n resourceId: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n snapshotBefore: before,\n changes: {\n removedTaskAssignments: { from: result.removedTaskAssignments, to: 0 },\n removedEntryAssignments: { from: result.removedEntryAssignments, to: 0 },\n },\n payload: {\n undo: {\n before,\n taskAssignments: prepared?.taskAssignments ?? null,\n entryAssignments: prepared?.entryAssignments ?? null,\n assignmentsTruncated: prepared?.assignmentsTruncated ?? false,\n } satisfies TagUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<TagUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const snapshotScope = staffSnapshotScopeFromSnapshot(before)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n let tag = await em.findOne(StaffTimeTag, scopedStaffSnapshotWhere(before.id, snapshotScope))\n\n await withAtomicFlush(\n em,\n [\n () => {\n if (!tag) {\n tag = em.create(StaffTimeTag, {\n id: before.id,\n tenantId: before.tenantId,\n organizationId: before.organizationId,\n slug: before.slug,\n label: before.label,\n color: before.color ?? null,\n createdAt: new Date(),\n updatedAt: new Date(),\n deletedAt: null,\n })\n em.persist(tag)\n return\n }\n tag.slug = before.slug\n tag.label = before.label\n tag.color = before.color ?? null\n tag.deletedAt = null\n tag.updatedAt = new Date()\n },\n // Restoring assignments re-reads the junction rows, so it is its own phase.\n () => taskJunctionOps.restoreAssignments(em, payload?.taskAssignments ?? []),\n () => entryJunctionOps.restoreAssignments(em, payload?.entryAssignments ?? []),\n ],\n { transaction: true, label: `${staffTimeTagCommandIds.delete}.undo` },\n )\n\n await emitCrudUndoSideEffects({\n dataEngine: ctx.container.resolve('dataEngine'),\n action: 'created',\n entity: tag,\n identifiers: { id: before.id, organizationId: before.organizationId, tenantId: before.tenantId },\n indexer: tagCrudIndexer,\n })\n },\n}\n\ntype AssignmentResult = {\n targetId: string\n assignedTagIds: string[]\n alreadyAssignedTagIds: string[]\n tagIds: string[]\n}\n\ntype UnassignmentResult = {\n targetId: string\n removedTagIds: string[]\n notAssignedTagIds: string[]\n tagIds: string[]\n}\n\ntype AssignmentTarget = {\n id: string\n tenantId: string\n organizationId: string\n}\n\nasync function requireTaskTarget(\n em: EntityManager,\n taskId: string,\n scope: StaffCommandScope,\n translate: Translate,\n): Promise<AssignmentTarget> {\n const task = await em.findOne(StaffTimeTask, applyScopeToWhere<StaffTimeTask>({ id: taskId, deletedAt: null }, scope))\n if (!task) throw targetNotFoundError(translate, 'task')\n return { id: task.id, tenantId: task.tenantId, organizationId: task.organizationId }\n}\n\nasync function requireEntryTarget(\n em: EntityManager,\n timeEntryId: string,\n scope: StaffCommandScope,\n translate: Translate,\n): Promise<AssignmentTarget> {\n const entry = await em.findOne(\n StaffTimeEntry,\n applyScopeToWhere<StaffTimeEntry>({ id: timeEntryId, deletedAt: null }, scope),\n )\n if (!entry) throw targetNotFoundError(translate, 'time_entry')\n if (entry.lockedReportId) {\n // An entry frozen in a closed report is read-only, and its tags are part of\n // what the report renders.\n throw entryTagsLockedError(translate, entry.lockedReportId)\n }\n return { id: entry.id, tenantId: entry.tenantId, organizationId: entry.organizationId }\n}\n\nfunction entryTagsLockedError(translate: Translate, lockedReportId: string): CrudHttpError {\n return new CrudHttpError(409, {\n code: TAG_TARGET_LOCKED_CODE,\n error: translate(\n 'staff.time_tracking.tags.errors.entryLocked',\n 'This time entry is locked in a closed report and its tags cannot be changed.',\n ),\n lockedReportId,\n })\n}\n\n/**\n * Undo restores a tag set the entry had before a report froze it, so it is held\n * to the same lock as the forward commands. It deliberately ignores `deletedAt`\n * and a missing row: neither can carry a lock, and undo behaved that way before.\n */\nasync function assertEntryTagUndoAllowed(\n payload: AssignmentUndoPayload,\n ctx: { container: { resolve: (name: string) => unknown } },\n): Promise<void> {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const entry = await findOneWithDecryption(\n em,\n StaffTimeEntry,\n { id: payload.targetId, tenantId: payload.tenantId, organizationId: payload.organizationId },\n undefined,\n { tenantId: payload.tenantId, organizationId: payload.organizationId },\n )\n if (!entry?.lockedReportId) return\n const { translate } = await resolveTranslations()\n throw entryTagsLockedError(translate, entry.lockedReportId)\n}\n\ntype AssignmentCommandInput = {\n tenantId: string\n organizationId: string\n targetId: string\n tagIds: string[]\n}\n\nfunction refreshTargetSideEffects(ops: TagJunctionOps) {\n return async (ctx: { container: { resolve: (name: string) => unknown } }, target: AssignmentTarget) => {\n const dataEngine = ctx.container.resolve('dataEngine') as DataEngine\n // The target's own index document and cached lists carry its tags, so they\n // are refreshed even though no column on the target row changed.\n await emitCrudSideEffects({\n dataEngine,\n action: 'updated',\n entity: target,\n identifiers: { id: target.id, organizationId: target.organizationId, tenantId: target.tenantId },\n indexer: ops.target === 'task' ? taskCrudIndexer : timeEntryCrudIndexer,\n })\n }\n}\n\nasync function runAssignment(\n ops: TagJunctionOps,\n requireTarget: (\n em: EntityManager,\n targetId: string,\n scope: StaffCommandScope,\n translate: Translate,\n ) => Promise<AssignmentTarget>,\n parsed: AssignmentCommandInput,\n ctx: Parameters<CommandHandler<AssignmentCommandInput, AssignmentResult>['execute']>[1],\n): Promise<AssignmentResult> {\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n const scope = commandInputScope(ctx, parsed.tenantId, parsed.organizationId)\n const { translate } = await resolveTranslations()\n\n const requestedTagIds = Array.from(new Set(parsed.tagIds))\n\n let target: AssignmentTarget | null = null\n let assignedTagIds: string[] = []\n let alreadyAssignedTagIds: string[] = []\n let tagIdsAfter: string[] = []\n\n // Each attempt gets its own fork: a rolled-back unit of work must not carry\n // its persisted-but-uncommitted rows into the retry.\n const attempt = async (): Promise<void> => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n await withAtomicFlush(\n em,\n [\n async () => {\n target = await requireTarget(em, parsed.targetId, scope, translate)\n await requireKnownTags(em, requestedTagIds, scope, translate)\n const existing = await ops.loadAssignments(em, parsed.targetId, scope)\n const existingTagIds = new Set(existing.map((row) => row.tagId))\n alreadyAssignedTagIds = requestedTagIds.filter((tagId) => existingTagIds.has(tagId))\n assignedTagIds = requestedTagIds.filter((tagId) => !existingTagIds.has(tagId))\n tagIdsAfter = Array.from(new Set([...existingTagIds, ...requestedTagIds]))\n },\n () => {\n for (const tagId of assignedTagIds) {\n ops.createAssignment(em, {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n tagId,\n targetId: parsed.targetId,\n })\n }\n },\n ],\n { transaction: true, label: `${ops.target}.tags.assign` },\n )\n }\n\n try {\n await attempt()\n } catch (err) {\n if (!isUniqueViolation(err)) throw err\n // A concurrent assign won the race. Re-reading turns the collision into the\n // no-op success the second caller asked for; a second violation would mean\n // the row is there either way, which is the requested end state.\n try {\n await attempt()\n } catch (retryErr) {\n if (!isUniqueViolation(retryErr)) throw retryErr\n assignedTagIds = []\n alreadyAssignedTagIds = requestedTagIds\n tagIdsAfter = requestedTagIds\n }\n }\n\n const resolvedTarget = target as AssignmentTarget | null\n if (!resolvedTarget) throw targetNotFoundError(translate, ops.target)\n\n if (assignedTagIds.length > 0) {\n await refreshTargetSideEffects(ops)(ctx, resolvedTarget)\n }\n\n return {\n targetId: parsed.targetId,\n assignedTagIds,\n alreadyAssignedTagIds,\n tagIds: tagIdsAfter,\n }\n}\n\nasync function runUnassignment(\n ops: TagJunctionOps,\n requireTarget: (\n em: EntityManager,\n targetId: string,\n scope: StaffCommandScope,\n translate: Translate,\n ) => Promise<AssignmentTarget>,\n parsed: AssignmentCommandInput,\n ctx: Parameters<CommandHandler<AssignmentCommandInput, UnassignmentResult>['execute']>[1],\n): Promise<UnassignmentResult> {\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n const scope = commandInputScope(ctx, parsed.tenantId, parsed.organizationId)\n const { translate } = await resolveTranslations()\n\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const requestedTagIds = Array.from(new Set(parsed.tagIds))\n\n let target: AssignmentTarget | null = null\n let removedTagIds: string[] = []\n let notAssignedTagIds: string[] = []\n let tagIdsAfter: string[] = []\n\n await withAtomicFlush(\n em,\n [\n async () => {\n target = await requireTarget(em, parsed.targetId, scope, translate)\n const existing = await ops.loadAssignments(em, parsed.targetId, scope)\n const byTagId = new Map(existing.map((row) => [row.tagId, row]))\n removedTagIds = requestedTagIds.filter((tagId) => byTagId.has(tagId))\n notAssignedTagIds = requestedTagIds.filter((tagId) => !byTagId.has(tagId))\n tagIdsAfter = existing.map((row) => row.tagId).filter((tagId) => !removedTagIds.includes(tagId))\n const removableIds = removedTagIds\n .map((tagId) => byTagId.get(tagId)?.id)\n .filter((value): value is string => typeof value === 'string')\n // Removing a tag that is not assigned is a no-op, not a 404: the caller\n // asked for an end state that already holds.\n if (removableIds.length > 0) await ops.deleteAssignments(em, removableIds, scope)\n },\n ],\n { transaction: true, label: `${ops.target}.tags.unassign` },\n )\n\n const resolvedTarget = target as AssignmentTarget | null\n if (!resolvedTarget) throw targetNotFoundError(translate, ops.target)\n\n if (removedTagIds.length > 0) {\n await refreshTargetSideEffects(ops)(ctx, resolvedTarget)\n }\n\n return {\n targetId: parsed.targetId,\n removedTagIds,\n notAssignedTagIds,\n tagIds: tagIdsAfter,\n }\n}\n\nasync function restoreAssignmentState(\n ops: TagJunctionOps,\n payload: AssignmentUndoPayload,\n ctx: { container: { resolve: (name: string) => unknown } },\n): Promise<void> {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const scope: StaffCommandScope = {\n tenantId: payload.tenantId,\n organizationId: payload.organizationId,\n requireTenant: true,\n requireOrganization: true,\n }\n\n await withAtomicFlush(\n em,\n [\n async () => {\n if (payload.added.length > 0) {\n const current = await ops.loadAssignments(em, payload.targetId, scope)\n const removableIds = current\n .filter((row) => payload.added.includes(row.tagId))\n .map((row) => row.id)\n if (removableIds.length > 0) await ops.deleteAssignments(em, removableIds, scope)\n }\n },\n () => ops.restoreAssignments(em, payload.removed),\n ],\n { transaction: true, label: `${ops.target}.tags.undo` },\n )\n}\n\nfunction assignmentLogPayload(\n ops: TagJunctionOps,\n parsed: AssignmentCommandInput,\n result: AssignmentResult,\n): AssignmentUndoPayload {\n return {\n target: ops.target,\n targetId: parsed.targetId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n tagIdsBefore: result.tagIds.filter((tagId) => !result.assignedTagIds.includes(tagId)),\n tagIdsAfter: result.tagIds,\n added: result.assignedTagIds,\n removed: [],\n }\n}\n\nfunction unassignmentLogPayload(\n ops: TagJunctionOps,\n parsed: AssignmentCommandInput,\n result: UnassignmentResult,\n removedRows: TagAssignmentSnapshot[],\n): AssignmentUndoPayload {\n return {\n target: ops.target,\n targetId: parsed.targetId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n tagIdsBefore: [...result.tagIds, ...result.removedTagIds],\n tagIdsAfter: result.tagIds,\n added: [],\n removed: removedRows,\n }\n}\n\nfunction toTaskCommandInput(parsed: StaffTimeTaskTagAssignmentInput): AssignmentCommandInput {\n return {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n targetId: parsed.taskId,\n tagIds: parsed.tagIds,\n }\n}\n\nfunction toEntryCommandInput(parsed: StaffTimeEntryTagAssignmentInput): AssignmentCommandInput {\n return {\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n targetId: parsed.timeEntryId,\n tagIds: parsed.tagIds,\n }\n}\n\nconst assignTaskTagsCommand: CommandHandler<StaffTimeTaskTagAssignmentInput, AssignmentResult> = {\n id: staffTimeTagCommandIds.assignTask,\n async prepare(rawInput, ctx) {\n const parsed = staffTimeTaskTagAssignmentSchema.safeParse(rawInput)\n if (!parsed.success) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const rows = await taskJunctionOps.loadAssignmentsForSnapshot(\n em,\n parsed.data.taskId,\n staffSnapshotScopeFromContext(ctx),\n )\n return { before: { tagIds: rows.map((row) => row.tagId) } }\n },\n execute: async (rawInput, ctx) => {\n const parsed = staffTimeTaskTagAssignmentSchema.parse(rawInput)\n return runAssignment(taskJunctionOps, requireTaskTarget, toTaskCommandInput(parsed), ctx)\n },\n buildLog: async ({ input, result, snapshots }) => {\n const parsed = staffTimeTaskTagAssignmentSchema.parse(input)\n const commandInput = toTaskCommandInput(parsed)\n const before = (snapshots.before as { tagIds?: string[] } | undefined)?.tagIds ?? []\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tags.assign_task', 'Assign task tags'),\n resourceKind: taskJunctionOps.resourceKind,\n resourceId: commandInput.targetId,\n tenantId: commandInput.tenantId,\n organizationId: commandInput.organizationId,\n snapshotBefore: { tagIds: before },\n snapshotAfter: { tagIds: result.tagIds },\n changes: { tagIds: { from: before, to: result.tagIds } },\n payload: {\n undo: assignmentLogPayload(taskJunctionOps, commandInput, result),\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AssignmentUndoPayload>(logEntry)\n if (!payload) return\n await restoreAssignmentState(taskJunctionOps, payload, ctx)\n },\n}\n\nconst unassignTaskTagsCommand: CommandHandler<StaffTimeTaskTagAssignmentInput, UnassignmentResult> = {\n id: staffTimeTagCommandIds.unassignTask,\n async prepare(rawInput, ctx) {\n const parsed = staffTimeTaskTagAssignmentSchema.safeParse(rawInput)\n if (!parsed.success) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const rows = await taskJunctionOps.loadAssignmentsForSnapshot(\n em,\n parsed.data.taskId,\n staffSnapshotScopeFromContext(ctx),\n )\n return { before: { rows } }\n },\n execute: async (rawInput, ctx) => {\n const parsed = staffTimeTaskTagAssignmentSchema.parse(rawInput)\n return runUnassignment(taskJunctionOps, requireTaskTarget, toTaskCommandInput(parsed), ctx)\n },\n buildLog: async ({ input, result, snapshots }) => {\n const parsed = staffTimeTaskTagAssignmentSchema.parse(input)\n const commandInput = toTaskCommandInput(parsed)\n const rows = (snapshots.before as { rows?: TagAssignmentSnapshot[] } | undefined)?.rows ?? []\n const removedRows = rows.filter((row) => result.removedTagIds.includes(row.tagId))\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tags.unassign_task', 'Unassign task tags'),\n resourceKind: taskJunctionOps.resourceKind,\n resourceId: commandInput.targetId,\n tenantId: commandInput.tenantId,\n organizationId: commandInput.organizationId,\n snapshotBefore: { tagIds: rows.map((row) => row.tagId) },\n snapshotAfter: { tagIds: result.tagIds },\n changes: { tagIds: { from: rows.map((row) => row.tagId), to: result.tagIds } },\n payload: {\n undo: unassignmentLogPayload(taskJunctionOps, commandInput, result, removedRows),\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AssignmentUndoPayload>(logEntry)\n if (!payload) return\n await restoreAssignmentState(taskJunctionOps, payload, ctx)\n },\n}\n\nconst assignEntryTagsCommand: CommandHandler<StaffTimeEntryTagAssignmentInput, AssignmentResult> = {\n id: staffTimeTagCommandIds.assignEntry,\n async prepare(rawInput, ctx) {\n const parsed = staffTimeEntryTagAssignmentSchema.safeParse(rawInput)\n if (!parsed.success) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const rows = await entryJunctionOps.loadAssignmentsForSnapshot(\n em,\n parsed.data.timeEntryId,\n staffSnapshotScopeFromContext(ctx),\n )\n return { before: { tagIds: rows.map((row) => row.tagId) } }\n },\n execute: async (rawInput, ctx) => {\n const parsed = staffTimeEntryTagAssignmentSchema.parse(rawInput)\n return runAssignment(entryJunctionOps, requireEntryTarget, toEntryCommandInput(parsed), ctx)\n },\n buildLog: async ({ input, result, snapshots }) => {\n const parsed = staffTimeEntryTagAssignmentSchema.parse(input)\n const commandInput = toEntryCommandInput(parsed)\n const before = (snapshots.before as { tagIds?: string[] } | undefined)?.tagIds ?? []\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tags.assign_entry', 'Assign time entry tags'),\n resourceKind: entryJunctionOps.resourceKind,\n resourceId: commandInput.targetId,\n tenantId: commandInput.tenantId,\n organizationId: commandInput.organizationId,\n snapshotBefore: { tagIds: before },\n snapshotAfter: { tagIds: result.tagIds },\n changes: { tagIds: { from: before, to: result.tagIds } },\n payload: {\n undo: assignmentLogPayload(entryJunctionOps, commandInput, result),\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AssignmentUndoPayload>(logEntry)\n if (!payload) return\n await assertEntryTagUndoAllowed(payload, ctx)\n await restoreAssignmentState(entryJunctionOps, payload, ctx)\n },\n}\n\nconst unassignEntryTagsCommand: CommandHandler<StaffTimeEntryTagAssignmentInput, UnassignmentResult> = {\n id: staffTimeTagCommandIds.unassignEntry,\n async prepare(rawInput, ctx) {\n const parsed = staffTimeEntryTagAssignmentSchema.safeParse(rawInput)\n if (!parsed.success) return {}\n const em = ctx.container.resolve('em') as EntityManager\n const rows = await entryJunctionOps.loadAssignmentsForSnapshot(\n em,\n parsed.data.timeEntryId,\n staffSnapshotScopeFromContext(ctx),\n )\n return { before: { rows } }\n },\n execute: async (rawInput, ctx) => {\n const parsed = staffTimeEntryTagAssignmentSchema.parse(rawInput)\n return runUnassignment(entryJunctionOps, requireEntryTarget, toEntryCommandInput(parsed), ctx)\n },\n buildLog: async ({ input, result, snapshots }) => {\n const parsed = staffTimeEntryTagAssignmentSchema.parse(input)\n const commandInput = toEntryCommandInput(parsed)\n const rows = (snapshots.before as { rows?: TagAssignmentSnapshot[] } | undefined)?.rows ?? []\n const removedRows = rows.filter((row) => result.removedTagIds.includes(row.tagId))\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('staff.audit.timesheets.tags.unassign_entry', 'Unassign time entry tags'),\n resourceKind: entryJunctionOps.resourceKind,\n resourceId: commandInput.targetId,\n tenantId: commandInput.tenantId,\n organizationId: commandInput.organizationId,\n snapshotBefore: { tagIds: rows.map((row) => row.tagId) },\n snapshotAfter: { tagIds: result.tagIds },\n changes: { tagIds: { from: rows.map((row) => row.tagId), to: result.tagIds } },\n payload: {\n undo: unassignmentLogPayload(entryJunctionOps, commandInput, result, removedRows),\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<AssignmentUndoPayload>(logEntry)\n if (!payload) return\n await assertEntryTagUndoAllowed(payload, ctx)\n await restoreAssignmentState(entryJunctionOps, payload, ctx)\n },\n}\n\nregisterCommand(createTagCommand)\nregisterCommand(updateTagCommand)\nregisterCommand(deleteTagCommand)\nregisterCommand(assignTaskTagsCommand)\nregisterCommand(unassignTaskTagsCommand)\nregisterCommand(assignEntryTagsCommand)\nregisterCommand(unassignEntryTagsCommand)\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -13,7 +13,11 @@ import { checkAttachmentAccess } from "@open-mercato/core/modules/attachments/li
13
13
  import { attachmentCrudEvents, attachmentCrudIndexer } from "@open-mercato/core/modules/attachments/lib/crud";
14
14
  import { readAttachmentMetadata } from "@open-mercato/core/modules/attachments/lib/metadata";
15
15
  import { clearAttachmentThumbnailCache } from "@open-mercato/core/modules/attachments/lib/thumbnailCache";
16
- import { isMultipartRequestWithinUploadLimit } from "@open-mercato/core/modules/attachments/lib/upload-limits";
16
+ import {
17
+ isMultipartUploadLimitError,
18
+ parseMultipartFormDataWithinUploadLimit,
19
+ resolveDefaultAttachmentMaxUploadBytes
20
+ } from "@open-mercato/core/modules/attachments/lib/upload-limits";
17
21
  import {
18
22
  isScopedAttachmentUploadError
19
23
  } from "@open-mercato/core/modules/attachments/lib/scoped-upload-service";
@@ -289,6 +293,13 @@ async function GET(req) {
289
293
  totalPages: Math.max(1, Math.ceil(visible.length / pageSize))
290
294
  });
291
295
  }
296
+ function uploadTooLargeResponse(t) {
297
+ return NextResponse.json({
298
+ ok: false,
299
+ error: t("attachments.errors.maxUploadSize", "Attachment exceeds the maximum upload size."),
300
+ code: "max_upload_size"
301
+ }, { status: 413 });
302
+ }
292
303
  async function handleUpload(req, replacement) {
293
304
  const contextOrResponse = await resolvePortalContext(req);
294
305
  if (contextOrResponse instanceof Response) return contextOrResponse;
@@ -298,13 +309,13 @@ async function handleUpload(req, replacement) {
298
309
  if (!contentType.toLowerCase().includes("multipart/form-data")) {
299
310
  return NextResponse.json({ ok: false, error: translate("warranty_claims.errors.invalidInput", "Invalid input") }, { status: 400 });
300
311
  }
301
- if (!isMultipartRequestWithinUploadLimit(req.headers.get("content-length"))) {
302
- return NextResponse.json({
303
- ok: false,
304
- error: t("attachments.errors.maxUploadSize", "Attachment exceeds the maximum upload size.")
305
- }, { status: 413 });
312
+ let form;
313
+ try {
314
+ form = await parseMultipartFormDataWithinUploadLimit(req);
315
+ } catch (error) {
316
+ if (isMultipartUploadLimitError(error)) return uploadTooLargeResponse(t);
317
+ throw error;
306
318
  }
307
- const form = await req.formData();
308
319
  const parsed = uploadBodySchema.safeParse({
309
320
  claimId: form.get("claimId") ?? void 0,
310
321
  file: form.get("file") instanceof File ? form.get("file").name : ""
@@ -321,6 +332,7 @@ async function handleUpload(req, replacement) {
321
332
  if (!(file instanceof File)) {
322
333
  return NextResponse.json({ ok: false, error: translate("warranty_claims.portal.detail.fileRequired", "Choose a file before uploading.") }, { status: 400 });
323
334
  }
335
+ if (file.size > resolveDefaultAttachmentMaxUploadBytes()) return uploadTooLargeResponse(t);
324
336
  const buffer = Buffer.from(await file.arrayBuffer());
325
337
  const attachmentId = replacement && parsedAttachmentId?.success ? parsedAttachmentId.data : null;
326
338
  const replacementTarget = attachmentId ? await loadOwnedVisibleAttachment(context, attachmentId) : null;
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/warranty_claims/api/portal/attachments/route.ts"],
4
- "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport type { CustomerAuthContext } from '@open-mercato/core/modules/customer_accounts/lib/customerAuth'\nimport { Attachment, AttachmentPartition } from '@open-mercato/core/modules/attachments/data/entities'\nimport { buildAttachmentImageUrl, slugifyAttachmentFileName } from '@open-mercato/core/modules/attachments/lib/imageUrls'\nimport { StorageDriverFactory } from '@open-mercato/core/modules/attachments/lib/drivers'\nimport { checkAttachmentAccess } from '@open-mercato/core/modules/attachments/lib/access'\nimport { attachmentCrudEvents, attachmentCrudIndexer } from '@open-mercato/core/modules/attachments/lib/crud'\nimport { readAttachmentMetadata } from '@open-mercato/core/modules/attachments/lib/metadata'\nimport { clearAttachmentThumbnailCache } from '@open-mercato/core/modules/attachments/lib/thumbnailCache'\nimport { isMultipartRequestWithinUploadLimit } from '@open-mercato/core/modules/attachments/lib/upload-limits'\nimport {\n isScopedAttachmentUploadError,\n type ScopedAttachmentUploadErrorCode,\n} from '@open-mercato/core/modules/attachments/lib/scoped-upload-service'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { WarrantyClaim } from '../../../data/entities'\nimport { WARRANTY_CLAIM_RESOURCE_KIND } from '../../../commands/shared'\nimport { CUSTOMER_VISIBLE_ATTACHMENT_TAG, isCustomerVisibleAttachment } from '../../../lib/attachmentVisibility'\nimport { loadPortalOwnedClaim } from '../../../lib/portalClaimAccess'\nimport { resolvePortalAttachmentUploadService } from '../../../lib/portalAttachmentUpload'\nimport { resolveLinkedCustomerAuth } from '../../../lib/portalAuthGuard'\n\nconst CLAIM_ATTACHMENT_ENTITY_ID = 'warranty_claims:warranty_claim'\nconst logger = createLogger('warranty_claims').child({ route: 'portal-attachments' })\nconst ATTACHMENT_ERROR_TRANSLATIONS: Partial<Record<ScopedAttachmentUploadErrorCode, readonly [string, string]>> = {\n dangerous_executable: ['attachments.errors.dangerousExecutable', 'Executable file types are not allowed as attachments.'],\n max_upload_size: ['attachments.errors.maxUploadSize', 'Attachment exceeds the maximum upload size.'],\n active_content: ['attachments.errors.activeContentBlocked', 'Active content uploads are not allowed.'],\n}\n\nexport const metadata = {\n GET: { requireAuth: false },\n POST: { requireAuth: false },\n PUT: { requireAuth: false },\n DELETE: { requireAuth: false },\n}\n\nconst attachmentQuerySchema = z.object({\n claimId: z.string().uuid(),\n page: z.coerce.number().int().min(1).optional(),\n pageSize: z.coerce.number().int().min(1).max(100).optional(),\n})\n\nconst uploadBodySchema = z.object({\n claimId: z.string().uuid(),\n file: z.string().min(1).describe('Binary file payload; supplied as multipart form-data'),\n})\n\nconst replaceBodySchema = uploadBodySchema.extend({\n attachmentId: z.string().uuid(),\n})\n\nconst deleteQuerySchema = z.object({\n attachmentId: z.string().uuid(),\n})\n\ntype PortalContext = {\n auth: CustomerAuthContext\n customerId: string\n tenantId: string\n organizationId: string\n em: EntityManager\n container: Awaited<ReturnType<typeof createRequestContainer>>\n}\n\nfunction toIso(value: Date | string | null | undefined): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString()\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toISOString()\n}\n\nasync function resolvePortalContext(req: Request): Promise<PortalContext | Response> {\n const authOrResponse = await resolveLinkedCustomerAuth(req)\n if (authOrResponse instanceof Response) return authOrResponse\n const auth = authOrResponse\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n return {\n auth,\n customerId: auth.customerEntityId,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n em,\n container,\n }\n}\n\nfunction attachmentAuth(context: PortalContext): NonNullable<AuthContext> {\n return {\n sub: context.auth.sub,\n tenantId: context.tenantId,\n orgId: context.organizationId,\n email: context.auth.email,\n }\n}\n\nasync function loadOwnedClaim(context: PortalContext, claimId: string): Promise<WarrantyClaim | null> {\n return loadPortalOwnedClaim(\n context.em,\n {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n customerId: context.customerId,\n },\n claimId,\n )\n}\n\nasync function runPortalAttachmentGuard(\n req: Request,\n context: PortalContext,\n claimId: string,\n operation: 'create' | 'update' | 'delete',\n mutationPayload: Record<string, unknown>,\n): Promise<RouteMutationGuardResult> {\n return runRouteMutationGuards({\n container: context.container,\n req,\n auth: {\n userId: context.auth.sub,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userFeatures: [],\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: claimId,\n operation,\n mutationPayload,\n },\n })\n}\n\nfunction serializeAttachment(attachment: Attachment) {\n const metadata = readAttachmentMetadata(attachment.storageMetadata)\n return {\n id: attachment.id,\n url: attachment.url,\n downloadUrl: `/api/warranty_claims/portal/attachments?attachmentId=${encodeURIComponent(attachment.id)}`,\n fileName: attachment.fileName,\n fileSize: attachment.fileSize,\n mimeType: attachment.mimeType ?? null,\n partitionCode: attachment.partitionCode,\n content: attachment.content ?? null,\n thumbnailUrl: buildAttachmentImageUrl(attachment.id, {\n width: 320,\n height: 320,\n slug: slugifyAttachmentFileName(attachment.fileName),\n }),\n tags: metadata.tags ?? [],\n assignments: metadata.assignments ?? [],\n createdAt: toIso(attachment.createdAt),\n }\n}\n\nasync function resolveStorageDriverFactory(context: PortalContext): Promise<StorageDriverFactory> {\n try {\n const resolved = context.container.resolve('storageDriverFactory') as StorageDriverFactory | null\n return resolved ?? new StorageDriverFactory(context.em)\n } catch {\n return new StorageDriverFactory(context.em)\n }\n}\n\nasync function loadOwnedVisibleAttachment(\n context: PortalContext,\n attachmentId: string,\n): Promise<{ attachment: Attachment; claim: WarrantyClaim } | null> {\n const scope = { tenantId: context.tenantId, organizationId: context.organizationId }\n const attachment = await findOneWithDecryption(\n context.em,\n Attachment,\n {\n id: attachmentId,\n entityId: CLAIM_ATTACHMENT_ENTITY_ID,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n {},\n scope,\n )\n if (!attachment) return null\n const claim = await loadOwnedClaim(context, attachment.recordId)\n if (!claim) return null\n if (!isCustomerVisibleAttachment(readAttachmentMetadata(attachment.storageMetadata).tags)) return null\n return { attachment, claim }\n}\n\nasync function deletePortalAttachment(context: PortalContext, attachment: Attachment): Promise<void> {\n await context.em.remove(attachment).flush()\n await clearAttachmentThumbnailCache(attachment.partitionCode, attachment.id).catch((error) => {\n logger.error('Failed to clean portal attachment thumbnails', { err: error, attachmentId: attachment.id })\n })\n if (attachment.storagePath) {\n try {\n const driver = await (await resolveStorageDriverFactory(context)).resolveForPartition(attachment.partitionCode, {\n tenantId: attachment.tenantId ?? context.tenantId,\n organizationId: attachment.organizationId ?? context.organizationId,\n })\n await driver.delete(attachment.partitionCode, attachment.storagePath)\n } catch (error) {\n logger.error('Failed to remove portal attachment storage', { err: error, attachmentId: attachment.id })\n }\n }\n\n let dataEngine: DataEngine | null = null\n try {\n dataEngine = context.container.resolve<DataEngine>('dataEngine') ?? null\n } catch {\n dataEngine = null\n }\n if (dataEngine) {\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: attachment,\n identifiers: {\n id: attachment.id,\n organizationId: attachment.organizationId ?? null,\n tenantId: attachment.tenantId ?? null,\n },\n events: attachmentCrudEvents,\n indexer: attachmentCrudIndexer,\n })\n await dataEngine.flushOrmEntityChanges()\n }\n}\n\nasync function streamOwnedAttachment(\n context: PortalContext,\n attachmentId: string,\n translate: (key: string, fallback?: string) => string,\n): Promise<Response> {\n const owned = await loadOwnedVisibleAttachment(context, attachmentId)\n if (!owned) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.notFound', 'Claim not found.') }, { status: 404 })\n }\n const { attachment } = owned\n const partition = await context.em.findOne(AttachmentPartition, { code: attachment.partitionCode })\n if (!partition) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.load_failed', 'Failed to load warranty claim data.') }, { status: 500 })\n }\n const access = checkAttachmentAccess(attachmentAuth(context), attachment, partition, { requireAuthForPublic: true })\n if (!access.ok) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.notFound', 'Claim not found.') }, { status: 404 })\n }\n const driver = await (await resolveStorageDriverFactory(context)).resolveForPartition(attachment.partitionCode, {\n tenantId: attachment.tenantId ?? '',\n organizationId: attachment.organizationId ?? '',\n })\n let buffer: Buffer\n try {\n buffer = (await driver.read(attachment.partitionCode, attachment.storagePath)).buffer\n } catch {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.notFound', 'Claim not found.') }, { status: 404 })\n }\n const headers: Record<string, string> = {\n 'Cache-Control': 'private, max-age=60',\n 'Content-Security-Policy': \"default-src 'none'; sandbox\",\n 'Content-Type': 'application/octet-stream',\n 'Content-Disposition': `attachment; filename=\"${slugifyAttachmentFileName(attachment.fileName)}\"`,\n 'X-Content-Type-Options': 'nosniff',\n }\n if (attachment.fileSize > 0) headers['Content-Length'] = String(attachment.fileSize)\n return new NextResponse(new Uint8Array(buffer), { status: 200, headers })\n}\n\nexport async function GET(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const { translate } = await resolveTranslations()\n const url = new URL(req.url)\n const attachmentId = url.searchParams.get('attachmentId')\n if (attachmentId) {\n const attachmentIdParsed = z.string().uuid().safeParse(attachmentId)\n if (!attachmentIdParsed.success) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n return streamOwnedAttachment(context, attachmentIdParsed.data, translate)\n }\n const parsed = attachmentQuerySchema.safeParse({\n claimId: url.searchParams.get('claimId') ?? undefined,\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n })\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const claim = await loadOwnedClaim(context, parsed.data.claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.notFound', 'Claim not found.') }, { status: 404 })\n }\n const filter = {\n entityId: CLAIM_ATTACHMENT_ENTITY_ID,\n recordId: claim.id,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n }\n const page = parsed.data.page ?? 1\n const pageSize = parsed.data.pageSize ?? 100\n const attachments = await findWithDecryption(\n context.em,\n Attachment,\n filter,\n { orderBy: { createdAt: 'DESC' } },\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n const partitionCodes = Array.from(new Set(attachments.map((attachment) => attachment.partitionCode)))\n const partitions = partitionCodes.length\n ? await context.em.find(AttachmentPartition, { code: { $in: partitionCodes } })\n : []\n const partitionsByCode = new Map(partitions.map((partition) => [partition.code, partition]))\n const auth = attachmentAuth(context)\n const visible = attachments.filter((attachment) => {\n const partition = partitionsByCode.get(attachment.partitionCode)\n if (!partition) return false\n if (!isCustomerVisibleAttachment(readAttachmentMetadata(attachment.storageMetadata).tags)) return false\n return checkAttachmentAccess(auth, attachment, partition, { requireAuthForPublic: true }).ok\n })\n\n const pagedVisible = visible.slice((page - 1) * pageSize, page * pageSize)\n\n return NextResponse.json({\n items: pagedVisible.map(serializeAttachment),\n total: visible.length,\n page,\n pageSize,\n totalPages: Math.max(1, Math.ceil(visible.length / pageSize)),\n })\n}\n\nasync function handleUpload(req: Request, replacement: boolean): Promise<Response> {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const { t, translate } = await resolveTranslations()\n const contentType = req.headers.get('content-type') || ''\n if (!contentType.toLowerCase().includes('multipart/form-data')) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n if (!isMultipartRequestWithinUploadLimit(req.headers.get('content-length'))) {\n return NextResponse.json({\n ok: false,\n error: t('attachments.errors.maxUploadSize', 'Attachment exceeds the maximum upload size.'),\n }, { status: 413 })\n }\n\n const form = await req.formData()\n const parsed = uploadBodySchema.safeParse({\n claimId: form.get('claimId') ?? undefined,\n file: form.get('file') instanceof File ? (form.get('file') as File).name : '',\n })\n const parsedAttachmentId = replacement\n ? z.string().uuid().safeParse(form.get('attachmentId') ?? undefined)\n : null\n if (!parsed.success || (replacement && !parsedAttachmentId?.success)) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const claim = await loadOwnedClaim(context, parsed.data.claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.notFound', 'Claim not found.') }, { status: 404 })\n }\n const file = form.get('file')\n if (!(file instanceof File)) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.portal.detail.fileRequired', 'Choose a file before uploading.') }, { status: 400 })\n }\n const buffer = Buffer.from(await file.arrayBuffer())\n\n const attachmentId = replacement && parsedAttachmentId?.success ? parsedAttachmentId.data : null\n const replacementTarget = attachmentId\n ? await loadOwnedVisibleAttachment(context, attachmentId)\n : null\n if (replacement && (!replacementTarget || replacementTarget.claim.id !== claim.id)) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.notFound', 'Claim not found.') }, { status: 404 })\n }\n\n const guarded = await runPortalAttachmentGuard(req, context, claim.id, replacement ? 'update' : 'create', {\n claimId: claim.id,\n attachmentId,\n fileName: file.name,\n fileSize: file.size,\n mimeType: file.type,\n })\n if (!guarded.ok) {\n return guarded.response\n }\n\n const uploadService = resolvePortalAttachmentUploadService(context.container)\n if (!uploadService) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.portal.detail.attachmentError', 'Attachment upload failed.') }, { status: 503 })\n }\n\n let attachment: Attachment\n try {\n attachment = await uploadService.upload({\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n entityId: CLAIM_ATTACHMENT_ENTITY_ID,\n recordId: claim.id,\n fileName: file.name,\n buffer,\n declaredMimeType: file.type,\n tags: [CUSTOMER_VISIBLE_ATTACHMENT_TAG],\n })\n } catch (error) {\n if (isScopedAttachmentUploadError(error)) {\n const [key, fallback] = ATTACHMENT_ERROR_TRANSLATIONS[error.code]\n ?? ['warranty_claims.portal.detail.attachmentError', 'Attachment upload failed.']\n return NextResponse.json(\n { ok: false, error: t(key, fallback), code: error.code },\n { status: error.status },\n )\n }\n throw error\n }\n\n if (replacementTarget) {\n await deletePortalAttachment(context, replacementTarget.attachment)\n }\n await guarded.runAfterSuccess()\n\n return NextResponse.json({\n ok: true,\n item: {\n ...serializeAttachment(attachment),\n entityType: 'attachments:attachment',\n },\n })\n}\n\nexport async function POST(req: Request) {\n return handleUpload(req, false)\n}\n\nexport async function PUT(req: Request) {\n return handleUpload(req, true)\n}\n\nexport async function DELETE(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const { translate } = await resolveTranslations()\n const url = new URL(req.url)\n const parsed = deleteQuerySchema.safeParse({\n attachmentId: url.searchParams.get('attachmentId') ?? undefined,\n })\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const owned = await loadOwnedVisibleAttachment(context, parsed.data.attachmentId)\n if (!owned) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.notFound', 'Claim not found.') }, { status: 404 })\n }\n const guarded = await runPortalAttachmentGuard(req, context, owned.claim.id, 'delete', {\n claimId: owned.claim.id,\n attachmentId: owned.attachment.id,\n })\n if (!guarded.ok) return guarded.response\n\n await deletePortalAttachment(context, owned.attachment)\n await guarded.runAfterSuccess()\n return NextResponse.json({ ok: true })\n}\n\nconst assignmentSchema = z.object({\n type: z.string(),\n id: z.string(),\n href: z.string().nullable().optional(),\n label: z.string().nullable().optional(),\n})\n\nconst attachmentItemSchema = z.object({\n id: z.string(),\n url: z.string(),\n downloadUrl: z.string(),\n fileName: z.string(),\n fileSize: z.number().int().nonnegative(),\n mimeType: z.string().nullable(),\n partitionCode: z.string(),\n content: z.string().nullable(),\n thumbnailUrl: z.string(),\n tags: z.array(z.string()),\n assignments: z.array(assignmentSchema),\n createdAt: z.string().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims Portal',\n summary: 'Customer portal warranty claim attachments',\n methods: {\n GET: {\n summary: 'List attachments for an owned claim',\n query: attachmentQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Claim attachments',\n schema: z.object({ items: z.array(attachmentItemSchema) }),\n },\n ],\n },\n POST: {\n summary: 'Upload an attachment for an owned claim',\n requestBody: { contentType: 'multipart/form-data', schema: uploadBodySchema },\n responses: [\n {\n status: 200,\n description: 'Attachment uploaded',\n schema: z.object({ ok: z.boolean(), item: attachmentItemSchema.extend({ entityType: z.string() }) }),\n },\n ],\n },\n PUT: {\n summary: 'Replace an attachment on an owned claim',\n requestBody: { contentType: 'multipart/form-data', schema: replaceBodySchema },\n responses: [\n {\n status: 200,\n description: 'Attachment replaced',\n schema: z.object({ ok: z.boolean(), item: attachmentItemSchema.extend({ entityType: z.string() }) }),\n },\n ],\n },\n DELETE: {\n summary: 'Delete an attachment from an owned claim',\n query: deleteQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Attachment deleted',\n schema: z.object({ ok: z.literal(true) }),\n },\n ],\n },\n },\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import { NextResponse } from 'next/server'\nimport { z } from 'zod'\nimport type { EntityManager } from '@mikro-orm/postgresql'\nimport type { AuthContext } from '@open-mercato/shared/lib/auth/server'\nimport { createRequestContainer } from '@open-mercato/shared/lib/di/container'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { runRouteMutationGuards, type RouteMutationGuardResult } from '@open-mercato/shared/lib/crud/route-mutation-guard'\nimport { emitCrudSideEffects } from '@open-mercato/shared/lib/commands/helpers'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { createLogger } from '@open-mercato/shared/lib/logger'\nimport type { CustomerAuthContext } from '@open-mercato/core/modules/customer_accounts/lib/customerAuth'\nimport { Attachment, AttachmentPartition } from '@open-mercato/core/modules/attachments/data/entities'\nimport { buildAttachmentImageUrl, slugifyAttachmentFileName } from '@open-mercato/core/modules/attachments/lib/imageUrls'\nimport { StorageDriverFactory } from '@open-mercato/core/modules/attachments/lib/drivers'\nimport { checkAttachmentAccess } from '@open-mercato/core/modules/attachments/lib/access'\nimport { attachmentCrudEvents, attachmentCrudIndexer } from '@open-mercato/core/modules/attachments/lib/crud'\nimport { readAttachmentMetadata } from '@open-mercato/core/modules/attachments/lib/metadata'\nimport { clearAttachmentThumbnailCache } from '@open-mercato/core/modules/attachments/lib/thumbnailCache'\nimport {\n isMultipartUploadLimitError,\n parseMultipartFormDataWithinUploadLimit,\n resolveDefaultAttachmentMaxUploadBytes,\n} from '@open-mercato/core/modules/attachments/lib/upload-limits'\nimport {\n isScopedAttachmentUploadError,\n type ScopedAttachmentUploadErrorCode,\n} from '@open-mercato/core/modules/attachments/lib/scoped-upload-service'\nimport type { OpenApiRouteDoc } from '@open-mercato/shared/lib/openapi'\nimport { WarrantyClaim } from '../../../data/entities'\nimport { WARRANTY_CLAIM_RESOURCE_KIND } from '../../../commands/shared'\nimport { CUSTOMER_VISIBLE_ATTACHMENT_TAG, isCustomerVisibleAttachment } from '../../../lib/attachmentVisibility'\nimport { loadPortalOwnedClaim } from '../../../lib/portalClaimAccess'\nimport { resolvePortalAttachmentUploadService } from '../../../lib/portalAttachmentUpload'\nimport { resolveLinkedCustomerAuth } from '../../../lib/portalAuthGuard'\n\nconst CLAIM_ATTACHMENT_ENTITY_ID = 'warranty_claims:warranty_claim'\nconst logger = createLogger('warranty_claims').child({ route: 'portal-attachments' })\nconst ATTACHMENT_ERROR_TRANSLATIONS: Partial<Record<ScopedAttachmentUploadErrorCode, readonly [string, string]>> = {\n dangerous_executable: ['attachments.errors.dangerousExecutable', 'Executable file types are not allowed as attachments.'],\n max_upload_size: ['attachments.errors.maxUploadSize', 'Attachment exceeds the maximum upload size.'],\n active_content: ['attachments.errors.activeContentBlocked', 'Active content uploads are not allowed.'],\n}\n\nexport const metadata = {\n GET: { requireAuth: false },\n POST: { requireAuth: false },\n PUT: { requireAuth: false },\n DELETE: { requireAuth: false },\n}\n\nconst attachmentQuerySchema = z.object({\n claimId: z.string().uuid(),\n page: z.coerce.number().int().min(1).optional(),\n pageSize: z.coerce.number().int().min(1).max(100).optional(),\n})\n\nconst uploadBodySchema = z.object({\n claimId: z.string().uuid(),\n file: z.string().min(1).describe('Binary file payload; supplied as multipart form-data'),\n})\n\nconst replaceBodySchema = uploadBodySchema.extend({\n attachmentId: z.string().uuid(),\n})\n\nconst deleteQuerySchema = z.object({\n attachmentId: z.string().uuid(),\n})\n\ntype PortalContext = {\n auth: CustomerAuthContext\n customerId: string\n tenantId: string\n organizationId: string\n em: EntityManager\n container: Awaited<ReturnType<typeof createRequestContainer>>\n}\n\nfunction toIso(value: Date | string | null | undefined): string | null {\n if (!value) return null\n if (value instanceof Date) return value.toISOString()\n const date = new Date(value)\n return Number.isNaN(date.getTime()) ? value : date.toISOString()\n}\n\nasync function resolvePortalContext(req: Request): Promise<PortalContext | Response> {\n const authOrResponse = await resolveLinkedCustomerAuth(req)\n if (authOrResponse instanceof Response) return authOrResponse\n const auth = authOrResponse\n const container = await createRequestContainer()\n const em = container.resolve('em') as EntityManager\n return {\n auth,\n customerId: auth.customerEntityId,\n tenantId: auth.tenantId,\n organizationId: auth.orgId,\n em,\n container,\n }\n}\n\nfunction attachmentAuth(context: PortalContext): NonNullable<AuthContext> {\n return {\n sub: context.auth.sub,\n tenantId: context.tenantId,\n orgId: context.organizationId,\n email: context.auth.email,\n }\n}\n\nasync function loadOwnedClaim(context: PortalContext, claimId: string): Promise<WarrantyClaim | null> {\n return loadPortalOwnedClaim(\n context.em,\n {\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n customerId: context.customerId,\n },\n claimId,\n )\n}\n\nasync function runPortalAttachmentGuard(\n req: Request,\n context: PortalContext,\n claimId: string,\n operation: 'create' | 'update' | 'delete',\n mutationPayload: Record<string, unknown>,\n): Promise<RouteMutationGuardResult> {\n return runRouteMutationGuards({\n container: context.container,\n req,\n auth: {\n userId: context.auth.sub,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n userFeatures: [],\n },\n input: {\n resourceKind: WARRANTY_CLAIM_RESOURCE_KIND,\n resourceId: claimId,\n operation,\n mutationPayload,\n },\n })\n}\n\nfunction serializeAttachment(attachment: Attachment) {\n const metadata = readAttachmentMetadata(attachment.storageMetadata)\n return {\n id: attachment.id,\n url: attachment.url,\n downloadUrl: `/api/warranty_claims/portal/attachments?attachmentId=${encodeURIComponent(attachment.id)}`,\n fileName: attachment.fileName,\n fileSize: attachment.fileSize,\n mimeType: attachment.mimeType ?? null,\n partitionCode: attachment.partitionCode,\n content: attachment.content ?? null,\n thumbnailUrl: buildAttachmentImageUrl(attachment.id, {\n width: 320,\n height: 320,\n slug: slugifyAttachmentFileName(attachment.fileName),\n }),\n tags: metadata.tags ?? [],\n assignments: metadata.assignments ?? [],\n createdAt: toIso(attachment.createdAt),\n }\n}\n\nasync function resolveStorageDriverFactory(context: PortalContext): Promise<StorageDriverFactory> {\n try {\n const resolved = context.container.resolve('storageDriverFactory') as StorageDriverFactory | null\n return resolved ?? new StorageDriverFactory(context.em)\n } catch {\n return new StorageDriverFactory(context.em)\n }\n}\n\nasync function loadOwnedVisibleAttachment(\n context: PortalContext,\n attachmentId: string,\n): Promise<{ attachment: Attachment; claim: WarrantyClaim } | null> {\n const scope = { tenantId: context.tenantId, organizationId: context.organizationId }\n const attachment = await findOneWithDecryption(\n context.em,\n Attachment,\n {\n id: attachmentId,\n entityId: CLAIM_ATTACHMENT_ENTITY_ID,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n },\n {},\n scope,\n )\n if (!attachment) return null\n const claim = await loadOwnedClaim(context, attachment.recordId)\n if (!claim) return null\n if (!isCustomerVisibleAttachment(readAttachmentMetadata(attachment.storageMetadata).tags)) return null\n return { attachment, claim }\n}\n\nasync function deletePortalAttachment(context: PortalContext, attachment: Attachment): Promise<void> {\n await context.em.remove(attachment).flush()\n await clearAttachmentThumbnailCache(attachment.partitionCode, attachment.id).catch((error) => {\n logger.error('Failed to clean portal attachment thumbnails', { err: error, attachmentId: attachment.id })\n })\n if (attachment.storagePath) {\n try {\n const driver = await (await resolveStorageDriverFactory(context)).resolveForPartition(attachment.partitionCode, {\n tenantId: attachment.tenantId ?? context.tenantId,\n organizationId: attachment.organizationId ?? context.organizationId,\n })\n await driver.delete(attachment.partitionCode, attachment.storagePath)\n } catch (error) {\n logger.error('Failed to remove portal attachment storage', { err: error, attachmentId: attachment.id })\n }\n }\n\n let dataEngine: DataEngine | null = null\n try {\n dataEngine = context.container.resolve<DataEngine>('dataEngine') ?? null\n } catch {\n dataEngine = null\n }\n if (dataEngine) {\n await emitCrudSideEffects({\n dataEngine,\n action: 'deleted',\n entity: attachment,\n identifiers: {\n id: attachment.id,\n organizationId: attachment.organizationId ?? null,\n tenantId: attachment.tenantId ?? null,\n },\n events: attachmentCrudEvents,\n indexer: attachmentCrudIndexer,\n })\n await dataEngine.flushOrmEntityChanges()\n }\n}\n\nasync function streamOwnedAttachment(\n context: PortalContext,\n attachmentId: string,\n translate: (key: string, fallback?: string) => string,\n): Promise<Response> {\n const owned = await loadOwnedVisibleAttachment(context, attachmentId)\n if (!owned) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.notFound', 'Claim not found.') }, { status: 404 })\n }\n const { attachment } = owned\n const partition = await context.em.findOne(AttachmentPartition, { code: attachment.partitionCode })\n if (!partition) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.load_failed', 'Failed to load warranty claim data.') }, { status: 500 })\n }\n const access = checkAttachmentAccess(attachmentAuth(context), attachment, partition, { requireAuthForPublic: true })\n if (!access.ok) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.notFound', 'Claim not found.') }, { status: 404 })\n }\n const driver = await (await resolveStorageDriverFactory(context)).resolveForPartition(attachment.partitionCode, {\n tenantId: attachment.tenantId ?? '',\n organizationId: attachment.organizationId ?? '',\n })\n let buffer: Buffer\n try {\n buffer = (await driver.read(attachment.partitionCode, attachment.storagePath)).buffer\n } catch {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.notFound', 'Claim not found.') }, { status: 404 })\n }\n const headers: Record<string, string> = {\n 'Cache-Control': 'private, max-age=60',\n 'Content-Security-Policy': \"default-src 'none'; sandbox\",\n 'Content-Type': 'application/octet-stream',\n 'Content-Disposition': `attachment; filename=\"${slugifyAttachmentFileName(attachment.fileName)}\"`,\n 'X-Content-Type-Options': 'nosniff',\n }\n if (attachment.fileSize > 0) headers['Content-Length'] = String(attachment.fileSize)\n return new NextResponse(new Uint8Array(buffer), { status: 200, headers })\n}\n\nexport async function GET(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const { translate } = await resolveTranslations()\n const url = new URL(req.url)\n const attachmentId = url.searchParams.get('attachmentId')\n if (attachmentId) {\n const attachmentIdParsed = z.string().uuid().safeParse(attachmentId)\n if (!attachmentIdParsed.success) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n return streamOwnedAttachment(context, attachmentIdParsed.data, translate)\n }\n const parsed = attachmentQuerySchema.safeParse({\n claimId: url.searchParams.get('claimId') ?? undefined,\n page: url.searchParams.get('page') ?? undefined,\n pageSize: url.searchParams.get('pageSize') ?? undefined,\n })\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const claim = await loadOwnedClaim(context, parsed.data.claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.notFound', 'Claim not found.') }, { status: 404 })\n }\n const filter = {\n entityId: CLAIM_ATTACHMENT_ENTITY_ID,\n recordId: claim.id,\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n }\n const page = parsed.data.page ?? 1\n const pageSize = parsed.data.pageSize ?? 100\n const attachments = await findWithDecryption(\n context.em,\n Attachment,\n filter,\n { orderBy: { createdAt: 'DESC' } },\n { tenantId: context.tenantId, organizationId: context.organizationId },\n )\n const partitionCodes = Array.from(new Set(attachments.map((attachment) => attachment.partitionCode)))\n const partitions = partitionCodes.length\n ? await context.em.find(AttachmentPartition, { code: { $in: partitionCodes } })\n : []\n const partitionsByCode = new Map(partitions.map((partition) => [partition.code, partition]))\n const auth = attachmentAuth(context)\n const visible = attachments.filter((attachment) => {\n const partition = partitionsByCode.get(attachment.partitionCode)\n if (!partition) return false\n if (!isCustomerVisibleAttachment(readAttachmentMetadata(attachment.storageMetadata).tags)) return false\n return checkAttachmentAccess(auth, attachment, partition, { requireAuthForPublic: true }).ok\n })\n\n const pagedVisible = visible.slice((page - 1) * pageSize, page * pageSize)\n\n return NextResponse.json({\n items: pagedVisible.map(serializeAttachment),\n total: visible.length,\n page,\n pageSize,\n totalPages: Math.max(1, Math.ceil(visible.length / pageSize)),\n })\n}\n\nfunction uploadTooLargeResponse(t: Awaited<ReturnType<typeof resolveTranslations>>['t']): Response {\n return NextResponse.json({\n ok: false,\n error: t('attachments.errors.maxUploadSize', 'Attachment exceeds the maximum upload size.'),\n code: 'max_upload_size',\n }, { status: 413 })\n}\n\nasync function handleUpload(req: Request, replacement: boolean): Promise<Response> {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const { t, translate } = await resolveTranslations()\n const contentType = req.headers.get('content-type') || ''\n if (!contentType.toLowerCase().includes('multipart/form-data')) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n let form: FormData\n try {\n form = await parseMultipartFormDataWithinUploadLimit(req)\n } catch (error) {\n if (isMultipartUploadLimitError(error)) return uploadTooLargeResponse(t)\n throw error\n }\n const parsed = uploadBodySchema.safeParse({\n claimId: form.get('claimId') ?? undefined,\n file: form.get('file') instanceof File ? (form.get('file') as File).name : '',\n })\n const parsedAttachmentId = replacement\n ? z.string().uuid().safeParse(form.get('attachmentId') ?? undefined)\n : null\n if (!parsed.success || (replacement && !parsedAttachmentId?.success)) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const claim = await loadOwnedClaim(context, parsed.data.claimId)\n if (!claim) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.notFound', 'Claim not found.') }, { status: 404 })\n }\n const file = form.get('file')\n if (!(file instanceof File)) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.portal.detail.fileRequired', 'Choose a file before uploading.') }, { status: 400 })\n }\n if (file.size > resolveDefaultAttachmentMaxUploadBytes()) return uploadTooLargeResponse(t)\n const buffer = Buffer.from(await file.arrayBuffer())\n\n const attachmentId = replacement && parsedAttachmentId?.success ? parsedAttachmentId.data : null\n const replacementTarget = attachmentId\n ? await loadOwnedVisibleAttachment(context, attachmentId)\n : null\n if (replacement && (!replacementTarget || replacementTarget.claim.id !== claim.id)) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.notFound', 'Claim not found.') }, { status: 404 })\n }\n\n const guarded = await runPortalAttachmentGuard(req, context, claim.id, replacement ? 'update' : 'create', {\n claimId: claim.id,\n attachmentId,\n fileName: file.name,\n fileSize: file.size,\n mimeType: file.type,\n })\n if (!guarded.ok) {\n return guarded.response\n }\n\n const uploadService = resolvePortalAttachmentUploadService(context.container)\n if (!uploadService) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.portal.detail.attachmentError', 'Attachment upload failed.') }, { status: 503 })\n }\n\n let attachment: Attachment\n try {\n attachment = await uploadService.upload({\n tenantId: context.tenantId,\n organizationId: context.organizationId,\n entityId: CLAIM_ATTACHMENT_ENTITY_ID,\n recordId: claim.id,\n fileName: file.name,\n buffer,\n declaredMimeType: file.type,\n tags: [CUSTOMER_VISIBLE_ATTACHMENT_TAG],\n })\n } catch (error) {\n if (isScopedAttachmentUploadError(error)) {\n const [key, fallback] = ATTACHMENT_ERROR_TRANSLATIONS[error.code]\n ?? ['warranty_claims.portal.detail.attachmentError', 'Attachment upload failed.']\n return NextResponse.json(\n { ok: false, error: t(key, fallback), code: error.code },\n { status: error.status },\n )\n }\n throw error\n }\n\n if (replacementTarget) {\n await deletePortalAttachment(context, replacementTarget.attachment)\n }\n await guarded.runAfterSuccess()\n\n return NextResponse.json({\n ok: true,\n item: {\n ...serializeAttachment(attachment),\n entityType: 'attachments:attachment',\n },\n })\n}\n\nexport async function POST(req: Request) {\n return handleUpload(req, false)\n}\n\nexport async function PUT(req: Request) {\n return handleUpload(req, true)\n}\n\nexport async function DELETE(req: Request) {\n const contextOrResponse = await resolvePortalContext(req)\n if (contextOrResponse instanceof Response) return contextOrResponse\n const context = contextOrResponse\n const { translate } = await resolveTranslations()\n const url = new URL(req.url)\n const parsed = deleteQuerySchema.safeParse({\n attachmentId: url.searchParams.get('attachmentId') ?? undefined,\n })\n if (!parsed.success) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.invalidInput', 'Invalid input') }, { status: 400 })\n }\n const owned = await loadOwnedVisibleAttachment(context, parsed.data.attachmentId)\n if (!owned) {\n return NextResponse.json({ ok: false, error: translate('warranty_claims.errors.notFound', 'Claim not found.') }, { status: 404 })\n }\n const guarded = await runPortalAttachmentGuard(req, context, owned.claim.id, 'delete', {\n claimId: owned.claim.id,\n attachmentId: owned.attachment.id,\n })\n if (!guarded.ok) return guarded.response\n\n await deletePortalAttachment(context, owned.attachment)\n await guarded.runAfterSuccess()\n return NextResponse.json({ ok: true })\n}\n\nconst assignmentSchema = z.object({\n type: z.string(),\n id: z.string(),\n href: z.string().nullable().optional(),\n label: z.string().nullable().optional(),\n})\n\nconst attachmentItemSchema = z.object({\n id: z.string(),\n url: z.string(),\n downloadUrl: z.string(),\n fileName: z.string(),\n fileSize: z.number().int().nonnegative(),\n mimeType: z.string().nullable(),\n partitionCode: z.string(),\n content: z.string().nullable(),\n thumbnailUrl: z.string(),\n tags: z.array(z.string()),\n assignments: z.array(assignmentSchema),\n createdAt: z.string().nullable(),\n})\n\nexport const openApi: OpenApiRouteDoc = {\n tag: 'Warranty Claims Portal',\n summary: 'Customer portal warranty claim attachments',\n methods: {\n GET: {\n summary: 'List attachments for an owned claim',\n query: attachmentQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Claim attachments',\n schema: z.object({ items: z.array(attachmentItemSchema) }),\n },\n ],\n },\n POST: {\n summary: 'Upload an attachment for an owned claim',\n requestBody: { contentType: 'multipart/form-data', schema: uploadBodySchema },\n responses: [\n {\n status: 200,\n description: 'Attachment uploaded',\n schema: z.object({ ok: z.boolean(), item: attachmentItemSchema.extend({ entityType: z.string() }) }),\n },\n ],\n },\n PUT: {\n summary: 'Replace an attachment on an owned claim',\n requestBody: { contentType: 'multipart/form-data', schema: replaceBodySchema },\n responses: [\n {\n status: 200,\n description: 'Attachment replaced',\n schema: z.object({ ok: z.boolean(), item: attachmentItemSchema.extend({ entityType: z.string() }) }),\n },\n ],\n },\n DELETE: {\n summary: 'Delete an attachment from an owned claim',\n query: deleteQuerySchema,\n responses: [\n {\n status: 200,\n description: 'Attachment deleted',\n schema: z.object({ ok: z.literal(true) }),\n },\n ],\n },\n },\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["metadata"]
7
7
  }