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.es.js CHANGED
@@ -3865,6 +3865,10 @@ class CachePlugin extends Plugin {
3865
3865
  }
3866
3866
  }
3867
3867
  shouldCacheResource(resourceName) {
3868
+ const resourceMetadata = this.database.savedMetadata?.resources?.[resourceName];
3869
+ if (resourceMetadata?.createdBy && resourceMetadata.createdBy !== "user" && !this.config.include) {
3870
+ return false;
3871
+ }
3868
3872
  if (resourceName.startsWith("plg_") && !this.config.include) {
3869
3873
  return false;
3870
3874
  }
@@ -4414,8 +4418,9 @@ class EventualConsistencyPlugin extends Plugin {
4414
4418
  behavior: "body-overflow",
4415
4419
  timestamps: true,
4416
4420
  partitions: partitionConfig,
4417
- asyncPartitions: true
4421
+ asyncPartitions: true,
4418
4422
  // Use async partitions for better performance
4423
+ createdBy: "EventualConsistencyPlugin"
4419
4424
  })
4420
4425
  );
4421
4426
  if (!ok && !this.database.resources[transactionResourceName]) {
@@ -4432,7 +4437,8 @@ class EventualConsistencyPlugin extends Plugin {
4432
4437
  workerId: "string|optional"
4433
4438
  },
4434
4439
  behavior: "body-only",
4435
- timestamps: false
4440
+ timestamps: false,
4441
+ createdBy: "EventualConsistencyPlugin"
4436
4442
  })
4437
4443
  );
4438
4444
  if (!lockOk && !this.database.resources[lockResourceName]) {
@@ -4445,8 +4451,24 @@ class EventualConsistencyPlugin extends Plugin {
4445
4451
  this.addHelperMethods();
4446
4452
  if (this.config.autoConsolidate) {
4447
4453
  this.startConsolidationTimer();
4454
+ if (this.config.verbose) {
4455
+ console.log(
4456
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Auto-consolidation ENABLED (interval: ${this.config.consolidationInterval}s, window: ${this.config.consolidationWindow}h, mode: ${this.config.mode})`
4457
+ );
4458
+ }
4459
+ } else {
4460
+ if (this.config.verbose) {
4461
+ console.log(
4462
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Auto-consolidation DISABLED (manual consolidation only)`
4463
+ );
4464
+ }
4448
4465
  }
4449
4466
  this.startGarbageCollectionTimer();
4467
+ if (this.config.verbose) {
4468
+ console.log(
4469
+ `[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}` : ""}`
4470
+ );
4471
+ }
4450
4472
  }
4451
4473
  async onStart() {
4452
4474
  if (this.deferredSetup) {
@@ -4528,7 +4550,8 @@ class EventualConsistencyPlugin extends Plugin {
4528
4550
  byCohort: {
4529
4551
  fields: { cohort: "string" }
4530
4552
  }
4531
- }
4553
+ },
4554
+ createdBy: "EventualConsistencyPlugin"
4532
4555
  })
4533
4556
  );
4534
4557
  if (!ok && !this.database.resources[analyticsResourceName]) {
@@ -4707,11 +4730,21 @@ class EventualConsistencyPlugin extends Plugin {
4707
4730
  };
4708
4731
  if (this.config.batchTransactions) {
4709
4732
  this.pendingTransactions.set(transaction.id, transaction);
4733
+ if (this.config.verbose) {
4734
+ console.log(
4735
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Transaction batched: ${data.operation} ${data.value} for ${data.originalId} (batch: ${this.pendingTransactions.size}/${this.config.batchSize})`
4736
+ );
4737
+ }
4710
4738
  if (this.pendingTransactions.size >= this.config.batchSize) {
4711
4739
  await this.flushPendingTransactions();
4712
4740
  }
4713
4741
  } else {
4714
4742
  await this.transactionResource.insert(transaction);
4743
+ if (this.config.verbose) {
4744
+ console.log(
4745
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Transaction created: ${data.operation} ${data.value} for ${data.originalId} (cohort: ${cohortInfo.hour}, applied: false)`
4746
+ );
4747
+ }
4715
4748
  }
4716
4749
  return transaction;
4717
4750
  }
@@ -4776,11 +4809,23 @@ class EventualConsistencyPlugin extends Plugin {
4776
4809
  }
4777
4810
  startConsolidationTimer() {
4778
4811
  const intervalMs = this.config.consolidationInterval * 1e3;
4812
+ if (this.config.verbose) {
4813
+ const nextRun = new Date(Date.now() + intervalMs);
4814
+ console.log(
4815
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Consolidation timer started. Next run at ${nextRun.toISOString()} (every ${this.config.consolidationInterval}s)`
4816
+ );
4817
+ }
4779
4818
  this.consolidationTimer = setInterval(async () => {
4780
4819
  await this.runConsolidation();
4781
4820
  }, intervalMs);
4782
4821
  }
4783
4822
  async runConsolidation() {
4823
+ const startTime = Date.now();
4824
+ if (this.config.verbose) {
4825
+ console.log(
4826
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Starting consolidation run at ${(/* @__PURE__ */ new Date()).toISOString()}`
4827
+ );
4828
+ }
4784
4829
  try {
4785
4830
  const now = /* @__PURE__ */ new Date();
4786
4831
  const hoursToCheck = this.config.consolidationWindow || 24;
@@ -4790,6 +4835,11 @@ class EventualConsistencyPlugin extends Plugin {
4790
4835
  const cohortInfo = this.getCohortInfo(date);
4791
4836
  cohortHours.push(cohortInfo.hour);
4792
4837
  }
4838
+ if (this.config.verbose) {
4839
+ console.log(
4840
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Querying ${hoursToCheck} hour partitions for pending transactions...`
4841
+ );
4842
+ }
4793
4843
  const transactionsByHour = await Promise.all(
4794
4844
  cohortHours.map(async (cohortHour) => {
4795
4845
  const [ok, err, txns] = await tryFn(
@@ -4804,26 +4854,47 @@ class EventualConsistencyPlugin extends Plugin {
4804
4854
  const transactions = transactionsByHour.flat();
4805
4855
  if (transactions.length === 0) {
4806
4856
  if (this.config.verbose) {
4807
- console.log(`[EventualConsistency] No pending transactions to consolidate`);
4857
+ console.log(
4858
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - No pending transactions found. Next run in ${this.config.consolidationInterval}s`
4859
+ );
4808
4860
  }
4809
4861
  return;
4810
4862
  }
4811
4863
  const uniqueIds = [...new Set(transactions.map((t) => t.originalId))];
4864
+ if (this.config.verbose) {
4865
+ console.log(
4866
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Found ${transactions.length} pending transactions for ${uniqueIds.length} records. Consolidating with concurrency=${this.config.consolidationConcurrency}...`
4867
+ );
4868
+ }
4812
4869
  const { results, errors } = await PromisePool.for(uniqueIds).withConcurrency(this.config.consolidationConcurrency).process(async (id) => {
4813
4870
  return await this.consolidateRecord(id);
4814
4871
  });
4872
+ const duration = Date.now() - startTime;
4815
4873
  if (errors && errors.length > 0) {
4816
- console.error(`Consolidation completed with ${errors.length} errors:`, errors);
4874
+ console.error(
4875
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Consolidation completed with ${errors.length} errors in ${duration}ms:`,
4876
+ errors
4877
+ );
4878
+ }
4879
+ if (this.config.verbose) {
4880
+ console.log(
4881
+ `[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`
4882
+ );
4817
4883
  }
4818
4884
  this.emit("eventual-consistency.consolidated", {
4819
4885
  resource: this.config.resource,
4820
4886
  field: this.config.field,
4821
4887
  recordCount: uniqueIds.length,
4822
4888
  successCount: results.length,
4823
- errorCount: errors.length
4889
+ errorCount: errors.length,
4890
+ duration
4824
4891
  });
4825
4892
  } catch (error) {
4826
- console.error("Consolidation error:", error);
4893
+ const duration = Date.now() - startTime;
4894
+ console.error(
4895
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Consolidation error after ${duration}ms:`,
4896
+ error
4897
+ );
4827
4898
  this.emit("eventual-consistency.consolidation-error", error);
4828
4899
  }
4829
4900
  }
@@ -4858,8 +4929,18 @@ class EventualConsistencyPlugin extends Plugin {
4858
4929
  })
4859
4930
  );
4860
4931
  if (!ok || !transactions || transactions.length === 0) {
4932
+ if (this.config.verbose) {
4933
+ console.log(
4934
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - No pending transactions for ${originalId}, skipping`
4935
+ );
4936
+ }
4861
4937
  return currentValue;
4862
4938
  }
4939
+ if (this.config.verbose) {
4940
+ console.log(
4941
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Consolidating ${originalId}: ${transactions.length} pending transactions (current: ${currentValue})`
4942
+ );
4943
+ }
4863
4944
  transactions.sort(
4864
4945
  (a, b) => new Date(a.timestamp).getTime() - new Date(b.timestamp).getTime()
4865
4946
  );
@@ -4868,6 +4949,11 @@ class EventualConsistencyPlugin extends Plugin {
4868
4949
  transactions.unshift(this._createSyntheticSetTransaction(currentValue));
4869
4950
  }
4870
4951
  const consolidatedValue = this.config.reducer(transactions);
4952
+ if (this.config.verbose) {
4953
+ console.log(
4954
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - ${originalId}: ${currentValue} \u2192 ${consolidatedValue} (${consolidatedValue > currentValue ? "+" : ""}${consolidatedValue - currentValue})`
4955
+ );
4956
+ }
4871
4957
  const [updateOk, updateErr] = await tryFn(
4872
4958
  () => this.targetResource.update(originalId, {
4873
4959
  [this.config.field]: consolidatedValue
@@ -4890,6 +4976,23 @@ class EventualConsistencyPlugin extends Plugin {
4890
4976
  if (this.config.enableAnalytics && transactionsToUpdate.length > 0) {
4891
4977
  await this.updateAnalytics(transactionsToUpdate);
4892
4978
  }
4979
+ if (this.targetResource.cache && typeof this.targetResource.cache.delete === "function") {
4980
+ try {
4981
+ const cacheKey = await this.targetResource.cacheKeyFor({ id: originalId });
4982
+ await this.targetResource.cache.delete(cacheKey);
4983
+ if (this.config.verbose) {
4984
+ console.log(
4985
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Cache invalidated for ${originalId}`
4986
+ );
4987
+ }
4988
+ } catch (cacheErr) {
4989
+ if (this.config.verbose) {
4990
+ console.warn(
4991
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Failed to invalidate cache for ${originalId}: ${cacheErr?.message}`
4992
+ );
4993
+ }
4994
+ }
4995
+ }
4893
4996
  }
4894
4997
  return consolidatedValue;
4895
4998
  } finally {
@@ -5104,21 +5207,43 @@ class EventualConsistencyPlugin extends Plugin {
5104
5207
  */
5105
5208
  async updateAnalytics(transactions) {
5106
5209
  if (!this.analyticsResource || transactions.length === 0) return;
5210
+ if (this.config.verbose) {
5211
+ console.log(
5212
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Updating analytics for ${transactions.length} transactions...`
5213
+ );
5214
+ }
5107
5215
  try {
5108
5216
  const byHour = this._groupByCohort(transactions, "cohortHour");
5217
+ const cohortCount = Object.keys(byHour).length;
5218
+ if (this.config.verbose) {
5219
+ console.log(
5220
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Updating ${cohortCount} hourly analytics cohorts...`
5221
+ );
5222
+ }
5109
5223
  for (const [cohort, txns] of Object.entries(byHour)) {
5110
5224
  await this._upsertAnalytics("hour", cohort, txns);
5111
5225
  }
5112
5226
  if (this.config.analyticsConfig.rollupStrategy === "incremental") {
5113
5227
  const uniqueHours = Object.keys(byHour);
5228
+ if (this.config.verbose) {
5229
+ console.log(
5230
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Rolling up ${uniqueHours.length} hours to daily/monthly analytics...`
5231
+ );
5232
+ }
5114
5233
  for (const cohortHour of uniqueHours) {
5115
5234
  await this._rollupAnalytics(cohortHour);
5116
5235
  }
5117
5236
  }
5118
- } catch (error) {
5119
5237
  if (this.config.verbose) {
5120
- console.warn(`[EventualConsistency] Analytics update error:`, error.message);
5238
+ console.log(
5239
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Analytics update complete for ${cohortCount} cohorts`
5240
+ );
5121
5241
  }
5242
+ } catch (error) {
5243
+ console.warn(
5244
+ `[EventualConsistency] ${this.config.resource}.${this.config.field} - Analytics update error:`,
5245
+ error.message
5246
+ );
5122
5247
  }
5123
5248
  }
5124
5249
  /**
@@ -9255,7 +9380,8 @@ ${errorDetails}`,
9255
9380
  versioningEnabled = false,
9256
9381
  events = {},
9257
9382
  asyncEvents = true,
9258
- asyncPartitions = true
9383
+ asyncPartitions = true,
9384
+ createdBy = "user"
9259
9385
  } = config;
9260
9386
  this.name = name;
9261
9387
  this.client = client;
@@ -9284,7 +9410,8 @@ ${errorDetails}`,
9284
9410
  autoDecrypt,
9285
9411
  allNestedObjectsOptional,
9286
9412
  asyncEvents,
9287
- asyncPartitions
9413
+ asyncPartitions,
9414
+ createdBy
9288
9415
  };
9289
9416
  this.hooks = {
9290
9417
  beforeInsert: [],
@@ -11648,7 +11775,7 @@ class Database extends EventEmitter {
11648
11775
  this.id = idGenerator(7);
11649
11776
  this.version = "1";
11650
11777
  this.s3dbVersion = (() => {
11651
- const [ok, err, version] = tryFn(() => true ? "10.0.5" : "latest");
11778
+ const [ok, err, version] = tryFn(() => true ? "10.0.7" : "latest");
11652
11779
  return ok ? version : "latest";
11653
11780
  })();
11654
11781
  this.resources = {};
@@ -12009,6 +12136,7 @@ class Database extends EventEmitter {
12009
12136
  metadata.resources[name] = {
12010
12137
  currentVersion: version,
12011
12138
  partitions: resource.config.partitions || {},
12139
+ createdBy: existingResource?.createdBy || resource.config.createdBy || "user",
12012
12140
  versions: {
12013
12141
  ...existingResource?.versions,
12014
12142
  // Preserve previous versions
@@ -12367,6 +12495,7 @@ class Database extends EventEmitter {
12367
12495
  * @param {boolean} [config.autoDecrypt=true] - Auto-decrypt secret fields
12368
12496
  * @param {Function|number} [config.idGenerator] - Custom ID generator or size
12369
12497
  * @param {number} [config.idSize=22] - Size for auto-generated IDs
12498
+ * @param {string} [config.createdBy='user'] - Who created this resource ('user', 'plugin', or plugin name)
12370
12499
  * @returns {Promise<Resource>} The created or updated resource
12371
12500
  */
12372
12501
  async createResource({ name, attributes, behavior = "user-managed", hooks, ...config }) {
@@ -12425,7 +12554,8 @@ class Database extends EventEmitter {
12425
12554
  idGenerator: config.idGenerator,
12426
12555
  idSize: config.idSize,
12427
12556
  asyncEvents: config.asyncEvents,
12428
- events: config.events || {}
12557
+ events: config.events || {},
12558
+ createdBy: config.createdBy || "user"
12429
12559
  });
12430
12560
  resource.database = this;
12431
12561
  this.resources[name] = resource;