@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.
- package/LICENSE-THIRD-PARTY +81 -3
- package/generators.json +28 -1
- package/package.json +2 -2
- package/src/connection/generator.d.ts +1 -1
- package/src/connection/generator.js +58 -42
- package/src/connection/generator.js.map +1 -1
- package/src/infra/app/__snapshots__/generator.spec.ts.snap +16 -16
- package/src/infra/app/generator.js +4 -4
- package/src/infra/app/generator.js.map +1 -1
- package/src/preset/__snapshots__/generator.spec.ts.snap +4 -2
- package/src/py/agent/__snapshots__/generator.spec.ts.snap +574 -5
- package/src/py/agent/files/http/init.py.template +5 -23
- package/src/py/agent/generator.js +27 -22
- package/src/py/agent/generator.js.map +1 -1
- package/src/py/dynamodb/__snapshots__/generator.spec.ts.snap +1486 -0
- package/src/py/dynamodb/agent-connection/__snapshots__/generator.spec.ts.snap +45 -0
- package/src/py/dynamodb/agent-connection/generator.d.ts +10 -0
- package/src/py/dynamodb/agent-connection/generator.js +39 -0
- package/src/py/dynamodb/agent-connection/generator.js.map +1 -0
- package/src/py/dynamodb/agent-connection/schema.d.ts +14 -0
- package/src/py/dynamodb/agent-connection/schema.json +22 -0
- package/src/py/dynamodb/fast-api-connection/__snapshots__/generator.spec.ts.snap +23 -0
- package/src/py/dynamodb/fast-api-connection/generator.d.ts +10 -0
- package/src/py/dynamodb/fast-api-connection/generator.js +37 -0
- package/src/py/dynamodb/fast-api-connection/generator.js.map +1 -0
- package/src/py/dynamodb/fast-api-connection/schema.d.ts +12 -0
- package/src/py/dynamodb/fast-api-connection/schema.json +18 -0
- package/src/py/dynamodb/files/__name__/__init__.py.template +6 -0
- package/src/py/dynamodb/files/__name__/client.py.template +39 -0
- package/src/py/dynamodb/files/__name__/entities/__init__.py.template +4 -0
- package/src/py/dynamodb/files/__name__/entities/base.py.template +63 -0
- package/src/py/dynamodb/files/__name__/entities/example.py.template +64 -0
- package/src/py/dynamodb/files/config.json.template +24 -0
- package/src/py/dynamodb/generator.d.ts +10 -0
- package/src/py/dynamodb/generator.js +106 -0
- package/src/py/dynamodb/generator.js.map +1 -0
- package/src/py/dynamodb/mcp-server-connection/__snapshots__/generator.spec.ts.snap +45 -0
- package/src/py/dynamodb/mcp-server-connection/generator.d.ts +10 -0
- package/src/py/dynamodb/mcp-server-connection/generator.js +39 -0
- package/src/py/dynamodb/mcp-server-connection/generator.js.map +1 -0
- package/src/py/dynamodb/mcp-server-connection/schema.d.ts +14 -0
- package/src/py/dynamodb/mcp-server-connection/schema.json +22 -0
- package/src/py/dynamodb/schema.d.ts +15 -0
- package/src/py/dynamodb/schema.json +70 -0
- package/src/py/fast-api/__snapshots__/generator.spec.ts.snap +5 -22
- package/src/py/fast-api/files/app/__name__/init.py.template +5 -22
- package/src/py/mcp-server/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/ts/agent/__snapshots__/generator.spec.ts.snap +564 -0
- package/src/ts/agent/generator.js +23 -19
- package/src/ts/agent/generator.js.map +1 -1
- package/src/ts/dynamodb/__snapshots__/generator.spec.ts.snap +303 -512
- package/src/ts/dynamodb/files/config.json.template +24 -0
- package/src/ts/dynamodb/files/src/client.ts.template +26 -10
- package/src/ts/dynamodb/files/src/index.ts.template +0 -8
- package/src/ts/dynamodb/generator.js +15 -12
- package/src/ts/dynamodb/generator.js.map +1 -1
- package/src/ts/dynamodb/schema.d.ts +1 -0
- package/src/ts/dynamodb/schema.json +20 -2
- package/src/ts/mcp-server/__snapshots__/generator.spec.ts.snap +1 -1
- package/src/ts/react-website/app/__snapshots__/generator.spec.ts.snap +86 -39
- package/src/utils/agent-chat/agent-chat.d.ts +31 -0
- package/src/utils/agent-chat/agent-chat.js +30 -0
- package/src/utils/agent-chat/agent-chat.js.map +1 -0
- package/src/utils/agent-chat/files/a2a/chat.ts.template +33 -0
- package/src/utils/agent-chat/files/ag-ui/chat.ts.template +17 -0
- package/src/utils/agent-chat/files/common/agentcore.ts.template +81 -0
- package/src/utils/agent-chat/files/http-py/chat.ts.template +43 -0
- package/src/utils/agent-chat/files/http-ts/chat.ts.template +54 -0
- package/src/utils/dynamodb-constructs/dynamodb-constructs.d.ts +0 -1
- package/src/utils/dynamodb-constructs/dynamodb-constructs.js.map +1 -1
- package/src/utils/dynamodb-constructs/files/cdk/app/dynamodb/__nameKebabCase__.ts.template +12 -13
- package/src/utils/dynamodb-constructs/files/terraform/app/dynamodb/__nameKebabCase__/__nameKebabCase__.tf.template +3 -3
- package/src/{ts/dynamodb/files/scripts → utils/files/common/scripts/src/dynamodb}/create-local-table.ts.template +63 -36
- package/src/utils/files/common/scripts/src/dynamodb/pull-image.ts.template +13 -0
- package/src/{ts/dynamodb/files/scripts → utils/files/common/scripts/src/dynamodb}/start-container.ts.template +28 -18
- package/src/utils/port.d.ts +6 -5
- package/src/utils/port.js +7 -5
- package/src/utils/port.js.map +1 -1
- package/src/utils/shared-constructs-constants.d.ts +1 -0
- package/src/utils/shared-constructs-constants.js +2 -1
- package/src/utils/shared-constructs-constants.js.map +1 -1
- package/src/utils/shared-dynamodb-scripts.d.ts +11 -0
- package/src/utils/shared-dynamodb-scripts.js +23 -0
- package/src/utils/shared-dynamodb-scripts.js.map +1 -0
- package/src/utils/shared-infra-scripts.d.ts +11 -0
- package/src/utils/shared-infra-scripts.js +35 -0
- package/src/utils/shared-infra-scripts.js.map +1 -0
- package/src/utils/shared-scripts.d.ts +1 -4
- package/src/utils/shared-scripts.js +2 -23
- package/src/utils/shared-scripts.js.map +1 -1
- package/src/utils/versions.d.ts +45 -44
- package/src/utils/versions.js +44 -43
- package/src/utils/versions.js.map +1 -1
- package/src/py/agent/scripts/http/chat.ts.template +0 -38
- package/src/ts/agent/scripts/http/chat.ts.template +0 -35
- package/src/ts/dynamodb/files/dynamodb.config.json +0 -14
- package/src/ts/dynamodb/files/scripts/pull-image.ts.template +0 -15
- package/src/ts/dynamodb/files/src/constants.ts.template +0 -9
- /package/src/utils/files/common/scripts/src/{index.ts.template → infra/index.ts.template} +0 -0
- /package/src/utils/files/common/scripts/src/{infra-deploy.ts.template → infra/infra-deploy.ts.template} +0 -0
- /package/src/utils/files/common/scripts/src/{infra-destroy.ts.template → infra/infra-destroy.ts.template} +0 -0
- /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/cdk-command.ts.template +0 -0
- /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/credentials.ts.template +0 -0
- /package/src/utils/files/common/scripts/src/{stage-credentials → infra/stage-credentials}/run.ts.template +0 -0
- /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 ?? '');
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dynamodb-constructs.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/utils/dynamodb-constructs/dynamodb-constructs.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,uCAOoB;AACpB,gCAAuC;AAEvC,8BAA0D;AAC1D,gFAIwC;
|
|
1
|
+
{"version":3,"file":"dynamodb-constructs.js","sourceRoot":"","sources":["../../../../../../packages/nx-plugin/src/utils/dynamodb-constructs/dynamodb-constructs.ts"],"names":[],"mappings":";;;AAAA;;;GAGG;AACH,uCAOoB;AACpB,gCAAuC;AAEvC,8BAA0D;AAC1D,gFAIwC;AAUjC,MAAM,gBAAgB,GAAG,KAAK,EACnC,IAAU,EACV,OAAmD,EACnD,EAAE;IACF,IAAI,OAAO,CAAC,GAAG,KAAK,KAAK,EAAE,CAAC;QAC1B,MAAM,IAAA,gCAAwB,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAChD,CAAC;SAAM,IAAI,OAAO,CAAC,GAAG,KAAK,WAAW,EAAE,CAAC;QACvC,IAAA,mCAA2B,EAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC7C,CAAC;SAAM,CAAC;QACN,MAAM,IAAI,KAAK,CAAC,mBAAmB,OAAO,CAAC,GAAG,EAAE,CAAC,CAAC;IACpD,CAAC;IAED,IAAA,mBAAU,EACR,IAAI,EACJ,IAAA,0BAAiB,EACf,0CAAY,EACZ,OAAO,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,mDAAqB,CAAC,CAAC,CAAC,kDAAoB,EACpE,cAAc,CACf,EACD,CAAC,MAA4B,EAAE,EAAE;QAC/B,IAAA,sCAAiC,EAC/B,MAAM,EACN,OAAO,EACP,GAAG,OAAO,CAAC,WAAW,QAAQ,CAC/B,CAAC;QACF,OAAO,MAAM,CAAC;IAChB,CAAC,CACF,CAAC;AACJ,CAAC,CAAC;AA5BW,QAAA,gBAAgB,oBA4B3B;AAEK,MAAM,wBAAwB,GAAG,KAAK,EAC3C,IAAU,EACV,OAAoC,EACpC,EAAE;IACF,IAAA,sBAAa,EACX,IAAI,EACJ,IAAA,0BAAiB,EAAC,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,CAAC,EACpD,IAAA,0BAAiB,EAAC,0CAAY,EAAE,mDAAqB,EAAE,KAAK,EAAE,MAAM,CAAC,EACrE,OAAO,EACP;QACE,iBAAiB,EAAE,0BAAiB,CAAC,YAAY;KAClD,CACF,CAAC;IAEF,IAAA,sBAAa,EACX,IAAI,EACJ,IAAA,0BAAiB,EAAC,SAAS,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,EAAE,UAAU,CAAC,EAC/D,IAAA,0BAAiB,EACf,0CAAY,EACZ,mDAAqB,EACrB,KAAK,EACL,KAAK,EACL,UAAU,CACX,EACD,OAAO,EACP;QACE,iBAAiB,EAAE,0BAAiB,CAAC,YAAY;KAClD,CACF,CAAC;IAEF,MAAM,IAAA,mBAAa,EACjB,IAAI,EACJ,IAAA,0BAAiB,EACf,0CAAY,EACZ,mDAAqB,EACrB,KAAK,EACL,MAAM,EACN,UAAU,CACX,EACD,eAAe,CAChB,CAAC;IACF,MAAM,IAAA,mBAAa,EACjB,IAAI,EACJ,IAAA,0BAAiB,EACf,0CAAY,EACZ,mDAAqB,EACrB,KAAK,EACL,KAAK,EACL,UAAU,EACV,UAAU,CACX,EACD,KAAK,OAAO,CAAC,aAAa,KAAK,CAChC,CAAC;IACF,MAAM,IAAA,mBAAa,EACjB,IAAI,EACJ,IAAA,0BAAiB,EACf,0CAAY,EACZ,mDAAqB,EACrB,KAAK,EACL,KAAK,EACL,UAAU,CACX,EACD,qBAAqB,CACtB,CAAC;AACJ,CAAC,CAAC;AAhEW,QAAA,wBAAwB,4BAgEnC;AAEK,MAAM,2BAA2B,GAAG,CACzC,IAAU,EACV,OAAoC,EACpC,EAAE;IACF,IAAA,sBAAa,EACX,IAAI,EACJ,IAAA,0BAAiB,EAAC,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,CAAC,EAC1D,IAAA,0BAAiB,EAAC,0CAAY,EAAE,kDAAoB,EAAE,KAAK,EAAE,MAAM,CAAC,EACpE,OAAO,EACP;QACE,iBAAiB,EAAE,0BAAiB,CAAC,YAAY;KAClD,CACF,CAAC;IAEF,IAAA,sBAAa,EACX,IAAI,EACJ,IAAA,0BAAiB,EAAC,SAAS,EAAE,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,UAAU,CAAC,EACrE,IAAA,0BAAiB,EACf,0CAAY,EACZ,kDAAoB,EACpB,KAAK,EACL,KAAK,EACL,UAAU,CACX,EACD,OAAO,EACP;QACE,iBAAiB,EAAE,0BAAiB,CAAC,YAAY;KAClD,CACF,CAAC;AACJ,CAAC,CAAC;AA7BW,QAAA,2BAA2B,+BA6BtC"}
|
|
@@ -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
|
|
31
|
+
runtimeConfigKey,
|
|
23
32
|
});
|
|
24
33
|
|
|
25
|
-
const
|
|
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 %>/
|
|
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 =
|
|
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
|
|
9
|
-
import { readFileSync } from
|
|
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
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
) as {
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
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 ===
|
|
49
|
+
Table?.TableStatus === TableStatus.ACTIVE &&
|
|
44
50
|
(Table?.GlobalSecondaryIndexes ?? []).every(
|
|
45
|
-
(g: GlobalSecondaryIndexDescription) => g.IndexStatus ===
|
|
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:
|
|
59
|
-
...(gsi.sortKey
|
|
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:
|
|
63
|
-
{ AttributeName:
|
|
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:
|
|
73
|
-
...(gsi.sortKey
|
|
80
|
+
{ AttributeName: gsi.partitionKey, KeyType: KeyType.HASH },
|
|
81
|
+
...(gsi.sortKey
|
|
82
|
+
? [{ AttributeName: gsi.sortKey, KeyType: KeyType.RANGE }]
|
|
83
|
+
: []),
|
|
74
84
|
],
|
|
75
|
-
Projection: { ProjectionType:
|
|
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:
|
|
87
|
-
{ AttributeName:
|
|
96
|
+
{ AttributeName: "pk", KeyType: KeyType.HASH },
|
|
97
|
+
{ AttributeName: "sk", KeyType: KeyType.RANGE },
|
|
88
98
|
],
|
|
89
|
-
BillingMode:
|
|
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:
|
|
140
|
-
...(gsi.sortKey
|
|
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
|
-
{
|
|
148
|
-
|
|
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:
|
|
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
|
|
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(
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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(
|
|
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
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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);
|
package/src/utils/port.d.ts
CHANGED
|
@@ -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
|
|
25
|
-
*
|
|
26
|
-
* project to register the port in
|
|
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
|
|
36
|
-
*
|
|
37
|
-
* project to register the port in
|
|
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
|
|
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 = [
|