@open-mercato/core 0.10.1-develop.7714.1.9dbb8200d8 → 0.10.1-develop.7716.1.cefe7975df

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.
@@ -17,7 +17,7 @@ import {
17
17
  buildCustomFieldResetMap,
18
18
  diffCustomFieldChanges
19
19
  } from "@open-mercato/shared/lib/commands/customFieldSnapshots";
20
- import { extractUndoPayload } from "@open-mercato/shared/lib/commands/undo";
20
+ import { extractUndoPayload, reviveSnapshotDates } from "@open-mercato/shared/lib/commands/undo";
21
21
  import { resolveRedoSnapshot } from "@open-mercato/shared/lib/commands/redo";
22
22
  import { withAtomicFlush } from "@open-mercato/shared/lib/commands/flush";
23
23
  import {
@@ -150,6 +150,7 @@ function captureOrganizationSnapshots(entity, childParents, custom) {
150
150
  undo: {
151
151
  id: String(entity.id),
152
152
  tenantId,
153
+ createdAt: entity.createdAt ? entity.createdAt.toISOString() : null,
153
154
  name: entity.name,
154
155
  slug: entity.slug ?? null,
155
156
  logoUrl: entity.logoUrl ?? null,
@@ -251,8 +252,7 @@ async function resolveUniqueSlug(em, tenantId, baseSlug, excludeId) {
251
252
  while (suffix < maxAttempts) {
252
253
  const filter = {
253
254
  tenant: tenantId,
254
- slug: candidate,
255
- deletedAt: null
255
+ slug: candidate
256
256
  };
257
257
  const existing = await em.findOne(Organization, filter);
258
258
  if (!existing || excludeId && String(existing.id) === excludeId) return candidate;
@@ -373,6 +373,7 @@ const createOrganizationCommand = {
373
373
  await assertOrganizationUndoTenantAccess(ctx, tenantId);
374
374
  const em = ctx.container.resolve("em");
375
375
  const de = ctx.container.resolve("dataEngine");
376
+ let organization = null;
376
377
  await withAtomicFlush(em, [
377
378
  async () => {
378
379
  await lockOrganizationHierarchyForTenant(em, tenantId);
@@ -391,14 +392,28 @@ const createOrganizationCommand = {
391
392
  });
392
393
  }
393
394
  }
394
- await de.deleteOrmEntity({
395
+ organization = await de.deleteOrmEntity({
395
396
  entity: Organization,
396
397
  where: buildOrganizationMutationFilter(after.id, tenantId),
397
- soft: false
398
+ soft: true,
399
+ softDeleteField: "deletedAt"
398
400
  });
401
+ if (organization) {
402
+ organization.isActive = false;
403
+ organization.parentId = null;
404
+ await em.flush();
405
+ }
399
406
  await rebuildHierarchyForTenant(em, tenantId);
400
407
  }
401
408
  ], { transaction: true });
409
+ await emitCrudUndoSideEffects({
410
+ dataEngine: de,
411
+ action: "deleted",
412
+ entity: organization,
413
+ identifiers: { id: after.id, tenantId, organizationId: after.id },
414
+ events: organizationCrudEvents,
415
+ indexer: organizationCrudIndexer
416
+ });
402
417
  },
403
418
  redo: async ({ logEntry, ctx }) => {
404
419
  const after = resolveRedoSnapshot(logEntry);
@@ -424,10 +439,14 @@ const createOrganizationCommand = {
424
439
  await em.flush();
425
440
  organization = existing;
426
441
  } else {
442
+ const snapshotAfter = logEntry.snapshotAfter;
443
+ const createdAt = after.createdAt ?? (snapshotAfter && typeof snapshotAfter === "object" && "createdAt" in snapshotAfter ? snapshotAfter.createdAt : void 0);
444
+ const creationTime = typeof createdAt === "string" || createdAt instanceof Date ? reviveSnapshotDates({ createdAt }, ["createdAt"]) : {};
427
445
  organization = await de.createOrmEntity({
428
446
  entity: Organization,
429
447
  data: {
430
448
  id: after.id,
449
+ ...creationTime,
431
450
  name: after.name,
432
451
  slug: after.slug ?? null,
433
452
  logoUrl: after.logoUrl ?? null,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/directory/commands/organizations.ts"],
4
- "sourcesContent": ["import type { CommandHandler, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { Organization, Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport { organizationCreateSchema, organizationUpdateSchema } from '@open-mercato/core/modules/directory/data/validators'\nimport {\n lockOrganizationHierarchyForTenant,\n rebuildHierarchyForTenant,\n} from '@open-mercato/core/modules/directory/lib/hierarchy'\nimport {\n enforceTenantSelection,\n normalizeTenantId,\n resolveIsSuperAdmin,\n} from '@open-mercato/core/modules/auth/lib/tenantAccess'\nimport { E } from '#generated/entities.ids.generated'\nimport type { CrudEmitContext, CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport {\n loadCustomFieldSnapshot,\n buildCustomFieldResetMap,\n diffCustomFieldChanges,\n} from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport {\n parseWithCustomFields,\n setCustomFieldsIfAny,\n emitCrudSideEffects,\n emitCrudUndoSideEffects,\n requireId,\n buildChanges,\n} from '@open-mercato/shared/lib/commands/helpers'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { toOptionalString } from '@open-mercato/shared/lib/string/coerce'\nimport { slugify } from '@open-mercato/shared/lib/slugify'\n\nexport const organizationCrudEvents: CrudEventsConfig = {\n module: 'directory',\n entity: 'organization',\n persistent: true,\n buildPayload: (ctx) => {\n const orgCtx = ctx as CrudEmitContext<Organization>\n return {\n id: orgCtx.identifiers.id,\n tenantId: tenantIdFromContext(orgCtx),\n organizationId: orgCtx.identifiers.id,\n }\n },\n}\n\nexport const organizationCrudIndexer: CrudIndexerConfig = {\n entityType: E.directory.organization,\n buildUpsertPayload: (ctx) => {\n const orgCtx = ctx as CrudEmitContext<Organization>\n return {\n entityType: E.directory.organization,\n recordId: orgCtx.identifiers.id,\n organizationId: orgCtx.identifiers.id,\n tenantId: tenantIdFromContext(orgCtx),\n }\n },\n buildDeletePayload: (ctx) => {\n const orgCtx = ctx as CrudEmitContext<Organization>\n return {\n entityType: E.directory.organization,\n recordId: orgCtx.identifiers.id,\n organizationId: orgCtx.identifiers.id,\n tenantId: tenantIdFromContext(orgCtx),\n }\n },\n}\n\ntype OrganizationTenantShape = {\n __tenantId?: unknown\n tenant?: string | { id?: unknown; getEntity?: () => { id?: unknown } }\n tenantId?: unknown\n tenant_id?: unknown\n}\n\ntype SerializedOrganization = ReturnType<typeof serializeOrganization>\n\ntype ChildParentSnapshot = {\n childId: string\n parentId: string | null\n}\n\ntype OrganizationUndoSnapshot = {\n id: string\n tenantId: string | null\n name: string\n slug?: string | null\n logoUrl?: string | null\n logoPreserveAspectRatio?: boolean\n isActive: boolean\n parentId: string | null\n childParents: ChildParentSnapshot[]\n custom?: Record<string, unknown>\n}\n\ntype OrganizationSnapshots = {\n view: SerializedOrganization\n undo: OrganizationUndoSnapshot\n}\n\ntype OrganizationActorScope = {\n isUnrestricted: boolean\n tenantId: string | null\n}\n\ntype AuthorizedOrganizationTarget = {\n organization: Organization\n tenantId: string\n}\n\nexport function resolveTenantIdFromEntity(entity: Organization): string | null {\n const shape = entity as unknown as OrganizationTenantShape\n const cached = toOptionalString(shape.__tenantId)\n if (cached) return cached\n const tenantRef = shape.tenant\n if (typeof tenantRef === 'string') return tenantRef\n if (tenantRef && typeof tenantRef === 'object') {\n const direct = toOptionalString(tenantRef.id)\n if (direct) return direct\n if (typeof tenantRef.getEntity === 'function') {\n const nested = tenantRef.getEntity()\n const nestedId = nested ? toOptionalString(nested.id) : null\n if (nestedId) return nestedId\n }\n }\n const fallback = toOptionalString(shape.tenantId) || toOptionalString(shape.tenant_id)\n return fallback\n}\n\nasync function resolveOrganizationActorScope(ctx: CommandRuntimeContext): Promise<OrganizationActorScope> {\n if (ctx.systemActor === true) return { isUnrestricted: true, tenantId: null }\n const tenantId = normalizeTenantId(ctx.auth?.tenantId ?? null) ?? null\n const isSuperAdmin = await resolveIsSuperAdmin(ctx)\n return { isUnrestricted: isSuperAdmin, tenantId }\n}\n\nfunction buildOrganizationTargetFilter(\n id: string,\n scope: OrganizationActorScope,\n): FilterQuery<Organization> | null {\n if (!scope.isUnrestricted && !scope.tenantId) return null\n return {\n id,\n deletedAt: null,\n ...(scope.isUnrestricted ? {} : { tenant: scope.tenantId }),\n } as FilterQuery<Organization>\n}\n\nasync function loadAuthorizedOrganizationTarget(\n em: EntityManager,\n id: string,\n ctx: CommandRuntimeContext,\n): Promise<AuthorizedOrganizationTarget> {\n const scope = await resolveOrganizationActorScope(ctx)\n const filter = buildOrganizationTargetFilter(id, scope)\n if (!filter) throw new CrudHttpError(404, { error: 'Not found' })\n const organization = await em.findOne(Organization, filter)\n if (!organization) throw new CrudHttpError(404, { error: 'Not found' })\n const tenantId = resolveTenantIdFromEntity(organization)\n if (!tenantId || (!scope.isUnrestricted && tenantId !== scope.tenantId)) {\n throw new CrudHttpError(404, { error: 'Not found' })\n }\n return { organization, tenantId }\n}\n\nfunction buildOrganizationMutationFilter(\n id: string,\n tenantId: string,\n): FilterQuery<Organization> {\n return {\n id,\n deletedAt: null,\n tenant: tenantId,\n } as FilterQuery<Organization>\n}\n\nasync function assertOrganizationUndoTenantAccess(\n ctx: CommandRuntimeContext,\n targetTenantId: string,\n): Promise<void> {\n const scope = await resolveOrganizationActorScope(ctx)\n if (!scope.isUnrestricted && scope.tenantId !== targetTenantId) {\n throw new CrudHttpError(404, { error: 'Not found' })\n }\n}\n\nfunction buildOrganizationUndoTargetFilter(\n id: string,\n tenantId: string,\n): FilterQuery<Organization> {\n return { id, tenant: tenantId } as FilterQuery<Organization>\n}\n\nfunction serializeOrganization(entity: Organization, custom?: Record<string, unknown> | null) {\n return {\n id: String(entity.id),\n tenantId: resolveTenantIdFromEntity(entity),\n name: entity.name,\n slug: entity.slug ?? null,\n logoUrl: entity.logoUrl ?? null,\n logoPreserveAspectRatio: !!entity.logoPreserveAspectRatio,\n isActive: !!entity.isActive,\n parentId: entity.parentId ?? null,\n ancestorIds: Array.isArray(entity.ancestorIds) ? [...entity.ancestorIds] : [],\n childIds: Array.isArray(entity.childIds) ? [...entity.childIds] : [],\n descendantIds: Array.isArray(entity.descendantIds) ? [...entity.descendantIds] : [],\n createdAt: entity.createdAt ? entity.createdAt.toISOString() : null,\n updatedAt: entity.updatedAt ? entity.updatedAt.toISOString() : null,\n ...(custom && Object.keys(custom).length ? { custom } : {}),\n }\n}\n\nfunction captureOrganizationSnapshots(\n entity: Organization,\n childParents: ChildParentSnapshot[],\n custom?: Record<string, unknown> | null\n): OrganizationSnapshots {\n const tenantId = resolveTenantIdFromEntity(entity)\n return {\n view: serializeOrganization(entity, custom),\n undo: {\n id: String(entity.id),\n tenantId,\n name: entity.name,\n slug: entity.slug ?? null,\n logoUrl: entity.logoUrl ?? null,\n logoPreserveAspectRatio: !!entity.logoPreserveAspectRatio,\n isActive: !!entity.isActive,\n parentId: entity.parentId ?? null,\n childParents: (childParents ?? []).map((entry) => ({\n childId: String(entry.childId),\n parentId: entry.parentId,\n })),\n ...(custom && Object.keys(custom).length ? { custom } : {}),\n },\n }\n}\n\nasync function loadChildParentSnapshots(\n em: EntityManager,\n tenantId: string | null,\n childIds: Iterable<string>\n): Promise<ChildParentSnapshot[]> {\n if (!tenantId) return []\n const ids = Array.from(new Set(Array.from(childIds ?? []).map((id) => String(id)).filter(Boolean)))\n if (!ids.length) return []\n const filter: FilterQuery<Organization> = {\n tenant: tenantId,\n deletedAt: null,\n id: { $in: ids },\n } as unknown as FilterQuery<Organization>\n const children = await em.find(Organization, filter)\n if (!children.length) return []\n const map = new Map(children.map((child) => [String(child.id), child.parentId ? String(child.parentId) : null]))\n return ids\n .filter((id) => map.has(id))\n .map((id) => ({\n childId: id,\n parentId: map.get(id) ?? null,\n }))\n}\n\nasync function restoreChildParents(em: EntityManager, tenantId: string, snapshots: ChildParentSnapshot[]) {\n if (!snapshots?.length) return\n const ids = Array.from(new Set(snapshots.map((entry) => entry.childId).filter(Boolean)))\n if (!ids.length) return\n const filter: FilterQuery<Organization> = {\n tenant: tenantId,\n deletedAt: null,\n id: { $in: ids },\n } as unknown as FilterQuery<Organization>\n const children = await em.find(Organization, filter)\n if (!children.length) return\n const desired = new Map(snapshots.map((entry) => [entry.childId, entry.parentId ?? null]))\n const toPersist: Organization[] = []\n for (const child of children) {\n const id = String(child.id)\n if (!desired.has(id)) continue\n const nextParent = desired.get(id) ?? null\n if (child.parentId !== nextParent) {\n child.parentId = nextParent\n toPersist.push(child)\n }\n }\n if (toPersist.length) await em.persist(toPersist).flush()\n}\n\nfunction normalizeChildIds(ids: readonly string[], exclude: string[]): string[] {\n const excludeSet = new Set(exclude)\n return Array.from(new Set(ids)).filter((id) => !excludeSet.has(id))\n}\n\nasync function ensureParentExists(em: EntityManager, tenantId: string, parentId: string | null): Promise<void> {\n if (!parentId) return\n const parentFilter: FilterQuery<Organization> = { id: parentId, tenant: tenantId, deletedAt: null }\n const parent = await em.findOne(Organization, parentFilter)\n if (!parent) throw new CrudHttpError(400, { error: 'Parent not found' })\n}\n\nasync function ensureChildrenValid(em: EntityManager, tenantId: string, childIds: string[]): Promise<void> {\n if (!childIds.length) return\n const childFilter: FilterQuery<Organization> = { id: { $in: childIds }, tenant: tenantId, deletedAt: null }\n const children = await em.find(Organization, childFilter)\n if (children.length !== childIds.length) throw new CrudHttpError(400, { error: 'Invalid child assignment' })\n}\n\nasync function assignChildren(\n em: EntityManager,\n tenantId: string,\n recordId: string,\n desiredChildIds: Iterable<string>\n): Promise<void> {\n const targetIds = Array.from(new Set(desiredChildIds)).filter((id) => id !== recordId)\n if (!targetIds.length) return\n const filter: FilterQuery<Organization> = { tenant: tenantId, deletedAt: null, id: { $in: targetIds } }\n const children = await em.find(Organization, filter)\n const toPersist: Organization[] = []\n for (const child of children) {\n if (String(child.id) === recordId) continue\n if (child.parentId !== recordId) {\n child.parentId = recordId\n toPersist.push(child)\n }\n }\n if (toPersist.length) await em.persist(toPersist).flush()\n}\n\nasync function clearRemovedChildren(em: EntityManager, tenantId: string, recordId: string, desiredChildIds: Set<string>): Promise<void> {\n const currentFilter: FilterQuery<Organization> = { tenant: tenantId, parentId: recordId, deletedAt: null }\n const current = await em.find(Organization, currentFilter)\n const toPersist = current.filter((child) => !desiredChildIds.has(String(child.id)))\n if (!toPersist.length) return\n for (const child of toPersist) child.parentId = null\n await em.persist(toPersist).flush()\n}\n\nasync function resolveUniqueSlug(em: EntityManager, tenantId: string, baseSlug: string, excludeId?: string): Promise<string> {\n let candidate = baseSlug\n let suffix = 0\n const maxAttempts = 50\n while (suffix < maxAttempts) {\n const filter: FilterQuery<Organization> = {\n tenant: tenantId,\n slug: candidate,\n deletedAt: null,\n } as unknown as FilterQuery<Organization>\n const existing = await em.findOne(Organization, filter)\n if (!existing || (excludeId && String(existing.id) === excludeId)) return candidate\n suffix += 1\n candidate = `${baseSlug}-${suffix}`\n }\n return `${baseSlug}-${Date.now()}`\n}\n\nconst createOrganizationCommand: CommandHandler<Record<string, unknown>, Organization> = {\n id: 'directory.organizations.create',\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(organizationCreateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const tenantId = await enforceTenantSelection(ctx, parsed.tenantId)\n if (!tenantId) throw new CrudHttpError(400, { error: 'Tenant scope required' })\n\n const parentId = parsed.parentId ?? null\n const childIds = normalizeChildIds(parsed.childIds ?? [], parentId ? [parentId] : [])\n if (parentId && childIds.includes(parentId)) throw new CrudHttpError(400, { error: 'Child cannot equal parent' })\n\n const tenantRef = em.getReference(Tenant, tenantId)\n const baseSlug = parsed.slug ? parsed.slug : slugify(parsed.name)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n\n let organization!: Organization\n await withAtomicFlush(em, [\n async () => {\n await lockOrganizationHierarchyForTenant(em, tenantId)\n if (parentId) {\n await ensureParentExists(em, tenantId, parentId)\n }\n await ensureChildrenValid(em, tenantId, childIds)\n const childParentsBefore = await loadChildParentSnapshots(em, tenantId, childIds)\n const slug = baseSlug ? await resolveUniqueSlug(em, tenantId, baseSlug) : null\n organization = await de.createOrmEntity({\n entity: Organization,\n data: {\n tenant: tenantRef,\n name: parsed.name,\n slug,\n logoUrl: parsed.logoUrl ?? null,\n logoPreserveAspectRatio: parsed.logoPreserveAspectRatio ?? false,\n isActive: parsed.isActive ?? true,\n parentId,\n },\n })\n setInternalTenantId(organization, tenantId)\n const recordId = String(organization.id)\n\n if (childIds.length) {\n await assignChildren(em, tenantId, recordId, childIds)\n }\n const childParentsAfter = await loadChildParentSnapshots(em, tenantId, childIds)\n setUndoMeta(organization, { childParentsBefore, childParentsAfter })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.directory.organization,\n recordId,\n tenantId,\n organizationId: recordId,\n values: custom,\n })\n\n await rebuildHierarchyForTenant(em, tenantId)\n },\n ], { transaction: true })\n\n const recordId = String(organization.id)\n const identifiers = { id: recordId, organizationId: recordId, tenantId }\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: organization,\n identifiers,\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n })\n\n return organization\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tenantId = resolveTenantIdFromEntity(result)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.directory.organization,\n recordId: String(result.id),\n tenantId,\n organizationId: String(result.id),\n })\n return serializeOrganization(result, custom)\n },\n buildLog: async ({ result, ctx }) => {\n const { translate } = await resolveTranslations()\n const meta = getUndoMeta(result)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tenantId = resolveTenantIdFromEntity(result)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.directory.organization,\n recordId: String(result.id),\n tenantId,\n organizationId: String(result.id),\n })\n const afterSnapshots = captureOrganizationSnapshots(result, meta.childParentsAfter ?? [], custom)\n return {\n actionLabel: translate('directory.audit.organizations.create', 'Create organization'),\n resourceKind: 'directory.organization',\n resourceId: String(result.id),\n tenantId,\n snapshotAfter: afterSnapshots.view,\n payload: {\n undo: {\n after: afterSnapshots.undo,\n childrenBefore: meta.childParentsBefore ?? [],\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<OrganizationUndoPayload>(logEntry)\n const after = payload?.after\n const childrenBefore = payload?.childrenBefore ?? []\n if (!after) return\n const tenantId = after.tenantId\n if (!tenantId) return\n await assertOrganizationUndoTenantAccess(ctx, tenantId)\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await withAtomicFlush(em, [\n async () => {\n await lockOrganizationHierarchyForTenant(em, tenantId)\n await restoreChildParents(em, tenantId, childrenBefore)\n if (after.custom && Object.keys(after.custom).length) {\n const reset = buildCustomFieldResetMap(undefined, after.custom)\n if (Object.keys(reset).length) {\n const resetValues = reset as Parameters<DataEngine['setCustomFields']>[0]['values']\n await de.setCustomFields({\n entityId: E.directory.organization,\n recordId: after.id,\n tenantId,\n organizationId: after.id,\n values: resetValues,\n notify: false,\n })\n }\n }\n await de.deleteOrmEntity({\n entity: Organization,\n where: buildOrganizationMutationFilter(after.id, tenantId),\n soft: false,\n })\n await rebuildHierarchyForTenant(em, tenantId)\n },\n ], { transaction: true })\n },\n redo: async ({ logEntry, ctx }) => {\n const after = resolveRedoSnapshot<OrganizationUndoSnapshot>(logEntry)\n if (!after) throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for organization create' })\n const tenantId = after.tenantId\n if (!tenantId) throw new CrudHttpError(400, { error: '[internal] redo snapshot missing tenant for organization create' })\n await assertOrganizationUndoTenantAccess(ctx, tenantId)\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let organization!: Organization\n await withAtomicFlush(em, [\n async () => {\n await lockOrganizationHierarchyForTenant(em, tenantId)\n const existing = await em.findOne(Organization, buildOrganizationUndoTargetFilter(after.id, tenantId))\n if (existing) {\n existing.deletedAt = null\n existing.name = after.name\n if (after.slug !== undefined) existing.slug = after.slug ?? null\n if (after.logoUrl !== undefined) existing.logoUrl = after.logoUrl ?? null\n if (after.logoPreserveAspectRatio !== undefined) existing.logoPreserveAspectRatio = !!after.logoPreserveAspectRatio\n existing.isActive = after.isActive\n existing.parentId = after.parentId\n await em.flush()\n organization = existing\n } else {\n organization = await de.createOrmEntity({\n entity: Organization,\n data: {\n id: after.id,\n name: after.name,\n slug: after.slug ?? null,\n logoUrl: after.logoUrl ?? null,\n logoPreserveAspectRatio: after.logoPreserveAspectRatio ?? false,\n tenant: em.getReference(Tenant, tenantId),\n isActive: after.isActive,\n parentId: after.parentId,\n },\n })\n }\n setInternalTenantId(organization, tenantId)\n\n if (after.custom && Object.keys(after.custom).length) {\n const reset = buildCustomFieldResetMap(after.custom, undefined)\n if (Object.keys(reset).length) {\n const resetValues = reset as Parameters<DataEngine['setCustomFields']>[0]['values']\n await de.setCustomFields({\n entityId: E.directory.organization,\n recordId: after.id,\n tenantId,\n organizationId: after.id,\n values: resetValues,\n notify: false,\n })\n }\n }\n\n await assignChildren(em, tenantId, after.id, after.childParents.map((entry) => entry.childId))\n await rebuildHierarchyForTenant(em, tenantId)\n },\n ], { transaction: true })\n\n const identifiers = { id: after.id, organizationId: after.id, tenantId }\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: organization,\n identifiers,\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n })\n\n return organization\n },\n}\n\nconst updateOrganizationCommand: CommandHandler<Record<string, unknown>, Organization> = {\n id: 'directory.organizations.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(organizationUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const { organization: current, tenantId } = await loadAuthorizedOrganizationTarget(em, parsed.id, ctx)\n const currentChildIds = Array.isArray(current.childIds) ? current.childIds : []\n const requestedChildIds = Array.isArray(parsed.childIds) ? parsed.childIds : []\n const combinedChildIds = new Set<string>([...currentChildIds.map(String), ...requestedChildIds.map(String)])\n const childParentsBefore = tenantId\n ? await loadChildParentSnapshots(em, tenantId, combinedChildIds)\n : []\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.directory.organization,\n recordId: String(current.id),\n tenantId,\n organizationId: String(current.id),\n })\n return { before: captureOrganizationSnapshots(current, childParentsBefore, custom) }\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(organizationUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const { tenantId } = await loadAuthorizedOrganizationTarget(em, parsed.id, ctx)\n\n const parentProvided = parsed.parentId !== undefined\n const childrenProvided = parsed.childIds !== undefined\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n\n let resolvedOrganization!: Organization\n await withAtomicFlush(em, [\n async () => {\n await lockOrganizationHierarchyForTenant(em, tenantId)\n const existing = await em.findOne(\n Organization,\n buildOrganizationMutationFilter(parsed.id, tenantId),\n { refresh: true },\n )\n if (!existing) throw new CrudHttpError(404, { error: 'Not found' })\n\n const parentId = parentProvided ? parsed.parentId ?? null : existing.parentId ?? null\n if (parentProvided && parentId) {\n if (parentId === parsed.id) throw new CrudHttpError(400, { error: 'Organization cannot be its own parent' })\n if (Array.isArray(existing.descendantIds) && existing.descendantIds.includes(parentId)) {\n throw new CrudHttpError(400, { error: 'Cannot assign descendant as parent' })\n }\n await ensureParentExists(em, tenantId, parentId)\n }\n\n const normalizedChildIds = normalizeChildIds(parsed.childIds ?? [], [parsed.id, parentProvided ? parentId ?? '' : ''])\n if (normalizedChildIds.some((id) => id === parentId)) throw new CrudHttpError(400, { error: 'Child cannot equal parent' })\n if (Array.isArray(existing.ancestorIds) && normalizedChildIds.some((id) => existing.ancestorIds.includes(id))) {\n throw new CrudHttpError(400, { error: 'Cannot assign ancestor as child' })\n }\n\n if (normalizedChildIds.length) {\n await ensureChildrenValid(em, tenantId, normalizedChildIds)\n const childFilter = {\n tenant: tenantId,\n deletedAt: null,\n id: { $in: normalizedChildIds },\n } as unknown as FilterQuery<Organization>\n const children = await em.find(Organization, childFilter)\n for (const child of children) {\n if (Array.isArray(child.descendantIds) && child.descendantIds.includes(parsed.id)) {\n throw new CrudHttpError(400, { error: 'Cannot assign descendant cycle' })\n }\n }\n }\n\n const combinedChildIds = new Set<string>([\n ...normalizedChildIds.map(String),\n ...(Array.isArray(existing.childIds) ? existing.childIds.map(String) : []),\n ])\n const childParentsBefore = await loadChildParentSnapshots(em, tenantId, combinedChildIds)\n\n let resolvedSlug: string | null | undefined\n if (parsed.slug !== undefined) {\n resolvedSlug = parsed.slug ? await resolveUniqueSlug(em, tenantId, parsed.slug, parsed.id) : parsed.slug\n }\n\n const organization = await de.updateOrmEntity({\n entity: Organization,\n where: buildOrganizationMutationFilter(parsed.id, tenantId),\n apply: (entity) => {\n if (parsed.name !== undefined) entity.name = parsed.name\n if (resolvedSlug !== undefined) entity.slug = resolvedSlug\n if (parsed.logoUrl !== undefined) entity.logoUrl = parsed.logoUrl ?? null\n if (parsed.logoPreserveAspectRatio !== undefined) entity.logoPreserveAspectRatio = parsed.logoPreserveAspectRatio\n if (parsed.isActive !== undefined) entity.isActive = parsed.isActive\n if (parentProvided) entity.parentId = parentId\n },\n })\n if (!organization) throw new CrudHttpError(404, { error: 'Not found' })\n setInternalTenantId(organization, tenantId)\n\n const recordId = String(organization.id)\n const desiredChildIds = new Set(normalizedChildIds.filter((id) => id !== recordId))\n if (childrenProvided) {\n await clearRemovedChildren(em, tenantId, recordId, desiredChildIds)\n await assignChildren(em, tenantId, recordId, desiredChildIds)\n }\n const childParentsAfter = await loadChildParentSnapshots(em, tenantId, combinedChildIds)\n setUndoMeta(organization, { childParentsBefore, childParentsAfter })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.directory.organization,\n recordId,\n tenantId,\n organizationId: recordId,\n values: custom,\n })\n\n await rebuildHierarchyForTenant(em, tenantId)\n resolvedOrganization = organization\n },\n ], { transaction: true })\n\n const recordId = String(resolvedOrganization.id)\n const identifiers = { id: recordId, organizationId: recordId, tenantId }\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: resolvedOrganization,\n identifiers,\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n })\n\n return resolvedOrganization\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tenantId = resolveTenantIdFromEntity(result)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.directory.organization,\n recordId: String(result.id),\n tenantId,\n organizationId: String(result.id),\n })\n return serializeOrganization(result, custom)\n },\n buildLog: async ({ snapshots, result, ctx }) => {\n const { translate } = await resolveTranslations()\n const meta = getUndoMeta(result)\n const beforeSnapshots = snapshots.before as OrganizationSnapshots | undefined\n const beforeRecord = beforeSnapshots?.view ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tenantId = resolveTenantIdFromEntity(result)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.directory.organization,\n recordId: String(result.id),\n tenantId,\n organizationId: String(result.id),\n })\n const after = serializeOrganization(result, custom)\n const changes = buildChanges(beforeRecord, after as Record<string, unknown>, ['name', 'slug', 'logoUrl', 'logoPreserveAspectRatio', 'isActive', 'parentId'])\n const customDiff = diffCustomFieldChanges(beforeRecord?.custom, custom)\n for (const [key, diff] of Object.entries(customDiff)) {\n changes[`cf_${key}`] = diff\n }\n return {\n actionLabel: translate('directory.audit.organizations.update', 'Update organization'),\n resourceKind: 'directory.organization',\n resourceId: String(result.id),\n changes,\n tenantId: after.tenantId,\n payload: {\n undo: {\n before: beforeSnapshots?.undo ?? null,\n after: captureOrganizationSnapshots(result, meta.childParentsAfter ?? [], custom).undo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<OrganizationUndoPayload>(logEntry)\n const before = payload?.before\n const after = payload?.after\n if (!before) return\n const tenantId = before.tenantId\n if (!tenantId) return\n await assertOrganizationUndoTenantAccess(ctx, tenantId)\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let updated: Organization | null = null\n await withAtomicFlush(em, [\n async () => {\n await lockOrganizationHierarchyForTenant(em, tenantId)\n updated = await de.updateOrmEntity({\n entity: Organization,\n where: buildOrganizationUndoTargetFilter(before.id, tenantId),\n apply: (entity) => {\n entity.name = before.name\n if (before.slug !== undefined) entity.slug = before.slug\n if (before.logoUrl !== undefined) entity.logoUrl = before.logoUrl ?? null\n if (before.logoPreserveAspectRatio !== undefined) entity.logoPreserveAspectRatio = !!before.logoPreserveAspectRatio\n entity.isActive = before.isActive\n entity.parentId = before.parentId\n },\n })\n if (!updated) throw new CrudHttpError(404, { error: 'Not found' })\n setInternalTenantId(updated, tenantId)\n const reset = buildCustomFieldResetMap(before.custom, after?.custom)\n if (Object.keys(reset).length) {\n const resetValues = reset as Parameters<DataEngine['setCustomFields']>[0]['values']\n await de.setCustomFields({\n entityId: E.directory.organization,\n recordId: before.id,\n tenantId,\n organizationId: before.id,\n values: resetValues,\n notify: false,\n })\n }\n const childSnapshots = before.childParents\n await restoreChildParents(em, tenantId, childSnapshots)\n await rebuildHierarchyForTenant(em, tenantId)\n },\n ], { transaction: true })\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: updated,\n identifiers: {\n id: before.id,\n tenantId,\n organizationId: before.id,\n },\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n })\n },\n}\n\nconst deleteOrganizationCommand: CommandHandler<{ body: any; query: Record<string, string> }, Organization> = {\n id: 'directory.organizations.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Organization id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const { organization: existing, tenantId } = await loadAuthorizedOrganizationTarget(em, id, ctx)\n const childParentsBefore = tenantId\n ? await loadChildParentSnapshots(em, tenantId, Array.isArray(existing.childIds) ? existing.childIds : [])\n : []\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.directory.organization,\n recordId: String(existing.id),\n tenantId,\n organizationId: String(existing.id),\n })\n return { before: captureOrganizationSnapshots(existing, childParentsBefore, custom) }\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Organization id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const { organization: existing, tenantId } = await loadAuthorizedOrganizationTarget(em, id, ctx)\n\n const parentId = existing.parentId ?? null\n const childSnapshotsBefore = await loadChildParentSnapshots(\n em,\n tenantId,\n Array.isArray(existing.childIds) ? existing.childIds : [],\n )\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n\n let resolvedDeleted!: Organization\n await withAtomicFlush(em, [\n async () => {\n await lockOrganizationHierarchyForTenant(em, tenantId)\n const deleted = await de.deleteOrmEntity({\n entity: Organization,\n where: buildOrganizationMutationFilter(id, tenantId),\n soft: true,\n softDeleteField: 'deletedAt',\n })\n if (!deleted) throw new CrudHttpError(404, { error: 'Not found' })\n setInternalTenantId(deleted, tenantId)\n deleted.isActive = false\n deleted.parentId = null\n resolvedDeleted = deleted\n },\n async () => {\n const deleted = resolvedDeleted\n const childrenFilter: FilterQuery<Organization> = { tenant: tenantId, parentId: id, deletedAt: null }\n const children = await em.find(Organization, childrenFilter)\n const toPersist: Organization[] = []\n for (const child of children) {\n child.parentId = parentId\n toPersist.push(child)\n }\n toPersist.push(deleted)\n if (toPersist.length) em.persist(toPersist)\n setUndoMeta(deleted, { childParentsBefore: childSnapshotsBefore })\n },\n async () => {\n await rebuildHierarchyForTenant(em, tenantId)\n },\n ], { transaction: true })\n\n const identifiers = { id, organizationId: id, tenantId }\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: resolvedDeleted,\n identifiers,\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n })\n\n return resolvedDeleted\n },\n buildLog: async ({ snapshots, input }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshots = snapshots.before as OrganizationSnapshots | undefined\n const beforeSnapshot = beforeSnapshots?.view ?? null\n const beforeUndo = beforeSnapshots?.undo ?? null\n const id = String(input?.body?.id ?? input?.query?.id ?? '')\n const fallbackId = beforeSnapshot?.id ?? null\n const fallbackTenant = beforeSnapshot?.tenantId ?? null\n return {\n actionLabel: translate('directory.audit.organizations.delete', 'Delete organization'),\n resourceKind: 'directory.organization',\n resourceId: id || fallbackId || null,\n snapshotBefore: beforeSnapshot ?? null,\n tenantId: beforeUndo?.tenantId ?? fallbackTenant,\n payload: {\n undo: {\n before: beforeUndo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<OrganizationUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const tenantId = before.tenantId\n if (!tenantId) return\n await assertOrganizationUndoTenantAccess(ctx, tenantId)\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let organization: Organization | null = null\n await withAtomicFlush(em, [\n async () => {\n await lockOrganizationHierarchyForTenant(em, tenantId)\n organization = await em.findOne(\n Organization,\n buildOrganizationUndoTargetFilter(before.id, tenantId),\n )\n if (organization) {\n organization.deletedAt = null\n organization.isActive = before.isActive\n organization.name = before.name\n if (before.slug !== undefined) organization.slug = before.slug\n organization.parentId = before.parentId\n await em.flush()\n if (tenantId) setInternalTenantId(organization, tenantId)\n } else {\n organization = await de.createOrmEntity({\n entity: Organization,\n data: {\n id: before.id,\n name: before.name,\n slug: before.slug ?? null,\n tenant: tenantId ? em.getReference(Tenant, tenantId) : undefined,\n isActive: before.isActive,\n parentId: before.parentId,\n },\n })\n if (tenantId) setInternalTenantId(organization, tenantId)\n }\n if (tenantId) {\n const customValues = buildCustomFieldResetMap(before.custom, undefined)\n if (Object.keys(customValues).length) {\n const resetValues = customValues as Parameters<DataEngine['setCustomFields']>[0]['values']\n await de.setCustomFields({\n entityId: E.directory.organization,\n recordId: before.id,\n tenantId,\n organizationId: before.id,\n values: resetValues,\n notify: false,\n })\n }\n }\n await restoreChildParents(em, tenantId, before.childParents)\n await rebuildHierarchyForTenant(em, tenantId)\n },\n ], { transaction: true })\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: organization,\n identifiers: {\n id: before.id,\n tenantId,\n organizationId: before.id,\n },\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n })\n },\n}\n\ntype OrganizationUndoPayload = {\n before?: OrganizationUndoSnapshot | null\n after?: OrganizationUndoSnapshot | null\n childrenBefore?: ChildParentSnapshot[] | null\n}\n\ntype OrganizationUndoMeta = {\n childParentsBefore?: ChildParentSnapshot[]\n childParentsAfter?: ChildParentSnapshot[]\n}\n\nconst UNDO_META_KEY: unique symbol = Symbol('directory.organization.undoMeta')\n\nfunction getUndoMeta(entity: Organization): OrganizationUndoMeta {\n return (Reflect.get(entity, UNDO_META_KEY) as OrganizationUndoMeta | undefined) ?? {}\n}\n\nfunction setUndoMeta(entity: Organization, meta: Partial<OrganizationUndoMeta>) {\n const current = getUndoMeta(entity)\n Reflect.set(entity, UNDO_META_KEY, { ...current, ...meta })\n}\n\nregisterCommand(createOrganizationCommand)\nregisterCommand(updateOrganizationCommand)\nregisterCommand(deleteOrganizationCommand)\n\nfunction setInternalTenantId(entity: Organization, tenantId: string) {\n Reflect.set(entity, '__tenantId', tenantId)\n}\n\nfunction tenantIdFromContext(ctx: CrudEmitContext<Organization>): string | null {\n return resolveTenantIdFromEntity(ctx.entity) ?? ctx.identifiers.tenantId ?? null\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { CommandHandler, CommandRuntimeContext } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { Organization, Tenant } from '@open-mercato/core/modules/directory/data/entities'\nimport { organizationCreateSchema, organizationUpdateSchema } from '@open-mercato/core/modules/directory/data/validators'\nimport {\n lockOrganizationHierarchyForTenant,\n rebuildHierarchyForTenant,\n} from '@open-mercato/core/modules/directory/lib/hierarchy'\nimport {\n enforceTenantSelection,\n normalizeTenantId,\n resolveIsSuperAdmin,\n} from '@open-mercato/core/modules/auth/lib/tenantAccess'\nimport { E } from '#generated/entities.ids.generated'\nimport type { CrudEmitContext, CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport {\n loadCustomFieldSnapshot,\n buildCustomFieldResetMap,\n diffCustomFieldChanges,\n} from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { extractUndoPayload, reviveSnapshotDates } from '@open-mercato/shared/lib/commands/undo'\nimport { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'\nimport { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'\nimport {\n parseWithCustomFields,\n setCustomFieldsIfAny,\n emitCrudSideEffects,\n emitCrudUndoSideEffects,\n requireId,\n buildChanges,\n} from '@open-mercato/shared/lib/commands/helpers'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport { toOptionalString } from '@open-mercato/shared/lib/string/coerce'\nimport { slugify } from '@open-mercato/shared/lib/slugify'\n\nexport const organizationCrudEvents: CrudEventsConfig = {\n module: 'directory',\n entity: 'organization',\n persistent: true,\n buildPayload: (ctx) => {\n const orgCtx = ctx as CrudEmitContext<Organization>\n return {\n id: orgCtx.identifiers.id,\n tenantId: tenantIdFromContext(orgCtx),\n organizationId: orgCtx.identifiers.id,\n }\n },\n}\n\nexport const organizationCrudIndexer: CrudIndexerConfig = {\n entityType: E.directory.organization,\n buildUpsertPayload: (ctx) => {\n const orgCtx = ctx as CrudEmitContext<Organization>\n return {\n entityType: E.directory.organization,\n recordId: orgCtx.identifiers.id,\n organizationId: orgCtx.identifiers.id,\n tenantId: tenantIdFromContext(orgCtx),\n }\n },\n buildDeletePayload: (ctx) => {\n const orgCtx = ctx as CrudEmitContext<Organization>\n return {\n entityType: E.directory.organization,\n recordId: orgCtx.identifiers.id,\n organizationId: orgCtx.identifiers.id,\n tenantId: tenantIdFromContext(orgCtx),\n }\n },\n}\n\ntype OrganizationTenantShape = {\n __tenantId?: unknown\n tenant?: string | { id?: unknown; getEntity?: () => { id?: unknown } }\n tenantId?: unknown\n tenant_id?: unknown\n}\n\ntype SerializedOrganization = ReturnType<typeof serializeOrganization>\n\ntype ChildParentSnapshot = {\n childId: string\n parentId: string | null\n}\n\ntype OrganizationUndoSnapshot = {\n id: string\n tenantId: string | null\n createdAt?: string | null\n name: string\n slug?: string | null\n logoUrl?: string | null\n logoPreserveAspectRatio?: boolean\n isActive: boolean\n parentId: string | null\n childParents: ChildParentSnapshot[]\n custom?: Record<string, unknown>\n}\n\ntype OrganizationSnapshots = {\n view: SerializedOrganization\n undo: OrganizationUndoSnapshot\n}\n\ntype OrganizationActorScope = {\n isUnrestricted: boolean\n tenantId: string | null\n}\n\ntype AuthorizedOrganizationTarget = {\n organization: Organization\n tenantId: string\n}\n\nexport function resolveTenantIdFromEntity(entity: Organization): string | null {\n const shape = entity as unknown as OrganizationTenantShape\n const cached = toOptionalString(shape.__tenantId)\n if (cached) return cached\n const tenantRef = shape.tenant\n if (typeof tenantRef === 'string') return tenantRef\n if (tenantRef && typeof tenantRef === 'object') {\n const direct = toOptionalString(tenantRef.id)\n if (direct) return direct\n if (typeof tenantRef.getEntity === 'function') {\n const nested = tenantRef.getEntity()\n const nestedId = nested ? toOptionalString(nested.id) : null\n if (nestedId) return nestedId\n }\n }\n const fallback = toOptionalString(shape.tenantId) || toOptionalString(shape.tenant_id)\n return fallback\n}\n\nasync function resolveOrganizationActorScope(ctx: CommandRuntimeContext): Promise<OrganizationActorScope> {\n if (ctx.systemActor === true) return { isUnrestricted: true, tenantId: null }\n const tenantId = normalizeTenantId(ctx.auth?.tenantId ?? null) ?? null\n const isSuperAdmin = await resolveIsSuperAdmin(ctx)\n return { isUnrestricted: isSuperAdmin, tenantId }\n}\n\nfunction buildOrganizationTargetFilter(\n id: string,\n scope: OrganizationActorScope,\n): FilterQuery<Organization> | null {\n if (!scope.isUnrestricted && !scope.tenantId) return null\n return {\n id,\n deletedAt: null,\n ...(scope.isUnrestricted ? {} : { tenant: scope.tenantId }),\n } as FilterQuery<Organization>\n}\n\nasync function loadAuthorizedOrganizationTarget(\n em: EntityManager,\n id: string,\n ctx: CommandRuntimeContext,\n): Promise<AuthorizedOrganizationTarget> {\n const scope = await resolveOrganizationActorScope(ctx)\n const filter = buildOrganizationTargetFilter(id, scope)\n if (!filter) throw new CrudHttpError(404, { error: 'Not found' })\n const organization = await em.findOne(Organization, filter)\n if (!organization) throw new CrudHttpError(404, { error: 'Not found' })\n const tenantId = resolveTenantIdFromEntity(organization)\n if (!tenantId || (!scope.isUnrestricted && tenantId !== scope.tenantId)) {\n throw new CrudHttpError(404, { error: 'Not found' })\n }\n return { organization, tenantId }\n}\n\nfunction buildOrganizationMutationFilter(\n id: string,\n tenantId: string,\n): FilterQuery<Organization> {\n return {\n id,\n deletedAt: null,\n tenant: tenantId,\n } as FilterQuery<Organization>\n}\n\nasync function assertOrganizationUndoTenantAccess(\n ctx: CommandRuntimeContext,\n targetTenantId: string,\n): Promise<void> {\n const scope = await resolveOrganizationActorScope(ctx)\n if (!scope.isUnrestricted && scope.tenantId !== targetTenantId) {\n throw new CrudHttpError(404, { error: 'Not found' })\n }\n}\n\nfunction buildOrganizationUndoTargetFilter(\n id: string,\n tenantId: string,\n): FilterQuery<Organization> {\n return { id, tenant: tenantId } as FilterQuery<Organization>\n}\n\nfunction serializeOrganization(entity: Organization, custom?: Record<string, unknown> | null) {\n return {\n id: String(entity.id),\n tenantId: resolveTenantIdFromEntity(entity),\n name: entity.name,\n slug: entity.slug ?? null,\n logoUrl: entity.logoUrl ?? null,\n logoPreserveAspectRatio: !!entity.logoPreserveAspectRatio,\n isActive: !!entity.isActive,\n parentId: entity.parentId ?? null,\n ancestorIds: Array.isArray(entity.ancestorIds) ? [...entity.ancestorIds] : [],\n childIds: Array.isArray(entity.childIds) ? [...entity.childIds] : [],\n descendantIds: Array.isArray(entity.descendantIds) ? [...entity.descendantIds] : [],\n createdAt: entity.createdAt ? entity.createdAt.toISOString() : null,\n updatedAt: entity.updatedAt ? entity.updatedAt.toISOString() : null,\n ...(custom && Object.keys(custom).length ? { custom } : {}),\n }\n}\n\nfunction captureOrganizationSnapshots(\n entity: Organization,\n childParents: ChildParentSnapshot[],\n custom?: Record<string, unknown> | null\n): OrganizationSnapshots {\n const tenantId = resolveTenantIdFromEntity(entity)\n return {\n view: serializeOrganization(entity, custom),\n undo: {\n id: String(entity.id),\n tenantId,\n createdAt: entity.createdAt ? entity.createdAt.toISOString() : null,\n name: entity.name,\n slug: entity.slug ?? null,\n logoUrl: entity.logoUrl ?? null,\n logoPreserveAspectRatio: !!entity.logoPreserveAspectRatio,\n isActive: !!entity.isActive,\n parentId: entity.parentId ?? null,\n childParents: (childParents ?? []).map((entry) => ({\n childId: String(entry.childId),\n parentId: entry.parentId,\n })),\n ...(custom && Object.keys(custom).length ? { custom } : {}),\n },\n }\n}\n\nasync function loadChildParentSnapshots(\n em: EntityManager,\n tenantId: string | null,\n childIds: Iterable<string>\n): Promise<ChildParentSnapshot[]> {\n if (!tenantId) return []\n const ids = Array.from(new Set(Array.from(childIds ?? []).map((id) => String(id)).filter(Boolean)))\n if (!ids.length) return []\n const filter: FilterQuery<Organization> = {\n tenant: tenantId,\n deletedAt: null,\n id: { $in: ids },\n } as unknown as FilterQuery<Organization>\n const children = await em.find(Organization, filter)\n if (!children.length) return []\n const map = new Map(children.map((child) => [String(child.id), child.parentId ? String(child.parentId) : null]))\n return ids\n .filter((id) => map.has(id))\n .map((id) => ({\n childId: id,\n parentId: map.get(id) ?? null,\n }))\n}\n\nasync function restoreChildParents(em: EntityManager, tenantId: string, snapshots: ChildParentSnapshot[]) {\n if (!snapshots?.length) return\n const ids = Array.from(new Set(snapshots.map((entry) => entry.childId).filter(Boolean)))\n if (!ids.length) return\n const filter: FilterQuery<Organization> = {\n tenant: tenantId,\n deletedAt: null,\n id: { $in: ids },\n } as unknown as FilterQuery<Organization>\n const children = await em.find(Organization, filter)\n if (!children.length) return\n const desired = new Map(snapshots.map((entry) => [entry.childId, entry.parentId ?? null]))\n const toPersist: Organization[] = []\n for (const child of children) {\n const id = String(child.id)\n if (!desired.has(id)) continue\n const nextParent = desired.get(id) ?? null\n if (child.parentId !== nextParent) {\n child.parentId = nextParent\n toPersist.push(child)\n }\n }\n if (toPersist.length) await em.persist(toPersist).flush()\n}\n\nfunction normalizeChildIds(ids: readonly string[], exclude: string[]): string[] {\n const excludeSet = new Set(exclude)\n return Array.from(new Set(ids)).filter((id) => !excludeSet.has(id))\n}\n\nasync function ensureParentExists(em: EntityManager, tenantId: string, parentId: string | null): Promise<void> {\n if (!parentId) return\n const parentFilter: FilterQuery<Organization> = { id: parentId, tenant: tenantId, deletedAt: null }\n const parent = await em.findOne(Organization, parentFilter)\n if (!parent) throw new CrudHttpError(400, { error: 'Parent not found' })\n}\n\nasync function ensureChildrenValid(em: EntityManager, tenantId: string, childIds: string[]): Promise<void> {\n if (!childIds.length) return\n const childFilter: FilterQuery<Organization> = { id: { $in: childIds }, tenant: tenantId, deletedAt: null }\n const children = await em.find(Organization, childFilter)\n if (children.length !== childIds.length) throw new CrudHttpError(400, { error: 'Invalid child assignment' })\n}\n\nasync function assignChildren(\n em: EntityManager,\n tenantId: string,\n recordId: string,\n desiredChildIds: Iterable<string>\n): Promise<void> {\n const targetIds = Array.from(new Set(desiredChildIds)).filter((id) => id !== recordId)\n if (!targetIds.length) return\n const filter: FilterQuery<Organization> = { tenant: tenantId, deletedAt: null, id: { $in: targetIds } }\n const children = await em.find(Organization, filter)\n const toPersist: Organization[] = []\n for (const child of children) {\n if (String(child.id) === recordId) continue\n if (child.parentId !== recordId) {\n child.parentId = recordId\n toPersist.push(child)\n }\n }\n if (toPersist.length) await em.persist(toPersist).flush()\n}\n\nasync function clearRemovedChildren(em: EntityManager, tenantId: string, recordId: string, desiredChildIds: Set<string>): Promise<void> {\n const currentFilter: FilterQuery<Organization> = { tenant: tenantId, parentId: recordId, deletedAt: null }\n const current = await em.find(Organization, currentFilter)\n const toPersist = current.filter((child) => !desiredChildIds.has(String(child.id)))\n if (!toPersist.length) return\n for (const child of toPersist) child.parentId = null\n await em.persist(toPersist).flush()\n}\n\nasync function resolveUniqueSlug(em: EntityManager, tenantId: string, baseSlug: string, excludeId?: string): Promise<string> {\n let candidate = baseSlug\n let suffix = 0\n const maxAttempts = 50\n while (suffix < maxAttempts) {\n const filter: FilterQuery<Organization> = {\n tenant: tenantId,\n slug: candidate,\n } as unknown as FilterQuery<Organization>\n const existing = await em.findOne(Organization, filter)\n if (!existing || (excludeId && String(existing.id) === excludeId)) return candidate\n suffix += 1\n candidate = `${baseSlug}-${suffix}`\n }\n return `${baseSlug}-${Date.now()}`\n}\n\nconst createOrganizationCommand: CommandHandler<Record<string, unknown>, Organization> = {\n id: 'directory.organizations.create',\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(organizationCreateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const tenantId = await enforceTenantSelection(ctx, parsed.tenantId)\n if (!tenantId) throw new CrudHttpError(400, { error: 'Tenant scope required' })\n\n const parentId = parsed.parentId ?? null\n const childIds = normalizeChildIds(parsed.childIds ?? [], parentId ? [parentId] : [])\n if (parentId && childIds.includes(parentId)) throw new CrudHttpError(400, { error: 'Child cannot equal parent' })\n\n const tenantRef = em.getReference(Tenant, tenantId)\n const baseSlug = parsed.slug ? parsed.slug : slugify(parsed.name)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n\n let organization!: Organization\n await withAtomicFlush(em, [\n async () => {\n await lockOrganizationHierarchyForTenant(em, tenantId)\n if (parentId) {\n await ensureParentExists(em, tenantId, parentId)\n }\n await ensureChildrenValid(em, tenantId, childIds)\n const childParentsBefore = await loadChildParentSnapshots(em, tenantId, childIds)\n const slug = baseSlug ? await resolveUniqueSlug(em, tenantId, baseSlug) : null\n organization = await de.createOrmEntity({\n entity: Organization,\n data: {\n tenant: tenantRef,\n name: parsed.name,\n slug,\n logoUrl: parsed.logoUrl ?? null,\n logoPreserveAspectRatio: parsed.logoPreserveAspectRatio ?? false,\n isActive: parsed.isActive ?? true,\n parentId,\n },\n })\n setInternalTenantId(organization, tenantId)\n const recordId = String(organization.id)\n\n if (childIds.length) {\n await assignChildren(em, tenantId, recordId, childIds)\n }\n const childParentsAfter = await loadChildParentSnapshots(em, tenantId, childIds)\n setUndoMeta(organization, { childParentsBefore, childParentsAfter })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.directory.organization,\n recordId,\n tenantId,\n organizationId: recordId,\n values: custom,\n })\n\n await rebuildHierarchyForTenant(em, tenantId)\n },\n ], { transaction: true })\n\n const recordId = String(organization.id)\n const identifiers = { id: recordId, organizationId: recordId, tenantId }\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: organization,\n identifiers,\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n })\n\n return organization\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tenantId = resolveTenantIdFromEntity(result)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.directory.organization,\n recordId: String(result.id),\n tenantId,\n organizationId: String(result.id),\n })\n return serializeOrganization(result, custom)\n },\n buildLog: async ({ result, ctx }) => {\n const { translate } = await resolveTranslations()\n const meta = getUndoMeta(result)\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tenantId = resolveTenantIdFromEntity(result)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.directory.organization,\n recordId: String(result.id),\n tenantId,\n organizationId: String(result.id),\n })\n const afterSnapshots = captureOrganizationSnapshots(result, meta.childParentsAfter ?? [], custom)\n return {\n actionLabel: translate('directory.audit.organizations.create', 'Create organization'),\n resourceKind: 'directory.organization',\n resourceId: String(result.id),\n tenantId,\n snapshotAfter: afterSnapshots.view,\n payload: {\n undo: {\n after: afterSnapshots.undo,\n childrenBefore: meta.childParentsBefore ?? [],\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<OrganizationUndoPayload>(logEntry)\n const after = payload?.after\n const childrenBefore = payload?.childrenBefore ?? []\n if (!after) return\n const tenantId = after.tenantId\n if (!tenantId) return\n await assertOrganizationUndoTenantAccess(ctx, tenantId)\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let organization: Organization | null = null\n await withAtomicFlush(em, [\n async () => {\n await lockOrganizationHierarchyForTenant(em, tenantId)\n await restoreChildParents(em, tenantId, childrenBefore)\n if (after.custom && Object.keys(after.custom).length) {\n const reset = buildCustomFieldResetMap(undefined, after.custom)\n if (Object.keys(reset).length) {\n const resetValues = reset as Parameters<DataEngine['setCustomFields']>[0]['values']\n await de.setCustomFields({\n entityId: E.directory.organization,\n recordId: after.id,\n tenantId,\n organizationId: after.id,\n values: resetValues,\n notify: false,\n })\n }\n }\n organization = await de.deleteOrmEntity({\n entity: Organization,\n where: buildOrganizationMutationFilter(after.id, tenantId),\n soft: true,\n softDeleteField: 'deletedAt',\n })\n if (organization) {\n organization.isActive = false\n organization.parentId = null\n await em.flush()\n }\n await rebuildHierarchyForTenant(em, tenantId)\n },\n ], { transaction: true })\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: organization,\n identifiers: { id: after.id, tenantId, organizationId: after.id },\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n })\n },\n redo: async ({ logEntry, ctx }) => {\n const after = resolveRedoSnapshot<OrganizationUndoSnapshot>(logEntry)\n if (!after) throw new CrudHttpError(400, { error: '[internal] redo snapshot unavailable for organization create' })\n const tenantId = after.tenantId\n if (!tenantId) throw new CrudHttpError(400, { error: '[internal] redo snapshot missing tenant for organization create' })\n await assertOrganizationUndoTenantAccess(ctx, tenantId)\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let organization!: Organization\n await withAtomicFlush(em, [\n async () => {\n await lockOrganizationHierarchyForTenant(em, tenantId)\n const existing = await em.findOne(Organization, buildOrganizationUndoTargetFilter(after.id, tenantId))\n if (existing) {\n existing.deletedAt = null\n existing.name = after.name\n if (after.slug !== undefined) existing.slug = after.slug ?? null\n if (after.logoUrl !== undefined) existing.logoUrl = after.logoUrl ?? null\n if (after.logoPreserveAspectRatio !== undefined) existing.logoPreserveAspectRatio = !!after.logoPreserveAspectRatio\n existing.isActive = after.isActive\n existing.parentId = after.parentId\n await em.flush()\n organization = existing\n } else {\n const snapshotAfter = logEntry.snapshotAfter\n const createdAt = after.createdAt ?? (\n snapshotAfter && typeof snapshotAfter === 'object' && 'createdAt' in snapshotAfter\n ? snapshotAfter.createdAt\n : undefined\n )\n const creationTime = typeof createdAt === 'string' || createdAt instanceof Date\n ? reviveSnapshotDates({ createdAt }, ['createdAt'])\n : {}\n organization = await de.createOrmEntity({\n entity: Organization,\n data: {\n id: after.id,\n ...creationTime,\n name: after.name,\n slug: after.slug ?? null,\n logoUrl: after.logoUrl ?? null,\n logoPreserveAspectRatio: after.logoPreserveAspectRatio ?? false,\n tenant: em.getReference(Tenant, tenantId),\n isActive: after.isActive,\n parentId: after.parentId,\n },\n })\n }\n setInternalTenantId(organization, tenantId)\n\n if (after.custom && Object.keys(after.custom).length) {\n const reset = buildCustomFieldResetMap(after.custom, undefined)\n if (Object.keys(reset).length) {\n const resetValues = reset as Parameters<DataEngine['setCustomFields']>[0]['values']\n await de.setCustomFields({\n entityId: E.directory.organization,\n recordId: after.id,\n tenantId,\n organizationId: after.id,\n values: resetValues,\n notify: false,\n })\n }\n }\n\n await assignChildren(em, tenantId, after.id, after.childParents.map((entry) => entry.childId))\n await rebuildHierarchyForTenant(em, tenantId)\n },\n ], { transaction: true })\n\n const identifiers = { id: after.id, organizationId: after.id, tenantId }\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: organization,\n identifiers,\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n })\n\n return organization\n },\n}\n\nconst updateOrganizationCommand: CommandHandler<Record<string, unknown>, Organization> = {\n id: 'directory.organizations.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(organizationUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const { organization: current, tenantId } = await loadAuthorizedOrganizationTarget(em, parsed.id, ctx)\n const currentChildIds = Array.isArray(current.childIds) ? current.childIds : []\n const requestedChildIds = Array.isArray(parsed.childIds) ? parsed.childIds : []\n const combinedChildIds = new Set<string>([...currentChildIds.map(String), ...requestedChildIds.map(String)])\n const childParentsBefore = tenantId\n ? await loadChildParentSnapshots(em, tenantId, combinedChildIds)\n : []\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.directory.organization,\n recordId: String(current.id),\n tenantId,\n organizationId: String(current.id),\n })\n return { before: captureOrganizationSnapshots(current, childParentsBefore, custom) }\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(organizationUpdateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const { tenantId } = await loadAuthorizedOrganizationTarget(em, parsed.id, ctx)\n\n const parentProvided = parsed.parentId !== undefined\n const childrenProvided = parsed.childIds !== undefined\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n\n let resolvedOrganization!: Organization\n await withAtomicFlush(em, [\n async () => {\n await lockOrganizationHierarchyForTenant(em, tenantId)\n const existing = await em.findOne(\n Organization,\n buildOrganizationMutationFilter(parsed.id, tenantId),\n { refresh: true },\n )\n if (!existing) throw new CrudHttpError(404, { error: 'Not found' })\n\n const parentId = parentProvided ? parsed.parentId ?? null : existing.parentId ?? null\n if (parentProvided && parentId) {\n if (parentId === parsed.id) throw new CrudHttpError(400, { error: 'Organization cannot be its own parent' })\n if (Array.isArray(existing.descendantIds) && existing.descendantIds.includes(parentId)) {\n throw new CrudHttpError(400, { error: 'Cannot assign descendant as parent' })\n }\n await ensureParentExists(em, tenantId, parentId)\n }\n\n const normalizedChildIds = normalizeChildIds(parsed.childIds ?? [], [parsed.id, parentProvided ? parentId ?? '' : ''])\n if (normalizedChildIds.some((id) => id === parentId)) throw new CrudHttpError(400, { error: 'Child cannot equal parent' })\n if (Array.isArray(existing.ancestorIds) && normalizedChildIds.some((id) => existing.ancestorIds.includes(id))) {\n throw new CrudHttpError(400, { error: 'Cannot assign ancestor as child' })\n }\n\n if (normalizedChildIds.length) {\n await ensureChildrenValid(em, tenantId, normalizedChildIds)\n const childFilter = {\n tenant: tenantId,\n deletedAt: null,\n id: { $in: normalizedChildIds },\n } as unknown as FilterQuery<Organization>\n const children = await em.find(Organization, childFilter)\n for (const child of children) {\n if (Array.isArray(child.descendantIds) && child.descendantIds.includes(parsed.id)) {\n throw new CrudHttpError(400, { error: 'Cannot assign descendant cycle' })\n }\n }\n }\n\n const combinedChildIds = new Set<string>([\n ...normalizedChildIds.map(String),\n ...(Array.isArray(existing.childIds) ? existing.childIds.map(String) : []),\n ])\n const childParentsBefore = await loadChildParentSnapshots(em, tenantId, combinedChildIds)\n\n let resolvedSlug: string | null | undefined\n if (parsed.slug !== undefined) {\n resolvedSlug = parsed.slug ? await resolveUniqueSlug(em, tenantId, parsed.slug, parsed.id) : parsed.slug\n }\n\n const organization = await de.updateOrmEntity({\n entity: Organization,\n where: buildOrganizationMutationFilter(parsed.id, tenantId),\n apply: (entity) => {\n if (parsed.name !== undefined) entity.name = parsed.name\n if (resolvedSlug !== undefined) entity.slug = resolvedSlug\n if (parsed.logoUrl !== undefined) entity.logoUrl = parsed.logoUrl ?? null\n if (parsed.logoPreserveAspectRatio !== undefined) entity.logoPreserveAspectRatio = parsed.logoPreserveAspectRatio\n if (parsed.isActive !== undefined) entity.isActive = parsed.isActive\n if (parentProvided) entity.parentId = parentId\n },\n })\n if (!organization) throw new CrudHttpError(404, { error: 'Not found' })\n setInternalTenantId(organization, tenantId)\n\n const recordId = String(organization.id)\n const desiredChildIds = new Set(normalizedChildIds.filter((id) => id !== recordId))\n if (childrenProvided) {\n await clearRemovedChildren(em, tenantId, recordId, desiredChildIds)\n await assignChildren(em, tenantId, recordId, desiredChildIds)\n }\n const childParentsAfter = await loadChildParentSnapshots(em, tenantId, combinedChildIds)\n setUndoMeta(organization, { childParentsBefore, childParentsAfter })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.directory.organization,\n recordId,\n tenantId,\n organizationId: recordId,\n values: custom,\n })\n\n await rebuildHierarchyForTenant(em, tenantId)\n resolvedOrganization = organization\n },\n ], { transaction: true })\n\n const recordId = String(resolvedOrganization.id)\n const identifiers = { id: recordId, organizationId: recordId, tenantId }\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: resolvedOrganization,\n identifiers,\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n })\n\n return resolvedOrganization\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tenantId = resolveTenantIdFromEntity(result)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.directory.organization,\n recordId: String(result.id),\n tenantId,\n organizationId: String(result.id),\n })\n return serializeOrganization(result, custom)\n },\n buildLog: async ({ snapshots, result, ctx }) => {\n const { translate } = await resolveTranslations()\n const meta = getUndoMeta(result)\n const beforeSnapshots = snapshots.before as OrganizationSnapshots | undefined\n const beforeRecord = beforeSnapshots?.view ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const tenantId = resolveTenantIdFromEntity(result)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.directory.organization,\n recordId: String(result.id),\n tenantId,\n organizationId: String(result.id),\n })\n const after = serializeOrganization(result, custom)\n const changes = buildChanges(beforeRecord, after as Record<string, unknown>, ['name', 'slug', 'logoUrl', 'logoPreserveAspectRatio', 'isActive', 'parentId'])\n const customDiff = diffCustomFieldChanges(beforeRecord?.custom, custom)\n for (const [key, diff] of Object.entries(customDiff)) {\n changes[`cf_${key}`] = diff\n }\n return {\n actionLabel: translate('directory.audit.organizations.update', 'Update organization'),\n resourceKind: 'directory.organization',\n resourceId: String(result.id),\n changes,\n tenantId: after.tenantId,\n payload: {\n undo: {\n before: beforeSnapshots?.undo ?? null,\n after: captureOrganizationSnapshots(result, meta.childParentsAfter ?? [], custom).undo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<OrganizationUndoPayload>(logEntry)\n const before = payload?.before\n const after = payload?.after\n if (!before) return\n const tenantId = before.tenantId\n if (!tenantId) return\n await assertOrganizationUndoTenantAccess(ctx, tenantId)\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let updated: Organization | null = null\n await withAtomicFlush(em, [\n async () => {\n await lockOrganizationHierarchyForTenant(em, tenantId)\n updated = await de.updateOrmEntity({\n entity: Organization,\n where: buildOrganizationUndoTargetFilter(before.id, tenantId),\n apply: (entity) => {\n entity.name = before.name\n if (before.slug !== undefined) entity.slug = before.slug\n if (before.logoUrl !== undefined) entity.logoUrl = before.logoUrl ?? null\n if (before.logoPreserveAspectRatio !== undefined) entity.logoPreserveAspectRatio = !!before.logoPreserveAspectRatio\n entity.isActive = before.isActive\n entity.parentId = before.parentId\n },\n })\n if (!updated) throw new CrudHttpError(404, { error: 'Not found' })\n setInternalTenantId(updated, tenantId)\n const reset = buildCustomFieldResetMap(before.custom, after?.custom)\n if (Object.keys(reset).length) {\n const resetValues = reset as Parameters<DataEngine['setCustomFields']>[0]['values']\n await de.setCustomFields({\n entityId: E.directory.organization,\n recordId: before.id,\n tenantId,\n organizationId: before.id,\n values: resetValues,\n notify: false,\n })\n }\n const childSnapshots = before.childParents\n await restoreChildParents(em, tenantId, childSnapshots)\n await rebuildHierarchyForTenant(em, tenantId)\n },\n ], { transaction: true })\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: updated,\n identifiers: {\n id: before.id,\n tenantId,\n organizationId: before.id,\n },\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n })\n },\n}\n\nconst deleteOrganizationCommand: CommandHandler<{ body: any; query: Record<string, string> }, Organization> = {\n id: 'directory.organizations.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Organization id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const { organization: existing, tenantId } = await loadAuthorizedOrganizationTarget(em, id, ctx)\n const childParentsBefore = tenantId\n ? await loadChildParentSnapshots(em, tenantId, Array.isArray(existing.childIds) ? existing.childIds : [])\n : []\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.directory.organization,\n recordId: String(existing.id),\n tenantId,\n organizationId: String(existing.id),\n })\n return { before: captureOrganizationSnapshots(existing, childParentsBefore, custom) }\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Organization id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const { organization: existing, tenantId } = await loadAuthorizedOrganizationTarget(em, id, ctx)\n\n const parentId = existing.parentId ?? null\n const childSnapshotsBefore = await loadChildParentSnapshots(\n em,\n tenantId,\n Array.isArray(existing.childIds) ? existing.childIds : [],\n )\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n\n let resolvedDeleted!: Organization\n await withAtomicFlush(em, [\n async () => {\n await lockOrganizationHierarchyForTenant(em, tenantId)\n const deleted = await de.deleteOrmEntity({\n entity: Organization,\n where: buildOrganizationMutationFilter(id, tenantId),\n soft: true,\n softDeleteField: 'deletedAt',\n })\n if (!deleted) throw new CrudHttpError(404, { error: 'Not found' })\n setInternalTenantId(deleted, tenantId)\n deleted.isActive = false\n deleted.parentId = null\n resolvedDeleted = deleted\n },\n async () => {\n const deleted = resolvedDeleted\n const childrenFilter: FilterQuery<Organization> = { tenant: tenantId, parentId: id, deletedAt: null }\n const children = await em.find(Organization, childrenFilter)\n const toPersist: Organization[] = []\n for (const child of children) {\n child.parentId = parentId\n toPersist.push(child)\n }\n toPersist.push(deleted)\n if (toPersist.length) em.persist(toPersist)\n setUndoMeta(deleted, { childParentsBefore: childSnapshotsBefore })\n },\n async () => {\n await rebuildHierarchyForTenant(em, tenantId)\n },\n ], { transaction: true })\n\n const identifiers = { id, organizationId: id, tenantId }\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: resolvedDeleted,\n identifiers,\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n })\n\n return resolvedDeleted\n },\n buildLog: async ({ snapshots, input }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshots = snapshots.before as OrganizationSnapshots | undefined\n const beforeSnapshot = beforeSnapshots?.view ?? null\n const beforeUndo = beforeSnapshots?.undo ?? null\n const id = String(input?.body?.id ?? input?.query?.id ?? '')\n const fallbackId = beforeSnapshot?.id ?? null\n const fallbackTenant = beforeSnapshot?.tenantId ?? null\n return {\n actionLabel: translate('directory.audit.organizations.delete', 'Delete organization'),\n resourceKind: 'directory.organization',\n resourceId: id || fallbackId || null,\n snapshotBefore: beforeSnapshot ?? null,\n tenantId: beforeUndo?.tenantId ?? fallbackTenant,\n payload: {\n undo: {\n before: beforeUndo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const payload = extractUndoPayload<OrganizationUndoPayload>(logEntry)\n const before = payload?.before\n if (!before) return\n const tenantId = before.tenantId\n if (!tenantId) return\n await assertOrganizationUndoTenantAccess(ctx, tenantId)\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let organization: Organization | null = null\n await withAtomicFlush(em, [\n async () => {\n await lockOrganizationHierarchyForTenant(em, tenantId)\n organization = await em.findOne(\n Organization,\n buildOrganizationUndoTargetFilter(before.id, tenantId),\n )\n if (organization) {\n organization.deletedAt = null\n organization.isActive = before.isActive\n organization.name = before.name\n if (before.slug !== undefined) organization.slug = before.slug\n organization.parentId = before.parentId\n await em.flush()\n if (tenantId) setInternalTenantId(organization, tenantId)\n } else {\n organization = await de.createOrmEntity({\n entity: Organization,\n data: {\n id: before.id,\n name: before.name,\n slug: before.slug ?? null,\n tenant: tenantId ? em.getReference(Tenant, tenantId) : undefined,\n isActive: before.isActive,\n parentId: before.parentId,\n },\n })\n if (tenantId) setInternalTenantId(organization, tenantId)\n }\n if (tenantId) {\n const customValues = buildCustomFieldResetMap(before.custom, undefined)\n if (Object.keys(customValues).length) {\n const resetValues = customValues as Parameters<DataEngine['setCustomFields']>[0]['values']\n await de.setCustomFields({\n entityId: E.directory.organization,\n recordId: before.id,\n tenantId,\n organizationId: before.id,\n values: resetValues,\n notify: false,\n })\n }\n }\n await restoreChildParents(em, tenantId, before.childParents)\n await rebuildHierarchyForTenant(em, tenantId)\n },\n ], { transaction: true })\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: organization,\n identifiers: {\n id: before.id,\n tenantId,\n organizationId: before.id,\n },\n events: organizationCrudEvents,\n indexer: organizationCrudIndexer,\n })\n },\n}\n\ntype OrganizationUndoPayload = {\n before?: OrganizationUndoSnapshot | null\n after?: OrganizationUndoSnapshot | null\n childrenBefore?: ChildParentSnapshot[] | null\n}\n\ntype OrganizationUndoMeta = {\n childParentsBefore?: ChildParentSnapshot[]\n childParentsAfter?: ChildParentSnapshot[]\n}\n\nconst UNDO_META_KEY: unique symbol = Symbol('directory.organization.undoMeta')\n\nfunction getUndoMeta(entity: Organization): OrganizationUndoMeta {\n return (Reflect.get(entity, UNDO_META_KEY) as OrganizationUndoMeta | undefined) ?? {}\n}\n\nfunction setUndoMeta(entity: Organization, meta: Partial<OrganizationUndoMeta>) {\n const current = getUndoMeta(entity)\n Reflect.set(entity, UNDO_META_KEY, { ...current, ...meta })\n}\n\nregisterCommand(createOrganizationCommand)\nregisterCommand(updateOrganizationCommand)\nregisterCommand(deleteOrganizationCommand)\n\nfunction setInternalTenantId(entity: Organization, tenantId: string) {\n Reflect.set(entity, '__tenantId', tenantId)\n}\n\nfunction tenantIdFromContext(ctx: CrudEmitContext<Organization>): string | null {\n return resolveTenantIdFromEntity(ctx.entity) ?? ctx.identifiers.tenantId ?? null\n}\n"],
5
+ "mappings": "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",
6
6
  "names": ["recordId"]
7
7
  }
@@ -109,6 +109,41 @@ async function readLiveEncryptionMapVersion(em, input) {
109
109
  const current = rows[0]?.updated_at;
110
110
  return current ? toDate(current) : null;
111
111
  }
112
+ async function appendMissingFieldsToOrganizationMaps(em, input) {
113
+ const updatedOrganizationIds = /* @__PURE__ */ new Set();
114
+ for (const rule of input.fields) {
115
+ const field = typeof rule?.field === "string" ? rule.field.trim() : "";
116
+ if (!field) continue;
117
+ const appended = rule.hashField ? { field, hashField: rule.hashField } : { field };
118
+ const rows = await em.execute(
119
+ `
120
+ update "encryption_maps"
121
+ set "fields_json" = coalesce("fields_json", '[]'::jsonb) || ?::jsonb, "updated_at" = now()
122
+ where "entity_id" = ?
123
+ and "tenant_id" = ?
124
+ and "organization_id" is not null
125
+ and "deleted_at" is null
126
+ and jsonb_typeof(coalesce("fields_json", '[]'::jsonb)) = 'array'
127
+ and not exists (
128
+ select 1
129
+ from jsonb_array_elements(case when jsonb_typeof("fields_json") = 'array' then "fields_json" else '[]'::jsonb end) as existing_rule
130
+ where existing_rule->>'field' = ?
131
+ )
132
+ returning "organization_id"
133
+ `,
134
+ [JSON.stringify([appended]), input.entityId, input.tenantId, field]
135
+ );
136
+ for (const row of rows) {
137
+ if (row?.organization_id) updatedOrganizationIds.add(String(row.organization_id));
138
+ }
139
+ }
140
+ if (updatedOrganizationIds.size) forgetEncryptionPolicyMemo(em);
141
+ return Array.from(updatedOrganizationIds, (organizationId) => ({
142
+ entityId: input.entityId,
143
+ tenantId: input.tenantId,
144
+ organizationId
145
+ }));
146
+ }
112
147
  async function upsertEncryptionMapSpecs(em, tenantId, organizationId, specs, logger = () => {
113
148
  }, onMaterializedScope) {
114
149
  const materializedScopes = [];
@@ -128,6 +163,17 @@ async function upsertEncryptionMapSpecs(em, tenantId, organizationId, specs, log
128
163
  materializedScopes.push(materializedScope);
129
164
  await onMaterializedScope?.(materializedScope);
130
165
  logger(`\u{1F512} Seeded encryption map for ${spec.entityId} \u2728`);
166
+ if (organizationId !== null) continue;
167
+ const organizationScopes = await appendMissingFieldsToOrganizationMaps(em, {
168
+ entityId: spec.entityId,
169
+ tenantId,
170
+ fields: spec.fields
171
+ });
172
+ for (const organizationScope of organizationScopes) {
173
+ materializedScopes.push(organizationScope);
174
+ await onMaterializedScope?.(organizationScope);
175
+ logger(`\u{1F512} Added missing fields to the ${spec.entityId} map of organization ${organizationScope.organizationId}`);
176
+ }
131
177
  }
132
178
  return materializedScopes;
133
179
  }
@@ -152,6 +198,7 @@ async function invalidateEncryptionMapScopes(encryptionService, scopes) {
152
198
  }
153
199
  export {
154
200
  EncryptionMapVersionConflictError,
201
+ appendMissingFieldsToOrganizationMaps,
155
202
  invalidateEncryptionMapScopes,
156
203
  mergeEncryptionMapFields,
157
204
  resolveCanonicalEncryptionMap,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/entities/lib/encryption-maps.ts"],
4
- "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { ModuleEncryptionFieldRule, ModuleEncryptionMap } from '@open-mercato/shared/modules/encryption'\nimport { forgetEncryptionPolicyMemo } from '@open-mercato/shared/lib/encryption/policyMemo'\nimport { getDefaultEncryptionMaps, type Module } from '@open-mercato/shared/modules/registry'\nimport type { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\n\nexport type EncryptionMapLike = {\n id: string\n entityId: string\n tenantId?: string | null\n organizationId?: string | null\n fieldsJson?: ModuleEncryptionFieldRule[] | null\n isActive: boolean\n createdAt: Date\n updatedAt: Date\n deletedAt?: Date | null\n}\n\nexport type CanonicalEncryptionMap = EncryptionMapLike & {\n fieldsJson: ModuleEncryptionFieldRule[]\n}\n\nexport type UpsertCanonicalEncryptionMapInput = {\n entityId: string\n tenantId: string | null\n organizationId: string | null\n fields: ModuleEncryptionFieldRule[]\n isActive: boolean\n /**\n * When set, an existing live row is only updated while its `updated_at` still equals this\n * version (millisecond precision, matching the optimistic-lock token). A concurrent write that\n * moved the version makes the upsert throw {@link EncryptionMapVersionConflictError} instead of\n * overwriting it.\n */\n expectedUpdatedAt?: Date | null\n}\n\nexport type UpsertedEncryptionMap = {\n id: string\n updatedAt: Date\n}\n\nexport class EncryptionMapVersionConflictError extends Error {\n constructor(\n readonly expectedUpdatedAt: Date,\n readonly currentUpdatedAt: Date | null,\n ) {\n super('[internal] Encryption map version changed before the conditional update')\n this.name = 'EncryptionMapVersionConflictError'\n }\n}\n\nfunction toDate(value: Date | string): Date {\n return value instanceof Date ? value : new Date(value)\n}\n\nfunction orderedMaps<T extends EncryptionMapLike>(records: readonly T[]): T[] {\n return [...records].sort((left, right) => {\n const createdDelta = left.createdAt.getTime() - right.createdAt.getTime()\n return createdDelta || left.id.localeCompare(right.id)\n })\n}\n\nexport function mergeEncryptionMapFields(records: readonly EncryptionMapLike[]): ModuleEncryptionFieldRule[] {\n const merged: ModuleEncryptionFieldRule[] = []\n const byField = new Map<string, ModuleEncryptionFieldRule>()\n for (const record of orderedMaps(records)) {\n for (const rule of record.fieldsJson ?? []) {\n const field = typeof rule?.field === 'string' ? rule.field.trim() : ''\n if (!field) continue\n const existing = byField.get(field)\n if (!existing) {\n const copy = { field, hashField: typeof rule.hashField === 'string' ? rule.hashField : null }\n byField.set(field, copy)\n merged.push(copy)\n continue\n }\n if (!existing.hashField && typeof rule.hashField === 'string' && rule.hashField) {\n existing.hashField = rule.hashField\n }\n }\n }\n return merged\n}\n\nexport function resolveCanonicalEncryptionMap<T extends EncryptionMapLike>(records: readonly T[]): CanonicalEncryptionMap | null {\n const ordered = orderedMaps(records)\n const canonical = ordered[0]\n if (!canonical) return null\n const active = ordered.filter((record) => record.isActive)\n return {\n ...canonical,\n isActive: active.length > 0,\n fieldsJson: mergeEncryptionMapFields(active.length > 0 ? active : ordered),\n }\n}\n\nexport async function upsertCanonicalEncryptionMap(\n em: EntityManager,\n input: UpsertCanonicalEncryptionMapInput,\n): Promise<UpsertedEncryptionMap> {\n const expectedUpdatedAt = input.expectedUpdatedAt ?? null\n const versionCondition = expectedUpdatedAt\n ? `where date_trunc('milliseconds', \"encryption_maps\".\"updated_at\") = date_trunc('milliseconds', ?::timestamptz)`\n : ''\n const params: unknown[] = [\n input.entityId,\n input.tenantId,\n input.organizationId,\n JSON.stringify(input.fields),\n input.isActive,\n ]\n if (expectedUpdatedAt) params.push(expectedUpdatedAt.toISOString())\n const rows = await em.execute<Array<{ id: string; updated_at: Date | string }>>(\n `\n insert into \"encryption_maps\"\n (\"id\", \"entity_id\", \"tenant_id\", \"organization_id\", \"fields_json\", \"is_active\", \"created_at\", \"updated_at\")\n values\n (gen_random_uuid(), ?, ?, ?, ?::jsonb, ?, now(), now())\n on conflict (\"entity_id\", \"tenant_id\", \"organization_id\") where \"deleted_at\" is null\n do update set\n \"fields_json\" = excluded.\"fields_json\",\n \"is_active\" = excluded.\"is_active\",\n \"updated_at\" = now()\n ${versionCondition}\n returning \"id\", \"updated_at\"\n `,\n params,\n )\n forgetEncryptionPolicyMemo(em)\n const saved = rows[0]\n if (!saved?.id || !saved.updated_at) {\n if (expectedUpdatedAt) {\n throw new EncryptionMapVersionConflictError(\n expectedUpdatedAt,\n await readLiveEncryptionMapVersion(em, input),\n )\n }\n throw new Error('[internal] Encryption map upsert did not return the canonical row')\n }\n return {\n id: saved.id,\n updatedAt: toDate(saved.updated_at),\n }\n}\n\nasync function readLiveEncryptionMapVersion(\n em: EntityManager,\n input: UpsertCanonicalEncryptionMapInput,\n): Promise<Date | null> {\n const rows = await em.execute<Array<{ updated_at: Date | string | null }>>(\n `\n select \"updated_at\"\n from \"encryption_maps\"\n where \"entity_id\" = ?\n and \"tenant_id\" is not distinct from ?\n and \"organization_id\" is not distinct from ?\n and \"deleted_at\" is null\n order by \"created_at\" asc, \"id\" asc\n limit 1\n `,\n [input.entityId, input.tenantId, input.organizationId],\n )\n const current = rows[0]?.updated_at\n return current ? toDate(current) : null\n}\n\n// Idempotently upsert a specific set of encryption-map specs for one (tenant, org) scope. Exported so\n// upgrade actions can backfill a newly-added encrypted entity for pre-existing tenants (whose maps were\n// seeded once at tenant creation and never re-run) without depending on the full CLI module registry.\nexport async function upsertEncryptionMapSpecs(\n em: EntityManager,\n tenantId: string,\n organizationId: string | null,\n specs: ModuleEncryptionMap[],\n logger: (msg: string) => void = () => {},\n onMaterializedScope?: (scope: { entityId: string; tenantId: string; organizationId: string | null }) => void | Promise<void>,\n): Promise<Array<{ entityId: string; tenantId: string; organizationId: string | null }>> {\n const materializedScopes: Array<{ entityId: string; tenantId: string; organizationId: string | null }> = []\n for (const spec of specs) {\n if (spec.keyScope === 'system') {\n logger(`Skipping ${spec.entityId}: system-scoped map, resolved from module code rather than a tenant row.`)\n continue\n }\n await upsertCanonicalEncryptionMap(em, {\n entityId: spec.entityId,\n tenantId,\n organizationId,\n fields: spec.fields,\n isActive: true,\n })\n const materializedScope = { entityId: spec.entityId, tenantId, organizationId }\n materializedScopes.push(materializedScope)\n await onMaterializedScope?.(materializedScope)\n logger(`\uD83D\uDD12 Seeded encryption map for ${spec.entityId} \u2728`)\n }\n return materializedScopes\n}\n\n// Exported so other modules (e.g. the directory subscribers that seed maps for a\n// newly-created organization) can seed the default module encryption maps for one\n// (tenant, org) scope. Callers resolve their own module list: a CLI command uses\n// `getCliModules()`/`getModules()` (cli.ts is an allowed CLI-registry entrypoint), a runtime\n// subscriber MUST use the app registry (`getModules()`) directly \u2014 this lib never reads the\n// CLI-only registry itself (`cli-registry-boundary.test.ts` in @open-mercato/shared enforces\n// that only `packages/cli/**` or a file literally named `cli.ts` may call `getCliModules()`).\nexport async function upsertEncryptionMaps(\n em: EntityManager,\n tenantId: string,\n organizationId: string | null,\n modules: Module[],\n logger: (msg: string) => void,\n onMaterializedScope?: (scope: { entityId: string; tenantId: string; organizationId: string | null }) => void | Promise<void>,\n) {\n return upsertEncryptionMapSpecs(\n em,\n tenantId,\n organizationId,\n getDefaultEncryptionMaps(modules),\n logger,\n onMaterializedScope,\n )\n}\n\nexport async function invalidateEncryptionMapScopes(\n encryptionService: Pick<TenantDataEncryptionService, 'invalidateMap'>,\n scopes: ReadonlyArray<{ entityId: string; tenantId: string | null; organizationId: string | null }>,\n): Promise<void> {\n const seen = new Set<string>()\n for (const scope of scopes) {\n const key = `${scope.entityId}\\u0000${scope.tenantId ?? ''}\\u0000${scope.organizationId ?? ''}`\n if (seen.has(key)) continue\n seen.add(key)\n await encryptionService.invalidateMap(scope.entityId, scope.tenantId, scope.organizationId)\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { EntityManager } from '@mikro-orm/postgresql'\nimport type { ModuleEncryptionFieldRule, ModuleEncryptionMap } from '@open-mercato/shared/modules/encryption'\nimport { forgetEncryptionPolicyMemo } from '@open-mercato/shared/lib/encryption/policyMemo'\nimport { getDefaultEncryptionMaps, type Module } from '@open-mercato/shared/modules/registry'\nimport type { TenantDataEncryptionService } from '@open-mercato/shared/lib/encryption/tenantDataEncryptionService'\n\nexport type EncryptionMapLike = {\n id: string\n entityId: string\n tenantId?: string | null\n organizationId?: string | null\n fieldsJson?: ModuleEncryptionFieldRule[] | null\n isActive: boolean\n createdAt: Date\n updatedAt: Date\n deletedAt?: Date | null\n}\n\nexport type CanonicalEncryptionMap = EncryptionMapLike & {\n fieldsJson: ModuleEncryptionFieldRule[]\n}\n\nexport type UpsertCanonicalEncryptionMapInput = {\n entityId: string\n tenantId: string | null\n organizationId: string | null\n fields: ModuleEncryptionFieldRule[]\n isActive: boolean\n /**\n * When set, an existing live row is only updated while its `updated_at` still equals this\n * version (millisecond precision, matching the optimistic-lock token). A concurrent write that\n * moved the version makes the upsert throw {@link EncryptionMapVersionConflictError} instead of\n * overwriting it.\n */\n expectedUpdatedAt?: Date | null\n}\n\nexport type UpsertedEncryptionMap = {\n id: string\n updatedAt: Date\n}\n\nexport class EncryptionMapVersionConflictError extends Error {\n constructor(\n readonly expectedUpdatedAt: Date,\n readonly currentUpdatedAt: Date | null,\n ) {\n super('[internal] Encryption map version changed before the conditional update')\n this.name = 'EncryptionMapVersionConflictError'\n }\n}\n\nfunction toDate(value: Date | string): Date {\n return value instanceof Date ? value : new Date(value)\n}\n\nfunction orderedMaps<T extends EncryptionMapLike>(records: readonly T[]): T[] {\n return [...records].sort((left, right) => {\n const createdDelta = left.createdAt.getTime() - right.createdAt.getTime()\n return createdDelta || left.id.localeCompare(right.id)\n })\n}\n\nexport function mergeEncryptionMapFields(records: readonly EncryptionMapLike[]): ModuleEncryptionFieldRule[] {\n const merged: ModuleEncryptionFieldRule[] = []\n const byField = new Map<string, ModuleEncryptionFieldRule>()\n for (const record of orderedMaps(records)) {\n for (const rule of record.fieldsJson ?? []) {\n const field = typeof rule?.field === 'string' ? rule.field.trim() : ''\n if (!field) continue\n const existing = byField.get(field)\n if (!existing) {\n const copy = { field, hashField: typeof rule.hashField === 'string' ? rule.hashField : null }\n byField.set(field, copy)\n merged.push(copy)\n continue\n }\n if (!existing.hashField && typeof rule.hashField === 'string' && rule.hashField) {\n existing.hashField = rule.hashField\n }\n }\n }\n return merged\n}\n\nexport function resolveCanonicalEncryptionMap<T extends EncryptionMapLike>(records: readonly T[]): CanonicalEncryptionMap | null {\n const ordered = orderedMaps(records)\n const canonical = ordered[0]\n if (!canonical) return null\n const active = ordered.filter((record) => record.isActive)\n return {\n ...canonical,\n isActive: active.length > 0,\n fieldsJson: mergeEncryptionMapFields(active.length > 0 ? active : ordered),\n }\n}\n\nexport async function upsertCanonicalEncryptionMap(\n em: EntityManager,\n input: UpsertCanonicalEncryptionMapInput,\n): Promise<UpsertedEncryptionMap> {\n const expectedUpdatedAt = input.expectedUpdatedAt ?? null\n const versionCondition = expectedUpdatedAt\n ? `where date_trunc('milliseconds', \"encryption_maps\".\"updated_at\") = date_trunc('milliseconds', ?::timestamptz)`\n : ''\n const params: unknown[] = [\n input.entityId,\n input.tenantId,\n input.organizationId,\n JSON.stringify(input.fields),\n input.isActive,\n ]\n if (expectedUpdatedAt) params.push(expectedUpdatedAt.toISOString())\n const rows = await em.execute<Array<{ id: string; updated_at: Date | string }>>(\n `\n insert into \"encryption_maps\"\n (\"id\", \"entity_id\", \"tenant_id\", \"organization_id\", \"fields_json\", \"is_active\", \"created_at\", \"updated_at\")\n values\n (gen_random_uuid(), ?, ?, ?, ?::jsonb, ?, now(), now())\n on conflict (\"entity_id\", \"tenant_id\", \"organization_id\") where \"deleted_at\" is null\n do update set\n \"fields_json\" = excluded.\"fields_json\",\n \"is_active\" = excluded.\"is_active\",\n \"updated_at\" = now()\n ${versionCondition}\n returning \"id\", \"updated_at\"\n `,\n params,\n )\n forgetEncryptionPolicyMemo(em)\n const saved = rows[0]\n if (!saved?.id || !saved.updated_at) {\n if (expectedUpdatedAt) {\n throw new EncryptionMapVersionConflictError(\n expectedUpdatedAt,\n await readLiveEncryptionMapVersion(em, input),\n )\n }\n throw new Error('[internal] Encryption map upsert did not return the canonical row')\n }\n return {\n id: saved.id,\n updatedAt: toDate(saved.updated_at),\n }\n}\n\nasync function readLiveEncryptionMapVersion(\n em: EntityManager,\n input: UpsertCanonicalEncryptionMapInput,\n): Promise<Date | null> {\n const rows = await em.execute<Array<{ updated_at: Date | string | null }>>(\n `\n select \"updated_at\"\n from \"encryption_maps\"\n where \"entity_id\" = ?\n and \"tenant_id\" is not distinct from ?\n and \"organization_id\" is not distinct from ?\n and \"deleted_at\" is null\n order by \"created_at\" asc, \"id\" asc\n limit 1\n `,\n [input.entityId, input.tenantId, input.organizationId],\n )\n const current = rows[0]?.updated_at\n return current ? toDate(current) : null\n}\n\nexport type OrganizationEncryptionMapScope = {\n entityId: string\n tenantId: string\n organizationId: string\n}\n\n/**\n * Appends the declared field rules a tenant's organization-level maps for `entityId` lack.\n *\n * Tenant setup seeds a map per organization, and the encryption service prefers an\n * organization map over the tenant map without merging the two. Re-seeding only the tenant map\n * therefore leaves those organizations on their old field list, and a newly declared field is\n * written as plaintext there. Existing rules are never removed or changed: the same contract as\n * `buildEncryptionMapBackfillSql`, so an admin's own additions survive.\n */\nexport async function appendMissingFieldsToOrganizationMaps(\n em: EntityManager,\n input: { entityId: string; tenantId: string; fields: ModuleEncryptionFieldRule[] },\n): Promise<OrganizationEncryptionMapScope[]> {\n const updatedOrganizationIds = new Set<string>()\n for (const rule of input.fields) {\n const field = typeof rule?.field === 'string' ? rule.field.trim() : ''\n if (!field) continue\n const appended = rule.hashField ? { field, hashField: rule.hashField } : { field }\n const rows = await em.execute<Array<{ organization_id: string }>>(\n `\n update \"encryption_maps\"\n set \"fields_json\" = coalesce(\"fields_json\", '[]'::jsonb) || ?::jsonb, \"updated_at\" = now()\n where \"entity_id\" = ?\n and \"tenant_id\" = ?\n and \"organization_id\" is not null\n and \"deleted_at\" is null\n and jsonb_typeof(coalesce(\"fields_json\", '[]'::jsonb)) = 'array'\n and not exists (\n select 1\n from jsonb_array_elements(case when jsonb_typeof(\"fields_json\") = 'array' then \"fields_json\" else '[]'::jsonb end) as existing_rule\n where existing_rule->>'field' = ?\n )\n returning \"organization_id\"\n `,\n [JSON.stringify([appended]), input.entityId, input.tenantId, field],\n )\n for (const row of rows) {\n if (row?.organization_id) updatedOrganizationIds.add(String(row.organization_id))\n }\n }\n if (updatedOrganizationIds.size) forgetEncryptionPolicyMemo(em)\n return Array.from(updatedOrganizationIds, (organizationId) => ({\n entityId: input.entityId,\n tenantId: input.tenantId,\n organizationId,\n }))\n}\n\n// Idempotently upsert a specific set of encryption-map specs for one (tenant, org) scope. Exported so\n// upgrade actions can backfill a newly-added encrypted entity for pre-existing tenants (whose maps were\n// seeded once at tenant creation and never re-run) without depending on the full CLI module registry.\nexport async function upsertEncryptionMapSpecs(\n em: EntityManager,\n tenantId: string,\n organizationId: string | null,\n specs: ModuleEncryptionMap[],\n logger: (msg: string) => void = () => {},\n onMaterializedScope?: (scope: { entityId: string; tenantId: string; organizationId: string | null }) => void | Promise<void>,\n): Promise<Array<{ entityId: string; tenantId: string; organizationId: string | null }>> {\n const materializedScopes: Array<{ entityId: string; tenantId: string; organizationId: string | null }> = []\n for (const spec of specs) {\n if (spec.keyScope === 'system') {\n logger(`Skipping ${spec.entityId}: system-scoped map, resolved from module code rather than a tenant row.`)\n continue\n }\n await upsertCanonicalEncryptionMap(em, {\n entityId: spec.entityId,\n tenantId,\n organizationId,\n fields: spec.fields,\n isActive: true,\n })\n const materializedScope = { entityId: spec.entityId, tenantId, organizationId }\n materializedScopes.push(materializedScope)\n await onMaterializedScope?.(materializedScope)\n logger(`\uD83D\uDD12 Seeded encryption map for ${spec.entityId} \u2728`)\n if (organizationId !== null) continue\n const organizationScopes = await appendMissingFieldsToOrganizationMaps(em, {\n entityId: spec.entityId,\n tenantId,\n fields: spec.fields,\n })\n for (const organizationScope of organizationScopes) {\n materializedScopes.push(organizationScope)\n await onMaterializedScope?.(organizationScope)\n logger(`\uD83D\uDD12 Added missing fields to the ${spec.entityId} map of organization ${organizationScope.organizationId}`)\n }\n }\n return materializedScopes\n}\n\n// Exported so other modules (e.g. the directory subscribers that seed maps for a\n// newly-created organization) can seed the default module encryption maps for one\n// (tenant, org) scope. Callers resolve their own module list: a CLI command uses\n// `getCliModules()`/`getModules()` (cli.ts is an allowed CLI-registry entrypoint), a runtime\n// subscriber MUST use the app registry (`getModules()`) directly \u2014 this lib never reads the\n// CLI-only registry itself (`cli-registry-boundary.test.ts` in @open-mercato/shared enforces\n// that only `packages/cli/**` or a file literally named `cli.ts` may call `getCliModules()`).\nexport async function upsertEncryptionMaps(\n em: EntityManager,\n tenantId: string,\n organizationId: string | null,\n modules: Module[],\n logger: (msg: string) => void,\n onMaterializedScope?: (scope: { entityId: string; tenantId: string; organizationId: string | null }) => void | Promise<void>,\n) {\n return upsertEncryptionMapSpecs(\n em,\n tenantId,\n organizationId,\n getDefaultEncryptionMaps(modules),\n logger,\n onMaterializedScope,\n )\n}\n\nexport async function invalidateEncryptionMapScopes(\n encryptionService: Pick<TenantDataEncryptionService, 'invalidateMap'>,\n scopes: ReadonlyArray<{ entityId: string; tenantId: string | null; organizationId: string | null }>,\n): Promise<void> {\n const seen = new Set<string>()\n for (const scope of scopes) {\n const key = `${scope.entityId}\\u0000${scope.tenantId ?? ''}\\u0000${scope.organizationId ?? ''}`\n if (seen.has(key)) continue\n seen.add(key)\n await encryptionService.invalidateMap(scope.entityId, scope.tenantId, scope.organizationId)\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/core",
3
- "version": "0.10.1-develop.7714.1.9dbb8200d8",
3
+ "version": "0.10.1-develop.7716.1.cefe7975df",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -265,16 +265,16 @@
265
265
  "zod": "^4.4.3"
266
266
  },
267
267
  "peerDependencies": {
268
- "@open-mercato/ai-assistant": "0.10.1-develop.7714.1.9dbb8200d8",
269
- "@open-mercato/shared": "0.10.1-develop.7714.1.9dbb8200d8",
270
- "@open-mercato/ui": "0.10.1-develop.7714.1.9dbb8200d8",
268
+ "@open-mercato/ai-assistant": "0.10.1-develop.7716.1.cefe7975df",
269
+ "@open-mercato/shared": "0.10.1-develop.7716.1.cefe7975df",
270
+ "@open-mercato/ui": "0.10.1-develop.7716.1.cefe7975df",
271
271
  "react": "^19.0.0",
272
272
  "react-dom": "^19.0.0"
273
273
  },
274
274
  "devDependencies": {
275
- "@open-mercato/ai-assistant": "0.10.1-develop.7714.1.9dbb8200d8",
276
- "@open-mercato/shared": "0.10.1-develop.7714.1.9dbb8200d8",
277
- "@open-mercato/ui": "0.10.1-develop.7714.1.9dbb8200d8",
275
+ "@open-mercato/ai-assistant": "0.10.1-develop.7716.1.cefe7975df",
276
+ "@open-mercato/shared": "0.10.1-develop.7716.1.cefe7975df",
277
+ "@open-mercato/ui": "0.10.1-develop.7716.1.cefe7975df",
278
278
  "@testing-library/dom": "^10.4.1",
279
279
  "@testing-library/jest-dom": "^7.0.1",
280
280
  "@testing-library/react": "^16.3.3",
@@ -21,7 +21,7 @@ import {
21
21
  buildCustomFieldResetMap,
22
22
  diffCustomFieldChanges,
23
23
  } from '@open-mercato/shared/lib/commands/customFieldSnapshots'
24
- import { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'
24
+ import { extractUndoPayload, reviveSnapshotDates } from '@open-mercato/shared/lib/commands/undo'
25
25
  import { resolveRedoSnapshot } from '@open-mercato/shared/lib/commands/redo'
26
26
  import { withAtomicFlush } from '@open-mercato/shared/lib/commands/flush'
27
27
  import {
@@ -89,6 +89,7 @@ type ChildParentSnapshot = {
89
89
  type OrganizationUndoSnapshot = {
90
90
  id: string
91
91
  tenantId: string | null
92
+ createdAt?: string | null
92
93
  name: string
93
94
  slug?: string | null
94
95
  logoUrl?: string | null
@@ -227,6 +228,7 @@ function captureOrganizationSnapshots(
227
228
  undo: {
228
229
  id: String(entity.id),
229
230
  tenantId,
231
+ createdAt: entity.createdAt ? entity.createdAt.toISOString() : null,
230
232
  name: entity.name,
231
233
  slug: entity.slug ?? null,
232
234
  logoUrl: entity.logoUrl ?? null,
@@ -348,7 +350,6 @@ async function resolveUniqueSlug(em: EntityManager, tenantId: string, baseSlug:
348
350
  const filter: FilterQuery<Organization> = {
349
351
  tenant: tenantId,
350
352
  slug: candidate,
351
- deletedAt: null,
352
353
  } as unknown as FilterQuery<Organization>
353
354
  const existing = await em.findOne(Organization, filter)
354
355
  if (!existing || (excludeId && String(existing.id) === excludeId)) return candidate
@@ -478,6 +479,7 @@ const createOrganizationCommand: CommandHandler<Record<string, unknown>, Organiz
478
479
  await assertOrganizationUndoTenantAccess(ctx, tenantId)
479
480
  const em = (ctx.container.resolve('em') as EntityManager)
480
481
  const de = (ctx.container.resolve('dataEngine') as DataEngine)
482
+ let organization: Organization | null = null
481
483
  await withAtomicFlush(em, [
482
484
  async () => {
483
485
  await lockOrganizationHierarchyForTenant(em, tenantId)
@@ -496,14 +498,28 @@ const createOrganizationCommand: CommandHandler<Record<string, unknown>, Organiz
496
498
  })
497
499
  }
498
500
  }
499
- await de.deleteOrmEntity({
501
+ organization = await de.deleteOrmEntity({
500
502
  entity: Organization,
501
503
  where: buildOrganizationMutationFilter(after.id, tenantId),
502
- soft: false,
504
+ soft: true,
505
+ softDeleteField: 'deletedAt',
503
506
  })
507
+ if (organization) {
508
+ organization.isActive = false
509
+ organization.parentId = null
510
+ await em.flush()
511
+ }
504
512
  await rebuildHierarchyForTenant(em, tenantId)
505
513
  },
506
514
  ], { transaction: true })
515
+ await emitCrudUndoSideEffects({
516
+ dataEngine: de,
517
+ action: 'deleted',
518
+ entity: organization,
519
+ identifiers: { id: after.id, tenantId, organizationId: after.id },
520
+ events: organizationCrudEvents,
521
+ indexer: organizationCrudIndexer,
522
+ })
507
523
  },
508
524
  redo: async ({ logEntry, ctx }) => {
509
525
  const after = resolveRedoSnapshot<OrganizationUndoSnapshot>(logEntry)
@@ -529,10 +545,20 @@ const createOrganizationCommand: CommandHandler<Record<string, unknown>, Organiz
529
545
  await em.flush()
530
546
  organization = existing
531
547
  } else {
548
+ const snapshotAfter = logEntry.snapshotAfter
549
+ const createdAt = after.createdAt ?? (
550
+ snapshotAfter && typeof snapshotAfter === 'object' && 'createdAt' in snapshotAfter
551
+ ? snapshotAfter.createdAt
552
+ : undefined
553
+ )
554
+ const creationTime = typeof createdAt === 'string' || createdAt instanceof Date
555
+ ? reviveSnapshotDates({ createdAt }, ['createdAt'])
556
+ : {}
532
557
  organization = await de.createOrmEntity({
533
558
  entity: Organization,
534
559
  data: {
535
560
  id: after.id,
561
+ ...creationTime,
536
562
  name: after.name,
537
563
  slug: after.slug ?? null,
538
564
  logoUrl: after.logoUrl ?? null,
@@ -165,6 +165,60 @@ async function readLiveEncryptionMapVersion(
165
165
  return current ? toDate(current) : null
166
166
  }
167
167
 
168
+ export type OrganizationEncryptionMapScope = {
169
+ entityId: string
170
+ tenantId: string
171
+ organizationId: string
172
+ }
173
+
174
+ /**
175
+ * Appends the declared field rules a tenant's organization-level maps for `entityId` lack.
176
+ *
177
+ * Tenant setup seeds a map per organization, and the encryption service prefers an
178
+ * organization map over the tenant map without merging the two. Re-seeding only the tenant map
179
+ * therefore leaves those organizations on their old field list, and a newly declared field is
180
+ * written as plaintext there. Existing rules are never removed or changed: the same contract as
181
+ * `buildEncryptionMapBackfillSql`, so an admin's own additions survive.
182
+ */
183
+ export async function appendMissingFieldsToOrganizationMaps(
184
+ em: EntityManager,
185
+ input: { entityId: string; tenantId: string; fields: ModuleEncryptionFieldRule[] },
186
+ ): Promise<OrganizationEncryptionMapScope[]> {
187
+ const updatedOrganizationIds = new Set<string>()
188
+ for (const rule of input.fields) {
189
+ const field = typeof rule?.field === 'string' ? rule.field.trim() : ''
190
+ if (!field) continue
191
+ const appended = rule.hashField ? { field, hashField: rule.hashField } : { field }
192
+ const rows = await em.execute<Array<{ organization_id: string }>>(
193
+ `
194
+ update "encryption_maps"
195
+ set "fields_json" = coalesce("fields_json", '[]'::jsonb) || ?::jsonb, "updated_at" = now()
196
+ where "entity_id" = ?
197
+ and "tenant_id" = ?
198
+ and "organization_id" is not null
199
+ and "deleted_at" is null
200
+ and jsonb_typeof(coalesce("fields_json", '[]'::jsonb)) = 'array'
201
+ and not exists (
202
+ select 1
203
+ from jsonb_array_elements(case when jsonb_typeof("fields_json") = 'array' then "fields_json" else '[]'::jsonb end) as existing_rule
204
+ where existing_rule->>'field' = ?
205
+ )
206
+ returning "organization_id"
207
+ `,
208
+ [JSON.stringify([appended]), input.entityId, input.tenantId, field],
209
+ )
210
+ for (const row of rows) {
211
+ if (row?.organization_id) updatedOrganizationIds.add(String(row.organization_id))
212
+ }
213
+ }
214
+ if (updatedOrganizationIds.size) forgetEncryptionPolicyMemo(em)
215
+ return Array.from(updatedOrganizationIds, (organizationId) => ({
216
+ entityId: input.entityId,
217
+ tenantId: input.tenantId,
218
+ organizationId,
219
+ }))
220
+ }
221
+
168
222
  // Idempotently upsert a specific set of encryption-map specs for one (tenant, org) scope. Exported so
169
223
  // upgrade actions can backfill a newly-added encrypted entity for pre-existing tenants (whose maps were
170
224
  // seeded once at tenant creation and never re-run) without depending on the full CLI module registry.
@@ -193,6 +247,17 @@ export async function upsertEncryptionMapSpecs(
193
247
  materializedScopes.push(materializedScope)
194
248
  await onMaterializedScope?.(materializedScope)
195
249
  logger(`🔒 Seeded encryption map for ${spec.entityId} ✨`)
250
+ if (organizationId !== null) continue
251
+ const organizationScopes = await appendMissingFieldsToOrganizationMaps(em, {
252
+ entityId: spec.entityId,
253
+ tenantId,
254
+ fields: spec.fields,
255
+ })
256
+ for (const organizationScope of organizationScopes) {
257
+ materializedScopes.push(organizationScope)
258
+ await onMaterializedScope?.(organizationScope)
259
+ logger(`🔒 Added missing fields to the ${spec.entityId} map of organization ${organizationScope.organizationId}`)
260
+ }
196
261
  }
197
262
  return materializedScopes
198
263
  }