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.es.js CHANGED
@@ -1,6 +1,6 @@
1
1
  /* istanbul ignore file */
2
2
  import { customAlphabet, urlAlphabet } from 'nanoid';
3
- import { chunk, merge, cloneDeep, isEmpty, invert, uniq, isString as isString$1, get as get$1, set, isFunction as isFunction$1 } from 'lodash-es';
3
+ import { chunk, merge, isEmpty, invert, uniq, cloneDeep, isString as isString$1, get as get$1, set, isFunction as isFunction$1 } from 'lodash-es';
4
4
  import { PromisePool } from '@supercharge/promise-pool';
5
5
  import { S3Client, PutObjectCommand, GetObjectCommand, HeadObjectCommand, CopyObjectCommand, DeleteObjectCommand, DeleteObjectsCommand, ListObjectsV2Command } from '@aws-sdk/client-s3';
6
6
  import { createHash } from 'crypto';
@@ -3480,9 +3480,10 @@ class Schema {
3480
3480
  this.attributes = attributes || {};
3481
3481
  this.passphrase = passphrase ?? "secret";
3482
3482
  this.options = merge({}, this.defaultOptions(), options);
3483
+ const processedAttributes = this.preprocessAttributesForValidation(this.attributes);
3483
3484
  this.validator = new ValidatorManager({ autoEncrypt: false }).compile(merge(
3484
3485
  { $$async: true },
3485
- cloneDeep(this.attributes)
3486
+ processedAttributes
3486
3487
  ));
3487
3488
  if (this.options.generateAutoHooks) this.generateAutoHooks();
3488
3489
  if (!isEmpty(map)) {
@@ -3628,6 +3629,26 @@ class Schema {
3628
3629
  await this.applyHooksActions(rest, "afterUnmap");
3629
3630
  return unflatten(rest);
3630
3631
  }
3632
+ /**
3633
+ * Preprocess attributes to convert nested objects into validator-compatible format
3634
+ * @param {Object} attributes - Original attributes
3635
+ * @returns {Object} Processed attributes for validator
3636
+ */
3637
+ preprocessAttributesForValidation(attributes) {
3638
+ const processed = {};
3639
+ for (const [key, value] of Object.entries(attributes)) {
3640
+ if (typeof value === "object" && value !== null && !Array.isArray(value)) {
3641
+ processed[key] = {
3642
+ type: "object",
3643
+ properties: this.preprocessAttributesForValidation(value),
3644
+ strict: false
3645
+ };
3646
+ } else {
3647
+ processed[key] = value;
3648
+ }
3649
+ }
3650
+ return processed;
3651
+ }
3631
3652
  }
3632
3653
 
3633
3654
  var global$1 = (typeof global !== "undefined" ? global :
@@ -8943,7 +8964,7 @@ class Resource extends EventEmitter {
8943
8964
  continue;
8944
8965
  }
8945
8966
  for (const fieldName of Object.keys(partitionDef.fields)) {
8946
- if (!currentAttributes.includes(fieldName)) {
8967
+ if (!this.fieldExistsInAttributes(fieldName)) {
8947
8968
  throw new Error(
8948
8969
  `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.`
8949
8970
  );
@@ -8951,6 +8972,25 @@ class Resource extends EventEmitter {
8951
8972
  }
8952
8973
  }
8953
8974
  }
8975
+ /**
8976
+ * Check if a field (including nested fields) exists in the current attributes
8977
+ * @param {string} fieldName - Field name (can be nested like 'utm.source')
8978
+ * @returns {boolean} True if field exists
8979
+ */
8980
+ fieldExistsInAttributes(fieldName) {
8981
+ if (!fieldName.includes(".")) {
8982
+ return Object.keys(this.attributes || {}).includes(fieldName);
8983
+ }
8984
+ const keys = fieldName.split(".");
8985
+ let currentLevel = this.attributes || {};
8986
+ for (const key of keys) {
8987
+ if (!currentLevel || typeof currentLevel !== "object" || !(key in currentLevel)) {
8988
+ return false;
8989
+ }
8990
+ currentLevel = currentLevel[key];
8991
+ }
8992
+ return true;
8993
+ }
8954
8994
  /**
8955
8995
  * Apply a single partition rule to a field value
8956
8996
  * @param {*} value - The field value
@@ -9013,17 +9053,38 @@ class Resource extends EventEmitter {
9013
9053
  const partitionSegments = [];
9014
9054
  const sortedFields = Object.entries(partition.fields).sort(([a], [b]) => a.localeCompare(b));
9015
9055
  for (const [fieldName, rule] of sortedFields) {
9016
- const fieldValue = this.applyPartitionRule(data[fieldName], rule);
9017
- if (fieldValue === void 0 || fieldValue === null) {
9056
+ const fieldValue = this.getNestedFieldValue(data, fieldName);
9057
+ const transformedValue = this.applyPartitionRule(fieldValue, rule);
9058
+ if (transformedValue === void 0 || transformedValue === null) {
9018
9059
  return null;
9019
9060
  }
9020
- partitionSegments.push(`${fieldName}=${fieldValue}`);
9061
+ partitionSegments.push(`${fieldName}=${transformedValue}`);
9021
9062
  }
9022
9063
  if (partitionSegments.length === 0) {
9023
9064
  return null;
9024
9065
  }
9025
9066
  return join(`resource=${this.name}`, `partition=${partitionName}`, ...partitionSegments, `id=${id}`);
9026
9067
  }
9068
+ /**
9069
+ * Get nested field value from data object using dot notation
9070
+ * @param {Object} data - Data object
9071
+ * @param {string} fieldPath - Field path (e.g., "utm.source", "address.city")
9072
+ * @returns {*} Field value
9073
+ */
9074
+ getNestedFieldValue(data, fieldPath) {
9075
+ if (!fieldPath.includes(".")) {
9076
+ return data[fieldPath];
9077
+ }
9078
+ const keys = fieldPath.split(".");
9079
+ let value = data;
9080
+ for (const key of keys) {
9081
+ if (value === null || value === void 0 || typeof value !== "object") {
9082
+ return void 0;
9083
+ }
9084
+ value = value[key];
9085
+ }
9086
+ return value;
9087
+ }
9027
9088
  async insert({ id, ...attributes }) {
9028
9089
  if (this.options.timestamps) {
9029
9090
  attributes.createdAt = (/* @__PURE__ */ new Date()).toISOString();
@@ -9699,7 +9760,7 @@ class Database extends EventEmitter {
9699
9760
  this.version = "1";
9700
9761
  this.s3dbVersion = (() => {
9701
9762
  try {
9702
- return true ? "4.0.1" : "latest";
9763
+ return true ? "4.0.2" : "latest";
9703
9764
  } catch (e) {
9704
9765
  return "latest";
9705
9766
  }