graphddb 0.3.0 → 0.3.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 +1 -0
- package/dist/chunk-PNIZS37E.js +916 -0
- package/dist/{chunk-QEOFIXTN.js → chunk-VST3WOK3.js} +235 -556
- package/dist/{chunk-6AIAHP3A.js → chunk-YIXXTGZ6.js} +898 -1
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +477 -3
- package/dist/index.js +412 -17
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +2 -2
- package/dist/{types-xJRn5qkv.d.ts → types-DuJ08bgc.d.ts} +675 -64
- package/package.json +1 -1
- package/dist/chunk-QQNP43JL.js +0 -461
package/dist/index.js
CHANGED
|
@@ -2,8 +2,6 @@ import {
|
|
|
2
2
|
BatchGetResult,
|
|
3
3
|
DDBModel,
|
|
4
4
|
EDGE_WRITES_MARKER,
|
|
5
|
-
ENTITY_WRITES_MARKER,
|
|
6
|
-
LIFECYCLE_CONTRACT_MARKER,
|
|
7
5
|
MARKER_ROW_ENTITY,
|
|
8
6
|
OLD_VALUE_NAMESPACE,
|
|
9
7
|
RELATION_TRAVERSAL_CONCURRENCY,
|
|
@@ -16,7 +14,6 @@ import {
|
|
|
16
14
|
buildBridgeBundle,
|
|
17
15
|
buildContexts,
|
|
18
16
|
buildContracts,
|
|
19
|
-
buildMaintenanceGraph,
|
|
20
17
|
buildManifest,
|
|
21
18
|
buildOperations,
|
|
22
19
|
buildProjection,
|
|
@@ -42,7 +39,6 @@ import {
|
|
|
42
39
|
detectRelationFields,
|
|
43
40
|
edgeWrites,
|
|
44
41
|
encodeCursor,
|
|
45
|
-
entityWrites,
|
|
46
42
|
evaluateFilter,
|
|
47
43
|
execute,
|
|
48
44
|
executeBatchGet,
|
|
@@ -54,10 +50,8 @@ import {
|
|
|
54
50
|
executeQuery,
|
|
55
51
|
from,
|
|
56
52
|
getEdgeWrites,
|
|
57
|
-
getEntityWrites,
|
|
58
53
|
getImplicitKeyFields,
|
|
59
54
|
hydrate,
|
|
60
|
-
identity,
|
|
61
55
|
isCommandModelContract,
|
|
62
56
|
isCommandPlan,
|
|
63
57
|
isContractComposeNode,
|
|
@@ -66,23 +60,17 @@ import {
|
|
|
66
60
|
isContractKeyRef,
|
|
67
61
|
isContractParamRef,
|
|
68
62
|
isEdgeWritesDefinition,
|
|
69
|
-
isEntityWritesDefinition,
|
|
70
|
-
isLifecycleContract,
|
|
71
|
-
isMaintainTrigger,
|
|
72
63
|
isMutationFragment,
|
|
73
64
|
isMutationInputRef,
|
|
74
65
|
isPlannedCommandMethod,
|
|
75
66
|
isQueryModelContract,
|
|
76
67
|
isSelectBuilder,
|
|
77
68
|
isTransactionRef,
|
|
78
|
-
lifecyclePhaseForIntent,
|
|
79
|
-
maintainTrigger,
|
|
80
69
|
mapWithConcurrency,
|
|
81
70
|
mintContractKeyFieldRef,
|
|
82
71
|
mintContractParamRef,
|
|
83
72
|
mutation,
|
|
84
73
|
plan,
|
|
85
|
-
preview,
|
|
86
74
|
publicCommandModel,
|
|
87
75
|
publicQueryModel,
|
|
88
76
|
query,
|
|
@@ -91,11 +79,23 @@ import {
|
|
|
91
79
|
validateDepth,
|
|
92
80
|
when,
|
|
93
81
|
wholeKeysSentinel
|
|
94
|
-
} from "./chunk-
|
|
82
|
+
} from "./chunk-VST3WOK3.js";
|
|
95
83
|
import {
|
|
96
84
|
CdcEmulator,
|
|
97
|
-
|
|
98
|
-
|
|
85
|
+
InMemoryProjectionSink,
|
|
86
|
+
MAINT_OUTBOX_PK_PREFIX,
|
|
87
|
+
MaintenanceDrain,
|
|
88
|
+
ProjectionSinkDrain,
|
|
89
|
+
compareDesc,
|
|
90
|
+
createCdcEmulator,
|
|
91
|
+
createMaintenanceDrain,
|
|
92
|
+
createMaintenanceDrainHandler,
|
|
93
|
+
createProjectionSinkDrain,
|
|
94
|
+
createProjectionSinkHandler,
|
|
95
|
+
orderAndTrimCollection,
|
|
96
|
+
pathField,
|
|
97
|
+
projectFrom
|
|
98
|
+
} from "./chunk-PNIZS37E.js";
|
|
99
99
|
import {
|
|
100
100
|
BATCH_GET_MAX_KEYS,
|
|
101
101
|
BATCH_WRITE_MAX_ITEMS,
|
|
@@ -103,15 +103,19 @@ import {
|
|
|
103
103
|
DEFAULT_MAX_ATTEMPTS,
|
|
104
104
|
DEFAULT_RETRY_POLICY,
|
|
105
105
|
DynamoExecutor,
|
|
106
|
+
ENTITY_WRITES_MARKER,
|
|
107
|
+
LIFECYCLE_CONTRACT_MARKER,
|
|
106
108
|
Linter,
|
|
107
109
|
MAX_TRANSACT_ITEMS,
|
|
108
110
|
MetadataRegistry,
|
|
109
111
|
RetryingExecutor,
|
|
110
112
|
TableMapping,
|
|
111
113
|
TransactionContext,
|
|
114
|
+
ViewRegistry,
|
|
112
115
|
attachModelClass,
|
|
113
116
|
buildConditionExpression,
|
|
114
117
|
buildDeleteInput,
|
|
118
|
+
buildMaintenanceGraph,
|
|
115
119
|
buildPutInput,
|
|
116
120
|
buildUpdateExpression,
|
|
117
121
|
buildUpdateInput,
|
|
@@ -120,31 +124,49 @@ import {
|
|
|
120
124
|
commitTransaction,
|
|
121
125
|
cond,
|
|
122
126
|
createDefaultLinter,
|
|
127
|
+
defineProjection,
|
|
128
|
+
defineProjections,
|
|
129
|
+
defineVersioned,
|
|
130
|
+
defineView,
|
|
131
|
+
defineViews,
|
|
132
|
+
entityWrites,
|
|
123
133
|
execItemKeySignature,
|
|
124
134
|
executeDelete,
|
|
125
135
|
executePut,
|
|
126
136
|
executeTransaction,
|
|
127
137
|
executeUpdate,
|
|
138
|
+
getEntityWrites,
|
|
128
139
|
gsi,
|
|
129
140
|
gsiAmbiguityRule,
|
|
141
|
+
identity,
|
|
130
142
|
isColumn,
|
|
143
|
+
isEntityWritesDefinition,
|
|
131
144
|
isKeySegment,
|
|
145
|
+
isLifecycleContract,
|
|
146
|
+
isMaintainTrigger,
|
|
132
147
|
isParam,
|
|
148
|
+
isProjectionDefinition,
|
|
133
149
|
isRawCondition,
|
|
134
150
|
isRetryableError,
|
|
135
151
|
isRetryableTransactionCancellation,
|
|
152
|
+
isViewDefinition,
|
|
136
153
|
k,
|
|
137
154
|
key,
|
|
155
|
+
lifecyclePhaseForIntent,
|
|
156
|
+
maintainTrigger,
|
|
138
157
|
missingGsiRule,
|
|
139
158
|
noScanRule,
|
|
140
159
|
param,
|
|
160
|
+
preview,
|
|
141
161
|
relationDepthRule,
|
|
142
162
|
requireLimitRule,
|
|
143
163
|
resolveConditionTree,
|
|
144
164
|
resolveKey,
|
|
145
165
|
resolveModelClass,
|
|
146
|
-
|
|
147
|
-
|
|
166
|
+
segmentFieldNames,
|
|
167
|
+
serializeFieldValue,
|
|
168
|
+
whenMember
|
|
169
|
+
} from "./chunk-YIXXTGZ6.js";
|
|
148
170
|
|
|
149
171
|
// src/metadata/prefix.ts
|
|
150
172
|
function derivePrefix(customPrefix, className) {
|
|
@@ -955,6 +977,360 @@ var queryBoundaryRule = {
|
|
|
955
977
|
}
|
|
956
978
|
};
|
|
957
979
|
|
|
980
|
+
// src/relation/maintenance-rebuild.ts
|
|
981
|
+
function toClass(m) {
|
|
982
|
+
try {
|
|
983
|
+
return resolveModelClass(m);
|
|
984
|
+
} catch {
|
|
985
|
+
return m;
|
|
986
|
+
}
|
|
987
|
+
}
|
|
988
|
+
var MaintenanceRebuilder = class {
|
|
989
|
+
graph;
|
|
990
|
+
constructor(opts = {}) {
|
|
991
|
+
const classes = (opts.models ?? []).map(toClass);
|
|
992
|
+
const views = opts.views ?? [];
|
|
993
|
+
const allClasses = [...classes, ...views.map((v) => v.viewClass)];
|
|
994
|
+
const scoped = allClasses.length > 0 ? new Map(allClasses.map((c) => [c, MetadataRegistry.get(c)])) : void 0;
|
|
995
|
+
this.graph = buildMaintenanceGraph(scoped, views);
|
|
996
|
+
}
|
|
997
|
+
/**
|
|
998
|
+
* Re-derive the materialized value for one owner row × maintainer from the source
|
|
999
|
+
* rows and report whether it differs from what the owner row currently stores. Does
|
|
1000
|
+
* NOT write anything (a read-only drift probe).
|
|
1001
|
+
*
|
|
1002
|
+
* @param owner The owner model class declaring the maintainer.
|
|
1003
|
+
* @param relationProperty The relation / `@aggregate` property the value lives under.
|
|
1004
|
+
* @param ownerKeyInput The owner row's key input (e.g. `{ threadId: 't1' }`).
|
|
1005
|
+
*/
|
|
1006
|
+
async detectDrift(owner, relationProperty, ownerKeyInput) {
|
|
1007
|
+
const { item, ownerMeta, ownerKey } = this.resolve(owner, relationProperty);
|
|
1008
|
+
const expected = await this.recompute(item, ownerMeta, ownerKeyInput);
|
|
1009
|
+
const ownerRow = await this.readOwnerRow(ownerMeta, ownerKey(ownerKeyInput));
|
|
1010
|
+
const actual = this.actualValue(item.effect, ownerRow);
|
|
1011
|
+
const ownerAbsentAndEmpty = ownerRow === void 0 && isEmptyMaintained(item.effect, expected);
|
|
1012
|
+
return {
|
|
1013
|
+
relationProperty,
|
|
1014
|
+
ownerEntity: item.ownerEntity,
|
|
1015
|
+
ownerKey: ownerKey(ownerKeyInput),
|
|
1016
|
+
kind: item.effect.kind,
|
|
1017
|
+
drifted: !ownerAbsentAndEmpty && !deepEqual(normalize(expected), normalize(actual)),
|
|
1018
|
+
expected,
|
|
1019
|
+
actual
|
|
1020
|
+
};
|
|
1021
|
+
}
|
|
1022
|
+
/**
|
|
1023
|
+
* Re-derive the materialized value for one owner row × maintainer from the source
|
|
1024
|
+
* rows and WRITE the absolute re-derived value onto the owner row when it drifts (or
|
|
1025
|
+
* always, when `force`). Returns the drift verdict plus whether a repair write ran.
|
|
1026
|
+
*
|
|
1027
|
+
* @param options.force Write even when no drift is detected (default: write only on drift).
|
|
1028
|
+
*/
|
|
1029
|
+
async rebuild(owner, relationProperty, ownerKeyInput, options = {}) {
|
|
1030
|
+
const report = await this.detectDrift(owner, relationProperty, ownerKeyInput);
|
|
1031
|
+
if (!report.drifted && options.force !== true) {
|
|
1032
|
+
return { ...report, repaired: false };
|
|
1033
|
+
}
|
|
1034
|
+
const { item, ownerMeta } = this.resolve(owner, relationProperty);
|
|
1035
|
+
await this.writeValue(item.effect, ownerMeta, report.ownerKey, report.expected);
|
|
1036
|
+
return { ...report, repaired: true };
|
|
1037
|
+
}
|
|
1038
|
+
/** Resolve an owner model + property to its {@link MaintainItem} + owner-key builder. */
|
|
1039
|
+
resolve(owner, relationProperty) {
|
|
1040
|
+
const ownerClass = toClass(owner);
|
|
1041
|
+
const ownerMeta = MetadataRegistry.get(ownerClass);
|
|
1042
|
+
const item = this.graph.items.find(
|
|
1043
|
+
(i) => i.relationProperty === relationProperty && (i.ownerClass === ownerClass || i.ownerClass.name === ownerClass.name)
|
|
1044
|
+
);
|
|
1045
|
+
if (item === void 0) {
|
|
1046
|
+
throw new Error(
|
|
1047
|
+
`Maintenance rebuild: no maintainer found for property '${relationProperty}' on '${ownerClass.name}'. The rebuilder repairs declared \`maintainedOn\` maintainers (snapshot / collection / counter); '${relationProperty}' declares none, or its model is not in this rebuilder's scope.`
|
|
1048
|
+
);
|
|
1049
|
+
}
|
|
1050
|
+
const ownerKey = (input) => {
|
|
1051
|
+
const { Key } = buildDeleteInput(
|
|
1052
|
+
ownerClass,
|
|
1053
|
+
input
|
|
1054
|
+
);
|
|
1055
|
+
return Key;
|
|
1056
|
+
};
|
|
1057
|
+
return { item, ownerMeta, ownerKey };
|
|
1058
|
+
}
|
|
1059
|
+
/**
|
|
1060
|
+
* Scan every source row bound to this owner row and re-derive the maintained value:
|
|
1061
|
+
*
|
|
1062
|
+
* - **collection** — project each source row, order + trim by the SAME read-side
|
|
1063
|
+
* rules (`orderBy` / `orderDir` / `maxItems`) the drain uses, return the list;
|
|
1064
|
+
* - **snapshot** — project the single bound source row (or `undefined` when none);
|
|
1065
|
+
* - **counter `count`** — the COUNT of source rows (absolute, not an ADD);
|
|
1066
|
+
* - **counter `max`** — the maximum source value of the tracked field.
|
|
1067
|
+
*/
|
|
1068
|
+
async recompute(item, _ownerMeta, ownerKeyInput) {
|
|
1069
|
+
const effect = item.effect;
|
|
1070
|
+
if (effect.kind === "membership") {
|
|
1071
|
+
throw new Error(
|
|
1072
|
+
`Maintenance rebuild: sparse-view membership maintainer '${item.relationProperty}' on '${item.ownerEntity}' is not yet supported by the rebuild planner (#133 follow-up). A membership row appears/disappears per source row by predicate; rebuild it by re-running the source events through the drain.`
|
|
1073
|
+
);
|
|
1074
|
+
}
|
|
1075
|
+
const sources = await this.scanSourceRows(item, ownerKeyInput);
|
|
1076
|
+
if (effect.kind === "collection") {
|
|
1077
|
+
return this.recomputeCollection(effect, sources);
|
|
1078
|
+
}
|
|
1079
|
+
if (effect.kind === "snapshot") {
|
|
1080
|
+
return this.recomputeSnapshot(effect, sources);
|
|
1081
|
+
}
|
|
1082
|
+
return this.recomputeCounter(effect, sources);
|
|
1083
|
+
}
|
|
1084
|
+
/**
|
|
1085
|
+
* The source rows bound to one owner row. The maintenance key binding is
|
|
1086
|
+
* `{ ownerField: '$.entity.<sourceField>' }` (the destination owner-key field ← the
|
|
1087
|
+
* source row's field). INVERTED for a rebuild: the owner row supplies the value, and
|
|
1088
|
+
* the source partition is queried for rows whose `<sourceField>` matches — exactly the
|
|
1089
|
+
* set the live `hasMany` / `@aggregate` read aggregates. Queries the source via the
|
|
1090
|
+
* same {@link plan} the read planner uses (a partition Query), paginating fully.
|
|
1091
|
+
*/
|
|
1092
|
+
async scanSourceRows(item, ownerKeyInput) {
|
|
1093
|
+
const sourceMeta = MetadataRegistry.get(item.sourceClass);
|
|
1094
|
+
const queryKey = {};
|
|
1095
|
+
for (const [ownerField, srcPath] of Object.entries(item.effect.keys)) {
|
|
1096
|
+
const sourceField = pathField(srcPath);
|
|
1097
|
+
queryKey[sourceField] = ownerKeyInput[ownerField];
|
|
1098
|
+
}
|
|
1099
|
+
const executor = ClientManager.getExecutor();
|
|
1100
|
+
const rows = [];
|
|
1101
|
+
let after;
|
|
1102
|
+
do {
|
|
1103
|
+
const execPlan = plan(sourceMeta, {
|
|
1104
|
+
key: queryKey,
|
|
1105
|
+
// Select all declared source fields so every projection / value source path
|
|
1106
|
+
// and the sort-key leaf is present on the scanned row.
|
|
1107
|
+
select: selectAll(sourceMeta),
|
|
1108
|
+
consistentRead: false,
|
|
1109
|
+
...after ? { after } : {}
|
|
1110
|
+
});
|
|
1111
|
+
const op = execPlan.operations[0];
|
|
1112
|
+
const res = await executor.execute(op);
|
|
1113
|
+
for (const it of res.items) rows.push(it);
|
|
1114
|
+
after = res.lastEvaluatedKey;
|
|
1115
|
+
} while (after !== void 0);
|
|
1116
|
+
return rows;
|
|
1117
|
+
}
|
|
1118
|
+
/** Re-derive a bounded collection: project, then order + trim by the read-side rules. */
|
|
1119
|
+
recomputeCollection(effect, sources) {
|
|
1120
|
+
if (effect.collection.orderBy === void 0 && effect.collection.maxItems !== void 0) {
|
|
1121
|
+
throw new Error(
|
|
1122
|
+
`Maintenance rebuild: relation '${effect.collection.field}' maintains an UNORDERED bounded collection (\`maxItems: ${effect.collection.maxItems}\` with no \`read.order\`). Its trimmed contents depend on stream ARRIVAL order, which the source rows do not record, so the value cannot be faithfully rebuilt from source \u2014 a rebuild would keep a different subset than the drain converged to. Declare a \`read.order\` on the relation so the maintained collection orders by the source's sort-key field (then the rebuild and the drain agree), or rebuild it unbounded.`
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
const projected = sources.map((row) => projectFrom(effect.project, row));
|
|
1126
|
+
return orderAndTrimCollection(projected, effect.collection);
|
|
1127
|
+
}
|
|
1128
|
+
/**
|
|
1129
|
+
* Re-derive a single-row snapshot. A snapshot mirrors ONE source row (a `belongsTo` /
|
|
1130
|
+
* `hasOne`, normally 1:1). In the unusual case where MORE THAN ONE source row binds, the
|
|
1131
|
+
* live single-value read takes the FIRST row in the source's natural query order
|
|
1132
|
+
* (`ScanIndexForward`, default ASC = the smallest sort-key leaf), so re-derive the same
|
|
1133
|
+
* row — {@link pickFirst} — for a verdict consistent with the live read. Returns
|
|
1134
|
+
* `undefined` when no source row is bound (the snapshot should be absent).
|
|
1135
|
+
*/
|
|
1136
|
+
recomputeSnapshot(effect, sources) {
|
|
1137
|
+
if (sources.length === 0) return void 0;
|
|
1138
|
+
const chosen = sources.length === 1 ? sources[0] : pickFirst(sources, this.sourceMetaOf(effect));
|
|
1139
|
+
return projectFrom(effect.project, chosen);
|
|
1140
|
+
}
|
|
1141
|
+
/** The source entity metadata a maintain effect projects from (its relation target). */
|
|
1142
|
+
sourceMetaOf(effect) {
|
|
1143
|
+
const item = this.graph.items.find((i) => i.effect === effect);
|
|
1144
|
+
return item ? MetadataRegistry.get(item.sourceClass) : void 0;
|
|
1145
|
+
}
|
|
1146
|
+
/** Re-derive a counter: the absolute COUNT of source rows, or the MAX of the tracked field. */
|
|
1147
|
+
recomputeCounter(effect, sources) {
|
|
1148
|
+
if (effect.value.op === "count") {
|
|
1149
|
+
return sources.length;
|
|
1150
|
+
}
|
|
1151
|
+
const field2 = effect.value.field;
|
|
1152
|
+
let best;
|
|
1153
|
+
for (const row of sources) {
|
|
1154
|
+
const v = row[field2];
|
|
1155
|
+
if (v === void 0 || v === null) continue;
|
|
1156
|
+
if (best === void 0 || compareDesc(v, best) < 0) best = v;
|
|
1157
|
+
}
|
|
1158
|
+
return best;
|
|
1159
|
+
}
|
|
1160
|
+
/** Read the owner row (consistent) for the drift comparison / actual value. */
|
|
1161
|
+
async readOwnerRow(ownerMeta, ownerKey) {
|
|
1162
|
+
const res = await ClientManager.getExecutor().execute({
|
|
1163
|
+
type: "GetItem",
|
|
1164
|
+
tableName: TableMapping.resolve(ownerMeta.tableName),
|
|
1165
|
+
keyCondition: ownerKey,
|
|
1166
|
+
consistentRead: true
|
|
1167
|
+
});
|
|
1168
|
+
return res.items[0];
|
|
1169
|
+
}
|
|
1170
|
+
/** The value currently stored on the owner row for this maintainer. */
|
|
1171
|
+
actualValue(effect, ownerRow) {
|
|
1172
|
+
if (ownerRow === void 0) return void 0;
|
|
1173
|
+
if (effect.kind === "collection") {
|
|
1174
|
+
return ownerRow[effect.collection.field];
|
|
1175
|
+
}
|
|
1176
|
+
if (effect.kind === "counter") {
|
|
1177
|
+
return ownerRow[effect.attribute];
|
|
1178
|
+
}
|
|
1179
|
+
const out = {};
|
|
1180
|
+
let anyPresent = false;
|
|
1181
|
+
for (const attr of Object.keys(effect.project)) {
|
|
1182
|
+
if (Object.prototype.hasOwnProperty.call(ownerRow, attr)) {
|
|
1183
|
+
out[attr] = ownerRow[attr];
|
|
1184
|
+
anyPresent = true;
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
return anyPresent ? out : void 0;
|
|
1188
|
+
}
|
|
1189
|
+
/** Write the re-derived ABSOLUTE value onto the owner row (the repair). */
|
|
1190
|
+
async writeValue(effect, ownerMeta, ownerKey, expected) {
|
|
1191
|
+
const TableName = TableMapping.resolve(ownerMeta.tableName);
|
|
1192
|
+
const executor = ClientManager.getExecutor();
|
|
1193
|
+
if (effect.kind === "collection") {
|
|
1194
|
+
await executor.update({
|
|
1195
|
+
TableName,
|
|
1196
|
+
Key: ownerKey,
|
|
1197
|
+
UpdateExpression: "SET #c = :list",
|
|
1198
|
+
ExpressionAttributeNames: { "#c": effect.collection.field },
|
|
1199
|
+
ExpressionAttributeValues: { ":list": expected ?? [] }
|
|
1200
|
+
});
|
|
1201
|
+
return;
|
|
1202
|
+
}
|
|
1203
|
+
if (effect.kind === "counter") {
|
|
1204
|
+
if (expected === void 0 || expected === null) {
|
|
1205
|
+
if (effect.value.op === "count") {
|
|
1206
|
+
await executor.update({
|
|
1207
|
+
TableName,
|
|
1208
|
+
Key: ownerKey,
|
|
1209
|
+
UpdateExpression: "SET #a = :v",
|
|
1210
|
+
ExpressionAttributeNames: { "#a": effect.attribute },
|
|
1211
|
+
ExpressionAttributeValues: { ":v": 0 }
|
|
1212
|
+
});
|
|
1213
|
+
} else {
|
|
1214
|
+
await executor.update({
|
|
1215
|
+
TableName,
|
|
1216
|
+
Key: ownerKey,
|
|
1217
|
+
UpdateExpression: "REMOVE #a",
|
|
1218
|
+
ExpressionAttributeNames: { "#a": effect.attribute }
|
|
1219
|
+
});
|
|
1220
|
+
}
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
await executor.update({
|
|
1224
|
+
TableName,
|
|
1225
|
+
Key: ownerKey,
|
|
1226
|
+
UpdateExpression: "SET #a = :v",
|
|
1227
|
+
ExpressionAttributeNames: { "#a": effect.attribute },
|
|
1228
|
+
ExpressionAttributeValues: { ":v": expected }
|
|
1229
|
+
});
|
|
1230
|
+
return;
|
|
1231
|
+
}
|
|
1232
|
+
if (expected === void 0) {
|
|
1233
|
+
const names2 = {};
|
|
1234
|
+
const removes = [];
|
|
1235
|
+
let i2 = 0;
|
|
1236
|
+
for (const attr of Object.keys(effect.project)) {
|
|
1237
|
+
names2[`#m${i2}`] = attr;
|
|
1238
|
+
removes.push(`#m${i2}`);
|
|
1239
|
+
i2 += 1;
|
|
1240
|
+
}
|
|
1241
|
+
if (removes.length === 0) return;
|
|
1242
|
+
await executor.update({
|
|
1243
|
+
TableName,
|
|
1244
|
+
Key: ownerKey,
|
|
1245
|
+
UpdateExpression: `REMOVE ${removes.join(", ")}`,
|
|
1246
|
+
ExpressionAttributeNames: names2
|
|
1247
|
+
});
|
|
1248
|
+
return;
|
|
1249
|
+
}
|
|
1250
|
+
const projection = expected;
|
|
1251
|
+
const names = {};
|
|
1252
|
+
const values = {};
|
|
1253
|
+
const sets = [];
|
|
1254
|
+
let i = 0;
|
|
1255
|
+
for (const [attr, value] of Object.entries(projection)) {
|
|
1256
|
+
names[`#m${i}`] = attr;
|
|
1257
|
+
values[`:m${i}`] = value;
|
|
1258
|
+
sets.push(`#m${i} = :m${i}`);
|
|
1259
|
+
i += 1;
|
|
1260
|
+
}
|
|
1261
|
+
if (sets.length === 0) return;
|
|
1262
|
+
await executor.update({
|
|
1263
|
+
TableName,
|
|
1264
|
+
Key: ownerKey,
|
|
1265
|
+
UpdateExpression: `SET ${sets.join(", ")}`,
|
|
1266
|
+
ExpressionAttributeNames: names,
|
|
1267
|
+
ExpressionAttributeValues: values
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
};
|
|
1271
|
+
function selectAll(meta) {
|
|
1272
|
+
const select = {};
|
|
1273
|
+
for (const f of meta.fields) select[f.propertyName] = true;
|
|
1274
|
+
return select;
|
|
1275
|
+
}
|
|
1276
|
+
function pickFirst(sources, sourceMeta) {
|
|
1277
|
+
const skFields = sourceMeta?.primaryKey ? segmentFieldNames(sourceMeta.primaryKey.segmented.skSegments) : [];
|
|
1278
|
+
const orderField = skFields[skFields.length - 1];
|
|
1279
|
+
if (orderField === void 0) return sources[0];
|
|
1280
|
+
return [...sources].sort((a, b) => compareDesc(b[orderField], a[orderField]))[0];
|
|
1281
|
+
}
|
|
1282
|
+
function normalize(value) {
|
|
1283
|
+
if (value === void 0 || value === null) return void 0;
|
|
1284
|
+
if (typeof value === "number") return value;
|
|
1285
|
+
if (typeof value === "string" && /^-?\d+$/.test(value)) {
|
|
1286
|
+
const n = Number(value);
|
|
1287
|
+
if (Number.isSafeInteger(n)) return n;
|
|
1288
|
+
}
|
|
1289
|
+
if (Array.isArray(value)) return value.map(normalize);
|
|
1290
|
+
if (typeof value === "object") {
|
|
1291
|
+
const out = {};
|
|
1292
|
+
for (const [k2, v] of Object.entries(value)) {
|
|
1293
|
+
const nv = normalize(v);
|
|
1294
|
+
if (nv === void 0) continue;
|
|
1295
|
+
out[k2] = nv;
|
|
1296
|
+
}
|
|
1297
|
+
return out;
|
|
1298
|
+
}
|
|
1299
|
+
return value;
|
|
1300
|
+
}
|
|
1301
|
+
function isEmptyMaintained(effect, expected) {
|
|
1302
|
+
if (effect.kind === "collection") {
|
|
1303
|
+
return Array.isArray(expected) && expected.length === 0;
|
|
1304
|
+
}
|
|
1305
|
+
if (effect.kind === "counter") {
|
|
1306
|
+
return effect.value.op === "count" ? expected === 0 : expected === void 0 || expected === null;
|
|
1307
|
+
}
|
|
1308
|
+
return expected === void 0;
|
|
1309
|
+
}
|
|
1310
|
+
function deepEqual(a, b) {
|
|
1311
|
+
if (a === b) return true;
|
|
1312
|
+
if (a === void 0 || b === void 0 || a === null || b === null) return a === b;
|
|
1313
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
1314
|
+
if (a.length !== b.length) return false;
|
|
1315
|
+
return a.every((v, i) => deepEqual(v, b[i]));
|
|
1316
|
+
}
|
|
1317
|
+
if (typeof a === "object" && typeof b === "object") {
|
|
1318
|
+
const ka = Object.keys(a);
|
|
1319
|
+
const kb = Object.keys(b);
|
|
1320
|
+
if (ka.length !== kb.length) return false;
|
|
1321
|
+
return ka.every(
|
|
1322
|
+
(k2) => deepEqual(
|
|
1323
|
+
a[k2],
|
|
1324
|
+
b[k2]
|
|
1325
|
+
)
|
|
1326
|
+
);
|
|
1327
|
+
}
|
|
1328
|
+
return false;
|
|
1329
|
+
}
|
|
1330
|
+
function createMaintenanceRebuilder(opts = {}) {
|
|
1331
|
+
return new MaintenanceRebuilder(opts);
|
|
1332
|
+
}
|
|
1333
|
+
|
|
958
1334
|
// src/define/define.ts
|
|
959
1335
|
function collectParams(structure, out = {}) {
|
|
960
1336
|
if (structure === null || typeof structure !== "object") return out;
|
|
@@ -1170,15 +1546,21 @@ export {
|
|
|
1170
1546
|
DynamoExecutor,
|
|
1171
1547
|
EDGE_WRITES_MARKER,
|
|
1172
1548
|
ENTITY_WRITES_MARKER,
|
|
1549
|
+
InMemoryProjectionSink,
|
|
1173
1550
|
LIFECYCLE_CONTRACT_MARKER,
|
|
1174
1551
|
Linter,
|
|
1552
|
+
MAINT_OUTBOX_PK_PREFIX,
|
|
1175
1553
|
MAX_TRANSACT_ITEMS,
|
|
1554
|
+
MaintenanceDrain,
|
|
1555
|
+
MaintenanceRebuilder,
|
|
1176
1556
|
MetadataRegistry,
|
|
1177
1557
|
OLD_VALUE_NAMESPACE,
|
|
1558
|
+
ProjectionSinkDrain,
|
|
1178
1559
|
RetryingExecutor,
|
|
1179
1560
|
SPEC_VERSION,
|
|
1180
1561
|
TableMapping,
|
|
1181
1562
|
TransactionContext,
|
|
1563
|
+
ViewRegistry,
|
|
1182
1564
|
aggregate,
|
|
1183
1565
|
assertBundleSerializable,
|
|
1184
1566
|
assertContractBoundaries,
|
|
@@ -1215,6 +1597,11 @@ export {
|
|
|
1215
1597
|
count,
|
|
1216
1598
|
createCdcEmulator,
|
|
1217
1599
|
createDefaultLinter,
|
|
1600
|
+
createMaintenanceDrain,
|
|
1601
|
+
createMaintenanceDrainHandler,
|
|
1602
|
+
createMaintenanceRebuilder,
|
|
1603
|
+
createProjectionSinkDrain,
|
|
1604
|
+
createProjectionSinkHandler,
|
|
1218
1605
|
datetime,
|
|
1219
1606
|
decodeCursor,
|
|
1220
1607
|
decodePerKeyCursor,
|
|
@@ -1222,12 +1609,17 @@ export {
|
|
|
1222
1609
|
defineDelete,
|
|
1223
1610
|
defineList,
|
|
1224
1611
|
definePlan,
|
|
1612
|
+
defineProjection,
|
|
1613
|
+
defineProjections,
|
|
1225
1614
|
definePut,
|
|
1226
1615
|
defineQueries,
|
|
1227
1616
|
defineQuery,
|
|
1228
1617
|
defineTransaction,
|
|
1229
1618
|
defineTransactions,
|
|
1230
1619
|
defineUpdate,
|
|
1620
|
+
defineVersioned,
|
|
1621
|
+
defineView,
|
|
1622
|
+
defineViews,
|
|
1231
1623
|
deriveEdgeWriteItems,
|
|
1232
1624
|
deriveEdgeWriteItemsFor,
|
|
1233
1625
|
deriveModelEdgeWriteItems,
|
|
@@ -1283,11 +1675,13 @@ export {
|
|
|
1283
1675
|
isMutationInputRef,
|
|
1284
1676
|
isParam,
|
|
1285
1677
|
isPlannedCommandMethod,
|
|
1678
|
+
isProjectionDefinition,
|
|
1286
1679
|
isQueryModelContract,
|
|
1287
1680
|
isRetryableError,
|
|
1288
1681
|
isRetryableTransactionCancellation,
|
|
1289
1682
|
isSelectBuilder,
|
|
1290
1683
|
isTransactionRef,
|
|
1684
|
+
isViewDefinition,
|
|
1291
1685
|
k,
|
|
1292
1686
|
key,
|
|
1293
1687
|
lifecyclePhaseForIntent,
|
|
@@ -1323,5 +1717,6 @@ export {
|
|
|
1323
1717
|
validateDepth,
|
|
1324
1718
|
validateGsiAmbiguity,
|
|
1325
1719
|
when,
|
|
1720
|
+
whenMember,
|
|
1326
1721
|
wholeKeysSentinel
|
|
1327
1722
|
};
|
package/dist/testing/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { E as Executor, D as DynamoDBOperation, R as ReadExecOptions, a as ExecutorResult, B as BatchGetExecInput, P as PutInput, W as WriteExecOptions, b as WriteResult, U as UpdateInput, c as DeleteInput, d as BatchWriteExecItem, e as BatchExecOptions, T as TransactWriteExecItem, M as ModelStatic, f as DDBModel, C as ChangeEvent } from '../types-
|
|
1
|
+
import { E as Executor, D as DynamoDBOperation, R as ReadExecOptions, a as ExecutorResult, B as BatchGetExecInput, P as PutInput, W as WriteExecOptions, b as WriteResult, U as UpdateInput, c as DeleteInput, d as BatchWriteExecItem, e as BatchExecOptions, T as TransactWriteExecItem, M as ModelStatic, f as DDBModel, C as ChangeEvent } from '../types-DuJ08bgc.js';
|
|
2
2
|
import '@aws-sdk/client-dynamodb';
|
|
3
3
|
|
|
4
4
|
/**
|
package/dist/testing/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createCdcEmulator
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-PNIZS37E.js";
|
|
4
4
|
import {
|
|
5
5
|
ClientManager,
|
|
6
6
|
MetadataRegistry,
|
|
7
7
|
TableMapping,
|
|
8
8
|
resolveModelClass
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-YIXXTGZ6.js";
|
|
10
10
|
|
|
11
11
|
// src/memory/memory-store.ts
|
|
12
12
|
function deepClone(value) {
|