@ouroboros/body 0.1.3 → 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.
package/src/index.ts ADDED
@@ -0,0 +1,514 @@
1
+ /**
2
+ * Body
3
+ *
4
+ * Manages communication with server side services
5
+ *
6
+ * @author Chris Nasr <chris@ouroboroscoding.com>
7
+ * @copyright Ouroboros Coding Inc.
8
+ * @created 2023-03-03
9
+ */
10
+
11
+ // Import const files
12
+ import * as constants from './constants';
13
+ import * as errors from './errors';
14
+ import * as regex from './regex';
15
+
16
+ // Then export them
17
+ export { constants, errors, regex };
18
+ export { default as Service } from './Service';
19
+
20
+ // Types
21
+ export type actionOptions = 'create' | 'delete' | 'read' | 'update';
22
+ export type callbackOptions = 'error' | 'errorCode' | 'requested' | 'requesting' | 'warning';
23
+ export type onCallbacks = {
24
+ error?: onError,
25
+ errorCode?: onErrorCode,
26
+ requested?: onRequested,
27
+ requesting?: onRequesting,
28
+ warning?: onWarning
29
+ }
30
+ export type onError = (error: string, info: onRequestedStruct) => void;
31
+ export type onErrorCode = (error: responseErrorStruct, info: onRequestedStruct) => void | false;
32
+ export type onRequested = (info: onRequestedStruct) => void;
33
+ export type onRequestedStruct = {
34
+ action: actionOptions,
35
+ data: any,
36
+ res?: responseStruct,
37
+ url: string,
38
+ xhr: XMLHttpRequest
39
+ };
40
+ export type onRequesting = (info: onRequestingStruct) => void;
41
+ export type onRequestingStruct = {
42
+ action: actionOptions,
43
+ data: any,
44
+ url: string,
45
+ xhr: XMLHttpRequest
46
+ };
47
+ export type onWarning = (warning: any, info: onRequestedStruct) => void;
48
+ export type responseStruct = {
49
+ data?: any,
50
+ error?: responseErrorStruct,
51
+ warning?: any
52
+ };
53
+ export type responseErrorStruct = {
54
+ code: number,
55
+ msg?: any,
56
+ handle?: (message: string) => void
57
+ }
58
+ export type responseResolve = (res: responseStruct) => void;
59
+ export type responseReject = (error: responseErrorStruct) => boolean;
60
+
61
+ // Actions to methods
62
+ const ACTIONS_TO_METHODS = {
63
+ create: 'POST',
64
+ delete: 'DELETE',
65
+ read: 'GET',
66
+ update: 'PUT'
67
+ }
68
+
69
+ /**
70
+ * Body
71
+ *
72
+ * The primary module class which handles communication with body services on
73
+ * the server side
74
+ *
75
+ * @name Body
76
+ */
77
+ class Body {
78
+
79
+ // The domain used to make requests to
80
+ private domain: string = '';
81
+
82
+ // The function to call for http and related errors that need to be reported
83
+ private error: onError | null = null;
84
+
85
+ // The function to call if we get body errors
86
+ private errorCode: onErrorCode | null = null;
87
+
88
+ // The function to call when we get a status 401, or error code
89
+ // REST_AUTHORIZATION
90
+ private noSession: (() => void) | null = null;
91
+
92
+ // The function to call after any request is sent
93
+ private requested: onRequested | null = null;
94
+
95
+ // The function to call before any request is sent
96
+ private requesting: onRequesting | null = null;
97
+
98
+ // The token associated with the current session
99
+ private token: string | null = null;
100
+
101
+ // Flag for being verbose
102
+ private verbose: boolean = false;
103
+
104
+ // The function to call if we get body warnings
105
+ private warning: onWarning | null = null;
106
+
107
+ /**
108
+ * Request
109
+ *
110
+ * Calls a request on the service given
111
+ *
112
+ * @name request
113
+ * @access public
114
+ * @param service The service to call
115
+ * @param noun The noun to call on the service
116
+ * @param data The data associated with the request
117
+ */
118
+ request(action: actionOptions, service: string, noun: string, data: any): Promise<any> {
119
+
120
+ // Generate the URL for the request
121
+ let url = `https://${this.domain}/${service}/${noun}`;
122
+
123
+ // Init the response object
124
+ let res: responseStruct;
125
+
126
+ // Set this
127
+ const $this = this;
128
+
129
+ // Create a new Promise and return it
130
+ return new Promise((resolve: responseResolve, reject) => {
131
+
132
+ // Create a new XMLHttpRequest
133
+ const xhr: XMLHttpRequest = new XMLHttpRequest();
134
+
135
+ // Handles an error based on whether an error handler is set
136
+ function handleError(message: string): void {
137
+ if($this.error) {
138
+ $this.error(message, { action, data, res, url, xhr });
139
+ } else {
140
+ throw new Error(message);
141
+ }
142
+ }
143
+
144
+ // Track abort
145
+ xhr.addEventListener('abort', event => {
146
+ if(this.verbose) {
147
+ console.log(`xhr.abort:\n\t${ACTIONS_TO_METHODS[action]} ${url}\n\t`, event);
148
+ }
149
+ });
150
+
151
+ // Track errors
152
+ xhr.addEventListener('error', event => {
153
+ if(this.verbose) {
154
+ console.log(`xhr.error:\n\t${ACTIONS_TO_METHODS[action]} ${url}\n\t`, event);
155
+ }
156
+ });
157
+
158
+ // Handle successful request
159
+ xhr.addEventListener('load', (event) => {
160
+ if(this.verbose) {
161
+ console.log(`xhr.load:\n\t${ACTIONS_TO_METHODS[action]} ${url}\n\t`, event);
162
+ }
163
+
164
+ // If we got anything other than 200
165
+ if(xhr.status !== 200) {
166
+
167
+ // If it's 401
168
+ if(xhr.status === 401) {
169
+
170
+ // If we have a no session callback
171
+ if(this.noSession) {
172
+ return this.noSession();
173
+ } else {
174
+ throw new Error(`${ACTIONS_TO_METHODS[action]} ${url} return 401 NOT AUTHORIZED`);
175
+ }
176
+ }
177
+
178
+ // Else, invalid status
179
+ else {
180
+ handleError(`${ACTIONS_TO_METHODS[action]} ${url} returned invalid status: ${xhr.status}`);
181
+ }
182
+ }
183
+
184
+ // If the Content-Type is missing or invalid
185
+ const contentType = xhr.getResponseHeader('Content-Type');
186
+ if(!contentType || contentType !== 'application/json; charset=utf-8') {
187
+ handleError(`${ACTIONS_TO_METHODS[action]} ${url} returned invalid Content-Type: ${contentType}`);
188
+ }
189
+
190
+ // Convert the text from JSON
191
+ res = JSON.parse(xhr.responseText);
192
+
193
+ // If the JSON failed to parse
194
+ if(!res) {
195
+ handleError(`${ACTIONS_TO_METHODS[action]} ${url} returned invalid JSON: ${xhr.responseText}`);
196
+ }
197
+
198
+ // If we got an error
199
+ if('error' in res && res.error) {
200
+
201
+ // Add the handle error function to it
202
+ res.error.handle = handleError;
203
+
204
+ // If we don't have an onErrorCode callback, or it we do and
205
+ // calling it returns false
206
+ if(!this.errorCode || this.errorCode(res.error as responseErrorStruct, { action, data, res, url, xhr }) === false) {
207
+ return reject(res.error);
208
+ }
209
+ }
210
+
211
+ // If we got data
212
+ if('data' in res) {
213
+
214
+ // Resolve it
215
+ return resolve(res.data);
216
+ }
217
+ });
218
+
219
+ // Handle the request being finished
220
+ xhr.addEventListener('loadend', (event) => {
221
+ if(this.verbose) {
222
+ console.log(`xhr.loadend:\n\t${ACTIONS_TO_METHODS[action]} ${url}\n\t`, event);
223
+ }
224
+
225
+ // If we have a requested callback
226
+ if(this.requested) {
227
+ this.requested({ action, data, res, url, xhr });
228
+ }
229
+ });
230
+
231
+ // Init the request body
232
+ let xml = '';
233
+
234
+ // If we got data
235
+ if(data !== null) {
236
+
237
+ // If we're in GET mode
238
+ if(action === 'read') {
239
+
240
+ // Append the data as a param
241
+ url += '?d=' + encodeURIComponent(JSON.stringify(data));
242
+ }
243
+
244
+ // Else, DELETE, POST, PUT, just encode the data
245
+ else {
246
+ xml = JSON.stringify(data);
247
+ }
248
+ }
249
+
250
+ // Open the request
251
+ xhr.open(ACTIONS_TO_METHODS[action], url);
252
+
253
+ // If we have a session token, add it as the Authorization header
254
+ if(this.token) {
255
+ xhr.setRequestHeader('Authorization', this.token);
256
+ }
257
+
258
+ // Set the Content-Type header
259
+ xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
260
+
261
+ // If we have a requesting callback, call it
262
+ if(this.requesting) {
263
+ this.requesting({ action, data, url, xhr });
264
+ }
265
+
266
+ // Send the request
267
+ xhr.send(xml);
268
+ });
269
+ }
270
+
271
+ /**
272
+ * Create
273
+ *
274
+ * Calls a create (POST) request on the service given
275
+ *
276
+ * @name create
277
+ * @access public
278
+ * @param service The service to call
279
+ * @param noun The noun to call on the service
280
+ * @param data The data associated with the request
281
+ */
282
+ create(service: string, noun: string, data: any = null): Promise<any> {
283
+ return this.request('create', service, noun, data);
284
+ }
285
+
286
+ /**
287
+ * Delete
288
+ *
289
+ * Calls a delete (DELETE) request on the service given
290
+ *
291
+ * @name delete
292
+ * @access public
293
+ * @param service The service to call
294
+ * @param noun The noun to call on the service
295
+ * @param data The data associated with the request
296
+ */
297
+ delete(service: string, noun: string, data: any = null): Promise<any> {
298
+ return this.request('delete', service, noun, data);
299
+ }
300
+
301
+ /**
302
+ * On
303
+ *
304
+ * Called to set multiple events at once
305
+ *
306
+ * @name on
307
+ * @access public
308
+ * @param callbacks A name to callback object to set multiple events
309
+ */
310
+ on(callbacks: onCallbacks): void {
311
+ for(const event of Object.keys(callbacks)) {
312
+ switch(event) {
313
+ case 'error':
314
+ this.error = callbacks.error as onError;
315
+ continue;
316
+ case 'errorCode':
317
+ this.errorCode = callbacks.errorCode as onErrorCode;
318
+ continue;
319
+ case 'requested':
320
+ this.requested = callbacks.requested as onRequested;
321
+ continue;
322
+ case 'requesting':
323
+ this.requesting = callbacks.requesting as onRequesting;
324
+ continue;
325
+ case 'warning':
326
+ this.warning = callbacks.warning as onWarning;
327
+ continue;
328
+ }
329
+ }
330
+ }
331
+
332
+ /**
333
+ * On Error
334
+ *
335
+ * Sets the callback called after any request is sent out
336
+ *
337
+ * @name onError
338
+ * @access public
339
+ * @param callback The function to call after making requests
340
+ */
341
+ onError(callback: onError): void {
342
+
343
+ // Make sure the callback is function
344
+ if(typeof callback !== 'function') {
345
+ throw new Error('onError() called with an invalid callback');
346
+ }
347
+
348
+ // Set the callback
349
+ this.error = callback;
350
+ }
351
+
352
+ /**
353
+ * On No Session
354
+ *
355
+ * Sets callback for whenever a request gets a REST_AUTHORIZATION error
356
+ *
357
+ * @name onNoSession
358
+ * @access public
359
+ * @param callback The function to call if there's an error
360
+ */
361
+ onErrorCode(callback: onErrorCode): void {
362
+
363
+ // Make sure the callback is function
364
+ if(typeof callback !== 'function') {
365
+ throw new Error('onErrorCode() called with an invalid callback');
366
+ }
367
+
368
+ // Set the callback
369
+ this.errorCode = callback;
370
+ }
371
+
372
+ /**
373
+ * On No Session
374
+ *
375
+ * Sets the callback called if any request fails the session
376
+ *
377
+ * @name onNoSession
378
+ * @access public
379
+ * @param callback The function to call if there are session errors
380
+ */
381
+ onNoSession(callback: () => void): void {
382
+
383
+ // Make sure the callback is function
384
+ if(typeof callback !== 'function') {
385
+ throw new Error('onNoSession() called with an invalid callback');
386
+ }
387
+
388
+ // Set the callback
389
+ this.noSession = callback;
390
+ }
391
+
392
+ /**
393
+ * On Requested
394
+ *
395
+ * Sets the callback called after any request is sent out
396
+ *
397
+ * @name onRequested
398
+ * @access public
399
+ * @param callback The function to call after making requests
400
+ */
401
+ onRequested(callback: onRequested): void {
402
+
403
+ // Make sure the callback is function
404
+ if(typeof callback !== 'function') {
405
+ throw new Error('onRequested() called with an invalid callback');
406
+ }
407
+
408
+ // Set the callback
409
+ this.requested = callback;
410
+ }
411
+
412
+ /**
413
+ * On Requesting
414
+ *
415
+ * Sets the callback called before any request is send out
416
+ *
417
+ * @name onRequesting
418
+ * @access public
419
+ * @param callback The function to call before making requests
420
+ */
421
+ onRequesting(callback: onRequesting): void {
422
+
423
+ // Make sure the callback is function
424
+ if(typeof callback !== 'function') {
425
+ throw new Error('onRequesting() called with an invalid callback');
426
+ }
427
+
428
+ // Set the callback
429
+ this.requesting = callback;
430
+ }
431
+
432
+ /**
433
+ * Read
434
+ *
435
+ * Calls a read (GET) request on the service given
436
+ *
437
+ * @name read
438
+ * @access public
439
+ * @param service The service to call
440
+ * @param noun The noun to call on the service
441
+ * @param data The data associated with the request
442
+ */
443
+ read(service: string, noun: string, data: any = null): Promise<any> {
444
+ return this.request('read', service, noun, data);
445
+ }
446
+
447
+ /**
448
+ * Session
449
+ *
450
+ * Set/Gets the current session token
451
+ *
452
+ * @name session
453
+ * @access public
454
+ * @param token The session to set
455
+ * @returns the session set
456
+ */
457
+ session(token?: string | null): string | null | void {
458
+
459
+ // If we are getting the token
460
+ if(token === undefined) {
461
+ return this.token;
462
+ }
463
+
464
+ // Else, we are setting the token
465
+ else {
466
+ this.token = token;
467
+ }
468
+ }
469
+
470
+ /**
471
+ * Update
472
+ *
473
+ * Calls a update (PUT) request on the service given
474
+ *
475
+ * @name update
476
+ * @access public
477
+ * @param service The service to call
478
+ * @param noun The noun to call on the service
479
+ * @param data The data associated with the request
480
+ */
481
+ update(service: string, noun: string, data: any = null): Promise<any> {
482
+ return this.request('update', service, noun, data);
483
+ }
484
+
485
+ /**
486
+ * Verbose Off
487
+ *
488
+ * Called to turn verbose mode off
489
+ *
490
+ * @name verbose_off
491
+ * @access
492
+ */
493
+ verbose_off(): void {
494
+ this.verbose = false;
495
+ }
496
+
497
+ /**
498
+ * Verbose On
499
+ *
500
+ * Called to turn verbose mode on
501
+ *
502
+ * @name verbose_on
503
+ * @access
504
+ */
505
+ verbose_on(): void {
506
+ this.verbose = true;
507
+ }
508
+ }
509
+
510
+ // Create an instance of Body
511
+ const body = new Body();
512
+
513
+ // Export it as the default
514
+ export default body;
package/src/regex.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ /**
2
+ * RegEx
3
+ *
4
+ * Holds common regular expressions
5
+ *
6
+ * @author Chris Nasr <chris@ouroboroscoding.com>
7
+ * @copyright Ouroboros Coding Inc.
8
+ * @created 2023-03-05
9
+ */
10
+ export declare const EMAIL_ADDRESS: RegExp;
11
+ export declare const PHONE_NUMBER_NA: RegExp;
package/src/regex.js ADDED
@@ -0,0 +1,13 @@
1
+ /**
2
+ * RegEx
3
+ *
4
+ * Holds common regular expressions
5
+ *
6
+ * @author Chris Nasr <chris@ouroboroscoding.com>
7
+ * @copyright Ouroboros Coding Inc.
8
+ * @created 2023-03-05
9
+ */
10
+ // E-Mail address
11
+ export const EMAIL_ADDRESS = /[^@\s]+@[^@\s]+\.[a-zA-Z0-9]{2,}$/;
12
+ // North American Phone Number
13
+ export const PHONE_NUMBER_NA = /\+?1?[ -]?\(?(\d{3})\)?[ -]?(\d{3})[ -]?(\d{4})/;
package/src/regex.ts ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * RegEx
3
+ *
4
+ * Holds common regular expressions
5
+ *
6
+ * @author Chris Nasr <chris@ouroboroscoding.com>
7
+ * @copyright Ouroboros Coding Inc.
8
+ * @created 2023-03-05
9
+ */
10
+
11
+ // E-Mail address
12
+ export const EMAIL_ADDRESS = /[^@\s]+@[^@\s]+\.[a-zA-Z0-9]{2,}$/
13
+
14
+ // North American Phone Number
15
+ export const PHONE_NUMBER_NA = /\+?1?[ -]?\(?(\d{3})\)?[ -]?(\d{3})[ -]?(\d{4})/
package/index.d.ts DELETED
@@ -1,3 +0,0 @@
1
- export { default as rest } from './rest';
2
- export { default as twoway } from './twoway';
3
- export { default as wshelper } from './wshelper';
package/index.js DELETED
@@ -1,3 +0,0 @@
1
- export { default as rest } from './rest';
2
- export { default as twoway } from './twoway';
3
- export { default as wshelper } from './wshelper';