dyno-table 2.0.2 → 2.1.1
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 +126 -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 +44 -21
- package/dist/builders/query-builder.cjs.map +1 -1
- package/dist/builders/query-builder.d.cts +1 -1
- package/dist/builders/query-builder.d.ts +1 -1
- package/dist/builders/query-builder.js +44 -21
- 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 +196 -47
- package/dist/entity.cjs.map +1 -1
- package/dist/entity.d.cts +19 -7
- package/dist/entity.d.ts +19 -7
- package/dist/entity.js +196 -47
- package/dist/entity.js.map +1 -1
- package/dist/index.cjs +246 -61
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +246 -61
- package/dist/index.js.map +1 -1
- package/dist/{query-builder-BNWRCrJW.d.ts → query-builder-CUWdavZw.d.ts} +2 -0
- package/dist/{query-builder-DZ9JKgBN.d.cts → query-builder-DoZzZz_c.d.cts} +2 -0
- package/dist/{table-BhEeYauU.d.ts → table-4UxlW_wD.d.ts} +2 -1
- package/dist/{table-BpNOboD9.d.cts → table-D-xNCVFa.d.cts} +2 -1
- package/dist/table.cjs +58 -22
- package/dist/table.cjs.map +1 -1
- package/dist/table.d.cts +2 -2
- package/dist/table.d.ts +2 -2
- package/dist/table.js +58 -22
- 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.cjs
CHANGED
|
@@ -521,30 +521,40 @@ var FilterBuilder = class {
|
|
|
521
521
|
* @returns The builder instance for method chaining
|
|
522
522
|
*/
|
|
523
523
|
filter(condition) {
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
beginsWith,
|
|
535
|
-
contains,
|
|
536
|
-
attributeExists,
|
|
537
|
-
attributeNotExists,
|
|
538
|
-
and,
|
|
539
|
-
or,
|
|
540
|
-
not
|
|
541
|
-
};
|
|
542
|
-
this.options.filter = condition(conditionOperator);
|
|
524
|
+
const newCondition = typeof condition === "function" ? condition(this.getConditionOperator()) : condition;
|
|
525
|
+
if (this.options.filter) {
|
|
526
|
+
if (this.options.filter.type === "and" && this.options.filter.conditions) {
|
|
527
|
+
this.options.filter = {
|
|
528
|
+
type: "and",
|
|
529
|
+
conditions: [...this.options.filter.conditions, newCondition]
|
|
530
|
+
};
|
|
531
|
+
} else {
|
|
532
|
+
this.options.filter = and(this.options.filter, newCondition);
|
|
533
|
+
}
|
|
543
534
|
} else {
|
|
544
|
-
this.options.filter =
|
|
535
|
+
this.options.filter = newCondition;
|
|
545
536
|
}
|
|
546
537
|
return this;
|
|
547
538
|
}
|
|
539
|
+
getConditionOperator() {
|
|
540
|
+
return {
|
|
541
|
+
eq,
|
|
542
|
+
ne,
|
|
543
|
+
lt,
|
|
544
|
+
lte,
|
|
545
|
+
gt,
|
|
546
|
+
gte,
|
|
547
|
+
between,
|
|
548
|
+
inArray,
|
|
549
|
+
beginsWith,
|
|
550
|
+
contains,
|
|
551
|
+
attributeExists,
|
|
552
|
+
attributeNotExists,
|
|
553
|
+
and,
|
|
554
|
+
or,
|
|
555
|
+
not
|
|
556
|
+
};
|
|
557
|
+
}
|
|
548
558
|
/**
|
|
549
559
|
* Specifies which attributes to return in the results.
|
|
550
560
|
*
|
|
@@ -835,10 +845,23 @@ var QueryBuilder = class _QueryBuilder extends FilterBuilder {
|
|
|
835
845
|
*/
|
|
836
846
|
clone() {
|
|
837
847
|
const clone = new _QueryBuilder(this.executor, this.keyCondition);
|
|
838
|
-
clone.options = {
|
|
848
|
+
clone.options = {
|
|
849
|
+
...this.options,
|
|
850
|
+
filter: this.deepCloneFilter(this.options.filter)
|
|
851
|
+
};
|
|
839
852
|
clone.selectedFields = new Set(this.selectedFields);
|
|
840
853
|
return clone;
|
|
841
854
|
}
|
|
855
|
+
deepCloneFilter(filter) {
|
|
856
|
+
if (!filter) return filter;
|
|
857
|
+
if (filter.type === "and" || filter.type === "or") {
|
|
858
|
+
return {
|
|
859
|
+
...filter,
|
|
860
|
+
conditions: filter.conditions?.map((condition) => this.deepCloneFilter(condition)).filter((c) => c !== void 0)
|
|
861
|
+
};
|
|
862
|
+
}
|
|
863
|
+
return { ...filter };
|
|
864
|
+
}
|
|
842
865
|
/**
|
|
843
866
|
* Executes the query against DynamoDB and returns a generator that behaves like an array.
|
|
844
867
|
*
|
|
@@ -3157,10 +3180,23 @@ var ScanBuilder = class _ScanBuilder extends FilterBuilder {
|
|
|
3157
3180
|
*/
|
|
3158
3181
|
clone() {
|
|
3159
3182
|
const clone = new _ScanBuilder(this.executor);
|
|
3160
|
-
clone.options = {
|
|
3183
|
+
clone.options = {
|
|
3184
|
+
...this.options,
|
|
3185
|
+
filter: this.deepCloneFilter(this.options.filter)
|
|
3186
|
+
};
|
|
3161
3187
|
clone.selectedFields = new Set(this.selectedFields);
|
|
3162
3188
|
return clone;
|
|
3163
3189
|
}
|
|
3190
|
+
deepCloneFilter(filter) {
|
|
3191
|
+
if (!filter) return filter;
|
|
3192
|
+
if (filter.type === "and" || filter.type === "or") {
|
|
3193
|
+
return {
|
|
3194
|
+
...filter,
|
|
3195
|
+
conditions: filter.conditions?.map((condition) => this.deepCloneFilter(condition)).filter((c) => c !== void 0)
|
|
3196
|
+
};
|
|
3197
|
+
}
|
|
3198
|
+
return { ...filter };
|
|
3199
|
+
}
|
|
3164
3200
|
/**
|
|
3165
3201
|
* Executes the scan against DynamoDB and returns a generator that behaves like an array.
|
|
3166
3202
|
*
|
|
@@ -3774,27 +3810,132 @@ function createEntityAwareDeleteBuilder(builder, entityName) {
|
|
|
3774
3810
|
return createEntityAwareBuilder(builder, entityName);
|
|
3775
3811
|
}
|
|
3776
3812
|
|
|
3777
|
-
// src/entity.ts
|
|
3778
|
-
|
|
3779
|
-
|
|
3780
|
-
|
|
3781
|
-
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
|
|
3787
|
-
|
|
3788
|
-
|
|
3789
|
-
|
|
3813
|
+
// src/entity/ddb-indexing.ts
|
|
3814
|
+
var IndexBuilder = class {
|
|
3815
|
+
/**
|
|
3816
|
+
* Creates a new IndexBuilder instance
|
|
3817
|
+
*
|
|
3818
|
+
* @param table - The DynamoDB table instance
|
|
3819
|
+
* @param indexes - The index definitions
|
|
3820
|
+
*/
|
|
3821
|
+
constructor(table, indexes = {}) {
|
|
3822
|
+
this.table = table;
|
|
3823
|
+
this.indexes = indexes;
|
|
3824
|
+
}
|
|
3825
|
+
/**
|
|
3826
|
+
* Build index attributes for item creation
|
|
3827
|
+
*
|
|
3828
|
+
* @param item - The item to generate indexes for
|
|
3829
|
+
* @param options - Options for building indexes
|
|
3830
|
+
* @returns Record of GSI attribute names to their values
|
|
3831
|
+
*/
|
|
3832
|
+
buildForCreate(item, options = {}) {
|
|
3833
|
+
const attributes = {};
|
|
3834
|
+
for (const [indexName, indexDef] of Object.entries(this.indexes)) {
|
|
3835
|
+
if (options.excludeReadOnly && indexDef.isReadOnly) {
|
|
3836
|
+
continue;
|
|
3837
|
+
}
|
|
3838
|
+
const key = indexDef.generateKey(item);
|
|
3839
|
+
const gsiConfig = this.table.gsis[indexName];
|
|
3840
|
+
if (!gsiConfig) {
|
|
3841
|
+
throw new Error(`GSI configuration not found for index: ${indexName}`);
|
|
3842
|
+
}
|
|
3843
|
+
if (key.pk) {
|
|
3844
|
+
attributes[gsiConfig.partitionKey] = key.pk;
|
|
3845
|
+
}
|
|
3846
|
+
if (key.sk && gsiConfig.sortKey) {
|
|
3847
|
+
attributes[gsiConfig.sortKey] = key.sk;
|
|
3848
|
+
}
|
|
3849
|
+
}
|
|
3850
|
+
return attributes;
|
|
3851
|
+
}
|
|
3852
|
+
/**
|
|
3853
|
+
* Build index attributes for item updates
|
|
3854
|
+
*
|
|
3855
|
+
* @param currentData - The current data before update
|
|
3856
|
+
* @param updates - The update data
|
|
3857
|
+
* @param options - Options for building indexes
|
|
3858
|
+
* @returns Record of GSI attribute names to their updated values
|
|
3859
|
+
*/
|
|
3860
|
+
buildForUpdate(currentData, updates) {
|
|
3861
|
+
const attributes = {};
|
|
3862
|
+
const updatedItem = { ...currentData, ...updates };
|
|
3863
|
+
for (const [indexName, indexDef] of Object.entries(this.indexes)) {
|
|
3864
|
+
if (indexDef.isReadOnly) {
|
|
3865
|
+
continue;
|
|
3866
|
+
}
|
|
3867
|
+
let shouldUpdateIndex = false;
|
|
3868
|
+
try {
|
|
3869
|
+
const currentKey = indexDef.generateKey(currentData);
|
|
3870
|
+
const updatedKey = indexDef.generateKey(updatedItem);
|
|
3871
|
+
if (currentKey.pk !== updatedKey.pk || currentKey.sk !== updatedKey.sk) {
|
|
3872
|
+
shouldUpdateIndex = true;
|
|
3790
3873
|
}
|
|
3791
|
-
|
|
3792
|
-
|
|
3874
|
+
} catch {
|
|
3875
|
+
shouldUpdateIndex = true;
|
|
3876
|
+
}
|
|
3877
|
+
if (!shouldUpdateIndex) {
|
|
3878
|
+
continue;
|
|
3879
|
+
}
|
|
3880
|
+
let key;
|
|
3881
|
+
try {
|
|
3882
|
+
key = indexDef.generateKey(updatedItem);
|
|
3883
|
+
} catch (error) {
|
|
3884
|
+
if (error instanceof Error) {
|
|
3885
|
+
throw new Error(`Missing attributes: ${error.message}`);
|
|
3793
3886
|
}
|
|
3794
|
-
|
|
3795
|
-
}
|
|
3796
|
-
{
|
|
3797
|
-
|
|
3887
|
+
throw error;
|
|
3888
|
+
}
|
|
3889
|
+
if (this.hasUndefinedValues(key)) {
|
|
3890
|
+
throw new Error(
|
|
3891
|
+
`Missing attributes: 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.`
|
|
3892
|
+
);
|
|
3893
|
+
}
|
|
3894
|
+
const gsiConfig = this.table.gsis[indexName];
|
|
3895
|
+
if (!gsiConfig) {
|
|
3896
|
+
throw new Error(`GSI configuration not found for index: ${indexName}`);
|
|
3897
|
+
}
|
|
3898
|
+
if (key.pk) {
|
|
3899
|
+
attributes[gsiConfig.partitionKey] = key.pk;
|
|
3900
|
+
}
|
|
3901
|
+
if (key.sk && gsiConfig.sortKey) {
|
|
3902
|
+
attributes[gsiConfig.sortKey] = key.sk;
|
|
3903
|
+
}
|
|
3904
|
+
}
|
|
3905
|
+
return attributes;
|
|
3906
|
+
}
|
|
3907
|
+
/**
|
|
3908
|
+
* Check if a key has undefined values
|
|
3909
|
+
*
|
|
3910
|
+
* @param key - The index key to check
|
|
3911
|
+
* @returns True if the key contains undefined values, false otherwise
|
|
3912
|
+
*/
|
|
3913
|
+
hasUndefinedValues(key) {
|
|
3914
|
+
return (key.pk?.includes("undefined") ?? false) || (key.sk?.includes("undefined") ?? false);
|
|
3915
|
+
}
|
|
3916
|
+
};
|
|
3917
|
+
|
|
3918
|
+
// src/entity/index-utils.ts
|
|
3919
|
+
function buildIndexes(dataForKeyGeneration, table, indexes, excludeReadOnly = false) {
|
|
3920
|
+
if (!indexes) {
|
|
3921
|
+
return {};
|
|
3922
|
+
}
|
|
3923
|
+
const indexBuilder = new IndexBuilder(table, indexes);
|
|
3924
|
+
return indexBuilder.buildForCreate(dataForKeyGeneration, { excludeReadOnly });
|
|
3925
|
+
}
|
|
3926
|
+
function buildIndexUpdates(currentData, updates, table, indexes) {
|
|
3927
|
+
if (!indexes) {
|
|
3928
|
+
return {};
|
|
3929
|
+
}
|
|
3930
|
+
const indexBuilder = new IndexBuilder(table, indexes);
|
|
3931
|
+
return indexBuilder.buildForUpdate(currentData, updates);
|
|
3932
|
+
}
|
|
3933
|
+
|
|
3934
|
+
// src/entity/entity.ts
|
|
3935
|
+
function defineEntity(config) {
|
|
3936
|
+
const entityTypeAttributeName = config.settings?.entityTypeAttributeName ?? "entityType";
|
|
3937
|
+
const buildIndexes2 = (dataForKeyGeneration, table, excludeReadOnly = false) => {
|
|
3938
|
+
return buildIndexes(dataForKeyGeneration, table, config.indexes, excludeReadOnly);
|
|
3798
3939
|
};
|
|
3799
3940
|
const wrapMethodWithPreparation = (originalMethod, prepareFn, context) => {
|
|
3800
3941
|
const wrappedMethod = (...args) => {
|
|
@@ -3846,7 +3987,7 @@ function defineEntity(config) {
|
|
|
3846
3987
|
...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
|
|
3847
3988
|
};
|
|
3848
3989
|
const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
|
|
3849
|
-
const indexes = buildIndexes(dataForKeyGeneration, table);
|
|
3990
|
+
const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
|
|
3850
3991
|
const validatedItem = {
|
|
3851
3992
|
...dataForKeyGeneration,
|
|
3852
3993
|
[entityTypeAttributeName]: config.name,
|
|
@@ -3872,7 +4013,7 @@ function defineEntity(config) {
|
|
|
3872
4013
|
...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
|
|
3873
4014
|
};
|
|
3874
4015
|
const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
|
|
3875
|
-
const indexes = buildIndexes(dataForKeyGeneration, table);
|
|
4016
|
+
const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
|
|
3876
4017
|
const validatedItem = {
|
|
3877
4018
|
...dataForKeyGeneration,
|
|
3878
4019
|
[entityTypeAttributeName]: config.name,
|
|
@@ -3914,7 +4055,7 @@ function defineEntity(config) {
|
|
|
3914
4055
|
...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
|
|
3915
4056
|
};
|
|
3916
4057
|
const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
|
|
3917
|
-
const indexes =
|
|
4058
|
+
const indexes = buildIndexes2(dataForKeyGeneration, table, false);
|
|
3918
4059
|
const validatedItem = {
|
|
3919
4060
|
[table.partitionKey]: primaryKey.pk,
|
|
3920
4061
|
...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
|
|
@@ -3940,7 +4081,7 @@ function defineEntity(config) {
|
|
|
3940
4081
|
...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
|
|
3941
4082
|
};
|
|
3942
4083
|
const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
|
|
3943
|
-
const indexes = buildIndexes(dataForKeyGeneration, table);
|
|
4084
|
+
const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
|
|
3944
4085
|
const validatedItem = {
|
|
3945
4086
|
[table.partitionKey]: primaryKey.pk,
|
|
3946
4087
|
...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
|
|
@@ -3974,13 +4115,21 @@ function defineEntity(config) {
|
|
|
3974
4115
|
}
|
|
3975
4116
|
return createEntityAwarePutBuilder(builder, config.name);
|
|
3976
4117
|
},
|
|
3977
|
-
get: (key) =>
|
|
4118
|
+
get: (key) => {
|
|
4119
|
+
return createEntityAwareGetBuilder(table.get(config.primaryKey.generateKey(key)), config.name);
|
|
4120
|
+
},
|
|
3978
4121
|
update: (key, data) => {
|
|
3979
4122
|
const primaryKeyObj = config.primaryKey.generateKey(key);
|
|
3980
4123
|
const builder = table.update(primaryKeyObj);
|
|
3981
4124
|
builder.condition(eq(entityTypeAttributeName, config.name));
|
|
3982
4125
|
const timestamps = generateTimestamps(["updatedAt"], data);
|
|
3983
|
-
|
|
4126
|
+
const indexUpdates = buildIndexUpdates(
|
|
4127
|
+
{ ...key },
|
|
4128
|
+
{ ...data, ...timestamps },
|
|
4129
|
+
table,
|
|
4130
|
+
config.indexes
|
|
4131
|
+
);
|
|
4132
|
+
builder.set({ ...data, ...timestamps, ...indexUpdates });
|
|
3984
4133
|
return builder;
|
|
3985
4134
|
},
|
|
3986
4135
|
delete: (key) => {
|
|
@@ -4051,21 +4200,57 @@ function createQueries() {
|
|
|
4051
4200
|
}
|
|
4052
4201
|
function createIndex() {
|
|
4053
4202
|
return {
|
|
4054
|
-
input: (schema) =>
|
|
4055
|
-
|
|
4056
|
-
|
|
4057
|
-
|
|
4058
|
-
|
|
4059
|
-
|
|
4060
|
-
|
|
4203
|
+
input: (schema) => {
|
|
4204
|
+
const createIndexBuilder = (isReadOnly = false) => ({
|
|
4205
|
+
partitionKey: (pkFn) => ({
|
|
4206
|
+
sortKey: (skFn) => {
|
|
4207
|
+
const index = {
|
|
4208
|
+
name: "custom",
|
|
4209
|
+
partitionKey: "pk",
|
|
4210
|
+
sortKey: "sk",
|
|
4211
|
+
isReadOnly,
|
|
4212
|
+
generateKey: (item) => {
|
|
4213
|
+
const data = schema["~standard"].validate(item);
|
|
4214
|
+
if ("issues" in data && data.issues) {
|
|
4215
|
+
throw new Error(`Index validation failed: ${data.issues.map((i) => i.message).join(", ")}`);
|
|
4216
|
+
}
|
|
4217
|
+
const validData = "value" in data ? data.value : item;
|
|
4218
|
+
return { pk: pkFn(validData), sk: skFn(validData) };
|
|
4219
|
+
}
|
|
4220
|
+
};
|
|
4221
|
+
return Object.assign(index, {
|
|
4222
|
+
readOnly: (value = false) => ({
|
|
4223
|
+
...index,
|
|
4224
|
+
isReadOnly: value
|
|
4225
|
+
})
|
|
4226
|
+
});
|
|
4227
|
+
},
|
|
4228
|
+
withoutSortKey: () => {
|
|
4229
|
+
const index = {
|
|
4230
|
+
name: "custom",
|
|
4231
|
+
partitionKey: "pk",
|
|
4232
|
+
isReadOnly,
|
|
4233
|
+
generateKey: (item) => {
|
|
4234
|
+
const data = schema["~standard"].validate(item);
|
|
4235
|
+
if ("issues" in data && data.issues) {
|
|
4236
|
+
throw new Error(`Index validation failed: ${data.issues.map((i) => i.message).join(", ")}`);
|
|
4237
|
+
}
|
|
4238
|
+
const validData = "value" in data ? data.value : item;
|
|
4239
|
+
return { pk: pkFn(validData) };
|
|
4240
|
+
}
|
|
4241
|
+
};
|
|
4242
|
+
return Object.assign(index, {
|
|
4243
|
+
readOnly: (value = true) => ({
|
|
4244
|
+
...index,
|
|
4245
|
+
isReadOnly: value
|
|
4246
|
+
})
|
|
4247
|
+
});
|
|
4248
|
+
}
|
|
4061
4249
|
}),
|
|
4062
|
-
|
|
4063
|
-
|
|
4064
|
-
|
|
4065
|
-
|
|
4066
|
-
})
|
|
4067
|
-
})
|
|
4068
|
-
})
|
|
4250
|
+
readOnly: (value = true) => createIndexBuilder(value)
|
|
4251
|
+
});
|
|
4252
|
+
return createIndexBuilder(false);
|
|
4253
|
+
}
|
|
4069
4254
|
};
|
|
4070
4255
|
}
|
|
4071
4256
|
|