@ductape/sdk 0.1.124 → 0.1.128

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.
Files changed (29) hide show
  1. package/dist/index.d.ts +28 -1
  2. package/dist/index.js +23 -0
  3. package/dist/index.js.map +1 -1
  4. package/dist/processor/services/processor.service.d.ts +4 -0
  5. package/dist/processor/services/processor.service.js +86 -3
  6. package/dist/processor/services/processor.service.js.map +1 -1
  7. package/dist/products/validators/joi-validators/create.productHealthcheck.validator.js +25 -10
  8. package/dist/products/validators/joi-validators/create.productHealthcheck.validator.js.map +1 -1
  9. package/dist/resilience/healthcheck.service.d.ts +22 -0
  10. package/dist/resilience/healthcheck.service.js +171 -2
  11. package/dist/resilience/healthcheck.service.js.map +1 -1
  12. package/dist/resilience/index.d.ts +1 -0
  13. package/dist/resilience/index.js +1 -0
  14. package/dist/resilience/index.js.map +1 -1
  15. package/dist/resilience/rate-limit.service.d.ts +37 -0
  16. package/dist/resilience/rate-limit.service.js +55 -0
  17. package/dist/resilience/rate-limit.service.js.map +1 -0
  18. package/dist/resilience/resilience.service.js +42 -0
  19. package/dist/resilience/resilience.service.js.map +1 -1
  20. package/dist/resilience/types/index.d.ts +46 -2
  21. package/dist/resilience/types/index.js +3 -0
  22. package/dist/resilience/types/index.js.map +1 -1
  23. package/dist/tsconfig.tsbuildinfo +1 -1
  24. package/dist/types/productsBuilder.types.d.ts +8 -1
  25. package/dist/types/productsBuilder.types.js +3 -0
  26. package/dist/types/productsBuilder.types.js.map +1 -1
  27. package/dist/vector/adapters/opensearch.adapter.js +1 -1
  28. package/dist/vector/adapters/opensearch.adapter.js.map +1 -1
  29. package/package.json +1 -1
@@ -157,6 +157,10 @@ export default class ProcessorService implements IProcessorService {
157
157
  messageBroker?: string;
158
158
  message_broker?: string;
159
159
  storage?: string;
160
+ cache?: string;
161
+ vector?: string;
162
+ notification?: string;
163
+ channels?: string[];
160
164
  event?: string;
161
165
  input?: any;
162
166
  };
@@ -57,6 +57,7 @@ const processor_utils_1 = require("../utils/processor.utils");
57
57
  const axios_1 = __importDefault(require("axios"));
58
58
  const processorApi_service_1 = require("../../api/services/processorApi.service");
59
59
  const cache_service_1 = require("../../cache/cache.service");
60
+ const vector_1 = require("../../vector");
60
61
  const expo_client_1 = __importDefault(require("../../clients/expo.client"));
61
62
  const handlebars_1 = require("handlebars");
62
63
  const string_utils_1 = require("../../products/utils/string.utils");
@@ -582,7 +583,7 @@ class ProcessorService {
582
583
  * Public so the SDK monitor worker can call it with full healthcheck (including probe).
583
584
  */
584
585
  async runOneHealthcheck(healthcheck, envSlug, decryptedInput, productTag) {
585
- var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m;
586
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
586
587
  const probe = healthcheck.probe;
587
588
  const retries = (_a = healthcheck.retries) !== null && _a !== void 0 ? _a : 0;
588
589
  const inferType = () => {
@@ -598,6 +599,12 @@ class ProcessorService {
598
599
  return 'graph';
599
600
  if (probe === null || probe === void 0 ? void 0 : probe.storage)
600
601
  return 'storage';
602
+ if (probe === null || probe === void 0 ? void 0 : probe.cache)
603
+ return 'cache';
604
+ if (probe === null || probe === void 0 ? void 0 : probe.vector)
605
+ return 'vector';
606
+ if (probe === null || probe === void 0 ? void 0 : probe.notification)
607
+ return 'notification';
601
608
  if ((probe === null || probe === void 0 ? void 0 : probe.events) || (probe === null || probe === void 0 ? void 0 : probe.messageBroker) || (probe === null || probe === void 0 ? void 0 : probe.message_broker))
602
609
  return 'events';
603
610
  return 'app';
@@ -664,12 +671,88 @@ class ProcessorService {
664
671
  input: { message: typeof decryptedInput === 'object' && decryptedInput != null ? decryptedInput : {} },
665
672
  });
666
673
  }
674
+ case 'cache': {
675
+ if (!this.redisClient) {
676
+ throw Object.assign(new Error('Healthcheck cache probes require Redis'), { code: 'HEALTHCHECK_REDIS_REQUIRED', retryable: false });
677
+ }
678
+ const started = Date.now();
679
+ const pong = await this.redisClient.ping();
680
+ return { status: pong === 'PONG' ? 'healthy' : 'unhealthy', latencyMs: Date.now() - started, cache: probe.cache };
681
+ }
682
+ case 'vector': {
683
+ const svc = new vector_1.VectorDatabaseService({
684
+ workspace_id: this.workspace_id, public_key: this.public_key, user_id: this.user_id,
685
+ token: this.token, env_type: this.environment, redis_client: this.redisClient,
686
+ private_key: this._privateKey, access_key: this.accessKey,
687
+ });
688
+ const started = Date.now();
689
+ const connection = await svc.connect({ product: productTag, env: envSlug, vector: probe.vector });
690
+ try {
691
+ const healthy = await connection.testConnection();
692
+ return { status: healthy ? 'healthy' : 'unhealthy', latencyMs: Date.now() - started, vector: probe.vector };
693
+ }
694
+ finally {
695
+ await svc.disconnect({ product: productTag, env: envSlug, vector: probe.vector });
696
+ }
697
+ }
698
+ case 'notification': {
699
+ // Non-delivering check: validate configuration and use provider identity/account
700
+ // endpoints where the provider supports one. Never sends a notification.
701
+ const started = Date.now();
702
+ const notification = await this.productBuilderService.fetchNotification(probe.notification);
703
+ if (!notification)
704
+ throw Object.assign(new Error(`Notification "${probe.notification}" not found`), { code: 'HEALTHCHECK_NOTIFICATION_NOT_FOUND' });
705
+ const requested = (_k = probe.channels) !== null && _k !== void 0 ? _k : [];
706
+ const envConfig = ((_l = notification.envs) !== null && _l !== void 0 ? _l : []).find((item) => item.slug === envSlug);
707
+ if (!envConfig)
708
+ throw Object.assign(new Error(`Notification has no environment "${envSlug}"`), { code: 'HEALTHCHECK_NOTIFICATION_ENV_MISSING' });
709
+ const configured = { sms: envConfig.sms, email: envConfig.emails, push: envConfig.push_notifications };
710
+ const missing = requested.filter((channel) => !configured[channel]);
711
+ if (missing.length)
712
+ throw Object.assign(new Error(`Notification channels are not configured: ${missing.join(', ')}`), { code: 'HEALTHCHECK_NOTIFICATION_CHANNEL_MISSING' });
713
+ const verifiedChannels = [];
714
+ for (const channel of requested) {
715
+ const config = configured[channel];
716
+ try {
717
+ if (channel === 'email' && String(config.provider).toLowerCase() === 'sendgrid') {
718
+ await axios_1.default.get('https://api.sendgrid.com/v3/user/profile', { headers: { Authorization: `Bearer ${(_m = config.sendgrid) === null || _m === void 0 ? void 0 : _m.apiKey}` }, timeout: 10000 });
719
+ verifiedChannels.push(channel);
720
+ }
721
+ else if (channel === 'sms' && ['nexmo', 'vonage'].includes(String(config.provider).toLowerCase())) {
722
+ await axios_1.default.get('https://rest.nexmo.com/account/get-balance', { params: { api_key: config.apiKey, api_secret: config.apiSecret }, timeout: 10000 });
723
+ verifiedChannels.push(channel);
724
+ }
725
+ else if (channel === 'push' && String(config.type).toLowerCase() === 'firebase') {
726
+ const app = await this.getFirebaseApp(config.credentials, { workspaceId: this.workspace_id, product: productTag });
727
+ await app.options.credential.getAccessToken();
728
+ verifiedChannels.push(channel);
729
+ }
730
+ }
731
+ catch (_r) {
732
+ // Do not leak provider credentials through an Axios error/config object.
733
+ throw Object.assign(new Error(`Notification ${channel} provider credential check failed`), {
734
+ code: 'HEALTHCHECK_NOTIFICATION_CREDENTIALS_INVALID',
735
+ retryable: false,
736
+ });
737
+ }
738
+ }
739
+ const allVerified = verifiedChannels.length === requested.length;
740
+ return {
741
+ status: allVerified ? 'healthy' : 'degraded',
742
+ latencyMs: Date.now() - started,
743
+ notification: probe.notification,
744
+ channels: requested,
745
+ verifiedChannels,
746
+ deliveryAttempted: false,
747
+ credentialsVerified: allVerified,
748
+ };
749
+ }
667
750
  default:
668
751
  return this.processAction({
669
752
  env: envSlug,
670
753
  product: productTag,
671
- app: (_k = healthcheck.app) !== null && _k !== void 0 ? _k : '',
672
- action: (_m = (_l = healthcheck.event) !== null && _l !== void 0 ? _l : event) !== null && _m !== void 0 ? _m : '',
754
+ app: (_o = healthcheck.app) !== null && _o !== void 0 ? _o : '',
755
+ action: (_q = (_p = healthcheck.event) !== null && _p !== void 0 ? _p : event) !== null && _q !== void 0 ? _q : '',
673
756
  input: decryptedInput !== null && decryptedInput !== void 0 ? decryptedInput : {},
674
757
  retries,
675
758
  });