@superblocksteam/sdk-api 2.0.158 → 2.0.159-next.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.
Files changed (41) hide show
  1. package/README.md +23 -0
  2. package/dist/index.d.ts +1 -1
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/integrations/dynamodb/client.d.ts +6 -2
  6. package/dist/integrations/dynamodb/client.d.ts.map +1 -1
  7. package/dist/integrations/dynamodb/client.js +83 -10
  8. package/dist/integrations/dynamodb/client.js.map +1 -1
  9. package/dist/integrations/dynamodb/client.test.d.ts +8 -0
  10. package/dist/integrations/dynamodb/client.test.d.ts.map +1 -0
  11. package/dist/integrations/dynamodb/client.test.js +198 -0
  12. package/dist/integrations/dynamodb/client.test.js.map +1 -0
  13. package/dist/integrations/dynamodb/index.d.ts +1 -1
  14. package/dist/integrations/dynamodb/index.d.ts.map +1 -1
  15. package/dist/integrations/dynamodb/index.js.map +1 -1
  16. package/dist/integrations/dynamodb/types.d.ts +27 -1
  17. package/dist/integrations/dynamodb/types.d.ts.map +1 -1
  18. package/dist/integrations/index.d.ts +1 -1
  19. package/dist/integrations/index.d.ts.map +1 -1
  20. package/dist/integrations/index.js.map +1 -1
  21. package/dist/runtime/context.d.ts +6 -0
  22. package/dist/runtime/context.d.ts.map +1 -1
  23. package/dist/runtime/context.js +2 -1
  24. package/dist/runtime/context.js.map +1 -1
  25. package/dist/runtime/executor.d.ts +6 -0
  26. package/dist/runtime/executor.d.ts.map +1 -1
  27. package/dist/runtime/executor.js +1 -0
  28. package/dist/runtime/executor.js.map +1 -1
  29. package/dist/types.d.ts +10 -0
  30. package/dist/types.d.ts.map +1 -1
  31. package/package.json +3 -3
  32. package/src/index.ts +1 -0
  33. package/src/integrations/dynamodb/README.md +33 -12
  34. package/src/integrations/dynamodb/client.test.ts +254 -0
  35. package/src/integrations/dynamodb/client.ts +121 -16
  36. package/src/integrations/dynamodb/index.ts +5 -1
  37. package/src/integrations/dynamodb/types.ts +33 -1
  38. package/src/integrations/index.ts +1 -0
  39. package/src/runtime/context.ts +9 -0
  40. package/src/runtime/executor.ts +8 -0
  41. package/src/types.ts +11 -0
@@ -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>,
@@ -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
 
@@ -57,6 +57,13 @@ export interface CreateContextOptions {
57
57
  /** Environment variables */
58
58
  env: Record<string, string>;
59
59
 
60
+ /**
61
+ * Server-resolved data tag key.
62
+ *
63
+ * Optional for compatibility with execution wrappers from older agents.
64
+ */
65
+ dataTag?: string;
66
+
60
67
  /** User information from JWT */
61
68
  user: ApiUser;
62
69
 
@@ -101,6 +108,7 @@ export function createApiContext(
101
108
  executeQuery,
102
109
  executionId,
103
110
  env,
111
+ dataTag,
104
112
  user,
105
113
  logger,
106
114
  } = options;
@@ -172,6 +180,7 @@ export function createApiContext(
172
180
  >["integrations"],
173
181
  log,
174
182
  env: Object.freeze({ ...env }),
183
+ dataTag,
175
184
  user,
176
185
  };
177
186
  }
@@ -36,6 +36,13 @@ export interface ExecuteApiRequest {
36
36
  /** Environment variables available to the API */
37
37
  env: Record<string, string>;
38
38
 
39
+ /**
40
+ * Server-resolved data tag key for this execution.
41
+ *
42
+ * Optional on the low-level request for compatibility with older runtimes.
43
+ */
44
+ dataTag?: string;
45
+
39
46
  /** User information extracted from the Superblocks JWT */
40
47
  user: ApiUser;
41
48
 
@@ -162,6 +169,7 @@ export async function executeApi<TInput = unknown, TOutput = unknown>(
162
169
  executeQuery: request.executeQuery,
163
170
  executionId: request.executionId,
164
171
  env: request.env,
172
+ dataTag: request.dataTag,
165
173
  user: request.user,
166
174
  });
167
175
 
package/src/types.ts CHANGED
@@ -182,6 +182,17 @@ export interface ApiContext<
182
182
  /** Access to environment variables */
183
183
  readonly env: Readonly<Record<string, string>>;
184
184
 
185
+ /**
186
+ * Canonical key of the requested data tag validated for this execution.
187
+ *
188
+ * This identifies the integration configuration in use, not a user's
189
+ * environment entitlement. It is supplied by the Superblocks runtime and
190
+ * cannot be selected through API input. It is undefined when no data tag was
191
+ * resolved or on runtimes that predate data-tag context support;
192
+ * authorization checks must deny access in those cases.
193
+ */
194
+ readonly dataTag?: string;
195
+
185
196
  /** User information from the Superblocks JWT */
186
197
  readonly user: ApiUser;
187
198
  }