function assertRuntimeOptionsValid()

in src/function-builder.ts [52:233]


function assertRuntimeOptionsValid(runtimeOptions: RuntimeOptions): boolean {
  if (
    runtimeOptions.memory &&
    !_.includes(VALID_MEMORY_OPTIONS, runtimeOptions.memory)
  ) {
    throw new Error(
      `The only valid memory allocation values are: ${VALID_MEMORY_OPTIONS.join(
        ', '
      )}`
    );
  }
  if (
    runtimeOptions.timeoutSeconds > MAX_TIMEOUT_SECONDS ||
    runtimeOptions.timeoutSeconds < 0
  ) {
    throw new Error(
      `TimeoutSeconds must be between 0 and ${MAX_TIMEOUT_SECONDS}`
    );
  }

  if (
    runtimeOptions.ingressSettings &&
    !_.includes(INGRESS_SETTINGS_OPTIONS, runtimeOptions.ingressSettings)
  ) {
    throw new Error(
      `The only valid ingressSettings values are: ${INGRESS_SETTINGS_OPTIONS.join(
        ','
      )}`
    );
  }

  if (
    runtimeOptions.vpcConnectorEgressSettings &&
    !_.includes(
      VPC_EGRESS_SETTINGS_OPTIONS,
      runtimeOptions.vpcConnectorEgressSettings
    )
  ) {
    throw new Error(
      `The only valid vpcConnectorEgressSettings values are: ${VPC_EGRESS_SETTINGS_OPTIONS.join(
        ','
      )}`
    );
  }

  if (runtimeOptions.failurePolicy !== undefined) {
    if (
      _.isBoolean(runtimeOptions.failurePolicy) === false &&
      _.isObjectLike(runtimeOptions.failurePolicy) === false
    ) {
      throw new Error(`failurePolicy must be a boolean or an object.`);
    }

    if (typeof runtimeOptions.failurePolicy === 'object') {
      if (
        _.isObjectLike(runtimeOptions.failurePolicy.retry) === false ||
        _.isEmpty(runtimeOptions.failurePolicy.retry) === false
      ) {
        throw new Error('failurePolicy.retry must be an empty object.');
      }
    }
  }

  if (
    runtimeOptions.serviceAccount &&
    runtimeOptions.serviceAccount !== 'default' &&
    !_.includes(runtimeOptions.serviceAccount, '@')
  ) {
    throw new Error(
      `serviceAccount must be set to 'default', a service account email, or '{serviceAccountName}@'`
    );
  }

  if (runtimeOptions.labels) {
    // Labels must follow the rules listed in
    // https://cloud.google.com/resource-manager/docs/creating-managing-labels#requirements

    if (Object.keys(runtimeOptions.labels).length > MAX_NUMBER_USER_LABELS) {
      throw new Error(
        `A function must not have more than ${MAX_NUMBER_USER_LABELS} user-defined labels.`
      );
    }

    // We reserve the 'deployment' and 'firebase' namespaces for future feature development.
    const reservedKeys = Object.keys(runtimeOptions.labels).filter(
      (key) => key.startsWith('deployment') || key.startsWith('firebase')
    );
    if (reservedKeys.length) {
      throw new Error(
        `Invalid labels: ${reservedKeys.join(
          ', '
        )}. Labels may not start with reserved names 'deployment' or 'firebase'`
      );
    }

    const invalidLengthKeys = Object.keys(runtimeOptions.labels).filter(
      (key) => key.length < 1 || key.length > 63
    );
    if (invalidLengthKeys.length > 0) {
      throw new Error(
        `Invalid labels: ${invalidLengthKeys.join(
          ', '
        )}. Label keys must be between 1 and 63 characters in length.`
      );
    }

    const invalidLengthValues = Object.values(runtimeOptions.labels).filter(
      (value) => value.length > 63
    );
    if (invalidLengthValues.length > 0) {
      throw new Error(
        `Invalid labels: ${invalidLengthValues.join(
          ', '
        )}. Label values must be less than 64 charcters.`
      );
    }

    // Keys can contain lowercase letters, foreign characters, numbers, _ or -. They must start with a letter.
    const validKeyPattern = /^[\p{Ll}\p{Lo}][\p{Ll}\p{Lo}\p{N}_-]{0,62}$/u;
    const invalidKeys = Object.keys(runtimeOptions.labels).filter(
      (key) => !validKeyPattern.test(key)
    );
    if (invalidKeys.length > 0) {
      throw new Error(
        `Invalid labels: ${invalidKeys.join(
          ', '
        )}. Label keys can only contain lowercase letters, international characters, numbers, _ or -, and must start with a letter.`
      );
    }

    // Values can contain lowercase letters, foreign characters, numbers, _ or -.
    const validValuePattern = /^[\p{Ll}\p{Lo}\p{N}_-]{0,63}$/u;
    const invalidValues = Object.values(runtimeOptions.labels).filter(
      (value) => !validValuePattern.test(value)
    );
    if (invalidValues.length > 0) {
      throw new Error(
        `Invalid labels: ${invalidValues.join(
          ', '
        )}. Label values can only contain lowercase letters, international characters, numbers, _ or -.`
      );
    }
  }

  if (
    typeof runtimeOptions.invoker === 'string' &&
    runtimeOptions.invoker.length === 0
  ) {
    throw new Error(
      'Invalid service account for function invoker, must be a non-empty string'
    );
  }
  if (
    runtimeOptions.invoker !== undefined &&
    Array.isArray(runtimeOptions.invoker)
  ) {
    if (runtimeOptions.invoker.length === 0) {
      throw new Error(
        'Invalid invoker array, must contain at least 1 service account entry'
      );
    }
    for (const serviceAccount of runtimeOptions.invoker) {
      if (serviceAccount.length === 0) {
        throw new Error(
          'Invalid invoker array, a service account must be a non-empty string'
        );
      }
      if (serviceAccount === 'public') {
        throw new Error(
          "Invalid invoker array, a service account cannot be set to the 'public' identifier"
        );
      }
      if (serviceAccount === 'private') {
        throw new Error(
          "Invalid invoker array, a service account cannot be set to the 'private' identifier"
        );
      }
    }
  }

  return true;
}