@alvin0/ai-agent-sdk-observability-fetch 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +187 -142
- package/dist/index.js.map +1 -1
- package/package.json +3 -3
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ObservationBoundary } from "@alvin0/ai-agent-sdk-core";
|
|
2
2
|
import { ExportAck, FlushResult, Observability, ObservationBatch, ObservationExporter, ObservationExporterPlugin } from "@alvin0/ai-agent-sdk-core/observability";
|
|
3
|
-
//#region src/fetch-
|
|
3
|
+
//#region src/fetch-options.d.ts
|
|
4
4
|
interface FetchObservationExporterOptions {
|
|
5
5
|
readonly id?: string;
|
|
6
6
|
readonly endpoint: string | URL;
|
|
@@ -23,6 +23,8 @@ interface FetchObservationExporterOptions {
|
|
|
23
23
|
/** Clock injection used only for HTTP-date `Retry-After`. */
|
|
24
24
|
readonly now?: () => number;
|
|
25
25
|
}
|
|
26
|
+
//#endregion
|
|
27
|
+
//#region src/fetch-exporter.d.ts
|
|
26
28
|
/** A remote-acknowledged exporter using only Fetch and Web streams. */
|
|
27
29
|
declare class FetchObservationExporter implements ObservationExporter {
|
|
28
30
|
readonly id: string;
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/fetch-exporter.ts","../src/wait-until.ts"],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/fetch-options.ts","../src/fetch-exporter.ts","../src/wait-until.ts"],"mappings":";;;UAYiB;WACN;WACA,mBAAmB;WACnB,UAAU,SAAS;WACnB,eAAe;;WAEf;WACA;WACA;WACA;WACA;WACA;WACA;WACA;WACA;;WAEA;;WAEA,SAAS,sBAAsB,QAAQ,gBAAgB;;WAEvD;;;;;cCfE,oCAAoC;WACtC;WACA,8BAA8B;mBACtB;EAEjB,YAAY,SAAS;EAWf,OAAO,OAAO,kBAAkB,QAAQ,cAAc,QAAQ;;;iBAQtD,yBACd,SAAS,kCACR;;;KCzCS,aAAa,SAAS;;iBAGlB,gCACd,aAAa,KAAK,yBAClB,WAAW,WACX,SAAS,cACR,QAAQ"}
|
package/dist/index.js
CHANGED
|
@@ -1,77 +1,16 @@
|
|
|
1
1
|
import { waitForSettlement } from "@alvin0/ai-agent-sdk-core";
|
|
2
2
|
import { defineObservationExporter } from "@alvin0/ai-agent-sdk-core/observability";
|
|
3
3
|
|
|
4
|
-
//#region src/fetch-
|
|
5
|
-
const DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
|
|
6
|
-
const DEFAULT_MAX_ATTEMPTS = 8;
|
|
7
|
-
const DEFAULT_BASE_DELAY_MS = 250;
|
|
8
|
-
const DEFAULT_MAX_DELAY_MS = 3e4;
|
|
9
|
-
const MAX_RETRY_AFTER_MS = 6e4;
|
|
10
|
-
const DEFAULT_MAX_BATCH_EVENTS = 256;
|
|
11
|
-
const DEFAULT_MAX_BATCH_BYTES = 524288;
|
|
12
|
-
const DEFAULT_MAX_ACK_BYTES = 65536;
|
|
13
|
-
const DEFAULT_MAX_ACK_CHUNKS = 1e3;
|
|
4
|
+
//#region src/fetch-transport.ts
|
|
14
5
|
const RETRYABLE_STATUSES = /* @__PURE__ */ new Set([
|
|
15
6
|
408,
|
|
16
7
|
425,
|
|
17
8
|
429
|
|
18
9
|
]);
|
|
19
|
-
const
|
|
20
|
-
"content-length",
|
|
21
|
-
"content-type",
|
|
22
|
-
"host",
|
|
23
|
-
"idempotency-key"
|
|
24
|
-
]);
|
|
10
|
+
const MAX_RETRY_AFTER_MS = 6e4;
|
|
25
11
|
var ObservationProtocolError = class extends Error {
|
|
26
12
|
name = "ObservationProtocolError";
|
|
27
13
|
};
|
|
28
|
-
function positiveSafeInteger(value, field) {
|
|
29
|
-
if (!Number.isSafeInteger(value) || value < 1) throw new RangeError(`${field} must be a positive safe integer`);
|
|
30
|
-
return value;
|
|
31
|
-
}
|
|
32
|
-
function nonNegativeFinite(value, field) {
|
|
33
|
-
if (!Number.isFinite(value) || value < 0) throw new RangeError(`${field} must be a non-negative finite number`);
|
|
34
|
-
return value;
|
|
35
|
-
}
|
|
36
|
-
function endpointOf(value, allowInsecureHttp) {
|
|
37
|
-
const endpoint = new URL(value);
|
|
38
|
-
if (endpoint.username.length > 0 || endpoint.password.length > 0) throw new TypeError("observation endpoint must not contain credentials");
|
|
39
|
-
const local = endpoint.hostname === "localhost" || endpoint.hostname === "127.0.0.1" || endpoint.hostname === "[::1]" || endpoint.hostname === "::1";
|
|
40
|
-
if (endpoint.protocol !== "https:" && !(allowInsecureHttp && endpoint.protocol === "http:" && local)) throw new TypeError("observation endpoint must use https");
|
|
41
|
-
return endpoint;
|
|
42
|
-
}
|
|
43
|
-
function headersOf(headers) {
|
|
44
|
-
const output = Object.create(null);
|
|
45
|
-
for (const [name, value] of Object.entries(headers ?? {})) {
|
|
46
|
-
const normalized = name.toLowerCase();
|
|
47
|
-
if (FORBIDDEN_HEADER_NAMES.has(normalized)) throw new TypeError(`observation header ${name} is managed by the exporter`);
|
|
48
|
-
if (typeof value !== "string" || !/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name) || /[\r\n]/.test(value)) throw new TypeError(`observation header ${name} is invalid`);
|
|
49
|
-
output[normalized] = value;
|
|
50
|
-
}
|
|
51
|
-
return Object.freeze(output);
|
|
52
|
-
}
|
|
53
|
-
function resolveOptions(options) {
|
|
54
|
-
if (typeof options?.fetch !== "function" && typeof globalThis.fetch !== "function") throw new TypeError("Fetch observation exporter requires fetch");
|
|
55
|
-
if (options.random !== void 0 && typeof options.random !== "function") throw new TypeError("random must be a function");
|
|
56
|
-
if (options.delay !== void 0 && typeof options.delay !== "function") throw new TypeError("delay must be a function");
|
|
57
|
-
if (options.now !== void 0 && typeof options.now !== "function") throw new TypeError("now must be a function");
|
|
58
|
-
return Object.freeze({
|
|
59
|
-
endpoint: endpointOf(options.endpoint, options.allowInsecureHttp ?? false),
|
|
60
|
-
headers: headersOf(options.headers),
|
|
61
|
-
fetch: options.fetch ?? globalThis.fetch,
|
|
62
|
-
requestTimeoutMs: positiveSafeInteger(options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS, "requestTimeoutMs"),
|
|
63
|
-
maxAttempts: positiveSafeInteger(options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS, "maxAttempts"),
|
|
64
|
-
baseDelayMs: nonNegativeFinite(options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS, "baseDelayMs"),
|
|
65
|
-
maxDelayMs: nonNegativeFinite(options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS, "maxDelayMs"),
|
|
66
|
-
maxBatchEvents: positiveSafeInteger(options.maxBatchEvents ?? DEFAULT_MAX_BATCH_EVENTS, "maxBatchEvents"),
|
|
67
|
-
maxBatchBytes: positiveSafeInteger(options.maxBatchBytes ?? DEFAULT_MAX_BATCH_BYTES, "maxBatchBytes"),
|
|
68
|
-
maxAckBytes: positiveSafeInteger(options.maxAckBytes ?? DEFAULT_MAX_ACK_BYTES, "maxAckBytes"),
|
|
69
|
-
maxAckChunks: positiveSafeInteger(options.maxAckChunks ?? DEFAULT_MAX_ACK_CHUNKS, "maxAckChunks"),
|
|
70
|
-
random: options.random ?? Math.random,
|
|
71
|
-
delay: options.delay ?? abortableDelay,
|
|
72
|
-
now: options.now ?? Date.now
|
|
73
|
-
});
|
|
74
|
-
}
|
|
75
14
|
function retryableStatus(status) {
|
|
76
15
|
return RETRYABLE_STATUSES.has(status) || status >= 500 && status <= 599;
|
|
77
16
|
}
|
|
@@ -190,103 +129,205 @@ async function validateNoRedirectResponse(response, endpoint) {
|
|
|
190
129
|
if (responseOrigin !== endpoint.origin) throw new ObservationProtocolError("observation response origin does not match its endpoint");
|
|
191
130
|
}
|
|
192
131
|
}
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
132
|
+
|
|
133
|
+
//#endregion
|
|
134
|
+
//#region src/fetch-options.ts
|
|
135
|
+
const DEFAULT_REQUEST_TIMEOUT_MS = 1e4;
|
|
136
|
+
const DEFAULT_MAX_ATTEMPTS = 8;
|
|
137
|
+
const DEFAULT_BASE_DELAY_MS = 250;
|
|
138
|
+
const DEFAULT_MAX_DELAY_MS = 3e4;
|
|
139
|
+
const DEFAULT_MAX_BATCH_EVENTS = 256;
|
|
140
|
+
const DEFAULT_MAX_BATCH_BYTES = 524288;
|
|
141
|
+
const DEFAULT_MAX_ACK_BYTES = 65536;
|
|
142
|
+
const DEFAULT_MAX_ACK_CHUNKS = 1e3;
|
|
143
|
+
const FORBIDDEN_HEADER_NAMES = /* @__PURE__ */ new Set([
|
|
144
|
+
"content-length",
|
|
145
|
+
"content-type",
|
|
146
|
+
"host",
|
|
147
|
+
"idempotency-key"
|
|
148
|
+
]);
|
|
149
|
+
function positiveSafeInteger(value, field) {
|
|
150
|
+
if (!Number.isSafeInteger(value) || value < 1) throw new RangeError(`${field} must be a positive safe integer`);
|
|
151
|
+
return value;
|
|
152
|
+
}
|
|
153
|
+
function nonNegativeFinite(value, field) {
|
|
154
|
+
if (!Number.isFinite(value) || value < 0) throw new RangeError(`${field} must be a non-negative finite number`);
|
|
155
|
+
return value;
|
|
156
|
+
}
|
|
157
|
+
function endpointOf(value, allowInsecureHttp) {
|
|
158
|
+
const endpoint = new URL(value);
|
|
159
|
+
if (endpoint.username.length > 0 || endpoint.password.length > 0) throw new TypeError("observation endpoint must not contain credentials");
|
|
160
|
+
const local = endpoint.hostname === "localhost" || endpoint.hostname === "127.0.0.1" || endpoint.hostname === "[::1]" || endpoint.hostname === "::1";
|
|
161
|
+
if (endpoint.protocol !== "https:" && !(allowInsecureHttp && endpoint.protocol === "http:" && local)) throw new TypeError("observation endpoint must use https");
|
|
162
|
+
return endpoint;
|
|
163
|
+
}
|
|
164
|
+
function headersOf(headers) {
|
|
165
|
+
const output = Object.create(null);
|
|
166
|
+
for (const [name, value] of Object.entries(headers ?? {})) {
|
|
167
|
+
const normalized = name.toLowerCase();
|
|
168
|
+
if (FORBIDDEN_HEADER_NAMES.has(normalized)) throw new TypeError(`observation header ${name} is managed by the exporter`);
|
|
169
|
+
if (typeof value !== "string" || !/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name) || /[\r\n]/.test(value)) throw new TypeError(`observation header ${name} is invalid`);
|
|
170
|
+
output[normalized] = value;
|
|
203
171
|
}
|
|
204
|
-
|
|
205
|
-
|
|
172
|
+
return Object.freeze(output);
|
|
173
|
+
}
|
|
174
|
+
function resolveOptions(options) {
|
|
175
|
+
validateInjectedFunctions(options);
|
|
176
|
+
return Object.freeze({
|
|
177
|
+
endpoint: endpointOf(options.endpoint, options.allowInsecureHttp ?? false),
|
|
178
|
+
headers: headersOf(options.headers),
|
|
179
|
+
fetch: options.fetch ?? globalThis.fetch,
|
|
180
|
+
...retryOptions(options),
|
|
181
|
+
...resourceLimits(options),
|
|
182
|
+
random: options.random ?? Math.random,
|
|
183
|
+
delay: options.delay ?? abortableDelay,
|
|
184
|
+
now: options.now ?? Date.now
|
|
185
|
+
});
|
|
186
|
+
}
|
|
187
|
+
function validateInjectedFunctions(options) {
|
|
188
|
+
if (typeof options?.fetch !== "function" && typeof globalThis.fetch !== "function") throw new TypeError("Fetch observation exporter requires fetch");
|
|
189
|
+
if (options.random !== void 0 && typeof options.random !== "function") throw new TypeError("random must be a function");
|
|
190
|
+
if (options.delay !== void 0 && typeof options.delay !== "function") throw new TypeError("delay must be a function");
|
|
191
|
+
if (options.now !== void 0 && typeof options.now !== "function") throw new TypeError("now must be a function");
|
|
192
|
+
}
|
|
193
|
+
function retryOptions(options) {
|
|
194
|
+
return {
|
|
195
|
+
requestTimeoutMs: positiveSafeInteger(options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS, "requestTimeoutMs"),
|
|
196
|
+
maxAttempts: positiveSafeInteger(options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS, "maxAttempts"),
|
|
197
|
+
baseDelayMs: nonNegativeFinite(options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS, "baseDelayMs"),
|
|
198
|
+
maxDelayMs: nonNegativeFinite(options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS, "maxDelayMs")
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function resourceLimits(options) {
|
|
202
|
+
return {
|
|
203
|
+
maxBatchEvents: positiveSafeInteger(options.maxBatchEvents ?? DEFAULT_MAX_BATCH_EVENTS, "maxBatchEvents"),
|
|
204
|
+
maxBatchBytes: positiveSafeInteger(options.maxBatchBytes ?? DEFAULT_MAX_BATCH_BYTES, "maxBatchBytes"),
|
|
205
|
+
maxAckBytes: positiveSafeInteger(options.maxAckBytes ?? DEFAULT_MAX_ACK_BYTES, "maxAckBytes"),
|
|
206
|
+
maxAckChunks: positiveSafeInteger(options.maxAckChunks ?? DEFAULT_MAX_ACK_CHUNKS, "maxAckChunks")
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
//#endregion
|
|
211
|
+
//#region src/fetch-send.ts
|
|
212
|
+
async function successfulResponse(response, options, batchId, signal) {
|
|
213
|
+
if (response.status === 204) {
|
|
214
|
+
await cancelBody(response);
|
|
206
215
|
return Object.freeze({
|
|
207
|
-
|
|
208
|
-
|
|
216
|
+
accepted: true,
|
|
217
|
+
retryable: false
|
|
209
218
|
});
|
|
210
219
|
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
220
|
+
const raw = await boundedText(response, options, signal);
|
|
221
|
+
let parsed;
|
|
222
|
+
try {
|
|
223
|
+
parsed = JSON.parse(raw);
|
|
224
|
+
} catch {
|
|
225
|
+
parsed = void 0;
|
|
226
|
+
}
|
|
227
|
+
const acceptedBatchId = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "acceptedBatchId") : void 0;
|
|
228
|
+
return Object.freeze({
|
|
229
|
+
accepted: acceptedBatchId === batchId,
|
|
230
|
+
retryable: false
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
async function responseResult(response, options, batchId, control) {
|
|
234
|
+
await validateNoRedirectResponse(response, options.endpoint);
|
|
235
|
+
if (response.status >= 200 && response.status < 300) return { result: await successfulResponse(response, options, batchId, control.signal) };
|
|
236
|
+
const retryable = retryableStatus(response.status);
|
|
237
|
+
const retryAfter = retryAfterMs(response.headers.get("retry-after"), options.now());
|
|
238
|
+
await cancelBody(response);
|
|
239
|
+
if (!retryable || control.attempt === options.maxAttempts) return { result: Object.freeze({
|
|
240
|
+
accepted: false,
|
|
241
|
+
retryable
|
|
242
|
+
}) };
|
|
243
|
+
return { retryDelay: retryAfter ?? backoffMs(control.attempt, options) };
|
|
244
|
+
}
|
|
245
|
+
function failedAttempt(error, options, attempt, signal) {
|
|
246
|
+
if (signal.aborted) throw signal.reason ?? error;
|
|
247
|
+
if (error instanceof ObservationProtocolError || error instanceof RangeError) return { result: Object.freeze({
|
|
248
|
+
accepted: false,
|
|
249
|
+
retryable: false
|
|
250
|
+
}) };
|
|
251
|
+
if (attempt === options.maxAttempts) return { result: Object.freeze({
|
|
252
|
+
accepted: false,
|
|
253
|
+
retryable: true
|
|
254
|
+
}) };
|
|
255
|
+
return { retryDelay: backoffMs(attempt, options) };
|
|
256
|
+
}
|
|
257
|
+
async function sendAttempt(options, batchId, body, control) {
|
|
258
|
+
const request = combinedSignal(control.signal, options.requestTimeoutMs);
|
|
259
|
+
const dispatch = options.fetch;
|
|
260
|
+
try {
|
|
261
|
+
return await responseResult(await raceAbort(Promise.resolve(dispatch(options.endpoint, {
|
|
262
|
+
method: "POST",
|
|
263
|
+
headers: {
|
|
264
|
+
...options.headers,
|
|
265
|
+
"content-type": "application/json",
|
|
266
|
+
"idempotency-key": batchId
|
|
267
|
+
},
|
|
268
|
+
body,
|
|
269
|
+
redirect: "manual",
|
|
270
|
+
signal: request.signal
|
|
271
|
+
})), request.signal), options, batchId, {
|
|
272
|
+
attempt: control.attempt,
|
|
273
|
+
signal: request.signal
|
|
274
|
+
});
|
|
275
|
+
} catch (error) {
|
|
276
|
+
return failedAttempt(error, options, control.attempt, control.signal);
|
|
277
|
+
} finally {
|
|
278
|
+
request.clear();
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
async function sendBatch(options, batch, signal) {
|
|
282
|
+
if (batch.count > options.maxBatchEvents) return Object.freeze({
|
|
214
283
|
accepted: false,
|
|
215
284
|
retryable: false
|
|
216
285
|
});
|
|
217
|
-
const body = JSON.stringify(batch);
|
|
286
|
+
const body = JSON.stringify(batch.value);
|
|
218
287
|
if (new TextEncoder().encode(body).byteLength > options.maxBatchBytes) return Object.freeze({
|
|
219
288
|
accepted: false,
|
|
220
289
|
retryable: false
|
|
221
290
|
});
|
|
222
|
-
const dispatch = options.fetch;
|
|
223
291
|
for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {
|
|
224
292
|
if (signal.aborted) throw signal.reason ?? /* @__PURE__ */ new Error("observation export aborted");
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
...options.headers,
|
|
232
|
-
"content-type": "application/json",
|
|
233
|
-
"idempotency-key": batchId
|
|
234
|
-
},
|
|
235
|
-
body,
|
|
236
|
-
redirect: "manual",
|
|
237
|
-
signal: request.signal
|
|
238
|
-
})), request.signal);
|
|
239
|
-
await validateNoRedirectResponse(response, options.endpoint);
|
|
240
|
-
if (response.status === 204) {
|
|
241
|
-
await cancelBody(response);
|
|
242
|
-
return Object.freeze({
|
|
243
|
-
accepted: true,
|
|
244
|
-
retryable: false
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
if (response.status >= 200 && response.status < 300) {
|
|
248
|
-
const raw = await boundedText(response, options, request.signal);
|
|
249
|
-
let parsed;
|
|
250
|
-
try {
|
|
251
|
-
parsed = JSON.parse(raw);
|
|
252
|
-
} catch {
|
|
253
|
-
parsed = void 0;
|
|
254
|
-
}
|
|
255
|
-
const acceptedBatchId = typeof parsed === "object" && parsed !== null ? Reflect.get(parsed, "acceptedBatchId") : void 0;
|
|
256
|
-
return Object.freeze({
|
|
257
|
-
accepted: acceptedBatchId === batchId,
|
|
258
|
-
retryable: false
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
const retryable = retryableStatus(response.status);
|
|
262
|
-
const retryAfter = retryAfterMs(response.headers.get("retry-after"), options.now());
|
|
263
|
-
await cancelBody(response);
|
|
264
|
-
if (!retryable || attempt === options.maxAttempts) return Object.freeze({
|
|
265
|
-
accepted: false,
|
|
266
|
-
retryable
|
|
267
|
-
});
|
|
268
|
-
retryDelay = retryAfter ?? backoffMs(attempt, options);
|
|
269
|
-
} catch (error) {
|
|
270
|
-
if (signal.aborted) throw signal.reason ?? error;
|
|
271
|
-
if (error instanceof ObservationProtocolError || error instanceof RangeError) return Object.freeze({
|
|
272
|
-
accepted: false,
|
|
273
|
-
retryable: false
|
|
274
|
-
});
|
|
275
|
-
if (attempt === options.maxAttempts) return Object.freeze({
|
|
276
|
-
accepted: false,
|
|
277
|
-
retryable: true
|
|
278
|
-
});
|
|
279
|
-
retryDelay = backoffMs(attempt, options);
|
|
280
|
-
} finally {
|
|
281
|
-
request.clear();
|
|
282
|
-
}
|
|
283
|
-
await raceAbort(Promise.resolve(options.delay(retryDelay ?? 0, signal)), signal);
|
|
293
|
+
const outcome = await sendAttempt(options, batch.id, body, {
|
|
294
|
+
attempt,
|
|
295
|
+
signal
|
|
296
|
+
});
|
|
297
|
+
if ("result" in outcome) return outcome.result;
|
|
298
|
+
await raceAbort(Promise.resolve(options.delay(outcome.retryDelay, signal)), signal);
|
|
284
299
|
}
|
|
285
300
|
return Object.freeze({
|
|
286
301
|
accepted: false,
|
|
287
302
|
retryable: true
|
|
288
303
|
});
|
|
289
304
|
}
|
|
305
|
+
|
|
306
|
+
//#endregion
|
|
307
|
+
//#region src/fetch-exporter.ts
|
|
308
|
+
/** A remote-acknowledged exporter using only Fetch and Web streams. */
|
|
309
|
+
var FetchObservationExporter = class {
|
|
310
|
+
id;
|
|
311
|
+
supportedBoundaries = Object.freeze(["remote-acknowledged"]);
|
|
312
|
+
options;
|
|
313
|
+
constructor(options) {
|
|
314
|
+
if (typeof options !== "object" || options === null) throw new TypeError("Fetch observation exporter options are required");
|
|
315
|
+
this.id = options.id ?? "fetch";
|
|
316
|
+
if (!/^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,63})$/.test(this.id)) throw new TypeError("Fetch observation exporter id must be a safe 1-64 character identifier");
|
|
317
|
+
this.options = resolveOptions(options);
|
|
318
|
+
}
|
|
319
|
+
async export(batch, signal) {
|
|
320
|
+
const result = await sendBatch(this.options, {
|
|
321
|
+
id: batch.batchId,
|
|
322
|
+
count: batch.events.length,
|
|
323
|
+
value: batch
|
|
324
|
+
}, signal);
|
|
325
|
+
return Object.freeze({
|
|
326
|
+
batchId: batch.batchId,
|
|
327
|
+
...result
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
};
|
|
290
331
|
/** Recommended inert runtime exporter; transport begins only when core exports a batch. */
|
|
291
332
|
function fetchObservationExporter(options) {
|
|
292
333
|
if (typeof options !== "object" || options === null) throw new TypeError("Fetch observation exporter options are required");
|
|
@@ -297,7 +338,11 @@ function fetchObservationExporter(options) {
|
|
|
297
338
|
id,
|
|
298
339
|
supportedBoundaries: ["remote-acknowledged"],
|
|
299
340
|
async export(batch, signal) {
|
|
300
|
-
const result = await sendBatch(captured,
|
|
341
|
+
const result = await sendBatch(captured, {
|
|
342
|
+
id: batch.id,
|
|
343
|
+
count: batch.events.length + batch.runRecords.length,
|
|
344
|
+
value: batch
|
|
345
|
+
}, signal);
|
|
301
346
|
return Object.freeze({
|
|
302
347
|
batchId: batch.id,
|
|
303
348
|
acceptedEventIds: result.accepted ? batch.events.map((event) => event.eventId) : [],
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/fetch-exporter.ts","../src/wait-until.ts"],"sourcesContent":["import { waitForSettlement, type ObservationBoundary } from '@alvin0/ai-agent-sdk-core'\nimport type {\n ExportAck,\n ObservationBatch,\n ObservationExporter,\n} from '@alvin0/ai-agent-sdk-core/observability'\nimport {\n defineObservationExporter,\n type ObservationDeliveryBatch,\n type ObservationExporterPlugin,\n} from '@alvin0/ai-agent-sdk-core/observability'\n\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10_000\nconst DEFAULT_MAX_ATTEMPTS = 8\nconst DEFAULT_BASE_DELAY_MS = 250\nconst DEFAULT_MAX_DELAY_MS = 30_000\nconst MAX_RETRY_AFTER_MS = 60_000\nconst DEFAULT_MAX_BATCH_EVENTS = 256\nconst DEFAULT_MAX_BATCH_BYTES = 512 * 1024\nconst DEFAULT_MAX_ACK_BYTES = 64 * 1024\nconst DEFAULT_MAX_ACK_CHUNKS = 1_000\nconst RETRYABLE_STATUSES = new Set([408, 425, 429])\nconst FORBIDDEN_HEADER_NAMES = new Set(['content-length', 'content-type', 'host', 'idempotency-key'])\n\nclass ObservationProtocolError extends Error {\n override readonly name = 'ObservationProtocolError'\n}\n\nexport interface FetchObservationExporterOptions {\n readonly id?: string\n readonly endpoint: string | URL\n readonly headers?: Readonly<Record<string, string>>\n readonly fetch?: typeof fetch\n /** Permit cleartext only for localhost/loopback test endpoints. */\n readonly allowInsecureHttp?: boolean\n readonly requestTimeoutMs?: number\n readonly maxAttempts?: number\n readonly baseDelayMs?: number\n readonly maxDelayMs?: number\n readonly maxBatchEvents?: number\n readonly maxBatchBytes?: number\n readonly maxAckBytes?: number\n readonly maxAckChunks?: number\n /** Deterministic injection for tests; defaults to `Math.random`. */\n readonly random?: () => number\n /** Timer injection for tests; defaults to an abortable Web timer. */\n readonly delay?: (milliseconds: number, signal: AbortSignal) => Promise<void>\n /** Clock injection used only for HTTP-date `Retry-After`. */\n readonly now?: () => number\n}\n\ninterface ResolvedOptions {\n readonly endpoint: URL\n readonly headers: Readonly<Record<string, string>>\n readonly fetch: typeof fetch\n readonly requestTimeoutMs: number\n readonly maxAttempts: number\n readonly baseDelayMs: number\n readonly maxDelayMs: number\n readonly maxBatchEvents: number\n readonly maxBatchBytes: number\n readonly maxAckBytes: number\n readonly maxAckChunks: number\n readonly random: () => number\n readonly delay: (milliseconds: number, signal: AbortSignal) => Promise<void>\n readonly now: () => number\n}\n\nfunction positiveSafeInteger(value: number, field: string): number {\n if (!Number.isSafeInteger(value) || value < 1) throw new RangeError(`${field} must be a positive safe integer`)\n return value\n}\n\nfunction nonNegativeFinite(value: number, field: string): number {\n if (!Number.isFinite(value) || value < 0) throw new RangeError(`${field} must be a non-negative finite number`)\n return value\n}\n\nfunction endpointOf(value: string | URL, allowInsecureHttp: boolean): URL {\n const endpoint = new URL(value)\n if (endpoint.username.length > 0 || endpoint.password.length > 0) {\n throw new TypeError('observation endpoint must not contain credentials')\n }\n const local = endpoint.hostname === 'localhost' || endpoint.hostname === '127.0.0.1'\n || endpoint.hostname === '[::1]' || endpoint.hostname === '::1'\n if (endpoint.protocol !== 'https:' && !(allowInsecureHttp && endpoint.protocol === 'http:' && local)) {\n throw new TypeError('observation endpoint must use https')\n }\n return endpoint\n}\n\nfunction headersOf(headers: Readonly<Record<string, string>> | undefined): Readonly<Record<string, string>> {\n const output = Object.create(null) as Record<string, string>\n for (const [name, value] of Object.entries(headers ?? {})) {\n const normalized = name.toLowerCase()\n if (FORBIDDEN_HEADER_NAMES.has(normalized)) throw new TypeError(`observation header ${name} is managed by the exporter`)\n if (typeof value !== 'string' || !/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name) || /[\\r\\n]/.test(value)) {\n throw new TypeError(`observation header ${name} is invalid`)\n }\n output[normalized] = value\n }\n return Object.freeze(output)\n}\n\nfunction resolveOptions(options: FetchObservationExporterOptions): ResolvedOptions {\n if (typeof options?.fetch !== 'function' && typeof globalThis.fetch !== 'function') {\n throw new TypeError('Fetch observation exporter requires fetch')\n }\n if (options.random !== undefined && typeof options.random !== 'function') {\n throw new TypeError('random must be a function')\n }\n if (options.delay !== undefined && typeof options.delay !== 'function') {\n throw new TypeError('delay must be a function')\n }\n if (options.now !== undefined && typeof options.now !== 'function') {\n throw new TypeError('now must be a function')\n }\n return Object.freeze({\n endpoint: endpointOf(options.endpoint, options.allowInsecureHttp ?? false),\n headers: headersOf(options.headers),\n fetch: options.fetch ?? globalThis.fetch,\n requestTimeoutMs: positiveSafeInteger(options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS, 'requestTimeoutMs'),\n maxAttempts: positiveSafeInteger(options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS, 'maxAttempts'),\n baseDelayMs: nonNegativeFinite(options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS, 'baseDelayMs'),\n maxDelayMs: nonNegativeFinite(options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS, 'maxDelayMs'),\n maxBatchEvents: positiveSafeInteger(options.maxBatchEvents ?? DEFAULT_MAX_BATCH_EVENTS, 'maxBatchEvents'),\n maxBatchBytes: positiveSafeInteger(options.maxBatchBytes ?? DEFAULT_MAX_BATCH_BYTES, 'maxBatchBytes'),\n maxAckBytes: positiveSafeInteger(options.maxAckBytes ?? DEFAULT_MAX_ACK_BYTES, 'maxAckBytes'),\n maxAckChunks: positiveSafeInteger(options.maxAckChunks ?? DEFAULT_MAX_ACK_CHUNKS, 'maxAckChunks'),\n random: options.random ?? Math.random,\n delay: options.delay ?? abortableDelay,\n now: options.now ?? Date.now,\n })\n}\n\nfunction retryableStatus(status: number): boolean {\n return RETRYABLE_STATUSES.has(status) || (status >= 500 && status <= 599)\n}\n\nfunction retryAfterMs(value: string | null, now: number): number | undefined {\n if (value === null) return undefined\n const trimmed = value.trim()\n if (/^\\d+(?:\\.\\d+)?$/.test(trimmed)) return Math.min(MAX_RETRY_AFTER_MS, Number(trimmed) * 1_000)\n const at = Date.parse(trimmed)\n if (!Number.isFinite(at)) return undefined\n return Math.min(MAX_RETRY_AFTER_MS, Math.max(0, at - now))\n}\n\nfunction backoffMs(attempt: number, options: ResolvedOptions): number {\n const cap = Math.min(options.maxDelayMs, options.baseDelayMs * (2 ** Math.max(0, attempt - 1)))\n const random = options.random()\n const unit = Number.isFinite(random) ? Math.min(1, Math.max(0, random)) : 0\n return Math.floor(cap * unit)\n}\n\nfunction combinedSignal(external: AbortSignal, timeoutMs: number): { signal: AbortSignal; clear(): void } {\n const controller = new AbortController()\n const timeout = setTimeout(() => controller.abort(new DOMException('observation request timed out', 'TimeoutError')), timeoutMs)\n const abort = () => controller.abort(external.reason ?? new DOMException('observation export aborted', 'AbortError'))\n if (external.aborted) abort()\n else external.addEventListener('abort', abort, { once: true })\n return {\n signal: controller.signal,\n clear() { clearTimeout(timeout); external.removeEventListener('abort', abort) },\n }\n}\n\nfunction raceAbort<T>(pending: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) return Promise.reject(signal.reason ?? new Error('observation export aborted'))\n return new Promise<T>((resolve, reject) => {\n const abort = () => { cleanup(); reject(signal.reason ?? new Error('observation export aborted')) }\n const cleanup = () => signal.removeEventListener('abort', abort)\n signal.addEventListener('abort', abort, { once: true })\n void pending.then(\n value => { cleanup(); resolve(value) },\n error => { cleanup(); reject(error) },\n )\n })\n}\n\nasync function abortableDelay(milliseconds: number, signal: AbortSignal): Promise<void> {\n if (milliseconds <= 0) return\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => { cleanup(); resolve() }, milliseconds)\n const abort = () => { cleanup(); reject(signal.reason ?? new Error('observation export aborted')) }\n const cleanup = () => { clearTimeout(timer); signal.removeEventListener('abort', abort) }\n if (signal.aborted) abort()\n else signal.addEventListener('abort', abort, { once: true })\n })\n}\n\nasync function cancelBody(response: Response): Promise<void> {\n if (response.body === null) return\n await waitForSettlement(response.body.cancel().catch(() => undefined), 1_000)\n}\n\nasync function boundedText(response: Response, options: ResolvedOptions, signal: AbortSignal): Promise<string> {\n const declared = Number(response.headers.get('content-length'))\n if (Number.isFinite(declared) && declared > options.maxAckBytes) {\n await cancelBody(response)\n throw new RangeError('observation acknowledgment exceeds its byte limit')\n }\n if (response.body === null) return ''\n const reader = response.body.getReader()\n const decoder = new TextDecoder()\n let output = ''\n let bytes = 0\n let chunks = 0\n try {\n while (true) {\n const next = await raceAbort(reader.read(), signal)\n if (next.done) return output + decoder.decode()\n if (next.value === undefined) continue\n bytes += next.value.byteLength\n chunks++\n if (bytes > options.maxAckBytes || chunks > options.maxAckChunks) {\n await waitForSettlement(reader.cancel().catch(() => undefined), 1_000)\n throw new RangeError('observation acknowledgment exceeds its resource limit')\n }\n output += decoder.decode(next.value, { stream: true })\n }\n } finally {\n reader.releaseLock()\n }\n}\n\nasync function validateNoRedirectResponse(response: Response, endpoint: URL): Promise<void> {\n const redirectStatus = response.status >= 300 && response.status < 400\n const opaqueRedirect = response.type === 'opaqueredirect'\n const responseUrlChanged = response.url.length > 0 && response.url !== endpoint.href\n if (response.redirected || redirectStatus || opaqueRedirect || responseUrlChanged) {\n await cancelBody(response)\n throw new ObservationProtocolError('observation exporter rejected a redirect before following it')\n }\n if (response.url.length > 0) {\n let responseOrigin: string\n try { responseOrigin = new URL(response.url).origin } catch {\n throw new ObservationProtocolError('observation response URL is invalid')\n }\n if (responseOrigin !== endpoint.origin) {\n throw new ObservationProtocolError('observation response origin does not match its endpoint')\n }\n }\n}\n\n/** A remote-acknowledged exporter using only Fetch and Web streams. */\nexport class FetchObservationExporter implements ObservationExporter {\n readonly id: string\n readonly supportedBoundaries: readonly ObservationBoundary[] = Object.freeze(['remote-acknowledged'])\n private readonly options: ResolvedOptions\n\n constructor(options: FetchObservationExporterOptions) {\n if (typeof options !== 'object' || options === null) {\n throw new TypeError('Fetch observation exporter options are required')\n }\n this.id = options.id ?? 'fetch'\n if (!/^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,63})$/.test(this.id)) {\n throw new TypeError('Fetch observation exporter id must be a safe 1-64 character identifier')\n }\n this.options = resolveOptions(options)\n }\n\n async export(batch: ObservationBatch, signal: AbortSignal): Promise<ExportAck> {\n const result = await sendBatch(this.options, batch.batchId, batch.events.length, batch, signal)\n return Object.freeze({ batchId: batch.batchId, ...result })\n }\n}\n\ninterface BatchSendResult {\n readonly accepted: boolean\n readonly retryable: boolean\n}\n\nasync function sendBatch(\n options: ResolvedOptions,\n batchId: string,\n itemCount: number,\n batch: unknown,\n signal: AbortSignal,\n): Promise<BatchSendResult> {\n if (itemCount > options.maxBatchEvents) return Object.freeze({ accepted: false, retryable: false })\n const body = JSON.stringify(batch)\n if (new TextEncoder().encode(body).byteLength > options.maxBatchBytes) {\n return Object.freeze({ accepted: false, retryable: false })\n }\n // Web IDL fetch rejects a property-call receiver such as `options.fetch(...)`\n // in browsers. Capture it as a standalone callable so native Window/Worker\n // fetch and injected implementations share the same invocation contract.\n const dispatch = options.fetch\n\n for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {\n if (signal.aborted) throw signal.reason ?? new Error('observation export aborted')\n const request = combinedSignal(signal, options.requestTimeoutMs)\n let retryDelay: number | undefined\n try {\n const response = await raceAbort(Promise.resolve(dispatch(options.endpoint, {\n method: 'POST',\n headers: {\n ...options.headers,\n 'content-type': 'application/json',\n 'idempotency-key': batchId,\n },\n body,\n redirect: 'manual',\n signal: request.signal,\n })), request.signal)\n await validateNoRedirectResponse(response, options.endpoint)\n if (response.status === 204) {\n await cancelBody(response)\n return Object.freeze({ accepted: true, retryable: false })\n }\n if (response.status >= 200 && response.status < 300) {\n const raw = await boundedText(response, options, request.signal)\n let parsed: unknown\n try { parsed = JSON.parse(raw) } catch { parsed = undefined }\n const acceptedBatchId = typeof parsed === 'object' && parsed !== null\n ? Reflect.get(parsed, 'acceptedBatchId')\n : undefined\n return Object.freeze({\n accepted: acceptedBatchId === batchId,\n retryable: false,\n })\n }\n const retryable = retryableStatus(response.status)\n const retryAfter = retryAfterMs(response.headers.get('retry-after'), options.now())\n await cancelBody(response)\n if (!retryable || attempt === options.maxAttempts) {\n return Object.freeze({ accepted: false, retryable })\n }\n retryDelay = retryAfter ?? backoffMs(attempt, options)\n } catch (error) {\n if (signal.aborted) throw signal.reason ?? error\n if (error instanceof ObservationProtocolError || error instanceof RangeError) {\n return Object.freeze({ accepted: false, retryable: false })\n }\n if (attempt === options.maxAttempts) {\n return Object.freeze({ accepted: false, retryable: true })\n }\n retryDelay = backoffMs(attempt, options)\n } finally {\n request.clear()\n }\n await raceAbort(Promise.resolve(options.delay(retryDelay ?? 0, signal)), signal)\n }\n return Object.freeze({ accepted: false, retryable: true })\n}\n\n/** Recommended inert runtime exporter; transport begins only when core exports a batch. */\nexport function fetchObservationExporter(\n options: FetchObservationExporterOptions,\n): ObservationExporterPlugin {\n if (typeof options !== 'object' || options === null) {\n throw new TypeError('Fetch observation exporter options are required')\n }\n const id = options.id ?? 'fetch'\n if (!/^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,63})$/.test(id)) {\n throw new TypeError('Fetch observation exporter id must be a safe 1-64 character identifier')\n }\n const captured = resolveOptions(options)\n return defineObservationExporter({\n id,\n supportedBoundaries: ['remote-acknowledged'],\n async export(batch: ObservationDeliveryBatch, signal: AbortSignal) {\n const result = await sendBatch(\n captured,\n batch.id,\n batch.events.length + batch.runRecords.length,\n batch,\n signal,\n )\n return Object.freeze({\n batchId: batch.id,\n acceptedEventIds: result.accepted ? batch.events.map(event => event.eventId) : [],\n acceptedRunIds: result.accepted ? batch.runRecords.map(record => record.runId) : [],\n })\n },\n })\n}\n","import type { FlushResult, Observability } from '@alvin0/ai-agent-sdk-core/observability'\n\nexport type WaitUntil = (pending: Promise<unknown>) => void\n\n/** Start one flush and explicitly attach it to an Edge host's lifetime. */\nexport function flushObservabilityWithWaitUntil(\n observation: Pick<Observability, 'flush'>,\n waitUntil: WaitUntil,\n signal?: AbortSignal,\n): Promise<FlushResult> {\n if (typeof waitUntil !== 'function') throw new TypeError('waitUntil must be a function')\n const pending = observation.flush(signal)\n waitUntil(pending)\n return pending\n}\n"],"mappings":";;;;AAYA,MAAM,6BAA6B;AACnC,MAAM,uBAAuB;AAC7B,MAAM,wBAAwB;AAC9B,MAAM,uBAAuB;AAC7B,MAAM,qBAAqB;AAC3B,MAAM,2BAA2B;AACjC,MAAM,0BAA0B;AAChC,MAAM,wBAAwB;AAC9B,MAAM,yBAAyB;AAC/B,MAAM,qCAAqB,IAAI,IAAI;CAAC;CAAK;CAAK;AAAG,CAAC;AAClD,MAAM,yCAAyB,IAAI,IAAI;CAAC;CAAkB;CAAgB;CAAQ;AAAiB,CAAC;AAEpG,IAAM,2BAAN,cAAuC,MAAM;CAC3C,AAAkB,OAAO;AAC3B;AA0CA,SAAS,oBAAoB,OAAe,OAAuB;CACjE,IAAI,CAAC,OAAO,cAAc,KAAK,KAAK,QAAQ,GAAG,MAAM,IAAI,WAAW,GAAG,MAAM,iCAAiC;CAC9G,OAAO;AACT;AAEA,SAAS,kBAAkB,OAAe,OAAuB;CAC/D,IAAI,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,GAAG,MAAM,IAAI,WAAW,GAAG,MAAM,sCAAsC;CAC9G,OAAO;AACT;AAEA,SAAS,WAAW,OAAqB,mBAAiC;CACxE,MAAM,WAAW,IAAI,IAAI,KAAK;CAC9B,IAAI,SAAS,SAAS,SAAS,KAAK,SAAS,SAAS,SAAS,GAC7D,MAAM,IAAI,UAAU,mDAAmD;CAEzE,MAAM,QAAQ,SAAS,aAAa,eAAe,SAAS,aAAa,eACpE,SAAS,aAAa,WAAW,SAAS,aAAa;CAC5D,IAAI,SAAS,aAAa,YAAY,EAAE,qBAAqB,SAAS,aAAa,WAAW,QAC5F,MAAM,IAAI,UAAU,qCAAqC;CAE3D,OAAO;AACT;AAEA,SAAS,UAAU,SAAyF;CAC1G,MAAM,SAAS,OAAO,OAAO,IAAI;CACjC,KAAK,MAAM,CAAC,MAAM,UAAU,OAAO,QAAQ,WAAW,CAAC,CAAC,GAAG;EACzD,MAAM,aAAa,KAAK,YAAY;EACpC,IAAI,uBAAuB,IAAI,UAAU,GAAG,MAAM,IAAI,UAAU,sBAAsB,KAAK,4BAA4B;EACvH,IAAI,OAAO,UAAU,YAAY,CAAC,gCAAgC,KAAK,IAAI,KAAK,SAAS,KAAK,KAAK,GACjG,MAAM,IAAI,UAAU,sBAAsB,KAAK,YAAY;EAE7D,OAAO,cAAc;CACvB;CACA,OAAO,OAAO,OAAO,MAAM;AAC7B;AAEA,SAAS,eAAe,SAA2D;CACjF,IAAI,OAAO,SAAS,UAAU,cAAc,OAAO,WAAW,UAAU,YACtE,MAAM,IAAI,UAAU,2CAA2C;CAEjE,IAAI,QAAQ,WAAW,UAAa,OAAO,QAAQ,WAAW,YAC5D,MAAM,IAAI,UAAU,2BAA2B;CAEjD,IAAI,QAAQ,UAAU,UAAa,OAAO,QAAQ,UAAU,YAC1D,MAAM,IAAI,UAAU,0BAA0B;CAEhD,IAAI,QAAQ,QAAQ,UAAa,OAAO,QAAQ,QAAQ,YACtD,MAAM,IAAI,UAAU,wBAAwB;CAE9C,OAAO,OAAO,OAAO;EACnB,UAAU,WAAW,QAAQ,UAAU,QAAQ,qBAAqB,KAAK;EACzE,SAAS,UAAU,QAAQ,OAAO;EAClC,OAAO,QAAQ,SAAS,WAAW;EACnC,kBAAkB,oBAAoB,QAAQ,oBAAoB,4BAA4B,kBAAkB;EAChH,aAAa,oBAAoB,QAAQ,eAAe,sBAAsB,aAAa;EAC3F,aAAa,kBAAkB,QAAQ,eAAe,uBAAuB,aAAa;EAC1F,YAAY,kBAAkB,QAAQ,cAAc,sBAAsB,YAAY;EACtF,gBAAgB,oBAAoB,QAAQ,kBAAkB,0BAA0B,gBAAgB;EACxG,eAAe,oBAAoB,QAAQ,iBAAiB,yBAAyB,eAAe;EACpG,aAAa,oBAAoB,QAAQ,eAAe,uBAAuB,aAAa;EAC5F,cAAc,oBAAoB,QAAQ,gBAAgB,wBAAwB,cAAc;EAChG,QAAQ,QAAQ,UAAU,KAAK;EAC/B,OAAO,QAAQ,SAAS;EACxB,KAAK,QAAQ,OAAO,KAAK;CAC3B,CAAC;AACH;AAEA,SAAS,gBAAgB,QAAyB;CAChD,OAAO,mBAAmB,IAAI,MAAM,KAAM,UAAU,OAAO,UAAU;AACvE;AAEA,SAAS,aAAa,OAAsB,KAAiC;CAC3E,IAAI,UAAU,MAAM,OAAO;CAC3B,MAAM,UAAU,MAAM,KAAK;CAC3B,IAAI,kBAAkB,KAAK,OAAO,GAAG,OAAO,KAAK,IAAI,oBAAoB,OAAO,OAAO,IAAI,GAAK;CAChG,MAAM,KAAK,KAAK,MAAM,OAAO;CAC7B,IAAI,CAAC,OAAO,SAAS,EAAE,GAAG,OAAO;CACjC,OAAO,KAAK,IAAI,oBAAoB,KAAK,IAAI,GAAG,KAAK,GAAG,CAAC;AAC3D;AAEA,SAAS,UAAU,SAAiB,SAAkC;CACpE,MAAM,MAAM,KAAK,IAAI,QAAQ,YAAY,QAAQ,cAAe,KAAK,KAAK,IAAI,GAAG,UAAU,CAAC,CAAE;CAC9F,MAAM,SAAS,QAAQ,OAAO;CAE9B,OAAO,KAAK,MAAM,OADL,OAAO,SAAS,MAAM,IAAI,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,MAAM,CAAC,IAAI,EAC9C;AAC9B;AAEA,SAAS,eAAe,UAAuB,WAA2D;CACxG,MAAM,aAAa,IAAI,gBAAgB;CACvC,MAAM,UAAU,iBAAiB,WAAW,MAAM,IAAI,aAAa,iCAAiC,cAAc,CAAC,GAAG,SAAS;CAC/H,MAAM,cAAc,WAAW,MAAM,SAAS,UAAU,IAAI,aAAa,8BAA8B,YAAY,CAAC;CACpH,IAAI,SAAS,SAAS,MAAM;MACvB,SAAS,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;CAC7D,OAAO;EACL,QAAQ,WAAW;EACnB,QAAQ;GAAE,aAAa,OAAO;GAAG,SAAS,oBAAoB,SAAS,KAAK;EAAE;CAChF;AACF;AAEA,SAAS,UAAa,SAAqB,QAAiC;CAC1E,IAAI,OAAO,SAAS,OAAO,QAAQ,OAAO,OAAO,0BAAU,IAAI,MAAM,4BAA4B,CAAC;CAClG,OAAO,IAAI,SAAY,SAAS,WAAW;EACzC,MAAM,cAAc;GAAE,QAAQ;GAAG,OAAO,OAAO,0BAAU,IAAI,MAAM,4BAA4B,CAAC;EAAE;EAClG,MAAM,gBAAgB,OAAO,oBAAoB,SAAS,KAAK;EAC/D,OAAO,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;EACtD,AAAK,QAAQ,MACX,UAAS;GAAE,QAAQ;GAAG,QAAQ,KAAK;EAAE,IACrC,UAAS;GAAE,QAAQ;GAAG,OAAO,KAAK;EAAE,CACtC;CACF,CAAC;AACH;AAEA,eAAe,eAAe,cAAsB,QAAoC;CACtF,IAAI,gBAAgB,GAAG;CACvB,MAAM,IAAI,SAAe,SAAS,WAAW;EAC3C,MAAM,QAAQ,iBAAiB;GAAE,QAAQ;GAAG,QAAQ;EAAE,GAAG,YAAY;EACrE,MAAM,cAAc;GAAE,QAAQ;GAAG,OAAO,OAAO,0BAAU,IAAI,MAAM,4BAA4B,CAAC;EAAE;EAClG,MAAM,gBAAgB;GAAE,aAAa,KAAK;GAAG,OAAO,oBAAoB,SAAS,KAAK;EAAE;EACxF,IAAI,OAAO,SAAS,MAAM;OACrB,OAAO,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;CAC7D,CAAC;AACH;AAEA,eAAe,WAAW,UAAmC;CAC3D,IAAI,SAAS,SAAS,MAAM;CAC5B,MAAM,kBAAkB,SAAS,KAAK,OAAO,CAAC,CAAC,YAAY,MAAS,GAAG,GAAK;AAC9E;AAEA,eAAe,YAAY,UAAoB,SAA0B,QAAsC;CAC7G,MAAM,WAAW,OAAO,SAAS,QAAQ,IAAI,gBAAgB,CAAC;CAC9D,IAAI,OAAO,SAAS,QAAQ,KAAK,WAAW,QAAQ,aAAa;EAC/D,MAAM,WAAW,QAAQ;EACzB,MAAM,IAAI,WAAW,mDAAmD;CAC1E;CACA,IAAI,SAAS,SAAS,MAAM,OAAO;CACnC,MAAM,SAAS,SAAS,KAAK,UAAU;CACvC,MAAM,UAAU,IAAI,YAAY;CAChC,IAAI,SAAS;CACb,IAAI,QAAQ;CACZ,IAAI,SAAS;CACb,IAAI;EACF,OAAO,MAAM;GACX,MAAM,OAAO,MAAM,UAAU,OAAO,KAAK,GAAG,MAAM;GAClD,IAAI,KAAK,MAAM,OAAO,SAAS,QAAQ,OAAO;GAC9C,IAAI,KAAK,UAAU,QAAW;GAC9B,SAAS,KAAK,MAAM;GACpB;GACA,IAAI,QAAQ,QAAQ,eAAe,SAAS,QAAQ,cAAc;IAChE,MAAM,kBAAkB,OAAO,OAAO,CAAC,CAAC,YAAY,MAAS,GAAG,GAAK;IACrE,MAAM,IAAI,WAAW,uDAAuD;GAC9E;GACA,UAAU,QAAQ,OAAO,KAAK,OAAO,EAAE,QAAQ,KAAK,CAAC;EACvD;CACF,UAAU;EACR,OAAO,YAAY;CACrB;AACF;AAEA,eAAe,2BAA2B,UAAoB,UAA8B;CAC1F,MAAM,iBAAiB,SAAS,UAAU,OAAO,SAAS,SAAS;CACnE,MAAM,iBAAiB,SAAS,SAAS;CACzC,MAAM,qBAAqB,SAAS,IAAI,SAAS,KAAK,SAAS,QAAQ,SAAS;CAChF,IAAI,SAAS,cAAc,kBAAkB,kBAAkB,oBAAoB;EACjF,MAAM,WAAW,QAAQ;EACzB,MAAM,IAAI,yBAAyB,8DAA8D;CACnG;CACA,IAAI,SAAS,IAAI,SAAS,GAAG;EAC3B,IAAI;EACJ,IAAI;GAAE,iBAAiB,IAAI,IAAI,SAAS,GAAG,CAAC,CAAC;EAAO,QAAQ;GAC1D,MAAM,IAAI,yBAAyB,qCAAqC;EAC1E;EACA,IAAI,mBAAmB,SAAS,QAC9B,MAAM,IAAI,yBAAyB,yDAAyD;CAEhG;AACF;;AAGA,IAAa,2BAAb,MAAqE;CACnE,AAAS;CACT,AAAS,sBAAsD,OAAO,OAAO,CAAC,qBAAqB,CAAC;CACpG,AAAiB;CAEjB,YAAY,SAA0C;EACpD,IAAI,OAAO,YAAY,YAAY,YAAY,MAC7C,MAAM,IAAI,UAAU,iDAAiD;EAEvE,KAAK,KAAK,QAAQ,MAAM;EACxB,IAAI,CAAC,wCAAwC,KAAK,KAAK,EAAE,GACvD,MAAM,IAAI,UAAU,wEAAwE;EAE9F,KAAK,UAAU,eAAe,OAAO;CACvC;CAEA,MAAM,OAAO,OAAyB,QAAyC;EAC7E,MAAM,SAAS,MAAM,UAAU,KAAK,SAAS,MAAM,SAAS,MAAM,OAAO,QAAQ,OAAO,MAAM;EAC9F,OAAO,OAAO,OAAO;GAAE,SAAS,MAAM;GAAS,GAAG;EAAO,CAAC;CAC5D;AACF;AAOA,eAAe,UACb,SACA,SACA,WACA,OACA,QAC0B;CAC1B,IAAI,YAAY,QAAQ,gBAAgB,OAAO,OAAO,OAAO;EAAE,UAAU;EAAO,WAAW;CAAM,CAAC;CAClG,MAAM,OAAO,KAAK,UAAU,KAAK;CACjC,IAAI,IAAI,YAAY,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,aAAa,QAAQ,eACtD,OAAO,OAAO,OAAO;EAAE,UAAU;EAAO,WAAW;CAAM,CAAC;CAK5D,MAAM,WAAW,QAAQ;CAEzB,KAAK,IAAI,UAAU,GAAG,WAAW,QAAQ,aAAa,WAAW;EAC/D,IAAI,OAAO,SAAS,MAAM,OAAO,0BAAU,IAAI,MAAM,4BAA4B;EACjF,MAAM,UAAU,eAAe,QAAQ,QAAQ,gBAAgB;EAC/D,IAAI;EACJ,IAAI;GACA,MAAM,WAAW,MAAM,UAAU,QAAQ,QAAQ,SAAS,QAAQ,UAAU;IAC1E,QAAQ;IACR,SAAS;KACP,GAAG,QAAQ;KACX,gBAAgB;KAChB,mBAAmB;IACrB;IACA;IACA,UAAU;IACV,QAAQ,QAAQ;GAClB,CAAC,CAAC,GAAG,QAAQ,MAAM;GACnB,MAAM,2BAA2B,UAAU,QAAQ,QAAQ;GAC3D,IAAI,SAAS,WAAW,KAAK;IAC3B,MAAM,WAAW,QAAQ;IACzB,OAAO,OAAO,OAAO;KAAE,UAAU;KAAM,WAAW;IAAM,CAAC;GAC3D;GACA,IAAI,SAAS,UAAU,OAAO,SAAS,SAAS,KAAK;IACnD,MAAM,MAAM,MAAM,YAAY,UAAU,SAAS,QAAQ,MAAM;IAC/D,IAAI;IACJ,IAAI;KAAE,SAAS,KAAK,MAAM,GAAG;IAAE,QAAQ;KAAE,SAAS;IAAU;IAC5D,MAAM,kBAAkB,OAAO,WAAW,YAAY,WAAW,OAC7D,QAAQ,IAAI,QAAQ,iBAAiB,IACrC;IACJ,OAAO,OAAO,OAAO;KACnB,UAAU,oBAAoB;KAC9B,WAAW;IACb,CAAC;GACH;GACA,MAAM,YAAY,gBAAgB,SAAS,MAAM;GACjD,MAAM,aAAa,aAAa,SAAS,QAAQ,IAAI,aAAa,GAAG,QAAQ,IAAI,CAAC;GAClF,MAAM,WAAW,QAAQ;GACzB,IAAI,CAAC,aAAa,YAAY,QAAQ,aACpC,OAAO,OAAO,OAAO;IAAE,UAAU;IAAO;GAAU,CAAC;GAErD,aAAa,cAAc,UAAU,SAAS,OAAO;EACzD,SAAS,OAAO;GACd,IAAI,OAAO,SAAS,MAAM,OAAO,UAAU;GAC3C,IAAI,iBAAiB,4BAA4B,iBAAiB,YAChE,OAAO,OAAO,OAAO;IAAE,UAAU;IAAO,WAAW;GAAM,CAAC;GAE5D,IAAI,YAAY,QAAQ,aACtB,OAAO,OAAO,OAAO;IAAE,UAAU;IAAO,WAAW;GAAK,CAAC;GAE3D,aAAa,UAAU,SAAS,OAAO;EACzC,UAAU;GACR,QAAQ,MAAM;EAChB;EACA,MAAM,UAAU,QAAQ,QAAQ,QAAQ,MAAM,cAAc,GAAG,MAAM,CAAC,GAAG,MAAM;CACjF;CACA,OAAO,OAAO,OAAO;EAAE,UAAU;EAAO,WAAW;CAAK,CAAC;AAC3D;;AAGA,SAAgB,yBACd,SAC2B;CAC3B,IAAI,OAAO,YAAY,YAAY,YAAY,MAC7C,MAAM,IAAI,UAAU,iDAAiD;CAEvE,MAAM,KAAK,QAAQ,MAAM;CACzB,IAAI,CAAC,wCAAwC,KAAK,EAAE,GAClD,MAAM,IAAI,UAAU,wEAAwE;CAE9F,MAAM,WAAW,eAAe,OAAO;CACvC,OAAO,0BAA0B;EAC/B;EACA,qBAAqB,CAAC,qBAAqB;EAC3C,MAAM,OAAO,OAAiC,QAAqB;GACjE,MAAM,SAAS,MAAM,UACnB,UACA,MAAM,IACN,MAAM,OAAO,SAAS,MAAM,WAAW,QACvC,OACA,MACF;GACA,OAAO,OAAO,OAAO;IACnB,SAAS,MAAM;IACf,kBAAkB,OAAO,WAAW,MAAM,OAAO,KAAI,UAAS,MAAM,OAAO,IAAI,CAAC;IAChF,gBAAgB,OAAO,WAAW,MAAM,WAAW,KAAI,WAAU,OAAO,KAAK,IAAI,CAAC;GACpF,CAAC;EACH;CACF,CAAC;AACH;;;;;ACpXA,SAAgB,gCACd,aACA,WACA,QACsB;CACtB,IAAI,OAAO,cAAc,YAAY,MAAM,IAAI,UAAU,8BAA8B;CACvF,MAAM,UAAU,YAAY,MAAM,MAAM;CACxC,UAAU,OAAO;CACjB,OAAO;AACT"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/fetch-transport.ts","../src/fetch-options.ts","../src/fetch-send.ts","../src/fetch-exporter.ts","../src/wait-until.ts"],"sourcesContent":["import { waitForSettlement } from '@alvin0/ai-agent-sdk-core'\nimport type { ResolvedOptions } from './fetch-options.ts'\nconst RETRYABLE_STATUSES = new Set([408, 425, 429])\nconst MAX_RETRY_AFTER_MS = 60_000\nexport class ObservationProtocolError extends Error {\n override readonly name = 'ObservationProtocolError'\n}\n\nexport function retryableStatus(status: number): boolean {\n return RETRYABLE_STATUSES.has(status) || (status >= 500 && status <= 599)\n}\n\nexport function retryAfterMs(value: string | null, now: number): number | undefined {\n if (value === null) return undefined\n const trimmed = value.trim()\n if (/^\\d+(?:\\.\\d+)?$/.test(trimmed)) return Math.min(MAX_RETRY_AFTER_MS, Number(trimmed) * 1_000)\n const at = Date.parse(trimmed)\n if (!Number.isFinite(at)) return undefined\n return Math.min(MAX_RETRY_AFTER_MS, Math.max(0, at - now))\n}\n\nexport function backoffMs(attempt: number, options: ResolvedOptions): number {\n const cap = Math.min(options.maxDelayMs, options.baseDelayMs * (2 ** Math.max(0, attempt - 1)))\n const random = options.random()\n const unit = Number.isFinite(random) ? Math.min(1, Math.max(0, random)) : 0\n return Math.floor(cap * unit)\n}\n\nexport function combinedSignal(external: AbortSignal, timeoutMs: number): { signal: AbortSignal; clear(): void } {\n const controller = new AbortController()\n const timeout = setTimeout(() =>\n controller.abort(new DOMException('observation request timed out', 'TimeoutError')), timeoutMs)\n const abort = () => controller.abort(external.reason ?? new DOMException('observation export aborted', 'AbortError'))\n if (external.aborted) abort()\n else external.addEventListener('abort', abort, { once: true })\n return {\n signal: controller.signal,\n clear() { clearTimeout(timeout); external.removeEventListener('abort', abort) },\n }\n}\n\nexport function raceAbort<T>(pending: Promise<T>, signal: AbortSignal): Promise<T> {\n if (signal.aborted) return Promise.reject(signal.reason ?? new Error('observation export aborted'))\n return new Promise<T>((resolve, reject) => {\n const abort = () => { cleanup(); reject(signal.reason ?? new Error('observation export aborted')) }\n const cleanup = () => signal.removeEventListener('abort', abort)\n signal.addEventListener('abort', abort, { once: true })\n void pending.then(\n value => { cleanup(); resolve(value) },\n error => { cleanup(); reject(error) },\n )\n })\n}\n\nexport async function abortableDelay(milliseconds: number, signal: AbortSignal): Promise<void> {\n if (milliseconds <= 0) return\n await new Promise<void>((resolve, reject) => {\n const timer = setTimeout(() => { cleanup(); resolve() }, milliseconds)\n const abort = () => { cleanup(); reject(signal.reason ?? new Error('observation export aborted')) }\n const cleanup = () => { clearTimeout(timer); signal.removeEventListener('abort', abort) }\n if (signal.aborted) abort()\n else signal.addEventListener('abort', abort, { once: true })\n })\n}\n\nexport async function cancelBody(response: Response): Promise<void> {\n if (response.body === null) return\n await waitForSettlement(response.body.cancel().catch(() => undefined), 1_000)\n}\n\nexport async function boundedText(response: Response, options: ResolvedOptions, signal: AbortSignal): Promise<string> {\n const declared = Number(response.headers.get('content-length'))\n if (Number.isFinite(declared) && declared > options.maxAckBytes) {\n await cancelBody(response)\n throw new RangeError('observation acknowledgment exceeds its byte limit')\n }\n if (response.body === null) return ''\n const reader = response.body.getReader()\n const decoder = new TextDecoder()\n let output = ''\n let bytes = 0\n let chunks = 0\n try {\n while (true) {\n const next = await raceAbort(reader.read(), signal)\n if (next.done) return output + decoder.decode()\n if (next.value === undefined) continue\n bytes += next.value.byteLength\n chunks++\n if (bytes > options.maxAckBytes || chunks > options.maxAckChunks) {\n await waitForSettlement(reader.cancel().catch(() => undefined), 1_000)\n throw new RangeError('observation acknowledgment exceeds its resource limit')\n }\n output += decoder.decode(next.value, { stream: true })\n }\n } finally {\n reader.releaseLock()\n }\n}\n\nexport async function validateNoRedirectResponse(response: Response, endpoint: URL): Promise<void> {\n const redirectStatus = response.status >= 300 && response.status < 400\n const opaqueRedirect = response.type === 'opaqueredirect'\n const responseUrlChanged = response.url.length > 0 && response.url !== endpoint.href\n if (response.redirected || redirectStatus || opaqueRedirect || responseUrlChanged) {\n await cancelBody(response)\n throw new ObservationProtocolError('observation exporter rejected a redirect before following it')\n }\n if (response.url.length > 0) {\n let responseOrigin: string\n try { responseOrigin = new URL(response.url).origin } catch {\n throw new ObservationProtocolError('observation response URL is invalid')\n }\n if (responseOrigin !== endpoint.origin) {\n throw new ObservationProtocolError('observation response origin does not match its endpoint')\n }\n }\n}\n\n","import { abortableDelay } from './fetch-transport.ts'\n\nconst DEFAULT_REQUEST_TIMEOUT_MS = 10_000\nconst DEFAULT_MAX_ATTEMPTS = 8\nconst DEFAULT_BASE_DELAY_MS = 250\nconst DEFAULT_MAX_DELAY_MS = 30_000\nconst DEFAULT_MAX_BATCH_EVENTS = 256\nconst DEFAULT_MAX_BATCH_BYTES = 512 * 1024\nconst DEFAULT_MAX_ACK_BYTES = 64 * 1024\nconst DEFAULT_MAX_ACK_CHUNKS = 1_000\nconst FORBIDDEN_HEADER_NAMES = new Set(['content-length', 'content-type', 'host', 'idempotency-key'])\n\nexport interface FetchObservationExporterOptions {\n readonly id?: string\n readonly endpoint: string | URL\n readonly headers?: Readonly<Record<string, string>>\n readonly fetch?: typeof fetch\n /** Permit cleartext only for localhost/loopback test endpoints. */\n readonly allowInsecureHttp?: boolean\n readonly requestTimeoutMs?: number\n readonly maxAttempts?: number\n readonly baseDelayMs?: number\n readonly maxDelayMs?: number\n readonly maxBatchEvents?: number\n readonly maxBatchBytes?: number\n readonly maxAckBytes?: number\n readonly maxAckChunks?: number\n /** Deterministic injection for tests; defaults to `Math.random`. */\n readonly random?: () => number\n /** Timer injection for tests; defaults to an abortable Web timer. */\n readonly delay?: (milliseconds: number, signal: AbortSignal) => Promise<void>\n /** Clock injection used only for HTTP-date `Retry-After`. */\n readonly now?: () => number\n}\n\nexport interface ResolvedOptions {\n readonly endpoint: URL\n readonly headers: Readonly<Record<string, string>>\n readonly fetch: typeof fetch\n readonly requestTimeoutMs: number\n readonly maxAttempts: number\n readonly baseDelayMs: number\n readonly maxDelayMs: number\n readonly maxBatchEvents: number\n readonly maxBatchBytes: number\n readonly maxAckBytes: number\n readonly maxAckChunks: number\n readonly random: () => number\n readonly delay: (milliseconds: number, signal: AbortSignal) => Promise<void>\n readonly now: () => number\n}\n\nfunction positiveSafeInteger(value: number, field: string): number {\n if (!Number.isSafeInteger(value) || value < 1) throw new RangeError(`${field} must be a positive safe integer`)\n return value\n}\n\nfunction nonNegativeFinite(value: number, field: string): number {\n if (!Number.isFinite(value) || value < 0) throw new RangeError(`${field} must be a non-negative finite number`)\n return value\n}\n\nfunction endpointOf(value: string | URL, allowInsecureHttp: boolean): URL {\n const endpoint = new URL(value)\n if (endpoint.username.length > 0 || endpoint.password.length > 0) {\n throw new TypeError('observation endpoint must not contain credentials')\n }\n const local = endpoint.hostname === 'localhost' || endpoint.hostname === '127.0.0.1'\n || endpoint.hostname === '[::1]' || endpoint.hostname === '::1'\n if (endpoint.protocol !== 'https:' && !(allowInsecureHttp && endpoint.protocol === 'http:' && local)) {\n throw new TypeError('observation endpoint must use https')\n }\n return endpoint\n}\n\nfunction headersOf(headers: Readonly<Record<string, string>> | undefined): Readonly<Record<string, string>> {\n const output = Object.create(null) as Record<string, string>\n for (const [name, value] of Object.entries(headers ?? {})) {\n const normalized = name.toLowerCase()\n if (FORBIDDEN_HEADER_NAMES.has(normalized)) {\n throw new TypeError(`observation header ${name} is managed by the exporter`)\n }\n if (typeof value !== 'string' || !/^[!#$%&'*+.^_`|~0-9A-Za-z-]+$/.test(name) || /[\\r\\n]/.test(value)) {\n throw new TypeError(`observation header ${name} is invalid`)\n }\n output[normalized] = value\n }\n return Object.freeze(output)\n}\n\nexport function resolveOptions(options: FetchObservationExporterOptions): ResolvedOptions {\n validateInjectedFunctions(options)\n return Object.freeze({\n endpoint: endpointOf(options.endpoint, options.allowInsecureHttp ?? false),\n headers: headersOf(options.headers),\n fetch: options.fetch ?? globalThis.fetch,\n ...retryOptions(options),\n ...resourceLimits(options),\n random: options.random ?? Math.random,\n delay: options.delay ?? abortableDelay,\n now: options.now ?? Date.now,\n })\n}\n\n\nfunction validateInjectedFunctions(options: FetchObservationExporterOptions) {\n if (typeof options?.fetch !== 'function' && typeof globalThis.fetch !== 'function') {\n throw new TypeError('Fetch observation exporter requires fetch')\n }\n if (options.random !== undefined && typeof options.random !== 'function') {\n throw new TypeError('random must be a function')\n }\n if (options.delay !== undefined && typeof options.delay !== 'function') {\n throw new TypeError('delay must be a function')\n }\n if (options.now !== undefined && typeof options.now !== 'function') {\n throw new TypeError('now must be a function')\n }\n}\n\nfunction retryOptions(options: FetchObservationExporterOptions) {\n return {\n requestTimeoutMs: positiveSafeInteger(options.requestTimeoutMs ?? DEFAULT_REQUEST_TIMEOUT_MS, 'requestTimeoutMs'),\n maxAttempts: positiveSafeInteger(options.maxAttempts ?? DEFAULT_MAX_ATTEMPTS, 'maxAttempts'),\n baseDelayMs: nonNegativeFinite(options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS, 'baseDelayMs'),\n maxDelayMs: nonNegativeFinite(options.maxDelayMs ?? DEFAULT_MAX_DELAY_MS, 'maxDelayMs'),\n }\n}\n\nfunction resourceLimits(options: FetchObservationExporterOptions) {\n return {\n maxBatchEvents: positiveSafeInteger(options.maxBatchEvents ?? DEFAULT_MAX_BATCH_EVENTS, 'maxBatchEvents'),\n maxBatchBytes: positiveSafeInteger(options.maxBatchBytes ?? DEFAULT_MAX_BATCH_BYTES, 'maxBatchBytes'),\n maxAckBytes: positiveSafeInteger(options.maxAckBytes ?? DEFAULT_MAX_ACK_BYTES, 'maxAckBytes'),\n maxAckChunks: positiveSafeInteger(options.maxAckChunks ?? DEFAULT_MAX_ACK_CHUNKS, 'maxAckChunks'),\n }\n}\n","import type { ResolvedOptions } from './fetch-options.ts'\nimport { retryableStatus, retryAfterMs, backoffMs, combinedSignal, raceAbort, cancelBody,\n boundedText, validateNoRedirectResponse, ObservationProtocolError } from './fetch-transport.ts'\n\ninterface BatchSendResult { readonly accepted: boolean; readonly retryable: boolean }\ninterface Batch { id: string; count: number; value: unknown }\ntype AttemptResult = { result: BatchSendResult } | { retryDelay: number }\n\nasync function successfulResponse(response: Response, options: ResolvedOptions, batchId: string, signal: AbortSignal)\n : Promise<BatchSendResult> {\n if (response.status === 204) {\n await cancelBody(response)\n return Object.freeze({ accepted: true, retryable: false })\n }\n const raw = await boundedText(response, options, signal)\n let parsed: unknown\n try { parsed = JSON.parse(raw) } catch { parsed = undefined }\n const acceptedBatchId = typeof parsed === 'object' && parsed !== null\n ? Reflect.get(parsed, 'acceptedBatchId') : undefined\n return Object.freeze({ accepted: acceptedBatchId === batchId, retryable: false })\n}\n\nasync function responseResult(response: Response, options: ResolvedOptions,\n batchId: string, control: { attempt: number; signal: AbortSignal }): Promise<AttemptResult> {\n await validateNoRedirectResponse(response, options.endpoint)\n if (response.status >= 200 && response.status < 300) {\n return { result: await successfulResponse(response, options, batchId, control.signal) }\n }\n const retryable = retryableStatus(response.status)\n const retryAfter = retryAfterMs(response.headers.get('retry-after'), options.now())\n await cancelBody(response)\n if (!retryable || control.attempt === options.maxAttempts) {\n return { result: Object.freeze({ accepted: false, retryable }) }\n }\n return { retryDelay: retryAfter ?? backoffMs(control.attempt, options) }\n}\n\nfunction failedAttempt(error: unknown, options: ResolvedOptions, attempt: number, signal: AbortSignal): AttemptResult {\n if (signal.aborted) throw signal.reason ?? error\n if (error instanceof ObservationProtocolError || error instanceof RangeError) {\n return { result: Object.freeze({ accepted: false, retryable: false }) }\n }\n if (attempt === options.maxAttempts) return { result: Object.freeze({ accepted: false, retryable: true }) }\n return { retryDelay: backoffMs(attempt, options) }\n}\n\nasync function sendAttempt(options: ResolvedOptions, batchId: string, body: string,\n control: { attempt: number; signal: AbortSignal }): Promise<AttemptResult> {\n const request = combinedSignal(control.signal, options.requestTimeoutMs)\n // Native Window/Worker fetch must be invoked as a standalone callable.\n const dispatch = options.fetch\n try {\n const response = await raceAbort(Promise.resolve(dispatch(options.endpoint, {\n method: 'POST', headers: { ...options.headers, 'content-type': 'application/json', 'idempotency-key': batchId },\n body, redirect: 'manual', signal: request.signal,\n })), request.signal)\n return await responseResult(response, options, batchId, { attempt: control.attempt, signal: request.signal })\n } catch (error) {\n return failedAttempt(error, options, control.attempt, control.signal)\n } finally { request.clear() }\n}\n\nexport async function sendBatch(options: ResolvedOptions, batch: Batch, signal: AbortSignal): Promise<BatchSendResult> {\n if (batch.count > options.maxBatchEvents) return Object.freeze({ accepted: false, retryable: false })\n const body = JSON.stringify(batch.value)\n if (new TextEncoder().encode(body).byteLength > options.maxBatchBytes) {\n return Object.freeze({ accepted: false, retryable: false })\n }\n for (let attempt = 1; attempt <= options.maxAttempts; attempt++) {\n if (signal.aborted) throw signal.reason ?? new Error('observation export aborted')\n const outcome = await sendAttempt(options, batch.id, body, { attempt, signal })\n if ('result' in outcome) return outcome.result\n await raceAbort(Promise.resolve(options.delay(outcome.retryDelay, signal)), signal)\n }\n return Object.freeze({ accepted: false, retryable: true })\n}\n","import { type ObservationBoundary } from '@alvin0/ai-agent-sdk-core'\nimport type {\n ExportAck,\n ObservationBatch,\n ObservationExporter,\n} from '@alvin0/ai-agent-sdk-core/observability'\nimport {\n defineObservationExporter,\n type ObservationDeliveryBatch,\n type ObservationExporterPlugin,\n} from '@alvin0/ai-agent-sdk-core/observability'\n\nimport { resolveOptions, type ResolvedOptions, type FetchObservationExporterOptions } from './fetch-options.ts'\nexport type { FetchObservationExporterOptions } from './fetch-options.ts'\nimport { sendBatch } from './fetch-send.ts'\n\n/** A remote-acknowledged exporter using only Fetch and Web streams. */\nexport class FetchObservationExporter implements ObservationExporter {\n readonly id: string\n readonly supportedBoundaries: readonly ObservationBoundary[] = Object.freeze(['remote-acknowledged'])\n private readonly options: ResolvedOptions\n\n constructor(options: FetchObservationExporterOptions) {\n if (typeof options !== 'object' || options === null) {\n throw new TypeError('Fetch observation exporter options are required')\n }\n this.id = options.id ?? 'fetch'\n if (!/^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,63})$/.test(this.id)) {\n throw new TypeError('Fetch observation exporter id must be a safe 1-64 character identifier')\n }\n this.options = resolveOptions(options)\n }\n\n async export(batch: ObservationBatch, signal: AbortSignal): Promise<ExportAck> {\n const result = await sendBatch(this.options,\n { id: batch.batchId, count: batch.events.length, value: batch }, signal)\n return Object.freeze({ batchId: batch.batchId, ...result })\n }\n}\n\n/** Recommended inert runtime exporter; transport begins only when core exports a batch. */\nexport function fetchObservationExporter(\n options: FetchObservationExporterOptions,\n): ObservationExporterPlugin {\n if (typeof options !== 'object' || options === null) {\n throw new TypeError('Fetch observation exporter options are required')\n }\n const id = options.id ?? 'fetch'\n if (!/^[A-Za-z0-9](?:[A-Za-z0-9._-]{0,63})$/.test(id)) {\n throw new TypeError('Fetch observation exporter id must be a safe 1-64 character identifier')\n }\n const captured = resolveOptions(options)\n return defineObservationExporter({\n id,\n supportedBoundaries: ['remote-acknowledged'],\n async export(batch: ObservationDeliveryBatch, signal: AbortSignal) {\n const result = await sendBatch(\n captured,\n { id: batch.id, count: batch.events.length + batch.runRecords.length, value: batch },\n signal,\n )\n return Object.freeze({\n batchId: batch.id,\n acceptedEventIds: result.accepted ? batch.events.map(event => event.eventId) : [],\n acceptedRunIds: result.accepted ? batch.runRecords.map(record => record.runId) : [],\n })\n },\n })\n}\n","import type { FlushResult, Observability } from '@alvin0/ai-agent-sdk-core/observability'\n\nexport type WaitUntil = (pending: Promise<unknown>) => void\n\n/** Start one flush and explicitly attach it to an Edge host's lifetime. */\nexport function flushObservabilityWithWaitUntil(\n observation: Pick<Observability, 'flush'>,\n waitUntil: WaitUntil,\n signal?: AbortSignal,\n): Promise<FlushResult> {\n if (typeof waitUntil !== 'function') throw new TypeError('waitUntil must be a function')\n const pending = observation.flush(signal)\n waitUntil(pending)\n return pending\n}\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"name": "alvin0 - chaulamdinhai",
|
|
5
5
|
"email": "chaulamdinhai@gmail.com"
|
|
6
6
|
},
|
|
7
|
-
"version": "0.1.
|
|
7
|
+
"version": "0.1.11",
|
|
8
8
|
"description": "Acknowledged HTTPS observation batch exporter for Edge and Web runtimes",
|
|
9
9
|
"license": "MIT",
|
|
10
10
|
"repository": {
|
|
@@ -38,10 +38,10 @@
|
|
|
38
38
|
"provenance": true
|
|
39
39
|
},
|
|
40
40
|
"peerDependencies": {
|
|
41
|
-
"@alvin0/ai-agent-sdk-core": "^0.1.
|
|
41
|
+
"@alvin0/ai-agent-sdk-core": "^0.1.11"
|
|
42
42
|
},
|
|
43
43
|
"devDependencies": {
|
|
44
|
-
"@alvin0/ai-agent-sdk-core": "^0.1.
|
|
44
|
+
"@alvin0/ai-agent-sdk-core": "^0.1.11",
|
|
45
45
|
"@arethetypeswrong/cli": "0.18.5",
|
|
46
46
|
"playwright": "1.62.1",
|
|
47
47
|
"publint": "0.3.24",
|