oilpriceapi 0.8.2 → 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 (53) hide show
  1. package/README.md +201 -19
  2. package/dist/cjs/client.js +139 -19
  3. package/dist/cjs/index.js +17 -3
  4. package/dist/cjs/resources/analytics.js +99 -137
  5. package/dist/cjs/resources/bunker-fuels.js +37 -23
  6. package/dist/cjs/resources/data-sources.js +13 -12
  7. package/dist/cjs/resources/ei/frac-focus.js +16 -6
  8. package/dist/cjs/resources/ei/well-permits.js +18 -6
  9. package/dist/cjs/resources/forecasts.js +11 -5
  10. package/dist/cjs/resources/futures.js +192 -1
  11. package/dist/cjs/resources/indicators.js +79 -0
  12. package/dist/cjs/resources/raw.js +128 -0
  13. package/dist/cjs/resources/rig-counts.js +5 -2
  14. package/dist/cjs/resources/spreads.js +105 -0
  15. package/dist/cjs/resources/storage.js +5 -5
  16. package/dist/cjs/resources/streaming.js +350 -0
  17. package/dist/cjs/resources/webhooks.js +3 -14
  18. package/dist/cjs/version.js +1 -1
  19. package/dist/client.d.ts +97 -1
  20. package/dist/client.js +139 -19
  21. package/dist/index.d.ts +12 -3
  22. package/dist/index.js +5 -0
  23. package/dist/resources/analytics.d.ts +147 -214
  24. package/dist/resources/analytics.js +99 -137
  25. package/dist/resources/bunker-fuels.d.ts +35 -12
  26. package/dist/resources/bunker-fuels.js +37 -23
  27. package/dist/resources/data-sources.d.ts +31 -31
  28. package/dist/resources/data-sources.js +13 -12
  29. package/dist/resources/ei/frac-focus.d.ts +23 -9
  30. package/dist/resources/ei/frac-focus.js +16 -6
  31. package/dist/resources/ei/well-permits.d.ts +25 -9
  32. package/dist/resources/ei/well-permits.js +18 -6
  33. package/dist/resources/forecasts.d.ts +4 -1
  34. package/dist/resources/forecasts.js +11 -5
  35. package/dist/resources/futures.d.ts +178 -1
  36. package/dist/resources/futures.js +190 -0
  37. package/dist/resources/indicators.d.ts +170 -0
  38. package/dist/resources/indicators.js +75 -0
  39. package/dist/resources/raw.d.ts +94 -0
  40. package/dist/resources/raw.js +124 -0
  41. package/dist/resources/rig-counts.js +5 -2
  42. package/dist/resources/spreads.d.ts +121 -0
  43. package/dist/resources/spreads.js +101 -0
  44. package/dist/resources/storage.d.ts +5 -4
  45. package/dist/resources/storage.js +5 -5
  46. package/dist/resources/streaming.d.ts +272 -0
  47. package/dist/resources/streaming.js +342 -0
  48. package/dist/resources/webhooks.d.ts +37 -23
  49. package/dist/resources/webhooks.js +3 -14
  50. package/dist/types.d.ts +41 -0
  51. package/dist/version.d.ts +1 -1
  52. package/dist/version.js +1 -1
  53. package/package.json +7 -1
@@ -0,0 +1,342 @@
1
+ /**
2
+ * WebSocket Streaming Resource
3
+ *
4
+ * Real-time price streaming via the OilPriceAPI ActionCable endpoint
5
+ * (`wss://api.oilpriceapi.com/cable`).
6
+ *
7
+ * Streaming is a **Reservoir Mastery (Professional+)** feature. Connections
8
+ * authenticate with your API key and subscribe to the `EnergyPricesChannel`,
9
+ * which pushes an initial `welcome` snapshot followed by live `price_update`
10
+ * and (for drilling-tier accounts) `rig_count_update` messages.
11
+ *
12
+ * The implementation speaks the raw ActionCable JSON subprotocol over the
13
+ * `ws` package: it performs the `welcome` -> `subscribe` ->
14
+ * `confirm_subscription` handshake, answers server `ping` frames, and
15
+ * surfaces decoded channel messages as typed events. Auto-reconnect with
16
+ * exponential backoff keeps the stream alive across transient network drops.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * const sub = client.stream.prices({}, (update) => {
21
+ * console.log(update.prices.oil.wti?.original_price);
22
+ * });
23
+ *
24
+ * sub.on("rig_count_update", (m) => console.log(m.rig_count.region, m.rig_count.count));
25
+ * sub.on("error", (err) => console.error(err));
26
+ *
27
+ * // later
28
+ * sub.close();
29
+ * ```
30
+ */
31
+ import { EventEmitter } from "node:events";
32
+ import WebSocket from "ws";
33
+ /**
34
+ * The ActionCable channel exposed by the OilPriceAPI server.
35
+ *
36
+ * Confirmed from `app/channels/energy_prices_channel.rb`
37
+ * (`class EnergyPricesChannel`).
38
+ */
39
+ export const ENERGY_PRICES_CHANNEL = "EnergyPricesChannel";
40
+ /** Maps streamed slug <-> upstream code so `commodities` filtering accepts either. */
41
+ const COMMODITY_CODE_TO_SLUG = {
42
+ BRENT_CRUDE_USD: "brent",
43
+ WTI_USD: "wti",
44
+ NATURAL_GAS_GBP: "uk",
45
+ NATURAL_GAS_USD: "us",
46
+ DUTCH_TTF_EUR: "eu",
47
+ };
48
+ /**
49
+ * Handle for an active price stream.
50
+ *
51
+ * Extends `EventEmitter`. Emitted events:
52
+ * - `"connected"` — transport connected and subscription confirmed
53
+ * - `"welcome"` — initial snapshot ({@link WelcomeMessage})
54
+ * - `"price_update"` — live price broadcast ({@link PriceUpdateMessage})
55
+ * - `"rig_count_update"` — drilling broadcast ({@link RigCountUpdateMessage})
56
+ * - `"message"` — every decoded channel message ({@link StreamMessage})
57
+ * - `"reconnecting"` — a reconnect attempt is scheduled (`{ attempt, delay }`)
58
+ * - `"disconnected"` — transport closed (`{ code, reason }`)
59
+ * - `"error"` — an `Error` (transport error, unauthorized, or retries exhausted)
60
+ * - `"close"` — the subscription was closed via {@link PriceStreamSubscription.close}
61
+ */
62
+ export class PriceStreamSubscription extends EventEmitter {
63
+ /**
64
+ * @param url - The `wss://.../cable` endpoint.
65
+ * @param apiKey - API key sent as the ActionCable `Authorization: Token <key>` header.
66
+ * @param options - Reconnect + filter options.
67
+ * @param wsImpl - Injectable WebSocket constructor (used by tests to mock).
68
+ * @internal Construct via {@link StreamingResource.prices}.
69
+ */
70
+ constructor(url, apiKey, options, wsImpl = WebSocket) {
71
+ super();
72
+ this.wsImpl = wsImpl;
73
+ this.ws = null;
74
+ this.closed = false;
75
+ this.subscribed = false;
76
+ this.reconnectAttempts = 0;
77
+ this.reconnectTimer = null;
78
+ this.url = url;
79
+ this.apiKey = apiKey;
80
+ this.options = {
81
+ autoReconnect: options.autoReconnect ?? true,
82
+ reconnectDelay: options.reconnectDelay ?? 1000,
83
+ maxReconnectDelay: options.maxReconnectDelay ?? 30000,
84
+ maxReconnectAttempts: options.maxReconnectAttempts ?? 10,
85
+ commodities: options.commodities,
86
+ };
87
+ // The ActionCable subscription identifier. The channel takes no params;
88
+ // we additionally pass `api_key` in the identifier for parity with the
89
+ // documented native-WebSocket clients, while the server's
90
+ // ApplicationCable::Connection authenticates from the Authorization header.
91
+ this.identifier = JSON.stringify({
92
+ channel: ENERGY_PRICES_CHANNEL,
93
+ api_key: apiKey,
94
+ });
95
+ if (options.commodities && options.commodities.length > 0) {
96
+ this.commodityFilter = new Set(options.commodities.map((c) => COMMODITY_CODE_TO_SLUG[c] ?? c.toLowerCase()));
97
+ }
98
+ else {
99
+ this.commodityFilter = null;
100
+ }
101
+ }
102
+ /**
103
+ * Open the transport and begin the ActionCable handshake.
104
+ * @internal Called once by {@link StreamingResource.prices}.
105
+ */
106
+ connect() {
107
+ if (this.closed)
108
+ return;
109
+ const ws = new this.wsImpl(this.url, {
110
+ headers: {
111
+ Authorization: `Token ${this.apiKey}`,
112
+ },
113
+ });
114
+ this.ws = ws;
115
+ ws.on("open", () => {
116
+ // ActionCable sends a transport `welcome` frame first; we subscribe on
117
+ // open (the server tolerates an early subscribe and replies with
118
+ // confirm_subscription once the connection is established).
119
+ this.send({ command: "subscribe", identifier: this.identifier });
120
+ });
121
+ ws.on("message", (raw) => this.handleRaw(raw));
122
+ ws.on("error", (err) => {
123
+ this.emit("error", err);
124
+ });
125
+ ws.on("close", (code, reason) => {
126
+ this.ws = null;
127
+ this.subscribed = false;
128
+ this.emit("disconnected", { code, reason: reason?.toString() ?? "" });
129
+ if (!this.closed) {
130
+ this.scheduleReconnect();
131
+ }
132
+ });
133
+ }
134
+ handleRaw(raw) {
135
+ let frame;
136
+ try {
137
+ frame = JSON.parse(raw.toString());
138
+ }
139
+ catch {
140
+ // Ignore malformed frames rather than crash the stream.
141
+ return;
142
+ }
143
+ // ActionCable transport-level frames carry a top-level `type`.
144
+ const transportType = frame["type"];
145
+ if (transportType === "ping") {
146
+ // Heartbeat — nothing to do; presence of frames keeps `ws` alive.
147
+ return;
148
+ }
149
+ if (transportType === "welcome") {
150
+ // Transport handshake complete. (subscribe already sent on open.)
151
+ return;
152
+ }
153
+ if (transportType === "confirm_subscription") {
154
+ this.subscribed = true;
155
+ this.reconnectAttempts = 0;
156
+ this.emit("connected");
157
+ return;
158
+ }
159
+ if (transportType === "reject_subscription") {
160
+ this.emit("error", new Error("WebSocket subscription rejected. Streaming requires a Reservoir Mastery " +
161
+ "(Professional+) plan and a valid API key."));
162
+ return;
163
+ }
164
+ if (transportType === "disconnect") {
165
+ // Server-initiated disconnect (e.g. auth failure). Let `close` drive reconnect.
166
+ return;
167
+ }
168
+ // Channel message: payload lives under `message`.
169
+ const payload = frame["message"];
170
+ if (payload && typeof payload === "object") {
171
+ this.dispatch(payload);
172
+ }
173
+ }
174
+ dispatch(message) {
175
+ this.emit("message", message);
176
+ switch (message.type) {
177
+ case "welcome":
178
+ this.emit("welcome", message);
179
+ break;
180
+ case "price_update": {
181
+ const update = message;
182
+ if (this.matchesFilter(update)) {
183
+ this.emit("price_update", update);
184
+ }
185
+ break;
186
+ }
187
+ case "rig_count_update":
188
+ this.emit("rig_count_update", message);
189
+ break;
190
+ default:
191
+ // Unknown message types are still emitted via `message` above.
192
+ break;
193
+ }
194
+ }
195
+ matchesFilter(update) {
196
+ if (!this.commodityFilter)
197
+ return true;
198
+ const { oil, natural_gas } = update.prices ?? {};
199
+ const present = {
200
+ brent: oil?.brent,
201
+ wti: oil?.wti,
202
+ uk: natural_gas?.uk,
203
+ us: natural_gas?.us,
204
+ eu: natural_gas?.eu,
205
+ };
206
+ for (const slug of this.commodityFilter) {
207
+ if (present[slug])
208
+ return true;
209
+ }
210
+ return false;
211
+ }
212
+ scheduleReconnect() {
213
+ if (this.closed || !this.options.autoReconnect)
214
+ return;
215
+ if (this.reconnectAttempts >= this.options.maxReconnectAttempts) {
216
+ this.emit("error", new Error(`WebSocket reconnect failed after ${this.reconnectAttempts} attempt(s); giving up.`));
217
+ return;
218
+ }
219
+ const attempt = this.reconnectAttempts++;
220
+ const delay = Math.min(this.options.reconnectDelay * Math.pow(2, attempt), this.options.maxReconnectDelay);
221
+ this.emit("reconnecting", { attempt: attempt + 1, delay });
222
+ this.reconnectTimer = setTimeout(() => {
223
+ this.reconnectTimer = null;
224
+ this.connect();
225
+ }, delay);
226
+ }
227
+ send(payload) {
228
+ if (this.ws && this.ws.readyState === this.wsImpl.OPEN) {
229
+ this.ws.send(JSON.stringify(payload));
230
+ }
231
+ }
232
+ /** Whether the channel subscription has been confirmed by the server. */
233
+ get isSubscribed() {
234
+ return this.subscribed;
235
+ }
236
+ /**
237
+ * Cleanly tear down the stream: cancels any pending reconnect, unsubscribes
238
+ * from the channel, and closes the socket. Safe to call multiple times.
239
+ * Emits `"close"` once.
240
+ */
241
+ close() {
242
+ if (this.closed)
243
+ return;
244
+ this.closed = true;
245
+ if (this.reconnectTimer) {
246
+ clearTimeout(this.reconnectTimer);
247
+ this.reconnectTimer = null;
248
+ }
249
+ if (this.ws) {
250
+ const ws = this.ws;
251
+ try {
252
+ if (ws.readyState === this.wsImpl.OPEN) {
253
+ ws.send(JSON.stringify({ command: "unsubscribe", identifier: this.identifier }));
254
+ }
255
+ }
256
+ catch {
257
+ // ignore — we're closing anyway
258
+ }
259
+ try {
260
+ ws.close();
261
+ }
262
+ catch {
263
+ // ignore
264
+ }
265
+ this.ws = null;
266
+ }
267
+ this.subscribed = false;
268
+ this.emit("close");
269
+ this.removeAllListeners();
270
+ }
271
+ }
272
+ /**
273
+ * Streaming resource — entry point for real-time price subscriptions.
274
+ *
275
+ * Accessed via `client.stream`.
276
+ */
277
+ export class StreamingResource {
278
+ constructor(client,
279
+ /**
280
+ * Injectable WebSocket implementation. Defaults to the `ws` package;
281
+ * tests pass a mock constructor.
282
+ * @internal
283
+ */
284
+ wsImpl = WebSocket) {
285
+ this.client = client;
286
+ this.wsImpl = wsImpl;
287
+ }
288
+ /**
289
+ * Derive the `wss://.../cable` endpoint from the client's REST base URL.
290
+ *
291
+ * `https://api.oilpriceapi.com` -> `wss://api.oilpriceapi.com/cable`
292
+ * `http://localhost:5000` -> `ws://localhost:5000/cable`
293
+ */
294
+ cableUrl() {
295
+ const base = this.client["baseUrl"];
296
+ const wsBase = base
297
+ .replace(/^http:/, "ws:")
298
+ .replace(/^https:/, "wss:")
299
+ .replace(/\/$/, "");
300
+ return `${wsBase}/cable`;
301
+ }
302
+ /**
303
+ * Open a real-time price stream over the `EnergyPricesChannel`.
304
+ *
305
+ * Returns a {@link PriceStreamSubscription} handle (an `EventEmitter`) you
306
+ * can attach further listeners to and `.close()` when done. The optional
307
+ * `onUpdate` callback is a convenience wired to the `price_update` event.
308
+ *
309
+ * @param options - Filtering and reconnect options.
310
+ * @param onUpdate - Optional callback for each `price_update` message.
311
+ * @returns The subscription handle.
312
+ *
313
+ * @throws {OilPriceAPIError} If no API key is configured on the client.
314
+ *
315
+ * @example
316
+ * ```typescript
317
+ * const client = new OilPriceAPI({ apiKey: process.env.OILPRICEAPI_KEY });
318
+ *
319
+ * const sub = client.stream.prices(
320
+ * { commodities: ["WTI_USD", "BRENT_CRUDE_USD"] },
321
+ * (update) => {
322
+ * const wti = update.prices.oil.wti;
323
+ * if (wti) console.log(`WTI ${wti.original_price} @ ${update.timestamp}`);
324
+ * },
325
+ * );
326
+ *
327
+ * sub.on("connected", () => console.log("streaming live"));
328
+ * sub.on("error", (err) => console.error("stream error:", err));
329
+ *
330
+ * process.on("SIGINT", () => sub.close());
331
+ * ```
332
+ */
333
+ prices(options = {}, onUpdate) {
334
+ const apiKey = this.client["apiKey"];
335
+ const sub = new PriceStreamSubscription(this.cableUrl(), apiKey, options, this.wsImpl);
336
+ if (onUpdate) {
337
+ sub.on("price_update", onUpdate);
338
+ }
339
+ sub.connect();
340
+ return sub;
341
+ }
342
+ }
@@ -10,18 +10,16 @@ import type { OilPriceAPI } from "../client.js";
10
10
  export interface WebhookEndpoint {
11
11
  /** Unique webhook identifier */
12
12
  id: string;
13
- /** User-friendly webhook name */
14
- name: string;
13
+ /** User-friendly description of the webhook */
14
+ description?: string;
15
15
  /** Webhook URL (must be HTTPS) */
16
16
  url: string;
17
17
  /** Event types to subscribe to */
18
18
  events: string[];
19
- /** Whether the webhook is active */
20
- enabled: boolean;
21
- /** Optional secret for signature verification */
19
+ /** Lifecycle status (e.g. 'active', 'disabled') */
20
+ status?: string;
21
+ /** Optional secret for signature verification (generated server-side) */
22
22
  secret?: string;
23
- /** Optional metadata */
24
- metadata?: Record<string, unknown>;
25
23
  /** Number of successful deliveries */
26
24
  successful_deliveries: number;
27
25
  /** Number of failed deliveries */
@@ -37,37 +35,53 @@ export interface WebhookEndpoint {
37
35
  }
38
36
  /**
39
37
  * Parameters for creating a webhook
38
+ *
39
+ * NOTE: The API permits a flat (un-nested) body with the fields below. Earlier
40
+ * SDK versions nested these under a `webhook` key and used `name`/`enabled`,
41
+ * which the controller dropped — the controller reads `description` and `status`.
40
42
  */
41
43
  export interface CreateWebhookParams {
42
- /** User-friendly webhook name */
43
- name: string;
44
44
  /** Webhook URL (must be HTTPS) */
45
45
  url: string;
46
46
  /** Event types to subscribe to */
47
47
  events: string[];
48
- /** Whether to enable immediately (default: true) */
49
- enabled?: boolean;
50
- /** Optional secret for signature verification */
51
- secret?: string;
52
- /** Optional metadata */
53
- metadata?: Record<string, unknown>;
48
+ /** User-friendly description */
49
+ description?: string;
50
+ /** Lifecycle status (e.g. 'active', 'disabled') */
51
+ status?: string;
52
+ /** Commodity codes to filter events to */
53
+ commodity_filters?: string[];
54
+ /** US state codes to filter events to */
55
+ state_filters?: string[];
56
+ /** Per-second delivery rate limit */
57
+ rate_limit_per_second?: number;
58
+ /** Delivery timeout in seconds */
59
+ timeout_seconds?: number;
60
+ /** Max delivery retry attempts */
61
+ max_retries?: number;
54
62
  }
55
63
  /**
56
64
  * Parameters for updating a webhook
57
65
  */
58
66
  export interface UpdateWebhookParams {
59
- /** User-friendly webhook name */
60
- name?: string;
61
67
  /** Webhook URL */
62
68
  url?: string;
63
69
  /** Event types to subscribe to */
64
70
  events?: string[];
65
- /** Whether the webhook is active */
66
- enabled?: boolean;
67
- /** Secret for signature verification */
68
- secret?: string;
69
- /** Metadata */
70
- metadata?: Record<string, unknown>;
71
+ /** User-friendly description */
72
+ description?: string;
73
+ /** Lifecycle status (e.g. 'active', 'disabled') */
74
+ status?: string;
75
+ /** Commodity codes to filter events to */
76
+ commodity_filters?: string[];
77
+ /** US state codes to filter events to */
78
+ state_filters?: string[];
79
+ /** Per-second delivery rate limit */
80
+ rate_limit_per_second?: number;
81
+ /** Delivery timeout in seconds */
82
+ timeout_seconds?: number;
83
+ /** Max delivery retry attempts */
84
+ max_retries?: number;
71
85
  }
72
86
  /**
73
87
  * Webhook test response
@@ -114,27 +114,16 @@ export class WebhooksResource {
114
114
  * ```
115
115
  */
116
116
  async create(params) {
117
- if (!params.name || typeof params.name !== "string") {
118
- throw new ValidationError("Webhook name is required");
119
- }
120
117
  if (!params.url || !params.url.startsWith("https://")) {
121
118
  throw new ValidationError("Webhook URL must use HTTPS protocol");
122
119
  }
123
120
  if (!params.events || !Array.isArray(params.events) || params.events.length === 0) {
124
121
  throw new ValidationError("At least one event type is required");
125
122
  }
123
+ // The controller reads a flat (un-nested) body via params.permit(...).
126
124
  const response = await this.client["request"]("/v1/webhooks", {}, {
127
125
  method: "POST",
128
- body: {
129
- webhook: {
130
- name: params.name,
131
- url: params.url,
132
- events: params.events,
133
- enabled: params.enabled ?? true,
134
- secret: params.secret,
135
- metadata: params.metadata,
136
- },
137
- },
126
+ body: { ...params },
138
127
  });
139
128
  return "webhook" in response ? response.webhook : response;
140
129
  }
@@ -172,7 +161,7 @@ export class WebhooksResource {
172
161
  }
173
162
  const response = await this.client["request"](`/v1/webhooks/${id}`, {}, {
174
163
  method: "PATCH",
175
- body: { webhook: params },
164
+ body: { ...params },
176
165
  });
177
166
  return "webhook" in response ? response.webhook : response;
178
167
  }
package/dist/types.d.ts CHANGED
@@ -240,6 +240,47 @@ export interface Commodity {
240
240
  export interface CommoditiesResponse {
241
241
  commodities: Commodity[];
242
242
  }
243
+ /**
244
+ * A single price entry from the no-auth demo prices endpoint.
245
+ */
246
+ export interface DemoPrice {
247
+ code: string;
248
+ name: string;
249
+ price: number;
250
+ currency: string;
251
+ updated_at: string;
252
+ change_24h?: number;
253
+ source?: string;
254
+ }
255
+ /**
256
+ * Parsed response from the no-auth `GET /v1/demo/prices` endpoint.
257
+ */
258
+ export interface DemoPricesResponse {
259
+ prices: DemoPrice[];
260
+ meta: {
261
+ demo_mode?: boolean;
262
+ rate_limit?: string;
263
+ available_commodities?: number;
264
+ [key: string]: unknown;
265
+ };
266
+ examples?: unknown;
267
+ }
268
+ /**
269
+ * Parsed response from the no-auth `GET /v1/demo/commodities` endpoint.
270
+ */
271
+ export interface DemoCommoditiesResponse {
272
+ /** Commodities grouped by category key. */
273
+ commodities: Record<string, Commodity[]>;
274
+ meta: {
275
+ /** Total number of demo-listed commodities. */
276
+ total: number;
277
+ /** Category keys present in the listing. */
278
+ categories: string[];
279
+ /** Commodity codes available on the free demo tier. */
280
+ free_commodities: string[];
281
+ [key: string]: unknown;
282
+ };
283
+ }
243
284
  /**
244
285
  * Category with its commodities
245
286
  */
package/dist/version.d.ts CHANGED
@@ -7,7 +7,7 @@
7
7
  * - X-Client-Version header
8
8
  * - Package.json (should match)
9
9
  */
10
- export declare const SDK_VERSION = "0.8.2";
10
+ export declare const SDK_VERSION = "0.9.0";
11
11
  /**
12
12
  * SDK identifier used in User-Agent and X-Api-Client headers
13
13
  */
package/dist/version.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * - X-Client-Version header
8
8
  * - Package.json (should match)
9
9
  */
10
- export const SDK_VERSION = "0.8.2";
10
+ export const SDK_VERSION = "0.9.0";
11
11
  /**
12
12
  * SDK identifier used in User-Agent and X-Api-Client headers
13
13
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oilpriceapi",
3
- "version": "0.8.2",
3
+ "version": "0.9.0",
4
4
  "description": "Official Node.js SDK for Oil Price API - Real-time and historical oil & commodity prices",
5
5
  "type": "module",
6
6
  "main": "./dist/cjs/index.js",
@@ -27,6 +27,7 @@
27
27
  "lint": "eslint src/",
28
28
  "test": "vitest run",
29
29
  "test:watch": "vitest",
30
+ "test:live": "vitest run --config vitest.live.config.ts",
30
31
  "docs": "typedoc",
31
32
  "prepublishOnly": "npm run build"
32
33
  },
@@ -62,10 +63,15 @@
62
63
  },
63
64
  "devDependencies": {
64
65
  "@types/node": "^20.10.0",
66
+ "@types/ws": "^8.18.1",
65
67
  "@vitest/coverage-v8": "^4.0.16",
66
68
  "eslint": "^9.39.4",
69
+ "typedoc": "^0.27.9",
67
70
  "typescript": "^5.3.0",
68
71
  "typescript-eslint": "^8.57.2",
69
72
  "vitest": "^4.0.16"
73
+ },
74
+ "dependencies": {
75
+ "ws": "^8.21.0"
70
76
  }
71
77
  }