@caitlyn-ai/sdk-core 0.0.2 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -7
- package/dist-cjs/index.cjs +309 -173
- package/dist-cjs/index.cjs.map +3 -3
- package/dist-es/index.mjs +302 -168
- package/dist-es/index.mjs.map +3 -3
- package/dist-types/index.d.ts +3169 -12
- package/package.json +1 -2
- package/dist-types/auth.d.ts +0 -140
- package/dist-types/aws.d.ts +0 -43
- package/dist-types/client.d.ts +0 -62
- package/dist-types/endpoint.d.ts +0 -4
- package/dist-types/plugin.d.ts +0 -21
- package/dist-types/protocol.d.ts +0 -24
- package/dist-types/retry.d.ts +0 -11
- package/dist-types/sse.d.ts +0 -6
- package/dist-types/stream.d.ts +0 -34
- package/dist-types/telemetry.d.ts +0 -3
- package/dist-types/upload.d.ts +0 -12
- package/dist-types/waiter.d.ts +0 -30
package/dist-es/index.mjs
CHANGED
|
@@ -14,6 +14,181 @@ import {
|
|
|
14
14
|
} from "@smithy/core/protocols";
|
|
15
15
|
import { NormalizedSchema, TypeRegistry } from "@smithy/core/schema";
|
|
16
16
|
import { decorateServiceException } from "@smithy/core/client";
|
|
17
|
+
|
|
18
|
+
// packages/sdk-core/src/plugin.ts
|
|
19
|
+
import { getOmitRetryHeadersPlugin } from "@smithy/core/retry";
|
|
20
|
+
import { HttpRequest } from "@smithy/core/protocols";
|
|
21
|
+
import { HttpResponse } from "@smithy/core/protocols";
|
|
22
|
+
|
|
23
|
+
// packages/sdk-core/src/retry.ts
|
|
24
|
+
var NO_RETRY = /* @__PURE__ */ Symbol.for("caitlyn.noRetry");
|
|
25
|
+
var Token = class {
|
|
26
|
+
constructor(count, delay) {
|
|
27
|
+
this.count = count;
|
|
28
|
+
this.delay = delay;
|
|
29
|
+
}
|
|
30
|
+
count;
|
|
31
|
+
delay;
|
|
32
|
+
getRetryCount() {
|
|
33
|
+
return this.count;
|
|
34
|
+
}
|
|
35
|
+
getRetryDelay() {
|
|
36
|
+
return this.delay;
|
|
37
|
+
}
|
|
38
|
+
};
|
|
39
|
+
var CaitlynRetryStrategy = class {
|
|
40
|
+
constructor(maxAttempts = 3, maxRetryDelayMs = 6e4) {
|
|
41
|
+
this.maxRetryDelayMs = maxRetryDelayMs;
|
|
42
|
+
this.maxAttempts = typeof maxAttempts === "number" ? async () => maxAttempts : maxAttempts;
|
|
43
|
+
}
|
|
44
|
+
maxRetryDelayMs;
|
|
45
|
+
mode = "standard";
|
|
46
|
+
maxAttempts;
|
|
47
|
+
async acquireInitialRetryToken() {
|
|
48
|
+
return new Token(0, 0);
|
|
49
|
+
}
|
|
50
|
+
async refreshRetryTokenForRetry(token, info) {
|
|
51
|
+
const count = token.getRetryCount();
|
|
52
|
+
const err = info.error;
|
|
53
|
+
if (count + 1 >= await this.maxAttempts()) throw new Error("retry: attempts exhausted");
|
|
54
|
+
if (!err || err[NO_RETRY]) throw new Error("retry: operation is not safe to retry");
|
|
55
|
+
const noResponse = err.$metadata?.httpStatusCode === void 0 && err.name !== "AbortError";
|
|
56
|
+
if (err.$retryable === void 0 && !noResponse) throw new Error("retry: error is not retryable");
|
|
57
|
+
let delay;
|
|
58
|
+
if (info.retryAfterHint instanceof Date) {
|
|
59
|
+
delay = Math.max(0, info.retryAfterHint.getTime() - Date.now());
|
|
60
|
+
if (delay > this.maxRetryDelayMs) throw new Error("retry: Retry-After exceeds maxRetryDelay");
|
|
61
|
+
} else {
|
|
62
|
+
delay = Math.random() * Math.min(8e3, 500 * 2 ** count);
|
|
63
|
+
}
|
|
64
|
+
return new Token(count + 1, delay);
|
|
65
|
+
}
|
|
66
|
+
recordSuccess() {
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
// packages/sdk-core/src/plugin.ts
|
|
71
|
+
var CAITLYN_SDK_USER_AGENT_PREFIX = "caitlyn-sdk-ts";
|
|
72
|
+
var NO_RESPONSE = /* @__PURE__ */ Symbol.for("caitlyn.noResponse");
|
|
73
|
+
function markNoResponse(err) {
|
|
74
|
+
if (err && typeof err === "object" && err.name !== "AbortError") Object.defineProperty(err, NO_RESPONSE, { value: true, configurable: true });
|
|
75
|
+
return err;
|
|
76
|
+
}
|
|
77
|
+
function getCaitlynConnectionErrorPlugin(toError) {
|
|
78
|
+
const mark = (next) => async (args) => {
|
|
79
|
+
try {
|
|
80
|
+
return await next(args);
|
|
81
|
+
} catch (err) {
|
|
82
|
+
const e = err;
|
|
83
|
+
if (!HttpResponse.isInstance(e?.$response) && e?.$metadata?.httpStatusCode === void 0) markNoResponse(err);
|
|
84
|
+
throw err;
|
|
85
|
+
}
|
|
86
|
+
};
|
|
87
|
+
const wrap = (next, context2) => async (args) => {
|
|
88
|
+
try {
|
|
89
|
+
return await next(args);
|
|
90
|
+
} catch (err) {
|
|
91
|
+
if (err && typeof err === "object" && err[NO_RESPONSE]) {
|
|
92
|
+
throw toError(context2.commandName?.replace(/Command$/, "") ?? "call", err);
|
|
93
|
+
}
|
|
94
|
+
throw err;
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
return {
|
|
98
|
+
applyToStack(stack) {
|
|
99
|
+
stack.add(mark, { step: "deserialize", name: "caitlynNoResponse", priority: "low", override: true });
|
|
100
|
+
stack.add(wrap, { step: "initialize", name: "caitlynConnectionError", priority: "normal", override: true });
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
var NotSupportedByServer = class extends Error {
|
|
105
|
+
constructor(operation, requires, server) {
|
|
106
|
+
super(`NotSupportedByServer(${operation}, requires ${requires}, server is ${server})`);
|
|
107
|
+
this.operation = operation;
|
|
108
|
+
this.requires = requires;
|
|
109
|
+
this.server = server;
|
|
110
|
+
this.name = "NotSupportedByServer";
|
|
111
|
+
}
|
|
112
|
+
operation;
|
|
113
|
+
requires;
|
|
114
|
+
server;
|
|
115
|
+
};
|
|
116
|
+
var semver = (a, b) => {
|
|
117
|
+
const pa = a.split(".").map(Number);
|
|
118
|
+
const pb = b.split(".").map(Number);
|
|
119
|
+
for (let i = 0; i < 3; i++) if (pa[i] !== pb[i]) return (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
120
|
+
return 0;
|
|
121
|
+
};
|
|
122
|
+
function runtime() {
|
|
123
|
+
const g = globalThis;
|
|
124
|
+
if (g.process?.versions?.bun) return `md/bun#${g.process.versions.bun}`;
|
|
125
|
+
if (g.Deno?.version?.deno) return `md/deno#${g.Deno.version.deno}`;
|
|
126
|
+
if (g.process?.versions?.node) return `md/nodejs#${g.process.versions.node}`;
|
|
127
|
+
return "md/browser";
|
|
128
|
+
}
|
|
129
|
+
function getCaitlynSdkPlugin(config, options) {
|
|
130
|
+
let server;
|
|
131
|
+
const since = new Map((options.since ?? []).map((e) => e.split("=")));
|
|
132
|
+
const sinceCheck = (next, context2) => async (args) => {
|
|
133
|
+
const op = context2.commandName?.replace(/Command$/, "");
|
|
134
|
+
if (op && server) {
|
|
135
|
+
const required = since.get(op);
|
|
136
|
+
if (required && semver(required, server) > 0) throw new NotSupportedByServer(op, required, server);
|
|
137
|
+
for (const [member, value] of Object.entries(args.input)) {
|
|
138
|
+
const memberSince = value === void 0 ? void 0 : since.get(`${op}.${member}`);
|
|
139
|
+
if (memberSince && semver(memberSince, server) > 0) throw new NotSupportedByServer(`${op}.${member}`, memberSince, server);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
return next(args);
|
|
143
|
+
};
|
|
144
|
+
const serverVersion = (next) => async (args) => {
|
|
145
|
+
try {
|
|
146
|
+
const out = await next(args);
|
|
147
|
+
if (HttpResponse.isInstance(out.response)) server = out.response.headers["caitlyn-api-version"] ?? server;
|
|
148
|
+
return out;
|
|
149
|
+
} catch (err) {
|
|
150
|
+
const res = err.$response;
|
|
151
|
+
if (HttpResponse.isInstance(res)) server = res.headers["caitlyn-api-version"] ?? server;
|
|
152
|
+
throw err;
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
const userAgent = [`${CAITLYN_SDK_USER_AGENT_PREFIX}/${config.caitlynSdkVersion ?? "0.0.0"}`, "lang/js", runtime(), config.customUserAgent].filter(Boolean).join(" ");
|
|
156
|
+
const identity = (next) => async (args) => {
|
|
157
|
+
if (HttpRequest.isInstance(args.request)) {
|
|
158
|
+
args.request.headers["user-agent"] = userAgent;
|
|
159
|
+
args.request.headers["caitlyn-sdk-model"] = options.sdkModel;
|
|
160
|
+
}
|
|
161
|
+
return next(args);
|
|
162
|
+
};
|
|
163
|
+
const safe = new Set(options.retrySafeOperations);
|
|
164
|
+
const retrySafety = (next, context2) => async (args) => {
|
|
165
|
+
try {
|
|
166
|
+
return await next(args);
|
|
167
|
+
} catch (err) {
|
|
168
|
+
const op = context2.commandName?.replace(/Command$/, "");
|
|
169
|
+
if (op && !safe.has(op) && err && typeof err === "object") Object.defineProperty(err, NO_RETRY, { value: true });
|
|
170
|
+
throw err;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
return {
|
|
174
|
+
applyToStack(stack) {
|
|
175
|
+
stack.add(sinceCheck, { step: "initialize", name: "caitlynSinceCheck", priority: "high", override: true });
|
|
176
|
+
stack.add(serverVersion, { step: "deserialize", name: "caitlynServerVersion", priority: "low", override: true });
|
|
177
|
+
stack.add(identity, { step: "build", name: "caitlynSdkIdentity", priority: "low", override: true });
|
|
178
|
+
stack.addRelativeTo(retrySafety, { toMiddleware: "retryMiddleware", relation: "after", name: "caitlynRetrySafety" });
|
|
179
|
+
getOmitRetryHeadersPlugin({}).applyToStack(stack);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// packages/sdk-core/src/protocol.ts
|
|
185
|
+
async function readBody(body, context2) {
|
|
186
|
+
try {
|
|
187
|
+
return await collectBody(body, context2);
|
|
188
|
+
} catch (err) {
|
|
189
|
+
throw markNoResponse(err);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
17
192
|
var SETTINGS = {
|
|
18
193
|
timestampFormat: {
|
|
19
194
|
useTrait: true,
|
|
@@ -65,7 +240,7 @@ var CaitlynRestJsonProtocol = class extends HttpBindingProtocol {
|
|
|
65
240
|
const ns = NormalizedSchema.of(operationSchema.output);
|
|
66
241
|
const streaming = [...ns.structIterator()].some(([, m]) => m.getMemberTraits().httpPayload && m.isStreaming());
|
|
67
242
|
if (response.statusCode >= 300) {
|
|
68
|
-
const bytes2 = await
|
|
243
|
+
const bytes2 = await readBody(response.body, context2);
|
|
69
244
|
response.body = bytes2;
|
|
70
245
|
if (!isJsonObject(bytes2)) {
|
|
71
246
|
await this.handleError(operationSchema, context2, response, {}, this.deserializeMetadata(response));
|
|
@@ -73,7 +248,7 @@ var CaitlynRestJsonProtocol = class extends HttpBindingProtocol {
|
|
|
73
248
|
return super.deserializeResponse(operationSchema, context2, response);
|
|
74
249
|
}
|
|
75
250
|
if (streaming) return super.deserializeResponse(operationSchema, context2, response);
|
|
76
|
-
const bytes = await
|
|
251
|
+
const bytes = await readBody(response.body, context2);
|
|
77
252
|
response.body = bytes;
|
|
78
253
|
const output = await super.deserializeResponse(operationSchema, context2, response);
|
|
79
254
|
if (bytes.byteLength > 0) {
|
|
@@ -94,7 +269,7 @@ var CaitlynRestJsonProtocol = class extends HttpBindingProtocol {
|
|
|
94
269
|
const registry = this.compositeErrorRegistry;
|
|
95
270
|
registry.copyFrom(TypeRegistry.for(this.options.defaultNamespace));
|
|
96
271
|
const errorMetadata = { $metadata: metadata, $fault: response.statusCode < 500 ? "client" : "server" };
|
|
97
|
-
const rawBody = response.body instanceof Uint8Array ?
|
|
272
|
+
const { rawBody, rawBodyTruncated } = rawBodyOf(response.body instanceof Uint8Array ? response.body : new Uint8Array());
|
|
98
273
|
const body = {
|
|
99
274
|
...dataObject,
|
|
100
275
|
// Always the HTTP status: a body's `status` can be wrong (a proxy, a faulty handler), and the class is
|
|
@@ -102,7 +277,8 @@ var CaitlynRestJsonProtocol = class extends HttpBindingProtocol {
|
|
|
102
277
|
status: response.statusCode,
|
|
103
278
|
code: typeof dataObject.code === "string" && dataObject.code ? dataObject.code : `HTTP_${response.statusCode}`,
|
|
104
279
|
requestId: dataObject.requestId ?? response.headers["caitlyn-request-id"],
|
|
105
|
-
rawBody
|
|
280
|
+
rawBody,
|
|
281
|
+
rawBodyTruncated
|
|
106
282
|
};
|
|
107
283
|
const message = body.detail ?? body.title ?? `HTTP ${response.statusCode}`;
|
|
108
284
|
const schema = registry.find((s) => {
|
|
@@ -113,7 +289,7 @@ var CaitlynRestJsonProtocol = class extends HttpBindingProtocol {
|
|
|
113
289
|
if (!schema) {
|
|
114
290
|
const base = registry.getBaseException();
|
|
115
291
|
const Ctor2 = base && registry.getErrorCtor(base) || Error;
|
|
116
|
-
const name =
|
|
292
|
+
const name = "CaitlynError";
|
|
117
293
|
throw decorateServiceException(Object.assign(new Ctor2({ name, message }), errorMetadata), body);
|
|
118
294
|
}
|
|
119
295
|
const ns = NormalizedSchema.of(schema);
|
|
@@ -130,6 +306,13 @@ var CaitlynRestJsonProtocol = class extends HttpBindingProtocol {
|
|
|
130
306
|
throw decorateServiceException(Object.assign(exception, errorMetadata, { $fault: ns.getMergedTraits().error, message }, fields), body);
|
|
131
307
|
}
|
|
132
308
|
};
|
|
309
|
+
var RAW_BODY_LIMIT = 8192;
|
|
310
|
+
function rawBodyOf(bytes) {
|
|
311
|
+
if (bytes.byteLength <= RAW_BODY_LIMIT) return { rawBody: new TextDecoder().decode(bytes), rawBodyTruncated: false };
|
|
312
|
+
let end = RAW_BODY_LIMIT;
|
|
313
|
+
while (end > 0 && (bytes[end] & 192) === 128) end--;
|
|
314
|
+
return { rawBody: new TextDecoder().decode(bytes.subarray(0, end)), rawBodyTruncated: true };
|
|
315
|
+
}
|
|
133
316
|
function isJsonObject(bytes) {
|
|
134
317
|
if (bytes.byteLength === 0) return false;
|
|
135
318
|
try {
|
|
@@ -170,140 +353,6 @@ function resolveContentType(input) {
|
|
|
170
353
|
return hasBody ? "application/json" : void 0;
|
|
171
354
|
}
|
|
172
355
|
|
|
173
|
-
// packages/sdk-core/src/plugin.ts
|
|
174
|
-
import { getOmitRetryHeadersPlugin } from "@smithy/core/retry";
|
|
175
|
-
import { HttpRequest } from "@smithy/core/protocols";
|
|
176
|
-
import { HttpResponse } from "@smithy/core/protocols";
|
|
177
|
-
|
|
178
|
-
// packages/sdk-core/src/retry.ts
|
|
179
|
-
var NO_RETRY = /* @__PURE__ */ Symbol.for("caitlyn.noRetry");
|
|
180
|
-
var Token = class {
|
|
181
|
-
constructor(count, delay) {
|
|
182
|
-
this.count = count;
|
|
183
|
-
this.delay = delay;
|
|
184
|
-
}
|
|
185
|
-
count;
|
|
186
|
-
delay;
|
|
187
|
-
getRetryCount() {
|
|
188
|
-
return this.count;
|
|
189
|
-
}
|
|
190
|
-
getRetryDelay() {
|
|
191
|
-
return this.delay;
|
|
192
|
-
}
|
|
193
|
-
};
|
|
194
|
-
var CaitlynRetryStrategy = class {
|
|
195
|
-
constructor(maxAttempts = 3, maxRetryDelayMs = 6e4) {
|
|
196
|
-
this.maxRetryDelayMs = maxRetryDelayMs;
|
|
197
|
-
this.maxAttempts = typeof maxAttempts === "number" ? async () => maxAttempts : maxAttempts;
|
|
198
|
-
}
|
|
199
|
-
maxRetryDelayMs;
|
|
200
|
-
mode = "standard";
|
|
201
|
-
maxAttempts;
|
|
202
|
-
async acquireInitialRetryToken() {
|
|
203
|
-
return new Token(0, 0);
|
|
204
|
-
}
|
|
205
|
-
async refreshRetryTokenForRetry(token, info) {
|
|
206
|
-
const count = token.getRetryCount();
|
|
207
|
-
const err = info.error;
|
|
208
|
-
if (count + 1 >= await this.maxAttempts()) throw new Error("retry: attempts exhausted");
|
|
209
|
-
if (!err || err[NO_RETRY]) throw new Error("retry: operation is not safe to retry");
|
|
210
|
-
const noResponse = err.$metadata?.httpStatusCode === void 0 && err.name !== "AbortError";
|
|
211
|
-
if (err.$retryable === void 0 && !noResponse) throw new Error("retry: error is not retryable");
|
|
212
|
-
let delay;
|
|
213
|
-
if (info.retryAfterHint instanceof Date) {
|
|
214
|
-
delay = Math.max(0, info.retryAfterHint.getTime() - Date.now());
|
|
215
|
-
if (delay > this.maxRetryDelayMs) throw new Error("retry: Retry-After exceeds maxRetryDelay");
|
|
216
|
-
} else {
|
|
217
|
-
delay = Math.random() * Math.min(8e3, 500 * 2 ** count);
|
|
218
|
-
}
|
|
219
|
-
return new Token(count + 1, delay);
|
|
220
|
-
}
|
|
221
|
-
recordSuccess() {
|
|
222
|
-
}
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
// packages/sdk-core/src/plugin.ts
|
|
226
|
-
var CAITLYN_SDK_USER_AGENT_PREFIX = "caitlyn-sdk-ts";
|
|
227
|
-
var NotSupportedByServer = class extends Error {
|
|
228
|
-
constructor(operation, requires, server) {
|
|
229
|
-
super(`NotSupportedByServer(${operation}, requires ${requires}, server is ${server})`);
|
|
230
|
-
this.operation = operation;
|
|
231
|
-
this.requires = requires;
|
|
232
|
-
this.server = server;
|
|
233
|
-
this.name = "NotSupportedByServer";
|
|
234
|
-
}
|
|
235
|
-
operation;
|
|
236
|
-
requires;
|
|
237
|
-
server;
|
|
238
|
-
};
|
|
239
|
-
var semver = (a, b) => {
|
|
240
|
-
const pa = a.split(".").map(Number);
|
|
241
|
-
const pb = b.split(".").map(Number);
|
|
242
|
-
for (let i = 0; i < 3; i++) if (pa[i] !== pb[i]) return (pa[i] ?? 0) - (pb[i] ?? 0);
|
|
243
|
-
return 0;
|
|
244
|
-
};
|
|
245
|
-
function runtime() {
|
|
246
|
-
const g = globalThis;
|
|
247
|
-
if (g.process?.versions?.bun) return `md/bun#${g.process.versions.bun}`;
|
|
248
|
-
if (g.Deno?.version?.deno) return `md/deno#${g.Deno.version.deno}`;
|
|
249
|
-
if (g.process?.versions?.node) return `md/nodejs#${g.process.versions.node}`;
|
|
250
|
-
return "md/browser";
|
|
251
|
-
}
|
|
252
|
-
function getCaitlynSdkPlugin(config, options) {
|
|
253
|
-
let server;
|
|
254
|
-
const since = new Map((options.since ?? []).map((e) => e.split("=")));
|
|
255
|
-
const sinceCheck = (next, context2) => async (args) => {
|
|
256
|
-
const op = context2.commandName?.replace(/Command$/, "");
|
|
257
|
-
if (op && server) {
|
|
258
|
-
const required = since.get(op);
|
|
259
|
-
if (required && semver(required, server) > 0) throw new NotSupportedByServer(op, required, server);
|
|
260
|
-
for (const [member, value] of Object.entries(args.input)) {
|
|
261
|
-
const memberSince = value === void 0 ? void 0 : since.get(`${op}.${member}`);
|
|
262
|
-
if (memberSince && semver(memberSince, server) > 0) throw new NotSupportedByServer(`${op}.${member}`, memberSince, server);
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
return next(args);
|
|
266
|
-
};
|
|
267
|
-
const serverVersion = (next) => async (args) => {
|
|
268
|
-
try {
|
|
269
|
-
const out = await next(args);
|
|
270
|
-
if (HttpResponse.isInstance(out.response)) server = out.response.headers["caitlyn-api-version"] ?? server;
|
|
271
|
-
return out;
|
|
272
|
-
} catch (err) {
|
|
273
|
-
const res = err.$response;
|
|
274
|
-
if (HttpResponse.isInstance(res)) server = res.headers["caitlyn-api-version"] ?? server;
|
|
275
|
-
throw err;
|
|
276
|
-
}
|
|
277
|
-
};
|
|
278
|
-
const userAgent = [`${CAITLYN_SDK_USER_AGENT_PREFIX}/${config.caitlynSdkVersion ?? "0.0.0"}`, "lang/js", runtime(), config.customUserAgent].filter(Boolean).join(" ");
|
|
279
|
-
const identity = (next) => async (args) => {
|
|
280
|
-
if (HttpRequest.isInstance(args.request)) {
|
|
281
|
-
args.request.headers["user-agent"] = userAgent;
|
|
282
|
-
args.request.headers["caitlyn-sdk-model"] = options.sdkModel;
|
|
283
|
-
}
|
|
284
|
-
return next(args);
|
|
285
|
-
};
|
|
286
|
-
const safe = new Set(options.retrySafeOperations);
|
|
287
|
-
const retrySafety = (next, context2) => async (args) => {
|
|
288
|
-
try {
|
|
289
|
-
return await next(args);
|
|
290
|
-
} catch (err) {
|
|
291
|
-
const op = context2.commandName?.replace(/Command$/, "");
|
|
292
|
-
if (op && !safe.has(op) && err && typeof err === "object") Object.defineProperty(err, NO_RETRY, { value: true });
|
|
293
|
-
throw err;
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
return {
|
|
297
|
-
applyToStack(stack) {
|
|
298
|
-
stack.add(sinceCheck, { step: "initialize", name: "caitlynSinceCheck", priority: "high", override: true });
|
|
299
|
-
stack.add(serverVersion, { step: "deserialize", name: "caitlynServerVersion", priority: "low", override: true });
|
|
300
|
-
stack.add(identity, { step: "build", name: "caitlynSdkIdentity", priority: "low", override: true });
|
|
301
|
-
stack.addRelativeTo(retrySafety, { toMiddleware: "retryMiddleware", relation: "after", name: "caitlynRetrySafety" });
|
|
302
|
-
getOmitRetryHeadersPlugin({}).applyToStack(stack);
|
|
303
|
-
}
|
|
304
|
-
};
|
|
305
|
-
}
|
|
306
|
-
|
|
307
356
|
// packages/sdk-core/src/endpoint.ts
|
|
308
357
|
var caitlynEndpointProvider = (params) => {
|
|
309
358
|
if (!params.endpoint) throw new Error("Caitlyn: no endpoint. Pass `workspace` or `endpoint` when creating the client.");
|
|
@@ -313,23 +362,42 @@ var caitlynEndpointProvider = (params) => {
|
|
|
313
362
|
// packages/sdk-core/src/telemetry.ts
|
|
314
363
|
import { HttpRequest as HttpRequest2, HttpResponse as HttpResponse2 } from "@smithy/core/protocols";
|
|
315
364
|
import { context, propagation, trace, SpanKind, SpanStatusCode } from "@opentelemetry/api";
|
|
365
|
+
function caitlynTracer(provider) {
|
|
366
|
+
return (provider ?? trace.getTracerProvider()).getTracer("@caitlyn-ai/sdk", "0.1.1");
|
|
367
|
+
}
|
|
368
|
+
function urlWithoutQuery(url) {
|
|
369
|
+
const u = new URL(url);
|
|
370
|
+
u.search = "";
|
|
371
|
+
u.hash = "";
|
|
372
|
+
u.username = "";
|
|
373
|
+
u.password = "";
|
|
374
|
+
return u.toString();
|
|
375
|
+
}
|
|
376
|
+
function recordFailure(span, err) {
|
|
377
|
+
const e = err;
|
|
378
|
+
const noResponse = !!e?.[NO_RESPONSE];
|
|
379
|
+
const name = noResponse ? "CaitlynConnectionError" : e?.name ?? "Error";
|
|
380
|
+
const message = String(e?.message ?? err);
|
|
381
|
+
span.recordException({ name, message, stack: err instanceof Error ? err.stack : void 0 });
|
|
382
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message });
|
|
383
|
+
span.setAttribute("error.type", name);
|
|
384
|
+
const code = noResponse ? "CONNECTION_ERROR" : e?.code;
|
|
385
|
+
if (typeof code === "string") span.setAttribute("caitlyn.error.code", code);
|
|
386
|
+
}
|
|
316
387
|
function getCaitlynTelemetryPlugin(provider) {
|
|
317
|
-
const tracer = (provider
|
|
388
|
+
const tracer = caitlynTracer(provider);
|
|
318
389
|
return {
|
|
319
390
|
applyToStack(stack) {
|
|
320
391
|
stack.add(
|
|
321
392
|
(next, ctx) => async (args) => {
|
|
322
393
|
const op = ctx.commandName?.replace(/Command$/, "") ?? "unknown";
|
|
323
|
-
return tracer.startActiveSpan(
|
|
394
|
+
return tracer.startActiveSpan("caitlyn.call", { kind: SpanKind.CLIENT, attributes: { "rpc.system": "caitlyn", "rpc.method": op } }, async (span) => {
|
|
324
395
|
ctx.__caitlynSpan = span;
|
|
325
396
|
ctx.__attempt = 0;
|
|
326
397
|
try {
|
|
327
|
-
|
|
328
|
-
return out;
|
|
398
|
+
return await next(args);
|
|
329
399
|
} catch (err) {
|
|
330
|
-
span
|
|
331
|
-
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
332
|
-
span.setAttribute("error.type", err.name);
|
|
400
|
+
recordFailure(span, err);
|
|
333
401
|
throw err;
|
|
334
402
|
} finally {
|
|
335
403
|
span.end();
|
|
@@ -343,27 +411,34 @@ function getCaitlynTelemetryPlugin(provider) {
|
|
|
343
411
|
const attempt = ctx.__attempt ?? 0;
|
|
344
412
|
ctx.__attempt = attempt + 1;
|
|
345
413
|
const parent = ctx.__caitlynSpan ? trace.setSpan(context.active(), ctx.__caitlynSpan) : context.active();
|
|
346
|
-
const span = tracer.startSpan(
|
|
414
|
+
const span = tracer.startSpan("caitlyn.attempt", { kind: SpanKind.CLIENT, attributes: { "http.request.resend_count": attempt } }, parent);
|
|
347
415
|
const req = args.request;
|
|
348
416
|
if (HttpRequest2.isInstance(req)) {
|
|
349
|
-
span.
|
|
417
|
+
span.setAttributes({
|
|
418
|
+
"http.request.method": req.method,
|
|
419
|
+
"server.address": req.hostname,
|
|
420
|
+
"url.full": urlWithoutQuery(`${req.protocol}//${req.hostname}${req.port ? `:${req.port}` : ""}${req.path}`)
|
|
421
|
+
});
|
|
350
422
|
propagation.inject(trace.setSpan(parent, span), req.headers);
|
|
351
423
|
}
|
|
424
|
+
const response = (res) => {
|
|
425
|
+
if (!HttpResponse2.isInstance(res)) return;
|
|
426
|
+
span.setAttribute("http.response.status_code", res.statusCode);
|
|
427
|
+
const rid = res.headers["caitlyn-request-id"];
|
|
428
|
+
if (rid) {
|
|
429
|
+
span.setAttribute("caitlyn.request_id", rid);
|
|
430
|
+
ctx.__caitlynSpan?.setAttribute("caitlyn.request_id", rid);
|
|
431
|
+
}
|
|
432
|
+
const api = res.headers["caitlyn-api-version"];
|
|
433
|
+
if (api) span.setAttribute("caitlyn.server.api_version", api);
|
|
434
|
+
};
|
|
352
435
|
try {
|
|
353
436
|
const out = await next(args);
|
|
354
|
-
|
|
355
|
-
span.setAttribute("http.response.status_code", out.response.statusCode);
|
|
356
|
-
const rid = out.response.headers["caitlyn-request-id"];
|
|
357
|
-
if (rid) {
|
|
358
|
-
span.setAttribute("caitlyn.request_id", rid);
|
|
359
|
-
ctx.__caitlynSpan?.setAttribute("caitlyn.request_id", rid);
|
|
360
|
-
}
|
|
361
|
-
}
|
|
437
|
+
response(out.response);
|
|
362
438
|
return out;
|
|
363
439
|
} catch (err) {
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
440
|
+
response(err.$response);
|
|
441
|
+
recordFailure(span, err);
|
|
367
442
|
throw err;
|
|
368
443
|
} finally {
|
|
369
444
|
span.end();
|
|
@@ -425,17 +500,23 @@ var SseStream = class {
|
|
|
425
500
|
seen = /* @__PURE__ */ new Set();
|
|
426
501
|
lastEventId;
|
|
427
502
|
reconnects = 0;
|
|
503
|
+
/** The Caitlyn-Request-Id of the response that opened the stream; set once iteration has started. */
|
|
504
|
+
requestId;
|
|
428
505
|
aborted() {
|
|
429
506
|
return this.signal?.reason ?? new DOMException("Aborted", "AbortError");
|
|
430
507
|
}
|
|
431
508
|
async *[Symbol.asyncIterator]() {
|
|
432
509
|
if (this.consumed) throw new Error("SseStream can only be iterated once");
|
|
433
510
|
this.consumed = true;
|
|
434
|
-
|
|
511
|
+
const opened = await this.open();
|
|
512
|
+
this.requestId = opened.$metadata?.requestId;
|
|
513
|
+
let body = opened.events;
|
|
435
514
|
for (; ; ) {
|
|
436
515
|
let ended = false;
|
|
516
|
+
let onResponse = 0;
|
|
437
517
|
try {
|
|
438
518
|
for await (const msg of parseSse(body)) {
|
|
519
|
+
onResponse++;
|
|
439
520
|
if (msg.id) {
|
|
440
521
|
if (this.seen.has(msg.id)) continue;
|
|
441
522
|
this.seen.add(msg.id);
|
|
@@ -452,7 +533,7 @@ var SseStream = class {
|
|
|
452
533
|
return;
|
|
453
534
|
}
|
|
454
535
|
}
|
|
455
|
-
if (!this.spec.final) return;
|
|
536
|
+
if (!this.spec.final || onResponse === 0) return;
|
|
456
537
|
} catch (err) {
|
|
457
538
|
if (this.signal?.aborted) throw this.aborted();
|
|
458
539
|
if (ended || err?.name === "AbortError" || err === void 0 || err.$fault || err instanceof SyntaxError) throw err;
|
|
@@ -460,7 +541,7 @@ var SseStream = class {
|
|
|
460
541
|
if (this.signal?.aborted) throw this.aborted();
|
|
461
542
|
if (!this.resume || ++this.reconnects > this.maxReconnects)
|
|
462
543
|
throw this.spec.toError({ status: 0, code: "STREAM_LOST", title: "The stream dropped and could not be resumed" });
|
|
463
|
-
body = await this.resume(this.lastEventId);
|
|
544
|
+
body = (await this.resume(this.lastEventId)).events;
|
|
464
545
|
}
|
|
465
546
|
}
|
|
466
547
|
/** Drains the stream if needed and returns the final event's member. */
|
|
@@ -472,15 +553,40 @@ var SseStream = class {
|
|
|
472
553
|
};
|
|
473
554
|
|
|
474
555
|
// packages/sdk-core/src/upload.ts
|
|
475
|
-
|
|
556
|
+
import { SpanKind as SpanKind2, SpanStatusCode as SpanStatusCode2 } from "@opentelemetry/api";
|
|
557
|
+
var STATUS = /* @__PURE__ */ Symbol("status");
|
|
558
|
+
async function uploadFile(file, fileName, reserve, toError, signal, tracer) {
|
|
476
559
|
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
477
560
|
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
|
|
478
561
|
let binary = "";
|
|
479
562
|
for (const b of digest) binary += String.fromCharCode(b);
|
|
480
563
|
const slot = await reserve({ fileName, contentType: file.type || "application/octet-stream", byteSize: bytes.byteLength, sha256: btoa(binary) });
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
|
|
564
|
+
const put = async () => {
|
|
565
|
+
const res = await fetch(slot.uploadUrl, { method: slot.uploadMethod, headers: slot.requiredHeaders, body: bytes, signal });
|
|
566
|
+
if (!res.ok) throw Object.assign(toError({ status: res.status, code: "UPLOAD_PUT_FAILED", title: "Uploading the bytes failed", detail: await res.text() }), { [STATUS]: res.status });
|
|
567
|
+
return res.status;
|
|
568
|
+
};
|
|
569
|
+
if (!tracer) {
|
|
570
|
+
await put();
|
|
571
|
+
return slot;
|
|
572
|
+
}
|
|
573
|
+
const url = new URL(slot.uploadUrl);
|
|
574
|
+
const span = tracer.startSpan("caitlyn.upload", {
|
|
575
|
+
kind: SpanKind2.CLIENT,
|
|
576
|
+
attributes: { "http.request.method": slot.uploadMethod || "PUT", "server.address": url.hostname, "url.full": urlWithoutQuery(slot.uploadUrl) }
|
|
577
|
+
});
|
|
578
|
+
try {
|
|
579
|
+
span.setAttribute("http.response.status_code", await put());
|
|
580
|
+
return slot;
|
|
581
|
+
} catch (err) {
|
|
582
|
+
const status = err[STATUS];
|
|
583
|
+
if (status !== void 0) span.setAttribute("http.response.status_code", status);
|
|
584
|
+
recordFailure(span, err);
|
|
585
|
+
span.setStatus({ code: SpanStatusCode2.ERROR });
|
|
586
|
+
throw err;
|
|
587
|
+
} finally {
|
|
588
|
+
span.end();
|
|
589
|
+
}
|
|
484
590
|
}
|
|
485
591
|
|
|
486
592
|
// packages/sdk-core/src/auth.ts
|
|
@@ -504,8 +610,30 @@ var SESSION_PLACEHOLDER = "session";
|
|
|
504
610
|
var refreshAt = (t) => t.expiresAt === void 0 ? void 0 : t.expiresAt - Math.min(REFRESH_EARLY_MS, (t.expiresAt - Date.now()) / 2);
|
|
505
611
|
var b64url = (bytes) => btoa(String.fromCharCode(...new Uint8Array(bytes))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
506
612
|
var randomString = (n = 32) => b64url(crypto.getRandomValues(new Uint8Array(n)));
|
|
507
|
-
var workspaceAudience = (workspace) => `https://${workspaceHost(workspace)}/api
|
|
613
|
+
var workspaceAudience = (workspace) => `https://${workspaceHost(workspace)}/api`;
|
|
508
614
|
var workspaceHost = (workspace) => workspace.replace(/^https?:\/\//, "").replace(/\/.*$/, "");
|
|
615
|
+
var isBrowser = () => {
|
|
616
|
+
const g = globalThis;
|
|
617
|
+
return typeof g.window !== "undefined" && typeof g.document !== "undefined";
|
|
618
|
+
};
|
|
619
|
+
var IN_BROWSER = "not_allowed_in_browser";
|
|
620
|
+
function guardBrowser(o) {
|
|
621
|
+
if (!isBrowser()) return;
|
|
622
|
+
if (o.mode === "clientCredentials" || o.mode === "oauth" && o.clientSecret) {
|
|
623
|
+
throw new CaitlynAuthError(
|
|
624
|
+
`${o.mode === "oauth" ? "oauth with a clientSecret" : "clientCredentials"} cannot be used in a browser: a client secret in a browser is visible to everyone who loads the page. Use oauth without a clientSecret (authorisation code with PKCE), or sign in on your server.`,
|
|
625
|
+
IN_BROWSER
|
|
626
|
+
);
|
|
627
|
+
}
|
|
628
|
+
if (o.mode === "awsIam" || o.mode === "workloadIdentity") {
|
|
629
|
+
throw new CaitlynAuthError(`${o.mode} cannot be used in a browser: it signs in a server workload with that workload's own identity. Use oauth in a browser.`, IN_BROWSER);
|
|
630
|
+
}
|
|
631
|
+
if (o.mode === "token" && !o.allowBrowserToken) {
|
|
632
|
+
console.warn(
|
|
633
|
+
"Caitlyn: a long-lived token in a browser is exposed to anyone who can run script on the page. token mode in a browser is for local testing; use oauth instead. Set auth.allowBrowserToken: true to silence this warning."
|
|
634
|
+
);
|
|
635
|
+
}
|
|
636
|
+
}
|
|
509
637
|
var AuthSession = class {
|
|
510
638
|
constructor(options, endpoint, workspace, fetchFn = (...a) => fetch(...a)) {
|
|
511
639
|
this.options = options;
|
|
@@ -516,6 +644,7 @@ var AuthSession = class {
|
|
|
516
644
|
this.authorizationEndpoint = `${endpoint}/oauth/authorize`;
|
|
517
645
|
this.workspaceUrl = `https://${workspace}`;
|
|
518
646
|
this.resource = "resource" in options && options.resource || workspaceAudience(workspace);
|
|
647
|
+
guardBrowser(options);
|
|
519
648
|
}
|
|
520
649
|
options;
|
|
521
650
|
endpoint;
|
|
@@ -528,7 +657,7 @@ var AuthSession = class {
|
|
|
528
657
|
memoryTokens;
|
|
529
658
|
tokenEndpoint;
|
|
530
659
|
authorizationEndpoint;
|
|
531
|
-
/** The workspace's resource URL, https://<workspace>/api
|
|
660
|
+
/** The workspace's resource URL, https://<workspace>/api: the audience every token is bound to. */
|
|
532
661
|
resource;
|
|
533
662
|
/** https://<workspace>: the default Origin for session writes. */
|
|
534
663
|
workspaceUrl;
|
|
@@ -752,6 +881,9 @@ function clientConfig(opts, auth = createAuth(opts)) {
|
|
|
752
881
|
workspace: workspaceHost(opts.workspace),
|
|
753
882
|
endpoint: opts.endpoint ?? workspaceEndpoint(opts.workspace),
|
|
754
883
|
token: tokenIdentityProvider(auth),
|
|
884
|
+
/** The tracer upload helpers record the byte transfer with (PROTOCOL.md 13); absent with telemetry off. */
|
|
885
|
+
caitlynTracer: opts.telemetry?.enabled === false ? void 0 : caitlynTracer(opts.telemetry?.tracerProvider),
|
|
886
|
+
...opts.requestHandler !== void 0 ? { requestHandler: opts.requestHandler } : {},
|
|
755
887
|
...opts.maxAttempts !== void 0 ? { maxAttempts: opts.maxAttempts } : {},
|
|
756
888
|
...opts.maxRetryDelayMs !== void 0 ? { retryStrategy: new CaitlynRetryStrategy(opts.maxAttempts ?? 3, opts.maxRetryDelayMs) } : {}
|
|
757
889
|
};
|
|
@@ -821,10 +953,12 @@ export {
|
|
|
821
953
|
SseStream,
|
|
822
954
|
WaiterError,
|
|
823
955
|
caitlynEndpointProvider,
|
|
956
|
+
caitlynTracer,
|
|
824
957
|
clientConfig,
|
|
825
958
|
createAuth,
|
|
826
959
|
defaultAwsCredentials,
|
|
827
960
|
getCaitlynAuthPlugin,
|
|
961
|
+
getCaitlynConnectionErrorPlugin,
|
|
828
962
|
getCaitlynSdkPlugin,
|
|
829
963
|
getCaitlynTelemetryPlugin,
|
|
830
964
|
getWebIdentityToken,
|