@open-mercato/core 0.8.1-develop.7458.1.3847638f67 → 0.9.1-develop.7459.1.99e3accd30

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 (109) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/dist/modules/availability/api/policies/route.js +6 -6
  3. package/dist/modules/availability/api/policies/route.js.map +2 -2
  4. package/dist/modules/availability/backend/availability/check/page.js +4 -3
  5. package/dist/modules/availability/backend/availability/check/page.js.map +2 -2
  6. package/dist/modules/availability/backend/availability/policies/create/page.js +3 -0
  7. package/dist/modules/availability/backend/availability/policies/create/page.js.map +2 -2
  8. package/dist/modules/availability/lib/organizationScope.js +24 -1
  9. package/dist/modules/availability/lib/organizationScope.js.map +2 -2
  10. package/dist/modules/catalog/api/categories/bulk-create/route.js +16 -2
  11. package/dist/modules/catalog/api/categories/bulk-create/route.js.map +2 -2
  12. package/dist/modules/catalog/api/products/bulk-create/route.js +16 -2
  13. package/dist/modules/catalog/api/products/bulk-create/route.js.map +2 -2
  14. package/dist/modules/catalog/backend/catalog/products/[id]/page.js +1 -0
  15. package/dist/modules/catalog/backend/catalog/products/[id]/page.js.map +2 -2
  16. package/dist/modules/catalog/components/prices/PriceScopeSelectors.js +2 -2
  17. package/dist/modules/catalog/components/prices/PriceScopeSelectors.js.map +2 -2
  18. package/dist/modules/customer_accounts/api/admin/users/[id]/sessions/[sessionId].js +78 -0
  19. package/dist/modules/customer_accounts/api/admin/users/[id]/sessions/[sessionId].js.map +7 -0
  20. package/dist/modules/customer_groups/api/customer-groups/price-kinds/route.js +90 -0
  21. package/dist/modules/customer_groups/api/customer-groups/price-kinds/route.js.map +7 -0
  22. package/dist/modules/customer_groups/backend/customer-groups/page.js +5 -1
  23. package/dist/modules/customer_groups/backend/customer-groups/page.js.map +2 -2
  24. package/dist/modules/customer_groups/components/CustomerGroupTermsPriceKindField.js +2 -2
  25. package/dist/modules/customer_groups/components/CustomerGroupTermsPriceKindField.js.map +2 -2
  26. package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js +3 -1
  27. package/dist/modules/customer_groups/components/CustomerGroupTermsSection.js.map +2 -2
  28. package/dist/modules/customer_groups/lib/priceKindScope.js +35 -1
  29. package/dist/modules/customer_groups/lib/priceKindScope.js.map +2 -2
  30. package/dist/modules/customer_groups/widgets/injection/person-groups-explain-terms.js +1 -1
  31. package/dist/modules/customer_groups/widgets/injection/person-groups-explain-terms.js.map +2 -2
  32. package/dist/modules/customers/commands/companies.js +14 -1
  33. package/dist/modules/customers/commands/companies.js.map +2 -2
  34. package/dist/modules/dictionaries/components/DictionariesManager.js +98 -87
  35. package/dist/modules/dictionaries/components/DictionariesManager.js.map +2 -2
  36. package/dist/modules/forms/backend/forms/[id]/submissions/components/SubmissionDrawer.js +3 -3
  37. package/dist/modules/forms/backend/forms/[id]/submissions/components/SubmissionDrawer.js.map +2 -2
  38. package/dist/modules/forms/backend/forms/[id]/submissions/components/formSummary.js +9 -0
  39. package/dist/modules/forms/backend/forms/[id]/submissions/components/formSummary.js.map +7 -0
  40. package/dist/modules/forms/backend/forms/[id]/submissions/page.js +3 -6
  41. package/dist/modules/forms/backend/forms/[id]/submissions/page.js.map +2 -2
  42. package/dist/modules/forms/commands/distribution.js +7 -3
  43. package/dist/modules/forms/commands/distribution.js.map +2 -2
  44. package/dist/modules/forms/commands/form-version.js +9 -4
  45. package/dist/modules/forms/commands/form-version.js.map +2 -2
  46. package/dist/modules/forms/commands/form.js +9 -4
  47. package/dist/modules/forms/commands/form.js.map +2 -2
  48. package/dist/modules/forms/commands/invitation.js +7 -3
  49. package/dist/modules/forms/commands/invitation.js.map +2 -2
  50. package/dist/modules/forms/commands/submission.js +13 -6
  51. package/dist/modules/forms/commands/submission.js.map +2 -2
  52. package/package.json +8 -8
  53. package/src/modules/availability/api/policies/route.ts +10 -11
  54. package/src/modules/availability/backend/availability/check/page.tsx +38 -35
  55. package/src/modules/availability/backend/availability/policies/create/page.tsx +3 -0
  56. package/src/modules/availability/i18n/de.json +1 -0
  57. package/src/modules/availability/i18n/en.json +1 -0
  58. package/src/modules/availability/i18n/es.json +1 -0
  59. package/src/modules/availability/i18n/ko.json +1 -0
  60. package/src/modules/availability/i18n/pl.json +1 -0
  61. package/src/modules/availability/lib/organizationScope.ts +37 -0
  62. package/src/modules/catalog/api/categories/bulk-create/route.ts +16 -2
  63. package/src/modules/catalog/api/products/bulk-create/route.ts +16 -2
  64. package/src/modules/catalog/backend/catalog/products/[id]/page.tsx +1 -0
  65. package/src/modules/catalog/components/prices/PriceScopeSelectors.tsx +2 -2
  66. package/src/modules/customer_accounts/AGENTS.md +1 -0
  67. package/src/modules/customer_accounts/api/admin/users/[id]/sessions/[sessionId].ts +89 -0
  68. package/src/modules/customer_groups/api/customer-groups/price-kinds/route.ts +99 -0
  69. package/src/modules/customer_groups/backend/customer-groups/page.tsx +7 -1
  70. package/src/modules/customer_groups/components/CustomerGroupTermsPriceKindField.tsx +5 -4
  71. package/src/modules/customer_groups/components/CustomerGroupTermsSection.tsx +8 -4
  72. package/src/modules/customer_groups/lib/priceKindScope.ts +43 -0
  73. package/src/modules/customer_groups/widgets/injection/person-groups-explain-terms.tsx +2 -2
  74. package/src/modules/customers/commands/companies.ts +16 -1
  75. package/src/modules/dictionaries/components/DictionariesManager.tsx +8 -1
  76. package/src/modules/eudr/i18n/de.json +1 -0
  77. package/src/modules/eudr/i18n/en.json +1 -0
  78. package/src/modules/eudr/i18n/es.json +1 -0
  79. package/src/modules/eudr/i18n/ko.json +1 -0
  80. package/src/modules/eudr/i18n/pl.json +1 -0
  81. package/src/modules/forms/backend/forms/[id]/submissions/components/SubmissionDrawer.tsx +6 -5
  82. package/src/modules/forms/backend/forms/[id]/submissions/components/formSummary.ts +19 -0
  83. package/src/modules/forms/backend/forms/[id]/submissions/page.tsx +4 -16
  84. package/src/modules/forms/commands/distribution.ts +7 -3
  85. package/src/modules/forms/commands/form-version.ts +9 -4
  86. package/src/modules/forms/commands/form.ts +9 -4
  87. package/src/modules/forms/commands/invitation.ts +7 -3
  88. package/src/modules/forms/commands/submission.ts +13 -6
  89. package/src/modules/forms/i18n/de.json +21 -0
  90. package/src/modules/forms/i18n/en.json +21 -0
  91. package/src/modules/forms/i18n/es.json +21 -0
  92. package/src/modules/forms/i18n/ko.json +21 -0
  93. package/src/modules/forms/i18n/pl.json +21 -0
  94. package/src/modules/notifications/AGENTS.md +1 -1
  95. package/src/modules/push_notifications/i18n/de.json +1 -0
  96. package/src/modules/push_notifications/i18n/en.json +1 -0
  97. package/src/modules/push_notifications/i18n/es.json +1 -0
  98. package/src/modules/push_notifications/i18n/ko.json +1 -0
  99. package/src/modules/push_notifications/i18n/pl.json +1 -0
  100. package/src/modules/warranty_claims/i18n/de.json +1 -0
  101. package/src/modules/warranty_claims/i18n/en.json +1 -0
  102. package/src/modules/warranty_claims/i18n/es.json +1 -0
  103. package/src/modules/warranty_claims/i18n/ko.json +1 -0
  104. package/src/modules/warranty_claims/i18n/pl.json +1 -0
  105. package/src/modules/wms/i18n/de.json +1 -0
  106. package/src/modules/wms/i18n/en.json +1 -0
  107. package/src/modules/wms/i18n/es.json +1 -0
  108. package/src/modules/wms/i18n/ko.json +1 -0
  109. package/src/modules/wms/i18n/pl.json +1 -0
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/forms/commands/form-version.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { Form, FormVersion } from '../data/entities'\nimport {\n formVersionArchiveCommandSchema,\n formVersionForkDraftCommandSchema,\n formVersionPublishCommandSchema,\n formVersionUpdateDraftCommandSchema,\n type FormVersionArchiveCommandInput,\n type FormVersionForkDraftCommandInput,\n type FormVersionPublishCommandInput,\n type FormVersionUpdateDraftCommandInput,\n} from '../data/validators'\nimport {\n FORMS_CACHE_TAGS,\n FORM_VERSION_RESOURCE_KIND,\n emitForms,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n findFormInScope,\n findFormVersionInScope,\n invalidateFormsCacheTags,\n resolveActorUserId,\n resolveEntityManager,\n serializeFormVersionSnapshot,\n type FormVersionSnapshot,\n} from './shared'\nimport type { FormVersionCompiler } from '../services/form-version-compiler'\nimport type { FieldTypeRegistry } from '../schema/field-type-registry'\nimport { FormCompilationError, isFormCompilationError } from '../services/form-version-compiler'\n\n// ----------------------------------------------------------------------------\n// Undo payloads\n// ----------------------------------------------------------------------------\n\ntype FormVersionUndoPayload = {\n before?: FormVersionSnapshot | null\n after?: FormVersionSnapshot | null\n /**\n * Captured at publish time so undo can restore the prior pointer cleanly.\n */\n previousCurrentPublishedVersionId?: string | null\n /** Captured at publish time so undo of publish can route through the right inverse. */\n publishInverseKind?: 'demote_to_draft' | 'archive'\n}\n\n// ----------------------------------------------------------------------------\n// Helpers\n// ----------------------------------------------------------------------------\n\nfunction resolveCompiler(ctx: { container: { resolve: <T>(name: string) => T } }): FormVersionCompiler {\n return ctx.container.resolve<FormVersionCompiler>('formVersionCompiler')\n}\n\nfunction resolveRegistry(ctx: { container: { resolve: <T>(name: string) => T } }): FieldTypeRegistry {\n return ctx.container.resolve<FieldTypeRegistry>('fieldTypeRegistry')\n}\n\nconst EMPTY_SCHEMA: Record<string, unknown> = {\n type: 'object',\n properties: {},\n required: [],\n 'x-om-roles': [],\n 'x-om-sections': [],\n}\n\nfunction deepClone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T\n}\n\nfunction arraysEqual<T>(a: T[], b: T[]): boolean {\n if (a.length !== b.length) return false\n return a.every((value, index) => value === b[index])\n}\n\n/**\n * The `roles` column mirrors the schema's `x-om-roles` (the studio edits the\n * latter). Keeping them in sync prevents the runtime declared-role check from\n * drifting from what the form actually declares. `admin` is always present.\n */\nfunction deriveRolesFromSchema(schema: Record<string, unknown>): string[] {\n const raw = schema['x-om-roles']\n const roles = Array.isArray(raw) ? raw.filter((entry): entry is string => typeof entry === 'string') : []\n return Array.from(new Set([...roles, 'admin']))\n}\n\n/**\n * Best-effort check for whether any submission references the given version.\n * Phase 1b runs before submission entities exist (1c), so the table may be\n * absent entirely. In that case there are clearly no submissions and we\n * return false. Once 1c lands, the same SQL works against the new table.\n */\nasync function hasSubmissionsForVersion(\n em: EntityManager,\n versionId: string,\n): Promise<boolean> {\n try {\n const db = (em as unknown as { getKysely: <T = unknown>() => unknown }).getKysely<unknown>() as unknown as {\n selectFrom: (name: string) => any\n }\n const tableExists = (await db\n .selectFrom('information_schema.tables')\n .select(['table_name'])\n .where('table_schema', '=', 'public')\n .where('table_name', '=', 'forms_form_submission')\n .execute()) as Array<{ table_name: string }>\n if (!tableExists.length) return false\n const rows = (await db\n .selectFrom('forms_form_submission')\n .select(['id'])\n .where('form_version_id', '=', versionId)\n .limit(1)\n .execute()) as Array<{ id: string }>\n return rows.length > 0\n } catch {\n // If the introspection query fails for any reason, fall back to the\n // safe assumption: treat as no submissions yet \u2014 the immutable\n // form_version row itself is still a viable inverse via archive.\n return false\n }\n}\n\nasync function compileOrThrowValidation(\n compiler: FormVersionCompiler,\n version: FormVersion,\n): Promise<void> {\n try {\n compiler.compile({\n id: version.id,\n updatedAt: version.updatedAt,\n schema: version.schema,\n uiSchema: version.uiSchema,\n })\n } catch (error) {\n if (isFormCompilationError(error)) {\n throw new CrudHttpError(422, {\n error: 'forms.errors.schema_invalid',\n code: error.code,\n path: error.path,\n message: error.message,\n })\n }\n throw error\n }\n}\n\n// ----------------------------------------------------------------------------\n// forms.form_version.fork_draft\n// ----------------------------------------------------------------------------\n\nconst forkDraftCommand: CommandHandler<FormVersionForkDraftCommandInput, { versionId: string }> = {\n id: 'forms.form_version.fork_draft',\n async execute(rawInput, ctx) {\n const parsed = formVersionForkDraftCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const form = await findFormInScope(em, parsed.formId, parsed.tenantId, parsed.organizationId)\n\n const existingDraft = await em.findOne(FormVersion, {\n formId: form.id,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n status: 'draft',\n })\n if (existingDraft) {\n throw new CrudHttpError(422, { error: 'forms.errors.draft_already_exists' })\n }\n\n const lastVersion = await em.findOne(\n FormVersion,\n { formId: form.id },\n { orderBy: { versionNumber: 'desc' } },\n )\n\n let baseSchema: Record<string, unknown> = deepClone(EMPTY_SCHEMA)\n let baseUiSchema: Record<string, unknown> = {}\n let baseRoles: string[] = []\n\n if (parsed.fromVersionId) {\n const fromVersion = await findFormVersionInScope(\n em,\n parsed.fromVersionId,\n parsed.tenantId,\n parsed.organizationId,\n )\n if (fromVersion.formId !== form.id) {\n throw new CrudHttpError(400, { error: 'forms.errors.fork_source_invalid' })\n }\n baseSchema = deepClone(fromVersion.schema)\n baseUiSchema = deepClone(fromVersion.uiSchema)\n baseRoles = [...fromVersion.roles]\n } else if (lastVersion) {\n baseSchema = deepClone(lastVersion.schema)\n baseUiSchema = deepClone(lastVersion.uiSchema)\n baseRoles = [...lastVersion.roles]\n }\n\n const compiler = resolveCompiler(ctx)\n const registry = resolveRegistry(ctx)\n const versionNumber = (lastVersion?.versionNumber ?? 0) + 1\n\n let schemaHash = ''\n let registryVersion = registry.getRegistryVersion()\n try {\n const probe = compiler.compile({\n id: `__fork__:${form.id}:${versionNumber}`,\n updatedAt: new Date(),\n schema: baseSchema,\n uiSchema: baseUiSchema,\n })\n schemaHash = probe.schemaHash\n registryVersion = probe.registryVersion\n } catch {\n // Empty schemas may fail compile (no roles); fall back to deterministic hash.\n schemaHash = `forking:${form.id}:${versionNumber}`\n }\n\n const now = new Date()\n const draft = em.create(FormVersion, {\n formId: form.id,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n versionNumber,\n status: 'draft',\n schema: baseSchema,\n uiSchema: baseUiSchema,\n roles: baseRoles,\n schemaHash,\n registryVersion,\n publishedAt: null,\n publishedBy: null,\n changelog: null,\n archivedAt: null,\n createdAt: now,\n updatedAt: now,\n } as never)\n em.persist(draft)\n await em.flush()\n\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n FORMS_CACHE_TAGS.formVersion(draft.id),\n ])\n\n return { versionId: draft.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const version = await em.findOne(FormVersion, { id: result.versionId })\n return version ? serializeFormVersionSnapshot(version) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const after = snapshots.after as FormVersionSnapshot | undefined\n return {\n actionLabel: 'forms.audit.form_version.fork_draft',\n resourceKind: FORM_VERSION_RESOURCE_KIND,\n resourceId: result.versionId,\n parentResourceKind: 'forms.form',\n parentResourceId: after?.formId ?? null,\n tenantId: after?.tenantId ?? null,\n organizationId: after?.organizationId ?? null,\n snapshotAfter: after ?? null,\n payload: { undo: { after: after ?? null } satisfies FormVersionUndoPayload },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormVersionUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = resolveEntityManager(ctx).fork()\n const version = await em.findOne(FormVersion, { id: after.id })\n if (!version) return\n if (version.status !== 'draft') return\n em.remove(version)\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(after.organizationId),\n FORMS_CACHE_TAGS.form(after.formId),\n FORMS_CACHE_TAGS.formVersion(after.id),\n ])\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.form_version.update_draft\n// ----------------------------------------------------------------------------\n\nconst updateDraftCommand: CommandHandler<FormVersionUpdateDraftCommandInput, { versionId: string; updatedAt: string }> = {\n id: 'forms.form_version.update_draft',\n async prepare(rawInput, ctx) {\n const parsed = formVersionUpdateDraftCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const version = await em.findOne(FormVersion, {\n id: parsed.versionId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n })\n if (!version) return {}\n return { before: serializeFormVersionSnapshot(version) }\n },\n async execute(rawInput, ctx) {\n const parsed = formVersionUpdateDraftCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const version = await findFormVersionInScope(em, parsed.versionId, parsed.tenantId, parsed.organizationId)\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: FORM_VERSION_RESOURCE_KIND,\n resourceId: version.id,\n current: version.updatedAt,\n request: ctx.request,\n })\n if (version.formId !== parsed.formId) {\n throw new CrudHttpError(400, { error: 'forms.errors.version_form_mismatch' })\n }\n if (version.status !== 'draft') {\n throw new CrudHttpError(409, { error: 'forms.errors.version_is_frozen' })\n }\n\n let touched = false\n if (parsed.schema !== undefined) {\n version.schema = deepClone(parsed.schema)\n touched = true\n // Keep the roles column in sync with the schema's x-om-roles.\n const derivedRoles = deriveRolesFromSchema(version.schema)\n if (!arraysEqual(derivedRoles, version.roles)) {\n version.roles = derivedRoles\n }\n }\n if (parsed.uiSchema !== undefined) {\n version.uiSchema = deepClone(parsed.uiSchema)\n touched = true\n }\n if (parsed.roles !== undefined && parsed.schema === undefined) {\n const next = Array.from(new Set(parsed.roles))\n if (!arraysEqual(next, version.roles)) {\n version.roles = next\n touched = true\n }\n }\n if (parsed.changelog !== undefined) {\n const next = parsed.changelog?.trim() ?? null\n if (next !== version.changelog) {\n version.changelog = next\n touched = true\n }\n }\n\n if (touched) {\n const compiler = resolveCompiler(ctx)\n const registry = resolveRegistry(ctx)\n try {\n const compiled = compiler.compile({\n id: `__draft__:${version.id}:${Date.now()}`,\n updatedAt: new Date(),\n schema: version.schema,\n uiSchema: version.uiSchema,\n })\n version.schemaHash = compiled.schemaHash\n version.registryVersion = compiled.registryVersion\n } catch (error) {\n if (isFormCompilationError(error)) {\n throw new CrudHttpError(422, {\n error: 'forms.errors.schema_invalid',\n code: error.code,\n path: error.path,\n message: error.message,\n })\n }\n // Fail CLOSED. This arm used to stamp the registry version and carry on,\n // so any compile failure that was not a recognised\n // `FormCompilationError` persisted an unvalidated schema and answered\n // 200 \u2014 the invalid draft only surfaced later as an opaque 500 from\n // publish. The other two compile sites in this file (`create` and\n // `publish`) already `throw error` here; this one was the outlier.\n throw error\n }\n version.updatedAt = new Date()\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(version.organizationId),\n FORMS_CACHE_TAGS.form(version.formId),\n FORMS_CACHE_TAGS.formVersion(version.id),\n ])\n }\n\n return { versionId: version.id, updatedAt: version.updatedAt.toISOString() }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const version = await em.findOne(FormVersion, { id: result.versionId })\n return version ? serializeFormVersionSnapshot(version) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const before = snapshots.before as FormVersionSnapshot | undefined\n const after = snapshots.after as FormVersionSnapshot | undefined\n return {\n actionLabel: 'forms.audit.form_version.update_draft',\n resourceKind: FORM_VERSION_RESOURCE_KIND,\n resourceId: result.versionId,\n parentResourceKind: 'forms.form',\n parentResourceId: after?.formId ?? before?.formId ?? null,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n } satisfies FormVersionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormVersionUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = resolveEntityManager(ctx).fork()\n const version = await em.findOne(FormVersion, { id: before.id })\n if (!version) return\n if (version.status !== 'draft') return\n version.schema = deepClone(before.schema)\n version.uiSchema = deepClone(before.uiSchema)\n version.roles = [...before.roles]\n version.schemaHash = before.schemaHash\n version.registryVersion = before.registryVersion\n version.changelog = before.changelog\n version.updatedAt = new Date()\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(version.organizationId),\n FORMS_CACHE_TAGS.form(version.formId),\n FORMS_CACHE_TAGS.formVersion(version.id),\n ])\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.form_version.publish\n// ----------------------------------------------------------------------------\n\nconst publishVersionCommand: CommandHandler<FormVersionPublishCommandInput, {\n versionId: string\n versionNumber: number\n updatedAt: string\n}> = {\n id: 'forms.form_version.publish',\n async prepare(rawInput, ctx) {\n const parsed = formVersionPublishCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const version = await em.findOne(FormVersion, {\n id: parsed.versionId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n })\n return version ? { before: serializeFormVersionSnapshot(version) } : {}\n },\n async execute(rawInput, ctx) {\n const parsed = formVersionPublishCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const actorUserId = resolveActorUserId(ctx)\n if (!actorUserId) {\n throw new CrudHttpError(401, { error: 'forms.errors.unauthorized' })\n }\n\n const em = resolveEntityManager(ctx).fork()\n const compiler = resolveCompiler(ctx)\n const registry = resolveRegistry(ctx)\n\n return await em.transactional(async (txEm) => {\n // SELECT ... FOR UPDATE to prevent lost-update under concurrent publish.\n const db = (txEm as unknown as { getKysely: <T = unknown>() => unknown }).getKysely<unknown>() as unknown as {\n selectFrom: (name: string) => any\n }\n const lockedRows = (await db\n .selectFrom('forms_form_version')\n .select(['id', 'status', 'schema_hash', 'form_id'])\n .where('id', '=', parsed.versionId)\n .where('tenant_id', '=', parsed.tenantId)\n .where('organization_id', '=', parsed.organizationId)\n .forUpdate()\n .execute()) as Array<{ id: string; status: string; schema_hash: string; form_id: string }>\n const locked = lockedRows[0]\n if (!locked) {\n throw new CrudHttpError(404, { error: 'forms.errors.version_not_found' })\n }\n if (locked.form_id !== parsed.formId) {\n throw new CrudHttpError(400, { error: 'forms.errors.version_form_mismatch' })\n }\n if (locked.status !== 'draft') {\n throw new CrudHttpError(409, { error: 'forms.errors.version_is_frozen' })\n }\n\n const version = await txEm.findOne(FormVersion, { id: parsed.versionId })\n if (!version) {\n throw new CrudHttpError(404, { error: 'forms.errors.version_not_found' })\n }\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: FORM_VERSION_RESOURCE_KIND,\n resourceId: version.id,\n current: version.updatedAt,\n request: ctx.request,\n })\n const form = await txEm.findOne(Form, {\n id: version.formId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (!form) {\n throw new CrudHttpError(404, { error: 'forms.errors.form_not_found' })\n }\n\n // Compile (validates the schema before publish).\n let compiled\n try {\n compiled = compiler.compile({\n id: version.id,\n updatedAt: version.updatedAt,\n schema: version.schema,\n uiSchema: version.uiSchema,\n })\n } catch (error) {\n if (isFormCompilationError(error)) {\n throw new CrudHttpError(422, {\n error: 'forms.errors.schema_invalid',\n code: error.code,\n path: error.path,\n message: error.message,\n })\n }\n throw error\n }\n\n // No-op publish detection vs the previously-published version.\n if (form.currentPublishedVersionId) {\n const previous = await txEm.findOne(FormVersion, {\n id: form.currentPublishedVersionId,\n })\n if (previous && previous.schemaHash === compiled.schemaHash) {\n throw new CrudHttpError(422, { error: 'forms.errors.no_op_publish' })\n }\n }\n\n const now = new Date()\n const previousCurrent = form.currentPublishedVersionId ?? null\n\n version.status = 'published'\n version.schemaHash = compiled.schemaHash\n version.registryVersion = registry.getRegistryVersion()\n version.publishedAt = now\n version.publishedBy = actorUserId\n // Re-derive the roles column from the schema so a stale draft column\n // cannot ship a published version that under-declares its roles.\n version.roles = deriveRolesFromSchema(version.schema)\n if (parsed.changelog !== undefined && parsed.changelog !== null) {\n version.changelog = parsed.changelog.trim() || null\n }\n version.updatedAt = now\n\n form.currentPublishedVersionId = version.id\n if (form.status === 'draft') form.status = 'active'\n form.updatedAt = now\n\n await txEm.flush()\n\n // Stash undo metadata on the runtime context \u2014 buildLog reads it.\n ;(ctx as unknown as { __formsPublishMeta?: unknown }).__formsPublishMeta = {\n previousCurrentPublishedVersionId: previousCurrent,\n }\n\n await emitForms('forms.form_version.published', {\n formId: form.id,\n versionId: version.id,\n versionNumber: version.versionNumber,\n publishedBy: actorUserId,\n })\n\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n FORMS_CACHE_TAGS.formVersion(version.id),\n ...(previousCurrent ? [FORMS_CACHE_TAGS.formVersion(previousCurrent)] : []),\n ])\n\n return {\n versionId: version.id,\n versionNumber: version.versionNumber,\n updatedAt: version.updatedAt.toISOString(),\n }\n })\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const version = await em.findOne(FormVersion, { id: result.versionId })\n return version ? serializeFormVersionSnapshot(version) : null\n },\n buildLog: async ({ result, snapshots, ctx }) => {\n const before = snapshots.before as FormVersionSnapshot | undefined\n const after = snapshots.after as FormVersionSnapshot | undefined\n const meta = (ctx as unknown as {\n __formsPublishMeta?: { previousCurrentPublishedVersionId?: string | null }\n }).__formsPublishMeta\n return {\n actionLabel: 'forms.audit.form_version.publish',\n resourceKind: FORM_VERSION_RESOURCE_KIND,\n resourceId: result.versionId,\n parentResourceKind: 'forms.form',\n parentResourceId: after?.formId ?? before?.formId ?? null,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n previousCurrentPublishedVersionId:\n meta?.previousCurrentPublishedVersionId ?? null,\n } satisfies FormVersionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormVersionUndoPayload>(logEntry)\n const before = payload?.before\n const after = payload?.after\n if (!after) return\n const em = resolveEntityManager(ctx).fork()\n const version = await em.findOne(FormVersion, { id: after.id })\n if (!version) return\n if (version.status !== 'published') return\n\n const form = await em.findOne(Form, { id: version.formId })\n if (!form) return\n\n const hasSubmissions = await hasSubmissionsForVersion(em, version.id)\n\n if (hasSubmissions) {\n // Inverse: archive the version (cannot demote, would break submission FK pin).\n version.status = 'archived'\n version.archivedAt = new Date()\n version.updatedAt = new Date()\n } else {\n // Inverse: demote to draft, restore previous current_published_version_id.\n version.status = 'draft'\n version.publishedAt = null\n version.publishedBy = null\n if (before) {\n version.changelog = before.changelog\n version.schema = deepClone(before.schema)\n version.uiSchema = deepClone(before.uiSchema)\n version.roles = [...before.roles]\n version.schemaHash = before.schemaHash\n version.registryVersion = before.registryVersion\n }\n version.updatedAt = new Date()\n\n const previousId = payload?.previousCurrentPublishedVersionId ?? null\n form.currentPublishedVersionId = previousId\n if (!previousId) form.status = form.status === 'archived' ? form.status : 'draft'\n form.updatedAt = new Date()\n }\n\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n FORMS_CACHE_TAGS.formVersion(version.id),\n ])\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.form_version.archive\n// ----------------------------------------------------------------------------\n\nconst archiveVersionCommand: CommandHandler<FormVersionArchiveCommandInput, { versionId: string; updatedAt: string }> = {\n id: 'forms.form_version.archive',\n async prepare(rawInput, ctx) {\n const parsed = formVersionArchiveCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const version = await em.findOne(FormVersion, {\n id: parsed.versionId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n })\n if (!version) return {}\n return { before: serializeFormVersionSnapshot(version) }\n },\n async execute(rawInput, ctx) {\n const parsed = formVersionArchiveCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const version = await findFormVersionInScope(em, parsed.versionId, parsed.tenantId, parsed.organizationId)\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: FORM_VERSION_RESOURCE_KIND,\n resourceId: version.id,\n current: version.updatedAt,\n request: ctx.request,\n })\n if (version.formId !== parsed.formId) {\n throw new CrudHttpError(400, { error: 'forms.errors.version_form_mismatch' })\n }\n\n if (version.status !== 'archived') {\n const now = new Date()\n version.status = 'archived'\n version.archivedAt = now\n version.updatedAt = now\n\n // If we archived the currently-published version, clear the form pointer.\n const form = await em.findOne(Form, { id: version.formId })\n if (form && form.currentPublishedVersionId === version.id) {\n form.currentPublishedVersionId = null\n if (form.status === 'active') form.status = 'draft'\n form.updatedAt = now\n }\n\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(version.organizationId),\n FORMS_CACHE_TAGS.form(version.formId),\n FORMS_CACHE_TAGS.formVersion(version.id),\n ])\n }\n\n return { versionId: version.id, updatedAt: version.updatedAt.toISOString() }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const version = await em.findOne(FormVersion, { id: result.versionId })\n return version ? serializeFormVersionSnapshot(version) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const before = snapshots.before as FormVersionSnapshot | undefined\n const after = snapshots.after as FormVersionSnapshot | undefined\n return {\n actionLabel: 'forms.audit.form_version.archive',\n resourceKind: FORM_VERSION_RESOURCE_KIND,\n resourceId: result.versionId,\n parentResourceKind: 'forms.form',\n parentResourceId: after?.formId ?? before?.formId ?? null,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n } satisfies FormVersionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormVersionUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = resolveEntityManager(ctx).fork()\n const version = await em.findOne(FormVersion, { id: before.id })\n if (!version) return\n version.status = before.status\n version.archivedAt = before.archivedAt ? new Date(before.archivedAt) : null\n version.updatedAt = new Date()\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(version.organizationId),\n FORMS_CACHE_TAGS.form(version.formId),\n FORMS_CACHE_TAGS.formVersion(version.id),\n ])\n },\n}\n\n// ----------------------------------------------------------------------------\n// Registration\n// ----------------------------------------------------------------------------\n\nregisterCommand(forkDraftCommand)\nregisterCommand(updateDraftCommand)\nregisterCommand(publishVersionCommand)\nregisterCommand(archiveVersionCommand)\n\n// Used by API tests that need direct access to compileOrThrowValidation logic\n// in the future. Keeping the export surface narrow on purpose.\nexport {\n forkDraftCommand,\n updateDraftCommand,\n publishVersionCommand,\n archiveVersionCommand,\n hasSubmissionsForVersion,\n compileOrThrowValidation,\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { Form, FormVersion } from '../data/entities'\nimport {\n formVersionArchiveCommandSchema,\n formVersionForkDraftCommandSchema,\n formVersionPublishCommandSchema,\n formVersionUpdateDraftCommandSchema,\n type FormVersionArchiveCommandInput,\n type FormVersionForkDraftCommandInput,\n type FormVersionPublishCommandInput,\n type FormVersionUpdateDraftCommandInput,\n} from '../data/validators'\nimport {\n FORMS_CACHE_TAGS,\n FORM_VERSION_RESOURCE_KIND,\n emitForms,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n findFormInScope,\n findFormVersionInScope,\n invalidateFormsCacheTags,\n resolveActorUserId,\n resolveEntityManager,\n serializeFormVersionSnapshot,\n type FormVersionSnapshot,\n} from './shared'\nimport type { FormVersionCompiler } from '../services/form-version-compiler'\nimport type { FieldTypeRegistry } from '../schema/field-type-registry'\nimport { FormCompilationError, isFormCompilationError } from '../services/form-version-compiler'\n\n// ----------------------------------------------------------------------------\n// Undo payloads\n// ----------------------------------------------------------------------------\n\ntype FormVersionUndoPayload = {\n before?: FormVersionSnapshot | null\n after?: FormVersionSnapshot | null\n /**\n * Captured at publish time so undo can restore the prior pointer cleanly.\n */\n previousCurrentPublishedVersionId?: string | null\n /** Captured at publish time so undo of publish can route through the right inverse. */\n publishInverseKind?: 'demote_to_draft' | 'archive'\n}\n\n// ----------------------------------------------------------------------------\n// Helpers\n// ----------------------------------------------------------------------------\n\nfunction resolveCompiler(ctx: { container: { resolve: <T>(name: string) => T } }): FormVersionCompiler {\n return ctx.container.resolve<FormVersionCompiler>('formVersionCompiler')\n}\n\nfunction resolveRegistry(ctx: { container: { resolve: <T>(name: string) => T } }): FieldTypeRegistry {\n return ctx.container.resolve<FieldTypeRegistry>('fieldTypeRegistry')\n}\n\nconst EMPTY_SCHEMA: Record<string, unknown> = {\n type: 'object',\n properties: {},\n required: [],\n 'x-om-roles': [],\n 'x-om-sections': [],\n}\n\nfunction deepClone<T>(value: T): T {\n return JSON.parse(JSON.stringify(value)) as T\n}\n\nfunction arraysEqual<T>(a: T[], b: T[]): boolean {\n if (a.length !== b.length) return false\n return a.every((value, index) => value === b[index])\n}\n\n/**\n * The `roles` column mirrors the schema's `x-om-roles` (the studio edits the\n * latter). Keeping them in sync prevents the runtime declared-role check from\n * drifting from what the form actually declares. `admin` is always present.\n */\nfunction deriveRolesFromSchema(schema: Record<string, unknown>): string[] {\n const raw = schema['x-om-roles']\n const roles = Array.isArray(raw) ? raw.filter((entry): entry is string => typeof entry === 'string') : []\n return Array.from(new Set([...roles, 'admin']))\n}\n\n/**\n * Best-effort check for whether any submission references the given version.\n * Phase 1b runs before submission entities exist (1c), so the table may be\n * absent entirely. In that case there are clearly no submissions and we\n * return false. Once 1c lands, the same SQL works against the new table.\n */\nasync function hasSubmissionsForVersion(\n em: EntityManager,\n versionId: string,\n): Promise<boolean> {\n try {\n const db = (em as unknown as { getKysely: <T = unknown>() => unknown }).getKysely<unknown>() as unknown as {\n selectFrom: (name: string) => any\n }\n const tableExists = (await db\n .selectFrom('information_schema.tables')\n .select(['table_name'])\n .where('table_schema', '=', 'public')\n .where('table_name', '=', 'forms_form_submission')\n .execute()) as Array<{ table_name: string }>\n if (!tableExists.length) return false\n const rows = (await db\n .selectFrom('forms_form_submission')\n .select(['id'])\n .where('form_version_id', '=', versionId)\n .limit(1)\n .execute()) as Array<{ id: string }>\n return rows.length > 0\n } catch {\n // If the introspection query fails for any reason, fall back to the\n // safe assumption: treat as no submissions yet \u2014 the immutable\n // form_version row itself is still a viable inverse via archive.\n return false\n }\n}\n\nasync function compileOrThrowValidation(\n compiler: FormVersionCompiler,\n version: FormVersion,\n): Promise<void> {\n try {\n compiler.compile({\n id: version.id,\n updatedAt: version.updatedAt,\n schema: version.schema,\n uiSchema: version.uiSchema,\n })\n } catch (error) {\n if (isFormCompilationError(error)) {\n throw new CrudHttpError(422, {\n error: 'forms.errors.schema_invalid',\n code: error.code,\n path: error.path,\n message: error.message,\n })\n }\n throw error\n }\n}\n\n// ----------------------------------------------------------------------------\n// forms.form_version.fork_draft\n// ----------------------------------------------------------------------------\n\nconst forkDraftCommand: CommandHandler<FormVersionForkDraftCommandInput, { versionId: string }> = {\n id: 'forms.form_version.fork_draft',\n async execute(rawInput, ctx) {\n const parsed = formVersionForkDraftCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const form = await findFormInScope(em, parsed.formId, parsed.tenantId, parsed.organizationId)\n\n const existingDraft = await em.findOne(FormVersion, {\n formId: form.id,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n status: 'draft',\n })\n if (existingDraft) {\n throw new CrudHttpError(422, { error: 'forms.errors.draft_already_exists' })\n }\n\n const lastVersion = await em.findOne(\n FormVersion,\n { formId: form.id },\n { orderBy: { versionNumber: 'desc' } },\n )\n\n let baseSchema: Record<string, unknown> = deepClone(EMPTY_SCHEMA)\n let baseUiSchema: Record<string, unknown> = {}\n let baseRoles: string[] = []\n\n if (parsed.fromVersionId) {\n const fromVersion = await findFormVersionInScope(\n em,\n parsed.fromVersionId,\n parsed.tenantId,\n parsed.organizationId,\n )\n if (fromVersion.formId !== form.id) {\n throw new CrudHttpError(400, { error: 'forms.errors.fork_source_invalid' })\n }\n baseSchema = deepClone(fromVersion.schema)\n baseUiSchema = deepClone(fromVersion.uiSchema)\n baseRoles = [...fromVersion.roles]\n } else if (lastVersion) {\n baseSchema = deepClone(lastVersion.schema)\n baseUiSchema = deepClone(lastVersion.uiSchema)\n baseRoles = [...lastVersion.roles]\n }\n\n const compiler = resolveCompiler(ctx)\n const registry = resolveRegistry(ctx)\n const versionNumber = (lastVersion?.versionNumber ?? 0) + 1\n\n let schemaHash = ''\n let registryVersion = registry.getRegistryVersion()\n try {\n const probe = compiler.compile({\n id: `__fork__:${form.id}:${versionNumber}`,\n updatedAt: new Date(),\n schema: baseSchema,\n uiSchema: baseUiSchema,\n })\n schemaHash = probe.schemaHash\n registryVersion = probe.registryVersion\n } catch {\n // Empty schemas may fail compile (no roles); fall back to deterministic hash.\n schemaHash = `forking:${form.id}:${versionNumber}`\n }\n\n const now = new Date()\n const draft = em.create(FormVersion, {\n formId: form.id,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n versionNumber,\n status: 'draft',\n schema: baseSchema,\n uiSchema: baseUiSchema,\n roles: baseRoles,\n schemaHash,\n registryVersion,\n publishedAt: null,\n publishedBy: null,\n changelog: null,\n archivedAt: null,\n createdAt: now,\n updatedAt: now,\n } as never)\n em.persist(draft)\n await em.flush()\n\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n FORMS_CACHE_TAGS.formVersion(draft.id),\n ])\n\n return { versionId: draft.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const version = await em.findOne(FormVersion, { id: result.versionId })\n return version ? serializeFormVersionSnapshot(version) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const after = snapshots.after as FormVersionSnapshot | undefined\n return {\n actionLabel: translate('forms.audit.form_version.fork_draft', 'Create draft form version'),\n resourceKind: FORM_VERSION_RESOURCE_KIND,\n resourceId: result.versionId,\n parentResourceKind: 'forms.form',\n parentResourceId: after?.formId ?? null,\n tenantId: after?.tenantId ?? null,\n organizationId: after?.organizationId ?? null,\n snapshotAfter: after ?? null,\n payload: { undo: { after: after ?? null } satisfies FormVersionUndoPayload },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormVersionUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = resolveEntityManager(ctx).fork()\n const version = await em.findOne(FormVersion, { id: after.id })\n if (!version) return\n if (version.status !== 'draft') return\n em.remove(version)\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(after.organizationId),\n FORMS_CACHE_TAGS.form(after.formId),\n FORMS_CACHE_TAGS.formVersion(after.id),\n ])\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.form_version.update_draft\n// ----------------------------------------------------------------------------\n\nconst updateDraftCommand: CommandHandler<FormVersionUpdateDraftCommandInput, { versionId: string; updatedAt: string }> = {\n id: 'forms.form_version.update_draft',\n async prepare(rawInput, ctx) {\n const parsed = formVersionUpdateDraftCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const version = await em.findOne(FormVersion, {\n id: parsed.versionId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n })\n if (!version) return {}\n return { before: serializeFormVersionSnapshot(version) }\n },\n async execute(rawInput, ctx) {\n const parsed = formVersionUpdateDraftCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const version = await findFormVersionInScope(em, parsed.versionId, parsed.tenantId, parsed.organizationId)\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: FORM_VERSION_RESOURCE_KIND,\n resourceId: version.id,\n current: version.updatedAt,\n request: ctx.request,\n })\n if (version.formId !== parsed.formId) {\n throw new CrudHttpError(400, { error: 'forms.errors.version_form_mismatch' })\n }\n if (version.status !== 'draft') {\n throw new CrudHttpError(409, { error: 'forms.errors.version_is_frozen' })\n }\n\n let touched = false\n if (parsed.schema !== undefined) {\n version.schema = deepClone(parsed.schema)\n touched = true\n // Keep the roles column in sync with the schema's x-om-roles.\n const derivedRoles = deriveRolesFromSchema(version.schema)\n if (!arraysEqual(derivedRoles, version.roles)) {\n version.roles = derivedRoles\n }\n }\n if (parsed.uiSchema !== undefined) {\n version.uiSchema = deepClone(parsed.uiSchema)\n touched = true\n }\n if (parsed.roles !== undefined && parsed.schema === undefined) {\n const next = Array.from(new Set(parsed.roles))\n if (!arraysEqual(next, version.roles)) {\n version.roles = next\n touched = true\n }\n }\n if (parsed.changelog !== undefined) {\n const next = parsed.changelog?.trim() ?? null\n if (next !== version.changelog) {\n version.changelog = next\n touched = true\n }\n }\n\n if (touched) {\n const compiler = resolveCompiler(ctx)\n const registry = resolveRegistry(ctx)\n try {\n const compiled = compiler.compile({\n id: `__draft__:${version.id}:${Date.now()}`,\n updatedAt: new Date(),\n schema: version.schema,\n uiSchema: version.uiSchema,\n })\n version.schemaHash = compiled.schemaHash\n version.registryVersion = compiled.registryVersion\n } catch (error) {\n if (isFormCompilationError(error)) {\n throw new CrudHttpError(422, {\n error: 'forms.errors.schema_invalid',\n code: error.code,\n path: error.path,\n message: error.message,\n })\n }\n // Fail CLOSED. This arm used to stamp the registry version and carry on,\n // so any compile failure that was not a recognised\n // `FormCompilationError` persisted an unvalidated schema and answered\n // 200 \u2014 the invalid draft only surfaced later as an opaque 500 from\n // publish. The other two compile sites in this file (`create` and\n // `publish`) already `throw error` here; this one was the outlier.\n throw error\n }\n version.updatedAt = new Date()\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(version.organizationId),\n FORMS_CACHE_TAGS.form(version.formId),\n FORMS_CACHE_TAGS.formVersion(version.id),\n ])\n }\n\n return { versionId: version.id, updatedAt: version.updatedAt.toISOString() }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const version = await em.findOne(FormVersion, { id: result.versionId })\n return version ? serializeFormVersionSnapshot(version) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as FormVersionSnapshot | undefined\n const after = snapshots.after as FormVersionSnapshot | undefined\n return {\n actionLabel: translate('forms.audit.form_version.update_draft', 'Update draft form version'),\n resourceKind: FORM_VERSION_RESOURCE_KIND,\n resourceId: result.versionId,\n parentResourceKind: 'forms.form',\n parentResourceId: after?.formId ?? before?.formId ?? null,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n } satisfies FormVersionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormVersionUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = resolveEntityManager(ctx).fork()\n const version = await em.findOne(FormVersion, { id: before.id })\n if (!version) return\n if (version.status !== 'draft') return\n version.schema = deepClone(before.schema)\n version.uiSchema = deepClone(before.uiSchema)\n version.roles = [...before.roles]\n version.schemaHash = before.schemaHash\n version.registryVersion = before.registryVersion\n version.changelog = before.changelog\n version.updatedAt = new Date()\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(version.organizationId),\n FORMS_CACHE_TAGS.form(version.formId),\n FORMS_CACHE_TAGS.formVersion(version.id),\n ])\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.form_version.publish\n// ----------------------------------------------------------------------------\n\nconst publishVersionCommand: CommandHandler<FormVersionPublishCommandInput, {\n versionId: string\n versionNumber: number\n updatedAt: string\n}> = {\n id: 'forms.form_version.publish',\n async prepare(rawInput, ctx) {\n const parsed = formVersionPublishCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const version = await em.findOne(FormVersion, {\n id: parsed.versionId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n })\n return version ? { before: serializeFormVersionSnapshot(version) } : {}\n },\n async execute(rawInput, ctx) {\n const parsed = formVersionPublishCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const actorUserId = resolveActorUserId(ctx)\n if (!actorUserId) {\n throw new CrudHttpError(401, { error: 'forms.errors.unauthorized' })\n }\n\n const em = resolveEntityManager(ctx).fork()\n const compiler = resolveCompiler(ctx)\n const registry = resolveRegistry(ctx)\n\n return await em.transactional(async (txEm) => {\n // SELECT ... FOR UPDATE to prevent lost-update under concurrent publish.\n const db = (txEm as unknown as { getKysely: <T = unknown>() => unknown }).getKysely<unknown>() as unknown as {\n selectFrom: (name: string) => any\n }\n const lockedRows = (await db\n .selectFrom('forms_form_version')\n .select(['id', 'status', 'schema_hash', 'form_id'])\n .where('id', '=', parsed.versionId)\n .where('tenant_id', '=', parsed.tenantId)\n .where('organization_id', '=', parsed.organizationId)\n .forUpdate()\n .execute()) as Array<{ id: string; status: string; schema_hash: string; form_id: string }>\n const locked = lockedRows[0]\n if (!locked) {\n throw new CrudHttpError(404, { error: 'forms.errors.version_not_found' })\n }\n if (locked.form_id !== parsed.formId) {\n throw new CrudHttpError(400, { error: 'forms.errors.version_form_mismatch' })\n }\n if (locked.status !== 'draft') {\n throw new CrudHttpError(409, { error: 'forms.errors.version_is_frozen' })\n }\n\n const version = await txEm.findOne(FormVersion, { id: parsed.versionId })\n if (!version) {\n throw new CrudHttpError(404, { error: 'forms.errors.version_not_found' })\n }\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: FORM_VERSION_RESOURCE_KIND,\n resourceId: version.id,\n current: version.updatedAt,\n request: ctx.request,\n })\n const form = await txEm.findOne(Form, {\n id: version.formId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (!form) {\n throw new CrudHttpError(404, { error: 'forms.errors.form_not_found' })\n }\n\n // Compile (validates the schema before publish).\n let compiled\n try {\n compiled = compiler.compile({\n id: version.id,\n updatedAt: version.updatedAt,\n schema: version.schema,\n uiSchema: version.uiSchema,\n })\n } catch (error) {\n if (isFormCompilationError(error)) {\n throw new CrudHttpError(422, {\n error: 'forms.errors.schema_invalid',\n code: error.code,\n path: error.path,\n message: error.message,\n })\n }\n throw error\n }\n\n // No-op publish detection vs the previously-published version.\n if (form.currentPublishedVersionId) {\n const previous = await txEm.findOne(FormVersion, {\n id: form.currentPublishedVersionId,\n })\n if (previous && previous.schemaHash === compiled.schemaHash) {\n throw new CrudHttpError(422, { error: 'forms.errors.no_op_publish' })\n }\n }\n\n const now = new Date()\n const previousCurrent = form.currentPublishedVersionId ?? null\n\n version.status = 'published'\n version.schemaHash = compiled.schemaHash\n version.registryVersion = registry.getRegistryVersion()\n version.publishedAt = now\n version.publishedBy = actorUserId\n // Re-derive the roles column from the schema so a stale draft column\n // cannot ship a published version that under-declares its roles.\n version.roles = deriveRolesFromSchema(version.schema)\n if (parsed.changelog !== undefined && parsed.changelog !== null) {\n version.changelog = parsed.changelog.trim() || null\n }\n version.updatedAt = now\n\n form.currentPublishedVersionId = version.id\n if (form.status === 'draft') form.status = 'active'\n form.updatedAt = now\n\n await txEm.flush()\n\n // Stash undo metadata on the runtime context \u2014 buildLog reads it.\n ;(ctx as unknown as { __formsPublishMeta?: unknown }).__formsPublishMeta = {\n previousCurrentPublishedVersionId: previousCurrent,\n }\n\n await emitForms('forms.form_version.published', {\n formId: form.id,\n versionId: version.id,\n versionNumber: version.versionNumber,\n publishedBy: actorUserId,\n })\n\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n FORMS_CACHE_TAGS.formVersion(version.id),\n ...(previousCurrent ? [FORMS_CACHE_TAGS.formVersion(previousCurrent)] : []),\n ])\n\n return {\n versionId: version.id,\n versionNumber: version.versionNumber,\n updatedAt: version.updatedAt.toISOString(),\n }\n })\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const version = await em.findOne(FormVersion, { id: result.versionId })\n return version ? serializeFormVersionSnapshot(version) : null\n },\n buildLog: async ({ result, snapshots, ctx }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as FormVersionSnapshot | undefined\n const after = snapshots.after as FormVersionSnapshot | undefined\n const meta = (ctx as unknown as {\n __formsPublishMeta?: { previousCurrentPublishedVersionId?: string | null }\n }).__formsPublishMeta\n return {\n actionLabel: translate('forms.audit.form_version.publish', 'Publish form version'),\n resourceKind: FORM_VERSION_RESOURCE_KIND,\n resourceId: result.versionId,\n parentResourceKind: 'forms.form',\n parentResourceId: after?.formId ?? before?.formId ?? null,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n previousCurrentPublishedVersionId:\n meta?.previousCurrentPublishedVersionId ?? null,\n } satisfies FormVersionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormVersionUndoPayload>(logEntry)\n const before = payload?.before\n const after = payload?.after\n if (!after) return\n const em = resolveEntityManager(ctx).fork()\n const version = await em.findOne(FormVersion, { id: after.id })\n if (!version) return\n if (version.status !== 'published') return\n\n const form = await em.findOne(Form, { id: version.formId })\n if (!form) return\n\n const hasSubmissions = await hasSubmissionsForVersion(em, version.id)\n\n if (hasSubmissions) {\n // Inverse: archive the version (cannot demote, would break submission FK pin).\n version.status = 'archived'\n version.archivedAt = new Date()\n version.updatedAt = new Date()\n } else {\n // Inverse: demote to draft, restore previous current_published_version_id.\n version.status = 'draft'\n version.publishedAt = null\n version.publishedBy = null\n if (before) {\n version.changelog = before.changelog\n version.schema = deepClone(before.schema)\n version.uiSchema = deepClone(before.uiSchema)\n version.roles = [...before.roles]\n version.schemaHash = before.schemaHash\n version.registryVersion = before.registryVersion\n }\n version.updatedAt = new Date()\n\n const previousId = payload?.previousCurrentPublishedVersionId ?? null\n form.currentPublishedVersionId = previousId\n if (!previousId) form.status = form.status === 'archived' ? form.status : 'draft'\n form.updatedAt = new Date()\n }\n\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n FORMS_CACHE_TAGS.formVersion(version.id),\n ])\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.form_version.archive\n// ----------------------------------------------------------------------------\n\nconst archiveVersionCommand: CommandHandler<FormVersionArchiveCommandInput, { versionId: string; updatedAt: string }> = {\n id: 'forms.form_version.archive',\n async prepare(rawInput, ctx) {\n const parsed = formVersionArchiveCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const version = await em.findOne(FormVersion, {\n id: parsed.versionId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n })\n if (!version) return {}\n return { before: serializeFormVersionSnapshot(version) }\n },\n async execute(rawInput, ctx) {\n const parsed = formVersionArchiveCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const version = await findFormVersionInScope(em, parsed.versionId, parsed.tenantId, parsed.organizationId)\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: FORM_VERSION_RESOURCE_KIND,\n resourceId: version.id,\n current: version.updatedAt,\n request: ctx.request,\n })\n if (version.formId !== parsed.formId) {\n throw new CrudHttpError(400, { error: 'forms.errors.version_form_mismatch' })\n }\n\n if (version.status !== 'archived') {\n const now = new Date()\n version.status = 'archived'\n version.archivedAt = now\n version.updatedAt = now\n\n // If we archived the currently-published version, clear the form pointer.\n const form = await em.findOne(Form, { id: version.formId })\n if (form && form.currentPublishedVersionId === version.id) {\n form.currentPublishedVersionId = null\n if (form.status === 'active') form.status = 'draft'\n form.updatedAt = now\n }\n\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(version.organizationId),\n FORMS_CACHE_TAGS.form(version.formId),\n FORMS_CACHE_TAGS.formVersion(version.id),\n ])\n }\n\n return { versionId: version.id, updatedAt: version.updatedAt.toISOString() }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const version = await em.findOne(FormVersion, { id: result.versionId })\n return version ? serializeFormVersionSnapshot(version) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as FormVersionSnapshot | undefined\n const after = snapshots.after as FormVersionSnapshot | undefined\n return {\n actionLabel: translate('forms.audit.form_version.archive', 'Archive form version'),\n resourceKind: FORM_VERSION_RESOURCE_KIND,\n resourceId: result.versionId,\n parentResourceKind: 'forms.form',\n parentResourceId: after?.formId ?? before?.formId ?? null,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n } satisfies FormVersionUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormVersionUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = resolveEntityManager(ctx).fork()\n const version = await em.findOne(FormVersion, { id: before.id })\n if (!version) return\n version.status = before.status\n version.archivedAt = before.archivedAt ? new Date(before.archivedAt) : null\n version.updatedAt = new Date()\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(version.organizationId),\n FORMS_CACHE_TAGS.form(version.formId),\n FORMS_CACHE_TAGS.formVersion(version.id),\n ])\n },\n}\n\n// ----------------------------------------------------------------------------\n// Registration\n// ----------------------------------------------------------------------------\n\nregisterCommand(forkDraftCommand)\nregisterCommand(updateDraftCommand)\nregisterCommand(publishVersionCommand)\nregisterCommand(archiveVersionCommand)\n\n// Used by API tests that need direct access to compileOrThrowValidation logic\n// in the future. Keeping the export surface narrow on purpose.\nexport {\n forkDraftCommand,\n updateDraftCommand,\n publishVersionCommand,\n archiveVersionCommand,\n hasSubmissionsForVersion,\n compileOrThrowValidation,\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,5 @@
1
1
  import { registerCommand } from "@open-mercato/shared/lib/commands";
2
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
2
3
  import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
3
4
  import { enforceCommandOptimisticLockWithGuards } from "@open-mercato/shared/lib/crud/optimistic-lock-command";
4
5
  import { Form } from "../data/entities.js";
@@ -87,9 +88,10 @@ const createFormCommand = {
87
88
  return form ? serializeFormSnapshot(form) : null;
88
89
  },
89
90
  buildLog: async ({ result, snapshots }) => {
91
+ const { translate } = await resolveTranslations();
90
92
  const after = snapshots.after;
91
93
  return {
92
- actionLabel: "forms.audit.form.create",
94
+ actionLabel: translate("forms.audit.form.create", "Create form"),
93
95
  resourceKind: FORM_RESOURCE_KIND,
94
96
  resourceId: result.formId,
95
97
  tenantId: after?.tenantId ?? null,
@@ -194,10 +196,11 @@ const renameFormCommand = {
194
196
  return form ? serializeFormSnapshot(form) : null;
195
197
  },
196
198
  buildLog: async ({ result, snapshots }) => {
199
+ const { translate } = await resolveTranslations();
197
200
  const before = snapshots.before;
198
201
  const after = snapshots.after;
199
202
  return {
200
- actionLabel: "forms.audit.form.rename",
203
+ actionLabel: translate("forms.audit.form.rename", "Rename form"),
201
204
  resourceKind: FORM_RESOURCE_KIND,
202
205
  resourceId: result.formId,
203
206
  tenantId: after?.tenantId ?? before?.tenantId ?? null,
@@ -278,10 +281,11 @@ const archiveFormCommand = {
278
281
  return form ? serializeFormSnapshot(form) : null;
279
282
  },
280
283
  buildLog: async ({ result, snapshots }) => {
284
+ const { translate } = await resolveTranslations();
281
285
  const before = snapshots.before;
282
286
  const after = snapshots.after;
283
287
  return {
284
- actionLabel: "forms.audit.form.archive",
288
+ actionLabel: translate("forms.audit.form.archive", "Archive form"),
285
289
  resourceKind: FORM_RESOURCE_KIND,
286
290
  resourceId: result.formId,
287
291
  tenantId: after?.tenantId ?? before?.tenantId ?? null,
@@ -358,10 +362,11 @@ const restoreFormCommand = {
358
362
  return form ? serializeFormSnapshot(form) : null;
359
363
  },
360
364
  buildLog: async ({ result, snapshots }) => {
365
+ const { translate } = await resolveTranslations();
361
366
  const before = snapshots.before;
362
367
  const after = snapshots.after;
363
368
  return {
364
- actionLabel: "forms.audit.form.restore",
369
+ actionLabel: translate("forms.audit.form.restore", "Restore form"),
365
370
  resourceKind: FORM_RESOURCE_KIND,
366
371
  resourceId: result.formId,
367
372
  tenantId: after?.tenantId ?? before?.tenantId ?? null,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/forms/commands/form.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { Form } from '../data/entities'\nimport {\n formArchiveCommandSchema,\n formCreateCommandSchema,\n formRenameCommandSchema,\n formRestoreCommandSchema,\n type FormArchiveCommandInput,\n type FormCreateCommandInput,\n type FormRenameCommandInput,\n type FormRestoreCommandInput,\n} from '../data/validators'\nimport {\n FORMS_CACHE_TAGS,\n FORM_RESOURCE_KIND,\n emitForms,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n findFormInScope,\n invalidateFormsCacheTags,\n resolveActorUserId,\n resolveEntityManager,\n resolveFormLocaleUpdate,\n serializeFormSnapshot,\n type FormSnapshot,\n} from './shared'\n\n// ----------------------------------------------------------------------------\n// Undo payloads\n// ----------------------------------------------------------------------------\n\ntype FormUndoPayload = {\n before?: FormSnapshot | null\n after?: FormSnapshot | null\n}\n\n// ----------------------------------------------------------------------------\n// Helpers\n// ----------------------------------------------------------------------------\n\nfunction isUniqueViolation(error: unknown, constraint: string): boolean {\n if (!error || typeof error !== 'object') return false\n const candidate = error as Record<string, unknown>\n const code = candidate.code\n const detail = typeof candidate.detail === 'string' ? candidate.detail : ''\n const constraintName = typeof candidate.constraint === 'string' ? candidate.constraint : ''\n return (\n code === '23505' &&\n (constraintName === constraint || detail.includes(constraint))\n )\n}\n\n// ----------------------------------------------------------------------------\n// forms.form.create\n// ----------------------------------------------------------------------------\n\nconst createFormCommand: CommandHandler<FormCreateCommandInput, { formId: string }> = {\n id: 'forms.form.create',\n async execute(rawInput, ctx) {\n const parsed = formCreateCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const actorUserId = resolveActorUserId(ctx)\n if (!actorUserId) {\n throw new CrudHttpError(401, { error: 'forms.errors.unauthorized' })\n }\n\n const em = resolveEntityManager(ctx).fork()\n const supportedLocales = Array.from(new Set(parsed.supportedLocales))\n if (!supportedLocales.includes(parsed.defaultLocale)) {\n supportedLocales.unshift(parsed.defaultLocale)\n }\n\n const now = new Date()\n const form = em.create(Form, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n key: parsed.key,\n name: parsed.name.trim(),\n description: parsed.description?.trim() ?? null,\n status: 'draft',\n currentPublishedVersionId: null,\n defaultLocale: parsed.defaultLocale,\n supportedLocales,\n retentionDays: parsed.retentionDays ?? null,\n createdBy: actorUserId,\n archivedAt: null,\n createdAt: now,\n updatedAt: now,\n } as never)\n em.persist(form)\n\n try {\n await em.flush()\n } catch (error) {\n if (isUniqueViolation(error, 'forms_form_org_key_unique')) {\n throw new CrudHttpError(422, { error: 'forms.errors.form_key_taken' })\n }\n throw error\n }\n\n await emitForms('forms.form.created', {\n formId: form.id,\n organizationId: form.organizationId,\n })\n\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n ])\n\n return { formId: form.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const form = await em.findOne(Form, { id: result.formId })\n return form ? serializeFormSnapshot(form) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const after = snapshots.after as FormSnapshot | undefined\n return {\n actionLabel: 'forms.audit.form.create',\n resourceKind: FORM_RESOURCE_KIND,\n resourceId: result.formId,\n tenantId: after?.tenantId ?? null,\n organizationId: after?.organizationId ?? null,\n snapshotAfter: after ?? null,\n payload: { undo: { after: after ?? null } satisfies FormUndoPayload },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = resolveEntityManager(ctx).fork()\n const form = await em.findOne(Form, { id: after.id })\n if (!form) return\n em.remove(form)\n await em.flush()\n await emitForms('forms.form.archived', { formId: after.id })\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(after.organizationId),\n FORMS_CACHE_TAGS.form(after.id),\n ])\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.form.rename\n// ----------------------------------------------------------------------------\n\nconst renameFormCommand: CommandHandler<FormRenameCommandInput, { formId: string; updatedAt: string }> = {\n id: 'forms.form.rename',\n async prepare(rawInput, ctx) {\n const parsed = formRenameCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const form = await em.findOne(Form, {\n id: parsed.id,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (!form) return {}\n return { before: serializeFormSnapshot(form) }\n },\n async execute(rawInput, ctx) {\n const parsed = formRenameCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const form = await findFormInScope(em, parsed.id, parsed.tenantId, parsed.organizationId)\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: FORM_RESOURCE_KIND,\n resourceId: form.id,\n current: form.updatedAt,\n request: ctx.request,\n })\n\n let touched = false\n if (parsed.name !== undefined) {\n const next = parsed.name.trim()\n if (next !== form.name) {\n form.name = next\n touched = true\n }\n }\n if (parsed.description !== undefined) {\n const next = parsed.description?.trim() ?? null\n if (next !== form.description) {\n form.description = next\n touched = true\n }\n }\n\n if (parsed.retentionDays !== undefined) {\n const next = parsed.retentionDays ?? null\n if (next !== (form.retentionDays ?? null)) {\n form.retentionDays = next\n touched = true\n }\n }\n\n if (parsed.supportedLocales !== undefined || parsed.defaultLocale !== undefined) {\n const { defaultLocale: nextDefault, supportedLocales: nextSupported } = resolveFormLocaleUpdate({\n current: { defaultLocale: form.defaultLocale, supportedLocales: [...form.supportedLocales] },\n supportedLocales: parsed.supportedLocales,\n defaultLocale: parsed.defaultLocale,\n })\n const supportedChanged =\n nextSupported.length !== form.supportedLocales.length ||\n nextSupported.some((locale, index) => locale !== form.supportedLocales[index])\n if (supportedChanged) {\n form.supportedLocales = nextSupported\n touched = true\n }\n if (nextDefault !== form.defaultLocale) {\n form.defaultLocale = nextDefault\n touched = true\n }\n }\n\n if (touched) {\n form.updatedAt = new Date()\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n ])\n }\n\n return { formId: form.id, updatedAt: form.updatedAt.toISOString() }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const form = await em.findOne(Form, { id: result.formId })\n return form ? serializeFormSnapshot(form) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const before = snapshots.before as FormSnapshot | undefined\n const after = snapshots.after as FormSnapshot | undefined\n return {\n actionLabel: 'forms.audit.form.rename',\n resourceKind: FORM_RESOURCE_KIND,\n resourceId: result.formId,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n } satisfies FormUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = resolveEntityManager(ctx).fork()\n const form = await em.findOne(Form, { id: before.id })\n if (!form) return\n form.name = before.name\n form.description = before.description ?? null\n form.defaultLocale = before.defaultLocale\n form.supportedLocales = [...before.supportedLocales]\n form.retentionDays = before.retentionDays ?? null\n form.updatedAt = new Date()\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n ])\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.form.archive\n// ----------------------------------------------------------------------------\n\nconst archiveFormCommand: CommandHandler<FormArchiveCommandInput, { formId: string; updatedAt: string }> = {\n id: 'forms.form.archive',\n async prepare(rawInput, ctx) {\n const parsed = formArchiveCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const form = await em.findOne(Form, {\n id: parsed.id,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (!form) return {}\n return { before: serializeFormSnapshot(form) }\n },\n async execute(rawInput, ctx) {\n const parsed = formArchiveCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const form = await findFormInScope(em, parsed.id, parsed.tenantId, parsed.organizationId)\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: FORM_RESOURCE_KIND,\n resourceId: form.id,\n current: form.updatedAt,\n request: ctx.request,\n })\n\n if (form.status !== 'archived') {\n const now = new Date()\n form.status = 'archived'\n form.archivedAt = now\n form.updatedAt = now\n await em.flush()\n await emitForms('forms.form.archived', { formId: form.id })\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n ])\n }\n\n return { formId: form.id, updatedAt: form.updatedAt.toISOString() }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const form = await em.findOne(Form, { id: result.formId })\n return form ? serializeFormSnapshot(form) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const before = snapshots.before as FormSnapshot | undefined\n const after = snapshots.after as FormSnapshot | undefined\n return {\n actionLabel: 'forms.audit.form.archive',\n resourceKind: FORM_RESOURCE_KIND,\n resourceId: result.formId,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n } satisfies FormUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = resolveEntityManager(ctx).fork()\n const form = await em.findOne(Form, { id: before.id })\n if (!form) return\n form.status = before.status\n form.archivedAt = before.archivedAt ? new Date(before.archivedAt) : null\n form.updatedAt = new Date()\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n ])\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.form.restore\n// ----------------------------------------------------------------------------\n\nconst restoreFormCommand: CommandHandler<FormRestoreCommandInput, { formId: string; updatedAt: string }> = {\n id: 'forms.form.restore',\n async prepare(rawInput, ctx) {\n const parsed = formRestoreCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const form = await em.findOne(Form, {\n id: parsed.id,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (!form) return {}\n return { before: serializeFormSnapshot(form) }\n },\n async execute(rawInput, ctx) {\n const parsed = formRestoreCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const form = await findFormInScope(em, parsed.id, parsed.tenantId, parsed.organizationId)\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: FORM_RESOURCE_KIND,\n resourceId: form.id,\n current: form.updatedAt,\n request: ctx.request,\n })\n\n if (form.status === 'archived') {\n const now = new Date()\n form.status = form.currentPublishedVersionId ? 'active' : 'draft'\n form.archivedAt = null\n form.updatedAt = now\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n ])\n }\n\n return { formId: form.id, updatedAt: form.updatedAt.toISOString() }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const form = await em.findOne(Form, { id: result.formId })\n return form ? serializeFormSnapshot(form) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const before = snapshots.before as FormSnapshot | undefined\n const after = snapshots.after as FormSnapshot | undefined\n return {\n actionLabel: 'forms.audit.form.restore',\n resourceKind: FORM_RESOURCE_KIND,\n resourceId: result.formId,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n } satisfies FormUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = resolveEntityManager(ctx).fork()\n const form = await em.findOne(Form, { id: before.id })\n if (!form) return\n form.status = before.status\n form.archivedAt = before.archivedAt ? new Date(before.archivedAt) : null\n form.updatedAt = new Date()\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n ])\n },\n}\n\nregisterCommand(createFormCommand)\nregisterCommand(renameFormCommand)\nregisterCommand(archiveFormCommand)\nregisterCommand(restoreFormCommand)\n\nexport {\n createFormCommand,\n renameFormCommand,\n archiveFormCommand,\n restoreFormCommand,\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { enforceCommandOptimisticLockWithGuards } from '@open-mercato/shared/lib/crud/optimistic-lock-command'\nimport { Form } from '../data/entities'\nimport {\n formArchiveCommandSchema,\n formCreateCommandSchema,\n formRenameCommandSchema,\n formRestoreCommandSchema,\n type FormArchiveCommandInput,\n type FormCreateCommandInput,\n type FormRenameCommandInput,\n type FormRestoreCommandInput,\n} from '../data/validators'\nimport {\n FORMS_CACHE_TAGS,\n FORM_RESOURCE_KIND,\n emitForms,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n findFormInScope,\n invalidateFormsCacheTags,\n resolveActorUserId,\n resolveEntityManager,\n resolveFormLocaleUpdate,\n serializeFormSnapshot,\n type FormSnapshot,\n} from './shared'\n\n// ----------------------------------------------------------------------------\n// Undo payloads\n// ----------------------------------------------------------------------------\n\ntype FormUndoPayload = {\n before?: FormSnapshot | null\n after?: FormSnapshot | null\n}\n\n// ----------------------------------------------------------------------------\n// Helpers\n// ----------------------------------------------------------------------------\n\nfunction isUniqueViolation(error: unknown, constraint: string): boolean {\n if (!error || typeof error !== 'object') return false\n const candidate = error as Record<string, unknown>\n const code = candidate.code\n const detail = typeof candidate.detail === 'string' ? candidate.detail : ''\n const constraintName = typeof candidate.constraint === 'string' ? candidate.constraint : ''\n return (\n code === '23505' &&\n (constraintName === constraint || detail.includes(constraint))\n )\n}\n\n// ----------------------------------------------------------------------------\n// forms.form.create\n// ----------------------------------------------------------------------------\n\nconst createFormCommand: CommandHandler<FormCreateCommandInput, { formId: string }> = {\n id: 'forms.form.create',\n async execute(rawInput, ctx) {\n const parsed = formCreateCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const actorUserId = resolveActorUserId(ctx)\n if (!actorUserId) {\n throw new CrudHttpError(401, { error: 'forms.errors.unauthorized' })\n }\n\n const em = resolveEntityManager(ctx).fork()\n const supportedLocales = Array.from(new Set(parsed.supportedLocales))\n if (!supportedLocales.includes(parsed.defaultLocale)) {\n supportedLocales.unshift(parsed.defaultLocale)\n }\n\n const now = new Date()\n const form = em.create(Form, {\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n key: parsed.key,\n name: parsed.name.trim(),\n description: parsed.description?.trim() ?? null,\n status: 'draft',\n currentPublishedVersionId: null,\n defaultLocale: parsed.defaultLocale,\n supportedLocales,\n retentionDays: parsed.retentionDays ?? null,\n createdBy: actorUserId,\n archivedAt: null,\n createdAt: now,\n updatedAt: now,\n } as never)\n em.persist(form)\n\n try {\n await em.flush()\n } catch (error) {\n if (isUniqueViolation(error, 'forms_form_org_key_unique')) {\n throw new CrudHttpError(422, { error: 'forms.errors.form_key_taken' })\n }\n throw error\n }\n\n await emitForms('forms.form.created', {\n formId: form.id,\n organizationId: form.organizationId,\n })\n\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n ])\n\n return { formId: form.id }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const form = await em.findOne(Form, { id: result.formId })\n return form ? serializeFormSnapshot(form) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const after = snapshots.after as FormSnapshot | undefined\n return {\n actionLabel: translate('forms.audit.form.create', 'Create form'),\n resourceKind: FORM_RESOURCE_KIND,\n resourceId: result.formId,\n tenantId: after?.tenantId ?? null,\n organizationId: after?.organizationId ?? null,\n snapshotAfter: after ?? null,\n payload: { undo: { after: after ?? null } satisfies FormUndoPayload },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormUndoPayload>(logEntry)\n const after = payload?.after\n if (!after) return\n const em = resolveEntityManager(ctx).fork()\n const form = await em.findOne(Form, { id: after.id })\n if (!form) return\n em.remove(form)\n await em.flush()\n await emitForms('forms.form.archived', { formId: after.id })\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(after.organizationId),\n FORMS_CACHE_TAGS.form(after.id),\n ])\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.form.rename\n// ----------------------------------------------------------------------------\n\nconst renameFormCommand: CommandHandler<FormRenameCommandInput, { formId: string; updatedAt: string }> = {\n id: 'forms.form.rename',\n async prepare(rawInput, ctx) {\n const parsed = formRenameCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const form = await em.findOne(Form, {\n id: parsed.id,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (!form) return {}\n return { before: serializeFormSnapshot(form) }\n },\n async execute(rawInput, ctx) {\n const parsed = formRenameCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const form = await findFormInScope(em, parsed.id, parsed.tenantId, parsed.organizationId)\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: FORM_RESOURCE_KIND,\n resourceId: form.id,\n current: form.updatedAt,\n request: ctx.request,\n })\n\n let touched = false\n if (parsed.name !== undefined) {\n const next = parsed.name.trim()\n if (next !== form.name) {\n form.name = next\n touched = true\n }\n }\n if (parsed.description !== undefined) {\n const next = parsed.description?.trim() ?? null\n if (next !== form.description) {\n form.description = next\n touched = true\n }\n }\n\n if (parsed.retentionDays !== undefined) {\n const next = parsed.retentionDays ?? null\n if (next !== (form.retentionDays ?? null)) {\n form.retentionDays = next\n touched = true\n }\n }\n\n if (parsed.supportedLocales !== undefined || parsed.defaultLocale !== undefined) {\n const { defaultLocale: nextDefault, supportedLocales: nextSupported } = resolveFormLocaleUpdate({\n current: { defaultLocale: form.defaultLocale, supportedLocales: [...form.supportedLocales] },\n supportedLocales: parsed.supportedLocales,\n defaultLocale: parsed.defaultLocale,\n })\n const supportedChanged =\n nextSupported.length !== form.supportedLocales.length ||\n nextSupported.some((locale, index) => locale !== form.supportedLocales[index])\n if (supportedChanged) {\n form.supportedLocales = nextSupported\n touched = true\n }\n if (nextDefault !== form.defaultLocale) {\n form.defaultLocale = nextDefault\n touched = true\n }\n }\n\n if (touched) {\n form.updatedAt = new Date()\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n ])\n }\n\n return { formId: form.id, updatedAt: form.updatedAt.toISOString() }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const form = await em.findOne(Form, { id: result.formId })\n return form ? serializeFormSnapshot(form) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as FormSnapshot | undefined\n const after = snapshots.after as FormSnapshot | undefined\n return {\n actionLabel: translate('forms.audit.form.rename', 'Rename form'),\n resourceKind: FORM_RESOURCE_KIND,\n resourceId: result.formId,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n } satisfies FormUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = resolveEntityManager(ctx).fork()\n const form = await em.findOne(Form, { id: before.id })\n if (!form) return\n form.name = before.name\n form.description = before.description ?? null\n form.defaultLocale = before.defaultLocale\n form.supportedLocales = [...before.supportedLocales]\n form.retentionDays = before.retentionDays ?? null\n form.updatedAt = new Date()\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n ])\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.form.archive\n// ----------------------------------------------------------------------------\n\nconst archiveFormCommand: CommandHandler<FormArchiveCommandInput, { formId: string; updatedAt: string }> = {\n id: 'forms.form.archive',\n async prepare(rawInput, ctx) {\n const parsed = formArchiveCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const form = await em.findOne(Form, {\n id: parsed.id,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (!form) return {}\n return { before: serializeFormSnapshot(form) }\n },\n async execute(rawInput, ctx) {\n const parsed = formArchiveCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const form = await findFormInScope(em, parsed.id, parsed.tenantId, parsed.organizationId)\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: FORM_RESOURCE_KIND,\n resourceId: form.id,\n current: form.updatedAt,\n request: ctx.request,\n })\n\n if (form.status !== 'archived') {\n const now = new Date()\n form.status = 'archived'\n form.archivedAt = now\n form.updatedAt = now\n await em.flush()\n await emitForms('forms.form.archived', { formId: form.id })\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n ])\n }\n\n return { formId: form.id, updatedAt: form.updatedAt.toISOString() }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const form = await em.findOne(Form, { id: result.formId })\n return form ? serializeFormSnapshot(form) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as FormSnapshot | undefined\n const after = snapshots.after as FormSnapshot | undefined\n return {\n actionLabel: translate('forms.audit.form.archive', 'Archive form'),\n resourceKind: FORM_RESOURCE_KIND,\n resourceId: result.formId,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n } satisfies FormUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = resolveEntityManager(ctx).fork()\n const form = await em.findOne(Form, { id: before.id })\n if (!form) return\n form.status = before.status\n form.archivedAt = before.archivedAt ? new Date(before.archivedAt) : null\n form.updatedAt = new Date()\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n ])\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.form.restore\n// ----------------------------------------------------------------------------\n\nconst restoreFormCommand: CommandHandler<FormRestoreCommandInput, { formId: string; updatedAt: string }> = {\n id: 'forms.form.restore',\n async prepare(rawInput, ctx) {\n const parsed = formRestoreCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const form = await em.findOne(Form, {\n id: parsed.id,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (!form) return {}\n return { before: serializeFormSnapshot(form) }\n },\n async execute(rawInput, ctx) {\n const parsed = formRestoreCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const form = await findFormInScope(em, parsed.id, parsed.tenantId, parsed.organizationId)\n await enforceCommandOptimisticLockWithGuards(ctx.container, {\n resourceKind: FORM_RESOURCE_KIND,\n resourceId: form.id,\n current: form.updatedAt,\n request: ctx.request,\n })\n\n if (form.status === 'archived') {\n const now = new Date()\n form.status = form.currentPublishedVersionId ? 'active' : 'draft'\n form.archivedAt = null\n form.updatedAt = now\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n ])\n }\n\n return { formId: form.id, updatedAt: form.updatedAt.toISOString() }\n },\n captureAfter: async (_input, result, ctx) => {\n const em = resolveEntityManager(ctx)\n const form = await em.findOne(Form, { id: result.formId })\n return form ? serializeFormSnapshot(form) : null\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as FormSnapshot | undefined\n const after = snapshots.after as FormSnapshot | undefined\n return {\n actionLabel: translate('forms.audit.form.restore', 'Restore form'),\n resourceKind: FORM_RESOURCE_KIND,\n resourceId: result.formId,\n tenantId: after?.tenantId ?? before?.tenantId ?? null,\n organizationId: after?.organizationId ?? before?.organizationId ?? null,\n snapshotBefore: before ?? null,\n snapshotAfter: after ?? null,\n payload: {\n undo: {\n before: before ?? null,\n after: after ?? null,\n } satisfies FormUndoPayload,\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = resolveEntityManager(ctx).fork()\n const form = await em.findOne(Form, { id: before.id })\n if (!form) return\n form.status = before.status\n form.archivedAt = before.archivedAt ? new Date(before.archivedAt) : null\n form.updatedAt = new Date()\n await em.flush()\n await invalidateFormsCacheTags(ctx, [\n FORMS_CACHE_TAGS.formList(form.organizationId),\n FORMS_CACHE_TAGS.form(form.id),\n ])\n },\n}\n\nregisterCommand(createFormCommand)\nregisterCommand(renameFormCommand)\nregisterCommand(archiveFormCommand)\nregisterCommand(restoreFormCommand)\n\nexport {\n createFormCommand,\n renameFormCommand,\n archiveFormCommand,\n restoreFormCommand,\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -1,4 +1,5 @@
1
1
  import { registerCommand } from "@open-mercato/shared/lib/commands";
2
+ import { resolveTranslations } from "@open-mercato/shared/lib/i18n/server";
2
3
  import { CrudHttpError } from "@open-mercato/shared/lib/crud/errors";
3
4
  import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
4
5
  import { FormDistribution, FormInvitation } from "../data/entities.js";
@@ -142,8 +143,9 @@ const createInvitationCommand = {
142
143
  };
143
144
  },
144
145
  buildLog: async ({ input, result }) => {
146
+ const { translate } = await resolveTranslations();
145
147
  return {
146
- actionLabel: "forms.audit.invitation.create",
148
+ actionLabel: translate("forms.audit.invitation.create", "Create invitation"),
147
149
  resourceKind: FORM_INVITATION_RESOURCE_KIND,
148
150
  resourceId: input.distributionId,
149
151
  tenantId: input.tenantId,
@@ -224,9 +226,10 @@ const sendInvitationCommand = {
224
226
  return { invitationId: invitation.id };
225
227
  },
226
228
  buildLog: async ({ result, snapshots }) => {
229
+ const { translate } = await resolveTranslations();
227
230
  const before = snapshots.before;
228
231
  return {
229
- actionLabel: "forms.audit.invitation.send",
232
+ actionLabel: translate("forms.audit.invitation.send", "Send invitation"),
230
233
  resourceKind: FORM_INVITATION_RESOURCE_KIND,
231
234
  resourceId: result.invitationId,
232
235
  tenantId: before?.tenantId ?? null,
@@ -295,9 +298,10 @@ const revokeInvitationCommand = {
295
298
  return { invitationId: invitation.id };
296
299
  },
297
300
  buildLog: async ({ result, snapshots }) => {
301
+ const { translate } = await resolveTranslations();
298
302
  const before = snapshots.before;
299
303
  return {
300
- actionLabel: "forms.audit.invitation.revoke",
304
+ actionLabel: translate("forms.audit.invitation.revoke", "Revoke invitation"),
301
305
  resourceKind: FORM_INVITATION_RESOURCE_KIND,
302
306
  resourceId: result.invitationId,
303
307
  tenantId: before?.tenantId ?? null,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/forms/commands/invitation.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { FormDistribution, FormInvitation } from '../data/entities'\nimport {\n invitationCreateCommandSchema,\n invitationRevokeCommandSchema,\n invitationSendCommandSchema,\n type FormInvitationCreateCommandInput,\n type FormInvitationRevokeCommandInput,\n type FormInvitationSendCommandInput,\n} from '../data/validators'\nimport {\n generateRawInvitationToken,\n getInvitationTokenTtlSeconds,\n hashInvitationToken,\n} from '../services/distribution-token'\nimport { enqueueInvitationEmail } from '../subscribers/invitation-email'\nimport {\n emitForms,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n invalidateFormsCacheTags,\n resolveActorUserId,\n resolveEntityManager,\n} from './shared'\nimport { DISTRIBUTION_CACHE_TAGS } from './distribution'\n\nexport const FORM_INVITATION_RESOURCE_KIND = 'forms.invitation'\n\n// ----------------------------------------------------------------------------\n// Snapshots + undo payloads\n// ----------------------------------------------------------------------------\n\ntype FormInvitationSendSnapshot = {\n id: string\n organizationId: string\n tenantId: string\n status: FormInvitation['status']\n sentAt: string | null\n sendCount: number\n}\n\ntype FormInvitationStatusSnapshot = {\n id: string\n organizationId: string\n tenantId: string\n status: FormInvitation['status']\n}\n\ntype FormInvitationSendUndoPayload = {\n before?: FormInvitationSendSnapshot | null\n}\n\ntype FormInvitationRevokeUndoPayload = {\n before?: FormInvitationStatusSnapshot | null\n}\n\n// ----------------------------------------------------------------------------\n// Helpers\n// ----------------------------------------------------------------------------\n\nfunction resolveAppUrl(): string | null {\n const raw = process.env.APP_URL?.trim()\n if (!raw) return null\n return raw.replace(/\\/$/, '')\n}\n\nfunction buildPersonalLink(rawToken: string): string | null {\n const base = resolveAppUrl()\n if (!base) return null\n return `${base}/i/${rawToken}`\n}\n\nasync function findDistributionInScope(\n em: EntityManager,\n distributionId: string,\n tenantId: string,\n organizationId: string,\n): Promise<FormDistribution> {\n const distribution = await em.findOne(FormDistribution, {\n id: distributionId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n if (!distribution) {\n throw new CrudHttpError(404, { error: 'forms.errors.distribution_not_found' })\n }\n return distribution\n}\n\nasync function findInvitationInScope(\n em: EntityManager,\n invitationId: string,\n tenantId: string,\n organizationId: string,\n): Promise<FormInvitation> {\n const invitation = await findOneWithDecryption(\n em,\n FormInvitation,\n { id: invitationId, tenantId, organizationId, deletedAt: null },\n undefined,\n { tenantId, organizationId },\n )\n if (!invitation) {\n throw new CrudHttpError(404, { error: 'forms.errors.invitation_not_found' })\n }\n return invitation\n}\n\n// ----------------------------------------------------------------------------\n// forms.invitation.create \u2014 bulk\n// ----------------------------------------------------------------------------\n\ntype InvitationCreateResult = {\n invitations: Array<{ id: string; rawToken: string | null }>\n}\n\nconst createInvitationCommand: CommandHandler<FormInvitationCreateCommandInput, InvitationCreateResult> = {\n id: 'forms.invitation.create',\n async execute(rawInput, ctx) {\n const parsed = invitationCreateCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const actorUserId = resolveActorUserId(ctx)\n const em = resolveEntityManager(ctx).fork()\n const distribution = await findDistributionInScope(\n em,\n parsed.distributionId,\n parsed.tenantId,\n parsed.organizationId,\n )\n\n const isPersonal = distribution.mode === 'personal'\n const now = new Date()\n const ttlSeconds = getInvitationTokenTtlSeconds()\n\n const created: Array<{ invitation: FormInvitation; rawToken: string | null }> = []\n for (const recipient of parsed.recipients) {\n const rawToken = isPersonal ? generateRawInvitationToken() : null\n const expiresAt = recipient.expiresAt\n ? new Date(recipient.expiresAt)\n : isPersonal\n ? new Date(now.getTime() + ttlSeconds * 1000)\n : null\n const invitation = em.create(FormInvitation, {\n distributionId: distribution.id,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n recipientEmail: recipient.email?.trim() ?? null,\n recipientName: recipient.name?.trim() ?? null,\n recipientRef: recipient.ref?.trim() ?? null,\n role: recipient.role ?? null,\n tokenHash: rawToken ? hashInvitationToken(rawToken) : null,\n status: 'pending',\n submissionId: null,\n locale: recipient.locale ?? null,\n expiresAt,\n sentAt: null,\n openedAt: null,\n startedAt: null,\n submittedAt: null,\n sendCount: 0,\n lastError: null,\n createdBy: actorUserId,\n createdAt: now,\n updatedAt: now,\n } as never)\n em.persist(invitation)\n created.push({ invitation, rawToken })\n }\n\n await em.flush()\n\n for (const entry of created) {\n await emitForms('forms.invitation.created', {\n invitationId: entry.invitation.id,\n distributionId: distribution.id,\n })\n }\n\n // Inline best-effort personal-link email for recipients that supplied an\n // email. This is the only point where the raw token is available; the\n // subscriber resend path cannot rebuild the link.\n for (const entry of created) {\n const email = entry.invitation.recipientEmail?.trim()\n if (!email || !entry.rawToken) continue\n const link = buildPersonalLink(entry.rawToken)\n const delivered = await enqueueInvitationEmail({\n em,\n invitation: entry.invitation,\n recipientEmail: email,\n link,\n })\n if (delivered) {\n entry.invitation.status = 'sent'\n entry.invitation.sentAt = new Date()\n entry.invitation.sendCount += 1\n await em.flush()\n await emitForms('forms.invitation.sent', {\n invitationId: entry.invitation.id,\n distributionId: distribution.id,\n })\n }\n }\n\n await invalidateFormsCacheTags(ctx, [\n DISTRIBUTION_CACHE_TAGS.distribution(distribution.id),\n ])\n\n return {\n invitations: created.map((entry) => ({ id: entry.invitation.id, rawToken: entry.rawToken })),\n }\n },\n buildLog: async ({ input, result }) => {\n return {\n actionLabel: 'forms.audit.invitation.create',\n resourceKind: FORM_INVITATION_RESOURCE_KIND,\n resourceId: input.distributionId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n payload: { undo: { invitationIds: result.invitations.map((entry) => entry.id) } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<{ invitationIds?: string[] }>(logEntry)\n const ids = payload?.invitationIds\n if (!ids || ids.length === 0) return\n const em = resolveEntityManager(ctx).fork()\n const invitations = await em.find(FormInvitation, { id: { $in: ids } })\n for (const invitation of invitations) {\n em.remove(invitation)\n }\n await em.flush()\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.invitation.send \u2014 also the resend path\n// ----------------------------------------------------------------------------\n\nconst sendInvitationCommand: CommandHandler<FormInvitationSendCommandInput, { invitationId: string }> = {\n id: 'forms.invitation.send',\n async prepare(rawInput, ctx) {\n const parsed = invitationSendCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const invitation = await em.findOne(FormInvitation, {\n id: parsed.invitationId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (!invitation) return {}\n return {\n before: {\n id: invitation.id,\n organizationId: invitation.organizationId,\n tenantId: invitation.tenantId,\n status: invitation.status,\n sentAt: invitation.sentAt ? invitation.sentAt.toISOString() : null,\n sendCount: invitation.sendCount,\n } satisfies FormInvitationSendSnapshot,\n }\n },\n async execute(rawInput, ctx) {\n const parsed = invitationSendCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const invitation = await findInvitationInScope(\n em,\n parsed.invitationId,\n parsed.tenantId,\n parsed.organizationId,\n )\n\n if (invitation.status === 'revoked') {\n throw new CrudHttpError(409, { error: 'forms.errors.invitation_revoked' })\n }\n if (invitation.status === 'submitted') {\n throw new CrudHttpError(409, { error: 'forms.errors.invitation_submitted' })\n }\n const recipientEmail = invitation.recipientEmail?.trim()\n if (!recipientEmail) {\n throw new CrudHttpError(422, { error: 'forms.errors.invitation_no_email' })\n }\n\n if (invitation.status === 'pending' || invitation.status === 'opened') {\n invitation.status = 'sent'\n }\n invitation.sentAt = new Date()\n invitation.sendCount += 1\n invitation.updatedAt = new Date()\n await em.flush()\n\n await emitForms('forms.invitation.sent', {\n invitationId: invitation.id,\n distributionId: invitation.distributionId,\n })\n\n await invalidateFormsCacheTags(ctx, [\n DISTRIBUTION_CACHE_TAGS.distribution(invitation.distributionId),\n ])\n\n return { invitationId: invitation.id }\n },\n buildLog: async ({ result, snapshots }) => {\n const before = snapshots.before as FormInvitationSendSnapshot | undefined\n return {\n actionLabel: 'forms.audit.invitation.send',\n resourceKind: FORM_INVITATION_RESOURCE_KIND,\n resourceId: result.invitationId,\n tenantId: before?.tenantId ?? null,\n organizationId: before?.organizationId ?? null,\n payload: { undo: { before: before ?? null } satisfies FormInvitationSendUndoPayload },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormInvitationSendUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = resolveEntityManager(ctx).fork()\n const invitation = await em.findOne(FormInvitation, { id: before.id })\n if (!invitation) return\n invitation.status = before.status\n invitation.sentAt = before.sentAt ? new Date(before.sentAt) : null\n invitation.sendCount = before.sendCount\n invitation.updatedAt = new Date()\n await em.flush()\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.invitation.revoke \u2014 soft revoke (row retained, status only)\n// ----------------------------------------------------------------------------\n\nconst revokeInvitationCommand: CommandHandler<FormInvitationRevokeCommandInput, { invitationId: string }> = {\n id: 'forms.invitation.revoke',\n async prepare(rawInput, ctx) {\n const parsed = invitationRevokeCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const invitation = await em.findOne(FormInvitation, {\n id: parsed.invitationId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (!invitation) return {}\n return {\n before: {\n id: invitation.id,\n organizationId: invitation.organizationId,\n tenantId: invitation.tenantId,\n status: invitation.status,\n } satisfies FormInvitationStatusSnapshot,\n }\n },\n async execute(rawInput, ctx) {\n const parsed = invitationRevokeCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const invitation = await em.findOne(FormInvitation, {\n id: parsed.invitationId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (!invitation) {\n throw new CrudHttpError(404, { error: 'forms.errors.invitation_not_found' })\n }\n\n if (invitation.status !== 'revoked') {\n invitation.status = 'revoked'\n invitation.updatedAt = new Date()\n await em.flush()\n await emitForms('forms.invitation.revoked', { invitationId: invitation.id })\n await invalidateFormsCacheTags(ctx, [\n DISTRIBUTION_CACHE_TAGS.distribution(invitation.distributionId),\n ])\n }\n\n return { invitationId: invitation.id }\n },\n buildLog: async ({ result, snapshots }) => {\n const before = snapshots.before as FormInvitationStatusSnapshot | undefined\n return {\n actionLabel: 'forms.audit.invitation.revoke',\n resourceKind: FORM_INVITATION_RESOURCE_KIND,\n resourceId: result.invitationId,\n tenantId: before?.tenantId ?? null,\n organizationId: before?.organizationId ?? null,\n payload: { undo: { before: before ?? null } satisfies FormInvitationRevokeUndoPayload },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormInvitationRevokeUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = resolveEntityManager(ctx).fork()\n const invitation = await em.findOne(FormInvitation, { id: before.id })\n if (!invitation) return\n invitation.status = before.status\n invitation.updatedAt = new Date()\n await em.flush()\n },\n}\n\nregisterCommand(createInvitationCommand)\nregisterCommand(sendInvitationCommand)\nregisterCommand(revokeInvitationCommand)\n\nexport {\n createInvitationCommand,\n sendInvitationCommand,\n revokeInvitationCommand,\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { findOneWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport { FormDistribution, FormInvitation } from '../data/entities'\nimport {\n invitationCreateCommandSchema,\n invitationRevokeCommandSchema,\n invitationSendCommandSchema,\n type FormInvitationCreateCommandInput,\n type FormInvitationRevokeCommandInput,\n type FormInvitationSendCommandInput,\n} from '../data/validators'\nimport {\n generateRawInvitationToken,\n getInvitationTokenTtlSeconds,\n hashInvitationToken,\n} from '../services/distribution-token'\nimport { enqueueInvitationEmail } from '../subscribers/invitation-email'\nimport {\n emitForms,\n ensureOrganizationScope,\n ensureTenantScope,\n extractUndoPayload,\n invalidateFormsCacheTags,\n resolveActorUserId,\n resolveEntityManager,\n} from './shared'\nimport { DISTRIBUTION_CACHE_TAGS } from './distribution'\n\nexport const FORM_INVITATION_RESOURCE_KIND = 'forms.invitation'\n\n// ----------------------------------------------------------------------------\n// Snapshots + undo payloads\n// ----------------------------------------------------------------------------\n\ntype FormInvitationSendSnapshot = {\n id: string\n organizationId: string\n tenantId: string\n status: FormInvitation['status']\n sentAt: string | null\n sendCount: number\n}\n\ntype FormInvitationStatusSnapshot = {\n id: string\n organizationId: string\n tenantId: string\n status: FormInvitation['status']\n}\n\ntype FormInvitationSendUndoPayload = {\n before?: FormInvitationSendSnapshot | null\n}\n\ntype FormInvitationRevokeUndoPayload = {\n before?: FormInvitationStatusSnapshot | null\n}\n\n// ----------------------------------------------------------------------------\n// Helpers\n// ----------------------------------------------------------------------------\n\nfunction resolveAppUrl(): string | null {\n const raw = process.env.APP_URL?.trim()\n if (!raw) return null\n return raw.replace(/\\/$/, '')\n}\n\nfunction buildPersonalLink(rawToken: string): string | null {\n const base = resolveAppUrl()\n if (!base) return null\n return `${base}/i/${rawToken}`\n}\n\nasync function findDistributionInScope(\n em: EntityManager,\n distributionId: string,\n tenantId: string,\n organizationId: string,\n): Promise<FormDistribution> {\n const distribution = await em.findOne(FormDistribution, {\n id: distributionId,\n tenantId,\n organizationId,\n deletedAt: null,\n })\n if (!distribution) {\n throw new CrudHttpError(404, { error: 'forms.errors.distribution_not_found' })\n }\n return distribution\n}\n\nasync function findInvitationInScope(\n em: EntityManager,\n invitationId: string,\n tenantId: string,\n organizationId: string,\n): Promise<FormInvitation> {\n const invitation = await findOneWithDecryption(\n em,\n FormInvitation,\n { id: invitationId, tenantId, organizationId, deletedAt: null },\n undefined,\n { tenantId, organizationId },\n )\n if (!invitation) {\n throw new CrudHttpError(404, { error: 'forms.errors.invitation_not_found' })\n }\n return invitation\n}\n\n// ----------------------------------------------------------------------------\n// forms.invitation.create \u2014 bulk\n// ----------------------------------------------------------------------------\n\ntype InvitationCreateResult = {\n invitations: Array<{ id: string; rawToken: string | null }>\n}\n\nconst createInvitationCommand: CommandHandler<FormInvitationCreateCommandInput, InvitationCreateResult> = {\n id: 'forms.invitation.create',\n async execute(rawInput, ctx) {\n const parsed = invitationCreateCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const actorUserId = resolveActorUserId(ctx)\n const em = resolveEntityManager(ctx).fork()\n const distribution = await findDistributionInScope(\n em,\n parsed.distributionId,\n parsed.tenantId,\n parsed.organizationId,\n )\n\n const isPersonal = distribution.mode === 'personal'\n const now = new Date()\n const ttlSeconds = getInvitationTokenTtlSeconds()\n\n const created: Array<{ invitation: FormInvitation; rawToken: string | null }> = []\n for (const recipient of parsed.recipients) {\n const rawToken = isPersonal ? generateRawInvitationToken() : null\n const expiresAt = recipient.expiresAt\n ? new Date(recipient.expiresAt)\n : isPersonal\n ? new Date(now.getTime() + ttlSeconds * 1000)\n : null\n const invitation = em.create(FormInvitation, {\n distributionId: distribution.id,\n organizationId: parsed.organizationId,\n tenantId: parsed.tenantId,\n recipientEmail: recipient.email?.trim() ?? null,\n recipientName: recipient.name?.trim() ?? null,\n recipientRef: recipient.ref?.trim() ?? null,\n role: recipient.role ?? null,\n tokenHash: rawToken ? hashInvitationToken(rawToken) : null,\n status: 'pending',\n submissionId: null,\n locale: recipient.locale ?? null,\n expiresAt,\n sentAt: null,\n openedAt: null,\n startedAt: null,\n submittedAt: null,\n sendCount: 0,\n lastError: null,\n createdBy: actorUserId,\n createdAt: now,\n updatedAt: now,\n } as never)\n em.persist(invitation)\n created.push({ invitation, rawToken })\n }\n\n await em.flush()\n\n for (const entry of created) {\n await emitForms('forms.invitation.created', {\n invitationId: entry.invitation.id,\n distributionId: distribution.id,\n })\n }\n\n // Inline best-effort personal-link email for recipients that supplied an\n // email. This is the only point where the raw token is available; the\n // subscriber resend path cannot rebuild the link.\n for (const entry of created) {\n const email = entry.invitation.recipientEmail?.trim()\n if (!email || !entry.rawToken) continue\n const link = buildPersonalLink(entry.rawToken)\n const delivered = await enqueueInvitationEmail({\n em,\n invitation: entry.invitation,\n recipientEmail: email,\n link,\n })\n if (delivered) {\n entry.invitation.status = 'sent'\n entry.invitation.sentAt = new Date()\n entry.invitation.sendCount += 1\n await em.flush()\n await emitForms('forms.invitation.sent', {\n invitationId: entry.invitation.id,\n distributionId: distribution.id,\n })\n }\n }\n\n await invalidateFormsCacheTags(ctx, [\n DISTRIBUTION_CACHE_TAGS.distribution(distribution.id),\n ])\n\n return {\n invitations: created.map((entry) => ({ id: entry.invitation.id, rawToken: entry.rawToken })),\n }\n },\n buildLog: async ({ input, result }) => {\n const { translate } = await resolveTranslations()\n return {\n actionLabel: translate('forms.audit.invitation.create', 'Create invitation'),\n resourceKind: FORM_INVITATION_RESOURCE_KIND,\n resourceId: input.distributionId,\n tenantId: input.tenantId,\n organizationId: input.organizationId,\n payload: { undo: { invitationIds: result.invitations.map((entry) => entry.id) } },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<{ invitationIds?: string[] }>(logEntry)\n const ids = payload?.invitationIds\n if (!ids || ids.length === 0) return\n const em = resolveEntityManager(ctx).fork()\n const invitations = await em.find(FormInvitation, { id: { $in: ids } })\n for (const invitation of invitations) {\n em.remove(invitation)\n }\n await em.flush()\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.invitation.send \u2014 also the resend path\n// ----------------------------------------------------------------------------\n\nconst sendInvitationCommand: CommandHandler<FormInvitationSendCommandInput, { invitationId: string }> = {\n id: 'forms.invitation.send',\n async prepare(rawInput, ctx) {\n const parsed = invitationSendCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const invitation = await em.findOne(FormInvitation, {\n id: parsed.invitationId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (!invitation) return {}\n return {\n before: {\n id: invitation.id,\n organizationId: invitation.organizationId,\n tenantId: invitation.tenantId,\n status: invitation.status,\n sentAt: invitation.sentAt ? invitation.sentAt.toISOString() : null,\n sendCount: invitation.sendCount,\n } satisfies FormInvitationSendSnapshot,\n }\n },\n async execute(rawInput, ctx) {\n const parsed = invitationSendCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const invitation = await findInvitationInScope(\n em,\n parsed.invitationId,\n parsed.tenantId,\n parsed.organizationId,\n )\n\n if (invitation.status === 'revoked') {\n throw new CrudHttpError(409, { error: 'forms.errors.invitation_revoked' })\n }\n if (invitation.status === 'submitted') {\n throw new CrudHttpError(409, { error: 'forms.errors.invitation_submitted' })\n }\n const recipientEmail = invitation.recipientEmail?.trim()\n if (!recipientEmail) {\n throw new CrudHttpError(422, { error: 'forms.errors.invitation_no_email' })\n }\n\n if (invitation.status === 'pending' || invitation.status === 'opened') {\n invitation.status = 'sent'\n }\n invitation.sentAt = new Date()\n invitation.sendCount += 1\n invitation.updatedAt = new Date()\n await em.flush()\n\n await emitForms('forms.invitation.sent', {\n invitationId: invitation.id,\n distributionId: invitation.distributionId,\n })\n\n await invalidateFormsCacheTags(ctx, [\n DISTRIBUTION_CACHE_TAGS.distribution(invitation.distributionId),\n ])\n\n return { invitationId: invitation.id }\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as FormInvitationSendSnapshot | undefined\n return {\n actionLabel: translate('forms.audit.invitation.send', 'Send invitation'),\n resourceKind: FORM_INVITATION_RESOURCE_KIND,\n resourceId: result.invitationId,\n tenantId: before?.tenantId ?? null,\n organizationId: before?.organizationId ?? null,\n payload: { undo: { before: before ?? null } satisfies FormInvitationSendUndoPayload },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormInvitationSendUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = resolveEntityManager(ctx).fork()\n const invitation = await em.findOne(FormInvitation, { id: before.id })\n if (!invitation) return\n invitation.status = before.status\n invitation.sentAt = before.sentAt ? new Date(before.sentAt) : null\n invitation.sendCount = before.sendCount\n invitation.updatedAt = new Date()\n await em.flush()\n },\n}\n\n// ----------------------------------------------------------------------------\n// forms.invitation.revoke \u2014 soft revoke (row retained, status only)\n// ----------------------------------------------------------------------------\n\nconst revokeInvitationCommand: CommandHandler<FormInvitationRevokeCommandInput, { invitationId: string }> = {\n id: 'forms.invitation.revoke',\n async prepare(rawInput, ctx) {\n const parsed = invitationRevokeCommandSchema.parse(rawInput)\n const em = resolveEntityManager(ctx)\n const invitation = await em.findOne(FormInvitation, {\n id: parsed.invitationId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (!invitation) return {}\n return {\n before: {\n id: invitation.id,\n organizationId: invitation.organizationId,\n tenantId: invitation.tenantId,\n status: invitation.status,\n } satisfies FormInvitationStatusSnapshot,\n }\n },\n async execute(rawInput, ctx) {\n const parsed = invitationRevokeCommandSchema.parse(rawInput)\n ensureTenantScope(ctx, parsed.tenantId)\n ensureOrganizationScope(ctx, parsed.organizationId)\n\n const em = resolveEntityManager(ctx).fork()\n const invitation = await em.findOne(FormInvitation, {\n id: parsed.invitationId,\n tenantId: parsed.tenantId,\n organizationId: parsed.organizationId,\n deletedAt: null,\n })\n if (!invitation) {\n throw new CrudHttpError(404, { error: 'forms.errors.invitation_not_found' })\n }\n\n if (invitation.status !== 'revoked') {\n invitation.status = 'revoked'\n invitation.updatedAt = new Date()\n await em.flush()\n await emitForms('forms.invitation.revoked', { invitationId: invitation.id })\n await invalidateFormsCacheTags(ctx, [\n DISTRIBUTION_CACHE_TAGS.distribution(invitation.distributionId),\n ])\n }\n\n return { invitationId: invitation.id }\n },\n buildLog: async ({ result, snapshots }) => {\n const { translate } = await resolveTranslations()\n const before = snapshots.before as FormInvitationStatusSnapshot | undefined\n return {\n actionLabel: translate('forms.audit.invitation.revoke', 'Revoke invitation'),\n resourceKind: FORM_INVITATION_RESOURCE_KIND,\n resourceId: result.invitationId,\n tenantId: before?.tenantId ?? null,\n organizationId: before?.organizationId ?? null,\n payload: { undo: { before: before ?? null } satisfies FormInvitationRevokeUndoPayload },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<FormInvitationRevokeUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const em = resolveEntityManager(ctx).fork()\n const invitation = await em.findOne(FormInvitation, { id: before.id })\n if (!invitation) return\n invitation.status = before.status\n invitation.updatedAt = new Date()\n await em.flush()\n },\n}\n\nregisterCommand(createInvitationCommand)\nregisterCommand(sendInvitationCommand)\nregisterCommand(revokeInvitationCommand)\n\nexport {\n createInvitationCommand,\n sendInvitationCommand,\n revokeInvitationCommand,\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }