s3db.js 10.0.5 → 10.0.7

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
@@ -3869,6 +3869,10 @@ class CachePlugin extends Plugin {
3869
3869
  }
3870
3870
  }
3871
3871
  shouldCacheResource(resourceName) {
3872
+ const resourceMetadata = this.database.savedMetadata?.resources?.[resourceName];
3873
+ if (resourceMetadata?.createdBy && resourceMetadata.createdBy !== "user" && !this.config.include) {
3874
+ return false;
3875
+ }
3872
3876
  if (resourceName.startsWith("plg_") && !this.config.include) {
3873
3877
  return false;
3874
3878
  }
@@ -4418,8 +4422,9 @@ class EventualConsistencyPlugin extends Plugin {
4418
4422
  behavior: "body-overflow",
4419
4423
  timestamps: true,
4420
4424
  partitions: partitionConfig,
4421
- asyncPartitions: true
4425
+ asyncPartitions: true,
4422
4426
  // Use async partitions for better performance
4427
+ createdBy: "EventualConsistencyPlugin"
4423
4428
  })
4424
4429
  );
4425
4430
  if (!ok && !this.database.resources[transactionResourceName]) {
@@ -4436,7 +4441,8 @@ class EventualConsistencyPlugin extends Plugin {
4436
4441
  workerId: "string|optional"
4437
4442
  },
4438
4443
  behavior: "body-only",
4439
- timestamps: false
4444
+ timestamps: false,
4445
+ createdBy: "EventualConsistencyPlugin"
4440
4446
  })
4441
4447
  );
4442
4448
  if (!lockOk && !this.database.resources[lockResourceName]) {
@@ -4449,8 +4455,24 @@ class EventualConsistencyPlugin extends Plugin {
4449
4455
  this.addHelperMethods();
4450
4456
  if (this.config.autoConsolidate) {
4451
4457
  this.startConsolidationTimer();
4458
+ if (this.config.verbose) {
4459
+ console.log(
4460
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Auto-consolidation ENABLED (interval: ${this.config.consolidationInterval}s, window: ${this.config.consolidationWindow}h, mode: ${this.config.mode})`
4461
+ );
4462
+ }
4463
+ } else {
4464
+ if (this.config.verbose) {
4465
+ console.log(
4466
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Auto-consolidation DISABLED (manual consolidation only)`
4467
+ );
4468
+ }
4452
4469
  }
4453
4470
  this.startGarbageCollectionTimer();
4471
+ if (this.config.verbose) {
4472
+ console.log(
4473
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Setup complete. Resources: ${this.config.resource}_transactions_${this.config.field}, ${this.config.resource}_consolidation_locks_${this.config.field}${this.config.enableAnalytics ? `, ${this.config.resource}_analytics_${this.config.field}` : ""}`
4474
+ );
4475
+ }
4454
4476
  }
4455
4477
  async onStart() {
4456
4478
  if (this.deferredSetup) {
@@ -4532,7 +4554,8 @@ class EventualConsistencyPlugin extends Plugin {
4532
4554
  byCohort: {
4533
4555
  fields: { cohort: "string" }
4534
4556
  }
4535
- }
4557
+ },
4558
+ createdBy: "EventualConsistencyPlugin"
4536
4559
  })
4537
4560
  );
4538
4561
  if (!ok && !this.database.resources[analyticsResourceName]) {
@@ -4711,11 +4734,21 @@ class EventualConsistencyPlugin extends Plugin {
4711
4734
  };
4712
4735
  if (this.config.batchTransactions) {
4713
4736
  this.pendingTransactions.set(transaction.id, transaction);
4737
+ if (this.config.verbose) {
4738
+ console.log(
4739
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Transaction batched: ${data.operation} ${data.value} for ${data.originalId} (batch: ${this.pendingTransactions.size}/${this.config.batchSize})`
4740
+ );
4741
+ }
4714
4742
  if (this.pendingTransactions.size >= this.config.batchSize) {
4715
4743
  await this.flushPendingTransactions();
4716
4744
  }
4717
4745
  } else {
4718
4746
  await this.transactionResource.insert(transaction);
4747
+ if (this.config.verbose) {
4748
+ console.log(
4749
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Transaction created: ${data.operation} ${data.value} for ${data.originalId} (cohort: ${cohortInfo.hour}, applied: false)`
4750
+ );
4751
+ }
4719
4752
  }
4720
4753
  return transaction;
4721
4754
  }
@@ -4780,11 +4813,23 @@ class EventualConsistencyPlugin extends Plugin {
4780
4813
  }
4781
4814
  startConsolidationTimer() {
4782
4815
  const intervalMs = this.config.consolidationInterval * 1e3;
4816
+ if (this.config.verbose) {
4817
+ const nextRun = new Date(Date.now() + intervalMs);
4818
+ console.log(
4819
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Consolidation timer started. Next run at ${nextRun.toISOString()} (every ${this.config.consolidationInterval}s)`
4820
+ );
4821
+ }
4783
4822
  this.consolidationTimer = setInterval(async () => {
4784
4823
  await this.runConsolidation();
4785
4824
  }, intervalMs);
4786
4825
  }
4787
4826
  async runConsolidation() {
4827
+ const startTime = Date.now();
4828
+ if (this.config.verbose) {
4829
+ console.log(
4830
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Starting consolidation run at ${(/* @__PURE__ */ new Date()).toISOString()}`
4831
+ );
4832
+ }
4788
4833
  try {
4789
4834
  const now = /* @__PURE__ */ new Date();
4790
4835
  const hoursToCheck = this.config.consolidationWindow || 24;
@@ -4794,6 +4839,11 @@ class EventualConsistencyPlugin extends Plugin {
4794
4839
  const cohortInfo = this.getCohortInfo(date);
4795
4840
  cohortHours.push(cohortInfo.hour);
4796
4841
  }
4842
+ if (this.config.verbose) {
4843
+ console.log(
4844
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Querying ${hoursToCheck} hour partitions for pending transactions...`
4845
+ );
4846
+ }
4797
4847
  const transactionsByHour = await Promise.all(
4798
4848
  cohortHours.map(async (cohortHour) => {
4799
4849
  const [ok, err, txns] = await tryFn(
@@ -4808,26 +4858,47 @@ class EventualConsistencyPlugin extends Plugin {
4808
4858
  const transactions = transactionsByHour.flat();
4809
4859
  if (transactions.length === 0) {
4810
4860
  if (this.config.verbose) {
4811
- console.log(`[EventualConsistency] No pending transactions to consolidate`);
4861
+ console.log(
4862
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - No pending transactions found. Next run in ${this.config.consolidationInterval}s`
4863
+ );
4812
4864
  }
4813
4865
  return;
4814
4866
  }
4815
4867
  const uniqueIds = [...new Set(transactions.map((t) => t.originalId))];
4868
+ if (this.config.verbose) {
4869
+ console.log(
4870
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Found ${transactions.length} pending transactions for ${uniqueIds.length} records. Consolidating with concurrency=${this.config.consolidationConcurrency}...`
4871
+ );
4872
+ }
4816
4873
  const { results, errors } = await promisePool.PromisePool.for(uniqueIds).withConcurrency(this.config.consolidationConcurrency).process(async (id) => {
4817
4874
  return await this.consolidateRecord(id);
4818
4875
  });
4876
+ const duration = Date.now() - startTime;
4819
4877
  if (errors && errors.length > 0) {
4820
- console.error(`Consolidation completed with ${errors.length} errors:`, errors);
4878
+ console.error(
4879
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Consolidation completed with ${errors.length} errors in ${duration}ms:`,
4880
+ errors
4881
+ );
4882
+ }
4883
+ if (this.config.verbose) {
4884
+ console.log(
4885
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Consolidation complete: ${results.length} records consolidated in ${duration}ms (${errors.length} errors). Next run in ${this.config.consolidationInterval}s`
4886
+ );
4821
4887
  }
4822
4888
  this.emit("eventual-consistency.consolidated", {
4823
4889
  resource: this.config.resource,
4824
4890
  field: this.config.field,
4825
4891
  recordCount: uniqueIds.length,
4826
4892
  successCount: results.length,
4827
- errorCount: errors.length
4893
+ errorCount: errors.length,
4894
+ duration
4828
4895
  });
4829
4896
  } catch (error) {
4830
- console.error("Consolidation error:", error);
4897
+ const duration = Date.now() - startTime;
4898
+ console.error(
4899
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Consolidation error after ${duration}ms:`,
4900
+ error
4901
+ );
4831
4902
  this.emit("eventual-consistency.consolidation-error", error);
4832
4903
  }
4833
4904
  }
@@ -4862,8 +4933,18 @@ class EventualConsistencyPlugin extends Plugin {
4862
4933
  })
4863
4934
  );
4864
4935
  if (!ok || !transactions || transactions.length === 0) {
4936
+ if (this.config.verbose) {
4937
+ console.log(
4938
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - No pending transactions for ${originalId}, skipping`
4939
+ );
4940
+ }
4865
4941
  return currentValue;
4866
4942
  }
4943
+ if (this.config.verbose) {
4944
+ console.log(
4945
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Consolidating ${originalId}: ${transactions.length} pending transactions (current: ${currentValue})`
4946
+ );
4947
+ }
4867
4948
  transactions.sort(
4868
4949
  (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
4869
4950
  );
@@ -4872,6 +4953,11 @@ class EventualConsistencyPlugin extends Plugin {
4872
4953
  transactions.unshift(this._createSyntheticSetTransaction(currentValue));
4873
4954
  }
4874
4955
  const consolidatedValue = this.config.reducer(transactions);
4956
+ if (this.config.verbose) {
4957
+ console.log(
4958
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - ${originalId}: ${currentValue} \u2192 ${consolidatedValue} (${consolidatedValue > currentValue ? "+" : ""}${consolidatedValue - currentValue})`
4959
+ );
4960
+ }
4875
4961
  const [updateOk, updateErr] = await tryFn(
4876
4962
  () => this.targetResource.update(originalId, {
4877
4963
  [this.config.field]: consolidatedValue
@@ -4894,6 +4980,23 @@ class EventualConsistencyPlugin extends Plugin {
4894
4980
  if (this.config.enableAnalytics && transactionsToUpdate.length > 0) {
4895
4981
  await this.updateAnalytics(transactionsToUpdate);
4896
4982
  }
4983
+ if (this.targetResource.cache && typeof this.targetResource.cache.delete === "function") {
4984
+ try {
4985
+ const cacheKey = await this.targetResource.cacheKeyFor({ id: originalId });
4986
+ await this.targetResource.cache.delete(cacheKey);
4987
+ if (this.config.verbose) {
4988
+ console.log(
4989
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Cache invalidated for ${originalId}`
4990
+ );
4991
+ }
4992
+ } catch (cacheErr) {
4993
+ if (this.config.verbose) {
4994
+ console.warn(
4995
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Failed to invalidate cache for ${originalId}: ${cacheErr?.message}`
4996
+ );
4997
+ }
4998
+ }
4999
+ }
4897
5000
  }
4898
5001
  return consolidatedValue;
4899
5002
  } finally {
@@ -5108,21 +5211,43 @@ class EventualConsistencyPlugin extends Plugin {
5108
5211
  */
5109
5212
  async updateAnalytics(transactions) {
5110
5213
  if (!this.analyticsResource || transactions.length === 0) return;
5214
+ if (this.config.verbose) {
5215
+ console.log(
5216
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Updating analytics for ${transactions.length} transactions...`
5217
+ );
5218
+ }
5111
5219
  try {
5112
5220
  const byHour = this._groupByCohort(transactions, "cohortHour");
5221
+ const cohortCount = Object.keys(byHour).length;
5222
+ if (this.config.verbose) {
5223
+ console.log(
5224
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Updating ${cohortCount} hourly analytics cohorts...`
5225
+ );
5226
+ }
5113
5227
  for (const [cohort, txns] of Object.entries(byHour)) {
5114
5228
  await this._upsertAnalytics("hour", cohort, txns);
5115
5229
  }
5116
5230
  if (this.config.analyticsConfig.rollupStrategy === "incremental") {
5117
5231
  const uniqueHours = Object.keys(byHour);
5232
+ if (this.config.verbose) {
5233
+ console.log(
5234
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Rolling up ${uniqueHours.length} hours to daily/monthly analytics...`
5235
+ );
5236
+ }
5118
5237
  for (const cohortHour of uniqueHours) {
5119
5238
  await this._rollupAnalytics(cohortHour);
5120
5239
  }
5121
5240
  }
5122
- } catch (error) {
5123
5241
  if (this.config.verbose) {
5124
- console.warn(`[EventualConsistency] Analytics update error:`, error.message);
5242
+ console.log(
5243
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Analytics update complete for ${cohortCount} cohorts`
5244
+ );
5125
5245
  }
5246
+ } catch (error) {
5247
+ console.warn(
5248
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Analytics update error:`,
5249
+ error.message
5250
+ );
5126
5251
  }
5127
5252
  }
5128
5253
  /**
@@ -9259,7 +9384,8 @@ ${errorDetails}`,
9259
9384
  versioningEnabled = false,
9260
9385
  events = {},
9261
9386
  asyncEvents = true,
9262
- asyncPartitions = true
9387
+ asyncPartitions = true,
9388
+ createdBy = "user"
9263
9389
  } = config;
9264
9390
  this.name = name;
9265
9391
  this.client = client;
@@ -9288,7 +9414,8 @@ ${errorDetails}`,
9288
9414
  autoDecrypt,
9289
9415
  allNestedObjectsOptional,
9290
9416
  asyncEvents,
9291
- asyncPartitions
9417
+ asyncPartitions,
9418
+ createdBy
9292
9419
  };
9293
9420
  this.hooks = {
9294
9421
  beforeInsert: [],
@@ -11652,7 +11779,7 @@ class Database extends EventEmitter {
11652
11779
  this.id = idGenerator(7);
11653
11780
  this.version = "1";
11654
11781
  this.s3dbVersion = (() => {
11655
- const [ok, err, version] = tryFn(() => true ? "10.0.5" : "latest");
11782
+ const [ok, err, version] = tryFn(() => true ? "10.0.7" : "latest");
11656
11783
  return ok ? version : "latest";
11657
11784
  })();
11658
11785
  this.resources = {};
@@ -12013,6 +12140,7 @@ class Database extends EventEmitter {
12013
12140
  metadata.resources[name] = {
12014
12141
  currentVersion: version,
12015
12142
  partitions: resource.config.partitions || {},
12143
+ createdBy: existingResource?.createdBy || resource.config.createdBy || "user",
12016
12144
  versions: {
12017
12145
  ...existingResource?.versions,
12018
12146
  // Preserve previous versions
@@ -12371,6 +12499,7 @@ class Database extends EventEmitter {
12371
12499
  * @param {boolean} [config.autoDecrypt=true] - Auto-decrypt secret fields
12372
12500
  * @param {Function|number} [config.idGenerator] - Custom ID generator or size
12373
12501
  * @param {number} [config.idSize=22] - Size for auto-generated IDs
12502
+ * @param {string} [config.createdBy='user'] - Who created this resource ('user', 'plugin', or plugin name)
12374
12503
  * @returns {Promise<Resource>} The created or updated resource
12375
12504
  */
12376
12505
  async createResource({ name, attributes, behavior = "user-managed", hooks, ...config }) {
@@ -12429,7 +12558,8 @@ class Database extends EventEmitter {
12429
12558
  idGenerator: config.idGenerator,
12430
12559
  idSize: config.idSize,
12431
12560
  asyncEvents: config.asyncEvents,
12432
- events: config.events || {}
12561
+ events: config.events || {},
12562
+ createdBy: config.createdBy || "user"
12433
12563
  });
12434
12564
  resource.database = this;
12435
12565
  this.resources[name] = resource;