@flowcore/sdk 1.10.2 → 1.11.1

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.
@@ -0,0 +1,80 @@
1
+ import type { Subject } from "rxjs";
2
+ import { type Logger } from "../utils/logger.js";
3
+ /**
4
+ * Represents an event notification from the Flowcore system
5
+ */
6
+ export type NotificationEvent = {
7
+ pattern: string;
8
+ data: {
9
+ tenant: string;
10
+ eventId: string;
11
+ dataCoreId: string;
12
+ flowType: string;
13
+ eventType: string;
14
+ validTime: string;
15
+ };
16
+ };
17
+ /**
18
+ * Interface for OIDC authentication client
19
+ */
20
+ export type OidcClient = {
21
+ getToken: () => Promise<{
22
+ accessToken: string;
23
+ }>;
24
+ };
25
+ /**
26
+ * Configuration options for the NotificationClient
27
+ */
28
+ export type NotificationClientOptions = {
29
+ reconnectInterval: number;
30
+ maxReconnects?: number;
31
+ maxEvents?: number;
32
+ logger?: Logger;
33
+ };
34
+ /**
35
+ * Client for handling WebSocket connections to the Flowcore notification system.
36
+ * Manages connection lifecycle, authentication, and event handling.
37
+ */
38
+ export declare class NotificationClient {
39
+ private readonly observer;
40
+ private readonly oidcClient;
41
+ private readonly subscriptionSpec;
42
+ private url;
43
+ private webSocket;
44
+ private options;
45
+ private logger;
46
+ private eventCount;
47
+ private reconnectInterval;
48
+ private reconnectAttempts;
49
+ /**
50
+ * Creates a new NotificationClient instance
51
+ * @param observer - RxJS Subject for emitting notification events
52
+ * @param oidcClient - Client for handling OIDC authentication
53
+ * @param subscriptionSpec - Specification for what notifications to subscribe to
54
+ * @param options - Configuration options for the client
55
+ */
56
+ constructor(observer: Subject<NotificationEvent>, oidcClient: OidcClient, subscriptionSpec: {
57
+ tenant: string;
58
+ dataCore: string;
59
+ flowType?: string;
60
+ eventType?: string;
61
+ }, options?: Partial<NotificationClientOptions>);
62
+ /**
63
+ * Establishes WebSocket connection and sets up event handlers
64
+ */
65
+ connect(): Promise<void>;
66
+ /**
67
+ * Attempts to reconnect to the WebSocket server using exponential backoff
68
+ */
69
+ private attemptReconnect;
70
+ /**
71
+ * Closes the WebSocket connection
72
+ */
73
+ disconnect(): void;
74
+ /**
75
+ * Overrides the base WebSocket URL for testing or different environments
76
+ * @param url - The new base URL to use
77
+ */
78
+ overrideBaseUrl(url: string): void;
79
+ }
80
+ //# sourceMappingURL=notification-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"notification-client.d.ts","sourceRoot":"","sources":["../../src/common/notification-client.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAA;AASnC,OAAO,EAAiB,KAAK,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAU/D;;GAEG;AACH,MAAM,MAAM,iBAAiB,GAAG;IAC9B,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE;QACJ,MAAM,EAAE,MAAM,CAAA;QACd,OAAO,EAAE,MAAM,CAAA;QACf,UAAU,EAAE,MAAM,CAAA;QAClB,QAAQ,EAAE,MAAM,CAAA;QAChB,SAAS,EAAE,MAAM,CAAA;QACjB,SAAS,EAAE,MAAM,CAAA;KAClB,CAAA;CACF,CAAA;AAED;;GAEG;AACH,MAAM,MAAM,UAAU,GAAG;IACvB,QAAQ,EAAE,MAAM,OAAO,CAAC;QACtB,WAAW,EAAE,MAAM,CAAA;KACpB,CAAC,CAAA;CACH,CAAA;AAcD;;GAEG;AACH,MAAM,MAAM,yBAAyB,GAAG;IACtC,iBAAiB,EAAE,MAAM,CAAA;IACzB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAA;AAKD;;;GAGG;AACH,qBAAa,kBAAkB;IAiB3B,OAAO,CAAC,QAAQ,CAAC,QAAQ;IACzB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAC3B,OAAO,CAAC,QAAQ,CAAC,gBAAgB;IAlBnC,OAAO,CAAC,GAAG,CAA+C;IAC1D,OAAO,CAAC,SAAS,CAAoB;IACrC,OAAO,CAAC,OAAO,CAA2B;IAC1C,OAAO,CAAC,MAAM,CAAQ;IACtB,OAAO,CAAC,UAAU,CAAI;IACtB,OAAO,CAAC,iBAAiB,CAAQ;IACjC,OAAO,CAAC,iBAAiB,CAAI;IAE7B;;;;;;OAMG;gBAEgB,QAAQ,EAAE,OAAO,CAAC,iBAAiB,CAAC,EACpC,UAAU,EAAE,UAAU,EACtB,gBAAgB,EAAE;QACjC,MAAM,EAAE,MAAM,CAAA;QACd,QAAQ,EAAE,MAAM,CAAA;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,EACD,OAAO,CAAC,EAAE,OAAO,CAAC,yBAAyB,CAAC;IAU9C;;OAEG;IACG,OAAO;IAyHb;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAqBxB;;OAEG;IACH,UAAU;IAMV;;;OAGG;IACI,eAAe,CAAC,GAAG,EAAE,MAAM;CAGnC"}
@@ -0,0 +1,239 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.NotificationClient = void 0;
27
+ const dntShim = __importStar(require("../_dnt.shims.js"));
28
+ const mod_js_1 = require("../mod.js");
29
+ const logger_js_1 = require("../utils/logger.js");
30
+ const flowcore_client_js_1 = require("./flowcore-client.js");
31
+ // Maximum reconnection interval in milliseconds
32
+ const MAX_RECONNECT_INTERVAL = 30_000;
33
+ /**
34
+ * Client for handling WebSocket connections to the Flowcore notification system.
35
+ * Manages connection lifecycle, authentication, and event handling.
36
+ */
37
+ class NotificationClient {
38
+ /**
39
+ * Creates a new NotificationClient instance
40
+ * @param observer - RxJS Subject for emitting notification events
41
+ * @param oidcClient - Client for handling OIDC authentication
42
+ * @param subscriptionSpec - Specification for what notifications to subscribe to
43
+ * @param options - Configuration options for the client
44
+ */
45
+ constructor(observer, oidcClient, subscriptionSpec, options) {
46
+ Object.defineProperty(this, "observer", {
47
+ enumerable: true,
48
+ configurable: true,
49
+ writable: true,
50
+ value: observer
51
+ });
52
+ Object.defineProperty(this, "oidcClient", {
53
+ enumerable: true,
54
+ configurable: true,
55
+ writable: true,
56
+ value: oidcClient
57
+ });
58
+ Object.defineProperty(this, "subscriptionSpec", {
59
+ enumerable: true,
60
+ configurable: true,
61
+ writable: true,
62
+ value: subscriptionSpec
63
+ });
64
+ Object.defineProperty(this, "url", {
65
+ enumerable: true,
66
+ configurable: true,
67
+ writable: true,
68
+ value: "wss://tenant.api.flowcore.io/notifications"
69
+ });
70
+ Object.defineProperty(this, "webSocket", {
71
+ enumerable: true,
72
+ configurable: true,
73
+ writable: true,
74
+ value: void 0
75
+ });
76
+ Object.defineProperty(this, "options", {
77
+ enumerable: true,
78
+ configurable: true,
79
+ writable: true,
80
+ value: void 0
81
+ });
82
+ Object.defineProperty(this, "logger", {
83
+ enumerable: true,
84
+ configurable: true,
85
+ writable: true,
86
+ value: void 0
87
+ });
88
+ Object.defineProperty(this, "eventCount", {
89
+ enumerable: true,
90
+ configurable: true,
91
+ writable: true,
92
+ value: 0
93
+ });
94
+ Object.defineProperty(this, "reconnectInterval", {
95
+ enumerable: true,
96
+ configurable: true,
97
+ writable: true,
98
+ value: void 0
99
+ });
100
+ Object.defineProperty(this, "reconnectAttempts", {
101
+ enumerable: true,
102
+ configurable: true,
103
+ writable: true,
104
+ value: 0
105
+ });
106
+ this.options = {
107
+ reconnectInterval: 1000,
108
+ ...options,
109
+ };
110
+ this.logger = options?.logger ?? logger_js_1.defaultLogger;
111
+ this.reconnectInterval = options?.reconnectInterval ?? 1000;
112
+ }
113
+ /**
114
+ * Establishes WebSocket connection and sets up event handlers
115
+ */
116
+ async connect() {
117
+ const token = await this.oidcClient.getToken();
118
+ const flowcoreClient = new flowcore_client_js_1.FlowcoreClient({
119
+ getBearerToken: () => Promise.resolve(token.accessToken),
120
+ });
121
+ const tenant = await flowcoreClient.execute(new mod_js_1.TenantFetchCommand({
122
+ tenant: this.subscriptionSpec.tenant,
123
+ }));
124
+ const dataCore = await flowcoreClient.execute(new mod_js_1.DataCoreFetchCommand({
125
+ tenantId: tenant.id,
126
+ dataCore: this.subscriptionSpec.dataCore,
127
+ }));
128
+ let flowType;
129
+ let eventType;
130
+ if (this.subscriptionSpec.flowType) {
131
+ flowType = await flowcoreClient.execute(new mod_js_1.FlowTypeFetchCommand({
132
+ dataCoreId: dataCore.id,
133
+ flowType: this.subscriptionSpec.flowType,
134
+ }));
135
+ if (this.subscriptionSpec.eventType) {
136
+ eventType = await flowcoreClient.execute(new mod_js_1.EventTypeFetchCommand({
137
+ flowTypeId: flowType?.id,
138
+ eventType: this.subscriptionSpec.eventType,
139
+ }));
140
+ }
141
+ }
142
+ this.webSocket = new dntShim.WebSocket(`${this.url}?token=${encodeURIComponent(token.accessToken)}`);
143
+ this.webSocket.onopen = () => {
144
+ this.logger.info("WebSocket connection opened.");
145
+ this.reconnectInterval = this.options.reconnectInterval;
146
+ this.reconnectAttempts = 0;
147
+ this.webSocket.send(JSON.stringify({
148
+ tenant: this.subscriptionSpec.tenant,
149
+ dataCoreId: dataCore.id,
150
+ flowTypeId: flowType?.id,
151
+ eventTypeId: eventType?.id,
152
+ }));
153
+ };
154
+ this.webSocket.onmessage = (event) => {
155
+ let parsedData;
156
+ if (event.data instanceof ArrayBuffer) {
157
+ parsedData = new TextDecoder().decode(event.data);
158
+ }
159
+ else if (Buffer.isBuffer(event.data)) {
160
+ parsedData = event.data.toString();
161
+ }
162
+ else if (Array.isArray(event.data)) {
163
+ // Handle Buffer arrays by concatenating them
164
+ parsedData = Buffer.concat(event.data).toString();
165
+ }
166
+ else {
167
+ parsedData = event.data;
168
+ }
169
+ const data = JSON.parse(parsedData);
170
+ if (data.type === "validation") {
171
+ this.logger.error(`Bad request: ${data.summary} - ${data.message} - ${data.found} - ${data.errors}`);
172
+ return;
173
+ }
174
+ const parsed = JSON.parse(data.message);
175
+ this.logger.debug(`Received event: ${parsed.pattern}`);
176
+ this.observer.next({
177
+ pattern: parsed.pattern,
178
+ data: {
179
+ tenant: parsed.data.tenantId,
180
+ eventId: parsed.data.eventId,
181
+ dataCoreId: parsed.data.dataCore,
182
+ flowType: parsed.data.aggregator,
183
+ eventType: parsed.data.eventType,
184
+ validTime: parsed.data.validTime,
185
+ },
186
+ });
187
+ this.eventCount++;
188
+ if (this.options.maxEvents && this.options.maxEvents <= this.eventCount) {
189
+ this.observer.complete();
190
+ this.eventCount = 0;
191
+ this.webSocket.close(1000, "Max events received");
192
+ }
193
+ };
194
+ this.webSocket.onclose = (event) => {
195
+ this.logger.info(`Connection closed: Code [${event.code}], Reason: ${event.reason}`);
196
+ if (event.code !== 1000) {
197
+ this.attemptReconnect();
198
+ return;
199
+ }
200
+ this.observer.complete();
201
+ };
202
+ this.webSocket.onerror = (error) => {
203
+ this.logger.error(`WebSocket encountered error: ${error}`);
204
+ this.observer.error(error);
205
+ this.webSocket.close();
206
+ };
207
+ }
208
+ /**
209
+ * Attempts to reconnect to the WebSocket server using exponential backoff
210
+ */
211
+ attemptReconnect() {
212
+ if (this.options.maxReconnects && this.reconnectAttempts >= this.options.maxReconnects) {
213
+ this.logger.error(`Max reconnect attempts ${this.reconnectAttempts}/${this.options.maxReconnects} reached. Giving up.`);
214
+ return;
215
+ }
216
+ this.reconnectAttempts++;
217
+ this.logger.info(`Attempting reconnection ${this.reconnectAttempts}${this.options.maxReconnects ? `/${this.options.maxReconnects}` : ""} in ${this.reconnectInterval} ms...`);
218
+ setTimeout(() => {
219
+ this.connect();
220
+ }, this.reconnectInterval);
221
+ this.reconnectInterval = Math.min(MAX_RECONNECT_INTERVAL, this.reconnectInterval * 2);
222
+ }
223
+ /**
224
+ * Closes the WebSocket connection
225
+ */
226
+ disconnect() {
227
+ if (this.webSocket) {
228
+ this.webSocket.close();
229
+ }
230
+ }
231
+ /**
232
+ * Overrides the base WebSocket URL for testing or different environments
233
+ * @param url - The new base URL to use
234
+ */
235
+ overrideBaseUrl(url) {
236
+ this.url = url;
237
+ }
238
+ }
239
+ exports.NotificationClient = NotificationClient;
package/script/mod.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export * from "./commands/index.js";
2
2
  export * from "./common/command.js";
3
3
  export * from "./common/flowcore-client.js";
4
+ export * from "./common/notification-client.js";
4
5
  export * from "./contracts/index.js";
5
6
  export * from "./exceptions/index.js";
6
7
  export * from "./utils/parse-response-helper.js";
@@ -1 +1 @@
1
- {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,sBAAsB,CAAA;AACpC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kCAAkC,CAAA"}
1
+ {"version":3,"file":"mod.d.ts","sourceRoot":"","sources":["../src/mod.ts"],"names":[],"mappings":"AAAA,cAAc,qBAAqB,CAAA;AACnC,cAAc,qBAAqB,CAAA;AACnC,cAAc,6BAA6B,CAAA;AAC3C,cAAc,iCAAiC,CAAA;AAC/C,cAAc,sBAAsB,CAAA;AACpC,cAAc,uBAAuB,CAAA;AACrC,cAAc,kCAAkC,CAAA"}
package/script/mod.js CHANGED
@@ -17,6 +17,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
17
17
  __exportStar(require("./commands/index.js"), exports);
18
18
  __exportStar(require("./common/command.js"), exports);
19
19
  __exportStar(require("./common/flowcore-client.js"), exports);
20
+ __exportStar(require("./common/notification-client.js"), exports);
20
21
  __exportStar(require("./contracts/index.js"), exports);
21
22
  __exportStar(require("./exceptions/index.js"), exports);
22
23
  __exportStar(require("./utils/parse-response-helper.js"), exports);
@@ -0,0 +1,8 @@
1
+ export type Logger = {
2
+ debug: (message: string, meta?: Record<string, unknown>) => void;
3
+ info: (message: string, meta?: Record<string, unknown>) => void;
4
+ warn: (message: string, meta?: Record<string, unknown>) => void;
5
+ error: (message: string | Error, meta?: Record<string, unknown>) => void;
6
+ };
7
+ export declare const defaultLogger: Logger;
8
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,MAAM,GAAG;IACnB,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;IAChE,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;IAC/D,IAAI,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;IAC/D,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,CAAA;CACzE,CAAA;AAED,eAAO,MAAM,aAAa,EAAE,MAa3B,CAAA"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultLogger = void 0;
4
+ exports.defaultLogger = {
5
+ debug: (message, meta) => {
6
+ console.debug(message, meta);
7
+ },
8
+ info: (message, meta) => {
9
+ console.info(message, meta);
10
+ },
11
+ warn: (message, meta) => {
12
+ console.warn(message, meta);
13
+ },
14
+ error: (message, meta) => {
15
+ console.error(message, meta);
16
+ },
17
+ };