@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.
- package/.turbo/turbo-build.log +37 -0
- package/LICENSE +21 -0
- package/README.md +57 -0
- package/dist/chunk-AQVECMXP.js +1 -0
- package/dist/chunk-DU7UPWBW.js +1 -0
- package/dist/chunk-GPYZK4WY.js +1 -0
- package/dist/chunk-NPPZW6VT.js +1 -0
- package/dist/chunk-TOYV2M4M.js +1 -0
- package/dist/chunk-UM3YF5EC.js +1 -0
- package/dist/columns.d.ts +1 -0
- package/dist/columns.js +1 -0
- package/dist/db-zHIHBm1E.d.ts +815 -0
- package/dist/db.d.ts +3 -0
- package/dist/db.js +1 -0
- package/dist/diff.d.ts +18 -0
- package/dist/diff.js +1 -0
- package/dist/index.d.ts +42 -0
- package/dist/index.js +1 -0
- package/dist/introspection.d.ts +7 -0
- package/dist/introspection.js +1 -0
- package/dist/operators-BVreW0ky.d.ts +719 -0
- package/dist/snapshot.d.ts +24 -0
- package/dist/snapshot.js +1 -0
- package/dist/table.d.ts +1 -0
- package/dist/table.js +1 -0
- package/dist/transaction-RE7LXTGV.js +1 -0
- package/package.json +73 -24
- package/src/builders/base.ts +53 -56
- package/src/builders/batch-get.ts +63 -58
- package/src/builders/batch-write.ts +81 -78
- package/src/builders/delete.ts +46 -53
- package/src/builders/insert.ts +158 -150
- package/src/builders/query-promise.ts +26 -35
- package/src/builders/relational-builder.ts +214 -191
- package/src/builders/select.ts +250 -238
- package/src/builders/transaction.ts +170 -151
- package/src/builders/update.ts +198 -191
- package/src/columns/binary-set.ts +29 -38
- package/src/columns/binary.ts +25 -35
- package/src/columns/boolean.ts +25 -30
- package/src/columns/date.ts +57 -64
- package/src/columns/index.ts +15 -15
- package/src/columns/json.ts +39 -48
- package/src/columns/list.ts +26 -36
- package/src/columns/map.ts +26 -34
- package/src/columns/number-set.ts +29 -38
- package/src/columns/number.ts +33 -40
- package/src/columns/string-set.ts +38 -47
- package/src/columns/string.ts +37 -49
- package/src/columns/uuid.ts +26 -33
- package/src/core/client.ts +9 -9
- package/src/core/column-builder.ts +194 -220
- package/src/core/column.ts +127 -135
- package/src/core/diff.ts +40 -34
- package/src/core/errors.ts +20 -17
- package/src/core/introspection.ts +62 -58
- package/src/core/operations.ts +17 -23
- package/src/core/parser.ts +82 -88
- package/src/core/relations.ts +165 -152
- package/src/core/retry.ts +52 -52
- package/src/core/snapshot.ts +131 -130
- package/src/core/strategies.ts +222 -214
- package/src/core/table.ts +189 -202
- package/src/core/validation.ts +52 -52
- package/src/db.ts +216 -213
- package/src/expressions/actions.ts +26 -26
- package/src/expressions/builder.ts +62 -54
- package/src/expressions/operators.ts +48 -48
- package/src/expressions/update-builder.ts +79 -75
- package/src/index.ts +2 -1
- package/src/indexes.ts +8 -8
- package/test/batch-resilience.test.ts +138 -0
- package/test/builders/delete.test.ts +100 -0
- package/test/builders/insert.test.ts +216 -0
- package/test/builders/relational-types.test.ts +55 -0
- package/test/builders/relational.integration.test.ts +291 -0
- package/test/builders/relational.test.ts +66 -0
- package/test/builders/select.test.ts +411 -0
- package/test/builders/transaction-errors.test.ts +46 -0
- package/test/builders/transaction-execution.test.ts +99 -0
- package/test/builders/transaction-proxy.test.ts +41 -0
- package/test/builders/update-expression.test.ts +106 -0
- package/test/builders/update.test.ts +179 -0
- package/test/core/diff.test.ts +152 -0
- package/test/core/expressions.test.ts +64 -0
- package/test/core/introspection.test.ts +47 -0
- package/test/core/parser.test.ts +69 -0
- package/test/core/snapshot-gen.test.ts +155 -0
- package/test/core/snapshot.test.ts +52 -0
- package/test/date-column.test.ts +159 -0
- package/test/fluent-writes.integration.test.ts +148 -0
- package/test/integration-retry.test.ts +77 -0
- package/test/integration.test.ts +105 -0
- package/test/item-size-error.test.ts +16 -0
- package/test/item-size-validation.test.ts +82 -0
- package/test/item-size.test.ts +47 -0
- package/test/iterator-pagination.integration.test.ts +132 -0
- package/test/jsdoc-builders.test.ts +55 -0
- package/test/jsdoc-schema.test.ts +107 -0
- package/test/json-column.test.ts +51 -0
- package/test/metadata.test.ts +54 -0
- package/test/mizzle-package.test.ts +20 -0
- package/test/relational-centralized.test.ts +83 -0
- package/test/relational-definition.test.ts +75 -0
- package/test/relational-init.test.ts +30 -0
- package/test/relational-proxy.test.ts +52 -0
- package/test/relations.test.ts +45 -0
- package/test/resilience-config.test.ts +34 -0
- package/test/retry-handler.test.ts +63 -0
- package/test/transaction.integration.test.ts +153 -0
- package/test/unified-select.integration.test.ts +153 -0
- package/test/unified-update.integration.test.ts +139 -0
- package/test/update.integration.test.ts +132 -0
- package/tsconfig.json +12 -9
- package/tsup.config.ts +11 -11
- 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 "@
|
|
2
|
-
import type { Simplify, Update } from "@
|
|
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
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
TIndexes
|
|
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
|
-
|
|
31
|
-
|
|
23
|
+
TColumns extends Record<string, Column>,
|
|
24
|
+
TPhysicalConfig extends PhysicalTableConfig,
|
|
32
25
|
> = (columns: TColumns) => {
|
|
33
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
30
|
+
name: string;
|
|
31
|
+
table: PhysicalTable;
|
|
32
|
+
columns: Record<string, TColumn>;
|
|
42
33
|
}
|
|
43
34
|
|
|
44
35
|
export interface PhysicalTableConfig {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
36
|
+
pk: ColumnBuilderBase;
|
|
37
|
+
sk?: ColumnBuilderBase;
|
|
38
|
+
indexes?: Record<string, IndexBuilder>;
|
|
48
39
|
}
|
|
49
40
|
|
|
50
|
-
export class PhysicalTable<
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
|
|
129
|
-
|
|
130
|
-
|
|
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
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
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
|
-
|
|
158
|
-
|
|
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
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
-
|
|
171
|
-
|
|
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
|
-
|
|
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
|
-
|
|
170
|
+
[Key in keyof T["columns"]]: T["columns"][Key];
|
|
182
171
|
};
|
|
183
172
|
|
|
184
173
|
export type UpdateTableConfig<
|
|
185
|
-
|
|
186
|
-
|
|
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
|
-
|
|
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
|
-
|
|
212
|
-
|
|
213
|
-
|
|
201
|
+
TName extends string,
|
|
202
|
+
TTable extends PhysicalTable,
|
|
203
|
+
TColumnsMap extends Record<string, ColumnBuilderBase>,
|
|
214
204
|
>(
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
-
|
|
224
|
-
|
|
225
|
-
|
|
210
|
+
name: TName;
|
|
211
|
+
table: TTable;
|
|
212
|
+
columns: BuildColumns<TName, TColumnsMap>;
|
|
226
213
|
}> {
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
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
|
-
|
|
247
|
+
const rawEntity = new Entity(name, table, {}, normalizedStrategies);
|
|
261
248
|
|
|
262
|
-
|
|
249
|
+
rawEntity[ENTITY_SYMBOLS.COLUMNS] = builtColumns;
|
|
263
250
|
|
|
264
|
-
|
|
251
|
+
const entity = Object.assign(rawEntity, builtColumns);
|
|
265
252
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
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
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
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
|
}
|
package/src/core/validation.ts
CHANGED
|
@@ -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
|
-
|
|
6
|
+
let size = 0;
|
|
7
7
|
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
for (const [key, value] of Object.entries(item)) {
|
|
9
|
+
// Attribute name size
|
|
10
|
+
size += Buffer.byteLength(key, "utf8");
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
size += calculateValueSize(value);
|
|
13
|
+
}
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
return size;
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
function calculateValueSize(value: unknown): number {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
if (value === null || value === undefined) {
|
|
20
|
+
return 1; // NULL
|
|
21
|
+
}
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
23
|
+
if (typeof value === "string") {
|
|
24
|
+
return Buffer.byteLength(value, "utf8");
|
|
25
|
+
}
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
37
|
-
|
|
38
|
-
|
|
36
|
+
if (typeof value === "boolean") {
|
|
37
|
+
return 1;
|
|
38
|
+
}
|
|
39
39
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
if (value instanceof Buffer || value instanceof Uint8Array) {
|
|
41
|
+
return value.byteLength;
|
|
42
|
+
}
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
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
|
-
|
|
74
|
+
return 0;
|
|
75
75
|
}
|