@verdocs/js-sdk 4.0.6 → 4.0.9

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/dist/index.js CHANGED
@@ -2,693 +2,253 @@
2
2
 
3
3
  var axios = require('axios');
4
4
 
5
- /* tslint:disable:no-bitwise */
6
- const b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
7
- // Regular expression to check formal correctness of base64 encoded strings
8
- const b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
9
5
  /**
10
- * Simplified, Node/Browser-safe alternative to atob() for base64 decoding.
11
- * Modified from https://github.com/MaxArt2501/base64-js/blob/master/base64.js
6
+ * Given a `rgba(r,g,b,a)` string value, returns the hex equivalent, dropping the alpha channel.
12
7
  */
13
- const AtoB = (str) => {
14
- // atob can work with strings with whitespaces, even inside the encoded part,
15
- // but only \t, \n, \f, \r and ' ', which can be stripped.
16
- str = String(str).replace(/[\t\n\f\r ]+/g, '');
17
- if (!b64re.test(str))
18
- throw new TypeError("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");
19
- // Adding the padding if missing, for semplicity
20
- str += '=='.slice(2 - (str.length & 3));
21
- let bitmap;
22
- let result = '';
23
- let r1;
24
- let r2;
25
- let i = 0;
26
- for (; i < str.length;) {
27
- bitmap =
28
- (b64.indexOf(str.charAt(i++)) << 18) |
29
- (b64.indexOf(str.charAt(i++)) << 12) |
30
- ((r1 = b64.indexOf(str.charAt(i++))) << 6) |
31
- (r2 = b64.indexOf(str.charAt(i++)));
32
- result +=
33
- r1 === 64
34
- ? String.fromCharCode((bitmap >> 16) & 255)
35
- : r2 === 64
36
- ? String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255)
37
- : String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255, bitmap & 255);
8
+ function getRGB(rgba) {
9
+ const rgbNumbers = rgba.replace('rgba(', '').replace(')', '').split(',');
10
+ const rgbObject = {
11
+ red: +rgbNumbers[0],
12
+ green: +rgbNumbers[1],
13
+ blue: +rgbNumbers[2],
14
+ alpha: +rgbNumbers[3],
15
+ };
16
+ const alpha = 1 - rgbObject.alpha;
17
+ const red = Math.round((rgbObject.alpha * (rgbObject.red / 255) + alpha) * 255);
18
+ const green = Math.round((rgbObject.alpha * (rgbObject.green / 255) + alpha) * 255);
19
+ const blue = Math.round((rgbObject.alpha * (rgbObject.blue / 255) + alpha) * 255);
20
+ return '#' + rgbToHex(red) + rgbToHex(green) + rgbToHex(blue);
21
+ }
22
+ /**
23
+ * Given an RGB string value, returns the hex equivalent.
24
+ */
25
+ function rgbToHex(rgb) {
26
+ const hex = rgb.toString(16);
27
+ if (hex.length < 2) {
28
+ return '0' + hex;
38
29
  }
39
- return result;
40
- };
30
+ return hex;
31
+ }
41
32
  /**
42
- * Decode the body of a JWT. This helper may allow front-end applications to avoid a dependency on `jsonwebtoken` in
43
- * many cases. Note that this should only be used for true JWTs. Opaque tokens will cause this to throw.
33
+ * Given a signer role index, return the color code for that signer.
44
34
  */
45
- const decodeJWTBody = (token) => JSON.parse(AtoB((token || '').split('.')[1] || ''));
35
+ function getRGBA(roleIndex) {
36
+ switch (roleIndex % 10) {
37
+ case 0:
38
+ return roleIndex === 0 ? 'rgba(255, 193, 7, 0.4)' : 'rgba(134, 134, 134, 0.3)'; // #FFE69C
39
+ case 1:
40
+ return 'rgba(156, 39, 176, .4)'; // '#E3C3E9'
41
+ case 2:
42
+ return 'rgba(33, 150, 243, .4)'; // '#C1E1FB'
43
+ case 3:
44
+ return 'rgba(220, 231, 117, 0.3)';
45
+ case 4:
46
+ return 'rgba(121, 134, 203, 0.3)';
47
+ case 5:
48
+ return 'rgba(77, 182, 172, 0.3)';
49
+ case 6:
50
+ return 'rgba(255, 202, 165, 0.3)';
51
+ case 7:
52
+ return 'rgba(2, 247, 190, 0.3)';
53
+ case 8:
54
+ return 'rgba(255, 138, 101, 0.3)';
55
+ case 9:
56
+ return 'rgba(82, 255, 79, 0.3)';
57
+ default:
58
+ return 'rgba(229, 115, 155, 0.3)';
59
+ }
60
+ }
46
61
  /**
47
- * Decode the body of an Verdocs access token. Note that raw tokens contain namespaced fields, e.g.
48
- * `https://verdocs.com/profile_id`. To make these tokens easier to use in front-end code, this name-spacing
49
- * will be removed. Note that user and signing sessions have different access token formats. The calling
50
- * application should distinguish between the two based on the context of the authenticated session, or by
51
- * the presence of the `document_id` field, which will only be present for signing sessions.
62
+ * Given a role name, return a color code for it. This works by computing a hash code so the specific color returned
63
+ * is not specified explicitly, but will be the same for every call with the same input value.
52
64
  */
53
- const decodeAccessTokenBody = (token) => {
54
- let decoded;
55
- try {
56
- decoded = decodeJWTBody(token);
57
- if (decoded === null) {
58
- return null;
65
+ function nameToRGBA(str) {
66
+ if (!!str) {
67
+ const validNum = parseInt(str.slice(-1), 10);
68
+ if (!isNaN(validNum)) {
69
+ str += (validNum * 99).toString();
70
+ }
71
+ let hash = 0;
72
+ for (let i = 0; i < str.length; i++) {
73
+ // tslint:disable-next-line:no-bitwise
74
+ hash = str.charCodeAt(i) + ((hash << 5) - hash);
59
75
  }
76
+ hash = Math.round(hash / 1.3);
77
+ // tslint:disable-next-line:no-bitwise
78
+ const c = (hash & 0x00ffff08).toString(16).toUpperCase();
79
+ const hex = '#' + '00000'.substring(0, 6 - c.length) + c;
80
+ const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
81
+ const color = {
82
+ r: parseInt(result[1], 16),
83
+ g: parseInt(result[2], 16),
84
+ b: parseInt(result[3], 16),
85
+ };
86
+ return `rgba(${color.r}, ${color.g}, ${color.b}, 0.2)`;
60
87
  }
61
- catch (e) {
62
- return null;
88
+ }
89
+ /**
90
+ * Helper function to obtain a color code given a role name given various possible inputs.
91
+ */
92
+ function getRoleColor(name, roles, index) {
93
+ if (index) {
94
+ return getRGBA(index);
63
95
  }
64
- Object.keys(decoded).forEach((key) => {
65
- if (typeof key === 'string' && key.startsWith('https://verdocs.com/')) {
66
- decoded[key.replace('https://verdocs.com/', '')] = decoded[key];
67
- delete decoded[key];
96
+ else if (roles && roles.length > 0) {
97
+ const roleIndex = roles.findIndex((role) => role === name);
98
+ if (roleIndex > -1) {
99
+ return getRGBA(roleIndex);
68
100
  }
69
- });
70
- return decoded;
71
- };
72
-
73
- function getDefaultExportFromCjs (x) {
74
- return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
101
+ else {
102
+ return nameToRGBA(name);
103
+ }
104
+ }
105
+ else {
106
+ return nameToRGBA(name);
107
+ }
75
108
  }
76
109
 
77
- // This file provides a polyfill for managing globals in both NodeJS and browser environments. This is
78
- // an anti-pattern we'd hoped to avoid, but we have several projects dependending on one common library
79
- // (this js-sdk) and we want that library to provide a common endpoint to all callers (so authentication
80
- // tokens only need to be tracked in one place). The trouble is, one of those libraries is based on
81
- // StencilJS and is compiling its modules into Web Components. Because of how module resolution works,
82
- // when those Components load js-sdk they get a separate instance. Without messy options like having to
83
- // pass raw data from the caller to each Component, or pass around references to a common Endpoint, they
84
- // have no way to access authenticated sessions unless we make the Endpoint a true global.
85
- //
86
- // @credit https://github.com/medikoo/es5-ext/blob/master/global.js
87
- // @credit https://mathiasbynens.be/notes/globalthis
88
- var naiveFallback = function () {
89
- if (typeof self === 'object' && self)
90
- return self;
91
- if (typeof window === 'object' && window)
92
- return window;
93
- throw new Error('Unable to resolve global `this`');
94
- };
95
- var globalThis_1 = (function () {
96
- if (this)
97
- return this;
98
- // Unexpected strict mode (may happen if e.g. bundled into ESM module)
99
- // Fallback to standard globalThis if available
100
- if (typeof globalThis === 'object' && globalThis)
101
- return globalThis;
102
- // Thanks @mathiasbynens -> https://mathiasbynens.be/notes/globalthis
103
- // In all ES5+ engines global object inherits from Object.prototype
104
- // (if you approached one that doesn't please report)
105
- try {
106
- Object.defineProperty(Object.prototype, '__global__', {
107
- get: function () {
108
- return this;
109
- },
110
- configurable: true,
111
- });
110
+ const YEAR = 365 * 24 * 60 * 60;
111
+ // const MONTH = 30 * 24 * 60 * 60;
112
+ const WEEK = 7 * 24 * 60 * 60;
113
+ const DAY = 24 * 60 * 60;
114
+ const HOUR = 60 * 60;
115
+ const MINUTE = 60;
116
+ const formatShortTimeAgo = (val) => {
117
+ if (val === undefined || val === null) {
118
+ return '';
112
119
  }
113
- catch (error) {
114
- // Unfortunate case of updates to Object.prototype being restricted
115
- // via preventExtensions, seal or freeze
116
- return naiveFallback();
120
+ let dateInput;
121
+ if (typeof val === 'string' || typeof val === 'number') {
122
+ dateInput = new Date(val);
117
123
  }
118
- try {
119
- // Safari case (window.__global__ works, but __global__ does not)
120
- if (!__global__)
121
- return naiveFallback();
122
- return __global__;
124
+ else if (typeof val === 'object') {
125
+ dateInput = val;
123
126
  }
124
- finally {
125
- delete Object.prototype.__global__;
127
+ else {
128
+ return '';
126
129
  }
127
- })();
128
-
129
- var globalThis$1 = /*@__PURE__*/getDefaultExportFromCjs(globalThis_1);
130
-
131
- // @credit https://derickbailey.com/2016/03/09/creating-a-true-singleton-in-node-js-with-es6-symbols/
132
- // Also see globalThis for comments about why we're doing this in the first place.
133
- const ENDPOINT_KEY = Symbol.for('verdocs-default-endpoint');
134
- const requestLogger = (r) => {
135
- // tslint:disable-next-line
136
- console.debug(`[JS-SDK] ${r.method.toUpperCase()} ${r.baseURL}${r.url}`, r.data ? JSON.stringify(r.data) : '');
137
- return r;
130
+ const timeDiff = Math.floor((new Date().getTime() - dateInput.getTime()) / 1000);
131
+ if (timeDiff >= YEAR) {
132
+ return Math.floor(timeDiff / YEAR) + 'Y';
133
+ }
134
+ // if (timeDiff >= MONTH) {
135
+ // return Math.floor(timeDiff / MONTH) + 'M';
136
+ // }
137
+ if (timeDiff >= WEEK) {
138
+ return Math.floor(timeDiff / WEEK) + 'W';
139
+ }
140
+ if (timeDiff >= DAY) {
141
+ return Math.floor(timeDiff / DAY) + 'D';
142
+ }
143
+ if (timeDiff >= HOUR) {
144
+ return Math.floor(timeDiff / HOUR) + 'H';
145
+ }
146
+ if (timeDiff >= MINUTE) {
147
+ return Math.floor(timeDiff / MINUTE) + 'M';
148
+ }
149
+ return `${timeDiff}S`;
138
150
  };
139
- /**
140
- * VerdocsEndpoint is a class wrapper for a specific connection and authorization context for calling the Verdocs APIs.
141
- * Endpoints can be used for isolated session tasks.
142
- *
143
- * For instance, ephemeral signing sessions may be created independently of a caller's status as an authenticated user.
144
- * In that case, an Endpoint can be created and authenticated, used for calls related to signing operations, then
145
- * discarded once signing is complete.
146
- *
147
- * Note that endpoint configuration functions return the instance, so they can be chained, e.g.
148
- *
149
- * ```typescript
150
- * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
151
- *
152
- * const endpoint = new VerdocsEndpoint();
153
- * endpoint
154
- * .setSessionType('signing')
155
- * .logRequests(true)
156
- * .setClientID('1234)
157
- * .setTimeout(30000);
158
- * ```
159
- */
160
- class VerdocsEndpoint {
161
- environment = 'verdocs';
162
- sessionType = 'user';
163
- baseURL = (window.location.origin === 'https://beta.verdocs.com' || window.location.origin === 'https://stage.verdocs.com'
164
- ? 'https://stage-api.verdocs.com'
165
- : 'https://api.verdocs.com');
166
- clientID = 'not-set';
167
- timeout = 60000;
168
- token = null;
169
- nextListenerId = 0;
170
- sessionListeners = new Map();
171
- requestLoggerId = null;
172
- /**
173
- * The current user session, or null if not authenticated. May be either a User or Signing session. If set, the
174
- * presence of the `document_id` field can be used to differentiate the types. Only signing sessions are associated
175
- * with Envelopes.
176
- */
177
- session = null;
178
- api;
179
- /**
180
- * Create a new VerdocsEndpoint to call Verdocs platform services.
181
- *
182
- * ```typescript
183
- * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
184
- * const endpoint = new VerdocsEndpoint();
185
- * ```
186
- */
187
- constructor(options) {
188
- this.baseURL = options?.baseURL || this.baseURL;
189
- this.timeout = options?.timeout || this.timeout;
190
- this.environment = options?.environment || this.environment;
191
- this.sessionType = options?.sessionType || this.sessionType;
192
- this.clientID = options?.clientID || this.clientID;
193
- this.api = axios.create({ baseURL: this.baseURL, timeout: this.timeout });
194
- }
195
- setDefault() {
196
- globalThis$1[ENDPOINT_KEY] = this;
197
- }
198
- static getDefault() {
199
- if (!globalThis$1[ENDPOINT_KEY]) {
200
- globalThis$1[ENDPOINT_KEY] = new VerdocsEndpoint();
201
- window.console.debug('[JS_SDK] Created default endpoint', globalThis$1[ENDPOINT_KEY].baseURL);
202
- }
203
- return globalThis$1[ENDPOINT_KEY];
204
- }
205
- /**
206
- * Get the current environment.
207
- */
208
- getEnvironment() {
209
- return this.environment;
210
- }
211
- /**
212
- * Get the current session type.
213
- */
214
- getSessionType() {
215
- return this.sessionType;
216
- }
217
- /**
218
- * Get the current base URL. This should rarely be anything other than 'https://api.verdocs.com'.
219
- */
220
- getBaseURL() {
221
- return this.baseURL;
222
- }
223
- /**
224
- * Get the current client ID, if set.
225
- */
226
- getClientID() {
227
- return this.clientID;
228
- }
229
- /**
230
- * Get the current timeout.
231
- */
232
- getTimeout() {
233
- return this.timeout;
234
- }
235
- /**
236
- * Get the current session, if any.
237
- */
238
- getSession() {
239
- return this.session;
240
- }
241
- /**
242
- * Set the operating environment. This should rarely be anything other than 'verdocs'.
243
- *
244
- * ```typescript
245
- * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
246
- *
247
- * const endpoint = new VerdocsEndpoint();
248
- * endpoint.setEnvironment('verdocs-stage');
249
- * ```
250
- */
251
- setEnvironment(environment) {
252
- this.environment = environment;
253
- return this;
254
- }
255
- /**
256
- * Set the session type. In general this should be done immediately when the endpoint is created. Changing the
257
- * session type may be done at any time, but may have unintended consequences if the endpoint is shared between
258
- * multiple widgets.
259
- *
260
- * Changing the session type will clear/reload the action session. This may trigger notifications to session state
261
- * observers. Apps that use observers to trigger UI updates such as logging the user out should be prepared to
262
- * handle this event.
263
- *
264
- * ```typescript
265
- * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
266
- *
267
- * const endpoint = new VerdocsEndpoint();
268
- * endpoint.setEnvironment('verdocs-stage');
269
- * ```
270
- */
271
- setSessionType(sessionType) {
272
- this.sessionType = sessionType;
273
- return this;
151
+ function timePeriod(type) {
152
+ let endDate = new Date().getTime();
153
+ const today = new Date();
154
+ const month = today.getMonth();
155
+ const year = today.getFullYear();
156
+ let startDate = null;
157
+ switch (type) {
158
+ case '30d':
159
+ startDate = endDate - 60 * 60 * 24 * 30 * 1000;
160
+ break;
161
+ case '60d':
162
+ startDate = endDate - 60 * 60 * 24 * 60 * 1000;
163
+ break;
164
+ case '6m':
165
+ startDate = endDate - 60 * 60 * 24 * 30 * 6 * 1000;
166
+ break;
167
+ case 'this_month':
168
+ startDate = new Date(year, month, 1).getTime();
169
+ break;
170
+ case 'last_month':
171
+ startDate = new Date(year, month - 1, 1).getTime();
172
+ endDate = new Date(year, month, 0).getTime();
173
+ break;
174
+ case 'this_year':
175
+ startDate = new Date(year, 0, 1);
176
+ break;
177
+ case 'all_time':
178
+ default:
179
+ return null;
274
180
  }
275
- /**
276
- * Set the base URL for API calls. Should be called only upon direction from Verdocs Customer Solutions Engineering.
277
- *
278
- * ```typescript
279
- * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
280
- *
281
- * const endpoint = new VerdocsEndpoint();
282
- * endpoint.setBaseURL('https://api.verdocs.com');
283
- * ```
284
- */
285
- setBaseURL(url) {
286
- this.baseURL = url;
287
- this.api.defaults.baseURL = url;
288
- return this;
181
+ if (startDate === null && endDate === null) {
182
+ return null;
289
183
  }
290
- /**
291
- * Set the Client ID for Verdocs API calls.
292
- *
293
- * ```typescript
294
- * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
295
- *
296
- * const endpoint = new VerdocsEndpoint();
297
- * endpoint.setClientID('1234);
298
- * ```
299
- */
300
- setClientID(clientID) {
301
- this.clientID = clientID;
302
- this.api.defaults.headers.common['X-Client-ID'] = clientID;
303
- return this;
184
+ return {
185
+ start_time: new Date(startDate).toISOString(),
186
+ end_time: new Date(endDate).toISOString(),
187
+ };
188
+ }
189
+
190
+ function getRTop(y, fieldHeight, iTextHeight, yRatio) {
191
+ return iTextHeight - (y + fieldHeight) * yRatio;
192
+ }
193
+ function getRLeft(x, ratio) {
194
+ return x * ratio;
195
+ }
196
+ function getRValue(y, ratio) {
197
+ return y * ratio;
198
+ }
199
+ function blobToBase64(image) {
200
+ const fileReader = new FileReader();
201
+ return new Promise((resolve, reject) => {
202
+ fileReader.onerror = () => {
203
+ reject(new DOMException('Problem reading blob.'));
204
+ };
205
+ fileReader.onload = () => {
206
+ resolve(fileReader.result);
207
+ };
208
+ fileReader.readAsDataURL(image);
209
+ });
210
+ }
211
+ function rescale(r, n) {
212
+ return r * n;
213
+ }
214
+
215
+ /**
216
+ * Given a File, extract the file's content as a base64 encoded data URL. The response will have a prefix that
217
+ * includes the MIME type of the file, e.g. "data:image/jpeg;base64,iVBORw0K......"
218
+ */
219
+ const fileToDataUrl = (file) => new Promise((resolve, reject) => {
220
+ const reader = new FileReader();
221
+ reader.onload = () => resolve({
222
+ lastModified: file.lastModified,
223
+ size: file.size,
224
+ type: file.type,
225
+ name: file.name,
226
+ data: reader.result,
227
+ });
228
+ reader.onerror = reject;
229
+ if (file) {
230
+ reader.readAsDataURL(file);
304
231
  }
305
- /**
306
- * Set the timeout for API calls in milliseconds. 5000-20000ms is recommended for most purposes. 15000ms is the default.
307
- * Note that some calls may involve rendering operations that require some time to complete, so very short timeouts
308
- * are not recommended.
309
- *
310
- * ```typescript
311
- * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
312
- *
313
- * const endpoint = new VerdocsEndpoint();
314
- * endpoint.setTimeout(3000);
315
- * ```
316
- */
317
- setTimeout(timeout) {
318
- this.timeout = timeout;
319
- this.api.defaults.timeout = timeout;
320
- return this;
232
+ else {
233
+ reject(new Error('Invalid file'));
321
234
  }
322
- /**
323
- * Enable or disable request logging. This may expose sensitive data in the console log, so it should only be used for debugging.
324
- *
325
- * ```typescript
326
- * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
327
- *
328
- * const endpoint = new VerdocsEndpoint();
329
- * endpoint.logRequests(true);
330
- * ```
331
- */
332
- logRequests(enable) {
333
- if (enable && this.requestLoggerId === null) {
334
- this.requestLoggerId = this.api.interceptors.request.use(requestLogger);
335
- }
336
- else if (!enable && this.requestLoggerId !== null) {
337
- this.api.interceptors.request.eject(this.requestLoggerId);
338
- }
339
- return this;
340
- }
341
- /**
342
- * Set the authorization token that will be used for Verdocs API calls. This will also set the session metadata
343
- * and notify any listeners of the new data.
344
- *
345
- * If this Endpoint will be used for non-default purposes (e.g. signing, or in an alternate environment) those
346
- * settings should be made before calling this. Sessions are persisted to localStorage, and the environment and
347
- * type become part of the storage key.
348
- *
349
- * ```typescript
350
- * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
351
- *
352
- * const endpoint = new VerdocsEndpoint();
353
- * endpoint.setToken(accessToken);
354
- * ```
355
- */
356
- setToken(token) {
357
- if (!token) {
358
- return this.clearSession();
359
- }
360
- const session = decodeAccessTokenBody(token);
361
- if (session === null || (session.exp && session.exp * 1000 < new Date().getTime())) {
362
- window.console.warn('[JS_SDK] Ignoring attempt to use expired session token');
363
- return this.clearSession();
364
- }
365
- this.token = token;
366
- this.session = session;
367
- if (this.sessionType === 'user') {
368
- this.api.defaults.headers.common.Authorization = `Bearer ${token}`;
369
- }
370
- else {
371
- this.api.defaults.headers.common.signer = `Bearer ${token}`;
372
- }
373
- localStorage.setItem(this.sessionStorageKey(), token);
374
- this.notifySessionListeners();
375
- return this;
376
- }
377
- /**
378
- * Retrieves the current session token, if any. Tokens should rarely be used for direct actions, but this is
379
- * required by the `<VerdocsView>` and other components to authorize requests to raw PDF files.
380
- */
381
- getToken() {
382
- return this.token;
383
- }
384
- sessionStorageKey() {
385
- return `verdocs-session-${this.getSessionType()}-${this.getEnvironment()}`;
386
- }
387
- /**
388
- * Clear the active session.
389
- */
390
- clearSession() {
391
- localStorage.removeItem(this.sessionStorageKey());
392
- delete this.api.defaults.headers.common.Authorization;
393
- delete this.api.defaults.headers.common.signer;
394
- this.session = null;
395
- this.token = null;
396
- this.notifySessionListeners();
397
- return this;
398
- }
399
- /**
400
- * Clear the active signing session.
401
- */
402
- clearSignerSession() {
403
- localStorage.removeItem(this.sessionStorageKey());
404
- delete this.api.defaults.headers.common.Authorization;
405
- this.session = null;
406
- this.token = null;
407
- this.notifySessionListeners();
408
- return this;
409
- }
410
- notifySessionListeners() {
411
- this.sessionListeners.forEach((listener) => {
412
- try {
413
- listener(this, this.session);
414
- }
415
- catch (e) {
416
- // NOOP
417
- }
418
- });
419
- }
420
- /**
421
- * Subscribe to session state change events.
422
- */
423
- onSessionChanged(listener) {
424
- // There's no value in randomizing this, a simple counter is fine
425
- this.nextListenerId++;
426
- const listenerSymbol = Symbol.for('' + this.nextListenerId);
427
- this.sessionListeners.set(listenerSymbol, listener);
428
- return () => {
429
- this.sessionListeners.delete(listenerSymbol);
430
- };
431
- }
432
- /**
433
- * Load a persisted session from localStorage. Typically called once after the endpoint is configured when the app
434
- * or component starts.
435
- */
436
- loadSession() {
437
- const token = localStorage.getItem(this.sessionStorageKey());
438
- if (!token) {
439
- return this.clearSession();
440
- }
441
- return this.setToken(token);
442
- }
443
- }
444
-
445
- /**
446
- * Given a `rgba(r,g,b,a)` string value, returns the hex equivalent, dropping the alpha channel.
447
- */
448
- function getRGB(rgba) {
449
- const rgbNumbers = rgba.replace('rgba(', '').replace(')', '').split(',');
450
- const rgbObject = {
451
- red: +rgbNumbers[0],
452
- green: +rgbNumbers[1],
453
- blue: +rgbNumbers[2],
454
- alpha: +rgbNumbers[3],
455
- };
456
- const alpha = 1 - rgbObject.alpha;
457
- const red = Math.round((rgbObject.alpha * (rgbObject.red / 255) + alpha) * 255);
458
- const green = Math.round((rgbObject.alpha * (rgbObject.green / 255) + alpha) * 255);
459
- const blue = Math.round((rgbObject.alpha * (rgbObject.blue / 255) + alpha) * 255);
460
- return '#' + rgbToHex(red) + rgbToHex(green) + rgbToHex(blue);
461
- }
462
- /**
463
- * Given an RGB string value, returns the hex equivalent.
464
- */
465
- function rgbToHex(rgb) {
466
- const hex = rgb.toString(16);
467
- if (hex.length < 2) {
468
- return '0' + hex;
469
- }
470
- return hex;
471
- }
472
- /**
473
- * Given a signer role index, return the color code for that signer.
474
- */
475
- function getRGBA(roleIndex) {
476
- switch (roleIndex % 10) {
477
- case 0:
478
- return roleIndex === 0 ? 'rgba(255, 193, 7, 0.4)' : 'rgba(134, 134, 134, 0.3)'; // #FFE69C
479
- case 1:
480
- return 'rgba(156, 39, 176, .4)'; // '#E3C3E9'
481
- case 2:
482
- return 'rgba(33, 150, 243, .4)'; // '#C1E1FB'
483
- case 3:
484
- return 'rgba(220, 231, 117, 0.3)';
485
- case 4:
486
- return 'rgba(121, 134, 203, 0.3)';
487
- case 5:
488
- return 'rgba(77, 182, 172, 0.3)';
489
- case 6:
490
- return 'rgba(255, 202, 165, 0.3)';
491
- case 7:
492
- return 'rgba(2, 247, 190, 0.3)';
493
- case 8:
494
- return 'rgba(255, 138, 101, 0.3)';
495
- case 9:
496
- return 'rgba(82, 255, 79, 0.3)';
497
- default:
498
- return 'rgba(229, 115, 155, 0.3)';
499
- }
500
- }
501
- /**
502
- * Given a role name, return a color code for it. This works by computing a hash code so the specific color returned
503
- * is not specified explicitly, but will be the same for every call with the same input value.
504
- */
505
- function nameToRGBA(str) {
506
- if (!!str) {
507
- const validNum = parseInt(str.slice(-1), 10);
508
- if (!isNaN(validNum)) {
509
- str += (validNum * 99).toString();
510
- }
511
- let hash = 0;
512
- for (let i = 0; i < str.length; i++) {
513
- // tslint:disable-next-line:no-bitwise
514
- hash = str.charCodeAt(i) + ((hash << 5) - hash);
515
- }
516
- hash = Math.round(hash / 1.3);
517
- // tslint:disable-next-line:no-bitwise
518
- const c = (hash & 0x00ffff08).toString(16).toUpperCase();
519
- const hex = '#' + '00000'.substring(0, 6 - c.length) + c;
520
- const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
521
- const color = {
522
- r: parseInt(result[1], 16),
523
- g: parseInt(result[2], 16),
524
- b: parseInt(result[3], 16),
525
- };
526
- return `rgba(${color.r}, ${color.g}, ${color.b}, 0.2)`;
527
- }
528
- }
235
+ });
529
236
  /**
530
- * Helper function to obtain a color code given a role name given various possible inputs.
237
+ * Trigger a download dialog to save a blob as a file on disk.
531
238
  */
532
- function getRoleColor(name, roles, index) {
533
- if (index) {
534
- return getRGBA(index);
535
- }
536
- else if (roles && roles.length > 0) {
537
- const roleIndex = roles.findIndex((role) => role === name);
538
- if (roleIndex > -1) {
539
- return getRGBA(roleIndex);
540
- }
541
- else {
542
- return nameToRGBA(name);
543
- }
544
- }
545
- else {
546
- return nameToRGBA(name);
547
- }
548
- }
549
-
550
- const YEAR = 365 * 24 * 60 * 60;
551
- // const MONTH = 30 * 24 * 60 * 60;
552
- const WEEK = 7 * 24 * 60 * 60;
553
- const DAY = 24 * 60 * 60;
554
- const HOUR = 60 * 60;
555
- const MINUTE = 60;
556
- const formatShortTimeAgo = (val) => {
557
- if (val === undefined || val === null) {
558
- return '';
559
- }
560
- let dateInput;
561
- if (typeof val === 'string' || typeof val === 'number') {
562
- dateInput = new Date(val);
563
- }
564
- else if (typeof val === 'object') {
565
- dateInput = val;
566
- }
567
- else {
568
- return '';
569
- }
570
- const timeDiff = Math.floor((new Date().getTime() - dateInput.getTime()) / 1000);
571
- if (timeDiff >= YEAR) {
572
- return Math.floor(timeDiff / YEAR) + 'Y';
573
- }
574
- // if (timeDiff >= MONTH) {
575
- // return Math.floor(timeDiff / MONTH) + 'M';
576
- // }
577
- if (timeDiff >= WEEK) {
578
- return Math.floor(timeDiff / WEEK) + 'W';
579
- }
580
- if (timeDiff >= DAY) {
581
- return Math.floor(timeDiff / DAY) + 'D';
582
- }
583
- if (timeDiff >= HOUR) {
584
- return Math.floor(timeDiff / HOUR) + 'H';
585
- }
586
- if (timeDiff >= MINUTE) {
587
- return Math.floor(timeDiff / MINUTE) + 'M';
588
- }
589
- return `${timeDiff}S`;
590
- };
591
- function timePeriod(type) {
592
- let endDate = new Date().getTime();
593
- const today = new Date();
594
- const month = today.getMonth();
595
- const year = today.getFullYear();
596
- let startDate = null;
597
- switch (type) {
598
- case '30d':
599
- startDate = endDate - 60 * 60 * 24 * 30 * 1000;
600
- break;
601
- case '60d':
602
- startDate = endDate - 60 * 60 * 24 * 60 * 1000;
603
- break;
604
- case '6m':
605
- startDate = endDate - 60 * 60 * 24 * 30 * 6 * 1000;
606
- break;
607
- case 'this_month':
608
- startDate = new Date(year, month, 1).getTime();
609
- break;
610
- case 'last_month':
611
- startDate = new Date(year, month - 1, 1).getTime();
612
- endDate = new Date(year, month, 0).getTime();
613
- break;
614
- case 'this_year':
615
- startDate = new Date(year, 0, 1);
616
- break;
617
- case 'all_time':
618
- default:
619
- return null;
620
- }
621
- if (startDate === null && endDate === null) {
622
- return null;
623
- }
624
- return {
625
- start_time: new Date(startDate).toISOString(),
626
- end_time: new Date(endDate).toISOString(),
627
- };
628
- }
629
-
630
- function getRTop(y, fieldHeight, iTextHeight, yRatio) {
631
- return iTextHeight - (y + fieldHeight) * yRatio;
632
- }
633
- function getRLeft(x, ratio) {
634
- return x * ratio;
635
- }
636
- function getRValue(y, ratio) {
637
- return y * ratio;
638
- }
639
- function blobToBase64(image) {
640
- const fileReader = new FileReader();
641
- return new Promise((resolve, reject) => {
642
- fileReader.onerror = () => {
643
- reject(new DOMException('Problem reading blob.'));
644
- };
645
- fileReader.onload = () => {
646
- resolve(fileReader.result);
647
- };
648
- fileReader.readAsDataURL(image);
649
- });
650
- }
651
- function rescale(r, n) {
652
- return r * n;
653
- }
654
-
655
- /**
656
- * Given a File, extract the file's content as a base64 encoded data URL. The response will have a prefix that
657
- * includes the MIME type of the file, e.g. "data:image/jpeg;base64,iVBORw0K......"
658
- */
659
- const fileToDataUrl = (file) => new Promise((resolve, reject) => {
660
- const reader = new FileReader();
661
- reader.onload = () => resolve({
662
- lastModified: file.lastModified,
663
- size: file.size,
664
- type: file.type,
665
- name: file.name,
666
- data: reader.result,
667
- });
668
- reader.onerror = reject;
669
- if (file) {
670
- reader.readAsDataURL(file);
671
- }
672
- else {
673
- reject(new Error('Invalid file'));
674
- }
675
- });
676
- /**
677
- * Trigger a download dialog to save a blob as a file on disk.
678
- */
679
- const downloadBlob = (blob, name = 'file.pdf') => {
680
- const blobUrl = URL.createObjectURL(blob);
681
- const link = document.createElement('a');
682
- link.href = blobUrl;
683
- link.download = name;
684
- document.body.appendChild(link);
685
- link.dispatchEvent(new MouseEvent('click', {
686
- bubbles: true,
687
- cancelable: true,
688
- view: window,
689
- }));
690
- document.body.removeChild(link);
691
- };
239
+ const downloadBlob = (blob, name = 'file.pdf') => {
240
+ const blobUrl = URL.createObjectURL(blob);
241
+ const link = document.createElement('a');
242
+ link.href = blobUrl;
243
+ link.download = name;
244
+ document.body.appendChild(link);
245
+ link.dispatchEvent(new MouseEvent('click', {
246
+ bubbles: true,
247
+ cancelable: true,
248
+ view: window,
249
+ }));
250
+ document.body.removeChild(link);
251
+ };
692
252
 
693
253
  const Countries = [
694
254
  { code: '+7 840', name: 'Abkhazia', value: '+7' },
@@ -935,179 +495,644 @@ function getCountryByCode(code) {
935
495
  if (isFrenchGuiana(code)) {
936
496
  return { code: '+594', name: 'French Guiana', value: '+594' };
937
497
  }
938
- else if (isGuadeloupe(code)) {
939
- return { code: '+590', name: 'Guadeloupe', value: '+590' };
498
+ else if (isGuadeloupe(code)) {
499
+ return { code: '+590', name: 'Guadeloupe', value: '+590' };
500
+ }
501
+ else if (isMartinique(code)) {
502
+ return { code: '+596', name: 'Martinique', value: '+596' };
503
+ }
504
+ else if (isMayotte(code)) {
505
+ return { code: '+262', name: 'Mayotte or Réunion', value: '+262' };
506
+ }
507
+ return null;
508
+ }
509
+ function isFrenchGuiana(code) {
510
+ return '+594' === code.substring(0, 4);
511
+ }
512
+ function isGuadeloupe(code) {
513
+ return '+590' === code.substring(0, 4);
514
+ }
515
+ function isMartinique(code) {
516
+ return '+596' === code.substring(0, 4);
517
+ }
518
+ function isMayotte(code) {
519
+ return '+262' === code.substring(0, 4);
520
+ }
521
+ function getPlusOneCountry(code) {
522
+ let info = null;
523
+ switch (code.substring(0, 5)) {
524
+ case '+1684':
525
+ info = { code: '+1', name: 'American Samoa', value: '+1' };
526
+ break;
527
+ case '+1264':
528
+ info = { code: '+1', name: 'Anguilla', value: '+1' };
529
+ break;
530
+ case '+1268':
531
+ info = { code: '+1', name: 'Antigua and Barbuda', value: '+1' };
532
+ break;
533
+ case '+1242':
534
+ info = { code: '+1', name: 'Bahamas', value: '+1' };
535
+ break;
536
+ case '+1246':
537
+ info = { code: '+1', name: 'Barbados', value: '+1' };
538
+ break;
539
+ case '+1441':
540
+ info = { code: '+1', name: 'Bermuda', value: '+1' };
541
+ break;
542
+ case '+1284':
543
+ info = { code: '+1', name: 'British Virgin Islands', value: '+1' };
544
+ break;
545
+ case '+1':
546
+ info = { code: '+1', name: '', value: '+1' };
547
+ break;
548
+ }
549
+ return info;
550
+ }
551
+ function isCanada(code) {
552
+ const canadianAreaCodes = [
553
+ '403',
554
+ '587',
555
+ '780',
556
+ '825',
557
+ '604',
558
+ '250',
559
+ '778',
560
+ '236',
561
+ '204',
562
+ '431',
563
+ '506',
564
+ '709',
565
+ '867',
566
+ '782',
567
+ '902',
568
+ '867',
569
+ '548',
570
+ '705',
571
+ '365',
572
+ '613',
573
+ '807',
574
+ '226',
575
+ '289',
576
+ '437',
577
+ '519',
578
+ '647',
579
+ '905',
580
+ '249',
581
+ '343',
582
+ '416',
583
+ '902',
584
+ '782',
585
+ '450',
586
+ '418',
587
+ '579',
588
+ '873',
589
+ '367',
590
+ '514',
591
+ '581',
592
+ '819',
593
+ '438',
594
+ '639',
595
+ '306',
596
+ '867',
597
+ ];
598
+ const areaCode = code.substring(0, 5);
599
+ return canadianAreaCodes.findIndex((x) => '+1' + x === areaCode) > -1;
600
+ }
601
+ function isAmericanSamoa(code) {
602
+ return code.substring(0, 5) === '+1684';
603
+ }
604
+ function isDominicanRepublic(code) {
605
+ return '+1809' === code.substring(0, 5) || '+1829' === code.substring(0, 5) || '+1849' === code.substring(0, 5);
606
+ }
607
+ function isPuertoRico(code) {
608
+ return code.substring(0, 5) === '+' || code.substring(0, 5) === '+';
609
+ }
610
+ // need to finish
611
+ function getMatchingCountry(code, substrings) {
612
+ const toMatch = code.substring(0, substrings);
613
+ return Countries.filter((c) => c.code === toMatch).length;
614
+ }
615
+ // const e164Regex = new RegExp(/\+[1-9]\d{6,14}/g);
616
+ // export function simpleE164Validator(code: string) {
617
+ // return (code !== null && code.length < 16 && code.length > 6 && e164Regex.test(code)) || code === '' || code === null;
618
+ // }
619
+
620
+ /**
621
+ * Capitalize the first letter of a string.
622
+ */
623
+ const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
624
+ /**
625
+ * Convert a phone-number-like string to E164 format.
626
+ * @see https://46elks.com/kb/e164
627
+ */
628
+ const convertToE164 = (input) => {
629
+ // "(212) 555-1212" => +12125551212
630
+ // "+46766861004" => "+46766861004"
631
+ // "212-555-1212" => +12125551212
632
+ // "212.555.1212" => +12125551212
633
+ // "212 555 1212" => +12125551212
634
+ let temp = (input || '').trim();
635
+ // If we are already prefixed, assume the user did it deliberately and attempt to use what they entered. We also short-circuit blanks.
636
+ if (!temp || temp.startsWith('+')) {
637
+ return temp;
638
+ }
639
+ // Remove any spaces, parenthesis or other punctuation.
640
+ temp = temp.replace(/[^0-9]/g, '');
641
+ // If the number begins with a zero, remove the leading zero. Do not combine this with the previous step because it needs to be removed
642
+ // whether it's the actual first character e.g. `0(5)` or just the first digit e.g. `(05`.
643
+ temp = temp.replace(/^0/g, '');
644
+ // Prepend the country code and +. We're assuming US in this case given the target demographic. Users in other countries would/should be
645
+ // already entering a prefix so they'd shortcut out of this routine via the + prefix check.
646
+ return `+1${temp}`;
647
+ };
648
+
649
+ /**
650
+ * Create an array containing a sequence of integers, e.g. [START, START+1, START+2, ...] This is frequently useful
651
+ * in rendering operations when there is no source array to .map() across.
652
+ */
653
+ const integerSequence = (start, count) => Array(count)
654
+ .fill(1)
655
+ .map((_, index) => index + start);
656
+ /**
657
+ * Format a profile's full name
658
+ */
659
+ const formatFullName = (profile) => profile ? `${capitalize(profile.first_name)} ${capitalize(profile.last_name)}` : 'Invalid User';
660
+ /**
661
+ * Format a profile's initials
662
+ */
663
+ const formatInitials = (profile) => profile ? `${capitalize(profile.first_name).charAt(0)} ${capitalize(profile.last_name).charAt(0)}` : '--';
664
+ /**
665
+ * Generate suggested initials for a full name, e.g. "John Doe" will yield "JD".
666
+ */
667
+ const fullNameToInitials = (name) => name
668
+ .split(' ')
669
+ .map((word) => word[0])
670
+ .join('');
671
+
672
+ /* tslint:disable:no-bitwise */
673
+ const b64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
674
+ // Regular expression to check formal correctness of base64 encoded strings
675
+ const b64re = /^(?:[A-Za-z\d+\/]{4})*?(?:[A-Za-z\d+\/]{2}(?:==)?|[A-Za-z\d+\/]{3}=?)?$/;
676
+ /**
677
+ * Simplified, Node/Browser-safe alternative to atob() for base64 decoding.
678
+ * Modified from https://github.com/MaxArt2501/base64-js/blob/master/base64.js
679
+ */
680
+ const AtoB = (str) => {
681
+ // atob can work with strings with whitespaces, even inside the encoded part,
682
+ // but only \t, \n, \f, \r and ' ', which can be stripped.
683
+ str = String(str).replace(/[\t\n\f\r ]+/g, '');
684
+ if (!b64re.test(str))
685
+ throw new TypeError("Failed to execute 'atob' on 'Window': The string to be decoded is not correctly encoded.");
686
+ // Adding the padding if missing, for semplicity
687
+ str += '=='.slice(2 - (str.length & 3));
688
+ let bitmap;
689
+ let result = '';
690
+ let r1;
691
+ let r2;
692
+ let i = 0;
693
+ for (; i < str.length;) {
694
+ bitmap =
695
+ (b64.indexOf(str.charAt(i++)) << 18) |
696
+ (b64.indexOf(str.charAt(i++)) << 12) |
697
+ ((r1 = b64.indexOf(str.charAt(i++))) << 6) |
698
+ (r2 = b64.indexOf(str.charAt(i++)));
699
+ result +=
700
+ r1 === 64
701
+ ? String.fromCharCode((bitmap >> 16) & 255)
702
+ : r2 === 64
703
+ ? String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255)
704
+ : String.fromCharCode((bitmap >> 16) & 255, (bitmap >> 8) & 255, bitmap & 255);
705
+ }
706
+ return result;
707
+ };
708
+ /**
709
+ * Decode the body of a JWT. This helper may allow front-end applications to avoid a dependency on `jsonwebtoken` in
710
+ * many cases. Note that this should only be used for true JWTs. Opaque tokens will cause this to throw.
711
+ */
712
+ const decodeJWTBody = (token) => JSON.parse(AtoB((token || '').split('.')[1] || ''));
713
+ /**
714
+ * Decode the body of an Verdocs access token. Note that raw tokens contain namespaced fields, e.g.
715
+ * `https://verdocs.com/profile_id`. To make these tokens easier to use in front-end code, this name-spacing
716
+ * will be removed. Note that user and signing sessions have different access token formats. The calling
717
+ * application should distinguish between the two based on the context of the authenticated session, or by
718
+ * the presence of the `document_id` field, which will only be present for signing sessions.
719
+ */
720
+ const decodeAccessTokenBody = (token) => {
721
+ let decoded;
722
+ try {
723
+ decoded = decodeJWTBody(token);
724
+ if (decoded === null) {
725
+ return null;
726
+ }
727
+ }
728
+ catch (e) {
729
+ return null;
730
+ }
731
+ Object.keys(decoded).forEach((key) => {
732
+ if (typeof key === 'string' && key.startsWith('https://verdocs.com/')) {
733
+ decoded[key.replace('https://verdocs.com/', '')] = decoded[key];
734
+ delete decoded[key];
735
+ }
736
+ });
737
+ return decoded;
738
+ };
739
+
740
+ function getDefaultExportFromCjs (x) {
741
+ return x && x.__esModule && Object.prototype.hasOwnProperty.call(x, 'default') ? x['default'] : x;
742
+ }
743
+
744
+ // This file provides a polyfill for managing globals in both NodeJS and browser environments. This is
745
+ // an anti-pattern we'd hoped to avoid, but we have several projects dependending on one common library
746
+ // (this js-sdk) and we want that library to provide a common endpoint to all callers (so authentication
747
+ // tokens only need to be tracked in one place). The trouble is, one of those libraries is based on
748
+ // StencilJS and is compiling its modules into Web Components. Because of how module resolution works,
749
+ // when those Components load js-sdk they get a separate instance. Without messy options like having to
750
+ // pass raw data from the caller to each Component, or pass around references to a common Endpoint, they
751
+ // have no way to access authenticated sessions unless we make the Endpoint a true global.
752
+ //
753
+ // @credit https://github.com/medikoo/es5-ext/blob/master/global.js
754
+ // @credit https://mathiasbynens.be/notes/globalthis
755
+ var naiveFallback = function () {
756
+ if (typeof self === 'object' && self)
757
+ return self;
758
+ if (typeof window === 'object' && window)
759
+ return window;
760
+ throw new Error('Unable to resolve global `this`');
761
+ };
762
+ var globalThis_1 = (function () {
763
+ if (this)
764
+ return this;
765
+ // Unexpected strict mode (may happen if e.g. bundled into ESM module)
766
+ // Fallback to standard globalThis if available
767
+ if (typeof globalThis === 'object' && globalThis)
768
+ return globalThis;
769
+ // Thanks @mathiasbynens -> https://mathiasbynens.be/notes/globalthis
770
+ // In all ES5+ engines global object inherits from Object.prototype
771
+ // (if you approached one that doesn't please report)
772
+ try {
773
+ Object.defineProperty(Object.prototype, '__global__', {
774
+ get: function () {
775
+ return this;
776
+ },
777
+ configurable: true,
778
+ });
779
+ }
780
+ catch (error) {
781
+ // Unfortunate case of updates to Object.prototype being restricted
782
+ // via preventExtensions, seal or freeze
783
+ return naiveFallback();
784
+ }
785
+ try {
786
+ // Safari case (window.__global__ works, but __global__ does not)
787
+ if (!__global__)
788
+ return naiveFallback();
789
+ return __global__;
790
+ }
791
+ finally {
792
+ delete Object.prototype.__global__;
793
+ }
794
+ })();
795
+
796
+ var globalThis$1 = /*@__PURE__*/getDefaultExportFromCjs(globalThis_1);
797
+
798
+ // @credit https://derickbailey.com/2016/03/09/creating-a-true-singleton-in-node-js-with-es6-symbols/
799
+ // Also see globalThis for comments about why we're doing this in the first place.
800
+ const ENDPOINT_KEY = Symbol.for('vƒbaseerdocs-default-endpoint');
801
+ const requestLogger = (r) => {
802
+ // tslint:disable-next-line
803
+ console.debug(`[JS-SDK] ${r.method.toUpperCase()} ${r.baseURL}${r.url}`, r.data ? JSON.stringify(r.data) : '');
804
+ return r;
805
+ };
806
+ /**
807
+ * VerdocsEndpoint is a class wrapper for a specific connection and authorization context for calling the Verdocs APIs.
808
+ * Endpoints can be used for isolated session tasks.
809
+ *
810
+ * For instance, ephemeral signing sessions may be created independently of a caller's status as an authenticated user.
811
+ * In that case, an Endpoint can be created and authenticated, used for calls related to signing operations, then
812
+ * discarded once signing is complete.
813
+ *
814
+ * Note that endpoint configuration functions return the instance, so they can be chained, e.g.
815
+ *
816
+ * ```typescript
817
+ * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
818
+ *
819
+ * const endpoint = new VerdocsEndpoint();
820
+ * endpoint
821
+ * .setSessionType('signing')
822
+ * .logRequests(true)
823
+ * .setClientID('1234)
824
+ * .setTimeout(30000);
825
+ * ```
826
+ */
827
+ class VerdocsEndpoint {
828
+ environment = 'verdocs';
829
+ sessionType = 'user';
830
+ baseURL = (window.location.origin === 'https://beta.verdocs.com' || window.location.origin === 'https://stage.verdocs.com'
831
+ ? 'https://stage-api.verdocs.com'
832
+ : 'https://api.verdocs.com');
833
+ baseURLv2 = (window.location.origin === 'https://beta.verdocs.com' || window.location.origin === 'https://stage.verdocs.com'
834
+ ? 'https://stage-api.verdocs.com/v2'
835
+ : 'https://api.verdocs.com/v2');
836
+ clientID = 'not-set';
837
+ timeout = 60000;
838
+ token = null;
839
+ nextListenerId = 0;
840
+ sessionListeners = new Map();
841
+ requestLoggerId = null;
842
+ /**
843
+ * The current user session, or null if not authenticated. May be either a User or Signing session. If set, the
844
+ * presence of the `document_id` field can be used to differentiate the types. Only signing sessions are associated
845
+ * with Envelopes.
846
+ */
847
+ session = null;
848
+ api;
849
+ /**
850
+ * Create a new VerdocsEndpoint to call Verdocs platform services.
851
+ *
852
+ * ```typescript
853
+ * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
854
+ * const endpoint = new VerdocsEndpoint();
855
+ * ```
856
+ */
857
+ constructor(options) {
858
+ this.baseURL = options?.baseURL || this.baseURL;
859
+ this.timeout = options?.timeout || this.timeout;
860
+ this.environment = options?.environment || this.environment;
861
+ this.sessionType = options?.sessionType || this.sessionType;
862
+ this.clientID = options?.clientID || this.clientID;
863
+ this.api = axios.create({ baseURL: this.baseURL, timeout: this.timeout });
864
+ }
865
+ setDefault() {
866
+ globalThis$1[ENDPOINT_KEY] = this;
867
+ }
868
+ static getDefault() {
869
+ if (!globalThis$1[ENDPOINT_KEY]) {
870
+ globalThis$1[ENDPOINT_KEY] = new VerdocsEndpoint();
871
+ window.console.debug('[JS_SDK] Created default endpoint', globalThis$1[ENDPOINT_KEY].baseURL);
872
+ }
873
+ return globalThis$1[ENDPOINT_KEY];
874
+ }
875
+ /**
876
+ * Get the current environment.
877
+ */
878
+ getEnvironment() {
879
+ return this.environment;
880
+ }
881
+ /**
882
+ * Get the current session type.
883
+ */
884
+ getSessionType() {
885
+ return this.sessionType;
886
+ }
887
+ /**
888
+ * Get the current base URL. This should rarely be anything other than 'https://api.verdocs.com'.
889
+ */
890
+ getBaseURL() {
891
+ return this.baseURL;
892
+ }
893
+ /**
894
+ * Get the current base URL for the v2 APIs.
895
+ * This should rarely be anything other than 'https://api-v2.verdocs.com'.
896
+ */
897
+ getBaseURLv2() {
898
+ return this.baseURLv2;
899
+ }
900
+ /**
901
+ * Get the current client ID, if set.
902
+ */
903
+ getClientID() {
904
+ return this.clientID;
905
+ }
906
+ /**
907
+ * Get the current timeout.
908
+ */
909
+ getTimeout() {
910
+ return this.timeout;
911
+ }
912
+ /**
913
+ * Get the current session, if any.
914
+ */
915
+ getSession() {
916
+ return this.session;
917
+ }
918
+ /**
919
+ * Set the operating environment. This should rarely be anything other than 'verdocs'.
920
+ *
921
+ * ```typescript
922
+ * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
923
+ *
924
+ * const endpoint = new VerdocsEndpoint();
925
+ * endpoint.setEnvironment('verdocs-stage');
926
+ * ```
927
+ */
928
+ setEnvironment(environment) {
929
+ this.environment = environment;
930
+ return this;
931
+ }
932
+ /**
933
+ * Set the session type. In general this should be done immediately when the endpoint is created. Changing the
934
+ * session type may be done at any time, but may have unintended consequences if the endpoint is shared between
935
+ * multiple widgets.
936
+ *
937
+ * Changing the session type will clear/reload the action session. This may trigger notifications to session state
938
+ * observers. Apps that use observers to trigger UI updates such as logging the user out should be prepared to
939
+ * handle this event.
940
+ *
941
+ * ```typescript
942
+ * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
943
+ *
944
+ * const endpoint = new VerdocsEndpoint();
945
+ * endpoint.setEnvironment('verdocs-stage');
946
+ * ```
947
+ */
948
+ setSessionType(sessionType) {
949
+ this.sessionType = sessionType;
950
+ return this;
951
+ }
952
+ /**
953
+ * Set the base URL for API calls. Should be called only upon direction from Verdocs Customer Solutions Engineering.
954
+ *
955
+ * ```typescript
956
+ * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
957
+ *
958
+ * const endpoint = new VerdocsEndpoint();
959
+ * endpoint.setBaseURL('https://api.verdocs.com');
960
+ * ```
961
+ */
962
+ setBaseURL(url) {
963
+ this.baseURL = url;
964
+ this.api.defaults.baseURL = url;
965
+ return this;
966
+ }
967
+ /**
968
+ * Set the base URL for API calls. Should be called only upon direction from Verdocs Customer Solutions Engineering.
969
+ *
970
+ * ```typescript
971
+ * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
972
+ *
973
+ * const endpoint = new VerdocsEndpoint();
974
+ * endpoint.setBaseURL('https://api.verdocs.com');
975
+ * ```
976
+ */
977
+ setBaseURLv2(url) {
978
+ this.baseURLv2 = url;
979
+ // NOTE: We do not set this on the Axios instance because v1 is still the standard.
980
+ return this;
981
+ }
982
+ /**
983
+ * Set the Client ID for Verdocs API calls.
984
+ *
985
+ * ```typescript
986
+ * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
987
+ *
988
+ * const endpoint = new VerdocsEndpoint();
989
+ * endpoint.setClientID('1234);
990
+ * ```
991
+ */
992
+ setClientID(clientID) {
993
+ this.clientID = clientID;
994
+ this.api.defaults.headers.common['X-Client-ID'] = clientID;
995
+ return this;
996
+ }
997
+ /**
998
+ * Set the timeout for API calls in milliseconds. 5000-20000ms is recommended for most purposes. 15000ms is the default.
999
+ * Note that some calls may involve rendering operations that require some time to complete, so very short timeouts
1000
+ * are not recommended.
1001
+ *
1002
+ * ```typescript
1003
+ * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
1004
+ *
1005
+ * const endpoint = new VerdocsEndpoint();
1006
+ * endpoint.setTimeout(3000);
1007
+ * ```
1008
+ */
1009
+ setTimeout(timeout) {
1010
+ this.timeout = timeout;
1011
+ this.api.defaults.timeout = timeout;
1012
+ return this;
1013
+ }
1014
+ /**
1015
+ * Enable or disable request logging. This may expose sensitive data in the console log, so it should only be used for debugging.
1016
+ *
1017
+ * ```typescript
1018
+ * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
1019
+ *
1020
+ * const endpoint = new VerdocsEndpoint();
1021
+ * endpoint.logRequests(true);
1022
+ * ```
1023
+ */
1024
+ logRequests(enable) {
1025
+ if (enable && this.requestLoggerId === null) {
1026
+ this.requestLoggerId = this.api.interceptors.request.use(requestLogger);
1027
+ }
1028
+ else if (!enable && this.requestLoggerId !== null) {
1029
+ this.api.interceptors.request.eject(this.requestLoggerId);
1030
+ }
1031
+ return this;
1032
+ }
1033
+ /**
1034
+ * Set the authorization token that will be used for Verdocs API calls. This will also set the session metadata
1035
+ * and notify any listeners of the new data.
1036
+ *
1037
+ * If this Endpoint will be used for non-default purposes (e.g. signing, or in an alternate environment) those
1038
+ * settings should be made before calling this. Sessions are persisted to localStorage, and the environment and
1039
+ * type become part of the storage key.
1040
+ *
1041
+ * ```typescript
1042
+ * import {VerdocsEndpoint} from '@verdocs/js-sdk/HTTP';
1043
+ *
1044
+ * const endpoint = new VerdocsEndpoint();
1045
+ * endpoint.setToken(accessToken);
1046
+ * ```
1047
+ */
1048
+ setToken(token) {
1049
+ if (!token) {
1050
+ return this.clearSession();
1051
+ }
1052
+ const session = decodeAccessTokenBody(token);
1053
+ if (session === null || (session.exp && session.exp * 1000 < new Date().getTime())) {
1054
+ window.console.warn('[JS_SDK] Ignoring attempt to use expired session token');
1055
+ return this.clearSession();
1056
+ }
1057
+ this.token = token;
1058
+ this.session = session;
1059
+ if (this.sessionType === 'user') {
1060
+ this.api.defaults.headers.common.Authorization = `Bearer ${token}`;
1061
+ }
1062
+ else {
1063
+ this.api.defaults.headers.common.signer = `Bearer ${token}`;
1064
+ }
1065
+ localStorage.setItem(this.sessionStorageKey(), token);
1066
+ this.notifySessionListeners();
1067
+ return this;
1068
+ }
1069
+ /**
1070
+ * Retrieves the current session token, if any. Tokens should rarely be used for direct actions, but this is
1071
+ * required by the `<VerdocsView>` and other components to authorize requests to raw PDF files.
1072
+ */
1073
+ getToken() {
1074
+ return this.token;
940
1075
  }
941
- else if (isMartinique(code)) {
942
- return { code: '+596', name: 'Martinique', value: '+596' };
1076
+ sessionStorageKey() {
1077
+ return `verdocs-session-${this.getSessionType()}-${this.getEnvironment()}`;
943
1078
  }
944
- else if (isMayotte(code)) {
945
- return { code: '+262', name: 'Mayotte or Réunion', value: '+262' };
1079
+ /**
1080
+ * Clear the active session.
1081
+ */
1082
+ clearSession() {
1083
+ localStorage.removeItem(this.sessionStorageKey());
1084
+ delete this.api.defaults.headers.common.Authorization;
1085
+ delete this.api.defaults.headers.common.signer;
1086
+ this.session = null;
1087
+ this.token = null;
1088
+ this.notifySessionListeners();
1089
+ return this;
946
1090
  }
947
- return null;
948
- }
949
- function isFrenchGuiana(code) {
950
- return '+594' === code.substring(0, 4);
951
- }
952
- function isGuadeloupe(code) {
953
- return '+590' === code.substring(0, 4);
954
- }
955
- function isMartinique(code) {
956
- return '+596' === code.substring(0, 4);
957
- }
958
- function isMayotte(code) {
959
- return '+262' === code.substring(0, 4);
960
- }
961
- function getPlusOneCountry(code) {
962
- let info = null;
963
- switch (code.substring(0, 5)) {
964
- case '+1684':
965
- info = { code: '+1', name: 'American Samoa', value: '+1' };
966
- break;
967
- case '+1264':
968
- info = { code: '+1', name: 'Anguilla', value: '+1' };
969
- break;
970
- case '+1268':
971
- info = { code: '+1', name: 'Antigua and Barbuda', value: '+1' };
972
- break;
973
- case '+1242':
974
- info = { code: '+1', name: 'Bahamas', value: '+1' };
975
- break;
976
- case '+1246':
977
- info = { code: '+1', name: 'Barbados', value: '+1' };
978
- break;
979
- case '+1441':
980
- info = { code: '+1', name: 'Bermuda', value: '+1' };
981
- break;
982
- case '+1284':
983
- info = { code: '+1', name: 'British Virgin Islands', value: '+1' };
984
- break;
985
- case '+1':
986
- info = { code: '+1', name: '', value: '+1' };
987
- break;
1091
+ /**
1092
+ * Clear the active signing session.
1093
+ */
1094
+ clearSignerSession() {
1095
+ localStorage.removeItem(this.sessionStorageKey());
1096
+ delete this.api.defaults.headers.common.Authorization;
1097
+ this.session = null;
1098
+ this.token = null;
1099
+ this.notifySessionListeners();
1100
+ return this;
988
1101
  }
989
- return info;
990
- }
991
- function isCanada(code) {
992
- const canadianAreaCodes = [
993
- '403',
994
- '587',
995
- '780',
996
- '825',
997
- '604',
998
- '250',
999
- '778',
1000
- '236',
1001
- '204',
1002
- '431',
1003
- '506',
1004
- '709',
1005
- '867',
1006
- '782',
1007
- '902',
1008
- '867',
1009
- '548',
1010
- '705',
1011
- '365',
1012
- '613',
1013
- '807',
1014
- '226',
1015
- '289',
1016
- '437',
1017
- '519',
1018
- '647',
1019
- '905',
1020
- '249',
1021
- '343',
1022
- '416',
1023
- '902',
1024
- '782',
1025
- '450',
1026
- '418',
1027
- '579',
1028
- '873',
1029
- '367',
1030
- '514',
1031
- '581',
1032
- '819',
1033
- '438',
1034
- '639',
1035
- '306',
1036
- '867',
1037
- ];
1038
- const areaCode = code.substring(0, 5);
1039
- return canadianAreaCodes.findIndex((x) => '+1' + x === areaCode) > -1;
1040
- }
1041
- function isAmericanSamoa(code) {
1042
- return code.substring(0, 5) === '+1684';
1043
- }
1044
- function isDominicanRepublic(code) {
1045
- return '+1809' === code.substring(0, 5) || '+1829' === code.substring(0, 5) || '+1849' === code.substring(0, 5);
1046
- }
1047
- function isPuertoRico(code) {
1048
- return code.substring(0, 5) === '+' || code.substring(0, 5) === '+';
1049
- }
1050
- // need to finish
1051
- function getMatchingCountry(code, substrings) {
1052
- const toMatch = code.substring(0, substrings);
1053
- return Countries.filter((c) => c.code === toMatch).length;
1054
- }
1055
- // const e164Regex = new RegExp(/\+[1-9]\d{6,14}/g);
1056
- // export function simpleE164Validator(code: string) {
1057
- // return (code !== null && code.length < 16 && code.length > 6 && e164Regex.test(code)) || code === '' || code === null;
1058
- // }
1059
-
1060
- /**
1061
- * Capitalize the first letter of a string.
1062
- */
1063
- const capitalize = (str) => str.charAt(0).toUpperCase() + str.slice(1);
1064
- /**
1065
- * Convert a phone-number-like string to E164 format.
1066
- * @see https://46elks.com/kb/e164
1067
- */
1068
- const convertToE164 = (input) => {
1069
- // "(212) 555-1212" => +12125551212
1070
- // "+46766861004" => "+46766861004"
1071
- // "212-555-1212" => +12125551212
1072
- // "212.555.1212" => +12125551212
1073
- // "212 555 1212" => +12125551212
1074
- let temp = (input || '').trim();
1075
- // If we are already prefixed, assume the user did it deliberately and attempt to use what they entered. We also short-circuit blanks.
1076
- if (!temp || temp.startsWith('+')) {
1077
- return temp;
1102
+ notifySessionListeners() {
1103
+ this.sessionListeners.forEach((listener) => {
1104
+ try {
1105
+ listener(this, this.session);
1106
+ }
1107
+ catch (e) {
1108
+ // NOOP
1109
+ }
1110
+ });
1078
1111
  }
1079
- // Remove any spaces, parenthesis or other punctuation.
1080
- temp = temp.replace(/[^0-9]/g, '');
1081
- // If the number begins with a zero, remove the leading zero. Do not combine this with the previous step because it needs to be removed
1082
- // whether it's the actual first character e.g. `0(5)` or just the first digit e.g. `(05`.
1083
- temp = temp.replace(/^0/g, '');
1084
- // Prepend the country code and +. We're assuming US in this case given the target demographic. Users in other countries would/should be
1085
- // already entering a prefix so they'd shortcut out of this routine via the + prefix check.
1086
- return `+1${temp}`;
1087
- };
1088
-
1089
- /**
1090
- * Create an array containing a sequence of integers, e.g. [START, START+1, START+2, ...] This is frequently useful
1091
- * in rendering operations when there is no source array to .map() across.
1092
- */
1093
- const integerSequence = (start, count) => Array(count)
1094
- .fill(1)
1095
- .map((_, index) => index + start);
1096
- /**
1097
- * Format a profile's full name
1098
- */
1099
- const formatFullName = (profile) => profile ? `${capitalize(profile.first_name)} ${capitalize(profile.last_name)}` : 'Invalid User';
1100
- /**
1101
- * Format a profile's initials
1102
- */
1103
- const formatInitials = (profile) => profile ? `${capitalize(profile.first_name).charAt(0)} ${capitalize(profile.last_name).charAt(0)}` : '--';
1104
- /**
1105
- * Generate suggested initials for a full name, e.g. "John Doe" will yield "JD".
1106
- */
1107
- const fullNameToInitials = (name) => name
1108
- .split(' ')
1109
- .map((word) => word[0])
1110
- .join('');
1112
+ /**
1113
+ * Subscribe to session state change events.
1114
+ */
1115
+ onSessionChanged(listener) {
1116
+ // There's no value in randomizing this, a simple counter is fine
1117
+ this.nextListenerId++;
1118
+ const listenerSymbol = Symbol.for('' + this.nextListenerId);
1119
+ this.sessionListeners.set(listenerSymbol, listener);
1120
+ return () => {
1121
+ this.sessionListeners.delete(listenerSymbol);
1122
+ };
1123
+ }
1124
+ /**
1125
+ * Load a persisted session from localStorage. Typically called once after the endpoint is configured when the app
1126
+ * or component starts.
1127
+ */
1128
+ loadSession() {
1129
+ const token = localStorage.getItem(this.sessionStorageKey());
1130
+ if (!token) {
1131
+ return this.clearSession();
1132
+ }
1133
+ return this.setToken(token);
1134
+ }
1135
+ }
1111
1136
 
1112
1137
  /**
1113
1138
  * Create an envelope
@@ -2497,18 +2522,6 @@ const getCurrentProfile = (endpoint) => endpoint.api //
2497
2522
  const getRoles = (endpoint) => endpoint.api //
2498
2523
  .get('/roles')
2499
2524
  .then((r) => r.data);
2500
- /**
2501
- * Get a list of system roles.
2502
- *
2503
- * ```typescript
2504
- * import {Profiles} from '@verdocs/js-sdk/Users';
2505
- *
2506
- * const permissions = await Profiles.getPermissions();
2507
- * ```
2508
- */
2509
- const getPermissions = (endpoint) => endpoint.api //
2510
- .get('/permissions')
2511
- .then((r) => r.data);
2512
2525
  /**
2513
2526
  * Create a profile. If the caller does not have a "current" profile set, the new profile will be made current.
2514
2527
  *
@@ -2534,18 +2547,6 @@ const createProfile = (endpoint, params) => endpoint.api //
2534
2547
  const getProfile = (endpoint, profileId) => endpoint.api //
2535
2548
  .get(`/profiles/${profileId}`)
2536
2549
  .then((r) => r.data);
2537
- /**
2538
- * Get a profile's permissions. The caller must have admin access to the given profile.
2539
- *
2540
- * ```typescript
2541
- * import {Profiles} from '@verdocs/js-sdk/Users';
2542
- *
2543
- * const permissions = await Profiles.getProfilePermissions('PROFILEID');
2544
- * ```
2545
- */
2546
- const getProfilePermissions = (endpoint, profileId) => endpoint.api //
2547
- .get(`/profiles/${profileId}/permissions`)
2548
- .then((r) => r.data);
2549
2550
  /**
2550
2551
  * Get a profile's groups.
2551
2552
  *
@@ -2582,8 +2583,11 @@ const switchProfile = (endpoint, profileId) => endpoint.api //
2582
2583
  * ```
2583
2584
  */
2584
2585
  const updateProfile = (endpoint, profileId, params) => endpoint.api //
2585
- .put(`/profiles/${profileId}`, params)
2586
+ .put(`/profiles/${profileId}`, params, { baseURL: endpoint.getBaseURLv2() })
2586
2587
  .then((r) => r.data);
2588
+ // endpoint.api //
2589
+ // .post<IEnvelopeSummaries>('/envelopes/list', params, {baseURL: endpoint.getBaseURLv2()})
2590
+ // .then((r) => r.data);
2587
2591
  /**
2588
2592
  * Delete a profile. If the requested profile is the caller's curent profile, the next available profile will be selected.
2589
2593
  *
@@ -2713,11 +2717,9 @@ exports.getOrganizationInvitations = getOrganizationInvitations;
2713
2717
  exports.getOrganizationMemberPlans = getOrganizationMemberPlans;
2714
2718
  exports.getOrganizationMembers = getOrganizationMembers;
2715
2719
  exports.getOrganizations = getOrganizations;
2716
- exports.getPermissions = getPermissions;
2717
2720
  exports.getPlusOneCountry = getPlusOneCountry;
2718
2721
  exports.getProfile = getProfile;
2719
2722
  exports.getProfileGroups = getProfileGroups;
2720
- exports.getProfilePermissions = getProfilePermissions;
2721
2723
  exports.getProfiles = getProfiles;
2722
2724
  exports.getRGB = getRGB;
2723
2725
  exports.getRGBA = getRGBA;