@forge/kvs 1.5.1-next.0 → 1.6.0-next.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.
@@ -102,25 +102,41 @@ describe('KVS', () => {
102
102
  }));
103
103
  });
104
104
  it('should handle unexpected metadata fields', async () => {
105
- const response = new Response(JSON.stringify({
105
+ const body = JSON.stringify({
106
106
  code: 'BAD_REQUEST',
107
107
  message: 'Provided request body is invalid'
108
- }), {
108
+ });
109
+ const response = new Response(body, {
109
110
  status: 400,
110
111
  statusText: 'Bad Request',
111
112
  headers: { 'x-trace-id': traceId }
112
113
  });
113
114
  const { sut } = prepare(response);
114
- await expect(sut.get('foo', { metadataFields: ['INVALID_METADATA_FIELD'] })).rejects.toMatchError(new errors_1.ForgeKvsAPIError({ status: 400, statusText: 'Bad Request', traceId }, { code: 'BAD_REQUEST', message: 'Provided request body is invalid' }));
115
+ await expect(sut.get('foo', { metadataFields: ['INVALID_METADATA_FIELD'] })).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
116
+ status: 400,
117
+ statusText: 'Bad Request',
118
+ traceId,
119
+ httpMethod: 'POST',
120
+ httpPath: '/api/v1/get',
121
+ responseBodyLength: body.length
122
+ }, { code: 'BAD_REQUEST', message: 'Provided request body is invalid' }));
115
123
  });
116
124
  it('should handle unexpected response', async () => {
117
- const response = new Response(JSON.stringify({ code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }), {
125
+ const body = JSON.stringify({ code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' });
126
+ const response = new Response(body, {
118
127
  status: 500,
119
128
  statusText: 'Internal Server Error',
120
129
  headers: { 'x-trace-id': traceId }
121
130
  });
122
131
  const { sut } = prepare(response);
123
- await expect(sut.get('foo')).rejects.toMatchError(new errors_1.ForgeKvsAPIError({ status: 500, statusText: 'Internal Server Error', traceId }, { code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }));
132
+ await expect(sut.get('foo')).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
133
+ status: 500,
134
+ statusText: 'Internal Server Error',
135
+ traceId,
136
+ httpMethod: 'POST',
137
+ httpPath: '/api/v1/get',
138
+ responseBodyLength: body.length
139
+ }, { code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }));
124
140
  });
125
141
  it('should handle non-json response even though status is ok', async () => {
126
142
  const responseText = 'Text that will fail to parse';
@@ -130,7 +146,14 @@ describe('KVS', () => {
130
146
  headers: { 'x-trace-id': traceId, 'content-length': responseText.length.toString() }
131
147
  });
132
148
  const { sut } = prepare(response);
133
- await expect(sut.get('foo')).rejects.toMatchError(new errors_1.ForgeKvsAPIError({ status: 200, statusText: 'OK', traceId }, {
149
+ await expect(sut.get('foo')).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
150
+ status: 200,
151
+ statusText: 'OK',
152
+ traceId,
153
+ httpMethod: 'POST',
154
+ httpPath: '/api/v1/get',
155
+ responseBodyLength: responseText.length
156
+ }, {
134
157
  code: 'UNKNOWN_ERROR',
135
158
  message: 'Unexpected error in Forge KVS API. Response was not valid JSON',
136
159
  context: { contentLength: responseText.length.toString() }
@@ -1178,14 +1201,22 @@ describe('KVS', () => {
1178
1201
  }));
1179
1202
  });
1180
1203
  it('should handle batchSet API error response', async () => {
1181
- const response = new Response(JSON.stringify({ code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }), {
1204
+ const body = JSON.stringify({ code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' });
1205
+ const response = new Response(body, {
1182
1206
  status: 500,
1183
1207
  statusText: 'Internal Server Error',
1184
1208
  headers: { 'x-trace-id': traceId }
1185
1209
  });
1186
1210
  const { sut } = prepare(response);
1187
1211
  const items = [{ key: 'foo', value: 'bar' }];
1188
- await expect(sut.batchSet(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({ status: 500, statusText: 'Internal Server Error', traceId }, { code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }));
1212
+ await expect(sut.batchSet(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
1213
+ status: 500,
1214
+ statusText: 'Internal Server Error',
1215
+ traceId,
1216
+ httpMethod: 'POST',
1217
+ httpPath: '/api/v1/batch/set',
1218
+ responseBodyLength: body.length
1219
+ }, { code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }));
1189
1220
  });
1190
1221
  it('should handle batchDelete correctly with mixed entity and non-entity items', async () => {
1191
1222
  const response = new Response(JSON.stringify({
@@ -1277,14 +1308,22 @@ describe('KVS', () => {
1277
1308
  }));
1278
1309
  });
1279
1310
  it('should handle batchDelete API error response', async () => {
1280
- const response = new Response(JSON.stringify({ code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }), {
1311
+ const body = JSON.stringify({ code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' });
1312
+ const response = new Response(body, {
1281
1313
  status: 500,
1282
1314
  statusText: 'Internal Server Error',
1283
1315
  headers: { 'x-trace-id': traceId }
1284
1316
  });
1285
1317
  const { sut } = prepare(response);
1286
1318
  const items = [{ key: 'foo' }];
1287
- await expect(sut.batchDelete(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({ status: 500, statusText: 'Internal Server Error', traceId }, { code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }));
1319
+ await expect(sut.batchDelete(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
1320
+ status: 500,
1321
+ statusText: 'Internal Server Error',
1322
+ traceId,
1323
+ httpMethod: 'POST',
1324
+ httpPath: '/api/v1/batch/delete',
1325
+ responseBodyLength: body.length
1326
+ }, { code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }));
1288
1327
  });
1289
1328
  it('should handle batchGet with mixed entity and non-entity items', async () => {
1290
1329
  const response = new Response(JSON.stringify({
@@ -1451,20 +1490,29 @@ describe('KVS', () => {
1451
1490
  }));
1452
1491
  });
1453
1492
  it('should handle batchGet API error response', async () => {
1454
- const response = new Response(JSON.stringify({ code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }), {
1493
+ const body = JSON.stringify({ code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' });
1494
+ const response = new Response(body, {
1455
1495
  status: 500,
1456
1496
  statusText: 'Internal Server Error',
1457
1497
  headers: { 'x-trace-id': traceId }
1458
1498
  });
1459
1499
  const { sut } = prepare(response);
1460
1500
  const items = [{ key: 'foo' }];
1461
- await expect(sut.batchGet(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({ status: 500, statusText: 'Internal Server Error', traceId }, { code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }));
1501
+ await expect(sut.batchGet(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
1502
+ status: 500,
1503
+ statusText: 'Internal Server Error',
1504
+ traceId,
1505
+ httpMethod: 'POST',
1506
+ httpPath: '/api/v1/batch/get',
1507
+ responseBodyLength: body.length
1508
+ }, { code: 'INTERNAL_SERVER_ERROR', message: 'An internal server error has occurred' }));
1462
1509
  });
1463
1510
  it('should handle batchGet with invalid metadata fields', async () => {
1464
- const response = new Response(JSON.stringify({
1511
+ const body = JSON.stringify({
1465
1512
  code: 'BAD_REQUEST',
1466
1513
  message: 'Provided request body is invalid'
1467
- }), {
1514
+ });
1515
+ const response = new Response(body, {
1468
1516
  status: 400,
1469
1517
  statusText: 'Bad Request',
1470
1518
  headers: { 'x-trace-id': traceId }
@@ -1476,6 +1524,13 @@ describe('KVS', () => {
1476
1524
  options: { metadataFields: ['INVALID_METADATA_FIELD'] }
1477
1525
  }
1478
1526
  ];
1479
- await expect(sut.batchGet(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({ status: 400, statusText: 'Bad Request', traceId }, { code: 'BAD_REQUEST', message: 'Provided request body is invalid' }));
1527
+ await expect(sut.batchGet(items)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
1528
+ status: 400,
1529
+ statusText: 'Bad Request',
1530
+ traceId,
1531
+ httpMethod: 'POST',
1532
+ httpPath: '/api/v1/batch/get',
1533
+ responseBodyLength: body.length
1534
+ }, { code: 'BAD_REQUEST', message: 'Provided request body is invalid' }));
1480
1535
  });
1481
1536
  });
package/out/errors.d.ts CHANGED
@@ -7,9 +7,13 @@ export interface APIErrorResponseDetails {
7
7
  status: number;
8
8
  statusText: string;
9
9
  traceId?: string | null;
10
+ httpMethod?: string;
11
+ httpPath?: string;
12
+ responseBodyLength?: number;
10
13
  }
11
14
  export declare class ForgeKvsError extends Error {
12
- constructor(message: string);
15
+ packageVersion: string;
16
+ constructor(message: string, packageVersion?: string);
13
17
  }
14
18
  export declare class ForgeKvsAPIError extends ForgeKvsError {
15
19
  responseDetails: APIErrorResponseDetails;
@@ -1 +1 @@
1
- {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB;AAED,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,OAAO,EAAE,MAAM;CAI5B;AAED,qBAAa,gBAAiB,SAAQ,aAAa;IACjD,eAAe,EAAE,uBAAuB,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAErB,eAAe,EAAE,uBAAuB,EAAE,UAAU,EAAE,UAAU;CAU7E"}
1
+ {"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAEA,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACnC;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,qBAAa,aAAc,SAAQ,KAAK;IACtC,cAAc,EAAE,MAAM,CAAC;gBAEX,OAAO,EAAE,MAAM,EAAE,cAAc,GAAE,MAAwB;CAKtE;AAED,qBAAa,gBAAiB,SAAQ,aAAa;IACjD,eAAe,EAAE,uBAAuB,CAAC;IACzC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;gBAErB,eAAe,EAAE,uBAAuB,EAAE,UAAU,EAAE,UAAU;CAU7E"}
package/out/errors.js CHANGED
@@ -1,10 +1,13 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.ForgeKvsAPIError = exports.ForgeKvsError = void 0;
4
+ const version_1 = require("./version");
4
5
  class ForgeKvsError extends Error {
5
- constructor(message) {
6
+ packageVersion;
7
+ constructor(message, packageVersion = version_1.PACKAGE_VERSION) {
6
8
  super(message);
7
9
  this.name = 'ForgeKvsError';
10
+ this.packageVersion = packageVersion;
8
11
  }
9
12
  }
10
13
  exports.ForgeKvsError = ForgeKvsError;
@@ -15,8 +18,8 @@ class ForgeKvsAPIError extends ForgeKvsError {
15
18
  context;
16
19
  constructor(responseDetails, forgeError) {
17
20
  super(forgeError.message);
18
- const { status, statusText, traceId } = responseDetails;
19
- this.responseDetails = { status, statusText, traceId };
21
+ const { status, statusText, traceId, httpMethod, httpPath, responseBodyLength } = responseDetails;
22
+ this.responseDetails = { status, statusText, traceId, httpMethod, httpPath, responseBodyLength };
20
23
  const { code, message, context, ...bodyData } = forgeError;
21
24
  this.code = code;
22
25
  this.message = message;
@@ -1 +1 @@
1
- {"version":3,"file":"storage-api.d.ts","sourceRoot":"","sources":["../src/storage-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EACL,SAAS,EACT,UAAU,EACV,WAAW,EAGX,SAAS,EAGT,cAAc,EACf,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,kBAAkB,EAElB,eAAe,EACf,eAAe,EAEf,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAEhB,kBAAkB,EAElB,gBAAgB,EAChB,UAAU,EAEV,YAAY,EAEZ,mBAAmB,EACnB,gBAAgB,EAEhB,gBAAgB,EAChB,UAAU,EAEV,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAQ9B,qBAAa,UAAU;IACT,OAAO,CAAC,SAAS;gBAAT,SAAS,EAAE,WAAW;IAEpC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAO/D,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAO3E,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAO3E,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAKrE,SAAS,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAKjF,SAAS,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAKjF,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1C,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtD,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAQpD,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAQhE,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC;IAQ3D,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC;IAQ3D,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IAQ9D,QAAQ,CAAC,CAAC,EAAE,kBAAkB,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;YAI7D,iBAAiB;YAWjB,OAAO;IA6CrB,OAAO,CAAC,kBAAkB;IAyB1B,OAAO,CAAC,kBAAkB;CAgB3B"}
1
+ {"version":3,"file":"storage-api.d.ts","sourceRoot":"","sources":["../src/storage-api.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAEzC,OAAO,EACL,SAAS,EACT,UAAU,EACV,WAAW,EAGX,SAAS,EAGT,cAAc,EACf,MAAM,oBAAoB,CAAC;AAQ5B,OAAO,EACL,kBAAkB,EAElB,eAAe,EACf,eAAe,EAEf,aAAa,EACb,mBAAmB,EACnB,gBAAgB,EAEhB,kBAAkB,EAElB,gBAAgB,EAChB,UAAU,EAEV,YAAY,EAEZ,mBAAmB,EACnB,gBAAgB,EAEhB,gBAAgB,EAChB,UAAU,EAEV,kBAAkB,EACnB,MAAM,sBAAsB,CAAC;AAQ9B,qBAAa,UAAU;IACT,OAAO,CAAC,SAAS;gBAAT,SAAS,EAAE,WAAW;IAEpC,GAAG,CAAC,CAAC,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAO/D,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAO3E,SAAS,CAAC,CAAC,EAAE,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAO3E,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAKrE,SAAS,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAKjF,SAAS,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,EAAE,IAAI,EAAE,gBAAgB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,CAAC,GAAG,SAAS,CAAC;IAKjF,MAAM,CAAC,IAAI,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC;IAM1C,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtD,YAAY,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAAC,IAAI,CAAC;IAMtD,KAAK,CAAC,CAAC,EAAE,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAQpD,WAAW,CAAC,CAAC,EAAE,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;IAQhE,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC;IAQ3D,WAAW,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAAC,WAAW,CAAC;IAQ3D,QAAQ,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,GAAG,OAAO,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IAQ9D,QAAQ,CAAC,CAAC,EAAE,kBAAkB,EAAE,kBAAkB,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC;YAI7D,iBAAiB;YAWjB,OAAO;IA8CrB,OAAO,CAAC,kBAAkB;IAyB1B,OAAO,CAAC,kBAAkB;CAgB3B"}
@@ -118,7 +118,11 @@ class StorageApi {
118
118
  }
119
119
  };
120
120
  const response = await this.apiClient(path, requestBody);
121
- await (0, error_handling_1.checkResponseError)(response);
121
+ const requestContext = {
122
+ httpMethod: requestBody.method,
123
+ httpPath: path
124
+ };
125
+ await (0, error_handling_1.checkResponseError)(response, requestContext);
122
126
  if (responseType === ResponseType.NONE) {
123
127
  return;
124
128
  }
@@ -128,11 +132,7 @@ class StorageApi {
128
132
  }
129
133
  const parsedBody = (0, error_handling_1.safeGetParsedBody)(responseText);
130
134
  if (parsedBody === undefined) {
131
- const details = {
132
- status: response.status,
133
- statusText: response.statusText,
134
- traceId: (0, error_handling_1.extractTraceId)(response)
135
- };
135
+ const details = (0, error_handling_1.getAPIErrorResponseDetails)(response, responseText, requestContext);
136
136
  throw new errors_1.ForgeKvsAPIError(details, {
137
137
  code: 'UNKNOWN_ERROR',
138
138
  message: 'Unexpected error in Forge KVS API. Response was not valid JSON',
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const errors_1 = require("../../errors");
4
+ const version_1 = require("../../version");
4
5
  const error_handling_1 = require("../error-handling");
5
6
  describe('error-handling', () => {
6
7
  it('isForgeError', () => {
@@ -30,7 +31,8 @@ describe('error-handling', () => {
30
31
  const message = 'A test error has occurred';
31
32
  const code = 'ERROR_CODE';
32
33
  it('should return a ForgeKvsAPIError when response body is a Forge error', async () => {
33
- const mockResponse = new Response(JSON.stringify({ code, message }), {
34
+ const body = JSON.stringify({ code, message });
35
+ const mockResponse = new Response(body, {
34
36
  status: 400,
35
37
  statusText: 'Bad Request',
36
38
  headers: { 'x-trace-id': traceId }
@@ -38,12 +40,14 @@ describe('error-handling', () => {
38
40
  await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
39
41
  status: 400,
40
42
  statusText: 'Bad Request',
41
- traceId
43
+ traceId,
44
+ responseBodyLength: body.length
42
45
  }, { code, message }));
43
46
  });
44
47
  it('should include context if present in the Forge error', async () => {
45
48
  const context = { key: 'value' };
46
- const mockResponse = new Response(JSON.stringify({ code, message, context }), {
49
+ const body = JSON.stringify({ code, message, context });
50
+ const mockResponse = new Response(body, {
47
51
  status: 400,
48
52
  statusText: 'Bad Request',
49
53
  headers: { 'x-trace-id': traceId }
@@ -51,12 +55,14 @@ describe('error-handling', () => {
51
55
  await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
52
56
  status: 400,
53
57
  statusText: 'Bad Request',
54
- traceId
58
+ traceId,
59
+ responseBodyLength: body.length
55
60
  }, { code, message, context }));
56
61
  });
57
62
  it('should include top level additional fields if present in the Forge error', async () => {
58
63
  const extraFields = { extraValue: 'value', debug: true };
59
- const mockResponse = new Response(JSON.stringify({ code, message, ...extraFields }), {
64
+ const body = JSON.stringify({ code, message, ...extraFields });
65
+ const mockResponse = new Response(body, {
60
66
  status: 400,
61
67
  statusText: 'Bad Request',
62
68
  headers: { 'x-trace-id': traceId }
@@ -64,13 +70,15 @@ describe('error-handling', () => {
64
70
  await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
65
71
  status: 400,
66
72
  statusText: 'Bad Request',
67
- traceId
73
+ traceId,
74
+ responseBodyLength: body.length
68
75
  }, { code, message, context: extraFields }));
69
76
  });
70
77
  it('should merge context and additional top level fields if both present in the Forge error', async () => {
71
78
  const context = { key: 'value' };
72
79
  const extraFields = { extraValue: 'value', debug: true };
73
- const mockResponse = new Response(JSON.stringify({ code, message, context, ...extraFields }), {
80
+ const body = JSON.stringify({ code, message, context, ...extraFields });
81
+ const mockResponse = new Response(body, {
74
82
  status: 400,
75
83
  statusText: 'Bad Request',
76
84
  headers: { 'x-trace-id': traceId }
@@ -78,7 +86,8 @@ describe('error-handling', () => {
78
86
  await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toThrowError(new errors_1.ForgeKvsAPIError({
79
87
  status: 400,
80
88
  statusText: 'Bad Request',
81
- traceId
89
+ traceId,
90
+ responseBodyLength: body.length
82
91
  }, { code, message, context: { ...context, ...extraFields } }));
83
92
  });
84
93
  describe('Handle non forge errors', () => {
@@ -91,7 +100,8 @@ describe('error-handling', () => {
91
100
  await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
92
101
  status: 404,
93
102
  statusText: 'Not Found',
94
- traceId
103
+ traceId,
104
+ responseBodyLength: 0
95
105
  }, {
96
106
  code: 'UNKNOWN_ERROR',
97
107
  context: { responseText: '' },
@@ -108,7 +118,8 @@ describe('error-handling', () => {
108
118
  await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
109
119
  status: 500,
110
120
  statusText: 'Internal Server Error',
111
- traceId
121
+ traceId,
122
+ responseBodyLength: body.length
112
123
  }, {
113
124
  code: 'UNKNOWN_ERROR',
114
125
  context: { responseText: body },
@@ -117,5 +128,65 @@ describe('error-handling', () => {
117
128
  });
118
129
  });
119
130
  });
131
+ describe('with request context', () => {
132
+ const requestContext = {
133
+ httpMethod: 'POST',
134
+ httpPath: '/api/v1/get'
135
+ };
136
+ it('should include request context in ForgeKvsAPIError when provided', async () => {
137
+ const body = JSON.stringify({ code: 'ERROR', message: 'Test error' });
138
+ const mockResponse = new Response(body, {
139
+ status: 400,
140
+ statusText: 'Bad Request',
141
+ headers: { 'x-trace-id': traceId }
142
+ });
143
+ await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse, requestContext)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
144
+ status: 400,
145
+ statusText: 'Bad Request',
146
+ traceId,
147
+ httpMethod: 'POST',
148
+ httpPath: '/api/v1/get',
149
+ responseBodyLength: body.length
150
+ }, { code: 'ERROR', message: 'Test error' }));
151
+ });
152
+ it('should include request context in UNKNOWN_ERROR when provided', async () => {
153
+ const body = 'not valid json';
154
+ const mockResponse = new Response(body, {
155
+ status: 500,
156
+ statusText: 'Internal Server Error',
157
+ headers: { 'x-trace-id': traceId }
158
+ });
159
+ await expect(async () => await (0, error_handling_1.checkResponseError)(mockResponse, requestContext)).rejects.toMatchError(new errors_1.ForgeKvsAPIError({
160
+ status: 500,
161
+ statusText: 'Internal Server Error',
162
+ traceId,
163
+ httpMethod: 'POST',
164
+ httpPath: '/api/v1/get',
165
+ responseBodyLength: body.length
166
+ }, {
167
+ code: 'UNKNOWN_ERROR',
168
+ context: { responseText: body },
169
+ message: 'Unexpected error in Forge KVS API'
170
+ }));
171
+ });
172
+ });
173
+ });
174
+ describe('packageVersion', () => {
175
+ it('should auto-populate packageVersion on ForgeKvsError', () => {
176
+ const error = new errors_1.ForgeKvsError('Test error');
177
+ expect(error.packageVersion).toBeDefined();
178
+ expect(typeof error.packageVersion).toBe('string');
179
+ expect(error.packageVersion).toMatch(/^\d+\.\d+\.\d+/);
180
+ });
181
+ it('should auto-populate packageVersion on ForgeKvsAPIError', () => {
182
+ const error = new errors_1.ForgeKvsAPIError({ status: 500, statusText: 'Internal Server Error' }, { code: 'ERROR', message: 'Test error' });
183
+ expect(error.packageVersion).toBeDefined();
184
+ expect(typeof error.packageVersion).toBe('string');
185
+ expect(error.packageVersion).toMatch(/^\d+\.\d+\.\d+/);
186
+ });
187
+ it('should match the version in package.json', () => {
188
+ const error = new errors_1.ForgeKvsError('Test error');
189
+ expect(error.packageVersion).toBe(version_1.PACKAGE_VERSION);
190
+ });
120
191
  });
121
192
  });
@@ -1,7 +1,12 @@
1
1
  import { APIResponse } from '@forge/api';
2
- import { ForgeError } from '../errors';
2
+ import { APIErrorResponseDetails, ForgeError } from '../errors';
3
+ export interface RequestContext {
4
+ httpMethod: string;
5
+ httpPath: string;
6
+ }
3
7
  export declare function isForgeError(body: unknown): body is ForgeError;
4
8
  export declare function safeGetParsedBody(text: string): unknown | undefined;
9
+ export declare function getAPIErrorResponseDetails(response: APIResponse, responseText: string, requestContext?: RequestContext): APIErrorResponseDetails;
5
10
  export declare function extractTraceId(response: APIResponse): string | null;
6
- export declare function checkResponseError(response: APIResponse): Promise<void>;
11
+ export declare function checkResponseError(response: APIResponse, requestContext?: RequestContext): Promise<void>;
7
12
  //# sourceMappingURL=error-handling.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-handling.d.ts","sourceRoot":"","sources":["../../src/utils/error-handling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAA2B,UAAU,EAAoB,MAAM,WAAW,CAAC;AAElF,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,UAAU,CAE9D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAMnE;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAEnE;AAED,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CAwB7E"}
1
+ {"version":3,"file":"error-handling.d.ts","sourceRoot":"","sources":["../../src/utils/error-handling.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AACzC,OAAO,EAAE,uBAAuB,EAAE,UAAU,EAAoB,MAAM,WAAW,CAAC;AAElF,MAAM,WAAW,cAAc;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,YAAY,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,UAAU,CAE9D;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,SAAS,CAMnE;AAED,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,WAAW,EACrB,YAAY,EAAE,MAAM,EACpB,cAAc,CAAC,EAAE,cAAc,GAC9B,uBAAuB,CASzB;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,WAAW,GAAG,MAAM,GAAG,IAAI,CAEnE;AAED,wBAAsB,kBAAkB,CAAC,QAAQ,EAAE,WAAW,EAAE,cAAc,CAAC,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAmB9G"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.checkResponseError = exports.extractTraceId = exports.safeGetParsedBody = exports.isForgeError = void 0;
3
+ exports.checkResponseError = exports.extractTraceId = exports.getAPIErrorResponseDetails = exports.safeGetParsedBody = exports.isForgeError = void 0;
4
4
  const errors_1 = require("../errors");
5
5
  function isForgeError(body) {
6
6
  return typeof body === 'object' && body !== null && 'code' in body && 'message' in body;
@@ -15,20 +15,27 @@ function safeGetParsedBody(text) {
15
15
  }
16
16
  }
17
17
  exports.safeGetParsedBody = safeGetParsedBody;
18
+ function getAPIErrorResponseDetails(response, responseText, requestContext) {
19
+ return {
20
+ status: response.status,
21
+ statusText: response.statusText,
22
+ traceId: extractTraceId(response),
23
+ httpMethod: requestContext?.httpMethod,
24
+ httpPath: requestContext?.httpPath,
25
+ responseBodyLength: responseText.length
26
+ };
27
+ }
28
+ exports.getAPIErrorResponseDetails = getAPIErrorResponseDetails;
18
29
  function extractTraceId(response) {
19
30
  return response.headers.get('x-b3-traceid') || response.headers.get('x-trace-id');
20
31
  }
21
32
  exports.extractTraceId = extractTraceId;
22
- async function checkResponseError(response) {
33
+ async function checkResponseError(response, requestContext) {
23
34
  if (response.ok) {
24
35
  return;
25
36
  }
26
37
  const responseText = await response.text();
27
- const details = {
28
- status: response.status,
29
- statusText: response.statusText,
30
- traceId: extractTraceId(response)
31
- };
38
+ const details = getAPIErrorResponseDetails(response, responseText, requestContext);
32
39
  const parsedBody = safeGetParsedBody(responseText);
33
40
  if (parsedBody && isForgeError(parsedBody)) {
34
41
  throw new errors_1.ForgeKvsAPIError(details, parsedBody);
@@ -0,0 +1,3 @@
1
+ export declare const PACKAGE_NAME: string;
2
+ export declare const PACKAGE_VERSION: string;
3
+ //# sourceMappingURL=version.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"version.d.ts","sourceRoot":"","sources":["../src/version.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,YAAY,EAAE,MAAyB,CAAC;AACrD,eAAO,MAAM,eAAe,EAAE,MAA4B,CAAC"}
package/out/version.js ADDED
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PACKAGE_VERSION = exports.PACKAGE_NAME = void 0;
4
+ const packageInfo = require('../package.json');
5
+ exports.PACKAGE_NAME = packageInfo.name;
6
+ exports.PACKAGE_VERSION = packageInfo.version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@forge/kvs",
3
- "version": "1.5.1-next.0",
3
+ "version": "1.6.0-next.1",
4
4
  "description": "Forge Key Value Store SDK",
5
5
  "author": "Atlassian",
6
6
  "license": "SEE LICENSE IN LICENSE.txt",