s3db.js 4.1.8 → 4.1.11

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
@@ -236,6 +236,8 @@ var S3DB = (function (exports, nanoid, lodashEs, promisePool, clientS3, crypto,
236
236
  ;
237
237
 
238
238
  const idGenerator = nanoid.customAlphabet(nanoid.urlAlphabet, 22);
239
+ const passwordAlphabet = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
240
+ nanoid.customAlphabet(passwordAlphabet, 12);
239
241
 
240
242
  var domain;
241
243
 
@@ -736,42 +738,85 @@ ${JSON.stringify(rest, null, 2)}`;
736
738
  return `${this.name} | ${this.message}`;
737
739
  }
738
740
  }
739
- class NoSuchBucket extends BaseError {
741
+ class S3DBError extends BaseError {
742
+ constructor(message, details = {}) {
743
+ super({ message, ...details });
744
+ }
745
+ }
746
+ class DatabaseError extends S3DBError {
747
+ constructor(message, details = {}) {
748
+ super(message, details);
749
+ Object.assign(this, details);
750
+ }
751
+ }
752
+ class ValidationError extends S3DBError {
753
+ constructor(message, details = {}) {
754
+ super(message, details);
755
+ Object.assign(this, details);
756
+ }
757
+ }
758
+ class AuthenticationError extends S3DBError {
759
+ constructor(message, details = {}) {
760
+ super(message, details);
761
+ Object.assign(this, details);
762
+ }
763
+ }
764
+ class PermissionError extends S3DBError {
765
+ constructor(message, details = {}) {
766
+ super(message, details);
767
+ Object.assign(this, details);
768
+ }
769
+ }
770
+ class EncryptionError extends S3DBError {
771
+ constructor(message, details = {}) {
772
+ super(message, details);
773
+ Object.assign(this, details);
774
+ }
775
+ }
776
+ class ResourceNotFound extends S3DBError {
777
+ constructor({ bucket, resourceName, id, ...rest }) {
778
+ super(`Resource not found: ${resourceName}/${id} [bucket:${bucket}]`, {
779
+ bucket,
780
+ resourceName,
781
+ id,
782
+ ...rest
783
+ });
784
+ }
785
+ }
786
+ class NoSuchBucket extends S3DBError {
740
787
  constructor({ bucket, ...rest }) {
741
- super({ ...rest, bucket, message: `Bucket does not exists [bucket:${bucket}]` });
788
+ super(`Bucket does not exists [bucket:${bucket}]`, { bucket, ...rest });
742
789
  }
743
790
  }
744
- class NoSuchKey extends BaseError {
791
+ class NoSuchKey extends S3DBError {
745
792
  constructor({ bucket, key, ...rest }) {
746
- super({ ...rest, bucket, message: `Key [${key}] does not exists [bucket:${bucket}/${key}]` });
747
- this.key = key;
793
+ super(`Key [${key}] does not exists [bucket:${bucket}/${key}]`, { bucket, key, ...rest });
748
794
  }
749
795
  }
750
796
  class NotFound extends NoSuchKey {
751
797
  }
752
- class MissingMetadata extends BaseError {
798
+ class MissingMetadata extends S3DBError {
753
799
  constructor({ bucket, ...rest }) {
754
- super({ ...rest, bucket, message: `Missing metadata for bucket [bucket:${bucket}]` });
800
+ super(`Missing metadata for bucket [bucket:${bucket}]`, { bucket, ...rest });
755
801
  }
756
802
  }
757
- class InvalidResourceItem extends BaseError {
803
+ class InvalidResourceItem extends S3DBError {
758
804
  constructor({
759
805
  bucket,
760
806
  resourceName,
761
807
  attributes,
762
808
  validation
763
809
  }) {
764
- super({
810
+ super(`This item is not valid. Resource=${resourceName} [bucket:${bucket}].
811
+ ${JSON.stringify(validation, null, 2)}`, {
765
812
  bucket,
766
- message: `This item is not valid. Resource=${resourceName} [bucket:${bucket}].
767
- ${JSON.stringify(validation, null, 2)}`
813
+ resourceName,
814
+ attributes,
815
+ validation
768
816
  });
769
- this.resourceName = resourceName;
770
- this.attributes = attributes;
771
- this.validation = validation;
772
817
  }
773
818
  }
774
- class UnknownError extends BaseError {
819
+ class UnknownError extends S3DBError {
775
820
  }
776
821
  const ErrorMap = {
777
822
  "NotFound": NotFound,
@@ -800,9 +845,9 @@ ${JSON.stringify(validation, null, 2)}`
800
845
  }
801
846
  }
802
847
  defineS3(uri) {
803
- this.bucket = uri.hostname;
804
- this.accessKeyId = uri.username;
805
- this.secretAccessKey = uri.password;
848
+ this.bucket = decodeURIComponent(uri.hostname);
849
+ this.accessKeyId = decodeURIComponent(uri.username);
850
+ this.secretAccessKey = decodeURIComponent(uri.password);
806
851
  this.endpoint = S3_DEFAULT_ENDPOINT;
807
852
  if (["/", "", null].includes(uri.pathname)) {
808
853
  this.keyPrefix = "";
@@ -814,14 +859,14 @@ ${JSON.stringify(validation, null, 2)}`
814
859
  defineMinio(uri) {
815
860
  this.forcePathStyle = true;
816
861
  this.endpoint = uri.origin;
817
- this.accessKeyId = uri.username;
818
- this.secretAccessKey = uri.password;
862
+ this.accessKeyId = decodeURIComponent(uri.username);
863
+ this.secretAccessKey = decodeURIComponent(uri.password);
819
864
  if (["/", "", null].includes(uri.pathname)) {
820
865
  this.bucket = "s3db";
821
866
  this.keyPrefix = "";
822
867
  } else {
823
868
  let [, bucket, ...subpath] = uri.pathname.split("/");
824
- this.bucket = bucket;
869
+ this.bucket = decodeURIComponent(bucket);
825
870
  this.keyPrefix = [...subpath || []].join("/");
826
871
  }
827
872
  }
@@ -8830,18 +8875,83 @@ ${JSON.stringify(validation, null, 2)}`
8830
8875
  const DEFAULT_BEHAVIOR = "user-management";
8831
8876
 
8832
8877
  class Resource extends EventEmitter {
8833
- constructor({
8834
- name,
8835
- client,
8836
- version = "1",
8837
- options = {},
8838
- attributes = {},
8839
- parallelism = 10,
8840
- passphrase = "secret",
8841
- observers = [],
8842
- behavior = DEFAULT_BEHAVIOR
8843
- }) {
8878
+ /**
8879
+ * Create a new Resource instance
8880
+ * @param {Object} config - Resource configuration
8881
+ * @param {string} config.name - Resource name
8882
+ * @param {Object} config.client - S3 client instance
8883
+ * @param {string} [config.version='v0'] - Resource version
8884
+ * @param {Object} [config.attributes={}] - Resource attributes schema
8885
+ * @param {string} [config.behavior='user-management'] - Resource behavior strategy
8886
+ * @param {string} [config.passphrase='secret'] - Encryption passphrase
8887
+ * @param {number} [config.parallelism=10] - Parallelism for bulk operations
8888
+ * @param {Array} [config.observers=[]] - Observer instances
8889
+ * @param {boolean} [config.cache=false] - Enable caching
8890
+ * @param {boolean} [config.autoDecrypt=true] - Auto-decrypt secret fields
8891
+ * @param {boolean} [config.timestamps=false] - Enable automatic timestamps
8892
+ * @param {Object} [config.partitions={}] - Partition definitions
8893
+ * @param {boolean} [config.paranoid=true] - Security flag for dangerous operations
8894
+ * @param {boolean} [config.allNestedObjectsOptional=false] - Make nested objects optional
8895
+ * @param {Object} [config.hooks={}] - Custom hooks
8896
+ * @param {Object} [config.options={}] - Additional options
8897
+ * @example
8898
+ * const users = new Resource({
8899
+ * name: 'users',
8900
+ * client: s3Client,
8901
+ * attributes: {
8902
+ * name: 'string|required',
8903
+ * email: 'string|required',
8904
+ * password: 'secret|required'
8905
+ * },
8906
+ * behavior: 'user-management',
8907
+ * passphrase: 'my-secret-key',
8908
+ * timestamps: true,
8909
+ * partitions: {
8910
+ * byRegion: {
8911
+ * fields: { region: 'string' }
8912
+ * }
8913
+ * },
8914
+ * hooks: {
8915
+ * preInsert: [async (data) => {
8916
+ * console.log('Pre-insert hook:', data);
8917
+ * return data;
8918
+ * }]
8919
+ * }
8920
+ * });
8921
+ */
8922
+ constructor(config) {
8844
8923
  super();
8924
+ const validation = validateResourceConfig(config);
8925
+ if (!validation.isValid) {
8926
+ throw new Error(`Invalid Resource configuration:
8927
+ ${validation.errors.join("\n")}`);
8928
+ }
8929
+ const {
8930
+ name,
8931
+ client,
8932
+ version = "1",
8933
+ attributes = {},
8934
+ behavior = DEFAULT_BEHAVIOR,
8935
+ passphrase = "secret",
8936
+ parallelism = 10,
8937
+ observers = [],
8938
+ cache = false,
8939
+ autoDecrypt = true,
8940
+ timestamps = false,
8941
+ partitions = {},
8942
+ paranoid = true,
8943
+ allNestedObjectsOptional = true,
8944
+ hooks = {},
8945
+ options = {}
8946
+ } = config;
8947
+ const mergedOptions = {
8948
+ cache: typeof options.cache === "boolean" ? options.cache : cache,
8949
+ autoDecrypt: typeof options.autoDecrypt === "boolean" ? options.autoDecrypt : autoDecrypt,
8950
+ timestamps: typeof options.timestamps === "boolean" ? options.timestamps : timestamps,
8951
+ paranoid: typeof options.paranoid === "boolean" ? options.paranoid : paranoid,
8952
+ allNestedObjectsOptional: typeof options.allNestedObjectsOptional === "boolean" ? options.allNestedObjectsOptional : allNestedObjectsOptional,
8953
+ partitions: options.partitions || partitions || {}
8954
+ };
8845
8955
  this.name = name;
8846
8956
  this.client = client;
8847
8957
  this.version = version;
@@ -8849,15 +8959,14 @@ ${JSON.stringify(validation, null, 2)}`
8849
8959
  this.observers = observers;
8850
8960
  this.parallelism = parallelism;
8851
8961
  this.passphrase = passphrase ?? "secret";
8852
- this.options = {
8853
- cache: false,
8854
- autoDecrypt: true,
8855
- timestamps: false,
8856
- partitions: {},
8857
- paranoid: true,
8858
- // Security flag for dangerous operations
8859
- allNestedObjectsOptional: options.allNestedObjectsOptional ?? false,
8860
- ...options
8962
+ this.config = {
8963
+ cache: mergedOptions.cache,
8964
+ hooks,
8965
+ paranoid: mergedOptions.paranoid,
8966
+ timestamps: mergedOptions.timestamps,
8967
+ partitions: mergedOptions.partitions,
8968
+ autoDecrypt: mergedOptions.autoDecrypt,
8969
+ allNestedObjectsOptional: mergedOptions.allNestedObjectsOptional
8861
8970
  };
8862
8971
  this.hooks = {
8863
8972
  preInsert: [],
@@ -8868,18 +8977,21 @@ ${JSON.stringify(validation, null, 2)}`
8868
8977
  afterDelete: []
8869
8978
  };
8870
8979
  this.attributes = attributes || {};
8871
- if (options.timestamps) {
8980
+ if (this.config.timestamps) {
8872
8981
  this.attributes.createdAt = "string|optional";
8873
8982
  this.attributes.updatedAt = "string|optional";
8874
- if (!this.options.partitions.byCreatedDate) {
8875
- this.options.partitions.byCreatedDate = {
8983
+ if (!this.config.partitions) {
8984
+ this.config.partitions = {};
8985
+ }
8986
+ if (!this.config.partitions.byCreatedDate) {
8987
+ this.config.partitions.byCreatedDate = {
8876
8988
  fields: {
8877
8989
  createdAt: "date|maxlength:10"
8878
8990
  }
8879
8991
  };
8880
8992
  }
8881
- if (!this.options.partitions.byUpdatedDate) {
8882
- this.options.partitions.byUpdatedDate = {
8993
+ if (!this.config.partitions.byUpdatedDate) {
8994
+ this.config.partitions.byUpdatedDate = {
8883
8995
  fields: {
8884
8996
  updatedAt: "date|maxlength:10"
8885
8997
  }
@@ -8892,25 +9004,47 @@ ${JSON.stringify(validation, null, 2)}`
8892
9004
  passphrase,
8893
9005
  version: this.version,
8894
9006
  options: {
8895
- ...this.options,
8896
- allNestedObjectsOptional: this.options.allNestedObjectsOptional ?? false
9007
+ autoDecrypt: this.config.autoDecrypt,
9008
+ allNestedObjectsOptional: this.config.allNestedObjectsOptional
8897
9009
  }
8898
9010
  });
8899
- this.validatePartitions();
8900
9011
  this.setupPartitionHooks();
8901
- if (options.hooks) {
8902
- for (const [event, hooksArr] of Object.entries(options.hooks)) {
9012
+ this.validatePartitions();
9013
+ if (hooks) {
9014
+ for (const [event, hooksArr] of Object.entries(hooks)) {
8903
9015
  if (Array.isArray(hooksArr) && this.hooks[event]) {
8904
9016
  for (const fn of hooksArr) {
8905
- this.hooks[event].push(fn.bind(this));
9017
+ if (typeof fn === "function") {
9018
+ this.hooks[event].push(fn.bind(this));
9019
+ }
8906
9020
  }
8907
9021
  }
8908
9022
  }
8909
9023
  }
8910
9024
  }
9025
+ /**
9026
+ * Get resource options (for backward compatibility with tests)
9027
+ */
9028
+ get options() {
9029
+ return {
9030
+ timestamps: this.config.timestamps,
9031
+ partitions: this.config.partitions || {},
9032
+ cache: this.config.cache,
9033
+ autoDecrypt: this.config.autoDecrypt,
9034
+ paranoid: this.config.paranoid,
9035
+ allNestedObjectsOptional: this.config.allNestedObjectsOptional
9036
+ };
9037
+ }
8911
9038
  export() {
8912
9039
  const exported = this.schema.export();
8913
9040
  exported.behavior = this.behavior;
9041
+ exported.timestamps = this.config.timestamps;
9042
+ exported.partitions = this.config.partitions || {};
9043
+ exported.paranoid = this.config.paranoid;
9044
+ exported.allNestedObjectsOptional = this.config.allNestedObjectsOptional;
9045
+ exported.autoDecrypt = this.config.autoDecrypt;
9046
+ exported.cache = this.config.cache;
9047
+ exported.hooks = this.hooks;
8914
9048
  return exported;
8915
9049
  }
8916
9050
  /**
@@ -8920,18 +9054,21 @@ ${JSON.stringify(validation, null, 2)}`
8920
9054
  updateAttributes(newAttributes) {
8921
9055
  const oldAttributes = this.attributes;
8922
9056
  this.attributes = newAttributes;
8923
- if (this.options.timestamps) {
9057
+ if (this.config.timestamps) {
8924
9058
  newAttributes.createdAt = "string|optional";
8925
9059
  newAttributes.updatedAt = "string|optional";
8926
- if (!this.options.partitions.byCreatedDate) {
8927
- this.options.partitions.byCreatedDate = {
9060
+ if (!this.config.partitions) {
9061
+ this.config.partitions = {};
9062
+ }
9063
+ if (!this.config.partitions.byCreatedDate) {
9064
+ this.config.partitions.byCreatedDate = {
8928
9065
  fields: {
8929
9066
  createdAt: "date|maxlength:10"
8930
9067
  }
8931
9068
  };
8932
9069
  }
8933
- if (!this.options.partitions.byUpdatedDate) {
8934
- this.options.partitions.byUpdatedDate = {
9070
+ if (!this.config.partitions.byUpdatedDate) {
9071
+ this.config.partitions.byUpdatedDate = {
8935
9072
  fields: {
8936
9073
  updatedAt: "date|maxlength:10"
8937
9074
  }
@@ -8943,10 +9080,13 @@ ${JSON.stringify(validation, null, 2)}`
8943
9080
  attributes: newAttributes,
8944
9081
  passphrase: this.passphrase,
8945
9082
  version: this.version,
8946
- options: this.options
9083
+ options: {
9084
+ autoDecrypt: this.config.autoDecrypt,
9085
+ allNestedObjectsOptional: this.config.allNestedObjectsOptional
9086
+ }
8947
9087
  });
8948
- this.validatePartitions();
8949
9088
  this.setupPartitionHooks();
9089
+ this.validatePartitions();
8950
9090
  return { oldAttributes, newAttributes };
8951
9091
  }
8952
9092
  /**
@@ -8977,15 +9117,24 @@ ${JSON.stringify(validation, null, 2)}`
8977
9117
  * Setup automatic partition hooks
8978
9118
  */
8979
9119
  setupPartitionHooks() {
8980
- const partitions = this.options.partitions;
8981
- if (!partitions || Object.keys(partitions).length === 0) {
9120
+ if (!this.config.partitions) {
9121
+ return;
9122
+ }
9123
+ const partitions = this.config.partitions;
9124
+ if (Object.keys(partitions).length === 0) {
8982
9125
  return;
8983
9126
  }
8984
- this.addHook("afterInsert", async (data) => {
9127
+ if (!this.hooks.afterInsert) {
9128
+ this.hooks.afterInsert = [];
9129
+ }
9130
+ this.hooks.afterInsert.push(async (data) => {
8985
9131
  await this.createPartitionReferences(data);
8986
9132
  return data;
8987
9133
  });
8988
- this.addHook("afterDelete", async (data) => {
9134
+ if (!this.hooks.afterDelete) {
9135
+ this.hooks.afterDelete = [];
9136
+ }
9137
+ this.hooks.afterDelete.push(async (data) => {
8989
9138
  await this.deletePartitionReferences(data);
8990
9139
  return data;
8991
9140
  });
@@ -9010,8 +9159,11 @@ ${JSON.stringify(validation, null, 2)}`
9010
9159
  * @throws {Error} If partition fields don't exist in current schema
9011
9160
  */
9012
9161
  validatePartitions() {
9013
- const partitions = this.options.partitions;
9014
- if (!partitions || Object.keys(partitions).length === 0) {
9162
+ if (!this.config.partitions) {
9163
+ return;
9164
+ }
9165
+ const partitions = this.config.partitions;
9166
+ if (Object.keys(partitions).length === 0) {
9015
9167
  return;
9016
9168
  }
9017
9169
  const currentAttributes = Object.keys(this.attributes || {});
@@ -9118,10 +9270,10 @@ ${JSON.stringify(validation, null, 2)}`
9118
9270
  * // Returns: null
9119
9271
  */
9120
9272
  getPartitionKey({ partitionName, id, data }) {
9121
- const partition = this.options.partitions[partitionName];
9122
- if (!partition) {
9273
+ if (!this.config.partitions || !this.config.partitions[partitionName]) {
9123
9274
  throw new Error(`Partition '${partitionName}' not found`);
9124
9275
  }
9276
+ const partition = this.config.partitions[partitionName];
9125
9277
  const partitionSegments = [];
9126
9278
  const sortedFields = Object.entries(partition.fields).sort(([a], [b]) => a.localeCompare(b));
9127
9279
  for (const [fieldName, rule] of sortedFields) {
@@ -9177,6 +9329,12 @@ ${JSON.stringify(validation, null, 2)}`
9177
9329
  * name: 'Jane Smith',
9178
9330
  * email: 'jane@example.com'
9179
9331
  * });
9332
+ *
9333
+ * // Insert with auto-generated password for secret field
9334
+ * const user = await resource.insert({
9335
+ * name: 'John Doe',
9336
+ * email: 'john@example.com',
9337
+ * });
9180
9338
  */
9181
9339
  async insert({ id, ...attributes }) {
9182
9340
  if (this.options.timestamps) {
@@ -9261,6 +9419,58 @@ ${JSON.stringify(validation, null, 2)}`
9261
9419
  this.emit("get", data);
9262
9420
  return data;
9263
9421
  } catch (error) {
9422
+ if (error.message.includes("Cipher job failed") || error.message.includes("OperationError") || error.originalError?.message?.includes("Cipher job failed")) {
9423
+ try {
9424
+ console.warn(`Decryption failed for resource ${id}, attempting to get raw metadata`);
9425
+ const request = await this.client.headObject(key);
9426
+ const objectVersion = this.extractVersionFromKey(key) || this.version;
9427
+ const tempSchema = new Schema({
9428
+ name: this.name,
9429
+ attributes: this.attributes,
9430
+ passphrase: this.passphrase,
9431
+ version: objectVersion,
9432
+ options: {
9433
+ ...this.config,
9434
+ autoDecrypt: false,
9435
+ // Disable decryption
9436
+ autoEncrypt: false
9437
+ // Disable encryption
9438
+ }
9439
+ });
9440
+ let metadata = await tempSchema.unmapper(request.Metadata);
9441
+ const behaviorImpl = getBehavior(this.behavior);
9442
+ let body = "";
9443
+ if (request.ContentLength > 0) {
9444
+ try {
9445
+ const fullObject = await this.client.getObject(key);
9446
+ body = await streamToString(fullObject.Body);
9447
+ } catch (bodyError) {
9448
+ console.warn(`Failed to read body for resource ${id}:`, bodyError.message);
9449
+ body = "";
9450
+ }
9451
+ }
9452
+ const { metadata: processedMetadata } = await behaviorImpl.handleGet({
9453
+ resource: this,
9454
+ metadata,
9455
+ body
9456
+ });
9457
+ let data = processedMetadata;
9458
+ data.id = id;
9459
+ data._contentLength = request.ContentLength;
9460
+ data._lastModified = request.LastModified;
9461
+ data._hasContent = request.ContentLength > 0;
9462
+ data._mimeType = request.ContentType || null;
9463
+ data._version = objectVersion;
9464
+ data._decryptionFailed = true;
9465
+ if (request.VersionId) data._versionId = request.VersionId;
9466
+ if (request.Expiration) data._expiresAt = request.Expiration;
9467
+ data._definitionHash = this.getDefinitionHash();
9468
+ this.emit("get", data);
9469
+ return data;
9470
+ } catch (fallbackError) {
9471
+ console.error(`Fallback attempt also failed for resource ${id}:`, fallbackError.message);
9472
+ }
9473
+ }
9264
9474
  const enhancedError = new Error(`Failed to get resource with id '${id}': ${error.message}`);
9265
9475
  enhancedError.originalError = error;
9266
9476
  enhancedError.resourceId = id;
@@ -9307,7 +9517,7 @@ ${JSON.stringify(validation, null, 2)}`
9307
9517
  */
9308
9518
  async update(id, attributes) {
9309
9519
  const live = await this.get(id);
9310
- if (this.options.timestamps) {
9520
+ if (this.config.timestamps) {
9311
9521
  attributes.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
9312
9522
  }
9313
9523
  const preProcessedData = await this.executeHooks("preUpdate", attributes);
@@ -9429,7 +9639,7 @@ ${JSON.stringify(validation, null, 2)}`
9429
9639
  async count({ partition = null, partitionValues = {} } = {}) {
9430
9640
  let prefix;
9431
9641
  if (partition && Object.keys(partitionValues).length > 0) {
9432
- const partitionDef = this.options.partitions[partition];
9642
+ const partitionDef = this.config.partitions[partition];
9433
9643
  if (!partitionDef) {
9434
9644
  throw new Error(`Partition '${partition}' not found`);
9435
9645
  }
@@ -9514,9 +9724,9 @@ ${JSON.stringify(validation, null, 2)}`
9514
9724
  return results;
9515
9725
  }
9516
9726
  async deleteAll() {
9517
- if (this.options.paranoid !== false) {
9727
+ if (this.config.paranoid !== false) {
9518
9728
  throw new Error(
9519
- `deleteAll() is a dangerous operation and requires paranoid: false option. Current paranoid setting: ${this.options.paranoid}`
9729
+ `deleteAll() is a dangerous operation and requires paranoid: false option. Current paranoid setting: ${this.config.paranoid}`
9520
9730
  );
9521
9731
  }
9522
9732
  const prefix = `resource=${this.name}/v=${this.version}`;
@@ -9533,9 +9743,9 @@ ${JSON.stringify(validation, null, 2)}`
9533
9743
  * @returns {Promise<Object>} Deletion report
9534
9744
  */
9535
9745
  async deleteAllData() {
9536
- if (this.options.paranoid !== false) {
9746
+ if (this.config.paranoid !== false) {
9537
9747
  throw new Error(
9538
- `deleteAllData() is a dangerous operation and requires paranoid: false option. Current paranoid setting: ${this.options.paranoid}`
9748
+ `deleteAllData() is a dangerous operation and requires paranoid: false option. Current paranoid setting: ${this.config.paranoid}`
9539
9749
  );
9540
9750
  }
9541
9751
  const prefix = `resource=${this.name}`;
@@ -9578,10 +9788,10 @@ ${JSON.stringify(validation, null, 2)}`
9578
9788
  async listIds({ partition = null, partitionValues = {}, limit, offset = 0 } = {}) {
9579
9789
  let prefix;
9580
9790
  if (partition && Object.keys(partitionValues).length > 0) {
9581
- const partitionDef = this.options.partitions[partition];
9582
- if (!partitionDef) {
9791
+ if (!this.config.partitions || !this.config.partitions[partition]) {
9583
9792
  throw new Error(`Partition '${partition}' not found`);
9584
9793
  }
9794
+ const partitionDef = this.config.partitions[partition];
9585
9795
  const partitionSegments = [];
9586
9796
  const sortedFields = Object.entries(partitionDef.fields).sort(([a], [b]) => a.localeCompare(b));
9587
9797
  for (const [fieldName, rule] of sortedFields) {
@@ -9650,16 +9860,32 @@ ${JSON.stringify(validation, null, 2)}`
9650
9860
  if (limit) {
9651
9861
  filteredIds2 = filteredIds2.slice(0, limit);
9652
9862
  }
9653
- const { results: results2 } = await promisePool.PromisePool.for(filteredIds2).withConcurrency(this.parallelism).process(async (id) => {
9654
- return await this.get(id);
9863
+ const { results: results2, errors: errors2 } = await promisePool.PromisePool.for(filteredIds2).withConcurrency(this.parallelism).handleError(async (error, id) => {
9864
+ console.warn(`Failed to get resource ${id}:`, error.message);
9865
+ return null;
9866
+ }).process(async (id) => {
9867
+ try {
9868
+ return await this.get(id);
9869
+ } catch (error) {
9870
+ if (error.message.includes("Cipher job failed") || error.message.includes("OperationError")) {
9871
+ console.warn(`Decryption failed for ${id}, returning basic info`);
9872
+ return {
9873
+ id,
9874
+ _decryptionFailed: true,
9875
+ _error: error.message
9876
+ };
9877
+ }
9878
+ throw error;
9879
+ }
9655
9880
  });
9656
- this.emit("list", { partition, partitionValues, count: results2.length });
9657
- return results2;
9881
+ const validResults2 = results2.filter((item) => item !== null);
9882
+ this.emit("list", { partition, partitionValues, count: validResults2.length, errors: errors2.length });
9883
+ return validResults2;
9658
9884
  }
9659
- const partitionDef = this.options.partitions[partition];
9660
- if (!partitionDef) {
9885
+ if (!this.config.partitions || !this.config.partitions[partition]) {
9661
9886
  throw new Error(`Partition '${partition}' not found`);
9662
9887
  }
9888
+ const partitionDef = this.config.partitions[partition];
9663
9889
  const partitionSegments = [];
9664
9890
  const sortedFields = Object.entries(partitionDef.fields).sort(([a], [b]) => a.localeCompare(b));
9665
9891
  for (const [fieldName, rule] of sortedFields) {
@@ -9685,11 +9911,29 @@ ${JSON.stringify(validation, null, 2)}`
9685
9911
  if (limit) {
9686
9912
  filteredIds = filteredIds.slice(0, limit);
9687
9913
  }
9688
- const { results } = await promisePool.PromisePool.for(filteredIds).withConcurrency(this.parallelism).process(async (id) => {
9689
- return await this.getFromPartition({ id, partitionName: partition, partitionValues });
9914
+ const { results, errors } = await promisePool.PromisePool.for(filteredIds).withConcurrency(this.parallelism).handleError(async (error, id) => {
9915
+ console.warn(`Failed to get partition resource ${id}:`, error.message);
9916
+ return null;
9917
+ }).process(async (id) => {
9918
+ try {
9919
+ return await this.getFromPartition({ id, partitionName: partition, partitionValues });
9920
+ } catch (error) {
9921
+ if (error.message.includes("Cipher job failed") || error.message.includes("OperationError")) {
9922
+ console.warn(`Decryption failed for partition resource ${id}, returning basic info`);
9923
+ return {
9924
+ id,
9925
+ _partition: partition,
9926
+ _partitionValues: partitionValues,
9927
+ _decryptionFailed: true,
9928
+ _error: error.message
9929
+ };
9930
+ }
9931
+ throw error;
9932
+ }
9690
9933
  });
9691
- this.emit("list", { partition, partitionValues, count: results.length });
9692
- return results;
9934
+ const validResults = results.filter((item) => item !== null);
9935
+ this.emit("list", { partition, partitionValues, count: validResults.length, errors: errors.length });
9936
+ return validResults;
9693
9937
  }
9694
9938
  /**
9695
9939
  * Get multiple resources by their IDs
@@ -9700,11 +9944,30 @@ ${JSON.stringify(validation, null, 2)}`
9700
9944
  * users.forEach(user => console.log(user.name));
9701
9945
  */
9702
9946
  async getMany(ids) {
9703
- const { results } = await promisePool.PromisePool.for(ids).withConcurrency(this.client.parallelism).process(async (id) => {
9947
+ const { results, errors } = await promisePool.PromisePool.for(ids).withConcurrency(this.client.parallelism).handleError(async (error, id) => {
9948
+ console.warn(`Failed to get resource ${id}:`, error.message);
9949
+ return {
9950
+ id,
9951
+ _error: error.message,
9952
+ _decryptionFailed: error.message.includes("Cipher job failed") || error.message.includes("OperationError")
9953
+ };
9954
+ }).process(async (id) => {
9704
9955
  this.emit("id", id);
9705
- const data = await this.get(id);
9706
- this.emit("data", data);
9707
- return data;
9956
+ try {
9957
+ const data = await this.get(id);
9958
+ this.emit("data", data);
9959
+ return data;
9960
+ } catch (error) {
9961
+ if (error.message.includes("Cipher job failed") || error.message.includes("OperationError")) {
9962
+ console.warn(`Decryption failed for ${id}, returning basic info`);
9963
+ return {
9964
+ id,
9965
+ _decryptionFailed: true,
9966
+ _error: error.message
9967
+ };
9968
+ }
9969
+ throw error;
9970
+ }
9708
9971
  });
9709
9972
  this.emit("getMany", ids.length);
9710
9973
  return results;
@@ -9719,9 +9982,28 @@ ${JSON.stringify(validation, null, 2)}`
9719
9982
  async getAll() {
9720
9983
  let ids = await this.listIds();
9721
9984
  if (ids.length === 0) return [];
9722
- const { results } = await promisePool.PromisePool.for(ids).withConcurrency(this.client.parallelism).process(async (id) => {
9723
- const data = await this.get(id);
9724
- return data;
9985
+ const { results, errors } = await promisePool.PromisePool.for(ids).withConcurrency(this.client.parallelism).handleError(async (error, id) => {
9986
+ console.warn(`Failed to get resource ${id}:`, error.message);
9987
+ return {
9988
+ id,
9989
+ _error: error.message,
9990
+ _decryptionFailed: error.message.includes("Cipher job failed") || error.message.includes("OperationError")
9991
+ };
9992
+ }).process(async (id) => {
9993
+ try {
9994
+ const data = await this.get(id);
9995
+ return data;
9996
+ } catch (error) {
9997
+ if (error.message.includes("Cipher job failed") || error.message.includes("OperationError")) {
9998
+ console.warn(`Decryption failed for ${id}, returning basic info`);
9999
+ return {
10000
+ id,
10001
+ _decryptionFailed: true,
10002
+ _error: error.message
10003
+ };
10004
+ }
10005
+ throw error;
10006
+ }
9725
10007
  });
9726
10008
  this.emit("getAll", results.length);
9727
10009
  return results;
@@ -9908,16 +10190,14 @@ ${JSON.stringify(validation, null, 2)}`
9908
10190
  }
9909
10191
  /**
9910
10192
  * Generate definition hash for this resource
9911
- * @returns {string} SHA256 hash of the schema definition
10193
+ * @returns {string} SHA256 hash of the resource definition (name + attributes)
9912
10194
  */
9913
10195
  getDefinitionHash() {
9914
- const attributes = this.schema.export().attributes;
9915
- const stableAttributes = { ...attributes };
9916
- if (this.options.timestamps) {
9917
- delete stableAttributes.createdAt;
9918
- delete stableAttributes.updatedAt;
9919
- }
9920
- const stableString = jsonStableStringify(stableAttributes);
10196
+ const definition = {
10197
+ attributes: this.attributes,
10198
+ behavior: this.behavior
10199
+ };
10200
+ const stableString = jsonStableStringify(definition);
9921
10201
  return `sha256:${crypto.createHash("sha256").update(stableString).digest("hex")}`;
9922
10202
  }
9923
10203
  /**
@@ -9936,14 +10216,34 @@ ${JSON.stringify(validation, null, 2)}`
9936
10216
  * @returns {Object} Schema object for the version
9937
10217
  */
9938
10218
  async getSchemaForVersion(version) {
9939
- return this.schema;
10219
+ if (version === this.version) {
10220
+ return this.schema;
10221
+ }
10222
+ try {
10223
+ const compatibleSchema = new Schema({
10224
+ name: this.name,
10225
+ attributes: this.attributes,
10226
+ passphrase: this.passphrase,
10227
+ version,
10228
+ options: {
10229
+ ...this.config,
10230
+ // For older versions, be more lenient with decryption
10231
+ autoDecrypt: true,
10232
+ autoEncrypt: true
10233
+ }
10234
+ });
10235
+ return compatibleSchema;
10236
+ } catch (error) {
10237
+ console.warn(`Failed to create compatible schema for version ${version}, using current schema:`, error.message);
10238
+ return this.schema;
10239
+ }
9940
10240
  }
9941
10241
  /**
9942
10242
  * Create partition references after insert
9943
10243
  * @param {Object} data - Inserted object data
9944
10244
  */
9945
10245
  async createPartitionReferences(data) {
9946
- const partitions = this.options.partitions;
10246
+ const partitions = this.config.partitions;
9947
10247
  if (!partitions || Object.keys(partitions).length === 0) {
9948
10248
  return;
9949
10249
  }
@@ -9974,7 +10274,7 @@ ${JSON.stringify(validation, null, 2)}`
9974
10274
  * @param {Object} data - Deleted object data
9975
10275
  */
9976
10276
  async deletePartitionReferences(data) {
9977
- const partitions = this.options.partitions;
10277
+ const partitions = this.config.partitions;
9978
10278
  if (!partitions || Object.keys(partitions).length === 0) {
9979
10279
  return;
9980
10280
  }
@@ -10066,7 +10366,7 @@ ${JSON.stringify(validation, null, 2)}`
10066
10366
  * @param {Object} data - Updated object data
10067
10367
  */
10068
10368
  async updatePartitionReferences(data) {
10069
- const partitions = this.options.partitions;
10369
+ const partitions = this.config.partitions;
10070
10370
  if (!partitions || Object.keys(partitions).length === 0) {
10071
10371
  return;
10072
10372
  }
@@ -10122,10 +10422,10 @@ ${JSON.stringify(validation, null, 2)}`
10122
10422
  * });
10123
10423
  */
10124
10424
  async getFromPartition({ id, partitionName, partitionValues = {} }) {
10125
- const partition = this.options.partitions[partitionName];
10126
- if (!partition) {
10425
+ if (!this.config.partitions || !this.config.partitions[partitionName]) {
10127
10426
  throw new Error(`Partition '${partitionName}' not found`);
10128
10427
  }
10428
+ const partition = this.config.partitions[partitionName];
10129
10429
  const partitionSegments = [];
10130
10430
  const sortedFields = Object.entries(partition.fields).sort(([a], [b]) => a.localeCompare(b));
10131
10431
  for (const [fieldName, rule] of sortedFields) {
@@ -10173,6 +10473,98 @@ ${JSON.stringify(validation, null, 2)}`
10173
10473
  return data;
10174
10474
  }
10175
10475
  }
10476
+ function validateResourceConfig(config) {
10477
+ const errors = [];
10478
+ if (!config.name) {
10479
+ errors.push("Resource 'name' is required");
10480
+ } else if (typeof config.name !== "string") {
10481
+ errors.push("Resource 'name' must be a string");
10482
+ } else if (config.name.trim() === "") {
10483
+ errors.push("Resource 'name' cannot be empty");
10484
+ }
10485
+ if (!config.client) {
10486
+ errors.push("S3 'client' is required");
10487
+ }
10488
+ if (!config.attributes) {
10489
+ errors.push("Resource 'attributes' are required");
10490
+ } else if (typeof config.attributes !== "object" || Array.isArray(config.attributes)) {
10491
+ errors.push("Resource 'attributes' must be an object");
10492
+ } else if (Object.keys(config.attributes).length === 0) {
10493
+ errors.push("Resource 'attributes' cannot be empty");
10494
+ }
10495
+ if (config.version !== void 0 && typeof config.version !== "string") {
10496
+ errors.push("Resource 'version' must be a string");
10497
+ }
10498
+ if (config.behavior !== void 0 && typeof config.behavior !== "string") {
10499
+ errors.push("Resource 'behavior' must be a string");
10500
+ }
10501
+ if (config.passphrase !== void 0 && typeof config.passphrase !== "string") {
10502
+ errors.push("Resource 'passphrase' must be a string");
10503
+ }
10504
+ if (config.parallelism !== void 0) {
10505
+ if (typeof config.parallelism !== "number" || !Number.isInteger(config.parallelism)) {
10506
+ errors.push("Resource 'parallelism' must be an integer");
10507
+ } else if (config.parallelism < 1) {
10508
+ errors.push("Resource 'parallelism' must be greater than 0");
10509
+ }
10510
+ }
10511
+ if (config.observers !== void 0 && !Array.isArray(config.observers)) {
10512
+ errors.push("Resource 'observers' must be an array");
10513
+ }
10514
+ const booleanFields = ["cache", "autoDecrypt", "timestamps", "paranoid", "allNestedObjectsOptional"];
10515
+ for (const field of booleanFields) {
10516
+ if (config[field] !== void 0 && typeof config[field] !== "boolean") {
10517
+ errors.push(`Resource '${field}' must be a boolean`);
10518
+ }
10519
+ }
10520
+ if (config.partitions !== void 0) {
10521
+ if (typeof config.partitions !== "object" || Array.isArray(config.partitions)) {
10522
+ errors.push("Resource 'partitions' must be an object");
10523
+ } else {
10524
+ for (const [partitionName, partitionDef] of Object.entries(config.partitions)) {
10525
+ if (typeof partitionDef !== "object" || Array.isArray(partitionDef)) {
10526
+ errors.push(`Partition '${partitionName}' must be an object`);
10527
+ } else if (!partitionDef.fields) {
10528
+ errors.push(`Partition '${partitionName}' must have a 'fields' property`);
10529
+ } else if (typeof partitionDef.fields !== "object" || Array.isArray(partitionDef.fields)) {
10530
+ errors.push(`Partition '${partitionName}.fields' must be an object`);
10531
+ } else {
10532
+ for (const [fieldName, fieldType] of Object.entries(partitionDef.fields)) {
10533
+ if (typeof fieldType !== "string") {
10534
+ errors.push(`Partition '${partitionName}.fields.${fieldName}' must be a string`);
10535
+ }
10536
+ }
10537
+ }
10538
+ }
10539
+ }
10540
+ }
10541
+ if (config.hooks !== void 0) {
10542
+ if (typeof config.hooks !== "object" || Array.isArray(config.hooks)) {
10543
+ errors.push("Resource 'hooks' must be an object");
10544
+ } else {
10545
+ const validHookEvents = ["preInsert", "afterInsert", "preUpdate", "afterUpdate", "preDelete", "afterDelete"];
10546
+ for (const [event, hooksArr] of Object.entries(config.hooks)) {
10547
+ if (!validHookEvents.includes(event)) {
10548
+ errors.push(`Invalid hook event '${event}'. Valid events: ${validHookEvents.join(", ")}`);
10549
+ } else if (!Array.isArray(hooksArr)) {
10550
+ errors.push(`Resource 'hooks.${event}' must be an array`);
10551
+ } else {
10552
+ for (let i = 0; i < hooksArr.length; i++) {
10553
+ const hook = hooksArr[i];
10554
+ if (typeof hook !== "function") {
10555
+ if (typeof hook === "string") continue;
10556
+ continue;
10557
+ }
10558
+ }
10559
+ }
10560
+ }
10561
+ }
10562
+ }
10563
+ return {
10564
+ isValid: errors.length === 0,
10565
+ errors
10566
+ };
10567
+ }
10176
10568
 
10177
10569
  class Database extends EventEmitter {
10178
10570
  constructor(options) {
@@ -10180,7 +10572,7 @@ ${JSON.stringify(validation, null, 2)}`
10180
10572
  this.version = "1";
10181
10573
  this.s3dbVersion = (() => {
10182
10574
  try {
10183
- return true ? "4.1.7" : "latest";
10575
+ return true ? "4.1.10" : "latest";
10184
10576
  } catch (e) {
10185
10577
  return "latest";
10186
10578
  }
@@ -10193,10 +10585,21 @@ ${JSON.stringify(validation, null, 2)}`
10193
10585
  this.plugins = options.plugins || [];
10194
10586
  this.cache = options.cache;
10195
10587
  this.passphrase = options.passphrase || "secret";
10588
+ let connectionString = options.connectionString;
10589
+ if (!connectionString && (options.bucket || options.accessKeyId || options.secretAccessKey)) {
10590
+ connectionString = ConnectionString.buildFromParams({
10591
+ bucket: options.bucket,
10592
+ region: options.region,
10593
+ accessKeyId: options.accessKeyId,
10594
+ secretAccessKey: options.secretAccessKey,
10595
+ endpoint: options.endpoint,
10596
+ forcePathStyle: options.forcePathStyle
10597
+ });
10598
+ }
10196
10599
  this.client = options.client || new Client({
10197
10600
  verbose: this.verbose,
10198
10601
  parallelism: this.parallelism,
10199
- connectionString: options.connectionString
10602
+ connectionString
10200
10603
  });
10201
10604
  this.bucket = this.client.bucket;
10202
10605
  this.keyPrefix = this.client.keyPrefix;
@@ -10221,15 +10624,18 @@ ${JSON.stringify(validation, null, 2)}`
10221
10624
  name,
10222
10625
  client: this.client,
10223
10626
  version: currentVersion,
10224
- options: {
10225
- ...versionData.options,
10226
- partitions: resourceMetadata.partitions || versionData.options?.partitions || {}
10227
- },
10228
10627
  attributes: versionData.attributes,
10229
10628
  behavior: versionData.behavior || "user-management",
10230
10629
  parallelism: this.parallelism,
10231
10630
  passphrase: this.passphrase,
10232
- observers: [this]
10631
+ observers: [this],
10632
+ cache: this.cache,
10633
+ timestamps: versionData.options?.timestamps || false,
10634
+ partitions: resourceMetadata.partitions || versionData.options?.partitions || {},
10635
+ paranoid: versionData.options?.paranoid !== false,
10636
+ allNestedObjectsOptional: versionData.options?.allNestedObjectsOptional || false,
10637
+ autoDecrypt: versionData.options?.autoDecrypt !== false,
10638
+ hooks: {}
10233
10639
  });
10234
10640
  }
10235
10641
  }
@@ -10298,7 +10704,7 @@ ${JSON.stringify(validation, null, 2)}`
10298
10704
  generateDefinitionHash(definition, behavior = void 0) {
10299
10705
  const attributes = definition.attributes;
10300
10706
  const stableAttributes = { ...attributes };
10301
- if (definition.options?.timestamps) {
10707
+ if (definition.timestamps) {
10302
10708
  delete stableAttributes.createdAt;
10303
10709
  delete stableAttributes.updatedAt;
10304
10710
  }
@@ -10360,14 +10766,21 @@ ${JSON.stringify(validation, null, 2)}`
10360
10766
  }
10361
10767
  metadata.resources[name] = {
10362
10768
  currentVersion: version,
10363
- partitions: resourceDef.options?.partitions || {},
10769
+ partitions: resource.config.partitions || {},
10364
10770
  versions: {
10365
10771
  ...existingResource?.versions,
10366
10772
  // Preserve previous versions
10367
10773
  [version]: {
10368
10774
  hash: definitionHash,
10369
10775
  attributes: resourceDef.attributes,
10370
- options: resourceDef.options,
10776
+ options: {
10777
+ timestamps: resource.config.timestamps,
10778
+ partitions: resource.config.partitions,
10779
+ paranoid: resource.config.paranoid,
10780
+ allNestedObjectsOptional: resource.config.allNestedObjectsOptional,
10781
+ autoDecrypt: resource.config.autoDecrypt,
10782
+ cache: resource.config.cache
10783
+ },
10371
10784
  behavior: resourceDef.behavior || "user-management",
10372
10785
  createdAt: isNewVersion ? (/* @__PURE__ */ new Date()).toISOString() : existingVersionData?.createdAt
10373
10786
  }
@@ -10420,10 +10833,9 @@ ${JSON.stringify(validation, null, 2)}`
10420
10833
  observers: [],
10421
10834
  client: this.client,
10422
10835
  version: "temp",
10423
- options: {
10424
- cache: this.cache,
10425
- ...options
10426
- }
10836
+ passphrase: this.passphrase,
10837
+ cache: this.cache,
10838
+ ...options
10427
10839
  });
10428
10840
  const newHash = this.generateDefinitionHash(tempResource.export(), behavior);
10429
10841
  const existingHash = this.generateDefinitionHash(this.resources[name].export(), this.resources[name].behavior);
@@ -10463,7 +10875,7 @@ ${JSON.stringify(validation, null, 2)}`
10463
10875
  async createResource({ name, attributes, options = {}, behavior = "user-management" }) {
10464
10876
  if (this.resources[name]) {
10465
10877
  const existingResource = this.resources[name];
10466
- Object.assign(existingResource.options, {
10878
+ Object.assign(existingResource.config, {
10467
10879
  cache: this.cache,
10468
10880
  ...options
10469
10881
  });
@@ -10490,10 +10902,9 @@ ${JSON.stringify(validation, null, 2)}`
10490
10902
  observers: [this],
10491
10903
  client: this.client,
10492
10904
  version,
10493
- options: {
10494
- cache: this.cache,
10495
- ...options
10496
- }
10905
+ passphrase: this.passphrase,
10906
+ cache: this.cache,
10907
+ ...options
10497
10908
  });
10498
10909
  this.resources[name] = resource;
10499
10910
  await this.uploadMetadataFile();
@@ -17195,6 +17606,7 @@ ${JSON.stringify(validation, null, 2)}`
17195
17606
  }
17196
17607
  }
17197
17608
 
17609
+ exports.AuthenticationError = AuthenticationError;
17198
17610
  exports.BaseError = BaseError;
17199
17611
  exports.Cache = Cache;
17200
17612
  exports.CachePlugin = CachePlugin;
@@ -17202,6 +17614,8 @@ ${JSON.stringify(validation, null, 2)}`
17202
17614
  exports.ConnectionString = ConnectionString;
17203
17615
  exports.CostsPlugin = CostsPlugin;
17204
17616
  exports.Database = Database;
17617
+ exports.DatabaseError = DatabaseError;
17618
+ exports.EncryptionError = EncryptionError;
17205
17619
  exports.ErrorMap = ErrorMap;
17206
17620
  exports.InvalidResourceItem = InvalidResourceItem;
17207
17621
  exports.MemoryCache = MemoryCache;
@@ -17209,24 +17623,34 @@ ${JSON.stringify(validation, null, 2)}`
17209
17623
  exports.NoSuchBucket = NoSuchBucket;
17210
17624
  exports.NoSuchKey = NoSuchKey;
17211
17625
  exports.NotFound = NotFound;
17626
+ exports.PermissionError = PermissionError;
17212
17627
  exports.Plugin = Plugin;
17213
17628
  exports.PluginObject = PluginObject;
17214
17629
  exports.ResourceIdsPageReader = ResourceIdsPageReader;
17215
17630
  exports.ResourceIdsReader = ResourceIdsReader;
17631
+ exports.ResourceNotFound = ResourceNotFound;
17632
+ exports.ResourceNotFoundError = ResourceNotFound;
17216
17633
  exports.ResourceReader = ResourceReader;
17217
17634
  exports.ResourceWriter = ResourceWriter;
17218
17635
  exports.S3Cache = S3Cache;
17636
+ exports.S3DB = S3db;
17637
+ exports.S3DBError = S3DBError;
17219
17638
  exports.S3_DEFAULT_ENDPOINT = S3_DEFAULT_ENDPOINT;
17220
17639
  exports.S3_DEFAULT_REGION = S3_DEFAULT_REGION;
17221
17640
  exports.S3db = S3db;
17641
+ exports.S3dbError = S3DBError;
17222
17642
  exports.UnknownError = UnknownError;
17643
+ exports.ValidationError = ValidationError;
17223
17644
  exports.Validator = Validator;
17224
17645
  exports.ValidatorManager = ValidatorManager;
17225
17646
  exports.decrypt = decrypt;
17647
+ exports.default = S3db;
17226
17648
  exports.encrypt = encrypt;
17227
17649
  exports.sha256 = sha256;
17228
17650
  exports.streamToString = streamToString;
17229
17651
 
17652
+ Object.defineProperty(exports, '__esModule', { value: true });
17653
+
17230
17654
  return exports;
17231
17655
 
17232
17656
  })({}, nanoid, lodashEs, promisePool, clientS3, crypto, flat, FastestValidator, web);