@magek/core 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/authorizer.js +1 -1
- package/dist/command-dispatcher.js +47 -41
- package/dist/core-concepts/data-migration/entities/data-migration-entity.js +5 -2
- package/dist/core-concepts/data-migration/events/data-migration-finished.js +3 -1
- package/dist/core-concepts/data-migration/events/data-migration-started.js +3 -1
- package/dist/core-concepts/data-migration/events/entity-migrated.js +4 -0
- package/dist/core-concepts/touch-entity/events/entity-touched.js +2 -0
- package/dist/data-migrations.js +59 -54
- package/dist/decorators/command.d.ts +4 -11
- package/dist/decorators/command.js +10 -18
- package/dist/decorators/data-migration.d.ts +4 -1
- package/dist/decorators/data-migration.js +3 -1
- package/dist/decorators/decorator-types.d.ts +50 -0
- package/dist/decorators/decorator-types.js +11 -0
- package/dist/decorators/decorator-utils.d.ts +1 -0
- package/dist/decorators/decorator-utils.js +2 -0
- package/dist/decorators/entity.d.ts +7 -15
- package/dist/decorators/entity.js +27 -37
- package/dist/decorators/event-handler.d.ts +11 -1
- package/dist/decorators/event-handler.js +13 -1
- package/dist/decorators/event.d.ts +4 -1
- package/dist/decorators/event.js +3 -2
- package/dist/decorators/field-metadata-reader.d.ts +8 -3
- package/dist/decorators/field-metadata-reader.js +50 -62
- package/dist/decorators/field.d.ts +23 -0
- package/dist/decorators/field.js +41 -0
- package/dist/decorators/global-error-handler.d.ts +10 -1
- package/dist/decorators/global-error-handler.js +9 -1
- package/dist/decorators/global-event-handler.d.ts +10 -1
- package/dist/decorators/global-event-handler.js +9 -1
- package/dist/decorators/health-sensor.d.ts +4 -1
- package/dist/decorators/health-sensor.js +3 -2
- package/dist/decorators/index.d.ts +3 -0
- package/dist/decorators/index.js +2 -0
- package/dist/decorators/metadata.d.ts +17 -1
- package/dist/decorators/metadata.js +22 -22
- package/dist/decorators/non-exposed.d.ts +13 -2
- package/dist/decorators/non-exposed.js +22 -20
- package/dist/decorators/notification.d.ts +6 -18
- package/dist/decorators/notification.js +10 -50
- package/dist/decorators/projects.d.ts +5 -18
- package/dist/decorators/projects.js +23 -54
- package/dist/decorators/query.d.ts +11 -1
- package/dist/decorators/query.js +18 -4
- package/dist/decorators/read-model.d.ts +13 -27
- package/dist/decorators/read-model.js +45 -77
- package/dist/decorators/role.d.ts +9 -3
- package/dist/decorators/role.js +8 -3
- package/dist/decorators/scheduled-command.d.ts +4 -1
- package/dist/decorators/scheduled-command.js +3 -1
- package/dist/decorators/schema-migration.d.ts +11 -27
- package/dist/decorators/schema-migration.js +32 -77
- package/dist/decorators/sequenced-by.d.ts +7 -25
- package/dist/decorators/sequenced-by.js +11 -71
- package/dist/event-dispatcher.js +29 -24
- package/dist/event-processor.js +106 -103
- package/dist/event-stream-consumer.js +25 -20
- package/dist/event-stream-producer.js +22 -17
- package/dist/events-reader.js +1 -0
- package/dist/global-error-dispatcher.js +3 -2
- package/dist/graphql-dispatcher.js +161 -156
- package/dist/index.js +4 -0
- package/dist/instrumentation/decorator/trace.d.ts +11 -3
- package/dist/instrumentation/decorator/trace.js +17 -71
- package/dist/magek.js +2 -2
- package/dist/query-dispatcher.js +2 -0
- package/dist/read-model-schema-migrator.js +71 -68
- package/dist/read-models-reader.js +178 -180
- package/dist/register-handler.js +3 -3
- package/dist/scheduled-command-dispatcher.js +48 -42
- package/dist/schema-migrator.js +63 -59
- package/dist/sensor/health/health-service.js +2 -1
- package/dist/services/event-store.js +221 -224
- package/dist/services/graphql/graphql-generator.js +11 -8
- package/dist/services/graphql/graphql-mutation-generator.js +4 -0
- package/dist/services/graphql/graphql-query-generator.js +14 -0
- package/dist/services/graphql/graphql-subcriptions-generator.js +7 -0
- package/dist/services/graphql/graphql-type-informer.js +4 -3
- package/dist/services/graphql/query-generators/graphql-query-by-keys-generator.js +4 -0
- package/dist/services/graphql/query-generators/graphql-query-events-generator.js +3 -0
- package/dist/services/graphql/query-generators/graphql-query-filters-generator.js +6 -0
- package/dist/services/graphql/query-generators/graphql-query-generator.js +4 -0
- package/dist/services/graphql/query-generators/graphql-query-listed-generator.js +7 -0
- package/dist/services/graphql/query-helpers/graphql-handled-fields-generator.js +5 -2
- package/dist/services/graphql/query-helpers/graphql-query-filter-arguments-builder.js +3 -0
- package/dist/services/graphql/query-helpers/graphql-query-filter-fields-builder.js +4 -0
- package/dist/services/graphql/query-helpers/graphql-query-sort-builder.js +4 -2
- package/dist/services/graphql/websocket-protocol/graphql-websocket-protocol.js +5 -3
- package/dist/services/pub-sub/read-model-pub-sub.js +1 -0
- package/dist/services/raw-events-parser.js +1 -1
- package/dist/services/read-model-store.js +16 -17
- package/dist/services/token-verifiers/jwks-uri-token-verifier.js +8 -4
- package/dist/services/token-verifiers/public-key-token-verifier.js +4 -2
- package/dist/services/token-verifiers/role-based-token-verifier.js +2 -1
- package/dist/services/token-verifiers/utilities.js +1 -1
- package/dist/subscribers-notifier.js +98 -92
- package/dist/token-verifier.js +2 -1
- package/package.json +4 -4
- package/dist/decorators/stage3-utils.d.ts +0 -6
- package/dist/decorators/stage3-utils.js +0 -25
|
@@ -1,35 +1,23 @@
|
|
|
1
1
|
import { Class, NotificationInterface } from '@magek/common';
|
|
2
|
+
import { FieldDecoratorContext, ClassDecoratorContext } from './decorator-types';
|
|
2
3
|
export type NotificationOptions = {
|
|
3
4
|
topic?: string;
|
|
4
5
|
};
|
|
5
|
-
/**
|
|
6
|
-
* Stage 3 field decorator context
|
|
7
|
-
*/
|
|
8
|
-
interface Stage3FieldContext {
|
|
9
|
-
kind: 'field';
|
|
10
|
-
name: string | symbol;
|
|
11
|
-
static: boolean;
|
|
12
|
-
private: boolean;
|
|
13
|
-
metadata: Record<string | symbol, unknown>;
|
|
14
|
-
addInitializer?: (initializer: () => void) => void;
|
|
15
|
-
}
|
|
16
6
|
/**
|
|
17
7
|
* Decorator to mark a class as a Magek Notification.
|
|
18
8
|
* Notifications are events that can be published to external systems.
|
|
19
9
|
*
|
|
10
|
+
* Uses TC39 Stage 3 decorators.
|
|
11
|
+
*
|
|
20
12
|
* @param options - Optional configuration for the notification (e.g., topic name)
|
|
21
13
|
* @returns A class decorator function
|
|
22
14
|
*/
|
|
23
|
-
export declare const Notification: <TEvent extends NotificationInterface>(options?: NotificationOptions) => (eventClass: Class<TEvent>, context
|
|
24
|
-
kind: "class";
|
|
25
|
-
metadata: Record<string | symbol, unknown>;
|
|
26
|
-
}) => void;
|
|
15
|
+
export declare const Notification: <TEvent extends NotificationInterface>(options?: NotificationOptions) => (eventClass: Class<TEvent>, context: ClassDecoratorContext) => void;
|
|
27
16
|
/**
|
|
28
17
|
* Optional decorator that marks a field in a notification class as the partition key.
|
|
29
18
|
* This is useful if you want to guarantee that all the event handlers for notifications
|
|
30
19
|
* of the same type will be executed in the same order.
|
|
31
20
|
*
|
|
32
|
-
*
|
|
21
|
+
* Uses TC39 Stage 3 decorators.
|
|
33
22
|
*/
|
|
34
|
-
export declare function partitionKey(
|
|
35
|
-
export {};
|
|
23
|
+
export declare function partitionKey(_value: undefined, context: FieldDecoratorContext): void;
|
|
@@ -3,41 +3,31 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Notification = void 0;
|
|
4
4
|
exports.partitionKey = partitionKey;
|
|
5
5
|
const magek_1 = require("../magek");
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Type guard to detect Stage 3 field decorator context
|
|
9
|
-
*/
|
|
10
|
-
function isStage3FieldContext(arg) {
|
|
11
|
-
return (arg !== null &&
|
|
12
|
-
typeof arg === 'object' &&
|
|
13
|
-
'kind' in arg &&
|
|
14
|
-
arg.kind === 'field' &&
|
|
15
|
-
'name' in arg);
|
|
16
|
-
}
|
|
17
|
-
// Symbol for storing partition key in Stage 3 metadata
|
|
6
|
+
// Symbol for storing partition key in decorator metadata
|
|
18
7
|
const PARTITION_KEY_SYMBOL = Symbol.for('magek:partitionKey');
|
|
19
8
|
/**
|
|
20
9
|
* Decorator to mark a class as a Magek Notification.
|
|
21
10
|
* Notifications are events that can be published to external systems.
|
|
22
11
|
*
|
|
12
|
+
* Uses TC39 Stage 3 decorators.
|
|
13
|
+
*
|
|
23
14
|
* @param options - Optional configuration for the notification (e.g., topic name)
|
|
24
15
|
* @returns A class decorator function
|
|
25
16
|
*/
|
|
26
17
|
const Notification = (options) => (eventClass, context) => {
|
|
27
18
|
// Handle Stage 3: transfer partition key from context.metadata to config
|
|
28
|
-
if (context
|
|
19
|
+
if (context.metadata && context.metadata[PARTITION_KEY_SYMBOL]) {
|
|
29
20
|
const propertyName = context.metadata[PARTITION_KEY_SYMBOL];
|
|
30
21
|
magek_1.Magek.configureCurrentEnv((config) => {
|
|
31
22
|
config.partitionKeys[eventClass.name] = propertyName;
|
|
32
23
|
});
|
|
33
24
|
}
|
|
34
25
|
magek_1.Magek.configureCurrentEnv((config) => {
|
|
35
|
-
var _a;
|
|
36
26
|
if (config.notifications[eventClass.name] || config.events[eventClass.name]) {
|
|
37
27
|
throw new Error(`A notification called ${eventClass.name} is already registered.
|
|
38
28
|
If you think that this is an error, try performing a clean build.`);
|
|
39
29
|
}
|
|
40
|
-
const topic =
|
|
30
|
+
const topic = options?.topic ?? 'defaultTopic';
|
|
41
31
|
if (topic) {
|
|
42
32
|
config.eventToTopic[eventClass.name] = topic;
|
|
43
33
|
config.topicToEvent[topic] = eventClass.name;
|
|
@@ -53,42 +43,12 @@ exports.Notification = Notification;
|
|
|
53
43
|
* This is useful if you want to guarantee that all the event handlers for notifications
|
|
54
44
|
* of the same type will be executed in the same order.
|
|
55
45
|
*
|
|
56
|
-
*
|
|
46
|
+
* Uses TC39 Stage 3 decorators.
|
|
57
47
|
*/
|
|
58
|
-
function partitionKey(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
const propertyName = String(context.name);
|
|
63
|
-
// Store in context.metadata so @Notification can read it
|
|
48
|
+
function partitionKey(_value, context) {
|
|
49
|
+
const propertyName = String(context.name);
|
|
50
|
+
// Store in context.metadata so @Notification can read it
|
|
51
|
+
if (context.metadata) {
|
|
64
52
|
context.metadata[PARTITION_KEY_SYMBOL] = propertyName;
|
|
65
|
-
return;
|
|
66
|
-
}
|
|
67
|
-
// Property decorator usage: @partitionKey on a class property (legacy)
|
|
68
|
-
if (propertyKeyOrContext !== undefined && parameterIndex === undefined && target) {
|
|
69
|
-
const notificationClass = target.constructor;
|
|
70
|
-
const propertyName = String(propertyKeyOrContext);
|
|
71
|
-
magek_1.Magek.configureCurrentEnv((config) => {
|
|
72
|
-
if (config.partitionKeys[notificationClass.name] && config.partitionKeys[notificationClass.name] !== propertyName) {
|
|
73
|
-
throw new Error(`Error trying to register a partition key named \`${propertyName}\` for class \`${notificationClass.name}\`. It already had the partition key \`${config.partitionKeys[notificationClass.name]}\` defined and only one partition key is allowed for each notification event.`);
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
config.partitionKeys[notificationClass.name] = propertyName;
|
|
77
|
-
}
|
|
78
|
-
});
|
|
79
|
-
}
|
|
80
|
-
// Parameter decorator usage: @partitionKey on constructor parameter
|
|
81
|
-
else if (parameterIndex !== undefined && target) {
|
|
82
|
-
const notificationClass = target;
|
|
83
|
-
const args = (0, metadata_1.getFunctionArguments)(notificationClass);
|
|
84
|
-
const propertyName = args[parameterIndex];
|
|
85
|
-
magek_1.Magek.configureCurrentEnv((config) => {
|
|
86
|
-
if (config.partitionKeys[notificationClass.name] && config.partitionKeys[notificationClass.name] !== propertyName) {
|
|
87
|
-
throw new Error(`Error trying to register a partition key named \`${propertyName}\` for class \`${notificationClass.name}\`. It already had the partition key \`${config.partitionKeys[notificationClass.name]}\` defined and only one partition key is allowed for each notification event.`);
|
|
88
|
-
}
|
|
89
|
-
else {
|
|
90
|
-
config.partitionKeys[notificationClass.name] = propertyName;
|
|
91
|
-
}
|
|
92
|
-
});
|
|
93
53
|
}
|
|
94
54
|
}
|
|
@@ -1,32 +1,19 @@
|
|
|
1
1
|
import { Class, EntityInterface, ProjectionInfo, ProjectionResult, ReadModelInterface, ReadModelJoinKeyFunction, UUID } from '@magek/common';
|
|
2
|
+
import { MethodDecoratorContext } from './decorator-types';
|
|
2
3
|
type PropertyType<TObj, TProp extends keyof TObj> = TObj[TProp];
|
|
3
4
|
type JoinKeyType<TEntity extends EntityInterface, TReadModel extends ReadModelInterface> = keyof TEntity | ReadModelJoinKeyFunction<TEntity, TReadModel>;
|
|
4
|
-
type UUIDLike = string | UUID;
|
|
5
|
-
/**
|
|
6
|
-
* Stage 3 method decorator context
|
|
7
|
-
*/
|
|
8
|
-
interface Stage3MethodContext {
|
|
9
|
-
kind: 'method';
|
|
10
|
-
name: string | symbol;
|
|
11
|
-
static: boolean;
|
|
12
|
-
private: boolean;
|
|
13
|
-
metadata: Record<string | symbol, unknown>;
|
|
14
|
-
addInitializer?: (initializer: () => void) => void;
|
|
15
|
-
}
|
|
16
5
|
/**
|
|
17
6
|
* Decorator to register a read model method as a projection
|
|
18
|
-
* for a specific entity
|
|
7
|
+
* for a specific entity.
|
|
8
|
+
*
|
|
9
|
+
* Uses TC39 Stage 3 decorators.
|
|
19
10
|
*
|
|
20
11
|
* @param originEntity The entity that this method will react to
|
|
21
12
|
* @param joinKey
|
|
22
13
|
* @param unProject
|
|
23
14
|
*/
|
|
24
|
-
export declare function
|
|
15
|
+
export declare function projects<TEntity extends EntityInterface, TJoinKey extends keyof TEntity, TReadModel extends ReadModelInterface>(originEntity: Class<TEntity>, joinKey: JoinKeyType<TEntity, TReadModel>, unProject?: UnprojectionMethod<TEntity, TReadModel, PropertyType<TEntity, TJoinKey>>): <TReceivedReadModel extends ReadModelInterface>(method: Function, context: MethodDecoratorContext) => void;
|
|
25
16
|
type ProjectionMethodDefinitionForArray<TEntity, TReadModel> = (_: TEntity, readModelID: UUID, readModel?: TReadModel, projectionInfo?: ProjectionInfo) => ProjectionResult<TReadModel>;
|
|
26
17
|
type ProjectionMethodDefinition<TEntity, TReadModel> = (_: TEntity, readModel?: TReadModel, projectionInfo?: ProjectionInfo) => ProjectionResult<TReadModel>;
|
|
27
|
-
type ProjectionMethod<TEntity extends EntityInterface, TReadModel extends ReadModelInterface, TJoinKeyType extends JoinKeyType<TEntity, TReadModel>> = TJoinKeyType extends ReadModelJoinKeyFunction<TEntity, TReadModel> ? ProjectionMethodWithEntityConditionalReadModelIdAndReadModel<TEntity, TReadModel> : TJoinKeyType extends keyof TEntity ? NonNullable<PropertyType<TEntity, TJoinKeyType>> extends Array<UUIDLike> ? ProjectionMethodWithEntityReadModelIdAndReadModel<TEntity, TReadModel> : ProjectionMethodWithEntityAndReadModel<TEntity, TReadModel> : never;
|
|
28
|
-
type ProjectionMethodWithEntityAndReadModel<TEntity extends EntityInterface, TReadModel extends ReadModelInterface> = TypedPropertyDescriptor<(_: TEntity, readModel?: TReadModel) => ProjectionResult<TReadModel>>;
|
|
29
|
-
type ProjectionMethodWithEntityConditionalReadModelIdAndReadModel<TEntity extends EntityInterface, TReadModel extends ReadModelInterface> = TypedPropertyDescriptor<(_: TEntity, readModelID: UUID | undefined, readModel?: TReadModel) => ProjectionResult<TReadModel>>;
|
|
30
|
-
type ProjectionMethodWithEntityReadModelIdAndReadModel<TEntity extends EntityInterface, TReadModel extends ReadModelInterface> = TypedPropertyDescriptor<(_: TEntity, readModelID: UUID, readModel?: TReadModel) => ProjectionResult<TReadModel>>;
|
|
31
18
|
type UnprojectionMethod<TEntity, TReadModel, TPropType> = TPropType extends Array<UUID> ? ProjectionMethodDefinitionForArray<TEntity, TReadModel> : ProjectionMethodDefinition<TEntity, TReadModel>;
|
|
32
19
|
export {};
|
|
@@ -1,69 +1,38 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.projects = projects;
|
|
4
4
|
const magek_1 = require("../magek");
|
|
5
|
-
/**
|
|
6
|
-
* Type guard for Stage 3 method context
|
|
7
|
-
*/
|
|
8
|
-
function isStage3MethodContext(arg) {
|
|
9
|
-
return (arg !== null &&
|
|
10
|
-
typeof arg === 'object' &&
|
|
11
|
-
'kind' in arg &&
|
|
12
|
-
arg.kind === 'method' &&
|
|
13
|
-
'name' in arg);
|
|
14
|
-
}
|
|
15
5
|
/**
|
|
16
6
|
* Decorator to register a read model method as a projection
|
|
17
|
-
* for a specific entity
|
|
7
|
+
* for a specific entity.
|
|
8
|
+
*
|
|
9
|
+
* Uses TC39 Stage 3 decorators.
|
|
18
10
|
*
|
|
19
11
|
* @param originEntity The entity that this method will react to
|
|
20
12
|
* @param joinKey
|
|
21
13
|
* @param unProject
|
|
22
14
|
*/
|
|
23
|
-
function
|
|
24
|
-
return (
|
|
25
|
-
//
|
|
26
|
-
if (
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
const readModelClass = context.static ? this : this.constructor;
|
|
32
|
-
const projectionMetadata = {
|
|
33
|
-
joinKey: joinKey,
|
|
34
|
-
class: readModelClass,
|
|
35
|
-
methodName: context.name.toString(),
|
|
36
|
-
};
|
|
37
|
-
registerProjection(originEntity.name, projectionMetadata);
|
|
38
|
-
if (unProject) {
|
|
39
|
-
const unProjectionMetadata = {
|
|
40
|
-
joinKey,
|
|
41
|
-
class: readModelClass,
|
|
42
|
-
methodName: unProject.name,
|
|
43
|
-
};
|
|
44
|
-
registerUnProjection(originEntity.name, unProjectionMetadata);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
else {
|
|
50
|
-
// Legacy decorator
|
|
51
|
-
const readModelClass = readModelClassOrMethod;
|
|
52
|
-
const methodName = methodNameOrContext;
|
|
53
|
-
const projectionMetadata = {
|
|
54
|
-
joinKey: joinKey,
|
|
55
|
-
class: readModelClass,
|
|
56
|
-
methodName: methodName,
|
|
57
|
-
};
|
|
58
|
-
registerProjection(originEntity.name, projectionMetadata);
|
|
59
|
-
if (unProject) {
|
|
60
|
-
const unProjectionMetadata = {
|
|
61
|
-
joinKey,
|
|
15
|
+
function projects(originEntity, joinKey, unProject) {
|
|
16
|
+
return (_method, context) => {
|
|
17
|
+
// Stage 3 decorator - use addInitializer to get the class
|
|
18
|
+
if (context.addInitializer) {
|
|
19
|
+
context.addInitializer(function () {
|
|
20
|
+
const readModelClass = context.static ? this : this.constructor;
|
|
21
|
+
const projectionMetadata = {
|
|
22
|
+
joinKey: joinKey,
|
|
62
23
|
class: readModelClass,
|
|
63
|
-
methodName:
|
|
24
|
+
methodName: context.name.toString(),
|
|
64
25
|
};
|
|
65
|
-
|
|
66
|
-
|
|
26
|
+
registerProjection(originEntity.name, projectionMetadata);
|
|
27
|
+
if (unProject) {
|
|
28
|
+
const unProjectionMetadata = {
|
|
29
|
+
joinKey,
|
|
30
|
+
class: readModelClass,
|
|
31
|
+
methodName: unProject.name,
|
|
32
|
+
};
|
|
33
|
+
registerUnProjection(originEntity.name, unProjectionMetadata);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
67
36
|
}
|
|
68
37
|
};
|
|
69
38
|
}
|
|
@@ -1,2 +1,12 @@
|
|
|
1
1
|
import { CommandFilterHooks, QueryInterface, QueryRoleAccess } from '@magek/common';
|
|
2
|
-
|
|
2
|
+
import { ClassDecoratorContext } from './decorator-utils';
|
|
3
|
+
/**
|
|
4
|
+
* Decorator to mark a class as a Magek Query.
|
|
5
|
+
* Queries represent read operations that don't modify state.
|
|
6
|
+
*
|
|
7
|
+
* Uses TC39 Stage 3 decorators.
|
|
8
|
+
*
|
|
9
|
+
* @param attributes - Role access control and filter hooks configuration
|
|
10
|
+
* @returns A class decorator function
|
|
11
|
+
*/
|
|
12
|
+
export declare function Query(attributes: QueryRoleAccess & CommandFilterHooks): <TCommand>(queryClass: QueryInterface<TCommand>, context: ClassDecoratorContext) => void;
|
package/dist/decorators/query.js
CHANGED
|
@@ -4,22 +4,36 @@ exports.Query = Query;
|
|
|
4
4
|
const magek_1 = require("../magek");
|
|
5
5
|
const metadata_1 = require("./metadata");
|
|
6
6
|
const authorizer_1 = require("../authorizer");
|
|
7
|
+
/**
|
|
8
|
+
* Decorator to mark a class as a Magek Query.
|
|
9
|
+
* Queries represent read operations that don't modify state.
|
|
10
|
+
*
|
|
11
|
+
* Uses TC39 Stage 3 decorators.
|
|
12
|
+
*
|
|
13
|
+
* @param attributes - Role access control and filter hooks configuration
|
|
14
|
+
* @returns A class decorator function
|
|
15
|
+
*/
|
|
7
16
|
function Query(attributes) {
|
|
8
|
-
return (queryClass) => {
|
|
17
|
+
return (queryClass, context) => {
|
|
9
18
|
magek_1.Magek.configureCurrentEnv((config) => {
|
|
10
|
-
var _a;
|
|
11
19
|
if (config.queryHandlers[queryClass.name]) {
|
|
12
20
|
throw new Error(`A query called ${queryClass.name} is already registered.
|
|
13
21
|
If you think that this is an error, try performing a clean build.`);
|
|
14
22
|
}
|
|
15
|
-
|
|
23
|
+
// Pass context.metadata because Symbol.metadata isn't attached to class yet during decorator execution
|
|
24
|
+
const metadata = (0, metadata_1.getClassMetadata)(queryClass, context.metadata);
|
|
16
25
|
config.queryHandlers[queryClass.name] = {
|
|
17
26
|
class: queryClass,
|
|
18
27
|
authorizer: authorizer_1.MagekAuthorizer.build(attributes),
|
|
19
28
|
properties: metadata.fields,
|
|
20
29
|
methods: metadata.methods,
|
|
21
|
-
before:
|
|
30
|
+
before: attributes.before ?? [],
|
|
22
31
|
};
|
|
32
|
+
// Register non-exposed fields from context.metadata
|
|
33
|
+
const nonExposedFields = (0, metadata_1.getNonExposedFields)(context.metadata);
|
|
34
|
+
if (nonExposedFields.length > 0) {
|
|
35
|
+
config.nonExposedGraphQLMetadataKey[queryClass.name] = nonExposedFields;
|
|
36
|
+
}
|
|
23
37
|
});
|
|
24
38
|
};
|
|
25
39
|
}
|
|
@@ -1,39 +1,25 @@
|
|
|
1
1
|
import { Class, ReadModelFilterHooks, ReadModelInterface, ReadModelRoleAccess } from '@magek/common';
|
|
2
|
+
import { ClassDecoratorContext, GetterDecoratorContext } from './decorator-utils';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
name: string | undefined;
|
|
8
|
-
metadata: Record<string | symbol, unknown>;
|
|
9
|
-
addInitializer?: (initializer: () => void) => void;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Decorator to register a class as a ReadModel
|
|
4
|
+
* Decorator to register a class as a ReadModel.
|
|
5
|
+
*
|
|
6
|
+
* Uses TC39 Stage 3 decorators.
|
|
7
|
+
*
|
|
13
8
|
* @param attributes
|
|
14
9
|
*/
|
|
15
|
-
export declare function ReadModel(attributes: ReadModelRoleAccess & ReadModelFilterHooks): (readModelClass: Class<ReadModelInterface>, context
|
|
10
|
+
export declare function ReadModel(attributes: ReadModelRoleAccess & ReadModelFilterHooks): (readModelClass: Class<ReadModelInterface>, context: ClassDecoratorContext) => void;
|
|
16
11
|
interface CalculatedFieldOptions {
|
|
17
12
|
dependsOn: string[];
|
|
18
13
|
}
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
*/
|
|
22
|
-
interface Stage3GetterContext {
|
|
23
|
-
kind: 'getter';
|
|
24
|
-
name: string | symbol;
|
|
25
|
-
static: boolean;
|
|
26
|
-
private: boolean;
|
|
27
|
-
metadata: Record<string | symbol, unknown>;
|
|
28
|
-
access?: {
|
|
29
|
-
get: () => unknown;
|
|
30
|
-
};
|
|
31
|
-
addInitializer?: (initializer: () => void) => void;
|
|
32
|
-
}
|
|
14
|
+
/** Symbol for storing calculated field dependencies in decorator context.metadata */
|
|
15
|
+
export declare const CALCULATED_FIELDS_SYMBOL: unique symbol;
|
|
33
16
|
/**
|
|
34
17
|
* Decorator to mark a property as a calculated field with dependencies.
|
|
35
|
-
*
|
|
18
|
+
*
|
|
19
|
+
* Uses TC39 Stage 3 decorators.
|
|
20
|
+
* Dependencies are stored in context.metadata and read by field-metadata-reader.
|
|
21
|
+
*
|
|
36
22
|
* @param options - A `CalculatedFieldOptions` object indicating the dependencies.
|
|
37
23
|
*/
|
|
38
|
-
export declare function
|
|
24
|
+
export declare function calculatedField(options: CalculatedFieldOptions): (target: Function, context: GetterDecoratorContext) => void;
|
|
39
25
|
export {};
|
|
@@ -1,56 +1,59 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CALCULATED_FIELDS_SYMBOL = void 0;
|
|
3
4
|
exports.ReadModel = ReadModel;
|
|
4
|
-
exports.
|
|
5
|
-
const common_1 = require("@magek/common");
|
|
5
|
+
exports.calculatedField = calculatedField;
|
|
6
6
|
const magek_1 = require("../magek");
|
|
7
7
|
const authorizer_1 = require("../authorizer");
|
|
8
8
|
const metadata_1 = require("./metadata");
|
|
9
|
-
const stage3_utils_1 = require("./stage3-utils");
|
|
10
9
|
const sequenced_by_1 = require("./sequenced-by");
|
|
11
10
|
/**
|
|
12
|
-
*
|
|
11
|
+
* Register sequence key for a class
|
|
13
12
|
*/
|
|
14
|
-
function
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
13
|
+
function registerSequenceKey(klass, propertyName) {
|
|
14
|
+
magek_1.Magek.configureCurrentEnv((config) => {
|
|
15
|
+
if (config.readModelSequenceKeys[klass.name] && config.readModelSequenceKeys[klass.name] !== propertyName) {
|
|
16
|
+
throw new Error(`Error trying to register a sort key named \`${propertyName}\` for class \`${klass.name}\`. It already had the sort key \`${config.readModelSequenceKeys[klass.name]}\` defined and only one sort key is allowed for each read model.`);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
config.readModelSequenceKeys[klass.name] = propertyName;
|
|
20
|
+
}
|
|
21
|
+
});
|
|
20
22
|
}
|
|
21
23
|
/**
|
|
22
|
-
* Decorator to register a class as a ReadModel
|
|
24
|
+
* Decorator to register a class as a ReadModel.
|
|
25
|
+
*
|
|
26
|
+
* Uses TC39 Stage 3 decorators.
|
|
27
|
+
*
|
|
23
28
|
* @param attributes
|
|
24
29
|
*/
|
|
25
30
|
function ReadModel(attributes) {
|
|
26
31
|
return (readModelClass, context) => {
|
|
27
|
-
//
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
(
|
|
31
|
-
transferCalculatedFieldDependencies(readModelClass, context.metadata);
|
|
32
|
+
// Read sequence key from Symbol.metadata and register to config
|
|
33
|
+
const sequenceKey = context.metadata?.[sequenced_by_1.SEQUENCE_KEY_SYMBOL];
|
|
34
|
+
if (sequenceKey) {
|
|
35
|
+
registerSequenceKey(readModelClass, sequenceKey);
|
|
32
36
|
}
|
|
33
37
|
magek_1.Magek.configureCurrentEnv((config) => {
|
|
34
|
-
var _a;
|
|
35
38
|
if (config.readModels[readModelClass.name]) {
|
|
36
39
|
throw new Error(`A read model called ${readModelClass.name} is already registered.
|
|
37
40
|
If you think that this is an error, try performing a clean build.`);
|
|
38
41
|
}
|
|
39
42
|
const authorizer = authorizer_1.MagekAuthorizer.build(attributes);
|
|
40
|
-
|
|
41
|
-
const
|
|
42
|
-
// Combine fields with
|
|
43
|
+
// Pass context.metadata because Symbol.metadata isn't attached to class yet during decorator execution
|
|
44
|
+
const classMetadata = (0, metadata_1.getClassMetadata)(readModelClass, context.metadata);
|
|
45
|
+
// Combine fields with empty dependencies (fields don't have dependencies)
|
|
43
46
|
const fieldProperties = classMetadata.fields.map((field) => {
|
|
44
47
|
return {
|
|
45
48
|
...field,
|
|
46
|
-
dependencies:
|
|
49
|
+
dependencies: [],
|
|
47
50
|
};
|
|
48
51
|
});
|
|
49
52
|
// Include calculated fields (getters) from methods with their dependencies
|
|
53
|
+
// Dependencies are already included in method from getAllGetters (reads from Symbol.metadata)
|
|
50
54
|
const methodProperties = classMetadata.methods.map((method) => {
|
|
51
55
|
return {
|
|
52
56
|
...method,
|
|
53
|
-
// Dependencies already included in method from getAllGetters
|
|
54
57
|
};
|
|
55
58
|
});
|
|
56
59
|
// Merge fields and methods into properties
|
|
@@ -59,71 +62,36 @@ function ReadModel(attributes) {
|
|
|
59
62
|
class: readModelClass,
|
|
60
63
|
properties,
|
|
61
64
|
authorizer,
|
|
62
|
-
before:
|
|
65
|
+
before: attributes.before ?? [],
|
|
63
66
|
};
|
|
67
|
+
// Register non-exposed fields from context.metadata
|
|
68
|
+
const nonExposedFields = (0, metadata_1.getNonExposedFields)(context.metadata);
|
|
69
|
+
if (nonExposedFields.length > 0) {
|
|
70
|
+
config.nonExposedGraphQLMetadataKey[readModelClass.name] = nonExposedFields;
|
|
71
|
+
}
|
|
64
72
|
});
|
|
65
73
|
};
|
|
66
74
|
}
|
|
67
|
-
/**
|
|
68
|
-
|
|
69
|
-
*/
|
|
70
|
-
function isStage3GetterContext(arg) {
|
|
71
|
-
return (arg !== null &&
|
|
72
|
-
typeof arg === 'object' &&
|
|
73
|
-
'kind' in arg &&
|
|
74
|
-
arg.kind === 'getter' &&
|
|
75
|
-
'name' in arg &&
|
|
76
|
-
'metadata' in arg);
|
|
77
|
-
}
|
|
78
|
-
// Symbol for storing calculated field dependencies in Stage 3 decorator context.metadata
|
|
79
|
-
const CALCULATED_FIELDS_SYMBOL = Symbol.for('magek:calculatedFields');
|
|
80
|
-
/**
|
|
81
|
-
* Transfer calculated field dependencies from Stage 3 context.metadata to class metadata.
|
|
82
|
-
* Called by the ReadModel class decorator.
|
|
83
|
-
*/
|
|
84
|
-
function transferCalculatedFieldDependencies(classType, contextMetadata) {
|
|
85
|
-
const calculatedFields = contextMetadata[CALCULATED_FIELDS_SYMBOL];
|
|
86
|
-
if (calculatedFields) {
|
|
87
|
-
const existingDependencies = (0, common_1.getMetadata)('dynamic:dependencies', classType) || {};
|
|
88
|
-
for (const [propertyName, dependencies] of Object.entries(calculatedFields)) {
|
|
89
|
-
existingDependencies[propertyName] = dependencies;
|
|
90
|
-
}
|
|
91
|
-
(0, common_1.defineMetadata)('dynamic:dependencies', existingDependencies, classType);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
75
|
+
/** Symbol for storing calculated field dependencies in decorator context.metadata */
|
|
76
|
+
exports.CALCULATED_FIELDS_SYMBOL = Symbol.for('magek:calculatedFields');
|
|
94
77
|
/**
|
|
95
78
|
* Decorator to mark a property as a calculated field with dependencies.
|
|
96
|
-
*
|
|
79
|
+
*
|
|
80
|
+
* Uses TC39 Stage 3 decorators.
|
|
81
|
+
* Dependencies are stored in context.metadata and read by field-metadata-reader.
|
|
82
|
+
*
|
|
97
83
|
* @param options - A `CalculatedFieldOptions` object indicating the dependencies.
|
|
98
84
|
*/
|
|
99
|
-
function
|
|
100
|
-
return (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
if (!context.metadata[CALCULATED_FIELDS_SYMBOL]) {
|
|
107
|
-
context.metadata[CALCULATED_FIELDS_SYMBOL] = {};
|
|
85
|
+
function calculatedField(options) {
|
|
86
|
+
return (_target, context) => {
|
|
87
|
+
const propertyName = String(context.name);
|
|
88
|
+
// Store in context.metadata (becomes class[Symbol.metadata])
|
|
89
|
+
if (context.metadata) {
|
|
90
|
+
if (!context.metadata[exports.CALCULATED_FIELDS_SYMBOL]) {
|
|
91
|
+
context.metadata[exports.CALCULATED_FIELDS_SYMBOL] = {};
|
|
108
92
|
}
|
|
109
|
-
const calculatedFields = context.metadata[CALCULATED_FIELDS_SYMBOL];
|
|
93
|
+
const calculatedFields = context.metadata[exports.CALCULATED_FIELDS_SYMBOL];
|
|
110
94
|
calculatedFields[propertyName] = options.dependsOn;
|
|
111
|
-
// Also use addInitializer to set Reflect metadata
|
|
112
|
-
if (context.addInitializer) {
|
|
113
|
-
context.addInitializer(function () {
|
|
114
|
-
const klass = this.constructor;
|
|
115
|
-
const existingDependencies = (0, common_1.getMetadata)('dynamic:dependencies', klass) || {};
|
|
116
|
-
existingDependencies[propertyName] = options.dependsOn;
|
|
117
|
-
(0, common_1.defineMetadata)('dynamic:dependencies', existingDependencies, klass);
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
return;
|
|
121
95
|
}
|
|
122
|
-
// Legacy decorator
|
|
123
|
-
const propertyKey = propertyKeyOrContext;
|
|
124
|
-
const existingDependencies = (0, common_1.getMetadata)('dynamic:dependencies', target.constructor) ||
|
|
125
|
-
{};
|
|
126
|
-
existingDependencies[propertyKey] = options.dependsOn;
|
|
127
|
-
(0, common_1.defineMetadata)('dynamic:dependencies', existingDependencies, target.constructor);
|
|
128
96
|
};
|
|
129
97
|
}
|
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { Class, RoleMetadata, RoleInterface } from '@magek/common';
|
|
2
|
+
import { ClassDecoratorContext } from './decorator-types';
|
|
2
3
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
4
|
+
* Decorator to mark a class as a Magek Role.
|
|
5
|
+
* Roles define authorization and authentication configurations.
|
|
6
|
+
*
|
|
7
|
+
* Uses TC39 Stage 3 decorators.
|
|
8
|
+
*
|
|
9
|
+
* @param roleMetadata - Role configuration including auth settings
|
|
10
|
+
* @returns A class decorator function
|
|
5
11
|
*/
|
|
6
|
-
export declare function Role(roleMetadata?: RoleMetadata): (role: Class<RoleInterface
|
|
12
|
+
export declare function Role(roleMetadata?: RoleMetadata): (role: Class<RoleInterface>, context: ClassDecoratorContext) => void;
|
package/dist/decorators/role.js
CHANGED
|
@@ -3,11 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.Role = Role;
|
|
4
4
|
const magek_1 = require("../magek");
|
|
5
5
|
/**
|
|
6
|
-
*
|
|
7
|
-
*
|
|
6
|
+
* Decorator to mark a class as a Magek Role.
|
|
7
|
+
* Roles define authorization and authentication configurations.
|
|
8
|
+
*
|
|
9
|
+
* Uses TC39 Stage 3 decorators.
|
|
10
|
+
*
|
|
11
|
+
* @param roleMetadata - Role configuration including auth settings
|
|
12
|
+
* @returns A class decorator function
|
|
8
13
|
*/
|
|
9
14
|
function Role(roleMetadata = { auth: {} }) {
|
|
10
|
-
return (role) => {
|
|
15
|
+
return (role, _context) => {
|
|
11
16
|
magek_1.Magek.configureCurrentEnv((config) => {
|
|
12
17
|
config.roles[role.name] = roleMetadata;
|
|
13
18
|
});
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { ScheduledCommandInterface, ScheduleInterface } from '@magek/common';
|
|
2
|
+
import { ClassDecoratorContext } from './decorator-types';
|
|
2
3
|
/**
|
|
3
4
|
* Decorator to mark a class as a Magek Scheduled Command.
|
|
4
5
|
* Scheduled commands are executed automatically based on a schedule.
|
|
5
6
|
*
|
|
7
|
+
* Uses TC39 Stage 3 decorators.
|
|
8
|
+
*
|
|
6
9
|
* @param attributes - Schedule configuration (e.g., cron expression)
|
|
7
10
|
* @returns A class decorator function
|
|
8
11
|
*/
|
|
9
|
-
export declare function ScheduledCommand(attributes: ScheduleInterface): (scheduledCommandClass: ScheduledCommandInterface) => void;
|
|
12
|
+
export declare function ScheduledCommand(attributes: ScheduleInterface): (scheduledCommandClass: ScheduledCommandInterface, context: ClassDecoratorContext) => void;
|
|
@@ -6,11 +6,13 @@ const magek_1 = require("../magek");
|
|
|
6
6
|
* Decorator to mark a class as a Magek Scheduled Command.
|
|
7
7
|
* Scheduled commands are executed automatically based on a schedule.
|
|
8
8
|
*
|
|
9
|
+
* Uses TC39 Stage 3 decorators.
|
|
10
|
+
*
|
|
9
11
|
* @param attributes - Schedule configuration (e.g., cron expression)
|
|
10
12
|
* @returns A class decorator function
|
|
11
13
|
*/
|
|
12
14
|
function ScheduledCommand(attributes) {
|
|
13
|
-
return (commandClass) => {
|
|
15
|
+
return (commandClass, _context) => {
|
|
14
16
|
magek_1.Magek.configureCurrentEnv((config) => {
|
|
15
17
|
if (config.scheduledCommandHandlers[commandClass.name]) {
|
|
16
18
|
throw new Error(`A command called ${commandClass.name} is already registered.
|