graphddb 0.2.4 → 0.2.5
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 +314 -305
- package/dist/{chunk-GCPEUPAA.js → chunk-4TYK3AV6.js} +671 -155
- package/dist/{chunk-LNESVUTO.js → chunk-ITHQ2EDH.js} +1 -1
- package/dist/{chunk-IQEOJVHI.js → chunk-SGYBE2OV.js} +378 -49
- package/dist/cli.js +2 -2
- package/dist/index.d.ts +167 -3
- package/dist/index.js +3 -3
- package/dist/testing/index.d.ts +1 -1
- package/dist/testing/index.js +2 -2
- package/dist/{types-BEeQ_nSd.d.ts → types--tdHK3vi.d.ts} +674 -212
- package/package.json +1 -1
|
@@ -4,17 +4,22 @@ import {
|
|
|
4
4
|
ClientManager,
|
|
5
5
|
MAX_TRANSACT_ITEMS,
|
|
6
6
|
MetadataRegistry,
|
|
7
|
+
NO_MIDDLEWARE,
|
|
7
8
|
TableMapping,
|
|
8
9
|
attachHiddenKey,
|
|
9
10
|
attachModelClass,
|
|
10
11
|
buildConditionExpression,
|
|
11
12
|
buildDeleteInput,
|
|
12
13
|
buildPutInput,
|
|
14
|
+
buildReadRuntime,
|
|
13
15
|
buildUpdateInput,
|
|
16
|
+
buildWriteRuntime,
|
|
14
17
|
captureWrite,
|
|
18
|
+
chunkArray,
|
|
15
19
|
commitTransaction,
|
|
16
20
|
compileRawCondition,
|
|
17
21
|
createColumnMap,
|
|
22
|
+
ctxModelFor,
|
|
18
23
|
execItemKeySignature,
|
|
19
24
|
executeDelete,
|
|
20
25
|
executePut,
|
|
@@ -32,7 +37,7 @@ import {
|
|
|
32
37
|
serializeFieldValue,
|
|
33
38
|
serializeRawCondition,
|
|
34
39
|
skTemplate
|
|
35
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-4TYK3AV6.js";
|
|
36
41
|
|
|
37
42
|
// src/spec/types.ts
|
|
38
43
|
var SPEC_VERSION = "1.0";
|
|
@@ -810,23 +815,71 @@ async function executeListInternal(modelClass, key, selectSpec, options = {}) {
|
|
|
810
815
|
updatable: options.updatable ?? false
|
|
811
816
|
});
|
|
812
817
|
const operation = executionPlan.operations[0];
|
|
813
|
-
const
|
|
814
|
-
|
|
815
|
-
|
|
818
|
+
const mw = options.mw ?? NO_MIDDLEWARE;
|
|
819
|
+
const sendList = (op) => execute(op, options.retry !== void 0 ? { retry: options.retry } : void 0).then(
|
|
820
|
+
(r) => r
|
|
816
821
|
);
|
|
817
|
-
|
|
822
|
+
let lastEvaluatedKey;
|
|
823
|
+
let rawItems;
|
|
824
|
+
if (mw.active) {
|
|
825
|
+
let captured;
|
|
826
|
+
const opModel = ctxModelFor(modelClass);
|
|
827
|
+
rawItems = await mw.runOp(
|
|
828
|
+
operation,
|
|
829
|
+
options.relationPath ?? [],
|
|
830
|
+
opModel,
|
|
831
|
+
async (op) => {
|
|
832
|
+
captured = await sendList(op);
|
|
833
|
+
return captured.items;
|
|
834
|
+
}
|
|
835
|
+
);
|
|
836
|
+
lastEvaluatedKey = captured?.lastEvaluatedKey;
|
|
837
|
+
} else {
|
|
838
|
+
const result = await sendList(operation);
|
|
839
|
+
rawItems = result.items;
|
|
840
|
+
lastEvaluatedKey = result.lastEvaluatedKey;
|
|
841
|
+
}
|
|
818
842
|
const hydrated = hydrate(rawItems, select, metadata, options.updatable ?? false);
|
|
819
|
-
const cursor =
|
|
843
|
+
const cursor = lastEvaluatedKey ? encodeCursor(lastEvaluatedKey) : null;
|
|
820
844
|
return { items: hydrated, rawItems, cursor };
|
|
821
845
|
}
|
|
822
846
|
async function executeList(modelClass, key, selectSpec, options = {}) {
|
|
823
|
-
const
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
847
|
+
const mw = buildReadRuntime(options.context);
|
|
848
|
+
if (!mw.active) {
|
|
849
|
+
const { items, cursor } = await executeListInternal(
|
|
850
|
+
modelClass,
|
|
851
|
+
key,
|
|
852
|
+
selectSpec,
|
|
853
|
+
options
|
|
854
|
+
);
|
|
855
|
+
return { items, cursor };
|
|
856
|
+
}
|
|
857
|
+
const ctxModel = ctxModelFor(modelClass);
|
|
858
|
+
const opts = { ...options };
|
|
859
|
+
const params = {
|
|
860
|
+
key: { ...key },
|
|
861
|
+
select: selectSpec && typeof selectSpec === "object" ? selectSpec : {},
|
|
862
|
+
options: opts
|
|
863
|
+
};
|
|
864
|
+
let reqCtx;
|
|
865
|
+
try {
|
|
866
|
+
reqCtx = await mw.runRequestBefore("list", ctxModel, params);
|
|
867
|
+
const { items, cursor } = await executeListInternal(
|
|
868
|
+
modelClass,
|
|
869
|
+
params.key,
|
|
870
|
+
params.select,
|
|
871
|
+
// Thread the SAME runtime + an empty (root) relation path so R2/R3 fire on
|
|
872
|
+
// the partition Query; carry the (possibly R1-mutated) per-call options.
|
|
873
|
+
{ ...params.options, mw, relationPath: [] }
|
|
874
|
+
);
|
|
875
|
+
const result = { items, cursor };
|
|
876
|
+
return await mw.runRequestAfter(reqCtx, result);
|
|
877
|
+
} catch (err) {
|
|
878
|
+
return mw.runRequestError(
|
|
879
|
+
reqCtx ?? mw.requestCtx("list", ctxModel, params),
|
|
880
|
+
err
|
|
881
|
+
);
|
|
882
|
+
}
|
|
830
883
|
}
|
|
831
884
|
|
|
832
885
|
// src/planner/relation-planner.ts
|
|
@@ -1174,12 +1227,23 @@ async function fetchBelongsToHasOneBatch(rel, rawParentItems, selectSpec, target
|
|
|
1174
1227
|
);
|
|
1175
1228
|
const executor = ClientManager.getExecutor();
|
|
1176
1229
|
const queryKeyToRaw = /* @__PURE__ */ new Map();
|
|
1230
|
+
const mw = options.mw ?? NO_MIDDLEWARE;
|
|
1231
|
+
const opPath = [...options.relationPath ?? [], rel.propertyName];
|
|
1232
|
+
const opModel = mw.active ? ctxModelFor(rel.targetFactory()) : void 0;
|
|
1177
1233
|
for (const operation of batchOps) {
|
|
1178
|
-
const
|
|
1234
|
+
const items = mw.active ? await mw.runOp(
|
|
1235
|
+
operation,
|
|
1236
|
+
opPath,
|
|
1237
|
+
opModel,
|
|
1238
|
+
(op) => executor.batchGet(
|
|
1239
|
+
op,
|
|
1240
|
+
options.retry !== void 0 ? { retry: options.retry } : void 0
|
|
1241
|
+
).then((r) => r.items)
|
|
1242
|
+
) : (await executor.batchGet(
|
|
1179
1243
|
operation,
|
|
1180
1244
|
options.retry !== void 0 ? { retry: options.retry } : void 0
|
|
1181
|
-
);
|
|
1182
|
-
for (const item of
|
|
1245
|
+
)).items;
|
|
1246
|
+
for (const item of items) {
|
|
1183
1247
|
for (const queryKey of queryKeys) {
|
|
1184
1248
|
if (!matchesQueryKey(item, queryKey)) {
|
|
1185
1249
|
continue;
|
|
@@ -1265,9 +1329,11 @@ async function resolveRelations(parentItem, rawParentItem, select, metadata, opt
|
|
|
1265
1329
|
const relations = detectRelationFields(select, metadata);
|
|
1266
1330
|
const plan2 = options.plan ?? (currentPath === "$" ? buildRelationExecutionPlan(select, metadata) : void 0);
|
|
1267
1331
|
const opts = plan2 === options.plan ? options : { ...options, plan: plan2 };
|
|
1332
|
+
const basePath = opts.relationPath ?? [];
|
|
1268
1333
|
await runStaged(relations, currentPath, opts.plan, async (rel) => {
|
|
1269
1334
|
const selectSpec = relationSpec(select[rel.propertyName]);
|
|
1270
1335
|
const ownPath = relationResultPath(currentPath, rel);
|
|
1336
|
+
const relPath = [...basePath, rel.propertyName];
|
|
1271
1337
|
if (rel.type === "hasMany") {
|
|
1272
1338
|
const targetClass2 = rel.targetFactory();
|
|
1273
1339
|
const queryKey = buildRelationQueryKey(rel, rawParentItem);
|
|
@@ -1279,7 +1345,9 @@ async function resolveRelations(parentItem, rawParentItem, select, metadata, opt
|
|
|
1279
1345
|
order,
|
|
1280
1346
|
filter: selectSpec.filter,
|
|
1281
1347
|
updatable: opts.updatable,
|
|
1282
|
-
retry: opts.retry
|
|
1348
|
+
retry: opts.retry,
|
|
1349
|
+
mw: opts.mw,
|
|
1350
|
+
relationPath: relPath
|
|
1283
1351
|
});
|
|
1284
1352
|
let items = listResult.items;
|
|
1285
1353
|
const rawItems = listResult.rawItems;
|
|
@@ -1291,7 +1359,7 @@ async function resolveRelations(parentItem, rawParentItem, select, metadata, opt
|
|
|
1291
1359
|
rawItems,
|
|
1292
1360
|
selectSpec.select,
|
|
1293
1361
|
targetMeta2,
|
|
1294
|
-
opts,
|
|
1362
|
+
{ ...opts, relationPath: relPath },
|
|
1295
1363
|
currentDepth + 1,
|
|
1296
1364
|
ownPath
|
|
1297
1365
|
);
|
|
@@ -1320,6 +1388,36 @@ async function resolveRelations(parentItem, rawParentItem, select, metadata, opt
|
|
|
1320
1388
|
|
|
1321
1389
|
// src/operations/query.ts
|
|
1322
1390
|
async function executeQuery(modelClass, key, selectSpec, options) {
|
|
1391
|
+
const mw = buildReadRuntime(options?.context);
|
|
1392
|
+
if (!mw.active) {
|
|
1393
|
+
return executeQueryInner(modelClass, key, selectSpec, options, mw);
|
|
1394
|
+
}
|
|
1395
|
+
const ctxModel = ctxModelFor(modelClass);
|
|
1396
|
+
const opts = { ...options ?? {} };
|
|
1397
|
+
const params = {
|
|
1398
|
+
key: { ...key },
|
|
1399
|
+
select: selectSpec && typeof selectSpec === "object" ? selectSpec : {},
|
|
1400
|
+
options: opts
|
|
1401
|
+
};
|
|
1402
|
+
let reqCtx;
|
|
1403
|
+
try {
|
|
1404
|
+
reqCtx = await mw.runRequestBefore("query", ctxModel, params);
|
|
1405
|
+
const result = await executeQueryInner(
|
|
1406
|
+
modelClass,
|
|
1407
|
+
params.key,
|
|
1408
|
+
params.select,
|
|
1409
|
+
params.options,
|
|
1410
|
+
mw
|
|
1411
|
+
);
|
|
1412
|
+
return await mw.runRequestAfter(reqCtx, result);
|
|
1413
|
+
} catch (err) {
|
|
1414
|
+
return mw.runRequestError(
|
|
1415
|
+
reqCtx ?? mw.requestCtx("query", ctxModel, params),
|
|
1416
|
+
err
|
|
1417
|
+
);
|
|
1418
|
+
}
|
|
1419
|
+
}
|
|
1420
|
+
async function executeQueryInner(modelClass, key, selectSpec, options, mw) {
|
|
1323
1421
|
const metadata = MetadataRegistry.get(modelClass);
|
|
1324
1422
|
const queryFields = Object.keys(key);
|
|
1325
1423
|
const resolved = resolveKey(queryFields, metadata);
|
|
@@ -1353,11 +1451,25 @@ async function executeQuery(modelClass, key, selectSpec, options) {
|
|
|
1353
1451
|
)
|
|
1354
1452
|
);
|
|
1355
1453
|
const retry = options?.retry;
|
|
1356
|
-
const
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1454
|
+
const sendRoot = () => execute(operation, retry !== void 0 ? { retry } : void 0);
|
|
1455
|
+
const parentPromise = mw.active ? (() => {
|
|
1456
|
+
const opModel = ctxModelFor(modelClass);
|
|
1457
|
+
return mw.runOp(operation, [], opModel, async (op) => {
|
|
1458
|
+
const r = await execute(
|
|
1459
|
+
op,
|
|
1460
|
+
retry !== void 0 ? { retry } : void 0
|
|
1461
|
+
);
|
|
1462
|
+
return r.items;
|
|
1463
|
+
}).then((items) => ({ items }));
|
|
1464
|
+
})() : sendRoot();
|
|
1465
|
+
const relationPromise = canParallelizeRelations ? resolveRelations(
|
|
1466
|
+
{},
|
|
1467
|
+
key,
|
|
1468
|
+
select,
|
|
1469
|
+
metadata,
|
|
1470
|
+
{ maxDepth, updatable, retry, mw, relationPath: [] },
|
|
1471
|
+
1
|
|
1472
|
+
) : null;
|
|
1361
1473
|
let result;
|
|
1362
1474
|
try {
|
|
1363
1475
|
result = await parentPromise;
|
|
@@ -1384,7 +1496,7 @@ async function executeQuery(modelClass, key, selectSpec, options) {
|
|
|
1384
1496
|
rawItem,
|
|
1385
1497
|
select,
|
|
1386
1498
|
metadata,
|
|
1387
|
-
{ maxDepth, updatable, retry },
|
|
1499
|
+
{ maxDepth, updatable, retry, mw, relationPath: [] },
|
|
1388
1500
|
1
|
|
1389
1501
|
);
|
|
1390
1502
|
if (hydratedItem && updatable) {
|
|
@@ -1465,16 +1577,37 @@ function hydrateBatchItems(rawItems, metadata) {
|
|
|
1465
1577
|
return merged;
|
|
1466
1578
|
});
|
|
1467
1579
|
}
|
|
1468
|
-
async function executeBatchGetForTable(tableName, entries) {
|
|
1580
|
+
async function executeBatchGetForTable(tableName, entries, mw, opModel) {
|
|
1469
1581
|
const groups = /* @__PURE__ */ new Map();
|
|
1470
1582
|
if (entries.length === 0) {
|
|
1471
1583
|
return groups;
|
|
1472
1584
|
}
|
|
1473
1585
|
const keys = entries.map((entry) => entry.dynamoKey);
|
|
1474
|
-
const
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1586
|
+
const sendChunk = (op) => execute(op).then((r) => r.items);
|
|
1587
|
+
let rawItems;
|
|
1588
|
+
if (mw.active) {
|
|
1589
|
+
rawItems = [];
|
|
1590
|
+
for (const chunk of chunkArray(keys, BATCH_GET_MAX_KEYS)) {
|
|
1591
|
+
const operation = {
|
|
1592
|
+
type: "BatchGetItem",
|
|
1593
|
+
tableName,
|
|
1594
|
+
keys: chunk
|
|
1595
|
+
};
|
|
1596
|
+
const chunkItems = await mw.runOp(
|
|
1597
|
+
operation,
|
|
1598
|
+
[],
|
|
1599
|
+
opModel,
|
|
1600
|
+
(op) => sendChunk(op)
|
|
1601
|
+
);
|
|
1602
|
+
rawItems.push(...chunkItems);
|
|
1603
|
+
}
|
|
1604
|
+
} else {
|
|
1605
|
+
const { items } = await ClientManager.getExecutor().batchGet({
|
|
1606
|
+
tableName,
|
|
1607
|
+
keys
|
|
1608
|
+
});
|
|
1609
|
+
rawItems = items;
|
|
1610
|
+
}
|
|
1478
1611
|
const entriesByModel = /* @__PURE__ */ new Map();
|
|
1479
1612
|
for (const entry of entries) {
|
|
1480
1613
|
const existing = entriesByModel.get(entry.modelClass) ?? [];
|
|
@@ -1495,12 +1628,42 @@ async function executeBatchGetForTable(tableName, entries) {
|
|
|
1495
1628
|
}
|
|
1496
1629
|
return groups;
|
|
1497
1630
|
}
|
|
1498
|
-
async function executeBatchGet(requests) {
|
|
1631
|
+
async function executeBatchGet(requests, options) {
|
|
1632
|
+
const mw = buildReadRuntime(options?.context);
|
|
1633
|
+
if (!mw.active || requests.length === 0) {
|
|
1634
|
+
return executeBatchGetInner(requests, mw);
|
|
1635
|
+
}
|
|
1636
|
+
const firstModel = ctxModelFor(resolveModelClass(requests[0].model));
|
|
1637
|
+
const reqList = [...requests];
|
|
1638
|
+
const params = {
|
|
1639
|
+
key: {},
|
|
1640
|
+
select: {},
|
|
1641
|
+
options: {},
|
|
1642
|
+
requests: reqList
|
|
1643
|
+
};
|
|
1644
|
+
let reqCtx;
|
|
1645
|
+
try {
|
|
1646
|
+
reqCtx = await mw.runRequestBefore("batchGet", firstModel, params);
|
|
1647
|
+
const effectiveRequests = params.requests;
|
|
1648
|
+
const result = await executeBatchGetInner(effectiveRequests, mw);
|
|
1649
|
+
return await mw.runRequestAfter(reqCtx, result);
|
|
1650
|
+
} catch (err) {
|
|
1651
|
+
return mw.runRequestError(
|
|
1652
|
+
reqCtx ?? mw.requestCtx("batchGet", firstModel, params),
|
|
1653
|
+
err
|
|
1654
|
+
);
|
|
1655
|
+
}
|
|
1656
|
+
}
|
|
1657
|
+
async function executeBatchGetInner(requests, mw) {
|
|
1499
1658
|
const entriesByTable = /* @__PURE__ */ new Map();
|
|
1659
|
+
const firstModelClassByTable = /* @__PURE__ */ new Map();
|
|
1500
1660
|
for (const request of requests) {
|
|
1501
1661
|
const modelClass = resolveModelClass(request.model);
|
|
1502
1662
|
const metadata = MetadataRegistry.get(modelClass);
|
|
1503
1663
|
const tableName = TableMapping.resolve(metadata.tableName);
|
|
1664
|
+
if (!firstModelClassByTable.has(tableName)) {
|
|
1665
|
+
firstModelClassByTable.set(tableName, modelClass);
|
|
1666
|
+
}
|
|
1504
1667
|
for (const keyObj of request.keys) {
|
|
1505
1668
|
const entry = {
|
|
1506
1669
|
modelClass,
|
|
@@ -1515,7 +1678,13 @@ async function executeBatchGet(requests) {
|
|
|
1515
1678
|
const mergedGroups = /* @__PURE__ */ new Map();
|
|
1516
1679
|
for (const [, entries] of entriesByTable) {
|
|
1517
1680
|
const tableName = TableMapping.resolve(entries[0].metadata.tableName);
|
|
1518
|
-
const
|
|
1681
|
+
const opModel = mw.active ? ctxModelFor(firstModelClassByTable.get(tableName) ?? entries[0].modelClass) : void 0;
|
|
1682
|
+
const tableGroups = await executeBatchGetForTable(
|
|
1683
|
+
tableName,
|
|
1684
|
+
entries,
|
|
1685
|
+
mw,
|
|
1686
|
+
opModel
|
|
1687
|
+
);
|
|
1519
1688
|
for (const [modelClass, items] of tableGroups) {
|
|
1520
1689
|
const existing = mergedGroups.get(modelClass) ?? [];
|
|
1521
1690
|
mergedGroups.set(modelClass, existing.concat(items));
|
|
@@ -2076,7 +2245,7 @@ function renderIdempotencyGuardItems(guard, key, params, contextLabel) {
|
|
|
2076
2245
|
return { Put: put };
|
|
2077
2246
|
});
|
|
2078
2247
|
}
|
|
2079
|
-
async function executeReferentialTransaction(writes, edgeItems, updateItems, guardItemsRaw, outboxItems, idempotencyItems, checks, methodName, modelBySignature = /* @__PURE__ */ new Map(), retry) {
|
|
2248
|
+
async function executeReferentialTransaction(writes, edgeItems, updateItems, guardItemsRaw, outboxItems, idempotencyItems, checks, methodName, modelBySignature = /* @__PURE__ */ new Map(), retry, middleware) {
|
|
2080
2249
|
const writeItems = writes.map((w) => {
|
|
2081
2250
|
const options = w.condition !== void 0 ? { condition: w.condition } : void 0;
|
|
2082
2251
|
let item;
|
|
@@ -2104,6 +2273,7 @@ async function executeReferentialTransaction(writes, edgeItems, updateItems, gua
|
|
|
2104
2273
|
{
|
|
2105
2274
|
modelBySignature,
|
|
2106
2275
|
...retry !== void 0 ? { retry } : {},
|
|
2276
|
+
...middleware !== void 0 ? { middleware } : {},
|
|
2107
2277
|
limitError: (count) => new Error(
|
|
2108
2278
|
`Contract runtime: command method '${methodName}' composes ${count} items (writes + edges + derived updates + uniqueness guards + outbox events + idempotency guard + ConditionChecks) into one TransactWriteItems, but DynamoDB caps a transaction at ${MAX_TRANSACT_ITEMS} items and a transaction is atomic \u2014 it cannot be split. Reduce the fragment / effect count.`
|
|
2109
2279
|
)
|
|
@@ -2317,7 +2487,24 @@ function readBackForCompiledOp(op, write) {
|
|
|
2317
2487
|
}
|
|
2318
2488
|
async function executeCompiledWriteOp(ops, options) {
|
|
2319
2489
|
const label = options.label;
|
|
2320
|
-
const
|
|
2490
|
+
const writeMw = buildWriteRuntime(options.context);
|
|
2491
|
+
const writeCtxs = [];
|
|
2492
|
+
const txId = { id: /* @__PURE__ */ Symbol("graphddb.mutate") };
|
|
2493
|
+
let rewrites = [];
|
|
2494
|
+
if (writeMw.active) {
|
|
2495
|
+
const w1ed = [];
|
|
2496
|
+
for (const op of ops) {
|
|
2497
|
+
const r = await runEnvelopeW1(op, writeMw, txId, writeCtxs);
|
|
2498
|
+
w1ed.push(r.op);
|
|
2499
|
+
rewrites.push(r.rewritten ?? null);
|
|
2500
|
+
}
|
|
2501
|
+
ops = w1ed;
|
|
2502
|
+
} else {
|
|
2503
|
+
rewrites = ops.map(() => null);
|
|
2504
|
+
}
|
|
2505
|
+
const renderedOps = ops.map(
|
|
2506
|
+
(op, i) => rewrites[i] !== null ? rewrittenCompiledOp(rewrites[i]) : renderCompiledOp(op, `${label} (op #${i})`)
|
|
2507
|
+
);
|
|
2321
2508
|
const rendered = renderedOps.map((r) => r.write);
|
|
2322
2509
|
const modelBySignature = /* @__PURE__ */ new Map();
|
|
2323
2510
|
const edgeItems = [];
|
|
@@ -2336,8 +2523,13 @@ async function executeCompiledWriteOp(ops, options) {
|
|
|
2336
2523
|
checkItems.push(...r.checkItems);
|
|
2337
2524
|
}
|
|
2338
2525
|
const hasDerivedEffects = renderedOps.some((r) => r.hasDerivedEffects);
|
|
2526
|
+
const origins = writeMw.active ? rendered.map((w) => ({ model: ctxModelFor(w.modelClass), kind: w.operation })) : [];
|
|
2339
2527
|
if (rendered.length === 1 && !hasDerivedEffects) {
|
|
2340
|
-
|
|
2528
|
+
if (writeMw.active) {
|
|
2529
|
+
await persistSingleWriteWithHooks(rendered[0], writeMw, origins, txId, options.retry);
|
|
2530
|
+
} else {
|
|
2531
|
+
await executeSingleWrite(rendered[0], options.retry);
|
|
2532
|
+
}
|
|
2341
2533
|
} else {
|
|
2342
2534
|
await executeReferentialTransaction(
|
|
2343
2535
|
rendered,
|
|
@@ -2349,23 +2541,115 @@ async function executeCompiledWriteOp(ops, options) {
|
|
|
2349
2541
|
checkItems,
|
|
2350
2542
|
label,
|
|
2351
2543
|
modelBySignature,
|
|
2352
|
-
options.retry
|
|
2544
|
+
options.retry,
|
|
2545
|
+
writeMw.active ? { runtime: writeMw, origins, transaction: txId } : void 0
|
|
2353
2546
|
);
|
|
2354
2547
|
}
|
|
2548
|
+
if (writeMw.active) {
|
|
2549
|
+
for (let i = writeCtxs.length - 1; i >= 0; i--) {
|
|
2550
|
+
await writeMw.runWriteAfter(writeCtxs[i], {});
|
|
2551
|
+
}
|
|
2552
|
+
}
|
|
2355
2553
|
const readBacks = await Promise.all(
|
|
2356
2554
|
ops.map((op, i) => readBackForCompiledOp(op, rendered[i]))
|
|
2357
2555
|
);
|
|
2358
2556
|
return { readBacks };
|
|
2359
2557
|
}
|
|
2558
|
+
async function persistSingleWriteWithHooks(w, runtime, origins, transaction, retry) {
|
|
2559
|
+
const options = w.condition !== void 0 || retry !== void 0 ? {
|
|
2560
|
+
...w.condition !== void 0 ? { condition: w.condition } : {},
|
|
2561
|
+
...retry !== void 0 ? { retry } : {}
|
|
2562
|
+
} : void 0;
|
|
2563
|
+
let item;
|
|
2564
|
+
if (w.operation === "put") {
|
|
2565
|
+
item = { Put: buildPutInput(w.modelClass, w.item ?? {}, options) };
|
|
2566
|
+
} else if (w.operation === "update") {
|
|
2567
|
+
item = { Update: buildUpdateInput(w.modelClass, w.key, w.changes ?? {}, options) };
|
|
2568
|
+
} else {
|
|
2569
|
+
item = { Delete: buildDeleteInput(w.modelClass, w.key, options) };
|
|
2570
|
+
}
|
|
2571
|
+
const persistCtx = runtime.persistCtx([item], origins, transaction);
|
|
2572
|
+
await runtime.runPersist(persistCtx, async (items) => {
|
|
2573
|
+
const sent = items[0];
|
|
2574
|
+
const sendOpts = retry !== void 0 ? { retry } : void 0;
|
|
2575
|
+
const exec = ClientManager.getExecutor();
|
|
2576
|
+
if ("Put" in sent) return exec.put(sent.Put, sendOpts);
|
|
2577
|
+
if ("Update" in sent) return exec.update(sent.Update, sendOpts);
|
|
2578
|
+
if ("Delete" in sent) return exec.delete(sent.Delete, sendOpts);
|
|
2579
|
+
throw new Error("graphddb middleware: a single-op persist received a ConditionCheck.");
|
|
2580
|
+
});
|
|
2581
|
+
}
|
|
2582
|
+
async function runEnvelopeW1(op, runtime, transaction, writeCtxs) {
|
|
2583
|
+
const kind = op.fragment.op.operation;
|
|
2584
|
+
const keyFields = new Set(op.fragment.keyFields);
|
|
2585
|
+
const input = {};
|
|
2586
|
+
if (kind === "put") {
|
|
2587
|
+
input.item = { ...op.params };
|
|
2588
|
+
} else {
|
|
2589
|
+
const key = {};
|
|
2590
|
+
const changes = {};
|
|
2591
|
+
for (const [k, v] of Object.entries(op.params)) {
|
|
2592
|
+
if (keyFields.has(k)) key[k] = v;
|
|
2593
|
+
else changes[k] = v;
|
|
2594
|
+
}
|
|
2595
|
+
input.key = key;
|
|
2596
|
+
input.changes = changes;
|
|
2597
|
+
}
|
|
2598
|
+
const modelClass = op.fragment.op.entity.modelClass;
|
|
2599
|
+
const ctx = runtime.writeCtx(kind, ctxModelFor(modelClass), input, transaction);
|
|
2600
|
+
await runtime.runWriteBefore(ctx);
|
|
2601
|
+
writeCtxs.push(ctx);
|
|
2602
|
+
if (ctx.kind !== kind) {
|
|
2603
|
+
return { op, rewritten: renderRewrittenWrite(modelClass, ctx) };
|
|
2604
|
+
}
|
|
2605
|
+
const merged = ctx.kind === "put" ? { ...ctx.input.item ?? {} } : { ...ctx.input.key ?? {}, ...ctx.input.changes ?? {} };
|
|
2606
|
+
return { op: { ...op, params: merged } };
|
|
2607
|
+
}
|
|
2608
|
+
function renderRewrittenWrite(modelClass, ctx) {
|
|
2609
|
+
const modelStatic = modelClass.asModel();
|
|
2610
|
+
const condition = ctx.input.options?.condition;
|
|
2611
|
+
const base = {
|
|
2612
|
+
modelClass,
|
|
2613
|
+
modelStatic,
|
|
2614
|
+
...condition !== void 0 ? { condition } : {}
|
|
2615
|
+
};
|
|
2616
|
+
if (ctx.kind === "put") {
|
|
2617
|
+
const item = ctx.input.item ?? {};
|
|
2618
|
+
return { ...base, operation: "put", key: item, item };
|
|
2619
|
+
}
|
|
2620
|
+
if (ctx.kind === "update") {
|
|
2621
|
+
return {
|
|
2622
|
+
...base,
|
|
2623
|
+
operation: "update",
|
|
2624
|
+
key: ctx.input.key ?? {},
|
|
2625
|
+
changes: ctx.input.changes ?? {}
|
|
2626
|
+
};
|
|
2627
|
+
}
|
|
2628
|
+
return { ...base, operation: "delete", key: ctx.input.key ?? {} };
|
|
2629
|
+
}
|
|
2630
|
+
function rewrittenCompiledOp(write) {
|
|
2631
|
+
return {
|
|
2632
|
+
write,
|
|
2633
|
+
edgeItems: [],
|
|
2634
|
+
updateItems: [],
|
|
2635
|
+
guardItems: [],
|
|
2636
|
+
outboxItems: [],
|
|
2637
|
+
idempotencyItems: [],
|
|
2638
|
+
checkItems: [],
|
|
2639
|
+
modelBySignature: /* @__PURE__ */ new Map(),
|
|
2640
|
+
hasDerivedEffects: false
|
|
2641
|
+
};
|
|
2642
|
+
}
|
|
2360
2643
|
async function executeCompiledParallelWrites(ops, options) {
|
|
2361
2644
|
const label = options.label;
|
|
2362
2645
|
const results = new Array(ops.length);
|
|
2363
2646
|
const renderedOps = ops.map((op, i) => renderCompiledOp(op, `${label} (op #${i})`));
|
|
2364
2647
|
const coalescibleIdx = [];
|
|
2365
2648
|
const individualIdx = [];
|
|
2649
|
+
const hooksActive = buildWriteRuntime(options.context).active;
|
|
2366
2650
|
renderedOps.forEach((r, i) => {
|
|
2367
2651
|
const w = r.write;
|
|
2368
|
-
if (!r.hasDerivedEffects && w.condition === void 0 && (w.operation === "put" || w.operation === "delete")) {
|
|
2652
|
+
if (!hooksActive && !r.hasDerivedEffects && w.condition === void 0 && (w.operation === "put" || w.operation === "delete")) {
|
|
2369
2653
|
coalescibleIdx.push(i);
|
|
2370
2654
|
} else {
|
|
2371
2655
|
individualIdx.push(i);
|
|
@@ -2397,7 +2681,8 @@ async function executeCompiledParallelWrites(ops, options) {
|
|
|
2397
2681
|
try {
|
|
2398
2682
|
const { readBacks } = await executeCompiledWriteOp([ops[i]], {
|
|
2399
2683
|
label: `${label} (op #${i})`,
|
|
2400
|
-
...options.retry !== void 0 ? { retry: options.retry } : {}
|
|
2684
|
+
...options.retry !== void 0 ? { retry: options.retry } : {},
|
|
2685
|
+
...options.context !== void 0 ? { context: options.context } : {}
|
|
2401
2686
|
});
|
|
2402
2687
|
results[i] = { ok: true, value: readBacks[0] };
|
|
2403
2688
|
} catch (e) {
|
|
@@ -2455,14 +2740,16 @@ async function executeReadRoute(alias, route) {
|
|
|
2455
2740
|
if (isQuery) {
|
|
2456
2741
|
return executeQuery(modelClass, route.key, route.select, {
|
|
2457
2742
|
consistentRead: opt.consistentRead,
|
|
2458
|
-
maxDepth: opt.maxDepth
|
|
2743
|
+
maxDepth: opt.maxDepth,
|
|
2744
|
+
context: opt.context
|
|
2459
2745
|
});
|
|
2460
2746
|
}
|
|
2461
2747
|
return executeList(modelClass, route.key, route.select, {
|
|
2462
2748
|
limit: opt.limit,
|
|
2463
2749
|
after: opt.after,
|
|
2464
2750
|
order: opt.order,
|
|
2465
|
-
filter: opt.filter
|
|
2751
|
+
filter: opt.filter,
|
|
2752
|
+
context: opt.context
|
|
2466
2753
|
});
|
|
2467
2754
|
}
|
|
2468
2755
|
var INTENT_KEYS = ["create", "update", "remove"];
|
|
@@ -2476,9 +2763,9 @@ async function executeWriteEnvelope(envelope, options = {}) {
|
|
|
2476
2763
|
}
|
|
2477
2764
|
const ops = aliases.flatMap((alias) => normalizeWriteOp(alias, envelope[alias]));
|
|
2478
2765
|
if (mode === "transaction") {
|
|
2479
|
-
return executeTransactionMode(ops, options.retry);
|
|
2766
|
+
return executeTransactionMode(ops, options.retry, options.context);
|
|
2480
2767
|
}
|
|
2481
|
-
return executeParallelMode(ops, options.retry);
|
|
2768
|
+
return executeParallelMode(ops, options.retry, options.context);
|
|
2482
2769
|
}
|
|
2483
2770
|
function normalizeWriteOp(alias, d) {
|
|
2484
2771
|
if (d === null || typeof d !== "object") {
|
|
@@ -2551,11 +2838,12 @@ function mapToRefs($, fields) {
|
|
|
2551
2838
|
for (const f of fields) out[f] = $[f];
|
|
2552
2839
|
return out;
|
|
2553
2840
|
}
|
|
2554
|
-
async function executeTransactionMode(ops, retry) {
|
|
2841
|
+
async function executeTransactionMode(ops, retry, context) {
|
|
2555
2842
|
const compiled = ops.map(compileWriteOp);
|
|
2556
2843
|
const { readBacks } = await executeCompiledWriteOp(compiled, {
|
|
2557
2844
|
label: "DDBModel.mutate",
|
|
2558
|
-
...retry !== void 0 ? { retry } : {}
|
|
2845
|
+
...retry !== void 0 ? { retry } : {},
|
|
2846
|
+
...context !== void 0 ? { context } : {}
|
|
2559
2847
|
});
|
|
2560
2848
|
const out = {};
|
|
2561
2849
|
ops.forEach((op, i) => {
|
|
@@ -2570,11 +2858,12 @@ async function executeTransactionMode(ops, retry) {
|
|
|
2570
2858
|
});
|
|
2571
2859
|
return out;
|
|
2572
2860
|
}
|
|
2573
|
-
async function executeParallelMode(ops, retry) {
|
|
2861
|
+
async function executeParallelMode(ops, retry, context) {
|
|
2574
2862
|
const compiled = ops.map(compileWriteOp);
|
|
2575
2863
|
const settled = await executeCompiledParallelWrites(compiled, {
|
|
2576
2864
|
label: "DDBModel.mutate",
|
|
2577
|
-
...retry !== void 0 ? { retry } : {}
|
|
2865
|
+
...retry !== void 0 ? { retry } : {},
|
|
2866
|
+
...context !== void 0 ? { context } : {}
|
|
2578
2867
|
});
|
|
2579
2868
|
const out = {};
|
|
2580
2869
|
ops.forEach((op, i) => {
|
|
@@ -2634,8 +2923,36 @@ var DDBModel = class {
|
|
|
2634
2923
|
static setRetryPolicy(policy) {
|
|
2635
2924
|
ClientManager.setRetryPolicy(policy);
|
|
2636
2925
|
}
|
|
2637
|
-
|
|
2638
|
-
|
|
2926
|
+
/**
|
|
2927
|
+
* Register a {@link Middleware} (issue #50; read path #138, write path #139).
|
|
2928
|
+
* One registered object may carry read and/or write hooks.
|
|
2929
|
+
*
|
|
2930
|
+
* - **Read** R1–R5 — request entry (`read.before`), each physical op before send
|
|
2931
|
+
* / after raw fetch (`read.op.before` / `read.op.afterFetch`, INCLUDING every
|
|
2932
|
+
* relation fan-out fetch), the final hydrated result (`read.afterFetch`), and on
|
|
2933
|
+
* error (`read.op.onError` / `read.onError`).
|
|
2934
|
+
* - **Write** W1–W5 — the logical write before effect derivation (`write.before`,
|
|
2935
|
+
* may mutate `ctx.input` / `ctx.kind` incl. a delete→update rewrite), after commit
|
|
2936
|
+
* (`write.after`), the physical persist on the fully composed batch
|
|
2937
|
+
* (`write.persist.before` / `write.persist.after`, fires ONCE per atomic
|
|
2938
|
+
* transaction), and on error (`write.persist.onError` / `write.onError`).
|
|
2939
|
+
*
|
|
2940
|
+
* Hooks may observe, mutate `ctx`, `throw` to cancel, or (on `onError`) recover by
|
|
2941
|
+
* returning a value; the library imposes no semantics. Ordering: `before*` runs
|
|
2942
|
+
* first-registered-first (FIFO); `after*` / `onError` run last-registered-first
|
|
2943
|
+
* (LIFO). Mirrors {@link setRetryPolicy} — a host-runtime global stored on
|
|
2944
|
+
* {@link ClientManager}; never serialized (the bridge #48 is unaffected). Per-call
|
|
2945
|
+
* host context flows in via the read's / write's `{ context }` option.
|
|
2946
|
+
*/
|
|
2947
|
+
static use(mw) {
|
|
2948
|
+
ClientManager.use(mw);
|
|
2949
|
+
}
|
|
2950
|
+
/** Remove every registered middleware (read AND write; teardown / tests). See {@link use}. */
|
|
2951
|
+
static clearMiddleware() {
|
|
2952
|
+
ClientManager.clearMiddleware();
|
|
2953
|
+
}
|
|
2954
|
+
static async transaction(fn, options) {
|
|
2955
|
+
await executeTransaction(fn, options);
|
|
2639
2956
|
}
|
|
2640
2957
|
/**
|
|
2641
2958
|
* In-process multi-route READ (issue #101 — the unified envelope). Each alias
|
|
@@ -2650,8 +2967,20 @@ var DDBModel = class {
|
|
|
2650
2967
|
static mutate(envelope, options) {
|
|
2651
2968
|
return executeWriteEnvelope(envelope, options);
|
|
2652
2969
|
}
|
|
2653
|
-
|
|
2654
|
-
|
|
2970
|
+
/**
|
|
2971
|
+
* Multi-key, multi-model batch read. Each {@link BatchGetRequest} contributes its
|
|
2972
|
+
* keys to a chunked `BatchGetItem` per physical table; results are re-grouped by
|
|
2973
|
+
* model on the returned {@link BatchGetResult}.
|
|
2974
|
+
*
|
|
2975
|
+
* Fires the read hooks (issue #142): request-level R1/R4/R5 with kind
|
|
2976
|
+
* `'batchGet'`, and op-level R2/R3/R5 for each underlying physical `BatchGetItem`
|
|
2977
|
+
* op. The optional `{ context }` is exposed to every hook as `ctx.context` and
|
|
2978
|
+
* threaded UNCHANGED to every op (absent ⇒ `{}`), so a global hook (e.g. a
|
|
2979
|
+
* tenant-scope R2 FilterExpression) applies across the whole batch. With no
|
|
2980
|
+
* middleware registered the read path is unchanged (zero-overhead fast path).
|
|
2981
|
+
*/
|
|
2982
|
+
static async batchGet(requests, options) {
|
|
2983
|
+
return executeBatchGet(requests, options);
|
|
2655
2984
|
}
|
|
2656
2985
|
static async batchWrite(requests) {
|
|
2657
2986
|
return executeBatchWrite(requests);
|
package/dist/cli.js
CHANGED
|
@@ -2,10 +2,10 @@
|
|
|
2
2
|
import {
|
|
3
3
|
buildBridgeBundle,
|
|
4
4
|
normalizeSelectSpec
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-SGYBE2OV.js";
|
|
6
6
|
import {
|
|
7
7
|
MetadataRegistry
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-4TYK3AV6.js";
|
|
9
9
|
|
|
10
10
|
// src/cli.ts
|
|
11
11
|
import { createRequire } from "module";
|