@rytass/wms-module-graphql 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (61) hide show
  1. package/README.md +55 -0
  2. package/adapters/event-emitter.d.ts +29 -0
  3. package/adapters/event-emitter.js +41 -0
  4. package/adapters/member-base.d.ts +35 -0
  5. package/adapters/member-base.js +47 -0
  6. package/docs/README.md +6 -0
  7. package/docs/api-reference.md +100 -1
  8. package/docs/authorization.md +349 -0
  9. package/docs/events.md +304 -0
  10. package/docs/integration-guide.md +121 -4
  11. package/index.cjs.js +299 -3
  12. package/index.d.ts +4 -0
  13. package/index.js +7 -0
  14. package/lib/auth/host-skip.constant.d.ts +27 -0
  15. package/lib/auth/host-skip.constant.js +29 -0
  16. package/lib/auth/index.d.ts +4 -0
  17. package/lib/auth/wms-auth.guard.d.ts +19 -0
  18. package/lib/auth/wms-auth.guard.js +41 -0
  19. package/lib/auth/wms-auth.types.d.ts +36 -0
  20. package/lib/auth/wms-auth.types.js +6 -0
  21. package/lib/auth/wms-operation.decorator.d.ts +23 -0
  22. package/lib/auth/wms-operation.decorator.js +29 -0
  23. package/lib/events/index.d.ts +3 -0
  24. package/lib/events/wms-mutation-event.constants.d.ts +12 -0
  25. package/lib/events/wms-mutation-event.constants.js +4 -0
  26. package/lib/events/wms-mutation-event.decorator.d.ts +15 -0
  27. package/lib/events/wms-mutation-event.decorator.js +20 -0
  28. package/lib/events/wms-mutation.interceptor.d.ts +19 -0
  29. package/lib/events/wms-mutation.interceptor.js +72 -0
  30. package/lib/graphql.module.d.ts +20 -3
  31. package/lib/graphql.module.js +19 -4
  32. package/lib/mutations/allocate-inventory-order.mutation.js +10 -0
  33. package/lib/mutations/hold-inventory-order.mutation.js +10 -0
  34. package/lib/mutations/loader.mutation.js +14 -0
  35. package/lib/mutations/location.mutation.js +8 -0
  36. package/lib/mutations/material.mutation.js +8 -0
  37. package/lib/mutations/merge-batch.mutation.js +6 -0
  38. package/lib/mutations/quality-inspection-order.mutation.js +6 -0
  39. package/lib/mutations/receive-inventory-order.mutation.js +10 -0
  40. package/lib/mutations/reclassify-inventory-order.mutation.js +6 -0
  41. package/lib/mutations/scrape-order.mutation.js +6 -0
  42. package/lib/mutations/ship-inventory-order.mutation.js +12 -0
  43. package/lib/mutations/split-batch.mutation.js +6 -0
  44. package/lib/mutations/transfer-customer-order.mutation.js +10 -0
  45. package/lib/mutations/transfer-order.mutation.js +12 -0
  46. package/lib/mutations/transfer-vendor-order.mutation.js +10 -0
  47. package/lib/mutations/warehouse-map.mutation.js +6 -0
  48. package/lib/queries/allocate-inventory-order.query.js +3 -0
  49. package/lib/queries/batch.query.js +3 -0
  50. package/lib/queries/hold-inventory-order.query.js +3 -0
  51. package/lib/queries/loader.query.js +8 -0
  52. package/lib/queries/location.query.js +4 -0
  53. package/lib/queries/material.query.js +4 -0
  54. package/lib/queries/recieve-inventory-order.query.js +4 -0
  55. package/lib/queries/sale-order.query.js +4 -0
  56. package/lib/queries/ship-inventory-order.query.js +4 -0
  57. package/lib/queries/stock.query.js +7 -0
  58. package/lib/queries/transfer-order.query.js +4 -0
  59. package/lib/queries/unit.query.js +3 -0
  60. package/lib/queries/warehouse-map.query.js +3 -0
  61. package/package.json +17 -3
package/index.d.ts CHANGED
@@ -3,3 +3,7 @@ export { LoaderService } from './lib/helpers/loader.service';
3
3
  export type { LoaderOptions, LoaderOptionsWithPagination, } from './lib/helpers/loader.service';
4
4
  export type { LoaderType } from './lib/helpers/dataloader.type';
5
5
  export { createWmsLoaders, type WmsLoaders, } from './lib/helpers/create-wms-loaders';
6
+ export { HOST_SKIP_METADATA_KEY, WmsAuthGuard, WmsOperation, WMS_AUTH_OPTIONS, WMS_OPERATION_METADATA, type WmsAuthChecker, type WmsAuthModuleOptions, type WmsOperationRequirement, } from './lib/auth';
7
+ export type { WmsModuleGraphqlModuleAsyncOptions } from './lib/graphql.module';
8
+ export { WmsMutationEvent, WmsMutationInterceptor, WMS_MUTATION_EVENT_METADATA, type WmsMutationEventConfig, } from './lib/events';
9
+ export type { WmsAfterEventContext, WmsAfterListener, WmsAfterListenerMap, WmsBeforeHook, WmsBeforeHookContext, WmsBeforeHookMap, WmsDomainEvent, WmsEventModuleOptions, WmsEventName, WmsEventPayloadMap, WmsOperationEventName, WmsOperationEventPayload, WmsStockEventName, WmsStockEventPayload, } from '@rytass/wms-module-core';
package/index.js CHANGED
@@ -1,3 +1,10 @@
1
1
  export { WmsModuleGraphqlModule } from './lib/graphql.module.js';
2
2
  export { LoaderService } from './lib/helpers/loader.service.js';
3
3
  export { createWmsLoaders } from './lib/helpers/create-wms-loaders.js';
4
+ export { HOST_SKIP_METADATA_KEY } from './lib/auth/host-skip.constant.js';
5
+ export { WmsAuthGuard } from './lib/auth/wms-auth.guard.js';
6
+ export { WmsOperation } from './lib/auth/wms-operation.decorator.js';
7
+ export { WMS_AUTH_OPTIONS, WMS_OPERATION_METADATA } from './lib/auth/wms-auth.types.js';
8
+ export { WmsMutationEvent } from './lib/events/wms-mutation-event.decorator.js';
9
+ export { WMS_MUTATION_EVENT_METADATA } from './lib/events/wms-mutation-event.constants.js';
10
+ export { WmsMutationInterceptor } from './lib/events/wms-mutation.interceptor.js';
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Metadata key stamped on every `@WmsOperation` handler so that a host
3
+ * application's *globally registered* auth guard (`APP_GUARD`) treats WMS
4
+ * resolvers as "skip / public" and defers the real authorization decision to
5
+ * {@link WmsAuthGuard}.
6
+ *
7
+ * Why this exists
8
+ * ---------------
9
+ * A deny-by-default global guard — e.g. the `CasbinGuard` from
10
+ * `@rytass/member-base-nestjs-module` — blankets every resolver in the compiled
11
+ * schema, including the ones this library owns. WMS resolvers carry no
12
+ * host-specific permission metadata, so such a guard rejects them all. By
13
+ * stamping this key WMS tells the host guard "let me through", and then runs
14
+ * its own {@link WmsAuthGuard} (wired to the consumer's `authChecker`) to do the
15
+ * actual enforcement.
16
+ *
17
+ * The single coupling point
18
+ * -------------------------
19
+ * The default value matches `@rytass/member-base-nestjs-module`'s
20
+ * `IS_ROUTE_PUBLIC` metadata key. This is the *one and only* place WMS assumes
21
+ * anything about a specific host guard. If your host guard uses a different
22
+ * "public / skip" metadata key, fork this constant — nothing else changes.
23
+ *
24
+ * If you do not register a global guard at all (or set
25
+ * `enableGlobalGuard: false`), this marker is simply inert.
26
+ */
27
+ export declare const HOST_SKIP_METADATA_KEY = "IS_ROUTE_PUBLIC";
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Metadata key stamped on every `@WmsOperation` handler so that a host
3
+ * application's *globally registered* auth guard (`APP_GUARD`) treats WMS
4
+ * resolvers as "skip / public" and defers the real authorization decision to
5
+ * {@link WmsAuthGuard}.
6
+ *
7
+ * Why this exists
8
+ * ---------------
9
+ * A deny-by-default global guard — e.g. the `CasbinGuard` from
10
+ * `@rytass/member-base-nestjs-module` — blankets every resolver in the compiled
11
+ * schema, including the ones this library owns. WMS resolvers carry no
12
+ * host-specific permission metadata, so such a guard rejects them all. By
13
+ * stamping this key WMS tells the host guard "let me through", and then runs
14
+ * its own {@link WmsAuthGuard} (wired to the consumer's `authChecker`) to do the
15
+ * actual enforcement.
16
+ *
17
+ * The single coupling point
18
+ * -------------------------
19
+ * The default value matches `@rytass/member-base-nestjs-module`'s
20
+ * `IS_ROUTE_PUBLIC` metadata key. This is the *one and only* place WMS assumes
21
+ * anything about a specific host guard. If your host guard uses a different
22
+ * "public / skip" metadata key, fork this constant — nothing else changes.
23
+ *
24
+ * If you do not register a global guard at all (or set
25
+ * `enableGlobalGuard: false`), this marker is simply inert.
26
+ */
27
+ const HOST_SKIP_METADATA_KEY = 'IS_ROUTE_PUBLIC';
28
+
29
+ export { HOST_SKIP_METADATA_KEY };
@@ -0,0 +1,4 @@
1
+ export { HOST_SKIP_METADATA_KEY } from './host-skip.constant';
2
+ export { WmsAuthGuard } from './wms-auth.guard';
3
+ export { WmsOperation } from './wms-operation.decorator';
4
+ export { WMS_AUTH_OPTIONS, WMS_OPERATION_METADATA, type WmsAuthChecker, type WmsAuthModuleOptions, type WmsOperationRequirement, } from './wms-auth.types';
@@ -0,0 +1,19 @@
1
+ import { type CanActivate, type ExecutionContext } from '@nestjs/common';
2
+ import { Reflector } from '@nestjs/core';
3
+ import { type WmsAuthModuleOptions } from './wms-auth.types';
4
+ /**
5
+ * Route-level guard applied automatically by `@WmsOperation`. It reads the
6
+ * requirement declared on the handler and delegates the allow/deny decision to
7
+ * the consumer-supplied {@link WmsAuthModuleOptions.authChecker}.
8
+ *
9
+ * It is deliberately permissive in two cases:
10
+ * - the handler carries no requirement (field resolvers reached only through an
11
+ * already-authorized root operation), and
12
+ * - no `authChecker` was configured (local dev / auth handled elsewhere).
13
+ */
14
+ export declare class WmsAuthGuard implements CanActivate {
15
+ private readonly reflector;
16
+ private readonly options;
17
+ constructor(reflector: Reflector, options: WmsAuthModuleOptions);
18
+ canActivate(context: ExecutionContext): boolean | Promise<boolean>;
19
+ }
@@ -0,0 +1,41 @@
1
+ import { __decorate, __param, __metadata } from 'tslib';
2
+ import { Injectable, Inject } from '@nestjs/common';
3
+ import { Reflector } from '@nestjs/core';
4
+ import { WMS_AUTH_OPTIONS, WMS_OPERATION_METADATA } from './wms-auth.types.js';
5
+
6
+ /**
7
+ * Route-level guard applied automatically by `@WmsOperation`. It reads the
8
+ * requirement declared on the handler and delegates the allow/deny decision to
9
+ * the consumer-supplied {@link WmsAuthModuleOptions.authChecker}.
10
+ *
11
+ * It is deliberately permissive in two cases:
12
+ * - the handler carries no requirement (field resolvers reached only through an
13
+ * already-authorized root operation), and
14
+ * - no `authChecker` was configured (local dev / auth handled elsewhere).
15
+ */
16
+ let WmsAuthGuard = class WmsAuthGuard {
17
+ reflector;
18
+ options;
19
+ constructor(reflector, options) {
20
+ this.reflector = reflector;
21
+ this.options = options;
22
+ }
23
+ canActivate(context) {
24
+ const requirement = this.reflector.get(WMS_OPERATION_METADATA, context.getHandler());
25
+ if (!requirement) {
26
+ return true;
27
+ }
28
+ const { authChecker } = this.options;
29
+ if (!authChecker) {
30
+ return true;
31
+ }
32
+ return authChecker(context, requirement);
33
+ }
34
+ };
35
+ WmsAuthGuard = __decorate([
36
+ Injectable(),
37
+ __param(1, Inject(WMS_AUTH_OPTIONS)),
38
+ __metadata("design:paramtypes", [Reflector, Object])
39
+ ], WmsAuthGuard);
40
+
41
+ export { WmsAuthGuard };
@@ -0,0 +1,36 @@
1
+ import type { ExecutionContext } from '@nestjs/common';
2
+ /**
3
+ * A single, host-neutral permission requirement attached to a WMS root
4
+ * operation. `resource` and `action` are plain strings on purpose — the WMS
5
+ * library never knows (nor cares) how a consumer maps them onto Casbin, RBAC
6
+ * tiers, JWT scopes or anything else. The mapping lives entirely in the
7
+ * consumer-supplied {@link WmsAuthChecker}.
8
+ */
9
+ export interface WmsOperationRequirement {
10
+ readonly resource: string;
11
+ readonly action: string;
12
+ }
13
+ /**
14
+ * The authorization contract a consumer wires into
15
+ * `WmsModuleGraphqlModule.forRootAsync({ authChecker })`. It receives the live
16
+ * NestJS {@link ExecutionContext} (so the consumer can reach the request,
17
+ * GraphQL context, current member, enforcer, etc.) plus the requirement
18
+ * declared by `@WmsOperation` on the resolver being invoked.
19
+ *
20
+ * Return `true` to allow, `false` to deny. Throwing is also fine — the
21
+ * exception propagates exactly like any guard rejection.
22
+ */
23
+ export type WmsAuthChecker = (context: ExecutionContext, requirement: WmsOperationRequirement) => boolean | Promise<boolean>;
24
+ /**
25
+ * Options accepted by `WmsModuleGraphqlModule.forRootAsync` that govern
26
+ * authorization. Intentionally optional: when `authChecker` is omitted the
27
+ * module stays fully permissive, preserving the pre-auth release behaviour for
28
+ * local development and consumers that handle authorization elsewhere.
29
+ */
30
+ export interface WmsAuthModuleOptions {
31
+ readonly authChecker?: WmsAuthChecker;
32
+ }
33
+ /** DI token holding the resolved {@link WmsAuthModuleOptions}. */
34
+ export declare const WMS_AUTH_OPTIONS: unique symbol;
35
+ /** Reflector metadata key carrying the {@link WmsOperationRequirement}. */
36
+ export declare const WMS_OPERATION_METADATA = "wms:operation";
@@ -0,0 +1,6 @@
1
+ /** DI token holding the resolved {@link WmsAuthModuleOptions}. */
2
+ const WMS_AUTH_OPTIONS = Symbol('WMS_AUTH_OPTIONS');
3
+ /** Reflector metadata key carrying the {@link WmsOperationRequirement}. */
4
+ const WMS_OPERATION_METADATA = 'wms:operation';
5
+
6
+ export { WMS_AUTH_OPTIONS, WMS_OPERATION_METADATA };
@@ -0,0 +1,23 @@
1
+ import { type WmsOperationRequirement } from './wms-auth.types';
2
+ /**
3
+ * Declares the permission requirement for a WMS root operation and wires up its
4
+ * enforcement. Apply it to every `@Query` / `@Mutation` the library exposes.
5
+ *
6
+ * It bundles three concerns so a single annotation keeps a resolver
7
+ * self-protecting and host-friendly:
8
+ *
9
+ * 1. {@link WMS_OPERATION_METADATA} — the host-neutral `{ resource, action }`
10
+ * that {@link WmsAuthGuard} hands to the consumer's `authChecker`.
11
+ * 2. {@link HOST_SKIP_METADATA_KEY} — tells a host's global guard to defer (see
12
+ * that constant for the single coupling point with member-base).
13
+ * 3. `@UseGuards(WmsAuthGuard)` — the route-level guard that performs the real
14
+ * authorization through the configured `authChecker`.
15
+ *
16
+ * @example
17
+ * ```ts
18
+ * @Query(() => MaterialDto)
19
+ * @WmsOperation({ resource: 'material', action: 'read' })
20
+ * material(@Args('id') id: string): Promise<MaterialDto> { ... }
21
+ * ```
22
+ */
23
+ export declare const WmsOperation: (requirement: WmsOperationRequirement) => MethodDecorator;
@@ -0,0 +1,29 @@
1
+ import { applyDecorators, SetMetadata, UseGuards } from '@nestjs/common';
2
+ import { HOST_SKIP_METADATA_KEY } from './host-skip.constant.js';
3
+ import { WmsAuthGuard } from './wms-auth.guard.js';
4
+ import { WMS_OPERATION_METADATA } from './wms-auth.types.js';
5
+
6
+ /**
7
+ * Declares the permission requirement for a WMS root operation and wires up its
8
+ * enforcement. Apply it to every `@Query` / `@Mutation` the library exposes.
9
+ *
10
+ * It bundles three concerns so a single annotation keeps a resolver
11
+ * self-protecting and host-friendly:
12
+ *
13
+ * 1. {@link WMS_OPERATION_METADATA} — the host-neutral `{ resource, action }`
14
+ * that {@link WmsAuthGuard} hands to the consumer's `authChecker`.
15
+ * 2. {@link HOST_SKIP_METADATA_KEY} — tells a host's global guard to defer (see
16
+ * that constant for the single coupling point with member-base).
17
+ * 3. `@UseGuards(WmsAuthGuard)` — the route-level guard that performs the real
18
+ * authorization through the configured `authChecker`.
19
+ *
20
+ * @example
21
+ * ```ts
22
+ * @Query(() => MaterialDto)
23
+ * @WmsOperation({ resource: 'material', action: 'read' })
24
+ * material(@Args('id') id: string): Promise<MaterialDto> { ... }
25
+ * ```
26
+ */
27
+ const WmsOperation = (requirement) => applyDecorators(SetMetadata(WMS_OPERATION_METADATA, requirement), SetMetadata(HOST_SKIP_METADATA_KEY, true), UseGuards(WmsAuthGuard));
28
+
29
+ export { WmsOperation };
@@ -0,0 +1,3 @@
1
+ export { WmsMutationEvent } from './wms-mutation-event.decorator';
2
+ export { WMS_MUTATION_EVENT_METADATA, type WmsMutationEventConfig, } from './wms-mutation-event.constants';
3
+ export { WmsMutationInterceptor } from './wms-mutation.interceptor';
@@ -0,0 +1,12 @@
1
+ import type { WmsOperationEventName } from '@rytass/wms-module-core';
2
+ /** Reflector metadata key carrying the {@link WmsMutationEventConfig}. */
3
+ export declare const WMS_MUTATION_EVENT_METADATA = "wms:mutation-event";
4
+ export interface WmsMutationEventConfig {
5
+ /** The operation event emitted for this mutation (post-commit). */
6
+ readonly event: WmsOperationEventName;
7
+ /**
8
+ * Optional override for deriving the operation's primary id from the handler
9
+ * result and/or arguments. Defaults to `result.id ?? args.id ?? null`.
10
+ */
11
+ readonly resolveId?: (result: unknown, args: Readonly<Record<string, unknown>>) => string | null;
12
+ }
@@ -0,0 +1,4 @@
1
+ /** Reflector metadata key carrying the {@link WmsMutationEventConfig}. */
2
+ const WMS_MUTATION_EVENT_METADATA = 'wms:mutation-event';
3
+
4
+ export { WMS_MUTATION_EVENT_METADATA };
@@ -0,0 +1,15 @@
1
+ import { type WmsMutationEventConfig } from './wms-mutation-event.constants';
2
+ /**
3
+ * Declares the operation event for a WMS root mutation and wires the
4
+ * {@link WmsMutationInterceptor} that runs before-hooks (veto) and flushes
5
+ * collected events post-commit. Apply alongside `@WmsOperation` on each mutation.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * @Mutation(() => ShipInventoryOrderDto)
10
+ * @WmsOperation({ resource: 'ship-inventory-order', action: 'ship' })
11
+ * @WmsMutationEvent({ event: 'ship-inventory-order.shipped' })
12
+ * ship(@Args() args: ShipArgs): Promise<boolean> { ... }
13
+ * ```
14
+ */
15
+ export declare const WmsMutationEvent: (config: WmsMutationEventConfig) => MethodDecorator;
@@ -0,0 +1,20 @@
1
+ import { applyDecorators, SetMetadata, UseInterceptors } from '@nestjs/common';
2
+ import { WMS_MUTATION_EVENT_METADATA } from './wms-mutation-event.constants.js';
3
+ import { WmsMutationInterceptor } from './wms-mutation.interceptor.js';
4
+
5
+ /**
6
+ * Declares the operation event for a WMS root mutation and wires the
7
+ * {@link WmsMutationInterceptor} that runs before-hooks (veto) and flushes
8
+ * collected events post-commit. Apply alongside `@WmsOperation` on each mutation.
9
+ *
10
+ * @example
11
+ * ```ts
12
+ * @Mutation(() => ShipInventoryOrderDto)
13
+ * @WmsOperation({ resource: 'ship-inventory-order', action: 'ship' })
14
+ * @WmsMutationEvent({ event: 'ship-inventory-order.shipped' })
15
+ * ship(@Args() args: ShipArgs): Promise<boolean> { ... }
16
+ * ```
17
+ */
18
+ const WmsMutationEvent = (config) => applyDecorators(SetMetadata(WMS_MUTATION_EVENT_METADATA, config), UseInterceptors(WmsMutationInterceptor));
19
+
20
+ export { WmsMutationEvent };
@@ -0,0 +1,19 @@
1
+ import { type CallHandler, type ExecutionContext, type NestInterceptor } from '@nestjs/common';
2
+ import { Reflector } from '@nestjs/core';
3
+ import { WmsEventDispatcher } from '@rytass/wms-module-core';
4
+ import { type Observable } from 'rxjs';
5
+ /**
6
+ * Wraps each `@WmsMutationEvent` mutation in a dispatch scope:
7
+ * 1. runs the before-hook (throw -> veto, before any transaction);
8
+ * 2. executes the resolver (services collect stock events into the scope);
9
+ * 3. emits the operation event after the handler resolves (post-commit);
10
+ * 4. flushes the whole buffer to after-listeners + emitter bridge.
11
+ *
12
+ * When no hook/listener/bridge is configured it passes through untouched.
13
+ */
14
+ export declare class WmsMutationInterceptor implements NestInterceptor {
15
+ private readonly reflector;
16
+ private readonly dispatcher;
17
+ constructor(reflector: Reflector, dispatcher: WmsEventDispatcher);
18
+ intercept(context: ExecutionContext, next: CallHandler): Promise<Observable<unknown>>;
19
+ }
@@ -0,0 +1,72 @@
1
+ import { __decorate, __metadata } from 'tslib';
2
+ import { Injectable } from '@nestjs/common';
3
+ import { Reflector } from '@nestjs/core';
4
+ import { GqlExecutionContext } from '@nestjs/graphql';
5
+ import { WmsEventDispatcher, runInDispatchScope, collectToScope } from '@rytass/wms-module-core';
6
+ import { firstValueFrom, of } from 'rxjs';
7
+ import { WMS_MUTATION_EVENT_METADATA } from './wms-mutation-event.constants.js';
8
+
9
+ const defaultResolveId = (result, args) => {
10
+ if (result &&
11
+ typeof result === 'object' &&
12
+ 'id' in result &&
13
+ typeof result.id === 'string') {
14
+ return result.id;
15
+ }
16
+ const argId = args['id'];
17
+ return typeof argId === 'string' ? argId : null;
18
+ };
19
+ /**
20
+ * Wraps each `@WmsMutationEvent` mutation in a dispatch scope:
21
+ * 1. runs the before-hook (throw -> veto, before any transaction);
22
+ * 2. executes the resolver (services collect stock events into the scope);
23
+ * 3. emits the operation event after the handler resolves (post-commit);
24
+ * 4. flushes the whole buffer to after-listeners + emitter bridge.
25
+ *
26
+ * When no hook/listener/bridge is configured it passes through untouched.
27
+ */
28
+ let WmsMutationInterceptor = class WmsMutationInterceptor {
29
+ reflector;
30
+ dispatcher;
31
+ constructor(reflector, dispatcher) {
32
+ this.reflector = reflector;
33
+ this.dispatcher = dispatcher;
34
+ }
35
+ async intercept(context, next) {
36
+ const config = this.reflector.get(WMS_MUTATION_EVENT_METADATA, context.getHandler());
37
+ if (!config || !this.dispatcher.isActive()) {
38
+ return next.handle();
39
+ }
40
+ const args = GqlExecutionContext.create(context).getArgs() ??
41
+ {};
42
+ // Before-hook runs before any DB work; a throw vetoes with zero side effects.
43
+ await this.dispatcher.runBeforeHook({
44
+ event: config.event,
45
+ args,
46
+ executionContext: context,
47
+ });
48
+ const { result, events } = await runInDispatchScope(async () => {
49
+ const handlerResult = await firstValueFrom(next.handle());
50
+ const id = config.resolveId
51
+ ? config.resolveId(handlerResult, args)
52
+ : defaultResolveId(handlerResult, args);
53
+ const operationEvent = {
54
+ name: config.event,
55
+ payload: { id, result: handlerResult },
56
+ };
57
+ collectToScope(operationEvent);
58
+ return handlerResult;
59
+ });
60
+ if (events) {
61
+ await this.dispatcher.flush(events);
62
+ }
63
+ return of(result);
64
+ }
65
+ };
66
+ WmsMutationInterceptor = __decorate([
67
+ Injectable(),
68
+ __metadata("design:paramtypes", [Reflector,
69
+ WmsEventDispatcher])
70
+ ], WmsMutationInterceptor);
71
+
72
+ export { WmsMutationInterceptor };
@@ -1,5 +1,22 @@
1
- import { DynamicModule } from '@nestjs/common';
2
- import type { WmsModuleCoreModuleAsyncOptions } from '@rytass/wms-module-core';
1
+ import { DynamicModule, type Provider } from '@nestjs/common';
2
+ import { type WmsModuleCoreModuleAsyncOptions, type WmsEventModuleOptions } from '@rytass/wms-module-core';
3
+ import { type WmsAuthModuleOptions } from './auth';
4
+ /**
5
+ * Options for `WmsModuleGraphqlModule.forRootAsync`. Extends the core module
6
+ * options with:
7
+ * - {@link WmsAuthModuleOptions.authChecker} — opt-in resolver authorization;
8
+ * - {@link WmsEventModuleOptions} — `beforeHooks` (veto) / `afterListeners`
9
+ * (post-commit notify) for the mutation event/hook system;
10
+ * - `eventEmitterBridge` — pass `wmsEventEmitterBridgeProvider` from
11
+ * `@rytass/wms-module-graphql/adapters/event-emitter` to also fan events out
12
+ * to `@nestjs/event-emitter` (`@OnEvent('wms.<event>')`).
13
+ *
14
+ * Everything is optional; with nothing configured the module behaves exactly as
15
+ * before (no events emitted, fully permissive).
16
+ */
17
+ export type WmsModuleGraphqlModuleAsyncOptions = WmsModuleCoreModuleAsyncOptions & WmsAuthModuleOptions & WmsEventModuleOptions & {
18
+ readonly eventEmitterBridge?: Provider;
19
+ };
3
20
  export declare class WmsModuleGraphqlModule {
4
- static forRootAsync(options: WmsModuleCoreModuleAsyncOptions): DynamicModule;
21
+ static forRootAsync(options: WmsModuleGraphqlModuleAsyncOptions): DynamicModule;
5
22
  }
@@ -1,9 +1,12 @@
1
1
  import { __decorate } from 'tslib';
2
2
  import { Module } from '@nestjs/common';
3
- import { WmsModuleCoreModule } from '@rytass/wms-module-core';
3
+ import { WmsModuleCoreModule, WMS_EVENT_OPTIONS, WmsEventDispatcher } from '@rytass/wms-module-core';
4
4
  import './helpers/loader.service.js';
5
5
  import { HelperModule } from './helpers/helper.module.js';
6
6
  import './helpers/query-path.js';
7
+ import { WmsAuthGuard } from './auth/wms-auth.guard.js';
8
+ import { WMS_AUTH_OPTIONS } from './auth/wms-auth.types.js';
9
+ import { WmsMutationInterceptor } from './events/wms-mutation.interceptor.js';
7
10
  import { LoaderMutation } from './mutations/loader.mutation.js';
8
11
  import { LoaderQuery } from './queries/loader.query.js';
9
12
  import { MaterialMutation } from './mutations/material.mutation.js';
@@ -108,12 +111,24 @@ const exportDependencies = [
108
111
  ];
109
112
  let WmsModuleGraphqlModule = WmsModuleGraphqlModule_1 = class WmsModuleGraphqlModule {
110
113
  static forRootAsync(options) {
111
- const coreModule = WmsModuleCoreModule.forRootAsync(options);
114
+ const { authChecker, beforeHooks, afterListeners, eventEmitterBridge, ...coreOptions } = options;
115
+ const coreModule = WmsModuleCoreModule.forRootAsync(coreOptions);
116
+ const authOptions = { authChecker };
117
+ const eventOptions = { beforeHooks, afterListeners };
118
+ const providers = [
119
+ ...exportDependencies,
120
+ WmsAuthGuard,
121
+ { provide: WMS_AUTH_OPTIONS, useValue: authOptions },
122
+ { provide: WMS_EVENT_OPTIONS, useValue: eventOptions },
123
+ WmsEventDispatcher,
124
+ WmsMutationInterceptor,
125
+ ...(eventEmitterBridge ? [eventEmitterBridge] : []),
126
+ ];
112
127
  return {
113
128
  module: WmsModuleGraphqlModule_1,
114
129
  imports: [coreModule, HelperModule],
115
- providers: exportDependencies,
116
- exports: [...exportDependencies, HelperModule, coreModule],
130
+ providers,
131
+ exports: [...exportDependencies, WmsAuthGuard, HelperModule, coreModule],
117
132
  };
118
133
  }
119
134
  };
@@ -5,6 +5,10 @@ import { AllocateInventoryOrderService, StockService } from '@rytass/wms-module-
5
5
  import { AllocateInventoryOrderDto } from '../dto/allocate-inventory-order.dto.js';
6
6
  import { CreateAllocateInventoryOrderArgs, UpdateAllocateInventoryOrderArgs } from '../inputs/create-allocate-inventory-order.args.js';
7
7
  import { DeallocateOrderArgs } from '../inputs/deallocate-order.args.js';
8
+ import '../auth/wms-auth.guard.js';
9
+ import { WmsOperation } from '../auth/wms-operation.decorator.js';
10
+ import { WmsMutationEvent } from '../events/wms-mutation-event.decorator.js';
11
+ import '../events/wms-mutation.interceptor.js';
8
12
 
9
13
  let AllocateInventoryOrderMutation = class AllocateInventoryOrderMutation {
10
14
  allocateInventoryOrderService;
@@ -53,6 +57,8 @@ let AllocateInventoryOrderMutation = class AllocateInventoryOrderMutation {
53
57
  };
54
58
  __decorate([
55
59
  Mutation(() => AllocateInventoryOrderDto),
60
+ WmsOperation({ resource: 'allocate-inventory-order', action: 'create' }),
61
+ WmsMutationEvent({ event: 'allocate-inventory-order.created' }),
56
62
  __param(0, Args()),
57
63
  __metadata("design:type", Function),
58
64
  __metadata("design:paramtypes", [CreateAllocateInventoryOrderArgs]),
@@ -60,6 +66,8 @@ __decorate([
60
66
  ], AllocateInventoryOrderMutation.prototype, "createAllocateInventoryOrder", null);
61
67
  __decorate([
62
68
  Mutation(() => AllocateInventoryOrderDto),
69
+ WmsOperation({ resource: 'allocate-inventory-order', action: 'update' }),
70
+ WmsMutationEvent({ event: 'allocate-inventory-order.updated' }),
63
71
  __param(0, Args()),
64
72
  __metadata("design:type", Function),
65
73
  __metadata("design:paramtypes", [UpdateAllocateInventoryOrderArgs]),
@@ -67,6 +75,8 @@ __decorate([
67
75
  ], AllocateInventoryOrderMutation.prototype, "updateAllocateInventoryOrder", null);
68
76
  __decorate([
69
77
  Mutation(() => Boolean),
78
+ WmsOperation({ resource: 'allocate-inventory-order', action: 'deallocate' }),
79
+ WmsMutationEvent({ event: 'allocate-inventory-order.deallocated' }),
70
80
  __param(0, Args('items', { type: () => [DeallocateOrderArgs] })),
71
81
  __metadata("design:type", Function),
72
82
  __metadata("design:paramtypes", [Array]),
@@ -5,6 +5,10 @@ import { HoldInventoryOrderService, StockService } from '@rytass/wms-module-core
5
5
  import { HoldInventoryOrderDto } from '../dto/hold-inventory-order.dto.js';
6
6
  import { CreateHoldInventoryOrderArgs, UpdateHoldInventoryOrderArgs } from '../inputs/create-hold-inventory-order.args.js';
7
7
  import { ReleaseOrderArgs } from '../inputs/release-order.args.js';
8
+ import '../auth/wms-auth.guard.js';
9
+ import { WmsOperation } from '../auth/wms-operation.decorator.js';
10
+ import { WmsMutationEvent } from '../events/wms-mutation-event.decorator.js';
11
+ import '../events/wms-mutation.interceptor.js';
8
12
 
9
13
  let HoldInventoryOrderMutation = class HoldInventoryOrderMutation {
10
14
  holdInventoryOrderService;
@@ -53,6 +57,8 @@ let HoldInventoryOrderMutation = class HoldInventoryOrderMutation {
53
57
  };
54
58
  __decorate([
55
59
  Mutation(() => HoldInventoryOrderDto),
60
+ WmsOperation({ resource: 'hold-inventory-order', action: 'create' }),
61
+ WmsMutationEvent({ event: 'hold-inventory-order.created' }),
56
62
  __param(0, Args()),
57
63
  __metadata("design:type", Function),
58
64
  __metadata("design:paramtypes", [CreateHoldInventoryOrderArgs]),
@@ -60,6 +66,8 @@ __decorate([
60
66
  ], HoldInventoryOrderMutation.prototype, "createHoldInventoryOrder", null);
61
67
  __decorate([
62
68
  Mutation(() => HoldInventoryOrderDto),
69
+ WmsOperation({ resource: 'hold-inventory-order', action: 'update' }),
70
+ WmsMutationEvent({ event: 'hold-inventory-order.updated' }),
63
71
  __param(0, Args()),
64
72
  __metadata("design:type", Function),
65
73
  __metadata("design:paramtypes", [UpdateHoldInventoryOrderArgs]),
@@ -67,6 +75,8 @@ __decorate([
67
75
  ], HoldInventoryOrderMutation.prototype, "updateHoldInventoryOrder", null);
68
76
  __decorate([
69
77
  Mutation(() => Boolean),
78
+ WmsOperation({ resource: 'hold-inventory-order', action: 'release' }),
79
+ WmsMutationEvent({ event: 'hold-inventory-order.released' }),
70
80
  __param(0, Args('items', { type: () => [ReleaseOrderArgs] })),
71
81
  __metadata("design:type", Function),
72
82
  __metadata("design:paramtypes", [Array]),
@@ -29,6 +29,10 @@ import { Mutation, Args } from '@nestjs/graphql';
29
29
  import { CreateLoaderTypeArgs, UpdateLoaderTypeArgs } from '../inputs/create-loader-type.args.js';
30
30
  import { CreateLoaderArgs, CreateLoadersArgs, UpdateLoaderArgs } from '../inputs/create-loader.args.js';
31
31
  import { In } from 'typeorm';
32
+ import '../auth/wms-auth.guard.js';
33
+ import { WmsOperation } from '../auth/wms-operation.decorator.js';
34
+ import { WmsMutationEvent } from '../events/wms-mutation-event.decorator.js';
35
+ import '../events/wms-mutation.interceptor.js';
32
36
 
33
37
  let LoaderMutation = class LoaderMutation {
34
38
  loaderService;
@@ -64,6 +68,8 @@ let LoaderMutation = class LoaderMutation {
64
68
  };
65
69
  __decorate([
66
70
  Mutation(() => [LoaderDto]),
71
+ WmsOperation({ resource: 'loader', action: 'create' }),
72
+ WmsMutationEvent({ event: 'loader.created' }),
67
73
  __param(0, Args()),
68
74
  __metadata("design:type", Function),
69
75
  __metadata("design:paramtypes", [CreateLoaderArgs]),
@@ -71,6 +77,8 @@ __decorate([
71
77
  ], LoaderMutation.prototype, "createLoader", null);
72
78
  __decorate([
73
79
  Mutation(() => [LoaderDto]),
80
+ WmsOperation({ resource: 'loader', action: 'create' }),
81
+ WmsMutationEvent({ event: 'loader.batch-created' }),
74
82
  __param(0, Args('items', { type: () => [CreateLoadersArgs] })),
75
83
  __metadata("design:type", Function),
76
84
  __metadata("design:paramtypes", [Array]),
@@ -78,6 +86,8 @@ __decorate([
78
86
  ], LoaderMutation.prototype, "createBatchLoader", null);
79
87
  __decorate([
80
88
  Mutation(() => LoaderDto),
89
+ WmsOperation({ resource: 'loader', action: 'update' }),
90
+ WmsMutationEvent({ event: 'loader.updated' }),
81
91
  __param(0, Args()),
82
92
  __metadata("design:type", Function),
83
93
  __metadata("design:paramtypes", [UpdateLoaderArgs]),
@@ -85,6 +95,8 @@ __decorate([
85
95
  ], LoaderMutation.prototype, "updateLoader", null);
86
96
  __decorate([
87
97
  Mutation(() => LoaderTypeDto),
98
+ WmsOperation({ resource: 'loader', action: 'create' }),
99
+ WmsMutationEvent({ event: 'loader.type-created' }),
88
100
  __param(0, Args()),
89
101
  __metadata("design:type", Function),
90
102
  __metadata("design:paramtypes", [CreateLoaderTypeArgs]),
@@ -92,6 +104,8 @@ __decorate([
92
104
  ], LoaderMutation.prototype, "createLoaderType", null);
93
105
  __decorate([
94
106
  Mutation(() => LoaderTypeDto),
107
+ WmsOperation({ resource: 'loader', action: 'update' }),
108
+ WmsMutationEvent({ event: 'loader.type-updated' }),
95
109
  __param(0, Args()),
96
110
  __metadata("design:type", Function),
97
111
  __metadata("design:paramtypes", [UpdateLoaderTypeArgs]),
@@ -4,6 +4,10 @@ import { Mutation, Args } from '@nestjs/graphql';
4
4
  import { LocationService } from '@rytass/wms-module-core';
5
5
  import { LocationDto } from '../dto/location.dto.js';
6
6
  import { CreateLocationArgs, UpdateLocationArgs } from '../inputs/create-location.args.js';
7
+ import '../auth/wms-auth.guard.js';
8
+ import { WmsOperation } from '../auth/wms-operation.decorator.js';
9
+ import { WmsMutationEvent } from '../events/wms-mutation-event.decorator.js';
10
+ import '../events/wms-mutation.interceptor.js';
7
11
 
8
12
  let LocationMutation = class LocationMutation {
9
13
  locationService;
@@ -19,6 +23,8 @@ let LocationMutation = class LocationMutation {
19
23
  };
20
24
  __decorate([
21
25
  Mutation(() => LocationDto),
26
+ WmsOperation({ resource: 'location', action: 'create' }),
27
+ WmsMutationEvent({ event: 'location.created' }),
22
28
  __param(0, Args()),
23
29
  __metadata("design:type", Function),
24
30
  __metadata("design:paramtypes", [CreateLocationArgs]),
@@ -26,6 +32,8 @@ __decorate([
26
32
  ], LocationMutation.prototype, "createLocation", null);
27
33
  __decorate([
28
34
  Mutation(() => LocationDto),
35
+ WmsOperation({ resource: 'location', action: 'update' }),
36
+ WmsMutationEvent({ event: 'location.updated' }),
29
37
  __param(0, Args()),
30
38
  __metadata("design:type", Function),
31
39
  __metadata("design:paramtypes", [UpdateLocationArgs]),