@rytass/wms-module-graphql 0.4.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 (36) hide show
  1. package/README.md +27 -0
  2. package/adapters/event-emitter.d.ts +29 -0
  3. package/adapters/event-emitter.js +41 -0
  4. package/docs/README.md +3 -0
  5. package/docs/api-reference.md +40 -0
  6. package/docs/events.md +304 -0
  7. package/docs/integration-guide.md +83 -2
  8. package/index.cjs.js +128 -1
  9. package/index.d.ts +2 -0
  10. package/index.js +3 -0
  11. package/lib/events/index.d.ts +3 -0
  12. package/lib/events/wms-mutation-event.constants.d.ts +12 -0
  13. package/lib/events/wms-mutation-event.constants.js +4 -0
  14. package/lib/events/wms-mutation-event.decorator.d.ts +15 -0
  15. package/lib/events/wms-mutation-event.decorator.js +20 -0
  16. package/lib/events/wms-mutation.interceptor.d.ts +19 -0
  17. package/lib/events/wms-mutation.interceptor.js +72 -0
  18. package/lib/graphql.module.d.ts +15 -5
  19. package/lib/graphql.module.js +8 -2
  20. package/lib/mutations/allocate-inventory-order.mutation.js +5 -0
  21. package/lib/mutations/hold-inventory-order.mutation.js +5 -0
  22. package/lib/mutations/loader.mutation.js +7 -0
  23. package/lib/mutations/location.mutation.js +4 -0
  24. package/lib/mutations/material.mutation.js +4 -0
  25. package/lib/mutations/merge-batch.mutation.js +3 -0
  26. package/lib/mutations/quality-inspection-order.mutation.js +3 -0
  27. package/lib/mutations/receive-inventory-order.mutation.js +5 -0
  28. package/lib/mutations/reclassify-inventory-order.mutation.js +3 -0
  29. package/lib/mutations/scrape-order.mutation.js +3 -0
  30. package/lib/mutations/ship-inventory-order.mutation.js +6 -0
  31. package/lib/mutations/split-batch.mutation.js +3 -0
  32. package/lib/mutations/transfer-customer-order.mutation.js +5 -0
  33. package/lib/mutations/transfer-order.mutation.js +6 -0
  34. package/lib/mutations/transfer-vendor-order.mutation.js +5 -0
  35. package/lib/mutations/warehouse-map.mutation.js +3 -0
  36. package/package.json +13 -3
package/index.cjs.js CHANGED
@@ -10,6 +10,7 @@ var typeorm = require('typeorm');
10
10
  var DataLoader = require('dataloader');
11
11
  var graphql = require('@nestjs/graphql');
12
12
  var core = require('@nestjs/core');
13
+ var rxjs = require('rxjs');
13
14
  var wmsBaseNestjsModule = require('@rytass/wms-base-nestjs-module');
14
15
  var warehouseMap_entity = require('@rytass/wms-base-nestjs-module/models/warehouse-map.entity');
15
16
 
@@ -304,6 +305,87 @@ exports.WmsAuthGuard = tslib.__decorate([
304
305
  */
305
306
  const WmsOperation = (requirement) => common.applyDecorators(common.SetMetadata(WMS_OPERATION_METADATA, requirement), common.SetMetadata(HOST_SKIP_METADATA_KEY, true), common.UseGuards(exports.WmsAuthGuard));
306
307
 
308
+ /** Reflector metadata key carrying the {@link WmsMutationEventConfig}. */
309
+ const WMS_MUTATION_EVENT_METADATA = 'wms:mutation-event';
310
+
311
+ const defaultResolveId = (result, args) => {
312
+ if (result &&
313
+ typeof result === 'object' &&
314
+ 'id' in result &&
315
+ typeof result.id === 'string') {
316
+ return result.id;
317
+ }
318
+ const argId = args['id'];
319
+ return typeof argId === 'string' ? argId : null;
320
+ };
321
+ /**
322
+ * Wraps each `@WmsMutationEvent` mutation in a dispatch scope:
323
+ * 1. runs the before-hook (throw -> veto, before any transaction);
324
+ * 2. executes the resolver (services collect stock events into the scope);
325
+ * 3. emits the operation event after the handler resolves (post-commit);
326
+ * 4. flushes the whole buffer to after-listeners + emitter bridge.
327
+ *
328
+ * When no hook/listener/bridge is configured it passes through untouched.
329
+ */
330
+ exports.WmsMutationInterceptor = class WmsMutationInterceptor {
331
+ reflector;
332
+ dispatcher;
333
+ constructor(reflector, dispatcher) {
334
+ this.reflector = reflector;
335
+ this.dispatcher = dispatcher;
336
+ }
337
+ async intercept(context, next) {
338
+ const config = this.reflector.get(WMS_MUTATION_EVENT_METADATA, context.getHandler());
339
+ if (!config || !this.dispatcher.isActive()) {
340
+ return next.handle();
341
+ }
342
+ const args = graphql.GqlExecutionContext.create(context).getArgs() ??
343
+ {};
344
+ // Before-hook runs before any DB work; a throw vetoes with zero side effects.
345
+ await this.dispatcher.runBeforeHook({
346
+ event: config.event,
347
+ args,
348
+ executionContext: context,
349
+ });
350
+ const { result, events } = await wmsModuleCore.runInDispatchScope(async () => {
351
+ const handlerResult = await rxjs.firstValueFrom(next.handle());
352
+ const id = config.resolveId
353
+ ? config.resolveId(handlerResult, args)
354
+ : defaultResolveId(handlerResult, args);
355
+ const operationEvent = {
356
+ name: config.event,
357
+ payload: { id, result: handlerResult },
358
+ };
359
+ wmsModuleCore.collectToScope(operationEvent);
360
+ return handlerResult;
361
+ });
362
+ if (events) {
363
+ await this.dispatcher.flush(events);
364
+ }
365
+ return rxjs.of(result);
366
+ }
367
+ };
368
+ exports.WmsMutationInterceptor = tslib.__decorate([
369
+ common.Injectable(),
370
+ tslib.__metadata("design:paramtypes", [core.Reflector,
371
+ wmsModuleCore.WmsEventDispatcher])
372
+ ], exports.WmsMutationInterceptor);
373
+
374
+ /**
375
+ * Declares the operation event for a WMS root mutation and wires the
376
+ * {@link WmsMutationInterceptor} that runs before-hooks (veto) and flushes
377
+ * collected events post-commit. Apply alongside `@WmsOperation` on each mutation.
378
+ *
379
+ * @example
380
+ * ```ts
381
+ * @Mutation(() => ShipInventoryOrderDto)
382
+ * @WmsOperation({ resource: 'ship-inventory-order', action: 'ship' })
383
+ * @WmsMutationEvent({ event: 'ship-inventory-order.shipped' })
384
+ * ship(@Args() args: ShipArgs): Promise<boolean> { ... }
385
+ * ```
386
+ */
387
+ const WmsMutationEvent = (config) => common.applyDecorators(common.SetMetadata(WMS_MUTATION_EVENT_METADATA, config), common.UseInterceptors(exports.WmsMutationInterceptor));
388
+
307
389
  const IdGenFunction = (input) => {
308
390
  const hash = crypto.createHash('sha256');
309
391
  hash.update(input);
@@ -1633,6 +1715,7 @@ let LoaderMutation = class LoaderMutation {
1633
1715
  tslib.__decorate([
1634
1716
  graphql.Mutation(() => [LoaderDto]),
1635
1717
  WmsOperation({ resource: 'loader', action: 'create' }),
1718
+ WmsMutationEvent({ event: 'loader.created' }),
1636
1719
  tslib.__param(0, graphql.Args()),
1637
1720
  tslib.__metadata("design:type", Function),
1638
1721
  tslib.__metadata("design:paramtypes", [CreateLoaderArgs]),
@@ -1641,6 +1724,7 @@ tslib.__decorate([
1641
1724
  tslib.__decorate([
1642
1725
  graphql.Mutation(() => [LoaderDto]),
1643
1726
  WmsOperation({ resource: 'loader', action: 'create' }),
1727
+ WmsMutationEvent({ event: 'loader.batch-created' }),
1644
1728
  tslib.__param(0, graphql.Args('items', { type: () => [CreateLoadersArgs] })),
1645
1729
  tslib.__metadata("design:type", Function),
1646
1730
  tslib.__metadata("design:paramtypes", [Array]),
@@ -1649,6 +1733,7 @@ tslib.__decorate([
1649
1733
  tslib.__decorate([
1650
1734
  graphql.Mutation(() => LoaderDto),
1651
1735
  WmsOperation({ resource: 'loader', action: 'update' }),
1736
+ WmsMutationEvent({ event: 'loader.updated' }),
1652
1737
  tslib.__param(0, graphql.Args()),
1653
1738
  tslib.__metadata("design:type", Function),
1654
1739
  tslib.__metadata("design:paramtypes", [UpdateLoaderArgs]),
@@ -1657,6 +1742,7 @@ tslib.__decorate([
1657
1742
  tslib.__decorate([
1658
1743
  graphql.Mutation(() => LoaderTypeDto),
1659
1744
  WmsOperation({ resource: 'loader', action: 'create' }),
1745
+ WmsMutationEvent({ event: 'loader.type-created' }),
1660
1746
  tslib.__param(0, graphql.Args()),
1661
1747
  tslib.__metadata("design:type", Function),
1662
1748
  tslib.__metadata("design:paramtypes", [CreateLoaderTypeArgs]),
@@ -1665,6 +1751,7 @@ tslib.__decorate([
1665
1751
  tslib.__decorate([
1666
1752
  graphql.Mutation(() => LoaderTypeDto),
1667
1753
  WmsOperation({ resource: 'loader', action: 'update' }),
1754
+ WmsMutationEvent({ event: 'loader.type-updated' }),
1668
1755
  tslib.__param(0, graphql.Args()),
1669
1756
  tslib.__metadata("design:type", Function),
1670
1757
  tslib.__metadata("design:paramtypes", [UpdateLoaderTypeArgs]),
@@ -2028,6 +2115,7 @@ let MaterialMutation = class MaterialMutation {
2028
2115
  tslib.__decorate([
2029
2116
  graphql.Mutation(() => MaterialDto),
2030
2117
  WmsOperation({ resource: 'material', action: 'create' }),
2118
+ WmsMutationEvent({ event: 'material.created' }),
2031
2119
  tslib.__param(0, graphql.Args()),
2032
2120
  tslib.__metadata("design:type", Function),
2033
2121
  tslib.__metadata("design:paramtypes", [CreateMaterialArgs]),
@@ -2036,6 +2124,7 @@ tslib.__decorate([
2036
2124
  tslib.__decorate([
2037
2125
  graphql.Mutation(() => MaterialDto),
2038
2126
  WmsOperation({ resource: 'material', action: 'update' }),
2127
+ WmsMutationEvent({ event: 'material.updated' }),
2039
2128
  tslib.__param(0, graphql.Args()),
2040
2129
  tslib.__metadata("design:type", Function),
2041
2130
  tslib.__metadata("design:paramtypes", [UpdateMaterialArgs]),
@@ -2431,6 +2520,7 @@ let ReceiveInventoryOrderMutation = class ReceiveInventoryOrderMutation {
2431
2520
  tslib.__decorate([
2432
2521
  graphql.Mutation(() => ReceiveInventoryOrderDto),
2433
2522
  WmsOperation({ resource: 'receive-inventory-order', action: 'create' }),
2523
+ WmsMutationEvent({ event: 'receive-inventory-order.created' }),
2434
2524
  tslib.__param(0, graphql.Args()),
2435
2525
  tslib.__metadata("design:type", Function),
2436
2526
  tslib.__metadata("design:paramtypes", [CreateReceiveInventoryOrderArgs]),
@@ -2439,6 +2529,7 @@ tslib.__decorate([
2439
2529
  tslib.__decorate([
2440
2530
  graphql.Mutation(() => ReceiveInventoryOrderDto),
2441
2531
  WmsOperation({ resource: 'receive-inventory-order', action: 'update' }),
2532
+ WmsMutationEvent({ event: 'receive-inventory-order.updated' }),
2442
2533
  tslib.__param(0, graphql.Args()),
2443
2534
  tslib.__metadata("design:type", Function),
2444
2535
  tslib.__metadata("design:paramtypes", [UpdateReceiveInventoryOrderArgs]),
@@ -2447,6 +2538,7 @@ tslib.__decorate([
2447
2538
  tslib.__decorate([
2448
2539
  graphql.Mutation(() => Boolean),
2449
2540
  WmsOperation({ resource: 'receive-inventory-order', action: 'receive' }),
2541
+ WmsMutationEvent({ event: 'receive-inventory-order.received' }),
2450
2542
  tslib.__param(0, graphql.Args('items', { type: () => [ReceiveOrderArgs] })),
2451
2543
  tslib.__metadata("design:type", Function),
2452
2544
  tslib.__metadata("design:paramtypes", [Array]),
@@ -2790,6 +2882,7 @@ let ShipInventoryOrderMutation = class ShipInventoryOrderMutation {
2790
2882
  tslib.__decorate([
2791
2883
  graphql.Mutation(() => ShipInventoryOrderDto),
2792
2884
  WmsOperation({ resource: 'ship-inventory-order', action: 'create' }),
2885
+ WmsMutationEvent({ event: 'ship-inventory-order.created' }),
2793
2886
  tslib.__param(0, graphql.Args()),
2794
2887
  tslib.__metadata("design:type", Function),
2795
2888
  tslib.__metadata("design:paramtypes", [CreateShipInventoryOrderArgs]),
@@ -2798,6 +2891,7 @@ tslib.__decorate([
2798
2891
  tslib.__decorate([
2799
2892
  graphql.Mutation(() => ShipInventoryOrderDto),
2800
2893
  WmsOperation({ resource: 'ship-inventory-order', action: 'update' }),
2894
+ WmsMutationEvent({ event: 'ship-inventory-order.updated' }),
2801
2895
  tslib.__param(0, graphql.Args()),
2802
2896
  tslib.__metadata("design:type", Function),
2803
2897
  tslib.__metadata("design:paramtypes", [UpdateShipInventoryOrderArgs]),
@@ -2806,6 +2900,7 @@ tslib.__decorate([
2806
2900
  tslib.__decorate([
2807
2901
  graphql.Mutation(() => Boolean),
2808
2902
  WmsOperation({ resource: 'ship-inventory-order', action: 'ship' }),
2903
+ WmsMutationEvent({ event: 'ship-inventory-order.shipped' }),
2809
2904
  tslib.__param(0, graphql.Args('items', { type: () => [ShipOrderArgs] })),
2810
2905
  tslib.__metadata("design:type", Function),
2811
2906
  tslib.__metadata("design:paramtypes", [Array]),
@@ -2814,6 +2909,7 @@ tslib.__decorate([
2814
2909
  tslib.__decorate([
2815
2910
  graphql.Mutation(() => Boolean),
2816
2911
  WmsOperation({ resource: 'ship-inventory-order', action: 'cancel' }),
2912
+ WmsMutationEvent({ event: 'ship-inventory-order.ship-cancelled' }),
2817
2913
  tslib.__param(0, graphql.Args('items', { type: () => [CancelShipOrderArgs] })),
2818
2914
  tslib.__metadata("design:type", Function),
2819
2915
  tslib.__metadata("design:paramtypes", [Array]),
@@ -3469,6 +3565,7 @@ let WarehouseMapMutation = class WarehouseMapMutation {
3469
3565
  tslib.__decorate([
3470
3566
  graphql.Mutation(() => MapDataDto),
3471
3567
  WmsOperation({ resource: 'warehouse-map', action: 'update' }),
3568
+ WmsMutationEvent({ event: 'warehouse-map.updated' }),
3472
3569
  tslib.__param(0, graphql.Args()),
3473
3570
  tslib.__metadata("design:type", Function),
3474
3571
  tslib.__metadata("design:paramtypes", [UpdateWarehouseMapArgs]),
@@ -3650,6 +3747,7 @@ let LocationMutation = class LocationMutation {
3650
3747
  tslib.__decorate([
3651
3748
  graphql.Mutation(() => LocationDto),
3652
3749
  WmsOperation({ resource: 'location', action: 'create' }),
3750
+ WmsMutationEvent({ event: 'location.created' }),
3653
3751
  tslib.__param(0, graphql.Args()),
3654
3752
  tslib.__metadata("design:type", Function),
3655
3753
  tslib.__metadata("design:paramtypes", [CreateLocationArgs]),
@@ -3658,6 +3756,7 @@ tslib.__decorate([
3658
3756
  tslib.__decorate([
3659
3757
  graphql.Mutation(() => LocationDto),
3660
3758
  WmsOperation({ resource: 'location', action: 'update' }),
3759
+ WmsMutationEvent({ event: 'location.updated' }),
3661
3760
  tslib.__param(0, graphql.Args()),
3662
3761
  tslib.__metadata("design:type", Function),
3663
3762
  tslib.__metadata("design:paramtypes", [UpdateLocationArgs]),
@@ -3961,6 +4060,7 @@ let TransferOrderMutation = class TransferOrderMutation {
3961
4060
  tslib.__decorate([
3962
4061
  graphql.Mutation(() => TransferOrderDto),
3963
4062
  WmsOperation({ resource: 'transfer-order', action: 'create' }),
4063
+ WmsMutationEvent({ event: 'transfer-order.created' }),
3964
4064
  tslib.__param(0, graphql.Args()),
3965
4065
  tslib.__metadata("design:type", Function),
3966
4066
  tslib.__metadata("design:paramtypes", [CreateTransferOrderArgs]),
@@ -3969,6 +4069,7 @@ tslib.__decorate([
3969
4069
  tslib.__decorate([
3970
4070
  graphql.Mutation(() => TransferOrderDto),
3971
4071
  WmsOperation({ resource: 'transfer-order', action: 'update' }),
4072
+ WmsMutationEvent({ event: 'transfer-order.updated' }),
3972
4073
  tslib.__param(0, graphql.Args()),
3973
4074
  tslib.__metadata("design:type", Function),
3974
4075
  tslib.__metadata("design:paramtypes", [UpdateTransferOrderArgs]),
@@ -3977,6 +4078,7 @@ tslib.__decorate([
3977
4078
  tslib.__decorate([
3978
4079
  graphql.Mutation(() => Boolean),
3979
4080
  WmsOperation({ resource: 'transfer-order', action: 'transfer' }),
4081
+ WmsMutationEvent({ event: 'transfer-order.transferred' }),
3980
4082
  tslib.__param(0, graphql.Args('items', { type: () => [TransferOrderArgs] })),
3981
4083
  tslib.__metadata("design:type", Function),
3982
4084
  tslib.__metadata("design:paramtypes", [Array]),
@@ -3985,6 +4087,7 @@ tslib.__decorate([
3985
4087
  tslib.__decorate([
3986
4088
  graphql.Mutation(() => Boolean),
3987
4089
  WmsOperation({ resource: 'transfer-order', action: 'cancel' }),
4090
+ WmsMutationEvent({ event: 'transfer-order.transfer-cancelled' }),
3988
4091
  tslib.__param(0, graphql.Args('items', { type: () => [CancelTransferOrderArgs] })),
3989
4092
  tslib.__metadata("design:type", Function),
3990
4093
  tslib.__metadata("design:paramtypes", [Array]),
@@ -4436,6 +4539,7 @@ let HoldInventoryOrderMutation = class HoldInventoryOrderMutation {
4436
4539
  tslib.__decorate([
4437
4540
  graphql.Mutation(() => HoldInventoryOrderDto),
4438
4541
  WmsOperation({ resource: 'hold-inventory-order', action: 'create' }),
4542
+ WmsMutationEvent({ event: 'hold-inventory-order.created' }),
4439
4543
  tslib.__param(0, graphql.Args()),
4440
4544
  tslib.__metadata("design:type", Function),
4441
4545
  tslib.__metadata("design:paramtypes", [CreateHoldInventoryOrderArgs]),
@@ -4444,6 +4548,7 @@ tslib.__decorate([
4444
4548
  tslib.__decorate([
4445
4549
  graphql.Mutation(() => HoldInventoryOrderDto),
4446
4550
  WmsOperation({ resource: 'hold-inventory-order', action: 'update' }),
4551
+ WmsMutationEvent({ event: 'hold-inventory-order.updated' }),
4447
4552
  tslib.__param(0, graphql.Args()),
4448
4553
  tslib.__metadata("design:type", Function),
4449
4554
  tslib.__metadata("design:paramtypes", [UpdateHoldInventoryOrderArgs]),
@@ -4452,6 +4557,7 @@ tslib.__decorate([
4452
4557
  tslib.__decorate([
4453
4558
  graphql.Mutation(() => Boolean),
4454
4559
  WmsOperation({ resource: 'hold-inventory-order', action: 'release' }),
4560
+ WmsMutationEvent({ event: 'hold-inventory-order.released' }),
4455
4561
  tslib.__param(0, graphql.Args('items', { type: () => [ReleaseOrderArgs] })),
4456
4562
  tslib.__metadata("design:type", Function),
4457
4563
  tslib.__metadata("design:paramtypes", [Array]),
@@ -4970,6 +5076,7 @@ let AllocateInventoryOrderMutation = class AllocateInventoryOrderMutation {
4970
5076
  tslib.__decorate([
4971
5077
  graphql.Mutation(() => AllocateInventoryOrderDto),
4972
5078
  WmsOperation({ resource: 'allocate-inventory-order', action: 'create' }),
5079
+ WmsMutationEvent({ event: 'allocate-inventory-order.created' }),
4973
5080
  tslib.__param(0, graphql.Args()),
4974
5081
  tslib.__metadata("design:type", Function),
4975
5082
  tslib.__metadata("design:paramtypes", [CreateAllocateInventoryOrderArgs]),
@@ -4978,6 +5085,7 @@ tslib.__decorate([
4978
5085
  tslib.__decorate([
4979
5086
  graphql.Mutation(() => AllocateInventoryOrderDto),
4980
5087
  WmsOperation({ resource: 'allocate-inventory-order', action: 'update' }),
5088
+ WmsMutationEvent({ event: 'allocate-inventory-order.updated' }),
4981
5089
  tslib.__param(0, graphql.Args()),
4982
5090
  tslib.__metadata("design:type", Function),
4983
5091
  tslib.__metadata("design:paramtypes", [UpdateAllocateInventoryOrderArgs]),
@@ -4986,6 +5094,7 @@ tslib.__decorate([
4986
5094
  tslib.__decorate([
4987
5095
  graphql.Mutation(() => Boolean),
4988
5096
  WmsOperation({ resource: 'allocate-inventory-order', action: 'deallocate' }),
5097
+ WmsMutationEvent({ event: 'allocate-inventory-order.deallocated' }),
4989
5098
  tslib.__param(0, graphql.Args('items', { type: () => [DeallocateOrderArgs] })),
4990
5099
  tslib.__metadata("design:type", Function),
4991
5100
  tslib.__metadata("design:paramtypes", [Array]),
@@ -5442,6 +5551,7 @@ let SplitBatchMutation = class SplitBatchMutation {
5442
5551
  tslib.__decorate([
5443
5552
  graphql.Mutation(() => SplitBatchOrderDto),
5444
5553
  WmsOperation({ resource: 'batch', action: 'split' }),
5554
+ WmsMutationEvent({ event: 'batch.splitted' }),
5445
5555
  tslib.__param(0, graphql.Args()),
5446
5556
  tslib.__metadata("design:type", Function),
5447
5557
  tslib.__metadata("design:paramtypes", [SplitBatchArgs]),
@@ -5561,6 +5671,7 @@ let MergeBatchMutation = class MergeBatchMutation {
5561
5671
  tslib.__decorate([
5562
5672
  graphql.Mutation(() => MergeBatchOrderDto),
5563
5673
  WmsOperation({ resource: 'batch', action: 'merge' }),
5674
+ WmsMutationEvent({ event: 'batch.merged' }),
5564
5675
  tslib.__param(0, graphql.Args()),
5565
5676
  tslib.__metadata("design:type", Function),
5566
5677
  tslib.__metadata("design:paramtypes", [MergeBatchArgs]),
@@ -5686,6 +5797,7 @@ let ReclassifyInventoryOrderMutation = class ReclassifyInventoryOrderMutation {
5686
5797
  tslib.__decorate([
5687
5798
  graphql.Mutation(() => ReclassifyInventoryOrderDto),
5688
5799
  WmsOperation({ resource: 'reclassify-inventory-order', action: 'reclassify' }),
5800
+ WmsMutationEvent({ event: 'reclassify-inventory-order.reclassified' }),
5689
5801
  tslib.__param(0, graphql.Args()),
5690
5802
  tslib.__metadata("design:type", Function),
5691
5803
  tslib.__metadata("design:paramtypes", [ReclassifyInventoryOrderArgs]),
@@ -5859,6 +5971,7 @@ let TransferCustomerOrderMutation = class TransferCustomerOrderMutation {
5859
5971
  tslib.__decorate([
5860
5972
  graphql.Mutation(() => TransferCustomerOrderDto),
5861
5973
  WmsOperation({ resource: 'transfer-customer-order', action: 'create' }),
5974
+ WmsMutationEvent({ event: 'transfer-customer-order.created' }),
5862
5975
  tslib.__param(0, graphql.Args()),
5863
5976
  tslib.__metadata("design:type", Function),
5864
5977
  tslib.__metadata("design:paramtypes", [CreateTransferCustomerOrderArgs]),
@@ -5867,6 +5980,7 @@ tslib.__decorate([
5867
5980
  tslib.__decorate([
5868
5981
  graphql.Mutation(() => TransferCustomerOrderDto),
5869
5982
  WmsOperation({ resource: 'transfer-customer-order', action: 'update' }),
5983
+ WmsMutationEvent({ event: 'transfer-customer-order.updated' }),
5870
5984
  tslib.__param(0, graphql.Args()),
5871
5985
  tslib.__metadata("design:type", Function),
5872
5986
  tslib.__metadata("design:paramtypes", [UpdateTransferCustomerOrderArgs]),
@@ -5875,6 +5989,7 @@ tslib.__decorate([
5875
5989
  tslib.__decorate([
5876
5990
  graphql.Mutation(() => Boolean),
5877
5991
  WmsOperation({ resource: 'transfer-customer-order', action: 'return' }),
5992
+ WmsMutationEvent({ event: 'transfer-customer-order.returned' }),
5878
5993
  tslib.__param(0, graphql.Args('items', { type: () => [ReturnTransferCustomerOrderArgs] })),
5879
5994
  tslib.__metadata("design:type", Function),
5880
5995
  tslib.__metadata("design:paramtypes", [Array]),
@@ -6067,6 +6182,7 @@ let TransferVendorOrderMutation = class TransferVendorOrderMutation {
6067
6182
  tslib.__decorate([
6068
6183
  graphql.Mutation(() => TransferVendorOrderDto),
6069
6184
  WmsOperation({ resource: 'transfer-vendor-order', action: 'create' }),
6185
+ WmsMutationEvent({ event: 'transfer-vendor-order.created' }),
6070
6186
  tslib.__param(0, graphql.Args()),
6071
6187
  tslib.__metadata("design:type", Function),
6072
6188
  tslib.__metadata("design:paramtypes", [CreateTransferVendorOrderArgs]),
@@ -6075,6 +6191,7 @@ tslib.__decorate([
6075
6191
  tslib.__decorate([
6076
6192
  graphql.Mutation(() => TransferVendorOrderDto),
6077
6193
  WmsOperation({ resource: 'transfer-vendor-order', action: 'update' }),
6194
+ WmsMutationEvent({ event: 'transfer-vendor-order.updated' }),
6078
6195
  tslib.__param(0, graphql.Args()),
6079
6196
  tslib.__metadata("design:type", Function),
6080
6197
  tslib.__metadata("design:paramtypes", [UpdateTransferVendorOrderArgs]),
@@ -6083,6 +6200,7 @@ tslib.__decorate([
6083
6200
  tslib.__decorate([
6084
6201
  graphql.Mutation(() => Boolean),
6085
6202
  WmsOperation({ resource: 'transfer-vendor-order', action: 'return' }),
6203
+ WmsMutationEvent({ event: 'transfer-vendor-order.returned' }),
6086
6204
  tslib.__param(0, graphql.Args('items', { type: () => [ReturnTransferVendorOrderArgs] })),
6087
6205
  tslib.__metadata("design:type", Function),
6088
6206
  tslib.__metadata("design:paramtypes", [Array]),
@@ -6205,6 +6323,7 @@ let ScrapeMutation = class ScrapeMutation {
6205
6323
  tslib.__decorate([
6206
6324
  graphql.Mutation(() => ScrapeOrderDto),
6207
6325
  WmsOperation({ resource: 'scrape-order', action: 'scrape' }),
6326
+ WmsMutationEvent({ event: 'scrape-order.scraped' }),
6208
6327
  tslib.__param(0, graphql.Args()),
6209
6328
  tslib.__metadata("design:type", Function),
6210
6329
  tslib.__metadata("design:paramtypes", [ScrapeOrderArgs]),
@@ -6288,6 +6407,7 @@ let QualityInspectionOrderMutation = class QualityInspectionOrderMutation {
6288
6407
  tslib.__decorate([
6289
6408
  graphql.Mutation(() => QualityInspectionOrderDto),
6290
6409
  WmsOperation({ resource: 'quality-inspection-order', action: 'inspect' }),
6410
+ WmsMutationEvent({ event: 'quality-inspection-order.inspected' }),
6291
6411
  tslib.__param(0, graphql.Args()),
6292
6412
  tslib.__metadata("design:type", Function),
6293
6413
  tslib.__metadata("design:paramtypes", [CreateQualityInspectionOrderArgs]),
@@ -6401,13 +6521,18 @@ const exportDependencies = [
6401
6521
  ];
6402
6522
  exports.WmsModuleGraphqlModule = WmsModuleGraphqlModule_1 = class WmsModuleGraphqlModule {
6403
6523
  static forRootAsync(options) {
6404
- const { authChecker, ...coreOptions } = options;
6524
+ const { authChecker, beforeHooks, afterListeners, eventEmitterBridge, ...coreOptions } = options;
6405
6525
  const coreModule = wmsModuleCore.WmsModuleCoreModule.forRootAsync(coreOptions);
6406
6526
  const authOptions = { authChecker };
6527
+ const eventOptions = { beforeHooks, afterListeners };
6407
6528
  const providers = [
6408
6529
  ...exportDependencies,
6409
6530
  exports.WmsAuthGuard,
6410
6531
  { provide: WMS_AUTH_OPTIONS, useValue: authOptions },
6532
+ { provide: wmsModuleCore.WMS_EVENT_OPTIONS, useValue: eventOptions },
6533
+ wmsModuleCore.WmsEventDispatcher,
6534
+ exports.WmsMutationInterceptor,
6535
+ ...(eventEmitterBridge ? [eventEmitterBridge] : []),
6411
6536
  ];
6412
6537
  return {
6413
6538
  module: WmsModuleGraphqlModule_1,
@@ -6425,6 +6550,8 @@ exports.WmsModuleGraphqlModule = WmsModuleGraphqlModule_1 = tslib.__decorate([
6425
6550
 
6426
6551
  exports.HOST_SKIP_METADATA_KEY = HOST_SKIP_METADATA_KEY;
6427
6552
  exports.WMS_AUTH_OPTIONS = WMS_AUTH_OPTIONS;
6553
+ exports.WMS_MUTATION_EVENT_METADATA = WMS_MUTATION_EVENT_METADATA;
6428
6554
  exports.WMS_OPERATION_METADATA = WMS_OPERATION_METADATA;
6555
+ exports.WmsMutationEvent = WmsMutationEvent;
6429
6556
  exports.WmsOperation = WmsOperation;
6430
6557
  exports.createWmsLoaders = createWmsLoaders;
package/index.d.ts CHANGED
@@ -5,3 +5,5 @@ export type { LoaderType } from './lib/helpers/dataloader.type';
5
5
  export { createWmsLoaders, type WmsLoaders, } from './lib/helpers/create-wms-loaders';
6
6
  export { HOST_SKIP_METADATA_KEY, WmsAuthGuard, WmsOperation, WMS_AUTH_OPTIONS, WMS_OPERATION_METADATA, type WmsAuthChecker, type WmsAuthModuleOptions, type WmsOperationRequirement, } from './lib/auth';
7
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
@@ -5,3 +5,6 @@ export { HOST_SKIP_METADATA_KEY } from './lib/auth/host-skip.constant.js';
5
5
  export { WmsAuthGuard } from './lib/auth/wms-auth.guard.js';
6
6
  export { WmsOperation } from './lib/auth/wms-operation.decorator.js';
7
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,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,12 +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
3
  import { type WmsAuthModuleOptions } from './auth';
4
4
  /**
5
5
  * Options for `WmsModuleGraphqlModule.forRootAsync`. Extends the core module
6
- * options with an optional {@link WmsAuthModuleOptions.authChecker}; when the
7
- * checker is omitted the module stays fully permissive (pre-auth behaviour).
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).
8
16
  */
9
- export type WmsModuleGraphqlModuleAsyncOptions = WmsModuleCoreModuleAsyncOptions & WmsAuthModuleOptions;
17
+ export type WmsModuleGraphqlModuleAsyncOptions = WmsModuleCoreModuleAsyncOptions & WmsAuthModuleOptions & WmsEventModuleOptions & {
18
+ readonly eventEmitterBridge?: Provider;
19
+ };
10
20
  export declare class WmsModuleGraphqlModule {
11
21
  static forRootAsync(options: WmsModuleGraphqlModuleAsyncOptions): DynamicModule;
12
22
  }
@@ -1,11 +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
7
  import { WmsAuthGuard } from './auth/wms-auth.guard.js';
8
8
  import { WMS_AUTH_OPTIONS } from './auth/wms-auth.types.js';
9
+ import { WmsMutationInterceptor } from './events/wms-mutation.interceptor.js';
9
10
  import { LoaderMutation } from './mutations/loader.mutation.js';
10
11
  import { LoaderQuery } from './queries/loader.query.js';
11
12
  import { MaterialMutation } from './mutations/material.mutation.js';
@@ -110,13 +111,18 @@ const exportDependencies = [
110
111
  ];
111
112
  let WmsModuleGraphqlModule = WmsModuleGraphqlModule_1 = class WmsModuleGraphqlModule {
112
113
  static forRootAsync(options) {
113
- const { authChecker, ...coreOptions } = options;
114
+ const { authChecker, beforeHooks, afterListeners, eventEmitterBridge, ...coreOptions } = options;
114
115
  const coreModule = WmsModuleCoreModule.forRootAsync(coreOptions);
115
116
  const authOptions = { authChecker };
117
+ const eventOptions = { beforeHooks, afterListeners };
116
118
  const providers = [
117
119
  ...exportDependencies,
118
120
  WmsAuthGuard,
119
121
  { provide: WMS_AUTH_OPTIONS, useValue: authOptions },
122
+ { provide: WMS_EVENT_OPTIONS, useValue: eventOptions },
123
+ WmsEventDispatcher,
124
+ WmsMutationInterceptor,
125
+ ...(eventEmitterBridge ? [eventEmitterBridge] : []),
120
126
  ];
121
127
  return {
122
128
  module: WmsModuleGraphqlModule_1,