@acorex/connectivity 21.0.0-next.5 → 21.0.0-next.7

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.
@@ -14,11 +14,10 @@ import { AXMSubscriptionManagementSubscriptionEntityService, AXMSubscriptionMana
14
14
  import { AXPSystemStatusType, AXPFileStorageStatus, AXPFileStorageService, AXVChangeType as AXVChangeType$1, AXP_SEARCH_PROVIDER, AXPLockService, AXPVersioningService, AXPSystemStatuses, AXPAppVersionService } from '@acorex/platform/common';
15
15
  import { AXMTenantService, AXMTenantManagementTenantUserEntityService, AXMTenantManagementPermissionsKeys, RootConfig as RootConfig$q } from '@acorex/modules/tenant-management';
16
16
  import { RootConfig as RootConfig$1 } from '@acorex/modules/calendar-management';
17
+ import { provideCommandSetups, AXPCommandService, provideQuerySetups, AXPQueryService, AXPRuntimeModule } from '@acorex/platform/runtime';
17
18
  import Dexie from 'dexie';
18
19
  import { AXFileService } from '@acorex/core/file';
19
- import { AXPWorkflowExecutionService } from '@acorex/platform/workflow';
20
- import { AXPCommandService, provideCommandSetups, provideQuerySetups, AXPQueryService, AXPRuntimeModule } from '@acorex/platform/runtime';
21
- import { AXMPermissionsKeys as AXMPermissionsKeys$3, AXMDistributionServiceImpl, AXMDocumentService, AXMDistributionInteractionService, RootConfig as RootConfig$6, AXMFolderServiceImpl, AXMDocumentServiceImpl, AXMReviewService, AXMReviewServiceImpl, AXMDistributionInteractionServiceImpl, AXMFolderService, AXMDistributionService } from '@acorex/modules/document-management';
20
+ import { AXMPermissionsKeys as AXMPermissionsKeys$3, AXMDistributionInteractionServiceImpl, AXMDistributionServiceImpl, AXMDocumentService, AXMDistributionInteractionService, AXMFolderServiceImpl, AXMDocumentServiceImpl, AXMReviewService, AXMDocumentTypeServiceImpl, RootConfig as RootConfig$6, AXMReviewServiceImpl, AXMFolderService, AXMDistributionService, AXPDocumentManagementService } from '@acorex/modules/document-management';
22
21
  import { AXMPermissionsKeys as AXMPermissionsKeys$2, RootConfig as RootConfig$5, AXMMetaDataDefinitionService, META_DATA_SELECTOR_FIELD, AXMQueryServiceImpl, AXMQueryService, AXMDataManagementFeatureKeys } from '@acorex/modules/data-management';
23
22
  import { AXMPermissionsKeys as AXMPermissionsKeys$4, RootConfig as RootConfig$a, AXPHumanCapitalManagementFeatureKeys } from '@acorex/modules/human-capital-management';
24
23
  import { AXMSettingsManagementPermissionKeys } from '@acorex/modules/settings-management';
@@ -60,6 +59,7 @@ import { RootConfig as RootConfig$v, AXM_ORDER_NUMBER_IDENTIFIER_RULE } from '@a
60
59
  import { RootConfig as RootConfig$x } from '@acorex/modules/help-desk';
61
60
  import { AXCExternalAuthorizationService } from '@acorex/connectivity/utils';
62
61
  import { RootConfig as RootConfig$y } from '@acorex/modules/workflow-management';
62
+ import { AXP_WORKFLOW_CATEGORY_PROVIDER, AXP_WORKFLOW_PROVIDER } from '@acorex/platform/workflow';
63
63
  import { AXCalendarService } from '@acorex/core/date-time';
64
64
 
65
65
  //#region ---- Constants ----
@@ -611,6 +611,9 @@ class MockFeatureChecker {
611
611
  check(keys, context, baseResult) {
612
612
  const user = context.user;
613
613
  //console.log('[Feature Checker]', keys, user?.name, baseResult);
614
+ // if (keys.includes("SecurityManagement")) {
615
+ // return false;
616
+ // }
614
617
  // Root user always has all features enabled
615
618
  if (user?.name === 'super-root') {
616
619
  return true;
@@ -1609,6 +1612,219 @@ class EntitySearchProvider {
1609
1612
  }
1610
1613
  }
1611
1614
 
1615
+ //#endregion
1616
+ //#region ---- Update Status Command ----
1617
+ /**
1618
+ * Generic command to update entity status
1619
+ * Supports both single entity (id) and bulk updates (ids)
1620
+ *
1621
+ * Options:
1622
+ * - entity: string - Entity key (e.g., 'OrderManagement.GroupOrder')
1623
+ * - id?: string - Single entity ID
1624
+ * - ids?: string[] - Multiple entity IDs (for bulk updates)
1625
+ * - status: string - Target status value
1626
+ * - statusField?: string - Status field name (default: 'statusId')
1627
+ */
1628
+ class AXCEntityUpdateStatusCommand {
1629
+ constructor() {
1630
+ this.entityService = inject(AXPEntityService);
1631
+ }
1632
+ async execute(input) {
1633
+ try {
1634
+ const { entity, id, ids, status, statusField = 'statusId' } = input;
1635
+ // Validate input
1636
+ if (!entity) {
1637
+ throw new Error('Entity name is required');
1638
+ }
1639
+ if (!id && (!ids || ids.length === 0)) {
1640
+ throw new Error('Either id or ids must be provided');
1641
+ }
1642
+ if (!status) {
1643
+ throw new Error('Status value is required');
1644
+ }
1645
+ const dataAccessor = this.entityService.withEntity(entity).data();
1646
+ const updateData = { [statusField]: status };
1647
+ // Handle bulk update
1648
+ if (ids && ids.length > 0) {
1649
+ const results = [];
1650
+ for (const entityId of ids) {
1651
+ const updated = await dataAccessor.update(entityId, updateData);
1652
+ results.push(updated);
1653
+ }
1654
+ return {
1655
+ success: true,
1656
+ data: results,
1657
+ };
1658
+ }
1659
+ // Handle single update
1660
+ if (id) {
1661
+ const updated = await dataAccessor.update(id, updateData);
1662
+ return {
1663
+ success: true,
1664
+ data: updated,
1665
+ message: {
1666
+ text: 'Entity status updated successfully',
1667
+ },
1668
+ };
1669
+ }
1670
+ throw new Error('Invalid input: neither id nor ids provided');
1671
+ }
1672
+ catch (error) {
1673
+ return {
1674
+ success: false,
1675
+ message: {
1676
+ text: error instanceof Error ? error.message : 'Failed to update entity status',
1677
+ },
1678
+ };
1679
+ }
1680
+ }
1681
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCEntityUpdateStatusCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1682
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCEntityUpdateStatusCommand, providedIn: 'root' }); }
1683
+ }
1684
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCEntityUpdateStatusCommand, decorators: [{
1685
+ type: Injectable,
1686
+ args: [{ providedIn: 'root' }]
1687
+ }] });
1688
+
1689
+ class FileStorageDatabase extends Dexie {
1690
+ constructor() {
1691
+ super('ACoreXFileStorage');
1692
+ this.version(1).stores({
1693
+ files: 'fileId,refId,refType,category,isPrimary,status',
1694
+ });
1695
+ }
1696
+ }
1697
+ const db = new FileStorageDatabase();
1698
+ class AXCFileStorageService {
1699
+ async mapToFileStorageInfo(record) {
1700
+ if (!record) {
1701
+ throw new Error('Record not found');
1702
+ }
1703
+ const { binary, ...fileInfo } = record;
1704
+ const url = await this.fileService.blobToBase64(binary);
1705
+ return { ...record, url };
1706
+ }
1707
+ async save(request) {
1708
+ const fileId = AXPDataGenerator.uuid();
1709
+ const fileInfo = {
1710
+ fileId,
1711
+ refId: request.refId,
1712
+ refType: request.refType,
1713
+ category: request.category,
1714
+ size: request.file.size,
1715
+ mimeType: request.file.type,
1716
+ uploadedAt: new Date(),
1717
+ isPublic: request.metadata?.['isPublic'] || true,
1718
+ isPrimary: request.isPrimary || false,
1719
+ status: request.status ?? AXPFileStorageStatus.Temporary, // Use enum
1720
+ name: request.name,
1721
+ binary: request.file,
1722
+ };
1723
+ // Save the binary content along with metadata in Dexie
1724
+ await db.files.add({ ...fileInfo, binary: request.file });
1725
+ return fileInfo;
1726
+ }
1727
+ async commit(fileId) {
1728
+ const file = await db.files.get(fileId);
1729
+ if (!file) {
1730
+ throw new Error('File not found');
1731
+ }
1732
+ file.status = AXPFileStorageStatus.Committed; // Use enum
1733
+ await db.files.put(file);
1734
+ }
1735
+ async markForDeletion(fileId) {
1736
+ const file = await db.files.get(fileId);
1737
+ if (!file) {
1738
+ throw new Error('File not found');
1739
+ }
1740
+ file.status = AXPFileStorageStatus.PendingDeletion; // Use enum
1741
+ await db.files.put(file);
1742
+ }
1743
+ async update(request) {
1744
+ const file = await db.files.get(request.fileId);
1745
+ if (!file) {
1746
+ throw new Error('File not found');
1747
+ }
1748
+ const updatedFileInfo = {
1749
+ ...file,
1750
+ ...request.metadata,
1751
+ name: request.name !== undefined ? request.name : file.name,
1752
+ isPrimary: request.isPrimary !== undefined ? request.isPrimary : file.isPrimary,
1753
+ };
1754
+ await db.files.put(updatedFileInfo);
1755
+ return this.mapToFileStorageInfo(updatedFileInfo);
1756
+ }
1757
+ async find(request) {
1758
+ const files = await db.files.toArray();
1759
+ const filteredFiles = files.filter((file) => {
1760
+ return ((!request.refId || file.refId === request.refId) &&
1761
+ (!request.refType || file.refType === request.refType) &&
1762
+ (!request.category || file.category === request.category) &&
1763
+ (!request.isPrimary || file.isPrimary === request.isPrimary) &&
1764
+ (!request.isPublic || file.isPublic === request.isPublic) &&
1765
+ (!request.mimeType || file.mimeType === request.mimeType) &&
1766
+ (!request.uploadedAtRange ||
1767
+ (file.uploadedAt >= request.uploadedAtRange.from && file.uploadedAt <= request.uploadedAtRange.to)) &&
1768
+ file.status === AXPFileStorageStatus.Committed // Use enum
1769
+ );
1770
+ });
1771
+ // Map all filtered files to `AXPFileStorageInfo`
1772
+ return Promise.all(filteredFiles.map((file) => this.mapToFileStorageInfo(file)));
1773
+ }
1774
+ async getInfo(fileId) {
1775
+ // Simulate a delay of 2 seconds (2000 milliseconds)
1776
+ await new Promise((resolve) => setTimeout(resolve, 2000));
1777
+ const file = await db.files.get(fileId);
1778
+ return this.mapToFileStorageInfo(file);
1779
+ }
1780
+ async remove(fileId) {
1781
+ await db.files.delete(fileId);
1782
+ }
1783
+ async cleanupTemporaryFiles() {
1784
+ const files = await db.files.toArray();
1785
+ const temporaryFiles = files.filter((file) => file.status === AXPFileStorageStatus.Temporary);
1786
+ for (const file of temporaryFiles) {
1787
+ await db.files.delete(file.fileId);
1788
+ }
1789
+ }
1790
+ async cleanupMarkedFiles(gracePeriod) {
1791
+ const now = new Date();
1792
+ const files = await db.files.toArray();
1793
+ for (const file of files) {
1794
+ if (file.status === AXPFileStorageStatus.PendingDeletion &&
1795
+ now.getTime() - file.uploadedAt.getTime() > gracePeriod) {
1796
+ await db.files.delete(file.fileId);
1797
+ }
1798
+ }
1799
+ }
1800
+ // Semi-background cleanup job
1801
+ constructor() {
1802
+ this.fileService = inject(AXFileService);
1803
+ setInterval(() => {
1804
+ this.cleanupMarkedFiles(5 * 60 * 1000) // Grace period: 5 minutes in milliseconds
1805
+ .catch((error) => console.error('Error during cleanup:', error));
1806
+ this.cleanupTemporaryFiles() // Ensure temporary files are cleaned
1807
+ .catch((error) => console.error('Error during cleanup:', error));
1808
+ }, 5 * 60 * 1000); // Runs every 5 minutes
1809
+ }
1810
+ async findMany(ids) {
1811
+ const files = await db.files.where('fileId').anyOf(ids).toArray();
1812
+ return files.map((file) => ({
1813
+ id: file.fileId,
1814
+ fileId: file.fileId,
1815
+ refId: file.refId,
1816
+ refType: file.refType,
1817
+ size: file.size,
1818
+ name: file.name,
1819
+ }));
1820
+ }
1821
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFileStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1822
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFileStorageService }); }
1823
+ }
1824
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFileStorageService, decorators: [{
1825
+ type: Injectable
1826
+ }], ctorParameters: () => [] });
1827
+
1612
1828
  class AXCLockService {
1613
1829
  constructor() {
1614
1830
  // Local Dexie database for storing lock information independent of AXPEntityStorageService
@@ -1760,145 +1976,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
1760
1976
  type: Injectable
1761
1977
  }], ctorParameters: () => [] });
1762
1978
 
1763
- class FileStorageDatabase extends Dexie {
1764
- constructor() {
1765
- super('ACoreXFileStorage');
1766
- this.version(1).stores({
1767
- files: 'fileId,refId,refType,category,isPrimary,status',
1768
- });
1769
- }
1770
- }
1771
- const db = new FileStorageDatabase();
1772
- class AXCFileStorageService {
1773
- async mapToFileStorageInfo(record) {
1774
- if (!record) {
1775
- throw new Error('Record not found');
1776
- }
1777
- const { binary, ...fileInfo } = record;
1778
- const url = await this.fileService.blobToBase64(binary);
1779
- return { ...record, url };
1780
- }
1781
- async save(request) {
1782
- const fileId = AXPDataGenerator.uuid();
1783
- const fileInfo = {
1784
- fileId,
1785
- refId: request.refId,
1786
- refType: request.refType,
1787
- category: request.category,
1788
- size: request.file.size,
1789
- mimeType: request.file.type,
1790
- uploadedAt: new Date(),
1791
- isPublic: request.metadata?.['isPublic'] || true,
1792
- isPrimary: request.isPrimary || false,
1793
- status: request.status ?? AXPFileStorageStatus.Temporary, // Use enum
1794
- name: request.name,
1795
- binary: request.file,
1796
- };
1797
- // Save the binary content along with metadata in Dexie
1798
- await db.files.add({ ...fileInfo, binary: request.file });
1799
- return fileInfo;
1800
- }
1801
- async commit(fileId) {
1802
- const file = await db.files.get(fileId);
1803
- if (!file) {
1804
- throw new Error('File not found');
1805
- }
1806
- file.status = AXPFileStorageStatus.Committed; // Use enum
1807
- await db.files.put(file);
1808
- }
1809
- async markForDeletion(fileId) {
1810
- const file = await db.files.get(fileId);
1811
- if (!file) {
1812
- throw new Error('File not found');
1813
- }
1814
- file.status = AXPFileStorageStatus.PendingDeletion; // Use enum
1815
- await db.files.put(file);
1816
- }
1817
- async update(request) {
1818
- const file = await db.files.get(request.fileId);
1819
- if (!file) {
1820
- throw new Error('File not found');
1821
- }
1822
- const updatedFileInfo = {
1823
- ...file,
1824
- ...request.metadata,
1825
- name: request.name !== undefined ? request.name : file.name,
1826
- isPrimary: request.isPrimary !== undefined ? request.isPrimary : file.isPrimary,
1827
- };
1828
- await db.files.put(updatedFileInfo);
1829
- return this.mapToFileStorageInfo(updatedFileInfo);
1830
- }
1831
- async find(request) {
1832
- const files = await db.files.toArray();
1833
- const filteredFiles = files.filter((file) => {
1834
- return ((!request.refId || file.refId === request.refId) &&
1835
- (!request.refType || file.refType === request.refType) &&
1836
- (!request.category || file.category === request.category) &&
1837
- (!request.isPrimary || file.isPrimary === request.isPrimary) &&
1838
- (!request.isPublic || file.isPublic === request.isPublic) &&
1839
- (!request.mimeType || file.mimeType === request.mimeType) &&
1840
- (!request.uploadedAtRange ||
1841
- (file.uploadedAt >= request.uploadedAtRange.from && file.uploadedAt <= request.uploadedAtRange.to)) &&
1842
- file.status === AXPFileStorageStatus.Committed // Use enum
1843
- );
1844
- });
1845
- // Map all filtered files to `AXPFileStorageInfo`
1846
- return Promise.all(filteredFiles.map((file) => this.mapToFileStorageInfo(file)));
1847
- }
1848
- async getInfo(fileId) {
1849
- // Simulate a delay of 2 seconds (2000 milliseconds)
1850
- await new Promise((resolve) => setTimeout(resolve, 2000));
1851
- const file = await db.files.get(fileId);
1852
- return this.mapToFileStorageInfo(file);
1853
- }
1854
- async remove(fileId) {
1855
- await db.files.delete(fileId);
1856
- }
1857
- async cleanupTemporaryFiles() {
1858
- const files = await db.files.toArray();
1859
- const temporaryFiles = files.filter((file) => file.status === AXPFileStorageStatus.Temporary);
1860
- for (const file of temporaryFiles) {
1861
- await db.files.delete(file.fileId);
1862
- }
1863
- }
1864
- async cleanupMarkedFiles(gracePeriod) {
1865
- const now = new Date();
1866
- const files = await db.files.toArray();
1867
- for (const file of files) {
1868
- if (file.status === AXPFileStorageStatus.PendingDeletion &&
1869
- now.getTime() - file.uploadedAt.getTime() > gracePeriod) {
1870
- await db.files.delete(file.fileId);
1871
- }
1872
- }
1873
- }
1874
- // Semi-background cleanup job
1875
- constructor() {
1876
- this.fileService = inject(AXFileService);
1877
- setInterval(() => {
1878
- this.cleanupMarkedFiles(5 * 60 * 1000) // Grace period: 5 minutes in milliseconds
1879
- .catch((error) => console.error('Error during cleanup:', error));
1880
- this.cleanupTemporaryFiles() // Ensure temporary files are cleaned
1881
- .catch((error) => console.error('Error during cleanup:', error));
1882
- }, 5 * 60 * 1000); // Runs every 5 minutes
1883
- }
1884
- async findMany(ids) {
1885
- const files = await db.files.where('fileId').anyOf(ids).toArray();
1886
- return files.map((file) => ({
1887
- id: file.fileId,
1888
- fileId: file.fileId,
1889
- refId: file.refId,
1890
- refType: file.refType,
1891
- size: file.size,
1892
- name: file.name,
1893
- }));
1894
- }
1895
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFileStorageService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
1896
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFileStorageService }); }
1897
- }
1898
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFileStorageService, decorators: [{
1899
- type: Injectable
1900
- }], ctorParameters: () => [] });
1901
-
1902
1979
  //#region ---- Imports ----
1903
1980
  var AXVChangeType;
1904
1981
  (function (AXVChangeType) {
@@ -2322,1004 +2399,6 @@ function buildSummaryFromChanges(changes, changeType) {
2322
2399
  return result;
2323
2400
  }
2324
2401
 
2325
- //#region ---- Dexie DB ----
2326
- /**
2327
- * Dexie database for workflow instances.
2328
- */
2329
- class AXCWorkflowExecutionDB extends Dexie {
2330
- constructor() {
2331
- super('ACoreXWorkflowExecutions');
2332
- this.version(2).stores({
2333
- instances: 'id, definitionId, status, subStatus, createdAt, updatedAt',
2334
- });
2335
- // Migration from v1 to v2: clear old executions table
2336
- this.version(1).stores({
2337
- executions: 'executionId, workflowId, status, currentStepId, lastUpdated',
2338
- });
2339
- }
2340
- }
2341
- /**
2342
- * Singleton instance of workflow execution database.
2343
- */
2344
- const axWorkflowExecutionDB = new AXCWorkflowExecutionDB();
2345
- //#endregion
2346
-
2347
- /**
2348
- * Mock implementation of AXPWorkflowExecutionService.
2349
- *
2350
- * Uses IndexedDB (via Dexie) to store workflow execution states.
2351
- *
2352
- * @example
2353
- * ```typescript
2354
- * // In module providers
2355
- * {
2356
- * provide: AXPWorkflowExecutionService,
2357
- * useClass: AXCWorkflowExecutionService,
2358
- * }
2359
- * ```
2360
- */
2361
- class AXCWorkflowExecutionService {
2362
- constructor() {
2363
- this.db = axWorkflowExecutionDB;
2364
- this.commandService = inject(AXPCommandService);
2365
- this.entityStorageService = inject(AXPEntityStorageService);
2366
- }
2367
- /**
2368
- * Start a new workflow execution.
2369
- * Creates a workflow instance and stores it in IndexedDB.
2370
- */
2371
- async startExecution(request) {
2372
- const instanceId = AXPDataGenerator.uuid();
2373
- const now = new Date().toISOString();
2374
- // Get workflow definition to find first step
2375
- const workflowResponse = await this.getWorkflowDefinition({
2376
- workflowId: request.workflowId
2377
- });
2378
- if (!workflowResponse.workflow) {
2379
- throw new Error(`Workflow definition not found: ${request.workflowId}`);
2380
- }
2381
- const workflow = workflowResponse.workflow;
2382
- // Find first step from Flowchart (activity with no incoming connections)
2383
- const flowchart = workflow.root;
2384
- const activities = flowchart.activities || [];
2385
- const connections = flowchart.connections || [];
2386
- // Build connection map: target -> source[]
2387
- const incomingConnections = new Map();
2388
- connections.forEach((conn) => {
2389
- const targetId = conn.target.activity;
2390
- if (!incomingConnections.has(targetId)) {
2391
- incomingConnections.set(targetId, []);
2392
- }
2393
- incomingConnections.get(targetId).push(conn.source.activity);
2394
- });
2395
- // Find root activity (activity with no incoming connections, or first activity)
2396
- let firstActivity = null;
2397
- for (const activity of activities) {
2398
- if (!incomingConnections.has(activity.id)) {
2399
- firstActivity = activity;
2400
- break;
2401
- }
2402
- }
2403
- // If no root found, use first activity
2404
- if (!firstActivity && activities.length > 0) {
2405
- firstActivity = activities[0];
2406
- }
2407
- const firstStepId = firstActivity?.id || 'root';
2408
- // Create workflow state
2409
- const workflowState = {
2410
- id: `state-${instanceId}`,
2411
- definitionId: workflow.name, // Use name as definitionId (backward compatibility)
2412
- //definitionVersion: workflow.version,
2413
- definitionVersion: 1,
2414
- correlationId: null,
2415
- status: 'Running',
2416
- subStatus: 'Executing',
2417
- bookmarks: [],
2418
- incidents: [],
2419
- fault: null,
2420
- completionCallbacks: [],
2421
- activityExecutionContexts: firstActivity ? [{
2422
- id: `ctx-${firstStepId}`,
2423
- parentContextId: null,
2424
- scheduledActivityNodeId: firstStepId,
2425
- ownerActivityNodeId: null,
2426
- properties: {},
2427
- activityState: null,
2428
- dynamicVariables: [],
2429
- status: 'Running',
2430
- isExecuting: false,
2431
- faultCount: 0,
2432
- startedAt: now,
2433
- completedAt: null,
2434
- tag: null
2435
- }] : [],
2436
- input: request.input || {},
2437
- output: {},
2438
- properties: {},
2439
- createdAt: now,
2440
- updatedAt: now,
2441
- finishedAt: null
2442
- };
2443
- // Create workflow instance
2444
- const instance = {
2445
- $schema: 'https://elsaworkflows.io/schemas/workflow-instance/v3.0.0/schema.json',
2446
- id: instanceId,
2447
- definitionId: workflow.name, // Use name as definitionId (backward compatibility)
2448
- //definitionVersionId: workflow.id,
2449
- //version: workflow.version,
2450
- version: 1,
2451
- definitionVersionId: '1',
2452
- parentWorkflowInstanceId: null,
2453
- workflowState,
2454
- status: 'Running',
2455
- subStatus: 'Executing',
2456
- correlationId: null,
2457
- name: workflow.name || null,
2458
- incidentCount: 0,
2459
- createdAt: now,
2460
- updatedAt: now,
2461
- finishedAt: null
2462
- };
2463
- // Store instance in IndexedDB
2464
- await this.db.instances.add(instance, instance.id);
2465
- // 🎯 Also store in entity service for entity-based access
2466
- await this.saveInstanceToEntityService(instance);
2467
- // Convert to execution state for response
2468
- let state = this.instanceToExecutionState(instance);
2469
- // 🎯 KEY LOGIC: Execute all backend activities until we reach a frontend activity
2470
- let currentActivity = firstActivity;
2471
- let executionCount = 0;
2472
- const maxExecutions = 100; // Prevent infinite loops
2473
- while (currentActivity && executionCount < maxExecutions) {
2474
- executionCount++;
2475
- // Map activity type to ACoreX format
2476
- let activityType = currentActivity.type;
2477
- if (activityType.startsWith('Elsa.')) {
2478
- let type = activityType.replace(/^Elsa\./, '');
2479
- type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
2480
- activityType = `workflow-activity:${type}`;
2481
- }
2482
- // Determine execution mode
2483
- const executionMode = this.determineExecutionMode(activityType, currentActivity);
2484
- // If frontend activity → stop and return as pendingTask
2485
- if (executionMode === 'frontend') {
2486
- const pendingTask = this.createTaskFromActivity(currentActivity, state);
2487
- // Update state to suspended (waiting for frontend)
2488
- state = {
2489
- ...state,
2490
- status: 'suspended',
2491
- currentStepId: currentActivity.id,
2492
- };
2493
- await this.updateInstanceFromExecutionState(instanceId, state);
2494
- return {
2495
- executionId: instanceId,
2496
- state,
2497
- pendingTask
2498
- };
2499
- }
2500
- // Backend activity → execute it
2501
- console.log(`[WorkflowExecutionService] Executing backend activity '${currentActivity.name || currentActivity.id}' (${activityType})`);
2502
- const executionResult = await this.executeBackendActivityLocally(currentActivity, { ...state.variables, ...state.input }, state);
2503
- // Update variables with output
2504
- state = {
2505
- ...state,
2506
- variables: {
2507
- ...state.variables,
2508
- ...executionResult.output
2509
- },
2510
- currentStepId: currentActivity.id,
2511
- lastUpdated: new Date()
2512
- };
2513
- // Find next activity based on outcome
2514
- const outcome = Object.keys(executionResult.outcomes)[0] || 'Done';
2515
- const nextConnection = connections.find((conn) => conn.source.activity === currentActivity.id &&
2516
- (conn.source.port === outcome || (!conn.source.port && outcome === 'Done')));
2517
- if (!nextConnection) {
2518
- // No next activity → workflow completed
2519
- state = {
2520
- ...state,
2521
- status: 'completed',
2522
- currentStepId: undefined,
2523
- output: state.variables
2524
- };
2525
- await this.updateInstanceFromExecutionState(instanceId, state);
2526
- return {
2527
- executionId: instanceId,
2528
- state,
2529
- pendingTask: null
2530
- };
2531
- }
2532
- // Find next activity
2533
- currentActivity = this.findActivityInFlowchart(flowchart, nextConnection.target.activity);
2534
- }
2535
- // If we exit loop without frontend activity, workflow is complete
2536
- state = {
2537
- ...state,
2538
- status: 'completed',
2539
- currentStepId: undefined,
2540
- output: state.variables
2541
- };
2542
- await this.updateInstanceFromExecutionState(instanceId, state);
2543
- return {
2544
- executionId: instanceId,
2545
- state,
2546
- pendingTask: null
2547
- };
2548
- }
2549
- /**
2550
- * Find activity in Flowchart by ID.
2551
- */
2552
- findActivityInFlowchart(flowchart, activityId) {
2553
- const activities = flowchart.activities || [];
2554
- return activities.find(a => a.id === activityId) || null;
2555
- }
2556
- /**
2557
- * Resume a suspended workflow execution.
2558
- *
2559
- * After frontend completes its task:
2560
- * 1. Validates taskToken
2561
- * 2. Finds next activity based on outcome
2562
- * 3. Executes ALL backend activities in sequence
2563
- * 4. Stops at first frontend activity and returns it as pendingTask
2564
- */
2565
- async resumeExecution(request) {
2566
- // Get current state
2567
- let state = await this.getExecutionState({
2568
- executionId: request.executionId,
2569
- });
2570
- if (!state) {
2571
- throw new Error(`Workflow execution not found: ${request.executionId}`);
2572
- }
2573
- if (state.status !== 'suspended') {
2574
- throw new Error(`Cannot resume workflow with status: ${state.status}. Expected: suspended`);
2575
- }
2576
- // TODO: Validate taskToken here for security
2577
- // For now, just verify stepId matches
2578
- if (state.currentStepId !== request.stepId) {
2579
- throw new Error(`Step mismatch: expected '${state.currentStepId}', got '${request.stepId}'`);
2580
- }
2581
- // Get workflow definition
2582
- const workflowResponse = await this.getWorkflowDefinition({
2583
- workflowId: state.workflowId
2584
- });
2585
- const workflow = workflowResponse.workflow;
2586
- const connections = workflow.root.connections || [];
2587
- // Update state with frontend activity outcome and user input
2588
- state = {
2589
- ...state,
2590
- variables: {
2591
- ...state.variables,
2592
- [`${request.stepId}_outcome`]: request.outcome,
2593
- [`${request.stepId}_userInput`]: request.userInput || {},
2594
- },
2595
- lastUpdated: new Date()
2596
- };
2597
- // Find next activity based on outcome from frontend
2598
- const nextConnection = connections.find((conn) => conn.source.activity === request.stepId &&
2599
- (conn.source.port === request.outcome || (!conn.source.port && request.outcome === 'Done')));
2600
- console.log('[AXCWorkflowExecutionService] 🔍 Looking for next connection', {
2601
- stepId: request.stepId,
2602
- outcome: request.outcome,
2603
- connectionsCount: connections.length,
2604
- foundConnection: !!nextConnection
2605
- });
2606
- if (!nextConnection) {
2607
- // No next activity → workflow completed
2608
- console.log('[AXCWorkflowExecutionService] ✅ No next activity found - workflow completed');
2609
- state = {
2610
- ...state,
2611
- status: 'completed',
2612
- currentStepId: undefined,
2613
- output: state.variables
2614
- };
2615
- console.log('[AXCWorkflowExecutionService] 📝 Updating instance to completed state');
2616
- await this.updateInstanceFromExecutionState(request.executionId, state);
2617
- return {
2618
- output: { outcome: request.outcome, userInput: request.userInput },
2619
- outcomes: { [request.outcome]: true },
2620
- nextTask: null,
2621
- state
2622
- };
2623
- }
2624
- // 🎯 KEY LOGIC: Execute all backend activities until we reach next frontend activity
2625
- let currentActivity = this.findActivityInFlowchart(workflow.root, nextConnection.target.activity);
2626
- let executionCount = 0;
2627
- const maxExecutions = 100;
2628
- while (currentActivity && executionCount < maxExecutions) {
2629
- executionCount++;
2630
- // Map activity type
2631
- let activityType = currentActivity.type;
2632
- if (activityType.startsWith('Elsa.')) {
2633
- let type = activityType.replace(/^Elsa\./, '');
2634
- type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
2635
- activityType = `workflow-activity:${type}`;
2636
- }
2637
- // Determine execution mode
2638
- const executionMode = this.determineExecutionMode(activityType, currentActivity);
2639
- // If frontend activity → stop and return as pendingTask
2640
- if (executionMode === 'frontend') {
2641
- const pendingTask = this.createTaskFromActivity(currentActivity, state);
2642
- state = {
2643
- ...state,
2644
- status: 'suspended',
2645
- currentStepId: currentActivity.id,
2646
- };
2647
- await this.updateInstanceFromExecutionState(request.executionId, state);
2648
- return {
2649
- output: { outcome: request.outcome, userInput: request.userInput },
2650
- outcomes: { [request.outcome]: true },
2651
- nextTask: pendingTask,
2652
- state
2653
- };
2654
- }
2655
- // Backend activity → execute it
2656
- console.log(`[WorkflowExecutionService] Executing backend activity '${currentActivity.name || currentActivity.id}' (${activityType})`);
2657
- const executionResult = await this.executeBackendActivityLocally(currentActivity, { ...state.variables, ...state.input }, state);
2658
- // Update variables with output
2659
- state = {
2660
- ...state,
2661
- variables: {
2662
- ...state.variables,
2663
- ...executionResult.output
2664
- },
2665
- currentStepId: currentActivity.id,
2666
- lastUpdated: new Date()
2667
- };
2668
- // Find next activity based on outcome
2669
- const outcome = Object.keys(executionResult.outcomes)[0] || 'Done';
2670
- const nextConn = connections.find((conn) => conn.source.activity === currentActivity.id &&
2671
- (conn.source.port === outcome || (!conn.source.port && outcome === 'Done')));
2672
- if (!nextConn) {
2673
- // No next activity → workflow completed
2674
- state = {
2675
- ...state,
2676
- status: 'completed',
2677
- currentStepId: undefined,
2678
- output: state.variables
2679
- };
2680
- await this.updateInstanceFromExecutionState(request.executionId, state);
2681
- return {
2682
- output: state.variables,
2683
- outcomes: { Done: true },
2684
- nextTask: null,
2685
- state
2686
- };
2687
- }
2688
- // Find next activity
2689
- currentActivity = this.findActivityInFlowchart(workflow.root, nextConn.target.activity);
2690
- }
2691
- // If we exit loop, workflow is complete
2692
- state = {
2693
- ...state,
2694
- status: 'completed',
2695
- currentStepId: undefined,
2696
- output: state.variables
2697
- };
2698
- await this.updateInstanceFromExecutionState(request.executionId, state);
2699
- return {
2700
- output: state.variables,
2701
- outcomes: { Done: true },
2702
- nextTask: null,
2703
- state
2704
- };
2705
- }
2706
- /**
2707
- * Get current workflow execution state.
2708
- */
2709
- async getExecutionState(request) {
2710
- // Get instance from IndexedDB
2711
- const instance = await this.getInstance(request.executionId);
2712
- if (!instance) {
2713
- throw new Error(`Workflow execution not found: ${request.executionId}`);
2714
- }
2715
- // Convert instance to execution state
2716
- return this.instanceToExecutionState(instance);
2717
- }
2718
- /**
2719
- * Get workflow definition by ID.
2720
- *
2721
- * In mock implementation, this retrieves workflow definition from localStorage.
2722
- * In production, this would make an HTTP call to backend API.
2723
- *
2724
- * @param request - Get workflow definition request
2725
- * @returns Workflow definition
2726
- */
2727
- async getWorkflowDefinition(request) {
2728
- // In mock, try to get from localStorage first (for workflow-studio testing)
2729
- const storedWorkflow = localStorage.getItem(`workflow:${request.workflowId}`);
2730
- if (storedWorkflow) {
2731
- try {
2732
- const workflowDefinition = JSON.parse(storedWorkflow);
2733
- console.log('[AXCWorkflowExecutionService] 📄 Parsed workflow definition', {
2734
- hasRoot: !!workflowDefinition.root,
2735
- rootType: workflowDefinition.root?.type
2736
- });
2737
- // Validate format - if root is already a Flowchart
2738
- if (workflowDefinition.root && workflowDefinition.root.type === 'workflow-activity:flowchart') {
2739
- return { workflow: workflowDefinition };
2740
- }
2741
- // If stored in materializerContext.stringData, parse it
2742
- if (workflowDefinition.materializerContext?.stringData) {
2743
- try {
2744
- const rootFlowchart = JSON.parse(workflowDefinition.materializerContext.stringData);
2745
- if (rootFlowchart.type === 'workflow-activity:flowchart') {
2746
- return {
2747
- workflow: {
2748
- ...workflowDefinition,
2749
- root: rootFlowchart
2750
- }
2751
- };
2752
- }
2753
- }
2754
- catch (error) {
2755
- console.warn('[AXCWorkflowExecutionService] ⚠️ Failed to parse stringData from materializerContext:', error);
2756
- }
2757
- }
2758
- // 🎯 NEW: If root is a single activity (not Flowchart), convert it to Flowchart
2759
- if (workflowDefinition.root && workflowDefinition.root.type !== 'workflow-activity:flowchart') {
2760
- console.log('[AXCWorkflowExecutionService] 🔄 Converting single activity to Flowchart', workflowDefinition.root);
2761
- const singleActivity = workflowDefinition.root;
2762
- // Create a Flowchart with this single activity
2763
- const flowchart = {
2764
- id: 'root-flowchart',
2765
- nodeId: 'root-flowchart',
2766
- type: 'workflow-activity:flowchart',
2767
- version: 1,
2768
- activities: [singleActivity],
2769
- connections: [],
2770
- variables: []
2771
- };
2772
- console.log('[AXCWorkflowExecutionService] ✅ Created Flowchart from single activity', {
2773
- activitiesCount: flowchart.activities.length,
2774
- activityType: singleActivity.type
2775
- });
2776
- return {
2777
- workflow: {
2778
- ...workflowDefinition,
2779
- root: flowchart
2780
- }
2781
- };
2782
- }
2783
- }
2784
- catch (error) {
2785
- console.warn(`[AXCWorkflowExecutionService] ⚠️ Failed to parse stored workflow ${request.workflowId}:`, error);
2786
- }
2787
- }
2788
- // If not found in localStorage, return a default mock workflow
2789
- // In production, this would be an HTTP call to backend
2790
- const now = new Date().toISOString();
2791
- const defaultWorkflow = {
2792
- name: request.workflowId, // workflowId is the name (unique key)
2793
- title: `Workflow ${request.workflowId}`, // Display title
2794
- description: 'Default mock workflow',
2795
- variables: [],
2796
- inputs: [],
2797
- outputs: [],
2798
- outcomes: ['Done'],
2799
- customProperties: {},
2800
- options: {
2801
- usableAsActivity: false,
2802
- autoUpdateConsumingWorkflows: false
2803
- },
2804
- root: {
2805
- id: 'root-flowchart',
2806
- nodeId: 'root-flowchart',
2807
- type: 'workflow-activity:flowchart',
2808
- version: 1,
2809
- activities: [],
2810
- connections: [],
2811
- variables: []
2812
- }
2813
- };
2814
- return { workflow: defaultWorkflow };
2815
- }
2816
- //#region ---- Task-Based Helper Methods ----
2817
- /**
2818
- * Generate unique task token for task-based execution.
2819
- * Inspired by Temporal's task tokens for secure task completion.
2820
- */
2821
- generateTaskToken(executionId, activityId) {
2822
- return `${executionId}:${activityId}:${Date.now()}`;
2823
- }
2824
- /**
2825
- * Determine execution mode for an activity.
2826
- *
2827
- * Priority (highest to lowest):
2828
- * 1. Activity instance's executionMode (from AXPActivity in workflow definition)
2829
- * 2. ActivityDescriptor's executionMode (from ActivityProviderService)
2830
- * 3. Default: 'frontend'
2831
- *
2832
- * @param activityType - Activity type (e.g., 'workflow-activity:write-line')
2833
- * @param activity - Activity from workflow definition (AXPActivity)
2834
- * @returns Execution mode: 'frontend', 'backend', or 'both'
2835
- */
2836
- determineExecutionMode(activityType, activity) {
2837
- // 1. Check if executionMode is explicitly set in activity instance (highest priority)
2838
- if (activity?.executionMode) {
2839
- return activity.executionMode;
2840
- }
2841
- // 2. Check ActivityDescriptor from ActivityProviderService
2842
- // const descriptor = this.activityProviderService.getMetadata(activityType);
2843
- // if (descriptor?.executionMode) {
2844
- // return descriptor.executionMode;
2845
- // }
2846
- // 3. Default to frontend (UI activities are more common)
2847
- return 'frontend';
2848
- }
2849
- /**
2850
- * Create a task from a workflow activity.
2851
- * Inspired by Temporal's Activity Task and Elsa's Bookmark pattern.
2852
- */
2853
- createTaskFromActivity(activity, state) {
2854
- // Extract properties from activity
2855
- // Activities have properties as AXPInputValue objects or primitives
2856
- const properties = {};
2857
- // Iterate through all properties of the activity
2858
- Object.keys(activity).forEach(key => {
2859
- // Skip standard Elsa fields
2860
- if (['id', 'nodeId', 'name', 'type', 'version', 'customProperties', 'metadata', 'executionMode'].includes(key)) {
2861
- return;
2862
- }
2863
- const value = activity[key];
2864
- // 🎯 SPECIAL HANDLING: If key is 'properties' and it's an object, flatten it
2865
- // This handles workflow-studio format where properties are nested
2866
- if (key === 'properties' && value && typeof value === 'object' && !('typeName' in value) && !('expression' in value)) {
2867
- // Flatten properties object
2868
- Object.keys(value).forEach(propKey => {
2869
- const propValue = value[propKey];
2870
- // Handle AXPInputValue format within properties
2871
- if (propValue && typeof propValue === 'object' && 'typeName' in propValue && 'expression' in propValue) {
2872
- if (propValue.expression && propValue.expression.value !== undefined) {
2873
- properties[propKey] = propValue.expression.value;
2874
- }
2875
- else {
2876
- properties[propKey] = null;
2877
- }
2878
- }
2879
- else {
2880
- // Direct value
2881
- properties[propKey] = propValue;
2882
- }
2883
- });
2884
- return;
2885
- }
2886
- // Handle AXPInputValue format
2887
- if (value && typeof value === 'object' && 'typeName' in value && 'expression' in value) {
2888
- // It's an AXPInputValue - extract the expression value
2889
- if (value.expression && value.expression.value !== undefined) {
2890
- properties[key] = value.expression.value;
2891
- }
2892
- else {
2893
- properties[key] = null;
2894
- }
2895
- }
2896
- else {
2897
- // Direct value
2898
- properties[key] = value;
2899
- }
2900
- });
2901
- console.log('[AXCWorkflowExecutionService] 📦 Extracted properties for task', properties);
2902
- // Map activity type to ACoreX activity type
2903
- // Example: "Elsa.WriteLine" -> "workflow-activity:write-line"
2904
- let activityType = activity.type;
2905
- if (activityType.startsWith('Elsa.')) {
2906
- let type = activityType.replace(/^Elsa\./, '');
2907
- // Convert PascalCase to kebab-case
2908
- type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
2909
- activityType = `workflow-activity:${type}`;
2910
- }
2911
- // Get executionMode from activity descriptor
2912
- const executionMode = this.determineExecutionMode(activityType, activity);
2913
- return {
2914
- taskToken: this.generateTaskToken(state.executionId, activity.id),
2915
- activityId: activity.id,
2916
- activityType,
2917
- activityName: activity.name || undefined,
2918
- executionMode,
2919
- input: (executionMode === 'frontend' || executionMode === 'both') ? properties : undefined,
2920
- config: (executionMode === 'frontend' || executionMode === 'both') ? properties : undefined
2921
- };
2922
- }
2923
- /**
2924
- * Save workflow instance to entity service for entity-based access.
2925
- */
2926
- async saveInstanceToEntityService(instance) {
2927
- try {
2928
- const entityModel = {
2929
- id: instance.id,
2930
- definitionId: instance.definitionId,
2931
- definitionVersionId: instance.definitionVersionId,
2932
- version: instance.version,
2933
- status: instance.status,
2934
- subStatus: instance.subStatus,
2935
- correlationId: instance.correlationId || undefined,
2936
- name: instance.name || undefined,
2937
- incidentCount: instance.incidentCount,
2938
- parentWorkflowInstanceId: instance.parentWorkflowInstanceId || undefined,
2939
- createdAt: instance.createdAt,
2940
- updatedAt: instance.updatedAt,
2941
- finishedAt: instance.finishedAt || undefined,
2942
- workflowInstance: instance
2943
- };
2944
- const entityName = 'WorkflowManagement.WorkflowInstance';
2945
- // Check if instance already exists
2946
- try {
2947
- const existing = await this.entityStorageService.getOne(entityName, entityModel.id);
2948
- if (existing) {
2949
- // Already exists - don't insert again, will be updated later
2950
- console.log('[AXCWorkflowExecutionService] ℹ️ Instance already exists in entity service, will be updated later', {
2951
- instanceId: instance.id
2952
- });
2953
- return;
2954
- }
2955
- }
2956
- catch (getError) {
2957
- // Instance doesn't exist - proceed with insert
2958
- }
2959
- // Insert new instance
2960
- await this.entityStorageService.insertOne(entityName, entityModel);
2961
- console.log('[AXCWorkflowExecutionService] ✅ Inserted new instance in entity service', {
2962
- instanceId: instance.id,
2963
- status: instance.status,
2964
- subStatus: instance.subStatus
2965
- });
2966
- }
2967
- catch (error) {
2968
- console.warn('[AXCWorkflowExecutionService] Failed to save instance to entity service:', error);
2969
- // Don't throw - IndexedDB is primary storage
2970
- }
2971
- }
2972
- /**
2973
- * Update workflow instance in entity service.
2974
- * Uses upsert pattern: try update first, if fails then insert.
2975
- */
2976
- async updateInstanceInEntityService(instance) {
2977
- try {
2978
- const entityModel = {
2979
- id: instance.id,
2980
- definitionId: instance.definitionId,
2981
- definitionVersionId: instance.definitionVersionId,
2982
- version: instance.version,
2983
- status: instance.status,
2984
- subStatus: instance.subStatus,
2985
- correlationId: instance.correlationId || undefined,
2986
- name: instance.name || undefined,
2987
- incidentCount: instance.incidentCount,
2988
- parentWorkflowInstanceId: instance.parentWorkflowInstanceId || undefined,
2989
- createdAt: instance.createdAt,
2990
- updatedAt: instance.updatedAt,
2991
- finishedAt: instance.finishedAt || undefined,
2992
- workflowInstance: instance
2993
- };
2994
- const entityName = 'WorkflowManagement.WorkflowInstance';
2995
- // Try update first (upsert pattern)
2996
- try {
2997
- await this.entityStorageService.updateOne(entityName, entityModel.id, entityModel);
2998
- console.log('[AXCWorkflowExecutionService] ✅ Updated instance in entity service', {
2999
- instanceId: instance.id,
3000
- status: instance.status,
3001
- subStatus: instance.subStatus
3002
- });
3003
- return; // Successfully updated, exit
3004
- }
3005
- catch (updateError) {
3006
- // Update failed - instance doesn't exist, try insert
3007
- console.log('[AXCWorkflowExecutionService] ℹ️ Update failed, trying insert', {
3008
- instanceId: instance.id,
3009
- error: updateError.message
3010
- });
3011
- }
3012
- // Try insert
3013
- try {
3014
- await this.entityStorageService.insertOne(entityName, entityModel);
3015
- console.log('[AXCWorkflowExecutionService] ✅ Inserted instance in entity service', {
3016
- instanceId: instance.id,
3017
- status: instance.status,
3018
- subStatus: instance.subStatus
3019
- });
3020
- }
3021
- catch (insertError) {
3022
- // Insert might fail if instance was created between update and insert
3023
- // Try update again as fallback
3024
- try {
3025
- await this.entityStorageService.updateOne(entityName, entityModel.id, entityModel);
3026
- console.log('[AXCWorkflowExecutionService] ✅ Updated instance in entity service (after insert failed)', {
3027
- instanceId: instance.id,
3028
- status: instance.status,
3029
- subStatus: instance.subStatus
3030
- });
3031
- }
3032
- catch (finalError) {
3033
- console.warn('[AXCWorkflowExecutionService] Failed to upsert instance in entity service:', finalError);
3034
- }
3035
- }
3036
- }
3037
- catch (error) {
3038
- console.warn('[AXCWorkflowExecutionService] Failed to update instance in entity service:', error);
3039
- // Don't throw - IndexedDB is primary storage
3040
- }
3041
- }
3042
- //#endregion
3043
- //#region ---- Backend Activity Executors ----
3044
- /**
3045
- * Execute backend activities locally using CommandBus.
3046
- *
3047
- * Backend activities are registered as Commands and executed via CommandService.
3048
- * This provides a unified execution channel for all activities.
3049
- */
3050
- async executeBackendActivityLocally(activity, input, state) {
3051
- console.log('[AXCWorkflowExecutionService] 🔧 executeBackendActivityLocally', {
3052
- activityId: activity.id,
3053
- activityType: activity.type,
3054
- input
3055
- });
3056
- // Extract properties from activity
3057
- const properties = {};
3058
- Object.keys(activity).forEach(key => {
3059
- if (['id', 'nodeId', 'name', 'type', 'version', 'customProperties', 'metadata', 'executionMode'].includes(key)) {
3060
- return;
3061
- }
3062
- const value = activity[key];
3063
- if (value && typeof value === 'object' && 'typeName' in value && 'expression' in value) {
3064
- if (value.expression && value.expression.value !== undefined) {
3065
- properties[key] = value.expression.value;
3066
- }
3067
- else {
3068
- properties[key] = null;
3069
- }
3070
- }
3071
- else {
3072
- properties[key] = value;
3073
- }
3074
- });
3075
- console.log('[AXCWorkflowExecutionService] 📦 Extracted properties', properties);
3076
- // Map activity type to ACoreX activity type
3077
- let activityType = activity.type;
3078
- if (activityType.startsWith('Elsa.')) {
3079
- let type = activityType.replace(/^Elsa\./, '');
3080
- type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
3081
- activityType = `workflow-activity:${type}`;
3082
- }
3083
- console.log('[AXCWorkflowExecutionService] 🔀 Activity type mapped', {
3084
- original: activity.type,
3085
- mapped: activityType
3086
- });
3087
- // Check if activity is registered as a Command
3088
- const commandExists = this.commandService.exists(activityType);
3089
- console.log('[AXCWorkflowExecutionService] 🔍 Command exists?', commandExists, activityType);
3090
- if (commandExists) {
3091
- console.log(`[AXCWorkflowExecutionService] ⚡ Executing '${activityType}' via CommandBus`);
3092
- try {
3093
- // Execute via CommandBus
3094
- const result = await this.commandService.execute(activityType, properties);
3095
- const finalResult = {
3096
- output: result?.data?.output || result,
3097
- outcomes: result?.data?.outcomes || { Done: true }
3098
- };
3099
- console.log('[AXCWorkflowExecutionService] ✅ Final result', finalResult);
3100
- return finalResult;
3101
- }
3102
- catch (error) {
3103
- console.error(`[AXCWorkflowExecutionService] ❌ Backend activity '${activityType}' failed:`, error);
3104
- return {
3105
- output: { error: error.message || 'Unknown error' },
3106
- outcomes: { Failed: true }
3107
- };
3108
- }
3109
- }
3110
- // Fallback: Handle built-in activities that aren't Commands yet
3111
- console.warn(`[WorkflowExecutionService] Activity '${activityType}' not registered as Command, using fallback`);
3112
- switch (activityType) {
3113
- case 'workflow-activity:set-variable':
3114
- return this.executeSetVariable({ type: activityType, properties }, input);
3115
- case 'workflow-activity:http-request':
3116
- return this.executeHttpRequest({ type: activityType, properties }, input);
3117
- default:
3118
- return { output: null, outcomes: { Done: true } };
3119
- }
3120
- }
3121
- /**
3122
- * Set variable activity - stores value in workflow state.
3123
- */
3124
- async executeSetVariable(activity, input) {
3125
- const variableName = activity.properties?.variableName;
3126
- const value = activity.properties?.value;
3127
- return {
3128
- output: { [variableName]: value },
3129
- outcomes: { Done: true }
3130
- };
3131
- }
3132
- /**
3133
- * Execute HTTP request - makes real HTTP call.
3134
- * Supports different HTTP methods and handles timeouts.
3135
- */
3136
- async executeHttpRequest(activity, input) {
3137
- const url = activity.properties?.url;
3138
- const method = activity.properties?.method || 'GET';
3139
- const timeout = activity.properties?.timeout || 30000;
3140
- const headers = activity.properties?.headers || {};
3141
- const body = activity.properties?.body;
3142
- try {
3143
- const controller = new AbortController();
3144
- const timeoutId = setTimeout(() => controller.abort(), timeout);
3145
- const fetchOptions = {
3146
- method,
3147
- signal: controller.signal,
3148
- headers: {
3149
- 'Content-Type': 'application/json',
3150
- ...headers
3151
- }
3152
- };
3153
- if (body && method !== 'GET' && method !== 'HEAD') {
3154
- fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);
3155
- }
3156
- const response = await fetch(url, fetchOptions);
3157
- clearTimeout(timeoutId);
3158
- const statusCode = response.status.toString();
3159
- let data;
3160
- try {
3161
- data = await response.json();
3162
- }
3163
- catch {
3164
- data = await response.text();
3165
- }
3166
- // Convert headers to object
3167
- const responseHeaders = {};
3168
- response.headers.forEach((value, key) => {
3169
- responseHeaders[key] = value;
3170
- });
3171
- return {
3172
- output: {
3173
- statusCode,
3174
- data,
3175
- status: response.status,
3176
- headers: responseHeaders
3177
- },
3178
- outcomes: {
3179
- [statusCode]: true,
3180
- Done: true
3181
- }
3182
- };
3183
- }
3184
- catch (error) {
3185
- if (error.name === 'AbortError') {
3186
- return {
3187
- output: { error: 'Request timeout' },
3188
- outcomes: { Timeout: true }
3189
- };
3190
- }
3191
- return {
3192
- output: { error: error.message },
3193
- outcomes: { Failed: true }
3194
- };
3195
- }
3196
- }
3197
- //#endregion
3198
- //#region ---- Instance Management ----
3199
- /**
3200
- * Convert workflow instance to execution state (for backward compatibility).
3201
- */
3202
- instanceToExecutionState(instance) {
3203
- const state = instance.workflowState;
3204
- // Find current activity from execution contexts
3205
- const currentContext = state.activityExecutionContexts.find(ctx => ctx.status === 'Running' && !ctx.completedAt);
3206
- // Map status
3207
- let status = 'running';
3208
- if (instance.status === 'Finished') {
3209
- if (instance.subStatus === 'Faulted') {
3210
- status = 'error';
3211
- }
3212
- else {
3213
- status = 'completed';
3214
- }
3215
- }
3216
- else if (instance.subStatus === 'Suspended') {
3217
- status = 'suspended';
3218
- }
3219
- return {
3220
- executionId: instance.id,
3221
- workflowId: instance.definitionId,
3222
- status,
3223
- currentStepId: currentContext?.scheduledActivityNodeId,
3224
- variables: state.properties || {},
3225
- input: state.input || {},
3226
- output: state.output || {},
3227
- lastUpdated: new Date(instance.updatedAt)
3228
- };
3229
- }
3230
- /**
3231
- * Update execution state in instance and save to IndexedDB.
3232
- */
3233
- async updateInstanceFromExecutionState(executionId, executionState) {
3234
- const instance = await this.getInstance(executionId);
3235
- if (!instance) {
3236
- throw new Error(`Workflow instance not found: ${executionId}`);
3237
- }
3238
- // Update instance from execution state
3239
- const now = new Date().toISOString();
3240
- // Map execution state status to instance status
3241
- let status = 'Running';
3242
- let subStatus = 'Executing';
3243
- console.log('[AXCWorkflowExecutionService] 🔄 updateInstanceFromExecutionState', {
3244
- executionStateStatus: executionState.status,
3245
- instanceId: instance.id,
3246
- currentInstanceStatus: instance.status
3247
- });
3248
- if (executionState.status === 'completed') {
3249
- console.log('[AXCWorkflowExecutionService] ✅ Marking workflow as Finished');
3250
- status = 'Finished';
3251
- subStatus = 'Finished';
3252
- instance.finishedAt = now;
3253
- }
3254
- else if (executionState.status === 'error') {
3255
- status = 'Finished';
3256
- subStatus = 'Faulted';
3257
- instance.finishedAt = now;
3258
- }
3259
- else if (executionState.status === 'suspended') {
3260
- subStatus = 'Suspended';
3261
- }
3262
- instance.status = status;
3263
- instance.subStatus = subStatus;
3264
- instance.updatedAt = now;
3265
- // Update workflow state
3266
- instance.workflowState.status = status;
3267
- instance.workflowState.subStatus = subStatus;
3268
- instance.workflowState.input = executionState.input || {};
3269
- instance.workflowState.output = executionState.output || {};
3270
- instance.workflowState.properties = executionState.variables || {};
3271
- instance.workflowState.updatedAt = now;
3272
- if (executionState.status === 'completed' || executionState.status === 'error') {
3273
- instance.workflowState.finishedAt = now;
3274
- }
3275
- // Update activity execution context if currentStepId changed
3276
- if (executionState.currentStepId) {
3277
- const existingContext = instance.workflowState.activityExecutionContexts.find(ctx => ctx.scheduledActivityNodeId === executionState.currentStepId);
3278
- if (!existingContext) {
3279
- // Add new context
3280
- instance.workflowState.activityExecutionContexts.push({
3281
- id: `ctx-${executionState.currentStepId}`,
3282
- parentContextId: null,
3283
- scheduledActivityNodeId: executionState.currentStepId,
3284
- ownerActivityNodeId: null,
3285
- properties: {},
3286
- activityState: null,
3287
- dynamicVariables: [],
3288
- status: 'Running',
3289
- isExecuting: false,
3290
- faultCount: 0,
3291
- startedAt: now,
3292
- completedAt: null,
3293
- tag: null
3294
- });
3295
- }
3296
- }
3297
- // Save to IndexedDB
3298
- await this.updateInstance(instance);
3299
- // 🎯 Also update in entity service
3300
- await this.updateInstanceInEntityService(instance);
3301
- return instance;
3302
- }
3303
- /**
3304
- * Get workflow instance from IndexedDB.
3305
- */
3306
- async getInstance(executionId) {
3307
- return await this.db.instances.get(executionId) || null;
3308
- }
3309
- /**
3310
- * Update workflow instance in IndexedDB.
3311
- */
3312
- async updateInstance(instance) {
3313
- instance.updatedAt = new Date().toISOString();
3314
- await this.db.instances.update(instance.id, instance);
3315
- }
3316
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowExecutionService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3317
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowExecutionService }); }
3318
- }
3319
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowExecutionService, decorators: [{
3320
- type: Injectable
3321
- }] });
3322
-
3323
2402
  /**
3324
2403
  * Mock workflow definitions for testing and demonstration.
3325
2404
  */
@@ -3812,259 +2891,1084 @@ const workflowDefinitionMock = [
3812
2891
  },
3813
2892
  ];
3814
2893
  /**
3815
- * Export definitionId list for easy access
2894
+ * Export definitionId list for easy access
2895
+ */
2896
+ const workflowDefinitionIds = {
2897
+ createUser: 'create-user-workflow',
2898
+ welcome: 'welcome-workflow',
2899
+ dataProcessing: 'data-processing-workflow',
2900
+ };
2901
+
2902
+ /**
2903
+ * Seeds workflow definitions into localStorage for mock/demo purposes.
2904
+ *
2905
+ * Workflow definitions are stored in localStorage with key pattern:
2906
+ * `workflow:{definitionId}`
2907
+ *
2908
+ * This allows the mock workflow execution service to retrieve definitions
2909
+ * without making HTTP calls.
2910
+ */
2911
+ let AXCWorkflowDefinitionDataSeeder$1 = class AXCWorkflowDefinitionDataSeeder {
2912
+ constructor() {
2913
+ this.storageService = inject(AXPEntityStorageService);
2914
+ }
2915
+ async seed() {
2916
+ // Store each workflow definition in localStorage
2917
+ // for (const workflow of workflowDefinitionMock) {
2918
+ // localStorage.setItem(`workflow:${workflow.definitionId}`, JSON.stringify(workflow));
2919
+ // console.log(`[WorkflowDefinitionSeeder] Seeded workflow: ${workflow.name} (${workflow.definitionId})`);
2920
+ // }
2921
+ // Also store in entity storage for entity-based access
2922
+ await this.storageService.initial('Workflow.WorkflowDefinition', workflowDefinitionMock);
2923
+ }
2924
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionDataSeeder, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2925
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionDataSeeder }); }
2926
+ };
2927
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionDataSeeder$1, decorators: [{
2928
+ type: Injectable
2929
+ }] });
2930
+
2931
+ class AXCCommonMockModule {
2932
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCCommonMockModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
2933
+ static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: AXCCommonMockModule }); }
2934
+ static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCCommonMockModule, providers: [
2935
+ {
2936
+ provide: AXP_ACTIVITY_LOG_PROVIDER,
2937
+ useClass: AXCPActivityLogService,
2938
+ multi: true,
2939
+ },
2940
+ {
2941
+ provide: AXP_SEARCH_PROVIDER,
2942
+ useClass: EntitySearchProvider,
2943
+ multi: true,
2944
+ },
2945
+ {
2946
+ provide: AXPLockService,
2947
+ useClass: AXCLockService,
2948
+ },
2949
+ {
2950
+ provide: AXPFileStorageService,
2951
+ useClass: AXCFileStorageService,
2952
+ },
2953
+ {
2954
+ provide: AXPVersioningService,
2955
+ useClass: AXCVersioningService,
2956
+ },
2957
+ // {
2958
+ // provide: AXP_ACTIVITY_PROVIDER,
2959
+ // useClass: AXCActivityProvider,
2960
+ // multi: true,
2961
+ // },
2962
+ //TODO ADD after testing workflow local engine
2963
+ // {
2964
+ // provide: AXP_WORKFLOW_ENGINE,
2965
+ // useClass: AXCWorkflowRuntimeService,
2966
+ // },
2967
+ {
2968
+ provide: AXP_DATA_SEEDER_TOKEN,
2969
+ useClass: AXCWorkflowDefinitionDataSeeder$1,
2970
+ multi: true,
2971
+ },
2972
+ // Register generic Entity:UpdateStatus command
2973
+ provideCommandSetups([{ key: 'Entity:UpdateStatus', command: () => AXCEntityUpdateStatusCommand }]),
2974
+ AXCEntityUpdateStatusCommand,
2975
+ ] }); }
2976
+ }
2977
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCCommonMockModule, decorators: [{
2978
+ type: NgModule,
2979
+ args: [{
2980
+ imports: [],
2981
+ exports: [],
2982
+ declarations: [],
2983
+ providers: [
2984
+ {
2985
+ provide: AXP_ACTIVITY_LOG_PROVIDER,
2986
+ useClass: AXCPActivityLogService,
2987
+ multi: true,
2988
+ },
2989
+ {
2990
+ provide: AXP_SEARCH_PROVIDER,
2991
+ useClass: EntitySearchProvider,
2992
+ multi: true,
2993
+ },
2994
+ {
2995
+ provide: AXPLockService,
2996
+ useClass: AXCLockService,
2997
+ },
2998
+ {
2999
+ provide: AXPFileStorageService,
3000
+ useClass: AXCFileStorageService,
3001
+ },
3002
+ {
3003
+ provide: AXPVersioningService,
3004
+ useClass: AXCVersioningService,
3005
+ },
3006
+ // {
3007
+ // provide: AXP_ACTIVITY_PROVIDER,
3008
+ // useClass: AXCActivityProvider,
3009
+ // multi: true,
3010
+ // },
3011
+ //TODO ADD after testing workflow local engine
3012
+ // {
3013
+ // provide: AXP_WORKFLOW_ENGINE,
3014
+ // useClass: AXCWorkflowRuntimeService,
3015
+ // },
3016
+ {
3017
+ provide: AXP_DATA_SEEDER_TOKEN,
3018
+ useClass: AXCWorkflowDefinitionDataSeeder$1,
3019
+ multi: true,
3020
+ },
3021
+ // Register generic Entity:UpdateStatus command
3022
+ provideCommandSetups([{ key: 'Entity:UpdateStatus', command: () => AXCEntityUpdateStatusCommand }]),
3023
+ AXCEntityUpdateStatusCommand,
3024
+ ],
3025
+ }]
3026
+ }] });
3027
+
3028
+ //#region ---- Imports ----
3029
+ function toUiRows(changes) {
3030
+ return changes.map((c) => ({
3031
+ badge: toBadge(c.op),
3032
+ path: c.path,
3033
+ summary: toSummary(c),
3034
+ oldValue: c.oldValue,
3035
+ newValue: c.newValue,
3036
+ }));
3037
+ }
3038
+ function toBadge(op) {
3039
+ switch (op) {
3040
+ case 'add':
3041
+ return 'Added';
3042
+ case 'remove':
3043
+ return 'Removed';
3044
+ case 'replace':
3045
+ return 'Replaced';
3046
+ case 'rename':
3047
+ return 'Renamed';
3048
+ case 'metadata':
3049
+ return 'Metadata';
3050
+ default:
3051
+ return 'Updated';
3052
+ }
3053
+ }
3054
+ function toSummary(c) {
3055
+ switch (c.op) {
3056
+ case 'add':
3057
+ return 'Added';
3058
+ case 'remove':
3059
+ return 'Removed';
3060
+ case 'replace':
3061
+ return 'File replaced';
3062
+ case 'rename':
3063
+ return 'File renamed';
3064
+ case 'metadata':
3065
+ return 'File metadata updated';
3066
+ default:
3067
+ return 'Updated';
3068
+ }
3069
+ }
3070
+ //#endregion
3071
+
3072
+ //#region ---- Dexie DB ----
3073
+ /**
3074
+ * Dexie database for workflow instances.
3816
3075
  */
3817
- const workflowDefinitionIds = {
3818
- createUser: 'create-user-workflow',
3819
- welcome: 'welcome-workflow',
3820
- dataProcessing: 'data-processing-workflow',
3821
- };
3076
+ class AXCWorkflowExecutionDB extends Dexie {
3077
+ constructor() {
3078
+ super('ACoreXWorkflowExecutions');
3079
+ this.version(2).stores({
3080
+ instances: 'id, definitionId, status, subStatus, createdAt, updatedAt',
3081
+ });
3082
+ // Migration from v1 to v2: clear old executions table
3083
+ this.version(1).stores({
3084
+ executions: 'executionId, workflowId, status, currentStepId, lastUpdated',
3085
+ });
3086
+ }
3087
+ }
3088
+ /**
3089
+ * Singleton instance of workflow execution database.
3090
+ */
3091
+ const axWorkflowExecutionDB = new AXCWorkflowExecutionDB();
3092
+ //#endregion
3822
3093
 
3823
3094
  /**
3824
- * Seeds workflow definitions into localStorage for mock/demo purposes.
3095
+ * Mock implementation of AXPWorkflowEngine.
3825
3096
  *
3826
- * Workflow definitions are stored in localStorage with key pattern:
3827
- * `workflow:{definitionId}`
3097
+ * Uses IndexedDB (via Dexie) to store workflow instance states.
3828
3098
  *
3829
- * This allows the mock workflow execution service to retrieve definitions
3830
- * without making HTTP calls.
3099
+ * @example
3100
+ * ```typescript
3101
+ * // In module providers
3102
+ * {
3103
+ * provide: AXP_WORKFLOW_ENGINE,
3104
+ * useClass: AXCWorkflowRuntimeService,
3105
+ * }
3106
+ * ```
3831
3107
  */
3832
- let AXCWorkflowDefinitionDataSeeder$1 = class AXCWorkflowDefinitionDataSeeder {
3108
+ class AXCWorkflowRuntimeService {
3833
3109
  constructor() {
3834
- this.storageService = inject(AXPEntityStorageService);
3110
+ this.db = axWorkflowExecutionDB;
3111
+ this.commandService = inject(AXPCommandService);
3112
+ this.entityStorageService = inject(AXPEntityStorageService);
3835
3113
  }
3836
- async seed() {
3837
- // Store each workflow definition in localStorage
3838
- // for (const workflow of workflowDefinitionMock) {
3839
- // localStorage.setItem(`workflow:${workflow.definitionId}`, JSON.stringify(workflow));
3840
- // console.log(`[WorkflowDefinitionSeeder] Seeded workflow: ${workflow.name} (${workflow.definitionId})`);
3114
+ /**
3115
+ * Start a new workflow instance.
3116
+ * Creates a workflow instance and stores it in IndexedDB.
3117
+ */
3118
+ async start(request) {
3119
+ const instanceId = AXPDataGenerator.uuid();
3120
+ const now = new Date().toISOString();
3121
+ // Get workflow definition from entity storage service
3122
+ const workflowEntity = await this.entityStorageService.getOne('WorkflowManagement.WorkflowDefinition', request.workflowId);
3123
+ if (!workflowEntity) {
3124
+ throw new Error(`Workflow definition not found: ${request.workflowId}`);
3125
+ }
3126
+ // Convert entity model to workflow definition
3127
+ const workflow = workflowEntity;
3128
+ // Find first step from Flowchart (activity with no incoming connections)
3129
+ const flowchart = workflow.root;
3130
+ const activities = flowchart.activities || [];
3131
+ const connections = flowchart.connections || [];
3132
+ // Build connection map: target -> source[]
3133
+ const incomingConnections = new Map();
3134
+ connections.forEach((conn) => {
3135
+ const targetId = conn.target.activity;
3136
+ if (!incomingConnections.has(targetId)) {
3137
+ incomingConnections.set(targetId, []);
3138
+ }
3139
+ incomingConnections.get(targetId).push(conn.source.activity);
3140
+ });
3141
+ // Find root activity (activity with no incoming connections, or first activity)
3142
+ let firstActivity = null;
3143
+ for (const activity of activities) {
3144
+ if (!incomingConnections.has(activity.id)) {
3145
+ firstActivity = activity;
3146
+ break;
3147
+ }
3148
+ }
3149
+ // If no root found, use first activity
3150
+ if (!firstActivity && activities.length > 0) {
3151
+ firstActivity = activities[0];
3152
+ }
3153
+ const firstStepId = firstActivity?.id || 'root';
3154
+ // Create workflow state
3155
+ const workflowState = {
3156
+ id: `state-${instanceId}`,
3157
+ definitionId: workflow.name, // Use name as definitionId (backward compatibility)
3158
+ //definitionVersion: workflow.version,
3159
+ definitionVersion: 1,
3160
+ correlationId: null,
3161
+ status: 'Running',
3162
+ subStatus: 'Executing',
3163
+ bookmarks: [],
3164
+ incidents: [],
3165
+ fault: null,
3166
+ completionCallbacks: [],
3167
+ activityExecutionContexts: firstActivity ? [{
3168
+ id: `ctx-${firstStepId}`,
3169
+ parentContextId: null,
3170
+ scheduledActivityNodeId: firstStepId,
3171
+ ownerActivityNodeId: null,
3172
+ properties: {},
3173
+ activityState: null,
3174
+ dynamicVariables: [],
3175
+ status: 'Running',
3176
+ isExecuting: false,
3177
+ faultCount: 0,
3178
+ startedAt: now,
3179
+ completedAt: null,
3180
+ tag: null
3181
+ }] : [],
3182
+ input: request.input || {},
3183
+ output: {},
3184
+ properties: {},
3185
+ createdAt: now,
3186
+ updatedAt: now,
3187
+ finishedAt: null
3188
+ };
3189
+ // Create workflow instance
3190
+ const instance = {
3191
+ $schema: 'https://elsaworkflows.io/schemas/workflow-instance/v3.0.0/schema.json',
3192
+ id: instanceId,
3193
+ definitionId: workflow.name, // Use name as definitionId (backward compatibility)
3194
+ //definitionVersionId: workflow.id,
3195
+ //version: workflow.version,
3196
+ version: 1,
3197
+ definitionVersionId: '1',
3198
+ parentWorkflowInstanceId: null,
3199
+ workflowState,
3200
+ status: 'Running',
3201
+ subStatus: 'Executing',
3202
+ correlationId: null,
3203
+ name: workflow.name || null,
3204
+ incidentCount: 0,
3205
+ createdAt: now,
3206
+ updatedAt: now,
3207
+ finishedAt: null
3208
+ };
3209
+ // Store instance in IndexedDB
3210
+ await this.db.instances.add(instance, instance.id);
3211
+ // 🎯 Also store in entity service for entity-based access
3212
+ await this.saveInstanceToEntityService(instance);
3213
+ // Convert to instance state for response
3214
+ let state = this.instanceToInstanceState(instance);
3215
+ // 🎯 KEY LOGIC: Execute all backend activities until we reach a frontend activity
3216
+ let currentActivity = firstActivity;
3217
+ let executionCount = 0;
3218
+ const maxExecutions = 100; // Prevent infinite loops
3219
+ while (currentActivity && executionCount < maxExecutions) {
3220
+ executionCount++;
3221
+ // Map activity type to ACoreX format
3222
+ let activityType = currentActivity.type;
3223
+ if (activityType.startsWith('Elsa.')) {
3224
+ let type = activityType.replace(/^Elsa\./, '');
3225
+ type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
3226
+ activityType = `workflow-activity:${type}`;
3227
+ }
3228
+ // Determine execution mode
3229
+ const executionMode = this.determineExecutionMode(activityType, currentActivity);
3230
+ // If frontend activity → stop and return as pendingTask
3231
+ if (executionMode === 'frontend') {
3232
+ const pendingTask = this.createTaskFromActivity(currentActivity, state);
3233
+ // Update state to suspended (waiting for frontend)
3234
+ state = {
3235
+ ...state,
3236
+ status: 'suspended',
3237
+ currentStepId: currentActivity.id,
3238
+ };
3239
+ await this.updateInstanceFromState(instanceId, state);
3240
+ return {
3241
+ instanceId: instanceId,
3242
+ state,
3243
+ pendingTask
3244
+ };
3245
+ }
3246
+ // Backend activity → execute it
3247
+ console.log(`[WorkflowExecutionService] Executing backend activity '${currentActivity.name || currentActivity.id}' (${activityType})`);
3248
+ const executionResult = await this.executeBackendActivityLocally(currentActivity, { ...state.variables, ...state.input }, state);
3249
+ // Update variables with output
3250
+ state = {
3251
+ ...state,
3252
+ variables: {
3253
+ ...state.variables,
3254
+ ...executionResult.output
3255
+ },
3256
+ currentStepId: currentActivity.id,
3257
+ lastUpdated: new Date()
3258
+ };
3259
+ // Find next activity based on outcome
3260
+ const outcome = Object.keys(executionResult.outcomes)[0] || 'Done';
3261
+ const nextConnection = connections.find((conn) => conn.source.activity === currentActivity.id &&
3262
+ (conn.source.port === outcome || (!conn.source.port && outcome === 'Done')));
3263
+ if (!nextConnection) {
3264
+ // No next activity → workflow completed
3265
+ state = {
3266
+ ...state,
3267
+ status: 'completed',
3268
+ currentStepId: undefined,
3269
+ output: state.variables
3270
+ };
3271
+ await this.updateInstanceFromState(instanceId, state);
3272
+ return {
3273
+ instanceId: instanceId,
3274
+ state,
3275
+ pendingTask: null
3276
+ };
3277
+ }
3278
+ // Find next activity
3279
+ currentActivity = this.findActivityInFlowchart(flowchart, nextConnection.target.activity);
3280
+ }
3281
+ // If we exit loop without frontend activity, workflow is complete
3282
+ state = {
3283
+ ...state,
3284
+ status: 'completed',
3285
+ currentStepId: undefined,
3286
+ output: state.variables
3287
+ };
3288
+ await this.updateInstanceFromState(instanceId, state);
3289
+ return {
3290
+ instanceId: instanceId,
3291
+ state,
3292
+ pendingTask: null
3293
+ };
3294
+ }
3295
+ /**
3296
+ * Find activity in Flowchart by ID.
3297
+ */
3298
+ findActivityInFlowchart(flowchart, activityId) {
3299
+ const activities = flowchart.activities || [];
3300
+ return activities.find(a => a.id === activityId) || null;
3301
+ }
3302
+ /**
3303
+ * Resume a suspended workflow instance.
3304
+ *
3305
+ * After frontend completes its task:
3306
+ * 1. Validates taskToken
3307
+ * 2. Finds next activity based on outcome
3308
+ * 3. Executes ALL backend activities in sequence
3309
+ * 4. Stops at first frontend activity and returns it as pendingTask
3310
+ */
3311
+ async resume(request) {
3312
+ // Get current state
3313
+ let state = await this.getState({
3314
+ instanceId: request.instanceId,
3315
+ });
3316
+ if (!state) {
3317
+ throw new Error(`Workflow instance not found: ${request.instanceId}`);
3318
+ }
3319
+ if (state.status !== 'suspended') {
3320
+ throw new Error(`Cannot resume workflow with status: ${state.status}. Expected: suspended`);
3321
+ }
3322
+ // TODO: Validate taskToken here for security
3323
+ // For now, just verify stepId matches
3324
+ if (state.currentStepId !== request.stepId) {
3325
+ throw new Error(`Step mismatch: expected '${state.currentStepId}', got '${request.stepId}'`);
3326
+ }
3327
+ // Get workflow definition from entity storage service
3328
+ const workflowEntity = await this.entityStorageService.getOne('WorkflowManagement.WorkflowDefinition', state.workflowId);
3329
+ if (!workflowEntity) {
3330
+ throw new Error(`Workflow definition not found: ${state.workflowId}`);
3331
+ }
3332
+ // Convert entity model to workflow definition
3333
+ const workflow = workflowEntity;
3334
+ const connections = workflow.root.connections || [];
3335
+ // Update state with frontend activity outcome and user input
3336
+ state = {
3337
+ ...state,
3338
+ variables: {
3339
+ ...state.variables,
3340
+ [`${request.stepId}_outcome`]: request.outcome,
3341
+ [`${request.stepId}_userInput`]: request.userInput || {},
3342
+ },
3343
+ lastUpdated: new Date()
3344
+ };
3345
+ // Find next activity based on outcome from frontend
3346
+ const nextConnection = connections.find((conn) => conn.source.activity === request.stepId &&
3347
+ (conn.source.port === request.outcome || (!conn.source.port && request.outcome === 'Done')));
3348
+ console.log('[AXCWorkflowExecutionService] 🔍 Looking for next connection', {
3349
+ stepId: request.stepId,
3350
+ outcome: request.outcome,
3351
+ connectionsCount: connections.length,
3352
+ foundConnection: !!nextConnection
3353
+ });
3354
+ if (!nextConnection) {
3355
+ // No next activity → workflow completed
3356
+ console.log('[AXCWorkflowExecutionService] ✅ No next activity found - workflow completed');
3357
+ state = {
3358
+ ...state,
3359
+ status: 'completed',
3360
+ currentStepId: undefined,
3361
+ output: state.variables
3362
+ };
3363
+ console.log('[AXCWorkflowExecutionService] 📝 Updating instance to completed state');
3364
+ await this.updateInstanceFromState(request.instanceId, state);
3365
+ return {
3366
+ output: { outcome: request.outcome, userInput: request.userInput },
3367
+ outcomes: { [request.outcome]: true },
3368
+ nextTask: null,
3369
+ state
3370
+ };
3371
+ }
3372
+ // 🎯 KEY LOGIC: Execute all backend activities until we reach next frontend activity
3373
+ let currentActivity = this.findActivityInFlowchart(workflow.root, nextConnection.target.activity);
3374
+ let executionCount = 0;
3375
+ const maxExecutions = 100;
3376
+ while (currentActivity && executionCount < maxExecutions) {
3377
+ executionCount++;
3378
+ // Map activity type
3379
+ let activityType = currentActivity.type;
3380
+ if (activityType.startsWith('Elsa.')) {
3381
+ let type = activityType.replace(/^Elsa\./, '');
3382
+ type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
3383
+ activityType = `workflow-activity:${type}`;
3384
+ }
3385
+ // Determine execution mode
3386
+ const executionMode = this.determineExecutionMode(activityType, currentActivity);
3387
+ // If frontend activity → stop and return as pendingTask
3388
+ if (executionMode === 'frontend') {
3389
+ const pendingTask = this.createTaskFromActivity(currentActivity, state);
3390
+ state = {
3391
+ ...state,
3392
+ status: 'suspended',
3393
+ currentStepId: currentActivity.id,
3394
+ };
3395
+ await this.updateInstanceFromState(request.instanceId, state);
3396
+ return {
3397
+ output: { outcome: request.outcome, userInput: request.userInput },
3398
+ outcomes: { [request.outcome]: true },
3399
+ nextTask: pendingTask,
3400
+ state
3401
+ };
3402
+ }
3403
+ // Backend activity → execute it
3404
+ console.log(`[WorkflowExecutionService] Executing backend activity '${currentActivity.name || currentActivity.id}' (${activityType})`);
3405
+ const executionResult = await this.executeBackendActivityLocally(currentActivity, { ...state.variables, ...state.input }, state);
3406
+ // Update variables with output
3407
+ state = {
3408
+ ...state,
3409
+ variables: {
3410
+ ...state.variables,
3411
+ ...executionResult.output
3412
+ },
3413
+ currentStepId: currentActivity.id,
3414
+ lastUpdated: new Date()
3415
+ };
3416
+ // Find next activity based on outcome
3417
+ const outcome = Object.keys(executionResult.outcomes)[0] || 'Done';
3418
+ const nextConn = connections.find((conn) => conn.source.activity === currentActivity.id &&
3419
+ (conn.source.port === outcome || (!conn.source.port && outcome === 'Done')));
3420
+ if (!nextConn) {
3421
+ // No next activity → workflow completed
3422
+ state = {
3423
+ ...state,
3424
+ status: 'completed',
3425
+ currentStepId: undefined,
3426
+ output: state.variables
3427
+ };
3428
+ await this.updateInstanceFromState(request.instanceId, state);
3429
+ return {
3430
+ output: state.variables,
3431
+ outcomes: { Done: true },
3432
+ nextTask: null,
3433
+ state
3434
+ };
3435
+ }
3436
+ // Find next activity
3437
+ currentActivity = this.findActivityInFlowchart(workflow.root, nextConn.target.activity);
3438
+ }
3439
+ // If we exit loop, workflow is complete
3440
+ state = {
3441
+ ...state,
3442
+ status: 'completed',
3443
+ currentStepId: undefined,
3444
+ output: state.variables
3445
+ };
3446
+ await this.updateInstanceFromState(request.instanceId, state);
3447
+ return {
3448
+ output: state.variables,
3449
+ outcomes: { Done: true },
3450
+ nextTask: null,
3451
+ state
3452
+ };
3453
+ }
3454
+ /**
3455
+ * Get current workflow execution state.
3456
+ */
3457
+ async getState(request) {
3458
+ // Get instance from IndexedDB
3459
+ const instance = await this.getInstance(request.instanceId);
3460
+ if (!instance) {
3461
+ throw new Error(`Workflow instance not found: ${request.instanceId}`);
3462
+ }
3463
+ // Convert instance to state
3464
+ return this.instanceToInstanceState(instance);
3465
+ }
3466
+ //#region ---- Task-Based Helper Methods ----
3467
+ /**
3468
+ * Generate unique task token for task-based execution.
3469
+ * Inspired by Temporal's task tokens for secure task completion.
3470
+ */
3471
+ generateTaskToken(instanceId, activityId) {
3472
+ return `${instanceId}:${activityId}:${Date.now()}`;
3473
+ }
3474
+ /**
3475
+ * Determine execution mode for an activity.
3476
+ *
3477
+ * Priority (highest to lowest):
3478
+ * 1. Activity instance's executionMode (from AXPActivity in workflow definition)
3479
+ * 2. ActivityDescriptor's executionMode (from ActivityProviderService)
3480
+ * 3. Default: 'frontend'
3481
+ *
3482
+ * @param activityType - Activity type (e.g., 'workflow-activity:write-line')
3483
+ * @param activity - Activity from workflow definition (AXPActivity)
3484
+ * @returns Execution mode: 'frontend', 'backend', or 'both'
3485
+ */
3486
+ determineExecutionMode(activityType, activity) {
3487
+ // 1. Check if executionMode is explicitly set in activity instance (highest priority)
3488
+ if (activity?.executionMode) {
3489
+ return activity.executionMode;
3490
+ }
3491
+ // 2. Check ActivityDescriptor from ActivityProviderService
3492
+ // const descriptor = this.activityProviderService.getMetadata(activityType);
3493
+ // if (descriptor?.executionMode) {
3494
+ // return descriptor.executionMode;
3841
3495
  // }
3842
- // Also store in entity storage for entity-based access
3843
- await this.storageService.initial('Workflow.WorkflowDefinition', workflowDefinitionMock);
3496
+ // 3. Default to frontend (UI activities are more common)
3497
+ return 'frontend';
3844
3498
  }
3845
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionDataSeeder, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3846
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionDataSeeder }); }
3847
- };
3848
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionDataSeeder$1, decorators: [{
3849
- type: Injectable
3850
- }] });
3851
-
3852
- //#endregion
3853
- //#region ---- Update Status Command ----
3854
- /**
3855
- * Generic command to update entity status
3856
- * Supports both single entity (id) and bulk updates (ids)
3857
- *
3858
- * Options:
3859
- * - entity: string - Entity key (e.g., 'OrderManagement.GroupOrder')
3860
- * - id?: string - Single entity ID
3861
- * - ids?: string[] - Multiple entity IDs (for bulk updates)
3862
- * - status: string - Target status value
3863
- * - statusField?: string - Status field name (default: 'statusId')
3864
- */
3865
- class AXCEntityUpdateStatusCommand {
3866
- constructor() {
3867
- this.entityService = inject(AXPEntityService);
3499
+ /**
3500
+ * Create a task from a workflow activity.
3501
+ * Inspired by Temporal's Activity Task and Elsa's Bookmark pattern.
3502
+ */
3503
+ createTaskFromActivity(activity, state) {
3504
+ // Extract properties from activity
3505
+ // Activities have properties as AXPInputValue objects or primitives
3506
+ const properties = {};
3507
+ // Iterate through all properties of the activity
3508
+ Object.keys(activity).forEach(key => {
3509
+ // Skip standard Elsa fields
3510
+ if (['id', 'nodeId', 'name', 'type', 'version', 'customProperties', 'metadata', 'executionMode'].includes(key)) {
3511
+ return;
3512
+ }
3513
+ const value = activity[key];
3514
+ // 🎯 SPECIAL HANDLING: If key is 'properties' and it's an object, flatten it
3515
+ // This handles workflow-studio format where properties are nested
3516
+ if (key === 'properties' && value && typeof value === 'object' && !('typeName' in value) && !('expression' in value)) {
3517
+ // Flatten properties object
3518
+ Object.keys(value).forEach(propKey => {
3519
+ const propValue = value[propKey];
3520
+ // Handle AXPInputValue format within properties
3521
+ if (propValue && typeof propValue === 'object' && 'typeName' in propValue && 'expression' in propValue) {
3522
+ if (propValue.expression && propValue.expression.value !== undefined) {
3523
+ properties[propKey] = propValue.expression.value;
3524
+ }
3525
+ else {
3526
+ properties[propKey] = null;
3527
+ }
3528
+ }
3529
+ else {
3530
+ // Direct value
3531
+ properties[propKey] = propValue;
3532
+ }
3533
+ });
3534
+ return;
3535
+ }
3536
+ // Handle AXPInputValue format
3537
+ if (value && typeof value === 'object' && 'typeName' in value && 'expression' in value) {
3538
+ // It's an AXPInputValue - extract the expression value
3539
+ if (value.expression && value.expression.value !== undefined) {
3540
+ properties[key] = value.expression.value;
3541
+ }
3542
+ else {
3543
+ properties[key] = null;
3544
+ }
3545
+ }
3546
+ else {
3547
+ // Direct value
3548
+ properties[key] = value;
3549
+ }
3550
+ });
3551
+ console.log('[AXCWorkflowExecutionService] 📦 Extracted properties for task', properties);
3552
+ // Map activity type to ACoreX activity type
3553
+ // Example: "Elsa.WriteLine" -> "workflow-activity:write-line"
3554
+ let activityType = activity.type;
3555
+ if (activityType.startsWith('Elsa.')) {
3556
+ let type = activityType.replace(/^Elsa\./, '');
3557
+ // Convert PascalCase to kebab-case
3558
+ type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
3559
+ activityType = `workflow-activity:${type}`;
3560
+ }
3561
+ // Get executionMode from activity descriptor
3562
+ const executionMode = this.determineExecutionMode(activityType, activity);
3563
+ return {
3564
+ taskToken: this.generateTaskToken(state.instanceId, activity.id),
3565
+ activityId: activity.id,
3566
+ activityType,
3567
+ activityName: activity.name || undefined,
3568
+ executionMode,
3569
+ input: (executionMode === 'frontend' || executionMode === 'both') ? properties : undefined,
3570
+ config: (executionMode === 'frontend' || executionMode === 'both') ? properties : undefined
3571
+ };
3572
+ }
3573
+ /**
3574
+ * Save workflow instance to entity service for entity-based access.
3575
+ */
3576
+ async saveInstanceToEntityService(instance) {
3577
+ try {
3578
+ const entityModel = {
3579
+ id: instance.id,
3580
+ definitionId: instance.definitionId,
3581
+ definitionVersionId: instance.definitionVersionId,
3582
+ version: instance.version,
3583
+ status: instance.status,
3584
+ subStatus: instance.subStatus,
3585
+ correlationId: instance.correlationId || undefined,
3586
+ name: instance.name || undefined,
3587
+ incidentCount: instance.incidentCount,
3588
+ parentWorkflowInstanceId: instance.parentWorkflowInstanceId || undefined,
3589
+ createdAt: instance.createdAt,
3590
+ updatedAt: instance.updatedAt,
3591
+ finishedAt: instance.finishedAt || undefined,
3592
+ workflowInstance: instance
3593
+ };
3594
+ const entityName = 'WorkflowManagement.WorkflowInstance';
3595
+ // Check if instance already exists
3596
+ try {
3597
+ const existing = await this.entityStorageService.getOne(entityName, entityModel.id);
3598
+ if (existing) {
3599
+ // Already exists - don't insert again, will be updated later
3600
+ console.log('[AXCWorkflowExecutionService] ℹ️ Instance already exists in entity service, will be updated later', {
3601
+ instanceId: instance.id
3602
+ });
3603
+ return;
3604
+ }
3605
+ }
3606
+ catch (getError) {
3607
+ // Instance doesn't exist - proceed with insert
3608
+ }
3609
+ // Insert new instance
3610
+ await this.entityStorageService.insertOne(entityName, entityModel);
3611
+ console.log('[AXCWorkflowExecutionService] ✅ Inserted new instance in entity service', {
3612
+ instanceId: instance.id,
3613
+ status: instance.status,
3614
+ subStatus: instance.subStatus
3615
+ });
3616
+ }
3617
+ catch (error) {
3618
+ console.warn('[AXCWorkflowExecutionService] Failed to save instance to entity service:', error);
3619
+ // Don't throw - IndexedDB is primary storage
3620
+ }
3868
3621
  }
3869
- async execute(input) {
3622
+ /**
3623
+ * Update workflow instance in entity service.
3624
+ * Uses upsert pattern: try update first, if fails then insert.
3625
+ */
3626
+ async updateInstanceInEntityService(instance) {
3870
3627
  try {
3871
- const { entity, id, ids, status, statusField = 'statusId' } = input;
3872
- // Validate input
3873
- if (!entity) {
3874
- throw new Error('Entity name is required');
3628
+ const entityModel = {
3629
+ id: instance.id,
3630
+ definitionId: instance.definitionId,
3631
+ definitionVersionId: instance.definitionVersionId,
3632
+ version: instance.version,
3633
+ status: instance.status,
3634
+ subStatus: instance.subStatus,
3635
+ correlationId: instance.correlationId || undefined,
3636
+ name: instance.name || undefined,
3637
+ incidentCount: instance.incidentCount,
3638
+ parentWorkflowInstanceId: instance.parentWorkflowInstanceId || undefined,
3639
+ createdAt: instance.createdAt,
3640
+ updatedAt: instance.updatedAt,
3641
+ finishedAt: instance.finishedAt || undefined,
3642
+ workflowInstance: instance
3643
+ };
3644
+ const entityName = 'WorkflowManagement.WorkflowInstance';
3645
+ // Try update first (upsert pattern)
3646
+ try {
3647
+ await this.entityStorageService.updateOne(entityName, entityModel.id, entityModel);
3648
+ console.log('[AXCWorkflowExecutionService] ✅ Updated instance in entity service', {
3649
+ instanceId: instance.id,
3650
+ status: instance.status,
3651
+ subStatus: instance.subStatus
3652
+ });
3653
+ return; // Successfully updated, exit
3875
3654
  }
3876
- if (!id && (!ids || ids.length === 0)) {
3877
- throw new Error('Either id or ids must be provided');
3655
+ catch (updateError) {
3656
+ // Update failed - instance doesn't exist, try insert
3657
+ console.log('[AXCWorkflowExecutionService] ℹ️ Update failed, trying insert', {
3658
+ instanceId: instance.id,
3659
+ error: updateError.message
3660
+ });
3878
3661
  }
3879
- if (!status) {
3880
- throw new Error('Status value is required');
3662
+ // Try insert
3663
+ try {
3664
+ await this.entityStorageService.insertOne(entityName, entityModel);
3665
+ console.log('[AXCWorkflowExecutionService] ✅ Inserted instance in entity service', {
3666
+ instanceId: instance.id,
3667
+ status: instance.status,
3668
+ subStatus: instance.subStatus
3669
+ });
3881
3670
  }
3882
- const dataAccessor = this.entityService.withEntity(entity).data();
3883
- const updateData = { [statusField]: status };
3884
- // Handle bulk update
3885
- if (ids && ids.length > 0) {
3886
- const results = [];
3887
- for (const entityId of ids) {
3888
- const updated = await dataAccessor.update(entityId, updateData);
3889
- results.push(updated);
3671
+ catch (insertError) {
3672
+ // Insert might fail if instance was created between update and insert
3673
+ // Try update again as fallback
3674
+ try {
3675
+ await this.entityStorageService.updateOne(entityName, entityModel.id, entityModel);
3676
+ console.log('[AXCWorkflowExecutionService] ✅ Updated instance in entity service (after insert failed)', {
3677
+ instanceId: instance.id,
3678
+ status: instance.status,
3679
+ subStatus: instance.subStatus
3680
+ });
3890
3681
  }
3891
- return {
3892
- success: true,
3893
- data: results,
3682
+ catch (finalError) {
3683
+ console.warn('[AXCWorkflowExecutionService] Failed to upsert instance in entity service:', finalError);
3684
+ }
3685
+ }
3686
+ }
3687
+ catch (error) {
3688
+ console.warn('[AXCWorkflowExecutionService] Failed to update instance in entity service:', error);
3689
+ // Don't throw - IndexedDB is primary storage
3690
+ }
3691
+ }
3692
+ //#endregion
3693
+ //#region ---- Backend Activity Executors ----
3694
+ /**
3695
+ * Execute backend activities locally using CommandBus.
3696
+ *
3697
+ * Backend activities are registered as Commands and executed via CommandService.
3698
+ * This provides a unified execution channel for all activities.
3699
+ */
3700
+ async executeBackendActivityLocally(activity, input, state) {
3701
+ console.log('[AXCWorkflowExecutionService] 🔧 executeBackendActivityLocally', {
3702
+ activityId: activity.id,
3703
+ activityType: activity.type,
3704
+ input
3705
+ });
3706
+ // Extract properties from activity
3707
+ const properties = {};
3708
+ Object.keys(activity).forEach(key => {
3709
+ if (['id', 'nodeId', 'name', 'type', 'version', 'customProperties', 'metadata', 'executionMode'].includes(key)) {
3710
+ return;
3711
+ }
3712
+ const value = activity[key];
3713
+ if (value && typeof value === 'object' && 'typeName' in value && 'expression' in value) {
3714
+ if (value.expression && value.expression.value !== undefined) {
3715
+ properties[key] = value.expression.value;
3716
+ }
3717
+ else {
3718
+ properties[key] = null;
3719
+ }
3720
+ }
3721
+ else {
3722
+ properties[key] = value;
3723
+ }
3724
+ });
3725
+ console.log('[AXCWorkflowExecutionService] 📦 Extracted properties', properties);
3726
+ // Map activity type to ACoreX activity type
3727
+ let activityType = activity.type;
3728
+ if (activityType.startsWith('Elsa.')) {
3729
+ let type = activityType.replace(/^Elsa\./, '');
3730
+ type = type.replace(/([A-Z])/g, '-$1').toLowerCase().replace(/^-/, '');
3731
+ activityType = `workflow-activity:${type}`;
3732
+ }
3733
+ console.log('[AXCWorkflowExecutionService] 🔀 Activity type mapped', {
3734
+ original: activity.type,
3735
+ mapped: activityType
3736
+ });
3737
+ // Check if activity is registered as a Command
3738
+ const commandExists = this.commandService.exists(activityType);
3739
+ console.log('[AXCWorkflowExecutionService] 🔍 Command exists?', commandExists, activityType);
3740
+ if (commandExists) {
3741
+ console.log(`[AXCWorkflowExecutionService] ⚡ Executing '${activityType}' via CommandBus`);
3742
+ try {
3743
+ // Execute via CommandBus
3744
+ const result = await this.commandService.execute(activityType, properties);
3745
+ const finalResult = {
3746
+ output: result?.data?.output || result,
3747
+ outcomes: result?.data?.outcomes || { Done: true }
3894
3748
  };
3749
+ console.log('[AXCWorkflowExecutionService] ✅ Final result', finalResult);
3750
+ return finalResult;
3895
3751
  }
3896
- // Handle single update
3897
- if (id) {
3898
- const updated = await dataAccessor.update(id, updateData);
3752
+ catch (error) {
3753
+ console.error(`[AXCWorkflowExecutionService] ❌ Backend activity '${activityType}' failed:`, error);
3899
3754
  return {
3900
- success: true,
3901
- data: updated,
3902
- message: {
3903
- text: 'Entity status updated successfully',
3904
- },
3755
+ output: { error: error.message || 'Unknown error' },
3756
+ outcomes: { Failed: true }
3905
3757
  };
3906
3758
  }
3907
- throw new Error('Invalid input: neither id nor ids provided');
3908
3759
  }
3909
- catch (error) {
3760
+ // Fallback: Handle built-in activities that aren't Commands yet
3761
+ console.warn(`[WorkflowExecutionService] Activity '${activityType}' not registered as Command, using fallback`);
3762
+ switch (activityType) {
3763
+ case 'workflow-activity:set-variable':
3764
+ return this.executeSetVariable({ type: activityType, properties }, input);
3765
+ case 'workflow-activity:http-request':
3766
+ return this.executeHttpRequest({ type: activityType, properties }, input);
3767
+ default:
3768
+ return { output: null, outcomes: { Done: true } };
3769
+ }
3770
+ }
3771
+ /**
3772
+ * Set variable activity - stores value in workflow state.
3773
+ */
3774
+ async executeSetVariable(activity, input) {
3775
+ const variableName = activity.properties?.variableName;
3776
+ const value = activity.properties?.value;
3777
+ return {
3778
+ output: { [variableName]: value },
3779
+ outcomes: { Done: true }
3780
+ };
3781
+ }
3782
+ /**
3783
+ * Execute HTTP request - makes real HTTP call.
3784
+ * Supports different HTTP methods and handles timeouts.
3785
+ */
3786
+ async executeHttpRequest(activity, input) {
3787
+ const url = activity.properties?.url;
3788
+ const method = activity.properties?.method || 'GET';
3789
+ const timeout = activity.properties?.timeout || 30000;
3790
+ const headers = activity.properties?.headers || {};
3791
+ const body = activity.properties?.body;
3792
+ try {
3793
+ const controller = new AbortController();
3794
+ const timeoutId = setTimeout(() => controller.abort(), timeout);
3795
+ const fetchOptions = {
3796
+ method,
3797
+ signal: controller.signal,
3798
+ headers: {
3799
+ 'Content-Type': 'application/json',
3800
+ ...headers
3801
+ }
3802
+ };
3803
+ if (body && method !== 'GET' && method !== 'HEAD') {
3804
+ fetchOptions.body = typeof body === 'string' ? body : JSON.stringify(body);
3805
+ }
3806
+ const response = await fetch(url, fetchOptions);
3807
+ clearTimeout(timeoutId);
3808
+ const statusCode = response.status.toString();
3809
+ let data;
3810
+ try {
3811
+ data = await response.json();
3812
+ }
3813
+ catch {
3814
+ data = await response.text();
3815
+ }
3816
+ // Convert headers to object
3817
+ const responseHeaders = {};
3818
+ response.headers.forEach((value, key) => {
3819
+ responseHeaders[key] = value;
3820
+ });
3910
3821
  return {
3911
- success: false,
3912
- message: {
3913
- text: error instanceof Error ? error.message : 'Failed to update entity status',
3822
+ output: {
3823
+ statusCode,
3824
+ data,
3825
+ status: response.status,
3826
+ headers: responseHeaders
3914
3827
  },
3828
+ outcomes: {
3829
+ [statusCode]: true,
3830
+ Done: true
3831
+ }
3832
+ };
3833
+ }
3834
+ catch (error) {
3835
+ if (error.name === 'AbortError') {
3836
+ return {
3837
+ output: { error: 'Request timeout' },
3838
+ outcomes: { Timeout: true }
3839
+ };
3840
+ }
3841
+ return {
3842
+ output: { error: error.message },
3843
+ outcomes: { Failed: true }
3915
3844
  };
3916
3845
  }
3917
3846
  }
3918
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCEntityUpdateStatusCommand, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3919
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCEntityUpdateStatusCommand, providedIn: 'root' }); }
3920
- }
3921
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCEntityUpdateStatusCommand, decorators: [{
3922
- type: Injectable,
3923
- args: [{ providedIn: 'root' }]
3924
- }] });
3925
-
3926
- class AXCCommonMockModule {
3927
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCCommonMockModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
3928
- static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: AXCCommonMockModule }); }
3929
- static { this.ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCCommonMockModule, providers: [
3930
- {
3931
- provide: AXP_ACTIVITY_LOG_PROVIDER,
3932
- useClass: AXCPActivityLogService,
3933
- multi: true,
3934
- },
3935
- {
3936
- provide: AXP_SEARCH_PROVIDER,
3937
- useClass: EntitySearchProvider,
3938
- multi: true,
3939
- },
3940
- {
3941
- provide: AXPLockService,
3942
- useClass: AXCLockService,
3943
- },
3944
- {
3945
- provide: AXPFileStorageService,
3946
- useClass: AXCFileStorageService,
3947
- },
3948
- {
3949
- provide: AXPVersioningService,
3950
- useClass: AXCVersioningService,
3951
- },
3952
- // {
3953
- // provide: AXP_ACTIVITY_PROVIDER,
3954
- // useClass: AXCActivityProvider,
3955
- // multi: true,
3956
- // },
3957
- {
3958
- provide: AXPWorkflowExecutionService,
3959
- useClass: AXCWorkflowExecutionService,
3960
- },
3961
- {
3962
- provide: AXP_DATA_SEEDER_TOKEN,
3963
- useClass: AXCWorkflowDefinitionDataSeeder$1,
3964
- multi: true,
3965
- },
3966
- // Register generic Entity:UpdateStatus command
3967
- provideCommandSetups([
3968
- { key: 'Entity:UpdateStatus', command: () => AXCEntityUpdateStatusCommand },
3969
- ]),
3970
- AXCEntityUpdateStatusCommand,
3971
- ] }); }
3972
- }
3973
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCCommonMockModule, decorators: [{
3974
- type: NgModule,
3975
- args: [{
3976
- imports: [],
3977
- exports: [],
3978
- declarations: [],
3979
- providers: [
3980
- {
3981
- provide: AXP_ACTIVITY_LOG_PROVIDER,
3982
- useClass: AXCPActivityLogService,
3983
- multi: true,
3984
- },
3985
- {
3986
- provide: AXP_SEARCH_PROVIDER,
3987
- useClass: EntitySearchProvider,
3988
- multi: true,
3989
- },
3990
- {
3991
- provide: AXPLockService,
3992
- useClass: AXCLockService,
3993
- },
3994
- {
3995
- provide: AXPFileStorageService,
3996
- useClass: AXCFileStorageService,
3997
- },
3998
- {
3999
- provide: AXPVersioningService,
4000
- useClass: AXCVersioningService,
4001
- },
4002
- // {
4003
- // provide: AXP_ACTIVITY_PROVIDER,
4004
- // useClass: AXCActivityProvider,
4005
- // multi: true,
4006
- // },
4007
- {
4008
- provide: AXPWorkflowExecutionService,
4009
- useClass: AXCWorkflowExecutionService,
4010
- },
4011
- {
4012
- provide: AXP_DATA_SEEDER_TOKEN,
4013
- useClass: AXCWorkflowDefinitionDataSeeder$1,
4014
- multi: true,
4015
- },
4016
- // Register generic Entity:UpdateStatus command
4017
- provideCommandSetups([
4018
- { key: 'Entity:UpdateStatus', command: () => AXCEntityUpdateStatusCommand },
4019
- ]),
4020
- AXCEntityUpdateStatusCommand,
4021
- ],
4022
- }]
4023
- }] });
4024
-
4025
- //#region ---- Imports ----
4026
- function toUiRows(changes) {
4027
- return changes.map((c) => ({
4028
- badge: toBadge(c.op),
4029
- path: c.path,
4030
- summary: toSummary(c),
4031
- oldValue: c.oldValue,
4032
- newValue: c.newValue,
4033
- }));
4034
- }
4035
- function toBadge(op) {
4036
- switch (op) {
4037
- case 'add':
4038
- return 'Added';
4039
- case 'remove':
4040
- return 'Removed';
4041
- case 'replace':
4042
- return 'Replaced';
4043
- case 'rename':
4044
- return 'Renamed';
4045
- case 'metadata':
4046
- return 'Metadata';
4047
- default:
4048
- return 'Updated';
3847
+ //#endregion
3848
+ //#region ---- Instance Management ----
3849
+ /**
3850
+ * Convert workflow instance to instance state.
3851
+ */
3852
+ instanceToInstanceState(instance) {
3853
+ const state = instance.workflowState;
3854
+ // Find current activity from execution contexts
3855
+ const currentContext = state.activityExecutionContexts.find(ctx => ctx.status === 'Running' && !ctx.completedAt);
3856
+ // Map status
3857
+ let status = 'running';
3858
+ if (instance.status === 'Finished') {
3859
+ if (instance.subStatus === 'Faulted') {
3860
+ status = 'error';
3861
+ }
3862
+ else {
3863
+ status = 'completed';
3864
+ }
3865
+ }
3866
+ else if (instance.subStatus === 'Suspended') {
3867
+ status = 'suspended';
3868
+ }
3869
+ return {
3870
+ instanceId: instance.id,
3871
+ workflowId: instance.definitionId,
3872
+ status,
3873
+ currentStepId: currentContext?.scheduledActivityNodeId,
3874
+ variables: state.properties || {},
3875
+ input: state.input || {},
3876
+ output: state.output || {},
3877
+ lastUpdated: new Date(instance.updatedAt)
3878
+ };
4049
3879
  }
4050
- }
4051
- function toSummary(c) {
4052
- switch (c.op) {
4053
- case 'add':
4054
- return 'Added';
4055
- case 'remove':
4056
- return 'Removed';
4057
- case 'replace':
4058
- return 'File replaced';
4059
- case 'rename':
4060
- return 'File renamed';
4061
- case 'metadata':
4062
- return 'File metadata updated';
4063
- default:
4064
- return 'Updated';
3880
+ /**
3881
+ * Update instance state in instance and save to IndexedDB.
3882
+ */
3883
+ async updateInstanceFromState(instanceId, instanceState) {
3884
+ const instance = await this.getInstance(instanceId);
3885
+ if (!instance) {
3886
+ throw new Error(`Workflow instance not found: ${instanceId}`);
3887
+ }
3888
+ // Update instance from state
3889
+ const now = new Date().toISOString();
3890
+ // Map state status to instance status
3891
+ let status = 'Running';
3892
+ let subStatus = 'Executing';
3893
+ console.log('[AXCWorkflowRuntimeService] 🔄 updateInstanceFromState', {
3894
+ instanceStateStatus: instanceState.status,
3895
+ instanceId: instance.id,
3896
+ currentInstanceStatus: instance.status
3897
+ });
3898
+ if (instanceState.status === 'completed') {
3899
+ console.log('[AXCWorkflowRuntimeService] ✅ Marking workflow as Finished');
3900
+ status = 'Finished';
3901
+ subStatus = 'Finished';
3902
+ instance.finishedAt = now;
3903
+ }
3904
+ else if (instanceState.status === 'error') {
3905
+ status = 'Finished';
3906
+ subStatus = 'Faulted';
3907
+ instance.finishedAt = now;
3908
+ }
3909
+ else if (instanceState.status === 'suspended') {
3910
+ subStatus = 'Suspended';
3911
+ }
3912
+ instance.status = status;
3913
+ instance.subStatus = subStatus;
3914
+ instance.updatedAt = now;
3915
+ // Update workflow state
3916
+ instance.workflowState.status = status;
3917
+ instance.workflowState.subStatus = subStatus;
3918
+ instance.workflowState.input = instanceState.input || {};
3919
+ instance.workflowState.output = instanceState.output || {};
3920
+ instance.workflowState.properties = instanceState.variables || {};
3921
+ instance.workflowState.updatedAt = now;
3922
+ if (instanceState.status === 'completed' || instanceState.status === 'error') {
3923
+ instance.workflowState.finishedAt = now;
3924
+ }
3925
+ // Update activity execution context if currentStepId changed
3926
+ if (instanceState.currentStepId) {
3927
+ const existingContext = instance.workflowState.activityExecutionContexts.find(ctx => ctx.scheduledActivityNodeId === instanceState.currentStepId);
3928
+ if (!existingContext) {
3929
+ // Add new context
3930
+ instance.workflowState.activityExecutionContexts.push({
3931
+ id: `ctx-${instanceState.currentStepId}`,
3932
+ parentContextId: null,
3933
+ scheduledActivityNodeId: instanceState.currentStepId,
3934
+ ownerActivityNodeId: null,
3935
+ properties: {},
3936
+ activityState: null,
3937
+ dynamicVariables: [],
3938
+ status: 'Running',
3939
+ isExecuting: false,
3940
+ faultCount: 0,
3941
+ startedAt: now,
3942
+ completedAt: null,
3943
+ tag: null
3944
+ });
3945
+ }
3946
+ }
3947
+ // Save to IndexedDB
3948
+ await this.updateInstance(instance);
3949
+ // 🎯 Also update in entity service
3950
+ await this.updateInstanceInEntityService(instance);
3951
+ return instance;
3952
+ }
3953
+ /**
3954
+ * Get workflow instance from IndexedDB.
3955
+ */
3956
+ async getInstance(instanceId) {
3957
+ return await this.db.instances.get(instanceId) || null;
3958
+ }
3959
+ /**
3960
+ * Update workflow instance in IndexedDB.
3961
+ */
3962
+ async updateInstance(instance) {
3963
+ instance.updatedAt = new Date().toISOString();
3964
+ await this.db.instances.update(instance.id, instance);
4065
3965
  }
3966
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowRuntimeService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
3967
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowRuntimeService }); }
4066
3968
  }
4067
- //#endregion
3969
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowRuntimeService, decorators: [{
3970
+ type: Injectable
3971
+ }] });
4068
3972
 
4069
3973
  //#region ---- Imports ----
4070
3974
  //#endregion
@@ -10219,6 +10123,198 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
10219
10123
  }]
10220
10124
  }] });
10221
10125
 
10126
+ class AXCDistributionInteractionMockService extends AXMDistributionInteractionServiceImpl {
10127
+ constructor() {
10128
+ super(...arguments);
10129
+ //#region ---- Services & Dependencies ----
10130
+ this.usersService = inject(AXMUsersEntityService);
10131
+ this.sessionService = inject(AXPSessionService);
10132
+ }
10133
+ //#endregion
10134
+ async query(request) {
10135
+ // Raw mode: return ungrouped interaction items (used for bulk aggregation)
10136
+ if (request?.params?.raw === true) {
10137
+ const rawRes = await super.query({
10138
+ filter: request.filter,
10139
+ sort: request.sort,
10140
+ params: request.params,
10141
+ take: 100000,
10142
+ skip: 0,
10143
+ });
10144
+ const total = rawRes.items.length;
10145
+ const skip = request?.skip ?? 0;
10146
+ const take = request?.take ?? total;
10147
+ const pagedItems = rawRes.items.slice(skip, skip + take);
10148
+ return {
10149
+ items: pagedItems,
10150
+ total,
10151
+ };
10152
+ }
10153
+ const items = (await super.query({
10154
+ filter: request.filter,
10155
+ sort: request.sort,
10156
+ params: request.params,
10157
+ take: 100000,
10158
+ skip: 0,
10159
+ })).items;
10160
+ // Extract documentDistributionId from request filter
10161
+ let documentDistributionId;
10162
+ if (request.filter && typeof request.filter === 'object') {
10163
+ if (request.filter.filters && request.filter.filters.length > 0 && request.filter.filters[0].field === 'documentDistributionId') {
10164
+ documentDistributionId = request.filter.filters[0].value;
10165
+ }
10166
+ }
10167
+ // Get the distribution record to access assignedTo property using storage service to avoid circular calls
10168
+ if (documentDistributionId) {
10169
+ try {
10170
+ // Use storage service directly to get distribution record
10171
+ const distribution = await this.storageService.getOne('DocumentManagement.Distribution', documentDistributionId);
10172
+ if (distribution && distribution.assignedTo && Array.isArray(distribution.assignedTo)) {
10173
+ // Get all assigned user IDs
10174
+ const assignedUserIds = distribution.assignedTo.map((user) => user.id || user);
10175
+ // Get existing interaction user IDs (from created.user.id)
10176
+ const existingUserIds = new Set(items
10177
+ .map((item) => item?.created?.user?.id)
10178
+ .filter((id) => !!id));
10179
+ // Find users who are assigned but don't have interaction records
10180
+ const missingUserIds = assignedUserIds.filter((userId) => !existingUserIds.has(userId));
10181
+ // Create missing interaction records
10182
+ for (const userId of missingUserIds) {
10183
+ let fullName = 'Unknown';
10184
+ try {
10185
+ const userRes = await this.usersService.query({
10186
+ skip: 0,
10187
+ take: 1,
10188
+ filter: { field: 'id', operator: { type: 'equal' }, value: userId },
10189
+ });
10190
+ const u = userRes.items?.[0];
10191
+ if (u) {
10192
+ const candidate = `${u.firstName ?? ''} ${u.lastName ?? ''}`.trim();
10193
+ fullName = candidate || u.username || u.title || u.name || fullName;
10194
+ }
10195
+ }
10196
+ catch { }
10197
+ const missingRecord = {
10198
+ id: `missing-${documentDistributionId}-${userId}`,
10199
+ documentDistributionId: documentDistributionId,
10200
+ auditInfo: {
10201
+ created: { at: new Date(), by: { id: userId, type: 'user', fullName } },
10202
+ updated: { at: new Date(), by: { id: userId, type: 'user', fullName } },
10203
+ },
10204
+ action: 'Fake', // Default action, but with false flags
10205
+ };
10206
+ // Add the missing record to the items array
10207
+ items.push(missingRecord);
10208
+ }
10209
+ }
10210
+ }
10211
+ catch (error) {
10212
+ }
10213
+ }
10214
+ // Group items by creator user id
10215
+ const groupedItems = this.groupItemsByCreatedBy(items);
10216
+ // Collect distinct user IDs from grouped items
10217
+ const userIds = Array.from(new Set(groupedItems.map((g) => g.createdBy).filter((id) => !!id)));
10218
+ // Fetch users by IDs (in operator)
10219
+ let usersMap = new Map();
10220
+ if (userIds.length > 0) {
10221
+ try {
10222
+ const usersRes = await this.usersService.query({
10223
+ skip: 0,
10224
+ take: userIds.length,
10225
+ filter: {
10226
+ field: 'id',
10227
+ operator: { type: 'in' },
10228
+ value: userIds,
10229
+ },
10230
+ });
10231
+ usersMap = new Map(usersRes.items.map((u) => [
10232
+ u.id,
10233
+ {
10234
+ id: u.id,
10235
+ name: u.username,
10236
+ title: `${u.firstName} ${u.lastName}`,
10237
+ },
10238
+ ]));
10239
+ }
10240
+ catch {
10241
+ // If fetching users fails, continue without enrichment
10242
+ usersMap = new Map();
10243
+ }
10244
+ }
10245
+ // Convert grouped items back to the expected format with user enrichment
10246
+ const enrichedItems = groupedItems.map((summary) => ({
10247
+ id: summary.id,
10248
+ createdBy: summary.createdBy,
10249
+ hasViewed: summary.hasViewed,
10250
+ lastViewDate: summary.lastViewDate,
10251
+ hasSigned: summary.hasSigned,
10252
+ lastSignDate: summary.lastSignDate,
10253
+ totalInteractions: summary.totalInteractions,
10254
+ user: usersMap.get(summary.createdBy) ?? {
10255
+ id: this.sessionService?.user?.id ?? '',
10256
+ name: this.sessionService?.user?.name ?? '',
10257
+ title: this.sessionService?.user?.title ?? '',
10258
+ },
10259
+ }));
10260
+ // Apply pagination using request.skip and request.take
10261
+ const total = enrichedItems.length;
10262
+ const skip = request?.skip ?? 0;
10263
+ const take = request?.take ?? total;
10264
+ const pagedItems = enrichedItems.slice(skip, skip + take);
10265
+ return {
10266
+ items: pagedItems,
10267
+ total,
10268
+ };
10269
+ }
10270
+ /**
10271
+ * Groups distribution interaction items by createdBy and creates a summary
10272
+ */
10273
+ groupItemsByCreatedBy(items) {
10274
+ console.log(items);
10275
+ const groupedMap = new Map();
10276
+ for (const item of items) {
10277
+ const createdBy = item?.created?.user?.id || 'unknown';
10278
+ if (!groupedMap.has(createdBy)) {
10279
+ // Initialize summary for new createdBy
10280
+ groupedMap.set(createdBy, {
10281
+ id: createdBy, // Use createdBy as the ID for the summary
10282
+ createdBy: createdBy,
10283
+ hasViewed: false,
10284
+ lastViewDate: undefined,
10285
+ hasSigned: false,
10286
+ lastSignDate: undefined,
10287
+ totalInteractions: 0,
10288
+ });
10289
+ }
10290
+ const summary = groupedMap.get(createdBy);
10291
+ summary.totalInteractions++;
10292
+ // Check for view actions
10293
+ if (item.action === 'Viewed') {
10294
+ summary.hasViewed = true;
10295
+ // Update last view date if this is more recent
10296
+ if (!summary.lastViewDate || item.actionTime && item.actionTime > summary.lastViewDate) {
10297
+ summary.lastViewDate = item.actionTime;
10298
+ }
10299
+ }
10300
+ // Check for sign actions
10301
+ if (item.action === 'Signed') {
10302
+ summary.hasSigned = true;
10303
+ // Update last sign date if this is more recent
10304
+ if (!summary.lastSignDate || item.actionTime && item.actionTime > summary.lastSignDate) {
10305
+ summary.lastSignDate = item.actionTime;
10306
+ }
10307
+ }
10308
+ }
10309
+ return Array.from(groupedMap.values());
10310
+ }
10311
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDistributionInteractionMockService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
10312
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDistributionInteractionMockService }); }
10313
+ }
10314
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDistributionInteractionMockService, decorators: [{
10315
+ type: Injectable
10316
+ }] });
10317
+
10222
10318
  class AXCDistributionMockService extends AXMDistributionServiceImpl {
10223
10319
  constructor() {
10224
10320
  super(...arguments);
@@ -10328,153 +10424,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
10328
10424
  type: Injectable
10329
10425
  }] });
10330
10426
 
10331
- const DOCUMENT_TYPES = [
10332
- {
10333
- id: AXPDataGenerator.uuid(),
10334
- name: 'risk-assessment-report',
10335
- title: 'Risk Assessment Report',
10336
- maxSize: 5000,
10337
- isEncrypted: false,
10338
- enableVersioning: true,
10339
- enableExpiration: false,
10340
- requiresReview: true,
10341
- requiresDistribution: false,
10342
- metaDataList: [
10343
- {
10344
- id: AXPDataGenerator.uuid(),
10345
- text: 'Description',
10346
- // "parentId": "9e3c7586-4722-4170-bc55-e612ca2ab4b5",
10347
- hasCheckbox: true,
10348
- isSelected: true,
10349
- interface: '{"type":"text-editor","name":"description-editor1","meta":{"header":true},"options":{"multiple":false,"disabled":false,"hasClearButton":false},"defaultValue":"","path":"description"}',
10350
- name: 'description',
10351
- title: 'Description',
10352
- isRequired: true,
10353
- placeholder: '',
10354
- status: 'active',
10355
- },
10356
- ],
10357
- type: {
10358
- selectedItems: [
10359
- {
10360
- name: 'image.jpg',
10361
- title: 'Image/JPEG',
10362
- icon: 'fa-light fa-image ax-text-purple-500',
10363
- },
10364
- {
10365
- name: 'image.png',
10366
- title: 'Image/PNG',
10367
- icon: 'fa-light fa-image ax-text-purple-500',
10368
- },
10369
- {
10370
- name: 'image.gif',
10371
- title: 'Image/GIF',
10372
- icon: 'fa-light fa-image ax-text-purple-500',
10373
- },
10374
- ],
10375
- context: {
10376
- image: {
10377
- width: '22',
10378
- height: '33',
10379
- },
10380
- gift: {
10381
- duration: '11',
10382
- },
10383
- },
10384
- },
10385
- },
10386
- {
10387
- id: AXPDataGenerator.uuid(),
10388
- name: 'any',
10389
- title: 'Upload File',
10390
- maxSize: 50000,
10391
- isEncrypted: false,
10392
- enableVersioning: false,
10393
- enableExpiration: false,
10394
- requiresReview: false,
10395
- requiresDistribution: false,
10396
- control: { isSystem: true },
10397
- metaDataList: [],
10398
- type: {
10399
- selectedItems: [],
10400
- context: {
10401
- audio: {
10402
- bitrate: '320',
10403
- duration: '180',
10404
- },
10405
- },
10406
- },
10407
- },
10408
- {
10409
- id: AXPDataGenerator.uuid(),
10410
- name: 'file-meeting',
10411
- title: 'File Meeting',
10412
- maxSize: 50000,
10413
- isEncrypted: false,
10414
- enableVersioning: true,
10415
- enableExpiration: true,
10416
- requiresReview: true,
10417
- requiresDistribution: true,
10418
- metaDataList: [
10419
- {
10420
- id: AXPDataGenerator.uuid(),
10421
- text: 'Description',
10422
- // "parentId": "9e3c7586-4722-4170-bc55-e612ca2ab4b5",
10423
- hasCheckbox: true,
10424
- isSelected: true,
10425
- interface: '{"type":"text-editor","name":"description-editor1","meta":{"header":true},"options":{"multiple":false,"disabled":false,"hasClearButton":false},"defaultValue":"","path":"description"}',
10426
- name: 'description',
10427
- title: 'Description',
10428
- isRequired: true,
10429
- placeholder: '',
10430
- status: 'active',
10431
- },
10432
- ],
10433
- type: {
10434
- selectedItems: [
10435
- {
10436
- name: 'image.jpg',
10437
- title: 'Image/JPEG',
10438
- icon: 'fa-light fa-image ax-text-purple-500',
10439
- },
10440
- {
10441
- name: 'image.png',
10442
- title: 'Image/PNG',
10443
- icon: 'fa-light fa-image ax-text-purple-500',
10444
- },
10445
- {
10446
- name: 'image.gif',
10447
- title: 'Image/GIF',
10448
- icon: 'fa-light fa-image ax-text-purple-500',
10449
- },
10450
- ],
10451
- context: {
10452
- image: {
10453
- width: '22',
10454
- height: '33',
10455
- },
10456
- gift: {
10457
- duration: '11',
10458
- },
10459
- },
10460
- },
10461
- },
10462
- ];
10463
-
10464
- class AXCDocumentTypeDataSeeder {
10465
- constructor() {
10466
- this.storageService = inject(AXPEntityStorageService);
10467
- }
10468
- async seed() {
10469
- await this.storageService.initial(`${RootConfig$6.module.name}.${RootConfig$6.entities.documentType.name}`, DOCUMENT_TYPES);
10470
- }
10471
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentTypeDataSeeder, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
10472
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentTypeDataSeeder }); }
10473
- }
10474
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentTypeDataSeeder, decorators: [{
10475
- type: Injectable
10476
- }] });
10477
-
10478
10427
  class AXCFolderMockService extends AXMFolderServiceImpl {
10479
10428
  async updateParentFolders(folder) {
10480
10429
  if (folder.parentId) {
@@ -10535,24 +10484,6 @@ class AXCFolderMockService extends AXMFolderServiceImpl {
10535
10484
  await super.deleteOne(id);
10536
10485
  }
10537
10486
  }
10538
- async getPath(folderId, stopFolderId) {
10539
- const path = [];
10540
- let currentId = folderId;
10541
- while (currentId) {
10542
- const folder = await super.getOne(currentId);
10543
- if (!folder) {
10544
- // Folder not found in persisted storage
10545
- break;
10546
- }
10547
- path.push(folder);
10548
- if (currentId == stopFolderId) {
10549
- // Stop if we reach the specified stop folder ID
10550
- break;
10551
- }
10552
- currentId = folder.parentId ?? null;
10553
- }
10554
- return path.reverse();
10555
- }
10556
10487
  /**
10557
10488
  * Finds the first non-inheriting folder in the hierarchy
10558
10489
  * @param folderId ID of the starting folder
@@ -10645,46 +10576,6 @@ class AXCFolderMockService extends AXMFolderServiceImpl {
10645
10576
  }
10646
10577
  return folder;
10647
10578
  }
10648
- /**
10649
- * Search folders with specified name using recursive method
10650
- * @param searchTerm search term
10651
- * @param rootFolderId ID of the root folder to start the search
10652
- */
10653
- async searchFolders(searchTerm, rootFolderId) {
10654
- const result = [];
10655
- await this.searchFoldersRecursive(searchTerm, rootFolderId, result);
10656
- return result;
10657
- }
10658
- /**
10659
- * Recursive folder search
10660
- * @param searchTerm search term
10661
- * @param folderId current folder ID
10662
- * @param result results array
10663
- */
10664
- async searchFoldersRecursive(searchTerm, folderId, result) {
10665
- try {
10666
- // Get current folder
10667
- const currentFolder = await this.getOne(folderId);
10668
- if (!currentFolder) {
10669
- return;
10670
- }
10671
- // Check subfolders
10672
- const subFolders = currentFolder.folders || [];
10673
- // Add folders that match the search term
10674
- for (const folder of subFolders) {
10675
- if (folder.name.toLowerCase().includes(searchTerm.toLowerCase())) {
10676
- result.push(folder);
10677
- }
10678
- // Recursive search in subfolders
10679
- if (folder.id) {
10680
- await this.searchFoldersRecursive(searchTerm, folder.id, result);
10681
- }
10682
- }
10683
- }
10684
- catch (error) {
10685
- console.error('Error searching folders:', error);
10686
- }
10687
- }
10688
10579
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFolderMockService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
10689
10580
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCFolderMockService }); }
10690
10581
  }
@@ -10751,12 +10642,78 @@ class AXMFileMockService extends AXMDocumentServiceImpl {
10751
10642
  await super.deleteOne(id);
10752
10643
  }
10753
10644
  }
10754
- async getPath(documentId, stop) {
10755
- const file = await this.getOne(documentId);
10645
+ async query(request) {
10646
+ const result = await super.query(request);
10647
+ // Use lazy injection to avoid circular dependency
10648
+ const reviewService = this.injector.get(AXMReviewService, null);
10649
+ if (reviewService) {
10650
+ // Process items sequentially to avoid performance issues
10651
+ for (const item of result.items) {
10652
+ try {
10653
+ if (item.documentType?.requiresReview) {
10654
+ const reviewResult = await reviewService.query({
10655
+ skip: 0,
10656
+ take: 1,
10657
+ filter: {
10658
+ logic: 'and',
10659
+ filters: [
10660
+ {
10661
+ field: 'documentId',
10662
+ operator: {
10663
+ type: 'equal',
10664
+ },
10665
+ value: item.id,
10666
+ },
10667
+ {
10668
+ field: 'version',
10669
+ operator: {
10670
+ type: 'equal',
10671
+ },
10672
+ value: item.version,
10673
+ },
10674
+ ],
10675
+ },
10676
+ });
10677
+ item.documentReview = reviewResult.items[0];
10678
+ }
10679
+ }
10680
+ catch (error) {
10681
+ // Continue processing other items if one fails
10682
+ }
10683
+ }
10684
+ }
10685
+ // Map result items to fileData structure
10686
+ const mappedResult = {
10687
+ ...result,
10688
+ items: result.items.map((item) => ({
10689
+ ...item,
10690
+ // fileId: 'file:' + item.fileId,
10691
+ })),
10692
+ };
10693
+ return mappedResult;
10694
+ }
10695
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMFileMockService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
10696
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMFileMockService }); }
10697
+ }
10698
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMFileMockService, decorators: [{
10699
+ type: Injectable
10700
+ }], ctorParameters: () => [] });
10701
+
10702
+ class AXCDocumentManagementService {
10703
+ constructor() {
10704
+ // Create instances directly to avoid circular dependency
10705
+ // These services all use the same Dexie database, so they share data
10706
+ this.folderService = new AXCFolderMockService();
10707
+ this.documentService = new AXMFileMockService();
10708
+ this.documentTypeService = new AXMDocumentTypeServiceImpl();
10709
+ }
10710
+ //#region ---- Document Methods ----
10711
+ async getDocumentPath(documentId, stopFolderId) {
10712
+ const file = await this.documentService.getOne(documentId);
10756
10713
  if (!file) {
10757
10714
  throw new Error('File not found');
10758
10715
  }
10759
- return await this.folderService.getPath(file.parentId, stop);
10716
+ return await this.getFolderPath(file.parentId, stopFolderId);
10760
10717
  }
10761
10718
  async searchFiles(searchTerm, rootFolderId) {
10762
10719
  const result = [];
@@ -10764,10 +10721,10 @@ class AXMFileMockService extends AXMDocumentServiceImpl {
10764
10721
  return result;
10765
10722
  }
10766
10723
  /**
10767
- * جستجوی بازگشتی فایل‌ها
10768
- * @param searchTerm عبارت جستجو
10769
- * @param folderId شناسه فولدر فعلی
10770
- * @param result آرایه نتایج
10724
+ * Recursive file search
10725
+ * @param searchTerm search term
10726
+ * @param folderId current folder ID
10727
+ * @param result results array
10771
10728
  */
10772
10729
  async searchFilesRecursive(searchTerm, folderId, result) {
10773
10730
  try {
@@ -10792,63 +10749,247 @@ class AXMFileMockService extends AXMDocumentServiceImpl {
10792
10749
  console.error('Error searching files:', error);
10793
10750
  }
10794
10751
  }
10795
- async query(request) {
10796
- const result = await super.query(request);
10797
- // Use lazy injection to avoid circular dependency
10798
- const reviewService = this.injector.get(AXMReviewService, null);
10799
- if (reviewService) {
10800
- // Process items sequentially to avoid performance issues
10801
- for (const item of result.items) {
10802
- try {
10803
- if (item.documentType?.requiresReview) {
10804
- const reviewResult = await reviewService.query({
10805
- skip: 0,
10806
- take: 1,
10807
- filter: {
10808
- logic: 'and',
10809
- filters: [
10810
- {
10811
- field: 'documentId',
10812
- operator: {
10813
- type: 'equal',
10814
- },
10815
- value: item.id,
10816
- },
10817
- {
10818
- field: 'version',
10819
- operator: {
10820
- type: 'equal',
10821
- },
10822
- value: item.version,
10823
- },
10824
- ],
10825
- },
10826
- });
10827
- item.documentReview = reviewResult.items[0];
10828
- }
10752
+ async findManyDocuments(ids) {
10753
+ const resp = await this.documentService.query({
10754
+ skip: 0,
10755
+ take: ids.length || 1000,
10756
+ filter: {
10757
+ field: 'id',
10758
+ operator: { type: 'in' },
10759
+ value: ids,
10760
+ },
10761
+ });
10762
+ return (resp?.items ?? []);
10763
+ }
10764
+ //#endregion
10765
+ //#region ---- Folder Methods ----
10766
+ async getFolderPath(folderId, stopFolderId) {
10767
+ const path = [];
10768
+ let currentId = folderId;
10769
+ while (currentId) {
10770
+ const folder = await this.folderService.getOne(currentId);
10771
+ if (!folder) {
10772
+ // Folder not found in persisted storage
10773
+ break;
10774
+ }
10775
+ path.push(folder);
10776
+ if (currentId == stopFolderId) {
10777
+ // Stop if we reach the specified stop folder ID
10778
+ break;
10779
+ }
10780
+ currentId = folder.parentId ?? null;
10781
+ }
10782
+ return path.reverse();
10783
+ }
10784
+ async searchFolders(searchTerm, rootFolderId) {
10785
+ const result = [];
10786
+ await this.searchFoldersRecursive(searchTerm, rootFolderId, result);
10787
+ return result;
10788
+ }
10789
+ /**
10790
+ * Recursive folder search
10791
+ * @param searchTerm search term
10792
+ * @param folderId current folder ID
10793
+ * @param result results array
10794
+ */
10795
+ async searchFoldersRecursive(searchTerm, folderId, result) {
10796
+ try {
10797
+ // Get current folder
10798
+ const currentFolder = await this.folderService.getOne(folderId);
10799
+ if (!currentFolder) {
10800
+ return;
10801
+ }
10802
+ // Check subfolders
10803
+ const subFolders = currentFolder.folders || [];
10804
+ // Add folders that match the search term
10805
+ for (const folder of subFolders) {
10806
+ if (folder.name.toLowerCase().includes(searchTerm.toLowerCase())) {
10807
+ result.push(folder);
10829
10808
  }
10830
- catch (error) {
10831
- // Continue processing other items if one fails
10809
+ // Recursive search in subfolders
10810
+ if (folder.id) {
10811
+ await this.searchFoldersRecursive(searchTerm, folder.id, result);
10832
10812
  }
10833
10813
  }
10834
10814
  }
10835
- // Map result items to fileData structure
10836
- const mappedResult = {
10837
- ...result,
10838
- items: result.items.map((item) => ({
10839
- ...item,
10840
- // fileId: 'file:' + item.fileId,
10841
- })),
10842
- };
10843
- return mappedResult;
10815
+ catch (error) {
10816
+ console.error('Error searching folders:', error);
10817
+ }
10844
10818
  }
10845
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMFileMockService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
10846
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMFileMockService }); }
10819
+ //#endregion
10820
+ //#region ---- DocumentType Methods ----
10821
+ async getDocumentTypeByName(name) {
10822
+ return (await this.documentTypeService.query({
10823
+ skip: 0,
10824
+ take: 1,
10825
+ filter: {
10826
+ logic: 'and',
10827
+ filters: [
10828
+ {
10829
+ field: 'name',
10830
+ operator: {
10831
+ type: 'equal',
10832
+ },
10833
+ value: name,
10834
+ },
10835
+ ],
10836
+ },
10837
+ })).items[0];
10838
+ }
10839
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentManagementService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
10840
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentManagementService }); }
10847
10841
  }
10848
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXMFileMockService, decorators: [{
10842
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentManagementService, decorators: [{
10849
10843
  type: Injectable
10850
10844
  }], ctorParameters: () => [] });
10851
10845
 
10846
+ const DOCUMENT_TYPES = [
10847
+ {
10848
+ id: AXPDataGenerator.uuid(),
10849
+ name: 'risk-assessment-report',
10850
+ title: 'Risk Assessment Report',
10851
+ maxSize: 5000,
10852
+ isEncrypted: false,
10853
+ enableVersioning: true,
10854
+ enableExpiration: false,
10855
+ requiresReview: true,
10856
+ requiresDistribution: false,
10857
+ metaDataList: [
10858
+ {
10859
+ id: AXPDataGenerator.uuid(),
10860
+ text: 'Description',
10861
+ // "parentId": "9e3c7586-4722-4170-bc55-e612ca2ab4b5",
10862
+ hasCheckbox: true,
10863
+ isSelected: true,
10864
+ interface: '{"type":"text-editor","name":"description-editor1","meta":{"header":true},"options":{"multiple":false,"disabled":false,"hasClearButton":false},"defaultValue":"","path":"description"}',
10865
+ name: 'description',
10866
+ title: 'Description',
10867
+ isRequired: true,
10868
+ placeholder: '',
10869
+ status: 'active',
10870
+ },
10871
+ ],
10872
+ type: {
10873
+ selectedItems: [
10874
+ {
10875
+ name: 'image.jpg',
10876
+ title: 'Image/JPEG',
10877
+ icon: 'fa-light fa-image ax-text-purple-500',
10878
+ },
10879
+ {
10880
+ name: 'image.png',
10881
+ title: 'Image/PNG',
10882
+ icon: 'fa-light fa-image ax-text-purple-500',
10883
+ },
10884
+ {
10885
+ name: 'image.gif',
10886
+ title: 'Image/GIF',
10887
+ icon: 'fa-light fa-image ax-text-purple-500',
10888
+ },
10889
+ ],
10890
+ context: {
10891
+ image: {
10892
+ width: '22',
10893
+ height: '33',
10894
+ },
10895
+ gift: {
10896
+ duration: '11',
10897
+ },
10898
+ },
10899
+ },
10900
+ },
10901
+ {
10902
+ id: AXPDataGenerator.uuid(),
10903
+ name: 'any',
10904
+ title: 'Upload File',
10905
+ maxSize: 50000,
10906
+ isEncrypted: false,
10907
+ enableVersioning: false,
10908
+ enableExpiration: false,
10909
+ requiresReview: false,
10910
+ requiresDistribution: false,
10911
+ control: { isSystem: true },
10912
+ metaDataList: [],
10913
+ type: {
10914
+ selectedItems: [],
10915
+ context: {
10916
+ audio: {
10917
+ bitrate: '320',
10918
+ duration: '180',
10919
+ },
10920
+ },
10921
+ },
10922
+ },
10923
+ {
10924
+ id: AXPDataGenerator.uuid(),
10925
+ name: 'file-meeting',
10926
+ title: 'File Meeting',
10927
+ maxSize: 50000,
10928
+ isEncrypted: false,
10929
+ enableVersioning: true,
10930
+ enableExpiration: true,
10931
+ requiresReview: true,
10932
+ requiresDistribution: true,
10933
+ metaDataList: [
10934
+ {
10935
+ id: AXPDataGenerator.uuid(),
10936
+ text: 'Description',
10937
+ // "parentId": "9e3c7586-4722-4170-bc55-e612ca2ab4b5",
10938
+ hasCheckbox: true,
10939
+ isSelected: true,
10940
+ interface: '{"type":"text-editor","name":"description-editor1","meta":{"header":true},"options":{"multiple":false,"disabled":false,"hasClearButton":false},"defaultValue":"","path":"description"}',
10941
+ name: 'description',
10942
+ title: 'Description',
10943
+ isRequired: true,
10944
+ placeholder: '',
10945
+ status: 'active',
10946
+ },
10947
+ ],
10948
+ type: {
10949
+ selectedItems: [
10950
+ {
10951
+ name: 'image.jpg',
10952
+ title: 'Image/JPEG',
10953
+ icon: 'fa-light fa-image ax-text-purple-500',
10954
+ },
10955
+ {
10956
+ name: 'image.png',
10957
+ title: 'Image/PNG',
10958
+ icon: 'fa-light fa-image ax-text-purple-500',
10959
+ },
10960
+ {
10961
+ name: 'image.gif',
10962
+ title: 'Image/GIF',
10963
+ icon: 'fa-light fa-image ax-text-purple-500',
10964
+ },
10965
+ ],
10966
+ context: {
10967
+ image: {
10968
+ width: '22',
10969
+ height: '33',
10970
+ },
10971
+ gift: {
10972
+ duration: '11',
10973
+ },
10974
+ },
10975
+ },
10976
+ },
10977
+ ];
10978
+
10979
+ class AXCDocumentTypeDataSeeder {
10980
+ constructor() {
10981
+ this.storageService = inject(AXPEntityStorageService);
10982
+ }
10983
+ async seed() {
10984
+ await this.storageService.initial(`${RootConfig$6.module.name}.${RootConfig$6.entities.documentType.name}`, DOCUMENT_TYPES);
10985
+ }
10986
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentTypeDataSeeder, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
10987
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentTypeDataSeeder }); }
10988
+ }
10989
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentTypeDataSeeder, decorators: [{
10990
+ type: Injectable
10991
+ }] });
10992
+
10852
10993
  // Section: Constants and Initial Setup
10853
10994
  const checkbox = '{"type":"document-layout","children":[{"type":"page-layout","children":[{"type":"checkbox-editor","name":"checkbox-editor1","options":{"label":"i accept","readonly":false,"disabled":false},"defaultValue":false,"path":"accept-check-box"}],"name":"page-layout1"}],"name":"document"}';
10854
10995
  const allTypes = DOCUMENT_TYPES;
@@ -11438,198 +11579,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
11438
11579
  type: Injectable
11439
11580
  }] });
11440
11581
 
11441
- class AXCDistributionInteractionMockService extends AXMDistributionInteractionServiceImpl {
11442
- constructor() {
11443
- super(...arguments);
11444
- //#region ---- Services & Dependencies ----
11445
- this.usersService = inject(AXMUsersEntityService);
11446
- this.sessionService = inject(AXPSessionService);
11447
- }
11448
- //#endregion
11449
- async query(request) {
11450
- // Raw mode: return ungrouped interaction items (used for bulk aggregation)
11451
- if (request?.params?.raw === true) {
11452
- const rawRes = await super.query({
11453
- filter: request.filter,
11454
- sort: request.sort,
11455
- params: request.params,
11456
- take: 100000,
11457
- skip: 0,
11458
- });
11459
- const total = rawRes.items.length;
11460
- const skip = request?.skip ?? 0;
11461
- const take = request?.take ?? total;
11462
- const pagedItems = rawRes.items.slice(skip, skip + take);
11463
- return {
11464
- items: pagedItems,
11465
- total,
11466
- };
11467
- }
11468
- const items = (await super.query({
11469
- filter: request.filter,
11470
- sort: request.sort,
11471
- params: request.params,
11472
- take: 100000,
11473
- skip: 0,
11474
- })).items;
11475
- // Extract documentDistributionId from request filter
11476
- let documentDistributionId;
11477
- if (request.filter && typeof request.filter === 'object') {
11478
- if (request.filter.filters && request.filter.filters.length > 0 && request.filter.filters[0].field === 'documentDistributionId') {
11479
- documentDistributionId = request.filter.filters[0].value;
11480
- }
11481
- }
11482
- // Get the distribution record to access assignedTo property using storage service to avoid circular calls
11483
- if (documentDistributionId) {
11484
- try {
11485
- // Use storage service directly to get distribution record
11486
- const distribution = await this.storageService.getOne('DocumentManagement.Distribution', documentDistributionId);
11487
- if (distribution && distribution.assignedTo && Array.isArray(distribution.assignedTo)) {
11488
- // Get all assigned user IDs
11489
- const assignedUserIds = distribution.assignedTo.map((user) => user.id || user);
11490
- // Get existing interaction user IDs (from created.user.id)
11491
- const existingUserIds = new Set(items
11492
- .map((item) => item?.created?.user?.id)
11493
- .filter((id) => !!id));
11494
- // Find users who are assigned but don't have interaction records
11495
- const missingUserIds = assignedUserIds.filter((userId) => !existingUserIds.has(userId));
11496
- // Create missing interaction records
11497
- for (const userId of missingUserIds) {
11498
- let fullName = 'Unknown';
11499
- try {
11500
- const userRes = await this.usersService.query({
11501
- skip: 0,
11502
- take: 1,
11503
- filter: { field: 'id', operator: { type: 'equal' }, value: userId },
11504
- });
11505
- const u = userRes.items?.[0];
11506
- if (u) {
11507
- const candidate = `${u.firstName ?? ''} ${u.lastName ?? ''}`.trim();
11508
- fullName = candidate || u.username || u.title || u.name || fullName;
11509
- }
11510
- }
11511
- catch { }
11512
- const missingRecord = {
11513
- id: `missing-${documentDistributionId}-${userId}`,
11514
- documentDistributionId: documentDistributionId,
11515
- auditInfo: {
11516
- created: { at: new Date(), by: { id: userId, type: 'user', fullName } },
11517
- updated: { at: new Date(), by: { id: userId, type: 'user', fullName } },
11518
- },
11519
- action: 'Fake', // Default action, but with false flags
11520
- };
11521
- // Add the missing record to the items array
11522
- items.push(missingRecord);
11523
- }
11524
- }
11525
- }
11526
- catch (error) {
11527
- }
11528
- }
11529
- // Group items by creator user id
11530
- const groupedItems = this.groupItemsByCreatedBy(items);
11531
- // Collect distinct user IDs from grouped items
11532
- const userIds = Array.from(new Set(groupedItems.map((g) => g.createdBy).filter((id) => !!id)));
11533
- // Fetch users by IDs (in operator)
11534
- let usersMap = new Map();
11535
- if (userIds.length > 0) {
11536
- try {
11537
- const usersRes = await this.usersService.query({
11538
- skip: 0,
11539
- take: userIds.length,
11540
- filter: {
11541
- field: 'id',
11542
- operator: { type: 'in' },
11543
- value: userIds,
11544
- },
11545
- });
11546
- usersMap = new Map(usersRes.items.map((u) => [
11547
- u.id,
11548
- {
11549
- id: u.id,
11550
- name: u.username,
11551
- title: `${u.firstName} ${u.lastName}`,
11552
- },
11553
- ]));
11554
- }
11555
- catch {
11556
- // If fetching users fails, continue without enrichment
11557
- usersMap = new Map();
11558
- }
11559
- }
11560
- // Convert grouped items back to the expected format with user enrichment
11561
- const enrichedItems = groupedItems.map((summary) => ({
11562
- id: summary.id,
11563
- createdBy: summary.createdBy,
11564
- hasViewed: summary.hasViewed,
11565
- lastViewDate: summary.lastViewDate,
11566
- hasSigned: summary.hasSigned,
11567
- lastSignDate: summary.lastSignDate,
11568
- totalInteractions: summary.totalInteractions,
11569
- user: usersMap.get(summary.createdBy) ?? {
11570
- id: this.sessionService?.user?.id ?? '',
11571
- name: this.sessionService?.user?.name ?? '',
11572
- title: this.sessionService?.user?.title ?? '',
11573
- },
11574
- }));
11575
- // Apply pagination using request.skip and request.take
11576
- const total = enrichedItems.length;
11577
- const skip = request?.skip ?? 0;
11578
- const take = request?.take ?? total;
11579
- const pagedItems = enrichedItems.slice(skip, skip + take);
11580
- return {
11581
- items: pagedItems,
11582
- total,
11583
- };
11584
- }
11585
- /**
11586
- * Groups distribution interaction items by createdBy and creates a summary
11587
- */
11588
- groupItemsByCreatedBy(items) {
11589
- console.log(items);
11590
- const groupedMap = new Map();
11591
- for (const item of items) {
11592
- const createdBy = item?.created?.user?.id || 'unknown';
11593
- if (!groupedMap.has(createdBy)) {
11594
- // Initialize summary for new createdBy
11595
- groupedMap.set(createdBy, {
11596
- id: createdBy, // Use createdBy as the ID for the summary
11597
- createdBy: createdBy,
11598
- hasViewed: false,
11599
- lastViewDate: undefined,
11600
- hasSigned: false,
11601
- lastSignDate: undefined,
11602
- totalInteractions: 0,
11603
- });
11604
- }
11605
- const summary = groupedMap.get(createdBy);
11606
- summary.totalInteractions++;
11607
- // Check for view actions
11608
- if (item.action === 'Viewed') {
11609
- summary.hasViewed = true;
11610
- // Update last view date if this is more recent
11611
- if (!summary.lastViewDate || item.actionTime && item.actionTime > summary.lastViewDate) {
11612
- summary.lastViewDate = item.actionTime;
11613
- }
11614
- }
11615
- // Check for sign actions
11616
- if (item.action === 'Signed') {
11617
- summary.hasSigned = true;
11618
- // Update last sign date if this is more recent
11619
- if (!summary.lastSignDate || item.actionTime && item.actionTime > summary.lastSignDate) {
11620
- summary.lastSignDate = item.actionTime;
11621
- }
11622
- }
11623
- }
11624
- return Array.from(groupedMap.values());
11625
- }
11626
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDistributionInteractionMockService, deps: null, target: i0.ɵɵFactoryTarget.Injectable }); }
11627
- static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDistributionInteractionMockService }); }
11628
- }
11629
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDistributionInteractionMockService, decorators: [{
11630
- type: Injectable
11631
- }] });
11632
-
11633
11582
  class AXCDocumentManagementMockModule {
11634
11583
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentManagementMockModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
11635
11584
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: AXCDocumentManagementMockModule }); }
@@ -11640,6 +11589,10 @@ class AXCDocumentManagementMockModule {
11640
11589
  { provide: AXMDocumentService, useClass: AXMFileMockService },
11641
11590
  { provide: AXMDistributionService, useClass: AXCDistributionMockService },
11642
11591
  { provide: AXMReviewService, useClass: AXCReviewMockService },
11592
+ {
11593
+ provide: AXPDocumentManagementService,
11594
+ useClass: AXCDocumentManagementService,
11595
+ },
11643
11596
  { provide: AXMDistributionInteractionService, useClass: AXCDistributionInteractionMockService },
11644
11597
  ] }); }
11645
11598
  }
@@ -11656,6 +11609,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
11656
11609
  { provide: AXMDocumentService, useClass: AXMFileMockService },
11657
11610
  { provide: AXMDistributionService, useClass: AXCDistributionMockService },
11658
11611
  { provide: AXMReviewService, useClass: AXCReviewMockService },
11612
+ {
11613
+ provide: AXPDocumentManagementService,
11614
+ useClass: AXCDocumentManagementService,
11615
+ },
11659
11616
  { provide: AXMDistributionInteractionService, useClass: AXCDistributionInteractionMockService },
11660
11617
  ],
11661
11618
  }]
@@ -12521,7 +12478,7 @@ class AXMFormDataSourcesProvider {
12521
12478
  }
12522
12479
  }
12523
12480
 
12524
- function generateCategories$3() {
12481
+ function generateCategories$4() {
12525
12482
  const categories = [];
12526
12483
  const ids = new Map();
12527
12484
  // Root categories - reduced to 5 main areas
@@ -12667,7 +12624,7 @@ function generateCategories$3() {
12667
12624
  });
12668
12625
  return categories;
12669
12626
  }
12670
- const TEMPLATE_CATEGORIES = generateCategories$3();
12627
+ const TEMPLATE_CATEGORIES = generateCategories$4();
12671
12628
 
12672
12629
  class AXMFormTemplateCategoryDataSeeder {
12673
12630
  constructor() {
@@ -39109,7 +39066,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
39109
39066
  * - itemsCount: Direct reports in this category
39110
39067
  * - No need for boolean flags (hasChild, hasReport)
39111
39068
  */
39112
- function generateCategories$2() {
39069
+ function generateCategories$3() {
39113
39070
  const categories = [];
39114
39071
  // Root categories with child count metadata
39115
39072
  const rootCategories = [
@@ -39162,7 +39119,7 @@ function generateCategories$2() {
39162
39119
  });
39163
39120
  return categories;
39164
39121
  }
39165
- const PRODUCT_REPORT_CATEGORIES = generateCategories$2();
39122
+ const PRODUCT_REPORT_CATEGORIES = generateCategories$3();
39166
39123
  //#endregion
39167
39124
  //#region ---- Mock Parameter Groups ----
39168
39125
  const productCatalogParameterGroups = [
@@ -39852,6 +39809,8 @@ const APPLICATION_TREE = [
39852
39809
  'DocumentManagement',
39853
39810
  'AssessmentManagement',
39854
39811
  'SecurityManagement',
39812
+ 'CalendarManagement',
39813
+ 'TaskManagement',
39855
39814
  AXPCommonFeatureKeys.GlobalSearch,
39856
39815
  AXPHumanCapitalManagementFeatureKeys.EmployeeManagement,
39857
39816
  AXPHumanCapitalManagementFeatureKeys.LeaveManagement,
@@ -41117,7 +41076,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
41117
41076
  * - itemsCount: Direct reports in this category
41118
41077
  * - No need for boolean flags (hasChild, hasReport)
41119
41078
  */
41120
- function generateCategories$1() {
41079
+ function generateCategories$2() {
41121
41080
  const categories = [];
41122
41081
  // Root category: Platform Analytics
41123
41082
  const rootCategories = [
@@ -41141,7 +41100,7 @@ function generateCategories$1() {
41141
41100
  });
41142
41101
  return categories;
41143
41102
  }
41144
- const PLATFORM_ANALYTICS_REPORT_CATEGORIES = generateCategories$1();
41103
+ const PLATFORM_ANALYTICS_REPORT_CATEGORIES = generateCategories$2();
41145
41104
  //#endregion
41146
41105
  //#region ---- Mock Parameter Groups ----
41147
41106
  const platformAnalyticsParameterGroups = [
@@ -46333,6 +46292,14 @@ const createFileCastMiddleware = {
46333
46292
  return undefined;
46334
46293
  }
46335
46294
  })();
46295
+ const documentManagementService = (() => {
46296
+ try {
46297
+ return inject(AXPDocumentManagementService);
46298
+ }
46299
+ catch {
46300
+ return undefined;
46301
+ }
46302
+ })();
46336
46303
  const folderService = (() => {
46337
46304
  try {
46338
46305
  return inject(AXMFolderService);
@@ -46431,14 +46398,17 @@ const createFileCastMiddleware = {
46431
46398
  try {
46432
46399
  await fileStorageService.remove(fileId);
46433
46400
  }
46434
- catch (error) {
46435
- }
46401
+ catch (error) { }
46436
46402
  }
46437
46403
  // Return null to indicate this file should be removed from the list
46438
46404
  return null;
46439
46405
  }
46440
46406
  // Case 2: Reference-based file (handle document updates)
46441
- if (file.source?.kind === 'reference' && file.source.value && typeof file.source.value === 'object' && 'id' in file.source.value && 'type' in file.source.value) {
46407
+ if (file.source?.kind === 'reference' &&
46408
+ file.source.value &&
46409
+ typeof file.source.value === 'object' &&
46410
+ 'id' in file.source.value &&
46411
+ 'type' in file.source.value) {
46442
46412
  const reference = file.source.value;
46443
46413
  // Only handle document references
46444
46414
  if (reference.type === 'document' && documentService) {
@@ -46504,8 +46474,7 @@ const createFileCastMiddleware = {
46504
46474
  });
46505
46475
  return { id: docId, kind: 'document' };
46506
46476
  }
46507
- catch (error) {
46508
- }
46477
+ catch (error) { }
46509
46478
  }
46510
46479
  else {
46511
46480
  }
@@ -46553,8 +46522,7 @@ const createFileCastMiddleware = {
46553
46522
  });
46554
46523
  return { id: docId, kind: 'document' };
46555
46524
  }
46556
- catch (error) {
46557
- }
46525
+ catch (error) { }
46558
46526
  }
46559
46527
  else {
46560
46528
  }
@@ -46614,8 +46582,11 @@ const createFileCastMiddleware = {
46614
46582
  const values = Object.values(candidate.meta);
46615
46583
  if (!Array.isArray(values) || values.length === 0)
46616
46584
  return false;
46617
- return values.every((m) => m && typeof m === 'object' &&
46618
- typeof m.name === 'string' && typeof m.title === 'string' && typeof m.value === 'string');
46585
+ return values.every((m) => m &&
46586
+ typeof m === 'object' &&
46587
+ typeof m.name === 'string' &&
46588
+ typeof m.title === 'string' &&
46589
+ typeof m.value === 'string');
46619
46590
  };
46620
46591
  const ensureDocumentRecord = async (svc, payload) => {
46621
46592
  // Resolve or create folder hierarchy: <Entity>/<Record Title|Name>
@@ -46623,9 +46594,7 @@ const createFileCastMiddleware = {
46623
46594
  try {
46624
46595
  if (folderService) {
46625
46596
  const findFolderByName = async (name, parent) => {
46626
- const filters = [
46627
- { field: 'name', operator: { type: 'equal' }, value: name },
46628
- ];
46597
+ const filters = [{ field: 'name', operator: { type: 'equal' }, value: name }];
46629
46598
  if (parent) {
46630
46599
  filters.push({ field: 'parentId', operator: { type: 'equal' }, value: parent });
46631
46600
  }
@@ -46663,7 +46632,10 @@ const createFileCastMiddleware = {
46663
46632
  if (root?.id) {
46664
46633
  const entityFullName = ctx.entityName || 'Entity';
46665
46634
  const entityNameOnly = entityFullName.split('.').pop() || entityFullName;
46666
- const recordFolderName = (ctx?.data?.title || ctx?.data?.name || payload.name || 'Record').toString();
46635
+ const recordFolderName = (ctx?.data?.title ||
46636
+ ctx?.data?.name ||
46637
+ payload.name ||
46638
+ 'Record').toString();
46667
46639
  const entityFolderId = await ensureFolder(entityNameOnly, root.id);
46668
46640
  parentId = await ensureFolder(recordFolderName, entityFolderId);
46669
46641
  }
@@ -46824,8 +46796,8 @@ const createFileCastMiddleware = {
46824
46796
  const allDocIds = [...documentIdList, ...referenceDocIds];
46825
46797
  const [manyInfos, documents] = await Promise.all([
46826
46798
  fileIdList.length ? fileStorageService.findMany(fileIdList) : Promise.resolve([]),
46827
- allDocIds.length && documentService
46828
- ? documentService.findMany(allDocIds)
46799
+ allDocIds.length && documentManagementService
46800
+ ? documentManagementService.findManyDocuments(allDocIds)
46829
46801
  : Promise.resolve([]),
46830
46802
  ]);
46831
46803
  const infoMap = new Map(manyInfos.map((i) => [i.fileId, i]));
@@ -46934,8 +46906,8 @@ const createFileCastMiddleware = {
46934
46906
  else if (isDocumentId(value)) {
46935
46907
  const actualDocId = removeDocumentPrefix(value);
46936
46908
  try {
46937
- if (documentService) {
46938
- const docs = await documentService.findMany([actualDocId]);
46909
+ if (documentManagementService) {
46910
+ const docs = await documentManagementService.findManyDocuments([actualDocId]);
46939
46911
  const doc = docs?.[0];
46940
46912
  if (doc) {
46941
46913
  const listItem = {
@@ -46970,7 +46942,11 @@ const createFileCastMiddleware = {
46970
46942
  }
46971
46943
  }
46972
46944
  catch (error) {
46973
- console.error('[file-cast] ❌ Failed to get document info', { keyPath, documentId: actualDocId, error });
46945
+ console.error('[file-cast] ❌ Failed to get document info', {
46946
+ keyPath,
46947
+ documentId: actualDocId,
46948
+ error,
46949
+ });
46974
46950
  setValueByPath(resultData, keyPath, {
46975
46951
  id: actualDocId,
46976
46952
  name: 'Unknown Document',
@@ -46981,9 +46957,9 @@ const createFileCastMiddleware = {
46981
46957
  }
46982
46958
  else if (isReference(value)) {
46983
46959
  const reference = parseReference(value);
46984
- if (reference && reference.type === 'document' && documentService) {
46960
+ if (reference && reference.type === 'document' && documentManagementService) {
46985
46961
  try {
46986
- const docs = await documentService.findMany([reference.id]);
46962
+ const docs = await documentManagementService.findManyDocuments([reference.id]);
46987
46963
  const doc = docs?.[0];
46988
46964
  if (doc) {
46989
46965
  const listItem = {
@@ -47009,7 +46985,11 @@ const createFileCastMiddleware = {
47009
46985
  }
47010
46986
  }
47011
46987
  catch (error) {
47012
- console.error('[file-cast] ❌ Failed to get reference document info', { keyPath, reference, error });
46988
+ console.error('[file-cast] ❌ Failed to get reference document info', {
46989
+ keyPath,
46990
+ reference,
46991
+ error,
46992
+ });
47013
46993
  setValueByPath(resultData, keyPath, {
47014
46994
  id: reference.id,
47015
46995
  name: 'Unknown Document',
@@ -48839,7 +48819,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
48839
48819
  * - childrenCount: Direct child categories (for lazy loading)
48840
48820
  * - itemsCount: Direct activities in this category
48841
48821
  */
48842
- function generateCategories() {
48822
+ function generateCategories$1() {
48843
48823
  const categories = [];
48844
48824
  // Root categories
48845
48825
  const rootCategories = [
@@ -48941,7 +48921,7 @@ function generateCategories() {
48941
48921
  });
48942
48922
  return categories;
48943
48923
  }
48944
- const ACTIVITY_CATEGORIES = generateCategories();
48924
+ const ACTIVITY_CATEGORIES = generateCategories$1();
48945
48925
  //#endregion
48946
48926
 
48947
48927
  class AXCActivityCategorySeeder {
@@ -48972,6 +48952,7 @@ function generateActivityDefinitions() {
48972
48952
  const queryCategory = ACTIVITY_CATEGORIES.find(c => c.title === 'Query');
48973
48953
  // Command category activities
48974
48954
  if (commandCategory) {
48955
+ // Check Permission Activity
48975
48956
  definitions.push({
48976
48957
  name: 'check-permission',
48977
48958
  type: 'workflow-activity:check-permission',
@@ -48979,8 +48960,73 @@ function generateActivityDefinitions() {
48979
48960
  description: 'Checks if a user has a specific permission',
48980
48961
  category: commandCategory.id,
48981
48962
  executionMode: 'backend',
48963
+ icon: 'fa-shield-alt',
48982
48964
  outcomes: ['HasPermission', 'NoPermission'],
48965
+ inputs: [
48966
+ {
48967
+ name: 'permission',
48968
+ title: 'Permission Key',
48969
+ description: 'Permission key to check (e.g., "Users.Create")',
48970
+ schema: {
48971
+ dataType: 'string',
48972
+ nullable: false,
48973
+ interface: {
48974
+ type: AXPWidgetsList.Editors.TextBox,
48975
+ options: {
48976
+ placeholder: 'Enter permission key (e.g., Users.Create)',
48977
+ },
48978
+ },
48979
+ },
48980
+ validations: [
48981
+ {
48982
+ rule: 'required',
48983
+ },
48984
+ ],
48985
+ },
48986
+ {
48987
+ name: 'userId',
48988
+ title: 'User ID',
48989
+ description: 'User ID to check (optional, defaults to current user)',
48990
+ schema: {
48991
+ dataType: 'string',
48992
+ nullable: true,
48993
+ interface: {
48994
+ type: AXPWidgetsList.Editors.TextBox,
48995
+ options: {
48996
+ placeholder: 'Enter user ID (leave empty for current user)',
48997
+ },
48998
+ },
48999
+ },
49000
+ },
49001
+ ],
49002
+ outputs: [
49003
+ {
49004
+ name: 'hasPermission',
49005
+ title: 'Has Permission',
49006
+ description: 'Whether user has the permission',
49007
+ schema: {
49008
+ dataType: 'boolean',
49009
+ },
49010
+ },
49011
+ {
49012
+ name: 'userId',
49013
+ title: 'User ID',
49014
+ description: 'User ID that was checked',
49015
+ schema: {
49016
+ dataType: 'string',
49017
+ },
49018
+ },
49019
+ {
49020
+ name: 'permission',
49021
+ title: 'Permission',
49022
+ description: 'Permission that was checked',
49023
+ schema: {
49024
+ dataType: 'string',
49025
+ },
49026
+ },
49027
+ ],
48983
49028
  });
49029
+ // Execute Command Activity
48984
49030
  definitions.push({
48985
49031
  name: 'execute-command',
48986
49032
  type: 'workflow-activity:execute-command',
@@ -48988,8 +49034,82 @@ function generateActivityDefinitions() {
48988
49034
  description: 'Executes a command operation',
48989
49035
  category: commandCategory.id,
48990
49036
  executionMode: 'backend',
49037
+ icon: 'fa-terminal',
48991
49038
  outcomes: ['Done', 'Failed'],
49039
+ inputs: [
49040
+ {
49041
+ name: 'commandKey',
49042
+ title: 'Command Key',
49043
+ description: 'Command key to execute (e.g., "User:Create")',
49044
+ schema: {
49045
+ dataType: 'string',
49046
+ nullable: false,
49047
+ interface: {
49048
+ type: AXPWidgetsList.Editors.TextBox,
49049
+ options: {
49050
+ placeholder: 'Enter command key (e.g., User:Create)',
49051
+ },
49052
+ },
49053
+ },
49054
+ validations: [
49055
+ {
49056
+ rule: 'required',
49057
+ },
49058
+ ],
49059
+ },
49060
+ {
49061
+ name: 'input',
49062
+ title: 'Command Input',
49063
+ description: 'Command input data as JSON object',
49064
+ schema: {
49065
+ dataType: 'object',
49066
+ nullable: true,
49067
+ interface: {
49068
+ type: AXPWidgetsList.Advanced.CodeEditor,
49069
+ options: {
49070
+ language: 'json',
49071
+ placeholder: 'Enter JSON object for command input',
49072
+ },
49073
+ },
49074
+ },
49075
+ },
49076
+ ],
49077
+ outputs: [
49078
+ {
49079
+ name: 'result',
49080
+ title: 'Result',
49081
+ description: 'Command result data',
49082
+ schema: {
49083
+ dataType: 'object',
49084
+ },
49085
+ },
49086
+ {
49087
+ name: 'success',
49088
+ title: 'Success',
49089
+ description: 'Whether command executed successfully',
49090
+ schema: {
49091
+ dataType: 'boolean',
49092
+ },
49093
+ },
49094
+ {
49095
+ name: 'error',
49096
+ title: 'Error',
49097
+ description: 'Error message if command failed',
49098
+ schema: {
49099
+ dataType: 'string',
49100
+ },
49101
+ },
49102
+ {
49103
+ name: 'entityId',
49104
+ title: 'Entity ID',
49105
+ description: 'Created/Updated entity ID (if applicable)',
49106
+ schema: {
49107
+ dataType: 'string',
49108
+ },
49109
+ },
49110
+ ],
48992
49111
  });
49112
+ // Show Confirm Popup Activity
48993
49113
  definitions.push({
48994
49114
  name: 'show-confirm-popup',
48995
49115
  type: 'workflow-activity:show-confirm-popup',
@@ -48997,11 +49117,272 @@ function generateActivityDefinitions() {
48997
49117
  description: 'Shows a confirmation popup dialog with OK and Cancel buttons',
48998
49118
  category: commandCategory.id,
48999
49119
  executionMode: 'frontend',
49120
+ icon: 'fa-question-circle',
49000
49121
  outcomes: ['ok', 'cancel'],
49122
+ inputs: [
49123
+ {
49124
+ name: 'title',
49125
+ title: 'Title',
49126
+ description: 'Title of the confirmation dialog',
49127
+ schema: {
49128
+ dataType: 'string',
49129
+ nullable: false,
49130
+ interface: {
49131
+ type: AXPWidgetsList.Editors.TextBox,
49132
+ options: {
49133
+ placeholder: 'Enter dialog title',
49134
+ },
49135
+ },
49136
+ },
49137
+ validations: [
49138
+ {
49139
+ rule: 'required',
49140
+ },
49141
+ ],
49142
+ },
49143
+ {
49144
+ name: 'message',
49145
+ title: 'Message',
49146
+ description: 'Message/content of the confirmation dialog',
49147
+ schema: {
49148
+ dataType: 'string',
49149
+ nullable: false,
49150
+ interface: {
49151
+ type: AXPWidgetsList.Editors.LargeTextBox,
49152
+ options: {
49153
+ placeholder: 'Enter confirmation message',
49154
+ rows: 4,
49155
+ },
49156
+ },
49157
+ },
49158
+ validations: [
49159
+ {
49160
+ rule: 'required',
49161
+ },
49162
+ ],
49163
+ },
49164
+ {
49165
+ name: 'type',
49166
+ title: 'Dialog Type',
49167
+ description: 'Dialog type/color (e.g., warning, danger, info, success)',
49168
+ schema: {
49169
+ dataType: 'string',
49170
+ nullable: true,
49171
+ defaultValue: 'warning',
49172
+ interface: {
49173
+ type: AXPWidgetsList.Editors.SelectBox,
49174
+ options: {
49175
+ valueField: 'value',
49176
+ textField: 'text',
49177
+ dataSource: [
49178
+ { value: 'warning', text: 'Warning' },
49179
+ { value: 'danger', text: 'Danger' },
49180
+ { value: 'info', text: 'Info' },
49181
+ { value: 'success', text: 'Success' },
49182
+ ],
49183
+ },
49184
+ },
49185
+ },
49186
+ },
49187
+ {
49188
+ name: 'direction',
49189
+ title: 'Button Direction',
49190
+ description: 'Button layout direction',
49191
+ schema: {
49192
+ dataType: 'string',
49193
+ nullable: true,
49194
+ defaultValue: 'horizontal',
49195
+ interface: {
49196
+ type: AXPWidgetsList.Editors.SelectBox,
49197
+ options: {
49198
+ valueField: 'value',
49199
+ textField: 'text',
49200
+ dataSource: [
49201
+ { value: 'horizontal', text: 'Horizontal' },
49202
+ { value: 'vertical', text: 'Vertical' },
49203
+ ],
49204
+ },
49205
+ },
49206
+ },
49207
+ },
49208
+ {
49209
+ name: 'closable',
49210
+ title: 'Closable',
49211
+ description: 'Whether dialog is closable',
49212
+ schema: {
49213
+ dataType: 'boolean',
49214
+ nullable: true,
49215
+ defaultValue: false,
49216
+ interface: {
49217
+ type: AXPWidgetsList.Editors.ToggleSwitch,
49218
+ },
49219
+ },
49220
+ },
49221
+ {
49222
+ name: 'defaultAction',
49223
+ title: 'Default Action',
49224
+ description: 'Default action button',
49225
+ schema: {
49226
+ dataType: 'string',
49227
+ nullable: true,
49228
+ defaultValue: 'cancel',
49229
+ interface: {
49230
+ type: AXPWidgetsList.Editors.SelectBox,
49231
+ options: {
49232
+ valueField: 'value',
49233
+ textField: 'text',
49234
+ dataSource: [
49235
+ { value: 'confirm', text: 'Confirm' },
49236
+ { value: 'cancel', text: 'Cancel' },
49237
+ ],
49238
+ },
49239
+ },
49240
+ },
49241
+ },
49242
+ ],
49243
+ outputs: [],
49244
+ });
49245
+ // Show Layout Popup Activity
49246
+ definitions.push({
49247
+ name: 'show-layout-popup',
49248
+ type: 'workflow-activity:show-layout-popup',
49249
+ title: 'Show Layout Popup',
49250
+ description: 'Shows a popup dialog with a custom layout built using layout builder. Actions are defined dynamically and each action creates a corresponding outcome.',
49251
+ category: commandCategory.id,
49252
+ executionMode: 'frontend',
49253
+ icon: 'fa-window-maximize',
49254
+ // Outcomes are dynamic based on actions - default outcomes shown for reference
49255
+ outcomes: ['submit', 'cancel'],
49256
+ inputs: [
49257
+ {
49258
+ name: 'layout',
49259
+ title: 'Layout Definition',
49260
+ description: 'Layout definition as JSON string or AXPWidgetNode object',
49261
+ schema: {
49262
+ dataType: 'string',
49263
+ nullable: false,
49264
+ interface: {
49265
+ type: AXPWidgetsList.Advanced.CodeEditor,
49266
+ options: {
49267
+ language: 'json',
49268
+ placeholder: 'Enter layout definition as JSON',
49269
+ },
49270
+ },
49271
+ },
49272
+ validations: [
49273
+ {
49274
+ rule: 'required',
49275
+ },
49276
+ ],
49277
+ },
49278
+ {
49279
+ name: 'title',
49280
+ title: 'Dialog Title',
49281
+ description: 'Title of the dialog',
49282
+ schema: {
49283
+ dataType: 'string',
49284
+ nullable: true,
49285
+ interface: {
49286
+ type: AXPWidgetsList.Editors.TextBox,
49287
+ options: {
49288
+ placeholder: 'Enter dialog title',
49289
+ },
49290
+ },
49291
+ },
49292
+ },
49293
+ {
49294
+ name: 'size',
49295
+ title: 'Dialog Size',
49296
+ description: 'Size of the dialog',
49297
+ schema: {
49298
+ dataType: 'string',
49299
+ nullable: true,
49300
+ defaultValue: 'md',
49301
+ interface: {
49302
+ type: AXPWidgetsList.Editors.SelectBox,
49303
+ options: {
49304
+ valueField: 'value',
49305
+ textField: 'text',
49306
+ dataSource: [
49307
+ { value: 'sm', text: 'Small' },
49308
+ { value: 'md', text: 'Medium' },
49309
+ { value: 'lg', text: 'Large' },
49310
+ { value: 'xl', text: 'Extra Large' },
49311
+ { value: 'xxl', text: 'Extra Extra Large' },
49312
+ ],
49313
+ },
49314
+ },
49315
+ },
49316
+ },
49317
+ {
49318
+ name: 'closeButton',
49319
+ title: 'Show Close Button',
49320
+ description: 'Whether to show close button',
49321
+ schema: {
49322
+ dataType: 'boolean',
49323
+ nullable: true,
49324
+ defaultValue: true,
49325
+ interface: {
49326
+ type: AXPWidgetsList.Editors.ToggleSwitch,
49327
+ },
49328
+ },
49329
+ },
49330
+ {
49331
+ name: 'context',
49332
+ title: 'Initial Context',
49333
+ description: 'Initial context data for the form as JSON object',
49334
+ schema: {
49335
+ dataType: 'object',
49336
+ nullable: true,
49337
+ interface: {
49338
+ type: AXPWidgetsList.Advanced.CodeEditor,
49339
+ options: {
49340
+ language: 'json',
49341
+ placeholder: 'Enter initial context as JSON object',
49342
+ },
49343
+ },
49344
+ },
49345
+ },
49346
+ {
49347
+ name: 'actions',
49348
+ title: 'Dialog Actions',
49349
+ description: 'Dialog actions (buttons) to display in footer. Each action will create a corresponding outcome. Format: { prefix?: Array<Action>, suffix?: Array<Action> }',
49350
+ schema: {
49351
+ dataType: 'object',
49352
+ nullable: true,
49353
+ interface: {
49354
+ type: AXPWidgetsList.Advanced.CodeEditor,
49355
+ options: {
49356
+ language: 'json',
49357
+ placeholder: 'Enter actions as JSON object with prefix and/or suffix arrays',
49358
+ },
49359
+ },
49360
+ },
49361
+ },
49362
+ ],
49363
+ outputs: [
49364
+ {
49365
+ name: 'result',
49366
+ title: 'Result',
49367
+ description: 'Result data from the dialog (form values)',
49368
+ schema: {
49369
+ dataType: 'object',
49370
+ },
49371
+ },
49372
+ {
49373
+ name: 'action',
49374
+ title: 'Action',
49375
+ description: 'Action that was clicked (e.g., submit, cancel, or custom action name)',
49376
+ schema: {
49377
+ dataType: 'string',
49378
+ },
49379
+ },
49380
+ ],
49001
49381
  });
49002
49382
  }
49003
49383
  // Query category activities
49004
49384
  if (queryCategory) {
49385
+ // Execute Query Activity
49005
49386
  definitions.push({
49006
49387
  name: 'execute-query',
49007
49388
  type: 'workflow-activity:execute-query',
@@ -49009,7 +49390,72 @@ function generateActivityDefinitions() {
49009
49390
  description: 'Executes a query operation',
49010
49391
  category: queryCategory.id,
49011
49392
  executionMode: 'backend',
49393
+ icon: 'fa-search',
49012
49394
  outcomes: ['Done', 'Valid', 'Exists', 'NotFound', 'Failed'],
49395
+ inputs: [
49396
+ {
49397
+ name: 'queryKey',
49398
+ title: 'Query Key',
49399
+ description: 'Query key to execute (e.g., "User:CheckEmailExists")',
49400
+ schema: {
49401
+ dataType: 'string',
49402
+ nullable: false,
49403
+ interface: {
49404
+ type: AXPWidgetsList.Editors.TextBox,
49405
+ options: {
49406
+ placeholder: 'Enter query key (e.g., User:CheckEmailExists)',
49407
+ },
49408
+ },
49409
+ },
49410
+ validations: [
49411
+ {
49412
+ rule: 'required',
49413
+ },
49414
+ ],
49415
+ },
49416
+ {
49417
+ name: 'parameters',
49418
+ title: 'Query Parameters',
49419
+ description: 'Query parameters as JSON object',
49420
+ schema: {
49421
+ dataType: 'object',
49422
+ nullable: true,
49423
+ interface: {
49424
+ type: AXPWidgetsList.Advanced.CodeEditor,
49425
+ options: {
49426
+ language: 'json',
49427
+ placeholder: 'Enter query parameters as JSON object',
49428
+ },
49429
+ },
49430
+ },
49431
+ },
49432
+ ],
49433
+ outputs: [
49434
+ {
49435
+ name: 'data',
49436
+ title: 'Data',
49437
+ description: 'Query result data',
49438
+ schema: {
49439
+ dataType: 'object',
49440
+ },
49441
+ },
49442
+ {
49443
+ name: 'success',
49444
+ title: 'Success',
49445
+ description: 'Whether query executed successfully',
49446
+ schema: {
49447
+ dataType: 'boolean',
49448
+ },
49449
+ },
49450
+ {
49451
+ name: 'error',
49452
+ title: 'Error',
49453
+ description: 'Error message if query failed',
49454
+ schema: {
49455
+ dataType: 'string',
49456
+ },
49457
+ },
49458
+ ],
49013
49459
  });
49014
49460
  }
49015
49461
  return definitions;
@@ -49034,6 +49480,156 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
49034
49480
  type: Injectable
49035
49481
  }] });
49036
49482
 
49483
+ //#region ---- Mock Categories ----
49484
+ /**
49485
+ * Generate workflow categories with optimized count metadata
49486
+ *
49487
+ * Uses childrenCount & itemsCount for performance:
49488
+ * - childrenCount: Direct child categories (for lazy loading)
49489
+ * - itemsCount: Direct workflows in this category
49490
+ */
49491
+ function generateCategories() {
49492
+ const categories = [];
49493
+ // Root categories
49494
+ const rootCategories = [
49495
+ {
49496
+ title: 'Business Process',
49497
+ description: 'Business process workflows',
49498
+ childrenCount: 0,
49499
+ itemsCount: 2,
49500
+ },
49501
+ {
49502
+ title: 'Approval',
49503
+ description: 'Approval and review workflows',
49504
+ childrenCount: 0,
49505
+ itemsCount: 1,
49506
+ },
49507
+ {
49508
+ title: 'Data Processing',
49509
+ description: 'Data processing and transformation workflows',
49510
+ childrenCount: 0,
49511
+ itemsCount: 1,
49512
+ },
49513
+ {
49514
+ title: 'Integration',
49515
+ description: 'Integration and API workflows',
49516
+ childrenCount: 0,
49517
+ itemsCount: 0,
49518
+ },
49519
+ {
49520
+ title: 'Notification',
49521
+ description: 'Notification and communication workflows',
49522
+ childrenCount: 0,
49523
+ itemsCount: 0,
49524
+ },
49525
+ {
49526
+ title: 'General',
49527
+ description: 'General purpose workflows',
49528
+ childrenCount: 0,
49529
+ itemsCount: 0,
49530
+ },
49531
+ ];
49532
+ rootCategories.forEach(({ title, description, childrenCount, itemsCount }) => {
49533
+ const categoryId = AXPDataGenerator.uuid();
49534
+ categories.push({
49535
+ id: categoryId,
49536
+ title,
49537
+ description,
49538
+ parentId: undefined,
49539
+ childrenCount,
49540
+ itemsCount,
49541
+ });
49542
+ });
49543
+ return categories;
49544
+ }
49545
+ const WORKFLOW_CATEGORIES = generateCategories();
49546
+ //#endregion
49547
+
49548
+ /**
49549
+ * Mock Workflow Category Provider
49550
+ *
49551
+ * Provides workflow categories from mock data.
49552
+ */
49553
+ class AXCWorkflowCategoryProvider {
49554
+ async getList(parentId) {
49555
+ if (parentId === undefined) {
49556
+ // Return root categories
49557
+ return WORKFLOW_CATEGORIES.filter(cat => cat.parentId === undefined);
49558
+ }
49559
+ // Return child categories
49560
+ return WORKFLOW_CATEGORIES.filter(cat => cat.parentId === parentId);
49561
+ }
49562
+ async getById(id) {
49563
+ return WORKFLOW_CATEGORIES.find(cat => cat.id === id);
49564
+ }
49565
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowCategoryProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
49566
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowCategoryProvider }); }
49567
+ }
49568
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowCategoryProvider, decorators: [{
49569
+ type: Injectable
49570
+ }] });
49571
+
49572
+ /**
49573
+ * Mock Workflow Definition Provider
49574
+ *
49575
+ * Provides workflow definition metadata from mock data.
49576
+ * Converts workflow definitions to metadata format.
49577
+ */
49578
+ class AXCWorkflowDefinitionMetadataProvider {
49579
+ constructor() {
49580
+ this.metadataCache = null;
49581
+ }
49582
+ getMetadataCache() {
49583
+ if (this.metadataCache) {
49584
+ return this.metadataCache;
49585
+ }
49586
+ this.metadataCache = new Map();
49587
+ // Find Business Process category (first category with itemsCount > 0)
49588
+ const businessProcessCategory = WORKFLOW_CATEGORIES.find(cat => cat.title === 'Business Process');
49589
+ const approvalCategory = WORKFLOW_CATEGORIES.find(cat => cat.title === 'Approval');
49590
+ const dataProcessingCategory = WORKFLOW_CATEGORIES.find(cat => cat.title === 'Data Processing');
49591
+ // Convert workflow definitions to metadata
49592
+ workflowDefinitionMock.forEach(def => {
49593
+ let categoryId;
49594
+ // Map workflow to category based on name/type
49595
+ if (def.name?.includes('approval') || def.name?.includes('review')) {
49596
+ categoryId = approvalCategory?.id;
49597
+ }
49598
+ else if (def.name?.includes('process') || def.name?.includes('data')) {
49599
+ categoryId = dataProcessingCategory?.id;
49600
+ }
49601
+ else {
49602
+ categoryId = businessProcessCategory?.id;
49603
+ }
49604
+ const metadata = {
49605
+ name: def.name || '',
49606
+ title: def.title || def.name || '',
49607
+ description: def.description || undefined,
49608
+ category: categoryId,
49609
+ icon: 'fa-light fa-diagram-project',
49610
+ version: 1,
49611
+ isPublished: true,
49612
+ isBrowsable: true,
49613
+ };
49614
+ this.metadataCache.set(metadata.name, metadata);
49615
+ });
49616
+ return this.metadataCache;
49617
+ }
49618
+ async getList(categoryId) {
49619
+ const cache = this.getMetadataCache();
49620
+ return Array.from(cache.values()).filter(meta => meta.category === categoryId);
49621
+ }
49622
+ async getById(name) {
49623
+ const cache = this.getMetadataCache();
49624
+ return cache.get(name);
49625
+ }
49626
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionMetadataProvider, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
49627
+ static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionMetadataProvider }); }
49628
+ }
49629
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowDefinitionMetadataProvider, decorators: [{
49630
+ type: Injectable
49631
+ }] });
49632
+
49037
49633
  class AXCWorkflowManagementMockModule {
49038
49634
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowManagementMockModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule }); }
49039
49635
  static { this.ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowManagementMockModule }); }
@@ -49059,6 +49655,18 @@ class AXCWorkflowManagementMockModule {
49059
49655
  useClass: AXCActivityDefinitionSeeder,
49060
49656
  multi: true,
49061
49657
  },
49658
+ // Workflow Category Provider
49659
+ {
49660
+ provide: AXP_WORKFLOW_CATEGORY_PROVIDER,
49661
+ useClass: AXCWorkflowCategoryProvider,
49662
+ multi: true,
49663
+ },
49664
+ // Workflow Definition Metadata Provider
49665
+ {
49666
+ provide: AXP_WORKFLOW_PROVIDER,
49667
+ useClass: AXCWorkflowDefinitionMetadataProvider,
49668
+ multi: true,
49669
+ },
49062
49670
  ] }); }
49063
49671
  }
49064
49672
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImport: i0, type: AXCWorkflowManagementMockModule, decorators: [{
@@ -49089,6 +49697,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.12", ngImpo
49089
49697
  useClass: AXCActivityDefinitionSeeder,
49090
49698
  multi: true,
49091
49699
  },
49700
+ // Workflow Category Provider
49701
+ {
49702
+ provide: AXP_WORKFLOW_CATEGORY_PROVIDER,
49703
+ useClass: AXCWorkflowCategoryProvider,
49704
+ multi: true,
49705
+ },
49706
+ // Workflow Definition Metadata Provider
49707
+ {
49708
+ provide: AXP_WORKFLOW_PROVIDER,
49709
+ useClass: AXCWorkflowDefinitionMetadataProvider,
49710
+ multi: true,
49711
+ },
49092
49712
  ],
49093
49713
  }]
49094
49714
  }] });
@@ -49585,5 +50205,5 @@ const AXCMockEntityLogListener = {
49585
50205
  * Generated bundle index. Do not edit.
49586
50206
  */
49587
50207
 
49588
- export { ACTIVITY_CATEGORIES, ACTIVITY_DEFINITIONS, APPLICATIONS, AXCActivityCategorySeeder, AXCActivityDefinitionSeeder, AXCAppTermDataSeeder, AXCAppVersionDataSeeder, AXCApplicationDataSeeder, AXCApplicationManagementMockModule, AXCAssessmentManagementMockModule, AXCAssetManagementMockModule, AXCAuthMockModule, AXCCalendarDataSeeder, AXCCalendarEventDataSeeder, AXCCalendarManagementMockModule, AXCCommonMockModule, AXCContactManagementMockModule, AXCContentManagementMockModule, AXCConversationMockModule, AXCCustomerManagementMockModule, AXCDashboardManagementMockModule, AXCDataManagementMockModule, AXCDexieEntityStorageService, AXCDocumentManagementMockModule, AXCEditionDataSeeder, AXCFileStorageService, AXCFinancialCoreMockModule, AXCFormTemplateManagementMockModule, AXCGoogleStrategyMock, AXCHelpDeskMockModule, AXCHumanCapitalManagementMockModule, AXCIdentifierManagementMockModule, AXCJsaSeeder, AXCLearningManagementMockModule, AXCLocaleManagementMockModule, AXCLocationManagementMockModule, AXCLockService, AXCMeasurementCoreMockModule, AXCMeetingManagementMockModule, AXCMetaDataDefinitionDataSeeder, AXCMetadataCategorySeeder, AXCMiddlewaresModule, AXCMockEntityLogListener, AXCMockModule, AXCNotificationManagementMockModule, AXCOrderManagementMockModule, AXCOrganizationManagementMockModule, AXCPersonManagementMockModule, AXCPlatformManagementMockModule, AXCProcurementManagementMockModule, AXCProductCatalogMockModule, AXCProjectManagementMockModule, AXCQueryCategoryMetadataInheritanceQuery, AXCReportManagementMockModule, AXCSecurityManagementMockModule, AXCSubscriptionManagementMockModule, AXCSupplierManagementMockModule, AXCTaskManagementMockModule, AXCTenantManagementMockModule, AXCTenantSeeder, AXCTokensDataSeeder, AXCUserPassStrategyMock, AXCVersionDB, AXCVersioningService, AXCWorkflowDefinitionDataSeeder, AXCWorkflowExecutionDB, AXCWorkflowExecutionService, AXCWorkflowManagementMockModule, AXMAiResponderService, AXMAssessmentCaseDataSeeder, AXMAssessmentSessionDataSeeder, AXMBusinessUnitDataSeeder, AXMCalendarEventTypeSeeder, AXMEmployeeDataSeeder, AXMEmploymentTypeDataSeeder, AXMFormDataSourcesProvider, AXMJobDefinitionDataSeeder, AXMJobLevelDataSeeder, AXMLeaveRequestDataSeeder, AXMMeetingDataSeeder, AXMMeetingFilesDataSeeder, AXMMeetingParticipantDataSeeder, AXMMeetingRoleTypeDataSeeder, AXMMeetingSessionDataSeeder, AXMMeetingTypeDataSeeder, AXMMeetingTypeFileTemplateDataSeeder, AXMPositionAssignmentDataSeeder, AXMPositionDataSeeder, AXMQuestionBankItemCategoryDataSeeder, AXMQuestionBankItemDataSeeder, AXMQuestionnaireCategoryDataSeeder, AXMQuestionnaireDataSeeder, AXMReportExecuteCommand, AXMResponsibilityDataSeeder, AXMTagDataSeeder, AXMTeamDataSeeder, AXPDashboardDataSeeder, AXPIdentifierDB, AXPMessageDataSeeder, AXPMockChecksumProvider, AXPMockClockProvider, AXPMockIdentifierService, AXPMockLookupProvider, AXPMockPolicyProvider, AXPMockSequenceProvider, AXPRoomDataSeeder, AXPSecurityManagementRoleDataSeeder, AXPSecurityManagementUserDataSeeder, AXPTaskBoardPlatformManagementTaskProvider, AXPTaskBoardProjectManagementTaskProvider, AXVChangeType, BUSINESS_UNITS_MOCK, COSTMANAGER_ENTERPRISE, COSTMANAGER_PROFESSIONAL, COSTMANAGER_STANDARD, CRM_ENTERPRISE, DASHBOARDS, EDITIONS, EMPLOYEES_MOCK, EMPLOYMENT_TYPES_MOCK, FINANCE_BASIC, FINANCE_ENTERPRISE, FINANCE_PRO, HR_ENTERPRISE, JOB_DEFINITIONS_CATEGORY_MOCK, JOB_DEFINITIONS_MOCK, JOB_LEVELS_MOCK, LASER_PLUMBING_TENANT_ID, LEAVE_REQUESTS_MOCK, ORDERING_BASIC, ORDERING_ENTERPRISE, ORDERING_STANDARD, OWNERSHIP_FILTER_BYPASS, PLATFORM_CONSOLE, PLATFORM_TENANT_ID, POSITIONS_CATEGORY_MOCK, POSITIONS_MOCK, POSITION_ASSIGNMENTS_MOCK, QUESTIONNAIRE_CATEGORY_MOCK, QUESTION_BANK_ITEM_CATEGORY_MOCK, RESPONSIBILITIES_CATEGORY_MOCK, RESPONSIBILITIES_MOCK, SAFETYMINDER_BASIC, SAFETYMINDER_ENTERPRISE, SAFETYMINDER_PROFESSIONAL, SHOP_BASIC, SHOP_ENTERPRISE, SHOP_PRO, TAGS_MOCK, TASKS, TASK_STATUSES, TASK_TEMPLATES, TASK_TYPES, TEAMS_MOCK, TIMEPLICITY_TENANT_ID, TOKENS, VISIBILITY_FILTER_BYPASS, assessmentCaseMock, assessmentSessionMock, auditLoggerMiddleware, avatarInterface, axVersionDB, axWorkflowExecutionDB, bypassAllFilters, bypassOwnershipFilter, bypassVisibilityFilter, calendarEventMock, calendarEventTypeMockData, calendarMock, checkboxInterface, childCountMiddleware, colorInterface, computeDiff, contactInterface, createAndSaveDashboardForUser, createDashboardForUser, createFileCastMiddleware, dateInterface, dateTimeInterface, descriptionInterface, emailInterface, fileUploaderInterface, findContactMethod, findEmployeeById, findPersonById, generateUserDashboard, groupOrderItemCalculatorMiddleware, historyLoggerMiddleware, identifierCommitMiddleware, imageInterface, lockGuardMiddleware, longTextAnswerInterface, lookupInterface, lookupResolverMiddleware, mapInterface, meetingFilesMock, meetingIds, meetingMock, meetingParticipantMock, meetingRoleTypeMock, meetingSessionMock, meetingTimeSlotMock, meetingTypeFileTemplateMock, meetingTypeMock, mergeDetailRelationMiddleware, metadataCategoryMocks, mockRoleDefinitions, mockUsers, multipleChoiceInterface, multipleSelectInterface, normalizeAnyFileArrayForView, normalizeSnapshotFileFieldsForView, numberInterface, participantIds, passwordInterface, phoneInterface, primaryMiddleware, qrcodeInterface, questionBankItemMock, questionnaireMock, ratingInterface, richTextInterface, scaleInterface, selectInterface, selectionListInterface, signatureInterface, signatureLoaderMiddleware, tagInterface, tenantMocks, textAreaInterface, textInterface, timeDurationInterface, titleInterface, toUiRows, toggleInterface, urlInterface, versionInterface, visibilityFilterMiddleware, workflowDefinitionEntityMock, workflowDefinitionIds, workflowDefinitionMock, yesNoInterface };
50208
+ export { ACTIVITY_CATEGORIES, ACTIVITY_DEFINITIONS, APPLICATIONS, AXCActivityCategorySeeder, AXCActivityDefinitionSeeder, AXCAppTermDataSeeder, AXCAppVersionDataSeeder, AXCApplicationDataSeeder, AXCApplicationManagementMockModule, AXCAssessmentManagementMockModule, AXCAssetManagementMockModule, AXCAuthMockModule, AXCCalendarDataSeeder, AXCCalendarEventDataSeeder, AXCCalendarManagementMockModule, AXCCommonMockModule, AXCContactManagementMockModule, AXCContentManagementMockModule, AXCConversationMockModule, AXCCustomerManagementMockModule, AXCDashboardManagementMockModule, AXCDataManagementMockModule, AXCDexieEntityStorageService, AXCDocumentManagementMockModule, AXCEditionDataSeeder, AXCFileStorageService, AXCFinancialCoreMockModule, AXCFormTemplateManagementMockModule, AXCGoogleStrategyMock, AXCHelpDeskMockModule, AXCHumanCapitalManagementMockModule, AXCIdentifierManagementMockModule, AXCJsaSeeder, AXCLearningManagementMockModule, AXCLocaleManagementMockModule, AXCLocationManagementMockModule, AXCLockService, AXCMeasurementCoreMockModule, AXCMeetingManagementMockModule, AXCMetaDataDefinitionDataSeeder, AXCMetadataCategorySeeder, AXCMiddlewaresModule, AXCMockEntityLogListener, AXCMockModule, AXCNotificationManagementMockModule, AXCOrderManagementMockModule, AXCOrganizationManagementMockModule, AXCPersonManagementMockModule, AXCPlatformManagementMockModule, AXCProcurementManagementMockModule, AXCProductCatalogMockModule, AXCProjectManagementMockModule, AXCQueryCategoryMetadataInheritanceQuery, AXCReportManagementMockModule, AXCSecurityManagementMockModule, AXCSubscriptionManagementMockModule, AXCSupplierManagementMockModule, AXCTaskManagementMockModule, AXCTenantManagementMockModule, AXCTenantSeeder, AXCTokensDataSeeder, AXCUserPassStrategyMock, AXCVersionDB, AXCVersioningService, AXCWorkflowCategoryProvider, AXCWorkflowDefinitionDataSeeder, AXCWorkflowDefinitionMetadataProvider, AXCWorkflowExecutionDB, AXCWorkflowManagementMockModule, AXCWorkflowRuntimeService, AXMAiResponderService, AXMAssessmentCaseDataSeeder, AXMAssessmentSessionDataSeeder, AXMBusinessUnitDataSeeder, AXMCalendarEventTypeSeeder, AXMEmployeeDataSeeder, AXMEmploymentTypeDataSeeder, AXMFormDataSourcesProvider, AXMJobDefinitionDataSeeder, AXMJobLevelDataSeeder, AXMLeaveRequestDataSeeder, AXMMeetingDataSeeder, AXMMeetingFilesDataSeeder, AXMMeetingParticipantDataSeeder, AXMMeetingRoleTypeDataSeeder, AXMMeetingSessionDataSeeder, AXMMeetingTypeDataSeeder, AXMMeetingTypeFileTemplateDataSeeder, AXMPositionAssignmentDataSeeder, AXMPositionDataSeeder, AXMQuestionBankItemCategoryDataSeeder, AXMQuestionBankItemDataSeeder, AXMQuestionnaireCategoryDataSeeder, AXMQuestionnaireDataSeeder, AXMReportExecuteCommand, AXMResponsibilityDataSeeder, AXMTagDataSeeder, AXMTeamDataSeeder, AXPDashboardDataSeeder, AXPIdentifierDB, AXPMessageDataSeeder, AXPMockChecksumProvider, AXPMockClockProvider, AXPMockIdentifierService, AXPMockLookupProvider, AXPMockPolicyProvider, AXPMockSequenceProvider, AXPRoomDataSeeder, AXPSecurityManagementRoleDataSeeder, AXPSecurityManagementUserDataSeeder, AXPTaskBoardPlatformManagementTaskProvider, AXPTaskBoardProjectManagementTaskProvider, AXVChangeType, BUSINESS_UNITS_MOCK, COSTMANAGER_ENTERPRISE, COSTMANAGER_PROFESSIONAL, COSTMANAGER_STANDARD, CRM_ENTERPRISE, DASHBOARDS, EDITIONS, EMPLOYEES_MOCK, EMPLOYMENT_TYPES_MOCK, FINANCE_BASIC, FINANCE_ENTERPRISE, FINANCE_PRO, HR_ENTERPRISE, JOB_DEFINITIONS_CATEGORY_MOCK, JOB_DEFINITIONS_MOCK, JOB_LEVELS_MOCK, LASER_PLUMBING_TENANT_ID, LEAVE_REQUESTS_MOCK, ORDERING_BASIC, ORDERING_ENTERPRISE, ORDERING_STANDARD, OWNERSHIP_FILTER_BYPASS, PLATFORM_CONSOLE, PLATFORM_TENANT_ID, POSITIONS_CATEGORY_MOCK, POSITIONS_MOCK, POSITION_ASSIGNMENTS_MOCK, QUESTIONNAIRE_CATEGORY_MOCK, QUESTION_BANK_ITEM_CATEGORY_MOCK, RESPONSIBILITIES_CATEGORY_MOCK, RESPONSIBILITIES_MOCK, SAFETYMINDER_BASIC, SAFETYMINDER_ENTERPRISE, SAFETYMINDER_PROFESSIONAL, SHOP_BASIC, SHOP_ENTERPRISE, SHOP_PRO, TAGS_MOCK, TASKS, TASK_STATUSES, TASK_TEMPLATES, TASK_TYPES, TEAMS_MOCK, TIMEPLICITY_TENANT_ID, TOKENS, VISIBILITY_FILTER_BYPASS, WORKFLOW_CATEGORIES, assessmentCaseMock, assessmentSessionMock, auditLoggerMiddleware, avatarInterface, axVersionDB, axWorkflowExecutionDB, bypassAllFilters, bypassOwnershipFilter, bypassVisibilityFilter, calendarEventMock, calendarEventTypeMockData, calendarMock, checkboxInterface, childCountMiddleware, colorInterface, computeDiff, contactInterface, createAndSaveDashboardForUser, createDashboardForUser, createFileCastMiddleware, dateInterface, dateTimeInterface, descriptionInterface, emailInterface, fileUploaderInterface, findContactMethod, findEmployeeById, findPersonById, generateUserDashboard, groupOrderItemCalculatorMiddleware, historyLoggerMiddleware, identifierCommitMiddleware, imageInterface, lockGuardMiddleware, longTextAnswerInterface, lookupInterface, lookupResolverMiddleware, mapInterface, meetingFilesMock, meetingIds, meetingMock, meetingParticipantMock, meetingRoleTypeMock, meetingSessionMock, meetingTimeSlotMock, meetingTypeFileTemplateMock, meetingTypeMock, mergeDetailRelationMiddleware, metadataCategoryMocks, mockRoleDefinitions, mockUsers, multipleChoiceInterface, multipleSelectInterface, normalizeAnyFileArrayForView, normalizeSnapshotFileFieldsForView, numberInterface, participantIds, passwordInterface, phoneInterface, primaryMiddleware, qrcodeInterface, questionBankItemMock, questionnaireMock, ratingInterface, richTextInterface, scaleInterface, selectInterface, selectionListInterface, signatureInterface, signatureLoaderMiddleware, tagInterface, tenantMocks, textAreaInterface, textInterface, timeDurationInterface, titleInterface, toUiRows, toggleInterface, urlInterface, versionInterface, visibilityFilterMiddleware, workflowDefinitionEntityMock, workflowDefinitionIds, workflowDefinitionMock, yesNoInterface };
49589
50209
  //# sourceMappingURL=acorex-connectivity-mock.mjs.map