raindrop-ai 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -0
- package/dist/{chunk-DP5ED5DR.mjs → chunk-UICWZEYM.mjs} +251 -48
- package/dist/index.d.mts +34 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +358 -72
- package/dist/index.mjs +130 -25
- package/dist/tracing/index.js +229 -48
- package/dist/tracing/index.mjs +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -5771,7 +5771,9 @@ __export(index_exports, {
|
|
|
5771
5771
|
});
|
|
5772
5772
|
module.exports = __toCommonJS(index_exports);
|
|
5773
5773
|
|
|
5774
|
-
// ../core/dist/chunk-
|
|
5774
|
+
// ../core/dist/chunk-U5HUTMR5.js
|
|
5775
|
+
var DEFAULT_REQUEST_TIMEOUT_MS = 3e4;
|
|
5776
|
+
var MAX_RETRY_DELAY_MS = 3e4;
|
|
5775
5777
|
function wait(ms) {
|
|
5776
5778
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
5777
5779
|
}
|
|
@@ -5779,6 +5781,46 @@ function formatEndpoint(endpoint) {
|
|
|
5779
5781
|
if (!endpoint) return void 0;
|
|
5780
5782
|
return endpoint.endsWith("/") ? endpoint : `${endpoint}/`;
|
|
5781
5783
|
}
|
|
5784
|
+
function redactUrlForLog(url) {
|
|
5785
|
+
try {
|
|
5786
|
+
const parsed = new URL(url);
|
|
5787
|
+
parsed.username = "";
|
|
5788
|
+
parsed.password = "";
|
|
5789
|
+
parsed.search = "";
|
|
5790
|
+
parsed.hash = "";
|
|
5791
|
+
return parsed.toString();
|
|
5792
|
+
} catch (e) {
|
|
5793
|
+
return "<unparseable-url>";
|
|
5794
|
+
}
|
|
5795
|
+
}
|
|
5796
|
+
var RATE_LIMITED_LOG_INTERVAL_MS = 3e4;
|
|
5797
|
+
var rateLimitedLogLast = /* @__PURE__ */ new Map();
|
|
5798
|
+
function rateLimitedLog(key, log) {
|
|
5799
|
+
const now = Date.now();
|
|
5800
|
+
const last = rateLimitedLogLast.get(key);
|
|
5801
|
+
if (last !== void 0 && now - last < RATE_LIMITED_LOG_INTERVAL_MS) {
|
|
5802
|
+
return false;
|
|
5803
|
+
}
|
|
5804
|
+
rateLimitedLogLast.set(key, now);
|
|
5805
|
+
log();
|
|
5806
|
+
return true;
|
|
5807
|
+
}
|
|
5808
|
+
async function raceWithTimeout(promise, timeoutMs) {
|
|
5809
|
+
let timer;
|
|
5810
|
+
const settledInTime = await Promise.race([
|
|
5811
|
+
promise.then(
|
|
5812
|
+
() => true,
|
|
5813
|
+
() => true
|
|
5814
|
+
),
|
|
5815
|
+
new Promise((resolve) => {
|
|
5816
|
+
var _a;
|
|
5817
|
+
timer = setTimeout(() => resolve(false), Math.max(0, timeoutMs));
|
|
5818
|
+
(_a = timer.unref) == null ? void 0 : _a.call(timer);
|
|
5819
|
+
})
|
|
5820
|
+
]);
|
|
5821
|
+
if (timer) clearTimeout(timer);
|
|
5822
|
+
return settledInTime;
|
|
5823
|
+
}
|
|
5782
5824
|
function parseRetryAfter(headers) {
|
|
5783
5825
|
var _a;
|
|
5784
5826
|
const value = (_a = headers.get("Retry-After")) != null ? _a : headers.get("retry-after");
|
|
@@ -5795,12 +5837,12 @@ function parseRetryAfter(headers) {
|
|
|
5795
5837
|
function getRetryDelayMs(attemptNumber, previousError) {
|
|
5796
5838
|
if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
|
|
5797
5839
|
const v = previousError.retryAfterMs;
|
|
5798
|
-
if (typeof v === "number") return Math.max(0, v);
|
|
5840
|
+
if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
|
|
5799
5841
|
}
|
|
5800
5842
|
if (attemptNumber <= 1) return 0;
|
|
5801
5843
|
const base = 500;
|
|
5802
5844
|
const factor = Math.pow(2, attemptNumber - 2);
|
|
5803
|
-
return base * factor;
|
|
5845
|
+
return Math.min(base * factor, MAX_RETRY_DELAY_MS);
|
|
5804
5846
|
}
|
|
5805
5847
|
async function withRetry(operation, opName, opts) {
|
|
5806
5848
|
const prefix = opts.sdkName ? `[raindrop-ai/${opts.sdkName}]` : "[raindrop-ai/core]";
|
|
@@ -5835,7 +5877,9 @@ async function withRetry(operation, opName, opts) {
|
|
|
5835
5877
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
5836
5878
|
}
|
|
5837
5879
|
async function postJson(url, body, headers, opts) {
|
|
5838
|
-
|
|
5880
|
+
var _a;
|
|
5881
|
+
const opName = `POST ${redactUrlForLog(url)}`;
|
|
5882
|
+
const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
5839
5883
|
await withRetry(
|
|
5840
5884
|
async () => {
|
|
5841
5885
|
const resp = await fetch(url, {
|
|
@@ -5844,7 +5888,8 @@ async function postJson(url, body, headers, opts) {
|
|
|
5844
5888
|
"Content-Type": "application/json",
|
|
5845
5889
|
...headers
|
|
5846
5890
|
},
|
|
5847
|
-
body: JSON.stringify(body)
|
|
5891
|
+
body: JSON.stringify(body),
|
|
5892
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
5848
5893
|
});
|
|
5849
5894
|
if (!resp.ok) {
|
|
5850
5895
|
const text = await resp.text().catch(() => "");
|
|
@@ -5861,6 +5906,108 @@ async function postJson(url, body, headers, opts) {
|
|
|
5861
5906
|
opts
|
|
5862
5907
|
);
|
|
5863
5908
|
}
|
|
5909
|
+
var DEFAULT_MAX_TEXT_FIELD_CHARS = 1e6;
|
|
5910
|
+
var TRUNCATION_MARKER = "...[truncated by raindrop]";
|
|
5911
|
+
var BOUNDED_CLONE_MAX_DEPTH = 12;
|
|
5912
|
+
var BOUNDED_CLONE_SLACK = 256;
|
|
5913
|
+
var currentDefaultMaxTextFieldChars = DEFAULT_MAX_TEXT_FIELD_CHARS;
|
|
5914
|
+
function setDefaultMaxTextFieldChars(value) {
|
|
5915
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
5916
|
+
currentDefaultMaxTextFieldChars = Math.floor(value);
|
|
5917
|
+
}
|
|
5918
|
+
}
|
|
5919
|
+
function resolveMaxTextFieldChars(value) {
|
|
5920
|
+
if (typeof value === "number" && Number.isFinite(value) && value > 0) {
|
|
5921
|
+
return Math.floor(value);
|
|
5922
|
+
}
|
|
5923
|
+
return currentDefaultMaxTextFieldChars;
|
|
5924
|
+
}
|
|
5925
|
+
function truncateToLimit(text, limit) {
|
|
5926
|
+
if (limit > TRUNCATION_MARKER.length) {
|
|
5927
|
+
return text.slice(0, limit - TRUNCATION_MARKER.length) + TRUNCATION_MARKER;
|
|
5928
|
+
}
|
|
5929
|
+
return text.slice(0, Math.max(0, limit));
|
|
5930
|
+
}
|
|
5931
|
+
function capText(value, limit) {
|
|
5932
|
+
if (typeof value !== "string") return value;
|
|
5933
|
+
const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
|
|
5934
|
+
if (value.length <= max) return value;
|
|
5935
|
+
return truncateToLimit(value, max);
|
|
5936
|
+
}
|
|
5937
|
+
function boundedClone(obj, charBudget) {
|
|
5938
|
+
let budget = charBudget;
|
|
5939
|
+
const seen = /* @__PURE__ */ new WeakSet();
|
|
5940
|
+
const walk = (node, depth) => {
|
|
5941
|
+
if (budget <= 0) return TRUNCATION_MARKER;
|
|
5942
|
+
if (typeof node === "string") {
|
|
5943
|
+
if (node.length > budget) {
|
|
5944
|
+
const taken = node.slice(0, Math.max(0, budget)) + TRUNCATION_MARKER;
|
|
5945
|
+
budget = 0;
|
|
5946
|
+
return taken;
|
|
5947
|
+
}
|
|
5948
|
+
budget -= Math.max(node.length, 1);
|
|
5949
|
+
return node;
|
|
5950
|
+
}
|
|
5951
|
+
if (node === null || typeof node === "number" || typeof node === "boolean") {
|
|
5952
|
+
budget -= 8;
|
|
5953
|
+
return node;
|
|
5954
|
+
}
|
|
5955
|
+
if (typeof node !== "object") {
|
|
5956
|
+
budget -= 8;
|
|
5957
|
+
return node;
|
|
5958
|
+
}
|
|
5959
|
+
if (seen.has(node)) return "[CIRCULAR]";
|
|
5960
|
+
if (depth >= BOUNDED_CLONE_MAX_DEPTH) {
|
|
5961
|
+
budget -= 16;
|
|
5962
|
+
return `<max depth>`;
|
|
5963
|
+
}
|
|
5964
|
+
seen.add(node);
|
|
5965
|
+
if (Array.isArray(node)) {
|
|
5966
|
+
const out2 = [];
|
|
5967
|
+
for (const v of node) {
|
|
5968
|
+
if (budget <= 0) {
|
|
5969
|
+
out2.push(TRUNCATION_MARKER);
|
|
5970
|
+
break;
|
|
5971
|
+
}
|
|
5972
|
+
out2.push(walk(v, depth + 1));
|
|
5973
|
+
}
|
|
5974
|
+
return out2;
|
|
5975
|
+
}
|
|
5976
|
+
if (typeof node.toJSON === "function") {
|
|
5977
|
+
budget -= 16;
|
|
5978
|
+
return node;
|
|
5979
|
+
}
|
|
5980
|
+
const out = {};
|
|
5981
|
+
for (const k in node) {
|
|
5982
|
+
if (!Object.prototype.hasOwnProperty.call(node, k)) continue;
|
|
5983
|
+
if (budget <= 0) {
|
|
5984
|
+
out["..."] = TRUNCATION_MARKER;
|
|
5985
|
+
break;
|
|
5986
|
+
}
|
|
5987
|
+
const key = walk(k, depth + 1);
|
|
5988
|
+
out[key] = walk(node[k], depth + 1);
|
|
5989
|
+
}
|
|
5990
|
+
return out;
|
|
5991
|
+
};
|
|
5992
|
+
return walk(obj, 0);
|
|
5993
|
+
}
|
|
5994
|
+
function stringifyBounded(value, limit) {
|
|
5995
|
+
const max = limit != null ? limit : currentDefaultMaxTextFieldChars;
|
|
5996
|
+
if (typeof value === "string") {
|
|
5997
|
+
return capText(value, max);
|
|
5998
|
+
}
|
|
5999
|
+
const pruned = boundedClone(value, max + TRUNCATION_MARKER.length + BOUNDED_CLONE_SLACK);
|
|
6000
|
+
let json;
|
|
6001
|
+
try {
|
|
6002
|
+
json = JSON.stringify(pruned);
|
|
6003
|
+
} catch (e) {
|
|
6004
|
+
json = void 0;
|
|
6005
|
+
}
|
|
6006
|
+
if (json === void 0) {
|
|
6007
|
+
return capText(String(value), max);
|
|
6008
|
+
}
|
|
6009
|
+
return json.length <= max ? json : truncateToLimit(json, max);
|
|
6010
|
+
}
|
|
5864
6011
|
var LOCAL_DEBUGGER_ENV_VAR = "RAINDROP_LOCAL_DEBUGGER";
|
|
5865
6012
|
var WORKSHOP_ENV_VAR = "RAINDROP_WORKSHOP";
|
|
5866
6013
|
var DEFAULT_LOCAL_WORKSHOP_URL = "http://localhost:5899/v1/";
|
|
@@ -5969,6 +6116,13 @@ function sendLocalDebuggerLiveEvent(event, options = {}) {
|
|
|
5969
6116
|
).catch(() => {
|
|
5970
6117
|
});
|
|
5971
6118
|
}
|
|
6119
|
+
var SHUTDOWN_DEADLINE_MS = 1e4;
|
|
6120
|
+
var POST_SHUTDOWN_TIMEOUT_MS = 5e3;
|
|
6121
|
+
var DEFAULT_REDACT_ATTRIBUTE_KEYS = [
|
|
6122
|
+
"ai.request.providerOptions",
|
|
6123
|
+
"ai.response.providerMetadata"
|
|
6124
|
+
];
|
|
6125
|
+
var REDACT_JSON_ATTRIBUTE_KEYS = new Set(DEFAULT_REDACT_ATTRIBUTE_KEYS);
|
|
5972
6126
|
|
|
5973
6127
|
// ../core/dist/index.node.js
|
|
5974
6128
|
var import_async_hooks = require("async_hooks");
|
|
@@ -10082,7 +10236,7 @@ var SignalEventSchema = external_exports.object({
|
|
|
10082
10236
|
// package.json
|
|
10083
10237
|
var package_default = {
|
|
10084
10238
|
name: "raindrop-ai",
|
|
10085
|
-
version: "0.1.
|
|
10239
|
+
version: "0.1.2",
|
|
10086
10240
|
main: "dist/index.js",
|
|
10087
10241
|
module: "dist/index.mjs",
|
|
10088
10242
|
types: "dist/index.d.ts",
|
|
@@ -10326,12 +10480,12 @@ function parseRetryAfter2(headers) {
|
|
|
10326
10480
|
function getRetryDelayMs2(attemptNumber, previousError) {
|
|
10327
10481
|
if (previousError && typeof previousError === "object" && previousError !== null && "retryAfterMs" in previousError) {
|
|
10328
10482
|
const v = previousError.retryAfterMs;
|
|
10329
|
-
if (typeof v === "number") return Math.max(0, v);
|
|
10483
|
+
if (typeof v === "number") return Math.min(Math.max(0, v), MAX_RETRY_DELAY_MS);
|
|
10330
10484
|
}
|
|
10331
10485
|
if (attemptNumber <= 1) return 0;
|
|
10332
10486
|
const base = 500;
|
|
10333
10487
|
const factor = Math.pow(2, attemptNumber - 2);
|
|
10334
|
-
return base * factor;
|
|
10488
|
+
return Math.min(base * factor, MAX_RETRY_DELAY_MS);
|
|
10335
10489
|
}
|
|
10336
10490
|
async function withRetry2(operation, opName, opts) {
|
|
10337
10491
|
let lastError = void 0;
|
|
@@ -10363,7 +10517,9 @@ async function withRetry2(operation, opName, opts) {
|
|
|
10363
10517
|
throw lastError instanceof Error ? lastError : new Error(String(lastError));
|
|
10364
10518
|
}
|
|
10365
10519
|
async function postJson2(url, body, headers, opts) {
|
|
10366
|
-
|
|
10520
|
+
var _a;
|
|
10521
|
+
const opName = `POST ${redactUrlForLog(url)}`;
|
|
10522
|
+
const timeoutMs = (_a = opts.timeoutMs) != null ? _a : DEFAULT_REQUEST_TIMEOUT_MS;
|
|
10367
10523
|
await withRetry2(
|
|
10368
10524
|
async () => {
|
|
10369
10525
|
const resp = await fetch(url, {
|
|
@@ -10372,7 +10528,8 @@ async function postJson2(url, body, headers, opts) {
|
|
|
10372
10528
|
"Content-Type": "application/json",
|
|
10373
10529
|
...headers
|
|
10374
10530
|
},
|
|
10375
|
-
body: JSON.stringify(body)
|
|
10531
|
+
body: JSON.stringify(body),
|
|
10532
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
10376
10533
|
});
|
|
10377
10534
|
if (!resp.ok) {
|
|
10378
10535
|
const text = await resp.text().catch(() => "");
|
|
@@ -10520,16 +10677,19 @@ function getActiveTraceContext() {
|
|
|
10520
10677
|
|
|
10521
10678
|
// src/tracing/direct/shipper.ts
|
|
10522
10679
|
function serializeAssociationValue(value) {
|
|
10523
|
-
|
|
10524
|
-
return value;
|
|
10525
|
-
}
|
|
10526
|
-
const jsonValue = JSON.stringify(value);
|
|
10527
|
-
return jsonValue === void 0 ? String(value) : jsonValue;
|
|
10680
|
+
return stringifyBounded(value);
|
|
10528
10681
|
}
|
|
10529
10682
|
var DirectTraceShipper = class {
|
|
10530
10683
|
constructor(opts) {
|
|
10531
10684
|
this.queue = [];
|
|
10532
10685
|
this.inFlight = /* @__PURE__ */ new Set();
|
|
10686
|
+
/**
|
|
10687
|
+
* Set once `shutdown()` begins and never cleared. Sends issued after the
|
|
10688
|
+
* drain window (stragglers, or flush work the deadline abandoned
|
|
10689
|
+
* mid-drain) run as a single short attempt instead of regaining the full
|
|
10690
|
+
* retry schedule.
|
|
10691
|
+
*/
|
|
10692
|
+
this.hasShutdown = false;
|
|
10533
10693
|
var _a, _b, _c, _d, _e, _f;
|
|
10534
10694
|
this.writeKey = opts.writeKey;
|
|
10535
10695
|
this.baseUrl = (_a = formatEndpoint2(opts.endpoint)) != null ? _a : "https://api.raindrop.ai/v1/";
|
|
@@ -10654,14 +10814,19 @@ var DirectTraceShipper = class {
|
|
|
10654
10814
|
if (this.debug) console.log(`[raindrop] direct: dropped ${batch.length} cloud spans (local-only)`);
|
|
10655
10815
|
continue;
|
|
10656
10816
|
}
|
|
10817
|
+
const opts = this.requestOpts();
|
|
10818
|
+
if (!opts) {
|
|
10819
|
+
rateLimitedLog(
|
|
10820
|
+
"js-sdk.direct.shutdown_deadline",
|
|
10821
|
+
() => console.warn(
|
|
10822
|
+
`[raindrop] direct: shutdown flush deadline exceeded; dropping ${batch.length} spans`
|
|
10823
|
+
)
|
|
10824
|
+
);
|
|
10825
|
+
continue;
|
|
10826
|
+
}
|
|
10657
10827
|
const body = buildExportTraceServiceRequest2(batch);
|
|
10658
10828
|
const url = `${this.baseUrl}traces`;
|
|
10659
|
-
const p = postJson2(
|
|
10660
|
-
url,
|
|
10661
|
-
body,
|
|
10662
|
-
{ Authorization: `Bearer ${this.writeKey}` },
|
|
10663
|
-
{ maxAttempts: 3, debug: this.debug }
|
|
10664
|
-
);
|
|
10829
|
+
const p = postJson2(url, body, { Authorization: `Bearer ${this.writeKey}` }, opts);
|
|
10665
10830
|
this.inFlight.add(p);
|
|
10666
10831
|
try {
|
|
10667
10832
|
try {
|
|
@@ -10678,14 +10843,48 @@ var DirectTraceShipper = class {
|
|
|
10678
10843
|
}
|
|
10679
10844
|
}
|
|
10680
10845
|
}
|
|
10846
|
+
/**
|
|
10847
|
+
* Retry/timeout options for one batch POST, honoring the shutdown budget:
|
|
10848
|
+
* outside shutdown, the normal 3-attempt schedule; during shutdown, a
|
|
10849
|
+
* single attempt clamped to the remaining window; `null` once the window
|
|
10850
|
+
* is exhausted (caller drops the batch). After shutdown() returns,
|
|
10851
|
+
* stragglers run as a single short attempt.
|
|
10852
|
+
*/
|
|
10853
|
+
requestOpts() {
|
|
10854
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
10855
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
10856
|
+
if (remainingMs <= 0) return null;
|
|
10857
|
+
return {
|
|
10858
|
+
maxAttempts: 1,
|
|
10859
|
+
debug: this.debug,
|
|
10860
|
+
timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs)
|
|
10861
|
+
};
|
|
10862
|
+
}
|
|
10863
|
+
if (this.hasShutdown) {
|
|
10864
|
+
return { maxAttempts: 1, debug: this.debug, timeoutMs: POST_SHUTDOWN_TIMEOUT_MS };
|
|
10865
|
+
}
|
|
10866
|
+
return { maxAttempts: 3, debug: this.debug };
|
|
10867
|
+
}
|
|
10681
10868
|
async shutdown() {
|
|
10682
|
-
|
|
10683
|
-
|
|
10684
|
-
|
|
10869
|
+
this.hasShutdown = true;
|
|
10870
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
10871
|
+
try {
|
|
10872
|
+
if (this.timer) {
|
|
10873
|
+
clearTimeout(this.timer);
|
|
10874
|
+
this.timer = void 0;
|
|
10875
|
+
}
|
|
10876
|
+
const drain = async () => {
|
|
10877
|
+
await this.flush();
|
|
10878
|
+
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
10879
|
+
})));
|
|
10880
|
+
};
|
|
10881
|
+
const settled = await raceWithTimeout(drain(), SHUTDOWN_DEADLINE_MS);
|
|
10882
|
+
if (!settled && this.debug) {
|
|
10883
|
+
console.warn("[raindrop] direct: shutdown deadline exceeded; abandoning in-flight spans");
|
|
10884
|
+
}
|
|
10885
|
+
} finally {
|
|
10886
|
+
this.shutdownDeadlineAt = void 0;
|
|
10685
10887
|
}
|
|
10686
|
-
await this.flush();
|
|
10687
|
-
await Promise.all([...this.inFlight].map((p) => p.catch(() => {
|
|
10688
|
-
})));
|
|
10689
10888
|
}
|
|
10690
10889
|
};
|
|
10691
10890
|
|
|
@@ -10701,7 +10900,7 @@ function getPropertiesFromContext(context5) {
|
|
|
10701
10900
|
...context5.properties || {}
|
|
10702
10901
|
};
|
|
10703
10902
|
if (context5.attachments && context5.attachments.length > 0) {
|
|
10704
|
-
properties.attachments =
|
|
10903
|
+
properties.attachments = stringifyBounded(context5.attachments);
|
|
10705
10904
|
}
|
|
10706
10905
|
return properties;
|
|
10707
10906
|
}
|
|
@@ -10719,11 +10918,7 @@ function serializeAssociationProperties(properties) {
|
|
|
10719
10918
|
);
|
|
10720
10919
|
}
|
|
10721
10920
|
function serializeSpanValue(value) {
|
|
10722
|
-
|
|
10723
|
-
return value;
|
|
10724
|
-
}
|
|
10725
|
-
const jsonValue = JSON.stringify(value);
|
|
10726
|
-
return jsonValue === void 0 ? String(value) : jsonValue;
|
|
10921
|
+
return stringifyBounded(value);
|
|
10727
10922
|
}
|
|
10728
10923
|
function getLocalDebuggerMetadata(context5) {
|
|
10729
10924
|
return {
|
|
@@ -11002,7 +11197,7 @@ var LiveInteraction = class {
|
|
|
11002
11197
|
error = err instanceof Error ? err.message : String(err);
|
|
11003
11198
|
const endTimeMs2 = Date.now();
|
|
11004
11199
|
const durationMs2 = endTimeMs2 - startTimeMs;
|
|
11005
|
-
const inputStr2 = params.inputParameters ?
|
|
11200
|
+
const inputStr2 = params.inputParameters ? stringifyBounded(params.inputParameters) : void 0;
|
|
11006
11201
|
this.directShipper.sendToolSpan({
|
|
11007
11202
|
name: toolName,
|
|
11008
11203
|
spanId: spanIdB64,
|
|
@@ -11031,8 +11226,8 @@ var LiveInteraction = class {
|
|
|
11031
11226
|
}
|
|
11032
11227
|
const endTimeMs = Date.now();
|
|
11033
11228
|
const durationMs = endTimeMs - startTimeMs;
|
|
11034
|
-
const inputStr = params.inputParameters ?
|
|
11035
|
-
const outputStr = result !== void 0 ?
|
|
11229
|
+
const inputStr = params.inputParameters ? stringifyBounded(params.inputParameters) : void 0;
|
|
11230
|
+
const outputStr = result !== void 0 ? stringifyBounded(result) : void 0;
|
|
11036
11231
|
this.directShipper.sendToolSpan({
|
|
11037
11232
|
name: toolName,
|
|
11038
11233
|
spanId: spanIdB64,
|
|
@@ -11269,8 +11464,8 @@ var LiveInteraction = class {
|
|
|
11269
11464
|
const traceIdB64 = this.ensureTraceId(activeContext.traceIdB64);
|
|
11270
11465
|
const { parentSpanIdB64 } = activeContext;
|
|
11271
11466
|
const spanIdB64 = base64Encode2(randomBytes2(8));
|
|
11272
|
-
const inputStr = input !== void 0 ?
|
|
11273
|
-
const outputStr = output !== void 0 ?
|
|
11467
|
+
const inputStr = input !== void 0 ? stringifyBounded(input) : void 0;
|
|
11468
|
+
const outputStr = output !== void 0 ? stringifyBounded(output) : void 0;
|
|
11274
11469
|
this.directShipper.sendToolSpan({
|
|
11275
11470
|
name,
|
|
11276
11471
|
spanId: spanIdB64,
|
|
@@ -11317,12 +11512,10 @@ var LiveInteraction = class {
|
|
|
11317
11512
|
span.setAttribute("traceloop.span.kind", "tool");
|
|
11318
11513
|
setAssociationProperties(span, properties);
|
|
11319
11514
|
if (input !== void 0) {
|
|
11320
|
-
|
|
11321
|
-
span.setAttribute("traceloop.entity.input", inputStr);
|
|
11515
|
+
span.setAttribute("traceloop.entity.input", stringifyBounded(input));
|
|
11322
11516
|
}
|
|
11323
11517
|
if (output !== void 0) {
|
|
11324
|
-
|
|
11325
|
-
span.setAttribute("traceloop.entity.output", outputStr);
|
|
11518
|
+
span.setAttribute("traceloop.entity.output", stringifyBounded(output));
|
|
11326
11519
|
}
|
|
11327
11520
|
if (durationMs !== void 0) {
|
|
11328
11521
|
span.setAttribute("traceloop.entity.duration_ms", durationMs);
|
|
@@ -11410,8 +11603,8 @@ var LiveTracer = class {
|
|
|
11410
11603
|
if (this.directShipper) {
|
|
11411
11604
|
const { traceIdB64, parentSpanIdB64 } = getActiveTraceContext();
|
|
11412
11605
|
const spanIdB64 = base64Encode2(randomBytes2(8));
|
|
11413
|
-
const inputStr = input !== void 0 ?
|
|
11414
|
-
const outputStr = output !== void 0 ?
|
|
11606
|
+
const inputStr = input !== void 0 ? stringifyBounded(input) : void 0;
|
|
11607
|
+
const outputStr = output !== void 0 ? stringifyBounded(output) : void 0;
|
|
11415
11608
|
this.directShipper.sendToolSpan({
|
|
11416
11609
|
name,
|
|
11417
11610
|
spanId: spanIdB64,
|
|
@@ -11455,12 +11648,10 @@ var LiveTracer = class {
|
|
|
11455
11648
|
});
|
|
11456
11649
|
span.setAttribute("traceloop.span.kind", "tool");
|
|
11457
11650
|
if (input !== void 0) {
|
|
11458
|
-
|
|
11459
|
-
span.setAttribute("traceloop.entity.input", inputStr);
|
|
11651
|
+
span.setAttribute("traceloop.entity.input", stringifyBounded(input));
|
|
11460
11652
|
}
|
|
11461
11653
|
if (output !== void 0) {
|
|
11462
|
-
|
|
11463
|
-
span.setAttribute("traceloop.entity.output", outputStr);
|
|
11654
|
+
span.setAttribute("traceloop.entity.output", stringifyBounded(output));
|
|
11464
11655
|
}
|
|
11465
11656
|
if (durationMs !== void 0) {
|
|
11466
11657
|
span.setAttribute("traceloop.entity.duration_ms", durationMs);
|
|
@@ -11892,6 +12083,7 @@ function getCurrentTraceId() {
|
|
|
11892
12083
|
return (_a = import_api4.trace.getSpan(import_api4.context.active())) == null ? void 0 : _a.spanContext().traceId;
|
|
11893
12084
|
}
|
|
11894
12085
|
var tracing = (analytics, apiUrl, writeKey, options, isDebug = false) => {
|
|
12086
|
+
process.env.TRACELOOP_TELEMETRY = "false";
|
|
11895
12087
|
const {
|
|
11896
12088
|
logLevel,
|
|
11897
12089
|
useExternalOtel,
|
|
@@ -12192,7 +12384,6 @@ var PartialClientAiTrack = ClientAiTrack.extend({
|
|
|
12192
12384
|
var AttachmentTypeSchema = import_zod.z.enum(["code", "text", "image", "iframe"]);
|
|
12193
12385
|
|
|
12194
12386
|
// src/index.ts
|
|
12195
|
-
process.env.TRACELOOP_TELEMETRY = "false";
|
|
12196
12387
|
var MAX_INGEST_SIZE_BYTES = 1 * 1024 * 1024;
|
|
12197
12388
|
var SELF_DIAGNOSTICS_TOOL_NAME_DEFAULT = "__raindrop_report";
|
|
12198
12389
|
var SELF_DIAGNOSTICS_SOURCE_DEFAULT = "agent_reporting_tool";
|
|
@@ -12317,12 +12508,12 @@ function parseRetryAfter3(headers) {
|
|
|
12317
12508
|
}
|
|
12318
12509
|
function getRetryDelayMs3(attemptNumber, previousError) {
|
|
12319
12510
|
if (previousError && typeof previousError.retryAfterMs === "number") {
|
|
12320
|
-
return Math.max(0, previousError.retryAfterMs);
|
|
12511
|
+
return Math.min(Math.max(0, previousError.retryAfterMs), MAX_RETRY_DELAY_MS);
|
|
12321
12512
|
}
|
|
12322
12513
|
if (attemptNumber <= 1) return 0;
|
|
12323
12514
|
const base = 500;
|
|
12324
12515
|
const factor = Math.pow(2, attemptNumber - 2);
|
|
12325
|
-
return base * factor;
|
|
12516
|
+
return Math.min(base * factor, MAX_RETRY_DELAY_MS);
|
|
12326
12517
|
}
|
|
12327
12518
|
async function withRetry3(operation, opName, maxAttempts, debugLogs = false) {
|
|
12328
12519
|
var _a;
|
|
@@ -12361,7 +12552,16 @@ var Raindrop = class {
|
|
|
12361
12552
|
this.partialEventBuffer = /* @__PURE__ */ new Map();
|
|
12362
12553
|
this.partialEventTimeouts = /* @__PURE__ */ new Map();
|
|
12363
12554
|
this.inFlightRequests = /* @__PURE__ */ new Set();
|
|
12555
|
+
/**
|
|
12556
|
+
* Set once `close()` begins and never cleared. Sends issued after the
|
|
12557
|
+
* drain window (stragglers, or flush work the deadline abandoned
|
|
12558
|
+
* mid-drain) run as a single short attempt instead of regaining the full
|
|
12559
|
+
* retry schedule.
|
|
12560
|
+
*/
|
|
12561
|
+
this.hasShutdown = false;
|
|
12364
12562
|
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k;
|
|
12563
|
+
setDefaultMaxTextFieldChars(config.maxTextFieldChars);
|
|
12564
|
+
this.maxTextFieldChars = resolveMaxTextFieldChars(config.maxTextFieldChars);
|
|
12365
12565
|
this.writeKey = (_a = config.writeKey) != null ? _a : "";
|
|
12366
12566
|
this.localDebuggerUrlOpt = config.localWorkshopUrl === false ? null : config.localWorkshopUrl;
|
|
12367
12567
|
this.apiUrl = (_b = this.formatEndpoint(config.endpoint)) != null ? _b : `https://api.raindrop.ai/v1/`;
|
|
@@ -12584,14 +12784,16 @@ var Raindrop = class {
|
|
|
12584
12784
|
eventId,
|
|
12585
12785
|
userId,
|
|
12586
12786
|
model,
|
|
12587
|
-
input,
|
|
12588
|
-
output,
|
|
12787
|
+
input: rawInput,
|
|
12788
|
+
output: rawOutput,
|
|
12589
12789
|
convoId,
|
|
12590
12790
|
attachments,
|
|
12591
12791
|
properties,
|
|
12592
12792
|
timestamp,
|
|
12593
12793
|
...rest
|
|
12594
12794
|
} = e;
|
|
12795
|
+
const input = capText(rawInput, this.maxTextFieldChars);
|
|
12796
|
+
const output = capText(rawOutput, this.maxTextFieldChars);
|
|
12595
12797
|
const customEventId = eventId != null ? eventId : crypto.randomUUID();
|
|
12596
12798
|
const parsedAttachments = AttachmentSchema.array().safeParse(attachments != null ? attachments : []);
|
|
12597
12799
|
if (!parsedAttachments.success) {
|
|
@@ -12798,20 +13000,51 @@ var Raindrop = class {
|
|
|
12798
13000
|
void fetch(`${localUrl}${endpoint}`, {
|
|
12799
13001
|
method: "POST",
|
|
12800
13002
|
headers,
|
|
12801
|
-
body: JSON.stringify(body)
|
|
13003
|
+
body: JSON.stringify(body),
|
|
13004
|
+
// Bounded like the cloud paths: a black-holed Workshop URL must not
|
|
13005
|
+
// pin connections indefinitely (fire-and-forget still holds sockets).
|
|
13006
|
+
signal: AbortSignal.timeout(DEFAULT_REQUEST_TIMEOUT_MS)
|
|
12802
13007
|
}).catch(() => {
|
|
12803
13008
|
});
|
|
12804
13009
|
} catch (e) {
|
|
12805
13010
|
}
|
|
12806
13011
|
}
|
|
13012
|
+
/**
|
|
13013
|
+
* Attempts/timeout budget for one POST, honoring the close() deadline.
|
|
13014
|
+
* Returns `null` when the shutdown budget is exhausted — the caller must
|
|
13015
|
+
* drop the payload instead of issuing a request that could outlive
|
|
13016
|
+
* process exit. Checked fresh on every send so a deadline that expires
|
|
13017
|
+
* mid-drain takes effect immediately.
|
|
13018
|
+
*/
|
|
13019
|
+
requestBudget() {
|
|
13020
|
+
if (this.shutdownDeadlineAt !== void 0) {
|
|
13021
|
+
const remainingMs = this.shutdownDeadlineAt - Date.now();
|
|
13022
|
+
if (remainingMs <= 0) return null;
|
|
13023
|
+
return { maxAttempts: 1, timeoutMs: Math.min(DEFAULT_REQUEST_TIMEOUT_MS, remainingMs) };
|
|
13024
|
+
}
|
|
13025
|
+
if (this.hasShutdown) {
|
|
13026
|
+
return { maxAttempts: 1, timeoutMs: POST_SHUTDOWN_TIMEOUT_MS };
|
|
13027
|
+
}
|
|
13028
|
+
return { maxAttempts: 3, timeoutMs: DEFAULT_REQUEST_TIMEOUT_MS };
|
|
13029
|
+
}
|
|
13030
|
+
warnShutdownDrop(what) {
|
|
13031
|
+
rateLimitedLog(
|
|
13032
|
+
"js-sdk.shutdown_deadline",
|
|
13033
|
+
() => console.warn(`[raindrop] shutdown flush deadline exceeded; dropping ${what}`)
|
|
13034
|
+
);
|
|
13035
|
+
}
|
|
12807
13036
|
async sendBatchToApi(events, endpoint) {
|
|
12808
|
-
var _a;
|
|
12809
13037
|
const body = events.map(({ type, ...event }) => event);
|
|
12810
13038
|
this.mirrorBatchToLocalDebugger(endpoint, body);
|
|
12811
13039
|
if (this.localOnly) {
|
|
12812
13040
|
return;
|
|
12813
13041
|
}
|
|
12814
|
-
const opName = `POST ${endpoint}`;
|
|
13042
|
+
const opName = `POST ${redactUrlForLog(this.apiUrl + endpoint)}`;
|
|
13043
|
+
const budget = this.requestBudget();
|
|
13044
|
+
if (!budget) {
|
|
13045
|
+
this.warnShutdownDrop(`${events.length} event(s) for ${endpoint}`);
|
|
13046
|
+
return;
|
|
13047
|
+
}
|
|
12815
13048
|
try {
|
|
12816
13049
|
const response = await withRetry3(
|
|
12817
13050
|
async () => {
|
|
@@ -12821,7 +13054,11 @@ var Raindrop = class {
|
|
|
12821
13054
|
"Content-Type": "application/json",
|
|
12822
13055
|
Authorization: `Bearer ${this.writeKey}`
|
|
12823
13056
|
},
|
|
12824
|
-
body: JSON.stringify(body)
|
|
13057
|
+
body: JSON.stringify(body),
|
|
13058
|
+
// Bounded: a dead or black-holed endpoint must never pin this
|
|
13059
|
+
// request (and the flush/close paths awaiting it) until the OS
|
|
13060
|
+
// gives up.
|
|
13061
|
+
signal: AbortSignal.timeout(budget.timeoutMs)
|
|
12825
13062
|
});
|
|
12826
13063
|
if (!resp.ok) {
|
|
12827
13064
|
const text = await resp.text().catch(() => "");
|
|
@@ -12835,7 +13072,7 @@ var Raindrop = class {
|
|
|
12835
13072
|
return resp;
|
|
12836
13073
|
},
|
|
12837
13074
|
opName,
|
|
12838
|
-
|
|
13075
|
+
budget.maxAttempts,
|
|
12839
13076
|
this.debugLogs
|
|
12840
13077
|
);
|
|
12841
13078
|
if (this.debugLogs) {
|
|
@@ -12844,9 +13081,18 @@ var Raindrop = class {
|
|
|
12844
13081
|
return response;
|
|
12845
13082
|
} catch (error) {
|
|
12846
13083
|
if (error == null ? void 0 : error.response) {
|
|
12847
|
-
|
|
13084
|
+
rateLimitedLog(
|
|
13085
|
+
`js-sdk.batch_failed.${endpoint}`,
|
|
13086
|
+
() => console.error(`[raindrop] Error in response (${opName}): ` + error.response.data)
|
|
13087
|
+
);
|
|
12848
13088
|
} else {
|
|
12849
|
-
|
|
13089
|
+
rateLimitedLog(
|
|
13090
|
+
`js-sdk.batch_failed.${endpoint}`,
|
|
13091
|
+
() => {
|
|
13092
|
+
var _a;
|
|
13093
|
+
return console.error(`[raindrop] Error (${opName}): ` + ((_a = error == null ? void 0 : error.message) != null ? _a : String(error)));
|
|
13094
|
+
}
|
|
13095
|
+
);
|
|
12850
13096
|
}
|
|
12851
13097
|
}
|
|
12852
13098
|
}
|
|
@@ -12903,6 +13149,12 @@ var Raindrop = class {
|
|
|
12903
13149
|
console.warn("[raindrop] trackAiPartial requires an eventId.");
|
|
12904
13150
|
return Promise.resolve();
|
|
12905
13151
|
}
|
|
13152
|
+
if (typeof rest.input === "string") {
|
|
13153
|
+
rest.input = capText(rest.input, this.maxTextFieldChars);
|
|
13154
|
+
}
|
|
13155
|
+
if (typeof rest.output === "string") {
|
|
13156
|
+
rest.output = capText(rest.output, this.maxTextFieldChars);
|
|
13157
|
+
}
|
|
12906
13158
|
const existingEvent = this.partialEventBuffer.get(eventId) || {};
|
|
12907
13159
|
const mergedEvent = this.deepMergeObjects(
|
|
12908
13160
|
{ ...existingEvent },
|
|
@@ -12933,7 +13185,7 @@ var Raindrop = class {
|
|
|
12933
13185
|
this.partialEventTimeouts.set(eventId, newTimeout);
|
|
12934
13186
|
if (this.debugLogs) {
|
|
12935
13187
|
console.log(
|
|
12936
|
-
`[raindrop] Updated partial event buffer for eventId: ${eventId}. Timeout reset (
|
|
13188
|
+
`[raindrop] Updated partial event buffer for eventId: ${eventId}. Timeout reset (2s). Event: ${stringifyBounded(mergedEvent, 2e3)}`
|
|
12937
13189
|
);
|
|
12938
13190
|
}
|
|
12939
13191
|
return Promise.resolve();
|
|
@@ -12949,9 +13201,12 @@ var Raindrop = class {
|
|
|
12949
13201
|
try {
|
|
12950
13202
|
await flushPromise;
|
|
12951
13203
|
} catch (error) {
|
|
12952
|
-
|
|
12953
|
-
|
|
12954
|
-
|
|
13204
|
+
rateLimitedLog(
|
|
13205
|
+
"js-sdk.partial_flush_failed",
|
|
13206
|
+
() => console.error(
|
|
13207
|
+
`[raindrop] Failed to flush partial event ${eventId}:`,
|
|
13208
|
+
error instanceof Error ? error : String(error)
|
|
13209
|
+
)
|
|
12955
13210
|
);
|
|
12956
13211
|
} finally {
|
|
12957
13212
|
this.inFlightRequests.delete(flushPromise);
|
|
@@ -13019,7 +13274,7 @@ var Raindrop = class {
|
|
|
13019
13274
|
const parsed = PartialClientAiTrack.partial().safeParse(eventToSend);
|
|
13020
13275
|
if (!parsed.success) {
|
|
13021
13276
|
console.warn(
|
|
13022
|
-
`[raindrop] Invalid accumulated data for partial eventId ${eventId}. Event not sent. Errors: ${this.formatZodError(parsed.error)} Data: ${
|
|
13277
|
+
`[raindrop] Invalid accumulated data for partial eventId ${eventId}. Event not sent. Errors: ${this.formatZodError(parsed.error)} Data: ${stringifyBounded(eventToSend, 2e3)}`
|
|
13023
13278
|
);
|
|
13024
13279
|
return;
|
|
13025
13280
|
}
|
|
@@ -13043,7 +13298,6 @@ var Raindrop = class {
|
|
|
13043
13298
|
* @param event - The event data conforming to ClientAiTrack schema.
|
|
13044
13299
|
*/
|
|
13045
13300
|
async sendPartialEvent(event) {
|
|
13046
|
-
var _a;
|
|
13047
13301
|
const endpoint = "events/track_partial";
|
|
13048
13302
|
const opName = `POST ${endpoint}`;
|
|
13049
13303
|
mirrorPartialEventToLocalDebugger(event, {
|
|
@@ -13055,6 +13309,11 @@ var Raindrop = class {
|
|
|
13055
13309
|
if (this.localOnly) {
|
|
13056
13310
|
return;
|
|
13057
13311
|
}
|
|
13312
|
+
const budget = this.requestBudget();
|
|
13313
|
+
if (!budget) {
|
|
13314
|
+
this.warnShutdownDrop(`partial event ${event.event_id}`);
|
|
13315
|
+
return;
|
|
13316
|
+
}
|
|
13058
13317
|
try {
|
|
13059
13318
|
const response = await withRetry3(
|
|
13060
13319
|
async () => {
|
|
@@ -13064,8 +13323,10 @@ var Raindrop = class {
|
|
|
13064
13323
|
"Content-Type": "application/json",
|
|
13065
13324
|
Authorization: `Bearer ${this.writeKey}`
|
|
13066
13325
|
},
|
|
13067
|
-
body: JSON.stringify(event)
|
|
13326
|
+
body: JSON.stringify(event),
|
|
13068
13327
|
// Send the single event object
|
|
13328
|
+
// Bounded: see sendBatchToApi.
|
|
13329
|
+
signal: AbortSignal.timeout(budget.timeoutMs)
|
|
13069
13330
|
});
|
|
13070
13331
|
if (!resp.ok) {
|
|
13071
13332
|
const errorBody = await resp.text().catch(() => "");
|
|
@@ -13079,7 +13340,7 @@ var Raindrop = class {
|
|
|
13079
13340
|
return resp;
|
|
13080
13341
|
},
|
|
13081
13342
|
opName,
|
|
13082
|
-
|
|
13343
|
+
budget.maxAttempts,
|
|
13083
13344
|
this.debugLogs
|
|
13084
13345
|
);
|
|
13085
13346
|
if (this.debugLogs) {
|
|
@@ -13089,8 +13350,14 @@ var Raindrop = class {
|
|
|
13089
13350
|
}
|
|
13090
13351
|
return response;
|
|
13091
13352
|
} catch (error) {
|
|
13092
|
-
|
|
13093
|
-
|
|
13353
|
+
rateLimitedLog(
|
|
13354
|
+
"js-sdk.partial_failed",
|
|
13355
|
+
() => {
|
|
13356
|
+
var _a;
|
|
13357
|
+
return console.error(
|
|
13358
|
+
`[raindrop] Failed to send partial event ${event.event_id} to ${endpoint}: ${(_a = error == null ? void 0 : error.message) != null ? _a : String(error)}`
|
|
13359
|
+
);
|
|
13360
|
+
}
|
|
13094
13361
|
);
|
|
13095
13362
|
throw error;
|
|
13096
13363
|
}
|
|
@@ -13109,7 +13376,21 @@ var Raindrop = class {
|
|
|
13109
13376
|
}
|
|
13110
13377
|
}
|
|
13111
13378
|
async close() {
|
|
13112
|
-
|
|
13379
|
+
this.hasShutdown = true;
|
|
13380
|
+
this.shutdownDeadlineAt = Date.now() + SHUTDOWN_DEADLINE_MS;
|
|
13381
|
+
try {
|
|
13382
|
+
await this.closeWithinDeadline();
|
|
13383
|
+
} finally {
|
|
13384
|
+
this.shutdownDeadlineAt = void 0;
|
|
13385
|
+
}
|
|
13386
|
+
}
|
|
13387
|
+
async closeWithinDeadline() {
|
|
13388
|
+
const remainingMs = () => {
|
|
13389
|
+
var _a;
|
|
13390
|
+
return Math.max(0, ((_a = this.shutdownDeadlineAt) != null ? _a : Date.now()) - Date.now());
|
|
13391
|
+
};
|
|
13392
|
+
const tracingSettled = await raceWithTimeout(this._tracing.close(), remainingMs());
|
|
13393
|
+
if (!tracingSettled) this.warnShutdownDrop("in-flight trace flush");
|
|
13113
13394
|
try {
|
|
13114
13395
|
await this.flush();
|
|
13115
13396
|
} catch (error) {
|
|
@@ -13138,7 +13419,12 @@ var Raindrop = class {
|
|
|
13138
13419
|
);
|
|
13139
13420
|
}
|
|
13140
13421
|
try {
|
|
13141
|
-
await
|
|
13422
|
+
const settled = await raceWithTimeout(
|
|
13423
|
+
Promise.allSettled(Array.from(this.inFlightRequests)).then(() => {
|
|
13424
|
+
}),
|
|
13425
|
+
remainingMs()
|
|
13426
|
+
);
|
|
13427
|
+
if (!settled) this.warnShutdownDrop("in-flight partial event request(s)");
|
|
13142
13428
|
} catch (e) {
|
|
13143
13429
|
console.error(`[raindrop] Error waiting for in-flight requests:`, e);
|
|
13144
13430
|
}
|