@magek/core 0.0.7 → 0.0.9
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.
- package/dist/decorators/field-metadata-reader.js +11 -0
- package/dist/decorators/index.d.ts +1 -0
- package/dist/decorators/index.js +1 -0
- package/dist/decorators/returns.d.ts +37 -0
- package/dist/decorators/returns.js +154 -0
- package/dist/event-processor.js +6 -5
- package/dist/services/read-model-store.js +4 -3
- package/dist/subscribers-notifier.js +2 -1
- package/dist/utils/promises.d.ts +25 -0
- package/dist/utils/promises.js +43 -0
- package/package.json +3 -3
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.buildClassMetadataFromFields = buildClassMetadataFromFields;
|
|
4
4
|
const decorator_types_1 = require("./decorator-types");
|
|
5
5
|
const read_model_1 = require("./read-model");
|
|
6
|
+
const returns_1 = require("./returns");
|
|
6
7
|
/**
|
|
7
8
|
* Extract TypeMetadata from a @field() decorator's metadata
|
|
8
9
|
*/
|
|
@@ -200,6 +201,16 @@ function buildClassMetadataFromFields(classType, contextMetadata) {
|
|
|
200
201
|
}));
|
|
201
202
|
// Get getter methods (for @calculatedField)
|
|
202
203
|
const methods = getAllGetters(classType, contextMetadata);
|
|
204
|
+
// Check for @returns decorated handle method
|
|
205
|
+
const handleReturnType = (0, returns_1.getReturnTypeMetadata)(contextMetadata, 'handle');
|
|
206
|
+
if (handleReturnType) {
|
|
207
|
+
// Add handle method with its return type to methods array
|
|
208
|
+
methods.push({
|
|
209
|
+
name: 'handle',
|
|
210
|
+
typeInfo: handleReturnType,
|
|
211
|
+
dependencies: [],
|
|
212
|
+
});
|
|
213
|
+
}
|
|
203
214
|
return {
|
|
204
215
|
name: classType.name,
|
|
205
216
|
type: classType,
|
|
@@ -10,6 +10,7 @@ export * from './global-error-handler';
|
|
|
10
10
|
export * from './notification';
|
|
11
11
|
export * from './projects';
|
|
12
12
|
export * from './read-model';
|
|
13
|
+
export * from './returns';
|
|
13
14
|
export * from './role';
|
|
14
15
|
export * from './scheduled-command';
|
|
15
16
|
export * from './schema-migration';
|
package/dist/decorators/index.js
CHANGED
|
@@ -12,6 +12,7 @@ tslib_1.__exportStar(require("./global-error-handler"), exports);
|
|
|
12
12
|
tslib_1.__exportStar(require("./notification"), exports);
|
|
13
13
|
tslib_1.__exportStar(require("./projects"), exports);
|
|
14
14
|
tslib_1.__exportStar(require("./read-model"), exports);
|
|
15
|
+
tslib_1.__exportStar(require("./returns"), exports);
|
|
15
16
|
tslib_1.__exportStar(require("./role"), exports);
|
|
16
17
|
tslib_1.__exportStar(require("./scheduled-command"), exports);
|
|
17
18
|
tslib_1.__exportStar(require("./schema-migration"), exports);
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { TypeFunction, TypeMetadata } from '@magek/common';
|
|
2
|
+
import { MethodDecoratorContext, DecoratorMetadataObject } from './decorator-types';
|
|
3
|
+
/** Symbol used to store return type metadata in decorator context.metadata */
|
|
4
|
+
export declare const RETURNS_METADATA_KEY: unique symbol;
|
|
5
|
+
/** Metadata stored for each method's return type */
|
|
6
|
+
export interface ReturnsMetadata {
|
|
7
|
+
methodName: string;
|
|
8
|
+
typeFunction: TypeFunction;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get the return type metadata for a method from decorator metadata.
|
|
12
|
+
*
|
|
13
|
+
* @param contextMetadata - The context.metadata from a class decorator
|
|
14
|
+
* @param methodName - The name of the method to get return type for
|
|
15
|
+
*/
|
|
16
|
+
export declare function getReturnTypeMetadata(contextMetadata: DecoratorMetadataObject | undefined, methodName: string): TypeMetadata | undefined;
|
|
17
|
+
/**
|
|
18
|
+
* @returns() decorator for explicit return type declaration on methods.
|
|
19
|
+
*
|
|
20
|
+
* Uses TC39 Stage 3 decorators to capture return type metadata that is used
|
|
21
|
+
* for GraphQL schema generation. The type specified should be the GraphQL
|
|
22
|
+
* return type (not wrapped in Promise).
|
|
23
|
+
*
|
|
24
|
+
* Usage:
|
|
25
|
+
* @returns(type => UUID)
|
|
26
|
+
* public static async handle(...): Promise<UUID> { ... }
|
|
27
|
+
*
|
|
28
|
+
* @returns(type => String)
|
|
29
|
+
* public static async handle(...): Promise<string> { ... }
|
|
30
|
+
*
|
|
31
|
+
* @returns(type => [CartItem])
|
|
32
|
+
* public static async handle(...): Promise<CartItem[]> { ... }
|
|
33
|
+
*/
|
|
34
|
+
export declare function returns(typeFunction: TypeFunction): MethodDecorator;
|
|
35
|
+
/** Type for the returns decorator */
|
|
36
|
+
type MethodDecorator = (target: unknown, context: MethodDecoratorContext) => void;
|
|
37
|
+
export {};
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.RETURNS_METADATA_KEY = void 0;
|
|
4
|
+
exports.getReturnTypeMetadata = getReturnTypeMetadata;
|
|
5
|
+
exports.returns = returns;
|
|
6
|
+
/** Symbol used to store return type metadata in decorator context.metadata */
|
|
7
|
+
exports.RETURNS_METADATA_KEY = Symbol.for('magek:returns');
|
|
8
|
+
/**
|
|
9
|
+
* Analyze a type and convert it to TypeMetadata for return types.
|
|
10
|
+
* This returns the GraphQL-compatible type directly (not wrapped in Promise).
|
|
11
|
+
*/
|
|
12
|
+
function analyzeReturnType(targetType) {
|
|
13
|
+
// Handle primitives
|
|
14
|
+
if (targetType === String) {
|
|
15
|
+
return {
|
|
16
|
+
name: 'string',
|
|
17
|
+
typeGroup: 'String',
|
|
18
|
+
typeName: 'String',
|
|
19
|
+
parameters: [],
|
|
20
|
+
isNullable: false,
|
|
21
|
+
isGetAccessor: false,
|
|
22
|
+
type: String,
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
if (targetType === Number) {
|
|
26
|
+
return {
|
|
27
|
+
name: 'number',
|
|
28
|
+
typeGroup: 'Number',
|
|
29
|
+
typeName: 'Number',
|
|
30
|
+
parameters: [],
|
|
31
|
+
isNullable: false,
|
|
32
|
+
isGetAccessor: false,
|
|
33
|
+
type: Number,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
if (targetType === Boolean) {
|
|
37
|
+
return {
|
|
38
|
+
name: 'boolean',
|
|
39
|
+
typeGroup: 'Boolean',
|
|
40
|
+
typeName: 'Boolean',
|
|
41
|
+
parameters: [],
|
|
42
|
+
isNullable: false,
|
|
43
|
+
isGetAccessor: false,
|
|
44
|
+
type: Boolean,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
// Handle void - return never to signal void return
|
|
48
|
+
if (targetType === undefined || targetType === null) {
|
|
49
|
+
return {
|
|
50
|
+
name: 'never',
|
|
51
|
+
typeGroup: 'Other',
|
|
52
|
+
typeName: 'never',
|
|
53
|
+
parameters: [],
|
|
54
|
+
isNullable: false,
|
|
55
|
+
isGetAccessor: false,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// Handle Array
|
|
59
|
+
if (Array.isArray(targetType)) {
|
|
60
|
+
if (targetType.length === 0) {
|
|
61
|
+
throw new Error('@returns decorator array type must specify an element type, e.g., @returns(type => [String])');
|
|
62
|
+
}
|
|
63
|
+
const elementType = targetType[0];
|
|
64
|
+
const elementMetadata = analyzeReturnType(elementType);
|
|
65
|
+
return {
|
|
66
|
+
name: `${elementMetadata.name}[]`,
|
|
67
|
+
typeGroup: 'Array',
|
|
68
|
+
typeName: 'Array',
|
|
69
|
+
parameters: [elementMetadata],
|
|
70
|
+
isNullable: false,
|
|
71
|
+
isGetAccessor: false,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
// Handle Class types (e.g., UUID, custom types)
|
|
75
|
+
if (typeof targetType === 'function') {
|
|
76
|
+
return {
|
|
77
|
+
name: targetType.name || 'Unknown',
|
|
78
|
+
typeGroup: 'Class',
|
|
79
|
+
typeName: targetType.name,
|
|
80
|
+
parameters: [],
|
|
81
|
+
isNullable: false,
|
|
82
|
+
isGetAccessor: false,
|
|
83
|
+
type: targetType,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// Default fallback
|
|
87
|
+
return {
|
|
88
|
+
name: 'unknown',
|
|
89
|
+
typeGroup: 'Other',
|
|
90
|
+
typeName: 'unknown',
|
|
91
|
+
parameters: [],
|
|
92
|
+
isNullable: false,
|
|
93
|
+
isGetAccessor: false,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Get the return type metadata for a method from decorator metadata.
|
|
98
|
+
*
|
|
99
|
+
* @param contextMetadata - The context.metadata from a class decorator
|
|
100
|
+
* @param methodName - The name of the method to get return type for
|
|
101
|
+
*/
|
|
102
|
+
function getReturnTypeMetadata(contextMetadata, methodName) {
|
|
103
|
+
if (!contextMetadata) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
const returnsMetadataList = contextMetadata[exports.RETURNS_METADATA_KEY];
|
|
107
|
+
if (!returnsMetadataList) {
|
|
108
|
+
return undefined;
|
|
109
|
+
}
|
|
110
|
+
const metadata = returnsMetadataList.find((m) => m.methodName === methodName);
|
|
111
|
+
if (!metadata) {
|
|
112
|
+
return undefined;
|
|
113
|
+
}
|
|
114
|
+
// Evaluate the type function and analyze the result
|
|
115
|
+
const typeResult = metadata.typeFunction();
|
|
116
|
+
// Return the actual type (not wrapped in Promise) - this is the GraphQL return type
|
|
117
|
+
return analyzeReturnType(typeResult);
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* @returns() decorator for explicit return type declaration on methods.
|
|
121
|
+
*
|
|
122
|
+
* Uses TC39 Stage 3 decorators to capture return type metadata that is used
|
|
123
|
+
* for GraphQL schema generation. The type specified should be the GraphQL
|
|
124
|
+
* return type (not wrapped in Promise).
|
|
125
|
+
*
|
|
126
|
+
* Usage:
|
|
127
|
+
* @returns(type => UUID)
|
|
128
|
+
* public static async handle(...): Promise<UUID> { ... }
|
|
129
|
+
*
|
|
130
|
+
* @returns(type => String)
|
|
131
|
+
* public static async handle(...): Promise<string> { ... }
|
|
132
|
+
*
|
|
133
|
+
* @returns(type => [CartItem])
|
|
134
|
+
* public static async handle(...): Promise<CartItem[]> { ... }
|
|
135
|
+
*/
|
|
136
|
+
function returns(typeFunction) {
|
|
137
|
+
return function returnsDecorator(_target, context) {
|
|
138
|
+
const methodName = context.name.toString();
|
|
139
|
+
// Store in context.metadata for later retrieval by class decorators
|
|
140
|
+
if (context.metadata) {
|
|
141
|
+
if (!context.metadata[exports.RETURNS_METADATA_KEY]) {
|
|
142
|
+
context.metadata[exports.RETURNS_METADATA_KEY] = [];
|
|
143
|
+
}
|
|
144
|
+
const returnsList = context.metadata[exports.RETURNS_METADATA_KEY];
|
|
145
|
+
// Remove any existing entry for this method (in case of decorator re-application)
|
|
146
|
+
const filteredList = returnsList.filter((m) => m.methodName !== methodName);
|
|
147
|
+
filteredList.push({
|
|
148
|
+
methodName,
|
|
149
|
+
typeFunction,
|
|
150
|
+
});
|
|
151
|
+
context.metadata[exports.RETURNS_METADATA_KEY] = filteredList;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
package/dist/event-processor.js
CHANGED
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.MagekEventProcessor = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const common_1 = require("@magek/common");
|
|
6
|
+
const promises_1 = require("./utils/promises");
|
|
6
7
|
const register_handler_1 = require("./register-handler");
|
|
7
8
|
const global_error_dispatcher_1 = require("./global-error-dispatcher");
|
|
8
9
|
const instrumentation_1 = require("./instrumentation");
|
|
@@ -33,7 +34,7 @@ let MagekEventProcessor = (() => {
|
|
|
33
34
|
if (!(entityName in config.topicToEvent)) {
|
|
34
35
|
eventEnvelopesProcessors.push(MagekEventProcessor.snapshotAndUpdateReadModels(config, entityName, entityID, eventStore, readModelStore));
|
|
35
36
|
}
|
|
36
|
-
await
|
|
37
|
+
await promises_1.Promises.allSettledAndFulfilled(eventEnvelopesProcessors);
|
|
37
38
|
};
|
|
38
39
|
}
|
|
39
40
|
static async filterDispatched(config, eventEnvelopes, eventStore) {
|
|
@@ -66,7 +67,7 @@ let MagekEventProcessor = (() => {
|
|
|
66
67
|
static async dispatchEntityEventsToEventHandlers(entityEventEnvelopes, config) {
|
|
67
68
|
const logger = (0, common_1.getLogger)(config, 'MagekEventDispatcher.dispatchEntityEventsToEventHandlers');
|
|
68
69
|
try {
|
|
69
|
-
await
|
|
70
|
+
await promises_1.Promises.allSettledAndFulfilled(entityEventEnvelopes.map(async (eventEnvelope) => {
|
|
70
71
|
let eventHandlers = config.eventHandlers[eventEnvelope.typeName] || [];
|
|
71
72
|
const globalEventHandler = config.eventHandlers[decorators_1.GLOBAL_EVENT_HANDLERS];
|
|
72
73
|
if (globalEventHandler && globalEventHandler.length > 0) {
|
|
@@ -79,13 +80,13 @@ let MagekEventProcessor = (() => {
|
|
|
79
80
|
const eventInstance = this.getEventInstance(config, eventEnvelope);
|
|
80
81
|
if (eventHandlers && eventHandlers.length > 0) {
|
|
81
82
|
try {
|
|
82
|
-
await
|
|
83
|
+
await promises_1.Promises.allSettledAndFulfilled(eventHandlers.map(async (eventHandler) => {
|
|
83
84
|
logger.debug('Calling "handle" method on event handler: ', eventHandler);
|
|
84
85
|
await this.callEventHandler(eventHandler, eventInstance, eventEnvelope, config);
|
|
85
86
|
}));
|
|
86
87
|
}
|
|
87
88
|
catch (error) {
|
|
88
|
-
if (error instanceof
|
|
89
|
+
if (error instanceof promises_1.PromisesError) {
|
|
89
90
|
logger.error(`Failed to process handlers for event ${eventEnvelope.typeName}:`, error.failedReasons);
|
|
90
91
|
}
|
|
91
92
|
else {
|
|
@@ -96,7 +97,7 @@ let MagekEventProcessor = (() => {
|
|
|
96
97
|
}));
|
|
97
98
|
}
|
|
98
99
|
catch (error) {
|
|
99
|
-
if (error instanceof
|
|
100
|
+
if (error instanceof promises_1.PromisesError) {
|
|
100
101
|
logger.error('Failed to process events:', error.failedReasons);
|
|
101
102
|
}
|
|
102
103
|
else {
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ReadModelStore = void 0;
|
|
4
4
|
const common_1 = require("@magek/common");
|
|
5
|
+
const promises_1 = require("../utils/promises");
|
|
5
6
|
const global_error_dispatcher_1 = require("../global-error-dispatcher");
|
|
6
7
|
const read_model_searcher_1 = require("./read-model-searcher");
|
|
7
8
|
const read_model_schema_migrator_1 = require("../read-model-schema-migrator");
|
|
@@ -28,7 +29,7 @@ class ReadModelStore {
|
|
|
28
29
|
const sequenceKey = this.sequenceKeyForProjection(entityInstance, projectionMetadata);
|
|
29
30
|
return this.projectEntity(entityInstance, projectionMetadata, entityMetadata, entitySnapshotEnvelope, readModelName, deleteEvent, sequenceKey);
|
|
30
31
|
});
|
|
31
|
-
await
|
|
32
|
+
await promises_1.Promises.allSettledAndFulfilled(projectReadModelPromises);
|
|
32
33
|
}
|
|
33
34
|
/**
|
|
34
35
|
* Gets the read models for a given entity instance using the projection metadata
|
|
@@ -123,10 +124,10 @@ class ReadModelStore {
|
|
|
123
124
|
const newProjections = await this.projectionsForReadModels(entitySnapshotEnvelope, readModelName, sequenceKey, projectionMetadata, entityInstance, entityMetadata, deleteEvent, currentReadModel);
|
|
124
125
|
existingReadModelsProjections.push(...newProjections);
|
|
125
126
|
}
|
|
126
|
-
return
|
|
127
|
+
return promises_1.Promises.allSettledAndFulfilled(existingReadModelsProjections);
|
|
127
128
|
}
|
|
128
129
|
const newProjections = await this.projectionsForReadModels(entitySnapshotEnvelope, readModelName, sequenceKey, projectionMetadata, entityInstance, entityMetadata, deleteEvent);
|
|
129
|
-
return
|
|
130
|
+
return promises_1.Promises.allSettledAndFulfilled(newProjections);
|
|
130
131
|
}
|
|
131
132
|
async projectionsForReadModels(entitySnapshotEnvelope, readModelName, sequenceKey, projectionMetadata, entityInstance, entityMetadata, deleteEvent, currentReadModel) {
|
|
132
133
|
const projections = [];
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.MagekSubscribersNotifier = void 0;
|
|
4
4
|
const tslib_1 = require("tslib");
|
|
5
5
|
const common_1 = require("@magek/common");
|
|
6
|
+
const promises_1 = require("./utils/promises");
|
|
6
7
|
const graphql = require("graphql");
|
|
7
8
|
const graphql_generator_1 = require("./services/graphql/graphql-generator");
|
|
8
9
|
const read_model_pub_sub_1 = require("./services/pub-sub/read-model-pub-sub");
|
|
@@ -32,7 +33,7 @@ let MagekSubscribersNotifier = (() => {
|
|
|
32
33
|
const subscriptions = await this.getSubscriptions(readModelEnvelopes);
|
|
33
34
|
logger.debug('Found the following subscriptions for those read models: ', subscriptions);
|
|
34
35
|
const pubSub = this.getPubSub(readModelEnvelopes);
|
|
35
|
-
await
|
|
36
|
+
await promises_1.Promises.allSettledAndFulfilled(subscriptions.map(this.runSubscriptionAndNotify.bind(this, pubSub)));
|
|
36
37
|
}
|
|
37
38
|
catch (e) {
|
|
38
39
|
logger.error(e);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
export declare class Promises {
|
|
2
|
+
/**
|
|
3
|
+
* Waits until all the passed promise-like values are settled, no matter if they were fulfilled or rejected.
|
|
4
|
+
* If some rejected were found, an array with all the rejected promises is thrown.
|
|
5
|
+
* If all were fulfilled, an array of PromiseFulfilledResult is returned
|
|
6
|
+
* @param values Array of promise-like values to be wait for
|
|
7
|
+
* @throws an array of PromiseRejectedResult with all the rejected promises, if any
|
|
8
|
+
*
|
|
9
|
+
* Comparison with other similar Promise methods:
|
|
10
|
+
* - `Promise.all`: This has an "all-or-nothing" behavior. As long as one of the promises is rejected, the result is
|
|
11
|
+
* rejected. More importantly, **it does not wait for al the promises to finish**, which could lead to undesired behaviors
|
|
12
|
+
* - `Promise.allSettled`: This method waits for all the promises to finish and then returns an array of results. Some
|
|
13
|
+
* of them will be fulfilled and some rejected. More importantly, **it never throws an error**, which could lead to
|
|
14
|
+
* unexpected consequences. For example if you do "await Promise.allSettle(...)" expecting it to throw if some of them
|
|
15
|
+
* failed, you won't get that.
|
|
16
|
+
*
|
|
17
|
+
* In brief, `Promises.allSettledAndFulfilled` behaves exactly the same way as `Promise.allSettle` but it throws with
|
|
18
|
+
* an array of the failed promises, only if there are any.
|
|
19
|
+
*/
|
|
20
|
+
static allSettledAndFulfilled<TValue>(values: Iterable<TValue>): ReturnType<PromiseConstructor['allSettled']>;
|
|
21
|
+
}
|
|
22
|
+
export declare class PromisesError extends Error {
|
|
23
|
+
readonly failedReasons: Array<unknown>;
|
|
24
|
+
constructor(rejectedResults: Array<PromiseRejectedResult>);
|
|
25
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.PromisesError = exports.Promises = void 0;
|
|
4
|
+
class Promises {
|
|
5
|
+
/**
|
|
6
|
+
* Waits until all the passed promise-like values are settled, no matter if they were fulfilled or rejected.
|
|
7
|
+
* If some rejected were found, an array with all the rejected promises is thrown.
|
|
8
|
+
* If all were fulfilled, an array of PromiseFulfilledResult is returned
|
|
9
|
+
* @param values Array of promise-like values to be wait for
|
|
10
|
+
* @throws an array of PromiseRejectedResult with all the rejected promises, if any
|
|
11
|
+
*
|
|
12
|
+
* Comparison with other similar Promise methods:
|
|
13
|
+
* - `Promise.all`: This has an "all-or-nothing" behavior. As long as one of the promises is rejected, the result is
|
|
14
|
+
* rejected. More importantly, **it does not wait for al the promises to finish**, which could lead to undesired behaviors
|
|
15
|
+
* - `Promise.allSettled`: This method waits for all the promises to finish and then returns an array of results. Some
|
|
16
|
+
* of them will be fulfilled and some rejected. More importantly, **it never throws an error**, which could lead to
|
|
17
|
+
* unexpected consequences. For example if you do "await Promise.allSettle(...)" expecting it to throw if some of them
|
|
18
|
+
* failed, you won't get that.
|
|
19
|
+
*
|
|
20
|
+
* In brief, `Promises.allSettledAndFulfilled` behaves exactly the same way as `Promise.allSettle` but it throws with
|
|
21
|
+
* an array of the failed promises, only if there are any.
|
|
22
|
+
*/
|
|
23
|
+
static async allSettledAndFulfilled(values) {
|
|
24
|
+
const results = await Promise.allSettled(values); // Promise.allSettled never throws
|
|
25
|
+
// Get all the failed promises
|
|
26
|
+
const failed = results.filter((result) => result.status === 'rejected');
|
|
27
|
+
// Throw if we found any failed ones
|
|
28
|
+
if (failed.length > 0) {
|
|
29
|
+
throw new PromisesError(failed);
|
|
30
|
+
}
|
|
31
|
+
return results;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.Promises = Promises;
|
|
35
|
+
class PromisesError extends Error {
|
|
36
|
+
failedReasons;
|
|
37
|
+
constructor(rejectedResults) {
|
|
38
|
+
const reasons = rejectedResults.map((res) => res.reason);
|
|
39
|
+
super(reasons.join('. '));
|
|
40
|
+
this.failedReasons = reasons;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
exports.PromisesError = PromisesError;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magek/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.9",
|
|
4
4
|
"description": "Library for your Magek apps",
|
|
5
5
|
"author": "Boosterin Labs SLU",
|
|
6
6
|
"homepage": "https://magek.ai",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"node": ">=22.0.0 <23.0.0"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"@magek/common": "^0.0.
|
|
26
|
+
"@magek/common": "^0.0.9",
|
|
27
27
|
"fp-ts": "2.16.11",
|
|
28
28
|
"graphql-scalars": "1.25.0",
|
|
29
29
|
"inflected": "2.1.0",
|
|
@@ -35,7 +35,7 @@
|
|
|
35
35
|
"fast-check": "4.5.3"
|
|
36
36
|
},
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@magek/eslint-config": "^0.0.
|
|
38
|
+
"@magek/eslint-config": "^0.0.9",
|
|
39
39
|
"@types/chai": "5.2.3",
|
|
40
40
|
"@types/chai-as-promised": "8.0.2",
|
|
41
41
|
"@types/inflected": "2.1.3",
|