@aurios/mizzle 1.0.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.
Files changed (48) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/package.json +62 -0
  3. package/src/builders/base.ts +71 -0
  4. package/src/builders/batch-get.ts +76 -0
  5. package/src/builders/batch-write.ts +98 -0
  6. package/src/builders/delete.ts +61 -0
  7. package/src/builders/insert.ts +156 -0
  8. package/src/builders/query-promise.ts +41 -0
  9. package/src/builders/relational-builder.ts +211 -0
  10. package/src/builders/select.ts +196 -0
  11. package/src/builders/transaction.ts +170 -0
  12. package/src/builders/update.ts +155 -0
  13. package/src/columns/binary-set.ts +49 -0
  14. package/src/columns/binary.ts +48 -0
  15. package/src/columns/boolean.ts +45 -0
  16. package/src/columns/date.ts +83 -0
  17. package/src/columns/index.ts +44 -0
  18. package/src/columns/json.ts +62 -0
  19. package/src/columns/list.ts +47 -0
  20. package/src/columns/map.ts +46 -0
  21. package/src/columns/number-set.ts +49 -0
  22. package/src/columns/number.ts +57 -0
  23. package/src/columns/string-set.ts +59 -0
  24. package/src/columns/string.ts +64 -0
  25. package/src/columns/uuid.ts +51 -0
  26. package/src/core/client.ts +18 -0
  27. package/src/core/column-builder.ts +272 -0
  28. package/src/core/column.ts +167 -0
  29. package/src/core/diff.ts +50 -0
  30. package/src/core/errors.ts +34 -0
  31. package/src/core/introspection.ts +73 -0
  32. package/src/core/operations.ts +34 -0
  33. package/src/core/parser.ts +109 -0
  34. package/src/core/relations.ts +149 -0
  35. package/src/core/retry.ts +70 -0
  36. package/src/core/snapshot.ts +192 -0
  37. package/src/core/strategies.ts +271 -0
  38. package/src/core/table.ts +260 -0
  39. package/src/core/validation.ts +75 -0
  40. package/src/db.ts +199 -0
  41. package/src/expressions/actions.ts +66 -0
  42. package/src/expressions/builder.ts +77 -0
  43. package/src/expressions/operators.ts +108 -0
  44. package/src/expressions/update-builder.ts +98 -0
  45. package/src/index.ts +20 -0
  46. package/src/indexes.ts +19 -0
  47. package/tsconfig.json +11 -0
  48. package/tsup.config.ts +20 -0
@@ -0,0 +1,59 @@
1
+ import {
2
+ Column,
3
+ type ColumnBaseConfig,
4
+ type ColumnRuntimeConfig,
5
+ } from "../core/column";
6
+ import {
7
+ ColumnBuider,
8
+ type ColumnBuilderBaseConfig,
9
+ type HasDefault,
10
+ type MakeColumnConfig,
11
+ } from "../core/column-builder";
12
+ import type { AnyTable } from "../core/table";
13
+
14
+ export type StringSetColumnInitial<TName extends string> =
15
+ StringSetColumnBuilder<{
16
+ name: TName;
17
+ dataType: "string";
18
+ columnType: "SS";
19
+ data: Set<string>;
20
+ }>;
21
+
22
+ export class StringSetColumnBuilder<
23
+ T extends ColumnBuilderBaseConfig<"string", "SS">,
24
+ > extends ColumnBuider<T> {
25
+ constructor(name: string) {
26
+ super(name, "string", "SS");
27
+ }
28
+
29
+ override default(
30
+ value: this["_"] extends { $type: infer U } ? U : this["_"]["data"],
31
+ ): HasDefault<this> {
32
+ const setVal = value instanceof Set ? value : new Set(value as []);
33
+ this.config.default = setVal;
34
+ this.config.hasDefault = true;
35
+ return this as HasDefault<this>;
36
+ }
37
+
38
+ build<TTableName extends string>(
39
+ table: AnyTable,
40
+ ): StringSetColumn<MakeColumnConfig<T, TTableName>> {
41
+ return new StringSetColumn<MakeColumnConfig<T, TTableName>>(
42
+ table,
43
+ this.config as any,
44
+ );
45
+ }
46
+ }
47
+
48
+ export class StringSetColumn<
49
+ T extends ColumnBaseConfig<"string", "SS">,
50
+ > extends Column<T> {
51
+ }
52
+
53
+ export function stringSet(): StringSetColumnInitial<"">;
54
+ export function stringSet<TName extends string>(
55
+ name: TName,
56
+ ): StringSetColumnInitial<TName>;
57
+ export function stringSet(name?: string) {
58
+ return new StringSetColumnBuilder(name ?? "");
59
+ }
@@ -0,0 +1,64 @@
1
+ import {
2
+ Column,
3
+ type ColumnBaseConfig,
4
+ type ColumnRuntimeConfig,
5
+ } from "../core/column";
6
+ import {
7
+ ColumnBuider,
8
+ type ColumnBuilderBaseConfig,
9
+ type MakeColumnConfig,
10
+ } from "../core/column-builder";
11
+ import type { AnyTable } from "../core/table";
12
+
13
+ export type StringColumnInitial<TName extends string> = StringColumnBuilder<{
14
+ name: TName;
15
+ dataType: "string";
16
+ columnType: "S";
17
+ data: string;
18
+ }>;
19
+
20
+ class StringColumnBuilder<
21
+ T extends ColumnBuilderBaseConfig<"string", "S">,
22
+ > extends ColumnBuider<
23
+ T,
24
+ { validators?: { length?: number; email?: boolean } }
25
+ > {
26
+ constructor(name: string) {
27
+ super(name, "string", "S");
28
+ }
29
+
30
+ length(value: number): this {
31
+ this.config.validators ??= {};
32
+ this.config.validators.length = value;
33
+ return this;
34
+ }
35
+
36
+ email(): this {
37
+ this.config.validators ??= {};
38
+ this.config.validators.email = true;
39
+ return this;
40
+ }
41
+
42
+ /** @internal */
43
+ build<TTableName extends string>(
44
+ table: AnyTable,
45
+ ): StringColumn<MakeColumnConfig<T, TTableName>> {
46
+ return new StringColumn<MakeColumnConfig<T, TTableName>>(
47
+ table,
48
+ this.config as any,
49
+ );
50
+ }
51
+ }
52
+
53
+ export class StringColumn<
54
+ T extends ColumnBaseConfig<"string", "S">,
55
+ > extends Column<T> {
56
+ }
57
+
58
+ export function string(): StringColumnInitial<"">;
59
+ export function string<TName extends string>(
60
+ name: TName,
61
+ ): StringColumnInitial<TName>;
62
+ export function string(name?: string) {
63
+ return new StringColumnBuilder(name ?? "");
64
+ }
@@ -0,0 +1,51 @@
1
+ import { Column, type ColumnBaseConfig } from "../core/column";
2
+ import { v7 as uuidV7 } from "uuid";
3
+ import {
4
+ ColumnBuider,
5
+ type ColumnBuilderBaseConfig,
6
+ type ColumnBuilderRuntimeConfig,
7
+ type MakeColumnConfig,
8
+ } from "../core/column-builder";
9
+ import type { AnyTable } from "../core/table";
10
+
11
+ const uuidDefault = () => uuidV7();
12
+
13
+ export type UUIDColumnInitial<TName extends string> = UUIDColumnBuilder<{
14
+ name: TName;
15
+ dataType: "string";
16
+ columnType: "S";
17
+ data: string;
18
+ }>;
19
+
20
+ export class UUIDColumnBuilder<
21
+ T extends ColumnBuilderBaseConfig<"string", "S">,
22
+ > extends ColumnBuider<T> {
23
+ constructor(name: string) {
24
+ super(name, "string", "S");
25
+
26
+ this.config.defaultFn = uuidDefault;
27
+ }
28
+
29
+ /** @internal */
30
+ build<TTableName extends string>(
31
+ table: AnyTable,
32
+ ): UUIDColumn<MakeColumnConfig<T, TTableName>> {
33
+ return new UUIDColumn<MakeColumnConfig<T, TTableName>>(
34
+ table,
35
+ this.config as any,
36
+ );
37
+ }
38
+ }
39
+
40
+ export class UUIDColumn<
41
+ T extends ColumnBaseConfig<"string", "S">,
42
+ > extends Column<T> {
43
+ }
44
+
45
+ export function uuid(): UUIDColumnInitial<"">;
46
+ export function uuid<TName extends string>(
47
+ name: TName,
48
+ ): UUIDColumnInitial<TName>;
49
+ export function uuid(name?: string) {
50
+ return new UUIDColumnBuilder(name ?? "");
51
+ }
@@ -0,0 +1,18 @@
1
+ import { DynamoDBDocumentClient } from "@aws-sdk/lib-dynamodb";
2
+ import { RetryHandler } from "./retry";
3
+
4
+ // We define a simplified interface for what we need from the client to avoid complex generic matching
5
+ export interface IMizzleClient {
6
+ send(command: any, options?: any): Promise<any>;
7
+ }
8
+
9
+ export class MizzleClient implements IMizzleClient {
10
+ constructor(
11
+ private client: DynamoDBDocumentClient,
12
+ private retryHandler: RetryHandler
13
+ ) {}
14
+
15
+ send(command: any, options?: any): Promise<any> {
16
+ return this.retryHandler.execute(() => this.client.send(command, options));
17
+ }
18
+ }
@@ -0,0 +1,272 @@
1
+ import { type Column, ExtraConfigColumn } from "./column";
2
+ import type { AnyTable } from "./table";
3
+ import type { Assume, Simplify } from "@mizzle/shared";
4
+
5
+ export type ColumnDataType =
6
+ | "S"
7
+ | "N"
8
+ | "BOOL"
9
+ | "M"
10
+ | "L"
11
+ | "B"
12
+ | "BS"
13
+ | "NS"
14
+ | "NULL"
15
+ | "SS";
16
+
17
+ export interface ColumnBuilderBaseConfig<
18
+ TDataType extends string,
19
+ TColumnDataType extends ColumnDataType,
20
+ > {
21
+ name: string;
22
+ dataType: TDataType;
23
+ columnType: TColumnDataType;
24
+ data: unknown;
25
+ }
26
+
27
+ export type BuildExtraConfigColumns<
28
+ TConfigMap extends Record<string, ColumnBuilderBase>,
29
+ > = Simplify<{ [Key in keyof TConfigMap]: ExtraConfigColumn }>;
30
+
31
+ export type MakeColumnConfig<
32
+ T extends ColumnBuilderBaseConfig<string, ColumnDataType>,
33
+ TTableName extends string,
34
+ TData = T extends { $type: infer U } ? U : T["data"],
35
+ > = {
36
+ name: T["name"];
37
+ tableName: TTableName;
38
+ dataType: T["dataType"];
39
+ columnType: T["columnType"];
40
+ data: TData;
41
+ notNull: T extends { notNull: true } ? true : false;
42
+ hasDefault: T extends { hasDefault: true } ? true : false;
43
+ hasRuntimeDefault: T extends { hasRuntimeDefault: true } ? true : false;
44
+ isPartitionKey: T extends { isPartitionKey: true } ? true : false;
45
+ isSortKey: T extends { isSortKey: true } ? true : false;
46
+ } & object;
47
+
48
+ export type ColumnBuilderRuntimeConfig<
49
+ TData,
50
+ TRuntimeConfig extends object = object,
51
+ > = {
52
+ name: string;
53
+ notNull: boolean;
54
+ default: TData;
55
+ dataType: string;
56
+ columnType: string;
57
+ isPartitionKey: boolean;
58
+ isSortKey: boolean;
59
+ hasDefault: boolean;
60
+ partitionKey: boolean;
61
+ sortKey: boolean;
62
+ defaultFn: () => TData | undefined;
63
+ onUpdateFn: () => TData | undefined;
64
+ } & TRuntimeConfig;
65
+
66
+ export type ColumnBuilderTypeConfig<
67
+ T extends ColumnBuilderBaseConfig<string, ColumnDataType>,
68
+ TTypeConfig extends object = object,
69
+ > = Simplify<
70
+ {
71
+ name: T["name"];
72
+ dataType: T["dataType"];
73
+ data: T["data"];
74
+ columnType: T["columnType"];
75
+ notNull: T extends { notNull: infer U } ? U : boolean;
76
+ hasDefault: T extends { hasDefault: infer U } ? U : boolean;
77
+ isPartitionKey: T extends { isPartitionKey: infer U } ? U : boolean;
78
+ isSortKey: T extends { isSortKey: infer U } ? U : boolean;
79
+ } & TTypeConfig
80
+ >;
81
+
82
+ export interface ColumnBuilderBase<
83
+ T extends ColumnBuilderBaseConfig<
84
+ string,
85
+ ColumnDataType
86
+ > = ColumnBuilderBaseConfig<string, ColumnDataType>,
87
+ TTypeConfig extends object = object,
88
+ > {
89
+ _: ColumnBuilderTypeConfig<T, TTypeConfig>;
90
+ }
91
+
92
+ export interface ColumnBuiderExtraConfig {
93
+ partitionKeyHasDefault?: boolean;
94
+ }
95
+
96
+ export type NotNull<T extends ColumnBuilderBase> = T & {
97
+ _: {
98
+ notNull: true;
99
+ };
100
+ };
101
+
102
+ export type HasDefault<T extends ColumnBuilderBase> = T & {
103
+ _: {
104
+ hasDefault: true;
105
+ };
106
+ };
107
+
108
+ export type HasRuntimeDefault<T extends ColumnBuilderBase> = T & {
109
+ _: {
110
+ hasRuntimeDefault: true;
111
+ };
112
+ };
113
+
114
+ export type IsPartitionKey<T extends ColumnBuilderBase> = T & {
115
+ _: {
116
+ isPartitionKey: true;
117
+ notNull: false;
118
+ };
119
+ };
120
+
121
+ export type IsSortKey<T extends ColumnBuilderBase> = T & {
122
+ _: {
123
+ isSortKey: true;
124
+ notNull: false;
125
+ };
126
+ };
127
+
128
+ export type $Type<T extends ColumnBuilderBase, TType> = T & {
129
+ _: { $type: TType };
130
+ };
131
+
132
+ export type BuildColumn<
133
+ TTableName extends string,
134
+ TBuilder extends ColumnBuilderBase,
135
+ > = Column<
136
+ MakeColumnConfig<TBuilder["_"], TTableName>,
137
+ object,
138
+ Simplify<
139
+ Omit<TBuilder["_"], keyof MakeColumnConfig<TBuilder["_"], TTableName>>
140
+ >
141
+ >;
142
+
143
+ export type BuildColumns<
144
+ TTableName extends string,
145
+ TConfigMap extends Record<string, ColumnBuilderBase>,
146
+ > = Simplify<{
147
+ [Key in keyof TConfigMap]: BuildColumn<
148
+ TTableName,
149
+ {
150
+ _: Omit<TConfigMap[Key]["_"], "name"> & {
151
+ name: TConfigMap[Key]["_"]["name"] extends ""
152
+ ? Assume<Key, string>
153
+ : TConfigMap[Key]["_"]["name"];
154
+ };
155
+ }
156
+ >;
157
+ }>;
158
+
159
+ export abstract class ColumnBuider<
160
+ T extends ColumnBuilderBaseConfig<
161
+ string,
162
+ ColumnDataType
163
+ > = ColumnBuilderBaseConfig<string, ColumnDataType>,
164
+ TRuntimeConfig extends object = object,
165
+ TTypeConfig extends object = object,
166
+ TExtraConfig extends ColumnBuiderExtraConfig = ColumnBuiderExtraConfig,
167
+ > implements ColumnBuilderBase<T, TTypeConfig>
168
+ {
169
+ declare _: ColumnBuilderTypeConfig<T, TTypeConfig>;
170
+ public config: ColumnBuilderRuntimeConfig<T["data"], TRuntimeConfig>;
171
+
172
+ constructor(
173
+ name: T["name"],
174
+ dataType: T["dataType"],
175
+ columnType: T["columnType"],
176
+ ) {
177
+ this.config = {
178
+ name,
179
+ dataType,
180
+ columnType,
181
+ notNull: false,
182
+ default: undefined,
183
+ hasDefault: false,
184
+ isPartitionKey: false,
185
+ isSortKey: false,
186
+ } as ColumnBuilderRuntimeConfig<T["data"], TRuntimeConfig>;
187
+ }
188
+
189
+ default(
190
+ value: this["_"] extends { $type: infer U } ? U : this["_"]["data"],
191
+ ): HasDefault<this> {
192
+ this.config.default = value;
193
+ this.config.hasDefault = true;
194
+ return this as HasDefault<this>;
195
+ }
196
+
197
+ $defaultFn(
198
+ fn: () => this["_"] extends { $type: infer U } ? U : this["_"]["data"],
199
+ ): HasRuntimeDefault<HasDefault<this>> {
200
+ this.config.defaultFn = fn;
201
+ this.config.hasDefault = true;
202
+ return this as HasRuntimeDefault<HasDefault<this>>;
203
+ }
204
+
205
+ $onUpdateFn(
206
+ fn: () => this["_"] extends { $type: infer U } ? U : this["_"]["data"],
207
+ ): HasDefault<this> {
208
+ this.config.onUpdateFn = fn;
209
+ this.config.hasDefault = true;
210
+ return this as HasDefault<this>;
211
+ }
212
+
213
+ /**
214
+ * Change de data type of the column.
215
+ *
216
+ * ```ts
217
+ * const users = dynamoTable('users', {
218
+ * details: map().$type<UserDetails>().nullable()
219
+ * });
220
+ * ```
221
+ */
222
+ $type<TType>(): $Type<this, TType> {
223
+ return this as $Type<this, TType>;
224
+ }
225
+
226
+ /** @internal */
227
+ getConfig() {
228
+ return this.config;
229
+ }
230
+
231
+ partitionKey(): TExtraConfig["partitionKeyHasDefault"] extends true
232
+ ? IsPartitionKey<HasDefault<NotNull<this>>>
233
+ : IsPartitionKey<NotNull<this>> {
234
+ this.config.isPartitionKey = true;
235
+ this.config.notNull = false;
236
+
237
+ return this as TExtraConfig["partitionKeyHasDefault"] extends true
238
+ ? IsPartitionKey<HasDefault<NotNull<this>>>
239
+ : IsPartitionKey<NotNull<this>>;
240
+ }
241
+
242
+ sortKey(): IsSortKey<this> {
243
+ this.config.isSortKey = true;
244
+ this.config.notNull = false;
245
+ return this as IsSortKey<this>;
246
+ }
247
+
248
+ /**
249
+ * Turns a column nullable.
250
+ *
251
+ * ```ts
252
+ * const users = dynamoTable('users', {
253
+ * name: string().nullable()
254
+ * });
255
+ * ```
256
+ */
257
+ notNull(): NotNull<this> {
258
+ this.config.notNull = true;
259
+ return this as NotNull<this>;
260
+ }
261
+
262
+ /** @internal */
263
+ setName(name: string) {
264
+ if (this.config.name !== "") return;
265
+ this.config.name = name;
266
+ }
267
+
268
+ /** @internal */
269
+ abstract build<TTableName extends string>(
270
+ table: AnyTable,
271
+ ): Column<MakeColumnConfig<T, TTableName>>;
272
+ }
@@ -0,0 +1,167 @@
1
+ import { INFER_MODE, NULLS, ORDER } from "@mizzle/shared";
2
+ import type { Update } from "@mizzle/shared";
3
+ import type {
4
+ ColumnBuilderBaseConfig,
5
+ ColumnBuilderRuntimeConfig,
6
+ ColumnDataType,
7
+ } from "./column-builder";
8
+ import type { PhysicalTable } from "./table";
9
+
10
+ export type AnyColumn<
11
+ TPartial extends Partial<ColumnBaseConfig<string, ColumnDataType>> = Record<string, never>,
12
+ > = Column<
13
+ Required<Update<ColumnBaseConfig<string, ColumnDataType>, TPartial>>
14
+ >;
15
+
16
+ export interface ColumnBaseConfig<
17
+ TDataType extends string,
18
+ TColumnType extends ColumnDataType,
19
+ > extends ColumnBuilderBaseConfig<TDataType, TColumnType> {
20
+ tableName: string;
21
+ name: string;
22
+ notNull: boolean;
23
+ hasDefault: boolean;
24
+ hasRuntimeDefault: boolean;
25
+ isPartitionKey: boolean;
26
+ isSortKey: boolean;
27
+ validators?: Record<string, unknown>;
28
+ }
29
+
30
+ export type ColumnTypeConfig<
31
+ T extends ColumnBaseConfig<string, ColumnDataType>,
32
+ TTypeConfig extends object,
33
+ > = T & {
34
+ tableName: T["tableName"];
35
+ name: T["name"];
36
+ dataType: T["dataType"];
37
+ columnType: T["columnType"];
38
+ data: T["data"];
39
+ notNull: T["notNull"];
40
+ hasDefault: T["hasDefault"];
41
+ hasRuntimeDefault: T["hasRuntimeDefault"];
42
+ isPartitionKey: T["isPartitionKey"];
43
+ isSortKey: T["isSortKey"];
44
+ } & TTypeConfig;
45
+
46
+ export type ColumnRuntimeConfig<
47
+ TData,
48
+ TRuntimeConfig extends object,
49
+ > = ColumnBuilderRuntimeConfig<TData, TRuntimeConfig>;
50
+
51
+ export abstract class Column<
52
+ T extends ColumnBaseConfig<string, ColumnDataType> = ColumnBaseConfig<
53
+ string,
54
+ ColumnDataType
55
+ >,
56
+ TRuntimeConfig extends object = object,
57
+ TTypeConfig extends object = object,
58
+ > {
59
+ declare readonly _: ColumnTypeConfig<T, TTypeConfig>;
60
+
61
+ readonly name: string;
62
+ readonly primary: boolean;
63
+ readonly notNull: boolean;
64
+ readonly default: T["data"] | undefined;
65
+ readonly defaultFn: () => T["data"] | undefined;
66
+ readonly onUpdateFn: () => T["data"] | undefined;
67
+ readonly hasDefault: boolean;
68
+ readonly dataType: T["dataType"];
69
+ readonly columnType: T["columnType"];
70
+
71
+ protected config: ColumnRuntimeConfig<T["data"], TRuntimeConfig>;
72
+
73
+ constructor(
74
+ readonly table: PhysicalTable,
75
+ config: ColumnRuntimeConfig<T["data"], TRuntimeConfig>,
76
+ ) {
77
+ this.config = config;
78
+ this.name = config.name;
79
+ this.primary = config.partitionKey;
80
+ this.notNull = config.notNull;
81
+ this.default = config.default;
82
+ this.defaultFn = config.defaultFn;
83
+ this.onUpdateFn = config.onUpdateFn;
84
+ this.hasDefault = config.hasDefault;
85
+ this.dataType = config.dataType;
86
+ this.columnType = config.columnType as T["columnType"];
87
+ }
88
+
89
+ getDynamoType(): string {
90
+ return this.columnType;
91
+ }
92
+
93
+ mapFromDynamoValue(value: unknown): unknown {
94
+ return value;
95
+ }
96
+
97
+ mapToDynamoValue(value: unknown): unknown {
98
+ return value;
99
+ }
100
+ }
101
+
102
+ export type GetColumnData<
103
+ TColumn extends Column,
104
+ TInferMode extends "query" | "raw" = "query",
105
+ > = TInferMode extends typeof INFER_MODE.RAW
106
+ ? TColumn["_"]["data"]
107
+ : TColumn["_"]["notNull"] extends false
108
+ ? TColumn["_"]["data"]
109
+ : TColumn["_"]["data"] | null;
110
+
111
+ export type InferColumndsDataTypes<TColumns extends Record<string, Column>> = {
112
+ [Key in keyof TColumns]: GetColumnData<TColumns[Key]>;
113
+ };
114
+
115
+ export type IndexedExtraConfigType = {
116
+ order?: typeof ORDER.ASC | typeof ORDER.DESC;
117
+ nulls?: typeof NULLS.FIRST | typeof NULLS.LAST;
118
+ opClass?: string;
119
+ };
120
+
121
+ export class ExtraConfigColumn<
122
+ T extends ColumnBaseConfig<string, ColumnDataType> = ColumnBaseConfig<
123
+ string,
124
+ ColumnDataType
125
+ >,
126
+ > extends Column<T, IndexedExtraConfigType> {
127
+ override getDynamoType(): string {
128
+ return this.getDynamoType();
129
+ }
130
+
131
+ indexConfig: IndexedExtraConfigType = {
132
+ order: this.config.order ?? ORDER.ASC,
133
+ nulls: this.config.nulls ?? NULLS.LAST,
134
+ opClass: this.config.opClass,
135
+ };
136
+
137
+ defaultConfig: IndexedExtraConfigType = {
138
+ order: ORDER.ASC,
139
+ nulls: NULLS.LAST,
140
+ opClass: undefined,
141
+ };
142
+
143
+ asc(): Omit<this, "asc" | "desc"> {
144
+ this.indexConfig.order = ORDER.ASC;
145
+ return this;
146
+ }
147
+
148
+ desc(): Omit<this, "asc" | "desc"> {
149
+ this.indexConfig.order = ORDER.DESC;
150
+ return this;
151
+ }
152
+
153
+ nullsFirst(): Omit<this, "nullsFirst" | "nullsLast"> {
154
+ this.indexConfig.nulls = NULLS.FIRST;
155
+ return this;
156
+ }
157
+
158
+ nullsLast(): Omit<this, "nullsFirst" | "nullsLast"> {
159
+ this.indexConfig.nulls = NULLS.LAST;
160
+ return this;
161
+ }
162
+
163
+ op(opClass: string): Omit<this, "opClass"> {
164
+ this.indexConfig.opClass = opClass;
165
+ return this;
166
+ }
167
+ }
@@ -0,0 +1,50 @@
1
+ import type { MizzleSnapshot, TableSnapshot, SchemaCurrent } from "./snapshot";
2
+ import { generateSnapshot } from "./snapshot";
3
+
4
+ export type SchemaChange =
5
+ | { type: "create"; table: TableSnapshot }
6
+ | { type: "delete"; tableName: string }
7
+ | { type: "update"; tableName: string; changes: unknown[] };
8
+
9
+ export function compareSchema(current: SchemaCurrent, snapshot: MizzleSnapshot): SchemaChange[] {
10
+ const changes: SchemaChange[] = [];
11
+ const currentSnapshot = generateSnapshot(current);
12
+
13
+ const currentTables = currentSnapshot.tables;
14
+ const snapshotTables = snapshot.tables || {};
15
+
16
+ const allTableNames = new Set([...Object.keys(currentTables), ...Object.keys(snapshotTables)]);
17
+
18
+ for (const tableName of allTableNames) {
19
+ const currentTable = currentTables[tableName];
20
+ const snapshotTable = snapshotTables[tableName];
21
+
22
+ if (currentTable && !snapshotTable) {
23
+ changes.push({ type: "create", table: currentTable });
24
+ } else if (!currentTable && snapshotTable) {
25
+ changes.push({ type: "delete", tableName });
26
+ } else if (currentTable && snapshotTable) {
27
+ if (!areSnapshotsEqual(currentTable, snapshotTable)) {
28
+ changes.push({ type: "update", tableName, changes: ["Changed"] });
29
+ }
30
+ }
31
+ }
32
+
33
+ return changes;
34
+ }
35
+
36
+ function areSnapshotsEqual(a: TableSnapshot, b: TableSnapshot): boolean {
37
+ const sortSnapshot = (s: TableSnapshot) => {
38
+ const copy = { ...s };
39
+ copy.AttributeDefinitions = [...(copy.AttributeDefinitions || [])].sort((x, y) => (x.AttributeName || "").localeCompare(y.AttributeName || ""));
40
+ if (copy.GlobalSecondaryIndexes) {
41
+ copy.GlobalSecondaryIndexes = [...copy.GlobalSecondaryIndexes].sort((x, y) => (x.IndexName || "").localeCompare(y.IndexName || ""));
42
+ }
43
+ if (copy.LocalSecondaryIndexes) {
44
+ copy.LocalSecondaryIndexes = [...copy.LocalSecondaryIndexes].sort((x, y) => (x.IndexName || "").localeCompare(y.IndexName || ""));
45
+ }
46
+ return copy;
47
+ }
48
+
49
+ return JSON.stringify(sortSnapshot(a)) === JSON.stringify(sortSnapshot(b));
50
+ }