@ouroboros/body 0.1.3 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.d.ts ADDED
@@ -0,0 +1,223 @@
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
+ import * as constants from './constants';
11
+ import * as errors from './errors';
12
+ import * as regex from './regex';
13
+ export { constants, errors, regex };
14
+ export { default as Service } from './Service';
15
+ export type actionOptions = 'create' | 'delete' | 'read' | 'update';
16
+ export type callbackOptions = 'error' | 'errorCode' | 'requested' | 'requesting' | 'warning';
17
+ export type onCallbacks = {
18
+ error?: onError;
19
+ errorCode?: onErrorCode;
20
+ requested?: onRequested;
21
+ requesting?: onRequesting;
22
+ warning?: onWarning;
23
+ };
24
+ export type onError = (error: string, info: onRequestedStruct) => void;
25
+ export type onErrorCode = (error: responseErrorStruct, info: onRequestedStruct) => void | false;
26
+ export type onRequested = (info: onRequestedStruct) => void;
27
+ export type onRequestedStruct = {
28
+ action: actionOptions;
29
+ data: any;
30
+ res?: responseStruct;
31
+ url: string;
32
+ xhr: XMLHttpRequest;
33
+ };
34
+ export type onRequesting = (info: onRequestingStruct) => void;
35
+ export type onRequestingStruct = {
36
+ action: actionOptions;
37
+ data: any;
38
+ url: string;
39
+ xhr: XMLHttpRequest;
40
+ };
41
+ export type onWarning = (warning: any, info: onRequestedStruct) => void;
42
+ export type responseStruct = {
43
+ data?: any;
44
+ error?: responseErrorStruct;
45
+ warning?: any;
46
+ };
47
+ export type responseErrorStruct = {
48
+ code: number;
49
+ msg?: any;
50
+ handle?: (message: string) => void;
51
+ };
52
+ export type responseResolve = (res: responseStruct) => void;
53
+ export type responseReject = (error: responseErrorStruct) => boolean;
54
+ /**
55
+ * Body
56
+ *
57
+ * The primary module class which handles communication with body services on
58
+ * the server side
59
+ *
60
+ * @name Body
61
+ */
62
+ declare class Body {
63
+ private domain;
64
+ private error;
65
+ private errorCode;
66
+ private noSession;
67
+ private requested;
68
+ private requesting;
69
+ private token;
70
+ private verbose;
71
+ private warning;
72
+ /**
73
+ * Request
74
+ *
75
+ * Calls a request on the service given
76
+ *
77
+ * @name request
78
+ * @access public
79
+ * @param service The service to call
80
+ * @param noun The noun to call on the service
81
+ * @param data The data associated with the request
82
+ */
83
+ request(action: actionOptions, service: string, noun: string, data: any): Promise<any>;
84
+ /**
85
+ * Create
86
+ *
87
+ * Calls a create (POST) request on the service given
88
+ *
89
+ * @name create
90
+ * @access public
91
+ * @param service The service to call
92
+ * @param noun The noun to call on the service
93
+ * @param data The data associated with the request
94
+ */
95
+ create(service: string, noun: string, data?: any): Promise<any>;
96
+ /**
97
+ * Delete
98
+ *
99
+ * Calls a delete (DELETE) request on the service given
100
+ *
101
+ * @name delete
102
+ * @access public
103
+ * @param service The service to call
104
+ * @param noun The noun to call on the service
105
+ * @param data The data associated with the request
106
+ */
107
+ delete(service: string, noun: string, data?: any): Promise<any>;
108
+ /**
109
+ * On
110
+ *
111
+ * Called to set multiple events at once
112
+ *
113
+ * @name on
114
+ * @access public
115
+ * @param callbacks A name to callback object to set multiple events
116
+ */
117
+ on(callbacks: onCallbacks): void;
118
+ /**
119
+ * On Error
120
+ *
121
+ * Sets the callback called after any request is sent out
122
+ *
123
+ * @name onError
124
+ * @access public
125
+ * @param callback The function to call after making requests
126
+ */
127
+ onError(callback: onError): void;
128
+ /**
129
+ * On No Session
130
+ *
131
+ * Sets callback for whenever a request gets a REST_AUTHORIZATION error
132
+ *
133
+ * @name onNoSession
134
+ * @access public
135
+ * @param callback The function to call if there's an error
136
+ */
137
+ onErrorCode(callback: onErrorCode): void;
138
+ /**
139
+ * On No Session
140
+ *
141
+ * Sets the callback called if any request fails the session
142
+ *
143
+ * @name onNoSession
144
+ * @access public
145
+ * @param callback The function to call if there are session errors
146
+ */
147
+ onNoSession(callback: () => void): void;
148
+ /**
149
+ * On Requested
150
+ *
151
+ * Sets the callback called after any request is sent out
152
+ *
153
+ * @name onRequested
154
+ * @access public
155
+ * @param callback The function to call after making requests
156
+ */
157
+ onRequested(callback: onRequested): void;
158
+ /**
159
+ * On Requesting
160
+ *
161
+ * Sets the callback called before any request is send out
162
+ *
163
+ * @name onRequesting
164
+ * @access public
165
+ * @param callback The function to call before making requests
166
+ */
167
+ onRequesting(callback: onRequesting): void;
168
+ /**
169
+ * Read
170
+ *
171
+ * Calls a read (GET) request on the service given
172
+ *
173
+ * @name read
174
+ * @access public
175
+ * @param service The service to call
176
+ * @param noun The noun to call on the service
177
+ * @param data The data associated with the request
178
+ */
179
+ read(service: string, noun: string, data?: any): Promise<any>;
180
+ /**
181
+ * Session
182
+ *
183
+ * Set/Gets the current session token
184
+ *
185
+ * @name session
186
+ * @access public
187
+ * @param token The session to set
188
+ * @returns the session set
189
+ */
190
+ session(token?: string | null): string | null | void;
191
+ /**
192
+ * Update
193
+ *
194
+ * Calls a update (PUT) request on the service given
195
+ *
196
+ * @name update
197
+ * @access public
198
+ * @param service The service to call
199
+ * @param noun The noun to call on the service
200
+ * @param data The data associated with the request
201
+ */
202
+ update(service: string, noun: string, data?: any): Promise<any>;
203
+ /**
204
+ * Verbose Off
205
+ *
206
+ * Called to turn verbose mode off
207
+ *
208
+ * @name verbose_off
209
+ * @access
210
+ */
211
+ verbose_off(): void;
212
+ /**
213
+ * Verbose On
214
+ *
215
+ * Called to turn verbose mode on
216
+ *
217
+ * @name verbose_on
218
+ * @access
219
+ */
220
+ verbose_on(): void;
221
+ }
222
+ declare const body: Body;
223
+ export default body;
package/src/index.js ADDED
@@ -0,0 +1,401 @@
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
+ // Import const files
11
+ import * as constants from './constants';
12
+ import * as errors from './errors';
13
+ import * as regex from './regex';
14
+ // Then export them
15
+ export { constants, errors, regex };
16
+ export { default as Service } from './Service';
17
+ // Actions to methods
18
+ const ACTIONS_TO_METHODS = {
19
+ create: 'POST',
20
+ delete: 'DELETE',
21
+ read: 'GET',
22
+ update: 'PUT'
23
+ };
24
+ /**
25
+ * Body
26
+ *
27
+ * The primary module class which handles communication with body services on
28
+ * the server side
29
+ *
30
+ * @name Body
31
+ */
32
+ class Body {
33
+ // The domain used to make requests to
34
+ domain = '';
35
+ // The function to call for http and related errors that need to be reported
36
+ error = null;
37
+ // The function to call if we get body errors
38
+ errorCode = null;
39
+ // The function to call when we get a status 401, or error code
40
+ // REST_AUTHORIZATION
41
+ noSession = null;
42
+ // The function to call after any request is sent
43
+ requested = null;
44
+ // The function to call before any request is sent
45
+ requesting = null;
46
+ // The token associated with the current session
47
+ token = null;
48
+ // Flag for being verbose
49
+ verbose = false;
50
+ // The function to call if we get body warnings
51
+ warning = null;
52
+ /**
53
+ * Request
54
+ *
55
+ * Calls a request on the service given
56
+ *
57
+ * @name request
58
+ * @access public
59
+ * @param service The service to call
60
+ * @param noun The noun to call on the service
61
+ * @param data The data associated with the request
62
+ */
63
+ request(action, service, noun, data) {
64
+ // Generate the URL for the request
65
+ let url = `https://${this.domain}/${service}/${noun}`;
66
+ // Init the response object
67
+ let res;
68
+ // Set this
69
+ const $this = this;
70
+ // Create a new Promise and return it
71
+ return new Promise((resolve, reject) => {
72
+ // Create a new XMLHttpRequest
73
+ const xhr = new XMLHttpRequest();
74
+ // Handles an error based on whether an error handler is set
75
+ function handleError(message) {
76
+ if ($this.error) {
77
+ $this.error(message, { action, data, res, url, xhr });
78
+ }
79
+ else {
80
+ throw new Error(message);
81
+ }
82
+ }
83
+ // Track abort
84
+ xhr.addEventListener('abort', event => {
85
+ if (this.verbose) {
86
+ console.log(`xhr.abort:\n\t${ACTIONS_TO_METHODS[action]} ${url}\n\t`, event);
87
+ }
88
+ });
89
+ // Track errors
90
+ xhr.addEventListener('error', event => {
91
+ if (this.verbose) {
92
+ console.log(`xhr.error:\n\t${ACTIONS_TO_METHODS[action]} ${url}\n\t`, event);
93
+ }
94
+ });
95
+ // Handle successful request
96
+ xhr.addEventListener('load', (event) => {
97
+ if (this.verbose) {
98
+ console.log(`xhr.load:\n\t${ACTIONS_TO_METHODS[action]} ${url}\n\t`, event);
99
+ }
100
+ // If we got anything other than 200
101
+ if (xhr.status !== 200) {
102
+ // If it's 401
103
+ if (xhr.status === 401) {
104
+ // If we have a no session callback
105
+ if (this.noSession) {
106
+ return this.noSession();
107
+ }
108
+ else {
109
+ throw new Error(`${ACTIONS_TO_METHODS[action]} ${url} return 401 NOT AUTHORIZED`);
110
+ }
111
+ }
112
+ // Else, invalid status
113
+ else {
114
+ handleError(`${ACTIONS_TO_METHODS[action]} ${url} returned invalid status: ${xhr.status}`);
115
+ }
116
+ }
117
+ // If the Content-Type is missing or invalid
118
+ const contentType = xhr.getResponseHeader('Content-Type');
119
+ if (!contentType || contentType !== 'application/json; charset=utf-8') {
120
+ handleError(`${ACTIONS_TO_METHODS[action]} ${url} returned invalid Content-Type: ${contentType}`);
121
+ }
122
+ // Convert the text from JSON
123
+ res = JSON.parse(xhr.responseText);
124
+ // If the JSON failed to parse
125
+ if (!res) {
126
+ handleError(`${ACTIONS_TO_METHODS[action]} ${url} returned invalid JSON: ${xhr.responseText}`);
127
+ }
128
+ // If we got an error
129
+ if ('error' in res && res.error) {
130
+ // Add the handle error function to it
131
+ res.error.handle = handleError;
132
+ // If we don't have an onErrorCode callback, or it we do and
133
+ // calling it returns false
134
+ if (!this.errorCode || this.errorCode(res.error, { action, data, res, url, xhr }) === false) {
135
+ return reject(res.error);
136
+ }
137
+ }
138
+ // If we got data
139
+ if ('data' in res) {
140
+ // Resolve it
141
+ return resolve(res.data);
142
+ }
143
+ });
144
+ // Handle the request being finished
145
+ xhr.addEventListener('loadend', (event) => {
146
+ if (this.verbose) {
147
+ console.log(`xhr.loadend:\n\t${ACTIONS_TO_METHODS[action]} ${url}\n\t`, event);
148
+ }
149
+ // If we have a requested callback
150
+ if (this.requested) {
151
+ this.requested({ action, data, res, url, xhr });
152
+ }
153
+ });
154
+ // Init the request body
155
+ let xml = '';
156
+ // If we got data
157
+ if (data !== null) {
158
+ // If we're in GET mode
159
+ if (action === 'read') {
160
+ // Append the data as a param
161
+ url += '?d=' + encodeURIComponent(JSON.stringify(data));
162
+ }
163
+ // Else, DELETE, POST, PUT, just encode the data
164
+ else {
165
+ xml = JSON.stringify(data);
166
+ }
167
+ }
168
+ // Open the request
169
+ xhr.open(ACTIONS_TO_METHODS[action], url);
170
+ // If we have a session token, add it as the Authorization header
171
+ if (this.token) {
172
+ xhr.setRequestHeader('Authorization', this.token);
173
+ }
174
+ // Set the Content-Type header
175
+ xhr.setRequestHeader('Content-Type', 'application/json; charset=utf-8');
176
+ // If we have a requesting callback, call it
177
+ if (this.requesting) {
178
+ this.requesting({ action, data, url, xhr });
179
+ }
180
+ // Send the request
181
+ xhr.send(xml);
182
+ });
183
+ }
184
+ /**
185
+ * Create
186
+ *
187
+ * Calls a create (POST) request on the service given
188
+ *
189
+ * @name create
190
+ * @access public
191
+ * @param service The service to call
192
+ * @param noun The noun to call on the service
193
+ * @param data The data associated with the request
194
+ */
195
+ create(service, noun, data = null) {
196
+ return this.request('create', service, noun, data);
197
+ }
198
+ /**
199
+ * Delete
200
+ *
201
+ * Calls a delete (DELETE) request on the service given
202
+ *
203
+ * @name delete
204
+ * @access public
205
+ * @param service The service to call
206
+ * @param noun The noun to call on the service
207
+ * @param data The data associated with the request
208
+ */
209
+ delete(service, noun, data = null) {
210
+ return this.request('delete', service, noun, data);
211
+ }
212
+ /**
213
+ * On
214
+ *
215
+ * Called to set multiple events at once
216
+ *
217
+ * @name on
218
+ * @access public
219
+ * @param callbacks A name to callback object to set multiple events
220
+ */
221
+ on(callbacks) {
222
+ for (const event of Object.keys(callbacks)) {
223
+ switch (event) {
224
+ case 'error':
225
+ this.error = callbacks.error;
226
+ continue;
227
+ case 'errorCode':
228
+ this.errorCode = callbacks.errorCode;
229
+ continue;
230
+ case 'requested':
231
+ this.requested = callbacks.requested;
232
+ continue;
233
+ case 'requesting':
234
+ this.requesting = callbacks.requesting;
235
+ continue;
236
+ case 'warning':
237
+ this.warning = callbacks.warning;
238
+ continue;
239
+ }
240
+ }
241
+ }
242
+ /**
243
+ * On Error
244
+ *
245
+ * Sets the callback called after any request is sent out
246
+ *
247
+ * @name onError
248
+ * @access public
249
+ * @param callback The function to call after making requests
250
+ */
251
+ onError(callback) {
252
+ // Make sure the callback is function
253
+ if (typeof callback !== 'function') {
254
+ throw new Error('onError() called with an invalid callback');
255
+ }
256
+ // Set the callback
257
+ this.error = callback;
258
+ }
259
+ /**
260
+ * On No Session
261
+ *
262
+ * Sets callback for whenever a request gets a REST_AUTHORIZATION error
263
+ *
264
+ * @name onNoSession
265
+ * @access public
266
+ * @param callback The function to call if there's an error
267
+ */
268
+ onErrorCode(callback) {
269
+ // Make sure the callback is function
270
+ if (typeof callback !== 'function') {
271
+ throw new Error('onErrorCode() called with an invalid callback');
272
+ }
273
+ // Set the callback
274
+ this.errorCode = callback;
275
+ }
276
+ /**
277
+ * On No Session
278
+ *
279
+ * Sets the callback called if any request fails the session
280
+ *
281
+ * @name onNoSession
282
+ * @access public
283
+ * @param callback The function to call if there are session errors
284
+ */
285
+ onNoSession(callback) {
286
+ // Make sure the callback is function
287
+ if (typeof callback !== 'function') {
288
+ throw new Error('onNoSession() called with an invalid callback');
289
+ }
290
+ // Set the callback
291
+ this.noSession = callback;
292
+ }
293
+ /**
294
+ * On Requested
295
+ *
296
+ * Sets the callback called after any request is sent out
297
+ *
298
+ * @name onRequested
299
+ * @access public
300
+ * @param callback The function to call after making requests
301
+ */
302
+ onRequested(callback) {
303
+ // Make sure the callback is function
304
+ if (typeof callback !== 'function') {
305
+ throw new Error('onRequested() called with an invalid callback');
306
+ }
307
+ // Set the callback
308
+ this.requested = callback;
309
+ }
310
+ /**
311
+ * On Requesting
312
+ *
313
+ * Sets the callback called before any request is send out
314
+ *
315
+ * @name onRequesting
316
+ * @access public
317
+ * @param callback The function to call before making requests
318
+ */
319
+ onRequesting(callback) {
320
+ // Make sure the callback is function
321
+ if (typeof callback !== 'function') {
322
+ throw new Error('onRequesting() called with an invalid callback');
323
+ }
324
+ // Set the callback
325
+ this.requesting = callback;
326
+ }
327
+ /**
328
+ * Read
329
+ *
330
+ * Calls a read (GET) request on the service given
331
+ *
332
+ * @name read
333
+ * @access public
334
+ * @param service The service to call
335
+ * @param noun The noun to call on the service
336
+ * @param data The data associated with the request
337
+ */
338
+ read(service, noun, data = null) {
339
+ return this.request('read', service, noun, data);
340
+ }
341
+ /**
342
+ * Session
343
+ *
344
+ * Set/Gets the current session token
345
+ *
346
+ * @name session
347
+ * @access public
348
+ * @param token The session to set
349
+ * @returns the session set
350
+ */
351
+ session(token) {
352
+ // If we are getting the token
353
+ if (token === undefined) {
354
+ return this.token;
355
+ }
356
+ // Else, we are setting the token
357
+ else {
358
+ this.token = token;
359
+ }
360
+ }
361
+ /**
362
+ * Update
363
+ *
364
+ * Calls a update (PUT) request on the service given
365
+ *
366
+ * @name update
367
+ * @access public
368
+ * @param service The service to call
369
+ * @param noun The noun to call on the service
370
+ * @param data The data associated with the request
371
+ */
372
+ update(service, noun, data = null) {
373
+ return this.request('update', service, noun, data);
374
+ }
375
+ /**
376
+ * Verbose Off
377
+ *
378
+ * Called to turn verbose mode off
379
+ *
380
+ * @name verbose_off
381
+ * @access
382
+ */
383
+ verbose_off() {
384
+ this.verbose = false;
385
+ }
386
+ /**
387
+ * Verbose On
388
+ *
389
+ * Called to turn verbose mode on
390
+ *
391
+ * @name verbose_on
392
+ * @access
393
+ */
394
+ verbose_on() {
395
+ this.verbose = true;
396
+ }
397
+ }
398
+ // Create an instance of Body
399
+ const body = new Body();
400
+ // Export it as the default
401
+ export default body;