@fjell/lib-sequelize 4.4.53 → 4.4.55

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
@@ -26,7 +26,7 @@ var LibLogger = Logging.getLogger("@fjell/lib-sequelize");
26
26
  var logger_default = LibLogger;
27
27
 
28
28
  // src/Coordinate.ts
29
- import { createCoordinate as createBaseCoordinate } from "@fjell/registry";
29
+ import { createCoordinate as createBaseCoordinate } from "@fjell/core";
30
30
  var logger = logger_default.get("Coordinate");
31
31
  var SCOPE_SEQUELIZE = "sequelize";
32
32
  var createCoordinate = (kta, scopes) => {
@@ -48,10 +48,14 @@ var createDefinition = (kta, scopes, libOptions) => {
48
48
  };
49
49
 
50
50
  // src/SequelizeLibrary.ts
51
+ import * as Library3 from "@fjell/lib";
52
+
53
+ // src/Operations.ts
51
54
  import * as Library2 from "@fjell/lib";
52
55
 
53
56
  // src/ops/all.ts
54
- import { validateKeys } from "@fjell/core";
57
+ import { createAllWrapper } from "@fjell/core";
58
+ import { validateKeys } from "@fjell/core/validation";
55
59
 
56
60
  // src/QueryBuilder.ts
57
61
  import {
@@ -376,52 +380,6 @@ var buildQuery = (itemQuery, model) => {
376
380
  return options;
377
381
  };
378
382
 
379
- // src/validation/LocationKeyValidator.ts
380
- var logger4 = logger_default.get("LocationKeyValidator");
381
- var validateLocations = (locations, coordinate, operation) => {
382
- if (!locations || locations.length === 0) {
383
- return;
384
- }
385
- const keyTypeArray = coordinate.kta;
386
- const expectedLocationTypes = keyTypeArray.slice(1);
387
- const actualLocationTypes = locations.map((loc) => loc.kt);
388
- logger4.debug(`Validating locations for ${operation}`, {
389
- expected: expectedLocationTypes,
390
- actual: actualLocationTypes,
391
- coordinate: keyTypeArray
392
- });
393
- if (actualLocationTypes.length > expectedLocationTypes.length) {
394
- logger4.error("Location key array has too many elements", {
395
- expected: expectedLocationTypes.length,
396
- actual: actualLocationTypes.length,
397
- expectedTypes: expectedLocationTypes,
398
- actualTypes: actualLocationTypes,
399
- coordinate,
400
- operation
401
- });
402
- throw new Error(
403
- `Invalid location key array for ${operation}: Expected at most ${expectedLocationTypes.length} location keys (hierarchy: [${expectedLocationTypes.join(", ")}]), but received ${actualLocationTypes.length} (types: [${actualLocationTypes.join(", ")}])`
404
- );
405
- }
406
- for (let i = 0; i < actualLocationTypes.length; i++) {
407
- if (expectedLocationTypes[i] !== actualLocationTypes[i]) {
408
- logger4.error("Location key array order mismatch", {
409
- position: i,
410
- expected: expectedLocationTypes[i],
411
- actual: actualLocationTypes[i],
412
- expectedHierarchy: expectedLocationTypes,
413
- actualOrder: actualLocationTypes,
414
- coordinate,
415
- operation
416
- });
417
- throw new Error(
418
- `Invalid location key array order for ${operation}: At position ${i}, expected key type "${expectedLocationTypes[i]}" but received "${actualLocationTypes[i]}". Location keys must be ordered according to the hierarchy: [${expectedLocationTypes.join(", ")}]. Received order: [${actualLocationTypes.join(", ")}]`
419
- );
420
- }
421
- }
422
- logger4.debug(`Location key array validation passed for ${operation}`, { locations });
423
- };
424
-
425
383
  // src/util/relationshipUtils.ts
426
384
  var buildRelationshipChain = (targetModel, kta, currentIndex, targetIndex) => {
427
385
  const associationParts = [];
@@ -502,9 +460,9 @@ var buildRelationshipPath = (targetModel, locatorType, kta, includeIsDirect = fa
502
460
  };
503
461
 
504
462
  // src/KeyMaster.ts
505
- var logger5 = logger_default.get("sequelize", "KeyMaster");
463
+ var logger4 = logger_default.get("sequelize", "KeyMaster");
506
464
  var extractLocationKeyValue = (model, item, locatorType, kta) => {
507
- logger5.default("Extracting location key value", { locatorType, kta });
465
+ logger4.default("Extracting location key value", { locatorType, kta });
508
466
  const relationshipInfo = buildRelationshipPath(model, locatorType, kta, true);
509
467
  if (!relationshipInfo.found) {
510
468
  throw new Error(`Location key '${locatorType}' cannot be resolved on model '${model.name}' or through its relationships. Key type array: [${kta.join(", ")}]. Available associations: [${Object.keys(model.associations || {}).join(", ")}]`);
@@ -548,12 +506,12 @@ var extractLocationKeyValue = (model, item, locatorType, kta) => {
548
506
  }
549
507
  };
550
508
  var removeKey = (item) => {
551
- logger5.default("Removing Key", { item });
509
+ logger4.default("Removing Key", { item });
552
510
  delete item.key;
553
511
  return item;
554
512
  };
555
513
  var addKey = (model, item, keyTypes) => {
556
- logger5.default("Adding Key", { item });
514
+ logger4.default("Adding Key", { item });
557
515
  const key = {};
558
516
  const modelClass = model.constructor;
559
517
  const primaryKeyAttr = modelClass.primaryKeyAttribute;
@@ -568,7 +526,7 @@ var addKey = (model, item, keyTypes) => {
568
526
  locationKeys.push({ kt: locatorType, lk });
569
527
  } catch (error) {
570
528
  const errorMessage = error instanceof Error ? error.message : String(error);
571
- logger5.error(`Failed to extract location key for '${locatorType}'`, { error: errorMessage, item, keyTypes });
529
+ logger4.error(`Failed to extract location key for '${locatorType}'`, { error: errorMessage, item, keyTypes });
572
530
  throw error;
573
531
  }
574
532
  }
@@ -589,7 +547,7 @@ import {
589
547
 
590
548
  // src/EventCoordinator.ts
591
549
  import deepmerge from "deepmerge";
592
- var logger6 = logger_default.get("sequelize", "EventCoordinator");
550
+ var logger5 = logger_default.get("sequelize", "EventCoordinator");
593
551
  var populateEvents = (item) => {
594
552
  const events = {
595
553
  created: { at: item.createdAt || null },
@@ -600,7 +558,7 @@ var populateEvents = (item) => {
600
558
  return item;
601
559
  };
602
560
  var extractEvents = (item) => {
603
- logger6.default("Extracting Events to database fields", { item });
561
+ logger5.default("Extracting Events to database fields", { item });
604
562
  if (item.events) {
605
563
  if (item.events.created?.at) {
606
564
  item.createdAt = item.events.created.at;
@@ -615,7 +573,7 @@ var extractEvents = (item) => {
615
573
  return item;
616
574
  };
617
575
  var removeEvents = (item) => {
618
- logger6.default("Removing Events", { item });
576
+ logger5.default("Removing Events", { item });
619
577
  delete item.events;
620
578
  return item;
621
579
  };
@@ -623,10 +581,11 @@ var removeEvents = (item) => {
623
581
  // src/processing/ReferenceBuilder.ts
624
582
  var buildSequelizeReference = async (item, referenceDefinition, registry, context) => {
625
583
  const libLogger = logger_default.get("processing", "ReferenceBuilder");
584
+ const isCompositeItem = referenceDefinition.kta.length > 1;
626
585
  const primaryKeyType = referenceDefinition.kta[0];
627
- if (referenceDefinition.kta.length > 1) {
586
+ if (isCompositeItem) {
628
587
  libLogger.debug(
629
- "Using multikey reference with PriKey assumption",
588
+ "Detected composite item reference - will use ComKey with empty loc array",
630
589
  {
631
590
  kta: referenceDefinition.kta,
632
591
  primaryKeyType,
@@ -634,10 +593,6 @@ var buildSequelizeReference = async (item, referenceDefinition, registry, contex
634
593
  column: referenceDefinition.column
635
594
  }
636
595
  );
637
- libLogger.debug(
638
- 'ASSUMPTION: The primary key for key type "%s" is unique and can be used to retrieve composite items',
639
- primaryKeyType
640
- );
641
596
  }
642
597
  if (!registry) {
643
598
  throw new Error(
@@ -655,38 +610,91 @@ var buildSequelizeReference = async (item, referenceDefinition, registry, contex
655
610
  item[referenceDefinition.property] = null;
656
611
  return item;
657
612
  }
658
- const priKey = {
659
- kt: primaryKeyType,
660
- pk: columnValue
661
- };
613
+ let itemKey;
614
+ if (!isCompositeItem) {
615
+ itemKey = {
616
+ kt: primaryKeyType,
617
+ pk: columnValue
618
+ };
619
+ } else if (referenceDefinition.locationColumns && referenceDefinition.locationColumns.length > 0) {
620
+ const locationTypes = referenceDefinition.kta.slice(1);
621
+ const loc = [];
622
+ for (let i = 0; i < referenceDefinition.locationColumns.length; i++) {
623
+ const columnName = referenceDefinition.locationColumns[i];
624
+ const locValue = item[columnName];
625
+ if (locValue == null) {
626
+ libLogger.warning(
627
+ `Location column '${columnName}' is null/undefined for reference '${referenceDefinition.property}'. Falling back to empty loc array search.`
628
+ );
629
+ itemKey = {
630
+ kt: primaryKeyType,
631
+ pk: columnValue,
632
+ loc: []
633
+ };
634
+ break;
635
+ }
636
+ loc.push({
637
+ kt: locationTypes[i],
638
+ lk: locValue
639
+ });
640
+ }
641
+ if (!itemKey) {
642
+ itemKey = {
643
+ kt: primaryKeyType,
644
+ pk: columnValue,
645
+ loc
646
+ };
647
+ libLogger.debug("Built full ComKey with location context", {
648
+ itemKey,
649
+ locationColumns: referenceDefinition.locationColumns,
650
+ property: referenceDefinition.property
651
+ });
652
+ }
653
+ } else {
654
+ itemKey = {
655
+ kt: primaryKeyType,
656
+ pk: columnValue,
657
+ loc: []
658
+ };
659
+ libLogger.debug("Using empty loc array for composite item reference", {
660
+ kta: referenceDefinition.kta,
661
+ property: referenceDefinition.property
662
+ });
663
+ }
664
+ libLogger.debug("Created reference key", {
665
+ itemKey,
666
+ isCompositeItem,
667
+ hasLocationColumns: !!referenceDefinition.locationColumns,
668
+ property: referenceDefinition.property
669
+ });
662
670
  let referencedItem;
663
671
  if (context) {
664
- if (context.isCached(priKey)) {
665
- libLogger.debug("Using cached reference", { priKey, property: referenceDefinition.property });
666
- referencedItem = context.getCached(priKey);
667
- } else if (context.isInProgress(priKey)) {
672
+ if (context.isCached(itemKey)) {
673
+ libLogger.debug("Using cached reference", { itemKey, property: referenceDefinition.property });
674
+ referencedItem = context.getCached(itemKey);
675
+ } else if (context.isInProgress(itemKey)) {
668
676
  libLogger.debug("Circular dependency detected, creating reference placeholder", {
669
- priKey,
677
+ itemKey,
670
678
  property: referenceDefinition.property
671
679
  });
672
680
  referencedItem = {
673
- key: priKey
681
+ key: itemKey
674
682
  // Add any other minimal properties that might be needed
675
683
  // This prevents infinite loops while still providing the key for identification
676
684
  };
677
685
  } else {
678
- context.markInProgress(priKey);
686
+ context.markInProgress(itemKey);
679
687
  try {
680
- referencedItem = await library.operations.get(priKey);
681
- context.setCached(priKey, referencedItem);
688
+ referencedItem = await library.operations.get(itemKey);
689
+ context.setCached(itemKey, referencedItem);
682
690
  } catch (error) {
683
691
  throw error;
684
692
  } finally {
685
- context.markComplete(priKey);
693
+ context.markComplete(itemKey);
686
694
  }
687
695
  }
688
696
  } else {
689
- referencedItem = await library.operations.get(priKey);
697
+ referencedItem = await library.operations.get(itemKey);
690
698
  }
691
699
  item[referenceDefinition.property] = referencedItem;
692
700
  return item;
@@ -700,27 +708,27 @@ var stripSequelizeReferenceItems = (item, referenceDefinitions) => {
700
708
  };
701
709
 
702
710
  // src/RowProcessor.ts
703
- var logger7 = logger_default.get("sequelize", "RowProcessor");
711
+ var logger6 = logger_default.get("sequelize", "RowProcessor");
704
712
  var processRow = async (row, keyTypes, referenceDefinitions, aggregationDefinitions, registry, context) => {
705
- logger7.default("Processing Row", { row });
713
+ logger6.default("Processing Row", { row });
706
714
  const operationContext = context || createOperationContext();
707
715
  return contextManager.withContext(operationContext, async () => {
708
716
  let item = row.get({ plain: true });
709
- logger7.default("Adding Key to Item with Key Types: %s", stringifyJSON(keyTypes));
717
+ logger6.default("Adding Key to Item with Key Types: %s", stringifyJSON(keyTypes));
710
718
  item = addKey(row, item, keyTypes);
711
719
  item = populateEvents(item);
712
- logger7.default("Key Added to Item: %s", stringifyJSON(item.key));
720
+ logger6.default("Key Added to Item: %s", stringifyJSON(item.key));
713
721
  operationContext.markInProgress(item.key);
714
722
  try {
715
723
  if (referenceDefinitions && referenceDefinitions.length > 0) {
716
724
  for (const referenceDefinition of referenceDefinitions) {
717
- logger7.default("Processing Reference for %s to %s", item.key.kt, stringifyJSON(referenceDefinition.kta));
725
+ logger6.default("Processing Reference for %s to %s", item.key.kt, stringifyJSON(referenceDefinition.kta));
718
726
  item = await buildSequelizeReference(item, referenceDefinition, registry, operationContext);
719
727
  }
720
728
  }
721
729
  if (aggregationDefinitions && aggregationDefinitions.length > 0) {
722
730
  for (const aggregationDefinition of aggregationDefinitions) {
723
- logger7.default("Processing Aggregation for %s from %s", item.key.kt, stringifyJSON(aggregationDefinition.kta));
731
+ logger6.default("Processing Aggregation for %s from %s", item.key.kt, stringifyJSON(aggregationDefinition.kta));
724
732
  item = await buildAggregation(item, aggregationDefinition, registry, operationContext);
725
733
  }
726
734
  }
@@ -728,14 +736,205 @@ var processRow = async (row, keyTypes, referenceDefinitions, aggregationDefiniti
728
736
  } finally {
729
737
  operationContext.markComplete(item.key);
730
738
  }
731
- logger7.default("Processed Row: %j", stringifyJSON(item));
739
+ logger6.default("Processed Row: %j", stringifyJSON(item));
732
740
  return item;
733
741
  });
734
742
  };
735
743
 
736
744
  // src/ops/all.ts
737
745
  import { Op as Op2 } from "sequelize";
738
- var logger8 = logger_default.get("sequelize", "ops", "all");
746
+
747
+ // src/errors/sequelizeErrorHandler.ts
748
+ import {
749
+ BusinessLogicError,
750
+ DuplicateError,
751
+ ValidationError
752
+ } from "@fjell/core";
753
+ function transformSequelizeError(error, itemType, key, modelName, itemData) {
754
+ if (error.code === "23505" || error.original?.code === "23505") {
755
+ const constraint = error.original?.constraint;
756
+ const detail = error.original?.detail;
757
+ let message = "";
758
+ if (constraint && detail) {
759
+ message = `Duplicate value violates unique constraint '${constraint}'. ${detail}`;
760
+ } else if (detail) {
761
+ message = `Duplicate value detected. This record already exists. ${detail}`;
762
+ } else {
763
+ message = `${itemType} already exists`;
764
+ }
765
+ const field = error.fields ? Object.keys(error.fields)[0] : "unique constraint";
766
+ return new DuplicateError(message, key, field);
767
+ }
768
+ if (error.name === "SequelizeUniqueConstraintError" || error.code === "ER_DUP_ENTRY") {
769
+ const field = error.fields ? Object.keys(error.fields)[0] : "unique constraint";
770
+ return new DuplicateError(
771
+ `${itemType} already exists`,
772
+ key,
773
+ field
774
+ );
775
+ }
776
+ if (error.name === "SequelizeValidationError") {
777
+ const fields = error.errors?.map((e) => e.path) || [];
778
+ const message = error.errors?.map((e) => e.message).join(", ") || `Validation failed for ${itemType}`;
779
+ return new ValidationError(
780
+ message,
781
+ fields,
782
+ "Fix validation errors and try again"
783
+ );
784
+ }
785
+ if (error.code === "23503" || error.original?.code === "23503") {
786
+ const constraint = error.original?.constraint;
787
+ const detail = error.original?.detail;
788
+ let message = "";
789
+ if (constraint && detail) {
790
+ message = `Foreign key constraint '${constraint}' violated. Referenced record does not exist. ${detail}`;
791
+ } else if (constraint) {
792
+ message = `Foreign key constraint '${constraint}' violated. Referenced record does not exist.`;
793
+ } else if (detail) {
794
+ message = `Referenced record does not exist. Check that all related records are valid. ${detail}`;
795
+ } else {
796
+ message = "Referenced item does not exist";
797
+ }
798
+ return new ValidationError(
799
+ message,
800
+ void 0,
801
+ "Ensure all referenced items exist before creating this relationship"
802
+ );
803
+ }
804
+ if (error.name === "SequelizeForeignKeyConstraintError" || error.code === "ER_NO_REFERENCED_ROW") {
805
+ return new ValidationError(
806
+ "Referenced item does not exist",
807
+ void 0,
808
+ "Ensure all referenced items exist before creating this relationship"
809
+ );
810
+ }
811
+ if (error.code === "23502" || error.original?.code === "23502") {
812
+ const column = error.original?.column;
813
+ const fields = column ? [column] : void 0;
814
+ return new ValidationError(
815
+ column ? `Required field '${column}' cannot be null` : "Required field is missing or null",
816
+ fields,
817
+ "Provide all required fields"
818
+ );
819
+ }
820
+ if (error.code === "23514" || error.original?.code === "23514") {
821
+ const constraint = error.original?.constraint;
822
+ const detail = error.original?.detail;
823
+ let message = "";
824
+ if (constraint && detail) {
825
+ message = `Check constraint '${constraint}' violated. ${detail}`;
826
+ } else if (detail) {
827
+ message = `Data validation failed. Check constraint violated. ${detail}`;
828
+ } else if (constraint) {
829
+ message = `Check constraint '${constraint}' violated`;
830
+ } else {
831
+ message = "Data validation failed";
832
+ }
833
+ return new ValidationError(
834
+ message,
835
+ void 0,
836
+ "Ensure data meets all constraints"
837
+ );
838
+ }
839
+ if (error.name === "SequelizeDatabaseError" && error.message?.includes("Unknown column")) {
840
+ const match = error.message.match(/Unknown column '(.+?)'/);
841
+ const field = match ? match[1] : "unknown field";
842
+ return new ValidationError(
843
+ `Invalid field: ${field}`,
844
+ [field],
845
+ "Check the field names in your request"
846
+ );
847
+ }
848
+ if (error.code === "22001" || error.original?.code === "22001") {
849
+ const detail = error.original?.detail;
850
+ const message = detail ? `Data too long for field. Check string lengths. ${detail}` : "Data too long for field";
851
+ return new ValidationError(
852
+ message,
853
+ void 0,
854
+ "Check string lengths and try again"
855
+ );
856
+ }
857
+ if (error.code === "22003" || error.original?.code === "22003") {
858
+ const detail = error.original?.detail;
859
+ const message = detail ? `Numeric value out of range. Check number values. ${detail}` : "Numeric value out of range";
860
+ return new ValidationError(
861
+ message,
862
+ void 0,
863
+ "Check number values and try again"
864
+ );
865
+ }
866
+ if (error.code === "42703" || error.original?.code === "42703") {
867
+ const column = error.original?.column;
868
+ const fields = column ? [column] : void 0;
869
+ const message = column && modelName ? `Column '${column}' does not exist in table '${modelName}'` : column ? `Column '${column}' does not exist` : "Referenced column does not exist";
870
+ return new ValidationError(
871
+ message,
872
+ fields,
873
+ "Check the field names in your request"
874
+ );
875
+ }
876
+ if (error.code === "42P01" || error.original?.code === "42P01") {
877
+ const message = modelName ? `Table '${modelName}' does not exist` : "Table does not exist";
878
+ return new ValidationError(
879
+ message,
880
+ void 0,
881
+ "Check the table name and database configuration"
882
+ );
883
+ }
884
+ if (error.message?.includes("notNull Violation") || error.message?.includes("cannot be null")) {
885
+ const fieldMatches = error.message.match(/([a-zA-Z]+\.[a-zA-Z]+) cannot be null/g);
886
+ if (fieldMatches) {
887
+ const fields = fieldMatches.map((match) => {
888
+ const parts = match.split(".");
889
+ return parts[1]?.split(" ")[0];
890
+ }).filter(Boolean);
891
+ if (fields.length > 0) {
892
+ return new ValidationError(
893
+ `Required field${fields.length > 1 ? "s" : ""} ${fields.join(", ")} cannot be null`,
894
+ fields,
895
+ "Provide all required fields"
896
+ );
897
+ }
898
+ }
899
+ return new ValidationError(
900
+ "Required fields are missing",
901
+ void 0,
902
+ "Provide all required fields"
903
+ );
904
+ }
905
+ if (error.name === "SequelizeConnectionError" || error.name === "SequelizeConnectionRefusedError") {
906
+ return new BusinessLogicError(
907
+ "Database connection failed",
908
+ "Check database connectivity and try again",
909
+ true
910
+ // retryable
911
+ );
912
+ }
913
+ if (error.name === "SequelizeTimeoutError") {
914
+ return new BusinessLogicError(
915
+ "Database operation timed out",
916
+ "Try again or simplify the operation",
917
+ true
918
+ // retryable
919
+ );
920
+ }
921
+ if (!error.original && modelName && itemData && error.message) {
922
+ const formattedData = JSON.stringify(itemData, null, 2);
923
+ return new Error(
924
+ `Database error in ${modelName}.create(): ${error.message}. Item data: ${formattedData}`
925
+ );
926
+ }
927
+ if (error.original && modelName && itemData) {
928
+ const formattedData = JSON.stringify(itemData, null, 2);
929
+ return new Error(
930
+ `Database error in ${modelName}.create(): ${error.message}. Item data: ${formattedData}`
931
+ );
932
+ }
933
+ return error;
934
+ }
935
+
936
+ // src/ops/all.ts
937
+ var logger7 = logger_default.get("sequelize", "ops", "all");
739
938
  var mergeIncludes = (existingIncludes, newIncludes) => {
740
939
  const mergedIncludes = [...existingIncludes];
741
940
  for (const newInclude of newIncludes) {
@@ -757,162 +956,105 @@ var mergeIncludes = (existingIncludes, newIncludes) => {
757
956
  };
758
957
  var getAllOperation = (models, definition, registry) => {
759
958
  const { coordinate, options: { references, aggregations } } = definition;
760
- const all = async (itemQuery, locations) => {
761
- logger8.debug(`ALL operation called on ${models[0].name} with ${locations?.length || 0} location filters: ${locations?.map((loc2) => `${loc2.kt}=${loc2.lk}`).join(", ") || "none"}`);
762
- validateLocations(locations, coordinate, "all");
763
- const loc = locations || [];
764
- const model = models[0];
765
- const options = buildQuery(itemQuery, model);
766
- if (loc.length > 0) {
767
- const { kta } = coordinate;
768
- const directLocations = [];
769
- const hierarchicalLocations = [];
770
- const additionalIncludes = [];
771
- for (const locKey of loc) {
772
- const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta, true);
773
- if (!relationshipInfo.found) {
774
- const errorMessage = `Location key '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
775
- logger8.error(errorMessage, { locations: loc, kta });
776
- throw new Error(errorMessage);
777
- }
778
- if (relationshipInfo.isDirect) {
779
- directLocations.push(locKey);
780
- } else {
781
- hierarchicalLocations.push(locKey);
782
- }
783
- }
784
- for (const locKey of directLocations) {
785
- if (locKey.lk === void 0 || locKey.lk == null || locKey.lk === "" || typeof locKey.lk === "object" && Object.keys(locKey.lk).length === 0) {
786
- logger8.error(`Location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`, { locKey, locations: loc });
787
- throw new Error(`Location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`);
788
- }
789
- const foreignKeyField = locKey.kt + "Id";
790
- if (options.where[foreignKeyField]) {
791
- logger8.debug(`[ALL] Field ${foreignKeyField} already constrained by itemQuery, skipping location constraint to avoid conflicts`);
792
- continue;
793
- }
794
- logger8.trace(`[ALL] Setting direct location where clause: ${foreignKeyField} = ${stringifyJSON(locKey.lk)} (type: ${typeof locKey.lk})`);
795
- options.where[foreignKeyField] = {
796
- [Op2.eq]: locKey.lk
797
- };
798
- }
799
- for (const locKey of hierarchicalLocations) {
800
- if (locKey.lk === void 0 || locKey.lk == null || locKey.lk === "" || typeof locKey.lk === "object" && Object.keys(locKey.lk).length === 0) {
801
- logger8.error(`Hierarchical location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`, { locKey, locations: loc });
802
- throw new Error(`Hierarchical location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`);
803
- }
804
- const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta);
805
- if (relationshipInfo.found && relationshipInfo.path) {
806
- if (options.where[relationshipInfo.path]) {
807
- logger8.debug(`[ALL] Field ${relationshipInfo.path} already constrained by itemQuery, skipping hierarchical location constraint to avoid conflicts`);
808
- continue;
959
+ return createAllWrapper(
960
+ coordinate,
961
+ async (itemQuery, locations) => {
962
+ try {
963
+ const locs = locations ?? [];
964
+ logger7.debug(`ALL operation called on ${models[0].name} with ${locs.length} location filters: ${locs.map((loc2) => `${loc2.kt}=${loc2.lk}`).join(", ") || "none"}`);
965
+ const loc = locs;
966
+ const model = models[0];
967
+ const options = buildQuery(itemQuery ?? {}, model);
968
+ if (loc.length > 0) {
969
+ const { kta } = coordinate;
970
+ const directLocations = [];
971
+ const hierarchicalLocations = [];
972
+ const additionalIncludes = [];
973
+ for (const locKey of loc) {
974
+ const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta, true);
975
+ if (!relationshipInfo.found) {
976
+ const errorMessage = `Location key '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
977
+ logger7.error(errorMessage, { locations: loc, kta });
978
+ throw new Error(errorMessage);
979
+ }
980
+ if (relationshipInfo.isDirect) {
981
+ directLocations.push(locKey);
982
+ } else {
983
+ hierarchicalLocations.push(locKey);
984
+ }
809
985
  }
810
- logger8.trace(`[ALL] Setting hierarchical location where clause: ${relationshipInfo.path} = ${stringifyJSON(locKey.lk)} (type: ${typeof locKey.lk})`);
811
- options.where[relationshipInfo.path] = {
812
- [Op2.eq]: locKey.lk
813
- };
814
- if (relationshipInfo.includes) {
815
- additionalIncludes.push(...relationshipInfo.includes);
986
+ for (const locKey of directLocations) {
987
+ if (locKey.lk === void 0 || locKey.lk == null || locKey.lk === "" || typeof locKey.lk === "object" && Object.keys(locKey.lk).length === 0) {
988
+ logger7.error(`Location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`, { locKey, locations: loc });
989
+ throw new Error(`Location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`);
990
+ }
991
+ const foreignKeyField = locKey.kt + "Id";
992
+ if (options.where[foreignKeyField]) {
993
+ logger7.debug(`[ALL] Field ${foreignKeyField} already constrained by itemQuery, skipping location constraint to avoid conflicts`);
994
+ continue;
995
+ }
996
+ logger7.trace(`[ALL] Setting direct location where clause: ${foreignKeyField} = ${stringifyJSON(locKey.lk)} (type: ${typeof locKey.lk})`);
997
+ options.where[foreignKeyField] = {
998
+ [Op2.eq]: locKey.lk
999
+ };
1000
+ }
1001
+ for (const locKey of hierarchicalLocations) {
1002
+ if (locKey.lk === void 0 || locKey.lk == null || locKey.lk === "" || typeof locKey.lk === "object" && Object.keys(locKey.lk).length === 0) {
1003
+ logger7.error(`Hierarchical location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`, { locKey, locations: loc });
1004
+ throw new Error(`Hierarchical location key '${locKey.kt}' has invalid lk value: ${stringifyJSON(locKey.lk)}`);
1005
+ }
1006
+ const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta);
1007
+ if (relationshipInfo.found && relationshipInfo.path) {
1008
+ if (options.where[relationshipInfo.path]) {
1009
+ logger7.debug(`[ALL] Field ${relationshipInfo.path} already constrained by itemQuery, skipping hierarchical location constraint to avoid conflicts`);
1010
+ continue;
1011
+ }
1012
+ logger7.trace(`[ALL] Setting hierarchical location where clause: ${relationshipInfo.path} = ${stringifyJSON(locKey.lk)} (type: ${typeof locKey.lk})`);
1013
+ options.where[relationshipInfo.path] = {
1014
+ [Op2.eq]: locKey.lk
1015
+ };
1016
+ if (relationshipInfo.includes) {
1017
+ additionalIncludes.push(...relationshipInfo.includes);
1018
+ }
1019
+ }
1020
+ }
1021
+ if (additionalIncludes.length > 0) {
1022
+ const existingIncludes = options.include || [];
1023
+ options.include = mergeIncludes(existingIncludes, additionalIncludes);
816
1024
  }
817
1025
  }
1026
+ logger7.default(`All query configured for ${model.name} with where fields: ${options.where ? Object.keys(options.where).join(", ") : "none"}, includes: ${options.include?.length || 0}`);
1027
+ try {
1028
+ logger7.trace(`[ALL] Executing ${model.name}.findAll() with options: ${JSON.stringify(options, null, 2)}`);
1029
+ } catch {
1030
+ logger7.trace(`[ALL] Executing ${model.name}.findAll() with options containing non-serializable operators (${Object.keys(options.where || {}).length} where conditions)`);
1031
+ }
1032
+ const matchingItems = await model.findAll(options);
1033
+ const currentContext = contextManager.getCurrentContext();
1034
+ const results = await Promise.all(matchingItems.map(async (row) => {
1035
+ const processedRow = await processRow(row, coordinate.kta, references || [], aggregations || [], registry, currentContext);
1036
+ return validateKeys(processedRow, coordinate.kta);
1037
+ }));
1038
+ logger7.debug(`[ALL] Returning ${results.length} ${model.name} records`);
1039
+ return results;
1040
+ } catch (error) {
1041
+ throw transformSequelizeError(error, coordinate.kta[0]);
818
1042
  }
819
- if (additionalIncludes.length > 0) {
820
- const existingIncludes = options.include || [];
821
- options.include = mergeIncludes(existingIncludes, additionalIncludes);
822
- }
823
- }
824
- logger8.default(`All query configured for ${model.name} with where fields: ${options.where ? Object.keys(options.where).join(", ") : "none"}, includes: ${options.include?.length || 0}`);
825
- try {
826
- logger8.trace(`[ALL] Executing ${model.name}.findAll() with options: ${JSON.stringify(options, null, 2)}`);
827
- } catch {
828
- logger8.trace(`[ALL] Executing ${model.name}.findAll() with options containing non-serializable operators (${Object.keys(options.where || {}).length} where conditions)`);
829
1043
  }
830
- const matchingItems = await model.findAll(options);
831
- const currentContext = contextManager.getCurrentContext();
832
- const results = await Promise.all(matchingItems.map(async (row) => {
833
- const processedRow = await processRow(row, coordinate.kta, references || [], aggregations || [], registry, currentContext);
834
- return validateKeys(processedRow, coordinate.kta);
835
- }));
836
- logger8.debug(`[ALL] Returning ${results.length} ${model.name} records`);
837
- return results;
838
- };
839
- return all;
1044
+ );
840
1045
  };
841
1046
 
842
1047
  // src/ops/create.ts
843
- import { isComKey as isComKey2, isPriKey as isPriKey2, validateKeys as validateKeys2 } from "@fjell/core";
844
- var logger9 = logger_default.get("sequelize", "ops", "create");
845
- function translateDatabaseError(error, itemData, modelName) {
846
- const originalMessage = error.message || "";
847
- const errorCode = error.original?.code;
848
- const constraint = error.original?.constraint;
849
- const detail = error.original?.detail;
850
- logger9.error("Database error during create operation", {
851
- errorCode,
852
- constraint,
853
- detail,
854
- originalMessage,
855
- modelName,
856
- itemData: JSON.stringify(itemData, null, 2)
857
- });
858
- switch (errorCode) {
859
- case "23505":
860
- if (constraint) {
861
- return new Error(`Duplicate value violates unique constraint '${constraint}'. ${detail || ""}`);
862
- }
863
- return new Error(`Duplicate value detected. This record already exists. ${detail || ""}`);
864
- case "23503":
865
- if (constraint) {
866
- return new Error(`Foreign key constraint '${constraint}' violated. Referenced record does not exist. ${detail || ""}`);
867
- }
868
- return new Error(`Referenced record does not exist. Check that all related records are valid. ${detail || ""}`);
869
- case "23502":
870
- const column = error.original?.column;
871
- if (column) {
872
- return new Error(`Required field '${column}' cannot be null`);
873
- }
874
- return new Error(`Required field is missing or null`);
875
- case "23514":
876
- if (constraint) {
877
- return new Error(`Check constraint '${constraint}' violated. ${detail || ""}`);
878
- }
879
- return new Error(`Data validation failed. Check constraint violated. ${detail || ""}`);
880
- case "22001":
881
- return new Error(`Data too long for field. Check string lengths. ${detail || ""}`);
882
- case "22003":
883
- return new Error(`Numeric value out of range. Check number values. ${detail || ""}`);
884
- case "42703":
885
- const undefinedColumn = error.original?.column;
886
- if (undefinedColumn) {
887
- return new Error(`Column '${undefinedColumn}' does not exist in table '${modelName}'`);
888
- }
889
- return new Error(`Referenced column does not exist`);
890
- case "42P01":
891
- return new Error(`Table '${modelName}' does not exist`);
892
- default:
893
- if (originalMessage.includes("notNull Violation")) {
894
- const fieldMatches = originalMessage.match(/([a-zA-Z]+\.[a-zA-Z]+) cannot be null/g);
895
- if (fieldMatches) {
896
- const fields = fieldMatches.map((match) => {
897
- const parts = match.split(".");
898
- return parts[1]?.split(" ")[0];
899
- }).filter(Boolean);
900
- if (fields.length > 0) {
901
- return new Error(`Required field${fields.length > 1 ? "s" : ""} ${fields.join(", ")} cannot be null`);
902
- }
903
- }
904
- return new Error("Required fields are missing");
905
- }
906
- return new Error(`Database error in ${modelName}.create(): ${originalMessage}. Item data: ${JSON.stringify(itemData, null, 2)}`);
907
- }
908
- }
1048
+ import { createCreateWrapper, isComKey as isComKey2, isPriKey as isPriKey2 } from "@fjell/core";
1049
+ import { validateKeys as validateKeys2 } from "@fjell/core/validation";
1050
+ var logger8 = logger_default.get("sequelize", "ops", "create");
909
1051
  async function validateHierarchicalChain(models, locKey, kta) {
1052
+ const locatorIndex = kta.indexOf(locKey.kt);
1053
+ if (locatorIndex === -1) {
1054
+ throw new Error(`Locator type '${locKey.kt}' not found in kta array`);
1055
+ }
1056
+ const locatorModel = models[locatorIndex] || models[0];
910
1057
  try {
911
- const locatorIndex = kta.indexOf(locKey.kt);
912
- if (locatorIndex === -1) {
913
- throw new Error(`Locator type '${locKey.kt}' not found in kta array`);
914
- }
915
- const locatorModel = models[locatorIndex] || models[0];
916
1058
  const chainResult = buildRelationshipChain(locatorModel, kta, locatorIndex, kta.length - 1);
917
1059
  if (!chainResult.success) {
918
1060
  const record2 = await locatorModel.findByPk(locKey.lk);
@@ -933,56 +1075,82 @@ async function validateHierarchicalChain(models, locKey, kta) {
933
1075
  }
934
1076
  } catch (error) {
935
1077
  if (error.original) {
936
- throw translateDatabaseError(error, { locKey, kta }, locKey.kt);
1078
+ throw transformSequelizeError(error, locKey.kt, { locKey, kta }, locatorModel.name);
937
1079
  }
938
1080
  throw error;
939
1081
  }
940
1082
  }
941
1083
  var getCreateOperation = (models, definition, registry) => {
942
- const create = async (item, options) => {
943
- const constraints = options?.key ? `key: pk=${options.key.pk}, loc=[${isComKey2(options.key) ? options.key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ") : ""}]` : options?.locations ? `locations: ${options.locations.map((loc) => `${loc.kt}=${loc.lk}`).join(", ")}` : "no constraints";
944
- logger9.debug(`CREATE operation called on ${models[0].name} with ${constraints}`);
945
- logger9.default(`Create configured for ${models[0].name} with ${Object.keys(item).length} item fields`);
946
- const { coordinate, options: { references, aggregations } } = definition;
947
- const { kta } = coordinate;
948
- if (options?.locations) {
949
- validateLocations(options.locations, coordinate, "create");
950
- }
951
- if (options?.key && isComKey2(options.key)) {
952
- validateLocations(options.key.loc, coordinate, "create");
953
- }
954
- const model = models[0];
955
- const modelAttributes = model.getAttributes();
956
- let itemData = { ...item };
957
- itemData = extractEvents(itemData);
958
- itemData = removeEvents(itemData);
959
- const invalidAttributes = [];
960
- for (const key of Object.keys(itemData)) {
961
- if (!modelAttributes[key]) {
962
- invalidAttributes.push(key);
1084
+ const { coordinate, options: { references, aggregations } } = definition;
1085
+ const { kta } = coordinate;
1086
+ return createCreateWrapper(
1087
+ coordinate,
1088
+ async (item, options) => {
1089
+ const constraints = options?.key ? `key: pk=${options.key.pk}, loc=[${isComKey2(options.key) ? options.key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ") : ""}]` : options?.locations ? `locations: ${options.locations.map((loc) => `${loc.kt}=${loc.lk}`).join(", ")}` : "no constraints";
1090
+ logger8.debug(`CREATE operation called on ${models[0].name} with ${constraints}`);
1091
+ logger8.default(`Create configured for ${models[0].name} with ${Object.keys(item).length} item fields`);
1092
+ const model = models[0];
1093
+ const modelAttributes = model.getAttributes();
1094
+ let itemData = { ...item };
1095
+ itemData = extractEvents(itemData);
1096
+ itemData = removeEvents(itemData);
1097
+ const invalidAttributes = [];
1098
+ for (const key of Object.keys(itemData)) {
1099
+ if (!modelAttributes[key]) {
1100
+ invalidAttributes.push(key);
1101
+ }
963
1102
  }
964
- }
965
- if (invalidAttributes.length > 0) {
966
- const availableAttributes = Object.keys(modelAttributes).join(", ");
967
- throw new Error(
968
- `Invalid attributes for model '${model.name}': [${invalidAttributes.join(", ")}]. Available attributes: [${availableAttributes}]. Item data: ${JSON.stringify(itemData, null, 2)}`
969
- );
970
- }
971
- if (options?.key) {
972
- const key = options.key;
973
- if (isPriKey2(key)) {
974
- itemData.id = key.pk;
975
- } else if (isComKey2(key)) {
976
- itemData.id = key.pk;
977
- const comKey = key;
1103
+ if (invalidAttributes.length > 0) {
1104
+ const availableAttributes = Object.keys(modelAttributes).join(", ");
1105
+ throw new Error(
1106
+ `Invalid attributes for model '${model.name}': [${invalidAttributes.join(", ")}]. Available attributes: [${availableAttributes}]. Item data: ${JSON.stringify(itemData, null, 2)}`
1107
+ );
1108
+ }
1109
+ if (options?.key) {
1110
+ const key = options.key;
1111
+ if (isPriKey2(key)) {
1112
+ itemData.id = key.pk;
1113
+ } else if (isComKey2(key)) {
1114
+ itemData.id = key.pk;
1115
+ const comKey = key;
1116
+ const directLocations = [];
1117
+ const hierarchicalLocations = [];
1118
+ for (const locKey of comKey.loc) {
1119
+ const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta, true);
1120
+ if (!relationshipInfo.found) {
1121
+ const associations = model.associations ? Object.keys(model.associations) : [];
1122
+ const errorMessage = `Composite key locator '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships. Available associations: [${associations.join(", ")}]. KTA: [${kta.join(", ")}]. Composite key: ${JSON.stringify(comKey, null, 2)}`;
1123
+ logger8.error(errorMessage, { key: comKey, kta, associations });
1124
+ throw new Error(errorMessage);
1125
+ }
1126
+ if (relationshipInfo.isDirect) {
1127
+ directLocations.push(locKey);
1128
+ } else {
1129
+ hierarchicalLocations.push(locKey);
1130
+ }
1131
+ }
1132
+ for (const locKey of directLocations) {
1133
+ if (locKey.lk == null || locKey.lk === "") {
1134
+ logger8.error(`Composite key location '${locKey.kt}' has undefined/null lk value`, { locKey, key: comKey });
1135
+ throw new Error(`Composite key location '${locKey.kt}' has undefined/null lk value`);
1136
+ }
1137
+ const foreignKeyField = locKey.kt + "Id";
1138
+ itemData[foreignKeyField] = locKey.lk;
1139
+ }
1140
+ for (const locKey of hierarchicalLocations) {
1141
+ await validateHierarchicalChain(models, locKey, kta);
1142
+ }
1143
+ }
1144
+ }
1145
+ if (options?.locations) {
978
1146
  const directLocations = [];
979
1147
  const hierarchicalLocations = [];
980
- for (const locKey of comKey.loc) {
1148
+ for (const locKey of options.locations) {
981
1149
  const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta, true);
982
1150
  if (!relationshipInfo.found) {
983
1151
  const associations = model.associations ? Object.keys(model.associations) : [];
984
- const errorMessage = `Composite key locator '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships. Available associations: [${associations.join(", ")}]. KTA: [${kta.join(", ")}]. Composite key: ${JSON.stringify(comKey, null, 2)}`;
985
- logger9.error(errorMessage, { key: comKey, kta, associations });
1152
+ const errorMessage = `Location key '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships. Available associations: [${associations.join(", ")}]. KTA: [${kta.join(", ")}]. Locations: ${JSON.stringify(options.locations, null, 2)}`;
1153
+ logger8.error(errorMessage, { locations: options.locations, kta, associations });
986
1154
  throw new Error(errorMessage);
987
1155
  }
988
1156
  if (relationshipInfo.isDirect) {
@@ -993,8 +1161,8 @@ var getCreateOperation = (models, definition, registry) => {
993
1161
  }
994
1162
  for (const locKey of directLocations) {
995
1163
  if (locKey.lk == null || locKey.lk === "") {
996
- logger9.error(`Composite key location '${locKey.kt}' has undefined/null lk value`, { locKey, key: comKey });
997
- throw new Error(`Composite key location '${locKey.kt}' has undefined/null lk value`);
1164
+ logger8.error(`Location option '${locKey.kt}' has undefined/null lk value`, { locKey, locations: options.locations });
1165
+ throw new Error(`Location option '${locKey.kt}' has undefined/null lk value`);
998
1166
  }
999
1167
  const foreignKeyField = locKey.kt + "Id";
1000
1168
  itemData[foreignKeyField] = locKey.lk;
@@ -1003,99 +1171,78 @@ var getCreateOperation = (models, definition, registry) => {
1003
1171
  await validateHierarchicalChain(models, locKey, kta);
1004
1172
  }
1005
1173
  }
1006
- }
1007
- if (options?.locations) {
1008
- const directLocations = [];
1009
- const hierarchicalLocations = [];
1010
- for (const locKey of options.locations) {
1011
- const relationshipInfo = buildRelationshipPath(model, locKey.kt, kta, true);
1012
- if (!relationshipInfo.found) {
1013
- const associations = model.associations ? Object.keys(model.associations) : [];
1014
- const errorMessage = `Location key '${locKey.kt}' cannot be resolved on model '${model.name}' or through its relationships. Available associations: [${associations.join(", ")}]. KTA: [${kta.join(", ")}]. Locations: ${JSON.stringify(options.locations, null, 2)}`;
1015
- logger9.error(errorMessage, { locations: options.locations, kta, associations });
1016
- throw new Error(errorMessage);
1017
- }
1018
- if (relationshipInfo.isDirect) {
1019
- directLocations.push(locKey);
1020
- } else {
1021
- hierarchicalLocations.push(locKey);
1022
- }
1023
- }
1024
- for (const locKey of directLocations) {
1025
- if (locKey.lk == null || locKey.lk === "") {
1026
- logger9.error(`Location option '${locKey.kt}' has undefined/null lk value`, { locKey, locations: options.locations });
1027
- throw new Error(`Location option '${locKey.kt}' has undefined/null lk value`);
1028
- }
1029
- const foreignKeyField = locKey.kt + "Id";
1030
- itemData[foreignKeyField] = locKey.lk;
1031
- }
1032
- for (const locKey of hierarchicalLocations) {
1033
- await validateHierarchicalChain(models, locKey, kta);
1174
+ try {
1175
+ logger8.trace(`[CREATE] Executing ${model.name}.create() with data: ${stringifyJSON(itemData)}`);
1176
+ const createdRecord = await model.create(itemData);
1177
+ const processedRecord = await processRow(createdRecord, kta, references || [], aggregations || [], registry);
1178
+ const result = validateKeys2(processedRecord, kta);
1179
+ logger8.debug(`[CREATE] Created ${model.name} with key: ${result.key ? JSON.stringify(result.key) : `id=${createdRecord.id}`}`);
1180
+ return result;
1181
+ } catch (error) {
1182
+ throw transformSequelizeError(error, kta[0], options?.key, model.name, itemData);
1034
1183
  }
1035
1184
  }
1036
- try {
1037
- logger9.trace(`[CREATE] Executing ${model.name}.create() with data: ${stringifyJSON(itemData)}`);
1038
- const createdRecord = await model.create(itemData);
1039
- const processedRecord = await processRow(createdRecord, kta, references || [], aggregations || [], registry);
1040
- const result = validateKeys2(processedRecord, kta);
1041
- logger9.debug(`[CREATE] Created ${model.name} with key: ${result.key ? JSON.stringify(result.key) : `id=${createdRecord.id}`}`);
1042
- return result;
1043
- } catch (error) {
1044
- throw translateDatabaseError(error, itemData, model.name);
1045
- }
1046
- };
1047
- return create;
1185
+ );
1048
1186
  };
1049
1187
 
1050
1188
  // src/ops/find.ts
1051
- import { validateKeys as validateKeys3 } from "@fjell/core";
1052
- var logger10 = logger_default.get("sequelize", "ops", "find");
1189
+ import { createFindWrapper } from "@fjell/core";
1190
+ import { validateKeys as validateKeys3 } from "@fjell/core/validation";
1191
+ var logger9 = logger_default.get("sequelize", "ops", "find");
1053
1192
  var getFindOperation = (models, definition, registry) => {
1054
1193
  const { options: { finders, references, aggregations } } = definition;
1055
- const find = async (finder, finderParams, locations) => {
1056
- const locationFilters = locations?.map((loc) => `${loc.kt}=${loc.lk}`).join(", ") || "none";
1057
- logger10.debug(
1058
- `FIND operation called on ${models[0].name} with finder '${finder}' and ${locations?.length || 0} location filters: ${locationFilters}`
1059
- );
1060
- logger10.default(`Find configured for ${models[0].name} using finder '${finder}' with ${Object.keys(finderParams).length} params`);
1061
- validateLocations(locations, definition.coordinate, "find");
1062
- if (finders && finders[finder]) {
1063
- const finderMethod = finders[finder];
1064
- if (finderMethod) {
1065
- logger10.trace(`[FIND] Executing finder '${finder}' on ${models[0].name} with params: ${stringifyJSON(finderParams)}, locations: ${stringifyJSON(locations)}`);
1066
- const results = await finderMethod(finderParams, locations);
1067
- if (results && results.length > 0) {
1068
- const processedResults = await Promise.all(results.map(async (row) => {
1069
- const processedRow = await processRow(row, definition.coordinate.kta, references || [], aggregations || [], registry);
1070
- return validateKeys3(processedRow, definition.coordinate.kta);
1071
- }));
1072
- logger10.debug(`[FIND] Found ${processedResults.length} ${models[0].name} records using finder '${finder}'`);
1073
- return processedResults;
1194
+ return createFindWrapper(
1195
+ definition.coordinate,
1196
+ async (finder, finderParams, locations) => {
1197
+ try {
1198
+ const locs = locations ?? [];
1199
+ const params = finderParams ?? {};
1200
+ const locationFilters = locs.map((loc) => `${loc.kt}=${loc.lk}`).join(", ") || "none";
1201
+ logger9.debug(
1202
+ `FIND operation called on ${models[0].name} with finder '${finder}' and ${locs.length} location filters: ${locationFilters}`
1203
+ );
1204
+ logger9.default(`Find configured for ${models[0].name} using finder '${finder}' with ${Object.keys(params).length} params`);
1205
+ if (finders && finders[finder]) {
1206
+ const finderMethod = finders[finder];
1207
+ if (finderMethod) {
1208
+ logger9.trace(`[FIND] Executing finder '${finder}' on ${models[0].name} with params: ${stringifyJSON(params)}, locations: ${stringifyJSON(locs)}`);
1209
+ const results = await finderMethod(params, locs);
1210
+ if (results && results.length > 0) {
1211
+ const processedResults = await Promise.all(results.map(async (row) => {
1212
+ const processedRow = await processRow(row, definition.coordinate.kta, references || [], aggregations || [], registry);
1213
+ return validateKeys3(processedRow, definition.coordinate.kta);
1214
+ }));
1215
+ logger9.debug(`[FIND] Found ${processedResults.length} ${models[0].name} records using finder '${finder}'`);
1216
+ return processedResults;
1217
+ } else {
1218
+ logger9.debug(`[FIND] Found 0 ${models[0].name} records using finder '${finder}'`);
1219
+ return [];
1220
+ }
1221
+ } else {
1222
+ logger9.error(`Finder %s not found`, finder);
1223
+ throw new Error(`Finder ${finder} not found`);
1224
+ }
1074
1225
  } else {
1075
- logger10.debug(`[FIND] Found 0 ${models[0].name} records using finder '${finder}'`);
1076
- return [];
1226
+ logger9.error(`No finders have been defined for this lib`);
1227
+ throw new Error(`No finders found`);
1077
1228
  }
1078
- } else {
1079
- logger10.error(`Finder %s not found`, finder);
1080
- throw new Error(`Finder ${finder} not found`);
1229
+ } catch (error) {
1230
+ throw transformSequelizeError(error, definition.coordinate.kta[0]);
1081
1231
  }
1082
- } else {
1083
- logger10.error(`No finders have been defined for this lib`);
1084
- throw new Error(`No finders found`);
1085
1232
  }
1086
- };
1087
- return find;
1233
+ );
1088
1234
  };
1089
1235
 
1090
1236
  // src/ops/get.ts
1091
1237
  import {
1238
+ createGetWrapper,
1092
1239
  isComKey as isComKey3,
1093
1240
  isPriKey as isPriKey3,
1094
1241
  isValidItemKey,
1095
1242
  validateKeys as validateKeys4
1096
1243
  } from "@fjell/core";
1097
- import { NotFoundError } from "@fjell/lib";
1098
- var logger11 = logger_default.get("sequelize", "ops", "get");
1244
+ import { NotFoundError } from "@fjell/core";
1245
+ var logger10 = logger_default.get("sequelize", "ops", "get");
1099
1246
  var processCompositeKey = (comKey, model, kta) => {
1100
1247
  const where = { id: comKey.pk };
1101
1248
  const includes = [];
@@ -1103,7 +1250,7 @@ var processCompositeKey = (comKey, model, kta) => {
1103
1250
  const relationshipInfo = buildRelationshipPath(model, locator.kt, kta);
1104
1251
  if (!relationshipInfo.found) {
1105
1252
  const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships. Key type array: [${kta.join(", ")}], Composite key: ${stringifyJSON(comKey)}, Available associations: [${Object.keys(model.associations || {}).join(", ")}]`;
1106
- logger11.error(errorMessage, { key: comKey, kta });
1253
+ logger10.error(errorMessage, { key: comKey, kta });
1107
1254
  throw new Error(errorMessage);
1108
1255
  }
1109
1256
  if (relationshipInfo.path) {
@@ -1125,64 +1272,82 @@ var processCompositeKey = (comKey, model, kta) => {
1125
1272
  var getGetOperation = (models, definition, registry) => {
1126
1273
  const { coordinate, options: { references, aggregations } } = definition;
1127
1274
  const { kta } = coordinate;
1128
- const get = async (key) => {
1129
- if (!isValidItemKey(key)) {
1130
- logger11.error("Key for Get is not a valid ItemKey: %j", key);
1131
- throw new Error("Key for Get is not a valid ItemKey");
1132
- }
1133
- const keyDescription = isPriKey3(key) ? `primary key: pk=${key.pk}` : `composite key: pk=${key.pk}, loc=[${key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ")}]`;
1134
- logger11.debug(`GET operation called on ${models[0].name} with ${keyDescription}`);
1135
- logger11.default(`Get configured for ${models[0].name} with ${isPriKey3(key) ? "primary" : "composite"} key`);
1136
- const itemKey = key;
1137
- const model = models[0];
1138
- let item;
1139
- if (isPriKey3(itemKey)) {
1140
- logger11.trace(`[GET] Executing ${model.name}.findByPk() with pk: ${itemKey.pk}`);
1141
- item = await model.findByPk(itemKey.pk);
1142
- } else if (isComKey3(itemKey)) {
1143
- const comKey = itemKey;
1144
- const queryOptions = processCompositeKey(comKey, model, kta);
1145
- logger11.default("Composite key query", { queryOptions });
1146
- logger11.trace(`[GET] Executing ${model.name}.findOne() with options: ${stringifyJSON(queryOptions)}`);
1147
- item = await model.findOne(queryOptions);
1148
- }
1149
- if (!item) {
1150
- throw new NotFoundError("get", coordinate, key);
1151
- } else {
1152
- const currentContext = contextManager.getCurrentContext();
1153
- const result = validateKeys4(await processRow(item, kta, references || [], aggregations || [], registry, currentContext), kta);
1154
- logger11.debug(`[GET] Retrieved ${model.name} with key: ${result.key ? JSON.stringify(result.key) : `id=${item.id}`}`);
1155
- return result;
1275
+ return createGetWrapper(
1276
+ coordinate,
1277
+ async (key) => {
1278
+ try {
1279
+ if (!isValidItemKey(key)) {
1280
+ logger10.error("Key for Get is not a valid ItemKey: %j", key);
1281
+ throw new Error("Key for Get is not a valid ItemKey");
1282
+ }
1283
+ const keyDescription = isPriKey3(key) ? `primary key: pk=${key.pk}` : `composite key: pk=${key.pk}, loc=[${key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ")}]`;
1284
+ logger10.debug(`GET operation called on ${models[0].name} with ${keyDescription}`);
1285
+ logger10.default(`Get configured for ${models[0].name} with ${isPriKey3(key) ? "primary" : "composite"} key`);
1286
+ const itemKey = key;
1287
+ const model = models[0];
1288
+ let item;
1289
+ if (isPriKey3(itemKey)) {
1290
+ logger10.trace(`[GET] Executing ${model.name}.findByPk() with pk: ${itemKey.pk}`);
1291
+ item = await model.findByPk(itemKey.pk);
1292
+ } else if (isComKey3(itemKey)) {
1293
+ const comKey = itemKey;
1294
+ if (comKey.loc.length === 0) {
1295
+ logger10.debug(`[GET] Empty loc array detected - finding by primary key across all locations: ${comKey.pk}`);
1296
+ logger10.trace(`[GET] Executing ${model.name}.findByPk() with pk: ${comKey.pk}`);
1297
+ item = await model.findByPk(comKey.pk);
1298
+ } else {
1299
+ const queryOptions = processCompositeKey(comKey, model, kta);
1300
+ logger10.default("Composite key query", { queryOptions });
1301
+ logger10.trace(`[GET] Executing ${model.name}.findOne() with options: ${stringifyJSON(queryOptions)}`);
1302
+ item = await model.findOne(queryOptions);
1303
+ }
1304
+ }
1305
+ if (!item) {
1306
+ throw new NotFoundError(
1307
+ `${kta[0]} not found`,
1308
+ kta[0],
1309
+ key
1310
+ );
1311
+ }
1312
+ const currentContext = contextManager.getCurrentContext();
1313
+ const result = validateKeys4(await processRow(item, kta, references || [], aggregations || [], registry, currentContext), kta);
1314
+ logger10.debug(`[GET] Retrieved ${model.name} with key: ${result.key ? JSON.stringify(result.key) : `id=${item.id}`}`);
1315
+ return result;
1316
+ } catch (error) {
1317
+ if (error instanceof NotFoundError) throw error;
1318
+ throw transformSequelizeError(error, kta[0], key);
1319
+ }
1156
1320
  }
1157
- };
1158
- return get;
1321
+ );
1159
1322
  };
1160
1323
 
1161
1324
  // src/ops/one.ts
1162
- var logger12 = logger_default.get("sequelize", "ops", "one");
1325
+ import { createOneWrapper } from "@fjell/core";
1326
+ var logger11 = logger_default.get("sequelize", "ops", "one");
1163
1327
  var getOneOperation = (models, definition, registry) => {
1164
- const one = async (itemQuery, locations = []) => {
1165
- logger12.debug(`ONE operation called on ${models[0].name} with ${locations.length} location filters: ${locations.map((loc) => `${loc.kt}=${loc.lk}`).join(", ") || "none"}`);
1166
- logger12.default(`One configured for ${models[0].name} delegating to all operation`);
1167
- validateLocations(locations, definition.coordinate, "one");
1168
- const items = await getAllOperation(models, definition, registry)(itemQuery, locations);
1169
- if (items.length > 0) {
1170
- const result = items[0];
1171
- logger12.debug(`[ONE] Found ${models[0].name} record with key: ${result.key ? JSON.stringify(result.key) : "unknown"}`);
1172
- return result;
1173
- } else {
1174
- logger12.debug(`[ONE] No ${models[0].name} record found`);
1175
- return null;
1328
+ return createOneWrapper(
1329
+ definition.coordinate,
1330
+ async (itemQuery, locations) => {
1331
+ const locs = locations ?? [];
1332
+ logger11.debug(`ONE operation called on ${models[0].name} with ${locs.length} location filters: ${locs.map((loc) => `${loc.kt}=${loc.lk}`).join(", ") || "none"}`);
1333
+ logger11.default(`One configured for ${models[0].name} delegating to all operation`);
1334
+ const items = await getAllOperation(models, definition, registry)(itemQuery ?? {}, locs);
1335
+ if (items.length > 0) {
1336
+ const result = items[0];
1337
+ logger11.debug(`[ONE] Found ${models[0].name} record with key: ${result.key ? JSON.stringify(result.key) : "unknown"}`);
1338
+ return result;
1339
+ } else {
1340
+ logger11.debug(`[ONE] No ${models[0].name} record found`);
1341
+ return null;
1342
+ }
1176
1343
  }
1177
- };
1178
- return one;
1344
+ );
1179
1345
  };
1180
1346
 
1181
1347
  // src/ops/remove.ts
1182
- import { isValidItemKey as isValidItemKey2 } from "@fjell/core";
1183
- import { abbrevIK, isComKey as isComKey4, isPriKey as isPriKey4 } from "@fjell/core";
1184
- import { NotFoundError as NotFoundError2 } from "@fjell/lib";
1185
- var logger13 = logger_default.get("sequelize", "ops", "remove");
1348
+ import { abbrevIK, isComKey as isComKey4, isPriKey as isPriKey4, isValidItemKey as isValidItemKey2, createRemoveWrapper } from "@fjell/core";
1349
+ import { NotFoundError as NotFoundError2 } from "@fjell/core";
1350
+ var logger12 = logger_default.get("sequelize", "ops", "remove");
1186
1351
  var processCompositeKey2 = (comKey, model, kta) => {
1187
1352
  const where = { id: comKey.pk };
1188
1353
  const includes = [];
@@ -1190,7 +1355,7 @@ var processCompositeKey2 = (comKey, model, kta) => {
1190
1355
  const relationshipInfo = buildRelationshipPath(model, locator.kt, kta);
1191
1356
  if (!relationshipInfo.found) {
1192
1357
  const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
1193
- logger13.error(errorMessage, { key: comKey, kta });
1358
+ logger12.error(errorMessage, { key: comKey, kta });
1194
1359
  throw new Error(errorMessage);
1195
1360
  }
1196
1361
  if (relationshipInfo.path) {
@@ -1212,66 +1377,77 @@ var processCompositeKey2 = (comKey, model, kta) => {
1212
1377
  var getRemoveOperation = (models, definition, _registry) => {
1213
1378
  const { coordinate, options } = definition;
1214
1379
  const { kta } = coordinate;
1215
- const remove = async (key) => {
1216
- if (!isValidItemKey2(key)) {
1217
- logger13.error("Key for Remove is not a valid ItemKey: %j", key);
1218
- throw new Error("Key for Remove is not a valid ItemKey");
1219
- }
1220
- const keyDescription = isPriKey4(key) ? `primary key: pk=${key.pk}` : `composite key: pk=${key.pk}, loc=[${key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ")}]`;
1221
- logger13.debug(`REMOVE operation called on ${models[0].name} with ${keyDescription}`);
1222
- logger13.default(`Remove configured for ${models[0].name} with ${isPriKey4(key) ? "primary" : "composite"} key`);
1223
- const model = models[0];
1224
- let item;
1225
- let returnItem;
1226
- logger13.debug("remove: %s", abbrevIK(key));
1227
- if (isPriKey4(key)) {
1228
- logger13.debug(`[REMOVE] Executing ${model.name}.findByPk() with pk: ${key.pk}`);
1229
- item = await model.findByPk(key.pk);
1230
- } else if (isComKey4(key)) {
1231
- const comKey = key;
1232
- const queryOptions = processCompositeKey2(comKey, model, kta);
1233
- logger13.default(`Remove composite key query for ${model.name} with where fields: ${queryOptions.where ? Object.keys(queryOptions.where).join(", ") : "none"}`);
1234
- logger13.debug(`[REMOVE] Executing ${model.name}.findOne() with options: ${stringifyJSON(queryOptions)}`);
1235
- item = await model.findOne(queryOptions);
1236
- }
1237
- if (!item) {
1238
- throw new NotFoundError2("remove", coordinate, key);
1239
- }
1240
- const isDeletedAttribute = model.getAttributes().isDeleted;
1241
- const deletedAtAttribute = model.getAttributes().deletedAt;
1242
- if (isDeletedAttribute || deletedAtAttribute) {
1243
- if (model.getAttributes().isDeleted) {
1244
- item.isDeleted = true;
1245
- }
1246
- if (model.getAttributes().deletedAt) {
1247
- item.deletedAt = /* @__PURE__ */ new Date();
1380
+ return createRemoveWrapper(
1381
+ coordinate,
1382
+ async (key) => {
1383
+ try {
1384
+ if (!isValidItemKey2(key)) {
1385
+ logger12.error("Key for Remove is not a valid ItemKey: %j", key);
1386
+ throw new Error("Key for Remove is not a valid ItemKey");
1387
+ }
1388
+ const keyDescription = isPriKey4(key) ? `primary key: pk=${key.pk}` : `composite key: pk=${key.pk}, loc=[${key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ")}]`;
1389
+ logger12.debug(`REMOVE operation called on ${models[0].name} with ${keyDescription}`);
1390
+ logger12.default(`Remove configured for ${models[0].name} with ${isPriKey4(key) ? "primary" : "composite"} key`);
1391
+ const model = models[0];
1392
+ let item;
1393
+ let returnItem;
1394
+ logger12.debug("remove: %s", abbrevIK(key));
1395
+ if (isPriKey4(key)) {
1396
+ logger12.debug(`[REMOVE] Executing ${model.name}.findByPk() with pk: ${key.pk}`);
1397
+ item = await model.findByPk(key.pk);
1398
+ } else if (isComKey4(key)) {
1399
+ const comKey = key;
1400
+ const queryOptions = processCompositeKey2(comKey, model, kta);
1401
+ logger12.default(`Remove composite key query for ${model.name} with where fields: ${queryOptions.where ? Object.keys(queryOptions.where).join(", ") : "none"}`);
1402
+ logger12.debug(`[REMOVE] Executing ${model.name}.findOne() with options: ${stringifyJSON(queryOptions)}`);
1403
+ item = await model.findOne(queryOptions);
1404
+ }
1405
+ if (!item) {
1406
+ throw new NotFoundError2(
1407
+ `Cannot remove: ${kta[0]} not found`,
1408
+ kta[0],
1409
+ key
1410
+ );
1411
+ }
1412
+ const isDeletedAttribute = model.getAttributes().isDeleted;
1413
+ const deletedAtAttribute = model.getAttributes().deletedAt;
1414
+ if (isDeletedAttribute || deletedAtAttribute) {
1415
+ if (model.getAttributes().isDeleted) {
1416
+ item.isDeleted = true;
1417
+ }
1418
+ if (model.getAttributes().deletedAt) {
1419
+ item.deletedAt = /* @__PURE__ */ new Date();
1420
+ }
1421
+ logger12.debug(`[REMOVE] Executing ${model.name}.save() for soft delete`);
1422
+ await item?.save();
1423
+ returnItem = item?.get({ plain: true });
1424
+ returnItem = addKey(item, returnItem, kta);
1425
+ returnItem = populateEvents(returnItem);
1426
+ } else if (options.deleteOnRemove) {
1427
+ logger12.debug(`[REMOVE] Executing ${model.name}.destroy() for hard delete`);
1428
+ await item?.destroy();
1429
+ returnItem = item?.get({ plain: true });
1430
+ returnItem = addKey(item, returnItem, kta);
1431
+ returnItem = populateEvents(returnItem);
1432
+ } else {
1433
+ throw new Error("No deletedAt or isDeleted attribute found in model, and deleteOnRemove is not set");
1434
+ }
1435
+ logger12.debug(`[REMOVE] Removed ${model.name} with key: ${returnItem.key ? JSON.stringify(returnItem.key) : `id=${item.id}`}`);
1436
+ return returnItem;
1437
+ } catch (error) {
1438
+ if (error instanceof NotFoundError2) throw error;
1439
+ throw transformSequelizeError(error, kta[0], key);
1248
1440
  }
1249
- logger13.debug(`[REMOVE] Executing ${model.name}.save() for soft delete`);
1250
- await item?.save();
1251
- returnItem = item?.get({ plain: true });
1252
- returnItem = addKey(item, returnItem, kta);
1253
- returnItem = populateEvents(returnItem);
1254
- } else if (options.deleteOnRemove) {
1255
- logger13.debug(`[REMOVE] Executing ${model.name}.destroy() for hard delete`);
1256
- await item?.destroy();
1257
- returnItem = item?.get({ plain: true });
1258
- returnItem = addKey(item, returnItem, kta);
1259
- returnItem = populateEvents(returnItem);
1260
- } else {
1261
- throw new Error("No deletedAt or isDeleted attribute found in model, and deleteOnRemove is not set");
1262
1441
  }
1263
- logger13.debug(`[REMOVE] Removed ${model.name} with key: ${returnItem.key ? JSON.stringify(returnItem.key) : `id=${item.id}`}`);
1264
- return returnItem;
1265
- };
1266
- return remove;
1442
+ );
1267
1443
  };
1268
1444
 
1269
1445
  // src/ops/update.ts
1270
- import { abbrevIK as abbrevIK2, isComKey as isComKey5, validateKeys as validateKeys5 } from "@fjell/core";
1271
- import { isPriKey as isPriKey5 } from "@fjell/core";
1272
- import { NotFoundError as NotFoundError3 } from "@fjell/lib";
1446
+ import { abbrevIK as abbrevIK2, createUpdateWrapper, isComKey as isComKey5, isPriKey as isPriKey5 } from "@fjell/core";
1447
+ import { validateKeys as validateKeys5 } from "@fjell/core/validation";
1448
+ import { NotFoundError as NotFoundError3 } from "@fjell/core";
1273
1449
  import { Op as Op3 } from "sequelize";
1274
- var logger14 = logger_default.get("sequelize", "ops", "update");
1450
+ var logger13 = logger_default.get("sequelize", "ops", "update");
1275
1451
  var mergeIncludes2 = (existingIncludes, newIncludes) => {
1276
1452
  const mergedIncludes = [...existingIncludes];
1277
1453
  for (const newInclude of newIncludes) {
@@ -1293,130 +1469,136 @@ var mergeIncludes2 = (existingIncludes, newIncludes) => {
1293
1469
  };
1294
1470
  var getUpdateOperation = (models, definition, registry) => {
1295
1471
  const { options: { references, aggregations } } = definition;
1296
- const update = async (key, item) => {
1297
- const keyDescription = isPriKey5(key) ? `primary key: pk=${key.pk}` : `composite key: pk=${key.pk}, loc=[${key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ")}]`;
1298
- logger14.debug(`UPDATE operation called on ${models[0].name} with ${keyDescription}`);
1299
- const { coordinate } = definition;
1300
- const { kta } = coordinate;
1301
- logger14.debug("update: %s, %j", abbrevIK2(key), item);
1302
- const model = models[0];
1303
- let response;
1304
- if (isPriKey5(key)) {
1305
- const priKey = key;
1306
- logger14.trace(`[UPDATE] Executing ${model.name}.findByPk() with pk: ${priKey.pk}`);
1307
- response = await model.findByPk(priKey.pk);
1308
- } else if (isComKey5(key)) {
1309
- const comKey = key;
1310
- const where = { id: comKey.pk };
1311
- const additionalIncludes = [];
1312
- for (const locator of comKey.loc) {
1313
- const relationshipInfo = buildRelationshipPath(model, locator.kt, kta, true);
1314
- if (!relationshipInfo.found) {
1315
- const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
1316
- logger14.error(errorMessage, { key: comKey, kta });
1317
- throw new Error(errorMessage);
1318
- }
1319
- if (relationshipInfo.isDirect) {
1320
- const fieldName = `${locator.kt}Id`;
1321
- where[fieldName] = locator.lk;
1322
- } else if (relationshipInfo.path) {
1323
- where[relationshipInfo.path] = {
1324
- [Op3.eq]: locator.lk
1325
- };
1326
- if (relationshipInfo.includes) {
1327
- additionalIncludes.push(...relationshipInfo.includes);
1472
+ return createUpdateWrapper(
1473
+ definition.coordinate,
1474
+ async (key, item) => {
1475
+ try {
1476
+ const keyDescription = isPriKey5(key) ? `primary key: pk=${key.pk}` : `composite key: pk=${key.pk}, loc=[${key.loc.map((l) => `${l.kt}=${l.lk}`).join(", ")}]`;
1477
+ logger13.debug(`UPDATE operation called on ${models[0].name} with ${keyDescription}`);
1478
+ const { coordinate } = definition;
1479
+ const { kta } = coordinate;
1480
+ logger13.debug("update: %s, %j", abbrevIK2(key), item);
1481
+ const model = models[0];
1482
+ let response;
1483
+ if (isPriKey5(key)) {
1484
+ const priKey = key;
1485
+ logger13.trace(`[UPDATE] Executing ${model.name}.findByPk() with pk: ${priKey.pk}`);
1486
+ response = await model.findByPk(priKey.pk);
1487
+ } else if (isComKey5(key)) {
1488
+ const comKey = key;
1489
+ const where = { id: comKey.pk };
1490
+ const additionalIncludes = [];
1491
+ for (const locator of comKey.loc) {
1492
+ const relationshipInfo = buildRelationshipPath(model, locator.kt, kta, true);
1493
+ if (!relationshipInfo.found) {
1494
+ const errorMessage = `Composite key locator '${locator.kt}' cannot be resolved on model '${model.name}' or through its relationships.`;
1495
+ logger13.error(errorMessage, { key: comKey, kta });
1496
+ throw new Error(errorMessage);
1497
+ }
1498
+ if (relationshipInfo.isDirect) {
1499
+ const fieldName = `${locator.kt}Id`;
1500
+ where[fieldName] = locator.lk;
1501
+ } else if (relationshipInfo.path) {
1502
+ where[relationshipInfo.path] = {
1503
+ [Op3.eq]: locator.lk
1504
+ };
1505
+ if (relationshipInfo.includes) {
1506
+ additionalIncludes.push(...relationshipInfo.includes);
1507
+ }
1508
+ }
1328
1509
  }
1510
+ const queryOptions = { where };
1511
+ if (additionalIncludes.length > 0) {
1512
+ queryOptions.include = mergeIncludes2([], additionalIncludes);
1513
+ }
1514
+ logger13.default(`Update composite key query for ${model.name} with where fields: ${queryOptions.where ? Object.keys(queryOptions.where).join(", ") : "none"}`);
1515
+ logger13.trace(`[UPDATE] Executing ${model.name}.findOne() with options: ${stringifyJSON(queryOptions)}`);
1516
+ response = await model.findOne(queryOptions);
1329
1517
  }
1518
+ if (!response) {
1519
+ throw new NotFoundError3(
1520
+ `Cannot update: ${kta[0]} not found`,
1521
+ kta[0],
1522
+ key
1523
+ );
1524
+ }
1525
+ let updateProps = removeKey(item);
1526
+ updateProps = extractEvents(updateProps);
1527
+ updateProps = removeEvents(updateProps);
1528
+ logger13.default(`Update found ${model.name} record to modify`);
1529
+ logger13.default(`Update properties configured: ${Object.keys(updateProps).join(", ")}`);
1530
+ logger13.trace(`[UPDATE] Executing ${model.name}.update() with properties: ${stringifyJSON(updateProps)}`);
1531
+ response = await response.update(updateProps);
1532
+ const processedItem = await processRow(response, kta, references || [], aggregations || [], registry);
1533
+ const returnItem = validateKeys5(processedItem, kta);
1534
+ logger13.debug(`[UPDATE] Updated ${model.name} with key: ${returnItem.key ? JSON.stringify(returnItem.key) : `id=${response.id}`}`);
1535
+ return returnItem;
1536
+ } catch (error) {
1537
+ if (error instanceof NotFoundError3) throw error;
1538
+ throw transformSequelizeError(error, definition.coordinate.kta[0], key);
1330
1539
  }
1331
- const queryOptions = { where };
1332
- if (additionalIncludes.length > 0) {
1333
- queryOptions.include = mergeIncludes2([], additionalIncludes);
1334
- }
1335
- logger14.default(`Update composite key query for ${model.name} with where fields: ${queryOptions.where ? Object.keys(queryOptions.where).join(", ") : "none"}`);
1336
- logger14.trace(`[UPDATE] Executing ${model.name}.findOne() with options: ${stringifyJSON(queryOptions)}`);
1337
- response = await model.findOne(queryOptions);
1338
- }
1339
- if (response) {
1340
- let updateProps = removeKey(item);
1341
- updateProps = extractEvents(updateProps);
1342
- updateProps = removeEvents(updateProps);
1343
- logger14.default(`Update found ${model.name} record to modify`);
1344
- logger14.default(`Update properties configured: ${Object.keys(updateProps).join(", ")}`);
1345
- logger14.trace(`[UPDATE] Executing ${model.name}.update() with properties: ${stringifyJSON(updateProps)}`);
1346
- response = await response.update(updateProps);
1347
- const processedItem = await processRow(response, kta, references || [], aggregations || [], registry);
1348
- const returnItem = validateKeys5(processedItem, kta);
1349
- logger14.debug(`[UPDATE] Updated ${model.name} with key: ${returnItem.key ? JSON.stringify(returnItem.key) : `id=${response.id}`}`);
1350
- return returnItem;
1351
- } else {
1352
- throw new NotFoundError3("update", coordinate, key);
1353
1540
  }
1354
- };
1355
- return update;
1541
+ );
1356
1542
  };
1357
1543
 
1358
1544
  // src/ops/upsert.ts
1359
- import { isValidItemKey as isValidItemKey3 } from "@fjell/core";
1360
- var logger15 = logger_default.get("sequelize", "ops", "upsert");
1545
+ import { createUpsertWrapper, isValidItemKey as isValidItemKey3 } from "@fjell/core";
1546
+ var logger14 = logger_default.get("sequelize", "ops", "upsert");
1361
1547
  var getUpsertOperation = (models, definition, registry) => {
1362
1548
  const get = getGetOperation(models, definition, registry);
1363
1549
  const update = getUpdateOperation(models, definition, registry);
1364
1550
  const create = getCreateOperation(models, definition, registry);
1365
- const upsert = async (key, item) => {
1366
- if (!isValidItemKey3(key)) {
1367
- logger15.error("Key for Upsert is not a valid ItemKey: %j", key);
1368
- throw new Error(`Key for Upsert is not a valid ItemKey: ${stringifyJSON(key)}`);
1369
- }
1370
- logger15.debug(`[UPSERT] Attempting upsert with key: ${stringifyJSON(key)}`);
1371
- try {
1372
- const existingItem = await get(key);
1373
- if (existingItem) {
1374
- logger15.debug(`[UPSERT] Item exists, updating with key: ${stringifyJSON(key)}`);
1375
- return await update(key, item);
1551
+ return createUpsertWrapper(
1552
+ definition.coordinate,
1553
+ async (key, item) => {
1554
+ if (!isValidItemKey3(key)) {
1555
+ logger14.error("Key for Upsert is not a valid ItemKey: %j", key);
1556
+ throw new Error(`Key for Upsert is not a valid ItemKey: ${stringifyJSON(key)}`);
1376
1557
  }
1377
- } catch {
1378
- logger15.debug(`[UPSERT] Item not found, creating new item with key: ${stringifyJSON(key)}`);
1558
+ logger14.debug(`[UPSERT] Attempting upsert with key: ${stringifyJSON(key)}`);
1559
+ try {
1560
+ const existingItem = await get(key);
1561
+ if (existingItem) {
1562
+ logger14.debug(`[UPSERT] Item exists, updating with key: ${stringifyJSON(key)}`);
1563
+ return await update(key, item);
1564
+ }
1565
+ } catch {
1566
+ logger14.debug(`[UPSERT] Item not found, creating new item with key: ${stringifyJSON(key)}`);
1567
+ }
1568
+ return await create(item, { key });
1379
1569
  }
1380
- return await create(item, { key });
1381
- };
1382
- return upsert;
1570
+ );
1383
1571
  };
1384
1572
 
1385
1573
  // src/Operations.ts
1386
1574
  var createOperations = (models, coordinate, registry, options) => {
1387
- const operations = {};
1388
1575
  const definition = { coordinate, options };
1389
- operations.all = getAllOperation(models, definition, registry);
1390
- operations.one = getOneOperation(models, definition, registry);
1391
- operations.create = getCreateOperation(models, definition, registry);
1392
- operations.update = getUpdateOperation(models, definition, registry);
1393
- operations.get = getGetOperation(models, definition, registry);
1394
- operations.remove = getRemoveOperation(models, definition, registry);
1395
- operations.find = getFindOperation(models, definition, registry);
1396
- operations.upsert = getUpsertOperation(models, definition, registry);
1397
- operations.allFacet = async () => {
1398
- };
1399
- operations.allAction = async () => {
1400
- };
1401
- operations.action = async () => {
1576
+ const implOps = {
1577
+ all: getAllOperation(models, definition, registry),
1578
+ one: getOneOperation(models, definition, registry),
1579
+ create: getCreateOperation(models, definition, registry),
1580
+ update: getUpdateOperation(models, definition, registry),
1581
+ get: getGetOperation(models, definition, registry),
1582
+ remove: getRemoveOperation(models, definition, registry),
1583
+ find: getFindOperation(models, definition, registry),
1584
+ // findOne depends on find, so set it after
1585
+ findOne: null,
1586
+ upsert: getUpsertOperation(models, definition, registry)
1402
1587
  };
1403
- operations.facet = async () => {
1588
+ implOps.findOne = async (finder, params, locations) => {
1589
+ const results = await implOps.find(finder, params || {}, locations);
1590
+ return results.length > 0 ? results[0] : null;
1404
1591
  };
1405
- operations.finders = { ...options.finders || {} };
1406
- operations.actions = { ...options.actions || {} };
1407
- operations.facets = { ...options.facets || {} };
1408
- operations.allActions = { ...options.allActions || {} };
1409
- operations.allFacets = { ...options.allFacets || {} };
1410
- return operations;
1592
+ return Library2.wrapImplementationOperations(implOps, options);
1411
1593
  };
1412
1594
 
1413
1595
  // src/SequelizeLibrary.ts
1414
- var logger16 = logger_default.get("SequelizeLibrary");
1596
+ var logger15 = logger_default.get("SequelizeLibrary");
1415
1597
  var createSequelizeLibrary = (registry, coordinate, models, options) => {
1416
- logger16.debug("createSequelizeLibrary", { coordinate, models, registry, options });
1598
+ logger15.debug("createSequelizeLibrary", { coordinate, models, registry, options });
1417
1599
  const operations = createOperations(models, coordinate, registry, options);
1418
- const wrappedOperations = Library2.wrapOperations(operations, options, coordinate, registry);
1419
- const libLibrary = Library2.createLibrary(registry, coordinate, wrappedOperations, options);
1600
+ const wrappedOperations = Library3.wrapOperations(operations, options, coordinate, registry);
1601
+ const libLibrary = Library3.createLibrary(registry, coordinate, wrappedOperations, options);
1420
1602
  return {
1421
1603
  ...libLibrary,
1422
1604
  models
@@ -1427,10 +1609,10 @@ var isSequelizeLibrary = (library) => {
1427
1609
  };
1428
1610
 
1429
1611
  // src/SequelizeLibraryFactory.ts
1430
- var logger17 = logger_default.get("InstanceFactory");
1612
+ var logger16 = logger_default.get("InstanceFactory");
1431
1613
  var createSequelizeLibraryFactory = (models, options) => {
1432
1614
  return (coordinate, context) => {
1433
- logger17.debug("Creating Sequelize instance", {
1615
+ logger16.debug("Creating Sequelize instance", {
1434
1616
  coordinate,
1435
1617
  registry: context.registry,
1436
1618
  models: models.map((m) => m.name),
@@ -1475,9 +1657,9 @@ __export(primary_exports, {
1475
1657
 
1476
1658
  // src/primary/SequelizeLibrary.ts
1477
1659
  import { Primary } from "@fjell/lib";
1478
- var logger18 = logger_default.get("lib-sequelize", "primary", "library");
1660
+ var logger17 = logger_default.get("lib-sequelize", "primary", "library");
1479
1661
  function createSequelizeLibrary3(keyType, models, libOptions = {}, scopes = [], registry) {
1480
- logger18.debug("createSequelizeLibrary", { keyType, models, libOptions, scopes });
1662
+ logger17.debug("createSequelizeLibrary", { keyType, models, libOptions, scopes });
1481
1663
  const coordinate = createCoordinate([keyType], scopes);
1482
1664
  const options = createOptions2(libOptions);
1483
1665
  const operations = createOperations(models, coordinate, registry, options);