@aurios/mizzle 1.1.3 → 1.1.5
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/.turbo/turbo-build.log +7 -7
- package/README.md +138 -29
- package/dist/{chunk-NPPZW6VT.js → chunk-WTOBEKFQ.js} +1 -1
- package/dist/{db-zHIHBm1E.d.ts → db-ZX6HGN1x.d.ts} +3 -1
- package/dist/db.d.ts +1 -1
- package/dist/db.js +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/core/relations.ts +12 -0
- package/src/db.ts +12 -6
- package/test/repro-user-issue.test.ts +33 -0
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
|
|
2
|
-
> @aurios/mizzle@1.1.
|
|
2
|
+
> @aurios/mizzle@1.1.5 build /home/runner/work/mizzle/mizzle/packages/mizzle
|
|
3
3
|
> tsup
|
|
4
4
|
|
|
5
5
|
[34mCLI[39m Building entry: {"index":"src/index.ts","columns":"src/columns/index.ts","table":"src/core/table.ts","snapshot":"src/core/snapshot.ts","diff":"src/core/diff.ts","introspection":"src/core/introspection.ts","db":"src/db.ts"}
|
|
@@ -10,22 +10,22 @@
|
|
|
10
10
|
[34mCLI[39m Cleaning output folder
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[32mESM[39m [1mdist/index.js [22m[32m2.32 KB[39m
|
|
13
|
+
[32mESM[39m [1mdist/columns.js [22m[32m299.00 B[39m
|
|
13
14
|
[32mESM[39m [1mdist/table.js [22m[32m144.00 B[39m
|
|
14
15
|
[32mESM[39m [1mdist/snapshot.js [22m[32m193.00 B[39m
|
|
15
|
-
[32mESM[39m [1mdist/chunk-TOYV2M4M.js [22m[32m866.00 B[39m
|
|
16
16
|
[32mESM[39m [1mdist/diff.js [22m[32m147.00 B[39m
|
|
17
|
+
[32mESM[39m [1mdist/chunk-TOYV2M4M.js [22m[32m866.00 B[39m
|
|
17
18
|
[32mESM[39m [1mdist/chunk-AQVECMXP.js [22m[32m2.38 KB[39m
|
|
18
19
|
[32mESM[39m [1mdist/introspection.js [22m[32m1.47 KB[39m
|
|
19
20
|
[32mESM[39m [1mdist/db.js [22m[32m166.00 B[39m
|
|
20
|
-
[32mESM[39m [1mdist/chunk-
|
|
21
|
+
[32mESM[39m [1mdist/chunk-WTOBEKFQ.js [22m[32m9.74 KB[39m
|
|
21
22
|
[32mESM[39m [1mdist/transaction-RE7LXTGV.js [22m[32m193.00 B[39m
|
|
22
23
|
[32mESM[39m [1mdist/chunk-UM3YF5EC.js [22m[32m15.21 KB[39m
|
|
23
24
|
[32mESM[39m [1mdist/chunk-GPYZK4WY.js [22m[32m1.12 KB[39m
|
|
24
25
|
[32mESM[39m [1mdist/chunk-DU7UPWBW.js [22m[32m5.17 KB[39m
|
|
25
|
-
[32mESM[39m
|
|
26
|
-
[32mESM[39m ⚡️ Build success in 50ms
|
|
26
|
+
[32mESM[39m ⚡️ Build success in 76ms
|
|
27
27
|
[34mDTS[39m Build start
|
|
28
|
-
[32mDTS[39m ⚡️ Build success in
|
|
28
|
+
[32mDTS[39m ⚡️ Build success in 6614ms
|
|
29
29
|
[32mDTS[39m [1mdist/index.d.ts [22m[32m3.84 KB[39m
|
|
30
30
|
[32mDTS[39m [1mdist/diff.d.ts [22m[32m485.00 B[39m
|
|
31
31
|
[32mDTS[39m [1mdist/introspection.d.ts [22m[32m258.00 B[39m
|
|
@@ -33,5 +33,5 @@
|
|
|
33
33
|
[32mDTS[39m [1mdist/db.d.ts [22m[32m169.00 B[39m
|
|
34
34
|
[32mDTS[39m [1mdist/columns.d.ts [22m[32m250.00 B[39m
|
|
35
35
|
[32mDTS[39m [1mdist/table.d.ts [22m[32m403.00 B[39m
|
|
36
|
-
[32mDTS[39m [1mdist/db-
|
|
36
|
+
[32mDTS[39m [1mdist/db-ZX6HGN1x.d.ts [22m[32m31.83 KB[39m
|
|
37
37
|
[32mDTS[39m [1mdist/operators-BVreW0ky.d.ts [22m[32m29.24 KB[39m
|
package/README.md
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
|
-
#
|
|
1
|
+
# 🌧️ mizzle
|
|
2
2
|
|
|
3
|
+

|
|
4
|
+

|
|
5
|
+

|
|
6
|
+
|
|
7
|
+

|
|
3
8
|
A Drizzle-like ORM for DynamoDB. Mizzle provides a type-safe, fluent API for interacting with DynamoDB, supporting relational queries, batch operations, and transactions.
|
|
4
9
|
|
|
5
|
-
##
|
|
10
|
+
## Key Features
|
|
11
|
+
|
|
12
|
+
- **Type-Safe Schema Definition**: Define your tables and entities with strict TypeScript types.
|
|
13
|
+
- **Fluent Query Builder**: precise API for `insert`, `select`, `update`, and `delete` operations.
|
|
14
|
+
- **Relational Queries**: Query related entities with `db.query`.
|
|
15
|
+
- **Batch Operations**: `batchGet` and `batchWrite` support.
|
|
16
|
+
- **Transactions**: Atomic operations using `db.transaction`.
|
|
17
|
+
- **Automatic Type Inference**: `InferSelectModel` and `InferInsertModel` utilities.
|
|
18
|
+
|
|
19
|
+
> Skip this with the [way less boring docs](https://mizzle-docs.vercel.app)
|
|
20
|
+
|
|
21
|
+
## 🚀 Installation
|
|
6
22
|
|
|
7
23
|
```bash
|
|
8
24
|
npm install @aurios/mizzle
|
|
@@ -10,48 +26,141 @@ npm install @aurios/mizzle
|
|
|
10
26
|
bun add @aurios/mizzle
|
|
11
27
|
```
|
|
12
28
|
|
|
13
|
-
##
|
|
29
|
+
## Get Started
|
|
30
|
+
|
|
31
|
+
### Defining the Table
|
|
32
|
+
|
|
33
|
+
In DynamoDB, you first need a physical table. Mizzle separates the definition of the physical table structure (PK, SK, Indexes) from the logical entities that live within it and with this making your database well organized and easier to reason about.
|
|
34
|
+
|
|
35
|
+
```ts
|
|
36
|
+
import { dynamoTable, string } from "@aurios/mizzle";
|
|
37
|
+
|
|
38
|
+
// This matches your actual DynamoDB table configuration
|
|
39
|
+
export const myTable = dynamoTable("JediOrder", {
|
|
40
|
+
pk: string("pk"), // The partition key attribute name
|
|
41
|
+
sk: string("sk"), // The sort key attribute name (optional)
|
|
42
|
+
});
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
### Defining the Entity
|
|
46
|
+
|
|
47
|
+
An Entity represents your data model (e.g., an user, an item on an user). You map the Entity to a Physical Table and define how its keys are generated, so every entity looks kinda like an separated table.
|
|
48
|
+
|
|
49
|
+
```ts
|
|
50
|
+
import { dynamoEntity, string, uuid, number, enum, date, prefixKey, staticKey } from "@aurios/mizzle";
|
|
51
|
+
|
|
52
|
+
export const jedi = dynamoEntity(
|
|
53
|
+
myTable,
|
|
54
|
+
"Jedi",
|
|
55
|
+
{
|
|
56
|
+
id: uuid(), // Automatically generates a v7 UUID
|
|
57
|
+
name: string(),
|
|
58
|
+
homeworld: string()
|
|
59
|
+
},
|
|
60
|
+
(cols) => ({
|
|
61
|
+
// PK will look like "JEDI#<uuid>"
|
|
62
|
+
pk: prefixKey("JEDI#", cols.id),
|
|
63
|
+
// SK will be a static string "PROFILE"
|
|
64
|
+
sk: staticKey("PROFILE"),
|
|
65
|
+
}),
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
export const jediRank = dynamoEntity(
|
|
69
|
+
myTable,
|
|
70
|
+
'JediRank',
|
|
71
|
+
{
|
|
72
|
+
jediId: uuid(),
|
|
73
|
+
position: string().default('initiate'),
|
|
74
|
+
joinedCouncilDate: string(),
|
|
75
|
+
},
|
|
76
|
+
(cols) => ({
|
|
77
|
+
// Same as the jedi so they can be related
|
|
78
|
+
pk: prefixKey('JEDI#', cols.jediId),
|
|
79
|
+
// RANK#initiate
|
|
80
|
+
sk: prefixKey('RANK#', cols.position),
|
|
81
|
+
})
|
|
82
|
+
)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
> The UUID V7 was chosen for better sorting, since the values are time-sortable with 1 millisecond precision.
|
|
86
|
+
|
|
87
|
+
### Define the Relations
|
|
88
|
+
|
|
89
|
+
Relationships in Mizzle are established using the defineRelations function. This step creates a logical map of how your entities interact, enabling you to perform powerful relational queries—such as fetching a Jedi along with their entire rank history—in a single operation.
|
|
90
|
+
|
|
91
|
+
```ts
|
|
92
|
+
import { defineRelations } from "@aurios/mizzle";
|
|
93
|
+
import * as schema from "./schema";
|
|
94
|
+
|
|
95
|
+
export const relations = defineRelations(schema, (r) => ({
|
|
96
|
+
jedi: {
|
|
97
|
+
// A Jedi could've a lot of ranks throughout his year
|
|
98
|
+
ranks: r.many.jediRank({
|
|
99
|
+
fields: [r.jedi.id],
|
|
100
|
+
references: [r.jediRank.jediId],
|
|
101
|
+
}),
|
|
102
|
+
},
|
|
103
|
+
jediRank: {
|
|
104
|
+
// Every registry points to one Jedi only
|
|
105
|
+
member: r.one.jedi({
|
|
106
|
+
fields: [r.jediRank.jediId],
|
|
107
|
+
references: [r.jedi.id],
|
|
108
|
+
}),
|
|
109
|
+
},
|
|
110
|
+
}));
|
|
111
|
+
```
|
|
14
112
|
|
|
15
113
|
### Initialization
|
|
16
114
|
|
|
115
|
+
Initialize the mizzle client by passing it an instance of the standard AWS DynamoDBClient and the relations we just defined.
|
|
116
|
+
|
|
17
117
|
```ts
|
|
18
118
|
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
19
119
|
import { mizzle } from "@aurios/mizzle";
|
|
120
|
+
import { relations } from "./relations";
|
|
20
121
|
|
|
21
|
-
const client = new DynamoDBClient({});
|
|
22
|
-
const db = mizzle(client);
|
|
122
|
+
const client = new DynamoDBClient({ region: "us-east-1" });
|
|
123
|
+
export const db = mizzle({ client, relations });
|
|
23
124
|
```
|
|
24
125
|
|
|
25
|
-
###
|
|
126
|
+
### Query
|
|
26
127
|
|
|
27
|
-
|
|
28
|
-
- **Fluent Query Builder**: precise API for `insert`, `select`, `update`, and `delete` operations.
|
|
29
|
-
- **Relational Queries**: Query related entities with `db.query`.
|
|
30
|
-
- **Batch Operations**: `batchGet` and `batchWrite` support.
|
|
31
|
-
- **Transactions**: Atomic operations using `db.transaction`.
|
|
32
|
-
- **Automatic Type Inference**: `InferSelectModel` and `InferInsertModel` utilities.
|
|
128
|
+
Now you can use the fluent API to interact with your data in the best way possible.
|
|
33
129
|
|
|
34
|
-
|
|
130
|
+
#### Insert Data
|
|
35
131
|
|
|
36
|
-
```
|
|
37
|
-
//
|
|
38
|
-
import {
|
|
132
|
+
```typescript
|
|
133
|
+
// api/jedi/new.ts
|
|
134
|
+
import { jedi } from "$lib/schema.ts";
|
|
39
135
|
|
|
40
|
-
const
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
136
|
+
const newJedi = await db
|
|
137
|
+
.insert(jedi)
|
|
138
|
+
.values({
|
|
139
|
+
name: "Luke Skywalker",
|
|
140
|
+
homeworld: "Tatooine",
|
|
141
|
+
})
|
|
142
|
+
.returning();
|
|
44
143
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
name: string("name"),
|
|
48
|
-
email: string("email"),
|
|
49
|
-
});
|
|
144
|
+
console.log(newJedi.id); // The auto-generated UUID
|
|
145
|
+
```
|
|
50
146
|
|
|
51
|
-
|
|
52
|
-
|
|
147
|
+
#### Select Data
|
|
148
|
+
|
|
149
|
+
Mizzle intelligently routes your request to `GetItem`, `Query`, or `Scan` based on the filters you provide.
|
|
150
|
+
|
|
151
|
+
```typescript
|
|
152
|
+
// /api/jedi/get.ts
|
|
153
|
+
import { jedi } from "$lib/schema.ts";
|
|
154
|
+
import { eq } from "@aurios/mizzle";
|
|
155
|
+
|
|
156
|
+
// This will use GetItem because both PK and SK are fully resolved
|
|
157
|
+
const user = await db.select().from(jedi).where(eq(jedi.id, "some-uuid")).execute();
|
|
53
158
|
```
|
|
54
159
|
|
|
55
|
-
##
|
|
160
|
+
## Mizzling
|
|
161
|
+
|
|
162
|
+
This will work if you already has a DynamoDB table with data in it. If you want to create a new table with the schema you defined, you can use the [`mizzling`](https://www.npmjs.com/package/@aurios/mizzling) CLI.
|
|
163
|
+
|
|
164
|
+
## 📄 License
|
|
56
165
|
|
|
57
|
-
MIT
|
|
166
|
+
This project is licensed under the MIT License.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{D as Q,E as U,I as L,J as w,K as H,L as $,N as J,P as X,Q as Z,S as ee,e as P,p as q,q as G,r as z}from"./chunk-UM3YF5EC.js";import{b as D}from"./chunk-GPYZK4WY.js";import{d as E,e as d,f as
|
|
1
|
+
import{D as Q,E as U,I as L,J as w,K as H,L as $,N as J,P as X,Q as Z,S as ee,e as P,p as q,q as G,r as z}from"./chunk-UM3YF5EC.js";import{b as D}from"./chunk-GPYZK4WY.js";import{d as E,e as d,f as C,i as v}from"./chunk-DU7UPWBW.js";import{DynamoDBClient as ne}from"@aws-sdk/client-dynamodb";import{DynamoDBDocumentClient as le}from"@aws-sdk/lib-dynamodb";import{GetCommand as ie,QueryCommand as se,ScanCommand as oe}from"@aws-sdk/lib-dynamodb";var O=class{constructor(t,e){this.client=t;this.fields=e}from(t){return new b(t,this.client,this.fields)}},b=class extends w{constructor(e,i,n){super(e,i);this.fields=n}static[d.ENTITY_KIND]="SelectBase";_whereClause;_limitVal;_pageSizeVal;_consistentReadVal;_sortForward=!0;_forcedIndexName;where(e){return this._whereClause=e,this}limit(e){return this._limitVal=e,this}pageSize(e){return this._pageSizeVal=e,this}consistentRead(e=!0){return this._consistentReadVal=e,this}sort(e){return this._sortForward=e,this}index(e){return this._forcedIndexName=e,this}iterator(){return async function*(){let e,i=0;do{let n=await this.fetchPage(e);for(let s of n.items)if(yield s,i++,this._limitVal!==void 0&&i>=this._limitVal)return;e=n.lastEvaluatedKey}while(e)}.bind(this)()}async fetchPage(e){let i=this.resolveKeys(this._whereClause,void 0,this._forcedIndexName);return i.hasPartitionKey&&i.hasSortKey&&!i.indexName&&!e?{items:await this.executeGet(i.keys)}:i.hasPartitionKey||i.indexName?await this.executeQuery(i,e):await this.executeScan(e)}async execute(){let{items:e}=await this.fetchPage();return e}async executeGet(e){let i=new ie({TableName:this.tableName,Key:e,ConsistentRead:this._consistentReadVal}),n=await this.client.send(i);return n.Item?[this.mapToLogical(n.Item)]:[]}async executeQuery(e,i){let{expressionAttributeNames:n,expressionAttributeValues:s,addName:r,addValue:o}=this.createExpressionContext(),l=[],c=new Set;for(let[p,_]of Object.entries(e.keys))l.push(`${r(p)} = ${o(_)}`),c.add(p);let m=l.join(" AND "),u=this._whereClause?z(this._whereClause,r,o,c):void 0,f=new se({TableName:this.tableName,IndexName:e.indexName,KeyConditionExpression:m,FilterExpression:u||void 0,ExpressionAttributeNames:Object.keys(n).length>0?n:void 0,ExpressionAttributeValues:Object.keys(s).length>0?s:void 0,Limit:this._pageSizeVal??this._limitVal,ScanIndexForward:this._sortForward,ConsistentRead:e.indexName?void 0:this._consistentReadVal,ExclusiveStartKey:i}),T=await this.client.send(f);return{items:(T.Items||[]).map(p=>this.mapToLogical(p)),lastEvaluatedKey:T.LastEvaluatedKey}}async executeScan(e){let{expressionAttributeNames:i,expressionAttributeValues:n,addName:s,addValue:r}=this.createExpressionContext(),o=this._whereClause?z(this._whereClause,s,r):void 0,l=new oe({TableName:this.tableName,FilterExpression:o,ExpressionAttributeNames:Object.keys(i).length>0?i:void 0,ExpressionAttributeValues:Object.keys(n).length>0?n:void 0,Limit:this._pageSizeVal??this._limitVal,ConsistentRead:this._consistentReadVal,ExclusiveStartKey:e}),c=await this.client.send(l);return{items:(c.Items||[]).map(m=>this.mapToLogical(m)),lastEvaluatedKey:c.LastEvaluatedKey}}};var M=class{constructor(t){this.schema=t}parse(t,e,i={}){let n=this.schema.entities[e];if(!n)throw new Error(`Root entity ${e} not found in schema`);let s=[],r=[];for(let o of t)this.isEntity(o,n.entity)?s.push({...o}):r.push(o);for(let o of s)for(let[l,c]of Object.entries(i)){if(!c)continue;let m=n.relations[l];if(!m)continue;let u=m.config.to,f=r.filter(T=>this.isEntity(T,u));f.length>0&&(m.type==="many"?o[l]=f:m.type==="one"&&(o[l]=f[0]||null))}return s}isEntity(t,e){let i=e[d.ENTITY_STRATEGY],n=e[d.PHYSICAL_TABLE];if(!n||!n[E.PARTITION_KEY])return!1;let s=n[E.PARTITION_KEY].name,r=n[E.SORT_KEY]?.name,o=this.matchStrategy(t[s],i.pk),l=r?this.matchStrategy(t[r],i.sk):!0;return o&&l}matchStrategy(t,e){if(!e)return!0;if(t==null)return!1;let i=String(t);if(e.type==="static")return i===e.segments[0];if(e.type==="prefix"||e.type==="composite"){let n=e.segments[0];return typeof n=="string"&&i.startsWith(n)}return!1}};var k=class a{constructor(t,e,i,n){this.client=t;this.table=e;this.schema=i;this.entityName=n}async findFirst(t={}){return(await this.findMany({...t,limit:1}))[0]}async findMany(t={}){let e=new b(this.table,this.client,void 0);t.orderBy&&e.sort(t.orderBy==="asc");let i;if(t.where){let r=this.table._?.columns||this.table;typeof t.where=="function"?i=t.where(r,G):i=t.where}let n,s=L(this.table,i);if(this.schema&&this.entityName&&(t.with||t.include)&&s.hasPartitionKey&&!s.indexName){let r=this.table[d.PHYSICAL_TABLE];if(!r)throw new Error("Physical table not found for entity");let o=r[E.PARTITION_KEY].name,l=s.keys[o];e.where(P({name:o},l));let m=(await e).map(f=>v(this.table,f));n=new M(this.schema).parse(m,this.entityName,t.with||t.include),t.limit&&(n=n.slice(0,t.limit))}else t.limit&&e.limit(t.limit),i&&e.where(i),n=await e;if(this.schema&&this.entityName&&(t.with||t.include)){let r=t.with||t.include;await Promise.all(n.map(async o=>{for(let[l,c]of Object.entries(r)){let m=this.schema.entities[this.entityName];if(!m)continue;let u=m.relations[l];if(!u)continue;let f=o[l]!==void 0,T=typeof c=="object"&&(c.with||c.include);if(f&&!T)continue;let p=u.config.to,_=Object.entries(this.schema.entities).find(([R,y])=>y.entity===p)?.[0],V=v(this.table,o);if(u.config.fields&&u.config.references){let R={};u.config.fields.forEach((y,g)=>{let h=u.config.references[g];if(!h)return;let F=Object.entries(p[d.COLUMNS]).find(([K,I])=>I===h||I.name===h.name)?.[0];if(F){let K=Object.entries(this.table[d.COLUMNS]).find(([de,W])=>W===y||W.name===y.name)?.[0],I=K?o[K]??o[y.name]:o[y.name];I!==void 0&&(R[F]=I)}}),V=R}let j=L(p,void 0,V);if(j.hasPartitionKey){let R=new a(this.client,p,this.schema,_),y=[];for(let[g,h]of Object.entries(j.keys))y.push(P({name:g},h));if(y.length>0){let g=y.length===1?y[0]:q(...y),h=typeof c=="object"?c:{};u.type==="one"?o[l]=await R.findFirst({...h,where:g}):o[l]=await R.findMany({...h,where:g})}}}}))}return n}};import{BatchGetCommand as re}from"@aws-sdk/lib-dynamodb";var N=class{constructor(t){this.client=t}items(t,e){return new A(t,this.client,e)}},A=class extends w{constructor(e,i,n){super(e,i);this.keysData=n}static[d.ENTITY_KIND]="BatchGetBase";async execute(){let e=[],i=[],n=this.keysData.map(o=>this.resolveKeys(void 0,o).keys),s=0,r=5;for(;n.length>0&&s<r;){s++;let o=new re({RequestItems:{[this.tableName]:{Keys:n}}}),l=await this.client.send(o);l.Responses?.[this.tableName]&&e.push(...l.Responses[this.tableName]);let c=l.UnprocessedKeys?.[this.tableName]?.Keys;c&&c.length>0?n=c:n=[]}return n.length>0&&i.push(...n),{succeeded:e,failed:i}}};import{BatchWriteCommand as ae}from"@aws-sdk/lib-dynamodb";var B=class{constructor(t){this.client=t}operations(t,e){return new Y(t,this.client,e)}},Y=class extends w{constructor(e,i,n){super(e,i);this.ops=n}static[d.ENTITY_KIND]="BatchWriteBase";async execute(){let e=0,i=[],s=[...this.ops.map(l=>{if(l.type==="put"){let c=l.item;if(H(c)>400*1024)throw new $("Item in batch exceeds the 400KB limit.");return{PutRequest:{Item:c}}}else return{DeleteRequest:{Key:l.keys}}})],r=0,o=5;for(;s.length>0&&r<o;){r++;let l=new ae({RequestItems:{[this.tableName]:s}}),m=(await this.client.send(l)).UnprocessedItems?.[this.tableName]||[];e+=s.length-m.length,m.length>0?s=m:s=[]}return s.length>0&&(i=s),{succeededCount:e,failed:i}}};var x=class{constructor(t,e){this.type=t;this.config=e}};function st(a,t){if(a instanceof D){let i=t({one:(n,s)=>new x("one",{to:n,...s}),many:(n,s)=>new x("many",{to:n,...s})});return{entity:a,config:i,[C.RELATION_CONFIG]:!0}}else{let e=a,i=t,n={one:{},many:{}};for(let[r,o]of Object.entries(e))n.one[r]=l=>new x("one",{to:o,...l}),n.many[r]=l=>new x("many",{to:o,...l}),n[r]=o;let s=i(n);return{schema:e,definitions:s,[C.RELATION_CONFIG]:!0}}}function te(a){let t={entities:{}};if(a&&a[C.RELATION_CONFIG]&&a.definitions){let e=a;for(let[i,n]of Object.entries(e.schema))t.entities[i]={entity:n,relations:e.definitions[i]||{}};return t}for(let[e,i]of Object.entries(a))i instanceof D&&(t.entities[e]={entity:i,relations:{}});for(let[,e]of Object.entries(a))if(e&&typeof e=="object"&&e[C.RELATION_CONFIG]){let i=e;if(i.entity){let n=e,s=Object.entries(t.entities).find(([r,o])=>o.entity===n.entity);if(s){let[,r]=s;r.relations={...r.relations,...n.config}}}else if(i.definitions){let n=e;for(let[s,r]of Object.entries(n.definitions)){let o=n.schema[s],l=Object.entries(t.entities).find(([c,m])=>m.entity===o);if(l&&r){let[,c]=l;c.relations={...c.relations,...r}}}}}return t}var S=class{docClient;schema;retryConfig;query;constructor(t,e,i){this.retryConfig={maxAttempts:i?.maxAttempts??3,baseDelay:i?.baseDelay??100},this.docClient=new U(le.from(t),new Q(this.retryConfig)),e&&(this.schema=te(e)),this.query=new Proxy({},{get:(n,s)=>{if(typeof s!="string")return;if(!this.schema)throw new Error("No relations defined. Initialize mizzle with a relations object to use db.query.");let r=this.schema.entities[s];if(!r)throw new Error(`Entity ${s} not found in relations schema.`);return new k(this.docClient,r.entity,this.schema,s)}})}insert(t){return new J(t,this.docClient)}select(t){return new O(this.docClient,t)}batchGet(t,e){return new N(this.docClient).items(t,e)}batchWrite(t,e){return new B(this.docClient).operations(t,e)}_query(t){return new k(this.docClient,t)}update(t){return new X(t,this.docClient)}delete(t,e){return new Z(t,this.docClient,e)}async transaction(t,e){let i=new ee(this.docClient),n=await e(i);if(n.length===0)return;if(n.length>100)throw new Error("DynamoDB transactions are limited to 100 items.");let{TransactionExecutor:s}=await import("./transaction-RE7LXTGV.js");await new s(this.docClient).execute(t,n)}};function gt(a){return a instanceof ne?new S(a):"client"in a&&a.client instanceof ne?new S(a.client,a.relations,a.retry):"client"in a&&a.client?new S(a.client,a.relations,a.retry):new S(a)}export{O as a,b,N as c,A as d,B as e,Y as f,k as g,x as h,st as i,te as j,S as k,gt as l};
|
|
@@ -654,7 +654,9 @@ declare class RelationnalQueryBuilder<T extends Entity> {
|
|
|
654
654
|
findMany<TOptions extends RelationalQueryOptions<T>>(options?: TOptions): Promise<InferRelationalModel<T, TOptions>[]>;
|
|
655
655
|
}
|
|
656
656
|
|
|
657
|
-
type QuerySchema<TSchema extends Record<string, any>> = {
|
|
657
|
+
type QuerySchema<TSchema extends Record<string, any>> = TSchema extends MultiRelationsDefinition<infer T> ? {
|
|
658
|
+
[K in keyof T]: RelationnalQueryBuilder<T[K]>;
|
|
659
|
+
} : {
|
|
658
660
|
[K in keyof TSchema as TSchema[K] extends Entity ? K : never]: RelationnalQueryBuilder<TSchema[K] extends Entity ? TSchema[K] : never>;
|
|
659
661
|
};
|
|
660
662
|
/**
|
package/dist/db.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
import './operators-BVreW0ky.js';
|
|
2
|
-
export { i as DynamoDB, M as MizzleConfig, Q as QuerySchema, X as mizzle } from './db-
|
|
2
|
+
export { i as DynamoDB, M as MizzleConfig, Q as QuerySchema, X as mizzle } from './db-ZX6HGN1x.js';
|
|
3
3
|
import '@aws-sdk/client-dynamodb';
|
package/dist/db.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{k as a,l as b}from"./chunk-
|
|
1
|
+
import{k as a,l as b}from"./chunk-WTOBEKFQ.js";import"./chunk-UM3YF5EC.js";import"./chunk-GPYZK4WY.js";import"./chunk-DU7UPWBW.js";export{a as DynamoDB,b as mizzle};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { E as Expression } from './operators-BVreW0ky.js';
|
|
2
2
|
export { A as AnyTable, a as Assume, b as AtomicValues, B as BinaryExpression, C as ColumnType, c as ColumnsBuilder, d as Condition, e as ENTITY_SYMBOLS, f as Entity, g as EntityConfig, h as EntityWithColumns, F as FunctionExpression, I as INFER_MODE, i as IndexBuilder, j as IndexColumnConfig, k as InferInsertModel, l as InferModelFromColumns, m as InferSelectModel, n as InferSelectedModel, K as KeyStrategy, o as KeyStrategyType, L as LogicalExpression, M as MapColumnName, N as NULLS, O as ORDER, p as Operators, P as PhysicalTable, q as PhysicalTableConfig, R as RELATION_SYMBOLS, S as Simplify, r as StrategyCallback, s as StrategyResolution, T as TABLE_SYMBOLS, t as TableDefinition, U as Update, u as UpdateTableConfig, v as and, w as attributeExists, x as beginsWith, y as between, z as binary, D as binarySet, G as boolean, H as compositeKey, J as contains, Q as date, V as dynamoEntity, W as dynamoTable, X as eq, Y as getColumnBuilders, Z as getEntityColumns, _ as gsi, $ as gt, a0 as gte, a1 as inList, a2 as json, a3 as list, a4 as lsi, a5 as lt, a6 as lte, a7 as map, a8 as mapToLogical, a9 as number, aa as numberSet, ab as operators, ac as or, ad as prefixKey, ae as resolveStrategies, af as resolveTableName, ag as staticKey, ah as string, ai as stringSet, aj as uuid } from './operators-BVreW0ky.js';
|
|
3
|
-
export { A as ActionType, a as AddAction, B as BatchGetBase, b as BatchGetBuilder, c as BatchGetResult, d as BatchWriteBase, e as BatchWriteBuilder, f as BatchWriteOperation, g as BatchWriteResult, C as ConditionCheckBuilder, D as DeleteAction, h as DeleteBuilder, i as DynamoDB, E as EntityMetadata, I as IncludeOptions, j as InferRelationalModel, k as InsertBase, l as InsertBuilder, m as InternalRelationalSchema, M as MizzleConfig, n as MultiRelationsCallback, o as MultiRelationsDefinition, Q as QuerySchema, R as Relation, p as RelationConfig, q as RelationType, r as RelationalQueryOptions, s as RelationnalQueryBuilder, t as RelationsCallback, u as RelationsDefinition, v as RelationsHelpers, w as RemoveAction, x as RetryConfig, y as RetryHandler, S as SelectBase, z as SelectBuilder, F as SelectedFields, G as SetAction, T as TransactionExecutor, H as TransactionProxy, U as UpdateAction, J as UpdateBuilder, K as add, L as addToSet, N as append, O as defineRelations, P as deleteFromSet, V as extractMetadata, W as ifNotExists, X as mizzle, Y as remove } from './db-
|
|
3
|
+
export { A as ActionType, a as AddAction, B as BatchGetBase, b as BatchGetBuilder, c as BatchGetResult, d as BatchWriteBase, e as BatchWriteBuilder, f as BatchWriteOperation, g as BatchWriteResult, C as ConditionCheckBuilder, D as DeleteAction, h as DeleteBuilder, i as DynamoDB, E as EntityMetadata, I as IncludeOptions, j as InferRelationalModel, k as InsertBase, l as InsertBuilder, m as InternalRelationalSchema, M as MizzleConfig, n as MultiRelationsCallback, o as MultiRelationsDefinition, Q as QuerySchema, R as Relation, p as RelationConfig, q as RelationType, r as RelationalQueryOptions, s as RelationnalQueryBuilder, t as RelationsCallback, u as RelationsDefinition, v as RelationsHelpers, w as RemoveAction, x as RetryConfig, y as RetryHandler, S as SelectBase, z as SelectBuilder, F as SelectedFields, G as SetAction, T as TransactionExecutor, H as TransactionProxy, U as UpdateAction, J as UpdateBuilder, K as add, L as addToSet, N as append, O as defineRelations, P as deleteFromSet, V as extractMetadata, W as ifNotExists, X as mizzle, Y as remove } from './db-ZX6HGN1x.js';
|
|
4
4
|
export { SchemaChange, compareSchema } from './diff.js';
|
|
5
5
|
import '@aws-sdk/client-dynamodb';
|
|
6
6
|
import './snapshot.js';
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{a as Dr}from"./chunk-TOYV2M4M.js";import"./chunk-AQVECMXP.js";import{a as lr,b as kr,c as Ir,d as dr,e as hr,f as jr,g as qr,h as vr,i as zr,j as Ar,k as Er,l as Fr}from"./chunk-
|
|
1
|
+
import{a as Dr}from"./chunk-TOYV2M4M.js";import"./chunk-AQVECMXP.js";import{a as lr,b as kr,c as Ir,d as dr,e as hr,f as jr,g as qr,h as vr,i as zr,j as Ar,k as Er,l as Fr}from"./chunk-WTOBEKFQ.js";import{A as O,B as P,C as Q,D as fr,F as xr,G as mr,H as nr,I as ir,L as sr,M as gr,N as cr,O as ur,P as Cr,Q as ar,R as br,S as wr,T as yr,a as u,b as l,c as k,d as C,e as a,f as b,g as w,h as y,i as I,j as d,k as h,l as j,m as q,n as v,o as z,p as A,q as D,r as E,s as F,t as G,u as H,v as J,w as K,x as L,y as M,z as N}from"./chunk-UM3YF5EC.js";import{a as or,b as tr,c as er,d as pr}from"./chunk-GPYZK4WY.js";import{a as p,b as f,c as x,d as m,e as n,f as i,g as s,h as g,i as c,k as R,l as S,m as T,n as U,o as V,p as W,q as X,r as Y,s as Z,t as _,u as $,v as B,w as rr}from"./chunk-DU7UPWBW.js";var o=class{constructor(t,e){this.type=t;this.config=e}};function Gr(r,t){return new o("gsi",{pk:r,sk:t})}function Hr(r){return new o("lsi",{sk:r})}export{H as AddAction,dr as BatchGetBase,Ir as BatchGetBuilder,jr as BatchWriteBase,hr as BatchWriteBuilder,l as BinaryExpression,br as ConditionCheckBuilder,K as DeleteAction,ar as DeleteBuilder,Er as DynamoDB,n as ENTITY_SYMBOLS,tr as Entity,u as Expression,C as FunctionExpression,p as INFER_MODE,o as IndexBuilder,ur as InsertBase,cr as InsertBuilder,sr as ItemSizeExceededError,k as LogicalExpression,x as NULLS,f as ORDER,or as PhysicalTable,i as RELATION_SYMBOLS,vr as Relation,qr as RelationnalQueryBuilder,J as RemoveAction,fr as RetryHandler,kr as SelectBase,lr as SelectBuilder,G as SetAction,m as TABLE_SYMBOLS,yr as TransactionExecutor,gr as TransactionFailedError,wr as TransactionProxy,F as UpdateAction,Cr as UpdateBuilder,L as add,P as addToSet,A as and,M as append,v as attributeExists,j as beginsWith,d as between,R as binary,S as binarySet,T as boolean,E as buildExpression,Dr as compareSchema,nr as compositeKey,q as contains,U as date,zr as defineRelations,Q as deleteFromSet,er as dynamoEntity,pr as dynamoTable,a as eq,Ar as extractMetadata,rr as getColumnBuilders,s as getEntityColumns,Gr as gsi,b as gt,w as gte,N as ifNotExists,h as inList,B as json,V as list,Hr as lsi,y as lt,I as lte,W as map,c as mapToLogical,Fr as mizzle,X as number,Y as numberSet,D as operators,z as or,mr as prefixKey,O as remove,ir as resolveStrategies,g as resolveTableName,xr as staticKey,Z as string,_ as stringSet,$ as uuid};
|
package/package.json
CHANGED
package/src/core/relations.ts
CHANGED
|
@@ -238,6 +238,18 @@ export function extractMetadata(schema: Record<string, unknown>): InternalRelati
|
|
|
238
238
|
entities: {},
|
|
239
239
|
};
|
|
240
240
|
|
|
241
|
+
// If the schema itself is a MultiRelationsDefinition, unwrap it
|
|
242
|
+
if (schema && (schema as any)[RELATION_SYMBOLS.RELATION_CONFIG] && (schema as any).definitions) {
|
|
243
|
+
const multiDef = schema as unknown as MultiRelationsDefinition;
|
|
244
|
+
for (const [key, entity] of Object.entries(multiDef.schema)) {
|
|
245
|
+
metadata.entities[key] = {
|
|
246
|
+
entity,
|
|
247
|
+
relations: multiDef.definitions[key] || {},
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
return metadata;
|
|
251
|
+
}
|
|
252
|
+
|
|
241
253
|
// First pass: identify entities
|
|
242
254
|
for (const [key, value] of Object.entries(schema)) {
|
|
243
255
|
if (value instanceof Entity) {
|
package/src/db.ts
CHANGED
|
@@ -10,15 +10,21 @@ import { DeleteBuilder } from "./builders/delete";
|
|
|
10
10
|
import { BatchGetBuilder } from "./builders/batch-get";
|
|
11
11
|
import { BatchWriteBuilder, type BatchWriteOperation } from "./builders/batch-write";
|
|
12
12
|
import { TransactionProxy } from "./builders/transaction";
|
|
13
|
-
import { extractMetadata, type InternalRelationalSchema } from "./core/relations";
|
|
13
|
+
import { extractMetadata, type InternalRelationalSchema, type MultiRelationsDefinition } from "./core/relations";
|
|
14
14
|
import { RetryHandler, type RetryConfig } from "./core/retry";
|
|
15
15
|
import { MizzleClient, type IMizzleClient } from "./core/client";
|
|
16
16
|
|
|
17
|
-
export type QuerySchema<TSchema extends Record<string, any>> =
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
export type QuerySchema<TSchema extends Record<string, any>> = TSchema extends MultiRelationsDefinition<
|
|
18
|
+
infer T
|
|
19
|
+
>
|
|
20
|
+
? {
|
|
21
|
+
[K in keyof T]: RelationnalQueryBuilder<T[K]>;
|
|
22
|
+
}
|
|
23
|
+
: {
|
|
24
|
+
[K in keyof TSchema as TSchema[K] extends Entity ? K : never]: RelationnalQueryBuilder<
|
|
25
|
+
TSchema[K] extends Entity ? TSchema[K] : never
|
|
26
|
+
>;
|
|
27
|
+
};
|
|
22
28
|
|
|
23
29
|
/**
|
|
24
30
|
* DynamoDB database instance.
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
|
|
3
|
+
import { dynamoTable, dynamoEntity } from "../src/core/table";
|
|
4
|
+
import { defineRelations } from "../src/core/relations";
|
|
5
|
+
import { string, uuid } from "../src/columns";
|
|
6
|
+
import { mizzle } from "../src/db";
|
|
7
|
+
|
|
8
|
+
describe("User Issue Reproduction: MultiRelationsDefinition as schema", () => {
|
|
9
|
+
const table = dynamoTable("mizzle-test", {
|
|
10
|
+
pk: string("pk"),
|
|
11
|
+
sk: string("sk"),
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
const shoppingLists = dynamoEntity(table, "shoppingLists", {
|
|
15
|
+
id: uuid("id"),
|
|
16
|
+
name: string("name"),
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const client = new DynamoDBClient({ region: "us-east-1" });
|
|
20
|
+
|
|
21
|
+
it("should allow using MultiRelationsDefinition directly in mizzle config", () => {
|
|
22
|
+
const relations = defineRelations({ shoppingLists }, () => ({}));
|
|
23
|
+
|
|
24
|
+
const db = mizzle({
|
|
25
|
+
client,
|
|
26
|
+
relations
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
// This is the part that fails in TypeScript for the user
|
|
30
|
+
// We are testing runtime behavior here, but the types should also be checked
|
|
31
|
+
expect(db.query.shoppingLists).toBeDefined();
|
|
32
|
+
});
|
|
33
|
+
});
|