dyno-table 2.0.1 → 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/{batch-builder-TpkmiBp5.d.ts → batch-builder-BytHNL_u.d.ts} +1 -1
- package/dist/{batch-builder-CzNAxWNT.d.cts → batch-builder-CcxFDKhe.d.cts} +1 -1
- package/dist/builders/condition-check-builder.cjs +19 -0
- package/dist/builders/condition-check-builder.cjs.map +1 -1
- package/dist/builders/condition-check-builder.d.cts +12 -3
- package/dist/builders/condition-check-builder.d.ts +12 -3
- package/dist/builders/condition-check-builder.js +19 -0
- package/dist/builders/condition-check-builder.js.map +1 -1
- package/dist/builders/delete-builder.cjs +19 -0
- package/dist/builders/delete-builder.cjs.map +1 -1
- package/dist/builders/delete-builder.d.cts +12 -3
- package/dist/builders/delete-builder.d.ts +12 -3
- package/dist/builders/delete-builder.js +19 -0
- 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 +19 -0
- package/dist/builders/put-builder.cjs.map +1 -1
- package/dist/builders/put-builder.d.cts +12 -3
- package/dist/builders/put-builder.d.ts +12 -3
- package/dist/builders/put-builder.js +19 -0
- package/dist/builders/put-builder.js.map +1 -1
- package/dist/builders/query-builder.cjs.map +1 -1
- package/dist/builders/query-builder.d.cts +2 -2
- package/dist/builders/query-builder.d.ts +2 -2
- package/dist/builders/query-builder.js.map +1 -1
- package/dist/builders/transaction-builder.cjs +19 -0
- package/dist/builders/transaction-builder.cjs.map +1 -1
- package/dist/builders/transaction-builder.d.cts +2 -2
- package/dist/builders/transaction-builder.d.ts +2 -2
- package/dist/builders/transaction-builder.js +19 -0
- package/dist/builders/transaction-builder.js.map +1 -1
- package/dist/builders/update-builder.cjs +19 -0
- package/dist/builders/update-builder.cjs.map +1 -1
- package/dist/builders/update-builder.d.cts +11 -2
- package/dist/builders/update-builder.d.ts +11 -2
- package/dist/builders/update-builder.js +19 -0
- package/dist/builders/update-builder.js.map +1 -1
- package/dist/{conditions-3ae5znV_.d.cts → conditions-CC3NDfUU.d.cts} +15 -13
- package/dist/{conditions-BtynAviC.d.ts → conditions-DD0bvyHm.d.ts} +15 -13
- package/dist/conditions.cjs.map +1 -1
- package/dist/conditions.d.cts +1 -1
- package/dist/conditions.d.ts +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 +20 -8
- package/dist/entity.d.ts +20 -8
- package/dist/entity.js +192 -42
- package/dist/entity.js.map +1 -1
- package/dist/index.cjs +203 -34
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +203 -34
- package/dist/index.js.map +1 -1
- package/dist/{query-builder-BK9eM-gt.d.ts → query-builder-BNWRCrJW.d.ts} +1 -1
- package/dist/{query-builder-BALW-vW_.d.cts → query-builder-DZ9JKgBN.d.cts} +1 -1
- package/dist/{table-CEL7Lt1r.d.ts → table-BhEeYauU.d.ts} +3 -3
- package/dist/{table-DhvYVXQ6.d.cts → table-BpNOboD9.d.cts} +3 -3
- package/dist/table.cjs +19 -0
- package/dist/table.cjs.map +1 -1
- package/dist/table.d.cts +4 -4
- package/dist/table.d.ts +4 -4
- package/dist/table.js +19 -0
- 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
|
@@ -47,6 +47,25 @@ var not = (condition) => ({
|
|
|
47
47
|
|
|
48
48
|
// src/expression.ts
|
|
49
49
|
var generateAttributeName = (params, attr) => {
|
|
50
|
+
if (attr.includes(".")) {
|
|
51
|
+
const pathSegments = attr.split(".");
|
|
52
|
+
const segmentNames = [];
|
|
53
|
+
for (const segment of pathSegments) {
|
|
54
|
+
let segmentName;
|
|
55
|
+
for (const [existingName, existingAttr] of Object.entries(params.expressionAttributeNames)) {
|
|
56
|
+
if (existingAttr === segment) {
|
|
57
|
+
segmentName = existingName;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
if (!segmentName) {
|
|
62
|
+
segmentName = `#${Object.keys(params.expressionAttributeNames).length}`;
|
|
63
|
+
params.expressionAttributeNames[segmentName] = segment;
|
|
64
|
+
}
|
|
65
|
+
segmentNames.push(segmentName);
|
|
66
|
+
}
|
|
67
|
+
return segmentNames.join(".");
|
|
68
|
+
}
|
|
50
69
|
for (const [existingName, existingAttr] of Object.entries(params.expressionAttributeNames)) {
|
|
51
70
|
if (existingAttr === attr) {
|
|
52
71
|
return existingName;
|
|
@@ -3755,27 +3774,133 @@ function createEntityAwareDeleteBuilder(builder, entityName) {
|
|
|
3755
3774
|
return createEntityAwareBuilder(builder, entityName);
|
|
3756
3775
|
}
|
|
3757
3776
|
|
|
3758
|
-
// src/entity.ts
|
|
3759
|
-
|
|
3760
|
-
|
|
3761
|
-
|
|
3762
|
-
|
|
3763
|
-
|
|
3764
|
-
|
|
3765
|
-
|
|
3777
|
+
// src/entity/ddb-indexing.ts
|
|
3778
|
+
var IndexBuilder = class {
|
|
3779
|
+
/**
|
|
3780
|
+
* Creates a new IndexBuilder instance
|
|
3781
|
+
*
|
|
3782
|
+
* @param table - The DynamoDB table instance
|
|
3783
|
+
* @param indexes - The index definitions
|
|
3784
|
+
*/
|
|
3785
|
+
constructor(table, indexes = {}) {
|
|
3786
|
+
this.table = table;
|
|
3787
|
+
this.indexes = indexes;
|
|
3788
|
+
}
|
|
3789
|
+
/**
|
|
3790
|
+
* Build index attributes for item creation
|
|
3791
|
+
*
|
|
3792
|
+
* @param item - The item to generate indexes for
|
|
3793
|
+
* @param options - Options for building indexes
|
|
3794
|
+
* @returns Record of GSI attribute names to their values
|
|
3795
|
+
*/
|
|
3796
|
+
buildForCreate(item, options = {}) {
|
|
3797
|
+
const attributes = {};
|
|
3798
|
+
for (const [indexName, indexDef] of Object.entries(this.indexes)) {
|
|
3799
|
+
if (options.excludeReadOnly && indexDef.isReadOnly) {
|
|
3800
|
+
continue;
|
|
3801
|
+
}
|
|
3802
|
+
const key = indexDef.generateKey(item);
|
|
3803
|
+
const gsiConfig = this.table.gsis[indexName];
|
|
3804
|
+
if (!gsiConfig) {
|
|
3805
|
+
throw new Error(`GSI configuration not found for index: ${indexName}`);
|
|
3806
|
+
}
|
|
3807
|
+
if (key.pk) {
|
|
3808
|
+
attributes[gsiConfig.partitionKey] = key.pk;
|
|
3809
|
+
}
|
|
3810
|
+
if (key.sk && gsiConfig.sortKey) {
|
|
3811
|
+
attributes[gsiConfig.sortKey] = key.sk;
|
|
3812
|
+
}
|
|
3813
|
+
}
|
|
3814
|
+
return attributes;
|
|
3815
|
+
}
|
|
3816
|
+
/**
|
|
3817
|
+
* Build index attributes for item updates
|
|
3818
|
+
*
|
|
3819
|
+
* @param currentData - The current data before update
|
|
3820
|
+
* @param updates - The update data
|
|
3821
|
+
* @param options - Options for building indexes
|
|
3822
|
+
* @returns Record of GSI attribute names to their updated values
|
|
3823
|
+
*/
|
|
3824
|
+
buildForUpdate(currentData, updates) {
|
|
3825
|
+
const attributes = {};
|
|
3826
|
+
const updatedItem = { ...currentData, ...updates };
|
|
3827
|
+
for (const [indexName, indexDef] of Object.entries(this.indexes)) {
|
|
3828
|
+
if (indexDef.isReadOnly) {
|
|
3829
|
+
continue;
|
|
3830
|
+
}
|
|
3831
|
+
let shouldUpdateIndex = false;
|
|
3832
|
+
try {
|
|
3833
|
+
const currentKey = indexDef.generateKey(currentData);
|
|
3834
|
+
const updatedKey = indexDef.generateKey(updatedItem);
|
|
3835
|
+
if (currentKey.pk !== updatedKey.pk || currentKey.sk !== updatedKey.sk) {
|
|
3836
|
+
shouldUpdateIndex = true;
|
|
3837
|
+
}
|
|
3838
|
+
} catch {
|
|
3839
|
+
shouldUpdateIndex = true;
|
|
3840
|
+
}
|
|
3841
|
+
if (!shouldUpdateIndex) {
|
|
3842
|
+
continue;
|
|
3843
|
+
}
|
|
3844
|
+
try {
|
|
3845
|
+
const key = indexDef.generateKey(updatedItem);
|
|
3846
|
+
if (this.hasUndefinedValues(key)) {
|
|
3847
|
+
throw new Error(
|
|
3848
|
+
`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.`
|
|
3849
|
+
);
|
|
3850
|
+
}
|
|
3851
|
+
const gsiConfig = this.table.gsis[indexName];
|
|
3766
3852
|
if (!gsiConfig) {
|
|
3767
3853
|
throw new Error(`GSI configuration not found for index: ${indexName}`);
|
|
3768
3854
|
}
|
|
3769
3855
|
if (key.pk) {
|
|
3770
|
-
|
|
3856
|
+
attributes[gsiConfig.partitionKey] = key.pk;
|
|
3771
3857
|
}
|
|
3772
3858
|
if (key.sk && gsiConfig.sortKey) {
|
|
3773
|
-
|
|
3859
|
+
attributes[gsiConfig.sortKey] = key.sk;
|
|
3774
3860
|
}
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3861
|
+
} catch (error) {
|
|
3862
|
+
if (error instanceof Error && error.message.includes("insufficient data")) {
|
|
3863
|
+
throw error;
|
|
3864
|
+
}
|
|
3865
|
+
throw new Error(
|
|
3866
|
+
`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.`
|
|
3867
|
+
);
|
|
3868
|
+
}
|
|
3869
|
+
}
|
|
3870
|
+
return attributes;
|
|
3871
|
+
}
|
|
3872
|
+
/**
|
|
3873
|
+
* Check if a key has undefined values
|
|
3874
|
+
*
|
|
3875
|
+
* @param key - The index key to check
|
|
3876
|
+
* @returns True if the key contains undefined values, false otherwise
|
|
3877
|
+
*/
|
|
3878
|
+
hasUndefinedValues(key) {
|
|
3879
|
+
return (key.pk?.includes("undefined") ?? false) || (key.sk?.includes("undefined") ?? false);
|
|
3880
|
+
}
|
|
3881
|
+
};
|
|
3882
|
+
|
|
3883
|
+
// src/entity/index-utils.ts
|
|
3884
|
+
function buildIndexes(dataForKeyGeneration, table, indexes, excludeReadOnly = false) {
|
|
3885
|
+
if (!indexes) {
|
|
3886
|
+
return {};
|
|
3887
|
+
}
|
|
3888
|
+
const indexBuilder = new IndexBuilder(table, indexes);
|
|
3889
|
+
return indexBuilder.buildForCreate(dataForKeyGeneration, { excludeReadOnly });
|
|
3890
|
+
}
|
|
3891
|
+
function buildIndexUpdates(currentData, updates, table, indexes) {
|
|
3892
|
+
if (!indexes) {
|
|
3893
|
+
return {};
|
|
3894
|
+
}
|
|
3895
|
+
const indexBuilder = new IndexBuilder(table, indexes);
|
|
3896
|
+
return indexBuilder.buildForUpdate(currentData, updates);
|
|
3897
|
+
}
|
|
3898
|
+
|
|
3899
|
+
// src/entity/entity.ts
|
|
3900
|
+
function defineEntity(config) {
|
|
3901
|
+
const entityTypeAttributeName = config.settings?.entityTypeAttributeName ?? "entityType";
|
|
3902
|
+
const buildIndexes2 = (dataForKeyGeneration, table, excludeReadOnly = false) => {
|
|
3903
|
+
return buildIndexes(dataForKeyGeneration, table, config.indexes, excludeReadOnly);
|
|
3779
3904
|
};
|
|
3780
3905
|
const wrapMethodWithPreparation = (originalMethod, prepareFn, context) => {
|
|
3781
3906
|
const wrappedMethod = (...args) => {
|
|
@@ -3827,7 +3952,7 @@ function defineEntity(config) {
|
|
|
3827
3952
|
...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
|
|
3828
3953
|
};
|
|
3829
3954
|
const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
|
|
3830
|
-
const indexes = buildIndexes(dataForKeyGeneration, table);
|
|
3955
|
+
const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
|
|
3831
3956
|
const validatedItem = {
|
|
3832
3957
|
...dataForKeyGeneration,
|
|
3833
3958
|
[entityTypeAttributeName]: config.name,
|
|
@@ -3853,7 +3978,7 @@ function defineEntity(config) {
|
|
|
3853
3978
|
...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
|
|
3854
3979
|
};
|
|
3855
3980
|
const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
|
|
3856
|
-
const indexes = buildIndexes(dataForKeyGeneration, table);
|
|
3981
|
+
const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
|
|
3857
3982
|
const validatedItem = {
|
|
3858
3983
|
...dataForKeyGeneration,
|
|
3859
3984
|
[entityTypeAttributeName]: config.name,
|
|
@@ -3895,7 +4020,7 @@ function defineEntity(config) {
|
|
|
3895
4020
|
...generateTimestamps(["createdAt", "updatedAt"], validatedData.value)
|
|
3896
4021
|
};
|
|
3897
4022
|
const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
|
|
3898
|
-
const indexes =
|
|
4023
|
+
const indexes = buildIndexes2(dataForKeyGeneration, table, false);
|
|
3899
4024
|
const validatedItem = {
|
|
3900
4025
|
[table.partitionKey]: primaryKey.pk,
|
|
3901
4026
|
...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
|
|
@@ -3921,7 +4046,7 @@ function defineEntity(config) {
|
|
|
3921
4046
|
...generateTimestamps(["createdAt", "updatedAt"], validationResult.value)
|
|
3922
4047
|
};
|
|
3923
4048
|
const primaryKey = config.primaryKey.generateKey(dataForKeyGeneration);
|
|
3924
|
-
const indexes = buildIndexes(dataForKeyGeneration, table);
|
|
4049
|
+
const indexes = buildIndexes(dataForKeyGeneration, table, config.indexes, false);
|
|
3925
4050
|
const validatedItem = {
|
|
3926
4051
|
[table.partitionKey]: primaryKey.pk,
|
|
3927
4052
|
...table.sortKey ? { [table.sortKey]: primaryKey.sk } : {},
|
|
@@ -3955,13 +4080,21 @@ function defineEntity(config) {
|
|
|
3955
4080
|
}
|
|
3956
4081
|
return createEntityAwarePutBuilder(builder, config.name);
|
|
3957
4082
|
},
|
|
3958
|
-
get: (key) =>
|
|
4083
|
+
get: (key) => {
|
|
4084
|
+
return createEntityAwareGetBuilder(table.get(config.primaryKey.generateKey(key)), config.name);
|
|
4085
|
+
},
|
|
3959
4086
|
update: (key, data) => {
|
|
3960
4087
|
const primaryKeyObj = config.primaryKey.generateKey(key);
|
|
3961
4088
|
const builder = table.update(primaryKeyObj);
|
|
3962
4089
|
builder.condition(eq(entityTypeAttributeName, config.name));
|
|
3963
4090
|
const timestamps = generateTimestamps(["updatedAt"], data);
|
|
3964
|
-
|
|
4091
|
+
const indexUpdates = buildIndexUpdates(
|
|
4092
|
+
{ ...key },
|
|
4093
|
+
{ ...data, ...timestamps },
|
|
4094
|
+
table,
|
|
4095
|
+
config.indexes
|
|
4096
|
+
);
|
|
4097
|
+
builder.set({ ...data, ...timestamps, ...indexUpdates });
|
|
3965
4098
|
return builder;
|
|
3966
4099
|
},
|
|
3967
4100
|
delete: (key) => {
|
|
@@ -4032,21 +4165,57 @@ function createQueries() {
|
|
|
4032
4165
|
}
|
|
4033
4166
|
function createIndex() {
|
|
4034
4167
|
return {
|
|
4035
|
-
input: (schema) =>
|
|
4036
|
-
|
|
4037
|
-
|
|
4038
|
-
|
|
4039
|
-
|
|
4040
|
-
|
|
4041
|
-
|
|
4168
|
+
input: (schema) => {
|
|
4169
|
+
const createIndexBuilder = (isReadOnly = false) => ({
|
|
4170
|
+
partitionKey: (pkFn) => ({
|
|
4171
|
+
sortKey: (skFn) => {
|
|
4172
|
+
const index = {
|
|
4173
|
+
name: "custom",
|
|
4174
|
+
partitionKey: "pk",
|
|
4175
|
+
sortKey: "sk",
|
|
4176
|
+
isReadOnly,
|
|
4177
|
+
generateKey: (item) => {
|
|
4178
|
+
const data = schema["~standard"].validate(item);
|
|
4179
|
+
if ("issues" in data && data.issues) {
|
|
4180
|
+
throw new Error(`Index validation failed: ${data.issues.map((i) => i.message).join(", ")}`);
|
|
4181
|
+
}
|
|
4182
|
+
const validData = "value" in data ? data.value : item;
|
|
4183
|
+
return { pk: pkFn(validData), sk: skFn(validData) };
|
|
4184
|
+
}
|
|
4185
|
+
};
|
|
4186
|
+
return Object.assign(index, {
|
|
4187
|
+
readOnly: (value = false) => ({
|
|
4188
|
+
...index,
|
|
4189
|
+
isReadOnly: value
|
|
4190
|
+
})
|
|
4191
|
+
});
|
|
4192
|
+
},
|
|
4193
|
+
withoutSortKey: () => {
|
|
4194
|
+
const index = {
|
|
4195
|
+
name: "custom",
|
|
4196
|
+
partitionKey: "pk",
|
|
4197
|
+
isReadOnly,
|
|
4198
|
+
generateKey: (item) => {
|
|
4199
|
+
const data = schema["~standard"].validate(item);
|
|
4200
|
+
if ("issues" in data && data.issues) {
|
|
4201
|
+
throw new Error(`Index validation failed: ${data.issues.map((i) => i.message).join(", ")}`);
|
|
4202
|
+
}
|
|
4203
|
+
const validData = "value" in data ? data.value : item;
|
|
4204
|
+
return { pk: pkFn(validData) };
|
|
4205
|
+
}
|
|
4206
|
+
};
|
|
4207
|
+
return Object.assign(index, {
|
|
4208
|
+
readOnly: (value = true) => ({
|
|
4209
|
+
...index,
|
|
4210
|
+
isReadOnly: value
|
|
4211
|
+
})
|
|
4212
|
+
});
|
|
4213
|
+
}
|
|
4042
4214
|
}),
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
-
|
|
4046
|
-
|
|
4047
|
-
})
|
|
4048
|
-
})
|
|
4049
|
-
})
|
|
4215
|
+
readOnly: (value = true) => createIndexBuilder(value)
|
|
4216
|
+
});
|
|
4217
|
+
return createIndexBuilder(false);
|
|
4218
|
+
}
|
|
4050
4219
|
};
|
|
4051
4220
|
}
|
|
4052
4221
|
|