@pipeline-builder/pipeline-core 0.0.1
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 +202 -0
- package/README.md +69 -0
- package/lib/cdk.d.ts +38 -0
- package/lib/cdk.js +44 -0
- package/lib/config/app-config.d.ts +109 -0
- package/lib/config/app-config.js +197 -0
- package/lib/config/aws-config-cdk.d.ts +49 -0
- package/lib/config/aws-config-cdk.js +65 -0
- package/lib/config/billing-config.d.ts +24 -0
- package/lib/config/billing-config.js +497 -0
- package/lib/config/config-types.d.ts +346 -0
- package/lib/config/config-types.js +4 -0
- package/lib/config/entitlements.d.ts +23 -0
- package/lib/config/entitlements.js +46 -0
- package/lib/config/handler-constants.d.ts +10 -0
- package/lib/config/handler-constants.js +39 -0
- package/lib/config/infrastructure-config.d.ts +66 -0
- package/lib/config/infrastructure-config.js +197 -0
- package/lib/config/server-config.d.ts +53 -0
- package/lib/config/server-config.js +175 -0
- package/lib/config/service-client.d.ts +13 -0
- package/lib/config/service-client.js +20 -0
- package/lib/core/artifact-manager.d.ts +62 -0
- package/lib/core/artifact-manager.js +83 -0
- package/lib/core/id-generator.d.ts +50 -0
- package/lib/core/id-generator.js +50 -0
- package/lib/core/metadata-builder.d.ts +48 -0
- package/lib/core/metadata-builder.js +278 -0
- package/lib/core/metadata-helpers.d.ts +31 -0
- package/lib/core/metadata-helpers.js +55 -0
- package/lib/core/network-types.d.ts +200 -0
- package/lib/core/network-types.js +4 -0
- package/lib/core/network.d.ts +33 -0
- package/lib/core/network.js +112 -0
- package/lib/core/pipeline-helpers.d.ts +61 -0
- package/lib/core/pipeline-helpers.js +563 -0
- package/lib/core/pipeline-types.d.ts +118 -0
- package/lib/core/pipeline-types.js +131 -0
- package/lib/core/role-types.d.ts +254 -0
- package/lib/core/role-types.js +4 -0
- package/lib/core/role.d.ts +14 -0
- package/lib/core/role.js +117 -0
- package/lib/core/security-group-types.d.ts +84 -0
- package/lib/core/security-group-types.js +4 -0
- package/lib/core/security-group.d.ts +14 -0
- package/lib/core/security-group.js +33 -0
- package/lib/handlers/plugin-lookup-handler.d.ts +32 -0
- package/lib/handlers/plugin-lookup-handler.js +292 -0
- package/lib/handlers/pnpm-lock.yaml +12 -0
- package/lib/index.d.ts +45 -0
- package/lib/index.js +53 -0
- package/lib/pipeline/pipeline-builder.d.ts +123 -0
- package/lib/pipeline/pipeline-builder.js +476 -0
- package/lib/pipeline/pipeline-configuration.d.ts +81 -0
- package/lib/pipeline/pipeline-configuration.js +214 -0
- package/lib/pipeline/plugin-lookup.d.ts +131 -0
- package/lib/pipeline/plugin-lookup.js +316 -0
- package/lib/pipeline/plugin-spec.d.ts +138 -0
- package/lib/pipeline/plugin-spec.js +4 -0
- package/lib/pipeline/source-builder.d.ts +62 -0
- package/lib/pipeline/source-builder.js +164 -0
- package/lib/pipeline/source-types.d.ts +221 -0
- package/lib/pipeline/source-types.js +4 -0
- package/lib/pipeline/stage-builder.d.ts +78 -0
- package/lib/pipeline/stage-builder.js +129 -0
- package/lib/pipeline/step-types.d.ts +222 -0
- package/lib/pipeline/step-types.js +4 -0
- package/lib/template/evaluator.d.ts +42 -0
- package/lib/template/evaluator.js +164 -0
- package/lib/template/index.d.ts +34 -0
- package/lib/template/index.js +118 -0
- package/lib/template/metrics.d.ts +10 -0
- package/lib/template/metrics.js +43 -0
- package/lib/template/plugin-resolver.d.ts +8 -0
- package/lib/template/plugin-resolver.js +48 -0
- package/lib/template/tokenizer.d.ts +49 -0
- package/lib/template/tokenizer.js +233 -0
- package/lib/template/topo-sort.d.ts +17 -0
- package/lib/template/topo-sort.js +88 -0
- package/lib/template/validate.d.ts +45 -0
- package/lib/template/validate.js +96 -0
- package/lib/template/walker.d.ts +34 -0
- package/lib/template/walker.js +83 -0
- package/package.json +184 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import path from 'path';
|
|
4
|
+
/**
|
|
5
|
+
* Load Docker registry configuration from environment variables.
|
|
6
|
+
*
|
|
7
|
+
* Environment variables:
|
|
8
|
+
* - `IMAGE_REGISTRY_HOST` — Registry hostname (default: `'registry'`)
|
|
9
|
+
* - `IMAGE_REGISTRY_PORT` — Registry port (default: `5000`)
|
|
10
|
+
* - `IMAGE_REGISTRY_PULL_HOST` — Host an out-of-cluster client (CodeBuild)
|
|
11
|
+
* uses to pull plugin images. When unset, derived from `PLATFORM_BASE_URL`'s
|
|
12
|
+
* host (the public endpoint CodeBuild can resolve), then `IMAGE_REGISTRY_HOST`.
|
|
13
|
+
* - `IMAGE_REGISTRY_PULL_PORT` — Port for the above. When unset, derived from
|
|
14
|
+
* `PLATFORM_BASE_URL`'s port, then `IMAGE_REGISTRY_PORT`.
|
|
15
|
+
* - `DOCKER_NETWORK` — Docker network for build/push (default: `''`)
|
|
16
|
+
* - `IMAGE_REGISTRY_HTTP` — Use plain HTTP instead of HTTPS (default: `true`,
|
|
17
|
+
* the in-cluster registry has no TLS).
|
|
18
|
+
*
|
|
19
|
+
* @returns Registry configuration
|
|
20
|
+
*/
|
|
21
|
+
export function loadRegistryConfig() {
|
|
22
|
+
const host = process.env.IMAGE_REGISTRY_HOST || 'registry';
|
|
23
|
+
const port = parseInt(process.env.IMAGE_REGISTRY_PORT || '5000', 10);
|
|
24
|
+
// When no explicit pull host is set, derive it from PLATFORM_BASE_URL — the
|
|
25
|
+
// public platform endpoint an out-of-cluster client (AWS CodeBuild) can
|
|
26
|
+
// resolve, unlike the in-cluster `registry` ClusterIP. Only falls back to the
|
|
27
|
+
// in-cluster host when PLATFORM_BASE_URL is unset/unparseable (e.g. local).
|
|
28
|
+
const platform = parsePlatformBaseUrl(process.env.PLATFORM_BASE_URL);
|
|
29
|
+
return {
|
|
30
|
+
host,
|
|
31
|
+
port,
|
|
32
|
+
pullHost: process.env.IMAGE_REGISTRY_PULL_HOST || platform?.host || host,
|
|
33
|
+
pullPort: process.env.IMAGE_REGISTRY_PULL_PORT
|
|
34
|
+
? parseInt(process.env.IMAGE_REGISTRY_PULL_PORT, 10)
|
|
35
|
+
: (platform?.port ?? port),
|
|
36
|
+
network: process.env.DOCKER_NETWORK || '',
|
|
37
|
+
http: process.env.IMAGE_REGISTRY_HTTP !== 'false',
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
/** Extract host + port from PLATFORM_BASE_URL (e.g. `https://pipeline-builder.com`
|
|
41
|
+
* → `{ host: 'pipeline-builder.com', port: 443 }`). Returns null when the value
|
|
42
|
+
* is unset or not a valid URL. Exported so the CLI (pipeline-manager) derives
|
|
43
|
+
* the registry pull target with the SAME parsing, instead of duplicating it. */
|
|
44
|
+
export function parsePlatformBaseUrl(raw) {
|
|
45
|
+
if (!raw)
|
|
46
|
+
return null;
|
|
47
|
+
try {
|
|
48
|
+
const u = new URL(raw);
|
|
49
|
+
// A scheme-less value like `host:8443` parses with a custom protocol and an
|
|
50
|
+
// EMPTY hostname — reject it so callers fall back rather than derive a
|
|
51
|
+
// host-less endpoint.
|
|
52
|
+
if (!u.hostname)
|
|
53
|
+
return null;
|
|
54
|
+
const port = u.port ? parseInt(u.port, 10) : (u.protocol === 'https:' ? 443 : 80);
|
|
55
|
+
return { host: u.hostname, port };
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return null;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
export function loadRedisConfig() {
|
|
62
|
+
return {
|
|
63
|
+
host: process.env.REDIS_HOST || 'localhost',
|
|
64
|
+
port: parseInt(process.env.REDIS_PORT || '6379', 10),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Load plugin build queue configuration.
|
|
69
|
+
*
|
|
70
|
+
* Environment variables:
|
|
71
|
+
* - `PLUGIN_BUILD_CONCURRENCY` — Max concurrent plugin builds (default: `1`)
|
|
72
|
+
*/
|
|
73
|
+
export function loadPluginBuildConfig() {
|
|
74
|
+
return {
|
|
75
|
+
concurrency: parseInt(process.env.PLUGIN_BUILD_CONCURRENCY || '1', 10),
|
|
76
|
+
maxAttempts: parseInt(process.env.PLUGIN_BUILD_MAX_ATTEMPTS || '2', 10),
|
|
77
|
+
backoffDelayMs: parseInt(process.env.PLUGIN_BUILD_BACKOFF_DELAY_MS || '5000', 10),
|
|
78
|
+
workerTimeoutMs: parseInt(process.env.PLUGIN_BUILD_WORKER_TIMEOUT_MS || '10000', 10),
|
|
79
|
+
tempDirMaxAgeMs: parseInt(process.env.TEMP_DIR_MAX_AGE_MS || '14400000', 10),
|
|
80
|
+
dlqMaxAttempts: parseInt(process.env.PLUGIN_DLQ_MAX_ATTEMPTS || '3', 10),
|
|
81
|
+
dlqBackoffBaseMs: parseInt(process.env.PLUGIN_DLQ_BACKOFF_BASE_MS || '300000', 10),
|
|
82
|
+
dlqMaxSize: parseInt(process.env.PLUGIN_DLQ_MAX_SIZE || '20', 10),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
export function loadDatabaseConfig() {
|
|
86
|
+
return {
|
|
87
|
+
postgres: {
|
|
88
|
+
host: process.env.DB_HOST || 'postgres',
|
|
89
|
+
port: parseInt(process.env.DB_PORT || '5432', 10),
|
|
90
|
+
database: process.env.DATABASE || 'pipeline_builder',
|
|
91
|
+
user: process.env.DB_USER || 'postgres',
|
|
92
|
+
password: process.env.DB_PASSWORD || '',
|
|
93
|
+
},
|
|
94
|
+
drizzle: {
|
|
95
|
+
maxPoolSize: parseInt(process.env.DRIZZLE_MAX_POOL_SIZE || '20', 10),
|
|
96
|
+
idleTimeoutMillis: parseInt(process.env.DRIZZLE_IDLE_TIMEOUT_MILLIS || '30000', 10),
|
|
97
|
+
connectionTimeoutMillis: parseInt(process.env.DRIZZLE_CONNECTION_TIMEOUT_MILLIS || '10000', 10),
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
export function loadObservabilityConfig() {
|
|
102
|
+
return {
|
|
103
|
+
logLevel: process.env.LOG_LEVEL || 'info',
|
|
104
|
+
logFormat: process.env.LOG_FORMAT || 'json',
|
|
105
|
+
serviceName: process.env.SERVICE_NAME || 'api',
|
|
106
|
+
tracing: {
|
|
107
|
+
enabled: process.env.OTEL_TRACING_ENABLED === 'true',
|
|
108
|
+
endpoint: process.env.OTEL_EXPORTER_OTLP_ENDPOINT || 'http://localhost:4318/v1/traces',
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
export function loadComplianceConfig() {
|
|
113
|
+
return {
|
|
114
|
+
scanSchedulerIntervalMs: parseInt(process.env.SCAN_SCHEDULER_INTERVAL_MS || '60000', 10),
|
|
115
|
+
systemOrgScansEnabled: process.env.SYSTEM_ORG_SCANS_ENABLED === 'true',
|
|
116
|
+
scanLockTtlMs: parseInt(process.env.SCAN_LOCK_TTL_MS || '300000', 10),
|
|
117
|
+
digestSchedulerIntervalMs: parseInt(process.env.DIGEST_SCHEDULER_INTERVAL_MS || '3600000', 10),
|
|
118
|
+
digestLockTtlMs: parseInt(process.env.DIGEST_LOCK_TTL_MS || '300000', 10),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Load plugin build configuration. Builds run against a rootless `moby/buildkit`
|
|
123
|
+
* sidecar — see `BUILDKIT_HOST`.
|
|
124
|
+
*
|
|
125
|
+
* Environment variables:
|
|
126
|
+
* - `DOCKER_BUILD_TEMP_ROOT` — Temp directory for build contexts (default: `<cwd>/tmp`)
|
|
127
|
+
* - `DOCKER_BUILD_TIMEOUT_MS` — Build timeout in milliseconds (default: `900000` / 15 min)
|
|
128
|
+
* - `DOCKER_PUSH_TIMEOUT_MS` — Push timeout in milliseconds (default: `300000` / 5 min)
|
|
129
|
+
* - `BUILDKIT_HOST` — buildctl `--addr` value for the buildkitd sidecar
|
|
130
|
+
* (default: `unix:///run/buildkit/buildkitd.sock`)
|
|
131
|
+
*/
|
|
132
|
+
export function loadDockerConfig() {
|
|
133
|
+
return {
|
|
134
|
+
tempRoot: process.env.DOCKER_BUILD_TEMP_ROOT || path.join(process.cwd(), 'tmp'),
|
|
135
|
+
timeoutMs: parseInt(process.env.DOCKER_BUILD_TIMEOUT_MS || '900000', 10),
|
|
136
|
+
pushTimeoutMs: parseInt(process.env.DOCKER_PUSH_TIMEOUT_MS || '300000', 10),
|
|
137
|
+
buildkitAddr: process.env.BUILDKIT_HOST || 'unix:///run/buildkit/buildkitd.sock',
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* Load AWS infrastructure configuration from environment variables.
|
|
142
|
+
*
|
|
143
|
+
* Environment variables:
|
|
144
|
+
* - `LAMBDA_RUNTIME` — Lambda runtime (default: `'nodejs24.x'`; unknown values fall back to it)
|
|
145
|
+
* - `LAMBDA_TIMEOUT` — Lambda timeout in seconds (default: `900`)
|
|
146
|
+
* - `LAMBDA_MEMORY_SIZE` — Lambda memory in MB (default: `512`)
|
|
147
|
+
* - `LAMBDA_ARCHITECTURE` — `'x86_64'` or ARM (default: ARM_64)
|
|
148
|
+
* - `LOG_GROUP_NAME` — CloudWatch log group (default: `'/pipeline-builder/logs'`)
|
|
149
|
+
* - `LOG_RETENTION` — Log retention in days (default: `7`)
|
|
150
|
+
* - `LOG_REMOVAL_POLICY` — `'RETAIN'` or destroy (default: DESTROY)
|
|
151
|
+
* - `CODEBUILD_COMPUTE_TYPE` — CodeBuild compute type (default: `'SMALL'`)
|
|
152
|
+
*
|
|
153
|
+
* @returns AWS infrastructure configuration
|
|
154
|
+
*/
|
|
155
|
+
export function loadAWSConfig() {
|
|
156
|
+
return {
|
|
157
|
+
lambda: {
|
|
158
|
+
runtime: process.env.LAMBDA_RUNTIME || 'nodejs24.x',
|
|
159
|
+
timeoutSeconds: parseInt(process.env.LAMBDA_TIMEOUT || '900', 10),
|
|
160
|
+
memorySize: parseInt(process.env.LAMBDA_MEMORY_SIZE || '512', 10),
|
|
161
|
+
architecture: process.env.LAMBDA_ARCHITECTURE === 'x86_64' ? 'x86_64' : 'arm64',
|
|
162
|
+
reservedConcurrentExecutions: process.env.LAMBDA_RESERVED_CONCURRENCY
|
|
163
|
+
? parseInt(process.env.LAMBDA_RESERVED_CONCURRENCY, 10)
|
|
164
|
+
: undefined,
|
|
165
|
+
},
|
|
166
|
+
logging: {
|
|
167
|
+
groupName: process.env.LOG_GROUP_NAME || '/pipeline-builder/logs',
|
|
168
|
+
retentionDays: parseRetentionDays(process.env.LOG_RETENTION || '7'),
|
|
169
|
+
removalPolicy: process.env.LOG_REMOVAL_POLICY === 'RETAIN' ? 'retain' : 'destroy',
|
|
170
|
+
},
|
|
171
|
+
codeBuild: {
|
|
172
|
+
computeType: (process.env.CODEBUILD_COMPUTE_TYPE || 'SMALL').toUpperCase(),
|
|
173
|
+
defaultImage: process.env.CODEBUILD_DEFAULT_IMAGE || 'pipeline-bootstrap:1.0',
|
|
174
|
+
},
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
/**
|
|
178
|
+
* Valid CloudWatch Logs retention periods, in days. Mirrors the day counts of
|
|
179
|
+
* CDK's `RetentionDays` enum (whose members ARE the day counts), but written out
|
|
180
|
+
* so this module stays free of `aws-cdk-lib` see the note on {@link AWSConfig}.
|
|
181
|
+
* `0` is CDK's `INFINITE` (never expire).
|
|
182
|
+
*/
|
|
183
|
+
const VALID_RETENTION_DAYS = new Set([
|
|
184
|
+
0, 1, 3, 5, 7, 14, 30, 60, 90, 120, 150, 180, 365, 400, 545, 731,
|
|
185
|
+
1096, 1827, 2192, 2557, 2922, 3288, 3653,
|
|
186
|
+
]);
|
|
187
|
+
/**
|
|
188
|
+
* Parse a log-retention string into a valid CloudWatch retention day count.
|
|
189
|
+
*
|
|
190
|
+
* @param days - Retention period in days as a string (e.g. `'30'`)
|
|
191
|
+
* @returns the day count; falls back to 1 day for unknown/invalid values
|
|
192
|
+
*/
|
|
193
|
+
function parseRetentionDays(days) {
|
|
194
|
+
const parsed = parseInt(days, 10);
|
|
195
|
+
return VALID_RETENTION_DAYS.has(parsed) ? parsed : 1;
|
|
196
|
+
}
|
|
197
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5mcmFzdHJ1Y3R1cmUtY29uZmlnLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbmZpZy9pbmZyYXN0cnVjdHVyZS1jb25maWcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsK0NBQStDO0FBQy9DLHNDQUFzQztBQUV0QyxPQUFPLElBQUksTUFBTSxNQUFNLENBQUM7QUFHeEI7Ozs7Ozs7Ozs7Ozs7Ozs7R0FnQkc7QUFDSCxNQUFNLFVBQVUsa0JBQWtCO0lBQ2hDLE1BQU0sSUFBSSxHQUFHLE9BQU8sQ0FBQyxHQUFHLENBQUMsbUJBQW1CLElBQUksVUFBVSxDQUFDO0lBQzNELE1BQU0sSUFBSSxHQUFHLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLG1CQUFtQixJQUFJLE1BQU0sRUFBRSxFQUFFLENBQUMsQ0FBQztJQUNyRSw0RUFBNEU7SUFDNUUsd0VBQXdFO0lBQ3hFLDhFQUE4RTtJQUM5RSw0RUFBNEU7SUFDNUUsTUFBTSxRQUFRLEdBQUcsb0JBQW9CLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxpQkFBaUIsQ0FBQyxDQUFDO0lBQ3JFLE9BQU87UUFDTCxJQUFJO1FBQ0osSUFBSTtRQUNKLFFBQVEsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLHdCQUF3QixJQUFJLFFBQVEsRUFBRSxJQUFJLElBQUksSUFBSTtRQUN4RSxRQUFRLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyx3QkFBd0I7WUFDNUMsQ0FBQyxDQUFDLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLHdCQUF3QixFQUFFLEVBQUUsQ0FBQztZQUNwRCxDQUFDLENBQUMsQ0FBQyxRQUFRLEVBQUUsSUFBSSxJQUFJLElBQUksQ0FBQztRQUM1QixPQUFPLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxjQUFjLElBQUksRUFBRTtRQUN6QyxJQUFJLEVBQUUsT0FBTyxDQUFDLEdBQUcsQ0FBQyxtQkFBbUIsS0FBSyxPQUFPO0tBQ2xELENBQUM7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QUM7SUFDbEMsT0FBTyxvQkFBb0IsQ0FBQyxHQUFHLENBQUMsTUFBTSxDQUFDLENBQUMsQ0FBQyxDQUFDLE1BQU0sQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDO0FBQ3ZELENBQUMiLCJzb3VyY2VzQ29udGVudCI6WyIvLyBDb3B5cmlnaHQgMjAyNiBQaXBlbGluZSBCdWlsZGVyIENvbnRyaWJ1dG9yc1xuLy8gU1BEWC1MaWNlbnNlLUlkZW50aWZpZXI6IEFwYWNoZS0yLjBcblxuaW1wb3J0IHBhdGggZnJvbSAncGF0aCc7XG5pbXBvcnQgdHlwZSB7IEFXU0NvbmZpZywgQnVpbGRDb25maWcsIENvbXBsaWFuY2VDb25maWcsIERhdGFiYXNlQ29uZmlnLCBPYnNlcnZhYmlsaXR5Q29uZmlnLCBQbHVnaW5CdWlsZENvbmZpZywgUmVkaXNDb25maWcsIFJlZ2lzdHJ5Q29uZmlnIH0gZnJvbSAnLi9jb25maWctdHlwZXMuanMnO1xuXG4vKipcbiAqIExvYWQgRG9ja2VyIHJlZ2lzdHJ5IGNvbmZpZ3VyYXRpb24gZnJvbSBlbnZpcm9ubWVudCB2YXJpYWJsZXMuXG4gKlxuICogRW52aXJvbm1lbnQgdmFyaWFibGVzOlxuICogLSBgSU1BR0VfUkVHSVNUUllfSE9TVGAg4oCUIFJlZ2lzdHJ5IGhvc3RuYW1lIChkZWZhdWx0OiBgJ3JlZ2lzdHJ5J2ApXG4gKiAtIGBJTUFHRV9SRUdJU1RSWV9QT1JUYCDigJQgUmVnaXN0cnkgcG9ydCAoZGVmYXVsdDogYDUwMDBgKVxuICogLSBgSU1BR0VfUkVHSVNUUllfUFVMTF9IT1NUYCDigJQgSG9zdCBhbiBvdXQtb2YtY2x1c3RlciBjbGllbnQgKENvZGVCdWlsZClcbiAqICAgdXNlcyB0byBwdWxsIHBsdWdpbiBpbWFnZXMuIFdoZW4gdW5zZXQsIGRlcml2ZWQgZnJvbSBgUExBVEZPUk1fQkFTRV9VUkxgJ3NcbiAqICAgaG9zdCAodGhlIHB1YmxpYyBlbmRwb2ludCBDb2RlQnVpbGQgY2FuIHJlc29sdmUpLCB0aGVuIGBJTUFHRV9SRUdJU1RSWV9IT1NUYC5cbiAqIC0gYElNQUdFX1JFR0lTVFJZX1BVTExfUE9SVGAg4oCUIFBvcnQgZm9yIHRoZSBhYm92ZS4gV2hlbiB1bnNldCwgZGVyaXZlZCBmcm9tXG4gKiAgIGBQTEFURk9STV9CQVNFX1VSTGAncyBwb3J0LCB0aGVuIGBJTUFHRV9SRUdJU1RSWV9QT1JUYC5cbiAqIC0gYERPQ0tFUl9ORVRXT1JLYCDigJQgRG9ja2VyIG5ldHdvcmsgZm9yIGJ1aWxkL3B1c2ggKGRlZmF1bHQ6IGAnJ2ApXG4gKiAtIGBJTUFHRV9SRUdJU1RSWV9IVFRQYCDigJQgVXNlIHBsYWluIEhUVFAgaW5zdGVhZCBvZiBIVFRQUyAoZGVmYXVsdDogYHRydWVgLFxuICogICB0aGUgaW4tY2x1c3RlciByZWdpc3RyeSBoYXMgbm8gVExTKS5cbiAqXG4gKiBAcmV0dXJucyBSZWdpc3RyeSBjb25maWd1cmF0aW9uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsb2FkUmVnaXN0cnlDb25maWcoKTogUmVnaXN0cnlDb25maWcge1xuICBjb25zdCBob3N0ID0gcHJvY2Vzcy5lbnYuSU1BR0VfUkVHSVNUUllfSE9TVCB8fCAncmVnaXN0cnknO1xuICBjb25zdCBwb3J0ID0gcGFyc2VJbnQocHJvY2Vzcy5lbnYuSU1BR0VfUkVHSVNUUllfUE9SVCB8fCAnNTAwMCcsIDEwKTtcbiAgLy8gV2hlbiBubyBleHBsaWNpdCBwdWxsIGhvc3QgaXMgc2V0LCBkZXJpdmUgaXQgZnJvbSBQTEFURk9STV9CQVNFX1VSTCDigJQgdGhlXG4gIC8vIHB1YmxpYyBwbGF0Zm9ybSBlbmRwb2ludCBhbiBvdXQtb2YtY2x1c3RlciBjbGllbnQgKEFXUyBDb2RlQnVpbGQpIGNhblxuICAvLyByZXNvbHZlLCB1bmxpa2UgdGhlIGluLWNsdXN0ZXIgYHJlZ2lzdHJ5YCBDbHVzdGVySVAuIE9ubHkgZmFsbHMgYmFjayB0byB0aGVcbiAgLy8gaW4tY2x1c3RlciBob3N0IHdoZW4gUExBVEZPUk1fQkFTRV9VUkwgaXMgdW5zZXQvdW5wYXJzZWFibGUgKGUuZy4gbG9jYWwpLlxuICBjb25zdCBwbGF0Zm9ybSA9IHBhcnNlUGxhdGZvcm1CYXNlVXJsKHByb2Nlc3MuZW52LlBMQVRGT1JNX0JBU0VfVVJMKTtcbiAgcmV0dXJuIHtcbiAgICBob3N0LFxuICAgIHBvcnQsXG4gICAgcHVsbEhvc3Q6IHByb2Nlc3MuZW52LklNQUdFX1JFR0lTVFJZX1BVTExfSE9TVCB8fCBwbGF0Zm9ybT8uaG9zdCB8fCBob3N0LFxuICAgIHB1bGxQb3J0OiBwcm9jZXNzLmVudi5JTUFHRV9SRUdJU1RSWV9QVUxMX1BPUlRcbiAgICAgID8gcGFyc2VJbnQocHJvY2Vzcy5lbnYuSU1BR0VfUkVHSVNUUllfUFVMTF9QT1JULCAxMClcbiAgICAgIDogKHBsYXRmb3JtPy5wb3J0ID8/IHBvcnQpLFxuICAgIG5ldHdvcms6IHByb2Nlc3MuZW52LkRPQ0tFUl9ORVRXT1JLIHx8ICcnLFxuICAgIGh0dHA6IHByb2Nlc3MuZW52LklNQUdFX1JFR0lTVFJZX0hUVFAgIT09ICdmYWxzZScsXG4gIH07XG59XG5cbi8qKiBFeHRyYWN0IGhvc3QgKyBwb3J0IGZyb20gUExBVEZPUk1fQkFTRV9VUkwgKGUuZy4gYGh0dHBzOi8vcGlwZWxpbmUtYnVpbGRlci5jb21gXG4gKiAg4oaSIGB7IGhvc3Q6ICdwaXBlbGluZS1idWlsZGVyLmNvbScsIHBvcnQ6IDQ0MyB9YCkuIFJldHVybnMgbnVsbCB3aGVuIHRoZSB2YWx1ZVxuICogIGlzIHVuc2V0IG9yIG5vdCBhIHZhbGlkIFVSTC4gRXhwb3J0ZWQgc28gdGhlIENMSSAocGlwZWxpbmUtbWFuYWdlcikgZGVyaXZlc1xuICogIHRoZSByZWdpc3RyeSBwdWxsIHRhcmdldCB3aXRoIHRoZSBTQU1FIHBhcnNpbmcsIGluc3RlYWQgb2YgZHVwbGljYXRpbmcgaXQuICovXG5leHBvcnQgZnVuY3Rpb24gcGFyc2VQbGF0Zm9ybUJhc2VVcmwocmF3Pzogc3RyaW5nKTogeyBob3N0OiBzdHJpbmc7IHBvcnQ6IG51bWJlciB9IHwgbnVsbCB7XG4gIGlmICghcmF3KSByZXR1cm4gbnVsbDtcbiAgdHJ5IHtcbiAgICBjb25zdCB1ID0gbmV3IFVSTChyYXcpO1xuICAgIC8vIEEgc2NoZW1lLWxlc3MgdmFsdWUgbGlrZSBgaG9zdDo4NDQzYCBwYXJzZXMgd2l0aCBhIGN1c3RvbSBwcm90b2NvbCBhbmQgYW5cbiAgICAvLyBFTVBUWSBob3N0bmFtZSDigJQgcmVqZWN0IGl0IHNvIGNhbGxlcnMgZmFsbCBiYWNrIHJhdGhlciB0aGFuIGRlcml2ZSBhXG4gICAgLy8gaG9zdC1sZXNzIGVuZHBvaW50LlxuICAgIGlmICghdS5ob3N0bmFtZSkgcmV0dXJuIG51bGw7XG4gICAgY29uc3QgcG9ydCA9IHUucG9ydCA/IHBhcnNlSW50KHUucG9ydCwgMTApIDogKHUucHJvdG9jb2wgPT09ICdodHRwczonID8gNDQzIDogODApO1xuICAgIHJldHVybiB7IGhvc3Q6IHUuaG9zdG5hbWUsIHBvcnQgfTtcbiAgfSBjYXRjaCB7XG4gICAgcmV0dXJuIG51bGw7XG4gIH1cbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxvYWRSZWRpc0NvbmZpZygpOiBSZWRpc0NvbmZpZyB7XG4gIHJldHVybiB7XG4gICAgaG9zdDogcHJvY2Vzcy5lbnYuUkVESVNfSE9TVCB8fCAnbG9jYWxob3N0JyxcbiAgICBwb3J0OiBwYXJzZUludChwcm9jZXNzLmVudi5SRURJU19QT1JUIHx8ICc2Mzc5JywgMTApLFxuICB9O1xufVxuXG4vKipcbiAqIExvYWQgcGx1Z2luIGJ1aWxkIHF1ZXVlIGNvbmZpZ3VyYXRpb24uXG4gKlxuICogRW52aXJvbm1lbnQgdmFyaWFibGVzOlxuICogLSBgUExVR0lOX0JVSUxEX0NPTkNVUlJFTkNZYCDigJQgTWF4IGNvbmN1cnJlbnQgcGx1Z2luIGJ1aWxkcyAoZGVmYXVsdDogYDFgKVxuICovXG5leHBvcnQgZnVuY3Rpb24gbG9hZFBsdWdpbkJ1aWxkQ29uZmlnKCk6IFBsdWdpbkJ1aWxkQ29uZmlnIHtcbiAgcmV0dXJuIHtcbiAgICBjb25jdXJyZW5jeTogcGFyc2VJbnQocHJvY2Vzcy5lbnYuUExVR0lOX0JVSUxEX0NPTkNVUlJFTkNZIHx8ICcxJywgMTApLFxuICAgIG1heEF0dGVtcHRzOiBwYXJzZUludChwcm9jZXNzLmVudi5QTFVHSU5fQlVJTERfTUFYX0FUVEVNUFRTIHx8ICcyJywgMTApLFxuICAgIGJhY2tvZmZEZWxheU1zOiBwYXJzZUludChwcm9jZXNzLmVudi5QTFVHSU5fQlVJTERfQkFDS09GRl9ERUxBWV9NUyB8fCAnNTAwMCcsIDEwKSxcbiAgICB3b3JrZXJUaW1lb3V0TXM6IHBhcnNlSW50KHByb2Nlc3MuZW52LlBMVUdJTl9CVUlMRF9XT1JLRVJfVElNRU9VVF9NUyB8fCAnMTAwMDAnLCAxMCksXG4gICAgdGVtcERpck1heEFnZU1zOiBwYXJzZUludChwcm9jZXNzLmVudi5URU1QX0RJUl9NQVhfQUdFX01TIHx8ICcxNDQwMDAwMCcsIDEwKSxcbiAgICBkbHFNYXhBdHRlbXB0czogcGFyc2VJbnQocHJvY2Vzcy5lbnYuUExVR0lOX0RMUV9NQVhfQVRURU1QVFMgfHwgJzMnLCAxMCksXG4gICAgZGxxQmFja29mZkJhc2VNczogcGFyc2VJbnQocHJvY2Vzcy5lbnYuUExVR0lOX0RMUV9CQUNLT0ZGX0JBU0VfTVMgfHwgJzMwMDAwMCcsIDEwKSxcbiAgICBkbHFNYXhTaXplOiBwYXJzZUludChwcm9jZXNzLmVudi5QTFVHSU5fRExRX01BWF9TSVpFIHx8ICcyMCcsIDEwKSxcbiAgfTtcbn1cblxuZXhwb3J0IGZ1bmN0aW9uIGxvYWREYXRhYmFzZUNvbmZpZygpOiBEYXRhYmFzZUNvbmZpZyB7XG4gIHJldHVybiB7XG4gICAgcG9zdGdyZXM6IHtcbiAgICAgIGhvc3Q6IHByb2Nlc3MuZW52LkRCX0hPU1QgfHwgJ3Bvc3RncmVzJyxcbiAgICAgIHBvcnQ6IHBhcnNlSW50KHByb2Nlc3MuZW52LkRCX1BPUlQgfHwgJzU0MzInLCAxMCksXG4gICAgICBkYXRhYmFzZTogcHJvY2Vzcy5lbnYuREFUQUJBU0UgfHwgJ3BpcGVsaW5lX2J1aWxkZXInLFxuICAgICAgdXNlcjogcHJvY2Vzcy5lbnYuREJfVVNFUiB8fCAncG9zdGdyZXMnLFxuICAgICAgcGFzc3dvcmQ6IHByb2Nlc3MuZW52LkRCX1BBU1NXT1JEIHx8ICcnLFxuICAgIH0sXG4gICAgZHJpenpsZToge1xuICAgICAgbWF4UG9vbFNpemU6IHBhcnNlSW50KHByb2Nlc3MuZW52LkRSSVpaTEVfTUFYX1BPT0xfU0laRSB8fCAnMjAnLCAxMCksXG4gICAgICBpZGxlVGltZW91dE1pbGxpczogcGFyc2VJbnQocHJvY2Vzcy5lbnYuRFJJWlpMRV9JRExFX1RJTUVPVVRfTUlMTElTIHx8ICczMDAwMCcsIDEwKSxcbiAgICAgIGNvbm5lY3Rpb25UaW1lb3V0TWlsbGlzOiBwYXJzZUludChwcm9jZXNzLmVudi5EUklaWkxFX0NPTk5FQ1RJT05fVElNRU9VVF9NSUxMSVMgfHwgJzEwMDAwJywgMTApLFxuICAgIH0sXG4gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2FkT2JzZXJ2YWJpbGl0eUNvbmZpZygpOiBPYnNlcnZhYmlsaXR5Q29uZmlnIHtcbiAgcmV0dXJuIHtcbiAgICBsb2dMZXZlbDogcHJvY2Vzcy5lbnYuTE9HX0xFVkVMIHx8ICdpbmZvJyxcbiAgICBsb2dGb3JtYXQ6IHByb2Nlc3MuZW52LkxPR19GT1JNQVQgfHwgJ2pzb24nLFxuICAgIHNlcnZpY2VOYW1lOiBwcm9jZXNzLmVudi5TRVJWSUNFX05BTUUgfHwgJ2FwaScsXG4gICAgdHJhY2luZzoge1xuICAgICAgZW5hYmxlZDogcHJvY2Vzcy5lbnYuT1RFTF9UUkFDSU5HX0VOQUJMRUQgPT09ICd0cnVlJyxcbiAgICAgIGVuZHBvaW50OiBwcm9jZXNzLmVudi5PVEVMX0VYUE9SVEVSX09UTFBfRU5EUE9JTlQgfHwgJ2h0dHA6Ly9sb2NhbGhvc3Q6NDMxOC92MS90cmFjZXMnLFxuICAgIH0sXG4gIH07XG59XG5cbmV4cG9ydCBmdW5jdGlvbiBsb2FkQ29tcGxpYW5jZUNvbmZpZygpOiBDb21wbGlhbmNlQ29uZmlnIHtcbiAgcmV0dXJuIHtcbiAgICBzY2FuU2NoZWR1bGVySW50ZXJ2YWxNczogcGFyc2VJbnQocHJvY2Vzcy5lbnYuU0NBTl9TQ0hFRFVMRVJfSU5URVJWQUxfTVMgfHwgJzYwMDAwJywgMTApLFxuICAgIHN5c3RlbU9yZ1NjYW5zRW5hYmxlZDogcHJvY2Vzcy5lbnYuU1lTVEVNX09SR19TQ0FOU19FTkFCTEVEID09PSAndHJ1ZScsXG4gICAgc2NhbkxvY2tUdGxNczogcGFyc2VJbnQocHJvY2Vzcy5lbnYuU0NBTl9MT0NLX1RUTF9NUyB8fCAnMzAwMDAwJywgMTApLFxuICAgIGRpZ2VzdFNjaGVkdWxlckludGVydmFsTXM6IHBhcnNlSW50KHByb2Nlc3MuZW52LkRJR0VTVF9TQ0hFRFVMRVJfSU5URVJWQUxfTVMgfHwgJzM2MDAwMDAnLCAxMCksXG4gICAgZGlnZXN0TG9ja1R0bE1zOiBwYXJzZUludChwcm9jZXNzLmVudi5ESUdFU1RfTE9DS19UVExfTVMgfHwgJzMwMDAwMCcsIDEwKSxcbiAgfTtcbn1cblxuLyoqXG4gKiBMb2FkIHBsdWdpbiBidWlsZCBjb25maWd1cmF0aW9uLiBCdWlsZHMgcnVuIGFnYWluc3QgYSByb290bGVzcyBgbW9ieS9idWlsZGtpdGBcbiAqIHNpZGVjYXIg4oCUIHNlZSBgQlVJTERLSVRfSE9TVGAuXG4gKlxuICogRW52aXJvbm1lbnQgdmFyaWFibGVzOlxuICogLSBgRE9DS0VSX0JVSUxEX1RFTVBfUk9PVGAg4oCUIFRlbXAgZGlyZWN0b3J5IGZvciBidWlsZCBjb250ZXh0cyAoZGVmYXVsdDogYDxjd2Q+L3RtcGApXG4gKiAtIGBET0NLRVJfQlVJTERfVElNRU9VVF9NU2Ag4oCUIEJ1aWxkIHRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIChkZWZhdWx0OiBgOTAwMDAwYCAvIDE1IG1pbilcbiAqIC0gYERPQ0tFUl9QVVNIX1RJTUVPVVRfTVNgIOKAlCBQdXNoIHRpbWVvdXQgaW4gbWlsbGlzZWNvbmRzIChkZWZhdWx0OiBgMzAwMDAwYCAvIDUgbWluKVxuICogLSBgQlVJTERLSVRfSE9TVGAg4oCUIGJ1aWxkY3RsIGAtLWFkZHJgIHZhbHVlIGZvciB0aGUgYnVpbGRraXRkIHNpZGVjYXJcbiAqICAgKGRlZmF1bHQ6IGB1bml4Oi8vL3J1bi9idWlsZGtpdC9idWlsZGtpdGQuc29ja2ApXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsb2FkRG9ja2VyQ29uZmlnKCk6IEJ1aWxkQ29uZmlnIHtcbiAgcmV0dXJuIHtcbiAgICB0ZW1wUm9vdDogcHJvY2Vzcy5lbnYuRE9DS0VSX0JVSUxEX1RFTVBfUk9PVCB8fCBwYXRoLmpvaW4ocHJvY2Vzcy5jd2QoKSwgJ3RtcCcpLFxuICAgIHRpbWVvdXRNczogcGFyc2VJbnQocHJvY2Vzcy5lbnYuRE9DS0VSX0JVSUxEX1RJTUVPVVRfTVMgfHwgJzkwMDAwMCcsIDEwKSxcbiAgICBwdXNoVGltZW91dE1zOiBwYXJzZUludChwcm9jZXNzLmVudi5ET0NLRVJfUFVTSF9USU1FT1VUX01TIHx8ICczMDAwMDAnLCAxMCksXG4gICAgYnVpbGRraXRBZGRyOiBwcm9jZXNzLmVudi5CVUlMREtJVF9IT1NUIHx8ICd1bml4Oi8vL3J1bi9idWlsZGtpdC9idWlsZGtpdGQuc29jaycsXG4gIH07XG59XG5cbi8qKlxuICogTG9hZCBBV1MgaW5mcmFzdHJ1Y3R1cmUgY29uZmlndXJhdGlvbiBmcm9tIGVudmlyb25tZW50IHZhcmlhYmxlcy5cbiAqXG4gKiBFbnZpcm9ubWVudCB2YXJpYWJsZXM6XG4gKiAtIGBMQU1CREFfUlVOVElNRWAg4oCUIExhbWJkYSBydW50aW1lIChkZWZhdWx0OiBgJ25vZGVqczI0LngnYDsgdW5rbm93biB2YWx1ZXMgZmFsbCBiYWNrIHRvIGl0KVxuICogLSBgTEFNQkRBX1RJTUVPVVRgIOKAlCBMYW1iZGEgdGltZW91dCBpbiBzZWNvbmRzIChkZWZhdWx0OiBgOTAwYClcbiAqIC0gYExBTUJEQV9NRU1PUllfU0laRWAg4oCUIExhbWJkYSBtZW1vcnkgaW4gTUIgKGRlZmF1bHQ6IGA1MTJgKVxuICogLSBgTEFNQkRBX0FSQ0hJVEVDVFVSRWAg4oCUIGAneDg2XzY0J2Agb3IgQVJNIChkZWZhdWx0OiBBUk1fNjQpXG4gKiAtIGBMT0dfR1JPVVBfTkFNRWAg4oCUIENsb3VkV2F0Y2ggbG9nIGdyb3VwIChkZWZhdWx0OiBgJy9waXBlbGluZS1idWlsZGVyL2xvZ3MnYClcbiAqIC0gYExPR19SRVRFTlRJT05gIOKAlCBMb2cgcmV0ZW50aW9uIGluIGRheXMgKGRlZmF1bHQ6IGA3YClcbiAqIC0gYExPR19SRU1PVkFMX1BPTElDWWAg4oCUIGAnUkVUQUlOJ2Agb3IgZGVzdHJveSAoZGVmYXVsdDogREVTVFJPWSlcbiAqIC0gYENPREVCVUlMRF9DT01QVVRFX1RZUEVgIOKAlCBDb2RlQnVpbGQgY29tcHV0ZSB0eXBlIChkZWZhdWx0OiBgJ1NNQUxMJ2ApXG4gKlxuICogQHJldHVybnMgQVdTIGluZnJhc3RydWN0dXJlIGNvbmZpZ3VyYXRpb25cbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGxvYWRBV1NDb25maWcoKTogQVdTQ29uZmlnIHtcbiAgcmV0dXJuIHtcbiAgICBsYW1iZGE6IHtcbiAgICAgIHJ1bnRpbWU6IHByb2Nlc3MuZW52LkxBTUJEQV9SVU5USU1FIHx8ICdub2RlanMyNC54JyxcbiAgICAgIHRpbWVvdXRTZWNvbmRzOiBwYXJzZUludChwcm9jZXNzLmVudi5MQU1CREFfVElNRU9VVCB8fCAnOTAwJywgMTApLFxuICAgICAgbWVtb3J5U2l6ZTogcGFyc2VJbnQocHJvY2Vzcy5lbnYuTEFNQkRBX01FTU9SWV9TSVpFIHx8ICc1MTInLCAxMCksXG4gICAgICBhcmNoaXRlY3R1cmU6IHByb2Nlc3MuZW52LkxBTUJEQV9BUkNISVRFQ1RVUkUgPT09ICd4ODZfNjQnID8gJ3g4Nl82NCcgOiAnYXJtNjQnLFxuICAgICAgcmVzZXJ2ZWRDb25jdXJyZW50RXhlY3V0aW9uczogcHJvY2Vzcy5lbnYuTEFNQkRBX1JFU0VSVkVEX0NPTkNVUlJFTkNZXG4gICAgICAgID8gcGFyc2VJbnQocHJvY2Vzcy5lbnYuTEFNQkRBX1JFU0VSVkVEX0NPTkNVUlJFTkNZLCAxMClcbiAgICAgICAgOiB1bmRlZmluZWQsXG4gICAgfSxcblxuICAgIGxvZ2dpbmc6IHtcbiAgICAgIGdyb3VwTmFtZTogcHJvY2Vzcy5lbnYuTE9HX0dST1VQX05BTUUgfHwgJy9waXBlbGluZS1idWlsZGVyL2xvZ3MnLFxuICAgICAgcmV0ZW50aW9uRGF5czogcGFyc2VSZXRlbnRpb25EYXlzKHByb2Nlc3MuZW52LkxPR19SRVRFTlRJT04gfHwgJzcnKSxcbiAgICAgIHJlbW92YWxQb2xpY3k6IHByb2Nlc3MuZW52LkxPR19SRU1PVkFMX1BPTElDWSA9PT0gJ1JFVEFJTicgPyAncmV0YWluJyA6ICdkZXN0cm95JyxcbiAgICB9LFxuXG4gICAgY29kZUJ1aWxkOiB7XG4gICAgICBjb21wdXRlVHlwZTogKHByb2Nlc3MuZW52LkNPREVCVUlMRF9DT01QVVRFX1RZUEUgfHwgJ1NNQUxMJykudG9VcHBlckNhc2UoKSxcbiAgICAgIGRlZmF1bHRJbWFnZTogcHJvY2Vzcy5lbnYuQ09ERUJVSUxEX0RFRkFVTFRfSU1BR0UgfHwgJ3BpcGVsaW5lLWJvb3RzdHJhcDoxLjAnLFxuICAgIH0sXG4gIH07XG59XG5cbi8qKlxuICogVmFsaWQgQ2xvdWRXYXRjaCBMb2dzIHJldGVudGlvbiBwZXJpb2RzLCBpbiBkYXlzLiBNaXJyb3JzIHRoZSBkYXkgY291bnRzIG9mXG4gKiBDREsncyBgUmV0ZW50aW9uRGF5c2AgZW51bSAod2hvc2UgbWVtYmVycyBBUkUgdGhlIGRheSBjb3VudHMpLCBidXQgd3JpdHRlbiBvdXRcbiAqIHNvIHRoaXMgbW9kdWxlIHN0YXlzIGZyZWUgb2YgYGF3cy1jZGstbGliYCAgc2VlIHRoZSBub3RlIG9uIHtAbGluayBBV1NDb25maWd9LlxuICogYDBgIGlzIENESydzIGBJTkZJTklURWAgKG5ldmVyIGV4cGlyZSkuXG4gKi9cbmNvbnN0IFZBTElEX1JFVEVOVElPTl9EQVlTID0gbmV3IFNldChbXG4gIDAsIDEsIDMsIDUsIDcsIDE0LCAzMCwgNjAsIDkwLCAxMjAsIDE1MCwgMTgwLCAzNjUsIDQwMCwgNTQ1LCA3MzEsXG4gIDEwOTYsIDE4MjcsIDIxOTIsIDI1NTcsIDI5MjIsIDMyODgsIDM2NTMsXG5dKTtcblxuLyoqXG4gKiBQYXJzZSBhIGxvZy1yZXRlbnRpb24gc3RyaW5nIGludG8gYSB2YWxpZCBDbG91ZFdhdGNoIHJldGVudGlvbiBkYXkgY291bnQuXG4gKlxuICogQHBhcmFtIGRheXMgLSBSZXRlbnRpb24gcGVyaW9kIGluIGRheXMgYXMgYSBzdHJpbmcgKGUuZy4gYCczMCdgKVxuICogQHJldHVybnMgdGhlIGRheSBjb3VudDsgZmFsbHMgYmFjayB0byAxIGRheSBmb3IgdW5rbm93bi9pbnZhbGlkIHZhbHVlc1xuICovXG5mdW5jdGlvbiBwYXJzZVJldGVudGlvbkRheXMoZGF5czogc3RyaW5nKTogbnVtYmVyIHtcbiAgY29uc3QgcGFyc2VkID0gcGFyc2VJbnQoZGF5cywgMTApO1xuICByZXR1cm4gVkFMSURfUkVURU5USU9OX0RBWVMuaGFzKHBhcnNlZCkgPyBwYXJzZWQgOiAxO1xufVxuIl19
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { ServerConfig, AuthConfig, RateLimitConfig } from './config-types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Load server configuration from environment variables.
|
|
4
|
+
*
|
|
5
|
+
* Environment variables:
|
|
6
|
+
* - `PORT` — HTTP listen port (default: `3000`)
|
|
7
|
+
* - `CORS_ORIGIN` — Comma-separated allowed origins (default: `PLATFORM_BASE_URL`)
|
|
8
|
+
* - `CORS_CREDENTIALS` — Allow credentials; set to `'false'` to disable (default: `true`)
|
|
9
|
+
* - `TRUST_PROXY` — Express trust proxy hops (default: `1`)
|
|
10
|
+
* - `PLATFORM_BASE_URL` — Frontend URL used as CORS fallback (default: `'https://localhost:8443'`)
|
|
11
|
+
*
|
|
12
|
+
* @returns Server configuration with port, CORS, trust proxy, and platform URL
|
|
13
|
+
*/
|
|
14
|
+
export declare function loadServerConfig(): ServerConfig;
|
|
15
|
+
/**
|
|
16
|
+
* Load authentication configuration from environment variables.
|
|
17
|
+
*
|
|
18
|
+
* Environment variables:
|
|
19
|
+
* - `JWT_SECRET` — **Required.** Secret key for signing JWTs
|
|
20
|
+
* - `REFRESH_TOKEN_SECRET` — **Required.** Secret key for signing refresh tokens
|
|
21
|
+
* - `JWT_EXPIRES_IN` — JWT lifetime in seconds (default: `7200` = 2 hours)
|
|
22
|
+
* - `JWT_ALGORITHM` — JWT signing algorithm (default: `'HS256'`)
|
|
23
|
+
* - `BCRYPT_SALT_ROUNDS` — bcrypt salt rounds for password hashing (default: `12`)
|
|
24
|
+
* - `REFRESH_TOKEN_EXPIRES_IN` — Refresh token lifetime in seconds (default: `2592000` = 30 days)
|
|
25
|
+
*
|
|
26
|
+
* @returns Authentication configuration with safe defaults (empty strings when env vars are unset).
|
|
27
|
+
* Call {@link validateAuthConfig} at server startup to enforce required secrets.
|
|
28
|
+
*/
|
|
29
|
+
export declare function loadAuthConfig(): AuthConfig;
|
|
30
|
+
/**
|
|
31
|
+
* Load rate limiting configuration from environment variables.
|
|
32
|
+
*
|
|
33
|
+
* Environment variables:
|
|
34
|
+
* - `LIMITER_MAX` — Max requests per window (default: `100`)
|
|
35
|
+
* - `LIMITER_WINDOWMS` — Rate limit window in ms (default: `900000` = 15 minutes)
|
|
36
|
+
*
|
|
37
|
+
* @returns Rate limit configuration
|
|
38
|
+
*/
|
|
39
|
+
export declare function loadRateLimitConfig(): RateLimitConfig;
|
|
40
|
+
/**
|
|
41
|
+
* Validate server configuration and log warnings for insecure settings.
|
|
42
|
+
*
|
|
43
|
+
* @param config - Server configuration to validate
|
|
44
|
+
*/
|
|
45
|
+
export declare function validateServerConfig(config: ServerConfig): void;
|
|
46
|
+
/**
|
|
47
|
+
* Validate authentication configuration (JWT secrets, algorithms, expiration).
|
|
48
|
+
* Call this at server startup, not during CDK synthesis.
|
|
49
|
+
*
|
|
50
|
+
* @param config - Auth configuration to validate
|
|
51
|
+
* @throws {Error} If secrets are insecure, too short (<32 chars), or use disallowed algorithms
|
|
52
|
+
*/
|
|
53
|
+
export declare function validateAuthConfig(config: AuthConfig): void;
|
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { createLogger } from '@pipeline-builder/api-core';
|
|
4
|
+
import { CoreConstants } from './app-config.js';
|
|
5
|
+
const log = createLogger('server-config');
|
|
6
|
+
/**
|
|
7
|
+
* Load server configuration from environment variables.
|
|
8
|
+
*
|
|
9
|
+
* Environment variables:
|
|
10
|
+
* - `PORT` — HTTP listen port (default: `3000`)
|
|
11
|
+
* - `CORS_ORIGIN` — Comma-separated allowed origins (default: `PLATFORM_BASE_URL`)
|
|
12
|
+
* - `CORS_CREDENTIALS` — Allow credentials; set to `'false'` to disable (default: `true`)
|
|
13
|
+
* - `TRUST_PROXY` — Express trust proxy hops (default: `1`)
|
|
14
|
+
* - `PLATFORM_BASE_URL` — Frontend URL used as CORS fallback (default: `'https://localhost:8443'`)
|
|
15
|
+
*
|
|
16
|
+
* @returns Server configuration with port, CORS, trust proxy, and platform URL
|
|
17
|
+
*/
|
|
18
|
+
export function loadServerConfig() {
|
|
19
|
+
return {
|
|
20
|
+
port: parseInt(process.env.PORT || '3000', 10),
|
|
21
|
+
cors: {
|
|
22
|
+
credentials: process.env.CORS_CREDENTIALS !== 'false',
|
|
23
|
+
origin: process.env.CORS_ORIGIN
|
|
24
|
+
? process.env.CORS_ORIGIN.split(',').map(o => o.trim())
|
|
25
|
+
: [process.env.PLATFORM_BASE_URL || CoreConstants.DEFAULT_PLATFORM_URL],
|
|
26
|
+
},
|
|
27
|
+
trustProxy: parseInt(process.env.TRUST_PROXY || '1', 10),
|
|
28
|
+
platformUrl: process.env.PLATFORM_BASE_URL || CoreConstants.DEFAULT_PLATFORM_URL,
|
|
29
|
+
httpClient: {
|
|
30
|
+
timeout: parseInt(process.env.HTTP_CLIENT_TIMEOUT || '5000', 10),
|
|
31
|
+
maxRetries: parseInt(process.env.HTTP_CLIENT_MAX_RETRIES || '2', 10),
|
|
32
|
+
retryDelayMs: parseInt(process.env.HTTP_CLIENT_RETRY_DELAY_MS || '200', 10),
|
|
33
|
+
},
|
|
34
|
+
sse: {
|
|
35
|
+
maxClientsPerRequest: parseInt(process.env.SSE_MAX_CLIENTS_PER_REQUEST || '10', 10),
|
|
36
|
+
clientTimeoutMs: parseInt(process.env.SSE_CLIENT_TIMEOUT_MS || '1800000', 10),
|
|
37
|
+
cleanupIntervalMs: parseInt(process.env.SSE_CLEANUP_INTERVAL_MS || '300000', 10),
|
|
38
|
+
},
|
|
39
|
+
services: {
|
|
40
|
+
pluginHost: process.env.PLUGIN_SERVICE_HOST || 'plugin',
|
|
41
|
+
pluginPort: parseInt(process.env.PLUGIN_SERVICE_PORT || '3000', 10),
|
|
42
|
+
pipelineHost: process.env.PIPELINE_SERVICE_HOST || 'pipeline',
|
|
43
|
+
pipelinePort: parseInt(process.env.PIPELINE_SERVICE_PORT || '3000', 10),
|
|
44
|
+
messageHost: process.env.MESSAGE_SERVICE_HOST || 'message',
|
|
45
|
+
messagePort: parseInt(process.env.MESSAGE_SERVICE_PORT || '3000', 10),
|
|
46
|
+
platformHost: process.env.PLATFORM_SERVICE_HOST || 'platform',
|
|
47
|
+
platformPort: parseInt(process.env.PLATFORM_SERVICE_PORT || '3000', 10),
|
|
48
|
+
complianceHost: process.env.COMPLIANCE_SERVICE_HOST || 'compliance',
|
|
49
|
+
compliancePort: parseInt(process.env.COMPLIANCE_SERVICE_PORT || '3000', 10),
|
|
50
|
+
billingHost: process.env.BILLING_SERVICE_HOST || 'billing',
|
|
51
|
+
billingPort: parseInt(process.env.BILLING_SERVICE_PORT || '3000', 10),
|
|
52
|
+
billingTimeout: parseInt(process.env.BILLING_SERVICE_TIMEOUT || '5000', 10),
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Load authentication configuration from environment variables.
|
|
58
|
+
*
|
|
59
|
+
* Environment variables:
|
|
60
|
+
* - `JWT_SECRET` — **Required.** Secret key for signing JWTs
|
|
61
|
+
* - `REFRESH_TOKEN_SECRET` — **Required.** Secret key for signing refresh tokens
|
|
62
|
+
* - `JWT_EXPIRES_IN` — JWT lifetime in seconds (default: `7200` = 2 hours)
|
|
63
|
+
* - `JWT_ALGORITHM` — JWT signing algorithm (default: `'HS256'`)
|
|
64
|
+
* - `BCRYPT_SALT_ROUNDS` — bcrypt salt rounds for password hashing (default: `12`)
|
|
65
|
+
* - `REFRESH_TOKEN_EXPIRES_IN` — Refresh token lifetime in seconds (default: `2592000` = 30 days)
|
|
66
|
+
*
|
|
67
|
+
* @returns Authentication configuration with safe defaults (empty strings when env vars are unset).
|
|
68
|
+
* Call {@link validateAuthConfig} at server startup to enforce required secrets.
|
|
69
|
+
*/
|
|
70
|
+
export function loadAuthConfig() {
|
|
71
|
+
return {
|
|
72
|
+
jwt: {
|
|
73
|
+
secret: process.env.JWT_SECRET ?? '',
|
|
74
|
+
expiresIn: parseInt(process.env.JWT_EXPIRES_IN || '7200', 10),
|
|
75
|
+
algorithm: (process.env.JWT_ALGORITHM || 'HS256'),
|
|
76
|
+
saltRounds: parseInt(process.env.BCRYPT_SALT_ROUNDS || '12', 10),
|
|
77
|
+
},
|
|
78
|
+
refreshToken: {
|
|
79
|
+
secret: process.env.REFRESH_TOKEN_SECRET || '',
|
|
80
|
+
expiresIn: parseInt(process.env.REFRESH_TOKEN_EXPIRES_IN || '2592000', 10),
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Load rate limiting configuration from environment variables.
|
|
86
|
+
*
|
|
87
|
+
* Environment variables:
|
|
88
|
+
* - `LIMITER_MAX` — Max requests per window (default: `100`)
|
|
89
|
+
* - `LIMITER_WINDOWMS` — Rate limit window in ms (default: `900000` = 15 minutes)
|
|
90
|
+
*
|
|
91
|
+
* @returns Rate limit configuration
|
|
92
|
+
*/
|
|
93
|
+
export function loadRateLimitConfig() {
|
|
94
|
+
return {
|
|
95
|
+
max: parseInt(process.env.LIMITER_MAX || '100', 10),
|
|
96
|
+
windowMs: parseInt(process.env.LIMITER_WINDOWMS || '900000', 10),
|
|
97
|
+
legacyHeaders: false,
|
|
98
|
+
standardHeaders: true,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Validate server configuration and log warnings for insecure settings.
|
|
103
|
+
*
|
|
104
|
+
* @param config - Server configuration to validate
|
|
105
|
+
*/
|
|
106
|
+
export function validateServerConfig(config) {
|
|
107
|
+
const warnings = [];
|
|
108
|
+
// Check CORS configuration
|
|
109
|
+
const origin = config.cors.origin;
|
|
110
|
+
const isWildcard = origin === '*' || (Array.isArray(origin) && origin.includes('*'));
|
|
111
|
+
if (isWildcard) {
|
|
112
|
+
warnings.push('CORS origin set to wildcard (*) - consider restricting to specific domains');
|
|
113
|
+
}
|
|
114
|
+
if (isWildcard && config.cors.credentials) {
|
|
115
|
+
// Browsers reject this combination, but it indicates a misconfiguration
|
|
116
|
+
throw new Error('SECURITY ERROR: CORS_ORIGIN=* with CORS_CREDENTIALS=true is an invalid and insecure configuration. ' +
|
|
117
|
+
'Set CORS_ORIGIN to specific domains or disable CORS_CREDENTIALS.');
|
|
118
|
+
}
|
|
119
|
+
// Check platform URL uses HTTPS
|
|
120
|
+
if (config.platformUrl.startsWith('http://') &&
|
|
121
|
+
!config.platformUrl.includes('localhost')) {
|
|
122
|
+
warnings.push('Platform URL uses HTTP instead of HTTPS - insecure for production');
|
|
123
|
+
}
|
|
124
|
+
// Display warnings
|
|
125
|
+
if (warnings.length > 0) {
|
|
126
|
+
log.warn('Server configuration warnings:');
|
|
127
|
+
warnings.forEach(warning => log.warn(` - ${warning}`));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
const INSECURE_PATTERNS = ['secret', 'password', 'changeme', 'default', '123456', 'admin'];
|
|
131
|
+
function isInsecureSecret(secret) {
|
|
132
|
+
const lower = secret.toLowerCase();
|
|
133
|
+
return INSECURE_PATTERNS.some(s => lower === s || (lower.length < 64 && lower.includes(s)));
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Validate authentication configuration (JWT secrets, algorithms, expiration).
|
|
137
|
+
* Call this at server startup, not during CDK synthesis.
|
|
138
|
+
*
|
|
139
|
+
* @param config - Auth configuration to validate
|
|
140
|
+
* @throws {Error} If secrets are insecure, too short (<32 chars), or use disallowed algorithms
|
|
141
|
+
*/
|
|
142
|
+
export function validateAuthConfig(config) {
|
|
143
|
+
const errors = [];
|
|
144
|
+
const warnings = [];
|
|
145
|
+
// Validate both secrets with the same checks
|
|
146
|
+
for (const [label, secret] of [['JWT', config.jwt.secret], ['Refresh token', config.refreshToken.secret]]) {
|
|
147
|
+
if (secret.length < 32) {
|
|
148
|
+
errors.push(`${label} secret should be at least 32 characters long`);
|
|
149
|
+
}
|
|
150
|
+
if (isInsecureSecret(secret)) {
|
|
151
|
+
errors.push(`${label} secret appears to be insecure or default value`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
// Check JWT expiration times
|
|
155
|
+
if (config.jwt.expiresIn > 86400) {
|
|
156
|
+
errors.push('JWT expiration must not exceed 24 hours (86400 seconds)');
|
|
157
|
+
}
|
|
158
|
+
else if (config.jwt.expiresIn > 7200) {
|
|
159
|
+
warnings.push('JWT expiration time is greater than 2 hours - shorter expiration recommended');
|
|
160
|
+
}
|
|
161
|
+
// Check algorithm
|
|
162
|
+
if (!CoreConstants.ALLOWED_JWT_ALGORITHMS.includes(config.jwt.algorithm)) {
|
|
163
|
+
errors.push(`JWT algorithm ${config.jwt.algorithm} is not in the allowed list`);
|
|
164
|
+
}
|
|
165
|
+
// Display warnings
|
|
166
|
+
if (warnings.length > 0) {
|
|
167
|
+
log.warn('Auth configuration warnings:');
|
|
168
|
+
warnings.forEach(warning => log.warn(` - ${warning}`));
|
|
169
|
+
}
|
|
170
|
+
// Throw errors
|
|
171
|
+
if (errors.length > 0) {
|
|
172
|
+
throw new Error(`Auth configuration validation failed:\n${errors.map(e => ` - ${e}`).join('\n')}`);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmVyLWNvbmZpZy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9jb25maWcvc2VydmVyLWNvbmZpZy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSwrQ0FBK0M7QUFDL0Msc0NBQXNDO0FBRXRDLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSw0QkFBNEIsQ0FBQztBQUUxRCxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0saUJBQWlCLENBQUM7QUFHaEQsTUFBTSxHQUFHLEdBQUcsWUFBWSxDQUFDLGVBQWUsQ0FBQyxDQUFDO0FBRTFDOzs7Ozs7Ozs7OztHQVdHO0FBQ0gsTUFBTSxVQUFVLGdCQUFnQjtJQUM5QixPQUFPO1FBQ0wsSUFBSSxFQUFFLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLElBQUksSUFBSSxNQUFNLEVBQUUsRUFBRSxDQUFDO1FBQzlDLElBQUksRUFBRTtZQUNKLFdBQVcsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLGdCQUFnQixLQUFLLE9BQU87WUFDckQsTUFBTSxFQUFFLE9BQU8sQ0FBQyxHQUFHLENBQUMsV0FBVztnQkFDN0IsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLEtBQUssQ0FBQyxHQUFHLENBQUMsQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsSUFBSSxFQUFFLENBQUM7Z0JBQ3ZELENBQUMsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxHQUFHLENBQUMsaUJBQWlCLElBQUksYUFBYSxDQUFDLG9CQUFvQixDQUFDO1NBQzFFO1FBQ0QsVUFBVSxFQUFFLFFBQVEsQ0FBQyxPQUFPLENBQUMsR0FBRyxDQUFDLFdBQVcsSUFBSSxHQUFHLEVBQUUsRUFBRSxDQUFDO1FBQ3hELFdBQVcsRUFBRSxPQUFPLENBQUMsR0FBRyxDQUFDLGlCQUFpQixJQUFJLGFBQWEsQ0FBQyxvQkFBb0I7UUFFaEYsVUFBVSxFQUFFO1lBQ1YsT0FBTyxFQUFF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SUFFRCxlQUFlO0lBQ2YsSUFBSSxNQUFNLENBQUMsTUFBTSxHQUFHLENBQUMsRUFBRSxDQUFDO1FBQ3RCLE1BQU0sSUFBSSxLQUFLLENBQ2IsMENBQTBDLE1BQU0sQ0FBQyxHQUFHLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUMsSUFBSSxDQUFDLElBQUksQ0FBQyxFQUFFLENBQ25GLENBQUM7SUFDSixDQUFDO0FBQ0gsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgeyBjcmVhdGVMb2dnZXIgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgdHlwZSB7IEFsZ29yaXRobSB9IGZyb20gJ2pzb253ZWJ0b2tlbic7XG5pbXBvcnQgeyBDb3JlQ29uc3RhbnRzIH0gZnJvbSAnLi9hcHAtY29uZmlnLmpzJztcbmltcG9ydCB0eXBlIHsgU2VydmVyQ29uZmlnLCBBdXRoQ29uZmlnLCBSYXRlTGltaXRDb25maWcgfSBmcm9tICcuL2NvbmZpZy10eXBlcy5qcyc7XG5cbmNvbnN0IGxvZyA9IGNyZWF0ZUxvZ2dlcignc2VydmVyLWNvbmZpZycpO1xuXG4vKipcbiAqIExvYWQgc2VydmVyIGNvbmZpZ3VyYXRpb24gZnJvbSBlbnZpcm9ubWVudCB2YXJpYWJsZXMuXG4gKlxuICogRW52aXJvbm1lbnQgdmFyaWFibGVzOlxuICogLSBgUE9SVGAg4oCUIEhUVFAgbGlzdGVuIHBvcnQgKGRlZmF1bHQ6IGAzMDAwYClcbiAqIC0gYENPUlNfT1JJR0lOYCDigJQgQ29tbWEtc2VwYXJhdGVkIGFsbG93ZWQgb3JpZ2lucyAoZGVmYXVsdDogYFBMQVRGT1JNX0JBU0VfVVJMYClcbiAqIC0gYENPUlNfQ1JFREVOVElBTFNgIOKAlCBBbGxvdyBjcmVkZW50aWFsczsgc2V0IHRvIGAnZmFsc2UnYCB0byBkaXNhYmxlIChkZWZhdWx0OiBgdHJ1ZWApXG4gKiAtIGBUUlVTVF9QUk9YWWAg4oCUIEV4cHJlc3MgdHJ1c3QgcHJveHkgaG9wcyAoZGVmYXVsdDogYDFgKVxuICogLSBgUExBVEZPUk1fQkFTRV9VUkxgIOKAlCBGcm9udGVuZCBVUkwgdXNlZCBhcyBDT1JTIGZhbGxiYWNrIChkZWZhdWx0OiBgJ2h0dHBzOi8vbG9jYWxob3N0Ojg0NDMnYClcbiAqXG4gKiBAcmV0dXJucyBTZXJ2ZXIgY29uZmlndXJhdGlvbiB3aXRoIHBvcnQsIENPUlMsIHRydXN0IHByb3h5LCBhbmQgcGxhdGZvcm0gVVJMXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsb2FkU2VydmVyQ29uZmlnKCk6IFNlcnZlckNvbmZpZyB7XG4gIHJldHVybiB7XG4gICAgcG9ydDogcGFyc2VJbnQocHJvY2Vzcy5lbnYuUE9SVCB8fCAnMzAwMCcsIDEwKSxcbiAgICBjb3JzOiB7XG4gICAgICBjcmVkZW50aWFsczogcHJvY2Vzcy5lbnYuQ09SU19DUkVERU5USUFMUyAhPT0gJ2ZhbHNlJyxcbiAgICAgIG9yaWdpbjogcHJvY2Vzcy5lbnYuQ09SU19PUklHSU5cbiAgICAgICAgPyBwcm9jZXNzLmVudi5DT1JTX09SSUdJTi5zcGxpdCgnLCcpLm1hcChvID0+IG8udHJpbSgpKVxuICAgICAgICA6IFtwcm9jZXNzLmVudi5QTEFURk9STV9CQVNFX1VSTCB8fCBDb3JlQ29uc3RhbnRzLkRFRkFVTFRfUExBVEZPUk1fVVJMXSxcbiAgICB9LFxuICAgIHRydXN0UHJveHk6IHBhcnNlSW50KHByb2Nlc3MuZW52LlRSVVNUX1BST1hZIHx8ICcxJywgMTApLFxuICAgIHBsYXRmb3JtVXJsOiBwcm9jZXNzLmVudi5QTEFURk9STV9CQVNFX1VSTCB8fCBDb3JlQ29uc3RhbnRzLkRFRkFVTFRfUExBVEZPUk1fVVJMLFxuXG4gICAgaHR0cENsaWVudDoge1xuICAgICAgdGltZW91dDogcGFyc2VJbnQocHJvY2Vzcy5lbnYuSFRUUF9DTElFTlRfVElNRU9VVCB8fCAnNTAwMCcsIDEwKSxcbiAgICAgIG1heFJldHJpZXM6IHBhcnNlSW50KHByb2Nlc3MuZW52LkhUVFBfQ0xJRU5UX01BWF9SRVRSSUVTIHx8ICcyJywgMTApLFxuICAgICAgcmV0cnlEZWxheU1zOiBwYXJzZUludChwcm9jZXNzLmVudi5IVFRQX0NMSUVOVF9SRVRSWV9ERUxBWV9NUyB8fCAnMjAwJywgMTApLFxuICAgIH0sXG5cbiAgICBzc2U6IHtcbiAgICAgIG1heENsaWVudHNQZXJSZXF1ZXN0OiBwYXJzZUludChwcm9jZXNzLmVudi5TU0VfTUFYX0NMSUVOVFNfUEVSX1JFUVVFU1QgfHwgJzEwJywgMTApLFxuICAgICAgY2xpZW50VGltZW91dE1zOiBwYXJzZUludChwcm9jZXNzLmVudi5TU0VfQ0xJRU5UX1RJTUVPVVRfTVMgfHwgJzE4MDAwMDAnLCAxMCksXG4gICAgICBjbGVhbnVwSW50ZXJ2YWxNczogcGFyc2VJbnQocHJvY2Vzcy5lbnYuU1NFX0NMRUFOVVBfSU5URVJWQUxfTVMgfHwgJzMwMDAwMCcsIDEwKSxcbiAgICB9LFxuXG4gICAgc2VydmljZXM6IHtcbiAgICAgIHBsdWdpbkhvc3Q6IHByb2Nlc3MuZW52LlBMVUdJTl9TRVJWSUNFX0hPU1QgfHwgJ3BsdWdpbicsXG4gICAgICBwbHVnaW5Qb3J0OiBwYXJzZUludChwcm9jZXNzLmVudi5QTFVHSU5fU0VSVklDRV9QT1JUIHx8ICczMDAwJywgMTApLFxuICAgICAgcGlwZWxpbmVIb3N0OiBwcm9jZXNzLmVudi5QSVBFTElORV9TRVJWSUNFX0hPU1QgfHwgJ3BpcGVsaW5lJyxcbiAgICAgIHBpcGVsaW5lUG9ydDogcGFyc2VJbnQocHJvY2Vzcy5lbnYuUElQRUxJTkVfU0VSVklDRV9QT1JUIHx8ICczMDAwJywgMTApLFxuICAgICAgbWVzc2FnZUhvc3Q6IHByb2Nlc3MuZW52Lk1FU1NBR0VfU0VSVklDRV9IT1NUIHx8ICdtZXNzYWdlJyxcbiAgICAgIG1lc3NhZ2VQb3J0OiBwYXJzZUludChwcm9jZXNzLmVudi5NRVNTQUdFX1NFUlZJQ0VfUE9SVCB8fCAnMzAwMCcsIDEwKSxcbiAgICAgIHBsYXRmb3JtSG9zdDogcHJvY2Vzcy5lbnYuUExBVEZPUk1fU0VSVklDRV9IT1NUIHx8ICdwbGF0Zm9ybScsXG4gICAgICBwbGF0Zm9ybVBvcnQ6IHBhcnNlSW50KHByb2Nlc3MuZW52LlBMQVRGT1JNX1NFUlZJQ0VfUE9SVCB8fCAnMzAwMCcsIDEwKSxcbiAgICAgIGNvbXBsaWFuY2VIb3N0OiBwcm9jZXNzLmVudi5DT01QTElBTkNFX1NFUlZJQ0VfSE9TVCB8fCAnY29tcGxpYW5jZScsXG4gICAgICBjb21wbGlhbmNlUG9ydDogcGFyc2VJbnQocHJvY2Vzcy5lbnYuQ09NUExJQU5DRV9TRVJWSUNFX1BPUlQgfHwgJzMwMDAnLCAxMCksXG4gICAgICBiaWxsaW5nSG9zdDogcHJvY2Vzcy5lbnYuQklMTElOR19TRVJWSUNFX0hPU1QgfHwgJ2JpbGxpbmcnLFxuICAgICAgYmlsbGluZ1BvcnQ6IHBhcnNlSW50KHByb2Nlc3MuZW52LkJJTExJTkdfU0VSVklDRV9QT1JUIHx8ICczMDAwJywgMTApLFxuICAgICAgYmlsbGluZ1RpbWVvdXQ6IHBhcnNlSW50KHByb2Nlc3MuZW52LkJJTExJTkdfU0VSVklDRV9USU1FT1VUIHx8ICc1MDAwJywgMTApLFxuICAgIH0sXG4gIH07XG59XG5cbi8qKlxuICogTG9hZCBhdXRoZW50aWNhdGlvbiBjb25maWd1cmF0aW9uIGZyb20gZW52aXJvbm1lbnQgdmFyaWFibGVzLlxuICpcbiAqIEVudmlyb25tZW50IHZhcmlhYmxlczpcbiAqIC0gYEpXVF9TRUNSRVRgIOKAlCAqKlJlcXVpcmVkLioqIFNlY3JldCBrZXkgZm9yIHNpZ25pbmcgSldUc1xuICogLSBgUkVGUkVTSF9UT0tFTl9TRUNSRVRgIOKAlCAqKlJlcXVpcmVkLioqIFNlY3JldCBrZXkgZm9yIHNpZ25pbmcgcmVmcmVzaCB0b2tlbnNcbiAqIC0gYEpXVF9FWFBJUkVTX0lOYCDigJQgSldUIGxpZmV0aW1lIGluIHNlY29uZHMgKGRlZmF1bHQ6IGA3MjAwYCA9IDIgaG91cnMpXG4gKiAtIGBKV1RfQUxHT1JJVEhNYCDigJQgSldUIHNpZ25pbmcgYWxnb3JpdGhtIChkZWZhdWx0OiBgJ0hTMjU2J2ApXG4gKiAtIGBCQ1JZUFRfU0FMVF9ST1VORFNgIOKAlCBiY3J5cHQgc2FsdCByb3VuZHMgZm9yIHBhc3N3b3JkIGhhc2hpbmcgKGRlZmF1bHQ6IGAxMmApXG4gKiAtIGBSRUZSRVNIX1RPS0VOX0VYUElSRVNfSU5gIOKAlCBSZWZyZXNoIHRva2VuIGxpZmV0aW1lIGluIHNlY29uZHMgKGRlZmF1bHQ6IGAyNTkyMDAwYCA9IDMwIGRheXMpXG4gKlxuICogQHJldHVybnMgQXV0aGVudGljYXRpb24gY29uZmlndXJhdGlvbiB3aXRoIHNhZmUgZGVmYXVsdHMgKGVtcHR5IHN0cmluZ3Mgd2hlbiBlbnYgdmFycyBhcmUgdW5zZXQpLlxuICogQ2FsbCB7QGxpbmsgdmFsaWRhdGVBdXRoQ29uZmlnfSBhdCBzZXJ2ZXIgc3RhcnR1cCB0byBlbmZvcmNlIHJlcXVpcmVkIHNlY3JldHMuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsb2FkQXV0aENvbmZpZygpOiBBdXRoQ29uZmlnIHtcbiAgcmV0dXJuIHtcbiAgICBqd3Q6IHtcbiAgICAgIHNlY3JldDogcHJvY2Vzcy5lbnYuSldUX1NFQ1JFVCA/PyAnJyxcbiAgICAgIGV4cGlyZXNJbjogcGFyc2VJbnQocHJvY2Vzcy5lbnYuSldUX0VYUElSRVNfSU4gfHwgJzcyMDAnLCAxMCksXG4gICAgICBhbGdvcml0aG06IChwcm9jZXNzLmVudi5KV1RfQUxHT1JJVEhNIHx8ICdIUzI1NicpIGFzIEFsZ29yaXRobSxcbiAgICAgIHNhbHRSb3VuZHM6IHBhcnNlSW50KHByb2Nlc3MuZW52LkJDUllQVF9TQUxUX1JPVU5EUyB8fCAnMTInLCAxMCksXG4gICAgfSxcbiAgICByZWZyZXNoVG9rZW46IHtcbiAgICAgIHNlY3JldDogcHJvY2Vzcy5lbnYuUkVGUkVTSF9UT0tFTl9TRUNSRVQgfHwgJycsXG4gICAgICBleHBpcmVzSW46IHBhcnNlSW50KHByb2Nlc3MuZW52LlJFRlJFU0hfVE9LRU5fRVhQSVJFU19JTiB8fCAnMjU5MjAwMCcsIDEwKSxcbiAgICB9LFxuICB9O1xufVxuXG4vKipcbiAqIExvYWQgcmF0ZSBsaW1pdGluZyBjb25maWd1cmF0aW9uIGZyb20gZW52aXJvbm1lbnQgdmFyaWFibGVzLlxuICpcbiAqIEVudmlyb25tZW50IHZhcmlhYmxlczpcbiAqIC0gYExJTUlURVJfTUFYYCDigJQgTWF4IHJlcXVlc3RzIHBlciB3aW5kb3cgKGRlZmF1bHQ6IGAxMDBgKVxuICogLSBgTElNSVRFUl9XSU5ET1dNU2Ag4oCUIFJhdGUgbGltaXQgd2luZG93IGluIG1zIChkZWZhdWx0OiBgOTAwMDAwYCA9IDE1IG1pbnV0ZXMpXG4gKlxuICogQHJldHVybnMgUmF0ZSBsaW1pdCBjb25maWd1cmF0aW9uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBsb2FkUmF0ZUxpbWl0Q29uZmlnKCk6IFJhdGVMaW1pdENvbmZpZyB7XG4gIHJldHVybiB7XG4gICAgbWF4OiBwYXJzZUludChwcm9jZXNzLmVudi5MSU1JVEVSX01BWCB8fCAnMTAwJywgMTApLFxuICAgIHdpbmRvd01zOiBwYXJzZUludChwcm9jZXNzLmVudi5MSU1JVEVSX1dJTkRPV01TIHx8ICc5MDAwMDAnLCAxMCksXG4gICAgbGVnYWN5SGVhZGVyczogZmFsc2UsXG4gICAgc3RhbmRhcmRIZWFkZXJzOiB0cnVlLFxuICB9O1xufVxuXG4vKipcbiAqIFZhbGlkYXRlIHNlcnZlciBjb25maWd1cmF0aW9uIGFuZCBsb2cgd2FybmluZ3MgZm9yIGluc2VjdXJlIHNldHRpbmdzLlxuICpcbiAqIEBwYXJhbSBjb25maWcgLSBTZXJ2ZXIgY29uZmlndXJhdGlvbiB0byB2YWxpZGF0ZVxuICovXG5leHBvcnQgZnVuY3Rpb24gdmFsaWRhdGVTZXJ2ZXJDb25maWcoY29uZmlnOiBTZXJ2ZXJDb25maWcpOiB2b2lkIHtcbiAgY29uc3Qgd2FybmluZ3M6IHN0cmluZ1tdID0gW107XG5cbiAgLy8gQ2hlY2sgQ09SUyBjb25maWd1cmF0aW9uXG4gIGNvbnN0IG9yaWdpbiA9IGNvbmZpZy5jb3JzLm9yaWdpbjtcbiAgY29uc3QgaXNXaWxkY2FyZCA9IG9yaWdpbiA9PT0gJyonIHx8IChBcnJheS5pc0FycmF5KG9yaWdpbikgJiYgb3JpZ2luLmluY2x1ZGVzKCcqJykpO1xuICBpZiAoaXNXaWxkY2FyZCkge1xuICAgIHdhcm5pbmdzLnB1c2goJ0NPUlMgb3JpZ2luIHNldCB0byB3aWxkY2FyZCAoKikgLSBjb25zaWRlciByZXN0cmljdGluZyB0byBzcGVjaWZpYyBkb21haW5zJyk7XG4gIH1cbiAgaWYgKGlzV2lsZGNhcmQgJiYgY29uZmlnLmNvcnMuY3JlZGVudGlhbHMpIHtcbiAgICAvLyBCcm93c2VycyByZWplY3QgdGhpcyBjb21iaW5hdGlvbiwgYnV0IGl0IGluZGljYXRlcyBhIG1pc2NvbmZpZ3VyYXRpb25cbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAnU0VDVVJJVFkgRVJST1I6IENPUlNfT1JJR0lOPSogd2l0aCBDT1JTX0NSRURFTlRJQUxTPXRydWUgaXMgYW4gaW52YWxpZCBhbmQgaW5zZWN1cmUgY29uZmlndXJhdGlvbi4gJyArXG4gICAgICAnU2V0IENPUlNfT1JJR0lOIHRvIHNwZWNpZmljIGRvbWFpbnMgb3IgZGlzYWJsZSBDT1JTX0NSRURFTlRJQUxTLicsXG4gICAgKTtcbiAgfVxuXG4gIC8vIENoZWNrIHBsYXRmb3JtIFVSTCB1c2VzIEhUVFBTXG4gIGlmIChjb25maWcucGxhdGZvcm1Vcmwuc3RhcnRzV2l0aCgnaHR0cDovLycpICYmXG4gICAgIWNvbmZpZy5wbGF0Zm9ybVVybC5pbmNsdWRlcygnbG9jYWxob3N0JykpIHtcbiAgICB3YXJuaW5ncy5wdXNoKCdQbGF0Zm9ybSBVUkwgdXNlcyBIVFRQIGluc3RlYWQgb2YgSFRUUFMgLSBpbnNlY3VyZSBmb3IgcHJvZHVjdGlvbicpO1xuICB9XG5cbiAgLy8gRGlzcGxheSB3YXJuaW5nc1xuICBpZiAod2FybmluZ3MubGVuZ3RoID4gMCkge1xuICAgIGxvZy53YXJuKCdTZXJ2ZXIgY29uZmlndXJhdGlvbiB3YXJuaW5nczonKTtcbiAgICB3YXJuaW5ncy5mb3JFYWNoKHdhcm5pbmcgPT4gbG9nLndhcm4oYCAgLSAke3dhcm5pbmd9YCkpO1xuICB9XG59XG5cbmNvbnN0IElOU0VDVVJFX1BBVFRFUk5TID0gWydzZWNyZXQnLCAncGFzc3dvcmQnLCAnY2hhbmdlbWUnLCAnZGVmYXVsdCcsICcxMjM0NTYnLCAnYWRtaW4nXTtcblxuZnVuY3Rpb24gaXNJbnNlY3VyZVNlY3JldChzZWNyZXQ6IHN0cmluZyk6IGJvb2xlYW4ge1xuICBjb25zdCBsb3dlciA9IHNlY3JldC50b0xvd2VyQ2FzZSgpO1xuICByZXR1cm4gSU5TRUNVUkVfUEFUVEVSTlMuc29tZShzID0+IGxvd2VyID09PSBzIHx8IChsb3dlci5sZW5ndGggPCA2NCAmJiBsb3dlci5pbmNsdWRlcyhzKSkpO1xufVxuXG4vKipcbiAqIFZhbGlkYXRlIGF1dGhlbnRpY2F0aW9uIGNvbmZpZ3VyYXRpb24gKEpXVCBzZWNyZXRzLCBhbGdvcml0aG1zLCBleHBpcmF0aW9uKS5cbiAqIENhbGwgdGhpcyBhdCBzZXJ2ZXIgc3RhcnR1cCwgbm90IGR1cmluZyBDREsgc3ludGhlc2lzLlxuICpcbiAqIEBwYXJhbSBjb25maWcgLSBBdXRoIGNvbmZpZ3VyYXRpb24gdG8gdmFsaWRhdGVcbiAqIEB0aHJvd3Mge0Vycm9yfSBJZiBzZWNyZXRzIGFyZSBpbnNlY3VyZSwgdG9vIHNob3J0ICg8MzIgY2hhcnMpLCBvciB1c2UgZGlzYWxsb3dlZCBhbGdvcml0aG1zXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiB2YWxpZGF0ZUF1dGhDb25maWcoY29uZmlnOiBBdXRoQ29uZmlnKTogdm9pZCB7XG4gIGNvbnN0IGVycm9yczogc3RyaW5nW10gPSBbXTtcbiAgY29uc3Qgd2FybmluZ3M6IHN0cmluZ1tdID0gW107XG5cbiAgLy8gVmFsaWRhdGUgYm90aCBzZWNyZXRzIHdpdGggdGhlIHNhbWUgY2hlY2tzXG4gIGZvciAoY29uc3QgW2xhYmVsLCBzZWNyZXRdIG9mIFtbJ0pXVCcsIGNvbmZpZy5qd3Quc2VjcmV0XSwgWydSZWZyZXNoIHRva2VuJywgY29uZmlnLnJlZnJlc2hUb2tlbi5zZWNyZXRdXSBhcyBjb25zdCkge1xuICAgIGlmIChzZWNyZXQubGVuZ3RoIDwgMzIpIHtcbiAgICAgIGVycm9ycy5wdXNoKGAke2xhYmVsfSBzZWNyZXQgc2hvdWxkIGJlIGF0IGxlYXN0IDMyIGNoYXJhY3RlcnMgbG9uZ2ApO1xuICAgIH1cbiAgICBpZiAoaXNJbnNlY3VyZVNlY3JldChzZWNyZXQpKSB7XG4gICAgICBlcnJvcnMucHVzaChgJHtsYWJlbH0gc2VjcmV0IGFwcGVhcnMgdG8gYmUgaW5zZWN1cmUgb3IgZGVmYXVsdCB2YWx1ZWApO1xuICAgIH1cbiAgfVxuXG4gIC8vIENoZWNrIEpXVCBleHBpcmF0aW9uIHRpbWVzXG4gIGlmIChjb25maWcuand0LmV4cGlyZXNJbiA+IDg2NDAwKSB7XG4gICAgZXJyb3JzLnB1c2goJ0pXVCBleHBpcmF0aW9uIG11c3Qgbm90IGV4Y2VlZCAyNCBob3VycyAoODY0MDAgc2Vjb25kcyknKTtcbiAgfSBlbHNlIGlmIChjb25maWcuand0LmV4cGlyZXNJbiA+IDcyMDApIHtcbiAgICB3YXJuaW5ncy5wdXNoKCdKV1QgZXhwaXJhdGlvbiB0aW1lIGlzIGdyZWF0ZXIgdGhhbiAyIGhvdXJzIC0gc2hvcnRlciBleHBpcmF0aW9uIHJlY29tbWVuZGVkJyk7XG4gIH1cblxuICAvLyBDaGVjayBhbGdvcml0aG1cbiAgaWYgKCFDb3JlQ29uc3RhbnRzLkFMTE9XRURfSldUX0FMR09SSVRITVMuaW5jbHVkZXMoY29uZmlnLmp3dC5hbGdvcml0aG0pKSB7XG4gICAgZXJyb3JzLnB1c2goYEpXVCBhbGdvcml0aG0gJHtjb25maWcuand0LmFsZ29yaXRobX0gaXMgbm90IGluIHRoZSBhbGxvd2VkIGxpc3RgKTtcbiAgfVxuXG4gIC8vIERpc3BsYXkgd2FybmluZ3NcbiAgaWYgKHdhcm5pbmdzLmxlbmd0aCA+IDApIHtcbiAgICBsb2cud2FybignQXV0aCBjb25maWd1cmF0aW9uIHdhcm5pbmdzOicpO1xuICAgIHdhcm5pbmdzLmZvckVhY2god2FybmluZyA9PiBsb2cud2FybihgICAtICR7d2FybmluZ31gKSk7XG4gIH1cblxuICAvLyBUaHJvdyBlcnJvcnNcbiAgaWYgKGVycm9ycy5sZW5ndGggPiAwKSB7XG4gICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgYEF1dGggY29uZmlndXJhdGlvbiB2YWxpZGF0aW9uIGZhaWxlZDpcXG4ke2Vycm9ycy5tYXAoZSA9PiBgICAtICR7ZX1gKS5qb2luKCdcXG4nKX1gLFxuICAgICk7XG4gIH1cbn1cbiJdfQ==
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { InternalHttpClient } from '@pipeline-builder/api-core';
|
|
2
|
+
/** Sibling services reachable over the internal network, keyed by the typed
|
|
3
|
+
* `server.services.<name>Host`/`<name>Port` config entries. */
|
|
4
|
+
export type InternalService = 'plugin' | 'pipeline' | 'message' | 'platform' | 'compliance' | 'billing';
|
|
5
|
+
/**
|
|
6
|
+
* Construct an `InternalHttpClient` for a sibling service from the typed
|
|
7
|
+
* `server.services` config — replacing the per-client boilerplate of reading
|
|
8
|
+
* `Config.getAny('server')` with a loose cast and hand-building the host/port.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* export const messageClient = createServiceClient('message');
|
|
12
|
+
*/
|
|
13
|
+
export declare function createServiceClient(service: InternalService): InternalHttpClient;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Copyright 2026 Pipeline Builder Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
import { InternalHttpClient } from '@pipeline-builder/api-core';
|
|
4
|
+
import { Config } from './app-config.js';
|
|
5
|
+
/**
|
|
6
|
+
* Construct an `InternalHttpClient` for a sibling service from the typed
|
|
7
|
+
* `server.services` config — replacing the per-client boilerplate of reading
|
|
8
|
+
* `Config.getAny('server')` with a loose cast and hand-building the host/port.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* export const messageClient = createServiceClient('message');
|
|
12
|
+
*/
|
|
13
|
+
export function createServiceClient(service) {
|
|
14
|
+
const { services } = Config.get('server');
|
|
15
|
+
return new InternalHttpClient({
|
|
16
|
+
host: services[`${service}Host`],
|
|
17
|
+
port: services[`${service}Port`],
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic2VydmljZS1jbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvY29uZmlnL3NlcnZpY2UtY2xpZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLCtDQUErQztBQUMvQyxzQ0FBc0M7QUFFdEMsT0FBTyxFQUFFLGtCQUFrQixFQUFFLE1BQU0sNEJBQTRCLENBQUM7QUFDaEUsT0FBTyxFQUFFLE1BQU0sRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBTXpDOzs7Ozs7O0dBT0c7QUFDSCxNQUFNLFVBQVUsbUJBQW1CLENBQUMsT0FBd0I7SUFDMUQsTUFBTSxFQUFFLFFBQVEsRUFBRSxHQUFHLE1BQU0sQ0FBQyxHQUFHLENBQUMsUUFBUSxDQUFDLENBQUM7SUFDMUMsT0FBTyxJQUFJLGtCQUFrQixDQUFDO1FBQzVCLElBQUksRUFBRSxRQUFRLENBQUMsR0FBRyxPQUFPLE1BQU0sQ0FBQztRQUNoQyxJQUFJLEVBQUUsUUFBUSxDQUFDLEdBQUcsT0FBTyxNQUFNLENBQUM7S0FDakMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbIi8vIENvcHlyaWdodCAyMDI2IFBpcGVsaW5lIEJ1aWxkZXIgQ29udHJpYnV0b3JzXG4vLyBTUERYLUxpY2Vuc2UtSWRlbnRpZmllcjogQXBhY2hlLTIuMFxuXG5pbXBvcnQgeyBJbnRlcm5hbEh0dHBDbGllbnQgfSBmcm9tICdAcGlwZWxpbmUtYnVpbGRlci9hcGktY29yZSc7XG5pbXBvcnQgeyBDb25maWcgfSBmcm9tICcuL2FwcC1jb25maWcuanMnO1xuXG4vKiogU2libGluZyBzZXJ2aWNlcyByZWFjaGFibGUgb3ZlciB0aGUgaW50ZXJuYWwgbmV0d29yaywga2V5ZWQgYnkgdGhlIHR5cGVkXG4gKiAgYHNlcnZlci5zZXJ2aWNlcy48bmFtZT5Ib3N0YC9gPG5hbWU+UG9ydGAgY29uZmlnIGVudHJpZXMuICovXG5leHBvcnQgdHlwZSBJbnRlcm5hbFNlcnZpY2UgPSAncGx1Z2luJyB8ICdwaXBlbGluZScgfCAnbWVzc2FnZScgfCAncGxhdGZvcm0nIHwgJ2NvbXBsaWFuY2UnIHwgJ2JpbGxpbmcnO1xuXG4vKipcbiAqIENvbnN0cnVjdCBhbiBgSW50ZXJuYWxIdHRwQ2xpZW50YCBmb3IgYSBzaWJsaW5nIHNlcnZpY2UgZnJvbSB0aGUgdHlwZWRcbiAqIGBzZXJ2ZXIuc2VydmljZXNgIGNvbmZpZyDigJQgcmVwbGFjaW5nIHRoZSBwZXItY2xpZW50IGJvaWxlcnBsYXRlIG9mIHJlYWRpbmdcbiAqIGBDb25maWcuZ2V0QW55KCdzZXJ2ZXInKWAgd2l0aCBhIGxvb3NlIGNhc3QgYW5kIGhhbmQtYnVpbGRpbmcgdGhlIGhvc3QvcG9ydC5cbiAqXG4gKiBAZXhhbXBsZVxuICogICBleHBvcnQgY29uc3QgbWVzc2FnZUNsaWVudCA9IGNyZWF0ZVNlcnZpY2VDbGllbnQoJ21lc3NhZ2UnKTtcbiAqL1xuZXhwb3J0IGZ1bmN0aW9uIGNyZWF0ZVNlcnZpY2VDbGllbnQoc2VydmljZTogSW50ZXJuYWxTZXJ2aWNlKTogSW50ZXJuYWxIdHRwQ2xpZW50IHtcbiAgY29uc3QgeyBzZXJ2aWNlcyB9ID0gQ29uZmlnLmdldCgnc2VydmVyJyk7XG4gIHJldHVybiBuZXcgSW50ZXJuYWxIdHRwQ2xpZW50KHtcbiAgICBob3N0OiBzZXJ2aWNlc1tgJHtzZXJ2aWNlfUhvc3RgXSxcbiAgICBwb3J0OiBzZXJ2aWNlc1tgJHtzZXJ2aWNlfVBvcnRgXSxcbiAgfSk7XG59XG4iXX0=
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { CodeBuildStep, FileSet, ShellStep } from 'aws-cdk-lib/pipelines';
|
|
2
|
+
export interface ArtifactKey {
|
|
3
|
+
readonly stageName: string;
|
|
4
|
+
readonly stageAlias: string;
|
|
5
|
+
readonly pluginName: string;
|
|
6
|
+
readonly pluginAlias: string;
|
|
7
|
+
readonly outputDirectory: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Manages build step artifacts with hierarchical key-based lookup.
|
|
11
|
+
* Keys follow the pattern: stageName:stageAlias:pluginName:pluginAlias:outputDirectory
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* // Synth step: "no-stage:no-stage-alias:cdk-synth:cdk-synth-alias:cdk.out"
|
|
16
|
+
* // Build step: "build:build-alias:nodejs-build:nodejs-build-alias:dist"
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export declare class ArtifactManager {
|
|
20
|
+
private readonly artifacts;
|
|
21
|
+
/**
|
|
22
|
+
* Generate a key string from artifact parameters
|
|
23
|
+
*/
|
|
24
|
+
private generateKey;
|
|
25
|
+
/**
|
|
26
|
+
* Register a build step artifact
|
|
27
|
+
* @param key - The hierarchical key identifying this artifact (includes output directory)
|
|
28
|
+
* @param step - The CodeBuildStep or ShellStep to store
|
|
29
|
+
*/
|
|
30
|
+
add(key: ArtifactKey, step: CodeBuildStep | ShellStep): void;
|
|
31
|
+
/**
|
|
32
|
+
* Get a build step by its artifact key
|
|
33
|
+
* @param key - The hierarchical key identifying the artifact
|
|
34
|
+
* @returns The stored step, or undefined if not found
|
|
35
|
+
*/
|
|
36
|
+
get(key: ArtifactKey): CodeBuildStep | ShellStep | undefined;
|
|
37
|
+
/**
|
|
38
|
+
* Get the primary output FileSet from a registered step.
|
|
39
|
+
* @param key - The artifact key identifying the step
|
|
40
|
+
* @returns The primary output FileSet
|
|
41
|
+
* @throws Error if the step is not found or has no primary output
|
|
42
|
+
*/
|
|
43
|
+
getOutput(key: ArtifactKey): FileSet;
|
|
44
|
+
/**
|
|
45
|
+
* Register an additional output directory on a stored step and return its FileSet.
|
|
46
|
+
* Calls CDK's addOutputDirectory() to create a named output beyond the primary.
|
|
47
|
+
* @param key - The artifact key identifying the step
|
|
48
|
+
* @param directory - The additional output directory path to register
|
|
49
|
+
* @returns The FileSet for the additional output directory
|
|
50
|
+
* @throws Error if the step is not found
|
|
51
|
+
*/
|
|
52
|
+
addOutput(key: ArtifactKey, directory: string): FileSet;
|
|
53
|
+
/**
|
|
54
|
+
* Get a step by key or throw with a helpful error listing available artifacts.
|
|
55
|
+
*/
|
|
56
|
+
private require;
|
|
57
|
+
/**
|
|
58
|
+
* List all artifact keys
|
|
59
|
+
* @returns Array of all stored artifact key strings
|
|
60
|
+
*/
|
|
61
|
+
list(): string[];
|
|
62
|
+
}
|