s3db.js 4.0.1 → 4.1.0

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/s3db.iife.js CHANGED
@@ -3474,9 +3474,10 @@ ${JSON.stringify(validation, null, 2)}`
3474
3474
  this.attributes = attributes || {};
3475
3475
  this.passphrase = passphrase ?? "secret";
3476
3476
  this.options = lodashEs.merge({}, this.defaultOptions(), options);
3477
+ const processedAttributes = this.preprocessAttributesForValidation(this.attributes);
3477
3478
  this.validator = new ValidatorManager({ autoEncrypt: false }).compile(lodashEs.merge(
3478
3479
  { $$async: true },
3479
- lodashEs.cloneDeep(this.attributes)
3480
+ processedAttributes
3480
3481
  ));
3481
3482
  if (this.options.generateAutoHooks) this.generateAutoHooks();
3482
3483
  if (!lodashEs.isEmpty(map)) {
@@ -3622,6 +3623,26 @@ ${JSON.stringify(validation, null, 2)}`
3622
3623
  await this.applyHooksActions(rest, "afterUnmap");
3623
3624
  return flat.unflatten(rest);
3624
3625
  }
3626
+ /**
3627
+ * Preprocess attributes to convert nested objects into validator-compatible format
3628
+ * @param {Object} attributes - Original attributes
3629
+ * @returns {Object} Processed attributes for validator
3630
+ */
3631
+ preprocessAttributesForValidation(attributes) {
3632
+ const processed = {};
3633
+ for (const [key, value] of Object.entries(attributes)) {
3634
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3635
+ processed[key] = {
3636
+ type: "object",
3637
+ properties: this.preprocessAttributesForValidation(value),
3638
+ strict: false
3639
+ };
3640
+ } else {
3641
+ processed[key] = value;
3642
+ }
3643
+ }
3644
+ return processed;
3645
+ }
3625
3646
  }
3626
3647
 
3627
3648
  var global$1 = (typeof global !== "undefined" ? global :
@@ -8937,7 +8958,7 @@ ${JSON.stringify(validation, null, 2)}`
8937
8958
  continue;
8938
8959
  }
8939
8960
  for (const fieldName of Object.keys(partitionDef.fields)) {
8940
- if (!currentAttributes.includes(fieldName)) {
8961
+ if (!this.fieldExistsInAttributes(fieldName)) {
8941
8962
  throw new Error(
8942
8963
  `Partition '${partitionName}' uses field '${fieldName}' which does not exist in resource version '${this.version}'. Available fields: ${currentAttributes.join(", ")}. This version of resource does not have support for this partition.`
8943
8964
  );
@@ -8945,6 +8966,25 @@ ${JSON.stringify(validation, null, 2)}`
8945
8966
  }
8946
8967
  }
8947
8968
  }
8969
+ /**
8970
+ * Check if a field (including nested fields) exists in the current attributes
8971
+ * @param {string} fieldName - Field name (can be nested like 'utm.source')
8972
+ * @returns {boolean} True if field exists
8973
+ */
8974
+ fieldExistsInAttributes(fieldName) {
8975
+ if (!fieldName.includes(".")) {
8976
+ return Object.keys(this.attributes || {}).includes(fieldName);
8977
+ }
8978
+ const keys = fieldName.split(".");
8979
+ let currentLevel = this.attributes || {};
8980
+ for (const key of keys) {
8981
+ if (!currentLevel || typeof currentLevel !== "object" || !(key in currentLevel)) {
8982
+ return false;
8983
+ }
8984
+ currentLevel = currentLevel[key];
8985
+ }
8986
+ return true;
8987
+ }
8948
8988
  /**
8949
8989
  * Apply a single partition rule to a field value
8950
8990
  * @param {*} value - The field value
@@ -9007,17 +9047,38 @@ ${JSON.stringify(validation, null, 2)}`
9007
9047
  const partitionSegments = [];
9008
9048
  const sortedFields = Object.entries(partition.fields).sort(([a], [b]) => a.localeCompare(b));
9009
9049
  for (const [fieldName, rule] of sortedFields) {
9010
- const fieldValue = this.applyPartitionRule(data[fieldName], rule);
9011
- if (fieldValue === void 0 || fieldValue === null) {
9050
+ const fieldValue = this.getNestedFieldValue(data, fieldName);
9051
+ const transformedValue = this.applyPartitionRule(fieldValue, rule);
9052
+ if (transformedValue === void 0 || transformedValue === null) {
9012
9053
  return null;
9013
9054
  }
9014
- partitionSegments.push(`${fieldName}=${fieldValue}`);
9055
+ partitionSegments.push(`${fieldName}=${transformedValue}`);
9015
9056
  }
9016
9057
  if (partitionSegments.length === 0) {
9017
9058
  return null;
9018
9059
  }
9019
9060
  return join(`resource=${this.name}`, `partition=${partitionName}`, ...partitionSegments, `id=${id}`);
9020
9061
  }
9062
+ /**
9063
+ * Get nested field value from data object using dot notation
9064
+ * @param {Object} data - Data object
9065
+ * @param {string} fieldPath - Field path (e.g., "utm.source", "address.city")
9066
+ * @returns {*} Field value
9067
+ */
9068
+ getNestedFieldValue(data, fieldPath) {
9069
+ if (!fieldPath.includes(".")) {
9070
+ return data[fieldPath];
9071
+ }
9072
+ const keys = fieldPath.split(".");
9073
+ let value = data;
9074
+ for (const key of keys) {
9075
+ if (value === null || value === void 0 || typeof value !== "object") {
9076
+ return void 0;
9077
+ }
9078
+ value = value[key];
9079
+ }
9080
+ return value;
9081
+ }
9021
9082
  async insert({ id, ...attributes }) {
9022
9083
  if (this.options.timestamps) {
9023
9084
  attributes.createdAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -9693,7 +9754,7 @@ ${JSON.stringify(validation, null, 2)}`
9693
9754
  this.version = "1";
9694
9755
  this.s3dbVersion = (() => {
9695
9756
  try {
9696
- return true ? "4.0.1" : "latest";
9757
+ return true ? "4.0.2" : "latest";
9697
9758
  } catch (e) {
9698
9759
  return "latest";
9699
9760
  }