@quanticjs/core 1.1.1 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (123) hide show
  1. package/dist/QuanticCoreModule.d.ts +4 -0
  2. package/dist/QuanticCoreModule.js +53 -0
  3. package/dist/cqrs/behaviors/LogBehavior.d.ts +4 -1
  4. package/dist/cqrs/behaviors/LogBehavior.js +3 -0
  5. package/dist/cqrs/behaviors/TransactionalBehavior.d.ts +4 -11
  6. package/dist/cqrs/behaviors/TransactionalBehavior.js +3 -12
  7. package/dist/cqrs/behaviors/ValidationBehavior.d.ts +4 -1
  8. package/dist/cqrs/behaviors/ValidationBehavior.js +3 -0
  9. package/dist/cqrs/pipeline/QuanticCommandBus.d.ts +3 -28
  10. package/dist/cqrs/pipeline/QuanticCommandBus.js +11 -59
  11. package/dist/cqrs/pipeline/QuanticQueryBus.d.ts +3 -19
  12. package/dist/cqrs/pipeline/QuanticQueryBus.js +11 -38
  13. package/dist/cqrs/pipeline/behavior-order.d.ts +11 -0
  14. package/dist/cqrs/pipeline/behavior-order.js +14 -0
  15. package/dist/cqrs/pipeline/types.d.ts +8 -0
  16. package/dist/cqrs/pipeline/types.js +4 -0
  17. package/dist/index.d.ts +6 -22
  18. package/dist/index.js +13 -44
  19. package/package.json +26 -80
  20. package/dist/cqrs/PipelineExecutor.d.ts +0 -31
  21. package/dist/cqrs/PipelineExecutor.js +0 -81
  22. package/dist/cqrs/behaviors/CacheBehavior.d.ts +0 -9
  23. package/dist/cqrs/behaviors/CacheBehavior.js +0 -68
  24. package/dist/cqrs/behaviors/DistributedLockBehavior.d.ts +0 -12
  25. package/dist/cqrs/behaviors/DistributedLockBehavior.js +0 -90
  26. package/dist/cqrs/behaviors/FeatureFlagBehavior.d.ts +0 -8
  27. package/dist/cqrs/behaviors/FeatureFlagBehavior.js +0 -58
  28. package/dist/cqrs/behaviors/InvalidateCacheBehavior.d.ts +0 -9
  29. package/dist/cqrs/behaviors/InvalidateCacheBehavior.js +0 -61
  30. package/dist/cqrs/behaviors/PerformanceBehavior.d.ts +0 -12
  31. package/dist/cqrs/behaviors/PerformanceBehavior.js +0 -56
  32. package/dist/cqrs/behaviors/WorkflowBehavior.d.ts +0 -8
  33. package/dist/cqrs/behaviors/WorkflowBehavior.js +0 -61
  34. package/dist/events/DomainEvent.d.ts +0 -14
  35. package/dist/events/DomainEvent.js +0 -27
  36. package/dist/events/OutboxEvent.entity.d.ts +0 -18
  37. package/dist/events/OutboxEvent.entity.js +0 -87
  38. package/dist/events/OutboxPublisherService.d.ts +0 -14
  39. package/dist/events/OutboxPublisherService.js +0 -104
  40. package/dist/events/RedisStreamConsumer.d.ts +0 -43
  41. package/dist/events/RedisStreamConsumer.js +0 -158
  42. package/dist/events/RedisStreamPublisher.d.ts +0 -9
  43. package/dist/events/RedisStreamPublisher.js +0 -60
  44. package/dist/metrics/MetricsController.d.ts +0 -7
  45. package/dist/metrics/MetricsController.js +0 -42
  46. package/dist/metrics/MetricsService.d.ts +0 -13
  47. package/dist/metrics/MetricsService.js +0 -58
  48. package/dist/redis/redis.module.d.ts +0 -8
  49. package/dist/redis/redis.module.js +0 -49
  50. package/dist/shared-kernel.module.d.ts +0 -13
  51. package/dist/shared-kernel.module.js +0 -87
  52. package/dist/testing/TestingModuleFactory.d.ts +0 -23
  53. package/dist/testing/TestingModuleFactory.js +0 -63
  54. package/dist/testing/index.d.ts +0 -2
  55. package/dist/testing/index.js +0 -7
  56. package/dist/unleash/initial-flags.d.ts +0 -7
  57. package/dist/unleash/initial-flags.js +0 -9
  58. package/dist/unleash/unleash.module.d.ts +0 -9
  59. package/dist/unleash/unleash.module.js +0 -47
  60. package/src/bootstrap/bootstrapService.ts +0 -72
  61. package/src/cqrs/behaviors/CacheBehavior.spec.ts +0 -63
  62. package/src/cqrs/behaviors/CacheBehavior.ts +0 -54
  63. package/src/cqrs/behaviors/DistributedLockBehavior.ts +0 -88
  64. package/src/cqrs/behaviors/FeatureFlagBehavior.ts +0 -46
  65. package/src/cqrs/behaviors/InvalidateCacheBehavior.spec.ts +0 -89
  66. package/src/cqrs/behaviors/InvalidateCacheBehavior.ts +0 -50
  67. package/src/cqrs/behaviors/LogBehavior.spec.ts +0 -55
  68. package/src/cqrs/behaviors/LogBehavior.ts +0 -121
  69. package/src/cqrs/behaviors/PerformanceBehavior.spec.ts +0 -48
  70. package/src/cqrs/behaviors/PerformanceBehavior.ts +0 -43
  71. package/src/cqrs/behaviors/TransactionalBehavior.ts +0 -64
  72. package/src/cqrs/behaviors/ValidationBehavior.spec.ts +0 -114
  73. package/src/cqrs/behaviors/ValidationBehavior.ts +0 -29
  74. package/src/cqrs/behaviors/WorkflowBehavior.spec.ts +0 -97
  75. package/src/cqrs/behaviors/WorkflowBehavior.ts +0 -62
  76. package/src/cqrs/constants.ts +0 -2
  77. package/src/cqrs/decorators/Cache.decorator.ts +0 -24
  78. package/src/cqrs/decorators/DistributedLock.decorator.ts +0 -34
  79. package/src/cqrs/decorators/FeatureFlag.decorator.ts +0 -23
  80. package/src/cqrs/decorators/InvalidateCache.decorator.spec.ts +0 -20
  81. package/src/cqrs/decorators/InvalidateCache.decorator.ts +0 -17
  82. package/src/cqrs/decorators/IsolatedTransaction.decorator.ts +0 -24
  83. package/src/cqrs/decorators/Log.decorator.ts +0 -22
  84. package/src/cqrs/decorators/Validate.decorator.ts +0 -39
  85. package/src/cqrs/decorators/Workflow.decorator.ts +0 -22
  86. package/src/cqrs/interfaces/WorkflowEngine.ts +0 -19
  87. package/src/cqrs/pipeline/QuanticCommandBus.ts +0 -69
  88. package/src/cqrs/pipeline/QuanticQueryBus.ts +0 -56
  89. package/src/cqrs/pipeline/runPipeline.ts +0 -22
  90. package/src/cqrs/transaction/TransactionContext.ts +0 -26
  91. package/src/cqrs/transaction/getTransactionalRepo.ts +0 -23
  92. package/src/cqrs/validation/ICommandValidator.ts +0 -55
  93. package/src/entities/BaseEntity.ts +0 -16
  94. package/src/entities/TenantBaseEntity.ts +0 -7
  95. package/src/events/DomainEvent.ts +0 -27
  96. package/src/events/OutboxEvent.entity.ts +0 -56
  97. package/src/events/OutboxPublisherService.ts +0 -94
  98. package/src/events/RedisStreamConsumer.ts +0 -172
  99. package/src/events/RedisStreamPublisher.ts +0 -54
  100. package/src/filters/GlobalExceptionFilter.ts +0 -125
  101. package/src/guards/JwtAuthGuard.ts +0 -29
  102. package/src/guards/JwtStrategy.ts +0 -41
  103. package/src/guards/RolesGuard.ts +0 -39
  104. package/src/index.ts +0 -118
  105. package/src/interceptors/ResultInterceptor.ts +0 -93
  106. package/src/lifecycle/GracefulShutdownService.ts +0 -77
  107. package/src/logging/pino-config.ts +0 -80
  108. package/src/metrics/MetricsController.ts +0 -17
  109. package/src/metrics/MetricsService.ts +0 -55
  110. package/src/middleware/CorrelationIdMiddleware.ts +0 -27
  111. package/src/middleware/CorrelationStore.ts +0 -13
  112. package/src/middleware/TenantContextMiddleware.ts +0 -21
  113. package/src/middleware/TenantStore.ts +0 -11
  114. package/src/redis/redis.module.ts +0 -41
  115. package/src/resilience/CircuitBreakerFactory.ts +0 -33
  116. package/src/result/Result.ts +0 -66
  117. package/src/shared-kernel.module.ts +0 -87
  118. package/src/subscribers/TenantSubscriber.ts +0 -47
  119. package/src/testing/TestingModuleFactory.ts +0 -78
  120. package/src/testing/index.ts +0 -2
  121. package/src/testing/mocks.ts +0 -59
  122. package/src/unleash/unleash.module.ts +0 -45
  123. package/tsconfig.json +0 -22
@@ -1,114 +0,0 @@
1
- import { z } from 'zod';
2
- import { ValidationBehavior } from './ValidationBehavior';
3
- import { Result, ErrorType } from '../../result/Result';
4
- import { Validate } from '../decorators/Validate.decorator';
5
- import { ICommandValidator, validateCommand } from '../validation/ICommandValidator';
6
-
7
- // --- Validator classes ---
8
-
9
- class BlockUserValidator implements ICommandValidator<BlockUserCommand> {
10
- private schema = z
11
- .object({
12
- blockerId: z.string().min(1, 'blockerId is required'),
13
- blockedUserId: z.string().min(1, 'blockedUserId is required'),
14
- })
15
- .refine((d) => d.blockerId !== d.blockedUserId, {
16
- message: 'Cannot block yourself',
17
- });
18
-
19
- validate(command: BlockUserCommand): Result<void> {
20
- return validateCommand(this.schema, command);
21
- }
22
- }
23
-
24
- class NameValidator implements ICommandValidator<ValidatedCommand> {
25
- private schema = z.object({
26
- name: z.string().min(5, 'Name must be at least 5 characters'),
27
- });
28
-
29
- validate(command: ValidatedCommand): Result<void> {
30
- return validateCommand(this.schema, command);
31
- }
32
- }
33
-
34
- // --- Commands ---
35
-
36
- @Validate(NameValidator)
37
- class ValidatedCommand {
38
- name: string;
39
- constructor(name: string) {
40
- this.name = name;
41
- }
42
- }
43
-
44
- @Validate(BlockUserValidator)
45
- class BlockUserCommand {
46
- constructor(
47
- public readonly blockerId: string,
48
- public readonly blockedUserId: string,
49
- ) {}
50
- }
51
-
52
- class UnvalidatedCommand {
53
- name: string;
54
- constructor(name: string) {
55
- this.name = name;
56
- }
57
- }
58
-
59
- describe('ValidationBehavior', () => {
60
- let behavior: ValidationBehavior;
61
- const next = jest.fn().mockResolvedValue(Result.success('ok'));
62
-
63
- beforeEach(() => {
64
- behavior = new ValidationBehavior();
65
- next.mockClear();
66
- });
67
-
68
- it('should pass through when command has no @Validate decorator', async () => {
69
- const result = await behavior.execute(new UnvalidatedCommand('hi'), next);
70
-
71
- expect(result.isSuccess).toBe(true);
72
- expect(next).toHaveBeenCalledTimes(1);
73
- });
74
-
75
- it('should pass through when validation succeeds', async () => {
76
- const result = await behavior.execute(new ValidatedCommand('hello'), next);
77
-
78
- expect(result.isSuccess).toBe(true);
79
- expect(next).toHaveBeenCalledTimes(1);
80
- });
81
-
82
- it('should return validation error when validation fails', async () => {
83
- const result = await behavior.execute(new ValidatedCommand('hi'), next);
84
-
85
- expect(result.isSuccess).toBe(false);
86
- expect(result.errorType).toBe(ErrorType.ValidationError);
87
- expect(result.errorMessage).toContain('at least 5 characters');
88
- expect(next).not.toHaveBeenCalled();
89
- });
90
-
91
- it('should support conditional validation with refine', async () => {
92
- const result = await behavior.execute(new BlockUserCommand('user-1', 'user-1'), next);
93
-
94
- expect(result.isSuccess).toBe(false);
95
- expect(result.errorType).toBe(ErrorType.ValidationError);
96
- expect(result.errorMessage).toContain('Cannot block yourself');
97
- expect(next).not.toHaveBeenCalled();
98
- });
99
-
100
- it('should pass valid block user command', async () => {
101
- const result = await behavior.execute(new BlockUserCommand('user-1', 'user-2'), next);
102
-
103
- expect(result.isSuccess).toBe(true);
104
- expect(next).toHaveBeenCalledTimes(1);
105
- });
106
-
107
- it('should report missing required fields', async () => {
108
- const result = await behavior.execute(new BlockUserCommand('', 'user-2'), next);
109
-
110
- expect(result.isSuccess).toBe(false);
111
- expect(result.errorMessage).toContain('blockerId');
112
- expect(next).not.toHaveBeenCalled();
113
- });
114
- });
@@ -1,29 +0,0 @@
1
- import { Injectable } from '@nestjs/common';
2
- import { shouldValidate, getValidatorClass } from '../decorators/Validate.decorator';
3
- import { Result, ErrorType } from '../../result/Result';
4
-
5
- @Injectable()
6
- export class ValidationBehavior {
7
- async execute<T>(command: object, next: () => Promise<Result<T>>): Promise<Result<T>> {
8
- if (!shouldValidate(command.constructor)) {
9
- return next();
10
- }
11
-
12
- const ValidatorClass = getValidatorClass(command.constructor);
13
- if (!ValidatorClass) {
14
- return next();
15
- }
16
-
17
- const validator = new ValidatorClass();
18
- const validationResult = validator.validate(command);
19
-
20
- if (!validationResult.isSuccess) {
21
- return Result.failure<T>(
22
- validationResult.errorType ?? ErrorType.ValidationError,
23
- validationResult.errorMessage ?? 'Validation failed',
24
- );
25
- }
26
-
27
- return next();
28
- }
29
- }
@@ -1,97 +0,0 @@
1
- import { WorkflowBehavior } from './WorkflowBehavior';
2
- import { Workflow } from '../decorators/Workflow.decorator';
3
- import { Result } from '../../result/Result';
4
- import { WorkflowEngine, WorkflowStartResult } from '../interfaces/WorkflowEngine';
5
-
6
- const mockEngine: jest.Mocked<WorkflowEngine> = {
7
- startProcess: jest.fn(),
8
- signalProcess: jest.fn(),
9
- abortProcess: jest.fn(),
10
- };
11
-
12
- @Workflow('onboarding-process')
13
- class DecoratedCommand {
14
- constructor(public readonly userId: string) {}
15
- }
16
-
17
- @Workflow('guarded-process', { fallback: 'skip' })
18
- class SkipFallbackCommand {
19
- constructor(public readonly id: string) {}
20
- }
21
-
22
- class PlainCommand {
23
- constructor(public readonly id: string) {}
24
- }
25
-
26
- describe('WorkflowBehavior', () => {
27
- let behavior: WorkflowBehavior;
28
- let next: jest.Mock;
29
-
30
- beforeEach(() => {
31
- jest.clearAllMocks();
32
- behavior = new WorkflowBehavior(mockEngine);
33
- next = jest.fn().mockResolvedValue(Result.success({ done: true }));
34
- });
35
-
36
- it('passes through commands without @Workflow decorator', async () => {
37
- const cmd = new PlainCommand('123');
38
- const result = await behavior.execute(cmd, next);
39
-
40
- expect(next).toHaveBeenCalled();
41
- expect(result.isSuccess).toBe(true);
42
- expect(mockEngine.startProcess).not.toHaveBeenCalled();
43
- });
44
-
45
- it('short-circuits decorated commands and starts a process', async () => {
46
- const startResult: WorkflowStartResult = {
47
- workflowInstanceId: 'wf-1',
48
- processInstanceId: 'pi-1',
49
- status: 'STARTED',
50
- };
51
- mockEngine.startProcess.mockResolvedValue(startResult);
52
-
53
- const cmd = new DecoratedCommand('user-42');
54
- const result = await behavior.execute(cmd, next);
55
-
56
- expect(next).not.toHaveBeenCalled();
57
- expect(mockEngine.startProcess).toHaveBeenCalledWith(
58
- 'onboarding-process',
59
- cmd,
60
- { commandType: 'DecoratedCommand' },
61
- );
62
- expect(result.isSuccess).toBe(true);
63
- expect(result.value).toEqual(startResult);
64
- });
65
-
66
- it('passes through when no WorkflowEngine is injected', async () => {
67
- const behaviorNoEngine = new WorkflowBehavior(undefined);
68
- const cmd = new DecoratedCommand('user-42');
69
-
70
- const result = await behaviorNoEngine.execute(cmd, next);
71
-
72
- expect(next).toHaveBeenCalled();
73
- expect(result.isSuccess).toBe(true);
74
- });
75
-
76
- it('applies fallback throw on engine error', async () => {
77
- mockEngine.startProcess.mockRejectedValue(new Error('connection refused'));
78
-
79
- const cmd = new DecoratedCommand('user-42');
80
- const result = await behavior.execute(cmd, next);
81
-
82
- expect(next).not.toHaveBeenCalled();
83
- expect(result.isSuccess).toBe(false);
84
- expect(result.errorMessage).toContain('Workflow start failed');
85
- expect(result.errorMessage).toContain('connection refused');
86
- });
87
-
88
- it('applies fallback skip on engine error (calls next)', async () => {
89
- mockEngine.startProcess.mockRejectedValue(new Error('timeout'));
90
-
91
- const cmd = new SkipFallbackCommand('123');
92
- const result = await behavior.execute(cmd, next);
93
-
94
- expect(next).toHaveBeenCalled();
95
- expect(result.isSuccess).toBe(true);
96
- });
97
- });
@@ -1,62 +0,0 @@
1
- import { Injectable, Optional, Inject, Logger } from '@nestjs/common';
2
- import { getWorkflowMetadata } from '../decorators/Workflow.decorator';
3
- import { WORKFLOW_ENGINE, WorkflowEngine } from '../interfaces/WorkflowEngine';
4
- import { Result, ErrorType } from '../../result/Result';
5
-
6
- @Injectable()
7
- export class WorkflowBehavior {
8
- private readonly logger = new Logger(WorkflowBehavior.name);
9
-
10
- constructor(
11
- @Optional() @Inject(WORKFLOW_ENGINE)
12
- private readonly engine?: WorkflowEngine,
13
- ) {}
14
-
15
- async execute<T>(
16
- command: object,
17
- next: () => Promise<Result<T>>,
18
- ): Promise<Result<T>> {
19
- const metadata = getWorkflowMetadata(command.constructor);
20
-
21
- if (!metadata) {
22
- return next();
23
- }
24
-
25
- if (!this.engine) {
26
- this.logger.debug('No WorkflowEngine provided, skipping workflow behavior');
27
- return next();
28
- }
29
-
30
- try {
31
- const result = await this.engine.startProcess(
32
- metadata.processDefinitionId,
33
- command,
34
- { commandType: command.constructor.name },
35
- );
36
- return Result.success(result as unknown as T);
37
- } catch (error: any) {
38
- const fallback = metadata.fallback ?? 'throw';
39
-
40
- this.logger.warn(
41
- `Workflow start failed for "${metadata.processDefinitionId}", applying fallback: ${fallback}`,
42
- error.message,
43
- );
44
-
45
- switch (fallback) {
46
- case 'skip':
47
- return next();
48
- case 'queue':
49
- return Result.failure<T>(
50
- ErrorType.InternalError,
51
- `Workflow queuing not available without companion package: ${error.message}`,
52
- );
53
- case 'throw':
54
- default:
55
- return Result.failure<T>(
56
- ErrorType.InternalError,
57
- `Workflow start failed: ${error.message}`,
58
- );
59
- }
60
- }
61
- }
62
- }
@@ -1,2 +0,0 @@
1
- /** Injection token for the Redis client (ioredis) */
2
- export const REDIS_CLIENT = 'REDIS_CLIENT';
@@ -1,24 +0,0 @@
1
- import 'reflect-metadata';
2
-
3
- const CACHE_KEY = 'arex:cache';
4
-
5
- export interface CacheOptions {
6
- /** Cache key template — use {propName} for interpolation, e.g. 'user:{id}' */
7
- key: string;
8
- /** TTL in seconds (default: 60) */
9
- ttlSeconds?: number;
10
- }
11
-
12
- /**
13
- * @Cache('key:{prop}', { ttlSeconds: 60 }) — caches query results in Redis.
14
- * Reads from cache on hit; sets on miss with TTL.
15
- */
16
- export function Cache(key: string, options: Omit<CacheOptions, 'key'> = {}): ClassDecorator {
17
- return (target: object) => {
18
- Reflect.defineMetadata(CACHE_KEY, { key, ttlSeconds: 60, ...options }, target);
19
- };
20
- }
21
-
22
- export function getCacheMetadata(target: object): CacheOptions | undefined {
23
- return Reflect.getMetadata(CACHE_KEY, target);
24
- }
@@ -1,34 +0,0 @@
1
- import 'reflect-metadata';
2
-
3
- const LOCK_KEY = 'arex:distributed-lock';
4
-
5
- export interface DistributedLockOptions {
6
- /** Lock key template — use {propName} for interpolation */
7
- key: string;
8
- /** Max time to wait for lock acquisition in seconds (default: 5) */
9
- acquireTimeoutSeconds?: number;
10
- /** Lock auto-release time in seconds (default: 30) */
11
- lockTtlSeconds?: number;
12
- }
13
-
14
- /**
15
- * @DistributedLock('key:{prop}') — acquires a Redis distributed lock before execution.
16
- * Throws ConflictException on timeout.
17
- */
18
- export function DistributedLock(
19
- key: string,
20
- options: Omit<DistributedLockOptions, 'key'> = {},
21
- ): ClassDecorator {
22
- return (target: object) => {
23
- Reflect.defineMetadata(LOCK_KEY, {
24
- key,
25
- acquireTimeoutSeconds: 5,
26
- lockTtlSeconds: 30,
27
- ...options,
28
- }, target);
29
- };
30
- }
31
-
32
- export function getDistributedLockMetadata(target: object): DistributedLockOptions | undefined {
33
- return Reflect.getMetadata(LOCK_KEY, target);
34
- }
@@ -1,23 +0,0 @@
1
- import 'reflect-metadata';
2
-
3
- const FEATURE_FLAG_KEY = 'arex:feature-flag';
4
-
5
- export interface FeatureFlagOptions {
6
- fallback?: 'throw' | 'skip' | 'default';
7
- defaultValue?: unknown;
8
- }
9
-
10
- export function FeatureFlag(
11
- flagName: string,
12
- options: FeatureFlagOptions = { fallback: 'throw' },
13
- ): ClassDecorator {
14
- return (target: object) => {
15
- Reflect.defineMetadata(FEATURE_FLAG_KEY, { flagName, ...options }, target);
16
- };
17
- }
18
-
19
- export function getFeatureFlagMetadata(
20
- target: object,
21
- ): { flagName: string } & FeatureFlagOptions | undefined {
22
- return Reflect.getMetadata(FEATURE_FLAG_KEY, target);
23
- }
@@ -1,20 +0,0 @@
1
- import { InvalidateCache, getInvalidateCacheMetadata } from './InvalidateCache.decorator';
2
-
3
- @InvalidateCache(['user:{userId}', 'users:list'])
4
- class DecoratedCommand {}
5
-
6
- class PlainCommand {}
7
-
8
- describe('InvalidateCache decorator', () => {
9
- it('should store metadata on decorated class', () => {
10
- const metadata = getInvalidateCacheMetadata(DecoratedCommand);
11
-
12
- expect(metadata).toEqual({ keys: ['user:{userId}', 'users:list'] });
13
- });
14
-
15
- it('should return undefined for undecorated class', () => {
16
- const metadata = getInvalidateCacheMetadata(PlainCommand);
17
-
18
- expect(metadata).toBeUndefined();
19
- });
20
- });
@@ -1,17 +0,0 @@
1
- import 'reflect-metadata';
2
-
3
- const INVALIDATE_CACHE_KEY = 'arex:invalidate-cache';
4
-
5
- export interface InvalidateCacheOptions {
6
- keys: string[];
7
- }
8
-
9
- export function InvalidateCache(keys: string[]): ClassDecorator {
10
- return (target: object) => {
11
- Reflect.defineMetadata(INVALIDATE_CACHE_KEY, { keys }, target);
12
- };
13
- }
14
-
15
- export function getInvalidateCacheMetadata(target: object): InvalidateCacheOptions | undefined {
16
- return Reflect.getMetadata(INVALIDATE_CACHE_KEY, target);
17
- }
@@ -1,24 +0,0 @@
1
- import 'reflect-metadata';
2
-
3
- const ISOLATED_TRANSACTION_KEY = 'arex:isolated-transaction';
4
-
5
- /**
6
- * @IsolatedTransaction() — forces a command to run in its own dedicated transaction,
7
- * even when an outer transaction scope already exists.
8
- *
9
- * Use for operations that must commit independently of the parent flow:
10
- * - Audit logging (must persist even if the business operation rolls back)
11
- * - Notifications / side-effects that should not be lost on rollback
12
- *
13
- * By default all commands share the ambient transaction (UnitOfWork pattern).
14
- * This decorator opts out of that behavior.
15
- */
16
- export function IsolatedTransaction(): ClassDecorator {
17
- return (target: object) => {
18
- Reflect.defineMetadata(ISOLATED_TRANSACTION_KEY, true, target);
19
- };
20
- }
21
-
22
- export function isIsolatedTransaction(target: object): boolean {
23
- return Reflect.getMetadata(ISOLATED_TRANSACTION_KEY, target) === true;
24
- }
@@ -1,22 +0,0 @@
1
- import 'reflect-metadata';
2
-
3
- const LOG_KEY = 'arex:log';
4
-
5
- export interface LogOptions {
6
- /** Whether to log the command/query payload (default: true) */
7
- logPayload?: boolean;
8
- }
9
-
10
- /**
11
- * @Log() — marks a command/query for automatic entry/exit logging.
12
- * Applied globally by QuanticCommandBus/QuanticQueryBus when no explicit decorator is present.
13
- */
14
- export function Log(options: LogOptions = {}): ClassDecorator {
15
- return (target: object) => {
16
- Reflect.defineMetadata(LOG_KEY, { logPayload: true, ...options }, target);
17
- };
18
- }
19
-
20
- export function getLogMetadata(target: object): LogOptions | undefined {
21
- return Reflect.getMetadata(LOG_KEY, target);
22
- }
@@ -1,39 +0,0 @@
1
- import 'reflect-metadata';
2
- import type { ICommandValidator } from '../validation/ICommandValidator';
3
-
4
- const VALIDATE_KEY = 'arex:validate';
5
- const VALIDATOR_CLASS_KEY = 'arex:validator-class';
6
-
7
- type ValidatorClass = new () => ICommandValidator;
8
-
9
- /**
10
- * @Validate(ValidatorClass) — links a command/query to its Zod-based validator.
11
- *
12
- * The ValidationBehavior instantiates the validator and calls validate()
13
- * before the handler runs. Returns Result.validationError() on failure.
14
- *
15
- * Usage:
16
- * ```typescript
17
- * @Validate(BlockUserValidator)
18
- * export class BlockUserCommand {
19
- * constructor(
20
- * public readonly blockerId: string,
21
- * public readonly blockedUserId: string,
22
- * ) {}
23
- * }
24
- * ```
25
- */
26
- export function Validate(validatorClass: ValidatorClass): ClassDecorator {
27
- return (target: object) => {
28
- Reflect.defineMetadata(VALIDATE_KEY, true, target);
29
- Reflect.defineMetadata(VALIDATOR_CLASS_KEY, validatorClass, target);
30
- };
31
- }
32
-
33
- export function shouldValidate(target: object): boolean {
34
- return Reflect.getMetadata(VALIDATE_KEY, target) === true;
35
- }
36
-
37
- export function getValidatorClass(target: object): ValidatorClass | undefined {
38
- return Reflect.getMetadata(VALIDATOR_CLASS_KEY, target);
39
- }
@@ -1,22 +0,0 @@
1
- import 'reflect-metadata';
2
-
3
- const WORKFLOW_KEY = 'arex:workflow';
4
-
5
- export interface WorkflowOptions {
6
- fallback?: 'throw' | 'skip' | 'queue';
7
- }
8
-
9
- export function Workflow(
10
- processDefinitionId: string,
11
- options?: WorkflowOptions,
12
- ): ClassDecorator {
13
- return (target: object) => {
14
- Reflect.defineMetadata(WORKFLOW_KEY, { processDefinitionId, ...options }, target);
15
- };
16
- }
17
-
18
- export function getWorkflowMetadata(
19
- target: object,
20
- ): { processDefinitionId: string } & WorkflowOptions | undefined {
21
- return Reflect.getMetadata(WORKFLOW_KEY, target);
22
- }
@@ -1,19 +0,0 @@
1
- export const WORKFLOW_ENGINE = Symbol('WORKFLOW_ENGINE');
2
-
3
- export interface WorkflowStartResult {
4
- workflowInstanceId: string;
5
- processInstanceId: string;
6
- status: 'STARTED';
7
- }
8
-
9
- export interface WorkflowEngine {
10
- startProcess(
11
- processDefinitionId: string,
12
- command: object,
13
- metadata: { commandType: string; correlationId?: string },
14
- ): Promise<WorkflowStartResult>;
15
-
16
- signalProcess(processInstanceId: string, signal: string, data?: unknown): Promise<void>;
17
-
18
- abortProcess(processInstanceId: string): Promise<void>;
19
- }
@@ -1,69 +0,0 @@
1
- import { Inject, Injectable, Logger, OnModuleInit, Optional } from '@nestjs/common';
2
- import { CommandBus } from '@nestjs/cqrs';
3
- import { Result } from '../../result/Result';
4
- import { LogBehavior } from '../behaviors/LogBehavior';
5
- import { FeatureFlagBehavior } from '../behaviors/FeatureFlagBehavior';
6
- import { ValidationBehavior } from '../behaviors/ValidationBehavior';
7
- import { CacheBehavior } from '../behaviors/CacheBehavior';
8
- import { DistributedLockBehavior } from '../behaviors/DistributedLockBehavior';
9
- import { TransactionalBehavior } from '../behaviors/TransactionalBehavior';
10
- import { PerformanceBehavior } from '../behaviors/PerformanceBehavior';
11
- import { WorkflowBehavior } from '../behaviors/WorkflowBehavior';
12
- import { InvalidateCacheBehavior } from '../behaviors/InvalidateCacheBehavior';
13
- import { BehaviorFn, runPipeline } from './runPipeline';
14
-
15
- /**
16
- * QuanticCommandBus — wraps the @nestjs/cqrs CommandBus with a behavior pipeline.
17
- *
18
- * Pipeline order:
19
- * InvalidateCache → Log → Performance → FeatureFlag → Validate → Workflow → Cache → DistributedLock → Transactional → Handler
20
- *
21
- * Controllers inject the standard CommandBus from @nestjs/cqrs.
22
- * This class patches its execute() in onModuleInit so the pipeline is transparent.
23
- */
24
- @Injectable()
25
- export class QuanticCommandBus implements OnModuleInit {
26
- private readonly logger = new Logger(QuanticCommandBus.name);
27
- private behaviors: BehaviorFn[] = [];
28
- private originalExecute!: CommandBus['execute'];
29
-
30
- constructor(
31
- private readonly commandBus: CommandBus,
32
- private readonly logBehavior: LogBehavior,
33
- private readonly performanceBehavior: PerformanceBehavior,
34
- @Optional() @Inject(FeatureFlagBehavior) private readonly featureFlagBehavior: FeatureFlagBehavior | undefined,
35
- private readonly validationBehavior: ValidationBehavior,
36
- @Optional() @Inject(WorkflowBehavior) private readonly workflowBehavior: WorkflowBehavior | undefined,
37
- private readonly cacheBehavior: CacheBehavior,
38
- private readonly distributedLockBehavior: DistributedLockBehavior,
39
- private readonly transactionalBehavior: TransactionalBehavior,
40
- private readonly invalidateCacheBehavior: InvalidateCacheBehavior,
41
- ) {}
42
-
43
- onModuleInit() {
44
- const optional = (behavior: { execute: BehaviorFn } | undefined): BehaviorFn[] =>
45
- behavior ? [(cmd, next) => behavior.execute(cmd, next)] : [];
46
-
47
- this.behaviors = [
48
- (cmd, next) => this.invalidateCacheBehavior.execute(cmd, next),
49
- (cmd, next) => this.logBehavior.execute(cmd, next),
50
- (cmd, next) => this.performanceBehavior.execute(cmd, next),
51
- ...optional(this.featureFlagBehavior),
52
- (cmd, next) => this.validationBehavior.execute(cmd, next),
53
- ...optional(this.workflowBehavior),
54
- (cmd, next) => this.cacheBehavior.execute(cmd, next),
55
- (cmd, next) => this.distributedLockBehavior.execute(cmd, next),
56
- (cmd, next) => this.transactionalBehavior.execute(cmd, next),
57
- ];
58
-
59
- this.originalExecute = this.commandBus.execute.bind(this.commandBus);
60
-
61
- const self = this;
62
- this.commandBus.execute = <T>(command: object): Promise<T> => {
63
- const handler = () => self.originalExecute(command) as Promise<Result<T>>;
64
- return runPipeline(command, handler, self.behaviors) as Promise<T>;
65
- };
66
-
67
- this.logger.log(`Command pipeline initialized — ${this.behaviors.length} behaviors`);
68
- }
69
- }