dyno-table 2.0.2 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +10 -0
- package/dist/builders/condition-check-builder.cjs.map +1 -1
- package/dist/builders/condition-check-builder.js.map +1 -1
- package/dist/builders/delete-builder.cjs.map +1 -1
- package/dist/builders/delete-builder.js.map +1 -1
- package/dist/builders/paginator.cjs.map +1 -1
- package/dist/builders/paginator.js.map +1 -1
- package/dist/builders/put-builder.cjs.map +1 -1
- package/dist/builders/put-builder.js.map +1 -1
- package/dist/builders/query-builder.cjs.map +1 -1
- package/dist/builders/query-builder.js.map +1 -1
- package/dist/builders/transaction-builder.cjs.map +1 -1
- package/dist/builders/transaction-builder.js.map +1 -1
- package/dist/builders/update-builder.cjs.map +1 -1
- package/dist/builders/update-builder.js.map +1 -1
- package/dist/conditions.cjs.map +1 -1
- package/dist/conditions.js.map +1 -1
- package/dist/entity.cjs +192 -42
- package/dist/entity.cjs.map +1 -1
- package/dist/entity.d.cts +18 -6
- package/dist/entity.d.ts +18 -6
- package/dist/entity.js +192 -42
- package/dist/entity.js.map +1 -1
- package/dist/index.cjs +184 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +184 -34
- package/dist/index.js.map +1 -1
- package/dist/table.cjs.map +1 -1
- package/dist/table.js.map +1 -1
- package/dist/types.d.cts +9 -2
- package/dist/types.d.ts +9 -2
- package/dist/utils.cjs.map +1 -1
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3772,27 +3772,133 @@ function createEntityAwareDeleteBuilder(builder, entityName) {
|
|
|
3772
3772
|
return createEntityAwareBuilder(builder, entityName);
|
|
3773
3773
|
}
|
|
3774
3774
|
|
|
3775
|
-
// src/entity.ts
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3775
|
+
// src/entity/ddb-indexing.ts
|
|
3776
|
+
var IndexBuilder = class {
|
|
3777
|
+
/**
|
|
3778
|
+
* Creates a new IndexBuilder instance
|
|
3779
|
+
*
|
|
3780
|
+
* @param table - The DynamoDB table instance
|
|
3781
|
+
* @param indexes - The index definitions
|
|
3782
|
+
*/
|
|
3783
|
+
constructor(table, indexes = {}) {
|
|
3784
|
+
this.table = table;
|
|
3785
|
+
this.indexes = indexes;
|
|
3786
|
+
}
|
|
3787
|
+
/**
|
|
3788
|
+
* Build index attributes for item creation
|
|
3789
|
+
*
|
|
3790
|
+
* @param item - The item to generate indexes for
|
|
3791
|
+
* @param options - Options for building indexes
|
|
3792
|
+
* @returns Record of GSI attribute names to their values
|
|
3793
|
+
*/
|
|
3794
|
+
buildForCreate(item, options = {}) {
|
|
3795
|
+
const attributes = {};
|
|
3796
|
+
for (const [indexName, indexDef] of Object.entries(this.indexes)) {
|
|
3797
|
+
if (options.excludeReadOnly && indexDef.isReadOnly) {
|
|
3798
|
+
continue;
|
|
3799
|
+
}
|
|
3800
|
+
const key = indexDef.generateKey(item);
|
|
3801
|
+
const gsiConfig = this.table.gsis[indexName];
|
|
3802
|
+
if (!gsiConfig) {
|
|
3803
|
+
throw new Error(`GSI configuration not found for index: ${indexName}`);
|
|
3804
|
+
}
|
|
3805
|
+
if (key.pk) {
|
|
3806
|
+
attributes[gsiConfig.partitionKey] = key.pk;
|
|
3807
|
+
}
|
|
3808
|
+
if (key.sk && gsiConfig.sortKey) {
|
|
3809
|
+
attributes[gsiConfig.sortKey] = key.sk;
|
|
3810
|
+
}
|
|
3811
|
+
}
|
|
3812
|
+
return attributes;
|
|
3813
|
+
}
|
|
3814
|
+
/**
|
|
3815
|
+
* Build index attributes for item updates
|
|
3816
|
+
*
|
|
3817
|
+
* @param currentData - The current data before update
|
|
3818
|
+
* @param updates - The update data
|
|
3819
|
+
* @param options - Options for building indexes
|
|
3820
|
+
* @returns Record of GSI attribute names to their updated values
|
|
3821
|
+
*/
|
|
3822
|
+
buildForUpdate(currentData, updates) {
|
|
3823
|
+
const attributes = {};
|
|
3824
|
+
const updatedItem = { ...currentData, ...updates };
|
|
3825
|
+
for (const [indexName, indexDef] of Object.entries(this.indexes)) {
|
|
3826
|
+
if (indexDef.isReadOnly) {
|
|
3827
|
+
continue;
|
|
3828
|
+
}
|
|
3829
|
+
let shouldUpdateIndex = false;
|
|
3830
|
+
try {
|
|
3831
|
+
const currentKey = indexDef.generateKey(currentData);
|
|
3832
|
+
const updatedKey = indexDef.generateKey(updatedItem);
|
|
3833
|
+
if (currentKey.pk !== updatedKey.pk || currentKey.sk !== updatedKey.sk) {
|
|
3834
|
+
shouldUpdateIndex = true;
|
|
3835
|
+
}
|
|
3836
|
+
} catch {
|
|
3837
|
+
shouldUpdateIndex = true;
|
|
3838
|
+
}
|
|
3839
|
+
if (!shouldUpdateIndex) {
|
|
3840
|
+
continue;
|
|
3841
|
+
}
|
|
3842
|
+
try {
|
|
3843
|
+
const key = indexDef.generateKey(updatedItem);
|
|
3844
|
+
if (this.hasUndefinedValues(key)) {
|
|
3845
|
+
throw new Error(
|
|
3846
|
+
`Cannot update entity: insufficient data to regenerate index "${indexName}". All attributes required by the index must be provided in the update operation, or the index must be marked as readOnly.`
|
|
3847
|
+
);
|
|
3848
|
+
}
|
|
3849
|
+
const gsiConfig = this.table.gsis[indexName];
|
|
3783
3850
|
if (!gsiConfig) {
|
|
3784
3851
|
throw new Error(`GSI configuration not found for index: ${indexName}`);
|
|
3785
3852
|
}
|
|
3786
3853
|
if (key.pk) {
|
|
3787
|
-
|
|
3854
|
+
attributes[gsiConfig.partitionKey] = key.pk;
|
|
3788
3855
|
}
|
|
3789
3856
|
if (key.sk && gsiConfig.sortKey) {
|
|
3790
|
-
|
|
3857
|
+
attributes[gsiConfig.sortKey] = key.sk;
|
|
3791
3858
|
}
|
|
3792
|
-
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3859
|
+
} catch (error) {
|
|
3860
|
+
if (error instanceof Error && error.message.includes("insufficient data")) {
|
|
3861
|
+
throw error;
|
|
3862
|
+
}
|
|
3863
|
+
throw new Error(
|
|
3864
|
+
`Cannot update entity: insufficient data to regenerate index "${indexName}". All attributes required by the index must be provided in the update operation, or the index must be readOnly.`
|
|
3865
|
+
);
|
|
3866
|
+
}
|
|
3867
|
+
}
|
|
3868
|
+
return attributes;
|
|
3869
|
+
}
|
|
3870
|
+
/**
|
|
3871
|
+
* Check if a key has undefined values
|
|
3872
|
+
*
|
|
3873
|
+
* @param key - The index key to check
|
|
3874
|
+
* @returns True if the key contains undefined values, false otherwise
|
|
3875
|
+
*/
|
|
3876
|
+
hasUndefinedValues(key) {
|
|
3877
|
+
return (key.pk?.includes("undefined") ?? false) || (key.sk?.includes("undefined") ?? false);
|
|
3878
|
+
}
|
|
3879
|
+
};
|
|
3880
|
+
|
|
3881
|
+
// src/entity/index-utils.ts
|
|
3882
|
+
function buildIndexes(dataForKeyGeneration, table, indexes, excludeReadOnly = false) {
|
|
3883
|
+
if (!indexes) {
|
|
3884
|
+
return {};
|
|
3885
|
+
}
|
|
3886
|
+
const indexBuilder = new IndexBuilder(table, indexes);
|
|
3887
|
+
return indexBuilder.buildForCreate(dataForKeyGeneration, { excludeReadOnly });
|
|
3888
|
+
}
|
|
3889
|
+
function buildIndexUpdates(currentData, updates, table, indexes) {
|
|
3890
|
+
if (!indexes) {
|
|
3891
|
+
return {};
|
|
3892
|
+
}
|
|
3893
|
+
const indexBuilder = new IndexBuilder(table, indexes);
|
|
3894
|
+
return indexBuilder.buildForUpdate(currentData, updates);
|
|
3895
|
+
}
|
|
3896
|
+
|
|
3897
|
+
// src/entity/entity.ts
|
|
3898
|
+
function defineEntity(config) {
|
|
3899
|
+
const entityTypeAttributeName = config.settings?.entityTypeAttributeName ?? "entityType";
|
|
3900
|
+
const buildIndexes2 = (dataForKeyGeneration, table, excludeReadOnly = false) => {
|
|
3901
|
+
return buildIndexes(dataForKeyGeneration, table, config.indexes, excludeReadOnly);
|
|
3796
3902
|
};
|
|
3797
3903
|
const wrapMethodWithPreparation = (originalMethod, prepareFn, context) => {
|
|
3798
3904
|
const wrappedMethod = (...args) => {
|
|
@@ -3844,7 +3950,7 @@ function defineEntity(config) {
|
|
|
3844
3950
|
...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
|
|
3845
3951
|
};
|
|
3846
3952
|
const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
|
|
3847
|
-
const indexes = buildIndexes(dataForKeyGeneration, table);
|
|
3953
|
+
const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
|
|
3848
3954
|
const validatedItem = {
|
|
3849
3955
|
...dataForKeyGeneration,
|
|
3850
3956
|
[entityTypeAttributeName]: config.name,
|
|
@@ -3870,7 +3976,7 @@ function defineEntity(config) {
|
|
|
3870
3976
|
...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
|
|
3871
3977
|
};
|
|
3872
3978
|
const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
|
|
3873
|
-
const indexes = buildIndexes(dataForKeyGeneration, table);
|
|
3979
|
+
const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
|
|
3874
3980
|
const validatedItem = {
|
|
3875
3981
|
...dataForKeyGeneration,
|
|
3876
3982
|
[entityTypeAttributeName]: config.name,
|
|
@@ -3912,7 +4018,7 @@ function defineEntity(config) {
|
|
|
3912
4018
|
...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
|
|
3913
4019
|
};
|
|
3914
4020
|
const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
|
|
3915
|
-
const indexes =
|
|
4021
|
+
const indexes = buildIndexes2(dataForKeyGeneration, table, false);
|
|
3916
4022
|
const validatedItem = {
|
|
3917
4023
|
[table.partitionKey]: primaryKey.pk,
|
|
3918
4024
|
...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
|
|
@@ -3938,7 +4044,7 @@ function defineEntity(config) {
|
|
|
3938
4044
|
...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
|
|
3939
4045
|
};
|
|
3940
4046
|
const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
|
|
3941
|
-
const indexes = buildIndexes(dataForKeyGeneration, table);
|
|
4047
|
+
const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
|
|
3942
4048
|
const validatedItem = {
|
|
3943
4049
|
[table.partitionKey]: primaryKey.pk,
|
|
3944
4050
|
...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
|
|
@@ -3972,13 +4078,21 @@ function defineEntity(config) {
|
|
|
3972
4078
|
}
|
|
3973
4079
|
return createEntityAwarePutBuilder(builder, config.name);
|
|
3974
4080
|
},
|
|
3975
|
-
get: (key) =>
|
|
4081
|
+
get: (key) => {
|
|
4082
|
+
return createEntityAwareGetBuilder(table.get(config.primaryKey.generateKey(key)), config.name);
|
|
4083
|
+
},
|
|
3976
4084
|
update: (key, data) => {
|
|
3977
4085
|
const primaryKeyObj = config.primaryKey.generateKey(key);
|
|
3978
4086
|
const builder = table.update(primaryKeyObj);
|
|
3979
4087
|
builder.condition(eq(entityTypeAttributeName, config.name));
|
|
3980
4088
|
const timestamps = generateTimestamps(["updatedAt"], data);
|
|
3981
|
-
|
|
4089
|
+
const indexUpdates = buildIndexUpdates(
|
|
4090
|
+
{ ...key },
|
|
4091
|
+
{ ...data, ...timestamps },
|
|
4092
|
+
table,
|
|
4093
|
+
config.indexes
|
|
4094
|
+
);
|
|
4095
|
+
builder.set({ ...data, ...timestamps, ...indexUpdates });
|
|
3982
4096
|
return builder;
|
|
3983
4097
|
},
|
|
3984
4098
|
delete: (key) => {
|
|
@@ -4049,21 +4163,57 @@ function createQueries() {
|
|
|
4049
4163
|
}
|
|
4050
4164
|
function createIndex() {
|
|
4051
4165
|
return {
|
|
4052
|
-
input: (schema) =>
|
|
4053
|
-
|
|
4054
|
-
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4166
|
+
input: (schema) => {
|
|
4167
|
+
const createIndexBuilder = (isReadOnly = false) => ({
|
|
4168
|
+
partitionKey: (pkFn) => ({
|
|
4169
|
+
sortKey: (skFn) => {
|
|
4170
|
+
const index = {
|
|
4171
|
+
name: "custom",
|
|
4172
|
+
partitionKey: "pk",
|
|
4173
|
+
sortKey: "sk",
|
|
4174
|
+
isReadOnly,
|
|
4175
|
+
generateKey: (item) => {
|
|
4176
|
+
const data = schema["~standard"].validate(item);
|
|
4177
|
+
if ("issues" in data && data.issues) {
|
|
4178
|
+
throw new Error(`Index validation failed: ${data.issues.map((i) => i.message).join(", ")}`);
|
|
4179
|
+
}
|
|
4180
|
+
const validData = "value" in data ? data.value : item;
|
|
4181
|
+
return { pk: pkFn(validData), sk: skFn(validData) };
|
|
4182
|
+
}
|
|
4183
|
+
};
|
|
4184
|
+
return Object.assign(index, {
|
|
4185
|
+
readOnly: (value = false) => ({
|
|
4186
|
+
...index,
|
|
4187
|
+
isReadOnly: value
|
|
4188
|
+
})
|
|
4189
|
+
});
|
|
4190
|
+
},
|
|
4191
|
+
withoutSortKey: () => {
|
|
4192
|
+
const index = {
|
|
4193
|
+
name: "custom",
|
|
4194
|
+
partitionKey: "pk",
|
|
4195
|
+
isReadOnly,
|
|
4196
|
+
generateKey: (item) => {
|
|
4197
|
+
const data = schema["~standard"].validate(item);
|
|
4198
|
+
if ("issues" in data && data.issues) {
|
|
4199
|
+
throw new Error(`Index validation failed: ${data.issues.map((i) => i.message).join(", ")}`);
|
|
4200
|
+
}
|
|
4201
|
+
const validData = "value" in data ? data.value : item;
|
|
4202
|
+
return { pk: pkFn(validData) };
|
|
4203
|
+
}
|
|
4204
|
+
};
|
|
4205
|
+
return Object.assign(index, {
|
|
4206
|
+
readOnly: (value = true) => ({
|
|
4207
|
+
...index,
|
|
4208
|
+
isReadOnly: value
|
|
4209
|
+
})
|
|
4210
|
+
});
|
|
4211
|
+
}
|
|
4059
4212
|
}),
|
|
4060
|
-
|
|
4061
|
-
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
})
|
|
4065
|
-
})
|
|
4066
|
-
})
|
|
4213
|
+
readOnly: (value = true) => createIndexBuilder(value)
|
|
4214
|
+
});
|
|
4215
|
+
return createIndexBuilder(false);
|
|
4216
|
+
}
|
|
4067
4217
|
};
|
|
4068
4218
|
}
|
|
4069
4219
|
|