dynamodb-expression-builder 0.1.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -154,6 +154,24 @@ const FILTER_OPERATORS = [
154
154
  "L"
155
155
  ]
156
156
  },
157
+ {
158
+ value: "not_contains",
159
+ label: "Not Contains",
160
+ symbol: "∌",
161
+ wireForm: "NOT_CONTAINS",
162
+ requiresValue: true,
163
+ requiresValue2: false,
164
+ typeOptional: false,
165
+ keyAllowedTypes: [],
166
+ scanAllowedTypes: [
167
+ "S",
168
+ "B",
169
+ "SS",
170
+ "NS",
171
+ "BS",
172
+ "L"
173
+ ]
174
+ },
157
175
  {
158
176
  value: "begins_with",
159
177
  label: "Begins With",
@@ -370,6 +388,9 @@ function buildOne(row, index, prefix, names, typedValues) {
370
388
  case "CONTAINS":
371
389
  typedValues[valueRef] = makeTypedValue(elementType(row.type), row.value);
372
390
  return `contains(${nameRef}, ${valueRef})`;
391
+ case "NOT_CONTAINS":
392
+ typedValues[valueRef] = makeTypedValue(elementType(row.type), row.value);
393
+ return `NOT contains(${nameRef}, ${valueRef})`;
373
394
  case "BEGINS_WITH":
374
395
  typedValues[valueRef] = single$1(row);
375
396
  return `begins_with(${nameRef}, ${valueRef})`;
@@ -726,7 +747,7 @@ function hasBinaryValue(request) {
726
747
  //#endregion
727
748
  //#region src/emit/sdk-v3.ts
728
749
  /** Operation → the `@aws-sdk/client-dynamodb` command class name. */
729
- const COMMAND_BY_OP = {
750
+ const COMMAND_BY_OP$1 = {
730
751
  GetItem: "GetItemCommand",
731
752
  Query: "QueryCommand",
732
753
  Scan: "ScanCommand",
@@ -784,11 +805,11 @@ function jsLiteral(value, indent) {
784
805
  }
785
806
  /** Emit the `new <Op>Command({...})` source snippet for a canonical request. */
786
807
  function emitSdkV3(request) {
787
- return `new ${COMMAND_BY_OP[request.operation]}(${jsLiteral(buildSdkV3Params(request), 0)})`;
808
+ return `new ${COMMAND_BY_OP$1[request.operation]}(${jsLiteral(buildSdkV3Params(request), 0)})`;
788
809
  }
789
810
  /** The `@aws-sdk/client-dynamodb` command class name for an operation. */
790
811
  function sdkV3CommandName(operation) {
791
- return COMMAND_BY_OP[operation];
812
+ return COMMAND_BY_OP$1[operation];
792
813
  }
793
814
  /**
794
815
  * Render an arbitrary params value as the same pretty-printed JS literal
@@ -967,6 +988,7 @@ function predicate(row) {
967
988
  case "BETWEEN": return `${id} BETWEEN ${literal(makeTypedValue(row.type, row.value))} AND ${literal(makeTypedValue(row.type, row.value2 ?? ""))}`;
968
989
  case "BEGINS_WITH": return `begins_with(${id}, ${literal(single(row))})`;
969
990
  case "CONTAINS": return `contains(${id}, ${literal(makeTypedValue(elementType(row.type), row.value))})`;
991
+ case "NOT_CONTAINS": return `NOT contains(${id}, ${literal(makeTypedValue(elementType(row.type), row.value))})`;
970
992
  case "IN": return `${id} IN (${(row.values ?? (row.value ? [row.value] : [])).map((m) => literal(makeTypedValue(row.type, m))).join(", ")})`;
971
993
  case "EXISTS": return `${id} IS NOT MISSING`;
972
994
  case "NOT_EXISTS": return `${id} IS MISSING`;
@@ -1095,7 +1117,7 @@ const REQUEST_CLASS_BY_OP$1 = {
1095
1117
  Delete: "DeleteItemRequest"
1096
1118
  };
1097
1119
  /** Operation → the `DynamoDbClient` method. */
1098
- const CLIENT_METHOD_BY_OP$2 = {
1120
+ const CLIENT_METHOD_BY_OP$6 = {
1099
1121
  GetItem: "getItem",
1100
1122
  Query: "query",
1101
1123
  Scan: "scan",
@@ -1109,7 +1131,7 @@ function javaRequestClassName(operation) {
1109
1131
  }
1110
1132
  /** The `DynamoDbClient` method name for an operation (`query`, …). */
1111
1133
  function javaClientMethodName(operation) {
1112
- return CLIENT_METHOD_BY_OP$2[operation];
1134
+ return CLIENT_METHOD_BY_OP$6[operation];
1113
1135
  }
1114
1136
  /** Java string literal — `JSON.stringify` escapes are all valid Java escapes. */
1115
1137
  function javaString(value) {
@@ -1188,7 +1210,7 @@ const INPUT_TYPE_BY_OP = {
1188
1210
  Delete: "DeleteItemInput"
1189
1211
  };
1190
1212
  /** Operation → the `dynamodb.Client` method. */
1191
- const CLIENT_METHOD_BY_OP$1 = {
1213
+ const CLIENT_METHOD_BY_OP$5 = {
1192
1214
  GetItem: "GetItem",
1193
1215
  Query: "Query",
1194
1216
  Scan: "Scan",
@@ -1202,7 +1224,7 @@ function goInputTypeName(operation) {
1202
1224
  }
1203
1225
  /** The `dynamodb.Client` method name for an operation. */
1204
1226
  function goClientMethodName(operation) {
1205
- return CLIENT_METHOD_BY_OP$1[operation];
1227
+ return CLIENT_METHOD_BY_OP$5[operation];
1206
1228
  }
1207
1229
  /** Go string literal — `JSON.stringify` escapes are all valid Go escapes. */
1208
1230
  function goString(value) {
@@ -1292,7 +1314,7 @@ const REQUEST_CLASS_BY_OP = {
1292
1314
  Delete: "DeleteItemRequest"
1293
1315
  };
1294
1316
  /** Operation → the async `AmazonDynamoDBClient` method. */
1295
- const CLIENT_METHOD_BY_OP = {
1317
+ const CLIENT_METHOD_BY_OP$4 = {
1296
1318
  GetItem: "GetItemAsync",
1297
1319
  Query: "QueryAsync",
1298
1320
  Scan: "ScanAsync",
@@ -1306,7 +1328,7 @@ function dotnetRequestClassName(operation) {
1306
1328
  }
1307
1329
  /** The async client method name for an operation (`QueryAsync`, …). */
1308
1330
  function dotnetClientMethodName(operation) {
1309
- return CLIENT_METHOD_BY_OP[operation];
1331
+ return CLIENT_METHOD_BY_OP$4[operation];
1310
1332
  }
1311
1333
  /** C# string literal — `JSON.stringify` escapes are all valid C# escapes. */
1312
1334
  function csString(value) {
@@ -1373,6 +1395,102 @@ function emitDotnet(request) {
1373
1395
  return renderCsRequest(request, "");
1374
1396
  }
1375
1397
 
1398
+ //#endregion
1399
+ //#region src/emit/rust.ts
1400
+ /** Operation → the `aws_sdk_dynamodb::Client` fluent method. */
1401
+ const CLIENT_METHOD_BY_OP$3 = {
1402
+ GetItem: "get_item",
1403
+ Query: "query",
1404
+ Scan: "scan",
1405
+ Update: "update_item",
1406
+ Put: "put_item",
1407
+ Delete: "delete_item"
1408
+ };
1409
+ /** The `Client` method name (snake_case) for an operation. */
1410
+ function rustClientMethodName(operation) {
1411
+ return CLIENT_METHOD_BY_OP$3[operation];
1412
+ }
1413
+ /** Rust string literal — JSON escapes with the three Rust-incompatible ones fixed. */
1414
+ function rustString(value) {
1415
+ return JSON.stringify(value).replace(/\\b/g, "\\u{0008}").replace(/\\f/g, "\\u{000c}").replace(/\\u([0-9a-fA-F]{4})/g, "\\u{$1}");
1416
+ }
1417
+ /** `"…".to_string()` — the owned String the AttributeValue constructors take. */
1418
+ function rustOwned(value) {
1419
+ return `${rustString(value)}.to_string()`;
1420
+ }
1421
+ /** Decode canonical base64 into a `Blob::new(vec![0x…])` (fail-loud on bad input). */
1422
+ function rustBlob(base64) {
1423
+ let raw;
1424
+ try {
1425
+ raw = atob(base64);
1426
+ } catch {
1427
+ throw new Error(`invalid base64 in a binary (B/BS) value: ${base64}`);
1428
+ }
1429
+ return `Blob::new(vec![${Array.from(raw, (c) => `0x${c.charCodeAt(0).toString(16).padStart(2, "0")}`).join(", ")}])`;
1430
+ }
1431
+ /** Render one wire AttributeValue as an `AttributeValue::…` constructor. */
1432
+ function renderRustAv(av) {
1433
+ if ("S" in av) return `AttributeValue::S(${rustOwned(av.S)})`;
1434
+ if ("N" in av) return `AttributeValue::N(${rustOwned(av.N)})`;
1435
+ if ("B" in av) return `AttributeValue::B(${rustBlob(av.B)})`;
1436
+ if ("BOOL" in av) return `AttributeValue::Bool(${av.BOOL})`;
1437
+ if ("SS" in av) return `AttributeValue::Ss(vec![${av.SS.map(rustOwned).join(", ")}])`;
1438
+ if ("NS" in av) return `AttributeValue::Ns(vec![${av.NS.map(rustOwned).join(", ")}])`;
1439
+ if ("BS" in av) return `AttributeValue::Bs(vec![${av.BS.map(rustBlob).join(", ")}])`;
1440
+ return "AttributeValue::Null(true)";
1441
+ }
1442
+ /** One `.method(key, av)` line per entry of a typed map. */
1443
+ function avEntryLines(method, map, indent) {
1444
+ return Object.entries(typedMapToAvMap(map)).map(([name, av]) => `${indent}.${method}(${rustString(name)}, ${renderRustAv(av)})`);
1445
+ }
1446
+ /**
1447
+ * Render the fluent builder chain for a request — every line at `indent`,
1448
+ * starting with `.{op}()` and ending BEFORE `.send()` so callers own the
1449
+ * terminal (the bare emitter awaits inline; the program emitter may hand the
1450
+ * chain to `into_paginator()` instead). Exported for the program emitter.
1451
+ */
1452
+ function renderRustBuilder(request, indent) {
1453
+ const lines = [`${indent}.${CLIENT_METHOD_BY_OP$3[request.operation]}()`];
1454
+ lines.push(`${indent}.table_name(${rustString(request.tableName)})`);
1455
+ if (request.indexName) lines.push(`${indent}.index_name(${rustString(request.indexName)})`);
1456
+ if (request.key) lines.push(...avEntryLines("key", request.key, indent));
1457
+ if (request.item) lines.push(...avEntryLines("item", request.item, indent));
1458
+ if (request.keyConditionExpression) lines.push(`${indent}.key_condition_expression(${rustString(request.keyConditionExpression)})`);
1459
+ if (request.updateExpression) lines.push(`${indent}.update_expression(${rustString(request.updateExpression)})`);
1460
+ if (request.conditionExpression) lines.push(`${indent}.condition_expression(${rustString(request.conditionExpression)})`);
1461
+ if (request.filterExpression) lines.push(`${indent}.filter_expression(${rustString(request.filterExpression)})`);
1462
+ if (request.projectionExpression) lines.push(`${indent}.projection_expression(${rustString(request.projectionExpression)})`);
1463
+ if (request.names) lines.push(...Object.entries(request.names).map(([alias, name]) => `${indent}.expression_attribute_names(${rustString(alias)}, ${rustString(name)})`));
1464
+ if (request.typedValues) lines.push(...avEntryLines("expression_attribute_values", request.typedValues, indent));
1465
+ if (request.limit !== void 0) lines.push(`${indent}.limit(${request.limit})`);
1466
+ if (request.consistentRead) lines.push(`${indent}.consistent_read(true)`);
1467
+ if (request.scanIndexForward === false) lines.push(`${indent}.scan_index_forward(false)`);
1468
+ if (request.exclusiveStartKey) lines.push(...avEntryLines("exclusive_start_key", request.exclusiveStartKey, indent));
1469
+ return lines;
1470
+ }
1471
+ /** Does this request build any `AttributeValue`? Drives the program emitter's imports. */
1472
+ function rustUsesAttributeValue(request) {
1473
+ return request.key !== void 0 || request.item !== void 0 || request.typedValues !== void 0 || request.exclusiveStartKey !== void 0;
1474
+ }
1475
+ /** Does this request build any binary Blob? Drives the `primitives::Blob` import. */
1476
+ function rustUsesBlob(request) {
1477
+ return [
1478
+ request.key,
1479
+ request.item,
1480
+ request.typedValues,
1481
+ request.exclusiveStartKey
1482
+ ].some((map) => map && Object.values(typedMapToAvMap(map)).some((av) => "B" in av || "BS" in av));
1483
+ }
1484
+ /** Emit the bare fluent call for a canonical request (awaited, `?`-propagated). */
1485
+ function emitRust(request) {
1486
+ return [
1487
+ "let response = client",
1488
+ ...renderRustBuilder(request, " "),
1489
+ " .send()",
1490
+ " .await?;"
1491
+ ].join("\n");
1492
+ }
1493
+
1376
1494
  //#endregion
1377
1495
  //#region src/emit/ddbtoolbox.ts
1378
1496
  /** Key attr DynamoDB type (S/N/B) → the Table `type` string dynamodb-toolbox wants. */
@@ -1438,6 +1556,7 @@ function renderCondition(f) {
1438
1556
  if (f.operator === "exists") return `{ attr: ${attr}, exists: true }`;
1439
1557
  if (f.operator === "not_exists") return `{ attr: ${attr}, exists: false }`;
1440
1558
  if (f.operator === "contains") return `{ attr: ${attr}, contains: ${val()} }`;
1559
+ if (f.operator === "not_contains") return `{ not: { attr: ${attr}, contains: ${val()} } }`;
1441
1560
  if (f.operator === "begins_with") return `{ attr: ${attr}, beginsWith: ${val()} }`;
1442
1561
  if (f.operator === "between") return `{ attr: ${attr}, between: [${renderNativeValue(scalar(f.type, f.value))}, ${renderNativeValue(scalar(f.type, f.value2 ?? ""))}] }`;
1443
1562
  if (f.operator === "in") return `{ attr: ${attr}, in: [${(f.values ?? []).map((v) => renderNativeValue(scalar(f.type, v))).join(", ")}] }`;
@@ -1583,6 +1702,292 @@ function emitDdbToolboxProgram(request, paginate) {
1583
1702
  ].filter((l) => l !== null).join("\n");
1584
1703
  }
1585
1704
 
1705
+ //#endregion
1706
+ //#region src/emit/docclient.ts
1707
+ /** Operation → the `@aws-sdk/lib-dynamodb` command class. */
1708
+ const COMMAND_BY_OP = {
1709
+ GetItem: "GetCommand",
1710
+ Query: "QueryCommand",
1711
+ Scan: "ScanCommand",
1712
+ Update: "UpdateCommand",
1713
+ Put: "PutCommand",
1714
+ Delete: "DeleteCommand"
1715
+ };
1716
+ /** The lib-dynamodb command class name for an operation. */
1717
+ function docClientCommandName(operation) {
1718
+ return COMMAND_BY_OP[operation];
1719
+ }
1720
+ /** Render a typed map as a native-value object literal, one entry per line. */
1721
+ function renderNativeMap(map, indent) {
1722
+ const inner = `${indent} `;
1723
+ return `{\n${Object.entries(map).map(([name, tv]) => `${inner}${JSON.stringify(name)}: ${renderNativeValue(tv)},`).join("\n")}\n${indent}}`;
1724
+ }
1725
+ /** Render the plain string map (`ExpressionAttributeNames`). */
1726
+ function renderNameMap$3(map, indent) {
1727
+ const inner = `${indent} `;
1728
+ return `{\n${Object.entries(map).map(([alias, name]) => `${inner}${JSON.stringify(alias)}: ${JSON.stringify(name)},`).join("\n")}\n${indent}}`;
1729
+ }
1730
+ /**
1731
+ * Render the command's params object literal, one field per line at
1732
+ * `indent + 2`. Exported for the program emitter.
1733
+ */
1734
+ function renderDocClientParams(request, indent) {
1735
+ const pad = `${indent} `;
1736
+ const fields = [`TableName: ${JSON.stringify(request.tableName)},`];
1737
+ if (request.indexName) fields.push(`IndexName: ${JSON.stringify(request.indexName)},`);
1738
+ if (request.key) fields.push(`Key: ${renderNativeMap(request.key, pad)},`);
1739
+ if (request.item) fields.push(`Item: ${renderNativeMap(request.item, pad)},`);
1740
+ if (request.keyConditionExpression) fields.push(`KeyConditionExpression: ${JSON.stringify(request.keyConditionExpression)},`);
1741
+ if (request.updateExpression) fields.push(`UpdateExpression: ${JSON.stringify(request.updateExpression)},`);
1742
+ if (request.conditionExpression) fields.push(`ConditionExpression: ${JSON.stringify(request.conditionExpression)},`);
1743
+ if (request.filterExpression) fields.push(`FilterExpression: ${JSON.stringify(request.filterExpression)},`);
1744
+ if (request.projectionExpression) fields.push(`ProjectionExpression: ${JSON.stringify(request.projectionExpression)},`);
1745
+ if (request.names) fields.push(`ExpressionAttributeNames: ${renderNameMap$3(request.names, pad)},`);
1746
+ if (request.typedValues) fields.push(`ExpressionAttributeValues: ${renderNativeMap(request.typedValues, pad)},`);
1747
+ if (request.limit !== void 0) fields.push(`Limit: ${request.limit},`);
1748
+ if (request.consistentRead) fields.push("ConsistentRead: true,");
1749
+ if (request.scanIndexForward === false) fields.push("ScanIndexForward: false,");
1750
+ if (request.exclusiveStartKey) fields.push(`ExclusiveStartKey: ${renderNativeMap(request.exclusiveStartKey, pad)},`);
1751
+ return [
1752
+ "{",
1753
+ ...fields.map((f) => `${pad}${f}`),
1754
+ `${indent}}`
1755
+ ].join("\n");
1756
+ }
1757
+ /** Emit the bare `new XCommand({…})` snippet with native values. */
1758
+ function emitDocClient(request) {
1759
+ return `new ${COMMAND_BY_OP[request.operation]}(${renderDocClientParams(request, "")})`;
1760
+ }
1761
+
1762
+ //#endregion
1763
+ //#region src/emit/kotlin.ts
1764
+ /** Operation → the `DynamoDbClient` suspend function. */
1765
+ const CLIENT_METHOD_BY_OP$2 = {
1766
+ GetItem: "getItem",
1767
+ Query: "query",
1768
+ Scan: "scan",
1769
+ Update: "updateItem",
1770
+ Put: "putItem",
1771
+ Delete: "deleteItem"
1772
+ };
1773
+ /** The client method name (camelCase) for an operation. */
1774
+ function kotlinClientMethodName(operation) {
1775
+ return CLIENT_METHOD_BY_OP$2[operation];
1776
+ }
1777
+ /** Kotlin string literal — JSON escapes are valid Kotlin escapes, plus `$` must
1778
+ * be escaped (string templates). */
1779
+ function ktString(value) {
1780
+ return JSON.stringify(value).replace(/\$/g, "\\$");
1781
+ }
1782
+ /** Decode canonical base64 into a `byteArrayOf(0x…)` literal (fail-loud). */
1783
+ function ktBytes(base64) {
1784
+ let raw;
1785
+ try {
1786
+ raw = atob(base64);
1787
+ } catch {
1788
+ throw new Error(`invalid base64 in a binary (B/BS) value: ${base64}`);
1789
+ }
1790
+ return `byteArrayOf(${Array.from(raw, (c) => `0x${c.charCodeAt(0).toString(16).padStart(2, "0")}`).join(", ")})`;
1791
+ }
1792
+ /** Render one wire AttributeValue as an `AttributeValue.…` constructor. */
1793
+ function renderKotlinAv(av) {
1794
+ if ("S" in av) return `AttributeValue.S(${ktString(av.S)})`;
1795
+ if ("N" in av) return `AttributeValue.N(${ktString(av.N)})`;
1796
+ if ("B" in av) return `AttributeValue.B(${ktBytes(av.B)})`;
1797
+ if ("BOOL" in av) return `AttributeValue.Bool(${av.BOOL})`;
1798
+ if ("SS" in av) return `AttributeValue.Ss(listOf(${av.SS.map(ktString).join(", ")}))`;
1799
+ if ("NS" in av) return `AttributeValue.Ns(listOf(${av.NS.map(ktString).join(", ")}))`;
1800
+ if ("BS" in av) return `AttributeValue.Bs(listOf(${av.BS.map(ktBytes).join(", ")}))`;
1801
+ return "AttributeValue.Null(true)";
1802
+ }
1803
+ /** Render a typed map as `mapOf("k" to AttributeValue.…)`, one entry per line. */
1804
+ function renderAvMap$1(map, indent) {
1805
+ const inner = `${indent} `;
1806
+ return `mapOf(\n${Object.entries(typedMapToAvMap(map)).map(([name, av]) => `${inner}${ktString(name)} to ${renderKotlinAv(av)},`).join("\n")}\n${indent})`;
1807
+ }
1808
+ /** Render the plain string map (`expressionAttributeNames`). */
1809
+ function renderNameMap$2(map, indent) {
1810
+ const inner = `${indent} `;
1811
+ return `mapOf(\n${Object.entries(map).map(([alias, name]) => `${inner}${ktString(alias)} to ${ktString(name)},`).join("\n")}\n${indent})`;
1812
+ }
1813
+ /**
1814
+ * Render the builder-lambda body — one property assignment per line at
1815
+ * `indent`. Exported for the program emitter, which wraps the same body in
1816
+ * either the plain suspend call or the `Paginated` flow variant.
1817
+ */
1818
+ function renderKotlinBuilder(request, indent) {
1819
+ const lines = [`${indent}tableName = ${ktString(request.tableName)}`];
1820
+ if (request.indexName) lines.push(`${indent}indexName = ${ktString(request.indexName)}`);
1821
+ if (request.key) lines.push(`${indent}key = ${renderAvMap$1(request.key, indent)}`);
1822
+ if (request.item) lines.push(`${indent}item = ${renderAvMap$1(request.item, indent)}`);
1823
+ if (request.keyConditionExpression) lines.push(`${indent}keyConditionExpression = ${ktString(request.keyConditionExpression)}`);
1824
+ if (request.updateExpression) lines.push(`${indent}updateExpression = ${ktString(request.updateExpression)}`);
1825
+ if (request.conditionExpression) lines.push(`${indent}conditionExpression = ${ktString(request.conditionExpression)}`);
1826
+ if (request.filterExpression) lines.push(`${indent}filterExpression = ${ktString(request.filterExpression)}`);
1827
+ if (request.projectionExpression) lines.push(`${indent}projectionExpression = ${ktString(request.projectionExpression)}`);
1828
+ if (request.names) lines.push(`${indent}expressionAttributeNames = ${renderNameMap$2(request.names, indent)}`);
1829
+ if (request.typedValues) lines.push(`${indent}expressionAttributeValues = ${renderAvMap$1(request.typedValues, indent)}`);
1830
+ if (request.limit !== void 0) lines.push(`${indent}limit = ${request.limit}`);
1831
+ if (request.consistentRead) lines.push(`${indent}consistentRead = true`);
1832
+ if (request.scanIndexForward === false) lines.push(`${indent}scanIndexForward = false`);
1833
+ if (request.exclusiveStartKey) lines.push(`${indent}exclusiveStartKey = ${renderAvMap$1(request.exclusiveStartKey, indent)}`);
1834
+ return lines;
1835
+ }
1836
+ /** Emit the bare `val response = client.…{ … }` call for a canonical request. */
1837
+ function emitKotlin(request) {
1838
+ return [
1839
+ `val response = client.${CLIENT_METHOD_BY_OP$2[request.operation]} {`,
1840
+ ...renderKotlinBuilder(request, " "),
1841
+ "}"
1842
+ ].join("\n");
1843
+ }
1844
+
1845
+ //#endregion
1846
+ //#region src/emit/php.ts
1847
+ /** Operation → the `DynamoDbClient` method. */
1848
+ const CLIENT_METHOD_BY_OP$1 = {
1849
+ GetItem: "getItem",
1850
+ Query: "query",
1851
+ Scan: "scan",
1852
+ Update: "updateItem",
1853
+ Put: "putItem",
1854
+ Delete: "deleteItem"
1855
+ };
1856
+ /** The client method name (camelCase) for an operation. */
1857
+ function phpClientMethodName(operation) {
1858
+ return CLIENT_METHOD_BY_OP$1[operation];
1859
+ }
1860
+ /** PHP single-quoted string — only `\` and `'` need escaping. */
1861
+ function phpString(value) {
1862
+ return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
1863
+ }
1864
+ /** Render one wire AttributeValue as a nested `['TAG' => …]` literal. */
1865
+ function renderPhpAv(av) {
1866
+ if ("S" in av) return `['S' => ${phpString(av.S)}]`;
1867
+ if ("N" in av) return `['N' => ${phpString(av.N)}]`;
1868
+ if ("B" in av) return `['B' => base64_decode(${phpString(av.B)})]`;
1869
+ if ("BOOL" in av) return `['BOOL' => ${av.BOOL}]`;
1870
+ if ("SS" in av) return `['SS' => [${av.SS.map(phpString).join(", ")}]]`;
1871
+ if ("NS" in av) return `['NS' => [${av.NS.map(phpString).join(", ")}]]`;
1872
+ if ("BS" in av) return `['BS' => [${av.BS.map((b) => `base64_decode(${phpString(b)})`).join(", ")}]]`;
1873
+ return "['NULL' => true]";
1874
+ }
1875
+ /** Render a typed map as a PHP array of AV literals, one entry per line. */
1876
+ function renderAvMap(map, indent) {
1877
+ const inner = `${indent} `;
1878
+ return `[\n${Object.entries(typedMapToAvMap(map)).map(([name, av]) => `${inner}${phpString(name)} => ${renderPhpAv(av)},`).join("\n")}\n${indent}]`;
1879
+ }
1880
+ /** Render the plain string map (`ExpressionAttributeNames`). */
1881
+ function renderNameMap$1(map, indent) {
1882
+ const inner = `${indent} `;
1883
+ return `[\n${Object.entries(map).map(([alias, name]) => `${inner}${phpString(alias)} => ${phpString(name)},`).join("\n")}\n${indent}]`;
1884
+ }
1885
+ /**
1886
+ * Render the request array literal, one field per line at `indent + 4`.
1887
+ * Exported for the program emitter.
1888
+ */
1889
+ function renderPhpParams(request, indent) {
1890
+ const pad = `${indent} `;
1891
+ const fields = [`'TableName' => ${phpString(request.tableName)},`];
1892
+ if (request.indexName) fields.push(`'IndexName' => ${phpString(request.indexName)},`);
1893
+ if (request.key) fields.push(`'Key' => ${renderAvMap(request.key, pad)},`);
1894
+ if (request.item) fields.push(`'Item' => ${renderAvMap(request.item, pad)},`);
1895
+ if (request.keyConditionExpression) fields.push(`'KeyConditionExpression' => ${phpString(request.keyConditionExpression)},`);
1896
+ if (request.updateExpression) fields.push(`'UpdateExpression' => ${phpString(request.updateExpression)},`);
1897
+ if (request.conditionExpression) fields.push(`'ConditionExpression' => ${phpString(request.conditionExpression)},`);
1898
+ if (request.filterExpression) fields.push(`'FilterExpression' => ${phpString(request.filterExpression)},`);
1899
+ if (request.projectionExpression) fields.push(`'ProjectionExpression' => ${phpString(request.projectionExpression)},`);
1900
+ if (request.names) fields.push(`'ExpressionAttributeNames' => ${renderNameMap$1(request.names, pad)},`);
1901
+ if (request.typedValues) fields.push(`'ExpressionAttributeValues' => ${renderAvMap(request.typedValues, pad)},`);
1902
+ if (request.limit !== void 0) fields.push(`'Limit' => ${request.limit},`);
1903
+ if (request.consistentRead) fields.push("'ConsistentRead' => true,");
1904
+ if (request.scanIndexForward === false) fields.push("'ScanIndexForward' => false,");
1905
+ if (request.exclusiveStartKey) fields.push(`'ExclusiveStartKey' => ${renderAvMap(request.exclusiveStartKey, pad)},`);
1906
+ return [
1907
+ "[",
1908
+ ...fields.map((f) => `${pad}${f}`),
1909
+ `${indent}]`
1910
+ ].join("\n");
1911
+ }
1912
+ /** Emit the bare `$response = $client->…([…]);` call for a canonical request. */
1913
+ function emitPhp(request) {
1914
+ return `$response = $client->${CLIENT_METHOD_BY_OP$1[request.operation]}(${renderPhpParams(request, "")});`;
1915
+ }
1916
+
1917
+ //#endregion
1918
+ //#region src/emit/ruby.ts
1919
+ /** Operation → the `Aws::DynamoDB::Client` method (snake_case). */
1920
+ const CLIENT_METHOD_BY_OP = {
1921
+ GetItem: "get_item",
1922
+ Query: "query",
1923
+ Scan: "scan",
1924
+ Update: "update_item",
1925
+ Put: "put_item",
1926
+ Delete: "delete_item"
1927
+ };
1928
+ /** The client method name (snake_case) for an operation. */
1929
+ function rubyClientMethodName(operation) {
1930
+ return CLIENT_METHOD_BY_OP[operation];
1931
+ }
1932
+ /** Ruby single-quoted string — only `\` and `'` need escaping. */
1933
+ function rubyString(value) {
1934
+ return `'${value.replace(/\\/g, "\\\\").replace(/'/g, "\\'")}'`;
1935
+ }
1936
+ /** Render one typed value as the native Ruby literal the SDK marshals. */
1937
+ function renderRubyValue(tv) {
1938
+ switch (tv.type) {
1939
+ case "S": return rubyString(tv.value);
1940
+ case "N": return tv.value;
1941
+ case "B": return `Base64.decode64(${rubyString(tv.value)})`;
1942
+ case "BOOL": return tv.value === "true" ? "true" : "false";
1943
+ case "NULL": return "nil";
1944
+ case "SS": return `Set.new([${(tv.values ?? []).map(rubyString).join(", ")}])`;
1945
+ case "NS": return `Set.new([${(tv.values ?? []).join(", ")}])`;
1946
+ case "BS": return `Set.new([${(tv.values ?? []).map((b) => `Base64.decode64(${rubyString(b)})`).join(", ")}])`;
1947
+ }
1948
+ }
1949
+ /** Render a typed map as a Ruby hash of native values, one entry per line. */
1950
+ function renderValueMap(map, indent) {
1951
+ const inner = `${indent} `;
1952
+ return `{\n${Object.entries(map).map(([name, tv]) => `${inner}${rubyString(name)} => ${renderRubyValue(tv)},`).join("\n")}\n${indent}}`;
1953
+ }
1954
+ /** Render the plain string map (`expression_attribute_names`). */
1955
+ function renderNameMap(map, indent) {
1956
+ const inner = `${indent} `;
1957
+ return `{\n${Object.entries(map).map(([alias, name]) => `${inner}${rubyString(alias)} => ${rubyString(name)},`).join("\n")}\n${indent}}`;
1958
+ }
1959
+ /**
1960
+ * Render the request keyword-argument hash, one field per line at `indent + 2`.
1961
+ * Exported for the program emitter.
1962
+ */
1963
+ function renderRubyParams(request, indent) {
1964
+ const pad = `${indent} `;
1965
+ const fields = [`table_name: ${rubyString(request.tableName)},`];
1966
+ if (request.indexName) fields.push(`index_name: ${rubyString(request.indexName)},`);
1967
+ if (request.key) fields.push(`key: ${renderValueMap(request.key, pad)},`);
1968
+ if (request.item) fields.push(`item: ${renderValueMap(request.item, pad)},`);
1969
+ if (request.keyConditionExpression) fields.push(`key_condition_expression: ${rubyString(request.keyConditionExpression)},`);
1970
+ if (request.updateExpression) fields.push(`update_expression: ${rubyString(request.updateExpression)},`);
1971
+ if (request.conditionExpression) fields.push(`condition_expression: ${rubyString(request.conditionExpression)},`);
1972
+ if (request.filterExpression) fields.push(`filter_expression: ${rubyString(request.filterExpression)},`);
1973
+ if (request.projectionExpression) fields.push(`projection_expression: ${rubyString(request.projectionExpression)},`);
1974
+ if (request.names) fields.push(`expression_attribute_names: ${renderNameMap(request.names, pad)},`);
1975
+ if (request.typedValues) fields.push(`expression_attribute_values: ${renderValueMap(request.typedValues, pad)},`);
1976
+ if (request.limit !== void 0) fields.push(`limit: ${request.limit},`);
1977
+ if (request.consistentRead) fields.push("consistent_read: true,");
1978
+ if (request.scanIndexForward === false) fields.push("scan_index_forward: false,");
1979
+ if (request.exclusiveStartKey) fields.push(`exclusive_start_key: ${renderValueMap(request.exclusiveStartKey, pad)},`);
1980
+ return [
1981
+ "{",
1982
+ ...fields.map((f) => `${pad}${f}`),
1983
+ `${indent}}`
1984
+ ].join("\n");
1985
+ }
1986
+ /** Emit the bare `response = client.…(…)` call for a canonical request. */
1987
+ function emitRuby(request) {
1988
+ return `response = client.${CLIENT_METHOD_BY_OP[request.operation]}(${renderRubyParams(request, "")})`;
1989
+ }
1990
+
1586
1991
  //#endregion
1587
1992
  //#region src/program.ts
1588
1993
  /**
@@ -1627,6 +2032,26 @@ function emitQueryProgram(config, format) {
1627
2032
  ok: true,
1628
2033
  code: emitDotnetProgram(request, config.paginate === true)
1629
2034
  };
2035
+ case "rust": return {
2036
+ ok: true,
2037
+ code: emitRustProgram(request, config.paginate === true)
2038
+ };
2039
+ case "docclient": return {
2040
+ ok: true,
2041
+ code: emitDocClientProgram(request, config.paginate === true)
2042
+ };
2043
+ case "kotlin": return {
2044
+ ok: true,
2045
+ code: emitKotlinProgram(request, config.paginate === true)
2046
+ };
2047
+ case "php": return {
2048
+ ok: true,
2049
+ code: emitPhpProgram(request, config.paginate === true)
2050
+ };
2051
+ case "ruby": return {
2052
+ ok: true,
2053
+ code: emitRubyProgram(request, config.paginate === true)
2054
+ };
1630
2055
  case "ddbtoolbox": return {
1631
2056
  ok: true,
1632
2057
  code: emitDdbToolboxProgram(request, config.paginate === true)
@@ -1824,6 +2249,173 @@ function emitGoProgram(request, paginate) {
1824
2249
  "}"
1825
2250
  ].join("\n");
1826
2251
  }
2252
+ function emitRustProgram(request, paginate) {
2253
+ const usesAv = rustUsesAttributeValue(request);
2254
+ const usesBlob = rustUsesBlob(request);
2255
+ const header = [
2256
+ ...[
2257
+ "// Cargo.toml: aws-config, aws-sdk-dynamodb, tokio (features = [\"full\"])",
2258
+ "use aws_sdk_dynamodb::Client;",
2259
+ ...usesAv ? ["use aws_sdk_dynamodb::types::AttributeValue;"] : [],
2260
+ ...usesBlob ? ["use aws_sdk_dynamodb::primitives::Blob;"] : []
2261
+ ],
2262
+ "",
2263
+ "#[tokio::main]",
2264
+ "async fn main() -> Result<(), aws_sdk_dynamodb::Error> {",
2265
+ " let config = aws_config::load_from_env().await;",
2266
+ " let client = Client::new(&config);",
2267
+ ""
2268
+ ];
2269
+ const builder = renderRustBuilder(request, " ");
2270
+ if (!paginate) return [
2271
+ ...header,
2272
+ " let response = client",
2273
+ ...builder,
2274
+ " .send()",
2275
+ " .await?;",
2276
+ " println!(\"{:?}\", response.items());",
2277
+ "",
2278
+ " Ok(())",
2279
+ "}"
2280
+ ].join("\n");
2281
+ return [
2282
+ ...header,
2283
+ " let mut items = client",
2284
+ ...builder,
2285
+ " .into_paginator()",
2286
+ " .items()",
2287
+ " .send();",
2288
+ "",
2289
+ " while let Some(item) = items.next().await {",
2290
+ " println!(\"{:?}\", item?);",
2291
+ " }",
2292
+ "",
2293
+ " Ok(())",
2294
+ "}"
2295
+ ].join("\n");
2296
+ }
2297
+ function emitDocClientProgram(request, paginate) {
2298
+ const command = docClientCommandName(request.operation);
2299
+ renderDocClientParams(request, "");
2300
+ if (!paginate) return [
2301
+ "import { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";",
2302
+ `import { DynamoDBDocumentClient, ${command} } from "@aws-sdk/lib-dynamodb";`,
2303
+ "",
2304
+ "const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));",
2305
+ "",
2306
+ "const response = await client.send(",
2307
+ ` new ${command}(${renderDocClientParams(request, " ")})`,
2308
+ ");",
2309
+ "",
2310
+ "console.log(response.Items);"
2311
+ ].join("\n");
2312
+ const paginator = request.operation === "Scan" ? "paginateScan" : "paginateQuery";
2313
+ return [
2314
+ "import { DynamoDBClient } from \"@aws-sdk/client-dynamodb\";",
2315
+ `import { DynamoDBDocumentClient, ${paginator} } from "@aws-sdk/lib-dynamodb";`,
2316
+ "",
2317
+ "const client = DynamoDBDocumentClient.from(new DynamoDBClient({}));",
2318
+ "",
2319
+ `const paginator = ${paginator}(`,
2320
+ " { client },",
2321
+ ` ${renderDocClientParams(request, " ")}`,
2322
+ ");",
2323
+ "",
2324
+ "for await (const page of paginator) {",
2325
+ " console.log(page.Items);",
2326
+ "}"
2327
+ ].join("\n");
2328
+ }
2329
+ function emitKotlinProgram(request, paginate) {
2330
+ const method = kotlinClientMethodName(request.operation);
2331
+ const header = [
2332
+ "import aws.sdk.kotlin.services.dynamodb.DynamoDbClient",
2333
+ "import aws.sdk.kotlin.services.dynamodb.model.AttributeValue",
2334
+ ...paginate ? [
2335
+ `import aws.sdk.kotlin.services.dynamodb.paginators.${method}Paginated`,
2336
+ "import aws.sdk.kotlin.services.dynamodb.paginators.items",
2337
+ "import kotlinx.coroutines.flow.collect"
2338
+ ] : [],
2339
+ "",
2340
+ "suspend fun main() {",
2341
+ " DynamoDbClient.fromEnvironment().use { client ->"
2342
+ ];
2343
+ const body = renderKotlinBuilder(request, " ");
2344
+ if (!paginate) return [
2345
+ ...header,
2346
+ ` val response = client.${method} {`,
2347
+ ...body,
2348
+ " }",
2349
+ " println(response.items)",
2350
+ " }",
2351
+ "}"
2352
+ ].join("\n");
2353
+ return [
2354
+ ...header,
2355
+ ` client.${method}Paginated {`,
2356
+ ...body,
2357
+ " }",
2358
+ " .items()",
2359
+ " .collect { item -> println(item) }",
2360
+ " }",
2361
+ "}"
2362
+ ].join("\n");
2363
+ }
2364
+ function emitPhpProgram(request, paginate) {
2365
+ const method = phpClientMethodName(request.operation);
2366
+ const header = [
2367
+ "<?php",
2368
+ "",
2369
+ "require 'vendor/autoload.php';",
2370
+ "",
2371
+ "use Aws\\DynamoDb\\DynamoDbClient;",
2372
+ "",
2373
+ "$client = new DynamoDbClient(['region' => 'us-east-1', 'version' => 'latest']);",
2374
+ ""
2375
+ ];
2376
+ if (!paginate) return [
2377
+ ...header,
2378
+ `$response = $client->${method}(${renderPhpParams(request, "")});`,
2379
+ "",
2380
+ "print_r($response['Items']);"
2381
+ ].join("\n");
2382
+ const operation = request.operation === "Scan" ? "Scan" : "Query";
2383
+ return [
2384
+ ...header,
2385
+ `$pages = $client->getPaginator('${operation}', ${renderPhpParams(request, "")});`,
2386
+ "",
2387
+ "foreach ($pages as $page) {",
2388
+ " print_r($page['Items']);",
2389
+ "}"
2390
+ ].join("\n");
2391
+ }
2392
+ function emitRubyProgram(request, paginate) {
2393
+ const method = rubyClientMethodName(request.operation);
2394
+ const usesSets = /Set\.new\(/.test(renderRubyParams(request, ""));
2395
+ const usesBase64 = /Base64\.decode64\(/.test(renderRubyParams(request, ""));
2396
+ const header = [
2397
+ "require 'aws-sdk-dynamodb'",
2398
+ ...usesSets ? ["require 'set'"] : [],
2399
+ ...usesBase64 ? ["require 'base64'"] : [],
2400
+ "",
2401
+ "client = Aws::DynamoDB::Client.new",
2402
+ ""
2403
+ ];
2404
+ if (!paginate) return [
2405
+ ...header,
2406
+ `response = client.${method}(${renderRubyParams(request, "")})`,
2407
+ "",
2408
+ "puts response.items"
2409
+ ].join("\n");
2410
+ return [
2411
+ ...header,
2412
+ `response = client.${method}(${renderRubyParams(request, "")})`,
2413
+ "",
2414
+ "response.each do |page|",
2415
+ " puts page.items",
2416
+ "end"
2417
+ ].join("\n");
2418
+ }
1827
2419
  function emitDotnetProgram(request, paginate) {
1828
2420
  const method = dotnetClientMethodName(request.operation);
1829
2421
  const binary = hasBinaryValue(request);
@@ -1880,17 +2472,23 @@ exports.buildKeyMap = buildKeyMap;
1880
2472
  exports.buildRequest = buildRequest;
1881
2473
  exports.buildSdkV3Params = buildSdkV3Params;
1882
2474
  exports.buildUpdateExpression = buildUpdateExpression;
2475
+ exports.docClientCommandName = docClientCommandName;
1883
2476
  exports.dotnetClientMethodName = dotnetClientMethodName;
1884
2477
  exports.dotnetRequestClassName = dotnetRequestClassName;
1885
2478
  exports.elementType = elementType;
1886
2479
  exports.emitBoto3 = emitBoto3;
1887
2480
  exports.emitCli = emitCli;
1888
2481
  exports.emitDdbToolboxProgram = emitDdbToolboxProgram;
2482
+ exports.emitDocClient = emitDocClient;
1889
2483
  exports.emitDotnet = emitDotnet;
1890
2484
  exports.emitGo = emitGo;
1891
2485
  exports.emitJava = emitJava;
2486
+ exports.emitKotlin = emitKotlin;
1892
2487
  exports.emitPartiql = emitPartiql;
2488
+ exports.emitPhp = emitPhp;
1893
2489
  exports.emitQueryProgram = emitQueryProgram;
2490
+ exports.emitRuby = emitRuby;
2491
+ exports.emitRust = emitRust;
1894
2492
  exports.emitSdkV3 = emitSdkV3;
1895
2493
  exports.getCompatibleComparisonOperators = getCompatibleComparisonOperators;
1896
2494
  exports.getCompatibleFilterOperators = getCompatibleFilterOperators;
@@ -1900,9 +2498,17 @@ exports.hasBinaryValue = hasBinaryValue;
1900
2498
  exports.isSetType = isSetType;
1901
2499
  exports.javaClientMethodName = javaClientMethodName;
1902
2500
  exports.javaRequestClassName = javaRequestClassName;
2501
+ exports.kotlinClientMethodName = kotlinClientMethodName;
1903
2502
  exports.makeTypedValue = makeTypedValue;
2503
+ exports.phpClientMethodName = phpClientMethodName;
1904
2504
  exports.renderJsValue = renderJsValue;
2505
+ exports.renderKotlinAv = renderKotlinAv;
2506
+ exports.renderPhpAv = renderPhpAv;
1905
2507
  exports.renderPyValue = renderPyValue;
2508
+ exports.renderRubyValue = renderRubyValue;
2509
+ exports.renderRustAv = renderRustAv;
2510
+ exports.rubyClientMethodName = rubyClientMethodName;
2511
+ exports.rustClientMethodName = rustClientMethodName;
1906
2512
  exports.sdkV3CommandName = sdkV3CommandName;
1907
2513
  exports.typedMapToAvMap = typedMapToAvMap;
1908
2514
  exports.typedValueToAv = typedValueToAv;