@open-mercato/core 0.5.1-develop.3032.01699048cb → 0.5.1-develop.3043.1a796c3920

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (175) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +13 -1
  3. package/dist/helpers/integration/api.js +29 -16
  4. package/dist/helpers/integration/api.js.map +2 -2
  5. package/dist/helpers/integration/auth.js +11 -6
  6. package/dist/helpers/integration/auth.js.map +3 -3
  7. package/dist/modules/auth/api/sidebar/preferences/route.js +2 -2
  8. package/dist/modules/auth/api/sidebar/preferences/route.js.map +2 -2
  9. package/dist/modules/auth/api/sidebar/variants/[id]/route.js +2 -2
  10. package/dist/modules/auth/api/sidebar/variants/[id]/route.js.map +2 -2
  11. package/dist/modules/auth/api/sidebar/variants/route.js +1 -1
  12. package/dist/modules/auth/api/sidebar/variants/route.js.map +2 -2
  13. package/dist/modules/auth/backend/sidebar-customization/page.meta.js +1 -0
  14. package/dist/modules/auth/backend/sidebar-customization/page.meta.js.map +2 -2
  15. package/dist/modules/auth/commands/roles.js +9 -12
  16. package/dist/modules/auth/commands/roles.js.map +2 -2
  17. package/dist/modules/catalog/ai-agents-context.js +147 -0
  18. package/dist/modules/catalog/ai-agents-context.js.map +7 -0
  19. package/dist/modules/catalog/ai-agents.js +383 -0
  20. package/dist/modules/catalog/ai-agents.js.map +7 -0
  21. package/dist/modules/catalog/ai-tools/_shared.js +318 -0
  22. package/dist/modules/catalog/ai-tools/_shared.js.map +7 -0
  23. package/dist/modules/catalog/ai-tools/authoring-pack.js +391 -0
  24. package/dist/modules/catalog/ai-tools/authoring-pack.js.map +7 -0
  25. package/dist/modules/catalog/ai-tools/categories-pack.js +167 -0
  26. package/dist/modules/catalog/ai-tools/categories-pack.js.map +7 -0
  27. package/dist/modules/catalog/ai-tools/configuration-pack.js +120 -0
  28. package/dist/modules/catalog/ai-tools/configuration-pack.js.map +7 -0
  29. package/dist/modules/catalog/ai-tools/media-tags-pack.js +107 -0
  30. package/dist/modules/catalog/ai-tools/media-tags-pack.js.map +7 -0
  31. package/dist/modules/catalog/ai-tools/merchandising-pack.js +429 -0
  32. package/dist/modules/catalog/ai-tools/merchandising-pack.js.map +7 -0
  33. package/dist/modules/catalog/ai-tools/mutation-pack.js +576 -0
  34. package/dist/modules/catalog/ai-tools/mutation-pack.js.map +7 -0
  35. package/dist/modules/catalog/ai-tools/prices-offers-pack.js +208 -0
  36. package/dist/modules/catalog/ai-tools/prices-offers-pack.js.map +7 -0
  37. package/dist/modules/catalog/ai-tools/products-pack.js +298 -0
  38. package/dist/modules/catalog/ai-tools/products-pack.js.map +7 -0
  39. package/dist/modules/catalog/ai-tools/stats-pack.js +57 -0
  40. package/dist/modules/catalog/ai-tools/stats-pack.js.map +7 -0
  41. package/dist/modules/catalog/ai-tools/types.js +10 -0
  42. package/dist/modules/catalog/ai-tools/types.js.map +7 -0
  43. package/dist/modules/catalog/ai-tools/variants-pack.js +75 -0
  44. package/dist/modules/catalog/ai-tools/variants-pack.js.map +7 -0
  45. package/dist/modules/catalog/ai-tools.js +28 -0
  46. package/dist/modules/catalog/ai-tools.js.map +7 -0
  47. package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js +466 -0
  48. package/dist/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.js.map +7 -0
  49. package/dist/modules/catalog/backend/catalog/products/page.js +7 -1
  50. package/dist/modules/catalog/backend/catalog/products/page.js.map +2 -2
  51. package/dist/modules/catalog/components/CatalogStatsCard.js +91 -0
  52. package/dist/modules/catalog/components/CatalogStatsCard.js.map +7 -0
  53. package/dist/modules/catalog/components/products/ProductsDataTable.js +23 -3
  54. package/dist/modules/catalog/components/products/ProductsDataTable.js.map +2 -2
  55. package/dist/modules/catalog/events.js +7 -4
  56. package/dist/modules/catalog/events.js.map +2 -2
  57. package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js +59 -0
  58. package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.js.map +7 -0
  59. package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.js +17 -0
  60. package/dist/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.js.map +7 -0
  61. package/dist/modules/catalog/widgets/injection/product-seo/widget.client.js +1 -1
  62. package/dist/modules/catalog/widgets/injection/product-seo/widget.client.js.map +2 -2
  63. package/dist/modules/catalog/widgets/injection-table.js +13 -1
  64. package/dist/modules/catalog/widgets/injection-table.js.map +2 -2
  65. package/dist/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.client.js +94 -0
  66. package/dist/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.client.js.map +7 -0
  67. package/dist/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.js +17 -0
  68. package/dist/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.js.map +7 -0
  69. package/dist/modules/customer_accounts/widgets/injection-table.js +9 -0
  70. package/dist/modules/customer_accounts/widgets/injection-table.js.map +2 -2
  71. package/dist/modules/customers/ai-agents-context.js +96 -0
  72. package/dist/modules/customers/ai-agents-context.js.map +7 -0
  73. package/dist/modules/customers/ai-agents.js +244 -0
  74. package/dist/modules/customers/ai-agents.js.map +7 -0
  75. package/dist/modules/customers/ai-tools/activities-tasks-pack.js +1015 -0
  76. package/dist/modules/customers/ai-tools/activities-tasks-pack.js.map +7 -0
  77. package/dist/modules/customers/ai-tools/addresses-tags-pack.js +134 -0
  78. package/dist/modules/customers/ai-tools/addresses-tags-pack.js.map +7 -0
  79. package/dist/modules/customers/ai-tools/companies-pack.js +249 -0
  80. package/dist/modules/customers/ai-tools/companies-pack.js.map +7 -0
  81. package/dist/modules/customers/ai-tools/deals-pack.js +348 -0
  82. package/dist/modules/customers/ai-tools/deals-pack.js.map +7 -0
  83. package/dist/modules/customers/ai-tools/people-pack.js +261 -0
  84. package/dist/modules/customers/ai-tools/people-pack.js.map +7 -0
  85. package/dist/modules/customers/ai-tools/settings-pack.js +102 -0
  86. package/dist/modules/customers/ai-tools/settings-pack.js.map +7 -0
  87. package/dist/modules/customers/ai-tools/types.js +10 -0
  88. package/dist/modules/customers/ai-tools/types.js.map +7 -0
  89. package/dist/modules/customers/ai-tools.js +20 -0
  90. package/dist/modules/customers/ai-tools.js.map +7 -0
  91. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js +469 -0
  92. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.js.map +7 -0
  93. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js +17 -0
  94. package/dist/modules/customers/widgets/injection/ai-assistant-trigger/widget.js.map +7 -0
  95. package/dist/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.client.js +117 -0
  96. package/dist/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.client.js.map +7 -0
  97. package/dist/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.js +17 -0
  98. package/dist/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.js.map +7 -0
  99. package/dist/modules/customers/widgets/injection-table.js +26 -0
  100. package/dist/modules/customers/widgets/injection-table.js.map +7 -0
  101. package/dist/modules/inbox_ops/ai-tools.js +4 -0
  102. package/dist/modules/inbox_ops/ai-tools.js.map +2 -2
  103. package/dist/modules/inbox_ops/lib/llmProvider.js +52 -7
  104. package/dist/modules/inbox_ops/lib/llmProvider.js.map +2 -2
  105. package/dist/modules/notifications/setup.js +13 -0
  106. package/dist/modules/notifications/setup.js.map +7 -0
  107. package/jest.config.cjs +1 -0
  108. package/jest.setup.ts +18 -0
  109. package/package.json +5 -3
  110. package/src/helpers/integration/api.ts +38 -16
  111. package/src/helpers/integration/auth.ts +13 -6
  112. package/src/modules/auth/api/sidebar/preferences/route.ts +2 -2
  113. package/src/modules/auth/api/sidebar/variants/[id]/route.ts +2 -2
  114. package/src/modules/auth/api/sidebar/variants/route.ts +1 -1
  115. package/src/modules/auth/backend/sidebar-customization/page.meta.ts +1 -8
  116. package/src/modules/auth/commands/roles.ts +10 -12
  117. package/src/modules/catalog/AGENTS.md +11 -0
  118. package/src/modules/catalog/ai-agents-context.ts +239 -0
  119. package/src/modules/catalog/ai-agents.ts +525 -0
  120. package/src/modules/catalog/ai-tools/_shared.ts +487 -0
  121. package/src/modules/catalog/ai-tools/authoring-pack.ts +600 -0
  122. package/src/modules/catalog/ai-tools/categories-pack.ts +192 -0
  123. package/src/modules/catalog/ai-tools/configuration-pack.ts +218 -0
  124. package/src/modules/catalog/ai-tools/media-tags-pack.ts +127 -0
  125. package/src/modules/catalog/ai-tools/merchandising-pack.ts +608 -0
  126. package/src/modules/catalog/ai-tools/mutation-pack.ts +761 -0
  127. package/src/modules/catalog/ai-tools/prices-offers-pack.ts +376 -0
  128. package/src/modules/catalog/ai-tools/products-pack.ts +387 -0
  129. package/src/modules/catalog/ai-tools/stats-pack.ts +84 -0
  130. package/src/modules/catalog/ai-tools/types.ts +81 -0
  131. package/src/modules/catalog/ai-tools/variants-pack.ts +147 -0
  132. package/src/modules/catalog/ai-tools.ts +78 -0
  133. package/src/modules/catalog/backend/catalog/products/MerchandisingAssistantSheet.tsx +597 -0
  134. package/src/modules/catalog/backend/catalog/products/page.tsx +23 -2
  135. package/src/modules/catalog/components/CatalogStatsCard.tsx +118 -0
  136. package/src/modules/catalog/components/products/ProductsDataTable.tsx +54 -6
  137. package/src/modules/catalog/events.ts +7 -4
  138. package/src/modules/catalog/i18n/de.json +17 -0
  139. package/src/modules/catalog/i18n/en.json +17 -0
  140. package/src/modules/catalog/i18n/es.json +17 -0
  141. package/src/modules/catalog/i18n/pl.json +17 -0
  142. package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.client.tsx +109 -0
  143. package/src/modules/catalog/widgets/injection/merchandising-assistant-trigger/widget.ts +29 -0
  144. package/src/modules/catalog/widgets/injection/product-seo/widget.client.tsx +1 -1
  145. package/src/modules/catalog/widgets/injection-table.ts +12 -0
  146. package/src/modules/customer_accounts/i18n/de.json +5 -0
  147. package/src/modules/customer_accounts/i18n/en.json +5 -0
  148. package/src/modules/customer_accounts/i18n/es.json +5 -0
  149. package/src/modules/customer_accounts/i18n/pl.json +5 -0
  150. package/src/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.client.tsx +136 -0
  151. package/src/modules/customer_accounts/widgets/injection/portal-ai-assistant-trigger/widget.ts +43 -0
  152. package/src/modules/customer_accounts/widgets/injection-table.ts +9 -0
  153. package/src/modules/customers/AGENTS.md +13 -0
  154. package/src/modules/customers/ai-agents-context.ts +150 -0
  155. package/src/modules/customers/ai-agents.ts +355 -0
  156. package/src/modules/customers/ai-tools/activities-tasks-pack.ts +1248 -0
  157. package/src/modules/customers/ai-tools/addresses-tags-pack.ts +145 -0
  158. package/src/modules/customers/ai-tools/companies-pack.ts +362 -0
  159. package/src/modules/customers/ai-tools/deals-pack.ts +505 -0
  160. package/src/modules/customers/ai-tools/people-pack.ts +369 -0
  161. package/src/modules/customers/ai-tools/settings-pack.ts +121 -0
  162. package/src/modules/customers/ai-tools/types.ts +76 -0
  163. package/src/modules/customers/ai-tools.ts +34 -0
  164. package/src/modules/customers/i18n/de.json +25 -0
  165. package/src/modules/customers/i18n/en.json +25 -0
  166. package/src/modules/customers/i18n/es.json +25 -0
  167. package/src/modules/customers/i18n/pl.json +25 -0
  168. package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.client.tsx +580 -0
  169. package/src/modules/customers/widgets/injection/ai-assistant-trigger/widget.ts +36 -0
  170. package/src/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.client.tsx +191 -0
  171. package/src/modules/customers/widgets/injection/ai-deal-detail-trigger/widget.ts +37 -0
  172. package/src/modules/customers/widgets/injection-table.ts +41 -0
  173. package/src/modules/inbox_ops/ai-tools.ts +4 -0
  174. package/src/modules/inbox_ops/lib/llmProvider.ts +83 -7
  175. package/src/modules/notifications/setup.ts +11 -0
@@ -20,11 +20,13 @@ import {
20
20
  } from "@open-mercato/shared/lib/commands/customFieldSnapshots";
21
21
  import { extractUndoPayload } from "@open-mercato/shared/lib/commands/undo";
22
22
  const RESERVED_ROLE_NAMES = /* @__PURE__ */ new Set(["superadmin", "admin"]);
23
- function assertRoleNameAllowed(name) {
24
- if (typeof name !== "string") return;
23
+ function isReservedRoleName(name) {
24
+ if (typeof name !== "string") return false;
25
25
  const normalized = name.trim().toLowerCase();
26
- if (!normalized) return;
27
- if (RESERVED_ROLE_NAMES.has(normalized)) {
26
+ return normalized.length > 0 && RESERVED_ROLE_NAMES.has(normalized);
27
+ }
28
+ function assertRoleNameAllowed(name) {
29
+ if (isReservedRoleName(name)) {
28
30
  throw new CrudHttpError(400, { error: "Role name is reserved" });
29
31
  }
30
32
  }
@@ -178,22 +180,17 @@ const updateRoleCommand = {
178
180
  async execute(rawInput, ctx) {
179
181
  const { parsed, custom } = parseWithCustomFields(updateSchema, rawInput);
180
182
  const em = ctx.container.resolve("em");
183
+ const current = await findOneWithDecryption(em, Role, { id: parsed.id, deletedAt: null }, {}, { tenantId: null, organizationId: null });
184
+ if (!current) throw new CrudHttpError(404, { error: "Role not found" });
181
185
  if (parsed.name !== void 0) {
182
- assertRoleNameAllowed(parsed.name);
183
- const current = await findOneWithDecryption(em, Role, { id: parsed.id, deletedAt: null }, {}, { tenantId: null, organizationId: null });
184
- if (!current) throw new CrudHttpError(404, { error: "Role not found" });
185
- assertRoleNameAllowed(current.name);
186
186
  const nextName = parsed.name;
187
+ if (nextName !== current.name) assertRoleNameAllowed(nextName);
187
188
  if (nextName !== current.name) {
188
189
  const assignments = await em.count(UserRole, { role: current, deletedAt: null });
189
190
  if (assignments > 0) {
190
191
  throw new CrudHttpError(400, { error: "Role name cannot be changed while users are assigned" });
191
192
  }
192
193
  }
193
- } else {
194
- const current = await findOneWithDecryption(em, Role, { id: parsed.id, deletedAt: null }, {}, { tenantId: null, organizationId: null });
195
- if (!current) throw new CrudHttpError(404, { error: "Role not found" });
196
- assertRoleNameAllowed(current.name);
197
194
  }
198
195
  const de = ctx.container.resolve("dataEngine");
199
196
  const role = await de.updateOrmEntity({
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/modules/auth/commands/roles.ts"],
4
- "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport {\n parseWithCustomFields,\n setCustomFieldsIfAny,\n emitCrudSideEffects,\n emitCrudUndoSideEffects,\n buildChanges,\n requireId,\n} from '@open-mercato/shared/lib/commands/helpers'\nimport type { CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { Role, RoleAcl, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n loadCustomFieldSnapshot,\n buildCustomFieldResetMap,\n diffCustomFieldChanges,\n} from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\n\ntype SerializedRole = {\n name: string\n tenantId: string\n custom?: Record<string, unknown>\n}\n\ntype RoleAclSnapshot = {\n id: string | null\n tenantId: string\n features: string[] | null\n isSuperAdmin: boolean\n organizations: string[] | null\n}\n\ntype RoleUndoSnapshot = {\n id: string\n name: string\n tenantId: string\n acls: RoleAclSnapshot[]\n custom?: Record<string, unknown>\n}\n\ntype RoleSnapshots = {\n view: SerializedRole\n undo: RoleUndoSnapshot\n}\n\nconst RESERVED_ROLE_NAMES = new Set(['superadmin', 'admin'])\n\nfunction assertRoleNameAllowed(name: string | undefined | null) {\n if (typeof name !== 'string') return\n const normalized = name.trim().toLowerCase()\n if (!normalized) return\n if (RESERVED_ROLE_NAMES.has(normalized)) {\n throw new CrudHttpError(400, { error: 'Role name is reserved' })\n }\n}\n\nconst createSchema = z.object({\n name: z.string().min(2).max(100),\n tenantId: z.string().uuid().optional(),\n})\n\nconst updateSchema = z.object({\n id: z.string().uuid(),\n name: z.string().min(2).max(100).optional(),\n tenantId: z.string().uuid().optional(),\n})\n\nexport const roleCrudEvents: CrudEventsConfig = {\n module: 'auth',\n entity: 'role',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nexport const roleCrudIndexer: CrudIndexerConfig = {\n entityType: E.auth.role,\n buildUpsertPayload: (ctx) => ({\n entityType: E.auth.role,\n recordId: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n buildDeletePayload: (ctx) => ({\n entityType: E.auth.role,\n recordId: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nconst createRoleCommand: CommandHandler<Record<string, unknown>, Role> = {\n id: 'auth.roles.create',\n async execute(rawInput, ctx) {\n const rawBody = rawInput && typeof rawInput === 'object' ? rawInput as Record<string, unknown> : {}\n if ('tenantId' in rawBody && rawBody.tenantId === null) {\n throw new CrudHttpError(400, { error: 'tenantId cannot be null \u2014 global roles are not supported' })\n }\n const { parsed, custom } = parseWithCustomFields(createSchema, rawInput)\n assertRoleNameAllowed(parsed.name)\n const resolvedTenantId = parsed.tenantId ?? ctx.auth?.tenantId ?? null\n if (!resolvedTenantId) {\n throw new CrudHttpError(400, { error: 'tenantId is required \u2014 global roles are not supported' })\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const role = await de.createOrmEntity({\n entity: Role,\n data: {\n name: parsed.name,\n tenantId: resolvedTenantId,\n },\n })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: String(role.id),\n organizationId: null,\n tenantId: resolvedTenantId,\n values: custom,\n })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: role,\n identifiers: {\n id: String(role.id),\n organizationId: null,\n tenantId: resolvedTenantId,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return role\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n return serializeRole(result, custom)\n },\n buildLog: async ({ result, ctx }) => {\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n const snapshot = captureRoleSnapshots(result, [], custom)\n return {\n actionLabel: translate('auth.audit.roles.create', 'Create role'),\n resourceKind: 'auth.role',\n resourceId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n snapshotAfter: snapshot.view,\n payload: {\n undo: {\n after: snapshot.undo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<RoleUndoPayload>(logEntry)?.after\n if (!undo) return\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await em.nativeDelete(RoleAcl, { role: undo.id as unknown as Role })\n if (undo.custom && Object.keys(undo.custom).length) {\n const reset = buildCustomFieldResetMap(undefined, undo.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: undo.id,\n organizationId: null,\n tenantId: undo.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n }\n await de.deleteOrmEntity({\n entity: Role,\n where: { id: undo.id, deletedAt: null } as FilterQuery<Role>,\n soft: false,\n })\n },\n}\n\nconst updateRoleCommand: CommandHandler<Record<string, unknown>, Role> = {\n id: 'auth.roles.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(updateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const existing = await findOneWithDecryption(em, Role, { id: parsed.id, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n if (!existing) throw new CrudHttpError(404, { error: 'Role not found' })\n const acls = await loadRoleAclSnapshots(em, parsed.id)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: parsed.id,\n tenantId: existing.tenantId ? String(existing.tenantId) : null,\n })\n return { before: captureRoleSnapshots(existing, acls, custom) }\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(updateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n if (parsed.name !== undefined) {\n assertRoleNameAllowed(parsed.name)\n const current = await findOneWithDecryption(em, Role, { id: parsed.id, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n if (!current) throw new CrudHttpError(404, { error: 'Role not found' })\n assertRoleNameAllowed(current.name)\n const nextName = parsed.name\n if (nextName !== current.name) {\n const assignments = await em.count(UserRole, { role: current, deletedAt: null })\n if (assignments > 0) {\n throw new CrudHttpError(400, { error: 'Role name cannot be changed while users are assigned' })\n }\n }\n } else {\n const current = await findOneWithDecryption(em, Role, { id: parsed.id, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n if (!current) throw new CrudHttpError(404, { error: 'Role not found' })\n assertRoleNameAllowed(current.name)\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const role = await de.updateOrmEntity({\n entity: Role,\n where: { id: parsed.id, deletedAt: null } as FilterQuery<Role>,\n apply: (entity) => {\n if (parsed.name !== undefined) entity.name = parsed.name\n if (parsed.tenantId !== undefined) entity.tenantId = parsed.tenantId\n },\n })\n if (!role) throw new CrudHttpError(404, { error: 'Role not found' })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: String(role.id),\n organizationId: null,\n tenantId: role.tenantId ? String(role.tenantId) : null,\n values: custom,\n })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: role,\n identifiers: {\n id: String(role.id),\n organizationId: null,\n tenantId: role.tenantId ? String(role.tenantId) : null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return role\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n return serializeRole(result, custom)\n },\n buildLog: async ({ result, snapshots, ctx }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshots = snapshots.before as RoleSnapshots | undefined\n const before = beforeSnapshots?.view\n const beforeUndo = beforeSnapshots?.undo ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const afterAcls = await loadRoleAclSnapshots(em, String(result.id))\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n const afterSnapshots = captureRoleSnapshots(result, afterAcls, custom)\n const after = afterSnapshots.view\n const changes = buildChanges(before ?? null, after as Record<string, unknown>, ['name', 'tenantId'])\n const customDiff = diffCustomFieldChanges(before?.custom, custom)\n for (const [key, diff] of Object.entries(customDiff)) {\n changes[`cf_${key}`] = diff\n }\n return {\n actionLabel: translate('auth.audit.roles.update', 'Update role'),\n resourceKind: 'auth.role',\n resourceId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n changes,\n snapshotBefore: before ?? null,\n snapshotAfter: after,\n payload: {\n undo: {\n before: beforeUndo,\n after: afterSnapshots.undo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<RoleUndoPayload>(logEntry)\n const before = undo?.before\n const after = undo?.after\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const updated = await de.updateOrmEntity({\n entity: Role,\n where: { id: before.id, deletedAt: null } as FilterQuery<Role>,\n apply: (entity) => {\n entity.name = before.name\n entity.tenantId = before.tenantId\n },\n })\n if (updated) {\n await restoreRoleAcls(em, before.id, before.acls)\n }\n const reset = buildCustomFieldResetMap(before.custom, after?.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: before.id,\n organizationId: null,\n tenantId: before.tenantId,\n values: reset,\n notify: false,\n })\n }\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: updated,\n identifiers: {\n id: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n },\n}\n\nconst deleteRoleCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, Role> = {\n id: 'auth.roles.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Role id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const existing = await findOneWithDecryption(em, Role, { id, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n if (!existing) return {}\n const acls = await loadRoleAclSnapshots(em, id)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: id,\n tenantId: existing.tenantId ? String(existing.tenantId) : null,\n })\n return { before: captureRoleSnapshots(existing, acls, custom) }\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Role id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const role = await findOneWithDecryption(em, Role, { id, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n if (!role) throw new CrudHttpError(404, { error: 'Role not found' })\n const activeAssignments = await em.count(UserRole, { role, deletedAt: null })\n if (activeAssignments > 0) throw new CrudHttpError(400, { error: 'Role has assigned users' })\n\n await em.nativeDelete(RoleAcl, { role: id })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const deleted = await de.deleteOrmEntity({\n entity: Role,\n where: { id, deletedAt: null } as FilterQuery<Role>,\n soft: false,\n })\n if (!deleted) throw new CrudHttpError(404, { error: 'Role not found' })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: deleted,\n identifiers: {\n id,\n organizationId: null,\n tenantId: deleted.tenantId ? String(deleted.tenantId) : null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return deleted\n },\n buildLog: async ({ snapshots, input }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshots = snapshots.before as RoleSnapshots | undefined\n const before = beforeSnapshots?.view\n const beforeUndo = beforeSnapshots?.undo ?? null\n const id = requireId(input, 'Role id required')\n return {\n actionLabel: translate('auth.audit.roles.delete', 'Delete role'),\n resourceKind: 'auth.role',\n resourceId: id,\n tenantId: before?.tenantId ?? null,\n snapshotBefore: before ?? null,\n payload: {\n undo: {\n before: beforeUndo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const before = extractUndoPayload<RoleUndoPayload>(logEntry)?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let role = await findOneWithDecryption(em, Role, { id: before.id }, {}, { tenantId: null, organizationId: null })\n if (role) {\n role.deletedAt = null\n role.name = before.name\n role.tenantId = before.tenantId\n await em.flush()\n } else {\n role = await de.createOrmEntity({\n entity: Role,\n data: {\n id: before.id,\n name: before.name,\n tenantId: before.tenantId,\n },\n })\n }\n await restoreRoleAcls(em, before.id, before.acls)\n const reset = buildCustomFieldResetMap(before.custom, undefined)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: role,\n identifiers: {\n id: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n },\n}\n\nregisterCommand(createRoleCommand)\nregisterCommand(updateRoleCommand)\nregisterCommand(deleteRoleCommand)\n\nfunction serializeRole(role: Role, custom?: Record<string, unknown> | null): SerializedRole {\n const payload: SerializedRole = {\n name: String(role.name ?? ''),\n tenantId: String(role.tenantId),\n }\n if (custom && Object.keys(custom).length) payload.custom = custom\n return payload\n}\n\nfunction captureRoleSnapshots(\n role: Role,\n acls: RoleAclSnapshot[] = [],\n custom?: Record<string, unknown> | null\n): RoleSnapshots {\n return {\n view: serializeRole(role, custom),\n undo: {\n id: String(role.id),\n name: String(role.name ?? ''),\n tenantId: String(role.tenantId),\n acls,\n ...(custom && Object.keys(custom).length ? { custom } : {}),\n },\n }\n}\n\nasync function loadRoleAclSnapshots(em: EntityManager, roleId: string): Promise<RoleAclSnapshot[]> {\n const entries = await findWithDecryption(em, RoleAcl, { role: roleId as unknown as Role }, {}, { tenantId: null, organizationId: null })\n return entries.map((entry) => ({\n id: entry.id ? String(entry.id) : null,\n tenantId: String(entry.tenantId),\n features: Array.isArray(entry.featuresJson) ? [...entry.featuresJson] : null,\n isSuperAdmin: Boolean(entry.isSuperAdmin),\n organizations: Array.isArray(entry.organizationsJson) ? [...entry.organizationsJson] : null,\n }))\n}\n\nasync function restoreRoleAcls(em: EntityManager, roleId: string, acls: RoleAclSnapshot[]) {\n await em.nativeDelete(RoleAcl, { role: roleId as unknown as Role })\n if (!acls.length) {\n await em.flush()\n return\n }\n const roleRef = em.getReference(Role, roleId)\n for (const acl of acls) {\n const entity = em.create(RoleAcl, {\n id: acl.id ?? undefined,\n role: roleRef,\n tenantId: acl.tenantId,\n featuresJson: acl.features ?? null,\n isSuperAdmin: acl.isSuperAdmin,\n organizationsJson: acl.organizations ?? null,\n createdAt: new Date(),\n })\n em.persist(entity)\n }\n await em.flush()\n}\n\ntype RoleUndoPayload = { before?: RoleUndoSnapshot | null; after?: RoleUndoSnapshot | null }\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import type { CommandHandler } from '@open-mercato/shared/lib/commands'\nimport { registerCommand } from '@open-mercato/shared/lib/commands'\nimport {\n parseWithCustomFields,\n setCustomFieldsIfAny,\n emitCrudSideEffects,\n emitCrudUndoSideEffects,\n buildChanges,\n requireId,\n} from '@open-mercato/shared/lib/commands/helpers'\nimport type { CrudEventsConfig, CrudIndexerConfig } from '@open-mercato/shared/lib/crud/types'\nimport { CrudHttpError } from '@open-mercato/shared/lib/crud/errors'\nimport { resolveTranslations } from '@open-mercato/shared/lib/i18n/server'\nimport type { DataEngine } from '@open-mercato/shared/lib/data/engine'\nimport { findOneWithDecryption, findWithDecryption } from '@open-mercato/shared/lib/encryption/find'\nimport type { EntityManager, FilterQuery } from '@mikro-orm/postgresql'\nimport { z } from 'zod'\nimport { Role, RoleAcl, UserRole } from '@open-mercato/core/modules/auth/data/entities'\nimport { E } from '#generated/entities.ids.generated'\nimport {\n loadCustomFieldSnapshot,\n buildCustomFieldResetMap,\n diffCustomFieldChanges,\n} from '@open-mercato/shared/lib/commands/customFieldSnapshots'\nimport { extractUndoPayload } from '@open-mercato/shared/lib/commands/undo'\n\ntype SerializedRole = {\n name: string\n tenantId: string\n custom?: Record<string, unknown>\n}\n\ntype RoleAclSnapshot = {\n id: string | null\n tenantId: string\n features: string[] | null\n isSuperAdmin: boolean\n organizations: string[] | null\n}\n\ntype RoleUndoSnapshot = {\n id: string\n name: string\n tenantId: string\n acls: RoleAclSnapshot[]\n custom?: Record<string, unknown>\n}\n\ntype RoleSnapshots = {\n view: SerializedRole\n undo: RoleUndoSnapshot\n}\n\nconst RESERVED_ROLE_NAMES = new Set(['superadmin', 'admin'])\n\nfunction isReservedRoleName(name: string | undefined | null): boolean {\n if (typeof name !== 'string') return false\n const normalized = name.trim().toLowerCase()\n return normalized.length > 0 && RESERVED_ROLE_NAMES.has(normalized)\n}\n\nfunction assertRoleNameAllowed(name: string | undefined | null) {\n if (isReservedRoleName(name)) {\n throw new CrudHttpError(400, { error: 'Role name is reserved' })\n }\n}\n\nconst createSchema = z.object({\n name: z.string().min(2).max(100),\n tenantId: z.string().uuid().optional(),\n})\n\nconst updateSchema = z.object({\n id: z.string().uuid(),\n name: z.string().min(2).max(100).optional(),\n tenantId: z.string().uuid().optional(),\n})\n\nexport const roleCrudEvents: CrudEventsConfig = {\n module: 'auth',\n entity: 'role',\n persistent: true,\n buildPayload: (ctx) => ({\n id: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nexport const roleCrudIndexer: CrudIndexerConfig = {\n entityType: E.auth.role,\n buildUpsertPayload: (ctx) => ({\n entityType: E.auth.role,\n recordId: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n buildDeletePayload: (ctx) => ({\n entityType: E.auth.role,\n recordId: ctx.identifiers.id,\n tenantId: ctx.identifiers.tenantId,\n }),\n}\n\nconst createRoleCommand: CommandHandler<Record<string, unknown>, Role> = {\n id: 'auth.roles.create',\n async execute(rawInput, ctx) {\n const rawBody = rawInput && typeof rawInput === 'object' ? rawInput as Record<string, unknown> : {}\n if ('tenantId' in rawBody && rawBody.tenantId === null) {\n throw new CrudHttpError(400, { error: 'tenantId cannot be null \u2014 global roles are not supported' })\n }\n const { parsed, custom } = parseWithCustomFields(createSchema, rawInput)\n assertRoleNameAllowed(parsed.name)\n const resolvedTenantId = parsed.tenantId ?? ctx.auth?.tenantId ?? null\n if (!resolvedTenantId) {\n throw new CrudHttpError(400, { error: 'tenantId is required \u2014 global roles are not supported' })\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const role = await de.createOrmEntity({\n entity: Role,\n data: {\n name: parsed.name,\n tenantId: resolvedTenantId,\n },\n })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: String(role.id),\n organizationId: null,\n tenantId: resolvedTenantId,\n values: custom,\n })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'created',\n entity: role,\n identifiers: {\n id: String(role.id),\n organizationId: null,\n tenantId: resolvedTenantId,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return role\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n return serializeRole(result, custom)\n },\n buildLog: async ({ result, ctx }) => {\n const { translate } = await resolveTranslations()\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n const snapshot = captureRoleSnapshots(result, [], custom)\n return {\n actionLabel: translate('auth.audit.roles.create', 'Create role'),\n resourceKind: 'auth.role',\n resourceId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n snapshotAfter: snapshot.view,\n payload: {\n undo: {\n after: snapshot.undo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<RoleUndoPayload>(logEntry)?.after\n if (!undo) return\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n await em.nativeDelete(RoleAcl, { role: undo.id as unknown as Role })\n if (undo.custom && Object.keys(undo.custom).length) {\n const reset = buildCustomFieldResetMap(undefined, undo.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: undo.id,\n organizationId: null,\n tenantId: undo.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n }\n await de.deleteOrmEntity({\n entity: Role,\n where: { id: undo.id, deletedAt: null } as FilterQuery<Role>,\n soft: false,\n })\n },\n}\n\nconst updateRoleCommand: CommandHandler<Record<string, unknown>, Role> = {\n id: 'auth.roles.update',\n async prepare(rawInput, ctx) {\n const { parsed } = parseWithCustomFields(updateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const existing = await findOneWithDecryption(em, Role, { id: parsed.id, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n if (!existing) throw new CrudHttpError(404, { error: 'Role not found' })\n const acls = await loadRoleAclSnapshots(em, parsed.id)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: parsed.id,\n tenantId: existing.tenantId ? String(existing.tenantId) : null,\n })\n return { before: captureRoleSnapshots(existing, acls, custom) }\n },\n async execute(rawInput, ctx) {\n const { parsed, custom } = parseWithCustomFields(updateSchema, rawInput)\n const em = (ctx.container.resolve('em') as EntityManager)\n const current = await findOneWithDecryption(em, Role, { id: parsed.id, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n if (!current) throw new CrudHttpError(404, { error: 'Role not found' })\n if (parsed.name !== undefined) {\n const nextName = parsed.name\n if (nextName !== current.name) assertRoleNameAllowed(nextName)\n if (nextName !== current.name) {\n const assignments = await em.count(UserRole, { role: current, deletedAt: null })\n if (assignments > 0) {\n throw new CrudHttpError(400, { error: 'Role name cannot be changed while users are assigned' })\n }\n }\n }\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const role = await de.updateOrmEntity({\n entity: Role,\n where: { id: parsed.id, deletedAt: null } as FilterQuery<Role>,\n apply: (entity) => {\n if (parsed.name !== undefined) entity.name = parsed.name\n if (parsed.tenantId !== undefined) entity.tenantId = parsed.tenantId\n },\n })\n if (!role) throw new CrudHttpError(404, { error: 'Role not found' })\n\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: String(role.id),\n organizationId: null,\n tenantId: role.tenantId ? String(role.tenantId) : null,\n values: custom,\n })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: role,\n identifiers: {\n id: String(role.id),\n organizationId: null,\n tenantId: role.tenantId ? String(role.tenantId) : null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return role\n },\n captureAfter: async (_input, result, ctx) => {\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n return serializeRole(result, custom)\n },\n buildLog: async ({ result, snapshots, ctx }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshots = snapshots.before as RoleSnapshots | undefined\n const before = beforeSnapshots?.view\n const beforeUndo = beforeSnapshots?.undo ?? null\n const em = (ctx.container.resolve('em') as EntityManager).fork()\n const afterAcls = await loadRoleAclSnapshots(em, String(result.id))\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n })\n const afterSnapshots = captureRoleSnapshots(result, afterAcls, custom)\n const after = afterSnapshots.view\n const changes = buildChanges(before ?? null, after as Record<string, unknown>, ['name', 'tenantId'])\n const customDiff = diffCustomFieldChanges(before?.custom, custom)\n for (const [key, diff] of Object.entries(customDiff)) {\n changes[`cf_${key}`] = diff\n }\n return {\n actionLabel: translate('auth.audit.roles.update', 'Update role'),\n resourceKind: 'auth.role',\n resourceId: String(result.id),\n tenantId: result.tenantId ? String(result.tenantId) : null,\n changes,\n snapshotBefore: before ?? null,\n snapshotAfter: after,\n payload: {\n undo: {\n before: beforeUndo,\n after: afterSnapshots.undo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const undo = extractUndoPayload<RoleUndoPayload>(logEntry)\n const before = undo?.before\n const after = undo?.after\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const updated = await de.updateOrmEntity({\n entity: Role,\n where: { id: before.id, deletedAt: null } as FilterQuery<Role>,\n apply: (entity) => {\n entity.name = before.name\n entity.tenantId = before.tenantId\n },\n })\n if (updated) {\n await restoreRoleAcls(em, before.id, before.acls)\n }\n const reset = buildCustomFieldResetMap(before.custom, after?.custom)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: before.id,\n organizationId: null,\n tenantId: before.tenantId,\n values: reset,\n notify: false,\n })\n }\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: updated,\n identifiers: {\n id: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n },\n}\n\nconst deleteRoleCommand: CommandHandler<{ body?: Record<string, unknown>; query?: Record<string, unknown> }, Role> = {\n id: 'auth.roles.delete',\n async prepare(input, ctx) {\n const id = requireId(input, 'Role id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const existing = await findOneWithDecryption(em, Role, { id, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n if (!existing) return {}\n const acls = await loadRoleAclSnapshots(em, id)\n const custom = await loadCustomFieldSnapshot(em, {\n entityId: E.auth.role,\n recordId: id,\n tenantId: existing.tenantId ? String(existing.tenantId) : null,\n })\n return { before: captureRoleSnapshots(existing, acls, custom) }\n },\n async execute(input, ctx) {\n const id = requireId(input, 'Role id required')\n const em = (ctx.container.resolve('em') as EntityManager)\n const role = await findOneWithDecryption(em, Role, { id, deletedAt: null }, {}, { tenantId: null, organizationId: null })\n if (!role) throw new CrudHttpError(404, { error: 'Role not found' })\n const activeAssignments = await em.count(UserRole, { role, deletedAt: null })\n if (activeAssignments > 0) throw new CrudHttpError(400, { error: 'Role has assigned users' })\n\n await em.nativeDelete(RoleAcl, { role: id })\n\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n const deleted = await de.deleteOrmEntity({\n entity: Role,\n where: { id, deletedAt: null } as FilterQuery<Role>,\n soft: false,\n })\n if (!deleted) throw new CrudHttpError(404, { error: 'Role not found' })\n\n await emitCrudSideEffects({\n dataEngine: de,\n action: 'deleted',\n entity: deleted,\n identifiers: {\n id,\n organizationId: null,\n tenantId: deleted.tenantId ? String(deleted.tenantId) : null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n\n return deleted\n },\n buildLog: async ({ snapshots, input }) => {\n const { translate } = await resolveTranslations()\n const beforeSnapshots = snapshots.before as RoleSnapshots | undefined\n const before = beforeSnapshots?.view\n const beforeUndo = beforeSnapshots?.undo ?? null\n const id = requireId(input, 'Role id required')\n return {\n actionLabel: translate('auth.audit.roles.delete', 'Delete role'),\n resourceKind: 'auth.role',\n resourceId: id,\n tenantId: before?.tenantId ?? null,\n snapshotBefore: before ?? null,\n payload: {\n undo: {\n before: beforeUndo,\n },\n },\n }\n },\n undo: async ({ logEntry, ctx }) => {\n const before = extractUndoPayload<RoleUndoPayload>(logEntry)?.before\n if (!before) return\n const em = (ctx.container.resolve('em') as EntityManager)\n const de = (ctx.container.resolve('dataEngine') as DataEngine)\n let role = await findOneWithDecryption(em, Role, { id: before.id }, {}, { tenantId: null, organizationId: null })\n if (role) {\n role.deletedAt = null\n role.name = before.name\n role.tenantId = before.tenantId\n await em.flush()\n } else {\n role = await de.createOrmEntity({\n entity: Role,\n data: {\n id: before.id,\n name: before.name,\n tenantId: before.tenantId,\n },\n })\n }\n await restoreRoleAcls(em, before.id, before.acls)\n const reset = buildCustomFieldResetMap(before.custom, undefined)\n if (Object.keys(reset).length) {\n await setCustomFieldsIfAny({\n dataEngine: de,\n entityId: E.auth.role,\n recordId: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n values: reset,\n notify: false,\n })\n }\n await emitCrudUndoSideEffects({\n dataEngine: de,\n action: 'updated',\n entity: role,\n identifiers: {\n id: before.id,\n organizationId: null,\n tenantId: before.tenantId ?? null,\n },\n events: roleCrudEvents,\n indexer: roleCrudIndexer,\n })\n },\n}\n\nregisterCommand(createRoleCommand)\nregisterCommand(updateRoleCommand)\nregisterCommand(deleteRoleCommand)\n\nfunction serializeRole(role: Role, custom?: Record<string, unknown> | null): SerializedRole {\n const payload: SerializedRole = {\n name: String(role.name ?? ''),\n tenantId: String(role.tenantId),\n }\n if (custom && Object.keys(custom).length) payload.custom = custom\n return payload\n}\n\nfunction captureRoleSnapshots(\n role: Role,\n acls: RoleAclSnapshot[] = [],\n custom?: Record<string, unknown> | null\n): RoleSnapshots {\n return {\n view: serializeRole(role, custom),\n undo: {\n id: String(role.id),\n name: String(role.name ?? ''),\n tenantId: String(role.tenantId),\n acls,\n ...(custom && Object.keys(custom).length ? { custom } : {}),\n },\n }\n}\n\nasync function loadRoleAclSnapshots(em: EntityManager, roleId: string): Promise<RoleAclSnapshot[]> {\n const entries = await findWithDecryption(em, RoleAcl, { role: roleId as unknown as Role }, {}, { tenantId: null, organizationId: null })\n return entries.map((entry) => ({\n id: entry.id ? String(entry.id) : null,\n tenantId: String(entry.tenantId),\n features: Array.isArray(entry.featuresJson) ? [...entry.featuresJson] : null,\n isSuperAdmin: Boolean(entry.isSuperAdmin),\n organizations: Array.isArray(entry.organizationsJson) ? [...entry.organizationsJson] : null,\n }))\n}\n\nasync function restoreRoleAcls(em: EntityManager, roleId: string, acls: RoleAclSnapshot[]) {\n await em.nativeDelete(RoleAcl, { role: roleId as unknown as Role })\n if (!acls.length) {\n await em.flush()\n return\n }\n const roleRef = em.getReference(Role, roleId)\n for (const acl of acls) {\n const entity = em.create(RoleAcl, {\n id: acl.id ?? undefined,\n role: roleRef,\n tenantId: acl.tenantId,\n featuresJson: acl.features ?? null,\n isSuperAdmin: acl.isSuperAdmin,\n organizationsJson: acl.organizations ?? null,\n createdAt: new Date(),\n })\n em.persist(entity)\n }\n await em.flush()\n}\n\ntype RoleUndoPayload = { before?: RoleUndoSnapshot | null; after?: RoleUndoSnapshot | null }\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,147 @@
1
+ import catalogAiTools from "./ai-tools.js";
2
+ const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
3
+ const SELECTION_CAP = 10;
4
+ function isUuid(value) {
5
+ return typeof value === "string" && UUID_REGEX.test(value);
6
+ }
7
+ function parseSelectionIds(raw) {
8
+ if (!raw) return [];
9
+ const unique = /* @__PURE__ */ new Set();
10
+ for (const token of raw.split(",")) {
11
+ const trimmed = token.trim();
12
+ if (isUuid(trimmed)) unique.add(trimmed);
13
+ if (unique.size >= SELECTION_CAP) break;
14
+ }
15
+ return Array.from(unique);
16
+ }
17
+ function findTool(name) {
18
+ return catalogAiTools.find((tool) => tool.name === name) ?? null;
19
+ }
20
+ function buildToolContext(container, tenantId, organizationId) {
21
+ return {
22
+ tenantId,
23
+ organizationId,
24
+ userId: null,
25
+ container,
26
+ userFeatures: [],
27
+ isSuperAdmin: true,
28
+ apiKeySecret: void 0,
29
+ sessionId: void 0
30
+ };
31
+ }
32
+ function renderContextBlock(label, payload) {
33
+ return `## Page context \u2014 ${label}
34
+ ${JSON.stringify(payload, null, 2)}`;
35
+ }
36
+ const SINGLE_PRODUCT_ENTITY_TYPES = /* @__PURE__ */ new Set([
37
+ "product",
38
+ "catalog.product",
39
+ "catalog:catalog_product"
40
+ ]);
41
+ const PRODUCTS_LIST_ENTITY_TYPES = /* @__PURE__ */ new Set([
42
+ "catalog.products.list",
43
+ "catalog.products.selection",
44
+ "products.list",
45
+ "products.selection"
46
+ ]);
47
+ async function invokeTool(toolName, args, toolContext, reasonPrefix) {
48
+ const tool = findTool(toolName);
49
+ if (!tool) {
50
+ console.warn(`[${reasonPrefix}] resolvePageContext: tool "${toolName}" not registered`);
51
+ return null;
52
+ }
53
+ try {
54
+ const result = await tool.handler(args, toolContext);
55
+ return result ?? null;
56
+ } catch (error) {
57
+ console.warn(
58
+ `[${reasonPrefix}] resolvePageContext: tool "${toolName}" failed (reason="hydration_error"); skipping`,
59
+ error instanceof Error ? error.message : error
60
+ );
61
+ return null;
62
+ }
63
+ }
64
+ async function hydrateCatalogAssistantContext(input) {
65
+ const tenantId = input.tenantId;
66
+ if (!tenantId) return null;
67
+ const entityType = input.entityType.trim().toLowerCase();
68
+ if (!entityType) return null;
69
+ const toolContext = buildToolContext(input.container, tenantId, input.organizationId);
70
+ if (SINGLE_PRODUCT_ENTITY_TYPES.has(entityType)) {
71
+ if (!isUuid(input.recordId)) return null;
72
+ const result = await invokeTool(
73
+ "catalog.get_product",
74
+ { productId: input.recordId },
75
+ toolContext,
76
+ "catalog.catalog_assistant"
77
+ );
78
+ if (!result || typeof result !== "object") return null;
79
+ if (result.found === false) return null;
80
+ return renderContextBlock(`Product ${input.recordId}`, result);
81
+ }
82
+ if (PRODUCTS_LIST_ENTITY_TYPES.has(entityType)) {
83
+ const ids = parseSelectionIds(input.recordId);
84
+ if (ids.length === 0) return null;
85
+ const result = await invokeTool(
86
+ "catalog.list_selected_products",
87
+ { productIds: ids },
88
+ toolContext,
89
+ "catalog.catalog_assistant"
90
+ );
91
+ if (!result || typeof result !== "object") return null;
92
+ const { items, missingIds } = result;
93
+ const summaries = Array.isArray(items) ? items.map((item) => item && typeof item === "object" ? item.product ?? null : null).filter((value) => value !== null) : [];
94
+ if (summaries.length === 0) return null;
95
+ return renderContextBlock(
96
+ `Products selection (${summaries.length} of ${ids.length})`,
97
+ { items: summaries, missingIds: missingIds ?? [] }
98
+ );
99
+ }
100
+ return null;
101
+ }
102
+ async function hydrateMerchandisingAssistantContext(input) {
103
+ const tenantId = input.tenantId;
104
+ if (!tenantId) return null;
105
+ const entityType = input.entityType.trim().toLowerCase();
106
+ if (!entityType) return null;
107
+ const toolContext = buildToolContext(input.container, tenantId, input.organizationId);
108
+ if (SINGLE_PRODUCT_ENTITY_TYPES.has(entityType)) {
109
+ if (!isUuid(input.recordId)) return null;
110
+ const result = await invokeTool(
111
+ "catalog.get_product_bundle",
112
+ { productId: input.recordId },
113
+ toolContext,
114
+ "catalog.merchandising_assistant"
115
+ );
116
+ if (!result || typeof result !== "object") return null;
117
+ if (result.found === false) return null;
118
+ return renderContextBlock(`Product bundle ${input.recordId}`, result);
119
+ }
120
+ if (PRODUCTS_LIST_ENTITY_TYPES.has(entityType)) {
121
+ const ids = parseSelectionIds(input.recordId);
122
+ if (ids.length === 0) return null;
123
+ const result = await invokeTool(
124
+ "catalog.list_selected_products",
125
+ { productIds: ids },
126
+ toolContext,
127
+ "catalog.merchandising_assistant"
128
+ );
129
+ if (!result || typeof result !== "object") return null;
130
+ const { items, missingIds } = result;
131
+ const bundles = Array.isArray(items) ? items : [];
132
+ if (bundles.length === 0) return null;
133
+ return renderContextBlock(
134
+ `Products selection bundles (${bundles.length} of ${ids.length})`,
135
+ {
136
+ items: bundles,
137
+ missingIds: missingIds ?? []
138
+ }
139
+ );
140
+ }
141
+ return null;
142
+ }
143
+ export {
144
+ hydrateCatalogAssistantContext,
145
+ hydrateMerchandisingAssistantContext
146
+ };
147
+ //# sourceMappingURL=ai-agents-context.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../src/modules/catalog/ai-agents-context.ts"],
4
+ "sourcesContent": ["/**\n * Page-context hydration helpers for the catalog agents\n * (Phase 3 WS-A, Step 5.2).\n *\n * Two flavors:\n *\n * 1. `hydrateCatalogAssistantContext` \u2014 `catalog.catalog_assistant`. Loads\n * a lightweight product summary for a single UUID (`catalog.product`),\n * or a batch of up to 10 summaries when the request carries a\n * comma-separated UUID list keyed as `catalog.products.list`.\n *\n * 2. `hydrateMerchandisingAssistantContext` \u2014\n * `catalog.merchandising_assistant`. Loads the full\n * `catalog.get_product_bundle` aggregate for a single product, or a\n * capped-at-10 selection via `catalog.list_selected_products`. When\n * the request carries the products-list page view, the incoming\n * `pageContext.extra.filter` is pretty-printed into the context block\n * so the agent can reason about the narrowed set even when no\n * selection is active.\n *\n * Both helpers route every read through an existing tool-pack handler\n * (Step 3.10 base pack + Step 3.11 D18 pack) so the agent-reachable\n * surface and the hydration surface stay in lock-step. Tenant + org\n * scope is enforced by the tool handlers themselves; cross-tenant ids\n * surface as `{ found: false }` / `missingIds`, which we translate to a\n * silent null return (the runtime then proceeds without hydration).\n *\n * Error swallowing is required by the Step 3.2 runtime contract \u2014 a\n * hydration fault MUST NEVER break the chat request.\n */\nimport type { AwilixContainer } from 'awilix'\nimport catalogAiTools from './ai-tools'\nimport type {\n CatalogAiToolDefinition,\n CatalogToolContext,\n} from './ai-tools/types'\n\nconst UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i\n\nconst SELECTION_CAP = 10\n\nfunction isUuid(value: unknown): value is string {\n return typeof value === 'string' && UUID_REGEX.test(value)\n}\n\nfunction parseSelectionIds(raw: string): string[] {\n if (!raw) return []\n const unique = new Set<string>()\n for (const token of raw.split(',')) {\n const trimmed = token.trim()\n if (isUuid(trimmed)) unique.add(trimmed)\n if (unique.size >= SELECTION_CAP) break\n }\n return Array.from(unique)\n}\n\nfunction findTool(name: string): CatalogAiToolDefinition | null {\n return (\n (catalogAiTools as CatalogAiToolDefinition[]).find((tool) => tool.name === name) ?? null\n )\n}\n\nfunction buildToolContext(\n container: AwilixContainer,\n tenantId: string,\n organizationId: string | null,\n): CatalogToolContext {\n return {\n tenantId,\n organizationId,\n userId: null,\n container,\n userFeatures: [],\n isSuperAdmin: true,\n apiKeySecret: undefined,\n sessionId: undefined,\n }\n}\n\nfunction renderContextBlock(label: string, payload: unknown): string {\n return `## Page context \u2014 ${label}\\n${JSON.stringify(payload, null, 2)}`\n}\n\nexport interface HydrateCatalogContextInput {\n entityType: string\n recordId: string\n container: AwilixContainer\n tenantId: string | null\n organizationId: string | null\n}\n\nconst SINGLE_PRODUCT_ENTITY_TYPES = new Set([\n 'product',\n 'catalog.product',\n 'catalog:catalog_product',\n])\n\nconst PRODUCTS_LIST_ENTITY_TYPES = new Set([\n 'catalog.products.list',\n 'catalog.products.selection',\n 'products.list',\n 'products.selection',\n])\n\nasync function invokeTool(\n toolName: string,\n args: Record<string, unknown>,\n toolContext: CatalogToolContext,\n reasonPrefix: string,\n): Promise<unknown | null> {\n const tool = findTool(toolName)\n if (!tool) {\n console.warn(`[${reasonPrefix}] resolvePageContext: tool \"${toolName}\" not registered`)\n return null\n }\n try {\n const result = await tool.handler(args as never, toolContext)\n return result ?? null\n } catch (error) {\n console.warn(\n `[${reasonPrefix}] resolvePageContext: tool \"${toolName}\" failed (reason=\"hydration_error\"); skipping`,\n error instanceof Error ? error.message : error,\n )\n return null\n }\n}\n\n// -----------------------------------------------------------------------------\n// catalog.catalog_assistant hydration\n// -----------------------------------------------------------------------------\n\nexport async function hydrateCatalogAssistantContext(\n input: HydrateCatalogContextInput,\n): Promise<string | null> {\n const tenantId = input.tenantId\n if (!tenantId) return null\n const entityType = input.entityType.trim().toLowerCase()\n if (!entityType) return null\n const toolContext = buildToolContext(input.container, tenantId, input.organizationId)\n\n if (SINGLE_PRODUCT_ENTITY_TYPES.has(entityType)) {\n if (!isUuid(input.recordId)) return null\n const result = await invokeTool(\n 'catalog.get_product',\n { productId: input.recordId },\n toolContext,\n 'catalog.catalog_assistant',\n )\n if (!result || typeof result !== 'object') return null\n if ((result as { found?: boolean }).found === false) return null\n return renderContextBlock(`Product ${input.recordId}`, result)\n }\n\n if (PRODUCTS_LIST_ENTITY_TYPES.has(entityType)) {\n const ids = parseSelectionIds(input.recordId)\n if (ids.length === 0) return null\n // Reuse the D18 merchandising bundle tool \u2014 its result carries\n // summaries inside full bundles. For the base catalog_assistant we\n // keep the payload lightweight by projecting each bundle onto the\n // summary subset the agent cares about.\n const result = await invokeTool(\n 'catalog.list_selected_products',\n { productIds: ids },\n toolContext,\n 'catalog.catalog_assistant',\n )\n if (!result || typeof result !== 'object') return null\n const { items, missingIds } = result as {\n items?: Array<{ product?: unknown }>\n missingIds?: string[]\n }\n const summaries = Array.isArray(items)\n ? items\n .map((item) => (item && typeof item === 'object' ? (item as { product?: unknown }).product ?? null : null))\n .filter((value) => value !== null)\n : []\n if (summaries.length === 0) return null\n return renderContextBlock(\n `Products selection (${summaries.length} of ${ids.length})`,\n { items: summaries, missingIds: missingIds ?? [] },\n )\n }\n\n return null\n}\n\n// -----------------------------------------------------------------------------\n// catalog.merchandising_assistant hydration\n// -----------------------------------------------------------------------------\n\nexport async function hydrateMerchandisingAssistantContext(\n input: HydrateCatalogContextInput,\n): Promise<string | null> {\n const tenantId = input.tenantId\n if (!tenantId) return null\n const entityType = input.entityType.trim().toLowerCase()\n if (!entityType) return null\n const toolContext = buildToolContext(input.container, tenantId, input.organizationId)\n\n if (SINGLE_PRODUCT_ENTITY_TYPES.has(entityType)) {\n if (!isUuid(input.recordId)) return null\n const result = await invokeTool(\n 'catalog.get_product_bundle',\n { productId: input.recordId },\n toolContext,\n 'catalog.merchandising_assistant',\n )\n if (!result || typeof result !== 'object') return null\n if ((result as { found?: boolean }).found === false) return null\n return renderContextBlock(`Product bundle ${input.recordId}`, result)\n }\n\n if (PRODUCTS_LIST_ENTITY_TYPES.has(entityType)) {\n const ids = parseSelectionIds(input.recordId)\n if (ids.length === 0) return null\n const result = await invokeTool(\n 'catalog.list_selected_products',\n { productIds: ids },\n toolContext,\n 'catalog.merchandising_assistant',\n )\n if (!result || typeof result !== 'object') return null\n const { items, missingIds } = result as {\n items?: unknown[]\n missingIds?: string[]\n }\n const bundles = Array.isArray(items) ? items : []\n if (bundles.length === 0) return null\n return renderContextBlock(\n `Products selection bundles (${bundles.length} of ${ids.length})`,\n {\n items: bundles,\n missingIds: missingIds ?? [],\n },\n )\n }\n\n return null\n}\n"],
5
+ "mappings": "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",
6
+ "names": []
7
+ }