@ftschopp/dynatable-core 1.0.0 → 1.2.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/CHANGELOG.md +19 -0
- package/README.md +528 -2
- package/dist/builders/delete/types.d.ts +6 -1
- package/dist/builders/delete/types.d.ts.map +1 -1
- package/dist/builders/get/types.d.ts +6 -1
- package/dist/builders/get/types.d.ts.map +1 -1
- package/dist/builders/put/types.d.ts +6 -1
- package/dist/builders/put/types.d.ts.map +1 -1
- package/dist/builders/query/create-query-builder.d.ts +1 -1
- package/dist/builders/query/create-query-builder.d.ts.map +1 -1
- package/dist/builders/query/types.d.ts +9 -10
- package/dist/builders/query/types.d.ts.map +1 -1
- package/dist/builders/transact-write/create-transact-write-builder.d.ts.map +1 -1
- package/dist/builders/transact-write/create-transact-write-builder.js +1 -1
- package/dist/builders/transact-write/types.d.ts +33 -6
- package/dist/builders/transact-write/types.d.ts.map +1 -1
- package/dist/builders/update/create-update-builder.d.ts.map +1 -1
- package/dist/builders/update/create-update-builder.js +30 -9
- package/dist/builders/update/types.d.ts +8 -2
- package/dist/builders/update/types.d.ts.map +1 -1
- package/dist/core/types.d.ts +96 -11
- package/dist/core/types.d.ts.map +1 -1
- package/dist/entity/create-entity-api.d.ts +15 -0
- package/dist/entity/create-entity-api.d.ts.map +1 -0
- package/dist/entity/create-entity-api.js +124 -0
- package/dist/entity/index.d.ts +12 -0
- package/dist/entity/index.d.ts.map +1 -0
- package/dist/entity/index.js +18 -0
- package/dist/entity/middleware/factories.d.ts +6 -0
- package/dist/entity/middleware/factories.d.ts.map +1 -0
- package/dist/entity/middleware/factories.js +15 -0
- package/dist/entity/middleware/types.d.ts +8 -0
- package/dist/entity/middleware/types.d.ts.map +1 -0
- package/dist/entity/middleware/types.js +2 -0
- package/dist/entity/middleware/with-middleware.d.ts +8 -0
- package/dist/entity/middleware/with-middleware.d.ts.map +1 -0
- package/dist/entity/middleware/with-middleware.js +29 -0
- package/dist/{entity.d.ts → entity/types.d.ts} +6 -17
- package/dist/entity/types.d.ts.map +1 -0
- package/dist/entity/types.js +2 -0
- package/dist/entity/validation/key-validation.d.ts +7 -0
- package/dist/entity/validation/key-validation.d.ts.map +1 -0
- package/dist/entity/validation/key-validation.js +25 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/table.d.ts +8 -1
- package/dist/table.d.ts.map +1 -1
- package/dist/table.js +1 -0
- package/dist/utils/model-utils.d.ts +7 -1
- package/dist/utils/model-utils.d.ts.map +1 -1
- package/dist/utils/model-utils.js +32 -3
- package/dist/utils/zod-utils.d.ts +3 -2
- package/dist/utils/zod-utils.d.ts.map +1 -1
- package/dist/utils/zod-utils.js +33 -11
- package/package.json +3 -2
- package/src/builders/README.md +68 -1
- package/src/builders/delete/types.ts +7 -1
- package/src/builders/get/types.ts +7 -1
- package/src/builders/put/types.ts +7 -1
- package/src/builders/query/create-query-builder.ts +4 -6
- package/src/builders/query/types.ts +9 -12
- package/src/builders/transact-write/README.md +37 -1
- package/src/builders/transact-write/create-transact-write-builder.ts +20 -14
- package/src/builders/transact-write/types.ts +44 -6
- package/src/builders/update/create-update-builder.test.ts +43 -0
- package/src/builders/update/create-update-builder.ts +47 -9
- package/src/builders/update/types.ts +9 -2
- package/src/core/types.test.ts +137 -0
- package/src/core/types.ts +97 -20
- package/src/entity/create-entity-api.ts +212 -0
- package/src/entity/index.ts +19 -0
- package/src/entity/middleware/factories.ts +15 -0
- package/src/entity/middleware/types.ts +7 -0
- package/src/entity/middleware/with-middleware.ts +37 -0
- package/src/entity/types.ts +79 -0
- package/src/entity/validation/key-validation.ts +34 -0
- package/src/index.ts +1 -0
- package/src/table.ts +10 -3
- package/src/utils/model-utils.test.ts +131 -1
- package/src/utils/model-utils.ts +35 -2
- package/src/utils/zod-utils.test.ts +97 -2
- package/src/utils/zod-utils.ts +31 -12
- package/dist/entity.d.ts.map +0 -1
- package/dist/entity.js +0 -161
- package/src/entity.ts +0 -337
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
# @ftschopp/dynatable-core [1.2.0](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.1.0...@ftschopp/dynatable-core@1.2.0) (2026-03-20)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* support nested objects and arrays schema ([c06e43e](https://github.com/ftschopp/dynatable/commit/c06e43e6b425bae09f127d2f767b5c8c62dd1142))
|
|
7
|
+
|
|
8
|
+
# @ftschopp/dynatable-core [1.1.0](https://github.com/ftschopp/dynatable/compare/@ftschopp/dynatable-core@1.0.0...@ftschopp/dynatable-core@1.1.0) (2026-01-20)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* improve types ([efdf362](https://github.com/ftschopp/dynatable/commit/efdf362cefa94f5b6cba653b3a0cc8d017da6966))
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
### Features
|
|
17
|
+
|
|
18
|
+
* improve types ([3a87c5c](https://github.com/ftschopp/dynatable/commit/3a87c5c37565f92d8c4a4790396eefa57cd9a5c5))
|
|
19
|
+
|
|
1
20
|
# @ftschopp/dynatable-core 1.0.0 (2026-01-10)
|
|
2
21
|
|
|
3
22
|
|
package/README.md
CHANGED
|
@@ -1,17 +1,543 @@
|
|
|
1
1
|
# @ftschopp/dynatable-core
|
|
2
2
|
|
|
3
|
-
A type-safe, functional programming library for AWS DynamoDB with Single Table Design support.
|
|
3
|
+
A type-safe, functional programming library for AWS DynamoDB with Single Table Design support. Built with TypeScript and designed to make DynamoDB development elegant and productive.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔐 **Type-Safe** - Full TypeScript support with end-to-end type inference
|
|
8
|
+
- 🎯 **Single Table Design** - Built-in support for DynamoDB best practices
|
|
9
|
+
- 🔄 **Functional API** - Chainable, composable operations with immutable builders
|
|
10
|
+
- ⚡️ **Auto-generated IDs** - ULID/UUID generation for unique identifiers
|
|
11
|
+
- 🕒 **Automatic Timestamps** - Auto-manage `createdAt` and `updatedAt`
|
|
12
|
+
- 🔒 **Transactions** - Atomic operations with `TransactWrite` and `TransactGet`
|
|
13
|
+
- 📦 **Batch Operations** - Efficient `BatchGet` and `BatchWrite` operations
|
|
14
|
+
- 🎨 **Query Builder** - Intuitive, type-safe API for complex queries
|
|
15
|
+
- ✅ **Validation** - Built-in Zod schema validation
|
|
16
|
+
- 🧪 **Testable** - Easy to mock and test with AWS SDK client mock support
|
|
4
17
|
|
|
5
18
|
## Installation
|
|
6
19
|
|
|
7
20
|
```bash
|
|
8
21
|
npm install @ftschopp/dynatable-core
|
|
22
|
+
# or
|
|
23
|
+
yarn add @ftschopp/dynatable-core
|
|
24
|
+
# or
|
|
25
|
+
pnpm add @ftschopp/dynatable-core
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Quick Start
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
import { Table } from '@ftschopp/dynatable-core';
|
|
32
|
+
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
33
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
34
|
+
|
|
35
|
+
// Define your schema
|
|
36
|
+
const schema = {
|
|
37
|
+
format: 'dynatable:1.0.0',
|
|
38
|
+
version: '1.0.0',
|
|
39
|
+
indexes: {
|
|
40
|
+
primary: { hash: 'PK', sort: 'SK' },
|
|
41
|
+
},
|
|
42
|
+
models: {
|
|
43
|
+
User: {
|
|
44
|
+
key: {
|
|
45
|
+
PK: { type: String, value: 'USER#${username}' },
|
|
46
|
+
SK: { type: String, value: 'USER#${username}' },
|
|
47
|
+
},
|
|
48
|
+
attributes: {
|
|
49
|
+
username: { type: String, required: true },
|
|
50
|
+
name: { type: String, required: true },
|
|
51
|
+
email: { type: String, required: true },
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
params: {
|
|
56
|
+
timestamps: true,
|
|
57
|
+
isoDates: true,
|
|
58
|
+
},
|
|
59
|
+
} as const;
|
|
60
|
+
|
|
61
|
+
// Create DynamoDB client
|
|
62
|
+
const ddbClient = new DynamoDBClient({ region: 'us-east-1' });
|
|
63
|
+
const client = DynamoDBDocumentClient.from(ddbClient);
|
|
64
|
+
|
|
65
|
+
// Create table instance
|
|
66
|
+
const table = new Table({
|
|
67
|
+
name: 'MyTable',
|
|
68
|
+
client,
|
|
69
|
+
schema,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
// Use it!
|
|
73
|
+
async function example() {
|
|
74
|
+
// Create
|
|
75
|
+
const user = await table.entities.User.put({
|
|
76
|
+
username: 'alice',
|
|
77
|
+
name: 'Alice Smith',
|
|
78
|
+
email: 'alice@example.com',
|
|
79
|
+
}).execute();
|
|
80
|
+
|
|
81
|
+
// Read
|
|
82
|
+
const retrieved = await table.entities.User.get({
|
|
83
|
+
username: 'alice',
|
|
84
|
+
}).execute();
|
|
85
|
+
|
|
86
|
+
// Update
|
|
87
|
+
await table.entities.User.update({ username: 'alice' })
|
|
88
|
+
.set('name', 'Alice Johnson')
|
|
89
|
+
.execute();
|
|
90
|
+
|
|
91
|
+
// Query
|
|
92
|
+
const users = await table.entities.User.query()
|
|
93
|
+
.where((attr, op) => op.eq(attr.username, 'alice'))
|
|
94
|
+
.execute();
|
|
95
|
+
|
|
96
|
+
// Delete
|
|
97
|
+
await table.entities.User.delete({ username: 'alice' }).execute();
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Core Concepts
|
|
102
|
+
|
|
103
|
+
### Schema Definition
|
|
104
|
+
|
|
105
|
+
Define your data models with full type inference:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
const schema = {
|
|
109
|
+
format: 'dynatable:1.0.0',
|
|
110
|
+
version: '1.0.0',
|
|
111
|
+
|
|
112
|
+
indexes: {
|
|
113
|
+
primary: { hash: 'PK', sort: 'SK' },
|
|
114
|
+
gsi1: { hash: 'GSI1PK', sort: 'GSI1SK' },
|
|
115
|
+
},
|
|
116
|
+
|
|
117
|
+
models: {
|
|
118
|
+
User: {
|
|
119
|
+
key: {
|
|
120
|
+
PK: { type: String, value: 'USER#${username}' },
|
|
121
|
+
SK: { type: String, value: 'USER#${username}' },
|
|
122
|
+
},
|
|
123
|
+
index: {
|
|
124
|
+
GSI1PK: { type: String, value: 'USER' },
|
|
125
|
+
GSI1SK: { type: String, value: 'USER#${username}' },
|
|
126
|
+
},
|
|
127
|
+
attributes: {
|
|
128
|
+
username: { type: String, required: true },
|
|
129
|
+
name: { type: String, required: true },
|
|
130
|
+
email: { type: String },
|
|
131
|
+
userId: { type: String, generate: 'ulid' },
|
|
132
|
+
followerCount: { type: Number, default: 0 },
|
|
133
|
+
// Nested object with typed schema
|
|
134
|
+
address: {
|
|
135
|
+
type: Object,
|
|
136
|
+
schema: {
|
|
137
|
+
street: { type: String },
|
|
138
|
+
city: { type: String, required: true },
|
|
139
|
+
country: { type: String, required: true },
|
|
140
|
+
},
|
|
141
|
+
},
|
|
142
|
+
// Typed array with item schema
|
|
143
|
+
tags: {
|
|
144
|
+
type: Array,
|
|
145
|
+
default: [],
|
|
146
|
+
items: { type: String },
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
params: {
|
|
153
|
+
timestamps: true, // Auto createdAt/updatedAt
|
|
154
|
+
isoDates: true, // Use ISO 8601 dates
|
|
155
|
+
cleanInternalKeys: false, // Hide PK/SK from results
|
|
156
|
+
},
|
|
157
|
+
} as const;
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Nested Objects and Arrays
|
|
161
|
+
|
|
162
|
+
Attributes support typed nested schemas for full TypeScript inference:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
Story: {
|
|
166
|
+
attributes: {
|
|
167
|
+
// Typed array of objects
|
|
168
|
+
frames: {
|
|
169
|
+
type: Array,
|
|
170
|
+
default: [],
|
|
171
|
+
items: {
|
|
172
|
+
type: Object,
|
|
173
|
+
schema: {
|
|
174
|
+
url: { type: String, required: true },
|
|
175
|
+
duration: { type: Number },
|
|
176
|
+
mediaType: { type: String },
|
|
177
|
+
},
|
|
178
|
+
},
|
|
179
|
+
},
|
|
180
|
+
// Nested object with a typed schema
|
|
181
|
+
location: {
|
|
182
|
+
type: Object,
|
|
183
|
+
schema: {
|
|
184
|
+
city: { type: String },
|
|
185
|
+
country: { type: String },
|
|
186
|
+
lat: { type: Number },
|
|
187
|
+
lng: { type: Number },
|
|
188
|
+
},
|
|
189
|
+
},
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Use `ArrayItem<T>` to extract item types from array attributes:
|
|
195
|
+
|
|
196
|
+
```typescript
|
|
197
|
+
import type { InferModelFromSchema, ArrayItem } from '@ftschopp/dynatable-core';
|
|
198
|
+
|
|
199
|
+
type StoryEntity = InferModelFromSchema<typeof schema, 'Story'>;
|
|
200
|
+
type StoryFrame = ArrayItem<StoryEntity['frames']>;
|
|
201
|
+
// → { url: string; duration?: number; mediaType?: string }
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
### Type Inference
|
|
205
|
+
|
|
206
|
+
Extract types from your schema:
|
|
207
|
+
|
|
208
|
+
```typescript
|
|
209
|
+
import type {
|
|
210
|
+
InferModel,
|
|
211
|
+
InferInput,
|
|
212
|
+
InferKeyInput,
|
|
213
|
+
InferModelFromSchema,
|
|
214
|
+
InferInputFromSchema,
|
|
215
|
+
ArrayItem,
|
|
216
|
+
} from '@ftschopp/dynatable-core';
|
|
217
|
+
|
|
218
|
+
// Preferred: infer from the full schema (timestamps included automatically)
|
|
219
|
+
type User = InferModelFromSchema<typeof schema, 'User'>;
|
|
220
|
+
// { username: string; name: string; email?: string; userId: string; followerCount: number; createdAt: string; updatedAt: string }
|
|
221
|
+
|
|
222
|
+
type UserInput = InferInputFromSchema<typeof schema, 'User'>;
|
|
223
|
+
// { username: string; name: string; email?: string; followerCount?: number }
|
|
224
|
+
|
|
225
|
+
// Full model type (deprecated — use InferModelFromSchema)
|
|
226
|
+
type UserLegacy = InferModel<typeof schema.models.User>;
|
|
227
|
+
|
|
228
|
+
// Input type (deprecated — use InferInputFromSchema)
|
|
229
|
+
type UserInputLegacy = InferInput<typeof schema.models.User>;
|
|
230
|
+
|
|
231
|
+
// Key input type (only key template variables)
|
|
232
|
+
type UserKey = InferKeyInput<typeof schema.models.User>;
|
|
233
|
+
// { username: string }
|
|
234
|
+
|
|
235
|
+
// Extract item type from an array attribute
|
|
236
|
+
type StoryEntity = InferModelFromSchema<typeof schema, 'Story'>;
|
|
237
|
+
type StoryFrame = ArrayItem<StoryEntity['frames']>;
|
|
238
|
+
// → { url: string; duration?: number; mediaType?: string }
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Builder Operations
|
|
242
|
+
|
|
243
|
+
All operations use the immutable builder pattern:
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
// GET - Retrieve item
|
|
247
|
+
const user = await table.entities.User.get({ username: 'alice' })
|
|
248
|
+
.select(['name', 'email'])
|
|
249
|
+
.consistentRead()
|
|
250
|
+
.execute();
|
|
251
|
+
|
|
252
|
+
// PUT - Insert/replace item
|
|
253
|
+
await table.entities.User.put({
|
|
254
|
+
username: 'alice',
|
|
255
|
+
name: 'Alice',
|
|
256
|
+
email: 'alice@example.com',
|
|
257
|
+
})
|
|
258
|
+
.ifNotExists()
|
|
259
|
+
.returning('ALL_OLD')
|
|
260
|
+
.execute();
|
|
261
|
+
|
|
262
|
+
// UPDATE - Modify attributes
|
|
263
|
+
await table.entities.User.update({ username: 'alice' })
|
|
264
|
+
.set('name', 'Alice Johnson')
|
|
265
|
+
.add('followerCount', 1)
|
|
266
|
+
.remove('email')
|
|
267
|
+
.returning('ALL_NEW')
|
|
268
|
+
.where((attr, op) => op.gt(attr.followerCount, 0))
|
|
269
|
+
.execute();
|
|
270
|
+
|
|
271
|
+
// DELETE - Remove item
|
|
272
|
+
await table.entities.User.delete({ username: 'alice' })
|
|
273
|
+
.returning('ALL_OLD')
|
|
274
|
+
.where((attr, op) => op.exists(attr.email))
|
|
275
|
+
.execute();
|
|
276
|
+
|
|
277
|
+
// QUERY - Query with conditions
|
|
278
|
+
const photos = await table.entities.Photo.query()
|
|
279
|
+
.where((attr, op) =>
|
|
280
|
+
op.and(
|
|
281
|
+
op.eq(attr.username, 'alice'),
|
|
282
|
+
op.gt(attr.likesCount, 10)
|
|
283
|
+
)
|
|
284
|
+
)
|
|
285
|
+
.limit(20)
|
|
286
|
+
.scanIndexForward(false)
|
|
287
|
+
.execute();
|
|
288
|
+
|
|
289
|
+
// SCAN - Full table scan with filter
|
|
290
|
+
const activeUsers = await table.entities.User.scan()
|
|
291
|
+
.where((attr, op) => op.gt(attr.followerCount, 1000))
|
|
292
|
+
.limit(50)
|
|
293
|
+
.execute();
|
|
294
|
+
|
|
295
|
+
// BATCH GET - Retrieve multiple items
|
|
296
|
+
const users = await table.entities.User.batchGet([
|
|
297
|
+
{ username: 'alice' },
|
|
298
|
+
{ username: 'bob' },
|
|
299
|
+
{ username: 'charlie' },
|
|
300
|
+
]).execute();
|
|
301
|
+
|
|
302
|
+
// BATCH WRITE - Write multiple items
|
|
303
|
+
await table.entities.User.batchWrite([
|
|
304
|
+
{ username: 'alice', name: 'Alice', email: 'alice@example.com' },
|
|
305
|
+
{ username: 'bob', name: 'Bob', email: 'bob@example.com' },
|
|
306
|
+
]).execute();
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
### Transactions
|
|
310
|
+
|
|
311
|
+
Atomic operations across multiple items:
|
|
312
|
+
|
|
313
|
+
```typescript
|
|
314
|
+
// TransactWrite - Atomic writes
|
|
315
|
+
await table.transactWrite()
|
|
316
|
+
.addPut(
|
|
317
|
+
table.entities.Like.put({
|
|
318
|
+
photoId: 'photo1',
|
|
319
|
+
likingUsername: 'alice',
|
|
320
|
+
})
|
|
321
|
+
.ifNotExists()
|
|
322
|
+
.dbParams()
|
|
323
|
+
)
|
|
324
|
+
.addUpdate(
|
|
325
|
+
table.entities.Photo.update({ photoId: 'photo1' })
|
|
326
|
+
.add('likesCount', 1)
|
|
327
|
+
.dbParams()
|
|
328
|
+
)
|
|
329
|
+
.execute();
|
|
330
|
+
|
|
331
|
+
// TransactGet - Atomic reads
|
|
332
|
+
const result = await table.transactGet()
|
|
333
|
+
.addGet(table.entities.User.get({ username: 'alice' }).dbParams())
|
|
334
|
+
.addGet(table.entities.Photo.get({ photoId: 'photo1' }).dbParams())
|
|
335
|
+
.execute();
|
|
336
|
+
|
|
337
|
+
const [user, photo] = result.items;
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
## Available Operators
|
|
341
|
+
|
|
342
|
+
Build complex conditions with type-safe operators:
|
|
343
|
+
|
|
344
|
+
### Comparison
|
|
345
|
+
- `eq(attr, value)` - Equals
|
|
346
|
+
- `ne(attr, value)` - Not equals
|
|
347
|
+
- `lt(attr, value)` - Less than
|
|
348
|
+
- `lte(attr, value)` - Less than or equal
|
|
349
|
+
- `gt(attr, value)` - Greater than
|
|
350
|
+
- `gte(attr, value)` - Greater than or equal
|
|
351
|
+
- `between(attr, low, high)` - Between values
|
|
352
|
+
|
|
353
|
+
### String
|
|
354
|
+
- `beginsWith(attr, prefix)` - Begins with prefix
|
|
355
|
+
- `contains(attr, value)` - Contains value (strings, sets, lists)
|
|
356
|
+
|
|
357
|
+
### Existence
|
|
358
|
+
- `exists(attr)` - Attribute exists
|
|
359
|
+
- `notExists(attr)` - Attribute doesn't exist
|
|
360
|
+
|
|
361
|
+
### Advanced
|
|
362
|
+
- `attributeType(attr, type)` - Check attribute type ('S', 'N', 'M', 'L', etc.)
|
|
363
|
+
- `in(attr, values[])` - Value in array
|
|
364
|
+
- `size(attr)` - Get size, returns object with `.eq()`, `.gt()`, etc.
|
|
365
|
+
|
|
366
|
+
### Logical
|
|
367
|
+
- `and(...conditions)` - Combine with AND
|
|
368
|
+
- `or(...conditions)` - Combine with OR
|
|
369
|
+
- `not(condition)` - Negate condition
|
|
370
|
+
|
|
371
|
+
### Examples
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
// Exists check
|
|
375
|
+
await table.entities.User.update({ username: 'alice' })
|
|
376
|
+
.set('email', 'alice@example.com')
|
|
377
|
+
.where((attr, op) => op.notExists(attr.email))
|
|
378
|
+
.execute();
|
|
379
|
+
|
|
380
|
+
// Contains
|
|
381
|
+
const users = await table.entities.User.query()
|
|
382
|
+
.where((attr, op) =>
|
|
383
|
+
op.and(
|
|
384
|
+
op.eq(attr.username, 'alice'),
|
|
385
|
+
op.contains(attr.tags, 'premium')
|
|
386
|
+
)
|
|
387
|
+
)
|
|
388
|
+
.execute();
|
|
389
|
+
|
|
390
|
+
// IN operator
|
|
391
|
+
const activeUsers = await table.entities.User.scan()
|
|
392
|
+
.where((attr, op) => op.in(attr.status, ['active', 'pending']))
|
|
393
|
+
.execute();
|
|
394
|
+
|
|
395
|
+
// Size function
|
|
396
|
+
const posts = await table.entities.Post.query()
|
|
397
|
+
.where((attr, op) =>
|
|
398
|
+
op.and(
|
|
399
|
+
op.eq(attr.userId, 'alice'),
|
|
400
|
+
op.size(attr.tags).gte(3)
|
|
401
|
+
)
|
|
402
|
+
)
|
|
403
|
+
.execute();
|
|
404
|
+
|
|
405
|
+
// Complex nested conditions
|
|
406
|
+
await table.entities.Photo.query()
|
|
407
|
+
.where((attr, op) =>
|
|
408
|
+
op.and(
|
|
409
|
+
op.eq(attr.username, 'alice'),
|
|
410
|
+
op.or(
|
|
411
|
+
op.gt(attr.likesCount, 100),
|
|
412
|
+
op.and(
|
|
413
|
+
op.gt(attr.commentCount, 50),
|
|
414
|
+
op.exists(attr.featured)
|
|
415
|
+
)
|
|
416
|
+
)
|
|
417
|
+
)
|
|
418
|
+
)
|
|
419
|
+
.execute();
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
## DynamoDB Logger
|
|
423
|
+
|
|
424
|
+
Debug your DynamoDB operations:
|
|
425
|
+
|
|
426
|
+
```typescript
|
|
427
|
+
import { createDynamoDBLogger } from '@ftschopp/dynatable-core';
|
|
428
|
+
|
|
429
|
+
const logger = createDynamoDBLogger({
|
|
430
|
+
enabled: true,
|
|
431
|
+
logParams: true,
|
|
432
|
+
logResponse: false,
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
const table = new Table({
|
|
436
|
+
name: 'MyTable',
|
|
437
|
+
client,
|
|
438
|
+
schema,
|
|
439
|
+
logger, // Attach logger
|
|
440
|
+
});
|
|
441
|
+
|
|
442
|
+
// All operations now logged to console
|
|
9
443
|
```
|
|
10
444
|
|
|
445
|
+
## Pagination
|
|
446
|
+
|
|
447
|
+
Built-in pagination support:
|
|
448
|
+
|
|
449
|
+
```typescript
|
|
450
|
+
// Execute with pagination
|
|
451
|
+
const page1 = await table.entities.Post.query()
|
|
452
|
+
.where((attr, op) => op.eq(attr.userId, 'alice'))
|
|
453
|
+
.limit(20)
|
|
454
|
+
.executeWithPagination();
|
|
455
|
+
|
|
456
|
+
// Get next page
|
|
457
|
+
if (page1.lastEvaluatedKey) {
|
|
458
|
+
const page2 = await table.entities.Post.query()
|
|
459
|
+
.where((attr, op) => op.eq(attr.userId, 'alice'))
|
|
460
|
+
.startFrom(page1.lastEvaluatedKey)
|
|
461
|
+
.limit(20)
|
|
462
|
+
.executeWithPagination();
|
|
463
|
+
}
|
|
464
|
+
```
|
|
465
|
+
|
|
466
|
+
## API Reference
|
|
467
|
+
|
|
468
|
+
### Core Exports
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
export {
|
|
472
|
+
Table, // Main Table class
|
|
473
|
+
type SchemaDefinition, // Schema type
|
|
474
|
+
type ModelDefinition, // Model type
|
|
475
|
+
type AttributeDefinition, // Union of all attribute types
|
|
476
|
+
type ScalarAttributeDefinition, // String/Number/Boolean/Date attribute
|
|
477
|
+
type ObjectAttributeDefinition, // Nested object attribute with schema
|
|
478
|
+
type ArrayAttributeDefinition, // Typed array attribute with items
|
|
479
|
+
type PrimaryKeyDefinition, // PK + SK key definition
|
|
480
|
+
type KeyDefinition, // Single key definition
|
|
481
|
+
type IndexDefinition, // Index (hash + optional sort)
|
|
482
|
+
type IndexesDefinition, // All table indexes
|
|
483
|
+
type SchemaParams, // Global schema params
|
|
484
|
+
type InferModel, // Infer model type (deprecated)
|
|
485
|
+
type InferInput, // Infer input type (deprecated)
|
|
486
|
+
type InferKeyInput, // Infer key type
|
|
487
|
+
type InferModelFromSchema, // Infer from full schema (preferred)
|
|
488
|
+
type InferInputFromSchema, // Infer input from full schema (preferred)
|
|
489
|
+
type TimestampFields, // createdAt/updatedAt fields type
|
|
490
|
+
type ArrayItem, // Extract item type from array attribute
|
|
491
|
+
createDynamoDBLogger, // Logger factory
|
|
492
|
+
type DynamoDBLogger, // Logger type
|
|
493
|
+
type DynamoDBLoggerConfig, // Logger config
|
|
494
|
+
};
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
### Builder Types
|
|
498
|
+
|
|
499
|
+
Each builder type is exported for advanced use cases:
|
|
500
|
+
|
|
501
|
+
- `GetBuilder`, `PutBuilder`, `UpdateBuilder`, `DeleteBuilder`
|
|
502
|
+
- `QueryBuilder`, `ScanBuilder`
|
|
503
|
+
- `BatchGetBuilder`, `BatchWriteBuilder`
|
|
504
|
+
- `TransactWriteBuilder`, `TransactGetBuilder`
|
|
505
|
+
- `Condition`, `OpBuilder`, `AttrBuilder`, `SizeRef`
|
|
506
|
+
|
|
507
|
+
## Requirements
|
|
508
|
+
|
|
509
|
+
- Node.js >= 18
|
|
510
|
+
- TypeScript >= 5.0 (recommended)
|
|
511
|
+
- AWS SDK v3 (`@aws-sdk/client-dynamodb`, `@aws-sdk/lib-dynamodb`)
|
|
512
|
+
|
|
513
|
+
## Dependencies
|
|
514
|
+
|
|
515
|
+
- `@aws-sdk/client-dynamodb` ^3.965.0
|
|
516
|
+
- `@aws-sdk/lib-dynamodb` ^3.965.0
|
|
517
|
+
- `zod` ^4.3.5 - Runtime validation
|
|
518
|
+
- `ulid` ^3.0.2 - ULID generation
|
|
519
|
+
- `ramda` ^0.32.0 - Functional utilities
|
|
520
|
+
|
|
11
521
|
## Documentation
|
|
12
522
|
|
|
13
|
-
|
|
523
|
+
For complete documentation, examples, and guides, visit the [main repository](https://github.com/ftschopp/dynatable).
|
|
524
|
+
|
|
525
|
+
## Testing
|
|
526
|
+
|
|
527
|
+
The library includes comprehensive test coverage with Jest:
|
|
528
|
+
|
|
529
|
+
```bash
|
|
530
|
+
npm test
|
|
531
|
+
```
|
|
14
532
|
|
|
15
533
|
## License
|
|
16
534
|
|
|
17
535
|
MIT
|
|
536
|
+
|
|
537
|
+
## Contributing
|
|
538
|
+
|
|
539
|
+
Contributions are welcome! Please see the [main repository](https://github.com/ftschopp/dynatable) for contribution guidelines.
|
|
540
|
+
|
|
541
|
+
## Related Packages
|
|
542
|
+
|
|
543
|
+
- [@ftschopp/dynatable-migrations](https://www.npmjs.com/package/@ftschopp/dynatable-migrations) - Database migration tool for schema evolution
|
|
@@ -1,11 +1,16 @@
|
|
|
1
|
+
import type { DeleteCommandInput } from '@aws-sdk/lib-dynamodb';
|
|
1
2
|
import { OperationBuilder } from '../shared';
|
|
2
3
|
/**
|
|
3
4
|
* Builder interface for DynamoDB DeleteItem operations
|
|
4
5
|
*/
|
|
5
|
-
export interface DeleteBuilder<Model> extends OperationBuilder<Model> {
|
|
6
|
+
export interface DeleteBuilder<Model> extends Omit<OperationBuilder<Model>, 'dbParams'> {
|
|
6
7
|
/**
|
|
7
8
|
* Configures what values should be returned after the delete operation
|
|
8
9
|
*/
|
|
9
10
|
returning(mode: 'NONE' | 'ALL_OLD'): DeleteBuilder<Model>;
|
|
11
|
+
/**
|
|
12
|
+
* Converts the builder state to DynamoDB DeleteItem parameters
|
|
13
|
+
*/
|
|
14
|
+
dbParams(): DeleteCommandInput;
|
|
10
15
|
}
|
|
11
16
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/builders/delete/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,KAAK,CAAE,SAAQ,gBAAgB,CAAC,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/builders/delete/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,uBAAuB,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,KAAK,CAAE,SAAQ,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IACrF;;OAEG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IAE1D;;OAEG;IACH,QAAQ,IAAI,kBAAkB,CAAC;CAChC"}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import type { GetCommandInput } from '@aws-sdk/lib-dynamodb';
|
|
1
2
|
import { ExecutableBuilder } from '../shared';
|
|
2
3
|
/**
|
|
3
4
|
* Builder for DynamoDB GetItem operations.
|
|
4
5
|
* Supports projection, consistent read, and inspection of the final parameters.
|
|
5
6
|
*/
|
|
6
|
-
export interface GetBuilder<KeyInput, Model> extends ExecutableBuilder<Model | undefined> {
|
|
7
|
+
export interface GetBuilder<KeyInput, Model> extends Omit<ExecutableBuilder<Model | undefined>, 'dbParams'> {
|
|
7
8
|
/**
|
|
8
9
|
* Adds a projection expression to only return specific attributes.
|
|
9
10
|
* Returns a new immutable builder.
|
|
@@ -24,5 +25,9 @@ export interface GetBuilder<KeyInput, Model> extends ExecutableBuilder<Model | u
|
|
|
24
25
|
* - NONE: No consumed capacity data returned (default)
|
|
25
26
|
*/
|
|
26
27
|
returnConsumedCapacity(mode: 'INDEXES' | 'TOTAL' | 'NONE'): GetBuilder<KeyInput, Model>;
|
|
28
|
+
/**
|
|
29
|
+
* Converts the builder state to DynamoDB GetItem parameters
|
|
30
|
+
*/
|
|
31
|
+
dbParams(): GetCommandInput;
|
|
27
32
|
}
|
|
28
33
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/builders/get/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C;;;GAGG;AACH,MAAM,WAAW,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAE,SAAQ,iBAAiB,CAAC,KAAK,GAAG,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/builders/get/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,WAAW,CAAC;AAE9C;;;GAGG;AACH,MAAM,WAAW,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAE,SAAQ,IAAI,CAAC,iBAAiB,CAAC,KAAK,GAAG,SAAS,CAAC,EAAE,UAAU,CAAC;IACzG;;;OAGG;IACH,MAAM,CAAC,KAAK,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,GAAG,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAE5D;;;OAGG;IACH,cAAc,IAAI,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAE9C;;;;;;;;OAQG;IACH,sBAAsB,CAAC,IAAI,EAAE,SAAS,GAAG,OAAO,GAAG,MAAM,GAAG,UAAU,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;IAExF;;OAEG;IACH,QAAQ,IAAI,eAAe,CAAC;CAC7B"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
import type { PutCommandInput } from '@aws-sdk/lib-dynamodb';
|
|
1
2
|
import { OperationBuilder } from '../shared';
|
|
2
3
|
/**
|
|
3
4
|
* Builder interface for DynamoDB PutItem operations
|
|
4
5
|
*/
|
|
5
|
-
export interface PutBuilder<Model> extends OperationBuilder<Model> {
|
|
6
|
+
export interface PutBuilder<Model> extends Omit<OperationBuilder<Model>, 'dbParams'> {
|
|
6
7
|
/**
|
|
7
8
|
* Adds a condition that the item must not exist (checks pk and sk)
|
|
8
9
|
*/
|
|
@@ -13,5 +14,9 @@ export interface PutBuilder<Model> extends OperationBuilder<Model> {
|
|
|
13
14
|
* - ALL_OLD: Returns the item as it was before being replaced (if it existed)
|
|
14
15
|
*/
|
|
15
16
|
returning(mode: 'NONE' | 'ALL_OLD'): PutBuilder<Model>;
|
|
17
|
+
/**
|
|
18
|
+
* Converts the builder state to DynamoDB PutItem parameters
|
|
19
|
+
*/
|
|
20
|
+
dbParams(): PutCommandInput;
|
|
16
21
|
}
|
|
17
22
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/builders/put/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,UAAU,CAAC,KAAK,CAAE,SAAQ,gBAAgB,CAAC,KAAK,CAAC;
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/builders/put/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAC7D,OAAO,EAAE,gBAAgB,EAAE,MAAM,WAAW,CAAC;AAE7C;;GAEG;AACH,MAAM,WAAW,UAAU,CAAC,KAAK,CAAE,SAAQ,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,UAAU,CAAC;IAClF;;OAEG;IACH,WAAW,IAAI,UAAU,CAAC,KAAK,CAAC,CAAC;IAEjC;;;;OAIG;IACH,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;IAEvD;;OAEG;IACH,QAAQ,IAAI,eAAe,CAAC;CAC7B"}
|
|
@@ -5,5 +5,5 @@ import { DynamoDBLogger } from '../../utils/dynamodb-logger';
|
|
|
5
5
|
/**
|
|
6
6
|
* Creates a QueryBuilder for a table
|
|
7
7
|
*/
|
|
8
|
-
export declare function createQueryBuilder<Model
|
|
8
|
+
export declare function createQueryBuilder<Model>(tableName: string, client: DynamoDBClient, model?: ModelDefinition, logger?: DynamoDBLogger): QueryBuilder<Model>;
|
|
9
9
|
//# sourceMappingURL=create-query-builder.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"create-query-builder.d.ts","sourceRoot":"","sources":["../../../src/builders/query/create-query-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAK1D,OAAO,EAAE,YAAY,EAA8B,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;
|
|
1
|
+
{"version":3,"file":"create-query-builder.d.ts","sourceRoot":"","sources":["../../../src/builders/query/create-query-builder.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAK1D,OAAO,EAAE,YAAY,EAA8B,MAAM,SAAS,CAAC;AACnE,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAiT7D;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EACtC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,cAAc,EACtB,KAAK,CAAC,EAAE,eAAe,EACvB,MAAM,CAAC,EAAE,cAAc,GACtB,YAAY,CAAC,KAAK,CAAC,CA4BrB"}
|