s3db.js 5.1.0 → 6.0.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.cjs.js CHANGED
@@ -8941,6 +8941,9 @@ function getBehavior(behaviorName) {
8941
8941
  const AVAILABLE_BEHAVIORS = Object.keys(behaviors);
8942
8942
  const DEFAULT_BEHAVIOR = "user-management";
8943
8943
 
8944
+ function createIdGeneratorWithSize(size) {
8945
+ return nanoid.customAlphabet(nanoid.urlAlphabet, size);
8946
+ }
8944
8947
  class Resource extends EventEmitter {
8945
8948
  /**
8946
8949
  * Create a new Resource instance
@@ -8961,6 +8964,8 @@ class Resource extends EventEmitter {
8961
8964
  * @param {boolean} [config.allNestedObjectsOptional=false] - Make nested objects optional
8962
8965
  * @param {Object} [config.hooks={}] - Custom hooks
8963
8966
  * @param {Object} [config.options={}] - Additional options
8967
+ * @param {Function} [config.idGenerator] - Custom ID generator function
8968
+ * @param {number} [config.idSize=22] - Size for auto-generated IDs
8964
8969
  * @example
8965
8970
  * const users = new Resource({
8966
8971
  * name: 'users',
@@ -8985,6 +8990,30 @@ class Resource extends EventEmitter {
8985
8990
  * }]
8986
8991
  * }
8987
8992
  * });
8993
+ *
8994
+ * // With custom ID size
8995
+ * const shortIdUsers = new Resource({
8996
+ * name: 'users',
8997
+ * client: s3Client,
8998
+ * attributes: { name: 'string|required' },
8999
+ * idSize: 8 // Generate 8-character IDs
9000
+ * });
9001
+ *
9002
+ * // With custom ID generator function
9003
+ * const customIdUsers = new Resource({
9004
+ * name: 'users',
9005
+ * client: s3Client,
9006
+ * attributes: { name: 'string|required' },
9007
+ * idGenerator: () => `user_${Date.now()}_${Math.random().toString(36).substr(2, 5)}`
9008
+ * });
9009
+ *
9010
+ * // With custom ID generator using size parameter
9011
+ * const longIdUsers = new Resource({
9012
+ * name: 'users',
9013
+ * client: s3Client,
9014
+ * attributes: { name: 'string|required' },
9015
+ * idGenerator: 32 // Generate 32-character IDs (same as idSize: 32)
9016
+ * });
8988
9017
  */
8989
9018
  constructor(config) {
8990
9019
  super();
@@ -9008,7 +9037,9 @@ ${validation.errors.join("\n")}`);
9008
9037
  partitions = {},
9009
9038
  paranoid = true,
9010
9039
  allNestedObjectsOptional = true,
9011
- hooks = {}
9040
+ hooks = {},
9041
+ idGenerator: customIdGenerator,
9042
+ idSize = 22
9012
9043
  } = config;
9013
9044
  this.name = name;
9014
9045
  this.client = client;
@@ -9017,6 +9048,7 @@ ${validation.errors.join("\n")}`);
9017
9048
  this.observers = observers;
9018
9049
  this.parallelism = parallelism;
9019
9050
  this.passphrase = passphrase ?? "secret";
9051
+ this.idGenerator = this.configureIdGenerator(customIdGenerator, idSize);
9020
9052
  this.config = {
9021
9053
  cache,
9022
9054
  hooks,
@@ -9048,6 +9080,25 @@ ${validation.errors.join("\n")}`);
9048
9080
  }
9049
9081
  }
9050
9082
  }
9083
+ /**
9084
+ * Configure ID generator based on provided options
9085
+ * @param {Function|number} customIdGenerator - Custom ID generator function or size
9086
+ * @param {number} idSize - Size for auto-generated IDs
9087
+ * @returns {Function} Configured ID generator function
9088
+ * @private
9089
+ */
9090
+ configureIdGenerator(customIdGenerator, idSize) {
9091
+ if (typeof customIdGenerator === "function") {
9092
+ return customIdGenerator;
9093
+ }
9094
+ if (typeof customIdGenerator === "number" && customIdGenerator > 0) {
9095
+ return createIdGeneratorWithSize(customIdGenerator);
9096
+ }
9097
+ if (typeof idSize === "number" && idSize > 0) {
9098
+ return createIdGeneratorWithSize(idSize);
9099
+ }
9100
+ return idGenerator;
9101
+ }
9051
9102
  /**
9052
9103
  * Get resource options (for backward compatibility with tests)
9053
9104
  */
@@ -9396,7 +9447,7 @@ ${validation.errors.join("\n")}`);
9396
9447
  validation: errors
9397
9448
  });
9398
9449
  }
9399
- if (!id && id !== 0) id = idGenerator();
9450
+ if (!id && id !== 0) id = this.idGenerator();
9400
9451
  const mappedData = await this.schema.mapper(validated);
9401
9452
  const behaviorImpl = getBehavior(this.behavior);
9402
9453
  const { mappedData: processedMetadata, body } = await behaviorImpl.handleInsert({
@@ -9405,10 +9456,19 @@ ${validation.errors.join("\n")}`);
9405
9456
  mappedData
9406
9457
  });
9407
9458
  const key = this.getResourceKey(id);
9459
+ let contentType = void 0;
9460
+ if (body && body !== "") {
9461
+ try {
9462
+ JSON.parse(body);
9463
+ contentType = "application/json";
9464
+ } catch {
9465
+ }
9466
+ }
9408
9467
  await this.client.putObject({
9409
9468
  metadata: processedMetadata,
9410
9469
  key,
9411
- body
9470
+ body,
9471
+ contentType
9412
9472
  });
9413
9473
  const final = lodashEs.merge({ id }, validated);
9414
9474
  await this.executeHooks("afterInsert", final);
@@ -9603,10 +9663,18 @@ ${validation.errors.join("\n")}`);
9603
9663
  } catch (error) {
9604
9664
  }
9605
9665
  }
9666
+ let finalContentType = existingContentType;
9667
+ if (finalBody && finalBody !== "" && !finalContentType) {
9668
+ try {
9669
+ JSON.parse(finalBody);
9670
+ finalContentType = "application/json";
9671
+ } catch {
9672
+ }
9673
+ }
9606
9674
  await this.client.putObject({
9607
9675
  key,
9608
9676
  body: finalBody,
9609
- contentType: existingContentType,
9677
+ contentType: finalContentType,
9610
9678
  metadata: processedMetadata
9611
9679
  });
9612
9680
  validated.id = id;
@@ -9703,9 +9771,7 @@ ${validation.errors.join("\n")}`);
9703
9771
  } else {
9704
9772
  prefix = `resource=${this.name}/v=${this.version}`;
9705
9773
  }
9706
- const count = await this.client.count({
9707
- prefix
9708
- });
9774
+ const count = await this.client.count({ prefix });
9709
9775
  this.emit("count", count);
9710
9776
  return count;
9711
9777
  }
@@ -10373,10 +10439,19 @@ ${validation.errors.join("\n")}`);
10373
10439
  ...processedMetadata,
10374
10440
  _version: this.version
10375
10441
  };
10442
+ let contentType = void 0;
10443
+ if (body && body !== "") {
10444
+ try {
10445
+ JSON.parse(body);
10446
+ contentType = "application/json";
10447
+ } catch {
10448
+ }
10449
+ }
10376
10450
  await this.client.putObject({
10377
10451
  key: partitionKey,
10378
10452
  metadata: partitionMetadata,
10379
- body
10453
+ body,
10454
+ contentType
10380
10455
  });
10381
10456
  }
10382
10457
  }
@@ -10548,10 +10623,19 @@ ${validation.errors.join("\n")}`);
10548
10623
  _version: this.version
10549
10624
  };
10550
10625
  if (partitionMetadata.undefined !== void 0) delete partitionMetadata.undefined;
10626
+ let contentType = void 0;
10627
+ if (body && body !== "") {
10628
+ try {
10629
+ JSON.parse(body);
10630
+ contentType = "application/json";
10631
+ } catch {
10632
+ }
10633
+ }
10551
10634
  await this.client.putObject({
10552
10635
  key: newPartitionKey,
10553
10636
  metadata: partitionMetadata,
10554
- body
10637
+ body,
10638
+ contentType
10555
10639
  });
10556
10640
  } catch (error) {
10557
10641
  console.warn(`New partition object could not be created for ${partitionName}:`, error.message);
@@ -10574,10 +10658,19 @@ ${validation.errors.join("\n")}`);
10574
10658
  _version: this.version
10575
10659
  };
10576
10660
  if (partitionMetadata.undefined !== void 0) delete partitionMetadata.undefined;
10661
+ let contentType = void 0;
10662
+ if (body && body !== "") {
10663
+ try {
10664
+ JSON.parse(body);
10665
+ contentType = "application/json";
10666
+ } catch {
10667
+ }
10668
+ }
10577
10669
  await this.client.putObject({
10578
10670
  key: newPartitionKey,
10579
10671
  metadata: partitionMetadata,
10580
- body
10672
+ body,
10673
+ contentType
10581
10674
  });
10582
10675
  } catch (error) {
10583
10676
  console.warn(`Partition object could not be updated for ${partitionName}:`, error.message);
@@ -10612,11 +10705,20 @@ ${validation.errors.join("\n")}`);
10612
10705
  ...processedMetadata,
10613
10706
  _version: this.version
10614
10707
  };
10708
+ let contentType = void 0;
10709
+ if (body && body !== "") {
10710
+ try {
10711
+ JSON.parse(body);
10712
+ contentType = "application/json";
10713
+ } catch {
10714
+ }
10715
+ }
10615
10716
  try {
10616
10717
  await this.client.putObject({
10617
10718
  key: partitionKey,
10618
10719
  metadata: partitionMetadata,
10619
- body
10720
+ body,
10721
+ contentType
10620
10722
  });
10621
10723
  } catch (error) {
10622
10724
  console.warn(`Partition object could not be updated for ${partitionName}:`, error.message);
@@ -10745,6 +10847,20 @@ function validateResourceConfig(config) {
10745
10847
  errors.push(`Resource '${field}' must be a boolean`);
10746
10848
  }
10747
10849
  }
10850
+ if (config.idGenerator !== void 0) {
10851
+ if (typeof config.idGenerator !== "function" && typeof config.idGenerator !== "number") {
10852
+ errors.push("Resource 'idGenerator' must be a function or a number (size)");
10853
+ } else if (typeof config.idGenerator === "number" && config.idGenerator <= 0) {
10854
+ errors.push("Resource 'idGenerator' size must be greater than 0");
10855
+ }
10856
+ }
10857
+ if (config.idSize !== void 0) {
10858
+ if (typeof config.idSize !== "number" || !Number.isInteger(config.idSize)) {
10859
+ errors.push("Resource 'idSize' must be an integer");
10860
+ } else if (config.idSize <= 0) {
10861
+ errors.push("Resource 'idSize' must be greater than 0");
10862
+ }
10863
+ }
10748
10864
  if (config.partitions !== void 0) {
10749
10865
  if (typeof config.partitions !== "object" || Array.isArray(config.partitions)) {
10750
10866
  errors.push("Resource 'partitions' must be an object");
@@ -10800,7 +10916,7 @@ class Database extends EventEmitter {
10800
10916
  this.version = "1";
10801
10917
  this.s3dbVersion = (() => {
10802
10918
  try {
10803
- return true ? "5.0.0" : "latest";
10919
+ return true ? "5.2.0" : "latest";
10804
10920
  } catch (e) {
10805
10921
  return "latest";
10806
10922
  }