@nest-yalc-2/api-strategy 1.3.3

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 (82) hide show
  1. package/LICENSE +661 -0
  2. package/README.md +40 -0
  3. package/package.json +33 -0
  4. package/src/__tests__/call-strategy-wrappers.spec.ts +217 -0
  5. package/src/__tests__/context-call.service.spec.ts +52 -0
  6. package/src/__tests__/context-event.service.spec.ts +52 -0
  7. package/src/__tests__/event-strategy-composition.spec.ts +185 -0
  8. package/src/__tests__/header-whitelist.helper.spec.ts +30 -0
  9. package/src/__tests__/http-abstract-call.strategy.spec.ts +17 -0
  10. package/src/__tests__/nest-http-call.strategy.spec.ts +164 -0
  11. package/src/__tests__/nest-local-call.strategy.spec.ts +263 -0
  12. package/src/__tests__/nest-local-event.strategy.spec.ts +55 -0
  13. package/src/__tests__/rabbitmq-event.strategy.spec.ts +220 -0
  14. package/src/__tests__/strategy-selector.provider.spec.ts +182 -0
  15. package/src/context-call.interface.d.ts +12 -0
  16. package/src/context-call.interface.js +3 -0
  17. package/src/context-call.interface.js.map +1 -0
  18. package/src/context-call.interface.ts +49 -0
  19. package/src/context-call.service.d.ts +7 -0
  20. package/src/context-call.service.js +32 -0
  21. package/src/context-call.service.js.map +1 -0
  22. package/src/context-call.service.ts +31 -0
  23. package/src/context-event.interface.d.ts +4 -0
  24. package/src/context-event.interface.js +3 -0
  25. package/src/context-event.interface.js.map +1 -0
  26. package/src/context-event.interface.ts +4 -0
  27. package/src/context-event.service.d.ts +7 -0
  28. package/src/context-event.service.js +32 -0
  29. package/src/context-event.service.js.map +1 -0
  30. package/src/context-event.service.ts +31 -0
  31. package/src/header-whitelist.helper.d.ts +2 -0
  32. package/src/header-whitelist.helper.js +13 -0
  33. package/src/header-whitelist.helper.js.map +1 -0
  34. package/src/header-whitelist.helper.ts +12 -0
  35. package/src/index.d.ts +15 -0
  36. package/src/index.js +32 -0
  37. package/src/index.js.map +1 -0
  38. package/src/index.ts +17 -0
  39. package/src/strategies/composite-event.strategy.d.ts +14 -0
  40. package/src/strategies/composite-event.strategy.js +52 -0
  41. package/src/strategies/composite-event.strategy.js.map +1 -0
  42. package/src/strategies/composite-event.strategy.ts +76 -0
  43. package/src/strategies/conditional-call.strategy.d.ts +17 -0
  44. package/src/strategies/conditional-call.strategy.js +41 -0
  45. package/src/strategies/conditional-call.strategy.js.map +1 -0
  46. package/src/strategies/conditional-call.strategy.ts +70 -0
  47. package/src/strategies/conditional-event.strategy.d.ts +16 -0
  48. package/src/strategies/conditional-event.strategy.js +38 -0
  49. package/src/strategies/conditional-event.strategy.js.map +1 -0
  50. package/src/strategies/conditional-event.strategy.ts +54 -0
  51. package/src/strategies/fallback-call.strategy.d.ts +14 -0
  52. package/src/strategies/fallback-call.strategy.js +41 -0
  53. package/src/strategies/fallback-call.strategy.js.map +1 -0
  54. package/src/strategies/fallback-call.strategy.ts +67 -0
  55. package/src/strategies/http-abstract-call.strategy.d.ts +44 -0
  56. package/src/strategies/http-abstract-call.strategy.js +19 -0
  57. package/src/strategies/http-abstract-call.strategy.js.map +1 -0
  58. package/src/strategies/http-abstract-call.strategy.ts +238 -0
  59. package/src/strategies/nest-http-call.strategy.d.ts +30 -0
  60. package/src/strategies/nest-http-call.strategy.js +68 -0
  61. package/src/strategies/nest-http-call.strategy.js.map +1 -0
  62. package/src/strategies/nest-http-call.strategy.ts +121 -0
  63. package/src/strategies/nest-local-call.strategy.d.ts +32 -0
  64. package/src/strategies/nest-local-call.strategy.js +84 -0
  65. package/src/strategies/nest-local-call.strategy.js.map +1 -0
  66. package/src/strategies/nest-local-call.strategy.ts +153 -0
  67. package/src/strategies/nest-local-event.strategy.d.ts +17 -0
  68. package/src/strategies/nest-local-event.strategy.js +30 -0
  69. package/src/strategies/nest-local-event.strategy.js.map +1 -0
  70. package/src/strategies/nest-local-event.strategy.ts +42 -0
  71. package/src/strategies/rabbitmq-event.strategy.d.ts +34 -0
  72. package/src/strategies/rabbitmq-event.strategy.js +97 -0
  73. package/src/strategies/rabbitmq-event.strategy.js.map +1 -0
  74. package/src/strategies/rabbitmq-event.strategy.ts +154 -0
  75. package/src/strategies/shadow-call.strategy.d.ts +17 -0
  76. package/src/strategies/shadow-call.strategy.js +42 -0
  77. package/src/strategies/shadow-call.strategy.js.map +1 -0
  78. package/src/strategies/shadow-call.strategy.ts +87 -0
  79. package/src/strategy-selector.provider.d.ts +19 -0
  80. package/src/strategy-selector.provider.js +52 -0
  81. package/src/strategy-selector.provider.js.map +1 -0
  82. package/src/strategy-selector.provider.ts +103 -0
@@ -0,0 +1,87 @@
1
+ import {
2
+ IApiCallStrategy,
3
+ ICallOptions,
4
+ IObjectWithData,
5
+ } from '../context-call.interface.js';
6
+
7
+ export type ShadowCallStrategyErrorMode = 'throw' | 'ignore';
8
+
9
+ export interface ShadowCallStrategyOptions {
10
+ awaitShadows?: boolean;
11
+ shadowErrorMode?: ShadowCallStrategyErrorMode;
12
+ }
13
+
14
+ export class ShadowCallStrategy implements IApiCallStrategy {
15
+ private readonly awaitShadows: boolean;
16
+ private readonly shadowErrorMode: ShadowCallStrategyErrorMode;
17
+
18
+ constructor(
19
+ private readonly primary: IApiCallStrategy,
20
+ private readonly shadows: IApiCallStrategy[],
21
+ options: ShadowCallStrategyOptions = {},
22
+ ) {
23
+ if (shadows.length === 0) {
24
+ throw new Error(
25
+ 'ShadowCallStrategy requires at least one shadow strategy.',
26
+ );
27
+ }
28
+
29
+ this.shadowErrorMode = options.shadowErrorMode ?? 'ignore';
30
+ this.awaitShadows =
31
+ options.awaitShadows ?? this.shadowErrorMode === 'throw';
32
+ }
33
+
34
+ call<TOptData, TParams extends Record<string, any>, TResData>(
35
+ path: string,
36
+ options?: ICallOptions<TOptData, TParams>,
37
+ ): Promise<IObjectWithData<TResData>> {
38
+ return this.execute(
39
+ (strategy) => strategy.call(path, options),
40
+ (strategy) => strategy.call(path, options),
41
+ );
42
+ }
43
+
44
+ get<TOptData, TParams extends Record<string, any>, TResData>(
45
+ path: string,
46
+ options?: ICallOptions<TOptData, TParams>,
47
+ ): Promise<IObjectWithData<TResData>> {
48
+ return this.execute(
49
+ (strategy) => strategy.get(path, options),
50
+ (strategy) => strategy.get(path, options),
51
+ );
52
+ }
53
+
54
+ post<TOptData, TParams extends Record<string, any>, TResData>(
55
+ path: string,
56
+ options?: ICallOptions<TOptData, TParams>,
57
+ ): Promise<IObjectWithData<TResData>> {
58
+ return this.execute(
59
+ (strategy) => strategy.post(path, options),
60
+ (strategy) => strategy.post(path, options),
61
+ );
62
+ }
63
+
64
+ private async execute<TResData>(
65
+ primaryCall: (
66
+ strategy: IApiCallStrategy,
67
+ ) => Promise<IObjectWithData<TResData>>,
68
+ shadowCall: (strategy: IApiCallStrategy) => Promise<IObjectWithData<any>>,
69
+ ) {
70
+ const primaryResult = await primaryCall(this.primary);
71
+ const shadowExecution = Promise.all(
72
+ this.shadows.map((strategy) => shadowCall(strategy)),
73
+ );
74
+
75
+ if (this.awaitShadows) {
76
+ if (this.shadowErrorMode === 'throw') {
77
+ await shadowExecution;
78
+ } else {
79
+ await shadowExecution.catch(() => undefined);
80
+ }
81
+ } else {
82
+ void shadowExecution.catch(() => undefined);
83
+ }
84
+
85
+ return primaryResult;
86
+ }
87
+ }
@@ -0,0 +1,19 @@
1
+ import type { Provider } from '@nestjs/common';
2
+ import type { IApiCallStrategy } from './context-call.interface.js';
3
+ import type { IEventStrategy } from './context-event.interface.js';
4
+ export type StrategySelectorToken<TStrategy = unknown> = string | symbol | (abstract new (...args: any[]) => TStrategy) | (new (...args: any[]) => TStrategy);
5
+ export type StrategySelectorBehavior = 'throw' | 'fallback';
6
+ export interface StrategySelectorFactoryOptions {
7
+ inject?: any[];
8
+ useFactory?: (...args: any[]) => string | null | undefined | Promise<string | null | undefined>;
9
+ }
10
+ export interface StrategySelectorProviderOptions<TStrategy> {
11
+ provide: StrategySelectorToken<TStrategy>;
12
+ defaultStrategy: string;
13
+ strategies: Record<string, StrategySelectorToken<TStrategy>>;
14
+ selector?: StrategySelectorFactoryOptions;
15
+ unknownStrategyBehavior?: StrategySelectorBehavior;
16
+ }
17
+ export declare function StrategySelectorProvider<TStrategy>(options: StrategySelectorProviderOptions<TStrategy>): Provider<TStrategy>;
18
+ export declare function ApiCallStrategySelectorProvider(options: StrategySelectorProviderOptions<IApiCallStrategy>): Provider<IApiCallStrategy>;
19
+ export declare function EventStrategySelectorProvider(options: StrategySelectorProviderOptions<IEventStrategy>): Provider<IEventStrategy>;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.StrategySelectorProvider = StrategySelectorProvider;
4
+ exports.ApiCallStrategySelectorProvider = ApiCallStrategySelectorProvider;
5
+ exports.EventStrategySelectorProvider = EventStrategySelectorProvider;
6
+ function normalizeStrategyKey(strategy) {
7
+ const value = strategy?.trim();
8
+ return value ? value : undefined;
9
+ }
10
+ function StrategySelectorProvider(options) {
11
+ const strategyEntries = Object.entries(options.strategies);
12
+ if (strategyEntries.length === 0) {
13
+ throw new Error('Strategy selector requires at least one strategy.');
14
+ }
15
+ const defaultStrategyIndex = strategyEntries.findIndex(([key]) => key === options.defaultStrategy);
16
+ if (defaultStrategyIndex < 0) {
17
+ throw new Error(`Default strategy "${options.defaultStrategy}" is not registered.`);
18
+ }
19
+ const behavior = options.unknownStrategyBehavior ?? 'throw';
20
+ const selectorInject = options.selector?.inject ?? [];
21
+ return {
22
+ provide: options.provide,
23
+ useFactory: async (...args) => {
24
+ const strategyInstances = args.slice(0, strategyEntries.length);
25
+ const selectorArgs = args.slice(strategyEntries.length);
26
+ const configuredStrategy = normalizeStrategyKey(await options.selector?.useFactory?.(...selectorArgs));
27
+ const selectedStrategy = configuredStrategy ?? options.defaultStrategy;
28
+ const selectedStrategyIndex = strategyEntries.findIndex(([key]) => key === selectedStrategy);
29
+ if (selectedStrategyIndex >= 0) {
30
+ return strategyInstances[selectedStrategyIndex];
31
+ }
32
+ if (behavior === 'fallback') {
33
+ return strategyInstances[defaultStrategyIndex];
34
+ }
35
+ const availableStrategies = strategyEntries
36
+ .map(([key]) => key)
37
+ .join(', ');
38
+ throw new Error(`Unknown strategy "${selectedStrategy}". Available strategies: ${availableStrategies}.`);
39
+ },
40
+ inject: [
41
+ ...strategyEntries.map(([, strategyToken]) => strategyToken),
42
+ ...selectorInject,
43
+ ],
44
+ };
45
+ }
46
+ function ApiCallStrategySelectorProvider(options) {
47
+ return StrategySelectorProvider(options);
48
+ }
49
+ function EventStrategySelectorProvider(options) {
50
+ return StrategySelectorProvider(options);
51
+ }
52
+ //# sourceMappingURL=strategy-selector.provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"strategy-selector.provider.js","sourceRoot":"","sources":["strategy-selector.provider.ts"],"names":[],"mappings":";;AAgCA,4DA0DC;AAED,0EAIC;AAED,sEAIC;AA3ED,SAAS,oBAAoB,CAAC,QAAmC;IAC/D,MAAM,KAAK,GAAG,QAAQ,EAAE,IAAI,EAAE,CAAC;IAC/B,OAAO,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;AACnC,CAAC;AAED,SAAgB,wBAAwB,CACtC,OAAmD;IAEnD,MAAM,eAAe,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAE3D,IAAI,eAAe,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACjC,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IAED,MAAM,oBAAoB,GAAG,eAAe,CAAC,SAAS,CACpD,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,OAAO,CAAC,eAAe,CAC3C,CAAC;IAEF,IAAI,oBAAoB,GAAG,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CACb,qBAAqB,OAAO,CAAC,eAAe,sBAAsB,CACnE,CAAC;IACJ,CAAC;IAED,MAAM,QAAQ,GAAG,OAAO,CAAC,uBAAuB,IAAI,OAAO,CAAC;IAC5D,MAAM,cAAc,GAAG,OAAO,CAAC,QAAQ,EAAE,MAAM,IAAI,EAAE,CAAC;IAEtD,OAAO;QACL,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,UAAU,EAAE,KAAK,EAAE,GAAG,IAAW,EAAE,EAAE;YACnC,MAAM,iBAAiB,GAAG,IAAI,CAAC,KAAK,CAClC,CAAC,EACD,eAAe,CAAC,MAAM,CACR,CAAC;YACjB,MAAM,YAAY,GAAG,IAAI,CAAC,KAAK,CAAC,eAAe,CAAC,MAAM,CAAC,CAAC;YACxD,MAAM,kBAAkB,GAAG,oBAAoB,CAC7C,MAAM,OAAO,CAAC,QAAQ,EAAE,UAAU,EAAE,CAAC,GAAG,YAAY,CAAC,CACtD,CAAC;YACF,MAAM,gBAAgB,GAAG,kBAAkB,IAAI,OAAO,CAAC,eAAe,CAAC;YACvE,MAAM,qBAAqB,GAAG,eAAe,CAAC,SAAS,CACrD,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,KAAK,gBAAgB,CACpC,CAAC;YAEF,IAAI,qBAAqB,IAAI,CAAC,EAAE,CAAC;gBAC/B,OAAO,iBAAiB,CAAC,qBAAqB,CAAC,CAAC;YAClD,CAAC;YAED,IAAI,QAAQ,KAAK,UAAU,EAAE,CAAC;gBAC5B,OAAO,iBAAiB,CAAC,oBAAoB,CAAC,CAAC;YACjD,CAAC;YAED,MAAM,mBAAmB,GAAG,eAAe;iBACxC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC;iBACnB,IAAI,CAAC,IAAI,CAAC,CAAC;YACd,MAAM,IAAI,KAAK,CACb,qBAAqB,gBAAgB,4BAA4B,mBAAmB,GAAG,CACxF,CAAC;QACJ,CAAC;QACD,MAAM,EAAE;YACN,GAAG,eAAe,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,aAAa,CAAC,EAAE,EAAE,CAAC,aAAa,CAAC;YAC5D,GAAG,cAAc;SAClB;KACF,CAAC;AACJ,CAAC;AAED,SAAgB,+BAA+B,CAC7C,OAA0D;IAE1D,OAAO,wBAAwB,CAAC,OAAO,CAAC,CAAC;AAC3C,CAAC;AAED,SAAgB,6BAA6B,CAC3C,OAAwD;IAExD,OAAO,wBAAwB,CAAC,OAAO,CAAC,CAAC;AAC3C,CAAC"}
@@ -0,0 +1,103 @@
1
+ import type { Provider } from '@nestjs/common';
2
+ import type { IApiCallStrategy } from './context-call.interface.js';
3
+ import type { IEventStrategy } from './context-event.interface.js';
4
+
5
+ export type StrategySelectorToken<TStrategy = unknown> =
6
+ | string
7
+ | symbol
8
+ | (abstract new (...args: any[]) => TStrategy)
9
+ | (new (...args: any[]) => TStrategy);
10
+
11
+ export type StrategySelectorBehavior = 'throw' | 'fallback';
12
+
13
+ export interface StrategySelectorFactoryOptions {
14
+ inject?: any[];
15
+ useFactory?: (
16
+ ...args: any[]
17
+ ) => string | null | undefined | Promise<string | null | undefined>;
18
+ }
19
+
20
+ export interface StrategySelectorProviderOptions<TStrategy> {
21
+ provide: StrategySelectorToken<TStrategy>;
22
+ defaultStrategy: string;
23
+ strategies: Record<string, StrategySelectorToken<TStrategy>>;
24
+ selector?: StrategySelectorFactoryOptions;
25
+ unknownStrategyBehavior?: StrategySelectorBehavior;
26
+ }
27
+
28
+ function normalizeStrategyKey(strategy: string | null | undefined) {
29
+ const value = strategy?.trim();
30
+ return value ? value : undefined;
31
+ }
32
+
33
+ export function StrategySelectorProvider<TStrategy>(
34
+ options: StrategySelectorProviderOptions<TStrategy>,
35
+ ): Provider<TStrategy> {
36
+ const strategyEntries = Object.entries(options.strategies);
37
+
38
+ if (strategyEntries.length === 0) {
39
+ throw new Error('Strategy selector requires at least one strategy.');
40
+ }
41
+
42
+ const defaultStrategyIndex = strategyEntries.findIndex(
43
+ ([key]) => key === options.defaultStrategy,
44
+ );
45
+
46
+ if (defaultStrategyIndex < 0) {
47
+ throw new Error(
48
+ `Default strategy "${options.defaultStrategy}" is not registered.`,
49
+ );
50
+ }
51
+
52
+ const behavior = options.unknownStrategyBehavior ?? 'throw';
53
+ const selectorInject = options.selector?.inject ?? [];
54
+
55
+ return {
56
+ provide: options.provide,
57
+ useFactory: async (...args: any[]) => {
58
+ const strategyInstances = args.slice(
59
+ 0,
60
+ strategyEntries.length,
61
+ ) as TStrategy[];
62
+ const selectorArgs = args.slice(strategyEntries.length);
63
+ const configuredStrategy = normalizeStrategyKey(
64
+ await options.selector?.useFactory?.(...selectorArgs),
65
+ );
66
+ const selectedStrategy = configuredStrategy ?? options.defaultStrategy;
67
+ const selectedStrategyIndex = strategyEntries.findIndex(
68
+ ([key]) => key === selectedStrategy,
69
+ );
70
+
71
+ if (selectedStrategyIndex >= 0) {
72
+ return strategyInstances[selectedStrategyIndex];
73
+ }
74
+
75
+ if (behavior === 'fallback') {
76
+ return strategyInstances[defaultStrategyIndex];
77
+ }
78
+
79
+ const availableStrategies = strategyEntries
80
+ .map(([key]) => key)
81
+ .join(', ');
82
+ throw new Error(
83
+ `Unknown strategy "${selectedStrategy}". Available strategies: ${availableStrategies}.`,
84
+ );
85
+ },
86
+ inject: [
87
+ ...strategyEntries.map(([, strategyToken]) => strategyToken),
88
+ ...selectorInject,
89
+ ],
90
+ };
91
+ }
92
+
93
+ export function ApiCallStrategySelectorProvider(
94
+ options: StrategySelectorProviderOptions<IApiCallStrategy>,
95
+ ): Provider<IApiCallStrategy> {
96
+ return StrategySelectorProvider(options);
97
+ }
98
+
99
+ export function EventStrategySelectorProvider(
100
+ options: StrategySelectorProviderOptions<IEventStrategy>,
101
+ ): Provider<IEventStrategy> {
102
+ return StrategySelectorProvider(options);
103
+ }