@fjell/client-api 4.4.43 → 4.4.45

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -17,12 +17,12 @@ var getAllOperation = (api, apiOptions, utilities) => {
17
17
  const params = queryToParams(query);
18
18
  const requestOptions = Object.assign({}, apiOptions.getOptions, { isAuthenticated: apiOptions.allAuthenticated, params });
19
19
  logger.default("all", { query, locations, requestOptions });
20
- return utilities.validatePK(await utilities.processArray(
20
+ return await utilities.processArray(
21
21
  api.httpGet(
22
22
  utilities.getPath(loc),
23
23
  requestOptions
24
24
  )
25
- ));
25
+ );
26
26
  };
27
27
  return all;
28
28
  };
@@ -30,17 +30,17 @@ var getAllOperation = (api, apiOptions, utilities) => {
30
30
  // src/ops/action.ts
31
31
  var logger2 = logger_default.get("client-api", "ops", "action");
32
32
  var getActionOperation = (api, apiOptions, utilities) => {
33
- const action = async (ik, action2, body = {}) => {
33
+ const action = async (ik, action2, params) => {
34
34
  const requestOptions = Object.assign({}, apiOptions.postOptions, { isAuthenticated: apiOptions.writeAuthenticated });
35
- logger2.default("action", { ik, action: action2, body, requestOptions });
35
+ logger2.default("action", { ik, action: action2, params, requestOptions });
36
36
  const response = await api.httpPost(
37
37
  `${utilities.getPath(ik)}/${action2}`,
38
- body,
38
+ params || {},
39
39
  requestOptions
40
40
  );
41
41
  const [item, affectedItems] = response;
42
42
  return [
43
- utilities.validatePK(await utilities.processOne(Promise.resolve(item))),
43
+ await utilities.processOne(Promise.resolve(item)),
44
44
  affectedItems
45
45
  ];
46
46
  };
@@ -50,24 +50,23 @@ var getActionOperation = (api, apiOptions, utilities) => {
50
50
  // src/ops/allAction.ts
51
51
  var logger3 = logger_default.get("client-api", "ops", "allAction");
52
52
  var getAllActionOperation = (api, apiOptions, utilities) => {
53
- const allAction = async (action, body = {}, locations = []) => {
53
+ const allAction = async (action, params, locations) => {
54
54
  const requestOptions = Object.assign({}, apiOptions.postOptions, { isAuthenticated: apiOptions.writeAuthenticated });
55
- logger3.default("allAction", { action, body, locations, requestOptions });
56
- utilities.verifyLocations(locations);
57
- const loc = locations;
55
+ const loc = locations || [];
56
+ logger3.default("allAction", { action, params, locations: loc, requestOptions });
57
+ utilities.verifyLocations(loc);
58
58
  const response = await api.httpPost(
59
59
  `${utilities.getPath(loc)}/${action}`,
60
- body,
60
+ params || {},
61
61
  requestOptions
62
62
  );
63
63
  let items = [];
64
64
  let affectedItems = [];
65
65
  if (Array.isArray(response)) {
66
- if (response[0] !== void 0 && response[1] !== void 0) {
66
+ if (response.length === 2 && Array.isArray(response[0])) {
67
67
  [items, affectedItems] = response;
68
- } else if (response[0] !== void 0) {
69
- items = response[0];
70
- affectedItems = [];
68
+ } else {
69
+ return response;
71
70
  }
72
71
  } else if (response && typeof response === "object" && Object.keys(response).length === 0) {
73
72
  items = [];
@@ -76,10 +75,12 @@ var getAllActionOperation = (api, apiOptions, utilities) => {
76
75
  items = [];
77
76
  affectedItems = [];
78
77
  }
78
+ const processedItems = await utilities.processArray(Promise.resolve(items));
79
+ if (Array.isArray(processedItems)) {
80
+ processedItems.forEach((item) => utilities.validatePK(item));
81
+ }
79
82
  return [
80
- utilities.validatePK(
81
- await utilities.processArray(Promise.resolve(items))
82
- ),
83
+ processedItems,
83
84
  affectedItems
84
85
  ];
85
86
  };
@@ -99,12 +100,12 @@ var getOneOperation = (api, apiOptions, utilities) => {
99
100
  const requestOptions = Object.assign({}, apiOptions.getOptions, { isAuthenticated: apiOptions.readAuthenticated, params });
100
101
  logger4.default("one", { query, locations, requestOptions });
101
102
  let item = null;
102
- const items = utilities.validatePK(await utilities.processArray(
103
+ const items = await utilities.processArray(
103
104
  api.httpGet(
104
105
  utilities.getPath(loc),
105
106
  requestOptions
106
107
  )
107
- ));
108
+ );
108
109
  if (items.length > 0) {
109
110
  item = items[0];
110
111
  }
@@ -114,7 +115,11 @@ var getOneOperation = (api, apiOptions, utilities) => {
114
115
  };
115
116
 
116
117
  // src/ops/errorHandling.ts
118
+ import { isFjellHttpError } from "@fjell/http-api";
117
119
  function shouldRetryError(error) {
120
+ if (isFjellHttpError(error)) {
121
+ return error.isRetryable();
122
+ }
118
123
  if (error.code === "ECONNREFUSED" || error.code === "ENOTFOUND" || error.code === "ENETUNREACH" || error.message?.includes("timeout") || error.message?.includes("network")) {
119
124
  return true;
120
125
  }
@@ -134,6 +139,9 @@ function calculateRetryDelay(attempt, config) {
134
139
  }
135
140
  function enhanceError(error, context) {
136
141
  if (!error) return new Error("Unknown error occurred");
142
+ if (isFjellHttpError(error)) {
143
+ return error;
144
+ }
137
145
  if (error.context) return error;
138
146
  const enhancedError = new Error(error.message || "HTTP operation failed");
139
147
  Object.assign(enhancedError, {
@@ -153,27 +161,27 @@ function getRetryConfig(apiOptions) {
153
161
  ...apiOptions.retryConfig
154
162
  };
155
163
  }
156
- function executeErrorHandler(errorHandler, error, context, logger21) {
164
+ function executeErrorHandler(errorHandler, error, context, logger22) {
157
165
  if (!errorHandler) return;
158
166
  try {
159
167
  errorHandler(error, context);
160
168
  } catch (handlerError) {
161
- logger21.error("Custom error handler failed", {
169
+ logger22.error("Custom error handler failed", {
162
170
  originalError: error.message,
163
171
  handlerError: handlerError?.message || String(handlerError)
164
172
  });
165
173
  }
166
174
  }
167
- async function executeWithRetry(operation, operationName, operationContext, apiOptions, logger21, specialErrorHandling) {
175
+ async function executeWithRetry(operation, operationName, operationContext, apiOptions, logger22, specialErrorHandling) {
168
176
  const retryConfig = getRetryConfig(apiOptions);
169
177
  let lastError = null;
170
178
  const startTime = Date.now();
171
179
  for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) {
172
180
  try {
173
- logger21.debug(`Executing ${operationName} (attempt ${attempt + 1})`, operationContext);
181
+ logger22.debug(`Executing ${operationName} (attempt ${attempt + 1})`, operationContext);
174
182
  const result = await operation();
175
183
  if (attempt > 0) {
176
- logger21.info(`${operationName} operation succeeded after ${attempt} retries`, {
184
+ logger22.info(`${operationName} operation succeeded after ${attempt} retries`, {
177
185
  ...operationContext,
178
186
  totalAttempts: attempt + 1,
179
187
  duration: Date.now() - startTime
@@ -193,7 +201,7 @@ async function executeWithRetry(operation, operationName, operationContext, apiO
193
201
  }
194
202
  const isRetryable = shouldRetryError(error);
195
203
  if (!isRetryable) {
196
- logger21.debug(`Not retrying ${operationName} operation due to non-retryable error`, {
204
+ logger22.debug(`Not retrying ${operationName} operation due to non-retryable error`, {
197
205
  ...operationContext,
198
206
  errorMessage: error.message,
199
207
  errorCode: error.code || error.status,
@@ -202,7 +210,7 @@ async function executeWithRetry(operation, operationName, operationContext, apiO
202
210
  break;
203
211
  }
204
212
  const delay = calculateRetryDelay(attempt, retryConfig);
205
- logger21.warning(`Retrying ${operationName} operation (attempt ${attempt + 2}) after ${delay}ms`, {
213
+ logger22.warning(`Retrying ${operationName} operation (attempt ${attempt + 2}) after ${delay}ms`, {
206
214
  ...operationContext,
207
215
  errorMessage: error.message,
208
216
  errorCode: error.code || error.status,
@@ -213,8 +221,8 @@ async function executeWithRetry(operation, operationName, operationContext, apiO
213
221
  }
214
222
  }
215
223
  const finalError = enhanceError(lastError, operationContext);
216
- executeErrorHandler(apiOptions.errorHandler, finalError, operationContext, logger21);
217
- logger21.error(`${operationName} operation failed after ${retryConfig.maxRetries + 1} attempts`, {
224
+ executeErrorHandler(apiOptions.errorHandler, finalError, operationContext, logger22);
225
+ logger22.error(`${operationName} operation failed after ${retryConfig.maxRetries + 1} attempts`, {
218
226
  ...operationContext,
219
227
  errorMessage: finalError.message,
220
228
  errorCode: finalError.code || finalError.status,
@@ -227,10 +235,15 @@ async function executeWithRetry(operation, operationName, operationContext, apiO
227
235
  // src/ops/create.ts
228
236
  var logger5 = logger_default.get("client-api", "ops", "create");
229
237
  var getCreateOperation = (api, apiOptions, utilities) => {
230
- const create = async (item, locations = []) => {
238
+ const create = async (item, options) => {
239
+ const locations = options?.locations || [];
231
240
  const requestOptions = Object.assign({}, apiOptions.postOptions, { isAuthenticated: apiOptions.writeAuthenticated });
232
- logger5.default("create", { item, locations, requestOptions });
241
+ logger5.default("create", { item, options, locations, requestOptions });
233
242
  utilities.verifyLocations(locations);
243
+ let itemToCreate = item;
244
+ if (options?.key) {
245
+ itemToCreate = { ...item, ...options.key };
246
+ }
234
247
  const loc = locations;
235
248
  const operationContext = {
236
249
  operation: "create",
@@ -252,10 +265,10 @@ var getCreateOperation = (api, apiOptions, utilities) => {
252
265
  logger5.debug(`Creating item (attempt ${attempt + 1})`, operationContext);
253
266
  const result = await utilities.processOne(api.httpPost(
254
267
  utilities.getPath(loc),
255
- item,
268
+ itemToCreate,
256
269
  requestOptions
257
270
  ));
258
- const created = utilities.validatePK(result);
271
+ utilities.validatePK(result);
259
272
  if (attempt > 0) {
260
273
  logger5.info(`Create operation succeeded after ${attempt} retries`, {
261
274
  ...operationContext,
@@ -263,7 +276,7 @@ var getCreateOperation = (api, apiOptions, utilities) => {
263
276
  duration: Date.now() - startTime
264
277
  });
265
278
  }
266
- return created;
279
+ return result;
267
280
  } catch (error) {
268
281
  lastError = error;
269
282
  if (attempt === retryConfig.maxRetries) {
@@ -319,23 +332,42 @@ var getUpdateOperation = (api, apiOptions, utilities) => {
319
332
  const update = async (ik, item) => {
320
333
  const requestOptions = Object.assign({}, apiOptions.putOptions, { isAuthenticated: apiOptions.writeAuthenticated });
321
334
  logger6.default("update", { ik, item, requestOptions });
322
- return utilities.validatePK(await utilities.processOne(
335
+ return await utilities.processOne(
323
336
  api.httpPut(
324
337
  utilities.getPath(ik),
325
338
  item,
326
339
  requestOptions
327
340
  )
328
- ));
341
+ );
329
342
  };
330
343
  return update;
331
344
  };
332
345
 
346
+ // src/ops/upsert.ts
347
+ var logger7 = logger_default.get("client-api", "ops", "upsert");
348
+ var getUpsertOperation = (api, apiOptions, utilities) => {
349
+ const upsert = async (key, item, locations) => {
350
+ const requestOptions = Object.assign({}, apiOptions.putOptions, { isAuthenticated: apiOptions.writeAuthenticated });
351
+ logger7.default("upsert", { key, item, locations, requestOptions });
352
+ const path = utilities.getPath(key);
353
+ const url = locations && locations.length > 0 ? `${path}?locations=${encodeURIComponent(JSON.stringify(locations))}` : path;
354
+ return await utilities.processOne(
355
+ api.httpPut(
356
+ url,
357
+ { ...item, upsert: true },
358
+ requestOptions
359
+ )
360
+ );
361
+ };
362
+ return upsert;
363
+ };
364
+
333
365
  // src/ops/get.ts
334
- var logger7 = logger_default.get("client-api", "ops", "get");
366
+ var logger8 = logger_default.get("client-api", "ops", "get");
335
367
  var getGetOperation = (api, apiOptions, utilities) => {
336
368
  const get = async (ik) => {
337
369
  const requestOptions = Object.assign({}, apiOptions.getOptions, { isAuthenticated: apiOptions.readAuthenticated });
338
- logger7.default("get", { ik, requestOptions });
370
+ logger8.default("get", { ik, requestOptions });
339
371
  const operationContext = {
340
372
  operation: "get",
341
373
  path: utilities.getPath(ik),
@@ -352,26 +384,26 @@ var getGetOperation = (api, apiOptions, utilities) => {
352
384
  const startTime = Date.now();
353
385
  for (let attempt = 0; attempt <= retryConfig.maxRetries; attempt++) {
354
386
  try {
355
- logger7.debug(`Getting item (attempt ${attempt + 1})`, operationContext);
387
+ logger8.debug(`Getting item (attempt ${attempt + 1})`, operationContext);
356
388
  const result = await utilities.processOne(
357
389
  api.httpGet(
358
390
  utilities.getPath(ik),
359
391
  requestOptions
360
392
  )
361
393
  );
362
- const item = utilities.validatePK(result);
394
+ utilities.validatePK(result);
363
395
  if (attempt > 0) {
364
- logger7.info(`Get operation succeeded after ${attempt} retries`, {
396
+ logger8.info(`Get operation succeeded after ${attempt} retries`, {
365
397
  ...operationContext,
366
398
  totalAttempts: attempt + 1,
367
399
  duration: Date.now() - startTime
368
400
  });
369
401
  }
370
- return item;
402
+ return result;
371
403
  } catch (error) {
372
404
  lastError = error;
373
405
  if (error.status === 404) {
374
- logger7.debug("Item not found (404)", operationContext);
406
+ logger8.debug("Item not found (404)", operationContext);
375
407
  return null;
376
408
  }
377
409
  if (attempt === retryConfig.maxRetries) {
@@ -379,7 +411,7 @@ var getGetOperation = (api, apiOptions, utilities) => {
379
411
  }
380
412
  const isRetryable = shouldRetryError(error);
381
413
  if (!isRetryable) {
382
- logger7.debug("Not retrying get operation due to non-retryable error", {
414
+ logger8.debug("Not retrying get operation due to non-retryable error", {
383
415
  ...operationContext,
384
416
  errorMessage: error.message,
385
417
  errorCode: error.code || error.status,
@@ -388,7 +420,7 @@ var getGetOperation = (api, apiOptions, utilities) => {
388
420
  break;
389
421
  }
390
422
  const delay = calculateRetryDelay(attempt, retryConfig);
391
- logger7.warning(`Retrying get operation (attempt ${attempt + 2}) after ${delay}ms`, {
423
+ logger8.warning(`Retrying get operation (attempt ${attempt + 2}) after ${delay}ms`, {
392
424
  ...operationContext,
393
425
  errorMessage: error.message,
394
426
  errorCode: error.code || error.status,
@@ -403,13 +435,13 @@ var getGetOperation = (api, apiOptions, utilities) => {
403
435
  try {
404
436
  apiOptions.errorHandler(finalError, operationContext);
405
437
  } catch (handlerError) {
406
- logger7.error("Custom error handler failed", {
438
+ logger8.error("Custom error handler failed", {
407
439
  originalError: finalError.message,
408
440
  handlerError: handlerError?.message || String(handlerError)
409
441
  });
410
442
  }
411
443
  }
412
- logger7.error(`Get operation failed after ${retryConfig.maxRetries + 1} attempts`, {
444
+ logger8.error(`Get operation failed after ${retryConfig.maxRetries + 1} attempts`, {
413
445
  ...operationContext,
414
446
  errorMessage: finalError.message,
415
447
  errorCode: finalError.code || finalError.status,
@@ -422,37 +454,41 @@ var getGetOperation = (api, apiOptions, utilities) => {
422
454
  };
423
455
 
424
456
  // src/ops/remove.ts
425
- var logger8 = logger_default.get("client-api", "ops", "remove");
457
+ var logger9 = logger_default.get("client-api", "ops", "remove");
426
458
  var getRemoveOperation = (api, apiOptions, utilities) => {
427
459
  const remove = async (ik) => {
428
460
  const requestOptions = Object.assign({}, apiOptions.deleteOptions, { isAuthenticated: apiOptions.writeAuthenticated });
429
- logger8.default("remove", { ik, requestOptions });
430
- return api.httpDelete(utilities.getPath(ik), requestOptions);
461
+ logger9.default("remove", { ik, requestOptions });
462
+ const result = await api.httpDelete(utilities.getPath(ik), requestOptions);
463
+ if (typeof result === "boolean") {
464
+ return;
465
+ }
466
+ return result;
431
467
  };
432
468
  return remove;
433
469
  };
434
470
 
435
471
  // src/ops/find.ts
436
- var logger9 = logger_default.get("client-api", "ops", "find");
472
+ var logger10 = logger_default.get("client-api", "ops", "find");
437
473
  var getFindOperation = (api, apiOptions, utilities) => {
438
474
  const find = async (finder, finderParams = {}, locations = []) => {
439
475
  utilities.verifyLocations(locations);
440
476
  const loc = locations;
441
477
  const mergedParams = finderToParams(finder, finderParams);
442
478
  const requestOptions = Object.assign({}, apiOptions.getOptions, { isAuthenticated: apiOptions.allAuthenticated, params: mergedParams });
443
- logger9.default("find", { finder, finderParams, locations, requestOptions });
444
- return utilities.validatePK(await utilities.processArray(
479
+ logger10.default("find", { finder, finderParams, locations, requestOptions });
480
+ return await utilities.processArray(
445
481
  api.httpGet(
446
482
  utilities.getPath(loc),
447
483
  requestOptions
448
484
  )
449
- ));
485
+ );
450
486
  };
451
487
  return find;
452
488
  };
453
489
 
454
490
  // src/ops/findOne.ts
455
- var logger10 = logger_default.get("client-api", "ops", "find");
491
+ var logger11 = logger_default.get("client-api", "ops", "find");
456
492
  var getFindOneOperation = (api, apiOptions, utilities) => {
457
493
  const findOne = async (finder, finderParams = {}, locations = []) => {
458
494
  utilities.verifyLocations(locations);
@@ -460,23 +496,28 @@ var getFindOneOperation = (api, apiOptions, utilities) => {
460
496
  const params = finderToParams(finder, finderParams);
461
497
  params.one = true;
462
498
  const requestOptions = Object.assign({}, apiOptions.getOptions, { isAuthenticated: apiOptions.allAuthenticated, params });
463
- logger10.default("findOne", { finder, finderParams, locations, requestOptions });
464
- return utilities.validatePK(await utilities.processArray(
499
+ logger11.default("findOne", { finder, finderParams, locations, requestOptions });
500
+ const results = await utilities.processArray(
465
501
  api.httpGet(
466
502
  utilities.getPath(loc),
467
503
  requestOptions
468
504
  )
469
- ))[0];
505
+ );
506
+ const result = results[0];
507
+ if (result) {
508
+ utilities.validatePK(result);
509
+ }
510
+ return result;
470
511
  };
471
512
  return findOne;
472
513
  };
473
514
 
474
515
  // src/ops/facet.ts
475
- var logger11 = logger_default.get("client-api", "ops", "facet");
516
+ var logger12 = logger_default.get("client-api", "ops", "facet");
476
517
  var getFacetOperation = (api, apiOptions, utilities) => {
477
518
  const facet = async (ik, facet2, params = {}) => {
478
519
  const requestOptions = Object.assign({}, apiOptions.getOptions, { isAuthenticated: apiOptions.writeAuthenticated, params });
479
- logger11.default("facet", { ik, facet: facet2, requestOptions });
520
+ logger12.default("facet", { ik, facet: facet2, requestOptions });
480
521
  return api.httpGet(
481
522
  `${utilities.getPath(ik)}/${facet2}`,
482
523
  requestOptions
@@ -486,11 +527,11 @@ var getFacetOperation = (api, apiOptions, utilities) => {
486
527
  };
487
528
 
488
529
  // src/ops/allFacet.ts
489
- var logger12 = logger_default.get("client-api", "ops", "allFacet");
530
+ var logger13 = logger_default.get("client-api", "ops", "allFacet");
490
531
  var getAllFacetOperation = (api, apiOptions, utilities) => {
491
532
  const allFacet = async (facet, params = {}, locations = []) => {
492
533
  const requestOptions = Object.assign({}, apiOptions.getOptions, { isAuthenticated: apiOptions.writeAuthenticated, params });
493
- logger12.default("allFacet", { facet, locations, requestOptions });
534
+ logger13.default("allFacet", { facet, locations, requestOptions });
494
535
  utilities.verifyLocations(locations);
495
536
  const loc = locations;
496
537
  return api.httpGet(
@@ -563,6 +604,11 @@ var getOperations = (api, apiOptions, utilities) => {
563
604
  api,
564
605
  apiOptions,
565
606
  utilities
607
+ ),
608
+ upsert: getUpsertOperation(
609
+ api,
610
+ apiOptions,
611
+ utilities
566
612
  )
567
613
  };
568
614
  };
@@ -574,9 +620,9 @@ import {
574
620
  isPriKey
575
621
  } from "@fjell/core";
576
622
  import deepmerge from "deepmerge";
577
- var logger13 = logger_default.get("client-api", "Utility");
623
+ var logger14 = logger_default.get("client-api", "Utility");
578
624
  var createUtilities = (pkType, pathNames) => {
579
- logger13.default("createUtilities", { pkType, pathNames });
625
+ logger14.default("createUtilities", { pkType, pathNames });
580
626
  const verifyLocations = (locations) => {
581
627
  if (locations && locations.length < pathNames.length - 1) {
582
628
  throw new Error("Not enough locations for pathNames: locations:" + locations.length + " pathNames:" + pathNames.length);
@@ -584,18 +630,18 @@ var createUtilities = (pkType, pathNames) => {
584
630
  return true;
585
631
  };
586
632
  const processOne = async (apiCall) => {
587
- logger13.default("processOne", { apiCall });
633
+ logger14.default("processOne", { apiCall });
588
634
  const response = await apiCall;
589
- logger13.default("processOne response", {
635
+ logger14.default("processOne response", {
590
636
  responseType: typeof response,
591
637
  hasData: !!response
592
638
  });
593
639
  return convertDoc(response);
594
640
  };
595
641
  const processArray = async (api) => {
596
- logger13.default("processArray", { api });
642
+ logger14.default("processArray", { api });
597
643
  const response = await api;
598
- logger13.default("processArray response", {
644
+ logger14.default("processArray response", {
599
645
  responseType: typeof response,
600
646
  isArray: Array.isArray(response),
601
647
  length: Array.isArray(response) ? response.length : 0
@@ -605,12 +651,12 @@ var createUtilities = (pkType, pathNames) => {
605
651
  (subjectChat) => convertDoc(subjectChat)
606
652
  );
607
653
  } else {
608
- logger13.error("Response was not an array", { response });
654
+ logger14.error("Response was not an array", { response });
609
655
  throw new Error("Response was not an array");
610
656
  }
611
657
  };
612
658
  const convertDoc = (doc) => {
613
- logger13.default("convertDoc", { doc });
659
+ logger14.default("convertDoc", { doc });
614
660
  if (doc && doc.events) {
615
661
  const events = doc.events;
616
662
  for (const key in events) {
@@ -621,101 +667,58 @@ var createUtilities = (pkType, pathNames) => {
621
667
  return doc;
622
668
  }
623
669
  };
624
- const validateLocationKeyOrder = (keys) => {
625
- const locKeys = keys.filter((k) => !isPriKey(k));
626
- if (locKeys.length <= 1) {
627
- return;
628
- }
629
- const pathNameOrder = /* @__PURE__ */ new Map();
630
- pathNames.forEach((pathName, index) => {
631
- const pathParts = pathName.split("/");
632
- const lastPart = pathParts[pathParts.length - 1];
633
- pathNameOrder.set(pathName, index);
634
- pathNameOrder.set(pathName.toLowerCase(), index);
635
- pathNameOrder.set(lastPart, index);
636
- pathNameOrder.set(lastPart.toLowerCase(), index);
637
- const singular = lastPart.endsWith("s") ? lastPart.slice(0, -1) : lastPart;
638
- const plural = lastPart + "s";
639
- const pluralEs = lastPart + "es";
640
- pathNameOrder.set(singular, index);
641
- pathNameOrder.set(plural, index);
642
- pathNameOrder.set(pluralEs, index);
643
- pathNameOrder.set(singular.toLowerCase(), index);
644
- pathNameOrder.set(plural.toLowerCase(), index);
645
- });
646
- let lastIndex = -1;
647
- const keyDetails = [];
648
- for (const locKey of locKeys) {
649
- const keyType = locKey.kt;
650
- const currentIndex = pathNameOrder.get(keyType);
651
- keyDetails.push({ kt: keyType, pathNameIndex: currentIndex });
652
- if (typeof currentIndex !== "undefined") {
653
- if (currentIndex <= lastIndex) {
654
- logger13.error("Location keys are not in the correct hierarchical order", {
655
- keys: locKeys.map((k) => ({ kt: k.kt, lk: k.lk })),
656
- pathNames,
657
- keyDetails,
658
- issue: `Key type "${keyType}" (index ${currentIndex}) should come before the previous key (index ${lastIndex})`
659
- });
660
- throw new Error(
661
- `Location keys must be ordered from parent to child according to the entity hierarchy. Expected order based on pathNames: [${pathNames.join(", ")}]. Received key types in order: [${locKeys.map((k) => k.kt).join(", ")}]. Key "${keyType}" is out of order - it should appear earlier in the hierarchy.`
662
- );
663
- }
664
- lastIndex = currentIndex;
665
- }
666
- }
667
- logger13.default("Location key order validation passed", {
668
- locKeys: locKeys.map((k) => ({ kt: k.kt, lk: k.lk })),
669
- keyDetails
670
- });
671
- };
672
670
  const getPath = (key) => {
673
671
  const localPathNames = [...pathNames];
674
- logger13.default("getPath", { key, pathNames: localPathNames });
672
+ logger14.default("getPath", { key, pathNames: localPathNames });
675
673
  const keys = generateKeyArray(key);
676
- validateLocationKeyOrder(keys);
677
674
  if (keys.length > 1) {
678
675
  const priKeys = keys.filter((k) => isPriKey(k));
679
676
  const locKeys = keys.filter((k) => !isPriKey(k));
680
- const reorderedKeys = [...locKeys, ...priKeys];
681
- logger13.default("Reordered keys for contained item", {
677
+ const reversedLocKeys = [...locKeys].reverse();
678
+ const reorderedKeys = [...reversedLocKeys, ...priKeys];
679
+ logger14.default("Reordered keys for contained item", {
682
680
  original: keys,
681
+ locKeys,
682
+ reversedLocKeys,
683
683
  reordered: reorderedKeys,
684
- priKeys,
685
- locKeys
684
+ priKeys
686
685
  });
687
686
  let path = addPath("", reorderedKeys, localPathNames);
688
687
  if (localPathNames.length === 1) {
689
688
  path = `${path}/${localPathNames[0]}`;
690
689
  }
691
- logger13.default("getPath created", { key, path });
690
+ logger14.default("getPath created", { key, path });
692
691
  return path;
693
692
  } else {
694
- let path = addPath("", keys, localPathNames);
693
+ const priKeys = keys.filter((k) => isPriKey(k));
694
+ const locKeys = keys.filter((k) => !isPriKey(k));
695
+ const reversedLocKeys = locKeys.length > 0 ? [...locKeys].reverse() : [];
696
+ const orderedKeys = [...reversedLocKeys, ...priKeys];
697
+ let path = addPath("", orderedKeys, localPathNames);
695
698
  if (localPathNames.length === 1) {
696
699
  path = `${path}/${localPathNames[0]}`;
697
700
  }
698
- logger13.default("getPath created", { key, path });
701
+ logger14.default("getPath created", { key, path });
699
702
  return path;
700
703
  }
701
704
  };
702
705
  const addPath = (base, keys, localPathNames) => {
703
- logger13.default("addPath", { base, keys, pathNames: localPathNames });
706
+ logger14.default("addPath", { base, keys, pathNames: localPathNames });
704
707
  if (keys.length < localPathNames.length - 1) {
705
- logger13.error(
708
+ logger14.error(
706
709
  "addPath should never have keys with a length less than the length of pathNames - 1",
707
710
  { keys, localPathNames }
708
711
  );
709
712
  throw new Error("addPath should never have keys with a length less than the length of pathNames - 1: " + keys.length + " " + localPathNames.length + " " + JSON.stringify(keys, localPathNames));
710
713
  } else if (keys.length > localPathNames.length) {
711
- logger13.error(
714
+ logger14.error(
712
715
  "addPath should never have keys with a length greater than the length of pathNames",
713
716
  { keys, pathNames }
714
717
  );
715
718
  throw new Error("addPath should never have keys with a length greater than the length of pathNames: " + keys.length + " " + localPathNames.length + " " + JSON.stringify(keys, localPathNames));
716
719
  }
717
720
  if (keys.length === 0) {
718
- logger13.default("addPath returning base", { base });
721
+ logger14.default("addPath returning base", { base });
719
722
  return base;
720
723
  } else {
721
724
  const currentKey = keys[0];
@@ -734,7 +737,7 @@ var createUtilities = (pkType, pathNames) => {
734
737
  const key = keys.shift();
735
738
  const id = isPriKey(key) ? key.pk : key.lk;
736
739
  const nextBase = `${base}/${pathName}/${id}`;
737
- logger13.default("Adding Path (matched)", {
740
+ logger14.default("Adding Path (matched)", {
738
741
  pathName,
739
742
  keyType,
740
743
  isPriKey: isPriKey(key),
@@ -747,7 +750,7 @@ var createUtilities = (pkType, pathNames) => {
747
750
  const key = keys.shift();
748
751
  const id = isPriKey(key) ? key.pk : key.lk;
749
752
  const nextBase = `${base}/${pathName}/${id}`;
750
- logger13.default("Adding Path (no match, using first)", {
753
+ logger14.default("Adding Path (no match, using first)", {
751
754
  pathName,
752
755
  keyType,
753
756
  isPriKey: isPriKey(key),
@@ -772,7 +775,7 @@ var createUtilities = (pkType, pathNames) => {
772
775
  };
773
776
 
774
777
  // src/AItemAPI.ts
775
- var logger14 = logger_default.get("AItemAPI");
778
+ var logger15 = logger_default.get("AItemAPI");
776
779
  var finderToParams = (finder, finderParams) => {
777
780
  return {
778
781
  finder,
@@ -780,7 +783,7 @@ var finderToParams = (finder, finderParams) => {
780
783
  };
781
784
  };
782
785
  var createAItemAPI = (api, pkType, pathNames, options) => {
783
- logger14.default("createAItemAPI", { pkType, pathNames, options });
786
+ logger15.default("createAItemAPI", { pkType, pathNames, options });
784
787
  let mergedOptions;
785
788
  const defaultOptions = {
786
789
  readAuthenticated: true,
@@ -810,14 +813,15 @@ var createAItemAPI = (api, pkType, pathNames, options) => {
810
813
  get: operations.get,
811
814
  one: operations.one,
812
815
  remove: operations.remove,
813
- update: operations.update
816
+ update: operations.update,
817
+ upsert: operations.upsert
814
818
  };
815
819
  };
816
820
 
817
821
  // src/CItemAPI.ts
818
- var logger15 = logger_default.get("CItemAPI");
822
+ var logger16 = logger_default.get("CItemAPI");
819
823
  var createCItemApi = (api, type, pathNames, options) => {
820
- logger15.default("createCItemApi", { api, type, pathNames, options });
824
+ logger16.default("createCItemApi", { api, type, pathNames, options });
821
825
  const aItemAPI = createAItemAPI(api, type, pathNames, options);
822
826
  return {
823
827
  action: aItemAPI.action,
@@ -829,6 +833,7 @@ var createCItemApi = (api, type, pathNames, options) => {
829
833
  create: aItemAPI.create,
830
834
  remove: aItemAPI.remove,
831
835
  update: aItemAPI.update,
836
+ upsert: aItemAPI.upsert,
832
837
  facet: aItemAPI.facet,
833
838
  find: aItemAPI.find,
834
839
  findOne: aItemAPI.findOne
@@ -836,24 +841,24 @@ var createCItemApi = (api, type, pathNames, options) => {
836
841
  };
837
842
 
838
843
  // src/PItemAPI.ts
839
- var logger16 = logger_default.get("PItemAPI");
844
+ var logger17 = logger_default.get("PItemAPI");
840
845
  var createPItemApi = (api, type, pathName, options) => {
841
- logger16.default("createPItemApi", { type, pathName, options });
846
+ logger17.default("createPItemApi", { type, pathName, options });
842
847
  const aItemAPI = createAItemAPI(api, type, [pathName], options);
843
- const action = async (ik, action2, body = {}) => await aItemAPI.action(ik, action2, body);
844
- const all = async (query = {}) => await aItemAPI.all(query, []);
845
- const allAction = async (action2, body = {}) => await aItemAPI.allAction(action2, body, []);
846
- const allFacet = async (facet2, params = {}) => await aItemAPI.allFacet(facet2, params);
847
- const one = async (query = {}) => await aItemAPI.one(query, []);
848
+ const action = async (ik, action2, params) => await aItemAPI.action(ik, action2, params);
849
+ const all = async (query) => await aItemAPI.all(query || {}, []);
850
+ const allAction = async (action2, params) => await aItemAPI.allAction(action2, params, []);
851
+ const allFacet = async (facet2, params) => await aItemAPI.allFacet(facet2, params);
852
+ const one = async (query) => await aItemAPI.one(query || {}, []);
848
853
  const get = async (ik) => await aItemAPI.get(ik);
849
- const create = async (item) => await aItemAPI.create(item, []);
854
+ const create = async (item, options2) => await aItemAPI.create(item, options2);
850
855
  const remove = async (ik) => await aItemAPI.remove(ik);
851
856
  const update = async (ik, item) => await aItemAPI.update(ik, item);
852
- const facet = async (ik, facet2, params = {}) => await aItemAPI.facet(ik, facet2, params);
853
- const find = async (finder, finderParams = {}) => await aItemAPI.find(finder, finderParams);
854
- const findOne = async (finder, finderParams = {}) => await aItemAPI.findOne(finder, finderParams);
857
+ const upsert = async (ik, item, locations) => await aItemAPI.upsert(ik, item, locations);
858
+ const facet = async (ik, facet2, params) => await aItemAPI.facet(ik, facet2, params);
859
+ const find = async (finder, finderParams) => await aItemAPI.find(finder, finderParams);
860
+ const findOne = async (finder, finderParams) => await aItemAPI.findOne(finder, finderParams);
855
861
  return {
856
- ...aItemAPI,
857
862
  action,
858
863
  all,
859
864
  allAction,
@@ -863,6 +868,7 @@ var createPItemApi = (api, type, pathName, options) => {
863
868
  create,
864
869
  remove,
865
870
  update,
871
+ upsert,
866
872
  facet,
867
873
  find,
868
874
  findOne
@@ -871,18 +877,18 @@ var createPItemApi = (api, type, pathName, options) => {
871
877
 
872
878
  // src/Instance.ts
873
879
  import { createInstance as createBaseInstance } from "@fjell/registry";
874
- var logger17 = logger_default.get("Instance");
880
+ var logger18 = logger_default.get("Instance");
875
881
  var createInstance = (registry, coordinate, clientApi) => {
876
- logger17.debug("createInstance", { coordinate, clientApi, registry });
882
+ logger18.debug("createInstance", { coordinate, clientApi, registry });
877
883
  const baseInstance = createBaseInstance(registry, coordinate);
878
884
  return { ...baseInstance, clientApi };
879
885
  };
880
886
 
881
887
  // src/InstanceFactory.ts
882
- var logger18 = logger_default.get("InstanceFactory");
888
+ var logger19 = logger_default.get("InstanceFactory");
883
889
  var createInstanceFactory = (clientApi) => {
884
890
  return (coordinate, context) => {
885
- logger18.debug("Creating client-api instance", { coordinate, registry: context.registry, clientApi });
891
+ logger19.debug("Creating client-api instance", { coordinate, registry: context.registry, clientApi });
886
892
  return createInstance(context.registry, coordinate, clientApi);
887
893
  };
888
894
  };
@@ -891,13 +897,13 @@ var createInstanceFactory = (clientApi) => {
891
897
  import {
892
898
  createRegistry as createBaseRegistry
893
899
  } from "@fjell/registry";
894
- var logger19 = logger_default.get("Registry");
900
+ var logger20 = logger_default.get("Registry");
895
901
  var createRegistryFactory = () => {
896
902
  return (type, registryHub) => {
897
903
  if (type !== "client-api") {
898
904
  throw new Error(`Client API registry factory can only create 'client-api' type registries, got: ${type}`);
899
905
  }
900
- logger19.debug("Creating client-api registry", { type, registryHub });
906
+ logger20.debug("Creating client-api registry", { type, registryHub });
901
907
  const baseRegistry = createBaseRegistry(type, registryHub);
902
908
  return baseRegistry;
903
909
  };
@@ -1096,8 +1102,11 @@ function isClientApiError(error) {
1096
1102
  return error instanceof ClientApiError;
1097
1103
  }
1098
1104
 
1105
+ // src/index.ts
1106
+ import { FjellHttpError, isFjellHttpError as isFjellHttpError2, extractErrorInfo } from "@fjell/http-api";
1107
+
1099
1108
  // src/http/HttpWrapper.ts
1100
- var logger20 = {
1109
+ var logger21 = {
1101
1110
  debug: (message, context) => console.debug(message, context),
1102
1111
  info: (message, context) => console.info(message, context),
1103
1112
  warning: (message, context) => console.warn(message, context),
@@ -1115,7 +1124,7 @@ var DEFAULT_RETRY_CONFIG = {
1115
1124
  return false;
1116
1125
  },
1117
1126
  onRetry: (error, attemptNumber, delay) => {
1118
- logger20.warning(`Retrying HTTP request (attempt ${attemptNumber + 1}) after ${delay}ms`, {
1127
+ logger21.warning(`Retrying HTTP request (attempt ${attemptNumber + 1}) after ${delay}ms`, {
1119
1128
  errorCode: error.code,
1120
1129
  errorMessage: error.message,
1121
1130
  delay,
@@ -1148,14 +1157,14 @@ var HttpWrapper = class {
1148
1157
  const startTime = Date.now();
1149
1158
  for (let attempt = 0; attempt <= this.retryConfig.maxRetries; attempt++) {
1150
1159
  try {
1151
- logger20.debug(`Executing ${operationName}`, {
1160
+ logger21.debug(`Executing ${operationName}`, {
1152
1161
  attempt: attempt + 1,
1153
1162
  maxRetries: this.retryConfig.maxRetries + 1,
1154
1163
  ...context
1155
1164
  });
1156
1165
  const result = await operation();
1157
1166
  if (attempt > 0) {
1158
- logger20.info(`${operationName} succeeded after ${attempt} retries`, {
1167
+ logger21.info(`${operationName} succeeded after ${attempt} retries`, {
1159
1168
  totalAttempts: attempt + 1,
1160
1169
  duration: Date.now() - startTime,
1161
1170
  ...context
@@ -1168,7 +1177,7 @@ var HttpWrapper = class {
1168
1177
  break;
1169
1178
  }
1170
1179
  if (!this.retryConfig.shouldRetry(lastError, attempt)) {
1171
- logger20.debug(`Not retrying ${operationName} due to non-retryable error`, {
1180
+ logger21.debug(`Not retrying ${operationName} due to non-retryable error`, {
1172
1181
  errorCode: lastError.code,
1173
1182
  errorMessage: lastError.message,
1174
1183
  attempt: attempt + 1,
@@ -1184,7 +1193,7 @@ var HttpWrapper = class {
1184
1193
  await sleep(delay);
1185
1194
  }
1186
1195
  }
1187
- logger20.error(`${operationName} failed after ${this.retryConfig.maxRetries + 1} attempts`, {
1196
+ logger21.error(`${operationName} failed after ${this.retryConfig.maxRetries + 1} attempts`, {
1188
1197
  errorCode: lastError?.code,
1189
1198
  errorMessage: lastError?.message,
1190
1199
  duration: Date.now() - startTime,
@@ -1276,6 +1285,7 @@ export {
1276
1285
  ClientApiError,
1277
1286
  ConfigurationError,
1278
1287
  ConflictError,
1288
+ FjellHttpError,
1279
1289
  HttpError,
1280
1290
  HttpWrapper,
1281
1291
  NetworkError,
@@ -1298,8 +1308,10 @@ export {
1298
1308
  enhanceError,
1299
1309
  executeErrorHandler,
1300
1310
  executeWithRetry,
1311
+ extractErrorInfo,
1301
1312
  getRetryConfig,
1302
1313
  isClientApiError,
1314
+ isFjellHttpError2 as isFjellHttpError,
1303
1315
  isRetryableError,
1304
1316
  shouldRetryError
1305
1317
  };