@oxy-hq/sdk 2.12.0 → 2.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +67 -0
- package/dist/{function-context-D8eyZuw_.d.cts → function-context-BNpL5bFb.d.cts} +223 -20
- package/dist/function-context-BNpL5bFb.d.cts.map +1 -0
- package/dist/{function-context-D8eyZuw_.d.mts → function-context-BNpL5bFb.d.mts} +223 -20
- package/dist/function-context-BNpL5bFb.d.mts.map +1 -0
- package/dist/index.cjs +80 -16
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +164 -3
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +164 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +78 -16
- package/dist/index.mjs.map +1 -1
- package/dist/ops.d.cts +1 -1
- package/dist/ops.d.mts +1 -1
- package/dist/{react-DW7Z96sD.d.mts → react-CljeXJuw.d.cts} +142 -8
- package/dist/react-CljeXJuw.d.cts.map +1 -0
- package/dist/{react-DW7Z96sD.d.cts → react-CljeXJuw.d.mts} +142 -8
- package/dist/react-CljeXJuw.d.mts.map +1 -0
- package/dist/{react-DcT-mUPj.cjs → react-Dvkv2deI.cjs} +110 -59
- package/dist/react-Dvkv2deI.cjs.map +1 -0
- package/dist/{react-BXGyzgz0.mjs → react-OW1t_J0M.mjs} +103 -26
- package/dist/react-OW1t_J0M.mjs.map +1 -0
- package/dist/rolldown-runtime-KC0qvQup.cjs +34 -0
- package/dist/shell.cjs +38 -3
- package/dist/shell.cjs.map +1 -1
- package/dist/shell.d.cts +26 -3
- package/dist/shell.d.cts.map +1 -1
- package/dist/shell.d.mts +26 -3
- package/dist/shell.d.mts.map +1 -1
- package/dist/shell.mjs +34 -2
- package/dist/shell.mjs.map +1 -1
- package/dist/testing.cjs +4431 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +656 -0
- package/dist/testing.d.cts.map +1 -0
- package/dist/testing.d.mts +656 -0
- package/dist/testing.d.mts.map +1 -0
- package/dist/testing.mjs +4395 -0
- package/dist/testing.mjs.map +1 -0
- package/package.json +22 -9
- package/dist/function-context-D8eyZuw_.d.cts.map +0 -1
- package/dist/function-context-D8eyZuw_.d.mts.map +0 -1
- package/dist/react-BXGyzgz0.mjs.map +0 -1
- package/dist/react-DW7Z96sD.d.cts.map +0 -1
- package/dist/react-DW7Z96sD.d.mts.map +0 -1
- package/dist/react-DcT-mUPj.cjs.map +0 -1
|
@@ -87,6 +87,25 @@ async function apiErrorFromResponse(resp) {
|
|
|
87
87
|
message: snippet || `HTTP ${resp.status}`
|
|
88
88
|
});
|
|
89
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Normalize a rejected request into the `Error` a hook reports.
|
|
92
|
+
*
|
|
93
|
+
* An `AbortError` reaching a hook's `.catch` is by construction an abort the
|
|
94
|
+
* hook did NOT cause — its own teardown is marked separately — so it is a
|
|
95
|
+
* fetcher's request timeout, a dropped socket, or a navigation. The platform's
|
|
96
|
+
* wording for those ("The user aborted a request.") is wrong on its face by
|
|
97
|
+
* the time a bundle renders it, so rephrase and keep the original as `cause`.
|
|
98
|
+
*
|
|
99
|
+
* The name is used only to WORD an error already being reported. Whether to
|
|
100
|
+
* report at all is the caller's teardown flag, never the name — that
|
|
101
|
+
* conflation is what left hooks loading forever.
|
|
102
|
+
*/
|
|
103
|
+
function asReportableError(err) {
|
|
104
|
+
const name = typeof err === "object" && err !== null && "name" in err ? String(err.name) : "";
|
|
105
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
106
|
+
if (name !== "AbortError" && e.name !== "AbortError") return e;
|
|
107
|
+
return Object.assign(/* @__PURE__ */ new Error("request was interrupted (the connection dropped or timed out)"), { cause: e });
|
|
108
|
+
}
|
|
90
109
|
const ARCH_DOC = "internal-docs/customer-apps.md";
|
|
91
110
|
/** Interpret a thrown error as a structured report for UI display. */
|
|
92
111
|
function interpretCustomAppError(err) {
|
|
@@ -299,7 +318,7 @@ const FUNCTION_NAME_RE = /^[a-z][a-z0-9-]{0,63}$/;
|
|
|
299
318
|
*
|
|
300
319
|
* This runs at manifest LOAD (app boot / `pnpm dev`), not at `oxy build` — the
|
|
301
320
|
* build-time gate is the vite plugin's `validateManifest`, which now checks
|
|
302
|
-
* function names too. `
|
|
321
|
+
* function names too. `oxyc publish` also validates them locally before esbuild,
|
|
303
322
|
* so a bad name fails before the upload regardless.
|
|
304
323
|
*/
|
|
305
324
|
function validateFunctions(raw) {
|
|
@@ -338,6 +357,10 @@ function validateFunctions(raw) {
|
|
|
338
357
|
if (typeof t !== "number" || !Number.isInteger(t) || t < 1 || t > 300) throw new Error(`oxy-app.json: function "${fnName}" \`timeoutSeconds\` must be an integer in [1, 300]`);
|
|
339
358
|
fn.timeoutSeconds = t;
|
|
340
359
|
}
|
|
360
|
+
if (value.check !== void 0) {
|
|
361
|
+
if (typeof value.check !== "boolean") throw new Error(`oxy-app.json: function "${fnName}" \`check\` must be a boolean`);
|
|
362
|
+
fn.check = value.check;
|
|
363
|
+
}
|
|
341
364
|
if (value.cache !== void 0) {
|
|
342
365
|
const c = value.cache;
|
|
343
366
|
if (!isRecord(c)) throw new Error(`oxy-app.json: function "${fnName}" \`cache\` must be an object`);
|
|
@@ -364,10 +387,25 @@ function validateFunctions(raw) {
|
|
|
364
387
|
}
|
|
365
388
|
fn.retries = retries;
|
|
366
389
|
}
|
|
390
|
+
if (value.webhook !== void 0) {
|
|
391
|
+
const w = value.webhook;
|
|
392
|
+
if (!isRecord(w)) throw new Error(`oxy-app.json: function "${fnName}" \`webhook\` must be an object`);
|
|
393
|
+
for (const key of ["secretVar", "signatureHeader"]) if (typeof w[key] !== "string" || !w[key].trim()) throw new Error(`oxy-app.json: function "${fnName}" \`webhook.${key}\` must be a non-empty string`);
|
|
394
|
+
const webhook = {
|
|
395
|
+
secretVar: w.secretVar,
|
|
396
|
+
signatureHeader: w.signatureHeader
|
|
397
|
+
};
|
|
398
|
+
if (w.encoding !== void 0) {
|
|
399
|
+
if (w.encoding !== "hex" && w.encoding !== "base64") throw new Error(`oxy-app.json: function "${fnName}" \`webhook.encoding\` must be "hex" or "base64"`);
|
|
400
|
+
webhook.encoding = w.encoding;
|
|
401
|
+
}
|
|
402
|
+
fn.webhook = webhook;
|
|
403
|
+
}
|
|
367
404
|
if (value.inputExample !== void 0) fn.inputExample = value.inputExample;
|
|
368
405
|
const hasSchedule = fn.schedule !== void 0;
|
|
369
406
|
const hasAirway = fn.airwayStep !== void 0;
|
|
370
|
-
|
|
407
|
+
const hasWebhook = fn.webhook !== void 0;
|
|
408
|
+
if (!(fn.route ?? !(hasSchedule || hasAirway || hasWebhook)) && !hasSchedule && !hasAirway && !hasWebhook) throw new Error(`oxy-app.json: function "${fnName}" must enable at least one of route/schedule/airwayStep/webhook`);
|
|
371
409
|
out[fnName] = fn;
|
|
372
410
|
}
|
|
373
411
|
return out;
|
|
@@ -621,6 +659,44 @@ async function sharedQuery(fetcher, projectId, sql, db, opts = {}) {
|
|
|
621
659
|
return p;
|
|
622
660
|
}
|
|
623
661
|
|
|
662
|
+
//#endregion
|
|
663
|
+
//#region src/custom-app/traceparent.ts
|
|
664
|
+
const HEX = "0123456789abcdef";
|
|
665
|
+
function randomHex(bytes) {
|
|
666
|
+
const buf = new Uint8Array(bytes);
|
|
667
|
+
const c = globalThis.crypto;
|
|
668
|
+
if (c && typeof c.getRandomValues === "function") c.getRandomValues(buf);
|
|
669
|
+
else for (let i = 0; i < bytes; i++) buf[i] = Math.floor(Math.random() * 256);
|
|
670
|
+
let out = "";
|
|
671
|
+
for (let i = 0; i < bytes; i++) out += HEX[buf[i] >> 4] + HEX[buf[i] & 15];
|
|
672
|
+
return out;
|
|
673
|
+
}
|
|
674
|
+
/** Mint a fresh, sampled `traceparent`. All-zero ids are invalid per the spec;
|
|
675
|
+
* the loop guards the astronomically unlikely draw. */
|
|
676
|
+
function newTraceparent() {
|
|
677
|
+
let traceId = randomHex(16);
|
|
678
|
+
while (/^0+$/.test(traceId)) traceId = randomHex(16);
|
|
679
|
+
let spanId = randomHex(8);
|
|
680
|
+
while (/^0+$/.test(spanId)) spanId = randomHex(8);
|
|
681
|
+
return {
|
|
682
|
+
header: `00-${traceId}-${spanId}-01`,
|
|
683
|
+
traceId
|
|
684
|
+
};
|
|
685
|
+
}
|
|
686
|
+
/**
|
|
687
|
+
* Stamp the ids of a failed invoke onto whatever was thrown, so the app (and
|
|
688
|
+
* the platform's error beacon, which reads `traceId`) can name the trace and
|
|
689
|
+
* the server-minted request id. Non-objects are returned untouched.
|
|
690
|
+
*/
|
|
691
|
+
function withInvocationIds(err, traceId, requestId) {
|
|
692
|
+
if (err && typeof err === "object") {
|
|
693
|
+
const target = err;
|
|
694
|
+
if (!target.traceId) target.traceId = traceId;
|
|
695
|
+
if (requestId && !target.requestId) target.requestId = requestId;
|
|
696
|
+
}
|
|
697
|
+
return err;
|
|
698
|
+
}
|
|
699
|
+
|
|
624
700
|
//#endregion
|
|
625
701
|
//#region src/custom-app/react.tsx
|
|
626
702
|
function defaultFetcher(input, init) {
|
|
@@ -839,7 +915,7 @@ function useQuery(input, opts = {}) {
|
|
|
839
915
|
setState((s) => ({
|
|
840
916
|
...s,
|
|
841
917
|
loading: false,
|
|
842
|
-
error:
|
|
918
|
+
error: asReportableError(err)
|
|
843
919
|
}));
|
|
844
920
|
});
|
|
845
921
|
return () => {
|
|
@@ -893,9 +969,11 @@ function useFunction(name) {
|
|
|
893
969
|
error: null
|
|
894
970
|
}));
|
|
895
971
|
try {
|
|
972
|
+
const trace = newTraceparent();
|
|
896
973
|
const headers = {
|
|
897
974
|
"content-type": "application/json",
|
|
898
|
-
accept: "text/event-stream"
|
|
975
|
+
accept: "text/event-stream",
|
|
976
|
+
traceparent: trace.header
|
|
899
977
|
};
|
|
900
978
|
if (opts?.idempotencyKey) headers["idempotency-key"] = opts.idempotencyKey;
|
|
901
979
|
const result = await sharedFunctionInvoke(functionInvokeKey(name, body), async () => {
|
|
@@ -904,8 +982,13 @@ function useFunction(name) {
|
|
|
904
982
|
headers,
|
|
905
983
|
body: JSON.stringify(body ?? {})
|
|
906
984
|
});
|
|
907
|
-
|
|
908
|
-
|
|
985
|
+
const requestId = resp.headers?.get?.("x-oxy-request-id") ?? null;
|
|
986
|
+
if (!resp.ok && resp.status !== 200) throw withInvocationIds(await apiErrorFromResponse(resp), trace.traceId, requestId);
|
|
987
|
+
try {
|
|
988
|
+
return await readFunctionSseStream(resp);
|
|
989
|
+
} catch (err) {
|
|
990
|
+
throw withInvocationIds(err, trace.traceId, requestId);
|
|
991
|
+
}
|
|
909
992
|
});
|
|
910
993
|
setState({
|
|
911
994
|
data: result.value,
|
|
@@ -1010,11 +1093,10 @@ function useSemanticQuery(input, opts = {}) {
|
|
|
1010
1093
|
});
|
|
1011
1094
|
}).catch((err) => {
|
|
1012
1095
|
if (cancelled) return;
|
|
1013
|
-
if (err instanceof DOMException && err.name === "AbortError") return;
|
|
1014
1096
|
setState((s) => ({
|
|
1015
1097
|
...s,
|
|
1016
1098
|
loading: false,
|
|
1017
|
-
error:
|
|
1099
|
+
error: asReportableError(err)
|
|
1018
1100
|
}));
|
|
1019
1101
|
});
|
|
1020
1102
|
return () => {
|
|
@@ -1171,13 +1253,13 @@ function useProcedureRun(input, opts = {}) {
|
|
|
1171
1253
|
return;
|
|
1172
1254
|
}
|
|
1173
1255
|
} catch (e) {
|
|
1174
|
-
if (
|
|
1256
|
+
if (ctrl.signal.aborted) return;
|
|
1175
1257
|
inflight.current.runId = void 0;
|
|
1176
1258
|
setState({
|
|
1177
1259
|
state: "failed",
|
|
1178
1260
|
progress: null,
|
|
1179
1261
|
result: null,
|
|
1180
|
-
error:
|
|
1262
|
+
error: asReportableError(e)
|
|
1181
1263
|
});
|
|
1182
1264
|
}
|
|
1183
1265
|
})();
|
|
@@ -1278,6 +1360,8 @@ function useAgentRun(input) {
|
|
|
1278
1360
|
if (ctrl.signal.aborted) return;
|
|
1279
1361
|
if (terminated) return;
|
|
1280
1362
|
attempts += 1;
|
|
1363
|
+
const idBeforeAttempt = lastEventId;
|
|
1364
|
+
let windowError;
|
|
1281
1365
|
try {
|
|
1282
1366
|
await consumeSseStream({
|
|
1283
1367
|
url: `/api/projects/${projectId}/agents/runs/${encodeURIComponent(run_id)}/events`,
|
|
@@ -1327,36 +1411,29 @@ function useAgentRun(input) {
|
|
|
1327
1411
|
}
|
|
1328
1412
|
});
|
|
1329
1413
|
} catch (err) {
|
|
1330
|
-
if (
|
|
1331
|
-
|
|
1332
|
-
inflight.current.runId = void 0;
|
|
1333
|
-
setState((s) => ({
|
|
1334
|
-
...s,
|
|
1335
|
-
state: "failed",
|
|
1336
|
-
error: err instanceof Error ? err : new Error(String(err))
|
|
1337
|
-
}));
|
|
1338
|
-
return;
|
|
1339
|
-
}
|
|
1414
|
+
if (ctrl.signal.aborted) return;
|
|
1415
|
+
windowError = err;
|
|
1340
1416
|
}
|
|
1341
1417
|
if (terminated) return;
|
|
1342
|
-
if (
|
|
1418
|
+
if (lastEventId !== idBeforeAttempt) attempts = 0;
|
|
1419
|
+
else if (attempts >= 5) {
|
|
1343
1420
|
inflight.current.runId = void 0;
|
|
1344
1421
|
setState((s) => ({
|
|
1345
1422
|
...s,
|
|
1346
1423
|
state: "failed",
|
|
1347
|
-
error: /* @__PURE__ */ new Error("run event stream closed without a terminal event")
|
|
1424
|
+
error: windowError ? asReportableError(windowError) : /* @__PURE__ */ new Error("run event stream closed without a terminal event")
|
|
1348
1425
|
}));
|
|
1349
1426
|
return;
|
|
1350
1427
|
}
|
|
1351
1428
|
await sleep(1e3, ctrl.signal);
|
|
1352
1429
|
}
|
|
1353
1430
|
} catch (e) {
|
|
1354
|
-
if (
|
|
1431
|
+
if (ctrl.signal.aborted) return;
|
|
1355
1432
|
inflight.current.runId = void 0;
|
|
1356
1433
|
setState((s) => ({
|
|
1357
1434
|
...s,
|
|
1358
1435
|
state: "failed",
|
|
1359
|
-
error:
|
|
1436
|
+
error: asReportableError(e)
|
|
1360
1437
|
}));
|
|
1361
1438
|
}
|
|
1362
1439
|
})();
|
|
@@ -2296,5 +2373,5 @@ const styles = {
|
|
|
2296
2373
|
};
|
|
2297
2374
|
|
|
2298
2375
|
//#endregion
|
|
2299
|
-
export {
|
|
2300
|
-
//# sourceMappingURL=react-
|
|
2376
|
+
export { asReportableError as _, useFunction as a, setOxyAppLogger as b, useQuery as c, useTrackEvent as d, _resetCustomAppManifestCacheForTest as f, apiErrorFromResponse as g, OxyApiError as h, useAgentRun as i, useResolvedManifest as l, readInjectedAppConfig as m, OxyAppProvider as n, useOxyApp as o, loadCustomAppManifest as p, OxyChat as r, useProcedureRun as s, OxyAnswer as t, useSemanticQuery as u, interpretCustomAppError as v, getOxyAppLogger as y };
|
|
2377
|
+
//# sourceMappingURL=react-OW1t_J0M.mjs.map
|