orakle 0.0.2 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -1
- package/dist/index.cjs +34 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +88 -9
- package/dist/index.d.ts +88 -9
- package/dist/index.js +31 -11
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/README.md
CHANGED
|
@@ -1,3 +1,149 @@
|
|
|
1
1
|
# orakle
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
TypeScript SDK for [orakle.xyz](https://orakle.xyz) — AI-powered pricing intelligence.
|
|
4
|
+
|
|
5
|
+
Submits pricing jobs and streams results over SSE. No polling required. Works in Node.js 18+ and modern browsers.
|
|
6
|
+
|
|
7
|
+
## Install
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
npm install orakle
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import { OrakleClient } from "orakle";
|
|
17
|
+
|
|
18
|
+
const client = new OrakleClient({ apiKey: "orakle_..." });
|
|
19
|
+
|
|
20
|
+
// Natural language query
|
|
21
|
+
const result = await client.priceCheck({
|
|
22
|
+
request_type: "nl",
|
|
23
|
+
query: "iPhone 15 Pro 256GB unlocked mint condition",
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
console.log(result.fused_distribution.quantiles);
|
|
27
|
+
// { "0.1": 780, "0.25": 820, "0.5": 870, "0.75": 920, "0.9": 960 }
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Structured Query
|
|
31
|
+
|
|
32
|
+
```ts
|
|
33
|
+
const result = await client.priceCheck({
|
|
34
|
+
request_type: "structured",
|
|
35
|
+
product: {
|
|
36
|
+
category: "smartphone",
|
|
37
|
+
make: "Apple",
|
|
38
|
+
model: "iPhone 15 Pro",
|
|
39
|
+
storage: "256GB",
|
|
40
|
+
condition: "mint",
|
|
41
|
+
carrier_lock: "unlocked",
|
|
42
|
+
},
|
|
43
|
+
geography: "US",
|
|
44
|
+
});
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Streaming Progress
|
|
48
|
+
|
|
49
|
+
Track pipeline stages as they happen:
|
|
50
|
+
|
|
51
|
+
```ts
|
|
52
|
+
// Callback-based
|
|
53
|
+
const result = await client.priceCheck(
|
|
54
|
+
{ request_type: "nl", query: "MacBook Air M3 16GB" },
|
|
55
|
+
{
|
|
56
|
+
onProgress: (event) => {
|
|
57
|
+
console.log(`[${event.stage}] ${event.message}`);
|
|
58
|
+
// [analyzing] Parsing product attributes...
|
|
59
|
+
// [price_lookup] Fetching market data...
|
|
60
|
+
// [planning] Generating scenario forecasts...
|
|
61
|
+
// [dispatching] Running pricing agents...
|
|
62
|
+
// [fusing] Combining modality outputs...
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Iterator-based — full control over each event
|
|
68
|
+
for await (const event of client.priceCheckStream({
|
|
69
|
+
request_type: "nl",
|
|
70
|
+
query: "PS5 Disc Edition",
|
|
71
|
+
})) {
|
|
72
|
+
if (event.event_type === "stage") {
|
|
73
|
+
console.log(event.stage, event.message);
|
|
74
|
+
}
|
|
75
|
+
if (event.event_type === "completed") {
|
|
76
|
+
console.log("Result:", event.result);
|
|
77
|
+
}
|
|
78
|
+
if (event.event_type === "failed") {
|
|
79
|
+
console.error("Failed:", event.error);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Configuration
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
const client = new OrakleClient({
|
|
88
|
+
apiKey: "orakle_...", // Required
|
|
89
|
+
timeout: 300_000, // Default: 180_000 (3 min), minimum: 180_000
|
|
90
|
+
});
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Per-request options:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
const controller = new AbortController();
|
|
97
|
+
|
|
98
|
+
const result = await client.priceCheck(
|
|
99
|
+
{ request_type: "nl", query: "..." },
|
|
100
|
+
{
|
|
101
|
+
timeout: 30_000, // Override client timeout
|
|
102
|
+
signal: controller.signal, // AbortController support
|
|
103
|
+
onProgress: (e) => {}, // Stage progress callback
|
|
104
|
+
},
|
|
105
|
+
);
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
## Error Handling
|
|
109
|
+
|
|
110
|
+
```ts
|
|
111
|
+
import {
|
|
112
|
+
OrakleApiError,
|
|
113
|
+
OrakleTimeoutError,
|
|
114
|
+
OrakleConnectionError,
|
|
115
|
+
} from "orakle";
|
|
116
|
+
|
|
117
|
+
try {
|
|
118
|
+
await client.priceCheck({ request_type: "nl", query: "..." });
|
|
119
|
+
} catch (err) {
|
|
120
|
+
if (err instanceof OrakleApiError) {
|
|
121
|
+
console.error(err.code, err.status, err.message);
|
|
122
|
+
// "rate_limited", 429, "Rate limit exceeded"
|
|
123
|
+
}
|
|
124
|
+
if (err instanceof OrakleTimeoutError) {
|
|
125
|
+
console.error(`Timed out after ${err.timeoutMs}ms`);
|
|
126
|
+
}
|
|
127
|
+
if (err instanceof OrakleConnectionError) {
|
|
128
|
+
console.error("Network error:", err.message);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
Error codes: `invalid_request`, `auth_required`, `rate_limited`, `job_not_found`, `insufficient_data`, `internal_error`, `service_unavailable`.
|
|
134
|
+
|
|
135
|
+
## Result Shape
|
|
136
|
+
|
|
137
|
+
```ts
|
|
138
|
+
interface PriceCheckResult {
|
|
139
|
+
product_profile: ProductProfile;
|
|
140
|
+
fused_distribution: PriceDistribution;
|
|
141
|
+
scenario_forecast?: ScenarioForecast | null;
|
|
142
|
+
}
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
`fused_distribution.quantiles` contains percentile price points (p10, p25, p50, p75, p90). `scenario_forecast` includes probability-weighted price scenarios when available.
|
|
146
|
+
|
|
147
|
+
## License
|
|
148
|
+
|
|
149
|
+
Apache-2.0
|
package/dist/index.cjs
CHANGED
|
@@ -24,7 +24,9 @@ __export(index_exports, {
|
|
|
24
24
|
OrakleClient: () => OrakleClient,
|
|
25
25
|
OrakleConnectionError: () => OrakleConnectionError,
|
|
26
26
|
OrakleError: () => OrakleError,
|
|
27
|
-
OrakleTimeoutError: () => OrakleTimeoutError
|
|
27
|
+
OrakleTimeoutError: () => OrakleTimeoutError,
|
|
28
|
+
SSEHttpError: () => SSEHttpError,
|
|
29
|
+
createSSEStream: () => createSSEStream
|
|
28
30
|
});
|
|
29
31
|
module.exports = __toCommonJS(index_exports);
|
|
30
32
|
|
|
@@ -108,7 +110,9 @@ var parseSSEBody = async function* (body, signal) {
|
|
|
108
110
|
let currentData = [];
|
|
109
111
|
const readOrAbort = () => {
|
|
110
112
|
if (signal?.aborted) {
|
|
111
|
-
return Promise.reject(
|
|
113
|
+
return Promise.reject(
|
|
114
|
+
signal.reason ?? new DOMException("Aborted", "AbortError")
|
|
115
|
+
);
|
|
112
116
|
}
|
|
113
117
|
return new Promise((resolve, reject) => {
|
|
114
118
|
const onAbort = () => {
|
|
@@ -236,14 +240,18 @@ var SSEHttpError = class extends Error {
|
|
|
236
240
|
// src/client.ts
|
|
237
241
|
var DEFAULT_BASE_URL = "https://api.orakle.xyz";
|
|
238
242
|
var DEFAULT_TIMEOUT_MS = 18e4;
|
|
243
|
+
var MIN_TIMEOUT_MS = 18e4;
|
|
239
244
|
var OrakleClient = class {
|
|
240
245
|
apiKey;
|
|
241
|
-
baseUrl;
|
|
242
246
|
timeout;
|
|
247
|
+
baseUrl;
|
|
243
248
|
constructor(config) {
|
|
244
249
|
this.apiKey = config.apiKey;
|
|
250
|
+
this.timeout = Math.max(
|
|
251
|
+
config.timeout ?? DEFAULT_TIMEOUT_MS,
|
|
252
|
+
MIN_TIMEOUT_MS
|
|
253
|
+
);
|
|
245
254
|
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
246
|
-
this.timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
247
255
|
}
|
|
248
256
|
/**
|
|
249
257
|
* Submit a pricing job and wait for the result.
|
|
@@ -251,7 +259,10 @@ var OrakleClient = class {
|
|
|
251
259
|
* Internally: POST /v1/jobs -> open SSE stream -> resolve on completed, reject on failed.
|
|
252
260
|
*/
|
|
253
261
|
async priceCheck(params, options) {
|
|
254
|
-
const timeoutMs =
|
|
262
|
+
const timeoutMs = Math.max(
|
|
263
|
+
options?.timeout ?? this.timeout,
|
|
264
|
+
MIN_TIMEOUT_MS
|
|
265
|
+
);
|
|
255
266
|
const controller = new AbortController();
|
|
256
267
|
const externalSignal = options?.signal;
|
|
257
268
|
const onExternalAbort = () => controller.abort();
|
|
@@ -274,13 +285,18 @@ var OrakleClient = class {
|
|
|
274
285
|
case "stage":
|
|
275
286
|
options?.onProgress?.(event);
|
|
276
287
|
break;
|
|
288
|
+
case "partial":
|
|
289
|
+
options?.onPartial?.(event);
|
|
290
|
+
break;
|
|
277
291
|
case "completed":
|
|
278
292
|
return event.result;
|
|
279
293
|
case "failed":
|
|
280
294
|
throw new OrakleError(event.error);
|
|
281
295
|
}
|
|
282
296
|
}
|
|
283
|
-
throw new OrakleError(
|
|
297
|
+
throw new OrakleError(
|
|
298
|
+
"SSE stream ended without a completed or failed event"
|
|
299
|
+
);
|
|
284
300
|
} catch (err) {
|
|
285
301
|
throw this.normalizeError(err);
|
|
286
302
|
} finally {
|
|
@@ -294,7 +310,10 @@ var OrakleClient = class {
|
|
|
294
310
|
* Yields StageEvent and CompletedEvent/FailedEvent as they arrive.
|
|
295
311
|
*/
|
|
296
312
|
async *priceCheckStream(params, options) {
|
|
297
|
-
const timeoutMs =
|
|
313
|
+
const timeoutMs = Math.max(
|
|
314
|
+
options?.timeout ?? this.timeout,
|
|
315
|
+
MIN_TIMEOUT_MS
|
|
316
|
+
);
|
|
298
317
|
const controller = new AbortController();
|
|
299
318
|
const externalSignal = options?.signal;
|
|
300
319
|
const onExternalAbort = () => controller.abort();
|
|
@@ -315,6 +334,8 @@ var OrakleClient = class {
|
|
|
315
334
|
if (!event) continue;
|
|
316
335
|
if (event.event_type === "stage") {
|
|
317
336
|
options?.onProgress?.(event);
|
|
337
|
+
} else if (event.event_type === "partial") {
|
|
338
|
+
options?.onPartial?.(event);
|
|
318
339
|
}
|
|
319
340
|
yield event;
|
|
320
341
|
if (event.event_type === "completed" || event.event_type === "failed") {
|
|
@@ -347,10 +368,7 @@ var OrakleClient = class {
|
|
|
347
368
|
if (err instanceof DOMException && err.name === "AbortError") {
|
|
348
369
|
throw err;
|
|
349
370
|
}
|
|
350
|
-
throw new OrakleConnectionError(
|
|
351
|
-
"Failed to connect to Orakle API",
|
|
352
|
-
err
|
|
353
|
-
);
|
|
371
|
+
throw new OrakleConnectionError("Failed to connect to Orakle API", err);
|
|
354
372
|
}
|
|
355
373
|
if (!response.ok) {
|
|
356
374
|
throw await this.buildApiError(response);
|
|
@@ -369,6 +387,8 @@ var OrakleClient = class {
|
|
|
369
387
|
switch (eventType) {
|
|
370
388
|
case "stage":
|
|
371
389
|
return { ...parsed, event_type: "stage" };
|
|
390
|
+
case "partial":
|
|
391
|
+
return { ...parsed, event_type: "partial" };
|
|
372
392
|
case "completed":
|
|
373
393
|
return { ...parsed, event_type: "completed" };
|
|
374
394
|
case "failed":
|
|
@@ -435,6 +455,8 @@ var OrakleClient = class {
|
|
|
435
455
|
OrakleClient,
|
|
436
456
|
OrakleConnectionError,
|
|
437
457
|
OrakleError,
|
|
438
|
-
OrakleTimeoutError
|
|
458
|
+
OrakleTimeoutError,
|
|
459
|
+
SSEHttpError,
|
|
460
|
+
createSSEStream
|
|
439
461
|
});
|
|
440
462
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +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"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/errors.ts","../src/sse.ts","../src/client.ts"],"sourcesContent":["// Client\nexport { OrakleClient } from \"./client.js\";\n// Errors\nexport {\n OrakleApiError,\n OrakleConnectionError,\n OrakleError,\n OrakleTimeoutError,\n} from \"./errors.js\";\n// SSE primitives — exposed so server-side proxies (e.g. a Next.js route\n// handler that re-emits a sanitized event stream to the browser) can\n// consume the same parser the client uses, without re-implementing it.\nexport { createSSEStream, SSEHttpError } from \"./sse.js\";\n\n// Types\nexport type {\n ApiErrorBody,\n AttributeCorrection,\n CompletedEvent,\n DistributionComponent,\n ErrorCode,\n FailedEvent,\n ForecastPoint,\n Geography,\n JobEvent,\n JobRequest,\n JobStatus,\n JobSubmitResponse,\n NLJobRequest,\n OrakleConfig,\n PartialEvent,\n PipelineStage,\n PriceCheckOptions,\n PriceCheckResult,\n PriceDistribution,\n ProductProfile,\n Scenario,\n ScenarioForecast,\n ScenarioSource,\n SSEFrame,\n StageEvent,\n StructuredJobRequest,\n ValueDriver,\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(\n signal.reason ?? new DOMException(\"Aborted\", \"AbortError\"),\n );\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 { createSSEStream, SSEHttpError } from \"./sse.js\";\nimport type {\n ApiErrorBody,\n CompletedEvent,\n ErrorCode,\n FailedEvent,\n JobEvent,\n JobRequest,\n JobSubmitResponse,\n OrakleConfig,\n PartialEvent,\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;\nconst MIN_TIMEOUT_MS = 180_000;\n\nexport class OrakleClient {\n private readonly apiKey: string;\n private readonly timeout: number;\n private readonly baseUrl: string;\n\n constructor(config: OrakleConfig) {\n this.apiKey = config.apiKey;\n this.timeout = Math.max(\n config.timeout ?? DEFAULT_TIMEOUT_MS,\n MIN_TIMEOUT_MS,\n );\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\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 = Math.max(\n options?.timeout ?? this.timeout,\n MIN_TIMEOUT_MS,\n );\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 \"partial\":\n options?.onPartial?.(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(\n \"SSE stream ended without a completed or failed event\",\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 * 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 = Math.max(\n options?.timeout ?? this.timeout,\n MIN_TIMEOUT_MS,\n );\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 } else if (event.event_type === \"partial\") {\n options?.onPartial?.(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(\"Failed to connect to Orakle API\", err);\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 \"partial\":\n return { ...parsed, event_type: \"partial\" } as PartialEvent;\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 =\n body?.code ?? this.httpStatusToErrorCode(response.status);\n const message =\n 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 =\n 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"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -3,25 +3,36 @@
|
|
|
3
3
|
*/
|
|
4
4
|
interface OrakleConfig {
|
|
5
5
|
apiKey: string;
|
|
6
|
-
baseUrl?: string;
|
|
7
6
|
timeout?: number;
|
|
7
|
+
/** Override the API base URL. Defaults to https://api.orakle.xyz. */
|
|
8
|
+
baseUrl?: string;
|
|
8
9
|
}
|
|
9
10
|
type JobStatus = "pending" | "processing" | "completed" | "failed";
|
|
10
11
|
type PipelineStage = "analyzing" | "price_lookup" | "planning" | "dispatching" | "fusing";
|
|
11
12
|
type ErrorCode = "invalid_request" | "auth_required" | "rate_limited" | "job_not_found" | "insufficient_data" | "internal_error" | "service_unavailable";
|
|
12
13
|
type Geography = "US" | "GB" | "CA" | "AU" | "AE";
|
|
14
|
+
interface AttributeCorrection {
|
|
15
|
+
key: string;
|
|
16
|
+
original: string;
|
|
17
|
+
corrected: string;
|
|
18
|
+
reason: string;
|
|
19
|
+
}
|
|
20
|
+
interface ValueDriver {
|
|
21
|
+
/** Snake_case attribute key matching an entry in `attributes`. */
|
|
22
|
+
key: string;
|
|
23
|
+
/** Attribute value as it appears in `attributes`. */
|
|
24
|
+
value: string;
|
|
25
|
+
}
|
|
13
26
|
interface ProductProfile {
|
|
14
27
|
category: string;
|
|
15
28
|
make: string;
|
|
16
29
|
model: string;
|
|
17
|
-
storage?: string | null;
|
|
18
30
|
condition?: string | null;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
color?: string | null;
|
|
31
|
+
attributes?: Record<string, string>;
|
|
32
|
+
corrections?: AttributeCorrection[];
|
|
22
33
|
geography?: Geography;
|
|
23
34
|
region?: string | null;
|
|
24
|
-
|
|
35
|
+
value_drivers?: ValueDriver[];
|
|
25
36
|
missing_attributes?: string[];
|
|
26
37
|
}
|
|
27
38
|
interface DistributionComponent {
|
|
@@ -52,6 +63,13 @@ interface ScenarioForecast {
|
|
|
52
63
|
base_price: number;
|
|
53
64
|
weighted_expected_price: number;
|
|
54
65
|
}
|
|
66
|
+
interface ForecastPoint {
|
|
67
|
+
date: string;
|
|
68
|
+
segment: "historical" | "forecast";
|
|
69
|
+
price: number;
|
|
70
|
+
p10?: number;
|
|
71
|
+
p90?: number;
|
|
72
|
+
}
|
|
55
73
|
interface NLJobRequest {
|
|
56
74
|
request_type: "nl";
|
|
57
75
|
query: string;
|
|
@@ -69,11 +87,14 @@ interface PriceCheckResult {
|
|
|
69
87
|
product_profile: ProductProfile;
|
|
70
88
|
fused_distribution: PriceDistribution;
|
|
71
89
|
scenario_forecast?: ScenarioForecast | null;
|
|
90
|
+
forecast_series?: ForecastPoint[];
|
|
91
|
+
forecast_narrative?: string;
|
|
72
92
|
}
|
|
73
93
|
interface JobSubmitResponse {
|
|
74
94
|
job_id: string;
|
|
75
95
|
status: JobStatus;
|
|
76
96
|
poll_url: string;
|
|
97
|
+
stream_url: string;
|
|
77
98
|
estimated_seconds: number;
|
|
78
99
|
}
|
|
79
100
|
interface StageEvent {
|
|
@@ -83,6 +104,18 @@ interface StageEvent {
|
|
|
83
104
|
message: string;
|
|
84
105
|
timestamp: string;
|
|
85
106
|
}
|
|
107
|
+
interface PartialEvent {
|
|
108
|
+
event_type: "partial";
|
|
109
|
+
job_id: string;
|
|
110
|
+
/**
|
|
111
|
+
* Subset of PriceCheckResult that the orchestrator emits before the
|
|
112
|
+
* full job completes. Today this is `{ product_profile }` after the
|
|
113
|
+
* Analyzing stage; future stages may add `fused_distribution` etc.
|
|
114
|
+
*/
|
|
115
|
+
result: Partial<PriceCheckResult>;
|
|
116
|
+
message?: string;
|
|
117
|
+
timestamp: string;
|
|
118
|
+
}
|
|
86
119
|
interface CompletedEvent {
|
|
87
120
|
event_type: "completed";
|
|
88
121
|
job_id: string;
|
|
@@ -97,11 +130,24 @@ interface FailedEvent {
|
|
|
97
130
|
message?: string;
|
|
98
131
|
timestamp: string;
|
|
99
132
|
}
|
|
100
|
-
type JobEvent = StageEvent | CompletedEvent | FailedEvent;
|
|
133
|
+
type JobEvent = StageEvent | PartialEvent | CompletedEvent | FailedEvent;
|
|
134
|
+
interface SSEFrame {
|
|
135
|
+
event: string;
|
|
136
|
+
id: string;
|
|
137
|
+
data: string;
|
|
138
|
+
}
|
|
101
139
|
interface PriceCheckOptions {
|
|
102
140
|
signal?: AbortSignal;
|
|
103
141
|
timeout?: number;
|
|
104
142
|
onProgress?: (event: StageEvent) => void;
|
|
143
|
+
/**
|
|
144
|
+
* Fires once with the analyzed product profile (and any other early
|
|
145
|
+
* fields) the moment the Analyzing stage finishes — well before the
|
|
146
|
+
* full result is ready. Use this to populate UI placeholders so users
|
|
147
|
+
* see the parsed query immediately. The shape is a partial
|
|
148
|
+
* PriceCheckResult; today it carries `{ product_profile }`.
|
|
149
|
+
*/
|
|
150
|
+
onPartial?: (event: PartialEvent) => void;
|
|
105
151
|
}
|
|
106
152
|
interface ApiErrorBody {
|
|
107
153
|
code: ErrorCode;
|
|
@@ -119,8 +165,8 @@ interface ApiErrorBody {
|
|
|
119
165
|
|
|
120
166
|
declare class OrakleClient {
|
|
121
167
|
private readonly apiKey;
|
|
122
|
-
private readonly baseUrl;
|
|
123
168
|
private readonly timeout;
|
|
169
|
+
private readonly baseUrl;
|
|
124
170
|
constructor(config: OrakleConfig);
|
|
125
171
|
/**
|
|
126
172
|
* Submit a pricing job and wait for the result.
|
|
@@ -171,4 +217,37 @@ declare class OrakleConnectionError extends OrakleError {
|
|
|
171
217
|
constructor(message: string, cause?: unknown);
|
|
172
218
|
}
|
|
173
219
|
|
|
174
|
-
|
|
220
|
+
/**
|
|
221
|
+
* SSE parser using fetch + ReadableStream.
|
|
222
|
+
*
|
|
223
|
+
* Cannot use browser EventSource — it doesn't support custom headers
|
|
224
|
+
* (needed for X-Orakle-API-Key). This is the same approach used by
|
|
225
|
+
* the OpenAI SDK. Works in browsers and Node.js 18+ (native fetch).
|
|
226
|
+
*/
|
|
227
|
+
|
|
228
|
+
interface SSEOptions {
|
|
229
|
+
url: string;
|
|
230
|
+
headers: Record<string, string>;
|
|
231
|
+
signal?: AbortSignal;
|
|
232
|
+
lastEventId?: string;
|
|
233
|
+
onRetry?: (retryMs: number) => void;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Parse a `text/event-stream` response into SSE frames.
|
|
237
|
+
*
|
|
238
|
+
* Yields `{ event, id, data }` for each complete SSE event block.
|
|
239
|
+
* Handles multi-line `data:` fields (joined with newlines).
|
|
240
|
+
* Supports reconnection via `Last-Event-Id` header.
|
|
241
|
+
*/
|
|
242
|
+
declare const createSSEStream: (options: SSEOptions) => AsyncGenerator<SSEFrame>;
|
|
243
|
+
/**
|
|
244
|
+
* Internal error used to communicate HTTP status from the SSE fetch.
|
|
245
|
+
* The client layer catches this and maps it to OrakleApiError.
|
|
246
|
+
*/
|
|
247
|
+
declare class SSEHttpError extends Error {
|
|
248
|
+
readonly status: number;
|
|
249
|
+
readonly body: string;
|
|
250
|
+
constructor(status: number, body: string);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export { type ApiErrorBody, type AttributeCorrection, type CompletedEvent, type DistributionComponent, type ErrorCode, type FailedEvent, type ForecastPoint, type Geography, type JobEvent, type JobRequest, type JobStatus, type JobSubmitResponse, type NLJobRequest, OrakleApiError, OrakleClient, type OrakleConfig, OrakleConnectionError, OrakleError, OrakleTimeoutError, type PartialEvent, type PipelineStage, type PriceCheckOptions, type PriceCheckResult, type PriceDistribution, type ProductProfile, type SSEFrame, SSEHttpError, type Scenario, type ScenarioForecast, type ScenarioSource, type StageEvent, type StructuredJobRequest, type ValueDriver, createSSEStream };
|
package/dist/index.d.ts
CHANGED
|
@@ -3,25 +3,36 @@
|
|
|
3
3
|
*/
|
|
4
4
|
interface OrakleConfig {
|
|
5
5
|
apiKey: string;
|
|
6
|
-
baseUrl?: string;
|
|
7
6
|
timeout?: number;
|
|
7
|
+
/** Override the API base URL. Defaults to https://api.orakle.xyz. */
|
|
8
|
+
baseUrl?: string;
|
|
8
9
|
}
|
|
9
10
|
type JobStatus = "pending" | "processing" | "completed" | "failed";
|
|
10
11
|
type PipelineStage = "analyzing" | "price_lookup" | "planning" | "dispatching" | "fusing";
|
|
11
12
|
type ErrorCode = "invalid_request" | "auth_required" | "rate_limited" | "job_not_found" | "insufficient_data" | "internal_error" | "service_unavailable";
|
|
12
13
|
type Geography = "US" | "GB" | "CA" | "AU" | "AE";
|
|
14
|
+
interface AttributeCorrection {
|
|
15
|
+
key: string;
|
|
16
|
+
original: string;
|
|
17
|
+
corrected: string;
|
|
18
|
+
reason: string;
|
|
19
|
+
}
|
|
20
|
+
interface ValueDriver {
|
|
21
|
+
/** Snake_case attribute key matching an entry in `attributes`. */
|
|
22
|
+
key: string;
|
|
23
|
+
/** Attribute value as it appears in `attributes`. */
|
|
24
|
+
value: string;
|
|
25
|
+
}
|
|
13
26
|
interface ProductProfile {
|
|
14
27
|
category: string;
|
|
15
28
|
make: string;
|
|
16
29
|
model: string;
|
|
17
|
-
storage?: string | null;
|
|
18
30
|
condition?: string | null;
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
color?: string | null;
|
|
31
|
+
attributes?: Record<string, string>;
|
|
32
|
+
corrections?: AttributeCorrection[];
|
|
22
33
|
geography?: Geography;
|
|
23
34
|
region?: string | null;
|
|
24
|
-
|
|
35
|
+
value_drivers?: ValueDriver[];
|
|
25
36
|
missing_attributes?: string[];
|
|
26
37
|
}
|
|
27
38
|
interface DistributionComponent {
|
|
@@ -52,6 +63,13 @@ interface ScenarioForecast {
|
|
|
52
63
|
base_price: number;
|
|
53
64
|
weighted_expected_price: number;
|
|
54
65
|
}
|
|
66
|
+
interface ForecastPoint {
|
|
67
|
+
date: string;
|
|
68
|
+
segment: "historical" | "forecast";
|
|
69
|
+
price: number;
|
|
70
|
+
p10?: number;
|
|
71
|
+
p90?: number;
|
|
72
|
+
}
|
|
55
73
|
interface NLJobRequest {
|
|
56
74
|
request_type: "nl";
|
|
57
75
|
query: string;
|
|
@@ -69,11 +87,14 @@ interface PriceCheckResult {
|
|
|
69
87
|
product_profile: ProductProfile;
|
|
70
88
|
fused_distribution: PriceDistribution;
|
|
71
89
|
scenario_forecast?: ScenarioForecast | null;
|
|
90
|
+
forecast_series?: ForecastPoint[];
|
|
91
|
+
forecast_narrative?: string;
|
|
72
92
|
}
|
|
73
93
|
interface JobSubmitResponse {
|
|
74
94
|
job_id: string;
|
|
75
95
|
status: JobStatus;
|
|
76
96
|
poll_url: string;
|
|
97
|
+
stream_url: string;
|
|
77
98
|
estimated_seconds: number;
|
|
78
99
|
}
|
|
79
100
|
interface StageEvent {
|
|
@@ -83,6 +104,18 @@ interface StageEvent {
|
|
|
83
104
|
message: string;
|
|
84
105
|
timestamp: string;
|
|
85
106
|
}
|
|
107
|
+
interface PartialEvent {
|
|
108
|
+
event_type: "partial";
|
|
109
|
+
job_id: string;
|
|
110
|
+
/**
|
|
111
|
+
* Subset of PriceCheckResult that the orchestrator emits before the
|
|
112
|
+
* full job completes. Today this is `{ product_profile }` after the
|
|
113
|
+
* Analyzing stage; future stages may add `fused_distribution` etc.
|
|
114
|
+
*/
|
|
115
|
+
result: Partial<PriceCheckResult>;
|
|
116
|
+
message?: string;
|
|
117
|
+
timestamp: string;
|
|
118
|
+
}
|
|
86
119
|
interface CompletedEvent {
|
|
87
120
|
event_type: "completed";
|
|
88
121
|
job_id: string;
|
|
@@ -97,11 +130,24 @@ interface FailedEvent {
|
|
|
97
130
|
message?: string;
|
|
98
131
|
timestamp: string;
|
|
99
132
|
}
|
|
100
|
-
type JobEvent = StageEvent | CompletedEvent | FailedEvent;
|
|
133
|
+
type JobEvent = StageEvent | PartialEvent | CompletedEvent | FailedEvent;
|
|
134
|
+
interface SSEFrame {
|
|
135
|
+
event: string;
|
|
136
|
+
id: string;
|
|
137
|
+
data: string;
|
|
138
|
+
}
|
|
101
139
|
interface PriceCheckOptions {
|
|
102
140
|
signal?: AbortSignal;
|
|
103
141
|
timeout?: number;
|
|
104
142
|
onProgress?: (event: StageEvent) => void;
|
|
143
|
+
/**
|
|
144
|
+
* Fires once with the analyzed product profile (and any other early
|
|
145
|
+
* fields) the moment the Analyzing stage finishes — well before the
|
|
146
|
+
* full result is ready. Use this to populate UI placeholders so users
|
|
147
|
+
* see the parsed query immediately. The shape is a partial
|
|
148
|
+
* PriceCheckResult; today it carries `{ product_profile }`.
|
|
149
|
+
*/
|
|
150
|
+
onPartial?: (event: PartialEvent) => void;
|
|
105
151
|
}
|
|
106
152
|
interface ApiErrorBody {
|
|
107
153
|
code: ErrorCode;
|
|
@@ -119,8 +165,8 @@ interface ApiErrorBody {
|
|
|
119
165
|
|
|
120
166
|
declare class OrakleClient {
|
|
121
167
|
private readonly apiKey;
|
|
122
|
-
private readonly baseUrl;
|
|
123
168
|
private readonly timeout;
|
|
169
|
+
private readonly baseUrl;
|
|
124
170
|
constructor(config: OrakleConfig);
|
|
125
171
|
/**
|
|
126
172
|
* Submit a pricing job and wait for the result.
|
|
@@ -171,4 +217,37 @@ declare class OrakleConnectionError extends OrakleError {
|
|
|
171
217
|
constructor(message: string, cause?: unknown);
|
|
172
218
|
}
|
|
173
219
|
|
|
174
|
-
|
|
220
|
+
/**
|
|
221
|
+
* SSE parser using fetch + ReadableStream.
|
|
222
|
+
*
|
|
223
|
+
* Cannot use browser EventSource — it doesn't support custom headers
|
|
224
|
+
* (needed for X-Orakle-API-Key). This is the same approach used by
|
|
225
|
+
* the OpenAI SDK. Works in browsers and Node.js 18+ (native fetch).
|
|
226
|
+
*/
|
|
227
|
+
|
|
228
|
+
interface SSEOptions {
|
|
229
|
+
url: string;
|
|
230
|
+
headers: Record<string, string>;
|
|
231
|
+
signal?: AbortSignal;
|
|
232
|
+
lastEventId?: string;
|
|
233
|
+
onRetry?: (retryMs: number) => void;
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Parse a `text/event-stream` response into SSE frames.
|
|
237
|
+
*
|
|
238
|
+
* Yields `{ event, id, data }` for each complete SSE event block.
|
|
239
|
+
* Handles multi-line `data:` fields (joined with newlines).
|
|
240
|
+
* Supports reconnection via `Last-Event-Id` header.
|
|
241
|
+
*/
|
|
242
|
+
declare const createSSEStream: (options: SSEOptions) => AsyncGenerator<SSEFrame>;
|
|
243
|
+
/**
|
|
244
|
+
* Internal error used to communicate HTTP status from the SSE fetch.
|
|
245
|
+
* The client layer catches this and maps it to OrakleApiError.
|
|
246
|
+
*/
|
|
247
|
+
declare class SSEHttpError extends Error {
|
|
248
|
+
readonly status: number;
|
|
249
|
+
readonly body: string;
|
|
250
|
+
constructor(status: number, body: string);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export { type ApiErrorBody, type AttributeCorrection, type CompletedEvent, type DistributionComponent, type ErrorCode, type FailedEvent, type ForecastPoint, type Geography, type JobEvent, type JobRequest, type JobStatus, type JobSubmitResponse, type NLJobRequest, OrakleApiError, OrakleClient, type OrakleConfig, OrakleConnectionError, OrakleError, OrakleTimeoutError, type PartialEvent, type PipelineStage, type PriceCheckOptions, type PriceCheckResult, type PriceDistribution, type ProductProfile, type SSEFrame, SSEHttpError, type Scenario, type ScenarioForecast, type ScenarioSource, type StageEvent, type StructuredJobRequest, type ValueDriver, createSSEStream };
|
package/dist/index.js
CHANGED
|
@@ -78,7 +78,9 @@ var parseSSEBody = async function* (body, signal) {
|
|
|
78
78
|
let currentData = [];
|
|
79
79
|
const readOrAbort = () => {
|
|
80
80
|
if (signal?.aborted) {
|
|
81
|
-
return Promise.reject(
|
|
81
|
+
return Promise.reject(
|
|
82
|
+
signal.reason ?? new DOMException("Aborted", "AbortError")
|
|
83
|
+
);
|
|
82
84
|
}
|
|
83
85
|
return new Promise((resolve, reject) => {
|
|
84
86
|
const onAbort = () => {
|
|
@@ -206,14 +208,18 @@ var SSEHttpError = class extends Error {
|
|
|
206
208
|
// src/client.ts
|
|
207
209
|
var DEFAULT_BASE_URL = "https://api.orakle.xyz";
|
|
208
210
|
var DEFAULT_TIMEOUT_MS = 18e4;
|
|
211
|
+
var MIN_TIMEOUT_MS = 18e4;
|
|
209
212
|
var OrakleClient = class {
|
|
210
213
|
apiKey;
|
|
211
|
-
baseUrl;
|
|
212
214
|
timeout;
|
|
215
|
+
baseUrl;
|
|
213
216
|
constructor(config) {
|
|
214
217
|
this.apiKey = config.apiKey;
|
|
218
|
+
this.timeout = Math.max(
|
|
219
|
+
config.timeout ?? DEFAULT_TIMEOUT_MS,
|
|
220
|
+
MIN_TIMEOUT_MS
|
|
221
|
+
);
|
|
215
222
|
this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
|
|
216
|
-
this.timeout = config.timeout ?? DEFAULT_TIMEOUT_MS;
|
|
217
223
|
}
|
|
218
224
|
/**
|
|
219
225
|
* Submit a pricing job and wait for the result.
|
|
@@ -221,7 +227,10 @@ var OrakleClient = class {
|
|
|
221
227
|
* Internally: POST /v1/jobs -> open SSE stream -> resolve on completed, reject on failed.
|
|
222
228
|
*/
|
|
223
229
|
async priceCheck(params, options) {
|
|
224
|
-
const timeoutMs =
|
|
230
|
+
const timeoutMs = Math.max(
|
|
231
|
+
options?.timeout ?? this.timeout,
|
|
232
|
+
MIN_TIMEOUT_MS
|
|
233
|
+
);
|
|
225
234
|
const controller = new AbortController();
|
|
226
235
|
const externalSignal = options?.signal;
|
|
227
236
|
const onExternalAbort = () => controller.abort();
|
|
@@ -244,13 +253,18 @@ var OrakleClient = class {
|
|
|
244
253
|
case "stage":
|
|
245
254
|
options?.onProgress?.(event);
|
|
246
255
|
break;
|
|
256
|
+
case "partial":
|
|
257
|
+
options?.onPartial?.(event);
|
|
258
|
+
break;
|
|
247
259
|
case "completed":
|
|
248
260
|
return event.result;
|
|
249
261
|
case "failed":
|
|
250
262
|
throw new OrakleError(event.error);
|
|
251
263
|
}
|
|
252
264
|
}
|
|
253
|
-
throw new OrakleError(
|
|
265
|
+
throw new OrakleError(
|
|
266
|
+
"SSE stream ended without a completed or failed event"
|
|
267
|
+
);
|
|
254
268
|
} catch (err) {
|
|
255
269
|
throw this.normalizeError(err);
|
|
256
270
|
} finally {
|
|
@@ -264,7 +278,10 @@ var OrakleClient = class {
|
|
|
264
278
|
* Yields StageEvent and CompletedEvent/FailedEvent as they arrive.
|
|
265
279
|
*/
|
|
266
280
|
async *priceCheckStream(params, options) {
|
|
267
|
-
const timeoutMs =
|
|
281
|
+
const timeoutMs = Math.max(
|
|
282
|
+
options?.timeout ?? this.timeout,
|
|
283
|
+
MIN_TIMEOUT_MS
|
|
284
|
+
);
|
|
268
285
|
const controller = new AbortController();
|
|
269
286
|
const externalSignal = options?.signal;
|
|
270
287
|
const onExternalAbort = () => controller.abort();
|
|
@@ -285,6 +302,8 @@ var OrakleClient = class {
|
|
|
285
302
|
if (!event) continue;
|
|
286
303
|
if (event.event_type === "stage") {
|
|
287
304
|
options?.onProgress?.(event);
|
|
305
|
+
} else if (event.event_type === "partial") {
|
|
306
|
+
options?.onPartial?.(event);
|
|
288
307
|
}
|
|
289
308
|
yield event;
|
|
290
309
|
if (event.event_type === "completed" || event.event_type === "failed") {
|
|
@@ -317,10 +336,7 @@ var OrakleClient = class {
|
|
|
317
336
|
if (err instanceof DOMException && err.name === "AbortError") {
|
|
318
337
|
throw err;
|
|
319
338
|
}
|
|
320
|
-
throw new OrakleConnectionError(
|
|
321
|
-
"Failed to connect to Orakle API",
|
|
322
|
-
err
|
|
323
|
-
);
|
|
339
|
+
throw new OrakleConnectionError("Failed to connect to Orakle API", err);
|
|
324
340
|
}
|
|
325
341
|
if (!response.ok) {
|
|
326
342
|
throw await this.buildApiError(response);
|
|
@@ -339,6 +355,8 @@ var OrakleClient = class {
|
|
|
339
355
|
switch (eventType) {
|
|
340
356
|
case "stage":
|
|
341
357
|
return { ...parsed, event_type: "stage" };
|
|
358
|
+
case "partial":
|
|
359
|
+
return { ...parsed, event_type: "partial" };
|
|
342
360
|
case "completed":
|
|
343
361
|
return { ...parsed, event_type: "completed" };
|
|
344
362
|
case "failed":
|
|
@@ -404,6 +422,8 @@ export {
|
|
|
404
422
|
OrakleClient,
|
|
405
423
|
OrakleConnectionError,
|
|
406
424
|
OrakleError,
|
|
407
|
-
OrakleTimeoutError
|
|
425
|
+
OrakleTimeoutError,
|
|
426
|
+
SSEHttpError,
|
|
427
|
+
createSSEStream
|
|
408
428
|
};
|
|
409
429
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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":"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","names":["body"]}
|
|
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(\n signal.reason ?? new DOMException(\"Aborted\", \"AbortError\"),\n );\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 { createSSEStream, SSEHttpError } from \"./sse.js\";\nimport type {\n ApiErrorBody,\n CompletedEvent,\n ErrorCode,\n FailedEvent,\n JobEvent,\n JobRequest,\n JobSubmitResponse,\n OrakleConfig,\n PartialEvent,\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;\nconst MIN_TIMEOUT_MS = 180_000;\n\nexport class OrakleClient {\n private readonly apiKey: string;\n private readonly timeout: number;\n private readonly baseUrl: string;\n\n constructor(config: OrakleConfig) {\n this.apiKey = config.apiKey;\n this.timeout = Math.max(\n config.timeout ?? DEFAULT_TIMEOUT_MS,\n MIN_TIMEOUT_MS,\n );\n this.baseUrl = (config.baseUrl ?? DEFAULT_BASE_URL).replace(/\\/+$/, \"\");\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 = Math.max(\n options?.timeout ?? this.timeout,\n MIN_TIMEOUT_MS,\n );\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 \"partial\":\n options?.onPartial?.(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(\n \"SSE stream ended without a completed or failed event\",\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 * 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 = Math.max(\n options?.timeout ?? this.timeout,\n MIN_TIMEOUT_MS,\n );\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 } else if (event.event_type === \"partial\") {\n options?.onPartial?.(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(\"Failed to connect to Orakle API\", err);\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 \"partial\":\n return { ...parsed, event_type: \"partial\" } as PartialEvent;\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 =\n body?.code ?? this.httpStatusToErrorCode(response.status);\n const message =\n 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 =\n 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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "orakle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Client SDK for orakle.xyz",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"node": ">=18.0.0"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
|
-
"@biomejs/biome": "
|
|
31
|
+
"@biomejs/biome": "2.4.12",
|
|
32
32
|
"@types/node": "^22.15.3",
|
|
33
33
|
"tsup": "^8.4.0",
|
|
34
34
|
"typescript": "^5.8.3",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
},
|
|
37
37
|
"scripts": {
|
|
38
38
|
"build": "tsup",
|
|
39
|
-
"
|
|
39
|
+
"check": "tsc --noEmit && biome check",
|
|
40
40
|
"test": "vitest run",
|
|
41
41
|
"test:watch": "vitest"
|
|
42
42
|
}
|