@superblocksteam/sdk-api 2.0.158 → 2.0.159-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/README.md +23 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/integrations/dynamodb/client.d.ts +6 -2
- package/dist/integrations/dynamodb/client.d.ts.map +1 -1
- package/dist/integrations/dynamodb/client.js +83 -10
- package/dist/integrations/dynamodb/client.js.map +1 -1
- package/dist/integrations/dynamodb/client.test.d.ts +8 -0
- package/dist/integrations/dynamodb/client.test.d.ts.map +1 -0
- package/dist/integrations/dynamodb/client.test.js +198 -0
- package/dist/integrations/dynamodb/client.test.js.map +1 -0
- package/dist/integrations/dynamodb/index.d.ts +1 -1
- package/dist/integrations/dynamodb/index.d.ts.map +1 -1
- package/dist/integrations/dynamodb/index.js.map +1 -1
- package/dist/integrations/dynamodb/types.d.ts +27 -1
- package/dist/integrations/dynamodb/types.d.ts.map +1 -1
- package/dist/integrations/index.d.ts +1 -1
- package/dist/integrations/index.d.ts.map +1 -1
- package/dist/integrations/index.js.map +1 -1
- package/dist/runtime/context.d.ts +6 -0
- package/dist/runtime/context.d.ts.map +1 -1
- package/dist/runtime/context.js +2 -1
- package/dist/runtime/context.js.map +1 -1
- package/dist/runtime/executor.d.ts +6 -0
- package/dist/runtime/executor.d.ts.map +1 -1
- package/dist/runtime/executor.js +1 -0
- package/dist/runtime/executor.js.map +1 -1
- package/dist/types.d.ts +10 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +3 -3
- package/src/index.ts +1 -0
- package/src/integrations/dynamodb/README.md +33 -12
- package/src/integrations/dynamodb/client.test.ts +254 -0
- package/src/integrations/dynamodb/client.ts +121 -16
- package/src/integrations/dynamodb/index.ts +5 -1
- package/src/integrations/dynamodb/types.ts +33 -1
- package/src/integrations/index.ts +1 -0
- package/src/runtime/context.ts +9 -0
- package/src/runtime/executor.ts +8 -0
- 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 {
|
|
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
|
-
|
|
196
|
-
|
|
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
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
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
|
-
|
|
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
|
-
|
|
339
|
+
if (
|
|
340
|
+
expressionAttributeNames &&
|
|
341
|
+
Object.keys(expressionAttributeNames).length > 0
|
|
342
|
+
) {
|
|
238
343
|
params.ExpressionAttributeNames = expressionAttributeNames;
|
|
239
344
|
}
|
|
240
345
|
|
|
@@ -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
|
|
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>,
|
package/src/runtime/context.ts
CHANGED
|
@@ -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
|
}
|
package/src/runtime/executor.ts
CHANGED
|
@@ -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
|
}
|