@sakeetech/vendure-payment-viva 0.2.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.
Files changed (135) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +952 -0
  3. package/dist/api/admin-internal.controller.d.ts +59 -0
  4. package/dist/api/admin-internal.controller.d.ts.map +1 -0
  5. package/dist/api/admin-internal.controller.js +229 -0
  6. package/dist/api/admin-internal.controller.js.map +1 -0
  7. package/dist/api/admin-onboarding.controller.d.ts +72 -0
  8. package/dist/api/admin-onboarding.controller.d.ts.map +1 -0
  9. package/dist/api/admin-onboarding.controller.js +496 -0
  10. package/dist/api/admin-onboarding.controller.js.map +1 -0
  11. package/dist/api/admin-sources.controller.d.ts +50 -0
  12. package/dist/api/admin-sources.controller.d.ts.map +1 -0
  13. package/dist/api/admin-sources.controller.js +283 -0
  14. package/dist/api/admin-sources.controller.js.map +1 -0
  15. package/dist/api/shop-api.extension.d.ts +15 -0
  16. package/dist/api/shop-api.extension.d.ts.map +1 -0
  17. package/dist/api/shop-api.extension.js +35 -0
  18. package/dist/api/shop-api.extension.js.map +1 -0
  19. package/dist/api/shop-api.resolver.d.ts +42 -0
  20. package/dist/api/shop-api.resolver.d.ts.map +1 -0
  21. package/dist/api/shop-api.resolver.js +256 -0
  22. package/dist/api/shop-api.resolver.js.map +1 -0
  23. package/dist/api/webhook.controller.d.ts +58 -0
  24. package/dist/api/webhook.controller.d.ts.map +1 -0
  25. package/dist/api/webhook.controller.js +204 -0
  26. package/dist/api/webhook.controller.js.map +1 -0
  27. package/dist/cli/bin.d.ts +28 -0
  28. package/dist/cli/bin.d.ts.map +1 -0
  29. package/dist/cli/bin.js +104 -0
  30. package/dist/cli/bin.js.map +1 -0
  31. package/dist/cli/plan.d.ts +41 -0
  32. package/dist/cli/plan.d.ts.map +1 -0
  33. package/dist/cli/plan.js +115 -0
  34. package/dist/cli/plan.js.map +1 -0
  35. package/dist/cli/register-webhooks.d.ts +45 -0
  36. package/dist/cli/register-webhooks.d.ts.map +1 -0
  37. package/dist/cli/register-webhooks.js +400 -0
  38. package/dist/cli/register-webhooks.js.map +1 -0
  39. package/dist/cli/types.d.ts +75 -0
  40. package/dist/cli/types.d.ts.map +1 -0
  41. package/dist/cli/types.js +10 -0
  42. package/dist/cli/types.js.map +1 -0
  43. package/dist/constants.d.ts +35 -0
  44. package/dist/constants.d.ts.map +1 -0
  45. package/dist/constants.js +40 -0
  46. package/dist/constants.js.map +1 -0
  47. package/dist/entities/index.d.ts +4 -0
  48. package/dist/entities/index.d.ts.map +1 -0
  49. package/dist/entities/index.js +3 -0
  50. package/dist/entities/index.js.map +1 -0
  51. package/dist/entities/viva-transaction.entity.d.ts +70 -0
  52. package/dist/entities/viva-transaction.entity.d.ts.map +1 -0
  53. package/dist/entities/viva-transaction.entity.js +133 -0
  54. package/dist/entities/viva-transaction.entity.js.map +1 -0
  55. package/dist/entities/viva-webhook-event.entity.d.ts +71 -0
  56. package/dist/entities/viva-webhook-event.entity.d.ts.map +1 -0
  57. package/dist/entities/viva-webhook-event.entity.js +138 -0
  58. package/dist/entities/viva-webhook-event.entity.js.map +1 -0
  59. package/dist/index.d.ts +27 -0
  60. package/dist/index.d.ts.map +1 -0
  61. package/dist/index.js +23 -0
  62. package/dist/index.js.map +1 -0
  63. package/dist/jobs/process-viva-webhook.handler.d.ts +95 -0
  64. package/dist/jobs/process-viva-webhook.handler.d.ts.map +1 -0
  65. package/dist/jobs/process-viva-webhook.handler.js +530 -0
  66. package/dist/jobs/process-viva-webhook.handler.js.map +1 -0
  67. package/dist/jobs/queue-names.d.ts +18 -0
  68. package/dist/jobs/queue-names.d.ts.map +1 -0
  69. package/dist/jobs/queue-names.js +19 -0
  70. package/dist/jobs/queue-names.js.map +1 -0
  71. package/dist/jobs/retention-cleanup.handler.d.ts +31 -0
  72. package/dist/jobs/retention-cleanup.handler.d.ts.map +1 -0
  73. package/dist/jobs/retention-cleanup.handler.js +94 -0
  74. package/dist/jobs/retention-cleanup.handler.js.map +1 -0
  75. package/dist/loaders/bootstrap.d.ts +28 -0
  76. package/dist/loaders/bootstrap.d.ts.map +1 -0
  77. package/dist/loaders/bootstrap.js +90 -0
  78. package/dist/loaders/bootstrap.js.map +1 -0
  79. package/dist/migrations/1714000000000-create-viva-tables.d.ts +22 -0
  80. package/dist/migrations/1714000000000-create-viva-tables.d.ts.map +1 -0
  81. package/dist/migrations/1714000000000-create-viva-tables.js +105 -0
  82. package/dist/migrations/1714000000000-create-viva-tables.js.map +1 -0
  83. package/dist/observability/metrics-state.service.d.ts +43 -0
  84. package/dist/observability/metrics-state.service.d.ts.map +1 -0
  85. package/dist/observability/metrics-state.service.js +207 -0
  86. package/dist/observability/metrics-state.service.js.map +1 -0
  87. package/dist/payment-method-handler.d.ts +26 -0
  88. package/dist/payment-method-handler.d.ts.map +1 -0
  89. package/dist/payment-method-handler.js +693 -0
  90. package/dist/payment-method-handler.js.map +1 -0
  91. package/dist/plugin.d.ts +95 -0
  92. package/dist/plugin.d.ts.map +1 -0
  93. package/dist/plugin.js +241 -0
  94. package/dist/plugin.js.map +1 -0
  95. package/dist/providers/viva-oauth2-strategy.provider.d.ts +41 -0
  96. package/dist/providers/viva-oauth2-strategy.provider.d.ts.map +1 -0
  97. package/dist/providers/viva-oauth2-strategy.provider.js +60 -0
  98. package/dist/providers/viva-oauth2-strategy.provider.js.map +1 -0
  99. package/dist/services/connected-accounts.service.d.ts +53 -0
  100. package/dist/services/connected-accounts.service.d.ts.map +1 -0
  101. package/dist/services/connected-accounts.service.js +108 -0
  102. package/dist/services/connected-accounts.service.js.map +1 -0
  103. package/dist/services/per-merchant-semaphore.service.d.ts +49 -0
  104. package/dist/services/per-merchant-semaphore.service.d.ts.map +1 -0
  105. package/dist/services/per-merchant-semaphore.service.js +156 -0
  106. package/dist/services/per-merchant-semaphore.service.js.map +1 -0
  107. package/dist/services/state-machine.service.d.ts +100 -0
  108. package/dist/services/state-machine.service.d.ts.map +1 -0
  109. package/dist/services/state-machine.service.js +233 -0
  110. package/dist/services/state-machine.service.js.map +1 -0
  111. package/dist/types.d.ts +286 -0
  112. package/dist/types.d.ts.map +1 -0
  113. package/dist/types.js +23 -0
  114. package/dist/types.js.map +1 -0
  115. package/dist/util/currency.d.ts +32 -0
  116. package/dist/util/currency.d.ts.map +1 -0
  117. package/dist/util/currency.js +90 -0
  118. package/dist/util/currency.js.map +1 -0
  119. package/dist/util/error-envelope.d.ts +51 -0
  120. package/dist/util/error-envelope.d.ts.map +1 -0
  121. package/dist/util/error-envelope.js +157 -0
  122. package/dist/util/error-envelope.js.map +1 -0
  123. package/dist/util/ip-allowlist.d.ts +44 -0
  124. package/dist/util/ip-allowlist.d.ts.map +1 -0
  125. package/dist/util/ip-allowlist.js +139 -0
  126. package/dist/util/ip-allowlist.js.map +1 -0
  127. package/dist/util/normalize-options.d.ts +24 -0
  128. package/dist/util/normalize-options.d.ts.map +1 -0
  129. package/dist/util/normalize-options.js +189 -0
  130. package/dist/util/normalize-options.js.map +1 -0
  131. package/dist/util/url-template.d.ts +18 -0
  132. package/dist/util/url-template.d.ts.map +1 -0
  133. package/dist/util/url-template.js +22 -0
  134. package/dist/util/url-template.js.map +1 -0
  135. package/package.json +75 -0
@@ -0,0 +1,94 @@
1
+ /**
2
+ * jobs/retention-cleanup.handler.ts — 90-day webhook event retention cleanup.
3
+ *
4
+ * Runs once per day via setInterval (lightest-weight option — no new dep).
5
+ * Deletes `viva_webhook_event` rows where:
6
+ * processed_at < now() - INTERVAL '90 days'
7
+ *
8
+ * Rows with processed_at = NULL are NOT deleted regardless of age.
9
+ *
10
+ * Design note: Vendure's built-in session-cache cleanup uses a similar
11
+ * setInterval-in-OnApplicationBootstrap pattern (see SessionCacheService).
12
+ * We follow the same approach rather than introducing a scheduler dep.
13
+ *
14
+ * @see docs/plans/vendure-plugin-v0.md §"Webhook Design — 90-day retention cleanup"
15
+ */
16
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
17
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
18
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
19
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
20
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
21
+ };
22
+ var __metadata = (this && this.__metadata) || function (k, v) {
23
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
24
+ };
25
+ import { Injectable } from '@nestjs/common';
26
+ import { Logger, TransactionalConnection } from '@vendure/core';
27
+ import { LessThan } from 'typeorm';
28
+ import { VivaWebhookEvent } from '../entities/viva-webhook-event.entity.js';
29
+ import { VIVA_LOG_CONTEXT, WEBHOOK_RETENTION_DAYS } from '../constants.js';
30
+ // ---------------------------------------------------------------------------
31
+ // Constants
32
+ // ---------------------------------------------------------------------------
33
+ const RETENTION_MS = WEBHOOK_RETENTION_DAYS * 24 * 60 * 60 * 1_000;
34
+ const CLEANUP_INTERVAL_MS = 24 * 60 * 60 * 1_000; // 1 day
35
+ // ---------------------------------------------------------------------------
36
+ // Service
37
+ // ---------------------------------------------------------------------------
38
+ let RetentionCleanupHandler = class RetentionCleanupHandler {
39
+ connection;
40
+ _timer;
41
+ constructor(connection) {
42
+ this.connection = connection;
43
+ }
44
+ onApplicationBootstrap() {
45
+ // Run the first cleanup shortly after boot (10s delay to let DB settle)
46
+ const firstRunDelay = setTimeout(() => {
47
+ void this._runCleanup();
48
+ }, 10_000);
49
+ if (firstRunDelay.unref)
50
+ firstRunDelay.unref();
51
+ // Then run once per day
52
+ this._timer = setInterval(() => {
53
+ void this._runCleanup();
54
+ }, CLEANUP_INTERVAL_MS);
55
+ if (this._timer.unref)
56
+ this._timer.unref();
57
+ }
58
+ onApplicationShutdown() {
59
+ if (this._timer) {
60
+ clearInterval(this._timer);
61
+ }
62
+ }
63
+ /**
64
+ * Delete processed webhook events older than 90 days.
65
+ * Exposed for direct invocation from tests.
66
+ */
67
+ async runCleanup() {
68
+ return this._runCleanup();
69
+ }
70
+ async _runCleanup() {
71
+ const cutoff = new Date(Date.now() - RETENTION_MS);
72
+ try {
73
+ const repo = this.connection.rawConnection.getRepository(VivaWebhookEvent);
74
+ const result = await repo.delete({
75
+ processedAt: LessThan(cutoff),
76
+ });
77
+ const deleted = result.affected ?? 0;
78
+ if (deleted > 0) {
79
+ Logger.info(`[RetentionCleanup] Deleted ${deleted} webhook event rows older than ${WEBHOOK_RETENTION_DAYS} days.`, VIVA_LOG_CONTEXT);
80
+ }
81
+ return deleted;
82
+ }
83
+ catch (err) {
84
+ Logger.warn(`[RetentionCleanup] Cleanup failed: ${err instanceof Error ? err.message : String(err)}`, VIVA_LOG_CONTEXT);
85
+ return 0;
86
+ }
87
+ }
88
+ };
89
+ RetentionCleanupHandler = __decorate([
90
+ Injectable(),
91
+ __metadata("design:paramtypes", [TransactionalConnection])
92
+ ], RetentionCleanupHandler);
93
+ export { RetentionCleanupHandler };
94
+ //# sourceMappingURL=retention-cleanup.handler.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"retention-cleanup.handler.js","sourceRoot":"","sources":["../../src/jobs/retention-cleanup.handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;;;;;;;;;;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,gBAAgB,CAAC;AAE5C,OAAO,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAChE,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAE,MAAM,0CAA0C,CAAC;AAC5E,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAE3E,8EAA8E;AAC9E,YAAY;AACZ,8EAA8E;AAE9E,MAAM,YAAY,GAAG,sBAAsB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC;AACnE,MAAM,mBAAmB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,KAAK,CAAC,CAAC,QAAQ;AAE1D,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAGvE,IAAM,uBAAuB,GAA7B,MAAM,uBAAuB;IAGL;IAFrB,MAAM,CAA6C;IAE3D,YAA6B,UAAmC;QAAnC,eAAU,GAAV,UAAU,CAAyB;IAAG,CAAC;IAEpE,sBAAsB;QACpB,wEAAwE;QACxE,MAAM,aAAa,GAAG,UAAU,CAAC,GAAG,EAAE;YACpC,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1B,CAAC,EAAE,MAAM,CAAC,CAAC;QACX,IAAI,aAAa,CAAC,KAAK;YAAE,aAAa,CAAC,KAAK,EAAE,CAAC;QAE/C,wBAAwB;QACxB,IAAI,CAAC,MAAM,GAAG,WAAW,CAAC,GAAG,EAAE;YAC7B,KAAK,IAAI,CAAC,WAAW,EAAE,CAAC;QAC1B,CAAC,EAAE,mBAAmB,CAAC,CAAC;QACxB,IAAI,IAAI,CAAC,MAAM,CAAC,KAAK;YAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;IAC7C,CAAC;IAED,qBAAqB;QACnB,IAAI,IAAI,CAAC,MAAM,EAAE,CAAC;YAChB,aAAa,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC7B,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,UAAU;QACd,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC;IAC5B,CAAC;IAEO,KAAK,CAAC,WAAW;QACvB,MAAM,MAAM,GAAG,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,YAAY,CAAC,CAAC;QACnD,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC,aAAa,CAAC,gBAAgB,CAAC,CAAC;YAC3E,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,MAAM,CAAC;gBAC/B,WAAW,EAAE,QAAQ,CAAC,MAAM,CAAC;aAC9B,CAAC,CAAC;YACH,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,IAAI,CAAC,CAAC;YACrC,IAAI,OAAO,GAAG,CAAC,EAAE,CAAC;gBAChB,MAAM,CAAC,IAAI,CACT,8BAA8B,OAAO,kCAAkC,sBAAsB,QAAQ,EACrG,gBAAgB,CACjB,CAAC;YACJ,CAAC;YACD,OAAO,OAAO,CAAC;QACjB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,MAAM,CAAC,IAAI,CACT,sCAAsC,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,EACxF,gBAAgB,CACjB,CAAC;YACF,OAAO,CAAC,CAAC;QACX,CAAC;IACH,CAAC;CACF,CAAA;AAxDY,uBAAuB;IADnC,UAAU,EAAE;qCAI8B,uBAAuB;GAHrD,uBAAuB,CAwDnC"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * loaders/bootstrap.ts — Vendure plugin bootstrap warmup hook.
3
+ *
4
+ * Implements D12 from the plan: eager OAuth warm-up + undici Pool pre-connect
5
+ * on application startup. Both warmups are best-effort:
6
+ * - If Viva's token endpoint is unreachable the app still boots.
7
+ * - A single retry is scheduled after BOOTSTRAP_RETRY_DELAY_MS (~10s).
8
+ * - If the retry also fails the token cache stays empty; lazy-fetch in
9
+ * createPayment will obtain a token on the first real call.
10
+ *
11
+ * Logger: uses Vendure's static `Logger` so output is routed through whatever
12
+ * VendureLogger the host app configures.
13
+ *
14
+ * @see docs/plans/vendure-plugin-v0.md §"Build Plan — V4" (D12)
15
+ */
16
+ import type { OnApplicationBootstrap } from '@nestjs/common';
17
+ import type { VivaPaymentPluginOptions } from '../types.js';
18
+ import type { VivaOAuth2Strategy } from '../providers/viva-oauth2-strategy.provider.js';
19
+ export declare class VivaBootstrap implements OnApplicationBootstrap {
20
+ private readonly options;
21
+ private readonly oauth2;
22
+ constructor(options: VivaPaymentPluginOptions, oauth2: VivaOAuth2Strategy);
23
+ private _lastWarmupFailed;
24
+ get lastWarmupFailed(): boolean;
25
+ onApplicationBootstrap(): Promise<void>;
26
+ private _runWarmup;
27
+ }
28
+ //# sourceMappingURL=bootstrap.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.d.ts","sourceRoot":"","sources":["../../src/loaders/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAG7D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,+CAA+C,CAAC;AAYxF,qBACa,aAAc,YAAW,sBAAsB;IAE3B,OAAO,CAAC,QAAQ,CAAC,OAAO;IACjB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADb,OAAO,EAAE,wBAAwB,EAC1B,MAAM,EAAE,kBAAkB;IAIjF,OAAO,CAAC,iBAAiB,CAAS;IAClC,IAAI,gBAAgB,IAAI,OAAO,CAAmC;IAE5D,sBAAsB,IAAI,OAAO,CAAC,IAAI,CAAC;YAS/B,UAAU;CAsCzB"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * loaders/bootstrap.ts — Vendure plugin bootstrap warmup hook.
3
+ *
4
+ * Implements D12 from the plan: eager OAuth warm-up + undici Pool pre-connect
5
+ * on application startup. Both warmups are best-effort:
6
+ * - If Viva's token endpoint is unreachable the app still boots.
7
+ * - A single retry is scheduled after BOOTSTRAP_RETRY_DELAY_MS (~10s).
8
+ * - If the retry also fails the token cache stays empty; lazy-fetch in
9
+ * createPayment will obtain a token on the first real call.
10
+ *
11
+ * Logger: uses Vendure's static `Logger` so output is routed through whatever
12
+ * VendureLogger the host app configures.
13
+ *
14
+ * @see docs/plans/vendure-plugin-v0.md §"Build Plan — V4" (D12)
15
+ */
16
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
17
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
18
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
19
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
20
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
21
+ };
22
+ var __metadata = (this && this.__metadata) || function (k, v) {
23
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
24
+ };
25
+ var __param = (this && this.__param) || function (paramIndex, decorator) {
26
+ return function (target, key) { decorator(target, key, paramIndex); }
27
+ };
28
+ import { Injectable, Inject } from '@nestjs/common';
29
+ import { Logger } from '@vendure/core';
30
+ import { getAuthDispatcher, getApiDispatcher } from '@sakeetech/viva-payments-core/auth';
31
+ import { VIVA_PLUGIN_OPTIONS, VIVA_OAUTH2_STRATEGY_TOKEN, VIVA_LOG_CONTEXT, BOOTSTRAP_RETRY_DELAY_MS, } from '../constants.js';
32
+ // ---------------------------------------------------------------------------
33
+ // Bootstrap service
34
+ // ---------------------------------------------------------------------------
35
+ let VivaBootstrap = class VivaBootstrap {
36
+ options;
37
+ oauth2;
38
+ constructor(options, oauth2) {
39
+ this.options = options;
40
+ this.oauth2 = oauth2;
41
+ }
42
+ // Exposed for tests to read whether the last warmup succeeded.
43
+ _lastWarmupFailed = false;
44
+ get lastWarmupFailed() { return this._lastWarmupFailed; }
45
+ async onApplicationBootstrap() {
46
+ Logger.info('Starting Viva Wallet bootstrap warmup…', VIVA_LOG_CONTEXT);
47
+ await this._runWarmup(false);
48
+ }
49
+ // ---------------------------------------------------------------------------
50
+ // Internal
51
+ // ---------------------------------------------------------------------------
52
+ async _runWarmup(isRetry) {
53
+ const tag = isRetry ? '[retry] ' : '';
54
+ try {
55
+ // 1. Eager OAuth token fetch — warms the in-memory cache.
56
+ await this.oauth2.getBearerToken();
57
+ Logger.info(`${tag}Viva OAuth2 token cached successfully.`, VIVA_LOG_CONTEXT);
58
+ // 2. Pre-connect undici pools to both Viva hosts.
59
+ // Fire-and-forget: just accessing the pool initialises the connection.
60
+ // We don't need to do an actual HTTP request for the warmup.
61
+ getAuthDispatcher(this.options.environment);
62
+ getApiDispatcher(this.options.environment);
63
+ Logger.info(`${tag}Viva undici pools pre-connected (env=${this.options.environment}).`, VIVA_LOG_CONTEXT);
64
+ this._lastWarmupFailed = false;
65
+ }
66
+ catch (err) {
67
+ this._lastWarmupFailed = true;
68
+ const msg = err instanceof Error ? err.message : String(err);
69
+ if (isRetry) {
70
+ // Retry also failed — leave cache empty, log warning, stop retrying.
71
+ Logger.warn(`Viva bootstrap warmup retry failed: ${msg}. Token will be lazily fetched on first createPayment call.`, VIVA_LOG_CONTEXT);
72
+ }
73
+ else {
74
+ // First attempt failed — schedule one retry.
75
+ Logger.warn(`Viva bootstrap warmup failed: ${msg}. Scheduling retry in ${BOOTSTRAP_RETRY_DELAY_MS}ms.`, VIVA_LOG_CONTEXT);
76
+ setTimeout(() => {
77
+ void this._runWarmup(true);
78
+ }, BOOTSTRAP_RETRY_DELAY_MS);
79
+ }
80
+ }
81
+ }
82
+ };
83
+ VivaBootstrap = __decorate([
84
+ Injectable(),
85
+ __param(0, Inject(VIVA_PLUGIN_OPTIONS)),
86
+ __param(1, Inject(VIVA_OAUTH2_STRATEGY_TOKEN)),
87
+ __metadata("design:paramtypes", [Object, Object])
88
+ ], VivaBootstrap);
89
+ export { VivaBootstrap };
90
+ //# sourceMappingURL=bootstrap.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"bootstrap.js","sourceRoot":"","sources":["../../src/loaders/bootstrap.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;;;;;;;;;;;;;AAEH,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;AAEpD,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC;AACvC,OAAO,EAAE,iBAAiB,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAC;AAGzF,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,gBAAgB,EAChB,wBAAwB,GACzB,MAAM,iBAAiB,CAAC;AAEzB,8EAA8E;AAC9E,oBAAoB;AACpB,8EAA8E;AAGvE,IAAM,aAAa,GAAnB,MAAM,aAAa;IAEwB;IACO;IAFvD,YACgD,OAAiC,EAC1B,MAA0B;QADjC,YAAO,GAAP,OAAO,CAA0B;QAC1B,WAAM,GAAN,MAAM,CAAoB;IAC9E,CAAC;IAEJ,+DAA+D;IACvD,iBAAiB,GAAG,KAAK,CAAC;IAClC,IAAI,gBAAgB,KAAc,OAAO,IAAI,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAElE,KAAK,CAAC,sBAAsB;QAC1B,MAAM,CAAC,IAAI,CAAC,wCAAwC,EAAE,gBAAgB,CAAC,CAAC;QACxE,MAAM,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAED,8EAA8E;IAC9E,WAAW;IACX,8EAA8E;IAEtE,KAAK,CAAC,UAAU,CAAC,OAAgB;QACvC,MAAM,GAAG,GAAG,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC;QAEtC,IAAI,CAAC;YACH,0DAA0D;YAC1D,MAAM,IAAI,CAAC,MAAM,CAAC,cAAc,EAAE,CAAC;YACnC,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,wCAAwC,EAAE,gBAAgB,CAAC,CAAC;YAE9E,kDAAkD;YAClD,0EAA0E;YAC1E,gEAAgE;YAChE,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YAC5C,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,CAAC,CAAC;YAC3C,MAAM,CAAC,IAAI,CAAC,GAAG,GAAG,wCAAwC,IAAI,CAAC,OAAO,CAAC,WAAW,IAAI,EAAE,gBAAgB,CAAC,CAAC;YAE1G,IAAI,CAAC,iBAAiB,GAAG,KAAK,CAAC;QACjC,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,IAAI,CAAC,iBAAiB,GAAG,IAAI,CAAC;YAC9B,MAAM,GAAG,GAAG,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;YAE7D,IAAI,OAAO,EAAE,CAAC;gBACZ,qEAAqE;gBACrE,MAAM,CAAC,IAAI,CACT,uCAAuC,GAAG,6DAA6D,EACvG,gBAAgB,CACjB,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,6CAA6C;gBAC7C,MAAM,CAAC,IAAI,CACT,iCAAiC,GAAG,yBAAyB,wBAAwB,KAAK,EAC1F,gBAAgB,CACjB,CAAC;gBACF,UAAU,CAAC,GAAG,EAAE;oBACd,KAAK,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;gBAC7B,CAAC,EAAE,wBAAwB,CAAC,CAAC;YAC/B,CAAC;QACH,CAAC;IACH,CAAC;CACF,CAAA;AAzDY,aAAa;IADzB,UAAU,EAAE;IAGR,WAAA,MAAM,CAAC,mBAAmB,CAAC,CAAA;IAC3B,WAAA,MAAM,CAAC,0BAA0B,CAAC,CAAA;;GAH1B,aAAa,CAyDzB"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * 1714000000000-create-viva-tables.ts — Initial migration for Viva Wallet plugin tables.
3
+ *
4
+ * @see docs/plans/vendure-plugin-v0.md §"Data Model" + §"Migrations"
5
+ *
6
+ * Creates:
7
+ * - viva_transaction — payment lifecycle tracking
8
+ * - viva_webhook_event — webhook dedupe + audit log
9
+ *
10
+ * Column types match entity decorators exactly. Timestamps use
11
+ * `timestamp with time zone` (UTC).
12
+ *
13
+ * Partial indexes (WHERE clauses) use raw SQL via queryRunner.query() because
14
+ * TypeORM's createIndex API does not support WHERE predicates.
15
+ */
16
+ import { type MigrationInterface, type QueryRunner } from 'typeorm';
17
+ export declare class CreateVivaTables1714000000000 implements MigrationInterface {
18
+ name: string;
19
+ up(queryRunner: QueryRunner): Promise<void>;
20
+ down(queryRunner: QueryRunner): Promise<void>;
21
+ }
22
+ //# sourceMappingURL=1714000000000-create-viva-tables.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"1714000000000-create-viva-tables.d.ts","sourceRoot":"","sources":["../../src/migrations/1714000000000-create-viva-tables.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAAE,KAAK,kBAAkB,EAAE,KAAK,WAAW,EAAE,MAAM,SAAS,CAAC;AAEpE,qBAAa,6BAA8B,YAAW,kBAAkB;IACtE,IAAI,SAAmC;IAEjC,EAAE,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAiF3C,IAAI,CAAC,WAAW,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;CAYpD"}
@@ -0,0 +1,105 @@
1
+ /**
2
+ * 1714000000000-create-viva-tables.ts — Initial migration for Viva Wallet plugin tables.
3
+ *
4
+ * @see docs/plans/vendure-plugin-v0.md §"Data Model" + §"Migrations"
5
+ *
6
+ * Creates:
7
+ * - viva_transaction — payment lifecycle tracking
8
+ * - viva_webhook_event — webhook dedupe + audit log
9
+ *
10
+ * Column types match entity decorators exactly. Timestamps use
11
+ * `timestamp with time zone` (UTC).
12
+ *
13
+ * Partial indexes (WHERE clauses) use raw SQL via queryRunner.query() because
14
+ * TypeORM's createIndex API does not support WHERE predicates.
15
+ */
16
+ import {} from 'typeorm';
17
+ export class CreateVivaTables1714000000000 {
18
+ name = 'CreateVivaTables1714000000000';
19
+ async up(queryRunner) {
20
+ // -----------------------------------------------------------------------
21
+ // viva_transaction
22
+ // -----------------------------------------------------------------------
23
+ await queryRunner.query(`
24
+ CREATE TABLE IF NOT EXISTS "viva_transaction" (
25
+ "id" uuid NOT NULL DEFAULT gen_random_uuid(),
26
+ "channel_id" integer NOT NULL,
27
+ "payment_id" integer NOT NULL,
28
+ "viva_order_code" character varying DEFAULT NULL,
29
+ "viva_transaction_id" character varying DEFAULT NULL,
30
+ "status" character varying NOT NULL,
31
+ "amount_minor" bigint NOT NULL,
32
+ "currency_code" character varying(3) NOT NULL,
33
+ "isv_amount_minor" bigint NOT NULL DEFAULT 0,
34
+ "metadata" jsonb NOT NULL DEFAULT '{}',
35
+ "created_at" timestamp with time zone NOT NULL DEFAULT now(),
36
+ "updated_at" timestamp with time zone NOT NULL DEFAULT now(),
37
+ CONSTRAINT "PK_viva_transaction" PRIMARY KEY ("id")
38
+ )
39
+ `);
40
+ // Composite unique: one transaction row per (channel, payment).
41
+ await queryRunner.query(`
42
+ CREATE UNIQUE INDEX IF NOT EXISTS "idx_viva_transaction_channel_payment"
43
+ ON "viva_transaction" ("channel_id", "payment_id")
44
+ `);
45
+ // Partial unique: viva_order_code is unique only among non-NULL values
46
+ // (two NULL rows are fine — they represent payments pre-redirect).
47
+ await queryRunner.query(`
48
+ CREATE UNIQUE INDEX IF NOT EXISTS "idx_viva_transaction_order_code"
49
+ ON "viva_transaction" ("viva_order_code")
50
+ WHERE "viva_order_code" IS NOT NULL
51
+ `);
52
+ // Non-unique: channel-scoped order-code lookups + transaction-id lookups.
53
+ await queryRunner.query(`
54
+ CREATE INDEX IF NOT EXISTS "idx_viva_transaction_channel_order_code"
55
+ ON "viva_transaction" ("channel_id", "viva_order_code")
56
+ `);
57
+ await queryRunner.query(`
58
+ CREATE INDEX IF NOT EXISTS "idx_viva_transaction_transaction_id"
59
+ ON "viva_transaction" ("viva_transaction_id")
60
+ `);
61
+ // -----------------------------------------------------------------------
62
+ // viva_webhook_event
63
+ // -----------------------------------------------------------------------
64
+ await queryRunner.query(`
65
+ CREATE TABLE IF NOT EXISTS "viva_webhook_event" (
66
+ "message_id" uuid NOT NULL,
67
+ "event_type_id" integer NOT NULL,
68
+ "merchant_id" uuid DEFAULT NULL,
69
+ "transaction_id" character varying DEFAULT NULL,
70
+ "account_id" character varying DEFAULT NULL,
71
+ "correlation_id" character varying DEFAULT NULL,
72
+ "retry_count" integer NOT NULL DEFAULT 0,
73
+ "payload" jsonb NOT NULL,
74
+ "received_at" timestamp with time zone NOT NULL DEFAULT now(),
75
+ "processed_at" timestamp with time zone DEFAULT NULL,
76
+ "error" text DEFAULT NULL,
77
+ CONSTRAINT "PK_viva_webhook_event" PRIMARY KEY ("message_id")
78
+ )
79
+ `);
80
+ // Non-unique composite: merchant + event type queries (A6 re-walk, stats).
81
+ await queryRunner.query(`
82
+ CREATE INDEX IF NOT EXISTS "idx_viva_webhook_event_merchant_type"
83
+ ON "viva_webhook_event" ("merchant_id", "event_type_id")
84
+ `);
85
+ // Partial index: efficient pending-event scan (WHERE processed_at IS NULL).
86
+ // Raw SQL required — TypeORM createIndex API has no WHERE support.
87
+ await queryRunner.query(`
88
+ CREATE INDEX IF NOT EXISTS "idx_viva_webhook_event_pending_received"
89
+ ON "viva_webhook_event" ("received_at")
90
+ WHERE "processed_at" IS NULL
91
+ `);
92
+ }
93
+ async down(queryRunner) {
94
+ // Drop indexes before tables (some DBs require explicit drops).
95
+ await queryRunner.query(`DROP INDEX IF EXISTS "idx_viva_webhook_event_pending_received"`);
96
+ await queryRunner.query(`DROP INDEX IF EXISTS "idx_viva_webhook_event_merchant_type"`);
97
+ await queryRunner.query(`DROP TABLE IF EXISTS "viva_webhook_event"`);
98
+ await queryRunner.query(`DROP INDEX IF EXISTS "idx_viva_transaction_transaction_id"`);
99
+ await queryRunner.query(`DROP INDEX IF EXISTS "idx_viva_transaction_channel_order_code"`);
100
+ await queryRunner.query(`DROP INDEX IF EXISTS "idx_viva_transaction_order_code"`);
101
+ await queryRunner.query(`DROP INDEX IF EXISTS "idx_viva_transaction_channel_payment"`);
102
+ await queryRunner.query(`DROP TABLE IF EXISTS "viva_transaction"`);
103
+ }
104
+ }
105
+ //# sourceMappingURL=1714000000000-create-viva-tables.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"1714000000000-create-viva-tables.js","sourceRoot":"","sources":["../../src/migrations/1714000000000-create-viva-tables.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,EAA6C,MAAM,SAAS,CAAC;AAEpE,MAAM,OAAO,6BAA6B;IACxC,IAAI,GAAG,+BAA+B,CAAC;IAEvC,KAAK,CAAC,EAAE,CAAC,WAAwB;QAC/B,0EAA0E;QAC1E,mBAAmB;QACnB,0EAA0E;QAC1E,MAAM,WAAW,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;;KAgBvB,CAAC,CAAC;QAEH,gEAAgE;QAChE,MAAM,WAAW,CAAC,KAAK,CAAC;;;KAGvB,CAAC,CAAC;QAEH,uEAAuE;QACvE,mEAAmE;QACnE,MAAM,WAAW,CAAC,KAAK,CAAC;;;;KAIvB,CAAC,CAAC;QAEH,0EAA0E;QAC1E,MAAM,WAAW,CAAC,KAAK,CAAC;;;KAGvB,CAAC,CAAC;QACH,MAAM,WAAW,CAAC,KAAK,CAAC;;;KAGvB,CAAC,CAAC;QAEH,0EAA0E;QAC1E,qBAAqB;QACrB,0EAA0E;QAC1E,MAAM,WAAW,CAAC,KAAK,CAAC;;;;;;;;;;;;;;;KAevB,CAAC,CAAC;QAEH,2EAA2E;QAC3E,MAAM,WAAW,CAAC,KAAK,CAAC;;;KAGvB,CAAC,CAAC;QAEH,4EAA4E;QAC5E,mEAAmE;QACnE,MAAM,WAAW,CAAC,KAAK,CAAC;;;;KAIvB,CAAC,CAAC;IACL,CAAC;IAED,KAAK,CAAC,IAAI,CAAC,WAAwB;QACjC,gEAAgE;QAChE,MAAM,WAAW,CAAC,KAAK,CAAC,gEAAgE,CAAC,CAAC;QAC1F,MAAM,WAAW,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;QACvF,MAAM,WAAW,CAAC,KAAK,CAAC,2CAA2C,CAAC,CAAC;QAErE,MAAM,WAAW,CAAC,KAAK,CAAC,4DAA4D,CAAC,CAAC;QACtF,MAAM,WAAW,CAAC,KAAK,CAAC,gEAAgE,CAAC,CAAC;QAC1F,MAAM,WAAW,CAAC,KAAK,CAAC,wDAAwD,CAAC,CAAC;QAClF,MAAM,WAAW,CAAC,KAAK,CAAC,6DAA6D,CAAC,CAAC;QACvF,MAAM,WAAW,CAAC,KAAK,CAAC,yCAAyC,CAAC,CAAC;IACrE,CAAC;CACF"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * observability/metrics-state.service.ts — In-process metrics state singleton.
3
+ *
4
+ * Hand-rolled Prometheus-compatible metrics. No prom-client dependency.
5
+ * Registered as a NestJS injectable singleton. Injected into payment handler,
6
+ * webhook controller, and webhook job to increment counters at the right call sites.
7
+ *
8
+ * Metrics exported:
9
+ * viva_oauth2_token_present gauge (0 or 1)
10
+ * viva_oauth2_token_expires_in_seconds gauge
11
+ * viva_webhook_events_received_total counter (by event_type_id)
12
+ * viva_webhook_events_processed_total counter (by event_type_id)
13
+ * viva_webhook_events_pending gauge
14
+ * viva_webhook_event_oldest_pending_age_seconds gauge
15
+ * viva_payment_state_transitions_total counter (by from, to)
16
+ * viva_isv_api_call_duration_seconds histogram (by endpoint)
17
+ *
18
+ * Histogram buckets (seconds): [0.05, 0.1, 0.25, 0.5, 1, 2, 5]
19
+ * Rationale: Viva API P99 should be <2s; 0.05–5 covers the useful range with
20
+ * 7 finite buckets matching plan spec exactly.
21
+ *
22
+ * @see docs/plans/vendure-plugin-v0.md §"Build Plan — V10"
23
+ */
24
+ export declare const ISV_API_DURATION_BUCKETS: readonly number[];
25
+ export declare class MetricsStateService {
26
+ private _tokenPresent;
27
+ private _tokenExpiresInSeconds;
28
+ private _webhookEventsPending;
29
+ private _webhookOldestPendingAgeSeconds;
30
+ private readonly _counters;
31
+ private readonly _counterLabels;
32
+ private readonly _histograms;
33
+ setTokenPresent(present: boolean, expiresInSeconds?: number): void;
34
+ setWebhookEventsPending(count: number): void;
35
+ setWebhookOldestPendingAgeSeconds(ageSeconds: number | null): void;
36
+ incrementCounter(name: string, labels?: Record<string, string>, delta?: number): void;
37
+ recordWebhookReceived(eventTypeId: number): void;
38
+ recordWebhookProcessed(eventTypeId: number): void;
39
+ recordPaymentStateTransition(from: string, to: string): void;
40
+ recordIsvApiCall(endpoint: string, durationSeconds: number): void;
41
+ toPromText(): string;
42
+ }
43
+ //# sourceMappingURL=metrics-state.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics-state.service.d.ts","sourceRoot":"","sources":["../../src/observability/metrics-state.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAQH,eAAO,MAAM,wBAAwB,EAAE,SAAS,MAAM,EAEpD,CAAC;AAkCH,qBACa,mBAAmB;IAI9B,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,sBAAsB,CAAK;IACnC,OAAO,CAAC,qBAAqB,CAAK;IAClC,OAAO,CAAC,+BAA+B,CAAK;IAK5C,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA0C;IACpE,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA0D;IAKzF,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAqC;IAMjE,eAAe,CAAC,OAAO,EAAE,OAAO,EAAE,gBAAgB,SAAI,GAAG,IAAI;IAK7D,uBAAuB,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI;IAI5C,iCAAiC,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI;IAQlE,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAM,EAAE,KAAK,SAAI,GAAG,IAAI;IAkBpF,qBAAqB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAMhD,sBAAsB,CAAC,WAAW,EAAE,MAAM,GAAG,IAAI;IAMjD,4BAA4B,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,GAAG,IAAI;IAQ5D,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,GAAG,IAAI;IA0CjE,UAAU,IAAI,MAAM;CAuDrB"}
@@ -0,0 +1,207 @@
1
+ /**
2
+ * observability/metrics-state.service.ts — In-process metrics state singleton.
3
+ *
4
+ * Hand-rolled Prometheus-compatible metrics. No prom-client dependency.
5
+ * Registered as a NestJS injectable singleton. Injected into payment handler,
6
+ * webhook controller, and webhook job to increment counters at the right call sites.
7
+ *
8
+ * Metrics exported:
9
+ * viva_oauth2_token_present gauge (0 or 1)
10
+ * viva_oauth2_token_expires_in_seconds gauge
11
+ * viva_webhook_events_received_total counter (by event_type_id)
12
+ * viva_webhook_events_processed_total counter (by event_type_id)
13
+ * viva_webhook_events_pending gauge
14
+ * viva_webhook_event_oldest_pending_age_seconds gauge
15
+ * viva_payment_state_transitions_total counter (by from, to)
16
+ * viva_isv_api_call_duration_seconds histogram (by endpoint)
17
+ *
18
+ * Histogram buckets (seconds): [0.05, 0.1, 0.25, 0.5, 1, 2, 5]
19
+ * Rationale: Viva API P99 should be <2s; 0.05–5 covers the useful range with
20
+ * 7 finite buckets matching plan spec exactly.
21
+ *
22
+ * @see docs/plans/vendure-plugin-v0.md §"Build Plan — V10"
23
+ */
24
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
25
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
26
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
27
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
28
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
29
+ };
30
+ import { Injectable } from '@nestjs/common';
31
+ // ---------------------------------------------------------------------------
32
+ // Histogram buckets
33
+ // ---------------------------------------------------------------------------
34
+ export const ISV_API_DURATION_BUCKETS = Object.freeze([
35
+ 0.05, 0.1, 0.25, 0.5, 1, 2, 5,
36
+ ]);
37
+ // ---------------------------------------------------------------------------
38
+ // Helpers
39
+ // ---------------------------------------------------------------------------
40
+ function labelKey(labels) {
41
+ const keys = Object.keys(labels).sort();
42
+ return keys.map((k) => `${k}=${labels[k]}`).join(',');
43
+ }
44
+ function labelStr(labels) {
45
+ const keys = Object.keys(labels).sort();
46
+ if (keys.length === 0)
47
+ return '';
48
+ const parts = keys.map((k) => `${k}="${labels[k]}"`);
49
+ return `{${parts.join(',')}}`;
50
+ }
51
+ // ---------------------------------------------------------------------------
52
+ // MetricsStateService
53
+ // ---------------------------------------------------------------------------
54
+ let MetricsStateService = class MetricsStateService {
55
+ // -------------------------------------------------------------------------
56
+ // Gauge state
57
+ // -------------------------------------------------------------------------
58
+ _tokenPresent = 0;
59
+ _tokenExpiresInSeconds = 0;
60
+ _webhookEventsPending = 0;
61
+ _webhookOldestPendingAgeSeconds = 0;
62
+ // -------------------------------------------------------------------------
63
+ // Counter state: name → labelKey → value
64
+ // -------------------------------------------------------------------------
65
+ _counters = new Map();
66
+ _counterLabels = new Map();
67
+ // -------------------------------------------------------------------------
68
+ // Histogram state: name → HistogramState
69
+ // -------------------------------------------------------------------------
70
+ _histograms = new Map();
71
+ // ---------------------------------------------------------------------------
72
+ // Gauge setters — called by bootstrap, auth strategy refresh
73
+ // ---------------------------------------------------------------------------
74
+ setTokenPresent(present, expiresInSeconds = 0) {
75
+ this._tokenPresent = present ? 1 : 0;
76
+ this._tokenExpiresInSeconds = present ? Math.max(0, expiresInSeconds) : 0;
77
+ }
78
+ setWebhookEventsPending(count) {
79
+ this._webhookEventsPending = count;
80
+ }
81
+ setWebhookOldestPendingAgeSeconds(ageSeconds) {
82
+ this._webhookOldestPendingAgeSeconds = ageSeconds ?? 0;
83
+ }
84
+ // ---------------------------------------------------------------------------
85
+ // Counter increment
86
+ // ---------------------------------------------------------------------------
87
+ incrementCounter(name, labels = {}, delta = 1) {
88
+ let byLabel = this._counters.get(name);
89
+ let byLabelObj = this._counterLabels.get(name);
90
+ if (!byLabel) {
91
+ byLabel = new Map();
92
+ this._counters.set(name, byLabel);
93
+ }
94
+ if (!byLabelObj) {
95
+ byLabelObj = new Map();
96
+ this._counterLabels.set(name, byLabelObj);
97
+ }
98
+ const key = labelKey(labels);
99
+ byLabel.set(key, (byLabel.get(key) ?? 0) + delta);
100
+ byLabelObj.set(key, labels);
101
+ }
102
+ // Convenience wrappers
103
+ recordWebhookReceived(eventTypeId) {
104
+ this.incrementCounter('viva_webhook_events_received_total', {
105
+ event_type_id: String(eventTypeId),
106
+ });
107
+ }
108
+ recordWebhookProcessed(eventTypeId) {
109
+ this.incrementCounter('viva_webhook_events_processed_total', {
110
+ event_type_id: String(eventTypeId),
111
+ });
112
+ }
113
+ recordPaymentStateTransition(from, to) {
114
+ this.incrementCounter('viva_payment_state_transitions_total', { from, to });
115
+ }
116
+ // ---------------------------------------------------------------------------
117
+ // Histogram record
118
+ // ---------------------------------------------------------------------------
119
+ recordIsvApiCall(endpoint, durationSeconds) {
120
+ const name = 'viva_isv_api_call_duration_seconds';
121
+ const labels = { endpoint };
122
+ const key = labelKey(labels);
123
+ let state = this._histograms.get(name);
124
+ if (!state) {
125
+ state = {
126
+ counts: new Map(),
127
+ sum: new Map(),
128
+ count: new Map(),
129
+ labelMap: new Map(),
130
+ };
131
+ this._histograms.set(name, state);
132
+ }
133
+ state.labelMap.set(key, labels);
134
+ if (!state.counts.has(key)) {
135
+ // +1 slot for +Inf
136
+ state.counts.set(key, new Array(ISV_API_DURATION_BUCKETS.length + 1).fill(0));
137
+ }
138
+ const bucketCounts = state.counts.get(key);
139
+ for (let i = 0; i < ISV_API_DURATION_BUCKETS.length; i++) {
140
+ if (durationSeconds <= ISV_API_DURATION_BUCKETS[i]) {
141
+ bucketCounts[i];
142
+ bucketCounts[i] = (bucketCounts[i] ?? 0) + 1;
143
+ }
144
+ }
145
+ // +Inf always
146
+ bucketCounts[ISV_API_DURATION_BUCKETS.length] =
147
+ (bucketCounts[ISV_API_DURATION_BUCKETS.length] ?? 0) + 1;
148
+ state.sum.set(key, (state.sum.get(key) ?? 0) + durationSeconds);
149
+ state.count.set(key, (state.count.get(key) ?? 0) + 1);
150
+ }
151
+ // ---------------------------------------------------------------------------
152
+ // Prometheus text exposition
153
+ // ---------------------------------------------------------------------------
154
+ toPromText() {
155
+ const lines = [];
156
+ // Gauges
157
+ lines.push('# HELP viva_oauth2_token_present 1 if an OAuth2 token is cached, 0 otherwise.');
158
+ lines.push('# TYPE viva_oauth2_token_present gauge');
159
+ lines.push(`viva_oauth2_token_present ${this._tokenPresent}`);
160
+ lines.push('');
161
+ lines.push('# HELP viva_oauth2_token_expires_in_seconds Seconds until the cached OAuth2 token expires.');
162
+ lines.push('# TYPE viva_oauth2_token_expires_in_seconds gauge');
163
+ lines.push(`viva_oauth2_token_expires_in_seconds ${this._tokenExpiresInSeconds}`);
164
+ lines.push('');
165
+ lines.push('# HELP viva_webhook_events_pending Number of webhook events with processed_at IS NULL.');
166
+ lines.push('# TYPE viva_webhook_events_pending gauge');
167
+ lines.push(`viva_webhook_events_pending ${this._webhookEventsPending}`);
168
+ lines.push('');
169
+ lines.push('# HELP viva_webhook_event_oldest_pending_age_seconds Age in seconds of the oldest pending webhook event.');
170
+ lines.push('# TYPE viva_webhook_event_oldest_pending_age_seconds gauge');
171
+ lines.push(`viva_webhook_event_oldest_pending_age_seconds ${this._webhookOldestPendingAgeSeconds}`);
172
+ lines.push('');
173
+ // Counters
174
+ for (const [name, byLabel] of this._counters) {
175
+ const labelObjMap = this._counterLabels.get(name);
176
+ lines.push(`# HELP ${name} Viva plugin counter.`);
177
+ lines.push(`# TYPE ${name} counter`);
178
+ for (const [key, value] of byLabel) {
179
+ const labels = labelObjMap.get(key) ?? {};
180
+ lines.push(`${name}${labelStr(labels)} ${value}`);
181
+ }
182
+ lines.push('');
183
+ }
184
+ // Histograms
185
+ for (const [name, state] of this._histograms) {
186
+ lines.push(`# HELP ${name} Viva ISV API call duration in seconds.`);
187
+ lines.push(`# TYPE ${name} histogram`);
188
+ for (const [key, bucketCounts] of state.counts) {
189
+ const labels = state.labelMap.get(key) ?? {};
190
+ for (let i = 0; i < ISV_API_DURATION_BUCKETS.length; i++) {
191
+ const le = ISV_API_DURATION_BUCKETS[i];
192
+ lines.push(`${name}_bucket${labelStr({ ...labels, le: String(le) })} ${bucketCounts[i] ?? 0}`);
193
+ }
194
+ lines.push(`${name}_bucket${labelStr({ ...labels, le: '+Inf' })} ${bucketCounts[ISV_API_DURATION_BUCKETS.length] ?? 0}`);
195
+ lines.push(`${name}_sum${labelStr(labels)} ${state.sum.get(key) ?? 0}`);
196
+ lines.push(`${name}_count${labelStr(labels)} ${state.count.get(key) ?? 0}`);
197
+ }
198
+ lines.push('');
199
+ }
200
+ return lines.join('\n');
201
+ }
202
+ };
203
+ MetricsStateService = __decorate([
204
+ Injectable()
205
+ ], MetricsStateService);
206
+ export { MetricsStateService };
207
+ //# sourceMappingURL=metrics-state.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metrics-state.service.js","sourceRoot":"","sources":["../../src/observability/metrics-state.service.ts"],"names":[],"mappings":"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"}