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
@@ -3,7 +3,15 @@
3
3
  *
4
4
  * Access advanced analytics including performance metrics, statistical analysis,
5
5
  * correlations, trend detection, spreads, and forecasts.
6
+ *
7
+ * NOTE ON PARAMETERS: The OilPriceAPI analytics controller reads commodity
8
+ * identifiers as `code` / `code1` / `code2` and the lookback window as `period`
9
+ * (in days). Earlier versions of this SDK sent `commodity` / `commodity1` /
10
+ * `commodity2` / `days`, which the API silently ignored (and `correlation`
11
+ * returned "code1 and code2 parameters are required"). These methods now send
12
+ * the parameter names the controller actually reads.
6
13
  */
14
+ import { ValidationError } from "../errors.js";
7
15
  /**
8
16
  * Analytics Resource
9
17
  *
@@ -16,13 +24,8 @@
16
24
  *
17
25
  * const client = new OilPriceAPI({ apiKey: 'your_key' });
18
26
  *
19
- * // Get performance metrics
20
- * const perf = await client.analytics.performance({ commodity: 'WTI_USD', days: 30 });
21
- * console.log(`30-day return: ${perf.return_percent}%`);
22
- * console.log(`Volatility: ${perf.volatility}`);
23
- *
24
- * // Analyze correlation
25
- * const corr = await client.analytics.correlation('WTI_USD', 'BRENT_CRUDE_USD', 90);
27
+ * // Analyze correlation between two commodities (90-day window)
28
+ * const corr = await client.analytics.correlation('WTI_USD', 'BRENT_CRUDE_USD', { period: 90 });
26
29
  * console.log(`Correlation: ${corr.correlation}`);
27
30
  * ```
28
31
  */
@@ -31,191 +34,151 @@ export class AnalyticsResource {
31
34
  this.client = client;
32
35
  }
33
36
  /**
34
- * Get performance metrics for a commodity
35
- *
36
- * Returns key performance indicators including returns, volatility,
37
- * and price range over a specified period.
38
- *
39
- * @param options - Analysis parameters
40
- * @returns Performance metrics
37
+ * Get the authenticated user's API usage analytics for the dashboard.
41
38
  *
42
- * @throws {OilPriceAPIError} If API request fails
39
+ * Maps to `GET /v1/analytics/performance`, which reads a `range`
40
+ * parameter (`7d` | `30d` | `90d`).
43
41
  *
44
- * @example
45
- * ```typescript
46
- * const metrics = await client.analytics.performance({
47
- * commodity: 'WTI_USD',
48
- * days: 30
49
- * });
50
- * console.log(`Return: ${metrics.return_percent}%`);
51
- * console.log(`Volatility: ${metrics.volatility}`);
52
- * console.log(`High: $${metrics.high}, Low: $${metrics.low}`);
53
- * ```
42
+ * @param options - `{ range }` (default range '30d')
43
+ * @returns Usage analytics (overview, daily usage, endpoint breakdown, ...)
54
44
  */
55
45
  async performance(options) {
56
46
  const params = {};
57
- if (options?.commodity)
58
- params.commodity = options.commodity;
59
- if (options?.days)
60
- params.days = options.days.toString();
47
+ if (options?.range)
48
+ params.range = options.range;
61
49
  return this.client["request"]("/v1/analytics/performance", params);
62
50
  }
63
51
  /**
64
- * Get statistical analysis for a commodity
52
+ * Get statistical analysis for a commodity.
65
53
  *
66
- * Returns comprehensive statistical metrics including mean, median,
67
- * standard deviation, and distribution characteristics.
54
+ * Maps to `GET /v1/analytics/statistics`, which reads `code` and `period`.
68
55
  *
69
- * @param commodity - Commodity code to analyze
70
- * @param days - Number of days to analyze (default: 30)
56
+ * @param code - Commodity code to analyze (e.g. 'WTI_USD')
57
+ * @param period - Lookback window in days (default: 30)
71
58
  * @returns Statistical analysis
72
59
  *
73
- * @throws {NotFoundError} If commodity not found
74
- * @throws {OilPriceAPIError} If API request fails
75
- *
76
- * @example
77
- * ```typescript
78
- * const stats = await client.analytics.statistics('BRENT_CRUDE_USD', 90);
79
- * console.log(`Mean: $${stats.mean}`);
80
- * console.log(`Std Dev: ${stats.std_dev}`);
81
- * console.log(`Range: $${stats.min} - $${stats.max}`);
82
- * ```
60
+ * @throws {ValidationError} If code is empty
83
61
  */
84
- async statistics(commodity, days) {
85
- if (!commodity || typeof commodity !== "string") {
86
- throw new Error("Commodity code must be a non-empty string");
62
+ async statistics(code, period) {
63
+ if (!code || typeof code !== "string") {
64
+ throw new ValidationError("Commodity code must be a non-empty string");
87
65
  }
88
- const params = { commodity };
89
- if (days)
90
- params.days = days.toString();
66
+ const params = { code };
67
+ if (period !== undefined)
68
+ params.period = period.toString();
91
69
  return this.client["request"]("/v1/analytics/statistics", params);
92
70
  }
93
71
  /**
94
- * Analyze correlation between two commodities
72
+ * Analyze correlation between two commodities.
95
73
  *
96
- * Calculates the Pearson correlation coefficient to measure how closely
97
- * two commodities move together.
74
+ * Maps to `GET /v1/analytics/correlation`, which reads `code1`, `code2`,
75
+ * `period` and optional `type`, `window`, `codes`.
98
76
  *
99
- * @param commodity1 - First commodity code
100
- * @param commodity2 - Second commodity code
101
- * @param days - Number of days to analyze (default: 30)
77
+ * @param code1 - First commodity code
78
+ * @param code2 - Second commodity code
79
+ * @param options - Lookback window in days, or `{ period, type, window, codes }`
102
80
  * @returns Correlation analysis
103
81
  *
104
- * @throws {NotFoundError} If either commodity not found
105
- * @throws {OilPriceAPIError} If API request fails
82
+ * @throws {ValidationError} If either code is empty
106
83
  *
107
84
  * @example
108
85
  * ```typescript
109
- * const corr = await client.analytics.correlation('WTI_USD', 'BRENT_CRUDE_USD', 90);
110
- * console.log(`Correlation: ${corr.correlation}`);
111
- * console.log(`Strength: ${corr.strength}`);
112
- * console.log(`R-squared: ${corr.r_squared}`);
86
+ * const corr = await client.analytics.correlation('WTI_USD', 'BRENT_CRUDE_USD', { period: 90 });
87
+ * console.log(corr.correlation);
113
88
  * ```
114
89
  */
115
- async correlation(commodity1, commodity2, days) {
116
- if (!commodity1 || typeof commodity1 !== "string") {
117
- throw new Error("First commodity code must be a non-empty string");
90
+ async correlation(code1, code2, options) {
91
+ if (!code1 || typeof code1 !== "string") {
92
+ throw new ValidationError("First commodity code must be a non-empty string");
118
93
  }
119
- if (!commodity2 || typeof commodity2 !== "string") {
120
- throw new Error("Second commodity code must be a non-empty string");
94
+ if (!code2 || typeof code2 !== "string") {
95
+ throw new ValidationError("Second commodity code must be a non-empty string");
121
96
  }
122
- const params = {
123
- commodity1,
124
- commodity2,
125
- };
126
- if (days)
127
- params.days = days.toString();
97
+ const opts = typeof options === "number" ? { period: options } : options || {};
98
+ const params = { code1, code2 };
99
+ if (opts.period !== undefined)
100
+ params.period = opts.period.toString();
101
+ if (opts.type)
102
+ params.type = opts.type;
103
+ if (opts.window !== undefined)
104
+ params.window = opts.window.toString();
105
+ if (opts.codes && opts.codes.length > 0)
106
+ params.codes = opts.codes.join(",");
128
107
  return this.client["request"]("/v1/analytics/correlation", params);
129
108
  }
130
109
  /**
131
- * Analyze price trend for a commodity
110
+ * Analyze price trend for a commodity.
132
111
  *
133
- * Detects trend direction, strength, and key levels (support/resistance).
112
+ * Maps to `GET /v1/analytics/trend`, which reads `code`, `period` and
113
+ * optional `type` ('analysis' | 'sma' | 'ema' | 'rsi' | 'levels') and `window`.
134
114
  *
135
- * @param commodity - Commodity code to analyze
136
- * @param days - Number of days to analyze (default: 30)
115
+ * @param code - Commodity code to analyze
116
+ * @param options - Lookback window in days, or `{ period, type, window }`
137
117
  * @returns Trend analysis
138
118
  *
139
- * @throws {NotFoundError} If commodity not found
140
- * @throws {OilPriceAPIError} If API request fails
141
- *
142
- * @example
143
- * ```typescript
144
- * const trend = await client.analytics.trend('WTI_USD', 60);
145
- * console.log(`Trend: ${trend.trend} (strength: ${trend.strength})`);
146
- * console.log(`Support: $${trend.support}`);
147
- * console.log(`Resistance: $${trend.resistance}`);
148
- * ```
119
+ * @throws {ValidationError} If code is empty
149
120
  */
150
- async trend(commodity, days) {
151
- if (!commodity || typeof commodity !== "string") {
152
- throw new Error("Commodity code must be a non-empty string");
121
+ async trend(code, options) {
122
+ if (!code || typeof code !== "string") {
123
+ throw new ValidationError("Commodity code must be a non-empty string");
153
124
  }
154
- const params = { commodity };
155
- if (days)
156
- params.days = days.toString();
125
+ const opts = typeof options === "number" ? { period: options } : options || {};
126
+ const params = { code };
127
+ if (opts.period !== undefined)
128
+ params.period = opts.period.toString();
129
+ if (opts.type)
130
+ params.type = opts.type;
131
+ if (opts.window !== undefined)
132
+ params.window = opts.window.toString();
157
133
  return this.client["request"]("/v1/analytics/trend", params);
158
134
  }
159
135
  /**
160
- * Analyze spread between two commodities
161
- *
162
- * Calculates current spread and compares to historical patterns.
136
+ * Analyze a named commodity spread (basis / crack / ratio).
163
137
  *
164
- * @param commodity1 - First commodity code
165
- * @param commodity2 - Second commodity code
166
- * @returns Spread analysis
138
+ * Maps to `GET /v1/analytics/spread`, which reads `spread` (the named spread,
139
+ * e.g. 'wti_brent'), `period` and optional `type` ('current' | 'historical').
140
+ * Call with no `spread` to list the available named spreads.
167
141
  *
168
- * @throws {NotFoundError} If either commodity not found
169
- * @throws {OilPriceAPIError} If API request fails
142
+ * @param spread - Named spread (e.g. 'wti_brent'); omit to list available spreads
143
+ * @param options - Lookback window in days, or `{ period, type }`
144
+ * @returns Spread analysis (or the list of available spreads)
170
145
  *
171
146
  * @example
172
147
  * ```typescript
173
- * const spread = await client.analytics.spread('BRENT_CRUDE_USD', 'WTI_USD');
174
- * console.log(`Current spread: $${spread.spread}`);
175
- * console.log(`Average spread: $${spread.average_spread}`);
176
- * console.log(`Z-score: ${spread.z_score}`);
148
+ * const spread = await client.analytics.spread('wti_brent', { period: 30 });
149
+ * console.log(spread.current_spread);
177
150
  * ```
178
151
  */
179
- async spread(commodity1, commodity2) {
180
- if (!commodity1 || typeof commodity1 !== "string") {
181
- throw new Error("First commodity code must be a non-empty string");
182
- }
183
- if (!commodity2 || typeof commodity2 !== "string") {
184
- throw new Error("Second commodity code must be a non-empty string");
185
- }
186
- return this.client["request"]("/v1/analytics/spread", {
187
- commodity1,
188
- commodity2,
189
- });
152
+ async spread(spread, options) {
153
+ const opts = typeof options === "number" ? { period: options } : options || {};
154
+ const params = {};
155
+ if (spread)
156
+ params.spread = spread;
157
+ if (opts.period !== undefined)
158
+ params.period = opts.period.toString();
159
+ if (opts.type)
160
+ params.type = opts.type;
161
+ return this.client["request"]("/v1/analytics/spread", params);
190
162
  }
191
163
  /**
192
- * Get price forecast for a commodity
193
- *
194
- * Returns forecasted prices with confidence intervals.
195
- *
196
- * @param commodity - Commodity code to forecast
197
- * @returns Price forecast
164
+ * Get a statistical price forecast for a commodity.
198
165
  *
199
- * @throws {NotFoundError} If commodity not found
200
- * @throws {OilPriceAPIError} If API request fails
166
+ * Maps to `GET /v1/analytics/forecast`, which reads `code`, `method`
167
+ * (default 'ema') and `period` (default 90). Call with no `code` to list
168
+ * the available forecast methods.
201
169
  *
202
- * @example
203
- * ```typescript
204
- * const forecast = await client.analytics.forecast('WTI_USD');
205
- * console.log(`Forecast model: ${forecast.model}`);
206
- * console.log(`Horizon: ${forecast.horizon_days} days`);
207
- *
208
- * forecast.forecast.forEach(point => {
209
- * console.log(`${point.date}: $${point.price} (${point.lower_bound}-${point.upper_bound})`);
210
- * });
211
- * ```
170
+ * @param code - Commodity code to forecast; omit to list available methods
171
+ * @param options - `{ method, period }`
172
+ * @returns Price forecast (or the list of available methods)
212
173
  */
213
- async forecast(commodity) {
214
- if (!commodity || typeof commodity !== "string") {
215
- throw new Error("Commodity code must be a non-empty string");
216
- }
217
- return this.client["request"]("/v1/analytics/forecast", {
218
- commodity,
219
- });
174
+ async forecast(code, options) {
175
+ const params = {};
176
+ if (code)
177
+ params.code = code;
178
+ if (options?.method)
179
+ params.method = options.method;
180
+ if (options?.period !== undefined)
181
+ params.period = options.period.toString();
182
+ return this.client["request"]("/v1/analytics/forecast", params);
220
183
  }
221
184
  }
@@ -115,13 +115,29 @@ export interface HistoricalBunkerPrice {
115
115
  unit: string;
116
116
  }
117
117
  /**
118
- * Options for historical bunker fuel query
118
+ * Options for historical bunker fuel query.
119
+ *
120
+ * The controller reads `from` / `to` (dates) and `interval` — not `start_date` /
121
+ * `end_date` / `fuel_type`. History is returned per port (all grades).
119
122
  */
120
123
  export interface HistoricalBunkerOptions {
121
124
  /** Start date in ISO 8601 format (YYYY-MM-DD) */
122
125
  startDate?: string;
123
126
  /** End date in ISO 8601 format (YYYY-MM-DD) */
124
127
  endDate?: string;
128
+ /** Aggregation interval (e.g. 'daily') */
129
+ interval?: string;
130
+ }
131
+ /**
132
+ * Options for port-to-port bunker spread query.
133
+ */
134
+ export interface BunkerSpreadOptions {
135
+ /** Origin port code */
136
+ from: string;
137
+ /** Destination port code */
138
+ to: string;
139
+ /** Fuel grade (e.g. 'VLSFO') */
140
+ grade?: string;
125
141
  }
126
142
  /**
127
143
  * Bunker Fuels Resource
@@ -206,23 +222,23 @@ export declare class BunkerFuelsResource {
206
222
  */
207
223
  compare(ports: string[]): Promise<PortPriceComparison>;
208
224
  /**
209
- * Get bunker fuel price spreads
225
+ * Get the bunker fuel price spread between two ports.
210
226
  *
211
- * Returns spreads between different fuel grades (e.g., VLSFO-IFO380).
227
+ * Maps to `GET /v1/bunker-fuels/spreads/ports`, which reads `from`, `to`
228
+ * and `grade`. (Earlier SDKs called `/v1/bunker-fuels/spreads` with no params,
229
+ * which 404'd.)
212
230
  *
213
- * @returns Fuel price spreads
231
+ * @param options - `{ from, to, grade? }`
232
+ * @returns Port-to-port fuel price spread
214
233
  *
215
- * @throws {OilPriceAPIError} If API request fails
234
+ * @throws {ValidationError} If from/to are missing
216
235
  *
217
236
  * @example
218
237
  * ```typescript
219
- * const spreads = await client.bunkerFuels.spreads();
220
- * spreads.spreads.forEach(spread => {
221
- * console.log(`${spread.fuel1}-${spread.fuel2} spread: $${spread.average_spread}`);
222
- * });
238
+ * const spread = await client.bunkerFuels.spreads({ from: 'SIN', to: 'RTM', grade: 'VLSFO' });
223
239
  * ```
224
240
  */
225
- spreads(): Promise<BunkerFuelSpreads>;
241
+ spreads(options: BunkerSpreadOptions): Promise<BunkerFuelSpreads>;
226
242
  /**
227
243
  * Get historical bunker fuel prices
228
244
  *
@@ -234,9 +250,16 @@ export declare class BunkerFuelsResource {
234
250
  * @throws {NotFoundError} If port or fuel type not found
235
251
  * @throws {OilPriceAPIError} If API request fails
236
252
  *
253
+ * The history endpoint is keyed by port in the PATH (`/historical/:port_code`)
254
+ * and returns all grades for that port; it does not filter by a single fuel
255
+ * type. It reads `from` / `to` / `interval` query params.
256
+ *
257
+ * @param port - Port code (e.g., "SIN", "RTM") — used as a path segment
258
+ * @param options - `{ startDate, endDate, interval }`
259
+ *
237
260
  * @example
238
261
  * ```typescript
239
- * const history = await client.bunkerFuels.historical('SIN', 'VLSFO', {
262
+ * const history = await client.bunkerFuels.historical('SIN', {
240
263
  * startDate: '2024-01-01',
241
264
  * endDate: '2024-12-31'
242
265
  * });
@@ -246,7 +269,7 @@ export declare class BunkerFuelsResource {
246
269
  * });
247
270
  * ```
248
271
  */
249
- historical(port: string, fuelType: string, options?: HistoricalBunkerOptions): Promise<HistoricalBunkerPrice[]>;
272
+ historical(port: string, options?: HistoricalBunkerOptions): Promise<HistoricalBunkerPrice[]>;
250
273
  /**
251
274
  * Export bunker fuel data
252
275
  *
@@ -4,6 +4,7 @@
4
4
  * Access bunker fuel prices at major ports worldwide, including VLSFO, MGO,
5
5
  * and IFO380. Compare prices across ports and track historical trends.
6
6
  */
7
+ import { ValidationError } from "../errors.js";
7
8
  /**
8
9
  * Bunker Fuels Resource
9
10
  *
@@ -50,7 +51,8 @@ export class BunkerFuelsResource {
50
51
  * ```
51
52
  */
52
53
  async all() {
53
- const response = await this.client["request"]("/v1/bunker-fuels", {});
54
+ // Route is GET /v1/bunker-fuels/all (the bare /v1/bunker-fuels has no route).
55
+ const response = await this.client["request"]("/v1/bunker-fuels/all", {});
54
56
  return Array.isArray(response) ? response : response.prices;
55
57
  }
56
58
  /**
@@ -72,7 +74,7 @@ export class BunkerFuelsResource {
72
74
  */
73
75
  async port(code) {
74
76
  if (!code || typeof code !== "string") {
75
- throw new Error("Port code must be a non-empty string");
77
+ throw new ValidationError("Port code must be a non-empty string");
76
78
  }
77
79
  return this.client["request"](`/v1/bunker-fuels/ports/${code}`, {});
78
80
  }
@@ -96,31 +98,40 @@ export class BunkerFuelsResource {
96
98
  */
97
99
  async compare(ports) {
98
100
  if (!Array.isArray(ports) || ports.length === 0) {
99
- throw new Error("Ports must be a non-empty array of port codes");
101
+ throw new ValidationError("Ports must be a non-empty array of port codes");
100
102
  }
101
103
  return this.client["request"]("/v1/bunker-fuels/compare", {
102
104
  ports: ports.join(","),
103
105
  });
104
106
  }
105
107
  /**
106
- * Get bunker fuel price spreads
108
+ * Get the bunker fuel price spread between two ports.
107
109
  *
108
- * Returns spreads between different fuel grades (e.g., VLSFO-IFO380).
110
+ * Maps to `GET /v1/bunker-fuels/spreads/ports`, which reads `from`, `to`
111
+ * and `grade`. (Earlier SDKs called `/v1/bunker-fuels/spreads` with no params,
112
+ * which 404'd.)
109
113
  *
110
- * @returns Fuel price spreads
114
+ * @param options - `{ from, to, grade? }`
115
+ * @returns Port-to-port fuel price spread
111
116
  *
112
- * @throws {OilPriceAPIError} If API request fails
117
+ * @throws {ValidationError} If from/to are missing
113
118
  *
114
119
  * @example
115
120
  * ```typescript
116
- * const spreads = await client.bunkerFuels.spreads();
117
- * spreads.spreads.forEach(spread => {
118
- * console.log(`${spread.fuel1}-${spread.fuel2} spread: $${spread.average_spread}`);
119
- * });
121
+ * const spread = await client.bunkerFuels.spreads({ from: 'SIN', to: 'RTM', grade: 'VLSFO' });
120
122
  * ```
121
123
  */
122
- async spreads() {
123
- return this.client["request"]("/v1/bunker-fuels/spreads", {});
124
+ async spreads(options) {
125
+ if (!options?.from || !options?.to) {
126
+ throw new ValidationError("Both 'from' and 'to' port codes are required");
127
+ }
128
+ const params = {
129
+ from: options.from,
130
+ to: options.to,
131
+ };
132
+ if (options.grade)
133
+ params.grade = options.grade;
134
+ return this.client["request"]("/v1/bunker-fuels/spreads/ports", params);
124
135
  }
125
136
  /**
126
137
  * Get historical bunker fuel prices
@@ -133,9 +144,16 @@ export class BunkerFuelsResource {
133
144
  * @throws {NotFoundError} If port or fuel type not found
134
145
  * @throws {OilPriceAPIError} If API request fails
135
146
  *
147
+ * The history endpoint is keyed by port in the PATH (`/historical/:port_code`)
148
+ * and returns all grades for that port; it does not filter by a single fuel
149
+ * type. It reads `from` / `to` / `interval` query params.
150
+ *
151
+ * @param port - Port code (e.g., "SIN", "RTM") — used as a path segment
152
+ * @param options - `{ startDate, endDate, interval }`
153
+ *
136
154
  * @example
137
155
  * ```typescript
138
- * const history = await client.bunkerFuels.historical('SIN', 'VLSFO', {
156
+ * const history = await client.bunkerFuels.historical('SIN', {
139
157
  * startDate: '2024-01-01',
140
158
  * endDate: '2024-12-31'
141
159
  * });
@@ -145,22 +163,19 @@ export class BunkerFuelsResource {
145
163
  * });
146
164
  * ```
147
165
  */
148
- async historical(port, fuelType, options) {
166
+ async historical(port, options) {
149
167
  if (!port || typeof port !== "string") {
150
- throw new Error("Port code must be a non-empty string");
151
- }
152
- if (!fuelType || typeof fuelType !== "string") {
153
- throw new Error("Fuel type must be a non-empty string");
168
+ throw new ValidationError("Port code must be a non-empty string");
154
169
  }
155
- const params = {
156
- port,
157
- fuel_type: fuelType,
158
- };
170
+ const params = {};
159
171
  if (options?.startDate)
160
- params.start_date = options.startDate;
172
+ params.from = options.startDate;
161
173
  if (options?.endDate)
162
- params.end_date = options.endDate;
163
- const response = await this.client["request"]("/v1/bunker-fuels/historical", params);
174
+ params.to = options.endDate;
175
+ if (options?.interval)
176
+ params.interval = options.interval;
177
+ // Port code is a PATH segment: GET /v1/bunker-fuels/historical/:port_code
178
+ const response = await this.client["request"](`/v1/bunker-fuels/historical/${port}`, params);
164
179
  return Array.isArray(response) ? response : response.data;
165
180
  }
166
181
  /**
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Get metadata about supported commodities and categories.
5
5
  */
6
+ import { ValidationError } from "../errors.js";
6
7
  /**
7
8
  * Commodities Resource
8
9
  *
@@ -75,7 +76,7 @@ export class CommoditiesResource {
75
76
  */
76
77
  async get(code) {
77
78
  if (!code || typeof code !== "string") {
78
- throw new Error("Commodity code must be a non-empty string");
79
+ throw new ValidationError("Commodity code must be a non-empty string");
79
80
  }
80
81
  return this.client["request"](`/v1/commodities/${code}`, {});
81
82
  }
@@ -3,6 +3,7 @@
3
3
  *
4
4
  * Access data quality metrics, reports, and monitoring for commodity price data.
5
5
  */
6
+ import { ValidationError } from "../errors.js";
6
7
  /**
7
8
  * Data Quality Resource
8
9
  *
@@ -132,7 +133,7 @@ export class DataQualityResource {
132
133
  */
133
134
  async report(code) {
134
135
  if (!code || typeof code !== "string") {
135
- throw new Error("Report code must be a non-empty string");
136
+ throw new ValidationError("Report code must be a non-empty string");
136
137
  }
137
138
  return this.client["request"](`/v1/data-quality/reports/${code}`, {});
138
139
  }