@prisme.ai/sdk 1.0.1 → 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.
@@ -0,0 +1,22 @@
1
+ export type ImportProcessing = {
2
+ processing?: boolean;
3
+ message?: string;
4
+ };
5
+ export class ImportProcessingError extends Error {
6
+ error: ImportProcessing;
7
+ constructor(error: ImportProcessing) {
8
+ super();
9
+ this.error = error;
10
+ }
11
+ }
12
+ export type ImportSuccess = {
13
+ createdWorkspaceIds?: string[];
14
+ updatedWorkspaceIds?: string[];
15
+ imported: string[];
16
+ errors?: {
17
+ [name: string]: any;
18
+ }[];
19
+ workspace?: Prismeai.DSULReadOnly;
20
+ publishedApps?: Prismeai.App[];
21
+ deleted?: string[];
22
+ };
package/lib/api.test.ts CHANGED
@@ -47,11 +47,7 @@ it('should call /login/anonymous', async () => {
47
47
  const api = new Api({ host: '/fake/' });
48
48
  api.post = jest.fn();
49
49
  const user = await api.createAnonymousSession();
50
- expect(api.post).toHaveBeenCalledWith(
51
- '/login/anonymous',
52
- {},
53
- { credentials: 'omit' }
54
- );
50
+ expect(api.post).toHaveBeenCalledWith('/login/anonymous');
55
51
  expect(api.user).toBe(user);
56
52
  });
57
53
 
@@ -706,6 +702,25 @@ it('should call automation', () => {
706
702
  );
707
703
  });
708
704
 
705
+ it('should get automation from url', () => {
706
+ api.host = 'https://host.com';
707
+ expect(api.getAutomationFromUrl('13234', 'https://www.prisme.ai')).toBe(
708
+ false
709
+ );
710
+ expect(
711
+ api.getAutomationFromUrl(
712
+ '1234',
713
+ 'https://host.com/workspaces/1234/webhooks/endpoint'
714
+ )
715
+ ).toBe('endpoint');
716
+ expect(
717
+ api.getAutomationFromUrl(
718
+ '1235',
719
+ 'https://host.com/workspaces/1234/webhooks/endpoint'
720
+ )
721
+ ).toBe(false);
722
+ });
723
+
709
724
  it('should get workspace usage', () => {
710
725
  const api = new Api({ host: '/fake/' });
711
726
  const get: jest.Mock = (api.get = jest.fn((): any => {}));
package/lib/api.ts CHANGED
@@ -7,6 +7,11 @@ import { removedUndefinedProperties } from './utils';
7
7
  import WorkspacesEndpoint from './endpoints/workspaces';
8
8
  import ApiError from './ApiError';
9
9
  import UsersEndpoint from './endpoints/users';
10
+ import {
11
+ ImportProcessing,
12
+ ImportProcessingError,
13
+ ImportSuccess,
14
+ } from './ImportProcessing';
10
15
 
11
16
  interface PageWithMetadata extends Prismeai.Page {
12
17
  createdAt: string;
@@ -34,6 +39,9 @@ export interface ApiOptions {
34
39
  clientIdHeader?: string;
35
40
  redirectUri: string;
36
41
  };
42
+ websockets?: {
43
+ transports?: string[];
44
+ };
37
45
  }
38
46
 
39
47
  export interface AccessToken {
@@ -91,6 +99,10 @@ export class Api extends Fetcher {
91
99
  redirectUri: 'http://studio.local.prisme.ai:3000/signin',
92
100
  ...opts.oidc,
93
101
  },
102
+ websockets: {
103
+ ...opts.websockets,
104
+ transports: opts.websockets?.transports || ['polling', 'websocket'],
105
+ },
94
106
  };
95
107
  }
96
108
 
@@ -195,13 +207,7 @@ export class Api extends Fetcher {
195
207
  Prismeai.User & {
196
208
  token: string;
197
209
  }
198
- >(
199
- '/login/anonymous',
200
- {},
201
- {
202
- credentials: 'omit',
203
- }
204
- );
210
+ >('/login/anonymous');
205
211
  this._user = user;
206
212
  return user;
207
213
  }
@@ -217,13 +223,19 @@ export class Api extends Fetcher {
217
223
  token: string;
218
224
  }
219
225
  > {
220
- return await this.post('/signup', {
221
- email: email,
222
- password,
223
- firstName,
224
- lastName,
225
- language,
226
- });
226
+ return await this.post(
227
+ '/signup',
228
+ {
229
+ email: email,
230
+ password,
231
+ firstName,
232
+ lastName,
233
+ language,
234
+ },
235
+ {
236
+ credentials: 'omit',
237
+ }
238
+ );
227
239
  }
228
240
 
229
241
  getSignoutURL(redirectUri?: string) {
@@ -259,7 +271,7 @@ export class Api extends Fetcher {
259
271
 
260
272
  // Workspaces
261
273
  async getWorkspaces(): Promise<Workspace[]> {
262
- return await this.get('/workspaces?limit=600');
274
+ return await this.get('/workspaces?limit=2000');
263
275
  }
264
276
 
265
277
  async getWorkspace(
@@ -287,14 +299,41 @@ export class Api extends Fetcher {
287
299
  return await this.get(`/workspaces/${id}/security/roles`);
288
300
  }
289
301
 
290
- async createWorkspace(name: string): Promise<Workspace> {
291
- return await this.post('/workspaces', { name });
302
+ async getWorkspaceSecrets(
303
+ id: string
304
+ ): Promise<Fetched<PrismeaiAPI.GetWorkspaceSecrets.Responses.$200>> {
305
+ return await this.get(`/workspaces/${id}/security/secrets`);
306
+ }
307
+
308
+ async updateWorkspaceSecrets(
309
+ id: string,
310
+ newSecrets: any
311
+ ): Promise<Workspace> {
312
+ return await this.patch(`/workspaces/${id}/security/secrets`, {
313
+ ...newSecrets,
314
+ });
315
+ }
316
+
317
+ async deleteWorkspaceSecrets(
318
+ id: string,
319
+ secretName: string
320
+ ): Promise<Workspace> {
321
+ return await this.delete(
322
+ `/workspaces/${id}/security/secrets/${secretName}`
323
+ );
324
+ }
325
+
326
+ async createWorkspace(
327
+ newWorkspace: Partial<Prismeai.Workspace>
328
+ ): Promise<Workspace> {
329
+ return await this.post('/workspaces', newWorkspace);
292
330
  }
293
331
 
294
332
  async duplicateWorkspace({ id }: { id: string }): Promise<Workspace | null> {
295
333
  return await this.post(`/workspaces/${id}/versions/current/duplicate`, {});
296
334
  }
297
335
 
336
+ // @deprecated. Use api.workspaces(id).update() instead
298
337
  async updateWorkspace(
299
338
  workspace: Prismeai.DSULPatch
300
339
  ): Promise<Fetched<PrismeaiAPI.UpdateWorkspace.Responses.$200> | null> {
@@ -305,6 +344,7 @@ export class Api extends Fetcher {
305
344
  );
306
345
  }
307
346
 
347
+ // @deprecated. Use api.workspaces(id).delete() instead
308
348
  async deleteWorkspace(workspaceId: Workspace['id']): Promise<Workspace> {
309
349
  return await this.delete(`/workspaces/${workspaceId}`);
310
350
  }
@@ -366,7 +406,9 @@ export class Api extends Fetcher {
366
406
  automationSlug: string
367
407
  ): Promise<Fetched<PrismeaiAPI.GetAutomation.Responses.$200>> {
368
408
  return await this.get(
369
- `/workspaces/${workspaceId}/automations/${automationSlug}`
409
+ `/workspaces/${workspaceId}/automations/${encodeURIComponent(
410
+ automationSlug
411
+ )}`
370
412
  );
371
413
  }
372
414
 
@@ -385,13 +427,15 @@ export class Api extends Fetcher {
385
427
  automation: Prismeai.Automation
386
428
  ): Promise<Fetched<Prismeai.Automation & { slug: string }>> {
387
429
  return await this.patch(
388
- `/workspaces/${workspaceId}/automations/${slug}`,
430
+ `/workspaces/${workspaceId}/automations/${encodeURIComponent(slug)}`,
389
431
  await this.replaceAllImagesData(automation, workspaceId)
390
432
  );
391
433
  }
392
434
 
393
435
  async deleteAutomation(workspaceId: string, slug: string): Promise<string> {
394
- return await this.delete(`/workspaces/${workspaceId}/automations/${slug}`);
436
+ return await this.delete(
437
+ `/workspaces/${workspaceId}/automations/${encodeURIComponent(slug)}`
438
+ );
395
439
  }
396
440
 
397
441
  // Pages
@@ -471,6 +515,7 @@ export class Api extends Fetcher {
471
515
  if (filters && filters['source.sessionId'] === true) {
472
516
  if (this.sessionId) {
473
517
  filters['source.sessionId'] = this.sessionId;
518
+ filters['target.userTopic'] = ''; // We do not want to receive userTopics emitted by ourself for this session listener
474
519
  } else {
475
520
  delete filters['source.sessionId'];
476
521
  }
@@ -483,6 +528,7 @@ export class Api extends Fetcher {
483
528
  apiHost: this.host,
484
529
  filters,
485
530
  api: this,
531
+ transports: this.opts?.websockets?.transports,
486
532
  });
487
533
  return new Promise((resolve, reject) => {
488
534
  const off = events.once('connect_error', () => {
@@ -590,7 +636,6 @@ export class Api extends Fetcher {
590
636
  {
591
637
  error: 'CollaboratorNotFound',
592
638
  message: 'This user does not exist',
593
- details: { email },
594
639
  },
595
640
  404
596
641
  );
@@ -645,6 +690,14 @@ export class Api extends Fetcher {
645
690
  return await this.get(`/apps?${params.toString()}`);
646
691
  }
647
692
 
693
+ async getApp({
694
+ slug = '',
695
+ }: {
696
+ slug?: PrismeaiAPI.GetApp.PathParameters['appSlug'];
697
+ }): Promise<Fetched<PrismeaiAPI.GetApp.Responses.$200>> {
698
+ return await this.get(`/apps/${encodeURIComponent(slug)}`);
699
+ }
700
+
648
701
  async installApp(
649
702
  workspaceId: PrismeaiAPI.InstallAppInstance.PathParameters['workspaceId'],
650
703
  body: PrismeaiAPI.InstallAppInstance.RequestBody
@@ -673,6 +726,7 @@ export class Api extends Fetcher {
673
726
  return await this.post(`/apps`, body);
674
727
  }
675
728
 
729
+ // @deprecated. Use api.workspaces(id).listAppInstances())
676
730
  async listAppInstances(
677
731
  workspaceId: PrismeaiAPI.ListAppInstances.PathParameters['workspaceId']
678
732
  ): Promise<Fetched<PrismeaiAPI.ListAppInstances.Responses.$200>> {
@@ -720,13 +774,31 @@ export class Api extends Fetcher {
720
774
  return response;
721
775
  }
722
776
 
723
- async uploadFiles(files: string | string[], workspaceId: string) {
777
+ // @deprecated. Use api.workspaces(id).uploadFiles()
778
+ async uploadFiles(
779
+ files: string | string[],
780
+ workspaceId: string,
781
+ opts?: {
782
+ expiresAfter?: number;
783
+ public?: boolean;
784
+ shareToken?: boolean;
785
+ }
786
+ ) {
724
787
  const formData = new FormData();
725
788
  (Array.isArray(files) ? files : [files]).forEach((file) => {
726
789
  try {
727
790
  formData.append('file', ...dataURItoBlob(file));
728
791
  } catch {}
729
792
  });
793
+ if (opts?.expiresAfter) {
794
+ formData.append('expiresAfter', `${opts?.expiresAfter}`);
795
+ }
796
+ if (typeof opts?.public === 'boolean') {
797
+ formData.append('public', `${opts?.public}`);
798
+ }
799
+ if (typeof opts?.shareToken === 'boolean') {
800
+ formData.append('shareToken', `${opts?.shareToken}`);
801
+ }
730
802
  try {
731
803
  return await this._fetch<PrismeaiAPI.UploadFile.Responses.$200>(
732
804
  `/workspaces/${workspaceId}/files`,
@@ -783,6 +855,13 @@ export class Api extends Fetcher {
783
855
  return replaced;
784
856
  }
785
857
 
858
+ getAutomationFromUrl(workspaceId: string, url: string) {
859
+ const match = url.match(
860
+ `${this.host}/workspaces/${workspaceId}/webhooks\/(.*$)`
861
+ );
862
+ return match ? match[1] : false;
863
+ }
864
+
786
865
  async callAutomation(
787
866
  workspaceId: string,
788
867
  automation: string,
@@ -808,6 +887,7 @@ export class Api extends Fetcher {
808
887
  });
809
888
  }
810
889
 
890
+ // @deprecated. Use api.workspaces(id).getUsage())
811
891
  async getWorkspaceUsage(
812
892
  workspaceId: PrismeaiAPI.WorkspaceUsage.Parameters.WorkspaceId,
813
893
  {
@@ -834,24 +914,22 @@ export class Api extends Fetcher {
834
914
  return this.get(`/workspaces/${workspaceId}/usage?${params.toString()}`);
835
915
  }
836
916
 
837
- async importArchive(
838
- archive: File
839
- ): Promise<PrismeaiAPI.ImportNewWorkspace.Responses.$200> {
917
+ async importArchive(archive: File): Promise<ImportSuccess> {
840
918
  return new Promise((resolve) => {
841
919
  const fileReader = new FileReader();
842
920
  fileReader.addEventListener('load', async ({ target }) => {
843
921
  const file = target?.result as string;
844
922
  const formData = new FormData();
845
923
  formData.append('archive', ...dataURItoBlob(file));
846
- resolve(
847
- await this._fetch<PrismeaiAPI.ImportNewWorkspace.Responses.$200>(
848
- `/workspaces/import`,
849
- {
850
- method: 'POST',
851
- body: formData,
852
- }
853
- )
924
+
925
+ const result = await this.post<ImportProcessing | ImportSuccess>(
926
+ `/workspaces/import`,
927
+ formData
854
928
  );
929
+ if ((result as ImportProcessing).processing) {
930
+ throw new ImportProcessingError(result as ImportProcessing);
931
+ }
932
+ resolve(result as ImportSuccess);
855
933
  });
856
934
  fileReader.readAsDataURL(archive);
857
935
  });
@@ -1,4 +1,5 @@
1
1
  import { Api } from '../api';
2
+ import { dataURItoBlob, isDataURL } from '../utils';
2
3
 
3
4
  export class UsersEndpoint {
4
5
  private id: string;
@@ -9,13 +10,48 @@ export class UsersEndpoint {
9
10
  this.api = api;
10
11
  }
11
12
 
12
- async setMeta(k: string, v: any) {
13
- await this.api.post(`/user/meta`, {
13
+ async update(
14
+ data: Partial<Prismeai.User>
15
+ ): Promise<PrismeaiAPI.PatchUser.Responses.$200> {
16
+ if (isDataURL(data.photo)) {
17
+ await this.updatePhoto(data.photo);
18
+ delete data.photo;
19
+ }
20
+ return await this.api.patch('/user', data);
21
+ }
22
+
23
+ async updatePhoto(
24
+ photo: string
25
+ ): Promise<PrismeaiAPI.PostUserPhoto.Responses.$200> {
26
+ if (!isDataURL(photo)) {
27
+ throw new Error('Photo must be a dataurl file');
28
+ }
29
+ const formData = new FormData();
30
+ formData.append('photo', ...dataURItoBlob(photo));
31
+
32
+ return await this.api.post<PrismeaiAPI.PostUserPhoto.Responses.$200>(
33
+ `/user/photo`,
34
+ formData
35
+ );
36
+ }
37
+
38
+ async setMeta(
39
+ k: string,
40
+ v: any
41
+ ): Promise<PrismeaiAPI.SetMeta.Responses.$200> {
42
+ return await this.api.post(`/user/meta`, {
14
43
  [k]: v,
15
44
  });
16
45
  }
17
- async deleteMeta(k: string) {
18
- await this.api.delete(`/user/meta/${k}`);
46
+ async deleteMeta(k: string): Promise<PrismeaiAPI.DeleteMeta.Responses.$200> {
47
+ return await this.api.delete(`/user/meta/${k}`);
48
+ }
49
+
50
+ async sendDeleteValidation() {
51
+ return await this.api.delete(`/user`);
52
+ }
53
+ async delete(token?: string) {
54
+ return await this.api.delete(`/users/${this.id}?token=${token}`);
19
55
  }
20
56
  }
21
57
 
@@ -1,4 +1,11 @@
1
1
  import { Api } from '../api';
2
+ import { Fetched } from '../fetcher';
3
+ import {
4
+ ImportProcessing,
5
+ ImportProcessingError,
6
+ ImportSuccess,
7
+ } from '../ImportProcessing';
8
+ import { dataURItoBlob, removedUndefinedProperties } from '../utils';
2
9
  import WorkspacesVersionsEndpoint from './workspacesVersions';
3
10
 
4
11
  export class WorkspacesEndpoint {
@@ -13,6 +20,102 @@ export class WorkspacesEndpoint {
13
20
  get versions() {
14
21
  return new WorkspacesVersionsEndpoint(this.id, this.api);
15
22
  }
23
+
24
+ async update(
25
+ workspace: Prismeai.DSULPatch
26
+ ): Promise<Fetched<PrismeaiAPI.UpdateWorkspace.Responses.$200> | null> {
27
+ return await this.api.patch(
28
+ `/workspaces/${this.id}`,
29
+ await this.api.replaceAllImagesData(workspace, this.id)
30
+ );
31
+ }
32
+
33
+ async delete(): Promise<PrismeaiAPI.DeleteWorkspace.Responses.$200> {
34
+ return await this.api.delete(`/workspaces/${this.id}`);
35
+ }
36
+
37
+ async uploadFiles(
38
+ files: string | string[],
39
+ opts?: {
40
+ expiresAfter?: number;
41
+ public?: boolean;
42
+ shareToken?: boolean;
43
+ }
44
+ ) {
45
+ const formData = new FormData();
46
+ (Array.isArray(files) ? files : [files]).forEach((file) => {
47
+ try {
48
+ formData.append('file', ...dataURItoBlob(file));
49
+ } catch {}
50
+ });
51
+ if (opts?.expiresAfter) {
52
+ formData.append('expiresAfter', `${opts?.expiresAfter}`);
53
+ }
54
+ if (typeof opts?.public === 'boolean') {
55
+ formData.append('public', `${opts?.public}`);
56
+ }
57
+ if (typeof opts?.shareToken === 'boolean') {
58
+ formData.append('shareToken', `${opts?.shareToken}`);
59
+ }
60
+ try {
61
+ return await this.api.post<PrismeaiAPI.UploadFile.Responses.$200>(
62
+ `/workspaces/${this.id}/files`,
63
+ formData
64
+ );
65
+ } catch (e) {
66
+ console.error(e);
67
+ }
68
+ return [];
69
+ }
70
+
71
+ async listAppInstances(): Promise<
72
+ Fetched<PrismeaiAPI.ListAppInstances.Responses.$200>
73
+ > {
74
+ return await this.api.get(`/workspaces/${this.id}/apps`);
75
+ }
76
+
77
+ async getUsage({
78
+ afterDate,
79
+ beforeDate,
80
+ details,
81
+ }: {
82
+ afterDate?: PrismeaiAPI.WorkspaceUsage.Parameters.AfterDate;
83
+ beforeDate?: PrismeaiAPI.WorkspaceUsage.Parameters.BeforeDate;
84
+ details?: PrismeaiAPI.WorkspaceUsage.Parameters.Details;
85
+ } = {}): Promise<Fetched<PrismeaiAPI.WorkspaceUsage.Responses.$200>> {
86
+ const params = new URLSearchParams(
87
+ removedUndefinedProperties(
88
+ {
89
+ afterDate: `${afterDate || ''}`,
90
+ beforeDate: `${beforeDate || ''}`,
91
+ details: `${details || ''}`,
92
+ },
93
+ true
94
+ )
95
+ );
96
+
97
+ return this.api.get(`/workspaces/${this.id}/usage?${params.toString()}`);
98
+ }
99
+
100
+ async importArchive(archive: File): Promise<ImportSuccess> {
101
+ return new Promise((resolve) => {
102
+ const fileReader = new FileReader();
103
+ fileReader.addEventListener('load', async ({ target }) => {
104
+ const file = target?.result as string;
105
+ const formData = new FormData();
106
+ formData.append('archive', ...dataURItoBlob(file));
107
+ const result = await this.api.post<ImportProcessing | ImportSuccess>(
108
+ `/workspaces/${this.id}/import`,
109
+ formData
110
+ );
111
+ if ((result as ImportProcessing).processing) {
112
+ throw new ImportProcessingError(result as ImportProcessing);
113
+ }
114
+ resolve(result as ImportSuccess);
115
+ });
116
+ fileReader.readAsDataURL(archive);
117
+ });
118
+ }
16
119
  }
17
120
 
18
121
  export default WorkspacesEndpoint;
@@ -10,24 +10,28 @@ export class WorkspacesVersionsEndpoint {
10
10
  }
11
11
 
12
12
  create(version?: PrismeaiAPI.PublishWorkspaceVersion.RequestBody) {
13
- this.api.post(`/workspaces/${this.workspaceId}/versions`, version);
13
+ return this.api.post(`/workspaces/${this.workspaceId}/versions`, version);
14
14
  }
15
15
  rollback(
16
- versionId: PrismeaiAPI.RollbackWorkspaceVersion.PathParameters['versionId']
16
+ versionId: PrismeaiAPI.PullWorkspaceVersion.PathParameters['versionId'],
17
+ opts?: PrismeaiAPI.PullWorkspaceVersion.RequestBody
17
18
  ) {
18
- this.api.post(
19
- `/workspaces/${this.workspaceId}/versions/${versionId}/rollback`
19
+ return this.api.post(
20
+ `/workspaces/${this.workspaceId}/versions/${versionId}/pull`,
21
+ opts
20
22
  );
21
23
  }
22
24
 
23
- async export(version: PrismeaiAPI.ExportWorkspaceVersion.Parameters.VersionId = 'current') {
25
+ async export(
26
+ version: PrismeaiAPI.ExportWorkspaceVersion.Parameters.VersionId = 'current'
27
+ ) {
24
28
  const res = await this.api.prepareRequest(
25
- `/workspaces/${this.workspaceId}/versions/${version}/export`, {
26
- method: 'post'
29
+ `/workspaces/${this.workspaceId}/versions/${version}/export`,
30
+ {
31
+ method: 'post',
27
32
  }
28
- )
33
+ );
29
34
  return new Blob([await res.arrayBuffer()], { type: 'application/zip' });
30
-
31
35
  }
32
36
  }
33
37
 
package/lib/events.ts CHANGED
@@ -20,6 +20,7 @@ export class Events {
20
20
  private listenedUserTopics: Map<string, string[]>;
21
21
  private listeners: Map<string, Function[]> = new Map();
22
22
  private lastReceivedEventDate: Date;
23
+ private socketId?: string;
23
24
 
24
25
  constructor({
25
26
  workspaceId,
@@ -29,6 +30,7 @@ export class Events {
29
30
  apiHost = 'https://api.eda.prisme.ai',
30
31
  filters,
31
32
  api,
33
+ transports,
32
34
  }: {
33
35
  workspaceId: string;
34
36
  token: string;
@@ -37,33 +39,62 @@ export class Events {
37
39
  apiHost?: string;
38
40
  filters?: Record<string, any>;
39
41
  api: Api;
42
+ transports?: string[];
40
43
  }) {
41
44
  this.workspaceId = workspaceId;
42
45
  const queryString = new URLSearchParams(filters || {}).toString();
43
46
  const fullQueryString = queryString ? `?${queryString}` : '';
44
47
  const extraHeaders: any = token
45
48
  ? {
46
- Authorization: `Bearer ${token}`,
49
+ authorization: `Bearer ${token}`,
47
50
  }
48
51
  : { 'x-prismeai-token': legacyToken };
49
52
  this.lastReceivedEventDate = new Date();
50
53
  if (apiKey) {
51
54
  extraHeaders['x-prismeai-api-key'] = apiKey;
52
55
  }
56
+ this.filters = [filters || {}];
57
+ this.listenedUserTopics = new Map();
53
58
 
54
59
  this.client = io(
55
60
  `${apiHost}/workspaces/${workspaceId}/events${fullQueryString}`,
56
61
  {
57
62
  extraHeaders,
58
- withCredentials: true,
63
+ withCredentials: !extraHeaders.authorization,
64
+ transports: transports || ['polling', 'websocket'],
65
+ auth: (cb) => {
66
+ cb({
67
+ // Browser websockets cannot send extraHeaders, so we use socketio-client auth instead
68
+ extraHeaders:
69
+ transports && transports[0] === 'websocket' ? extraHeaders : {},
70
+
71
+ filters: {
72
+ payloadQuery: this.filters,
73
+ },
74
+
75
+ reuseSocketId: this.socketId,
76
+ });
77
+ },
59
78
  }
60
79
  );
61
80
 
81
+ this.client.on('connect_error', (err) => {
82
+ console.error(`Failed websocket connection : `, err);
83
+ // revert to classic upgrade
84
+ this.client.io.opts.transports = ['polling', 'websocket'];
85
+ });
86
+ this.client.on('error', (err) => {
87
+ this.client.io.opts.transports = ['polling', 'websocket'];
88
+ });
89
+
62
90
  const onConnect = () => {
63
- // Reset last filters
64
- this.updateFilters({
65
- payloadQuery: this.filters,
66
- });
91
+ // First connection
92
+ if (!this.socketId) {
93
+ this.socketId = this.client.id;
94
+ return;
95
+ }
96
+
97
+ // Retrieve lost history on reconnection
67
98
  setTimeout(async () => {
68
99
  const events = await api.getEvents(workspaceId, {
69
100
  ...this.filters[this.filters.length - 1],
@@ -76,16 +107,23 @@ export class Events {
76
107
  });
77
108
  }, 2000);
78
109
  };
110
+ this.client.on('connect', onConnect);
111
+
79
112
  this.client.on('disconnect', () => {
80
113
  if (!this.lastReceivedEventDate) {
81
114
  this.lastReceivedEventDate = new Date();
82
115
  }
116
+ // Make sure we reconnect with current filters & socketId
117
+ this.client.auth = {
118
+ ...this.client.auth,
119
+ filters: {
120
+ payloadQuery: this.filters,
121
+ },
122
+ reuseSocketId: this.socketId,
123
+ };
83
124
  this.client.off('connect', onConnect);
84
125
  this.client.on('connect', onConnect);
85
126
  });
86
-
87
- this.filters = [filters || {}];
88
- this.listenedUserTopics = new Map();
89
127
  }
90
128
 
91
129
  get socket() {
@@ -105,12 +143,16 @@ export class Events {
105
143
  }
106
144
 
107
145
  all(listener: (eventName: string, eventData: Prismeai.PrismeEvent) => void) {
108
- this.client.onAny((eventName: string, eventData: Prismeai.PrismeEvent) => {
146
+ const anyListener = (
147
+ eventName: string,
148
+ eventData: Prismeai.PrismeEvent
149
+ ) => {
109
150
  this.lastReceivedEventDate = new Date(eventData?.createdAt);
110
151
  return listener(eventName, eventData);
111
- });
152
+ };
153
+ this.client.onAny(anyListener);
112
154
 
113
- return () => this.client.offAny(listener);
155
+ return () => this.client.offAny(anyListener);
114
156
  }
115
157
 
116
158
  on(ev: string, listener: (eventData: Prismeai.PrismeEvent) => void) {