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.es.js CHANGED
@@ -615,6 +615,12 @@ const idGenerator = customAlphabet(urlAlphabet, 22);
615
615
  const passwordAlphabet = "ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz23456789";
616
616
  const passwordGenerator = customAlphabet(passwordAlphabet, 16);
617
617
 
618
+ var id = /*#__PURE__*/Object.freeze({
619
+ __proto__: null,
620
+ idGenerator: idGenerator,
621
+ passwordGenerator: passwordGenerator
622
+ });
623
+
618
624
  var domain;
619
625
 
620
626
  // This constructor is used to store event handlers. Instantiating this is
@@ -1308,14 +1314,15 @@ class AuditPlugin extends plugin_class_default {
1308
1314
  }
1309
1315
  setupResourceAuditing(resource) {
1310
1316
  resource.on("insert", async (data) => {
1317
+ const partitionValues = this.config.includePartitions ? this.getPartitionValues(data, resource) : null;
1311
1318
  await this.logAudit({
1312
1319
  resourceName: resource.name,
1313
1320
  operation: "insert",
1314
1321
  recordId: data.id || "auto-generated",
1315
1322
  oldData: null,
1316
1323
  newData: this.config.includeData ? JSON.stringify(this.truncateData(data)) : null,
1317
- partition: this.config.includePartitions && this.getPartitionValues(data, resource) ? this.getPrimaryPartition(this.getPartitionValues(data, resource)) : null,
1318
- partitionValues: this.config.includePartitions && this.getPartitionValues(data, resource) ? JSON.stringify(this.getPartitionValues(data, resource)) : null
1324
+ partition: partitionValues ? this.getPrimaryPartition(partitionValues) : null,
1325
+ partitionValues: partitionValues ? JSON.stringify(partitionValues) : null
1319
1326
  });
1320
1327
  });
1321
1328
  resource.on("update", async (data) => {
@@ -1324,14 +1331,15 @@ class AuditPlugin extends plugin_class_default {
1324
1331
  const [ok, err, fetched] = await try_fn_default(() => resource.get(data.id));
1325
1332
  if (ok) oldData = fetched;
1326
1333
  }
1334
+ const partitionValues = this.config.includePartitions ? this.getPartitionValues(data, resource) : null;
1327
1335
  await this.logAudit({
1328
1336
  resourceName: resource.name,
1329
1337
  operation: "update",
1330
1338
  recordId: data.id,
1331
1339
  oldData: oldData && this.config.includeData ? JSON.stringify(this.truncateData(oldData)) : null,
1332
1340
  newData: this.config.includeData ? JSON.stringify(this.truncateData(data)) : null,
1333
- partition: this.config.includePartitions && this.getPartitionValues(data, resource) ? this.getPrimaryPartition(this.getPartitionValues(data, resource)) : null,
1334
- partitionValues: this.config.includePartitions && this.getPartitionValues(data, resource) ? JSON.stringify(this.getPartitionValues(data, resource)) : null
1341
+ partition: partitionValues ? this.getPrimaryPartition(partitionValues) : null,
1342
+ partitionValues: partitionValues ? JSON.stringify(partitionValues) : null
1335
1343
  });
1336
1344
  });
1337
1345
  resource.on("delete", async (data) => {
@@ -1340,16 +1348,49 @@ class AuditPlugin extends plugin_class_default {
1340
1348
  const [ok, err, fetched] = await try_fn_default(() => resource.get(data.id));
1341
1349
  if (ok) oldData = fetched;
1342
1350
  }
1351
+ const partitionValues = oldData && this.config.includePartitions ? this.getPartitionValues(oldData, resource) : null;
1343
1352
  await this.logAudit({
1344
1353
  resourceName: resource.name,
1345
1354
  operation: "delete",
1346
1355
  recordId: data.id,
1347
1356
  oldData: oldData && this.config.includeData ? JSON.stringify(this.truncateData(oldData)) : null,
1348
1357
  newData: null,
1349
- partition: oldData && this.config.includePartitions && this.getPartitionValues(oldData, resource) ? this.getPrimaryPartition(this.getPartitionValues(oldData, resource)) : null,
1350
- partitionValues: oldData && this.config.includePartitions && this.getPartitionValues(oldData, resource) ? JSON.stringify(this.getPartitionValues(oldData, resource)) : null
1358
+ partition: partitionValues ? this.getPrimaryPartition(partitionValues) : null,
1359
+ partitionValues: partitionValues ? JSON.stringify(partitionValues) : null
1351
1360
  });
1352
1361
  });
1362
+ const originalDeleteMany = resource.deleteMany.bind(resource);
1363
+ const plugin = this;
1364
+ resource.deleteMany = async function(ids) {
1365
+ const objectsToDelete = [];
1366
+ if (plugin.config.includeData) {
1367
+ for (const id of ids) {
1368
+ const [ok, err, fetched] = await try_fn_default(() => resource.get(id));
1369
+ if (ok) {
1370
+ objectsToDelete.push(fetched);
1371
+ } else {
1372
+ objectsToDelete.push({ id });
1373
+ }
1374
+ }
1375
+ } else {
1376
+ objectsToDelete.push(...ids.map((id) => ({ id })));
1377
+ }
1378
+ const result = await originalDeleteMany(ids);
1379
+ for (const oldData of objectsToDelete) {
1380
+ const partitionValues = oldData && plugin.config.includePartitions ? plugin.getPartitionValues(oldData, resource) : null;
1381
+ await plugin.logAudit({
1382
+ resourceName: resource.name,
1383
+ operation: "deleteMany",
1384
+ recordId: oldData.id,
1385
+ oldData: oldData && plugin.config.includeData ? JSON.stringify(plugin.truncateData(oldData)) : null,
1386
+ newData: null,
1387
+ partition: partitionValues ? plugin.getPrimaryPartition(partitionValues) : null,
1388
+ partitionValues: partitionValues ? JSON.stringify(partitionValues) : null
1389
+ });
1390
+ }
1391
+ return result;
1392
+ };
1393
+ resource._originalDeleteMany = originalDeleteMany;
1353
1394
  }
1354
1395
  // Backward compatibility for tests
1355
1396
  installEventListenersForResource(resource) {
@@ -1387,9 +1428,13 @@ class AuditPlugin extends plugin_class_default {
1387
1428
  }
1388
1429
  }
1389
1430
  getPartitionValues(data, resource) {
1390
- if (!this.config.includePartitions || !resource.partitions) return null;
1431
+ if (!this.config.includePartitions) return null;
1432
+ const partitions = resource.config?.partitions || resource.partitions;
1433
+ if (!partitions) {
1434
+ return null;
1435
+ }
1391
1436
  const partitionValues = {};
1392
- for (const [partitionName, partitionConfig] of Object.entries(resource.partitions)) {
1437
+ for (const [partitionName, partitionConfig] of Object.entries(partitions)) {
1393
1438
  const values = {};
1394
1439
  for (const field of Object.keys(partitionConfig.fields)) {
1395
1440
  values[field] = this.getNestedFieldValue(data, field);
@@ -1432,7 +1477,7 @@ class AuditPlugin extends plugin_class_default {
1432
1477
  }
1433
1478
  async getAuditLogs(options = {}) {
1434
1479
  if (!this.auditResource) return [];
1435
- const { resourceName, operation, recordId, partition, startDate, endDate, limit = 100 } = options;
1480
+ const { resourceName, operation, recordId, partition, startDate, endDate, limit = 100, offset = 0 } = options;
1436
1481
  let query = {};
1437
1482
  if (resourceName) query.resourceName = resourceName;
1438
1483
  if (operation) query.operation = operation;
@@ -1443,7 +1488,7 @@ class AuditPlugin extends plugin_class_default {
1443
1488
  if (startDate) query.timestamp.$gte = startDate;
1444
1489
  if (endDate) query.timestamp.$lte = endDate;
1445
1490
  }
1446
- const result = await this.auditResource.page({ query, limit });
1491
+ const result = await this.auditResource.page({ query, limit, offset });
1447
1492
  return result.items || [];
1448
1493
  }
1449
1494
  async getRecordHistory(resourceName, recordId) {
@@ -6558,7 +6603,7 @@ class S3Cache extends Cache {
6558
6603
  ttl = 0,
6559
6604
  prefix = void 0
6560
6605
  }) {
6561
- super({ client, keyPrefix, ttl, prefix });
6606
+ super();
6562
6607
  this.client = client;
6563
6608
  this.keyPrefix = keyPrefix;
6564
6609
  this.config.ttl = ttl;
@@ -7239,6 +7284,26 @@ class PartitionAwareFilesystemCache extends FilesystemCache {
7239
7284
  }
7240
7285
  return super._set(key, data);
7241
7286
  }
7287
+ /**
7288
+ * Public set method with partition support
7289
+ */
7290
+ async set(resource, action, data, 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._set(key, data, { resource, action, ...options });
7294
+ }
7295
+ return super.set(resource, action);
7296
+ }
7297
+ /**
7298
+ * Public get method with partition support
7299
+ */
7300
+ async get(resource, action, options = {}) {
7301
+ if (typeof resource === "string" && typeof action === "string" && options.partition) {
7302
+ const key = this._getPartitionCacheKey(resource, action, options.partition, options.partitionValues, options.params);
7303
+ return this._get(key, { resource, action, ...options });
7304
+ }
7305
+ return super.get(resource);
7306
+ }
7242
7307
  /**
7243
7308
  * Enhanced get method with partition awareness
7244
7309
  */
@@ -10801,7 +10866,7 @@ async function handleInsert$4({ resource, data, mappedData, originalData }) {
10801
10866
  if (totalSize > effectiveLimit) {
10802
10867
  throw new Error(`S3 metadata size exceeds 2KB limit. Current size: ${totalSize} bytes, effective limit: ${effectiveLimit} bytes, absolute limit: ${S3_METADATA_LIMIT_BYTES} bytes`);
10803
10868
  }
10804
- return { mappedData, body: JSON.stringify(mappedData) };
10869
+ return { mappedData, body: "" };
10805
10870
  }
10806
10871
  async function handleUpdate$4({ resource, id, data, mappedData, originalData }) {
10807
10872
  const totalSize = calculateTotalSize(mappedData);
@@ -10864,8 +10929,9 @@ async function handleInsert$3({ resource, data, mappedData, originalData }) {
10864
10929
  excess: totalSize - 2047,
10865
10930
  data: originalData || data
10866
10931
  });
10932
+ return { mappedData: { _v: mappedData._v }, body: JSON.stringify(mappedData) };
10867
10933
  }
10868
- return { mappedData, body: JSON.stringify(data) };
10934
+ return { mappedData, body: "" };
10869
10935
  }
10870
10936
  async function handleUpdate$3({ resource, id, data, mappedData, originalData }) {
10871
10937
  const totalSize = calculateTotalSize(mappedData);
@@ -10912,6 +10978,18 @@ async function handleUpsert$3({ resource, id, data, mappedData, originalData })
10912
10978
  return { mappedData, body: JSON.stringify(data) };
10913
10979
  }
10914
10980
  async function handleGet$3({ resource, metadata, body }) {
10981
+ if (body && body.trim() !== "") {
10982
+ try {
10983
+ const bodyData = JSON.parse(body);
10984
+ const mergedData = {
10985
+ ...bodyData,
10986
+ ...metadata
10987
+ };
10988
+ return { metadata: mergedData, body };
10989
+ } catch (error) {
10990
+ return { metadata, body };
10991
+ }
10992
+ }
10915
10993
  return { metadata, body };
10916
10994
  }
10917
10995
 
@@ -10979,7 +11057,7 @@ async function handleInsert$2({ resource, data, mappedData, originalData }) {
10979
11057
  if (truncated) {
10980
11058
  resultFields[TRUNCATED_FLAG] = TRUNCATED_FLAG_VALUE;
10981
11059
  }
10982
- return { mappedData: resultFields, body: JSON.stringify(mappedData) };
11060
+ return { mappedData: resultFields, body: "" };
10983
11061
  }
10984
11062
  async function handleUpdate$2({ resource, id, data, mappedData, originalData }) {
10985
11063
  return handleInsert$2({ resource, data, mappedData, originalData });
@@ -11069,7 +11147,6 @@ async function handleInsert$1({ resource, data, mappedData, originalData }) {
11069
11147
  }
11070
11148
  const hasOverflow = Object.keys(bodyFields).length > 0;
11071
11149
  let body = hasOverflow ? JSON.stringify(bodyFields) : "";
11072
- if (!hasOverflow) body = "{}";
11073
11150
  return { mappedData: metadataFields, body };
11074
11151
  }
11075
11152
  async function handleUpdate$1({ resource, id, data, mappedData, originalData }) {
@@ -11721,16 +11798,16 @@ ${errorDetails}`,
11721
11798
  * email: 'john@example.com'
11722
11799
  * });
11723
11800
  */
11724
- async insert({ id, ...attributes }) {
11725
- const exists = await this.exists(id);
11726
- if (exists) throw new Error(`Resource with id '${id}' already exists`);
11727
- this.getResourceKey(id || "(auto)");
11801
+ async insert({ id: id$1, ...attributes }) {
11802
+ const exists = await this.exists(id$1);
11803
+ if (exists) throw new Error(`Resource with id '${id$1}' already exists`);
11804
+ this.getResourceKey(id$1 || "(auto)");
11728
11805
  if (this.options.timestamps) {
11729
11806
  attributes.createdAt = (/* @__PURE__ */ new Date()).toISOString();
11730
11807
  attributes.updatedAt = (/* @__PURE__ */ new Date()).toISOString();
11731
11808
  }
11732
11809
  const attributesWithDefaults = this.applyDefaults(attributes);
11733
- const completeData = { id, ...attributesWithDefaults };
11810
+ const completeData = { id: id$1, ...attributesWithDefaults };
11734
11811
  const preProcessedData = await this.executeHooks("beforeInsert", completeData);
11735
11812
  const extraProps = Object.keys(preProcessedData).filter(
11736
11813
  (k) => !(k in completeData) || preProcessedData[k] !== completeData[k]
@@ -11754,7 +11831,14 @@ ${errorDetails}`,
11754
11831
  }
11755
11832
  const { id: validatedId, ...validatedAttributes } = validated;
11756
11833
  Object.assign(validatedAttributes, extraData);
11757
- const finalId = validatedId || id || this.idGenerator();
11834
+ let finalId = validatedId || id$1;
11835
+ if (!finalId) {
11836
+ finalId = this.idGenerator();
11837
+ if (!finalId || finalId.trim() === "") {
11838
+ const { idGenerator } = await Promise.resolve().then(function () { return id; });
11839
+ finalId = idGenerator();
11840
+ }
11841
+ }
11758
11842
  const mappedData = await this.schema.mapper(validatedAttributes);
11759
11843
  mappedData._v = String(this.version);
11760
11844
  const behaviorImpl = getBehavior(this.behavior);
@@ -11799,26 +11883,11 @@ ${errorDetails}`,
11799
11883
  errPut.excess = excess;
11800
11884
  throw new ResourceError("metadata headers exceed", { resourceName: this.name, operation: "insert", id: finalId, totalSize, effectiveLimit, excess, suggestion: "Reduce metadata size or number of fields." });
11801
11885
  }
11802
- throw mapAwsError(errPut, {
11803
- bucket: this.client.config.bucket,
11804
- key,
11805
- resourceName: this.name,
11806
- operation: "insert",
11807
- id: finalId
11808
- });
11886
+ throw errPut;
11809
11887
  }
11810
- let insertedData = await this.composeFullObjectFromWrite({
11811
- id: finalId,
11812
- metadata: finalMetadata,
11813
- body,
11814
- behavior: this.behavior
11815
- });
11816
- const finalResult = await this.executeHooks("afterInsert", insertedData);
11817
- this.emit("insert", {
11818
- ...insertedData,
11819
- $before: { ...completeData },
11820
- $after: { ...finalResult }
11821
- });
11888
+ const insertedObject = await this.get(finalId);
11889
+ const finalResult = await this.executeHooks("afterInsert", insertedObject);
11890
+ this.emit("insert", finalResult);
11822
11891
  return finalResult;
11823
11892
  }
11824
11893
  /**
@@ -11827,7 +11896,7 @@ ${errorDetails}`,
11827
11896
  * @returns {Promise<Object>} The resource object with all attributes and metadata
11828
11897
  * @example
11829
11898
  * const user = await resource.get('user-123');
11830
- */
11899
+ */
11831
11900
  async get(id) {
11832
11901
  if (isObject$1(id)) throw new Error(`id cannot be an object`);
11833
11902
  if (isEmpty(id)) throw new Error("id cannot be empty");
@@ -11842,17 +11911,6 @@ ${errorDetails}`,
11842
11911
  id
11843
11912
  });
11844
11913
  }
11845
- if (request.ContentLength === 0) {
11846
- const noContentErr = new Error(`No such key: ${key} [bucket:${this.client.config.bucket}]`);
11847
- noContentErr.name = "NoSuchKey";
11848
- throw mapAwsError(noContentErr, {
11849
- bucket: this.client.config.bucket,
11850
- key,
11851
- resourceName: this.name,
11852
- operation: "get",
11853
- id
11854
- });
11855
- }
11856
11914
  const objectVersionRaw = request.Metadata?._v || this.version;
11857
11915
  const objectVersion = typeof objectVersionRaw === "string" && objectVersionRaw.startsWith("v") ? objectVersionRaw.slice(1) : objectVersionRaw;
11858
11916
  const schema = await this.getSchemaForVersion(objectVersion);
@@ -13191,6 +13249,18 @@ ${errorDetails}`,
13191
13249
  });
13192
13250
  return result2;
13193
13251
  }
13252
+ if (behavior === "user-managed" && body && body.trim() !== "") {
13253
+ const [okBody, errBody, parsedBody] = await try_fn_default(() => Promise.resolve(JSON.parse(body)));
13254
+ if (okBody) {
13255
+ const [okUnmap, errUnmap, unmappedBody] = await try_fn_default(() => this.schema.unmapper(parsedBody));
13256
+ const bodyData = okUnmap ? unmappedBody : {};
13257
+ const merged = { ...bodyData, ...unmappedMetadata, id };
13258
+ Object.keys(merged).forEach((k) => {
13259
+ merged[k] = fixValue(merged[k]);
13260
+ });
13261
+ return filterInternalFields(merged);
13262
+ }
13263
+ }
13194
13264
  const result = { ...unmappedMetadata, id };
13195
13265
  Object.keys(result).forEach((k) => {
13196
13266
  result[k] = fixValue(result[k]);
@@ -13433,7 +13503,7 @@ class Database extends EventEmitter {
13433
13503
  this.id = idGenerator(7);
13434
13504
  this.version = "1";
13435
13505
  this.s3dbVersion = (() => {
13436
- const [ok, err, version] = try_fn_default(() => true ? "8.1.0" : "latest");
13506
+ const [ok, err, version] = try_fn_default(() => true ? "8.1.1" : "latest");
13437
13507
  return ok ? version : "latest";
13438
13508
  })();
13439
13509
  this.resources = {};