@hazeljs/pubsub 0.7.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.
Files changed (39) hide show
  1. package/LICENSE +192 -0
  2. package/README.md +187 -0
  3. package/dist/__tests__/index.test.d.ts +2 -0
  4. package/dist/__tests__/index.test.d.ts.map +1 -0
  5. package/dist/__tests__/index.test.js +48 -0
  6. package/dist/__tests__/pubsub-publisher.service.test.d.ts +2 -0
  7. package/dist/__tests__/pubsub-publisher.service.test.d.ts.map +1 -0
  8. package/dist/__tests__/pubsub-publisher.service.test.js +45 -0
  9. package/dist/__tests__/pubsub-subscriber.service.test.d.ts +2 -0
  10. package/dist/__tests__/pubsub-subscriber.service.test.d.ts.map +1 -0
  11. package/dist/__tests__/pubsub-subscriber.service.test.js +300 -0
  12. package/dist/__tests__/pubsub.decorator.test.d.ts +2 -0
  13. package/dist/__tests__/pubsub.decorator.test.d.ts.map +1 -0
  14. package/dist/__tests__/pubsub.decorator.test.js +90 -0
  15. package/dist/__tests__/pubsub.module.test.d.ts +2 -0
  16. package/dist/__tests__/pubsub.module.test.d.ts.map +1 -0
  17. package/dist/__tests__/pubsub.module.test.js +33 -0
  18. package/dist/decorators/pubsub-consumer.decorator.d.ts +7 -0
  19. package/dist/decorators/pubsub-consumer.decorator.d.ts.map +1 -0
  20. package/dist/decorators/pubsub-consumer.decorator.js +37 -0
  21. package/dist/decorators/pubsub-subscribe.decorator.d.ts +6 -0
  22. package/dist/decorators/pubsub-subscribe.decorator.d.ts.map +1 -0
  23. package/dist/decorators/pubsub-subscribe.decorator.js +29 -0
  24. package/dist/index.d.ts +10 -0
  25. package/dist/index.d.ts.map +1 -0
  26. package/dist/index.js +20 -0
  27. package/dist/pubsub-publisher.service.d.ts +13 -0
  28. package/dist/pubsub-publisher.service.d.ts.map +1 -0
  29. package/dist/pubsub-publisher.service.js +60 -0
  30. package/dist/pubsub-subscriber.service.d.ts +15 -0
  31. package/dist/pubsub-subscriber.service.d.ts.map +1 -0
  32. package/dist/pubsub-subscriber.service.js +153 -0
  33. package/dist/pubsub.module.d.ts +10 -0
  34. package/dist/pubsub.module.d.ts.map +1 -0
  35. package/dist/pubsub.module.js +65 -0
  36. package/dist/pubsub.types.d.ts +49 -0
  37. package/dist/pubsub.types.d.ts.map +1 -0
  38. package/dist/pubsub.types.js +5 -0
  39. package/package.json +54 -0
@@ -0,0 +1,300 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const pubsub_subscriber_service_1 = require("../pubsub-subscriber.service");
4
+ const pubsub_consumer_decorator_1 = require("../decorators/pubsub-consumer.decorator");
5
+ const pubsub_subscribe_decorator_1 = require("../decorators/pubsub-subscribe.decorator");
6
+ jest.mock('../decorators/pubsub-consumer.decorator', () => ({
7
+ getPubSubConsumerMetadata: jest.fn(),
8
+ PUBSUB_CONSUMER_METADATA_KEY: Symbol('pubsub:consumer'),
9
+ }));
10
+ jest.mock('../decorators/pubsub-subscribe.decorator', () => ({
11
+ getPubSubSubscribeMetadata: jest.fn(),
12
+ PUBSUB_SUBSCRIBE_METADATA_KEY: Symbol('pubsub:subscribe'),
13
+ }));
14
+ const mockOn = jest.fn();
15
+ const mockRemoveListener = jest.fn();
16
+ const mockExists = jest.fn().mockResolvedValue([true]);
17
+ const mockSubscription = {
18
+ on: mockOn,
19
+ removeListener: mockRemoveListener,
20
+ exists: mockExists,
21
+ };
22
+ const mockCreateSubscription = jest.fn().mockResolvedValue([{}]);
23
+ const mockTopic = jest.fn().mockReturnValue({
24
+ createSubscription: mockCreateSubscription,
25
+ });
26
+ const mockPubSub = {
27
+ subscription: jest.fn().mockReturnValue(mockSubscription),
28
+ topic: mockTopic,
29
+ };
30
+ const mockGetConsumerMetadata = pubsub_consumer_decorator_1.getPubSubConsumerMetadata;
31
+ const mockGetSubscribeMetadata = pubsub_subscribe_decorator_1.getPubSubSubscribeMetadata;
32
+ describe('PubSubSubscriberService', () => {
33
+ let service;
34
+ beforeEach(() => {
35
+ jest.clearAllMocks();
36
+ mockExists.mockResolvedValue([true]);
37
+ service = new pubsub_subscriber_service_1.PubSubSubscriberService(mockPubSub);
38
+ });
39
+ it('returns when provider has no @PubSubConsumer metadata', async () => {
40
+ mockGetConsumerMetadata.mockReturnValue(undefined);
41
+ mockGetSubscribeMetadata.mockReturnValue([]);
42
+ class Provider {
43
+ }
44
+ await service.registerFromProvider(new Provider());
45
+ expect(mockPubSub.subscription).not.toHaveBeenCalled();
46
+ });
47
+ it('returns when provider has no @PubSubSubscribe metadata', async () => {
48
+ mockGetConsumerMetadata.mockReturnValue({ ackOnSuccess: true });
49
+ mockGetSubscribeMetadata.mockReturnValue([]);
50
+ class Provider {
51
+ }
52
+ await service.registerFromProvider(new Provider());
53
+ expect(mockPubSub.subscription).not.toHaveBeenCalled();
54
+ });
55
+ it('registers and wires message handlers', async () => {
56
+ mockGetConsumerMetadata.mockReturnValue({
57
+ ackOnSuccess: true,
58
+ nackOnError: true,
59
+ parseJson: true,
60
+ });
61
+ mockGetSubscribeMetadata.mockReturnValue([
62
+ { methodName: 'handleMessage', options: { subscription: 'orders-sub' } },
63
+ ]);
64
+ class Provider {
65
+ async handleMessage() { }
66
+ }
67
+ await service.registerFromProvider(new Provider());
68
+ expect(mockPubSub.subscription).toHaveBeenCalledWith('orders-sub');
69
+ expect(mockOn).toHaveBeenCalledWith('message', expect.any(Function));
70
+ expect(service.getSubscriptionCount()).toBe(1);
71
+ });
72
+ it('skips registration when method does not exist on provider', async () => {
73
+ mockGetConsumerMetadata.mockReturnValue({ ackOnSuccess: true });
74
+ mockGetSubscribeMetadata.mockReturnValue([
75
+ { methodName: 'missingMethod', options: { subscription: 'orders-sub' } },
76
+ ]);
77
+ class Provider {
78
+ }
79
+ await service.registerFromProvider(new Provider());
80
+ expect(mockOn).not.toHaveBeenCalled();
81
+ expect(service.getSubscriptionCount()).toBe(0);
82
+ });
83
+ it('acks on successful handler completion by default', async () => {
84
+ mockGetConsumerMetadata.mockReturnValue({
85
+ ackOnSuccess: true,
86
+ nackOnError: true,
87
+ parseJson: true,
88
+ });
89
+ mockGetSubscribeMetadata.mockReturnValue([
90
+ { methodName: 'handleMessage', options: { subscription: 'orders-sub' } },
91
+ ]);
92
+ const handleSpy = jest.fn().mockResolvedValue(undefined);
93
+ class Provider {
94
+ async handleMessage(payload) {
95
+ handleSpy(payload);
96
+ }
97
+ }
98
+ await service.registerFromProvider(new Provider());
99
+ const messageHandler = mockOn.mock.calls.find((call) => call[0] === 'message')?.[1];
100
+ const ack = jest.fn();
101
+ const nack = jest.fn();
102
+ await messageHandler({
103
+ data: Buffer.from(JSON.stringify({ id: 1 })),
104
+ attributes: {},
105
+ id: 'm-1',
106
+ ack,
107
+ nack,
108
+ });
109
+ expect(handleSpy).toHaveBeenCalled();
110
+ expect(ack).toHaveBeenCalledTimes(1);
111
+ expect(nack).not.toHaveBeenCalled();
112
+ });
113
+ it('nacks on handler error when enabled', async () => {
114
+ mockGetConsumerMetadata.mockReturnValue({
115
+ ackOnSuccess: true,
116
+ nackOnError: true,
117
+ parseJson: true,
118
+ });
119
+ mockGetSubscribeMetadata.mockReturnValue([
120
+ { methodName: 'handleMessage', options: { subscription: 'orders-sub' } },
121
+ ]);
122
+ class Provider {
123
+ async handleMessage() {
124
+ throw new Error('failed');
125
+ }
126
+ }
127
+ await service.registerFromProvider(new Provider());
128
+ const messageHandler = mockOn.mock.calls.find((call) => call[0] === 'message')?.[1];
129
+ const ack = jest.fn();
130
+ const nack = jest.fn();
131
+ await messageHandler({
132
+ data: Buffer.from('test'),
133
+ attributes: {},
134
+ id: 'm-2',
135
+ ack,
136
+ nack,
137
+ });
138
+ expect(ack).not.toHaveBeenCalled();
139
+ expect(nack).toHaveBeenCalledTimes(1);
140
+ });
141
+ it('does not nack on handler error when nackOnError is false', async () => {
142
+ mockGetConsumerMetadata.mockReturnValue({
143
+ ackOnSuccess: true,
144
+ nackOnError: false,
145
+ parseJson: true,
146
+ });
147
+ mockGetSubscribeMetadata.mockReturnValue([
148
+ { methodName: 'handleMessage', options: { subscription: 'orders-sub' } },
149
+ ]);
150
+ class Provider {
151
+ async handleMessage() {
152
+ throw new Error('failed');
153
+ }
154
+ }
155
+ await service.registerFromProvider(new Provider());
156
+ const messageHandler = mockOn.mock.calls.find((call) => call[0] === 'message')?.[1];
157
+ const ack = jest.fn();
158
+ const nack = jest.fn();
159
+ await messageHandler({
160
+ data: Buffer.from('test'),
161
+ attributes: {},
162
+ id: 'm-3',
163
+ ack,
164
+ nack,
165
+ });
166
+ expect(ack).not.toHaveBeenCalled();
167
+ expect(nack).not.toHaveBeenCalled();
168
+ });
169
+ it('nacks when handler explicitly returns "nack"', async () => {
170
+ mockGetConsumerMetadata.mockReturnValue({
171
+ ackOnSuccess: true,
172
+ nackOnError: true,
173
+ parseJson: true,
174
+ });
175
+ mockGetSubscribeMetadata.mockReturnValue([
176
+ { methodName: 'handleMessage', options: { subscription: 'orders-sub' } },
177
+ ]);
178
+ class Provider {
179
+ async handleMessage() {
180
+ return 'nack';
181
+ }
182
+ }
183
+ await service.registerFromProvider(new Provider());
184
+ const messageHandler = mockOn.mock.calls.find((call) => call[0] === 'message')?.[1];
185
+ const ack = jest.fn();
186
+ const nack = jest.fn();
187
+ await messageHandler({
188
+ data: Buffer.from('test'),
189
+ attributes: {},
190
+ id: 'm-4',
191
+ ack,
192
+ nack,
193
+ });
194
+ expect(ack).not.toHaveBeenCalled();
195
+ expect(nack).toHaveBeenCalledTimes(1);
196
+ });
197
+ it('passes publish metadata and supports manual ack/nack in payload', async () => {
198
+ mockGetConsumerMetadata.mockReturnValue({
199
+ ackOnSuccess: false,
200
+ nackOnError: true,
201
+ parseJson: false,
202
+ });
203
+ mockGetSubscribeMetadata.mockReturnValue([
204
+ { methodName: 'handleMessage', options: { subscription: 'orders-sub' } },
205
+ ]);
206
+ const capturedPayloads = [];
207
+ class Provider {
208
+ async handleMessage(payload) {
209
+ capturedPayloads.push(payload);
210
+ payload.ack();
211
+ payload.nack();
212
+ return 'ack';
213
+ }
214
+ }
215
+ await service.registerFromProvider(new Provider());
216
+ const messageHandler = mockOn.mock.calls.find((call) => call[0] === 'message')?.[1];
217
+ const ack = jest.fn();
218
+ const nack = jest.fn();
219
+ await messageHandler({
220
+ data: Buffer.from('plain text'),
221
+ attributes: { source: 'test' },
222
+ id: 'm-5',
223
+ orderingKey: 'order-123',
224
+ publishTime: '2026-01-01T00:00:00.000Z',
225
+ ack,
226
+ nack,
227
+ });
228
+ expect(capturedPayloads).toHaveLength(1);
229
+ const payload = capturedPayloads[0];
230
+ expect(payload.data).toBe('plain text');
231
+ expect(payload.orderingKey).toBe('order-123');
232
+ expect(payload.publishTime).toBeInstanceOf(Date);
233
+ expect(ack).toHaveBeenCalled();
234
+ expect(nack).toHaveBeenCalled();
235
+ });
236
+ it('supports auto-creating subscriptions', async () => {
237
+ mockGetConsumerMetadata.mockReturnValue({ autoCreateSubscription: true });
238
+ mockGetSubscribeMetadata.mockReturnValue([
239
+ {
240
+ methodName: 'handleMessage',
241
+ options: {
242
+ subscription: 'new-subscription',
243
+ topic: 'orders',
244
+ autoCreateSubscription: true,
245
+ },
246
+ },
247
+ ]);
248
+ mockExists.mockResolvedValue([false]);
249
+ class Provider {
250
+ async handleMessage() { }
251
+ }
252
+ await service.registerFromProvider(new Provider());
253
+ expect(mockCreateSubscription).toHaveBeenCalledWith('new-subscription');
254
+ });
255
+ it('throws when auto-create is enabled without topic', async () => {
256
+ mockGetConsumerMetadata.mockReturnValue({ autoCreateSubscription: true });
257
+ mockGetSubscribeMetadata.mockReturnValue([
258
+ {
259
+ methodName: 'handleMessage',
260
+ options: {
261
+ subscription: 'new-subscription',
262
+ autoCreateSubscription: true,
263
+ },
264
+ },
265
+ ]);
266
+ mockExists.mockResolvedValue([false]);
267
+ class Provider {
268
+ async handleMessage() { }
269
+ }
270
+ await expect(service.registerFromProvider(new Provider())).rejects.toThrow(/without a topic/i);
271
+ });
272
+ it('cleans up listeners on destroy', async () => {
273
+ mockGetConsumerMetadata.mockReturnValue({ ackOnSuccess: true });
274
+ mockGetSubscribeMetadata.mockReturnValue([
275
+ { methodName: 'handleMessage', options: { subscription: 'orders-sub' } },
276
+ ]);
277
+ class Provider {
278
+ async handleMessage() { }
279
+ }
280
+ await service.registerFromProvider(new Provider());
281
+ await service.onModuleDestroy();
282
+ expect(mockRemoveListener).toHaveBeenCalledWith('message', expect.any(Function));
283
+ expect(service.getSubscriptionCount()).toBe(0);
284
+ });
285
+ it('handles removeListener errors during destroy', async () => {
286
+ mockGetConsumerMetadata.mockReturnValue({ ackOnSuccess: true });
287
+ mockGetSubscribeMetadata.mockReturnValue([
288
+ { methodName: 'handleMessage', options: { subscription: 'orders-sub' } },
289
+ ]);
290
+ class Provider {
291
+ async handleMessage() { }
292
+ }
293
+ await service.registerFromProvider(new Provider());
294
+ mockRemoveListener.mockImplementationOnce(() => {
295
+ throw new Error('remove failed');
296
+ });
297
+ await expect(service.onModuleDestroy()).resolves.toBeUndefined();
298
+ expect(service.getSubscriptionCount()).toBe(0);
299
+ });
300
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pubsub.decorator.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pubsub.decorator.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/pubsub.decorator.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const pubsub_consumer_decorator_1 = require("../decorators/pubsub-consumer.decorator");
13
+ const pubsub_subscribe_decorator_1 = require("../decorators/pubsub-subscribe.decorator");
14
+ describe('PubSubConsumer decorator', () => {
15
+ it('sets default consumer metadata', () => {
16
+ let TestConsumer = class TestConsumer {
17
+ };
18
+ TestConsumer = __decorate([
19
+ (0, pubsub_consumer_decorator_1.PubSubConsumer)()
20
+ ], TestConsumer);
21
+ const metadata = (0, pubsub_consumer_decorator_1.getPubSubConsumerMetadata)(TestConsumer.prototype);
22
+ expect(metadata).toBeDefined();
23
+ expect(metadata?.ackOnSuccess).toBe(true);
24
+ expect(metadata?.nackOnError).toBe(true);
25
+ expect(metadata?.parseJson).toBe(true);
26
+ });
27
+ it('sets custom consumer metadata', () => {
28
+ let CustomConsumer = class CustomConsumer {
29
+ };
30
+ CustomConsumer = __decorate([
31
+ (0, pubsub_consumer_decorator_1.PubSubConsumer)({ ackOnSuccess: false, parseJson: false })
32
+ ], CustomConsumer);
33
+ const metadata = (0, pubsub_consumer_decorator_1.getPubSubConsumerMetadata)(CustomConsumer.prototype);
34
+ expect(metadata?.ackOnSuccess).toBe(false);
35
+ expect(metadata?.parseJson).toBe(false);
36
+ });
37
+ it('identifies pubsub consumer classes', () => {
38
+ let ConsumerClass = class ConsumerClass {
39
+ };
40
+ ConsumerClass = __decorate([
41
+ (0, pubsub_consumer_decorator_1.PubSubConsumer)()
42
+ ], ConsumerClass);
43
+ expect((0, pubsub_consumer_decorator_1.isPubSubConsumer)(ConsumerClass.prototype)).toBe(true);
44
+ });
45
+ it('returns false for non-consumer classes', () => {
46
+ class RegularClass {
47
+ }
48
+ expect((0, pubsub_consumer_decorator_1.isPubSubConsumer)(RegularClass.prototype)).toBe(false);
49
+ });
50
+ });
51
+ describe('PubSubSubscribe decorator', () => {
52
+ it('stores subscription metadata on method', () => {
53
+ class TestConsumer {
54
+ handleOrders() { }
55
+ }
56
+ __decorate([
57
+ (0, pubsub_subscribe_decorator_1.PubSubSubscribe)({ subscription: 'orders-sub' }),
58
+ __metadata("design:type", Function),
59
+ __metadata("design:paramtypes", []),
60
+ __metadata("design:returntype", void 0)
61
+ ], TestConsumer.prototype, "handleOrders", null);
62
+ const metadata = (0, pubsub_subscribe_decorator_1.getPubSubSubscribeMetadata)(TestConsumer.prototype);
63
+ expect(metadata).toHaveLength(1);
64
+ expect(metadata[0].methodName).toBe('handleOrders');
65
+ expect(metadata[0].options.subscription).toBe('orders-sub');
66
+ });
67
+ it('supports multiple subscription handlers', () => {
68
+ class MultiConsumer {
69
+ handleA() { }
70
+ handleB() { }
71
+ }
72
+ __decorate([
73
+ (0, pubsub_subscribe_decorator_1.PubSubSubscribe)({ subscription: 'sub-a' }),
74
+ __metadata("design:type", Function),
75
+ __metadata("design:paramtypes", []),
76
+ __metadata("design:returntype", void 0)
77
+ ], MultiConsumer.prototype, "handleA", null);
78
+ __decorate([
79
+ (0, pubsub_subscribe_decorator_1.PubSubSubscribe)({ subscription: 'sub-b', parseJson: false }),
80
+ __metadata("design:type", Function),
81
+ __metadata("design:paramtypes", []),
82
+ __metadata("design:returntype", void 0)
83
+ ], MultiConsumer.prototype, "handleB", null);
84
+ const metadata = (0, pubsub_subscribe_decorator_1.getPubSubSubscribeMetadata)(MultiConsumer.prototype);
85
+ expect(metadata).toHaveLength(2);
86
+ expect(metadata[0].options.subscription).toBe('sub-a');
87
+ expect(metadata[1].options.subscription).toBe('sub-b');
88
+ expect(metadata[1].options.parseJson).toBe(false);
89
+ });
90
+ });
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=pubsub.module.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pubsub.module.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/pubsub.module.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@hazeljs/core");
4
+ const pubsub_module_1 = require("../pubsub.module");
5
+ const pubsub_publisher_service_1 = require("../pubsub-publisher.service");
6
+ describe('PubSubModule', () => {
7
+ describe('forRoot', () => {
8
+ it('returns PubSubModule and registers client', () => {
9
+ const result = pubsub_module_1.PubSubModule.forRoot({
10
+ projectId: 'test-project',
11
+ });
12
+ expect(result).toBe(pubsub_module_1.PubSubModule);
13
+ const container = core_1.Container.getInstance();
14
+ const client = container.resolve(pubsub_publisher_service_1.PUBSUB_CLIENT_TOKEN);
15
+ expect(client).toBeDefined();
16
+ expect(client?.topic).toBeDefined();
17
+ });
18
+ });
19
+ describe('forRootAsync', () => {
20
+ it('returns PubSubModule and registers client', async () => {
21
+ const result = await pubsub_module_1.PubSubModule.forRootAsync({
22
+ useFactory: () => Promise.resolve({
23
+ projectId: 'async-project',
24
+ }),
25
+ inject: [],
26
+ });
27
+ expect(result).toBe(pubsub_module_1.PubSubModule);
28
+ const container = core_1.Container.getInstance();
29
+ const client = container.resolve(pubsub_publisher_service_1.PUBSUB_CLIENT_TOKEN);
30
+ expect(client).toBeDefined();
31
+ });
32
+ });
33
+ });
@@ -0,0 +1,7 @@
1
+ import 'reflect-metadata';
2
+ import { PubSubConsumerOptions } from '../pubsub.types';
3
+ export declare const PUBSUB_CONSUMER_METADATA_KEY: unique symbol;
4
+ export declare function PubSubConsumer(options?: PubSubConsumerOptions): ClassDecorator;
5
+ export declare function getPubSubConsumerMetadata(target: object): PubSubConsumerOptions | undefined;
6
+ export declare function isPubSubConsumer(target: object): boolean;
7
+ //# sourceMappingURL=pubsub-consumer.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pubsub-consumer.decorator.d.ts","sourceRoot":"","sources":["../../src/decorators/pubsub-consumer.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,qBAAqB,EAAE,MAAM,iBAAiB,CAAC;AAGxD,eAAO,MAAM,4BAA4B,eAA4B,CAAC;AAEtE,wBAAgB,cAAc,CAAC,OAAO,GAAE,qBAA0B,GAAG,cAAc,CAalF;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,GAAG,qBAAqB,GAAG,SAAS,CAK3F;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAKxD"}
@@ -0,0 +1,37 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PUBSUB_CONSUMER_METADATA_KEY = void 0;
7
+ exports.PubSubConsumer = PubSubConsumer;
8
+ exports.getPubSubConsumerMetadata = getPubSubConsumerMetadata;
9
+ exports.isPubSubConsumer = isPubSubConsumer;
10
+ require("reflect-metadata");
11
+ const core_1 = __importDefault(require("@hazeljs/core"));
12
+ exports.PUBSUB_CONSUMER_METADATA_KEY = Symbol('pubsub:consumer');
13
+ function PubSubConsumer(options = {}) {
14
+ return (target) => {
15
+ const defaults = {
16
+ ackOnSuccess: options.ackOnSuccess ?? true,
17
+ nackOnError: options.nackOnError ?? true,
18
+ parseJson: options.parseJson ?? true,
19
+ autoCreateSubscription: options.autoCreateSubscription ?? false,
20
+ };
21
+ const targetName = typeof target === 'function' ? target.name : 'unknown';
22
+ core_1.default.debug(`Marking ${targetName} as Pub/Sub consumer`);
23
+ Reflect.defineMetadata(exports.PUBSUB_CONSUMER_METADATA_KEY, defaults, target);
24
+ };
25
+ }
26
+ function getPubSubConsumerMetadata(target) {
27
+ const constructor = typeof target === 'function' ? target : target.constructor;
28
+ if (!constructor)
29
+ return undefined;
30
+ return Reflect.getMetadata(exports.PUBSUB_CONSUMER_METADATA_KEY, constructor);
31
+ }
32
+ function isPubSubConsumer(target) {
33
+ const constructor = typeof target === 'function' ? target : target.constructor;
34
+ if (!constructor)
35
+ return false;
36
+ return Reflect.hasMetadata(exports.PUBSUB_CONSUMER_METADATA_KEY, constructor);
37
+ }
@@ -0,0 +1,6 @@
1
+ import 'reflect-metadata';
2
+ import { PubSubSubscribeMetadata, PubSubSubscribeOptions } from '../pubsub.types';
3
+ export declare const PUBSUB_SUBSCRIBE_METADATA_KEY: unique symbol;
4
+ export declare function PubSubSubscribe(options: PubSubSubscribeOptions): MethodDecorator;
5
+ export declare function getPubSubSubscribeMetadata(target: object): PubSubSubscribeMetadata[];
6
+ //# sourceMappingURL=pubsub-subscribe.decorator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pubsub-subscribe.decorator.d.ts","sourceRoot":"","sources":["../../src/decorators/pubsub-subscribe.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,uBAAuB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAGlF,eAAO,MAAM,6BAA6B,eAA6B,CAAC;AAExE,wBAAgB,eAAe,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CAqBhF;AAED,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,MAAM,GAAG,uBAAuB,EAAE,CAKpF"}
@@ -0,0 +1,29 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.PUBSUB_SUBSCRIBE_METADATA_KEY = void 0;
7
+ exports.PubSubSubscribe = PubSubSubscribe;
8
+ exports.getPubSubSubscribeMetadata = getPubSubSubscribeMetadata;
9
+ require("reflect-metadata");
10
+ const core_1 = __importDefault(require("@hazeljs/core"));
11
+ exports.PUBSUB_SUBSCRIBE_METADATA_KEY = Symbol('pubsub:subscribe');
12
+ function PubSubSubscribe(options) {
13
+ return (target, propertyKey, _descriptor) => {
14
+ const existingSubscriptions = Reflect.getMetadata(exports.PUBSUB_SUBSCRIBE_METADATA_KEY, target.constructor) || [];
15
+ const subscription = {
16
+ methodName: propertyKey.toString(),
17
+ options,
18
+ };
19
+ existingSubscriptions.push(subscription);
20
+ Reflect.defineMetadata(exports.PUBSUB_SUBSCRIBE_METADATA_KEY, existingSubscriptions, target.constructor);
21
+ core_1.default.debug(`PubSubSubscribe applied to ${target.constructor.name}.${String(propertyKey)} for subscription: ${options.subscription}`);
22
+ };
23
+ }
24
+ function getPubSubSubscribeMetadata(target) {
25
+ const constructor = typeof target === 'function' ? target : target.constructor;
26
+ if (!constructor)
27
+ return [];
28
+ return Reflect.getMetadata(exports.PUBSUB_SUBSCRIBE_METADATA_KEY, constructor) || [];
29
+ }
@@ -0,0 +1,10 @@
1
+ /**
2
+ * @hazeljs/pubsub - Google Cloud Pub/Sub module for HazelJS
3
+ */
4
+ export { PubSubModule } from './pubsub.module';
5
+ export { PubSubPublisherService, PUBSUB_CLIENT_TOKEN } from './pubsub-publisher.service';
6
+ export { PubSubSubscriberService } from './pubsub-subscriber.service';
7
+ export { PubSubConsumer, getPubSubConsumerMetadata, isPubSubConsumer, } from './decorators/pubsub-consumer.decorator';
8
+ export { PubSubSubscribe, getPubSubSubscribeMetadata, } from './decorators/pubsub-subscribe.decorator';
9
+ export type { PubSubClientOptions, PubSubModuleOptions, PubSubPublishOptions, PubSubSubscribeOptions, PubSubConsumerOptions, PubSubSubscribeMetadata, PubSubSubscriptionHandlerPayload, PubSubHandlerResult, } from './pubsub.types';
10
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AAC/C,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACzF,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC;AACtE,OAAO,EACL,cAAc,EACd,yBAAyB,EACzB,gBAAgB,GACjB,MAAM,wCAAwC,CAAC;AAChD,OAAO,EACL,eAAe,EACf,0BAA0B,GAC3B,MAAM,yCAAyC,CAAC;AACjD,YAAY,EACV,mBAAmB,EACnB,mBAAmB,EACnB,oBAAoB,EACpB,sBAAsB,EACtB,qBAAqB,EACrB,uBAAuB,EACvB,gCAAgC,EAChC,mBAAmB,GACpB,MAAM,gBAAgB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /**
3
+ * @hazeljs/pubsub - Google Cloud Pub/Sub module for HazelJS
4
+ */
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getPubSubSubscribeMetadata = exports.PubSubSubscribe = exports.isPubSubConsumer = exports.getPubSubConsumerMetadata = exports.PubSubConsumer = exports.PubSubSubscriberService = exports.PUBSUB_CLIENT_TOKEN = exports.PubSubPublisherService = exports.PubSubModule = void 0;
7
+ var pubsub_module_1 = require("./pubsub.module");
8
+ Object.defineProperty(exports, "PubSubModule", { enumerable: true, get: function () { return pubsub_module_1.PubSubModule; } });
9
+ var pubsub_publisher_service_1 = require("./pubsub-publisher.service");
10
+ Object.defineProperty(exports, "PubSubPublisherService", { enumerable: true, get: function () { return pubsub_publisher_service_1.PubSubPublisherService; } });
11
+ Object.defineProperty(exports, "PUBSUB_CLIENT_TOKEN", { enumerable: true, get: function () { return pubsub_publisher_service_1.PUBSUB_CLIENT_TOKEN; } });
12
+ var pubsub_subscriber_service_1 = require("./pubsub-subscriber.service");
13
+ Object.defineProperty(exports, "PubSubSubscriberService", { enumerable: true, get: function () { return pubsub_subscriber_service_1.PubSubSubscriberService; } });
14
+ var pubsub_consumer_decorator_1 = require("./decorators/pubsub-consumer.decorator");
15
+ Object.defineProperty(exports, "PubSubConsumer", { enumerable: true, get: function () { return pubsub_consumer_decorator_1.PubSubConsumer; } });
16
+ Object.defineProperty(exports, "getPubSubConsumerMetadata", { enumerable: true, get: function () { return pubsub_consumer_decorator_1.getPubSubConsumerMetadata; } });
17
+ Object.defineProperty(exports, "isPubSubConsumer", { enumerable: true, get: function () { return pubsub_consumer_decorator_1.isPubSubConsumer; } });
18
+ var pubsub_subscribe_decorator_1 = require("./decorators/pubsub-subscribe.decorator");
19
+ Object.defineProperty(exports, "PubSubSubscribe", { enumerable: true, get: function () { return pubsub_subscribe_decorator_1.PubSubSubscribe; } });
20
+ Object.defineProperty(exports, "getPubSubSubscribeMetadata", { enumerable: true, get: function () { return pubsub_subscribe_decorator_1.getPubSubSubscribeMetadata; } });
@@ -0,0 +1,13 @@
1
+ import { PubSub } from '@google-cloud/pubsub';
2
+ import { PubSubPublishOptions } from './pubsub.types';
3
+ export declare const PUBSUB_CLIENT_TOKEN = "PUBSUB_CLIENT";
4
+ export declare class PubSubPublisherService {
5
+ private readonly pubsub;
6
+ constructor(pubsub: PubSub);
7
+ publish(topicName: string, data: string | Buffer | object, options?: PubSubPublishOptions): Promise<string>;
8
+ publishJson<T extends object>(topicName: string, data: T, options?: Omit<PubSubPublishOptions, 'attributes'> & {
9
+ attributes?: Record<string, string>;
10
+ }): Promise<string>;
11
+ private toBuffer;
12
+ }
13
+ //# sourceMappingURL=pubsub-publisher.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pubsub-publisher.service.d.ts","sourceRoot":"","sources":["../src/pubsub-publisher.service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,sBAAsB,CAAC;AAC9C,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAGtD,eAAO,MAAM,mBAAmB,kBAAkB,CAAC;AAEnD,qBACa,sBAAsB;IAG/B,OAAO,CAAC,QAAQ,CAAC,MAAM;gBAAN,MAAM,EAAE,MAAM;IAG3B,OAAO,CACX,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAC9B,OAAO,GAAE,oBAAyB,GACjC,OAAO,CAAC,MAAM,CAAC;IAYZ,WAAW,CAAC,CAAC,SAAS,MAAM,EAChC,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,CAAC,EACP,OAAO,GAAE,IAAI,CAAC,oBAAoB,EAAE,YAAY,CAAC,GAAG;QAAE,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;KAAO,GAC/F,OAAO,CAAC,MAAM,CAAC;IAUlB,OAAO,CAAC,QAAQ;CAKjB"}
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
3
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
4
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
5
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
7
+ };
8
+ var __metadata = (this && this.__metadata) || function (k, v) {
9
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
10
+ };
11
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
12
+ return function (target, key) { decorator(target, key, paramIndex); }
13
+ };
14
+ var __importDefault = (this && this.__importDefault) || function (mod) {
15
+ return (mod && mod.__esModule) ? mod : { "default": mod };
16
+ };
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.PubSubPublisherService = exports.PUBSUB_CLIENT_TOKEN = void 0;
19
+ const core_1 = require("@hazeljs/core");
20
+ const pubsub_1 = require("@google-cloud/pubsub");
21
+ const core_2 = __importDefault(require("@hazeljs/core"));
22
+ exports.PUBSUB_CLIENT_TOKEN = 'PUBSUB_CLIENT';
23
+ let PubSubPublisherService = class PubSubPublisherService {
24
+ constructor(pubsub) {
25
+ this.pubsub = pubsub;
26
+ }
27
+ async publish(topicName, data, options = {}) {
28
+ const topic = this.pubsub.topic(topicName);
29
+ const payload = this.toBuffer(data);
30
+ const messageId = await topic.publishMessage({
31
+ data: payload,
32
+ attributes: options.attributes,
33
+ orderingKey: options.orderingKey,
34
+ });
35
+ core_2.default.debug(`Published Pub/Sub message to topic "${topicName}" (id: ${messageId})`);
36
+ return messageId;
37
+ }
38
+ async publishJson(topicName, data, options = {}) {
39
+ return this.publish(topicName, data, {
40
+ ...options,
41
+ attributes: {
42
+ 'content-type': 'application/json',
43
+ ...(options.attributes ?? {}),
44
+ },
45
+ });
46
+ }
47
+ toBuffer(data) {
48
+ if (Buffer.isBuffer(data))
49
+ return data;
50
+ if (typeof data === 'string')
51
+ return Buffer.from(data);
52
+ return Buffer.from(JSON.stringify(data));
53
+ }
54
+ };
55
+ exports.PubSubPublisherService = PubSubPublisherService;
56
+ exports.PubSubPublisherService = PubSubPublisherService = __decorate([
57
+ (0, core_1.Service)(),
58
+ __param(0, (0, core_1.Inject)(exports.PUBSUB_CLIENT_TOKEN)),
59
+ __metadata("design:paramtypes", [pubsub_1.PubSub])
60
+ ], PubSubPublisherService);