@nikovirtala/typesafe-dynamodb 0.0.1 → 0.0.2

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 (27) hide show
  1. package/README.md +46 -9
  2. package/examples/schema-validated-delete-document.ts +63 -0
  3. package/examples/schema-validated-get-document.ts +59 -0
  4. package/examples/schema-validated-put-document.ts +64 -0
  5. package/examples/schema-validated-query-document.ts +69 -0
  6. package/examples/schema-validated-scan-document.ts +57 -0
  7. package/examples/schema-validated-stream-event.ts +60 -0
  8. package/examples/schema-validated-update-document.ts +75 -0
  9. package/lib/schema-validated-delete-document-command.d.ts +6 -0
  10. package/lib/schema-validated-delete-document-command.js +10 -0
  11. package/lib/schema-validated-document-client.d.ts +35 -0
  12. package/lib/schema-validated-document-client.js +25 -0
  13. package/lib/schema-validated-get-document-command.d.ts +6 -0
  14. package/lib/schema-validated-get-document-command.js +10 -0
  15. package/lib/schema-validated-put-document-command.d.ts +6 -0
  16. package/lib/schema-validated-put-document-command.js +10 -0
  17. package/lib/schema-validated-query-document-command.d.ts +6 -0
  18. package/lib/schema-validated-query-document-command.js +10 -0
  19. package/lib/schema-validated-scan-document-command.d.ts +6 -0
  20. package/lib/schema-validated-scan-document-command.js +10 -0
  21. package/lib/schema-validated-stream-event.d.ts +4 -0
  22. package/lib/schema-validated-stream-event.js +30 -0
  23. package/lib/schema-validated-update-document-command.d.ts +6 -0
  24. package/lib/schema-validated-update-document-command.js +10 -0
  25. package/package.json +4 -1
  26. package/perf/README.md +114 -0
  27. package/perf/performance-test.ts +563 -0
package/README.md CHANGED
@@ -1,17 +1,19 @@
1
- # typesafe-dynamodb
1
+ # @nikovirtala/typesafe-dynamodb
2
2
 
3
- [![npm version](https://badge.fury.io/js/typesafe-dynamodb.svg)](https://badge.fury.io/js/typesafe-dynamodb)
3
+ [![npm version](https://badge.fury.io/js/@nikovirtala%2Ftypesafe-dynamodb.svg)](https://badge.fury.io/js/@nikovirtala%2Ftypesafe-dynamodb)
4
4
 
5
- `typesafe-dynamodb` is a type-only library which replaces the type signatures of the AWS SDK v3's DynamoDB client. It substitutes `getItem`, `putItem`, `deleteItem` and `query` API methods with type-safe alternatives that are aware of the data in your tables and also adaptive to the semantics of the API request, e.g. by validating `ExpressionAttributeNames` and `ExpressionAttributeValues` contain all the values used in a `ConditionExpression` string, or by understanding the effect of a `ProjectionExpression` on the returned data type.
5
+ `@nikovirtala/typesafe-dynamodb` is a fork of `typesafe-dynamodb` (a type-only library which replaces the type signatures of the AWS SDK v3's DynamoDB client) with schema validation based on [zod](https://zod.dev).
6
6
 
7
- The end goal is to provide types that have total understanding of the AWS DynamoDB API and enable full utilization of the TypeScript type system for modeling complex DynamoDB tables, such as the application of union types and template string literals for single-table designs.
7
+ It substitutes `getItem`, `putItem`, `deleteItem` and `query` API methods with type-safe and schema validated alternatives that are aware of the data in your tables and also adaptive to the semantics of the API request, e.g. by validating `ExpressionAttributeNames` and `ExpressionAttributeValues` contain all the values used in a `ConditionExpression` string, or by understanding the effect of a `ProjectionExpression` on the returned data type.
8
+
9
+ The end goal is to provide types and validation that have total understanding of the AWS DynamoDB API and enable full utilization of the TypeScript type system for modeling complex DynamoDB Tables, such as the application of union types and template string literals for single-table designs without forgetting runtime safety.
8
10
 
9
11
  ![typesafe putItem ConditionExpression](img/put-item-expression.gif)
10
12
 
11
13
  ## Installation
12
14
 
13
15
  ```
14
- npm install --save-dev typesafe-dynamodb
16
+ npm install --save-dev @nikovirtala/typesafe-dynamodb
15
17
  ```
16
18
 
17
19
  ## Usage
@@ -108,7 +110,7 @@ import { TypeSafeDocumentClientV3 } from "typesafe-dynamodb/lib/document-client-
108
110
  const client = new DynamoDBClient({});
109
111
 
110
112
  const docClient = DynamoDBDocumentClient.from(
111
- client
113
+ client,
112
114
  ) as TypeSafeDocumentClientV3<MyType, "key", "sort">;
113
115
  ```
114
116
 
@@ -134,7 +136,7 @@ Same for the `Item` in the response:
134
136
 
135
137
  ### Single Table Design
136
138
 
137
- Below are two `interface` declarations, representing two types of data stored in a single DynamoDB table - `User` and `Order`. Single table design in DynamoDB is achieved by creating "composite keys", e.g. `USER#${UserID}`. In TypeScript, we use template literal types to encode this in the Type System.
139
+ Below are two `interface` declarations, representing two types of data stored in a single DynamoDB Table - `User` and `Order`. Single table design in DynamoDB is achieved by creating "composite keys", e.g. `USER#${UserID}`. In TypeScript, we use template literal types to encode this in the Type System.
138
140
 
139
141
  ```ts
140
142
  interface User<UserID extends string = string> {
@@ -149,7 +151,7 @@ interface User<UserID extends string = string> {
149
151
 
150
152
  interface Order<
151
153
  UserID extends string = string,
152
- OrderID extends string = string
154
+ OrderID extends string = string,
153
155
  > {
154
156
  PK: `USER#${UserID}`;
155
157
  SK: `ORDER#${OrderID}`;
@@ -187,6 +189,41 @@ export async function handle(
187
189
 
188
190
  The event's type is derived from the data type and the the `StreamViewType`, e.g. `"NEW_IMAGE" | "OLD_IMAGE" | "KEYS_ONLY" | "NEW_AND_OLD_IMAGES"`.
189
191
 
192
+ ### Schema-Validated DynamoDBStreamEvent
193
+
194
+ Validate DynamoDB stream events at runtime using Zod schemas:
195
+
196
+ ```ts
197
+ import { z } from "zod";
198
+ import { validateStreamEvent } from "typesafe-dynamodb";
199
+ import type { DynamoDBStreamEvent } from "typesafe-dynamodb/lib/stream-event";
200
+
201
+ const UserSchema = z.object({
202
+ PK: z.string(),
203
+ SK: z.string(),
204
+ name: z.string(),
205
+ email: z.string().email(),
206
+ });
207
+
208
+ type User = z.infer<typeof UserSchema>;
209
+
210
+ export async function handle(
211
+ event: DynamoDBStreamEvent<User, "PK", "SK", "NEW_AND_OLD_IMAGES">,
212
+ ) {
213
+ try {
214
+ const validatedEvent = validateStreamEvent(event, UserSchema);
215
+ // Process validated stream records
216
+ } catch (error) {
217
+ if (error instanceof z.ZodError) {
218
+ console.error("Schema validation failed:", error.issues);
219
+ }
220
+ throw error;
221
+ }
222
+ }
223
+ ```
224
+
225
+ The `validateStreamEvent` function validates both `NewImage` and `OldImage` data against your Zod schema, ensuring runtime type safety for your stream processing logic.
226
+
190
227
  ### Filter result with ProjectionExpression
191
228
 
192
229
  The `ProjectionExpression` field is parsed and applied to filter the returned type of `getItem` and `query`.
@@ -293,4 +330,4 @@ const unmarshalled: {
293
330
  readonly list: readonly [...];
294
331
  readonly record: Unmarshall<...>;
295
332
  };
296
- ```
333
+ ```
@@ -0,0 +1,63 @@
1
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
2
+ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
3
+ import { z } from "zod";
4
+ import { SchemaValidatedDocumentClient } from "../src/schema-validated-document-client";
5
+ import { SchemaValidatedDeleteDocumentCommand } from "../src/schema-validated-delete-document-command";
6
+
7
+ const UserSchema = z.object({
8
+ pk: z.templateLiteral(["USER", "#", z.uuid()]),
9
+ sk: z.literal("METADATA"),
10
+ userId: z.string(),
11
+ email: z.string().email(),
12
+ name: z.string(),
13
+ age: z.number().min(0),
14
+ preferences: z.object({
15
+ theme: z.enum(["light", "dark"]),
16
+ notifications: z.boolean(),
17
+ }),
18
+ });
19
+
20
+ type User = z.infer<typeof UserSchema>;
21
+
22
+ const client = new DynamoDBClient({ region: "eu-west-1" });
23
+ const documentClient = DynamoDBDocumentClient.from(client);
24
+ const schemaValidatedDocumentClient = new SchemaValidatedDocumentClient(
25
+ documentClient,
26
+ );
27
+
28
+ const DeleteUserCommand = SchemaValidatedDeleteDocumentCommand<
29
+ User,
30
+ "pk",
31
+ "sk"
32
+ >(UserSchema);
33
+
34
+ async function deleteUserDocumentExample() {
35
+ try {
36
+ const result = await schemaValidatedDocumentClient.send(
37
+ new DeleteUserCommand({
38
+ TableName: "Users",
39
+ Key: {
40
+ pk: "USER#00000000-0000-0000-0000-000000000000",
41
+ sk: "METADATA",
42
+ },
43
+ ConditionExpression: "attribute_exists(pk)",
44
+ ReturnValues: "ALL_OLD",
45
+ }),
46
+ UserSchema,
47
+ );
48
+
49
+ if (result.Attributes) {
50
+ console.log("Deleted user:", result.Attributes);
51
+ return result.Attributes;
52
+ }
53
+ } catch (error) {
54
+ if (error instanceof z.ZodError) {
55
+ console.error("Schema validation failed:", error.issues);
56
+ } else {
57
+ console.error("DynamoDB error:", error);
58
+ }
59
+ throw error;
60
+ }
61
+ }
62
+
63
+ export { deleteUserDocumentExample, UserSchema };
@@ -0,0 +1,59 @@
1
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
2
+ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
3
+ import { z } from "zod";
4
+ import { SchemaValidatedDocumentClient } from "../src/schema-validated-document-client";
5
+ import { SchemaValidatedGetDocumentCommand } from "../src/schema-validated-get-document-command";
6
+
7
+ const UserSchema = z.object({
8
+ pk: z.templateLiteral(["USER", "#", z.uuid()]),
9
+ sk: z.literal("METADATA"),
10
+ userId: z.string(),
11
+ email: z.string().email(),
12
+ name: z.string(),
13
+ age: z.number().min(0),
14
+ preferences: z.object({
15
+ theme: z.enum(["light", "dark"]),
16
+ notifications: z.boolean(),
17
+ }),
18
+ });
19
+
20
+ type User = z.infer<typeof UserSchema>;
21
+
22
+ const client = new DynamoDBClient({ region: "eu-west-1" });
23
+ const documentClient = DynamoDBDocumentClient.from(client);
24
+ const schemaValidatedDocumentClient = new SchemaValidatedDocumentClient(
25
+ documentClient,
26
+ );
27
+
28
+ const GetUserCommand = SchemaValidatedGetDocumentCommand<User, "pk", "sk">(
29
+ UserSchema,
30
+ );
31
+
32
+ async function getUserDocumentExample() {
33
+ try {
34
+ const result = await schemaValidatedDocumentClient.send(
35
+ new GetUserCommand({
36
+ TableName: "Users",
37
+ Key: {
38
+ pk: "USER#00000000-0000-0000-0000-000000000000",
39
+ sk: "METADATA",
40
+ },
41
+ }),
42
+ UserSchema,
43
+ );
44
+
45
+ if (result.Item) {
46
+ console.log("Validated user:", result.Item);
47
+ return result.Item;
48
+ }
49
+ } catch (error) {
50
+ if (error instanceof z.ZodError) {
51
+ console.error("Schema validation failed:", error.issues);
52
+ } else {
53
+ console.error("DynamoDB error:", error);
54
+ }
55
+ throw error;
56
+ }
57
+ }
58
+
59
+ export { getUserDocumentExample, UserSchema };
@@ -0,0 +1,64 @@
1
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
2
+ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
3
+ import { z } from "zod";
4
+ import { SchemaValidatedDocumentClient } from "../src/schema-validated-document-client";
5
+ import { SchemaValidatedPutDocumentCommand } from "../src/schema-validated-put-document-command";
6
+
7
+ const UserSchema = z.object({
8
+ pk: z.templateLiteral(["USER", "#", z.uuid()]),
9
+ sk: z.literal("METADATA"),
10
+ userId: z.string(),
11
+ email: z.string().email(),
12
+ name: z.string(),
13
+ age: z.number().min(0),
14
+ preferences: z.object({
15
+ theme: z.enum(["light", "dark"]),
16
+ notifications: z.boolean(),
17
+ }),
18
+ });
19
+
20
+ type User = z.infer<typeof UserSchema>;
21
+
22
+ const client = new DynamoDBClient({ region: "eu-west-1" });
23
+ const documentClient = DynamoDBDocumentClient.from(client);
24
+ const schemaValidatedDocumentClient = new SchemaValidatedDocumentClient(
25
+ documentClient,
26
+ );
27
+
28
+ const PutUserCommand = SchemaValidatedPutDocumentCommand<User>(UserSchema);
29
+
30
+ async function putUserDocumentExample() {
31
+ try {
32
+ const result = await schemaValidatedDocumentClient.send(
33
+ new PutUserCommand({
34
+ TableName: "Users",
35
+ Item: {
36
+ pk: "USER#00000000-0000-0000-0000-000000000000",
37
+ sk: "METADATA",
38
+ userId: "user123",
39
+ email: "user@example.com",
40
+ name: "John Doe",
41
+ age: 30,
42
+ preferences: {
43
+ theme: "dark",
44
+ notifications: true,
45
+ },
46
+ },
47
+ ConditionExpression: "attribute_not_exists(pk)",
48
+ }),
49
+ UserSchema,
50
+ );
51
+
52
+ console.log("User created successfully:", result);
53
+ return result;
54
+ } catch (error) {
55
+ if (error instanceof z.ZodError) {
56
+ console.error("Schema validation failed:", error.issues);
57
+ } else {
58
+ console.error("DynamoDB error:", error);
59
+ }
60
+ throw error;
61
+ }
62
+ }
63
+
64
+ export { putUserDocumentExample, UserSchema };
@@ -0,0 +1,69 @@
1
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
2
+ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
3
+ import { z } from "zod";
4
+ import { SchemaValidatedDocumentClient } from "../src/schema-validated-document-client";
5
+ import { SchemaValidatedQueryDocumentCommand } from "../src/schema-validated-query-document-command";
6
+
7
+ const OrderSchema = z.object({
8
+ pk: z.templateLiteral(["USER", "#", z.uuid()]),
9
+ sk: z.templateLiteral(["ORDER", "#", z.string()]),
10
+ userId: z.string(),
11
+ orderId: z.string(),
12
+ status: z.enum(["PLACED", "SHIPPED", "DELIVERED"]),
13
+ amount: z.number().min(0),
14
+ items: z.array(
15
+ z.object({
16
+ productId: z.string(),
17
+ quantity: z.number().min(1),
18
+ price: z.number().min(0),
19
+ }),
20
+ ),
21
+ createdAt: z.string().datetime(),
22
+ });
23
+
24
+ type Order = z.infer<typeof OrderSchema>;
25
+
26
+ const client = new DynamoDBClient({ region: "eu-west-1" });
27
+ const documentClient = DynamoDBDocumentClient.from(client);
28
+ const schemaValidatedDocumentClient = new SchemaValidatedDocumentClient(
29
+ documentClient,
30
+ );
31
+
32
+ const QueryOrdersCommand =
33
+ SchemaValidatedQueryDocumentCommand<Order>(OrderSchema);
34
+
35
+ async function queryUserOrdersExample() {
36
+ try {
37
+ const result = await schemaValidatedDocumentClient.send(
38
+ new QueryOrdersCommand({
39
+ TableName: "Orders",
40
+ KeyConditionExpression:
41
+ "pk = :userId AND begins_with(sk, :orderPrefix)",
42
+ FilterExpression: "#status = :status",
43
+ ExpressionAttributeNames: {
44
+ "#status": "status",
45
+ },
46
+ ExpressionAttributeValues: {
47
+ ":userId": "USER#00000000-0000-0000-0000-000000000000",
48
+ ":orderPrefix": "ORDER#",
49
+ ":status": "SHIPPED",
50
+ },
51
+ }),
52
+ OrderSchema,
53
+ );
54
+
55
+ if (result.Items) {
56
+ console.log("Validated orders:", result.Items);
57
+ return result.Items;
58
+ }
59
+ } catch (error) {
60
+ if (error instanceof z.ZodError) {
61
+ console.error("Schema validation failed:", error.issues);
62
+ } else {
63
+ console.error("DynamoDB error:", error);
64
+ }
65
+ throw error;
66
+ }
67
+ }
68
+
69
+ export { queryUserOrdersExample, OrderSchema };
@@ -0,0 +1,57 @@
1
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
2
+ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
3
+ import { z } from "zod";
4
+ import { SchemaValidatedDocumentClient } from "../src/schema-validated-document-client";
5
+ import { SchemaValidatedScanDocumentCommand } from "../src/schema-validated-scan-document-command";
6
+
7
+ const UserSchema = z.object({
8
+ pk: z.templateLiteral(["USER", "#", z.uuid()]),
9
+ sk: z.literal("METADATA"),
10
+ userId: z.string(),
11
+ email: z.string().email(),
12
+ name: z.string(),
13
+ age: z.number().min(0),
14
+ preferences: z.object({
15
+ theme: z.enum(["light", "dark"]),
16
+ notifications: z.boolean(),
17
+ }),
18
+ });
19
+
20
+ type User = z.infer<typeof UserSchema>;
21
+
22
+ const client = new DynamoDBClient({ region: "eu-west-1" });
23
+ const documentClient = DynamoDBDocumentClient.from(client);
24
+ const schemaValidatedDocumentClient = new SchemaValidatedDocumentClient(
25
+ documentClient,
26
+ );
27
+
28
+ const ScanUsersCommand = SchemaValidatedScanDocumentCommand<User>(UserSchema);
29
+
30
+ async function scanUsersDocumentExample() {
31
+ try {
32
+ const result = await schemaValidatedDocumentClient.send(
33
+ new ScanUsersCommand({
34
+ TableName: "Users",
35
+ FilterExpression: "age > :minAge",
36
+ ExpressionAttributeValues: {
37
+ ":minAge": 18,
38
+ },
39
+ }),
40
+ UserSchema,
41
+ );
42
+
43
+ if (result.Items) {
44
+ console.log("Validated users:", result.Items);
45
+ return result.Items;
46
+ }
47
+ } catch (error) {
48
+ if (error instanceof z.ZodError) {
49
+ console.error("Schema validation failed:", error.issues);
50
+ } else {
51
+ console.error("DynamoDB error:", error);
52
+ }
53
+ throw error;
54
+ }
55
+ }
56
+
57
+ export { scanUsersDocumentExample, UserSchema };
@@ -0,0 +1,60 @@
1
+ import { z } from "zod";
2
+ import { validateStreamEvent } from "../src/schema-validated-stream-event";
3
+ import type { DynamoDBStreamEvent } from "../src/stream-event";
4
+
5
+ const UserSchema = z.object({
6
+ pk: z.templateLiteral(["USER", "#", z.string()]),
7
+ sk: z.literal("PROFILE"),
8
+ userId: z.string(),
9
+ email: z.string().email(),
10
+ name: z.string(),
11
+ age: z.number().min(0),
12
+ preferences: z.object({
13
+ theme: z.enum(["light", "dark"]),
14
+ notifications: z.boolean(),
15
+ }),
16
+ });
17
+
18
+ type User = z.infer<typeof UserSchema>;
19
+
20
+ export async function handleUserStreamEvent(
21
+ event: DynamoDBStreamEvent<User, "pk", "sk", "NEW_AND_OLD_IMAGES">,
22
+ ) {
23
+ try {
24
+ const validatedEvent = validateStreamEvent(event, UserSchema);
25
+
26
+ for (const record of validatedEvent.Records) {
27
+ console.log(`Processing ${record.eventName} event for ${record.eventID}`);
28
+
29
+ if (record.dynamodb?.NewImage) {
30
+ console.log("New user data validated successfully");
31
+ }
32
+
33
+ if (record.dynamodb?.OldImage) {
34
+ console.log("Old user data validated successfully");
35
+ }
36
+
37
+ switch (record.eventName) {
38
+ case "INSERT":
39
+ console.log("User created");
40
+ break;
41
+ case "MODIFY":
42
+ console.log("User updated");
43
+ break;
44
+ case "REMOVE":
45
+ console.log("User deleted");
46
+ break;
47
+ }
48
+ }
49
+ } catch (error) {
50
+ if (error instanceof z.ZodError) {
51
+ console.error("Schema validation failed:", error.issues);
52
+ throw new Error(`Invalid stream data: ${error.message}`);
53
+ } else {
54
+ console.error("Stream processing error:", error);
55
+ throw error;
56
+ }
57
+ }
58
+ }
59
+
60
+ export { UserSchema };
@@ -0,0 +1,75 @@
1
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
2
+ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
3
+ import { z } from "zod";
4
+ import { SchemaValidatedDocumentClient } from "../src/schema-validated-document-client";
5
+ import { SchemaValidatedUpdateDocumentCommand } from "../src/schema-validated-update-document-command";
6
+
7
+ const UserSchema = z.object({
8
+ pk: z.templateLiteral(["USER", "#", z.uuid()]),
9
+ sk: z.literal("METADATA"),
10
+ userId: z.string(),
11
+ email: z.string().email(),
12
+ name: z.string(),
13
+ age: z.number().min(0),
14
+ preferences: z.object({
15
+ theme: z.enum(["light", "dark"]),
16
+ notifications: z.boolean(),
17
+ }),
18
+ });
19
+
20
+ type User = z.infer<typeof UserSchema>;
21
+
22
+ const client = new DynamoDBClient({ region: "eu-west-1" });
23
+ const documentClient = DynamoDBDocumentClient.from(client);
24
+ const schemaValidatedDocumentClient = new SchemaValidatedDocumentClient(
25
+ documentClient,
26
+ );
27
+
28
+ const UpdateUserCommand = SchemaValidatedUpdateDocumentCommand<
29
+ User,
30
+ "pk",
31
+ "sk"
32
+ >(UserSchema);
33
+
34
+ async function updateUserDocumentExample() {
35
+ try {
36
+ const result = await schemaValidatedDocumentClient.send(
37
+ new UpdateUserCommand({
38
+ TableName: "Users",
39
+ Key: {
40
+ pk: "USER#00000000-0000-0000-0000-000000000000",
41
+ sk: "METADATA",
42
+ },
43
+ UpdateExpression:
44
+ "SET #name = :name, #age = :age, #preferences.#theme = :theme",
45
+ ExpressionAttributeNames: {
46
+ "#name": "name",
47
+ "#age": "age",
48
+ "#preferences": "preferences",
49
+ "#theme": "theme",
50
+ },
51
+ ExpressionAttributeValues: {
52
+ ":name": "John Doe",
53
+ ":age": 30,
54
+ ":theme": "dark",
55
+ },
56
+ ReturnValues: "ALL_NEW",
57
+ }),
58
+ UserSchema,
59
+ );
60
+
61
+ if (result.Attributes) {
62
+ console.log("Updated user:", result.Attributes);
63
+ return result.Attributes;
64
+ }
65
+ } catch (error) {
66
+ if (error instanceof z.ZodError) {
67
+ console.error("Schema validation failed:", error.issues);
68
+ } else {
69
+ console.error("DynamoDB error:", error);
70
+ }
71
+ throw error;
72
+ }
73
+ }
74
+
75
+ export { updateUserDocumentExample, UserSchema };
@@ -0,0 +1,6 @@
1
+ import { z } from "zod";
2
+ import type { DeleteCommand } from "./delete-item";
3
+ import type { JsonFormat } from "./json-format";
4
+ export declare function SchemaValidatedDeleteDocumentCommand<Item extends object, PartitionKey extends keyof Item, RangeKey extends keyof Item | undefined>(schema: z.ZodSchema<Item>): DeleteCommand<Item, PartitionKey, RangeKey, JsonFormat.Document> & {
5
+ _schema: z.ZodSchema<Item>;
6
+ };
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchemaValidatedDeleteDocumentCommand = SchemaValidatedDeleteDocumentCommand;
4
+ const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
5
+ function SchemaValidatedDeleteDocumentCommand(schema) {
6
+ const Command = lib_dynamodb_1.DeleteCommand;
7
+ Command._schema = schema;
8
+ return Command;
9
+ }
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hLXZhbGlkYXRlZC1kZWxldGUtZG9jdW1lbnQtY29tbWFuZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9zY2hlbWEtdmFsaWRhdGVkLWRlbGV0ZS1kb2N1bWVudC1jb21tYW5kLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBS0Esb0ZBWUM7QUFqQkQsd0RBQXdFO0FBS3hFLFNBQWdCLG9DQUFvQyxDQUtsRCxNQUF5QjtJQUl6QixNQUFNLE9BQU8sR0FBRyw0QkFBcUIsQ0FBQztJQUN0QyxPQUFPLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQztJQUN6QixPQUFPLE9BQU8sQ0FBQztBQUNqQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRGVsZXRlQ29tbWFuZCBhcyBfRGVsZXRlQ29tbWFuZCB9IGZyb20gXCJAYXdzLXNkay9saWItZHluYW1vZGJcIjtcbmltcG9ydCB7IHogfSBmcm9tIFwiem9kXCI7XG5pbXBvcnQgdHlwZSB7IERlbGV0ZUNvbW1hbmQgfSBmcm9tIFwiLi9kZWxldGUtaXRlbVwiO1xuaW1wb3J0IHR5cGUgeyBKc29uRm9ybWF0IH0gZnJvbSBcIi4vanNvbi1mb3JtYXRcIjtcblxuZXhwb3J0IGZ1bmN0aW9uIFNjaGVtYVZhbGlkYXRlZERlbGV0ZURvY3VtZW50Q29tbWFuZDxcbiAgSXRlbSBleHRlbmRzIG9iamVjdCxcbiAgUGFydGl0aW9uS2V5IGV4dGVuZHMga2V5b2YgSXRlbSxcbiAgUmFuZ2VLZXkgZXh0ZW5kcyBrZXlvZiBJdGVtIHwgdW5kZWZpbmVkLFxuPihcbiAgc2NoZW1hOiB6LlpvZFNjaGVtYTxJdGVtPixcbik6IERlbGV0ZUNvbW1hbmQ8SXRlbSwgUGFydGl0aW9uS2V5LCBSYW5nZUtleSwgSnNvbkZvcm1hdC5Eb2N1bWVudD4gJiB7XG4gIF9zY2hlbWE6IHouWm9kU2NoZW1hPEl0ZW0+O1xufSB7XG4gIGNvbnN0IENvbW1hbmQgPSBfRGVsZXRlQ29tbWFuZCBhcyBhbnk7XG4gIENvbW1hbmQuX3NjaGVtYSA9IHNjaGVtYTtcbiAgcmV0dXJuIENvbW1hbmQ7XG59XG4iXX0=
@@ -0,0 +1,35 @@
1
+ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
2
+ import { z } from "zod";
3
+ export declare class SchemaValidatedDocumentClient {
4
+ private client;
5
+ constructor(client: DynamoDBDocumentClient);
6
+ send<T, Item extends object>(command: T, schema?: z.ZodSchema<Item>): Promise<T extends {
7
+ _brand: "UpdateItemCommand";
8
+ } ? {
9
+ Attributes?: Item;
10
+ $metadata: any;
11
+ } : T extends {
12
+ _brand: "DeleteItemCommand";
13
+ } ? {
14
+ Attributes?: Item;
15
+ $metadata: any;
16
+ } : T extends {
17
+ _brand: "PutItemCommand";
18
+ } ? {
19
+ Attributes?: Item;
20
+ $metadata: any;
21
+ } : T extends {
22
+ _brand: "GetItemCommand";
23
+ } ? {
24
+ Item?: Item;
25
+ $metadata: any;
26
+ } : T extends {
27
+ _brand: "QueryCommand" | "ScanCommand";
28
+ } ? {
29
+ Items?: Item[];
30
+ $metadata: any;
31
+ Count?: number;
32
+ ScannedCount?: number;
33
+ LastEvaluatedKey?: any;
34
+ } : Awaited<ReturnType<DynamoDBDocumentClient["send"]>>>;
35
+ }
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchemaValidatedDocumentClient = void 0;
4
+ class SchemaValidatedDocumentClient {
5
+ constructor(client) {
6
+ this.client = client;
7
+ }
8
+ async send(command, schema) {
9
+ const result = await this.client.send(command);
10
+ if (schema) {
11
+ if ("Item" in result && result.Item) {
12
+ result.Item = schema.parse(result.Item);
13
+ }
14
+ if ("Attributes" in result && result.Attributes) {
15
+ result.Attributes = schema.parse(result.Attributes);
16
+ }
17
+ if ("Items" in result && result.Items) {
18
+ result.Items = result.Items.map((item) => schema.parse(item));
19
+ }
20
+ }
21
+ return result;
22
+ }
23
+ }
24
+ exports.SchemaValidatedDocumentClient = SchemaValidatedDocumentClient;
25
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hLXZhbGlkYXRlZC1kb2N1bWVudC1jbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvc2NoZW1hLXZhbGlkYXRlZC1kb2N1bWVudC1jbGllbnQudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7O0FBR0EsTUFBYSw2QkFBNkI7SUFDeEMsWUFBb0IsTUFBOEI7UUFBOUIsV0FBTSxHQUFOLE1BQU0sQ0FBd0I7SUFBRyxDQUFDO0lBRXRELEtBQUssQ0FBQyxJQUFJLENBQ1IsT0FBVSxFQUNWLE1BQTBCO1FBb0IxQixNQUFNLE1BQU0sR0FBRyxNQUFNLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLE9BQWMsQ0FBQyxDQUFDO1FBRXRELElBQUksTUFBTSxFQUFFLENBQUM7WUFDWCxJQUFJLE1BQU0sSUFBSSxNQUFNLElBQUksTUFBTSxDQUFDLElBQUksRUFBRSxDQUFDO2dCQUNuQyxNQUFjLENBQUMsSUFBSSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDO1lBQ25ELENBQUM7WUFFRCxJQUFJLFlBQVksSUFBSSxNQUFNLElBQUksTUFBTSxDQUFDLFVBQVUsRUFBRSxDQUFDO2dCQUMvQyxNQUFjLENBQUMsVUFBVSxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsTUFBTSxDQUFDLFVBQVUsQ0FBQyxDQUFDO1lBQy9ELENBQUM7WUFFRCxJQUFJLE9BQU8sSUFBSSxNQUFNLElBQUksTUFBTSxDQUFDLEtBQUssRUFBRSxDQUFDO2dCQUNyQyxNQUFjLENBQUMsS0FBSyxHQUFHLE1BQU0sQ0FBQyxLQUFLLENBQUMsR0FBRyxDQUFDLENBQUMsSUFBUyxFQUFFLEVBQUUsQ0FDckQsTUFBTSxDQUFDLEtBQUssQ0FBQyxJQUFJLENBQUMsQ0FDbkIsQ0FBQztZQUNKLENBQUM7UUFDSCxDQUFDO1FBRUQsT0FBTyxNQUFhLENBQUM7SUFDdkIsQ0FBQztDQUNGO0FBN0NELHNFQTZDQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IER5bmFtb0RCRG9jdW1lbnRDbGllbnQgfSBmcm9tIFwiQGF3cy1zZGsvbGliLWR5bmFtb2RiXCI7XG5pbXBvcnQgeyB6IH0gZnJvbSBcInpvZFwiO1xuXG5leHBvcnQgY2xhc3MgU2NoZW1hVmFsaWRhdGVkRG9jdW1lbnRDbGllbnQge1xuICBjb25zdHJ1Y3Rvcihwcml2YXRlIGNsaWVudDogRHluYW1vREJEb2N1bWVudENsaWVudCkge31cblxuICBhc3luYyBzZW5kPFQsIEl0ZW0gZXh0ZW5kcyBvYmplY3Q+KFxuICAgIGNvbW1hbmQ6IFQsXG4gICAgc2NoZW1hPzogei5ab2RTY2hlbWE8SXRlbT4sXG4gICk6IFByb21pc2U8XG4gICAgVCBleHRlbmRzIHsgX2JyYW5kOiBcIlVwZGF0ZUl0ZW1Db21tYW5kXCIgfVxuICAgICAgPyB7IEF0dHJpYnV0ZXM/OiBJdGVtOyAkbWV0YWRhdGE6IGFueSB9XG4gICAgICA6IFQgZXh0ZW5kcyB7IF9icmFuZDogXCJEZWxldGVJdGVtQ29tbWFuZFwiIH1cbiAgICAgICAgPyB7IEF0dHJpYnV0ZXM/OiBJdGVtOyAkbWV0YWRhdGE6IGFueSB9XG4gICAgICAgIDogVCBleHRlbmRzIHsgX2JyYW5kOiBcIlB1dEl0ZW1Db21tYW5kXCIgfVxuICAgICAgICAgID8geyBBdHRyaWJ1dGVzPzogSXRlbTsgJG1ldGFkYXRhOiBhbnkgfVxuICAgICAgICAgIDogVCBleHRlbmRzIHsgX2JyYW5kOiBcIkdldEl0ZW1Db21tYW5kXCIgfVxuICAgICAgICAgICAgPyB7IEl0ZW0/OiBJdGVtOyAkbWV0YWRhdGE6IGFueSB9XG4gICAgICAgICAgICA6IFQgZXh0ZW5kcyB7IF9icmFuZDogXCJRdWVyeUNvbW1hbmRcIiB8IFwiU2NhbkNvbW1hbmRcIiB9XG4gICAgICAgICAgICAgID8ge1xuICAgICAgICAgICAgICAgICAgSXRlbXM/OiBJdGVtW107XG4gICAgICAgICAgICAgICAgICAkbWV0YWRhdGE6IGFueTtcbiAgICAgICAgICAgICAgICAgIENvdW50PzogbnVtYmVyO1xuICAgICAgICAgICAgICAgICAgU2Nhbm5lZENvdW50PzogbnVtYmVyO1xuICAgICAgICAgICAgICAgICAgTGFzdEV2YWx1YXRlZEtleT86IGFueTtcbiAgICAgICAgICAgICAgICB9XG4gICAgICAgICAgICAgIDogQXdhaXRlZDxSZXR1cm5UeXBlPER5bmFtb0RCRG9jdW1lbnRDbGllbnRbXCJzZW5kXCJdPj5cbiAgPiB7XG4gICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdGhpcy5jbGllbnQuc2VuZChjb21tYW5kIGFzIGFueSk7XG5cbiAgICBpZiAoc2NoZW1hKSB7XG4gICAgICBpZiAoXCJJdGVtXCIgaW4gcmVzdWx0ICYmIHJlc3VsdC5JdGVtKSB7XG4gICAgICAgIChyZXN1bHQgYXMgYW55KS5JdGVtID0gc2NoZW1hLnBhcnNlKHJlc3VsdC5JdGVtKTtcbiAgICAgIH1cblxuICAgICAgaWYgKFwiQXR0cmlidXRlc1wiIGluIHJlc3VsdCAmJiByZXN1bHQuQXR0cmlidXRlcykge1xuICAgICAgICAocmVzdWx0IGFzIGFueSkuQXR0cmlidXRlcyA9IHNjaGVtYS5wYXJzZShyZXN1bHQuQXR0cmlidXRlcyk7XG4gICAgICB9XG5cbiAgICAgIGlmIChcIkl0ZW1zXCIgaW4gcmVzdWx0ICYmIHJlc3VsdC5JdGVtcykge1xuICAgICAgICAocmVzdWx0IGFzIGFueSkuSXRlbXMgPSByZXN1bHQuSXRlbXMubWFwKChpdGVtOiBhbnkpID0+XG4gICAgICAgICAgc2NoZW1hLnBhcnNlKGl0ZW0pLFxuICAgICAgICApO1xuICAgICAgfVxuICAgIH1cblxuICAgIHJldHVybiByZXN1bHQgYXMgYW55O1xuICB9XG59XG4iXX0=
@@ -0,0 +1,6 @@
1
+ import { z } from "zod";
2
+ import type { GetCommand } from "./get-command";
3
+ import type { JsonFormat } from "./json-format";
4
+ export declare function SchemaValidatedGetDocumentCommand<Item extends object, PartitionKey extends keyof Item, RangeKey extends keyof Item | undefined>(schema: z.ZodSchema<Item>): GetCommand<Item, PartitionKey, RangeKey, JsonFormat.Document> & {
5
+ _schema: z.ZodSchema<Item>;
6
+ };
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SchemaValidatedGetDocumentCommand = SchemaValidatedGetDocumentCommand;
4
+ const lib_dynamodb_1 = require("@aws-sdk/lib-dynamodb");
5
+ function SchemaValidatedGetDocumentCommand(schema) {
6
+ const Command = lib_dynamodb_1.GetCommand;
7
+ Command._schema = schema;
8
+ return Command;
9
+ }
10
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2NoZW1hLXZhbGlkYXRlZC1nZXQtZG9jdW1lbnQtY29tbWFuZC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9zY2hlbWEtdmFsaWRhdGVkLWdldC1kb2N1bWVudC1jb21tYW5kLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7O0FBS0EsOEVBWUM7QUFqQkQsd0RBQWtFO0FBS2xFLFNBQWdCLGlDQUFpQyxDQUsvQyxNQUF5QjtJQUl6QixNQUFNLE9BQU8sR0FBRyx5QkFBa0IsQ0FBQztJQUNuQyxPQUFPLENBQUMsT0FBTyxHQUFHLE1BQU0sQ0FBQztJQUN6QixPQUFPLE9BQU8sQ0FBQztBQUNqQixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgR2V0Q29tbWFuZCBhcyBfR2V0Q29tbWFuZCB9IGZyb20gXCJAYXdzLXNkay9saWItZHluYW1vZGJcIjtcbmltcG9ydCB7IHogfSBmcm9tIFwiem9kXCI7XG5pbXBvcnQgdHlwZSB7IEdldENvbW1hbmQgfSBmcm9tIFwiLi9nZXQtY29tbWFuZFwiO1xuaW1wb3J0IHR5cGUgeyBKc29uRm9ybWF0IH0gZnJvbSBcIi4vanNvbi1mb3JtYXRcIjtcblxuZXhwb3J0IGZ1bmN0aW9uIFNjaGVtYVZhbGlkYXRlZEdldERvY3VtZW50Q29tbWFuZDxcbiAgSXRlbSBleHRlbmRzIG9iamVjdCxcbiAgUGFydGl0aW9uS2V5IGV4dGVuZHMga2V5b2YgSXRlbSxcbiAgUmFuZ2VLZXkgZXh0ZW5kcyBrZXlvZiBJdGVtIHwgdW5kZWZpbmVkLFxuPihcbiAgc2NoZW1hOiB6LlpvZFNjaGVtYTxJdGVtPixcbik6IEdldENvbW1hbmQ8SXRlbSwgUGFydGl0aW9uS2V5LCBSYW5nZUtleSwgSnNvbkZvcm1hdC5Eb2N1bWVudD4gJiB7XG4gIF9zY2hlbWE6IHouWm9kU2NoZW1hPEl0ZW0+O1xufSB7XG4gIGNvbnN0IENvbW1hbmQgPSBfR2V0Q29tbWFuZCBhcyBhbnk7XG4gIENvbW1hbmQuX3NjaGVtYSA9IHNjaGVtYTtcbiAgcmV0dXJuIENvbW1hbmQ7XG59XG4iXX0=
@@ -0,0 +1,6 @@
1
+ import { z } from "zod";
2
+ import type { JsonFormat } from "./json-format";
3
+ import type { PutCommand } from "./put-item";
4
+ export declare function SchemaValidatedPutDocumentCommand<Item extends object>(schema: z.ZodSchema<Item>): PutCommand<Item, JsonFormat.Document> & {
5
+ _schema: z.ZodSchema<Item>;
6
+ };