@aws/nx-plugin 1.0.0-rc.3 → 1.0.0-rc.5

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 (138) hide show
  1. package/LICENSE-THIRD-PARTY +1437 -41
  2. package/executors.json +9 -0
  3. package/generators.json +34 -0
  4. package/package.json +5 -1
  5. package/sdk/license.d.ts +8 -0
  6. package/sdk/license.js +13 -0
  7. package/sdk/license.js.map +1 -0
  8. package/src/connection/generator.d.ts +1 -1
  9. package/src/connection/generator.js +19 -0
  10. package/src/connection/generator.js.map +1 -1
  11. package/src/license/__snapshots__/config.spec.ts.snap +23 -0
  12. package/src/license/config-types.d.ts +17 -0
  13. package/src/license/config.d.ts +63 -5
  14. package/src/license/config.js +315 -7
  15. package/src/license/config.js.map +1 -1
  16. package/src/license/dependency-check/check.d.ts +12 -0
  17. package/src/license/dependency-check/check.js +111 -0
  18. package/src/license/dependency-check/check.js.map +1 -0
  19. package/src/license/dependency-check/collectors/collector.d.ts +21 -0
  20. package/src/license/dependency-check/collectors/collector.js +76 -0
  21. package/src/license/dependency-check/collectors/collector.js.map +1 -0
  22. package/src/license/dependency-check/collectors/npm-collector.d.ts +10 -0
  23. package/src/license/dependency-check/collectors/npm-collector.js +75 -0
  24. package/src/license/dependency-check/collectors/npm-collector.js.map +1 -0
  25. package/src/license/dependency-check/collectors/python-collector.d.ts +19 -0
  26. package/src/license/dependency-check/collectors/python-collector.js +121 -0
  27. package/src/license/dependency-check/collectors/python-collector.js.map +1 -0
  28. package/src/license/dependency-check/evaluator.d.ts +9 -0
  29. package/src/license/dependency-check/evaluator.js +122 -0
  30. package/src/license/dependency-check/evaluator.js.map +1 -0
  31. package/src/license/dependency-check/executor-schema.json +6 -0
  32. package/src/license/dependency-check/executor.d.ts +9 -0
  33. package/src/license/dependency-check/executor.js +43 -0
  34. package/src/license/dependency-check/executor.js.map +1 -0
  35. package/src/license/dependency-check/pre-approved.d.ts +6 -0
  36. package/src/license/dependency-check/pre-approved.js +1280 -0
  37. package/src/license/dependency-check/pre-approved.js.map +1 -0
  38. package/src/license/dependency-check/types.d.ts +45 -0
  39. package/src/license/dependency-check/types.js +7 -0
  40. package/src/license/dependency-check/types.js.map +1 -0
  41. package/src/license/generator.js +110 -11
  42. package/src/license/generator.js.map +1 -1
  43. package/src/license/index.d.ts +8 -0
  44. package/src/license/index.js +13 -0
  45. package/src/license/index.js.map +1 -0
  46. package/src/license/known-exceptions.d.ts +7 -0
  47. package/src/license/known-exceptions.js +33 -0
  48. package/src/license/known-exceptions.js.map +1 -0
  49. package/src/license/schema.d.ts +5 -0
  50. package/src/license/schema.json +6 -0
  51. package/src/license/sync/generator.js +10 -10
  52. package/src/license/sync/generator.js.map +1 -1
  53. package/src/license/sync/project-file-sync.d.ts +2 -2
  54. package/src/license/sync/project-file-sync.js.map +1 -1
  55. package/src/preset/__snapshots__/generator.spec.ts.snap +70 -0
  56. package/src/preset/generator.d.ts +1 -1
  57. package/src/preset/generator.js +5 -1
  58. package/src/preset/generator.js.map +1 -1
  59. package/src/preset/schema.d.ts +1 -0
  60. package/src/preset/schema.json +5 -0
  61. package/src/py/agent/generator.js +5 -0
  62. package/src/py/agent/generator.js.map +1 -1
  63. package/src/py/mcp-server/generator.js +3 -0
  64. package/src/py/mcp-server/generator.js.map +1 -1
  65. package/src/py/project/generator.js +7 -0
  66. package/src/py/project/generator.js.map +1 -1
  67. package/src/ts/dynamodb/__snapshots__/generator.spec.ts.snap +1502 -0
  68. package/src/ts/dynamodb/agent-connection/__snapshots__/generator.spec.ts.snap +45 -0
  69. package/src/ts/dynamodb/agent-connection/generator.d.ts +10 -0
  70. package/src/ts/dynamodb/agent-connection/generator.js +32 -0
  71. package/src/ts/dynamodb/agent-connection/generator.js.map +1 -0
  72. package/src/ts/dynamodb/agent-connection/schema.d.ts +14 -0
  73. package/src/ts/dynamodb/agent-connection/schema.json +22 -0
  74. package/src/ts/dynamodb/files/scripts/create-local-table.ts.template +161 -0
  75. package/src/ts/{rdb/files/scripts/docker-pull.ts.template → dynamodb/files/scripts/pull-image.ts.template} +1 -1
  76. package/src/ts/dynamodb/files/scripts/start-container.ts.template +81 -0
  77. package/src/ts/dynamodb/files/src/client.ts.template +45 -0
  78. package/src/ts/dynamodb/files/src/constants.ts.template +9 -0
  79. package/src/ts/dynamodb/files/src/entities/example.ts.template +47 -0
  80. package/src/ts/dynamodb/files/src/entities/index.ts.template +1 -0
  81. package/src/ts/dynamodb/files/src/gsi.ts.template +14 -0
  82. package/src/ts/dynamodb/files/src/index.ts.template +11 -0
  83. package/src/ts/dynamodb/generator.d.ts +10 -0
  84. package/src/ts/dynamodb/generator.js +108 -0
  85. package/src/ts/dynamodb/generator.js.map +1 -0
  86. package/src/ts/dynamodb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +45 -0
  87. package/src/ts/dynamodb/mcp-server-connection/generator.d.ts +10 -0
  88. package/src/ts/dynamodb/mcp-server-connection/generator.js +32 -0
  89. package/src/ts/dynamodb/mcp-server-connection/generator.js.map +1 -0
  90. package/src/ts/dynamodb/mcp-server-connection/schema.d.ts +14 -0
  91. package/src/ts/dynamodb/mcp-server-connection/schema.json +22 -0
  92. package/src/ts/dynamodb/schema.d.ts +14 -0
  93. package/src/ts/dynamodb/schema.json +52 -0
  94. package/src/ts/dynamodb/smithy-connection/__snapshots__/generator.spec.ts.snap +23 -0
  95. package/src/ts/dynamodb/smithy-connection/generator.d.ts +10 -0
  96. package/src/ts/dynamodb/smithy-connection/generator.js +30 -0
  97. package/src/ts/dynamodb/smithy-connection/generator.js.map +1 -0
  98. package/src/ts/dynamodb/smithy-connection/schema.d.ts +12 -0
  99. package/src/ts/dynamodb/smithy-connection/schema.json +18 -0
  100. package/src/ts/dynamodb/trpc-connection/__snapshots__/generator.spec.ts.snap +23 -0
  101. package/src/ts/dynamodb/trpc-connection/generator.d.ts +10 -0
  102. package/src/ts/dynamodb/trpc-connection/generator.js +30 -0
  103. package/src/ts/dynamodb/trpc-connection/generator.js.map +1 -0
  104. package/src/ts/dynamodb/trpc-connection/schema.d.ts +12 -0
  105. package/src/ts/dynamodb/trpc-connection/schema.json +18 -0
  106. package/src/ts/lib/ts-project-utils.js +6 -0
  107. package/src/ts/lib/ts-project-utils.js.map +1 -1
  108. package/src/ts/mcp-server/generator.js +3 -0
  109. package/src/ts/mcp-server/generator.js.map +1 -1
  110. package/src/ts/rdb/__snapshots__/generator.spec.ts.snap +22 -38
  111. package/src/ts/rdb/files/scripts/pull-image.ts.template +15 -0
  112. package/src/ts/rdb/files/scripts/{docker-start.ts.template → start-container.ts.template} +23 -23
  113. package/src/ts/rdb/generator.js +8 -8
  114. package/src/ts/rdb/generator.js.map +1 -1
  115. package/src/utils/ast.d.ts +28 -0
  116. package/src/utils/ast.js +59 -1
  117. package/src/utils/ast.js.map +1 -1
  118. package/src/utils/config/utils.d.ts +11 -1
  119. package/src/utils/config/utils.js +37 -5
  120. package/src/utils/config/utils.js.map +1 -1
  121. package/src/utils/dynamodb-constructs/dynamodb-constructs.d.ts +18 -0
  122. package/src/utils/dynamodb-constructs/dynamodb-constructs.js +57 -0
  123. package/src/utils/dynamodb-constructs/dynamodb-constructs.js.map +1 -0
  124. package/src/utils/dynamodb-constructs/files/cdk/app/dynamodb/__nameKebabCase__.ts.template +30 -0
  125. package/src/utils/dynamodb-constructs/files/cdk/core/dynamodb.ts.template +105 -0
  126. package/src/utils/dynamodb-constructs/files/terraform/app/dynamodb/__nameKebabCase__/__nameKebabCase__.tf.template +65 -0
  127. package/src/utils/dynamodb-constructs/files/terraform/core/dynamodb/dynamodb.tf.template +155 -0
  128. package/src/utils/mcp.d.ts +25 -0
  129. package/src/utils/mcp.js +81 -0
  130. package/src/utils/mcp.js.map +1 -0
  131. package/src/utils/port.d.ts +6 -0
  132. package/src/utils/port.js +23 -2
  133. package/src/utils/port.js.map +1 -1
  134. package/src/utils/rdb-constructs/files/terraform/app/dbs/__nameKebabCase__/__nameKebabCase__.tf.template +4 -4
  135. package/src/utils/rdb-constructs/rdb-constructs.d.ts +1 -1
  136. package/src/utils/versions.d.ts +2 -1
  137. package/src/utils/versions.js +1 -0
  138. package/src/utils/versions.js.map +1 -1
@@ -0,0 +1,45 @@
1
+ // Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2
+
3
+ exports[`ts#dynamodb agent-connection generator > should add dynamodb serve-local dependency to agent serve-local 1`] = `
4
+ {
5
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
6
+ "name": "my-agent",
7
+ "root": "packages/my-agent",
8
+ "targets": {
9
+ "agent-serve-local": {
10
+ "continuous": true,
11
+ "dependsOn": [
12
+ {
13
+ "projects": [
14
+ "db",
15
+ ],
16
+ "target": "serve-local",
17
+ },
18
+ ],
19
+ "executor": "nx:run-commands",
20
+ },
21
+ },
22
+ }
23
+ `;
24
+
25
+ exports[`ts#dynamodb agent-connection generator > should use sourceComponent name when provided 1`] = `
26
+ {
27
+ "$schema": "../../node_modules/nx/schemas/project-schema.json",
28
+ "name": "my-agent",
29
+ "root": "packages/my-agent",
30
+ "targets": {
31
+ "custom-agent-serve-local": {
32
+ "continuous": true,
33
+ "dependsOn": [
34
+ {
35
+ "projects": [
36
+ "db",
37
+ ],
38
+ "target": "serve-local",
39
+ },
40
+ ],
41
+ "executor": "nx:run-commands",
42
+ },
43
+ },
44
+ }
45
+ `;
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { Tree } from '@nx/devkit';
6
+ import { TsDynamoDBAgentConnectionGeneratorSchema } from './schema';
7
+ import { NxGeneratorInfo } from '../../../utils/nx';
8
+ export declare const TS_DYNAMODB_AGENT_CONNECTION_GENERATOR_INFO: NxGeneratorInfo;
9
+ export declare const tsDynamoDBAgentConnectionGenerator: (tree: Tree, options: TsDynamoDBAgentConnectionGeneratorSchema) => Promise<void>;
10
+ export default tsDynamoDBAgentConnectionGenerator;
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tsDynamoDBAgentConnectionGenerator = exports.TS_DYNAMODB_AGENT_CONNECTION_GENERATOR_INFO = void 0;
4
+ /**
5
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
6
+ * SPDX-License-Identifier: Apache-2.0
7
+ */
8
+ const devkit_1 = require("@nx/devkit");
9
+ const nx_1 = require("../../../utils/nx");
10
+ const metrics_1 = require("../../../utils/metrics");
11
+ const format_1 = require("../../../utils/format");
12
+ exports.TS_DYNAMODB_AGENT_CONNECTION_GENERATOR_INFO = (0, nx_1.getGeneratorInfo)(__filename);
13
+ const tsDynamoDBAgentConnectionGenerator = async (tree, options) => {
14
+ const sourceProject = (0, nx_1.readProjectConfigurationUnqualified)(tree, options.sourceProject);
15
+ const targetProject = (0, nx_1.readProjectConfigurationUnqualified)(tree, options.targetProject);
16
+ const agentName = options.sourceComponent?.name ?? 'agent';
17
+ const serveLocalTarget = `${agentName}-serve-local`;
18
+ if (sourceProject.targets?.[serveLocalTarget]) {
19
+ (0, nx_1.addDependencyToTargetIfNotPresent)(sourceProject, serveLocalTarget, {
20
+ projects: [targetProject.name],
21
+ target: 'serve-local',
22
+ });
23
+ (0, devkit_1.updateProjectConfiguration)(tree, sourceProject.name, sourceProject);
24
+ }
25
+ await (0, metrics_1.addGeneratorMetricsIfApplicable)(tree, [
26
+ exports.TS_DYNAMODB_AGENT_CONNECTION_GENERATOR_INFO,
27
+ ]);
28
+ await (0, format_1.formatFilesInSubtree)(tree);
29
+ };
30
+ exports.tsDynamoDBAgentConnectionGenerator = tsDynamoDBAgentConnectionGenerator;
31
+ exports.default = exports.tsDynamoDBAgentConnectionGenerator;
32
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../../packages/nx-plugin/src/ts/dynamodb/agent-connection/generator.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,uCAA8D;AAE9D,0CAK2B;AAC3B,oDAAyE;AACzE,kDAA6D;AAEhD,QAAA,2CAA2C,GACtD,IAAA,qBAAgB,EAAC,UAAU,CAAC,CAAC;AAExB,MAAM,kCAAkC,GAAG,KAAK,EACrD,IAAU,EACV,OAAiD,EAClC,EAAE;IACjB,MAAM,aAAa,GAAG,IAAA,wCAAmC,EACvD,IAAI,EACJ,OAAO,CAAC,aAAa,CACtB,CAAC;IACF,MAAM,aAAa,GAAG,IAAA,wCAAmC,EACvD,IAAI,EACJ,OAAO,CAAC,aAAa,CACtB,CAAC;IAEF,MAAM,SAAS,GAAG,OAAO,CAAC,eAAe,EAAE,IAAI,IAAI,OAAO,CAAC;IAC3D,MAAM,gBAAgB,GAAG,GAAG,SAAS,cAAc,CAAC;IAEpD,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAC9C,IAAA,sCAAiC,EAAC,aAAa,EAAE,gBAAgB,EAAE;YACjE,QAAQ,EAAE,CAAC,aAAa,CAAC,IAAI,CAAC;YAC9B,MAAM,EAAE,aAAa;SACtB,CAAC,CAAC;QACH,IAAA,mCAA0B,EAAC,IAAI,EAAE,aAAa,CAAC,IAAI,EAAE,aAAa,CAAC,CAAC;IACtE,CAAC;IAED,MAAM,IAAA,yCAA+B,EAAC,IAAI,EAAE;QAC1C,mDAA2C;KAC5C,CAAC,CAAC;IACH,MAAM,IAAA,6BAAoB,EAAC,IAAI,CAAC,CAAC;AACnC,CAAC,CAAC;AA5BW,QAAA,kCAAkC,sCA4B7C;AAEF,kBAAe,0CAAkC,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { ComponentMetadata } from '../../../utils/nx';
6
+
7
+ /**
8
+ * TypeScript types for options defined in schema.json
9
+ */
10
+ export interface TsDynamoDBAgentConnectionGeneratorSchema {
11
+ sourceProject: string;
12
+ targetProject: string;
13
+ sourceComponent?: ComponentMetadata;
14
+ }
@@ -0,0 +1,22 @@
1
+ {
2
+ "$schema": "https://json-schema.org/schema",
3
+ "$id": "ts#dynamodb#agent-connection",
4
+ "title": "ts#dynamodb#agent-connection",
5
+ "description": "Connect a ts#agent to a ts#dynamodb project",
6
+ "type": "object",
7
+ "properties": {
8
+ "sourceProject": {
9
+ "type": "string",
10
+ "description": "The project containing the ts#agent to connect from"
11
+ },
12
+ "targetProject": {
13
+ "type": "string",
14
+ "description": "The ts#dynamodb project to connect to"
15
+ },
16
+ "sourceComponent": {
17
+ "type": "string",
18
+ "description": "The agent component name"
19
+ }
20
+ },
21
+ "required": ["sourceProject", "targetProject"]
22
+ }
@@ -0,0 +1,161 @@
1
+ import {
2
+ CreateTableCommand,
3
+ DescribeTableCommand,
4
+ DynamoDBClient,
5
+ GlobalSecondaryIndexDescription,
6
+ ResourceInUseException,
7
+ UpdateTableCommand,
8
+ } from '@aws-sdk/client-dynamodb';
9
+ import { GLOBAL_SECONDARY_INDEXES } from '../src/gsi.js';
10
+
11
+ const [tableName, port] = process.argv.slice(2);
12
+ const endpoint = `http://localhost:${port}`;
13
+
14
+ const REQUEST_TIMEOUT_MS = 10_000;
15
+ const timeoutSignal = () => AbortSignal.timeout(REQUEST_TIMEOUT_MS);
16
+
17
+ const client = new DynamoDBClient({
18
+ endpoint,
19
+ region: 'us-east-1',
20
+ credentials: {
21
+ accessKeyId: 'accessKeyId',
22
+ secretAccessKey: 'secretAccessKey',
23
+ },
24
+ });
25
+
26
+ const waitForTableActive = async (
27
+ waitClient: DynamoDBClient,
28
+ waitTableName: string,
29
+ ): Promise<void> => {
30
+ for (;;) {
31
+ const { Table } = await waitClient.send(
32
+ new DescribeTableCommand({ TableName: waitTableName }),
33
+ { abortSignal: timeoutSignal() },
34
+ );
35
+ const allActive =
36
+ Table?.TableStatus === 'ACTIVE' &&
37
+ (Table?.GlobalSecondaryIndexes ?? []).every(
38
+ (g: GlobalSecondaryIndexDescription) => g.IndexStatus === 'ACTIVE',
39
+ );
40
+ if (allActive) return;
41
+ await new Promise((resolve) => setTimeout(resolve, 500));
42
+ }
43
+ };
44
+
45
+ const ensureTable = async (
46
+ tblClient: DynamoDBClient,
47
+ tblName: string,
48
+ gsIs: { indexName: string; partitionKey: string; sortKey: string }[],
49
+ ): Promise<void> => {
50
+ const gsiAttributeDefinitions = gsIs.flatMap((gsi) => [
51
+ { AttributeName: gsi.partitionKey, AttributeType: 'S' as const },
52
+ { AttributeName: gsi.sortKey, AttributeType: 'S' as const },
53
+ ]);
54
+ const attributeDefinitions = [
55
+ { AttributeName: 'pk', AttributeType: 'S' as const },
56
+ { AttributeName: 'sk', AttributeType: 'S' as const },
57
+ ...gsiAttributeDefinitions.filter(
58
+ (a, i, arr) =>
59
+ arr.findIndex((b) => b.AttributeName === a.AttributeName) === i,
60
+ ),
61
+ ];
62
+ const globalSecondaryIndexes = gsIs.map((gsi) => ({
63
+ IndexName: gsi.indexName,
64
+ KeySchema: [
65
+ { AttributeName: gsi.partitionKey, KeyType: 'HASH' as const },
66
+ { AttributeName: gsi.sortKey, KeyType: 'RANGE' as const },
67
+ ],
68
+ Projection: { ProjectionType: 'ALL' as const },
69
+ }));
70
+
71
+ for (let attempt = 0; attempt < 30; attempt++) {
72
+ try {
73
+ // New table: create with all GSIs in one shot.
74
+ await tblClient.send(
75
+ new CreateTableCommand({
76
+ TableName: tblName,
77
+ AttributeDefinitions: attributeDefinitions,
78
+ KeySchema: [
79
+ { AttributeName: 'pk', KeyType: 'HASH' },
80
+ { AttributeName: 'sk', KeyType: 'RANGE' },
81
+ ],
82
+ BillingMode: 'PAY_PER_REQUEST',
83
+ ...(globalSecondaryIndexes.length > 0 && {
84
+ GlobalSecondaryIndexes: globalSecondaryIndexes,
85
+ }),
86
+ }),
87
+ { abortSignal: timeoutSignal() },
88
+ );
89
+ console.log(`Created table: ${tblName}`);
90
+ for (const gsi of globalSecondaryIndexes) {
91
+ console.log(`Created GSI: ${gsi.IndexName}`);
92
+ }
93
+ return;
94
+ } catch (e) {
95
+ // Table already exists: sync GSIs to match desired state.
96
+ if (e instanceof ResourceInUseException) {
97
+ const { Table } = await tblClient.send(
98
+ new DescribeTableCommand({ TableName: tblName }),
99
+ { abortSignal: timeoutSignal() },
100
+ );
101
+ const existingGSIs = Table?.GlobalSecondaryIndexes ?? [];
102
+ const existingNames = new Set(
103
+ existingGSIs.map(
104
+ (g: GlobalSecondaryIndexDescription) => g.IndexName ?? '',
105
+ ),
106
+ );
107
+ const desiredNames = new Set(gsIs.map((g) => g.indexName));
108
+
109
+ // DynamoDB Local only allows one GSI to be created or deleted at a time.
110
+ for (const existing of existingGSIs) {
111
+ if (!existing.IndexName || desiredNames.has(existing.IndexName))
112
+ continue;
113
+ await tblClient.send(
114
+ new UpdateTableCommand({
115
+ TableName: tblName,
116
+ GlobalSecondaryIndexUpdates: [
117
+ { Delete: { IndexName: existing.IndexName } },
118
+ ],
119
+ }),
120
+ { abortSignal: timeoutSignal() },
121
+ );
122
+ console.log(`Deleted GSI: ${existing.IndexName}`);
123
+ await waitForTableActive(tblClient, tblName);
124
+ }
125
+
126
+ for (const gsi of gsIs) {
127
+ if (existingNames.has(gsi.indexName)) continue;
128
+ await tblClient.send(
129
+ new UpdateTableCommand({
130
+ TableName: tblName,
131
+ AttributeDefinitions: [
132
+ { AttributeName: gsi.partitionKey, AttributeType: 'S' },
133
+ { AttributeName: gsi.sortKey, AttributeType: 'S' },
134
+ ],
135
+ GlobalSecondaryIndexUpdates: [
136
+ {
137
+ Create: {
138
+ IndexName: gsi.indexName,
139
+ KeySchema: [
140
+ { AttributeName: gsi.partitionKey, KeyType: 'HASH' },
141
+ { AttributeName: gsi.sortKey, KeyType: 'RANGE' },
142
+ ],
143
+ Projection: { ProjectionType: 'ALL' },
144
+ },
145
+ },
146
+ ],
147
+ }),
148
+ { abortSignal: timeoutSignal() },
149
+ );
150
+ console.log(`Created GSI: ${gsi.indexName}`);
151
+ await waitForTableActive(tblClient, tblName);
152
+ }
153
+ return;
154
+ }
155
+ if (attempt === 29) throw e;
156
+ await new Promise((resolve) => setTimeout(resolve, 1000));
157
+ }
158
+ }
159
+ };
160
+
161
+ await ensureTable(client, tableName, GLOBAL_SECONDARY_INDEXES);
@@ -1,6 +1,6 @@
1
1
  import { spawnSync } from 'child_process';
2
2
 
3
- const ENGINE = '<%= containers %>';
3
+ const ENGINE = '<%= containerEngine %>';
4
4
  const image = process.argv[2];
5
5
 
6
6
  const inspect = spawnSync(ENGINE, ['image', 'inspect', image], {
@@ -0,0 +1,81 @@
1
+ import { spawn, spawnSync } from 'child_process';
2
+
3
+ const ENGINE = '<%= containerEngine %>';
4
+
5
+ const [containerName, image, hostPort] = process.argv.slice(2);
6
+
7
+ const containerExists = (): boolean =>
8
+ spawnSync(ENGINE, ['container', 'inspect', containerName], {
9
+ stdio: 'ignore',
10
+ }).status === 0;
11
+
12
+ const isRunning = (): boolean => {
13
+ const result = spawnSync(
14
+ ENGINE,
15
+ ['container', 'inspect', '-f', '{{.State.Running}}', containerName],
16
+ { encoding: 'utf-8' },
17
+ );
18
+ return result.status === 0 && result.stdout.trim() === 'true';
19
+ };
20
+
21
+ // Multiple dynamo table packages run serve-local in parallel sharing the same
22
+ // container. All processes stream logs, but only the one that created the
23
+ // container stops it on exit — others just detach.
24
+ let created = false;
25
+ let runRetries = 0;
26
+
27
+ for (;;) {
28
+ if (containerExists()) {
29
+ if (!isRunning()) {
30
+ // Another process may have started it between our inspect and start — container engine start
31
+ // exits 0 even if the container is already running, so no special-casing needed.
32
+ const start = spawnSync(ENGINE, ['start', containerName], {
33
+ stdio: 'inherit',
34
+ });
35
+ if (start.status !== 0) process.exit(start.status ?? 1);
36
+ }
37
+ break;
38
+ }
39
+ const runArgs = (
40
+ `run <%- containerEngine === 'docker' ? '--rm ' : '' %>--name ${containerName} -u root -w /home/dynamodblocal` +
41
+ ` -p ${hostPort}:${hostPort} -v ${containerName}-data:/home/dynamodblocal/data` +
42
+ ` -d ${image} -jar DynamoDBLocal.jar -sharedDb -dbPath ./data -port ${hostPort} -optimizeDbBeforeStartup`
43
+ ).split(' ');
44
+ const create = spawnSync(ENGINE, runArgs, { stdio: 'pipe' });
45
+ if (create.status === 0) {
46
+ created = true;
47
+ break;
48
+ }
49
+ if (!containerExists() && ++runRetries > 3) {
50
+ process.stderr.write(create.stderr);
51
+ process.exit(create.status ?? 1);
52
+ }
53
+ }
54
+
55
+ const logs = spawn(ENGINE, ['logs', '-f', containerName], {
56
+ stdio: ['ignore', 'inherit', 'inherit'],
57
+ });
58
+
59
+ let exiting = false;
60
+
61
+ const cleanup = () => {
62
+ if (exiting) return;
63
+ exiting = true;
64
+ if (created) {
65
+ <%_ if (containerEngine === 'docker') { _%>
66
+ spawnSync(ENGINE, ['stop', containerName], { stdio: 'ignore' });
67
+ <%_ } else { _%>
68
+ spawnSync(ENGINE, ['rm', '-f', containerName], { stdio: 'ignore' });
69
+ <%_ } _%>
70
+ }
71
+ logs.kill();
72
+ process.exit(0);
73
+ };
74
+
75
+ process.on('SIGTERM', cleanup);
76
+ process.on('SIGINT', cleanup);
77
+ process.on('SIGHUP', cleanup);
78
+
79
+ logs.on('exit', (code) => {
80
+ if (!exiting) process.exit(code ?? 0);
81
+ });
@@ -0,0 +1,45 @@
1
+ import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
2
+ import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';
3
+ import { PACKAGE_NAME, LOCAL_ACCESS_KEY_ID, LOCAL_ENDPOINT, LOCAL_REGION, LOCAL_SECRET_ACCESS_KEY, LOCAL_TABLE_NAME } from './constants.js';
4
+
5
+ let _client: DynamoDBClient | undefined;
6
+
7
+ export const getDynamoDBClient = (): DynamoDBClient => {
8
+ _client ??= process.env.SERVE_LOCAL === 'true'
9
+ ? new DynamoDBClient({
10
+ endpoint: LOCAL_ENDPOINT,
11
+ region: LOCAL_REGION,
12
+ credentials: { accessKeyId: LOCAL_ACCESS_KEY_ID, secretAccessKey: LOCAL_SECRET_ACCESS_KEY },
13
+ })
14
+ : new DynamoDBClient({});
15
+ return _client;
16
+ };
17
+
18
+ type DynamoDBConfig = {
19
+ tableName: string;
20
+ };
21
+
22
+ let _tableName: string | undefined;
23
+
24
+ export const resolveTableName = async (): Promise<string> => {
25
+ if (process.env.SERVE_LOCAL === 'true') {
26
+ return LOCAL_TABLE_NAME;
27
+ }
28
+ if (_tableName === undefined) {
29
+ const appId = process.env.RUNTIME_CONFIG_APP_ID;
30
+ if (!appId) {
31
+ throw new Error('RUNTIME_CONFIG_APP_ID environment variable is not set');
32
+ }
33
+ const config = await getAppConfig<{ [key: string]: DynamoDBConfig | undefined }>('dynamodb', {
34
+ application: appId,
35
+ environment: 'default',
36
+ transform: 'json',
37
+ });
38
+ const tableName = config?.[PACKAGE_NAME]?.tableName;
39
+ if (!tableName) {
40
+ throw new Error('Could not resolve table name from runtime config');
41
+ }
42
+ _tableName = tableName;
43
+ }
44
+ return _tableName!;
45
+ };
@@ -0,0 +1,9 @@
1
+ export const PACKAGE_NAME = '<%= runtimeConfigKey %>';
2
+
3
+ // Local development connection details (used when SERVE_LOCAL=true, see serve-local Nx target)
4
+ export const LOCAL_PORT = <%= localDynamoPort %>;
5
+ export const LOCAL_REGION = 'us-east-1';
6
+ export const LOCAL_ACCESS_KEY_ID = 'accessKeyId';
7
+ export const LOCAL_SECRET_ACCESS_KEY = 'secretAccessKey';
8
+ export const LOCAL_ENDPOINT = `http://localhost:${LOCAL_PORT}`;
9
+ export const LOCAL_TABLE_NAME = '<%= localTableName %>';
@@ -0,0 +1,47 @@
1
+ import { Entity } from 'electrodb';
2
+ import { getDynamoDBClient, resolveTableName } from '../client.js';
3
+
4
+ // Example entity - replace with your own entities using ElectroDB
5
+ // See https://electrodb.dev/en/modeling/entities/ for documentation
6
+ export const createExampleEntity = async () =>
7
+ new Entity(
8
+ {
9
+ model: {
10
+ entity: 'example',
11
+ version: '1',
12
+ service: '<%= runtimeConfigKey %>',
13
+ },
14
+ attributes: {
15
+ id: {
16
+ type: 'string',
17
+ required: true,
18
+ },
19
+ createdAt: {
20
+ type: 'string',
21
+ required: true,
22
+ default: () => new Date().toISOString(),
23
+ readOnly: true,
24
+ },
25
+ updatedAt: {
26
+ type: 'string',
27
+ required: true,
28
+ default: () => new Date().toISOString(),
29
+ watch: '*',
30
+ set: () => new Date().toISOString(),
31
+ },
32
+ },
33
+ indexes: {
34
+ primary: {
35
+ pk: {
36
+ field: 'pk',
37
+ composite: ['id'],
38
+ },
39
+ sk: {
40
+ field: 'sk',
41
+ composite: [],
42
+ },
43
+ },
44
+ },
45
+ },
46
+ { client: getDynamoDBClient(), table: await resolveTableName() },
47
+ );
@@ -0,0 +1 @@
1
+ export { createExampleEntity } from './example.js';
@@ -0,0 +1,14 @@
1
+ // GSIs to synchronize on the local table at startup and provision in infrastructure.
2
+ // ElectroDB names GSI keys gsi{n}pk / gsi{n}sk — add one entry per index defined in your entities.
3
+ export const GLOBAL_SECONDARY_INDEXES = [
4
+ {
5
+ indexName: 'gsi1pk-gsi1sk-index',
6
+ partitionKey: 'gsi1pk',
7
+ sortKey: 'gsi1sk',
8
+ },
9
+ {
10
+ indexName: 'gsi2pk-gsi2sk-index',
11
+ partitionKey: 'gsi2pk',
12
+ sortKey: 'gsi2sk',
13
+ },
14
+ ];
@@ -0,0 +1,11 @@
1
+ export {
2
+ PACKAGE_NAME,
3
+ LOCAL_ACCESS_KEY_ID,
4
+ LOCAL_ENDPOINT,
5
+ LOCAL_REGION,
6
+ LOCAL_SECRET_ACCESS_KEY,
7
+ LOCAL_TABLE_NAME,
8
+ } from './constants.js';
9
+ export { getDynamoDBClient, resolveTableName } from './client.js';
10
+ export { GLOBAL_SECONDARY_INDEXES } from './gsi.js';
11
+ export * from './entities/index.js';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+ import { GeneratorCallback, Tree } from '@nx/devkit';
6
+ import { TsDynamoDBGeneratorSchema } from './schema';
7
+ import { NxGeneratorInfo } from '../../utils/nx';
8
+ export declare const TS_DYNAMODB_GENERATOR_INFO: NxGeneratorInfo;
9
+ export declare const tsDynamoDBGenerator: (tree: Tree, options: TsDynamoDBGeneratorSchema) => Promise<GeneratorCallback>;
10
+ export default tsDynamoDBGenerator;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.tsDynamoDBGenerator = exports.TS_DYNAMODB_GENERATOR_INFO = void 0;
4
+ const tslib_1 = require("tslib");
5
+ /**
6
+ * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
7
+ * SPDX-License-Identifier: Apache-2.0
8
+ */
9
+ const devkit_1 = require("@nx/devkit");
10
+ const nx_1 = require("../../utils/nx");
11
+ const metrics_1 = require("../../utils/metrics");
12
+ const format_1 = require("../../utils/format");
13
+ const shared_constructs_1 = require("../../utils/shared-constructs");
14
+ const dynamodb_constructs_1 = require("../../utils/dynamodb-constructs/dynamodb-constructs");
15
+ const names_1 = require("../../utils/names");
16
+ const npm_scope_1 = require("../../utils/npm-scope");
17
+ const generator_1 = require("../lib/generator");
18
+ const generator_2 = tslib_1.__importDefault(require("../lib/generator"));
19
+ const iac_1 = require("../../utils/iac");
20
+ const containers_1 = require("../../utils/containers");
21
+ const npm_scope_2 = require("../../utils/npm-scope");
22
+ const versions_1 = require("../../utils/versions");
23
+ const port_1 = require("../../utils/port");
24
+ exports.TS_DYNAMODB_GENERATOR_INFO = (0, nx_1.getGeneratorInfo)(__filename);
25
+ const DYNAMODB_LOCAL_IMAGE = 'public.ecr.aws/aws-dynamodb-local/aws-dynamodb-local:latest';
26
+ const tsDynamoDBGenerator = async (tree, options) => {
27
+ const nameKebabCase = (0, names_1.kebabCase)(options.name);
28
+ const nameClassName = (0, names_1.toClassName)(options.name);
29
+ const localTableName = `${(0, npm_scope_2.getNpmScope)(tree)}-${(0, names_1.kebabCase)(options.tableName ?? options.name)}`;
30
+ const containerEngine = await (0, containers_1.resolveContainers)(tree, 'inherit');
31
+ const { fullyQualifiedName, dir } = (0, generator_1.getTsLibDetails)(tree, {
32
+ name: options.name,
33
+ directory: options.directory,
34
+ subDirectory: options.subDirectory,
35
+ });
36
+ let projectExists;
37
+ try {
38
+ (0, devkit_1.readProjectConfiguration)(tree, fullyQualifiedName);
39
+ projectExists = true;
40
+ }
41
+ catch {
42
+ projectExists = false;
43
+ }
44
+ if (!projectExists) {
45
+ await (0, generator_2.default)(tree, {
46
+ name: options.name,
47
+ directory: options.directory,
48
+ });
49
+ }
50
+ const projectConfig = (0, devkit_1.readProjectConfiguration)(tree, fullyQualifiedName);
51
+ const localDynamoPort = (0, port_1.assignSharedPort)(tree, projectConfig, exports.TS_DYNAMODB_GENERATOR_INFO.id, 8000);
52
+ const templateOptions = {
53
+ runtimeConfigKey: nameClassName,
54
+ dynamoPackageAlias: (0, npm_scope_1.toScopeAlias)(fullyQualifiedName),
55
+ localDynamoPort,
56
+ localTableName,
57
+ containerEngine,
58
+ };
59
+ (0, devkit_1.generateFiles)(tree, (0, devkit_1.joinPathFragments)(__dirname, 'files'), dir, templateOptions);
60
+ const containerName = `${(0, npm_scope_2.getNpmScope)(tree)}-dynamodb`;
61
+ projectConfig.targets['pull-image'] = {
62
+ executor: 'nx:run-commands',
63
+ options: {
64
+ command: `tsx scripts/pull-image.ts ${DYNAMODB_LOCAL_IMAGE}`,
65
+ cwd: '{projectRoot}',
66
+ },
67
+ };
68
+ projectConfig.targets['serve-local'] = {
69
+ executor: 'nx:run-commands',
70
+ continuous: true,
71
+ dependsOn: ['pull-image'],
72
+ options: {
73
+ commands: [
74
+ `tsx scripts/start-container.ts ${containerName} ${DYNAMODB_LOCAL_IMAGE} ${localDynamoPort}`,
75
+ `tsx scripts/create-local-table.ts ${localTableName} ${localDynamoPort}`,
76
+ ],
77
+ parallel: true,
78
+ cwd: '{projectRoot}',
79
+ },
80
+ };
81
+ (0, devkit_1.updateProjectConfiguration)(tree, fullyQualifiedName, projectConfig);
82
+ (0, nx_1.addGeneratorMetadata)(tree, fullyQualifiedName, exports.TS_DYNAMODB_GENERATOR_INFO);
83
+ if (options.infra !== 'none') {
84
+ const iac = await (0, iac_1.resolveIac)(tree, options.iac);
85
+ await (0, shared_constructs_1.sharedConstructsGenerator)(tree, { iac });
86
+ await (0, dynamodb_constructs_1.addDynamoDBInfra)(tree, {
87
+ iac,
88
+ projectName: fullyQualifiedName,
89
+ nameClassName,
90
+ nameKebabCase,
91
+ dynamoPackageAlias: (0, npm_scope_1.toScopeAlias)(fullyQualifiedName),
92
+ tableName: localTableName,
93
+ });
94
+ }
95
+ (0, devkit_1.addDependenciesToPackageJson)(tree, (0, versions_1.withVersions)([
96
+ '@aws-sdk/client-dynamodb',
97
+ 'electrodb',
98
+ '@aws-lambda-powertools/parameters',
99
+ ]), (0, versions_1.withVersions)(['tsx', '@types/aws-lambda']));
100
+ await (0, metrics_1.addGeneratorMetricsIfApplicable)(tree, [exports.TS_DYNAMODB_GENERATOR_INFO]);
101
+ await (0, format_1.formatFilesInSubtree)(tree);
102
+ return () => {
103
+ (0, devkit_1.installPackagesTask)(tree);
104
+ };
105
+ };
106
+ exports.tsDynamoDBGenerator = tsDynamoDBGenerator;
107
+ exports.default = exports.tsDynamoDBGenerator;
108
+ //# sourceMappingURL=generator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"generator.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/ts/dynamodb/generator.ts"],"names":[],"mappings":"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"}