graphddb 0.9.0 → 0.9.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.
@@ -1,7 +1,7 @@
1
- import { w as CdcEmulatorOptions, x as ChangeHandler, y as Unsubscribe, F as FaultSpec, z as ConcurrentRecomputeRef, v as ChangeEvent, A as EventLog, G as ReplayOptions, H as ShardId, I as MaintainTrigger, E as EffectPath, g as ProjectionMap, j as MembershipPredicate } from '../key-Dne-a20Q.js';
2
- export { J as BatchResult, K as CdcMode, L as ChangeBatch, N as ChangeEventName, O as ClockMode, V as StartingPosition, X as StreamViewType, Y as SubscribeHandler, Z as SubscribeHandlers, _ as buildSubscribeHandler } from '../key-Dne-a20Q.js';
3
- import { E as EntityMetadata } from '../types-BnFsTZwl.js';
4
- import '../types-CfxzTEFL.js';
1
+ import { w as CdcEmulatorOptions, x as ChangeHandler, y as Unsubscribe, F as FaultSpec, z as ConcurrentRecomputeRef, v as ChangeEvent, A as EventLog, G as ReplayOptions, H as ShardId, I as MaintainTrigger, E as EffectPath, g as ProjectionMap, j as MembershipPredicate } from '../key-CWytoEaE.js';
2
+ export { J as BatchResult, K as CdcMode, L as ChangeBatch, N as ChangeEventName, O as ClockMode, V as StartingPosition, X as StreamViewType, Y as SubscribeHandler, Z as SubscribeHandlers, _ as buildSubscribeHandler } from '../key-CWytoEaE.js';
3
+ import { E as EntityMetadata } from '../types-nk5okD7d.js';
4
+ import '../types-CgXS-4Ox.js';
5
5
  import 'behavior-contracts';
6
6
 
7
7
  /**
package/dist/cdc/index.js CHANGED
@@ -8,12 +8,12 @@ import {
8
8
  createMaintenanceDrainHandler,
9
9
  hashString,
10
10
  shardIdFor
11
- } from "../chunk-YGQ2NO6J.js";
11
+ } from "../chunk-VECUS35D.js";
12
12
  import {
13
13
  buildSubscribeHandler,
14
14
  parseChange
15
- } from "../chunk-KOIJ4SNO.js";
16
- import "../chunk-ZNU7OI5I.js";
15
+ } from "../chunk-7OCXY4R6.js";
16
+ import "../chunk-GWWRXIHF.js";
17
17
  import "../chunk-XTWXMOHD.js";
18
18
  export {
19
19
  CdcEmulator,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  MetadataRegistry,
3
3
  attachHiddenKey
4
- } from "./chunk-ZNU7OI5I.js";
4
+ } from "./chunk-GWWRXIHF.js";
5
5
  import {
6
6
  resolveKey,
7
7
  segmentFieldNames
@@ -2070,9 +2070,21 @@ function buildUpdateInput(modelClass, entity, changes, options) {
2070
2070
  const embeddedFieldNames = new Set(
2071
2071
  meta.embeddedFields.map((e) => e.propertyName)
2072
2072
  );
2073
- const updateResult = buildUpdateExpression(serializedChanges, embeddedFieldNames);
2074
- const names = { ...updateResult.names };
2075
- const values = { ...updateResult.values };
2073
+ const addEntries = Object.entries(options?.add ?? {});
2074
+ const addNames = {};
2075
+ const addValues = {};
2076
+ const addClauses = [];
2077
+ addEntries.forEach(([field, delta], i) => {
2078
+ const nameKey = `#add${i}`;
2079
+ const valueKey = `:add${i}`;
2080
+ addNames[nameKey] = field;
2081
+ addValues[valueKey] = delta;
2082
+ addClauses.push(`${nameKey} ${valueKey}`);
2083
+ });
2084
+ const hasChanges = Object.keys(serializedChanges).length > 0;
2085
+ const updateResult = hasChanges || addClauses.length === 0 ? buildUpdateExpression(serializedChanges, embeddedFieldNames) : { expression: "", names: {}, values: {} };
2086
+ const names = { ...updateResult.names, ...addNames };
2087
+ const values = { ...updateResult.values, ...addValues };
2076
2088
  const gsiSetClauses = buildGsiRederiveSets(
2077
2089
  modelClass,
2078
2090
  meta,
@@ -2090,13 +2102,16 @@ function buildUpdateInput(modelClass, entity, changes, options) {
2090
2102
  Object.assign(names, condResult.names);
2091
2103
  Object.assign(values, condResult.values);
2092
2104
  }
2105
+ const setPortion = appendSetClauses(updateResult.expression, gsiSetClauses);
2106
+ const addPortion = addClauses.length > 0 ? `ADD ${addClauses.join(", ")}` : "";
2107
+ const updateExpression = [setPortion, addPortion].filter((s) => s !== "").join(" ");
2093
2108
  const updateInput = {
2094
2109
  TableName: tableName,
2095
2110
  Key: {
2096
2111
  PK: pk,
2097
2112
  SK: sk
2098
2113
  },
2099
- UpdateExpression: appendSetClauses(updateResult.expression, gsiSetClauses),
2114
+ UpdateExpression: updateExpression,
2100
2115
  ExpressionAttributeNames: names
2101
2116
  };
2102
2117
  if (Object.keys(values).length > 0) {
@@ -17,7 +17,7 @@ import {
17
17
  normalizeTopLevelSelect,
18
18
  parseChange,
19
19
  validateInlineSnapshotSelect
20
- } from "./chunk-KOIJ4SNO.js";
20
+ } from "./chunk-7OCXY4R6.js";
21
21
  import {
22
22
  EXPR_VERSION,
23
23
  MARKER_ROW_ENTITY,
@@ -61,7 +61,7 @@ import {
61
61
  serializeFieldValue,
62
62
  serializeRawCondition,
63
63
  skTemplate
64
- } from "./chunk-ZNU7OI5I.js";
64
+ } from "./chunk-GWWRXIHF.js";
65
65
  import {
66
66
  TableMapping,
67
67
  createColumnMap,
@@ -2520,7 +2520,12 @@ function renderWrite(op, key, params, contextLabel) {
2520
2520
  return { ...base, item: renderRecord(op.item, key, params, `${contextLabel} item`) };
2521
2521
  }
2522
2522
  if (op.operation === "update") {
2523
- return { ...base, changes: renderRecord(op.changes, key, params, `${contextLabel} changes`) };
2523
+ const add = op.add !== void 0 && Object.keys(op.add).length > 0 ? renderRecord(op.add, key, params, `${contextLabel} add`) : void 0;
2524
+ return {
2525
+ ...base,
2526
+ changes: renderRecord(op.changes, key, params, `${contextLabel} changes`),
2527
+ ...add !== void 0 ? { add } : {}
2528
+ };
2524
2529
  }
2525
2530
  return base;
2526
2531
  }
@@ -2534,11 +2539,34 @@ async function executeSingleWrite(w, retry) {
2534
2539
  return;
2535
2540
  }
2536
2541
  if (w.operation === "update") {
2542
+ if (w.add !== void 0) {
2543
+ const primaryOptions = { ...options ?? {}, add: w.add };
2544
+ if (w.condition === void 0) {
2545
+ await executeUpdate(w.modelClass, w.key, w.changes ?? {}, primaryOptions);
2546
+ return;
2547
+ }
2548
+ try {
2549
+ await executeUpdate(w.modelClass, w.key, w.changes ?? {}, primaryOptions);
2550
+ } catch (e) {
2551
+ if (!isConditionalCheckFailure(e)) throw e;
2552
+ const fallbackOptions = {
2553
+ add: w.add,
2554
+ ...retry !== void 0 ? { retry } : {}
2555
+ };
2556
+ await executeUpdate(w.modelClass, w.key, {}, fallbackOptions);
2557
+ }
2558
+ return;
2559
+ }
2537
2560
  await executeUpdate(w.modelClass, w.key, w.changes ?? {}, options);
2538
2561
  return;
2539
2562
  }
2540
2563
  await executeDelete(w.modelClass, w.key, options);
2541
2564
  }
2565
+ function isConditionalCheckFailure(e) {
2566
+ if (e === null || typeof e !== "object") return false;
2567
+ const name = e.name;
2568
+ return name === "ConditionalCheckFailedException" || name === "ConditionalCheckFailed";
2569
+ }
2542
2570
  function renderConditionCheck(check, key, params, contextLabel) {
2543
2571
  const modelClass = check.entity.modelClass;
2544
2572
  const referencedKey = {};
@@ -4428,8 +4456,17 @@ function isConcreteLiteral(value) {
4428
4456
  const t = typeof value;
4429
4457
  return value === null || value instanceof Date || t === "string" || t === "number" || t === "boolean";
4430
4458
  }
4431
- function makeFragment(intent, entity, input, use, condition) {
4459
+ function makeFragment(intent, entity, input, use, condition, add) {
4432
4460
  const checked = assertFaithfulInput(input ?? {}, intent, entity.name);
4461
+ let checkedAdd;
4462
+ if (add !== void 0 && Object.keys(add).length > 0) {
4463
+ if (intent !== "update") {
4464
+ throw new Error(
4465
+ `mutation: the '${intent}' fragment on '${entity.name}' declares \`add\`, but an atomic \`ADD\` (issue #301) is only valid on an \`update\` fragment.`
4466
+ );
4467
+ }
4468
+ checkedAdd = assertFaithfulInput(add, intent, entity.name);
4469
+ }
4433
4470
  if (use !== void 0 && !isEntityWritesDefinition(use)) {
4434
4471
  throw new Error(
4435
4472
  `mutation: the '${intent}' fragment on '${entity.name}' was given a \`use:\` that is not an \`entityWrites(...)\` save contract. \`use:\` takes the WHOLE \`writes\` set (an \`entityWrites(...)\` value), never a single lifecycle (e.g. \`Model.writes.create\`) \u2014 the fragment's intent already selects the lifecycle.`
@@ -4441,6 +4478,7 @@ function makeFragment(intent, entity, input, use, condition) {
4441
4478
  intent,
4442
4479
  entity,
4443
4480
  input: checked,
4481
+ ...checkedAdd !== void 0 ? { add: checkedAdd } : {},
4444
4482
  ...use !== void 0 ? { use } : {},
4445
4483
  ...checkedCondition !== void 0 ? { condition: checkedCondition } : {}
4446
4484
  };
@@ -4536,7 +4574,8 @@ function mutation(nameOrBody, maybeBody) {
4536
4574
  const { intent, entity } = resolveDescriptorTarget(descriptor, alias);
4537
4575
  const merged = mergeBindings(descriptor, intent, alias);
4538
4576
  const resolved = resolveAliasRefs(merged, aliasToIndex, alias);
4539
- return makeFragment(intent, entity, resolved, descriptor.use, descriptor.condition);
4577
+ const add = descriptor.add !== void 0 ? resolveAliasRefs(descriptor.add, aliasToIndex, alias) : void 0;
4578
+ return makeFragment(intent, entity, resolved, descriptor.use, descriptor.condition, add);
4540
4579
  });
4541
4580
  return { [COMMAND_PLAN_BRAND]: true, name, fragments };
4542
4581
  }
@@ -5007,6 +5046,35 @@ function writeDescriptorToPlan(name, d) {
5007
5046
  const model = resolveDescriptorModel(d[intent], name);
5008
5047
  const keyFieldNames = Object.keys(d.key);
5009
5048
  const inputFieldNames = d.input !== void 0 ? Object.keys(d.input) : [];
5049
+ const addFieldNames = d.add !== void 0 ? Object.keys(d.add) : [];
5050
+ if (addFieldNames.length > 0) {
5051
+ if (intent !== "update") {
5052
+ throw new Error(
5053
+ `publishCommand: method '${name}' declares \`add\` on a '${intent}' descriptor, but an atomic \`ADD\` (issue #301) is only valid on an \`update\` (a standalone UpdateItem). A create / upsert / remove cannot carry \`add\`.`
5054
+ );
5055
+ }
5056
+ if (d.add === null || typeof d.add !== "object") {
5057
+ throw new Error(`publishCommand: method '${name}' has a non-object \`add\`.`);
5058
+ }
5059
+ for (const field of addFieldNames) {
5060
+ const v = d.add[field];
5061
+ if (!isParam(v) || v.kind !== "number") {
5062
+ throw new Error(
5063
+ `publishCommand: method '${name}' binds \`add.${field}\` to a non-\`param.number()\` value. An atomic \`ADD\` delta (issue #301) is a caller \`param.number()\` \u2014 a compile-time constant or a non-numeric param is not a valid atomic increment.`
5064
+ );
5065
+ }
5066
+ if (keyFieldNames.includes(field)) {
5067
+ throw new Error(
5068
+ `publishCommand: method '${name}' binds field '${field}' in both \`key\` and \`add\`. A primary-key field cannot be atomically incremented \u2014 it belongs in \`key\` only.`
5069
+ );
5070
+ }
5071
+ if (inputFieldNames.includes(field)) {
5072
+ throw new Error(
5073
+ `publishCommand: method '${name}' binds field '${field}' in both \`input\` (SET) and \`add\` (ADD). A field is either overwritten (\`input\`) or atomically incremented (\`add\`), never both.`
5074
+ );
5075
+ }
5076
+ }
5077
+ }
5010
5078
  const plan2 = mutation(name, ($) => {
5011
5079
  const keyRefs = (fields) => {
5012
5080
  const out = {};
@@ -5024,7 +5092,10 @@ function writeDescriptorToPlan(name, d) {
5024
5092
  const descriptor = {
5025
5093
  [intent]: (() => model),
5026
5094
  key: keyRefs(keyFieldNames),
5027
- ...inputFieldNames.length > 0 ? { input: inputRefs(d.input) } : {}
5095
+ ...inputFieldNames.length > 0 ? { input: inputRefs(d.input) } : {},
5096
+ // #301: `add` deltas are always `param.number()` (validated above), so each binds
5097
+ // to a same-named `$.field` placeholder — the runtime resolves the caller delta.
5098
+ ...addFieldNames.length > 0 ? { add: keyRefs(addFieldNames) } : {}
5028
5099
  };
5029
5100
  return { [name]: descriptor };
5030
5101
  });
@@ -5798,6 +5869,12 @@ function compileFragment(fragment, index = 0, resolveEntityRef = null, maintenan
5798
5869
  if (keyFieldSet.has(field)) continue;
5799
5870
  changes[field] = renderInputLeaf(leaf, false, index, field, resolveEntityRef);
5800
5871
  }
5872
+ const add = {};
5873
+ if (fragment.add !== void 0) {
5874
+ for (const [field, leaf] of Object.entries(fragment.add)) {
5875
+ add[field] = renderInputLeaf(leaf, false, index, field, resolveEntityRef);
5876
+ }
5877
+ }
5801
5878
  op = {
5802
5879
  __isContractMethodOp: true,
5803
5880
  entity: fragment.entity,
@@ -5805,6 +5882,7 @@ function compileFragment(fragment, index = 0, resolveEntityRef = null, maintenan
5805
5882
  keys: wholeKeysSentinel(),
5806
5883
  keyFields,
5807
5884
  ...fragment.intent === "update" ? { changes } : {},
5885
+ ...fragment.intent === "update" && Object.keys(add).length > 0 ? { add } : {},
5808
5886
  // #242: a conditioned update / remove — the write is a CAS-style gate (the
5809
5887
  // row must satisfy the condition or the write is rejected, not silently
5810
5888
  // committed). Absent → an unconditional update / remove (unchanged).
@@ -2,7 +2,7 @@ import {
2
2
  PREPARED_FORMAT_VERSION,
3
3
  buildManifest,
4
4
  entityFingerprint
5
- } from "./chunk-NWTEUWJD.js";
5
+ } from "./chunk-PHXUFAY2.js";
6
6
  import {
7
7
  analyzeWriteRoute,
8
8
  assertBundleSerializable,
@@ -21,12 +21,12 @@ import {
21
21
  isContractKeyRef,
22
22
  isContractParamRef,
23
23
  isQueryModelContract
24
- } from "./chunk-XZTA4VJH.js";
24
+ } from "./chunk-HLFNCKFV.js";
25
25
  import {
26
26
  detectRelationFields,
27
27
  isInlineSnapshotSpec,
28
28
  normalizeSelectSpec
29
- } from "./chunk-KOIJ4SNO.js";
29
+ } from "./chunk-7OCXY4R6.js";
30
30
  import {
31
31
  EXPR_VERSION,
32
32
  MARKER_ROW_ENTITY,
@@ -46,7 +46,7 @@ import {
46
46
  isRawCondition,
47
47
  param,
48
48
  resolveModelClass
49
- } from "./chunk-ZNU7OI5I.js";
49
+ } from "./chunk-GWWRXIHF.js";
50
50
  import {
51
51
  TableMapping,
52
52
  isColumn,
@@ -232,6 +232,7 @@ function opToDefinition(contractName, methodName, op) {
232
232
  }
233
233
  const select = op.select !== void 0 ? assertBooleanProjection(op.select, `${contractName}.${methodName} select`) : void 0;
234
234
  const changes = op.changes !== void 0 ? convertStructure(op.changes, place, `${contractName}.${methodName} changes`) : void 0;
235
+ const add = op.add !== void 0 ? convertStructure(op.add, place, `${contractName}.${methodName} add`) : void 0;
235
236
  const condition = op.condition !== void 0 ? convertCondition(op.condition, place, `${contractName}.${methodName} condition`) : void 0;
236
237
  return {
237
238
  __isOperationDefinition: true,
@@ -240,6 +241,7 @@ function opToDefinition(contractName, methodName, op) {
240
241
  key,
241
242
  select,
242
243
  changes,
244
+ ...add !== void 0 ? { add } : {},
243
245
  ...condition !== void 0 ? { condition } : {},
244
246
  // #154: the use-case description captured on the recorded op (from the
245
247
  // descriptor's `description`) flows into the flattened `queries[op]` /
@@ -584,6 +586,14 @@ function toElementTemplate(template, keyParams) {
584
586
  );
585
587
  }
586
588
  function toElementRecord(record, keyParams) {
589
+ const out = {};
590
+ for (const field of Object.keys(record)) {
591
+ const value = record[field];
592
+ out[field] = typeof value === "string" ? toElementTemplate(value, keyParams) : value;
593
+ }
594
+ return out;
595
+ }
596
+ function toElementKeyRecord(record, keyParams) {
587
597
  const out = {};
588
598
  for (const field of Object.keys(record)) {
589
599
  out[field] = toElementTemplate(record[field], keyParams);
@@ -636,7 +646,7 @@ function synthesizeBatchTransaction(commandSpec, op) {
636
646
  tableName: commandSpec.tableName,
637
647
  entity: commandSpec.entity,
638
648
  ...commandSpec.item !== void 0 ? { item: toElementRecord(commandSpec.item, keyParams) } : {},
639
- ...commandSpec.keyCondition !== void 0 ? { keyCondition: toElementRecord(commandSpec.keyCondition, keyParams) } : {},
649
+ ...commandSpec.keyCondition !== void 0 ? { keyCondition: toElementKeyRecord(commandSpec.keyCondition, keyParams) } : {},
640
650
  ...commandSpec.changes !== void 0 ? { changes: toElementRecord(commandSpec.changes, keyParams) } : {},
641
651
  ...condition !== void 0 ? { condition } : {},
642
652
  forEach: { source: "keys" }
@@ -829,8 +839,9 @@ function buildDerivedUpdateItems(derivedUpdates) {
829
839
  tableName,
830
840
  entity: update.entity.name,
831
841
  keyCondition,
832
- // The delta is a compile-time constant → a literal numeric template (no param).
833
- add: { [update.attribute]: String(update.amount) }
842
+ // The delta is a compile-time constant → a TYPED numeric literal (no param),
843
+ // carried verbatim so it marshals as `N`, not a stringified `S "1"` (#299).
844
+ add: { [update.attribute]: update.amount }
834
845
  });
835
846
  collectTemplateParams(keyCondition, metadata, params);
836
847
  }
@@ -890,7 +901,8 @@ function buildMaintainWriteItems(maintainWrites) {
890
901
  ...maintain.kind === "counter" && maintain.counter !== void 0 && maintain.counter.op === "count" ? {
891
902
  counter: {
892
903
  attribute: maintain.counter.attribute,
893
- delta: String(maintain.counter.delta)
904
+ // TYPED numeric delta (#299): carried verbatim so it marshals as `N`.
905
+ delta: maintain.counter.delta
894
906
  }
895
907
  } : {}
896
908
  };
@@ -1713,7 +1725,7 @@ function conditionPorts(condition, ports) {
1713
1725
  return;
1714
1726
  case "equals": {
1715
1727
  for (const field of Object.keys(condition.fields).sort()) {
1716
- ports[`condition.equals.${field}`] = lowerTransactionTemplate(condition.fields[field]);
1728
+ ports[`condition.equals.${field}`] = lowerTransactionValue(condition.fields[field]);
1717
1729
  }
1718
1730
  return;
1719
1731
  }
@@ -1882,11 +1894,12 @@ function paramSpecs(params) {
1882
1894
  function metaFor(def) {
1883
1895
  return MetadataRegistry.get(def.entity.modelClass);
1884
1896
  }
1885
- function templateLeaf(key, value) {
1897
+ function templateValueLeaf(key, value) {
1886
1898
  if (isContractParamRef(value)) return value.token;
1887
1899
  if (isContractKeyFieldRef(value)) return `{${value.field}}`;
1888
1900
  if (isParam(value)) return `{${key}}`;
1889
1901
  if (value instanceof Date) return value.toISOString();
1902
+ if (typeof value === "number" || typeof value === "boolean") return value;
1890
1903
  return String(value);
1891
1904
  }
1892
1905
  function keyFieldNames(key) {
@@ -2220,6 +2233,8 @@ function buildCommandSpec(def) {
2220
2233
  ...description
2221
2234
  };
2222
2235
  }
2236
+ const addStructure = def.add;
2237
+ const add = addStructure !== void 0 && Object.keys(addStructure).length > 0 ? { add: templateRecord(addStructure) } : {};
2223
2238
  return {
2224
2239
  type: "UpdateItem",
2225
2240
  tableName,
@@ -2227,6 +2242,7 @@ function buildCommandSpec(def) {
2227
2242
  params,
2228
2243
  keyCondition: writeKeyCondition(metadata, def.key, entity),
2229
2244
  changes: templateRecord(def.changes),
2245
+ ...add,
2230
2246
  ...condition ? { condition } : {},
2231
2247
  ...description
2232
2248
  };
@@ -2253,7 +2269,7 @@ function writeKeyCondition(metadata, key, entity) {
2253
2269
  function templateRecord(structure) {
2254
2270
  const out = {};
2255
2271
  for (const key of Object.keys(structure).sort()) {
2256
- out[key] = templateLeaf(key, structure[key]);
2272
+ out[key] = templateValueLeaf(key, structure[key]);
2257
2273
  }
2258
2274
  return out;
2259
2275
  }
@@ -2261,7 +2277,7 @@ function extractCondition(def) {
2261
2277
  return conditionInputToSpec(
2262
2278
  def.condition,
2263
2279
  `Command on '${def.entity.name}'`,
2264
- (field, value) => templateLeaf(field, value),
2280
+ (field, value) => templateValueLeaf(field, value),
2265
2281
  (value, name) => conditionTreeLeaf(value, name),
2266
2282
  // A raw `cond` value slot (issue #114-B): a contract param / key ref carries
2267
2283
  // its own name; a plain `param.*` falls through to the positional default.
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  evaluateKey
3
- } from "./chunk-XZTA4VJH.js";
3
+ } from "./chunk-HLFNCKFV.js";
4
4
  import {
5
5
  SPEC_VERSION_KEY_EXPR
6
6
  } from "./chunk-WOFRHRXY.js";
7
7
  import {
8
8
  MetadataRegistry
9
- } from "./chunk-ZNU7OI5I.js";
9
+ } from "./chunk-GWWRXIHF.js";
10
10
  import {
11
11
  TableMapping
12
12
  } from "./chunk-XTWXMOHD.js";
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  buildMaintenanceGraph
3
- } from "./chunk-KOIJ4SNO.js";
3
+ } from "./chunk-7OCXY4R6.js";
4
4
  import {
5
5
  ChangeCaptureRegistry,
6
6
  ClientManager,
@@ -9,7 +9,7 @@ import {
9
9
  buildDeleteInput,
10
10
  buildPutInput,
11
11
  resolveModelClass
12
- } from "./chunk-ZNU7OI5I.js";
12
+ } from "./chunk-GWWRXIHF.js";
13
13
 
14
14
  // src/cdc/prng.ts
15
15
  var SeededRandom = class {
package/dist/cli.js CHANGED
@@ -4,16 +4,16 @@ import {
4
4
  buildOperations,
5
5
  portableIrDocument,
6
6
  renderKeyExprToTemplate
7
- } from "./chunk-SSVG6NQK.js";
7
+ } from "./chunk-MBJ4JVRM.js";
8
8
  import {
9
9
  buildManifest
10
- } from "./chunk-NWTEUWJD.js";
11
- import "./chunk-XZTA4VJH.js";
12
- import "./chunk-KOIJ4SNO.js";
10
+ } from "./chunk-PHXUFAY2.js";
11
+ import "./chunk-HLFNCKFV.js";
12
+ import "./chunk-7OCXY4R6.js";
13
13
  import "./chunk-WOFRHRXY.js";
14
14
  import {
15
15
  MetadataRegistry
16
- } from "./chunk-ZNU7OI5I.js";
16
+ } from "./chunk-GWWRXIHF.js";
17
17
  import {
18
18
  createDefaultLinter
19
19
  } from "./chunk-XTWXMOHD.js";
package/dist/index.d.ts CHANGED
@@ -1,10 +1,10 @@
1
- import { ak as SelectableOf, al as PrimaryKeyOf, e as ProjectionTransform, f as MaintainEvent, j as MembershipPredicate, h as MaintainConsistency, i as MaintainUpdateMode, am as MembershipPredicateOp, an as publishQuery, ao as publishCommand, D as DDBModel, ap as RetryPolicy, aq as Middleware } from './key-Dne-a20Q.js';
2
- export { ar as CdcModelRegistry, as as CdcSubscribeHandlers, at as Change, au as Column, av as ColumnMap, aw as CondSlot, ax as CtxBase, ay as CtxModel, az as FilterInput, aA as GsiDefinitionMarker, aB as GsiOptions, aC as InlineSnapshotSpec, aD as Item, aE as KeyDefinitionMarker, aF as KeySegment, aG as KeySlot, aH as KeyStructure, M as ModelStatic, aI as MutateAuthoringOptions, aJ as ParamDescriptor, aK as ParamStructure, aL as PartialQueryKeyOf, aM as PersistCtx, aN as PersistOrigin, aO as QueryKeyOf, aP as QueryResult, aQ as RawCondition, aR as ReadOpCtx, aS as ReadOpKind, aT as ReadParams, aU as ReadRequestCtx, aV as ReadRequestKind, aW as RelationBuilder, aX as RelationSelect, aY as RelationSpec, aZ as RequestContext, a_ as SegmentSpec, d as SegmentedKey, a$ as SelectBuilder, b0 as SelectOf, b1 as UniqueQueryKeyOf, b2 as Updatable, b3 as WriteCtx, b4 as WriteInput, b5 as WriteKind, b6 as WriteMiddleware, b7 as cond, b8 as entityWrites, b9 as getEntityWrites, ba as gsi, bb as identity, bc as k, bd as key, be as mutate, bf as preview, bg as when } from './key-Dne-a20Q.js';
1
+ import { ak as SelectableOf, al as PrimaryKeyOf, e as ProjectionTransform, f as MaintainEvent, j as MembershipPredicate, h as MaintainConsistency, i as MaintainUpdateMode, am as MembershipPredicateOp, an as publishQuery, ao as publishCommand, D as DDBModel, ap as RetryPolicy, aq as Middleware } from './key-CWytoEaE.js';
2
+ export { ar as CdcModelRegistry, as as CdcSubscribeHandlers, at as Change, au as Column, av as ColumnMap, aw as CondSlot, ax as CtxBase, ay as CtxModel, az as FilterInput, aA as GsiDefinitionMarker, aB as GsiOptions, aC as InlineSnapshotSpec, aD as Item, aE as KeyDefinitionMarker, aF as KeySegment, aG as KeySlot, aH as KeyStructure, M as ModelStatic, aI as MutateAuthoringOptions, aJ as ParamDescriptor, aK as ParamStructure, aL as PartialQueryKeyOf, aM as PersistCtx, aN as PersistOrigin, aO as QueryKeyOf, aP as QueryResult, aQ as RawCondition, aR as ReadOpCtx, aS as ReadOpKind, aT as ReadParams, aU as ReadRequestCtx, aV as ReadRequestKind, aW as RelationBuilder, aX as RelationSelect, aY as RelationSpec, aZ as RequestContext, a_ as SegmentSpec, d as SegmentedKey, a$ as SelectBuilder, b0 as SelectOf, b1 as UniqueQueryKeyOf, b2 as Updatable, b3 as WriteCtx, b4 as WriteInput, b5 as WriteKind, b6 as WriteMiddleware, b7 as cond, b8 as entityWrites, b9 as getEntityWrites, ba as gsi, bb as identity, bc as k, bd as key, be as mutate, bf as preview, bg as when } from './key-CWytoEaE.js';
3
3
  import * as _aws_sdk_client_dynamodb from '@aws-sdk/client-dynamodb';
4
4
  import { p as publishBehaviors } from './behaviors-DDltNivc.js';
5
5
  export { b as BehaviorComponentFns, a as BehaviorMethodSpec, B as BehaviorModelContract, P as PublishBehaviorsOptions, c as behaviorComponents, i as isBehaviorModelContract } from './behaviors-DDltNivc.js';
6
- import { M as ModelKind, F as FieldOptions, D as DynamoType, R as RelationOptions, A as AggregateOptions, a as AggregateValue } from './types-BnFsTZwl.js';
7
- export { a0 as LiteralParam, a1 as MapParam, a2 as NumberParam, a3 as Param, a4 as ParamKind, a5 as StringParam, a6 as param } from './types-CfxzTEFL.js';
6
+ import { M as ModelKind, F as FieldOptions, D as DynamoType, R as RelationOptions, A as AggregateOptions, a as AggregateValue } from './types-nk5okD7d.js';
7
+ export { a0 as LiteralParam, a1 as MapParam, a2 as NumberParam, a3 as Param, a4 as ParamKind, a5 as StringParam, a6 as param } from './types-CgXS-4Ox.js';
8
8
  import 'behavior-contracts';
9
9
 
10
10
  /**
package/dist/index.js CHANGED
@@ -3,13 +3,13 @@ import {
3
3
  mutate,
4
4
  publishCommand,
5
5
  publishQuery
6
- } from "./chunk-XZTA4VJH.js";
6
+ } from "./chunk-HLFNCKFV.js";
7
7
  import {
8
8
  entityWrites,
9
9
  getEntityWrites,
10
10
  identity,
11
11
  preview
12
- } from "./chunk-KOIJ4SNO.js";
12
+ } from "./chunk-7OCXY4R6.js";
13
13
  import {
14
14
  when
15
15
  } from "./chunk-WOFRHRXY.js";
@@ -22,7 +22,7 @@ import {
22
22
  deriveContractEffect,
23
23
  gsi,
24
24
  param
25
- } from "./chunk-ZNU7OI5I.js";
25
+ } from "./chunk-GWWRXIHF.js";
26
26
  import {
27
27
  TableMapping,
28
28
  k,
@@ -1,8 +1,8 @@
1
- import { P as PreparedWriteOpSpec, a as PreparedPlanDocument } from '../prepared-artifact-BXkARxwO.js';
2
- export { b as PreparedBindSpec, c as PreparedPlanSpec, d as PreparedReadRouteSpec } from '../prepared-artifact-BXkARxwO.js';
3
- import { M as Manifest } from '../types-CfxzTEFL.js';
4
- import { M as ModelStatic, D as DDBModel, S as Slot, P as PreparedWriteExecOptions, C as CommandReturn, a as ParallelOpResult, b as PreparedBody, c as PreparedStatement } from '../key-Dne-a20Q.js';
5
- import '../types-BnFsTZwl.js';
1
+ import { P as PreparedWriteOpSpec, a as PreparedPlanDocument } from '../prepared-artifact-CwH5ezFq.js';
2
+ export { b as PreparedBindSpec, c as PreparedPlanSpec, d as PreparedReadRouteSpec } from '../prepared-artifact-CwH5ezFq.js';
3
+ import { M as Manifest } from '../types-CgXS-4Ox.js';
4
+ import { M as ModelStatic, D as DDBModel, S as Slot, P as PreparedWriteExecOptions, C as CommandReturn, a as ParallelOpResult, b as PreparedBody, c as PreparedStatement } from '../key-CWytoEaE.js';
5
+ import '../types-nk5okD7d.js';
6
6
  import 'behavior-contracts';
7
7
 
8
8
  /**
@@ -2,15 +2,15 @@ import {
2
2
  PREPARED_FORMAT_VERSION,
3
3
  buildManifestEntity,
4
4
  entityFingerprint
5
- } from "../chunk-NWTEUWJD.js";
5
+ } from "../chunk-PHXUFAY2.js";
6
6
  import {
7
7
  LOGICAL_EFFECT_CATEGORIES,
8
8
  PreparedReadStatement,
9
9
  executeLogicalParallelWrites,
10
10
  executeLogicalWriteOps,
11
11
  serializeEffectParams
12
- } from "../chunk-XZTA4VJH.js";
13
- import "../chunk-KOIJ4SNO.js";
12
+ } from "../chunk-HLFNCKFV.js";
13
+ import "../chunk-7OCXY4R6.js";
14
14
  import {
15
15
  MARKER_ROW_ENTITY,
16
16
  SPEC_VERSION_KEY_EXPR,
@@ -21,7 +21,7 @@ import {
21
21
  buildConditionExpression,
22
22
  execItemKeySignature,
23
23
  resolveConditionTree
24
- } from "../chunk-ZNU7OI5I.js";
24
+ } from "../chunk-GWWRXIHF.js";
25
25
  import {
26
26
  TableMapping
27
27
  } from "../chunk-XTWXMOHD.js";
@@ -1,4 +1,4 @@
1
- import { a3 as Param, E as ExpressionSpec, a4 as ParamKind, T as TransactionSpec, Y as TransactionItemSpec } from './types-CfxzTEFL.js';
1
+ import { a3 as Param, E as ExpressionSpec, a4 as ParamKind, T as TransactionSpec, Y as TransactionItemSpec } from './types-CgXS-4Ox.js';
2
2
 
3
3
  /**
4
4
  * CDC emulator types (issue #72). These types are **cdc-module-owned**; core
@@ -1653,6 +1653,13 @@ interface OperationDefinition<T extends DDBModel, Op extends OperationKind, K, S
1653
1653
  readonly select: S;
1654
1654
  /** The parameterized changes for `update`; `undefined` otherwise. */
1655
1655
  readonly changes: Ch;
1656
+ /**
1657
+ * The parameterized atomic-`ADD` record for a standalone `update` (issue #301):
1658
+ * field name → a `param.number()` caller-delta leaf. Distinct from {@link changes}
1659
+ * (a SET) — each entry serializes to `CommandSpec.add` (`ADD #field :delta`).
1660
+ * `undefined` unless the command declared `add`.
1661
+ */
1662
+ readonly add?: Readonly<Record<string, unknown>>;
1656
1663
  /**
1657
1664
  * The declarative write condition for `put` / `update` / `delete`; `undefined`
1658
1665
  * when no condition is attached or for reads (issue #46).
@@ -2512,6 +2519,16 @@ interface MutationFragment {
2512
2519
  readonly intent: MutationIntent;
2513
2520
  readonly entity: EntityRef;
2514
2521
  readonly input: FragmentInput;
2522
+ /**
2523
+ * Atomic-**`ADD`** field binding on the self row (issue #301): model field name →
2524
+ * a {@link MutationInputRef} (`$.field`, a `param.number()` caller delta). Distinct
2525
+ * from {@link input} (a `SET`): the compiler ({@link
2526
+ * import('../spec/mutation-command.js').compileFragment}) lowers each leaf onto the
2527
+ * command spec's `add` record (an `ADD #field :delta`), so the runtime issues an
2528
+ * atomic increment (no read, concurrency-safe). Absent when the fragment declares no
2529
+ * `add`. Present only on an `update` fragment.
2530
+ */
2531
+ readonly add?: FragmentInput;
2515
2532
  readonly use?: EntityWritesDefinition;
2516
2533
  /**
2517
2534
  * The declarative write **condition** (gate) authored on the descriptor (issue
@@ -2581,6 +2598,16 @@ interface WriteDescriptor {
2581
2598
  readonly key: DescriptorBinding;
2582
2599
  /** The non-key field binding; optional (a `remove` writes no fields). */
2583
2600
  readonly input?: DescriptorBinding;
2601
+ /**
2602
+ * Optional atomic-**`ADD`** binding on the self row (issue #301) — `{ field: $.field }`
2603
+ * whose leaves are `$.field` input refs bound from `param.number()` caller deltas.
2604
+ * Distinct from `input` (a `SET`): an `add` field lowers to a `TransactionValueLeaf`
2605
+ * on the compiled command spec's `add` record (an `ADD #field :delta`), NOT to a
2606
+ * `changes` SET. Legal only on an `update` fragment. When present alongside a
2607
+ * `condition`, the runtime applies the ADD unconditionally (two-stage: ADD+SET under
2608
+ * the condition, fallback ADD-only on ConditionalCheckFailed).
2609
+ */
2610
+ readonly add?: DescriptorBinding;
2584
2611
  /**
2585
2612
  * Optional declarative write gate — the #114-A condition subset (issue #242,
2586
2613
  * Phase 2). Compiled, serialized, and evaluated at runtime; see the interface doc.
@@ -3038,6 +3065,13 @@ interface ContractMethodOp<Op extends OperationKind = OperationKind> {
3038
3065
  readonly select?: Readonly<Record<string, unknown>>;
3039
3066
  /** The changes structure for `update`; `undefined` otherwise. */
3040
3067
  readonly changes?: Readonly<Record<string, unknown>>;
3068
+ /**
3069
+ * The atomic-`ADD` structure for `update` (issue #301): model field name → a
3070
+ * `param.number()` caller-delta leaf (a `{token}` template). Distinct from
3071
+ * {@link changes} (a SET): each entry lowers to `ADD #field :delta`. Present only on
3072
+ * a standalone `update` command that declared `add`; `undefined` otherwise.
3073
+ */
3074
+ readonly add?: Readonly<Record<string, unknown>>;
3041
3075
  /** The item structure for `put`; `undefined` otherwise. */
3042
3076
  readonly item?: Readonly<Record<string, unknown>>;
3043
3077
  /** The optional declarative write condition for writes. */
@@ -3679,6 +3713,25 @@ interface PublicWriteDescriptor {
3679
3713
  readonly upsert?: ModelRef;
3680
3714
  readonly key: Readonly<Record<string, unknown>>;
3681
3715
  readonly input?: Readonly<Record<string, unknown>>;
3716
+ /**
3717
+ * Atomic **`ADD`** deltas on the SELF row (issue #301) — `{ field: param.number() }`
3718
+ * declaring an `ADD #field :delta` with a **caller-param** delta. Distinct from
3719
+ * `input` (a `SET` that overwrites): `add` is an atomic server-side increment that
3720
+ * needs no prior read and is concurrency-safe (the aggregate-tree `mark`'s
3721
+ * `version++`). Legal only on an `update` intent, with each value a
3722
+ * `param.number()`. An `add` field may NOT also appear in `input` (a field is either
3723
+ * SET or ADD, not both) nor be a primary-key field (a key cannot be incremented).
3724
+ *
3725
+ * **Two-stage semantics**: when an `update` carries BOTH `add` and a `condition`, the
3726
+ * `add` is UNCONDITIONAL (it must always apply — the mark's `version++` must always
3727
+ * happen) while `input` (SET) is gated by `condition`. Because DynamoDB rejects the
3728
+ * ENTIRE `UpdateItem` when the `ConditionExpression` fails, the RUNTIME performs a
3729
+ * transparent two-stage: `ADD + SET` under the condition (1 write, the common path);
3730
+ * on `ConditionalCheckFailedException`, a fallback `ADD`-only write (no SET, no
3731
+ * condition — the `version` still increments). read=0, retry=0. The author does NOT
3732
+ * write the fallback — it is derived from `add` + `condition` presence.
3733
+ */
3734
+ readonly add?: Readonly<Record<string, unknown>>;
3682
3735
  readonly condition?: Readonly<Record<string, unknown>> | RawCondition<any, any>;
3683
3736
  readonly result?: {
3684
3737
  readonly select?: Readonly<Record<string, boolean>>;
@@ -1,8 +1,8 @@
1
- import { L as Linter, a as LintRule } from '../linter-Cr1Hnw7n.js';
2
- export { b as LintResult } from '../linter-Cr1Hnw7n.js';
3
- import '../types-BnFsTZwl.js';
4
- import '../key-Dne-a20Q.js';
5
- import '../types-CfxzTEFL.js';
1
+ import { L as Linter, a as LintRule } from '../linter-jEwmZotm.js';
2
+ export { b as LintResult } from '../linter-jEwmZotm.js';
3
+ import '../types-nk5okD7d.js';
4
+ import '../key-CWytoEaE.js';
5
+ import '../types-CgXS-4Ox.js';
6
6
  import 'behavior-contracts';
7
7
 
8
8
  /**
@@ -1,4 +1,4 @@
1
- import { E as EntityMetadata } from './types-BnFsTZwl.js';
1
+ import { E as EntityMetadata } from './types-nk5okD7d.js';
2
2
 
3
3
  declare class MetadataRegistry {
4
4
  private static store;
@@ -1,6 +1,6 @@
1
- import { E as EntityMetadata } from './types-BnFsTZwl.js';
2
- import { C as ContractSpec, Q as QuerySpec, a as CommandSpec, T as TransactionSpec, b as ContextSpec, S as SPEC_VERSION_KEY_EXPR, P as ParamSpec } from './types-CfxzTEFL.js';
3
- import { Q as QueryModelContract, k as QueryMethodSpec, l as CommandModelContract, m as CommandMethodSpec } from './key-Dne-a20Q.js';
1
+ import { E as EntityMetadata } from './types-nk5okD7d.js';
2
+ import { C as ContractSpec, Q as QuerySpec, a as CommandSpec, T as TransactionSpec, b as ContextSpec, S as SPEC_VERSION_KEY_EXPR, P as ParamSpec } from './types-CgXS-4Ox.js';
3
+ import { Q as QueryModelContract, k as QueryMethodSpec, l as CommandModelContract, m as CommandMethodSpec } from './key-CWytoEaE.js';
4
4
 
5
5
  /**
6
6
  * Contract-layer serialization (issue #59, CQRS Contract layer, Epic #57;
@@ -1,14 +1,14 @@
1
- import { $ as AnyOperationDefinition, a0 as TransactionDefinition, b as PreparedBody } from '../key-Dne-a20Q.js';
2
- export { a1 as CompiledFragment, a2 as CompiledMutationPlan, a3 as DerivedConditionCheck, a4 as DerivedEdgeWrite, a5 as DerivedIdempotencyGuard, a6 as DerivedMaintainOutbox, a7 as DerivedMaintainWrite, a8 as DerivedOutboxEvent, a9 as DerivedUniqueGuard, aa as DerivedUpdate, ab as EntityRefResolver, ac as MAX_TRANSACT_COMPOSE_ITEMS, ad as assertNoCrossFragmentMaintainCollision, ae as compileFragment, af as compileMutationPlan, ag as compileSingleFragmentPlan, ah as resetMaintenanceGraphCache, ai as resolveLifecycle, aj as resolveMaintainers } from '../key-Dne-a20Q.js';
3
- import { M as MetadataRegistry } from '../linter-Cr1Hnw7n.js';
1
+ import { $ as AnyOperationDefinition, a0 as TransactionDefinition, b as PreparedBody } from '../key-CWytoEaE.js';
2
+ export { a1 as CompiledFragment, a2 as CompiledMutationPlan, a3 as DerivedConditionCheck, a4 as DerivedEdgeWrite, a5 as DerivedIdempotencyGuard, a6 as DerivedMaintainOutbox, a7 as DerivedMaintainWrite, a8 as DerivedOutboxEvent, a9 as DerivedUniqueGuard, aa as DerivedUpdate, ab as EntityRefResolver, ac as MAX_TRANSACT_COMPOSE_ITEMS, ad as assertNoCrossFragmentMaintainCollision, ae as compileFragment, af as compileMutationPlan, ag as compileSingleFragmentPlan, ah as resetMaintenanceGraphCache, ai as resolveLifecycle, aj as resolveMaintainers } from '../key-CWytoEaE.js';
3
+ import { M as MetadataRegistry } from '../linter-jEwmZotm.js';
4
4
  import { B as BehaviorModelContract } from '../behaviors-DDltNivc.js';
5
5
  export { a as BehaviorMethodSpec } from '../behaviors-DDltNivc.js';
6
- import { C as ContractMap, e as ContextOwnershipMap, c as PreparedPlanSpec, a as PreparedPlanDocument } from '../prepared-artifact-BXkARxwO.js';
7
- export { A as AnyModelContract, B as BuiltContracts, f as ContextOwnership, g as PREPARED_FORMAT_VERSION, h as PreparedBindMap, b as PreparedBindSpec, d as PreparedReadRouteSpec, P as PreparedWriteOpSpec, i as buildContexts, j as buildContracts, k as canonicalJson, l as entityFingerprint, p as planFingerprint } from '../prepared-artifact-BXkARxwO.js';
8
- import { O as OperationsDocument, Q as QuerySpec, M as Manifest, T as TransactionSpec, C as ContractSpec, B as BridgeBundle, c as ConditionSpec, E as ExpressionSpec, a as CommandSpec, d as SpecVersion } from '../types-CfxzTEFL.js';
9
- export { e as CommandContractMethodSpec, f as CommandResolutionTarget, g as ComposeSpec, h as CompositionPlanSpec, b as ContextSpec, i as ContractCardinality, j as ContractCommandResult, k as ContractInputArity, l as ContractKeySpec, m as ContractKind, n as ContractResolution, o as EXPR_VERSION, p as ExecutionPlanSpec, q as ExpressionArrNode, r as ExpressionFloatNode, s as ExpressionIntNode, t as ExpressionNode, u as ExpressionObjNode, v as ExpressionOpNode, w as ExpressionOperator, x as ExpressionRefNode, y as ExpressionRefOptNode, z as ExpressionScalar, F as FilterSpec, A as ManifestEntity, D as ManifestField, G as ManifestFieldType, H as ManifestGsi, I as ManifestKey, J as ManifestRelation, K as ManifestTable, L as OperationSpec, P as ParamSpec, N as QueryContractMethodSpec, R as RangeConditionSpec, U as ReadOperationType, V as SPEC_VERSION, S as SPEC_VERSION_KEY_EXPR, W as SPEC_VERSION_SCP, X as SPEC_VERSION_SUPPORTED, Y as TransactionItemSpec, Z as TransactionItemType, _ as WhenSpec, $ as WriteOperationType } from '../types-CfxzTEFL.js';
6
+ import { C as ContractMap, e as ContextOwnershipMap, c as PreparedPlanSpec, a as PreparedPlanDocument } from '../prepared-artifact-CwH5ezFq.js';
7
+ export { A as AnyModelContract, B as BuiltContracts, f as ContextOwnership, g as PREPARED_FORMAT_VERSION, h as PreparedBindMap, b as PreparedBindSpec, d as PreparedReadRouteSpec, P as PreparedWriteOpSpec, i as buildContexts, j as buildContracts, k as canonicalJson, l as entityFingerprint, p as planFingerprint } from '../prepared-artifact-CwH5ezFq.js';
8
+ import { O as OperationsDocument, Q as QuerySpec, M as Manifest, T as TransactionSpec, C as ContractSpec, B as BridgeBundle, c as ConditionSpec, E as ExpressionSpec, a as CommandSpec, d as SpecVersion } from '../types-CgXS-4Ox.js';
9
+ export { e as CommandContractMethodSpec, f as CommandResolutionTarget, g as ComposeSpec, h as CompositionPlanSpec, b as ContextSpec, i as ContractCardinality, j as ContractCommandResult, k as ContractInputArity, l as ContractKeySpec, m as ContractKind, n as ContractResolution, o as EXPR_VERSION, p as ExecutionPlanSpec, q as ExpressionArrNode, r as ExpressionFloatNode, s as ExpressionIntNode, t as ExpressionNode, u as ExpressionObjNode, v as ExpressionOpNode, w as ExpressionOperator, x as ExpressionRefNode, y as ExpressionRefOptNode, z as ExpressionScalar, F as FilterSpec, A as ManifestEntity, D as ManifestField, G as ManifestFieldType, H as ManifestGsi, I as ManifestKey, J as ManifestRelation, K as ManifestTable, L as OperationSpec, P as ParamSpec, N as QueryContractMethodSpec, R as RangeConditionSpec, U as ReadOperationType, V as SPEC_VERSION, S as SPEC_VERSION_KEY_EXPR, W as SPEC_VERSION_SCP, X as SPEC_VERSION_SUPPORTED, Y as TransactionItemSpec, Z as TransactionItemType, _ as WhenSpec, $ as WriteOperationType } from '../types-CgXS-4Ox.js';
10
10
  import { Component, ComponentGraphIR } from 'behavior-contracts';
11
- import '../types-BnFsTZwl.js';
11
+ import '../types-nk5okD7d.js';
12
12
 
13
13
  /**
14
14
  * Static parameterized operation-spec generation (issue #42, Phase 0b).
@@ -14,14 +14,14 @@ import {
14
14
  collectContractN1Violations,
15
15
  compilePreparedPlan,
16
16
  portableIrDocument
17
- } from "../chunk-SSVG6NQK.js";
17
+ } from "../chunk-MBJ4JVRM.js";
18
18
  import {
19
19
  PREPARED_FORMAT_VERSION,
20
20
  buildManifest,
21
21
  canonicalJson,
22
22
  entityFingerprint,
23
23
  planFingerprint
24
- } from "../chunk-NWTEUWJD.js";
24
+ } from "../chunk-PHXUFAY2.js";
25
25
  import {
26
26
  MAX_TRANSACT_COMPOSE_ITEMS,
27
27
  assertBundleSerializable,
@@ -36,8 +36,8 @@ import {
36
36
  resetMaintenanceGraphCache,
37
37
  resolveLifecycle,
38
38
  resolveMaintainers
39
- } from "../chunk-XZTA4VJH.js";
40
- import "../chunk-KOIJ4SNO.js";
39
+ } from "../chunk-HLFNCKFV.js";
40
+ import "../chunk-7OCXY4R6.js";
41
41
  import {
42
42
  EXPR_VERSION,
43
43
  SPEC_VERSION,
@@ -47,7 +47,7 @@ import {
47
47
  canonicalizeExpressionSpec,
48
48
  operationsSpecVersion
49
49
  } from "../chunk-WOFRHRXY.js";
50
- import "../chunk-ZNU7OI5I.js";
50
+ import "../chunk-GWWRXIHF.js";
51
51
  import "../chunk-XTWXMOHD.js";
52
52
  export {
53
53
  EXPR_VERSION,
@@ -1,5 +1,5 @@
1
- import { n as Executor, o as DynamoDBOperation, R as ReadExecOptions, p as ExecutorResult, B as BatchGetExecInput, q as PutInput, W as WriteExecOptions, r as WriteResult, U as UpdateInput, s as DeleteInput, t as BatchWriteExecItem, u as BatchExecOptions, T as TransactWriteExecItem, M as ModelStatic, D as DDBModel, v as ChangeEvent } from '../key-Dne-a20Q.js';
2
- import '../types-CfxzTEFL.js';
1
+ import { n as Executor, o as DynamoDBOperation, R as ReadExecOptions, p as ExecutorResult, B as BatchGetExecInput, q as PutInput, W as WriteExecOptions, r as WriteResult, U as UpdateInput, s as DeleteInput, t as BatchWriteExecItem, u as BatchExecOptions, T as TransactWriteExecItem, M as ModelStatic, D as DDBModel, v as ChangeEvent } from '../key-CWytoEaE.js';
2
+ import '../types-CgXS-4Ox.js';
3
3
  import 'behavior-contracts';
4
4
 
5
5
  /**
@@ -1,12 +1,12 @@
1
1
  import {
2
2
  createCdcEmulator
3
- } from "../chunk-YGQ2NO6J.js";
4
- import "../chunk-KOIJ4SNO.js";
3
+ } from "../chunk-VECUS35D.js";
4
+ import "../chunk-7OCXY4R6.js";
5
5
  import {
6
6
  ClientManager,
7
7
  MetadataRegistry,
8
8
  resolveModelClass
9
- } from "../chunk-ZNU7OI5I.js";
9
+ } from "../chunk-GWWRXIHF.js";
10
10
  import {
11
11
  TableMapping
12
12
  } from "../chunk-XTWXMOHD.js";
@@ -470,8 +470,9 @@ function removePath(target, segments) {
470
470
  delete cursor[segments[segments.length - 1]];
471
471
  }
472
472
  function applyUpdateExpression(item, expression, clauses) {
473
- const setMatch = /SET\s+(.*?)(?:\s+REMOVE\s+|$)/.exec(expression);
474
- const removeMatch = /REMOVE\s+(.*)$/.exec(expression);
473
+ const setMatch = /SET\s+(.*?)(?:\s+REMOVE\s+|\s+ADD\s+|$)/.exec(expression);
474
+ const removeMatch = /REMOVE\s+(.*?)(?:\s+ADD\s+|$)/.exec(expression);
475
+ const addMatch = /ADD\s+(.*)$/.exec(expression);
475
476
  if (setMatch && setMatch[1].trim().length > 0) {
476
477
  const assignments = splitTopLevel(setMatch[1]);
477
478
  for (const assignment of assignments) {
@@ -496,6 +497,32 @@ function applyUpdateExpression(item, expression, clauses) {
496
497
  removePath(item, resolvePath(trimmed, clauses.names));
497
498
  }
498
499
  }
500
+ if (addMatch && addMatch[1].trim().length > 0) {
501
+ const clausesList = splitTopLevel(addMatch[1]);
502
+ for (const clause of clausesList) {
503
+ const trimmed = clause.trim();
504
+ if (trimmed.length === 0) continue;
505
+ const sp = trimmed.indexOf(" ");
506
+ if (sp < 0) {
507
+ throw new Error(`memory update: malformed ADD clause '${trimmed}'`);
508
+ }
509
+ const pathExpr = trimmed.slice(0, sp).trim();
510
+ const valueAlias = trimmed.slice(sp + 1).trim();
511
+ if (!(valueAlias in clauses.values)) {
512
+ throw new Error(`memory update: unbound value alias '${valueAlias}'`);
513
+ }
514
+ const delta = clauses.values[valueAlias];
515
+ if (typeof delta !== "number") {
516
+ throw new Error(
517
+ `memory update: ADD delta for '${pathExpr}' is not numeric (${typeof delta})`
518
+ );
519
+ }
520
+ const segments = resolvePath(pathExpr, clauses.names);
521
+ const current = segments.length === 1 ? item[segments[0]] : void 0;
522
+ const base = typeof current === "number" ? current : 0;
523
+ setPath(item, segments, base + delta);
524
+ }
525
+ }
499
526
  }
500
527
  function splitTopLevel(clause) {
501
528
  const parts = [];
@@ -1,5 +1,5 @@
1
1
  import ts from 'typescript';
2
- import { E as ExpressionSpec } from '../types-CfxzTEFL.js';
2
+ import { E as ExpressionSpec } from '../types-CgXS-4Ox.js';
3
3
  import 'behavior-contracts';
4
4
 
5
5
  /**
@@ -3,7 +3,7 @@ import {
3
3
  SCP_LOWERED_MARKER,
4
4
  canonicalizeExpressionSpec
5
5
  } from "../chunk-WOFRHRXY.js";
6
- import "../chunk-ZNU7OI5I.js";
6
+ import "../chunk-GWWRXIHF.js";
7
7
  import "../chunk-XTWXMOHD.js";
8
8
 
9
9
  // src/transform/prepared-transform.ts
@@ -721,7 +721,9 @@ type WriteOperationType = 'PutItem' | 'UpdateItem' | 'DeleteItem';
721
721
  * - `{ kind: 'attributeNotExists'; field }` → `attribute_not_exists(<field>)` — the
722
722
  * named attribute must be absent (field-level uniqueness / first-write guard).
723
723
  * - `{ kind: 'equals'; fields }` → `#f = :v AND …` field equality (each value a
724
- * `{param}` / literal template).
724
+ * `{param}` string template or a typed literal — a `number` / `boolean` literal
725
+ * is carried verbatim so it compares as `N` / `BOOL`, not a stringified `S`,
726
+ * issue #299).
725
727
  * - `{ kind: 'expr'; declarative }` → the full declarative operator tree (issue
726
728
  * #114-A): comparison (`gt`/`ge`/`lt`/`le`/`ne`), `between`, `in`,
727
729
  * `begins_with`/`contains`/`notContains`, `attributeType`, and the logical
@@ -743,7 +745,7 @@ type ConditionSpec = {
743
745
  readonly field: string;
744
746
  } | {
745
747
  readonly kind: 'equals';
746
- readonly fields: Readonly<Record<string, string>>;
748
+ readonly fields: Readonly<Record<string, TransactionValueLeaf>>;
747
749
  } | {
748
750
  readonly kind: 'expr';
749
751
  readonly declarative: unknown;
@@ -792,10 +794,33 @@ interface CommandSpec {
792
794
  * Absent for `PutItem` (the whole item is built from `item`).
793
795
  */
794
796
  readonly keyCondition?: Readonly<Record<string, string>>;
795
- /** The full item template for `PutItem` (field name → template / literal). */
796
- readonly item?: Readonly<Record<string, string>>;
797
- /** Field-level changes for `UpdateItem` (field name template / literal). */
798
- readonly changes?: Readonly<Record<string, string>>;
797
+ /**
798
+ * The full item template for `PutItem` (field name → template / typed literal).
799
+ * A string value is a template ({@link TransactionValueLeaf}); a `number` /
800
+ * `boolean` is a typed literal carried verbatim so it lands as `N` / `BOOL`
801
+ * rather than a stringified `S` (issue #299, mirroring the transaction #245).
802
+ */
803
+ readonly item?: Readonly<Record<string, TransactionValueLeaf>>;
804
+ /**
805
+ * Field-level changes for `UpdateItem` (field name → template / typed literal).
806
+ * A string value is a template; a `number` / `boolean` is a typed literal
807
+ * carried verbatim (issue #299).
808
+ */
809
+ readonly changes?: Readonly<Record<string, TransactionValueLeaf>>;
810
+ /**
811
+ * Atomic-`ADD` deltas for a standalone `UpdateItem` (issue #301): field name →
812
+ * template / typed literal. Distinct from {@link changes} (a SET): each entry emits
813
+ * `ADD #field :delta` — an atomic, read-free, concurrency-safe increment (the
814
+ * aggregate-tree `mark`'s `version++`). The delta rides as a `{token}` template
815
+ * resolved from a caller `param.number()`. When present together with
816
+ * {@link condition}, the ADD is UNCONDITIONAL and the runtime executes a transparent
817
+ * TWO-STAGE write: `ADD + SET` under the condition (1 write); on
818
+ * `ConditionalCheckFailedException`, a fallback `ADD`-only write (no SET, no
819
+ * condition — the counter still increments). read=0, retry=0. Absent unless the
820
+ * command declared `add`, so a command with no `add` serializes byte-identically to
821
+ * the pre-#301 spec.
822
+ */
823
+ readonly add?: Readonly<Record<string, TransactionValueLeaf>>;
799
824
  /** Optional write condition (subset). */
800
825
  readonly condition?: ConditionSpec;
801
826
  /**
@@ -1023,8 +1048,12 @@ interface MaintainItemSpec {
1023
1048
  interface MaintainCounterSpec {
1024
1049
  /** The target attribute the counter increments (e.g. `postCount`). */
1025
1050
  readonly attribute: string;
1026
- /** The signed `ADD` delta as a numeric template string (`"1"` / `"-1"`). */
1027
- readonly delta: string;
1051
+ /**
1052
+ * The signed `ADD` delta. A compile-time constant delta is carried as a typed
1053
+ * `number` (`1` / `-1`) so it marshals as `N` (issue #299); a `{param}` numeric
1054
+ * template rides as a string. (Pre-#299 a constant was stringified to `"1"`.)
1055
+ */
1056
+ readonly delta: TransactionValueLeaf;
1028
1057
  }
1029
1058
  /** A declarative transaction definition's full execution spec. */
1030
1059
  interface TransactionSpec {
@@ -1,4 +1,4 @@
1
- import { d as SegmentedKey, e as ProjectionTransform, f as MaintainEvent, g as ProjectionMap, h as MaintainConsistency, i as MaintainUpdateMode, E as EffectPath, j as MembershipPredicate } from './key-Dne-a20Q.js';
1
+ import { d as SegmentedKey, e as ProjectionTransform, f as MaintainEvent, g as ProjectionMap, h as MaintainConsistency, i as MaintainUpdateMode, E as EffectPath, j as MembershipPredicate } from './key-CWytoEaE.js';
2
2
 
3
3
  type DynamoType = 'S' | 'N' | 'BOOL' | 'B' | 'SS' | 'NS' | 'L' | 'M';
4
4
  interface FieldOptions {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "graphddb",
3
- "version": "0.9.0",
3
+ "version": "0.9.1",
4
4
  "description": "Graph data modeling on DynamoDB with adjacency list pattern",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",