@ductape/sdk 0.2.7 → 0.2.9

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/index.js CHANGED
@@ -503,7 +503,7 @@ class Ductape {
503
503
  }
504
504
  }
505
505
  constructor({ accessKey, redis_url, product, env, local, ip_address, language, runtime_sync }) {
506
- var _a, _b, _c;
506
+ var _a, _b, _c, _d;
507
507
  this.productBuilders = new Map();
508
508
  this.appBuilders = new Map();
509
509
  // Cached service instances (lazily initialized after auth)
@@ -531,6 +531,9 @@ class Ductape {
531
531
  this.runtimePollJitter = 0.2;
532
532
  this.runtimeMaxBackoffMs = 300000;
533
533
  this.runtimeWarmupEnabled = true;
534
+ this.runtimeWarmupConnectionTimeoutMs = 5000;
535
+ this.runtimeStrictReady = false;
536
+ this.runtimeDatabaseTags = [];
534
537
  this.runtimeCredentialState = { status: 'idle', referenced: 0, cached: 0, missing: 0 };
535
538
  this.runtimeClosed = false;
536
539
  /** Register and inspect portable application functions used by Feature JSON. */
@@ -5456,6 +5459,8 @@ class Ductape {
5456
5459
  this.runtimePollJitter = Math.max(0, Math.min(1, (_b = runtime_sync.jitter) !== null && _b !== void 0 ? _b : 0.2));
5457
5460
  this.runtimeMaxBackoffMs = Math.max(this.runtimePollIntervalMs, (_c = runtime_sync.max_backoff_ms) !== null && _c !== void 0 ? _c : 300000);
5458
5461
  this.runtimeWarmupEnabled = runtime_sync.warm_connections !== false;
5462
+ this.runtimeWarmupConnectionTimeoutMs = Math.max(1000, (_d = runtime_sync.warm_connection_timeout_ms) !== null && _d !== void 0 ? _d : 5000);
5463
+ this.runtimeStrictReady = runtime_sync.strict_ready === true;
5459
5464
  }
5460
5465
  // Services are lazily initialized via factory methods (createNewDatabaseService, etc.)
5461
5466
  // This ensures initUserAuth() is called before service creation
@@ -5806,7 +5811,12 @@ class Ductape {
5806
5811
  const scope = `${this.workspace_id}:${response.snapshot.product_tag}:${response.snapshot.env_slug}`;
5807
5812
  (0, bootstrap_cache_1.primeRuntimeBootstrapCache)(this.workspace_id, scope, response.snapshot.entries);
5808
5813
  this.runtimeManifest = response.manifest;
5809
- this.runtimeCredentialBundle = (_a = response.snapshot.credentials) !== null && _a !== void 0 ? _a : { referenced_keys: [], encrypted_secrets: [] };
5814
+ const derivedReferences = (0, secrets_1.findAllSecretReferences)(response.snapshot.entries.map((entry) => entry.data)).sort();
5815
+ this.runtimeCredentialBundle = response.snapshot.credentials
5816
+ ? Object.assign(Object.assign({}, response.snapshot.credentials), { referenced_keys: [...new Set([
5817
+ ...((_a = response.snapshot.credentials.referenced_keys) !== null && _a !== void 0 ? _a : []),
5818
+ ...derivedReferences,
5819
+ ])].sort() }) : { referenced_keys: derivedReferences, encrypted_secrets: [] };
5810
5820
  const referenced = (_c = (_b = this.runtimeCredentialBundle.referenced_keys) === null || _b === void 0 ? void 0 : _b.length) !== null && _c !== void 0 ? _c : this.runtimeCredentialBundle.encrypted_secrets.length;
5811
5821
  const cached = this.runtimeCredentialBundle.encrypted_secrets.length;
5812
5822
  this.runtimeCredentialState = {
@@ -5862,6 +5872,7 @@ class Ductape {
5862
5872
  .filter((entry) => (entry === null || entry === void 0 ? void 0 : entry.domain) === 'database')
5863
5873
  .map((entry) => { var _a, _b, _c; return String(((_a = entry === null || entry === void 0 ? void 0 : entry.parts) === null || _a === void 0 ? void 0 : _a[2]) || ((_c = (_b = entry === null || entry === void 0 ? void 0 : entry.data) === null || _b === void 0 ? void 0 : _b.database) === null || _c === void 0 ? void 0 : _c.tag) || '').trim(); })
5864
5874
  .filter(Boolean))];
5875
+ this.runtimeDatabaseTags = databaseTags;
5865
5876
  this.startRuntimeWarmup(databaseTags, Boolean(previousManifest && previousManifest.connectionRevision !== response.manifest.connectionRevision));
5866
5877
  }
5867
5878
  startRuntimeWarmup(databaseTags, recycleConnections) {
@@ -5878,7 +5889,10 @@ class Ductape {
5878
5889
  const databases = await Promise.all(databaseTags.map(async (tag) => {
5879
5890
  const start = Date.now();
5880
5891
  try {
5881
- await service.connect({ product: this.defaultProduct, env: this.defaultEnv, database: tag });
5892
+ await service.connect({
5893
+ product: this.defaultProduct, env: this.defaultEnv, database: tag,
5894
+ options: { connectionTimeout: this.runtimeWarmupConnectionTimeoutMs },
5895
+ });
5882
5896
  return { tag, status: 'ready', durationMs: Date.now() - start };
5883
5897
  }
5884
5898
  catch (error) {
@@ -5916,7 +5930,7 @@ class Ductape {
5916
5930
  (_b = (_a = this.runtimePollTimer).unref) === null || _b === void 0 ? void 0 : _b.call(_a);
5917
5931
  }
5918
5932
  async pollRuntimeManifest(failureCount) {
5919
- var _a, _b;
5933
+ var _a, _b, _c;
5920
5934
  if (!this.defaultProduct || !this.defaultEnv || this.runtimeClosed)
5921
5935
  return;
5922
5936
  try {
@@ -5931,6 +5945,9 @@ class Ductape {
5931
5945
  }
5932
5946
  else if (this.defaultProduct && this.defaultEnv) {
5933
5947
  (0, bootstrap_cache_1.touchRuntimeBootstrapCache)(`${this.workspace_id}:${this.defaultProduct}:${this.defaultEnv}`);
5948
+ if ((_c = this.runtimeWarmupState) === null || _c === void 0 ? void 0 : _c.databases.some((database) => database.status === 'failed')) {
5949
+ this.startRuntimeWarmup(this.runtimeDatabaseTags, false);
5950
+ }
5934
5951
  }
5935
5952
  this.runtimeLastError = undefined;
5936
5953
  this.scheduleRuntimePoll(0);
@@ -5951,7 +5968,7 @@ class Ductape {
5951
5968
  if (response) {
5952
5969
  this.applyRuntimeSnapshot(response);
5953
5970
  const secrets = await this.createNewSecretsService();
5954
- this.applyRuntimeCredentialBundle(secrets);
5971
+ await this.prepareRuntimeCredentials(secrets);
5955
5972
  }
5956
5973
  this.scheduleRuntimePoll(0);
5957
5974
  })().finally(() => { this.runtimeRefreshInFlight = undefined; });
@@ -5972,7 +5989,7 @@ class Ductape {
5972
5989
  }
5973
5990
  assertRuntimeReady() {
5974
5991
  var _a, _b;
5975
- if (!this.runtimeSyncEnabled)
5992
+ if (!this.runtimeSyncEnabled || !this.runtimeStrictReady)
5976
5993
  return;
5977
5994
  if (this.runtimeCredentialState.status === 'incomplete') {
5978
5995
  throw new Error(`Runtime credentials are incomplete (${this.runtimeCredentialState.missing} referenced secret(s) unavailable)`);
@@ -6299,6 +6316,27 @@ class Ductape {
6299
6316
  throw error;
6300
6317
  }
6301
6318
  }
6319
+ async prepareRuntimeCredentials(service) {
6320
+ var _a, _b;
6321
+ this.applyRuntimeCredentialBundle(service);
6322
+ if (!this.runtimeCredentialBundle)
6323
+ return;
6324
+ const bundled = new Set(this.runtimeCredentialBundle.encrypted_secrets.map((secret) => secret.key));
6325
+ const missingKeys = ((_a = this.runtimeCredentialBundle.referenced_keys) !== null && _a !== void 0 ? _a : []).filter((key) => !bundled.has(key));
6326
+ if (!missingKeys.length)
6327
+ return;
6328
+ this.runtimeCredentialState = Object.assign(Object.assign({}, this.runtimeCredentialState), { status: 'loading', missing: missingKeys.length });
6329
+ const results = await Promise.allSettled(missingKeys.map((key) => service.fetch(key)));
6330
+ const failed = results.filter((result) => result.status === 'rejected').length;
6331
+ const cached = bundled.size + results.length - failed;
6332
+ this.runtimeCredentialState = {
6333
+ status: failed ? 'incomplete' : 'ready',
6334
+ referenced: ((_b = this.runtimeCredentialBundle.referenced_keys) !== null && _b !== void 0 ? _b : []).length,
6335
+ cached,
6336
+ missing: failed,
6337
+ refreshedAt: Date.now(),
6338
+ };
6339
+ }
6302
6340
  async createNewCloudService() {
6303
6341
  const subCheck = false;
6304
6342
  if (!this.token || !this.public_key) {