orakle 0.0.1 → 0.0.3
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 +440 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +174 -0
- package/dist/index.d.ts +174 -0
- package/dist/index.js +409 -0
- package/dist/index.js.map +1 -0
- package/package.json +40 -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
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"]}
|