@prisme.ai/sdk 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.
Files changed (38) hide show
  1. package/Readme.md +3 -1
  2. package/dist/_virtual/_tslib.js +17 -8
  3. package/dist/lib/ImportProcessing.d.ts +19 -0
  4. package/dist/lib/WorkspacesEndpoint.d.ts +10 -0
  5. package/dist/lib/api.d.ts +88 -29
  6. package/dist/lib/endpoints/users.d.ts +13 -0
  7. package/dist/lib/endpoints/workspaces.d.ts +16 -0
  8. package/dist/lib/endpoints/workspacesVersions.d.ts +2 -2
  9. package/dist/lib/events.d.ts +5 -1
  10. package/dist/lib/fetch.d.ts +2 -0
  11. package/dist/lib/fetcher.d.ts +11 -3
  12. package/dist/lib/interpolate.d.ts +2 -0
  13. package/dist/lib/interpolate.test.d.ts +1 -0
  14. package/dist/lib/interpolateVars.d.ts +2 -0
  15. package/dist/lib/utils.d.ts +3 -0
  16. package/dist/sdk/lib/ImportProcessing.js +17 -0
  17. package/dist/sdk/lib/WorkspacesEndpoint.js +19 -0
  18. package/dist/sdk/lib/api.js +248 -62
  19. package/dist/sdk/lib/endpoints/users.js +94 -0
  20. package/dist/sdk/lib/endpoints/workspaces.js +121 -0
  21. package/dist/sdk/lib/endpoints/workspacesVersions.js +4 -4
  22. package/dist/sdk/lib/events.js +51 -13
  23. package/dist/sdk/lib/fetch.js +12 -0
  24. package/dist/sdk/lib/fetcher.js +98 -42
  25. package/dist/sdk/lib/interpolate.js +26 -0
  26. package/dist/sdk/lib/interpolateVars.js +26 -0
  27. package/dist/sdk/lib/utils.js +48 -1
  28. package/lib/ImportProcessing.ts +22 -0
  29. package/lib/api.test.ts +90 -60
  30. package/lib/api.ts +318 -79
  31. package/lib/endpoints/users.ts +58 -0
  32. package/lib/endpoints/workspaces.ts +103 -0
  33. package/lib/endpoints/workspacesVersions.ts +13 -9
  34. package/lib/events.test.ts +2 -2
  35. package/lib/events.ts +60 -14
  36. package/lib/fetcher.ts +77 -12
  37. package/lib/utils.ts +39 -0
  38. package/package.json +3 -1
@@ -0,0 +1,58 @@
1
+ import { Api } from '../api';
2
+ import { dataURItoBlob, isDataURL } from '../utils';
3
+
4
+ export class UsersEndpoint {
5
+ private id: string;
6
+ private api: Api;
7
+
8
+ constructor(id: string, api: Api) {
9
+ this.id = id;
10
+ this.api = api;
11
+ }
12
+
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`, {
43
+ [k]: v,
44
+ });
45
+ }
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}`);
55
+ }
56
+ }
57
+
58
+ export default UsersEndpoint;
@@ -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
 
@@ -21,7 +21,7 @@ it('should connect to Websocket', () => {
21
21
  `https://api.eda.prisme.ai/workspaces/1/events`,
22
22
  {
23
23
  extraHeaders: {
24
- 'x-prismeai-token': 'abcde',
24
+ Authorization: 'Bearer abcde',
25
25
  },
26
26
  withCredentials: true,
27
27
  }
@@ -39,7 +39,7 @@ it('should connect to Websocket with apiKey', () => {
39
39
  `https://api.eda.prisme.ai/workspaces/1/events`,
40
40
  {
41
41
  extraHeaders: {
42
- 'x-prismeai-token': 'abcde',
42
+ Authorization: 'Bearer abcde',
43
43
  'x-prismeai-api-key': 'fghij',
44
44
  },
45
45
  withCredentials: true,
package/lib/events.ts CHANGED
@@ -20,46 +20,81 @@ 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,
26
27
  token,
28
+ legacyToken,
27
29
  apiKey,
28
30
  apiHost = 'https://api.eda.prisme.ai',
29
31
  filters,
30
32
  api,
33
+ transports,
31
34
  }: {
32
35
  workspaceId: string;
33
36
  token: string;
37
+ legacyToken?: string;
34
38
  apiKey?: string;
35
39
  apiHost?: string;
36
40
  filters?: Record<string, any>;
37
41
  api: Api;
42
+ transports?: string[];
38
43
  }) {
39
44
  this.workspaceId = workspaceId;
40
45
  const queryString = new URLSearchParams(filters || {}).toString();
41
46
  const fullQueryString = queryString ? `?${queryString}` : '';
42
- const extraHeaders: any = {
43
- 'x-prismeai-token': token,
44
- };
47
+ const extraHeaders: any = token
48
+ ? {
49
+ authorization: `Bearer ${token}`,
50
+ }
51
+ : { 'x-prismeai-token': legacyToken };
45
52
  this.lastReceivedEventDate = new Date();
46
53
  if (apiKey) {
47
54
  extraHeaders['x-prismeai-api-key'] = apiKey;
48
55
  }
56
+ this.filters = [filters || {}];
57
+ this.listenedUserTopics = new Map();
49
58
 
50
59
  this.client = io(
51
60
  `${apiHost}/workspaces/${workspaceId}/events${fullQueryString}`,
52
61
  {
53
62
  extraHeaders,
54
- 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
+ },
55
78
  }
56
79
  );
57
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
+
58
90
  const onConnect = () => {
59
- // Reset last filters
60
- this.updateFilters({
61
- payloadQuery: this.filters,
62
- });
91
+ // First connection
92
+ if (!this.socketId) {
93
+ this.socketId = this.client.id;
94
+ return;
95
+ }
96
+
97
+ // Retrieve lost history on reconnection
63
98
  setTimeout(async () => {
64
99
  const events = await api.getEvents(workspaceId, {
65
100
  ...this.filters[this.filters.length - 1],
@@ -72,16 +107,23 @@ export class Events {
72
107
  });
73
108
  }, 2000);
74
109
  };
110
+ this.client.on('connect', onConnect);
111
+
75
112
  this.client.on('disconnect', () => {
76
113
  if (!this.lastReceivedEventDate) {
77
114
  this.lastReceivedEventDate = new Date();
78
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
+ };
79
124
  this.client.off('connect', onConnect);
80
125
  this.client.on('connect', onConnect);
81
126
  });
82
-
83
- this.filters = [filters || {}];
84
- this.listenedUserTopics = new Map();
85
127
  }
86
128
 
87
129
  get socket() {
@@ -101,12 +143,16 @@ export class Events {
101
143
  }
102
144
 
103
145
  all(listener: (eventName: string, eventData: Prismeai.PrismeEvent) => void) {
104
- this.client.onAny((eventName: string, eventData: Prismeai.PrismeEvent) => {
146
+ const anyListener = (
147
+ eventName: string,
148
+ eventData: Prismeai.PrismeEvent
149
+ ) => {
105
150
  this.lastReceivedEventDate = new Date(eventData?.createdAt);
106
151
  return listener(eventName, eventData);
107
- });
152
+ };
153
+ this.client.onAny(anyListener);
108
154
 
109
- return () => this.client.offAny(listener);
155
+ return () => this.client.offAny(anyListener);
110
156
  }
111
157
 
112
158
  on(ev: string, listener: (eventData: Prismeai.PrismeEvent) => void) {
package/lib/fetcher.ts CHANGED
@@ -1,5 +1,7 @@
1
+ import FormData from 'form-data';
1
2
  import ApiError from './ApiError';
2
3
  import HTTPError from './HTTPError';
4
+ import { wait } from './utils';
3
5
 
4
6
  const headersAsObject = (headers: Headers) =>
5
7
  Array.from(headers).reduce(
@@ -10,36 +12,61 @@ const headersAsObject = (headers: Headers) =>
10
12
  {}
11
13
  );
12
14
 
15
+ export type Fetched<T> = T & {
16
+ headers?: Record<string, any>;
17
+ };
18
+
19
+ function isFormData(data: any): data is FormData {
20
+ return !!(data.append && typeof data.append === 'function');
21
+ }
22
+
23
+ const CSRF_TOKEN_HEADER = 'x-prismeai-csrf-token';
24
+
13
25
  export class Fetcher {
14
26
  public host: string;
15
27
  public token: string | null = null;
28
+ public legacyToken: string | null = null;
29
+ public overwriteClientId?: string;
30
+ private clientIdHeader?: string;
16
31
  protected _apiKey: string | null = null;
32
+ protected _csrfToken: string | null = null;
17
33
  public language: string | undefined;
34
+ public lastReceivedHeaders?: Record<string, any>;
18
35
 
19
- constructor(host: string) {
36
+ constructor(host: string, clientIdHeader?: string) {
20
37
  this.host = host;
38
+ this.clientIdHeader = clientIdHeader;
21
39
  }
22
40
 
23
41
  set apiKey(apiKey: string) {
24
42
  this._apiKey = apiKey;
25
43
  }
26
44
 
27
- prepareRequest(url: string,
28
- options: RequestInit = {}) {
45
+ prepareRequest(url: string, options: RequestInit = {}) {
29
46
  const headers = new Headers(options.headers || {});
30
47
 
31
- if (this.token && !headers.has('x-prismeai-token')) {
32
- headers.append('x-prismeai-token', this.token);
48
+ if (this.token && !headers.has('Authorization')) {
49
+ headers.append('Authorization', `Bearer ${this.token}`);
50
+ } else if (this.legacyToken && !headers.has('Authorization')) {
51
+ headers.append('x-prismeai-token', this.legacyToken);
33
52
  }
34
53
 
35
54
  if (this._apiKey && !headers.has('x-prismeai-apikey')) {
36
55
  headers.append('x-prismeai-api-key', this._apiKey);
37
56
  }
38
57
 
58
+ if (this._csrfToken && options.method && options.method !== 'GET') {
59
+ headers.append(CSRF_TOKEN_HEADER, this._csrfToken);
60
+ }
61
+
39
62
  if (this.language) {
40
63
  headers.append('accept-language', this.language);
41
64
  }
42
65
 
66
+ if (options.body instanceof URLSearchParams) {
67
+ headers.set('Content-Type', 'application/x-www-form-urlencoded');
68
+ }
69
+
43
70
  if (
44
71
  (!options.body || !(options.body instanceof FormData)) &&
45
72
  !headers.has('Content-Type')
@@ -47,9 +74,12 @@ export class Fetcher {
47
74
  headers.append('Content-Type', 'application/json');
48
75
  }
49
76
 
50
- headers.append('Access-Control-Allow-Origin', '*');
51
-
52
- return global.fetch(`${this.host}${url}`, {
77
+ const fullUrl =
78
+ url.startsWith('http://') || url.startsWith('https://')
79
+ ? url
80
+ : `${this.host}${url}`;
81
+ return global.fetch(fullUrl, {
82
+ credentials: headers.has('Authorization') ? 'omit' : 'include',
53
83
  ...options,
54
84
  headers,
55
85
  });
@@ -57,11 +87,42 @@ export class Fetcher {
57
87
 
58
88
  protected async _fetch<T>(
59
89
  url: string,
60
- options: RequestInit = {}
90
+ options: RequestInit = {},
91
+ maxRetries: number = 3
61
92
  ): Promise<T> {
62
- const res = await this.prepareRequest(url, options);
93
+ let res: Response;
94
+ try {
95
+ res = await this.prepareRequest(url, options);
96
+ } catch (e: any) {
97
+ if (maxRetries > 0 && e.message === 'Load failed') {
98
+ await wait(20);
99
+ return this._fetch(url, options, --maxRetries);
100
+ }
101
+ throw e;
102
+ }
103
+ if (options.redirect === 'manual' && res.status === 0) {
104
+ return { redirected: true } as T;
105
+ }
63
106
 
107
+ this.lastReceivedHeaders = headersAsObject(res.headers);
108
+ if (this.clientIdHeader && this.lastReceivedHeaders[this.clientIdHeader]) {
109
+ this.overwriteClientId = this.lastReceivedHeaders[this.clientIdHeader];
110
+ }
111
+ if (this.lastReceivedHeaders[CSRF_TOKEN_HEADER]) {
112
+ this._csrfToken = this.lastReceivedHeaders[CSRF_TOKEN_HEADER];
113
+ }
64
114
  if (!res.ok) {
115
+ if (
116
+ res.statusText?.length &&
117
+ res.statusText.includes('ECONNRESET') &&
118
+ maxRetries > 0
119
+ ) {
120
+ console.log(
121
+ `Retrying request towards ${url} as we received ECONNRESET error : ${res.statusText}`
122
+ );
123
+ await wait(20);
124
+ return this._fetch(url, options, --maxRetries);
125
+ }
65
126
  let error;
66
127
  try {
67
128
  error = new ApiError(await res.json(), res.status);
@@ -102,10 +163,14 @@ export class Fetcher {
102
163
  });
103
164
  }
104
165
 
105
- async post<T>(url: string, body?: Record<string, any>) {
166
+ async post<T>(url: string, body?: Record<string, any>, opts?: RequestInit) {
106
167
  return this._fetch<T>(url, {
107
168
  method: 'POST',
108
- body: body && !(body instanceof FormData) ? JSON.stringify(body) : body,
169
+ body:
170
+ body && !isFormData(body) && !(body instanceof URLSearchParams)
171
+ ? JSON.stringify(body)
172
+ : (body as BodyInit),
173
+ ...opts,
109
174
  });
110
175
  }
111
176
 
package/lib/utils.ts CHANGED
@@ -10,3 +10,42 @@ export const removedUndefinedProperties = (
10
10
  }
11
11
  return newObject;
12
12
  }, {});
13
+
14
+ export function dataURItoBlob(dataURI: string): [Blob, string] {
15
+ // convert base64/URLEncoded data component to raw binary data held in a string
16
+ let byteString;
17
+ if (dataURI.split(',')[0].indexOf('base64') >= 0)
18
+ byteString = atob(dataURI.split(',')[1].split(';')[0]);
19
+ else byteString = unescape(dataURI.split(',')[1]);
20
+ // separate out the mime component
21
+ const metadata = dataURI
22
+ .split(';')
23
+ .map((v) => v.split(/:/))
24
+ .filter((pair) => pair.length === 2);
25
+ const [, mimeString = ''] = metadata.find(([k, v]) => k === 'data') || [];
26
+ const [, ext] = mimeString.split(/\//);
27
+ const [, fileName = `file.${ext}`] =
28
+ metadata.find(([k, v]) => k === 'filename') || [];
29
+
30
+ const sanitizedFileName = encodeURIComponent(
31
+ decodeURIComponent(fileName).replace(/[;,\s]/g, '-')
32
+ );
33
+
34
+ // write the bytes of the string to a typed array
35
+ let ia = new Uint8Array(byteString.length);
36
+ for (var i = 0; i < byteString.length; i++) {
37
+ ia[i] = byteString.charCodeAt(i);
38
+ }
39
+
40
+ return [new Blob([ia], { type: mimeString }), sanitizedFileName];
41
+ }
42
+
43
+ export function isDataURL(file: any): file is string {
44
+ return !!(typeof file === 'string' && file.match(/^data\:/));
45
+ }
46
+
47
+ export async function wait(delay: number = 0) {
48
+ return new Promise((resolve) => {
49
+ setTimeout(resolve, delay);
50
+ });
51
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prisme.ai/sdk",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Communicate with Prisme.ai API",
5
5
  "main": "dist/sdk/index.js",
6
6
  "scripts": {
@@ -10,6 +10,8 @@
10
10
  "license": "ISC",
11
11
  "dependencies": {
12
12
  "@prisme.ai/types": "^1.0.9",
13
+ "form-data": "^4.0.0",
14
+ "pkce-challenge": "^3.1.0",
13
15
  "qs": "^6.10.3",
14
16
  "socket.io-client": "^4.4.1"
15
17
  }