@aws/nx-plugin 1.0.0-rc.12 → 1.0.0-rc.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 (105) hide show
  1. package/LICENSE-THIRD-PARTY +81 -3
  2. package/generators.json +28 -1
  3. package/package.json +2 -2
  4. package/src/connection/generator.d.ts +1 -1
  5. package/src/connection/generator.js +58 -42
  6. package/src/connection/generator.js.map +1 -1
  7. package/src/infra/app/__snapshots__/generator.spec.ts.snap +16 -16
  8. package/src/infra/app/generator.js +4 -4
  9. package/src/infra/app/generator.js.map +1 -1
  10. package/src/preset/__snapshots__/generator.spec.ts.snap +4 -2
  11. package/src/py/agent/__snapshots__/generator.spec.ts.snap +574 -5
  12. package/src/py/agent/files/http/init.py.template +5 -23
  13. package/src/py/agent/generator.js +27 -22
  14. package/src/py/agent/generator.js.map +1 -1
  15. package/src/py/dynamodb/__snapshots__/generator.spec.ts.snap +1486 -0
  16. package/src/py/dynamodb/agent-connection/__snapshots__/generator.spec.ts.snap +45 -0
  17. package/src/py/dynamodb/agent-connection/generator.d.ts +10 -0
  18. package/src/py/dynamodb/agent-connection/generator.js +39 -0
  19. package/src/py/dynamodb/agent-connection/generator.js.map +1 -0
  20. package/src/py/dynamodb/agent-connection/schema.d.ts +14 -0
  21. package/src/py/dynamodb/agent-connection/schema.json +22 -0
  22. package/src/py/dynamodb/fast-api-connection/__snapshots__/generator.spec.ts.snap +23 -0
  23. package/src/py/dynamodb/fast-api-connection/generator.d.ts +10 -0
  24. package/src/py/dynamodb/fast-api-connection/generator.js +37 -0
  25. package/src/py/dynamodb/fast-api-connection/generator.js.map +1 -0
  26. package/src/py/dynamodb/fast-api-connection/schema.d.ts +12 -0
  27. package/src/py/dynamodb/fast-api-connection/schema.json +18 -0
  28. package/src/py/dynamodb/files/__name__/__init__.py.template +6 -0
  29. package/src/py/dynamodb/files/__name__/client.py.template +39 -0
  30. package/src/py/dynamodb/files/__name__/entities/__init__.py.template +4 -0
  31. package/src/py/dynamodb/files/__name__/entities/base.py.template +63 -0
  32. package/src/py/dynamodb/files/__name__/entities/example.py.template +64 -0
  33. package/src/py/dynamodb/files/config.json.template +24 -0
  34. package/src/py/dynamodb/generator.d.ts +10 -0
  35. package/src/py/dynamodb/generator.js +106 -0
  36. package/src/py/dynamodb/generator.js.map +1 -0
  37. package/src/py/dynamodb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +45 -0
  38. package/src/py/dynamodb/mcp-server-connection/generator.d.ts +10 -0
  39. package/src/py/dynamodb/mcp-server-connection/generator.js +39 -0
  40. package/src/py/dynamodb/mcp-server-connection/generator.js.map +1 -0
  41. package/src/py/dynamodb/mcp-server-connection/schema.d.ts +14 -0
  42. package/src/py/dynamodb/mcp-server-connection/schema.json +22 -0
  43. package/src/py/dynamodb/schema.d.ts +15 -0
  44. package/src/py/dynamodb/schema.json +70 -0
  45. package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +5 -22
  46. package/src/py/fast-api/files/app/__name__/init.py.template +5 -22
  47. package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +1 -1
  48. package/src/ts/agent/__snapshots__/generator.spec.ts.snap +564 -0
  49. package/src/ts/agent/generator.js +23 -19
  50. package/src/ts/agent/generator.js.map +1 -1
  51. package/src/ts/dynamodb/__snapshots__/generator.spec.ts.snap +303 -512
  52. package/src/ts/dynamodb/files/config.json.template +24 -0
  53. package/src/ts/dynamodb/files/src/client.ts.template +26 -10
  54. package/src/ts/dynamodb/files/src/index.ts.template +0 -8
  55. package/src/ts/dynamodb/generator.js +15 -12
  56. package/src/ts/dynamodb/generator.js.map +1 -1
  57. package/src/ts/dynamodb/schema.d.ts +1 -0
  58. package/src/ts/dynamodb/schema.json +20 -2
  59. package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +1 -1
  60. package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +86 -39
  61. package/src/utils/agent-chat/agent-chat.d.ts +31 -0
  62. package/src/utils/agent-chat/agent-chat.js +30 -0
  63. package/src/utils/agent-chat/agent-chat.js.map +1 -0
  64. package/src/utils/agent-chat/files/a2a/chat.ts.template +33 -0
  65. package/src/utils/agent-chat/files/ag-ui/chat.ts.template +17 -0
  66. package/src/utils/agent-chat/files/common/agentcore.ts.template +81 -0
  67. package/src/utils/agent-chat/files/http-py/chat.ts.template +43 -0
  68. package/src/utils/agent-chat/files/http-ts/chat.ts.template +54 -0
  69. package/src/utils/dynamodb-constructs/dynamodb-constructs.d.ts +0 -1
  70. package/src/utils/dynamodb-constructs/dynamodb-constructs.js.map +1 -1
  71. package/src/utils/dynamodb-constructs/files/cdk/app/dynamodb/__nameKebabCase__.ts.template +12 -13
  72. package/src/utils/dynamodb-constructs/files/terraform/app/dynamodb/__nameKebabCase__/__nameKebabCase__.tf.template +3 -3
  73. package/src/{ts/dynamodb/files/scripts → utils/files/common/scripts/src/dynamodb}/create-local-table.ts.template +63 -36
  74. package/src/utils/files/common/scripts/src/dynamodb/pull-image.ts.template +13 -0
  75. package/src/{ts/dynamodb/files/scripts → utils/files/common/scripts/src/dynamodb}/start-container.ts.template +28 -18
  76. package/src/utils/port.d.ts +6 -5
  77. package/src/utils/port.js +7 -5
  78. package/src/utils/port.js.map +1 -1
  79. package/src/utils/shared-constructs-constants.d.ts +1 -0
  80. package/src/utils/shared-constructs-constants.js +2 -1
  81. package/src/utils/shared-constructs-constants.js.map +1 -1
  82. package/src/utils/shared-dynamodb-scripts.d.ts +11 -0
  83. package/src/utils/shared-dynamodb-scripts.js +23 -0
  84. package/src/utils/shared-dynamodb-scripts.js.map +1 -0
  85. package/src/utils/shared-infra-scripts.d.ts +11 -0
  86. package/src/utils/shared-infra-scripts.js +35 -0
  87. package/src/utils/shared-infra-scripts.js.map +1 -0
  88. package/src/utils/shared-scripts.d.ts +1 -4
  89. package/src/utils/shared-scripts.js +2 -23
  90. package/src/utils/shared-scripts.js.map +1 -1
  91. package/src/utils/versions.d.ts +45 -44
  92. package/src/utils/versions.js +44 -43
  93. package/src/utils/versions.js.map +1 -1
  94. package/src/py/agent/scripts/http/chat.ts.template +0 -38
  95. package/src/ts/agent/scripts/http/chat.ts.template +0 -35
  96. package/src/ts/dynamodb/files/dynamodb.config.json +0 -14
  97. package/src/ts/dynamodb/files/scripts/pull-image.ts.template +0 -15
  98. package/src/ts/dynamodb/files/src/constants.ts.template +0 -9
  99. /package/src/utils/files/common/scripts/src/{index.ts.template → infra/index.ts.template} +0 -0
  100. /package/src/utils/files/common/scripts/src/{infra-deploy.ts.template → infra/infra-deploy.ts.template} +0 -0
  101. /package/src/utils/files/common/scripts/src/{infra-destroy.ts.template → infra/infra-destroy.ts.template} +0 -0
  102. /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/cdk-command.ts.template +0 -0
  103. /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/credentials.ts.template +0 -0
  104. /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/run.ts.template +0 -0
  105. /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/stage-parser.ts.template +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"agent-chat.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/utils/agent-chat/agent-chat.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;AAGH,uCAIoB;AAsBpB;;;;;;GAMG;AACI,MAAM,mBAAmB,GAAG,CACjC,IAAU,EACV,OAAmC,EAC7B,EAAE;IACR,MAAM,eAAe,GAAG;QACtB,kBAAkB,EAAE,OAAO,CAAC,kBAAkB;QAC9C,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,mBAAmB,EAAE,OAAO,CAAC,mBAAmB,IAAI,EAAE;KACvD,CAAC;IAEF,kEAAkE;IAClE,IAAA,sBAAa,EACX,IAAI,EACJ,IAAA,0BAAiB,EAAC,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC,EAC/C,OAAO,CAAC,UAAU,EAClB,eAAe,EACf,EAAE,iBAAiB,EAAE,0BAAiB,CAAC,YAAY,EAAE,CACtD,CAAC;IAEF,2EAA2E;IAC3E,2EAA2E;IAC3E,MAAM,WAAW,GACf,OAAO,CAAC,QAAQ,KAAK,MAAM,CAAC,CAAC,CAAC,QAAQ,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC;IAC9E,IAAA,sBAAa,EACX,IAAI,EACJ,IAAA,0BAAiB,EAAC,SAAS,EAAE,OAAO,EAAE,WAAW,CAAC,EAClD,OAAO,CAAC,UAAU,EAClB,eAAe,EACf,EAAE,iBAAiB,EAAE,0BAAiB,CAAC,YAAY,EAAE,CACtD,CAAC;AACJ,CAAC,CAAC;AA9BW,QAAA,mBAAmB,uBA8B9B"}
@@ -0,0 +1,33 @@
1
+ // Chat CLI for <%- agentNameClassName %> (A2A protocol). Connects to the local
2
+ // `serve-local` server, or the deployed agent when `RUNTIME_CONFIG_APP_ID` is set.
3
+ import {
4
+ ClientFactory,
5
+ ClientFactoryOptions,
6
+ DefaultAgentCardResolver,
7
+ JsonRpcTransportFactory,
8
+ } from '@a2a-js/sdk/client';
9
+ import { A2AChatAdapter, chatLoop } from 'agent-chat-cli';
10
+ import { createAgentCoreFetch, resolveRemoteAgent } from './agentcore.js';
11
+
12
+ const remote = await resolveRemoteAgent();
13
+
14
+ const url = remote
15
+ ? `https://bedrock-agentcore.${remote.region}.amazonaws.com/runtimes/${encodeURIComponent(remote.arn)}/invocations/`
16
+ : process.env.URL!;
17
+
18
+ // Remote requests are authenticated via a custom fetch; locally the default client talks plain HTTP.
19
+ const clientFactory = remote
20
+ ? new ClientFactory({
21
+ ...ClientFactoryOptions.default,
22
+ transports: [
23
+ new JsonRpcTransportFactory({
24
+ fetchImpl: createAgentCoreFetch(<%- auth === 'iam' ? 'remote.region' : '' %>),
25
+ }),
26
+ ],
27
+ cardResolver: new DefaultAgentCardResolver({
28
+ fetchImpl: createAgentCoreFetch(<%- auth === 'iam' ? 'remote.region' : '' %>),
29
+ }),
30
+ })
31
+ : undefined;
32
+
33
+ await chatLoop(new A2AChatAdapter({ clientFactory }), url);
@@ -0,0 +1,17 @@
1
+ // Chat CLI for <%- agentNameClassName %> (AG-UI protocol). Connects to the local
2
+ // `serve-local` server, or the deployed agent when `RUNTIME_CONFIG_APP_ID` is set.
3
+ import { AGUIChatAdapter, chatLoop } from 'agent-chat-cli';
4
+ import { createAgentCoreFetch, resolveRemoteAgent } from './agentcore.js';
5
+
6
+ const remote = await resolveRemoteAgent();
7
+
8
+ const url = remote
9
+ ? `https://bedrock-agentcore.${remote.region}.amazonaws.com/runtimes/${encodeURIComponent(remote.arn)}/invocations?qualifier=DEFAULT`
10
+ : process.env.URL!;
11
+
12
+ // Remote requests are authenticated via a custom fetch; locally the default agent talks plain HTTP.
13
+ const fetchImpl = remote
14
+ ? createAgentCoreFetch(<%- auth === 'iam' ? 'remote.region' : '' %>)
15
+ : undefined;
16
+
17
+ await chatLoop(new AGUIChatAdapter({ fetch: fetchImpl }), url);
@@ -0,0 +1,81 @@
1
+ // Resolves the deployed <%- agentNameClassName %> agent from runtime config and authenticates requests to it.
2
+ import { randomUUID } from 'node:crypto';
3
+ import { getAppConfig } from '@aws-lambda-powertools/parameters/appconfig';
4
+ <%_ if (auth === 'iam') { _%>
5
+ import { fromNodeProviderChain } from '@aws-sdk/credential-providers';
6
+ import { AwsClient } from 'aws4fetch';
7
+ <%_ } _%>
8
+
9
+ const SESSION_HEADER = 'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id';
10
+
11
+ // AgentCore session ids must be at least 33 characters.
12
+ export const SESSION_ID = randomUUID().replaceAll('-', '').padEnd(33, '0');
13
+
14
+ export interface RemoteAgent {
15
+ /** ARN of the deployed Bedrock AgentCore runtime. */
16
+ arn: string;
17
+ /** AWS region parsed from the runtime ARN. */
18
+ region: string;
19
+ }
20
+
21
+ // Returns the deployed agent when `RUNTIME_CONFIG_APP_ID` is set, otherwise `undefined` to chat locally.
22
+ export const resolveRemoteAgent = async (): Promise<
23
+ RemoteAgent | undefined
24
+ > => {
25
+ const application = process.env.RUNTIME_CONFIG_APP_ID;
26
+ if (!application) {
27
+ return undefined;
28
+ }
29
+ const config = (await getAppConfig('agentcore', {
30
+ application,
31
+ environment: 'default',
32
+ transform: 'json',
33
+ })) as { agentRuntimes?: Record<string, string> };
34
+ const arn = config?.agentRuntimes?.['<%- agentNameClassName %>'];
35
+ if (!arn) {
36
+ throw new Error(
37
+ `No deployed agent named '<%- agentNameClassName %>' found in runtime configuration (application ${application}).`,
38
+ );
39
+ }
40
+ return { arn, region: arn.split(':')[3] };
41
+ };
42
+
43
+ <%_ if (auth === 'cognito') { _%>
44
+ /** The Cognito access token used to authenticate with the deployed agent. */
45
+ export const getAccessToken = (): string => {
46
+ const accessToken = process.env.AGENT_ACCESS_TOKEN;
47
+ if (!accessToken) {
48
+ throw new Error(
49
+ 'AGENT_ACCESS_TOKEN is not set. Provide a Cognito access token to chat with the deployed agent.',
50
+ );
51
+ }
52
+ return accessToken;
53
+ };
54
+ <%_ } _%>
55
+
56
+ // A `fetch` that authenticates requests to the deployed agent and forwards the session id.
57
+ <%_ if (auth === 'iam') { _%>
58
+ export const createAgentCoreFetch = (region: string): typeof fetch => {
59
+ const credentialProvider = fromNodeProviderChain();
60
+ return async (input, init) => {
61
+ const headers = new Headers(init?.headers);
62
+ headers.set(SESSION_HEADER, SESSION_ID);
63
+ const client = new AwsClient({
64
+ ...(await credentialProvider()),
65
+ service: 'bedrock-agentcore',
66
+ region,
67
+ });
68
+ return client.fetch(input, { ...init, headers });
69
+ };
70
+ };
71
+ <%_ } else { _%>
72
+ export const createAgentCoreFetch = (): typeof fetch => {
73
+ const accessToken = getAccessToken();
74
+ return (input, init) => {
75
+ const headers = new Headers(init?.headers);
76
+ headers.set(SESSION_HEADER, SESSION_ID);
77
+ headers.set('Authorization', `Bearer ${accessToken}`);
78
+ return fetch(input, { ...init, headers });
79
+ };
80
+ };
81
+ <%_ } _%>
@@ -0,0 +1,43 @@
1
+ // Chat CLI for <%- agentNameClassName %> (Python FastAPI / JSONL streaming), using the
2
+ // generated client. Connects to the local `serve-local` server, or the deployed
3
+ // agent when `RUNTIME_CONFIG_APP_ID` is set.
4
+ import { chatLoop, type ChatAdapter } from 'agent-chat-cli';
5
+ import { <%- agentNameClassName %> } from './generated/client.gen.js';
6
+ import {
7
+ createAgentCoreFetch,
8
+ resolveRemoteAgent,
9
+ SESSION_ID,
10
+ } from './agentcore.js';
11
+
12
+ const SESSION_ID_HEADER = 'X-Amzn-Bedrock-AgentCore-Runtime-Session-Id';
13
+
14
+ const remote = await resolveRemoteAgent();
15
+
16
+ class <%- agentNameClassName %>Adapter implements ChatAdapter {
17
+ private client!: <%- agentNameClassName %>;
18
+
19
+ async connect(url: string) {
20
+ this.client = remote
21
+ ? new <%- agentNameClassName %>({
22
+ url: `https://bedrock-agentcore.${remote.region}.amazonaws.com/runtimes/${encodeURIComponent(remote.arn)}`,
23
+ fetch: createAgentCoreFetch(<%- auth === 'iam' ? 'remote.region' : '' %>),
24
+ })
25
+ : new <%- agentNameClassName %>({
26
+ url,
27
+ fetch: (input, init) => {
28
+ const headers = new Headers(init?.headers);
29
+ headers.set(SESSION_ID_HEADER, SESSION_ID);
30
+ return fetch(input, { ...init, headers });
31
+ },
32
+ });
33
+ return { agentName: '<%- agentNameClassName %>' };
34
+ }
35
+
36
+ async *sendMessage(text: string): AsyncIterable<string> {
37
+ for await (const chunk of this.client.invoke({ message: text })) {
38
+ if (typeof chunk.content === 'string') yield chunk.content;
39
+ }
40
+ }
41
+ }
42
+
43
+ await chatLoop(new <%- agentNameClassName %>Adapter(), process.env.URL ?? '');
@@ -0,0 +1,54 @@
1
+ // Chat CLI for <%- agentNameClassName %> (HTTP / tRPC WebSocket). Connects to the local
2
+ // `serve-local` server, or the deployed agent when `RUNTIME_CONFIG_APP_ID` is set.
3
+ import { chatLoop, type ChatAdapter } from 'agent-chat-cli';
4
+ import { <%- agentNameClassName %>Client } from '<%- relativeAgentImport %>/client.js';
5
+ import {
6
+ resolveRemoteAgent,
7
+ SESSION_ID,
8
+ <%_ if (auth === 'cognito') { _%>
9
+ getAccessToken,
10
+ <%_ } _%>
11
+ } from './agentcore.js';
12
+
13
+ const remote = await resolveRemoteAgent();
14
+
15
+ class TrpcWebSocketAdapter implements ChatAdapter {
16
+ private client!: ReturnType<typeof <%- agentNameClassName %>Client.local>;
17
+
18
+ async connect(url: string) {
19
+ this.client = remote
20
+ <%_ if (auth === 'iam') { _%>
21
+ ? <%- agentNameClassName %>Client.withIamAuth({
22
+ agentRuntimeArn: remote.arn,
23
+ sessionId: SESSION_ID,
24
+ })
25
+ <%_ } else { _%>
26
+ ? <%- agentNameClassName %>Client.withJwtAuth({
27
+ agentRuntimeArn: remote.arn,
28
+ sessionId: SESSION_ID,
29
+ accessTokenProvider: async () => getAccessToken(),
30
+ })
31
+ <%_ } _%>
32
+ : <%- agentNameClassName %>Client.local({ url, sessionId: SESSION_ID });
33
+ return { agentName: '<%- agentNameClassName %>' };
34
+ }
35
+
36
+ async *sendMessage(text: string): AsyncIterable<string> {
37
+ const stream = new ReadableStream<string>({
38
+ start: (controller) => {
39
+ this.client.invoke.subscribe(
40
+ { message: text },
41
+ {
42
+ onData: (chunk: string) => controller.enqueue(chunk),
43
+ onComplete: () => controller.close(),
44
+ onError: (err: unknown) => controller.error(err),
45
+ },
46
+ );
47
+ },
48
+ });
49
+
50
+ yield* stream as unknown as AsyncIterable<string>;
51
+ }
52
+ }
53
+
54
+ await chatLoop(new TrpcWebSocketAdapter(), process.env.URL ?? '');
@@ -8,7 +8,6 @@ export interface AddDynamoDBConstructOptions {
8
8
  projectName: string;
9
9
  nameClassName: string;
10
10
  nameKebabCase: string;
11
- dynamoPackageAlias: string;
12
11
  tableName: string;
13
12
  projectRoot: string;
14
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"dynamodb-constructs.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/utils/dynamodb-constructs/dynamodb-constructs.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dynamodb-constructs.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/utils/dynamodb-constructs/dynamodb-constructs.ts"],"names":[],"mappings":"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"}
@@ -1,5 +1,4 @@
1
1
  import { Construct } from 'constructs';
2
- import { PACKAGE_NAME } from '<%= dynamoPackageAlias %>';
3
2
  import { AttributeType, ProjectionType } from 'aws-cdk-lib/aws-dynamodb';
4
3
  import {
5
4
  DynamoDBTable,
@@ -10,6 +9,16 @@ import { readFileSync } from 'fs';
10
9
  import { join } from "path";
11
10
  import { fileURLToPath } from "url";
12
11
 
12
+ const { runtimeConfigKey, tableConfig } = JSON.parse(
13
+ readFileSync(
14
+ join(
15
+ findWorkspaceRoot(fileURLToPath(new URL(import.meta.url))),
16
+ '<%= projectRoot %>/config.json',
17
+ ),
18
+ 'utf-8',
19
+ ),
20
+ ) as { runtimeConfigKey: string; tableConfig: { globalSecondaryIndexes: { indexName: string; partitionKey: string; sortKey?: string }[] } };
21
+
13
22
  export type <%= nameClassName %>Props = Omit<
14
23
  DynamoDBTableProps,
15
24
  'runtimeConfigKey'
@@ -19,20 +28,10 @@ export class <%= nameClassName %> extends DynamoDBTable {
19
28
  constructor(scope: Construct, id: string, props?: <%= nameClassName %>Props) {
20
29
  super(scope, id, {
21
30
  ...props,
22
- runtimeConfigKey: PACKAGE_NAME,
31
+ runtimeConfigKey,
23
32
  });
24
33
 
25
- const { globalSecondaryIndexes } = JSON.parse(
26
- readFileSync(
27
- join(
28
- findWorkspaceRoot(fileURLToPath(new URL(import.meta.url))),
29
- '<%= projectRoot %>/dynamodb.config.json',
30
- ),
31
- 'utf-8',
32
- ),
33
- ) as { globalSecondaryIndexes: { indexName: string; partitionKey: string; sortKey?: string }[] };
34
-
35
- for (const gsi of globalSecondaryIndexes) {
34
+ for (const gsi of tableConfig.globalSecondaryIndexes) {
36
35
  this.table.addGlobalSecondaryIndex({
37
36
  indexName: gsi.indexName,
38
37
  partitionKey: { name: gsi.partitionKey, type: AttributeType.STRING },
@@ -32,8 +32,8 @@ variable "enable_key_rotation" {
32
32
  }
33
33
 
34
34
  locals {
35
- dynamodb_config = jsondecode(file("${path.module}/../../../../../../../<%= projectRoot %>/dynamodb.config.json"))
36
- global_secondary_indexes = [for gsi in local.dynamodb_config.globalSecondaryIndexes : {
35
+ dynamodb_config = jsondecode(file("${path.module}/../../../../../../../<%= projectRoot %>/config.json"))
36
+ global_secondary_indexes = [for gsi in local.dynamodb_config.tableConfig.globalSecondaryIndexes : {
37
37
  name = gsi.indexName
38
38
  hash_key = gsi.partitionKey
39
39
  range_key = try(gsi.sortKey, null)
@@ -55,7 +55,7 @@ module "add_to_runtime_config" {
55
55
  source = "../../../core/runtime-config/entry"
56
56
 
57
57
  namespace = "dynamodb"
58
- key = "<%= nameClassName %>"
58
+ key = local.dynamodb_config.runtimeConfigKey
59
59
  value = {
60
60
  tableName = module.dynamodb_table.table_name
61
61
  }
@@ -1,33 +1,39 @@
1
1
  import {
2
+ BillingMode,
2
3
  CreateTableCommand,
3
4
  DescribeTableCommand,
4
5
  DynamoDBClient,
5
6
  GlobalSecondaryIndexDescription,
7
+ IndexStatus,
8
+ KeyType,
9
+ ProjectionType,
6
10
  ResourceInUseException,
11
+ ScalarAttributeType,
12
+ TableStatus,
7
13
  UpdateTableCommand,
8
- } from '@aws-sdk/client-dynamodb';
9
- import { readFileSync } from 'fs';
10
- import { join, dirname } from 'path';
11
- import { fileURLToPath } from 'url';
14
+ } from "@aws-sdk/client-dynamodb";
15
+ import { readFileSync } from "fs";
12
16
 
13
- const __dirname = dirname(fileURLToPath(import.meta.url));
14
- const { globalSecondaryIndexes } = JSON.parse(
15
- readFileSync(join(__dirname, '../dynamodb.config.json'), 'utf-8'),
16
- ) as { globalSecondaryIndexes: { indexName: string; partitionKey: string; sortKey?: string }[] };
17
-
18
- const [tableName, port] = process.argv.slice(2);
19
- const endpoint = `http://localhost:${port}`;
17
+ const {
18
+ serveLocal: { port, tableName },
19
+ tableConfig: { globalSecondaryIndexes },
20
+ } = JSON.parse(readFileSync("config.json", "utf-8")) as {
21
+ serveLocal: { port: number; tableName: string };
22
+ tableConfig: {
23
+ globalSecondaryIndexes: {
24
+ indexName: string;
25
+ partitionKey: string;
26
+ sortKey?: string;
27
+ }[];
28
+ };
29
+ };
20
30
 
21
- const REQUEST_TIMEOUT_MS = 10_000;
22
- const timeoutSignal = () => AbortSignal.timeout(REQUEST_TIMEOUT_MS);
31
+ const timeoutSignal = () => AbortSignal.timeout(10000);
23
32
 
24
33
  const client = new DynamoDBClient({
25
- endpoint,
34
+ endpoint: `http://localhost:${port}`,
26
35
  region: 'us-east-1',
27
- credentials: {
28
- accessKeyId: 'accessKeyId',
29
- secretAccessKey: 'secretAccessKey',
30
- },
36
+ credentials: { accessKeyId: 'UNUSED', secretAccessKey: 'UNUSED' },
31
37
  });
32
38
 
33
39
  const waitForTableActive = async (
@@ -40,9 +46,9 @@ const waitForTableActive = async (
40
46
  { abortSignal: timeoutSignal() },
41
47
  );
42
48
  const allActive =
43
- Table?.TableStatus === 'ACTIVE' &&
49
+ Table?.TableStatus === TableStatus.ACTIVE &&
44
50
  (Table?.GlobalSecondaryIndexes ?? []).every(
45
- (g: GlobalSecondaryIndexDescription) => g.IndexStatus === 'ACTIVE',
51
+ (g: GlobalSecondaryIndexDescription) => g.IndexStatus === IndexStatus.ACTIVE,
46
52
  );
47
53
  if (allActive) return;
48
54
  await new Promise((resolve) => setTimeout(resolve, 500));
@@ -55,12 +61,14 @@ const ensureTable = async (
55
61
  gsIs: { indexName: string; partitionKey: string; sortKey?: string }[],
56
62
  ): Promise<void> => {
57
63
  const gsiAttributeDefinitions = gsIs.flatMap((gsi) => [
58
- { AttributeName: gsi.partitionKey, AttributeType: 'S' as const },
59
- ...(gsi.sortKey ? [{ AttributeName: gsi.sortKey, AttributeType: 'S' as const }] : []),
64
+ { AttributeName: gsi.partitionKey, AttributeType: ScalarAttributeType.S },
65
+ ...(gsi.sortKey
66
+ ? [{ AttributeName: gsi.sortKey, AttributeType: ScalarAttributeType.S }]
67
+ : []),
60
68
  ]);
61
69
  const attributeDefinitions = [
62
- { AttributeName: 'pk', AttributeType: 'S' as const },
63
- { AttributeName: 'sk', AttributeType: 'S' as const },
70
+ { AttributeName: "pk", AttributeType: ScalarAttributeType.S },
71
+ { AttributeName: "sk", AttributeType: ScalarAttributeType.S },
64
72
  ...gsiAttributeDefinitions.filter(
65
73
  (a, i, arr) =>
66
74
  arr.findIndex((b) => b.AttributeName === a.AttributeName) === i,
@@ -69,10 +77,12 @@ const ensureTable = async (
69
77
  const globalSecondaryIndexes = gsIs.map((gsi) => ({
70
78
  IndexName: gsi.indexName,
71
79
  KeySchema: [
72
- { AttributeName: gsi.partitionKey, KeyType: 'HASH' as const },
73
- ...(gsi.sortKey ? [{ AttributeName: gsi.sortKey, KeyType: 'RANGE' as const }] : []),
80
+ { AttributeName: gsi.partitionKey, KeyType: KeyType.HASH },
81
+ ...(gsi.sortKey
82
+ ? [{ AttributeName: gsi.sortKey, KeyType: KeyType.RANGE }]
83
+ : []),
74
84
  ],
75
- Projection: { ProjectionType: 'ALL' as const },
85
+ Projection: { ProjectionType: ProjectionType.ALL },
76
86
  }));
77
87
 
78
88
  for (let attempt = 0; attempt < 30; attempt++) {
@@ -83,10 +93,10 @@ const ensureTable = async (
83
93
  TableName: tblName,
84
94
  AttributeDefinitions: attributeDefinitions,
85
95
  KeySchema: [
86
- { AttributeName: 'pk', KeyType: 'HASH' },
87
- { AttributeName: 'sk', KeyType: 'RANGE' },
96
+ { AttributeName: "pk", KeyType: KeyType.HASH },
97
+ { AttributeName: "sk", KeyType: KeyType.RANGE },
88
98
  ],
89
- BillingMode: 'PAY_PER_REQUEST',
99
+ BillingMode: BillingMode.PAY_PER_REQUEST,
90
100
  ...(globalSecondaryIndexes.length > 0 && {
91
101
  GlobalSecondaryIndexes: globalSecondaryIndexes,
92
102
  }),
@@ -108,7 +118,7 @@ const ensureTable = async (
108
118
  const existingGSIs = Table?.GlobalSecondaryIndexes ?? [];
109
119
  const existingNames = new Set(
110
120
  existingGSIs.map(
111
- (g: GlobalSecondaryIndexDescription) => g.IndexName ?? '',
121
+ (g: GlobalSecondaryIndexDescription) => g.IndexName ?? "",
112
122
  ),
113
123
  );
114
124
  const desiredNames = new Set(gsIs.map((g) => g.indexName));
@@ -136,18 +146,35 @@ const ensureTable = async (
136
146
  new UpdateTableCommand({
137
147
  TableName: tblName,
138
148
  AttributeDefinitions: [
139
- { AttributeName: gsi.partitionKey, AttributeType: 'S' },
140
- ...(gsi.sortKey ? [{ AttributeName: gsi.sortKey, AttributeType: 'S' as const }] : []),
149
+ { AttributeName: gsi.partitionKey, AttributeType: ScalarAttributeType.S },
150
+ ...(gsi.sortKey
151
+ ? [
152
+ {
153
+ AttributeName: gsi.sortKey,
154
+ AttributeType: ScalarAttributeType.S,
155
+ },
156
+ ]
157
+ : []),
141
158
  ],
142
159
  GlobalSecondaryIndexUpdates: [
143
160
  {
144
161
  Create: {
145
162
  IndexName: gsi.indexName,
146
163
  KeySchema: [
147
- { AttributeName: gsi.partitionKey, KeyType: 'HASH' },
148
- ...(gsi.sortKey ? [{ AttributeName: gsi.sortKey, KeyType: 'RANGE' }] : []),
164
+ {
165
+ AttributeName: gsi.partitionKey,
166
+ KeyType: KeyType.HASH,
167
+ },
168
+ ...(gsi.sortKey
169
+ ? [
170
+ {
171
+ AttributeName: gsi.sortKey,
172
+ KeyType: KeyType.RANGE,
173
+ },
174
+ ]
175
+ : []),
149
176
  ],
150
- Projection: { ProjectionType: 'ALL' },
177
+ Projection: { ProjectionType: ProjectionType.ALL },
151
178
  },
152
179
  },
153
180
  ],
@@ -0,0 +1,13 @@
1
+ import { spawnSync } from 'child_process';
2
+ import { readFileSync } from 'fs';
3
+
4
+ const { containerEngine, image } =
5
+ JSON.parse(readFileSync('config.json', 'utf-8')).serveLocal;
6
+
7
+ const inspect = spawnSync(containerEngine, ['image', 'inspect', image], { stdio: 'ignore' });
8
+ if (inspect.status !== 0) {
9
+ const pull = spawnSync(containerEngine, ['pull', image], { stdio: 'inherit' });
10
+ if (pull.status !== 0) {
11
+ process.exit(pull.status ?? 1);
12
+ }
13
+ }
@@ -1,17 +1,17 @@
1
1
  import { spawn, spawnSync } from 'child_process';
2
+ import { readFileSync } from 'fs';
2
3
 
3
- const ENGINE = '<%= containerEngine %>';
4
-
5
- const [containerName, image, hostPort] = process.argv.slice(2);
4
+ const { containerEngine, containerName, image, port } =
5
+ JSON.parse(readFileSync('config.json', 'utf-8')).serveLocal;
6
6
 
7
7
  const containerExists = (): boolean =>
8
- spawnSync(ENGINE, ['container', 'inspect', containerName], {
8
+ spawnSync(containerEngine, ['container', 'inspect', containerName], {
9
9
  stdio: 'ignore',
10
10
  }).status === 0;
11
11
 
12
12
  const isRunning = (): boolean => {
13
13
  const result = spawnSync(
14
- ENGINE,
14
+ containerEngine,
15
15
  ['container', 'inspect', '-f', '{{.State.Running}}', containerName],
16
16
  { encoding: 'utf-8' },
17
17
  );
@@ -29,19 +29,29 @@ for (;;) {
29
29
  if (!isRunning()) {
30
30
  // Another process may have started it between our inspect and start — container engine start
31
31
  // exits 0 even if the container is already running, so no special-casing needed.
32
- const start = spawnSync(ENGINE, ['start', containerName], {
32
+ const start = spawnSync(containerEngine, ['start', containerName], {
33
33
  stdio: 'inherit',
34
34
  });
35
35
  if (start.status !== 0) process.exit(start.status ?? 1);
36
36
  }
37
37
  break;
38
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' });
39
+ const runArgs = [
40
+ 'run',
41
+ ...(containerEngine === 'docker' ? ['--rm'] : []),
42
+ '--name', containerName,
43
+ '-u', 'root',
44
+ '-w', '/home/dynamodblocal',
45
+ `-p`, `${port}:${port}`,
46
+ '-v', `${containerName}-data:/home/dynamodblocal/data`,
47
+ '-d', image,
48
+ '-jar', 'DynamoDBLocal.jar',
49
+ '-sharedDb',
50
+ '-dbPath', './data',
51
+ '-port', `${port}`,
52
+ '-optimizeDbBeforeStartup',
53
+ ];
54
+ const create = spawnSync(containerEngine, runArgs, { stdio: 'pipe' });
45
55
  if (create.status === 0) {
46
56
  created = true;
47
57
  break;
@@ -52,7 +62,7 @@ for (;;) {
52
62
  }
53
63
  }
54
64
 
55
- const logs = spawn(ENGINE, ['logs', '-f', containerName], {
65
+ const logs = spawn(containerEngine, ['logs', '-f', containerName], {
56
66
  stdio: ['ignore', 'inherit', 'inherit'],
57
67
  });
58
68
 
@@ -62,11 +72,11 @@ const cleanup = () => {
62
72
  if (exiting) return;
63
73
  exiting = true;
64
74
  if (created) {
65
- <%_ if (containerEngine === 'docker') { _%>
66
- spawnSync(ENGINE, ['stop', containerName], { stdio: 'ignore' });
67
- <%_ } else { _%>
68
- spawnSync(ENGINE, ['rm', '-f', containerName], { stdio: 'ignore' });
69
- <%_ } _%>
75
+ if (containerEngine === 'docker') {
76
+ spawnSync(containerEngine, ['stop', containerName], { stdio: 'ignore' });
77
+ } else {
78
+ spawnSync(containerEngine, ['rm', '-f', containerName], { stdio: 'ignore' });
79
+ }
70
80
  }
71
81
  logs.kill();
72
82
  process.exit(0);
@@ -20,12 +20,13 @@ export interface AssignPortOptions {
20
20
  };
21
21
  }
22
22
  /**
23
- * Assign a port shared across all projects created by the same generator.
24
- * If this project already has a port, or a sibling project created by the same
25
- * generator has one, reuse it; otherwise assign a fresh one. Mutates the
26
- * project to register the port in its metadata.ports.
23
+ * Assign a port shared across all projects created by the same generator (or
24
+ * family of generators). If this project already has a port, or a sibling
25
+ * project created by any of the given generator IDs has one, reuse it;
26
+ * otherwise assign a fresh one. Mutates the project to register the port in
27
+ * its metadata.ports.
27
28
  */
28
- export declare const assignSharedPort: (tree: Tree, project: ProjectConfiguration, generatorId: string, startPort: number) => number;
29
+ export declare const assignSharedPort: (tree: Tree, project: ProjectConfiguration, generatorId: string | string[], startPort: number) => number;
29
30
  /**
30
31
  * Assign a port for a particular target within a project.
31
32
  * Mutates the project to add/update its metadata.ports with the newly assigned port.
package/src/utils/port.js CHANGED
@@ -31,12 +31,14 @@ const getExistingComponentPort = (project, component) => {
31
31
  return typeof match?.port === 'number' ? match.port : undefined;
32
32
  };
33
33
  /**
34
- * Assign a port shared across all projects created by the same generator.
35
- * If this project already has a port, or a sibling project created by the same
36
- * generator has one, reuse it; otherwise assign a fresh one. Mutates the
37
- * project to register the port in its metadata.ports.
34
+ * Assign a port shared across all projects created by the same generator (or
35
+ * family of generators). If this project already has a port, or a sibling
36
+ * project created by any of the given generator IDs has one, reuse it;
37
+ * otherwise assign a fresh one. Mutates the project to register the port in
38
+ * its metadata.ports.
38
39
  */
39
40
  const assignSharedPort = (tree, project, generatorId, startPort) => {
41
+ const generatorIds = Array.isArray(generatorId) ? generatorId : [generatorId];
40
42
  // Reuse this project's own port if it already has one (idempotent on re-run)
41
43
  const ownPort = getExistingProjectPort(project);
42
44
  if (ownPort !== undefined) {
@@ -49,7 +51,7 @@ const assignSharedPort = (tree, project, generatorId, startPort) => {
49
51
  generator: p.metadata?.generator,
50
52
  port: getExistingProjectPort(p),
51
53
  }))
52
- .find((p) => p.generator === generatorId && p.port !== undefined)?.port;
54
+ .find((p) => generatorIds.includes(p.generator) && p.port !== undefined)?.port;
53
55
  if (siblingPort !== undefined) {
54
56
  project.metadata ??= {};
55
57
  project.metadata.ports = [