@open-mercato/core 0.5.1-develop.2652.0276e72e45 → 0.5.1-develop.2663.2c29774b5b

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 (58) hide show
  1. package/.turbo/turbo-build.log +1 -1
  2. package/AGENTS.md +26 -0
  3. package/dist/modules/auth/lib/backendChrome.js +3 -1
  4. package/dist/modules/auth/lib/backendChrome.js.map +2 -2
  5. package/dist/modules/auth/services/rbacService.js +8 -2
  6. package/dist/modules/auth/services/rbacService.js.map +2 -2
  7. package/dist/modules/customer_accounts/api/password/reset-confirm.js +7 -0
  8. package/dist/modules/customer_accounts/api/password/reset-confirm.js.map +2 -2
  9. package/dist/modules/customer_accounts/api/portal/nav.js +77 -0
  10. package/dist/modules/customer_accounts/api/portal/nav.js.map +7 -0
  11. package/dist/modules/customer_accounts/api/signup.js +20 -8
  12. package/dist/modules/customer_accounts/api/signup.js.map +2 -2
  13. package/dist/modules/customer_accounts/services/customerSessionService.js +32 -0
  14. package/dist/modules/customer_accounts/services/customerSessionService.js.map +2 -2
  15. package/dist/modules/directory/api/organizations/route.js +10 -0
  16. package/dist/modules/directory/api/organizations/route.js.map +3 -3
  17. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js +13 -2
  18. package/dist/modules/directory/backend/directory/organizations/[id]/edit/page.js.map +2 -2
  19. package/dist/modules/directory/backend/directory/organizations/create/page.js +12 -2
  20. package/dist/modules/directory/backend/directory/organizations/create/page.js.map +2 -2
  21. package/dist/modules/messages/components/message-detail/hooks/useMessageDetails.js +4 -3
  22. package/dist/modules/messages/components/message-detail/hooks/useMessageDetails.js.map +2 -2
  23. package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.meta.js +17 -0
  24. package/dist/modules/portal/frontend/[orgSlug]/portal/dashboard/page.meta.js.map +7 -0
  25. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.meta.js +11 -0
  26. package/dist/modules/portal/frontend/[orgSlug]/portal/login/page.meta.js.map +7 -0
  27. package/dist/modules/portal/frontend/[orgSlug]/portal/page.meta.js +11 -0
  28. package/dist/modules/portal/frontend/[orgSlug]/portal/page.meta.js.map +7 -0
  29. package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.meta.js +17 -0
  30. package/dist/modules/portal/frontend/[orgSlug]/portal/profile/page.meta.js.map +7 -0
  31. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.meta.js +11 -0
  32. package/dist/modules/portal/frontend/[orgSlug]/portal/signup/page.meta.js.map +7 -0
  33. package/dist/modules/portal/frontend/[orgSlug]/portal/verify/page.meta.js +11 -0
  34. package/dist/modules/portal/frontend/[orgSlug]/portal/verify/page.meta.js.map +7 -0
  35. package/dist/modules/workflows/lib/activity-executor.js +35 -17
  36. package/dist/modules/workflows/lib/activity-executor.js.map +2 -2
  37. package/package.json +3 -3
  38. package/src/modules/auth/lib/backendChrome.tsx +3 -1
  39. package/src/modules/auth/services/rbacService.ts +8 -2
  40. package/src/modules/customer_accounts/api/password/reset-confirm.ts +9 -0
  41. package/src/modules/customer_accounts/api/portal/nav.ts +87 -0
  42. package/src/modules/customer_accounts/api/signup.ts +23 -7
  43. package/src/modules/customer_accounts/services/customerSessionService.ts +39 -0
  44. package/src/modules/directory/api/organizations/route.ts +11 -0
  45. package/src/modules/directory/backend/directory/organizations/[id]/edit/page.tsx +17 -3
  46. package/src/modules/directory/backend/directory/organizations/create/page.tsx +15 -3
  47. package/src/modules/directory/i18n/de.json +2 -0
  48. package/src/modules/directory/i18n/en.json +2 -0
  49. package/src/modules/directory/i18n/es.json +2 -0
  50. package/src/modules/directory/i18n/pl.json +2 -0
  51. package/src/modules/messages/components/message-detail/hooks/useMessageDetails.ts +4 -3
  52. package/src/modules/portal/frontend/[orgSlug]/portal/dashboard/page.meta.ts +15 -0
  53. package/src/modules/portal/frontend/[orgSlug]/portal/login/page.meta.ts +9 -0
  54. package/src/modules/portal/frontend/[orgSlug]/portal/page.meta.ts +9 -0
  55. package/src/modules/portal/frontend/[orgSlug]/portal/profile/page.meta.ts +15 -0
  56. package/src/modules/portal/frontend/[orgSlug]/portal/signup/page.meta.ts +9 -0
  57. package/src/modules/portal/frontend/[orgSlug]/portal/verify/page.meta.ts +9 -0
  58. package/src/modules/workflows/lib/activity-executor.ts +64 -25
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../../src/modules/directory/backend/directory/organizations/create/page.tsx"],
4
- "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { E } from '#generated/entities.ids.generated'\nimport { OrganizationSelect } from '@open-mercato/core/modules/directory/components/OrganizationSelect'\nimport { TenantSelect } from '@open-mercato/core/modules/directory/components/TenantSelect'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { type OrganizationTreeNode } from '@open-mercato/core/modules/directory/lib/tree'\n\ntype TreeResponse = {\n items: OrganizationTreeNode[]\n}\n\ntype ChildTreeSelectProps = {\n nodes: OrganizationTreeNode[]\n value: string[]\n onChange: (vals: string[]) => void\n}\n\nfunction ChildTreeSelect({ nodes, value, onChange }: ChildTreeSelectProps) {\n const t = useT()\n const selected = React.useMemo(() => new Set(value), [value])\n const handleToggle = React.useCallback((id: string) => {\n const next = new Set(value)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n onChange(Array.from(next))\n }, [value, onChange])\n\n if (!nodes.length) {\n return (\n <div className=\"text-sm text-muted-foreground\">\n {t('directory.organizations.form.children.empty', 'No organizations available to assign.')}\n </div>\n )\n }\n\n return (\n <div className=\"rounded border px-3 py-2 max-h-64 overflow-auto space-y-2\">\n <TreeCheckboxGroup nodes={nodes} selected={selected} onToggle={handleToggle} level={0} />\n </div>\n )\n}\n\nfunction TreeCheckboxGroup({ nodes, selected, onToggle, level }: { nodes: OrganizationTreeNode[]; selected: Set<string>; onToggle: (id: string) => void; level: number }) {\n return (\n <div className={level === 0 ? 'space-y-1' : 'space-y-1 pl-5'}>\n {nodes.map((node) => (\n <div key={node.id} className=\"space-y-1\">\n <label className=\"inline-flex items-start gap-2 text-sm\">\n <input\n type=\"checkbox\"\n className=\"size-4 mt-0.5\"\n checked={selected.has(node.id)}\n onChange={() => onToggle(node.id)}\n />\n <span>{node.name}</span>\n </label>\n {node.children?.length ? (\n <TreeCheckboxGroup nodes={node.children} selected={selected} onToggle={onToggle} level={level + 1} />\n ) : null}\n </div>\n ))}\n </div>\n )\n}\n\nexport default function CreateOrganizationPage() {\n const [tree, setTree] = React.useState<OrganizationTreeNode[]>([])\n const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false)\n const [selectedTenantId, setSelectedTenantId] = React.useState<string | null>(null)\n const t = useT()\n\n const loadTree = React.useCallback(async (tenantId: string | null) => {\n const params = new URLSearchParams({ view: 'tree', includeInactive: 'true' })\n if (tenantId) params.set('tenantId', tenantId)\n try {\n const call = await apiCall<TreeResponse>(`/api/directory/organizations?${params.toString()}`)\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n setTree(items)\n } catch {\n setTree([])\n }\n }, [])\n\n React.useEffect(() => {\n let cancelled = false\n async function bootstrap() {\n try {\n const call = await apiCall<{ isSuperAdmin?: boolean }>('/api/auth/roles?page=1&pageSize=1')\n if (!cancelled) setActorIsSuperAdmin(Boolean(call.result?.isSuperAdmin))\n } catch {\n if (!cancelled) setActorIsSuperAdmin(false)\n }\n if (!cancelled) await loadTree(null)\n }\n bootstrap()\n return () => { cancelled = true }\n }, [loadTree])\n\n React.useEffect(() => {\n if (!actorIsSuperAdmin) return\n void loadTree(selectedTenantId)\n }, [actorIsSuperAdmin, loadTree, selectedTenantId])\n\n const fields = React.useMemo<CrudField[]>(() => [\n ...(actorIsSuperAdmin ? [\n {\n id: 'tenantId',\n label: t('directory.organizations.form.field.tenant', 'Tenant'),\n type: 'custom',\n required: true,\n component: ({ value, setValue }) => (\n <TenantSelect\n id=\"tenantId\"\n value={typeof value === 'string' ? value : selectedTenantId}\n onChange={(next) => {\n const normalized = next ?? null\n setSelectedTenantId(normalized)\n setValue(normalized)\n }}\n includeEmptyOption\n emptyOptionLabel={t('directory.organizations.form.tenant.select', 'Select tenant')}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n />\n ),\n } as CrudField,\n ] : []),\n { id: 'name', label: t('directory.organizations.form.field.name', 'Name'), type: 'text', required: true },\n {\n id: 'parentId',\n label: t('directory.organizations.form.field.parent', 'Parent'),\n type: 'custom',\n component: ({ id, value, setValue }) => (\n <OrganizationSelect\n id={id}\n value={value ? String(value) : null}\n onChange={(next) => setValue(next ?? '')}\n tenantId={selectedTenantId}\n fetchOnMount={true}\n includeEmptyOption\n emptyOptionLabel={t('directory.organizations.form.rootOption', '\u2014 Root level \u2014')}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n />\n ),\n },\n {\n id: 'childIds',\n label: t('directory.organizations.form.field.children', 'Children'),\n type: 'custom',\n component: ({ value, setValue }) => (\n <ChildTreeSelect\n nodes={tree}\n value={Array.isArray(value) ? value : []}\n onChange={(vals) => setValue(vals)}\n />\n ),\n },\n { id: 'isActive', label: t('directory.organizations.form.field.isActive', 'Active'), type: 'checkbox' },\n ], [actorIsSuperAdmin, selectedTenantId, t, tree])\n\n const detailFields = React.useMemo(() => (\n actorIsSuperAdmin\n ? ['tenantId', 'name', 'parentId', 'childIds', 'isActive']\n : ['name', 'parentId', 'childIds', 'isActive']\n ), [actorIsSuperAdmin])\n\n const groups: CrudFormGroup[] = React.useMemo(() => ([\n { id: 'details', title: t('directory.organizations.form.group.details', 'Details'), column: 1, fields: detailFields },\n { id: 'custom', title: t('directory.organizations.form.group.customFields', 'Custom Data'), column: 2, kind: 'customFields' },\n ]), [detailFields, t])\n const formTitle = t('directory.nav.organizations.create', 'Create Organization')\n const successMessage = encodeURIComponent(t('directory.organizations.flash.created', 'Organization created'))\n\n return (\n <Page>\n <PageBody>\n <CrudForm\n title={formTitle}\n backHref=\"/backend/directory/organizations\"\n fields={fields}\n groups={groups}\n entityId={E.directory.organization}\n initialValues={{ tenantId: selectedTenantId ?? null, name: '', parentId: '', childIds: [], isActive: true }}\n submitLabel={t('directory.organizations.form.action.create', 'Create')}\n cancelHref=\"/backend/directory/organizations\"\n successRedirect={`/backend/directory/organizations?flash=${successMessage}&type=success`}\n onSubmit={async (values) => {\n await submitCreateOrganization({\n values: values as Record<string, unknown>,\n actorIsSuperAdmin,\n selectedTenantId,\n messages: {\n tenantRequired: t('directory.organizations.errors.tenantRequired', 'Tenant selection is required for super administrators'),\n },\n })\n }}\n />\n </PageBody>\n </Page>\n )\n}\n\ntype CreateOrganizationPayload = {\n name: string\n isActive: boolean\n parentId: string | null\n childIds: string[]\n tenantId?: string\n customFields?: Record<string, unknown>\n}\n\ntype CreateOrganizationRequest = (payload: CreateOrganizationPayload) => Promise<void>\n\nasync function defaultCreateOrganizationRequest(payload: CreateOrganizationPayload) {\n await createCrud('directory/organizations', payload)\n}\n\nexport async function submitCreateOrganization(options: {\n values: Record<string, unknown>\n actorIsSuperAdmin: boolean\n selectedTenantId: string | null\n createOrganization?: CreateOrganizationRequest\n messages?: {\n tenantRequired?: string\n }\n}): Promise<void> {\n const {\n values,\n actorIsSuperAdmin,\n selectedTenantId,\n createOrganization = defaultCreateOrganizationRequest,\n messages,\n } = options\n\n const customFields = collectCustomFieldValues(values)\n\n const tenantValue =\n typeof values.tenantId === 'string' && values.tenantId.trim().length\n ? values.tenantId.trim()\n : selectedTenantId\n\n if (actorIsSuperAdmin && !tenantValue) {\n const message = messages?.tenantRequired ?? 'Tenant selection is required for super administrators'\n throw createCrudFormError(message, {\n tenantId: message,\n })\n }\n\n const payload: CreateOrganizationPayload = {\n name: typeof values.name === 'string' ? values.name : '',\n isActive: values.isActive !== false,\n parentId: typeof values.parentId === 'string' && values.parentId.length\n ? values.parentId\n : null,\n childIds: Array.isArray(values.childIds) ? values.childIds.filter((id): id is string => typeof id === 'string') : [],\n }\n\n if (tenantValue) payload.tenantId = tenantValue\n if (Object.keys(customFields).length > 0) payload.customFields = customFields\n\n await createOrganization(payload)\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["\"use client\"\nimport * as React from 'react'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { E } from '#generated/entities.ids.generated'\nimport { OrganizationSelect } from '@open-mercato/core/modules/directory/components/OrganizationSelect'\nimport { TenantSelect } from '@open-mercato/core/modules/directory/components/TenantSelect'\nimport { Page, PageBody } from '@open-mercato/ui/backend/Page'\nimport { CrudForm, type CrudField, type CrudFormGroup } from '@open-mercato/ui/backend/CrudForm'\nimport { apiCall } from '@open-mercato/ui/backend/utils/apiCall'\nimport { collectCustomFieldValues } from '@open-mercato/ui/backend/utils/customFieldValues'\nimport { createCrud } from '@open-mercato/ui/backend/utils/crud'\nimport { createCrudFormError } from '@open-mercato/ui/backend/utils/serverErrors'\nimport { type OrganizationTreeNode } from '@open-mercato/core/modules/directory/lib/tree'\n\ntype TreeResponse = {\n items: OrganizationTreeNode[]\n}\n\ntype ChildTreeSelectProps = {\n nodes: OrganizationTreeNode[]\n value: string[]\n onChange: (vals: string[]) => void\n}\n\nfunction ChildTreeSelect({ nodes, value, onChange }: ChildTreeSelectProps) {\n const t = useT()\n const selected = React.useMemo(() => new Set(value), [value])\n const handleToggle = React.useCallback((id: string) => {\n const next = new Set(value)\n if (next.has(id)) next.delete(id)\n else next.add(id)\n onChange(Array.from(next))\n }, [value, onChange])\n\n if (!nodes.length) {\n return (\n <div className=\"text-sm text-muted-foreground\">\n {t('directory.organizations.form.children.empty', 'No organizations available to assign.')}\n </div>\n )\n }\n\n return (\n <div className=\"rounded border px-3 py-2 max-h-64 overflow-auto space-y-2\">\n <TreeCheckboxGroup nodes={nodes} selected={selected} onToggle={handleToggle} level={0} />\n </div>\n )\n}\n\nfunction TreeCheckboxGroup({ nodes, selected, onToggle, level }: { nodes: OrganizationTreeNode[]; selected: Set<string>; onToggle: (id: string) => void; level: number }) {\n return (\n <div className={level === 0 ? 'space-y-1' : 'space-y-1 pl-5'}>\n {nodes.map((node) => (\n <div key={node.id} className=\"space-y-1\">\n <label className=\"inline-flex items-start gap-2 text-sm\">\n <input\n type=\"checkbox\"\n className=\"size-4 mt-0.5\"\n checked={selected.has(node.id)}\n onChange={() => onToggle(node.id)}\n />\n <span>{node.name}</span>\n </label>\n {node.children?.length ? (\n <TreeCheckboxGroup nodes={node.children} selected={selected} onToggle={onToggle} level={level + 1} />\n ) : null}\n </div>\n ))}\n </div>\n )\n}\n\nexport default function CreateOrganizationPage() {\n const [tree, setTree] = React.useState<OrganizationTreeNode[]>([])\n const [actorIsSuperAdmin, setActorIsSuperAdmin] = React.useState(false)\n const [selectedTenantId, setSelectedTenantId] = React.useState<string | null>(null)\n const t = useT()\n\n const loadTree = React.useCallback(async (tenantId: string | null) => {\n const params = new URLSearchParams({ view: 'tree', includeInactive: 'true' })\n if (tenantId) params.set('tenantId', tenantId)\n try {\n const call = await apiCall<TreeResponse>(`/api/directory/organizations?${params.toString()}`)\n const items = Array.isArray(call.result?.items) ? call.result.items : []\n setTree(items)\n } catch {\n setTree([])\n }\n }, [])\n\n React.useEffect(() => {\n let cancelled = false\n async function bootstrap() {\n try {\n const call = await apiCall<{ isSuperAdmin?: boolean }>('/api/auth/roles?page=1&pageSize=1')\n if (!cancelled) setActorIsSuperAdmin(Boolean(call.result?.isSuperAdmin))\n } catch {\n if (!cancelled) setActorIsSuperAdmin(false)\n }\n if (!cancelled) await loadTree(null)\n }\n bootstrap()\n return () => { cancelled = true }\n }, [loadTree])\n\n React.useEffect(() => {\n if (!actorIsSuperAdmin) return\n void loadTree(selectedTenantId)\n }, [actorIsSuperAdmin, loadTree, selectedTenantId])\n\n const fields = React.useMemo<CrudField[]>(() => [\n ...(actorIsSuperAdmin ? [\n {\n id: 'tenantId',\n label: t('directory.organizations.form.field.tenant', 'Tenant'),\n type: 'custom',\n required: true,\n component: ({ value, setValue }) => (\n <TenantSelect\n id=\"tenantId\"\n value={typeof value === 'string' ? value : selectedTenantId}\n onChange={(next) => {\n const normalized = next ?? null\n setSelectedTenantId(normalized)\n setValue(normalized)\n }}\n includeEmptyOption\n emptyOptionLabel={t('directory.organizations.form.tenant.select', 'Select tenant')}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n />\n ),\n } as CrudField,\n ] : []),\n { id: 'name', label: t('directory.organizations.form.field.name', 'Name'), type: 'text', required: true },\n {\n id: 'slug',\n label: t('directory.organizations.form.field.slug', 'Slug'),\n type: 'text',\n description: t('directory.organizations.form.field.slug.description', 'URL-safe identifier used for the customer portal (lowercase letters, numbers, hyphens, underscores). Generated from the name when left blank.'),\n },\n {\n id: 'parentId',\n label: t('directory.organizations.form.field.parent', 'Parent'),\n type: 'custom',\n component: ({ id, value, setValue }) => (\n <OrganizationSelect\n id={id}\n value={value ? String(value) : null}\n onChange={(next) => setValue(next ?? '')}\n tenantId={selectedTenantId}\n fetchOnMount={true}\n includeEmptyOption\n emptyOptionLabel={t('directory.organizations.form.rootOption', '\u2014 Root level \u2014')}\n className=\"w-full h-9 rounded border px-2 text-sm\"\n />\n ),\n },\n {\n id: 'childIds',\n label: t('directory.organizations.form.field.children', 'Children'),\n type: 'custom',\n component: ({ value, setValue }) => (\n <ChildTreeSelect\n nodes={tree}\n value={Array.isArray(value) ? value : []}\n onChange={(vals) => setValue(vals)}\n />\n ),\n },\n { id: 'isActive', label: t('directory.organizations.form.field.isActive', 'Active'), type: 'checkbox' },\n ], [actorIsSuperAdmin, selectedTenantId, t, tree])\n\n const detailFields = React.useMemo(() => (\n actorIsSuperAdmin\n ? ['tenantId', 'name', 'slug', 'parentId', 'childIds', 'isActive']\n : ['name', 'slug', 'parentId', 'childIds', 'isActive']\n ), [actorIsSuperAdmin])\n\n const groups: CrudFormGroup[] = React.useMemo(() => ([\n { id: 'details', title: t('directory.organizations.form.group.details', 'Details'), column: 1, fields: detailFields },\n { id: 'custom', title: t('directory.organizations.form.group.customFields', 'Custom Data'), column: 2, kind: 'customFields' },\n ]), [detailFields, t])\n const formTitle = t('directory.nav.organizations.create', 'Create Organization')\n const successMessage = encodeURIComponent(t('directory.organizations.flash.created', 'Organization created'))\n\n return (\n <Page>\n <PageBody>\n <CrudForm\n title={formTitle}\n backHref=\"/backend/directory/organizations\"\n fields={fields}\n groups={groups}\n entityId={E.directory.organization}\n initialValues={{ tenantId: selectedTenantId ?? null, name: '', slug: '', parentId: '', childIds: [], isActive: true }}\n submitLabel={t('directory.organizations.form.action.create', 'Create')}\n cancelHref=\"/backend/directory/organizations\"\n successRedirect={`/backend/directory/organizations?flash=${successMessage}&type=success`}\n onSubmit={async (values) => {\n await submitCreateOrganization({\n values: values as Record<string, unknown>,\n actorIsSuperAdmin,\n selectedTenantId,\n messages: {\n tenantRequired: t('directory.organizations.errors.tenantRequired', 'Tenant selection is required for super administrators'),\n },\n })\n }}\n />\n </PageBody>\n </Page>\n )\n}\n\ntype CreateOrganizationPayload = {\n name: string\n slug?: string | null\n isActive: boolean\n parentId: string | null\n childIds: string[]\n tenantId?: string\n customFields?: Record<string, unknown>\n}\n\ntype CreateOrganizationRequest = (payload: CreateOrganizationPayload) => Promise<void>\n\nasync function defaultCreateOrganizationRequest(payload: CreateOrganizationPayload) {\n await createCrud('directory/organizations', payload)\n}\n\nexport async function submitCreateOrganization(options: {\n values: Record<string, unknown>\n actorIsSuperAdmin: boolean\n selectedTenantId: string | null\n createOrganization?: CreateOrganizationRequest\n messages?: {\n tenantRequired?: string\n }\n}): Promise<void> {\n const {\n values,\n actorIsSuperAdmin,\n selectedTenantId,\n createOrganization = defaultCreateOrganizationRequest,\n messages,\n } = options\n\n const customFields = collectCustomFieldValues(values)\n\n const tenantValue =\n typeof values.tenantId === 'string' && values.tenantId.trim().length\n ? values.tenantId.trim()\n : selectedTenantId\n\n if (actorIsSuperAdmin && !tenantValue) {\n const message = messages?.tenantRequired ?? 'Tenant selection is required for super administrators'\n throw createCrudFormError(message, {\n tenantId: message,\n })\n }\n\n const payload: CreateOrganizationPayload = {\n name: typeof values.name === 'string' ? values.name : '',\n isActive: values.isActive !== false,\n parentId: typeof values.parentId === 'string' && values.parentId.length\n ? values.parentId\n : null,\n childIds: Array.isArray(values.childIds) ? values.childIds.filter((id): id is string => typeof id === 'string') : [],\n }\n\n if (typeof values.slug === 'string') {\n const trimmedSlug = values.slug.trim()\n if (trimmedSlug.length) payload.slug = trimmedSlug\n }\n\n if (tenantValue) payload.tenantId = tenantValue\n if (Object.keys(customFields).length > 0) payload.customFields = customFields\n\n await createOrganization(payload)\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
@@ -19,8 +19,9 @@ function useMessageDetails(id) {
19
19
  scopeVersion,
20
20
  queryClient
21
21
  });
22
- const invalidateDetailQueries = React.useCallback(
22
+ const invalidateMessageQueries = React.useCallback(
23
23
  (payload) => {
24
+ void queryClient.invalidateQueries({ queryKey: ["messages", "list"] });
24
25
  void queryClient.invalidateQueries({ queryKey: ["messages", "detail", id] });
25
26
  const messageId = typeof payload.messageId === "string" ? payload.messageId : null;
26
27
  if (messageId && messageId !== id) {
@@ -32,9 +33,9 @@ function useMessageDetails(id) {
32
33
  useAppEvent(
33
34
  "messages.message.*",
34
35
  (evt) => {
35
- invalidateDetailQueries(evt.payload ?? {});
36
+ invalidateMessageQueries(evt.payload ?? {});
36
37
  },
37
- [invalidateDetailQueries]
38
+ [invalidateMessageQueries]
38
39
  );
39
40
  useAppEvent(
40
41
  "om:bridge:reconnected",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../../../src/modules/messages/components/message-detail/hooks/useMessageDetails.ts"],
4
- "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { useMessageDetailsQueries } from './useMessageDetailsQueries'\nimport { useMessageDetailsActions } from './useMessageDetailsActions'\nimport { useMessageDetailsConversation } from './useMessageDetailsConversation'\n\nexport function useMessageDetails(id: string) {\n const t = useT()\n const router = useRouter()\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n\n const queryState = useMessageDetailsQueries({\n id,\n t,\n scopeVersion,\n queryClient,\n })\n\n const invalidateDetailQueries = React.useCallback(\n (payload: Record<string, unknown>) => {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', id] })\n const messageId = typeof payload.messageId === 'string' ? payload.messageId : null\n if (messageId && messageId !== id) {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', messageId] })\n }\n },\n [id, queryClient],\n )\n\n useAppEvent(\n 'messages.message.*',\n (evt) => {\n invalidateDetailQueries((evt.payload ?? {}) as Record<string, unknown>)\n },\n [invalidateDetailQueries],\n )\n\n useAppEvent(\n 'om:bridge:reconnected',\n () => {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', id] })\n },\n [id, queryClient],\n )\n\n const isArchived = (queryState.detail?.recipients ?? []).some((item) => item.status === 'archived')\n\n const actionState = useMessageDetailsActions({\n id,\n t,\n detail: queryState.detail,\n detailQuery: queryState.detailQuery,\n attachments: queryState.attachments,\n isArchived,\n onDeleted: () => router.push('/backend/messages'),\n refreshDetailWithoutAutoMarkRead: queryState.refreshDetailWithoutAutoMarkRead,\n })\n\n const conversationState = useMessageDetailsConversation({\n detail: queryState.detail,\n t,\n })\n\n const backToList = React.useCallback(() => {\n router.push('/backend/messages')\n }, [router])\n\n return {\n t,\n router,\n backToList,\n ...queryState,\n ...conversationState,\n ...actionState,\n isArchived,\n }\n}\n"],
5
- "mappings": ";AAEA,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAC/B,SAAS,YAAY;AACrB,SAAS,mCAAmC;AAC5C,SAAS,mBAAmB;AAC5B,SAAS,gCAAgC;AACzC,SAAS,gCAAgC;AACzC,SAAS,qCAAqC;AAEvC,SAAS,kBAAkB,IAAY;AAC5C,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,eAAe,4BAA4B;AACjD,QAAM,cAAc,eAAe;AAEnC,QAAM,aAAa,yBAAyB;AAAA,IAC1C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,0BAA0B,MAAM;AAAA,IACpC,CAAC,YAAqC;AACpC,WAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,EAAE,EAAE,CAAC;AAC3E,YAAM,YAAY,OAAO,QAAQ,cAAc,WAAW,QAAQ,YAAY;AAC9E,UAAI,aAAa,cAAc,IAAI;AACjC,aAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,SAAS,EAAE,CAAC;AAAA,MACpF;AAAA,IACF;AAAA,IACA,CAAC,IAAI,WAAW;AAAA,EAClB;AAEA;AAAA,IACE;AAAA,IACA,CAAC,QAAQ;AACP,8BAAyB,IAAI,WAAW,CAAC,CAA6B;AAAA,IACxE;AAAA,IACA,CAAC,uBAAuB;AAAA,EAC1B;AAEA;AAAA,IACE;AAAA,IACA,MAAM;AACJ,WAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,EAAE,EAAE,CAAC;AAAA,IAC7E;AAAA,IACA,CAAC,IAAI,WAAW;AAAA,EAClB;AAEA,QAAM,cAAc,WAAW,QAAQ,cAAc,CAAC,GAAG,KAAK,CAAC,SAAS,KAAK,WAAW,UAAU;AAElG,QAAM,cAAc,yBAAyB;AAAA,IAC3C;AAAA,IACA;AAAA,IACA,QAAQ,WAAW;AAAA,IACnB,aAAa,WAAW;AAAA,IACxB,aAAa,WAAW;AAAA,IACxB;AAAA,IACA,WAAW,MAAM,OAAO,KAAK,mBAAmB;AAAA,IAChD,kCAAkC,WAAW;AAAA,EAC/C,CAAC;AAED,QAAM,oBAAoB,8BAA8B;AAAA,IACtD,QAAQ,WAAW;AAAA,IACnB;AAAA,EACF,CAAC;AAED,QAAM,aAAa,MAAM,YAAY,MAAM;AACzC,WAAO,KAAK,mBAAmB;AAAA,EACjC,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,IACH;AAAA,EACF;AACF;",
4
+ "sourcesContent": ["\"use client\"\n\nimport * as React from 'react'\nimport { useRouter } from 'next/navigation'\nimport { useQueryClient } from '@tanstack/react-query'\nimport { useT } from '@open-mercato/shared/lib/i18n/context'\nimport { useOrganizationScopeVersion } from '@open-mercato/shared/lib/frontend/useOrganizationScope'\nimport { useAppEvent } from '@open-mercato/ui/backend/injection/useAppEvent'\nimport { useMessageDetailsQueries } from './useMessageDetailsQueries'\nimport { useMessageDetailsActions } from './useMessageDetailsActions'\nimport { useMessageDetailsConversation } from './useMessageDetailsConversation'\n\nexport function useMessageDetails(id: string) {\n const t = useT()\n const router = useRouter()\n const scopeVersion = useOrganizationScopeVersion()\n const queryClient = useQueryClient()\n\n const queryState = useMessageDetailsQueries({\n id,\n t,\n scopeVersion,\n queryClient,\n })\n\n const invalidateMessageQueries = React.useCallback(\n (payload: Record<string, unknown>) => {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'list'] })\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', id] })\n const messageId = typeof payload.messageId === 'string' ? payload.messageId : null\n if (messageId && messageId !== id) {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', messageId] })\n }\n },\n [id, queryClient],\n )\n\n useAppEvent(\n 'messages.message.*',\n (evt) => {\n invalidateMessageQueries((evt.payload ?? {}) as Record<string, unknown>)\n },\n [invalidateMessageQueries],\n )\n\n useAppEvent(\n 'om:bridge:reconnected',\n () => {\n void queryClient.invalidateQueries({ queryKey: ['messages', 'detail', id] })\n },\n [id, queryClient],\n )\n\n const isArchived = (queryState.detail?.recipients ?? []).some((item) => item.status === 'archived')\n\n const actionState = useMessageDetailsActions({\n id,\n t,\n detail: queryState.detail,\n detailQuery: queryState.detailQuery,\n attachments: queryState.attachments,\n isArchived,\n onDeleted: () => router.push('/backend/messages'),\n refreshDetailWithoutAutoMarkRead: queryState.refreshDetailWithoutAutoMarkRead,\n })\n\n const conversationState = useMessageDetailsConversation({\n detail: queryState.detail,\n t,\n })\n\n const backToList = React.useCallback(() => {\n router.push('/backend/messages')\n }, [router])\n\n return {\n t,\n router,\n backToList,\n ...queryState,\n ...conversationState,\n ...actionState,\n isArchived,\n }\n}\n"],
5
+ "mappings": ";AAEA,YAAY,WAAW;AACvB,SAAS,iBAAiB;AAC1B,SAAS,sBAAsB;AAC/B,SAAS,YAAY;AACrB,SAAS,mCAAmC;AAC5C,SAAS,mBAAmB;AAC5B,SAAS,gCAAgC;AACzC,SAAS,gCAAgC;AACzC,SAAS,qCAAqC;AAEvC,SAAS,kBAAkB,IAAY;AAC5C,QAAM,IAAI,KAAK;AACf,QAAM,SAAS,UAAU;AACzB,QAAM,eAAe,4BAA4B;AACjD,QAAM,cAAc,eAAe;AAEnC,QAAM,aAAa,yBAAyB;AAAA,IAC1C;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,CAAC;AAED,QAAM,2BAA2B,MAAM;AAAA,IACrC,CAAC,YAAqC;AACpC,WAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,MAAM,EAAE,CAAC;AACrE,WAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,EAAE,EAAE,CAAC;AAC3E,YAAM,YAAY,OAAO,QAAQ,cAAc,WAAW,QAAQ,YAAY;AAC9E,UAAI,aAAa,cAAc,IAAI;AACjC,aAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,SAAS,EAAE,CAAC;AAAA,MACpF;AAAA,IACF;AAAA,IACA,CAAC,IAAI,WAAW;AAAA,EAClB;AAEA;AAAA,IACE;AAAA,IACA,CAAC,QAAQ;AACP,+BAA0B,IAAI,WAAW,CAAC,CAA6B;AAAA,IACzE;AAAA,IACA,CAAC,wBAAwB;AAAA,EAC3B;AAEA;AAAA,IACE;AAAA,IACA,MAAM;AACJ,WAAK,YAAY,kBAAkB,EAAE,UAAU,CAAC,YAAY,UAAU,EAAE,EAAE,CAAC;AAAA,IAC7E;AAAA,IACA,CAAC,IAAI,WAAW;AAAA,EAClB;AAEA,QAAM,cAAc,WAAW,QAAQ,cAAc,CAAC,GAAG,KAAK,CAAC,SAAS,KAAK,WAAW,UAAU;AAElG,QAAM,cAAc,yBAAyB;AAAA,IAC3C;AAAA,IACA;AAAA,IACA,QAAQ,WAAW;AAAA,IACnB,aAAa,WAAW;AAAA,IACxB,aAAa,WAAW;AAAA,IACxB;AAAA,IACA,WAAW,MAAM,OAAO,KAAK,mBAAmB;AAAA,IAChD,kCAAkC,WAAW;AAAA,EAC/C,CAAC;AAED,QAAM,oBAAoB,8BAA8B;AAAA,IACtD,QAAQ,WAAW;AAAA,IACnB;AAAA,EACF,CAAC;AAED,QAAM,aAAa,MAAM,YAAY,MAAM;AACzC,WAAO,KAAK,mBAAmB;AAAA,EACjC,GAAG,CAAC,MAAM,CAAC;AAEX,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,IACH,GAAG;AAAA,IACH,GAAG;AAAA,IACH;AAAA,EACF;AACF;",
6
6
  "names": []
7
7
  }
@@ -0,0 +1,17 @@
1
+ const metadata = {
2
+ requireCustomerAuth: true,
3
+ titleKey: "portal.dashboard.title",
4
+ title: "Dashboard",
5
+ nav: {
6
+ label: "Dashboard",
7
+ labelKey: "portal.nav.dashboard",
8
+ group: "main",
9
+ order: 10
10
+ }
11
+ };
12
+ var page_meta_default = metadata;
13
+ export {
14
+ page_meta_default as default,
15
+ metadata
16
+ };
17
+ //# sourceMappingURL=page.meta.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/portal/frontend/%5BorgSlug%5D/portal/dashboard/page.meta.ts"],
4
+ "sourcesContent": ["import type { PageMetadata } from '@open-mercato/shared/modules/registry'\n\nexport const metadata: PageMetadata = {\n requireCustomerAuth: true,\n titleKey: 'portal.dashboard.title',\n title: 'Dashboard',\n nav: {\n label: 'Dashboard',\n labelKey: 'portal.nav.dashboard',\n group: 'main',\n order: 10,\n },\n}\n\nexport default metadata\n"],
5
+ "mappings": "AAEO,MAAM,WAAyB;AAAA,EACpC,qBAAqB;AAAA,EACrB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,KAAK;AAAA,IACH,OAAO;AAAA,IACP,UAAU;AAAA,IACV,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AACF;AAEA,IAAO,oBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,11 @@
1
+ const metadata = {
2
+ titleKey: "portal.nav.login",
3
+ title: "Log In",
4
+ navHidden: true
5
+ };
6
+ var page_meta_default = metadata;
7
+ export {
8
+ page_meta_default as default,
9
+ metadata
10
+ };
11
+ //# sourceMappingURL=page.meta.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/portal/frontend/%5BorgSlug%5D/portal/login/page.meta.ts"],
4
+ "sourcesContent": ["import type { PageMetadata } from '@open-mercato/shared/modules/registry'\n\nexport const metadata: PageMetadata = {\n titleKey: 'portal.nav.login',\n title: 'Log In',\n navHidden: true,\n}\n\nexport default metadata\n"],
5
+ "mappings": "AAEO,MAAM,WAAyB;AAAA,EACpC,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AACb;AAEA,IAAO,oBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,11 @@
1
+ const metadata = {
2
+ titleKey: "portal.title",
3
+ title: "Customer Portal",
4
+ navHidden: true
5
+ };
6
+ var page_meta_default = metadata;
7
+ export {
8
+ page_meta_default as default,
9
+ metadata
10
+ };
11
+ //# sourceMappingURL=page.meta.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../src/modules/portal/frontend/%5BorgSlug%5D/portal/page.meta.ts"],
4
+ "sourcesContent": ["import type { PageMetadata } from '@open-mercato/shared/modules/registry'\n\nexport const metadata: PageMetadata = {\n titleKey: 'portal.title',\n title: 'Customer Portal',\n navHidden: true,\n}\n\nexport default metadata\n"],
5
+ "mappings": "AAEO,MAAM,WAAyB;AAAA,EACpC,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AACb;AAEA,IAAO,oBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,17 @@
1
+ const metadata = {
2
+ requireCustomerAuth: true,
3
+ titleKey: "portal.nav.profile",
4
+ title: "Profile",
5
+ nav: {
6
+ label: "Profile",
7
+ labelKey: "portal.nav.profile",
8
+ group: "account",
9
+ order: 10
10
+ }
11
+ };
12
+ var page_meta_default = metadata;
13
+ export {
14
+ page_meta_default as default,
15
+ metadata
16
+ };
17
+ //# sourceMappingURL=page.meta.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/portal/frontend/%5BorgSlug%5D/portal/profile/page.meta.ts"],
4
+ "sourcesContent": ["import type { PageMetadata } from '@open-mercato/shared/modules/registry'\n\nexport const metadata: PageMetadata = {\n requireCustomerAuth: true,\n titleKey: 'portal.nav.profile',\n title: 'Profile',\n nav: {\n label: 'Profile',\n labelKey: 'portal.nav.profile',\n group: 'account',\n order: 10,\n },\n}\n\nexport default metadata\n"],
5
+ "mappings": "AAEO,MAAM,WAAyB;AAAA,EACpC,qBAAqB;AAAA,EACrB,UAAU;AAAA,EACV,OAAO;AAAA,EACP,KAAK;AAAA,IACH,OAAO;AAAA,IACP,UAAU;AAAA,IACV,OAAO;AAAA,IACP,OAAO;AAAA,EACT;AACF;AAEA,IAAO,oBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,11 @@
1
+ const metadata = {
2
+ titleKey: "portal.nav.signup",
3
+ title: "Sign Up",
4
+ navHidden: true
5
+ };
6
+ var page_meta_default = metadata;
7
+ export {
8
+ page_meta_default as default,
9
+ metadata
10
+ };
11
+ //# sourceMappingURL=page.meta.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/portal/frontend/%5BorgSlug%5D/portal/signup/page.meta.ts"],
4
+ "sourcesContent": ["import type { PageMetadata } from '@open-mercato/shared/modules/registry'\n\nexport const metadata: PageMetadata = {\n titleKey: 'portal.nav.signup',\n title: 'Sign Up',\n navHidden: true,\n}\n\nexport default metadata\n"],
5
+ "mappings": "AAEO,MAAM,WAAyB;AAAA,EACpC,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AACb;AAEA,IAAO,oBAAQ;",
6
+ "names": []
7
+ }
@@ -0,0 +1,11 @@
1
+ const metadata = {
2
+ titleKey: "portal.title",
3
+ title: "Verify Email",
4
+ navHidden: true
5
+ };
6
+ var page_meta_default = metadata;
7
+ export {
8
+ page_meta_default as default,
9
+ metadata
10
+ };
11
+ //# sourceMappingURL=page.meta.js.map
@@ -0,0 +1,7 @@
1
+ {
2
+ "version": 3,
3
+ "sources": ["../../../../../../../src/modules/portal/frontend/%5BorgSlug%5D/portal/verify/page.meta.ts"],
4
+ "sourcesContent": ["import type { PageMetadata } from '@open-mercato/shared/modules/registry'\n\nexport const metadata: PageMetadata = {\n titleKey: 'portal.title',\n title: 'Verify Email',\n navHidden: true,\n}\n\nexport default metadata\n"],
5
+ "mappings": "AAEO,MAAM,WAAyB;AAAA,EACpC,UAAU;AAAA,EACV,OAAO;AAAA,EACP,WAAW;AACb;AAEA,IAAO,oBAAQ;",
6
+ "names": []
7
+ }
@@ -9,7 +9,16 @@ import { WORKFLOW_ACTIVITIES_QUEUE_NAME } from "./activity-queue-types.js";
9
9
  import { logWorkflowEvent } from "./event-logger.js";
10
10
  import { isPrivateUrl } from "@open-mercato/shared/lib/network";
11
11
  function isAllowPrivateWorkflowWebhookUrlsEnabled() {
12
- return parseBooleanWithDefault(process.env.OM_WORKFLOWS_ALLOW_PRIVATE_URLS, false);
12
+ if (parseBooleanWithDefault(process.env.OM_WORKFLOWS_ALLOW_PRIVATE_URLS, false)) {
13
+ return true;
14
+ }
15
+ if (parseBooleanWithDefault(process.env.WORKFLOW_WEBHOOK_ALLOW_PRIVATE_URLS, false)) {
16
+ console.warn(
17
+ "[CALL_WEBHOOK] WORKFLOW_WEBHOOK_ALLOW_PRIVATE_URLS is deprecated. Use OM_WORKFLOWS_ALLOW_PRIVATE_URLS instead. SSRF protection is bypassed."
18
+ );
19
+ return true;
20
+ }
21
+ return false;
13
22
  }
14
23
  class ActivityExecutionError extends Error {
15
24
  constructor(message, activityType, activityName, details) {
@@ -469,28 +478,19 @@ async function executeCallApi(em, config, context, container, signal) {
469
478
  }
470
479
  );
471
480
  }
472
- async function resolveCallApiRoleIds(em, instance) {
473
- if (!instance.definitionId) return [];
481
+ async function resolveActiveRoleIdsForUser(em, userId, scope) {
474
482
  const { findOneWithDecryption, findWithDecryption } = await import("@open-mercato/shared/lib/encryption/find");
475
483
  const { User, UserRole, Role } = await import("../../auth/data/entities.js");
476
- const { WorkflowDefinition } = await import("../data/entities.js");
477
- const scope = { tenantId: instance.tenantId, organizationId: instance.organizationId };
478
- const definition = await findOneWithDecryption(em, WorkflowDefinition, {
479
- id: instance.definitionId,
480
- tenantId: instance.tenantId
481
- }, {}, scope);
482
- const authorUserId = definition?.createdBy;
483
- if (!authorUserId) return [];
484
- const author = await findOneWithDecryption(em, User, {
485
- id: authorUserId,
486
- tenantId: instance.tenantId,
484
+ const user = await findOneWithDecryption(em, User, {
485
+ id: userId,
486
+ tenantId: scope.tenantId,
487
487
  deletedAt: null
488
488
  }, {}, scope);
489
- if (!author) return [];
489
+ if (!user) return [];
490
490
  const userRoles = await findWithDecryption(
491
491
  em,
492
492
  UserRole,
493
- { user: author.id, deletedAt: null },
493
+ { user: user.id, deletedAt: null },
494
494
  { populate: ["role"] },
495
495
  scope
496
496
  );
@@ -498,11 +498,29 @@ async function resolveCallApiRoleIds(em, instance) {
498
498
  if (roleIds.length === 0) return [];
499
499
  const scopedRoles = await findWithDecryption(em, Role, {
500
500
  id: { $in: roleIds },
501
- tenantId: instance.tenantId,
501
+ tenantId: scope.tenantId,
502
502
  deletedAt: null
503
503
  }, {}, scope);
504
504
  return scopedRoles.map((r) => r.id);
505
505
  }
506
+ async function resolveCallApiRoleIds(em, instance) {
507
+ if (!instance.definitionId) return [];
508
+ const { findOneWithDecryption } = await import("@open-mercato/shared/lib/encryption/find");
509
+ const { WorkflowDefinition } = await import("../data/entities.js");
510
+ const scope = { tenantId: instance.tenantId, organizationId: instance.organizationId };
511
+ const initiatorUserId = instance.metadata?.initiatedBy ?? null;
512
+ if (initiatorUserId) {
513
+ return resolveActiveRoleIdsForUser(em, initiatorUserId, scope);
514
+ }
515
+ const definition = await findOneWithDecryption(em, WorkflowDefinition, {
516
+ id: instance.definitionId,
517
+ tenantId: instance.tenantId,
518
+ deletedAt: null
519
+ }, {}, scope);
520
+ const authorUserId = definition?.createdBy;
521
+ if (!authorUserId) return [];
522
+ return resolveActiveRoleIdsForUser(em, authorUserId, scope);
523
+ }
506
524
  function buildApiUrl(endpoint) {
507
525
  const appUrl = process.env.APP_URL || "http://localhost:3000";
508
526
  if (endpoint.startsWith("/")) {