@randajan/api-kit 2.0.0 → 3.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.
@@ -20,17 +20,18 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
20
20
  var index_exports = {};
21
21
  __export(index_exports, {
22
22
  Api: () => Api,
23
- ApiError: () => ApiError,
24
23
  Fetch: () => Fetch,
24
+ FetchError: () => FetchError,
25
+ HttpError: () => HttpError,
25
26
  createApi: () => server_default,
26
27
  createFetch: () => client_default
27
28
  });
28
29
  module.exports = __toCommonJS(index_exports);
29
30
 
30
31
  // <define:__slib_info>
31
- var define_slib_info_default = { isBuild: true, name: "@randajan/api-kit", description: "A lightweight wrapper around fetch that always returns a structured JSON response, handling errors and network issues gracefully", version: "1.2.0", author: { name: "Jan Randa", email: "jnranda@gmail.com", url: "https://www.linkedin.com/in/randajan/" }, env: "development", mode: "web", port: 3e3, dir: { root: "C:\\dev\\lib\\api-kit", dist: "dist" } };
32
+ var define_slib_info_default = { isBuild: true, name: "@randajan/api-kit", description: "A lightweight wrapper around fetch that always returns a structured JSON response, handling errors and network issues gracefully", version: "3.0.0", author: { name: "Jan Randa", email: "jnranda@gmail.com", url: "https://www.linkedin.com/in/randajan/" }, env: "development", mode: "web", port: 3e3, dir: { root: "C:\\dev\\lib\\api-kit", dist: "dist" } };
32
33
 
33
- // src/arc/class/ApiError.js
34
+ // src/arc/class/FetchError.js
34
35
  var import_props = require("@randajan/props");
35
36
 
36
37
  // src/arc/tool.js
@@ -57,44 +58,34 @@ var doFn = (any) => isFn(any) ? any() : any;
57
58
  var isProm = (any) => any instanceof Promise;
58
59
  var isErr = (any) => any instanceof Error;
59
60
 
60
- // src/arc/class/ApiError.js
61
- var ApiError = class _ApiError extends Error {
62
- static create(code, message, httpStatusCode = 400, remoteStack = void 0) {
63
- return new _ApiError(code, message, httpStatusCode, remoteStack);
64
- }
61
+ // src/arc/class/FetchError.js
62
+ var FetchError = class _FetchError extends Error {
65
63
  static is(any) {
66
- return any instanceof _ApiError;
64
+ return any && any instanceof _FetchError;
67
65
  }
68
- static to(code, any, httpStatusCode = 500) {
69
- if (_ApiError.is(any)) {
70
- return any;
71
- }
72
- const msg = toStr(any?.message || any) || "Unknown";
73
- const apierr = _ApiError.create(code, msg, httpStatusCode);
74
- return (0, import_props.solid)(apierr, "stack", any?.stack);
66
+ static code(code, message, opt = {}) {
67
+ opt.code = code;
68
+ return new _FetchError(message, opt);
75
69
  }
76
- constructor(code, message, httpStatusCode = 400, remoteStack = void 0) {
77
- super(message);
70
+ constructor(message, options = {}) {
71
+ const { code, ...opt } = options || {};
72
+ super(message, opt);
78
73
  (0, import_props.safe)(this, {}, "code", (t, f) => mrgStr(t, f, "."));
79
- (0, import_props.solid)(this, "httpStatusCode", httpStatusCode ?? 400, true);
80
- (0, import_props.solid)(this, "remoteStack", remoteStack);
81
74
  this.rise(code);
82
75
  }
83
- rise(code = 0) {
84
- this.code = code || 0;
85
- return this;
86
- }
87
- exposeStack(exposeStack = true) {
88
- (0, import_props.solid)(this, "_exposeStack", !!exposeStack, false, true);
76
+ rise(code) {
77
+ if (code == null) {
78
+ return this;
79
+ }
80
+ this.code = code;
89
81
  return this;
90
82
  }
91
83
  toJSON() {
92
- const { message, stack, _exposeStack } = this;
84
+ const { message, cause } = this;
93
85
  const body = { message, ...this };
94
- if (_exposeStack) {
95
- body.stack = stack;
96
- } else {
97
- delete body.stack;
86
+ if (cause) {
87
+ const { message: message2, stack } = cause;
88
+ body.cause = { message: message2, stack };
98
89
  }
99
90
  return body;
100
91
  }
@@ -104,8 +95,47 @@ var ApiError = class _ApiError extends Error {
104
95
  }
105
96
  };
106
97
 
98
+ // src/arc/class/HttpError.js
99
+ var import_props2 = require("@randajan/props");
100
+ var HttpError = class _HttpError extends FetchError {
101
+ static is(any) {
102
+ return any && any instanceof _HttpError;
103
+ }
104
+ static code(code, message, httpStatusCode = 400, opt = {}) {
105
+ opt.code = code;
106
+ return new _HttpError(message, httpStatusCode, opt);
107
+ }
108
+ constructor(message, httpStatusCode = 400, options = {}) {
109
+ const { detail, exposeCause, ...opt } = options || {};
110
+ const http = httpStatusCode ?? 400;
111
+ const msg = message || `HTTP:${http}`;
112
+ super(msg, opt);
113
+ (0, import_props2.safe)(this, {}, "_exposeCause", (t) => !!t, void 0, false);
114
+ this.detail = detail;
115
+ this.httpStatusCode = http;
116
+ this._exposeCause = exposeCause;
117
+ }
118
+ exposeCause(exposeCause = true) {
119
+ this._exposeCause = exposeCause;
120
+ return this;
121
+ }
122
+ toJSON() {
123
+ const { message, cause, _exposeCause } = this;
124
+ const body = { message, ...this };
125
+ if (_exposeCause && cause) {
126
+ const { message: message2, stack } = cause;
127
+ body.cause = { message: message2, stack, ...cause };
128
+ }
129
+ return body;
130
+ }
131
+ toString() {
132
+ const { httpStatusCode } = this;
133
+ return super.toString() + ` [${httpStatusCode}]`;
134
+ }
135
+ };
136
+
107
137
  // src/server/class/Api.js
108
- var import_props3 = require("@randajan/props");
138
+ var import_props4 = require("@randajan/props");
109
139
 
110
140
  // src/arc/class/Functionable.js
111
141
  var Functionable = class extends Function {
@@ -137,10 +167,10 @@ var lockObject = (o) => {
137
167
  var info = lockObject(define_slib_info_default);
138
168
  var info_default = info;
139
169
 
140
- // src/arc/main.js
141
- var import_props2 = require("@randajan/props");
170
+ // src/arc/opt.js
171
+ var import_props3 = require("@randajan/props");
142
172
  var start = (opt) => {
143
- if (opt.timestamp) {
173
+ if (opt.timetrack) {
144
174
  return opt.startAt = Date.now();
145
175
  }
146
176
  };
@@ -159,7 +189,7 @@ var endTime = (body, startAt) => {
159
189
  time.netIn = body.time.startAt - startAt;
160
190
  time.netOut = endAt - body.time.endAt;
161
191
  }
162
- body.time = (0, import_props2.solids)({}, time);
192
+ body.time = (0, import_props3.solids)({}, time);
163
193
  };
164
194
  var end = (body, opt) => {
165
195
  const hasError = !!body.error;
@@ -172,29 +202,23 @@ var end = (body, opt) => {
172
202
  if (isFn(opt.onError)) {
173
203
  opt.onError(body, opt);
174
204
  }
175
- if (opt.throwError || opt.resultOnly) {
205
+ if (opt.throwError) {
176
206
  throw body.error;
177
207
  }
178
208
  }
179
- return opt.resultOnly ? body.result : Object.freeze(body);
209
+ return Object.freeze(body);
180
210
  };
181
- var diffVersion = (version) => {
182
- const v1 = toStr(info.version);
183
- const v2 = toStr(version);
184
- if (v1 === v2) {
185
- return;
186
- }
187
- const f1 = v1.split(".");
188
- const f2 = v2.split(".");
189
- if (f1[0] !== f2[0]) {
190
- return "major";
191
- }
192
- if (f1[1] !== f2[1]) {
193
- return "minor";
211
+ var createTrait = (trait) => {
212
+ if (!trait) {
213
+ return (opt) => opt;
194
214
  }
195
- if (f1[2] !== f2[2]) {
196
- return "patch";
215
+ if (isFn(trait)) {
216
+ return trait;
197
217
  }
218
+ throw new Error("config.trait should be a function");
219
+ };
220
+ var configTrait = (config) => {
221
+ config.trait = createTrait(config.trait);
198
222
  };
199
223
 
200
224
  // src/server/tool.js
@@ -221,15 +245,32 @@ var tryFn = (any) => {
221
245
  }
222
246
  return r.result.then(wrapResult).catch(wrapError);
223
247
  };
248
+ var normalizeServerError = (any, opt) => {
249
+ let err = any;
250
+ if (!HttpError.is(err)) {
251
+ let custom = isFn(opt.normalizeError);
252
+ if (custom) {
253
+ try {
254
+ err = opt.normalizeError(err, opt);
255
+ } catch {
256
+ custom = false;
257
+ }
258
+ }
259
+ if (!custom || !HttpError.is(err)) {
260
+ err = HttpError.code(0, "Unknown", 500, { cause: any });
261
+ }
262
+ }
263
+ return err.rise(1).rise(opt.code);
264
+ };
224
265
 
225
266
  // src/server/class/static.js
226
267
  var apiExit = (resp, opt) => {
227
- const { exposeStack } = opt;
268
+ const { exposeCause } = opt;
228
269
  resp.statusCode = 200;
229
270
  if (resp.error) {
230
- resp.error = ApiError.to(0, resp.error, 500).rise(1).rise(opt.code);
271
+ resp.error = normalizeServerError(resp.error, opt);
231
272
  resp.statusCode = resp.error.httpStatusCode;
232
- resp.error.exposeStack(!!exposeStack);
273
+ resp.error.exposeCause(!!exposeCause);
233
274
  }
234
275
  resp[info_default.name] = info_default.version;
235
276
  return end(resp, opt);
@@ -248,20 +289,6 @@ var apiResolve = (exe, opt) => {
248
289
  return resp.then((resp2) => apiExit(resp2, opt));
249
290
  };
250
291
 
251
- // src/arc/opt.js
252
- var createTrait = (trait) => {
253
- if (!trait) {
254
- return (opt) => opt;
255
- }
256
- if (isFn(trait)) {
257
- return trait;
258
- }
259
- throw new Error("config.trait should be a function");
260
- };
261
- var configTrait = (config) => {
262
- config.trait = createTrait(config.trait);
263
- };
264
-
265
292
  // src/server/class/Api.js
266
293
  var Api = class _Api extends Functionable {
267
294
  static create(config = {}) {
@@ -271,7 +298,7 @@ var Api = class _Api extends Functionable {
271
298
  configTrait(config);
272
299
  super((exe, opt) => apiResolve(exe, mrgObj(config, opt)));
273
300
  Object.freeze(config);
274
- (0, import_props3.solid)(this, "config", config);
301
+ (0, import_props4.solid)(this, "config", config);
275
302
  }
276
303
  code(code, exe, opt = {}) {
277
304
  opt.code = code;
@@ -287,25 +314,7 @@ var createApi = Api.create;
287
314
  var server_default = createApi;
288
315
 
289
316
  // src/client/class/static.js
290
- var import_props4 = require("@randajan/props");
291
-
292
- // src/arc/url.js
293
- var _regexp = /^https?:\/\//i;
294
- var buildUrl = (inputUrl, params) => {
295
- if (!params) {
296
- return inputUrl;
297
- }
298
- const hasHost = _regexp.test(inputUrl);
299
- const url = new URL(inputUrl, hasHost ? void 0 : "http://thisisonlyplaceholder.xyz");
300
- url.hash = "";
301
- for (const [key, value] of Object.entries(params)) {
302
- if (value != null) {
303
- url.searchParams.append(key, value);
304
- }
305
- }
306
- ;
307
- return hasHost ? url.toString() : url.pathname + url.search;
308
- };
317
+ var import_props5 = require("@randajan/props");
309
318
 
310
319
  // src/arc/types/kinds/json.js
311
320
  var json_exports = {};
@@ -330,175 +339,222 @@ var decode2 = (body) => Object.fromEntries(new URLSearchParams(body));
330
339
  var encode2 = (body) => new URLSearchParams(body);
331
340
 
332
341
  // src/arc/types/index.js
333
- var types = { json: json_exports, form: form_exports };
334
- var getType = (typeName) => {
335
- if (!typeName) {
342
+ var _types = { json: json_exports, form: form_exports };
343
+ var joinTypeNames = (types = {}) => {
344
+ const native = Object.keys(_types);
345
+ const custom = typeof types === "object" ? Object.keys(types) : [];
346
+ return `'${[.../* @__PURE__ */ new Set([...native, ...custom])].join("', '")}'`;
347
+ };
348
+ var getType = (propertyName, reqFnName, opt) => {
349
+ const tn = opt[propertyName] || opt.type;
350
+ if (!tn) {
336
351
  return json_exports;
337
352
  }
338
- const type = types[typeName];
339
- if (type) {
353
+ const customType = opt.types?.[tn];
354
+ const type = customType || _types[tn];
355
+ if (!type) {
356
+ throw new Error(`Unknown config.${propertyName} '${tn}'. Must be one of: ${joinTypeNames(opt.types)}`);
357
+ }
358
+ if (!customType) {
340
359
  return type;
341
360
  }
342
- throw new Error(`Unknow config.type '${typeName}'. Must be one of: '${[...Object.keys(types)].join("', '")}'`);
361
+ if (!type.mime || typeof type.mime !== "string") {
362
+ throw new Error(`Type definition '${tn}' requires 'mime' to be a string`);
363
+ }
364
+ if (!isFn(type[reqFnName])) {
365
+ throw new Error(`Type definition '${tn}' requires '${reqFnName}' to be a function`);
366
+ }
367
+ return type;
343
368
  };
344
369
  var encodeBody = (opt) => {
345
- const type = getType(opt.requestType || opt.type);
370
+ const type = getType("requestType", "encode", opt);
346
371
  opt.headers["Content-Type"] = type.mime;
347
372
  opt.body = type.encode(opt.body);
348
373
  };
349
374
  var attachBodyDecoder = (opt) => {
350
- const type = getType(opt.responseType || opt.type);
375
+ const type = getType("responseType", "decode", opt);
351
376
  opt.headers["Accept"] = type.mime;
352
377
  opt.decodeBody = type.decode;
353
378
  };
354
379
 
355
- // src/client/class/static.js
356
- var prepareOpt = (url, opt, method) => {
357
- start(opt);
358
- opt.url = buildUrl(mrgStr(opt.url, url), opt.query);
359
- if (method) {
360
- opt.method = method;
380
+ // src/client/url.js
381
+ var _regexp = /^https?:\/\//i;
382
+ var buildUrl = (inputUrl, params) => {
383
+ if (!params) {
384
+ return inputUrl;
361
385
  }
362
- if (opt.body) {
363
- encodeBody(opt);
386
+ const hasHost = _regexp.test(inputUrl);
387
+ const url = new URL(inputUrl, hasHost ? void 0 : "http://thisisonlyplaceholder.xyz");
388
+ url.hash = "";
389
+ for (const [key, value] of Object.entries(params)) {
390
+ if (value != null) {
391
+ url.searchParams.append(key, value);
392
+ }
364
393
  }
365
- attachBodyDecoder(opt);
366
- const hasTimeout = opt.timeout > 0;
367
- if (opt.abortable || hasTimeout) {
368
- opt.abortController = new AbortController();
369
- opt.signal = opt.abortController.signal;
394
+ ;
395
+ return hasHost ? url.toString() : url.pathname + url.search;
396
+ };
397
+
398
+ // src/client/tool.js
399
+ var diffVersion = (version) => {
400
+ const v1 = toStr(info.version);
401
+ const v2 = toStr(version);
402
+ if (v1 === v2) {
403
+ return;
370
404
  }
371
- if (hasTimeout) {
372
- opt.timeoutId = setTimeout(() => opt.abortController?.abort(ApiError.create(1, "Timeout", 408)), opt.timeout);
405
+ const f1 = v1.split(".");
406
+ const f2 = v2.split(".");
407
+ if (f1[0] !== f2[0]) {
408
+ return "major";
373
409
  }
374
- if (isFn(opt.trait)) {
375
- opt = opt.trait(opt) || opt;
410
+ if (f1[1] !== f2[1]) {
411
+ return "minor";
412
+ }
413
+ if (f1[2] !== f2[2]) {
414
+ return "patch";
376
415
  }
377
- return opt;
378
416
  };
379
- var localReject = (opt, error) => {
380
- const { httpStatusCode } = error;
381
- error.rise(0).rise(opt.code || 0);
382
- return end({ isOk: false, isRemote: false, isApiKit: false, statusCode: httpStatusCode, error }, opt);
417
+ var failedError = (cause) => FetchError.code(1, "Failed", { cause });
418
+ var timeoutError = (_) => FetchError.code(2, "Timeout");
419
+ var abortError = (_) => FetchError.code(3, "Aborted");
420
+ var statusError = (resp) => HttpError.code(4, resp.statusText, resp.status, { exposeCause: true });
421
+ var unreadableError = (resp, cause) => resp.ok ? FetchError.code(5, "Unreadable", { cause }) : statusError(resp);
422
+ var undecodableError = (resp, cause, detail) => resp.ok ? FetchError.code(6, "Undecodable", { cause, detail }) : statusError(resp);
423
+ var parseHeaders = (resp, opt) => {
424
+ if (!resp || !opt.parseHeaders) {
425
+ return;
426
+ }
427
+ return Object.freeze(Object.fromEntries(resp.headers.entries()));
383
428
  };
384
- var statusError = ({ status, statusText }) => ApiError.create(4, statusText || status, status);
385
- var unreadableError = (resp) => resp.ok ? ApiError.create(3, "Unreadable", 415) : statusError(resp);
386
- var foreignError = (resp) => ({ isApiKit: false, error: statusError(resp) });
387
429
  var readText = async (resp) => {
388
430
  try {
389
431
  return await resp.text();
390
- } catch {
391
- throw unreadableError(resp);
432
+ } catch (err) {
433
+ throw unreadableError(resp, err);
392
434
  }
393
435
  };
394
- var decodeText = (text, opt, resp) => {
436
+ var decodeText = (resp, opt, text) => {
395
437
  try {
396
438
  return opt.decodeBody(text);
397
- } catch {
398
- throw unreadableError(resp);
439
+ } catch (err) {
440
+ throw undecodableError(resp, err, text);
441
+ }
442
+ };
443
+ var normalizeClientError = (any, opt) => {
444
+ let err = any;
445
+ if (!FetchError.is(err)) {
446
+ let custom = isFn(opt.normalizeError);
447
+ if (custom) {
448
+ try {
449
+ err = opt.normalizeError(err, opt);
450
+ } catch {
451
+ custom = false;
452
+ }
453
+ }
454
+ if (!custom || !FetchError.is(err)) {
455
+ err = FetchError.code(0, "Unknown", { cause: any });
456
+ }
399
457
  }
458
+ return err.rise(0).rise(0);
400
459
  };
401
- var resolveEmpty = (resp) => {
402
- if (!resp.ok && resp.status !== 304) {
403
- return foreignError(resp);
460
+ var reconstructApiError = (remoteErr) => {
461
+ if (!remoteErr) {
462
+ return;
404
463
  }
405
- return { result: null, isApiKit: false };
464
+ const { message, code, httpStatusCode, cause, detail } = remoteErr;
465
+ return HttpError.code(code, message, httpStatusCode, { cause, detail, exposeCause: true });
466
+ };
467
+ var mrgOpt = (a, b) => {
468
+ const opt = mrgObj(a, b);
469
+ opt.body = mrgObj(a?.body, b?.body);
470
+ opt.params = mrgObj(a?.params, b?.params);
471
+ opt.headers = mrgObj(a?.headers, b?.headers, {});
472
+ return opt;
406
473
  };
407
- var resolveApiKit = (raw, opt) => {
408
- const apv = raw[info_default.name];
409
- const diff = diffVersion(apv);
410
- const msg = `Detected @randajan/api-kit ${diff} version difference at '${opt.url}'. Server '${apv}' vs. client '${info_default.version}'`;
411
- if (diff === "major") {
412
- console.error(msg);
474
+ var prepareOpt = (url, opt, method) => {
475
+ start(opt);
476
+ opt.url = buildUrl(mrgStr(opt.url, url), opt.params);
477
+ if (method) {
478
+ opt.method = method;
413
479
  }
414
- if (diff === "minor") {
415
- console.warn(msg);
480
+ if (opt.body) {
481
+ encodeBody(opt);
416
482
  }
417
- raw.isApiKit = true;
418
- return raw;
419
- };
420
- var resolveForeign = (raw, opt, resp) => {
421
- if (!resp.ok) {
422
- return foreignError(resp);
483
+ attachBodyDecoder(opt);
484
+ const hasTimeout = opt.timeout > 0;
485
+ if (opt.isAbortable || hasTimeout) {
486
+ opt.abortController = new AbortController();
487
+ opt.signal = opt.abortController.signal;
423
488
  }
424
- const body = { isApiKit: false };
425
- try {
426
- body.result = opt.parseBody(raw);
427
- } catch (err) {
428
- body.error = err.message || err;
489
+ if (hasTimeout) {
490
+ opt.timeoutId = setTimeout(() => opt.abortController?.abort(timeoutError()), opt.timeout);
429
491
  }
430
- return body;
431
- };
432
- var resolveDecoded = (raw, opt, resp) => {
433
- if (raw?.[info_default.name]) {
434
- return resolveApiKit(raw, opt);
492
+ if (isFn(opt.trait)) {
493
+ opt = opt.trait(opt) || opt;
435
494
  }
436
- return resolveForeign(raw, opt, resp);
495
+ return opt;
437
496
  };
438
- var normalizeError = (body, opt, resp) => {
439
- if (!body.error || ApiError.is(body.error)) {
440
- return;
497
+
498
+ // src/client/class/static.js
499
+ var localReject = (err, opt) => ({ error: normalizeClientError(FetchError.is(err) ? err : failedError(err), opt) });
500
+ var remoteReject = (err, opt) => ({ isRemote: true, error: normalizeClientError(err, opt) });
501
+ var remoteResolve = async (resp, opt) => {
502
+ const text = await readText(resp);
503
+ if (!text) {
504
+ if (resp.ok || resp.status === 304) {
505
+ return { isRemote: true };
506
+ }
507
+ throw statusError(resp);
441
508
  }
442
- const { code, message, stack, httpStatusCode } = body.error;
443
- body.error = ApiError.create(
444
- code || mrgStr(opt.code || 0, "1.0", "."),
445
- message || body.error,
446
- httpStatusCode ?? body.statusCode ?? resp.status,
447
- stack
448
- );
449
- };
450
- var remoteResolve = (body, opt, resp) => {
451
- normalizeError(body, opt, resp);
452
- body.isOk = !body.error;
453
- body.isRemote = true;
454
- body.isApiKit = !!body.isApiKit;
455
- body.statusCode = body.statusCode ?? body.error?.httpStatusCode ?? resp.status;
456
- if (opt.parseHeaders) {
457
- body.headers = Object.freeze(Object.fromEntries(resp.headers.entries()));
509
+ const body = decodeText(resp, opt, text);
510
+ const ver = body?.[info_default.name];
511
+ if (!ver) {
512
+ if (resp.ok) {
513
+ return { isRemote: true, result: body };
514
+ }
515
+ throw statusError(resp);
458
516
  }
459
- return end(body, opt);
517
+ body.isRemote = true;
518
+ body.isApiKit = true;
519
+ body.apiKitDiff = diffVersion(ver);
520
+ body.error = reconstructApiError(body.error);
521
+ return body;
460
522
  };
461
- var remoteReject = (opt, resp, error) => remoteResolve({ isApiKit: false, error }, opt, resp);
462
- var fetchExe = async (_fetch, opt) => {
523
+ var fetchBody = async (_fetch, opt) => {
463
524
  let resp, body;
464
525
  try {
465
526
  resp = await _fetch(opt.url, opt);
466
- } catch (err) {
467
- return localReject(opt, ApiError.is(err) ? err : ApiError.to(0, "Failed", 503));
527
+ } catch (err2) {
528
+ body = localReject(err2, opt);
468
529
  }
469
530
  try {
470
- const text = await readText(resp);
471
- body = text ? resolveDecoded(decodeText(text, opt, resp), opt, resp) : resolveEmpty(resp);
472
- } catch (err) {
473
- return remoteReject(opt, resp, ApiError.is(err) ? err : ApiError.to(0, err, 500));
531
+ body = body || await remoteResolve(resp, opt);
532
+ } catch (err2) {
533
+ body = remoteReject(err2, opt);
474
534
  }
475
- return remoteResolve(body, opt, resp);
476
- };
477
- var fetchResolve = (_fetch, url, opt, method) => {
478
- const prom = fetchExe(_fetch, prepareOpt(url, opt, method));
535
+ const err = body.error;
536
+ body.isOk = !err;
537
+ body.isRemote = !!body.isRemote;
538
+ body.isApiKit = !!body.isApiKit;
539
+ body.statusCode = err?.httpStatusCode || resp?.status;
540
+ body.headers = parseHeaders(resp, opt);
541
+ body = end(body, opt);
542
+ return opt.resultOnly ? body.result : body;
543
+ };
544
+ var executeFetch = (_fetch, url, opt, method) => {
545
+ opt = prepareOpt(url, opt, method);
546
+ const prom = fetchBody(_fetch, opt);
479
547
  if (opt.timeoutId) {
480
548
  prom.finally((_) => clearTimeout(opt.timeoutId));
481
549
  }
482
- if (!opt.abortable) {
550
+ if (!opt.isAbortable) {
483
551
  return prom;
484
552
  }
485
- return (0, import_props4.solid)(prom, "abort", (_) => opt.abortController?.abort(ApiError.create(2, "Aborted", 499)));
486
- };
487
-
488
- // src/client/class/Fetch.js
489
- var import_props5 = require("@randajan/props");
490
-
491
- // src/client/tool.js
492
- var mrgQuery = (opt) => mrgObj(opt?.params, opt?.query);
493
- var mrgOpt = (a, b) => {
494
- const opt = mrgObj(a, b);
495
- opt.body = mrgObj(a?.body, b?.body);
496
- opt.query = mrgObj(mrgQuery(a), mrgQuery(b));
497
- opt.headers = mrgObj(a?.headers, b?.headers, {});
498
- return opt;
553
+ return (0, import_props5.solid)(prom, "abort", (_) => opt.abortController?.abort(abortError()));
499
554
  };
500
555
 
501
556
  // src/client/class/Fetch.js
557
+ var import_props6 = require("@randajan/props");
502
558
  var Fetch = class _Fetch extends Functionable {
503
559
  static create(config = {}) {
504
560
  return new _Fetch(config);
@@ -506,14 +562,13 @@ var Fetch = class _Fetch extends Functionable {
506
562
  constructor(config = {}) {
507
563
  const _fetch = config.fetch || globalThis.fetch;
508
564
  if (!_fetch) {
509
- throw new Error("Missing fetch function. Please provide it inconfig");
565
+ throw new Error("Missing fetch function. Please provide it in config");
510
566
  }
511
567
  configTrait(config);
512
- config.parseBody = createTrait(config.parseBody);
513
568
  delete config.fetch;
514
- super((url, opt, method) => fetchResolve(_fetch, url, mrgOpt(config, opt), method));
569
+ super((url, opt, method) => executeFetch(_fetch, url, mrgOpt(config, opt), method));
515
570
  Object.freeze(config);
516
- (0, import_props5.solid)(this, "config", config);
571
+ (0, import_props6.solid)(this, "config", config);
517
572
  }
518
573
  extend(config) {
519
574
  return new _Fetch(mrgOpt(this.config, config));