effect-dynamodb 1.35.0 → 1.36.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.
Files changed (85) hide show
  1. package/dist/Aggregate.d.ts +1 -1
  2. package/dist/Aggregate.d.ts.map +1 -1
  3. package/dist/Aggregate.js +22 -6
  4. package/dist/Aggregate.js.map +1 -1
  5. package/dist/Batch.d.ts +1 -1
  6. package/dist/Batch.d.ts.map +1 -1
  7. package/dist/Batch.js +7 -5
  8. package/dist/Batch.js.map +1 -1
  9. package/dist/ConsumedCapacity.d.ts +107 -0
  10. package/dist/ConsumedCapacity.d.ts.map +1 -0
  11. package/dist/ConsumedCapacity.js +86 -0
  12. package/dist/ConsumedCapacity.js.map +1 -0
  13. package/dist/DynamoClient.d.ts +2 -0
  14. package/dist/DynamoClient.d.ts.map +1 -1
  15. package/dist/DynamoClient.js +9 -2
  16. package/dist/DynamoClient.js.map +1 -1
  17. package/dist/Entity.d.ts +1 -1
  18. package/dist/Entity.d.ts.map +1 -1
  19. package/dist/Entity.js +76 -42
  20. package/dist/Entity.js.map +1 -1
  21. package/dist/EventStore.d.ts +1 -1
  22. package/dist/EventStore.d.ts.map +1 -1
  23. package/dist/EventStore.js +35 -18
  24. package/dist/EventStore.js.map +1 -1
  25. package/dist/Query.d.ts +15 -0
  26. package/dist/Query.d.ts.map +1 -1
  27. package/dist/Query.js +23 -8
  28. package/dist/Query.js.map +1 -1
  29. package/dist/Transaction.d.ts +1 -1
  30. package/dist/Transaction.d.ts.map +1 -1
  31. package/dist/Transaction.js +7 -5
  32. package/dist/Transaction.js.map +1 -1
  33. package/dist/change-feed/ChangeFeed.d.ts.map +1 -1
  34. package/dist/change-feed/ChangeFeed.js +7 -4
  35. package/dist/change-feed/ChangeFeed.js.map +1 -1
  36. package/dist/change-feed/CheckpointStore.d.ts +1 -1
  37. package/dist/change-feed/CheckpointStore.d.ts.map +1 -1
  38. package/dist/change-feed/CheckpointStore.js +11 -3
  39. package/dist/change-feed/CheckpointStore.js.map +1 -1
  40. package/dist/change-feed/DynamoStreamsClient.d.ts +1 -1
  41. package/dist/change-feed/DynamoStreamsClient.d.ts.map +1 -1
  42. package/dist/change-feed/DynamoStreamsClient.js +3 -1
  43. package/dist/change-feed/DynamoStreamsClient.js.map +1 -1
  44. package/dist/index.d.ts +1 -0
  45. package/dist/index.d.ts.map +1 -1
  46. package/dist/index.js +1 -0
  47. package/dist/index.js.map +1 -1
  48. package/dist/internal/BoundCrud.js +3 -3
  49. package/dist/internal/BoundCrud.js.map +1 -1
  50. package/dist/internal/BoundQuery.d.ts +16 -0
  51. package/dist/internal/BoundQuery.d.ts.map +1 -1
  52. package/dist/internal/BoundQuery.js +21 -4
  53. package/dist/internal/BoundQuery.js.map +1 -1
  54. package/dist/internal/BoundVectorQuery.d.ts +1 -1
  55. package/dist/internal/BoundVectorQuery.d.ts.map +1 -1
  56. package/dist/internal/BoundVectorQuery.js +4 -3
  57. package/dist/internal/BoundVectorQuery.js.map +1 -1
  58. package/dist/internal/Capacity.d.ts +254 -0
  59. package/dist/internal/Capacity.d.ts.map +1 -0
  60. package/dist/internal/Capacity.js +653 -0
  61. package/dist/internal/Capacity.js.map +1 -0
  62. package/dist/internal/CredentialRefusal.d.ts +32 -0
  63. package/dist/internal/CredentialRefusal.d.ts.map +1 -0
  64. package/dist/internal/CredentialRefusal.js +80 -0
  65. package/dist/internal/CredentialRefusal.js.map +1 -0
  66. package/dist/internal/EntityCombinators.d.ts.map +1 -1
  67. package/dist/internal/EntityCombinators.js +4 -4
  68. package/dist/internal/EntityCombinators.js.map +1 -1
  69. package/dist/internal/EntityOps.d.ts +10 -1
  70. package/dist/internal/EntityOps.d.ts.map +1 -1
  71. package/dist/internal/EntityOps.js +22 -4
  72. package/dist/internal/EntityOps.js.map +1 -1
  73. package/dist/internal/EventProjection.d.ts.map +1 -1
  74. package/dist/internal/EventProjection.js +12 -8
  75. package/dist/internal/EventProjection.js.map +1 -1
  76. package/dist/internal/MultiProjection.d.ts.map +1 -1
  77. package/dist/internal/MultiProjection.js +11 -8
  78. package/dist/internal/MultiProjection.js.map +1 -1
  79. package/dist/internal/ProjectionEngine.d.ts.map +1 -1
  80. package/dist/internal/ProjectionEngine.js +5 -5
  81. package/dist/internal/ProjectionEngine.js.map +1 -1
  82. package/dist/internal/TransactPlan.d.ts.map +1 -1
  83. package/dist/internal/TransactPlan.js +5 -3
  84. package/dist/internal/TransactPlan.js.map +1 -1
  85. package/package.json +2 -2
package/dist/Entity.js CHANGED
@@ -17,10 +17,10 @@ import * as KeyComposer from "@effect-dynamodb/schema/KeyComposer.js";
17
17
  import { normalizeGsiConfig } from "@effect-dynamodb/schema/KeyComposer.js";
18
18
  import { deriveSourceText } from "@effect-dynamodb/schema/VectorIndex.js";
19
19
  import { Cause, Context, Crypto, Data, DateTime, Effect, Equal, Option, Schema, Stream, } from "effect";
20
- import { DynamoClient } from "./DynamoClient.js";
21
20
  import { attributeValueEquals } from "./internal/AttributeValueEquals.js";
22
21
  import { isBoundOp, makeBoundAppend, makeBoundDelete, makeBoundGet, makeBoundPut, makeBoundUpdate, } from "./internal/BoundCrud.js";
23
22
  import { BoundQueryImpl, entityNaming, } from "./internal/BoundQuery.js";
23
+ import * as Capacity from "./internal/Capacity.js";
24
24
  import { compileExpr, createConditionOps, emptyPartProblem, isExpr, nonEmptyCondition, parseShorthand, parseSimpleShorthand, toExpr, } from "./internal/Expr.js";
25
25
  import { countOperators, expressionFits } from "./internal/ExpressionLimits.js";
26
26
  import { BASE_DELAY_MS, MAX_RETRIES } from "./internal/Hydrate.js";
@@ -199,6 +199,11 @@ const compileCondition = (cond, resolveDbNameFn) => {
199
199
  return compileExpr(expr, resolveDbNameFn);
200
200
  };
201
201
  /** Attempts a guarded put makes before it reports a lost race (#133). */
202
+ /** Name a get- or delete-shaped op for consumed-capacity reports (its builder carries it). */
203
+ const namedOp = (op, name) => {
204
+ Capacity.nameBuilder(op._builder, name);
205
+ return op;
206
+ };
202
207
  const GUARDED_PUT_ATTEMPTS = 3;
203
208
  /**
204
209
  * @internal A delete's item was deleted between its read and its write, with
@@ -1458,7 +1463,7 @@ const makeImpl = (config, precomputedData) => {
1458
1463
  const needsFullSource = needing.some(([logicalName, definition]) => withVectors?.[logicalName] === undefined &&
1459
1464
  definition.sourceFields.some((field) => !(field in merged)));
1460
1465
  if (needsFullSource) {
1461
- const client = yield* DynamoClient;
1466
+ const client = yield* Capacity.client;
1462
1467
  const current = yield* client.getItem({ TableName: tableName, Key: marshalledKey });
1463
1468
  if (current.Item) {
1464
1469
  const currentDomain = fromAttributeMap(current.Item);
@@ -1766,7 +1771,7 @@ const makeImpl = (config, precomputedData) => {
1766
1771
  * update still WAS applied: {@link UpdateAppliedButUnreadable}.
1767
1772
  */
1768
1773
  const readRetainPostImage = (tableName, marshalledKey, replaced, ourVersion, ourIncarnation, derivedSets, derivedRemoves, ttlAttrName, encodedKey) => Effect.gen(function* () {
1769
- const client = yield* DynamoClient;
1774
+ const client = yield* Capacity.client;
1770
1775
  const ours = (item) => item !== undefined &&
1771
1776
  (incarnationAttr === undefined ||
1772
1777
  attributeValueEquals(item[incarnationAttr], ourIncarnation));
@@ -1962,7 +1967,7 @@ const makeImpl = (config, precomputedData) => {
1962
1967
  * a digit, a segment with a composite name — so a key range bounds them.
1963
1968
  */
1964
1969
  const legacyHistory = (args) => Effect.gen(function* () {
1965
- const client = yield* DynamoClient;
1970
+ const client = yield* Capacity.client;
1966
1971
  const primary = config.indexes.primary;
1967
1972
  const prefix = args.kind === "version"
1968
1973
  ? DynamoSchema.composeVersionKeyPrefix(schema, entityType)
@@ -2016,7 +2021,7 @@ const makeImpl = (config, precomputedData) => {
2016
2021
  * without a segment, whichever is later (its own on a tie).
2017
2022
  */
2018
2023
  const latestTombstone = (tableName, pk, liveSk) => Effect.gen(function* () {
2019
- const client = yield* DynamoClient;
2024
+ const client = yield* Capacity.client;
2020
2025
  const primary = config.indexes.primary;
2021
2026
  const own = (yield* client.query({
2022
2027
  TableName: tableName,
@@ -2144,7 +2149,7 @@ const makeImpl = (config, precomputedData) => {
2144
2149
  const ownedSentinels = (tableName, candidates, owner) => Effect.gen(function* () {
2145
2150
  if (candidates.length === 0)
2146
2151
  return [];
2147
- const client = yield* DynamoClient;
2152
+ const client = yield* Capacity.client;
2148
2153
  const pkField = config.indexes.primary.pk.field;
2149
2154
  const skField = config.indexes.primary.sk.field;
2150
2155
  const ownerPk = toAttributeValue(owner.pk);
@@ -2208,7 +2213,7 @@ const makeImpl = (config, precomputedData) => {
2208
2213
  * overwriting its earlier incarnation's history.
2209
2214
  */
2210
2215
  const highestRetainedVersion = (tableName, pk, liveSk) => Effect.gen(function* () {
2211
- const client = yield* DynamoClient;
2216
+ const client = yield* Capacity.client;
2212
2217
  const primary = config.indexes.primary;
2213
2218
  const { Items } = yield* client.query({
2214
2219
  TableName: tableName,
@@ -2260,7 +2265,7 @@ const makeImpl = (config, precomputedData) => {
2260
2265
  * is final.
2261
2266
  */
2262
2267
  const planPut = (args) => Effect.gen(function* () {
2263
- const client = yield* DynamoClient;
2268
+ const client = yield* Capacity.client;
2264
2269
  const pkField = config.indexes.primary.pk.field;
2265
2270
  const skField = config.indexes.primary.sk.field;
2266
2271
  const item = { ...args.item };
@@ -2470,7 +2475,7 @@ const makeImpl = (config, precomputedData) => {
2470
2475
  * concurrency error.
2471
2476
  */
2472
2477
  const runGuardedPut = (args) => Effect.gen(function* () {
2473
- const client = yield* DynamoClient;
2478
+ const client = yield* Capacity.client;
2474
2479
  let lost;
2475
2480
  for (let attempt = 0; attempt < GUARDED_PUT_ATTEMPTS; attempt++) {
2476
2481
  const plan = yield* planPut(args);
@@ -2601,7 +2606,7 @@ const makeImpl = (config, precomputedData) => {
2601
2606
  const executeCascade = (cascadeConfig, sourceDomainData, sourceIdValue) => Effect.gen(function* () {
2602
2607
  const { targets, filter, mode } = cascadeConfig;
2603
2608
  const sourceEntityType = entityType;
2604
- const client = yield* DynamoClient;
2609
+ const client = yield* Capacity.client;
2605
2610
  if (!sourceIdentifierField)
2606
2611
  return undefined;
2607
2612
  const allUpdateOps = [];
@@ -2788,7 +2793,7 @@ const makeImpl = (config, precomputedData) => {
2788
2793
  // dereference `self` when called by users, after make() has returned
2789
2794
  let self;
2790
2795
  const put = (input) => new EntityPutImpl((mode, opts) => Effect.gen(function* () {
2791
- const client = yield* DynamoClient;
2796
+ const client = yield* Capacity.client;
2792
2797
  const tc = yield* tableTag;
2793
2798
  const tableName = tc.name;
2794
2799
  const ttlAttrName = resolveTtlAttributeName(tc);
@@ -2974,7 +2979,7 @@ const makeImpl = (config, precomputedData) => {
2974
2979
  // get operation
2975
2980
  // ---------------------------------------------------------------------------
2976
2981
  const get = (key) => new EntityGetImpl((mode, opts) => Effect.gen(function* () {
2977
- const client = yield* DynamoClient;
2982
+ const client = yield* Capacity.client;
2978
2983
  const { name: tableName } = yield* tableTag;
2979
2984
  // Caller key: Type side in, ENCODED out (see `encodeKey`).
2980
2985
  const encodedKey = yield* encodeKey(key, "get.decode");
@@ -3091,7 +3096,7 @@ const makeImpl = (config, precomputedData) => {
3091
3096
  // The caller's condition, ANDed onto `patch`'s exists guard — a guard
3092
3097
  // no `.condition()` can replace (#133).
3093
3098
  const userCond = withGuard(uState.patch ? { attributeExists: [config.indexes.primary.pk.field] } : undefined, uState.condition);
3094
- const client = yield* DynamoClient;
3099
+ const client = yield* Capacity.client;
3095
3100
  const tc = yield* tableTag;
3096
3101
  const tableName = tc.name;
3097
3102
  const ttlAttrName = resolveTtlAttributeName(tc);
@@ -4574,7 +4579,7 @@ const makeImpl = (config, precomputedData) => {
4574
4579
  // so the row is read here, and a missing row is answered to the op as
4575
4580
  // its write would have been answered: it takes the same path.
4576
4581
  if (!recorded.readOwnRow && items.length > 0) {
4577
- const client = yield* DynamoClient;
4582
+ const client = yield* Capacity.client;
4578
4583
  const { name: tableName } = yield* tableTag;
4579
4584
  const read = yield* client.getItem({
4580
4585
  TableName: tableName,
@@ -4760,7 +4765,7 @@ const makeImpl = (config, precomputedData) => {
4760
4765
  // delete operation
4761
4766
  // ---------------------------------------------------------------------------
4762
4767
  const del = (key) => new EntityDeleteImpl((opts) => Effect.gen(function* () {
4763
- const client = yield* DynamoClient;
4768
+ const client = yield* Capacity.client;
4764
4769
  const tc = yield* tableTag;
4765
4770
  const tableName = tc.name;
4766
4771
  const ttlAttrName = resolveTtlAttributeName(tc);
@@ -5151,7 +5156,7 @@ const makeImpl = (config, precomputedData) => {
5151
5156
  * itself fails the upsert, as it fails an update.
5152
5157
  */
5153
5158
  const guardedUpsert = (input, mode, opts) => Effect.gen(function* () {
5154
- const client = yield* DynamoClient;
5159
+ const client = yield* Capacity.client;
5155
5160
  const { name: tableName } = yield* tableTag;
5156
5161
  yield* checkWithVectorNames(opts.withVectors, "upsert");
5157
5162
  // Required fields are required whether or not the item exists.
@@ -5252,7 +5257,7 @@ const makeImpl = (config, precomputedData) => {
5252
5257
  omitsIndexedDefault(input)) {
5253
5258
  return yield* guardedUpsert(input, mode, opts);
5254
5259
  }
5255
- const client = yield* DynamoClient;
5260
+ const client = yield* Capacity.client;
5256
5261
  const { name: tableName } = yield* tableTag;
5257
5262
  // Clock-backed time source for createdAt/updatedAt SET clauses.
5258
5263
  const now = yield* DateTime.now;
@@ -5561,7 +5566,7 @@ const makeImpl = (config, precomputedData) => {
5561
5566
  // `ValidationError("append.followUp")`. Undetected on skipFollowUp path.
5562
5567
  // ---------------------------------------------------------------------------
5563
5568
  const timeSeriesConfig = config.timeSeries;
5564
- const append = (input, givenCondition, skipFollowUp = false, removeAttrs) => Effect.gen(function* () {
5569
+ const appendCore = (input, givenCondition, skipFollowUp = false, removeAttrs) => Effect.gen(function* () {
5565
5570
  if (!timeSeriesConfig) {
5566
5571
  return yield* new ValidationError({
5567
5572
  entityType,
@@ -5590,7 +5595,7 @@ const makeImpl = (config, precomputedData) => {
5590
5595
  cause: `append: ${emptyPart} Nothing was sent.`,
5591
5596
  });
5592
5597
  }
5593
- const client = yield* DynamoClient;
5598
+ const client = yield* Capacity.client;
5594
5599
  const tc = yield* tableTag;
5595
5600
  const tableName = tc.name;
5596
5601
  const ttlAttrName = resolveTtlAttributeName(tc);
@@ -5962,6 +5967,8 @@ const makeImpl = (config, precomputedData) => {
5962
5967
  // ---------------------------------------------------------------------------
5963
5968
  // history operation — BoundQuery for event items only
5964
5969
  // ---------------------------------------------------------------------------
5970
+ // The public `append`: the core as the named operation (consumed capacity, #174).
5971
+ const append = (input, givenCondition, skipFollowUp = false, removeAttrs) => Capacity.operation("Entity.append", { target: entityType }, appendCore(input, givenCondition, skipFollowUp, removeAttrs));
5965
5972
  const history = (key) => {
5966
5973
  if (!timeSeriesConfig) {
5967
5974
  throw new Error(`[EDD-9010] Entity "${entityType}": .history() requires timeSeries config on the entity.`);
@@ -6061,7 +6068,7 @@ const makeImpl = (config, precomputedData) => {
6061
6068
  // getVersion operation
6062
6069
  // ---------------------------------------------------------------------------
6063
6070
  const getVersion = (key, versionNumber) => new EntityGetImpl((mode, opts) => Effect.gen(function* () {
6064
- const client = yield* DynamoClient;
6071
+ const client = yield* Capacity.client;
6065
6072
  const { name: tableName } = yield* tableTag;
6066
6073
  // Caller key: Type side in, ENCODED out (see `encodeKey`).
6067
6074
  const encodedKey = yield* encodeKey(key, "getVersion.decode");
@@ -6113,7 +6120,7 @@ const makeImpl = (config, precomputedData) => {
6113
6120
  const legacy = yield* legacyHistory({ tableName, pk: pkValue, liveSk, kind: "version" });
6114
6121
  if (legacy.length === 0)
6115
6122
  return {};
6116
- const client = yield* DynamoClient;
6123
+ const client = yield* Capacity.client;
6117
6124
  const own = new Set();
6118
6125
  let start;
6119
6126
  do {
@@ -6244,7 +6251,7 @@ const makeImpl = (config, precomputedData) => {
6244
6251
  // restore operation
6245
6252
  // ---------------------------------------------------------------------------
6246
6253
  const restore = (key) => new EntityGetImpl((mode, _opts) => Effect.gen(function* () {
6247
- const client = yield* DynamoClient;
6254
+ const client = yield* Capacity.client;
6248
6255
  const tc = yield* tableTag;
6249
6256
  const tableName = tc.name;
6250
6257
  const ttlAttrName = resolveTtlAttributeName(tc);
@@ -6446,7 +6453,7 @@ const makeImpl = (config, precomputedData) => {
6446
6453
  "delete(key).condition(...) instead.",
6447
6454
  });
6448
6455
  }
6449
- const client = yield* DynamoClient;
6456
+ const client = yield* Capacity.client;
6450
6457
  const { name: tableName } = yield* tableTag;
6451
6458
  // Caller key: Type side in, ENCODED out (see `encodeKey`).
6452
6459
  const encodedKey = yield* encodeKey(key, "purge.decode");
@@ -6743,7 +6750,7 @@ const makeImpl = (config, precomputedData) => {
6743
6750
  _liveRows: liveRows,
6744
6751
  _attachPrototype: attachPrototype,
6745
6752
  _overTombstone: (tableName, item) => Effect.gen(function* () {
6746
- const client = yield* DynamoClient;
6753
+ const client = yield* Capacity.client;
6747
6754
  return yield* overTombstone(self, client, tableName, item);
6748
6755
  }),
6749
6756
  _configure: (injectedSchema, injectedTableTag) => {
@@ -6776,11 +6783,16 @@ const makeImpl = (config, precomputedData) => {
6776
6783
  deleteIfExists,
6777
6784
  upsert,
6778
6785
  scan,
6779
- getVersion,
6786
+ // Lifecycle ops reuse the get / delete carriers; their builders carry the
6787
+ // operation name consumed-capacity reports use (#174).
6788
+ getVersion: (key, versionNumber) => namedOp(getVersion(key, versionNumber), "Entity.getVersion"),
6780
6789
  versions,
6781
- restore,
6782
- purge,
6783
- deleted: { get: deletedGet, list: deletedList },
6790
+ restore: (key) => namedOp(restore(key), "Entity.restore"),
6791
+ purge: (key) => namedOp(purge(key), "Entity.purge"),
6792
+ deleted: {
6793
+ get: (key) => namedOp(deletedGet(key), "Entity.deleted.get"),
6794
+ list: deletedList,
6795
+ },
6784
6796
  append,
6785
6797
  history,
6786
6798
  set,
@@ -6821,7 +6833,8 @@ export const bind = (entity) => Effect.gen(function* () {
6821
6833
  // Crypto-requiring entity ops typecheck. A platform override supplied via
6822
6834
  // `DynamoClient.make({ crypto })` is already in `baseCtx` and is respected
6823
6835
  // (we only fill the default when absent). See `DESIGN.md`.
6824
- const baseCtx = yield* Effect.context();
6836
+ // Detached: no current operation or parent span is pinned for later calls (#174).
6837
+ const baseCtx = Capacity.detachContext(yield* Effect.context());
6825
6838
  const ctx = Option.isSome(Context.getOption(baseCtx, Crypto.Crypto))
6826
6839
  ? baseCtx
6827
6840
  : Context.add(baseCtx, Crypto.Crypto, makeDefaultCrypto());
@@ -6849,12 +6862,13 @@ export const bind = (entity) => Effect.gen(function* () {
6849
6862
  // pre-resolved provide. Used by `versions` and `deleted.list` accessors so consumers
6850
6863
  // get the same fluent .collect() / .fetch() / .paginate() / .reverse() / .limit() /
6851
6864
  // .filter() ergonomics they get from index accessors and `.scan()`.
6852
- const wrapAsBoundQuery = (q) => {
6865
+ const wrapAsBoundQuery = (q, operation) => {
6853
6866
  const bqConfig = {
6854
6867
  pathBuilder: createPathBuilder(),
6855
6868
  conditionOps: createConditionOps(),
6856
6869
  provide,
6857
6870
  ...entityNaming(entity._resolveDbName),
6871
+ operation: { name: operation, target: entity.entityType },
6858
6872
  };
6859
6873
  return new BoundQueryImpl(q, bqConfig);
6860
6874
  };
@@ -6880,10 +6894,10 @@ export const bind = (entity) => Effect.gen(function* () {
6880
6894
  const primary = entity.indexes.primary;
6881
6895
  const boundEntityVersion = entity._entityVersion;
6882
6896
  const concurrency = options?.concurrency ?? 4;
6883
- return provide(Effect.gen(function* () {
6897
+ return provide(Capacity.operation("Entity.reembed", { target: entity.entityType }, Effect.gen(function* () {
6884
6898
  if (vectorDefs.length === 0)
6885
6899
  return 0;
6886
- const client = yield* DynamoClient;
6900
+ const client = yield* Capacity.client;
6887
6901
  const { name: tableName } = yield* entity._tableTag;
6888
6902
  const reembedItem = (marshalled) => Effect.gen(function* () {
6889
6903
  const raw = fromAttributeMap(marshalled);
@@ -6982,7 +6996,7 @@ export const bind = (entity) => Effect.gen(function* () {
6982
6996
  exclusiveStartKey = page.LastEvaluatedKey;
6983
6997
  } while (exclusiveStartKey !== undefined);
6984
6998
  return updated;
6985
- }));
6999
+ })));
6986
7000
  };
6987
7001
  return {
6988
7002
  // CRUD — fluent bound builders (yieldable, no .run() terminal)
@@ -6996,13 +7010,13 @@ export const bind = (entity) => Effect.gen(function* () {
6996
7010
  patch: (key) => makeBoundUpdate(entity.patch(key), boundCrudConfig),
6997
7011
  deleteIfExists: (key) => makeBoundDelete(entity.deleteIfExists(key), boundCrudConfig),
6998
7012
  // Lifecycle
6999
- getVersion: (key, version) => provide(entity.getVersion(key, version)._run("record")),
7000
- versions: (key) => wrapAsBoundQuery(entity.versions(key)),
7001
- restore: (key) => provide(entity.restore(key)._run("record")),
7013
+ getVersion: (key, version) => provide(entity.getVersion(key, version)._exec("record")),
7014
+ versions: (key) => wrapAsBoundQuery(entity.versions(key), "Entity.versions"),
7015
+ restore: (key) => provide(entity.restore(key)._exec("record")),
7002
7016
  purge: (key) => provide(entity.purge(key).asEffect()),
7003
7017
  deleted: {
7004
- get: (key) => provide(entity.deleted.get(key)._run("record")),
7005
- list: (key) => wrapAsBoundQuery(entity.deleted.list(key)),
7018
+ get: (key) => provide(entity.deleted.get(key)._exec("record")),
7019
+ list: (key) => wrapAsBoundQuery(entity.deleted.list(key), "Entity.deleted.list"),
7006
7020
  },
7007
7021
  // Time-series (no-ops when entity is not configured; runtime check in
7008
7022
  // the entity-level `append` returns a ValidationError).
@@ -7061,17 +7075,37 @@ export const bind = (entity) => Effect.gen(function* () {
7061
7075
  skFields: orderBy ? [orderBy] : [],
7062
7076
  composeSkCondition,
7063
7077
  ...entityNaming(entity._resolveDbName),
7078
+ operation: { name: "Entity.history", target: entity.entityType },
7064
7079
  };
7065
7080
  return new BoundQueryImpl(q, bqConfig);
7066
7081
  },
7067
7082
  // Query execution
7083
+ // Each helper is a named operation, inside `provide` (consumed capacity, #174).
7068
7084
  paginate: (q, ...combinators) => {
7069
7085
  const final = applyQuery(q, combinators);
7070
- return Stream.unwrap(provide(Query.paginate(final))).pipe(Stream.flatMap((page) => Stream.fromIterable(page)));
7086
+ const options = {
7087
+ ...Query.operationOptions(final),
7088
+ target: entity.entityType,
7089
+ };
7090
+ return Stream.unwrap(provide(Effect.flatMap(Query.paginate(final), (pages) => Capacity.operationStream("Entity.paginate", options, pages)))).pipe(Stream.flatMap((page) => Stream.fromIterable(page)));
7091
+ },
7092
+ collect: (q, ...combinators) => {
7093
+ const final = applyQuery(q, combinators);
7094
+ const options = {
7095
+ ...Query.operationOptions(final),
7096
+ target: entity.entityType,
7097
+ };
7098
+ return provide(Capacity.operation("Entity.collect", options, Query.collect(final)));
7099
+ },
7100
+ fetch: (q, ...combinators) => {
7101
+ const final = applyQuery(q, combinators);
7102
+ const options = {
7103
+ ...Query.operationOptions(final),
7104
+ target: entity.entityType,
7105
+ };
7106
+ return provide(Capacity.operation("Entity.fetch", options, Query.execute(final)));
7071
7107
  },
7072
- collect: (q, ...combinators) => provide(Query.collect(applyQuery(q, combinators))),
7073
- fetch: (q, ...combinators) => provide(Query.execute(applyQuery(q, combinators))),
7074
- scanFetch: (...combinators) => provide(Query.execute(applyQuery(entity.scan(), combinators))),
7108
+ scanFetch: (...combinators) => provide(Capacity.operation("Entity.scan", { target: entity.entityType }, Query.execute(applyQuery(entity.scan(), combinators)))),
7075
7109
  };
7076
7110
  });
7077
7111
  const tagged = (tags) => (e) => typeof e === "object" && e !== null && "_tag" in e && tags.has(String(e._tag));