ng2-rest 21.0.49 → 21.0.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. package/VERIFIED-BUILD-DATA.jsonc +3 -3
  2. package/browser/package.json +1 -1
  3. package/browser-prod/fesm2022/ng2-rest-browser-prod.mjs.map +1 -1
  4. package/browser-prod/package.json +1 -1
  5. package/lib/build-info._auto-generated_.d.ts +1 -1
  6. package/lib/build-info._auto-generated_.js +1 -1
  7. package/lib/package.json +1 -1
  8. package/lib-esm/app.d.ts +0 -0
  9. package/lib-esm/app.electron.d.ts +0 -0
  10. package/lib-esm/app.electron.js +82 -0
  11. package/lib-esm/app.electron.js.map +1 -0
  12. package/lib-esm/app.hosts.d.ts +3 -0
  13. package/lib-esm/app.hosts.js +48 -0
  14. package/lib-esm/app.hosts.js.map +1 -0
  15. package/lib-esm/app.js +318 -0
  16. package/lib-esm/app.js.map +1 -0
  17. package/lib-esm/app.vscode.d.ts +0 -0
  18. package/lib-esm/app.vscode.js +47 -0
  19. package/lib-esm/app.vscode.js.map +1 -0
  20. package/lib-esm/cli.d.ts +1 -0
  21. package/lib-esm/cli.js +4 -0
  22. package/lib-esm/cli.js.map +1 -0
  23. package/lib-esm/index.d.ts +1 -0
  24. package/lib-esm/index.js +2 -0
  25. package/lib-esm/index.js.map +1 -0
  26. package/lib-esm/lib/build-info._auto-generated_.d.ts +24 -0
  27. package/lib-esm/lib/build-info._auto-generated_.js +27 -0
  28. package/lib-esm/lib/build-info._auto-generated_.js.map +1 -0
  29. package/lib-esm/lib/env/env.angular-node-app.d.ts +66 -0
  30. package/lib-esm/lib/env/env.angular-node-app.js +135 -0
  31. package/lib-esm/lib/env/env.angular-node-app.js.map +1 -0
  32. package/lib-esm/lib/env/env.docs-webapp.d.ts +66 -0
  33. package/lib-esm/lib/env/env.docs-webapp.js +135 -0
  34. package/lib-esm/lib/env/env.docs-webapp.js.map +1 -0
  35. package/lib-esm/lib/env/env.electron-app.d.ts +66 -0
  36. package/lib-esm/lib/env/env.electron-app.js +135 -0
  37. package/lib-esm/lib/env/env.electron-app.js.map +1 -0
  38. package/lib-esm/lib/env/env.mobile-app.d.ts +66 -0
  39. package/lib-esm/lib/env/env.mobile-app.js +135 -0
  40. package/lib-esm/lib/env/env.mobile-app.js.map +1 -0
  41. package/lib-esm/lib/env/env.npm-lib-and-cli-tool.d.ts +66 -0
  42. package/lib-esm/lib/env/env.npm-lib-and-cli-tool.js +135 -0
  43. package/lib-esm/lib/env/env.npm-lib-and-cli-tool.js.map +1 -0
  44. package/lib-esm/lib/env/env.vscode-plugin.d.ts +66 -0
  45. package/lib-esm/lib/env/env.vscode-plugin.js +135 -0
  46. package/lib-esm/lib/env/env.vscode-plugin.js.map +1 -0
  47. package/lib-esm/lib/env/index.d.ts +6 -0
  48. package/lib-esm/lib/env/index.js +7 -0
  49. package/lib-esm/lib/env/index.js.map +1 -0
  50. package/lib-esm/lib/index._auto-generated_.d.ts +0 -0
  51. package/lib-esm/lib/index._auto-generated_.js +6 -0
  52. package/lib-esm/lib/index._auto-generated_.js.map +1 -0
  53. package/lib-esm/lib/index.d.ts +3 -0
  54. package/lib-esm/lib/index.js +4 -0
  55. package/lib-esm/lib/index.js.map +1 -0
  56. package/lib-esm/lib/new-mapping-decode.test.d.ts +1 -0
  57. package/lib-esm/lib/new-mapping-decode.test.js +119 -0
  58. package/lib-esm/lib/new-mapping-decode.test.js.map +1 -0
  59. package/lib-esm/lib/new-mapping-encode.test.d.ts +1 -0
  60. package/lib-esm/lib/new-mapping-encode.test.js +229 -0
  61. package/lib-esm/lib/new-mapping-encode.test.js.map +1 -0
  62. package/lib-esm/lib/new-mapping.d.ts +55 -0
  63. package/lib-esm/lib/new-mapping.js +314 -0
  64. package/lib-esm/lib/new-mapping.js.map +1 -0
  65. package/lib-esm/lib/new-mapping.test.d.ts +1 -0
  66. package/lib-esm/lib/new-mapping.test.js +7 -0
  67. package/lib-esm/lib/new-mapping.test.js.map +1 -0
  68. package/lib-esm/lib/ng2-rest.d.ts +301 -0
  69. package/lib-esm/lib/ng2-rest.js +998 -0
  70. package/lib-esm/lib/ng2-rest.js.map +1 -0
  71. package/lib-esm/lib/start-cli.d.ts +1 -0
  72. package/lib-esm/lib/start-cli.js +31 -0
  73. package/lib-esm/lib/start-cli.js.map +1 -0
  74. package/lib-prod/build-info._auto-generated_.d.ts +1 -1
  75. package/lib-prod/build-info._auto-generated_.js +1 -1
  76. package/lib-prod/new-mapping.js.map +1 -1
  77. package/lib-prod/ng2-rest.js.map +1 -1
  78. package/lib-prod/package.json +1 -1
  79. package/package.json +1 -1
  80. package/src.js +4 -20
  81. package/websql/package.json +1 -1
  82. package/websql-prod/fesm2022/ng2-rest-websql-prod.mjs.map +1 -1
  83. package/websql-prod/package.json +1 -1
@@ -0,0 +1,998 @@
1
+ //#region imports
2
+ import { URL } from 'url'; // @backend
3
+ import axios from 'axios';
4
+ import { JSON10 } from 'json10/lib';
5
+ import { Level, Log } from 'ng2-logger/lib';
6
+ import { firstValueFrom, from, Observable, shareReplay, Subject, } from 'rxjs';
7
+ import { CoreModels, Helpers, _ } from 'tnp-core/lib';
8
+ import { CLASS } from 'typescript-class-helpers/lib';
9
+ import { encodeMapping } from './new-mapping';
10
+ // import { Mapping } from './mapping';
11
+ //#endregion
12
+ const log = Log.create('ng2-rest', Level.WARN, Level.ERROR);
13
+ const listenErrorsSrc = new Subject();
14
+ //#region cookie
15
+ // TODO do it for nodejs
16
+ // import { CookieJar } from 'tough-cookie';
17
+ // const jar = new CookieJar();
18
+ // const rest = Resource.create('http://my-website.pl', 'api/v3/user/:userId', {
19
+ // cookieJar: jar,
20
+ // });
21
+ // await rest.model({ userId: 1 }).get(); // cookies persist across requests
22
+ // import type { CookieJar } from 'tough-cookie';
23
+ // interface ResourceOptions {
24
+ // // ...
25
+ // cookieJar?: CookieJar;
26
+ // }
27
+ // export class CookieJarInterceptor implements TaonAxiosClientInterceptor<any> {
28
+ // constructor(private readonly jar: CookieJar) {}
29
+ // intercept({ req, next }: TaonClientMiddlewareInterceptOptions<any>) {
30
+ // return new Observable<AxiosResponse<any>>(subscriber => {
31
+ // const url = req.url || '';
32
+ // // 1) attach Cookie header from jar
33
+ // this.jar.getCookieString(url, (err, cookieString) => {
34
+ // if (err) {
35
+ // subscriber.error(err);
36
+ // return;
37
+ // }
38
+ // if (cookieString) {
39
+ // req.headers = req.headers || {};
40
+ // // axios headers can be plain object or AxiosHeaders
41
+ // if (req.headers instanceof AxiosHeaders) {
42
+ // req.headers.set('Cookie', cookieString);
43
+ // } else {
44
+ // (req.headers as any)['Cookie'] = cookieString;
45
+ // }
46
+ // }
47
+ // // 2) proceed
48
+ // const sub = next.handle(req).subscribe({
49
+ // next: res => {
50
+ // // 3) store Set-Cookie back into jar
51
+ // const setCookie = (res.headers as any)?.['set-cookie'];
52
+ // const cookies: string[] =
53
+ // typeof setCookie === 'string'
54
+ // ? [setCookie]
55
+ // : Array.isArray(setCookie)
56
+ // ? setCookie
57
+ // : [];
58
+ // if (!cookies.length) {
59
+ // subscriber.next(res);
60
+ // return;
61
+ // }
62
+ // let pending = cookies.length;
63
+ // for (const c of cookies) {
64
+ // this.jar.setCookie(c, url, () => {
65
+ // pending--;
66
+ // if (pending === 0) {
67
+ // subscriber.next(res);
68
+ // }
69
+ // });
70
+ // }
71
+ // },
72
+ // error: e => subscriber.error(e),
73
+ // complete: () => subscriber.complete(),
74
+ // });
75
+ // return () => sub.unsubscribe();
76
+ // });
77
+ // });
78
+ // }
79
+ // }
80
+ export class Cookie {
81
+ static get Instance() {
82
+ if (!Cookie.__instance) {
83
+ Cookie.__instance = new Cookie();
84
+ }
85
+ return Cookie.__instance;
86
+ }
87
+ static __instance;
88
+ constructor() { }
89
+ read(name) {
90
+ if (typeof document === 'undefined')
91
+ return null;
92
+ var result = new RegExp('(?:^|; )' + encodeURIComponent(name) + '=([^;]*)').exec(document.cookie);
93
+ return result ? result[1] : null;
94
+ }
95
+ write(name, value, days) {
96
+ if (typeof document === 'undefined')
97
+ return null;
98
+ if (!days) {
99
+ days = 365 * 20;
100
+ }
101
+ var date = new Date();
102
+ date.setTime(date.getTime() + days * 24 * 60 * 60 * 1000);
103
+ var expires = '; expires=' + date.toUTCString();
104
+ document.cookie = name + '=' + value + expires + '; path=/';
105
+ }
106
+ remove(name) {
107
+ if (typeof document === 'undefined')
108
+ return null;
109
+ this.write(name, '', -1);
110
+ }
111
+ }
112
+ //#endregion
113
+ //#region get params url
114
+ /**
115
+ * Create query params string for url
116
+ *
117
+ * @export
118
+ * @param {UrlParams[]} params
119
+ * @returns {string}
120
+ */
121
+ export function getParamsUrl(params, doNotSerialize = false) {
122
+ params = _.cloneDeep(params); // TODO refactor it
123
+ let urlparts = [];
124
+ if (!params)
125
+ return '';
126
+ if (!(params instanceof Array))
127
+ return '';
128
+ if (params.length === 0)
129
+ return '';
130
+ params.forEach(urlparam => {
131
+ if (JSON.stringify(urlparam) !== '{}') {
132
+ let parameters = [];
133
+ let paramObject = urlparam;
134
+ for (let p in paramObject) {
135
+ if (paramObject[p] === void 0)
136
+ delete paramObject[p];
137
+ if (paramObject.hasOwnProperty(p) &&
138
+ typeof p === 'string' &&
139
+ p !== 'regex' &&
140
+ !(paramObject[p] instanceof RegExp)) {
141
+ if (p.length > 0 && p[0] === '/') {
142
+ let newName = p.slice(1, p.length - 1);
143
+ urlparam[newName] = urlparam[p];
144
+ urlparam[p] = void 0;
145
+ p = newName;
146
+ }
147
+ if (p.length > 0 && p[p.length - 1] === '/') {
148
+ let newName = p.slice(0, p.length - 2);
149
+ urlparam[newName] = urlparam[p];
150
+ urlparam[p] = void 0;
151
+ p = newName;
152
+ }
153
+ let v = urlparam[p];
154
+ if (v instanceof Object) {
155
+ urlparam[p] = JSON.stringify(urlparam[p]);
156
+ }
157
+ urlparam[p] = doNotSerialize
158
+ ? urlparam[p]
159
+ : encodeURIComponent(urlparam[p]);
160
+ if (urlparam.regex !== void 0 && urlparam.regex instanceof RegExp) {
161
+ if (!urlparam.regex.test(urlparam[p])) {
162
+ console.warn(`Data: ${urlparam[p]} incostistent with regex ${urlparam.regex.source}`);
163
+ }
164
+ }
165
+ parameters.push(`${p}=${urlparam[p]}`);
166
+ }
167
+ }
168
+ urlparts.push(parameters.join('&'));
169
+ }
170
+ });
171
+ let join = urlparts.join().trim();
172
+ if (join.trim() === '')
173
+ return '';
174
+ return `?${urlparts.join('&')}`;
175
+ }
176
+ //#endregion
177
+ //#region interpolate utils
178
+ export const regexisPath = /[^\..]+(\.[^\..]+)+/g;
179
+ /**
180
+ * Models like books/:id
181
+ */
182
+ const cutUrlModel = (params, models, output) => {
183
+ if (models.length === 0)
184
+ return output.join('\/');
185
+ let m = models.pop();
186
+ let param = m.match(/:[a-zA-Z0-9\.]+/)[0].replace(':', '');
187
+ const paramIsPath = regexisPath.test(param);
188
+ // log.i('cut param', param)
189
+ let model = m.match(/[a-zA-Z0-9]+\//)[0].replace('\/', '');
190
+ if (params === void 0 ||
191
+ (paramIsPath
192
+ ? _.get(params, param) === void 0
193
+ : params[param] === void 0) ||
194
+ param === 'undefined') {
195
+ output.length = 0;
196
+ output.unshift(model);
197
+ return cutUrlModel(params, models, output);
198
+ }
199
+ else {
200
+ if (paramIsPath) {
201
+ // log.i('param is path', param)
202
+ let mrep = m.replace(new RegExp(`:${param}`, 'g'), `${_.get(params, param)}`);
203
+ output.unshift(mrep);
204
+ return cutUrlModel(params, models, output);
205
+ }
206
+ else {
207
+ // log.i('param is normal', param)
208
+ let mrep = m.replace(new RegExp(`:${param}`, 'g'), `${params[param]}`);
209
+ output.unshift(mrep);
210
+ return cutUrlModel(params, models, output);
211
+ }
212
+ }
213
+ };
214
+ /**
215
+ * let pattern = '/books/:bookid';
216
+ * let url = `/books/34`;
217
+ */
218
+ export function interpolateParamsToUrl(params, url) {
219
+ const regexInt = /\[\[([^\..]+\.[^\..]+)+\]\]/g;
220
+ url = url
221
+ .split('/')
222
+ .map(p => {
223
+ // log.d('url parts', p)
224
+ let isParam = p.startsWith(':');
225
+ if (isParam) {
226
+ let part = p.slice(1);
227
+ // log.d('url param part', p)
228
+ if (regexInt.test(part)) {
229
+ // let level = (url.split('.').length - 1)
230
+ part = part.replace('[[', '');
231
+ part = part.replace(']]', '');
232
+ }
233
+ return `:${part}`;
234
+ }
235
+ return p;
236
+ })
237
+ .join('/');
238
+ // log.i('URL TO EXPOSE', url)
239
+ // log.i('params', params)
240
+ let slash = {
241
+ start: url.charAt(0) === '\/',
242
+ end: url.charAt(url.length - 1) === '\/',
243
+ };
244
+ let morePramsOnEnd = url.match(/(\/:[a-zA-Z0-9\.]+){2,10}/g);
245
+ if (morePramsOnEnd &&
246
+ Array.isArray(morePramsOnEnd) &&
247
+ morePramsOnEnd.length === 1) {
248
+ // log.i('morePramsOnEnd', morePramsOnEnd)
249
+ let m = morePramsOnEnd[0];
250
+ let match = m.match(/\/:[a-zA-Z0-9\.]+/g);
251
+ // log.i('match', match)
252
+ match.forEach(e => {
253
+ let c = e.replace('\/:', '');
254
+ // log.i('c', c)
255
+ if (regexisPath.test(c)) {
256
+ url = url.replace(e, `/${_.get(params, c)}`);
257
+ }
258
+ else {
259
+ url = url.replace(e, `/${params[c]}`);
260
+ }
261
+ // log.i('prog url', url)
262
+ });
263
+ return url;
264
+ }
265
+ let nestedParams = url.match(/[a-zA-Z0-9]+\/:[a-zA-Z0-9\.]+/g);
266
+ if (!nestedParams ||
267
+ (Array.isArray(nestedParams) && nestedParams.length === 0))
268
+ return url;
269
+ // check alone params
270
+ if (!slash.end)
271
+ url = `${url}/`;
272
+ let addUndefinedForAlone = !/:[a-zA-Z0-9\.]+\/$/g.test(url) && /[a-zA-Z0-9]+\/$/g.test(url);
273
+ let replace = (nestedParams.length > 1 ? nestedParams.join('\/') : nestedParams[0]) +
274
+ (addUndefinedForAlone ? '\/' + url.match(/[a-zA-Z0-9]+\/$/g)[0] : '\/');
275
+ let beginHref = url.replace(replace, '');
276
+ if (addUndefinedForAlone) {
277
+ url = url.replace(/\/$/g, '/:undefined');
278
+ nestedParams = url.match(/[a-zA-Z0-9]+\/:[a-zA-Z0-9\.]+/g);
279
+ url = cutUrlModel(params, nestedParams, []);
280
+ }
281
+ else {
282
+ url = cutUrlModel(params, nestedParams, []);
283
+ }
284
+ url = beginHref + url;
285
+ if (url.charAt(url.length - 1) !== '/' && slash.end)
286
+ url = `${url}/`;
287
+ if (url.charAt(0) !== '\/' && slash.start)
288
+ url = `/${url}`;
289
+ return url;
290
+ }
291
+ // Optional helper for passing around context (browser/client)
292
+ // === Backend handler (last in chain) ===
293
+ export class AxiosBackendHandler {
294
+ handle(req) {
295
+ // axios returns a Promise; wrap as Observable
296
+ return from(axios.request(req));
297
+ }
298
+ }
299
+ // === Chain builder (request: forward order, response: reverse order) ===
300
+ export const buildInterceptorChain = (globalInterceptors, backend) => {
301
+ return globalInterceptors.reduceRight((next, interceptor) => ({
302
+ handle: req => interceptor.intercept({ req, next }),
303
+ }), backend);
304
+ };
305
+ export class RestHeaders {
306
+ /** @internal header names are lower case */
307
+ _headers = new Map();
308
+ /** @internal map lower case names to actual names */
309
+ _normalizedNames = new Map();
310
+ static from(headers) {
311
+ return new RestHeaders(headers || {});
312
+ }
313
+ apply(headers) {
314
+ if (headers instanceof RestHeaders) {
315
+ headers.forEach((values, name) => {
316
+ values.forEach(value => this.set(name, value));
317
+ });
318
+ }
319
+ else {
320
+ Object.keys(headers).forEach((name) => {
321
+ const values = (Array.isArray(headers[name]) ? headers[name] : [headers[name]]);
322
+ this.delete(name);
323
+ values.forEach(value => this.set(name, value));
324
+ });
325
+ }
326
+ return this;
327
+ }
328
+ constructor(headers) {
329
+ this.apply(headers);
330
+ }
331
+ /**
332
+ * Returns a new RestHeaders instance from the given DOMString of Response RestHeaders
333
+ */
334
+ static fromResponseHeaderString(headersString) {
335
+ const headers = new RestHeaders();
336
+ // console.log({
337
+ // headersString
338
+ // })
339
+ headersString.split('\n').forEach(line => {
340
+ const index = line.indexOf(':');
341
+ if (index > 0) {
342
+ const name = line.slice(0, index);
343
+ const value = line.slice(index + 1).trim();
344
+ headers.set(name, value);
345
+ }
346
+ });
347
+ return headers;
348
+ }
349
+ /**
350
+ * Appends a header to existing list of header values for a given header name.
351
+ */
352
+ append(name, value) {
353
+ const values = this.getAll(name);
354
+ if (values === null) {
355
+ this.set(name, value);
356
+ }
357
+ else {
358
+ values.push(value);
359
+ }
360
+ }
361
+ /**
362
+ * Deletes all header values for the given name.
363
+ */
364
+ delete(name) {
365
+ const lcName = name.toLowerCase();
366
+ this._normalizedNames.delete(lcName);
367
+ this._headers.delete(lcName);
368
+ }
369
+ forEach(fn) {
370
+ this._headers.forEach((values, lcName) => fn(values, this._normalizedNames.get(lcName), this._headers));
371
+ }
372
+ /**
373
+ * Returns first header that matches given name.
374
+ */
375
+ get(name) {
376
+ const values = this.getAll(name);
377
+ if (values === null) {
378
+ return null;
379
+ }
380
+ return values.length > 0 ? values[0] : null;
381
+ }
382
+ /**
383
+ * Checks for existence of header by given name.
384
+ */
385
+ has(name) {
386
+ return this._headers.has(name.toLowerCase());
387
+ }
388
+ /**
389
+ * Returns the names of the headers
390
+ */
391
+ keys() {
392
+ return Array.from(this._normalizedNames.values());
393
+ }
394
+ /**
395
+ * Sets or overrides header value for given name.
396
+ */
397
+ set(name, value) {
398
+ if (Array.isArray(value)) {
399
+ if (value.length) {
400
+ this._headers.set(name.toLowerCase(), [value.join(',')]);
401
+ }
402
+ }
403
+ else {
404
+ this._headers.set(name.toLowerCase(), [value]);
405
+ }
406
+ this.mayBeSetNormalizedName(name);
407
+ }
408
+ /**
409
+ * Returns values of all headers.
410
+ */
411
+ values() {
412
+ return Array.from(this._headers.values());
413
+ }
414
+ /**
415
+ * Returns string of all headers.
416
+ */
417
+ // TODO(vicb): returns {[name: string]: string[]}
418
+ toJSON() {
419
+ const serialized = {};
420
+ if (!this._headers) {
421
+ // debugger
422
+ }
423
+ // console.log('serializing headers',this._headers)
424
+ this._headers.forEach((values, name) => {
425
+ const split = [];
426
+ values.forEach(v => split.push(...v.split(',')));
427
+ // console.log({
428
+ // values
429
+ // })
430
+ // values.forEach(v => split.push(...(v ? v : '').split(',')));
431
+ serialized[this._normalizedNames.get(name)] = split;
432
+ });
433
+ return serialized;
434
+ }
435
+ /**
436
+ * Returns list of header values for a given name.
437
+ */
438
+ getAll(name) {
439
+ return this.has(name) ? this._headers.get(name.toLowerCase()) : null;
440
+ }
441
+ mayBeSetNormalizedName(name) {
442
+ const lcName = name.toLowerCase();
443
+ if (!this._normalizedNames.has(lcName)) {
444
+ this._normalizedNames.set(lcName, name);
445
+ }
446
+ }
447
+ }
448
+ //#endregion
449
+ //#region handle result source request options
450
+ class RestCommonHttpResponseWrapper {
451
+ }
452
+ export class RestResponseWrapper extends RestCommonHttpResponseWrapper {
453
+ }
454
+ export class RestErrorResponseWrapper extends RestCommonHttpResponseWrapper {
455
+ }
456
+ //#endregion
457
+ //#region base body
458
+ export class BaseBody {
459
+ toJSON(data, opt) {
460
+ opt = opt || { isJSONArray: false };
461
+ let r = opt.isJSONArray ? [] : {};
462
+ if (typeof data === 'string') {
463
+ try {
464
+ let parsed = JSON.parse(data);
465
+ if (typeof parsed === 'string' && parsed.trim().startsWith('{')) {
466
+ parsed = JSON.parse(parsed);
467
+ }
468
+ if (opt.parsingError && parsed[CoreModels.TaonHttpErrorCustomProp]) {
469
+ return _.merge(new RestErrorResponseWrapper(), parsed);
470
+ }
471
+ return parsed;
472
+ }
473
+ catch (e) { }
474
+ }
475
+ else if (typeof data === 'object') {
476
+ return data;
477
+ }
478
+ return r;
479
+ }
480
+ }
481
+ export class HttpBody extends BaseBody {
482
+ url;
483
+ method;
484
+ headers;
485
+ responseText;
486
+ options;
487
+ isArray;
488
+ constructor(url, method, headers, responseText, options, isArray) {
489
+ super();
490
+ this.url = url;
491
+ this.method = method;
492
+ this.headers = headers;
493
+ this.responseText = responseText;
494
+ this.options = options;
495
+ this.isArray = isArray;
496
+ }
497
+ get entity() {
498
+ if (typeof this.options.responseMapping?.entity === 'string') {
499
+ // const headerWithMapping = headers.get(entity);
500
+ // console.log('header key ',this.options.responseMapping?.entity);
501
+ // console.log(this.headers)
502
+ let entityJSON = this.headers?.getAll(this.options.responseMapping?.entity);
503
+ if (!!entityJSON) {
504
+ return JSON.parse(entityJSON.join());
505
+ }
506
+ }
507
+ const entityAsResolvableFn = this.options?.responseMapping?.entity;
508
+ if (typeof entityAsResolvableFn === 'function') {
509
+ const mappingFromFunction = entityAsResolvableFn();
510
+ // console.log({ mappingFromFunction });
511
+ return mappingFromFunction;
512
+ }
513
+ return this.options.responseMapping?.entity;
514
+ }
515
+ get circular() {
516
+ if (typeof this.options.responseMapping?.circular === 'string') {
517
+ // const headerWithMapping = headers.get(circular);
518
+ let circuralJSON = this.headers?.getAll(this.options.responseMapping.circular);
519
+ if (!!circuralJSON) {
520
+ return JSON.parse(circuralJSON.join());
521
+ }
522
+ }
523
+ return (this.options.responseMapping?.circular || []);
524
+ }
525
+ get blob() {
526
+ return this.responseText;
527
+ }
528
+ get booleanValue() {
529
+ if (!Helpers.isBlob(this.responseText)) {
530
+ return ['ok', 'true'].includes(this.responseText?.trim());
531
+ }
532
+ }
533
+ get numericValue() {
534
+ if (!Helpers.isBlob(this.responseText)) {
535
+ return Number(this.responseText?.trim());
536
+ }
537
+ }
538
+ get rawJson() {
539
+ if (!Helpers.isBlob(this.responseText)) {
540
+ let res = this.toJSON(this.responseText, { isJSONArray: this.isArray });
541
+ if (this.circular && Array.isArray(this.circular)) {
542
+ res = JSON10.parse(JSON.stringify(res), this.circular);
543
+ }
544
+ return res;
545
+ }
546
+ }
547
+ get json() {
548
+ const isBlob = Helpers.isBlob(this.responseText);
549
+ if (isBlob) {
550
+ return void 0;
551
+ }
552
+ if (this.entity && typeof this.entity === 'object') {
553
+ const json = this.toJSON(this.responseText, {
554
+ isJSONArray: this.isArray,
555
+ });
556
+ // console.log({ entityMapping: this.entity })
557
+ const resEntityMapping = encodeMapping(json, this.entity, this.circular);
558
+ // console.log({ resEntityMapping })
559
+ this.displayWarningWhenNotUsingProperAPI(resEntityMapping);
560
+ return resEntityMapping;
561
+ }
562
+ let res = this.toJSON(this.responseText, { isJSONArray: this.isArray });
563
+ if (this.circular && Array.isArray(this.circular)) {
564
+ res = JSON10.parse(JSON.stringify(res), this.circular);
565
+ }
566
+ this.displayWarningWhenNotUsingProperAPI(res);
567
+ return res;
568
+ }
569
+ displayWarningWhenNotUsingProperAPI(res) {
570
+ if (!this.options.useArrayApiWarning) {
571
+ return;
572
+ }
573
+ if (this.isArray) {
574
+ Helpers.warn(`[${this.method}: ${this.url}]
575
+ Your api response is object, but you are using .array api`);
576
+ }
577
+ else {
578
+ if (Array.isArray(res)) {
579
+ Helpers.warn(`[${this.method}: ${this.url}]
580
+ Your api response is array, but you are using object api instread .arrray.`);
581
+ }
582
+ }
583
+ }
584
+ /**
585
+ * undefined when blob
586
+ */
587
+ get text() {
588
+ if (!Helpers.isBlob(this.responseText)) {
589
+ return this.responseText
590
+ .replace(/^\"/, '')
591
+ .replace(/\"$/, '');
592
+ }
593
+ }
594
+ }
595
+ export class ErrorBody extends BaseBody {
596
+ url;
597
+ data;
598
+ constructor(url, data) {
599
+ super();
600
+ this.url = url;
601
+ this.data = data;
602
+ }
603
+ get json() {
604
+ return this.toJSON(this.data, { parsingError: true });
605
+ }
606
+ get text() {
607
+ return this.data;
608
+ }
609
+ }
610
+ export class BaseResponse {
611
+ responseText;
612
+ options;
613
+ statusCode;
614
+ headers;
615
+ isArray;
616
+ constructor(responseText, options, statusCode, headers, isArray) {
617
+ this.responseText = responseText;
618
+ this.options = options;
619
+ this.statusCode = statusCode;
620
+ this.headers = headers;
621
+ this.isArray = isArray;
622
+ }
623
+ }
624
+ //#endregion
625
+ //#region http response
626
+ export class HttpResponse extends BaseResponse {
627
+ url;
628
+ method;
629
+ responseText;
630
+ headers;
631
+ statusCode;
632
+ options;
633
+ isArray;
634
+ body;
635
+ constructor(url, method, responseText, headers, statusCode, options, isArray) {
636
+ super(responseText, options, statusCode, headers, isArray);
637
+ this.url = url;
638
+ this.method = method;
639
+ this.responseText = responseText;
640
+ this.headers = headers;
641
+ this.statusCode = statusCode;
642
+ this.options = options;
643
+ this.isArray = isArray;
644
+ this.body = new HttpBody(url, method, headers, responseText, options, isArray);
645
+ }
646
+ }
647
+ export class HttpResponseError extends BaseResponse {
648
+ url;
649
+ method;
650
+ responseText;
651
+ options;
652
+ headers;
653
+ statusCode;
654
+ isArray;
655
+ body;
656
+ constructor(url, method, responseText, options, headers, statusCode, isArray) {
657
+ super(responseText, options, statusCode, headers, isArray);
658
+ this.url = url;
659
+ this.method = method;
660
+ this.responseText = responseText;
661
+ this.options = options;
662
+ this.headers = headers;
663
+ this.statusCode = statusCode;
664
+ this.isArray = isArray;
665
+ this.body = new ErrorBody(url, responseText);
666
+ }
667
+ }
668
+ //#region default headers
669
+ export const HeaderKeyContentType = 'Content-Type';
670
+ export const HeaderKeyAccept = 'Accept';
671
+ export const DEFAULT_HEADERS = {
672
+ // JSON (most APIs)
673
+ APPLICATION_JSON: RestHeaders.from({
674
+ [HeaderKeyContentType]: 'application/json',
675
+ [HeaderKeyAccept]: 'application/json',
676
+ }),
677
+ // JSON:API (you already have)
678
+ APPLICATION_VND_API_JSON: RestHeaders.from({
679
+ [HeaderKeyContentType]: 'application/vnd.api+json',
680
+ [HeaderKeyAccept]: 'application/vnd.api+json',
681
+ }),
682
+ // Form URL encoded (old APIs, OAuth token endpoints)
683
+ APPLICATION_X_WWW_FORM_URLENCODED: RestHeaders.from({
684
+ [HeaderKeyContentType]: 'application/x-www-form-urlencoded',
685
+ [HeaderKeyAccept]: 'application/json',
686
+ }),
687
+ // Multipart form-data (file uploads) — note: boundary will be set by FormData in Node
688
+ MULTIPART_FORM_DATA: RestHeaders.from({
689
+ [HeaderKeyContentType]: 'multipart/form-data',
690
+ [HeaderKeyAccept]: 'application/json',
691
+ }),
692
+ // Plain text request/response (health checks, simple endpoints)
693
+ TEXT_PLAIN: RestHeaders.from({
694
+ [HeaderKeyContentType]: 'text/plain; charset=utf-8',
695
+ [HeaderKeyAccept]: 'text/plain',
696
+ }),
697
+ // Accept anything (downloads, weird backends)
698
+ ACCEPT_ANY: RestHeaders.from({
699
+ [HeaderKeyAccept]: '*/*',
700
+ }),
701
+ // Binary download (still just headers; axios responseType controls actual handling)
702
+ OCTET_STREAM: RestHeaders.from({
703
+ [HeaderKeyAccept]: 'application/octet-stream',
704
+ }),
705
+ };
706
+ //#endregion
707
+ //#region abstract resource reponse class
708
+ export class ResourceResponse {
709
+ httpMethodName;
710
+ urlOrigin;
711
+ urlPathname;
712
+ options;
713
+ body;
714
+ urlParams;
715
+ axiosOptions;
716
+ isArray;
717
+ headers;
718
+ globalInterceptors;
719
+ methodsInterceptors;
720
+ [Symbol.toStringTag] = 'Promise';
721
+ _promise;
722
+ _promiseAbort;
723
+ _observable;
724
+ //#region constructor
725
+ constructor(httpMethodName, urlOrigin, urlPathname, options, body, urlParams, axiosOptions, isArray, headers, globalInterceptors, methodsInterceptors) {
726
+ this.httpMethodName = httpMethodName;
727
+ this.urlOrigin = urlOrigin;
728
+ this.urlPathname = urlPathname;
729
+ this.options = options;
730
+ this.body = body;
731
+ this.urlParams = urlParams;
732
+ this.axiosOptions = axiosOptions;
733
+ this.isArray = isArray;
734
+ this.headers = headers;
735
+ this.globalInterceptors = globalInterceptors;
736
+ this.methodsInterceptors = methodsInterceptors;
737
+ }
738
+ /**
739
+ * ✅ Explicit cancel (useful for "promise style")
740
+ */
741
+ cancel(reason) {
742
+ this._promiseAbort?.abort(reason);
743
+ }
744
+ /**
745
+ * Promise API (cannot be auto-cancelled by consumer, so we expose cancel())
746
+ */
747
+ get promise() {
748
+ if (!this._promise) {
749
+ this._promiseAbort = new AbortController();
750
+ this._promise = this.makeRequest(this._promiseAbort.signal);
751
+ }
752
+ return this._promise;
753
+ }
754
+ then(onfulfilled, onrejected) {
755
+ return this.promise.then(onfulfilled, onrejected);
756
+ }
757
+ catch(onrejected) {
758
+ return this.promise.catch(onrejected);
759
+ }
760
+ finally(onfinally) {
761
+ return this.promise.finally(onfinally);
762
+ }
763
+ /**
764
+ * ✅ Observable owns AbortController:
765
+ * - subscribe starts request
766
+ * - unsubscribe aborts request
767
+ * - shareReplay shares the same in-flight request among subscribers
768
+ */
769
+ get observable() {
770
+ if (!this._observable) {
771
+ this._observable = new Observable(subscriber => {
772
+ const ac = new AbortController();
773
+ this.makeRequest(ac.signal)
774
+ .then(res => {
775
+ if (res instanceof HttpResponseError) {
776
+ subscriber.error(res);
777
+ return;
778
+ }
779
+ subscriber.next(res);
780
+ subscriber.complete();
781
+ })
782
+ .catch(err => subscriber.error(err));
783
+ return () => ac.abort('rxjs-unsubscribe');
784
+ }).pipe(shareReplay({ bufferSize: 1, refCount: true }));
785
+ }
786
+ return this._observable;
787
+ }
788
+ // -------------------------
789
+ // Internals
790
+ // -------------------------
791
+ creatUrl(params, doNotSerializeParams = false) {
792
+ const origin = (this.urlOrigin || '').replace(/\/+$/, '');
793
+ const path = (this.urlPathname || '').replace(/^\/+/, '');
794
+ const endpoint = `${origin}/${path}`;
795
+ return `${endpoint}${getParamsUrl(params, doNotSerializeParams)}`;
796
+ }
797
+ }
798
+ //#endregion
799
+ //#region resource reponse http strategy
800
+ class ResourceResponseHttp extends ResourceResponse {
801
+ async makeRequest(abortSignal) {
802
+ const url = this.creatUrl(this.urlParams, !!this.axiosOptions?.doNotSerializeParams);
803
+ const method = this.httpMethodName;
804
+ log.d(`Requesting ${method} ${url}`);
805
+ const isFormData = CLASS.getNameFromObject(this.body) === 'FormData';
806
+ const formData = isFormData ? this.body : void 0;
807
+ //#region @backend
808
+ if (formData) {
809
+ const headersForm = formData.getHeaders();
810
+ headersForm['Content-Length'] = formData.getLengthSync();
811
+ for (const [key, value] of Object.entries(headersForm)) {
812
+ this.headers.set(key, value?.toString());
813
+ }
814
+ }
815
+ //#endregion
816
+ const responseType = this.headers.get('responsetypeaxios')?.toString() || 'text';
817
+ const headersObj = Object.fromEntries(Object.entries(this.headers.toJSON()).map(([k, v]) => [
818
+ k,
819
+ Array.isArray(v) ? v.join(',') : v,
820
+ ]));
821
+ const axiosConfig = {
822
+ url,
823
+ method,
824
+ data: this.body,
825
+ responseType,
826
+ headers: headersObj,
827
+ signal: abortSignal, // ✅ this is the key
828
+ ...this.axiosOptions,
829
+ };
830
+ if (isFormData) {
831
+ axiosConfig.maxBodyLength = Infinity;
832
+ }
833
+ try {
834
+ const uri = new URL(url);
835
+ const backend = new AxiosBackendHandler();
836
+ const globalInterceptors = Array.from(this.globalInterceptors.values());
837
+ const methodInterceptors = Array.from(this.methodsInterceptors.entries())
838
+ .filter(([key]) => key.endsWith(`-${method?.toUpperCase()}-${uri.pathname}`))
839
+ .map(([_, interceptor]) => interceptor);
840
+ const handler = buildInterceptorChain([...globalInterceptors, ...methodInterceptors], backend);
841
+ const response = await firstValueFrom(handler.handle(axiosConfig));
842
+ return new HttpResponse(url, method, response.data, RestHeaders.from(response.headers), response.status, this.options, this.isArray);
843
+ }
844
+ catch (catchedError) {
845
+ // ✅ treat cancellation separately (nice UX)
846
+ if (catchedError?.code === 'ERR_CANCELED' ||
847
+ catchedError?.name === 'CanceledError') {
848
+ throw new HttpResponseError(url, method, JSON.stringify({ message: 'Request canceled' }), this.options, RestHeaders.from(), 0, this.isArray);
849
+ }
850
+ //#region handle global error listener for notificaitons
851
+ if (typeof catchedError === 'object' &&
852
+ catchedError.response &&
853
+ catchedError.response.data) {
854
+ const err = catchedError.response.data;
855
+ const msg = catchedError.response.data.message || '';
856
+ // console.log({
857
+ // 'err.stack': err?.stack
858
+ // })
859
+ let stack = (err.stack || '').split('\n');
860
+ listenErrorsSrc.next({
861
+ msg,
862
+ stack,
863
+ data: catchedError.response.data,
864
+ });
865
+ }
866
+ //#endregion
867
+ const status = catchedError?.response?.status ?? 0; // ✅ FIX: you used "status" before defining it
868
+ const data = catchedError?.response?.data ?? catchedError?.message ?? catchedError;
869
+ const responseText = typeof data === 'string' ? data : JSON.stringify(data);
870
+ throw new HttpResponseError(url, method, responseText, this.options, RestHeaders.from(catchedError?.response?.headers), status, this.isArray);
871
+ }
872
+ }
873
+ }
874
+ [];
875
+ //#endregion
876
+ //#region resource namespace
877
+ export var Resource;
878
+ (function (Resource) {
879
+ Resource.globalInterceptors = new Map();
880
+ Resource.methodsInterceptors = new Map();
881
+ Resource.listenErrors = listenErrorsSrc.asObservable();
882
+ Resource.Cookies = Cookie.Instance;
883
+ // eslint-disable-next-line @typescript-eslint/explicit-function-return-type
884
+ function create(originUrl, pathnameModel, resourceOptions) {
885
+ return {
886
+ model: (interpolateParams, overrideOptions) => {
887
+ const methods = (isArray = false) => {
888
+ const methodsObj = {};
889
+ for (const methodName of CoreModels.HttpMethodArr) {
890
+ methodsObj[methodName] = (body, urlParams, axiosOptions) => {
891
+ let localPathname = pathnameModel;
892
+ if (!localPathname.startsWith('/')) {
893
+ localPathname = `/${localPathname}`;
894
+ }
895
+ let localOriginUrl = originUrl;
896
+ if (localOriginUrl.endsWith('/')) {
897
+ localOriginUrl = localOriginUrl.replace(/\/$/, '');
898
+ }
899
+ let localUrl = new URL(`${localOriginUrl}${localPathname}`);
900
+ //#region validate pathname model
901
+ const badRestRegEX = new RegExp('((\/:)[a-z]+)+', 'g');
902
+ const matchArr = localPathname.match(badRestRegEX) || [];
903
+ const badModelsNextToEachOther = matchArr.join();
904
+ const atleas2DoubleDots = (badModelsNextToEachOther.match(new RegExp(':', 'g')) || [])
905
+ .length >= 2;
906
+ if (atleas2DoubleDots &&
907
+ localPathname.search(badModelsNextToEachOther) !== -1) {
908
+ throw new Error(`
909
+
910
+ Bad rest model: ${localPathname}
911
+
912
+ Do not create rest models like this: /book/author/:bookid/:authorid
913
+ Instead use nested approach: /book/:bookid/author/:authorid
914
+ `);
915
+ }
916
+ //#endregion
917
+ let options = resourceOptions;
918
+ options = options || {};
919
+ options.responseMapping = options.responseMapping || {};
920
+ options = {
921
+ ...options,
922
+ ...(_.isFunction(overrideOptions)
923
+ ? overrideOptions(options)
924
+ : overrideOptions || {}),
925
+ };
926
+ if (interpolateParams) {
927
+ // console.log({ interpolateParams });
928
+ // interpolate args
929
+ let pathNameInterpolated = interpolateParamsToUrl(interpolateParams, localUrl.pathname);
930
+ // console.log(
931
+ // `interpolated ${pathNameInterpolated}, url ${url.toString()}`,
932
+ // );
933
+ localUrl = new URL(`${localUrl.origin}/${pathNameInterpolated}`);
934
+ }
935
+ const headers = RestHeaders.from(options.headers);
936
+ options.strategy = options.strategy || 'http';
937
+ options.defaultHeadersProfile =
938
+ options.defaultHeadersProfile || 'APPLICATION_JSON';
939
+ if (options.defaultHeadersProfile) {
940
+ DEFAULT_HEADERS[options.defaultHeadersProfile].forEach((values, name) => {
941
+ values.forEach(headerValue => headers.set(name, headerValue));
942
+ });
943
+ }
944
+ if (options.strategy === 'http') {
945
+ return new ResourceResponseHttp(methodName, localUrl.origin, localUrl.pathname, options, body, urlParams, axiosOptions, isArray, headers, Resource.globalInterceptors, Resource.methodsInterceptors);
946
+ }
947
+ else if (options.strategy === 'ipc-electron') {
948
+ // TODO later
949
+ }
950
+ else if (options.strategy === 'js-mock') {
951
+ // TODO later
952
+ }
953
+ };
954
+ }
955
+ return methodsObj;
956
+ };
957
+ const methodsRes = methods();
958
+ const methodsArrayRes = methods(true);
959
+ const res = {
960
+ get array() {
961
+ return methodsArrayRes;
962
+ },
963
+ ...methodsRes,
964
+ };
965
+ return res;
966
+ },
967
+ };
968
+ }
969
+ Resource.create = create;
970
+ })(Resource || (Resource = {}));
971
+ //#endregion
972
+ //#region exmaple usage
973
+ /**
974
+ * EXample useage
975
+ */
976
+ class ExampleBook {
977
+ title;
978
+ }
979
+ async function example() {
980
+ const rest = Resource.create('http://my-website.pl', 'api/v3/user/:userId', {
981
+ responseMapping: {
982
+ entity: () => ({ '': ExampleBook }),
983
+ },
984
+ });
985
+ const response = await rest.model({ userId: 1 }).get();
986
+ response; // type of response should be HttpResponse
987
+ const responseObservable = rest
988
+ .model({ userId: 1 })
989
+ .array.post([new ExampleBook()], [{ 'location-id': 123 }]).observable;
990
+ const responseObservableOnlyONe = rest
991
+ .model({ userId: 1 })
992
+ .post(new ExampleBook(), [{ 'location-id': 123 }]).observable;
993
+ responseObservable.subscribe(data => {
994
+ data; // HttpResponse<ExampleBook>
995
+ });
996
+ }
997
+ //#endregion
998
+ //# sourceMappingURL=ng2-rest.js.map