ec2-instance-running-scheduler 0.3.6 → 0.4.0

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.
@@ -20305,7 +20305,7 @@ var require_lib = __commonJS({
20305
20305
  "node_modules/fetch-retrier/lib/index.js"(exports2) {
20306
20306
  "use strict";
20307
20307
  Object.defineProperty(exports2, "__esModule", { value: true });
20308
- exports2.fetchRetrier = exports2.FetchRetrierUnreachableError = exports2.FetchRetrierNetworkError = exports2.FetchRetrierHttpError = exports2.FetchRetrierAlreadyAbortedError = exports2.FetchRetrierAbortError = void 0;
20308
+ exports2.parseRetryAfterMs = exports2.fetchRetrier = exports2.defaultShouldRetry = exports2.DEFAULT_RETRYABLE_HTTP_STATUSES = exports2.FetchRetrierUnreachableError = exports2.FetchRetrierInvalidOptionsError = exports2.FetchRetrierNetworkError = exports2.FetchRetrierHttpError = exports2.FetchRetrierAlreadyAbortedError = exports2.FetchRetrierAbortError = void 0;
20309
20309
  var FetchRetrierAbortError = class _FetchRetrierAbortError extends Error {
20310
20310
  /**
20311
20311
  * @param message - Human-readable reason (default: `'Aborted'`)
@@ -20332,10 +20332,12 @@ var require_lib = __commonJS({
20332
20332
  /**
20333
20333
  * @param message - Error description
20334
20334
  * @param status - HTTP status code from the last non-OK response
20335
+ * @param body - Response body text already read via `response.text()` for that attempt
20335
20336
  */
20336
- constructor(message, status) {
20337
+ constructor(message, status, body) {
20337
20338
  super(message);
20338
20339
  this.status = status;
20340
+ this.body = body;
20339
20341
  this.name = "FetchRetrierHttpError";
20340
20342
  Object.setPrototypeOf(this, _FetchRetrierHttpError.prototype);
20341
20343
  }
@@ -20354,6 +20356,17 @@ var require_lib = __commonJS({
20354
20356
  }
20355
20357
  };
20356
20358
  exports2.FetchRetrierNetworkError = FetchRetrierNetworkError;
20359
+ var FetchRetrierInvalidOptionsError = class _FetchRetrierInvalidOptionsError extends TypeError {
20360
+ /**
20361
+ * @param message - Human-readable reason describing the invalid option
20362
+ */
20363
+ constructor(message) {
20364
+ super(message);
20365
+ this.name = "FetchRetrierInvalidOptionsError";
20366
+ Object.setPrototypeOf(this, _FetchRetrierInvalidOptionsError.prototype);
20367
+ }
20368
+ };
20369
+ exports2.FetchRetrierInvalidOptionsError = FetchRetrierInvalidOptionsError;
20357
20370
  var FetchRetrierUnreachableError = class _FetchRetrierUnreachableError extends Error {
20358
20371
  /**
20359
20372
  * @param message - Human-readable reason (default: `'Unreachable'`)
@@ -20365,11 +20378,26 @@ var require_lib = __commonJS({
20365
20378
  }
20366
20379
  };
20367
20380
  exports2.FetchRetrierUnreachableError = FetchRetrierUnreachableError;
20368
- var defaultShouldRetry = (res) => {
20369
- return [429, 500, 502, 503, 504].includes(res.status);
20381
+ exports2.DEFAULT_RETRYABLE_HTTP_STATUSES = [408, 425, 429, 500, 502, 503, 504];
20382
+ var defaultShouldRetry = (response, _body) => {
20383
+ return exports2.DEFAULT_RETRYABLE_HTTP_STATUSES.includes(response.status);
20384
+ };
20385
+ exports2.defaultShouldRetry = defaultShouldRetry;
20386
+ var validateRequestOptions = (options) => {
20387
+ const { retries, timeoutMs, baseBackoffMs } = options;
20388
+ if (retries < 1) {
20389
+ throw new FetchRetrierInvalidOptionsError("retries must be >= 1");
20390
+ }
20391
+ if (timeoutMs <= 0) {
20392
+ throw new FetchRetrierInvalidOptionsError("timeoutMs must be > 0");
20393
+ }
20394
+ if (baseBackoffMs < 0) {
20395
+ throw new FetchRetrierInvalidOptionsError("baseBackoffMs must be >= 0");
20396
+ }
20370
20397
  };
20371
20398
  var fetchRetrier = async (url, options) => {
20372
- const { headers, init, retries, timeoutMs, baseBackoffMs, signal: externalSignal, shouldRetry = defaultShouldRetry } = options;
20399
+ const { headers, init, retries, timeoutMs, baseBackoffMs, signal: externalSignal, shouldRetry = exports2.defaultShouldRetry } = options;
20400
+ validateRequestOptions({ retries, timeoutMs, baseBackoffMs });
20373
20401
  for (let attempt = 1; attempt <= retries; attempt++) {
20374
20402
  if (externalSignal?.aborted) {
20375
20403
  throw new FetchRetrierAlreadyAbortedError();
@@ -20398,11 +20426,11 @@ var require_lib = __commonJS({
20398
20426
  const isContinue = shouldRetry(res, text);
20399
20427
  if (isContinue) {
20400
20428
  if (attempt === retries) {
20401
- throw new FetchRetrierHttpError(`HTTP ${res.status}`, res.status);
20429
+ throw new FetchRetrierHttpError(`HTTP ${res.status}`, res.status, text);
20402
20430
  }
20403
- await wait(fullJitter(baseBackoffMs, attempt));
20431
+ await wait(resolveRetryDelayMs(res, baseBackoffMs, attempt));
20404
20432
  } else {
20405
- throw new FetchRetrierHttpError(`Non-retriable HTTP error: ${res.status}`, res.status);
20433
+ throw new FetchRetrierHttpError(`Non-retriable HTTP error: ${res.status}`, res.status, text);
20406
20434
  }
20407
20435
  } catch (err) {
20408
20436
  clearTimeout(timer);
@@ -20432,12 +20460,68 @@ var require_lib = __commonJS({
20432
20460
  const cap = base * Math.pow(2, attempt);
20433
20461
  return Math.floor(Math.random() * cap);
20434
20462
  };
20463
+ var parseRetryAfterMs = (value, nowMs = Date.now()) => {
20464
+ const trimmed = value.trim();
20465
+ if (trimmed === "") {
20466
+ return void 0;
20467
+ }
20468
+ if (/^\d+$/.test(trimmed)) {
20469
+ return Number.parseInt(trimmed, 10) * 1e3;
20470
+ }
20471
+ if (/^-?\d+(\.\d+)?$/.test(trimmed)) {
20472
+ return void 0;
20473
+ }
20474
+ const dateMs = Date.parse(trimmed);
20475
+ if (Number.isNaN(dateMs)) {
20476
+ return void 0;
20477
+ }
20478
+ return Math.max(0, dateMs - nowMs);
20479
+ };
20480
+ exports2.parseRetryAfterMs = parseRetryAfterMs;
20481
+ var resolveRetryDelayMs = (response, baseBackoffMs, attempt, nowMs = Date.now()) => {
20482
+ const header = response.headers?.get("Retry-After");
20483
+ if (header == null) {
20484
+ return fullJitter(baseBackoffMs, attempt);
20485
+ }
20486
+ const fromHeader = (0, exports2.parseRetryAfterMs)(header, nowMs);
20487
+ if (fromHeader === void 0) {
20488
+ return fullJitter(baseBackoffMs, attempt);
20489
+ }
20490
+ return fromHeader;
20491
+ };
20435
20492
  }
20436
20493
  });
20437
20494
 
20438
- // node_modules/strict-env-resolver/lib/index.js
20495
+ // node_modules/quiet-json-parser/lib/index.js
20439
20496
  var require_lib2 = __commonJS({
20440
- "node_modules/strict-env-resolver/lib/index.js"(exports2) {
20497
+ "node_modules/quiet-json-parser/lib/index.js"(exports2) {
20498
+ "use strict";
20499
+ Object.defineProperty(exports2, "__esModule", { value: true });
20500
+ exports2.quietParse = void 0;
20501
+ var safeReviver = (key, value) => {
20502
+ if (key === "__proto__" || key === "constructor" || key === "prototype") {
20503
+ return void 0;
20504
+ }
20505
+ return value;
20506
+ };
20507
+ var quietParse = (jsonString, fallback, onError) => {
20508
+ if (!jsonString) {
20509
+ return fallback;
20510
+ }
20511
+ try {
20512
+ return JSON.parse(jsonString, safeReviver);
20513
+ } catch (error) {
20514
+ onError?.(error);
20515
+ return fallback;
20516
+ }
20517
+ };
20518
+ exports2.quietParse = quietParse;
20519
+ }
20520
+ });
20521
+
20522
+ // node_modules/aws-lambda-secret-fetcher/node_modules/strict-env-resolver/lib/index.js
20523
+ var require_lib3 = __commonJS({
20524
+ "node_modules/aws-lambda-secret-fetcher/node_modules/strict-env-resolver/lib/index.js"(exports2) {
20441
20525
  "use strict";
20442
20526
  Object.defineProperty(exports2, "__esModule", { value: true });
20443
20527
  exports2.StrictEnvResolver = exports2.StrictEnvType = exports2.StrictEnvValidationError = exports2.StrictEnvError = void 0;
@@ -20594,13 +20678,14 @@ ${lines.join("\n")}`;
20594
20678
  });
20595
20679
 
20596
20680
  // node_modules/aws-lambda-secret-fetcher/lib/index.js
20597
- var require_lib3 = __commonJS({
20681
+ var require_lib4 = __commonJS({
20598
20682
  "node_modules/aws-lambda-secret-fetcher/lib/index.js"(exports2) {
20599
20683
  "use strict";
20600
20684
  Object.defineProperty(exports2, "__esModule", { value: true });
20601
20685
  exports2.secretFetcher = void 0;
20602
20686
  var fetch_retrier_1 = require_lib();
20603
- var strict_env_resolver_1 = require_lib2();
20687
+ var quiet_json_parser_1 = require_lib2();
20688
+ var strict_env_resolver_1 = require_lib3();
20604
20689
  var getSecretValue = async (name, options = {}) => {
20605
20690
  const { extensionHttpPort, timeoutMs = 2e3, retries = 3, baseBackoffMs = 300 } = options;
20606
20691
  const port = resolveExtensionHttpPort(extensionHttpPort);
@@ -20614,10 +20699,12 @@ var require_lib3 = __commonJS({
20614
20699
  timeoutMs,
20615
20700
  baseBackoffMs,
20616
20701
  shouldRetry: (res, body) => {
20617
- if ([429, 500, 502, 503, 504].includes(res.status))
20702
+ if ((0, fetch_retrier_1.defaultShouldRetry)(res, body)) {
20618
20703
  return true;
20619
- if (res.status === 400 && /not\s+ready.*traffic/i.test(body))
20704
+ }
20705
+ if (res.status === 400 && /not\s+ready.*traffic/i.test(body)) {
20620
20706
  return true;
20707
+ }
20621
20708
  return false;
20622
20709
  }
20623
20710
  };
@@ -20627,11 +20714,7 @@ var require_lib3 = __commonJS({
20627
20714
  throw new Error("Invalid secret response format");
20628
20715
  }
20629
20716
  const data = raw;
20630
- const secretString = data.SecretString;
20631
- if (looksLikeJson(secretString)) {
20632
- return JSON.parse(secretString);
20633
- }
20634
- return secretString;
20717
+ return (0, quiet_json_parser_1.quietParse)(data.SecretString, data.SecretString);
20635
20718
  };
20636
20719
  var AWS_SESSION_TOKEN_GUIDANCE = "AWS_SESSION_TOKEN is not set. This library only works inside an AWS Lambda execution environment where the runtime provides AWS_SESSION_TOKEN for the Parameters and Secrets Extension. Attach the extension layer and run your code as a Lambda function handler.";
20637
20720
  var MIN_TCP_PORT = 1;
@@ -20669,15 +20752,212 @@ var require_lib3 = __commonJS({
20669
20752
  const v = value;
20670
20753
  return typeof v.SecretString === "string" && typeof v.Name === "string" && typeof v.ARN === "string" && (v.VersionId === void 0 || typeof v.VersionId === "string");
20671
20754
  };
20672
- var looksLikeJson = (str) => {
20673
- return typeof str === "string" && str.trim().startsWith("{");
20674
- };
20675
20755
  exports2.secretFetcher = {
20676
20756
  getSecretValue
20677
20757
  };
20678
20758
  }
20679
20759
  });
20680
20760
 
20761
+ // node_modules/strict-env-resolver/lib/index.js
20762
+ var require_lib5 = __commonJS({
20763
+ "node_modules/strict-env-resolver/lib/index.js"(exports2) {
20764
+ "use strict";
20765
+ Object.defineProperty(exports2, "__esModule", { value: true });
20766
+ exports2.StrictEnvResolver = exports2.StrictEnvType = exports2.StrictEnvValidationError = exports2.StrictEnvError = void 0;
20767
+ var TRUE_BOOLEAN_PATTERN = /^(1|true|yes|on)$/i;
20768
+ var parseNumber = (raw) => {
20769
+ const n = Number(raw);
20770
+ if (!Number.isFinite(n)) {
20771
+ return void 0;
20772
+ }
20773
+ return n;
20774
+ };
20775
+ var parseBooleanEnvValue = (normalizedRaw) => TRUE_BOOLEAN_PATTERN.test(normalizedRaw);
20776
+ var StrictEnvError = class extends Error {
20777
+ /**
20778
+ * @param message - Error message.
20779
+ */
20780
+ constructor(message) {
20781
+ super(message);
20782
+ this.name = "StrictEnvError";
20783
+ }
20784
+ };
20785
+ exports2.StrictEnvError = StrictEnvError;
20786
+ var StrictEnvValidationError = class _StrictEnvValidationError extends StrictEnvError {
20787
+ /**
20788
+ * Formats validation errors into a human-readable error message.
20789
+ *
20790
+ * @template K - Environment variable key type.
20791
+ * @param errors - Validation error entries to format.
20792
+ * @returns Multi-line summary listing each key and message.
20793
+ */
20794
+ static format(errors) {
20795
+ const lines = errors.map((e) => `- ${e.key}: ${e.message}${e.raw == null ? "" : ` (raw="${e.raw}")`}`);
20796
+ return `Invalid environment variables (${errors.length}):
20797
+ ${lines.join("\n")}`;
20798
+ }
20799
+ /**
20800
+ * Creates a new validation error from one or more `StrictEnvValidationEntry` entries.
20801
+ *
20802
+ * @param errors - One or more structured validation errors.
20803
+ */
20804
+ constructor(errors) {
20805
+ const msg = _StrictEnvValidationError.format(errors);
20806
+ super(msg);
20807
+ this.name = "StrictEnvValidationError";
20808
+ this.errors = errors;
20809
+ this.keys = errors.map((e) => e.key);
20810
+ }
20811
+ };
20812
+ exports2.StrictEnvValidationError = StrictEnvValidationError;
20813
+ var createNumberSpec = (constraints) => ({
20814
+ type: "number",
20815
+ ...constraints?.min !== void 0 ? { min: constraints.min } : {},
20816
+ ...constraints?.max !== void 0 ? { max: constraints.max } : {},
20817
+ ...constraints?.integer !== void 0 ? { integer: constraints.integer } : {}
20818
+ });
20819
+ var NumberSpec = Object.assign((constraints) => createNumberSpec(constraints), { type: "number" });
20820
+ exports2.StrictEnvType = {
20821
+ /** Spec for a string value (returned as-is unless `trim: true` is set in options). */
20822
+ String: { type: "string" },
20823
+ /**
20824
+ * Spec for a finite numeric value (`Number()` parsing; rejects `NaN` and `Infinity`).
20825
+ * Use as `StrictEnvType.Number`, or call as `StrictEnvType.Number({ min, max, integer })`.
20826
+ */
20827
+ Number: NumberSpec,
20828
+ /**
20829
+ * Spec for a positive integer (`>= 1`). Sugar for `Number({ min: 1, integer: true })`.
20830
+ */
20831
+ PositiveInt: createNumberSpec({ min: 1, integer: true }),
20832
+ /**
20833
+ * Spec for a negative integer (`<= -1`). Sugar for `Number({ max: -1, integer: true })`.
20834
+ */
20835
+ NegativeInt: createNumberSpec({ max: -1, integer: true }),
20836
+ /** Spec for a boolean value (`1`/`true`/`yes`/`on` → `true`; other non-empty values → `false`). */
20837
+ Boolean: { type: "boolean" },
20838
+ /**
20839
+ * Returns a spec that restricts the value to one of the given choices.
20840
+ *
20841
+ * @param choices - Allowed string literals.
20842
+ * @returns Enum spec for use with `resolve` or `resolveAll`.
20843
+ */
20844
+ Enum: (choices) => ({ type: "enum", choices })
20845
+ };
20846
+ var defaultTrimForSpec = (spec) => spec.type !== "string";
20847
+ var normalizeEnvRaw = (raw, trim) => {
20848
+ if (raw == null) {
20849
+ return void 0;
20850
+ }
20851
+ if (!trim) {
20852
+ return raw;
20853
+ }
20854
+ return raw.trim();
20855
+ };
20856
+ var validateNumberConstraints = (key, raw, n, constraints) => {
20857
+ if (constraints.integer === true && !Number.isInteger(n)) {
20858
+ return { key, message: `Env ${key}: expected integer, got "${raw}"`, raw, kind: "invalid_number" };
20859
+ }
20860
+ if (constraints.min !== void 0 && n < constraints.min) {
20861
+ return {
20862
+ key,
20863
+ message: `Env ${key}: must be >= ${constraints.min}, got ${n}`,
20864
+ raw,
20865
+ kind: "invalid_number"
20866
+ };
20867
+ }
20868
+ if (constraints.max !== void 0 && n > constraints.max) {
20869
+ return {
20870
+ key,
20871
+ message: `Env ${key}: must be <= ${constraints.max}, got ${n}`,
20872
+ raw,
20873
+ kind: "invalid_number"
20874
+ };
20875
+ }
20876
+ return void 0;
20877
+ };
20878
+ var resolveSchemaEntry = (entry) => ({
20879
+ spec: Array.isArray(entry) ? entry[0] : entry,
20880
+ options: Array.isArray(entry) ? entry[1] : void 0
20881
+ });
20882
+ var parseEnvValue = (key, spec, raw, defaultValue, options) => {
20883
+ const trim = options?.trim ?? defaultTrimForSpec(spec);
20884
+ const normalizedRaw = normalizeEnvRaw(raw, trim);
20885
+ const hasDefault = defaultValue !== void 0;
20886
+ if (normalizedRaw == null || normalizedRaw === "") {
20887
+ if (hasDefault) {
20888
+ return { ok: true, value: defaultValue };
20889
+ }
20890
+ return {
20891
+ ok: false,
20892
+ error: { key, message: `Missing required environment variable: ${key}`, raw, kind: "missing" }
20893
+ };
20894
+ }
20895
+ switch (spec.type) {
20896
+ case "number": {
20897
+ const n = parseNumber(normalizedRaw);
20898
+ if (n === void 0) {
20899
+ return {
20900
+ ok: false,
20901
+ error: { key, message: `Env ${key}: expected number, got "${raw}"`, raw, kind: "invalid_number" }
20902
+ };
20903
+ }
20904
+ const constraintError = validateNumberConstraints(key, raw, n, spec);
20905
+ if (constraintError !== void 0) {
20906
+ return { ok: false, error: constraintError };
20907
+ }
20908
+ return { ok: true, value: n };
20909
+ }
20910
+ case "boolean":
20911
+ return { ok: true, value: parseBooleanEnvValue(normalizedRaw) };
20912
+ case "enum":
20913
+ if (!spec.choices.includes(normalizedRaw)) {
20914
+ return {
20915
+ ok: false,
20916
+ error: {
20917
+ key,
20918
+ message: `Env ${key}: must be one of [${spec.choices.join(", ")}]`,
20919
+ raw,
20920
+ kind: "invalid_enum"
20921
+ }
20922
+ };
20923
+ }
20924
+ return { ok: true, value: normalizedRaw };
20925
+ default:
20926
+ return { ok: true, value: normalizedRaw };
20927
+ }
20928
+ };
20929
+ var resolve = (key, spec = exports2.StrictEnvType.String, options) => {
20930
+ const result = parseEnvValue(key, spec, process.env[key], options?.default, options);
20931
+ if (!result.ok) {
20932
+ throw new StrictEnvValidationError([result.error]);
20933
+ }
20934
+ return result.value;
20935
+ };
20936
+ var resolveAll = (schema) => {
20937
+ const envs = {};
20938
+ const errors = [];
20939
+ for (const key of Object.keys(schema)) {
20940
+ const { spec, options } = resolveSchemaEntry(schema[key]);
20941
+ const result = parseEnvValue(key, spec, process.env[key], options?.default, options);
20942
+ if (!result.ok) {
20943
+ errors.push(result.error);
20944
+ continue;
20945
+ }
20946
+ envs[key] = result.value;
20947
+ }
20948
+ if (errors.length > 0)
20949
+ throw new StrictEnvValidationError(errors);
20950
+ return envs;
20951
+ };
20952
+ exports2.StrictEnvResolver = {
20953
+ /** Reads and parses a single environment variable. See {@link resolve}. */
20954
+ resolve,
20955
+ /** Reads and parses multiple environment variables in one pass. See {@link resolveAll}. */
20956
+ resolveAll
20957
+ };
20958
+ }
20959
+ });
20960
+
20681
20961
  // src/funcs/running-scheduler.lambda.ts
20682
20962
  var running_scheduler_lambda_exports = {};
20683
20963
  __export(running_scheduler_lambda_exports, {
@@ -24203,14 +24483,16 @@ var DEFAULT_CONFIG = {
24203
24483
  var import_client_ec2 = require("@aws-sdk/client-ec2");
24204
24484
  var import_client_resource_groups_tagging_api = require("@aws-sdk/client-resource-groups-tagging-api");
24205
24485
  var import_web_api = __toESM(require_dist5());
24206
- var import_aws_lambda_secret_fetcher = __toESM(require_lib3());
24207
- var import_strict_env_resolver2 = __toESM(require_lib2());
24486
+ var import_aws_lambda_secret_fetcher = __toESM(require_lib4());
24487
+ var import_strict_env_resolver2 = __toESM(require_lib5());
24208
24488
 
24209
24489
  // src/funcs/running-scheduler-predicates.ts
24210
24490
  var DEFAULT_RESOURCE_WAIT_LIMITS = {
24211
24491
  maxLoopCount: 90,
24212
- maxElapsedSeconds: 1800
24492
+ maxElapsedSeconds: 1800,
24493
+ statusChangeWaitSeconds: 20
24213
24494
  };
24495
+ var DEFAULT_MAX_CONCURRENCY = 10;
24214
24496
  var isDesiredStableState = (mode, currentState) => mode === "Start" && currentState === "running" || mode === "Stop" && currentState === "stopped";
24215
24497
  var isTransitioningState = (mode, currentState) => mode === "Start" && currentState === "pending" || mode === "Stop" && (currentState === "stopping" || currentState === "shutting-down");
24216
24498
  var getWaitAbortReason = (loopCount, startedAtMs, nowMs, limits) => {
@@ -24235,54 +24517,71 @@ var formatResourceWaitFailure = (reason, context) => {
24235
24517
  };
24236
24518
 
24237
24519
  // src/funcs/running-scheduler-wait-env.ts
24238
- var import_strict_env_resolver = __toESM(require_lib2());
24520
+ var import_strict_env_resolver = __toESM(require_lib5());
24239
24521
 
24240
24522
  // src/funcs/running-scheduler-wait-config.ts
24241
24523
  var PROCESS_RESOURCE_MAX_LOOP_COUNT_ENV = "PROCESS_RESOURCE_MAX_LOOP_COUNT";
24242
24524
  var PROCESS_RESOURCE_MAX_ELAPSED_SECONDS_ENV = "PROCESS_RESOURCE_MAX_ELAPSED_SECONDS";
24525
+ var PROCESS_RESOURCE_STATUS_CHANGE_WAIT_SECONDS_ENV = "PROCESS_RESOURCE_STATUS_CHANGE_WAIT_SECONDS";
24526
+ var PROCESS_RESOURCES_MAX_CONCURRENCY_ENV = "PROCESS_RESOURCES_MAX_CONCURRENCY";
24243
24527
 
24244
24528
  // src/funcs/running-scheduler-wait-env.ts
24245
- var assertPositiveEnvInt = (key, value) => {
24246
- if (value <= 0) {
24247
- const raw = process.env[key];
24248
- throw new Error(`Invalid ${key}: must be a positive integer (got "${raw ?? ""}")`);
24249
- }
24250
- return value;
24251
- };
24252
24529
  var parseResourceWaitLimitsFromEnv = () => {
24253
24530
  const parsed = import_strict_env_resolver.StrictEnvResolver.resolveAll({
24254
24531
  [PROCESS_RESOURCE_MAX_LOOP_COUNT_ENV]: [
24255
- import_strict_env_resolver.StrictEnvType.Number,
24532
+ import_strict_env_resolver.StrictEnvType.PositiveInt,
24256
24533
  { default: DEFAULT_RESOURCE_WAIT_LIMITS.maxLoopCount }
24257
24534
  ],
24258
24535
  [PROCESS_RESOURCE_MAX_ELAPSED_SECONDS_ENV]: [
24259
- import_strict_env_resolver.StrictEnvType.Number,
24536
+ import_strict_env_resolver.StrictEnvType.PositiveInt,
24260
24537
  { default: DEFAULT_RESOURCE_WAIT_LIMITS.maxElapsedSeconds }
24538
+ ],
24539
+ [PROCESS_RESOURCE_STATUS_CHANGE_WAIT_SECONDS_ENV]: [
24540
+ import_strict_env_resolver.StrictEnvType.PositiveInt,
24541
+ { default: DEFAULT_RESOURCE_WAIT_LIMITS.statusChangeWaitSeconds }
24261
24542
  ]
24262
24543
  });
24263
24544
  return {
24264
- maxLoopCount: assertPositiveEnvInt(
24265
- PROCESS_RESOURCE_MAX_LOOP_COUNT_ENV,
24266
- parsed[PROCESS_RESOURCE_MAX_LOOP_COUNT_ENV]
24267
- ),
24268
- maxElapsedSeconds: assertPositiveEnvInt(
24269
- PROCESS_RESOURCE_MAX_ELAPSED_SECONDS_ENV,
24270
- parsed[PROCESS_RESOURCE_MAX_ELAPSED_SECONDS_ENV]
24271
- )
24545
+ maxLoopCount: parsed[PROCESS_RESOURCE_MAX_LOOP_COUNT_ENV],
24546
+ maxElapsedSeconds: parsed[PROCESS_RESOURCE_MAX_ELAPSED_SECONDS_ENV],
24547
+ statusChangeWaitSeconds: parsed[PROCESS_RESOURCE_STATUS_CHANGE_WAIT_SECONDS_ENV]
24272
24548
  };
24273
24549
  };
24550
+ var parseMaxConcurrencyFromEnv = () => {
24551
+ const parsed = import_strict_env_resolver.StrictEnvResolver.resolveAll({
24552
+ [PROCESS_RESOURCES_MAX_CONCURRENCY_ENV]: [
24553
+ import_strict_env_resolver.StrictEnvType.PositiveInt,
24554
+ { default: DEFAULT_MAX_CONCURRENCY }
24555
+ ]
24556
+ });
24557
+ return parsed[PROCESS_RESOURCES_MAX_CONCURRENCY_ENV];
24558
+ };
24274
24559
 
24275
24560
  // src/funcs/running-scheduler.lambda.ts
24276
24561
  var STATE_LIST = [
24277
24562
  { name: "RUNNING", emoji: "\u{1F606}", state: "running" },
24278
24563
  { name: "STOPPED", emoji: "\u{1F634}", state: "stopped" }
24279
24564
  ];
24280
- var STATUS_CHANGE_WAIT_SECONDS = 20;
24281
24565
  var SLACK_SECRET_FETCH_OPTIONS = {
24282
24566
  timeoutMs: 3e3,
24283
24567
  retries: 5,
24284
24568
  baseBackoffMs: 300
24285
24569
  };
24570
+ var isSlackSecret = (value) => {
24571
+ if (typeof value !== "object" || value === null) {
24572
+ return false;
24573
+ }
24574
+ if (!("token" in value) || !("channel" in value)) {
24575
+ return false;
24576
+ }
24577
+ if (typeof value.token !== "string" || value.token === "") {
24578
+ return false;
24579
+ }
24580
+ if (typeof value.channel !== "string" || value.channel === "") {
24581
+ return false;
24582
+ }
24583
+ return true;
24584
+ };
24286
24585
  var getStateDisplay = (current) => {
24287
24586
  const found = STATE_LIST.find((s) => s.state === current);
24288
24587
  return found ? { emoji: found.emoji, name: found.name } : void 0;
@@ -24301,7 +24600,8 @@ var processOneResource = async (ctx, targetResource, params, resourceIndex, wait
24301
24600
  account,
24302
24601
  mode: params.Mode,
24303
24602
  maxLoopCount: waitLimits.maxLoopCount,
24304
- maxElapsedSeconds: waitLimits.maxElapsedSeconds
24603
+ maxElapsedSeconds: waitLimits.maxElapsedSeconds,
24604
+ statusChangeWaitSeconds: waitLimits.statusChangeWaitSeconds
24305
24605
  });
24306
24606
  const startedAtMs = await ctx.step(`${stepPrefix}-wait-started-at`, async () => Date.now());
24307
24607
  let loopCount = 0;
@@ -24349,9 +24649,9 @@ var processOneResource = async (ctx, targetResource, params, resourceIndex, wait
24349
24649
  });
24350
24650
  ctx.logger.info("processOneResource: wait after start", {
24351
24651
  identifier,
24352
- seconds: STATUS_CHANGE_WAIT_SECONDS
24652
+ seconds: waitLimits.statusChangeWaitSeconds
24353
24653
  });
24354
- await ctx.wait({ seconds: STATUS_CHANGE_WAIT_SECONDS });
24654
+ await ctx.wait({ seconds: waitLimits.statusChangeWaitSeconds });
24355
24655
  loopCount += 1;
24356
24656
  continue;
24357
24657
  }
@@ -24363,9 +24663,9 @@ var processOneResource = async (ctx, targetResource, params, resourceIndex, wait
24363
24663
  });
24364
24664
  ctx.logger.info("processOneResource: wait after stop", {
24365
24665
  identifier,
24366
- seconds: STATUS_CHANGE_WAIT_SECONDS
24666
+ seconds: waitLimits.statusChangeWaitSeconds
24367
24667
  });
24368
- await ctx.wait({ seconds: STATUS_CHANGE_WAIT_SECONDS });
24668
+ await ctx.wait({ seconds: waitLimits.statusChangeWaitSeconds });
24369
24669
  loopCount += 1;
24370
24670
  continue;
24371
24671
  }
@@ -24376,9 +24676,9 @@ var processOneResource = async (ctx, targetResource, params, resourceIndex, wait
24376
24676
  loopCount,
24377
24677
  currentState,
24378
24678
  mode,
24379
- seconds: STATUS_CHANGE_WAIT_SECONDS
24679
+ seconds: waitLimits.statusChangeWaitSeconds
24380
24680
  });
24381
- await ctx.wait({ seconds: STATUS_CHANGE_WAIT_SECONDS });
24681
+ await ctx.wait({ seconds: waitLimits.statusChangeWaitSeconds });
24382
24682
  loopCount += 1;
24383
24683
  continue;
24384
24684
  }
@@ -24423,13 +24723,14 @@ var handler = withDurableExecution(async (event, ctx) => {
24423
24723
  throw new Error("Invalid event: Params.TagKey, Params.TagValues, Params.Mode are required.");
24424
24724
  }
24425
24725
  const waitLimits = parseResourceWaitLimitsFromEnv();
24726
+ const maxConcurrency = parseMaxConcurrencyFromEnv();
24426
24727
  const slackSecretName = import_strict_env_resolver2.StrictEnvResolver.resolve("SLACK_SECRET_NAME", import_strict_env_resolver2.StrictEnvType.String);
24427
24728
  const slackSecretValue = await ctx.step("fetch-slack-secret", async () => {
24428
24729
  ctx.logger.info("running-scheduler: fetching Slack secret", { secretName: slackSecretName });
24429
24730
  return import_aws_lambda_secret_fetcher.secretFetcher.getSecretValue(slackSecretName, SLACK_SECRET_FETCH_OPTIONS);
24430
24731
  });
24431
24732
  ctx.logger.info("running-scheduler: Slack secret loaded");
24432
- if (!slackSecretValue?.token || !slackSecretValue?.channel) {
24733
+ if (!isSlackSecret(slackSecretValue)) {
24433
24734
  throw new Error("Slack secret must contain token and channel.");
24434
24735
  }
24435
24736
  const targetResources = await ctx.step("get-target-resources", async () => {
@@ -24473,7 +24774,7 @@ var handler = withDurableExecution(async (event, ctx) => {
24473
24774
  });
24474
24775
  ctx.logger.info("running-scheduler: starting parallel instance processing", {
24475
24776
  count: targetResources.length,
24476
- maxConcurrency: 10
24777
+ maxConcurrency
24477
24778
  });
24478
24779
  const results = await ctx.map(
24479
24780
  targetResources,
@@ -24521,7 +24822,7 @@ var handler = withDurableExecution(async (event, ctx) => {
24521
24822
  return result;
24522
24823
  });
24523
24824
  },
24524
- { maxConcurrency: 10 }
24825
+ { maxConcurrency }
24525
24826
  );
24526
24827
  const resultList = Array.isArray(results) ? results : [];
24527
24828
  ctx.logger.info("running-scheduler: completed", {