@palmetto/pubsub 1.0.0 → 1.0.2

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.
@@ -9,5 +9,6 @@ export declare class BullMqPublisher implements PublisherProvider {
9
9
  readonly transport: string;
10
10
  private getQueue;
11
11
  publish(config: BullMqQueueConfiguration, message: string): Promise<void>;
12
+ init(config: BullMqQueueConfiguration): Promise<void>;
12
13
  close(): Promise<void>;
13
14
  }
@@ -50,6 +50,11 @@ class BullMqPublisher {
50
50
  });
51
51
  });
52
52
  }
53
+ init(config) {
54
+ return __awaiter(this, void 0, void 0, function* () {
55
+ yield this.getQueue(config);
56
+ });
57
+ }
53
58
  close() {
54
59
  return __awaiter(this, void 0, void 0, function* () {
55
60
  for (const queue of this.queues.values()) {
@@ -9,4 +9,5 @@ export declare class BullMqPubSubProvider implements PubSubProvider {
9
9
  publish(config: BullMqQueueConfiguration, message: string): Promise<void>;
10
10
  startSubscribe(config: BullMqQueueConfiguration, onMessage: (s: string, context: MessageContext) => Promise<MessageResult> | MessageResult): Promise<StopSubscribe>;
11
11
  close(): Promise<void>;
12
+ init(config: BullMqQueueConfiguration): Promise<void>;
12
13
  }
@@ -31,5 +31,10 @@ class BullMqPubSubProvider {
31
31
  yield this.publisher.close();
32
32
  });
33
33
  }
34
+ init(config) {
35
+ return __awaiter(this, void 0, void 0, function* () {
36
+ yield this.publisher.init(config);
37
+ });
38
+ }
34
39
  }
35
40
  exports.BullMqPubSubProvider = BullMqPubSubProvider;
@@ -22,6 +22,7 @@ export type BaseMessage = z4.infer<typeof IdMetaSchema>;
22
22
  */
23
23
  export interface PublisherProvider {
24
24
  transport: string;
25
+ init(config: PubSubConfiguration): Promise<void> | void;
25
26
  publish(config: PubSubConfiguration, message: string): Promise<void> | void;
26
27
  close(): Promise<void> | void;
27
28
  }
@@ -6,6 +6,9 @@ export declare class Publisher {
6
6
  constructor(logger: Logger, providers?: PublisherProvider[]);
7
7
  addProvider(provider: PublisherProvider): void;
8
8
  removeProvider(providerOrTransport: PublisherProvider | string): boolean;
9
+ init(config: PubSubConfiguration): Promise<void>;
9
10
  publish(config: PubSubConfiguration, message: BaseMessage): Promise<void>;
10
11
  close(): Promise<void>;
12
+ private getProvider;
13
+ private assertSchema;
11
14
  }
package/dist/publisher.js CHANGED
@@ -40,39 +40,36 @@ class Publisher {
40
40
  (_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, `Publisher removing provider for ${transport}`);
41
41
  return this.publisherProviders.delete(transport);
42
42
  }
43
+ init(config) {
44
+ return __awaiter(this, void 0, void 0, function* () {
45
+ yield this.getProvider(config).init(config);
46
+ yield this.assertSchema(config);
47
+ });
48
+ }
43
49
  publish(config, message) {
44
50
  return __awaiter(this, void 0, void 0, function* () {
45
51
  var _a, _b;
46
- const { transport, schema } = config;
47
- const provider = this.publisherProviders.get(transport);
48
- if (!provider) {
49
- throw new errors_1.MissingPubSubProviderError(`No provider configured for ${transport}`);
50
- }
52
+ const provider = this.getProvider(config);
51
53
  if (!message.id) {
52
54
  message.id = (0, uuid_1.v4)();
53
55
  }
54
- let hash = this.hashes.get(schema);
55
- if (!hash) {
56
- const jsonSchema = JSON.stringify(v4_1.z.toJSONSchema(schema), null, 3);
57
- hash = yield (0, crypto_hash_1.sha256)(jsonSchema);
58
- this.hashes.set(schema, hash);
59
- }
56
+ const { schema, schemaId } = yield this.assertSchema(config);
60
57
  if (!message.meta) {
61
58
  message.meta = {
62
59
  createdAt: new Date().toISOString(),
63
60
  publishedBy: "",
64
- schemaId: hash,
61
+ schemaId: schemaId,
65
62
  };
66
63
  }
67
64
  else {
68
- message.meta.schemaId = hash;
65
+ message.meta.schemaId = schemaId;
69
66
  }
70
67
  const json = JSON.stringify(message);
71
68
  const check = schema.safeParse(JSON.parse(json));
72
69
  if (!check.success) {
73
70
  throw new errors_1.SchemaValidationError(`Schema did not accept the published message: ${check.error.message}`);
74
71
  }
75
- (_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, `Publisher publishing message for ${transport}`);
72
+ (_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, `Publisher publishing message for ${provider.transport}`);
76
73
  yield provider.publish(config, json);
77
74
  });
78
75
  }
@@ -83,5 +80,28 @@ class Publisher {
83
80
  }
84
81
  });
85
82
  }
83
+ getProvider(config) {
84
+ const { transport } = config;
85
+ const provider = this.publisherProviders.get(transport);
86
+ if (!provider) {
87
+ throw new errors_1.MissingPubSubProviderError(`No provider configured for ${transport}`);
88
+ }
89
+ return provider;
90
+ }
91
+ assertSchema(config) {
92
+ return __awaiter(this, void 0, void 0, function* () {
93
+ const { schema } = config;
94
+ let hash = this.hashes.get(schema);
95
+ if (!hash) {
96
+ const jsonSchema = JSON.stringify(v4_1.z.toJSONSchema(schema), null, 3);
97
+ hash = yield (0, crypto_hash_1.sha256)(jsonSchema);
98
+ this.hashes.set(schema, hash);
99
+ }
100
+ return {
101
+ schema,
102
+ schemaId: hash,
103
+ };
104
+ });
105
+ }
86
106
  }
87
107
  exports.Publisher = Publisher;
@@ -18,6 +18,7 @@ export declare class RabbitMqConnection {
18
18
  static create(config: RabbitMqConnectionConfig, logger: Logger): Promise<RabbitMqConnection>;
19
19
  private constructor();
20
20
  close(): Promise<void>;
21
+ isConnected(): boolean;
21
22
  /**
22
23
  * Creates the exchanges necessary for the given config
23
24
  * @param channel
@@ -46,6 +46,9 @@ class RabbitMqConnection {
46
46
  yield this.connection.close();
47
47
  });
48
48
  }
49
+ isConnected() {
50
+ return this.connection.isConnected();
51
+ }
49
52
  /**
50
53
  * Creates the exchanges necessary for the given config
51
54
  * @param channel
@@ -1,4 +1,3 @@
1
- import type { ChannelWrapper } from "amqp-connection-manager";
2
1
  import { Logger, PublisherProvider } from "../interfaces";
3
2
  import { RabbitMqConnection } from "./connection";
4
3
  import { RabbitQueueExchangeConfiguration } from "./config";
@@ -10,13 +9,14 @@ export declare class RabbitMqPublisher implements PublisherProvider {
10
9
  private connected;
11
10
  constructor(connection: RabbitMqConnection, logger: Logger);
12
11
  readonly transport: string;
12
+ init(config: RabbitQueueExchangeConfiguration): Promise<void>;
13
13
  /**
14
14
  * Initializes the rabbit connection and asserts the exchange for the configuration.
15
15
  *
16
16
  * @param config
17
17
  * @returns a Promise containing the ChannelWrapper to publish messages with
18
18
  */
19
- init(config: RabbitQueueExchangeConfiguration): Promise<ChannelWrapper>;
19
+ private getChannel;
20
20
  /**
21
21
  * Publishes a message to Rabbit based on the configuration
22
22
  *
@@ -22,13 +22,18 @@ class RabbitMqPublisher {
22
22
  this.connected = false;
23
23
  this.transport = connection_1.RABBITMQ_TRANSPORT;
24
24
  }
25
+ init(config) {
26
+ return __awaiter(this, void 0, void 0, function* () {
27
+ yield this.getChannel(config);
28
+ });
29
+ }
25
30
  /**
26
31
  * Initializes the rabbit connection and asserts the exchange for the configuration.
27
32
  *
28
33
  * @param config
29
34
  * @returns a Promise containing the ChannelWrapper to publish messages with
30
35
  */
31
- init(config) {
36
+ getChannel(config) {
32
37
  return __awaiter(this, void 0, void 0, function* () {
33
38
  var _a, _b, _c, _d;
34
39
  const delayMs = this.connection.config.startupRetryDelayMs || 500;
@@ -76,7 +81,7 @@ class RabbitMqPublisher {
76
81
  publish(config, message) {
77
82
  return __awaiter(this, void 0, void 0, function* () {
78
83
  var _a, _b, _c, _d;
79
- const channel = yield this.init(config);
84
+ const channel = yield this.getChannel(config);
80
85
  (_b = (_a = this.logger).debug) === null || _b === void 0 ? void 0 : _b.call(_a, `Publishing message to ${(0, config_1.getExchangeName)(config)} - ${message} [starting]`);
81
86
  const exchangeName = (0, config_1.getExchangeName)(config);
82
87
  const ok = yield channel.publish(exchangeName, (0, config_1.getRoutingKey)(config), Buffer.from(message, "utf8"), {
@@ -12,4 +12,5 @@ export declare class RabbitMqPubSubProvider implements PubSubProvider {
12
12
  publish(config: RabbitQueueExchangeConfiguration, message: string): Promise<void>;
13
13
  startSubscribe(config: RabbitQueueExchangeConfiguration, onMessage: (s: string, context: MessageContext) => Promise<MessageResult> | MessageResult): StopSubscribe;
14
14
  close(): Promise<void>;
15
+ init(config: RabbitQueueExchangeConfiguration): Promise<void>;
15
16
  }
@@ -33,5 +33,10 @@ class RabbitMqPubSubProvider {
33
33
  yield Promise.all([this.subscriber.close(), this.publisher.close()]);
34
34
  });
35
35
  }
36
+ init(config) {
37
+ return __awaiter(this, void 0, void 0, function* () {
38
+ yield this.publisher.init(config);
39
+ });
40
+ }
36
41
  }
37
42
  exports.RabbitMqPubSubProvider = RabbitMqPubSubProvider;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@palmetto/pubsub",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "main": "./dist/main.js",
5
5
  "scripts": {
6
6
  "lint": "yarn run -T eslint --fix ./src",
@@ -15,10 +15,9 @@
15
15
  "hook:format": "prettier --write --loglevel warn",
16
16
  "hook:tc": "yarn tc",
17
17
  "prepublishOnly": "yarn build",
18
- "test": "vitest run",
19
- "test:watch": "vitest watch",
20
- "redis-up": "docker compose -f redis-compose.yml up -d",
21
- "rabbitmq-up": "docker compose -f rabbitmq-compose.yml up -d"
18
+ "test-runner": "../../scripts/test-runner.sh",
19
+ "test": "yarn run test-runner vitest run",
20
+ "test:watch": "yarn run test-runner vitest watch"
22
21
  },
23
22
  "devDependencies": {
24
23
  "@types/amqplib": "^0",
@@ -52,7 +52,15 @@ yarn add @palmetto/pubsub amqp-connection-manager amqplib zod
52
52
  };
53
53
  ```
54
54
 
55
- 6. Publish a message
55
+ 6. Initialize the publisher
56
+
57
+ For performance reasons, it's a good idea to initialize the publisher on your API startup. This can reduce the latency for the first message published.
58
+
59
+ ```ts
60
+ await publisher.init(config);
61
+ ```
62
+
63
+ 7. Publish a message
56
64
 
57
65
  ```ts
58
66
  const message: MyModel = {... };
@@ -60,7 +68,7 @@ yarn add @palmetto/pubsub amqp-connection-manager amqplib zod
60
68
  await publisher.publish(config, message);
61
69
  ```
62
70
 
63
- 7. Subscribe to a queue
71
+ 8. Subscribe to a queue
64
72
 
65
73
  ```ts
66
74
  subscriber.addSubscriber(queue, (message: MyModel, context: RabbitMqMessageContext) => {
@@ -69,7 +77,7 @@ yarn add @palmetto/pubsub amqp-connection-manager amqplib zod
69
77
  });
70
78
  ```
71
79
 
72
- 8. Subscribe to the dead-letter queue if you want to re-process completely failed messages. Set the `queueType` to `"dead-letter"`
80
+ 9. Subscribe to the dead-letter queue if you want to re-process completely failed messages. Set the `queueType` to `"dead-letter"`
73
81
 
74
82
  ```ts
75
83
  const dlQueue = {