stitchkit 0.76.2 → 0.78.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agent-runtime-coding-tools.js +1 -1
- package/dist/agent-runtime-harness.js +6 -6
- package/dist/agent-runtime.js +11 -11
- package/dist/application/decisions.d.ts +89 -0
- package/dist/application/decisions.d.ts.map +1 -0
- package/dist/application/kernel.d.ts +35 -1
- package/dist/application/kernel.d.ts.map +1 -1
- package/dist/application/keyspace.d.ts +11 -1
- package/dist/application/keyspace.d.ts.map +1 -1
- package/dist/application/schedule.d.ts.map +1 -1
- package/dist/application/server-resource.d.ts.map +1 -1
- package/dist/application-diagnostic-journal.d.ts +21 -0
- package/dist/application-diagnostic-journal.d.ts.map +1 -0
- package/dist/application-diagnostic-journal.js +714 -0
- package/dist/application-opentelemetry.js +92 -5
- package/dist/application-schemas.d.ts +15 -0
- package/dist/application-schemas.d.ts.map +1 -0
- package/dist/application-schemas.js +26 -0
- package/dist/application.d.ts +3 -3
- package/dist/application.d.ts.map +1 -1
- package/dist/application.js +1832 -844
- package/dist/cli.js +7 -7
- package/dist/contract/index.js +9 -7
- package/dist/files/boundary.d.ts.map +1 -1
- package/dist/files.js +1 -1
- package/dist/{index-3z73fh2c.js → index-1ckavx4h.js} +49 -1
- package/dist/{index-s2rchahr.js → index-31k1ns1s.js} +4 -4
- package/dist/{index-m668wzyc.js → index-4ntp63ps.js} +4 -4
- package/dist/{index-1rxswfbv.js → index-56fm73re.js} +1 -1
- package/dist/{index-ezmn6ac6.js → index-5r0ak8fr.js} +3 -3
- package/dist/{index-f6n5n7nz.js → index-7z69kyrh.js} +10 -87
- package/dist/{index-k2zczx1g.js → index-93xp5tgg.js} +1 -1
- package/dist/{index-bfcpjw20.js → index-actkayzn.js} +7 -3
- package/dist/index-anyqnqcv.js +79 -0
- package/dist/{index-7qy2ex0m.js → index-cwp38wxn.js} +1 -1
- package/dist/{index-35z5h2ty.js → index-dmfn9m4x.js} +125 -4
- package/dist/index-h4pj6fta.js +87 -0
- package/dist/{index-jqtsc9mj.js → index-j6mfaw26.js} +2 -2
- package/dist/{index-58jzmnn4.js → index-k45qqrdh.js} +513 -39
- package/dist/{index-8eywc9zv.js → index-kw12fsbc.js} +0 -1
- package/dist/index-n1g2xm6a.js +203 -0
- package/dist/{index-t8qyvrvg.js → index-n7apa6sy.js} +12 -12
- package/dist/{index-s4c8wy8m.js → index-qaajjfp6.js} +8 -8
- package/dist/{index-nemjkxjp.js → index-s5tn4kdv.js} +14 -10
- package/dist/index-wfq8zerm.js +92 -0
- package/dist/index.js +53 -1215
- package/dist/internal/deadline.d.ts +17 -0
- package/dist/internal/deadline.d.ts.map +1 -0
- package/dist/internal/decision.d.ts +31 -0
- package/dist/internal/decision.d.ts.map +1 -0
- package/dist/live/events.d.ts +8 -13
- package/dist/live/events.d.ts.map +1 -1
- package/dist/live.d.ts +1 -1
- package/dist/live.d.ts.map +1 -1
- package/dist/live.js +12 -89
- package/dist/node.js +5 -6
- package/dist/observability/context.d.ts.map +1 -1
- package/dist/observability/index.js +3 -3
- package/dist/primitives.js +2 -1
- package/dist/remote.js +3 -6
- package/dist/server/event-bus.d.ts +3 -2
- package/dist/server/event-bus.d.ts.map +1 -1
- package/dist/server/index.js +31 -30
- package/dist/testing.js +1562 -87
- package/dist/tool-invoker.js +6 -6
- package/dist/tools.js +66 -23
- package/llms-full.txt +276 -6
- package/package.json +12 -4
- package/dist/index-3cwck0rm.js +0 -897
- package/dist/index-7b188kmz.js +0 -49
- package/dist/index-a916km3r.js +0 -45
- package/dist/index-hvftzz91.js +0 -721
- package/dist/{index-da1aqnhb.js → index-aewy3x5j.js} +3 -3
- package/dist/{index-nt1mp8km.js → index-efm026pw.js} +3 -3
|
@@ -1,19 +1,71 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DEFAULT_STREAM_LINE_BYTES,
|
|
3
|
-
inputIsQuery,
|
|
4
|
-
parseSSE,
|
|
5
|
-
readBoundedUtf8Lines
|
|
6
|
-
} from "./index-1rxswfbv.js";
|
|
7
1
|
import {
|
|
8
2
|
ContractStreamFrameSchema,
|
|
9
3
|
DEFAULT_CONTRACT_STREAM_FRAME_BYTES,
|
|
10
4
|
parseTrailingWildcard
|
|
11
|
-
} from "./index-
|
|
5
|
+
} from "./index-7z69kyrh.js";
|
|
12
6
|
import {
|
|
13
7
|
isRecord,
|
|
14
8
|
mapObject,
|
|
9
|
+
transportResult,
|
|
15
10
|
typedEntries
|
|
16
|
-
} from "./index-
|
|
11
|
+
} from "./index-ksp6e2ye.js";
|
|
12
|
+
|
|
13
|
+
// src/internal/random-hex.ts
|
|
14
|
+
function randomHex(bytes) {
|
|
15
|
+
const arr = new Uint8Array(bytes);
|
|
16
|
+
crypto.getRandomValues(arr);
|
|
17
|
+
let hex = "";
|
|
18
|
+
for (const byte of arr)
|
|
19
|
+
hex += byte.toString(16).padStart(2, "0");
|
|
20
|
+
return hex;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// src/observability/trace.ts
|
|
24
|
+
var TRACEPARENT_RE = /^([0-9a-f]{2})-([0-9a-f]{32})-([0-9a-f]{16})-([0-9a-f]{2})(.*)$/i;
|
|
25
|
+
function createTraceContext() {
|
|
26
|
+
return { traceId: randomHex(16), spanId: randomHex(8) };
|
|
27
|
+
}
|
|
28
|
+
function parseTraceparent(header) {
|
|
29
|
+
if (!header)
|
|
30
|
+
return null;
|
|
31
|
+
const match = TRACEPARENT_RE.exec(header.trim());
|
|
32
|
+
if (!match?.[1] || !match[2] || !match[3] || !match[4] || match[5] === undefined)
|
|
33
|
+
return null;
|
|
34
|
+
const version = match[1].toLowerCase();
|
|
35
|
+
if (version === "ff")
|
|
36
|
+
return null;
|
|
37
|
+
const suffix = match[5];
|
|
38
|
+
if (version === "00" ? suffix !== "" : suffix !== "" && !/^(?:-[0-9a-f]{2,})+$/i.test(suffix)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
const traceId = match[2].toLowerCase();
|
|
42
|
+
const parentSpanId = match[3].toLowerCase();
|
|
43
|
+
const traceFlags = match[4].toLowerCase();
|
|
44
|
+
if (/^0+$/.test(traceId) || /^0+$/.test(parentSpanId))
|
|
45
|
+
return null;
|
|
46
|
+
return { traceId, spanId: randomHex(8), parentSpanId, traceFlags };
|
|
47
|
+
}
|
|
48
|
+
function formatTraceparent(ctx) {
|
|
49
|
+
return `00-${ctx.traceId}-${ctx.spanId}-${ctx.traceFlags ?? "01"}`;
|
|
50
|
+
}
|
|
51
|
+
function childSpan(parent) {
|
|
52
|
+
return {
|
|
53
|
+
traceId: parent.traceId,
|
|
54
|
+
spanId: randomHex(8),
|
|
55
|
+
parentSpanId: parent.spanId,
|
|
56
|
+
...parent.tracestate !== undefined && { tracestate: parent.tracestate },
|
|
57
|
+
...parent.baggage !== undefined && { baggage: parent.baggage },
|
|
58
|
+
...parent.traceFlags !== undefined && { traceFlags: parent.traceFlags }
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
var encoder = new TextEncoder;
|
|
62
|
+
|
|
63
|
+
// src/browser/http.ts
|
|
64
|
+
import ky, {
|
|
65
|
+
isHTTPError,
|
|
66
|
+
isNetworkError,
|
|
67
|
+
isTimeoutError
|
|
68
|
+
} from "ky";
|
|
17
69
|
|
|
18
70
|
// src/browser/cancellation.ts
|
|
19
71
|
class RequestCancellationError extends Error {
|
|
@@ -72,13 +124,6 @@ function cancellationError(cause) {
|
|
|
72
124
|
return new RequestCancellationError(cause);
|
|
73
125
|
}
|
|
74
126
|
|
|
75
|
-
// src/browser/http.ts
|
|
76
|
-
import ky, {
|
|
77
|
-
isHTTPError,
|
|
78
|
-
isNetworkError,
|
|
79
|
-
isTimeoutError
|
|
80
|
-
} from "ky";
|
|
81
|
-
|
|
82
127
|
// src/browser/request-id.ts
|
|
83
128
|
var REQUEST_ID_HEADER = "x-request-id";
|
|
84
129
|
function responseTraceId(response) {
|
|
@@ -114,6 +159,15 @@ class ApiError extends Error {
|
|
|
114
159
|
function refuseLocally(path, message) {
|
|
115
160
|
return new ApiError("VALIDATION_ERROR", 0, { issues: [{ path, code: "invalid_type", message }] }, message);
|
|
116
161
|
}
|
|
162
|
+
function shouldRetryBunNetworkError(error) {
|
|
163
|
+
if (ApiError.is(error) || isHTTPError(error) || isNetworkError(error) || isTimeoutError(error) || error instanceof RequestCancellationError || error instanceof DOMException && (error.name === "AbortError" || error.name === "TimeoutError")) {
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
if (!(error instanceof Error))
|
|
167
|
+
return;
|
|
168
|
+
const code = Object.getOwnPropertyDescriptor(error, "code");
|
|
169
|
+
return code && "value" in code && code.value === "ConnectionRefused" ? true : undefined;
|
|
170
|
+
}
|
|
117
171
|
function parseApiErrorBody(body) {
|
|
118
172
|
if (!isRecord(body) || !isRecord(body.error))
|
|
119
173
|
return null;
|
|
@@ -127,6 +181,329 @@ function parseApiErrorBody(body) {
|
|
|
127
181
|
hint: typeof error.hint === "string" ? error.hint : undefined
|
|
128
182
|
};
|
|
129
183
|
}
|
|
184
|
+
function createRetryAwareFetch(transportFetch, unix) {
|
|
185
|
+
const runtimeFetch = transportFetch;
|
|
186
|
+
let attempt = 0;
|
|
187
|
+
return (input, init) => {
|
|
188
|
+
attempt += 1;
|
|
189
|
+
if (unix === undefined && attempt === 1) {
|
|
190
|
+
return runtimeFetch(input, init);
|
|
191
|
+
}
|
|
192
|
+
if (!(input instanceof Request)) {
|
|
193
|
+
if (unix === undefined)
|
|
194
|
+
return runtimeFetch(input, init);
|
|
195
|
+
const unixInit = { ...init, unix };
|
|
196
|
+
return runtimeFetch(input, unixInit);
|
|
197
|
+
}
|
|
198
|
+
const streamedBody = input.body ? { body: input.body, duplex: "half" } : {};
|
|
199
|
+
const materialized = {
|
|
200
|
+
...init,
|
|
201
|
+
...unix !== undefined && { unix },
|
|
202
|
+
method: input.method,
|
|
203
|
+
headers: input.headers,
|
|
204
|
+
...streamedBody,
|
|
205
|
+
cache: input.cache,
|
|
206
|
+
credentials: input.credentials,
|
|
207
|
+
integrity: input.integrity,
|
|
208
|
+
keepalive: input.keepalive,
|
|
209
|
+
mode: input.mode,
|
|
210
|
+
redirect: input.redirect,
|
|
211
|
+
referrer: input.referrer,
|
|
212
|
+
referrerPolicy: input.referrerPolicy,
|
|
213
|
+
signal: input.signal
|
|
214
|
+
};
|
|
215
|
+
return runtimeFetch(input.url, materialized);
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function createHttpClient(config) {
|
|
219
|
+
if (config.fetch && config.unix) {
|
|
220
|
+
throw new TypeError("HttpClientConfig.fetch and unix are mutually exclusive");
|
|
221
|
+
}
|
|
222
|
+
if (config.unix !== undefined) {
|
|
223
|
+
if (!config.unix.startsWith("/") || config.unix.includes("\x00")) {
|
|
224
|
+
throw new TypeError("HttpClientConfig.unix must be an absolute Unix socket path");
|
|
225
|
+
}
|
|
226
|
+
if (typeof Reflect.get(globalThis, "Bun") !== "object") {
|
|
227
|
+
throw new TypeError("HttpClientConfig.unix requires Bun; on Bun or Node use createUnixClientTransport().fetch for an explicit portable transport");
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
let ssrCookies = null;
|
|
231
|
+
let isLoggedOut = false;
|
|
232
|
+
const listeners = new Set;
|
|
233
|
+
const suppressUnauthorizedFor = config.suppressUnauthorizedFor ?? [];
|
|
234
|
+
const parseError = config.parseError ?? parseApiErrorBody;
|
|
235
|
+
function emit(event) {
|
|
236
|
+
for (const fn of listeners) {
|
|
237
|
+
try {
|
|
238
|
+
fn(event);
|
|
239
|
+
} catch {}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
const client = ky.create({
|
|
243
|
+
prefix: config.baseUrl,
|
|
244
|
+
credentials: config.credentials ?? "include",
|
|
245
|
+
timeout: false,
|
|
246
|
+
retry: {
|
|
247
|
+
limit: config.retry?.limit ?? 2,
|
|
248
|
+
methods: config.retry?.methods ?? ["get"],
|
|
249
|
+
statusCodes: config.retry?.statusCodes ?? [],
|
|
250
|
+
shouldRetry: ({ error }) => shouldRetryBunNetworkError(error)
|
|
251
|
+
},
|
|
252
|
+
hooks: {
|
|
253
|
+
beforeRequest: [
|
|
254
|
+
({ request: request2 }) => {
|
|
255
|
+
if (ssrCookies) {
|
|
256
|
+
request2.headers.set("Cookie", ssrCookies);
|
|
257
|
+
}
|
|
258
|
+
const extra = typeof config.headers === "function" ? config.headers() : config.headers;
|
|
259
|
+
if (extra) {
|
|
260
|
+
for (const [key, value] of Object.entries(extra)) {
|
|
261
|
+
request2.headers.set(key, value);
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
if (config.trace && !request2.headers.has("traceparent")) {
|
|
265
|
+
request2.headers.set("traceparent", formatTraceparent(createTraceContext()));
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
],
|
|
269
|
+
afterResponse: [
|
|
270
|
+
async ({ request: request2, response }) => {
|
|
271
|
+
if (response.status === 401) {
|
|
272
|
+
const url = new URL(request2.url).pathname;
|
|
273
|
+
if (!isLoggedOut && !suppressUnauthorizedFor.some((matches) => matches(url))) {
|
|
274
|
+
isLoggedOut = true;
|
|
275
|
+
emit({ type: "unauthorized" });
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
if (!response.ok) {
|
|
279
|
+
const body = await response.clone().json().catch(() => null);
|
|
280
|
+
if (body) {
|
|
281
|
+
const parsed = parseError(body);
|
|
282
|
+
if (parsed) {
|
|
283
|
+
throw new ApiError(parsed.code, response.status, parsed.details, parsed.message, parsed.hint, responseTraceId(response));
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
]
|
|
289
|
+
}
|
|
290
|
+
});
|
|
291
|
+
async function request(method, url, data, options = {}) {
|
|
292
|
+
const cancellation = createRequestCancellation(options.signal, options.timeout ?? config.timeout ?? 30000);
|
|
293
|
+
const kyOptions = {
|
|
294
|
+
fetch: createRetryAwareFetch(config.fetch ?? globalThis.fetch.bind(globalThis), config.unix),
|
|
295
|
+
timeout: false,
|
|
296
|
+
signal: cancellation.signal
|
|
297
|
+
};
|
|
298
|
+
if (options.params) {
|
|
299
|
+
const searchParams = new URLSearchParams;
|
|
300
|
+
for (const [key, value] of Object.entries(options.params)) {
|
|
301
|
+
if (value === undefined)
|
|
302
|
+
continue;
|
|
303
|
+
if (Array.isArray(value)) {
|
|
304
|
+
for (const item of value)
|
|
305
|
+
searchParams.append(key, String(item));
|
|
306
|
+
} else {
|
|
307
|
+
searchParams.set(key, String(value));
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
if (searchParams.size > 0) {
|
|
311
|
+
kyOptions.searchParams = searchParams;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (data instanceof FormData) {
|
|
315
|
+
kyOptions.body = data;
|
|
316
|
+
} else if (data !== undefined) {
|
|
317
|
+
kyOptions.json = data;
|
|
318
|
+
}
|
|
319
|
+
try {
|
|
320
|
+
return await cancellation.run(async () => {
|
|
321
|
+
if (options.responseType === "blob") {
|
|
322
|
+
return transportResult(await client[method](url, kyOptions).blob());
|
|
323
|
+
}
|
|
324
|
+
if (options.responseType === "response") {
|
|
325
|
+
return transportResult(await client[method](url, kyOptions));
|
|
326
|
+
}
|
|
327
|
+
const response = await client[method](url, kyOptions);
|
|
328
|
+
if (options.responseType === "void") {
|
|
329
|
+
const text = await response.text();
|
|
330
|
+
if (text.length > 0) {
|
|
331
|
+
throw new Error("Server returned data for an endpoint with no output contract");
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (options.responseType === "void" || response.status === 204 || response.headers.get("content-length") === "0") {
|
|
335
|
+
return transportResult(undefined);
|
|
336
|
+
}
|
|
337
|
+
return await response.json();
|
|
338
|
+
});
|
|
339
|
+
} catch (error) {
|
|
340
|
+
if (error instanceof RequestCancellationError) {
|
|
341
|
+
throw new ApiError(error.cause === "caller" ? "REQUEST_ABORTED" : "REQUEST_TIMEOUT", 0, undefined, error.message);
|
|
342
|
+
}
|
|
343
|
+
if (ApiError.is(error))
|
|
344
|
+
throw error;
|
|
345
|
+
emit({ type: "network_error" });
|
|
346
|
+
const response = isHTTPError(error) ? error.response : undefined;
|
|
347
|
+
const status = response?.status ?? 0;
|
|
348
|
+
const msg = error instanceof Error ? error.message : undefined;
|
|
349
|
+
throw new ApiError("UNKNOWN_ERROR", status, msg ? { message: msg } : undefined, msg, undefined, responseTraceId(response), { cause: error });
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
return {
|
|
353
|
+
baseUrl: config.baseUrl,
|
|
354
|
+
get: (url, options) => request("get", url, undefined, options),
|
|
355
|
+
head: (url, options) => request("head", url, undefined, options),
|
|
356
|
+
post: (url, data, options) => request("post", url, data, options),
|
|
357
|
+
put: (url, data, options) => request("put", url, data, options),
|
|
358
|
+
patch: (url, data, options) => request("patch", url, data, options),
|
|
359
|
+
delete: (url, options) => request("delete", url, undefined, options),
|
|
360
|
+
setServerContext(cookies) {
|
|
361
|
+
ssrCookies = cookies;
|
|
362
|
+
},
|
|
363
|
+
subscribe(listener) {
|
|
364
|
+
listeners.add(listener);
|
|
365
|
+
return () => listeners.delete(listener);
|
|
366
|
+
},
|
|
367
|
+
logout() {
|
|
368
|
+
isLoggedOut = true;
|
|
369
|
+
emit({ type: "logout" });
|
|
370
|
+
},
|
|
371
|
+
resetLogoutState() {
|
|
372
|
+
isLoggedOut = false;
|
|
373
|
+
}
|
|
374
|
+
};
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
// src/internal/errors.ts
|
|
378
|
+
import { z } from "zod";
|
|
379
|
+
function issuePath(path) {
|
|
380
|
+
return path.length > 0 ? path.map(String).join(".") : "(root)";
|
|
381
|
+
}
|
|
382
|
+
function formatZodError(error) {
|
|
383
|
+
const issues = error.issues.slice(0, 5);
|
|
384
|
+
const lines = issues.map((issue) => `${issuePath(issue.path)}: ${issue.message}`);
|
|
385
|
+
const suffix = error.issues.length > 5 ? `
|
|
386
|
+
...and ${error.issues.length - 5} more issues` : "";
|
|
387
|
+
return lines.join(`
|
|
388
|
+
`) + suffix;
|
|
389
|
+
}
|
|
390
|
+
function zodIssues(error) {
|
|
391
|
+
return error.issues.map((issue) => ({
|
|
392
|
+
path: issuePath(issue.path),
|
|
393
|
+
code: issue.code,
|
|
394
|
+
message: issue.message
|
|
395
|
+
}));
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
// src/internal/bounded-lines.ts
|
|
399
|
+
var DEFAULT_STREAM_LINE_BYTES = 1024 * 1024;
|
|
400
|
+
function lineLimit(value) {
|
|
401
|
+
const resolved = value ?? DEFAULT_STREAM_LINE_BYTES;
|
|
402
|
+
if (!Number.isSafeInteger(resolved) || resolved <= 0) {
|
|
403
|
+
throw new TypeError("maxLineBytes must be a positive safe integer");
|
|
404
|
+
}
|
|
405
|
+
return resolved;
|
|
406
|
+
}
|
|
407
|
+
function joinBytes(left, right) {
|
|
408
|
+
if (left.byteLength === 0)
|
|
409
|
+
return right.slice();
|
|
410
|
+
const result = new Uint8Array(left.byteLength + right.byteLength);
|
|
411
|
+
result.set(left);
|
|
412
|
+
result.set(right, left.byteLength);
|
|
413
|
+
return result;
|
|
414
|
+
}
|
|
415
|
+
async function* readBoundedUtf8Lines(response, maxLineBytes, requireFinalNewline = false) {
|
|
416
|
+
const reader = response.body?.getReader();
|
|
417
|
+
if (!reader)
|
|
418
|
+
return;
|
|
419
|
+
const limit = lineLimit(maxLineBytes);
|
|
420
|
+
const decoder = new TextDecoder("utf-8", { fatal: true });
|
|
421
|
+
let pending = new Uint8Array;
|
|
422
|
+
try {
|
|
423
|
+
for (;; ) {
|
|
424
|
+
const chunk = await reader.read();
|
|
425
|
+
if (chunk.done)
|
|
426
|
+
break;
|
|
427
|
+
let start = 0;
|
|
428
|
+
for (let index = 0;index < chunk.value.byteLength; index += 1) {
|
|
429
|
+
if (chunk.value[index] !== 10)
|
|
430
|
+
continue;
|
|
431
|
+
const lineBytes = joinBytes(pending, chunk.value.slice(start, index));
|
|
432
|
+
if (lineBytes.byteLength > limit) {
|
|
433
|
+
throw new RangeError(`Stream line exceeds the ${limit} byte limit`);
|
|
434
|
+
}
|
|
435
|
+
yield decoder.decode(lineBytes);
|
|
436
|
+
pending = new Uint8Array;
|
|
437
|
+
start = index + 1;
|
|
438
|
+
}
|
|
439
|
+
pending = joinBytes(pending, chunk.value.slice(start));
|
|
440
|
+
if (pending.byteLength > limit) {
|
|
441
|
+
throw new RangeError(`Stream line exceeds the ${limit} byte limit`);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
if (pending.byteLength > 0) {
|
|
445
|
+
if (requireFinalNewline) {
|
|
446
|
+
throw new SyntaxError("Stream ended with an unterminated final line");
|
|
447
|
+
}
|
|
448
|
+
yield decoder.decode(pending);
|
|
449
|
+
}
|
|
450
|
+
} finally {
|
|
451
|
+
await reader.cancel().catch(() => {
|
|
452
|
+
return;
|
|
453
|
+
});
|
|
454
|
+
reader.releaseLock();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// src/server/stream.ts
|
|
459
|
+
async function* parseSSE(response, options) {
|
|
460
|
+
try {
|
|
461
|
+
for await (const rawLine of readBoundedUtf8Lines(response, options?.maxLineBytes ?? DEFAULT_STREAM_LINE_BYTES)) {
|
|
462
|
+
const line = rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine;
|
|
463
|
+
if (!line.startsWith("data:"))
|
|
464
|
+
continue;
|
|
465
|
+
const data = line.slice(5).replace(/^ /, "");
|
|
466
|
+
if (data === "[DONE]")
|
|
467
|
+
return;
|
|
468
|
+
try {
|
|
469
|
+
yield JSON.parse(data);
|
|
470
|
+
} catch (error) {
|
|
471
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
472
|
+
if (!options?.onParseError)
|
|
473
|
+
throw failure;
|
|
474
|
+
options.onParseError(data, failure);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
} catch (error) {
|
|
478
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
479
|
+
if (!options?.onParseError)
|
|
480
|
+
throw failure;
|
|
481
|
+
options.onParseError("", failure);
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
// src/browser/stream.ts
|
|
485
|
+
function parseFailure(raw, error, onParseError) {
|
|
486
|
+
const failure = error instanceof Error ? error : new Error(String(error));
|
|
487
|
+
if (!onParseError)
|
|
488
|
+
throw failure;
|
|
489
|
+
onParseError(raw, failure);
|
|
490
|
+
}
|
|
491
|
+
async function* parseNDJSON(response, options) {
|
|
492
|
+
try {
|
|
493
|
+
for await (const rawLine of readBoundedUtf8Lines(response, options?.maxLineBytes ?? DEFAULT_STREAM_LINE_BYTES, options?.finalLine === "require-newline")) {
|
|
494
|
+
const line = (rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine).trim();
|
|
495
|
+
if (line === "")
|
|
496
|
+
continue;
|
|
497
|
+
try {
|
|
498
|
+
yield JSON.parse(line);
|
|
499
|
+
} catch (error) {
|
|
500
|
+
parseFailure(line, error, options?.onParseError);
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
} catch (error) {
|
|
504
|
+
parseFailure("", error, options?.onParseError);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
130
507
|
|
|
131
508
|
// src/browser/client-multipart.ts
|
|
132
509
|
function isFileDescriptor(value) {
|
|
@@ -178,6 +555,11 @@ function buildMultipartForm(descriptor, values) {
|
|
|
178
555
|
return formData;
|
|
179
556
|
}
|
|
180
557
|
|
|
558
|
+
// src/internal/http-input.ts
|
|
559
|
+
function inputIsQuery(method) {
|
|
560
|
+
return method === "GET" || method === "DELETE";
|
|
561
|
+
}
|
|
562
|
+
|
|
181
563
|
// src/browser/client-url.ts
|
|
182
564
|
function isParamArray(value) {
|
|
183
565
|
return Array.isArray(value) && value.every((item) => typeof item === "string" || typeof item === "number");
|
|
@@ -217,6 +599,52 @@ function resolvePathPrefix(config, args) {
|
|
|
217
599
|
}
|
|
218
600
|
return config.pathPrefix(args);
|
|
219
601
|
}
|
|
602
|
+
function escapeRegex(value) {
|
|
603
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
604
|
+
}
|
|
605
|
+
function decodePathSegment(value) {
|
|
606
|
+
try {
|
|
607
|
+
return decodeURIComponent(value);
|
|
608
|
+
} catch {
|
|
609
|
+
return value;
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
function createClientRouteMatcher(endpoint, contractPrefix, config) {
|
|
613
|
+
if (typeof config?.pathPrefix === "function" && (!config.stripPrefixKeys || config.stripPrefixKeys.length === 0)) {
|
|
614
|
+
throw new Error("Dynamic pathPrefix matchers require stripPrefixKeys");
|
|
615
|
+
}
|
|
616
|
+
const markerByKey = {};
|
|
617
|
+
for (const [index, key] of (config?.stripPrefixKeys ?? []).entries()) {
|
|
618
|
+
markerByKey[key] = `__stitch_scope_${index}__`;
|
|
619
|
+
}
|
|
620
|
+
const pathPrefix = resolvePathPrefix(config, markerByKey);
|
|
621
|
+
const route = [pathPrefix, contractPrefix, endpoint.path === "/" ? "" : endpoint.path].filter(Boolean).join("/");
|
|
622
|
+
const patternSegments = route.split("/").filter(Boolean);
|
|
623
|
+
const wildcard = patternSegments.at(-1)?.startsWith("*") === true;
|
|
624
|
+
const fixedCount = wildcard ? patternSegments.length - 1 : patternSegments.length;
|
|
625
|
+
const markers = Object.values(markerByKey);
|
|
626
|
+
return (pathname) => {
|
|
627
|
+
const actualSegments = pathname.split("/").filter(Boolean).map(decodePathSegment);
|
|
628
|
+
if (wildcard ? actualSegments.length < fixedCount : actualSegments.length !== fixedCount) {
|
|
629
|
+
return false;
|
|
630
|
+
}
|
|
631
|
+
for (let index = 0;index < fixedCount; index += 1) {
|
|
632
|
+
const pattern = patternSegments[index];
|
|
633
|
+
const actual = actualSegments[index];
|
|
634
|
+
if (!pattern || actual === undefined)
|
|
635
|
+
return false;
|
|
636
|
+
if (pattern.startsWith(":"))
|
|
637
|
+
continue;
|
|
638
|
+
let source = escapeRegex(pattern);
|
|
639
|
+
for (const marker of markers) {
|
|
640
|
+
source = source.replaceAll(escapeRegex(marker), "[^/]+");
|
|
641
|
+
}
|
|
642
|
+
if (!new RegExp(`^${source}$`).test(actual))
|
|
643
|
+
return false;
|
|
644
|
+
}
|
|
645
|
+
return true;
|
|
646
|
+
};
|
|
647
|
+
}
|
|
220
648
|
function extractParamNames(path) {
|
|
221
649
|
const matches = path.match(/:(\w+)/g);
|
|
222
650
|
const names = matches ? matches.map((match) => match.slice(1)) : [];
|
|
@@ -290,29 +718,6 @@ function joinClientBaseUrl(baseUrl, relativeUrl) {
|
|
|
290
718
|
const path = relativeUrl.startsWith("/") ? relativeUrl : `/${relativeUrl}`;
|
|
291
719
|
return `${base}${path}`;
|
|
292
720
|
}
|
|
293
|
-
// src/browser/stream.ts
|
|
294
|
-
function parseFailure(raw, error, onParseError) {
|
|
295
|
-
const failure = error instanceof Error ? error : new Error(String(error));
|
|
296
|
-
if (!onParseError)
|
|
297
|
-
throw failure;
|
|
298
|
-
onParseError(raw, failure);
|
|
299
|
-
}
|
|
300
|
-
async function* parseNDJSON(response, options) {
|
|
301
|
-
try {
|
|
302
|
-
for await (const rawLine of readBoundedUtf8Lines(response, options?.maxLineBytes ?? DEFAULT_STREAM_LINE_BYTES, options?.finalLine === "require-newline")) {
|
|
303
|
-
const line = (rawLine.endsWith("\r") ? rawLine.slice(0, -1) : rawLine).trim();
|
|
304
|
-
if (line === "")
|
|
305
|
-
continue;
|
|
306
|
-
try {
|
|
307
|
-
yield JSON.parse(line);
|
|
308
|
-
} catch (error) {
|
|
309
|
-
parseFailure(line, error, options?.onParseError);
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
} catch (error) {
|
|
313
|
-
parseFailure("", error, options?.onParseError);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
721
|
|
|
317
722
|
// src/browser/contract-stream.ts
|
|
318
723
|
function sourceFor(response, descriptor) {
|
|
@@ -448,6 +853,25 @@ function withOutput(endpoint, result, abortStream) {
|
|
|
448
853
|
return schema.parse(value);
|
|
449
854
|
});
|
|
450
855
|
}
|
|
856
|
+
function contractEndpointMatchers(contract, endpointNames, contractConfig) {
|
|
857
|
+
const selected = endpointNames ? new Set(endpointNames) : null;
|
|
858
|
+
const matchers = [];
|
|
859
|
+
for (const [key, endpoint] of typedEntries(contract.endpoints)) {
|
|
860
|
+
if (selected && !selected.has(key))
|
|
861
|
+
continue;
|
|
862
|
+
if (endpoint.expose && !endpoint.expose.includes("HTTP")) {
|
|
863
|
+
if (selected) {
|
|
864
|
+
throw new Error(`Cannot create an HTTP route matcher for non-HTTP endpoint: ${String(key)}`);
|
|
865
|
+
}
|
|
866
|
+
continue;
|
|
867
|
+
}
|
|
868
|
+
matchers.push(createClientRouteMatcher(endpoint, contract.meta.prefix, contractConfig));
|
|
869
|
+
}
|
|
870
|
+
if (selected && matchers.length !== selected.size) {
|
|
871
|
+
throw new Error("Cannot create an HTTP route matcher for an unknown endpoint");
|
|
872
|
+
}
|
|
873
|
+
return matchers;
|
|
874
|
+
}
|
|
451
875
|
function createClient(contract, configOrClient, contractConfig) {
|
|
452
876
|
const client = {};
|
|
453
877
|
const makeExecutor = isHttpAdapter(configOrClient) ? (endpoint) => createHttpExecutor(endpoint, contract.meta.prefix, configOrClient, contractConfig) : (endpoint) => createFetchExecutor(endpoint, contract.meta.prefix, configOrClient, contractConfig);
|
|
@@ -461,6 +885,34 @@ function createClient(contract, configOrClient, contractConfig) {
|
|
|
461
885
|
function createClients(contracts, configOrClient, contractConfig) {
|
|
462
886
|
return mapObject(contracts, (_key, contract) => createClient(contract, configOrClient, contractConfig));
|
|
463
887
|
}
|
|
888
|
+
function buildScopedRegistry(contracts, scopeConfigs, surfaceName, build) {
|
|
889
|
+
const registry = {};
|
|
890
|
+
for (const [namespace, value] of Object.entries(contracts)) {
|
|
891
|
+
const list = Array.isArray(value) ? value : [value];
|
|
892
|
+
const surface = {};
|
|
893
|
+
for (const contract of list) {
|
|
894
|
+
const scope = contract.meta.scope;
|
|
895
|
+
if (!scope) {
|
|
896
|
+
throw new Error(`Contract in ${surfaceName} namespace "${namespace}" has no scope`);
|
|
897
|
+
}
|
|
898
|
+
const scopeConfig = scopeConfigs[scope];
|
|
899
|
+
if (!scopeConfig) {
|
|
900
|
+
throw new Error(`Missing ${surfaceName} config for scope: ${scope}`);
|
|
901
|
+
}
|
|
902
|
+
for (const [methodName, method] of Object.entries(build(contract, scopeConfig))) {
|
|
903
|
+
if (Object.hasOwn(surface, methodName)) {
|
|
904
|
+
throw new Error(`${surfaceName[0]?.toUpperCase()}${surfaceName.slice(1)} namespace "${namespace}" has duplicate method: ${methodName}`);
|
|
905
|
+
}
|
|
906
|
+
surface[methodName] = method;
|
|
907
|
+
}
|
|
908
|
+
}
|
|
909
|
+
registry[namespace] = surface;
|
|
910
|
+
}
|
|
911
|
+
return registry;
|
|
912
|
+
}
|
|
913
|
+
function createScopedClients(contracts, configOrClient, scopeConfigs) {
|
|
914
|
+
return buildScopedRegistry(contracts, scopeConfigs, "client", (contract, config) => createClient(contract, configOrClient, config));
|
|
915
|
+
}
|
|
464
916
|
function isHttpAdapter(value) {
|
|
465
917
|
return typeof value === "object" && "get" in value && typeof value.get === "function";
|
|
466
918
|
}
|
|
@@ -633,5 +1085,27 @@ async function throwForErrorResponse(res, config, fallbackBody) {
|
|
|
633
1085
|
}
|
|
634
1086
|
throw new ApiError("HTTP_ERROR", res.status, { body }, undefined, undefined, responseTraceId(res));
|
|
635
1087
|
}
|
|
1088
|
+
function createUrlBuilder(contract, source, contractConfig) {
|
|
1089
|
+
const builder = {};
|
|
1090
|
+
for (const [key, endpoint] of typedEntries(contract.endpoints)) {
|
|
1091
|
+
if (endpoint.expose && !endpoint.expose.includes("HTTP"))
|
|
1092
|
+
continue;
|
|
1093
|
+
setClientMethod(builder, key, (args) => {
|
|
1094
|
+
const plan = planClientRequest(endpoint, contract.meta.prefix, args ?? {}, contractConfig);
|
|
1095
|
+
if (endpoint.method !== "GET" && endpoint.method !== "DELETE" && Object.keys(plan.remainingArgs).length > 0) {
|
|
1096
|
+
const fields = Object.keys(plan.remainingArgs).join(", ");
|
|
1097
|
+
throw new Error(`URL builder for ${endpoint.method} ${endpoint.path} received non-URL fields: ${fields}`);
|
|
1098
|
+
}
|
|
1099
|
+
return joinClientBaseUrl(source.baseUrl, plan.relativeUrl);
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
return builder;
|
|
1103
|
+
}
|
|
1104
|
+
function createUrlBuilders(contracts, source, contractConfig) {
|
|
1105
|
+
return mapObject(contracts, (_key, contract) => createUrlBuilder(contract, source, contractConfig));
|
|
1106
|
+
}
|
|
1107
|
+
function createScopedUrlBuilders(contracts, source, scopeConfigs) {
|
|
1108
|
+
return buildScopedRegistry(contracts, scopeConfigs, "URL builder", (contract, config) => createUrlBuilder(contract, source, config));
|
|
1109
|
+
}
|
|
636
1110
|
|
|
637
|
-
export { ApiError, createClient, createClients };
|
|
1111
|
+
export { randomHex, createTraceContext, parseTraceparent, formatTraceparent, childSpan, ApiError, createHttpClient, formatZodError, zodIssues, parseSSE, parseNDJSON, contractEndpointMatchers, createClient, createClients, createScopedClients, createUrlBuilder, createUrlBuilders, createScopedUrlBuilders };
|
|
@@ -96,5 +96,4 @@ var ApplicationShutdownResultSchema = z.object({
|
|
|
96
96
|
resources: z.array(ApplicationResourceShutdownSchema).readonly(),
|
|
97
97
|
durationMs: z.number().nonnegative()
|
|
98
98
|
}).readonly();
|
|
99
|
-
|
|
100
99
|
export { ApplicationIdSchema, ApplicationLifecycleSchema, ApplicationHealthSchema, ManagedResourceStateSchema, ApplicationAdmissionSnapshotSchema, ManagedResourceSnapshotSchema, ApplicationSnapshotSchema, ApplicationStatusProjectionSchema, projectApplicationStatus, ApplicationResourceShutdownSchema, ApplicationShutdownResultSchema };
|