@caitlyn-ai/sdk-core 0.0.2 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +39 -7
- package/dist-cjs/index.cjs +305 -172
- package/dist-cjs/index.cjs.map +3 -3
- package/dist-es/index.mjs +298 -167
- 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,38 @@ 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.0");
|
|
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
|
+
span.recordException(err instanceof Error ? err : { name: String(e?.name ?? "Error"), message: String(e?.message ?? err) });
|
|
379
|
+
span.setStatus({ code: SpanStatusCode.ERROR, message: e?.message });
|
|
380
|
+
span.setAttribute("error.type", e?.name ?? "Error");
|
|
381
|
+
if (typeof e?.code === "string") span.setAttribute("caitlyn.error.code", e.code);
|
|
382
|
+
}
|
|
316
383
|
function getCaitlynTelemetryPlugin(provider) {
|
|
317
|
-
const tracer = (provider
|
|
384
|
+
const tracer = caitlynTracer(provider);
|
|
318
385
|
return {
|
|
319
386
|
applyToStack(stack) {
|
|
320
387
|
stack.add(
|
|
321
388
|
(next, ctx) => async (args) => {
|
|
322
389
|
const op = ctx.commandName?.replace(/Command$/, "") ?? "unknown";
|
|
323
|
-
return tracer.startActiveSpan(
|
|
390
|
+
return tracer.startActiveSpan("caitlyn.call", { kind: SpanKind.CLIENT, attributes: { "rpc.system": "caitlyn", "rpc.method": op } }, async (span) => {
|
|
324
391
|
ctx.__caitlynSpan = span;
|
|
325
392
|
ctx.__attempt = 0;
|
|
326
393
|
try {
|
|
327
|
-
|
|
328
|
-
return out;
|
|
394
|
+
return await next(args);
|
|
329
395
|
} catch (err) {
|
|
330
|
-
span
|
|
331
|
-
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
332
|
-
span.setAttribute("error.type", err.name);
|
|
396
|
+
recordFailure(span, err);
|
|
333
397
|
throw err;
|
|
334
398
|
} finally {
|
|
335
399
|
span.end();
|
|
@@ -343,26 +407,34 @@ function getCaitlynTelemetryPlugin(provider) {
|
|
|
343
407
|
const attempt = ctx.__attempt ?? 0;
|
|
344
408
|
ctx.__attempt = attempt + 1;
|
|
345
409
|
const parent = ctx.__caitlynSpan ? trace.setSpan(context.active(), ctx.__caitlynSpan) : context.active();
|
|
346
|
-
const span = tracer.startSpan(
|
|
410
|
+
const span = tracer.startSpan("caitlyn.attempt", { kind: SpanKind.CLIENT, attributes: { "http.request.resend_count": attempt } }, parent);
|
|
347
411
|
const req = args.request;
|
|
348
412
|
if (HttpRequest2.isInstance(req)) {
|
|
349
|
-
span.
|
|
413
|
+
span.setAttributes({
|
|
414
|
+
"http.request.method": req.method,
|
|
415
|
+
"server.address": req.hostname,
|
|
416
|
+
"url.full": urlWithoutQuery(`${req.protocol}//${req.hostname}${req.port ? `:${req.port}` : ""}${req.path}`)
|
|
417
|
+
});
|
|
350
418
|
propagation.inject(trace.setSpan(parent, span), req.headers);
|
|
351
419
|
}
|
|
420
|
+
const response = (res) => {
|
|
421
|
+
if (!HttpResponse2.isInstance(res)) return;
|
|
422
|
+
span.setAttribute("http.response.status_code", res.statusCode);
|
|
423
|
+
const rid = res.headers["caitlyn-request-id"];
|
|
424
|
+
if (rid) {
|
|
425
|
+
span.setAttribute("caitlyn.request_id", rid);
|
|
426
|
+
ctx.__caitlynSpan?.setAttribute("caitlyn.request_id", rid);
|
|
427
|
+
}
|
|
428
|
+
const api = res.headers["caitlyn-api-version"];
|
|
429
|
+
if (api) span.setAttribute("caitlyn.server.api_version", api);
|
|
430
|
+
if (res.statusCode >= 400) span.setStatus({ code: SpanStatusCode.ERROR });
|
|
431
|
+
};
|
|
352
432
|
try {
|
|
353
433
|
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
|
-
}
|
|
434
|
+
response(out.response);
|
|
362
435
|
return out;
|
|
363
436
|
} catch (err) {
|
|
364
|
-
|
|
365
|
-
if (HttpResponse2.isInstance(res)) span.setAttribute("http.response.status_code", res.statusCode);
|
|
437
|
+
response(err.$response);
|
|
366
438
|
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
367
439
|
throw err;
|
|
368
440
|
} finally {
|
|
@@ -425,17 +497,23 @@ var SseStream = class {
|
|
|
425
497
|
seen = /* @__PURE__ */ new Set();
|
|
426
498
|
lastEventId;
|
|
427
499
|
reconnects = 0;
|
|
500
|
+
/** The Caitlyn-Request-Id of the response that opened the stream; set once iteration has started. */
|
|
501
|
+
requestId;
|
|
428
502
|
aborted() {
|
|
429
503
|
return this.signal?.reason ?? new DOMException("Aborted", "AbortError");
|
|
430
504
|
}
|
|
431
505
|
async *[Symbol.asyncIterator]() {
|
|
432
506
|
if (this.consumed) throw new Error("SseStream can only be iterated once");
|
|
433
507
|
this.consumed = true;
|
|
434
|
-
|
|
508
|
+
const opened = await this.open();
|
|
509
|
+
this.requestId = opened.$metadata?.requestId;
|
|
510
|
+
let body = opened.events;
|
|
435
511
|
for (; ; ) {
|
|
436
512
|
let ended = false;
|
|
513
|
+
let onResponse = 0;
|
|
437
514
|
try {
|
|
438
515
|
for await (const msg of parseSse(body)) {
|
|
516
|
+
onResponse++;
|
|
439
517
|
if (msg.id) {
|
|
440
518
|
if (this.seen.has(msg.id)) continue;
|
|
441
519
|
this.seen.add(msg.id);
|
|
@@ -452,7 +530,7 @@ var SseStream = class {
|
|
|
452
530
|
return;
|
|
453
531
|
}
|
|
454
532
|
}
|
|
455
|
-
if (!this.spec.final) return;
|
|
533
|
+
if (!this.spec.final || onResponse === 0) return;
|
|
456
534
|
} catch (err) {
|
|
457
535
|
if (this.signal?.aborted) throw this.aborted();
|
|
458
536
|
if (ended || err?.name === "AbortError" || err === void 0 || err.$fault || err instanceof SyntaxError) throw err;
|
|
@@ -460,7 +538,7 @@ var SseStream = class {
|
|
|
460
538
|
if (this.signal?.aborted) throw this.aborted();
|
|
461
539
|
if (!this.resume || ++this.reconnects > this.maxReconnects)
|
|
462
540
|
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);
|
|
541
|
+
body = (await this.resume(this.lastEventId)).events;
|
|
464
542
|
}
|
|
465
543
|
}
|
|
466
544
|
/** Drains the stream if needed and returns the final event's member. */
|
|
@@ -472,15 +550,40 @@ var SseStream = class {
|
|
|
472
550
|
};
|
|
473
551
|
|
|
474
552
|
// packages/sdk-core/src/upload.ts
|
|
475
|
-
|
|
553
|
+
import { SpanKind as SpanKind2, SpanStatusCode as SpanStatusCode2 } from "@opentelemetry/api";
|
|
554
|
+
var STATUS = /* @__PURE__ */ Symbol("status");
|
|
555
|
+
async function uploadFile(file, fileName, reserve, toError, signal, tracer) {
|
|
476
556
|
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
477
557
|
const digest = new Uint8Array(await crypto.subtle.digest("SHA-256", bytes));
|
|
478
558
|
let binary = "";
|
|
479
559
|
for (const b of digest) binary += String.fromCharCode(b);
|
|
480
560
|
const slot = await reserve({ fileName, contentType: file.type || "application/octet-stream", byteSize: bytes.byteLength, sha256: btoa(binary) });
|
|
481
|
-
const
|
|
482
|
-
|
|
483
|
-
|
|
561
|
+
const put = async () => {
|
|
562
|
+
const res = await fetch(slot.uploadUrl, { method: slot.uploadMethod, headers: slot.requiredHeaders, body: bytes, signal });
|
|
563
|
+
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 });
|
|
564
|
+
return res.status;
|
|
565
|
+
};
|
|
566
|
+
if (!tracer) {
|
|
567
|
+
await put();
|
|
568
|
+
return slot;
|
|
569
|
+
}
|
|
570
|
+
const url = new URL(slot.uploadUrl);
|
|
571
|
+
const span = tracer.startSpan("caitlyn.upload", {
|
|
572
|
+
kind: SpanKind2.CLIENT,
|
|
573
|
+
attributes: { "http.request.method": slot.uploadMethod || "PUT", "server.address": url.hostname, "url.full": urlWithoutQuery(slot.uploadUrl) }
|
|
574
|
+
});
|
|
575
|
+
try {
|
|
576
|
+
span.setAttribute("http.response.status_code", await put());
|
|
577
|
+
return slot;
|
|
578
|
+
} catch (err) {
|
|
579
|
+
const status = err[STATUS];
|
|
580
|
+
if (status !== void 0) span.setAttribute("http.response.status_code", status);
|
|
581
|
+
recordFailure(span, err);
|
|
582
|
+
span.setStatus({ code: SpanStatusCode2.ERROR });
|
|
583
|
+
throw err;
|
|
584
|
+
} finally {
|
|
585
|
+
span.end();
|
|
586
|
+
}
|
|
484
587
|
}
|
|
485
588
|
|
|
486
589
|
// packages/sdk-core/src/auth.ts
|
|
@@ -504,8 +607,30 @@ var SESSION_PLACEHOLDER = "session";
|
|
|
504
607
|
var refreshAt = (t) => t.expiresAt === void 0 ? void 0 : t.expiresAt - Math.min(REFRESH_EARLY_MS, (t.expiresAt - Date.now()) / 2);
|
|
505
608
|
var b64url = (bytes) => btoa(String.fromCharCode(...new Uint8Array(bytes))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
506
609
|
var randomString = (n = 32) => b64url(crypto.getRandomValues(new Uint8Array(n)));
|
|
507
|
-
var workspaceAudience = (workspace) => `https://${workspaceHost(workspace)}/api
|
|
610
|
+
var workspaceAudience = (workspace) => `https://${workspaceHost(workspace)}/api`;
|
|
508
611
|
var workspaceHost = (workspace) => workspace.replace(/^https?:\/\//, "").replace(/\/.*$/, "");
|
|
612
|
+
var isBrowser = () => {
|
|
613
|
+
const g = globalThis;
|
|
614
|
+
return typeof g.window !== "undefined" && typeof g.document !== "undefined";
|
|
615
|
+
};
|
|
616
|
+
var IN_BROWSER = "not_allowed_in_browser";
|
|
617
|
+
function guardBrowser(o) {
|
|
618
|
+
if (!isBrowser()) return;
|
|
619
|
+
if (o.mode === "clientCredentials" || o.mode === "oauth" && o.clientSecret) {
|
|
620
|
+
throw new CaitlynAuthError(
|
|
621
|
+
`${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.`,
|
|
622
|
+
IN_BROWSER
|
|
623
|
+
);
|
|
624
|
+
}
|
|
625
|
+
if (o.mode === "awsIam" || o.mode === "workloadIdentity") {
|
|
626
|
+
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);
|
|
627
|
+
}
|
|
628
|
+
if (o.mode === "token" && !o.allowBrowserToken) {
|
|
629
|
+
console.warn(
|
|
630
|
+
"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."
|
|
631
|
+
);
|
|
632
|
+
}
|
|
633
|
+
}
|
|
509
634
|
var AuthSession = class {
|
|
510
635
|
constructor(options, endpoint, workspace, fetchFn = (...a) => fetch(...a)) {
|
|
511
636
|
this.options = options;
|
|
@@ -516,6 +641,7 @@ var AuthSession = class {
|
|
|
516
641
|
this.authorizationEndpoint = `${endpoint}/oauth/authorize`;
|
|
517
642
|
this.workspaceUrl = `https://${workspace}`;
|
|
518
643
|
this.resource = "resource" in options && options.resource || workspaceAudience(workspace);
|
|
644
|
+
guardBrowser(options);
|
|
519
645
|
}
|
|
520
646
|
options;
|
|
521
647
|
endpoint;
|
|
@@ -528,7 +654,7 @@ var AuthSession = class {
|
|
|
528
654
|
memoryTokens;
|
|
529
655
|
tokenEndpoint;
|
|
530
656
|
authorizationEndpoint;
|
|
531
|
-
/** The workspace's resource URL, https://<workspace>/api
|
|
657
|
+
/** The workspace's resource URL, https://<workspace>/api: the audience every token is bound to. */
|
|
532
658
|
resource;
|
|
533
659
|
/** https://<workspace>: the default Origin for session writes. */
|
|
534
660
|
workspaceUrl;
|
|
@@ -752,6 +878,9 @@ function clientConfig(opts, auth = createAuth(opts)) {
|
|
|
752
878
|
workspace: workspaceHost(opts.workspace),
|
|
753
879
|
endpoint: opts.endpoint ?? workspaceEndpoint(opts.workspace),
|
|
754
880
|
token: tokenIdentityProvider(auth),
|
|
881
|
+
/** The tracer upload helpers record the byte transfer with (PROTOCOL.md 13); absent with telemetry off. */
|
|
882
|
+
caitlynTracer: opts.telemetry?.enabled === false ? void 0 : caitlynTracer(opts.telemetry?.tracerProvider),
|
|
883
|
+
...opts.requestHandler !== void 0 ? { requestHandler: opts.requestHandler } : {},
|
|
755
884
|
...opts.maxAttempts !== void 0 ? { maxAttempts: opts.maxAttempts } : {},
|
|
756
885
|
...opts.maxRetryDelayMs !== void 0 ? { retryStrategy: new CaitlynRetryStrategy(opts.maxAttempts ?? 3, opts.maxRetryDelayMs) } : {}
|
|
757
886
|
};
|
|
@@ -821,10 +950,12 @@ export {
|
|
|
821
950
|
SseStream,
|
|
822
951
|
WaiterError,
|
|
823
952
|
caitlynEndpointProvider,
|
|
953
|
+
caitlynTracer,
|
|
824
954
|
clientConfig,
|
|
825
955
|
createAuth,
|
|
826
956
|
defaultAwsCredentials,
|
|
827
957
|
getCaitlynAuthPlugin,
|
|
958
|
+
getCaitlynConnectionErrorPlugin,
|
|
828
959
|
getCaitlynSdkPlugin,
|
|
829
960
|
getCaitlynTelemetryPlugin,
|
|
830
961
|
getWebIdentityToken,
|