@magek/common 0.0.6 → 0.0.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.
- package/dist/concepts/projection-metadata.d.ts +4 -4
- package/dist/concepts/projection-metadata.js +6 -6
- package/dist/concepts/reducer-metadata.d.ts +5 -0
- package/dist/concepts/reducer-metadata.js +5 -0
- package/dist/concepts/register.js +6 -1
- package/dist/config.js +74 -65
- package/dist/envelope.d.ts +1 -0
- package/dist/errors/command-handler-global-error.js +2 -0
- package/dist/errors/event-global-error.js +1 -0
- package/dist/errors/event-handler-global-error.js +3 -0
- package/dist/errors/global-error-container.js +1 -0
- package/dist/errors/projection-global-error.js +4 -0
- package/dist/errors/query-handler-global-error.js +1 -0
- package/dist/errors/reducer-global-error.js +4 -0
- package/dist/errors/schedule-command-global-error.js +2 -0
- package/dist/errors/snapshot-persist-handler-global-error.js +1 -0
- package/dist/errors.js +6 -3
- package/dist/graphql-websocket-messages.js +11 -7
- package/dist/http-service.js +2 -2
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/instances.d.ts +20 -0
- package/dist/instances.js +10 -0
- package/dist/logger.js +5 -6
- package/dist/metadata-types.d.ts +20 -0
- package/dist/promises.js +1 -0
- package/dist/retrier.js +3 -3
- package/dist/searcher.js +10 -3
- package/dist/timestamp-generator.d.ts +38 -0
- package/dist/timestamp-generator.js +84 -0
- package/package.json +2 -2
- package/dist/field-decorator.d.ts +0 -63
- package/dist/field-decorator.js +0 -122
|
@@ -8,8 +8,8 @@ export interface ProjectionMetadata<TEntity extends EntityInterface, TReadModel
|
|
|
8
8
|
methodName: string;
|
|
9
9
|
joinKey: keyof TEntity | ReadModelJoinKeyFunction<TEntity, TReadModel>;
|
|
10
10
|
}
|
|
11
|
-
export type ProjectionResult<TReadModel> = TReadModel |
|
|
12
|
-
export declare enum
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
export type ProjectionResult<TReadModel> = TReadModel | ProjectionAction;
|
|
12
|
+
export declare enum ProjectionAction {
|
|
13
|
+
Skip = 0,
|
|
14
|
+
Delete = 1
|
|
15
15
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
var
|
|
5
|
-
(function (
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
})(
|
|
3
|
+
exports.ProjectionAction = void 0;
|
|
4
|
+
var ProjectionAction;
|
|
5
|
+
(function (ProjectionAction) {
|
|
6
|
+
ProjectionAction[ProjectionAction["Skip"] = 0] = "Skip";
|
|
7
|
+
ProjectionAction[ProjectionAction["Delete"] = 1] = "Delete";
|
|
8
|
+
})(ProjectionAction || (exports.ProjectionAction = ProjectionAction = {}));
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import { AnyClass } from '..';
|
|
2
|
+
import { EntityInterface } from './entity';
|
|
2
3
|
export interface ReducerMetadata {
|
|
3
4
|
class: AnyClass;
|
|
4
5
|
methodName: string;
|
|
5
6
|
}
|
|
7
|
+
export declare enum ReducerAction {
|
|
8
|
+
Skip = 0
|
|
9
|
+
}
|
|
10
|
+
export type ReducerResult<TEntity extends EntityInterface> = TEntity | ReducerAction;
|
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ReducerAction = void 0;
|
|
4
|
+
var ReducerAction;
|
|
5
|
+
(function (ReducerAction) {
|
|
6
|
+
ReducerAction[ReducerAction["Skip"] = 0] = "Skip";
|
|
7
|
+
})(ReducerAction || (exports.ReducerAction = ReducerAction = {}));
|
|
@@ -23,6 +23,12 @@ exports.Register = void 0;
|
|
|
23
23
|
* ```
|
|
24
24
|
*/
|
|
25
25
|
class Register {
|
|
26
|
+
requestID;
|
|
27
|
+
responseHeaders;
|
|
28
|
+
flusher;
|
|
29
|
+
currentUser;
|
|
30
|
+
context;
|
|
31
|
+
eventList = [];
|
|
26
32
|
/**
|
|
27
33
|
* Creates a new instance of Register
|
|
28
34
|
*
|
|
@@ -56,7 +62,6 @@ class Register {
|
|
|
56
62
|
this.flusher = flusher;
|
|
57
63
|
this.currentUser = currentUser;
|
|
58
64
|
this.context = context;
|
|
59
|
-
this.eventList = [];
|
|
60
65
|
}
|
|
61
66
|
/**
|
|
62
67
|
* Register a list of events to be added to the event-store on handler completion
|
package/dist/config.js
CHANGED
|
@@ -9,74 +9,83 @@ const _1 = require(".");
|
|
|
9
9
|
* the Magek config.
|
|
10
10
|
*/
|
|
11
11
|
class MagekConfig {
|
|
12
|
+
environmentName;
|
|
13
|
+
logLevel = logger_1.Level.debug;
|
|
14
|
+
logPrefix;
|
|
15
|
+
logger;
|
|
16
|
+
_runtime;
|
|
17
|
+
eventStoreAdapter;
|
|
18
|
+
readModelStoreAdapter;
|
|
19
|
+
sessionStoreAdapter;
|
|
20
|
+
appName = 'new-magek-app';
|
|
21
|
+
assets;
|
|
22
|
+
defaultResponseHeaders = {};
|
|
23
|
+
subscriptions = {
|
|
24
|
+
maxConnectionDurationInSeconds: 7 * 24 * 60 * 60, // 7 days
|
|
25
|
+
maxDurationInSeconds: 2 * 24 * 60 * 60, // 2 days
|
|
26
|
+
};
|
|
27
|
+
enableGraphQLIntrospection = true;
|
|
28
|
+
_userProjectRootPath;
|
|
29
|
+
codeRelativePath = 'dist';
|
|
30
|
+
eventDispatcherHandler = path.join(this.codeRelativePath, 'index.eventDispatcher');
|
|
31
|
+
eventStreamConsumer = path.join(this.codeRelativePath, 'index.consumeEventStream');
|
|
32
|
+
eventStreamProducer = path.join(this.codeRelativePath, 'index.produceEventStream');
|
|
33
|
+
serveGraphQLHandler = path.join(this.codeRelativePath, 'index.graphQLDispatcher');
|
|
34
|
+
sensorHealthHandler = path.join(this.codeRelativePath, 'index.health');
|
|
35
|
+
scheduledTaskHandler = path.join(this.codeRelativePath, 'index.triggerScheduledCommands');
|
|
36
|
+
notifySubscribersHandler = path.join(this.codeRelativePath, 'index.notifySubscribers');
|
|
37
|
+
functionRelativePath = path.join('..', this.codeRelativePath, 'index.js');
|
|
38
|
+
events = {};
|
|
39
|
+
notifications = {};
|
|
40
|
+
partitionKeys = {};
|
|
41
|
+
topicToEvent = {};
|
|
42
|
+
eventToTopic = {};
|
|
43
|
+
entities = {};
|
|
44
|
+
reducers = {};
|
|
45
|
+
commandHandlers = {};
|
|
46
|
+
queryHandlers = {};
|
|
47
|
+
eventHandlers = {};
|
|
48
|
+
readModels = {};
|
|
49
|
+
projections = {};
|
|
50
|
+
unProjections = {};
|
|
51
|
+
readModelSequenceKeys = {};
|
|
52
|
+
roles = {};
|
|
53
|
+
schemaMigrations = {};
|
|
54
|
+
scheduledCommandHandlers = {};
|
|
55
|
+
dataMigrationHandlers = {};
|
|
56
|
+
userHealthIndicators = {};
|
|
57
|
+
sensorConfiguration = {
|
|
58
|
+
health: {
|
|
59
|
+
globalAuthorizer: {
|
|
60
|
+
authorize: 'all',
|
|
61
|
+
},
|
|
62
|
+
magek: _1.DEFAULT_SENSOR_HEALTH_CONFIGURATIONS,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
globalErrorsHandler;
|
|
66
|
+
enableSubscriptions = true;
|
|
67
|
+
nonExposedGraphQLMetadataKey = {};
|
|
68
|
+
// TTL for events stored in dispatched events table. Default to 5 minutes (i.e., 300 seconds).
|
|
69
|
+
dispatchedEventsTtl = 300;
|
|
70
|
+
traceConfiguration = {
|
|
71
|
+
enableTraceNotification: false,
|
|
72
|
+
includeInternal: false,
|
|
73
|
+
onStart: async () => { },
|
|
74
|
+
onEnd: async () => { },
|
|
75
|
+
};
|
|
76
|
+
eventStreamConfiguration = { enabled: false };
|
|
77
|
+
/** Environment variables to set when running the application */
|
|
78
|
+
env = {};
|
|
79
|
+
/**
|
|
80
|
+
* Add `TokenVerifier` implementations to this array to enable token verification.
|
|
81
|
+
* When a bearer token arrives in a request 'Authorization' header, it will be checked
|
|
82
|
+
* against all the verifiers registered here.
|
|
83
|
+
*/
|
|
84
|
+
tokenVerifiers = [];
|
|
12
85
|
constructor(environmentName) {
|
|
13
86
|
this.environmentName = environmentName;
|
|
14
|
-
this.logLevel = logger_1.Level.debug;
|
|
15
|
-
this.appName = 'new-magek-app';
|
|
16
|
-
this.defaultResponseHeaders = {};
|
|
17
|
-
this.subscriptions = {
|
|
18
|
-
maxConnectionDurationInSeconds: 7 * 24 * 60 * 60, // 7 days
|
|
19
|
-
maxDurationInSeconds: 2 * 24 * 60 * 60, // 2 days
|
|
20
|
-
};
|
|
21
|
-
this.enableGraphQLIntrospection = true;
|
|
22
|
-
this.codeRelativePath = 'dist';
|
|
23
|
-
this.eventDispatcherHandler = path.join(this.codeRelativePath, 'index.eventDispatcher');
|
|
24
|
-
this.eventStreamConsumer = path.join(this.codeRelativePath, 'index.consumeEventStream');
|
|
25
|
-
this.eventStreamProducer = path.join(this.codeRelativePath, 'index.produceEventStream');
|
|
26
|
-
this.serveGraphQLHandler = path.join(this.codeRelativePath, 'index.graphQLDispatcher');
|
|
27
|
-
this.sensorHealthHandler = path.join(this.codeRelativePath, 'index.health');
|
|
28
|
-
this.scheduledTaskHandler = path.join(this.codeRelativePath, 'index.triggerScheduledCommands');
|
|
29
|
-
this.notifySubscribersHandler = path.join(this.codeRelativePath, 'index.notifySubscribers');
|
|
30
|
-
this.functionRelativePath = path.join('..', this.codeRelativePath, 'index.js');
|
|
31
|
-
this.events = {};
|
|
32
|
-
this.notifications = {};
|
|
33
|
-
this.partitionKeys = {};
|
|
34
|
-
this.topicToEvent = {};
|
|
35
|
-
this.eventToTopic = {};
|
|
36
|
-
this.entities = {};
|
|
37
|
-
this.reducers = {};
|
|
38
|
-
this.commandHandlers = {};
|
|
39
|
-
this.queryHandlers = {};
|
|
40
|
-
this.eventHandlers = {};
|
|
41
|
-
this.readModels = {};
|
|
42
|
-
this.projections = {};
|
|
43
|
-
this.unProjections = {};
|
|
44
|
-
this.readModelSequenceKeys = {};
|
|
45
|
-
this.roles = {};
|
|
46
|
-
this.schemaMigrations = {};
|
|
47
|
-
this.scheduledCommandHandlers = {};
|
|
48
|
-
this.dataMigrationHandlers = {};
|
|
49
|
-
this.userHealthIndicators = {};
|
|
50
|
-
this.sensorConfiguration = {
|
|
51
|
-
health: {
|
|
52
|
-
globalAuthorizer: {
|
|
53
|
-
authorize: 'all',
|
|
54
|
-
},
|
|
55
|
-
magek: _1.DEFAULT_SENSOR_HEALTH_CONFIGURATIONS,
|
|
56
|
-
},
|
|
57
|
-
};
|
|
58
|
-
this.enableSubscriptions = true;
|
|
59
|
-
this.nonExposedGraphQLMetadataKey = {};
|
|
60
|
-
// TTL for events stored in dispatched events table. Default to 5 minutes (i.e., 300 seconds).
|
|
61
|
-
this.dispatchedEventsTtl = 300;
|
|
62
|
-
this.traceConfiguration = {
|
|
63
|
-
enableTraceNotification: false,
|
|
64
|
-
includeInternal: false,
|
|
65
|
-
onStart: async () => { },
|
|
66
|
-
onEnd: async () => { },
|
|
67
|
-
};
|
|
68
|
-
this.eventStreamConfiguration = { enabled: false };
|
|
69
|
-
/** Environment variables to set when running the application */
|
|
70
|
-
this.env = {};
|
|
71
|
-
/**
|
|
72
|
-
* Add `TokenVerifier` implementations to this array to enable token verification.
|
|
73
|
-
* When a bearer token arrives in a request 'Authorization' header, it will be checked
|
|
74
|
-
* against all the verifiers registered here.
|
|
75
|
-
*/
|
|
76
|
-
this.tokenVerifiers = [];
|
|
77
87
|
}
|
|
78
88
|
get resourceNames() {
|
|
79
|
-
var _a, _b;
|
|
80
89
|
if (this.appName.length === 0)
|
|
81
90
|
throw new Error('Application name cannot be empty');
|
|
82
91
|
const applicationStackName = this.appName + '-app';
|
|
@@ -87,7 +96,7 @@ class MagekConfig {
|
|
|
87
96
|
eventsDedup: applicationStackName + '-events-dedup',
|
|
88
97
|
subscriptionsStore: applicationStackName + '-subscriptions-store',
|
|
89
98
|
connectionsStore: applicationStackName + '-connections-store',
|
|
90
|
-
streamTopic:
|
|
99
|
+
streamTopic: this.eventStreamConfiguration.parameters?.streamTopic ?? 'magek_events',
|
|
91
100
|
forReadModel(readModelName) {
|
|
92
101
|
return applicationStackName + '-' + readModelName;
|
|
93
102
|
},
|
package/dist/envelope.d.ts
CHANGED
|
@@ -36,6 +36,7 @@ export interface EventStoreEntryEnvelope extends TypedEnvelope {
|
|
|
36
36
|
}
|
|
37
37
|
export interface NonPersistedEventEnvelope extends EventStoreEntryEnvelope {
|
|
38
38
|
kind: 'event';
|
|
39
|
+
createdAt: string;
|
|
39
40
|
}
|
|
40
41
|
export interface EventEnvelope extends NonPersistedEventEnvelope {
|
|
41
42
|
id?: string;
|
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.CommandHandlerGlobalError = void 0;
|
|
4
4
|
const global_error_container_1 = require("./global-error-container");
|
|
5
5
|
class CommandHandlerGlobalError extends global_error_container_1.GlobalErrorContainer {
|
|
6
|
+
commandEnvelope;
|
|
7
|
+
commandMetadata;
|
|
6
8
|
constructor(commandEnvelope, commandMetadata, originalError) {
|
|
7
9
|
super(originalError);
|
|
8
10
|
this.commandEnvelope = commandEnvelope;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.EventGlobalError = void 0;
|
|
4
4
|
const global_error_container_1 = require("./global-error-container");
|
|
5
5
|
class EventGlobalError extends global_error_container_1.GlobalErrorContainer {
|
|
6
|
+
eventEnvelope;
|
|
6
7
|
constructor(eventEnvelope, originalError) {
|
|
7
8
|
super(originalError);
|
|
8
9
|
this.eventEnvelope = eventEnvelope;
|
|
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.EventHandlerGlobalError = void 0;
|
|
4
4
|
const global_error_container_1 = require("./global-error-container");
|
|
5
5
|
class EventHandlerGlobalError extends global_error_container_1.GlobalErrorContainer {
|
|
6
|
+
eventEnvelope;
|
|
7
|
+
eventInstance;
|
|
8
|
+
eventHandlerMetadata;
|
|
6
9
|
constructor(eventEnvelope, eventInstance, eventHandlerMetadata, originalError) {
|
|
7
10
|
super(originalError);
|
|
8
11
|
this.eventEnvelope = eventEnvelope;
|
|
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ProjectionGlobalError = void 0;
|
|
4
4
|
const global_error_container_1 = require("./global-error-container");
|
|
5
5
|
class ProjectionGlobalError extends global_error_container_1.GlobalErrorContainer {
|
|
6
|
+
entityEnvelope;
|
|
7
|
+
entity;
|
|
8
|
+
readModel;
|
|
9
|
+
projectionMetadata;
|
|
6
10
|
constructor(entityEnvelope, entity, readModel, projectionMetadata, originalError) {
|
|
7
11
|
super(originalError);
|
|
8
12
|
this.entityEnvelope = entityEnvelope;
|
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.QueryHandlerGlobalError = void 0;
|
|
4
4
|
const global_error_container_1 = require("./global-error-container");
|
|
5
5
|
class QueryHandlerGlobalError extends global_error_container_1.GlobalErrorContainer {
|
|
6
|
+
query;
|
|
6
7
|
constructor(query, originalError) {
|
|
7
8
|
super(originalError);
|
|
8
9
|
this.query = query;
|
|
@@ -3,6 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ReducerGlobalError = void 0;
|
|
4
4
|
const global_error_container_1 = require("./global-error-container");
|
|
5
5
|
class ReducerGlobalError extends global_error_container_1.GlobalErrorContainer {
|
|
6
|
+
eventEnvelope;
|
|
7
|
+
eventInstance;
|
|
8
|
+
snapshotInstance;
|
|
9
|
+
reducerMetadata;
|
|
6
10
|
constructor(eventEnvelope, eventInstance, snapshotInstance, reducerMetadata, originalError) {
|
|
7
11
|
super(originalError);
|
|
8
12
|
this.eventEnvelope = eventEnvelope;
|
|
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.ScheduleCommandGlobalError = void 0;
|
|
4
4
|
const global_error_container_1 = require("./global-error-container");
|
|
5
5
|
class ScheduleCommandGlobalError extends global_error_container_1.GlobalErrorContainer {
|
|
6
|
+
scheduleCommandEnvelope;
|
|
7
|
+
scheduleCommandMetadata;
|
|
6
8
|
constructor(scheduleCommandEnvelope, scheduleCommandMetadata, originalError) {
|
|
7
9
|
super(originalError);
|
|
8
10
|
this.scheduleCommandEnvelope = scheduleCommandEnvelope;
|
|
@@ -13,6 +13,7 @@ const global_error_container_1 = require("./global-error-container");
|
|
|
13
13
|
* This class is kept for backwards compatibility.
|
|
14
14
|
*/
|
|
15
15
|
class SnapshotPersistHandlerGlobalError extends global_error_container_1.GlobalErrorContainer {
|
|
16
|
+
snapshot;
|
|
16
17
|
constructor(snapshot, originalError) {
|
|
17
18
|
super(originalError);
|
|
18
19
|
this.snapshot = snapshot;
|
package/dist/errors.js
CHANGED
|
@@ -3,10 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.InvalidReducerError = exports.InvalidEventError = exports.OptimisticConcurrencyUnexpectedVersionError = exports.InvalidVersionError = exports.NotFoundError = exports.MagekTokenNotBeforeError = exports.MagekTokenExpiredError = exports.NotAuthorizedError = exports.InvalidProtocolError = exports.InvalidParameterError = exports.MagekError = void 0;
|
|
4
4
|
exports.httpStatusCodeFor = httpStatusCodeFor;
|
|
5
5
|
class MagekError extends Error {
|
|
6
|
+
data;
|
|
7
|
+
code;
|
|
6
8
|
constructor(message, code, data) {
|
|
7
9
|
super(message);
|
|
8
10
|
this.data = data;
|
|
9
|
-
this.code = code
|
|
11
|
+
this.code = code ?? this.constructor.name;
|
|
10
12
|
}
|
|
11
13
|
}
|
|
12
14
|
exports.MagekError = MagekError;
|
|
@@ -38,6 +40,8 @@ class InvalidEventError extends MagekError {
|
|
|
38
40
|
}
|
|
39
41
|
exports.InvalidEventError = InvalidEventError;
|
|
40
42
|
class InvalidReducerError extends MagekError {
|
|
43
|
+
eventInstance;
|
|
44
|
+
snapshotInstance;
|
|
41
45
|
constructor(message, eventInstance, snapshotInstance) {
|
|
42
46
|
super(message);
|
|
43
47
|
this.eventInstance = eventInstance;
|
|
@@ -46,7 +50,6 @@ class InvalidReducerError extends MagekError {
|
|
|
46
50
|
}
|
|
47
51
|
exports.InvalidReducerError = InvalidReducerError;
|
|
48
52
|
function httpStatusCodeFor(error) {
|
|
49
|
-
var _a;
|
|
50
53
|
const errorToHTTPCode = {
|
|
51
54
|
[InvalidParameterError.name]: 400,
|
|
52
55
|
[InvalidProtocolError.name]: 400,
|
|
@@ -56,5 +59,5 @@ function httpStatusCodeFor(error) {
|
|
|
56
59
|
[NotFoundError.name]: 404,
|
|
57
60
|
[InvalidVersionError.name]: 422,
|
|
58
61
|
};
|
|
59
|
-
return
|
|
62
|
+
return errorToHTTPCode[error.constructor.name] ?? 500;
|
|
60
63
|
}
|
|
@@ -15,38 +15,42 @@ var MessageTypes;
|
|
|
15
15
|
MessageTypes["GQL_STOP"] = "stop";
|
|
16
16
|
})(MessageTypes || (exports.MessageTypes = MessageTypes = {}));
|
|
17
17
|
class GraphQLInitError {
|
|
18
|
+
payload;
|
|
19
|
+
type = MessageTypes.GQL_CONNECTION_ERROR;
|
|
18
20
|
constructor(payload) {
|
|
19
21
|
this.payload = payload;
|
|
20
|
-
this.type = MessageTypes.GQL_CONNECTION_ERROR;
|
|
21
22
|
}
|
|
22
23
|
}
|
|
23
24
|
exports.GraphQLInitError = GraphQLInitError;
|
|
24
25
|
class GraphQLInitAck {
|
|
25
|
-
|
|
26
|
-
this.type = MessageTypes.GQL_CONNECTION_ACK;
|
|
27
|
-
}
|
|
26
|
+
type = MessageTypes.GQL_CONNECTION_ACK;
|
|
28
27
|
}
|
|
29
28
|
exports.GraphQLInitAck = GraphQLInitAck;
|
|
30
29
|
class GraphQLData {
|
|
30
|
+
id;
|
|
31
|
+
payload;
|
|
32
|
+
type = MessageTypes.GQL_DATA;
|
|
31
33
|
constructor(id, payload) {
|
|
32
34
|
this.id = id;
|
|
33
35
|
this.payload = payload;
|
|
34
|
-
this.type = MessageTypes.GQL_DATA;
|
|
35
36
|
}
|
|
36
37
|
}
|
|
37
38
|
exports.GraphQLData = GraphQLData;
|
|
38
39
|
class GraphQLError {
|
|
40
|
+
id;
|
|
41
|
+
payload;
|
|
42
|
+
type = MessageTypes.GQL_ERROR;
|
|
39
43
|
constructor(id, payload) {
|
|
40
44
|
this.id = id;
|
|
41
45
|
this.payload = payload;
|
|
42
|
-
this.type = MessageTypes.GQL_ERROR;
|
|
43
46
|
}
|
|
44
47
|
}
|
|
45
48
|
exports.GraphQLError = GraphQLError;
|
|
46
49
|
class GraphQLComplete {
|
|
50
|
+
id;
|
|
51
|
+
type = MessageTypes.GQL_COMPLETE;
|
|
47
52
|
constructor(id) {
|
|
48
53
|
this.id = id;
|
|
49
|
-
this.type = MessageTypes.GQL_COMPLETE;
|
|
50
54
|
}
|
|
51
55
|
}
|
|
52
56
|
exports.GraphQLComplete = GraphQLComplete;
|
package/dist/http-service.js
CHANGED
|
@@ -23,7 +23,7 @@ async function request(url, method = 'GET', data = '', config = {}) {
|
|
|
23
23
|
const body = [];
|
|
24
24
|
res.on('data', (chunk) => body.push(chunk));
|
|
25
25
|
res.on('end', () => {
|
|
26
|
-
if (!
|
|
26
|
+
if (!res?.statusCode) {
|
|
27
27
|
return reject(new Error('Unknown HTTP status code'));
|
|
28
28
|
}
|
|
29
29
|
// Accept 2xx codes or any explicitly accepted status codes
|
|
@@ -32,7 +32,7 @@ async function request(url, method = 'GET', data = '', config = {}) {
|
|
|
32
32
|
}
|
|
33
33
|
const buffer = Buffer.concat(body).toString();
|
|
34
34
|
resolve({
|
|
35
|
-
status: res
|
|
35
|
+
status: res?.statusCode,
|
|
36
36
|
body: buffer,
|
|
37
37
|
});
|
|
38
38
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -26,5 +26,5 @@ export * from './runtime';
|
|
|
26
26
|
export * from './event-store-adapter';
|
|
27
27
|
export * from './read-model-store-adapter';
|
|
28
28
|
export * from './session-store-adapter';
|
|
29
|
-
export * from './field-decorator';
|
|
30
29
|
export * from './metadata-types';
|
|
30
|
+
export * from './timestamp-generator';
|
package/dist/index.js
CHANGED
|
@@ -29,5 +29,5 @@ tslib_1.__exportStar(require("./runtime"), exports);
|
|
|
29
29
|
tslib_1.__exportStar(require("./event-store-adapter"), exports);
|
|
30
30
|
tslib_1.__exportStar(require("./read-model-store-adapter"), exports);
|
|
31
31
|
tslib_1.__exportStar(require("./session-store-adapter"), exports);
|
|
32
|
-
tslib_1.__exportStar(require("./field-decorator"), exports);
|
|
33
32
|
tslib_1.__exportStar(require("./metadata-types"), exports);
|
|
33
|
+
tslib_1.__exportStar(require("./timestamp-generator"), exports);
|
package/dist/instances.d.ts
CHANGED
|
@@ -37,6 +37,26 @@ export declare function createInstance<T>(instanceClass: Class<T>, rawObject: Re
|
|
|
37
37
|
* @see {@link createInstance}
|
|
38
38
|
*/
|
|
39
39
|
export declare function createInstances<T>(instanceClass: Class<T>, rawObjects: Array<Record<string, any>>): T[];
|
|
40
|
+
/**
|
|
41
|
+
* Applies partial changes to an entity, optionally using defaults when creating a new instance.
|
|
42
|
+
* Designed for plain objects (entity state); use `createInstance` for class instances.
|
|
43
|
+
*
|
|
44
|
+
* @remarks
|
|
45
|
+
* This function always returns a shallow copy, ensuring immutability.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* // Update existing entity
|
|
50
|
+
* const updated = evolve(current, { balance: current.balance + event.amount })
|
|
51
|
+
* // Create new entity with defaults
|
|
52
|
+
* const created = evolve(undefined, { id: event.entityId, name: event.name }, { status: 'active' })
|
|
53
|
+
* // Create new entity without defaults (changes must be complete)
|
|
54
|
+
* const created = evolve(undefined, { id: event.entityId, name: event.name, status: 'active' })
|
|
55
|
+
* ```
|
|
56
|
+
*/
|
|
57
|
+
export declare function evolve<T extends object>(current: T, changes: Partial<T>): T;
|
|
58
|
+
export declare function evolve<C extends object, D extends object>(current: undefined, changes: C, defaults: D): C & D;
|
|
59
|
+
export declare function evolve<T extends object>(current: undefined, changes: T): T;
|
|
40
60
|
/**
|
|
41
61
|
* Creates an instance of the read model class with the calculated properties included
|
|
42
62
|
* @param instanceClass The read model class
|
package/dist/instances.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.createInstance = createInstance;
|
|
4
4
|
exports.createInstances = createInstances;
|
|
5
|
+
exports.evolve = evolve;
|
|
5
6
|
exports.createInstanceWithCalculatedProperties = createInstanceWithCalculatedProperties;
|
|
6
7
|
/**
|
|
7
8
|
* Creates an instance of the given class from the given raw object.
|
|
@@ -44,6 +45,15 @@ function createInstance(instanceClass, rawObject) {
|
|
|
44
45
|
function createInstances(instanceClass, rawObjects) {
|
|
45
46
|
return rawObjects.map((rawObject) => createInstance(instanceClass, rawObject));
|
|
46
47
|
}
|
|
48
|
+
function evolve(current, changes, defaults) {
|
|
49
|
+
if (current !== undefined) {
|
|
50
|
+
return { ...current, ...changes };
|
|
51
|
+
}
|
|
52
|
+
if (defaults !== undefined) {
|
|
53
|
+
return { ...defaults, ...changes };
|
|
54
|
+
}
|
|
55
|
+
return { ...changes };
|
|
56
|
+
}
|
|
47
57
|
/**
|
|
48
58
|
* Creates an instance of the read model class with the calculated properties included
|
|
49
59
|
* @param instanceClass The read model class
|
package/dist/logger.js
CHANGED
|
@@ -11,12 +11,11 @@ var Level;
|
|
|
11
11
|
})(Level || (exports.Level = Level = {}));
|
|
12
12
|
const defaultLogPrefix = 'Magek';
|
|
13
13
|
function getLogger(config, location, overridenLogPrefix) {
|
|
14
|
-
|
|
15
|
-
const
|
|
16
|
-
const
|
|
17
|
-
const
|
|
18
|
-
const
|
|
19
|
-
const logPrefix = (_j = overridenLogPrefix !== null && overridenLogPrefix !== void 0 ? overridenLogPrefix : config === null || config === void 0 ? void 0 : config.logPrefix) !== null && _j !== void 0 ? _j : defaultLogPrefix;
|
|
14
|
+
const debug = config.logger?.debug ?? console.debug;
|
|
15
|
+
const info = config.logger?.info ?? console.info;
|
|
16
|
+
const warn = config.logger?.warn ?? console.warn;
|
|
17
|
+
const error = config.logger?.error ?? console.error;
|
|
18
|
+
const logPrefix = overridenLogPrefix ?? config?.logPrefix ?? defaultLogPrefix;
|
|
20
19
|
const locationStr = location ? `|${location}: ` : ': ';
|
|
21
20
|
const prefix = `[${logPrefix}]${locationStr}`;
|
|
22
21
|
const prefixedDebugFunction = debug.bind(null, prefix);
|
package/dist/metadata-types.d.ts
CHANGED
|
@@ -2,6 +2,26 @@ import 'reflect-metadata';
|
|
|
2
2
|
export type ClassType = {
|
|
3
3
|
new (...args: unknown[]): unknown;
|
|
4
4
|
};
|
|
5
|
+
/**
|
|
6
|
+
* Type function for specifying field types
|
|
7
|
+
* The parameter is optional and not used - it's just for TypeGraphQL-style ergonomics
|
|
8
|
+
*/
|
|
9
|
+
export type TypeFunction = (type?: unknown) => unknown;
|
|
10
|
+
/**
|
|
11
|
+
* Options for the @field() decorator
|
|
12
|
+
*/
|
|
13
|
+
export interface FieldOptions {
|
|
14
|
+
nullable?: boolean;
|
|
15
|
+
readonly?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Metadata stored for each field
|
|
19
|
+
*/
|
|
20
|
+
export interface FieldMetadata {
|
|
21
|
+
name: string;
|
|
22
|
+
typeFunction?: TypeFunction;
|
|
23
|
+
options: FieldOptions;
|
|
24
|
+
}
|
|
5
25
|
export type TypeGroup = 'String' | 'Number' | 'Boolean' | 'Enum' | 'Union' | 'Intersection' | 'Function' | 'Class' | 'Interface' | 'Type' | 'Array' | 'Object' | 'ReadonlyArray' | 'Other';
|
|
6
26
|
export interface TypeMetadata {
|
|
7
27
|
name: string;
|
package/dist/promises.js
CHANGED
package/dist/retrier.js
CHANGED
|
@@ -15,9 +15,9 @@ async function retryIfError(logicToRetry, errorClassThatRetries, logger, maxRetr
|
|
|
15
15
|
let errorAfterMaxTries;
|
|
16
16
|
for (tryNumber = 1; tryNumber <= maxRetries; tryNumber++) {
|
|
17
17
|
try {
|
|
18
|
-
logger
|
|
18
|
+
logger?.debug(`[retryIfError] Try number ${tryNumber}`);
|
|
19
19
|
const result = await logicToRetry(tryNumber);
|
|
20
|
-
logger
|
|
20
|
+
logger?.debug(`[retryIfError] Succeeded after ${tryNumber} retries`);
|
|
21
21
|
return result;
|
|
22
22
|
}
|
|
23
23
|
catch (e) {
|
|
@@ -30,7 +30,7 @@ async function retryIfError(logicToRetry, errorClassThatRetries, logger, maxRetr
|
|
|
30
30
|
}
|
|
31
31
|
function checkRetryError(e, errorClassThatRetries, logger) {
|
|
32
32
|
if (!(e instanceof errorClassThatRetries)) {
|
|
33
|
-
logger
|
|
33
|
+
logger?.debug('[checkRetryError] Logic failed with an error that must not be retried. Rethrowing');
|
|
34
34
|
throw e;
|
|
35
35
|
}
|
|
36
36
|
}
|
package/dist/searcher.js
CHANGED
|
@@ -7,6 +7,16 @@ exports.Searcher = void 0;
|
|
|
7
7
|
* Check the documentation on the individual methods to know more about how to do so.
|
|
8
8
|
*/
|
|
9
9
|
class Searcher {
|
|
10
|
+
objectClass;
|
|
11
|
+
searcherFunction;
|
|
12
|
+
finderByKeyFunction;
|
|
13
|
+
// private offset?: number
|
|
14
|
+
_limit;
|
|
15
|
+
_afterCursor;
|
|
16
|
+
filters = {};
|
|
17
|
+
_sortByList = {};
|
|
18
|
+
_paginatedVersion = false;
|
|
19
|
+
_selectFor;
|
|
10
20
|
/**
|
|
11
21
|
* @param objectClass The class of the object you want to run the search for.
|
|
12
22
|
* @param searcherFunction The function that will receive all the filters and run the actual search
|
|
@@ -16,9 +26,6 @@ class Searcher {
|
|
|
16
26
|
this.objectClass = objectClass;
|
|
17
27
|
this.searcherFunction = searcherFunction;
|
|
18
28
|
this.finderByKeyFunction = finderByKeyFunction;
|
|
19
|
-
this.filters = {};
|
|
20
|
-
this._sortByList = {};
|
|
21
|
-
this._paginatedVersion = false;
|
|
22
29
|
}
|
|
23
30
|
/**
|
|
24
31
|
* Adds a filter for the search. For example: If you want to search for people whose age is greater than 30
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generates unique, monotonically increasing ISO 8601 timestamps with sub-millisecond precision.
|
|
3
|
+
*
|
|
4
|
+
* Format: 2024-01-28T12:34:56.12345678Z where:
|
|
5
|
+
* - Digits 1-3 (123): actual milliseconds from Date.now()
|
|
6
|
+
* - Digits 4-7 (4567): monotonic counter (0000-9999)
|
|
7
|
+
* - Digit 8 (8): random seed for distributed uniqueness (0-9)
|
|
8
|
+
*
|
|
9
|
+
* This provides:
|
|
10
|
+
* - 10,000 unique orderable timestamps per millisecond per instance
|
|
11
|
+
* - 100,000 total combinations across distributed instances (10,000 counter values × 10 seeds)
|
|
12
|
+
* - Standard ISO 8601 format parseable by any Date library
|
|
13
|
+
* - Deterministic ordering via string comparison
|
|
14
|
+
* - Random component for distributed uniqueness
|
|
15
|
+
*/
|
|
16
|
+
export declare class TimestampGenerator {
|
|
17
|
+
private lastMs;
|
|
18
|
+
private counter;
|
|
19
|
+
private readonly randomSeed;
|
|
20
|
+
constructor();
|
|
21
|
+
/**
|
|
22
|
+
* Generates the next unique timestamp with microsecond precision.
|
|
23
|
+
*
|
|
24
|
+
* @returns ISO 8601 timestamp string with 8 fractional digits
|
|
25
|
+
*/
|
|
26
|
+
next(): string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Returns the singleton TimestampGenerator instance.
|
|
30
|
+
* Creates it if it doesn't exist yet.
|
|
31
|
+
*
|
|
32
|
+
* @returns The global TimestampGenerator instance
|
|
33
|
+
*/
|
|
34
|
+
export declare function getTimestampGenerator(): TimestampGenerator;
|
|
35
|
+
/**
|
|
36
|
+
* Resets the singleton instance. Primarily for testing purposes.
|
|
37
|
+
*/
|
|
38
|
+
export declare function resetTimestampGenerator(): void;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TimestampGenerator = void 0;
|
|
4
|
+
exports.getTimestampGenerator = getTimestampGenerator;
|
|
5
|
+
exports.resetTimestampGenerator = resetTimestampGenerator;
|
|
6
|
+
/**
|
|
7
|
+
* Generates unique, monotonically increasing ISO 8601 timestamps with sub-millisecond precision.
|
|
8
|
+
*
|
|
9
|
+
* Format: 2024-01-28T12:34:56.12345678Z where:
|
|
10
|
+
* - Digits 1-3 (123): actual milliseconds from Date.now()
|
|
11
|
+
* - Digits 4-7 (4567): monotonic counter (0000-9999)
|
|
12
|
+
* - Digit 8 (8): random seed for distributed uniqueness (0-9)
|
|
13
|
+
*
|
|
14
|
+
* This provides:
|
|
15
|
+
* - 10,000 unique orderable timestamps per millisecond per instance
|
|
16
|
+
* - 100,000 total combinations across distributed instances (10,000 counter values × 10 seeds)
|
|
17
|
+
* - Standard ISO 8601 format parseable by any Date library
|
|
18
|
+
* - Deterministic ordering via string comparison
|
|
19
|
+
* - Random component for distributed uniqueness
|
|
20
|
+
*/
|
|
21
|
+
class TimestampGenerator {
|
|
22
|
+
lastMs = 0;
|
|
23
|
+
counter = 0;
|
|
24
|
+
randomSeed;
|
|
25
|
+
constructor() {
|
|
26
|
+
// Generate random seed 0-9 for distributed uniqueness
|
|
27
|
+
this.randomSeed = Math.floor(Math.random() * 10);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Generates the next unique timestamp with microsecond precision.
|
|
31
|
+
*
|
|
32
|
+
* @returns ISO 8601 timestamp string with 8 fractional digits
|
|
33
|
+
*/
|
|
34
|
+
next() {
|
|
35
|
+
const nowMs = Date.now();
|
|
36
|
+
// Reset counter only if we've moved forward to a new millisecond
|
|
37
|
+
// When nowMs <= lastMs (clock skew), continue using lastMs and incrementing counter
|
|
38
|
+
if (nowMs > this.lastMs) {
|
|
39
|
+
this.lastMs = nowMs;
|
|
40
|
+
this.counter = 0;
|
|
41
|
+
}
|
|
42
|
+
// If counter exceeds 9999, advance lastMs to maintain monotonicity
|
|
43
|
+
if (this.counter > 9999) {
|
|
44
|
+
this.lastMs++;
|
|
45
|
+
this.counter = 0;
|
|
46
|
+
}
|
|
47
|
+
// Format: milliseconds (3 digits) + counter (4 digits) + seed (1 digit)
|
|
48
|
+
const microPart = String(this.counter).padStart(4, '0') + String(this.randomSeed);
|
|
49
|
+
this.counter++;
|
|
50
|
+
// Build ISO 8601 timestamp with microsecond precision
|
|
51
|
+
// Use lastMs (which may be clamped due to clock skew) instead of nowMs
|
|
52
|
+
const date = new Date(this.lastMs);
|
|
53
|
+
const isoString = date.toISOString();
|
|
54
|
+
// Replace milliseconds with our extended precision
|
|
55
|
+
// ISO format: 2024-01-28T12:34:56.123Z
|
|
56
|
+
// ^^^
|
|
57
|
+
// We extend to: 2024-01-28T12:34:56.12345678Z
|
|
58
|
+
const withMicros = isoString.slice(0, -1) + microPart + 'Z';
|
|
59
|
+
return withMicros;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.TimestampGenerator = TimestampGenerator;
|
|
63
|
+
/**
|
|
64
|
+
* Global singleton instance of TimestampGenerator.
|
|
65
|
+
*/
|
|
66
|
+
let timestampGeneratorInstance;
|
|
67
|
+
/**
|
|
68
|
+
* Returns the singleton TimestampGenerator instance.
|
|
69
|
+
* Creates it if it doesn't exist yet.
|
|
70
|
+
*
|
|
71
|
+
* @returns The global TimestampGenerator instance
|
|
72
|
+
*/
|
|
73
|
+
function getTimestampGenerator() {
|
|
74
|
+
if (!timestampGeneratorInstance) {
|
|
75
|
+
timestampGeneratorInstance = new TimestampGenerator();
|
|
76
|
+
}
|
|
77
|
+
return timestampGeneratorInstance;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Resets the singleton instance. Primarily for testing purposes.
|
|
81
|
+
*/
|
|
82
|
+
function resetTimestampGenerator() {
|
|
83
|
+
timestampGeneratorInstance = undefined;
|
|
84
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@magek/common",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "Contains Magek common helpers used by the core and provider packages",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"framework-common-helpers"
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
"uuid": "^13.0.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
|
-
"@magek/eslint-config": "^0.0.
|
|
36
|
+
"@magek/eslint-config": "^0.0.7",
|
|
37
37
|
"@types/chai": "5.2.3",
|
|
38
38
|
"@types/chai-as-promised": "8.0.2",
|
|
39
39
|
"@types/mocha": "10.0.10",
|
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import 'reflect-metadata';
|
|
2
|
-
/**
|
|
3
|
-
* Type function for specifying field types
|
|
4
|
-
* The parameter is optional and not used - it's just for TypeGraphQL-style ergonomics
|
|
5
|
-
*/
|
|
6
|
-
export type TypeFunction = (type?: unknown) => unknown;
|
|
7
|
-
/**
|
|
8
|
-
* Options for the @Field() decorator
|
|
9
|
-
*/
|
|
10
|
-
export interface FieldOptions {
|
|
11
|
-
nullable?: boolean;
|
|
12
|
-
readonly?: boolean;
|
|
13
|
-
}
|
|
14
|
-
/**
|
|
15
|
-
* Metadata stored for each field
|
|
16
|
-
*/
|
|
17
|
-
export interface FieldMetadata {
|
|
18
|
-
name: string;
|
|
19
|
-
typeFunction?: TypeFunction;
|
|
20
|
-
options: FieldOptions;
|
|
21
|
-
designType?: unknown;
|
|
22
|
-
}
|
|
23
|
-
/**
|
|
24
|
-
* Stage 3 decorator context for class fields (TypeScript 5.0+)
|
|
25
|
-
*/
|
|
26
|
-
interface Stage3FieldContext {
|
|
27
|
-
kind: 'field';
|
|
28
|
-
name: string | symbol;
|
|
29
|
-
static: boolean;
|
|
30
|
-
private: boolean;
|
|
31
|
-
metadata: Record<string | symbol, unknown>;
|
|
32
|
-
access?: {
|
|
33
|
-
get: () => unknown;
|
|
34
|
-
set: (value: unknown) => void;
|
|
35
|
-
};
|
|
36
|
-
addInitializer?: (initializer: () => void) => void;
|
|
37
|
-
}
|
|
38
|
-
/**
|
|
39
|
-
* Decorator return type that supports both legacy and Stage 3 formats.
|
|
40
|
-
* This is a function that can be called with either signature.
|
|
41
|
-
*/
|
|
42
|
-
type UniversalFieldDecorator = {
|
|
43
|
-
(target: object, propertyKey: string | symbol): void;
|
|
44
|
-
(value: undefined, context: Stage3FieldContext): void;
|
|
45
|
-
};
|
|
46
|
-
/**
|
|
47
|
-
* @Field() decorator for explicit type declaration
|
|
48
|
-
*
|
|
49
|
-
* Supports both legacy decorators (experimentalDecorators) and
|
|
50
|
-
* Stage 3 TC39 decorators.
|
|
51
|
-
*
|
|
52
|
-
* Usage:
|
|
53
|
-
* @Field() - Simple type (inferred from design:type in legacy mode)
|
|
54
|
-
* @Field(type => String) - Explicit type
|
|
55
|
-
* @Field(type => [String]) - Array type
|
|
56
|
-
* @Field({ nullable: true }) - With options
|
|
57
|
-
* @Field(type => String, { nullable: true }) - Type with options
|
|
58
|
-
*/
|
|
59
|
-
export declare function Field(): UniversalFieldDecorator;
|
|
60
|
-
export declare function Field(options: FieldOptions): UniversalFieldDecorator;
|
|
61
|
-
export declare function Field(typeFunction: TypeFunction): UniversalFieldDecorator;
|
|
62
|
-
export declare function Field(typeFunction: TypeFunction, options: FieldOptions): UniversalFieldDecorator;
|
|
63
|
-
export {};
|
package/dist/field-decorator.js
DELETED
|
@@ -1,122 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.Field = Field;
|
|
4
|
-
require("reflect-metadata");
|
|
5
|
-
// Symbol for storing fields metadata (for Stage 3 decorators)
|
|
6
|
-
const FIELDS_KEY = Symbol.for('magek:fields');
|
|
7
|
-
/**
|
|
8
|
-
* Type guard to detect Stage 3 decorator context
|
|
9
|
-
*/
|
|
10
|
-
function isStage3Context(arg) {
|
|
11
|
-
return (arg !== null &&
|
|
12
|
-
typeof arg === 'object' &&
|
|
13
|
-
'kind' in arg &&
|
|
14
|
-
arg.kind === 'field' &&
|
|
15
|
-
'name' in arg &&
|
|
16
|
-
'metadata' in arg);
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Store field metadata on a constructor (works for both legacy and Stage 3)
|
|
20
|
-
*/
|
|
21
|
-
function storeFieldMetadata(constructor, fieldMetadata) {
|
|
22
|
-
// Get existing fields
|
|
23
|
-
let existingFields = [];
|
|
24
|
-
// Try Reflect.getMetadata first
|
|
25
|
-
try {
|
|
26
|
-
if (typeof Reflect !== 'undefined' && typeof Reflect.getMetadata === 'function') {
|
|
27
|
-
existingFields = Reflect.getMetadata('magek:fields', constructor) || [];
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
catch {
|
|
31
|
-
// Ignore
|
|
32
|
-
}
|
|
33
|
-
// Also check fallback property
|
|
34
|
-
const ctorWithFields = constructor;
|
|
35
|
-
if (existingFields.length === 0 && ctorWithFields.__magek_fields__) {
|
|
36
|
-
existingFields = ctorWithFields.__magek_fields__;
|
|
37
|
-
}
|
|
38
|
-
// Add this field (avoid duplicates by name)
|
|
39
|
-
const filteredFields = existingFields.filter((f) => f.name !== fieldMetadata.name);
|
|
40
|
-
filteredFields.push(fieldMetadata);
|
|
41
|
-
// Store using both mechanisms for reliability
|
|
42
|
-
try {
|
|
43
|
-
if (typeof Reflect !== 'undefined' && typeof Reflect.defineMetadata === 'function') {
|
|
44
|
-
Reflect.defineMetadata('magek:fields', filteredFields, constructor);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
catch {
|
|
48
|
-
// Ignore
|
|
49
|
-
}
|
|
50
|
-
// Also store as a fallback property
|
|
51
|
-
ctorWithFields.__magek_fields__ = filteredFields;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Handle legacy decorator format (experimentalDecorators)
|
|
55
|
-
*/
|
|
56
|
-
function handleLegacyDecorator(target, propertyKey, typeFunction, fieldOptions) {
|
|
57
|
-
// Get design type from TypeScript decorator metadata
|
|
58
|
-
let designType;
|
|
59
|
-
try {
|
|
60
|
-
if (typeof Reflect !== 'undefined' && typeof Reflect.getMetadata === 'function') {
|
|
61
|
-
designType = Reflect.getMetadata('design:type', target, propertyKey);
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
catch {
|
|
65
|
-
// Ignore - Reflect.getMetadata may fail if called before initialization
|
|
66
|
-
}
|
|
67
|
-
const fieldMetadata = {
|
|
68
|
-
name: propertyKey.toString(),
|
|
69
|
-
typeFunction,
|
|
70
|
-
options: fieldOptions,
|
|
71
|
-
designType,
|
|
72
|
-
};
|
|
73
|
-
const constructor = target.constructor;
|
|
74
|
-
if (constructor) {
|
|
75
|
-
storeFieldMetadata(constructor, fieldMetadata);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
/**
|
|
79
|
-
* Handle Stage 3 decorator format (TC39 decorators)
|
|
80
|
-
*/
|
|
81
|
-
function handleStage3Decorator(context, typeFunction, fieldOptions) {
|
|
82
|
-
const fieldMetadata = {
|
|
83
|
-
name: context.name.toString(),
|
|
84
|
-
typeFunction,
|
|
85
|
-
options: fieldOptions,
|
|
86
|
-
designType: undefined, // Stage 3 doesn't have design:type
|
|
87
|
-
};
|
|
88
|
-
// Store in context.metadata for later retrieval by class decorators
|
|
89
|
-
// Class decorators (like @Command, @Entity, etc.) will transfer this to the class constructor
|
|
90
|
-
if (!context.metadata[FIELDS_KEY]) {
|
|
91
|
-
context.metadata[FIELDS_KEY] = [];
|
|
92
|
-
}
|
|
93
|
-
const fields = context.metadata[FIELDS_KEY];
|
|
94
|
-
const filteredFields = fields.filter((f) => f.name !== fieldMetadata.name);
|
|
95
|
-
filteredFields.push(fieldMetadata);
|
|
96
|
-
context.metadata[FIELDS_KEY] = filteredFields;
|
|
97
|
-
}
|
|
98
|
-
function Field(typeFunctionOrOptions, options) {
|
|
99
|
-
// Parse arguments
|
|
100
|
-
let typeFunction;
|
|
101
|
-
let fieldOptions = {};
|
|
102
|
-
if (typeof typeFunctionOrOptions === 'function') {
|
|
103
|
-
typeFunction = typeFunctionOrOptions;
|
|
104
|
-
fieldOptions = options || {};
|
|
105
|
-
}
|
|
106
|
-
else if (typeFunctionOrOptions) {
|
|
107
|
-
fieldOptions = typeFunctionOrOptions;
|
|
108
|
-
}
|
|
109
|
-
// Return decorator that handles both formats
|
|
110
|
-
return function fieldDecorator(targetOrValue, propertyKeyOrContext) {
|
|
111
|
-
// Detect Stage 3 vs Legacy based on the second argument
|
|
112
|
-
if (isStage3Context(propertyKeyOrContext)) {
|
|
113
|
-
// Stage 3 decorator
|
|
114
|
-
handleStage3Decorator(propertyKeyOrContext, typeFunction, fieldOptions);
|
|
115
|
-
}
|
|
116
|
-
else if (targetOrValue) {
|
|
117
|
-
// Legacy decorator
|
|
118
|
-
handleLegacyDecorator(targetOrValue, propertyKeyOrContext, typeFunction, fieldOptions);
|
|
119
|
-
}
|
|
120
|
-
// If targetOrValue is undefined and not Stage 3, silently skip
|
|
121
|
-
};
|
|
122
|
-
}
|