betterauth-dynamodb-adapter 0.1.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/LICENSE +21 -0
- package/README.md +233 -0
- package/dist/index.cjs +1 -0
- package/dist/index.d.cts +27715 -0
- package/dist/index.d.mts +27716 -0
- package/dist/index.mjs +1 -0
- package/package.json +79 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 rokku-x
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
# betterauth-dynamodb-adapter
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/betterauth-dynamodb-adapter)
|
|
4
|
+
[](LICENSE)
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
A DynamoDB adapter for [Better Auth](https://www.better-auth.com/). Uses a single-table design with composite keys and a GSI for model-based queries.
|
|
8
|
+
|
|
9
|
+
## Features
|
|
10
|
+
|
|
11
|
+
- Single-table design — all Better Auth models stored in one DynamoDB table
|
|
12
|
+
- Composite primary keys (`_pk` / `_sk`) for direct item access
|
|
13
|
+
- GSI-based queries via a `_table` attribute for model-level scans
|
|
14
|
+
- Full filter expression support (`eq`, `ne`, `gt`, `gte`, `lt`, `lte`, `in`, `not_in`, `contains`, `starts_with`, `ends_with`)
|
|
15
|
+
- Fast path `GetItem` for single-ID lookups
|
|
16
|
+
- In-memory sorting and pagination for `findMany`
|
|
17
|
+
- Dual CJS/ESM build with full TypeScript declarations
|
|
18
|
+
|
|
19
|
+
## Installation
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install betterauth-dynamodb-adapter
|
|
23
|
+
# or
|
|
24
|
+
bun add betterauth-dynamodb-adapter
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
### Peer dependencies
|
|
28
|
+
|
|
29
|
+
You also need these installed in your project:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
npm install better-auth @aws-sdk/client-dynamodb @aws-sdk/lib-dynamodb
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## DynamoDB Table Setup
|
|
36
|
+
|
|
37
|
+
Create a DynamoDB table with the following schema:
|
|
38
|
+
|
|
39
|
+
| Attribute | Type | Role |
|
|
40
|
+
|-----------|--------|---------------|
|
|
41
|
+
| `_pk` | String | Partition key |
|
|
42
|
+
| `_sk` | String | Sort key |
|
|
43
|
+
|
|
44
|
+
Then create a Global Secondary Index:
|
|
45
|
+
|
|
46
|
+
| Index Name | Partition Key | Type |
|
|
47
|
+
|----------------|---------------|--------|
|
|
48
|
+
| `_table-index` | `_table` | String |
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
### AWS CLI
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
aws dynamodb create-table \
|
|
55
|
+
--table-name my-auth-table \
|
|
56
|
+
--attribute-definitions \
|
|
57
|
+
AttributeName=_pk,AttributeType=S \
|
|
58
|
+
AttributeName=_sk,AttributeType=S \
|
|
59
|
+
AttributeName=_table,AttributeType=S \
|
|
60
|
+
--key-schema \
|
|
61
|
+
AttributeName=_pk,KeyType=HASH \
|
|
62
|
+
AttributeName=_sk,KeyType=RANGE \
|
|
63
|
+
--global-secondary-indexes \
|
|
64
|
+
'[{
|
|
65
|
+
"IndexName": "_table-index",
|
|
66
|
+
"KeySchema": [{"AttributeName": "_table", "KeyType": "HASH"}],
|
|
67
|
+
"Projection": {"ProjectionType": "ALL"}
|
|
68
|
+
}]' \
|
|
69
|
+
--billing-mode PAY_PER_REQUEST
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### CDK
|
|
73
|
+
|
|
74
|
+
```ts
|
|
75
|
+
import { Table, AttributeType, BillingMode, ProjectionType } from "aws-cdk-lib/aws-dynamodb";
|
|
76
|
+
|
|
77
|
+
const table = new Table(this, "AuthTable", {
|
|
78
|
+
tableName: "my-auth-table",
|
|
79
|
+
partitionKey: { name: "_pk", type: AttributeType.STRING },
|
|
80
|
+
sortKey: { name: "_sk", type: AttributeType.STRING },
|
|
81
|
+
billingMode: BillingMode.PAY_PER_REQUEST,
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
table.addGlobalSecondaryIndex({
|
|
85
|
+
indexName: "_table-index",
|
|
86
|
+
partitionKey: { name: "_table", type: AttributeType.STRING },
|
|
87
|
+
projectionType: ProjectionType.ALL,
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
## Usage
|
|
92
|
+
|
|
93
|
+
> Your DynamoDB table must already exist before using this adapter. See [DynamoDB Table Setup](#dynamodb-table-setup) above.
|
|
94
|
+
|
|
95
|
+
### Basic setup
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
// auth.ts
|
|
99
|
+
import { betterAuth } from "better-auth";
|
|
100
|
+
import dynamoDBAdapter from "betterauth-dynamodb-adapter";
|
|
101
|
+
|
|
102
|
+
export const auth = betterAuth({
|
|
103
|
+
database: dynamoDBAdapter({
|
|
104
|
+
tableName: "my-auth-table",
|
|
105
|
+
region: "us-east-1",
|
|
106
|
+
}),
|
|
107
|
+
emailAndPassword: {
|
|
108
|
+
enabled: true,
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### With social providers
|
|
114
|
+
|
|
115
|
+
```ts
|
|
116
|
+
// auth.ts
|
|
117
|
+
import { betterAuth } from "better-auth";
|
|
118
|
+
import dynamoDBAdapter from "betterauth-dynamodb-adapter";
|
|
119
|
+
|
|
120
|
+
export const auth = betterAuth({
|
|
121
|
+
database: dynamoDBAdapter({
|
|
122
|
+
tableName: "my-auth-table",
|
|
123
|
+
region: "us-east-1",
|
|
124
|
+
}),
|
|
125
|
+
emailAndPassword: {
|
|
126
|
+
enabled: true,
|
|
127
|
+
},
|
|
128
|
+
socialProviders: {
|
|
129
|
+
google: {
|
|
130
|
+
clientId: process.env.GOOGLE_CLIENT_ID!,
|
|
131
|
+
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
|
|
132
|
+
},
|
|
133
|
+
github: {
|
|
134
|
+
clientId: process.env.GITHUB_CLIENT_ID!,
|
|
135
|
+
clientSecret: process.env.GITHUB_CLIENT_SECRET!,
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
});
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### With Next.js
|
|
142
|
+
|
|
143
|
+
```ts
|
|
144
|
+
// app/api/auth/[...all]/route.ts
|
|
145
|
+
import { auth } from "@/auth";
|
|
146
|
+
import { toNextJsHandler } from "better-auth/next-js";
|
|
147
|
+
|
|
148
|
+
export const { GET, POST } = toNextJsHandler(auth);
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
### Client-side
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
// lib/auth-client.ts
|
|
155
|
+
import { createAuthClient } from "better-auth/client";
|
|
156
|
+
|
|
157
|
+
export const authClient = createAuthClient();
|
|
158
|
+
|
|
159
|
+
// Sign up
|
|
160
|
+
await authClient.signUp.email({
|
|
161
|
+
email: "[email]",
|
|
162
|
+
password: "[password]",
|
|
163
|
+
name: "[name]",
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// Sign in
|
|
167
|
+
await authClient.signIn.email({
|
|
168
|
+
email: "[email]",
|
|
169
|
+
password: "[password]",
|
|
170
|
+
});
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## How It Works
|
|
174
|
+
|
|
175
|
+
### Single-table design
|
|
176
|
+
|
|
177
|
+
All Better Auth models (users, sessions, accounts, etc.) are stored in a single DynamoDB table. Each item has three internal attributes managed by the adapter:
|
|
178
|
+
|
|
179
|
+
| Attribute | Format | Purpose |
|
|
180
|
+
|-----------|----------------|---------------------------------|
|
|
181
|
+
| `_pk` | `{model}#{id}` | Partition key for direct access |
|
|
182
|
+
| `_sk` | `{model}#{id}` | Sort key (same as `_pk`) |
|
|
183
|
+
| `_table` | `{model}` | GSI partition key for queries |
|
|
184
|
+
|
|
185
|
+
These internal attributes are automatically stripped from results returned to Better Auth.
|
|
186
|
+
|
|
187
|
+
### Query strategy
|
|
188
|
+
|
|
189
|
+
- **Single ID lookup** → `GetItem` (fastest, single read unit)
|
|
190
|
+
- **Filtered queries** → GSI query on `_table-index` with DynamoDB `FilterExpression`
|
|
191
|
+
- **Sorting & pagination** → performed in-memory after query results are returned
|
|
192
|
+
|
|
193
|
+
### Supported filter operators
|
|
194
|
+
|
|
195
|
+
| Operator | DynamoDB expression |
|
|
196
|
+
|---------------|-----------------------------|
|
|
197
|
+
| `eq` | `field = value` |
|
|
198
|
+
| `ne` | `field <> value` |
|
|
199
|
+
| `gt` | `field > value` |
|
|
200
|
+
| `gte` | `field >= value` |
|
|
201
|
+
| `lt` | `field < value` |
|
|
202
|
+
| `lte` | `field <= value` |
|
|
203
|
+
| `in` | `field IN (values)` |
|
|
204
|
+
| `not_in` | `NOT field IN (values)` |
|
|
205
|
+
| `contains` | `contains(field, value)` |
|
|
206
|
+
| `starts_with` | `begins_with(field, value)` |
|
|
207
|
+
| `ends_with` | `contains(field, value)` * |
|
|
208
|
+
|
|
209
|
+
> \* DynamoDB does not natively support `ends_with`, so it falls back to `contains`.
|
|
210
|
+
|
|
211
|
+
## Configuration
|
|
212
|
+
|
|
213
|
+
```ts
|
|
214
|
+
dynamoDBAdapter({
|
|
215
|
+
tableName: string; // DynamoDB table name
|
|
216
|
+
region: string; // AWS region (e.g. "us-east-1")
|
|
217
|
+
})
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
## Adapter Capabilities
|
|
221
|
+
|
|
222
|
+
| Capability | Supported |
|
|
223
|
+
|-----------------|-----------|
|
|
224
|
+
| JSON fields | No |
|
|
225
|
+
| Date fields | No |
|
|
226
|
+
| Boolean fields | No |
|
|
227
|
+
| Numeric IDs | No |
|
|
228
|
+
|
|
229
|
+
All values are stored as DynamoDB strings/numbers. Dates should be stored as ISO strings or Unix timestamps by Better Auth before reaching the adapter.
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
[MIT](LICENSE)
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var e=(e,t)=>()=>(t||e((t={exports:{}}).exports,t),t.exports);let t=require(`better-auth/adapters`),n=require(`@aws-sdk/client-dynamodb`);var r=e((e=>{var t=class e{value;constructor(e){typeof e==`object`&&`N`in e?this.value=String(e.N):this.value=String(e);let t=typeof e.valueOf()==`number`?e.valueOf():0;if(t>2**53-1||t<-(2**53-1)||Math.abs(t)===1/0||Number.isNaN(t))throw Error(`NumberValue should not be initialized with an imprecise number=${t}. Use a string instead.`)}static from(t){return new e(t)}toAttributeValue(){return{N:this.toString()}}toBigInt(){let e=this.toString();return BigInt(e)}toString(){return String(this.value)}valueOf(){return this.toString()}};let n=(e,n)=>{if(e===void 0)throw Error(`Pass options.removeUndefinedValues=true to remove undefined values from map/array/set.`);if(e===null&&typeof e==`object`)return s();if(Array.isArray(e))return r(e,n);if(e?.constructor?.name===`Set`)return i(e,n);if(e?.constructor?.name===`Map`)return a(e,n);if(e?.constructor?.name===`Object`||!e.constructor&&typeof e==`object`)return o(e,n);if(p(e))return e.length===0&&n?.convertEmptyValues?s():c(e);if(typeof e==`boolean`||e?.constructor?.name===`Boolean`)return{BOOL:e.valueOf()};if(typeof e==`number`||e?.constructor?.name===`Number`)return f(e,n);if(e instanceof t)return e.toAttributeValue();if(typeof e==`bigint`)return u(e);if(typeof e==`string`||e?.constructor?.name===`String`)return e.length===0&&n?.convertEmptyValues?s():l(e);if(n?.convertClassInstanceToMap&&typeof e==`object`)return o(e,n);throw Error(`Unsupported type passed: ${e}. Pass options.convertClassInstanceToMap=true to marshall typeof object as map attribute.`)},r=(e,t)=>({L:e.filter(e=>typeof e!=`function`&&(!t?.removeUndefinedValues||t?.removeUndefinedValues&&e!==void 0)).map(e=>n(e,t))}),i=(e,n)=>{let r=n?.removeUndefinedValues?new Set([...e].filter(e=>e!==void 0)):e;if(!n?.removeUndefinedValues&&r.has(void 0))throw Error(`Pass options.removeUndefinedValues=true to remove undefined values from map/array/set.`);if(r.size===0){if(n?.convertEmptyValues)return s();throw Error(`Pass a non-empty set, or options.convertEmptyValues=true.`)}let i=r.values().next().value;if(i instanceof t)return{NS:Array.from(r).map(e=>e.toString())};if(typeof i==`number`)return{NS:Array.from(r).map(e=>f(e,n)).map(e=>e.N)};if(typeof i==`bigint`)return{NS:Array.from(r).map(u).map(e=>e.N)};if(typeof i==`string`)return{SS:Array.from(r).map(l).map(e=>e.S)};if(p(i))return{BS:Array.from(r).map(c).map(e=>e.B)};throw Error(`Only Number Set (NS), Binary Set (BS) or String Set (SS) are allowed.`)},a=(e,t)=>({M:(e=>{let r={};for(let[i,a]of e)typeof a!=`function`&&(a!==void 0||!t?.removeUndefinedValues)&&(r[i]=n(a,t));return r})(e)}),o=(e,t)=>({M:(e=>{let r={};for(let i in e){let a=e[i];typeof a!=`function`&&(a!==void 0||!t?.removeUndefinedValues)&&(r[i]=n(a,t))}return r})(e)}),s=()=>({NULL:!0}),c=e=>({B:e}),l=e=>({S:e.toString()}),u=e=>({N:e.toString()}),d=e=>{throw Error(`${e} Use NumberValue from @aws-sdk/lib-dynamodb.`)},f=(e,t)=>{if([NaN,1/0,-1/0].map(e=>e.toString()).includes(e.toString()))throw Error(`Special numeric value ${e.toString()} is not allowed`);return t?.allowImpreciseNumbers||(Number(e)>2**53-1?d(`Number ${e.toString()} is greater than Number.MAX_SAFE_INTEGER.`):Number(e)<-(2**53-1)&&d(`Number ${e.toString()} is lesser than Number.MIN_SAFE_INTEGER.`)),{N:e.toString()}},p=e=>e?.constructor?[`ArrayBuffer`,`Blob`,`Buffer`,`DataView`,`File`,`Int8Array`,`Uint8Array`,`Uint8ClampedArray`,`Int16Array`,`Uint16Array`,`Int32Array`,`Uint32Array`,`Float32Array`,`Float64Array`,`BigInt64Array`,`BigUint64Array`].includes(e.constructor.name):!1,m=(e,t)=>{for(let[n,r]of Object.entries(e))if(r!==void 0)switch(n){case`NULL`:return null;case`BOOL`:return!!r;case`N`:return h(r,t);case`B`:return _(r);case`S`:return g(r);case`L`:return v(r,t);case`M`:return y(r,t);case`NS`:return new Set(r.map(e=>h(e,t)));case`BS`:return new Set(r.map(_));case`SS`:return new Set(r.map(g));default:throw Error(`Unsupported type passed: ${n}`)}throw Error(`No value defined: ${JSON.stringify(e)}`)},h=(e,n)=>{if(typeof n?.wrapNumbers==`function`)return n?.wrapNumbers(e);if(n?.wrapNumbers)return t.from(e);let r=Number(e);if((r>2**53-1||r<-(2**53-1))&&![1/0,-1/0].includes(r))if(typeof BigInt==`function`)try{return BigInt(e)}catch{throw Error(`${e} can't be converted to BigInt. Set options.wrapNumbers to get string value.`)}else throw Error(`${e} is outside SAFE_INTEGER bounds. Set options.wrapNumbers to get string value.`);return r},g=e=>e,_=e=>e,v=(e,t)=>e.map(e=>m(e,t)),y=(e,t)=>Object.entries(e).reduce((e,[n,r])=>(e[n]=m(r,t),e),{});function b(e,t){let r=n(e,t),[i,a]=Object.entries(r)[0];switch(i){case`M`:case`L`:return t?.convertTopLevelContainer?r:a;default:return r}}e.marshall=b,e.unmarshall=(e,t)=>t?.convertWithoutMapWrapper?m(e,t):m({M:e},t)}))();const i=e=>{let{tableName:i,region:a}=e,o=new n.DynamoDBClient({region:a});function s(e){let{_pk:t,_sk:n,_table:r,...i}=e;return i}function c(e){let t=[],n={},r={};return e.forEach((e,i)=>{let a=`#w${i}`,o=`:w${i}`;r[a]=e.field;let s;switch(e.operator){case`ne`:n[o]=e.value,s=`${a} <> ${o}`;break;case`gt`:n[o]=e.value,s=`${a} > ${o}`;break;case`gte`:n[o]=e.value,s=`${a} >= ${o}`;break;case`lt`:n[o]=e.value,s=`${a} < ${o}`;break;case`lte`:n[o]=e.value,s=`${a} <= ${o}`;break;case`in`:case`not_in`:{let t=e.value,r=t.map((e,t)=>`:w${i}_${t}`);t.forEach((e,t)=>{n[`:w${i}_${t}`]=e});let o=`${a} IN (${r.join(`,`)})`;s=e.operator===`not_in`?`NOT ${o}`:o;break}case`contains`:n[o]=e.value,s=`contains(${a}, ${o})`;break;case`starts_with`:n[o]=e.value,s=`begins_with(${a}, ${o})`;break;case`ends_with`:n[o]=e.value,s=`contains(${a}, ${o})`;break;default:n[o]=e.value,s=`${a} = ${o}`;break}i>0&&t.push(e.connector===`OR`?`OR`:`AND`),t.push(s)}),{expression:t.join(` `),vals:n,names:r}}async function l(e,t){let a={"#_table":`_table`},l={":_table":e},u,d=a,f=l;if(t&&t.length>0){let e=c(t);u=e.expression,d={...a,...e.names},f={...l,...e.vals}}return((await o.send(new n.QueryCommand({TableName:i,IndexName:`_table-index`,KeyConditionExpression:`#_table = :_table`,FilterExpression:u,ExpressionAttributeNames:d,ExpressionAttributeValues:(0,r.marshall)(f)}))).Items||[]).map(e=>s((0,r.unmarshall)(e)))}return(0,t.createAdapterFactory)({config:{adapterId:`dynamodb`,adapterName:`DynamoDB Adapter`,supportsJSON:!1,supportsDates:!1,supportsBooleans:!1,supportsNumericIds:!1},adapter:()=>({create:async({model:e,data:t})=>{let a=t.id||crypto.randomUUID(),s={...t,id:a,_pk:`${e}#${a}`,_sk:`${e}#${a}`,_table:e};return await o.send(new n.PutItemCommand({TableName:i,Item:(0,r.marshall)(s,{removeUndefinedValues:!0})})),{...t,id:a}},findOne:async({model:e,where:t})=>{if(t.length===1&&t[0].field===`id`&&t[0].operator===`eq`){let a=t[0].value,c=await o.send(new n.GetItemCommand({TableName:i,Key:(0,r.marshall)({_pk:`${e}#${a}`,_sk:`${e}#${a}`})}));return c.Item?s((0,r.unmarshall)(c.Item)):null}return(await l(e,t))[0]||null},findMany:async({model:e,where:t,limit:n,sortBy:r,offset:i})=>{let a=await l(e,t);if(r){let e=r.direction===`desc`?-1:1;a.sort((t,n)=>t[r.field]<n[r.field]?-1*e:t[r.field]>n[r.field]?1*e:0)}return i&&(a=a.slice(i)),n&&(a=a.slice(0,n)),a},update:async({model:e,where:t,update:a})=>{let s=await l(e,t);if(s.length===0)return null;let c=s[0],u=c.id,d=Object.entries(a),f=[],p={},m={};return d.forEach(([e,t],n)=>{e===`id`||e.startsWith(`_`)||(f.push(`#u${n} = :u${n}`),m[`#u${n}`]=e,p[`:u${n}`]=t)}),f.length===0?c:(await o.send(new n.UpdateItemCommand({TableName:i,Key:(0,r.marshall)({_pk:`${e}#${u}`,_sk:`${e}#${u}`}),UpdateExpression:`SET ${f.join(`, `)}`,ExpressionAttributeNames:m,ExpressionAttributeValues:(0,r.marshall)(p,{removeUndefinedValues:!0})})),{...c,...a})},updateMany:async({model:e,where:t,update:a})=>{let s=await l(e,t),c=0;for(let t of s){let s=Object.entries(a),l=[],u={},d={};s.forEach(([e,t],n)=>{e===`id`||e.startsWith(`_`)||(l.push(`#u${n} = :u${n}`),d[`#u${n}`]=e,u[`:u${n}`]=t)}),l.length!==0&&(await o.send(new n.UpdateItemCommand({TableName:i,Key:(0,r.marshall)({_pk:`${e}#${t.id}`,_sk:`${e}#${t.id}`}),UpdateExpression:`SET ${l.join(`, `)}`,ExpressionAttributeNames:d,ExpressionAttributeValues:(0,r.marshall)(u,{removeUndefinedValues:!0})})),c++)}return c},delete:async({model:e,where:t})=>{let a=await l(e,t);a.length!==0&&await o.send(new n.DeleteItemCommand({TableName:i,Key:(0,r.marshall)({_pk:`${e}#${a[0].id}`,_sk:`${e}#${a[0].id}`})}))},deleteMany:async({model:e,where:t})=>{let a=await l(e,t);for(let t of a)await o.send(new n.DeleteItemCommand({TableName:i,Key:(0,r.marshall)({_pk:`${e}#${t.id}`,_sk:`${e}#${t.id}`})}));return a.length},count:async({model:e,where:t})=>(await l(e,t)).length})})};module.exports=i;
|