@theshelf/notification 0.3.2 → 0.4.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.
package/README.md CHANGED
@@ -1,9 +1,7 @@
1
1
 
2
- # Notification | The Shelf
2
+ # Notification core | The Shelf
3
3
 
4
- The notification package provides a universal interaction layer with an actual notification solution.
5
-
6
- This package is based on a push notification model.
4
+ This package contains the core implementation for the notification system.
7
5
 
8
6
  ## Installation
9
7
 
@@ -11,43 +9,20 @@ This package is based on a push notification model.
11
9
  npm install @theshelf/notification
12
10
  ```
13
11
 
14
- ## Drivers
15
-
16
- Currently, there are two drivers available:
17
-
18
- * **Memory** - non-persistent in memory notifications (suited for testing).
19
- * **WebPush** - web browser based push notifications.
20
-
21
12
  ## How to use
22
13
 
23
14
  The basic set up looks like this.
24
15
 
25
16
  ```ts
26
- import NotificationService, { MemoryDriver | WebPushDriver as SelectedDriver } from '@theshelf/notification';
17
+ import NotificationService, { MemoryDriver } from '@theshelf/notification';
27
18
 
28
- const driver = new SelectedDriver(/* configuration */);
19
+ const driver = new MemoryDriver();
29
20
  const notificationService = new NotificationService(driver);
30
21
 
31
22
  // Perform operations with the notificationService instance
32
23
  ```
33
24
 
34
- ### Configuration
35
-
36
- #### Memory driver
37
-
38
- No configuration options.
39
-
40
- #### WebPush driver
41
-
42
- ```ts
43
- type WebPushConfiguration = { // Vapid details
44
- subject: string;
45
- publicKey: string;
46
- privateKey: string;
47
- };
48
- ```
49
-
50
- ### Operations
25
+ ## Operations
51
26
 
52
27
  ```ts
53
28
  // Open connection
@@ -67,3 +42,16 @@ await notificationService.unsubscribe(recipientId);
67
42
  // Throws SubscriptionNotFound if subscription not found.
68
43
  await notificationService.sendNotification(recipientId, title, body);
69
44
  ```
45
+
46
+ ## Drivers
47
+
48
+ There is one driver included in this package. Other drivers are available in separate packages.
49
+
50
+ ### Memory
51
+
52
+ This driver is a non-persistent in memory implementation (suitable for testing purposes). It doesn't have any configuration options, but has an additional operation.
53
+
54
+ ```ts
55
+ // Clear the subscriptions
56
+ driver.clear();
57
+ ```
@@ -1,7 +1,8 @@
1
+ import type Logger from '@theshelf/logging';
1
2
  import type { Driver } from './definitions/interfaces.js';
2
- export default class NotificationService implements Driver {
3
+ export default class NotificationService {
3
4
  #private;
4
- constructor(driver: Driver);
5
+ constructor(driver: Driver, logger?: Logger);
5
6
  get connected(): boolean;
6
7
  get subscriptions(): Map<string, unknown>;
7
8
  connect(): Promise<void>;
@@ -1,7 +1,12 @@
1
+ import NotConnected from './errors/NotConnected.js';
1
2
  export default class NotificationService {
2
3
  #driver;
3
- constructor(driver) {
4
+ #logger;
5
+ #logPrefix;
6
+ constructor(driver, logger) {
4
7
  this.#driver = driver;
8
+ this.#logger = logger?.for(NotificationService.name);
9
+ this.#logPrefix = `${this.#driver.name} ->`;
5
10
  }
6
11
  get connected() {
7
12
  return this.#driver.connected;
@@ -9,19 +14,68 @@ export default class NotificationService {
9
14
  get subscriptions() {
10
15
  return new Map(this.#driver.subscriptions);
11
16
  }
12
- connect() {
13
- return this.#driver.connect();
17
+ async connect() {
18
+ if (this.connected === true) {
19
+ return;
20
+ }
21
+ this.#logger?.debug(this.#logPrefix, 'Connecting');
22
+ try {
23
+ await this.#driver.connect();
24
+ }
25
+ catch (error) {
26
+ this.#logger?.error(this.#logPrefix, 'Connect failed with error', error);
27
+ throw error;
28
+ }
14
29
  }
15
- disconnect() {
16
- return this.#driver.disconnect();
30
+ async disconnect() {
31
+ if (this.connected === false) {
32
+ return;
33
+ }
34
+ this.#logger?.debug(this.#logPrefix, 'Disconnecting');
35
+ try {
36
+ return await this.#driver.disconnect();
37
+ }
38
+ catch (error) {
39
+ this.#logger?.error(this.#logPrefix, 'Disconnect failed with error', error);
40
+ throw error;
41
+ }
17
42
  }
18
- subscribe(recipientId, subscription) {
19
- return this.#driver.subscribe(recipientId, subscription);
43
+ async subscribe(recipientId, subscription) {
44
+ this.#logger?.debug(this.#logPrefix, 'Subscribing with id', recipientId);
45
+ try {
46
+ this.#validateConnection();
47
+ return await this.#driver.subscribe(recipientId, subscription);
48
+ }
49
+ catch (error) {
50
+ this.#logger?.error(this.#logPrefix, 'Subscribe with id', recipientId, 'failed with error', error);
51
+ throw error;
52
+ }
20
53
  }
21
- unsubscribe(recipientId) {
22
- return this.#driver.unsubscribe(recipientId);
54
+ async unsubscribe(recipientId) {
55
+ this.#logger?.debug(this.#logPrefix, 'Unsubscribing with id', recipientId);
56
+ try {
57
+ this.#validateConnection();
58
+ return await this.#driver.unsubscribe(recipientId);
59
+ }
60
+ catch (error) {
61
+ this.#logger?.error(this.#logPrefix, 'Unsubscribe with id', recipientId, 'failed with error', error);
62
+ throw error;
63
+ }
23
64
  }
24
- sendNotification(recipientId, title, message) {
25
- return this.#driver.sendNotification(recipientId, title, message);
65
+ async sendNotification(recipientId, title, message) {
66
+ this.#logger?.debug(this.#logPrefix, 'Sending notification to id', recipientId);
67
+ try {
68
+ this.#validateConnection();
69
+ return await this.#driver.sendNotification(recipientId, title, message);
70
+ }
71
+ catch (error) {
72
+ this.#logger?.error(this.#logPrefix, 'Send notification to id', recipientId, 'failed with error', error);
73
+ throw error;
74
+ }
75
+ }
76
+ #validateConnection() {
77
+ if (this.connected === false) {
78
+ throw new NotConnected();
79
+ }
26
80
  }
27
81
  }
@@ -1,4 +1,5 @@
1
1
  export interface Driver {
2
+ get name(): string;
2
3
  get connected(): boolean;
3
4
  get subscriptions(): Map<string, unknown>;
4
5
  connect(): Promise<void>;
@@ -5,6 +5,7 @@ type Notification = {
5
5
  };
6
6
  export default class Memory implements Driver {
7
7
  #private;
8
+ get name(): string;
8
9
  get connected(): boolean;
9
10
  get subscriptions(): Map<string, Notification[]>;
10
11
  connect(): Promise<void>;
@@ -3,6 +3,7 @@ import SubscriptionNotFound from '../errors/SubscriptionNotFound.js';
3
3
  export default class Memory {
4
4
  #subscriptions = new Map();
5
5
  #connected = false;
6
+ get name() { return Memory.name; }
6
7
  get connected() { return this.#connected; }
7
8
  get subscriptions() {
8
9
  if (this.#connected === false) {
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type * from './definitions/interfaces.js';
1
+ export type { Driver } from './definitions/interfaces.js';
2
2
  export { default as NotConnected } from './errors/NotConnected.js';
3
3
  export { default as NotificationError } from './errors/NotificationError.js';
4
4
  export { default as SubscriptionNotFound } from './errors/SubscriptionNotFound.js';
package/package.json CHANGED
@@ -1,11 +1,12 @@
1
1
  {
2
2
  "name": "@theshelf/notification",
3
3
  "private": false,
4
- "version": "0.3.2",
4
+ "version": "0.4.0",
5
5
  "type": "module",
6
6
  "repository": {
7
7
  "url": "git+https://github.com/MaskingTechnology/theshelf.git"
8
8
  },
9
+ "license": "MIT",
9
10
  "scripts": {
10
11
  "build": "tsc",
11
12
  "clean": "rimraf dist",
@@ -19,9 +20,9 @@
19
20
  "README.md",
20
21
  "dist"
21
22
  ],
22
- "types": "dist/index.d.ts",
23
+ "types": "./dist/index.d.ts",
23
24
  "exports": "./dist/index.js",
24
- "dependencies": {
25
- "web-push": "3.6.7"
25
+ "peerDependencies": {
26
+ "@theshelf/logging": "^0.4.0"
26
27
  }
27
28
  }
@@ -1,19 +0,0 @@
1
- import type { PushSubscription } from 'web-push';
2
- import type { Driver } from '../definitions/interfaces.js';
3
- type WebPushConfiguration = {
4
- subject: string;
5
- publicKey: string;
6
- privateKey: string;
7
- };
8
- export default class WebPush implements Driver {
9
- #private;
10
- constructor(configuration: WebPushConfiguration);
11
- get connected(): boolean;
12
- get subscriptions(): Map<string, PushSubscription>;
13
- connect(): Promise<void>;
14
- disconnect(): Promise<void>;
15
- subscribe(recipientId: string, subscription: PushSubscription): Promise<void>;
16
- unsubscribe(recipientId: string): Promise<void>;
17
- sendNotification(recipientId: string, title: string, body: string): Promise<void>;
18
- }
19
- export {};
@@ -1,52 +0,0 @@
1
- import webpush from 'web-push';
2
- import NotConnected from '../errors/NotConnected.js';
3
- import SubscriptionNotFound from '../errors/SubscriptionNotFound.js';
4
- export default class WebPush {
5
- #configuration;
6
- #subscriptions;
7
- constructor(configuration) {
8
- this.#configuration = configuration;
9
- }
10
- get connected() {
11
- return this.#subscriptions !== undefined;
12
- }
13
- get subscriptions() {
14
- return this.#getSubscriptions();
15
- }
16
- async connect() {
17
- this.#subscriptions = new Map();
18
- webpush.setVapidDetails(this.#configuration.subject, this.#configuration.publicKey, this.#configuration.privateKey);
19
- }
20
- async disconnect() {
21
- this.#subscriptions = undefined;
22
- }
23
- async subscribe(recipientId, subscription) {
24
- const subscriptions = this.#getSubscriptions();
25
- subscriptions.set(recipientId, subscription);
26
- }
27
- async unsubscribe(recipientId) {
28
- const subscriptions = this.#getSubscriptions();
29
- if (subscriptions.has(recipientId) === false) {
30
- throw new SubscriptionNotFound(recipientId);
31
- }
32
- subscriptions.delete(recipientId);
33
- }
34
- async sendNotification(recipientId, title, body) {
35
- const subscription = this.#getSubscription(recipientId);
36
- await webpush.sendNotification(subscription, JSON.stringify({ title, body }));
37
- }
38
- #getSubscriptions() {
39
- if (this.#subscriptions === undefined) {
40
- throw new NotConnected();
41
- }
42
- return this.#subscriptions;
43
- }
44
- #getSubscription(recipientId) {
45
- const subscriptions = this.#getSubscriptions();
46
- const subscription = subscriptions.get(recipientId);
47
- if (subscription === undefined) {
48
- throw new SubscriptionNotFound(recipientId);
49
- }
50
- return subscription;
51
- }
52
- }