@superblocksteam/sdk-api 2.0.155 → 2.0.156-next.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/errors.d.ts +6 -6
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +23 -2
- package/dist/errors.js.map +1 -1
- package/dist/integrations/base/decode-worker-binary-response.d.ts +4 -0
- package/dist/integrations/base/decode-worker-binary-response.d.ts.map +1 -0
- package/dist/integrations/base/decode-worker-binary-response.js +49 -0
- package/dist/integrations/base/decode-worker-binary-response.js.map +1 -0
- package/dist/integrations/base/decode-worker-binary-response.test.d.ts +2 -0
- package/dist/integrations/base/decode-worker-binary-response.test.d.ts.map +1 -0
- package/dist/integrations/base/decode-worker-binary-response.test.js +81 -0
- package/dist/integrations/base/decode-worker-binary-response.test.js.map +1 -0
- package/dist/integrations/base/rest-api-client-base.d.ts +0 -27
- package/dist/integrations/base/rest-api-client-base.d.ts.map +1 -1
- package/dist/integrations/base/rest-api-client-base.js +23 -46
- package/dist/integrations/base/rest-api-client-base.js.map +1 -1
- package/dist/integrations/base/rest-api-integration-client.d.ts +7 -19
- package/dist/integrations/base/rest-api-integration-client.d.ts.map +1 -1
- package/dist/integrations/base/rest-api-integration-client.js +2 -29
- package/dist/integrations/base/rest-api-integration-client.js.map +1 -1
- package/dist/integrations/base/types.d.ts +36 -43
- package/dist/integrations/base/types.d.ts.map +1 -1
- package/dist/integrations/base/types.js +1 -26
- package/dist/integrations/base/types.js.map +1 -1
- package/dist/integrations/documentation-resolver.test.js +11 -5
- package/dist/integrations/documentation-resolver.test.js.map +1 -1
- package/dist/integrations/restapiintegration/client.test.d.ts +0 -10
- package/dist/integrations/restapiintegration/client.test.d.ts.map +1 -1
- package/dist/integrations/restapiintegration/client.test.js +146 -69
- package/dist/integrations/restapiintegration/client.test.js.map +1 -1
- package/dist/integrations/slack/client.test.js +26 -1
- package/dist/integrations/slack/client.test.js.map +1 -1
- package/package.json +2 -2
- package/src/errors.ts +33 -5
- package/src/integrations/anthropic/README.md +7 -0
- package/src/integrations/base/decode-worker-binary-response.test.ts +107 -0
- package/src/integrations/base/decode-worker-binary-response.ts +62 -0
- package/src/integrations/base/rest-api-client-base.ts +28 -56
- package/src/integrations/base/rest-api-integration-client.ts +14 -33
- package/src/integrations/base/types.ts +41 -45
- package/src/integrations/bigquery/README.md +1 -0
- package/src/integrations/box/README.md +3 -0
- package/src/integrations/cohere/README.md +7 -0
- package/src/integrations/documentation-resolver.test.ts +13 -6
- package/src/integrations/fireworks/README.md +7 -0
- package/src/integrations/gemini/README.md +8 -0
- package/src/integrations/groq/README.md +7 -0
- package/src/integrations/mistral/README.md +7 -0
- package/src/integrations/openai_v2/README.md +7 -0
- package/src/integrations/perplexity/README.md +7 -0
- package/src/integrations/restapiintegration/client.test.ts +207 -86
- package/src/integrations/restapiintegration/docs.manifest.json +5 -1
- package/src/integrations/restapiintegration/overlays/response-types-binary.md +51 -0
- package/src/integrations/s3/README.md +1 -0
- package/src/integrations/slack/client.test.ts +36 -1
- package/src/integrations/snowflakecortex/README.md +8 -0
- package/src/integrations/stabilityai/README.md +7 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
|
|
3
|
+
import { RestApiValidationError } from "../../errors.js";
|
|
4
|
+
import { decodeWorkerBinaryResponse } from "./decode-worker-binary-response.js";
|
|
5
|
+
|
|
6
|
+
describe("decodeWorkerBinaryResponse", () => {
|
|
7
|
+
it("copies a Uint8Array into a plain Uint8Array with the same bytes", () => {
|
|
8
|
+
const source = new Uint8Array([1, 2, 255]);
|
|
9
|
+
|
|
10
|
+
const result = decodeWorkerBinaryResponse(source);
|
|
11
|
+
|
|
12
|
+
expect(result).toBeInstanceOf(Uint8Array);
|
|
13
|
+
expect(result.constructor).toBe(Uint8Array);
|
|
14
|
+
expect(Array.from(result)).toEqual([1, 2, 255]);
|
|
15
|
+
source[0] = 9;
|
|
16
|
+
expect(result[0]).toBe(1);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it("converts worker Buffer JSON into a plain Uint8Array", () => {
|
|
20
|
+
const result = decodeWorkerBinaryResponse({
|
|
21
|
+
type: "Buffer",
|
|
22
|
+
data: [37, 80, 68, 70],
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
expect(result.constructor).toBe(Uint8Array);
|
|
26
|
+
expect(Array.from(result)).toEqual([37, 80, 68, 70]);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it("converts empty Buffer JSON into a zero-length Uint8Array", () => {
|
|
30
|
+
const result = decodeWorkerBinaryResponse({ type: "Buffer", data: [] });
|
|
31
|
+
|
|
32
|
+
expect(result).toBeInstanceOf(Uint8Array);
|
|
33
|
+
expect(result.byteLength).toBe(0);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("rejects a missing discriminant", () => {
|
|
37
|
+
expect(() => decodeWorkerBinaryResponse({ data: [1] })).toThrow(
|
|
38
|
+
RestApiValidationError,
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it("rejects a non-Buffer discriminant", () => {
|
|
43
|
+
expect(() =>
|
|
44
|
+
decodeWorkerBinaryResponse({ type: "Array", data: [1] }),
|
|
45
|
+
).toThrow(RestApiValidationError);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it("rejects a non-array data field", () => {
|
|
49
|
+
expect(() =>
|
|
50
|
+
decodeWorkerBinaryResponse({ type: "Buffer", data: "00ff" }),
|
|
51
|
+
).toThrow(RestApiValidationError);
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
it("rejects a byte outside 0 through 255", () => {
|
|
55
|
+
expect(() =>
|
|
56
|
+
decodeWorkerBinaryResponse({ type: "Buffer", data: [256] }),
|
|
57
|
+
).toThrow(RestApiValidationError);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
it("rejects a negative byte", () => {
|
|
61
|
+
expect(() =>
|
|
62
|
+
decodeWorkerBinaryResponse({ type: "Buffer", data: [-1] }),
|
|
63
|
+
).toThrow(RestApiValidationError);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it("reports the index of a non-integer byte", () => {
|
|
67
|
+
try {
|
|
68
|
+
decodeWorkerBinaryResponse({ type: "Buffer", data: [1, 1.5] });
|
|
69
|
+
} catch (error) {
|
|
70
|
+
expect(error).toBeInstanceOf(RestApiValidationError);
|
|
71
|
+
if (!(error instanceof RestApiValidationError)) {
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
expect(error.details.zodError.issues).toEqual([
|
|
75
|
+
expect.objectContaining({ path: ["data", 1] }),
|
|
76
|
+
]);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
throw new Error("Expected binary response validation to fail");
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it("redacts malformed binary data from validation errors", () => {
|
|
83
|
+
const data = Array.from({ length: 4096 }, () => 0);
|
|
84
|
+
data[4095] = 256;
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
decodeWorkerBinaryResponse({ type: "Buffer", data });
|
|
88
|
+
} catch (error) {
|
|
89
|
+
expect(error).toBeInstanceOf(RestApiValidationError);
|
|
90
|
+
if (!(error instanceof RestApiValidationError)) {
|
|
91
|
+
throw error;
|
|
92
|
+
}
|
|
93
|
+
expect(error.details.data).toEqual({
|
|
94
|
+
dataType: "binary",
|
|
95
|
+
redacted: true,
|
|
96
|
+
});
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
throw new Error("Expected malformed binary response to fail");
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
it("rejects a string payload", () => {
|
|
103
|
+
expect(() => decodeWorkerBinaryResponse("not-binary")).toThrow(
|
|
104
|
+
RestApiValidationError,
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
});
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
REDACTED_BINARY_RESPONSE_DATA,
|
|
5
|
+
RestApiValidationError,
|
|
6
|
+
} from "../../errors.js";
|
|
7
|
+
|
|
8
|
+
export { REDACTED_BINARY_RESPONSE_DATA };
|
|
9
|
+
|
|
10
|
+
const workerBinaryResponseSchema = z.union([
|
|
11
|
+
z.instanceof(Uint8Array).transform((value) => new Uint8Array(value)),
|
|
12
|
+
z
|
|
13
|
+
.object({
|
|
14
|
+
data: z.unknown(),
|
|
15
|
+
type: z.literal("Buffer"),
|
|
16
|
+
})
|
|
17
|
+
.transform((value, context) => {
|
|
18
|
+
if (!Array.isArray(value.data)) {
|
|
19
|
+
context.addIssue({
|
|
20
|
+
code: z.ZodIssueCode.custom,
|
|
21
|
+
message: "data must be an array of bytes",
|
|
22
|
+
path: ["data"],
|
|
23
|
+
});
|
|
24
|
+
return z.NEVER;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const bytes = new Uint8Array(value.data.length);
|
|
28
|
+
for (let index = 0; index < value.data.length; index += 1) {
|
|
29
|
+
const item = value.data[index];
|
|
30
|
+
if (
|
|
31
|
+
typeof item !== "number" ||
|
|
32
|
+
!Number.isInteger(item) ||
|
|
33
|
+
item < 0 ||
|
|
34
|
+
item > 255
|
|
35
|
+
) {
|
|
36
|
+
context.addIssue({
|
|
37
|
+
code: z.ZodIssueCode.custom,
|
|
38
|
+
message: "byte must be an integer from 0 through 255",
|
|
39
|
+
path: ["data", index],
|
|
40
|
+
});
|
|
41
|
+
return z.NEVER;
|
|
42
|
+
}
|
|
43
|
+
bytes[index] = item;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
return bytes;
|
|
47
|
+
}),
|
|
48
|
+
]);
|
|
49
|
+
|
|
50
|
+
export function decodeWorkerBinaryResponse(value: unknown): Uint8Array {
|
|
51
|
+
const result = workerBinaryResponseSchema.safeParse(value);
|
|
52
|
+
if (!result.success) {
|
|
53
|
+
throw new RestApiValidationError(
|
|
54
|
+
`Binary response is malformed: ${result.error.message}`,
|
|
55
|
+
{
|
|
56
|
+
data: REDACTED_BINARY_RESPONSE_DATA,
|
|
57
|
+
zodError: result.error,
|
|
58
|
+
},
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
return result.data;
|
|
62
|
+
}
|
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Abstract base for REST API Integration clients.
|
|
3
|
-
*
|
|
4
|
-
* Owns the shared infrastructure that every REST-API-backed integration
|
|
5
|
-
* needs: config storage, parameter helpers, body validation, request
|
|
6
|
-
* construction, and query execution. Subclasses add their own
|
|
7
|
-
* `apiRequest` with whatever response-handling strategy they need
|
|
8
|
-
* (e.g. direct Zod validation, discriminated-union wrapping).
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
1
|
import type { PartialMessage } from "@bufbuild/protobuf";
|
|
12
2
|
import { z } from "zod";
|
|
13
3
|
|
|
@@ -17,18 +7,12 @@ import type { Plugin as RestApiIntegrationPlugin } from "@superblocksteam/types/
|
|
|
17
7
|
import { RestApiValidationError } from "../../errors.js";
|
|
18
8
|
import type { QueryExecutor, TraceMetadata } from "../registry.js";
|
|
19
9
|
import type { IntegrationConfig, IntegrationClientImpl } from "../types.js";
|
|
10
|
+
import { decodeWorkerBinaryResponse } from "./decode-worker-binary-response.js";
|
|
20
11
|
import { REST_API_RESPONSE_TYPES } from "./types.js";
|
|
21
12
|
import type { ApiRequestOptions } from "./types.js";
|
|
22
13
|
|
|
23
14
|
export type RestApiRequest = PartialMessage<RestApiIntegrationPlugin>;
|
|
24
15
|
|
|
25
|
-
/**
|
|
26
|
-
* Shared base for all REST API Integration clients.
|
|
27
|
-
*
|
|
28
|
-
* Provides config fields, parameter helpers, body validation, request
|
|
29
|
-
* building, and query execution. Does NOT define `apiRequest` — each
|
|
30
|
-
* concrete subclass supplies its own return-type contract.
|
|
31
|
-
*/
|
|
32
16
|
export abstract class RestApiClientBase implements IntegrationClientImpl {
|
|
33
17
|
readonly name: string;
|
|
34
18
|
readonly pluginId: string;
|
|
@@ -53,23 +37,11 @@ export abstract class RestApiClientBase implements IntegrationClientImpl {
|
|
|
53
37
|
};
|
|
54
38
|
}
|
|
55
39
|
|
|
56
|
-
/**
|
|
57
|
-
* Validate the request body, build the proto request, and execute it.
|
|
58
|
-
*
|
|
59
|
-
* Returns the raw (unvalidated) response from the orchestrator.
|
|
60
|
-
* Subclasses call this, then apply their own response handling.
|
|
61
|
-
*
|
|
62
|
-
* @param options - Request configuration (see {@link ApiRequestOptions})
|
|
63
|
-
* @param bodySchema - Optional Zod schema for body validation
|
|
64
|
-
* @param metadata - Optional trace metadata for observability
|
|
65
|
-
* @returns Raw response from the orchestrator
|
|
66
|
-
*/
|
|
67
40
|
protected async executeApiRequest<TBody>(
|
|
68
41
|
options: ApiRequestOptions<TBody>,
|
|
69
42
|
bodySchema?: z.ZodSchema<TBody>,
|
|
70
43
|
metadata?: TraceMetadata,
|
|
71
44
|
): Promise<unknown> {
|
|
72
|
-
// Validate request body if both body and schema are present.
|
|
73
45
|
if (options.body !== undefined && bodySchema) {
|
|
74
46
|
const bodyParseResult = bodySchema.safeParse(options.body);
|
|
75
47
|
if (!bodyParseResult.success) {
|
|
@@ -97,16 +69,12 @@ export abstract class RestApiClientBase implements IntegrationClientImpl {
|
|
|
97
69
|
}
|
|
98
70
|
}
|
|
99
71
|
|
|
100
|
-
// The RestApiResponseType union is erased in the bundled JS user APIs
|
|
101
|
-
// run as, so values outside it (including wire values the orchestrator
|
|
102
|
-
// implements but the SDK deliberately does not expose, like "auto")
|
|
103
|
-
// must also be rejected at runtime, before the request is issued.
|
|
104
72
|
const responseTypeResult = z
|
|
105
73
|
.enum(REST_API_RESPONSE_TYPES)
|
|
106
74
|
.safeParse(options.responseType ?? "json");
|
|
107
75
|
if (!responseTypeResult.success) {
|
|
108
76
|
throw new RestApiValidationError(
|
|
109
|
-
`Unsupported responseType ${JSON.stringify(options.responseType)}
|
|
77
|
+
`Unsupported responseType ${JSON.stringify(options.responseType)} - expected one of: ${REST_API_RESPONSE_TYPES.join(", ")}`,
|
|
110
78
|
{
|
|
111
79
|
zodError: responseTypeResult.error,
|
|
112
80
|
data: options.responseType,
|
|
@@ -129,30 +97,34 @@ export abstract class RestApiClientBase implements IntegrationClientImpl {
|
|
|
129
97
|
request.bodyType = "jsonBody";
|
|
130
98
|
}
|
|
131
99
|
|
|
132
|
-
const result = await this.executeQuery(
|
|
133
|
-
|
|
134
|
-
undefined,
|
|
135
|
-
metadata,
|
|
136
|
-
);
|
|
137
|
-
|
|
138
|
-
// Neither `null` nor `undefined` is a legitimate decode result for the
|
|
139
|
-
// exposed response types: JSON parses to a value the schema sees, and
|
|
140
|
-
// an empty text body decodes to "". Either value only arises from a
|
|
141
|
-
// broken execution contract, so both throw. (If "auto" is ever exposed,
|
|
142
|
-
// `null` becomes legitimate — it parses a literal `null` JSON body.)
|
|
100
|
+
const result = await this.executeQuery({ ...request }, undefined, metadata);
|
|
101
|
+
|
|
143
102
|
if (result === null || result === undefined) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
103
|
+
throw new RestApiValidationError(
|
|
104
|
+
`Integration query returned ${String(result)} for responseType "${responseType}" - expected a response value`,
|
|
105
|
+
{
|
|
106
|
+
zodError: new z.ZodError([
|
|
107
|
+
{
|
|
108
|
+
code: z.ZodIssueCode.custom,
|
|
109
|
+
message: "response value is required",
|
|
110
|
+
path: [],
|
|
111
|
+
},
|
|
112
|
+
]),
|
|
113
|
+
data: result,
|
|
114
|
+
},
|
|
115
|
+
);
|
|
154
116
|
}
|
|
155
117
|
|
|
156
|
-
|
|
118
|
+
switch (responseType) {
|
|
119
|
+
case "binary":
|
|
120
|
+
return decodeWorkerBinaryResponse(result);
|
|
121
|
+
case "json":
|
|
122
|
+
case "text":
|
|
123
|
+
return result;
|
|
124
|
+
default: {
|
|
125
|
+
const _exhaustive: never = responseType;
|
|
126
|
+
return _exhaustive;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
157
129
|
}
|
|
158
130
|
}
|
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic REST API Integration client with Zod response validation.
|
|
3
|
-
*
|
|
4
|
-
* Extends RestApiClientBase with an apiRequest() that validates the
|
|
5
|
-
* full response against a caller-supplied Zod schema and throws
|
|
6
|
-
* RestApiValidationError on mismatch. When no response schema is
|
|
7
|
-
* provided, the raw result is returned without validation — useful for
|
|
8
|
-
* non-JSON responses (e.g. XML), which can alternatively be validated
|
|
9
|
-
* with a schema matching the decoded value (e.g. z.string()).
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
1
|
import { z } from "zod";
|
|
13
2
|
|
|
14
3
|
import { RestApiValidationError } from "../../errors.js";
|
|
@@ -21,19 +10,14 @@ import type {
|
|
|
21
10
|
SupportsApiRequest,
|
|
22
11
|
} from "./types.js";
|
|
23
12
|
|
|
24
|
-
/**
|
|
25
|
-
* Base implementation for REST API Integration clients.
|
|
26
|
-
*
|
|
27
|
-
* All OpenAPI-based integration clients (except those with
|
|
28
|
-
* integration-specific response handling) extend this class to
|
|
29
|
-
* inherit the generic apiRequest() method with runtime Zod validation.
|
|
30
|
-
*/
|
|
31
13
|
export abstract class RestApiIntegrationClient
|
|
32
14
|
extends RestApiClientBase
|
|
33
15
|
implements SupportsApiRequest
|
|
34
16
|
{
|
|
35
17
|
async apiRequest<TBody, TResponse>(
|
|
36
|
-
options: ApiRequestOptions<TBody
|
|
18
|
+
options: ApiRequestOptions<TBody> & {
|
|
19
|
+
responseType?: Exclude<RestApiResponseType, "binary">;
|
|
20
|
+
},
|
|
37
21
|
schema: ApiRequestSchema<TBody, TResponse> & {
|
|
38
22
|
response: z.ZodSchema<TResponse>;
|
|
39
23
|
},
|
|
@@ -41,7 +25,14 @@ export abstract class RestApiIntegrationClient
|
|
|
41
25
|
): Promise<TResponse>;
|
|
42
26
|
async apiRequest<TBody>(
|
|
43
27
|
options: ApiRequestOptions<TBody> & {
|
|
44
|
-
responseType:
|
|
28
|
+
responseType: "binary";
|
|
29
|
+
},
|
|
30
|
+
schema?: ApiRequestSchema<TBody, Uint8Array>,
|
|
31
|
+
metadata?: TraceMetadata,
|
|
32
|
+
): Promise<Uint8Array>;
|
|
33
|
+
async apiRequest<TBody>(
|
|
34
|
+
options: ApiRequestOptions<TBody> & {
|
|
35
|
+
responseType: "text";
|
|
45
36
|
},
|
|
46
37
|
schema?: ApiRequestSchema<TBody, unknown>,
|
|
47
38
|
metadata?: TraceMetadata,
|
|
@@ -50,13 +41,10 @@ export abstract class RestApiIntegrationClient
|
|
|
50
41
|
options: ApiRequestOptions<TBody>,
|
|
51
42
|
schema?: ApiRequestSchema<TBody, TResponse>,
|
|
52
43
|
metadata?: TraceMetadata,
|
|
53
|
-
): Promise<TResponse | unknown> {
|
|
54
|
-
// The overloads make schema-less JSON unrepresentable in TypeScript,
|
|
55
|
-
// but user API code executes as bundled JS with no type enforcement —
|
|
56
|
-
// enforce the same contract at runtime, before issuing the request.
|
|
44
|
+
): Promise<TResponse | Uint8Array | unknown> {
|
|
57
45
|
if ((options.responseType ?? "json") === "json" && !schema?.response) {
|
|
58
46
|
throw new RestApiValidationError(
|
|
59
|
-
'apiRequest() with responseType "json" requires a response schema
|
|
47
|
+
'apiRequest() with responseType "json" requires a response schema. Provide one, or set responseType to "text" or "binary"',
|
|
60
48
|
{
|
|
61
49
|
zodError: new z.ZodError([
|
|
62
50
|
{
|
|
@@ -76,13 +64,6 @@ export abstract class RestApiIntegrationClient
|
|
|
76
64
|
metadata,
|
|
77
65
|
);
|
|
78
66
|
|
|
79
|
-
// Without a response schema the raw result is returned as-is (typed
|
|
80
|
-
// unknown). Non-JSON responses (e.g. responseType "text" returning
|
|
81
|
-
// XML) can either omit the schema or pass one matching the decoded
|
|
82
|
-
// value, such as z.string(). Request-body validation is opt-in:
|
|
83
|
-
// executeApiRequest only validates options.body when schema.body is
|
|
84
|
-
// provided (unchanged from the previous contract, where schema.body
|
|
85
|
-
// was already optional).
|
|
86
67
|
if (!schema?.response) {
|
|
87
68
|
return result;
|
|
88
69
|
}
|
|
@@ -93,8 +74,8 @@ export abstract class RestApiIntegrationClient
|
|
|
93
74
|
throw new RestApiValidationError(
|
|
94
75
|
`Response validation failed: ${responseParseResult.error.message}`,
|
|
95
76
|
{
|
|
96
|
-
zodError: responseParseResult.error,
|
|
97
77
|
data: result,
|
|
78
|
+
zodError: responseParseResult.error,
|
|
98
79
|
},
|
|
99
80
|
);
|
|
100
81
|
}
|
|
@@ -1,43 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared types for REST API Integration (OpenAPI) based clients.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
1
|
import type { z } from "zod";
|
|
6
2
|
|
|
7
3
|
import type { TraceMetadata } from "../registry.js";
|
|
8
4
|
|
|
9
|
-
// Re-export for backwards compatibility
|
|
10
5
|
export type { TraceMetadata };
|
|
11
6
|
|
|
12
|
-
|
|
13
|
-
* How the orchestrator should decode the HTTP response body.
|
|
14
|
-
*
|
|
15
|
-
* - `"json"` (default): parse the response as JSON; fails on non-JSON bodies
|
|
16
|
-
* - `"text"`: return the decoded response body as a string (XML, CSV, HTML, ...)
|
|
17
|
-
*
|
|
18
|
-
* The orchestrator also implements `"auto"`, `"binary"`, and `"raw"` wire
|
|
19
|
-
* values; they are deliberately not exposed here (widening this union later
|
|
20
|
-
* is backward-compatible, narrowing it is not):
|
|
21
|
-
*
|
|
22
|
-
* - `"auto"` returns parsed JSON whenever the body happens to parse, which
|
|
23
|
-
* would let JSON responses bypass schema validation via the schema-less
|
|
24
|
-
* overload
|
|
25
|
-
* - `"binary"` would freeze the worker's internal Buffer JSON encoding
|
|
26
|
-
* (`{ type: "Buffer", data: number[] }`, ~4x payload inflation) into the
|
|
27
|
-
* public SDK contract
|
|
28
|
-
* - `"raw"` is rejected by the orchestrator for non-streaming requests
|
|
29
|
-
*
|
|
30
|
-
* The runtime tuple backs the request-time guard in `executeApiRequest`:
|
|
31
|
-
* the type-level union is erased in the bundled JS user APIs run as, so
|
|
32
|
-
* values outside it must also be rejected at runtime.
|
|
33
|
-
*/
|
|
34
|
-
export const REST_API_RESPONSE_TYPES = ["json", "text"] as const;
|
|
7
|
+
export const REST_API_RESPONSE_TYPES = ["binary", "json", "text"] as const;
|
|
35
8
|
|
|
36
9
|
export type RestApiResponseType = (typeof REST_API_RESPONSE_TYPES)[number];
|
|
37
10
|
|
|
38
|
-
/**
|
|
39
|
-
* Options for making a generic REST API request.
|
|
40
|
-
*/
|
|
41
11
|
export interface ApiRequestOptions<TBody = unknown> {
|
|
42
12
|
/**
|
|
43
13
|
* HTTP method for the request.
|
|
@@ -69,15 +39,14 @@ export interface ApiRequestOptions<TBody = unknown> {
|
|
|
69
39
|
* How the response body should be decoded. Defaults to `"json"`.
|
|
70
40
|
*
|
|
71
41
|
* Use `"text"` for endpoints that return non-JSON payloads such as
|
|
72
|
-
* XML.
|
|
73
|
-
*
|
|
42
|
+
* XML. Use `"binary"` for PDFs and other byte payloads. When
|
|
43
|
+
* requesting a text or binary response, omit the response schema (or
|
|
44
|
+
* use one matching the decoded value, e.g. `z.string()` or
|
|
45
|
+
* `z.instanceof(Uint8Array)`).
|
|
74
46
|
*/
|
|
75
47
|
responseType?: RestApiResponseType;
|
|
76
48
|
}
|
|
77
49
|
|
|
78
|
-
/**
|
|
79
|
-
* Schema configuration for API request validation.
|
|
80
|
-
*/
|
|
81
50
|
export interface ApiRequestSchema<TBody = unknown, TResponse = unknown> {
|
|
82
51
|
/**
|
|
83
52
|
* Optional Zod schema for request body validation.
|
|
@@ -96,10 +65,9 @@ export interface ApiRequestSchema<TBody = unknown, TResponse = unknown> {
|
|
|
96
65
|
* Interface for integration clients that support generic API requests.
|
|
97
66
|
*
|
|
98
67
|
* Providing a response schema gives type-safe, validated results. Omitting
|
|
99
|
-
* it returns the
|
|
100
|
-
* `responseType: "text"
|
|
101
|
-
*
|
|
102
|
-
* only make sense for JSON-shaped results.
|
|
68
|
+
* it returns the decoded response: `Uint8Array` for `responseType: "binary"`,
|
|
69
|
+
* or `unknown` for `responseType: "text"`. JSON still requires a schema.
|
|
70
|
+
* Object schemas only make sense for JSON-shaped results.
|
|
103
71
|
*/
|
|
104
72
|
export interface SupportsApiRequest {
|
|
105
73
|
/**
|
|
@@ -136,22 +104,50 @@ export interface SupportsApiRequest {
|
|
|
136
104
|
* ```
|
|
137
105
|
*/
|
|
138
106
|
apiRequest<TBody, TResponse>(
|
|
139
|
-
options: ApiRequestOptions<TBody
|
|
107
|
+
options: ApiRequestOptions<TBody> & {
|
|
108
|
+
responseType?: Exclude<RestApiResponseType, "binary">;
|
|
109
|
+
},
|
|
140
110
|
schema: ApiRequestSchema<TBody, TResponse> & {
|
|
141
111
|
response: z.ZodSchema<TResponse>;
|
|
142
112
|
},
|
|
143
113
|
metadata?: TraceMetadata,
|
|
144
114
|
): Promise<TResponse>;
|
|
145
115
|
|
|
116
|
+
/**
|
|
117
|
+
* Execute a generic API request that returns binary data.
|
|
118
|
+
*
|
|
119
|
+
* @param options - Request configuration; `responseType` must be "binary"
|
|
120
|
+
* @param schema - Optional Zod schemas for request body and decoded response validation
|
|
121
|
+
* @param metadata - Optional trace metadata for observability (label, description)
|
|
122
|
+
* @returns The decoded body as a `Uint8Array`
|
|
123
|
+
*
|
|
124
|
+
* @example
|
|
125
|
+
* ```typescript
|
|
126
|
+
* const pdf = await ctx.integrations.legacyApi.apiRequest({
|
|
127
|
+
* method: 'GET',
|
|
128
|
+
* path: '/file.pdf',
|
|
129
|
+
* responseType: 'binary',
|
|
130
|
+
* });
|
|
131
|
+
* // pdf is a Uint8Array
|
|
132
|
+
* ```
|
|
133
|
+
*/
|
|
134
|
+
apiRequest<TBody>(
|
|
135
|
+
options: ApiRequestOptions<TBody> & {
|
|
136
|
+
responseType: "binary";
|
|
137
|
+
},
|
|
138
|
+
schema?: ApiRequestSchema<TBody, Uint8Array>,
|
|
139
|
+
metadata?: TraceMetadata,
|
|
140
|
+
): Promise<Uint8Array>;
|
|
141
|
+
|
|
146
142
|
/**
|
|
147
143
|
* Execute a generic API request without response validation.
|
|
148
144
|
*
|
|
149
|
-
* This overload requires an explicit
|
|
145
|
+
* This overload requires an explicit `responseType: "text"` - JSON
|
|
150
146
|
* responses must always be consumed through the schema overload above,
|
|
151
|
-
* so "unvalidated JSON" is unrepresentable. The decoded
|
|
147
|
+
* so "unvalidated JSON" is unrepresentable. The decoded text
|
|
152
148
|
* response is returned as-is, typed `unknown`.
|
|
153
149
|
*
|
|
154
|
-
* Note: request-body validation is opt-in
|
|
150
|
+
* Note: request-body validation is opt-in - it runs only when
|
|
155
151
|
* `schema.body` is provided. Omitting `schema` sends `options.body`
|
|
156
152
|
* without validation.
|
|
157
153
|
*
|
|
@@ -172,7 +168,7 @@ export interface SupportsApiRequest {
|
|
|
172
168
|
*/
|
|
173
169
|
apiRequest<TBody>(
|
|
174
170
|
options: ApiRequestOptions<TBody> & {
|
|
175
|
-
responseType:
|
|
171
|
+
responseType: "text";
|
|
176
172
|
},
|
|
177
173
|
schema?: ApiRequestSchema<TBody, unknown>,
|
|
178
174
|
metadata?: TraceMetadata,
|
|
@@ -211,6 +211,13 @@ All methods accept an optional `metadata` parameter as the last argument for dia
|
|
|
211
211
|
|
|
212
212
|
## Common Pitfalls
|
|
213
213
|
|
|
214
|
+
### Streaming Is Not Supported
|
|
215
|
+
|
|
216
|
+
`apiRequest()` does not support streaming or Server-Sent Events. Do not set
|
|
217
|
+
`stream: true` — streaming responses fail schema validation. Every call
|
|
218
|
+
returns the complete response; if a UI needs real-time token streaming,
|
|
219
|
+
handle it at the frontend layer, not through the SDK.
|
|
220
|
+
|
|
214
221
|
### No Specialized Methods
|
|
215
222
|
|
|
216
223
|
```typescript
|
|
@@ -839,17 +839,23 @@ describe("resolveIntegrationDocumentation", () => {
|
|
|
839
839
|
});
|
|
840
840
|
|
|
841
841
|
describe("restapiintegration responseType gating (real docs)", () => {
|
|
842
|
-
|
|
843
|
-
// gating is the discoverability guard for the responseType feature
|
|
844
|
-
// (agents whose worker sdk-api predates it must never be told the
|
|
845
|
-
// option exists), so the real manifest content is the contract.
|
|
846
|
-
|
|
847
|
-
it("documents responseType for agents whose sdk-api supports it", async () => {
|
|
842
|
+
it("documents text responseType for agents whose sdk-api supports text only", async () => {
|
|
848
843
|
const docs = await resolveIntegrationDocumentation("restapiintegration", {
|
|
849
844
|
sdkVersion: "0.0.3",
|
|
850
845
|
});
|
|
851
846
|
|
|
852
847
|
expect(docs).toContain('responseType: "text"');
|
|
848
|
+
expect(docs).not.toContain('responseType: "binary"');
|
|
849
|
+
expect(docs).not.toContain("does not support the `responseType`");
|
|
850
|
+
});
|
|
851
|
+
|
|
852
|
+
it("documents binary responseType only for agents whose sdk-api supports it", async () => {
|
|
853
|
+
const docs = await resolveIntegrationDocumentation("restapiintegration", {
|
|
854
|
+
sdkVersion: "0.0.4",
|
|
855
|
+
});
|
|
856
|
+
|
|
857
|
+
expect(docs).toContain('responseType: "text"');
|
|
858
|
+
expect(docs).toContain('responseType: "binary"');
|
|
853
859
|
expect(docs).not.toContain("does not support the `responseType`");
|
|
854
860
|
});
|
|
855
861
|
|
|
@@ -860,6 +866,7 @@ describe("resolveIntegrationDocumentation", () => {
|
|
|
860
866
|
|
|
861
867
|
expect(docs).toContain("does not support the `responseType`");
|
|
862
868
|
expect(docs).not.toContain('responseType: "text"');
|
|
869
|
+
expect(docs).not.toContain('responseType: "binary"');
|
|
863
870
|
});
|
|
864
871
|
|
|
865
872
|
it("stays silent about responseType when the agent reports no sdk-api version", async () => {
|
|
@@ -293,6 +293,13 @@ All methods accept an optional `metadata` parameter as the last argument for dia
|
|
|
293
293
|
|
|
294
294
|
## Common Pitfalls
|
|
295
295
|
|
|
296
|
+
### Streaming Is Not Supported
|
|
297
|
+
|
|
298
|
+
`apiRequest()` does not support streaming or Server-Sent Events. Do not set
|
|
299
|
+
`stream: true` — streaming responses fail schema validation. Every call
|
|
300
|
+
returns the complete response; if a UI needs real-time token streaming,
|
|
301
|
+
handle it at the frontend layer, not through the SDK.
|
|
302
|
+
|
|
296
303
|
### No Specialized Methods
|
|
297
304
|
|
|
298
305
|
```typescript
|
|
@@ -265,6 +265,14 @@ All methods accept an optional `metadata` parameter as the last argument for dia
|
|
|
265
265
|
|
|
266
266
|
## Common Pitfalls
|
|
267
267
|
|
|
268
|
+
### Streaming Is Not Supported
|
|
269
|
+
|
|
270
|
+
`apiRequest()` does not support streaming or Server-Sent Events. Use
|
|
271
|
+
`:generateContent`, never `:streamGenerateContent` or `alt=sse` — streaming
|
|
272
|
+
responses fail schema validation. Every call returns the complete response;
|
|
273
|
+
if a UI needs real-time token streaming, handle it at the frontend layer,
|
|
274
|
+
not through the SDK.
|
|
275
|
+
|
|
268
276
|
### No Specialized Methods
|
|
269
277
|
|
|
270
278
|
```typescript
|
|
@@ -251,6 +251,13 @@ All methods accept an optional `metadata` parameter as the last argument for dia
|
|
|
251
251
|
|
|
252
252
|
## Common Pitfalls
|
|
253
253
|
|
|
254
|
+
### Streaming Is Not Supported
|
|
255
|
+
|
|
256
|
+
`apiRequest()` does not support streaming or Server-Sent Events. Do not set
|
|
257
|
+
`stream: true` — streaming responses fail schema validation. Every call
|
|
258
|
+
returns the complete response; if a UI needs real-time token streaming,
|
|
259
|
+
handle it at the frontend layer, not through the SDK.
|
|
260
|
+
|
|
254
261
|
### No Specialized Methods
|
|
255
262
|
|
|
256
263
|
```typescript
|
|
@@ -277,6 +277,13 @@ All methods accept an optional `metadata` parameter as the last argument for dia
|
|
|
277
277
|
|
|
278
278
|
## Common Pitfalls
|
|
279
279
|
|
|
280
|
+
### Streaming Is Not Supported
|
|
281
|
+
|
|
282
|
+
`apiRequest()` does not support streaming or Server-Sent Events. Do not set
|
|
283
|
+
`stream: true` — streaming responses fail schema validation. Every call
|
|
284
|
+
returns the complete response; if a UI needs real-time token streaming,
|
|
285
|
+
handle it at the frontend layer, not through the SDK.
|
|
286
|
+
|
|
280
287
|
### No Specialized Methods
|
|
281
288
|
|
|
282
289
|
```typescript
|