s3db.js 8.1.0 → 8.1.1

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
@@ -605,6 +605,12 @@ ${JSON.stringify(validation, null, 2)}`,
605
605
  const passwordAlphabet = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
606
606
  const passwordGenerator = nanoid.customAlphabet(passwordAlphabet, 16);
607
607
 
608
+ var id = /*#__PURE__*/Object.freeze({
609
+ __proto__: null,
610
+ idGenerator: idGenerator,
611
+ passwordGenerator: passwordGenerator
612
+ });
613
+
608
614
  var domain;
609
615
 
610
616
  // This constructor is used to store event handlers. Instantiating this is
@@ -1298,14 +1304,15 @@ ${JSON.stringify(validation, null, 2)}`,
1298
1304
  }
1299
1305
  setupResourceAuditing(resource) {
1300
1306
  resource.on("insert", async (data) => {
1307
+ const partitionValues = this.config.includePartitions ? this.getPartitionValues(data, resource) : null;
1301
1308
  await this.logAudit({
1302
1309
  resourceName: resource.name,
1303
1310
  operation: "insert",
1304
1311
  recordId: data.id || "auto-generated",
1305
1312
  oldData: null,
1306
1313
  newData: this.config.includeData ? JSON.stringify(this.truncateData(data)) : null,
1307
- partition: this.config.includePartitions && this.getPartitionValues(data, resource) ? this.getPrimaryPartition(this.getPartitionValues(data, resource)) : null,
1308
- partitionValues: this.config.includePartitions && this.getPartitionValues(data, resource) ? JSON.stringify(this.getPartitionValues(data, resource)) : null
1314
+ partition: partitionValues ? this.getPrimaryPartition(partitionValues) : null,
1315
+ partitionValues: partitionValues ? JSON.stringify(partitionValues) : null
1309
1316
  });
1310
1317
  });
1311
1318
  resource.on("update", async (data) => {
@@ -1314,14 +1321,15 @@ ${JSON.stringify(validation, null, 2)}`,
1314
1321
  const [ok, err, fetched] = await try_fn_default(() => resource.get(data.id));
1315
1322
  if (ok) oldData = fetched;
1316
1323
  }
1324
+ const partitionValues = this.config.includePartitions ? this.getPartitionValues(data, resource) : null;
1317
1325
  await this.logAudit({
1318
1326
  resourceName: resource.name,
1319
1327
  operation: "update",
1320
1328
  recordId: data.id,
1321
1329
  oldData: oldData && this.config.includeData ? JSON.stringify(this.truncateData(oldData)) : null,
1322
1330
  newData: this.config.includeData ? JSON.stringify(this.truncateData(data)) : null,
1323
- partition: this.config.includePartitions && this.getPartitionValues(data, resource) ? this.getPrimaryPartition(this.getPartitionValues(data, resource)) : null,
1324
- partitionValues: this.config.includePartitions && this.getPartitionValues(data, resource) ? JSON.stringify(this.getPartitionValues(data, resource)) : null
1331
+ partition: partitionValues ? this.getPrimaryPartition(partitionValues) : null,
1332
+ partitionValues: partitionValues ? JSON.stringify(partitionValues) : null
1325
1333
  });
1326
1334
  });
1327
1335
  resource.on("delete", async (data) => {
@@ -1330,16 +1338,49 @@ ${JSON.stringify(validation, null, 2)}`,
1330
1338
  const [ok, err, fetched] = await try_fn_default(() => resource.get(data.id));
1331
1339
  if (ok) oldData = fetched;
1332
1340
  }
1341
+ const partitionValues = oldData && this.config.includePartitions ? this.getPartitionValues(oldData, resource) : null;
1333
1342
  await this.logAudit({
1334
1343
  resourceName: resource.name,
1335
1344
  operation: "delete",
1336
1345
  recordId: data.id,
1337
1346
  oldData: oldData && this.config.includeData ? JSON.stringify(this.truncateData(oldData)) : null,
1338
1347
  newData: null,
1339
- partition: oldData && this.config.includePartitions && this.getPartitionValues(oldData, resource) ? this.getPrimaryPartition(this.getPartitionValues(oldData, resource)) : null,
1340
- partitionValues: oldData && this.config.includePartitions && this.getPartitionValues(oldData, resource) ? JSON.stringify(this.getPartitionValues(oldData, resource)) : null
1348
+ partition: partitionValues ? this.getPrimaryPartition(partitionValues) : null,
1349
+ partitionValues: partitionValues ? JSON.stringify(partitionValues) : null
1341
1350
  });
1342
1351
  });
1352
+ const originalDeleteMany = resource.deleteMany.bind(resource);
1353
+ const plugin = this;
1354
+ resource.deleteMany = async function(ids) {
1355
+ const objectsToDelete = [];
1356
+ if (plugin.config.includeData) {
1357
+ for (const id of ids) {
1358
+ const [ok, err, fetched] = await try_fn_default(() => resource.get(id));
1359
+ if (ok) {
1360
+ objectsToDelete.push(fetched);
1361
+ } else {
1362
+ objectsToDelete.push({ id });
1363
+ }
1364
+ }
1365
+ } else {
1366
+ objectsToDelete.push(...ids.map((id) => ({ id })));
1367
+ }
1368
+ const result = await originalDeleteMany(ids);
1369
+ for (const oldData of objectsToDelete) {
1370
+ const partitionValues = oldData && plugin.config.includePartitions ? plugin.getPartitionValues(oldData, resource) : null;
1371
+ await plugin.logAudit({
1372
+ resourceName: resource.name,
1373
+ operation: "deleteMany",
1374
+ recordId: oldData.id,
1375
+ oldData: oldData && plugin.config.includeData ? JSON.stringify(plugin.truncateData(oldData)) : null,
1376
+ newData: null,
1377
+ partition: partitionValues ? plugin.getPrimaryPartition(partitionValues) : null,
1378
+ partitionValues: partitionValues ? JSON.stringify(partitionValues) : null
1379
+ });
1380
+ }
1381
+ return result;
1382
+ };
1383
+ resource._originalDeleteMany = originalDeleteMany;
1343
1384
  }
1344
1385
  // Backward compatibility for tests
1345
1386
  installEventListenersForResource(resource) {
@@ -1377,9 +1418,13 @@ ${JSON.stringify(validation, null, 2)}`,
1377
1418
  }
1378
1419
  }
1379
1420
  getPartitionValues(data, resource) {
1380
- if (!this.config.includePartitions || !resource.partitions) return null;
1421
+ if (!this.config.includePartitions) return null;
1422
+ const partitions = resource.config?.partitions || resource.partitions;
1423
+ if (!partitions) {
1424
+ return null;
1425
+ }
1381
1426
  const partitionValues = {};
1382
- for (const [partitionName, partitionConfig] of Object.entries(resource.partitions)) {
1427
+ for (const [partitionName, partitionConfig] of Object.entries(partitions)) {
1383
1428
  const values = {};
1384
1429
  for (const field of Object.keys(partitionConfig.fields)) {
1385
1430
  values[field] = this.getNestedFieldValue(data, field);
@@ -1422,7 +1467,7 @@ ${JSON.stringify(validation, null, 2)}`,
1422
1467
  }
1423
1468
  async getAuditLogs(options = {}) {
1424
1469
  if (!this.auditResource) return [];
1425
- const { resourceName, operation, recordId, partition, startDate, endDate, limit = 100 } = options;
1470
+ const { resourceName, operation, recordId, partition, startDate, endDate, limit = 100, offset = 0 } = options;
1426
1471
  let query = {};
1427
1472
  if (resourceName) query.resourceName = resourceName;
1428
1473
  if (operation) query.operation = operation;
@@ -1433,7 +1478,7 @@ ${JSON.stringify(validation, null, 2)}`,
1433
1478
  if (startDate) query.timestamp.$gte = startDate;
1434
1479
  if (endDate) query.timestamp.$lte = endDate;
1435
1480
  }
1436
- const result = await this.auditResource.page({ query, limit });
1481
+ const result = await this.auditResource.page({ query, limit, offset });
1437
1482
  return result.items || [];
1438
1483
  }
1439
1484
  async getRecordHistory(resourceName, recordId) {
@@ -6548,7 +6593,7 @@ ${JSON.stringify(validation, null, 2)}`,
6548
6593
  ttl = 0,
6549
6594
  prefix = void 0
6550
6595
  }) {
6551
- super({ client, keyPrefix, ttl, prefix });
6596
+ super();
6552
6597
  this.client = client;
6553
6598
  this.keyPrefix = keyPrefix;
6554
6599
  this.config.ttl = ttl;
@@ -7229,6 +7274,26 @@ ${JSON.stringify(validation, null, 2)}`,
7229
7274
  }
7230
7275
  return super._set(key, data);
7231
7276
  }
7277
+ /**
7278
+ * Public set method with partition support
7279
+ */
7280
+ async set(resource, action, data, options = {}) {
7281
+ if (typeof resource === "string" && typeof action === "string" && options.partition) {
7282
+ const key = this._getPartitionCacheKey(resource, action, options.partition, options.partitionValues, options.params);
7283
+ return this._set(key, data, { resource, action, ...options });
7284
+ }
7285
+ return super.set(resource, action);
7286
+ }
7287
+ /**
7288
+ * Public get method with partition support
7289
+ */
7290
+ async get(resource, action, options = {}) {
7291
+ if (typeof resource === "string" && typeof action === "string" && options.partition) {
7292
+ const key = this._getPartitionCacheKey(resource, action, options.partition, options.partitionValues, options.params);
7293
+ return this._get(key, { resource, action, ...options });
7294
+ }
7295
+ return super.get(resource);
7296
+ }
7232
7297
  /**
7233
7298
  * Enhanced get method with partition awareness
7234
7299
  */
@@ -10791,7 +10856,7 @@ ${JSON.stringify(validation, null, 2)}`,
10791
10856
  if (totalSize > effectiveLimit) {
10792
10857
  throw new Error(`S3 metadata size exceeds 2KB limit. Current size: ${totalSize} bytes, effective limit: ${effectiveLimit} bytes, absolute limit: ${S3_METADATA_LIMIT_BYTES} bytes`);
10793
10858
  }
10794
- return { mappedData, body: JSON.stringify(mappedData) };
10859
+ return { mappedData, body: "" };
10795
10860
  }
10796
10861
  async function handleUpdate$4({ resource, id, data, mappedData, originalData }) {
10797
10862
  const totalSize = calculateTotalSize(mappedData);
@@ -10854,8 +10919,9 @@ ${JSON.stringify(validation, null, 2)}`,
10854
10919
  excess: totalSize - 2047,
10855
10920
  data: originalData || data
10856
10921
  });
10922
+ return { mappedData: { _v: mappedData._v }, body: JSON.stringify(mappedData) };
10857
10923
  }
10858
- return { mappedData, body: JSON.stringify(data) };
10924
+ return { mappedData, body: "" };
10859
10925
  }
10860
10926
  async function handleUpdate$3({ resource, id, data, mappedData, originalData }) {
10861
10927
  const totalSize = calculateTotalSize(mappedData);
@@ -10902,6 +10968,18 @@ ${JSON.stringify(validation, null, 2)}`,
10902
10968
  return { mappedData, body: JSON.stringify(data) };
10903
10969
  }
10904
10970
  async function handleGet$3({ resource, metadata, body }) {
10971
+ if (body && body.trim() !== "") {
10972
+ try {
10973
+ const bodyData = JSON.parse(body);
10974
+ const mergedData = {
10975
+ ...bodyData,
10976
+ ...metadata
10977
+ };
10978
+ return { metadata: mergedData, body };
10979
+ } catch (error) {
10980
+ return { metadata, body };
10981
+ }
10982
+ }
10905
10983
  return { metadata, body };
10906
10984
  }
10907
10985
 
@@ -10969,7 +11047,7 @@ ${JSON.stringify(validation, null, 2)}`,
10969
11047
  if (truncated) {
10970
11048
  resultFields[TRUNCATED_FLAG] = TRUNCATED_FLAG_VALUE;
10971
11049
  }
10972
- return { mappedData: resultFields, body: JSON.stringify(mappedData) };
11050
+ return { mappedData: resultFields, body: "" };
10973
11051
  }
10974
11052
  async function handleUpdate$2({ resource, id, data, mappedData, originalData }) {
10975
11053
  return handleInsert$2({ resource, data, mappedData, originalData });
@@ -11059,7 +11137,6 @@ ${JSON.stringify(validation, null, 2)}`,
11059
11137
  }
11060
11138
  const hasOverflow = Object.keys(bodyFields).length > 0;
11061
11139
  let body = hasOverflow ? JSON.stringify(bodyFields) : "";
11062
- if (!hasOverflow) body = "{}";
11063
11140
  return { mappedData: metadataFields, body };
11064
11141
  }
11065
11142
  async function handleUpdate$1({ resource, id, data, mappedData, originalData }) {
@@ -11711,16 +11788,16 @@ ${errorDetails}`,
11711
11788
  * email: 'john@example.com'
11712
11789
  * });
11713
11790
  */
11714
- async insert({ id, ...attributes }) {
11715
- const exists = await this.exists(id);
11716
- if (exists) throw new Error(`Resource with id '${id}' already exists`);
11717
- this.getResourceKey(id || "(auto)");
11791
+ async insert({ id: id$1, ...attributes }) {
11792
+ const exists = await this.exists(id$1);
11793
+ if (exists) throw new Error(`Resource with id '${id$1}' already exists`);
11794
+ this.getResourceKey(id$1 || "(auto)");
11718
11795
  if (this.options.timestamps) {
11719
11796
  attributes.createdAt = (/* @__PURE__ */ new Date()).toISOString();
11720
11797
  attributes.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
11721
11798
  }
11722
11799
  const attributesWithDefaults = this.applyDefaults(attributes);
11723
- const completeData = { id, ...attributesWithDefaults };
11800
+ const completeData = { id: id$1, ...attributesWithDefaults };
11724
11801
  const preProcessedData = await this.executeHooks("beforeInsert", completeData);
11725
11802
  const extraProps = Object.keys(preProcessedData).filter(
11726
11803
  (k) => !(k in completeData) || preProcessedData[k] !== completeData[k]
@@ -11744,7 +11821,14 @@ ${errorDetails}`,
11744
11821
  }
11745
11822
  const { id: validatedId, ...validatedAttributes } = validated;
11746
11823
  Object.assign(validatedAttributes, extraData);
11747
- const finalId = validatedId || id || this.idGenerator();
11824
+ let finalId = validatedId || id$1;
11825
+ if (!finalId) {
11826
+ finalId = this.idGenerator();
11827
+ if (!finalId || finalId.trim() === "") {
11828
+ const { idGenerator } = await Promise.resolve().then(function () { return id; });
11829
+ finalId = idGenerator();
11830
+ }
11831
+ }
11748
11832
  const mappedData = await this.schema.mapper(validatedAttributes);
11749
11833
  mappedData._v = String(this.version);
11750
11834
  const behaviorImpl = getBehavior(this.behavior);
@@ -11789,26 +11873,11 @@ ${errorDetails}`,
11789
11873
  errPut.excess = excess;
11790
11874
  throw new ResourceError("metadata headers exceed", { resourceName: this.name, operation: "insert", id: finalId, totalSize, effectiveLimit, excess, suggestion: "Reduce metadata size or number of fields." });
11791
11875
  }
11792
- throw mapAwsError(errPut, {
11793
- bucket: this.client.config.bucket,
11794
- key,
11795
- resourceName: this.name,
11796
- operation: "insert",
11797
- id: finalId
11798
- });
11876
+ throw errPut;
11799
11877
  }
11800
- let insertedData = await this.composeFullObjectFromWrite({
11801
- id: finalId,
11802
- metadata: finalMetadata,
11803
- body,
11804
- behavior: this.behavior
11805
- });
11806
- const finalResult = await this.executeHooks("afterInsert", insertedData);
11807
- this.emit("insert", {
11808
- ...insertedData,
11809
- $before: { ...completeData },
11810
- $after: { ...finalResult }
11811
- });
11878
+ const insertedObject = await this.get(finalId);
11879
+ const finalResult = await this.executeHooks("afterInsert", insertedObject);
11880
+ this.emit("insert", finalResult);
11812
11881
  return finalResult;
11813
11882
  }
11814
11883
  /**
@@ -11817,7 +11886,7 @@ ${errorDetails}`,
11817
11886
  * @returns {Promise<Object>} The resource object with all attributes and metadata
11818
11887
  * @example
11819
11888
  * const user = await resource.get('user-123');
11820
- */
11889
+ */
11821
11890
  async get(id) {
11822
11891
  if (lodashEs.isObject(id)) throw new Error(`id cannot be an object`);
11823
11892
  if (lodashEs.isEmpty(id)) throw new Error("id cannot be empty");
@@ -11832,17 +11901,6 @@ ${errorDetails}`,
11832
11901
  id
11833
11902
  });
11834
11903
  }
11835
- if (request.ContentLength === 0) {
11836
- const noContentErr = new Error(`No such key: ${key} [bucket:${this.client.config.bucket}]`);
11837
- noContentErr.name = "NoSuchKey";
11838
- throw mapAwsError(noContentErr, {
11839
- bucket: this.client.config.bucket,
11840
- key,
11841
- resourceName: this.name,
11842
- operation: "get",
11843
- id
11844
- });
11845
- }
11846
11904
  const objectVersionRaw = request.Metadata?._v || this.version;
11847
11905
  const objectVersion = typeof objectVersionRaw === "string" && objectVersionRaw.startsWith("v") ? objectVersionRaw.slice(1) : objectVersionRaw;
11848
11906
  const schema = await this.getSchemaForVersion(objectVersion);
@@ -13181,6 +13239,18 @@ ${errorDetails}`,
13181
13239
  });
13182
13240
  return result2;
13183
13241
  }
13242
+ if (behavior === "user-managed" && body && body.trim() !== "") {
13243
+ const [okBody, errBody, parsedBody] = await try_fn_default(() => Promise.resolve(JSON.parse(body)));
13244
+ if (okBody) {
13245
+ const [okUnmap, errUnmap, unmappedBody] = await try_fn_default(() => this.schema.unmapper(parsedBody));
13246
+ const bodyData = okUnmap ? unmappedBody : {};
13247
+ const merged = { ...bodyData, ...unmappedMetadata, id };
13248
+ Object.keys(merged).forEach((k) => {
13249
+ merged[k] = fixValue(merged[k]);
13250
+ });
13251
+ return filterInternalFields(merged);
13252
+ }
13253
+ }
13184
13254
  const result = { ...unmappedMetadata, id };
13185
13255
  Object.keys(result).forEach((k) => {
13186
13256
  result[k] = fixValue(result[k]);
@@ -13423,7 +13493,7 @@ ${errorDetails}`,
13423
13493
  this.id = idGenerator(7);
13424
13494
  this.version = "1";
13425
13495
  this.s3dbVersion = (() => {
13426
- const [ok, err, version] = try_fn_default(() => true ? "8.1.0" : "latest");
13496
+ const [ok, err, version] = try_fn_default(() => true ? "8.1.1" : "latest");
13427
13497
  return ok ? version : "latest";
13428
13498
  })();
13429
13499
  this.resources = {};