@aurios/mizzle 1.0.0 → 1.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/CHANGELOG.md +15 -0
- package/package.json +1 -1
- package/src/builders/base.ts +1 -1
- package/src/builders/batch-get.ts +6 -6
- package/src/builders/batch-write.ts +8 -7
- package/src/builders/delete.ts +10 -0
- package/src/builders/insert.ts +31 -3
- package/src/builders/select.ts +84 -0
- package/src/builders/update.ts +60 -0
- package/src/columns/binary-set.ts +16 -1
- package/src/columns/binary.ts +17 -1
- package/src/columns/boolean.ts +14 -1
- package/src/columns/date.ts +20 -1
- package/src/columns/json.ts +24 -1
- package/src/columns/list.ts +17 -1
- package/src/columns/map.ts +18 -1
- package/src/columns/number-set.ts +16 -1
- package/src/columns/number.ts +15 -1
- package/src/columns/string-set.ts +16 -1
- package/src/columns/string.ts +16 -1
- package/src/columns/uuid.ts +17 -1
- package/src/core/client.ts +4 -3
- package/src/core/errors.ts +1 -1
- package/src/core/parser.ts +8 -4
- package/src/core/relations.ts +153 -25
- package/src/core/retry.ts +5 -4
- package/src/core/snapshot.ts +1 -1
- package/src/core/table.ts +33 -0
- package/src/db.ts +84 -12
package/src/columns/list.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Column,
|
|
3
3
|
type ColumnBaseConfig,
|
|
4
|
-
type ColumnRuntimeConfig,
|
|
5
4
|
} from "../core/column";
|
|
6
5
|
import {
|
|
7
6
|
ColumnBuider,
|
|
@@ -28,6 +27,7 @@ export class ListColumnBuilder<
|
|
|
28
27
|
): ListColumn<MakeColumnConfig<T, TTableName>> {
|
|
29
28
|
return new ListColumn<MakeColumnConfig<T, TTableName>>(
|
|
30
29
|
table,
|
|
30
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
31
31
|
this.config as any,
|
|
32
32
|
);
|
|
33
33
|
}
|
|
@@ -42,6 +42,22 @@ export function list(): ListColumnInitial<"">;
|
|
|
42
42
|
export function list<TName extends string>(
|
|
43
43
|
name: TName,
|
|
44
44
|
): ListColumnInitial<TName>;
|
|
45
|
+
/**
|
|
46
|
+
* Defines a List column ("L") in DynamoDB.
|
|
47
|
+
*
|
|
48
|
+
* A list is an ordered collection of values. It can store mixed types,
|
|
49
|
+
* but you can enforce a specific type using `.$type<T[]>()`.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* const posts = defineTable("posts", {
|
|
54
|
+
* tags: list("tags").$type<string[]>(),
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @param name The name of the attribute in DynamoDB. If omitted, it will use the property name in the definition object.
|
|
59
|
+
* @returns A ListColumnBuilder instance.
|
|
60
|
+
*/
|
|
45
61
|
export function list(name?: string) {
|
|
46
62
|
return new ListColumnBuilder(name ?? "");
|
|
47
63
|
}
|
package/src/columns/map.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Column,
|
|
3
3
|
type ColumnBaseConfig,
|
|
4
|
-
type ColumnRuntimeConfig,
|
|
5
4
|
} from "../core/column";
|
|
6
5
|
import {
|
|
7
6
|
ColumnBuider,
|
|
@@ -29,6 +28,7 @@ export class MapColumnBuilder<
|
|
|
29
28
|
): MapColumn<MakeColumnConfig<T, TTableName>> {
|
|
30
29
|
return new MapColumn<MakeColumnConfig<T, TTableName>>(
|
|
31
30
|
table,
|
|
31
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
32
32
|
this.config as any,
|
|
33
33
|
);
|
|
34
34
|
}
|
|
@@ -41,6 +41,23 @@ export class MapColumn<
|
|
|
41
41
|
|
|
42
42
|
export function map(): MapColumnInitial<"">;
|
|
43
43
|
export function map<TName extends string>(name: TName): MapColumnInitial<TName>;
|
|
44
|
+
/**
|
|
45
|
+
* Defines a Map column ("M") in DynamoDB.
|
|
46
|
+
*
|
|
47
|
+
* A map is a set of key-value pairs (like a JSON object).
|
|
48
|
+
* Unlike `json()`, this is stored as a native DynamoDB Map, allowing you to filter/query nested properties
|
|
49
|
+
* more easily in some contexts, though `json()` is often preferred for simple object storage.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* const users = defineTable("users", {
|
|
54
|
+
* metadata: map("metadata").$type<{ verified: boolean, loginCount: number }>(),
|
|
55
|
+
* });
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @param name The name of the attribute in DynamoDB. If omitted, it will use the property name in the definition object.
|
|
59
|
+
* @returns A MapColumnBuilder instance.
|
|
60
|
+
*/
|
|
44
61
|
export function map(name?: string) {
|
|
45
62
|
return new MapColumnBuilder(name ?? "");
|
|
46
63
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Column,
|
|
3
3
|
type ColumnBaseConfig,
|
|
4
|
-
type ColumnRuntimeConfig,
|
|
5
4
|
} from "../core/column";
|
|
6
5
|
import {
|
|
7
6
|
ColumnBuider,
|
|
@@ -30,6 +29,7 @@ export class NumberSetColumnBuilder<
|
|
|
30
29
|
): NumberSetColumn<MakeColumnConfig<T, TTableName>> {
|
|
31
30
|
return new NumberSetColumn<MakeColumnConfig<T, TTableName>>(
|
|
32
31
|
table,
|
|
32
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
33
33
|
this.config as any,
|
|
34
34
|
);
|
|
35
35
|
}
|
|
@@ -44,6 +44,21 @@ export function numberSet(): NumberSetColumnInitial<"">;
|
|
|
44
44
|
export function numberSet<TName extends string>(
|
|
45
45
|
name: TName,
|
|
46
46
|
): NumberSetColumnInitial<TName>;
|
|
47
|
+
/**
|
|
48
|
+
* Defines a Number Set column ("NS") in DynamoDB.
|
|
49
|
+
*
|
|
50
|
+
* Represents a set of unique numbers. Mizzle handles conversion between JavaScript `Set<number>` (or arrays) and DynamoDB Sets.
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* ```ts
|
|
54
|
+
* const metrics = defineTable("metrics", {
|
|
55
|
+
* counts: numberSet("counts"),
|
|
56
|
+
* });
|
|
57
|
+
* ```
|
|
58
|
+
*
|
|
59
|
+
* @param name The name of the attribute in DynamoDB. If omitted, it will use the property name in the definition object.
|
|
60
|
+
* @returns A NumberSetColumnBuilder instance.
|
|
61
|
+
*/
|
|
47
62
|
export function numberSet(name?: string) {
|
|
48
63
|
return new NumberSetColumnBuilder(name ?? "");
|
|
49
64
|
}
|
package/src/columns/number.ts
CHANGED
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
ColumnBuider,
|
|
4
4
|
type MakeColumnConfig,
|
|
5
5
|
type ColumnBuilderBaseConfig,
|
|
6
|
-
type ColumnBuilderRuntimeConfig,
|
|
7
6
|
} from "../core/column-builder";
|
|
8
7
|
import type { AnyTable } from "../core/table";
|
|
9
8
|
|
|
@@ -38,6 +37,7 @@ export class NumberColumnBuilder<
|
|
|
38
37
|
): NumberColumn<MakeColumnConfig<T, TTableName>> {
|
|
39
38
|
return new NumberColumn<MakeColumnConfig<T, TTableName>>(
|
|
40
39
|
table,
|
|
40
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
41
41
|
this.config as any,
|
|
42
42
|
);
|
|
43
43
|
}
|
|
@@ -52,6 +52,20 @@ export function number(): NumberColumnInitial<"">;
|
|
|
52
52
|
export function number<TName extends string>(
|
|
53
53
|
name: TName,
|
|
54
54
|
): NumberColumnInitial<TName>;
|
|
55
|
+
/**
|
|
56
|
+
* Defines a Number column ("N") in DynamoDB.
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```ts
|
|
60
|
+
* const products = defineTable("products", {
|
|
61
|
+
* price: number("price"),
|
|
62
|
+
* quantity: number("quantity").default(0),
|
|
63
|
+
* });
|
|
64
|
+
* ```
|
|
65
|
+
*
|
|
66
|
+
* @param name The name of the attribute in DynamoDB. If omitted, it will use the property name in the definition object.
|
|
67
|
+
* @returns A NumberColumnBuilder instance.
|
|
68
|
+
*/
|
|
55
69
|
export function number(name?: string) {
|
|
56
70
|
return new NumberColumnBuilder(name ?? "");
|
|
57
71
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Column,
|
|
3
3
|
type ColumnBaseConfig,
|
|
4
|
-
type ColumnRuntimeConfig,
|
|
5
4
|
} from "../core/column";
|
|
6
5
|
import {
|
|
7
6
|
ColumnBuider,
|
|
@@ -40,6 +39,7 @@ export class StringSetColumnBuilder<
|
|
|
40
39
|
): StringSetColumn<MakeColumnConfig<T, TTableName>> {
|
|
41
40
|
return new StringSetColumn<MakeColumnConfig<T, TTableName>>(
|
|
42
41
|
table,
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
43
43
|
this.config as any,
|
|
44
44
|
);
|
|
45
45
|
}
|
|
@@ -54,6 +54,21 @@ export function stringSet(): StringSetColumnInitial<"">;
|
|
|
54
54
|
export function stringSet<TName extends string>(
|
|
55
55
|
name: TName,
|
|
56
56
|
): StringSetColumnInitial<TName>;
|
|
57
|
+
/**
|
|
58
|
+
* Defines a String Set column ("SS") in DynamoDB.
|
|
59
|
+
*
|
|
60
|
+
* Represents a set of unique strings. Mizzle handles conversion between JavaScript `Set<string>` (or arrays) and DynamoDB Sets.
|
|
61
|
+
*
|
|
62
|
+
* @example
|
|
63
|
+
* ```ts
|
|
64
|
+
* const users = defineTable("users", {
|
|
65
|
+
* roles: stringSet("roles"),
|
|
66
|
+
* });
|
|
67
|
+
* ```
|
|
68
|
+
*
|
|
69
|
+
* @param name The name of the attribute in DynamoDB. If omitted, it will use the property name in the definition object.
|
|
70
|
+
* @returns A StringSetColumnBuilder instance.
|
|
71
|
+
*/
|
|
57
72
|
export function stringSet(name?: string) {
|
|
58
73
|
return new StringSetColumnBuilder(name ?? "");
|
|
59
74
|
}
|
package/src/columns/string.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
Column,
|
|
3
3
|
type ColumnBaseConfig,
|
|
4
|
-
type ColumnRuntimeConfig,
|
|
5
4
|
} from "../core/column";
|
|
6
5
|
import {
|
|
7
6
|
ColumnBuider,
|
|
@@ -45,6 +44,7 @@ class StringColumnBuilder<
|
|
|
45
44
|
): StringColumn<MakeColumnConfig<T, TTableName>> {
|
|
46
45
|
return new StringColumn<MakeColumnConfig<T, TTableName>>(
|
|
47
46
|
table,
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
48
48
|
this.config as any,
|
|
49
49
|
);
|
|
50
50
|
}
|
|
@@ -59,6 +59,21 @@ export function string(): StringColumnInitial<"">;
|
|
|
59
59
|
export function string<TName extends string>(
|
|
60
60
|
name: TName,
|
|
61
61
|
): StringColumnInitial<TName>;
|
|
62
|
+
/**
|
|
63
|
+
* Defines a String column ("S") in DynamoDB.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```ts
|
|
67
|
+
* const users = defineTable("users", {
|
|
68
|
+
* name: string("name"),
|
|
69
|
+
* email: string("email").notNull(),
|
|
70
|
+
* status: string("status").default("active"),
|
|
71
|
+
* });
|
|
72
|
+
* ```
|
|
73
|
+
*
|
|
74
|
+
* @param name The name of the attribute in DynamoDB. If omitted, it will use the property name in the definition object.
|
|
75
|
+
* @returns A StringColumnBuilder instance.
|
|
76
|
+
*/
|
|
62
77
|
export function string(name?: string) {
|
|
63
78
|
return new StringColumnBuilder(name ?? "");
|
|
64
79
|
}
|
package/src/columns/uuid.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { v7 as uuidV7 } from "uuid";
|
|
|
3
3
|
import {
|
|
4
4
|
ColumnBuider,
|
|
5
5
|
type ColumnBuilderBaseConfig,
|
|
6
|
-
type ColumnBuilderRuntimeConfig,
|
|
7
6
|
type MakeColumnConfig,
|
|
8
7
|
} from "../core/column-builder";
|
|
9
8
|
import type { AnyTable } from "../core/table";
|
|
@@ -32,6 +31,7 @@ export class UUIDColumnBuilder<
|
|
|
32
31
|
): UUIDColumn<MakeColumnConfig<T, TTableName>> {
|
|
33
32
|
return new UUIDColumn<MakeColumnConfig<T, TTableName>>(
|
|
34
33
|
table,
|
|
34
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
35
35
|
this.config as any,
|
|
36
36
|
);
|
|
37
37
|
}
|
|
@@ -46,6 +46,22 @@ export function uuid(): UUIDColumnInitial<"">;
|
|
|
46
46
|
export function uuid<TName extends string>(
|
|
47
47
|
name: TName,
|
|
48
48
|
): UUIDColumnInitial<TName>;
|
|
49
|
+
/**
|
|
50
|
+
* Defines a UUID column.
|
|
51
|
+
*
|
|
52
|
+
* In DynamoDB, this is stored as a string ("S"). It includes a `.defaultRandom()` helper
|
|
53
|
+
* to automatically generate UUID v7 values on insert.
|
|
54
|
+
*
|
|
55
|
+
* @example
|
|
56
|
+
* ```ts
|
|
57
|
+
* const users = defineTable("users", {
|
|
58
|
+
* id: uuid("id").partitionKey().defaultRandom(),
|
|
59
|
+
* });
|
|
60
|
+
* ```
|
|
61
|
+
*
|
|
62
|
+
* @param name The name of the attribute in DynamoDB. If omitted, it will use the property name in the definition object.
|
|
63
|
+
* @returns A UUIDColumnBuilder instance.
|
|
64
|
+
*/
|
|
49
65
|
export function uuid(name?: string) {
|
|
50
66
|
return new UUIDColumnBuilder(name ?? "");
|
|
51
67
|
}
|
package/src/core/client.ts
CHANGED
|
@@ -3,7 +3,7 @@ import { RetryHandler } from "./retry";
|
|
|
3
3
|
|
|
4
4
|
// We define a simplified interface for what we need from the client to avoid complex generic matching
|
|
5
5
|
export interface IMizzleClient {
|
|
6
|
-
send(command:
|
|
6
|
+
send(command: unknown, options?: unknown): Promise<unknown>;
|
|
7
7
|
}
|
|
8
8
|
|
|
9
9
|
export class MizzleClient implements IMizzleClient {
|
|
@@ -12,7 +12,8 @@ export class MizzleClient implements IMizzleClient {
|
|
|
12
12
|
private retryHandler: RetryHandler
|
|
13
13
|
) {}
|
|
14
14
|
|
|
15
|
-
send(command:
|
|
16
|
-
|
|
15
|
+
send(command: unknown, options?: unknown): Promise<unknown> {
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
return this.retryHandler.execute(() => this.client.send(command as any, options as any));
|
|
17
18
|
}
|
|
18
19
|
}
|
package/src/core/errors.ts
CHANGED
package/src/core/parser.ts
CHANGED
|
@@ -4,6 +4,11 @@ import { type KeyStrategy } from "./strategies";
|
|
|
4
4
|
import { Entity } from "./table";
|
|
5
5
|
import { Column } from "./column";
|
|
6
6
|
|
|
7
|
+
interface MinimalPhysicalTable {
|
|
8
|
+
[TABLE_SYMBOLS.PARTITION_KEY]: Column;
|
|
9
|
+
[TABLE_SYMBOLS.SORT_KEY]?: Column;
|
|
10
|
+
}
|
|
11
|
+
|
|
7
12
|
/**
|
|
8
13
|
* Parser for DynamoDB item collections (Single-Table Design).
|
|
9
14
|
*/
|
|
@@ -72,11 +77,10 @@ export class ItemCollectionParser {
|
|
|
72
77
|
string,
|
|
73
78
|
KeyStrategy
|
|
74
79
|
>;
|
|
75
|
-
const physicalTable = entity[ENTITY_SYMBOLS.PHYSICAL_TABLE] as
|
|
80
|
+
const physicalTable = entity[ENTITY_SYMBOLS.PHYSICAL_TABLE] as unknown as MinimalPhysicalTable;
|
|
76
81
|
|
|
77
|
-
const pkName =
|
|
78
|
-
const skName =
|
|
79
|
-
?.name;
|
|
82
|
+
const pkName = physicalTable[TABLE_SYMBOLS.PARTITION_KEY].name;
|
|
83
|
+
const skName = physicalTable[TABLE_SYMBOLS.SORT_KEY]?.name;
|
|
80
84
|
|
|
81
85
|
const pkMatch = this.matchStrategy(item[pkName], strategies.pk);
|
|
82
86
|
const skMatch = skName
|
package/src/core/relations.ts
CHANGED
|
@@ -54,7 +54,46 @@ export interface RelationsDefinition<TEntity extends Entity = Entity> {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
/**
|
|
57
|
-
*
|
|
57
|
+
* Definition of relations for multiple entities in a schema.
|
|
58
|
+
*/
|
|
59
|
+
export interface MultiRelationsDefinition<TSchema extends Record<string, Entity> = Record<string, Entity>> {
|
|
60
|
+
/**
|
|
61
|
+
* The schema containing all entities.
|
|
62
|
+
*/
|
|
63
|
+
schema: TSchema;
|
|
64
|
+
/**
|
|
65
|
+
* Map of entity names to their relation configurations.
|
|
66
|
+
*/
|
|
67
|
+
definitions: {
|
|
68
|
+
[K in keyof TSchema]?: Record<string, Relation>;
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Helpers provided to the defineRelations callback.
|
|
74
|
+
*/
|
|
75
|
+
export type RelationsHelpers<TSchema extends Record<string, Entity>> = {
|
|
76
|
+
/**
|
|
77
|
+
* Define a one-to-one relationship.
|
|
78
|
+
*/
|
|
79
|
+
one: {
|
|
80
|
+
[K in keyof TSchema]: (config?: Omit<RelationConfig, "to">) => Relation<"one">;
|
|
81
|
+
};
|
|
82
|
+
/**
|
|
83
|
+
* Define a one-to-many relationship.
|
|
84
|
+
*/
|
|
85
|
+
many: {
|
|
86
|
+
[K in keyof TSchema]: (config?: Omit<RelationConfig, "to">) => Relation<"many">;
|
|
87
|
+
};
|
|
88
|
+
} & {
|
|
89
|
+
/**
|
|
90
|
+
* Access an entity in the schema to get its columns.
|
|
91
|
+
*/
|
|
92
|
+
[K in keyof TSchema]: TSchema[K];
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Callback function to define relations for a single entity.
|
|
58
97
|
*/
|
|
59
98
|
export type RelationsCallback = (helpers: {
|
|
60
99
|
/**
|
|
@@ -68,7 +107,16 @@ export type RelationsCallback = (helpers: {
|
|
|
68
107
|
}) => Record<string, Relation>;
|
|
69
108
|
|
|
70
109
|
/**
|
|
71
|
-
*
|
|
110
|
+
* Callback function to define relations for multiple entities.
|
|
111
|
+
*/
|
|
112
|
+
export type MultiRelationsCallback<TSchema extends Record<string, Entity>> = (
|
|
113
|
+
helpers: RelationsHelpers<TSchema>
|
|
114
|
+
) => {
|
|
115
|
+
[K in keyof TSchema]?: Record<string, Relation>;
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Defines relationships for a single entity.
|
|
72
120
|
*
|
|
73
121
|
* @example
|
|
74
122
|
* ```ts
|
|
@@ -76,21 +124,87 @@ export type RelationsCallback = (helpers: {
|
|
|
76
124
|
* posts: many(posts),
|
|
77
125
|
* }));
|
|
78
126
|
* ```
|
|
127
|
+
*
|
|
128
|
+
* @param entity The source entity.
|
|
129
|
+
* @param relations A callback function to define relations using provided helpers.
|
|
130
|
+
* @returns A relations definition for the entity.
|
|
79
131
|
*/
|
|
80
132
|
export function defineRelations<TEntity extends Entity>(
|
|
81
133
|
entity: TEntity,
|
|
82
134
|
relations: RelationsCallback
|
|
83
|
-
): RelationsDefinition<TEntity
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
135
|
+
): RelationsDefinition<TEntity>;
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Defines relationships for multiple entities in a centralized schema-aware way.
|
|
139
|
+
* This approach helps resolve circular dependencies between entities.
|
|
140
|
+
*
|
|
141
|
+
* @example
|
|
142
|
+
* ```ts
|
|
143
|
+
* export const relations = defineRelations({ users, posts }, (r) => ({
|
|
144
|
+
* users: {
|
|
145
|
+
* posts: r.many.posts(),
|
|
146
|
+
* },
|
|
147
|
+
* posts: {
|
|
148
|
+
* author: r.one.users({
|
|
149
|
+
* fields: [r.posts.authorId],
|
|
150
|
+
* references: [r.users.id],
|
|
151
|
+
* }),
|
|
152
|
+
* },
|
|
153
|
+
* }));
|
|
154
|
+
* ```
|
|
155
|
+
*
|
|
156
|
+
* @param schema An object mapping names to entity definitions.
|
|
157
|
+
* @param relations A callback function to define relations for all entities in the schema.
|
|
158
|
+
* @returns A multi-entity relations definition.
|
|
159
|
+
*/
|
|
160
|
+
export function defineRelations<TSchema extends Record<string, Entity>>(
|
|
161
|
+
schema: TSchema,
|
|
162
|
+
relations: MultiRelationsCallback<TSchema>
|
|
163
|
+
): MultiRelationsDefinition<TSchema>;
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Implementation of defineRelations.
|
|
167
|
+
*/
|
|
168
|
+
export function defineRelations(
|
|
169
|
+
first: Entity | Record<string, Entity>,
|
|
170
|
+
callback: Function
|
|
171
|
+
): any {
|
|
172
|
+
if (first instanceof Entity) {
|
|
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 }),
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
return {
|
|
180
|
+
entity: first,
|
|
181
|
+
config,
|
|
182
|
+
[RELATION_SYMBOLS.RELATION_CONFIG]: true
|
|
183
|
+
};
|
|
184
|
+
} else {
|
|
185
|
+
// Multi-entity mode
|
|
186
|
+
const schema = first as Record<string, Entity>;
|
|
187
|
+
|
|
188
|
+
// Build helpers
|
|
189
|
+
const helpers: any = {
|
|
190
|
+
one: {},
|
|
191
|
+
many: {},
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
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[key] = entity;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
const definitions = callback(helpers);
|
|
201
|
+
|
|
202
|
+
return {
|
|
203
|
+
schema,
|
|
204
|
+
definitions,
|
|
205
|
+
[RELATION_SYMBOLS.RELATION_CONFIG]: true
|
|
206
|
+
};
|
|
207
|
+
}
|
|
94
208
|
}
|
|
95
209
|
|
|
96
210
|
/**
|
|
@@ -129,18 +243,32 @@ export function extractMetadata(schema: Record<string, unknown>): InternalRelati
|
|
|
129
243
|
// Second pass: identify relations
|
|
130
244
|
for (const [, value] of Object.entries(schema)) {
|
|
131
245
|
if (value && (value as any)[RELATION_SYMBOLS.RELATION_CONFIG]) {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
...meta.relations,
|
|
142
|
-
|
|
143
|
-
|
|
246
|
+
if ((value as any).entity) {
|
|
247
|
+
// Single entity definition
|
|
248
|
+
const definition = value as RelationsDefinition;
|
|
249
|
+
const entityEntry = Object.entries(metadata.entities).find(
|
|
250
|
+
([_, meta]) => meta.entity === definition.entity
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
if (entityEntry) {
|
|
254
|
+
const [, meta] = entityEntry;
|
|
255
|
+
meta.relations = { ...meta.relations, ...definition.config };
|
|
256
|
+
}
|
|
257
|
+
} else if ((value as any).definitions) {
|
|
258
|
+
// Multi-entity definition
|
|
259
|
+
const multiDef = value as MultiRelationsDefinition;
|
|
260
|
+
for (const [entityName, relations] of Object.entries(multiDef.definitions)) {
|
|
261
|
+
// Try to find the entity in our metadata by matching the entity from the schema
|
|
262
|
+
const entityInSchema = multiDef.schema[entityName];
|
|
263
|
+
const metaEntry = Object.entries(metadata.entities).find(
|
|
264
|
+
([_, meta]) => meta.entity === entityInSchema
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
if (metaEntry && relations) {
|
|
268
|
+
const [, meta] = metaEntry;
|
|
269
|
+
meta.relations = { ...meta.relations, ...relations };
|
|
270
|
+
}
|
|
271
|
+
}
|
|
144
272
|
}
|
|
145
273
|
}
|
|
146
274
|
}
|
package/src/core/retry.ts
CHANGED
|
@@ -44,14 +44,15 @@ export class RetryHandler {
|
|
|
44
44
|
throw lastError;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
-
private isRetryable(error:
|
|
48
|
-
if (!error) return false;
|
|
47
|
+
private isRetryable(error: unknown): boolean {
|
|
48
|
+
if (!error || typeof error !== 'object') return false;
|
|
49
49
|
|
|
50
|
-
|
|
50
|
+
const err = error as { name?: string; $metadata?: { httpStatusCode?: number } };
|
|
51
|
+
if (err.name && RETRYABLE_ERRORS.has(err.name)) {
|
|
51
52
|
return true;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
if (
|
|
55
|
+
if (err.$metadata?.httpStatusCode && RETRYABLE_STATUS_CODES.has(err.$metadata.httpStatusCode)) {
|
|
55
56
|
return true;
|
|
56
57
|
}
|
|
57
58
|
|
package/src/core/snapshot.ts
CHANGED
|
@@ -153,7 +153,7 @@ function physicalTableToSnapshot(table: PhysicalTable, entities: Entity[]): Tabl
|
|
|
153
153
|
|
|
154
154
|
const attributeDefinitions = Array.from(attributeDefinitionsMap.entries()).map(([name, type]) => ({
|
|
155
155
|
AttributeName: name,
|
|
156
|
-
AttributeType: type as
|
|
156
|
+
AttributeType: type as "S" | "N" | "B"
|
|
157
157
|
})).sort((a, b) => a.AttributeName.localeCompare(b.AttributeName));
|
|
158
158
|
|
|
159
159
|
gsis.sort((a, b) => (a.IndexName || "").localeCompare(b.IndexName || ""));
|
package/src/core/table.ts
CHANGED
|
@@ -189,6 +189,24 @@ export type UpdateTableConfig<
|
|
|
189
189
|
export type AnyTable<TPartial extends Partial<PhysicalTableConfig> = object> =
|
|
190
190
|
PhysicalTable<UpdateTableConfig<PhysicalTableConfig, TPartial>>;
|
|
191
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Defines a logical entity that maps to items within a DynamoDB table.
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```ts
|
|
197
|
+
* const users = dynamoEntity(table, "users", {
|
|
198
|
+
* id: string("id"),
|
|
199
|
+
* name: string("name"),
|
|
200
|
+
* email: string("email"),
|
|
201
|
+
* });
|
|
202
|
+
* ```
|
|
203
|
+
*
|
|
204
|
+
* @param table The physical table definition this entity belongs to.
|
|
205
|
+
* @param name The unique name of the entity (used for typing and potentially in single-table design discriminators).
|
|
206
|
+
* @param columns A map of column definitions or a callback to define columns.
|
|
207
|
+
* @param strategies Optional configuration for key generation strategies (PK/SK construction).
|
|
208
|
+
* @returns The entity definition with strict typing.
|
|
209
|
+
*/
|
|
192
210
|
export function dynamoEntity<
|
|
193
211
|
TName extends string,
|
|
194
212
|
TTable extends PhysicalTable,
|
|
@@ -252,6 +270,21 @@ export function dynamoEntity<
|
|
|
252
270
|
}>;
|
|
253
271
|
}
|
|
254
272
|
|
|
273
|
+
/**
|
|
274
|
+
* Defines a physical DynamoDB table schema.
|
|
275
|
+
*
|
|
276
|
+
* @example
|
|
277
|
+
* ```ts
|
|
278
|
+
* const table = dynamoTable("my-app-table", {
|
|
279
|
+
* pk: string("pk"),
|
|
280
|
+
* sk: string("sk"),
|
|
281
|
+
* });
|
|
282
|
+
* ```
|
|
283
|
+
*
|
|
284
|
+
* @param name The actual name of the table in DynamoDB (or a reference name).
|
|
285
|
+
* @param config The table configuration, including primary key (pk) and sort key (sk) definitions.
|
|
286
|
+
* @returns A PhysicalTable instance representing the table schema.
|
|
287
|
+
*/
|
|
255
288
|
export function dynamoTable<
|
|
256
289
|
TTableName extends string,
|
|
257
290
|
TConfig extends PhysicalTableConfig,
|