@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.
Files changed (84) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +69 -0
  3. package/lib/cdk.d.ts +38 -0
  4. package/lib/cdk.js +44 -0
  5. package/lib/config/app-config.d.ts +109 -0
  6. package/lib/config/app-config.js +197 -0
  7. package/lib/config/aws-config-cdk.d.ts +49 -0
  8. package/lib/config/aws-config-cdk.js +65 -0
  9. package/lib/config/billing-config.d.ts +24 -0
  10. package/lib/config/billing-config.js +497 -0
  11. package/lib/config/config-types.d.ts +346 -0
  12. package/lib/config/config-types.js +4 -0
  13. package/lib/config/entitlements.d.ts +23 -0
  14. package/lib/config/entitlements.js +46 -0
  15. package/lib/config/handler-constants.d.ts +10 -0
  16. package/lib/config/handler-constants.js +39 -0
  17. package/lib/config/infrastructure-config.d.ts +66 -0
  18. package/lib/config/infrastructure-config.js +197 -0
  19. package/lib/config/server-config.d.ts +53 -0
  20. package/lib/config/server-config.js +175 -0
  21. package/lib/config/service-client.d.ts +13 -0
  22. package/lib/config/service-client.js +20 -0
  23. package/lib/core/artifact-manager.d.ts +62 -0
  24. package/lib/core/artifact-manager.js +83 -0
  25. package/lib/core/id-generator.d.ts +50 -0
  26. package/lib/core/id-generator.js +50 -0
  27. package/lib/core/metadata-builder.d.ts +48 -0
  28. package/lib/core/metadata-builder.js +278 -0
  29. package/lib/core/metadata-helpers.d.ts +31 -0
  30. package/lib/core/metadata-helpers.js +55 -0
  31. package/lib/core/network-types.d.ts +200 -0
  32. package/lib/core/network-types.js +4 -0
  33. package/lib/core/network.d.ts +33 -0
  34. package/lib/core/network.js +112 -0
  35. package/lib/core/pipeline-helpers.d.ts +61 -0
  36. package/lib/core/pipeline-helpers.js +563 -0
  37. package/lib/core/pipeline-types.d.ts +118 -0
  38. package/lib/core/pipeline-types.js +131 -0
  39. package/lib/core/role-types.d.ts +254 -0
  40. package/lib/core/role-types.js +4 -0
  41. package/lib/core/role.d.ts +14 -0
  42. package/lib/core/role.js +117 -0
  43. package/lib/core/security-group-types.d.ts +84 -0
  44. package/lib/core/security-group-types.js +4 -0
  45. package/lib/core/security-group.d.ts +14 -0
  46. package/lib/core/security-group.js +33 -0
  47. package/lib/handlers/plugin-lookup-handler.d.ts +32 -0
  48. package/lib/handlers/plugin-lookup-handler.js +292 -0
  49. package/lib/handlers/pnpm-lock.yaml +12 -0
  50. package/lib/index.d.ts +45 -0
  51. package/lib/index.js +53 -0
  52. package/lib/pipeline/pipeline-builder.d.ts +123 -0
  53. package/lib/pipeline/pipeline-builder.js +476 -0
  54. package/lib/pipeline/pipeline-configuration.d.ts +81 -0
  55. package/lib/pipeline/pipeline-configuration.js +214 -0
  56. package/lib/pipeline/plugin-lookup.d.ts +131 -0
  57. package/lib/pipeline/plugin-lookup.js +316 -0
  58. package/lib/pipeline/plugin-spec.d.ts +138 -0
  59. package/lib/pipeline/plugin-spec.js +4 -0
  60. package/lib/pipeline/source-builder.d.ts +62 -0
  61. package/lib/pipeline/source-builder.js +164 -0
  62. package/lib/pipeline/source-types.d.ts +221 -0
  63. package/lib/pipeline/source-types.js +4 -0
  64. package/lib/pipeline/stage-builder.d.ts +78 -0
  65. package/lib/pipeline/stage-builder.js +129 -0
  66. package/lib/pipeline/step-types.d.ts +222 -0
  67. package/lib/pipeline/step-types.js +4 -0
  68. package/lib/template/evaluator.d.ts +42 -0
  69. package/lib/template/evaluator.js +164 -0
  70. package/lib/template/index.d.ts +34 -0
  71. package/lib/template/index.js +118 -0
  72. package/lib/template/metrics.d.ts +10 -0
  73. package/lib/template/metrics.js +43 -0
  74. package/lib/template/plugin-resolver.d.ts +8 -0
  75. package/lib/template/plugin-resolver.js +48 -0
  76. package/lib/template/tokenizer.d.ts +49 -0
  77. package/lib/template/tokenizer.js +233 -0
  78. package/lib/template/topo-sort.d.ts +17 -0
  79. package/lib/template/topo-sort.js +88 -0
  80. package/lib/template/validate.d.ts +45 -0
  81. package/lib/template/validate.js +96 -0
  82. package/lib/template/walker.d.ts +34 -0
  83. package/lib/template/walker.js +83 -0
  84. 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
+ }