@aurios/mizzle 1.1.1 → 1.1.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.
package/README.md ADDED
@@ -0,0 +1,57 @@
1
+ # @aurios/mizzle
2
+
3
+ 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
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @aurios/mizzle
9
+ # or
10
+ bun add @aurios/mizzle
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ### Initialization
16
+
17
+ ```ts
18
+ import { DynamoDBClient } from "@aws-sdk/client-dynamodb";
19
+ import { mizzle } from "@aurios/mizzle";
20
+
21
+ const client = new DynamoDBClient({});
22
+ const db = mizzle(client);
23
+ ```
24
+
25
+ ### Key Features
26
+
27
+ - **Type-Safe Schema Definition**: Define your tables and entities with strict TypeScript types.
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.
33
+
34
+ ### Example
35
+
36
+ ```ts
37
+ // Define your schema (simplified)
38
+ import { dynamoTable, dynamoEntity, string } from "@aurios/mizzle";
39
+
40
+ const myTable = dynamoTable("my-app-table", {
41
+ pk: string("pk"),
42
+ sk: string("sk"),
43
+ });
44
+
45
+ const users = dynamoEntity(myTable, "users", {
46
+ id: string("id"),
47
+ name: string("name"),
48
+ email: string("email"),
49
+ });
50
+
51
+ // Query
52
+ const result = await db.select().from(users).where(eq(users.id, "123")).execute();
53
+ ```
54
+
55
+ ## License
56
+
57
+ MIT
package/package.json CHANGED
@@ -1,41 +1,74 @@
1
1
  {
2
- "name": "@aurios/mizzle",
3
- "version": "1.1.1",
4
- "private": false,
5
- "description": "a drizzle-like orm for dynamoDB",
6
- "type": "module",
7
- "main": "./src/index.ts",
8
- "module": "./src/index.ts",
9
- "types": "./src/index.ts",
10
- "exports": {
11
- ".": "./src/index.ts",
12
- "./columns": "./src/columns/index.ts",
13
- "./table": "./src/core/table.ts",
14
- "./snapshot": "./src/core/snapshot.ts",
15
- "./diff": "./src/core/diff.ts",
16
- "./introspection": "./src/core/introspection.ts",
17
- "./db": "./src/db.ts"
18
- },
19
- "author": {
20
- "name": "Lucas",
21
- "url": "https://github.com/realfakenerd"
22
- },
23
- "repository": {
24
- "url": "https://github.com/realfakenerd/mizzle"
25
- },
26
- "scripts": {
27
- "check": "tsc --noEmit",
28
- "build": "tsup",
29
- "lint": "eslint \"src/**/*.ts\""
30
- },
31
- "dependencies": {
32
- "@aws-sdk/client-dynamodb": "3.962.0",
33
- "@aws-sdk/lib-dynamodb": "3.962.0"
34
- },
35
- "devDependencies": {
36
- "@mizzle/eslint-config": "workspace:*",
37
- "@mizzle/shared": "workspace:*",
38
- "@mizzle/tsconfig": "workspace:*",
39
- "tsup": "^8.5.1"
40
- }
2
+ "name": "@aurios/mizzle",
3
+ "version": "1.1.2",
4
+ "description": "a drizzle-like orm for dynamoDB",
5
+ "type": "module",
6
+ "homepage": "https://mizzle-docs.vercel.app",
7
+ "main": "./dist/index.js",
8
+ "module": "./dist/index.js",
9
+ "types": "./dist/index.d.ts",
10
+ "exports": {
11
+ ".": {
12
+ "types": "./dist/index.d.ts",
13
+ "import": "./dist/index.js",
14
+ "development": "./src/index.ts"
15
+ },
16
+ "./columns": {
17
+ "types": "./dist/columns.d.ts",
18
+ "import": "./dist/columns.js",
19
+ "development": "./src/columns/index.ts"
20
+ },
21
+ "./table": {
22
+ "types": "./dist/table.d.ts",
23
+ "import": "./dist/table.js",
24
+ "development": "./src/core/table.ts"
25
+ },
26
+ "./snapshot": {
27
+ "types": "./dist/snapshot.d.ts",
28
+ "import": "./dist/snapshot.js",
29
+ "development": "./src/core/snapshot.ts"
30
+ },
31
+ "./diff": {
32
+ "types": "./dist/diff.d.ts",
33
+ "import": "./dist/diff.js",
34
+ "development": "./src/core/diff.ts"
35
+ },
36
+ "./introspection": {
37
+ "types": "./dist/introspection.d.ts",
38
+ "import": "./dist/introspection.js",
39
+ "development": "./src/core/introspection.ts"
40
+ },
41
+ "./db": {
42
+ "types": "./dist/db.d.ts",
43
+ "import": "./dist/db.js",
44
+ "development": "./src/db.ts"
45
+ }
46
+ },
47
+ "author": {
48
+ "name": "Lucas",
49
+ "url": "https://github.com/realfakenerd"
50
+ },
51
+ "repository": {
52
+ "url": "git+https://github.com/realfakenerd/mizzle",
53
+ "type": "git",
54
+ "directory": "packages/mizzle"
55
+ },
56
+ "bugs": {
57
+ "url": "https://github.com/realfakenerd/mizzle/issues"
58
+ },
59
+ "scripts": {
60
+ "check": "tsc --noEmit",
61
+ "build": "tsup",
62
+ "lint": "bunx oxlint --type-aware src",
63
+ "prepublishOnly": "bun run build"
64
+ },
65
+ "dependencies": {
66
+ "@aws-sdk/client-dynamodb": "3.962.0",
67
+ "@aws-sdk/lib-dynamodb": "3.962.0"
68
+ },
69
+ "devDependencies": {
70
+ "@mizzle/shared": "workspace:*",
71
+ "@mizzle/tsconfig": "workspace:*",
72
+ "tsup": "^8.5.1"
73
+ }
41
74
  }
@@ -18,7 +18,7 @@ export abstract class BaseBuilder<
18
18
  }
19
19
 
20
20
  public get tableName(): string {
21
- return resolveTableName(this.entity as unknown as Entity);
21
+ return resolveTableName(this.entity as any);
22
22
  }
23
23
 
24
24
  protected get physicalTable() {
@@ -66,6 +66,6 @@ export abstract class BaseBuilder<
66
66
  }
67
67
 
68
68
  protected mapToLogical(item: Record<string, unknown>): Record<string, unknown> {
69
- return mapToLogical(this.entity, item);
69
+ return mapToLogical(this.entity as any, item);
70
70
  }
71
71
  }
@@ -73,7 +73,7 @@ export class BatchWriteBase<
73
73
 
74
74
  const command = new BatchWriteCommand({
75
75
  RequestItems: {
76
- [this.tableName]: currentRequests as any[]
76
+ [this.tableName]: currentRequests as Record<string, unknown>[]
77
77
  }
78
78
  });
79
79
 
@@ -65,7 +65,7 @@ export class DeleteBuilder<
65
65
  ReturnValues: this._returnValues,
66
66
  });
67
67
 
68
- const response = await this.client.send(command);
68
+ const response = await this.client.send(command) as any;
69
69
  return response.Attributes as TResult;
70
70
  }
71
71
  }
@@ -14,7 +14,7 @@ export class InsertBuilder<TEntity extends Entity> {
14
14
  constructor(
15
15
  private entity: TEntity,
16
16
  private client: IMizzleClient,
17
- ) {}
17
+ ) { }
18
18
 
19
19
  /**
20
20
  * Sets the values to be inserted into the database.
@@ -71,7 +71,7 @@ export class InsertBase<
71
71
  buildItem(): Record<string, unknown> {
72
72
  const itemToSave = this.processValues(this.valuesData);
73
73
  const resolution = this.resolveKeys(undefined, itemToSave);
74
-
74
+
75
75
  const finalItem: Record<string, unknown> = { ...itemToSave, ...resolution.keys };
76
76
 
77
77
  // Also resolve GSI keys if they are defined in strategies but not in resolution.keys
@@ -164,11 +164,11 @@ export class InsertBase<
164
164
  }
165
165
 
166
166
  const finalValue = item[key];
167
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
168
- item[key] = typeof (col as any).mapToDynamoValue === "function"
169
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
170
- ? (col as any).mapToDynamoValue(finalValue)
171
- : finalValue;
167
+
168
+ // Check if column has mapToDynamoValue method (it's on the Column class)
169
+ if (col instanceof Column) {
170
+ item[key] = col.mapToDynamoValue(finalValue);
171
+ }
172
172
 
173
173
  if (["SS", "NS", "BS"].includes(col.columnType)) {
174
174
  if (Array.isArray(finalValue)) {
@@ -79,7 +79,7 @@ export class RelationnalQueryBuilder<T extends Entity> {
79
79
  if (options.where) {
80
80
  const columns = (this.table._?.columns || (this.table as unknown as Record<string, Column>)) as Record<string, Column>;
81
81
  if (typeof options.where === 'function') {
82
- condition = (options.where as WhereCallback<T>)(columns as any, operators);
82
+ condition = (options.where as WhereCallback<T>)(columns as unknown as Record<string, Column>, operators);
83
83
  } else {
84
84
  condition = options.where as Condition;
85
85
  }
@@ -106,10 +106,10 @@ export class RelationnalQueryBuilder<T extends Entity> {
106
106
 
107
107
  // Map physical attributes back to logical names for the parser
108
108
  // We need a helper since we are not inheriting from BaseBuilder here
109
- const logicalItems = rawItems.map(item => mapToLogical(this.table, item));
109
+ const logicalItems = rawItems.map(item => mapToLogical(this.table as any, item));
110
110
 
111
111
  const parser = new ItemCollectionParser(this.schema);
112
- results = parser.parse(logicalItems as any, this.entityName, (options.with || options.include) as Record<string, boolean | object>);
112
+ results = parser.parse(logicalItems as Record<string, unknown>[], this.entityName, (options.with || options.include) as Record<string, boolean | object>);
113
113
 
114
114
  if (options.limit) {
115
115
  results = results.slice(0, options.limit);
@@ -147,7 +147,7 @@ export class RelationnalQueryBuilder<T extends Entity> {
147
147
  const targetEntityName = Object.entries(this.schema!.entities).find(([_, m]) => m.entity === targetEntity)?.[0];
148
148
 
149
149
  // Map result to logical names for target strategy resolution
150
- const logicalValues = mapToLogical(this.table, result);
150
+ const logicalValues = mapToLogical(this.table as any, result);
151
151
 
152
152
  let finalLogicalValues = logicalValues;
153
153
  if (relation.config.fields && relation.config.references) {
@@ -3,7 +3,7 @@ import {
3
3
  QueryCommand,
4
4
  ScanCommand,
5
5
  } from "@aws-sdk/lib-dynamodb";
6
- import { ENTITY_SYMBOLS, TABLE_SYMBOLS } from "@mizzle/shared";
6
+ import { ENTITY_SYMBOLS } from "@mizzle/shared";
7
7
  import { Column } from "../core/column";
8
8
  import type { SelectedFields as SelectedFieldsBase } from "../core/operations";
9
9
  import {
@@ -159,28 +159,27 @@ export class SelectBase<
159
159
  * @returns An AsyncIterableIterator for the query results.
160
160
  */
161
161
  iterator(): AsyncIterableIterator<TResult> {
162
- const self = this;
163
- return (async function* () {
162
+ return (async function* (this: SelectBase<TEntity, TSelection, TResult>) {
163
+ let lastEvaluatedKey: Record<string, unknown> | undefined;
164
164
  let count = 0;
165
- let lastEvaluatedKey: Record<string, any> | undefined = undefined;
166
165
 
167
166
  do {
168
- const result = await self.fetchPage(lastEvaluatedKey);
167
+ const result = await this.fetchPage(lastEvaluatedKey);
169
168
 
170
169
  for (const item of result.items) {
171
170
  yield item;
172
171
  count++;
173
- if (self._limitVal !== undefined && count >= self._limitVal) {
172
+ if (this._limitVal !== undefined && count >= this._limitVal) {
174
173
  return;
175
174
  }
176
175
  }
177
176
 
178
177
  lastEvaluatedKey = result.lastEvaluatedKey;
179
178
  } while (lastEvaluatedKey);
180
- })();
179
+ }).bind(this)();
181
180
  }
182
181
 
183
- private async fetchPage(exclusiveStartKey?: Record<string, any>): Promise<{ items: TResult[], lastEvaluatedKey?: Record<string, any> }> {
182
+ private async fetchPage(exclusiveStartKey?: Record<string, unknown>): Promise<{ items: TResult[], lastEvaluatedKey?: Record<string, unknown> }> {
184
183
  const resolution = this.resolveKeys(this._whereClause, undefined, this._forcedIndexName);
185
184
 
186
185
  if (resolution.hasPartitionKey && resolution.hasSortKey && !resolution.indexName && !exclusiveStartKey) {
@@ -211,14 +210,14 @@ export class SelectBase<
211
210
  ConsistentRead: this._consistentReadVal,
212
211
  });
213
212
 
214
- const result = await this.client.send(command);
213
+ const result = await this.client.send(command) as any;
215
214
  return result.Item ? ([this.mapToLogical(result.Item)] as TResult[]) : [];
216
215
  }
217
216
 
218
217
  private async executeQuery(
219
218
  resolution: StrategyResolution,
220
- exclusiveStartKey?: Record<string, any>,
221
- ): Promise<{ items: TResult[], lastEvaluatedKey?: Record<string, any> }> {
219
+ exclusiveStartKey?: Record<string, unknown>,
220
+ ): Promise<{ items: TResult[], lastEvaluatedKey?: Record<string, unknown> }> {
222
221
  const { expressionAttributeNames, expressionAttributeValues, addName, addValue } = this.createExpressionContext();
223
222
 
224
223
  const keyParts: string[] = [];
@@ -247,14 +246,14 @@ export class SelectBase<
247
246
  ExclusiveStartKey: exclusiveStartKey,
248
247
  });
249
248
 
250
- const response = await this.client.send(command);
249
+ const response = await this.client.send(command) as any;
251
250
  return {
252
- items: (response.Items || []).map((item: any) => this.mapToLogical(item)) as TResult[],
251
+ items: (response.Items || []).map((item: Record<string, unknown>) => this.mapToLogical(item)) as TResult[],
253
252
  lastEvaluatedKey: response.LastEvaluatedKey,
254
253
  };
255
254
  }
256
255
 
257
- private async executeScan(exclusiveStartKey?: Record<string, any>): Promise<{ items: TResult[], lastEvaluatedKey?: Record<string, any> }> {
256
+ private async executeScan(exclusiveStartKey?: Record<string, unknown>): Promise<{ items: TResult[], lastEvaluatedKey?: Record<string, unknown> }> {
258
257
  const { expressionAttributeNames, expressionAttributeValues, addName, addValue } = this.createExpressionContext();
259
258
 
260
259
  const filterExpression = this._whereClause
@@ -271,9 +270,9 @@ export class SelectBase<
271
270
  ExclusiveStartKey: exclusiveStartKey,
272
271
  });
273
272
 
274
- const response = await this.client.send(command);
273
+ const response = await this.client.send(command) as any;
275
274
  return {
276
- items: (response.Items || []).map((item: any) => this.mapToLogical(item)) as TResult[],
275
+ items: (response.Items || []).map((item: Record<string, unknown>) => this.mapToLogical(item)) as TResult[],
277
276
  lastEvaluatedKey: response.LastEvaluatedKey,
278
277
  };
279
278
  }
@@ -6,7 +6,7 @@ import { DeleteBuilder } from "./delete";
6
6
  import { Expression } from "../expressions/operators";
7
7
  import { BaseBuilder } from "./base";
8
8
  import type { IMizzleClient } from "../core/client";
9
- import { ENTITY_SYMBOLS, TABLE_SYMBOLS } from "@mizzle/shared";
9
+ import { ENTITY_SYMBOLS } from "@mizzle/shared";
10
10
  import { buildExpression } from "../expressions/builder";
11
11
  import { buildUpdateExpressionString } from "../expressions/update-builder";
12
12
  import { TransactionFailedError } from "../core/errors";
@@ -71,7 +71,7 @@ export class TransactionProxy {
71
71
  export class TransactionExecutor {
72
72
  constructor(private client: IMizzleClient) {}
73
73
 
74
- async execute(token: string, operations: any[]): Promise<void> {
74
+ async execute(token: string, operations: BaseBuilder<Entity, unknown>[]): Promise<void> {
75
75
  const transactItems = operations.map(op => this.mapToTransactItem(op));
76
76
 
77
77
  const command = new TransactWriteCommand({
@@ -81,14 +81,15 @@ export class TransactionExecutor {
81
81
 
82
82
  try {
83
83
  await this.client.send(command);
84
- } catch (error: any) {
85
- if (error.name === "TransactionCanceledException" || error.__type?.includes("TransactionCanceledException")) {
86
- const reasons = (error.CancellationReasons || []).map((reason: any, index: number) => ({
84
+ } catch (error: unknown) {
85
+ const err = error as Error & { __type?: string; CancellationReasons?: Record<string, unknown>[] };
86
+ if (err.name === "TransactionCanceledException" || err.__type?.includes("TransactionCanceledException")) {
87
+ const reasons = (err.CancellationReasons || []).map((reason: Record<string, unknown>, index: number) => ({
87
88
  index,
88
- code: reason.Code,
89
- message: reason.Message,
90
- item: reason.Item
91
- })).filter((reason: any) => reason.code !== "None");
89
+ code: reason.Code as string,
90
+ message: reason.Message as string,
91
+ item: reason.Item as Record<string, unknown> | undefined
92
+ })).filter((reason) => reason.code !== "None");
92
93
 
93
94
  throw new TransactionFailedError("Transaction canceled by server.", reasons);
94
95
  }
@@ -96,24 +97,24 @@ export class TransactionExecutor {
96
97
  }
97
98
  }
98
99
 
99
- private mapToTransactItem(builder: any): any {
100
- const kind = builder.constructor[ENTITY_SYMBOLS.ENTITY_KIND];
100
+ private mapToTransactItem(builder: BaseBuilder<Entity, unknown>): Record<string, unknown> {
101
+ const kind = (builder.constructor as unknown as { [ENTITY_SYMBOLS.ENTITY_KIND]: string })[ENTITY_SYMBOLS.ENTITY_KIND];
101
102
 
102
103
  switch (kind) {
103
104
  case "InsertBase":
104
- return { Put: this.mapPut(builder) };
105
+ return { Put: this.mapPut(builder as InsertBase<Entity, unknown>) };
105
106
  case "UpdateBuilder":
106
- return { Update: this.mapUpdate(builder) };
107
+ return { Update: this.mapUpdate(builder as UpdateBuilder<Entity, unknown>) };
107
108
  case "DeleteBuilder":
108
- return { Delete: this.mapDelete(builder) };
109
+ return { Delete: this.mapDelete(builder as DeleteBuilder<Entity, unknown>) };
109
110
  case "ConditionCheckBuilder":
110
- return { ConditionCheck: this.mapConditionCheck(builder) };
111
+ return { ConditionCheck: this.mapConditionCheck(builder as ConditionCheckBuilder<Entity>) };
111
112
  default:
112
113
  throw new Error(`Unsupported transaction operation: ${kind}`);
113
114
  }
114
115
  }
115
116
 
116
- private mapPut(builder: InsertBase<any, any>): any {
117
+ private mapPut(builder: InsertBase<Entity, unknown>): Record<string, unknown> {
117
118
  const finalItem = builder.buildItem();
118
119
 
119
120
  return {
@@ -122,7 +123,7 @@ export class TransactionExecutor {
122
123
  };
123
124
  }
124
125
 
125
- private mapUpdate(builder: UpdateBuilder<any, any>): any {
126
+ private mapUpdate(builder: UpdateBuilder<Entity, unknown>): Record<string, unknown> {
126
127
  const { expressionAttributeNames, expressionAttributeValues, addName, addValue } = builder.createExpressionContext("up_");
127
128
  const updateExpression = buildUpdateExpressionString(builder.state, addName, addValue);
128
129
 
@@ -141,7 +142,7 @@ export class TransactionExecutor {
141
142
  };
142
143
  }
143
144
 
144
- private mapDelete(builder: DeleteBuilder<any, any>): any {
145
+ private mapDelete(builder: DeleteBuilder<Entity, unknown>): Record<string, unknown> {
145
146
  const resolution = builder.resolveKeys(undefined, builder.keys);
146
147
  return {
147
148
  TableName: builder.tableName,
@@ -149,7 +150,7 @@ export class TransactionExecutor {
149
150
  };
150
151
  }
151
152
 
152
- private mapConditionCheck(builder: ConditionCheckBuilder<any>): any {
153
+ private mapConditionCheck(builder: ConditionCheckBuilder<Entity>): Record<string, unknown> {
153
154
  const { expressionAttributeNames, expressionAttributeValues, addName, addValue } = builder.createExpressionContext("cc_");
154
155
 
155
156
  const resolution = builder.resolveKeys(builder.whereClause);
@@ -1,6 +1,7 @@
1
1
  import { UpdateCommand } from "@aws-sdk/lib-dynamodb";
2
2
  import { ENTITY_SYMBOLS } from "@mizzle/shared";
3
3
  import { Entity, type InferInsertModel } from "../core/table";
4
+ import { Column } from "../core/column";
4
5
  import { type Expression } from "../expressions/operators";
5
6
  import { BaseBuilder } from "./base";
6
7
  import { type IMizzleClient } from "../core/client";
@@ -59,7 +60,7 @@ export class UpdateBuilder<
59
60
  * @returns The current builder instance for chaining.
60
61
  */
61
62
  set(values: Partial<{ [K in keyof InferInsertModel<TEntity>]: InferInsertModel<TEntity>[K] | UpdateAction }>): this {
62
- partitionUpdateValues(values as Record<string, any>, this._state, this.entity[ENTITY_SYMBOLS.COLUMNS] as Record<string, any>);
63
+ partitionUpdateValues(values as Record<string, unknown | UpdateAction>, this._state, this.entity[ENTITY_SYMBOLS.COLUMNS] as unknown as Record<string, Column>);
63
64
  return this;
64
65
  }
65
66
 
@@ -71,9 +72,9 @@ export class UpdateBuilder<
71
72
  * @returns The current builder instance for chaining.
72
73
  */
73
74
  add(values: Partial<InferInsertModel<TEntity>>): this {
74
- const columns = this.entity[ENTITY_SYMBOLS.COLUMNS] as Record<string, any>;
75
+ const columns = this.entity[ENTITY_SYMBOLS.COLUMNS] as unknown as Record<string, Column>;
75
76
  for (const [key, val] of Object.entries(values)) {
76
- const col = columns[key];
77
+ const col = columns[key] as unknown as { mapToDynamoValue?: (v: unknown) => unknown };
77
78
  this._state.add[key] = (col && typeof col.mapToDynamoValue === "function")
78
79
  ? col.mapToDynamoValue(val)
79
80
  : val;
@@ -101,9 +102,9 @@ export class UpdateBuilder<
101
102
  * @returns The current builder instance for chaining.
102
103
  */
103
104
  delete(values: Partial<InferInsertModel<TEntity>>): this {
104
- const columns = this.entity[ENTITY_SYMBOLS.COLUMNS] as Record<string, any>;
105
+ const columns = this.entity[ENTITY_SYMBOLS.COLUMNS] as unknown as Record<string, Column>;
105
106
  for (const [key, val] of Object.entries(values)) {
106
- const col = columns[key];
107
+ const col = columns[key] as unknown as { mapToDynamoValue?: (v: unknown) => unknown };
107
108
  this._state.delete[key] = (col && typeof col.mapToDynamoValue === "function")
108
109
  ? col.mapToDynamoValue(val)
109
110
  : val;
@@ -155,12 +156,13 @@ export class UpdateBuilder<
155
156
  * @throws {ItemSizeExceededError} if the update exceeds 400KB.
156
157
  */
157
158
  public override async execute(): Promise<TResult> {
158
- const columns = this.entity[ENTITY_SYMBOLS.COLUMNS] as Record<string, any>;
159
+ const columns = this.entity[ENTITY_SYMBOLS.COLUMNS] as unknown as Record<string, Column>;
159
160
  for (const [key, col] of Object.entries(columns)) {
160
- if (col.onUpdateFn && !this._state.set[key] && !this._state.remove.includes(key)) {
161
- const val = col.onUpdateFn();
161
+ const column = col as unknown as { onUpdateFn?: () => unknown, mapToDynamoValue?: (v: unknown) => unknown };
162
+ if (column.onUpdateFn && !this._state.set[key] && !this._state.remove.includes(key)) {
163
+ const val = column.onUpdateFn();
162
164
  this._state.set[key] = {
163
- value: typeof col.mapToDynamoValue === "function" ? col.mapToDynamoValue(val) : val
165
+ value: typeof column.mapToDynamoValue === "function" ? column.mapToDynamoValue(val) : val
164
166
  };
165
167
  }
166
168
  }
@@ -199,7 +201,7 @@ export class UpdateBuilder<
199
201
  ReturnValues: this._returnValues,
200
202
  });
201
203
 
202
- const response = await this.client.send(command);
204
+ const response = await this.client.send(command) as any;
203
205
  return response.Attributes as TResult;
204
206
  }
205
207
 
@@ -5,7 +5,7 @@ import { Entity } from "./table";
5
5
  import { Column } from "./column";
6
6
 
7
7
  interface MinimalPhysicalTable {
8
- [TABLE_SYMBOLS.PARTITION_KEY]: Column;
8
+ [TABLE_SYMBOLS.PARTITION_KEY]: Column | undefined;
9
9
  [TABLE_SYMBOLS.SORT_KEY]?: Column;
10
10
  }
11
11
 
@@ -78,8 +78,9 @@ export class ItemCollectionParser {
78
78
  KeyStrategy
79
79
  >;
80
80
  const physicalTable = entity[ENTITY_SYMBOLS.PHYSICAL_TABLE] as unknown as MinimalPhysicalTable;
81
+ if (!physicalTable || !physicalTable[TABLE_SYMBOLS.PARTITION_KEY]) return false;
81
82
 
82
- const pkName = physicalTable[TABLE_SYMBOLS.PARTITION_KEY].name;
83
+ const pkName = physicalTable[TABLE_SYMBOLS.PARTITION_KEY]!.name;
83
84
  const skName = physicalTable[TABLE_SYMBOLS.SORT_KEY]?.name;
84
85
 
85
86
  const pkMatch = this.matchStrategy(item[pkName], strategies.pk);
@@ -167,13 +167,14 @@ export function defineRelations<TSchema extends Record<string, Entity>>(
167
167
  */
168
168
  export function defineRelations(
169
169
  first: Entity | Record<string, Entity>,
170
- callback: Function
171
- ): any {
170
+ callback: RelationsCallback | MultiRelationsCallback<any>
171
+ ): RelationsDefinition | MultiRelationsDefinition {
172
172
  if (first instanceof Entity) {
173
173
  // Single entity mode
174
- const config = callback({
175
- one: (to: Entity, config: any) => new Relation("one", { to, ...config }),
176
- many: (to: Entity, config: any) => new Relation("many", { to, ...config }),
174
+ const cb = callback as RelationsCallback;
175
+ const config = cb({
176
+ one: (to: Entity, config?: Omit<RelationConfig, "to">) => new Relation("one", { to, ...config }),
177
+ many: (to: Entity, config?: Omit<RelationConfig, "to">) => new Relation("many", { to, ...config }),
177
178
  });
178
179
 
179
180
  return {
@@ -184,24 +185,25 @@ export function defineRelations(
184
185
  } else {
185
186
  // Multi-entity mode
186
187
  const schema = first as Record<string, Entity>;
188
+ const cb = callback as MultiRelationsCallback<typeof schema>;
187
189
 
188
190
  // Build helpers
189
- const helpers: any = {
191
+ const helpers: Record<string, unknown> = {
190
192
  one: {},
191
193
  many: {},
192
194
  };
193
195
 
194
196
  for (const [key, entity] of Object.entries(schema)) {
195
- helpers.one[key] = (config: any) => new Relation("one", { to: entity, ...config });
196
- helpers.many[key] = (config: any) => new Relation("many", { to: entity, ...config });
197
+ (helpers.one as Record<string, unknown>)[key] = (config?: Omit<RelationConfig, "to">) => new Relation("one", { to: entity, ...config });
198
+ (helpers.many as Record<string, unknown>)[key] = (config?: Omit<RelationConfig, "to">) => new Relation("many", { to: entity, ...config });
197
199
  helpers[key] = entity;
198
200
  }
199
201
 
200
- const definitions = callback(helpers);
202
+ const definitions = cb(helpers as unknown as RelationsHelpers<typeof schema>);
201
203
 
202
204
  return {
203
205
  schema,
204
- definitions,
206
+ definitions: definitions as Record<string, Record<string, Relation>>,
205
207
  [RELATION_SYMBOLS.RELATION_CONFIG]: true
206
208
  };
207
209
  }
@@ -242,8 +244,9 @@ export function extractMetadata(schema: Record<string, unknown>): InternalRelati
242
244
 
243
245
  // Second pass: identify relations
244
246
  for (const [, value] of Object.entries(schema)) {
245
- if (value && (value as any)[RELATION_SYMBOLS.RELATION_CONFIG]) {
246
- if ((value as any).entity) {
247
+ if (value && typeof value === 'object' && (value as Record<string | symbol, unknown>)[RELATION_SYMBOLS.RELATION_CONFIG]) {
248
+ const relationConfig = value as Record<string | symbol, unknown>;
249
+ if (relationConfig.entity) {
247
250
  // Single entity definition
248
251
  const definition = value as RelationsDefinition;
249
252
  const entityEntry = Object.entries(metadata.entities).find(
@@ -254,7 +257,7 @@ export function extractMetadata(schema: Record<string, unknown>): InternalRelati
254
257
  const [, meta] = entityEntry;
255
258
  meta.relations = { ...meta.relations, ...definition.config };
256
259
  }
257
- } else if ((value as any).definitions) {
260
+ } else if (relationConfig.definitions) {
258
261
  // Multi-entity definition
259
262
  const multiDef = value as MultiRelationsDefinition;
260
263
  for (const [entityName, relations] of Object.entries(multiDef.definitions)) {
@@ -115,8 +115,8 @@ export function resolveStrategies(
115
115
  providedValues?: Record<string, unknown>,
116
116
  forcedIndexName?: string
117
117
  ): StrategyResolution {
118
- const strategies = entity[ENTITY_SYMBOLS.ENTITY_STRATEGY] as unknown as Record<string, any>;
119
- const physicalTable = entity[ENTITY_SYMBOLS.PHYSICAL_TABLE] as any;
118
+ const strategies = entity[ENTITY_SYMBOLS.ENTITY_STRATEGY] as unknown as Record<string, KeyStrategy | { pk: KeyStrategy; sk?: KeyStrategy }>;
119
+ const physicalTable = entity[ENTITY_SYMBOLS.PHYSICAL_TABLE] as unknown as Record<string | symbol, unknown>;
120
120
 
121
121
  const pkCol = physicalTable[TABLE_SYMBOLS.PARTITION_KEY] as Column;
122
122
  const skCol = physicalTable[TABLE_SYMBOLS.SORT_KEY] as Column | undefined;
@@ -150,9 +150,10 @@ export function resolveStrategies(
150
150
  };
151
151
 
152
152
  if (forcedIndexName) {
153
- const indexes = physicalTable[TABLE_SYMBOLS.INDEXES];
153
+ const indexes = physicalTable[TABLE_SYMBOLS.INDEXES] as Record<string, unknown> | undefined;
154
154
  const indexBuilder = indexes?.[forcedIndexName] as { config: { pk: string; sk?: string } } | undefined;
155
- const indexStrategy = strategies[forcedIndexName];
155
+ const strategy = strategies[forcedIndexName];
156
+ const indexStrategy = (strategy && 'pk' in strategy) ? strategy as { pk: KeyStrategy; sk?: KeyStrategy } : undefined;
156
157
 
157
158
  if (indexBuilder && indexStrategy) {
158
159
  // Check if availableValues already contains the PHYSICAL key
@@ -196,16 +197,18 @@ export function resolveStrategies(
196
197
  if (result.hasPartitionKey) return result;
197
198
  }
198
199
 
199
- if (strategies.pk) {
200
- const pkValue = resolveKeyStrategy(strategies.pk, availableValues);
200
+ if ('pk' in strategies && strategies.pk) {
201
+ const pkStrategy = strategies.pk as KeyStrategy;
202
+ const pkValue = resolveKeyStrategy(pkStrategy, availableValues);
201
203
  if (pkValue) {
202
204
  result.keys[pkCol.name] = pkValue;
203
205
  result.hasPartitionKey = true;
204
206
  }
205
207
  }
206
208
 
207
- if (strategies.sk) {
208
- const skValue = resolveKeyStrategy(strategies.sk, availableValues);
209
+ if ('sk' in strategies && strategies.sk) {
210
+ const skStrategy = strategies.sk as KeyStrategy;
211
+ const skValue = resolveKeyStrategy(skStrategy, availableValues);
209
212
  if (skValue) {
210
213
  if (skCol) {
211
214
  result.keys[skCol.name] = skValue;
@@ -217,13 +220,14 @@ export function resolveStrategies(
217
220
  }
218
221
 
219
222
  if (!result.hasPartitionKey) {
220
- const indexes = physicalTable[TABLE_SYMBOLS.INDEXES];
223
+ const indexes = physicalTable[TABLE_SYMBOLS.INDEXES] as Record<string, unknown> | undefined;
221
224
  if (indexes) {
222
225
  for (const [indexName, indexBuilderBase] of Object.entries(indexes)) {
223
226
  const indexBuilder = indexBuilderBase as { config: { pk: string; sk?: string } };
224
- const indexStrategy = strategies[indexName];
225
- if (!indexStrategy) continue;
227
+ const strategy = strategies[indexName];
228
+ const indexStrategy = (strategy && 'pk' in strategy) ? strategy as { pk: KeyStrategy; sk?: KeyStrategy } : undefined;
226
229
 
230
+ if (!indexStrategy) continue;
227
231
  if (availableValues[indexBuilder.config.pk] !== undefined) {
228
232
  result.indexName = indexName;
229
233
  result.keys = {};
package/src/core/table.ts CHANGED
@@ -244,7 +244,7 @@ export function dynamoEntity<
244
244
  normalizedStrategies[key] = { type: "prefix", segments: ["", val] };
245
245
  } else if (val && typeof val === "object" && !("type" in val && "segments" in val)) {
246
246
  // It's an index strategy object { pk, sk }
247
- const indexStrategy: any = { ...val };
247
+ const indexStrategy: Record<string, unknown> = { ...val as object };
248
248
  if (indexStrategy.pk instanceof Column) {
249
249
  indexStrategy.pk = { type: "prefix", segments: ["", indexStrategy.pk] };
250
250
  }
package/src/db.ts CHANGED
@@ -3,6 +3,7 @@ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
3
3
  import { InsertBuilder } from "./builders/insert";
4
4
  import { RelationnalQueryBuilder } from "./builders/relational-builder";
5
5
  import { SelectBuilder, type SelectedFields } from "./builders/select";
6
+ import type { BaseBuilder } from "./builders/base";
6
7
  import type { Entity, InferInsertModel, InferSelectModel } from "./core/table";
7
8
  import { UpdateBuilder } from "./builders/update";
8
9
  import { DeleteBuilder } from "./builders/delete";
@@ -13,14 +14,14 @@ import { extractMetadata, type InternalRelationalSchema } from "./core/relations
13
14
  import { RetryHandler, type RetryConfig } from "./core/retry";
14
15
  import { MizzleClient, type IMizzleClient } from "./core/client";
15
16
 
16
- export type QuerySchema<TSchema extends Record<string, unknown>> = {
17
+ export type QuerySchema<TSchema extends Record<string, any>> = {
17
18
  [K in keyof TSchema as TSchema[K] extends Entity ? K : never]: RelationnalQueryBuilder<TSchema[K] extends Entity ? TSchema[K] : never>;
18
19
  };
19
20
 
20
21
  /**
21
22
  * DynamoDB database instance.
22
23
  */
23
- export class DynamoDB<TSchema extends Record<string, unknown> = Record<string, unknown>> {
24
+ export class DynamoDB<TSchema extends Record<string, any> = Record<string, any>> {
24
25
  private docClient: IMizzleClient;
25
26
  private schema?: InternalRelationalSchema;
26
27
  private retryConfig: RetryConfig;
@@ -200,7 +201,7 @@ export class DynamoDB<TSchema extends Record<string, unknown> = Record<string, u
200
201
  */
201
202
  async transaction(
202
203
  token: string,
203
- callback: (tx: TransactionProxy) => any[] | Promise<any[]>
204
+ callback: (tx: TransactionProxy) => BaseBuilder<Entity, unknown>[] | Promise<BaseBuilder<Entity, unknown>[]>
204
205
  ): Promise<void> {
205
206
  const proxy = new TransactionProxy(this.docClient);
206
207
  const operations = await callback(proxy);
@@ -220,7 +221,7 @@ export class DynamoDB<TSchema extends Record<string, unknown> = Record<string, u
220
221
  /**
221
222
  * Configuration for initializing Mizzle.
222
223
  */
223
- export interface MizzleConfig<TSchema extends Record<string, unknown> = Record<string, unknown>> {
224
+ export interface MizzleConfig<TSchema extends Record<string, any> = Record<string, any>> {
224
225
  /**
225
226
  * AWS DynamoDB Client instance from `@aws-sdk/client-dynamodb`.
226
227
  */
@@ -253,7 +254,7 @@ export interface MizzleConfig<TSchema extends Record<string, unknown> = Record<s
253
254
  * @param config A DynamoDBClient instance or a MizzleConfig object.
254
255
  * @returns A DynamoDB instance for performing database operations.
255
256
  */
256
- export function mizzle<TSchema extends Record<string, unknown> = Record<string, unknown>>(
257
+ export function mizzle<TSchema extends Record<string, any> = Record<string, any>>(
257
258
  config: DynamoDBClient | MizzleConfig<TSchema>
258
259
  ): DynamoDB<TSchema> {
259
260
  if (config instanceof DynamoDBClient) {
@@ -35,8 +35,8 @@ export function buildExpression(
35
35
  return undefined;
36
36
  }
37
37
  const colName = addName(cond.column.name);
38
- const mapValue = (v: unknown) => typeof (cond.column as any).mapToDynamoValue === "function"
39
- ? (cond.column as any).mapToDynamoValue(v)
38
+ const mapValue = (v: unknown) => typeof (cond.column as unknown as { mapToDynamoValue: Function }).mapToDynamoValue === "function"
39
+ ? (cond.column as unknown as { mapToDynamoValue: (v: unknown) => unknown }).mapToDynamoValue(v)
40
40
  : v;
41
41
 
42
42
  if (cond.operator === "between") {
@@ -61,8 +61,8 @@ export function buildExpression(
61
61
  return undefined;
62
62
  }
63
63
  const colName = addName(cond.column.name);
64
- const mapValue = (v: unknown) => typeof (cond.column as any).mapToDynamoValue === "function"
65
- ? (cond.column as any).mapToDynamoValue(v)
64
+ const mapValue = (v: unknown) => typeof (cond.column as unknown as { mapToDynamoValue: Function }).mapToDynamoValue === "function"
65
+ ? (cond.column as unknown as { mapToDynamoValue: (v: unknown) => unknown }).mapToDynamoValue(v)
66
66
  : v;
67
67
 
68
68
  if (cond.operator === "attribute_exists") {
@@ -1,5 +1,5 @@
1
1
  import { type Column } from "../core/column";
2
- import { UpdateAction } from "./actions";
2
+ import { UpdateAction, SetAction, AddAction, DeleteAction } from "./actions";
3
3
 
4
4
  export interface UpdateState {
5
5
  set: Record<string, { value: unknown; functionName?: string; usePathAsFirstArg?: boolean }>;
@@ -24,27 +24,29 @@ export function partitionUpdateValues(
24
24
  ): void {
25
25
  for (const [key, val] of Object.entries(values)) {
26
26
  const col = columns?.[key];
27
- const mapValue = (v: unknown) => (col && typeof (col as any).mapToDynamoValue === "function")
28
- ? (col as any).mapToDynamoValue(v)
27
+ const mapValue = (v: unknown) => (col && typeof (col as unknown as { mapToDynamoValue: Function }).mapToDynamoValue === "function")
28
+ ? (col as unknown as { mapToDynamoValue: (v: unknown) => unknown }).mapToDynamoValue(v)
29
29
  : v;
30
30
 
31
31
  if (val instanceof UpdateAction) {
32
32
  switch (val.action) {
33
- case "SET":
33
+ case "SET": {
34
+ const setVal = val as SetAction;
34
35
  state.set[key] = {
35
- value: mapValue((val as any).value),
36
- functionName: (val as any).functionName,
37
- usePathAsFirstArg: (val as any).usePathAsFirstArg,
36
+ value: mapValue(setVal.value),
37
+ functionName: setVal.functionName,
38
+ usePathAsFirstArg: setVal.usePathAsFirstArg,
38
39
  };
39
40
  break;
41
+ }
40
42
  case "ADD":
41
- state.add[key] = mapValue((val as any).value);
43
+ state.add[key] = mapValue((val as AddAction).value);
42
44
  break;
43
45
  case "REMOVE":
44
46
  state.remove.push(key);
45
47
  break;
46
48
  case "DELETE":
47
- state.delete[key] = mapValue((val as any).value);
49
+ state.delete[key] = mapValue((val as DeleteAction).value);
48
50
  break;
49
51
  }
50
52
  } else if (val !== undefined) {
package/src/index.ts CHANGED
@@ -13,6 +13,7 @@ export * from "./builders/relational-builder";
13
13
  export * from "./core/table";
14
14
  export * from "./core/strategies";
15
15
  export * from "./core/relations";
16
+ export * from "./core/diff";
16
17
  export * from "./core/retry";
17
18
  export * from "./core/errors";
18
19
  export * from "./columns";
package/tsconfig.json CHANGED
@@ -2,9 +2,8 @@
2
2
  "extends": "@mizzle/tsconfig/base.json",
3
3
  "compilerOptions": {
4
4
  "outDir": "./dist",
5
- "baseUrl": ".",
6
5
  "paths": {
7
- "@mizzle/shared": ["../shared/src/index.ts"]
6
+ "@mizzle/shared": ["./../shared/src/index.ts"]
8
7
  }
9
8
  },
10
9
  "include": ["src"]