@aurios/mizzle 1.1.1 → 1.1.3

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.
Files changed (116) hide show
  1. package/.turbo/turbo-build.log +37 -0
  2. package/LICENSE +21 -0
  3. package/README.md +57 -0
  4. package/dist/chunk-AQVECMXP.js +1 -0
  5. package/dist/chunk-DU7UPWBW.js +1 -0
  6. package/dist/chunk-GPYZK4WY.js +1 -0
  7. package/dist/chunk-NPPZW6VT.js +1 -0
  8. package/dist/chunk-TOYV2M4M.js +1 -0
  9. package/dist/chunk-UM3YF5EC.js +1 -0
  10. package/dist/columns.d.ts +1 -0
  11. package/dist/columns.js +1 -0
  12. package/dist/db-zHIHBm1E.d.ts +815 -0
  13. package/dist/db.d.ts +3 -0
  14. package/dist/db.js +1 -0
  15. package/dist/diff.d.ts +18 -0
  16. package/dist/diff.js +1 -0
  17. package/dist/index.d.ts +42 -0
  18. package/dist/index.js +1 -0
  19. package/dist/introspection.d.ts +7 -0
  20. package/dist/introspection.js +1 -0
  21. package/dist/operators-BVreW0ky.d.ts +719 -0
  22. package/dist/snapshot.d.ts +24 -0
  23. package/dist/snapshot.js +1 -0
  24. package/dist/table.d.ts +1 -0
  25. package/dist/table.js +1 -0
  26. package/dist/transaction-RE7LXTGV.js +1 -0
  27. package/package.json +73 -24
  28. package/src/builders/base.ts +53 -56
  29. package/src/builders/batch-get.ts +63 -58
  30. package/src/builders/batch-write.ts +81 -78
  31. package/src/builders/delete.ts +46 -53
  32. package/src/builders/insert.ts +158 -150
  33. package/src/builders/query-promise.ts +26 -35
  34. package/src/builders/relational-builder.ts +214 -191
  35. package/src/builders/select.ts +250 -238
  36. package/src/builders/transaction.ts +170 -151
  37. package/src/builders/update.ts +198 -191
  38. package/src/columns/binary-set.ts +29 -38
  39. package/src/columns/binary.ts +25 -35
  40. package/src/columns/boolean.ts +25 -30
  41. package/src/columns/date.ts +57 -64
  42. package/src/columns/index.ts +15 -15
  43. package/src/columns/json.ts +39 -48
  44. package/src/columns/list.ts +26 -36
  45. package/src/columns/map.ts +26 -34
  46. package/src/columns/number-set.ts +29 -38
  47. package/src/columns/number.ts +33 -40
  48. package/src/columns/string-set.ts +38 -47
  49. package/src/columns/string.ts +37 -49
  50. package/src/columns/uuid.ts +26 -33
  51. package/src/core/client.ts +9 -9
  52. package/src/core/column-builder.ts +194 -220
  53. package/src/core/column.ts +127 -135
  54. package/src/core/diff.ts +40 -34
  55. package/src/core/errors.ts +20 -17
  56. package/src/core/introspection.ts +62 -58
  57. package/src/core/operations.ts +17 -23
  58. package/src/core/parser.ts +82 -88
  59. package/src/core/relations.ts +165 -152
  60. package/src/core/retry.ts +52 -52
  61. package/src/core/snapshot.ts +131 -130
  62. package/src/core/strategies.ts +222 -214
  63. package/src/core/table.ts +189 -202
  64. package/src/core/validation.ts +52 -52
  65. package/src/db.ts +216 -213
  66. package/src/expressions/actions.ts +26 -26
  67. package/src/expressions/builder.ts +62 -54
  68. package/src/expressions/operators.ts +48 -48
  69. package/src/expressions/update-builder.ts +79 -75
  70. package/src/index.ts +2 -1
  71. package/src/indexes.ts +8 -8
  72. package/test/batch-resilience.test.ts +138 -0
  73. package/test/builders/delete.test.ts +100 -0
  74. package/test/builders/insert.test.ts +216 -0
  75. package/test/builders/relational-types.test.ts +55 -0
  76. package/test/builders/relational.integration.test.ts +291 -0
  77. package/test/builders/relational.test.ts +66 -0
  78. package/test/builders/select.test.ts +411 -0
  79. package/test/builders/transaction-errors.test.ts +46 -0
  80. package/test/builders/transaction-execution.test.ts +99 -0
  81. package/test/builders/transaction-proxy.test.ts +41 -0
  82. package/test/builders/update-expression.test.ts +106 -0
  83. package/test/builders/update.test.ts +179 -0
  84. package/test/core/diff.test.ts +152 -0
  85. package/test/core/expressions.test.ts +64 -0
  86. package/test/core/introspection.test.ts +47 -0
  87. package/test/core/parser.test.ts +69 -0
  88. package/test/core/snapshot-gen.test.ts +155 -0
  89. package/test/core/snapshot.test.ts +52 -0
  90. package/test/date-column.test.ts +159 -0
  91. package/test/fluent-writes.integration.test.ts +148 -0
  92. package/test/integration-retry.test.ts +77 -0
  93. package/test/integration.test.ts +105 -0
  94. package/test/item-size-error.test.ts +16 -0
  95. package/test/item-size-validation.test.ts +82 -0
  96. package/test/item-size.test.ts +47 -0
  97. package/test/iterator-pagination.integration.test.ts +132 -0
  98. package/test/jsdoc-builders.test.ts +55 -0
  99. package/test/jsdoc-schema.test.ts +107 -0
  100. package/test/json-column.test.ts +51 -0
  101. package/test/metadata.test.ts +54 -0
  102. package/test/mizzle-package.test.ts +20 -0
  103. package/test/relational-centralized.test.ts +83 -0
  104. package/test/relational-definition.test.ts +75 -0
  105. package/test/relational-init.test.ts +30 -0
  106. package/test/relational-proxy.test.ts +52 -0
  107. package/test/relations.test.ts +45 -0
  108. package/test/resilience-config.test.ts +34 -0
  109. package/test/retry-handler.test.ts +63 -0
  110. package/test/transaction.integration.test.ts +153 -0
  111. package/test/unified-select.integration.test.ts +153 -0
  112. package/test/unified-update.integration.test.ts +139 -0
  113. package/test/update.integration.test.ts +132 -0
  114. package/tsconfig.json +12 -9
  115. package/tsup.config.ts +11 -11
  116. package/vitest.config.ts +8 -0
package/src/core/table.ts CHANGED
@@ -1,197 +1,187 @@
1
- import { ENTITY_SYMBOLS, INFER_MODE, TABLE_SYMBOLS } from "@mizzle/shared";
2
- import type { Simplify, Update } from "@mizzle/shared";
1
+ import { ENTITY_SYMBOLS, INFER_MODE, TABLE_SYMBOLS } from "@repo/shared";
2
+ import type { Simplify, Update } from "@repo/shared";
3
3
  import { Column, type GetColumnData } from "./column";
4
- import type {
5
- BuildColumns,
6
- ColumnBuider,
7
- ColumnBuilderBase,
8
- } from "./column-builder";
4
+ import type { BuildColumns, ColumnBuider, ColumnBuilderBase } from "./column-builder";
9
5
  import { getColumnBuilders, type ColumnsBuilder } from "../columns";
10
6
  import type { IndexBuilder } from "../indexes";
11
7
  import type { OpitionalKeyOnly, RequiredKeyOnly } from "./operations";
12
8
  import type { KeyStrategy } from "./strategies";
13
9
 
14
- type IndexStrategyConfig<TIndex extends IndexBuilder> =
15
- TIndex["type"] extends "lsi"
16
- ? { sk: KeyStrategy | Column }
17
- : {
18
- pk: KeyStrategy | Column;
19
- sk?: KeyStrategy | Column;
20
- };
21
-
22
- type IndexesStrategy<
23
- TIndexes extends Record<string, IndexBuilder> | undefined,
24
- > =
25
- TIndexes extends Record<string, IndexBuilder>
26
- ? { [K in keyof TIndexes]?: IndexStrategyConfig<TIndexes[K]> }
27
- : object;
10
+ type IndexStrategyConfig<TIndex extends IndexBuilder> = TIndex["type"] extends "lsi"
11
+ ? { sk: KeyStrategy | Column }
12
+ : {
13
+ pk: KeyStrategy | Column;
14
+ sk?: KeyStrategy | Column;
15
+ };
16
+
17
+ type IndexesStrategy<TIndexes extends Record<string, IndexBuilder> | undefined> =
18
+ TIndexes extends Record<string, IndexBuilder>
19
+ ? { [K in keyof TIndexes]?: IndexStrategyConfig<TIndexes[K]> }
20
+ : object;
28
21
 
29
22
  export type StrategyCallback<
30
- TColumns extends Record<string, Column>,
31
- TPhysicalConfig extends PhysicalTableConfig,
23
+ TColumns extends Record<string, Column>,
24
+ TPhysicalConfig extends PhysicalTableConfig,
32
25
  > = (columns: TColumns) => {
33
- [K in keyof TPhysicalConfig as K extends "pk" | "sk"
34
- ? K
35
- : never]: KeyStrategy | Column;
26
+ [K in keyof TPhysicalConfig as K extends "pk" | "sk" ? K : never]: KeyStrategy | Column;
36
27
  } & IndexesStrategy<TPhysicalConfig["indexes"]>;
37
28
 
38
29
  export interface EntityConfig<TColumn extends Column = Column> {
39
- name: string;
40
- table: PhysicalTable;
41
- columns: Record<string, TColumn>;
30
+ name: string;
31
+ table: PhysicalTable;
32
+ columns: Record<string, TColumn>;
42
33
  }
43
34
 
44
35
  export interface PhysicalTableConfig {
45
- pk: ColumnBuilderBase;
46
- sk?: ColumnBuilderBase;
47
- indexes?: Record<string, IndexBuilder>;
36
+ pk: ColumnBuilderBase;
37
+ sk?: ColumnBuilderBase;
38
+ indexes?: Record<string, IndexBuilder>;
48
39
  }
49
40
 
50
- export class PhysicalTable<
51
- T extends PhysicalTableConfig = PhysicalTableConfig,
52
- > {
53
- declare readonly _: {
54
- config: T;
55
- name: string;
56
- partitionKey: Column;
57
- sortKey?: Column;
58
- indexes: T["indexes"];
59
- };
60
-
61
- /** @internal */
62
- [TABLE_SYMBOLS.TABLE_NAME]: string = "";
63
-
64
- /** @internal */
65
- [TABLE_SYMBOLS.INDEXES]: T["indexes"] = undefined;
66
-
67
- /** @internal */
68
- [TABLE_SYMBOLS.PARTITION_KEY]: Column = {} as Column;
69
-
70
- /** @internal */
71
- [TABLE_SYMBOLS.SORT_KEY]?: Column = undefined;
72
-
73
- static readonly Symbol = TABLE_SYMBOLS;
74
-
75
- constructor(name: string, config: T) {
76
- this[TABLE_SYMBOLS.TABLE_NAME] = name;
77
- this[TABLE_SYMBOLS.PARTITION_KEY] = (config.pk as ColumnBuider).build(
78
- this as unknown as AnyTable,
79
- );
80
- this[TABLE_SYMBOLS.SORT_KEY] = config.sk
81
- ? (config.sk as ColumnBuider).build(this as unknown as AnyTable)
82
- : undefined;
83
- this[TABLE_SYMBOLS.INDEXES] = config.indexes;
84
- }
41
+ export class PhysicalTable<T extends PhysicalTableConfig = PhysicalTableConfig> {
42
+ declare readonly _: {
43
+ config: T;
44
+ name: string;
45
+ partitionKey: Column;
46
+ sortKey?: Column;
47
+ indexes: T["indexes"];
48
+ };
49
+
50
+ /** @internal */
51
+ [TABLE_SYMBOLS.TABLE_NAME]: string = "";
52
+
53
+ /** @internal */
54
+ [TABLE_SYMBOLS.INDEXES]: T["indexes"] = undefined;
55
+
56
+ /** @internal */
57
+ [TABLE_SYMBOLS.PARTITION_KEY]: Column = {} as Column;
58
+
59
+ /** @internal */
60
+ [TABLE_SYMBOLS.SORT_KEY]?: Column = undefined;
61
+
62
+ static readonly Symbol = TABLE_SYMBOLS;
63
+
64
+ constructor(name: string, config: T) {
65
+ this[TABLE_SYMBOLS.TABLE_NAME] = name;
66
+ this[TABLE_SYMBOLS.PARTITION_KEY] = (config.pk as ColumnBuider).build(
67
+ this as unknown as AnyTable,
68
+ );
69
+ this[TABLE_SYMBOLS.SORT_KEY] = config.sk
70
+ ? (config.sk as ColumnBuider).build(this as unknown as AnyTable)
71
+ : undefined;
72
+ this[TABLE_SYMBOLS.INDEXES] = config.indexes;
73
+ }
85
74
  }
86
75
 
87
76
  export class Entity<T extends EntityConfig = EntityConfig> {
88
- declare readonly _: {
89
- readonly config: T;
90
- readonly name: T["name"];
91
- readonly table: T["table"];
92
- readonly columns: T["columns"];
93
- readonly strategies: Record<string, KeyStrategy>;
94
- readonly inferSelect: InferSelectModel<Entity<T>>;
95
- readonly inferInsert: InferInsertModel<Entity<T>>;
96
- };
97
-
98
- declare readonly $inferSelect: InferSelectModel<Entity<T>>;
99
- declare readonly $inferInsert: InferInsertModel<Entity<T>>;
100
-
101
- /** @internal */
102
- [ENTITY_SYMBOLS.ENTITY_NAME]: string = "";
103
-
104
- /** @internal */
105
- [ENTITY_SYMBOLS.PHYSICAL_TABLE]: T["table"] = {} as T["table"];
106
-
107
- /** @internal */
108
- [ENTITY_SYMBOLS.COLUMNS]: T["columns"] = {} as T["columns"];
109
-
110
- [ENTITY_SYMBOLS.ENTITY_STRATEGY]: Record<string, KeyStrategy> = {};
111
-
112
- static readonly Symbol = ENTITY_SYMBOLS;
113
-
114
- constructor(
115
- name: T["name"],
116
- table: T["table"],
117
- columns: T["columns"],
118
- strategies: Record<string, KeyStrategy>,
119
- ) {
120
- this[ENTITY_SYMBOLS.ENTITY_NAME] = name;
121
- this[ENTITY_SYMBOLS.PHYSICAL_TABLE] = table;
122
- this[ENTITY_SYMBOLS.COLUMNS] = columns;
123
- this[ENTITY_SYMBOLS.ENTITY_STRATEGY] = strategies;
124
- }
77
+ declare readonly _: {
78
+ readonly config: T;
79
+ readonly name: T["name"];
80
+ readonly table: T["table"];
81
+ readonly columns: T["columns"];
82
+ readonly strategies: Record<string, KeyStrategy>;
83
+ readonly inferSelect: InferSelectModel<Entity<T>>;
84
+ readonly inferInsert: InferInsertModel<Entity<T>>;
85
+ };
86
+
87
+ declare readonly $inferSelect: InferSelectModel<Entity<T>>;
88
+ declare readonly $inferInsert: InferInsertModel<Entity<T>>;
89
+
90
+ /** @internal */
91
+ [ENTITY_SYMBOLS.ENTITY_NAME]: string = "";
92
+
93
+ /** @internal */
94
+ [ENTITY_SYMBOLS.PHYSICAL_TABLE]: T["table"] = {} as T["table"];
95
+
96
+ /** @internal */
97
+ [ENTITY_SYMBOLS.COLUMNS]: T["columns"] = {} as T["columns"];
98
+
99
+ [ENTITY_SYMBOLS.ENTITY_STRATEGY]: Record<string, KeyStrategy> = {};
100
+
101
+ static readonly Symbol = ENTITY_SYMBOLS;
102
+
103
+ constructor(
104
+ name: T["name"],
105
+ table: T["table"],
106
+ columns: T["columns"],
107
+ strategies: Record<string, KeyStrategy>,
108
+ ) {
109
+ this[ENTITY_SYMBOLS.ENTITY_NAME] = name;
110
+ this[ENTITY_SYMBOLS.PHYSICAL_TABLE] = table;
111
+ this[ENTITY_SYMBOLS.COLUMNS] = columns;
112
+ this[ENTITY_SYMBOLS.ENTITY_STRATEGY] = strategies;
113
+ }
125
114
  }
126
115
 
127
116
  export type MapColumnName<
128
- TName extends string,
129
- TColumn extends Column,
130
- TDBColumnNames extends boolean,
117
+ TName extends string,
118
+ TColumn extends Column,
119
+ TDBColumnNames extends boolean,
131
120
  > = TDBColumnNames extends true ? TColumn["_"]["name"] : TName;
132
121
 
133
122
  export type InferModelFromColumns<
134
- TColumns extends Record<string, Column>,
135
- TInferMode extends "select" | "insert" = "select",
136
- TConfig extends { dbColumnNames: boolean; override?: boolean } = {
137
- dbColumnNames: false;
138
- override: false;
139
- },
123
+ TColumns extends Record<string, Column>,
124
+ TInferMode extends "select" | "insert" = "select",
125
+ TConfig extends { dbColumnNames: boolean; override?: boolean } = {
126
+ dbColumnNames: false;
127
+ override: false;
128
+ },
140
129
  > = Simplify<
141
- {
142
- [Key in keyof TColumns & string as RequiredKeyOnly<
143
- MapColumnName<Key, TColumns[Key], TConfig["dbColumnNames"]>,
144
- TColumns[Key],
145
- TInferMode
146
- >]: GetColumnData<TColumns[Key], typeof INFER_MODE.QUERY>;
147
- } & {
148
- [Key in keyof TColumns & string as OpitionalKeyOnly<
149
- MapColumnName<Key, TColumns[Key], TConfig["dbColumnNames"]>,
150
- TColumns[Key],
151
- TInferMode
152
- >]?: GetColumnData<TColumns[Key], typeof INFER_MODE.QUERY> | undefined;
153
- }
130
+ {
131
+ [Key in keyof TColumns & string as RequiredKeyOnly<
132
+ MapColumnName<Key, TColumns[Key], TConfig["dbColumnNames"]>,
133
+ TColumns[Key],
134
+ TInferMode
135
+ >]: GetColumnData<TColumns[Key], typeof INFER_MODE.QUERY>;
136
+ } & {
137
+ [Key in keyof TColumns & string as OpitionalKeyOnly<
138
+ MapColumnName<Key, TColumns[Key], TConfig["dbColumnNames"]>,
139
+ TColumns[Key],
140
+ TInferMode
141
+ >]?: GetColumnData<TColumns[Key], typeof INFER_MODE.QUERY> | undefined;
142
+ }
154
143
  >;
155
144
 
156
145
  export type InferSelectModel<
157
- TTable extends Entity,
158
- TConfig extends { dbColumnNames: boolean } = { dbColumnNames: false },
146
+ TTable extends Entity,
147
+ TConfig extends { dbColumnNames: boolean } = { dbColumnNames: false },
159
148
  > = InferModelFromColumns<TTable["_"]["columns"], "select", TConfig>;
160
149
 
161
150
  export type InferInsertModel<
162
- TTable extends Entity,
163
- TConfig extends { dbColumnNames: boolean; override?: boolean } = {
164
- dbColumnNames: false;
165
- override: false;
166
- },
151
+ TTable extends Entity,
152
+ TConfig extends { dbColumnNames: boolean; override?: boolean } = {
153
+ dbColumnNames: false;
154
+ override: false;
155
+ },
167
156
  > = InferModelFromColumns<TTable["_"]["columns"], "insert", TConfig>;
168
157
 
169
158
  export type InferSelectedModel<
170
- TTable extends Entity,
171
- TConfig extends { dbColumnNames: boolean } = { dbColumnNames: false },
159
+ TTable extends Entity,
160
+ TConfig extends { dbColumnNames: boolean } = { dbColumnNames: false },
172
161
  > = InferSelectModel<TTable, TConfig>;
173
162
 
174
163
  export type TableDefinition<T extends EntityConfig = EntityConfig> = Entity<T> & {
175
- columns: T["columns"];
164
+ columns: T["columns"];
176
165
  };
177
166
 
178
167
  export type AtomicValues<T extends Entity> = Partial<InferInsertModel<T>>;
179
168
 
180
169
  export type EntityWithColumns<T extends EntityConfig> = Entity<T> & {
181
- [Key in keyof T["columns"]]: T["columns"][Key];
170
+ [Key in keyof T["columns"]]: T["columns"][Key];
182
171
  };
183
172
 
184
173
  export type UpdateTableConfig<
185
- T extends PhysicalTableConfig,
186
- TUpdate extends Partial<PhysicalTableConfig>,
174
+ T extends PhysicalTableConfig,
175
+ TUpdate extends Partial<PhysicalTableConfig>,
187
176
  > = Required<Update<T, TUpdate>>;
188
177
 
189
- export type AnyTable<TPartial extends Partial<PhysicalTableConfig> = object> =
190
- PhysicalTable<UpdateTableConfig<PhysicalTableConfig, TPartial>>;
178
+ export type AnyTable<TPartial extends Partial<PhysicalTableConfig> = object> = PhysicalTable<
179
+ UpdateTableConfig<PhysicalTableConfig, TPartial>
180
+ >;
191
181
 
192
182
  /**
193
183
  * Defines a logical entity that maps to items within a DynamoDB table.
194
- *
184
+ *
195
185
  * @example
196
186
  * ```ts
197
187
  * const users = dynamoEntity(table, "users", {
@@ -200,7 +190,7 @@ export type AnyTable<TPartial extends Partial<PhysicalTableConfig> = object> =
200
190
  * email: string("email"),
201
191
  * });
202
192
  * ```
203
- *
193
+ *
204
194
  * @param table The physical table definition this entity belongs to.
205
195
  * @param name The unique name of the entity (used for typing and potentially in single-table design discriminators).
206
196
  * @param columns A map of column definitions or a callback to define columns.
@@ -208,71 +198,68 @@ export type AnyTable<TPartial extends Partial<PhysicalTableConfig> = object> =
208
198
  * @returns The entity definition with strict typing.
209
199
  */
210
200
  export function dynamoEntity<
211
- TName extends string,
212
- TTable extends PhysicalTable,
213
- TColumnsMap extends Record<string, ColumnBuilderBase>,
201
+ TName extends string,
202
+ TTable extends PhysicalTable,
203
+ TColumnsMap extends Record<string, ColumnBuilderBase>,
214
204
  >(
215
- table: TTable,
216
- name: TName,
217
- columns: TColumnsMap | ((columnsTypes: ColumnsBuilder) => TColumnsMap),
218
- strategies?: StrategyCallback<
219
- BuildColumns<TName, TColumnsMap>,
220
- TTable["_"]["config"]
221
- >,
205
+ table: TTable,
206
+ name: TName,
207
+ columns: TColumnsMap | ((columnsTypes: ColumnsBuilder) => TColumnsMap),
208
+ strategies?: StrategyCallback<BuildColumns<TName, TColumnsMap>, TTable["_"]["config"]>,
222
209
  ): EntityWithColumns<{
223
- name: TName;
224
- table: TTable;
225
- columns: BuildColumns<TName, TColumnsMap>;
210
+ name: TName;
211
+ table: TTable;
212
+ columns: BuildColumns<TName, TColumnsMap>;
226
213
  }> {
227
- const parsedColumns: TColumnsMap =
228
- typeof columns === "function" ? columns(getColumnBuilders()) : columns;
229
-
230
- const builtColumns = Object.fromEntries(
231
- Object.entries(parsedColumns).map(([name, colBuilderBase]) => {
232
- const colBuilder = colBuilderBase as ColumnBuider;
233
- colBuilder.setName(name);
234
- const column = colBuilder.build({} as unknown as AnyTable);
235
- return [name, column];
236
- }),
237
- ) as BuildColumns<TName, TColumnsMap>;
238
-
239
- const definedStrategies = strategies ? strategies(builtColumns) : {};
240
-
241
- const normalizedStrategies: Record<string, any> = {};
242
- for (const [key, val] of Object.entries(definedStrategies)) {
243
- if (val instanceof Column) {
244
- normalizedStrategies[key] = { type: "prefix", segments: ["", val] };
245
- } else if (val && typeof val === "object" && !("type" in val && "segments" in val)) {
246
- // It's an index strategy object { pk, sk }
247
- const indexStrategy: any = { ...val };
248
- if (indexStrategy.pk instanceof Column) {
249
- indexStrategy.pk = { type: "prefix", segments: ["", indexStrategy.pk] };
250
- }
251
- if (indexStrategy.sk instanceof Column) {
252
- indexStrategy.sk = { type: "prefix", segments: ["", indexStrategy.sk] };
253
- }
254
- normalizedStrategies[key] = indexStrategy;
255
- } else {
256
- normalizedStrategies[key] = val;
257
- }
214
+ const parsedColumns: TColumnsMap =
215
+ typeof columns === "function" ? columns(getColumnBuilders()) : columns;
216
+
217
+ const builtColumns = Object.fromEntries(
218
+ Object.entries(parsedColumns).map(([name, colBuilderBase]) => {
219
+ const colBuilder = colBuilderBase as ColumnBuider;
220
+ colBuilder.setName(name);
221
+ const column = colBuilder.build({} as unknown as AnyTable);
222
+ return [name, column];
223
+ }),
224
+ ) as BuildColumns<TName, TColumnsMap>;
225
+
226
+ const definedStrategies = strategies ? strategies(builtColumns) : {};
227
+
228
+ const normalizedStrategies: Record<string, any> = {};
229
+ for (const [key, val] of Object.entries(definedStrategies)) {
230
+ if (val instanceof Column) {
231
+ normalizedStrategies[key] = { type: "prefix", segments: ["", val] };
232
+ } else if (val && typeof val === "object" && !("type" in val && "segments" in val)) {
233
+ // It's an index strategy object { pk, sk }
234
+ const indexStrategy: Record<string, unknown> = { ...(val as object) };
235
+ if (indexStrategy.pk instanceof Column) {
236
+ indexStrategy.pk = { type: "prefix", segments: ["", indexStrategy.pk] };
237
+ }
238
+ if (indexStrategy.sk instanceof Column) {
239
+ indexStrategy.sk = { type: "prefix", segments: ["", indexStrategy.sk] };
240
+ }
241
+ normalizedStrategies[key] = indexStrategy;
242
+ } else {
243
+ normalizedStrategies[key] = val;
258
244
  }
245
+ }
259
246
 
260
- const rawEntity = new Entity(name, table, {}, normalizedStrategies);
247
+ const rawEntity = new Entity(name, table, {}, normalizedStrategies);
261
248
 
262
- rawEntity[ENTITY_SYMBOLS.COLUMNS] = builtColumns;
249
+ rawEntity[ENTITY_SYMBOLS.COLUMNS] = builtColumns;
263
250
 
264
- const entity = Object.assign(rawEntity, builtColumns);
251
+ const entity = Object.assign(rawEntity, builtColumns);
265
252
 
266
- return entity as unknown as EntityWithColumns<{
267
- name: TName;
268
- table: TTable;
269
- columns: BuildColumns<TName, TColumnsMap>;
270
- }>;
253
+ return entity as unknown as EntityWithColumns<{
254
+ name: TName;
255
+ table: TTable;
256
+ columns: BuildColumns<TName, TColumnsMap>;
257
+ }>;
271
258
  }
272
259
 
273
260
  /**
274
261
  * Defines a physical DynamoDB table schema.
275
- *
262
+ *
276
263
  * @example
277
264
  * ```ts
278
265
  * const table = dynamoTable("my-app-table", {
@@ -280,14 +267,14 @@ export function dynamoEntity<
280
267
  * sk: string("sk"),
281
268
  * });
282
269
  * ```
283
- *
270
+ *
284
271
  * @param name The actual name of the table in DynamoDB (or a reference name).
285
272
  * @param config The table configuration, including primary key (pk) and sort key (sk) definitions.
286
273
  * @returns A PhysicalTable instance representing the table schema.
287
274
  */
288
- export function dynamoTable<
289
- TTableName extends string,
290
- TConfig extends PhysicalTableConfig,
291
- >(name: TTableName, config: TConfig) {
292
- return new PhysicalTable(name, config);
275
+ export function dynamoTable<TTableName extends string, TConfig extends PhysicalTableConfig>(
276
+ name: TTableName,
277
+ config: TConfig,
278
+ ) {
279
+ return new PhysicalTable(name, config);
293
280
  }
@@ -3,73 +3,73 @@
3
3
  * Reference: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/CapacityUnitCalculations.html
4
4
  */
5
5
  export function calculateItemSize(item: Record<string, unknown>): number {
6
- let size = 0;
6
+ let size = 0;
7
7
 
8
- for (const [key, value] of Object.entries(item)) {
9
- // Attribute name size
10
- size += Buffer.byteLength(key, "utf8");
8
+ for (const [key, value] of Object.entries(item)) {
9
+ // Attribute name size
10
+ size += Buffer.byteLength(key, "utf8");
11
11
 
12
- size += calculateValueSize(value);
13
- }
12
+ size += calculateValueSize(value);
13
+ }
14
14
 
15
- return size;
15
+ return size;
16
16
  }
17
17
 
18
18
  function calculateValueSize(value: unknown): number {
19
- if (value === null || value === undefined) {
20
- return 1; // NULL
21
- }
19
+ if (value === null || value === undefined) {
20
+ return 1; // NULL
21
+ }
22
22
 
23
- if (typeof value === "string") {
24
- return Buffer.byteLength(value, "utf8");
25
- }
23
+ if (typeof value === "string") {
24
+ return Buffer.byteLength(value, "utf8");
25
+ }
26
26
 
27
- if (typeof value === "number") {
28
- // Numbers are up to 38 significant digits.
29
- // In DynamoDB, they are stored as variable length.
30
- // Rule of thumb: length of string representation + extra byte.
31
- // AWS Doc: "Numbers are stored in a compressed format... up to 21 bytes."
32
- // We'll use a conservative estimate.
33
- return 21;
34
- }
27
+ if (typeof value === "number") {
28
+ // Numbers are up to 38 significant digits.
29
+ // In DynamoDB, they are stored as variable length.
30
+ // Rule of thumb: length of string representation + extra byte.
31
+ // AWS Doc: "Numbers are stored in a compressed format... up to 21 bytes."
32
+ // We'll use a conservative estimate.
33
+ return 21;
34
+ }
35
35
 
36
- if (typeof value === "boolean") {
37
- return 1;
38
- }
36
+ if (typeof value === "boolean") {
37
+ return 1;
38
+ }
39
39
 
40
- if (value instanceof Buffer || value instanceof Uint8Array) {
41
- return value.byteLength;
42
- }
40
+ if (value instanceof Buffer || value instanceof Uint8Array) {
41
+ return value.byteLength;
42
+ }
43
43
 
44
- if (Array.isArray(value)) {
45
- // List: 3 bytes overhead + sum of elements
46
- let listSize = 3;
47
- for (const item of value) {
48
- listSize += calculateValueSize(item);
49
- }
50
- return listSize;
44
+ if (Array.isArray(value)) {
45
+ // List: 3 bytes overhead + sum of elements
46
+ let listSize = 3;
47
+ for (const item of value) {
48
+ listSize += calculateValueSize(item);
51
49
  }
50
+ return listSize;
51
+ }
52
52
 
53
- if (value instanceof Set) {
54
- // Set: overhead? usually treated like List or similar.
55
- // AWS says: "size of all the elements plus the size of the attribute name."
56
- // We'll estimate it like a List for safety.
57
- let setSize = 3;
58
- for (const item of value) {
59
- setSize += calculateValueSize(item);
60
- }
61
- return setSize;
53
+ if (value instanceof Set) {
54
+ // Set: overhead? usually treated like List or similar.
55
+ // AWS says: "size of all the elements plus the size of the attribute name."
56
+ // We'll estimate it like a List for safety.
57
+ let setSize = 3;
58
+ for (const item of value) {
59
+ setSize += calculateValueSize(item);
62
60
  }
61
+ return setSize;
62
+ }
63
63
 
64
- if (typeof value === "object") {
65
- // Map: 3 bytes overhead + attribute names + attribute values
66
- let mapSize = 3;
67
- for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
68
- mapSize += Buffer.byteLength(k, "utf8");
69
- mapSize += calculateValueSize(v);
70
- }
71
- return mapSize;
64
+ if (typeof value === "object") {
65
+ // Map: 3 bytes overhead + attribute names + attribute values
66
+ let mapSize = 3;
67
+ for (const [k, v] of Object.entries(value as Record<string, unknown>)) {
68
+ mapSize += Buffer.byteLength(k, "utf8");
69
+ mapSize += calculateValueSize(v);
72
70
  }
71
+ return mapSize;
72
+ }
73
73
 
74
- return 0;
74
+ return 0;
75
75
  }