dyno-table 2.1.1 → 2.2.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 +540 -376
- package/dist/builders/delete-builder.d.cts +1 -1
- package/dist/builders/delete-builder.d.ts +1 -1
- package/dist/builders/put-builder.cjs.map +1 -1
- package/dist/builders/put-builder.d.cts +1 -1
- package/dist/builders/put-builder.d.ts +1 -1
- package/dist/builders/put-builder.js.map +1 -1
- package/dist/builders/update-builder.cjs.map +1 -1
- package/dist/builders/update-builder.d.cts +6 -6
- package/dist/builders/update-builder.d.ts +6 -6
- package/dist/builders/update-builder.js.map +1 -1
- package/dist/entity.cjs +165 -22
- package/dist/entity.cjs.map +1 -1
- package/dist/entity.d.cts +90 -9
- package/dist/entity.d.ts +90 -9
- package/dist/entity.js +165 -22
- package/dist/entity.js.map +1 -1
- package/dist/index.cjs +2440 -2297
- 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 +2440 -2297
- package/dist/index.js.map +1 -1
- package/dist/{table-4UxlW_wD.d.ts → table-CZBMkW2Z.d.ts} +8 -8
- package/dist/{table-D-xNCVFa.d.cts → table-f-3wsT7K.d.cts} +8 -8
- package/dist/table.cjs +2313 -2313
- package/dist/table.cjs.map +1 -1
- package/dist/table.d.cts +9 -9
- package/dist/table.d.ts +9 -9
- package/dist/table.js +2313 -2313
- package/dist/table.js.map +1 -1
- package/package.json +2 -2
- package/dist/{batch-builder-CcxFDKhe.d.cts → batch-builder-BPoHyN_Q.d.cts} +1 -1
- package/dist/{batch-builder-BytHNL_u.d.ts → batch-builder-Cdo49C2r.d.ts} +1 -1
package/dist/entity.d.ts
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import { G as GetBuilder } from './batch-builder-BytHNL_u.js';
|
|
2
|
-
import { S as ScanBuilder, T as Table } from './table-4UxlW_wD.js';
|
|
3
|
-
import { UpdateBuilder } from './builders/update-builder.js';
|
|
4
|
-
import { StandardSchemaV1 } from './standard-schema.js';
|
|
5
1
|
import { DynamoItem, TableConfig, Index } from './types.js';
|
|
6
2
|
import { PutBuilder } from './builders/put-builder.js';
|
|
3
|
+
import { G as GetBuilder } from './batch-builder-Cdo49C2r.js';
|
|
7
4
|
import { DeleteBuilder } from './builders/delete-builder.js';
|
|
5
|
+
import { UpdateBuilder } from './builders/update-builder.js';
|
|
6
|
+
import { s as Path, t as PathType, C as Condition, q as ConditionOperator, r as PrimaryKeyWithoutExpression, P as PrimaryKey } from './conditions-DD0bvyHm.js';
|
|
7
|
+
import { TransactionBuilder } from './builders/transaction-builder.js';
|
|
8
|
+
import { U as UpdateCommandParams } from './builder-types-CzuLR4Th.js';
|
|
9
|
+
import { T as Table, S as ScanBuilder } from './table-CZBMkW2Z.js';
|
|
8
10
|
import { Q as QueryBuilder } from './query-builder-CUWdavZw.js';
|
|
9
|
-
import {
|
|
10
|
-
import './builder-types-CzuLR4Th.js';
|
|
11
|
-
import './builders/transaction-builder.js';
|
|
11
|
+
import { StandardSchemaV1 } from './standard-schema.js';
|
|
12
12
|
import '@aws-sdk/lib-dynamodb';
|
|
13
13
|
import './builders/condition-check-builder.js';
|
|
14
14
|
import './builders/paginator.js';
|
|
15
15
|
|
|
16
|
+
type SetElementType<T> = T extends Set<infer U> ? U : T extends Array<infer U> ? U : never;
|
|
17
|
+
type PathSetElementType<T, K extends Path<T>> = SetElementType<PathType<T, K>>;
|
|
16
18
|
/**
|
|
17
19
|
* Entity-aware wrapper for PutBuilder that automatically provides entity name to batch operations
|
|
18
20
|
*/
|
|
@@ -31,8 +33,87 @@ type EntityAwareGetBuilder<T extends DynamoItem> = GetBuilder<T> & {
|
|
|
31
33
|
type EntityAwareDeleteBuilder = DeleteBuilder & {
|
|
32
34
|
readonly entityName: string;
|
|
33
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* Entity-aware wrapper for UpdateBuilder that adds forceIndexRebuild functionality
|
|
38
|
+
* and automatically provides entity name to batch operations
|
|
39
|
+
*/
|
|
40
|
+
declare class EntityAwareUpdateBuilder<T extends DynamoItem> {
|
|
41
|
+
private forceRebuildIndexes;
|
|
42
|
+
readonly entityName: string;
|
|
43
|
+
private builder;
|
|
44
|
+
private entityConfig?;
|
|
45
|
+
private updateDataApplied;
|
|
46
|
+
constructor(builder: UpdateBuilder<T>, entityName: string);
|
|
47
|
+
/**
|
|
48
|
+
* Configure entity-specific logic for automatic timestamp generation and index updates
|
|
49
|
+
*/
|
|
50
|
+
configureEntityLogic(config: {
|
|
51
|
+
data: Partial<T>;
|
|
52
|
+
key: T;
|
|
53
|
+
table: Table;
|
|
54
|
+
indexes: Record<string, IndexDefinition<T>> | undefined;
|
|
55
|
+
generateTimestamps: () => Record<string, string | number>;
|
|
56
|
+
buildIndexUpdates: (currentData: T, updates: Partial<T>, table: Table, indexes: Record<string, IndexDefinition<T>> | undefined, forceRebuildIndexes?: string[]) => Record<string, string>;
|
|
57
|
+
}): void;
|
|
58
|
+
/**
|
|
59
|
+
* Forces a rebuild of one or more readonly indexes during the update operation.
|
|
60
|
+
*
|
|
61
|
+
* By default, readonly indexes are not updated during entity updates to prevent
|
|
62
|
+
* errors when required index attributes are missing. This method allows you to
|
|
63
|
+
* override that behavior and force specific indexes to be rebuilt.
|
|
64
|
+
*
|
|
65
|
+
* @example
|
|
66
|
+
* ```typescript
|
|
67
|
+
* // Force rebuild a single readonly index
|
|
68
|
+
* const result = await repo.update({ id: 'TREX-001' }, { status: 'ACTIVE' })
|
|
69
|
+
* .forceIndexRebuild('gsi1')
|
|
70
|
+
* .execute();
|
|
71
|
+
*
|
|
72
|
+
* // Force rebuild multiple readonly indexes
|
|
73
|
+
* const result = await repo.update({ id: 'TREX-001' }, { status: 'ACTIVE' })
|
|
74
|
+
* .forceIndexRebuild(['gsi1', 'gsi2'])
|
|
75
|
+
* .execute();
|
|
76
|
+
*
|
|
77
|
+
* // Chain with other update operations
|
|
78
|
+
* const result = await repo.update({ id: 'TREX-001' }, { status: 'ACTIVE' })
|
|
79
|
+
* .set('lastUpdated', new Date().toISOString())
|
|
80
|
+
* .forceIndexRebuild('gsi1')
|
|
81
|
+
* .condition(op => op.eq('status', 'INACTIVE'))
|
|
82
|
+
* .execute();
|
|
83
|
+
* ```
|
|
84
|
+
*
|
|
85
|
+
* @param indexes - A single index name or array of index names to force rebuild
|
|
86
|
+
* @returns The builder instance for method chaining
|
|
87
|
+
*/
|
|
88
|
+
forceIndexRebuild(indexes: string | string[]): this;
|
|
89
|
+
/**
|
|
90
|
+
* Gets the list of indexes that should be force rebuilt.
|
|
91
|
+
* This is used internally by entity update logic.
|
|
92
|
+
*
|
|
93
|
+
* @returns Array of index names to force rebuild
|
|
94
|
+
*/
|
|
95
|
+
getForceRebuildIndexes(): string[];
|
|
96
|
+
/**
|
|
97
|
+
* Apply entity-specific update data (timestamps and index updates)
|
|
98
|
+
* This is called automatically when needed
|
|
99
|
+
*/
|
|
100
|
+
private applyEntityUpdates;
|
|
101
|
+
set(values: Partial<T>): this;
|
|
102
|
+
set<K extends Path<T>>(path: K, value: PathType<T, K>): this;
|
|
103
|
+
remove<K extends Path<T>>(path: K): this;
|
|
104
|
+
add<K extends Path<T>>(path: K, value: PathType<T, K>): this;
|
|
105
|
+
deleteElementsFromSet<K extends Path<T>>(path: K, value: PathSetElementType<T, K>[] | Set<PathSetElementType<T, K>>): this;
|
|
106
|
+
condition(condition: Condition | ((op: ConditionOperator<T>) => Condition)): this;
|
|
107
|
+
returnValues(returnValues: "ALL_NEW" | "UPDATED_NEW" | "ALL_OLD" | "UPDATED_OLD" | "NONE"): this;
|
|
108
|
+
toDynamoCommand(): UpdateCommandParams;
|
|
109
|
+
withTransaction(transaction: TransactionBuilder): void;
|
|
110
|
+
debug(): ReturnType<UpdateBuilder<T>['debug']>;
|
|
111
|
+
execute(): Promise<{
|
|
112
|
+
item?: T;
|
|
113
|
+
}>;
|
|
114
|
+
}
|
|
34
115
|
|
|
35
|
-
type QueryFunction<
|
|
116
|
+
type QueryFunction<_T extends DynamoItem, I, R> = (input: I) => R;
|
|
36
117
|
type QueryFunctionWithSchema<T extends DynamoItem, I, R> = QueryFunction<T, I, R> & {
|
|
37
118
|
schema?: StandardSchemaV1<I>;
|
|
38
119
|
};
|
|
@@ -120,7 +201,7 @@ Q extends QueryRecord<T> = QueryRecord<T>> {
|
|
|
120
201
|
create: (data: TInput) => EntityAwarePutBuilder<T>;
|
|
121
202
|
upsert: (data: TInput & I) => EntityAwarePutBuilder<T>;
|
|
122
203
|
get: (key: I) => EntityAwareGetBuilder<T>;
|
|
123
|
-
update: (key: I, data: Partial<T>) =>
|
|
204
|
+
update: (key: I, data: Partial<T>) => EntityAwareUpdateBuilder<T>;
|
|
124
205
|
delete: (key: I) => EntityAwareDeleteBuilder;
|
|
125
206
|
query: Q;
|
|
126
207
|
scan: () => ScanBuilder<T>;
|
package/dist/entity.js
CHANGED
|
@@ -25,6 +25,137 @@ function createEntityAwareGetBuilder(builder, entityName) {
|
|
|
25
25
|
function createEntityAwareDeleteBuilder(builder, entityName) {
|
|
26
26
|
return createEntityAwareBuilder(builder, entityName);
|
|
27
27
|
}
|
|
28
|
+
var EntityAwareUpdateBuilder = class {
|
|
29
|
+
forceRebuildIndexes = [];
|
|
30
|
+
entityName;
|
|
31
|
+
builder;
|
|
32
|
+
entityConfig;
|
|
33
|
+
updateDataApplied = false;
|
|
34
|
+
constructor(builder, entityName) {
|
|
35
|
+
this.builder = builder;
|
|
36
|
+
this.entityName = entityName;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Configure entity-specific logic for automatic timestamp generation and index updates
|
|
40
|
+
*/
|
|
41
|
+
configureEntityLogic(config) {
|
|
42
|
+
this.entityConfig = config;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Forces a rebuild of one or more readonly indexes during the update operation.
|
|
46
|
+
*
|
|
47
|
+
* By default, readonly indexes are not updated during entity updates to prevent
|
|
48
|
+
* errors when required index attributes are missing. This method allows you to
|
|
49
|
+
* override that behavior and force specific indexes to be rebuilt.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```typescript
|
|
53
|
+
* // Force rebuild a single readonly index
|
|
54
|
+
* const result = await repo.update({ id: 'TREX-001' }, { status: 'ACTIVE' })
|
|
55
|
+
* .forceIndexRebuild('gsi1')
|
|
56
|
+
* .execute();
|
|
57
|
+
*
|
|
58
|
+
* // Force rebuild multiple readonly indexes
|
|
59
|
+
* const result = await repo.update({ id: 'TREX-001' }, { status: 'ACTIVE' })
|
|
60
|
+
* .forceIndexRebuild(['gsi1', 'gsi2'])
|
|
61
|
+
* .execute();
|
|
62
|
+
*
|
|
63
|
+
* // Chain with other update operations
|
|
64
|
+
* const result = await repo.update({ id: 'TREX-001' }, { status: 'ACTIVE' })
|
|
65
|
+
* .set('lastUpdated', new Date().toISOString())
|
|
66
|
+
* .forceIndexRebuild('gsi1')
|
|
67
|
+
* .condition(op => op.eq('status', 'INACTIVE'))
|
|
68
|
+
* .execute();
|
|
69
|
+
* ```
|
|
70
|
+
*
|
|
71
|
+
* @param indexes - A single index name or array of index names to force rebuild
|
|
72
|
+
* @returns The builder instance for method chaining
|
|
73
|
+
*/
|
|
74
|
+
forceIndexRebuild(indexes) {
|
|
75
|
+
if (Array.isArray(indexes)) {
|
|
76
|
+
this.forceRebuildIndexes = [...this.forceRebuildIndexes, ...indexes];
|
|
77
|
+
} else {
|
|
78
|
+
this.forceRebuildIndexes.push(indexes);
|
|
79
|
+
}
|
|
80
|
+
return this;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Gets the list of indexes that should be force rebuilt.
|
|
84
|
+
* This is used internally by entity update logic.
|
|
85
|
+
*
|
|
86
|
+
* @returns Array of index names to force rebuild
|
|
87
|
+
*/
|
|
88
|
+
getForceRebuildIndexes() {
|
|
89
|
+
return [...this.forceRebuildIndexes];
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Apply entity-specific update data (timestamps and index updates)
|
|
93
|
+
* This is called automatically when needed
|
|
94
|
+
*/
|
|
95
|
+
applyEntityUpdates() {
|
|
96
|
+
if (!this.entityConfig || this.updateDataApplied) return;
|
|
97
|
+
const timestamps = this.entityConfig.generateTimestamps();
|
|
98
|
+
const updatedItem = { ...this.entityConfig.key, ...this.entityConfig.data, ...timestamps };
|
|
99
|
+
const indexUpdates = this.entityConfig.buildIndexUpdates(
|
|
100
|
+
this.entityConfig.key,
|
|
101
|
+
updatedItem,
|
|
102
|
+
this.entityConfig.table,
|
|
103
|
+
this.entityConfig.indexes,
|
|
104
|
+
this.forceRebuildIndexes
|
|
105
|
+
);
|
|
106
|
+
this.builder.set({ ...this.entityConfig.data, ...timestamps, ...indexUpdates });
|
|
107
|
+
this.updateDataApplied = true;
|
|
108
|
+
}
|
|
109
|
+
set(valuesOrPath, value) {
|
|
110
|
+
if (typeof valuesOrPath === "object") {
|
|
111
|
+
this.builder.set(valuesOrPath);
|
|
112
|
+
} else {
|
|
113
|
+
if (value === void 0) {
|
|
114
|
+
throw new Error("Value is required when setting a single path");
|
|
115
|
+
}
|
|
116
|
+
this.builder.set(valuesOrPath, value);
|
|
117
|
+
}
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
remove(path) {
|
|
121
|
+
this.builder.remove(path);
|
|
122
|
+
return this;
|
|
123
|
+
}
|
|
124
|
+
add(path, value) {
|
|
125
|
+
this.builder.add(path, value);
|
|
126
|
+
return this;
|
|
127
|
+
}
|
|
128
|
+
deleteElementsFromSet(path, value) {
|
|
129
|
+
this.builder.deleteElementsFromSet(path, value);
|
|
130
|
+
return this;
|
|
131
|
+
}
|
|
132
|
+
condition(condition) {
|
|
133
|
+
this.builder.condition(condition);
|
|
134
|
+
return this;
|
|
135
|
+
}
|
|
136
|
+
returnValues(returnValues) {
|
|
137
|
+
this.builder.returnValues(returnValues);
|
|
138
|
+
return this;
|
|
139
|
+
}
|
|
140
|
+
toDynamoCommand() {
|
|
141
|
+
return this.builder.toDynamoCommand();
|
|
142
|
+
}
|
|
143
|
+
withTransaction(transaction) {
|
|
144
|
+
this.applyEntityUpdates();
|
|
145
|
+
this.builder.withTransaction(transaction);
|
|
146
|
+
}
|
|
147
|
+
debug() {
|
|
148
|
+
return this.builder.debug();
|
|
149
|
+
}
|
|
150
|
+
async execute() {
|
|
151
|
+
this.updateDataApplied = false;
|
|
152
|
+
this.applyEntityUpdates();
|
|
153
|
+
return this.builder.execute();
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
function createEntityAwareUpdateBuilder(builder, entityName) {
|
|
157
|
+
return new EntityAwareUpdateBuilder(builder, entityName);
|
|
158
|
+
}
|
|
28
159
|
|
|
29
160
|
// src/conditions.ts
|
|
30
161
|
var createComparisonCondition = (type) => (attr, value) => ({
|
|
@@ -81,25 +212,36 @@ var IndexBuilder = class {
|
|
|
81
212
|
* @param options - Options for building indexes
|
|
82
213
|
* @returns Record of GSI attribute names to their updated values
|
|
83
214
|
*/
|
|
84
|
-
buildForUpdate(currentData, updates) {
|
|
215
|
+
buildForUpdate(currentData, updates, options = {}) {
|
|
85
216
|
const attributes = {};
|
|
86
217
|
const updatedItem = { ...currentData, ...updates };
|
|
218
|
+
if (options.forceRebuildIndexes && options.forceRebuildIndexes.length > 0) {
|
|
219
|
+
const invalidIndexes = options.forceRebuildIndexes.filter((indexName) => !this.indexes[indexName]);
|
|
220
|
+
if (invalidIndexes.length > 0) {
|
|
221
|
+
throw new Error(
|
|
222
|
+
`Cannot force rebuild unknown indexes: ${invalidIndexes.join(", ")}. Available indexes: ${Object.keys(this.indexes).join(", ")}`
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
87
226
|
for (const [indexName, indexDef] of Object.entries(this.indexes)) {
|
|
88
|
-
|
|
227
|
+
const isForced = options.forceRebuildIndexes?.includes(indexName);
|
|
228
|
+
if (indexDef.isReadOnly && !isForced) {
|
|
89
229
|
continue;
|
|
90
230
|
}
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
231
|
+
if (!isForced) {
|
|
232
|
+
let shouldUpdateIndex = false;
|
|
233
|
+
try {
|
|
234
|
+
const currentKey = indexDef.generateKey(currentData);
|
|
235
|
+
const updatedKey = indexDef.generateKey(updatedItem);
|
|
236
|
+
if (currentKey.pk !== updatedKey.pk || currentKey.sk !== updatedKey.sk) {
|
|
237
|
+
shouldUpdateIndex = true;
|
|
238
|
+
}
|
|
239
|
+
} catch {
|
|
96
240
|
shouldUpdateIndex = true;
|
|
97
241
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
if (!shouldUpdateIndex) {
|
|
102
|
-
continue;
|
|
242
|
+
if (!shouldUpdateIndex) {
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
103
245
|
}
|
|
104
246
|
let key;
|
|
105
247
|
try {
|
|
@@ -147,12 +289,12 @@ function buildIndexes(dataForKeyGeneration, table, indexes, excludeReadOnly = fa
|
|
|
147
289
|
const indexBuilder = new IndexBuilder(table, indexes);
|
|
148
290
|
return indexBuilder.buildForCreate(dataForKeyGeneration, { excludeReadOnly });
|
|
149
291
|
}
|
|
150
|
-
function buildIndexUpdates(currentData, updates, table, indexes) {
|
|
292
|
+
function buildIndexUpdates(currentData, updates, table, indexes, forceRebuildIndexes) {
|
|
151
293
|
if (!indexes) {
|
|
152
294
|
return {};
|
|
153
295
|
}
|
|
154
296
|
const indexBuilder = new IndexBuilder(table, indexes);
|
|
155
|
-
return indexBuilder.buildForUpdate(currentData, updates);
|
|
297
|
+
return indexBuilder.buildForUpdate(currentData, updates, { forceRebuildIndexes });
|
|
156
298
|
}
|
|
157
299
|
|
|
158
300
|
// src/entity/entity.ts
|
|
@@ -346,15 +488,16 @@ function defineEntity(config) {
|
|
|
346
488
|
const primaryKeyObj = config.primaryKey.generateKey(key);
|
|
347
489
|
const builder = table.update(primaryKeyObj);
|
|
348
490
|
builder.condition(eq(entityTypeAttributeName, config.name));
|
|
349
|
-
const
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
491
|
+
const entityAwareBuilder = createEntityAwareUpdateBuilder(builder, config.name);
|
|
492
|
+
entityAwareBuilder.configureEntityLogic({
|
|
493
|
+
data,
|
|
494
|
+
key,
|
|
353
495
|
table,
|
|
354
|
-
config.indexes
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
496
|
+
indexes: config.indexes,
|
|
497
|
+
generateTimestamps: () => generateTimestamps(["updatedAt"], data),
|
|
498
|
+
buildIndexUpdates
|
|
499
|
+
});
|
|
500
|
+
return entityAwareBuilder;
|
|
358
501
|
},
|
|
359
502
|
delete: (key) => {
|
|
360
503
|
const builder = table.delete(config.primaryKey.generateKey(key));
|