oilpriceapi 0.7.0 → 0.9.0

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 (81) hide show
  1. package/README.md +244 -30
  2. package/dist/cjs/client.js +610 -0
  3. package/dist/cjs/errors.js +80 -0
  4. package/dist/cjs/index.js +96 -0
  5. package/dist/cjs/package.json +1 -0
  6. package/dist/cjs/resources/alerts.js +387 -0
  7. package/dist/cjs/resources/analytics.js +188 -0
  8. package/dist/cjs/resources/bunker-fuels.js +210 -0
  9. package/dist/cjs/resources/commodities.js +115 -0
  10. package/dist/cjs/resources/data-quality.js +144 -0
  11. package/dist/cjs/resources/data-sources.js +298 -0
  12. package/dist/cjs/resources/diesel.js +119 -0
  13. package/dist/cjs/resources/drilling.js +269 -0
  14. package/dist/cjs/resources/ei/drilling-productivity.js +108 -0
  15. package/dist/cjs/resources/ei/forecasts.js +106 -0
  16. package/dist/cjs/resources/ei/frac-focus.js +165 -0
  17. package/dist/cjs/resources/ei/index.js +98 -0
  18. package/dist/cjs/resources/ei/oil-inventories.js +97 -0
  19. package/dist/cjs/resources/ei/opec-production.js +97 -0
  20. package/dist/cjs/resources/ei/rig-counts.js +93 -0
  21. package/dist/cjs/resources/ei/well-permits.js +136 -0
  22. package/dist/cjs/resources/forecasts.js +168 -0
  23. package/dist/cjs/resources/futures.js +424 -0
  24. package/dist/cjs/resources/indicators.js +79 -0
  25. package/dist/cjs/resources/raw.js +128 -0
  26. package/dist/cjs/resources/rig-counts.js +164 -0
  27. package/dist/cjs/resources/spreads.js +105 -0
  28. package/dist/cjs/resources/storage.js +166 -0
  29. package/dist/cjs/resources/streaming.js +350 -0
  30. package/dist/cjs/resources/webhooks.js +283 -0
  31. package/dist/cjs/types.js +2 -0
  32. package/dist/cjs/version.js +24 -0
  33. package/dist/client.d.ts +130 -3
  34. package/dist/client.js +206 -30
  35. package/dist/errors.d.ts +6 -0
  36. package/dist/errors.js +25 -16
  37. package/dist/index.d.ts +28 -5
  38. package/dist/index.js +29 -1
  39. package/dist/resources/alerts.js +31 -77
  40. package/dist/resources/analytics.d.ts +147 -214
  41. package/dist/resources/analytics.js +104 -141
  42. package/dist/resources/bunker-fuels.d.ts +35 -12
  43. package/dist/resources/bunker-fuels.js +41 -26
  44. package/dist/resources/commodities.js +2 -1
  45. package/dist/resources/data-quality.js +2 -1
  46. package/dist/resources/data-sources.d.ts +31 -31
  47. package/dist/resources/data-sources.js +30 -85
  48. package/dist/resources/diesel.d.ts +1 -1
  49. package/dist/resources/diesel.js +9 -38
  50. package/dist/resources/drilling.js +2 -1
  51. package/dist/resources/ei/drilling-productivity.js +2 -1
  52. package/dist/resources/ei/forecasts.js +2 -1
  53. package/dist/resources/ei/frac-focus.d.ts +23 -9
  54. package/dist/resources/ei/frac-focus.js +20 -9
  55. package/dist/resources/ei/index.js +2 -1
  56. package/dist/resources/ei/oil-inventories.js +2 -1
  57. package/dist/resources/ei/opec-production.js +2 -1
  58. package/dist/resources/ei/rig-counts.js +2 -1
  59. package/dist/resources/ei/well-permits.d.ts +25 -9
  60. package/dist/resources/ei/well-permits.js +20 -7
  61. package/dist/resources/forecasts.d.ts +4 -1
  62. package/dist/resources/forecasts.js +13 -6
  63. package/dist/resources/futures.d.ts +178 -1
  64. package/dist/resources/futures.js +199 -8
  65. package/dist/resources/indicators.d.ts +170 -0
  66. package/dist/resources/indicators.js +75 -0
  67. package/dist/resources/raw.d.ts +94 -0
  68. package/dist/resources/raw.js +124 -0
  69. package/dist/resources/rig-counts.js +5 -2
  70. package/dist/resources/spreads.d.ts +121 -0
  71. package/dist/resources/spreads.js +101 -0
  72. package/dist/resources/storage.d.ts +5 -4
  73. package/dist/resources/storage.js +7 -6
  74. package/dist/resources/streaming.d.ts +272 -0
  75. package/dist/resources/streaming.js +342 -0
  76. package/dist/resources/webhooks.d.ts +73 -23
  77. package/dist/resources/webhooks.js +59 -77
  78. package/dist/types.d.ts +43 -1
  79. package/dist/version.d.ts +1 -1
  80. package/dist/version.js +2 -2
  81. package/package.json +21 -6
package/dist/client.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { OilPriceAPIConfig, Price, LatestPricesOptions, HistoricalPricesOptions, Commodity, CommoditiesResponse, CategoriesResponse, DataConnectorPrice, DataConnectorOptions } from "./types.js";
1
+ import type { OilPriceAPIConfig, Price, LatestPricesOptions, HistoricalPricesOptions, Commodity, CommoditiesResponse, CategoriesResponse, DataConnectorPrice, DataConnectorOptions, DemoPricesResponse, DemoCommoditiesResponse } from "./types.js";
2
2
  import { DieselResource } from "./resources/diesel.js";
3
3
  import { AlertsResource } from "./resources/alerts.js";
4
4
  import { CommoditiesResource } from "./resources/commodities.js";
@@ -13,6 +13,26 @@ import { DrillingIntelligenceResource } from "./resources/drilling.js";
13
13
  import { EnergyIntelligenceResource } from "./resources/ei/index.js";
14
14
  import { WebhooksResource } from "./resources/webhooks.js";
15
15
  import { DataSourcesResource } from "./resources/data-sources.js";
16
+ import { SpreadsResource } from "./resources/spreads.js";
17
+ import { IndicatorsResource } from "./resources/indicators.js";
18
+ import { RawResource } from "./resources/raw.js";
19
+ import { StreamingResource } from "./resources/streaming.js";
20
+ /**
21
+ * Raw HTTP response wrapper.
22
+ *
23
+ * Returned by {@link OilPriceAPI.raw} accessors to expose the underlying
24
+ * HTTP status code and response headers alongside the parsed data.
25
+ *
26
+ * @typeParam T - The parsed response body type.
27
+ */
28
+ export interface APIResponse<T> {
29
+ /** Parsed response data (same shape the non-raw method would return). */
30
+ data: T;
31
+ /** HTTP status code (e.g., 200, 201). */
32
+ status: number;
33
+ /** Response headers. */
34
+ headers: Headers;
35
+ }
16
36
  /**
17
37
  * Official Node.js client for Oil Price API
18
38
  *
@@ -105,7 +125,29 @@ export declare class OilPriceAPI {
105
125
  * Data sources resource (BYOS - Bring Your Own Source)
106
126
  */
107
127
  readonly dataSources: DataSourcesResource;
108
- constructor(config: OilPriceAPIConfig);
128
+ /**
129
+ * Spreads resource (crack, basis, curve structure, margin, physical premium)
130
+ */
131
+ readonly spreads: SpreadsResource;
132
+ /**
133
+ * Indicators resource (fuel switching, price context, storage analytics,
134
+ * annotations, CFTC positioning, congressional trades)
135
+ */
136
+ readonly indicators: IndicatorsResource;
137
+ /**
138
+ * Raw-response accessor.
139
+ *
140
+ * Mirrors the top-level price/commodity methods but returns the underlying
141
+ * HTTP status and headers alongside the parsed data via {@link APIResponse}.
142
+ */
143
+ readonly raw: RawResource;
144
+ /**
145
+ * Real-time price streaming resource (WebSocket / ActionCable).
146
+ *
147
+ * Streaming requires a Reservoir Mastery (Professional+) plan.
148
+ */
149
+ readonly stream: StreamingResource;
150
+ constructor(config?: OilPriceAPIConfig);
109
151
  /**
110
152
  * Log debug messages if debug mode is enabled
111
153
  */
@@ -123,9 +165,28 @@ export declare class OilPriceAPI {
123
165
  */
124
166
  private isRetryable;
125
167
  /**
126
- * Internal method to make HTTP requests with retry and timeout
168
+ * Shape a parsed JSON response body into the value returned to callers.
169
+ *
170
+ * Centralizes the response-structure handling so that both {@link request}
171
+ * and {@link requestRaw} return identical data. Handles the latest/historical
172
+ * envelope shapes as well as the generic `{ data }` fallback used by resource
173
+ * mutations, alerts, webhooks, etc.
174
+ */
175
+ private shapeResponseData;
176
+ /**
177
+ * Internal method to make HTTP requests with retry and timeout.
178
+ * Supports all HTTP methods (GET, POST, PATCH, DELETE) with consistent
179
+ * retry logic, timeout handling, and typed error responses.
127
180
  */
128
181
  private request;
182
+ /**
183
+ * Internal method identical to {@link request} but returns the underlying
184
+ * HTTP status and headers alongside the parsed data.
185
+ *
186
+ * Used by the public {@link raw} accessor to expose response metadata
187
+ * (issue #7) without changing the return shape of existing methods.
188
+ */
189
+ private requestRaw;
129
190
  /**
130
191
  * Get the latest prices for all commodities or a specific commodity
131
192
  *
@@ -165,6 +226,35 @@ export declare class OilPriceAPI {
165
226
  * ```
166
227
  */
167
228
  getHistoricalPrices(options?: HistoricalPricesOptions): Promise<Price[]>;
229
+ /**
230
+ * Paginate through historical prices automatically.
231
+ *
232
+ * Returns an async generator that yields pages of prices, fetching
233
+ * the next page only when needed. Avoids loading all data into memory.
234
+ *
235
+ * @param options - Same options as getHistoricalPrices, plus perPage (default: 100)
236
+ *
237
+ * @example
238
+ * ```typescript
239
+ * // Iterate through all pages
240
+ * for await (const page of client.paginateHistoricalPrices({
241
+ * commodity: 'BRENT_CRUDE_USD',
242
+ * startDate: '2024-01-01',
243
+ * endDate: '2024-12-31',
244
+ * perPage: 100,
245
+ * })) {
246
+ * console.log(`Got ${page.length} prices`);
247
+ * // Process each page...
248
+ * }
249
+ *
250
+ * // Or collect all prices
251
+ * const allPrices: Price[] = [];
252
+ * for await (const page of client.paginateHistoricalPrices({ commodity: 'WTI_USD' })) {
253
+ * allPrices.push(...page);
254
+ * }
255
+ * ```
256
+ */
257
+ paginateHistoricalPrices(options?: HistoricalPricesOptions): AsyncGenerator<Price[]>;
168
258
  /**
169
259
  * Get prices from your connected data sources (BYOS)
170
260
  *
@@ -221,4 +311,41 @@ export declare class OilPriceAPI {
221
311
  * ```
222
312
  */
223
313
  getCommodity(code: string): Promise<Commodity>;
314
+ /**
315
+ * Fetch live sample prices from the public, no-auth demo endpoint.
316
+ *
317
+ * Hits `GET /v1/demo/prices` (no API key required) and returns the parsed
318
+ * `{ prices, meta }` envelope. Useful for trying the client without
319
+ * credentials. Subject to the demo rate limit (~20 requests/hour).
320
+ *
321
+ * @example
322
+ * ```typescript
323
+ * const demo = await client.getDemoPrices();
324
+ * const brent = demo.prices.find(p => p.code === 'BRENT_CRUDE_USD');
325
+ * console.log(brent?.price);
326
+ * ```
327
+ */
328
+ getDemoPrices(): Promise<DemoPricesResponse>;
329
+ /**
330
+ * Fetch the catalogue of commodities from the public, no-auth demo endpoint.
331
+ *
332
+ * Hits `GET /v1/demo/commodities` (no API key required) and returns the parsed
333
+ * `{ commodities, meta }` envelope, where `meta.free_commodities` lists the
334
+ * codes available on the free demo tier.
335
+ *
336
+ * @example
337
+ * ```typescript
338
+ * const demo = await client.getDemoCommodities();
339
+ * console.log(demo.meta.total, demo.meta.free_commodities);
340
+ * ```
341
+ */
342
+ getDemoCommodities(): Promise<DemoCommoditiesResponse>;
343
+ /**
344
+ * Minimal fetch for the no-auth demo endpoints.
345
+ *
346
+ * Unlike {@link request}, this does NOT run the latest/historical response
347
+ * shaping (which would strip the `meta` block) and does NOT require an API
348
+ * key — it returns the raw `data` envelope from `{ status, data }`.
349
+ */
350
+ private requestDemo;
224
351
  }
package/dist/client.js CHANGED
@@ -14,6 +14,10 @@ import { EnergyIntelligenceResource } from "./resources/ei/index.js";
14
14
  import { WebhooksResource } from "./resources/webhooks.js";
15
15
  import { DataSourcesResource } from "./resources/data-sources.js";
16
16
  import { SDK_VERSION, SDK_NAME, buildUserAgent } from "./version.js";
17
+ import { SpreadsResource } from "./resources/spreads.js";
18
+ import { IndicatorsResource } from "./resources/indicators.js";
19
+ import { RawResource } from "./resources/raw.js";
20
+ import { StreamingResource } from "./resources/streaming.js";
17
21
  /**
18
22
  * Official Node.js client for Oil Price API
19
23
  *
@@ -41,11 +45,11 @@ import { SDK_VERSION, SDK_NAME, buildUserAgent } from "./version.js";
41
45
  * ```
42
46
  */
43
47
  export class OilPriceAPI {
44
- constructor(config) {
45
- if (!config.apiKey) {
46
- throw new OilPriceAPIError("API key is required");
48
+ constructor(config = {}) {
49
+ this.apiKey = config.apiKey || process.env.OILPRICEAPI_KEY || "";
50
+ if (!this.apiKey) {
51
+ throw new OilPriceAPIError("API key required. Set OILPRICEAPI_KEY env var or pass apiKey in config.");
47
52
  }
48
- this.apiKey = config.apiKey;
49
53
  this.baseUrl = config.baseUrl || "https://api.oilpriceapi.com";
50
54
  this.retries = config.retries !== undefined ? config.retries : 3;
51
55
  this.retryDelay = config.retryDelay || 1000;
@@ -69,6 +73,10 @@ export class OilPriceAPI {
69
73
  this.ei = new EnergyIntelligenceResource(this);
70
74
  this.webhooks = new WebhooksResource(this);
71
75
  this.dataSources = new DataSourcesResource(this);
76
+ this.spreads = new SpreadsResource(this);
77
+ this.indicators = new IndicatorsResource(this);
78
+ this.raw = new RawResource(this);
79
+ this.stream = new StreamingResource(this);
72
80
  }
73
81
  /**
74
82
  * Log debug messages if debug mode is enabled
@@ -123,9 +131,50 @@ export class OilPriceAPI {
123
131
  return false;
124
132
  }
125
133
  /**
126
- * Internal method to make HTTP requests with retry and timeout
134
+ * Shape a parsed JSON response body into the value returned to callers.
135
+ *
136
+ * Centralizes the response-structure handling so that both {@link request}
137
+ * and {@link requestRaw} return identical data. Handles the latest/historical
138
+ * envelope shapes as well as the generic `{ data }` fallback used by resource
139
+ * mutations, alerts, webhooks, etc.
140
+ */
141
+ shapeResponseData(responseData) {
142
+ // Handle different response structures
143
+ // Latest endpoint: { status, data: { price, ... } }
144
+ // Historical endpoint: { status, data: { prices: [...] } }
145
+ if (responseData.status === "success" && responseData.data) {
146
+ if (responseData.data.prices) {
147
+ // Historical endpoint - return prices array
148
+ this.log(`Returning ${responseData.data.prices.length} prices`);
149
+ return responseData.data.prices;
150
+ }
151
+ else if (responseData.data.price !== undefined) {
152
+ // Latest endpoint - wrap single price in array
153
+ this.log("Returning single price (wrapped in array)");
154
+ return [responseData.data];
155
+ }
156
+ }
157
+ // Fallback - return data as-is (used by resource mutations, alerts, webhooks, etc.)
158
+ this.log("Returning data as-is");
159
+ return (responseData.data !== undefined ? responseData.data : responseData);
160
+ }
161
+ /**
162
+ * Internal method to make HTTP requests with retry and timeout.
163
+ * Supports all HTTP methods (GET, POST, PATCH, DELETE) with consistent
164
+ * retry logic, timeout handling, and typed error responses.
165
+ */
166
+ async request(endpoint, params, options) {
167
+ const { data } = await this.requestRaw(endpoint, params, options);
168
+ return data;
169
+ }
170
+ /**
171
+ * Internal method identical to {@link request} but returns the underlying
172
+ * HTTP status and headers alongside the parsed data.
173
+ *
174
+ * Used by the public {@link raw} accessor to expose response metadata
175
+ * (issue #7) without changing the return shape of existing methods.
127
176
  */
128
- async request(endpoint, params) {
177
+ async requestRaw(endpoint, params, options) {
129
178
  // Build URL with query parameters
130
179
  const url = new URL(`${this.baseUrl}${endpoint}`);
131
180
  if (params) {
@@ -163,11 +212,15 @@ export class OilPriceAPI {
163
212
  if (this.appName) {
164
213
  headers["X-App-Name"] = this.appName;
165
214
  }
166
- const response = await fetch(url.toString(), {
167
- method: "GET",
215
+ const fetchOptions = {
216
+ method: options?.method || "GET",
168
217
  headers,
169
218
  signal: controller.signal,
170
- });
219
+ };
220
+ if (options?.body !== undefined) {
221
+ fetchOptions.body = JSON.stringify(options.body);
222
+ }
223
+ const response = await fetch(url.toString(), fetchOptions);
171
224
  clearTimeout(timeoutId);
172
225
  this.log(`Response: ${response.status} ${response.statusText}`);
173
226
  // Handle error responses
@@ -177,8 +230,7 @@ export class OilPriceAPI {
177
230
  // Try to parse JSON error response
178
231
  try {
179
232
  const errorJson = JSON.parse(errorBody);
180
- errorMessage =
181
- errorJson.message || errorJson.error || errorMessage;
233
+ errorMessage = errorJson.message || errorJson.error || errorMessage;
182
234
  }
183
235
  catch {
184
236
  // Use default error message if response isn't JSON
@@ -209,30 +261,27 @@ export class OilPriceAPI {
209
261
  throw new OilPriceAPIError(errorMessage, response.status, "HTTP_ERROR");
210
262
  }
211
263
  }
264
+ // Handle empty responses (e.g., 204 No Content from DELETE)
265
+ const responseText = await response.text();
266
+ if (!responseText) {
267
+ this.log("Empty response body");
268
+ return {
269
+ data: {},
270
+ status: response.status,
271
+ headers: response.headers,
272
+ };
273
+ }
212
274
  // Parse successful response
213
- const responseData = await response.json();
275
+ const responseData = JSON.parse(responseText);
214
276
  this.log("Response data received", {
215
277
  status: responseData.status,
216
278
  hasData: !!responseData.data,
217
279
  });
218
- // Handle different response structures
219
- // Latest endpoint: { status, data: { price, ... } }
220
- // Historical endpoint: { status, data: { prices: [...] } }
221
- if (responseData.status === "success" && responseData.data) {
222
- if (responseData.data.prices) {
223
- // Historical endpoint - return prices array
224
- this.log(`Returning ${responseData.data.prices.length} prices`);
225
- return responseData.data.prices;
226
- }
227
- else if (responseData.data.price !== undefined) {
228
- // Latest endpoint - wrap single price in array
229
- this.log("Returning single price (wrapped in array)");
230
- return [responseData.data];
231
- }
232
- }
233
- // Fallback - return data as-is
234
- this.log("Returning data as-is");
235
- return responseData.data;
280
+ return {
281
+ data: this.shapeResponseData(responseData),
282
+ status: response.status,
283
+ headers: response.headers,
284
+ };
236
285
  }
237
286
  catch (error) {
238
287
  // Handle abort (timeout)
@@ -353,6 +402,51 @@ export class OilPriceAPI {
353
402
  // Solution: Use /v1/prices/past_year which uses direct WHERE clauses
354
403
  return this.request("/v1/prices/past_year", params);
355
404
  }
405
+ /**
406
+ * Paginate through historical prices automatically.
407
+ *
408
+ * Returns an async generator that yields pages of prices, fetching
409
+ * the next page only when needed. Avoids loading all data into memory.
410
+ *
411
+ * @param options - Same options as getHistoricalPrices, plus perPage (default: 100)
412
+ *
413
+ * @example
414
+ * ```typescript
415
+ * // Iterate through all pages
416
+ * for await (const page of client.paginateHistoricalPrices({
417
+ * commodity: 'BRENT_CRUDE_USD',
418
+ * startDate: '2024-01-01',
419
+ * endDate: '2024-12-31',
420
+ * perPage: 100,
421
+ * })) {
422
+ * console.log(`Got ${page.length} prices`);
423
+ * // Process each page...
424
+ * }
425
+ *
426
+ * // Or collect all prices
427
+ * const allPrices: Price[] = [];
428
+ * for await (const page of client.paginateHistoricalPrices({ commodity: 'WTI_USD' })) {
429
+ * allPrices.push(...page);
430
+ * }
431
+ * ```
432
+ */
433
+ async *paginateHistoricalPrices(options) {
434
+ const perPage = options?.perPage || 100;
435
+ let page = 1;
436
+ while (true) {
437
+ const results = await this.getHistoricalPrices({
438
+ ...options,
439
+ page,
440
+ perPage,
441
+ });
442
+ if (results.length === 0)
443
+ break;
444
+ yield results;
445
+ if (results.length < perPage)
446
+ break;
447
+ page++;
448
+ }
449
+ }
356
450
  /**
357
451
  * Get prices from your connected data sources (BYOS)
358
452
  *
@@ -427,4 +521,86 @@ export class OilPriceAPI {
427
521
  async getCommodity(code) {
428
522
  return this.request(`/v1/commodities/${code}`, {});
429
523
  }
524
+ /**
525
+ * Fetch live sample prices from the public, no-auth demo endpoint.
526
+ *
527
+ * Hits `GET /v1/demo/prices` (no API key required) and returns the parsed
528
+ * `{ prices, meta }` envelope. Useful for trying the client without
529
+ * credentials. Subject to the demo rate limit (~20 requests/hour).
530
+ *
531
+ * @example
532
+ * ```typescript
533
+ * const demo = await client.getDemoPrices();
534
+ * const brent = demo.prices.find(p => p.code === 'BRENT_CRUDE_USD');
535
+ * console.log(brent?.price);
536
+ * ```
537
+ */
538
+ async getDemoPrices() {
539
+ return this.requestDemo("/v1/demo/prices");
540
+ }
541
+ /**
542
+ * Fetch the catalogue of commodities from the public, no-auth demo endpoint.
543
+ *
544
+ * Hits `GET /v1/demo/commodities` (no API key required) and returns the parsed
545
+ * `{ commodities, meta }` envelope, where `meta.free_commodities` lists the
546
+ * codes available on the free demo tier.
547
+ *
548
+ * @example
549
+ * ```typescript
550
+ * const demo = await client.getDemoCommodities();
551
+ * console.log(demo.meta.total, demo.meta.free_commodities);
552
+ * ```
553
+ */
554
+ async getDemoCommodities() {
555
+ return this.requestDemo("/v1/demo/commodities");
556
+ }
557
+ /**
558
+ * Minimal fetch for the no-auth demo endpoints.
559
+ *
560
+ * Unlike {@link request}, this does NOT run the latest/historical response
561
+ * shaping (which would strip the `meta` block) and does NOT require an API
562
+ * key — it returns the raw `data` envelope from `{ status, data }`.
563
+ */
564
+ async requestDemo(endpoint) {
565
+ const url = `${this.baseUrl}${endpoint}`;
566
+ this.log(`Demo request: ${url}`);
567
+ const controller = new AbortController();
568
+ const timeoutId = setTimeout(() => controller.abort(), this.timeout);
569
+ try {
570
+ const response = await fetch(url, {
571
+ method: "GET",
572
+ headers: {
573
+ "Content-Type": "application/json",
574
+ "User-Agent": buildUserAgent(),
575
+ "X-SDK-Name": SDK_NAME,
576
+ "X-SDK-Version": SDK_VERSION,
577
+ },
578
+ signal: controller.signal,
579
+ });
580
+ if (!response.ok) {
581
+ const body = await response.text();
582
+ let message = `HTTP ${response.status}: ${response.statusText}`;
583
+ try {
584
+ const json = JSON.parse(body);
585
+ message = json.message || json.error || message;
586
+ }
587
+ catch {
588
+ // non-JSON error body
589
+ }
590
+ throw new OilPriceAPIError(message, response.status, "HTTP_ERROR");
591
+ }
592
+ const parsed = JSON.parse(await response.text());
593
+ // Demo envelope is { status: "success", data: { ... } }.
594
+ return (parsed.data !== undefined ? parsed.data : parsed);
595
+ }
596
+ catch (error) {
597
+ if (error instanceof Error && error.name === "AbortError") {
598
+ throw new TimeoutError("Request timeout", this.timeout);
599
+ }
600
+ throw error;
601
+ }
602
+ finally {
603
+ clearTimeout(timeoutId);
604
+ }
605
+ }
430
606
  }
package/dist/errors.d.ts CHANGED
@@ -40,6 +40,12 @@ export declare class NotFoundError extends OilPriceAPIError {
40
40
  export declare class ServerError extends OilPriceAPIError {
41
41
  constructor(message?: string, statusCode?: number);
42
42
  }
43
+ /**
44
+ * Thrown when SDK-side input validation fails
45
+ */
46
+ export declare class ValidationError extends OilPriceAPIError {
47
+ constructor(message: string);
48
+ }
43
49
  /**
44
50
  * Thrown when request exceeds timeout
45
51
  */
package/dist/errors.js CHANGED
@@ -4,7 +4,7 @@
4
4
  export class OilPriceAPIError extends Error {
5
5
  constructor(message, statusCode, code) {
6
6
  super(message);
7
- this.name = 'OilPriceAPIError';
7
+ this.name = "OilPriceAPIError";
8
8
  this.statusCode = statusCode;
9
9
  this.code = code;
10
10
  // Maintains proper stack trace for where our error was thrown (only available on V8)
@@ -17,18 +17,18 @@ export class OilPriceAPIError extends Error {
17
17
  * Thrown when API authentication fails (401)
18
18
  */
19
19
  export class AuthenticationError extends OilPriceAPIError {
20
- constructor(message = 'Invalid API key') {
21
- super(message, 401, 'AUTHENTICATION_ERROR');
22
- this.name = 'AuthenticationError';
20
+ constructor(message = "Invalid API key") {
21
+ super(message, 401, "AUTHENTICATION_ERROR");
22
+ this.name = "AuthenticationError";
23
23
  }
24
24
  }
25
25
  /**
26
26
  * Thrown when rate limit is exceeded (429)
27
27
  */
28
28
  export class RateLimitError extends OilPriceAPIError {
29
- constructor(message = 'Rate limit exceeded', retryAfter) {
30
- super(message, 429, 'RATE_LIMIT_ERROR');
31
- this.name = 'RateLimitError';
29
+ constructor(message = "Rate limit exceeded", retryAfter) {
30
+ super(message, 429, "RATE_LIMIT_ERROR");
31
+ this.name = "RateLimitError";
32
32
  this.retryAfter = retryAfter;
33
33
  }
34
34
  }
@@ -36,26 +36,35 @@ export class RateLimitError extends OilPriceAPIError {
36
36
  * Thrown when requested resource is not found (404)
37
37
  */
38
38
  export class NotFoundError extends OilPriceAPIError {
39
- constructor(message = 'Resource not found') {
40
- super(message, 404, 'NOT_FOUND_ERROR');
41
- this.name = 'NotFoundError';
39
+ constructor(message = "Resource not found") {
40
+ super(message, 404, "NOT_FOUND_ERROR");
41
+ this.name = "NotFoundError";
42
42
  }
43
43
  }
44
44
  /**
45
45
  * Thrown when server returns 5xx error
46
46
  */
47
47
  export class ServerError extends OilPriceAPIError {
48
- constructor(message = 'Internal server error', statusCode = 500) {
49
- super(message, statusCode, 'SERVER_ERROR');
50
- this.name = 'ServerError';
48
+ constructor(message = "Internal server error", statusCode = 500) {
49
+ super(message, statusCode, "SERVER_ERROR");
50
+ this.name = "ServerError";
51
+ }
52
+ }
53
+ /**
54
+ * Thrown when SDK-side input validation fails
55
+ */
56
+ export class ValidationError extends OilPriceAPIError {
57
+ constructor(message) {
58
+ super(message, undefined, "VALIDATION_ERROR");
59
+ this.name = "ValidationError";
51
60
  }
52
61
  }
53
62
  /**
54
63
  * Thrown when request exceeds timeout
55
64
  */
56
65
  export class TimeoutError extends OilPriceAPIError {
57
- constructor(message = 'Request timeout', timeout) {
58
- super(`${message} (${timeout}ms)`, undefined, 'TIMEOUT_ERROR');
59
- this.name = 'TimeoutError';
66
+ constructor(message = "Request timeout", timeout) {
67
+ super(`${message} (${timeout}ms)`, undefined, "TIMEOUT_ERROR");
68
+ this.name = "TimeoutError";
60
69
  }
61
70
  }
package/dist/index.d.ts CHANGED
@@ -6,22 +6,29 @@
6
6
  * @packageDocumentation
7
7
  */
8
8
  export { OilPriceAPI } from "./client.js";
9
+ export type { APIResponse } from "./client.js";
9
10
  export { SDK_VERSION, SDK_NAME } from "./version.js";
10
- export type { OilPriceAPIConfig, RetryStrategy, Price, LatestPricesOptions, HistoricalPricesOptions, HistoricalPeriod, AggregationInterval, Commodity, CommoditiesResponse, CommodityCategory, CategoriesResponse, DataConnectorPrice, DataConnectorOptions, } from "./types.js";
11
+ export type { OilPriceAPIConfig, RetryStrategy, Price, LatestPricesOptions, HistoricalPricesOptions, HistoricalPeriod, AggregationInterval, Commodity, CommoditiesResponse, CommodityCategory, CategoriesResponse, DataConnectorPrice, DataConnectorOptions, DemoPrice, DemoPricesResponse, DemoCommoditiesResponse, } from "./types.js";
11
12
  export type { DieselPrice, DieselStation, DieselStationsResponse, GetDieselStationsOptions, } from "./resources/diesel.js";
12
13
  export type { PriceAlert, CreateAlertParams, UpdateAlertParams, AlertOperator, } from "./resources/alerts.js";
13
- export type { FuturesPrice, HistoricalFuturesPrice, HistoricalFuturesOptions, FuturesOHLC, IntradayPrice, IntradayFuturesData, FuturesSpread, FuturesCurvePoint, FuturesCurveData, ContinuousContractPrice, ContinuousFuturesData, } from "./resources/futures.js";
14
+ export type { FuturesPrice, HistoricalFuturesPrice, HistoricalFuturesOptions, FuturesOHLC, IntradayPrice, IntradayFuturesData, FuturesSpread, FuturesCurvePoint, FuturesCurveData, ContinuousContractPrice, ContinuousFuturesData, FuturesSpreadHistoryPoint, FuturesSpreadHistory, FuturesContractFamilySlug, } from "./resources/futures.js";
15
+ export { FUTURES_CONTRACTS, FUTURES_FAMILY_SLUGS, FuturesContractFamily, } from "./resources/futures.js";
16
+ export type { SpreadType, SpreadValue, HistoricalSpreadValue, HistoricalSpreadOptions, } from "./resources/spreads.js";
17
+ export { SpreadsResource } from "./resources/spreads.js";
18
+ export type { IndicatorType, FuelSwitchingIndicator, PriceContextIndicator, StorageAnalyticsIndicator, AnnotationIndicator, CFTCPositioningIndicator, CongressionalTradeIndicator, } from "./resources/indicators.js";
19
+ export { IndicatorsResource } from "./resources/indicators.js";
20
+ export { RawResource } from "./resources/raw.js";
14
21
  export type { StorageData, HistoricalStorageData, HistoricalStorageOptions, } from "./resources/storage.js";
15
22
  export type { RigCountData, HistoricalRigCountData, HistoricalRigCountOptions, RigCountTrend, RigCountSummary, } from "./resources/rig-counts.js";
16
23
  export type { BunkerFuelPrice, PortBunkerPrices, PortPriceComparison, BunkerFuelSpreads, HistoricalBunkerPrice, HistoricalBunkerOptions, } from "./resources/bunker-fuels.js";
17
- export type { PerformanceMetrics, PerformanceOptions, StatisticalAnalysis, CorrelationAnalysis, TrendAnalysis, SpreadAnalysis, ForecastPoint, PriceForecast as AnalyticsPriceForecast, } from "./resources/analytics.js";
18
- export type { MonthlyForecast, ForecastAccuracy, ArchivedForecast, } from "./resources/forecasts.js";
24
+ export type { PerformanceMetrics, PerformanceOptions, StatisticalAnalysis, CorrelationAnalysis, CorrelationOptions, TrendAnalysis, TrendOptions, SpreadAnalysis, SpreadOptions, ForecastOptions, PriceForecast as AnalyticsPriceForecast, } from "./resources/analytics.js";
25
+ export type { MonthlyForecast, ForecastAccuracy, ArchivedForecast } from "./resources/forecasts.js";
19
26
  export type { DataQualitySummary, DataQualityReportMeta, DataQualityReport, } from "./resources/data-quality.js";
20
27
  export type { DrillingIntelligenceData, LatestDrillingData, DrillingSummary, DrillingTrend, FracSpreadData, WellPermitData, DUCWellData, CompletionData, WellsDrilledData, BasinDrillingData, } from "./resources/drilling.js";
21
28
  export type { WellTimelineEvent, WellTimeline, RigCountRecord, RigCountByBasin, RigCountByState, HistoricalRigCount, OilInventoryRecord, OilInventorySummary, InventoryByProduct, HistoricalInventory, CushingInventory, OPECProductionRecord, TotalOPECProduction, ProductionByCountry, HistoricalProduction, TopProducer, DrillingProductivityRecord, DrillingProductivitySummary, DUCWellInventory, ProductivityByBasin, HistoricalProductivity, ProductivityTrend, ForecastRecord, ForecastSummary, PriceForecast, ProductionForecast, HistoricalForecast, ForecastComparison, WellPermitRecord, WellPermitSummary, PermitsByState, PermitsByOperator, PermitsByFormation, WellPermitSearchQuery, FracFocusRecord, FracFocusSummary, DisclosuresByState, DisclosuresByOperator, ChemicalUsage, WellChemical, FracFocusSearchQuery, } from "./resources/ei/index.js";
22
29
  export type { WebhookEndpoint, CreateWebhookParams, UpdateWebhookParams, WebhookTestResponse as WebhookTestResult, WebhookEvent, } from "./resources/webhooks.js";
23
30
  export type { DataSourceType, DataSourceStatus, DataSource, CreateDataSourceParams, UpdateDataSourceParams, DataSourceTestResponse, DataSourceLog, DataSourceHealth, CredentialRotationResponse, } from "./resources/data-sources.js";
24
- export { OilPriceAPIError, AuthenticationError, RateLimitError, NotFoundError, ServerError, TimeoutError, } from "./errors.js";
31
+ export { OilPriceAPIError, AuthenticationError, RateLimitError, NotFoundError, ServerError, ValidationError, TimeoutError, } from "./errors.js";
25
32
  export { DieselResource } from "./resources/diesel.js";
26
33
  export { AlertsResource } from "./resources/alerts.js";
27
34
  export { CommoditiesResource } from "./resources/commodities.js";
@@ -36,3 +43,19 @@ export { DrillingIntelligenceResource } from "./resources/drilling.js";
36
43
  export { EnergyIntelligenceResource, EIRigCountsResource, EIOilInventoriesResource, EIOPECProductionResource, EIDrillingProductivityResource, EIForecastsResource, EIWellPermitsResource, EIFracFocusResource, } from "./resources/ei/index.js";
37
44
  export { WebhooksResource } from "./resources/webhooks.js";
38
45
  export { DataSourcesResource } from "./resources/data-sources.js";
46
+ export { StreamingResource, PriceStreamSubscription, ENERGY_PRICES_CHANNEL, } from "./resources/streaming.js";
47
+ export type { StreamPricesOptions, PriceUpdateHandler, StreamMessage, WelcomeMessage, PriceUpdateMessage, RigCountUpdateMessage, StreamedPrice, StreamedPriceMap, } from "./resources/streaming.js";
48
+ /**
49
+ * Standalone webhook signature verification.
50
+ *
51
+ * Convenience function for verifying webhook signatures without
52
+ * instantiating a full client.
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * import { verifyWebhookSignature } from 'oilpriceapi';
57
+ *
58
+ * const isValid = verifyWebhookSignature(rawBody, signatureHeader, secret);
59
+ * ```
60
+ */
61
+ export declare function verifyWebhookSignature(payload: string | Buffer, signature: string, secret: string): boolean;
package/dist/index.js CHANGED
@@ -5,9 +5,14 @@
5
5
  *
6
6
  * @packageDocumentation
7
7
  */
8
+ import { createHmac, timingSafeEqual } from "node:crypto";
8
9
  export { OilPriceAPI } from "./client.js";
9
10
  export { SDK_VERSION, SDK_NAME } from "./version.js";
10
- export { OilPriceAPIError, AuthenticationError, RateLimitError, NotFoundError, ServerError, TimeoutError, } from "./errors.js";
11
+ export { FUTURES_CONTRACTS, FUTURES_FAMILY_SLUGS, FuturesContractFamily, } from "./resources/futures.js";
12
+ export { SpreadsResource } from "./resources/spreads.js";
13
+ export { IndicatorsResource } from "./resources/indicators.js";
14
+ export { RawResource } from "./resources/raw.js";
15
+ export { OilPriceAPIError, AuthenticationError, RateLimitError, NotFoundError, ServerError, ValidationError, TimeoutError, } from "./errors.js";
11
16
  export { DieselResource } from "./resources/diesel.js";
12
17
  export { AlertsResource } from "./resources/alerts.js";
13
18
  export { CommoditiesResource } from "./resources/commodities.js";
@@ -22,3 +27,26 @@ export { DrillingIntelligenceResource } from "./resources/drilling.js";
22
27
  export { EnergyIntelligenceResource, EIRigCountsResource, EIOilInventoriesResource, EIOPECProductionResource, EIDrillingProductivityResource, EIForecastsResource, EIWellPermitsResource, EIFracFocusResource, } from "./resources/ei/index.js";
23
28
  export { WebhooksResource } from "./resources/webhooks.js";
24
29
  export { DataSourcesResource } from "./resources/data-sources.js";
30
+ export { StreamingResource, PriceStreamSubscription, ENERGY_PRICES_CHANNEL, } from "./resources/streaming.js";
31
+ /**
32
+ * Standalone webhook signature verification.
33
+ *
34
+ * Convenience function for verifying webhook signatures without
35
+ * instantiating a full client.
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * import { verifyWebhookSignature } from 'oilpriceapi';
40
+ *
41
+ * const isValid = verifyWebhookSignature(rawBody, signatureHeader, secret);
42
+ * ```
43
+ */
44
+ export function verifyWebhookSignature(payload, signature, secret) {
45
+ const expectedSignature = "sha256=" + createHmac("sha256", secret).update(payload).digest("hex");
46
+ try {
47
+ return timingSafeEqual(Buffer.from(expectedSignature), Buffer.from(signature));
48
+ }
49
+ catch {
50
+ return false;
51
+ }
52
+ }