@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.
@@ -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
  }
@@ -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
  }
@@ -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
  }
@@ -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
  }
@@ -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
  }
@@ -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: any, options?: any): Promise<any>;
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: any, options?: any): Promise<any> {
16
- return this.retryHandler.execute(() => this.client.send(command, options));
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
  }
@@ -18,7 +18,7 @@ export interface CancellationReason {
18
18
  index: number;
19
19
  code: string;
20
20
  message?: string;
21
- item?: Record<string, any>;
21
+ item?: Record<string, unknown>;
22
22
  }
23
23
 
24
24
  /**
@@ -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 any;
80
+ const physicalTable = entity[ENTITY_SYMBOLS.PHYSICAL_TABLE] as unknown as MinimalPhysicalTable;
76
81
 
77
- const pkName = (physicalTable[TABLE_SYMBOLS.PARTITION_KEY] as Column).name;
78
- const skName = (physicalTable[TABLE_SYMBOLS.SORT_KEY] as Column | undefined)
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
@@ -54,7 +54,46 @@ export interface RelationsDefinition<TEntity extends Entity = Entity> {
54
54
  }
55
55
 
56
56
  /**
57
- * Callback function to define relations.
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
- * Define relations for an entity.
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
- const config = relations({
85
- one: (to, config) => new Relation("one", { to, ...config }),
86
- many: (to, config) => new Relation("many", { to, ...config }),
87
- });
88
-
89
- return {
90
- entity,
91
- config,
92
- [RELATION_SYMBOLS.RELATION_CONFIG]: true
93
- } as unknown as RelationsDefinition<TEntity>;
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
- const definition = value as RelationsDefinition;
133
- // Find the key for this entity in the metadata
134
- const entityEntry = Object.entries(metadata.entities).find(
135
- ([_, meta]) => meta.entity === definition.entity
136
- );
137
-
138
- if (entityEntry) {
139
- const [, meta] = entityEntry;
140
- meta.relations = {
141
- ...meta.relations,
142
- ...definition.config,
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: any): boolean {
48
- if (!error) return false;
47
+ private isRetryable(error: unknown): boolean {
48
+ if (!error || typeof error !== 'object') return false;
49
49
 
50
- if (RETRYABLE_ERRORS.has(error.name)) {
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 (error.$metadata?.httpStatusCode && RETRYABLE_STATUS_CODES.has(error.$metadata.httpStatusCode)) {
55
+ if (err.$metadata?.httpStatusCode && RETRYABLE_STATUS_CODES.has(err.$metadata.httpStatusCode)) {
55
56
  return true;
56
57
  }
57
58
 
@@ -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 any
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,