chargebee 3.27.0 → 3.28.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.
@@ -1,4 +1,5 @@
1
1
  import { extend, callbackifyPromise, getApiURL, encodeListParams, encodeParams, serialize, getHost, log, generateUUIDv4 as uuidv4, } from './util.js';
2
+ import { buildRequestTelemetryContext, buildRequestTelemetryResult, extractHttpStatusCode, extractRequestTelemetryError, resolveChargebeeApiVersion, } from './telemetry/index.js';
2
3
  import { handleResponse } from './coreCommon.js';
3
4
  import { Buffer } from 'node:buffer';
4
5
  import { ChargebeeZodValidationError } from './chargebeeZodValidationError.js';
@@ -42,9 +43,28 @@ export class RequestWrapper {
42
43
  }
43
44
  return null;
44
45
  }
46
+ _buildRequestUrl(env, urlIdParam, params) {
47
+ let path = getApiURL(env, this.apiCall.urlPrefix, this.apiCall.urlSuffix, urlIdParam);
48
+ if (this.apiCall.httpMethod === 'GET') {
49
+ let requestParams = params;
50
+ if (typeof requestParams === 'undefined' || requestParams === null) {
51
+ requestParams = {};
52
+ }
53
+ const queryParam = this.apiCall.isListReq
54
+ ? encodeListParams(serialize(requestParams))
55
+ : encodeParams(serialize(requestParams));
56
+ path += '?' + queryParam;
57
+ }
58
+ return new URL(path, `${env.protocol}://${getHost(env, this.apiCall.subDomain)}${env.port ? `:${env.port}` : ''}`);
59
+ }
45
60
  async request() {
46
61
  let _env = {};
47
62
  extend(true, _env, this.envArg);
63
+ // Class-based adapters (e.g. OpenTelemetry) keep methods on the prototype;
64
+ // deep extend only copies own enumerable properties, so preserve by reference.
65
+ if (this.envArg.telemetryAdapter !== undefined) {
66
+ _env.telemetryAdapter = this.envArg.telemetryAdapter;
67
+ }
48
68
  const env = _env;
49
69
  const retryConfig = Object.assign({ enabled: false, maxRetries: 3, delayMs: 200, retryOn: [500, 502, 503, 504] }, env.retryConfig);
50
70
  const urlIdParam = this.apiCall.hasIdInUrl ? this.args[0] : null;
@@ -74,19 +94,43 @@ export class RequestWrapper {
74
94
  retryConfig.enabled) {
75
95
  this.httpHeaders['chargebee-idempotency-key'] = uuidv4();
76
96
  }
97
+ const telemetryAdapter = env.telemetryAdapter;
98
+ const telemetryHeaders = {};
99
+ const requestStartTime = Date.now();
100
+ const requestUrl = this._buildRequestUrl(env, urlIdParam, params);
101
+ // No telemetry adapter configured => skip all telemetry work (zero overhead).
102
+ let telemetryHandle;
103
+ if (telemetryAdapter !== undefined) {
104
+ const telemetryContext = buildRequestTelemetryContext({
105
+ resource: this.apiCall.resource,
106
+ operation: this.apiCall.methodName,
107
+ httpMethod: this.apiCall.httpMethod,
108
+ httpUrl: `${requestUrl.origin}${requestUrl.pathname}`,
109
+ serverAddress: requestUrl.hostname,
110
+ chargebeeSite: env.site,
111
+ chargebeeApiVersion: resolveChargebeeApiVersion(env.apiPath),
112
+ sdkVersion: env.clientVersion,
113
+ requestHeaders: this.httpHeaders,
114
+ });
115
+ try {
116
+ telemetryHandle = telemetryAdapter.onRequestStart(telemetryContext, telemetryHeaders);
117
+ }
118
+ catch (err) {
119
+ const message = err instanceof Error
120
+ ? err.message
121
+ : 'Unknown telemetry adapter error';
122
+ log(env, {
123
+ level: 'ERROR',
124
+ message: `Telemetry adapter onRequestStart failed: ${message}. Continuing without telemetry.`,
125
+ });
126
+ telemetryHandle = undefined;
127
+ }
128
+ }
77
129
  const makeRequest = async (attempt = 0) => {
78
- let path = getApiURL(env, this.apiCall.urlPrefix, this.apiCall.urlSuffix, urlIdParam);
79
130
  let requestParams = params;
80
131
  if (typeof requestParams === 'undefined' || requestParams === null) {
81
132
  requestParams = {};
82
133
  }
83
- if (this.apiCall.httpMethod === 'GET') {
84
- const queryParam = this.apiCall.isListReq
85
- ? encodeListParams(serialize(requestParams))
86
- : encodeParams(serialize(requestParams));
87
- path += '?' + queryParam;
88
- requestParams = {};
89
- }
90
134
  const jsonKeys = this.apiCall.jsonKeys;
91
135
  let data = null;
92
136
  if (this.apiCall.httpMethod !== 'GET') {
@@ -94,12 +138,7 @@ export class RequestWrapper {
94
138
  ? JSON.stringify(requestParams)
95
139
  : encodeParams(requestParams, undefined, undefined, undefined, jsonKeys);
96
140
  }
97
- const requestHeaders = Object.assign({}, this.httpHeaders);
98
- if (data && data.length) {
99
- extend(true, requestHeaders, {
100
- 'Content-Length': Buffer.byteLength(data, 'utf8'),
101
- });
102
- }
141
+ const requestHeaders = Object.assign(Object.assign({}, this.httpHeaders), telemetryHeaders);
103
142
  const contentType = this.apiCall.isJsonRequest
104
143
  ? 'application/json;charset=UTF-8'
105
144
  : 'application/x-www-form-urlencoded; charset=utf-8';
@@ -114,8 +153,7 @@ export class RequestWrapper {
114
153
  if (attempt > 0) {
115
154
  requestHeaders['X-CB-Retry-Attempt'] = attempt.toString();
116
155
  }
117
- const url = new URL(path, `${env.protocol}://${getHost(env, this.apiCall.subDomain)}${env.port ? `:${env.port}` : ''}`);
118
- const request = new Request(url, {
156
+ const request = new Request(requestUrl, {
119
157
  method: this.apiCall.httpMethod,
120
158
  body: data || undefined,
121
159
  headers: this._createHeaders(requestHeaders),
@@ -177,7 +215,55 @@ export class RequestWrapper {
177
215
  return await withRetry(retryCount + 1, startTime);
178
216
  }
179
217
  };
180
- const promise = withRetry(0, Date.now());
218
+ const runWithTelemetry = async (adapter) => {
219
+ var _a;
220
+ try {
221
+ const result = await withRetry(0, requestStartTime);
222
+ const httpStatusCode = typeof (result === null || result === void 0 ? void 0 : result.httpStatusCode) === 'number'
223
+ ? result.httpStatusCode
224
+ : 200;
225
+ try {
226
+ adapter.onRequestEnd(telemetryHandle, buildRequestTelemetryResult({
227
+ httpStatusCode,
228
+ durationMs: Date.now() - requestStartTime,
229
+ }));
230
+ }
231
+ catch (err) {
232
+ const message = err instanceof Error
233
+ ? err.message
234
+ : 'Unknown telemetry adapter error';
235
+ log(env, {
236
+ level: 'ERROR',
237
+ message: `Telemetry adapter onRequestEnd failed: ${message}.`,
238
+ });
239
+ }
240
+ return result;
241
+ }
242
+ catch (err) {
243
+ const httpStatusCode = (_a = extractHttpStatusCode(err)) !== null && _a !== void 0 ? _a : 500;
244
+ const telemetryError = extractRequestTelemetryError(err);
245
+ try {
246
+ adapter.onRequestEnd(telemetryHandle, buildRequestTelemetryResult({
247
+ httpStatusCode,
248
+ durationMs: Date.now() - requestStartTime,
249
+ error: telemetryError,
250
+ }));
251
+ }
252
+ catch (telemetryErr) {
253
+ const message = telemetryErr instanceof Error
254
+ ? telemetryErr.message
255
+ : 'Unknown telemetry adapter error';
256
+ log(env, {
257
+ level: 'ERROR',
258
+ message: `Telemetry adapter onRequestEnd failed: ${message}.`,
259
+ });
260
+ }
261
+ throw err;
262
+ }
263
+ };
264
+ const promise = telemetryAdapter !== undefined
265
+ ? runWithTelemetry(telemetryAdapter)
266
+ : withRetry(0, requestStartTime);
181
267
  return callbackifyPromise(promise);
182
268
  }
183
269
  _createHeaders(httpHeaders) {
@@ -7,5 +7,6 @@ export default Chargebee;
7
7
  export { WebhookEventType, WebhookContentType, } from './resources/webhook/handler.js';
8
8
  export { basicAuthValidator } from './resources/webhook/auth.js';
9
9
  export { WebhookError, WebhookAuthenticationError, WebhookPayloadValidationError, WebhookPayloadParseError, } from './resources/webhook/handler.js';
10
+ export { TelemetryAttributeKeys } from './telemetry/index.js';
10
11
  // Export validation error class
11
12
  export { ChargebeeZodValidationError } from './chargebeeZodValidationError.js';
@@ -1,3 +1,14 @@
1
+ var __rest = (this && this.__rest) || function (s, e) {
2
+ var t = {};
3
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
4
+ t[p] = s[p];
5
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
6
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
7
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
8
+ t[p[i]] = s[p[i]];
9
+ }
10
+ return t;
11
+ };
1
12
  import { RequestWrapper } from './RequestWrapper.js';
2
13
  import { Environment } from './environment.js';
3
14
  import { Endpoints } from './resources/api_endpoints.js';
@@ -7,10 +18,14 @@ import { WebhookHandler, createDefaultHandler, } from './resources/webhook/handl
7
18
  export const CreateChargebee = (httpClient) => {
8
19
  const Chargebee = function (conf) {
9
20
  this._env = Object.assign({}, Environment);
10
- extend(true, this._env, conf);
21
+ const { telemetryAdapter, httpClient: configHttpClient } = conf, confToMerge = __rest(conf, ["telemetryAdapter", "httpClient"]);
22
+ extend(true, this._env, confToMerge);
11
23
  // @ts-ignore
12
24
  this._env.httpClient =
13
- conf.httpClient != null ? conf.httpClient : httpClient;
25
+ configHttpClient != null ? configHttpClient : httpClient;
26
+ if (telemetryAdapter !== undefined) {
27
+ this._env.telemetryAdapter = telemetryAdapter;
28
+ }
14
29
  this._buildResources();
15
30
  this._endpoints = Endpoints;
16
31
  // Initialize webhooks handler with auto-configured Basic Auth (if env vars are set)
@@ -82,6 +97,7 @@ export const CreateChargebee = (httpClient) => {
82
97
  for (let apiIdx = 0; apiIdx < apiCalls.length; apiIdx++) {
83
98
  const metaArr = apiCalls[apiIdx];
84
99
  const apiCall = {
100
+ resource: res,
85
101
  methodName: metaArr[0],
86
102
  httpMethod: metaArr[1],
87
103
  urlPrefix: metaArr[2],
@@ -8,7 +8,7 @@ export const Environment = {
8
8
  hostSuffix: '.chargebee.com',
9
9
  apiPath: '/api/v2',
10
10
  timeout: DEFAULT_TIME_OUT,
11
- clientVersion: 'v3.27.0',
11
+ clientVersion: 'v3.28.1',
12
12
  port: DEFAULT_PORT,
13
13
  timemachineWaitInMillis: DEFAULT_TIME_MACHINE_WAIT,
14
14
  exportWaitInMillis: DEFAULT_EXPORT_WAIT,
@@ -0,0 +1,132 @@
1
+ /*
2
+ * This file is auto-generated by Chargebee.
3
+ * For more information on how to make changes to this file, please see the README.
4
+ * Reach out to dx@chargebee.com for any questions.
5
+ * Copyright 2026 Chargebee Inc.
6
+ */
7
+ import { CHARGEBEE_SDK_NAME, CHARGEBEE_TELEMETRY_HEADER_EXCLUDE_PREFIX, CHARGEBEE_TELEMETRY_HEADER_PREFIX, HTTP_REQUEST_HEADER_ATTRIBUTE_PREFIX, TELEMETRY_SPAN_NAME_PREFIX, TelemetryAttributeKeys, } from './types.js';
8
+ export class NoOpTelemetryAdapter {
9
+ onRequestStart() {
10
+ return;
11
+ }
12
+ onRequestEnd() {
13
+ return;
14
+ }
15
+ }
16
+ export const NO_OP_TELEMETRY_ADAPTER = new NoOpTelemetryAdapter();
17
+ export function buildSpanName(resource, operation) {
18
+ return `${TELEMETRY_SPAN_NAME_PREFIX}.${resource}.${operation}`;
19
+ }
20
+ export function resolveChargebeeApiVersion(apiPath) {
21
+ return apiPath === '/api/v1' ? 'v1' : 'v2';
22
+ }
23
+ /**
24
+ * Captures Chargebee custom request headers as OTel span attributes.
25
+ *
26
+ * Headers whose (lowercased) name starts with `chargebee-` are recorded as
27
+ * `http.request.header.<name>` with string[] values, per the OpenTelemetry HTTP semantic
28
+ * conventions. The `chargebee-request-origin-*` family (origin IP, email, device) is skipped
29
+ * because it carries end-user PII. Matching by prefix means new `chargebee-*` headers are
30
+ * captured automatically without an SDK upgrade.
31
+ */
32
+ export function buildRequestHeaderSpanAttributes(requestHeaders) {
33
+ const attributes = {};
34
+ if (!requestHeaders) {
35
+ return attributes;
36
+ }
37
+ for (const [name, value] of Object.entries(requestHeaders)) {
38
+ if (value === undefined || value === null) {
39
+ continue;
40
+ }
41
+ const lowerName = name.toLowerCase();
42
+ if (!lowerName.startsWith(CHARGEBEE_TELEMETRY_HEADER_PREFIX) ||
43
+ lowerName.startsWith(CHARGEBEE_TELEMETRY_HEADER_EXCLUDE_PREFIX)) {
44
+ continue;
45
+ }
46
+ attributes[`${HTTP_REQUEST_HEADER_ATTRIBUTE_PREFIX}${lowerName}`] = [
47
+ String(value),
48
+ ];
49
+ }
50
+ return attributes;
51
+ }
52
+ export function buildRequestStartSpanAttributes(input) {
53
+ return Object.assign({ [TelemetryAttributeKeys.URL_FULL]: input.httpUrl, [TelemetryAttributeKeys.HTTP_REQUEST_METHOD]: input.httpMethod, [TelemetryAttributeKeys.SERVER_ADDRESS]: input.serverAddress, [TelemetryAttributeKeys.CHARGEBEE_SITE]: input.chargebeeSite, [TelemetryAttributeKeys.CHARGEBEE_API_VERSION]: input.chargebeeApiVersion, [TelemetryAttributeKeys.CHARGEBEE_RESOURCE]: input.resource, [TelemetryAttributeKeys.CHARGEBEE_OPERATION]: input.operation, [TelemetryAttributeKeys.CHARGEBEE_SDK_NAME]: CHARGEBEE_SDK_NAME, [TelemetryAttributeKeys.CHARGEBEE_SDK_VERSION]: input.sdkVersion }, buildRequestHeaderSpanAttributes(input.requestHeaders));
54
+ }
55
+ export function buildRequestEndSpanAttributes(result) {
56
+ const attributes = {
57
+ [TelemetryAttributeKeys.HTTP_RESPONSE_STATUS_CODE]: result.httpStatusCode,
58
+ };
59
+ if (result.error) {
60
+ // error.type is the status code on failed requests
61
+ attributes[TelemetryAttributeKeys.ERROR_TYPE] = String(result.httpStatusCode);
62
+ if (result.error.chargebeeErrorCode) {
63
+ attributes[TelemetryAttributeKeys.CHARGEBEE_ERROR_CODE] =
64
+ result.error.chargebeeErrorCode;
65
+ }
66
+ if (result.error.chargebeeApiErrorType) {
67
+ attributes[TelemetryAttributeKeys.CHARGEBEE_ERROR_TYPE] =
68
+ result.error.chargebeeApiErrorType;
69
+ }
70
+ if (result.error.chargebeeErrorParam) {
71
+ attributes[TelemetryAttributeKeys.CHARGEBEE_ERROR_PARAM] =
72
+ result.error.chargebeeErrorParam;
73
+ }
74
+ }
75
+ return attributes;
76
+ }
77
+ export function buildRequestTelemetryContext(input) {
78
+ return {
79
+ spanName: buildSpanName(input.resource, input.operation),
80
+ resource: input.resource,
81
+ operation: input.operation,
82
+ httpMethod: input.httpMethod,
83
+ httpUrl: input.httpUrl,
84
+ serverAddress: input.serverAddress,
85
+ chargebeeSite: input.chargebeeSite,
86
+ chargebeeApiVersion: input.chargebeeApiVersion,
87
+ sdkName: CHARGEBEE_SDK_NAME,
88
+ sdkVersion: input.sdkVersion,
89
+ startAttributes: buildRequestStartSpanAttributes(input),
90
+ };
91
+ }
92
+ export function buildRequestTelemetryResult(result) {
93
+ return Object.assign(Object.assign({}, result), { endAttributes: buildRequestEndSpanAttributes(result) });
94
+ }
95
+ export function extractRequestTelemetryError(err) {
96
+ if (err == null || typeof err !== 'object') {
97
+ return undefined;
98
+ }
99
+ const errorObj = err;
100
+ const message = typeof errorObj.message === 'string'
101
+ ? errorObj.message
102
+ : 'Chargebee API request failed';
103
+ const result = { message };
104
+ if (typeof errorObj.api_error_code === 'string') {
105
+ result.chargebeeErrorCode = errorObj.api_error_code;
106
+ }
107
+ if (typeof errorObj.type === 'string') {
108
+ result.chargebeeApiErrorType = errorObj.type;
109
+ }
110
+ if (typeof errorObj.param === 'string') {
111
+ result.chargebeeErrorParam = errorObj.param;
112
+ }
113
+ return result;
114
+ }
115
+ export function extractHttpStatusCode(err) {
116
+ if (err == null || typeof err !== 'object') {
117
+ return undefined;
118
+ }
119
+ const errorObj = err;
120
+ for (const key of [
121
+ 'http_status_code',
122
+ 'httpStatusCode',
123
+ 'http_code',
124
+ 'statusCode',
125
+ ]) {
126
+ const value = errorObj[key];
127
+ if (typeof value === 'number') {
128
+ return value;
129
+ }
130
+ }
131
+ return undefined;
132
+ }
@@ -0,0 +1,8 @@
1
+ /*
2
+ * This file is auto-generated by Chargebee.
3
+ * For more information on how to make changes to this file, please see the README.
4
+ * Reach out to dx@chargebee.com for any questions.
5
+ * Copyright 2026 Chargebee Inc.
6
+ */
7
+ export { CHARGEBEE_SDK_NAME, CHARGEBEE_TELEMETRY_HEADER_EXCLUDE_PREFIX, CHARGEBEE_TELEMETRY_HEADER_PREFIX, HTTP_REQUEST_HEADER_ATTRIBUTE_PREFIX, TELEMETRY_SPAN_NAME_PREFIX, TelemetryAttributeKeys, } from './types.js';
8
+ export { NO_OP_TELEMETRY_ADAPTER, NoOpTelemetryAdapter, buildRequestEndSpanAttributes, buildRequestHeaderSpanAttributes, buildRequestStartSpanAttributes, buildRequestTelemetryContext, buildRequestTelemetryResult, buildSpanName, extractHttpStatusCode, extractRequestTelemetryError, resolveChargebeeApiVersion, } from './TelemetryAdapter.js';
@@ -0,0 +1,62 @@
1
+ /*
2
+ * This file is auto-generated by Chargebee.
3
+ * For more information on how to make changes to this file, please see the README.
4
+ * Reach out to dx@chargebee.com for any questions.
5
+ * Copyright 2026 Chargebee Inc.
6
+ */
7
+ import { context, propagation, SpanKind, SpanStatusCode, trace, } from '@opentelemetry/api';
8
+ import { CHARGEBEE_SDK_NAME, } from './types.js';
9
+ const tracer = trace.getTracer(CHARGEBEE_SDK_NAME);
10
+ function toRecordedException(error) {
11
+ var _a;
12
+ const exception = new Error(error.message);
13
+ exception.name = (_a = error.chargebeeErrorCode) !== null && _a !== void 0 ? _a : 'ChargebeeAPIError';
14
+ return exception;
15
+ }
16
+ /**
17
+ * Ready-to-use OpenTelemetry adapter for the Chargebee SDK.
18
+ *
19
+ * Each SDK request becomes a CLIENT span attached to the active OpenTelemetry
20
+ * context (or a root span when none is active), and the W3C trace context is
21
+ * propagated to Chargebee so the trace continues server-side.
22
+ *
23
+ * Exporting (endpoint, service name, credentials) is configured by your own
24
+ * OpenTelemetry runtime via the standard `OTEL_*` environment variables — this
25
+ * adapter only uses the globally registered tracer from `@opentelemetry/api`.
26
+ *
27
+ * `@opentelemetry/api` is an optional peer dependency; install it to use this
28
+ * adapter. A default-exported, ready-to-use instance is provided.
29
+ */
30
+ export class OtelTelemetryAdapter {
31
+ onRequestStart(ctx, requestHeaders) {
32
+ // startSpan adopts the active context's span as parent (or starts a root span if none).
33
+ const span = tracer.startSpan(ctx.spanName, {
34
+ kind: SpanKind.CLIENT,
35
+ attributes: ctx.startAttributes,
36
+ });
37
+ // Propagate W3C trace context to Chargebee so the trace continues server-side.
38
+ propagation.inject(trace.setSpan(context.active(), span), requestHeaders);
39
+ return span;
40
+ }
41
+ onRequestEnd(span, result) {
42
+ if (!span) {
43
+ return;
44
+ }
45
+ for (const [key, value] of Object.entries(result.endAttributes)) {
46
+ span.setAttribute(key, value);
47
+ }
48
+ if (result.error) {
49
+ span.recordException(toRecordedException(result.error));
50
+ span.setStatus({
51
+ code: SpanStatusCode.ERROR,
52
+ message: result.error.message,
53
+ });
54
+ }
55
+ else {
56
+ span.setStatus({ code: SpanStatusCode.OK });
57
+ }
58
+ span.end();
59
+ }
60
+ }
61
+ const otelDefaultAdapter = new OtelTelemetryAdapter();
62
+ export default otelDefaultAdapter;
@@ -0,0 +1,44 @@
1
+ /*
2
+ * This file is auto-generated by Chargebee.
3
+ * For more information on how to make changes to this file, please see the README.
4
+ * Reach out to dx@chargebee.com for any questions.
5
+ * Copyright 2026 Chargebee Inc.
6
+ */
7
+ /** SDK identifier recorded on telemetry spans. */
8
+ export const CHARGEBEE_SDK_NAME = 'chargebee-node';
9
+ /** Standard span name prefix: chargebee.{resource}.{operation} */
10
+ export const TELEMETRY_SPAN_NAME_PREFIX = 'chargebee';
11
+ /**
12
+ * OTel HTTP semantic-convention prefix for request-header attributes:
13
+ * `http.request.header.<lowercased-name>` with string[] values.
14
+ */
15
+ export const HTTP_REQUEST_HEADER_ATTRIBUTE_PREFIX = 'http.request.header.';
16
+ /**
17
+ * Request headers whose (lowercased) name starts with this prefix are captured as span
18
+ * attributes. Using a prefix instead of a fixed list means any future `chargebee-*` header
19
+ * is picked up automatically, with no SDK upgrade required.
20
+ */
21
+ export const CHARGEBEE_TELEMETRY_HEADER_PREFIX = 'chargebee-';
22
+ /**
23
+ * Headers under this sub-prefix carry end-user PII (origin IP, email, device) and are
24
+ * excluded from spans by default. Chargebee namespaces such headers under
25
+ * `chargebee-request-origin-*`, so future PII headers stay excluded automatically.
26
+ */
27
+ export const CHARGEBEE_TELEMETRY_HEADER_EXCLUDE_PREFIX = 'chargebee-request-origin-';
28
+ /** Span attribute keys — shared across Chargebee SDKs. */
29
+ export const TelemetryAttributeKeys = {
30
+ URL_FULL: 'url.full',
31
+ HTTP_REQUEST_METHOD: 'http.request.method',
32
+ HTTP_RESPONSE_STATUS_CODE: 'http.response.status_code',
33
+ SERVER_ADDRESS: 'server.address',
34
+ ERROR_TYPE: 'error.type',
35
+ CHARGEBEE_SITE: 'chargebee.site',
36
+ CHARGEBEE_API_VERSION: 'chargebee.api_version',
37
+ CHARGEBEE_RESOURCE: 'chargebee.resource',
38
+ CHARGEBEE_OPERATION: 'chargebee.operation',
39
+ CHARGEBEE_SDK_NAME: 'chargebee.sdk.name',
40
+ CHARGEBEE_SDK_VERSION: 'chargebee.sdk.version',
41
+ CHARGEBEE_ERROR_CODE: 'chargebee.error.code',
42
+ CHARGEBEE_ERROR_TYPE: 'chargebee.error.type',
43
+ CHARGEBEE_ERROR_PARAM: 'chargebee.error.param',
44
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chargebee",
3
- "version": "3.27.0",
3
+ "version": "3.28.1",
4
4
  "description": "A library for integrating with Chargebee.",
5
5
  "scripts": {
6
6
  "prepack": "npm install && npm run build",
@@ -34,33 +34,49 @@
34
34
  }
35
35
  ],
36
36
  "exports": {
37
- "types": "./types/index.d.ts",
38
- "browser": {
39
- "import": "./esm/chargebee.esm.worker.js",
40
- "require": "./cjs/chargebee.cjs.worker.js"
37
+ ".": {
38
+ "types": "./types/index.d.ts",
39
+ "browser": {
40
+ "import": "./esm/chargebee.esm.worker.js",
41
+ "require": "./cjs/chargebee.cjs.worker.js"
42
+ },
43
+ "worker": {
44
+ "import": "./esm/chargebee.esm.worker.js",
45
+ "require": "./cjs/chargebee.cjs.worker.js"
46
+ },
47
+ "workerd": {
48
+ "import": "./esm/chargebee.esm.worker.js",
49
+ "require": "./cjs/chargebee.cjs.worker.js"
50
+ },
51
+ "deno": {
52
+ "import": "./esm/chargebee.esm.worker.js",
53
+ "require": "./cjs/chargebee.cjs.worker.js"
54
+ },
55
+ "bun": {
56
+ "import": "./esm/chargebee.esm.worker.js",
57
+ "require": "./cjs/chargebee.cjs.worker.js"
58
+ },
59
+ "default": {
60
+ "import": "./esm/chargebee.esm.js",
61
+ "require": "./cjs/chargebee.cjs.js"
62
+ }
41
63
  },
42
- "worker": {
43
- "import": "./esm/chargebee.esm.worker.js",
44
- "require": "./cjs/chargebee.cjs.worker.js"
45
- },
46
- "workerd": {
47
- "import": "./esm/chargebee.esm.worker.js",
48
- "require": "./cjs/chargebee.cjs.worker.js"
49
- },
50
- "deno": {
51
- "import": "./esm/chargebee.esm.worker.js",
52
- "require": "./cjs/chargebee.cjs.worker.js"
53
- },
54
- "bun": {
55
- "import": "./esm/chargebee.esm.worker.js",
56
- "require": "./cjs/chargebee.cjs.worker.js"
57
- },
58
- "default": {
59
- "import": "./esm/chargebee.esm.js",
60
- "require": "./cjs/chargebee.cjs.js"
64
+ "./telemetry/otel": {
65
+ "types": "./types/telemetry/otel.d.ts",
66
+ "import": "./esm/telemetry/otel.js",
67
+ "require": "./cjs/telemetry/otel.js"
68
+ }
69
+ },
70
+ "peerDependencies": {
71
+ "@opentelemetry/api": "^1.9.0"
72
+ },
73
+ "peerDependenciesMeta": {
74
+ "@opentelemetry/api": {
75
+ "optional": true
61
76
  }
62
77
  },
63
78
  "devDependencies": {
79
+ "@opentelemetry/api": "^1.9.0",
64
80
  "@types/chai": "^4.3.5",
65
81
  "@types/mocha": "^10.0.10",
66
82
  "@types/node": "20.12.0",
package/types/index.d.ts CHANGED
@@ -169,6 +169,11 @@ declare module 'chargebee' {
169
169
  */
170
170
  httpClient?: HttpClientInterface;
171
171
 
172
+ /**
173
+ * @telemetryAdapter optional telemetry adapter for observability (e.g. OpenTelemetry)
174
+ */
175
+ telemetryAdapter?: TelemetryAdapter;
176
+
172
177
  /**
173
178
  * @enableValidation When true, every request's parameters are validated against each endpoint's generated Zod schema before the HTTP request is sent. Violations throw `ChargebeeZodValidationError` with structured Zod issues. Calls with no params argument are validated as `{}`. Required resource ids in the URL path are still checked separately.
174
179
  */
@@ -179,6 +184,70 @@ declare module 'chargebee' {
179
184
  makeApiRequest: (request: Request, timeout: number) => Promise<Response>;
180
185
  }
181
186
 
187
+ export type RequestTelemetryHandle = unknown;
188
+
189
+ export const TelemetryAttributeKeys: {
190
+ readonly URL_FULL: 'url.full';
191
+ readonly HTTP_REQUEST_METHOD: 'http.request.method';
192
+ readonly HTTP_RESPONSE_STATUS_CODE: 'http.response.status_code';
193
+ readonly SERVER_ADDRESS: 'server.address';
194
+ readonly ERROR_TYPE: 'error.type';
195
+ readonly CHARGEBEE_SITE: 'chargebee.site';
196
+ readonly CHARGEBEE_API_VERSION: 'chargebee.api_version';
197
+ readonly CHARGEBEE_RESOURCE: 'chargebee.resource';
198
+ readonly CHARGEBEE_OPERATION: 'chargebee.operation';
199
+ readonly CHARGEBEE_SDK_NAME: 'chargebee.sdk.name';
200
+ readonly CHARGEBEE_SDK_VERSION: 'chargebee.sdk.version';
201
+ readonly CHARGEBEE_ERROR_CODE: 'chargebee.error.code';
202
+ readonly CHARGEBEE_ERROR_TYPE: 'chargebee.error.type';
203
+ readonly CHARGEBEE_ERROR_PARAM: 'chargebee.error.param';
204
+ };
205
+
206
+ export type RequestTelemetryContext = {
207
+ spanName: string;
208
+ resource: string;
209
+ operation: string;
210
+ httpMethod: string;
211
+ httpUrl: string;
212
+ serverAddress: string;
213
+ chargebeeSite: string;
214
+ chargebeeApiVersion: 'v1' | 'v2';
215
+ sdkName: string;
216
+ sdkVersion: string;
217
+ /**
218
+ * Prebuilt span attributes — pass these to your tracer. Captured `chargebee-*` request
219
+ * headers appear as `http.request.header.<name>` with string[] values per OTel semconv.
220
+ */
221
+ startAttributes: Record<string, string | string[]>;
222
+ };
223
+
224
+ export type RequestTelemetryError = {
225
+ message: string;
226
+ chargebeeErrorCode?: string;
227
+ chargebeeApiErrorType?: string;
228
+ chargebeeErrorParam?: string;
229
+ };
230
+
231
+ export type RequestTelemetryResult = {
232
+ httpStatusCode: number;
233
+ durationMs: number;
234
+ error?: RequestTelemetryError;
235
+ /** Prebuilt span attributes — pass these to your tracer. */
236
+ endAttributes: Record<string, string | number>;
237
+ };
238
+
239
+ /**
240
+ * Optional telemetry adapter. Implement as a class or plain object — the SDK
241
+ * keeps it by reference (never deep-cloned). Wire OpenTelemetry or other tools here.
242
+ */
243
+ export interface TelemetryAdapter<THandle = RequestTelemetryHandle> {
244
+ onRequestStart(
245
+ context: RequestTelemetryContext,
246
+ requestHeaders: Record<string, string | number>,
247
+ ): THandle | void;
248
+ onRequestEnd(handle: THandle | void, result: RequestTelemetryResult): void;
249
+ }
250
+
182
251
  export type RetryConfig = {
183
252
  /**
184
253
  * @enabled whether to enable retry logic, default value is false
@@ -0,0 +1,22 @@
1
+ declare module 'chargebee/telemetry/otel' {
2
+ import type {
3
+ RequestTelemetryContext,
4
+ RequestTelemetryResult,
5
+ TelemetryAdapter,
6
+ } from 'chargebee';
7
+ /**
8
+ * Ready-to-use OpenTelemetry adapter for the Chargebee SDK. Exporting is
9
+ * configured by your own OpenTelemetry runtime via the standard `OTEL_*`
10
+ * environment variables; this adapter only uses the globally registered
11
+ * tracer from `@opentelemetry/api` (an optional peer dependency).
12
+ */
13
+ export class OtelTelemetryAdapter implements TelemetryAdapter {
14
+ onRequestStart(
15
+ ctx: RequestTelemetryContext,
16
+ requestHeaders: Record<string, string | number>,
17
+ ): unknown;
18
+ onRequestEnd(handle: unknown, result: RequestTelemetryResult): void;
19
+ }
20
+ const otelDefaultAdapter: OtelTelemetryAdapter;
21
+ export default otelDefaultAdapter;
22
+ }