ag-common 0.0.899 → 0.0.900

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,28 +1,9 @@
1
- interface DynamoTableConstruct {
2
- node: {
3
- _children: {
4
- Resource: {
5
- node: {
6
- scope: unknown;
7
- };
8
- };
9
- };
10
- };
11
- }
12
- /** ensure that dynamo tables in stack dont exceed passed in provisioned limits */
13
- export declare const enforceDynamoProvisionCap: ({ tables, readsMax, writesMax, mustEqual, }: {
14
- tables: DynamoTableConstruct[];
15
- /**
16
- * default 25
17
- */
1
+ import { aws_dynamodb as dynamodb } from "aws-cdk-lib";
2
+ export interface EnforceDynamoProvisionCapOptions {
3
+ tables: dynamodb.Table[];
18
4
  readsMax?: number;
19
- /**
20
- * default 25
21
- */
22
5
  writesMax?: number;
23
- /**
24
- * default false. if true, will throw if cap isnt met. will still throw if exceeds.
25
- */
26
6
  mustEqual?: boolean;
27
- }) => void;
28
- export {};
7
+ }
8
+ /** Ensure the supplied DynamoDB tables do not exceed the provisioned capacity limits. */
9
+ export declare const enforceDynamoProvisionCap: ({ tables, readsMax, writesMax, mustEqual, }: EnforceDynamoProvisionCapOptions) => void;
@@ -1,32 +1,123 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.enforceDynamoProvisionCap = void 0;
4
+ const aws_cdk_lib_1 = require("aws-cdk-lib");
4
5
  const log_1 = require("../../common/helpers/log");
5
- const math_1 = require("../../common/helpers/math");
6
- const json_1 = require("../../common/helpers/string/json");
7
- const trim_1 = require("../../common/helpers/string/trim");
8
- const extractSum = ({ str, regex }) => (0, math_1.sumArray)(str
9
- .match(regex)
10
- ?.map((s2) => (0, trim_1.trim)(s2.substring(s2.indexOf(":") + 1), ":", ",", " "))
11
- .filter((r) => r && Number(r))
12
- .map((r) => Number(r)) ?? []);
13
- /** ensure that dynamo tables in stack dont exceed passed in provisioned limits */
6
+ const ZERO_CAPACITY = {
7
+ reads: 0,
8
+ writes: 0,
9
+ };
10
+ const isResolvable = (value) => "resolve" in value && typeof value.resolve === "function";
11
+ const isRecord = (value) => typeof value === "object" && value !== null;
12
+ const isUnknownArray = (value) => Array.isArray(value);
13
+ const getResolvedCapacity = ({ value, label }) => {
14
+ if (aws_cdk_lib_1.Token.isUnresolved(value)) {
15
+ throw new Error(`Cannot enforce DynamoDB provision cap with unresolved ${label}`);
16
+ }
17
+ return value;
18
+ };
19
+ const getProvisionedThroughput = ({ throughput, label, }) => {
20
+ if (!throughput) {
21
+ return ZERO_CAPACITY;
22
+ }
23
+ if (isResolvable(throughput)) {
24
+ throw new Error(`Cannot enforce DynamoDB provision cap with unresolved ${label}`);
25
+ }
26
+ return {
27
+ reads: getResolvedCapacity({
28
+ value: throughput.readCapacityUnits,
29
+ label: `${label} read capacity`,
30
+ }),
31
+ writes: getResolvedCapacity({
32
+ value: throughput.writeCapacityUnits,
33
+ label: `${label} write capacity`,
34
+ }),
35
+ };
36
+ };
37
+ const getResolvedIndexThroughput = ({ throughput, label, }) => {
38
+ if (throughput === undefined) {
39
+ return ZERO_CAPACITY;
40
+ }
41
+ if (!isRecord(throughput) || isResolvable(throughput)) {
42
+ throw new Error(`Cannot enforce DynamoDB provision cap with unresolved ${label}`);
43
+ }
44
+ const reads = throughput.readCapacityUnits;
45
+ const writes = throughput.writeCapacityUnits;
46
+ if (typeof reads !== "number" || aws_cdk_lib_1.Token.isUnresolved(reads)) {
47
+ throw new Error(`Cannot enforce DynamoDB provision cap with unresolved ${label} read capacity`);
48
+ }
49
+ if (typeof writes !== "number" || aws_cdk_lib_1.Token.isUnresolved(writes)) {
50
+ throw new Error(`Cannot enforce DynamoDB provision cap with unresolved ${label} write capacity`);
51
+ }
52
+ return { reads, writes };
53
+ };
54
+ const getTableResource = (table) => {
55
+ const resource = table.node.defaultChild;
56
+ if (!(resource instanceof aws_cdk_lib_1.aws_dynamodb.CfnTable)) {
57
+ throw new Error(`Unable to inspect DynamoDB table resource: ${table.node.path}`);
58
+ }
59
+ return resource;
60
+ };
61
+ const getGlobalSecondaryIndexCapacity = (table) => {
62
+ const indexes = table.globalSecondaryIndexes;
63
+ if (!indexes) {
64
+ return ZERO_CAPACITY;
65
+ }
66
+ const resolvedIndexes = isResolvable(indexes)
67
+ ? aws_cdk_lib_1.Stack.of(table).resolve(indexes)
68
+ : indexes;
69
+ if (resolvedIndexes === undefined) {
70
+ return ZERO_CAPACITY;
71
+ }
72
+ if (!isUnknownArray(resolvedIndexes)) {
73
+ throw new Error(`Cannot enforce DynamoDB provision cap with unresolved indexes for ${table.node.path}`);
74
+ }
75
+ return resolvedIndexes.reduce((total, index, indexPosition) => {
76
+ if (!isRecord(index) || isResolvable(index)) {
77
+ throw new Error(`Cannot enforce DynamoDB provision cap with unresolved index ${indexPosition} for ${table.node.path}`);
78
+ }
79
+ const capacity = getResolvedIndexThroughput({
80
+ throughput: index.provisionedThroughput,
81
+ label: `${table.node.path} index ${indexPosition}`,
82
+ });
83
+ return {
84
+ reads: total.reads + capacity.reads,
85
+ writes: total.writes + capacity.writes,
86
+ };
87
+ }, { ...ZERO_CAPACITY });
88
+ };
89
+ const getTableCapacity = (table) => {
90
+ const resource = getTableResource(table);
91
+ const tableCapacity = getProvisionedThroughput({
92
+ throughput: resource.provisionedThroughput,
93
+ label: resource.node.path,
94
+ });
95
+ const indexCapacity = getGlobalSecondaryIndexCapacity(resource);
96
+ return {
97
+ reads: tableCapacity.reads + indexCapacity.reads,
98
+ writes: tableCapacity.writes + indexCapacity.writes,
99
+ };
100
+ };
101
+ /** Ensure the supplied DynamoDB tables do not exceed the provisioned capacity limits. */
14
102
  const enforceDynamoProvisionCap = ({ tables, readsMax = 25, writesMax = 25, mustEqual = false, }) => {
15
103
  if (tables.length === 0) {
16
104
  (0, log_1.warn)("error in dynamo FT enforce");
17
105
  return;
18
106
  }
19
- const t = tables[0];
20
- const s = (0, json_1.safeStringify)(t.node._children.Resource.node.scope);
21
- const reads = extractSum({ str: s, regex: /readCapacityUnits.*/gim });
22
- const writes = extractSum({ str: s, regex: /writeCapacityUnits.*/gim });
23
- (0, log_1.warn)(`dynamo table provisioned reads:${reads}/${readsMax}`);
24
- (0, log_1.warn)(`dynamo table provisioned writes:${writes}/${writesMax}`);
25
- if (reads > readsMax || writes > writesMax) {
107
+ const capacity = tables.reduce((total, table) => {
108
+ const tableCapacity = getTableCapacity(table);
109
+ return {
110
+ reads: total.reads + tableCapacity.reads,
111
+ writes: total.writes + tableCapacity.writes,
112
+ };
113
+ }, { ...ZERO_CAPACITY });
114
+ (0, log_1.warn)(`dynamo table provisioned reads:${capacity.reads}/${readsMax}`);
115
+ (0, log_1.warn)(`dynamo table provisioned writes:${capacity.writes}/${writesMax}`);
116
+ if (capacity.reads > readsMax || capacity.writes > writesMax) {
26
117
  throw new Error("exceeded dynamo provision cap");
27
118
  }
28
- if (mustEqual && (reads !== readsMax || writes !== writesMax)) {
29
- throw new Error(`dynamo provision cap not met`);
119
+ if (mustEqual && (capacity.reads !== readsMax || capacity.writes !== writesMax)) {
120
+ throw new Error("dynamo provision cap not met");
30
121
  }
31
122
  };
32
123
  exports.enforceDynamoProvisionCap = enforceDynamoProvisionCap;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ag-common",
3
- "version": "0.0.899",
3
+ "version": "0.0.900",
4
4
  "license": "ISC",
5
5
  "author": "admin@gec.dev",
6
6
  "repository": {
@@ -39,7 +39,6 @@
39
39
  "@radix-ui/react-scroll-area": "^1.2.18",
40
40
  "@radix-ui/react-select": "^2.3.7",
41
41
  "@radix-ui/react-switch": "^1.3.7",
42
- "aws-cdk-lib": "^2.262.1",
43
42
  "buffer": "^6.0.3",
44
43
  "class-variance-authority": "^0.7.1",
45
44
  "clsx": "^2.1.1",
@@ -61,6 +60,8 @@
61
60
  "@types/node": "^26.1.1",
62
61
  "@types/react": "^19.2.17",
63
62
  "@types/react-dom": "^19.2.3",
63
+ "aws-cdk-lib": "^2.265.0",
64
+ "constructs": "^10.8.1",
64
65
  "cross-env": "^10.1.0",
65
66
  "eslint-config-e7npm": "0.1.52",
66
67
  "globstar": "^1.0.0",
@@ -73,6 +74,10 @@
73
74
  "tsx": "^4.23.12",
74
75
  "typescript": "^7.0.2"
75
76
  },
77
+ "peerDependencies": {
78
+ "aws-cdk-lib": "^2.262.1",
79
+ "constructs": "^10.0.0"
80
+ },
76
81
  "scripts": {
77
82
  "preinstall": "npx only-allow pnpm",
78
83
  "format": "e7-oxfmt --config .oxfmtrc.json --disable-nested-config .",
@@ -81,6 +86,6 @@
81
86
  "build": "rimraf dist && tsc -p tsconfig.build.json",
82
87
  "start": "cross-env BROWSER=none cross-env storybook dev -p 6006",
83
88
  "build-storybook": "storybook build -o docs --quiet",
84
- "test": "globstar -- node --import tsx --test \"src/**/*.test.ts\""
89
+ "test": "globstar -- node --import tsx --test \"src/tests/**/*.test.ts\""
85
90
  }
86
91
  }