@kensio/yulin 1.21.13 → 1.21.14

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/service/dynamodb/command/authorize/sim-dynamodb-attributes.d.ts +27 -0
  2. package/dist/service/dynamodb/command/authorize/sim-dynamodb-attributes.js +35 -0
  3. package/dist/service/dynamodb/command/authorize/sim-dynamodb-authorizer.d.ts +2 -1
  4. package/dist/service/dynamodb/command/authorize/sim-dynamodb-authorizer.js +5 -18
  5. package/dist/service/dynamodb/command/authorize/sim-dynamodb-reached.d.ts +17 -0
  6. package/dist/service/dynamodb/command/authorize/sim-dynamodb-reached.js +11 -0
  7. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-get-item.js +4 -1
  8. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-read-limit.d.ts +6 -0
  9. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-read-limit.js +16 -0
  10. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-reads.d.ts +2 -0
  11. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-reads.js +14 -22
  12. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-tables.d.ts +5 -5
  13. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-tables.js +1 -1
  14. package/dist/service/dynamodb/command/batch/sim-dynamodb-batch-write-item.js +5 -1
  15. package/dist/service/dynamodb/command/item/sim-dynamodb-condition-check.d.ts +5 -1
  16. package/dist/service/dynamodb/command/item/sim-dynamodb-condition-check.js +9 -2
  17. package/dist/service/dynamodb/command/item/sim-dynamodb-delete-item.js +7 -1
  18. package/dist/service/dynamodb/command/item/sim-dynamodb-get-item.js +9 -2
  19. package/dist/service/dynamodb/command/item/sim-dynamodb-put-item.js +5 -1
  20. package/dist/service/dynamodb/command/item/sim-dynamodb-update-expressions.d.ts +2 -0
  21. package/dist/service/dynamodb/command/item/sim-dynamodb-update-expressions.js +1 -1
  22. package/dist/service/dynamodb/command/item/sim-dynamodb-update-item.js +7 -1
  23. package/dist/service/dynamodb/command/item/sim-dynamodb-update-plan.d.ts +8 -6
  24. package/dist/service/dynamodb/command/item/sim-dynamodb-update-plan.js +14 -13
  25. package/dist/service/dynamodb/command/item/sim-dynamodb-update-touched.d.ts +10 -0
  26. package/dist/service/dynamodb/command/item/sim-dynamodb-update-touched.js +16 -0
  27. package/dist/service/dynamodb/command/query/sim-dynamodb-query-expressions.d.ts +2 -0
  28. package/dist/service/dynamodb/command/query/sim-dynamodb-query-expressions.js +6 -1
  29. package/dist/service/dynamodb/command/query/sim-dynamodb-query.js +5 -2
  30. package/dist/service/dynamodb/command/scan/sim-dynamodb-scan-expressions.d.ts +2 -0
  31. package/dist/service/dynamodb/command/scan/sim-dynamodb-scan-expressions.js +2 -2
  32. package/dist/service/dynamodb/command/scan/sim-dynamodb-scan.js +1 -1
  33. package/dist/service/dynamodb/command/table/sim-dynamodb-table-access.d.ts +3 -3
  34. package/dist/service/dynamodb/command/table/sim-dynamodb-table-access.js +5 -21
  35. package/dist/service/dynamodb/command/table/sim-dynamodb-table-reach.d.ts +25 -0
  36. package/dist/service/dynamodb/command/table/sim-dynamodb-table-reach.js +27 -0
  37. package/dist/service/dynamodb/command/transact/sim-dynamodb-transact-gets.js +1 -1
  38. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-expression.d.ts +9 -1
  39. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-expression.js +2 -2
  40. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-grammar.js +1 -1
  41. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-operand-parser.d.ts +3 -2
  42. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-operand-parser.js +3 -3
  43. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-parser.d.ts +2 -1
  44. package/dist/service/dynamodb/expression/condition/sim-dynamodb-condition-parser.js +1 -1
  45. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-expression.js +1 -1
  46. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-operands.d.ts +3 -2
  47. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-operands.js +8 -6
  48. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-parser.d.ts +2 -1
  49. package/dist/service/dynamodb/expression/key-condition/sim-dynamodb-key-condition-parser.js +1 -1
  50. package/dist/service/dynamodb/expression/projection/sim-dynamodb-projection-expression.d.ts +9 -1
  51. package/dist/service/dynamodb/expression/projection/sim-dynamodb-projection-expression.js +5 -5
  52. package/dist/service/dynamodb/expression/sim-dynamodb-document-path-parser.d.ts +3 -3
  53. package/dist/service/dynamodb/expression/sim-dynamodb-document-path-parser.js +8 -4
  54. package/dist/service/dynamodb/expression/sim-dynamodb-expression-attributes.d.ts +33 -0
  55. package/dist/service/dynamodb/expression/sim-dynamodb-expression-attributes.js +40 -0
  56. package/dist/service/dynamodb/expression/sim-dynamodb-expression-parameters.d.ts +6 -0
  57. package/dist/service/dynamodb/expression/sim-dynamodb-expression-parameters.js +7 -0
  58. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-clause.d.ts +3 -3
  59. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-clause.js +3 -3
  60. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-expression.js +1 -1
  61. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-operand-parser.d.ts +3 -2
  62. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-operand-parser.js +3 -3
  63. package/dist/service/dynamodb/expression/update/sim-dynamodb-update-parser.d.ts +2 -1
  64. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-for-all-values-string-operator.d.ts +4 -1
  65. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-for-all-values-string-operator.js +6 -1
  66. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-for-any-value-string-operator.d.ts +4 -1
  67. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-for-any-value-string-operator.js +6 -1
  68. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-scalar-string-operator.d.ts +4 -1
  69. package/dist/service/iam/authorize/match/condition/negated/sim-iam-negated-scalar-string-operator.js +6 -1
  70. package/dist/service/iam/authorize/match/condition/null/sim-iam-null.d.ts +23 -0
  71. package/dist/service/iam/authorize/match/condition/null/sim-iam-null.js +59 -0
  72. package/dist/service/iam/authorize/match/condition/numeric/less-than-equals/sim-iam-number-lte.d.ts +4 -1
  73. package/dist/service/iam/authorize/match/condition/numeric/less-than-equals/sim-iam-number-lte.js +6 -1
  74. package/dist/service/iam/authorize/match/condition/sim-iam-condition-operator-parser.js +2 -0
  75. package/dist/service/iam/authorize/match/condition/sim-iam-condition-operator.d.ts +5 -1
  76. package/dist/service/iam/authorize/match/condition/sim-iam-policy-condition-matcher.js +1 -1
  77. package/dist/service/iam/authorize/match/condition/string/all-values/sim-iam-for-all-values-string-condition-operator.d.ts +4 -1
  78. package/dist/service/iam/authorize/match/condition/string/all-values/sim-iam-for-all-values-string-condition-operator.js +6 -1
  79. package/dist/service/iam/authorize/match/condition/string/any-value/sim-iam-for-any-value-string-condition-operator.d.ts +4 -1
  80. package/dist/service/iam/authorize/match/condition/string/any-value/sim-iam-for-any-value-string-condition-operator.js +6 -1
  81. package/dist/service/iam/authorize/match/condition/string/sim-iam-scalar-string-condition-operator.d.ts +4 -1
  82. package/dist/service/iam/authorize/match/condition/string/sim-iam-scalar-string-condition-operator.js +6 -1
  83. package/docs/services/dynamodb/README.md +23 -4
  84. package/docs/services/iam/README.md +17 -3
  85. package/package.json +1 -1
@@ -0,0 +1,27 @@
1
+ import type { SimDynamoDbItem } from "../../item/sim-dynamodb-item.js";
2
+ /**
3
+ * The condition key DynamoDB names a request's attributes by.
4
+ */
5
+ export declare const simDynamoDbAttributesConditionKey = "dynamodb:Attributes";
6
+ /**
7
+ * The top-level attribute names a `Key` or an `Item` carries.
8
+ *
9
+ * Those parameters name their attributes outright, so there is no expression
10
+ * to read them out of. Every name is top-level, since an item's attributes are
11
+ * what nesting starts from.
12
+ *
13
+ * The items are read when authorization asks for them, and whatever reading
14
+ * them throws is dropped. Authorization runs ahead of the checks a command
15
+ * makes on what it was given, so a request too malformed to read is authorized
16
+ * naming no attributes and refused by the check that follows.
17
+ */
18
+ export declare function simDynamoDbItemAttributeNames(items: () => readonly SimDynamoDbItem[]): readonly string[];
19
+ /**
20
+ * Gather the attribute names a request reaches, keeping each one once.
21
+ *
22
+ * A request names the same attribute in more than one place often enough for
23
+ * this to matter. An UpdateItem names its key in `Key` and may name it again
24
+ * in a condition, and AWS supplies one list of attributes rather than one per
25
+ * parameter.
26
+ */
27
+ export declare function simDynamoDbAttributesOf(...sources: readonly (readonly string[])[]): readonly string[];
@@ -0,0 +1,35 @@
1
+ /**
2
+ * The condition key DynamoDB names a request's attributes by.
3
+ */
4
+ export const simDynamoDbAttributesConditionKey = "dynamodb:Attributes";
5
+ /**
6
+ * The top-level attribute names a `Key` or an `Item` carries.
7
+ *
8
+ * Those parameters name their attributes outright, so there is no expression
9
+ * to read them out of. Every name is top-level, since an item's attributes are
10
+ * what nesting starts from.
11
+ *
12
+ * The items are read when authorization asks for them, and whatever reading
13
+ * them throws is dropped. Authorization runs ahead of the checks a command
14
+ * makes on what it was given, so a request too malformed to read is authorized
15
+ * naming no attributes and refused by the check that follows.
16
+ */
17
+ export function simDynamoDbItemAttributeNames(items) {
18
+ try {
19
+ return items().flatMap((item) => item.attributeNames());
20
+ }
21
+ catch {
22
+ return [];
23
+ }
24
+ }
25
+ /**
26
+ * Gather the attribute names a request reaches, keeping each one once.
27
+ *
28
+ * A request names the same attribute in more than one place often enough for
29
+ * this to matter. An UpdateItem names its key in `Key` and may name it again
30
+ * in a condition, and AWS supplies one list of attributes rather than one per
31
+ * parameter.
32
+ */
33
+ export function simDynamoDbAttributesOf(...sources) {
34
+ return [...new Set(sources.flat())];
35
+ }
@@ -2,6 +2,7 @@ import type { SimAwsCaller } from "../../../aws/caller/sim-aws-caller.js";
2
2
  import type { SimAwsResolvedCaller } from "../../../aws/caller/sim-aws-caller-resolver.js";
3
3
  import type { SimAwsAccountRegionScope } from "../../../aws/sim-aws-account-region-scope.js";
4
4
  import type { SimIamInterServiceAuthZ } from "../../../iam/authorize/sim-iam-inter-service-auth-z.js";
5
+ import type { SimDynamoDbReached } from "./sim-dynamodb-reached.js";
5
6
  interface SimDynamoDbAuthorizerProperties {
6
7
  readonly iam: SimIamInterServiceAuthZ;
7
8
  readonly accountRegionScope: SimAwsAccountRegionScope;
@@ -29,7 +30,7 @@ export declare class SimDynamoDbAuthorizer {
29
30
  * omitted caller, which defaults to Account root, from an explicit anonymous
30
31
  * caller.
31
32
  */
32
- authorizeTable(action: string, tableName: string, caller?: SimAwsCaller, leadingKeys?: readonly string[]): SimAwsResolvedCaller;
33
+ authorizeTable(action: string, tableName: string, caller?: SimAwsCaller, reached?: SimDynamoDbReached): SimAwsResolvedCaller;
33
34
  /**
34
35
  * Ensure the caller may perform an action on a table's stream.
35
36
  *
@@ -1,19 +1,6 @@
1
1
  import { SimIamAccessDenied } from "../../../iam/error/sim-iam.error.js";
2
2
  import { simDynamoDbTableArn } from "../../table/sim-dynamodb-table-arn.js";
3
- import { simDynamoDbLeadingKeysConditionKey } from "./sim-dynamodb-leading-keys.js";
4
- /**
5
- * The condition values a request carries for the table it names.
6
- *
7
- * A request reaching no partition key value supplies none rather than an empty
8
- * list, leaving the key absent from the context as it is on AWS for an
9
- * operation that names no item.
10
- */
11
- function conditionContextOf(leadingKeys) {
12
- if (leadingKeys === undefined || leadingKeys.length === 0) {
13
- return {};
14
- }
15
- return { [simDynamoDbLeadingKeysConditionKey]: leadingKeys };
16
- }
3
+ import { simDynamoDbConditionContextOf } from "./sim-dynamodb-reached.js";
17
4
  /**
18
5
  * Applies simulated IAM authorization to DynamoDB requests.
19
6
  *
@@ -40,8 +27,8 @@ export class SimDynamoDbAuthorizer {
40
27
  * omitted caller, which defaults to Account root, from an explicit anonymous
41
28
  * caller.
42
29
  */
43
- authorizeTable(action, tableName, caller, leadingKeys) {
44
- return this.authorizeResource(action, simDynamoDbTableArn(this.accountRegionScope, tableName), caller, leadingKeys);
30
+ authorizeTable(action, tableName, caller, reached = {}) {
31
+ return this.authorizeResource(action, simDynamoDbTableArn(this.accountRegionScope, tableName), caller, reached);
45
32
  }
46
33
  /**
47
34
  * Ensure the caller may perform an action on a table's stream.
@@ -65,12 +52,12 @@ export class SimDynamoDbAuthorizer {
65
52
  authorizeAnyTable(action, caller) {
66
53
  return this.authorizeResource(action, "*", caller);
67
54
  }
68
- authorizeResource(action, resource, caller, leadingKeys) {
55
+ authorizeResource(action, resource, caller, reached = {}) {
69
56
  const decision = this.iam.authorize({
70
57
  action,
71
58
  resource,
72
59
  caller,
73
- conditionContext: conditionContextOf(leadingKeys),
60
+ conditionContext: simDynamoDbConditionContextOf(reached),
74
61
  });
75
62
  if (decision.isDenied) {
76
63
  throw new SimIamAccessDenied({
@@ -0,0 +1,17 @@
1
+ /**
2
+ * What one request reaches in the table it names.
3
+ *
4
+ * Each entry becomes a DynamoDB condition key. A request reaching nothing
5
+ * under one of them leaves it out, as AWS leaves a key out for an operation
6
+ * that reaches nothing it names.
7
+ */
8
+ export interface SimDynamoDbReached {
9
+ /** The partition key values the request reaches. */
10
+ readonly leadingKeys?: readonly string[] | undefined;
11
+ /** The top-level attribute names the request names. */
12
+ readonly attributes?: readonly string[] | undefined;
13
+ }
14
+ /**
15
+ * The condition values a request carries for the table it names.
16
+ */
17
+ export declare function simDynamoDbConditionContextOf(reached: SimDynamoDbReached): Readonly<Record<string, readonly string[]>>;
@@ -0,0 +1,11 @@
1
+ import { simDynamoDbAttributesConditionKey } from "./sim-dynamodb-attributes.js";
2
+ import { simDynamoDbLeadingKeysConditionKey } from "./sim-dynamodb-leading-keys.js";
3
+ /**
4
+ * The condition values a request carries for the table it names.
5
+ */
6
+ export function simDynamoDbConditionContextOf(reached) {
7
+ return Object.fromEntries([
8
+ [simDynamoDbLeadingKeysConditionKey, reached.leadingKeys],
9
+ [simDynamoDbAttributesConditionKey, reached.attributes],
10
+ ].filter(([, values]) => (values ?? []).length > 0));
11
+ }
@@ -28,7 +28,10 @@ export class SimDynamoDbBatchGetItem {
28
28
  access: this.access,
29
29
  operation: "BatchGetItem",
30
30
  caller: options?.caller,
31
- leadingKeys: (entry) => simDynamoDbLeadingKeysOf(() => entry.keys),
31
+ reached: (entry) => ({
32
+ leadingKeys: simDynamoDbLeadingKeysOf(() => entry.keys),
33
+ attributes: entry.attributes,
34
+ }),
32
35
  });
33
36
  const responses = Object.fromEntries(reached.map((entry) => [entry.requested.reference, readItems(entry)]));
34
37
  // Nothing here throttles or stops at a response size, so no key is ever
@@ -0,0 +1,6 @@
1
+ import type { SimDynamoDbKeysAndAttributes } from "./batch.command.js";
2
+ import type { SimDynamoDbBatchTableRequest } from "./sim-dynamodb-batch-request-items.js";
3
+ /**
4
+ * Refuse a batch asking for more items than DynamoDB reads at once.
5
+ */
6
+ export declare function assertSimDynamoDbBatchReadLimit(tables: readonly SimDynamoDbBatchTableRequest<SimDynamoDbKeysAndAttributes>[], operation: string): void;
@@ -0,0 +1,16 @@
1
+ import { SimDynamoDbValidationException } from "../../error/dynamodb.error.js";
2
+ /**
3
+ * Real DynamoDB reads 100 items in one batch, counted across every table the
4
+ * request names rather than per table.
5
+ */
6
+ const greatestKeys = 100;
7
+ /**
8
+ * Refuse a batch asking for more items than DynamoDB reads at once.
9
+ */
10
+ export function assertSimDynamoDbBatchReadLimit(tables, operation) {
11
+ const total = tables.reduce((count, { requested }) => count + (requested.Keys ?? []).length, 0);
12
+ if (total > greatestKeys) {
13
+ throw new SimDynamoDbValidationException(`Too many items requested for the ${operation} call: ${total.toString()} ` +
14
+ `keys, where ${greatestKeys.toString()} is the most a batch reads`);
15
+ }
16
+ }
@@ -11,6 +11,8 @@ export interface SimDynamoDbBatchTableReads {
11
11
  readonly reference: string;
12
12
  readonly keys: readonly SimDynamoDbItem[];
13
13
  readonly projection: SimDynamoDbProjection | undefined;
14
+ /** The top-level attributes this table is asked for, per table as above. */
15
+ readonly attributes: readonly string[];
14
16
  }
15
17
  /**
16
18
  * Read every key a batch asks for, before any table is reached.
@@ -1,14 +1,11 @@
1
1
  import { SimDynamoDbValidationException } from "../../error/dynamodb.error.js";
2
2
  import { readSimDynamoDbProjection } from "../../expression/projection/sim-dynamodb-projection-expression.js";
3
3
  import { readSimDynamoDbKey } from "../item/sim-dynamodb-key-input.js";
4
- import { readSimDynamoDbBatchRequestItems, } from "./sim-dynamodb-batch-request-items.js";
4
+ import { simDynamoDbAttributesOf, simDynamoDbItemAttributeNames, } from "../authorize/sim-dynamodb-attributes.js";
5
+ import { readSimDynamoDbBatchRequestItems } from "./sim-dynamodb-batch-request-items.js";
6
+ import { assertSimDynamoDbBatchReadLimit } from "./sim-dynamodb-batch-read-limit.js";
5
7
  import { refuseUnsimulatedBatchKeysAndAttributes } from "./sim-dynamodb-unsimulated-batch-input.js";
6
8
  const operation = "BatchGetItem";
7
- /**
8
- * Real DynamoDB reads 100 items in one batch, counted across every table the
9
- * request names rather than per table.
10
- */
11
- const greatestKeys = 100;
12
9
  /**
13
10
  * Read every key a batch asks for, before any table is reached.
14
11
  *
@@ -17,22 +14,17 @@ const greatestKeys = 100;
17
14
  */
18
15
  export function readSimDynamoDbBatchReads(requestItems) {
19
16
  const tables = readSimDynamoDbBatchRequestItems(requestItems, operation);
20
- assertWithinKeyLimit(tables);
21
- return tables.map(({ reference, requested }) => ({
22
- reference,
23
- keys: readTableKeys(reference, requested),
24
- projection: readSimDynamoDbProjection(requested),
25
- }));
26
- }
27
- /**
28
- * Refuse a batch asking for more items than DynamoDB reads at once.
29
- */
30
- function assertWithinKeyLimit(tables) {
31
- const total = tables.reduce((count, { requested }) => count + (requested.Keys ?? []).length, 0);
32
- if (total > greatestKeys) {
33
- throw new SimDynamoDbValidationException(`Too many items requested for the ${operation} call: ${total.toString()} ` +
34
- `keys, where ${greatestKeys.toString()} is the most a batch reads`);
35
- }
17
+ assertSimDynamoDbBatchReadLimit(tables, operation);
18
+ return tables.map(({ reference, requested }) => {
19
+ const keys = readTableKeys(reference, requested);
20
+ const read = readSimDynamoDbProjection(requested);
21
+ return {
22
+ reference,
23
+ keys,
24
+ projection: read.projection,
25
+ attributes: simDynamoDbAttributesOf(read.attributes, simDynamoDbItemAttributeNames(() => keys)),
26
+ };
27
+ });
36
28
  }
37
29
  /**
38
30
  * Read the keys one table of the batch is asked for.
@@ -1,7 +1,7 @@
1
1
  import type { SimAwsCaller } from "../../../aws/caller/sim-aws-caller.js";
2
2
  import type { SimDynamoDbTable } from "../../table/sim-dynamodb-table.js";
3
3
  import type { SimDynamoDbTableAccess } from "../table/sim-dynamodb-table-access.js";
4
- import type { SimDynamoDbLeadingKeys } from "../authorize/sim-dynamodb-leading-keys.js";
4
+ import type { SimDynamoDbTableReach } from "../table/sim-dynamodb-table-reach.js";
5
5
  /**
6
6
  * What one table of a batch asks for, against the table it names.
7
7
  */
@@ -14,12 +14,12 @@ interface SimDynamoDbBatchReach<Requested> {
14
14
  readonly operation: string;
15
15
  readonly caller: SimAwsCaller | undefined;
16
16
  /**
17
- * How to read the partition key values one table of the batch is asked for.
17
+ * What one table of the batch is asked for.
18
18
  *
19
- * Each table is authorized on its own, so each carries the keys the batch
20
- * names in that table and no others.
19
+ * Each table is authorized on its own, so each carries the keys and the
20
+ * attributes the batch names in that table and no others.
21
21
  */
22
- readonly leadingKeys: (requested: Requested) => SimDynamoDbLeadingKeys;
22
+ readonly reached: (requested: Requested) => SimDynamoDbTableReach;
23
23
  }
24
24
  /**
25
25
  * Reach every table a batch names, authorizing the caller against each.
@@ -11,7 +11,7 @@ import { SimDynamoDbValidationException } from "../../error/dynamodb.error.js";
11
11
  export function reachSimDynamoDbBatchTables(requested, reach) {
12
12
  const action = `dynamodb:${reach.operation}`;
13
13
  const reached = requested.map((entry) => ({
14
- table: reach.access.required(action, entry.reference, reach.caller, reach.leadingKeys(entry)),
14
+ table: reach.access.required(action, entry.reference, reach.caller, reach.reached(entry)),
15
15
  requested: entry,
16
16
  }));
17
17
  assertDistinctTables(reached, reach.operation);
@@ -3,6 +3,7 @@ import { reachSimDynamoDbBatchTables } from "./sim-dynamodb-batch-tables.js";
3
3
  import { readSimDynamoDbBatchWrites } from "./sim-dynamodb-batch-writes.js";
4
4
  import { refuseUnsimulatedBatchWriteInput } from "./sim-dynamodb-unsimulated-batch-input.js";
5
5
  import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
6
+ import { simDynamoDbItemAttributeNames } from "../authorize/sim-dynamodb-attributes.js";
6
7
  const operation = "BatchWriteItem";
7
8
  /**
8
9
  * The BatchWriteItem command.
@@ -30,7 +31,10 @@ export class SimDynamoDbBatchWriteItem {
30
31
  access: this.access,
31
32
  operation,
32
33
  caller: options?.caller,
33
- leadingKeys: (entry) => simDynamoDbLeadingKeysOf(() => entry.writes.map((write) => write.names)),
34
+ reached: (entry) => ({
35
+ leadingKeys: simDynamoDbLeadingKeysOf(() => entry.writes.map((write) => write.names)),
36
+ attributes: simDynamoDbItemAttributeNames(() => entry.writes.map((write) => write.names)),
37
+ }),
34
38
  });
35
39
  // Marshalling every key checks it against the table's key schema, and is
36
40
  // what tells two operations on one item apart. Every table is checked
@@ -15,6 +15,10 @@ interface SimDynamoDbConditionCheckInput {
15
15
  * be nothing: that is what makes `attribute_not_exists` an insert if absent.
16
16
  */
17
17
  export declare class SimDynamoDbConditionCheck {
18
+ /**
19
+ * The top-level attributes the condition named, for `dynamodb:Attributes`.
20
+ */
21
+ readonly attributeNames: readonly string[];
18
22
  private readonly condition;
19
23
  private readonly reportsItem;
20
24
  private constructor();
@@ -31,7 +35,7 @@ export declare class SimDynamoDbConditionCheck {
31
35
  * UpdateItem reads its condition alongside its update expression, since the
32
36
  * two share the request's placeholders, and then hands the condition here.
33
37
  */
34
- static of(condition: SimDynamoDbCondition | undefined, input: SimDynamoDbConditionCheckInput, operation: string): SimDynamoDbConditionCheck;
38
+ static of(condition: SimDynamoDbCondition | undefined, input: SimDynamoDbConditionCheckInput, operation: string, attributeNames?: readonly string[]): SimDynamoDbConditionCheck;
35
39
  /**
36
40
  * Whether the request asked for the item that turned it away.
37
41
  */
@@ -13,11 +13,16 @@ const failureModes = new Set(["NONE", "ALL_OLD"]);
13
13
  * be nothing: that is what makes `attribute_not_exists` an insert if absent.
14
14
  */
15
15
  export class SimDynamoDbConditionCheck {
16
+ /**
17
+ * The top-level attributes the condition named, for `dynamodb:Attributes`.
18
+ */
19
+ attributeNames;
16
20
  condition;
17
21
  reportsItem;
18
22
  constructor(properties) {
19
23
  this.condition = properties.condition;
20
24
  this.reportsItem = properties.reportsItem;
25
+ this.attributeNames = properties.attributeNames;
21
26
  }
22
27
  /**
23
28
  * Read the condition a request carries, before anything is looked up.
@@ -26,7 +31,8 @@ export class SimDynamoDbConditionCheck {
26
31
  * anything, so a bad expression fails the same way every time.
27
32
  */
28
33
  static read(input, operation) {
29
- return this.of(readSimDynamoDbCondition(input), input, operation);
34
+ const read = readSimDynamoDbCondition(input);
35
+ return this.of(read.condition, input, operation, read.attributes);
30
36
  }
31
37
  /**
32
38
  * Guard a write with a condition that has already been read.
@@ -34,10 +40,11 @@ export class SimDynamoDbConditionCheck {
34
40
  * UpdateItem reads its condition alongside its update expression, since the
35
41
  * two share the request's placeholders, and then hands the condition here.
36
42
  */
37
- static of(condition, input, operation) {
43
+ static of(condition, input, operation, attributeNames = []) {
38
44
  return new this({
39
45
  condition,
40
46
  reportsItem: this.reportsItemFor(input, operation),
47
+ attributeNames,
41
48
  });
42
49
  }
43
50
  /**
@@ -1,4 +1,5 @@
1
1
  import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
2
+ import { simDynamoDbAttributesOf, simDynamoDbItemAttributeNames, } from "../authorize/sim-dynamodb-attributes.js";
2
3
  import { readSimDynamoDbKey } from "./sim-dynamodb-key-input.js";
3
4
  import { SimDynamoDbConditionCheck } from "./sim-dynamodb-condition-check.js";
4
5
  import { SimDynamoDbReturnValues } from "./sim-dynamodb-return-values.js";
@@ -27,7 +28,12 @@ export class SimDynamoDbDeleteItem {
27
28
  // The condition is read before the table is reached, so an expression
28
29
  // DynamoDB would refuse is refused whether or not the key holds anything.
29
30
  const check = SimDynamoDbConditionCheck.read(input, "DeleteItem");
30
- const table = this.access.required("dynamodb:DeleteItem", input.TableName, options?.caller, simDynamoDbLeadingKeysOf(() => [readSimDynamoDbKey(input.Key)]));
31
+ const table = this.access.required("dynamodb:DeleteItem", input.TableName, options?.caller, {
32
+ leadingKeys: simDynamoDbLeadingKeysOf(() => [
33
+ readSimDynamoDbKey(input.Key),
34
+ ]),
35
+ attributes: simDynamoDbAttributesOf(check.attributeNames, simDynamoDbItemAttributeNames(() => [readSimDynamoDbKey(input.Key)])),
36
+ });
31
37
  const asked = SimDynamoDbReturnValues.read(input.ReturnValues, "DeleteItem");
32
38
  const key = readSimDynamoDbKey(input.Key);
33
39
  check.assertHoldsFor(table.getItem(key));
@@ -2,6 +2,7 @@ import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys
2
2
  import { readSimDynamoDbKey } from "./sim-dynamodb-key-input.js";
3
3
  import { refuseUnsimulatedItemReadInput } from "./sim-dynamodb-unsimulated-item-read-input.js";
4
4
  import { readSimDynamoDbProjection } from "../../expression/projection/sim-dynamodb-projection-expression.js";
5
+ import { simDynamoDbAttributesOf, simDynamoDbItemAttributeNames, } from "../authorize/sim-dynamodb-attributes.js";
5
6
  /**
6
7
  * The GetItem command.
7
8
  *
@@ -26,8 +27,14 @@ export class SimDynamoDbGetItem {
26
27
  refuseUnsimulatedItemReadInput(input);
27
28
  // The projection is read before the table is reached, so an expression
28
29
  // DynamoDB would refuse is refused whether or not the key holds anything.
29
- const projection = readSimDynamoDbProjection(input);
30
- const table = this.access.required("dynamodb:GetItem", input.TableName, options?.caller, simDynamoDbLeadingKeysOf(() => [readSimDynamoDbKey(input.Key)]));
30
+ const read = readSimDynamoDbProjection(input);
31
+ const projection = read.projection;
32
+ const table = this.access.required("dynamodb:GetItem", input.TableName, options?.caller, {
33
+ leadingKeys: simDynamoDbLeadingKeysOf(() => [
34
+ readSimDynamoDbKey(input.Key),
35
+ ]),
36
+ attributes: simDynamoDbAttributesOf(read.attributes, simDynamoDbItemAttributeNames(() => [readSimDynamoDbKey(input.Key)])),
37
+ });
31
38
  const found = table.getItem(readSimDynamoDbKey(input.Key));
32
39
  if (found === undefined) {
33
40
  return { $metadata: {} };
@@ -1,6 +1,7 @@
1
1
  import { SimDynamoDbValidationException } from "../../error/dynamodb.error.js";
2
2
  import { SimDynamoDbItem } from "../../item/sim-dynamodb-item.js";
3
3
  import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
4
+ import { simDynamoDbAttributesOf, simDynamoDbItemAttributeNames, } from "../authorize/sim-dynamodb-attributes.js";
4
5
  import { SimDynamoDbConditionCheck } from "./sim-dynamodb-condition-check.js";
5
6
  import { SimDynamoDbReturnValues } from "./sim-dynamodb-return-values.js";
6
7
  import { refuseUnsimulatedItemWriteInput } from "./sim-dynamodb-unsimulated-item-write-input.js";
@@ -37,7 +38,10 @@ export class SimDynamoDbPutItem {
37
38
  // The condition is read before the table is reached, so an expression
38
39
  // DynamoDB would refuse is refused whether or not the key holds anything.
39
40
  const check = SimDynamoDbConditionCheck.read(input, "PutItem");
40
- const table = this.access.required("dynamodb:PutItem", input.TableName, options?.caller, simDynamoDbLeadingKeysOf(() => [readItem(input)]));
41
+ const table = this.access.required("dynamodb:PutItem", input.TableName, options?.caller, {
42
+ leadingKeys: simDynamoDbLeadingKeysOf(() => [readItem(input)]),
43
+ attributes: simDynamoDbAttributesOf(check.attributeNames, simDynamoDbItemAttributeNames(() => [readItem(input)])),
44
+ });
41
45
  const asked = SimDynamoDbReturnValues.read(input.ReturnValues, "PutItem");
42
46
  const item = readItem(input);
43
47
  check.assertHoldsFor(table.itemUnder(item));
@@ -14,6 +14,8 @@ export interface SimDynamoDbUpdateExpressionInput extends SimDynamoDbExpressionP
14
14
  export interface SimDynamoDbUpdateExpressions {
15
15
  readonly update: SimDynamoDbUpdate;
16
16
  readonly condition: SimDynamoDbCondition | undefined;
17
+ /** The top-level attributes the expressions named, for `dynamodb:Attributes`. */
18
+ readonly attributes: readonly string[];
17
19
  }
18
20
  /**
19
21
  * Read both expressions of an update against one set of placeholders.
@@ -13,7 +13,7 @@ export function readSimDynamoDbUpdateExpressions(input, expression) {
13
13
  const update = parseSimDynamoDbUpdate(expression, parameters);
14
14
  const condition = conditionIn(input.ConditionExpression, parameters);
15
15
  parameters.assertAllUsed();
16
- return { update, condition };
16
+ return { update, condition, attributes: parameters.attributes.topLevel };
17
17
  }
18
18
  /**
19
19
  * Read the condition guarding an update, if it names one.
@@ -1,4 +1,5 @@
1
1
  import { simDynamoDbLeadingKeysOf } from "../authorize/sim-dynamodb-leading-keys.js";
2
+ import { simDynamoDbAttributesOf, simDynamoDbItemAttributeNames, } from "../authorize/sim-dynamodb-attributes.js";
2
3
  import { readSimDynamoDbKey } from "./sim-dynamodb-key-input.js";
3
4
  import { SimDynamoDbReturnValues } from "./sim-dynamodb-return-values.js";
4
5
  import { refuseUnsimulatedItemUpdateInput } from "./sim-dynamodb-unsimulated-item-update-input.js";
@@ -29,7 +30,12 @@ export class SimDynamoDbUpdateItem {
29
30
  // The expressions are read before the table is reached, so one DynamoDB
30
31
  // would refuse is refused whether or not the key holds anything.
31
32
  const plan = SimDynamoDbUpdatePlan.read(input, "UpdateItem");
32
- const table = this.access.required("dynamodb:UpdateItem", input.TableName, options?.caller, simDynamoDbLeadingKeysOf(() => [readSimDynamoDbKey(input.Key)]));
33
+ const table = this.access.required("dynamodb:UpdateItem", input.TableName, options?.caller, {
34
+ leadingKeys: simDynamoDbLeadingKeysOf(() => [
35
+ readSimDynamoDbKey(input.Key),
36
+ ]),
37
+ attributes: simDynamoDbAttributesOf(plan.attributeNames, simDynamoDbItemAttributeNames(() => [readSimDynamoDbKey(input.Key)])),
38
+ });
33
39
  const asked = SimDynamoDbReturnValues.readForUpdate(input.ReturnValues, "UpdateItem");
34
40
  const key = readSimDynamoDbKey(input.Key);
35
41
  const existing = table.getItem(key);
@@ -1,4 +1,4 @@
1
- import { SimDynamoDbProjection } from "../../expression/projection/sim-dynamodb-projection.js";
1
+ import type { SimDynamoDbProjection } from "../../expression/projection/sim-dynamodb-projection.js";
2
2
  import type { SimDynamoDbItem } from "../../item/sim-dynamodb-item.js";
3
3
  import { SimDynamoDbConditionCheck } from "./sim-dynamodb-condition-check.js";
4
4
  import { type SimDynamoDbUpdateExpressionInput } from "./sim-dynamodb-update-expressions.js";
@@ -10,6 +10,12 @@ interface SimDynamoDbUpdatePlanInput extends SimDynamoDbUpdateExpressionInput {
10
10
  */
11
11
  export declare class SimDynamoDbUpdatePlan {
12
12
  readonly check: SimDynamoDbConditionCheck;
13
+ /**
14
+ * The top-level attributes both expressions named, for
15
+ * `dynamodb:Attributes`. They share one set of placeholders, so the update
16
+ * and the condition guarding it are gathered together.
17
+ */
18
+ readonly attributeNames: readonly string[];
13
19
  private readonly update;
14
20
  private constructor();
15
21
  /**
@@ -24,11 +30,7 @@ export declare class SimDynamoDbUpdatePlan {
24
30
  */
25
31
  assertLeavesKeyAlone(keyAttributeNames: readonly string[]): void;
26
32
  /**
27
- * The parts of an item this update touched, for the reporting modes that
28
- * answer with those and nothing else.
29
- *
30
- * A request that says nothing to change touched nothing, so the projection it
31
- * answers with finds nothing in either item.
33
+ * The parts of an item this update touched.
32
34
  */
33
35
  touched(): SimDynamoDbProjection;
34
36
  /**
@@ -1,5 +1,4 @@
1
- import { SimDynamoDbProjection } from "../../expression/projection/sim-dynamodb-projection.js";
2
- import { updateExpressionName } from "../../expression/update/sim-dynamodb-update-refusal.js";
1
+ import { simDynamoDbTouchedBy } from "./sim-dynamodb-update-touched.js";
3
2
  import { SimDynamoDbConditionCheck } from "./sim-dynamodb-condition-check.js";
4
3
  import { readSimDynamoDbUpdateExpressions, } from "./sim-dynamodb-update-expressions.js";
5
4
  /**
@@ -7,10 +6,17 @@ import { readSimDynamoDbUpdateExpressions, } from "./sim-dynamodb-update-express
7
6
  */
8
7
  export class SimDynamoDbUpdatePlan {
9
8
  check;
9
+ /**
10
+ * The top-level attributes both expressions named, for
11
+ * `dynamodb:Attributes`. They share one set of placeholders, so the update
12
+ * and the condition guarding it are gathered together.
13
+ */
14
+ attributeNames;
10
15
  update;
11
16
  constructor(properties) {
12
17
  this.update = properties.update;
13
18
  this.check = properties.check;
19
+ this.attributeNames = properties.attributeNames;
14
20
  }
15
21
  /**
16
22
  * Read the expressions a request carries, before anything is looked up.
@@ -21,15 +27,18 @@ export class SimDynamoDbUpdatePlan {
21
27
  static read(input, operation) {
22
28
  const expression = input.UpdateExpression;
23
29
  if (expression === undefined) {
30
+ const check = SimDynamoDbConditionCheck.read(input, operation);
24
31
  return new this({
25
32
  update: undefined,
26
- check: SimDynamoDbConditionCheck.read(input, operation),
33
+ check,
34
+ attributeNames: check.attributeNames,
27
35
  });
28
36
  }
29
37
  const read = readSimDynamoDbUpdateExpressions(input, expression);
30
38
  return new this({
31
39
  update: read.update,
32
40
  check: SimDynamoDbConditionCheck.of(read.condition, input, operation),
41
+ attributeNames: read.attributes,
33
42
  });
34
43
  }
35
44
  /**
@@ -39,18 +48,10 @@ export class SimDynamoDbUpdatePlan {
39
48
  this.update?.assertLeavesKeyAlone(keyAttributeNames);
40
49
  }
41
50
  /**
42
- * The parts of an item this update touched, for the reporting modes that
43
- * answer with those and nothing else.
44
- *
45
- * A request that says nothing to change touched nothing, so the projection it
46
- * answers with finds nothing in either item.
51
+ * The parts of an item this update touched.
47
52
  */
48
53
  touched() {
49
- return (this.update?.touched() ??
50
- new SimDynamoDbProjection({
51
- expressionName: updateExpressionName,
52
- paths: [],
53
- }));
54
+ return simDynamoDbTouchedBy(this.update);
54
55
  }
55
56
  /**
56
57
  * The item this update makes of the one that was there.
@@ -0,0 +1,10 @@
1
+ import { SimDynamoDbProjection } from "../../expression/projection/sim-dynamodb-projection.js";
2
+ import type { SimDynamoDbUpdate } from "../../expression/update/sim-dynamodb-update.js";
3
+ /**
4
+ * The parts of an item an update touched, for the reporting modes that answer
5
+ * with those and nothing else.
6
+ *
7
+ * A request that says nothing to change touched nothing, so the projection it
8
+ * answers with finds nothing in either item.
9
+ */
10
+ export declare function simDynamoDbTouchedBy(update: SimDynamoDbUpdate | undefined): SimDynamoDbProjection;
@@ -0,0 +1,16 @@
1
+ import { SimDynamoDbProjection } from "../../expression/projection/sim-dynamodb-projection.js";
2
+ import { updateExpressionName } from "../../expression/update/sim-dynamodb-update-refusal.js";
3
+ /**
4
+ * The parts of an item an update touched, for the reporting modes that answer
5
+ * with those and nothing else.
6
+ *
7
+ * A request that says nothing to change touched nothing, so the projection it
8
+ * answers with finds nothing in either item.
9
+ */
10
+ export function simDynamoDbTouchedBy(update) {
11
+ return (update?.touched() ??
12
+ new SimDynamoDbProjection({
13
+ expressionName: updateExpressionName,
14
+ paths: [],
15
+ }));
16
+ }
@@ -9,6 +9,8 @@ export interface SimDynamoDbQueryExpressions {
9
9
  readonly terms: SimDynamoDbKeyConditionTerms;
10
10
  readonly filter: SimDynamoDbFilter | undefined;
11
11
  readonly projection: SimDynamoDbProjection | undefined;
12
+ /** The top-level attributes the expressions named, for `dynamodb:Attributes`. */
13
+ readonly attributes: readonly string[];
12
14
  }
13
15
  /**
14
16
  * Read every expression of a query against one set of placeholders.
@@ -21,7 +21,12 @@ export function readSimDynamoDbQueryExpressions(input) {
21
21
  const filter = filterIn(input.FilterExpression, parameters);
22
22
  const projection = projectionIn(input.ProjectionExpression, parameters);
23
23
  parameters.assertAllUsed();
24
- return { terms, filter, projection };
24
+ return {
25
+ terms,
26
+ filter,
27
+ projection,
28
+ attributes: parameters.attributes.topLevel,
29
+ };
25
30
  }
26
31
  /**
27
32
  * Read the filter a query drops read items by, if it names one.