orakle 0.0.1 → 0.0.2

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.
package/dist/index.cjs ADDED
@@ -0,0 +1,440 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ OrakleApiError: () => OrakleApiError,
24
+ OrakleClient: () => OrakleClient,
25
+ OrakleConnectionError: () => OrakleConnectionError,
26
+ OrakleError: () => OrakleError,
27
+ OrakleTimeoutError: () => OrakleTimeoutError
28
+ });
29
+ module.exports = __toCommonJS(index_exports);
30
+
31
+ // src/errors.ts
32
+ var OrakleError = class extends Error {
33
+ constructor(message) {
34
+ super(message);
35
+ this.name = "OrakleError";
36
+ }
37
+ };
38
+ var OrakleApiError = class extends OrakleError {
39
+ status;
40
+ code;
41
+ constructor(message, status, code) {
42
+ super(message);
43
+ this.name = "OrakleApiError";
44
+ this.status = status;
45
+ this.code = code;
46
+ }
47
+ };
48
+ var OrakleTimeoutError = class extends OrakleError {
49
+ timeoutMs;
50
+ constructor(timeoutMs) {
51
+ super(`Request timed out after ${timeoutMs}ms`);
52
+ this.name = "OrakleTimeoutError";
53
+ this.timeoutMs = timeoutMs;
54
+ }
55
+ };
56
+ var OrakleConnectionError = class extends OrakleError {
57
+ cause;
58
+ constructor(message, cause) {
59
+ super(message);
60
+ this.name = "OrakleConnectionError";
61
+ this.cause = cause;
62
+ }
63
+ };
64
+
65
+ // src/sse.ts
66
+ var createSSEStream = async function* (options) {
67
+ const { url, headers, signal, lastEventId } = options;
68
+ const requestHeaders = {
69
+ ...headers,
70
+ Accept: "text/event-stream",
71
+ "Cache-Control": "no-cache"
72
+ };
73
+ if (lastEventId) {
74
+ requestHeaders["Last-Event-Id"] = lastEventId;
75
+ }
76
+ let response;
77
+ try {
78
+ response = await fetch(url, {
79
+ method: "GET",
80
+ headers: requestHeaders,
81
+ signal
82
+ });
83
+ } catch (err) {
84
+ if (err instanceof DOMException && err.name === "AbortError") {
85
+ throw err;
86
+ }
87
+ throw new OrakleConnectionError(
88
+ `Failed to connect to SSE stream: ${url}`,
89
+ err
90
+ );
91
+ }
92
+ if (!response.ok) {
93
+ const body2 = await response.text();
94
+ throw new SSEHttpError(response.status, body2);
95
+ }
96
+ const body = response.body;
97
+ if (!body) {
98
+ throw new OrakleConnectionError("Response body is null");
99
+ }
100
+ yield* parseSSEBody(body, signal);
101
+ };
102
+ var parseSSEBody = async function* (body, signal) {
103
+ const reader = body.getReader();
104
+ const decoder = new TextDecoder();
105
+ let buffer = "";
106
+ let currentEvent = "";
107
+ let currentId = "";
108
+ let currentData = [];
109
+ const readOrAbort = () => {
110
+ if (signal?.aborted) {
111
+ return Promise.reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
112
+ }
113
+ return new Promise((resolve, reject) => {
114
+ const onAbort = () => {
115
+ reject(signal?.reason ?? new DOMException("Aborted", "AbortError"));
116
+ };
117
+ signal?.addEventListener("abort", onAbort, { once: true });
118
+ reader.read().then(
119
+ (result) => {
120
+ signal?.removeEventListener("abort", onAbort);
121
+ resolve(result);
122
+ },
123
+ (err) => {
124
+ signal?.removeEventListener("abort", onAbort);
125
+ reject(err);
126
+ }
127
+ );
128
+ });
129
+ };
130
+ try {
131
+ while (true) {
132
+ if (signal?.aborted) {
133
+ break;
134
+ }
135
+ const { done, value } = await readOrAbort();
136
+ if (done) break;
137
+ buffer += decoder.decode(value, { stream: true });
138
+ const lines = buffer.split("\n");
139
+ buffer = lines.pop() ?? "";
140
+ for (const line of lines) {
141
+ if (line === "") {
142
+ if (currentData.length > 0) {
143
+ yield {
144
+ event: currentEvent ?? "message",
145
+ id: currentId,
146
+ data: currentData.join("\n")
147
+ };
148
+ }
149
+ currentEvent = "";
150
+ currentId = "";
151
+ currentData = [];
152
+ continue;
153
+ }
154
+ if (line.startsWith(":")) {
155
+ continue;
156
+ }
157
+ const colonIdx = line.indexOf(":");
158
+ if (colonIdx === -1) {
159
+ continue;
160
+ }
161
+ const field = line.slice(0, colonIdx);
162
+ let value_ = line.slice(colonIdx + 1);
163
+ if (value_.startsWith(" ")) {
164
+ value_ = value_.slice(1);
165
+ }
166
+ switch (field) {
167
+ case "event":
168
+ currentEvent = value_;
169
+ break;
170
+ case "id":
171
+ currentId = value_;
172
+ break;
173
+ case "data":
174
+ currentData.push(value_);
175
+ break;
176
+ case "retry":
177
+ break;
178
+ }
179
+ }
180
+ }
181
+ if (buffer !== "") {
182
+ const lines = buffer.split("\n");
183
+ for (const line of lines) {
184
+ if (line === "" && currentData.length > 0) {
185
+ yield {
186
+ event: currentEvent ?? "message",
187
+ id: currentId,
188
+ data: currentData.join("\n")
189
+ };
190
+ currentEvent = "";
191
+ currentId = "";
192
+ currentData = [];
193
+ continue;
194
+ }
195
+ if (line.startsWith(":") || line === "") continue;
196
+ const colonIdx = line.indexOf(":");
197
+ if (colonIdx === -1) continue;
198
+ const field = line.slice(0, colonIdx);
199
+ let value_ = line.slice(colonIdx + 1);
200
+ if (value_.startsWith(" ")) value_ = value_.slice(1);
201
+ switch (field) {
202
+ case "event":
203
+ currentEvent = value_;
204
+ break;
205
+ case "id":
206
+ currentId = value_;
207
+ break;
208
+ case "data":
209
+ currentData.push(value_);
210
+ break;
211
+ }
212
+ }
213
+ if (currentData.length > 0) {
214
+ yield {
215
+ event: currentEvent ?? "message",
216
+ id: currentId,
217
+ data: currentData.join("\n")
218
+ };
219
+ }
220
+ }
221
+ } finally {
222
+ reader.releaseLock();
223
+ }
224
+ };
225
+ var SSEHttpError = class extends Error {
226
+ status;
227
+ body;
228
+ constructor(status, body) {
229
+ super(`SSE request failed with status ${status}`);
230
+ this.name = "SSEHttpError";
231
+ this.status = status;
232
+ this.body = body;
233
+ }
234
+ };
235
+
236
+ // src/client.ts
237
+ var DEFAULT_BASE_URL = "https://api.orakle.xyz";
238
+ var DEFAULT_TIMEOUT_MS = 18e4;
239
+ var OrakleClient = class {
240
+ apiKey;
241
+ baseUrl;
242
+ timeout;
243
+ constructor(config) {
244
+ this.apiKey = config.apiKey;
245
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
246
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
247
+ }
248
+ /**
249
+ * Submit a pricing job and wait for the result.
250
+ *
251
+ * Internally: POST /v1/jobs -> open SSE stream -> resolve on completed, reject on failed.
252
+ */
253
+ async priceCheck(params, options) {
254
+ const timeoutMs = options?.timeout ?? this.timeout;
255
+ const controller = new AbortController();
256
+ const externalSignal = options?.signal;
257
+ const onExternalAbort = () => controller.abort();
258
+ externalSignal?.addEventListener("abort", onExternalAbort, { once: true });
259
+ const timer = setTimeout(() => {
260
+ controller.abort(new OrakleTimeoutError(timeoutMs));
261
+ }, timeoutMs);
262
+ try {
263
+ const job = await this.submitJob(params, controller.signal);
264
+ const streamUrl = `${this.baseUrl}/v1/jobs/${job.job_id}/stream`;
265
+ const stream = createSSEStream({
266
+ url: streamUrl,
267
+ headers: this.defaultHeaders(),
268
+ signal: controller.signal
269
+ });
270
+ for await (const frame of stream) {
271
+ const event = this.parseJobEvent(frame.event, frame.data);
272
+ if (!event) continue;
273
+ switch (event.event_type) {
274
+ case "stage":
275
+ options?.onProgress?.(event);
276
+ break;
277
+ case "completed":
278
+ return event.result;
279
+ case "failed":
280
+ throw new OrakleError(event.error);
281
+ }
282
+ }
283
+ throw new OrakleError("SSE stream ended without a completed or failed event");
284
+ } catch (err) {
285
+ throw this.normalizeError(err);
286
+ } finally {
287
+ clearTimeout(timer);
288
+ externalSignal?.removeEventListener("abort", onExternalAbort);
289
+ }
290
+ }
291
+ /**
292
+ * Submit a pricing job and return an async iterable of all events.
293
+ *
294
+ * Yields StageEvent and CompletedEvent/FailedEvent as they arrive.
295
+ */
296
+ async *priceCheckStream(params, options) {
297
+ const timeoutMs = options?.timeout ?? this.timeout;
298
+ const controller = new AbortController();
299
+ const externalSignal = options?.signal;
300
+ const onExternalAbort = () => controller.abort();
301
+ externalSignal?.addEventListener("abort", onExternalAbort, { once: true });
302
+ const timer = setTimeout(() => {
303
+ controller.abort(new OrakleTimeoutError(timeoutMs));
304
+ }, timeoutMs);
305
+ try {
306
+ const job = await this.submitJob(params, controller.signal);
307
+ const streamUrl = `${this.baseUrl}/v1/jobs/${job.job_id}/stream`;
308
+ const stream = createSSEStream({
309
+ url: streamUrl,
310
+ headers: this.defaultHeaders(),
311
+ signal: controller.signal
312
+ });
313
+ for await (const frame of stream) {
314
+ const event = this.parseJobEvent(frame.event, frame.data);
315
+ if (!event) continue;
316
+ if (event.event_type === "stage") {
317
+ options?.onProgress?.(event);
318
+ }
319
+ yield event;
320
+ if (event.event_type === "completed" || event.event_type === "failed") {
321
+ return;
322
+ }
323
+ }
324
+ } catch (err) {
325
+ throw this.normalizeError(err);
326
+ } finally {
327
+ clearTimeout(timer);
328
+ externalSignal?.removeEventListener("abort", onExternalAbort);
329
+ }
330
+ }
331
+ // ---------------------------------------------------------------------------
332
+ // Private helpers
333
+ // ---------------------------------------------------------------------------
334
+ async submitJob(params, signal) {
335
+ let response;
336
+ try {
337
+ response = await fetch(`${this.baseUrl}/v1/jobs`, {
338
+ method: "POST",
339
+ headers: {
340
+ ...this.defaultHeaders(),
341
+ "Content-Type": "application/json"
342
+ },
343
+ body: JSON.stringify(params),
344
+ signal
345
+ });
346
+ } catch (err) {
347
+ if (err instanceof DOMException && err.name === "AbortError") {
348
+ throw err;
349
+ }
350
+ throw new OrakleConnectionError(
351
+ "Failed to connect to Orakle API",
352
+ err
353
+ );
354
+ }
355
+ if (!response.ok) {
356
+ throw await this.buildApiError(response);
357
+ }
358
+ return await response.json();
359
+ }
360
+ defaultHeaders() {
361
+ return {
362
+ "X-Orakle-API-Key": this.apiKey
363
+ };
364
+ }
365
+ parseJobEvent(eventType, data) {
366
+ try {
367
+ const parsed = JSON.parse(data);
368
+ if (typeof parsed !== "object" || parsed === null) return void 0;
369
+ switch (eventType) {
370
+ case "stage":
371
+ return { ...parsed, event_type: "stage" };
372
+ case "completed":
373
+ return { ...parsed, event_type: "completed" };
374
+ case "failed":
375
+ return { ...parsed, event_type: "failed" };
376
+ default:
377
+ return void 0;
378
+ }
379
+ } catch {
380
+ return void 0;
381
+ }
382
+ }
383
+ async buildApiError(response) {
384
+ let body;
385
+ try {
386
+ body = await response.json();
387
+ } catch {
388
+ }
389
+ const code = body?.code ?? this.httpStatusToErrorCode(response.status);
390
+ const message = body?.message ?? `API request failed with status ${response.status}`;
391
+ return new OrakleApiError(message, response.status, code);
392
+ }
393
+ httpStatusToErrorCode(status) {
394
+ switch (status) {
395
+ case 400:
396
+ return "invalid_request";
397
+ case 401:
398
+ return "auth_required";
399
+ case 429:
400
+ return "rate_limited";
401
+ case 404:
402
+ return "job_not_found";
403
+ case 503:
404
+ return "service_unavailable";
405
+ default:
406
+ return "internal_error";
407
+ }
408
+ }
409
+ normalizeError(err) {
410
+ if (err instanceof OrakleError) {
411
+ return err;
412
+ }
413
+ if (err instanceof SSEHttpError) {
414
+ let body;
415
+ try {
416
+ body = JSON.parse(err.body);
417
+ } catch {
418
+ }
419
+ const code = body?.code ?? this.httpStatusToErrorCode(err.status);
420
+ const message = body?.message ?? `API request failed with status ${err.status}`;
421
+ return new OrakleApiError(message, err.status, code);
422
+ }
423
+ if (err instanceof DOMException && err.name === "AbortError") {
424
+ return new OrakleTimeoutError(this.timeout);
425
+ }
426
+ if (err instanceof Error) {
427
+ return new OrakleConnectionError(err.message, err);
428
+ }
429
+ return new OrakleConnectionError(String(err));
430
+ }
431
+ };
432
+ // Annotate the CommonJS export names for ESM import in node:
433
+ 0 && (module.exports = {
434
+ OrakleApiError,
435
+ OrakleClient,
436
+ OrakleConnectionError,
437
+ OrakleError,
438
+ OrakleTimeoutError
439
+ });
440
+ //# sourceMappingURL=index.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/sse.ts","../src/client.ts"],"sourcesContent":["// Client\nexport { OrakleClient } from \"./client.js\";\n\n// Errors\nexport {\n OrakleError,\n OrakleApiError,\n OrakleTimeoutError,\n OrakleConnectionError,\n} from \"./errors.js\";\n\n// Types\nexport type {\n OrakleConfig,\n JobStatus,\n PipelineStage,\n ErrorCode,\n Geography,\n ProductProfile,\n DistributionComponent,\n PriceDistribution,\n ScenarioSource,\n Scenario,\n ScenarioForecast,\n NLJobRequest,\n StructuredJobRequest,\n JobRequest,\n PriceCheckResult,\n JobSubmitResponse,\n StageEvent,\n CompletedEvent,\n FailedEvent,\n JobEvent,\n PriceCheckOptions,\n ApiErrorBody,\n} from \"./types.js\";\n","import type { ErrorCode } from \"./types.js\";\n\n/**\n * Base error for all Orakle SDK errors.\n */\nexport class OrakleError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"OrakleError\";\n }\n}\n\n/**\n * HTTP API error with status code and typed error code.\n */\nexport class OrakleApiError extends OrakleError {\n readonly status: number;\n readonly code: ErrorCode;\n\n constructor(message: string, status: number, code: ErrorCode) {\n super(message);\n this.name = \"OrakleApiError\";\n this.status = status;\n this.code = code;\n }\n}\n\n/**\n * Timeout error — request or SSE stream exceeded the configured timeout.\n */\nexport class OrakleTimeoutError extends OrakleError {\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Request timed out after ${timeoutMs}ms`);\n this.name = \"OrakleTimeoutError\";\n this.timeoutMs = timeoutMs;\n }\n}\n\n/**\n * Connection error — network failure, DNS resolution failure, etc.\n */\nexport class OrakleConnectionError extends OrakleError {\n readonly cause?: unknown;\n\n constructor(message: string, cause?: unknown) {\n super(message);\n this.name = \"OrakleConnectionError\";\n this.cause = cause;\n }\n}\n","/**\n * SSE parser using fetch + ReadableStream.\n *\n * Cannot use browser EventSource — it doesn't support custom headers\n * (needed for X-Orakle-API-Key). This is the same approach used by\n * the OpenAI SDK. Works in browsers and Node.js 18+ (native fetch).\n */\n\nimport { OrakleConnectionError } from \"./errors.js\";\nimport type { SSEFrame } from \"./types.js\";\n\ninterface SSEOptions {\n url: string;\n headers: Record<string, string>;\n signal?: AbortSignal;\n lastEventId?: string;\n onRetry?: (retryMs: number) => void;\n}\n\n/**\n * Parse a `text/event-stream` response into SSE frames.\n *\n * Yields `{ event, id, data }` for each complete SSE event block.\n * Handles multi-line `data:` fields (joined with newlines).\n * Supports reconnection via `Last-Event-Id` header.\n */\nexport const createSSEStream = async function* (\n options: SSEOptions,\n): AsyncGenerator<SSEFrame> {\n const { url, headers, signal, lastEventId } = options;\n\n const requestHeaders: Record<string, string> = {\n ...headers,\n Accept: \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n };\n\n if (lastEventId) {\n requestHeaders[\"Last-Event-Id\"] = lastEventId;\n }\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"GET\",\n headers: requestHeaders,\n signal,\n });\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw err;\n }\n throw new OrakleConnectionError(\n `Failed to connect to SSE stream: ${url}`,\n err,\n );\n }\n\n if (!response.ok) {\n // Let the caller handle HTTP error responses\n const body = await response.text();\n throw new SSEHttpError(response.status, body);\n }\n\n const body = response.body;\n if (!body) {\n throw new OrakleConnectionError(\"Response body is null\");\n }\n\n yield* parseSSEBody(body, signal);\n};\n\n/**\n * Internal: parse a ReadableStream<Uint8Array> as SSE.\n */\nconst parseSSEBody = async function* (\n body: ReadableStream<Uint8Array>,\n signal?: AbortSignal,\n): AsyncGenerator<SSEFrame> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n\n let buffer = \"\";\n let currentEvent = \"\";\n let currentId = \"\";\n let currentData: string[] = [];\n\n type ReadResult = Awaited<ReturnType<typeof reader.read>>;\n\n /** Race reader.read() against the abort signal. */\n const readOrAbort = (): Promise<ReadResult> => {\n if (signal?.aborted) {\n return Promise.reject(signal.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n }\n return new Promise<ReadResult>((resolve, reject) => {\n const onAbort = () => {\n reject(signal?.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n reader.read().then(\n (result) => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve(result);\n },\n (err: unknown) => {\n signal?.removeEventListener(\"abort\", onAbort);\n reject(err);\n },\n );\n });\n };\n\n try {\n while (true) {\n if (signal?.aborted) {\n break;\n }\n\n const { done, value } = await readOrAbort();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n // Keep the last partial line in the buffer\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (line === \"\") {\n // Empty line = end of event block\n if (currentData.length > 0) {\n yield {\n event: currentEvent ?? \"message\",\n id: currentId,\n data: currentData.join(\"\\n\"),\n };\n }\n // Reset for next event\n currentEvent = \"\";\n currentId = \"\";\n currentData = [];\n continue;\n }\n\n // SSE comment — ignore (keepalive)\n if (line.startsWith(\":\")) {\n continue;\n }\n\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) {\n // Field with no value\n continue;\n }\n\n const field = line.slice(0, colonIdx);\n // Value starts after \": \" (space after colon is optional per spec)\n let value_ = line.slice(colonIdx + 1);\n if (value_.startsWith(\" \")) {\n value_ = value_.slice(1);\n }\n\n switch (field) {\n case \"event\":\n currentEvent = value_;\n break;\n case \"id\":\n currentId = value_;\n break;\n case \"data\":\n currentData.push(value_);\n break;\n case \"retry\":\n // Optional retry field — notify caller\n break;\n }\n }\n }\n\n // Flush any remaining event in buffer\n if (buffer !== \"\") {\n const lines = buffer.split(\"\\n\");\n for (const line of lines) {\n if (line === \"\" && currentData.length > 0) {\n yield {\n event: currentEvent ?? \"message\",\n id: currentId,\n data: currentData.join(\"\\n\"),\n };\n currentEvent = \"\";\n currentId = \"\";\n currentData = [];\n continue;\n }\n if (line.startsWith(\":\") || line === \"\") continue;\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const field = line.slice(0, colonIdx);\n let value_ = line.slice(colonIdx + 1);\n if (value_.startsWith(\" \")) value_ = value_.slice(1);\n switch (field) {\n case \"event\":\n currentEvent = value_;\n break;\n case \"id\":\n currentId = value_;\n break;\n case \"data\":\n currentData.push(value_);\n break;\n }\n }\n if (currentData.length > 0) {\n yield {\n event: currentEvent ?? \"message\",\n id: currentId,\n data: currentData.join(\"\\n\"),\n };\n }\n }\n } finally {\n reader.releaseLock();\n }\n};\n\n/**\n * Internal error used to communicate HTTP status from the SSE fetch.\n * The client layer catches this and maps it to OrakleApiError.\n */\nexport class SSEHttpError extends Error {\n readonly status: number;\n readonly body: string;\n\n constructor(status: number, body: string) {\n super(`SSE request failed with status ${status}`);\n this.name = \"SSEHttpError\";\n this.status = status;\n this.body = body;\n }\n}\n","/**\n * OrakleClient — main entry point for the Orakle TypeScript SDK.\n *\n * Usage:\n * const client = new OrakleClient({ apiKey: \"...\" });\n * const result = await client.priceCheck({ query: \"iPhone 15 Pro 256GB\" });\n */\n\nimport {\n OrakleApiError,\n OrakleConnectionError,\n OrakleError,\n OrakleTimeoutError,\n} from \"./errors.js\";\nimport { SSEHttpError, createSSEStream } from \"./sse.js\";\nimport type {\n ApiErrorBody,\n CompletedEvent,\n ErrorCode,\n FailedEvent,\n JobEvent,\n JobRequest,\n JobSubmitResponse,\n OrakleConfig,\n PriceCheckOptions,\n PriceCheckResult,\n StageEvent,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.orakle.xyz\";\nconst DEFAULT_TIMEOUT_MS = 180_000;\n\nexport class OrakleClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(config: OrakleConfig) {\n this.apiKey = config.apiKey;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;\n }\n\n /**\n * Submit a pricing job and wait for the result.\n *\n * Internally: POST /v1/jobs -> open SSE stream -> resolve on completed, reject on failed.\n */\n async priceCheck(\n params: JobRequest,\n options?: PriceCheckOptions,\n ): Promise<PriceCheckResult> {\n const timeoutMs = options?.timeout ?? this.timeout;\n const controller = new AbortController();\n\n // Link external signal to our internal controller\n const externalSignal = options?.signal;\n const onExternalAbort = () => controller.abort();\n externalSignal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n\n const timer = setTimeout(() => {\n controller.abort(new OrakleTimeoutError(timeoutMs));\n }, timeoutMs);\n\n try {\n const job = await this.submitJob(params, controller.signal);\n const streamUrl = `${this.baseUrl}/v1/jobs/${job.job_id}/stream`;\n\n const stream = createSSEStream({\n url: streamUrl,\n headers: this.defaultHeaders(),\n signal: controller.signal,\n });\n\n for await (const frame of stream) {\n const event = this.parseJobEvent(frame.event, frame.data);\n if (!event) continue;\n\n switch (event.event_type) {\n case \"stage\":\n options?.onProgress?.(event);\n break;\n case \"completed\":\n return event.result;\n case \"failed\":\n throw new OrakleError(event.error);\n }\n }\n\n // Stream ended without a terminal event\n throw new OrakleError(\"SSE stream ended without a completed or failed event\");\n } catch (err: unknown) {\n throw this.normalizeError(err);\n } finally {\n clearTimeout(timer);\n externalSignal?.removeEventListener(\"abort\", onExternalAbort);\n }\n }\n\n /**\n * Submit a pricing job and return an async iterable of all events.\n *\n * Yields StageEvent and CompletedEvent/FailedEvent as they arrive.\n */\n async *priceCheckStream(\n params: JobRequest,\n options?: PriceCheckOptions,\n ): AsyncGenerator<JobEvent> {\n const timeoutMs = options?.timeout ?? this.timeout;\n const controller = new AbortController();\n\n const externalSignal = options?.signal;\n const onExternalAbort = () => controller.abort();\n externalSignal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n\n const timer = setTimeout(() => {\n controller.abort(new OrakleTimeoutError(timeoutMs));\n }, timeoutMs);\n\n try {\n const job = await this.submitJob(params, controller.signal);\n const streamUrl = `${this.baseUrl}/v1/jobs/${job.job_id}/stream`;\n\n const stream = createSSEStream({\n url: streamUrl,\n headers: this.defaultHeaders(),\n signal: controller.signal,\n });\n\n for await (const frame of stream) {\n const event = this.parseJobEvent(frame.event, frame.data);\n if (!event) continue;\n\n if (event.event_type === \"stage\") {\n options?.onProgress?.(event);\n }\n\n yield event;\n\n // Terminal events end the stream\n if (event.event_type === \"completed\" || event.event_type === \"failed\") {\n return;\n }\n }\n } catch (err: unknown) {\n throw this.normalizeError(err);\n } finally {\n clearTimeout(timer);\n externalSignal?.removeEventListener(\"abort\", onExternalAbort);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private async submitJob(\n params: JobRequest,\n signal: AbortSignal,\n ): Promise<JobSubmitResponse> {\n let response: Response;\n try {\n response = await fetch(`${this.baseUrl}/v1/jobs`, {\n method: \"POST\",\n headers: {\n ...this.defaultHeaders(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(params),\n signal,\n });\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw err;\n }\n throw new OrakleConnectionError(\n \"Failed to connect to Orakle API\",\n err,\n );\n }\n\n if (!response.ok) {\n throw await this.buildApiError(response);\n }\n\n return (await response.json()) as JobSubmitResponse;\n }\n\n private defaultHeaders(): Record<string, string> {\n return {\n \"X-Orakle-API-Key\": this.apiKey,\n };\n }\n\n private parseJobEvent(eventType: string, data: string): JobEvent | undefined {\n try {\n const parsed: unknown = JSON.parse(data);\n if (typeof parsed !== \"object\" || parsed === null) return undefined;\n\n switch (eventType) {\n case \"stage\":\n return { ...parsed, event_type: \"stage\" } as StageEvent;\n case \"completed\":\n return { ...parsed, event_type: \"completed\" } as CompletedEvent;\n case \"failed\":\n return { ...parsed, event_type: \"failed\" } as FailedEvent;\n default:\n return undefined;\n }\n } catch {\n return undefined;\n }\n }\n\n private async buildApiError(response: Response): Promise<OrakleApiError> {\n let body: ApiErrorBody | undefined;\n try {\n body = (await response.json()) as ApiErrorBody;\n } catch {\n // Response body is not JSON\n }\n\n const code: ErrorCode = body?.code ?? this.httpStatusToErrorCode(response.status);\n const message = body?.message ?? `API request failed with status ${response.status}`;\n\n return new OrakleApiError(message, response.status, code);\n }\n\n private httpStatusToErrorCode(status: number): ErrorCode {\n switch (status) {\n case 400:\n return \"invalid_request\";\n case 401:\n return \"auth_required\";\n case 429:\n return \"rate_limited\";\n case 404:\n return \"job_not_found\";\n case 503:\n return \"service_unavailable\";\n default:\n return \"internal_error\";\n }\n }\n\n private normalizeError(err: unknown): Error {\n if (err instanceof OrakleError) {\n return err;\n }\n\n if (err instanceof SSEHttpError) {\n let body: ApiErrorBody | undefined;\n try {\n body = JSON.parse(err.body) as ApiErrorBody;\n } catch {\n // Not JSON\n }\n const code = body?.code ?? this.httpStatusToErrorCode(err.status);\n const message = body?.message ?? `API request failed with status ${err.status}`;\n return new OrakleApiError(message, err.status, code);\n }\n\n // AbortError from timeout\n if (err instanceof DOMException && err.name === \"AbortError\") {\n // Check if our timeout caused the abort\n return new OrakleTimeoutError(this.timeout);\n }\n\n if (err instanceof Error) {\n return new OrakleConnectionError(err.message, err);\n }\n\n return new OrakleConnectionError(String(err));\n }\n}\n"],"mappings":"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","names":["body"]}
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Type definitions mirroring the Orakle backend models.
3
+ */
4
+ interface OrakleConfig {
5
+ apiKey: string;
6
+ baseUrl?: string;
7
+ timeout?: number;
8
+ }
9
+ type JobStatus = "pending" | "processing" | "completed" | "failed";
10
+ type PipelineStage = "analyzing" | "price_lookup" | "planning" | "dispatching" | "fusing";
11
+ type ErrorCode = "invalid_request" | "auth_required" | "rate_limited" | "job_not_found" | "insufficient_data" | "internal_error" | "service_unavailable";
12
+ type Geography = "US" | "GB" | "CA" | "AU" | "AE";
13
+ interface ProductProfile {
14
+ category: string;
15
+ make: string;
16
+ model: string;
17
+ storage?: string | null;
18
+ condition?: string | null;
19
+ carrier?: string | null;
20
+ carrier_lock?: string | null;
21
+ color?: string | null;
22
+ geography?: Geography;
23
+ region?: string | null;
24
+ key_value_drivers?: string[];
25
+ missing_attributes?: string[];
26
+ }
27
+ interface DistributionComponent {
28
+ weight: number;
29
+ mean: number;
30
+ std_dev: number;
31
+ label?: string | null;
32
+ }
33
+ interface PriceDistribution {
34
+ quantiles: Record<string, number>;
35
+ components?: DistributionComponent[] | null;
36
+ currency?: string;
37
+ }
38
+ interface ScenarioSource {
39
+ platform: string;
40
+ contract_id?: string | null;
41
+ url?: string | null;
42
+ }
43
+ interface Scenario {
44
+ name: string;
45
+ description: string;
46
+ probability: number;
47
+ price_impact: number;
48
+ sources?: ScenarioSource[];
49
+ }
50
+ interface ScenarioForecast {
51
+ scenarios: Scenario[];
52
+ base_price: number;
53
+ weighted_expected_price: number;
54
+ }
55
+ interface NLJobRequest {
56
+ request_type: "nl";
57
+ query: string;
58
+ geography?: Geography;
59
+ webhook_url?: string | null;
60
+ }
61
+ interface StructuredJobRequest {
62
+ request_type: "structured";
63
+ product: ProductProfile;
64
+ geography?: Geography;
65
+ webhook_url?: string | null;
66
+ }
67
+ type JobRequest = NLJobRequest | StructuredJobRequest;
68
+ interface PriceCheckResult {
69
+ product_profile: ProductProfile;
70
+ fused_distribution: PriceDistribution;
71
+ scenario_forecast?: ScenarioForecast | null;
72
+ }
73
+ interface JobSubmitResponse {
74
+ job_id: string;
75
+ status: JobStatus;
76
+ poll_url: string;
77
+ estimated_seconds: number;
78
+ }
79
+ interface StageEvent {
80
+ event_type: "stage";
81
+ job_id: string;
82
+ stage: PipelineStage;
83
+ message: string;
84
+ timestamp: string;
85
+ }
86
+ interface CompletedEvent {
87
+ event_type: "completed";
88
+ job_id: string;
89
+ result: PriceCheckResult;
90
+ message?: string;
91
+ timestamp: string;
92
+ }
93
+ interface FailedEvent {
94
+ event_type: "failed";
95
+ job_id: string;
96
+ error: string;
97
+ message?: string;
98
+ timestamp: string;
99
+ }
100
+ type JobEvent = StageEvent | CompletedEvent | FailedEvent;
101
+ interface PriceCheckOptions {
102
+ signal?: AbortSignal;
103
+ timeout?: number;
104
+ onProgress?: (event: StageEvent) => void;
105
+ }
106
+ interface ApiErrorBody {
107
+ code: ErrorCode;
108
+ message: string;
109
+ status: number;
110
+ }
111
+
112
+ /**
113
+ * OrakleClient — main entry point for the Orakle TypeScript SDK.
114
+ *
115
+ * Usage:
116
+ * const client = new OrakleClient({ apiKey: "..." });
117
+ * const result = await client.priceCheck({ query: "iPhone 15 Pro 256GB" });
118
+ */
119
+
120
+ declare class OrakleClient {
121
+ private readonly apiKey;
122
+ private readonly baseUrl;
123
+ private readonly timeout;
124
+ constructor(config: OrakleConfig);
125
+ /**
126
+ * Submit a pricing job and wait for the result.
127
+ *
128
+ * Internally: POST /v1/jobs -> open SSE stream -> resolve on completed, reject on failed.
129
+ */
130
+ priceCheck(params: JobRequest, options?: PriceCheckOptions): Promise<PriceCheckResult>;
131
+ /**
132
+ * Submit a pricing job and return an async iterable of all events.
133
+ *
134
+ * Yields StageEvent and CompletedEvent/FailedEvent as they arrive.
135
+ */
136
+ priceCheckStream(params: JobRequest, options?: PriceCheckOptions): AsyncGenerator<JobEvent>;
137
+ private submitJob;
138
+ private defaultHeaders;
139
+ private parseJobEvent;
140
+ private buildApiError;
141
+ private httpStatusToErrorCode;
142
+ private normalizeError;
143
+ }
144
+
145
+ /**
146
+ * Base error for all Orakle SDK errors.
147
+ */
148
+ declare class OrakleError extends Error {
149
+ constructor(message: string);
150
+ }
151
+ /**
152
+ * HTTP API error with status code and typed error code.
153
+ */
154
+ declare class OrakleApiError extends OrakleError {
155
+ readonly status: number;
156
+ readonly code: ErrorCode;
157
+ constructor(message: string, status: number, code: ErrorCode);
158
+ }
159
+ /**
160
+ * Timeout error — request or SSE stream exceeded the configured timeout.
161
+ */
162
+ declare class OrakleTimeoutError extends OrakleError {
163
+ readonly timeoutMs: number;
164
+ constructor(timeoutMs: number);
165
+ }
166
+ /**
167
+ * Connection error — network failure, DNS resolution failure, etc.
168
+ */
169
+ declare class OrakleConnectionError extends OrakleError {
170
+ readonly cause?: unknown;
171
+ constructor(message: string, cause?: unknown);
172
+ }
173
+
174
+ export { type ApiErrorBody, type CompletedEvent, type DistributionComponent, type ErrorCode, type FailedEvent, type Geography, type JobEvent, type JobRequest, type JobStatus, type JobSubmitResponse, type NLJobRequest, OrakleApiError, OrakleClient, type OrakleConfig, OrakleConnectionError, OrakleError, OrakleTimeoutError, type PipelineStage, type PriceCheckOptions, type PriceCheckResult, type PriceDistribution, type ProductProfile, type Scenario, type ScenarioForecast, type ScenarioSource, type StageEvent, type StructuredJobRequest };
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Type definitions mirroring the Orakle backend models.
3
+ */
4
+ interface OrakleConfig {
5
+ apiKey: string;
6
+ baseUrl?: string;
7
+ timeout?: number;
8
+ }
9
+ type JobStatus = "pending" | "processing" | "completed" | "failed";
10
+ type PipelineStage = "analyzing" | "price_lookup" | "planning" | "dispatching" | "fusing";
11
+ type ErrorCode = "invalid_request" | "auth_required" | "rate_limited" | "job_not_found" | "insufficient_data" | "internal_error" | "service_unavailable";
12
+ type Geography = "US" | "GB" | "CA" | "AU" | "AE";
13
+ interface ProductProfile {
14
+ category: string;
15
+ make: string;
16
+ model: string;
17
+ storage?: string | null;
18
+ condition?: string | null;
19
+ carrier?: string | null;
20
+ carrier_lock?: string | null;
21
+ color?: string | null;
22
+ geography?: Geography;
23
+ region?: string | null;
24
+ key_value_drivers?: string[];
25
+ missing_attributes?: string[];
26
+ }
27
+ interface DistributionComponent {
28
+ weight: number;
29
+ mean: number;
30
+ std_dev: number;
31
+ label?: string | null;
32
+ }
33
+ interface PriceDistribution {
34
+ quantiles: Record<string, number>;
35
+ components?: DistributionComponent[] | null;
36
+ currency?: string;
37
+ }
38
+ interface ScenarioSource {
39
+ platform: string;
40
+ contract_id?: string | null;
41
+ url?: string | null;
42
+ }
43
+ interface Scenario {
44
+ name: string;
45
+ description: string;
46
+ probability: number;
47
+ price_impact: number;
48
+ sources?: ScenarioSource[];
49
+ }
50
+ interface ScenarioForecast {
51
+ scenarios: Scenario[];
52
+ base_price: number;
53
+ weighted_expected_price: number;
54
+ }
55
+ interface NLJobRequest {
56
+ request_type: "nl";
57
+ query: string;
58
+ geography?: Geography;
59
+ webhook_url?: string | null;
60
+ }
61
+ interface StructuredJobRequest {
62
+ request_type: "structured";
63
+ product: ProductProfile;
64
+ geography?: Geography;
65
+ webhook_url?: string | null;
66
+ }
67
+ type JobRequest = NLJobRequest | StructuredJobRequest;
68
+ interface PriceCheckResult {
69
+ product_profile: ProductProfile;
70
+ fused_distribution: PriceDistribution;
71
+ scenario_forecast?: ScenarioForecast | null;
72
+ }
73
+ interface JobSubmitResponse {
74
+ job_id: string;
75
+ status: JobStatus;
76
+ poll_url: string;
77
+ estimated_seconds: number;
78
+ }
79
+ interface StageEvent {
80
+ event_type: "stage";
81
+ job_id: string;
82
+ stage: PipelineStage;
83
+ message: string;
84
+ timestamp: string;
85
+ }
86
+ interface CompletedEvent {
87
+ event_type: "completed";
88
+ job_id: string;
89
+ result: PriceCheckResult;
90
+ message?: string;
91
+ timestamp: string;
92
+ }
93
+ interface FailedEvent {
94
+ event_type: "failed";
95
+ job_id: string;
96
+ error: string;
97
+ message?: string;
98
+ timestamp: string;
99
+ }
100
+ type JobEvent = StageEvent | CompletedEvent | FailedEvent;
101
+ interface PriceCheckOptions {
102
+ signal?: AbortSignal;
103
+ timeout?: number;
104
+ onProgress?: (event: StageEvent) => void;
105
+ }
106
+ interface ApiErrorBody {
107
+ code: ErrorCode;
108
+ message: string;
109
+ status: number;
110
+ }
111
+
112
+ /**
113
+ * OrakleClient — main entry point for the Orakle TypeScript SDK.
114
+ *
115
+ * Usage:
116
+ * const client = new OrakleClient({ apiKey: "..." });
117
+ * const result = await client.priceCheck({ query: "iPhone 15 Pro 256GB" });
118
+ */
119
+
120
+ declare class OrakleClient {
121
+ private readonly apiKey;
122
+ private readonly baseUrl;
123
+ private readonly timeout;
124
+ constructor(config: OrakleConfig);
125
+ /**
126
+ * Submit a pricing job and wait for the result.
127
+ *
128
+ * Internally: POST /v1/jobs -> open SSE stream -> resolve on completed, reject on failed.
129
+ */
130
+ priceCheck(params: JobRequest, options?: PriceCheckOptions): Promise<PriceCheckResult>;
131
+ /**
132
+ * Submit a pricing job and return an async iterable of all events.
133
+ *
134
+ * Yields StageEvent and CompletedEvent/FailedEvent as they arrive.
135
+ */
136
+ priceCheckStream(params: JobRequest, options?: PriceCheckOptions): AsyncGenerator<JobEvent>;
137
+ private submitJob;
138
+ private defaultHeaders;
139
+ private parseJobEvent;
140
+ private buildApiError;
141
+ private httpStatusToErrorCode;
142
+ private normalizeError;
143
+ }
144
+
145
+ /**
146
+ * Base error for all Orakle SDK errors.
147
+ */
148
+ declare class OrakleError extends Error {
149
+ constructor(message: string);
150
+ }
151
+ /**
152
+ * HTTP API error with status code and typed error code.
153
+ */
154
+ declare class OrakleApiError extends OrakleError {
155
+ readonly status: number;
156
+ readonly code: ErrorCode;
157
+ constructor(message: string, status: number, code: ErrorCode);
158
+ }
159
+ /**
160
+ * Timeout error — request or SSE stream exceeded the configured timeout.
161
+ */
162
+ declare class OrakleTimeoutError extends OrakleError {
163
+ readonly timeoutMs: number;
164
+ constructor(timeoutMs: number);
165
+ }
166
+ /**
167
+ * Connection error — network failure, DNS resolution failure, etc.
168
+ */
169
+ declare class OrakleConnectionError extends OrakleError {
170
+ readonly cause?: unknown;
171
+ constructor(message: string, cause?: unknown);
172
+ }
173
+
174
+ export { type ApiErrorBody, type CompletedEvent, type DistributionComponent, type ErrorCode, type FailedEvent, type Geography, type JobEvent, type JobRequest, type JobStatus, type JobSubmitResponse, type NLJobRequest, OrakleApiError, OrakleClient, type OrakleConfig, OrakleConnectionError, OrakleError, OrakleTimeoutError, type PipelineStage, type PriceCheckOptions, type PriceCheckResult, type PriceDistribution, type ProductProfile, type Scenario, type ScenarioForecast, type ScenarioSource, type StageEvent, type StructuredJobRequest };
package/dist/index.js ADDED
@@ -0,0 +1,409 @@
1
+ // src/errors.ts
2
+ var OrakleError = class extends Error {
3
+ constructor(message) {
4
+ super(message);
5
+ this.name = "OrakleError";
6
+ }
7
+ };
8
+ var OrakleApiError = class extends OrakleError {
9
+ status;
10
+ code;
11
+ constructor(message, status, code) {
12
+ super(message);
13
+ this.name = "OrakleApiError";
14
+ this.status = status;
15
+ this.code = code;
16
+ }
17
+ };
18
+ var OrakleTimeoutError = class extends OrakleError {
19
+ timeoutMs;
20
+ constructor(timeoutMs) {
21
+ super(`Request timed out after ${timeoutMs}ms`);
22
+ this.name = "OrakleTimeoutError";
23
+ this.timeoutMs = timeoutMs;
24
+ }
25
+ };
26
+ var OrakleConnectionError = class extends OrakleError {
27
+ cause;
28
+ constructor(message, cause) {
29
+ super(message);
30
+ this.name = "OrakleConnectionError";
31
+ this.cause = cause;
32
+ }
33
+ };
34
+
35
+ // src/sse.ts
36
+ var createSSEStream = async function* (options) {
37
+ const { url, headers, signal, lastEventId } = options;
38
+ const requestHeaders = {
39
+ ...headers,
40
+ Accept: "text/event-stream",
41
+ "Cache-Control": "no-cache"
42
+ };
43
+ if (lastEventId) {
44
+ requestHeaders["Last-Event-Id"] = lastEventId;
45
+ }
46
+ let response;
47
+ try {
48
+ response = await fetch(url, {
49
+ method: "GET",
50
+ headers: requestHeaders,
51
+ signal
52
+ });
53
+ } catch (err) {
54
+ if (err instanceof DOMException && err.name === "AbortError") {
55
+ throw err;
56
+ }
57
+ throw new OrakleConnectionError(
58
+ `Failed to connect to SSE stream: ${url}`,
59
+ err
60
+ );
61
+ }
62
+ if (!response.ok) {
63
+ const body2 = await response.text();
64
+ throw new SSEHttpError(response.status, body2);
65
+ }
66
+ const body = response.body;
67
+ if (!body) {
68
+ throw new OrakleConnectionError("Response body is null");
69
+ }
70
+ yield* parseSSEBody(body, signal);
71
+ };
72
+ var parseSSEBody = async function* (body, signal) {
73
+ const reader = body.getReader();
74
+ const decoder = new TextDecoder();
75
+ let buffer = "";
76
+ let currentEvent = "";
77
+ let currentId = "";
78
+ let currentData = [];
79
+ const readOrAbort = () => {
80
+ if (signal?.aborted) {
81
+ return Promise.reject(signal.reason ?? new DOMException("Aborted", "AbortError"));
82
+ }
83
+ return new Promise((resolve, reject) => {
84
+ const onAbort = () => {
85
+ reject(signal?.reason ?? new DOMException("Aborted", "AbortError"));
86
+ };
87
+ signal?.addEventListener("abort", onAbort, { once: true });
88
+ reader.read().then(
89
+ (result) => {
90
+ signal?.removeEventListener("abort", onAbort);
91
+ resolve(result);
92
+ },
93
+ (err) => {
94
+ signal?.removeEventListener("abort", onAbort);
95
+ reject(err);
96
+ }
97
+ );
98
+ });
99
+ };
100
+ try {
101
+ while (true) {
102
+ if (signal?.aborted) {
103
+ break;
104
+ }
105
+ const { done, value } = await readOrAbort();
106
+ if (done) break;
107
+ buffer += decoder.decode(value, { stream: true });
108
+ const lines = buffer.split("\n");
109
+ buffer = lines.pop() ?? "";
110
+ for (const line of lines) {
111
+ if (line === "") {
112
+ if (currentData.length > 0) {
113
+ yield {
114
+ event: currentEvent ?? "message",
115
+ id: currentId,
116
+ data: currentData.join("\n")
117
+ };
118
+ }
119
+ currentEvent = "";
120
+ currentId = "";
121
+ currentData = [];
122
+ continue;
123
+ }
124
+ if (line.startsWith(":")) {
125
+ continue;
126
+ }
127
+ const colonIdx = line.indexOf(":");
128
+ if (colonIdx === -1) {
129
+ continue;
130
+ }
131
+ const field = line.slice(0, colonIdx);
132
+ let value_ = line.slice(colonIdx + 1);
133
+ if (value_.startsWith(" ")) {
134
+ value_ = value_.slice(1);
135
+ }
136
+ switch (field) {
137
+ case "event":
138
+ currentEvent = value_;
139
+ break;
140
+ case "id":
141
+ currentId = value_;
142
+ break;
143
+ case "data":
144
+ currentData.push(value_);
145
+ break;
146
+ case "retry":
147
+ break;
148
+ }
149
+ }
150
+ }
151
+ if (buffer !== "") {
152
+ const lines = buffer.split("\n");
153
+ for (const line of lines) {
154
+ if (line === "" && currentData.length > 0) {
155
+ yield {
156
+ event: currentEvent ?? "message",
157
+ id: currentId,
158
+ data: currentData.join("\n")
159
+ };
160
+ currentEvent = "";
161
+ currentId = "";
162
+ currentData = [];
163
+ continue;
164
+ }
165
+ if (line.startsWith(":") || line === "") continue;
166
+ const colonIdx = line.indexOf(":");
167
+ if (colonIdx === -1) continue;
168
+ const field = line.slice(0, colonIdx);
169
+ let value_ = line.slice(colonIdx + 1);
170
+ if (value_.startsWith(" ")) value_ = value_.slice(1);
171
+ switch (field) {
172
+ case "event":
173
+ currentEvent = value_;
174
+ break;
175
+ case "id":
176
+ currentId = value_;
177
+ break;
178
+ case "data":
179
+ currentData.push(value_);
180
+ break;
181
+ }
182
+ }
183
+ if (currentData.length > 0) {
184
+ yield {
185
+ event: currentEvent ?? "message",
186
+ id: currentId,
187
+ data: currentData.join("\n")
188
+ };
189
+ }
190
+ }
191
+ } finally {
192
+ reader.releaseLock();
193
+ }
194
+ };
195
+ var SSEHttpError = class extends Error {
196
+ status;
197
+ body;
198
+ constructor(status, body) {
199
+ super(`SSE request failed with status ${status}`);
200
+ this.name = "SSEHttpError";
201
+ this.status = status;
202
+ this.body = body;
203
+ }
204
+ };
205
+
206
+ // src/client.ts
207
+ var DEFAULT_BASE_URL = "https://api.orakle.xyz";
208
+ var DEFAULT_TIMEOUT_MS = 18e4;
209
+ var OrakleClient = class {
210
+ apiKey;
211
+ baseUrl;
212
+ timeout;
213
+ constructor(config) {
214
+ this.apiKey = config.apiKey;
215
+ this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
216
+ this.timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
217
+ }
218
+ /**
219
+ * Submit a pricing job and wait for the result.
220
+ *
221
+ * Internally: POST /v1/jobs -> open SSE stream -> resolve on completed, reject on failed.
222
+ */
223
+ async priceCheck(params, options) {
224
+ const timeoutMs = options?.timeout ?? this.timeout;
225
+ const controller = new AbortController();
226
+ const externalSignal = options?.signal;
227
+ const onExternalAbort = () => controller.abort();
228
+ externalSignal?.addEventListener("abort", onExternalAbort, { once: true });
229
+ const timer = setTimeout(() => {
230
+ controller.abort(new OrakleTimeoutError(timeoutMs));
231
+ }, timeoutMs);
232
+ try {
233
+ const job = await this.submitJob(params, controller.signal);
234
+ const streamUrl = `${this.baseUrl}/v1/jobs/${job.job_id}/stream`;
235
+ const stream = createSSEStream({
236
+ url: streamUrl,
237
+ headers: this.defaultHeaders(),
238
+ signal: controller.signal
239
+ });
240
+ for await (const frame of stream) {
241
+ const event = this.parseJobEvent(frame.event, frame.data);
242
+ if (!event) continue;
243
+ switch (event.event_type) {
244
+ case "stage":
245
+ options?.onProgress?.(event);
246
+ break;
247
+ case "completed":
248
+ return event.result;
249
+ case "failed":
250
+ throw new OrakleError(event.error);
251
+ }
252
+ }
253
+ throw new OrakleError("SSE stream ended without a completed or failed event");
254
+ } catch (err) {
255
+ throw this.normalizeError(err);
256
+ } finally {
257
+ clearTimeout(timer);
258
+ externalSignal?.removeEventListener("abort", onExternalAbort);
259
+ }
260
+ }
261
+ /**
262
+ * Submit a pricing job and return an async iterable of all events.
263
+ *
264
+ * Yields StageEvent and CompletedEvent/FailedEvent as they arrive.
265
+ */
266
+ async *priceCheckStream(params, options) {
267
+ const timeoutMs = options?.timeout ?? this.timeout;
268
+ const controller = new AbortController();
269
+ const externalSignal = options?.signal;
270
+ const onExternalAbort = () => controller.abort();
271
+ externalSignal?.addEventListener("abort", onExternalAbort, { once: true });
272
+ const timer = setTimeout(() => {
273
+ controller.abort(new OrakleTimeoutError(timeoutMs));
274
+ }, timeoutMs);
275
+ try {
276
+ const job = await this.submitJob(params, controller.signal);
277
+ const streamUrl = `${this.baseUrl}/v1/jobs/${job.job_id}/stream`;
278
+ const stream = createSSEStream({
279
+ url: streamUrl,
280
+ headers: this.defaultHeaders(),
281
+ signal: controller.signal
282
+ });
283
+ for await (const frame of stream) {
284
+ const event = this.parseJobEvent(frame.event, frame.data);
285
+ if (!event) continue;
286
+ if (event.event_type === "stage") {
287
+ options?.onProgress?.(event);
288
+ }
289
+ yield event;
290
+ if (event.event_type === "completed" || event.event_type === "failed") {
291
+ return;
292
+ }
293
+ }
294
+ } catch (err) {
295
+ throw this.normalizeError(err);
296
+ } finally {
297
+ clearTimeout(timer);
298
+ externalSignal?.removeEventListener("abort", onExternalAbort);
299
+ }
300
+ }
301
+ // ---------------------------------------------------------------------------
302
+ // Private helpers
303
+ // ---------------------------------------------------------------------------
304
+ async submitJob(params, signal) {
305
+ let response;
306
+ try {
307
+ response = await fetch(`${this.baseUrl}/v1/jobs`, {
308
+ method: "POST",
309
+ headers: {
310
+ ...this.defaultHeaders(),
311
+ "Content-Type": "application/json"
312
+ },
313
+ body: JSON.stringify(params),
314
+ signal
315
+ });
316
+ } catch (err) {
317
+ if (err instanceof DOMException && err.name === "AbortError") {
318
+ throw err;
319
+ }
320
+ throw new OrakleConnectionError(
321
+ "Failed to connect to Orakle API",
322
+ err
323
+ );
324
+ }
325
+ if (!response.ok) {
326
+ throw await this.buildApiError(response);
327
+ }
328
+ return await response.json();
329
+ }
330
+ defaultHeaders() {
331
+ return {
332
+ "X-Orakle-API-Key": this.apiKey
333
+ };
334
+ }
335
+ parseJobEvent(eventType, data) {
336
+ try {
337
+ const parsed = JSON.parse(data);
338
+ if (typeof parsed !== "object" || parsed === null) return void 0;
339
+ switch (eventType) {
340
+ case "stage":
341
+ return { ...parsed, event_type: "stage" };
342
+ case "completed":
343
+ return { ...parsed, event_type: "completed" };
344
+ case "failed":
345
+ return { ...parsed, event_type: "failed" };
346
+ default:
347
+ return void 0;
348
+ }
349
+ } catch {
350
+ return void 0;
351
+ }
352
+ }
353
+ async buildApiError(response) {
354
+ let body;
355
+ try {
356
+ body = await response.json();
357
+ } catch {
358
+ }
359
+ const code = body?.code ?? this.httpStatusToErrorCode(response.status);
360
+ const message = body?.message ?? `API request failed with status ${response.status}`;
361
+ return new OrakleApiError(message, response.status, code);
362
+ }
363
+ httpStatusToErrorCode(status) {
364
+ switch (status) {
365
+ case 400:
366
+ return "invalid_request";
367
+ case 401:
368
+ return "auth_required";
369
+ case 429:
370
+ return "rate_limited";
371
+ case 404:
372
+ return "job_not_found";
373
+ case 503:
374
+ return "service_unavailable";
375
+ default:
376
+ return "internal_error";
377
+ }
378
+ }
379
+ normalizeError(err) {
380
+ if (err instanceof OrakleError) {
381
+ return err;
382
+ }
383
+ if (err instanceof SSEHttpError) {
384
+ let body;
385
+ try {
386
+ body = JSON.parse(err.body);
387
+ } catch {
388
+ }
389
+ const code = body?.code ?? this.httpStatusToErrorCode(err.status);
390
+ const message = body?.message ?? `API request failed with status ${err.status}`;
391
+ return new OrakleApiError(message, err.status, code);
392
+ }
393
+ if (err instanceof DOMException && err.name === "AbortError") {
394
+ return new OrakleTimeoutError(this.timeout);
395
+ }
396
+ if (err instanceof Error) {
397
+ return new OrakleConnectionError(err.message, err);
398
+ }
399
+ return new OrakleConnectionError(String(err));
400
+ }
401
+ };
402
+ export {
403
+ OrakleApiError,
404
+ OrakleClient,
405
+ OrakleConnectionError,
406
+ OrakleError,
407
+ OrakleTimeoutError
408
+ };
409
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/errors.ts","../src/sse.ts","../src/client.ts"],"sourcesContent":["import type { ErrorCode } from \"./types.js\";\n\n/**\n * Base error for all Orakle SDK errors.\n */\nexport class OrakleError extends Error {\n constructor(message: string) {\n super(message);\n this.name = \"OrakleError\";\n }\n}\n\n/**\n * HTTP API error with status code and typed error code.\n */\nexport class OrakleApiError extends OrakleError {\n readonly status: number;\n readonly code: ErrorCode;\n\n constructor(message: string, status: number, code: ErrorCode) {\n super(message);\n this.name = \"OrakleApiError\";\n this.status = status;\n this.code = code;\n }\n}\n\n/**\n * Timeout error — request or SSE stream exceeded the configured timeout.\n */\nexport class OrakleTimeoutError extends OrakleError {\n readonly timeoutMs: number;\n\n constructor(timeoutMs: number) {\n super(`Request timed out after ${timeoutMs}ms`);\n this.name = \"OrakleTimeoutError\";\n this.timeoutMs = timeoutMs;\n }\n}\n\n/**\n * Connection error — network failure, DNS resolution failure, etc.\n */\nexport class OrakleConnectionError extends OrakleError {\n readonly cause?: unknown;\n\n constructor(message: string, cause?: unknown) {\n super(message);\n this.name = \"OrakleConnectionError\";\n this.cause = cause;\n }\n}\n","/**\n * SSE parser using fetch + ReadableStream.\n *\n * Cannot use browser EventSource — it doesn't support custom headers\n * (needed for X-Orakle-API-Key). This is the same approach used by\n * the OpenAI SDK. Works in browsers and Node.js 18+ (native fetch).\n */\n\nimport { OrakleConnectionError } from \"./errors.js\";\nimport type { SSEFrame } from \"./types.js\";\n\ninterface SSEOptions {\n url: string;\n headers: Record<string, string>;\n signal?: AbortSignal;\n lastEventId?: string;\n onRetry?: (retryMs: number) => void;\n}\n\n/**\n * Parse a `text/event-stream` response into SSE frames.\n *\n * Yields `{ event, id, data }` for each complete SSE event block.\n * Handles multi-line `data:` fields (joined with newlines).\n * Supports reconnection via `Last-Event-Id` header.\n */\nexport const createSSEStream = async function* (\n options: SSEOptions,\n): AsyncGenerator<SSEFrame> {\n const { url, headers, signal, lastEventId } = options;\n\n const requestHeaders: Record<string, string> = {\n ...headers,\n Accept: \"text/event-stream\",\n \"Cache-Control\": \"no-cache\",\n };\n\n if (lastEventId) {\n requestHeaders[\"Last-Event-Id\"] = lastEventId;\n }\n\n let response: Response;\n try {\n response = await fetch(url, {\n method: \"GET\",\n headers: requestHeaders,\n signal,\n });\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw err;\n }\n throw new OrakleConnectionError(\n `Failed to connect to SSE stream: ${url}`,\n err,\n );\n }\n\n if (!response.ok) {\n // Let the caller handle HTTP error responses\n const body = await response.text();\n throw new SSEHttpError(response.status, body);\n }\n\n const body = response.body;\n if (!body) {\n throw new OrakleConnectionError(\"Response body is null\");\n }\n\n yield* parseSSEBody(body, signal);\n};\n\n/**\n * Internal: parse a ReadableStream<Uint8Array> as SSE.\n */\nconst parseSSEBody = async function* (\n body: ReadableStream<Uint8Array>,\n signal?: AbortSignal,\n): AsyncGenerator<SSEFrame> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n\n let buffer = \"\";\n let currentEvent = \"\";\n let currentId = \"\";\n let currentData: string[] = [];\n\n type ReadResult = Awaited<ReturnType<typeof reader.read>>;\n\n /** Race reader.read() against the abort signal. */\n const readOrAbort = (): Promise<ReadResult> => {\n if (signal?.aborted) {\n return Promise.reject(signal.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n }\n return new Promise<ReadResult>((resolve, reject) => {\n const onAbort = () => {\n reject(signal?.reason ?? new DOMException(\"Aborted\", \"AbortError\"));\n };\n signal?.addEventListener(\"abort\", onAbort, { once: true });\n reader.read().then(\n (result) => {\n signal?.removeEventListener(\"abort\", onAbort);\n resolve(result);\n },\n (err: unknown) => {\n signal?.removeEventListener(\"abort\", onAbort);\n reject(err);\n },\n );\n });\n };\n\n try {\n while (true) {\n if (signal?.aborted) {\n break;\n }\n\n const { done, value } = await readOrAbort();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split(\"\\n\");\n // Keep the last partial line in the buffer\n buffer = lines.pop() ?? \"\";\n\n for (const line of lines) {\n if (line === \"\") {\n // Empty line = end of event block\n if (currentData.length > 0) {\n yield {\n event: currentEvent ?? \"message\",\n id: currentId,\n data: currentData.join(\"\\n\"),\n };\n }\n // Reset for next event\n currentEvent = \"\";\n currentId = \"\";\n currentData = [];\n continue;\n }\n\n // SSE comment — ignore (keepalive)\n if (line.startsWith(\":\")) {\n continue;\n }\n\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) {\n // Field with no value\n continue;\n }\n\n const field = line.slice(0, colonIdx);\n // Value starts after \": \" (space after colon is optional per spec)\n let value_ = line.slice(colonIdx + 1);\n if (value_.startsWith(\" \")) {\n value_ = value_.slice(1);\n }\n\n switch (field) {\n case \"event\":\n currentEvent = value_;\n break;\n case \"id\":\n currentId = value_;\n break;\n case \"data\":\n currentData.push(value_);\n break;\n case \"retry\":\n // Optional retry field — notify caller\n break;\n }\n }\n }\n\n // Flush any remaining event in buffer\n if (buffer !== \"\") {\n const lines = buffer.split(\"\\n\");\n for (const line of lines) {\n if (line === \"\" && currentData.length > 0) {\n yield {\n event: currentEvent ?? \"message\",\n id: currentId,\n data: currentData.join(\"\\n\"),\n };\n currentEvent = \"\";\n currentId = \"\";\n currentData = [];\n continue;\n }\n if (line.startsWith(\":\") || line === \"\") continue;\n const colonIdx = line.indexOf(\":\");\n if (colonIdx === -1) continue;\n const field = line.slice(0, colonIdx);\n let value_ = line.slice(colonIdx + 1);\n if (value_.startsWith(\" \")) value_ = value_.slice(1);\n switch (field) {\n case \"event\":\n currentEvent = value_;\n break;\n case \"id\":\n currentId = value_;\n break;\n case \"data\":\n currentData.push(value_);\n break;\n }\n }\n if (currentData.length > 0) {\n yield {\n event: currentEvent ?? \"message\",\n id: currentId,\n data: currentData.join(\"\\n\"),\n };\n }\n }\n } finally {\n reader.releaseLock();\n }\n};\n\n/**\n * Internal error used to communicate HTTP status from the SSE fetch.\n * The client layer catches this and maps it to OrakleApiError.\n */\nexport class SSEHttpError extends Error {\n readonly status: number;\n readonly body: string;\n\n constructor(status: number, body: string) {\n super(`SSE request failed with status ${status}`);\n this.name = \"SSEHttpError\";\n this.status = status;\n this.body = body;\n }\n}\n","/**\n * OrakleClient — main entry point for the Orakle TypeScript SDK.\n *\n * Usage:\n * const client = new OrakleClient({ apiKey: \"...\" });\n * const result = await client.priceCheck({ query: \"iPhone 15 Pro 256GB\" });\n */\n\nimport {\n OrakleApiError,\n OrakleConnectionError,\n OrakleError,\n OrakleTimeoutError,\n} from \"./errors.js\";\nimport { SSEHttpError, createSSEStream } from \"./sse.js\";\nimport type {\n ApiErrorBody,\n CompletedEvent,\n ErrorCode,\n FailedEvent,\n JobEvent,\n JobRequest,\n JobSubmitResponse,\n OrakleConfig,\n PriceCheckOptions,\n PriceCheckResult,\n StageEvent,\n} from \"./types.js\";\n\nconst DEFAULT_BASE_URL = \"https://api.orakle.xyz\";\nconst DEFAULT_TIMEOUT_MS = 180_000;\n\nexport class OrakleClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly timeout: number;\n\n constructor(config: OrakleConfig) {\n this.apiKey = config.apiKey;\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\n this.timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;\n }\n\n /**\n * Submit a pricing job and wait for the result.\n *\n * Internally: POST /v1/jobs -> open SSE stream -> resolve on completed, reject on failed.\n */\n async priceCheck(\n params: JobRequest,\n options?: PriceCheckOptions,\n ): Promise<PriceCheckResult> {\n const timeoutMs = options?.timeout ?? this.timeout;\n const controller = new AbortController();\n\n // Link external signal to our internal controller\n const externalSignal = options?.signal;\n const onExternalAbort = () => controller.abort();\n externalSignal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n\n const timer = setTimeout(() => {\n controller.abort(new OrakleTimeoutError(timeoutMs));\n }, timeoutMs);\n\n try {\n const job = await this.submitJob(params, controller.signal);\n const streamUrl = `${this.baseUrl}/v1/jobs/${job.job_id}/stream`;\n\n const stream = createSSEStream({\n url: streamUrl,\n headers: this.defaultHeaders(),\n signal: controller.signal,\n });\n\n for await (const frame of stream) {\n const event = this.parseJobEvent(frame.event, frame.data);\n if (!event) continue;\n\n switch (event.event_type) {\n case \"stage\":\n options?.onProgress?.(event);\n break;\n case \"completed\":\n return event.result;\n case \"failed\":\n throw new OrakleError(event.error);\n }\n }\n\n // Stream ended without a terminal event\n throw new OrakleError(\"SSE stream ended without a completed or failed event\");\n } catch (err: unknown) {\n throw this.normalizeError(err);\n } finally {\n clearTimeout(timer);\n externalSignal?.removeEventListener(\"abort\", onExternalAbort);\n }\n }\n\n /**\n * Submit a pricing job and return an async iterable of all events.\n *\n * Yields StageEvent and CompletedEvent/FailedEvent as they arrive.\n */\n async *priceCheckStream(\n params: JobRequest,\n options?: PriceCheckOptions,\n ): AsyncGenerator<JobEvent> {\n const timeoutMs = options?.timeout ?? this.timeout;\n const controller = new AbortController();\n\n const externalSignal = options?.signal;\n const onExternalAbort = () => controller.abort();\n externalSignal?.addEventListener(\"abort\", onExternalAbort, { once: true });\n\n const timer = setTimeout(() => {\n controller.abort(new OrakleTimeoutError(timeoutMs));\n }, timeoutMs);\n\n try {\n const job = await this.submitJob(params, controller.signal);\n const streamUrl = `${this.baseUrl}/v1/jobs/${job.job_id}/stream`;\n\n const stream = createSSEStream({\n url: streamUrl,\n headers: this.defaultHeaders(),\n signal: controller.signal,\n });\n\n for await (const frame of stream) {\n const event = this.parseJobEvent(frame.event, frame.data);\n if (!event) continue;\n\n if (event.event_type === \"stage\") {\n options?.onProgress?.(event);\n }\n\n yield event;\n\n // Terminal events end the stream\n if (event.event_type === \"completed\" || event.event_type === \"failed\") {\n return;\n }\n }\n } catch (err: unknown) {\n throw this.normalizeError(err);\n } finally {\n clearTimeout(timer);\n externalSignal?.removeEventListener(\"abort\", onExternalAbort);\n }\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private async submitJob(\n params: JobRequest,\n signal: AbortSignal,\n ): Promise<JobSubmitResponse> {\n let response: Response;\n try {\n response = await fetch(`${this.baseUrl}/v1/jobs`, {\n method: \"POST\",\n headers: {\n ...this.defaultHeaders(),\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(params),\n signal,\n });\n } catch (err: unknown) {\n if (err instanceof DOMException && err.name === \"AbortError\") {\n throw err;\n }\n throw new OrakleConnectionError(\n \"Failed to connect to Orakle API\",\n err,\n );\n }\n\n if (!response.ok) {\n throw await this.buildApiError(response);\n }\n\n return (await response.json()) as JobSubmitResponse;\n }\n\n private defaultHeaders(): Record<string, string> {\n return {\n \"X-Orakle-API-Key\": this.apiKey,\n };\n }\n\n private parseJobEvent(eventType: string, data: string): JobEvent | undefined {\n try {\n const parsed: unknown = JSON.parse(data);\n if (typeof parsed !== \"object\" || parsed === null) return undefined;\n\n switch (eventType) {\n case \"stage\":\n return { ...parsed, event_type: \"stage\" } as StageEvent;\n case \"completed\":\n return { ...parsed, event_type: \"completed\" } as CompletedEvent;\n case \"failed\":\n return { ...parsed, event_type: \"failed\" } as FailedEvent;\n default:\n return undefined;\n }\n } catch {\n return undefined;\n }\n }\n\n private async buildApiError(response: Response): Promise<OrakleApiError> {\n let body: ApiErrorBody | undefined;\n try {\n body = (await response.json()) as ApiErrorBody;\n } catch {\n // Response body is not JSON\n }\n\n const code: ErrorCode = body?.code ?? this.httpStatusToErrorCode(response.status);\n const message = body?.message ?? `API request failed with status ${response.status}`;\n\n return new OrakleApiError(message, response.status, code);\n }\n\n private httpStatusToErrorCode(status: number): ErrorCode {\n switch (status) {\n case 400:\n return \"invalid_request\";\n case 401:\n return \"auth_required\";\n case 429:\n return \"rate_limited\";\n case 404:\n return \"job_not_found\";\n case 503:\n return \"service_unavailable\";\n default:\n return \"internal_error\";\n }\n }\n\n private normalizeError(err: unknown): Error {\n if (err instanceof OrakleError) {\n return err;\n }\n\n if (err instanceof SSEHttpError) {\n let body: ApiErrorBody | undefined;\n try {\n body = JSON.parse(err.body) as ApiErrorBody;\n } catch {\n // Not JSON\n }\n const code = body?.code ?? this.httpStatusToErrorCode(err.status);\n const message = body?.message ?? `API request failed with status ${err.status}`;\n return new OrakleApiError(message, err.status, code);\n }\n\n // AbortError from timeout\n if (err instanceof DOMException && err.name === \"AbortError\") {\n // Check if our timeout caused the abort\n return new OrakleTimeoutError(this.timeout);\n }\n\n if (err instanceof Error) {\n return new OrakleConnectionError(err.message, err);\n }\n\n return new OrakleConnectionError(String(err));\n }\n}\n"],"mappings":";AAKO,IAAM,cAAN,cAA0B,MAAM;AAAA,EACrC,YAAY,SAAiB;AAC3B,UAAM,OAAO;AACb,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,iBAAN,cAA6B,YAAY;AAAA,EACrC;AAAA,EACA;AAAA,EAET,YAAY,SAAiB,QAAgB,MAAiB;AAC5D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EACd;AACF;AAKO,IAAM,qBAAN,cAAiC,YAAY;AAAA,EACzC;AAAA,EAET,YAAY,WAAmB;AAC7B,UAAM,2BAA2B,SAAS,IAAI;AAC9C,SAAK,OAAO;AACZ,SAAK,YAAY;AAAA,EACnB;AACF;AAKO,IAAM,wBAAN,cAAoC,YAAY;AAAA,EAC5C;AAAA,EAET,YAAY,SAAiB,OAAiB;AAC5C,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,SAAK,QAAQ;AAAA,EACf;AACF;;;ACzBO,IAAM,kBAAkB,iBAC7B,SAC0B;AAC1B,QAAM,EAAE,KAAK,SAAS,QAAQ,YAAY,IAAI;AAE9C,QAAM,iBAAyC;AAAA,IAC7C,GAAG;AAAA,IACH,QAAQ;AAAA,IACR,iBAAiB;AAAA,EACnB;AAEA,MAAI,aAAa;AACf,mBAAe,eAAe,IAAI;AAAA,EACpC;AAEA,MAAI;AACJ,MAAI;AACF,eAAW,MAAM,MAAM,KAAK;AAAA,MAC1B,QAAQ;AAAA,MACR,SAAS;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH,SAAS,KAAc;AACrB,QAAI,eAAe,gBAAgB,IAAI,SAAS,cAAc;AAC5D,YAAM;AAAA,IACR;AACA,UAAM,IAAI;AAAA,MACR,oCAAoC,GAAG;AAAA,MACvC;AAAA,IACF;AAAA,EACF;AAEA,MAAI,CAAC,SAAS,IAAI;AAEhB,UAAMA,QAAO,MAAM,SAAS,KAAK;AACjC,UAAM,IAAI,aAAa,SAAS,QAAQA,KAAI;AAAA,EAC9C;AAEA,QAAM,OAAO,SAAS;AACtB,MAAI,CAAC,MAAM;AACT,UAAM,IAAI,sBAAsB,uBAAuB;AAAA,EACzD;AAEA,SAAO,aAAa,MAAM,MAAM;AAClC;AAKA,IAAM,eAAe,iBACnB,MACA,QAC0B;AAC1B,QAAM,SAAS,KAAK,UAAU;AAC9B,QAAM,UAAU,IAAI,YAAY;AAEhC,MAAI,SAAS;AACb,MAAI,eAAe;AACnB,MAAI,YAAY;AAChB,MAAI,cAAwB,CAAC;AAK7B,QAAM,cAAc,MAA2B;AAC7C,QAAI,QAAQ,SAAS;AACnB,aAAO,QAAQ,OAAO,OAAO,UAAU,IAAI,aAAa,WAAW,YAAY,CAAC;AAAA,IAClF;AACA,WAAO,IAAI,QAAoB,CAAC,SAAS,WAAW;AAClD,YAAM,UAAU,MAAM;AACpB,eAAO,QAAQ,UAAU,IAAI,aAAa,WAAW,YAAY,CAAC;AAAA,MACpE;AACA,cAAQ,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AACzD,aAAO,KAAK,EAAE;AAAA,QACZ,CAAC,WAAW;AACV,kBAAQ,oBAAoB,SAAS,OAAO;AAC5C,kBAAQ,MAAM;AAAA,QAChB;AAAA,QACA,CAAC,QAAiB;AAChB,kBAAQ,oBAAoB,SAAS,OAAO;AAC5C,iBAAO,GAAG;AAAA,QACZ;AAAA,MACF;AAAA,IACF,CAAC;AAAA,EACH;AAEA,MAAI;AACF,WAAO,MAAM;AACX,UAAI,QAAQ,SAAS;AACnB;AAAA,MACF;AAEA,YAAM,EAAE,MAAM,MAAM,IAAI,MAAM,YAAY;AAC1C,UAAI,KAAM;AAEV,gBAAU,QAAQ,OAAO,OAAO,EAAE,QAAQ,KAAK,CAAC;AAEhD,YAAM,QAAQ,OAAO,MAAM,IAAI;AAE/B,eAAS,MAAM,IAAI,KAAK;AAExB,iBAAW,QAAQ,OAAO;AACxB,YAAI,SAAS,IAAI;AAEf,cAAI,YAAY,SAAS,GAAG;AAC1B,kBAAM;AAAA,cACJ,OAAO,gBAAgB;AAAA,cACvB,IAAI;AAAA,cACJ,MAAM,YAAY,KAAK,IAAI;AAAA,YAC7B;AAAA,UACF;AAEA,yBAAe;AACf,sBAAY;AACZ,wBAAc,CAAC;AACf;AAAA,QACF;AAGA,YAAI,KAAK,WAAW,GAAG,GAAG;AACxB;AAAA,QACF;AAEA,cAAM,WAAW,KAAK,QAAQ,GAAG;AACjC,YAAI,aAAa,IAAI;AAEnB;AAAA,QACF;AAEA,cAAM,QAAQ,KAAK,MAAM,GAAG,QAAQ;AAEpC,YAAI,SAAS,KAAK,MAAM,WAAW,CAAC;AACpC,YAAI,OAAO,WAAW,GAAG,GAAG;AAC1B,mBAAS,OAAO,MAAM,CAAC;AAAA,QACzB;AAEA,gBAAQ,OAAO;AAAA,UACb,KAAK;AACH,2BAAe;AACf;AAAA,UACF,KAAK;AACH,wBAAY;AACZ;AAAA,UACF,KAAK;AACH,wBAAY,KAAK,MAAM;AACvB;AAAA,UACF,KAAK;AAEH;AAAA,QACJ;AAAA,MACF;AAAA,IACF;AAGA,QAAI,WAAW,IAAI;AACjB,YAAM,QAAQ,OAAO,MAAM,IAAI;AAC/B,iBAAW,QAAQ,OAAO;AACxB,YAAI,SAAS,MAAM,YAAY,SAAS,GAAG;AACzC,gBAAM;AAAA,YACJ,OAAO,gBAAgB;AAAA,YACvB,IAAI;AAAA,YACJ,MAAM,YAAY,KAAK,IAAI;AAAA,UAC7B;AACA,yBAAe;AACf,sBAAY;AACZ,wBAAc,CAAC;AACf;AAAA,QACF;AACA,YAAI,KAAK,WAAW,GAAG,KAAK,SAAS,GAAI;AACzC,cAAM,WAAW,KAAK,QAAQ,GAAG;AACjC,YAAI,aAAa,GAAI;AACrB,cAAM,QAAQ,KAAK,MAAM,GAAG,QAAQ;AACpC,YAAI,SAAS,KAAK,MAAM,WAAW,CAAC;AACpC,YAAI,OAAO,WAAW,GAAG,EAAG,UAAS,OAAO,MAAM,CAAC;AACnD,gBAAQ,OAAO;AAAA,UACb,KAAK;AACH,2BAAe;AACf;AAAA,UACF,KAAK;AACH,wBAAY;AACZ;AAAA,UACF,KAAK;AACH,wBAAY,KAAK,MAAM;AACvB;AAAA,QACJ;AAAA,MACF;AACA,UAAI,YAAY,SAAS,GAAG;AAC1B,cAAM;AAAA,UACJ,OAAO,gBAAgB;AAAA,UACvB,IAAI;AAAA,UACJ,MAAM,YAAY,KAAK,IAAI;AAAA,QAC7B;AAAA,MACF;AAAA,IACF;AAAA,EACF,UAAE;AACA,WAAO,YAAY;AAAA,EACrB;AACF;AAMO,IAAM,eAAN,cAA2B,MAAM;AAAA,EAC7B;AAAA,EACA;AAAA,EAET,YAAY,QAAgB,MAAc;AACxC,UAAM,kCAAkC,MAAM,EAAE;AAChD,SAAK,OAAO;AACZ,SAAK,SAAS;AACd,SAAK,OAAO;AAAA,EACd;AACF;;;AClNA,IAAM,mBAAmB;AACzB,IAAM,qBAAqB;AAEpB,IAAM,eAAN,MAAmB;AAAA,EACP;AAAA,EACA;AAAA,EACA;AAAA,EAEjB,YAAY,QAAsB;AAChC,SAAK,SAAS,OAAO;AACrB,SAAK,WAAW,OAAO,WAAW,kBAAkB,QAAQ,QAAQ,EAAE;AACtE,SAAK,UAAU,OAAO,WAAW;AAAA,EACnC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,WACJ,QACA,SAC2B;AAC3B,UAAM,YAAY,SAAS,WAAW,KAAK;AAC3C,UAAM,aAAa,IAAI,gBAAgB;AAGvC,UAAM,iBAAiB,SAAS;AAChC,UAAM,kBAAkB,MAAM,WAAW,MAAM;AAC/C,oBAAgB,iBAAiB,SAAS,iBAAiB,EAAE,MAAM,KAAK,CAAC;AAEzE,UAAM,QAAQ,WAAW,MAAM;AAC7B,iBAAW,MAAM,IAAI,mBAAmB,SAAS,CAAC;AAAA,IACpD,GAAG,SAAS;AAEZ,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,UAAU,QAAQ,WAAW,MAAM;AAC1D,YAAM,YAAY,GAAG,KAAK,OAAO,YAAY,IAAI,MAAM;AAEvD,YAAM,SAAS,gBAAgB;AAAA,QAC7B,KAAK;AAAA,QACL,SAAS,KAAK,eAAe;AAAA,QAC7B,QAAQ,WAAW;AAAA,MACrB,CAAC;AAED,uBAAiB,SAAS,QAAQ;AAChC,cAAM,QAAQ,KAAK,cAAc,MAAM,OAAO,MAAM,IAAI;AACxD,YAAI,CAAC,MAAO;AAEZ,gBAAQ,MAAM,YAAY;AAAA,UACxB,KAAK;AACH,qBAAS,aAAa,KAAK;AAC3B;AAAA,UACF,KAAK;AACH,mBAAO,MAAM;AAAA,UACf,KAAK;AACH,kBAAM,IAAI,YAAY,MAAM,KAAK;AAAA,QACrC;AAAA,MACF;AAGA,YAAM,IAAI,YAAY,sDAAsD;AAAA,IAC9E,SAAS,KAAc;AACrB,YAAM,KAAK,eAAe,GAAG;AAAA,IAC/B,UAAE;AACA,mBAAa,KAAK;AAClB,sBAAgB,oBAAoB,SAAS,eAAe;AAAA,IAC9D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,OAAO,iBACL,QACA,SAC0B;AAC1B,UAAM,YAAY,SAAS,WAAW,KAAK;AAC3C,UAAM,aAAa,IAAI,gBAAgB;AAEvC,UAAM,iBAAiB,SAAS;AAChC,UAAM,kBAAkB,MAAM,WAAW,MAAM;AAC/C,oBAAgB,iBAAiB,SAAS,iBAAiB,EAAE,MAAM,KAAK,CAAC;AAEzE,UAAM,QAAQ,WAAW,MAAM;AAC7B,iBAAW,MAAM,IAAI,mBAAmB,SAAS,CAAC;AAAA,IACpD,GAAG,SAAS;AAEZ,QAAI;AACF,YAAM,MAAM,MAAM,KAAK,UAAU,QAAQ,WAAW,MAAM;AAC1D,YAAM,YAAY,GAAG,KAAK,OAAO,YAAY,IAAI,MAAM;AAEvD,YAAM,SAAS,gBAAgB;AAAA,QAC7B,KAAK;AAAA,QACL,SAAS,KAAK,eAAe;AAAA,QAC7B,QAAQ,WAAW;AAAA,MACrB,CAAC;AAED,uBAAiB,SAAS,QAAQ;AAChC,cAAM,QAAQ,KAAK,cAAc,MAAM,OAAO,MAAM,IAAI;AACxD,YAAI,CAAC,MAAO;AAEZ,YAAI,MAAM,eAAe,SAAS;AAChC,mBAAS,aAAa,KAAK;AAAA,QAC7B;AAEA,cAAM;AAGN,YAAI,MAAM,eAAe,eAAe,MAAM,eAAe,UAAU;AACrE;AAAA,QACF;AAAA,MACF;AAAA,IACF,SAAS,KAAc;AACrB,YAAM,KAAK,eAAe,GAAG;AAAA,IAC/B,UAAE;AACA,mBAAa,KAAK;AAClB,sBAAgB,oBAAoB,SAAS,eAAe;AAAA,IAC9D;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAMA,MAAc,UACZ,QACA,QAC4B;AAC5B,QAAI;AACJ,QAAI;AACF,iBAAW,MAAM,MAAM,GAAG,KAAK,OAAO,YAAY;AAAA,QAChD,QAAQ;AAAA,QACR,SAAS;AAAA,UACP,GAAG,KAAK,eAAe;AAAA,UACvB,gBAAgB;AAAA,QAClB;AAAA,QACA,MAAM,KAAK,UAAU,MAAM;AAAA,QAC3B;AAAA,MACF,CAAC;AAAA,IACH,SAAS,KAAc;AACrB,UAAI,eAAe,gBAAgB,IAAI,SAAS,cAAc;AAC5D,cAAM;AAAA,MACR;AACA,YAAM,IAAI;AAAA,QACR;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAEA,QAAI,CAAC,SAAS,IAAI;AAChB,YAAM,MAAM,KAAK,cAAc,QAAQ;AAAA,IACzC;AAEA,WAAQ,MAAM,SAAS,KAAK;AAAA,EAC9B;AAAA,EAEQ,iBAAyC;AAC/C,WAAO;AAAA,MACL,oBAAoB,KAAK;AAAA,IAC3B;AAAA,EACF;AAAA,EAEQ,cAAc,WAAmB,MAAoC;AAC3E,QAAI;AACF,YAAM,SAAkB,KAAK,MAAM,IAAI;AACvC,UAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO;AAE1D,cAAQ,WAAW;AAAA,QACjB,KAAK;AACH,iBAAO,EAAE,GAAG,QAAQ,YAAY,QAAQ;AAAA,QAC1C,KAAK;AACH,iBAAO,EAAE,GAAG,QAAQ,YAAY,YAAY;AAAA,QAC9C,KAAK;AACH,iBAAO,EAAE,GAAG,QAAQ,YAAY,SAAS;AAAA,QAC3C;AACE,iBAAO;AAAA,MACX;AAAA,IACF,QAAQ;AACN,aAAO;AAAA,IACT;AAAA,EACF;AAAA,EAEA,MAAc,cAAc,UAA6C;AACvE,QAAI;AACJ,QAAI;AACF,aAAQ,MAAM,SAAS,KAAK;AAAA,IAC9B,QAAQ;AAAA,IAER;AAEA,UAAM,OAAkB,MAAM,QAAQ,KAAK,sBAAsB,SAAS,MAAM;AAChF,UAAM,UAAU,MAAM,WAAW,kCAAkC,SAAS,MAAM;AAElF,WAAO,IAAI,eAAe,SAAS,SAAS,QAAQ,IAAI;AAAA,EAC1D;AAAA,EAEQ,sBAAsB,QAA2B;AACvD,YAAQ,QAAQ;AAAA,MACd,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT,KAAK;AACH,eAAO;AAAA,MACT;AACE,eAAO;AAAA,IACX;AAAA,EACF;AAAA,EAEQ,eAAe,KAAqB;AAC1C,QAAI,eAAe,aAAa;AAC9B,aAAO;AAAA,IACT;AAEA,QAAI,eAAe,cAAc;AAC/B,UAAI;AACJ,UAAI;AACF,eAAO,KAAK,MAAM,IAAI,IAAI;AAAA,MAC5B,QAAQ;AAAA,MAER;AACA,YAAM,OAAO,MAAM,QAAQ,KAAK,sBAAsB,IAAI,MAAM;AAChE,YAAM,UAAU,MAAM,WAAW,kCAAkC,IAAI,MAAM;AAC7E,aAAO,IAAI,eAAe,SAAS,IAAI,QAAQ,IAAI;AAAA,IACrD;AAGA,QAAI,eAAe,gBAAgB,IAAI,SAAS,cAAc;AAE5D,aAAO,IAAI,mBAAmB,KAAK,OAAO;AAAA,IAC5C;AAEA,QAAI,eAAe,OAAO;AACxB,aAAO,IAAI,sBAAsB,IAAI,SAAS,GAAG;AAAA,IACnD;AAEA,WAAO,IAAI,sBAAsB,OAAO,GAAG,CAAC;AAAA,EAC9C;AACF;","names":["body"]}
package/package.json CHANGED
@@ -1,6 +1,43 @@
1
1
  {
2
2
  "name": "orakle",
3
- "version": "0.0.1",
3
+ "version": "0.0.2",
4
4
  "description": "Client SDK for orakle.xyz",
5
- "license": "Apache-2.0"
6
- }
5
+ "license": "Apache-2.0",
6
+ "type": "module",
7
+ "exports": {
8
+ ".": {
9
+ "import": {
10
+ "types": "./dist/index.d.ts",
11
+ "default": "./dist/index.js"
12
+ },
13
+ "require": {
14
+ "types": "./dist/index.d.cts",
15
+ "default": "./dist/index.cjs"
16
+ }
17
+ }
18
+ },
19
+ "main": "./dist/index.cjs",
20
+ "module": "./dist/index.js",
21
+ "types": "./dist/index.d.ts",
22
+ "files": [
23
+ "dist",
24
+ "README.md",
25
+ "LICENSE"
26
+ ],
27
+ "engines": {
28
+ "node": ">=18.0.0"
29
+ },
30
+ "devDependencies": {
31
+ "@biomejs/biome": "^1.9.4",
32
+ "@types/node": "^22.15.3",
33
+ "tsup": "^8.4.0",
34
+ "typescript": "^5.8.3",
35
+ "vitest": "^3.1.2"
36
+ },
37
+ "scripts": {
38
+ "build": "tsup",
39
+ "typecheck": "tsc --noEmit",
40
+ "test": "vitest run",
41
+ "test:watch": "vitest"
42
+ }
43
+ }