@superblocksteam/sdk-api 0.0.8 → 0.0.10

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.
Files changed (84) hide show
  1. package/dist/errors.d.ts +6 -6
  2. package/dist/errors.d.ts.map +1 -1
  3. package/dist/errors.js +23 -2
  4. package/dist/errors.js.map +1 -1
  5. package/dist/index.d.ts +1 -1
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js.map +1 -1
  8. package/dist/integrations/base/decode-worker-binary-response.d.ts +4 -0
  9. package/dist/integrations/base/decode-worker-binary-response.d.ts.map +1 -0
  10. package/dist/integrations/base/decode-worker-binary-response.js +49 -0
  11. package/dist/integrations/base/decode-worker-binary-response.js.map +1 -0
  12. package/dist/integrations/base/decode-worker-binary-response.test.d.ts +2 -0
  13. package/dist/integrations/base/decode-worker-binary-response.test.d.ts.map +1 -0
  14. package/dist/integrations/base/decode-worker-binary-response.test.js +81 -0
  15. package/dist/integrations/base/decode-worker-binary-response.test.js.map +1 -0
  16. package/dist/integrations/base/rest-api-client-base.d.ts +0 -27
  17. package/dist/integrations/base/rest-api-client-base.d.ts.map +1 -1
  18. package/dist/integrations/base/rest-api-client-base.js +23 -46
  19. package/dist/integrations/base/rest-api-client-base.js.map +1 -1
  20. package/dist/integrations/base/rest-api-integration-client.d.ts +7 -19
  21. package/dist/integrations/base/rest-api-integration-client.d.ts.map +1 -1
  22. package/dist/integrations/base/rest-api-integration-client.js +2 -29
  23. package/dist/integrations/base/rest-api-integration-client.js.map +1 -1
  24. package/dist/integrations/base/types.d.ts +36 -43
  25. package/dist/integrations/base/types.d.ts.map +1 -1
  26. package/dist/integrations/base/types.js +1 -26
  27. package/dist/integrations/base/types.js.map +1 -1
  28. package/dist/integrations/documentation-resolver.test.js +11 -5
  29. package/dist/integrations/documentation-resolver.test.js.map +1 -1
  30. package/dist/integrations/dynamodb/client.d.ts +6 -2
  31. package/dist/integrations/dynamodb/client.d.ts.map +1 -1
  32. package/dist/integrations/dynamodb/client.js +83 -10
  33. package/dist/integrations/dynamodb/client.js.map +1 -1
  34. package/dist/integrations/dynamodb/client.test.d.ts +8 -0
  35. package/dist/integrations/dynamodb/client.test.d.ts.map +1 -0
  36. package/dist/integrations/dynamodb/client.test.js +198 -0
  37. package/dist/integrations/dynamodb/client.test.js.map +1 -0
  38. package/dist/integrations/dynamodb/index.d.ts +1 -1
  39. package/dist/integrations/dynamodb/index.d.ts.map +1 -1
  40. package/dist/integrations/dynamodb/index.js.map +1 -1
  41. package/dist/integrations/dynamodb/types.d.ts +27 -1
  42. package/dist/integrations/dynamodb/types.d.ts.map +1 -1
  43. package/dist/integrations/index.d.ts +1 -1
  44. package/dist/integrations/index.d.ts.map +1 -1
  45. package/dist/integrations/index.js.map +1 -1
  46. package/dist/integrations/restapiintegration/client.test.d.ts +0 -10
  47. package/dist/integrations/restapiintegration/client.test.d.ts.map +1 -1
  48. package/dist/integrations/restapiintegration/client.test.js +146 -69
  49. package/dist/integrations/restapiintegration/client.test.js.map +1 -1
  50. package/dist/integrations/slack/client.test.js +26 -1
  51. package/dist/integrations/slack/client.test.js.map +1 -1
  52. package/package.json +1 -1
  53. package/src/errors.ts +33 -5
  54. package/src/index.ts +1 -0
  55. package/src/integrations/anthropic/README.md +7 -0
  56. package/src/integrations/base/decode-worker-binary-response.test.ts +107 -0
  57. package/src/integrations/base/decode-worker-binary-response.ts +62 -0
  58. package/src/integrations/base/rest-api-client-base.ts +28 -56
  59. package/src/integrations/base/rest-api-integration-client.ts +14 -33
  60. package/src/integrations/base/types.ts +41 -45
  61. package/src/integrations/bigquery/README.md +1 -0
  62. package/src/integrations/box/README.md +3 -0
  63. package/src/integrations/cohere/README.md +7 -0
  64. package/src/integrations/documentation-resolver.test.ts +13 -6
  65. package/src/integrations/dynamodb/README.md +33 -12
  66. package/src/integrations/dynamodb/client.test.ts +254 -0
  67. package/src/integrations/dynamodb/client.ts +121 -16
  68. package/src/integrations/dynamodb/index.ts +5 -1
  69. package/src/integrations/dynamodb/types.ts +33 -1
  70. package/src/integrations/fireworks/README.md +7 -0
  71. package/src/integrations/gemini/README.md +8 -0
  72. package/src/integrations/groq/README.md +7 -0
  73. package/src/integrations/index.ts +1 -0
  74. package/src/integrations/mistral/README.md +7 -0
  75. package/src/integrations/openai_v2/README.md +7 -0
  76. package/src/integrations/perplexity/README.md +7 -0
  77. package/src/integrations/restapiintegration/client.test.ts +207 -86
  78. package/src/integrations/restapiintegration/docs.manifest.json +5 -1
  79. package/src/integrations/restapiintegration/overlays/response-types-binary.md +51 -0
  80. package/src/integrations/restapiintegration/overlays/response-types-unsupported.md +3 -1
  81. package/src/integrations/s3/README.md +1 -0
  82. package/src/integrations/slack/client.test.ts +36 -1
  83. package/src/integrations/snowflakecortex/README.md +8 -0
  84. package/src/integrations/stabilityai/README.md +7 -0
@@ -0,0 +1,254 @@
1
+ /**
2
+ * Unit tests for DynamoDB client request bodies.
3
+ *
4
+ * The orchestrator forwards `action` + JSON `body` to the AWS SDK, so these
5
+ * tests lock the wire params - especially ExclusiveStartKey pagination.
6
+ */
7
+
8
+ import { describe, it, expect, vi } from "vitest";
9
+ import { z } from "zod";
10
+
11
+ import type { IntegrationConfig } from "../types.js";
12
+ import { DynamoDBClientImpl } from "./client.js";
13
+
14
+ const TEST_CONFIG: IntegrationConfig = {
15
+ id: "dynamodb-test-id",
16
+ name: "Test DynamoDB",
17
+ pluginId: "dynamodb",
18
+ configuration: {},
19
+ };
20
+
21
+ const ItemsSchema = z.object({ Items: z.array(z.unknown()).optional() });
22
+
23
+ function createClient(mockResult: unknown = { Items: [] }) {
24
+ const executeQuery = vi.fn().mockResolvedValue(mockResult);
25
+ const client = new DynamoDBClientImpl(TEST_CONFIG, executeQuery);
26
+ return { client, executeQuery };
27
+ }
28
+
29
+ function parsedBody(executeQuery: ReturnType<typeof vi.fn>, callIndex = 0) {
30
+ const request = z
31
+ .object({ action: z.string(), body: z.string() })
32
+ .parse(executeQuery.mock.calls[callIndex]?.[0]);
33
+ return { action: request.action, body: JSON.parse(request.body) };
34
+ }
35
+
36
+ describe("DynamoDBClientImpl", () => {
37
+ describe("scan", () => {
38
+ it("omits ExclusiveStartKey on a positional scan", async () => {
39
+ const { client, executeQuery } = createClient();
40
+
41
+ await client.scan("retailers", ItemsSchema, "status = :s", {
42
+ ":s": { S: "active" },
43
+ });
44
+
45
+ expect(parsedBody(executeQuery)).toEqual({
46
+ action: "scan",
47
+ body: {
48
+ TableName: "retailers",
49
+ FilterExpression: "status = :s",
50
+ ExpressionAttributeValues: { ":s": { S: "active" } },
51
+ },
52
+ });
53
+ });
54
+
55
+ it("forwards ExclusiveStartKey from the options object", async () => {
56
+ const { client, executeQuery } = createClient();
57
+ const exclusiveStartKey = { id: { S: "retailer-18" } };
58
+
59
+ await client.scan("retailers", ItemsSchema, { exclusiveStartKey });
60
+
61
+ expect(parsedBody(executeQuery)).toEqual({
62
+ action: "scan",
63
+ body: {
64
+ TableName: "retailers",
65
+ ExclusiveStartKey: exclusiveStartKey,
66
+ },
67
+ });
68
+ });
69
+
70
+ it("treats an empty object as empty scan options", async () => {
71
+ const { client, executeQuery } = createClient();
72
+
73
+ await client.scan("retailers", ItemsSchema, {});
74
+
75
+ expect(parsedBody(executeQuery)).toEqual({
76
+ action: "scan",
77
+ body: { TableName: "retailers" },
78
+ });
79
+ });
80
+
81
+ it("rejects objects with unknown scan option keys", async () => {
82
+ const { client, executeQuery } = createClient();
83
+
84
+ await expect(
85
+ Reflect.apply(client.scan, client, [
86
+ "retailers",
87
+ ItemsSchema,
88
+ { label: "not scan options" },
89
+ ]),
90
+ ).rejects.toThrow("Invalid DynamoDB scan options: label");
91
+ expect(executeQuery).not.toHaveBeenCalled();
92
+ });
93
+
94
+ it("forwards pagination and parallel-scan options", async () => {
95
+ const { client, executeQuery } = createClient();
96
+
97
+ await client.scan(
98
+ "retailers",
99
+ ItemsSchema,
100
+ {
101
+ exclusiveStartKey: { id: { S: "page-2" } },
102
+ limit: 25,
103
+ projectionExpression: "id, name",
104
+ indexName: "gsi1",
105
+ segment: 0,
106
+ totalSegments: 4,
107
+ filterExpression: "#s = :s",
108
+ expressionAttributeNames: { "#s": "status" },
109
+ expressionAttributeValues: { ":s": { S: "active" } },
110
+ },
111
+ { label: "page retailers" },
112
+ );
113
+
114
+ expect(parsedBody(executeQuery)).toEqual({
115
+ action: "scan",
116
+ body: {
117
+ TableName: "retailers",
118
+ ExclusiveStartKey: { id: { S: "page-2" } },
119
+ Limit: 25,
120
+ ProjectionExpression: "id, name",
121
+ IndexName: "gsi1",
122
+ Segment: 0,
123
+ TotalSegments: 4,
124
+ FilterExpression: "#s = :s",
125
+ ExpressionAttributeNames: { "#s": "status" },
126
+ ExpressionAttributeValues: { ":s": { S: "active" } },
127
+ },
128
+ });
129
+ expect(executeQuery).toHaveBeenCalledWith(expect.anything(), undefined, {
130
+ label: "page retailers",
131
+ });
132
+ });
133
+
134
+ it("keeps positional names and metadata working together", async () => {
135
+ const { client, executeQuery } = createClient();
136
+
137
+ await client.scan(
138
+ "retailers",
139
+ ItemsSchema,
140
+ "#s = :s",
141
+ { ":s": { S: "active" } },
142
+ { "#s": "status" },
143
+ { label: "filtered scan" },
144
+ );
145
+
146
+ expect(parsedBody(executeQuery).body).toEqual({
147
+ TableName: "retailers",
148
+ FilterExpression: "#s = :s",
149
+ ExpressionAttributeValues: { ":s": { S: "active" } },
150
+ ExpressionAttributeNames: { "#s": "status" },
151
+ });
152
+ expect(executeQuery.mock.calls[0][2]).toEqual({ label: "filtered scan" });
153
+ });
154
+
155
+ it("keeps schemas that strip pagination cursors backward compatible", async () => {
156
+ const lastEvaluatedKey = {
157
+ id: { S: "retailer-18" },
158
+ version: { N: "2" },
159
+ };
160
+ const { client } = createClient({
161
+ Items: [{ id: { S: "retailer-1" } }],
162
+ LastEvaluatedKey: lastEvaluatedKey,
163
+ });
164
+
165
+ await expect(
166
+ client.scan("retailers", ItemsSchema, { limit: 25 }),
167
+ ).resolves.toEqual({
168
+ Items: [{ id: { S: "retailer-1" } }],
169
+ });
170
+ });
171
+
172
+ it("preserves mixed AttributeValue pagination cursors", async () => {
173
+ const lastEvaluatedKey = {
174
+ id: { S: "retailer-18" },
175
+ version: { N: "2" },
176
+ };
177
+ const PageSchema = z.object({
178
+ Items: z.array(z.unknown()).optional(),
179
+ LastEvaluatedKey: z.record(z.unknown()).optional(),
180
+ });
181
+ const { client } = createClient({
182
+ Items: [],
183
+ LastEvaluatedKey: lastEvaluatedKey,
184
+ });
185
+
186
+ const page = await client.scan("retailers", PageSchema, {});
187
+
188
+ expect(page.LastEvaluatedKey).toEqual(lastEvaluatedKey);
189
+ });
190
+
191
+ it("threads each returned pagination cursor into the next request", async () => {
192
+ const LastEvaluatedKeySchema = z.object({
193
+ id: z.object({ S: z.string() }),
194
+ });
195
+ const PageSchema = z.object({
196
+ Items: z.array(z.string()),
197
+ LastEvaluatedKey: LastEvaluatedKeySchema.optional(),
198
+ });
199
+ const lastEvaluatedKey = { id: { S: "retailer-1" } };
200
+ const executeQuery = vi
201
+ .fn()
202
+ .mockResolvedValueOnce({
203
+ Items: ["retailer-1"],
204
+ LastEvaluatedKey: lastEvaluatedKey,
205
+ })
206
+ .mockResolvedValueOnce({ Items: ["retailer-2"] });
207
+ const client = new DynamoDBClientImpl(TEST_CONFIG, executeQuery);
208
+
209
+ const items: string[] = [];
210
+ let exclusiveStartKey: z.infer<typeof LastEvaluatedKeySchema> | undefined;
211
+ do {
212
+ const page = await client.scan("retailers", PageSchema, {
213
+ exclusiveStartKey,
214
+ });
215
+ items.push(...page.Items);
216
+ exclusiveStartKey = page.LastEvaluatedKey;
217
+ } while (exclusiveStartKey);
218
+
219
+ expect(items).toEqual(["retailer-1", "retailer-2"]);
220
+ expect(executeQuery).toHaveBeenCalledTimes(2);
221
+ expect(parsedBody(executeQuery, 0).body).toEqual({
222
+ TableName: "retailers",
223
+ });
224
+ expect(parsedBody(executeQuery, 1).body).toEqual({
225
+ TableName: "retailers",
226
+ ExclusiveStartKey: lastEvaluatedKey,
227
+ });
228
+ });
229
+ });
230
+
231
+ describe("queryTable", () => {
232
+ it("treats a plain names map as ExpressionAttributeNames", async () => {
233
+ const { client, executeQuery } = createClient();
234
+
235
+ await client.queryTable(
236
+ "orders",
237
+ "#uid = :uid",
238
+ { ":uid": { S: "user-123" } },
239
+ ItemsSchema,
240
+ { "#uid": "userId" },
241
+ );
242
+
243
+ expect(parsedBody(executeQuery)).toEqual({
244
+ action: "query",
245
+ body: {
246
+ TableName: "orders",
247
+ KeyConditionExpression: "#uid = :uid",
248
+ ExpressionAttributeValues: { ":uid": { S: "user-123" } },
249
+ ExpressionAttributeNames: { "#uid": "userId" },
250
+ },
251
+ });
252
+ });
253
+ });
254
+ });
@@ -12,7 +12,23 @@ import { RestApiValidationError } from "../../errors.js";
12
12
  import { IntegrationError } from "../../runtime/errors.js";
13
13
  import type { QueryExecutor, TraceMetadata } from "../registry.js";
14
14
  import type { IntegrationConfig, IntegrationClientImpl } from "../types.js";
15
- import type { DynamoDBClient, DynamoDBAttributeValue } from "./types.js";
15
+ import type {
16
+ DynamoDBClient,
17
+ DynamoDBAttributeValue,
18
+ DynamoDBScanOptions,
19
+ } from "./types.js";
20
+
21
+ const DYNAMODB_SCAN_OPTION_KEYS = new Set<string>([
22
+ "filterExpression",
23
+ "expressionAttributeValues",
24
+ "expressionAttributeNames",
25
+ "exclusiveStartKey",
26
+ "limit",
27
+ "projectionExpression",
28
+ "indexName",
29
+ "segment",
30
+ "totalSegments",
31
+ ]);
16
32
 
17
33
  /**
18
34
  * Internal implementation of DynamoDBClient.
@@ -189,33 +205,120 @@ export class DynamoDBClientImpl
189
205
  return this.executeWithErrorHandling(request, "deleteItem", metadata);
190
206
  }
191
207
 
208
+ /** Copy optional AWS Scan fields onto the request body. */
209
+ private applyScanParams(
210
+ params: Record<string, unknown>,
211
+ options: DynamoDBScanOptions,
212
+ ): void {
213
+ if (options.filterExpression) {
214
+ params.FilterExpression = options.filterExpression;
215
+ }
216
+ if (
217
+ options.expressionAttributeValues &&
218
+ Object.keys(options.expressionAttributeValues).length > 0
219
+ ) {
220
+ params.ExpressionAttributeValues = options.expressionAttributeValues;
221
+ }
222
+ if (
223
+ options.expressionAttributeNames &&
224
+ Object.keys(options.expressionAttributeNames).length > 0
225
+ ) {
226
+ params.ExpressionAttributeNames = options.expressionAttributeNames;
227
+ }
228
+ if (options.exclusiveStartKey) {
229
+ params.ExclusiveStartKey = options.exclusiveStartKey;
230
+ }
231
+ if (options.limit !== undefined) {
232
+ params.Limit = options.limit;
233
+ }
234
+ if (options.projectionExpression) {
235
+ params.ProjectionExpression = options.projectionExpression;
236
+ }
237
+ if (options.indexName) {
238
+ params.IndexName = options.indexName;
239
+ }
240
+ if (options.segment !== undefined) {
241
+ params.Segment = options.segment;
242
+ }
243
+ if (options.totalSegments !== undefined) {
244
+ params.TotalSegments = options.totalSegments;
245
+ }
246
+ }
247
+
248
+ private isScanOptions(value: unknown): value is DynamoDBScanOptions {
249
+ return (
250
+ typeof value === "object" &&
251
+ value !== null &&
252
+ Object.keys(value).every((key) => DYNAMODB_SCAN_OPTION_KEYS.has(key))
253
+ );
254
+ }
255
+
256
+ private isTraceMetadata(value: unknown): value is TraceMetadata {
257
+ return (
258
+ typeof value === "object" &&
259
+ value !== null &&
260
+ Object.keys(value).every(
261
+ (key) => key === "label" || key === "description",
262
+ ) &&
263
+ Object.values(value).every(
264
+ (entry) => entry === undefined || typeof entry === "string",
265
+ )
266
+ );
267
+ }
268
+
192
269
  async scan<T>(
193
270
  table: string,
194
271
  schema: z.ZodSchema<T>,
195
- filterExpression?: string,
196
- expressionAttributeValues?: Record<string, DynamoDBAttributeValue>,
272
+ filterExpressionOrOptions?: string | DynamoDBScanOptions,
273
+ expressionAttributeValuesOrMetadata?:
274
+ | Record<string, DynamoDBAttributeValue>
275
+ | TraceMetadata,
197
276
  expressionAttributeNames?: Record<string, string>,
198
277
  metadata?: TraceMetadata,
199
278
  ): Promise<T> {
200
279
  const params: Record<string, unknown> = { TableName: table };
280
+ let resolvedMetadata = metadata;
281
+
282
+ if (
283
+ typeof filterExpressionOrOptions === "object" &&
284
+ filterExpressionOrOptions !== null
285
+ ) {
286
+ if (!this.isScanOptions(filterExpressionOrOptions)) {
287
+ throw new Error(
288
+ `Invalid DynamoDB scan options: ${Object.keys(filterExpressionOrOptions).join(", ")}`,
289
+ );
290
+ }
291
+ this.applyScanParams(params, filterExpressionOrOptions);
201
292
 
202
- if (filterExpression) {
203
- params.FilterExpression = filterExpression;
204
- }
205
-
206
- if (expressionAttributeValues) {
207
- params.ExpressionAttributeValues = expressionAttributeValues;
208
- }
209
-
210
- if (expressionAttributeNames) {
211
- params.ExpressionAttributeNames = expressionAttributeNames;
293
+ if (expressionAttributeValuesOrMetadata !== undefined) {
294
+ if (!this.isTraceMetadata(expressionAttributeValuesOrMetadata)) {
295
+ throw new Error("Invalid DynamoDB scan trace metadata");
296
+ }
297
+ resolvedMetadata = expressionAttributeValuesOrMetadata;
298
+ }
299
+ } else {
300
+ if (filterExpressionOrOptions) {
301
+ params.FilterExpression = filterExpressionOrOptions;
302
+ }
303
+ if (
304
+ expressionAttributeValuesOrMetadata &&
305
+ Object.keys(expressionAttributeValuesOrMetadata).length > 0
306
+ ) {
307
+ params.ExpressionAttributeValues = expressionAttributeValuesOrMetadata;
308
+ }
309
+ if (
310
+ expressionAttributeNames &&
311
+ Object.keys(expressionAttributeNames).length > 0
312
+ ) {
313
+ params.ExpressionAttributeNames = expressionAttributeNames;
314
+ }
212
315
  }
213
316
 
214
317
  const request = this.buildRequest("scan", params);
215
318
  const result = await this.executeWithErrorHandling(
216
319
  request,
217
320
  "scan",
218
- metadata,
321
+ resolvedMetadata,
219
322
  );
220
323
  return this.validateResult(result, schema, "scan");
221
324
  }
@@ -233,8 +336,10 @@ export class DynamoDBClientImpl
233
336
  KeyConditionExpression: keyConditionExpression,
234
337
  ExpressionAttributeValues: expressionAttributeValues,
235
338
  };
236
-
237
- if (expressionAttributeNames) {
339
+ if (
340
+ expressionAttributeNames &&
341
+ Object.keys(expressionAttributeNames).length > 0
342
+ ) {
238
343
  params.ExpressionAttributeNames = expressionAttributeNames;
239
344
  }
240
345
 
@@ -4,5 +4,9 @@
4
4
  * @module
5
5
  */
6
6
 
7
- export type { DynamoDBClient, DynamoDBAttributeValue } from "./types.js";
7
+ export type {
8
+ DynamoDBClient,
9
+ DynamoDBAttributeValue,
10
+ DynamoDBScanOptions,
11
+ } from "./types.js";
8
12
  export { DynamoDBClientImpl } from "./client.js";
@@ -37,6 +37,24 @@ export type DynamoDBAttributeValue =
37
37
  | { NS: string[] }
38
38
  | { BS: string[] };
39
39
 
40
+ /**
41
+ * Optional Scan parameters forwarded to the AWS SDK.
42
+ *
43
+ * Use `exclusiveStartKey` with `LastEvaluatedKey` from a previous page to
44
+ * continue past DynamoDB's 1 MB per-Scan limit.
45
+ */
46
+ export interface DynamoDBScanOptions {
47
+ filterExpression?: string;
48
+ expressionAttributeValues?: Record<string, DynamoDBAttributeValue>;
49
+ expressionAttributeNames?: Record<string, string>;
50
+ exclusiveStartKey?: Record<string, DynamoDBAttributeValue>;
51
+ limit?: number;
52
+ projectionExpression?: string;
53
+ indexName?: string;
54
+ segment?: number;
55
+ totalSegments?: number;
56
+ }
57
+
40
58
  /**
41
59
  * DynamoDB client for database operations.
42
60
  *
@@ -84,6 +102,11 @@ export type DynamoDBAttributeValue =
84
102
  * 'status = :s',
85
103
  * { ':s': { S: 'active' } }
86
104
  * );
105
+ *
106
+ * // Paginate past the 1 MB Scan limit
107
+ * const page = await ctx.integrations.db.scan('users', UsersSchema, {
108
+ * exclusiveStartKey: lastEvaluatedKey,
109
+ * });
87
110
  * ```
88
111
  */
89
112
  export interface DynamoDBClient extends BaseIntegrationClient {
@@ -122,7 +145,10 @@ export interface DynamoDBClient extends BaseIntegrationClient {
122
145
  ): Promise<T>;
123
146
 
124
147
  /**
125
- * Scan a DynamoDB table with optional filter.
148
+ * Scan a DynamoDB table.
149
+ *
150
+ * Prefer the options-object form when paginating (`exclusiveStartKey`) or
151
+ * when you need `limit`, `indexName`, or parallel scan segments.
126
152
  *
127
153
  * @param table - The table name
128
154
  * @param schema - Zod schema for validating the result
@@ -133,6 +159,12 @@ export interface DynamoDBClient extends BaseIntegrationClient {
133
159
  * @param metadata - Optional trace metadata for diagnostics
134
160
  * @returns The validated result
135
161
  */
162
+ scan<T>(
163
+ table: string,
164
+ schema: z.ZodSchema<T>,
165
+ options: DynamoDBScanOptions,
166
+ metadata?: TraceMetadata,
167
+ ): Promise<T>;
136
168
  scan<T>(
137
169
  table: string,
138
170
  schema: z.ZodSchema<T>,
@@ -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
@@ -363,6 +363,7 @@ export { MongoDBClientImpl } from "./mongodb/index.js";
363
363
  export type {
364
364
  DynamoDBClient,
365
365
  DynamoDBAttributeValue,
366
+ DynamoDBScanOptions,
366
367
  } from "./dynamodb/index.js";
367
368
  export { DynamoDBClientImpl } from "./dynamodb/index.js";
368
369
 
@@ -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
@@ -257,6 +257,13 @@ All methods accept an optional `metadata` parameter as the last argument for dia
257
257
 
258
258
  ## Common Pitfalls
259
259
 
260
+ ### Streaming Is Not Supported
261
+
262
+ `apiRequest()` does not support streaming or Server-Sent Events. Do not set
263
+ `stream: true` — streaming responses fail schema validation. Every call
264
+ returns the complete response; if a UI needs real-time token streaming,
265
+ handle it at the frontend layer, not through the SDK.
266
+
260
267
  ### No Specialized Methods
261
268
 
262
269
  The OpenAI client only provides `apiRequest()`. There are no other specialized methods:
@@ -189,6 +189,13 @@ All methods accept an optional `metadata` parameter as the last argument for dia
189
189
 
190
190
  ## Common Pitfalls
191
191
 
192
+ ### Streaming Is Not Supported
193
+
194
+ `apiRequest()` does not support streaming or Server-Sent Events. Do not set
195
+ `stream: true` — streaming responses fail schema validation. Every call
196
+ returns the complete response; if a UI needs real-time token streaming,
197
+ handle it at the frontend layer, not through the SDK.
198
+
192
199
  ### No Specialized Methods
193
200
 
194
201
  ```typescript