codepipeline-event-notifier 0.2.8 → 0.3.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/.editorconfig +12 -0
- package/.jsii +148 -14
- package/API.md +110 -2
- package/README.md +51 -6
- package/assets/funcs/notifier.lambda/index.js +152 -0
- package/assets/funcs/notifier.lambda/index.js.map +3 -3
- package/lib/constructs/codepipeline-event-notifier.d.ts +35 -1
- package/lib/constructs/codepipeline-event-notifier.js +80 -5
- package/lib/funcs/notifier.lambda.d.ts +1 -1
- package/lib/funcs/notifier.lambda.js +28 -2
- package/lib/funcs/target-pipeline-predicates.d.ts +99 -0
- package/lib/funcs/target-pipeline-predicates.js +212 -0
- package/lib/index.d.ts +1 -1
- package/lib/index.js +1 -1
- package/lib/stacks/codepipeline-event-notify-stack.js +11 -2
- package/package.json +5 -4
|
@@ -212,6 +212,135 @@ var resolvePipelineExecutionIdentity = (pipelineName, executionId) => {
|
|
|
212
212
|
return { pipelineName, executionId };
|
|
213
213
|
};
|
|
214
214
|
|
|
215
|
+
// src/funcs/target-pipeline-predicates.ts
|
|
216
|
+
var isNonEmptyString = (value) => typeof value === "string" && value.trim().length > 0;
|
|
217
|
+
var isPipelineTagFilterList = (value) => {
|
|
218
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
219
|
+
return false;
|
|
220
|
+
}
|
|
221
|
+
for (const item of value) {
|
|
222
|
+
if (!isPipelineTagFilter(item)) {
|
|
223
|
+
return false;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return true;
|
|
227
|
+
};
|
|
228
|
+
var isPipelineTagFilter = (value) => {
|
|
229
|
+
if (typeof value !== "object" || value === null) {
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
if (!("key" in value) || !("values" in value)) {
|
|
233
|
+
return false;
|
|
234
|
+
}
|
|
235
|
+
if (!isNonEmptyString(value.key)) {
|
|
236
|
+
return false;
|
|
237
|
+
}
|
|
238
|
+
if (!Array.isArray(value.values) || value.values.length === 0) {
|
|
239
|
+
return false;
|
|
240
|
+
}
|
|
241
|
+
for (const tagValue of value.values) {
|
|
242
|
+
if (!isNonEmptyString(tagValue)) {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
return true;
|
|
247
|
+
};
|
|
248
|
+
var parseTargetPipelineTags = (raw) => {
|
|
249
|
+
let parsed;
|
|
250
|
+
try {
|
|
251
|
+
parsed = JSON.parse(raw);
|
|
252
|
+
} catch {
|
|
253
|
+
throw new Error("TARGET_PIPELINE_TAGS must be valid JSON");
|
|
254
|
+
}
|
|
255
|
+
if (!isPipelineTagFilterList(parsed)) {
|
|
256
|
+
throw new Error("TARGET_PIPELINE_TAGS must be a non-empty array of { key, values }");
|
|
257
|
+
}
|
|
258
|
+
return normalizeTagFilters(parsed);
|
|
259
|
+
};
|
|
260
|
+
var normalizeTagFilters = (filters) => {
|
|
261
|
+
const seenKeys = /* @__PURE__ */ new Set();
|
|
262
|
+
const normalized = [];
|
|
263
|
+
for (const filter of filters) {
|
|
264
|
+
const key = filter.key.trim();
|
|
265
|
+
if (seenKeys.has(key)) {
|
|
266
|
+
throw new Error(`Tag filter list contains duplicate key: ${key}`);
|
|
267
|
+
}
|
|
268
|
+
seenKeys.add(key);
|
|
269
|
+
normalized.push({
|
|
270
|
+
key,
|
|
271
|
+
values: filter.values.map((value) => value.trim())
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
return normalized;
|
|
275
|
+
};
|
|
276
|
+
var toPipelineTagMap = (tags) => {
|
|
277
|
+
const mapped = {};
|
|
278
|
+
if (!tags) {
|
|
279
|
+
return mapped;
|
|
280
|
+
}
|
|
281
|
+
for (const tag of tags) {
|
|
282
|
+
if (!tag.key || tag.value === void 0) {
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
mapped[tag.key] = tag.value;
|
|
286
|
+
}
|
|
287
|
+
return mapped;
|
|
288
|
+
};
|
|
289
|
+
var matchesTargetPipelineTags = (pipelineTags, filters) => {
|
|
290
|
+
for (const filter of filters) {
|
|
291
|
+
const actual = pipelineTags[filter.key];
|
|
292
|
+
if (actual === void 0) {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
if (!filter.values.includes(actual)) {
|
|
296
|
+
return false;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
return true;
|
|
300
|
+
};
|
|
301
|
+
var resolvePipelineArn = (resources, region, account, pipelineName) => {
|
|
302
|
+
const fromResources = resources?.[0];
|
|
303
|
+
if (fromResources) {
|
|
304
|
+
return fromResources;
|
|
305
|
+
}
|
|
306
|
+
if (!region || !account) {
|
|
307
|
+
return void 0;
|
|
308
|
+
}
|
|
309
|
+
return `arn:aws:codepipeline:${region}:${account}:${pipelineName}`;
|
|
310
|
+
};
|
|
311
|
+
var isNonEmptyStringList = (value) => {
|
|
312
|
+
if (!Array.isArray(value) || value.length === 0) {
|
|
313
|
+
return false;
|
|
314
|
+
}
|
|
315
|
+
for (const item of value) {
|
|
316
|
+
if (!isNonEmptyString(item)) {
|
|
317
|
+
return false;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return true;
|
|
321
|
+
};
|
|
322
|
+
var parseAllowedPipelineArns = (raw) => {
|
|
323
|
+
if (raw === void 0 || raw.trim() === "") {
|
|
324
|
+
return void 0;
|
|
325
|
+
}
|
|
326
|
+
let parsed;
|
|
327
|
+
try {
|
|
328
|
+
parsed = JSON.parse(raw);
|
|
329
|
+
} catch {
|
|
330
|
+
throw new Error("TARGET_PIPELINE_ARNS must be valid JSON");
|
|
331
|
+
}
|
|
332
|
+
if (!isNonEmptyStringList(parsed)) {
|
|
333
|
+
throw new Error("TARGET_PIPELINE_ARNS must be a non-empty array of strings");
|
|
334
|
+
}
|
|
335
|
+
return parsed.map((arn) => arn.trim());
|
|
336
|
+
};
|
|
337
|
+
var isAllowedPipelineArn = (pipelineArn, allowedArns) => {
|
|
338
|
+
if (!allowedArns) {
|
|
339
|
+
return true;
|
|
340
|
+
}
|
|
341
|
+
return allowedArns.includes(pipelineArn);
|
|
342
|
+
};
|
|
343
|
+
|
|
215
344
|
// src/funcs/notifier.lambda.ts
|
|
216
345
|
var sns = new import_client_sns.SNSClient({});
|
|
217
346
|
var codepipeline = new import_client_codepipeline.CodePipelineClient({});
|
|
@@ -275,6 +404,26 @@ var waitPipelineExecution = async (params) => {
|
|
|
275
404
|
note: "Waiting timed out before terminal state."
|
|
276
405
|
});
|
|
277
406
|
};
|
|
407
|
+
var isTargetPipelineEvent = async (event, pipelineName) => {
|
|
408
|
+
const tagFilters = parseTargetPipelineTags(mustEnv("TARGET_PIPELINE_TAGS"));
|
|
409
|
+
const pipelineArn = resolvePipelineArn(
|
|
410
|
+
event.resources,
|
|
411
|
+
event.region,
|
|
412
|
+
event.account,
|
|
413
|
+
pipelineName
|
|
414
|
+
);
|
|
415
|
+
if (!pipelineArn) {
|
|
416
|
+
throw new Error("Unable to resolve pipeline ARN for tag lookup");
|
|
417
|
+
}
|
|
418
|
+
const allowedArns = parseAllowedPipelineArns(process.env.TARGET_PIPELINE_ARNS);
|
|
419
|
+
if (!isAllowedPipelineArn(pipelineArn, allowedArns)) {
|
|
420
|
+
return false;
|
|
421
|
+
}
|
|
422
|
+
const listed = await codepipeline.send(new import_client_codepipeline.ListTagsForResourceCommand({
|
|
423
|
+
resourceArn: pipelineArn
|
|
424
|
+
}));
|
|
425
|
+
return matchesTargetPipelineTags(toPipelineTagMap(listed.tags), tagFilters);
|
|
426
|
+
};
|
|
278
427
|
var handler = async (event) => {
|
|
279
428
|
const topicArn = mustEnv("SNS_TOPIC_ARN");
|
|
280
429
|
const identity = resolvePipelineExecutionIdentity(
|
|
@@ -291,6 +440,9 @@ var handler = async (event) => {
|
|
|
291
440
|
});
|
|
292
441
|
return;
|
|
293
442
|
}
|
|
443
|
+
if (!await isTargetPipelineEvent(event, identity.pipelineName)) {
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
294
446
|
const waitIntervalSeconds = import_strict_env_resolver.StrictEnvResolver.resolve("WAIT_INTERVAL_SECONDS", import_strict_env_resolver.StrictEnvType.Number, { default: 10 });
|
|
295
447
|
const maxWaitMinutes = import_strict_env_resolver.StrictEnvResolver.resolve("MAX_WAIT_MINUTES", import_strict_env_resolver.StrictEnvType.Number, { default: 14 });
|
|
296
448
|
await waitPipelineExecution({
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
|
-
"sources": ["../../../node_modules/strict-env-resolver/src/index.ts", "../../../src/funcs/notifier.lambda.ts", "../../../src/funcs/notifier-predicates.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * Spec for a string environment variable.\n * Returned as-is by default (`trim` defaults to `false`). Set `trim: true` in options to trim\n * whitespace and treat whitespace-only values as missing.\n * The default value is passed via the third argument of `resolve` or a `[spec, options]` tuple in `resolveAll`, not in the spec.\n */\nexport type StrictEnvTypeString = { type: 'string'; default?: string };\n\n/**\n * Spec for a numeric environment variable.\n * Values are parsed with `Number()`; `NaN`, `Infinity`, and `-Infinity` are rejected.\n * The default value is passed via the third argument of `resolve` or a `[spec, options]` tuple in `resolveAll`, not in the spec.\n */\nexport type StrictEnvTypeNumber = { type: 'number'; default?: number };\n\n/**\n * Pattern for boolean env values that parse as `true`.\n * Matches `1`, `true`, `yes`, and `on` (case-insensitive) on the full trimmed string.\n */\nconst TRUE_BOOLEAN_PATTERN = /^(1|true|yes|on)$/i;\n\n/**\n * Parses a string as a finite number for environment variables.\n *\n * @param raw - Trimmed environment variable value.\n * @returns Parsed number, or `undefined` when the value is not a finite number.\n */\nconst parseNumber = (raw: string): number | undefined => {\n const n = Number(raw);\n if (!Number.isFinite(n)) {\n return undefined;\n }\n return n;\n};\n\n/**\n * Parses a trimmed string as a boolean environment variable value.\n *\n * @param normalizedRaw - Trimmed raw environment variable value.\n * @returns `true` for `1`/`true`/`yes`/`on` (case-insensitive); otherwise `false`.\n */\nconst parseBooleanEnvValue = (normalizedRaw: string): boolean => TRUE_BOOLEAN_PATTERN.test(normalizedRaw);\n\n/**\n * Spec for a boolean environment variable.\n * Parses `1`, `true`, `yes`, `on` (case-insensitive, after trim) as `true`; any other non-empty value as `false`.\n * Trims leading/trailing whitespace by default (see `StrictEnvOptions.trim`).\n * The default value is passed via the third argument of `resolve` or a `[spec, options]` tuple in `resolveAll`, not in the spec.\n */\nexport type StrictEnvTypeBoolean = { type: 'boolean'; default?: boolean };\n\n/**\n * Spec for an enum environment variable with a fixed set of choices.\n * The value must be one of `choices` (compared after trim); otherwise validation fails.\n * Trims leading/trailing whitespace by default (see `StrictEnvOptions.trim`).\n * The default value is passed via the third argument of `resolve` or a `[spec, options]` tuple in `resolveAll`, not in the spec.\n *\n * @template T - Literal string union of allowed values.\n */\nexport type StrictEnvTypeEnum<T extends string = string> = { type: 'enum'; choices: readonly T[]; default?: T };\n\n/** Union of all environment variable spec types. */\nexport type StrictEnvSpec =\n | StrictEnvTypeString\n | StrictEnvTypeNumber\n | StrictEnvTypeBoolean\n | StrictEnvTypeEnum;\n\n/**\n * Discriminated union of error kinds emitted during environment variable parsing.\n *\n * - `missing` — Variable unset, empty string, or whitespace-only (when trim is enabled) without a default.\n * - `invalid_number` — Value is not a finite number (see `StrictEnvTypeNumber`).\n * - `invalid_enum` — Value is not one of the allowed enum choices.\n */\nexport type StrictEnvErrorKind = 'missing' | 'invalid_number' | 'invalid_enum';\n\n/**\n * A structured validation error entry for a single environment variable.\n *\n * @template K - Environment variable key type.\n */\nexport type StrictEnvValidationEntry<K extends string = string> = {\n /** Environment variable name. */\n key: K;\n /** Human-readable error message. */\n message: string;\n /** Raw value from `process.env`, if present. */\n raw?: string;\n /** Error category for programmatic handling. */\n kind: StrictEnvErrorKind;\n};\n\n/**\n * Base error class for this package.\n *\n * @example\n * ```ts\n * try {\n * StrictEnvResolver.resolve('PORT', StrictEnvType.Number);\n * } catch (e) {\n * if (e instanceof StrictEnvError) {\n * // Handle all strict-env-resolver errors\n * }\n * }\n * ```\n */\nexport abstract class StrictEnvError extends Error {\n /**\n * @param message - Error message.\n */\n protected constructor(message: string) {\n super(message);\n this.name = 'StrictEnvError';\n }\n}\n\n/**\n * Validation error that carries one or more environment variable issues.\n *\n * Thrown by `StrictEnvResolver.resolve()` (single-entry `errors`) and\n * `StrictEnvResolver.resolveAll()` (multi-entry `errors`).\n * Individual entries are produced by the internal `parseEnvValue` helper.\n *\n * @template K - Union of environment variable keys included in `errors`.\n */\nexport class StrictEnvValidationError<K extends string = string> extends StrictEnvError {\n /**\n * Formats validation errors into a human-readable error message.\n *\n * @template K - Environment variable key type.\n * @param errors - Validation error entries to format.\n * @returns Multi-line summary listing each key and message.\n */\n public static format<K extends string>(errors: readonly StrictEnvValidationEntry<K>[]): string {\n const lines = errors.map((e) => `- ${e.key}: ${e.message}${e.raw == null ? '' : ` (raw=\"${e.raw}\")`}`);\n return `Invalid environment variables (${errors.length}):\\n${lines.join('\\n')}`;\n }\n\n /**\n * Structured list of validation errors.\n */\n public readonly errors: readonly StrictEnvValidationEntry<K>[];\n /**\n * Convenience list of keys included in `errors`.\n */\n public readonly keys: readonly K[];\n\n /**\n * Creates a new validation error from one or more `StrictEnvValidationEntry` entries.\n *\n * @param errors - One or more structured validation errors.\n */\n public constructor(errors: readonly StrictEnvValidationEntry<K>[]) {\n const msg = StrictEnvValidationError.format(errors);\n super(msg);\n this.name = 'StrictEnvValidationError';\n this.errors = errors;\n this.keys = errors.map((e) => e.key);\n }\n}\n\n/**\n * Predefined spec constants for use with `resolve` or as schema values in `resolveAll`.\n * Provide defaults via the third argument of `resolve` or a `[spec, { default }]` tuple in `resolveAll`.\n */\nexport const StrictEnvType = {\n /** Spec for a string value (returned as-is unless `trim: true` is set in options). */\n String: { type: 'string' } as const satisfies StrictEnvTypeString,\n /**\n * Spec for a finite numeric value (`Number()` parsing; rejects `NaN` and `Infinity`).\n */\n Number: { type: 'number' } as const satisfies StrictEnvTypeNumber,\n /** Spec for a boolean value (`1`/`true`/`yes`/`on` → `true`; other non-empty values → `false`). */\n Boolean: { type: 'boolean' } as const satisfies StrictEnvTypeBoolean,\n /**\n * Returns a spec that restricts the value to one of the given choices.\n *\n * @param choices - Allowed string literals.\n * @returns Enum spec for use with `resolve` or `resolveAll`.\n */\n Enum: <T extends string>(choices: readonly T[]) => ({ type: 'enum', choices }) as StrictEnvTypeEnum<T>,\n} as const;\n\n/**\n * Infers the return type from the given spec.\n * @template S - A `StrictEnvSpec` variant.\n */\nexport type StrictEnvSpecToType<S> =\n S extends StrictEnvTypeString ? string\n : S extends StrictEnvTypeNumber ? number\n : S extends StrictEnvTypeBoolean ? boolean\n : S extends StrictEnvTypeEnum<infer T> ? T\n : never;\n\n/**\n * Options for reading an environment variable with an optional default.\n * Used as the third argument to `resolve` or as the second element of a `[spec, options]` tuple in `resolveAll`.\n *\n * `trim` defaults to `true` for `number`, `boolean`, and `enum` specs (whitespace-only values are\n * treated as missing). For `string` specs it defaults to `false` (values are returned as-is).\n *\n * @template S - Environment variable spec type.\n */\nexport type StrictEnvOptions<S extends StrictEnvSpec> = {\n default?: StrictEnvSpecToType<S>;\n /** When `true`, trims leading/trailing whitespace before validation. */\n trim?: boolean;\n};\n\n/**\n * Schema entry for a single env var.\n *\n * Either provide a spec directly, or a tuple of `[spec, options]` to attach a default.\n */\nexport type StrictEnvSchemaEntry<S extends StrictEnvSpec = StrictEnvSpec> = S | readonly [S, StrictEnvOptions<S>];\n\n/**\n * Schema object used by `resolveAll()`.\n *\n * Keys are env var names, values are specs (optionally with defaults).\n */\nexport type StrictEnvSchema = Record<string, StrictEnvSchemaEntry>;\n\n/**\n * Extracts the spec type from a schema entry or `[spec, options]` tuple.\n *\n * @template E - Schema entry type.\n */\ntype StrictEnvSchemaEntryToSpec<E> = E extends readonly [infer S, unknown] ? S : E;\n\n/**\n * Maps a schema object to the resulting parsed environment object type.\n *\n * @template TSchema - Schema object type passed to `resolveAll`.\n */\nexport type StrictEnvSchemaToType<TSchema extends StrictEnvSchema> = {\n [K in keyof TSchema]: StrictEnvSpecToType<StrictEnvSchemaEntryToSpec<TSchema[K]> & StrictEnvSpec>;\n};\n\n/**\n * Discriminated result of parsing a single environment variable.\n *\n * On success, `value` holds the parsed result. On failure, `error` holds a structured\n * `StrictEnvValidationEntry` entry (never throws).\n *\n * @template K - Environment variable key type.\n * @template S - Environment variable spec type.\n */\ntype ParseEnvValueResult<K extends string, S extends StrictEnvSpec> =\n | { ok: true; value: StrictEnvSpecToType<S> }\n | { ok: false; error: StrictEnvValidationEntry<K> };\n\n/**\n * Default trim behavior per spec type.\n * Parsed types trim by default; strings preserve the raw value unless opted in.\n *\n * @param spec - Environment variable spec.\n * @returns Whether leading/trailing whitespace should be trimmed before validation.\n */\nconst defaultTrimForSpec = (spec: StrictEnvSpec): boolean => spec.type !== 'string';\n\n/**\n * Normalizes a raw environment variable value before validation.\n *\n * @param raw - Raw value from `process.env`, if present.\n * @param trim - Whether to trim leading/trailing whitespace.\n * @returns Normalized value, or `undefined` when unset.\n */\nconst normalizeEnvRaw = (raw: string | undefined, trim: boolean): string | undefined => {\n if (raw == null) {\n return undefined;\n }\n if (!trim) {\n return raw;\n }\n return raw.trim();\n};\n\n/** Options accepted by `parseEnvValue` (subset of `StrictEnvOptions`). */\ntype ParseEnvValueOptions<S extends StrictEnvSpec> = Pick<StrictEnvOptions<S>, 'trim'>;\n\n/**\n * Resolves a schema entry into its spec and optional per-key options.\n *\n * @param entry - Schema entry or `[spec, options]` tuple.\n * @returns Parsed spec and options for `resolveAll`.\n */\nconst resolveSchemaEntry = (\n entry: StrictEnvSchemaEntry,\n): { spec: StrictEnvSpec; options: StrictEnvOptions<StrictEnvSpec> | undefined } => ({\n spec: (Array.isArray(entry) ? entry[0] : entry) as StrictEnvSpec,\n options: (Array.isArray(entry) ? entry[1] : undefined) as StrictEnvOptions<StrictEnvSpec> | undefined,\n});\n\n/**\n * Parses a single environment variable according to the given spec.\n *\n * Central validation helper shared by `resolve` and `resolveAll`. Does not throw;\n * callers decide whether to throw immediately or collect errors.\n *\n * Missing or empty (`\"\"`) values use `defaultValue` when provided. When `trim` is enabled,\n * whitespace-only values are treated as empty. Number specs delegate to `parseNumber`.\n * Boolean specs treat `1`/`true`/`yes`/`on` (case-insensitive, after trim) as `true`.\n * Enum specs require an exact match in `choices` (after trim).\n *\n * @template K - Environment variable key type.\n * @template S - Environment variable spec type.\n * @param key - Environment variable name.\n * @param spec - Type spec for the value.\n * @param raw - Raw value from `process.env`, if present.\n * @param defaultValue - Fallback when `raw` is missing or empty.\n * @param options - Optional trim override (`trim` defaults per spec type).\n * @returns Parsed value or a structured validation error.\n */\nconst parseEnvValue = <K extends string, S extends StrictEnvSpec>(\n key: K,\n spec: S,\n raw: string | undefined,\n defaultValue: StrictEnvSpecToType<S> | undefined,\n options?: ParseEnvValueOptions<S>,\n): ParseEnvValueResult<K, S> => {\n const trim = options?.trim ?? defaultTrimForSpec(spec);\n const normalizedRaw = normalizeEnvRaw(raw, trim);\n const hasDefault = defaultValue !== undefined;\n\n if (normalizedRaw == null || normalizedRaw === '') {\n if (hasDefault) {\n return { ok: true, value: defaultValue as StrictEnvSpecToType<S> };\n }\n return {\n ok: false,\n error: { key, message: `Missing required environment variable: ${key}`, raw, kind: 'missing' },\n };\n }\n\n switch (spec.type) {\n case 'number': {\n const n = parseNumber(normalizedRaw);\n if (n === undefined) {\n return {\n ok: false,\n error: { key, message: `Env ${key}: expected number, got \"${raw}\"`, raw, kind: 'invalid_number' },\n };\n }\n return { ok: true, value: n as StrictEnvSpecToType<S> };\n }\n case 'boolean':\n return { ok: true, value: parseBooleanEnvValue(normalizedRaw) as StrictEnvSpecToType<S> };\n case 'enum':\n if (!spec.choices.includes(normalizedRaw)) {\n return {\n ok: false,\n error: {\n key,\n message: `Env ${key}: must be one of [${spec.choices.join(', ')}]`,\n raw,\n kind: 'invalid_enum',\n },\n };\n }\n return { ok: true, value: normalizedRaw as StrictEnvSpecToType<S> };\n default:\n return { ok: true, value: normalizedRaw as StrictEnvSpecToType<S> };\n }\n};\n\n/**\n * Reads and parses an environment variable according to the given spec.\n *\n * Delegates validation to `parseEnvValue`. Missing or empty (`\"\"`) values use\n * `options.default` when provided; otherwise a `StrictEnvValidationError` is thrown.\n * For `StrictEnvType.Number`, values are parsed as finite numbers (see `StrictEnvTypeNumber`).\n *\n * @template K - Environment variable key type.\n * @template S - Environment variable spec type.\n * @param key - Environment variable name (e.g. `\"PORT\"`, `\"NODE_ENV\"`).\n * @param spec - Type spec; defaults to `StrictEnvType.String` when omitted.\n * @param options - Optional `{ default, trim }`; see {@link StrictEnvOptions}.\n * @returns Parsed value with type inferred from `spec`.\n * @throws {StrictEnvValidationError} When the variable is missing, empty without a default, or invalid for the spec.\n */\nconst resolve = <K extends string, S extends StrictEnvSpec = StrictEnvTypeString>(\n key: K,\n spec: S = StrictEnvType.String as S,\n options?: StrictEnvOptions<S>,\n): StrictEnvSpecToType<S> => {\n const result = parseEnvValue(key, spec, process.env[key], options?.default, options);\n if (!result.ok) {\n throw new StrictEnvValidationError([result.error]);\n }\n return result.value;\n};\n\n/**\n * Reads and parses multiple environment variables according to the given schema.\n *\n * Always evaluates every key in `schema`. Each entry is validated via `parseEnvValue`;\n * all errors are collected and thrown once in a single `StrictEnvValidationError`.\n * Number specs use the same finite-number rules as `resolve`.\n *\n * @template TSchema - Schema object type.\n * @param schema - Map of environment variable names to specs, optionally with per-key defaults via `[spec, { default }]`.\n * @returns Parsed environment object with types inferred from the schema.\n * @throws {StrictEnvValidationError} When one or more variables are missing, empty without a default, or invalid.\n */\nconst resolveAll = <TSchema extends StrictEnvSchema>(schema: TSchema): StrictEnvSchemaToType<TSchema> => {\n const envs: Partial<StrictEnvSchemaToType<TSchema>> = {};\n const errors: StrictEnvValidationEntry<Extract<keyof TSchema, string>>[] = [];\n\n for (const key of Object.keys(schema) as Array<Extract<keyof TSchema, string>>) {\n const { spec, options } = resolveSchemaEntry(schema[key]);\n\n const result = parseEnvValue(key, spec, process.env[key], options?.default, options);\n if (!result.ok) {\n errors.push(result.error);\n continue;\n }\n envs[key] = result.value as StrictEnvSchemaToType<TSchema>[typeof key];\n }\n\n if (errors.length > 0) throw new StrictEnvValidationError(errors);\n return envs as StrictEnvSchemaToType<TSchema>;\n};\n\n/**\n * Type-safe environment variable resolver for Node.js `process.env`.\n *\n * @example\n * ```ts\n * const port = StrictEnvResolver.resolve('PORT', StrictEnvType.Number);\n * const envs = StrictEnvResolver.resolveAll({\n * PORT: StrictEnvType.Number,\n * DEBUG: [StrictEnvType.Boolean, { default: false }],\n * });\n * ```\n */\nexport const StrictEnvResolver = {\n /** Reads and parses a single environment variable. See {@link resolve}. */\n resolve,\n /** Reads and parses multiple environment variables in one pass. See {@link resolveAll}. */\n resolveAll,\n} as const;\n", "import { CodePipelineClient, GetPipelineExecutionCommand } from '@aws-sdk/client-codepipeline';\nimport { PublishCommand, SNSClient } from '@aws-sdk/client-sns';\nimport type { EventBridgeEvent } from 'aws-lambda';\nimport { StrictEnvResolver, StrictEnvType } from 'strict-env-resolver';\nimport {\n isTerminalExecutionStatus,\n normalizeExecutionStatus,\n resolvePipelineExecutionIdentity,\n} from './notifier-predicates';\n\n/**\n * EventBridge detail payload for CodePipeline execution state-change events.\n */\ntype CodePipelineExecutionStartedDetail = {\n 'pipeline'?: string;\n 'state'?: string;\n 'version'?: string;\n 'execution-id'?: string;\n};\n\n/**\n * EventBridge event for CodePipeline execution state-change notifications.\n */\ntype CodePipelineExecutionStartedEvent = EventBridgeEvent<\n 'CodePipeline Pipeline Execution State Change',\n CodePipelineExecutionStartedDetail\n>;\n\n/**\n * SNS client used to publish notifications.\n */\nconst sns = new SNSClient({});\n\n/**\n * CodePipeline client used to wait for execution state changes.\n */\nconst codepipeline = new CodePipelineClient({});\n\n/**\n * Reads a required environment variable.\n */\nconst mustEnv = (name: string): string => StrictEnvResolver.resolve(name, StrictEnvType.String, { trim: true });\n\n/**\n * Sleeps for the specified number of milliseconds.\n */\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Publishes a JSON payload to the given SNS topic ARN.\n */\nconst publish = async (topicArn: string, payload: unknown): Promise<void> => {\n await sns.send(new PublishCommand({\n TopicArn: topicArn,\n Message: JSON.stringify(payload),\n }));\n};\n\n/**\n * Waits on a CodePipeline execution and publishes notifications whenever the execution status changes.\n */\nconst waitPipelineExecution = async (params: {\n topicArn: string;\n pipelineName: string;\n executionId: string;\n waitIntervalSeconds: number;\n maxWaitMinutes: number;\n startEvent: CodePipelineExecutionStartedEvent;\n}): Promise<void> => {\n const {\n topicArn,\n pipelineName,\n executionId,\n waitIntervalSeconds,\n maxWaitMinutes,\n startEvent,\n } = params;\n\n const deadline = Date.now() + maxWaitMinutes * 60_000;\n let lastStatus: string | undefined;\n\n // \u6700\u521D\u306B STARTED \u3092\u901A\u77E5\uFF08EventBridge\u7531\u6765\uFF09\n await publish(topicArn, {\n type: 'codepipeline.execution',\n phase: 'eventbridge',\n pipelineName,\n executionId,\n observedState: startEvent.detail?.state ?? 'STARTED',\n observedAt: new Date().toISOString(),\n event: startEvent,\n });\n\n while (Date.now() < deadline) {\n const res = await codepipeline.send(new GetPipelineExecutionCommand({\n pipelineName,\n pipelineExecutionId: executionId,\n }));\n\n const status = normalizeExecutionStatus(res.pipelineExecution?.status);\n if (status !== lastStatus) {\n lastStatus = status;\n await publish(topicArn, {\n type: 'codepipeline.execution',\n phase: 'wait',\n pipelineName,\n executionId,\n observedState: status,\n observedAt: new Date().toISOString(),\n });\n }\n\n if (isTerminalExecutionStatus(status)) {\n return;\n }\n\n await sleep(waitIntervalSeconds * 1000);\n }\n\n await publish(topicArn, {\n type: 'codepipeline.execution',\n phase: 'wait',\n pipelineName,\n executionId,\n observedState: lastStatus ?? 'UNKNOWN',\n observedAt: new Date().toISOString(),\n note: 'Waiting timed out before terminal state.',\n });\n};\n\n/**\n * Lambda handler triggered by EventBridge when a pipeline execution transitions to STARTED.\n * It waits for the execution status until it reaches a terminal state or times out.\n */\nexport const handler = async (event: CodePipelineExecutionStartedEvent): Promise<void> => {\n const topicArn = mustEnv('SNS_TOPIC_ARN');\n const identity = resolvePipelineExecutionIdentity(\n event.detail?.pipeline,\n event.detail?.['execution-id'],\n );\n\n if (!identity) {\n await publish(topicArn, {\n type: 'codepipeline.execution',\n phase: 'eventbridge',\n observedAt: new Date().toISOString(),\n note: 'Missing pipelineName or executionId in event.detail',\n event,\n });\n return;\n }\n\n const waitIntervalSeconds = StrictEnvResolver.resolve('WAIT_INTERVAL_SECONDS', StrictEnvType.Number, { default: 10 });\n const maxWaitMinutes = StrictEnvResolver.resolve('MAX_WAIT_MINUTES', StrictEnvType.Number, { default: 14 });\n\n await waitPipelineExecution({\n topicArn,\n pipelineName: identity.pipelineName,\n executionId: identity.executionId,\n waitIntervalSeconds: waitIntervalSeconds > 0 ? waitIntervalSeconds : 10,\n maxWaitMinutes: maxWaitMinutes > 0 ? maxWaitMinutes : 14,\n startEvent: event,\n });\n};\n", "/**\n * Pipeline and execution identifiers required to wait on a CodePipeline execution.\n */\nexport interface PipelineExecutionIdentity {\n readonly pipelineName: string;\n readonly executionId: string;\n}\n\n/**\n * Normalizes a raw CodePipeline execution status for comparison.\n *\n * @param statusRaw the status returned by GetPipelineExecution\n * @returns upper-cased status, or `UNKNOWN` when missing\n */\nexport const normalizeExecutionStatus = (statusRaw: string | undefined): string =>\n String(statusRaw ?? 'UNKNOWN').toUpperCase();\n\n/**\n * Returns whether a normalized CodePipeline execution status is terminal.\n *\n * @param status normalized execution status\n * @returns true when waiting should stop\n */\nexport const isTerminalExecutionStatus = (status: string): boolean => (\n status === 'SUCCEEDED'\n || status === 'FAILED'\n || status === 'STOPPED'\n || status === 'SUPERSEDED'\n);\n\n/**\n * Resolves pipeline/execution identifiers from EventBridge detail fields.\n *\n * @param pipelineName pipeline name from the event detail\n * @param executionId execution id from the event detail\n * @returns identifiers when both are present; otherwise undefined\n */\nexport const resolvePipelineExecutionIdentity = (\n pipelineName: string | undefined,\n executionId: string | undefined,\n): PipelineExecutionIdentity | undefined => {\n if (!pipelineName || !executionId) {\n return undefined;\n }\n return { pipelineName, executionId };\n};\n"],
|
|
5
|
-
"mappings": "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
|
|
3
|
+
"sources": ["../../../node_modules/strict-env-resolver/src/index.ts", "../../../src/funcs/notifier.lambda.ts", "../../../src/funcs/notifier-predicates.ts", "../../../src/funcs/target-pipeline-predicates.ts"],
|
|
4
|
+
"sourcesContent": ["/**\n * Spec for a string environment variable.\n * Returned as-is by default (`trim` defaults to `false`). Set `trim: true` in options to trim\n * whitespace and treat whitespace-only values as missing.\n * The default value is passed via the third argument of `resolve` or a `[spec, options]` tuple in `resolveAll`, not in the spec.\n */\nexport type StrictEnvTypeString = { type: 'string'; default?: string };\n\n/**\n * Spec for a numeric environment variable.\n * Values are parsed with `Number()`; `NaN`, `Infinity`, and `-Infinity` are rejected.\n * The default value is passed via the third argument of `resolve` or a `[spec, options]` tuple in `resolveAll`, not in the spec.\n */\nexport type StrictEnvTypeNumber = { type: 'number'; default?: number };\n\n/**\n * Pattern for boolean env values that parse as `true`.\n * Matches `1`, `true`, `yes`, and `on` (case-insensitive) on the full trimmed string.\n */\nconst TRUE_BOOLEAN_PATTERN = /^(1|true|yes|on)$/i;\n\n/**\n * Parses a string as a finite number for environment variables.\n *\n * @param raw - Trimmed environment variable value.\n * @returns Parsed number, or `undefined` when the value is not a finite number.\n */\nconst parseNumber = (raw: string): number | undefined => {\n const n = Number(raw);\n if (!Number.isFinite(n)) {\n return undefined;\n }\n return n;\n};\n\n/**\n * Parses a trimmed string as a boolean environment variable value.\n *\n * @param normalizedRaw - Trimmed raw environment variable value.\n * @returns `true` for `1`/`true`/`yes`/`on` (case-insensitive); otherwise `false`.\n */\nconst parseBooleanEnvValue = (normalizedRaw: string): boolean => TRUE_BOOLEAN_PATTERN.test(normalizedRaw);\n\n/**\n * Spec for a boolean environment variable.\n * Parses `1`, `true`, `yes`, `on` (case-insensitive, after trim) as `true`; any other non-empty value as `false`.\n * Trims leading/trailing whitespace by default (see `StrictEnvOptions.trim`).\n * The default value is passed via the third argument of `resolve` or a `[spec, options]` tuple in `resolveAll`, not in the spec.\n */\nexport type StrictEnvTypeBoolean = { type: 'boolean'; default?: boolean };\n\n/**\n * Spec for an enum environment variable with a fixed set of choices.\n * The value must be one of `choices` (compared after trim); otherwise validation fails.\n * Trims leading/trailing whitespace by default (see `StrictEnvOptions.trim`).\n * The default value is passed via the third argument of `resolve` or a `[spec, options]` tuple in `resolveAll`, not in the spec.\n *\n * @template T - Literal string union of allowed values.\n */\nexport type StrictEnvTypeEnum<T extends string = string> = { type: 'enum'; choices: readonly T[]; default?: T };\n\n/** Union of all environment variable spec types. */\nexport type StrictEnvSpec =\n | StrictEnvTypeString\n | StrictEnvTypeNumber\n | StrictEnvTypeBoolean\n | StrictEnvTypeEnum;\n\n/**\n * Discriminated union of error kinds emitted during environment variable parsing.\n *\n * - `missing` — Variable unset, empty string, or whitespace-only (when trim is enabled) without a default.\n * - `invalid_number` — Value is not a finite number (see `StrictEnvTypeNumber`).\n * - `invalid_enum` — Value is not one of the allowed enum choices.\n */\nexport type StrictEnvErrorKind = 'missing' | 'invalid_number' | 'invalid_enum';\n\n/**\n * A structured validation error entry for a single environment variable.\n *\n * @template K - Environment variable key type.\n */\nexport type StrictEnvValidationEntry<K extends string = string> = {\n /** Environment variable name. */\n key: K;\n /** Human-readable error message. */\n message: string;\n /** Raw value from `process.env`, if present. */\n raw?: string;\n /** Error category for programmatic handling. */\n kind: StrictEnvErrorKind;\n};\n\n/**\n * Base error class for this package.\n *\n * @example\n * ```ts\n * try {\n * StrictEnvResolver.resolve('PORT', StrictEnvType.Number);\n * } catch (e) {\n * if (e instanceof StrictEnvError) {\n * // Handle all strict-env-resolver errors\n * }\n * }\n * ```\n */\nexport abstract class StrictEnvError extends Error {\n /**\n * @param message - Error message.\n */\n protected constructor(message: string) {\n super(message);\n this.name = 'StrictEnvError';\n }\n}\n\n/**\n * Validation error that carries one or more environment variable issues.\n *\n * Thrown by `StrictEnvResolver.resolve()` (single-entry `errors`) and\n * `StrictEnvResolver.resolveAll()` (multi-entry `errors`).\n * Individual entries are produced by the internal `parseEnvValue` helper.\n *\n * @template K - Union of environment variable keys included in `errors`.\n */\nexport class StrictEnvValidationError<K extends string = string> extends StrictEnvError {\n /**\n * Formats validation errors into a human-readable error message.\n *\n * @template K - Environment variable key type.\n * @param errors - Validation error entries to format.\n * @returns Multi-line summary listing each key and message.\n */\n public static format<K extends string>(errors: readonly StrictEnvValidationEntry<K>[]): string {\n const lines = errors.map((e) => `- ${e.key}: ${e.message}${e.raw == null ? '' : ` (raw=\"${e.raw}\")`}`);\n return `Invalid environment variables (${errors.length}):\\n${lines.join('\\n')}`;\n }\n\n /**\n * Structured list of validation errors.\n */\n public readonly errors: readonly StrictEnvValidationEntry<K>[];\n /**\n * Convenience list of keys included in `errors`.\n */\n public readonly keys: readonly K[];\n\n /**\n * Creates a new validation error from one or more `StrictEnvValidationEntry` entries.\n *\n * @param errors - One or more structured validation errors.\n */\n public constructor(errors: readonly StrictEnvValidationEntry<K>[]) {\n const msg = StrictEnvValidationError.format(errors);\n super(msg);\n this.name = 'StrictEnvValidationError';\n this.errors = errors;\n this.keys = errors.map((e) => e.key);\n }\n}\n\n/**\n * Predefined spec constants for use with `resolve` or as schema values in `resolveAll`.\n * Provide defaults via the third argument of `resolve` or a `[spec, { default }]` tuple in `resolveAll`.\n */\nexport const StrictEnvType = {\n /** Spec for a string value (returned as-is unless `trim: true` is set in options). */\n String: { type: 'string' } as const satisfies StrictEnvTypeString,\n /**\n * Spec for a finite numeric value (`Number()` parsing; rejects `NaN` and `Infinity`).\n */\n Number: { type: 'number' } as const satisfies StrictEnvTypeNumber,\n /** Spec for a boolean value (`1`/`true`/`yes`/`on` → `true`; other non-empty values → `false`). */\n Boolean: { type: 'boolean' } as const satisfies StrictEnvTypeBoolean,\n /**\n * Returns a spec that restricts the value to one of the given choices.\n *\n * @param choices - Allowed string literals.\n * @returns Enum spec for use with `resolve` or `resolveAll`.\n */\n Enum: <T extends string>(choices: readonly T[]) => ({ type: 'enum', choices }) as StrictEnvTypeEnum<T>,\n} as const;\n\n/**\n * Infers the return type from the given spec.\n * @template S - A `StrictEnvSpec` variant.\n */\nexport type StrictEnvSpecToType<S> =\n S extends StrictEnvTypeString ? string\n : S extends StrictEnvTypeNumber ? number\n : S extends StrictEnvTypeBoolean ? boolean\n : S extends StrictEnvTypeEnum<infer T> ? T\n : never;\n\n/**\n * Options for reading an environment variable with an optional default.\n * Used as the third argument to `resolve` or as the second element of a `[spec, options]` tuple in `resolveAll`.\n *\n * `trim` defaults to `true` for `number`, `boolean`, and `enum` specs (whitespace-only values are\n * treated as missing). For `string` specs it defaults to `false` (values are returned as-is).\n *\n * @template S - Environment variable spec type.\n */\nexport type StrictEnvOptions<S extends StrictEnvSpec> = {\n default?: StrictEnvSpecToType<S>;\n /** When `true`, trims leading/trailing whitespace before validation. */\n trim?: boolean;\n};\n\n/**\n * Schema entry for a single env var.\n *\n * Either provide a spec directly, or a tuple of `[spec, options]` to attach a default.\n */\nexport type StrictEnvSchemaEntry<S extends StrictEnvSpec = StrictEnvSpec> = S | readonly [S, StrictEnvOptions<S>];\n\n/**\n * Schema object used by `resolveAll()`.\n *\n * Keys are env var names, values are specs (optionally with defaults).\n */\nexport type StrictEnvSchema = Record<string, StrictEnvSchemaEntry>;\n\n/**\n * Extracts the spec type from a schema entry or `[spec, options]` tuple.\n *\n * @template E - Schema entry type.\n */\ntype StrictEnvSchemaEntryToSpec<E> = E extends readonly [infer S, unknown] ? S : E;\n\n/**\n * Maps a schema object to the resulting parsed environment object type.\n *\n * @template TSchema - Schema object type passed to `resolveAll`.\n */\nexport type StrictEnvSchemaToType<TSchema extends StrictEnvSchema> = {\n [K in keyof TSchema]: StrictEnvSpecToType<StrictEnvSchemaEntryToSpec<TSchema[K]> & StrictEnvSpec>;\n};\n\n/**\n * Discriminated result of parsing a single environment variable.\n *\n * On success, `value` holds the parsed result. On failure, `error` holds a structured\n * `StrictEnvValidationEntry` entry (never throws).\n *\n * @template K - Environment variable key type.\n * @template S - Environment variable spec type.\n */\ntype ParseEnvValueResult<K extends string, S extends StrictEnvSpec> =\n | { ok: true; value: StrictEnvSpecToType<S> }\n | { ok: false; error: StrictEnvValidationEntry<K> };\n\n/**\n * Default trim behavior per spec type.\n * Parsed types trim by default; strings preserve the raw value unless opted in.\n *\n * @param spec - Environment variable spec.\n * @returns Whether leading/trailing whitespace should be trimmed before validation.\n */\nconst defaultTrimForSpec = (spec: StrictEnvSpec): boolean => spec.type !== 'string';\n\n/**\n * Normalizes a raw environment variable value before validation.\n *\n * @param raw - Raw value from `process.env`, if present.\n * @param trim - Whether to trim leading/trailing whitespace.\n * @returns Normalized value, or `undefined` when unset.\n */\nconst normalizeEnvRaw = (raw: string | undefined, trim: boolean): string | undefined => {\n if (raw == null) {\n return undefined;\n }\n if (!trim) {\n return raw;\n }\n return raw.trim();\n};\n\n/** Options accepted by `parseEnvValue` (subset of `StrictEnvOptions`). */\ntype ParseEnvValueOptions<S extends StrictEnvSpec> = Pick<StrictEnvOptions<S>, 'trim'>;\n\n/**\n * Resolves a schema entry into its spec and optional per-key options.\n *\n * @param entry - Schema entry or `[spec, options]` tuple.\n * @returns Parsed spec and options for `resolveAll`.\n */\nconst resolveSchemaEntry = (\n entry: StrictEnvSchemaEntry,\n): { spec: StrictEnvSpec; options: StrictEnvOptions<StrictEnvSpec> | undefined } => ({\n spec: (Array.isArray(entry) ? entry[0] : entry) as StrictEnvSpec,\n options: (Array.isArray(entry) ? entry[1] : undefined) as StrictEnvOptions<StrictEnvSpec> | undefined,\n});\n\n/**\n * Parses a single environment variable according to the given spec.\n *\n * Central validation helper shared by `resolve` and `resolveAll`. Does not throw;\n * callers decide whether to throw immediately or collect errors.\n *\n * Missing or empty (`\"\"`) values use `defaultValue` when provided. When `trim` is enabled,\n * whitespace-only values are treated as empty. Number specs delegate to `parseNumber`.\n * Boolean specs treat `1`/`true`/`yes`/`on` (case-insensitive, after trim) as `true`.\n * Enum specs require an exact match in `choices` (after trim).\n *\n * @template K - Environment variable key type.\n * @template S - Environment variable spec type.\n * @param key - Environment variable name.\n * @param spec - Type spec for the value.\n * @param raw - Raw value from `process.env`, if present.\n * @param defaultValue - Fallback when `raw` is missing or empty.\n * @param options - Optional trim override (`trim` defaults per spec type).\n * @returns Parsed value or a structured validation error.\n */\nconst parseEnvValue = <K extends string, S extends StrictEnvSpec>(\n key: K,\n spec: S,\n raw: string | undefined,\n defaultValue: StrictEnvSpecToType<S> | undefined,\n options?: ParseEnvValueOptions<S>,\n): ParseEnvValueResult<K, S> => {\n const trim = options?.trim ?? defaultTrimForSpec(spec);\n const normalizedRaw = normalizeEnvRaw(raw, trim);\n const hasDefault = defaultValue !== undefined;\n\n if (normalizedRaw == null || normalizedRaw === '') {\n if (hasDefault) {\n return { ok: true, value: defaultValue as StrictEnvSpecToType<S> };\n }\n return {\n ok: false,\n error: { key, message: `Missing required environment variable: ${key}`, raw, kind: 'missing' },\n };\n }\n\n switch (spec.type) {\n case 'number': {\n const n = parseNumber(normalizedRaw);\n if (n === undefined) {\n return {\n ok: false,\n error: { key, message: `Env ${key}: expected number, got \"${raw}\"`, raw, kind: 'invalid_number' },\n };\n }\n return { ok: true, value: n as StrictEnvSpecToType<S> };\n }\n case 'boolean':\n return { ok: true, value: parseBooleanEnvValue(normalizedRaw) as StrictEnvSpecToType<S> };\n case 'enum':\n if (!spec.choices.includes(normalizedRaw)) {\n return {\n ok: false,\n error: {\n key,\n message: `Env ${key}: must be one of [${spec.choices.join(', ')}]`,\n raw,\n kind: 'invalid_enum',\n },\n };\n }\n return { ok: true, value: normalizedRaw as StrictEnvSpecToType<S> };\n default:\n return { ok: true, value: normalizedRaw as StrictEnvSpecToType<S> };\n }\n};\n\n/**\n * Reads and parses an environment variable according to the given spec.\n *\n * Delegates validation to `parseEnvValue`. Missing or empty (`\"\"`) values use\n * `options.default` when provided; otherwise a `StrictEnvValidationError` is thrown.\n * For `StrictEnvType.Number`, values are parsed as finite numbers (see `StrictEnvTypeNumber`).\n *\n * @template K - Environment variable key type.\n * @template S - Environment variable spec type.\n * @param key - Environment variable name (e.g. `\"PORT\"`, `\"NODE_ENV\"`).\n * @param spec - Type spec; defaults to `StrictEnvType.String` when omitted.\n * @param options - Optional `{ default, trim }`; see {@link StrictEnvOptions}.\n * @returns Parsed value with type inferred from `spec`.\n * @throws {StrictEnvValidationError} When the variable is missing, empty without a default, or invalid for the spec.\n */\nconst resolve = <K extends string, S extends StrictEnvSpec = StrictEnvTypeString>(\n key: K,\n spec: S = StrictEnvType.String as S,\n options?: StrictEnvOptions<S>,\n): StrictEnvSpecToType<S> => {\n const result = parseEnvValue(key, spec, process.env[key], options?.default, options);\n if (!result.ok) {\n throw new StrictEnvValidationError([result.error]);\n }\n return result.value;\n};\n\n/**\n * Reads and parses multiple environment variables according to the given schema.\n *\n * Always evaluates every key in `schema`. Each entry is validated via `parseEnvValue`;\n * all errors are collected and thrown once in a single `StrictEnvValidationError`.\n * Number specs use the same finite-number rules as `resolve`.\n *\n * @template TSchema - Schema object type.\n * @param schema - Map of environment variable names to specs, optionally with per-key defaults via `[spec, { default }]`.\n * @returns Parsed environment object with types inferred from the schema.\n * @throws {StrictEnvValidationError} When one or more variables are missing, empty without a default, or invalid.\n */\nconst resolveAll = <TSchema extends StrictEnvSchema>(schema: TSchema): StrictEnvSchemaToType<TSchema> => {\n const envs: Partial<StrictEnvSchemaToType<TSchema>> = {};\n const errors: StrictEnvValidationEntry<Extract<keyof TSchema, string>>[] = [];\n\n for (const key of Object.keys(schema) as Array<Extract<keyof TSchema, string>>) {\n const { spec, options } = resolveSchemaEntry(schema[key]);\n\n const result = parseEnvValue(key, spec, process.env[key], options?.default, options);\n if (!result.ok) {\n errors.push(result.error);\n continue;\n }\n envs[key] = result.value as StrictEnvSchemaToType<TSchema>[typeof key];\n }\n\n if (errors.length > 0) throw new StrictEnvValidationError(errors);\n return envs as StrictEnvSchemaToType<TSchema>;\n};\n\n/**\n * Type-safe environment variable resolver for Node.js `process.env`.\n *\n * @example\n * ```ts\n * const port = StrictEnvResolver.resolve('PORT', StrictEnvType.Number);\n * const envs = StrictEnvResolver.resolveAll({\n * PORT: StrictEnvType.Number,\n * DEBUG: [StrictEnvType.Boolean, { default: false }],\n * });\n * ```\n */\nexport const StrictEnvResolver = {\n /** Reads and parses a single environment variable. See {@link resolve}. */\n resolve,\n /** Reads and parses multiple environment variables in one pass. See {@link resolveAll}. */\n resolveAll,\n} as const;\n", "import { CodePipelineClient, GetPipelineExecutionCommand, ListTagsForResourceCommand } from '@aws-sdk/client-codepipeline';\nimport { PublishCommand, SNSClient } from '@aws-sdk/client-sns';\nimport type { EventBridgeEvent } from 'aws-lambda';\nimport { StrictEnvResolver, StrictEnvType } from 'strict-env-resolver';\nimport {\n isTerminalExecutionStatus,\n normalizeExecutionStatus,\n resolvePipelineExecutionIdentity,\n} from './notifier-predicates';\nimport {\n isAllowedPipelineArn,\n matchesTargetPipelineTags,\n parseAllowedPipelineArns,\n parseTargetPipelineTags,\n resolvePipelineArn,\n toPipelineTagMap,\n} from './target-pipeline-predicates';\n\n/**\n * EventBridge detail payload for CodePipeline execution state-change events.\n */\ntype CodePipelineExecutionStartedDetail = {\n 'pipeline'?: string;\n 'state'?: string;\n 'version'?: string;\n 'execution-id'?: string;\n};\n\n/**\n * EventBridge event for CodePipeline execution state-change notifications.\n */\ntype CodePipelineExecutionStartedEvent = EventBridgeEvent<\n 'CodePipeline Pipeline Execution State Change',\n CodePipelineExecutionStartedDetail\n>;\n\n/**\n * SNS client used to publish notifications.\n */\nconst sns = new SNSClient({});\n\n/**\n * CodePipeline client used to wait for execution state changes.\n */\nconst codepipeline = new CodePipelineClient({});\n\n/**\n * Reads a required environment variable.\n */\nconst mustEnv = (name: string): string => StrictEnvResolver.resolve(name, StrictEnvType.String, { trim: true });\n\n/**\n * Sleeps for the specified number of milliseconds.\n */\nconst sleep = (ms: number): Promise<void> => new Promise((resolve) => setTimeout(resolve, ms));\n\n/**\n * Publishes a JSON payload to the given SNS topic ARN.\n */\nconst publish = async (topicArn: string, payload: unknown): Promise<void> => {\n await sns.send(new PublishCommand({\n TopicArn: topicArn,\n Message: JSON.stringify(payload),\n }));\n};\n\n/**\n * Waits on a CodePipeline execution and publishes notifications whenever the execution status changes.\n */\nconst waitPipelineExecution = async (params: {\n topicArn: string;\n pipelineName: string;\n executionId: string;\n waitIntervalSeconds: number;\n maxWaitMinutes: number;\n startEvent: CodePipelineExecutionStartedEvent;\n}): Promise<void> => {\n const {\n topicArn,\n pipelineName,\n executionId,\n waitIntervalSeconds,\n maxWaitMinutes,\n startEvent,\n } = params;\n\n const deadline = Date.now() + maxWaitMinutes * 60_000;\n let lastStatus: string | undefined;\n\n // \u6700\u521D\u306B STARTED \u3092\u901A\u77E5\uFF08EventBridge\u7531\u6765\uFF09\n await publish(topicArn, {\n type: 'codepipeline.execution',\n phase: 'eventbridge',\n pipelineName,\n executionId,\n observedState: startEvent.detail?.state ?? 'STARTED',\n observedAt: new Date().toISOString(),\n event: startEvent,\n });\n\n while (Date.now() < deadline) {\n const res = await codepipeline.send(new GetPipelineExecutionCommand({\n pipelineName,\n pipelineExecutionId: executionId,\n }));\n\n const status = normalizeExecutionStatus(res.pipelineExecution?.status);\n if (status !== lastStatus) {\n lastStatus = status;\n await publish(topicArn, {\n type: 'codepipeline.execution',\n phase: 'wait',\n pipelineName,\n executionId,\n observedState: status,\n observedAt: new Date().toISOString(),\n });\n }\n\n if (isTerminalExecutionStatus(status)) {\n return;\n }\n\n await sleep(waitIntervalSeconds * 1000);\n }\n\n await publish(topicArn, {\n type: 'codepipeline.execution',\n phase: 'wait',\n pipelineName,\n executionId,\n observedState: lastStatus ?? 'UNKNOWN',\n observedAt: new Date().toISOString(),\n note: 'Waiting timed out before terminal state.',\n });\n};\n\n/**\n * Returns whether the pipeline in the event matches the configured tag filters.\n *\n * @param event EventBridge STARTED event\n * @param pipelineName pipeline name from the event detail\n * @returns true when the pipeline should be notified\n */\nconst isTargetPipelineEvent = async (\n event: CodePipelineExecutionStartedEvent,\n pipelineName: string,\n): Promise<boolean> => {\n const tagFilters = parseTargetPipelineTags(mustEnv('TARGET_PIPELINE_TAGS'));\n const pipelineArn = resolvePipelineArn(\n event.resources,\n event.region,\n event.account,\n pipelineName,\n );\n if (!pipelineArn) {\n throw new Error('Unable to resolve pipeline ARN for tag lookup');\n }\n\n const allowedArns = parseAllowedPipelineArns(process.env.TARGET_PIPELINE_ARNS);\n if (!isAllowedPipelineArn(pipelineArn, allowedArns)) {\n return false;\n }\n\n const listed = await codepipeline.send(new ListTagsForResourceCommand({\n resourceArn: pipelineArn,\n }));\n\n return matchesTargetPipelineTags(toPipelineTagMap(listed.tags), tagFilters);\n};\n\n/**\n * Lambda handler triggered by EventBridge when a pipeline execution transitions to STARTED.\n * It ignores pipelines that do not match `TARGET_PIPELINE_TAGS` (and optional `TARGET_PIPELINE_ARNS`), then waits for a terminal state.\n */\nexport const handler = async (event: CodePipelineExecutionStartedEvent): Promise<void> => {\n const topicArn = mustEnv('SNS_TOPIC_ARN');\n const identity = resolvePipelineExecutionIdentity(\n event.detail?.pipeline,\n event.detail?.['execution-id'],\n );\n\n if (!identity) {\n await publish(topicArn, {\n type: 'codepipeline.execution',\n phase: 'eventbridge',\n observedAt: new Date().toISOString(),\n note: 'Missing pipelineName or executionId in event.detail',\n event,\n });\n return;\n }\n\n if (!await isTargetPipelineEvent(event, identity.pipelineName)) {\n return;\n }\n\n const waitIntervalSeconds = StrictEnvResolver.resolve('WAIT_INTERVAL_SECONDS', StrictEnvType.Number, { default: 10 });\n const maxWaitMinutes = StrictEnvResolver.resolve('MAX_WAIT_MINUTES', StrictEnvType.Number, { default: 14 });\n\n await waitPipelineExecution({\n topicArn,\n pipelineName: identity.pipelineName,\n executionId: identity.executionId,\n waitIntervalSeconds: waitIntervalSeconds > 0 ? waitIntervalSeconds : 10,\n maxWaitMinutes: maxWaitMinutes > 0 ? maxWaitMinutes : 14,\n startEvent: event,\n });\n};\n", "/**\n * Pipeline and execution identifiers required to wait on a CodePipeline execution.\n */\nexport interface PipelineExecutionIdentity {\n readonly pipelineName: string;\n readonly executionId: string;\n}\n\n/**\n * Normalizes a raw CodePipeline execution status for comparison.\n *\n * @param statusRaw the status returned by GetPipelineExecution\n * @returns upper-cased status, or `UNKNOWN` when missing\n */\nexport const normalizeExecutionStatus = (statusRaw: string | undefined): string =>\n String(statusRaw ?? 'UNKNOWN').toUpperCase();\n\n/**\n * Returns whether a normalized CodePipeline execution status is terminal.\n *\n * @param status normalized execution status\n * @returns true when waiting should stop\n */\nexport const isTerminalExecutionStatus = (status: string): boolean => (\n status === 'SUCCEEDED'\n || status === 'FAILED'\n || status === 'STOPPED'\n || status === 'SUPERSEDED'\n);\n\n/**\n * Resolves pipeline/execution identifiers from EventBridge detail fields.\n *\n * @param pipelineName pipeline name from the event detail\n * @param executionId execution id from the event detail\n * @returns identifiers when both are present; otherwise undefined\n */\nexport const resolvePipelineExecutionIdentity = (\n pipelineName: string | undefined,\n executionId: string | undefined,\n): PipelineExecutionIdentity | undefined => {\n if (!pipelineName || !executionId) {\n return undefined;\n }\n return { pipelineName, executionId };\n};\n", "/**\n * Tag filter used to decide whether a CodePipeline should be notified.\n */\nexport interface PipelineTagFilter {\n /**\n * Tag key that must be present on the pipeline.\n */\n readonly key: string;\n\n /**\n * Accepted values for {@link key}. Matching is OR within this list.\n */\n readonly values: readonly string[];\n}\n\nconst isNonEmptyString = (value: unknown): value is string =>\n typeof value === 'string' && value.trim().length > 0;\n\n/**\n * Returns whether `value` is a non-empty list of `{ key, values }` tag filters.\n *\n * @param value unknown JSON value\n * @returns true when the value can be used as tag filters\n */\nexport const isPipelineTagFilterList = (value: unknown): value is PipelineTagFilter[] => {\n if (!Array.isArray(value) || value.length === 0) {\n return false;\n }\n\n for (const item of value) {\n if (!isPipelineTagFilter(item)) {\n return false;\n }\n }\n\n return true;\n};\n\n/**\n * Returns whether `value` is a single tag filter with a non-empty key and values.\n *\n * @param value unknown JSON value\n * @returns true when the value is a tag filter\n */\nexport const isPipelineTagFilter = (value: unknown): value is PipelineTagFilter => {\n if (typeof value !== 'object' || value === null) {\n return false;\n }\n if (!('key' in value) || !('values' in value)) {\n return false;\n }\n if (!isNonEmptyString(value.key)) {\n return false;\n }\n if (!Array.isArray(value.values) || value.values.length === 0) {\n return false;\n }\n\n for (const tagValue of value.values) {\n if (!isNonEmptyString(tagValue)) {\n return false;\n }\n }\n\n return true;\n};\n\n/**\n * Parses `TARGET_PIPELINE_TAGS` JSON into tag filters.\n *\n * @param raw JSON string from the environment\n * @returns normalized tag filters\n * @throws when the JSON is invalid or not a tag-filter list\n */\nexport const parseTargetPipelineTags = (raw: string): PipelineTagFilter[] => {\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error('TARGET_PIPELINE_TAGS must be valid JSON');\n }\n\n if (!isPipelineTagFilterList(parsed)) {\n throw new Error('TARGET_PIPELINE_TAGS must be a non-empty array of { key, values }');\n }\n\n return normalizeTagFilters(parsed);\n};\n\n/**\n * Trims keys/values and rejects duplicate keys.\n *\n * @param filters tag filters to normalize\n * @returns normalized filters\n * @throws when a key is duplicated\n */\nexport const normalizeTagFilters = (filters: readonly PipelineTagFilter[]): PipelineTagFilter[] => {\n const seenKeys = new Set<string>();\n const normalized: PipelineTagFilter[] = [];\n\n for (const filter of filters) {\n const key = filter.key.trim();\n if (seenKeys.has(key)) {\n throw new Error(`Tag filter list contains duplicate key: ${key}`);\n }\n seenKeys.add(key);\n\n normalized.push({\n key,\n values: filter.values.map((value) => value.trim()),\n });\n }\n\n return normalized;\n};\n\n/**\n * Converts CodePipeline tag entries into a key/value map.\n *\n * @param tags tags returned by ListTagsForResource\n * @returns map of tag key to tag value\n */\nexport const toPipelineTagMap = (\n tags: readonly { readonly key?: string; readonly value?: string }[] | undefined,\n): { readonly [key: string]: string } => {\n const mapped: { [key: string]: string } = {};\n if (!tags) {\n return mapped;\n }\n\n for (const tag of tags) {\n if (!tag.key || tag.value === undefined) {\n continue;\n }\n mapped[tag.key] = tag.value;\n }\n\n return mapped;\n};\n\n/**\n * Returns whether pipeline tags satisfy every filter.\n * Keys are AND; values within a filter are OR.\n *\n * @param pipelineTags tags currently on the pipeline\n * @param filters required tag filters\n * @returns true when the pipeline matches all filters\n */\nexport const matchesTargetPipelineTags = (\n pipelineTags: Readonly<{ [key: string]: string }>,\n filters: readonly PipelineTagFilter[],\n): boolean => {\n for (const filter of filters) {\n const actual = pipelineTags[filter.key];\n if (actual === undefined) {\n return false;\n }\n if (!filter.values.includes(actual)) {\n return false;\n }\n }\n\n return true;\n};\n\n/**\n * Resolves a CodePipeline ARN for ListTagsForResource.\n *\n * @param resources EventBridge `resources` array\n * @param region event region\n * @param account event account\n * @param pipelineName pipeline name from the event detail\n * @returns ARN when it can be resolved\n */\nexport const resolvePipelineArn = (\n resources: readonly string[] | undefined,\n region: string | undefined,\n account: string | undefined,\n pipelineName: string,\n): string | undefined => {\n const fromResources = resources?.[0];\n if (fromResources) {\n return fromResources;\n }\n if (!region || !account) {\n return undefined;\n }\n\n return `arn:aws:codepipeline:${region}:${account}:${pipelineName}`;\n};\n\n/**\n * Returns whether `value` is a non-empty list of non-empty strings.\n *\n * @param value unknown JSON value\n * @returns true when the value is a string list\n */\nexport const isNonEmptyStringList = (value: unknown): value is string[] => {\n if (!Array.isArray(value) || value.length === 0) {\n return false;\n }\n\n for (const item of value) {\n if (!isNonEmptyString(item)) {\n return false;\n }\n }\n\n return true;\n};\n\n/**\n * Parses optional `TARGET_PIPELINE_ARNS` JSON into an ARN allowlist.\n *\n * @param raw JSON string from the environment, or undefined when unset\n * @returns normalized ARNs, or undefined when the allowlist is not configured\n * @throws when the JSON is invalid or not a string list\n */\nexport const parseAllowedPipelineArns = (raw: string | undefined): string[] | undefined => {\n if (raw === undefined || raw.trim() === '') {\n return undefined;\n }\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n throw new Error('TARGET_PIPELINE_ARNS must be valid JSON');\n }\n\n if (!isNonEmptyStringList(parsed)) {\n throw new Error('TARGET_PIPELINE_ARNS must be a non-empty array of strings');\n }\n\n return parsed.map((arn) => arn.trim());\n};\n\n/**\n * Returns whether a pipeline ARN is allowed when an allowlist is configured.\n *\n * @param pipelineArn resolved pipeline ARN\n * @param allowedArns optional allowlist\n * @returns true when no allowlist is set, or the ARN is listed\n */\nexport const isAllowedPipelineArn = (\n pipelineArn: string,\n allowedArns: readonly string[] | undefined,\n): boolean => {\n if (!allowedArns) {\n return true;\n }\n\n return allowedArns.includes(pipelineArn);\n};\n\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
6
|
"names": ["exports"]
|
|
7
7
|
}
|
|
@@ -1,9 +1,43 @@
|
|
|
1
1
|
import { Duration, aws_events as events, aws_sns as sns } from 'aws-cdk-lib';
|
|
2
2
|
import { Construct } from 'constructs';
|
|
3
|
+
/**
|
|
4
|
+
* Tag filter for selecting CodePipeline pipelines.
|
|
5
|
+
* Keys across entries are AND; values within an entry are OR.
|
|
6
|
+
*/
|
|
7
|
+
export interface TargetPipelineTag {
|
|
8
|
+
/**
|
|
9
|
+
* Tag key that must be present on the pipeline.
|
|
10
|
+
*/
|
|
11
|
+
readonly key: string;
|
|
12
|
+
/**
|
|
13
|
+
* Accepted values for {@link key}.
|
|
14
|
+
*/
|
|
15
|
+
readonly values: string[];
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Selection criteria for pipelines that should trigger notifications.
|
|
19
|
+
*/
|
|
20
|
+
export interface TargetPipeline {
|
|
21
|
+
/**
|
|
22
|
+
* Tag filters applied to CodePipeline resources.
|
|
23
|
+
*/
|
|
24
|
+
readonly tags: TargetPipelineTag[];
|
|
25
|
+
/**
|
|
26
|
+
* Pipeline ARNs used as IAM resources for `GetPipelineExecution` and `ListTagsForResource`.
|
|
27
|
+
* When set, the notifier also ignores STARTED events whose pipeline ARN is not in this list.
|
|
28
|
+
*
|
|
29
|
+
* @default all CodePipeline resources in this account and region
|
|
30
|
+
*/
|
|
31
|
+
readonly arns?: string[];
|
|
32
|
+
}
|
|
3
33
|
/**
|
|
4
34
|
* Properties for {@link CodePipelineEventNotifier}.
|
|
5
35
|
*/
|
|
6
36
|
export interface CodePipelineEventNotifierProps {
|
|
37
|
+
/**
|
|
38
|
+
* Pipelines that should produce notifications, selected by resource tags.
|
|
39
|
+
*/
|
|
40
|
+
readonly targetPipeline: TargetPipeline;
|
|
7
41
|
/**
|
|
8
42
|
* SNS topic that receives CodePipeline execution notifications.
|
|
9
43
|
* Subscriptions (email/HTTP/etc.) are intentionally not managed by this construct.
|
|
@@ -52,5 +86,5 @@ export declare class CodePipelineEventNotifier extends Construct {
|
|
|
52
86
|
* @param id the construct id
|
|
53
87
|
* @param props construct properties
|
|
54
88
|
*/
|
|
55
|
-
constructor(scope: Construct, id: string, props
|
|
89
|
+
constructor(scope: Construct, id: string, props: CodePipelineEventNotifierProps);
|
|
56
90
|
}
|
|
@@ -5,12 +5,76 @@ const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
|
|
|
5
5
|
const aws_cdk_lib_1 = require("aws-cdk-lib");
|
|
6
6
|
const constructs_1 = require("constructs");
|
|
7
7
|
const notifier_function_1 = require("../funcs/notifier-function");
|
|
8
|
+
/**
|
|
9
|
+
* Validates and normalizes {@link TargetPipeline} tag filters.
|
|
10
|
+
*
|
|
11
|
+
* @param targetPipeline pipeline selection from construct props
|
|
12
|
+
* @returns normalized tag filters
|
|
13
|
+
*/
|
|
14
|
+
const resolveTargetPipelineTags = (targetPipeline) => {
|
|
15
|
+
if (targetPipeline.tags.length === 0) {
|
|
16
|
+
throw new Error('targetPipeline.tags must be a non-empty array of { key, values }');
|
|
17
|
+
}
|
|
18
|
+
const seenKeys = new Set();
|
|
19
|
+
const normalized = [];
|
|
20
|
+
for (const tag of targetPipeline.tags) {
|
|
21
|
+
const key = tag.key.trim();
|
|
22
|
+
if (!key) {
|
|
23
|
+
throw new Error('targetPipeline.tags must be a non-empty array of { key, values }');
|
|
24
|
+
}
|
|
25
|
+
if (seenKeys.has(key)) {
|
|
26
|
+
throw new Error(`targetPipeline.tags contains duplicate key: ${key}`);
|
|
27
|
+
}
|
|
28
|
+
seenKeys.add(key);
|
|
29
|
+
if (tag.values.length === 0) {
|
|
30
|
+
throw new Error('targetPipeline.tags must be a non-empty array of { key, values }');
|
|
31
|
+
}
|
|
32
|
+
const values = [];
|
|
33
|
+
for (const rawValue of tag.values) {
|
|
34
|
+
const value = rawValue.trim();
|
|
35
|
+
if (!value) {
|
|
36
|
+
throw new Error('targetPipeline.tags must be a non-empty array of { key, values }');
|
|
37
|
+
}
|
|
38
|
+
values.push(value);
|
|
39
|
+
}
|
|
40
|
+
normalized.push({ key, values });
|
|
41
|
+
}
|
|
42
|
+
return normalized;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Validates optional pipeline ARNs used for IAM and runtime allowlisting.
|
|
46
|
+
*
|
|
47
|
+
* @param arns pipeline ARNs from construct props
|
|
48
|
+
* @returns normalized ARNs, or undefined when omitted
|
|
49
|
+
*/
|
|
50
|
+
const resolveTargetPipelineArns = (arns) => {
|
|
51
|
+
if (arns === undefined) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
if (arns.length === 0) {
|
|
55
|
+
throw new Error('targetPipeline.arns must be a non-empty array of ARNs');
|
|
56
|
+
}
|
|
57
|
+
const seenArns = new Set();
|
|
58
|
+
const normalized = [];
|
|
59
|
+
for (const rawArn of arns) {
|
|
60
|
+
const arn = rawArn.trim();
|
|
61
|
+
if (!arn) {
|
|
62
|
+
throw new Error('targetPipeline.arns must be a non-empty array of ARNs');
|
|
63
|
+
}
|
|
64
|
+
if (seenArns.has(arn)) {
|
|
65
|
+
throw new Error(`targetPipeline.arns contains duplicate ARN: ${arn}`);
|
|
66
|
+
}
|
|
67
|
+
seenArns.add(arn);
|
|
68
|
+
normalized.push(arn);
|
|
69
|
+
}
|
|
70
|
+
return normalized;
|
|
71
|
+
};
|
|
8
72
|
/**
|
|
9
73
|
* Provisions an EventBridge rule that listens for CodePipeline execution STARTED events,
|
|
10
74
|
* then invokes a notifier Lambda which publishes execution state changes to an SNS topic.
|
|
11
75
|
*/
|
|
12
76
|
class CodePipelineEventNotifier extends constructs_1.Construct {
|
|
13
|
-
static [JSII_RTTI_SYMBOL_1] = { fqn: "codepipeline-event-notifier.CodePipelineEventNotifier", version: "0.
|
|
77
|
+
static [JSII_RTTI_SYMBOL_1] = { fqn: "codepipeline-event-notifier.CodePipelineEventNotifier", version: "0.3.1" };
|
|
14
78
|
/**
|
|
15
79
|
* SNS topic that receives CodePipeline execution notifications.
|
|
16
80
|
*/
|
|
@@ -20,8 +84,16 @@ class CodePipelineEventNotifier extends constructs_1.Construct {
|
|
|
20
84
|
* @param id the construct id
|
|
21
85
|
* @param props construct properties
|
|
22
86
|
*/
|
|
23
|
-
constructor(scope, id, props
|
|
87
|
+
constructor(scope, id, props) {
|
|
24
88
|
super(scope, id);
|
|
89
|
+
const targetPipelineTags = resolveTargetPipelineTags(props.targetPipeline);
|
|
90
|
+
const targetPipelineArns = resolveTargetPipelineArns(props.targetPipeline.arns);
|
|
91
|
+
const pipelineIamResources = targetPipelineArns ?? [
|
|
92
|
+
aws_cdk_lib_1.Stack.of(this).formatArn({
|
|
93
|
+
service: 'codepipeline',
|
|
94
|
+
resource: '*',
|
|
95
|
+
}),
|
|
96
|
+
];
|
|
25
97
|
this.topic = props.topic ?? new aws_cdk_lib_1.aws_sns.Topic(this, 'PipelineEventTopic');
|
|
26
98
|
const waitInterval = props.waitInterval ?? aws_cdk_lib_1.Duration.seconds(10);
|
|
27
99
|
const maxWaitDuration = props.maxWaitDuration ?? aws_cdk_lib_1.Duration.minutes(14);
|
|
@@ -38,16 +110,19 @@ class CodePipelineEventNotifier extends constructs_1.Construct {
|
|
|
38
110
|
SNS_TOPIC_ARN: this.topic.topicArn,
|
|
39
111
|
WAIT_INTERVAL_SECONDS: String(waitInterval.toSeconds()),
|
|
40
112
|
MAX_WAIT_MINUTES: String(maxWaitDuration.toMinutes({ integral: false })),
|
|
113
|
+
TARGET_PIPELINE_TAGS: JSON.stringify(targetPipelineTags),
|
|
114
|
+
...(targetPipelineArns ? { TARGET_PIPELINE_ARNS: JSON.stringify(targetPipelineArns) } : {}),
|
|
41
115
|
},
|
|
42
116
|
timeout,
|
|
43
117
|
});
|
|
44
|
-
// Allow the notifier to publish notifications.
|
|
118
|
+
// Allow the notifier to publish notifications and look up pipeline tags.
|
|
45
119
|
this.topic.grantPublish(fn);
|
|
46
120
|
fn.addToRolePolicy(new aws_cdk_lib_1.aws_iam.PolicyStatement({
|
|
47
121
|
actions: [
|
|
48
122
|
'codepipeline:GetPipelineExecution',
|
|
123
|
+
'codepipeline:ListTagsForResource',
|
|
49
124
|
],
|
|
50
|
-
resources:
|
|
125
|
+
resources: pipelineIamResources,
|
|
51
126
|
}));
|
|
52
127
|
// Trigger the notifier when matching CodePipeline execution events arrive.
|
|
53
128
|
new aws_cdk_lib_1.aws_events.Rule(this, 'OnPipelineExecutionStartedRule', {
|
|
@@ -57,4 +132,4 @@ class CodePipelineEventNotifier extends constructs_1.Construct {
|
|
|
57
132
|
}
|
|
58
133
|
}
|
|
59
134
|
exports.CodePipelineEventNotifier = CodePipelineEventNotifier;
|
|
60
|
-
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29kZXBpcGVsaW5lLWV2ZW50LW5vdGlmaWVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbnN0cnVjdHMvY29kZXBpcGVsaW5lLWV2ZW50LW5vdGlmaWVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7QUFBQSw2Q0FBNEg7QUFDNUgsMkNBQXVDO0FBQ3ZDLGtFQUE4RDtBQTZDOUQ7OztHQUdHO0FBQ0gsTUFBYSx5QkFBMEIsU0FBUSxzQkFBUzs7SUFDdEQ7O09BRUc7SUFDYSxLQUFLLENBQWE7SUFFbEM7Ozs7T0FJRztJQUNILFlBQVksS0FBZ0IsRUFBRSxFQUFVLEVBQUUsUUFBd0MsRUFBRTtRQUNsRixLQUFLLENBQUMsS0FBSyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBRWpCLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDLEtBQUssSUFBSSxJQUFJLHFCQUFHLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxvQkFBb0IsQ0FBQyxDQUFDO1FBRXRFLE1BQU0sWUFBWSxHQUFHLEtBQUssQ0FBQyxZQUFZLElBQUksc0JBQVEsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDaEUsTUFBTSxlQUFlLEdBQUcsS0FBSyxDQUFDLGVBQWUsSUFBSSxzQkFBUSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN0RSxNQUFNLE9BQU8sR0FBRyxLQUFLLENBQUMsT0FBTyxJQUFJLHNCQUFRLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3RELE1BQU0sWUFBWSxHQUFHLEtBQUssQ0FBQyxZQUFZLElBQUk7WUFDekMsTUFBTSxFQUFFLENBQUMsa0JBQWtCLENBQUM7WUFDNUIsVUFBVSxFQUFFLENBQUMsOENBQThDLENBQUM7WUFDNUQsTUFBTSxFQUFFO2dCQUNOLEtBQUssRUFBRSxDQUFDLFNBQVMsQ0FBQzthQUNuQjtTQUNGLENBQUM7UUFFRixNQUFNLEVBQUUsR0FBRyxJQUFJLG9DQUFnQixDQUFDLElBQUksRUFBRSxrQkFBa0IsRUFBRTtZQUN4RCxXQUFXLEVBQUU7Z0JBQ1gsYUFBYSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUTtnQkFDbEMscUJBQXFCLEVBQUUsTUFBTSxDQUFDLFlBQVksQ0FBQyxTQUFTLEVBQUUsQ0FBQztnQkFDdkQsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLGVBQWUsQ0FBQyxTQUFTLENBQUMsRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQzthQUN6RTtZQUNELE9BQU87U0FDUixDQUFDLENBQUM7UUFFSCwrQ0FBK0M7UUFDL0MsSUFBSSxDQUFDLEtBQUssQ0FBQyxZQUFZLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDNUIsRUFBRSxDQUFDLGVBQWUsQ0FBQyxJQUFJLHFCQUFHLENBQUMsZUFBZSxDQUFDO1lBQ3pDLE9BQU8sRUFBRTtnQkFDUCxtQ0FBbUM7YUFDcEM7WUFDRCxTQUFTLEVBQUUsQ0FBQyxHQUFHLENBQUM7U0FDakIsQ0FBQyxDQUFDLENBQUM7UUFFSiwyRUFBMkU7UUFDM0UsSUFBSSx3QkFBTSxDQUFDLElBQUksQ0FBQyxJQUFJLEVBQUUsZ0NBQWdDLEVBQUU7WUFDdEQsWUFBWTtZQUNaLE9BQU8sRUFBRSxDQUFDLElBQUksZ0NBQU8sQ0FBQyxjQUFjLENBQUMsRUFBRSxDQUFDLENBQUM7U0FDMUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQzs7QUFsREgsOERBbURDIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgRHVyYXRpb24sIGF3c19ldmVudHMgYXMgZXZlbnRzLCBhd3NfZXZlbnRzX3RhcmdldHMgYXMgdGFyZ2V0cywgYXdzX2lhbSBhcyBpYW0sIGF3c19zbnMgYXMgc25zIH0gZnJvbSAnYXdzLWNkay1saWInO1xuaW1wb3J0IHsgQ29uc3RydWN0IH0gZnJvbSAnY29uc3RydWN0cyc7XG5pbXBvcnQgeyBOb3RpZmllckZ1bmN0aW9uIH0gZnJvbSAnLi4vZnVuY3Mvbm90aWZpZXItZnVuY3Rpb24nO1xuXG4vKipcbiAqIFByb3BlcnRpZXMgZm9yIHtAbGluayBDb2RlUGlwZWxpbmVFdmVudE5vdGlmaWVyfS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBDb2RlUGlwZWxpbmVFdmVudE5vdGlmaWVyUHJvcHMge1xuICAvKipcbiAgICogU05TIHRvcGljIHRoYXQgcmVjZWl2ZXMgQ29kZVBpcGVsaW5lIGV4ZWN1dGlvbiBub3RpZmljYXRpb25zLlxuICAgKiBTdWJzY3JpcHRpb25zIChlbWFpbC9IVFRQL2V0Yy4pIGFyZSBpbnRlbnRpb25hbGx5IG5vdCBtYW5hZ2VkIGJ5IHRoaXMgY29uc3RydWN0LlxuICAgKlxuICAgKiBAZGVmYXVsdCAtIGEgbmV3IHRvcGljIGlzIGNyZWF0ZWRcbiAgICovXG4gIHJlYWRvbmx5IHRvcGljPzogc25zLklUb3BpYztcblxuICAvKipcbiAgICogSW50ZXJ2YWwgYmV0d2VlbiBgR2V0UGlwZWxpbmVFeGVjdXRpb25gIHdhaXRzLlxuICAgKlxuICAgKiBAZGVmYXVsdCBEdXJhdGlvbi5zZWNvbmRzKDEwKVxuICAgKi9cbiAgcmVhZG9ubHkgd2FpdEludGVydmFsPzogRHVyYXRpb247XG5cbiAgLyoqXG4gICAqIE1heGltdW0gZHVyYXRpb24gdG8gd2FpdCBmb3IgZXhlY3V0aW9uIHN0YXRlIGNoYW5nZXMuXG4gICAqIE1hcHBlZCB0byB0aGUgbm90aWZpZXIgTGFtYmRhIGVudmlyb25tZW50IHZhcmlhYmxlIGBNQVhfV0FJVF9NSU5VVEVTYC5cbiAgICpcbiAgICogQGRlZmF1bHQgRHVyYXRpb24ubWludXRlcygxNClcbiAgICovXG4gIHJlYWRvbmx5IG1heFdhaXREdXJhdGlvbj86IER1cmF0aW9uO1xuXG4gIC8qKlxuICAgKiBUaW1lb3V0IGZvciB0aGUgbm90aWZpZXIgTGFtYmRhIGZ1bmN0aW9uLlxuICAgKiBTaG91bGQgYmUgZ3JlYXRlciB0aGFuIHtAbGluayBtYXhXYWl0RHVyYXRpb259LlxuICAgKlxuICAgKiBAZGVmYXVsdCBEdXJhdGlvbi5taW51dGVzKDE1KVxuICAgKi9cbiAgcmVhZG9ubHkgdGltZW91dD86IER1cmF0aW9uO1xuXG4gIC8qKlxuICAgKiBFdmVudEJyaWRnZSBldmVudCBwYXR0ZXJuIHRoYXQgdHJpZ2dlcnMgdGhlIG5vdGlmaWVyLlxuICAgKlxuICAgKiBAZGVmYXVsdCBDb2RlUGlwZWxpbmUgUGlwZWxpbmUgRXhlY3V0aW9uIFN0YXRlIENoYW5nZSB3aXRoIGBzdGF0ZT1TVEFSVEVEYFxuICAgKi9cbiAgcmVhZG9ubHkgZXZlbnRQYXR0ZXJuPzogZXZlbnRzLkV2ZW50UGF0dGVybjtcbn1cblxuLyoqXG4gKiBQcm92aXNpb25zIGFuIEV2ZW50QnJpZGdlIHJ1bGUgdGhhdCBsaXN0ZW5zIGZvciBDb2RlUGlwZWxpbmUgZXhlY3V0aW9uIFNUQVJURUQgZXZlbnRzLFxuICogdGhlbiBpbnZva2VzIGEgbm90aWZpZXIgTGFtYmRhIHdoaWNoIHB1Ymxpc2hlcyBleGVjdXRpb24gc3RhdGUgY2hhbmdlcyB0byBhbiBTTlMgdG9waWMuXG4gKi9cbmV4cG9ydCBjbGFzcyBDb2RlUGlwZWxpbmVFdmVudE5vdGlmaWVyIGV4dGVuZHMgQ29uc3RydWN0IHtcbiAgLyoqXG4gICAqIFNOUyB0b3BpYyB0aGF0IHJlY2VpdmVzIENvZGVQaXBlbGluZSBleGVjdXRpb24gbm90aWZpY2F0aW9ucy5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSB0b3BpYzogc25zLklUb3BpYztcblxuICAvKipcbiAgICogQHBhcmFtIHNjb3BlIHRoZSBjb25zdHJ1Y3Qgc2NvcGVcbiAgICogQHBhcmFtIGlkIHRoZSBjb25zdHJ1Y3QgaWRcbiAgICogQHBhcmFtIHByb3BzIGNvbnN0cnVjdCBwcm9wZXJ0aWVzXG4gICAqL1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogQ29kZVBpcGVsaW5lRXZlbnROb3RpZmllclByb3BzID0ge30pIHtcbiAgICBzdXBlcihzY29wZSwgaWQpO1xuXG4gICAgdGhpcy50b3BpYyA9IHByb3BzLnRvcGljID8/IG5ldyBzbnMuVG9waWModGhpcywgJ1BpcGVsaW5lRXZlbnRUb3BpYycpO1xuXG4gICAgY29uc3Qgd2FpdEludGVydmFsID0gcHJvcHMud2FpdEludGVydmFsID8/IER1cmF0aW9uLnNlY29uZHMoMTApO1xuICAgIGNvbnN0IG1heFdhaXREdXJhdGlvbiA9IHByb3BzLm1heFdhaXREdXJhdGlvbiA/PyBEdXJhdGlvbi5taW51dGVzKDE0KTtcbiAgICBjb25zdCB0aW1lb3V0ID0gcHJvcHMudGltZW91dCA/PyBEdXJhdGlvbi5taW51dGVzKDE1KTtcbiAgICBjb25zdCBldmVudFBhdHRlcm4gPSBwcm9wcy5ldmVudFBhdHRlcm4gPz8ge1xuICAgICAgc291cmNlOiBbJ2F3cy5jb2RlcGlwZWxpbmUnXSxcbiAgICAgIGRldGFpbFR5cGU6IFsnQ29kZVBpcGVsaW5lIFBpcGVsaW5lIEV4ZWN1dGlvbiBTdGF0ZSBDaGFuZ2UnXSxcbiAgICAgIGRldGFpbDoge1xuICAgICAgICBzdGF0ZTogWydTVEFSVEVEJ10sXG4gICAgICB9LFxuICAgIH07XG5cbiAgICBjb25zdCBmbiA9IG5ldyBOb3RpZmllckZ1bmN0aW9uKHRoaXMsICdOb3RpZmllckZ1bmN0aW9uJywge1xuICAgICAgZW52aXJvbm1lbnQ6IHtcbiAgICAgICAgU05TX1RPUElDX0FSTjogdGhpcy50b3BpYy50b3BpY0FybixcbiAgICAgICAgV0FJVF9JTlRFUlZBTF9TRUNPTkRTOiBTdHJpbmcod2FpdEludGVydmFsLnRvU2Vjb25kcygpKSxcbiAgICAgICAgTUFYX1dBSVRfTUlOVVRFUzogU3RyaW5nKG1heFdhaXREdXJhdGlvbi50b01pbnV0ZXMoeyBpbnRlZ3JhbDogZmFsc2UgfSkpLFxuICAgICAgfSxcbiAgICAgIHRpbWVvdXQsXG4gICAgfSk7XG5cbiAgICAvLyBBbGxvdyB0aGUgbm90aWZpZXIgdG8gcHVibGlzaCBub3RpZmljYXRpb25zLlxuICAgIHRoaXMudG9waWMuZ3JhbnRQdWJsaXNoKGZuKTtcbiAgICBmbi5hZGRUb1JvbGVQb2xpY3kobmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgYWN0aW9uczogW1xuICAgICAgICAnY29kZXBpcGVsaW5lOkdldFBpcGVsaW5lRXhlY3V0aW9uJyxcbiAgICAgIF0sXG4gICAgICByZXNvdXJjZXM6IFsnKiddLFxuICAgIH0pKTtcblxuICAgIC8vIFRyaWdnZXIgdGhlIG5vdGlmaWVyIHdoZW4gbWF0Y2hpbmcgQ29kZVBpcGVsaW5lIGV4ZWN1dGlvbiBldmVudHMgYXJyaXZlLlxuICAgIG5ldyBldmVudHMuUnVsZSh0aGlzLCAnT25QaXBlbGluZUV4ZWN1dGlvblN0YXJ0ZWRSdWxlJywge1xuICAgICAgZXZlbnRQYXR0ZXJuLFxuICAgICAgdGFyZ2V0czogW25ldyB0YXJnZXRzLkxhbWJkYUZ1bmN0aW9uKGZuKV0sXG4gICAgfSk7XG4gIH1cbn1cbiJdfQ==
|
|
135
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiY29kZXBpcGVsaW5lLWV2ZW50LW5vdGlmaWVyLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbnN0cnVjdHMvY29kZXBpcGVsaW5lLWV2ZW50LW5vdGlmaWVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7QUFBQSw2Q0FBbUk7QUFDbkksMkNBQXVDO0FBQ3ZDLGtFQUE4RDtBQW9GOUQ7Ozs7O0dBS0c7QUFDSCxNQUFNLHlCQUF5QixHQUFHLENBQUMsY0FBOEIsRUFBdUIsRUFBRTtJQUN4RixJQUFJLGNBQWMsQ0FBQyxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3JDLE1BQU0sSUFBSSxLQUFLLENBQUMsa0VBQWtFLENBQUMsQ0FBQztJQUN0RixDQUFDO0lBRUQsTUFBTSxRQUFRLEdBQUcsSUFBSSxHQUFHLEVBQVUsQ0FBQztJQUNuQyxNQUFNLFVBQVUsR0FBd0IsRUFBRSxDQUFDO0lBRTNDLEtBQUssTUFBTSxHQUFHLElBQUksY0FBYyxDQUFDLElBQUksRUFBRSxDQUFDO1FBQ3RDLE1BQU0sR0FBRyxHQUFHLEdBQUcsQ0FBQyxHQUFHLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDM0IsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ1QsTUFBTSxJQUFJLEtBQUssQ0FBQyxrRUFBa0UsQ0FBQyxDQUFDO1FBQ3RGLENBQUM7UUFDRCxJQUFJLFFBQVEsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUN0QixNQUFNLElBQUksS0FBSyxDQUFDLCtDQUErQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO1FBQ3hFLENBQUM7UUFDRCxRQUFRLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBRWxCLElBQUksR0FBRyxDQUFDLE1BQU0sQ0FBQyxNQUFNLEtBQUssQ0FBQyxFQUFFLENBQUM7WUFDNUIsTUFBTSxJQUFJLEtBQUssQ0FBQyxrRUFBa0UsQ0FBQyxDQUFDO1FBQ3RGLENBQUM7UUFFRCxNQUFNLE1BQU0sR0FBYSxFQUFFLENBQUM7UUFDNUIsS0FBSyxNQUFNLFFBQVEsSUFBSSxHQUFHLENBQUMsTUFBTSxFQUFFLENBQUM7WUFDbEMsTUFBTSxLQUFLLEdBQUcsUUFBUSxDQUFDLElBQUksRUFBRSxDQUFDO1lBQzlCLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQztnQkFDWCxNQUFNLElBQUksS0FBSyxDQUFDLGtFQUFrRSxDQUFDLENBQUM7WUFDdEYsQ0FBQztZQUNELE1BQU0sQ0FBQyxJQUFJLENBQUMsS0FBSyxDQUFDLENBQUM7UUFDckIsQ0FBQztRQUVELFVBQVUsQ0FBQyxJQUFJLENBQUMsRUFBRSxHQUFHLEVBQUUsTUFBTSxFQUFFLENBQUMsQ0FBQztJQUNuQyxDQUFDO0lBRUQsT0FBTyxVQUFVLENBQUM7QUFDcEIsQ0FBQyxDQUFDO0FBRUY7Ozs7O0dBS0c7QUFDSCxNQUFNLHlCQUF5QixHQUFHLENBQUMsSUFBMEIsRUFBd0IsRUFBRTtJQUNyRixJQUFJLElBQUksS0FBSyxTQUFTLEVBQUUsQ0FBQztRQUN2QixPQUFPLFNBQVMsQ0FBQztJQUNuQixDQUFDO0lBQ0QsSUFBSSxJQUFJLENBQUMsTUFBTSxLQUFLLENBQUMsRUFBRSxDQUFDO1FBQ3RCLE1BQU0sSUFBSSxLQUFLLENBQUMsdURBQXVELENBQUMsQ0FBQztJQUMzRSxDQUFDO0lBRUQsTUFBTSxRQUFRLEdBQUcsSUFBSSxHQUFHLEVBQVUsQ0FBQztJQUNuQyxNQUFNLFVBQVUsR0FBYSxFQUFFLENBQUM7SUFFaEMsS0FBSyxNQUFNLE1BQU0sSUFBSSxJQUFJLEVBQUUsQ0FBQztRQUMxQixNQUFNLEdBQUcsR0FBRyxNQUFNLENBQUMsSUFBSSxFQUFFLENBQUM7UUFDMUIsSUFBSSxDQUFDLEdBQUcsRUFBRSxDQUFDO1lBQ1QsTUFBTSxJQUFJLEtBQUssQ0FBQyx1REFBdUQsQ0FBQyxDQUFDO1FBQzNFLENBQUM7UUFDRCxJQUFJLFFBQVEsQ0FBQyxHQUFHLENBQUMsR0FBRyxDQUFDLEVBQUUsQ0FBQztZQUN0QixNQUFNLElBQUksS0FBSyxDQUFDLCtDQUErQyxHQUFHLEVBQUUsQ0FBQyxDQUFDO1FBQ3hFLENBQUM7UUFDRCxRQUFRLENBQUMsR0FBRyxDQUFDLEdBQUcsQ0FBQyxDQUFDO1FBQ2xCLFVBQVUsQ0FBQyxJQUFJLENBQUMsR0FBRyxDQUFDLENBQUM7SUFDdkIsQ0FBQztJQUVELE9BQU8sVUFBVSxDQUFDO0FBQ3BCLENBQUMsQ0FBQztBQUVGOzs7R0FHRztBQUNILE1BQWEseUJBQTBCLFNBQVEsc0JBQVM7O0lBQ3REOztPQUVHO0lBQ2EsS0FBSyxDQUFhO0lBRWxDOzs7O09BSUc7SUFDSCxZQUFZLEtBQWdCLEVBQUUsRUFBVSxFQUFFLEtBQXFDO1FBQzdFLEtBQUssQ0FBQyxLQUFLLEVBQUUsRUFBRSxDQUFDLENBQUM7UUFFakIsTUFBTSxrQkFBa0IsR0FBRyx5QkFBeUIsQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLENBQUM7UUFDM0UsTUFBTSxrQkFBa0IsR0FBRyx5QkFBeUIsQ0FBQyxLQUFLLENBQUMsY0FBYyxDQUFDLElBQUksQ0FBQyxDQUFDO1FBQ2hGLE1BQU0sb0JBQW9CLEdBQUcsa0JBQWtCLElBQUk7WUFDakQsbUJBQUssQ0FBQyxFQUFFLENBQUMsSUFBSSxDQUFDLENBQUMsU0FBUyxDQUFDO2dCQUN2QixPQUFPLEVBQUUsY0FBYztnQkFDdkIsUUFBUSxFQUFFLEdBQUc7YUFDZCxDQUFDO1NBQ0gsQ0FBQztRQUVGLElBQUksQ0FBQyxLQUFLLEdBQUcsS0FBSyxDQUFDLEtBQUssSUFBSSxJQUFJLHFCQUFHLENBQUMsS0FBSyxDQUFDLElBQUksRUFBRSxvQkFBb0IsQ0FBQyxDQUFDO1FBRXRFLE1BQU0sWUFBWSxHQUFHLEtBQUssQ0FBQyxZQUFZLElBQUksc0JBQVEsQ0FBQyxPQUFPLENBQUMsRUFBRSxDQUFDLENBQUM7UUFDaEUsTUFBTSxlQUFlLEdBQUcsS0FBSyxDQUFDLGVBQWUsSUFBSSxzQkFBUSxDQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUN0RSxNQUFNLE9BQU8sR0FBRyxLQUFLLENBQUMsT0FBTyxJQUFJLHNCQUFRLENBQUMsT0FBTyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQ3RELE1BQU0sWUFBWSxHQUFHLEtBQUssQ0FBQyxZQUFZLElBQUk7WUFDekMsTUFBTSxFQUFFLENBQUMsa0JBQWtCLENBQUM7WUFDNUIsVUFBVSxFQUFFLENBQUMsOENBQThDLENBQUM7WUFDNUQsTUFBTSxFQUFFO2dCQUNOLEtBQUssRUFBRSxDQUFDLFNBQVMsQ0FBQzthQUNuQjtTQUNGLENBQUM7UUFFRixNQUFNLEVBQUUsR0FBRyxJQUFJLG9DQUFnQixDQUFDLElBQUksRUFBRSxrQkFBa0IsRUFBRTtZQUN4RCxXQUFXLEVBQUU7Z0JBQ1gsYUFBYSxFQUFFLElBQUksQ0FBQyxLQUFLLENBQUMsUUFBUTtnQkFDbEMscUJBQXFCLEVBQUUsTUFBTSxDQUFDLFlBQVksQ0FBQyxTQUFTLEVBQUUsQ0FBQztnQkFDdkQsZ0JBQWdCLEVBQUUsTUFBTSxDQUFDLGVBQWUsQ0FBQyxTQUFTLENBQUMsRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLENBQUMsQ0FBQztnQkFDeEUsb0JBQW9CLEVBQUUsSUFBSSxDQUFDLFNBQVMsQ0FBQyxrQkFBa0IsQ0FBQztnQkFDeEQsR0FBRyxDQUFDLGtCQUFrQixDQUFDLENBQUMsQ0FBQyxFQUFFLG9CQUFvQixFQUFFLElBQUksQ0FBQyxTQUFTLENBQUMsa0JBQWtCLENBQUMsRUFBRSxDQUFDLENBQUMsQ0FBQyxFQUFFLENBQUM7YUFDNUY7WUFDRCxPQUFPO1NBQ1IsQ0FBQyxDQUFDO1FBRUgseUVBQXlFO1FBQ3pFLElBQUksQ0FBQyxLQUFLLENBQUMsWUFBWSxDQUFDLEVBQUUsQ0FBQyxDQUFDO1FBQzVCLEVBQUUsQ0FBQyxlQUFlLENBQUMsSUFBSSxxQkFBRyxDQUFDLGVBQWUsQ0FBQztZQUN6QyxPQUFPLEVBQUU7Z0JBQ1AsbUNBQW1DO2dCQUNuQyxrQ0FBa0M7YUFDbkM7WUFDRCxTQUFTLEVBQUUsb0JBQW9CO1NBQ2hDLENBQUMsQ0FBQyxDQUFDO1FBRUosMkVBQTJFO1FBQzNFLElBQUksd0JBQU0sQ0FBQyxJQUFJLENBQUMsSUFBSSxFQUFFLGdDQUFnQyxFQUFFO1lBQ3RELFlBQVk7WUFDWixPQUFPLEVBQUUsQ0FBQyxJQUFJLGdDQUFPLENBQUMsY0FBYyxDQUFDLEVBQUUsQ0FBQyxDQUFDO1NBQzFDLENBQUMsQ0FBQztJQUNMLENBQUM7O0FBOURILDhEQStEQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IER1cmF0aW9uLCBTdGFjaywgYXdzX2V2ZW50cyBhcyBldmVudHMsIGF3c19ldmVudHNfdGFyZ2V0cyBhcyB0YXJnZXRzLCBhd3NfaWFtIGFzIGlhbSwgYXdzX3NucyBhcyBzbnMgfSBmcm9tICdhd3MtY2RrLWxpYic7XG5pbXBvcnQgeyBDb25zdHJ1Y3QgfSBmcm9tICdjb25zdHJ1Y3RzJztcbmltcG9ydCB7IE5vdGlmaWVyRnVuY3Rpb24gfSBmcm9tICcuLi9mdW5jcy9ub3RpZmllci1mdW5jdGlvbic7XG5cbi8qKlxuICogVGFnIGZpbHRlciBmb3Igc2VsZWN0aW5nIENvZGVQaXBlbGluZSBwaXBlbGluZXMuXG4gKiBLZXlzIGFjcm9zcyBlbnRyaWVzIGFyZSBBTkQ7IHZhbHVlcyB3aXRoaW4gYW4gZW50cnkgYXJlIE9SLlxuICovXG5leHBvcnQgaW50ZXJmYWNlIFRhcmdldFBpcGVsaW5lVGFnIHtcbiAgLyoqXG4gICAqIFRhZyBrZXkgdGhhdCBtdXN0IGJlIHByZXNlbnQgb24gdGhlIHBpcGVsaW5lLlxuICAgKi9cbiAgcmVhZG9ubHkga2V5OiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIEFjY2VwdGVkIHZhbHVlcyBmb3Ige0BsaW5rIGtleX0uXG4gICAqL1xuICByZWFkb25seSB2YWx1ZXM6IHN0cmluZ1tdO1xufVxuXG4vKipcbiAqIFNlbGVjdGlvbiBjcml0ZXJpYSBmb3IgcGlwZWxpbmVzIHRoYXQgc2hvdWxkIHRyaWdnZXIgbm90aWZpY2F0aW9ucy5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBUYXJnZXRQaXBlbGluZSB7XG4gIC8qKlxuICAgKiBUYWcgZmlsdGVycyBhcHBsaWVkIHRvIENvZGVQaXBlbGluZSByZXNvdXJjZXMuXG4gICAqL1xuICByZWFkb25seSB0YWdzOiBUYXJnZXRQaXBlbGluZVRhZ1tdO1xuXG4gIC8qKlxuICAgKiBQaXBlbGluZSBBUk5zIHVzZWQgYXMgSUFNIHJlc291cmNlcyBmb3IgYEdldFBpcGVsaW5lRXhlY3V0aW9uYCBhbmQgYExpc3RUYWdzRm9yUmVzb3VyY2VgLlxuICAgKiBXaGVuIHNldCwgdGhlIG5vdGlmaWVyIGFsc28gaWdub3JlcyBTVEFSVEVEIGV2ZW50cyB3aG9zZSBwaXBlbGluZSBBUk4gaXMgbm90IGluIHRoaXMgbGlzdC5cbiAgICpcbiAgICogQGRlZmF1bHQgYWxsIENvZGVQaXBlbGluZSByZXNvdXJjZXMgaW4gdGhpcyBhY2NvdW50IGFuZCByZWdpb25cbiAgICovXG4gIHJlYWRvbmx5IGFybnM/OiBzdHJpbmdbXTtcbn1cblxuLyoqXG4gKiBQcm9wZXJ0aWVzIGZvciB7QGxpbmsgQ29kZVBpcGVsaW5lRXZlbnROb3RpZmllcn0uXG4gKi9cbmV4cG9ydCBpbnRlcmZhY2UgQ29kZVBpcGVsaW5lRXZlbnROb3RpZmllclByb3BzIHtcbiAgLyoqXG4gICAqIFBpcGVsaW5lcyB0aGF0IHNob3VsZCBwcm9kdWNlIG5vdGlmaWNhdGlvbnMsIHNlbGVjdGVkIGJ5IHJlc291cmNlIHRhZ3MuXG4gICAqL1xuICByZWFkb25seSB0YXJnZXRQaXBlbGluZTogVGFyZ2V0UGlwZWxpbmU7XG5cbiAgLyoqXG4gICAqIFNOUyB0b3BpYyB0aGF0IHJlY2VpdmVzIENvZGVQaXBlbGluZSBleGVjdXRpb24gbm90aWZpY2F0aW9ucy5cbiAgICogU3Vic2NyaXB0aW9ucyAoZW1haWwvSFRUUC9ldGMuKSBhcmUgaW50ZW50aW9uYWxseSBub3QgbWFuYWdlZCBieSB0aGlzIGNvbnN0cnVjdC5cbiAgICpcbiAgICogQGRlZmF1bHQgLSBhIG5ldyB0b3BpYyBpcyBjcmVhdGVkXG4gICAqL1xuICByZWFkb25seSB0b3BpYz86IHNucy5JVG9waWM7XG5cbiAgLyoqXG4gICAqIEludGVydmFsIGJldHdlZW4gYEdldFBpcGVsaW5lRXhlY3V0aW9uYCB3YWl0cy5cbiAgICpcbiAgICogQGRlZmF1bHQgRHVyYXRpb24uc2Vjb25kcygxMClcbiAgICovXG4gIHJlYWRvbmx5IHdhaXRJbnRlcnZhbD86IER1cmF0aW9uO1xuXG4gIC8qKlxuICAgKiBNYXhpbXVtIGR1cmF0aW9uIHRvIHdhaXQgZm9yIGV4ZWN1dGlvbiBzdGF0ZSBjaGFuZ2VzLlxuICAgKiBNYXBwZWQgdG8gdGhlIG5vdGlmaWVyIExhbWJkYSBlbnZpcm9ubWVudCB2YXJpYWJsZSBgTUFYX1dBSVRfTUlOVVRFU2AuXG4gICAqXG4gICAqIEBkZWZhdWx0IER1cmF0aW9uLm1pbnV0ZXMoMTQpXG4gICAqL1xuICByZWFkb25seSBtYXhXYWl0RHVyYXRpb24/OiBEdXJhdGlvbjtcblxuICAvKipcbiAgICogVGltZW91dCBmb3IgdGhlIG5vdGlmaWVyIExhbWJkYSBmdW5jdGlvbi5cbiAgICogU2hvdWxkIGJlIGdyZWF0ZXIgdGhhbiB7QGxpbmsgbWF4V2FpdER1cmF0aW9ufS5cbiAgICpcbiAgICogQGRlZmF1bHQgRHVyYXRpb24ubWludXRlcygxNSlcbiAgICovXG4gIHJlYWRvbmx5IHRpbWVvdXQ/OiBEdXJhdGlvbjtcblxuICAvKipcbiAgICogRXZlbnRCcmlkZ2UgZXZlbnQgcGF0dGVybiB0aGF0IHRyaWdnZXJzIHRoZSBub3RpZmllci5cbiAgICpcbiAgICogQGRlZmF1bHQgQ29kZVBpcGVsaW5lIFBpcGVsaW5lIEV4ZWN1dGlvbiBTdGF0ZSBDaGFuZ2Ugd2l0aCBgc3RhdGU9U1RBUlRFRGBcbiAgICovXG4gIHJlYWRvbmx5IGV2ZW50UGF0dGVybj86IGV2ZW50cy5FdmVudFBhdHRlcm47XG59XG5cbi8qKlxuICogVmFsaWRhdGVzIGFuZCBub3JtYWxpemVzIHtAbGluayBUYXJnZXRQaXBlbGluZX0gdGFnIGZpbHRlcnMuXG4gKlxuICogQHBhcmFtIHRhcmdldFBpcGVsaW5lIHBpcGVsaW5lIHNlbGVjdGlvbiBmcm9tIGNvbnN0cnVjdCBwcm9wc1xuICogQHJldHVybnMgbm9ybWFsaXplZCB0YWcgZmlsdGVyc1xuICovXG5jb25zdCByZXNvbHZlVGFyZ2V0UGlwZWxpbmVUYWdzID0gKHRhcmdldFBpcGVsaW5lOiBUYXJnZXRQaXBlbGluZSk6IFRhcmdldFBpcGVsaW5lVGFnW10gPT4ge1xuICBpZiAodGFyZ2V0UGlwZWxpbmUudGFncy5sZW5ndGggPT09IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ3RhcmdldFBpcGVsaW5lLnRhZ3MgbXVzdCBiZSBhIG5vbi1lbXB0eSBhcnJheSBvZiB7IGtleSwgdmFsdWVzIH0nKTtcbiAgfVxuXG4gIGNvbnN0IHNlZW5LZXlzID0gbmV3IFNldDxzdHJpbmc+KCk7XG4gIGNvbnN0IG5vcm1hbGl6ZWQ6IFRhcmdldFBpcGVsaW5lVGFnW10gPSBbXTtcblxuICBmb3IgKGNvbnN0IHRhZyBvZiB0YXJnZXRQaXBlbGluZS50YWdzKSB7XG4gICAgY29uc3Qga2V5ID0gdGFnLmtleS50cmltKCk7XG4gICAgaWYgKCFrZXkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcigndGFyZ2V0UGlwZWxpbmUudGFncyBtdXN0IGJlIGEgbm9uLWVtcHR5IGFycmF5IG9mIHsga2V5LCB2YWx1ZXMgfScpO1xuICAgIH1cbiAgICBpZiAoc2VlbktleXMuaGFzKGtleSkpIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgdGFyZ2V0UGlwZWxpbmUudGFncyBjb250YWlucyBkdXBsaWNhdGUga2V5OiAke2tleX1gKTtcbiAgICB9XG4gICAgc2VlbktleXMuYWRkKGtleSk7XG5cbiAgICBpZiAodGFnLnZhbHVlcy5sZW5ndGggPT09IDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcigndGFyZ2V0UGlwZWxpbmUudGFncyBtdXN0IGJlIGEgbm9uLWVtcHR5IGFycmF5IG9mIHsga2V5LCB2YWx1ZXMgfScpO1xuICAgIH1cblxuICAgIGNvbnN0IHZhbHVlczogc3RyaW5nW10gPSBbXTtcbiAgICBmb3IgKGNvbnN0IHJhd1ZhbHVlIG9mIHRhZy52YWx1ZXMpIHtcbiAgICAgIGNvbnN0IHZhbHVlID0gcmF3VmFsdWUudHJpbSgpO1xuICAgICAgaWYgKCF2YWx1ZSkge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoJ3RhcmdldFBpcGVsaW5lLnRhZ3MgbXVzdCBiZSBhIG5vbi1lbXB0eSBhcnJheSBvZiB7IGtleSwgdmFsdWVzIH0nKTtcbiAgICAgIH1cbiAgICAgIHZhbHVlcy5wdXNoKHZhbHVlKTtcbiAgICB9XG5cbiAgICBub3JtYWxpemVkLnB1c2goeyBrZXksIHZhbHVlcyB9KTtcbiAgfVxuXG4gIHJldHVybiBub3JtYWxpemVkO1xufTtcblxuLyoqXG4gKiBWYWxpZGF0ZXMgb3B0aW9uYWwgcGlwZWxpbmUgQVJOcyB1c2VkIGZvciBJQU0gYW5kIHJ1bnRpbWUgYWxsb3dsaXN0aW5nLlxuICpcbiAqIEBwYXJhbSBhcm5zIHBpcGVsaW5lIEFSTnMgZnJvbSBjb25zdHJ1Y3QgcHJvcHNcbiAqIEByZXR1cm5zIG5vcm1hbGl6ZWQgQVJOcywgb3IgdW5kZWZpbmVkIHdoZW4gb21pdHRlZFxuICovXG5jb25zdCByZXNvbHZlVGFyZ2V0UGlwZWxpbmVBcm5zID0gKGFybnM6IHN0cmluZ1tdIHwgdW5kZWZpbmVkKTogc3RyaW5nW10gfCB1bmRlZmluZWQgPT4ge1xuICBpZiAoYXJucyA9PT0gdW5kZWZpbmVkKSB7XG4gICAgcmV0dXJuIHVuZGVmaW5lZDtcbiAgfVxuICBpZiAoYXJucy5sZW5ndGggPT09IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ3RhcmdldFBpcGVsaW5lLmFybnMgbXVzdCBiZSBhIG5vbi1lbXB0eSBhcnJheSBvZiBBUk5zJyk7XG4gIH1cblxuICBjb25zdCBzZWVuQXJucyA9IG5ldyBTZXQ8c3RyaW5nPigpO1xuICBjb25zdCBub3JtYWxpemVkOiBzdHJpbmdbXSA9IFtdO1xuXG4gIGZvciAoY29uc3QgcmF3QXJuIG9mIGFybnMpIHtcbiAgICBjb25zdCBhcm4gPSByYXdBcm4udHJpbSgpO1xuICAgIGlmICghYXJuKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoJ3RhcmdldFBpcGVsaW5lLmFybnMgbXVzdCBiZSBhIG5vbi1lbXB0eSBhcnJheSBvZiBBUk5zJyk7XG4gICAgfVxuICAgIGlmIChzZWVuQXJucy5oYXMoYXJuKSkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGB0YXJnZXRQaXBlbGluZS5hcm5zIGNvbnRhaW5zIGR1cGxpY2F0ZSBBUk46ICR7YXJufWApO1xuICAgIH1cbiAgICBzZWVuQXJucy5hZGQoYXJuKTtcbiAgICBub3JtYWxpemVkLnB1c2goYXJuKTtcbiAgfVxuXG4gIHJldHVybiBub3JtYWxpemVkO1xufTtcblxuLyoqXG4gKiBQcm92aXNpb25zIGFuIEV2ZW50QnJpZGdlIHJ1bGUgdGhhdCBsaXN0ZW5zIGZvciBDb2RlUGlwZWxpbmUgZXhlY3V0aW9uIFNUQVJURUQgZXZlbnRzLFxuICogdGhlbiBpbnZva2VzIGEgbm90aWZpZXIgTGFtYmRhIHdoaWNoIHB1Ymxpc2hlcyBleGVjdXRpb24gc3RhdGUgY2hhbmdlcyB0byBhbiBTTlMgdG9waWMuXG4gKi9cbmV4cG9ydCBjbGFzcyBDb2RlUGlwZWxpbmVFdmVudE5vdGlmaWVyIGV4dGVuZHMgQ29uc3RydWN0IHtcbiAgLyoqXG4gICAqIFNOUyB0b3BpYyB0aGF0IHJlY2VpdmVzIENvZGVQaXBlbGluZSBleGVjdXRpb24gbm90aWZpY2F0aW9ucy5cbiAgICovXG4gIHB1YmxpYyByZWFkb25seSB0b3BpYzogc25zLklUb3BpYztcblxuICAvKipcbiAgICogQHBhcmFtIHNjb3BlIHRoZSBjb25zdHJ1Y3Qgc2NvcGVcbiAgICogQHBhcmFtIGlkIHRoZSBjb25zdHJ1Y3QgaWRcbiAgICogQHBhcmFtIHByb3BzIGNvbnN0cnVjdCBwcm9wZXJ0aWVzXG4gICAqL1xuICBjb25zdHJ1Y3RvcihzY29wZTogQ29uc3RydWN0LCBpZDogc3RyaW5nLCBwcm9wczogQ29kZVBpcGVsaW5lRXZlbnROb3RpZmllclByb3BzKSB7XG4gICAgc3VwZXIoc2NvcGUsIGlkKTtcblxuICAgIGNvbnN0IHRhcmdldFBpcGVsaW5lVGFncyA9IHJlc29sdmVUYXJnZXRQaXBlbGluZVRhZ3MocHJvcHMudGFyZ2V0UGlwZWxpbmUpO1xuICAgIGNvbnN0IHRhcmdldFBpcGVsaW5lQXJucyA9IHJlc29sdmVUYXJnZXRQaXBlbGluZUFybnMocHJvcHMudGFyZ2V0UGlwZWxpbmUuYXJucyk7XG4gICAgY29uc3QgcGlwZWxpbmVJYW1SZXNvdXJjZXMgPSB0YXJnZXRQaXBlbGluZUFybnMgPz8gW1xuICAgICAgU3RhY2sub2YodGhpcykuZm9ybWF0QXJuKHtcbiAgICAgICAgc2VydmljZTogJ2NvZGVwaXBlbGluZScsXG4gICAgICAgIHJlc291cmNlOiAnKicsXG4gICAgICB9KSxcbiAgICBdO1xuXG4gICAgdGhpcy50b3BpYyA9IHByb3BzLnRvcGljID8/IG5ldyBzbnMuVG9waWModGhpcywgJ1BpcGVsaW5lRXZlbnRUb3BpYycpO1xuXG4gICAgY29uc3Qgd2FpdEludGVydmFsID0gcHJvcHMud2FpdEludGVydmFsID8/IER1cmF0aW9uLnNlY29uZHMoMTApO1xuICAgIGNvbnN0IG1heFdhaXREdXJhdGlvbiA9IHByb3BzLm1heFdhaXREdXJhdGlvbiA/PyBEdXJhdGlvbi5taW51dGVzKDE0KTtcbiAgICBjb25zdCB0aW1lb3V0ID0gcHJvcHMudGltZW91dCA/PyBEdXJhdGlvbi5taW51dGVzKDE1KTtcbiAgICBjb25zdCBldmVudFBhdHRlcm4gPSBwcm9wcy5ldmVudFBhdHRlcm4gPz8ge1xuICAgICAgc291cmNlOiBbJ2F3cy5jb2RlcGlwZWxpbmUnXSxcbiAgICAgIGRldGFpbFR5cGU6IFsnQ29kZVBpcGVsaW5lIFBpcGVsaW5lIEV4ZWN1dGlvbiBTdGF0ZSBDaGFuZ2UnXSxcbiAgICAgIGRldGFpbDoge1xuICAgICAgICBzdGF0ZTogWydTVEFSVEVEJ10sXG4gICAgICB9LFxuICAgIH07XG5cbiAgICBjb25zdCBmbiA9IG5ldyBOb3RpZmllckZ1bmN0aW9uKHRoaXMsICdOb3RpZmllckZ1bmN0aW9uJywge1xuICAgICAgZW52aXJvbm1lbnQ6IHtcbiAgICAgICAgU05TX1RPUElDX0FSTjogdGhpcy50b3BpYy50b3BpY0FybixcbiAgICAgICAgV0FJVF9JTlRFUlZBTF9TRUNPTkRTOiBTdHJpbmcod2FpdEludGVydmFsLnRvU2Vjb25kcygpKSxcbiAgICAgICAgTUFYX1dBSVRfTUlOVVRFUzogU3RyaW5nKG1heFdhaXREdXJhdGlvbi50b01pbnV0ZXMoeyBpbnRlZ3JhbDogZmFsc2UgfSkpLFxuICAgICAgICBUQVJHRVRfUElQRUxJTkVfVEFHUzogSlNPTi5zdHJpbmdpZnkodGFyZ2V0UGlwZWxpbmVUYWdzKSxcbiAgICAgICAgLi4uKHRhcmdldFBpcGVsaW5lQXJucyA/IHsgVEFSR0VUX1BJUEVMSU5FX0FSTlM6IEpTT04uc3RyaW5naWZ5KHRhcmdldFBpcGVsaW5lQXJucykgfSA6IHt9KSxcbiAgICAgIH0sXG4gICAgICB0aW1lb3V0LFxuICAgIH0pO1xuXG4gICAgLy8gQWxsb3cgdGhlIG5vdGlmaWVyIHRvIHB1Ymxpc2ggbm90aWZpY2F0aW9ucyBhbmQgbG9vayB1cCBwaXBlbGluZSB0YWdzLlxuICAgIHRoaXMudG9waWMuZ3JhbnRQdWJsaXNoKGZuKTtcbiAgICBmbi5hZGRUb1JvbGVQb2xpY3kobmV3IGlhbS5Qb2xpY3lTdGF0ZW1lbnQoe1xuICAgICAgYWN0aW9uczogW1xuICAgICAgICAnY29kZXBpcGVsaW5lOkdldFBpcGVsaW5lRXhlY3V0aW9uJyxcbiAgICAgICAgJ2NvZGVwaXBlbGluZTpMaXN0VGFnc0ZvclJlc291cmNlJyxcbiAgICAgIF0sXG4gICAgICByZXNvdXJjZXM6IHBpcGVsaW5lSWFtUmVzb3VyY2VzLFxuICAgIH0pKTtcblxuICAgIC8vIFRyaWdnZXIgdGhlIG5vdGlmaWVyIHdoZW4gbWF0Y2hpbmcgQ29kZVBpcGVsaW5lIGV4ZWN1dGlvbiBldmVudHMgYXJyaXZlLlxuICAgIG5ldyBldmVudHMuUnVsZSh0aGlzLCAnT25QaXBlbGluZUV4ZWN1dGlvblN0YXJ0ZWRSdWxlJywge1xuICAgICAgZXZlbnRQYXR0ZXJuLFxuICAgICAgdGFyZ2V0czogW25ldyB0YXJnZXRzLkxhbWJkYUZ1bmN0aW9uKGZuKV0sXG4gICAgfSk7XG4gIH1cbn1cbiJdfQ==
|
|
@@ -14,7 +14,7 @@ type CodePipelineExecutionStartedDetail = {
|
|
|
14
14
|
type CodePipelineExecutionStartedEvent = EventBridgeEvent<'CodePipeline Pipeline Execution State Change', CodePipelineExecutionStartedDetail>;
|
|
15
15
|
/**
|
|
16
16
|
* Lambda handler triggered by EventBridge when a pipeline execution transitions to STARTED.
|
|
17
|
-
* It
|
|
17
|
+
* It ignores pipelines that do not match `TARGET_PIPELINE_TAGS` (and optional `TARGET_PIPELINE_ARNS`), then waits for a terminal state.
|
|
18
18
|
*/
|
|
19
19
|
export declare const handler: (event: CodePipelineExecutionStartedEvent) => Promise<void>;
|
|
20
20
|
export {};
|